xref: /openbmc/linux/drivers/gpio/gpio-pcie-idio-24.c (revision 10a2f11d3c9e48363c729419e0f0530dea76e4fe)
158556204SWilliam Breathitt Gray // SPDX-License-Identifier: GPL-2.0-only
258556204SWilliam Breathitt Gray /*
358556204SWilliam Breathitt Gray  * GPIO driver for the ACCES PCIe-IDIO-24 family
458556204SWilliam Breathitt Gray  * Copyright (C) 2018 William Breathitt Gray
558556204SWilliam Breathitt Gray  *
658556204SWilliam Breathitt Gray  * This program is free software; you can redistribute it and/or modify
758556204SWilliam Breathitt Gray  * it under the terms of the GNU General Public License, version 2, as
858556204SWilliam Breathitt Gray  * published by the Free Software Foundation.
958556204SWilliam Breathitt Gray  *
1058556204SWilliam Breathitt Gray  * This program is distributed in the hope that it will be useful, but
1158556204SWilliam Breathitt Gray  * WITHOUT ANY WARRANTY; without even the implied warranty of
1258556204SWilliam Breathitt Gray  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
1358556204SWilliam Breathitt Gray  * General Public License for more details.
1458556204SWilliam Breathitt Gray  *
1558556204SWilliam Breathitt Gray  * This driver supports the following ACCES devices: PCIe-IDIO-24,
1658556204SWilliam Breathitt Gray  * PCIe-IDI-24, PCIe-IDO-24, and PCIe-IDIO-12.
1758556204SWilliam Breathitt Gray  */
18ca370815SWilliam Breathitt Gray #include <linux/bitmap.h>
1958556204SWilliam Breathitt Gray #include <linux/bitops.h>
2058556204SWilliam Breathitt Gray #include <linux/device.h>
2158556204SWilliam Breathitt Gray #include <linux/errno.h>
2258556204SWilliam Breathitt Gray #include <linux/gpio/driver.h>
2358556204SWilliam Breathitt Gray #include <linux/interrupt.h>
2458556204SWilliam Breathitt Gray #include <linux/irqdesc.h>
2558556204SWilliam Breathitt Gray #include <linux/kernel.h>
2658556204SWilliam Breathitt Gray #include <linux/module.h>
2758556204SWilliam Breathitt Gray #include <linux/pci.h>
2858556204SWilliam Breathitt Gray #include <linux/spinlock.h>
2958556204SWilliam Breathitt Gray #include <linux/types.h>
3058556204SWilliam Breathitt Gray 
31*10a2f11dSArnaud de Turckheim /*
32*10a2f11dSArnaud de Turckheim  * PLX PEX8311 PCI LCS_INTCSR Interrupt Control/Status
33*10a2f11dSArnaud de Turckheim  *
34*10a2f11dSArnaud de Turckheim  * Bit: Description
35*10a2f11dSArnaud de Turckheim  *   0: Enable Interrupt Sources (Bit 0)
36*10a2f11dSArnaud de Turckheim  *   1: Enable Interrupt Sources (Bit 1)
37*10a2f11dSArnaud de Turckheim  *   2: Generate Internal PCI Bus Internal SERR# Interrupt
38*10a2f11dSArnaud de Turckheim  *   3: Mailbox Interrupt Enable
39*10a2f11dSArnaud de Turckheim  *   4: Power Management Interrupt Enable
40*10a2f11dSArnaud de Turckheim  *   5: Power Management Interrupt
41*10a2f11dSArnaud de Turckheim  *   6: Slave Read Local Data Parity Check Error Enable
42*10a2f11dSArnaud de Turckheim  *   7: Slave Read Local Data Parity Check Error Status
43*10a2f11dSArnaud de Turckheim  *   8: Internal PCI Wire Interrupt Enable
44*10a2f11dSArnaud de Turckheim  *   9: PCI Express Doorbell Interrupt Enable
45*10a2f11dSArnaud de Turckheim  *  10: PCI Abort Interrupt Enable
46*10a2f11dSArnaud de Turckheim  *  11: Local Interrupt Input Enable
47*10a2f11dSArnaud de Turckheim  *  12: Retry Abort Enable
48*10a2f11dSArnaud de Turckheim  *  13: PCI Express Doorbell Interrupt Active
49*10a2f11dSArnaud de Turckheim  *  14: PCI Abort Interrupt Active
50*10a2f11dSArnaud de Turckheim  *  15: Local Interrupt Input Active
51*10a2f11dSArnaud de Turckheim  *  16: Local Interrupt Output Enable
52*10a2f11dSArnaud de Turckheim  *  17: Local Doorbell Interrupt Enable
53*10a2f11dSArnaud de Turckheim  *  18: DMA Channel 0 Interrupt Enable
54*10a2f11dSArnaud de Turckheim  *  19: DMA Channel 1 Interrupt Enable
55*10a2f11dSArnaud de Turckheim  *  20: Local Doorbell Interrupt Active
56*10a2f11dSArnaud de Turckheim  *  21: DMA Channel 0 Interrupt Active
57*10a2f11dSArnaud de Turckheim  *  22: DMA Channel 1 Interrupt Active
58*10a2f11dSArnaud de Turckheim  *  23: Built-In Self-Test (BIST) Interrupt Active
59*10a2f11dSArnaud de Turckheim  *  24: Direct Master was the Bus Master during a Master or Target Abort
60*10a2f11dSArnaud de Turckheim  *  25: DMA Channel 0 was the Bus Master during a Master or Target Abort
61*10a2f11dSArnaud de Turckheim  *  26: DMA Channel 1 was the Bus Master during a Master or Target Abort
62*10a2f11dSArnaud de Turckheim  *  27: Target Abort after internal 256 consecutive Master Retrys
63*10a2f11dSArnaud de Turckheim  *  28: PCI Bus wrote data to LCS_MBOX0
64*10a2f11dSArnaud de Turckheim  *  29: PCI Bus wrote data to LCS_MBOX1
65*10a2f11dSArnaud de Turckheim  *  30: PCI Bus wrote data to LCS_MBOX2
66*10a2f11dSArnaud de Turckheim  *  31: PCI Bus wrote data to LCS_MBOX3
67*10a2f11dSArnaud de Turckheim  */
68*10a2f11dSArnaud de Turckheim #define PLX_PEX8311_PCI_LCS_INTCSR  0x68
69*10a2f11dSArnaud de Turckheim #define INTCSR_INTERNAL_PCI_WIRE    BIT(8)
70*10a2f11dSArnaud de Turckheim #define INTCSR_LOCAL_INPUT          BIT(11)
71*10a2f11dSArnaud de Turckheim 
7258556204SWilliam Breathitt Gray /**
7358556204SWilliam Breathitt Gray  * struct idio_24_gpio_reg - GPIO device registers structure
7458556204SWilliam Breathitt Gray  * @out0_7:	Read: FET Outputs 0-7
7558556204SWilliam Breathitt Gray  *		Write: FET Outputs 0-7
7658556204SWilliam Breathitt Gray  * @out8_15:	Read: FET Outputs 8-15
7758556204SWilliam Breathitt Gray  *		Write: FET Outputs 8-15
7858556204SWilliam Breathitt Gray  * @out16_23:	Read: FET Outputs 16-23
7958556204SWilliam Breathitt Gray  *		Write: FET Outputs 16-23
8058556204SWilliam Breathitt Gray  * @ttl_out0_7:	Read: TTL/CMOS Outputs 0-7
8158556204SWilliam Breathitt Gray  *		Write: TTL/CMOS Outputs 0-7
8258556204SWilliam Breathitt Gray  * @in0_7:	Read: Isolated Inputs 0-7
8358556204SWilliam Breathitt Gray  *		Write: Reserved
8458556204SWilliam Breathitt Gray  * @in8_15:	Read: Isolated Inputs 8-15
8558556204SWilliam Breathitt Gray  *		Write: Reserved
8658556204SWilliam Breathitt Gray  * @in16_23:	Read: Isolated Inputs 16-23
8758556204SWilliam Breathitt Gray  *		Write: Reserved
8858556204SWilliam Breathitt Gray  * @ttl_in0_7:	Read: TTL/CMOS Inputs 0-7
8958556204SWilliam Breathitt Gray  *		Write: Reserved
9058556204SWilliam Breathitt Gray  * @cos0_7:	Read: COS Status Inputs 0-7
9158556204SWilliam Breathitt Gray  *		Write: COS Clear Inputs 0-7
9258556204SWilliam Breathitt Gray  * @cos8_15:	Read: COS Status Inputs 8-15
9358556204SWilliam Breathitt Gray  *		Write: COS Clear Inputs 8-15
9458556204SWilliam Breathitt Gray  * @cos16_23:	Read: COS Status Inputs 16-23
9558556204SWilliam Breathitt Gray  *		Write: COS Clear Inputs 16-23
9658556204SWilliam Breathitt Gray  * @cos_ttl0_7:	Read: COS Status TTL/CMOS 0-7
9758556204SWilliam Breathitt Gray  *		Write: COS Clear TTL/CMOS 0-7
9858556204SWilliam Breathitt Gray  * @ctl:	Read: Control Register
9958556204SWilliam Breathitt Gray  *		Write: Control Register
10058556204SWilliam Breathitt Gray  * @reserved:	Read: Reserved
10158556204SWilliam Breathitt Gray  *		Write: Reserved
10258556204SWilliam Breathitt Gray  * @cos_enable:	Read: COS Enable
10358556204SWilliam Breathitt Gray  *		Write: COS Enable
10458556204SWilliam Breathitt Gray  * @soft_reset:	Read: IRQ Output Pin Status
10558556204SWilliam Breathitt Gray  *		Write: Software Board Reset
10658556204SWilliam Breathitt Gray  */
10758556204SWilliam Breathitt Gray struct idio_24_gpio_reg {
10858556204SWilliam Breathitt Gray 	u8 out0_7;
10958556204SWilliam Breathitt Gray 	u8 out8_15;
11058556204SWilliam Breathitt Gray 	u8 out16_23;
11158556204SWilliam Breathitt Gray 	u8 ttl_out0_7;
11258556204SWilliam Breathitt Gray 	u8 in0_7;
11358556204SWilliam Breathitt Gray 	u8 in8_15;
11458556204SWilliam Breathitt Gray 	u8 in16_23;
11558556204SWilliam Breathitt Gray 	u8 ttl_in0_7;
11658556204SWilliam Breathitt Gray 	u8 cos0_7;
11758556204SWilliam Breathitt Gray 	u8 cos8_15;
11858556204SWilliam Breathitt Gray 	u8 cos16_23;
11958556204SWilliam Breathitt Gray 	u8 cos_ttl0_7;
12058556204SWilliam Breathitt Gray 	u8 ctl;
12158556204SWilliam Breathitt Gray 	u8 reserved;
12258556204SWilliam Breathitt Gray 	u8 cos_enable;
12358556204SWilliam Breathitt Gray 	u8 soft_reset;
12458556204SWilliam Breathitt Gray };
12558556204SWilliam Breathitt Gray 
12658556204SWilliam Breathitt Gray /**
12758556204SWilliam Breathitt Gray  * struct idio_24_gpio - GPIO device private data structure
12858556204SWilliam Breathitt Gray  * @chip:	instance of the gpio_chip
12958556204SWilliam Breathitt Gray  * @lock:	synchronization lock to prevent I/O race conditions
13058556204SWilliam Breathitt Gray  * @reg:	I/O address offset for the GPIO device registers
13158556204SWilliam Breathitt Gray  * @irq_mask:	I/O bits affected by interrupts
13258556204SWilliam Breathitt Gray  */
13358556204SWilliam Breathitt Gray struct idio_24_gpio {
13458556204SWilliam Breathitt Gray 	struct gpio_chip chip;
13558556204SWilliam Breathitt Gray 	raw_spinlock_t lock;
136*10a2f11dSArnaud de Turckheim 	__u8 __iomem *plx;
13758556204SWilliam Breathitt Gray 	struct idio_24_gpio_reg __iomem *reg;
13858556204SWilliam Breathitt Gray 	unsigned long irq_mask;
13958556204SWilliam Breathitt Gray };
14058556204SWilliam Breathitt Gray 
14158556204SWilliam Breathitt Gray static int idio_24_gpio_get_direction(struct gpio_chip *chip,
14258556204SWilliam Breathitt Gray 	unsigned int offset)
14358556204SWilliam Breathitt Gray {
14458556204SWilliam Breathitt Gray 	struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
14558556204SWilliam Breathitt Gray 	const unsigned long out_mode_mask = BIT(1);
14658556204SWilliam Breathitt Gray 
14758556204SWilliam Breathitt Gray 	/* FET Outputs */
14858556204SWilliam Breathitt Gray 	if (offset < 24)
149e42615ecSMatti Vaittinen 		return GPIO_LINE_DIRECTION_OUT;
15058556204SWilliam Breathitt Gray 
15158556204SWilliam Breathitt Gray 	/* Isolated Inputs */
15258556204SWilliam Breathitt Gray 	if (offset < 48)
153e42615ecSMatti Vaittinen 		return GPIO_LINE_DIRECTION_IN;
15458556204SWilliam Breathitt Gray 
15558556204SWilliam Breathitt Gray 	/* TTL/CMOS I/O */
15658556204SWilliam Breathitt Gray 	/* OUT MODE = 1 when TTL/CMOS Output Mode is set */
157e42615ecSMatti Vaittinen 	if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask)
158e42615ecSMatti Vaittinen 		return GPIO_LINE_DIRECTION_OUT;
159e42615ecSMatti Vaittinen 
160e42615ecSMatti Vaittinen 	return GPIO_LINE_DIRECTION_IN;
16158556204SWilliam Breathitt Gray }
16258556204SWilliam Breathitt Gray 
16358556204SWilliam Breathitt Gray static int idio_24_gpio_direction_input(struct gpio_chip *chip,
16458556204SWilliam Breathitt Gray 	unsigned int offset)
16558556204SWilliam Breathitt Gray {
16658556204SWilliam Breathitt Gray 	struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
16758556204SWilliam Breathitt Gray 	unsigned long flags;
16858556204SWilliam Breathitt Gray 	unsigned int ctl_state;
16958556204SWilliam Breathitt Gray 	const unsigned long out_mode_mask = BIT(1);
17058556204SWilliam Breathitt Gray 
17158556204SWilliam Breathitt Gray 	/* TTL/CMOS I/O */
17258556204SWilliam Breathitt Gray 	if (offset > 47) {
17358556204SWilliam Breathitt Gray 		raw_spin_lock_irqsave(&idio24gpio->lock, flags);
17458556204SWilliam Breathitt Gray 
17558556204SWilliam Breathitt Gray 		/* Clear TTL/CMOS Output Mode */
17658556204SWilliam Breathitt Gray 		ctl_state = ioread8(&idio24gpio->reg->ctl) & ~out_mode_mask;
17758556204SWilliam Breathitt Gray 		iowrite8(ctl_state, &idio24gpio->reg->ctl);
17858556204SWilliam Breathitt Gray 
17958556204SWilliam Breathitt Gray 		raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
18058556204SWilliam Breathitt Gray 	}
18158556204SWilliam Breathitt Gray 
18258556204SWilliam Breathitt Gray 	return 0;
18358556204SWilliam Breathitt Gray }
18458556204SWilliam Breathitt Gray 
18558556204SWilliam Breathitt Gray static int idio_24_gpio_direction_output(struct gpio_chip *chip,
18658556204SWilliam Breathitt Gray 	unsigned int offset, int value)
18758556204SWilliam Breathitt Gray {
18858556204SWilliam Breathitt Gray 	struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
18958556204SWilliam Breathitt Gray 	unsigned long flags;
19058556204SWilliam Breathitt Gray 	unsigned int ctl_state;
19158556204SWilliam Breathitt Gray 	const unsigned long out_mode_mask = BIT(1);
19258556204SWilliam Breathitt Gray 
19358556204SWilliam Breathitt Gray 	/* TTL/CMOS I/O */
19458556204SWilliam Breathitt Gray 	if (offset > 47) {
19558556204SWilliam Breathitt Gray 		raw_spin_lock_irqsave(&idio24gpio->lock, flags);
19658556204SWilliam Breathitt Gray 
19758556204SWilliam Breathitt Gray 		/* Set TTL/CMOS Output Mode */
19858556204SWilliam Breathitt Gray 		ctl_state = ioread8(&idio24gpio->reg->ctl) | out_mode_mask;
19958556204SWilliam Breathitt Gray 		iowrite8(ctl_state, &idio24gpio->reg->ctl);
20058556204SWilliam Breathitt Gray 
20158556204SWilliam Breathitt Gray 		raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
20258556204SWilliam Breathitt Gray 	}
20358556204SWilliam Breathitt Gray 
20458556204SWilliam Breathitt Gray 	chip->set(chip, offset, value);
20558556204SWilliam Breathitt Gray 	return 0;
20658556204SWilliam Breathitt Gray }
20758556204SWilliam Breathitt Gray 
20858556204SWilliam Breathitt Gray static int idio_24_gpio_get(struct gpio_chip *chip, unsigned int offset)
20958556204SWilliam Breathitt Gray {
21058556204SWilliam Breathitt Gray 	struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
21158556204SWilliam Breathitt Gray 	const unsigned long offset_mask = BIT(offset % 8);
21258556204SWilliam Breathitt Gray 	const unsigned long out_mode_mask = BIT(1);
21358556204SWilliam Breathitt Gray 
21458556204SWilliam Breathitt Gray 	/* FET Outputs */
21558556204SWilliam Breathitt Gray 	if (offset < 8)
21658556204SWilliam Breathitt Gray 		return !!(ioread8(&idio24gpio->reg->out0_7) & offset_mask);
21758556204SWilliam Breathitt Gray 
21858556204SWilliam Breathitt Gray 	if (offset < 16)
21958556204SWilliam Breathitt Gray 		return !!(ioread8(&idio24gpio->reg->out8_15) & offset_mask);
22058556204SWilliam Breathitt Gray 
22158556204SWilliam Breathitt Gray 	if (offset < 24)
22258556204SWilliam Breathitt Gray 		return !!(ioread8(&idio24gpio->reg->out16_23) & offset_mask);
22358556204SWilliam Breathitt Gray 
22458556204SWilliam Breathitt Gray 	/* Isolated Inputs */
22558556204SWilliam Breathitt Gray 	if (offset < 32)
22658556204SWilliam Breathitt Gray 		return !!(ioread8(&idio24gpio->reg->in0_7) & offset_mask);
22758556204SWilliam Breathitt Gray 
22858556204SWilliam Breathitt Gray 	if (offset < 40)
22958556204SWilliam Breathitt Gray 		return !!(ioread8(&idio24gpio->reg->in8_15) & offset_mask);
23058556204SWilliam Breathitt Gray 
23158556204SWilliam Breathitt Gray 	if (offset < 48)
23258556204SWilliam Breathitt Gray 		return !!(ioread8(&idio24gpio->reg->in16_23) & offset_mask);
23358556204SWilliam Breathitt Gray 
23458556204SWilliam Breathitt Gray 	/* TTL/CMOS Outputs */
23558556204SWilliam Breathitt Gray 	if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask)
23658556204SWilliam Breathitt Gray 		return !!(ioread8(&idio24gpio->reg->ttl_out0_7) & offset_mask);
23758556204SWilliam Breathitt Gray 
23858556204SWilliam Breathitt Gray 	/* TTL/CMOS Inputs */
23958556204SWilliam Breathitt Gray 	return !!(ioread8(&idio24gpio->reg->ttl_in0_7) & offset_mask);
24058556204SWilliam Breathitt Gray }
24158556204SWilliam Breathitt Gray 
242ca370815SWilliam Breathitt Gray static int idio_24_gpio_get_multiple(struct gpio_chip *chip,
243ca370815SWilliam Breathitt Gray 	unsigned long *mask, unsigned long *bits)
244ca370815SWilliam Breathitt Gray {
245ca370815SWilliam Breathitt Gray 	struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
246c586aa8fSWilliam Breathitt Gray 	unsigned long offset;
247c586aa8fSWilliam Breathitt Gray 	unsigned long gpio_mask;
248304440aaSWilliam Breathitt Gray 	void __iomem *ports[] = {
249304440aaSWilliam Breathitt Gray 		&idio24gpio->reg->out0_7, &idio24gpio->reg->out8_15,
250304440aaSWilliam Breathitt Gray 		&idio24gpio->reg->out16_23, &idio24gpio->reg->in0_7,
251304440aaSWilliam Breathitt Gray 		&idio24gpio->reg->in8_15, &idio24gpio->reg->in16_23,
252ca370815SWilliam Breathitt Gray 	};
253c586aa8fSWilliam Breathitt Gray 	size_t index;
254c586aa8fSWilliam Breathitt Gray 	unsigned long port_state;
255ca370815SWilliam Breathitt Gray 	const unsigned long out_mode_mask = BIT(1);
256ca370815SWilliam Breathitt Gray 
257ca370815SWilliam Breathitt Gray 	/* clear bits array to a clean slate */
258ca370815SWilliam Breathitt Gray 	bitmap_zero(bits, chip->ngpio);
259ca370815SWilliam Breathitt Gray 
260c586aa8fSWilliam Breathitt Gray 	for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) {
261c586aa8fSWilliam Breathitt Gray 		index = offset / 8;
262ca370815SWilliam Breathitt Gray 
263ca370815SWilliam Breathitt Gray 		/* read bits from current gpio port (port 6 is TTL GPIO) */
264c586aa8fSWilliam Breathitt Gray 		if (index < 6)
265c586aa8fSWilliam Breathitt Gray 			port_state = ioread8(ports[index]);
266ca370815SWilliam Breathitt Gray 		else if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask)
267ca370815SWilliam Breathitt Gray 			port_state = ioread8(&idio24gpio->reg->ttl_out0_7);
268ca370815SWilliam Breathitt Gray 		else
269ca370815SWilliam Breathitt Gray 			port_state = ioread8(&idio24gpio->reg->ttl_in0_7);
270ca370815SWilliam Breathitt Gray 
271c586aa8fSWilliam Breathitt Gray 		port_state &= gpio_mask;
272c586aa8fSWilliam Breathitt Gray 
273c586aa8fSWilliam Breathitt Gray 		bitmap_set_value8(bits, port_state, offset);
274ca370815SWilliam Breathitt Gray 	}
275ca370815SWilliam Breathitt Gray 
276ca370815SWilliam Breathitt Gray 	return 0;
277ca370815SWilliam Breathitt Gray }
278ca370815SWilliam Breathitt Gray 
27958556204SWilliam Breathitt Gray static void idio_24_gpio_set(struct gpio_chip *chip, unsigned int offset,
28058556204SWilliam Breathitt Gray 	int value)
28158556204SWilliam Breathitt Gray {
28258556204SWilliam Breathitt Gray 	struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
28358556204SWilliam Breathitt Gray 	const unsigned long out_mode_mask = BIT(1);
28458556204SWilliam Breathitt Gray 	void __iomem *base;
28558556204SWilliam Breathitt Gray 	const unsigned int mask = BIT(offset % 8);
28658556204SWilliam Breathitt Gray 	unsigned long flags;
28758556204SWilliam Breathitt Gray 	unsigned int out_state;
28858556204SWilliam Breathitt Gray 
28958556204SWilliam Breathitt Gray 	/* Isolated Inputs */
29058556204SWilliam Breathitt Gray 	if (offset > 23 && offset < 48)
29158556204SWilliam Breathitt Gray 		return;
29258556204SWilliam Breathitt Gray 
29358556204SWilliam Breathitt Gray 	/* TTL/CMOS Inputs */
29458556204SWilliam Breathitt Gray 	if (offset > 47 && !(ioread8(&idio24gpio->reg->ctl) & out_mode_mask))
29558556204SWilliam Breathitt Gray 		return;
29658556204SWilliam Breathitt Gray 
29758556204SWilliam Breathitt Gray 	/* TTL/CMOS Outputs */
29858556204SWilliam Breathitt Gray 	if (offset > 47)
29958556204SWilliam Breathitt Gray 		base = &idio24gpio->reg->ttl_out0_7;
30058556204SWilliam Breathitt Gray 	/* FET Outputs */
30158556204SWilliam Breathitt Gray 	else if (offset > 15)
30258556204SWilliam Breathitt Gray 		base = &idio24gpio->reg->out16_23;
30358556204SWilliam Breathitt Gray 	else if (offset > 7)
30458556204SWilliam Breathitt Gray 		base = &idio24gpio->reg->out8_15;
30558556204SWilliam Breathitt Gray 	else
30658556204SWilliam Breathitt Gray 		base = &idio24gpio->reg->out0_7;
30758556204SWilliam Breathitt Gray 
30858556204SWilliam Breathitt Gray 	raw_spin_lock_irqsave(&idio24gpio->lock, flags);
30958556204SWilliam Breathitt Gray 
31058556204SWilliam Breathitt Gray 	if (value)
31158556204SWilliam Breathitt Gray 		out_state = ioread8(base) | mask;
31258556204SWilliam Breathitt Gray 	else
31358556204SWilliam Breathitt Gray 		out_state = ioread8(base) & ~mask;
31458556204SWilliam Breathitt Gray 
31558556204SWilliam Breathitt Gray 	iowrite8(out_state, base);
31658556204SWilliam Breathitt Gray 
31758556204SWilliam Breathitt Gray 	raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
31858556204SWilliam Breathitt Gray }
31958556204SWilliam Breathitt Gray 
320ca370815SWilliam Breathitt Gray static void idio_24_gpio_set_multiple(struct gpio_chip *chip,
321ca370815SWilliam Breathitt Gray 	unsigned long *mask, unsigned long *bits)
322ca370815SWilliam Breathitt Gray {
323ca370815SWilliam Breathitt Gray 	struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
324c586aa8fSWilliam Breathitt Gray 	unsigned long offset;
325ca370815SWilliam Breathitt Gray 	unsigned long gpio_mask;
326304440aaSWilliam Breathitt Gray 	void __iomem *ports[] = {
327304440aaSWilliam Breathitt Gray 		&idio24gpio->reg->out0_7, &idio24gpio->reg->out8_15,
328304440aaSWilliam Breathitt Gray 		&idio24gpio->reg->out16_23
329ca370815SWilliam Breathitt Gray 	};
330c586aa8fSWilliam Breathitt Gray 	size_t index;
331c586aa8fSWilliam Breathitt Gray 	unsigned long bitmask;
332c586aa8fSWilliam Breathitt Gray 	unsigned long flags;
333c586aa8fSWilliam Breathitt Gray 	unsigned long out_state;
334ca370815SWilliam Breathitt Gray 	const unsigned long out_mode_mask = BIT(1);
335ca370815SWilliam Breathitt Gray 
336c586aa8fSWilliam Breathitt Gray 	for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) {
337c586aa8fSWilliam Breathitt Gray 		index = offset / 8;
338ca370815SWilliam Breathitt Gray 
339c586aa8fSWilliam Breathitt Gray 		bitmask = bitmap_get_value8(bits, offset) & gpio_mask;
340c586aa8fSWilliam Breathitt Gray 
341c586aa8fSWilliam Breathitt Gray 		raw_spin_lock_irqsave(&idio24gpio->lock, flags);
342c586aa8fSWilliam Breathitt Gray 
343c586aa8fSWilliam Breathitt Gray 		/* read bits from current gpio port (port 6 is TTL GPIO) */
344c586aa8fSWilliam Breathitt Gray 		if (index < 6) {
345c586aa8fSWilliam Breathitt Gray 			out_state = ioread8(ports[index]);
346c586aa8fSWilliam Breathitt Gray 		} else if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask) {
347c586aa8fSWilliam Breathitt Gray 			out_state = ioread8(&idio24gpio->reg->ttl_out0_7);
348c586aa8fSWilliam Breathitt Gray 		} else {
349c586aa8fSWilliam Breathitt Gray 			/* skip TTL GPIO if set for input */
350c586aa8fSWilliam Breathitt Gray 			raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
351ca370815SWilliam Breathitt Gray 			continue;
352ca370815SWilliam Breathitt Gray 		}
353ca370815SWilliam Breathitt Gray 
354c586aa8fSWilliam Breathitt Gray 		/* set requested bit states */
355c586aa8fSWilliam Breathitt Gray 		out_state &= ~gpio_mask;
356c586aa8fSWilliam Breathitt Gray 		out_state |= bitmask;
357ca370815SWilliam Breathitt Gray 
358c586aa8fSWilliam Breathitt Gray 		/* write bits for current gpio port (port 6 is TTL GPIO) */
359c586aa8fSWilliam Breathitt Gray 		if (index < 6)
360c586aa8fSWilliam Breathitt Gray 			iowrite8(out_state, ports[index]);
361c586aa8fSWilliam Breathitt Gray 		else
362ca370815SWilliam Breathitt Gray 			iowrite8(out_state, &idio24gpio->reg->ttl_out0_7);
363ca370815SWilliam Breathitt Gray 
364ca370815SWilliam Breathitt Gray 		raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
365ca370815SWilliam Breathitt Gray 	}
366c586aa8fSWilliam Breathitt Gray }
367ca370815SWilliam Breathitt Gray 
36858556204SWilliam Breathitt Gray static void idio_24_irq_ack(struct irq_data *data)
36958556204SWilliam Breathitt Gray {
37058556204SWilliam Breathitt Gray }
37158556204SWilliam Breathitt Gray 
37258556204SWilliam Breathitt Gray static void idio_24_irq_mask(struct irq_data *data)
37358556204SWilliam Breathitt Gray {
37458556204SWilliam Breathitt Gray 	struct gpio_chip *const chip = irq_data_get_irq_chip_data(data);
37558556204SWilliam Breathitt Gray 	struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
37658556204SWilliam Breathitt Gray 	unsigned long flags;
37758556204SWilliam Breathitt Gray 	const unsigned long bit_offset = irqd_to_hwirq(data) - 24;
37858556204SWilliam Breathitt Gray 	unsigned char new_irq_mask;
37923a7fdc0SArnaud de Turckheim 	const unsigned long bank_offset = bit_offset / 8;
38058556204SWilliam Breathitt Gray 	unsigned char cos_enable_state;
38158556204SWilliam Breathitt Gray 
38258556204SWilliam Breathitt Gray 	raw_spin_lock_irqsave(&idio24gpio->lock, flags);
38358556204SWilliam Breathitt Gray 
384d8f270efSArnaud de Turckheim 	idio24gpio->irq_mask &= ~BIT(bit_offset);
38523a7fdc0SArnaud de Turckheim 	new_irq_mask = idio24gpio->irq_mask >> bank_offset * 8;
38658556204SWilliam Breathitt Gray 
38758556204SWilliam Breathitt Gray 	if (!new_irq_mask) {
38858556204SWilliam Breathitt Gray 		cos_enable_state = ioread8(&idio24gpio->reg->cos_enable);
38958556204SWilliam Breathitt Gray 
39058556204SWilliam Breathitt Gray 		/* Disable Rising Edge detection */
39158556204SWilliam Breathitt Gray 		cos_enable_state &= ~BIT(bank_offset);
39258556204SWilliam Breathitt Gray 		/* Disable Falling Edge detection */
39358556204SWilliam Breathitt Gray 		cos_enable_state &= ~BIT(bank_offset + 4);
39458556204SWilliam Breathitt Gray 
39558556204SWilliam Breathitt Gray 		iowrite8(cos_enable_state, &idio24gpio->reg->cos_enable);
39658556204SWilliam Breathitt Gray 	}
39758556204SWilliam Breathitt Gray 
39858556204SWilliam Breathitt Gray 	raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
39958556204SWilliam Breathitt Gray }
40058556204SWilliam Breathitt Gray 
40158556204SWilliam Breathitt Gray static void idio_24_irq_unmask(struct irq_data *data)
40258556204SWilliam Breathitt Gray {
40358556204SWilliam Breathitt Gray 	struct gpio_chip *const chip = irq_data_get_irq_chip_data(data);
40458556204SWilliam Breathitt Gray 	struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
40558556204SWilliam Breathitt Gray 	unsigned long flags;
40658556204SWilliam Breathitt Gray 	unsigned char prev_irq_mask;
40758556204SWilliam Breathitt Gray 	const unsigned long bit_offset = irqd_to_hwirq(data) - 24;
40823a7fdc0SArnaud de Turckheim 	const unsigned long bank_offset = bit_offset / 8;
40958556204SWilliam Breathitt Gray 	unsigned char cos_enable_state;
41058556204SWilliam Breathitt Gray 
41158556204SWilliam Breathitt Gray 	raw_spin_lock_irqsave(&idio24gpio->lock, flags);
41258556204SWilliam Breathitt Gray 
41323a7fdc0SArnaud de Turckheim 	prev_irq_mask = idio24gpio->irq_mask >> bank_offset * 8;
41458556204SWilliam Breathitt Gray 	idio24gpio->irq_mask |= BIT(bit_offset);
41558556204SWilliam Breathitt Gray 
41658556204SWilliam Breathitt Gray 	if (!prev_irq_mask) {
41758556204SWilliam Breathitt Gray 		cos_enable_state = ioread8(&idio24gpio->reg->cos_enable);
41858556204SWilliam Breathitt Gray 
41958556204SWilliam Breathitt Gray 		/* Enable Rising Edge detection */
42058556204SWilliam Breathitt Gray 		cos_enable_state |= BIT(bank_offset);
42158556204SWilliam Breathitt Gray 		/* Enable Falling Edge detection */
42258556204SWilliam Breathitt Gray 		cos_enable_state |= BIT(bank_offset + 4);
42358556204SWilliam Breathitt Gray 
42458556204SWilliam Breathitt Gray 		iowrite8(cos_enable_state, &idio24gpio->reg->cos_enable);
42558556204SWilliam Breathitt Gray 	}
42658556204SWilliam Breathitt Gray 
42758556204SWilliam Breathitt Gray 	raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
42858556204SWilliam Breathitt Gray }
42958556204SWilliam Breathitt Gray 
43058556204SWilliam Breathitt Gray static int idio_24_irq_set_type(struct irq_data *data, unsigned int flow_type)
43158556204SWilliam Breathitt Gray {
43258556204SWilliam Breathitt Gray 	/* The only valid irq types are none and both-edges */
43358556204SWilliam Breathitt Gray 	if (flow_type != IRQ_TYPE_NONE &&
43458556204SWilliam Breathitt Gray 		(flow_type & IRQ_TYPE_EDGE_BOTH) != IRQ_TYPE_EDGE_BOTH)
43558556204SWilliam Breathitt Gray 		return -EINVAL;
43658556204SWilliam Breathitt Gray 
43758556204SWilliam Breathitt Gray 	return 0;
43858556204SWilliam Breathitt Gray }
43958556204SWilliam Breathitt Gray 
44058556204SWilliam Breathitt Gray static struct irq_chip idio_24_irqchip = {
44158556204SWilliam Breathitt Gray 	.name = "pcie-idio-24",
44258556204SWilliam Breathitt Gray 	.irq_ack = idio_24_irq_ack,
44358556204SWilliam Breathitt Gray 	.irq_mask = idio_24_irq_mask,
44458556204SWilliam Breathitt Gray 	.irq_unmask = idio_24_irq_unmask,
44558556204SWilliam Breathitt Gray 	.irq_set_type = idio_24_irq_set_type
44658556204SWilliam Breathitt Gray };
44758556204SWilliam Breathitt Gray 
44858556204SWilliam Breathitt Gray static irqreturn_t idio_24_irq_handler(int irq, void *dev_id)
44958556204SWilliam Breathitt Gray {
45058556204SWilliam Breathitt Gray 	struct idio_24_gpio *const idio24gpio = dev_id;
45158556204SWilliam Breathitt Gray 	unsigned long irq_status;
45258556204SWilliam Breathitt Gray 	struct gpio_chip *const chip = &idio24gpio->chip;
45358556204SWilliam Breathitt Gray 	unsigned long irq_mask;
45458556204SWilliam Breathitt Gray 	int gpio;
45558556204SWilliam Breathitt Gray 
45658556204SWilliam Breathitt Gray 	raw_spin_lock(&idio24gpio->lock);
45758556204SWilliam Breathitt Gray 
45858556204SWilliam Breathitt Gray 	/* Read Change-Of-State status */
45958556204SWilliam Breathitt Gray 	irq_status = ioread32(&idio24gpio->reg->cos0_7);
46058556204SWilliam Breathitt Gray 
46158556204SWilliam Breathitt Gray 	raw_spin_unlock(&idio24gpio->lock);
46258556204SWilliam Breathitt Gray 
46358556204SWilliam Breathitt Gray 	/* Make sure our device generated IRQ */
46458556204SWilliam Breathitt Gray 	if (!irq_status)
46558556204SWilliam Breathitt Gray 		return IRQ_NONE;
46658556204SWilliam Breathitt Gray 
46758556204SWilliam Breathitt Gray 	/* Handle only unmasked IRQ */
46858556204SWilliam Breathitt Gray 	irq_mask = idio24gpio->irq_mask & irq_status;
46958556204SWilliam Breathitt Gray 
47058556204SWilliam Breathitt Gray 	for_each_set_bit(gpio, &irq_mask, chip->ngpio - 24)
47158556204SWilliam Breathitt Gray 		generic_handle_irq(irq_find_mapping(chip->irq.domain,
47258556204SWilliam Breathitt Gray 			gpio + 24));
47358556204SWilliam Breathitt Gray 
47458556204SWilliam Breathitt Gray 	raw_spin_lock(&idio24gpio->lock);
47558556204SWilliam Breathitt Gray 
47658556204SWilliam Breathitt Gray 	/* Clear Change-Of-State status */
47758556204SWilliam Breathitt Gray 	iowrite32(irq_status, &idio24gpio->reg->cos0_7);
47858556204SWilliam Breathitt Gray 
47958556204SWilliam Breathitt Gray 	raw_spin_unlock(&idio24gpio->lock);
48058556204SWilliam Breathitt Gray 
48158556204SWilliam Breathitt Gray 	return IRQ_HANDLED;
48258556204SWilliam Breathitt Gray }
48358556204SWilliam Breathitt Gray 
48458556204SWilliam Breathitt Gray #define IDIO_24_NGPIO 56
48558556204SWilliam Breathitt Gray static const char *idio_24_names[IDIO_24_NGPIO] = {
48658556204SWilliam Breathitt Gray 	"OUT0", "OUT1", "OUT2", "OUT3", "OUT4", "OUT5", "OUT6", "OUT7",
48758556204SWilliam Breathitt Gray 	"OUT8", "OUT9", "OUT10", "OUT11", "OUT12", "OUT13", "OUT14", "OUT15",
48858556204SWilliam Breathitt Gray 	"OUT16", "OUT17", "OUT18", "OUT19", "OUT20", "OUT21", "OUT22", "OUT23",
48958556204SWilliam Breathitt Gray 	"IIN0", "IIN1", "IIN2", "IIN3", "IIN4", "IIN5", "IIN6", "IIN7",
49058556204SWilliam Breathitt Gray 	"IIN8", "IIN9", "IIN10", "IIN11", "IIN12", "IIN13", "IIN14", "IIN15",
49158556204SWilliam Breathitt Gray 	"IIN16", "IIN17", "IIN18", "IIN19", "IIN20", "IIN21", "IIN22", "IIN23",
49258556204SWilliam Breathitt Gray 	"TTL0", "TTL1", "TTL2", "TTL3", "TTL4", "TTL5", "TTL6", "TTL7"
49358556204SWilliam Breathitt Gray };
49458556204SWilliam Breathitt Gray 
49558556204SWilliam Breathitt Gray static int idio_24_probe(struct pci_dev *pdev, const struct pci_device_id *id)
49658556204SWilliam Breathitt Gray {
49758556204SWilliam Breathitt Gray 	struct device *const dev = &pdev->dev;
49858556204SWilliam Breathitt Gray 	struct idio_24_gpio *idio24gpio;
49958556204SWilliam Breathitt Gray 	int err;
500*10a2f11dSArnaud de Turckheim 	const size_t pci_plx_bar_index = 1;
50158556204SWilliam Breathitt Gray 	const size_t pci_bar_index = 2;
50258556204SWilliam Breathitt Gray 	const char *const name = pci_name(pdev);
503866e863eSLinus Walleij 	struct gpio_irq_chip *girq;
50458556204SWilliam Breathitt Gray 
50558556204SWilliam Breathitt Gray 	idio24gpio = devm_kzalloc(dev, sizeof(*idio24gpio), GFP_KERNEL);
50658556204SWilliam Breathitt Gray 	if (!idio24gpio)
50758556204SWilliam Breathitt Gray 		return -ENOMEM;
50858556204SWilliam Breathitt Gray 
50958556204SWilliam Breathitt Gray 	err = pcim_enable_device(pdev);
51058556204SWilliam Breathitt Gray 	if (err) {
51158556204SWilliam Breathitt Gray 		dev_err(dev, "Failed to enable PCI device (%d)\n", err);
51258556204SWilliam Breathitt Gray 		return err;
51358556204SWilliam Breathitt Gray 	}
51458556204SWilliam Breathitt Gray 
515*10a2f11dSArnaud de Turckheim 	err = pcim_iomap_regions(pdev, BIT(pci_plx_bar_index) | BIT(pci_bar_index), name);
51658556204SWilliam Breathitt Gray 	if (err) {
51758556204SWilliam Breathitt Gray 		dev_err(dev, "Unable to map PCI I/O addresses (%d)\n", err);
51858556204SWilliam Breathitt Gray 		return err;
51958556204SWilliam Breathitt Gray 	}
52058556204SWilliam Breathitt Gray 
521*10a2f11dSArnaud de Turckheim 	idio24gpio->plx = pcim_iomap_table(pdev)[pci_plx_bar_index];
52258556204SWilliam Breathitt Gray 	idio24gpio->reg = pcim_iomap_table(pdev)[pci_bar_index];
52358556204SWilliam Breathitt Gray 
52458556204SWilliam Breathitt Gray 	idio24gpio->chip.label = name;
52558556204SWilliam Breathitt Gray 	idio24gpio->chip.parent = dev;
52658556204SWilliam Breathitt Gray 	idio24gpio->chip.owner = THIS_MODULE;
52758556204SWilliam Breathitt Gray 	idio24gpio->chip.base = -1;
52858556204SWilliam Breathitt Gray 	idio24gpio->chip.ngpio = IDIO_24_NGPIO;
52958556204SWilliam Breathitt Gray 	idio24gpio->chip.names = idio_24_names;
53058556204SWilliam Breathitt Gray 	idio24gpio->chip.get_direction = idio_24_gpio_get_direction;
53158556204SWilliam Breathitt Gray 	idio24gpio->chip.direction_input = idio_24_gpio_direction_input;
53258556204SWilliam Breathitt Gray 	idio24gpio->chip.direction_output = idio_24_gpio_direction_output;
53358556204SWilliam Breathitt Gray 	idio24gpio->chip.get = idio_24_gpio_get;
534ca370815SWilliam Breathitt Gray 	idio24gpio->chip.get_multiple = idio_24_gpio_get_multiple;
53558556204SWilliam Breathitt Gray 	idio24gpio->chip.set = idio_24_gpio_set;
536ca370815SWilliam Breathitt Gray 	idio24gpio->chip.set_multiple = idio_24_gpio_set_multiple;
53758556204SWilliam Breathitt Gray 
538866e863eSLinus Walleij 	girq = &idio24gpio->chip.irq;
539866e863eSLinus Walleij 	girq->chip = &idio_24_irqchip;
540866e863eSLinus Walleij 	/* This will let us handle the parent IRQ in the driver */
541866e863eSLinus Walleij 	girq->parent_handler = NULL;
542866e863eSLinus Walleij 	girq->num_parents = 0;
543866e863eSLinus Walleij 	girq->parents = NULL;
544866e863eSLinus Walleij 	girq->default_type = IRQ_TYPE_NONE;
545866e863eSLinus Walleij 	girq->handler = handle_edge_irq;
546866e863eSLinus Walleij 
54758556204SWilliam Breathitt Gray 	raw_spin_lock_init(&idio24gpio->lock);
54858556204SWilliam Breathitt Gray 
54958556204SWilliam Breathitt Gray 	/* Software board reset */
55058556204SWilliam Breathitt Gray 	iowrite8(0, &idio24gpio->reg->soft_reset);
551*10a2f11dSArnaud de Turckheim 	/*
552*10a2f11dSArnaud de Turckheim 	 * enable PLX PEX8311 internal PCI wire interrupt and local interrupt
553*10a2f11dSArnaud de Turckheim 	 * input
554*10a2f11dSArnaud de Turckheim 	 */
555*10a2f11dSArnaud de Turckheim 	iowrite8((INTCSR_INTERNAL_PCI_WIRE | INTCSR_LOCAL_INPUT) >> 8,
556*10a2f11dSArnaud de Turckheim 		 idio24gpio->plx + PLX_PEX8311_PCI_LCS_INTCSR + 1);
55758556204SWilliam Breathitt Gray 
55858556204SWilliam Breathitt Gray 	err = devm_gpiochip_add_data(dev, &idio24gpio->chip, idio24gpio);
55958556204SWilliam Breathitt Gray 	if (err) {
56058556204SWilliam Breathitt Gray 		dev_err(dev, "GPIO registering failed (%d)\n", err);
56158556204SWilliam Breathitt Gray 		return err;
56258556204SWilliam Breathitt Gray 	}
56358556204SWilliam Breathitt Gray 
56458556204SWilliam Breathitt Gray 	err = devm_request_irq(dev, pdev->irq, idio_24_irq_handler, IRQF_SHARED,
56558556204SWilliam Breathitt Gray 		name, idio24gpio);
56658556204SWilliam Breathitt Gray 	if (err) {
56758556204SWilliam Breathitt Gray 		dev_err(dev, "IRQ handler registering failed (%d)\n", err);
56858556204SWilliam Breathitt Gray 		return err;
56958556204SWilliam Breathitt Gray 	}
57058556204SWilliam Breathitt Gray 
57158556204SWilliam Breathitt Gray 	return 0;
57258556204SWilliam Breathitt Gray }
57358556204SWilliam Breathitt Gray 
57458556204SWilliam Breathitt Gray static const struct pci_device_id idio_24_pci_dev_id[] = {
57558556204SWilliam Breathitt Gray 	{ PCI_DEVICE(0x494F, 0x0FD0) }, { PCI_DEVICE(0x494F, 0x0BD0) },
57658556204SWilliam Breathitt Gray 	{ PCI_DEVICE(0x494F, 0x07D0) }, { PCI_DEVICE(0x494F, 0x0FC0) },
57758556204SWilliam Breathitt Gray 	{ 0 }
57858556204SWilliam Breathitt Gray };
57958556204SWilliam Breathitt Gray MODULE_DEVICE_TABLE(pci, idio_24_pci_dev_id);
58058556204SWilliam Breathitt Gray 
58158556204SWilliam Breathitt Gray static struct pci_driver idio_24_driver = {
58258556204SWilliam Breathitt Gray 	.name = "pcie-idio-24",
58358556204SWilliam Breathitt Gray 	.id_table = idio_24_pci_dev_id,
58458556204SWilliam Breathitt Gray 	.probe = idio_24_probe
58558556204SWilliam Breathitt Gray };
58658556204SWilliam Breathitt Gray 
58758556204SWilliam Breathitt Gray module_pci_driver(idio_24_driver);
58858556204SWilliam Breathitt Gray 
58958556204SWilliam Breathitt Gray MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
59058556204SWilliam Breathitt Gray MODULE_DESCRIPTION("ACCES PCIe-IDIO-24 GPIO driver");
59158556204SWilliam Breathitt Gray MODULE_LICENSE("GPL v2");
592