xref: /linux/drivers/mfd/cgbc-core.c (revision 80739fd00c7ea1315d362ce889bef499452913ef)
16f1067cfSThomas Richard // SPDX-License-Identifier: GPL-2.0-or-later
26f1067cfSThomas Richard /*
36f1067cfSThomas Richard  * Congatec Board Controller core driver.
46f1067cfSThomas Richard  *
56f1067cfSThomas Richard  * The x86 Congatec modules have an embedded micro controller named Board
66f1067cfSThomas Richard  * Controller. This Board Controller has a Watchdog timer, some GPIOs, and two
76f1067cfSThomas Richard  * I2C busses.
86f1067cfSThomas Richard  *
96f1067cfSThomas Richard  * Copyright (C) 2024 Bootlin
106f1067cfSThomas Richard  *
116f1067cfSThomas Richard  * Author: Thomas Richard <thomas.richard@bootlin.com>
126f1067cfSThomas Richard  */
136f1067cfSThomas Richard 
146f1067cfSThomas Richard #include <linux/dmi.h>
156f1067cfSThomas Richard #include <linux/iopoll.h>
166f1067cfSThomas Richard #include <linux/mfd/cgbc.h>
176f1067cfSThomas Richard #include <linux/mfd/core.h>
186f1067cfSThomas Richard #include <linux/module.h>
196f1067cfSThomas Richard #include <linux/platform_device.h>
206f1067cfSThomas Richard #include <linux/sysfs.h>
216f1067cfSThomas Richard 
226f1067cfSThomas Richard #define CGBC_IO_SESSION_BASE	0x0E20
236f1067cfSThomas Richard #define CGBC_IO_SESSION_END	0x0E30
246f1067cfSThomas Richard #define CGBC_IO_CMD_BASE	0x0E00
256f1067cfSThomas Richard #define CGBC_IO_CMD_END		0x0E10
266f1067cfSThomas Richard 
276f1067cfSThomas Richard #define CGBC_MASK_STATUS	(BIT(6) | BIT(7))
286f1067cfSThomas Richard #define CGBC_MASK_DATA_COUNT	0x1F
296f1067cfSThomas Richard #define CGBC_MASK_ERROR_CODE	0x1F
306f1067cfSThomas Richard 
316f1067cfSThomas Richard #define CGBC_STATUS_DATA_READY	0x00
326f1067cfSThomas Richard #define CGBC_STATUS_CMD_READY	BIT(6)
336f1067cfSThomas Richard #define CGBC_STATUS_ERROR	(BIT(6) | BIT(7))
346f1067cfSThomas Richard 
356f1067cfSThomas Richard #define CGBC_SESSION_CMD		0x00
366f1067cfSThomas Richard #define CGBC_SESSION_CMD_IDLE		0x00
376f1067cfSThomas Richard #define CGBC_SESSION_CMD_REQUEST	0x01
386f1067cfSThomas Richard #define CGBC_SESSION_DATA		0x01
396f1067cfSThomas Richard #define CGBC_SESSION_STATUS		0x02
406f1067cfSThomas Richard #define CGBC_SESSION_STATUS_FREE	0x03
416f1067cfSThomas Richard #define CGBC_SESSION_ACCESS		0x04
426f1067cfSThomas Richard #define CGBC_SESSION_ACCESS_GAINED	0x00
436f1067cfSThomas Richard 
446f1067cfSThomas Richard #define CGBC_SESSION_VALID_MIN  0x02
456f1067cfSThomas Richard #define CGBC_SESSION_VALID_MAX  0xFE
466f1067cfSThomas Richard 
476f1067cfSThomas Richard #define CGBC_CMD_STROBE			0x00
486f1067cfSThomas Richard #define CGBC_CMD_INDEX			0x02
496f1067cfSThomas Richard #define CGBC_CMD_INDEX_CBM_MAN8		0x00
506f1067cfSThomas Richard #define CGBC_CMD_INDEX_CBM_AUTO32	0x03
516f1067cfSThomas Richard #define CGBC_CMD_DATA			0x04
526f1067cfSThomas Richard #define CGBC_CMD_ACCESS			0x0C
536f1067cfSThomas Richard 
546f1067cfSThomas Richard #define CGBC_CMD_GET_FW_REV	0x21
556f1067cfSThomas Richard 
566f1067cfSThomas Richard static struct platform_device *cgbc_pdev;
576f1067cfSThomas Richard 
586f1067cfSThomas Richard /* Wait the Board Controller is ready to receive some session commands */
cgbc_wait_device(struct cgbc_device_data * cgbc)596f1067cfSThomas Richard static int cgbc_wait_device(struct cgbc_device_data *cgbc)
606f1067cfSThomas Richard {
616f1067cfSThomas Richard 	u16 status;
626f1067cfSThomas Richard 	int ret;
636f1067cfSThomas Richard 
646f1067cfSThomas Richard 	ret = readx_poll_timeout(ioread16, cgbc->io_session + CGBC_SESSION_STATUS, status,
656f1067cfSThomas Richard 				 status == CGBC_SESSION_STATUS_FREE, 0, 500000);
666f1067cfSThomas Richard 
676f1067cfSThomas Richard 	if (ret || ioread32(cgbc->io_session + CGBC_SESSION_ACCESS))
686f1067cfSThomas Richard 		ret = -ENODEV;
696f1067cfSThomas Richard 
706f1067cfSThomas Richard 	return ret;
716f1067cfSThomas Richard }
726f1067cfSThomas Richard 
cgbc_session_command(struct cgbc_device_data * cgbc,u8 cmd)736f1067cfSThomas Richard static int cgbc_session_command(struct cgbc_device_data *cgbc, u8 cmd)
746f1067cfSThomas Richard {
756f1067cfSThomas Richard 	int ret;
766f1067cfSThomas Richard 	u8 val;
776f1067cfSThomas Richard 
786f1067cfSThomas Richard 	ret = readx_poll_timeout(ioread8, cgbc->io_session + CGBC_SESSION_CMD, val,
796f1067cfSThomas Richard 				 val == CGBC_SESSION_CMD_IDLE, 0, 100000);
806f1067cfSThomas Richard 	if (ret)
816f1067cfSThomas Richard 		return ret;
826f1067cfSThomas Richard 
836f1067cfSThomas Richard 	iowrite8(cmd, cgbc->io_session + CGBC_SESSION_CMD);
846f1067cfSThomas Richard 
856f1067cfSThomas Richard 	ret = readx_poll_timeout(ioread8, cgbc->io_session + CGBC_SESSION_CMD, val,
866f1067cfSThomas Richard 				 val == CGBC_SESSION_CMD_IDLE, 0, 100000);
876f1067cfSThomas Richard 	if (ret)
886f1067cfSThomas Richard 		return ret;
896f1067cfSThomas Richard 
906f1067cfSThomas Richard 	ret = (int)ioread8(cgbc->io_session + CGBC_SESSION_DATA);
916f1067cfSThomas Richard 
926f1067cfSThomas Richard 	iowrite8(CGBC_SESSION_STATUS_FREE, cgbc->io_session + CGBC_SESSION_STATUS);
936f1067cfSThomas Richard 
946f1067cfSThomas Richard 	return ret;
956f1067cfSThomas Richard }
966f1067cfSThomas Richard 
cgbc_session_request(struct cgbc_device_data * cgbc)976f1067cfSThomas Richard static int cgbc_session_request(struct cgbc_device_data *cgbc)
986f1067cfSThomas Richard {
996f1067cfSThomas Richard 	unsigned int ret;
1006f1067cfSThomas Richard 
1016f1067cfSThomas Richard 	ret = cgbc_wait_device(cgbc);
1026f1067cfSThomas Richard 
1036f1067cfSThomas Richard 	if (ret)
1046f1067cfSThomas Richard 		return dev_err_probe(cgbc->dev, ret, "device not found or not ready\n");
1056f1067cfSThomas Richard 
1066f1067cfSThomas Richard 	cgbc->session = cgbc_session_command(cgbc, CGBC_SESSION_CMD_REQUEST);
1076f1067cfSThomas Richard 
1086f1067cfSThomas Richard 	/* The Board Controller sent us a wrong session handle, we cannot communicate with it */
1096f1067cfSThomas Richard 	if (cgbc->session < CGBC_SESSION_VALID_MIN || cgbc->session > CGBC_SESSION_VALID_MAX)
1106f1067cfSThomas Richard 		return dev_err_probe(cgbc->dev, -ECONNREFUSED,
1116f1067cfSThomas Richard 				     "failed to get a valid session handle\n");
1126f1067cfSThomas Richard 
1136f1067cfSThomas Richard 	return 0;
1146f1067cfSThomas Richard }
1156f1067cfSThomas Richard 
cgbc_session_release(struct cgbc_device_data * cgbc)1166f1067cfSThomas Richard static void cgbc_session_release(struct cgbc_device_data *cgbc)
1176f1067cfSThomas Richard {
1186f1067cfSThomas Richard 	if (cgbc_session_command(cgbc, cgbc->session) != cgbc->session)
1196f1067cfSThomas Richard 		dev_warn(cgbc->dev, "failed to release session\n");
1206f1067cfSThomas Richard }
1216f1067cfSThomas Richard 
cgbc_command_lock(struct cgbc_device_data * cgbc)1226f1067cfSThomas Richard static bool cgbc_command_lock(struct cgbc_device_data *cgbc)
1236f1067cfSThomas Richard {
1246f1067cfSThomas Richard 	iowrite8(cgbc->session, cgbc->io_cmd + CGBC_CMD_ACCESS);
1256f1067cfSThomas Richard 
1266f1067cfSThomas Richard 	return ioread8(cgbc->io_cmd + CGBC_CMD_ACCESS) == cgbc->session;
1276f1067cfSThomas Richard }
1286f1067cfSThomas Richard 
cgbc_command_unlock(struct cgbc_device_data * cgbc)1296f1067cfSThomas Richard static void cgbc_command_unlock(struct cgbc_device_data *cgbc)
1306f1067cfSThomas Richard {
1316f1067cfSThomas Richard 	iowrite8(cgbc->session, cgbc->io_cmd + CGBC_CMD_ACCESS);
1326f1067cfSThomas Richard }
1336f1067cfSThomas Richard 
cgbc_command(struct cgbc_device_data * cgbc,void * cmd,unsigned int cmd_size,void * data,unsigned int data_size,u8 * status)1346f1067cfSThomas Richard int cgbc_command(struct cgbc_device_data *cgbc, void *cmd, unsigned int cmd_size, void *data,
1356f1067cfSThomas Richard 		 unsigned int data_size, u8 *status)
1366f1067cfSThomas Richard {
1376f1067cfSThomas Richard 	u8 checksum = 0, data_checksum = 0, istatus = 0, val;
1386f1067cfSThomas Richard 	u8 *_data = (u8 *)data;
1396f1067cfSThomas Richard 	u8 *_cmd = (u8 *)cmd;
1406f1067cfSThomas Richard 	int mode_change = -1;
1416f1067cfSThomas Richard 	bool lock;
1426f1067cfSThomas Richard 	int ret, i;
1436f1067cfSThomas Richard 
1446f1067cfSThomas Richard 	mutex_lock(&cgbc->lock);
1456f1067cfSThomas Richard 
1466f1067cfSThomas Richard 	/* Request access */
1476f1067cfSThomas Richard 	ret = readx_poll_timeout(cgbc_command_lock, cgbc, lock, lock, 0, 100000);
1486f1067cfSThomas Richard 	if (ret)
1496f1067cfSThomas Richard 		goto out;
1506f1067cfSThomas Richard 
1516f1067cfSThomas Richard 	/* Wait board controller is ready */
1526f1067cfSThomas Richard 	ret = readx_poll_timeout(ioread8, cgbc->io_cmd + CGBC_CMD_STROBE, val,
1536f1067cfSThomas Richard 				 val == CGBC_CMD_STROBE, 0, 100000);
1546f1067cfSThomas Richard 	if (ret)
1556f1067cfSThomas Richard 		goto release;
1566f1067cfSThomas Richard 
1576f1067cfSThomas Richard 	/* Write command packet */
1586f1067cfSThomas Richard 	if (cmd_size <= 2) {
1596f1067cfSThomas Richard 		iowrite8(CGBC_CMD_INDEX_CBM_MAN8, cgbc->io_cmd + CGBC_CMD_INDEX);
1606f1067cfSThomas Richard 	} else {
1616f1067cfSThomas Richard 		iowrite8(CGBC_CMD_INDEX_CBM_AUTO32, cgbc->io_cmd + CGBC_CMD_INDEX);
1626f1067cfSThomas Richard 		if ((cmd_size % 4) != 0x03)
1636f1067cfSThomas Richard 			mode_change = (cmd_size & 0xFFFC) - 1;
1646f1067cfSThomas Richard 	}
1656f1067cfSThomas Richard 
1666f1067cfSThomas Richard 	for (i = 0; i < cmd_size; i++) {
1676f1067cfSThomas Richard 		iowrite8(_cmd[i], cgbc->io_cmd + CGBC_CMD_DATA + (i % 4));
1686f1067cfSThomas Richard 		checksum ^= _cmd[i];
1696f1067cfSThomas Richard 		if (mode_change == i)
1706f1067cfSThomas Richard 			iowrite8((i + 1) | CGBC_CMD_INDEX_CBM_MAN8, cgbc->io_cmd + CGBC_CMD_INDEX);
1716f1067cfSThomas Richard 	}
1726f1067cfSThomas Richard 
1736f1067cfSThomas Richard 	/* Append checksum byte */
1746f1067cfSThomas Richard 	iowrite8(checksum, cgbc->io_cmd + CGBC_CMD_DATA + (i % 4));
1756f1067cfSThomas Richard 
1766f1067cfSThomas Richard 	/* Perform command strobe */
1776f1067cfSThomas Richard 	iowrite8(cgbc->session, cgbc->io_cmd + CGBC_CMD_STROBE);
1786f1067cfSThomas Richard 
1796f1067cfSThomas Richard 	/* Rewind cmd buffer index */
1806f1067cfSThomas Richard 	iowrite8(CGBC_CMD_INDEX_CBM_AUTO32, cgbc->io_cmd + CGBC_CMD_INDEX);
1816f1067cfSThomas Richard 
1826f1067cfSThomas Richard 	/* Wait command completion */
1836f1067cfSThomas Richard 	ret = read_poll_timeout(ioread8, val, val == CGBC_CMD_STROBE, 0, 100000, false,
1846f1067cfSThomas Richard 				cgbc->io_cmd + CGBC_CMD_STROBE);
1856f1067cfSThomas Richard 	if (ret)
1866f1067cfSThomas Richard 		goto release;
1876f1067cfSThomas Richard 
1886f1067cfSThomas Richard 	istatus = ioread8(cgbc->io_cmd + CGBC_CMD_DATA);
1896f1067cfSThomas Richard 	checksum = istatus;
1906f1067cfSThomas Richard 
1916f1067cfSThomas Richard 	/* Check command status */
1926f1067cfSThomas Richard 	switch (istatus & CGBC_MASK_STATUS) {
1936f1067cfSThomas Richard 	case CGBC_STATUS_DATA_READY:
1946f1067cfSThomas Richard 		if (istatus > data_size)
1956f1067cfSThomas Richard 			istatus = data_size;
1966f1067cfSThomas Richard 		for (i = 0; i < istatus; i++) {
1976f1067cfSThomas Richard 			_data[i] = ioread8(cgbc->io_cmd + CGBC_CMD_DATA + ((i + 1) % 4));
1986f1067cfSThomas Richard 			checksum ^= _data[i];
1996f1067cfSThomas Richard 		}
2006f1067cfSThomas Richard 		data_checksum = ioread8(cgbc->io_cmd + CGBC_CMD_DATA + ((i + 1) % 4));
2016f1067cfSThomas Richard 		istatus &= CGBC_MASK_DATA_COUNT;
2026f1067cfSThomas Richard 		break;
2036f1067cfSThomas Richard 	case CGBC_STATUS_ERROR:
2046f1067cfSThomas Richard 	case CGBC_STATUS_CMD_READY:
2056f1067cfSThomas Richard 		data_checksum = ioread8(cgbc->io_cmd + CGBC_CMD_DATA + 1);
2066f1067cfSThomas Richard 		if ((istatus & CGBC_MASK_STATUS) == CGBC_STATUS_ERROR)
2076f1067cfSThomas Richard 			ret = -EIO;
2086f1067cfSThomas Richard 		istatus = istatus & CGBC_MASK_ERROR_CODE;
2096f1067cfSThomas Richard 		break;
2106f1067cfSThomas Richard 	default:
2116f1067cfSThomas Richard 		data_checksum = ioread8(cgbc->io_cmd + CGBC_CMD_DATA + 1);
2126f1067cfSThomas Richard 		istatus &= CGBC_MASK_ERROR_CODE;
2136f1067cfSThomas Richard 		ret = -EIO;
2146f1067cfSThomas Richard 		break;
2156f1067cfSThomas Richard 	}
2166f1067cfSThomas Richard 
2176f1067cfSThomas Richard 	/* Checksum verification */
2186f1067cfSThomas Richard 	if (ret == 0 && data_checksum != checksum)
2196f1067cfSThomas Richard 		ret = -EIO;
2206f1067cfSThomas Richard 
2216f1067cfSThomas Richard release:
2226f1067cfSThomas Richard 	cgbc_command_unlock(cgbc);
2236f1067cfSThomas Richard 
2246f1067cfSThomas Richard out:
2256f1067cfSThomas Richard 	mutex_unlock(&cgbc->lock);
2266f1067cfSThomas Richard 
2276f1067cfSThomas Richard 	if (status)
2286f1067cfSThomas Richard 		*status = istatus;
2296f1067cfSThomas Richard 
2306f1067cfSThomas Richard 	return ret;
2316f1067cfSThomas Richard }
2326f1067cfSThomas Richard EXPORT_SYMBOL_GPL(cgbc_command);
2336f1067cfSThomas Richard 
2346f1067cfSThomas Richard static struct mfd_cell cgbc_devs[] = {
2356f1067cfSThomas Richard 	{ .name = "cgbc-wdt"	},
2366f1067cfSThomas Richard 	{ .name = "cgbc-gpio"	},
2376f1067cfSThomas Richard 	{ .name = "cgbc-i2c", .id = 1 },
2386f1067cfSThomas Richard 	{ .name = "cgbc-i2c", .id = 2 },
2396f1067cfSThomas Richard };
2406f1067cfSThomas Richard 
cgbc_map(struct cgbc_device_data * cgbc)2416f1067cfSThomas Richard static int cgbc_map(struct cgbc_device_data *cgbc)
2426f1067cfSThomas Richard {
2436f1067cfSThomas Richard 	struct device *dev = cgbc->dev;
2446f1067cfSThomas Richard 	struct platform_device *pdev = to_platform_device(dev);
2456f1067cfSThomas Richard 	struct resource *ioport;
2466f1067cfSThomas Richard 
2476f1067cfSThomas Richard 	ioport = platform_get_resource(pdev, IORESOURCE_IO, 0);
2486f1067cfSThomas Richard 	if (!ioport)
2496f1067cfSThomas Richard 		return -EINVAL;
2506f1067cfSThomas Richard 
2516f1067cfSThomas Richard 	cgbc->io_session = devm_ioport_map(dev, ioport->start, resource_size(ioport));
2526f1067cfSThomas Richard 	if (!cgbc->io_session)
2536f1067cfSThomas Richard 		return -ENOMEM;
2546f1067cfSThomas Richard 
2556f1067cfSThomas Richard 	ioport = platform_get_resource(pdev, IORESOURCE_IO, 1);
2566f1067cfSThomas Richard 	if (!ioport)
2576f1067cfSThomas Richard 		return -EINVAL;
2586f1067cfSThomas Richard 
2596f1067cfSThomas Richard 	cgbc->io_cmd = devm_ioport_map(dev, ioport->start, resource_size(ioport));
2606f1067cfSThomas Richard 	if (!cgbc->io_cmd)
2616f1067cfSThomas Richard 		return -ENOMEM;
2626f1067cfSThomas Richard 
2636f1067cfSThomas Richard 	return 0;
2646f1067cfSThomas Richard }
2656f1067cfSThomas Richard 
2666f1067cfSThomas Richard static const struct resource cgbc_resources[] = {
2676f1067cfSThomas Richard 	{
2686f1067cfSThomas Richard 		.start  = CGBC_IO_SESSION_BASE,
2696f1067cfSThomas Richard 		.end    = CGBC_IO_SESSION_END,
2706f1067cfSThomas Richard 		.flags  = IORESOURCE_IO,
2716f1067cfSThomas Richard 	},
2726f1067cfSThomas Richard 	{
2736f1067cfSThomas Richard 		.start  = CGBC_IO_CMD_BASE,
2746f1067cfSThomas Richard 		.end    = CGBC_IO_CMD_END,
2756f1067cfSThomas Richard 		.flags  = IORESOURCE_IO,
2766f1067cfSThomas Richard 	},
2776f1067cfSThomas Richard };
2786f1067cfSThomas Richard 
cgbc_version_show(struct device * dev,struct device_attribute * attr,char * buf)2796f1067cfSThomas Richard static ssize_t cgbc_version_show(struct device *dev,
2806f1067cfSThomas Richard 				 struct device_attribute *attr, char *buf)
2816f1067cfSThomas Richard {
2826f1067cfSThomas Richard 	struct cgbc_device_data *cgbc = dev_get_drvdata(dev);
2836f1067cfSThomas Richard 
2846f1067cfSThomas Richard 	return sysfs_emit(buf, "CGBCP%c%c%c\n", cgbc->version.feature, cgbc->version.major,
2856f1067cfSThomas Richard 			  cgbc->version.minor);
2866f1067cfSThomas Richard }
2876f1067cfSThomas Richard 
2886f1067cfSThomas Richard static DEVICE_ATTR_RO(cgbc_version);
2896f1067cfSThomas Richard 
2906f1067cfSThomas Richard static struct attribute *cgbc_attrs[] = {
2916f1067cfSThomas Richard 	&dev_attr_cgbc_version.attr,
2926f1067cfSThomas Richard 	NULL
2936f1067cfSThomas Richard };
2946f1067cfSThomas Richard 
2956f1067cfSThomas Richard ATTRIBUTE_GROUPS(cgbc);
2966f1067cfSThomas Richard 
cgbc_get_version(struct cgbc_device_data * cgbc)2976f1067cfSThomas Richard static int cgbc_get_version(struct cgbc_device_data *cgbc)
2986f1067cfSThomas Richard {
2996f1067cfSThomas Richard 	u8 cmd = CGBC_CMD_GET_FW_REV;
3006f1067cfSThomas Richard 	u8 data[4];
3016f1067cfSThomas Richard 	int ret;
3026f1067cfSThomas Richard 
3036f1067cfSThomas Richard 	ret = cgbc_command(cgbc, &cmd, 1, &data, sizeof(data), NULL);
3046f1067cfSThomas Richard 	if (ret)
3056f1067cfSThomas Richard 		return ret;
3066f1067cfSThomas Richard 
3076f1067cfSThomas Richard 	cgbc->version.feature = data[0];
3086f1067cfSThomas Richard 	cgbc->version.major = data[1];
3096f1067cfSThomas Richard 	cgbc->version.minor = data[2];
3106f1067cfSThomas Richard 
3116f1067cfSThomas Richard 	return 0;
3126f1067cfSThomas Richard }
3136f1067cfSThomas Richard 
cgbc_init_device(struct cgbc_device_data * cgbc)3146f1067cfSThomas Richard static int cgbc_init_device(struct cgbc_device_data *cgbc)
3156f1067cfSThomas Richard {
3166f1067cfSThomas Richard 	int ret;
3176f1067cfSThomas Richard 
3186f1067cfSThomas Richard 	ret = cgbc_session_request(cgbc);
3196f1067cfSThomas Richard 	if (ret)
3206f1067cfSThomas Richard 		return ret;
3216f1067cfSThomas Richard 
3226f1067cfSThomas Richard 	ret = cgbc_get_version(cgbc);
3236f1067cfSThomas Richard 	if (ret)
324*5a700e77SChristophe JAILLET 		goto release_session;
3256f1067cfSThomas Richard 
326*5a700e77SChristophe JAILLET 	ret = mfd_add_devices(cgbc->dev, -1, cgbc_devs, ARRAY_SIZE(cgbc_devs),
327*5a700e77SChristophe JAILLET 			      NULL, 0, NULL);
328*5a700e77SChristophe JAILLET 	if (ret)
329*5a700e77SChristophe JAILLET 		goto release_session;
330*5a700e77SChristophe JAILLET 
331*5a700e77SChristophe JAILLET 	return 0;
332*5a700e77SChristophe JAILLET 
333*5a700e77SChristophe JAILLET release_session:
334*5a700e77SChristophe JAILLET 	cgbc_session_release(cgbc);
335*5a700e77SChristophe JAILLET 	return ret;
3366f1067cfSThomas Richard }
3376f1067cfSThomas Richard 
cgbc_probe(struct platform_device * pdev)3386f1067cfSThomas Richard static int cgbc_probe(struct platform_device *pdev)
3396f1067cfSThomas Richard {
3406f1067cfSThomas Richard 	struct device *dev = &pdev->dev;
3416f1067cfSThomas Richard 	struct cgbc_device_data *cgbc;
3426f1067cfSThomas Richard 	int ret;
3436f1067cfSThomas Richard 
3446f1067cfSThomas Richard 	cgbc = devm_kzalloc(dev, sizeof(*cgbc), GFP_KERNEL);
3456f1067cfSThomas Richard 	if (!cgbc)
3466f1067cfSThomas Richard 		return -ENOMEM;
3476f1067cfSThomas Richard 
3486f1067cfSThomas Richard 	cgbc->dev = dev;
3496f1067cfSThomas Richard 
3506f1067cfSThomas Richard 	ret = cgbc_map(cgbc);
3516f1067cfSThomas Richard 	if (ret)
3526f1067cfSThomas Richard 		return ret;
3536f1067cfSThomas Richard 
3546f1067cfSThomas Richard 	mutex_init(&cgbc->lock);
3556f1067cfSThomas Richard 
3566f1067cfSThomas Richard 	platform_set_drvdata(pdev, cgbc);
3576f1067cfSThomas Richard 
3586f1067cfSThomas Richard 	return cgbc_init_device(cgbc);
3596f1067cfSThomas Richard }
3606f1067cfSThomas Richard 
cgbc_remove(struct platform_device * pdev)3616f1067cfSThomas Richard static void cgbc_remove(struct platform_device *pdev)
3626f1067cfSThomas Richard {
3636f1067cfSThomas Richard 	struct cgbc_device_data *cgbc = platform_get_drvdata(pdev);
3646f1067cfSThomas Richard 
3656f1067cfSThomas Richard 	cgbc_session_release(cgbc);
3666f1067cfSThomas Richard 
3676f1067cfSThomas Richard 	mfd_remove_devices(&pdev->dev);
3686f1067cfSThomas Richard }
3696f1067cfSThomas Richard 
3706f1067cfSThomas Richard static struct platform_driver cgbc_driver = {
3716f1067cfSThomas Richard 	.driver		= {
3726f1067cfSThomas Richard 		.name		= "cgbc",
3736f1067cfSThomas Richard 		.dev_groups	= cgbc_groups,
3746f1067cfSThomas Richard 	},
3756f1067cfSThomas Richard 	.probe		= cgbc_probe,
37610821a06SUwe Kleine-König 	.remove		= cgbc_remove,
3776f1067cfSThomas Richard };
3786f1067cfSThomas Richard 
3796f1067cfSThomas Richard static const struct dmi_system_id cgbc_dmi_table[] __initconst = {
3806f1067cfSThomas Richard 	{
3816f1067cfSThomas Richard 		.ident = "SA7",
3826f1067cfSThomas Richard 		.matches = {
3836f1067cfSThomas Richard 			DMI_MATCH(DMI_BOARD_VENDOR, "congatec"),
3846f1067cfSThomas Richard 			DMI_MATCH(DMI_BOARD_NAME, "conga-SA7"),
3856f1067cfSThomas Richard 		},
3866f1067cfSThomas Richard 	},
3876f1067cfSThomas Richard 	{}
3886f1067cfSThomas Richard };
3896f1067cfSThomas Richard MODULE_DEVICE_TABLE(dmi, cgbc_dmi_table);
3906f1067cfSThomas Richard 
cgbc_init(void)3916f1067cfSThomas Richard static int __init cgbc_init(void)
3926f1067cfSThomas Richard {
3936f1067cfSThomas Richard 	const struct dmi_system_id *id;
3946f1067cfSThomas Richard 	int ret = -ENODEV;
3956f1067cfSThomas Richard 
3966f1067cfSThomas Richard 	id = dmi_first_match(cgbc_dmi_table);
3976f1067cfSThomas Richard 	if (IS_ERR_OR_NULL(id))
3986f1067cfSThomas Richard 		return ret;
3996f1067cfSThomas Richard 
4006f1067cfSThomas Richard 	cgbc_pdev = platform_device_register_simple("cgbc", PLATFORM_DEVID_NONE, cgbc_resources,
4016f1067cfSThomas Richard 						    ARRAY_SIZE(cgbc_resources));
4026f1067cfSThomas Richard 	if (IS_ERR(cgbc_pdev))
4036f1067cfSThomas Richard 		return PTR_ERR(cgbc_pdev);
4046f1067cfSThomas Richard 
4056f1067cfSThomas Richard 	return platform_driver_register(&cgbc_driver);
4066f1067cfSThomas Richard }
4076f1067cfSThomas Richard 
cgbc_exit(void)4086f1067cfSThomas Richard static void __exit cgbc_exit(void)
4096f1067cfSThomas Richard {
4106f1067cfSThomas Richard 	platform_device_unregister(cgbc_pdev);
4116f1067cfSThomas Richard 	platform_driver_unregister(&cgbc_driver);
4126f1067cfSThomas Richard }
4136f1067cfSThomas Richard 
4146f1067cfSThomas Richard module_init(cgbc_init);
4156f1067cfSThomas Richard module_exit(cgbc_exit);
4166f1067cfSThomas Richard 
4176f1067cfSThomas Richard MODULE_DESCRIPTION("Congatec Board Controller Core Driver");
4186f1067cfSThomas Richard MODULE_AUTHOR("Thomas Richard <thomas.richard@bootlin.com>");
4196f1067cfSThomas Richard MODULE_LICENSE("GPL");
4206f1067cfSThomas Richard MODULE_ALIAS("platform:cgbc-core");
421