linux/drivers/gpio/gpio-pci-idio-16.c
<<
>>
Prefs
   1/*
   2 * GPIO driver for the ACCES PCI-IDIO-16
   3 * Copyright (C) 2017 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#include <linux/bitmap.h>
  15#include <linux/bitops.h>
  16#include <linux/device.h>
  17#include <linux/errno.h>
  18#include <linux/gpio/driver.h>
  19#include <linux/interrupt.h>
  20#include <linux/irqdesc.h>
  21#include <linux/kernel.h>
  22#include <linux/module.h>
  23#include <linux/pci.h>
  24#include <linux/spinlock.h>
  25#include <linux/types.h>
  26
  27/**
  28 * struct idio_16_gpio_reg - GPIO device registers structure
  29 * @out0_7:     Read: FET Drive Outputs 0-7
  30 *              Write: FET Drive Outputs 0-7
  31 * @in0_7:      Read: Isolated Inputs 0-7
  32 *              Write: Clear Interrupt
  33 * @irq_ctl:    Read: Enable IRQ
  34 *              Write: Disable IRQ
  35 * @filter_ctl: Read: Activate Input Filters 0-15
  36 *              Write: Deactivate Input Filters 0-15
  37 * @out8_15:    Read: FET Drive Outputs 8-15
  38 *              Write: FET Drive Outputs 8-15
  39 * @in8_15:     Read: Isolated Inputs 8-15
  40 *              Write: Unused
  41 * @irq_status: Read: Interrupt status
  42 *              Write: Unused
  43 */
  44struct idio_16_gpio_reg {
  45        u8 out0_7;
  46        u8 in0_7;
  47        u8 irq_ctl;
  48        u8 filter_ctl;
  49        u8 out8_15;
  50        u8 in8_15;
  51        u8 irq_status;
  52};
  53
  54/**
  55 * struct idio_16_gpio - GPIO device private data structure
  56 * @chip:       instance of the gpio_chip
  57 * @lock:       synchronization lock to prevent I/O race conditions
  58 * @reg:        I/O address offset for the GPIO device registers
  59 * @irq_mask:   I/O bits affected by interrupts
  60 */
  61struct idio_16_gpio {
  62        struct gpio_chip chip;
  63        raw_spinlock_t lock;
  64        struct idio_16_gpio_reg __iomem *reg;
  65        unsigned long irq_mask;
  66};
  67
  68static int idio_16_gpio_get_direction(struct gpio_chip *chip,
  69        unsigned int offset)
  70{
  71        if (offset > 15)
  72                return 1;
  73
  74        return 0;
  75}
  76
  77static int idio_16_gpio_direction_input(struct gpio_chip *chip,
  78        unsigned int offset)
  79{
  80        return 0;
  81}
  82
  83static int idio_16_gpio_direction_output(struct gpio_chip *chip,
  84        unsigned int offset, int value)
  85{
  86        chip->set(chip, offset, value);
  87        return 0;
  88}
  89
  90static int idio_16_gpio_get(struct gpio_chip *chip, unsigned int offset)
  91{
  92        struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
  93        unsigned long mask = BIT(offset);
  94
  95        if (offset < 8)
  96                return !!(ioread8(&idio16gpio->reg->out0_7) & mask);
  97
  98        if (offset < 16)
  99                return !!(ioread8(&idio16gpio->reg->out8_15) & (mask >> 8));
 100
 101        if (offset < 24)
 102                return !!(ioread8(&idio16gpio->reg->in0_7) & (mask >> 16));
 103
 104        return !!(ioread8(&idio16gpio->reg->in8_15) & (mask >> 24));
 105}
 106
 107static int idio_16_gpio_get_multiple(struct gpio_chip *chip,
 108        unsigned long *mask, unsigned long *bits)
 109{
 110        struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
 111        size_t i;
 112        const unsigned int gpio_reg_size = 8;
 113        unsigned int bits_offset;
 114        size_t word_index;
 115        unsigned int word_offset;
 116        unsigned long word_mask;
 117        const unsigned long port_mask = GENMASK(gpio_reg_size - 1, 0);
 118        unsigned long port_state;
 119        void __iomem *ports[] = {
 120                &idio16gpio->reg->out0_7, &idio16gpio->reg->out8_15,
 121                &idio16gpio->reg->in0_7, &idio16gpio->reg->in8_15,
 122        };
 123
 124        /* clear bits array to a clean slate */
 125        bitmap_zero(bits, chip->ngpio);
 126
 127        /* get bits are evaluated a gpio port register at a time */
 128        for (i = 0; i < ARRAY_SIZE(ports); i++) {
 129                /* gpio offset in bits array */
 130                bits_offset = i * gpio_reg_size;
 131
 132                /* word index for bits array */
 133                word_index = BIT_WORD(bits_offset);
 134
 135                /* gpio offset within current word of bits array */
 136                word_offset = bits_offset % BITS_PER_LONG;
 137
 138                /* mask of get bits for current gpio within current word */
 139                word_mask = mask[word_index] & (port_mask << word_offset);
 140                if (!word_mask) {
 141                        /* no get bits in this port so skip to next one */
 142                        continue;
 143                }
 144
 145                /* read bits from current gpio port */
 146                port_state = ioread8(ports[i]);
 147
 148                /* store acquired bits at respective bits array offset */
 149                bits[word_index] |= port_state << word_offset;
 150        }
 151
 152        return 0;
 153}
 154
 155static void idio_16_gpio_set(struct gpio_chip *chip, unsigned int offset,
 156        int value)
 157{
 158        struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
 159        unsigned int mask = BIT(offset);
 160        void __iomem *base;
 161        unsigned long flags;
 162        unsigned int out_state;
 163
 164        if (offset > 15)
 165                return;
 166
 167        if (offset > 7) {
 168                mask >>= 8;
 169                base = &idio16gpio->reg->out8_15;
 170        } else
 171                base = &idio16gpio->reg->out0_7;
 172
 173        raw_spin_lock_irqsave(&idio16gpio->lock, flags);
 174
 175        if (value)
 176                out_state = ioread8(base) | mask;
 177        else
 178                out_state = ioread8(base) & ~mask;
 179
 180        iowrite8(out_state, base);
 181
 182        raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
 183}
 184
 185static void idio_16_gpio_set_multiple(struct gpio_chip *chip,
 186        unsigned long *mask, unsigned long *bits)
 187{
 188        struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
 189        unsigned long flags;
 190        unsigned int out_state;
 191
 192        raw_spin_lock_irqsave(&idio16gpio->lock, flags);
 193
 194        /* process output lines 0-7 */
 195        if (*mask & 0xFF) {
 196                out_state = ioread8(&idio16gpio->reg->out0_7) & ~*mask;
 197                out_state |= *mask & *bits;
 198                iowrite8(out_state, &idio16gpio->reg->out0_7);
 199        }
 200
 201        /* shift to next output line word */
 202        *mask >>= 8;
 203
 204        /* process output lines 8-15 */
 205        if (*mask & 0xFF) {
 206                *bits >>= 8;
 207                out_state = ioread8(&idio16gpio->reg->out8_15) & ~*mask;
 208                out_state |= *mask & *bits;
 209                iowrite8(out_state, &idio16gpio->reg->out8_15);
 210        }
 211
 212        raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
 213}
 214
 215static void idio_16_irq_ack(struct irq_data *data)
 216{
 217}
 218
 219static void idio_16_irq_mask(struct irq_data *data)
 220{
 221        struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
 222        struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
 223        const unsigned long mask = BIT(irqd_to_hwirq(data));
 224        unsigned long flags;
 225
 226        idio16gpio->irq_mask &= ~mask;
 227
 228        if (!idio16gpio->irq_mask) {
 229                raw_spin_lock_irqsave(&idio16gpio->lock, flags);
 230
 231                iowrite8(0, &idio16gpio->reg->irq_ctl);
 232
 233                raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
 234        }
 235}
 236
 237static void idio_16_irq_unmask(struct irq_data *data)
 238{
 239        struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
 240        struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
 241        const unsigned long mask = BIT(irqd_to_hwirq(data));
 242        const unsigned long prev_irq_mask = idio16gpio->irq_mask;
 243        unsigned long flags;
 244
 245        idio16gpio->irq_mask |= mask;
 246
 247        if (!prev_irq_mask) {
 248                raw_spin_lock_irqsave(&idio16gpio->lock, flags);
 249
 250                ioread8(&idio16gpio->reg->irq_ctl);
 251
 252                raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
 253        }
 254}
 255
 256static int idio_16_irq_set_type(struct irq_data *data, unsigned int flow_type)
 257{
 258        /* The only valid irq types are none and both-edges */
 259        if (flow_type != IRQ_TYPE_NONE &&
 260                (flow_type & IRQ_TYPE_EDGE_BOTH) != IRQ_TYPE_EDGE_BOTH)
 261                return -EINVAL;
 262
 263        return 0;
 264}
 265
 266static struct irq_chip idio_16_irqchip = {
 267        .name = "pci-idio-16",
 268        .irq_ack = idio_16_irq_ack,
 269        .irq_mask = idio_16_irq_mask,
 270        .irq_unmask = idio_16_irq_unmask,
 271        .irq_set_type = idio_16_irq_set_type
 272};
 273
 274static irqreturn_t idio_16_irq_handler(int irq, void *dev_id)
 275{
 276        struct idio_16_gpio *const idio16gpio = dev_id;
 277        unsigned int irq_status;
 278        struct gpio_chip *const chip = &idio16gpio->chip;
 279        int gpio;
 280
 281        raw_spin_lock(&idio16gpio->lock);
 282
 283        irq_status = ioread8(&idio16gpio->reg->irq_status);
 284
 285        raw_spin_unlock(&idio16gpio->lock);
 286
 287        /* Make sure our device generated IRQ */
 288        if (!(irq_status & 0x3) || !(irq_status & 0x4))
 289                return IRQ_NONE;
 290
 291        for_each_set_bit(gpio, &idio16gpio->irq_mask, chip->ngpio)
 292                generic_handle_irq(irq_find_mapping(chip->irq.domain, gpio));
 293
 294        raw_spin_lock(&idio16gpio->lock);
 295
 296        /* Clear interrupt */
 297        iowrite8(0, &idio16gpio->reg->in0_7);
 298
 299        raw_spin_unlock(&idio16gpio->lock);
 300
 301        return IRQ_HANDLED;
 302}
 303
 304#define IDIO_16_NGPIO 32
 305static const char *idio_16_names[IDIO_16_NGPIO] = {
 306        "OUT0", "OUT1", "OUT2", "OUT3", "OUT4", "OUT5", "OUT6", "OUT7",
 307        "OUT8", "OUT9", "OUT10", "OUT11", "OUT12", "OUT13", "OUT14", "OUT15",
 308        "IIN0", "IIN1", "IIN2", "IIN3", "IIN4", "IIN5", "IIN6", "IIN7",
 309        "IIN8", "IIN9", "IIN10", "IIN11", "IIN12", "IIN13", "IIN14", "IIN15"
 310};
 311
 312static int idio_16_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 313{
 314        struct device *const dev = &pdev->dev;
 315        struct idio_16_gpio *idio16gpio;
 316        int err;
 317        const size_t pci_bar_index = 2;
 318        const char *const name = pci_name(pdev);
 319
 320        idio16gpio = devm_kzalloc(dev, sizeof(*idio16gpio), GFP_KERNEL);
 321        if (!idio16gpio)
 322                return -ENOMEM;
 323
 324        err = pcim_enable_device(pdev);
 325        if (err) {
 326                dev_err(dev, "Failed to enable PCI device (%d)\n", err);
 327                return err;
 328        }
 329
 330        err = pcim_iomap_regions(pdev, BIT(pci_bar_index), name);
 331        if (err) {
 332                dev_err(dev, "Unable to map PCI I/O addresses (%d)\n", err);
 333                return err;
 334        }
 335
 336        idio16gpio->reg = pcim_iomap_table(pdev)[pci_bar_index];
 337
 338        /* Deactivate input filters */
 339        iowrite8(0, &idio16gpio->reg->filter_ctl);
 340
 341        idio16gpio->chip.label = name;
 342        idio16gpio->chip.parent = dev;
 343        idio16gpio->chip.owner = THIS_MODULE;
 344        idio16gpio->chip.base = -1;
 345        idio16gpio->chip.ngpio = IDIO_16_NGPIO;
 346        idio16gpio->chip.names = idio_16_names;
 347        idio16gpio->chip.get_direction = idio_16_gpio_get_direction;
 348        idio16gpio->chip.direction_input = idio_16_gpio_direction_input;
 349        idio16gpio->chip.direction_output = idio_16_gpio_direction_output;
 350        idio16gpio->chip.get = idio_16_gpio_get;
 351        idio16gpio->chip.get_multiple = idio_16_gpio_get_multiple;
 352        idio16gpio->chip.set = idio_16_gpio_set;
 353        idio16gpio->chip.set_multiple = idio_16_gpio_set_multiple;
 354
 355        raw_spin_lock_init(&idio16gpio->lock);
 356
 357        err = devm_gpiochip_add_data(dev, &idio16gpio->chip, idio16gpio);
 358        if (err) {
 359                dev_err(dev, "GPIO registering failed (%d)\n", err);
 360                return err;
 361        }
 362
 363        /* Disable IRQ by default and clear any pending interrupt */
 364        iowrite8(0, &idio16gpio->reg->irq_ctl);
 365        iowrite8(0, &idio16gpio->reg->in0_7);
 366
 367        err = gpiochip_irqchip_add(&idio16gpio->chip, &idio_16_irqchip, 0,
 368                handle_edge_irq, IRQ_TYPE_NONE);
 369        if (err) {
 370                dev_err(dev, "Could not add irqchip (%d)\n", err);
 371                return err;
 372        }
 373
 374        err = devm_request_irq(dev, pdev->irq, idio_16_irq_handler, IRQF_SHARED,
 375                name, idio16gpio);
 376        if (err) {
 377                dev_err(dev, "IRQ handler registering failed (%d)\n", err);
 378                return err;
 379        }
 380
 381        return 0;
 382}
 383
 384static const struct pci_device_id idio_16_pci_dev_id[] = {
 385        { PCI_DEVICE(0x494F, 0x0DC8) }, { 0 }
 386};
 387MODULE_DEVICE_TABLE(pci, idio_16_pci_dev_id);
 388
 389static struct pci_driver idio_16_driver = {
 390        .name = "pci-idio-16",
 391        .id_table = idio_16_pci_dev_id,
 392        .probe = idio_16_probe
 393};
 394
 395module_pci_driver(idio_16_driver);
 396
 397MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
 398MODULE_DESCRIPTION("ACCES PCI-IDIO-16 GPIO driver");
 399MODULE_LICENSE("GPL v2");
 400