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