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. 131ceacea2SWilliam Breathitt Gray */ 14a1184147SWilliam Breathitt Gray #include <linux/bitops.h> 151ceacea2SWilliam Breathitt Gray #include <linux/device.h> 161ceacea2SWilliam Breathitt Gray #include <linux/errno.h> 171ceacea2SWilliam Breathitt Gray #include <linux/gpio/driver.h> 181ceacea2SWilliam Breathitt Gray #include <linux/io.h> 191ceacea2SWilliam Breathitt Gray #include <linux/ioport.h> 20a1184147SWilliam Breathitt Gray #include <linux/interrupt.h> 21a1184147SWilliam Breathitt Gray #include <linux/irqdesc.h> 221ceacea2SWilliam Breathitt Gray #include <linux/kernel.h> 231ceacea2SWilliam Breathitt Gray #include <linux/module.h> 241ceacea2SWilliam Breathitt Gray #include <linux/moduleparam.h> 251ceacea2SWilliam Breathitt Gray #include <linux/platform_device.h> 261ceacea2SWilliam Breathitt Gray #include <linux/spinlock.h> 271ceacea2SWilliam Breathitt Gray 281ceacea2SWilliam Breathitt Gray static unsigned idio_16_base; 291ceacea2SWilliam Breathitt Gray module_param(idio_16_base, uint, 0); 301ceacea2SWilliam Breathitt Gray MODULE_PARM_DESC(idio_16_base, "ACCES 104-IDIO-16 base address"); 31a1184147SWilliam Breathitt Gray static unsigned idio_16_irq; 32a1184147SWilliam Breathitt Gray module_param(idio_16_irq, uint, 0); 33a1184147SWilliam Breathitt Gray MODULE_PARM_DESC(idio_16_irq, "ACCES 104-IDIO-16 interrupt line number"); 341ceacea2SWilliam Breathitt Gray 351ceacea2SWilliam Breathitt Gray /** 361ceacea2SWilliam Breathitt Gray * struct idio_16_gpio - GPIO device private data structure 371ceacea2SWilliam Breathitt Gray * @chip: instance of the gpio_chip 38a1184147SWilliam Breathitt Gray * @lock: synchronization lock to prevent I/O race conditions 39a1184147SWilliam Breathitt Gray * @irq_mask: I/O bits affected by interrupts 401ceacea2SWilliam Breathitt Gray * @base: base port address of the GPIO device 411ceacea2SWilliam Breathitt Gray * @extent: extent of port address region of the GPIO device 42a1184147SWilliam Breathitt Gray * @irq: Interrupt line number 431ceacea2SWilliam Breathitt Gray * @out_state: output bits state 441ceacea2SWilliam Breathitt Gray */ 451ceacea2SWilliam Breathitt Gray struct idio_16_gpio { 461ceacea2SWilliam Breathitt Gray struct gpio_chip chip; 471ceacea2SWilliam Breathitt Gray spinlock_t lock; 48a1184147SWilliam Breathitt Gray unsigned long irq_mask; 491ceacea2SWilliam Breathitt Gray unsigned base; 501ceacea2SWilliam Breathitt Gray unsigned extent; 51a1184147SWilliam Breathitt Gray unsigned irq; 521ceacea2SWilliam Breathitt Gray unsigned out_state; 531ceacea2SWilliam Breathitt Gray }; 541ceacea2SWilliam Breathitt Gray 551ceacea2SWilliam Breathitt Gray static int idio_16_gpio_get_direction(struct gpio_chip *chip, unsigned offset) 561ceacea2SWilliam Breathitt Gray { 571ceacea2SWilliam Breathitt Gray if (offset > 15) 581ceacea2SWilliam Breathitt Gray return 1; 591ceacea2SWilliam Breathitt Gray 601ceacea2SWilliam Breathitt Gray return 0; 611ceacea2SWilliam Breathitt Gray } 621ceacea2SWilliam Breathitt Gray 631ceacea2SWilliam Breathitt Gray static int idio_16_gpio_direction_input(struct gpio_chip *chip, unsigned offset) 641ceacea2SWilliam Breathitt Gray { 651ceacea2SWilliam Breathitt Gray return 0; 661ceacea2SWilliam Breathitt Gray } 671ceacea2SWilliam Breathitt Gray 681ceacea2SWilliam Breathitt Gray static int idio_16_gpio_direction_output(struct gpio_chip *chip, 691ceacea2SWilliam Breathitt Gray unsigned offset, int value) 701ceacea2SWilliam Breathitt Gray { 711ceacea2SWilliam Breathitt Gray chip->set(chip, offset, value); 721ceacea2SWilliam Breathitt Gray return 0; 731ceacea2SWilliam Breathitt Gray } 741ceacea2SWilliam Breathitt Gray 751ceacea2SWilliam Breathitt Gray static struct idio_16_gpio *to_idio16gpio(struct gpio_chip *gc) 761ceacea2SWilliam Breathitt Gray { 771ceacea2SWilliam Breathitt Gray return container_of(gc, struct idio_16_gpio, chip); 781ceacea2SWilliam Breathitt Gray } 791ceacea2SWilliam Breathitt Gray 801ceacea2SWilliam Breathitt Gray static int idio_16_gpio_get(struct gpio_chip *chip, unsigned offset) 811ceacea2SWilliam Breathitt Gray { 821ceacea2SWilliam Breathitt Gray struct idio_16_gpio *const idio16gpio = to_idio16gpio(chip); 836e0171b4SWilliam Breathitt Gray const unsigned mask = BIT(offset-16); 841ceacea2SWilliam Breathitt Gray 851ceacea2SWilliam Breathitt Gray if (offset < 16) 861ceacea2SWilliam Breathitt Gray return -EINVAL; 871ceacea2SWilliam Breathitt Gray 881ceacea2SWilliam Breathitt Gray if (offset < 24) 896e0171b4SWilliam Breathitt Gray return !!(inb(idio16gpio->base + 1) & mask); 901ceacea2SWilliam Breathitt Gray 916e0171b4SWilliam Breathitt Gray return !!(inb(idio16gpio->base + 5) & (mask>>8)); 921ceacea2SWilliam Breathitt Gray } 931ceacea2SWilliam Breathitt Gray 941ceacea2SWilliam Breathitt Gray static void idio_16_gpio_set(struct gpio_chip *chip, unsigned offset, int value) 951ceacea2SWilliam Breathitt Gray { 961ceacea2SWilliam Breathitt Gray struct idio_16_gpio *const idio16gpio = to_idio16gpio(chip); 976e0171b4SWilliam Breathitt Gray const unsigned mask = BIT(offset); 981ceacea2SWilliam Breathitt Gray unsigned long flags; 991ceacea2SWilliam Breathitt Gray 1001ceacea2SWilliam Breathitt Gray if (offset > 15) 1011ceacea2SWilliam Breathitt Gray return; 1021ceacea2SWilliam Breathitt Gray 1031ceacea2SWilliam Breathitt Gray spin_lock_irqsave(&idio16gpio->lock, flags); 1041ceacea2SWilliam Breathitt Gray 1051ceacea2SWilliam Breathitt Gray if (value) 1066e0171b4SWilliam Breathitt Gray idio16gpio->out_state |= mask; 1071ceacea2SWilliam Breathitt Gray else 1086e0171b4SWilliam Breathitt Gray idio16gpio->out_state &= ~mask; 1091ceacea2SWilliam Breathitt Gray 1101ceacea2SWilliam Breathitt Gray if (offset > 7) 1111ceacea2SWilliam Breathitt Gray outb(idio16gpio->out_state >> 8, idio16gpio->base + 4); 1121ceacea2SWilliam Breathitt Gray else 1131ceacea2SWilliam Breathitt Gray outb(idio16gpio->out_state, idio16gpio->base); 1141ceacea2SWilliam Breathitt Gray 1151ceacea2SWilliam Breathitt Gray spin_unlock_irqrestore(&idio16gpio->lock, flags); 1161ceacea2SWilliam Breathitt Gray } 1171ceacea2SWilliam Breathitt Gray 118a1184147SWilliam Breathitt Gray static void idio_16_irq_ack(struct irq_data *data) 119a1184147SWilliam Breathitt Gray { 120a1184147SWilliam Breathitt Gray struct gpio_chip *chip = irq_data_get_irq_chip_data(data); 121a1184147SWilliam Breathitt Gray struct idio_16_gpio *const idio16gpio = to_idio16gpio(chip); 122a1184147SWilliam Breathitt Gray unsigned long flags; 123a1184147SWilliam Breathitt Gray 124a1184147SWilliam Breathitt Gray spin_lock_irqsave(&idio16gpio->lock, flags); 125a1184147SWilliam Breathitt Gray 126a1184147SWilliam Breathitt Gray outb(0, idio16gpio->base + 1); 127a1184147SWilliam Breathitt Gray 128a1184147SWilliam Breathitt Gray spin_unlock_irqrestore(&idio16gpio->lock, flags); 129a1184147SWilliam Breathitt Gray } 130a1184147SWilliam Breathitt Gray 131a1184147SWilliam Breathitt Gray static void idio_16_irq_mask(struct irq_data *data) 132a1184147SWilliam Breathitt Gray { 133a1184147SWilliam Breathitt Gray struct gpio_chip *chip = irq_data_get_irq_chip_data(data); 134a1184147SWilliam Breathitt Gray struct idio_16_gpio *const idio16gpio = to_idio16gpio(chip); 135a1184147SWilliam Breathitt Gray const unsigned long mask = BIT(irqd_to_hwirq(data)); 136a1184147SWilliam Breathitt Gray unsigned long flags; 137a1184147SWilliam Breathitt Gray 138a1184147SWilliam Breathitt Gray idio16gpio->irq_mask &= ~mask; 139a1184147SWilliam Breathitt Gray 140a1184147SWilliam Breathitt Gray if (!idio16gpio->irq_mask) { 141a1184147SWilliam Breathitt Gray spin_lock_irqsave(&idio16gpio->lock, flags); 142a1184147SWilliam Breathitt Gray 143a1184147SWilliam Breathitt Gray outb(0, idio16gpio->base + 2); 144a1184147SWilliam Breathitt Gray 145a1184147SWilliam Breathitt Gray spin_unlock_irqrestore(&idio16gpio->lock, flags); 146a1184147SWilliam Breathitt Gray } 147a1184147SWilliam Breathitt Gray } 148a1184147SWilliam Breathitt Gray 149a1184147SWilliam Breathitt Gray static void idio_16_irq_unmask(struct irq_data *data) 150a1184147SWilliam Breathitt Gray { 151a1184147SWilliam Breathitt Gray struct gpio_chip *chip = irq_data_get_irq_chip_data(data); 152a1184147SWilliam Breathitt Gray struct idio_16_gpio *const idio16gpio = to_idio16gpio(chip); 153a1184147SWilliam Breathitt Gray const unsigned long mask = BIT(irqd_to_hwirq(data)); 154a1184147SWilliam Breathitt Gray const unsigned long prev_irq_mask = idio16gpio->irq_mask; 155a1184147SWilliam Breathitt Gray unsigned long flags; 156a1184147SWilliam Breathitt Gray 157a1184147SWilliam Breathitt Gray idio16gpio->irq_mask |= mask; 158a1184147SWilliam Breathitt Gray 159a1184147SWilliam Breathitt Gray if (!prev_irq_mask) { 160a1184147SWilliam Breathitt Gray spin_lock_irqsave(&idio16gpio->lock, flags); 161a1184147SWilliam Breathitt Gray 162a1184147SWilliam Breathitt Gray outb(0, idio16gpio->base + 1); 163a1184147SWilliam Breathitt Gray inb(idio16gpio->base + 2); 164a1184147SWilliam Breathitt Gray 165a1184147SWilliam Breathitt Gray spin_unlock_irqrestore(&idio16gpio->lock, flags); 166a1184147SWilliam Breathitt Gray } 167a1184147SWilliam Breathitt Gray } 168a1184147SWilliam Breathitt Gray 169a1184147SWilliam Breathitt Gray static int idio_16_irq_set_type(struct irq_data *data, unsigned flow_type) 170a1184147SWilliam Breathitt Gray { 171a1184147SWilliam Breathitt Gray /* The only valid irq types are none and both-edges */ 172a1184147SWilliam Breathitt Gray if (flow_type != IRQ_TYPE_NONE && 173a1184147SWilliam Breathitt Gray (flow_type & IRQ_TYPE_EDGE_BOTH) != IRQ_TYPE_EDGE_BOTH) 174a1184147SWilliam Breathitt Gray return -EINVAL; 175a1184147SWilliam Breathitt Gray 176a1184147SWilliam Breathitt Gray return 0; 177a1184147SWilliam Breathitt Gray } 178a1184147SWilliam Breathitt Gray 179a1184147SWilliam Breathitt Gray static struct irq_chip idio_16_irqchip = { 180a1184147SWilliam Breathitt Gray .name = "104-idio-16", 181a1184147SWilliam Breathitt Gray .irq_ack = idio_16_irq_ack, 182a1184147SWilliam Breathitt Gray .irq_mask = idio_16_irq_mask, 183a1184147SWilliam Breathitt Gray .irq_unmask = idio_16_irq_unmask, 184a1184147SWilliam Breathitt Gray .irq_set_type = idio_16_irq_set_type 185a1184147SWilliam Breathitt Gray }; 186a1184147SWilliam Breathitt Gray 187a1184147SWilliam Breathitt Gray static irqreturn_t idio_16_irq_handler(int irq, void *dev_id) 188a1184147SWilliam Breathitt Gray { 189a1184147SWilliam Breathitt Gray struct idio_16_gpio *const idio16gpio = dev_id; 190a1184147SWilliam Breathitt Gray struct gpio_chip *const chip = &idio16gpio->chip; 191a1184147SWilliam Breathitt Gray int gpio; 192a1184147SWilliam Breathitt Gray 193a1184147SWilliam Breathitt Gray for_each_set_bit(gpio, &idio16gpio->irq_mask, chip->ngpio) 194a1184147SWilliam Breathitt Gray generic_handle_irq(irq_find_mapping(chip->irqdomain, gpio)); 195a1184147SWilliam Breathitt Gray 196a1184147SWilliam Breathitt Gray return IRQ_HANDLED; 197a1184147SWilliam Breathitt Gray } 198a1184147SWilliam Breathitt Gray 1991ceacea2SWilliam Breathitt Gray static int __init idio_16_probe(struct platform_device *pdev) 2001ceacea2SWilliam Breathitt Gray { 2011ceacea2SWilliam Breathitt Gray struct device *dev = &pdev->dev; 2021ceacea2SWilliam Breathitt Gray struct idio_16_gpio *idio16gpio; 2036e0171b4SWilliam Breathitt Gray const unsigned base = idio_16_base; 2046e0171b4SWilliam Breathitt Gray const unsigned extent = 8; 2056e0171b4SWilliam Breathitt Gray const char *const name = dev_name(dev); 2061ceacea2SWilliam Breathitt Gray int err; 2076e0171b4SWilliam Breathitt Gray const unsigned irq = idio_16_irq; 2081ceacea2SWilliam Breathitt Gray 2091ceacea2SWilliam Breathitt Gray idio16gpio = devm_kzalloc(dev, sizeof(*idio16gpio), GFP_KERNEL); 2101ceacea2SWilliam Breathitt Gray if (!idio16gpio) 2111ceacea2SWilliam Breathitt Gray return -ENOMEM; 2121ceacea2SWilliam Breathitt Gray 2136e0171b4SWilliam Breathitt Gray if (!request_region(base, extent, name)) { 2141ceacea2SWilliam Breathitt Gray dev_err(dev, "Unable to lock %s port addresses (0x%X-0x%X)\n", 2156e0171b4SWilliam Breathitt Gray name, base, base + extent); 2161ceacea2SWilliam Breathitt Gray err = -EBUSY; 2171ceacea2SWilliam Breathitt Gray goto err_lock_io_port; 2181ceacea2SWilliam Breathitt Gray } 2191ceacea2SWilliam Breathitt Gray 2206e0171b4SWilliam Breathitt Gray idio16gpio->chip.label = name; 22158383c78SLinus Walleij idio16gpio->chip.parent = dev; 2221ceacea2SWilliam Breathitt Gray idio16gpio->chip.owner = THIS_MODULE; 2231ceacea2SWilliam Breathitt Gray idio16gpio->chip.base = -1; 2241ceacea2SWilliam Breathitt Gray idio16gpio->chip.ngpio = 32; 2251ceacea2SWilliam Breathitt Gray idio16gpio->chip.get_direction = idio_16_gpio_get_direction; 2261ceacea2SWilliam Breathitt Gray idio16gpio->chip.direction_input = idio_16_gpio_direction_input; 2271ceacea2SWilliam Breathitt Gray idio16gpio->chip.direction_output = idio_16_gpio_direction_output; 2281ceacea2SWilliam Breathitt Gray idio16gpio->chip.get = idio_16_gpio_get; 2291ceacea2SWilliam Breathitt Gray idio16gpio->chip.set = idio_16_gpio_set; 2306e0171b4SWilliam Breathitt Gray idio16gpio->base = base; 2316e0171b4SWilliam Breathitt Gray idio16gpio->extent = extent; 2326e0171b4SWilliam Breathitt Gray idio16gpio->irq = irq; 2331ceacea2SWilliam Breathitt Gray idio16gpio->out_state = 0xFFFF; 2341ceacea2SWilliam Breathitt Gray 2351ceacea2SWilliam Breathitt Gray spin_lock_init(&idio16gpio->lock); 2361ceacea2SWilliam Breathitt Gray 2371ceacea2SWilliam Breathitt Gray dev_set_drvdata(dev, idio16gpio); 2381ceacea2SWilliam Breathitt Gray 2391ceacea2SWilliam Breathitt Gray err = gpiochip_add(&idio16gpio->chip); 2401ceacea2SWilliam Breathitt Gray if (err) { 2411ceacea2SWilliam Breathitt Gray dev_err(dev, "GPIO registering failed (%d)\n", err); 2421ceacea2SWilliam Breathitt Gray goto err_gpio_register; 2431ceacea2SWilliam Breathitt Gray } 2441ceacea2SWilliam Breathitt Gray 245fb50cdfeSWilliam Breathitt Gray /* Disable IRQ by default */ 246fb50cdfeSWilliam Breathitt Gray outb(0, base + 2); 247fb50cdfeSWilliam Breathitt Gray 248a1184147SWilliam Breathitt Gray err = gpiochip_irqchip_add(&idio16gpio->chip, &idio_16_irqchip, 0, 249a1184147SWilliam Breathitt Gray handle_edge_irq, IRQ_TYPE_NONE); 250a1184147SWilliam Breathitt Gray if (err) { 251a1184147SWilliam Breathitt Gray dev_err(dev, "Could not add irqchip (%d)\n", err); 252a1184147SWilliam Breathitt Gray goto err_gpiochip_irqchip_add; 253a1184147SWilliam Breathitt Gray } 254a1184147SWilliam Breathitt Gray 2556e0171b4SWilliam Breathitt Gray err = request_irq(irq, idio_16_irq_handler, 0, name, idio16gpio); 256a1184147SWilliam Breathitt Gray if (err) { 257a1184147SWilliam Breathitt Gray dev_err(dev, "IRQ handler registering failed (%d)\n", err); 258a1184147SWilliam Breathitt Gray goto err_request_irq; 259a1184147SWilliam Breathitt Gray } 260a1184147SWilliam Breathitt Gray 2611ceacea2SWilliam Breathitt Gray return 0; 2621ceacea2SWilliam Breathitt Gray 263a1184147SWilliam Breathitt Gray err_request_irq: 264a1184147SWilliam Breathitt Gray err_gpiochip_irqchip_add: 265a1184147SWilliam Breathitt Gray gpiochip_remove(&idio16gpio->chip); 2661ceacea2SWilliam Breathitt Gray err_gpio_register: 2676e0171b4SWilliam Breathitt Gray release_region(base, extent); 2681ceacea2SWilliam Breathitt Gray err_lock_io_port: 2691ceacea2SWilliam Breathitt Gray return err; 2701ceacea2SWilliam Breathitt Gray } 2711ceacea2SWilliam Breathitt Gray 2721ceacea2SWilliam Breathitt Gray static int idio_16_remove(struct platform_device *pdev) 2731ceacea2SWilliam Breathitt Gray { 2741ceacea2SWilliam Breathitt Gray struct idio_16_gpio *const idio16gpio = platform_get_drvdata(pdev); 2751ceacea2SWilliam Breathitt Gray 276a1184147SWilliam Breathitt Gray free_irq(idio16gpio->irq, idio16gpio); 2771ceacea2SWilliam Breathitt Gray gpiochip_remove(&idio16gpio->chip); 2781ceacea2SWilliam Breathitt Gray release_region(idio16gpio->base, idio16gpio->extent); 2791ceacea2SWilliam Breathitt Gray 2801ceacea2SWilliam Breathitt Gray return 0; 2811ceacea2SWilliam Breathitt Gray } 2821ceacea2SWilliam Breathitt Gray 2831ceacea2SWilliam Breathitt Gray static struct platform_device *idio_16_device; 2841ceacea2SWilliam Breathitt Gray 2851ceacea2SWilliam Breathitt Gray static struct platform_driver idio_16_driver = { 2861ceacea2SWilliam Breathitt Gray .driver = { 2871ceacea2SWilliam Breathitt Gray .name = "104-idio-16" 2881ceacea2SWilliam Breathitt Gray }, 2891ceacea2SWilliam Breathitt Gray .remove = idio_16_remove 2901ceacea2SWilliam Breathitt Gray }; 2911ceacea2SWilliam Breathitt Gray 2921ceacea2SWilliam Breathitt Gray static void __exit idio_16_exit(void) 2931ceacea2SWilliam Breathitt Gray { 2941ceacea2SWilliam Breathitt Gray platform_device_unregister(idio_16_device); 2951ceacea2SWilliam Breathitt Gray platform_driver_unregister(&idio_16_driver); 2961ceacea2SWilliam Breathitt Gray } 2971ceacea2SWilliam Breathitt Gray 2981ceacea2SWilliam Breathitt Gray static int __init idio_16_init(void) 2991ceacea2SWilliam Breathitt Gray { 3001ceacea2SWilliam Breathitt Gray int err; 3011ceacea2SWilliam Breathitt Gray 3021ceacea2SWilliam Breathitt Gray idio_16_device = platform_device_alloc(idio_16_driver.driver.name, -1); 3031ceacea2SWilliam Breathitt Gray if (!idio_16_device) 3041ceacea2SWilliam Breathitt Gray return -ENOMEM; 3051ceacea2SWilliam Breathitt Gray 3061ceacea2SWilliam Breathitt Gray err = platform_device_add(idio_16_device); 3071ceacea2SWilliam Breathitt Gray if (err) 3081ceacea2SWilliam Breathitt Gray goto err_platform_device; 3091ceacea2SWilliam Breathitt Gray 3101ceacea2SWilliam Breathitt Gray err = platform_driver_probe(&idio_16_driver, idio_16_probe); 3111ceacea2SWilliam Breathitt Gray if (err) 3121ceacea2SWilliam Breathitt Gray goto err_platform_driver; 3131ceacea2SWilliam Breathitt Gray 3141ceacea2SWilliam Breathitt Gray return 0; 3151ceacea2SWilliam Breathitt Gray 3161ceacea2SWilliam Breathitt Gray err_platform_driver: 3171ceacea2SWilliam Breathitt Gray platform_device_del(idio_16_device); 3181ceacea2SWilliam Breathitt Gray err_platform_device: 3191ceacea2SWilliam Breathitt Gray platform_device_put(idio_16_device); 3201ceacea2SWilliam Breathitt Gray return err; 3211ceacea2SWilliam Breathitt Gray } 3221ceacea2SWilliam Breathitt Gray 3231ceacea2SWilliam Breathitt Gray module_init(idio_16_init); 3241ceacea2SWilliam Breathitt Gray module_exit(idio_16_exit); 3251ceacea2SWilliam Breathitt Gray 3261ceacea2SWilliam Breathitt Gray MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); 3271ceacea2SWilliam Breathitt Gray MODULE_DESCRIPTION("ACCES 104-IDIO-16 GPIO driver"); 3281ceacea2SWilliam Breathitt Gray MODULE_LICENSE("GPL"); 329