Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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