]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
drm/xe: Support for I2C attached MCUs
authorHeikki Krogerus <heikki.krogerus@linux.intel.com>
Tue, 1 Jul 2025 12:22:50 +0000 (15:22 +0300)
committerRodrigo Vivi <rodrigo.vivi@intel.com>
Thu, 10 Jul 2025 14:19:41 +0000 (10:19 -0400)
Adding adaption/glue layer where the I2C host adapter
(Synopsys DesignWare I2C adapter) and the I2C clients (the
microcontroller units) are enumerated.

The microcontroller units (MCU) that are attached to the GPU
depend on the OEM. The initially supported MCU will be the
Add-In Management Controller (AMC).

Co-developed-by: Michael J. Ruhl <michael.j.ruhl@intel.com>
Signed-off-by: Michael J. Ruhl <michael.j.ruhl@intel.com>
Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Reviewed-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
Reviewed-by: Andi Shyti <andi.shyti@linux.intel.com>
Link: https://lore.kernel.org/r/20250701122252.2590230-4-heikki.krogerus@linux.intel.com
Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
[Rodrigo fixed the co-developed tags and SPDX format in the .c file]

drivers/gpu/drm/xe/Kconfig
drivers/gpu/drm/xe/Makefile
drivers/gpu/drm/xe/regs/xe_i2c_regs.h [new file with mode: 0644]
drivers/gpu/drm/xe/regs/xe_irq_regs.h
drivers/gpu/drm/xe/regs/xe_pmt.h
drivers/gpu/drm/xe/regs/xe_regs.h
drivers/gpu/drm/xe/xe_device.c
drivers/gpu/drm/xe/xe_device_types.h
drivers/gpu/drm/xe/xe_i2c.c [new file with mode: 0644]
drivers/gpu/drm/xe/xe_i2c.h [new file with mode: 0644]
drivers/gpu/drm/xe/xe_irq.c

index f66e6d39e319e4a690e2faaac357f2bad1a0386e..2bb2bc052120d867107dceecd892e6252c92db33 100644 (file)
@@ -45,6 +45,7 @@ config DRM_XE
        select WANT_DEV_COREDUMP
        select AUXILIARY_BUS
        select HMM_MIRROR
+       select REGMAP if I2C
        help
          Driver for Intel Xe2 series GPUs and later. Experimental support
          for Xe series is also available.
index 7c039caefd0016160310cc72f0c0f946a2c37d82..d52cf5808d6fb75ed79115159ac6b5a423abb13e 100644 (file)
@@ -125,6 +125,7 @@ xe-y += xe_bb.o \
        xe_wait_user_fence.o \
        xe_wopcm.o
 
+xe-$(CONFIG_I2C)       += xe_i2c.o
 xe-$(CONFIG_HMM_MIRROR) += xe_hmm.o
 xe-$(CONFIG_DRM_XE_GPUSVM) += xe_svm.o
 
diff --git a/drivers/gpu/drm/xe/regs/xe_i2c_regs.h b/drivers/gpu/drm/xe/regs/xe_i2c_regs.h
new file mode 100644 (file)
index 0000000..92dae44
--- /dev/null
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: MIT */
+#ifndef _XE_I2C_REGS_H_
+#define _XE_I2C_REGS_H_
+
+#include "xe_reg_defs.h"
+#include "xe_regs.h"
+
+#define I2C_BRIDGE_OFFSET              (SOC_BASE + 0xd9000)
+#define I2C_CONFIG_SPACE_OFFSET                (SOC_BASE + 0xf6000)
+#define I2C_MEM_SPACE_OFFSET           (SOC_BASE + 0xf7400)
+
+#define REG_SG_REMAP_ADDR_PREFIX       XE_REG(SOC_BASE + 0x0164)
+#define REG_SG_REMAP_ADDR_POSTFIX      XE_REG(SOC_BASE + 0x0168)
+
+#endif /* _XE_I2C_REGS_H_ */
index f0ecfcac40037828e3123e2ad8140bb1085180ea..13635e4331d4b979f94a22509bec3238c31e4a2b 100644 (file)
@@ -19,6 +19,7 @@
 #define   MASTER_IRQ                           REG_BIT(31)
 #define   GU_MISC_IRQ                          REG_BIT(29)
 #define   DISPLAY_IRQ                          REG_BIT(16)
