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; 36d759f906SDavid Howells module_param_hw_array(base, uint, ioport, &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]; 40d759f906SDavid Howells module_param_hw_array(irq, uint, irq, 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 9315f59cffSWilliam Breathitt Gray static int idio_16_gpio_get_multiple(struct gpio_chip *chip, 9415f59cffSWilliam Breathitt Gray unsigned long *mask, unsigned long *bits) 9515f59cffSWilliam Breathitt Gray { 9615f59cffSWilliam Breathitt Gray struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip); 9715f59cffSWilliam Breathitt Gray 9815f59cffSWilliam Breathitt Gray *bits = 0; 9915f59cffSWilliam Breathitt Gray if (*mask & GENMASK(23, 16)) 10015f59cffSWilliam Breathitt Gray *bits |= (unsigned long)inb(idio16gpio->base + 1) << 16; 10115f59cffSWilliam Breathitt Gray if (*mask & GENMASK(31, 24)) 10215f59cffSWilliam Breathitt Gray *bits |= (unsigned long)inb(idio16gpio->base + 5) << 24; 10315f59cffSWilliam Breathitt Gray 10415f59cffSWilliam Breathitt Gray return 0; 10515f59cffSWilliam Breathitt Gray } 10615f59cffSWilliam Breathitt Gray 1071ceacea2SWilliam Breathitt Gray static void idio_16_gpio_set(struct gpio_chip *chip, unsigned offset, int value) 1081ceacea2SWilliam Breathitt Gray { 109d602ae90SLinus Walleij struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip); 1106e0171b4SWilliam Breathitt Gray const unsigned mask = BIT(offset); 1111ceacea2SWilliam Breathitt Gray unsigned long flags; 1121ceacea2SWilliam Breathitt Gray 1131ceacea2SWilliam Breathitt Gray if (offset > 15) 1141ceacea2SWilliam Breathitt Gray return; 1151ceacea2SWilliam Breathitt Gray 1163906e808SJulia Cartwright raw_spin_lock_irqsave(&idio16gpio->lock, flags); 1171ceacea2SWilliam Breathitt Gray 1181ceacea2SWilliam Breathitt Gray if (value) 1196e0171b4SWilliam Breathitt Gray idio16gpio->out_state |= mask; 1201ceacea2SWilliam Breathitt Gray else 1216e0171b4SWilliam Breathitt Gray idio16gpio->out_state &= ~mask; 1221ceacea2SWilliam Breathitt Gray 1231ceacea2SWilliam Breathitt Gray if (offset > 7) 1241ceacea2SWilliam Breathitt Gray outb(idio16gpio->out_state >> 8, idio16gpio->base + 4); 1251ceacea2SWilliam Breathitt Gray else 1261ceacea2SWilliam Breathitt Gray outb(idio16gpio->out_state, idio16gpio->base); 1271ceacea2SWilliam Breathitt Gray 1283906e808SJulia Cartwright raw_spin_unlock_irqrestore(&idio16gpio->lock, flags); 1291ceacea2SWilliam Breathitt Gray } 1301ceacea2SWilliam Breathitt Gray 1319d7ae812SWilliam Breathitt Gray static void idio_16_gpio_set_multiple(struct gpio_chip *chip, 1329d7ae812SWilliam Breathitt Gray unsigned long *mask, unsigned long *bits) 1339d7ae812SWilliam Breathitt Gray { 1349d7ae812SWilliam Breathitt Gray struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip); 1359d7ae812SWilliam Breathitt Gray unsigned long flags; 1369d7ae812SWilliam Breathitt Gray 1373906e808SJulia Cartwright raw_spin_lock_irqsave(&idio16gpio->lock, flags); 1389d7ae812SWilliam Breathitt Gray 1399d7ae812SWilliam Breathitt Gray idio16gpio->out_state &= ~*mask; 1409d7ae812SWilliam Breathitt Gray idio16gpio->out_state |= *mask & *bits; 1419d7ae812SWilliam Breathitt Gray 1429d7ae812SWilliam Breathitt Gray if (*mask & 0xFF) 1439d7ae812SWilliam Breathitt Gray outb(idio16gpio->out_state, idio16gpio->base); 1449d7ae812SWilliam Breathitt Gray if ((*mask >> 8) & 0xFF) 1459d7ae812SWilliam Breathitt Gray outb(idio16gpio->out_state >> 8, idio16gpio->base + 4); 1469d7ae812SWilliam Breathitt Gray 1473906e808SJulia Cartwright raw_spin_unlock_irqrestore(&idio16gpio->lock, flags); 1489d7ae812SWilliam Breathitt Gray } 1499d7ae812SWilliam Breathitt Gray 150a1184147SWilliam Breathitt Gray static void idio_16_irq_ack(struct irq_data *data) 151a1184147SWilliam Breathitt Gray { 152a1184147SWilliam Breathitt Gray } 153a1184147SWilliam Breathitt Gray 154a1184147SWilliam Breathitt Gray static void idio_16_irq_mask(struct irq_data *data) 155a1184147SWilliam Breathitt Gray { 156a1184147SWilliam Breathitt Gray struct gpio_chip *chip = irq_data_get_irq_chip_data(data); 157d602ae90SLinus Walleij struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip); 158a1184147SWilliam Breathitt Gray const unsigned long mask = BIT(irqd_to_hwirq(data)); 159a1184147SWilliam Breathitt Gray unsigned long flags; 160a1184147SWilliam Breathitt Gray 161a1184147SWilliam Breathitt Gray idio16gpio->irq_mask &= ~mask; 162a1184147SWilliam Breathitt Gray 163a1184147SWilliam Breathitt Gray if (!idio16gpio->irq_mask) { 1643906e808SJulia Cartwright raw_spin_lock_irqsave(&idio16gpio->lock, flags); 165a1184147SWilliam Breathitt Gray 166a1184147SWilliam Breathitt Gray outb(0, idio16gpio->base + 2); 167a1184147SWilliam Breathitt Gray 1683906e808SJulia Cartwright raw_spin_unlock_irqrestore(&idio16gpio->lock, flags); 169a1184147SWilliam Breathitt Gray } 170a1184147SWilliam Breathitt Gray } 171a1184147SWilliam Breathitt Gray 172a1184147SWilliam Breathitt Gray static void idio_16_irq_unmask(struct irq_data *data) 173a1184147SWilliam Breathitt Gray { 174a1184147SWilliam Breathitt Gray struct gpio_chip *chip = irq_data_get_irq_chip_data(data); 175d602ae90SLinus Walleij struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip); 176a1184147SWilliam Breathitt Gray const unsigned long mask = BIT(irqd_to_hwirq(data)); 177a1184147SWilliam Breathitt Gray const unsigned long prev_irq_mask = idio16gpio->irq_mask; 178a1184147SWilliam Breathitt Gray unsigned long flags; 179a1184147SWilliam Breathitt Gray 180a1184147SWilliam Breathitt Gray idio16gpio->irq_mask |= mask; 181a1184147SWilliam Breathitt Gray 182a1184147SWilliam Breathitt Gray if (!prev_irq_mask) { 1833906e808SJulia Cartwright raw_spin_lock_irqsave(&idio16gpio->lock, flags); 184a1184147SWilliam Breathitt Gray 185a1184147SWilliam Breathitt Gray inb(idio16gpio->base + 2); 186a1184147SWilliam Breathitt Gray 1873906e808SJulia Cartwright raw_spin_unlock_irqrestore(&idio16gpio->lock, flags); 188a1184147SWilliam Breathitt Gray } 189a1184147SWilliam Breathitt Gray } 190a1184147SWilliam Breathitt Gray 191a1184147SWilliam Breathitt Gray static int idio_16_irq_set_type(struct irq_data *data, unsigned flow_type) 192a1184147SWilliam Breathitt Gray { 193a1184147SWilliam Breathitt Gray /* The only valid irq types are none and both-edges */ 194a1184147SWilliam Breathitt Gray if (flow_type != IRQ_TYPE_NONE && 195a1184147SWilliam Breathitt Gray (flow_type & IRQ_TYPE_EDGE_BOTH) != IRQ_TYPE_EDGE_BOTH) 196a1184147SWilliam Breathitt Gray return -EINVAL; 197a1184147SWilliam Breathitt Gray 198a1184147SWilliam Breathitt Gray return 0; 199a1184147SWilliam Breathitt Gray } 200a1184147SWilliam Breathitt Gray 201a1184147SWilliam Breathitt Gray static struct irq_chip idio_16_irqchip = { 202a1184147SWilliam Breathitt Gray .name = "104-idio-16", 203a1184147SWilliam Breathitt Gray .irq_ack = idio_16_irq_ack, 204a1184147SWilliam Breathitt Gray .irq_mask = idio_16_irq_mask, 205a1184147SWilliam Breathitt Gray .irq_unmask = idio_16_irq_unmask, 206a1184147SWilliam Breathitt Gray .irq_set_type = idio_16_irq_set_type 207a1184147SWilliam Breathitt Gray }; 208a1184147SWilliam Breathitt Gray 209a1184147SWilliam Breathitt Gray static irqreturn_t idio_16_irq_handler(int irq, void *dev_id) 210a1184147SWilliam Breathitt Gray { 211a1184147SWilliam Breathitt Gray struct idio_16_gpio *const idio16gpio = dev_id; 212a1184147SWilliam Breathitt Gray struct gpio_chip *const chip = &idio16gpio->chip; 213a1184147SWilliam Breathitt Gray int gpio; 214a1184147SWilliam Breathitt Gray 215a1184147SWilliam Breathitt Gray for_each_set_bit(gpio, &idio16gpio->irq_mask, chip->ngpio) 216f0fbe7bcSThierry Reding generic_handle_irq(irq_find_mapping(chip->irq.domain, gpio)); 217a1184147SWilliam Breathitt Gray 2183906e808SJulia Cartwright raw_spin_lock(&idio16gpio->lock); 21912b61c9dSWilliam Breathitt Gray 22012b61c9dSWilliam Breathitt Gray outb(0, idio16gpio->base + 1); 22112b61c9dSWilliam Breathitt Gray 2223906e808SJulia Cartwright raw_spin_unlock(&idio16gpio->lock); 22312b61c9dSWilliam Breathitt Gray 224a1184147SWilliam Breathitt Gray return IRQ_HANDLED; 225a1184147SWilliam Breathitt Gray } 226a1184147SWilliam Breathitt Gray 227e0af4b5eSWilliam Breathitt Gray #define IDIO_16_NGPIO 32 228e0af4b5eSWilliam Breathitt Gray static const char *idio_16_names[IDIO_16_NGPIO] = { 229e0af4b5eSWilliam Breathitt Gray "OUT0", "OUT1", "OUT2", "OUT3", "OUT4", "OUT5", "OUT6", "OUT7", 230e0af4b5eSWilliam Breathitt Gray "OUT8", "OUT9", "OUT10", "OUT11", "OUT12", "OUT13", "OUT14", "OUT15", 231e0af4b5eSWilliam Breathitt Gray "IIN0", "IIN1", "IIN2", "IIN3", "IIN4", "IIN5", "IIN6", "IIN7", 232e0af4b5eSWilliam Breathitt Gray "IIN8", "IIN9", "IIN10", "IIN11", "IIN12", "IIN13", "IIN14", "IIN15" 233e0af4b5eSWilliam Breathitt Gray }; 234e0af4b5eSWilliam Breathitt Gray 23586ea8a95SWilliam Breathitt Gray static int idio_16_probe(struct device *dev, unsigned int id) 2361ceacea2SWilliam Breathitt Gray { 2371ceacea2SWilliam Breathitt Gray struct idio_16_gpio *idio16gpio; 2386e0171b4SWilliam Breathitt Gray const char *const name = dev_name(dev); 2391ceacea2SWilliam Breathitt Gray int err; 2401ceacea2SWilliam Breathitt Gray 2411ceacea2SWilliam Breathitt Gray idio16gpio = devm_kzalloc(dev, sizeof(*idio16gpio), GFP_KERNEL); 2421ceacea2SWilliam Breathitt Gray if (!idio16gpio) 2431ceacea2SWilliam Breathitt Gray return -ENOMEM; 2441ceacea2SWilliam Breathitt Gray 24586ea8a95SWilliam Breathitt Gray if (!devm_request_region(dev, base[id], IDIO_16_EXTENT, name)) { 246cb32389cSWilliam Breathitt Gray dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n", 24786ea8a95SWilliam Breathitt Gray base[id], base[id] + IDIO_16_EXTENT); 248cb32389cSWilliam Breathitt Gray return -EBUSY; 2491ceacea2SWilliam Breathitt Gray } 2501ceacea2SWilliam Breathitt Gray 2516e0171b4SWilliam Breathitt Gray idio16gpio->chip.label = name; 25258383c78SLinus Walleij idio16gpio->chip.parent = dev; 2531ceacea2SWilliam Breathitt Gray idio16gpio->chip.owner = THIS_MODULE; 2541ceacea2SWilliam Breathitt Gray idio16gpio->chip.base = -1; 255e0af4b5eSWilliam Breathitt Gray idio16gpio->chip.ngpio = IDIO_16_NGPIO; 256e0af4b5eSWilliam Breathitt Gray idio16gpio->chip.names = idio_16_names; 2571ceacea2SWilliam Breathitt Gray idio16gpio->chip.get_direction = idio_16_gpio_get_direction; 2581ceacea2SWilliam Breathitt Gray idio16gpio->chip.direction_input = idio_16_gpio_direction_input; 2591ceacea2SWilliam Breathitt Gray idio16gpio->chip.direction_output = idio_16_gpio_direction_output; 2601ceacea2SWilliam Breathitt Gray idio16gpio->chip.get = idio_16_gpio_get; 26115f59cffSWilliam Breathitt Gray idio16gpio->chip.get_multiple = idio_16_gpio_get_multiple; 2621ceacea2SWilliam Breathitt Gray idio16gpio->chip.set = idio_16_gpio_set; 2639d7ae812SWilliam Breathitt Gray idio16gpio->chip.set_multiple = idio_16_gpio_set_multiple; 26486ea8a95SWilliam Breathitt Gray idio16gpio->base = base[id]; 2651ceacea2SWilliam Breathitt Gray idio16gpio->out_state = 0xFFFF; 2661ceacea2SWilliam Breathitt Gray 2673906e808SJulia Cartwright raw_spin_lock_init(&idio16gpio->lock); 2681ceacea2SWilliam Breathitt Gray 269837143d3SWilliam Breathitt Gray err = devm_gpiochip_add_data(dev, &idio16gpio->chip, idio16gpio); 2701ceacea2SWilliam Breathitt Gray if (err) { 2711ceacea2SWilliam Breathitt Gray dev_err(dev, "GPIO registering failed (%d)\n", err); 272cb32389cSWilliam Breathitt Gray return err; 2731ceacea2SWilliam Breathitt Gray } 2741ceacea2SWilliam Breathitt Gray 275fb50cdfeSWilliam Breathitt Gray /* Disable IRQ by default */ 27686ea8a95SWilliam Breathitt Gray outb(0, base[id] + 2); 27786ea8a95SWilliam Breathitt Gray outb(0, base[id] + 1); 278fb50cdfeSWilliam Breathitt Gray 279a1184147SWilliam Breathitt Gray err = gpiochip_irqchip_add(&idio16gpio->chip, &idio_16_irqchip, 0, 280a1184147SWilliam Breathitt Gray handle_edge_irq, IRQ_TYPE_NONE); 281a1184147SWilliam Breathitt Gray if (err) { 282a1184147SWilliam Breathitt Gray dev_err(dev, "Could not add irqchip (%d)\n", err); 2831ceacea2SWilliam Breathitt Gray return err; 2841ceacea2SWilliam Breathitt Gray } 2851ceacea2SWilliam Breathitt Gray 286837143d3SWilliam Breathitt Gray err = devm_request_irq(dev, irq[id], idio_16_irq_handler, 0, name, 287837143d3SWilliam Breathitt Gray idio16gpio); 288837143d3SWilliam Breathitt Gray if (err) { 289837143d3SWilliam Breathitt Gray dev_err(dev, "IRQ handler registering failed (%d)\n", err); 290837143d3SWilliam Breathitt Gray return err; 291837143d3SWilliam Breathitt Gray } 2921ceacea2SWilliam Breathitt Gray 2931ceacea2SWilliam Breathitt Gray return 0; 2941ceacea2SWilliam Breathitt Gray } 2951ceacea2SWilliam Breathitt Gray 29686ea8a95SWilliam Breathitt Gray static struct isa_driver idio_16_driver = { 29786ea8a95SWilliam Breathitt Gray .probe = idio_16_probe, 2981ceacea2SWilliam Breathitt Gray .driver = { 2991ceacea2SWilliam Breathitt Gray .name = "104-idio-16" 3001ceacea2SWilliam Breathitt Gray }, 3011ceacea2SWilliam Breathitt Gray }; 3021ceacea2SWilliam Breathitt Gray 30386ea8a95SWilliam Breathitt Gray module_isa_driver(idio_16_driver, num_idio_16); 3041ceacea2SWilliam Breathitt Gray 3051ceacea2SWilliam Breathitt Gray MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); 3061ceacea2SWilliam Breathitt Gray MODULE_DESCRIPTION("ACCES 104-IDIO-16 GPIO driver"); 30722aeddb5SWilliam Breathitt Gray MODULE_LICENSE("GPL v2"); 308