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 */ 141ceacea2SWilliam Breathitt Gray #include <linux/device.h> 151ceacea2SWilliam Breathitt Gray #include <linux/errno.h> 161ceacea2SWilliam Breathitt Gray #include <linux/gpio/driver.h> 171ceacea2SWilliam Breathitt Gray #include <linux/io.h> 181ceacea2SWilliam Breathitt Gray #include <linux/ioport.h> 191ceacea2SWilliam Breathitt Gray #include <linux/kernel.h> 201ceacea2SWilliam Breathitt Gray #include <linux/module.h> 211ceacea2SWilliam Breathitt Gray #include <linux/moduleparam.h> 221ceacea2SWilliam Breathitt Gray #include <linux/platform_device.h> 231ceacea2SWilliam Breathitt Gray #include <linux/spinlock.h> 241ceacea2SWilliam Breathitt Gray 251ceacea2SWilliam Breathitt Gray static unsigned idio_16_base; 261ceacea2SWilliam Breathitt Gray module_param(idio_16_base, uint, 0); 271ceacea2SWilliam Breathitt Gray MODULE_PARM_DESC(idio_16_base, "ACCES 104-IDIO-16 base address"); 281ceacea2SWilliam Breathitt Gray 291ceacea2SWilliam Breathitt Gray /** 301ceacea2SWilliam Breathitt Gray * struct idio_16_gpio - GPIO device private data structure 311ceacea2SWilliam Breathitt Gray * @chip: instance of the gpio_chip 321ceacea2SWilliam Breathitt Gray * @lock: synchronization lock to prevent gpio_set race conditions 331ceacea2SWilliam Breathitt Gray * @base: base port address of the GPIO device 341ceacea2SWilliam Breathitt Gray * @extent: extent of port address region of the GPIO device 351ceacea2SWilliam Breathitt Gray * @out_state: output bits state 361ceacea2SWilliam Breathitt Gray */ 371ceacea2SWilliam Breathitt Gray struct idio_16_gpio { 381ceacea2SWilliam Breathitt Gray struct gpio_chip chip; 391ceacea2SWilliam Breathitt Gray spinlock_t lock; 401ceacea2SWilliam Breathitt Gray unsigned base; 411ceacea2SWilliam Breathitt Gray unsigned extent; 421ceacea2SWilliam Breathitt Gray unsigned out_state; 431ceacea2SWilliam Breathitt Gray }; 441ceacea2SWilliam Breathitt Gray 451ceacea2SWilliam Breathitt Gray static int idio_16_gpio_get_direction(struct gpio_chip *chip, unsigned offset) 461ceacea2SWilliam Breathitt Gray { 471ceacea2SWilliam Breathitt Gray if (offset > 15) 481ceacea2SWilliam Breathitt Gray return 1; 491ceacea2SWilliam Breathitt Gray 501ceacea2SWilliam Breathitt Gray return 0; 511ceacea2SWilliam Breathitt Gray } 521ceacea2SWilliam Breathitt Gray 531ceacea2SWilliam Breathitt Gray static int idio_16_gpio_direction_input(struct gpio_chip *chip, unsigned offset) 541ceacea2SWilliam Breathitt Gray { 551ceacea2SWilliam Breathitt Gray return 0; 561ceacea2SWilliam Breathitt Gray } 571ceacea2SWilliam Breathitt Gray 581ceacea2SWilliam Breathitt Gray static int idio_16_gpio_direction_output(struct gpio_chip *chip, 591ceacea2SWilliam Breathitt Gray unsigned offset, int value) 601ceacea2SWilliam Breathitt Gray { 611ceacea2SWilliam Breathitt Gray chip->set(chip, offset, value); 621ceacea2SWilliam Breathitt Gray return 0; 631ceacea2SWilliam Breathitt Gray } 641ceacea2SWilliam Breathitt Gray 651ceacea2SWilliam Breathitt Gray static struct idio_16_gpio *to_idio16gpio(struct gpio_chip *gc) 661ceacea2SWilliam Breathitt Gray { 671ceacea2SWilliam Breathitt Gray return container_of(gc, struct idio_16_gpio, chip); 681ceacea2SWilliam Breathitt Gray } 691ceacea2SWilliam Breathitt Gray 701ceacea2SWilliam Breathitt Gray static int idio_16_gpio_get(struct gpio_chip *chip, unsigned offset) 711ceacea2SWilliam Breathitt Gray { 721ceacea2SWilliam Breathitt Gray struct idio_16_gpio *const idio16gpio = to_idio16gpio(chip); 731ceacea2SWilliam Breathitt Gray const unsigned BIT_MASK = 1U << (offset-16); 741ceacea2SWilliam Breathitt Gray 751ceacea2SWilliam Breathitt Gray if (offset < 16) 761ceacea2SWilliam Breathitt Gray return -EINVAL; 771ceacea2SWilliam Breathitt Gray 781ceacea2SWilliam Breathitt Gray if (offset < 24) 791ceacea2SWilliam Breathitt Gray return !!(inb(idio16gpio->base + 1) & BIT_MASK); 801ceacea2SWilliam Breathitt Gray 811ceacea2SWilliam Breathitt Gray return !!(inb(idio16gpio->base + 5) & (BIT_MASK>>8)); 821ceacea2SWilliam Breathitt Gray } 831ceacea2SWilliam Breathitt Gray 841ceacea2SWilliam Breathitt Gray static void idio_16_gpio_set(struct gpio_chip *chip, unsigned offset, int value) 851ceacea2SWilliam Breathitt Gray { 861ceacea2SWilliam Breathitt Gray struct idio_16_gpio *const idio16gpio = to_idio16gpio(chip); 871ceacea2SWilliam Breathitt Gray const unsigned BIT_MASK = 1U << offset; 881ceacea2SWilliam Breathitt Gray unsigned long flags; 891ceacea2SWilliam Breathitt Gray 901ceacea2SWilliam Breathitt Gray if (offset > 15) 911ceacea2SWilliam Breathitt Gray return; 921ceacea2SWilliam Breathitt Gray 931ceacea2SWilliam Breathitt Gray spin_lock_irqsave(&idio16gpio->lock, flags); 941ceacea2SWilliam Breathitt Gray 951ceacea2SWilliam Breathitt Gray if (value) 961ceacea2SWilliam Breathitt Gray idio16gpio->out_state |= BIT_MASK; 971ceacea2SWilliam Breathitt Gray else 981ceacea2SWilliam Breathitt Gray idio16gpio->out_state &= ~BIT_MASK; 991ceacea2SWilliam Breathitt Gray 1001ceacea2SWilliam Breathitt Gray if (offset > 7) 1011ceacea2SWilliam Breathitt Gray outb(idio16gpio->out_state >> 8, idio16gpio->base + 4); 1021ceacea2SWilliam Breathitt Gray else 1031ceacea2SWilliam Breathitt Gray outb(idio16gpio->out_state, idio16gpio->base); 1041ceacea2SWilliam Breathitt Gray 1051ceacea2SWilliam Breathitt Gray spin_unlock_irqrestore(&idio16gpio->lock, flags); 1061ceacea2SWilliam Breathitt Gray } 1071ceacea2SWilliam Breathitt Gray 1081ceacea2SWilliam Breathitt Gray static int __init idio_16_probe(struct platform_device *pdev) 1091ceacea2SWilliam Breathitt Gray { 1101ceacea2SWilliam Breathitt Gray struct device *dev = &pdev->dev; 1111ceacea2SWilliam Breathitt Gray struct idio_16_gpio *idio16gpio; 1121ceacea2SWilliam Breathitt Gray int err; 1131ceacea2SWilliam Breathitt Gray 1141ceacea2SWilliam Breathitt Gray const unsigned BASE = idio_16_base; 1151ceacea2SWilliam Breathitt Gray const unsigned EXTENT = 8; 1161ceacea2SWilliam Breathitt Gray const char *const NAME = dev_name(dev); 1171ceacea2SWilliam Breathitt Gray 1181ceacea2SWilliam Breathitt Gray idio16gpio = devm_kzalloc(dev, sizeof(*idio16gpio), GFP_KERNEL); 1191ceacea2SWilliam Breathitt Gray if (!idio16gpio) 1201ceacea2SWilliam Breathitt Gray return -ENOMEM; 1211ceacea2SWilliam Breathitt Gray 1221ceacea2SWilliam Breathitt Gray if (!request_region(BASE, EXTENT, NAME)) { 1231ceacea2SWilliam Breathitt Gray dev_err(dev, "Unable to lock %s port addresses (0x%X-0x%X)\n", 1241ceacea2SWilliam Breathitt Gray NAME, BASE, BASE + EXTENT); 1251ceacea2SWilliam Breathitt Gray err = -EBUSY; 1261ceacea2SWilliam Breathitt Gray goto err_lock_io_port; 1271ceacea2SWilliam Breathitt Gray } 1281ceacea2SWilliam Breathitt Gray 1291ceacea2SWilliam Breathitt Gray idio16gpio->chip.label = NAME; 1301ceacea2SWilliam Breathitt Gray idio16gpio->chip.dev = dev; 1311ceacea2SWilliam Breathitt Gray idio16gpio->chip.owner = THIS_MODULE; 1321ceacea2SWilliam Breathitt Gray idio16gpio->chip.base = -1; 1331ceacea2SWilliam Breathitt Gray idio16gpio->chip.ngpio = 32; 1341ceacea2SWilliam Breathitt Gray idio16gpio->chip.get_direction = idio_16_gpio_get_direction; 1351ceacea2SWilliam Breathitt Gray idio16gpio->chip.direction_input = idio_16_gpio_direction_input; 1361ceacea2SWilliam Breathitt Gray idio16gpio->chip.direction_output = idio_16_gpio_direction_output; 1371ceacea2SWilliam Breathitt Gray idio16gpio->chip.get = idio_16_gpio_get; 1381ceacea2SWilliam Breathitt Gray idio16gpio->chip.set = idio_16_gpio_set; 1391ceacea2SWilliam Breathitt Gray idio16gpio->base = BASE; 1401ceacea2SWilliam Breathitt Gray idio16gpio->extent = EXTENT; 1411ceacea2SWilliam Breathitt Gray idio16gpio->out_state = 0xFFFF; 1421ceacea2SWilliam Breathitt Gray 1431ceacea2SWilliam Breathitt Gray spin_lock_init(&idio16gpio->lock); 1441ceacea2SWilliam Breathitt Gray 1451ceacea2SWilliam Breathitt Gray dev_set_drvdata(dev, idio16gpio); 1461ceacea2SWilliam Breathitt Gray 1471ceacea2SWilliam Breathitt Gray err = gpiochip_add(&idio16gpio->chip); 1481ceacea2SWilliam Breathitt Gray if (err) { 1491ceacea2SWilliam Breathitt Gray dev_err(dev, "GPIO registering failed (%d)\n", err); 1501ceacea2SWilliam Breathitt Gray goto err_gpio_register; 1511ceacea2SWilliam Breathitt Gray } 1521ceacea2SWilliam Breathitt Gray 1531ceacea2SWilliam Breathitt Gray return 0; 1541ceacea2SWilliam Breathitt Gray 1551ceacea2SWilliam Breathitt Gray err_gpio_register: 1561ceacea2SWilliam Breathitt Gray release_region(BASE, EXTENT); 1571ceacea2SWilliam Breathitt Gray err_lock_io_port: 1581ceacea2SWilliam Breathitt Gray return err; 1591ceacea2SWilliam Breathitt Gray } 1601ceacea2SWilliam Breathitt Gray 1611ceacea2SWilliam Breathitt Gray static int idio_16_remove(struct platform_device *pdev) 1621ceacea2SWilliam Breathitt Gray { 1631ceacea2SWilliam Breathitt Gray struct idio_16_gpio *const idio16gpio = platform_get_drvdata(pdev); 1641ceacea2SWilliam Breathitt Gray 1651ceacea2SWilliam Breathitt Gray gpiochip_remove(&idio16gpio->chip); 1661ceacea2SWilliam Breathitt Gray release_region(idio16gpio->base, idio16gpio->extent); 1671ceacea2SWilliam Breathitt Gray 1681ceacea2SWilliam Breathitt Gray return 0; 1691ceacea2SWilliam Breathitt Gray } 1701ceacea2SWilliam Breathitt Gray 1711ceacea2SWilliam Breathitt Gray static struct platform_device *idio_16_device; 1721ceacea2SWilliam Breathitt Gray 1731ceacea2SWilliam Breathitt Gray static struct platform_driver idio_16_driver = { 1741ceacea2SWilliam Breathitt Gray .driver = { 1751ceacea2SWilliam Breathitt Gray .name = "104-idio-16" 1761ceacea2SWilliam Breathitt Gray }, 1771ceacea2SWilliam Breathitt Gray .remove = idio_16_remove 1781ceacea2SWilliam Breathitt Gray }; 1791ceacea2SWilliam Breathitt Gray 1801ceacea2SWilliam Breathitt Gray static void __exit idio_16_exit(void) 1811ceacea2SWilliam Breathitt Gray { 1821ceacea2SWilliam Breathitt Gray platform_device_unregister(idio_16_device); 1831ceacea2SWilliam Breathitt Gray platform_driver_unregister(&idio_16_driver); 1841ceacea2SWilliam Breathitt Gray } 1851ceacea2SWilliam Breathitt Gray 1861ceacea2SWilliam Breathitt Gray static int __init idio_16_init(void) 1871ceacea2SWilliam Breathitt Gray { 1881ceacea2SWilliam Breathitt Gray int err; 1891ceacea2SWilliam Breathitt Gray 1901ceacea2SWilliam Breathitt Gray idio_16_device = platform_device_alloc(idio_16_driver.driver.name, -1); 1911ceacea2SWilliam Breathitt Gray if (!idio_16_device) 1921ceacea2SWilliam Breathitt Gray return -ENOMEM; 1931ceacea2SWilliam Breathitt Gray 1941ceacea2SWilliam Breathitt Gray err = platform_device_add(idio_16_device); 1951ceacea2SWilliam Breathitt Gray if (err) 1961ceacea2SWilliam Breathitt Gray goto err_platform_device; 1971ceacea2SWilliam Breathitt Gray 1981ceacea2SWilliam Breathitt Gray err = platform_driver_probe(&idio_16_driver, idio_16_probe); 1991ceacea2SWilliam Breathitt Gray if (err) 2001ceacea2SWilliam Breathitt Gray goto err_platform_driver; 2011ceacea2SWilliam Breathitt Gray 2021ceacea2SWilliam Breathitt Gray return 0; 2031ceacea2SWilliam Breathitt Gray 2041ceacea2SWilliam Breathitt Gray err_platform_driver: 2051ceacea2SWilliam Breathitt Gray platform_device_del(idio_16_device); 2061ceacea2SWilliam Breathitt Gray err_platform_device: 2071ceacea2SWilliam Breathitt Gray platform_device_put(idio_16_device); 2081ceacea2SWilliam Breathitt Gray return err; 2091ceacea2SWilliam Breathitt Gray } 2101ceacea2SWilliam Breathitt Gray 2111ceacea2SWilliam Breathitt Gray module_init(idio_16_init); 2121ceacea2SWilliam Breathitt Gray module_exit(idio_16_exit); 2131ceacea2SWilliam Breathitt Gray 2141ceacea2SWilliam Breathitt Gray MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); 2151ceacea2SWilliam Breathitt Gray MODULE_DESCRIPTION("ACCES 104-IDIO-16 GPIO driver"); 2161ceacea2SWilliam Breathitt Gray MODULE_LICENSE("GPL"); 217