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/bitmap.h>
  18#include <linux/bitops.h>
  19#include <linux/device.h>
  20#include <linux/errno.h>
  21#include <linux/gpio/driver.h>
  22#include <linux/io.h>
  23#include <linux/ioport.h>
  24#include <linux/isa.h>
  25#include <linux/kernel.h>
  26#include <linux/module.h>
  27#include <linux/moduleparam.h>
  28#include <linux/spinlock.h>
  29
  30#define GPIOMM_EXTENT 8
  31#define MAX_NUM_GPIOMM max_num_isa_dev(GPIOMM_EXTENT)
  32
  33static unsigned int base[MAX_NUM_GPIOMM];
  34static unsigned int num_gpiomm;
  35module_param_hw_array(base, uint, ioport, &num_gpiomm, 0);
  36MODULE_PARM_DESC(base, "Diamond Systems GPIO-MM base addresses");
  37
  38/**
  39 * struct gpiomm_gpio - GPIO device private data structure
  40 * @chip:       instance of the gpio_chip
  41 * @io_state:   bit I/O state (whether bit is set to input or output)
  42 * @out_state:  output bits state
  43 * @control:    Control registers state
  44 * @lock:       synchronization lock to prevent I/O race conditions
  45 * @base:       base port address of the GPIO device
  46 */
  47struct gpiomm_gpio {
  48        struct gpio_chip chip;
  49        unsigned char io_state[6];
  50        unsigned char out_state[6];
  51        unsigned char control[2];
  52        spinlock_t lock;
  53        unsigned int base;
  54};
  55
  56static int gpiomm_gpio_get_direction(struct gpio_chip *chip,
  57        unsigned int offset)
  58{
  59        struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
  60        const unsigned int port = offset / 8;
  61        const unsigned int mask = BIT(offset % 8);
  62
  63        return !!(gpiommgpio->io_state[port] & mask);
  64}
  65
  66static int gpiomm_gpio_direction_input(struct gpio_chip *chip,
  67        unsigned int offset)
  68{
  69        struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
  70        const unsigned int io_port = offset / 8;
  71        const unsigned int control_port = io_port / 3;
  72        const unsigned int control_addr = gpiommgpio->base + 3 + control_port*4;
  73        unsigned long flags;
  74        unsigned int control;
  75
  76        spin_lock_irqsave(&gpiommgpio->lock, flags);
  77
  78        /* Check if configuring Port C */
  79        if (io_port == 2 || io_port == 5) {
  80                /* Port C can be configured by nibble */
  81                if (offset % 8 > 3) {
  82                        gpiommgpio->io_state[io_port] |= 0xF0;
  83                        gpiommgpio->control[control_port] |= BIT(3);
  84                } else {
  85                        gpiommgpio->io_state[io_port] |= 0x0F;
  86                        gpiommgpio->control[control_port] |= BIT(0);
  87                }
  88        } else {
  89                gpiommgpio->io_state[io_port] |= 0xFF;
  90                if (io_port == 0 || io_port == 3)
  91                        gpiommgpio->control[control_port] |= BIT(4);
  92                else
  93                        gpiommgpio->control[control_port] |= BIT(1);
  94        }
  95
  96        control = BIT(7) | gpiommgpio->control[control_port];
  97        outb(control, control_addr);
  98
  99        spin_unlock_irqrestore(&gpiommgpio->lock, flags);
 100
 101        return 0;
 102}
 103
 104static int gpiomm_gpio_direction_output(struct gpio_chip *chip,
 105        unsigned int offset, int value)
 106{
 107        struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
 108        const unsigned int io_port = offset / 8;
 109        const unsigned int control_port = io_port / 3;
 110        const unsigned int mask = BIT(offset % 8);
 111        const unsigned int control_addr = gpiommgpio->base + 3 + control_port*4;
 112        const unsigned int out_port = (io_port > 2) ? io_port + 1 : io_port;
 113        unsigned long flags;
 114        unsigned int control;
 115
 116        spin_lock_irqsave(&gpiommgpio->lock, flags);
 117
 118        /* Check if configuring Port C */
 119        if (io_port == 2 || io_port == 5) {
 120                /* Port C can be configured by nibble */
 121                if (offset % 8 > 3) {
 122                        gpiommgpio->io_state[io_port] &= 0x0F;
 123                        gpiommgpio->control[control_port] &= ~BIT(3);
 124                } else {
 125                        gpiommgpio->io_state[io_port] &= 0xF0;
 126                        gpiommgpio->control[control_port] &= ~BIT(0);
 127                }
 128        } else {
 129                gpiommgpio->io_state[io_port] &= 0x00;
 130                if (io_port == 0 || io_port == 3)
 131                        gpiommgpio->control[control_port] &= ~BIT(4);
 132                else
 133                        gpiommgpio->control[control_port] &= ~BIT(1);
 134        }
 135
 136        if (value)
 137                gpiommgpio->out_state[io_port] |= mask;
 138        else
 139                gpiommgpio->out_state[io_port] &= ~mask;
 140
 141        control = BIT(7) | gpiommgpio->control[control_port];
 142        outb(control, control_addr);
 143
 144        outb(gpiommgpio->out_state[io_port], gpiommgpio->base + out_port);
 145
 146        spin_unlock_irqrestore(&gpiommgpio->lock, flags);
 147
 148        return 0;
 149}
 150
 151static int gpiomm_gpio_get(struct gpio_chip *chip, unsigned int offset)
 152{
 153        struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
 154        const unsigned int port = offset / 8;
 155        const unsigned int mask = BIT(offset % 8);
 156        const unsigned int in_port = (port > 2) ? port + 1 : port;
 157        unsigned long flags;
 158        unsigned int port_state;
 159
 160        spin_lock_irqsave(&gpiommgpio->lock, flags);
 161
 162        /* ensure that GPIO is set for input */
 163        if (!(gpiommgpio->io_state[port] & mask)) {
 164                spin_unlock_irqrestore(&gpiommgpio->lock, flags);
 165                return -EINVAL;
 166        }
 167
 168        port_state = inb(gpiommgpio->base + in_port);
 169
 170        spin_unlock_irqrestore(&gpiommgpio->lock, flags);
 171
 172        return !!(port_state & mask);
 173}
 174
 175static int gpiomm_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
 176        unsigned long *bits)
 177{
 178        struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
 179        size_t i;
 180        static const size_t ports[] = { 0, 1, 2, 4, 5, 6 };
 181        const unsigned int gpio_reg_size = 8;
 182        unsigned int bits_offset;
 183        size_t word_index;
 184        unsigned int word_offset;
 185        unsigned long word_mask;
 186        const unsigned long port_mask = GENMASK(gpio_reg_size - 1, 0);
 187        unsigned long port_state;
 188
 189        /* clear bits array to a clean slate */
 190        bitmap_zero(bits, chip->ngpio);
 191
 192        /* get bits are evaluated a gpio port register at a time */
 193        for (i = 0; i < ARRAY_SIZE(ports); i++) {
 194                /* gpio offset in bits array */
 195                bits_offset = i * gpio_reg_size;
 196
 197                /* word index for bits array */
 198                word_index = BIT_WORD(bits_offset);
 199
 200                /* gpio offset within current word of bits array */
 201                word_offset = bits_offset % BITS_PER_LONG;
 202
 203                /* mask of get bits for current gpio within current word */
 204                word_mask = mask[word_index] & (port_mask << word_offset);
 205                if (!word_mask) {
 206                        /* no get bits in this port so skip to next one */
 207                        continue;
 208                }
 209
 210                /* read bits from current gpio port */
 211                port_state = inb(gpiommgpio->base + ports[i]);
 212
 213                /* store acquired bits at respective bits array offset */
 214                bits[word_index] |= port_state << word_offset;
 215        }
 216
 217        return 0;
 218}
 219
 220static void gpiomm_gpio_set(struct gpio_chip *chip, unsigned int offset,
 221        int value)
 222{
 223        struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
 224        const unsigned int port = offset / 8;
 225        const unsigned int mask = BIT(offset % 8);
 226        const unsigned int out_port = (port > 2) ? port + 1 : port;
 227        unsigned long flags;
 228
 229        spin_lock_irqsave(&gpiommgpio->lock, flags);
 230
 231        if (value)
 232                gpiommgpio->out_state[port] |= mask;
 233        else
 234                gpiommgpio->out_state[port] &= ~mask;
 235
 236        outb(gpiommgpio->out_state[port], gpiommgpio->base + out_port);
 237
 238        spin_unlock_irqrestore(&gpiommgpio->lock, flags);
 239}
 240
 241static void gpiomm_gpio_set_multiple(struct gpio_chip *chip,
 242        unsigned long *mask, unsigned long *bits)
 243{
 244        struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
 245        unsigned int i;
 246        const unsigned int gpio_reg_size = 8;
 247        unsigned int port;
 248        unsigned int out_port;
 249        unsigned int bitmask;
 250        unsigned long flags;
 251
 252        /* set bits are evaluated a gpio register size at a time */
 253        for (i = 0; i < chip->ngpio; i += gpio_reg_size) {
 254                /* no more set bits in this mask word; skip to the next word */
 255                if (!mask[BIT_WORD(i)]) {
 256                        i = (BIT_WORD(i) + 1) * BITS_PER_LONG - gpio_reg_size;
 257                        continue;
 258                }
 259
 260                port = i / gpio_reg_size;
 261                out_port = (port > 2) ? port + 1 : port;
 262                bitmask = mask[BIT_WORD(i)] & bits[BIT_WORD(i)];
 263
 264                spin_lock_irqsave(&gpiommgpio->lock, flags);
 265
 266                /* update output state data and set device gpio register */
 267                gpiommgpio->out_state[port] &= ~mask[BIT_WORD(i)];
 268                gpiommgpio->out_state[port] |= bitmask;
 269                outb(gpiommgpio->out_state[port], gpiommgpio->base + out_port);
 270
 271                spin_unlock_irqrestore(&gpiommgpio->lock, flags);
 272
 273                /* prepare for next gpio register set */
 274                mask[BIT_WORD(i)] >>= gpio_reg_size;
 275                bits[BIT_WORD(i)] >>= gpio_reg_size;
 276        }
 277}
 278
 279#define GPIOMM_NGPIO 48
 280static const char *gpiomm_names[GPIOMM_NGPIO] = {
 281        "Port 1A0", "Port 1A1", "Port 1A2", "Port 1A3", "Port 1A4", "Port 1A5",
 282        "Port 1A6", "Port 1A7", "Port 1B0", "Port 1B1", "Port 1B2", "Port 1B3",
 283        "Port 1B4", "Port 1B5", "Port 1B6", "Port 1B7", "Port 1C0", "Port 1C1",
 284        "Port 1C2", "Port 1C3", "Port 1C4", "Port 1C5", "Port 1C6", "Port 1C7",
 285        "Port 2A0", "Port 2A1", "Port 2A2", "Port 2A3", "Port 2A4", "Port 2A5",
 286        "Port 2A6", "Port 2A7", "Port 2B0", "Port 2B1", "Port 2B2", "Port 2B3",
 287        "Port 2B4", "Port 2B5", "Port 2B6", "Port 2B7", "Port 2C0", "Port 2C1",
 288        "Port 2C2", "Port 2C3", "Port 2C4", "Port 2C5", "Port 2C6", "Port 2C7",
 289};
 290
 291static int gpiomm_probe(struct device *dev, unsigned int id)
 292{
 293        struct gpiomm_gpio *gpiommgpio;
 294        const char *const name = dev_name(dev);
 295        int err;
 296
 297        gpiommgpio = devm_kzalloc(dev, sizeof(*gpiommgpio), GFP_KERNEL);
 298        if (!gpiommgpio)
 299                return -ENOMEM;
 300
 301        if (!devm_request_region(dev, base[id], GPIOMM_EXTENT, name)) {
 302                dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
 303                        base[id], base[id] + GPIOMM_EXTENT);
 304                return -EBUSY;
 305        }
 306
 307        gpiommgpio->chip.label = name;
 308        gpiommgpio->chip.parent = dev;
 309        gpiommgpio->chip.owner = THIS_MODULE;
 310        gpiommgpio->chip.base = -1;
 311        gpiommgpio->chip.ngpio = GPIOMM_NGPIO;
 312        gpiommgpio->chip.names = gpiomm_names;
 313        gpiommgpio->chip.get_direction = gpiomm_gpio_get_direction;
 314        gpiommgpio->chip.direction_input = gpiomm_gpio_direction_input;
 315        gpiommgpio->chip.direction_output = gpiomm_gpio_direction_output;
 316        gpiommgpio->chip.get = gpiomm_gpio_get;
 317        gpiommgpio->chip.get_multiple = gpiomm_gpio_get_multiple;
 318        gpiommgpio->chip.set = gpiomm_gpio_set;
 319        gpiommgpio->chip.set_multiple = gpiomm_gpio_set_multiple;
 320        gpiommgpio->base = base[id];
 321
 322        spin_lock_init(&gpiommgpio->lock);
 323
 324        err = devm_gpiochip_add_data(dev, &gpiommgpio->chip, gpiommgpio);
 325        if (err) {
 326                dev_err(dev, "GPIO registering failed (%d)\n", err);
 327                return err;
 328        }
 329
 330        /* initialize all GPIO as output */
 331        outb(0x80, base[id] + 3);
 332        outb(0x00, base[id]);
 333        outb(0x00, base[id] + 1);
 334        outb(0x00, base[id] + 2);
 335        outb(0x80, base[id] + 7);
 336        outb(0x00, base[id] + 4);
 337        outb(0x00, base[id] + 5);
 338        outb(0x00, base[id] + 6);
 339
 340        return 0;
 341}
 342
 343static struct isa_driver gpiomm_driver = {
 344        .probe = gpiomm_probe,
 345        .driver = {
 346                .name = "gpio-mm"
 347        },
 348};
 349
 350module_isa_driver(gpiomm_driver, num_gpiomm);
 351
 352MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
 353MODULE_DESCRIPTION("Diamond Systems GPIO-MM GPIO driver");
 354MODULE_LICENSE("GPL v2");
 355