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