1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * TQ-Systems PLD MFD core driver, based on vendor driver by 4 * Vadim V.Vlasov <vvlasov@dev.rtsoft.ru> 5 * 6 * Copyright (c) 2015 TQ-Systems GmbH 7 * Copyright (c) 2019 Andrew Lunn <andrew@lunn.ch> 8 */ 9 10 #include <linux/delay.h> 11 #include <linux/dmi.h> 12 #include <linux/i2c.h> 13 #include <linux/io.h> 14 #include <linux/mfd/core.h> 15 #include <linux/module.h> 16 #include <linux/platform_data/i2c-ocores.h> 17 #include <linux/platform_device.h> 18 19 #define TQMX86_IOBASE 0x180 20 #define TQMX86_IOSIZE 0x20 21 #define TQMX86_IOBASE_I2C 0x1a0 22 #define TQMX86_IOSIZE_I2C 0xa 23 #define TQMX86_IOBASE_WATCHDOG 0x18b 24 #define TQMX86_IOSIZE_WATCHDOG 0x2 25 #define TQMX86_IOBASE_GPIO 0x18d 26 #define TQMX86_IOSIZE_GPIO 0x4 27 28 #define TQMX86_REG_BOARD_ID 0x00 29 #define TQMX86_REG_BOARD_ID_E38M 1 30 #define TQMX86_REG_BOARD_ID_50UC 2 31 #define TQMX86_REG_BOARD_ID_E38C 3 32 #define TQMX86_REG_BOARD_ID_60EB 4 33 #define TQMX86_REG_BOARD_ID_E39MS 5 34 #define TQMX86_REG_BOARD_ID_E39C1 6 35 #define TQMX86_REG_BOARD_ID_E39C2 7 36 #define TQMX86_REG_BOARD_ID_70EB 8 37 #define TQMX86_REG_BOARD_ID_80UC 9 38 #define TQMX86_REG_BOARD_ID_120UC 10 39 #define TQMX86_REG_BOARD_ID_110EB 11 40 #define TQMX86_REG_BOARD_ID_E40M 12 41 #define TQMX86_REG_BOARD_ID_E40S 13 42 #define TQMX86_REG_BOARD_ID_E40C1 14 43 #define TQMX86_REG_BOARD_ID_E40C2 15 44 #define TQMX86_REG_BOARD_ID_130UC 16 45 #define TQMX86_REG_BOARD_ID_E41S 19 46 #define TQMX86_REG_BOARD_ID_CU1_HPCM 24 47 #define TQMX86_REG_BOARD_ID_CU2_HPCM 25 48 #define TQMX86_REG_BOARD_REV 0x01 49 #define TQMX86_REG_IO_EXT_INT 0x06 50 #define TQMX86_REG_IO_EXT_INT_NONE 0 51 #define TQMX86_REG_IO_EXT_INT_7 1 52 #define TQMX86_REG_IO_EXT_INT_9 2 53 #define TQMX86_REG_IO_EXT_INT_12 3 54 #define TQMX86_REG_IO_EXT_INT_MASK 0x3 55 #define TQMX86_REG_IO_EXT_INT_I2C1_SHIFT 0 56 #define TQMX86_REG_IO_EXT_INT_GPIO_SHIFT 4 57 #define TQMX86_REG_SAUC 0x17 58 59 #define TQMX86_REG_I2C_DETECT 0x1a7 60 #define TQMX86_REG_I2C_DETECT_SOFT 0xa5 61 62 static uint gpio_irq; 63 module_param(gpio_irq, uint, 0); 64 MODULE_PARM_DESC(gpio_irq, "GPIO IRQ number (valid parameters: 7, 9, 12)"); 65 66 static uint i2c1_irq; 67 module_param(i2c1_irq, uint, 0); 68 MODULE_PARM_DESC(i2c1_irq, "I2C1 IRQ number (valid parameters: 7, 9, 12)"); 69 70 enum tqmx86_i2c1_resource_type { 71 TQMX86_I2C1_IO, 72 TQMX86_I2C1_IRQ, 73 }; 74 75 static struct resource tqmx_i2c_soft_resources[] = { 76 [TQMX86_I2C1_IO] = DEFINE_RES_IO(TQMX86_IOBASE_I2C, TQMX86_IOSIZE_I2C), 77 /* Placeholder for IRQ resource */ 78 [TQMX86_I2C1_IRQ] = {}, 79 }; 80 81 static const struct resource tqmx_watchdog_resources[] = { 82 DEFINE_RES_IO(TQMX86_IOBASE_WATCHDOG, TQMX86_IOSIZE_WATCHDOG), 83 }; 84 85 enum tqmx86_gpio_resource_type { 86 TQMX86_GPIO_IO, 87 TQMX86_GPIO_IRQ, 88 }; 89 90 static struct resource tqmx_gpio_resources[] = { 91 [TQMX86_GPIO_IO] = DEFINE_RES_IO(TQMX86_IOBASE_GPIO, TQMX86_IOSIZE_GPIO), 92 /* Placeholder for IRQ resource */ 93 [TQMX86_GPIO_IRQ] = {}, 94 }; 95 96 static struct i2c_board_info tqmx86_i2c_devices[] = { 97 { 98 /* 4K EEPROM at 0x50 */ 99 I2C_BOARD_INFO("24c32", 0x50), 100 }, 101 }; 102 103 static struct ocores_i2c_platform_data ocores_platform_data = { 104 .num_devices = ARRAY_SIZE(tqmx86_i2c_devices), 105 .devices = tqmx86_i2c_devices, 106 }; 107 108 static const struct mfd_cell tqmx86_i2c_soft_dev[] = { 109 { 110 .name = "ocores-i2c", 111 .platform_data = &ocores_platform_data, 112 .pdata_size = sizeof(ocores_platform_data), 113 .resources = tqmx_i2c_soft_resources, 114 .num_resources = ARRAY_SIZE(tqmx_i2c_soft_resources), 115 }, 116 }; 117 118 static const struct mfd_cell tqmx86_devs[] = { 119 { 120 .name = "tqmx86-wdt", 121 .resources = tqmx_watchdog_resources, 122 .num_resources = ARRAY_SIZE(tqmx_watchdog_resources), 123 .ignore_resource_conflicts = true, 124 }, 125 { 126 .name = "tqmx86-gpio", 127 .resources = tqmx_gpio_resources, 128 .num_resources = ARRAY_SIZE(tqmx_gpio_resources), 129 .ignore_resource_conflicts = true, 130 }, 131 }; 132 133 static const char *tqmx86_board_id_to_name(u8 board_id, u8 sauc) 134 { 135 switch (board_id) { 136 case TQMX86_REG_BOARD_ID_E38M: 137 return "TQMxE38M"; 138 case TQMX86_REG_BOARD_ID_50UC: 139 return "TQMx50UC"; 140 case TQMX86_REG_BOARD_ID_E38C: 141 return "TQMxE38C"; 142 case TQMX86_REG_BOARD_ID_60EB: 143 return "TQMx60EB"; 144 case TQMX86_REG_BOARD_ID_E39MS: 145 return (sauc == 0xff) ? "TQMxE39M" : "TQMxE39S"; 146 case TQMX86_REG_BOARD_ID_E39C1: 147 return "TQMxE39C1"; 148 case TQMX86_REG_BOARD_ID_E39C2: 149 return "TQMxE39C2"; 150 case TQMX86_REG_BOARD_ID_70EB: 151 return "TQMx70EB"; 152 case TQMX86_REG_BOARD_ID_80UC: 153 return "TQMx80UC"; 154 case TQMX86_REG_BOARD_ID_120UC: 155 return "TQMx120UC"; 156 case TQMX86_REG_BOARD_ID_110EB: 157 return "TQMx110EB"; 158 case TQMX86_REG_BOARD_ID_E40M: 159 return "TQMxE40M"; 160 case TQMX86_REG_BOARD_ID_E40S: 161 return "TQMxE40S"; 162 case TQMX86_REG_BOARD_ID_E40C1: 163 return "TQMxE40C1"; 164 case TQMX86_REG_BOARD_ID_E40C2: 165 return "TQMxE40C2"; 166 case TQMX86_REG_BOARD_ID_130UC: 167 return "TQMx130UC"; 168 case TQMX86_REG_BOARD_ID_E41S: 169 return "TQMxE41S"; 170 case TQMX86_REG_BOARD_ID_CU1_HPCM: 171 return "TQMxCU1-HPCM"; 172 case TQMX86_REG_BOARD_ID_CU2_HPCM: 173 return "TQMxCU2-HPCM"; 174 default: 175 return "Unknown"; 176 } 177 } 178 179 static int tqmx86_board_id_to_clk_rate(struct device *dev, u8 board_id) 180 { 181 switch (board_id) { 182 case TQMX86_REG_BOARD_ID_50UC: 183 case TQMX86_REG_BOARD_ID_60EB: 184 case TQMX86_REG_BOARD_ID_70EB: 185 case TQMX86_REG_BOARD_ID_80UC: 186 case TQMX86_REG_BOARD_ID_120UC: 187 case TQMX86_REG_BOARD_ID_110EB: 188 case TQMX86_REG_BOARD_ID_E40M: 189 case TQMX86_REG_BOARD_ID_E40S: 190 case TQMX86_REG_BOARD_ID_E40C1: 191 case TQMX86_REG_BOARD_ID_E40C2: 192 case TQMX86_REG_BOARD_ID_130UC: 193 case TQMX86_REG_BOARD_ID_E41S: 194 case TQMX86_REG_BOARD_ID_CU1_HPCM: 195 case TQMX86_REG_BOARD_ID_CU2_HPCM: 196 return 24000; 197 case TQMX86_REG_BOARD_ID_E39MS: 198 case TQMX86_REG_BOARD_ID_E39C1: 199 case TQMX86_REG_BOARD_ID_E39C2: 200 return 25000; 201 case TQMX86_REG_BOARD_ID_E38M: 202 case TQMX86_REG_BOARD_ID_E38C: 203 return 33000; 204 default: 205 dev_warn(dev, "unknown board %d, assuming 24MHz LPC clock\n", 206 board_id); 207 return 24000; 208 } 209 } 210 211 static int tqmx86_setup_irq(struct device *dev, const char *label, u8 irq, 212 void __iomem *io_base, u8 reg_shift) 213 { 214 u8 val, readback; 215 int irq_cfg; 216 217 switch (irq) { 218 case 0: 219 irq_cfg = TQMX86_REG_IO_EXT_INT_NONE; 220 break; 221 case 7: 222 irq_cfg = TQMX86_REG_IO_EXT_INT_7; 223 break; 224 case 9: 225 irq_cfg = TQMX86_REG_IO_EXT_INT_9; 226 break; 227 case 12: 228 irq_cfg = TQMX86_REG_IO_EXT_INT_12; 229 break; 230 default: 231 dev_err(dev, "invalid %s IRQ (%d)\n", label, irq); 232 return -EINVAL; 233 } 234 235 val = ioread8(io_base + TQMX86_REG_IO_EXT_INT); 236 val &= ~(TQMX86_REG_IO_EXT_INT_MASK << reg_shift); 237 val |= (irq_cfg & TQMX86_REG_IO_EXT_INT_MASK) << reg_shift; 238 239 iowrite8(val, io_base + TQMX86_REG_IO_EXT_INT); 240 readback = ioread8(io_base + TQMX86_REG_IO_EXT_INT); 241 if (readback != val) { 242 dev_warn(dev, "%s interrupts not supported\n", label); 243 return -EINVAL; 244 } 245 246 return 0; 247 } 248 249 static int tqmx86_probe(struct platform_device *pdev) 250 { 251 u8 board_id, sauc, rev, i2c_det; 252 struct device *dev = &pdev->dev; 253 const char *board_name; 254 void __iomem *io_base; 255 int err; 256 257 io_base = devm_ioport_map(dev, TQMX86_IOBASE, TQMX86_IOSIZE); 258 if (!io_base) 259 return -ENOMEM; 260 261 board_id = ioread8(io_base + TQMX86_REG_BOARD_ID); 262 sauc = ioread8(io_base + TQMX86_REG_SAUC); 263 board_name = tqmx86_board_id_to_name(board_id, sauc); 264 rev = ioread8(io_base + TQMX86_REG_BOARD_REV); 265 266 dev_info(dev, 267 "Found %s - Board ID %d, PCB Revision %d, PLD Revision %d\n", 268 board_name, board_id, rev >> 4, rev & 0xf); 269 270 /* 271 * The I2C_DETECT register is in the range assigned to the I2C driver 272 * later, so we don't extend TQMX86_IOSIZE. Use inb() for this one-off 273 * access instead of ioport_map + unmap. 274 */ 275 i2c_det = inb(TQMX86_REG_I2C_DETECT); 276 277 if (gpio_irq) { 278 err = tqmx86_setup_irq(dev, "GPIO", gpio_irq, io_base, 279 TQMX86_REG_IO_EXT_INT_GPIO_SHIFT); 280 if (!err) 281 tqmx_gpio_resources[TQMX86_GPIO_IRQ] = DEFINE_RES_IRQ(gpio_irq); 282 } 283 284 ocores_platform_data.clock_khz = tqmx86_board_id_to_clk_rate(dev, board_id); 285 286 if (i2c_det == TQMX86_REG_I2C_DETECT_SOFT) { 287 if (i2c1_irq) { 288 err = tqmx86_setup_irq(dev, "I2C1", i2c1_irq, io_base, 289 TQMX86_REG_IO_EXT_INT_I2C1_SHIFT); 290 if (!err) 291 tqmx_i2c_soft_resources[TQMX86_I2C1_IRQ] = DEFINE_RES_IRQ(i2c1_irq); 292 } 293 294 err = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, 295 tqmx86_i2c_soft_dev, 296 ARRAY_SIZE(tqmx86_i2c_soft_dev), 297 NULL, 0, NULL); 298 if (err) 299 return err; 300 } 301 302 return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, 303 tqmx86_devs, 304 ARRAY_SIZE(tqmx86_devs), 305 NULL, 0, NULL); 306 } 307 308 static int tqmx86_create_platform_device(const struct dmi_system_id *id) 309 { 310 struct platform_device *pdev; 311 int err; 312 313 pdev = platform_device_alloc("tqmx86", -1); 314 if (!pdev) 315 return -ENOMEM; 316 317 err = platform_device_add(pdev); 318 if (err) 319 platform_device_put(pdev); 320 321 return err; 322 } 323 324 static const struct dmi_system_id tqmx86_dmi_table[] __initconst = { 325 { 326 .ident = "TQMX86", 327 .matches = { 328 DMI_MATCH(DMI_SYS_VENDOR, "TQ-Group"), 329 DMI_MATCH(DMI_PRODUCT_NAME, "TQMx"), 330 }, 331 .callback = tqmx86_create_platform_device, 332 }, 333 { 334 .ident = "TQMX86", 335 .matches = { 336 DMI_MATCH(DMI_SYS_VENDOR, "TQ-Systems"), 337 DMI_MATCH(DMI_PRODUCT_NAME, "TQMx"), 338 }, 339 .callback = tqmx86_create_platform_device, 340 }, 341 {} 342 }; 343 MODULE_DEVICE_TABLE(dmi, tqmx86_dmi_table); 344 345 static struct platform_driver tqmx86_driver = { 346 .driver = { 347 .name = "tqmx86", 348 }, 349 .probe = tqmx86_probe, 350 }; 351 352 static int __init tqmx86_init(void) 353 { 354 if (!dmi_check_system(tqmx86_dmi_table)) 355 return -ENODEV; 356 357 return platform_driver_register(&tqmx86_driver); 358 } 359 360 module_init(tqmx86_init); 361 362 MODULE_DESCRIPTION("TQMx86 PLD Core Driver"); 363 MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>"); 364 MODULE_LICENSE("GPL"); 365 MODULE_ALIAS("platform:tqmx86"); 366