linux/drivers/gpio/gpio-pcie-idio-24.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * GPIO driver for the ACCES PCIe-IDIO-24 family
   4 * Copyright (C) 2018 William Breathitt Gray
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License, version 2, as
   8 * published by the Free Software Foundation.
   9 *
  10 * This program is distributed in the hope that it will be useful, but
  11 * WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13 * General Public License for more details.
  14 *
  15 * This driver supports the following ACCES devices: PCIe-IDIO-24,
  16 * PCIe-IDI-24, PCIe-IDO-24, and PCIe-IDIO-12.
  17 */
  18#include <linux/bitmap.h>
  19#include <linux/bitops.h>
  20#include <linux/device.h>
  21#include <linux/errno.h>
  22#include <linux/gpio/driver.h>
  23#include <linux/interrupt.h>
  24#include <linux/irqdesc.h>
  25#include <linux/kernel.h>
  26#include <linux/module.h>
  27#include <linux/pci.h>
  28#include <linux/spinlock.h>
  29#include <linux/types.h>
  30
  31/**
  32 * struct idio_24_gpio_reg - GPIO device registers structure
  33 * @out0_7:     Read: FET Outputs 0-7
  34 *              Write: FET Outputs 0-7
  35 * @out8_15:    Read: FET Outputs 8-15
  36 *              Write: FET Outputs 8-15
  37 * @out16_23:   Read: FET Outputs 16-23
  38 *              Write: FET Outputs 16-23
  39 * @ttl_out0_7: Read: TTL/CMOS Outputs 0-7
  40 *              Write: TTL/CMOS Outputs 0-7
  41 * @in0_7:      Read: Isolated Inputs 0-7
  42 *              Write: Reserved
  43 * @in8_15:     Read: Isolated Inputs 8-15
  44 *              Write: Reserved
  45 * @in16_23:    Read: Isolated Inputs 16-23
  46 *              Write: Reserved
  47 * @ttl_in0_7:  Read: TTL/CMOS Inputs 0-7
  48 *              Write: Reserved
  49 * @cos0_7:     Read: COS Status Inputs 0-7
  50 *              Write: COS Clear Inputs 0-7
  51 * @cos8_15:    Read: COS Status Inputs 8-15
  52 *              Write: COS Clear Inputs 8-15
  53 * @cos16_23:   Read: COS Status Inputs 16-23
  54 *              Write: COS Clear Inputs 16-23
  55 * @cos_ttl0_7: Read: COS Status TTL/CMOS 0-7
  56 *              Write: COS Clear TTL/CMOS 0-7
  57 * @ctl:        Read: Control Register
  58 *              Write: Control Register
  59 * @reserved:   Read: Reserved
  60 *              Write: Reserved
  61 * @cos_enable: Read: COS Enable
  62 *              Write: COS Enable
  63 * @soft_reset: Read: IRQ Output Pin Status
  64 *              Write: Software Board Reset
  65 */
  66struct idio_24_gpio_reg {
  67        u8 out0_7;
  68        u8 out8_15;
  69        u8 out16_23;
  70        u8 ttl_out0_7;
  71        u8 in0_7;
  72        u8 in8_15;
  73        u8 in16_23;
  74        u8 ttl_in0_7;
  75        u8 cos0_7;
  76        u8 cos8_15;
  77        u8 cos16_23;
  78        u8 cos_ttl0_7;
  79        u8 ctl;
  80        u8 reserved;
  81        u8 cos_enable;
  82        u8 soft_reset;
  83};
  84
  85/**
  86 * struct idio_24_gpio - GPIO device private data structure
  87 * @chip:       instance of the gpio_chip
  88 * @lock:       synchronization lock to prevent I/O race conditions
  89 * @reg:        I/O address offset for the GPIO device registers
  90 * @irq_mask:   I/O bits affected by interrupts
  91 */
  92struct idio_24_gpio {
  93        struct gpio_chip chip;
  94        raw_spinlock_t lock;
  95        struct idio_24_gpio_reg __iomem *reg;
  96        unsigned long irq_mask;
  97};
  98
  99static int idio_24_gpio_get_direction(struct gpio_chip *chip,
 100        unsigned int offset)
 101{
 102        struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
 103        const unsigned long out_mode_mask = BIT(1);
 104
 105        /* FET Outputs */
 106        if (offset < 24)
 107                return GPIO_LINE_DIRECTION_OUT;
 108
 109        /* Isolated Inputs */
 110        if (offset < 48)
 111                return GPIO_LINE_DIRECTION_IN;
 112
 113        /* TTL/CMOS I/O */
 114        /* OUT MODE = 1 when TTL/CMOS Output Mode is set */
 115        if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask)
 116                return GPIO_LINE_DIRECTION_OUT;
 117
 118        return GPIO_LINE_DIRECTION_IN;
 119}
 120
 121static int idio_24_gpio_direction_input(struct gpio_chip *chip,
 122        unsigned int offset)
 123{
 124        struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
 125        unsigned long flags;
 126        unsigned int ctl_state;
 127        const unsigned long out_mode_mask = BIT(1);
 128
 129        /* TTL/CMOS I/O */
 130        if (offset > 47) {
 131                raw_spin_lock_irqsave(&idio24gpio->lock, flags);
 132
 133                /* Clear TTL/CMOS Output Mode */
 134                ctl_state = ioread8(&idio24gpio->reg->ctl) & ~out_mode_mask;
 135                iowrite8(ctl_state, &idio24gpio->reg->ctl);
 136
 137                raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
 138        }
 139
 140        return 0;
 141}
 142
 143static int idio_24_gpio_direction_output(struct gpio_chip *chip,
 144        unsigned int offset, int value)
 145{
 146        struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
 147        unsigned long flags;
 148        unsigned int ctl_state;
 149        const unsigned long out_mode_mask = BIT(1);
 150
 151        /* TTL/CMOS I/O */
 152        if (offset > 47) {
 153                raw_spin_lock_irqsave(&idio24gpio->lock, flags);
 154
 155                /* Set TTL/CMOS Output Mode */
 156                ctl_state = ioread8(&idio24gpio->reg->ctl) | out_mode_mask;
 157                iowrite8(ctl_state, &idio24gpio->reg->ctl);
 158
 159                raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
 160        }
 161
 162        chip->set(chip, offset, value);
 163        return 0;
 164}
 165
 166static int idio_24_gpio_get(struct gpio_chip *chip, unsigned int offset)
 167{
 168        struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
 169        const unsigned long offset_mask = BIT(offset % 8);
 170        const unsigned long out_mode_mask = BIT(1);
 171
 172        /* FET Outputs */
 173        if (offset < 8)
 174                return !!(ioread8(&idio24gpio->reg->out0_7) & offset_mask);
 175
 176        if (offset < 16)
 177                return !!(ioread8(&idio24gpio->reg->out8_15) & offset_mask);
 178
 179        if (offset < 24)
 180                return !!(ioread8(&idio24gpio->reg->out16_23) & offset_mask);
 181
 182        /* Isolated Inputs */
 183        if (offset < 32)
 184                return !!(ioread8(&idio24gpio->reg->in0_7) & offset_mask);
 185
 186        if (offset < 40)
 187                return !!(ioread8(&idio24gpio->reg->in8_15) & offset_mask);
 188
 189        if (offset < 48)
 190                return !!(ioread8(&idio24gpio->reg->in16_23) & offset_mask);
 191
 192        /* TTL/CMOS Outputs */
 193        if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask)
 194                return !!(ioread8(&idio24gpio->reg->ttl_out0_7) & offset_mask);
 195
 196        /* TTL/CMOS Inputs */
 197        return !!(ioread8(&idio24gpio->reg->ttl_in0_7) & offset_mask);
 198}
 199
 200static int idio_24_gpio_get_multiple(struct gpio_chip *chip,
 201        unsigned long *mask, unsigned long *bits)
 202{
 203        struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
 204        unsigned long offset;
 205        unsigned long gpio_mask;
 206        void __iomem *ports[] = {
 207                &idio24gpio->reg->out0_7, &idio24gpio->reg->out8_15,
 208                &idio24gpio->reg->out16_23, &idio24gpio->reg->in0_7,
 209                &idio24gpio->reg->in8_15, &idio24gpio->reg->in16_23,
 210        };
 211        size_t index;
 212        unsigned long port_state;
 213        const unsigned long out_mode_mask = BIT(1);
 214
 215        /* clear bits array to a clean slate */
 216        bitmap_zero(bits, chip->ngpio);
 217
 218        for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) {
 219                index = offset / 8;
 220
 221                /* read bits from current gpio port (port 6 is TTL GPIO) */
 222                if (index < 6)
 223                        port_state = ioread8(ports[index]);
 224                else if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask)
 225                        port_state = ioread8(&idio24gpio->reg->ttl_out0_7);
 226                else
 227                        port_state = ioread8(&idio24gpio->reg->ttl_in0_7);
 228
 229                port_state &= gpio_mask;
 230
 231                bitmap_set_value8(bits, port_state, offset);
 232        }
 233
 234        return 0;
 235}
 236
 237static void idio_24_gpio_set(struct gpio_chip *chip, unsigned int offset,
 238        int value)
 239{
 240        struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
 241        const unsigned long out_mode_mask = BIT(1);
 242        void __iomem *base;
 243        const unsigned int mask = BIT(offset % 8);
 244        unsigned long flags;
 245        unsigned int out_state;
 246
 247        /* Isolated Inputs */
 248        if (offset > 23 && offset < 48)
 249                return;
 250
 251        /* TTL/CMOS Inputs */
 252        if (offset > 47 && !(ioread8(&idio24gpio->reg->ctl) & out_mode_mask))
 253                return;
 254
 255        /* TTL/CMOS Outputs */
 256        if (offset > 47)
 257                base = &idio24gpio->reg->ttl_out0_7;
 258        /* FET Outputs */
 259        else if (offset > 15)
 260                base = &idio24gpio->reg->out16_23;
 261        else if (offset > 7)
 262                base = &idio24gpio->reg->out8_15;
 263        else
 264                base = &idio24gpio->reg->out0_7;
 265
 266        raw_spin_lock_irqsave(&idio24gpio->lock, flags);
 267
 268        if (value)
 269                out_state = ioread8(base) | mask;
 270        else
 271                out_state = ioread8(base) & ~mask;
 272
 273        iowrite8(out_state, base);
 274
 275        raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
 276}
 277
 278static void idio_24_gpio_set_multiple(struct gpio_chip *chip,
 279        unsigned long *mask, unsigned long *bits)
 280{
 281        struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
 282        unsigned long offset;
 283        unsigned long gpio_mask;
 284        void __iomem *ports[] = {
 285                &idio24gpio->reg->out0_7, &idio24gpio->reg->out8_15,
 286                &idio24gpio->reg->out16_23
 287        };
 288        size_t index;
 289        unsigned long bitmask;
 290        unsigned long flags;
 291        unsigned long out_state;
 292        const unsigned long out_mode_mask = BIT(1);
 293
 294        for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) {
 295                index = offset / 8;
 296
 297                bitmask = bitmap_get_value8(bits, offset) & gpio_mask;
 298
 299                raw_spin_lock_irqsave(&idio24gpio->lock, flags);
 300
 301                /* read bits from current gpio port (port 6 is TTL GPIO) */
 302                if (index < 6) {
 303                        out_state = ioread8(ports[index]);
 304                } else if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask) {
 305                        out_state = ioread8(&idio24gpio->reg->ttl_out0_7);
 306                } else {
 307                        /* skip TTL GPIO if set for input */
 308                        raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
 309                        continue;
 310                }
 311
 312                /* set requested bit states */
 313                out_state &= ~gpio_mask;
 314                out_state |= bitmask;
 315
 316                /* write bits for current gpio port (port 6 is TTL GPIO) */
 317                if (index < 6)
 318                        iowrite8(out_state, ports[index]);
 319                else
 320                        iowrite8(out_state, &idio24gpio->reg->ttl_out0_7);
 321
 322                raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
 323        }
 324}
 325
 326static void idio_24_irq_ack(struct irq_data *data)
 327{
 328}
 329
 330static void idio_24_irq_mask(struct irq_data *data)
 331{
 332        struct gpio_chip *const chip = irq_data_get_irq_chip_data(data);
 333        struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
 334        unsigned long flags;
 335        const unsigned long bit_offset = irqd_to_hwirq(data) - 24;
 336        unsigned char new_irq_mask;
 337        const unsigned long bank_offset = bit_offset/8 * 8;
 338        unsigned char cos_enable_state;
 339
 340        raw_spin_lock_irqsave(&idio24gpio->lock, flags);
 341
 342        idio24gpio->irq_mask &= BIT(bit_offset);
 343        new_irq_mask = idio24gpio->irq_mask >> bank_offset;
 344
 345        if (!new_irq_mask) {
 346                cos_enable_state = ioread8(&idio24gpio->reg->cos_enable);
 347
 348                /* Disable Rising Edge detection */
 349                cos_enable_state &= ~BIT(bank_offset);
 350                /* Disable Falling Edge detection */
 351                cos_enable_state &= ~BIT(bank_offset + 4);
 352
 353                iowrite8(cos_enable_state, &idio24gpio->reg->cos_enable);
 354        }
 355
 356        raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
 357}
 358
 359static void idio_24_irq_unmask(struct irq_data *data)
 360{
 361        struct gpio_chip *const chip = irq_data_get_irq_chip_data(data);
 362        struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
 363        unsigned long flags;
 364        unsigned char prev_irq_mask;
 365        const unsigned long bit_offset = irqd_to_hwirq(data) - 24;
 366        const unsigned long bank_offset = bit_offset/8 * 8;
 367        unsigned char cos_enable_state;
 368
 369        raw_spin_lock_irqsave(&idio24gpio->lock, flags);
 370
 371        prev_irq_mask = idio24gpio->irq_mask >> bank_offset;
 372        idio24gpio->irq_mask |= BIT(bit_offset);
 373
 374        if (!prev_irq_mask) {
 375                cos_enable_state = ioread8(&idio24gpio->reg->cos_enable);
 376
 377                /* Enable Rising Edge detection */
 378                cos_enable_state |= BIT(bank_offset);
 379                /* Enable Falling Edge detection */
 380                cos_enable_state |= BIT(bank_offset + 4);
 381
 382                iowrite8(cos_enable_state, &idio24gpio->reg->cos_enable);
 383        }
 384
 385        raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
 386}
 387
 388static int idio_24_irq_set_type(struct irq_data *data, unsigned int flow_type)
 389{
 390        /* The only valid irq types are none and both-edges */
 391        if (flow_type != IRQ_TYPE_NONE &&
 392                (flow_type & IRQ_TYPE_EDGE_BOTH) != IRQ_TYPE_EDGE_BOTH)
 393                return -EINVAL;
 394
 395        return 0;
 396}
 397
 398static struct irq_chip idio_24_irqchip = {
 399        .name = "pcie-idio-24",
 400        .irq_ack = idio_24_irq_ack,
 401        .irq_mask = idio_24_irq_mask,
 402        .irq_unmask = idio_24_irq_unmask,
 403        .irq_set_type = idio_24_irq_set_type
 404};
 405
 406static irqreturn_t idio_24_irq_handler(int irq, void *dev_id)
 407{
 408        struct idio_24_gpio *const idio24gpio = dev_id;
 409        unsigned long irq_status;
 410        struct gpio_chip *const chip = &idio24gpio->chip;
 411        unsigned long irq_mask;
 412        int gpio;
 413
 414        raw_spin_lock(&idio24gpio->lock);
 415
 416        /* Read Change-Of-State status */
 417        irq_status = ioread32(&idio24gpio->reg->cos0_7);
 418
 419        raw_spin_unlock(&idio24gpio->lock);
 420
 421        /* Make sure our device generated IRQ */
 422        if (!irq_status)
 423                return IRQ_NONE;
 424
 425        /* Handle only unmasked IRQ */
 426        irq_mask = idio24gpio->irq_mask & irq_status;
 427
 428        for_each_set_bit(gpio, &irq_mask, chip->ngpio - 24)
 429                generic_handle_irq(irq_find_mapping(chip->irq.domain,
 430                        gpio + 24));
 431
 432        raw_spin_lock(&idio24gpio->lock);
 433
 434        /* Clear Change-Of-State status */
 435        iowrite32(irq_status, &idio24gpio->reg->cos0_7);
 436
 437        raw_spin_unlock(&idio24gpio->lock);
 438
 439        return IRQ_HANDLED;
 440}
 441
 442#define IDIO_24_NGPIO 56
 443static const char *idio_24_names[IDIO_24_NGPIO] = {
 444        "OUT0", "OUT1", "OUT2", "OUT3", "OUT4", "OUT5", "OUT6", "OUT7",
 445        "OUT8", "OUT9", "OUT10", "OUT11", "OUT12", "OUT13", "OUT14", "OUT15",
 446        "OUT16", "OUT17", "OUT18", "OUT19", "OUT20", "OUT21", "OUT22", "OUT23",
 447        "IIN0", "IIN1", "IIN2", "IIN3", "IIN4", "IIN5", "IIN6", "IIN7",
 448        "IIN8", "IIN9", "IIN10", "IIN11", "IIN12", "IIN13", "IIN14", "IIN15",
 449        "IIN16", "IIN17", "IIN18", "IIN19", "IIN20", "IIN21", "IIN22", "IIN23",
 450        "TTL0", "TTL1", "TTL2", "TTL3", "TTL4", "TTL5", "TTL6", "TTL7"
 451};
 452
 453static int idio_24_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 454{
 455        struct device *const dev = &pdev->dev;
 456        struct idio_24_gpio *idio24gpio;
 457        int err;
 458        const size_t pci_bar_index = 2;
 459        const char *const name = pci_name(pdev);
 460
 461        idio24gpio = devm_kzalloc(dev, sizeof(*idio24gpio), GFP_KERNEL);
 462        if (!idio24gpio)
 463                return -ENOMEM;
 464
 465        err = pcim_enable_device(pdev);
 466        if (err) {
 467                dev_err(dev, "Failed to enable PCI device (%d)\n", err);
 468                return err;
 469        }
 470
 471        err = pcim_iomap_regions(pdev, BIT(pci_bar_index), name);
 472        if (err) {
 473                dev_err(dev, "Unable to map PCI I/O addresses (%d)\n", err);
 474                return err;
 475        }
 476
 477        idio24gpio->reg = pcim_iomap_table(pdev)[pci_bar_index];
 478
 479        idio24gpio->chip.label = name;
 480        idio24gpio->chip.parent = dev;
 481        idio24gpio->chip.owner = THIS_MODULE;
 482        idio24gpio->chip.base = -1;
 483        idio24gpio->chip.ngpio = IDIO_24_NGPIO;
 484        idio24gpio->chip.names = idio_24_names;
 485        idio24gpio->chip.get_direction = idio_24_gpio_get_direction;
 486        idio24gpio->chip.direction_input = idio_24_gpio_direction_input;
 487        idio24gpio->chip.direction_output = idio_24_gpio_direction_output;
 488        idio24gpio->chip.get = idio_24_gpio_get;
 489        idio24gpio->chip.get_multiple = idio_24_gpio_get_multiple;
 490        idio24gpio->chip.set = idio_24_gpio_set;
 491        idio24gpio->chip.set_multiple = idio_24_gpio_set_multiple;
 492
 493        raw_spin_lock_init(&idio24gpio->lock);
 494
 495        /* Software board reset */
 496        iowrite8(0, &idio24gpio->reg->soft_reset);
 497
 498        err = devm_gpiochip_add_data(dev, &idio24gpio->chip, idio24gpio);
 499        if (err) {
 500                dev_err(dev, "GPIO registering failed (%d)\n", err);
 501                return err;
 502        }
 503
 504        err = gpiochip_irqchip_add(&idio24gpio->chip, &idio_24_irqchip, 0,
 505                handle_edge_irq, IRQ_TYPE_NONE);
 506        if (err) {
 507                dev_err(dev, "Could not add irqchip (%d)\n", err);
 508                return err;
 509        }
 510
 511        err = devm_request_irq(dev, pdev->irq, idio_24_irq_handler, IRQF_SHARED,
 512                name, idio24gpio);
 513        if (err) {
 514                dev_err(dev, "IRQ handler registering failed (%d)\n", err);
 515                return err;
 516        }
 517
 518        return 0;
 519}
 520
 521static const struct pci_device_id idio_24_pci_dev_id[] = {
 522        { PCI_DEVICE(0x494F, 0x0FD0) }, { PCI_DEVICE(0x494F, 0x0BD0) },
 523        { PCI_DEVICE(0x494F, 0x07D0) }, { PCI_DEVICE(0x494F, 0x0FC0) },
 524        { 0 }
 525};
 526MODULE_DEVICE_TABLE(pci, idio_24_pci_dev_id);
 527
 528static struct pci_driver idio_24_driver = {
 529        .name = "pcie-idio-24",
 530        .id_table = idio_24_pci_dev_id,
 531        .probe = idio_24_probe
 532};
 533
 534module_pci_driver(idio_24_driver);
 535
 536MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
 537MODULE_DESCRIPTION("ACCES PCIe-IDIO-24 GPIO driver");
 538MODULE_LICENSE("GPL v2");
 539