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