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