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_hw_array(base, uint, ioport, &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 void gpiomm_gpio_set_multiple(struct gpio_chip *chip,
 196        unsigned long *mask, unsigned long *bits)
 197{
 198        struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
 199        unsigned int i;
 200        const unsigned int gpio_reg_size = 8;
 201        unsigned int port;
 202        unsigned int out_port;
 203        unsigned int bitmask;
 204        unsigned long flags;
 205
 206        /* set bits are evaluated a gpio register size at a time */
 207        for (i = 0; i < chip->ngpio; i += gpio_reg_size) {
 208                /* no more set bits in this mask word; skip to the next word */
 209                if (!mask[BIT_WORD(i)]) {
 210                        i = (BIT_WORD(i) + 1) * BITS_PER_LONG - gpio_reg_size;
 211                        continue;
 212                }
 213
 214                port = i / gpio_reg_size;
 215                out_port = (port > 2) ? port + 1 : port;
 216                bitmask = mask[BIT_WORD(i)] & bits[BIT_WORD(i)];
 217
 218                spin_lock_irqsave(&gpiommgpio->lock, flags);
 219
 220                /* update output state data and set device gpio register */
 221                gpiommgpio->out_state[port] &= ~mask[BIT_WORD(i)];
 222                gpiommgpio->out_state[port] |= bitmask;
 223                outb(gpiommgpio->out_state[port], gpiommgpio->base + out_port);
 224
 225                spin_unlock_irqrestore(&gpiommgpio->lock, flags);
 226
 227                /* prepare for next gpio register set */
 228                mask[BIT_WORD(i)] >>= gpio_reg_size;
 229                bits[BIT_WORD(i)] >>= gpio_reg_size;
 230        }
 231}
 232
 233#define GPIOMM_NGPIO 48
 234static const char *gpiomm_names[GPIOMM_NGPIO] = {
 235        "Port 1A0", "Port 1A1", "Port 1A2", "Port 1A3", "Port 1A4", "Port 1A5",
 236        "Port 1A6", "Port 1A7", "Port 1B0", "Port 1B1", "Port 1B2", "Port 1B3",
 237        "Port 1B4", "Port 1B5", "Port 1B6", "Port 1B7", "Port 1C0", "Port 1C1",
 238        "Port 1C2", "Port 1C3", "Port 1C4", "Port 1C5", "Port 1C6", "Port 1C7",
 239        "Port 2A0", "Port 2A1", "Port 2A2", "Port 2A3", "Port 2A4", "Port 2A5",
 240        "Port 2A6", "Port 2A7", "Port 2B0", "Port 2B1", "Port 2B2", "Port 2B3",
 241        "Port 2B4", "Port 2B5", "Port 2B6", "Port 2B7", "Port 2C0", "Port 2C1",
 242        "Port 2C2", "Port 2C3", "Port 2C4", "Port 2C5", "Port 2C6", "Port 2C7",
 243};
 244
 245static int gpiomm_probe(struct device *dev, unsigned int id)
 246{
 247        struct gpiomm_gpio *gpiommgpio;
 248        const char *const name = dev_name(dev);
 249        int err;
 250
 251        gpiommgpio = devm_kzalloc(dev, sizeof(*gpiommgpio), GFP_KERNEL);
 252        if (!gpiommgpio)
 253                return -ENOMEM;
 254
 255        if (!devm_request_region(dev, base[id], GPIOMM_EXTENT, name)) {
 256                dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
 257                        base[id], base[id] + GPIOMM_EXTENT);
 258                return -EBUSY;
 259        }
 260
 261        gpiommgpio->chip.label = name;
 262        gpiommgpio->chip.parent = dev;
 263        gpiommgpio->chip.owner = THIS_MODULE;
 264        gpiommgpio->chip.base = -1;
 265        gpiommgpio->chip.ngpio = GPIOMM_NGPIO;
 266        gpiommgpio->chip.names = gpiomm_names;
 267        gpiommgpio->chip.get_direction = gpiomm_gpio_get_direction;
 268        gpiommgpio->chip.direction_input = gpiomm_gpio_direction_input;
 269        gpiommgpio->chip.direction_output = gpiomm_gpio_direction_output;
 270        gpiommgpio->chip.get = gpiomm_gpio_get;
 271        gpiommgpio->chip.set = gpiomm_gpio_set;
 272        gpiommgpio->chip.set_multiple = gpiomm_gpio_set_multiple;
 273        gpiommgpio->base = base[id];
 274
 275        spin_lock_init(&gpiommgpio->lock);
 276
 277        err = devm_gpiochip_add_data(dev, &gpiommgpio->chip, gpiommgpio);
 278        if (err) {
 279                dev_err(dev, "GPIO registering failed (%d)\n", err);
 280                return err;
 281        }
 282
 283        /* initialize all GPIO as output */
 284        outb(0x80, base[id] + 3);
 285        outb(0x00, base[id]);
 286        outb(0x00, base[id] + 1);
 287        outb(0x00, base[id] + 2);
 288        outb(0x80, base[id] + 7);
 289        outb(0x00, base[id] + 4);
 290        outb(0x00, base[id] + 5);
 291        outb(0x00, base[id] + 6);
 292
 293        return 0;
 294}
 295
 296static struct isa_driver gpiomm_driver = {
 297        .probe = gpiomm_probe,
 298        .driver = {
 299                .name = "gpio-mm"
 300        },
 301};
 302
 303module_isa_driver(gpiomm_driver, num_gpiomm);
 304
 305MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
 306MODULE_DESCRIPTION("Diamond Systems GPIO-MM GPIO driver");
 307MODULE_LICENSE("GPL v2");
 308