Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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");