315 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * TQ-Systems PLD MFD core driver, based on vendor driver by4 * Vadim V.Vlasov <vvlasov@dev.rtsoft.ru>5 *6 * Copyright (c) 2015 TQ-Systems GmbH7 * 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 0x18020#define TQMX86_IOSIZE 0x2021#define TQMX86_IOBASE_I2C 0x1a022#define TQMX86_IOSIZE_I2C 0xa23#define TQMX86_IOBASE_WATCHDOG 0x18b24#define TQMX86_IOSIZE_WATCHDOG 0x225#define TQMX86_IOBASE_GPIO 0x18d26#define TQMX86_IOSIZE_GPIO 0x427 28#define TQMX86_REG_BOARD_ID 0x0029#define TQMX86_REG_BOARD_ID_E38M 130#define TQMX86_REG_BOARD_ID_50UC 231#define TQMX86_REG_BOARD_ID_E38C 332#define TQMX86_REG_BOARD_ID_60EB 433#define TQMX86_REG_BOARD_ID_E39MS 534#define TQMX86_REG_BOARD_ID_E39C1 635#define TQMX86_REG_BOARD_ID_E39C2 736#define TQMX86_REG_BOARD_ID_70EB 837#define TQMX86_REG_BOARD_ID_80UC 938#define TQMX86_REG_BOARD_ID_110EB 1139#define TQMX86_REG_BOARD_ID_E40M 1240#define TQMX86_REG_BOARD_ID_E40S 1341#define TQMX86_REG_BOARD_ID_E40C1 1442#define TQMX86_REG_BOARD_ID_E40C2 1543#define TQMX86_REG_BOARD_REV 0x0144#define TQMX86_REG_IO_EXT_INT 0x0645#define TQMX86_REG_IO_EXT_INT_NONE 046#define TQMX86_REG_IO_EXT_INT_7 147#define TQMX86_REG_IO_EXT_INT_9 248#define TQMX86_REG_IO_EXT_INT_12 349#define TQMX86_REG_IO_EXT_INT_MASK 0x350#define TQMX86_REG_IO_EXT_INT_GPIO_SHIFT 451#define TQMX86_REG_SAUC 0x1752 53#define TQMX86_REG_I2C_DETECT 0x1a754#define TQMX86_REG_I2C_DETECT_SOFT 0xa555 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 the70 * 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, 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(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_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 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, sauc, 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 sauc = ioread8(io_base + TQMX86_REG_SAUC);210 board_name = tqmx86_board_id_to_name(board_id, sauc);211 rev = ioread8(io_base + TQMX86_REG_BOARD_REV);212 213 dev_info(dev,214 "Found %s - Board ID %d, PCB Revision %d, PLD Revision %d\n",215 board_name, board_id, rev >> 4, rev & 0xf);216 217 /*218 * The I2C_DETECT register is in the range assigned to the I2C driver219 * later, so we don't extend TQMX86_IOSIZE. Use inb() for this one-off220 * access instead of ioport_map + unmap.221 */222 i2c_det = inb(TQMX86_REG_I2C_DETECT);223 224 if (gpio_irq_cfg) {225 io_ext_int_val =226 gpio_irq_cfg << TQMX86_REG_IO_EXT_INT_GPIO_SHIFT;227 iowrite8(io_ext_int_val, io_base + TQMX86_REG_IO_EXT_INT);228 readback = ioread8(io_base + TQMX86_REG_IO_EXT_INT);229 if (readback != io_ext_int_val) {230 dev_warn(dev, "GPIO interrupts not supported.\n");231 return -EINVAL;232 }233 234 /* Assumes the IRQ resource is first. */235 tqmx_gpio_resources[0].start = gpio_irq;236 } else {237 tqmx_gpio_resources[0].flags = 0;238 }239 240 ocores_platform_data.clock_khz = tqmx86_board_id_to_clk_rate(dev, board_id);241 242 if (i2c_det == TQMX86_REG_I2C_DETECT_SOFT) {243 err = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,244 tqmx86_i2c_soft_dev,245 ARRAY_SIZE(tqmx86_i2c_soft_dev),246 NULL, 0, NULL);247 if (err)248 return err;249 }250 251 return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,252 tqmx86_devs,253 ARRAY_SIZE(tqmx86_devs),254 NULL, 0, NULL);255}256 257static int tqmx86_create_platform_device(const struct dmi_system_id *id)258{259 struct platform_device *pdev;260 int err;261 262 pdev = platform_device_alloc("tqmx86", -1);263 if (!pdev)264 return -ENOMEM;265 266 err = platform_device_add(pdev);267 if (err)268 platform_device_put(pdev);269 270 return err;271}272 273static const struct dmi_system_id tqmx86_dmi_table[] __initconst = {274 {275 .ident = "TQMX86",276 .matches = {277 DMI_MATCH(DMI_SYS_VENDOR, "TQ-Group"),278 DMI_MATCH(DMI_PRODUCT_NAME, "TQMx"),279 },280 .callback = tqmx86_create_platform_device,281 },282 {283 .ident = "TQMX86",284 .matches = {285 DMI_MATCH(DMI_SYS_VENDOR, "TQ-Systems"),286 DMI_MATCH(DMI_PRODUCT_NAME, "TQMx"),287 },288 .callback = tqmx86_create_platform_device,289 },290 {}291};292MODULE_DEVICE_TABLE(dmi, tqmx86_dmi_table);293 294static struct platform_driver tqmx86_driver = {295 .driver = {296 .name = "tqmx86",297 },298 .probe = tqmx86_probe,299};300 301static int __init tqmx86_init(void)302{303 if (!dmi_check_system(tqmx86_dmi_table))304 return -ENODEV;305 306 return platform_driver_register(&tqmx86_driver);307}308 309module_init(tqmx86_init);310 311MODULE_DESCRIPTION("TQMx86 PLD Core Driver");312MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>");313MODULE_LICENSE("GPL");314MODULE_ALIAS("platform:tqmx86");315