linux/drivers/gpio/gpio-gpio-mm.c
<<
>>
Prefs
   1/*
   2 * GPIO driver for the Diamond Systems GPIO-MM
   3 * Copyright (C) 2016 William Breathitt Gray
   4 *
   5 * This program is free software; you can redistribute it and/or modify
   6 * it under the terms of the GNU General Public License, version 2, as
   7 * published by the Free Software Foundation.
   8 *
   9 * This program is distributed in the hope that it will be useful, but
  10 * WITHOUT ANY WARRANTY; without even the implied warranty of
  11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12 * General Public License for more details.
  13 *
  14 * This driver supports the following Diamond Systems devices: GPIO-MM and
  15 * GPIO-MM-12.
  16 */
  17#include <linux/bitops.h>
  18#include <linux/device.h>
  19#include <linux/errno.h>
  20#include <linux/gpio/driver.h>
  21#include <linux/io.h>
  22#include <linux/ioport.h>
  23#include <linux/isa.h>
  24#include <linux/kernel.h>
  25#include <linux/module.h>
  26#include <linux/moduleparam.h>
  27#include <linux/spinlock.h>
  28
  29#define GPIOMM_EXTENT 8
  30#define MAX_NUM_GPIOMM max_num_isa_dev(GPIOMM_EXTENT)
  31
  32static unsigned int base[MAX_NUM_GPIOMM];
  33static unsigned int num_gpiomm;
  34module_param_array(base, uint, &num_gpiomm, 0);
  35MODULE_PARM_DESC(base, "Diamond Systems GPIO-MM base addresses");
  36
  37/**
  38 * struct gpiomm_gpio - GPIO device private data structure
  39 * @chip:       instance of the gpio_chip
  40 * @io_state:   bit I/O state (whether bit is set to input or output)
  41 * @out_state:  output bits state
  42 * @control:    Control registers state
  43 * @lock:       synchronization lock to prevent I/O race conditions
  44 * @base:       base port address of the GPIO device
  45 */
  46struct gpiomm_gpio {
  47        struct gpio_chip chip;
  48        unsigned char io_state[6];
  49        unsigned char out_state[6];
  50        unsigned char control[2];
  51        spinlock_t lock;
  52        unsigned int base;
  53};
  54
  55static int gpiomm_gpio_get_direction(struct gpio_chip *chip,
  56        unsigned int offset)
  57{
  58        struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
  59        const unsigned int port = offset / 8;
  60        const unsigned int mask = BIT(offset % 8);
  61
  62        return !!(gpiommgpio->io_state[port] & mask);
  63}
  64
  65static int gpiomm_gpio_direction_input(struct gpio_chip *chip,
  66        unsigned int offset)
  67{
  68        struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
  69        const unsigned int io_port = offset / 8;
  70        const unsigned int control_port = io_port / 3;
  71        const unsigned int control_addr = gpiommgpio->base + 3 + control_port*4;
  72        unsigned long flags;
  73        unsigned int control;
  74
  75        spin_lock_irqsave(&gpiommgpio->lock, flags);
  76
  77        /* Check if configuring Port C */
  78        if (io_port == 2 || io_port == 5) {
  79                /* Port C can be configured by nibble */
  80                if (offset % 8 > 3) {
  81                        gpiommgpio->io_state[io_port] |= 0xF0;
  82                        gpiommgpio->control[control_port] |= BIT(3);
  83                } else {
  84                        gpiommgpio->io_state[io_port] |= 0x0F;
  85                        gpiommgpio->control[control_port] |= BIT(0);
  86                }
  87        } else {
  88                gpiommgpio->io_state[io_port] |= 0xFF;
  89                if (io_port == 0 || io_port == 3)
  90                        gpiommgpio->control[control_port] |= BIT(4);
  91                else
  92                        gpiommgpio->control[control_port] |= BIT(1);
  93        }
  94
  95        control = BIT(7) | gpiommgpio->control[control_port];
  96        outb(control, control_addr);
  97
  98        spin_unlock_irqrestore(&gpiommgpio->lock, flags);
  99
 100        return 0;
 101}
 102
 103static int gpiomm_gpio_direction_output(struct gpio_chip *chip,
 104        unsigned int offset, int value)
 105{
 106        struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
 107        const unsigned int io_port = offset / 8;
 108        const unsigned int control_port = io_port / 3;
 109        const unsigned int mask = BIT(offset % 8);
 110        const unsigned int control_addr = gpiommgpio->base + 3 + control_port*4;
 111        const unsigned int out_port = (io_port > 2) ? io_port + 1 : io_port;
 112        unsigned long flags;
 113        unsigned int control;
 114
 115        spin_lock_irqsave(&gpiommgpio->lock, flags);
 116
 117        /* Check if configuring Port C */
 118        if (io_port == 2 || io_port == 5) {
 119                /* Port C can be configured by nibble */
 120                if (offset % 8 > 3) {
 121                        gpiommgpio->io_state[io_port] &= 0x0F;
 122                        gpiommgpio->control[control_port] &= ~BIT(3);
 123                } else {
 124                        gpiommgpio->io_state[io_port] &= 0xF0;
 125                        gpiommgpio->control[control_port] &= ~BIT(0);
 126                }
 127        } else {
 128                gpiommgpio->io_state[io_port] &= 0x00;
 129                if (io_port == 0 || io_port == 3)
 130                        gpiommgpio->control[control_port] &= ~BIT(4);
 131                else
 132                        gpiommgpio->control[control_port] &= ~BIT(1);
 133        }
 134
 135        if (value)
 136                gpiommgpio->out_state[io_port] |= mask;
 137        else
 138                gpiommgpio->out_state[io_port] &= ~mask;
 139
 140        control = BIT(7) | gpiommgpio->control[control_port];
 141        outb(control, control_addr);
 142
 143        outb(gpiommgpio->out_state[io_port], gpiommgpio->base + out_port);
 144
 145        spin_unlock_irqrestore(&gpiommgpio->lock, flags);
 146
 147        return 0;
 148}
 149
 150static int gpiomm_gpio_get(struct gpio_chip *chip, unsigned int offset)
 151{
 152        struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
 153        const unsigned int port = offset / 8;
 154        const unsigned int mask = BIT(offset % 8);
 155        const unsigned int in_port = (port > 2) ? port + 1 : port;
 156        unsigned long flags;
 157        unsigned int port_state;
 158
 159        spin_lock_irqsave(&gpiommgpio->lock, flags);
 160
 161        /* ensure that GPIO is set for input */
 162        if (!(gpiommgpio->io_state[port] & mask)) {
 163                spin_unlock_irqrestore(&gpiommgpio->lock, flags);
 164                return -EINVAL;
 165        }
 166
 167        port_state = inb(gpiommgpio->base + in_port);
 168
 169        spin_unlock_irqrestore(&gpiommgpio->lock, flags);
 170
 171        return !!(port_state & mask);
 172}
 173
 174static void gpiomm_gpio_set(struct gpio_chip *chip, unsigned int offset,
 175        int value)
 176{
 177        struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
 178        const unsigned int port = offset / 8;
 179        const unsigned int mask = BIT(offset % 8);
 180        const unsigned int out_port = (port > 2) ? port + 1 : port;
 181        unsigned long flags;
 182
 183        spin_lock_irqsave(&gpiommgpio->lock, flags);
 184
 185        if (value)
 186                gpiommgpio->out_state[port] |= mask;
 187        else
 188                gpiommgpio->out_state[port] &= ~mask;
 189
 190        outb(gpiommgpio->out_state[port], gpiommgpio->base + out_port);
 191
 192        spin_unlock_irqrestore(&gpiommgpio->lock, flags);
 193}
 194
 195static int gpiomm_probe(struct device *dev, unsigned int id)
 196{
 197        struct gpiomm_gpio *gpiommgpio;
 198        const char *const name = dev_name(dev);
 199        int err;
 200
 201        gpiommgpio = devm_kzalloc(dev, sizeof(*gpiommgpio), GFP_KERNEL);
 202        if (!gpiommgpio)
 203                return -ENOMEM;
 204
 205        if (!devm_request_region(dev, base[id], GPIOMM_EXTENT, name)) {
 206                dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
 207                        base[id], base[id] + GPIOMM_EXTENT);
 208                return -EBUSY;
 209        }
 210
 211        gpiommgpio->chip.label = name;
 212        gpiommgpio->chip.parent = dev;
 213        gpiommgpio->chip.owner = THIS_MODULE;
 214        gpiommgpio->chip.base = -1;
 215        gpiommgpio->chip.ngpio = 48;
 216        gpiommgpio->chip.get_direction = gpiomm_gpio_get_direction;
 217        gpiommgpio->chip.direction_input = gpiomm_gpio_direction_input;
 218        gpiommgpio->chip.direction_output = gpiomm_gpio_direction_output;
 219        gpiommgpio->chip.get = gpiomm_gpio_get;
 220        gpiommgpio->chip.set = gpiomm_gpio_set;
 221        gpiommgpio->base = base[id];
 222
 223        spin_lock_init(&gpiommgpio->lock);
 224
 225        dev_set_drvdata(dev, gpiommgpio);
 226
 227        err = gpiochip_add_data(&gpiommgpio->chip, gpiommgpio);
 228        if (err) {
 229                dev_err(dev, "GPIO registering failed (%d)\n", err);
 230                return err;
 231        }
 232
 233        /* initialize all GPIO as output */
 234        outb(0x80, base[id] + 3);
 235        outb(0x00, base[id]);
 236        outb(0x00, base[id] + 1);
 237        outb(0x00, base[id] + 2);
 238        outb(0x80, base[id] + 7);
 239        outb(0x00, base[id] + 4);
 240        outb(0x00, base[id] + 5);
 241        outb(0x00, base[id] + 6);
 242
 243        return 0;
 244}
 245
 246static int gpiomm_remove(struct device *dev, unsigned int id)
 247{
 248        struct gpiomm_gpio *const gpiommgpio = dev_get_drvdata(dev);
 249
 250        gpiochip_remove(&gpiommgpio->chip);
 251
 252        return 0;
 253}
 254
 255static struct isa_driver gpiomm_driver = {
 256        .probe = gpiomm_probe,
 257        .driver = {
 258                .name = "gpio-mm"
 259        },
 260        .remove = gpiomm_remove
 261};
 262
 263module_isa_driver(gpiomm_driver, num_gpiomm);
 264
 265MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
 266MODULE_DESCRIPTION("Diamond Systems GPIO-MM GPIO driver");
 267MODULE_LICENSE("GPL v2");
 268