tqmx86.c (7850B)
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 0x160 20#define TQMX86_IOSIZE 0x3f 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 0x20 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_E39M 5 34#define TQMX86_REG_BOARD_ID_E39C 6 35#define TQMX86_REG_BOARD_ID_E39x 7 36#define TQMX86_REG_BOARD_ID_70EB 8 37#define TQMX86_REG_BOARD_ID_80UC 9 38#define TQMX86_REG_BOARD_ID_110EB 11 39#define TQMX86_REG_BOARD_ID_E40M 12 40#define TQMX86_REG_BOARD_ID_E40S 13 41#define TQMX86_REG_BOARD_ID_E40C1 14 42#define TQMX86_REG_BOARD_ID_E40C2 15 43#define TQMX86_REG_BOARD_REV 0x21 44#define TQMX86_REG_IO_EXT_INT 0x26 45#define TQMX86_REG_IO_EXT_INT_NONE 0 46#define TQMX86_REG_IO_EXT_INT_7 1 47#define TQMX86_REG_IO_EXT_INT_9 2 48#define TQMX86_REG_IO_EXT_INT_12 3 49#define TQMX86_REG_IO_EXT_INT_MASK 0x3 50#define TQMX86_REG_IO_EXT_INT_GPIO_SHIFT 4 51 52#define TQMX86_REG_I2C_DETECT 0x47 53#define TQMX86_REG_I2C_DETECT_SOFT 0xa5 54#define TQMX86_REG_I2C_INT_EN 0x49 55 56static uint gpio_irq; 57module_param(gpio_irq, uint, 0); 58MODULE_PARM_DESC(gpio_irq, "GPIO IRQ number (7, 9, 12)"); 59 60static const struct resource tqmx_i2c_soft_resources[] = { 61 DEFINE_RES_IO(TQMX86_IOBASE_I2C, TQMX86_IOSIZE_I2C), 62}; 63 64static const struct resource tqmx_watchdog_resources[] = { 65 DEFINE_RES_IO(TQMX86_IOBASE_WATCHDOG, TQMX86_IOSIZE_WATCHDOG), 66}; 67 68/* 69 * The IRQ resource must be first, since it is updated with the 70 * configured IRQ in the probe function. 71 */ 72static struct resource tqmx_gpio_resources[] = { 73 DEFINE_RES_IRQ(0), 74 DEFINE_RES_IO(TQMX86_IOBASE_GPIO, TQMX86_IOSIZE_GPIO), 75}; 76 77static struct i2c_board_info tqmx86_i2c_devices[] = { 78 { 79 /* 4K EEPROM at 0x50 */ 80 I2C_BOARD_INFO("24c32", 0x50), 81 }, 82}; 83 84static struct ocores_i2c_platform_data ocores_platform_data = { 85 .num_devices = ARRAY_SIZE(tqmx86_i2c_devices), 86 .devices = tqmx86_i2c_devices, 87}; 88 89static const struct mfd_cell tqmx86_i2c_soft_dev[] = { 90 { 91 .name = "ocores-i2c", 92 .platform_data = &ocores_platform_data, 93 .pdata_size = sizeof(ocores_platform_data), 94 .resources = tqmx_i2c_soft_resources, 95 .num_resources = ARRAY_SIZE(tqmx_i2c_soft_resources), 96 }, 97}; 98 99static const struct mfd_cell tqmx86_devs[] = { 100 { 101 .name = "tqmx86-wdt", 102 .resources = tqmx_watchdog_resources, 103 .num_resources = ARRAY_SIZE(tqmx_watchdog_resources), 104 .ignore_resource_conflicts = true, 105 }, 106 { 107 .name = "tqmx86-gpio", 108 .resources = tqmx_gpio_resources, 109 .num_resources = ARRAY_SIZE(tqmx_gpio_resources), 110 .ignore_resource_conflicts = true, 111 }, 112}; 113 114static const char *tqmx86_board_id_to_name(u8 board_id) 115{ 116 switch (board_id) { 117 case TQMX86_REG_BOARD_ID_E38M: 118 return "TQMxE38M"; 119 case TQMX86_REG_BOARD_ID_50UC: 120 return "TQMx50UC"; 121 case TQMX86_REG_BOARD_ID_E38C: 122 return "TQMxE38C"; 123 case TQMX86_REG_BOARD_ID_60EB: 124 return "TQMx60EB"; 125 case TQMX86_REG_BOARD_ID_E39M: 126 return "TQMxE39M"; 127 case TQMX86_REG_BOARD_ID_E39C: 128 return "TQMxE39C"; 129 case TQMX86_REG_BOARD_ID_E39x: 130 return "TQMxE39x"; 131 case TQMX86_REG_BOARD_ID_70EB: 132 return "TQMx70EB"; 133 case TQMX86_REG_BOARD_ID_80UC: 134 return "TQMx80UC"; 135 case TQMX86_REG_BOARD_ID_110EB: 136 return "TQMx110EB"; 137 case TQMX86_REG_BOARD_ID_E40M: 138 return "TQMxE40M"; 139 case TQMX86_REG_BOARD_ID_E40S: 140 return "TQMxE40S"; 141 case TQMX86_REG_BOARD_ID_E40C1: 142 return "TQMxE40C1"; 143 case TQMX86_REG_BOARD_ID_E40C2: 144 return "TQMxE40C2"; 145 default: 146 return "Unknown"; 147 } 148} 149 150static int tqmx86_board_id_to_clk_rate(struct device *dev, u8 board_id) 151{ 152 switch (board_id) { 153 case TQMX86_REG_BOARD_ID_50UC: 154 case TQMX86_REG_BOARD_ID_60EB: 155 case TQMX86_REG_BOARD_ID_70EB: 156 case TQMX86_REG_BOARD_ID_80UC: 157 case TQMX86_REG_BOARD_ID_110EB: 158 case TQMX86_REG_BOARD_ID_E40M: 159 case TQMX86_REG_BOARD_ID_E40S: 160 case TQMX86_REG_BOARD_ID_E40C1: 161 case TQMX86_REG_BOARD_ID_E40C2: 162 return 24000; 163 case TQMX86_REG_BOARD_ID_E39M: 164 case TQMX86_REG_BOARD_ID_E39C: 165 case TQMX86_REG_BOARD_ID_E39x: 166 return 25000; 167 case TQMX86_REG_BOARD_ID_E38M: 168 case TQMX86_REG_BOARD_ID_E38C: 169 return 33000; 170 default: 171 dev_warn(dev, "unknown board %d, assuming 24MHz LPC clock\n", 172 board_id); 173 return 24000; 174 } 175} 176 177static int tqmx86_probe(struct platform_device *pdev) 178{ 179 u8 board_id, rev, i2c_det, io_ext_int_val; 180 struct device *dev = &pdev->dev; 181 u8 gpio_irq_cfg, readback; 182 const char *board_name; 183 void __iomem *io_base; 184 int err; 185 186 switch (gpio_irq) { 187 case 0: 188 gpio_irq_cfg = TQMX86_REG_IO_EXT_INT_NONE; 189 break; 190 case 7: 191 gpio_irq_cfg = TQMX86_REG_IO_EXT_INT_7; 192 break; 193 case 9: 194 gpio_irq_cfg = TQMX86_REG_IO_EXT_INT_9; 195 break; 196 case 12: 197 gpio_irq_cfg = TQMX86_REG_IO_EXT_INT_12; 198 break; 199 default: 200 pr_err("tqmx86: Invalid GPIO IRQ (%d)\n", gpio_irq); 201 return -EINVAL; 202 } 203 204 io_base = devm_ioport_map(dev, TQMX86_IOBASE, TQMX86_IOSIZE); 205 if (!io_base) 206 return -ENOMEM; 207 208 board_id = ioread8(io_base + TQMX86_REG_BOARD_ID); 209 board_name = tqmx86_board_id_to_name(board_id); 210 rev = ioread8(io_base + TQMX86_REG_BOARD_REV); 211 212 dev_info(dev, 213 "Found %s - Board ID %d, PCB Revision %d, PLD Revision %d\n", 214 board_name, board_id, rev >> 4, rev & 0xf); 215 216 i2c_det = ioread8(io_base + TQMX86_REG_I2C_DETECT); 217 218 if (gpio_irq_cfg) { 219 io_ext_int_val = 220 gpio_irq_cfg << TQMX86_REG_IO_EXT_INT_GPIO_SHIFT; 221 iowrite8(io_ext_int_val, io_base + TQMX86_REG_IO_EXT_INT); 222 readback = ioread8(io_base + TQMX86_REG_IO_EXT_INT); 223 if (readback != io_ext_int_val) { 224 dev_warn(dev, "GPIO interrupts not supported.\n"); 225 return -EINVAL; 226 } 227 228 /* Assumes the IRQ resource is first. */ 229 tqmx_gpio_resources[0].start = gpio_irq; 230 } else { 231 tqmx_gpio_resources[0].flags = 0; 232 } 233 234 ocores_platform_data.clock_khz = tqmx86_board_id_to_clk_rate(dev, board_id); 235 236 if (i2c_det == TQMX86_REG_I2C_DETECT_SOFT) { 237 err = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, 238 tqmx86_i2c_soft_dev, 239 ARRAY_SIZE(tqmx86_i2c_soft_dev), 240 NULL, 0, NULL); 241 if (err) 242 return err; 243 } 244 245 return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, 246 tqmx86_devs, 247 ARRAY_SIZE(tqmx86_devs), 248 NULL, 0, NULL); 249} 250 251static int tqmx86_create_platform_device(const struct dmi_system_id *id) 252{ 253 struct platform_device *pdev; 254 int err; 255 256 pdev = platform_device_alloc("tqmx86", -1); 257 if (!pdev) 258 return -ENOMEM; 259 260 err = platform_device_add(pdev); 261 if (err) 262 platform_device_put(pdev); 263 264 return err; 265} 266 267static const struct dmi_system_id tqmx86_dmi_table[] __initconst = { 268 { 269 .ident = "TQMX86", 270 .matches = { 271 DMI_MATCH(DMI_SYS_VENDOR, "TQ-Group"), 272 DMI_MATCH(DMI_PRODUCT_NAME, "TQMx"), 273 }, 274 .callback = tqmx86_create_platform_device, 275 }, 276 { 277 .ident = "TQMX86", 278 .matches = { 279 DMI_MATCH(DMI_SYS_VENDOR, "TQ-Systems"), 280 DMI_MATCH(DMI_PRODUCT_NAME, "TQMx"), 281 }, 282 .callback = tqmx86_create_platform_device, 283 }, 284 {} 285}; 286MODULE_DEVICE_TABLE(dmi, tqmx86_dmi_table); 287 288static struct platform_driver tqmx86_driver = { 289 .driver = { 290 .name = "tqmx86", 291 }, 292 .probe = tqmx86_probe, 293}; 294 295static int __init tqmx86_init(void) 296{ 297 if (!dmi_check_system(tqmx86_dmi_table)) 298 return -ENODEV; 299 300 return platform_driver_register(&tqmx86_driver); 301} 302 303module_init(tqmx86_init); 304 305MODULE_DESCRIPTION("TQMx86 PLD Core Driver"); 306MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>"); 307MODULE_LICENSE("GPL"); 308MODULE_ALIAS("platform:tqmx86");