16ea5dcdfSWilliam Breathitt Gray /* 26ea5dcdfSWilliam Breathitt Gray * GPIO driver for the Diamond Systems GPIO-MM 36ea5dcdfSWilliam Breathitt Gray * Copyright (C) 2016 William Breathitt Gray 46ea5dcdfSWilliam Breathitt Gray * 56ea5dcdfSWilliam Breathitt Gray * This program is free software; you can redistribute it and/or modify 66ea5dcdfSWilliam Breathitt Gray * it under the terms of the GNU General Public License, version 2, as 76ea5dcdfSWilliam Breathitt Gray * published by the Free Software Foundation. 86ea5dcdfSWilliam Breathitt Gray * 96ea5dcdfSWilliam Breathitt Gray * This program is distributed in the hope that it will be useful, but 106ea5dcdfSWilliam Breathitt Gray * WITHOUT ANY WARRANTY; without even the implied warranty of 116ea5dcdfSWilliam Breathitt Gray * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 126ea5dcdfSWilliam Breathitt Gray * General Public License for more details. 136ea5dcdfSWilliam Breathitt Gray * 146ea5dcdfSWilliam Breathitt Gray * This driver supports the following Diamond Systems devices: GPIO-MM and 156ea5dcdfSWilliam Breathitt Gray * GPIO-MM-12. 166ea5dcdfSWilliam Breathitt Gray */ 176ea5dcdfSWilliam Breathitt Gray #include <linux/bitops.h> 186ea5dcdfSWilliam Breathitt Gray #include <linux/device.h> 196ea5dcdfSWilliam Breathitt Gray #include <linux/errno.h> 206ea5dcdfSWilliam Breathitt Gray #include <linux/gpio/driver.h> 216ea5dcdfSWilliam Breathitt Gray #include <linux/io.h> 226ea5dcdfSWilliam Breathitt Gray #include <linux/ioport.h> 236ea5dcdfSWilliam Breathitt Gray #include <linux/isa.h> 246ea5dcdfSWilliam Breathitt Gray #include <linux/kernel.h> 256ea5dcdfSWilliam Breathitt Gray #include <linux/module.h> 266ea5dcdfSWilliam Breathitt Gray #include <linux/moduleparam.h> 276ea5dcdfSWilliam Breathitt Gray #include <linux/spinlock.h> 286ea5dcdfSWilliam Breathitt Gray 296ea5dcdfSWilliam Breathitt Gray #define GPIOMM_EXTENT 8 306ea5dcdfSWilliam Breathitt Gray #define MAX_NUM_GPIOMM max_num_isa_dev(GPIOMM_EXTENT) 316ea5dcdfSWilliam Breathitt Gray 326ea5dcdfSWilliam Breathitt Gray static unsigned int base[MAX_NUM_GPIOMM]; 336ea5dcdfSWilliam Breathitt Gray static unsigned int num_gpiomm; 346ea5dcdfSWilliam Breathitt Gray module_param_array(base, uint, &num_gpiomm, 0); 356ea5dcdfSWilliam Breathitt Gray MODULE_PARM_DESC(base, "Diamond Systems GPIO-MM base addresses"); 366ea5dcdfSWilliam Breathitt Gray 376ea5dcdfSWilliam Breathitt Gray /** 386ea5dcdfSWilliam Breathitt Gray * struct gpiomm_gpio - GPIO device private data structure 396ea5dcdfSWilliam Breathitt Gray * @chip: instance of the gpio_chip 406ea5dcdfSWilliam Breathitt Gray * @io_state: bit I/O state (whether bit is set to input or output) 416ea5dcdfSWilliam Breathitt Gray * @out_state: output bits state 426ea5dcdfSWilliam Breathitt Gray * @control: Control registers state 436ea5dcdfSWilliam Breathitt Gray * @lock: synchronization lock to prevent I/O race conditions 446ea5dcdfSWilliam Breathitt Gray * @base: base port address of the GPIO device 456ea5dcdfSWilliam Breathitt Gray */ 466ea5dcdfSWilliam Breathitt Gray struct gpiomm_gpio { 476ea5dcdfSWilliam Breathitt Gray struct gpio_chip chip; 486ea5dcdfSWilliam Breathitt Gray unsigned char io_state[6]; 496ea5dcdfSWilliam Breathitt Gray unsigned char out_state[6]; 506ea5dcdfSWilliam Breathitt Gray unsigned char control[2]; 516ea5dcdfSWilliam Breathitt Gray spinlock_t lock; 526ea5dcdfSWilliam Breathitt Gray unsigned int base; 536ea5dcdfSWilliam Breathitt Gray }; 546ea5dcdfSWilliam Breathitt Gray 556ea5dcdfSWilliam Breathitt Gray static int gpiomm_gpio_get_direction(struct gpio_chip *chip, 566ea5dcdfSWilliam Breathitt Gray unsigned int offset) 576ea5dcdfSWilliam Breathitt Gray { 586ea5dcdfSWilliam Breathitt Gray struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); 596ea5dcdfSWilliam Breathitt Gray const unsigned int port = offset / 8; 606ea5dcdfSWilliam Breathitt Gray const unsigned int mask = BIT(offset % 8); 616ea5dcdfSWilliam Breathitt Gray 626ea5dcdfSWilliam Breathitt Gray return !!(gpiommgpio->io_state[port] & mask); 636ea5dcdfSWilliam Breathitt Gray } 646ea5dcdfSWilliam Breathitt Gray 656ea5dcdfSWilliam Breathitt Gray static int gpiomm_gpio_direction_input(struct gpio_chip *chip, 666ea5dcdfSWilliam Breathitt Gray unsigned int offset) 676ea5dcdfSWilliam Breathitt Gray { 686ea5dcdfSWilliam Breathitt Gray struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); 696ea5dcdfSWilliam Breathitt Gray const unsigned int io_port = offset / 8; 706ea5dcdfSWilliam Breathitt Gray const unsigned int control_port = io_port / 3; 716ea5dcdfSWilliam Breathitt Gray const unsigned int control_addr = gpiommgpio->base + 3 + control_port*4; 726ea5dcdfSWilliam Breathitt Gray unsigned long flags; 736ea5dcdfSWilliam Breathitt Gray unsigned int control; 746ea5dcdfSWilliam Breathitt Gray 756ea5dcdfSWilliam Breathitt Gray spin_lock_irqsave(&gpiommgpio->lock, flags); 766ea5dcdfSWilliam Breathitt Gray 776ea5dcdfSWilliam Breathitt Gray /* Check if configuring Port C */ 786ea5dcdfSWilliam Breathitt Gray if (io_port == 2 || io_port == 5) { 796ea5dcdfSWilliam Breathitt Gray /* Port C can be configured by nibble */ 806ea5dcdfSWilliam Breathitt Gray if (offset % 8 > 3) { 816ea5dcdfSWilliam Breathitt Gray gpiommgpio->io_state[io_port] |= 0xF0; 826ea5dcdfSWilliam Breathitt Gray gpiommgpio->control[control_port] |= BIT(3); 836ea5dcdfSWilliam Breathitt Gray } else { 846ea5dcdfSWilliam Breathitt Gray gpiommgpio->io_state[io_port] |= 0x0F; 856ea5dcdfSWilliam Breathitt Gray gpiommgpio->control[control_port] |= BIT(0); 866ea5dcdfSWilliam Breathitt Gray } 876ea5dcdfSWilliam Breathitt Gray } else { 886ea5dcdfSWilliam Breathitt Gray gpiommgpio->io_state[io_port] |= 0xFF; 896ea5dcdfSWilliam Breathitt Gray if (io_port == 0 || io_port == 3) 906ea5dcdfSWilliam Breathitt Gray gpiommgpio->control[control_port] |= BIT(4); 916ea5dcdfSWilliam Breathitt Gray else 926ea5dcdfSWilliam Breathitt Gray gpiommgpio->control[control_port] |= BIT(1); 936ea5dcdfSWilliam Breathitt Gray } 946ea5dcdfSWilliam Breathitt Gray 956ea5dcdfSWilliam Breathitt Gray control = BIT(7) | gpiommgpio->control[control_port]; 966ea5dcdfSWilliam Breathitt Gray outb(control, control_addr); 976ea5dcdfSWilliam Breathitt Gray 986ea5dcdfSWilliam Breathitt Gray spin_unlock_irqrestore(&gpiommgpio->lock, flags); 996ea5dcdfSWilliam Breathitt Gray 1006ea5dcdfSWilliam Breathitt Gray return 0; 1016ea5dcdfSWilliam Breathitt Gray } 1026ea5dcdfSWilliam Breathitt Gray 1036ea5dcdfSWilliam Breathitt Gray static int gpiomm_gpio_direction_output(struct gpio_chip *chip, 1046ea5dcdfSWilliam Breathitt Gray unsigned int offset, int value) 1056ea5dcdfSWilliam Breathitt Gray { 1066ea5dcdfSWilliam Breathitt Gray struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); 1076ea5dcdfSWilliam Breathitt Gray const unsigned int io_port = offset / 8; 1086ea5dcdfSWilliam Breathitt Gray const unsigned int control_port = io_port / 3; 1096ea5dcdfSWilliam Breathitt Gray const unsigned int mask = BIT(offset % 8); 1106ea5dcdfSWilliam Breathitt Gray const unsigned int control_addr = gpiommgpio->base + 3 + control_port*4; 1116ea5dcdfSWilliam Breathitt Gray const unsigned int out_port = (io_port > 2) ? io_port + 1 : io_port; 1126ea5dcdfSWilliam Breathitt Gray unsigned long flags; 1136ea5dcdfSWilliam Breathitt Gray unsigned int control; 1146ea5dcdfSWilliam Breathitt Gray 1156ea5dcdfSWilliam Breathitt Gray spin_lock_irqsave(&gpiommgpio->lock, flags); 1166ea5dcdfSWilliam Breathitt Gray 1176ea5dcdfSWilliam Breathitt Gray /* Check if configuring Port C */ 1186ea5dcdfSWilliam Breathitt Gray if (io_port == 2 || io_port == 5) { 1196ea5dcdfSWilliam Breathitt Gray /* Port C can be configured by nibble */ 1206ea5dcdfSWilliam Breathitt Gray if (offset % 8 > 3) { 1216ea5dcdfSWilliam Breathitt Gray gpiommgpio->io_state[io_port] &= 0x0F; 1226ea5dcdfSWilliam Breathitt Gray gpiommgpio->control[control_port] &= ~BIT(3); 1236ea5dcdfSWilliam Breathitt Gray } else { 1246ea5dcdfSWilliam Breathitt Gray gpiommgpio->io_state[io_port] &= 0xF0; 1256ea5dcdfSWilliam Breathitt Gray gpiommgpio->control[control_port] &= ~BIT(0); 1266ea5dcdfSWilliam Breathitt Gray } 1276ea5dcdfSWilliam Breathitt Gray } else { 1286ea5dcdfSWilliam Breathitt Gray gpiommgpio->io_state[io_port] &= 0x00; 1296ea5dcdfSWilliam Breathitt Gray if (io_port == 0 || io_port == 3) 1306ea5dcdfSWilliam Breathitt Gray gpiommgpio->control[control_port] &= ~BIT(4); 1316ea5dcdfSWilliam Breathitt Gray else 1326ea5dcdfSWilliam Breathitt Gray gpiommgpio->control[control_port] &= ~BIT(1); 1336ea5dcdfSWilliam Breathitt Gray } 1346ea5dcdfSWilliam Breathitt Gray 1356ea5dcdfSWilliam Breathitt Gray if (value) 1366ea5dcdfSWilliam Breathitt Gray gpiommgpio->out_state[io_port] |= mask; 1376ea5dcdfSWilliam Breathitt Gray else 1386ea5dcdfSWilliam Breathitt Gray gpiommgpio->out_state[io_port] &= ~mask; 1396ea5dcdfSWilliam Breathitt Gray 1406ea5dcdfSWilliam Breathitt Gray control = BIT(7) | gpiommgpio->control[control_port]; 1416ea5dcdfSWilliam Breathitt Gray outb(control, control_addr); 1426ea5dcdfSWilliam Breathitt Gray 1436ea5dcdfSWilliam Breathitt Gray outb(gpiommgpio->out_state[io_port], gpiommgpio->base + out_port); 1446ea5dcdfSWilliam Breathitt Gray 1456ea5dcdfSWilliam Breathitt Gray spin_unlock_irqrestore(&gpiommgpio->lock, flags); 1466ea5dcdfSWilliam Breathitt Gray 1476ea5dcdfSWilliam Breathitt Gray return 0; 1486ea5dcdfSWilliam Breathitt Gray } 1496ea5dcdfSWilliam Breathitt Gray 1506ea5dcdfSWilliam Breathitt Gray static int gpiomm_gpio_get(struct gpio_chip *chip, unsigned int offset) 1516ea5dcdfSWilliam Breathitt Gray { 1526ea5dcdfSWilliam Breathitt Gray struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); 1536ea5dcdfSWilliam Breathitt Gray const unsigned int port = offset / 8; 1546ea5dcdfSWilliam Breathitt Gray const unsigned int mask = BIT(offset % 8); 1556ea5dcdfSWilliam Breathitt Gray const unsigned int in_port = (port > 2) ? port + 1 : port; 1566ea5dcdfSWilliam Breathitt Gray unsigned long flags; 1576ea5dcdfSWilliam Breathitt Gray unsigned int port_state; 1586ea5dcdfSWilliam Breathitt Gray 1596ea5dcdfSWilliam Breathitt Gray spin_lock_irqsave(&gpiommgpio->lock, flags); 1606ea5dcdfSWilliam Breathitt Gray 1616ea5dcdfSWilliam Breathitt Gray /* ensure that GPIO is set for input */ 1626ea5dcdfSWilliam Breathitt Gray if (!(gpiommgpio->io_state[port] & mask)) { 1636ea5dcdfSWilliam Breathitt Gray spin_unlock_irqrestore(&gpiommgpio->lock, flags); 1646ea5dcdfSWilliam Breathitt Gray return -EINVAL; 1656ea5dcdfSWilliam Breathitt Gray } 1666ea5dcdfSWilliam Breathitt Gray 1676ea5dcdfSWilliam Breathitt Gray port_state = inb(gpiommgpio->base + in_port); 1686ea5dcdfSWilliam Breathitt Gray 1696ea5dcdfSWilliam Breathitt Gray spin_unlock_irqrestore(&gpiommgpio->lock, flags); 1706ea5dcdfSWilliam Breathitt Gray 1716ea5dcdfSWilliam Breathitt Gray return !!(port_state & mask); 1726ea5dcdfSWilliam Breathitt Gray } 1736ea5dcdfSWilliam Breathitt Gray 1746ea5dcdfSWilliam Breathitt Gray static void gpiomm_gpio_set(struct gpio_chip *chip, unsigned int offset, 1756ea5dcdfSWilliam Breathitt Gray int value) 1766ea5dcdfSWilliam Breathitt Gray { 1776ea5dcdfSWilliam Breathitt Gray struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); 1786ea5dcdfSWilliam Breathitt Gray const unsigned int port = offset / 8; 1796ea5dcdfSWilliam Breathitt Gray const unsigned int mask = BIT(offset % 8); 1806ea5dcdfSWilliam Breathitt Gray const unsigned int out_port = (port > 2) ? port + 1 : port; 1816ea5dcdfSWilliam Breathitt Gray unsigned long flags; 1826ea5dcdfSWilliam Breathitt Gray 1836ea5dcdfSWilliam Breathitt Gray spin_lock_irqsave(&gpiommgpio->lock, flags); 1846ea5dcdfSWilliam Breathitt Gray 1856ea5dcdfSWilliam Breathitt Gray if (value) 1866ea5dcdfSWilliam Breathitt Gray gpiommgpio->out_state[port] |= mask; 1876ea5dcdfSWilliam Breathitt Gray else 1886ea5dcdfSWilliam Breathitt Gray gpiommgpio->out_state[port] &= ~mask; 1896ea5dcdfSWilliam Breathitt Gray 1906ea5dcdfSWilliam Breathitt Gray outb(gpiommgpio->out_state[port], gpiommgpio->base + out_port); 1916ea5dcdfSWilliam Breathitt Gray 1926ea5dcdfSWilliam Breathitt Gray spin_unlock_irqrestore(&gpiommgpio->lock, flags); 1936ea5dcdfSWilliam Breathitt Gray } 1946ea5dcdfSWilliam Breathitt Gray 1956ea5dcdfSWilliam Breathitt Gray static int gpiomm_probe(struct device *dev, unsigned int id) 1966ea5dcdfSWilliam Breathitt Gray { 1976ea5dcdfSWilliam Breathitt Gray struct gpiomm_gpio *gpiommgpio; 1986ea5dcdfSWilliam Breathitt Gray const char *const name = dev_name(dev); 1996ea5dcdfSWilliam Breathitt Gray int err; 2006ea5dcdfSWilliam Breathitt Gray 2016ea5dcdfSWilliam Breathitt Gray gpiommgpio = devm_kzalloc(dev, sizeof(*gpiommgpio), GFP_KERNEL); 2026ea5dcdfSWilliam Breathitt Gray if (!gpiommgpio) 2036ea5dcdfSWilliam Breathitt Gray return -ENOMEM; 2046ea5dcdfSWilliam Breathitt Gray 2056ea5dcdfSWilliam Breathitt Gray if (!devm_request_region(dev, base[id], GPIOMM_EXTENT, name)) { 2066ea5dcdfSWilliam Breathitt Gray dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n", 2076ea5dcdfSWilliam Breathitt Gray base[id], base[id] + GPIOMM_EXTENT); 2086ea5dcdfSWilliam Breathitt Gray return -EBUSY; 2096ea5dcdfSWilliam Breathitt Gray } 2106ea5dcdfSWilliam Breathitt Gray 2116ea5dcdfSWilliam Breathitt Gray gpiommgpio->chip.label = name; 2126ea5dcdfSWilliam Breathitt Gray gpiommgpio->chip.parent = dev; 2136ea5dcdfSWilliam Breathitt Gray gpiommgpio->chip.owner = THIS_MODULE; 2146ea5dcdfSWilliam Breathitt Gray gpiommgpio->chip.base = -1; 2156ea5dcdfSWilliam Breathitt Gray gpiommgpio->chip.ngpio = 48; 2166ea5dcdfSWilliam Breathitt Gray gpiommgpio->chip.get_direction = gpiomm_gpio_get_direction; 2176ea5dcdfSWilliam Breathitt Gray gpiommgpio->chip.direction_input = gpiomm_gpio_direction_input; 2186ea5dcdfSWilliam Breathitt Gray gpiommgpio->chip.direction_output = gpiomm_gpio_direction_output; 2196ea5dcdfSWilliam Breathitt Gray gpiommgpio->chip.get = gpiomm_gpio_get; 2206ea5dcdfSWilliam Breathitt Gray gpiommgpio->chip.set = gpiomm_gpio_set; 2216ea5dcdfSWilliam Breathitt Gray gpiommgpio->base = base[id]; 2226ea5dcdfSWilliam Breathitt Gray 2236ea5dcdfSWilliam Breathitt Gray spin_lock_init(&gpiommgpio->lock); 2246ea5dcdfSWilliam Breathitt Gray 2256ea5dcdfSWilliam Breathitt Gray dev_set_drvdata(dev, gpiommgpio); 2266ea5dcdfSWilliam Breathitt Gray 2276ea5dcdfSWilliam Breathitt Gray err = gpiochip_add_data(&gpiommgpio->chip, gpiommgpio); 2286ea5dcdfSWilliam Breathitt Gray if (err) { 2296ea5dcdfSWilliam Breathitt Gray dev_err(dev, "GPIO registering failed (%d)\n", err); 2306ea5dcdfSWilliam Breathitt Gray return err; 2316ea5dcdfSWilliam Breathitt Gray } 2326ea5dcdfSWilliam Breathitt Gray 2336ea5dcdfSWilliam Breathitt Gray /* initialize all GPIO as output */ 2346ea5dcdfSWilliam Breathitt Gray outb(0x80, base[id] + 3); 2356ea5dcdfSWilliam Breathitt Gray outb(0x00, base[id]); 2366ea5dcdfSWilliam Breathitt Gray outb(0x00, base[id] + 1); 2376ea5dcdfSWilliam Breathitt Gray outb(0x00, base[id] + 2); 2386ea5dcdfSWilliam Breathitt Gray outb(0x80, base[id] + 7); 2396ea5dcdfSWilliam Breathitt Gray outb(0x00, base[id] + 4); 2406ea5dcdfSWilliam Breathitt Gray outb(0x00, base[id] + 5); 2416ea5dcdfSWilliam Breathitt Gray outb(0x00, base[id] + 6); 2426ea5dcdfSWilliam Breathitt Gray 2436ea5dcdfSWilliam Breathitt Gray return 0; 2446ea5dcdfSWilliam Breathitt Gray } 2456ea5dcdfSWilliam Breathitt Gray 2466ea5dcdfSWilliam Breathitt Gray static int gpiomm_remove(struct device *dev, unsigned int id) 2476ea5dcdfSWilliam Breathitt Gray { 2486ea5dcdfSWilliam Breathitt Gray struct gpiomm_gpio *const gpiommgpio = dev_get_drvdata(dev); 2496ea5dcdfSWilliam Breathitt Gray 2506ea5dcdfSWilliam Breathitt Gray gpiochip_remove(&gpiommgpio->chip); 2516ea5dcdfSWilliam Breathitt Gray 2526ea5dcdfSWilliam Breathitt Gray return 0; 2536ea5dcdfSWilliam Breathitt Gray } 2546ea5dcdfSWilliam Breathitt Gray 2556ea5dcdfSWilliam Breathitt Gray static struct isa_driver gpiomm_driver = { 2566ea5dcdfSWilliam Breathitt Gray .probe = gpiomm_probe, 2576ea5dcdfSWilliam Breathitt Gray .driver = { 2586ea5dcdfSWilliam Breathitt Gray .name = "gpio-mm" 2596ea5dcdfSWilliam Breathitt Gray }, 2606ea5dcdfSWilliam Breathitt Gray .remove = gpiomm_remove 2616ea5dcdfSWilliam Breathitt Gray }; 2626ea5dcdfSWilliam Breathitt Gray 2636ea5dcdfSWilliam Breathitt Gray module_isa_driver(gpiomm_driver, num_gpiomm); 2646ea5dcdfSWilliam Breathitt Gray 2656ea5dcdfSWilliam Breathitt Gray MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); 2666ea5dcdfSWilliam Breathitt Gray MODULE_DESCRIPTION("Diamond Systems GPIO-MM GPIO driver"); 2676ea5dcdfSWilliam Breathitt Gray MODULE_LICENSE("GPL v2"); 268