// SPDX-License-Identifier: GPL-2.0 OR MIT /* * Intel Xe I2C attached Microcontroller Units (MCU) * * Copyright (C) 2025 Intel Corporation. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 (IS_ERR(fwnode)) return PTR_ERR(fwnode); /* * 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, }; void xe_i2c_pm_suspend(struct xe_device *xe) { struct xe_mmio *mmio = xe_root_tile_mmio(xe); if (!xe->i2c || xe->i2c->ep.cookie != XE_I2C_EP_COOKIE_DEVICE) return; xe_mmio_rmw32(mmio, I2C_CONFIG_PMCSR, PCI_PM_CTRL_STATE_MASK, (__force u32)PCI_D3hot); drm_dbg(&xe->drm, "pmcsr: 0x%08x\n", xe_mmio_read32(mmio, I2C_CONFIG_PMCSR)); } void xe_i2c_pm_resume(struct xe_device *xe, bool d3cold) { struct xe_mmio *mmio = xe_root_tile_mmio(xe); if (!xe->i2c || xe->i2c->ep.cookie != XE_I2C_EP_COOKIE_DEVICE) return; if (d3cold) xe_mmio_rmw32(mmio, I2C_CONFIG_CMD, 0, PCI_COMMAND_MEMORY); xe_mmio_rmw32(mmio, I2C_CONFIG_PMCSR, PCI_PM_CTRL_STATE_MASK, (__force u32)PCI_D0); drm_dbg(&xe->drm, "pmcsr: 0x%08x\n", xe_mmio_read32(mmio, I2C_CONFIG_PMCSR)); } 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; if (IS_SRIOV_VF(xe)) 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; xe->i2c = i2c; /* PCI PM isn't aware of this device, bring it up and match it with SGUnit state. */ xe_i2c_pm_resume(xe, true); 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; }