linux/drivers/gpio/gpio-104-idio-16.c
<<
>>
Prefs
   1/*
   2 * GPIO driver for the ACCES 104-IDIO-16 family
   3 * Copyright (C) 2015 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 ACCES devices: 104-IDIO-16,
  15 * 104-IDIO-16E, 104-IDO-16, 104-IDIO-8, 104-IDIO-8E, and 104-IDO-8.
  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/interrupt.h>
  24#include <linux/irqdesc.h>
  25#include <linux/isa.h>
  26#include <linux/kernel.h>
  27#include <linux/module.h>
  28#include <linux/moduleparam.h>
  29#include <linux/spinlock.h>
  30
  31#define IDIO_16_EXTENT 8
  32#define MAX_NUM_IDIO_16 max_num_isa_dev(IDIO_16_EXTENT)
  33
  34static unsigned int base[MAX_NUM_IDIO_16];
  35static unsigned int num_idio_16;
  36module_param_hw_array(base, uint, ioport, &num_idio_16, 0);
  37MODULE_PARM_DESC(base, "ACCES 104-IDIO-16 base addresses");
  38
  39static unsigned int irq[MAX_NUM_IDIO_16];
  40module_param_hw_array(irq, uint, irq, NULL, 0);
  41MODULE_PARM_DESC(irq, "ACCES 104-IDIO-16 interrupt line numbers");
  42
  43/**
  44 * struct idio_16_gpio - GPIO device private data structure
  45 * @chip:       instance of the gpio_chip
  46 * @lock:       synchronization lock to prevent I/O race conditions
  47 * @irq_mask:   I/O bits affected by interrupts
  48 * @base:       base port address of the GPIO device
  49 * @out_state:  output bits state
  50 */
  51struct idio_16_gpio {
  52        struct gpio_chip chip;
  53        raw_spinlock_t lock;
  54        unsigned long irq_mask;
  55        unsigned base;
  56        unsigned out_state;
  57};
  58
  59static int idio_16_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
  60{
  61        if (offset > 15)
  62                return 1;
  63
  64        return 0;
  65}
  66
  67static int idio_16_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
  68{
  69        return 0;
  70}
  71
  72static int idio_16_gpio_direction_output(struct gpio_chip *chip,
  73        unsigned offset, int value)
  74{
  75        chip->set(chip, offset, value);
  76        return 0;
  77}
  78
  79static int idio_16_gpio_get(struct gpio_chip *chip, unsigned offset)
  80{
  81        struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
  82        const unsigned mask = BIT(offset-16);
  83
  84        if (offset < 16)
  85                return -EINVAL;
  86
  87        if (offset < 24)
  88                return !!(inb(idio16gpio->base + 1) & mask);
  89
  90        return !!(inb(idio16gpio->base + 5) & (mask>>8));
  91}
  92
  93static void idio_16_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
  94{
  95        struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
  96        const unsigned mask = BIT(offset);
  97        unsigned long flags;
  98
  99        if (offset > 15)
 100                return;
 101
 102        raw_spin_lock_irqsave(&idio16gpio->lock, flags);
 103
 104        if (value)
 105                idio16gpio->out_state |= mask;
 106        else
 107                idio16gpio->out_state &= ~mask;
 108
 109        if (offset > 7)
 110                outb(idio16gpio->out_state >> 8, idio16gpio->base + 4);
 111        else
 112                outb(idio16gpio->out_state, idio16gpio->base);
 113
 114        raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
 115}
 116
 117static void idio_16_gpio_set_multiple(struct gpio_chip *chip,
 118        unsigned long *mask, unsigned long *bits)
 119{
 120        struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
 121        unsigned long flags;
 122
 123        raw_spin_lock_irqsave(&idio16gpio->lock, flags);
 124
 125        idio16gpio->out_state &= ~*mask;
 126        idio16gpio->out_state |= *mask & *bits;
 127
 128        if (*mask & 0xFF)
 129                outb(idio16gpio->out_state, idio16gpio->base);
 130        if ((*mask >> 8) & 0xFF)
 131                outb(idio16gpio->out_state >> 8, idio16gpio->base + 4);
 132
 133        raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
 134}
 135
 136static void idio_16_irq_ack(struct irq_data *data)
 137{
 138}
 139
 140static void idio_16_irq_mask(struct irq_data *data)
 141{
 142        struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
 143        struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
 144        const unsigned long mask = BIT(irqd_to_hwirq(data));
 145        unsigned long flags;
 146
 147        idio16gpio->irq_mask &= ~mask;
 148
 149        if (!idio16gpio->irq_mask) {
 150                raw_spin_lock_irqsave(&idio16gpio->lock, flags);
 151
 152                outb(0, idio16gpio->base + 2);
 153
 154                raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
 155        }
 156}
 157
 158static void idio_16_irq_unmask(struct irq_data *data)
 159{
 160        struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
 161        struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
 162        const unsigned long mask = BIT(irqd_to_hwirq(data));
 163        const unsigned long prev_irq_mask = idio16gpio->irq_mask;
 164        unsigned long flags;
 165
 166        idio16gpio->irq_mask |= mask;
 167
 168        if (!prev_irq_mask) {
 169                raw_spin_lock_irqsave(&idio16gpio->lock, flags);
 170
 171                inb(idio16gpio->base + 2);
 172
 173                raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
 174        }
 175}
 176
 177static int idio_16_irq_set_type(struct irq_data *data, unsigned flow_type)
 178{
 179        /* The only valid irq types are none and both-edges */
 180        if (flow_type != IRQ_TYPE_NONE &&
 181                (flow_type & IRQ_TYPE_EDGE_BOTH) != IRQ_TYPE_EDGE_BOTH)
 182                return -EINVAL;
 183
 184        return 0;
 185}
 186
 187static struct irq_chip idio_16_irqchip = {
 188        .name = "104-idio-16",
 189        .irq_ack = idio_16_irq_ack,
 190        .irq_mask = idio_16_irq_mask,
 191        .irq_unmask = idio_16_irq_unmask,
 192        .irq_set_type = idio_16_irq_set_type
 193};
 194
 195static irqreturn_t idio_16_irq_handler(int irq, void *dev_id)
 196{
 197        struct idio_16_gpio *const idio16gpio = dev_id;
 198        struct gpio_chip *const chip = &idio16gpio->chip;
 199        int gpio;
 200
 201        for_each_set_bit(gpio, &idio16gpio->irq_mask, chip->ngpio)
 202                generic_handle_irq(irq_find_mapping(chip->irqdomain, gpio));
 203
 204        raw_spin_lock(&idio16gpio->lock);
 205
 206        outb(0, idio16gpio->base + 1);
 207
 208        raw_spin_unlock(&idio16gpio->lock);
 209
 210        return IRQ_HANDLED;
 211}
 212
 213#define IDIO_16_NGPIO 32
 214static const char *idio_16_names[IDIO_16_NGPIO] = {
 215        "OUT0", "OUT1", "OUT2", "OUT3", "OUT4", "OUT5", "OUT6", "OUT7",
 216        "OUT8", "OUT9", "OUT10", "OUT11", "OUT12", "OUT13", "OUT14", "OUT15",
 217        "IIN0", "IIN1", "IIN2", "IIN3", "IIN4", "IIN5", "IIN6", "IIN7",
 218        "IIN8", "IIN9", "IIN10", "IIN11", "IIN12", "IIN13", "IIN14", "IIN15"
 219};
 220
 221static int idio_16_probe(struct device *dev, unsigned int id)
 222{
 223        struct idio_16_gpio *idio16gpio;
 224        const char *const name = dev_name(dev);
 225        int err;
 226
 227        idio16gpio = devm_kzalloc(dev, sizeof(*idio16gpio), GFP_KERNEL);
 228        if (!idio16gpio)
 229                return -ENOMEM;
 230
 231        if (!devm_request_region(dev, base[id], IDIO_16_EXTENT, name)) {
 232                dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
 233                        base[id], base[id] + IDIO_16_EXTENT);
 234                return -EBUSY;
 235        }
 236
 237        idio16gpio->chip.label = name;
 238        idio16gpio->chip.parent = dev;
 239        idio16gpio->chip.owner = THIS_MODULE;
 240        idio16gpio->chip.base = -1;
 241        idio16gpio->chip.ngpio = IDIO_16_NGPIO;
 242        idio16gpio->chip.names = idio_16_names;
 243        idio16gpio->chip.get_direction = idio_16_gpio_get_direction;
 244        idio16gpio->chip.direction_input = idio_16_gpio_direction_input;
 245        idio16gpio->chip.direction_output = idio_16_gpio_direction_output;
 246        idio16gpio->chip.get = idio_16_gpio_get;
 247        idio16gpio->chip.set = idio_16_gpio_set;
 248        idio16gpio->chip.set_multiple = idio_16_gpio_set_multiple;
 249        idio16gpio->base = base[id];
 250        idio16gpio->out_state = 0xFFFF;
 251
 252        raw_spin_lock_init(&idio16gpio->lock);
 253
 254        err = devm_gpiochip_add_data(dev, &idio16gpio->chip, idio16gpio);
 255        if (err) {
 256                dev_err(dev, "GPIO registering failed (%d)\n", err);
 257                return err;
 258        }
 259
 260        /* Disable IRQ by default */
 261        outb(0, base[id] + 2);
 262        outb(0, base[id] + 1);
 263
 264        err = gpiochip_irqchip_add(&idio16gpio->chip, &idio_16_irqchip, 0,
 265                handle_edge_irq, IRQ_TYPE_NONE);
 266        if (err) {
 267                dev_err(dev, "Could not add irqchip (%d)\n", err);
 268                return err;
 269        }
 270
 271        err = devm_request_irq(dev, irq[id], idio_16_irq_handler, 0, name,
 272                idio16gpio);
 273        if (err) {
 274                dev_err(dev, "IRQ handler registering failed (%d)\n", err);
 275                return err;
 276        }
 277
 278        return 0;
 279}
 280
 281static struct isa_driver idio_16_driver = {
 282        .probe = idio_16_probe,
 283        .driver = {
 284                .name = "104-idio-16"
 285        },
 286};
 287
 288module_isa_driver(idio_16_driver, num_idio_16);
 289
 290MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
 291MODULE_DESCRIPTION("ACCES 104-IDIO-16 GPIO driver");
 292MODULE_LICENSE("GPL v2");
 293