File Index
Files and directories
Sign in
FILE

tqmx86.c

File preview with syntax highlighting.

linux-6.19.14/drivers/mfd/tqmx86.c C
  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");
~/ Read-only file preview.