at master 9.6 kB view raw
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 62static uint gpio_irq; 63module_param(gpio_irq, uint, 0); 64MODULE_PARM_DESC(gpio_irq, "GPIO IRQ number (valid parameters: 7, 9, 12)"); 65 66static uint i2c1_irq; 67module_param(i2c1_irq, uint, 0); 68MODULE_PARM_DESC(i2c1_irq, "I2C1 IRQ number (valid parameters: 7, 9, 12)"); 69 70enum tqmx86_i2c1_resource_type { 71 TQMX86_I2C1_IO, 72 TQMX86_I2C1_IRQ, 73}; 74 75static 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 81static const struct resource tqmx_watchdog_resources[] = { 82 DEFINE_RES_IO(TQMX86_IOBASE_WATCHDOG, TQMX86_IOSIZE_WATCHDOG), 83}; 84 85enum tqmx86_gpio_resource_type { 86 TQMX86_GPIO_IO, 87 TQMX86_GPIO_IRQ, 88}; 89 90static 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 96static struct i2c_board_info tqmx86_i2c_devices[] = { 97 { 98 /* 4K EEPROM at 0x50 */ 99 I2C_BOARD_INFO("24c32", 0x50), 100 }, 101}; 102 103static struct ocores_i2c_platform_data ocores_platform_data = { 104 .num_devices = ARRAY_SIZE(tqmx86_i2c_devices), 105 .devices = tqmx86_i2c_devices, 106}; 107 108static 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 118static 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 133static 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 179static 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 211static 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 249static 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 308static 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 324static 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}; 343MODULE_DEVICE_TABLE(dmi, tqmx86_dmi_table); 344 345static struct platform_driver tqmx86_driver = { 346 .driver = { 347 .name = "tqmx86", 348 }, 349 .probe = tqmx86_probe, 350}; 351 352static 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 360module_init(tqmx86_init); 361 362MODULE_DESCRIPTION("TQMx86 PLD Core Driver"); 363MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>"); 364MODULE_LICENSE("GPL"); 365MODULE_ALIAS("platform:tqmx86");