1f0e53aadSHeikki Krogerus // SPDX-License-Identifier: GPL-2.0 OR MIT 2f0e53aadSHeikki Krogerus /* 3f0e53aadSHeikki Krogerus * Intel Xe I2C attached Microcontroller Units (MCU) 4f0e53aadSHeikki Krogerus * 5f0e53aadSHeikki Krogerus * Copyright (C) 2025 Intel Corporation. 6f0e53aadSHeikki Krogerus */ 7f0e53aadSHeikki Krogerus 8f0e53aadSHeikki Krogerus #include <linux/array_size.h> 9f0e53aadSHeikki Krogerus #include <linux/container_of.h> 10f0e53aadSHeikki Krogerus #include <linux/device.h> 11f0e53aadSHeikki Krogerus #include <linux/err.h> 12f0e53aadSHeikki Krogerus #include <linux/i2c.h> 13f0e53aadSHeikki Krogerus #include <linux/ioport.h> 14f0e53aadSHeikki Krogerus #include <linux/irq.h> 15f0e53aadSHeikki Krogerus #include <linux/irqdomain.h> 16f0e53aadSHeikki Krogerus #include <linux/notifier.h> 17f0e53aadSHeikki Krogerus #include <linux/pci.h> 18f0e53aadSHeikki Krogerus #include <linux/platform_device.h> 19f0e53aadSHeikki Krogerus #include <linux/property.h> 20f0e53aadSHeikki Krogerus #include <linux/regmap.h> 21f0e53aadSHeikki Krogerus #include <linux/sprintf.h> 22f0e53aadSHeikki Krogerus #include <linux/string.h> 23f0e53aadSHeikki Krogerus #include <linux/types.h> 24f0e53aadSHeikki Krogerus #include <linux/workqueue.h> 25f0e53aadSHeikki Krogerus 26f0e53aadSHeikki Krogerus #include "regs/xe_i2c_regs.h" 27f0e53aadSHeikki Krogerus #include "regs/xe_irq_regs.h" 28f0e53aadSHeikki Krogerus 29f0e53aadSHeikki Krogerus #include "xe_device.h" 30f0e53aadSHeikki Krogerus #include "xe_device_types.h" 31f0e53aadSHeikki Krogerus #include "xe_i2c.h" 32f0e53aadSHeikki Krogerus #include "xe_mmio.h" 33f0e53aadSHeikki Krogerus #include "xe_platform_types.h" 34f0e53aadSHeikki Krogerus 35f0e53aadSHeikki Krogerus /** 36f0e53aadSHeikki Krogerus * DOC: Xe I2C devices 37f0e53aadSHeikki Krogerus * 38f0e53aadSHeikki Krogerus * Register a platform device for the I2C host controller (Synpsys DesignWare 39f0e53aadSHeikki Krogerus * I2C) if the registers of that controller are mapped to the MMIO, and also the 40f0e53aadSHeikki Krogerus * I2C client device for the Add-In Management Controller (the MCU) attached to 41f0e53aadSHeikki Krogerus * the host controller. 42f0e53aadSHeikki Krogerus * 43f0e53aadSHeikki Krogerus * See drivers/i2c/busses/i2c-designware-* for more information on the I2C host 44f0e53aadSHeikki Krogerus * controller. 45f0e53aadSHeikki Krogerus */ 46f0e53aadSHeikki Krogerus 47f0e53aadSHeikki Krogerus static const char adapter_name[] = "i2c_designware"; 48f0e53aadSHeikki Krogerus 49f0e53aadSHeikki Krogerus static const struct property_entry xe_i2c_adapter_properties[] = { 50f0e53aadSHeikki Krogerus PROPERTY_ENTRY_STRING("compatible", "intel,xe-i2c"), 51f0e53aadSHeikki Krogerus PROPERTY_ENTRY_U32("clock-frequency", I2C_MAX_FAST_MODE_PLUS_FREQ), 52f0e53aadSHeikki Krogerus { } 53f0e53aadSHeikki Krogerus }; 54f0e53aadSHeikki Krogerus 55f0e53aadSHeikki Krogerus static inline void xe_i2c_read_endpoint(struct xe_mmio *mmio, void *ep) 56f0e53aadSHeikki Krogerus { 57f0e53aadSHeikki Krogerus u32 *val = ep; 58f0e53aadSHeikki Krogerus 59f0e53aadSHeikki Krogerus val[0] = xe_mmio_read32(mmio, REG_SG_REMAP_ADDR_PREFIX); 60f0e53aadSHeikki Krogerus val[1] = xe_mmio_read32(mmio, REG_SG_REMAP_ADDR_POSTFIX); 61f0e53aadSHeikki Krogerus } 62f0e53aadSHeikki Krogerus 63f0e53aadSHeikki Krogerus static void xe_i2c_client_work(struct work_struct *work) 64f0e53aadSHeikki Krogerus { 65f0e53aadSHeikki Krogerus struct xe_i2c *i2c = container_of(work, struct xe_i2c, work); 66f0e53aadSHeikki Krogerus struct i2c_board_info info = { 67f0e53aadSHeikki Krogerus .type = "amc", 68f0e53aadSHeikki Krogerus .flags = I2C_CLIENT_HOST_NOTIFY, 69f0e53aadSHeikki Krogerus .addr = i2c->ep.addr[1], 70f0e53aadSHeikki Krogerus }; 71f0e53aadSHeikki Krogerus 72f0e53aadSHeikki Krogerus i2c->client[0] = i2c_new_client_device(i2c->adapter, &info); 73f0e53aadSHeikki Krogerus } 74f0e53aadSHeikki Krogerus 75f0e53aadSHeikki Krogerus static int xe_i2c_notifier(struct notifier_block *nb, unsigned long action, void *data) 76f0e53aadSHeikki Krogerus { 77f0e53aadSHeikki Krogerus struct xe_i2c *i2c = container_of(nb, struct xe_i2c, bus_notifier); 78f0e53aadSHeikki Krogerus struct i2c_adapter *adapter = i2c_verify_adapter(data); 79f0e53aadSHeikki Krogerus struct device *dev = data; 80f0e53aadSHeikki Krogerus 81f0e53aadSHeikki Krogerus if (action == BUS_NOTIFY_ADD_DEVICE && 82f0e53aadSHeikki Krogerus adapter && dev->parent == &i2c->pdev->dev) { 83f0e53aadSHeikki Krogerus i2c->adapter = adapter; 84f0e53aadSHeikki Krogerus schedule_work(&i2c->work); 85f0e53aadSHeikki Krogerus return NOTIFY_OK; 86f0e53aadSHeikki Krogerus } 87f0e53aadSHeikki Krogerus 88f0e53aadSHeikki Krogerus return NOTIFY_DONE; 89f0e53aadSHeikki Krogerus } 90f0e53aadSHeikki Krogerus 91f0e53aadSHeikki Krogerus static int xe_i2c_register_adapter(struct xe_i2c *i2c) 92f0e53aadSHeikki Krogerus { 93f0e53aadSHeikki Krogerus struct pci_dev *pci = to_pci_dev(i2c->drm_dev); 94f0e53aadSHeikki Krogerus struct platform_device *pdev; 95f0e53aadSHeikki Krogerus struct fwnode_handle *fwnode; 96f0e53aadSHeikki Krogerus int ret; 97f0e53aadSHeikki Krogerus 98f0e53aadSHeikki Krogerus fwnode = fwnode_create_software_node(xe_i2c_adapter_properties, NULL); 99f0e53aadSHeikki Krogerus if (!fwnode) 100f0e53aadSHeikki Krogerus return -ENOMEM; 101f0e53aadSHeikki Krogerus 102f0e53aadSHeikki Krogerus /* 103f0e53aadSHeikki Krogerus * Not using platform_device_register_full() here because we don't have 104f0e53aadSHeikki Krogerus * a handle to the platform_device before it returns. xe_i2c_notifier() 105f0e53aadSHeikki Krogerus * uses that handle, but it may be called before 106f0e53aadSHeikki Krogerus * platform_device_register_full() is done. 107f0e53aadSHeikki Krogerus */ 108f0e53aadSHeikki Krogerus pdev = platform_device_alloc(adapter_name, pci_dev_id(pci)); 109f0e53aadSHeikki Krogerus if (!pdev) { 110f0e53aadSHeikki Krogerus ret = -ENOMEM; 111f0e53aadSHeikki Krogerus goto err_fwnode_remove; 112f0e53aadSHeikki Krogerus } 113f0e53aadSHeikki Krogerus 114f0e53aadSHeikki Krogerus if (i2c->adapter_irq) { 115f0e53aadSHeikki Krogerus struct resource res; 116f0e53aadSHeikki Krogerus 117f0e53aadSHeikki Krogerus res = DEFINE_RES_IRQ_NAMED(i2c->adapter_irq, "xe_i2c"); 118f0e53aadSHeikki Krogerus 119f0e53aadSHeikki Krogerus ret = platform_device_add_resources(pdev, &res, 1); 120f0e53aadSHeikki Krogerus if (ret) 121f0e53aadSHeikki Krogerus goto err_pdev_put; 122f0e53aadSHeikki Krogerus } 123f0e53aadSHeikki Krogerus 124f0e53aadSHeikki Krogerus pdev->dev.parent = i2c->drm_dev; 125f0e53aadSHeikki Krogerus pdev->dev.fwnode = fwnode; 126f0e53aadSHeikki Krogerus i2c->adapter_node = fwnode; 127f0e53aadSHeikki Krogerus i2c->pdev = pdev; 128f0e53aadSHeikki Krogerus 129f0e53aadSHeikki Krogerus ret = platform_device_add(pdev); 130f0e53aadSHeikki Krogerus if (ret) 131f0e53aadSHeikki Krogerus goto err_pdev_put; 132f0e53aadSHeikki Krogerus 133f0e53aadSHeikki Krogerus return 0; 134f0e53aadSHeikki Krogerus 135f0e53aadSHeikki Krogerus err_pdev_put: 136f0e53aadSHeikki Krogerus platform_device_put(pdev); 137f0e53aadSHeikki Krogerus err_fwnode_remove: 138f0e53aadSHeikki Krogerus fwnode_remove_software_node(fwnode); 139f0e53aadSHeikki Krogerus 140f0e53aadSHeikki Krogerus return ret; 141f0e53aadSHeikki Krogerus } 142f0e53aadSHeikki Krogerus 143f0e53aadSHeikki Krogerus static void xe_i2c_unregister_adapter(struct xe_i2c *i2c) 144f0e53aadSHeikki Krogerus { 145f0e53aadSHeikki Krogerus platform_device_unregister(i2c->pdev); 146f0e53aadSHeikki Krogerus fwnode_remove_software_node(i2c->adapter_node); 147f0e53aadSHeikki Krogerus } 148f0e53aadSHeikki Krogerus 149f0e53aadSHeikki Krogerus /** 150f0e53aadSHeikki Krogerus * xe_i2c_irq_handler: Handler for I2C interrupts 151f0e53aadSHeikki Krogerus * @xe: xe device instance 152f0e53aadSHeikki Krogerus * @master_ctl: interrupt register 153f0e53aadSHeikki Krogerus * 154f0e53aadSHeikki Krogerus * Forward interrupts generated by the I2C host adapter to the I2C host adapter 155f0e53aadSHeikki Krogerus * driver. 156f0e53aadSHeikki Krogerus */ 157f0e53aadSHeikki Krogerus void xe_i2c_irq_handler(struct xe_device *xe, u32 master_ctl) 158f0e53aadSHeikki Krogerus { 159f0e53aadSHeikki Krogerus if (!xe->i2c || !xe->i2c->adapter_irq) 160f0e53aadSHeikki Krogerus return; 161f0e53aadSHeikki Krogerus 162f0e53aadSHeikki Krogerus if (master_ctl & I2C_IRQ) 163f0e53aadSHeikki Krogerus generic_handle_irq_safe(xe->i2c->adapter_irq); 164f0e53aadSHeikki Krogerus } 165f0e53aadSHeikki Krogerus 166f0e53aadSHeikki Krogerus static int xe_i2c_irq_map(struct irq_domain *h, unsigned int virq, 167f0e53aadSHeikki Krogerus irq_hw_number_t hw_irq_num) 168f0e53aadSHeikki Krogerus { 169f0e53aadSHeikki Krogerus irq_set_chip_and_handler(virq, &dummy_irq_chip, handle_simple_irq); 170f0e53aadSHeikki Krogerus return 0; 171f0e53aadSHeikki Krogerus } 172f0e53aadSHeikki Krogerus 173f0e53aadSHeikki Krogerus static const struct irq_domain_ops xe_i2c_irq_ops = { 174f0e53aadSHeikki Krogerus .map = xe_i2c_irq_map, 175f0e53aadSHeikki Krogerus }; 176f0e53aadSHeikki Krogerus 177f0e53aadSHeikki Krogerus static int xe_i2c_create_irq(struct xe_i2c *i2c) 178f0e53aadSHeikki Krogerus { 179f0e53aadSHeikki Krogerus struct irq_domain *domain; 180f0e53aadSHeikki Krogerus 181f0e53aadSHeikki Krogerus if (!(i2c->ep.capabilities & XE_I2C_EP_CAP_IRQ)) 182f0e53aadSHeikki Krogerus return 0; 183f0e53aadSHeikki Krogerus 184f0e53aadSHeikki Krogerus domain = irq_domain_create_linear(dev_fwnode(i2c->drm_dev), 1, &xe_i2c_irq_ops, NULL); 185f0e53aadSHeikki Krogerus if (!domain) 186f0e53aadSHeikki Krogerus return -ENOMEM; 187f0e53aadSHeikki Krogerus 188f0e53aadSHeikki Krogerus i2c->adapter_irq = irq_create_mapping(domain, 0); 189f0e53aadSHeikki Krogerus i2c->irqdomain = domain; 190f0e53aadSHeikki Krogerus 191f0e53aadSHeikki Krogerus return 0; 192f0e53aadSHeikki Krogerus } 193f0e53aadSHeikki Krogerus 194f0e53aadSHeikki Krogerus static void xe_i2c_remove_irq(struct xe_i2c *i2c) 195f0e53aadSHeikki Krogerus { 196f0e53aadSHeikki Krogerus if (!i2c->irqdomain) 197f0e53aadSHeikki Krogerus return; 198f0e53aadSHeikki Krogerus 199f0e53aadSHeikki Krogerus irq_dispose_mapping(i2c->adapter_irq); 200f0e53aadSHeikki Krogerus irq_domain_remove(i2c->irqdomain); 201f0e53aadSHeikki Krogerus } 202f0e53aadSHeikki Krogerus 203f0e53aadSHeikki Krogerus static int xe_i2c_read(void *context, unsigned int reg, unsigned int *val) 204f0e53aadSHeikki Krogerus { 205f0e53aadSHeikki Krogerus struct xe_i2c *i2c = context; 206f0e53aadSHeikki Krogerus 207f0e53aadSHeikki Krogerus *val = xe_mmio_read32(i2c->mmio, XE_REG(reg + I2C_MEM_SPACE_OFFSET)); 208f0e53aadSHeikki Krogerus 209f0e53aadSHeikki Krogerus return 0; 210f0e53aadSHeikki Krogerus } 211f0e53aadSHeikki Krogerus 212f0e53aadSHeikki Krogerus static int xe_i2c_write(void *context, unsigned int reg, unsigned int val) 213f0e53aadSHeikki Krogerus { 214f0e53aadSHeikki Krogerus struct xe_i2c *i2c = context; 215f0e53aadSHeikki Krogerus 216f0e53aadSHeikki Krogerus xe_mmio_write32(i2c->mmio, XE_REG(reg + I2C_MEM_SPACE_OFFSET), val); 217f0e53aadSHeikki Krogerus 218f0e53aadSHeikki Krogerus return 0; 219f0e53aadSHeikki Krogerus } 220f0e53aadSHeikki Krogerus 221f0e53aadSHeikki Krogerus static const struct regmap_config i2c_regmap_config = { 222f0e53aadSHeikki Krogerus .reg_bits = 32, 223f0e53aadSHeikki Krogerus .val_bits = 32, 224f0e53aadSHeikki Krogerus .reg_read = xe_i2c_read, 225f0e53aadSHeikki Krogerus .reg_write = xe_i2c_write, 226f0e53aadSHeikki Krogerus .fast_io = true, 227f0e53aadSHeikki Krogerus }; 228f0e53aadSHeikki Krogerus 229*0ea07b69SRaag Jadav void xe_i2c_pm_suspend(struct xe_device *xe) 230*0ea07b69SRaag Jadav { 231*0ea07b69SRaag Jadav struct xe_mmio *mmio = xe_root_tile_mmio(xe); 232*0ea07b69SRaag Jadav 233*0ea07b69SRaag Jadav if (!xe->i2c || xe->i2c->ep.cookie != XE_I2C_EP_COOKIE_DEVICE) 234*0ea07b69SRaag Jadav return; 235*0ea07b69SRaag Jadav 236*0ea07b69SRaag Jadav xe_mmio_rmw32(mmio, I2C_CONFIG_PMCSR, PCI_PM_CTRL_STATE_MASK, (__force u32)PCI_D3hot); 237*0ea07b69SRaag Jadav drm_dbg(&xe->drm, "pmcsr: 0x%08x\n", xe_mmio_read32(mmio, I2C_CONFIG_PMCSR)); 238*0ea07b69SRaag Jadav } 239*0ea07b69SRaag Jadav 240*0ea07b69SRaag Jadav void xe_i2c_pm_resume(struct xe_device *xe, bool d3cold) 241*0ea07b69SRaag Jadav { 242*0ea07b69SRaag Jadav struct xe_mmio *mmio = xe_root_tile_mmio(xe); 243*0ea07b69SRaag Jadav 244*0ea07b69SRaag Jadav if (!xe->i2c || xe->i2c->ep.cookie != XE_I2C_EP_COOKIE_DEVICE) 245*0ea07b69SRaag Jadav return; 246*0ea07b69SRaag Jadav 247*0ea07b69SRaag Jadav if (d3cold) 248*0ea07b69SRaag Jadav xe_mmio_rmw32(mmio, I2C_CONFIG_CMD, 0, PCI_COMMAND_MEMORY); 249*0ea07b69SRaag Jadav 250*0ea07b69SRaag Jadav xe_mmio_rmw32(mmio, I2C_CONFIG_PMCSR, PCI_PM_CTRL_STATE_MASK, (__force u32)PCI_D0); 251*0ea07b69SRaag Jadav drm_dbg(&xe->drm, "pmcsr: 0x%08x\n", xe_mmio_read32(mmio, I2C_CONFIG_PMCSR)); 252*0ea07b69SRaag Jadav } 253*0ea07b69SRaag Jadav 254f0e53aadSHeikki Krogerus static void xe_i2c_remove(void *data) 255f0e53aadSHeikki Krogerus { 256f0e53aadSHeikki Krogerus struct xe_i2c *i2c = data; 257f0e53aadSHeikki Krogerus unsigned int i; 258f0e53aadSHeikki Krogerus 259f0e53aadSHeikki Krogerus for (i = 0; i < XE_I2C_MAX_CLIENTS; i++) 260f0e53aadSHeikki Krogerus i2c_unregister_device(i2c->client[i]); 261f0e53aadSHeikki Krogerus 262f0e53aadSHeikki Krogerus bus_unregister_notifier(&i2c_bus_type, &i2c->bus_notifier); 263f0e53aadSHeikki Krogerus xe_i2c_unregister_adapter(i2c); 264f0e53aadSHeikki Krogerus xe_i2c_remove_irq(i2c); 265f0e53aadSHeikki Krogerus } 266f0e53aadSHeikki Krogerus 267f0e53aadSHeikki Krogerus /** 268f0e53aadSHeikki Krogerus * xe_i2c_probe: Probe the I2C host adapter and the I2C clients attached to it 269f0e53aadSHeikki Krogerus * @xe: xe device instance 270f0e53aadSHeikki Krogerus * 271f0e53aadSHeikki Krogerus * Register all the I2C devices described in the I2C Endpoint data structure. 272f0e53aadSHeikki Krogerus * 273f0e53aadSHeikki Krogerus * Return: 0 on success, error code on failure 274f0e53aadSHeikki Krogerus */ 275f0e53aadSHeikki Krogerus int xe_i2c_probe(struct xe_device *xe) 276f0e53aadSHeikki Krogerus { 277f0e53aadSHeikki Krogerus struct device *drm_dev = xe->drm.dev; 278f0e53aadSHeikki Krogerus struct xe_i2c_endpoint ep; 279f0e53aadSHeikki Krogerus struct regmap *regmap; 280f0e53aadSHeikki Krogerus struct xe_i2c *i2c; 281f0e53aadSHeikki Krogerus int ret; 282f0e53aadSHeikki Krogerus 283f0e53aadSHeikki Krogerus if (xe->info.platform != XE_BATTLEMAGE) 284f0e53aadSHeikki Krogerus return 0; 285f0e53aadSHeikki Krogerus 286f0e53aadSHeikki Krogerus xe_i2c_read_endpoint(xe_root_tile_mmio(xe), &ep); 287f0e53aadSHeikki Krogerus if (ep.cookie != XE_I2C_EP_COOKIE_DEVICE) 288f0e53aadSHeikki Krogerus return 0; 289f0e53aadSHeikki Krogerus 290f0e53aadSHeikki Krogerus i2c = devm_kzalloc(drm_dev, sizeof(*i2c), GFP_KERNEL); 291f0e53aadSHeikki Krogerus if (!i2c) 292f0e53aadSHeikki Krogerus return -ENOMEM; 293f0e53aadSHeikki Krogerus 294f0e53aadSHeikki Krogerus INIT_WORK(&i2c->work, xe_i2c_client_work); 295f0e53aadSHeikki Krogerus i2c->mmio = xe_root_tile_mmio(xe); 296f0e53aadSHeikki Krogerus i2c->drm_dev = drm_dev; 297f0e53aadSHeikki Krogerus i2c->ep = ep; 298*0ea07b69SRaag Jadav xe->i2c = i2c; 299*0ea07b69SRaag Jadav 300*0ea07b69SRaag Jadav /* PCI PM isn't aware of this device, bring it up and match it with SGUnit state. */ 301*0ea07b69SRaag Jadav xe_i2c_pm_resume(xe, true); 302f0e53aadSHeikki Krogerus 303f0e53aadSHeikki Krogerus regmap = devm_regmap_init(drm_dev, NULL, i2c, &i2c_regmap_config); 304f0e53aadSHeikki Krogerus if (IS_ERR(regmap)) 305f0e53aadSHeikki Krogerus return PTR_ERR(regmap); 306f0e53aadSHeikki Krogerus 307f0e53aadSHeikki Krogerus i2c->bus_notifier.notifier_call = xe_i2c_notifier; 308f0e53aadSHeikki Krogerus ret = bus_register_notifier(&i2c_bus_type, &i2c->bus_notifier); 309f0e53aadSHeikki Krogerus if (ret) 310f0e53aadSHeikki Krogerus return ret; 311f0e53aadSHeikki Krogerus 312f0e53aadSHeikki Krogerus ret = xe_i2c_create_irq(i2c); 313f0e53aadSHeikki Krogerus if (ret) 314f0e53aadSHeikki Krogerus goto err_unregister_notifier; 315f0e53aadSHeikki Krogerus 316f0e53aadSHeikki Krogerus ret = xe_i2c_register_adapter(i2c); 317f0e53aadSHeikki Krogerus if (ret) 318f0e53aadSHeikki Krogerus goto err_remove_irq; 319f0e53aadSHeikki Krogerus 320f0e53aadSHeikki Krogerus return devm_add_action_or_reset(drm_dev, xe_i2c_remove, i2c); 321f0e53aadSHeikki Krogerus 322f0e53aadSHeikki Krogerus err_remove_irq: 323f0e53aadSHeikki Krogerus xe_i2c_remove_irq(i2c); 324f0e53aadSHeikki Krogerus 325f0e53aadSHeikki Krogerus err_unregister_notifier: 326f0e53aadSHeikki Krogerus bus_unregister_notifier(&i2c_bus_type, &i2c->bus_notifier); 327f0e53aadSHeikki Krogerus 328f0e53aadSHeikki Krogerus return ret; 329f0e53aadSHeikki Krogerus } 330