xref: /linux/drivers/mfd/cgbc-core.c (revision 6f1067cfbee72b04fc42234f7f1588f838cec0b6)
1*6f1067cfSThomas Richard // SPDX-License-Identifier: GPL-2.0-or-later
2*6f1067cfSThomas Richard /*
3*6f1067cfSThomas Richard  * Congatec Board Controller core driver.
4*6f1067cfSThomas Richard  *
5*6f1067cfSThomas Richard  * The x86 Congatec modules have an embedded micro controller named Board
6*6f1067cfSThomas Richard  * Controller. This Board Controller has a Watchdog timer, some GPIOs, and two
7*6f1067cfSThomas Richard  * I2C busses.
8*6f1067cfSThomas Richard  *
9*6f1067cfSThomas Richard  * Copyright (C) 2024 Bootlin
10*6f1067cfSThomas Richard  *
11*6f1067cfSThomas Richard  * Author: Thomas Richard <thomas.richard@bootlin.com>
12*6f1067cfSThomas Richard  */
13*6f1067cfSThomas Richard 
14*6f1067cfSThomas Richard #include <linux/dmi.h>
15*6f1067cfSThomas Richard #include <linux/iopoll.h>
16*6f1067cfSThomas Richard #include <linux/mfd/cgbc.h>
17*6f1067cfSThomas Richard #include <linux/mfd/core.h>
18*6f1067cfSThomas Richard #include <linux/module.h>
19*6f1067cfSThomas Richard #include <linux/platform_device.h>
20*6f1067cfSThomas Richard #include <linux/sysfs.h>
21*6f1067cfSThomas Richard 
22*6f1067cfSThomas Richard #define CGBC_IO_SESSION_BASE	0x0E20
23*6f1067cfSThomas Richard #define CGBC_IO_SESSION_END	0x0E30
24*6f1067cfSThomas Richard #define CGBC_IO_CMD_BASE	0x0E00
25*6f1067cfSThomas Richard #define CGBC_IO_CMD_END		0x0E10
26*6f1067cfSThomas Richard 
27*6f1067cfSThomas Richard #define CGBC_MASK_STATUS	(BIT(6) | BIT(7))
28*6f1067cfSThomas Richard #define CGBC_MASK_DATA_COUNT	0x1F
29*6f1067cfSThomas Richard #define CGBC_MASK_ERROR_CODE	0x1F
30*6f1067cfSThomas Richard 
31*6f1067cfSThomas Richard #define CGBC_STATUS_DATA_READY	0x00
32*6f1067cfSThomas Richard #define CGBC_STATUS_CMD_READY	BIT(6)
33*6f1067cfSThomas Richard #define CGBC_STATUS_ERROR	(BIT(6) | BIT(7))
34*6f1067cfSThomas Richard 
35*6f1067cfSThomas Richard #define CGBC_SESSION_CMD		0x00
36*6f1067cfSThomas Richard #define CGBC_SESSION_CMD_IDLE		0x00
37*6f1067cfSThomas Richard #define CGBC_SESSION_CMD_REQUEST	0x01
38*6f1067cfSThomas Richard #define CGBC_SESSION_DATA		0x01
39*6f1067cfSThomas Richard #define CGBC_SESSION_STATUS		0x02
40*6f1067cfSThomas Richard #define CGBC_SESSION_STATUS_FREE	0x03
41*6f1067cfSThomas Richard #define CGBC_SESSION_ACCESS		0x04
42*6f1067cfSThomas Richard #define CGBC_SESSION_ACCESS_GAINED	0x00
43*6f1067cfSThomas Richard 
44*6f1067cfSThomas Richard #define CGBC_SESSION_VALID_MIN  0x02
45*6f1067cfSThomas Richard #define CGBC_SESSION_VALID_MAX  0xFE
46*6f1067cfSThomas Richard 
47*6f1067cfSThomas Richard #define CGBC_CMD_STROBE			0x00
48*6f1067cfSThomas Richard #define CGBC_CMD_INDEX			0x02
49*6f1067cfSThomas Richard #define CGBC_CMD_INDEX_CBM_MAN8		0x00
50*6f1067cfSThomas Richard #define CGBC_CMD_INDEX_CBM_AUTO32	0x03
51*6f1067cfSThomas Richard #define CGBC_CMD_DATA			0x04
52*6f1067cfSThomas Richard #define CGBC_CMD_ACCESS			0x0C
53*6f1067cfSThomas Richard 
54*6f1067cfSThomas Richard #define CGBC_CMD_GET_FW_REV	0x21
55*6f1067cfSThomas Richard 
56*6f1067cfSThomas Richard static struct platform_device *cgbc_pdev;
57*6f1067cfSThomas Richard 
58*6f1067cfSThomas Richard /* Wait the Board Controller is ready to receive some session commands */
59*6f1067cfSThomas Richard static int cgbc_wait_device(struct cgbc_device_data *cgbc)
60*6f1067cfSThomas Richard {
61*6f1067cfSThomas Richard 	u16 status;
62*6f1067cfSThomas Richard 	int ret;
63*6f1067cfSThomas Richard 
64*6f1067cfSThomas Richard 	ret = readx_poll_timeout(ioread16, cgbc->io_session + CGBC_SESSION_STATUS, status,
65*6f1067cfSThomas Richard 				 status == CGBC_SESSION_STATUS_FREE, 0, 500000);
66*6f1067cfSThomas Richard 
67*6f1067cfSThomas Richard 	if (ret || ioread32(cgbc->io_session + CGBC_SESSION_ACCESS))
68*6f1067cfSThomas Richard 		ret = -ENODEV;
69*6f1067cfSThomas Richard 
70*6f1067cfSThomas Richard 	return ret;
71*6f1067cfSThomas Richard }
72*6f1067cfSThomas Richard 
73*6f1067cfSThomas Richard static int cgbc_session_command(struct cgbc_device_data *cgbc, u8 cmd)
74*6f1067cfSThomas Richard {
75*6f1067cfSThomas Richard 	int ret;
76*6f1067cfSThomas Richard 	u8 val;
77*6f1067cfSThomas Richard 
78*6f1067cfSThomas Richard 	ret = readx_poll_timeout(ioread8, cgbc->io_session + CGBC_SESSION_CMD, val,
79*6f1067cfSThomas Richard 				 val == CGBC_SESSION_CMD_IDLE, 0, 100000);
80*6f1067cfSThomas Richard 	if (ret)
81*6f1067cfSThomas Richard 		return ret;
82*6f1067cfSThomas Richard 
83*6f1067cfSThomas Richard 	iowrite8(cmd, cgbc->io_session + CGBC_SESSION_CMD);
84*6f1067cfSThomas Richard 
85*6f1067cfSThomas Richard 	ret = readx_poll_timeout(ioread8, cgbc->io_session + CGBC_SESSION_CMD, val,
86*6f1067cfSThomas Richard 				 val == CGBC_SESSION_CMD_IDLE, 0, 100000);
87*6f1067cfSThomas Richard 	if (ret)
88*6f1067cfSThomas Richard 		return ret;
89*6f1067cfSThomas Richard 
90*6f1067cfSThomas Richard 	ret = (int)ioread8(cgbc->io_session + CGBC_SESSION_DATA);
91*6f1067cfSThomas Richard 
92*6f1067cfSThomas Richard 	iowrite8(CGBC_SESSION_STATUS_FREE, cgbc->io_session + CGBC_SESSION_STATUS);
93*6f1067cfSThomas Richard 
94*6f1067cfSThomas Richard 	return ret;
95*6f1067cfSThomas Richard }
96*6f1067cfSThomas Richard 
97*6f1067cfSThomas Richard static int cgbc_session_request(struct cgbc_device_data *cgbc)
98*6f1067cfSThomas Richard {
99*6f1067cfSThomas Richard 	unsigned int ret;
100*6f1067cfSThomas Richard 
101*6f1067cfSThomas Richard 	ret = cgbc_wait_device(cgbc);
102*6f1067cfSThomas Richard 
103*6f1067cfSThomas Richard 	if (ret)
104*6f1067cfSThomas Richard 		return dev_err_probe(cgbc->dev, ret, "device not found or not ready\n");
105*6f1067cfSThomas Richard 
106*6f1067cfSThomas Richard 	cgbc->session = cgbc_session_command(cgbc, CGBC_SESSION_CMD_REQUEST);
107*6f1067cfSThomas Richard 
108*6f1067cfSThomas Richard 	/* The Board Controller sent us a wrong session handle, we cannot communicate with it */
109*6f1067cfSThomas Richard 	if (cgbc->session < CGBC_SESSION_VALID_MIN || cgbc->session > CGBC_SESSION_VALID_MAX)
110*6f1067cfSThomas Richard 		return dev_err_probe(cgbc->dev, -ECONNREFUSED,
111*6f1067cfSThomas Richard 				     "failed to get a valid session handle\n");
112*6f1067cfSThomas Richard 
113*6f1067cfSThomas Richard 	return 0;
114*6f1067cfSThomas Richard }
115*6f1067cfSThomas Richard 
116*6f1067cfSThomas Richard static void cgbc_session_release(struct cgbc_device_data *cgbc)
117*6f1067cfSThomas Richard {
118*6f1067cfSThomas Richard 	if (cgbc_session_command(cgbc, cgbc->session) != cgbc->session)
119*6f1067cfSThomas Richard 		dev_warn(cgbc->dev, "failed to release session\n");
120*6f1067cfSThomas Richard }
121*6f1067cfSThomas Richard 
122*6f1067cfSThomas Richard static bool cgbc_command_lock(struct cgbc_device_data *cgbc)
123*6f1067cfSThomas Richard {
124*6f1067cfSThomas Richard 	iowrite8(cgbc->session, cgbc->io_cmd + CGBC_CMD_ACCESS);
125*6f1067cfSThomas Richard 
126*6f1067cfSThomas Richard 	return ioread8(cgbc->io_cmd + CGBC_CMD_ACCESS) == cgbc->session;
127*6f1067cfSThomas Richard }
128*6f1067cfSThomas Richard 
129*6f1067cfSThomas Richard static void cgbc_command_unlock(struct cgbc_device_data *cgbc)
130*6f1067cfSThomas Richard {
131*6f1067cfSThomas Richard 	iowrite8(cgbc->session, cgbc->io_cmd + CGBC_CMD_ACCESS);
132*6f1067cfSThomas Richard }
133*6f1067cfSThomas Richard 
134*6f1067cfSThomas Richard int cgbc_command(struct cgbc_device_data *cgbc, void *cmd, unsigned int cmd_size, void *data,
135*6f1067cfSThomas Richard 		 unsigned int data_size, u8 *status)
136*6f1067cfSThomas Richard {
137*6f1067cfSThomas Richard 	u8 checksum = 0, data_checksum = 0, istatus = 0, val;
138*6f1067cfSThomas Richard 	u8 *_data = (u8 *)data;
139*6f1067cfSThomas Richard 	u8 *_cmd = (u8 *)cmd;
140*6f1067cfSThomas Richard 	int mode_change = -1;
141*6f1067cfSThomas Richard 	bool lock;
142*6f1067cfSThomas Richard 	int ret, i;
143*6f1067cfSThomas Richard 
144*6f1067cfSThomas Richard 	mutex_lock(&cgbc->lock);
145*6f1067cfSThomas Richard 
146*6f1067cfSThomas Richard 	/* Request access */
147*6f1067cfSThomas Richard 	ret = readx_poll_timeout(cgbc_command_lock, cgbc, lock, lock, 0, 100000);
148*6f1067cfSThomas Richard 	if (ret)
149*6f1067cfSThomas Richard 		goto out;
150*6f1067cfSThomas Richard 
151*6f1067cfSThomas Richard 	/* Wait board controller is ready */
152*6f1067cfSThomas Richard 	ret = readx_poll_timeout(ioread8, cgbc->io_cmd + CGBC_CMD_STROBE, val,
153*6f1067cfSThomas Richard 				 val == CGBC_CMD_STROBE, 0, 100000);
154*6f1067cfSThomas Richard 	if (ret)
155*6f1067cfSThomas Richard 		goto release;
156*6f1067cfSThomas Richard 
157*6f1067cfSThomas Richard 	/* Write command packet */
158*6f1067cfSThomas Richard 	if (cmd_size <= 2) {
159*6f1067cfSThomas Richard 		iowrite8(CGBC_CMD_INDEX_CBM_MAN8, cgbc->io_cmd + CGBC_CMD_INDEX);
160*6f1067cfSThomas Richard 	} else {
161*6f1067cfSThomas Richard 		iowrite8(CGBC_CMD_INDEX_CBM_AUTO32, cgbc->io_cmd + CGBC_CMD_INDEX);
162*6f1067cfSThomas Richard 		if ((cmd_size % 4) != 0x03)
163*6f1067cfSThomas Richard 			mode_change = (cmd_size & 0xFFFC) - 1;
164*6f1067cfSThomas Richard 	}
165*6f1067cfSThomas Richard 
166*6f1067cfSThomas Richard 	for (i = 0; i < cmd_size; i++) {
167*6f1067cfSThomas Richard 		iowrite8(_cmd[i], cgbc->io_cmd + CGBC_CMD_DATA + (i % 4));
168*6f1067cfSThomas Richard 		checksum ^= _cmd[i];
169*6f1067cfSThomas Richard 		if (mode_change == i)
170*6f1067cfSThomas Richard 			iowrite8((i + 1) | CGBC_CMD_INDEX_CBM_MAN8, cgbc->io_cmd + CGBC_CMD_INDEX);
171*6f1067cfSThomas Richard 	}
172*6f1067cfSThomas Richard 
173*6f1067cfSThomas Richard 	/* Append checksum byte */
174*6f1067cfSThomas Richard 	iowrite8(checksum, cgbc->io_cmd + CGBC_CMD_DATA + (i % 4));
175*6f1067cfSThomas Richard 
176*6f1067cfSThomas Richard 	/* Perform command strobe */
177*6f1067cfSThomas Richard 	iowrite8(cgbc->session, cgbc->io_cmd + CGBC_CMD_STROBE);
178*6f1067cfSThomas Richard 
179*6f1067cfSThomas Richard 	/* Rewind cmd buffer index */
180*6f1067cfSThomas Richard 	iowrite8(CGBC_CMD_INDEX_CBM_AUTO32, cgbc->io_cmd + CGBC_CMD_INDEX);
181*6f1067cfSThomas Richard 
182*6f1067cfSThomas Richard 	/* Wait command completion */
183*6f1067cfSThomas Richard 	ret = read_poll_timeout(ioread8, val, val == CGBC_CMD_STROBE, 0, 100000, false,
184*6f1067cfSThomas Richard 				cgbc->io_cmd + CGBC_CMD_STROBE);
185*6f1067cfSThomas Richard 	if (ret)
186*6f1067cfSThomas Richard 		goto release;
187*6f1067cfSThomas Richard 
188*6f1067cfSThomas Richard 	istatus = ioread8(cgbc->io_cmd + CGBC_CMD_DATA);
189*6f1067cfSThomas Richard 	checksum = istatus;
190*6f1067cfSThomas Richard 
191*6f1067cfSThomas Richard 	/* Check command status */
192*6f1067cfSThomas Richard 	switch (istatus & CGBC_MASK_STATUS) {
193*6f1067cfSThomas Richard 	case CGBC_STATUS_DATA_READY:
194*6f1067cfSThomas Richard 		if (istatus > data_size)
195*6f1067cfSThomas Richard 			istatus = data_size;
196*6f1067cfSThomas Richard 		for (i = 0; i < istatus; i++) {
197*6f1067cfSThomas Richard 			_data[i] = ioread8(cgbc->io_cmd + CGBC_CMD_DATA + ((i + 1) % 4));
198*6f1067cfSThomas Richard 			checksum ^= _data[i];
199*6f1067cfSThomas Richard 		}
200*6f1067cfSThomas Richard 		data_checksum = ioread8(cgbc->io_cmd + CGBC_CMD_DATA + ((i + 1) % 4));
201*6f1067cfSThomas Richard 		istatus &= CGBC_MASK_DATA_COUNT;
202*6f1067cfSThomas Richard 		break;
203*6f1067cfSThomas Richard 	case CGBC_STATUS_ERROR:
204*6f1067cfSThomas Richard 	case CGBC_STATUS_CMD_READY:
205*6f1067cfSThomas Richard 		data_checksum = ioread8(cgbc->io_cmd + CGBC_CMD_DATA + 1);
206*6f1067cfSThomas Richard 		if ((istatus & CGBC_MASK_STATUS) == CGBC_STATUS_ERROR)
207*6f1067cfSThomas Richard 			ret = -EIO;
208*6f1067cfSThomas Richard 		istatus = istatus & CGBC_MASK_ERROR_CODE;
209*6f1067cfSThomas Richard 		break;
210*6f1067cfSThomas Richard 	default:
211*6f1067cfSThomas Richard 		data_checksum = ioread8(cgbc->io_cmd + CGBC_CMD_DATA + 1);
212*6f1067cfSThomas Richard 		istatus &= CGBC_MASK_ERROR_CODE;
213*6f1067cfSThomas Richard 		ret = -EIO;
214*6f1067cfSThomas Richard 		break;
215*6f1067cfSThomas Richard 	}
216*6f1067cfSThomas Richard 
217*6f1067cfSThomas Richard 	/* Checksum verification */
218*6f1067cfSThomas Richard 	if (ret == 0 && data_checksum != checksum)
219*6f1067cfSThomas Richard 		ret = -EIO;
220*6f1067cfSThomas Richard 
221*6f1067cfSThomas Richard release:
222*6f1067cfSThomas Richard 	cgbc_command_unlock(cgbc);
223*6f1067cfSThomas Richard 
224*6f1067cfSThomas Richard out:
225*6f1067cfSThomas Richard 	mutex_unlock(&cgbc->lock);
226*6f1067cfSThomas Richard 
227*6f1067cfSThomas Richard 	if (status)
228*6f1067cfSThomas Richard 		*status = istatus;
229*6f1067cfSThomas Richard 
230*6f1067cfSThomas Richard 	return ret;
231*6f1067cfSThomas Richard }
232*6f1067cfSThomas Richard EXPORT_SYMBOL_GPL(cgbc_command);
233*6f1067cfSThomas Richard 
234*6f1067cfSThomas Richard static struct mfd_cell cgbc_devs[] = {
235*6f1067cfSThomas Richard 	{ .name = "cgbc-wdt"	},
236*6f1067cfSThomas Richard 	{ .name = "cgbc-gpio"	},
237*6f1067cfSThomas Richard 	{ .name = "cgbc-i2c", .id = 1 },
238*6f1067cfSThomas Richard 	{ .name = "cgbc-i2c", .id = 2 },
239*6f1067cfSThomas Richard };
240*6f1067cfSThomas Richard 
241*6f1067cfSThomas Richard static int cgbc_map(struct cgbc_device_data *cgbc)
242*6f1067cfSThomas Richard {
243*6f1067cfSThomas Richard 	struct device *dev = cgbc->dev;
244*6f1067cfSThomas Richard 	struct platform_device *pdev = to_platform_device(dev);
245*6f1067cfSThomas Richard 	struct resource *ioport;
246*6f1067cfSThomas Richard 
247*6f1067cfSThomas Richard 	ioport = platform_get_resource(pdev, IORESOURCE_IO, 0);
248*6f1067cfSThomas Richard 	if (!ioport)
249*6f1067cfSThomas Richard 		return -EINVAL;
250*6f1067cfSThomas Richard 
251*6f1067cfSThomas Richard 	cgbc->io_session = devm_ioport_map(dev, ioport->start, resource_size(ioport));
252*6f1067cfSThomas Richard 	if (!cgbc->io_session)
253*6f1067cfSThomas Richard 		return -ENOMEM;
254*6f1067cfSThomas Richard 
255*6f1067cfSThomas Richard 	ioport = platform_get_resource(pdev, IORESOURCE_IO, 1);
256*6f1067cfSThomas Richard 	if (!ioport)
257*6f1067cfSThomas Richard 		return -EINVAL;
258*6f1067cfSThomas Richard 
259*6f1067cfSThomas Richard 	cgbc->io_cmd = devm_ioport_map(dev, ioport->start, resource_size(ioport));
260*6f1067cfSThomas Richard 	if (!cgbc->io_cmd)
261*6f1067cfSThomas Richard 		return -ENOMEM;
262*6f1067cfSThomas Richard 
263*6f1067cfSThomas Richard 	return 0;
264*6f1067cfSThomas Richard }
265*6f1067cfSThomas Richard 
266*6f1067cfSThomas Richard static const struct resource cgbc_resources[] = {
267*6f1067cfSThomas Richard 	{
268*6f1067cfSThomas Richard 		.start  = CGBC_IO_SESSION_BASE,
269*6f1067cfSThomas Richard 		.end    = CGBC_IO_SESSION_END,
270*6f1067cfSThomas Richard 		.flags  = IORESOURCE_IO,
271*6f1067cfSThomas Richard 	},
272*6f1067cfSThomas Richard 	{
273*6f1067cfSThomas Richard 		.start  = CGBC_IO_CMD_BASE,
274*6f1067cfSThomas Richard 		.end    = CGBC_IO_CMD_END,
275*6f1067cfSThomas Richard 		.flags  = IORESOURCE_IO,
276*6f1067cfSThomas Richard 	},
277*6f1067cfSThomas Richard };
278*6f1067cfSThomas Richard 
279*6f1067cfSThomas Richard static ssize_t cgbc_version_show(struct device *dev,
280*6f1067cfSThomas Richard 				 struct device_attribute *attr, char *buf)
281*6f1067cfSThomas Richard {
282*6f1067cfSThomas Richard 	struct cgbc_device_data *cgbc = dev_get_drvdata(dev);
283*6f1067cfSThomas Richard 
284*6f1067cfSThomas Richard 	return sysfs_emit(buf, "CGBCP%c%c%c\n", cgbc->version.feature, cgbc->version.major,
285*6f1067cfSThomas Richard 			  cgbc->version.minor);
286*6f1067cfSThomas Richard }
287*6f1067cfSThomas Richard 
288*6f1067cfSThomas Richard static DEVICE_ATTR_RO(cgbc_version);
289*6f1067cfSThomas Richard 
290*6f1067cfSThomas Richard static struct attribute *cgbc_attrs[] = {
291*6f1067cfSThomas Richard 	&dev_attr_cgbc_version.attr,
292*6f1067cfSThomas Richard 	NULL
293*6f1067cfSThomas Richard };
294*6f1067cfSThomas Richard 
295*6f1067cfSThomas Richard ATTRIBUTE_GROUPS(cgbc);
296*6f1067cfSThomas Richard 
297*6f1067cfSThomas Richard static int cgbc_get_version(struct cgbc_device_data *cgbc)
298*6f1067cfSThomas Richard {
299*6f1067cfSThomas Richard 	u8 cmd = CGBC_CMD_GET_FW_REV;
300*6f1067cfSThomas Richard 	u8 data[4];
301*6f1067cfSThomas Richard 	int ret;
302*6f1067cfSThomas Richard 
303*6f1067cfSThomas Richard 	ret = cgbc_command(cgbc, &cmd, 1, &data, sizeof(data), NULL);
304*6f1067cfSThomas Richard 	if (ret)
305*6f1067cfSThomas Richard 		return ret;
306*6f1067cfSThomas Richard 
307*6f1067cfSThomas Richard 	cgbc->version.feature = data[0];
308*6f1067cfSThomas Richard 	cgbc->version.major = data[1];
309*6f1067cfSThomas Richard 	cgbc->version.minor = data[2];
310*6f1067cfSThomas Richard 
311*6f1067cfSThomas Richard 	return 0;
312*6f1067cfSThomas Richard }
313*6f1067cfSThomas Richard 
314*6f1067cfSThomas Richard static int cgbc_init_device(struct cgbc_device_data *cgbc)
315*6f1067cfSThomas Richard {
316*6f1067cfSThomas Richard 	int ret;
317*6f1067cfSThomas Richard 
318*6f1067cfSThomas Richard 	ret = cgbc_session_request(cgbc);
319*6f1067cfSThomas Richard 	if (ret)
320*6f1067cfSThomas Richard 		return ret;
321*6f1067cfSThomas Richard 
322*6f1067cfSThomas Richard 	ret = cgbc_get_version(cgbc);
323*6f1067cfSThomas Richard 	if (ret)
324*6f1067cfSThomas Richard 		return ret;
325*6f1067cfSThomas Richard 
326*6f1067cfSThomas Richard 	return mfd_add_devices(cgbc->dev, -1, cgbc_devs, ARRAY_SIZE(cgbc_devs), NULL, 0, NULL);
327*6f1067cfSThomas Richard }
328*6f1067cfSThomas Richard 
329*6f1067cfSThomas Richard static int cgbc_probe(struct platform_device *pdev)
330*6f1067cfSThomas Richard {
331*6f1067cfSThomas Richard 	struct device *dev = &pdev->dev;
332*6f1067cfSThomas Richard 	struct cgbc_device_data *cgbc;
333*6f1067cfSThomas Richard 	int ret;
334*6f1067cfSThomas Richard 
335*6f1067cfSThomas Richard 	cgbc = devm_kzalloc(dev, sizeof(*cgbc), GFP_KERNEL);
336*6f1067cfSThomas Richard 	if (!cgbc)
337*6f1067cfSThomas Richard 		return -ENOMEM;
338*6f1067cfSThomas Richard 
339*6f1067cfSThomas Richard 	cgbc->dev = dev;
340*6f1067cfSThomas Richard 
341*6f1067cfSThomas Richard 	ret = cgbc_map(cgbc);
342*6f1067cfSThomas Richard 	if (ret)
343*6f1067cfSThomas Richard 		return ret;
344*6f1067cfSThomas Richard 
345*6f1067cfSThomas Richard 	mutex_init(&cgbc->lock);
346*6f1067cfSThomas Richard 
347*6f1067cfSThomas Richard 	platform_set_drvdata(pdev, cgbc);
348*6f1067cfSThomas Richard 
349*6f1067cfSThomas Richard 	return cgbc_init_device(cgbc);
350*6f1067cfSThomas Richard }
351*6f1067cfSThomas Richard 
352*6f1067cfSThomas Richard static void cgbc_remove(struct platform_device *pdev)
353*6f1067cfSThomas Richard {
354*6f1067cfSThomas Richard 	struct cgbc_device_data *cgbc = platform_get_drvdata(pdev);
355*6f1067cfSThomas Richard 
356*6f1067cfSThomas Richard 	cgbc_session_release(cgbc);
357*6f1067cfSThomas Richard 
358*6f1067cfSThomas Richard 	mfd_remove_devices(&pdev->dev);
359*6f1067cfSThomas Richard }
360*6f1067cfSThomas Richard 
361*6f1067cfSThomas Richard static struct platform_driver cgbc_driver = {
362*6f1067cfSThomas Richard 	.driver		= {
363*6f1067cfSThomas Richard 		.name		= "cgbc",
364*6f1067cfSThomas Richard 		.dev_groups	= cgbc_groups,
365*6f1067cfSThomas Richard 	},
366*6f1067cfSThomas Richard 	.probe		= cgbc_probe,
367*6f1067cfSThomas Richard 	.remove_new	= cgbc_remove,
368*6f1067cfSThomas Richard };
369*6f1067cfSThomas Richard 
370*6f1067cfSThomas Richard static const struct dmi_system_id cgbc_dmi_table[] __initconst = {
371*6f1067cfSThomas Richard 	{
372*6f1067cfSThomas Richard 		.ident = "SA7",
373*6f1067cfSThomas Richard 		.matches = {
374*6f1067cfSThomas Richard 			DMI_MATCH(DMI_BOARD_VENDOR, "congatec"),
375*6f1067cfSThomas Richard 			DMI_MATCH(DMI_BOARD_NAME, "conga-SA7"),
376*6f1067cfSThomas Richard 		},
377*6f1067cfSThomas Richard 	},
378*6f1067cfSThomas Richard 	{}
379*6f1067cfSThomas Richard };
380*6f1067cfSThomas Richard MODULE_DEVICE_TABLE(dmi, cgbc_dmi_table);
381*6f1067cfSThomas Richard 
382*6f1067cfSThomas Richard static int __init cgbc_init(void)
383*6f1067cfSThomas Richard {
384*6f1067cfSThomas Richard 	const struct dmi_system_id *id;
385*6f1067cfSThomas Richard 	int ret = -ENODEV;
386*6f1067cfSThomas Richard 
387*6f1067cfSThomas Richard 	id = dmi_first_match(cgbc_dmi_table);
388*6f1067cfSThomas Richard 	if (IS_ERR_OR_NULL(id))
389*6f1067cfSThomas Richard 		return ret;
390*6f1067cfSThomas Richard 
391*6f1067cfSThomas Richard 	cgbc_pdev = platform_device_register_simple("cgbc", PLATFORM_DEVID_NONE, cgbc_resources,
392*6f1067cfSThomas Richard 						    ARRAY_SIZE(cgbc_resources));
393*6f1067cfSThomas Richard 	if (IS_ERR(cgbc_pdev))
394*6f1067cfSThomas Richard 		return PTR_ERR(cgbc_pdev);
395*6f1067cfSThomas Richard 
396*6f1067cfSThomas Richard 	return platform_driver_register(&cgbc_driver);
397*6f1067cfSThomas Richard }
398*6f1067cfSThomas Richard 
399*6f1067cfSThomas Richard static void __exit cgbc_exit(void)
400*6f1067cfSThomas Richard {
401*6f1067cfSThomas Richard 	platform_device_unregister(cgbc_pdev);
402*6f1067cfSThomas Richard 	platform_driver_unregister(&cgbc_driver);
403*6f1067cfSThomas Richard }
404*6f1067cfSThomas Richard 
405*6f1067cfSThomas Richard module_init(cgbc_init);
406*6f1067cfSThomas Richard module_exit(cgbc_exit);
407*6f1067cfSThomas Richard 
408*6f1067cfSThomas Richard MODULE_DESCRIPTION("Congatec Board Controller Core Driver");
409*6f1067cfSThomas Richard MODULE_AUTHOR("Thomas Richard <thomas.richard@bootlin.com>");
410*6f1067cfSThomas Richard MODULE_LICENSE("GPL");
411*6f1067cfSThomas Richard MODULE_ALIAS("platform:cgbc-core");
412