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
49a1184147SWilliam Breathitt Gray  * @irq:	Interrupt line number
501ceacea2SWilliam Breathitt Gray  * @out_state:	output bits state
511ceacea2SWilliam Breathitt Gray  */
521ceacea2SWilliam Breathitt Gray struct idio_16_gpio {
531ceacea2SWilliam Breathitt Gray 	struct gpio_chip chip;
541ceacea2SWilliam Breathitt Gray 	spinlock_t lock;
55a1184147SWilliam Breathitt Gray 	unsigned long irq_mask;
561ceacea2SWilliam Breathitt Gray 	unsigned base;
57a1184147SWilliam Breathitt Gray 	unsigned irq;
581ceacea2SWilliam Breathitt Gray 	unsigned out_state;
591ceacea2SWilliam Breathitt Gray };
601ceacea2SWilliam Breathitt Gray 
611ceacea2SWilliam Breathitt Gray static int idio_16_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
621ceacea2SWilliam Breathitt Gray {
631ceacea2SWilliam Breathitt Gray 	if (offset > 15)
641ceacea2SWilliam Breathitt Gray 		return 1;
651ceacea2SWilliam Breathitt Gray 
661ceacea2SWilliam Breathitt Gray 	return 0;
671ceacea2SWilliam Breathitt Gray }
681ceacea2SWilliam Breathitt Gray 
691ceacea2SWilliam Breathitt Gray static int idio_16_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
701ceacea2SWilliam Breathitt Gray {
711ceacea2SWilliam Breathitt Gray 	return 0;
721ceacea2SWilliam Breathitt Gray }
731ceacea2SWilliam Breathitt Gray 
741ceacea2SWilliam Breathitt Gray static int idio_16_gpio_direction_output(struct gpio_chip *chip,
751ceacea2SWilliam Breathitt Gray 	unsigned offset, int value)
761ceacea2SWilliam Breathitt Gray {
771ceacea2SWilliam Breathitt Gray 	chip->set(chip, offset, value);
781ceacea2SWilliam Breathitt Gray 	return 0;
791ceacea2SWilliam Breathitt Gray }
801ceacea2SWilliam Breathitt Gray 
811ceacea2SWilliam Breathitt Gray static int idio_16_gpio_get(struct gpio_chip *chip, unsigned offset)
821ceacea2SWilliam Breathitt Gray {
83d602ae90SLinus Walleij 	struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
846e0171b4SWilliam Breathitt Gray 	const unsigned mask = BIT(offset-16);
851ceacea2SWilliam Breathitt Gray 
861ceacea2SWilliam Breathitt Gray 	if (offset < 16)
871ceacea2SWilliam Breathitt Gray 		return -EINVAL;
881ceacea2SWilliam Breathitt Gray 
891ceacea2SWilliam Breathitt Gray 	if (offset < 24)
906e0171b4SWilliam Breathitt Gray 		return !!(inb(idio16gpio->base + 1) & mask);
911ceacea2SWilliam Breathitt Gray 
926e0171b4SWilliam Breathitt Gray 	return !!(inb(idio16gpio->base + 5) & (mask>>8));
931ceacea2SWilliam Breathitt Gray }
941ceacea2SWilliam Breathitt Gray 
951ceacea2SWilliam Breathitt Gray static void idio_16_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
961ceacea2SWilliam Breathitt Gray {
97d602ae90SLinus Walleij 	struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
986e0171b4SWilliam Breathitt Gray 	const unsigned mask = BIT(offset);
991ceacea2SWilliam Breathitt Gray 	unsigned long flags;
1001ceacea2SWilliam Breathitt Gray 
1011ceacea2SWilliam Breathitt Gray 	if (offset > 15)
1021ceacea2SWilliam Breathitt Gray 		return;
1031ceacea2SWilliam Breathitt Gray 
1041ceacea2SWilliam Breathitt Gray 	spin_lock_irqsave(&idio16gpio->lock, flags);
1051ceacea2SWilliam Breathitt Gray 
1061ceacea2SWilliam Breathitt Gray 	if (value)
1076e0171b4SWilliam Breathitt Gray 		idio16gpio->out_state |= mask;
1081ceacea2SWilliam Breathitt Gray 	else
1096e0171b4SWilliam Breathitt Gray 		idio16gpio->out_state &= ~mask;
1101ceacea2SWilliam Breathitt Gray 
1111ceacea2SWilliam Breathitt Gray 	if (offset > 7)
1121ceacea2SWilliam Breathitt Gray 		outb(idio16gpio->out_state >> 8, idio16gpio->base + 4);
1131ceacea2SWilliam Breathitt Gray 	else
1141ceacea2SWilliam Breathitt Gray 		outb(idio16gpio->out_state, idio16gpio->base);
1151ceacea2SWilliam Breathitt Gray 
1161ceacea2SWilliam Breathitt Gray 	spin_unlock_irqrestore(&idio16gpio->lock, flags);
1171ceacea2SWilliam Breathitt Gray }
1181ceacea2SWilliam Breathitt Gray 
1199d7ae812SWilliam Breathitt Gray static void idio_16_gpio_set_multiple(struct gpio_chip *chip,
1209d7ae812SWilliam Breathitt Gray 	unsigned long *mask, unsigned long *bits)
1219d7ae812SWilliam Breathitt Gray {
1229d7ae812SWilliam Breathitt Gray 	struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
1239d7ae812SWilliam Breathitt Gray 	unsigned long flags;
1249d7ae812SWilliam Breathitt Gray 
1259d7ae812SWilliam Breathitt Gray 	spin_lock_irqsave(&idio16gpio->lock, flags);
1269d7ae812SWilliam Breathitt Gray 
1279d7ae812SWilliam Breathitt Gray 	idio16gpio->out_state &= ~*mask;
1289d7ae812SWilliam Breathitt Gray 	idio16gpio->out_state |= *mask & *bits;
1299d7ae812SWilliam Breathitt Gray 
1309d7ae812SWilliam Breathitt Gray 	if (*mask & 0xFF)
1319d7ae812SWilliam Breathitt Gray 		outb(idio16gpio->out_state, idio16gpio->base);
1329d7ae812SWilliam Breathitt Gray 	if ((*mask >> 8) & 0xFF)
1339d7ae812SWilliam Breathitt Gray 		outb(idio16gpio->out_state >> 8, idio16gpio->base + 4);
1349d7ae812SWilliam Breathitt Gray 
1359d7ae812SWilliam Breathitt Gray 	spin_unlock_irqrestore(&idio16gpio->lock, flags);
1369d7ae812SWilliam Breathitt Gray }
1379d7ae812SWilliam Breathitt Gray 
138a1184147SWilliam Breathitt Gray static void idio_16_irq_ack(struct irq_data *data)
139a1184147SWilliam Breathitt Gray {
140a1184147SWilliam Breathitt Gray }
141a1184147SWilliam Breathitt Gray 
142a1184147SWilliam Breathitt Gray static void idio_16_irq_mask(struct irq_data *data)
143a1184147SWilliam Breathitt Gray {
144a1184147SWilliam Breathitt Gray 	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
145d602ae90SLinus Walleij 	struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
146a1184147SWilliam Breathitt Gray 	const unsigned long mask = BIT(irqd_to_hwirq(data));
147a1184147SWilliam Breathitt Gray 	unsigned long flags;
148a1184147SWilliam Breathitt Gray 
149a1184147SWilliam Breathitt Gray 	idio16gpio->irq_mask &= ~mask;
150a1184147SWilliam Breathitt Gray 
151a1184147SWilliam Breathitt Gray 	if (!idio16gpio->irq_mask) {
152a1184147SWilliam Breathitt Gray 		spin_lock_irqsave(&idio16gpio->lock, flags);
153a1184147SWilliam Breathitt Gray 
154a1184147SWilliam Breathitt Gray 		outb(0, idio16gpio->base + 2);
155a1184147SWilliam Breathitt Gray 
156a1184147SWilliam Breathitt Gray 		spin_unlock_irqrestore(&idio16gpio->lock, flags);
157a1184147SWilliam Breathitt Gray 	}
158a1184147SWilliam Breathitt Gray }
159a1184147SWilliam Breathitt Gray 
160a1184147SWilliam Breathitt Gray static void idio_16_irq_unmask(struct irq_data *data)
161a1184147SWilliam Breathitt Gray {
162a1184147SWilliam Breathitt Gray 	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
163d602ae90SLinus Walleij 	struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
164a1184147SWilliam Breathitt Gray 	const unsigned long mask = BIT(irqd_to_hwirq(data));
165a1184147SWilliam Breathitt Gray 	const unsigned long prev_irq_mask = idio16gpio->irq_mask;
166a1184147SWilliam Breathitt Gray 	unsigned long flags;
167a1184147SWilliam Breathitt Gray 
168a1184147SWilliam Breathitt Gray 	idio16gpio->irq_mask |= mask;
169a1184147SWilliam Breathitt Gray 
170a1184147SWilliam Breathitt Gray 	if (!prev_irq_mask) {
171a1184147SWilliam Breathitt Gray 		spin_lock_irqsave(&idio16gpio->lock, flags);
172a1184147SWilliam Breathitt Gray 
173a1184147SWilliam Breathitt Gray 		inb(idio16gpio->base + 2);
174a1184147SWilliam Breathitt Gray 
175a1184147SWilliam Breathitt Gray 		spin_unlock_irqrestore(&idio16gpio->lock, flags);
176a1184147SWilliam Breathitt Gray 	}
177a1184147SWilliam Breathitt Gray }
178a1184147SWilliam Breathitt Gray 
179a1184147SWilliam Breathitt Gray static int idio_16_irq_set_type(struct irq_data *data, unsigned flow_type)
180a1184147SWilliam Breathitt Gray {
181a1184147SWilliam Breathitt Gray 	/* The only valid irq types are none and both-edges */
182a1184147SWilliam Breathitt Gray 	if (flow_type != IRQ_TYPE_NONE &&
183a1184147SWilliam Breathitt Gray 		(flow_type & IRQ_TYPE_EDGE_BOTH) != IRQ_TYPE_EDGE_BOTH)
184a1184147SWilliam Breathitt Gray 		return -EINVAL;
185a1184147SWilliam Breathitt Gray 
186a1184147SWilliam Breathitt Gray 	return 0;
187a1184147SWilliam Breathitt Gray }
188a1184147SWilliam Breathitt Gray 
189a1184147SWilliam Breathitt Gray static struct irq_chip idio_16_irqchip = {
190a1184147SWilliam Breathitt Gray 	.name = "104-idio-16",
191a1184147SWilliam Breathitt Gray 	.irq_ack = idio_16_irq_ack,
192a1184147SWilliam Breathitt Gray 	.irq_mask = idio_16_irq_mask,
193a1184147SWilliam Breathitt Gray 	.irq_unmask = idio_16_irq_unmask,
194a1184147SWilliam Breathitt Gray 	.irq_set_type = idio_16_irq_set_type
195a1184147SWilliam Breathitt Gray };
196a1184147SWilliam Breathitt Gray 
197a1184147SWilliam Breathitt Gray static irqreturn_t idio_16_irq_handler(int irq, void *dev_id)
198a1184147SWilliam Breathitt Gray {
199a1184147SWilliam Breathitt Gray 	struct idio_16_gpio *const idio16gpio = dev_id;
200a1184147SWilliam Breathitt Gray 	struct gpio_chip *const chip = &idio16gpio->chip;
201a1184147SWilliam Breathitt Gray 	int gpio;
202a1184147SWilliam Breathitt Gray 
203a1184147SWilliam Breathitt Gray 	for_each_set_bit(gpio, &idio16gpio->irq_mask, chip->ngpio)
204a1184147SWilliam Breathitt Gray 		generic_handle_irq(irq_find_mapping(chip->irqdomain, gpio));
205a1184147SWilliam Breathitt Gray 
20612b61c9dSWilliam Breathitt Gray 	spin_lock(&idio16gpio->lock);
20712b61c9dSWilliam Breathitt Gray 
20812b61c9dSWilliam Breathitt Gray 	outb(0, idio16gpio->base + 1);
20912b61c9dSWilliam Breathitt Gray 
21012b61c9dSWilliam Breathitt Gray 	spin_unlock(&idio16gpio->lock);
21112b61c9dSWilliam Breathitt Gray 
212a1184147SWilliam Breathitt Gray 	return IRQ_HANDLED;
213a1184147SWilliam Breathitt Gray }
214a1184147SWilliam Breathitt Gray 
21586ea8a95SWilliam Breathitt Gray static int idio_16_probe(struct device *dev, unsigned int id)
2161ceacea2SWilliam Breathitt Gray {
2171ceacea2SWilliam Breathitt Gray 	struct idio_16_gpio *idio16gpio;
2186e0171b4SWilliam Breathitt Gray 	const char *const name = dev_name(dev);
2191ceacea2SWilliam Breathitt Gray 	int err;
2201ceacea2SWilliam Breathitt Gray 
2211ceacea2SWilliam Breathitt Gray 	idio16gpio = devm_kzalloc(dev, sizeof(*idio16gpio), GFP_KERNEL);
2221ceacea2SWilliam Breathitt Gray 	if (!idio16gpio)
2231ceacea2SWilliam Breathitt Gray 		return -ENOMEM;
2241ceacea2SWilliam Breathitt Gray 
22586ea8a95SWilliam Breathitt Gray 	if (!devm_request_region(dev, base[id], IDIO_16_EXTENT, name)) {
226cb32389cSWilliam Breathitt Gray 		dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
22786ea8a95SWilliam Breathitt Gray 			base[id], base[id] + IDIO_16_EXTENT);
228cb32389cSWilliam Breathitt Gray 		return -EBUSY;
2291ceacea2SWilliam Breathitt Gray 	}
2301ceacea2SWilliam Breathitt Gray 
2316e0171b4SWilliam Breathitt Gray 	idio16gpio->chip.label = name;
23258383c78SLinus Walleij 	idio16gpio->chip.parent = dev;
2331ceacea2SWilliam Breathitt Gray 	idio16gpio->chip.owner = THIS_MODULE;
2341ceacea2SWilliam Breathitt Gray 	idio16gpio->chip.base = -1;
2351ceacea2SWilliam Breathitt Gray 	idio16gpio->chip.ngpio = 32;
2361ceacea2SWilliam Breathitt Gray 	idio16gpio->chip.get_direction = idio_16_gpio_get_direction;
2371ceacea2SWilliam Breathitt Gray 	idio16gpio->chip.direction_input = idio_16_gpio_direction_input;
2381ceacea2SWilliam Breathitt Gray 	idio16gpio->chip.direction_output = idio_16_gpio_direction_output;
2391ceacea2SWilliam Breathitt Gray 	idio16gpio->chip.get = idio_16_gpio_get;
2401ceacea2SWilliam Breathitt Gray 	idio16gpio->chip.set = idio_16_gpio_set;
2419d7ae812SWilliam Breathitt Gray 	idio16gpio->chip.set_multiple = idio_16_gpio_set_multiple;
24286ea8a95SWilliam Breathitt Gray 	idio16gpio->base = base[id];
24386ea8a95SWilliam Breathitt Gray 	idio16gpio->irq = irq[id];
2441ceacea2SWilliam Breathitt Gray 	idio16gpio->out_state = 0xFFFF;
2451ceacea2SWilliam Breathitt Gray 
2461ceacea2SWilliam Breathitt Gray 	spin_lock_init(&idio16gpio->lock);
2471ceacea2SWilliam Breathitt Gray 
2481ceacea2SWilliam Breathitt Gray 	dev_set_drvdata(dev, idio16gpio);
2491ceacea2SWilliam Breathitt Gray 
250d602ae90SLinus Walleij 	err = gpiochip_add_data(&idio16gpio->chip, idio16gpio);
2511ceacea2SWilliam Breathitt Gray 	if (err) {
2521ceacea2SWilliam Breathitt Gray 		dev_err(dev, "GPIO registering failed (%d)\n", err);
253cb32389cSWilliam Breathitt Gray 		return err;
2541ceacea2SWilliam Breathitt Gray 	}
2551ceacea2SWilliam Breathitt Gray 
256fb50cdfeSWilliam Breathitt Gray 	/* Disable IRQ by default */
25786ea8a95SWilliam Breathitt Gray 	outb(0, base[id] + 2);
25886ea8a95SWilliam Breathitt Gray 	outb(0, base[id] + 1);
259fb50cdfeSWilliam Breathitt Gray 
260a1184147SWilliam Breathitt Gray 	err = gpiochip_irqchip_add(&idio16gpio->chip, &idio_16_irqchip, 0,
261a1184147SWilliam Breathitt Gray 		handle_edge_irq, IRQ_TYPE_NONE);
262a1184147SWilliam Breathitt Gray 	if (err) {
263a1184147SWilliam Breathitt Gray 		dev_err(dev, "Could not add irqchip (%d)\n", err);
264cb32389cSWilliam Breathitt Gray 		goto err_gpiochip_remove;
265a1184147SWilliam Breathitt Gray 	}
266a1184147SWilliam Breathitt Gray 
26786ea8a95SWilliam Breathitt Gray 	err = request_irq(irq[id], idio_16_irq_handler, 0, name, idio16gpio);
268a1184147SWilliam Breathitt Gray 	if (err) {
269a1184147SWilliam Breathitt Gray 		dev_err(dev, "IRQ handler registering failed (%d)\n", err);
270cb32389cSWilliam Breathitt Gray 		goto err_gpiochip_remove;
271a1184147SWilliam Breathitt Gray 	}
272a1184147SWilliam Breathitt Gray 
2731ceacea2SWilliam Breathitt Gray 	return 0;
2741ceacea2SWilliam Breathitt Gray 
275cb32389cSWilliam Breathitt Gray err_gpiochip_remove:
276a1184147SWilliam Breathitt Gray 	gpiochip_remove(&idio16gpio->chip);
2771ceacea2SWilliam Breathitt Gray 	return err;
2781ceacea2SWilliam Breathitt Gray }
2791ceacea2SWilliam Breathitt Gray 
28086ea8a95SWilliam Breathitt Gray static int idio_16_remove(struct device *dev, unsigned int id)
2811ceacea2SWilliam Breathitt Gray {
28286ea8a95SWilliam Breathitt Gray 	struct idio_16_gpio *const idio16gpio = dev_get_drvdata(dev);
2831ceacea2SWilliam Breathitt Gray 
284a1184147SWilliam Breathitt Gray 	free_irq(idio16gpio->irq, idio16gpio);
2851ceacea2SWilliam Breathitt Gray 	gpiochip_remove(&idio16gpio->chip);
2861ceacea2SWilliam Breathitt Gray 
2871ceacea2SWilliam Breathitt Gray 	return 0;
2881ceacea2SWilliam Breathitt Gray }
2891ceacea2SWilliam Breathitt Gray 
29086ea8a95SWilliam Breathitt Gray static struct isa_driver idio_16_driver = {
29186ea8a95SWilliam Breathitt Gray 	.probe = idio_16_probe,
2921ceacea2SWilliam Breathitt Gray 	.driver = {
2931ceacea2SWilliam Breathitt Gray 		.name = "104-idio-16"
2941ceacea2SWilliam Breathitt Gray 	},
2951ceacea2SWilliam Breathitt Gray 	.remove = idio_16_remove
2961ceacea2SWilliam Breathitt Gray };
2971ceacea2SWilliam Breathitt Gray 
29886ea8a95SWilliam Breathitt Gray module_isa_driver(idio_16_driver, num_idio_16);
2991ceacea2SWilliam Breathitt Gray 
3001ceacea2SWilliam Breathitt Gray MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
3011ceacea2SWilliam Breathitt Gray MODULE_DESCRIPTION("ACCES 104-IDIO-16 GPIO driver");
30222aeddb5SWilliam Breathitt Gray MODULE_LICENSE("GPL v2");
303