Linux Audio

Check our new training course

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