+#define   I2C_IRQ                              REG_BIT(12)
 #define   GT_DW_IRQ(x)                         REG_BIT(x)
 
 /*
index b0efd9b48d1e0ccbcf1d296b35744df23d4182ae..2995d72c3f78fdda357e7a3ab29d0748033a0722 100644 (file)
@@ -5,7 +5,7 @@
 #ifndef _XE_PMT_H_
 #define _XE_PMT_H_
 
-#define SOC_BASE                       0x280000
+#include "xe_regs.h"
 
 #define BMG_PMT_BASE_OFFSET            0xDB000
 #define BMG_DISCOVERY_OFFSET           (SOC_BASE + BMG_PMT_BASE_OFFSET)
index 3abb17d2ca336ee2e07338fb19e535c923c89f02..1926b4044314ece1d004be76e685dc5401612d43 100644 (file)
@@ -7,6 +7,8 @@
 
 #include "regs/xe_reg_defs.h"
 
+#define SOC_BASE                               0x280000
+
 #define GU_CNTL_PROTECTED                      XE_REG(0x10100C)
 #define   DRIVERINT_FLR_DIS                    REG_BIT(31)
 
index 0b73cb72bad1df63a66aabb62764f36914feea09..6db09cfc8eb84d368bc1a062a73643079f70c8b9 100644 (file)
@@ -43,6 +43,7 @@
 #include "xe_guc_pc.h"
 #include "xe_hw_engine_group.h"
 #include "xe_hwmon.h"
+#include "xe_i2c.h"
 #include "xe_irq.h"
 #include "xe_mmio.h"
 #include "xe_module.h"
@@ -902,6 +903,10 @@ int xe_device_probe(struct xe_device *xe)
        if (err)
                goto err_unregister_display;
 
+       err = xe_i2c_probe(xe);
+       if (err)
+               goto err_unregister_display;
+
        for_each_gt(gt, xe, id)
                xe_gt_sanitize_freq(gt);
 
index 78c4acafd268b797e28bb885be88c4f5d9b485e5..decc749fbf70b074890786ef1823585ce2278855 100644 (file)
@@ -34,6 +34,7 @@ struct dram_info;
 struct intel_display;
 struct intel_dg_nvm_dev;
 struct xe_ggtt;
+struct xe_i2c;
 struct xe_pat_ops;
 struct xe_pxp;
 
@@ -585,6 +586,9 @@ struct xe_device {
        /** @pmu: performance monitoring unit */
        struct xe_pmu pmu;
 
+       /** @i2c: I2C host controller */
+       struct xe_i2c *i2c;
+
        /** @atomic_svm_timeslice_ms: Atomic SVM fault timeslice MS */
        u32 atomic_svm_timeslice_ms;
 
