Skip to content

Commit f0e53aa

Browse files
Heikki Krogerusrodrigovivi
authored andcommitted
drm/xe: Support for I2C attached MCUs
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 <[email protected]> Signed-off-by: Michael J. Ruhl <[email protected]> Signed-off-by: Heikki Krogerus <[email protected]> Reviewed-by: Rodrigo Vivi <[email protected]> Reviewed-by: Andi Shyti <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Rodrigo Vivi <[email protected]> [Rodrigo fixed the co-developed tags and SPDX format in the .c file]
1 parent f6a8e9f commit f0e53aa

File tree

11 files changed

+390
-1
lines changed

11 files changed

+390
-1
lines changed

drivers/gpu/drm/xe/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ config DRM_XE
4545
select WANT_DEV_COREDUMP
4646
select AUXILIARY_BUS
4747
select HMM_MIRROR
48+
select REGMAP if I2C
4849
help
4950
Driver for Intel Xe2 series GPUs and later. Experimental support
5051
for Xe series is also available.

drivers/gpu/drm/xe/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ xe-y += xe_bb.o \
125125
xe_wait_user_fence.o \
126126
xe_wopcm.o
127127

128+
xe-$(CONFIG_I2C) += xe_i2c.o
128129
xe-$(CONFIG_HMM_MIRROR) += xe_hmm.o
129130
xe-$(CONFIG_DRM_XE_GPUSVM) += xe_svm.o
130131

drivers/gpu/drm/xe/regs/xe_i2c_regs.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/* SPDX-License-Identifier: MIT */
2+
#ifndef _XE_I2C_REGS_H_
3+
#define _XE_I2C_REGS_H_
4+
5+
#include "xe_reg_defs.h"
6+
#include "xe_regs.h"
7+
8+
#define I2C_BRIDGE_OFFSET (SOC_BASE + 0xd9000)
9+
#define I2C_CONFIG_SPACE_OFFSET (SOC_BASE + 0xf6000)
10+
#define I2C_MEM_SPACE_OFFSET (SOC_BASE + 0xf7400)
11+
12+
#define REG_SG_REMAP_ADDR_PREFIX XE_REG(SOC_BASE + 0x0164)
13+
#define REG_SG_REMAP_ADDR_POSTFIX XE_REG(SOC_BASE + 0x0168)
14+
15+
#endif /* _XE_I2C_REGS_H_ */

drivers/gpu/drm/xe/regs/xe_irq_regs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#define MASTER_IRQ REG_BIT(31)
2020
#define GU_MISC_IRQ REG_BIT(29)
2121
#define DISPLAY_IRQ REG_BIT(16)
22+
#define I2C_IRQ REG_BIT(12)
2223
#define GT_DW_IRQ(x) REG_BIT(x)
2324

2425
/*

drivers/gpu/drm/xe/regs/xe_pmt.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
#ifndef _XE_PMT_H_
66
#define _XE_PMT_H_
77

8-
#define SOC_BASE 0x280000
8+
#include "xe_regs.h"
99

1010
#define BMG_PMT_BASE_OFFSET 0xDB000
1111
#define BMG_DISCOVERY_OFFSET (SOC_BASE + BMG_PMT_BASE_OFFSET)

drivers/gpu/drm/xe/regs/xe_regs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
#include "regs/xe_reg_defs.h"
99

10+
#define SOC_BASE 0x280000
11+
1012
#define GU_CNTL_PROTECTED XE_REG(0x10100C)
1113
#define DRIVERINT_FLR_DIS REG_BIT(31)
1214

drivers/gpu/drm/xe/xe_device.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#include "xe_guc_pc.h"
4444
#include "xe_hw_engine_group.h"
4545
#include "xe_hwmon.h"
46+
#include "xe_i2c.h"
4647
#include "xe_irq.h"
4748
#include "xe_mmio.h"
4849
#include "xe_module.h"
@@ -902,6 +903,10 @@ int xe_device_probe(struct xe_device *xe)
902903
if (err)
903904
goto err_unregister_display;
904905

906+
err = xe_i2c_probe(xe);
907+
if (err)
908+
goto err_unregister_display;
909+
905910
for_each_gt(gt, xe, id)
906911
xe_gt_sanitize_freq(gt);
907912

drivers/gpu/drm/xe/xe_device_types.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ struct dram_info;
3434
struct intel_display;
3535
struct intel_dg_nvm_dev;
3636
struct xe_ggtt;
37+
struct xe_i2c;
3738
struct xe_pat_ops;
3839
struct xe_pxp;
3940

@@ -585,6 +586,9 @@ struct xe_device {
585586
/** @pmu: performance monitoring unit */
586587
struct xe_pmu pmu;
587588

589+
/** @i2c: I2C host controller */
590+
struct xe_i2c *i2c;
591+
588592
/** @atomic_svm_timeslice_ms: Atomic SVM fault timeslice MS */
589593
u32 atomic_svm_timeslice_ms;
590594

drivers/gpu/drm/xe/xe_i2c.c

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

0 commit comments

Comments
 (0)