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_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 0x01 44#define TQMX86_REG_IO_EXT_INT 0x06 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#define TQMX86_REG_SAUC 0x17 52 53#define TQMX86_REG_I2C_DETECT 0x1a7 54#define TQMX86_REG_I2C_DETECT_SOFT 0xa5 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_platfom_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_platfom_data, 93 .pdata_size = sizeof(ocores_platfom_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, u8 sauc) 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_E39MS: 126 return (sauc == 0xff) ? "TQMxE39M" : "TQMxE39S"; 127 case TQMX86_REG_BOARD_ID_E39C1: 128 return "TQMxE39C1"; 129 case TQMX86_REG_BOARD_ID_E39C2: 130 return "TQMxE39C2"; 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(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_E39MS: 164 case TQMX86_REG_BOARD_ID_E39C1: 165 case TQMX86_REG_BOARD_ID_E39C2: 166 return 25000; 167 case TQMX86_REG_BOARD_ID_E38M: 168 case TQMX86_REG_BOARD_ID_E38C: 169 return 33000; 170 default: 171 return 0; 172 } 173} 174 175static int tqmx86_probe(struct platform_device *pdev) 176{ 177 u8 board_id, sauc, rev, i2c_det, io_ext_int_val; 178 struct device *dev = &pdev->dev; 179 u8 gpio_irq_cfg, readback; 180 const char *board_name; 181 void __iomem *io_base; 182 int err; 183 184 switch (gpio_irq) { 185 case 0: 186 gpio_irq_cfg = TQMX86_REG_IO_EXT_INT_NONE; 187 break; 188 case 7: 189 gpio_irq_cfg = TQMX86_REG_IO_EXT_INT_7; 190 break; 191 case 9: 192 gpio_irq_cfg = TQMX86_REG_IO_EXT_INT_9; 193 break; 194 case 12: 195 gpio_irq_cfg = TQMX86_REG_IO_EXT_INT_12; 196 break; 197 default: 198 pr_err("tqmx86: Invalid GPIO IRQ (%d)\n", gpio_irq); 199 return -EINVAL; 200 } 201 202 io_base = devm_ioport_map(dev, TQMX86_IOBASE, TQMX86_IOSIZE); 203 if (!io_base) 204 return -ENOMEM; 205 206 board_id = ioread8(io_base + TQMX86_REG_BOARD_ID); 207 sauc = ioread8(io_base + TQMX86_REG_SAUC); 208 board_name = tqmx86_board_id_to_name(board_id, sauc); 209 rev = ioread8(io_base + TQMX86_REG_BOARD_REV); 210 211 dev_info(dev, 212 "Found %s - Board ID %d, PCB Revision %d, PLD Revision %d\n", 213 board_name, board_id, rev >> 4, rev & 0xf); 214 215 /* 216 * The I2C_DETECT register is in the range assigned to the I2C driver 217 * later, so we don't extend TQMX86_IOSIZE. Use inb() for this one-off 218 * access instead of ioport_map + unmap. 219 */ 220 i2c_det = inb(TQMX86_REG_I2C_DETECT); 221 222 if (gpio_irq_cfg) { 223 io_ext_int_val = 224 gpio_irq_cfg << TQMX86_REG_IO_EXT_INT_GPIO_SHIFT; 225 iowrite8(io_ext_int_val, io_base + TQMX86_REG_IO_EXT_INT); 226 readback = ioread8(io_base + TQMX86_REG_IO_EXT_INT); 227 if (readback != io_ext_int_val) { 228 dev_warn(dev, "GPIO interrupts not supported.\n"); 229 return -EINVAL; 230 } 231 232 /* Assumes the IRQ resource is first. */ 233 tqmx_gpio_resources[0].start = gpio_irq; 234 } else { 235 tqmx_gpio_resources[0].flags = 0; 236 } 237 238 ocores_platfom_data.clock_khz = tqmx86_board_id_to_clk_rate(board_id); 239 240 if (i2c_det == TQMX86_REG_I2C_DETECT_SOFT) { 241 err = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, 242 tqmx86_i2c_soft_dev, 243 ARRAY_SIZE(tqmx86_i2c_soft_dev), 244 NULL, 0, NULL); 245 if (err) 246 return err; 247 } 248 249 return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, 250 tqmx86_devs, 251 ARRAY_SIZE(tqmx86_devs), 252 NULL, 0, NULL); 253} 254 255static int tqmx86_create_platform_device(const struct dmi_system_id *id) 256{ 257 struct platform_device *pdev; 258 int err; 259 260 pdev = platform_device_alloc("tqmx86", -1); 261 if (!pdev) 262 return -ENOMEM; 263 264 err = platform_device_add(pdev); 265 if (err) 266 platform_device_put(pdev); 267 268 return err; 269} 270 271static const struct dmi_system_id tqmx86_dmi_table[] __initconst = { 272 { 273 .ident = "TQMX86", 274 .matches = { 275 DMI_MATCH(DMI_SYS_VENDOR, "TQ-Group"), 276 DMI_MATCH(DMI_PRODUCT_NAME, "TQMx"), 277 }, 278 .callback = tqmx86_create_platform_device, 279 }, 280 {} 281}; 282MODULE_DEVICE_TABLE(dmi, tqmx86_dmi_table); 283 284static struct platform_driver tqmx86_driver = { 285 .driver = { 286 .name = "tqmx86", 287 }, 288 .probe = tqmx86_probe, 289}; 290 291static int __init tqmx86_init(void) 292{ 293 if (!dmi_check_system(tqmx86_dmi_table)) 294 return -ENODEV; 295 296 return platform_driver_register(&tqmx86_driver); 297} 298 299module_init(tqmx86_init); 300 301MODULE_DESCRIPTION("TQMx86 PLD Core Driver"); 302MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>"); 303MODULE_LICENSE("GPL"); 304MODULE_ALIAS("platform:tqmx86"); 305