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