diff --git a/drivers/gpu/drm/xe/xe_i2c.c b/drivers/gpu/drm/xe/xe_i2c.c
new file mode 100644 (file)
index 0000000..6f05142
--- /dev/null
@@ -0,0 +1,300 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/*
+ * Intel Xe I2C attached Microcontroller Units (MCU)
+ *
+ * Copyright (C) 2025 Intel Corporation.
+ */
+
+#include <linux/array_size.h>
+#include <linux/container_of.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/notifier.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/sprintf.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+
+#include "regs/xe_i2c_regs.h"
+#include "regs/xe_irq_regs.h"
+
+#include "xe_device.h"
+#include "xe_device_types.h"
+#include "xe_i2c.h"
+#include "xe_mmio.h"
+#include "xe_platform_types.h"
+
+/**
+ * DOC: Xe I2C devices
+ *
+ * Register a platform device for the I2C host controller (Synpsys DesignWare
+ * I2C) if the registers of that controller are mapped to the MMIO, and also the
+ * I2C client device for the Add-In Management Controller (the MCU) attached to
+ * the host controller.
+ *
+ * See drivers/i2c/busses/i2c-designware-* for more information on the I2C host
+ * controller.
+ */
+
+static const char adapter_name[] = "i2c_designware";
+
+static const struct property_entry xe_i2c_adapter_properties[] = {
+       PROPERTY_ENTRY_STRING("compatible", "intel,xe-i2c"),
+       PROPERTY_ENTRY_U32("clock-frequency", I2C_MAX_FAST_MODE_PLUS_FREQ),
+       { }
+};
+
+static inline void xe_i2c_read_endpoint(struct xe_mmio *mmio, void *ep)
+{
+       u32 *val = ep;
+
+       val[0] = xe_mmio_read32(mmio, REG_SG_REMAP_ADDR_PREFIX);
+       val[1] = xe_mmio_read32(mmio, REG_SG_REMAP_ADDR_POSTFIX);
+}
+
+static void xe_i2c_client_work(struct work_struct *work)
+{
+       struct xe_i2c *i2c = container_of(work, struct xe_i2c, work);
+       struct i2c_board_info info = {
+               .type   = "amc",
+               .flags  = I2C_CLIENT_HOST_NOTIFY,
+               .addr   = i2c->ep.addr[1],
+       };
+
+       i2c->client[0] = i2c_new_client_device(i2c->adapter, &info);
+}
+
+static int xe_i2c_notifier(struct notifier_block *nb, unsigned long action, void *data)
+{
+       struct xe_i2c *i2c = container_of(nb, struct xe_i2c, bus_notifier);
+       struct i2c_adapter *adapter = i2c_verify_adapter(data);
+       struct device *dev = data;
+
+       if (action == BUS_NOTIFY_ADD_DEVICE &&
+           adapter && dev->parent == &i2c->pdev->dev) {
+               i2c->adapter = adapter;
+               schedule_work(&i2c->work);
+               return NOTIFY_OK;
+       }
+
+       return NOTIFY_DONE;
+}
+
+static int xe_i2c_register_adapter(struct xe_i2c *i2c)
+{
+       struct pci_dev *pci = to_pci_dev(i2c->drm_dev);
+       struct platform_device *pdev;
+       struct fwnode_handle *fwnode;
+       int ret;
+
+       fwnode = fwnode_create_software_node(xe_i2c_adapter_properties, NULL);
+       if (!fwnode)
+               return -ENOMEM;
+
+       /*
+        * Not using platform_device_register_full() here because we don't have
+        * a handle to the platform_device before it returns. xe_i2c_notifier()
+        * uses that handle, but it may be called before
+        * platform_device_register_full() is done.
+        */
+       pdev = platform_device_alloc(adapter_name, pci_dev_id(pci));
+       if (!pdev) {
+               ret = -ENOMEM;
+               goto err_fwnode_remove;
+       }
+
+       if (i2c->adapter_irq) {
+               struct resource res;
+
+               res = DEFINE_RES_IRQ_NAMED(i2c->adapter_irq, "xe_i2c");
+
+               ret = platform_device_add_resources(pdev, &res, 1);
+               if (ret)
+                       goto err_pdev_put;
+       }
+
+       pdev->dev.parent = i2c->drm_dev;
+       pdev->dev.fwnode = fwnode;
+       i2c->adapter_node = fwnode;
+       i2c->pdev = pdev;
+
+       ret = platform_device_add(pdev);
+       if (ret)
+               goto err_pdev_put;
+
+       return 0;
+
+err_pdev_put:
+       platform_device_put(pdev);
+err_fwnode_remove:
+       fwnode_remove_software_node(fwnode);
+
+       return ret;
+}
+
+static void xe_i2c_unregister_adapter(struct xe_i2c *i2c)
+{
+       platform_device_unregister(i2c->pdev);
+       fwnode_remove_software_node(i2c->adapter_node);
+}
+
+/**
+ * xe_i2c_irq_handler: Handler for I2C interrupts
+ * @xe: xe device instance
+ * @master_ctl: interrupt register
+ *
+ * Forward interrupts generated by the I2C host adapter to the I2C host adapter
+ * driver.
+ */
+void xe_i2c_irq_handler(struct xe_device *xe, u32 master_ctl)
+{
+       if (!xe->i2c || !xe->i2c->adapter_irq)
+               return;
+
+       if (master_ctl & I2C_IRQ)
+               generic_handle_irq_safe(xe->i2c->adapter_irq);
+}
+
+static int xe_i2c_irq_map(struct irq_domain *h, unsigned int virq,
+                         irq_hw_number_t hw_irq_num)
+{
+       irq_set_chip_and_handler(virq, &dummy_irq_chip, handle_simple_irq);
+       return 0;
+}
+
+static const struct irq_domain_ops xe_i2c_irq_ops = {
+       .map = xe_i2c_irq_map,
+};
+
+static int xe_i2c_create_irq(struct xe_i2c *i2c)
+{
+       struct irq_domain *domain;
+
+       if (!(i2c->ep.capabilities & XE_I2C_EP_CAP_IRQ))
+               return 0;
+
+       domain = irq_domain_create_linear(dev_fwnode(i2c->drm_dev), 1, &xe_i2c_irq_ops, NULL);
+       if (!domain)
+               return -ENOMEM;
+
+       i2c->adapter_irq = irq_create_mapping(domain, 0);
+       i2c->irqdomain = domain;
+
+       return 0;
+}
+
+static void xe_i2c_remove_irq(struct xe_i2c *i2c)
+{
+       if (!i2c->irqdomain)
+               return;
+
+       irq_dispose_mapping(i2c->adapter_irq);
+       irq_domain_remove(i2c->irqdomain);
+}
+
+static int xe_i2c_read(void *context, unsigned int reg, unsigned int *val)
+{
+       struct xe_i2c *i2c = context;
+
+       *val = xe_mmio_read32(i2c->mmio, XE_REG(reg + I2C_MEM_SPACE_OFFSET));
+
+       return 0;
+}
+
+static int xe_i2c_write(void *context, unsigned int reg, unsigned int val)
+{
+       struct xe_i2c *i2c = context;
+
+       xe_mmio_write32(i2c->mmio, XE_REG(reg + I2C_MEM_SPACE_OFFSET), val);
+
+       return 0;
+}
+
+static const struct regmap_config i2c_regmap_config = {
+       .reg_bits = 32,
+       .val_bits = 32,
+       .reg_read = xe_i2c_read,
+       .reg_write = xe_i2c_write,
+       .fast_io = true,
+};
+
+static void xe_i2c_remove(void *data)
+{
+       struct xe_i2c *i2c = data;
+       unsigned int i;
+
+       for (i = 0; i < XE_I2C_MAX_CLIENTS; i++)
+               i2c_unregister_device(i2c->client[i]);
+
+       bus_unregister_notifier(&i2c_bus_type, &i2c->bus_notifier);
+       xe_i2c_unregister_adapter(i2c);
+       xe_i2c_remove_irq(i2c);
+}
+
+/**
+ * xe_i2c_probe: Probe the I2C host adapter and the I2C clients attached to it
+ * @xe: xe device instance
+ *
+ * Register all the I2C devices described in the I2C Endpoint data structure.
+ *
+ * Return: 0 on success, error code on failure
+ */
+int xe_i2c_probe(struct xe_device *xe)
+{
+       struct device *drm_dev = xe->drm.dev;
+       struct xe_i2c_endpoint ep;
+       struct regmap *regmap;
+       struct xe_i2c *i2c;
+       int ret;
+
+       if (xe->info.platform != XE_BATTLEMAGE)
+               return 0;
+
+       xe_i2c_read_endpoint(xe_root_tile_mmio(xe), &ep);
+       if (ep.cookie != XE_I2C_EP_COOKIE_DEVICE)
+               return 0;
+
+       i2c = devm_kzalloc(drm_dev, sizeof(*i2c), GFP_KERNEL);
+       if (!i2c)
+               return -ENOMEM;
+
+       INIT_WORK(&i2c->work, xe_i2c_client_work);
+       i2c->mmio = xe_root_tile_mmio(xe);
+       i2c->drm_dev = drm_dev;
+       i2c->ep = ep;
+
+       regmap = devm_regmap_init(drm_dev, NULL, i2c, &i2c_regmap_config);
+       if (IS_ERR(regmap))
+               return PTR_ERR(regmap);
+
+       i2c->bus_notifier.notifier_call = xe_i2c_notifier;
+       ret = bus_register_notifier(&i2c_bus_type, &i2c->bus_notifier);
+       if (ret)
+               return ret;
+
+       ret = xe_i2c_create_irq(i2c);
+       if (ret)
+               goto err_unregister_notifier;
+
+       ret = xe_i2c_register_adapter(i2c);
+       if (ret)
+               goto err_remove_irq;
+
+       return devm_add_action_or_reset(drm_dev, xe_i2c_remove, i2c);
+
+err_remove_irq:
+       xe_i2c_remove_irq(i2c);
+
+err_unregister_notifier:
+       bus_unregister_notifier(&i2c_bus_type, &i2c->bus_notifier);
+
+       return ret;
+}
diff --git a/drivers/gpu/drm/xe/xe_i2c.h b/drivers/gpu/drm/xe/xe_i2c.h
new file mode 100644 (file)
index 0000000..7ea40f4
--- /dev/null
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: MIT */
+#ifndef _XE_I2C_H_
+#define _XE_I2C_H_
+
+#include <linux/bits.h>
+#include <linux/notifier.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+
+struct device;
+struct fwnode_handle;
+struct i2c_adapter;
+struct i2c_client;
+struct irq_domain;
+struct platform_device;
+struct xe_device;
+struct xe_mmio;
+
+#define XE_I2C_MAX_CLIENTS             3
+
+#define XE_I2C_EP_COOKIE_DEVICE                0xde
+
+/* Endpoint Capabilities */
+#define XE_I2C_EP_CAP_IRQ              BIT(0)
+
+struct xe_i2c_endpoint {
+       u8 cookie;
+       u8 capabilities;
+       u16 addr[XE_I2C_MAX_CLIENTS];
+};
+
+struct xe_i2c {
+       struct fwnode_handle *adapter_node;
+       struct platform_device *pdev;
+       struct i2c_adapter *adapter;
+       struct i2c_client *client[XE_I2C_MAX_CLIENTS];
+
+       struct notifier_block bus_notifier;
+       struct work_struct work;
+
+       struct irq_domain *irqdomain;
+       int adapter_irq;
+
+       struct xe_i2c_endpoint ep;
+       struct device *drm_dev;
+
+       struct xe_mmio *mmio;
+};
+
+#if IS_ENABLED(CONFIG_I2C)
+int xe_i2c_probe(struct xe_device *xe);
+void xe_i2c_irq_handler(struct xe_device *xe, u32 master_ctl);
+#else
+static inline int xe_i2c_probe(struct xe_device *xe) { return 0; }
+static inline void xe_i2c_irq_handler(struct xe_device *xe, u32 master_ctl) { }
+#endif
+
+#endif
index 5362d3174b06093a5f58b154fdf379da42410bdf..c43e62dc692eae78ce7db79206f8a9768c1fb3d9 100644 (file)
@@ -18,6 +18,7 @@
 #include "xe_gt.h"
 #include "xe_guc.h"
 #include "xe_hw_engine.h"
+#include "xe_i2c.h"
 #include "xe_memirq.h"
 #include "xe_mmio.h"
 #include "xe_pxp.h"
@@ -476,6 +477,7 @@ static irqreturn_t dg1_irq_handler(int irq, void *arg)
                        if (xe->info.has_heci_cscfi)
                                xe_heci_csc_irq_handler(xe, master_ctl);
                        xe_display_irq_handler(xe, master_ctl);
+                       xe_i2c_irq_handler(xe, master_ctl);
                        gu_misc_iir = gu_misc_irq_ack(xe, master_ctl);
                }
        }