11ceacea2SWilliam Breathitt Gray /*
21ceacea2SWilliam Breathitt Gray  * GPIO driver for the ACCES 104-IDIO-16 family
31ceacea2SWilliam Breathitt Gray  * Copyright (C) 2015 William Breathitt Gray
41ceacea2SWilliam Breathitt Gray  *
51ceacea2SWilliam Breathitt Gray  * This program is free software; you can redistribute it and/or modify
61ceacea2SWilliam Breathitt Gray  * it under the terms of the GNU General Public License, version 2, as
71ceacea2SWilliam Breathitt Gray  * published by the Free Software Foundation.
81ceacea2SWilliam Breathitt Gray  *
91ceacea2SWilliam Breathitt Gray  * This program is distributed in the hope that it will be useful, but
101ceacea2SWilliam Breathitt Gray  * WITHOUT ANY WARRANTY; without even the implied warranty of
111ceacea2SWilliam Breathitt Gray  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
121ceacea2SWilliam Breathitt Gray  * General Public License for more details.
1386ea8a95SWilliam Breathitt Gray  *
1486ea8a95SWilliam Breathitt Gray  * This driver supports the following ACCES devices: 104-IDIO-16,
1586ea8a95SWilliam Breathitt Gray  * 104-IDIO-16E, 104-IDO-16, 104-IDIO-8, 104-IDIO-8E, and 104-IDO-8.
161ceacea2SWilliam Breathitt Gray  */
17a1184147SWilliam Breathitt Gray #include <linux/bitops.h>
181ceacea2SWilliam Breathitt Gray #include <linux/device.h>
191ceacea2SWilliam Breathitt Gray #include <linux/errno.h>
201ceacea2SWilliam Breathitt Gray #include <linux/gpio/driver.h>
211ceacea2SWilliam Breathitt Gray #include <linux/io.h>
221ceacea2SWilliam Breathitt Gray #include <linux/ioport.h>
23a1184147SWilliam Breathitt Gray #include <linux/interrupt.h>
24a1184147SWilliam Breathitt Gray #include <linux/irqdesc.h>
2586ea8a95SWilliam Breathitt Gray #include <linux/isa.h>
261ceacea2SWilliam Breathitt Gray #include <linux/kernel.h>
271ceacea2SWilliam Breathitt Gray #include <linux/module.h>
281ceacea2SWilliam Breathitt Gray #include <linux/moduleparam.h>
291ceacea2SWilliam Breathitt Gray #include <linux/spinlock.h>
301ceacea2SWilliam Breathitt Gray 
3186ea8a95SWilliam Breathitt Gray #define IDIO_16_EXTENT 8
3286ea8a95SWilliam Breathitt Gray #define MAX_NUM_IDIO_16 max_num_isa_dev(IDIO_16_EXTENT)
3386ea8a95SWilliam Breathitt Gray 
3486ea8a95SWilliam Breathitt Gray static unsigned int base[MAX_NUM_IDIO_16];
3586ea8a95SWilliam Breathitt Gray static unsigned int num_idio_16;
3686ea8a95SWilliam Breathitt Gray module_param_array(base, uint, &num_idio_16, 0);
3786ea8a95SWilliam Breathitt Gray MODULE_PARM_DESC(base, "ACCES 104-IDIO-16 base addresses");
3886ea8a95SWilliam Breathitt Gray 
3986ea8a95SWilliam Breathitt Gray static unsigned int irq[MAX_NUM_IDIO_16];
4086ea8a95SWilliam Breathitt Gray module_param_array(irq, uint, NULL, 0);
4186ea8a95SWilliam Breathitt Gray MODULE_PARM_DESC(irq, "ACCES 104-IDIO-16 interrupt line numbers");
421ceacea2SWilliam Breathitt Gray 
431ceacea2SWilliam Breathitt Gray /**
441ceacea2SWilliam Breathitt Gray  * struct idio_16_gpio - GPIO device private data structure
451ceacea2SWilliam Breathitt Gray  * @chip:	instance of the gpio_chip
46a1184147SWilliam Breathitt Gray  * @lock:	synchronization lock to prevent I/O race conditions
47a1184147SWilliam Breathitt Gray  * @irq_mask:	I/O bits affected by interrupts
481ceacea2SWilliam Breathitt Gray  * @base:	base port address of the GPIO device
491ceacea2SWilliam Breathitt Gray  * @out_state:	output bits state
501ceacea2SWilliam Breathitt Gray  */
511ceacea2SWilliam Breathitt Gray struct idio_16_gpio {
521ceacea2SWilliam Breathitt Gray 	struct gpio_chip chip;
533906e808SJulia Cartwright 	raw_spinlock_t lock;
54a1184147SWilliam Breathitt Gray 	unsigned long irq_mask;
551ceacea2SWilliam Breathitt Gray 	unsigned base;
561ceacea2SWilliam Breathitt Gray 	unsigned out_state;
571ceacea2SWilliam Breathitt Gray };
581ceacea2SWilliam Breathitt Gray 
591ceacea2SWilliam Breathitt Gray static int idio_16_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
601ceacea2SWilliam Breathitt Gray {
611ceacea2SWilliam Breathitt Gray 	if (offset > 15)
621ceacea2SWilliam Breathitt Gray 		return 1;
631ceacea2SWilliam Breathitt Gray 
641ceacea2SWilliam Breathitt Gray 	return 0;
651ceacea2SWilliam Breathitt Gray }
661ceacea2SWilliam Breathitt Gray 
671ceacea2SWilliam Breathitt Gray static int idio_16_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
681ceacea2SWilliam Breathitt Gray {
691ceacea2SWilliam Breathitt Gray 	return 0;
701ceacea2SWilliam Breathitt Gray }
711ceacea2SWilliam Breathitt Gray 
721ceacea2SWilliam Breathitt Gray static int idio_16_gpio_direction_output(struct gpio_chip *chip,
731ceacea2SWilliam Breathitt Gray 	unsigned offset, int value)
741ceacea2SWilliam Breathitt Gray {
751ceacea2SWilliam Breathitt Gray 	chip->set(chip, offset, value);
761ceacea2SWilliam Breathitt Gray 	return 0;
771ceacea2SWilliam Breathitt Gray }
781ceacea2SWilliam Breathitt Gray 
791ceacea2SWilliam Breathitt Gray static int idio_16_gpio_get(struct gpio_chip *chip, unsigned offset)
801ceacea2SWilliam Breathitt Gray {
81d602ae90SLinus Walleij 	struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
826e0171b4SWilliam Breathitt Gray 	const unsigned mask = BIT(offset-16);
831ceacea2SWilliam Breathitt Gray 
841ceacea2SWilliam Breathitt Gray 	if (offset < 16)
851ceacea2SWilliam Breathitt Gray 		return -EINVAL;
861ceacea2SWilliam Breathitt Gray 
871ceacea2SWilliam Breathitt Gray 	if (offset < 24)
886e0171b4SWilliam Breathitt Gray 		return !!(inb(idio16gpio->base + 1) & mask);
891ceacea2SWilliam Breathitt Gray 
906e0171b4SWilliam Breathitt Gray 	return !!(inb(idio16gpio->base + 5) & (mask>>8));
911ceacea2SWilliam Breathitt Gray }
921ceacea2SWilliam Breathitt Gray 
931ceacea2SWilliam Breathitt Gray static void idio_16_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
941ceacea2SWilliam Breathitt Gray {
95d602ae90SLinus Walleij 	struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
966e0171b4SWilliam Breathitt Gray 	const unsigned mask = BIT(offset);
971ceacea2SWilliam Breathitt Gray 	unsigned long flags;
981ceacea2SWilliam Breathitt Gray 
991ceacea2SWilliam Breathitt Gray 	if (offset > 15)
1001ceacea2SWilliam Breathitt Gray 		return;
1011ceacea2SWilliam Breathitt Gray 
1023906e808SJulia Cartwright 	raw_spin_lock_irqsave(&idio16gpio->lock, flags);
1031ceacea2SWilliam Breathitt Gray 
1041ceacea2SWilliam Breathitt Gray 	if (value)
1056e0171b4SWilliam Breathitt Gray 		idio16gpio->out_state |= mask;
1061ceacea2SWilliam Breathitt Gray 	else
1076e0171b4SWilliam Breathitt Gray 		idio16gpio->out_state &= ~mask;
1081ceacea2SWilliam Breathitt Gray 
1091ceacea2SWilliam Breathitt Gray 	if (offset > 7)
1101ceacea2SWilliam Breathitt Gray 		outb(idio16gpio->out_state >> 8, idio16gpio->base + 4);
1111ceacea2SWilliam Breathitt Gray 	else
1121ceacea2SWilliam Breathitt Gray 		outb(idio16gpio->out_state, idio16gpio->base);
1131ceacea2SWilliam Breathitt Gray 
1143906e808SJulia Cartwright 	raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
1151ceacea2SWilliam Breathitt Gray }
1161ceacea2SWilliam Breathitt Gray 
1179d7ae812SWilliam Breathitt Gray static void idio_16_gpio_set_multiple(struct gpio_chip *chip,
1189d7ae812SWilliam Breathitt Gray 	unsigned long *mask, unsigned long *bits)
1199d7ae812SWilliam Breathitt Gray {
1209d7ae812SWilliam Breathitt Gray 	struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
1219d7ae812SWilliam Breathitt Gray 	unsigned long flags;
1229d7ae812SWilliam Breathitt Gray 
1233906e808SJulia Cartwright 	raw_spin_lock_irqsave(&idio16gpio->lock, flags);
1249d7ae812SWilliam Breathitt Gray 
1259d7ae812SWilliam Breathitt Gray 	idio16gpio->out_state &= ~*mask;
1269d7ae812SWilliam Breathitt Gray 	idio16gpio->out_state |= *mask & *bits;
1279d7ae812SWilliam Breathitt Gray 
1289d7ae812SWilliam Breathitt Gray 	if (*mask & 0xFF)
1299d7ae812SWilliam Breathitt Gray 		outb(idio16gpio->out_state, idio16gpio->base);
1309d7ae812SWilliam Breathitt Gray 	if ((*mask >> 8) & 0xFF)
1319d7ae812SWilliam Breathitt Gray 		outb(idio16gpio->out_state >> 8, idio16gpio->base + 4);
1329d7ae812SWilliam Breathitt Gray 
1333906e808SJulia Cartwright 	raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
1349d7ae812SWilliam Breathitt Gray }
1359d7ae812SWilliam Breathitt Gray 
136a1184147SWilliam Breathitt Gray static void idio_16_irq_ack(struct irq_data *data)
137a1184147SWilliam Breathitt Gray {
138a1184147SWilliam Breathitt Gray }
139a1184147SWilliam Breathitt Gray 
140a1184147SWilliam Breathitt Gray static void idio_16_irq_mask(struct irq_data *data)
141a1184147SWilliam Breathitt Gray {
142a1184147SWilliam Breathitt Gray 	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
143d602ae90SLinus Walleij 	struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
144a1184147SWilliam Breathitt Gray 	const unsigned long mask = BIT(irqd_to_hwirq(data));
145a1184147SWilliam Breathitt Gray 	unsigned long flags;
146a1184147SWilliam Breathitt Gray 
147a1184147SWilliam Breathitt Gray 	idio16gpio->irq_mask &= ~mask;
148a1184147SWilliam Breathitt Gray 
149a1184147SWilliam Breathitt Gray 	if (!idio16gpio->irq_mask) {
1503906e808SJulia Cartwright 		raw_spin_lock_irqsave(&idio16gpio->lock, flags);
151a1184147SWilliam Breathitt Gray 
152a1184147SWilliam Breathitt Gray 		outb(0, idio16gpio->base + 2);
153a1184147SWilliam Breathitt Gray 
1543906e808SJulia Cartwright 		raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
155a1184147SWilliam Breathitt Gray 	}
156a1184147SWilliam Breathitt Gray }
157a1184147SWilliam Breathitt Gray 
158a1184147SWilliam Breathitt Gray static void idio_16_irq_unmask(struct irq_data *data)
159a1184147SWilliam Breathitt Gray {
160a1184147SWilliam Breathitt Gray 	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
161d602ae90SLinus Walleij 	struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
162a1184147SWilliam Breathitt Gray 	const unsigned long mask = BIT(irqd_to_hwirq(data));
163a1184147SWilliam Breathitt Gray 	const unsigned long prev_irq_mask = idio16gpio->irq_mask;
164a1184147SWilliam Breathitt Gray 	unsigned long flags;
165a1184147SWilliam Breathitt Gray 
166a1184147SWilliam Breathitt Gray 	idio16gpio->irq_mask |= mask;
167a1184147SWilliam Breathitt Gray 
168a1184147SWilliam Breathitt Gray 	if (!prev_irq_mask) {
1693906e808SJulia Cartwright 		raw_spin_lock_irqsave(&idio16gpio->lock, flags);
170a1184147SWilliam Breathitt Gray 
171a1184147SWilliam Breathitt Gray 		inb(idio16gpio->base + 2);
172a1184147SWilliam Breathitt Gray 
1733906e808SJulia Cartwright 		raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
174a1184147SWilliam Breathitt Gray 	}
175a1184147SWilliam Breathitt Gray }
176a1184147SWilliam Breathitt Gray 
177a1184147SWilliam Breathitt Gray static int idio_16_irq_set_type(struct irq_data *data, unsigned flow_type)
178a1184147SWilliam Breathitt Gray {
179a1184147SWilliam Breathitt Gray 	/* The only valid irq types are none and both-edges */
180a1184147SWilliam Breathitt Gray 	if (flow_type != IRQ_TYPE_NONE &&
181a1184147SWilliam Breathitt Gray 		(flow_type & IRQ_TYPE_EDGE_BOTH) != IRQ_TYPE_EDGE_BOTH)
182a1184147SWilliam Breathitt Gray 		return -EINVAL;
183a1184147SWilliam Breathitt Gray 
184a1184147SWilliam Breathitt Gray 	return 0;
185a1184147SWilliam Breathitt Gray }
186a1184147SWilliam Breathitt Gray 
187a1184147SWilliam Breathitt Gray static struct irq_chip idio_16_irqchip = {
188a1184147SWilliam Breathitt Gray 	.name = "104-idio-16",
189a1184147SWilliam Breathitt Gray 	.irq_ack = idio_16_irq_ack,
190a1184147SWilliam Breathitt Gray 	.irq_mask = idio_16_irq_mask,
191a1184147SWilliam Breathitt Gray 	.irq_unmask = idio_16_irq_unmask,
192a1184147SWilliam Breathitt Gray 	.irq_set_type = idio_16_irq_set_type
193a1184147SWilliam Breathitt Gray };
194a1184147SWilliam Breathitt Gray 
195a1184147SWilliam Breathitt Gray static irqreturn_t idio_16_irq_handler(int irq, void *dev_id)
196a1184147SWilliam Breathitt Gray {
197a1184147SWilliam Breathitt Gray 	struct idio_16_gpio *const idio16gpio = dev_id;
198a1184147SWilliam Breathitt Gray 	struct gpio_chip *const chip = &idio16gpio->chip;
199a1184147SWilliam Breathitt Gray 	int gpio;
200a1184147SWilliam Breathitt Gray 
201a1184147SWilliam Breathitt Gray 	for_each_set_bit(gpio, &idio16gpio->irq_mask, chip->ngpio)
202a1184147SWilliam Breathitt Gray 		generic_handle_irq(irq_find_mapping(chip->irqdomain, gpio));
203a1184147SWilliam Breathitt Gray 
2043906e808SJulia Cartwright 	raw_spin_lock(&idio16gpio->lock);
20512b61c9dSWilliam Breathitt Gray 
20612b61c9dSWilliam Breathitt Gray 	outb(0, idio16gpio->base + 1);
20712b61c9dSWilliam Breathitt Gray 
2083906e808SJulia Cartwright 	raw_spin_unlock(&idio16gpio->lock);
20912b61c9dSWilliam Breathitt Gray 
210a1184147SWilliam Breathitt Gray 	return IRQ_HANDLED;
211a1184147SWilliam Breathitt Gray }
212a1184147SWilliam Breathitt Gray 
213e0af4b5eSWilliam Breathitt Gray #define IDIO_16_NGPIO 32
214e0af4b5eSWilliam Breathitt Gray static const char *idio_16_names[IDIO_16_NGPIO] = {
215e0af4b5eSWilliam Breathitt Gray 	"OUT0", "OUT1", "OUT2", "OUT3", "OUT4", "OUT5", "OUT6", "OUT7",
216e0af4b5eSWilliam Breathitt Gray 	"OUT8", "OUT9", "OUT10", "OUT11", "OUT12", "OUT13", "OUT14", "OUT15",
217e0af4b5eSWilliam Breathitt Gray 	"IIN0", "IIN1", "IIN2", "IIN3", "IIN4", "IIN5", "IIN6", "IIN7",
218e0af4b5eSWilliam Breathitt Gray 	"IIN8", "IIN9", "IIN10", "IIN11", "IIN12", "IIN13", "IIN14", "IIN15"
219e0af4b5eSWilliam Breathitt Gray };
220e0af4b5eSWilliam Breathitt Gray 
22186ea8a95SWilliam Breathitt Gray static int idio_16_probe(struct device *dev, unsigned int id)
2221ceacea2SWilliam Breathitt Gray {
2231ceacea2SWilliam Breathitt Gray 	struct idio_16_gpio *idio16gpio;
2246e0171b4SWilliam Breathitt Gray 	const char *const name = dev_name(dev);
2251ceacea2SWilliam Breathitt Gray 	int err;
2261ceacea2SWilliam Breathitt Gray 
2271ceacea2SWilliam Breathitt Gray 	idio16gpio = devm_kzalloc(dev, sizeof(*idio16gpio), GFP_KERNEL);
2281ceacea2SWilliam Breathitt Gray 	if (!idio16gpio)
2291ceacea2SWilliam Breathitt Gray 		return -ENOMEM;
2301ceacea2SWilliam Breathitt Gray 
23186ea8a95SWilliam Breathitt Gray 	if (!devm_request_region(dev, base[id], IDIO_16_EXTENT, name)) {
232cb32389cSWilliam Breathitt Gray 		dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
23386ea8a95SWilliam Breathitt Gray 			base[id], base[id] + IDIO_16_EXTENT);
234cb32389cSWilliam Breathitt Gray 		return -EBUSY;
2351ceacea2SWilliam Breathitt Gray 	}
2361ceacea2SWilliam Breathitt Gray 
2376e0171b4SWilliam Breathitt Gray 	idio16gpio->chip.label = name;
23858383c78SLinus Walleij 	idio16gpio->chip.parent = dev;
2391ceacea2SWilliam Breathitt Gray 	idio16gpio->chip.owner = THIS_MODULE;
2401ceacea2SWilliam Breathitt Gray 	idio16gpio->chip.base = -1;
241e0af4b5eSWilliam Breathitt Gray 	idio16gpio->chip.ngpio = IDIO_16_NGPIO;
242e0af4b5eSWilliam Breathitt Gray 	idio16gpio->chip.names = idio_16_names;
2431ceacea2SWilliam Breathitt Gray 	idio16gpio->chip.get_direction = idio_16_gpio_get_direction;
2441ceacea2SWilliam Breathitt Gray 	idio16gpio->chip.direction_input = idio_16_gpio_direction_input;
2451ceacea2SWilliam Breathitt Gray 	idio16gpio->chip.direction_output = idio_16_gpio_direction_output;
2461ceacea2SWilliam Breathitt Gray 	idio16gpio->chip.get = idio_16_gpio_get;
2471ceacea2SWilliam Breathitt Gray 	idio16gpio->chip.set = idio_16_gpio_set;
2489d7ae812SWilliam Breathitt Gray 	idio16gpio->chip.set_multiple = idio_16_gpio_set_multiple;
24986ea8a95SWilliam Breathitt Gray 	idio16gpio->base = base[id];
2501ceacea2SWilliam Breathitt Gray 	idio16gpio->out_state = 0xFFFF;
2511ceacea2SWilliam Breathitt Gray 
2523906e808SJulia Cartwright 	raw_spin_lock_init(&idio16gpio->lock);
2531ceacea2SWilliam Breathitt Gray 
254837143d3SWilliam Breathitt Gray 	err = devm_gpiochip_add_data(dev, &idio16gpio->chip, idio16gpio);
2551ceacea2SWilliam Breathitt Gray 	if (err) {
2561ceacea2SWilliam Breathitt Gray 		dev_err(dev, "GPIO registering failed (%d)\n", err);
257cb32389cSWilliam Breathitt Gray 		return err;
2581ceacea2SWilliam Breathitt Gray 	}
2591ceacea2SWilliam Breathitt Gray 
260fb50cdfeSWilliam Breathitt Gray 	/* Disable IRQ by default */
26186ea8a95SWilliam Breathitt Gray 	outb(0, base[id] + 2);
26286ea8a95SWilliam Breathitt Gray 	outb(0, base[id] + 1);
263fb50cdfeSWilliam Breathitt Gray 
264a1184147SWilliam Breathitt Gray 	err = gpiochip_irqchip_add(&idio16gpio->chip, &idio_16_irqchip, 0,
265a1184147SWilliam Breathitt Gray 		handle_edge_irq, IRQ_TYPE_NONE);
266a1184147SWilliam Breathitt Gray 	if (err) {
267a1184147SWilliam Breathitt Gray 		dev_err(dev, "Could not add irqchip (%d)\n", err);
2681ceacea2SWilliam Breathitt Gray 		return err;
2691ceacea2SWilliam Breathitt Gray 	}
2701ceacea2SWilliam Breathitt Gray 
271837143d3SWilliam Breathitt Gray 	err = devm_request_irq(dev, irq[id], idio_16_irq_handler, 0, name,
272837143d3SWilliam Breathitt Gray 		idio16gpio);
273837143d3SWilliam Breathitt Gray 	if (err) {
274837143d3SWilliam Breathitt Gray 		dev_err(dev, "IRQ handler registering failed (%d)\n", err);
275837143d3SWilliam Breathitt Gray 		return err;
276837143d3SWilliam Breathitt Gray 	}
2771ceacea2SWilliam Breathitt Gray 
2781ceacea2SWilliam Breathitt Gray 	return 0;
2791ceacea2SWilliam Breathitt Gray }
2801ceacea2SWilliam Breathitt Gray 
28186ea8a95SWilliam Breathitt Gray static struct isa_driver idio_16_driver = {
28286ea8a95SWilliam Breathitt Gray 	.probe = idio_16_probe,
2831ceacea2SWilliam Breathitt Gray 	.driver = {
2841ceacea2SWilliam Breathitt Gray 		.name = "104-idio-16"
2851ceacea2SWilliam Breathitt Gray 	},
2861ceacea2SWilliam Breathitt Gray };
2871ceacea2SWilliam Breathitt Gray 
28886ea8a95SWilliam Breathitt Gray module_isa_driver(idio_16_driver, num_idio_16);
2891ceacea2SWilliam Breathitt Gray 
2901ceacea2SWilliam Breathitt Gray MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
2911ceacea2SWilliam Breathitt Gray MODULE_DESCRIPTION("ACCES 104-IDIO-16 GPIO driver");
29222aeddb5SWilliam Breathitt Gray MODULE_LICENSE("GPL v2");
293