1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Digital I/O driver for Technologic Systems TS-5500
4 *
5 * Copyright (c) 2012 Savoir-faire Linux Inc.
6 *	Vivien Didelot <vivien.didelot@savoirfairelinux.com>
7 *
8 * Technologic Systems platforms have pin blocks, exposing several Digital
9 * Input/Output lines (DIO). This driver aims to support single pin blocks.
10 * In that sense, the support is not limited to the TS-5500 blocks.
11 * Actually, the following platforms have DIO support:
12 *
13 * TS-5500:
14 *   Documentation: https://docs.embeddedts.com/TS-5500
15 *   Blocks: DIO1, DIO2 and LCD port.
16 *
17 * TS-5600:
18 *   Documentation: https://docs.embeddedts.com/TS-5600
19 *   Blocks: LCD port (identical to TS-5500 LCD).
20 */
21
22#include <linux/bitops.h>
23#include <linux/gpio/driver.h>
24#include <linux/io.h>
25#include <linux/module.h>
26#include <linux/platform_device.h>
27#include <linux/slab.h>
28
29/* List of supported Technologic Systems platforms DIO blocks */
30enum ts5500_blocks { TS5500_DIO1, TS5500_DIO2, TS5500_LCD, TS5600_LCD };
31
32struct ts5500_priv {
33	const struct ts5500_dio *pinout;
34	struct gpio_chip gpio_chip;
35	spinlock_t lock;
36	bool strap;
37	u8 hwirq;
38};
39
40/*
41 * Hex 7D is used to control several blocks (e.g. DIO2 and LCD port).
42 * This flag ensures that the region has been requested by this driver.
43 */
44static bool hex7d_reserved;
45
46/*
47 * This structure is used to describe capabilities of DIO lines,
48 * such as available directions and connected interrupt (if any).
49 */
50struct ts5500_dio {
51	const u8 value_addr;
52	const u8 value_mask;
53	const u8 control_addr;
54	const u8 control_mask;
55	const bool no_input;
56	const bool no_output;
57	const u8 irq;
58};
59
60#define TS5500_DIO_IN_OUT(vaddr, vbit, caddr, cbit)	\
61	{						\
62		.value_addr = vaddr,			\
63		.value_mask = BIT(vbit),		\
64		.control_addr = caddr,			\
65		.control_mask = BIT(cbit),		\
66	}
67
68#define TS5500_DIO_IN(addr, bit)		\
69	{					\
70		.value_addr = addr,		\
71		.value_mask = BIT(bit),		\
72		.no_output = true,		\
73	}
74
75#define TS5500_DIO_IN_IRQ(addr, bit, _irq)	\
76	{					\
77		.value_addr = addr,		\
78		.value_mask = BIT(bit),		\
79		.no_output = true,		\
80		.irq = _irq,			\
81	}
82
83#define TS5500_DIO_OUT(addr, bit)		\
84	{					\
85		.value_addr = addr,		\
86		.value_mask = BIT(bit),		\
87		.no_input = true,		\
88	}
89
90/*
91 * Input/Output DIO lines are programmed in groups of 4. Their values are
92 * available through 4 consecutive bits in a value port, whereas the direction
93 * of these 4 lines is driven by only 1 bit in a control port.
94 */
95#define TS5500_DIO_GROUP(vaddr, vbitfrom, caddr, cbit)		\
96	TS5500_DIO_IN_OUT(vaddr, vbitfrom + 0, caddr, cbit),	\
97	TS5500_DIO_IN_OUT(vaddr, vbitfrom + 1, caddr, cbit),	\
98	TS5500_DIO_IN_OUT(vaddr, vbitfrom + 2, caddr, cbit),	\
99	TS5500_DIO_IN_OUT(vaddr, vbitfrom + 3, caddr, cbit)
100
101/*
102 * TS-5500 DIO1 block
103 *
104 *  value    control  dir    hw
105 *  addr bit addr bit in out irq name     pin offset
106 *
107 *  0x7b  0  0x7a  0  x   x      DIO1_0   1   0
108 *  0x7b  1  0x7a  0  x   x      DIO1_1   3   1
109 *  0x7b  2  0x7a  0  x   x      DIO1_2   5   2
110 *  0x7b  3  0x7a  0  x   x      DIO1_3   7   3
111 *  0x7b  4  0x7a  1  x   x      DIO1_4   9   4
112 *  0x7b  5  0x7a  1  x   x      DIO1_5   11  5
113 *  0x7b  6  0x7a  1  x   x      DIO1_6   13  6
114 *  0x7b  7  0x7a  1  x   x      DIO1_7   15  7
115 *  0x7c  0  0x7a  5  x   x      DIO1_8   4   8
116 *  0x7c  1  0x7a  5  x   x      DIO1_9   6   9
117 *  0x7c  2  0x7a  5  x   x      DIO1_10  8   10
118 *  0x7c  3  0x7a  5  x   x      DIO1_11  10  11
119 *  0x7c  4           x          DIO1_12  12  12
120 *  0x7c  5           x      7   DIO1_13  14  13
121 */
122static const struct ts5500_dio ts5500_dio1[] = {
123	TS5500_DIO_GROUP(0x7b, 0, 0x7a, 0),
124	TS5500_DIO_GROUP(0x7b, 4, 0x7a, 1),
125	TS5500_DIO_GROUP(0x7c, 0, 0x7a, 5),
126	TS5500_DIO_IN(0x7c, 4),
127	TS5500_DIO_IN_IRQ(0x7c, 5, 7),
128};
129
130/*
131 * TS-5500 DIO2 block
132 *
133 *  value    control  dir    hw
134 *  addr bit addr bit in out irq name     pin offset
135 *
136 *  0x7e  0  0x7d  0  x   x      DIO2_0   1   0
137 *  0x7e  1  0x7d  0  x   x      DIO2_1   3   1
138 *  0x7e  2  0x7d  0  x   x      DIO2_2   5   2
139 *  0x7e  3  0x7d  0  x   x      DIO2_3   7   3
140 *  0x7e  4  0x7d  1  x   x      DIO2_4   9   4
141 *  0x7e  5  0x7d  1  x   x      DIO2_5   11  5
142 *  0x7e  6  0x7d  1  x   x      DIO2_6   13  6
143 *  0x7e  7  0x7d  1  x   x      DIO2_7   15  7
144 *  0x7f  0  0x7d  5  x   x      DIO2_8   4   8
145 *  0x7f  1  0x7d  5  x   x      DIO2_9   6   9
146 *  0x7f  2  0x7d  5  x   x      DIO2_10  8   10
147 *  0x7f  3  0x7d  5  x   x      DIO2_11  10  11
148 *  0x7f  4           x      6   DIO2_13  14  12
149 */
150static const struct ts5500_dio ts5500_dio2[] = {
151	TS5500_DIO_GROUP(0x7e, 0, 0x7d, 0),
152	TS5500_DIO_GROUP(0x7e, 4, 0x7d, 1),
153	TS5500_DIO_GROUP(0x7f, 0, 0x7d, 5),
154	TS5500_DIO_IN_IRQ(0x7f, 4, 6),
155};
156
157/*
158 * TS-5500 LCD port used as DIO block
159 * TS-5600 LCD port is identical
160 *
161 *  value    control  dir    hw
162 *  addr bit addr bit in out irq name    pin offset
163 *
164 *  0x72  0  0x7d  2  x   x      LCD_0   8   0
165 *  0x72  1  0x7d  2  x   x      LCD_1   7   1
166 *  0x72  2  0x7d  2  x   x      LCD_2   10  2
167 *  0x72  3  0x7d  2  x   x      LCD_3   9   3
168 *  0x72  4  0x7d  3  x   x      LCD_4   12  4
169 *  0x72  5  0x7d  3  x   x      LCD_5   11  5
170 *  0x72  6  0x7d  3  x   x      LCD_6   14  6
171 *  0x72  7  0x7d  3  x   x      LCD_7   13  7
172 *  0x73  0               x      LCD_EN  5   8
173 *  0x73  6           x          LCD_WR  6   9
174 *  0x73  7           x      1   LCD_RS  3   10
175 */
176static const struct ts5500_dio ts5500_lcd[] = {
177	TS5500_DIO_GROUP(0x72, 0, 0x7d, 2),
178	TS5500_DIO_GROUP(0x72, 4, 0x7d, 3),
179	TS5500_DIO_OUT(0x73, 0),
180	TS5500_DIO_IN(0x73, 6),
181	TS5500_DIO_IN_IRQ(0x73, 7, 1),
182};
183
184static inline void ts5500_set_mask(u8 mask, u8 addr)
185{
186	u8 val = inb(addr);
187	val |= mask;
188	outb(val, addr);
189}
190
191static inline void ts5500_clear_mask(u8 mask, u8 addr)
192{
193	u8 val = inb(addr);
194	val &= ~mask;
195	outb(val, addr);
196}
197
198static int ts5500_gpio_input(struct gpio_chip *chip, unsigned offset)
199{
200	struct ts5500_priv *priv = gpiochip_get_data(chip);
201	const struct ts5500_dio line = priv->pinout[offset];
202	unsigned long flags;
203
204	if (line.no_input)
205		return -ENXIO;
206
207	if (line.no_output)
208		return 0;
209
210	spin_lock_irqsave(&priv->lock, flags);
211	ts5500_clear_mask(line.control_mask, line.control_addr);
212	spin_unlock_irqrestore(&priv->lock, flags);
213
214	return 0;
215}
216
217static int ts5500_gpio_get(struct gpio_chip *chip, unsigned offset)
218{
219	struct ts5500_priv *priv = gpiochip_get_data(chip);
220	const struct ts5500_dio line = priv->pinout[offset];
221
222	return !!(inb(line.value_addr) & line.value_mask);
223}
224
225static int ts5500_gpio_output(struct gpio_chip *chip, unsigned offset, int val)
226{
227	struct ts5500_priv *priv = gpiochip_get_data(chip);
228	const struct ts5500_dio line = priv->pinout[offset];
229	unsigned long flags;
230
231	if (line.no_output)
232		return -ENXIO;
233
234	spin_lock_irqsave(&priv->lock, flags);
235	if (!line.no_input)
236		ts5500_set_mask(line.control_mask, line.control_addr);
237
238	if (val)
239		ts5500_set_mask(line.value_mask, line.value_addr);
240	else
241		ts5500_clear_mask(line.value_mask, line.value_addr);
242	spin_unlock_irqrestore(&priv->lock, flags);
243
244	return 0;
245}
246
247static void ts5500_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
248{
249	struct ts5500_priv *priv = gpiochip_get_data(chip);
250	const struct ts5500_dio line = priv->pinout[offset];
251	unsigned long flags;
252
253	spin_lock_irqsave(&priv->lock, flags);
254	if (val)
255		ts5500_set_mask(line.value_mask, line.value_addr);
256	else
257		ts5500_clear_mask(line.value_mask, line.value_addr);
258	spin_unlock_irqrestore(&priv->lock, flags);
259}
260
261static int ts5500_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
262{
263	struct ts5500_priv *priv = gpiochip_get_data(chip);
264	const struct ts5500_dio *block = priv->pinout;
265	const struct ts5500_dio line = block[offset];
266
267	/* Only one pin is connected to an interrupt */
268	if (line.irq)
269		return line.irq;
270
271	/* As this pin is input-only, we may strap it to another in/out pin */
272	if (priv->strap)
273		return priv->hwirq;
274
275	return -ENXIO;
276}
277
278static int ts5500_enable_irq(struct ts5500_priv *priv)
279{
280	int ret = 0;
281	unsigned long flags;
282
283	spin_lock_irqsave(&priv->lock, flags);
284	if (priv->hwirq == 7)
285		ts5500_set_mask(BIT(7), 0x7a); /* DIO1_13 on IRQ7 */
286	else if (priv->hwirq == 6)
287		ts5500_set_mask(BIT(7), 0x7d); /* DIO2_13 on IRQ6 */
288	else if (priv->hwirq == 1)
289		ts5500_set_mask(BIT(6), 0x7d); /* LCD_RS on IRQ1 */
290	else
291		ret = -EINVAL;
292	spin_unlock_irqrestore(&priv->lock, flags);
293
294	return ret;
295}
296
297static void ts5500_disable_irq(struct ts5500_priv *priv)
298{
299	unsigned long flags;
300
301	spin_lock_irqsave(&priv->lock, flags);
302	if (priv->hwirq == 7)
303		ts5500_clear_mask(BIT(7), 0x7a); /* DIO1_13 on IRQ7 */
304	else if (priv->hwirq == 6)
305		ts5500_clear_mask(BIT(7), 0x7d); /* DIO2_13 on IRQ6 */
306	else if (priv->hwirq == 1)
307		ts5500_clear_mask(BIT(6), 0x7d); /* LCD_RS on IRQ1 */
308	else
309		dev_err(priv->gpio_chip.parent, "invalid hwirq %d\n",
310			priv->hwirq);
311	spin_unlock_irqrestore(&priv->lock, flags);
312}
313
314static int ts5500_dio_probe(struct platform_device *pdev)
315{
316	enum ts5500_blocks block = platform_get_device_id(pdev)->driver_data;
317	struct device *dev = &pdev->dev;
318	const char *name = dev_name(dev);
319	struct ts5500_priv *priv;
320	unsigned long flags;
321	int ret;
322
323	ret = platform_get_irq(pdev, 0);
324	if (ret < 0)
325		return ret;
326
327	priv = devm_kzalloc(dev, sizeof(struct ts5500_priv), GFP_KERNEL);
328	if (!priv)
329		return -ENOMEM;
330
331	platform_set_drvdata(pdev, priv);
332	priv->hwirq = ret;
333	spin_lock_init(&priv->lock);
334
335	priv->gpio_chip.owner = THIS_MODULE;
336	priv->gpio_chip.label = name;
337	priv->gpio_chip.parent = dev;
338	priv->gpio_chip.direction_input = ts5500_gpio_input;
339	priv->gpio_chip.direction_output = ts5500_gpio_output;
340	priv->gpio_chip.get = ts5500_gpio_get;
341	priv->gpio_chip.set = ts5500_gpio_set;
342	priv->gpio_chip.to_irq = ts5500_gpio_to_irq;
343	priv->gpio_chip.base = -1;
344
345	switch (block) {
346	case TS5500_DIO1:
347		priv->pinout = ts5500_dio1;
348		priv->gpio_chip.ngpio = ARRAY_SIZE(ts5500_dio1);
349
350		if (!devm_request_region(dev, 0x7a, 3, name)) {
351			dev_err(dev, "failed to request %s ports\n", name);
352			return -EBUSY;
353		}
354		break;
355	case TS5500_DIO2:
356		priv->pinout = ts5500_dio2;
357		priv->gpio_chip.ngpio = ARRAY_SIZE(ts5500_dio2);
358
359		if (!devm_request_region(dev, 0x7e, 2, name)) {
360			dev_err(dev, "failed to request %s ports\n", name);
361			return -EBUSY;
362		}
363
364		if (hex7d_reserved)
365			break;
366
367		if (!devm_request_region(dev, 0x7d, 1, name)) {
368			dev_err(dev, "failed to request %s 7D\n", name);
369			return -EBUSY;
370		}
371
372		hex7d_reserved = true;
373		break;
374	case TS5500_LCD:
375	case TS5600_LCD:
376		priv->pinout = ts5500_lcd;
377		priv->gpio_chip.ngpio = ARRAY_SIZE(ts5500_lcd);
378
379		if (!devm_request_region(dev, 0x72, 2, name)) {
380			dev_err(dev, "failed to request %s ports\n", name);
381			return -EBUSY;
382		}
383
384		if (!hex7d_reserved) {
385			if (!devm_request_region(dev, 0x7d, 1, name)) {
386				dev_err(dev, "failed to request %s 7D\n", name);
387				return -EBUSY;
388			}
389
390			hex7d_reserved = true;
391		}
392
393		/* Ensure usage of LCD port as DIO */
394		spin_lock_irqsave(&priv->lock, flags);
395		ts5500_clear_mask(BIT(4), 0x7d);
396		spin_unlock_irqrestore(&priv->lock, flags);
397		break;
398	}
399
400	ret = devm_gpiochip_add_data(dev, &priv->gpio_chip, priv);
401	if (ret) {
402		dev_err(dev, "failed to register the gpio chip\n");
403		return ret;
404	}
405
406	ret = ts5500_enable_irq(priv);
407	if (ret) {
408		dev_err(dev, "invalid interrupt %d\n", priv->hwirq);
409		return ret;
410	}
411
412	return 0;
413}
414
415static void ts5500_dio_remove(struct platform_device *pdev)
416{
417	struct ts5500_priv *priv = platform_get_drvdata(pdev);
418
419	ts5500_disable_irq(priv);
420}
421
422static const struct platform_device_id ts5500_dio_ids[] = {
423	{ "ts5500-dio1", TS5500_DIO1 },
424	{ "ts5500-dio2", TS5500_DIO2 },
425	{ "ts5500-dio-lcd", TS5500_LCD },
426	{ "ts5600-dio-lcd", TS5600_LCD },
427	{ }
428};
429MODULE_DEVICE_TABLE(platform, ts5500_dio_ids);
430
431static struct platform_driver ts5500_dio_driver = {
432	.driver = {
433		.name = "ts5500-dio",
434	},
435	.probe = ts5500_dio_probe,
436	.remove_new = ts5500_dio_remove,
437	.id_table = ts5500_dio_ids,
438};
439
440module_platform_driver(ts5500_dio_driver);
441
442MODULE_LICENSE("GPL");
443MODULE_AUTHOR("Savoir-faire Linux Inc. <kernel@savoirfairelinux.com>");
444MODULE_DESCRIPTION("Technologic Systems TS-5500 Digital I/O driver");
445