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 */ 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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