11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 202e74fc0SWilliam Breathitt Gray /* 302e74fc0SWilliam Breathitt Gray * GPIO driver for the ACCES PCI-IDIO-16 402e74fc0SWilliam Breathitt Gray * Copyright (C) 2017 William Breathitt Gray 502e74fc0SWilliam Breathitt Gray */ 6*e7f758faSWilliam Breathitt Gray #include <linux/bits.h> 702e74fc0SWilliam Breathitt Gray #include <linux/device.h> 802e74fc0SWilliam Breathitt Gray #include <linux/errno.h> 902e74fc0SWilliam Breathitt Gray #include <linux/gpio/driver.h> 1002e74fc0SWilliam Breathitt Gray #include <linux/interrupt.h> 1102e74fc0SWilliam Breathitt Gray #include <linux/irqdesc.h> 1202e74fc0SWilliam Breathitt Gray #include <linux/kernel.h> 1302e74fc0SWilliam Breathitt Gray #include <linux/module.h> 1402e74fc0SWilliam Breathitt Gray #include <linux/pci.h> 1502e74fc0SWilliam Breathitt Gray #include <linux/spinlock.h> 1602e74fc0SWilliam Breathitt Gray #include <linux/types.h> 1702e74fc0SWilliam Breathitt Gray 18*e7f758faSWilliam Breathitt Gray #include "gpio-idio-16.h" 1902e74fc0SWilliam Breathitt Gray 2002e74fc0SWilliam Breathitt Gray /** 2102e74fc0SWilliam Breathitt Gray * struct idio_16_gpio - GPIO device private data structure 2202e74fc0SWilliam Breathitt Gray * @chip: instance of the gpio_chip 2302e74fc0SWilliam Breathitt Gray * @lock: synchronization lock to prevent I/O race conditions 2402e74fc0SWilliam Breathitt Gray * @reg: I/O address offset for the GPIO device registers 25*e7f758faSWilliam Breathitt Gray * @state: ACCES IDIO-16 device state 2602e74fc0SWilliam Breathitt Gray * @irq_mask: I/O bits affected by interrupts 2702e74fc0SWilliam Breathitt Gray */ 2802e74fc0SWilliam Breathitt Gray struct idio_16_gpio { 2902e74fc0SWilliam Breathitt Gray struct gpio_chip chip; 30ea38ce08SJulia Cartwright raw_spinlock_t lock; 31*e7f758faSWilliam Breathitt Gray struct idio_16 __iomem *reg; 32*e7f758faSWilliam Breathitt Gray struct idio_16_state state; 3302e74fc0SWilliam Breathitt Gray unsigned long irq_mask; 3402e74fc0SWilliam Breathitt Gray }; 3502e74fc0SWilliam Breathitt Gray 3602e74fc0SWilliam Breathitt Gray static int idio_16_gpio_get_direction(struct gpio_chip *chip, 3702e74fc0SWilliam Breathitt Gray unsigned int offset) 3802e74fc0SWilliam Breathitt Gray { 39*e7f758faSWilliam Breathitt Gray if (idio_16_get_direction(offset)) 40e42615ecSMatti Vaittinen return GPIO_LINE_DIRECTION_IN; 4102e74fc0SWilliam Breathitt Gray 42e42615ecSMatti Vaittinen return GPIO_LINE_DIRECTION_OUT; 4302e74fc0SWilliam Breathitt Gray } 4402e74fc0SWilliam Breathitt Gray 4502e74fc0SWilliam Breathitt Gray static int idio_16_gpio_direction_input(struct gpio_chip *chip, 4602e74fc0SWilliam Breathitt Gray unsigned int offset) 4702e74fc0SWilliam Breathitt Gray { 4802e74fc0SWilliam Breathitt Gray return 0; 4902e74fc0SWilliam Breathitt Gray } 5002e74fc0SWilliam Breathitt Gray 5102e74fc0SWilliam Breathitt Gray static int idio_16_gpio_direction_output(struct gpio_chip *chip, 5202e74fc0SWilliam Breathitt Gray unsigned int offset, int value) 5302e74fc0SWilliam Breathitt Gray { 5402e74fc0SWilliam Breathitt Gray chip->set(chip, offset, value); 5502e74fc0SWilliam Breathitt Gray return 0; 5602e74fc0SWilliam Breathitt Gray } 5702e74fc0SWilliam Breathitt Gray 5802e74fc0SWilliam Breathitt Gray static int idio_16_gpio_get(struct gpio_chip *chip, unsigned int offset) 5902e74fc0SWilliam Breathitt Gray { 6002e74fc0SWilliam Breathitt Gray struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip); 6102e74fc0SWilliam Breathitt Gray 62*e7f758faSWilliam Breathitt Gray return idio_16_get(idio16gpio->reg, &idio16gpio->state, offset); 6302e74fc0SWilliam Breathitt Gray } 6402e74fc0SWilliam Breathitt Gray 65810ebfc5SWilliam Breathitt Gray static int idio_16_gpio_get_multiple(struct gpio_chip *chip, 66810ebfc5SWilliam Breathitt Gray unsigned long *mask, unsigned long *bits) 67810ebfc5SWilliam Breathitt Gray { 68810ebfc5SWilliam Breathitt Gray struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip); 69810ebfc5SWilliam Breathitt Gray 70*e7f758faSWilliam Breathitt Gray idio_16_get_multiple(idio16gpio->reg, &idio16gpio->state, mask, bits); 71810ebfc5SWilliam Breathitt Gray return 0; 72810ebfc5SWilliam Breathitt Gray } 73810ebfc5SWilliam Breathitt Gray 7402e74fc0SWilliam Breathitt Gray static void idio_16_gpio_set(struct gpio_chip *chip, unsigned int offset, 7502e74fc0SWilliam Breathitt Gray int value) 7602e74fc0SWilliam Breathitt Gray { 7702e74fc0SWilliam Breathitt Gray struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip); 7802e74fc0SWilliam Breathitt Gray 79*e7f758faSWilliam Breathitt Gray idio_16_set(idio16gpio->reg, &idio16gpio->state, offset, value); 8002e74fc0SWilliam Breathitt Gray } 8102e74fc0SWilliam Breathitt Gray 8202e74fc0SWilliam Breathitt Gray static void idio_16_gpio_set_multiple(struct gpio_chip *chip, 8302e74fc0SWilliam Breathitt Gray unsigned long *mask, unsigned long *bits) 8402e74fc0SWilliam Breathitt Gray { 8502e74fc0SWilliam Breathitt Gray struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip); 862dc7c3c1SWilliam Breathitt Gray 87*e7f758faSWilliam Breathitt Gray idio_16_set_multiple(idio16gpio->reg, &idio16gpio->state, mask, bits); 882dc7c3c1SWilliam Breathitt Gray } 8902e74fc0SWilliam Breathitt Gray 9002e74fc0SWilliam Breathitt Gray static void idio_16_irq_ack(struct irq_data *data) 9102e74fc0SWilliam Breathitt Gray { 9202e74fc0SWilliam Breathitt Gray } 9302e74fc0SWilliam Breathitt Gray 9402e74fc0SWilliam Breathitt Gray static void idio_16_irq_mask(struct irq_data *data) 9502e74fc0SWilliam Breathitt Gray { 9602e74fc0SWilliam Breathitt Gray struct gpio_chip *chip = irq_data_get_irq_chip_data(data); 9702e74fc0SWilliam Breathitt Gray struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip); 9802e74fc0SWilliam Breathitt Gray const unsigned long mask = BIT(irqd_to_hwirq(data)); 9902e74fc0SWilliam Breathitt Gray unsigned long flags; 10002e74fc0SWilliam Breathitt Gray 10102e74fc0SWilliam Breathitt Gray idio16gpio->irq_mask &= ~mask; 10202e74fc0SWilliam Breathitt Gray 10302e74fc0SWilliam Breathitt Gray if (!idio16gpio->irq_mask) { 104ea38ce08SJulia Cartwright raw_spin_lock_irqsave(&idio16gpio->lock, flags); 10502e74fc0SWilliam Breathitt Gray 10602e74fc0SWilliam Breathitt Gray iowrite8(0, &idio16gpio->reg->irq_ctl); 10702e74fc0SWilliam Breathitt Gray 108ea38ce08SJulia Cartwright raw_spin_unlock_irqrestore(&idio16gpio->lock, flags); 10902e74fc0SWilliam Breathitt Gray } 11002e74fc0SWilliam Breathitt Gray } 11102e74fc0SWilliam Breathitt Gray 11202e74fc0SWilliam Breathitt Gray static void idio_16_irq_unmask(struct irq_data *data) 11302e74fc0SWilliam Breathitt Gray { 11402e74fc0SWilliam Breathitt Gray struct gpio_chip *chip = irq_data_get_irq_chip_data(data); 11502e74fc0SWilliam Breathitt Gray struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip); 11602e74fc0SWilliam Breathitt Gray const unsigned long mask = BIT(irqd_to_hwirq(data)); 11702e74fc0SWilliam Breathitt Gray const unsigned long prev_irq_mask = idio16gpio->irq_mask; 11802e74fc0SWilliam Breathitt Gray unsigned long flags; 11902e74fc0SWilliam Breathitt Gray 12002e74fc0SWilliam Breathitt Gray idio16gpio->irq_mask |= mask; 12102e74fc0SWilliam Breathitt Gray 12202e74fc0SWilliam Breathitt Gray if (!prev_irq_mask) { 123ea38ce08SJulia Cartwright raw_spin_lock_irqsave(&idio16gpio->lock, flags); 12402e74fc0SWilliam Breathitt Gray 12502e74fc0SWilliam Breathitt Gray ioread8(&idio16gpio->reg->irq_ctl); 12602e74fc0SWilliam Breathitt Gray 127ea38ce08SJulia Cartwright raw_spin_unlock_irqrestore(&idio16gpio->lock, flags); 12802e74fc0SWilliam Breathitt Gray } 12902e74fc0SWilliam Breathitt Gray } 13002e74fc0SWilliam Breathitt Gray 13102e74fc0SWilliam Breathitt Gray static int idio_16_irq_set_type(struct irq_data *data, unsigned int flow_type) 13202e74fc0SWilliam Breathitt Gray { 13302e74fc0SWilliam Breathitt Gray /* The only valid irq types are none and both-edges */ 13402e74fc0SWilliam Breathitt Gray if (flow_type != IRQ_TYPE_NONE && 13502e74fc0SWilliam Breathitt Gray (flow_type & IRQ_TYPE_EDGE_BOTH) != IRQ_TYPE_EDGE_BOTH) 13602e74fc0SWilliam Breathitt Gray return -EINVAL; 13702e74fc0SWilliam Breathitt Gray 13802e74fc0SWilliam Breathitt Gray return 0; 13902e74fc0SWilliam Breathitt Gray } 14002e74fc0SWilliam Breathitt Gray 14102e74fc0SWilliam Breathitt Gray static struct irq_chip idio_16_irqchip = { 14202e74fc0SWilliam Breathitt Gray .name = "pci-idio-16", 14302e74fc0SWilliam Breathitt Gray .irq_ack = idio_16_irq_ack, 14402e74fc0SWilliam Breathitt Gray .irq_mask = idio_16_irq_mask, 14502e74fc0SWilliam Breathitt Gray .irq_unmask = idio_16_irq_unmask, 14602e74fc0SWilliam Breathitt Gray .irq_set_type = idio_16_irq_set_type 14702e74fc0SWilliam Breathitt Gray }; 14802e74fc0SWilliam Breathitt Gray 14902e74fc0SWilliam Breathitt Gray static irqreturn_t idio_16_irq_handler(int irq, void *dev_id) 15002e74fc0SWilliam Breathitt Gray { 15102e74fc0SWilliam Breathitt Gray struct idio_16_gpio *const idio16gpio = dev_id; 15202e74fc0SWilliam Breathitt Gray unsigned int irq_status; 15302e74fc0SWilliam Breathitt Gray struct gpio_chip *const chip = &idio16gpio->chip; 15402e74fc0SWilliam Breathitt Gray int gpio; 15502e74fc0SWilliam Breathitt Gray 156ea38ce08SJulia Cartwright raw_spin_lock(&idio16gpio->lock); 15702e74fc0SWilliam Breathitt Gray 15802e74fc0SWilliam Breathitt Gray irq_status = ioread8(&idio16gpio->reg->irq_status); 15902e74fc0SWilliam Breathitt Gray 160ea38ce08SJulia Cartwright raw_spin_unlock(&idio16gpio->lock); 16102e74fc0SWilliam Breathitt Gray 16202e74fc0SWilliam Breathitt Gray /* Make sure our device generated IRQ */ 16302e74fc0SWilliam Breathitt Gray if (!(irq_status & 0x3) || !(irq_status & 0x4)) 16402e74fc0SWilliam Breathitt Gray return IRQ_NONE; 16502e74fc0SWilliam Breathitt Gray 16602e74fc0SWilliam Breathitt Gray for_each_set_bit(gpio, &idio16gpio->irq_mask, chip->ngpio) 167dbd1c54fSMarc Zyngier generic_handle_domain_irq(chip->irq.domain, gpio); 16802e74fc0SWilliam Breathitt Gray 169ea38ce08SJulia Cartwright raw_spin_lock(&idio16gpio->lock); 17002e74fc0SWilliam Breathitt Gray 17102e74fc0SWilliam Breathitt Gray /* Clear interrupt */ 17202e74fc0SWilliam Breathitt Gray iowrite8(0, &idio16gpio->reg->in0_7); 17302e74fc0SWilliam Breathitt Gray 174ea38ce08SJulia Cartwright raw_spin_unlock(&idio16gpio->lock); 17502e74fc0SWilliam Breathitt Gray 17602e74fc0SWilliam Breathitt Gray return IRQ_HANDLED; 17702e74fc0SWilliam Breathitt Gray } 17802e74fc0SWilliam Breathitt Gray 17902e74fc0SWilliam Breathitt Gray #define IDIO_16_NGPIO 32 18002e74fc0SWilliam Breathitt Gray static const char *idio_16_names[IDIO_16_NGPIO] = { 18102e74fc0SWilliam Breathitt Gray "OUT0", "OUT1", "OUT2", "OUT3", "OUT4", "OUT5", "OUT6", "OUT7", 18202e74fc0SWilliam Breathitt Gray "OUT8", "OUT9", "OUT10", "OUT11", "OUT12", "OUT13", "OUT14", "OUT15", 18302e74fc0SWilliam Breathitt Gray "IIN0", "IIN1", "IIN2", "IIN3", "IIN4", "IIN5", "IIN6", "IIN7", 18402e74fc0SWilliam Breathitt Gray "IIN8", "IIN9", "IIN10", "IIN11", "IIN12", "IIN13", "IIN14", "IIN15" 18502e74fc0SWilliam Breathitt Gray }; 18602e74fc0SWilliam Breathitt Gray 1874530a840SLinus Walleij static int idio_16_irq_init_hw(struct gpio_chip *gc) 1884530a840SLinus Walleij { 1894530a840SLinus Walleij struct idio_16_gpio *const idio16gpio = gpiochip_get_data(gc); 1904530a840SLinus Walleij 1914530a840SLinus Walleij /* Disable IRQ by default and clear any pending interrupt */ 1924530a840SLinus Walleij iowrite8(0, &idio16gpio->reg->irq_ctl); 1934530a840SLinus Walleij iowrite8(0, &idio16gpio->reg->in0_7); 1944530a840SLinus Walleij 1954530a840SLinus Walleij return 0; 1964530a840SLinus Walleij } 1974530a840SLinus Walleij 19802e74fc0SWilliam Breathitt Gray static int idio_16_probe(struct pci_dev *pdev, const struct pci_device_id *id) 19902e74fc0SWilliam Breathitt Gray { 20002e74fc0SWilliam Breathitt Gray struct device *const dev = &pdev->dev; 20102e74fc0SWilliam Breathitt Gray struct idio_16_gpio *idio16gpio; 20202e74fc0SWilliam Breathitt Gray int err; 203deab2b05SWilliam Breathitt Gray const size_t pci_bar_index = 2; 20402e74fc0SWilliam Breathitt Gray const char *const name = pci_name(pdev); 2054530a840SLinus Walleij struct gpio_irq_chip *girq; 20602e74fc0SWilliam Breathitt Gray 20702e74fc0SWilliam Breathitt Gray idio16gpio = devm_kzalloc(dev, sizeof(*idio16gpio), GFP_KERNEL); 20802e74fc0SWilliam Breathitt Gray if (!idio16gpio) 20902e74fc0SWilliam Breathitt Gray return -ENOMEM; 21002e74fc0SWilliam Breathitt Gray 21102e74fc0SWilliam Breathitt Gray err = pcim_enable_device(pdev); 21202e74fc0SWilliam Breathitt Gray if (err) { 21302e74fc0SWilliam Breathitt Gray dev_err(dev, "Failed to enable PCI device (%d)\n", err); 21402e74fc0SWilliam Breathitt Gray return err; 21502e74fc0SWilliam Breathitt Gray } 21602e74fc0SWilliam Breathitt Gray 217deab2b05SWilliam Breathitt Gray err = pcim_iomap_regions(pdev, BIT(pci_bar_index), name); 21802e74fc0SWilliam Breathitt Gray if (err) { 21902e74fc0SWilliam Breathitt Gray dev_err(dev, "Unable to map PCI I/O addresses (%d)\n", err); 22002e74fc0SWilliam Breathitt Gray return err; 22102e74fc0SWilliam Breathitt Gray } 22202e74fc0SWilliam Breathitt Gray 223deab2b05SWilliam Breathitt Gray idio16gpio->reg = pcim_iomap_table(pdev)[pci_bar_index]; 22402e74fc0SWilliam Breathitt Gray 22502e74fc0SWilliam Breathitt Gray /* Deactivate input filters */ 22602e74fc0SWilliam Breathitt Gray iowrite8(0, &idio16gpio->reg->filter_ctl); 22702e74fc0SWilliam Breathitt Gray 22802e74fc0SWilliam Breathitt Gray idio16gpio->chip.label = name; 22902e74fc0SWilliam Breathitt Gray idio16gpio->chip.parent = dev; 23002e74fc0SWilliam Breathitt Gray idio16gpio->chip.owner = THIS_MODULE; 23102e74fc0SWilliam Breathitt Gray idio16gpio->chip.base = -1; 23202e74fc0SWilliam Breathitt Gray idio16gpio->chip.ngpio = IDIO_16_NGPIO; 23302e74fc0SWilliam Breathitt Gray idio16gpio->chip.names = idio_16_names; 23402e74fc0SWilliam Breathitt Gray idio16gpio->chip.get_direction = idio_16_gpio_get_direction; 23502e74fc0SWilliam Breathitt Gray idio16gpio->chip.direction_input = idio_16_gpio_direction_input; 23602e74fc0SWilliam Breathitt Gray idio16gpio->chip.direction_output = idio_16_gpio_direction_output; 23702e74fc0SWilliam Breathitt Gray idio16gpio->chip.get = idio_16_gpio_get; 238810ebfc5SWilliam Breathitt Gray idio16gpio->chip.get_multiple = idio_16_gpio_get_multiple; 23902e74fc0SWilliam Breathitt Gray idio16gpio->chip.set = idio_16_gpio_set; 24002e74fc0SWilliam Breathitt Gray idio16gpio->chip.set_multiple = idio_16_gpio_set_multiple; 24102e74fc0SWilliam Breathitt Gray 242*e7f758faSWilliam Breathitt Gray idio_16_state_init(&idio16gpio->state); 243*e7f758faSWilliam Breathitt Gray 2444530a840SLinus Walleij girq = &idio16gpio->chip.irq; 2454530a840SLinus Walleij girq->chip = &idio_16_irqchip; 2464530a840SLinus Walleij /* This will let us handle the parent IRQ in the driver */ 2474530a840SLinus Walleij girq->parent_handler = NULL; 2484530a840SLinus Walleij girq->num_parents = 0; 2494530a840SLinus Walleij girq->parents = NULL; 2504530a840SLinus Walleij girq->default_type = IRQ_TYPE_NONE; 2514530a840SLinus Walleij girq->handler = handle_edge_irq; 2524530a840SLinus Walleij girq->init_hw = idio_16_irq_init_hw; 2534530a840SLinus Walleij 254ea38ce08SJulia Cartwright raw_spin_lock_init(&idio16gpio->lock); 25502e74fc0SWilliam Breathitt Gray 25602e74fc0SWilliam Breathitt Gray err = devm_gpiochip_add_data(dev, &idio16gpio->chip, idio16gpio); 25702e74fc0SWilliam Breathitt Gray if (err) { 25802e74fc0SWilliam Breathitt Gray dev_err(dev, "GPIO registering failed (%d)\n", err); 25902e74fc0SWilliam Breathitt Gray return err; 26002e74fc0SWilliam Breathitt Gray } 26102e74fc0SWilliam Breathitt Gray 26202e74fc0SWilliam Breathitt Gray err = devm_request_irq(dev, pdev->irq, idio_16_irq_handler, IRQF_SHARED, 26302e74fc0SWilliam Breathitt Gray name, idio16gpio); 26402e74fc0SWilliam Breathitt Gray if (err) { 26502e74fc0SWilliam Breathitt Gray dev_err(dev, "IRQ handler registering failed (%d)\n", err); 26602e74fc0SWilliam Breathitt Gray return err; 26702e74fc0SWilliam Breathitt Gray } 26802e74fc0SWilliam Breathitt Gray 26902e74fc0SWilliam Breathitt Gray return 0; 27002e74fc0SWilliam Breathitt Gray } 27102e74fc0SWilliam Breathitt Gray 27202e74fc0SWilliam Breathitt Gray static const struct pci_device_id idio_16_pci_dev_id[] = { 273fd254a23SWilliam Breathitt Gray { PCI_DEVICE(0x494F, 0x0DC8) }, { 0 } 27402e74fc0SWilliam Breathitt Gray }; 27502e74fc0SWilliam Breathitt Gray MODULE_DEVICE_TABLE(pci, idio_16_pci_dev_id); 27602e74fc0SWilliam Breathitt Gray 27702e74fc0SWilliam Breathitt Gray static struct pci_driver idio_16_driver = { 27802e74fc0SWilliam Breathitt Gray .name = "pci-idio-16", 27902e74fc0SWilliam Breathitt Gray .id_table = idio_16_pci_dev_id, 28002e74fc0SWilliam Breathitt Gray .probe = idio_16_probe 28102e74fc0SWilliam Breathitt Gray }; 28202e74fc0SWilliam Breathitt Gray 28302e74fc0SWilliam Breathitt Gray module_pci_driver(idio_16_driver); 28402e74fc0SWilliam Breathitt Gray 28502e74fc0SWilliam Breathitt Gray MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); 28602e74fc0SWilliam Breathitt Gray MODULE_DESCRIPTION("ACCES PCI-IDIO-16 GPIO driver"); 28702e74fc0SWilliam Breathitt Gray MODULE_LICENSE("GPL v2"); 288*e7f758faSWilliam Breathitt Gray MODULE_IMPORT_NS(GPIO_IDIO_16); 289