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