xref: /openbmc/linux/drivers/gpio/gpio-pcie-idio-24.c (revision 866e863edb9bdffb06b216908998d4639a7b165d)
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 
3158556204SWilliam Breathitt Gray /**
3258556204SWilliam Breathitt Gray  * struct idio_24_gpio_reg - GPIO device registers structure
3358556204SWilliam Breathitt Gray  * @out0_7:	Read: FET Outputs 0-7
3458556204SWilliam Breathitt Gray  *		Write: FET Outputs 0-7
3558556204SWilliam Breathitt Gray  * @out8_15:	Read: FET Outputs 8-15
3658556204SWilliam Breathitt Gray  *		Write: FET Outputs 8-15
3758556204SWilliam Breathitt Gray  * @out16_23:	Read: FET Outputs 16-23
3858556204SWilliam Breathitt Gray  *		Write: FET Outputs 16-23
3958556204SWilliam Breathitt Gray  * @ttl_out0_7:	Read: TTL/CMOS Outputs 0-7
4058556204SWilliam Breathitt Gray  *		Write: TTL/CMOS Outputs 0-7
4158556204SWilliam Breathitt Gray  * @in0_7:	Read: Isolated Inputs 0-7
4258556204SWilliam Breathitt Gray  *		Write: Reserved
4358556204SWilliam Breathitt Gray  * @in8_15:	Read: Isolated Inputs 8-15
4458556204SWilliam Breathitt Gray  *		Write: Reserved
4558556204SWilliam Breathitt Gray  * @in16_23:	Read: Isolated Inputs 16-23
4658556204SWilliam Breathitt Gray  *		Write: Reserved
4758556204SWilliam Breathitt Gray  * @ttl_in0_7:	Read: TTL/CMOS Inputs 0-7
4858556204SWilliam Breathitt Gray  *		Write: Reserved
4958556204SWilliam Breathitt Gray  * @cos0_7:	Read: COS Status Inputs 0-7
5058556204SWilliam Breathitt Gray  *		Write: COS Clear Inputs 0-7
5158556204SWilliam Breathitt Gray  * @cos8_15:	Read: COS Status Inputs 8-15
5258556204SWilliam Breathitt Gray  *		Write: COS Clear Inputs 8-15
5358556204SWilliam Breathitt Gray  * @cos16_23:	Read: COS Status Inputs 16-23
5458556204SWilliam Breathitt Gray  *		Write: COS Clear Inputs 16-23
5558556204SWilliam Breathitt Gray  * @cos_ttl0_7:	Read: COS Status TTL/CMOS 0-7
5658556204SWilliam Breathitt Gray  *		Write: COS Clear TTL/CMOS 0-7
5758556204SWilliam Breathitt Gray  * @ctl:	Read: Control Register
5858556204SWilliam Breathitt Gray  *		Write: Control Register
5958556204SWilliam Breathitt Gray  * @reserved:	Read: Reserved
6058556204SWilliam Breathitt Gray  *		Write: Reserved
6158556204SWilliam Breathitt Gray  * @cos_enable:	Read: COS Enable
6258556204SWilliam Breathitt Gray  *		Write: COS Enable
6358556204SWilliam Breathitt Gray  * @soft_reset:	Read: IRQ Output Pin Status
6458556204SWilliam Breathitt Gray  *		Write: Software Board Reset
6558556204SWilliam Breathitt Gray  */
6658556204SWilliam Breathitt Gray struct idio_24_gpio_reg {
6758556204SWilliam Breathitt Gray 	u8 out0_7;
6858556204SWilliam Breathitt Gray 	u8 out8_15;
6958556204SWilliam Breathitt Gray 	u8 out16_23;
7058556204SWilliam Breathitt Gray 	u8 ttl_out0_7;
7158556204SWilliam Breathitt Gray 	u8 in0_7;
7258556204SWilliam Breathitt Gray 	u8 in8_15;
7358556204SWilliam Breathitt Gray 	u8 in16_23;
7458556204SWilliam Breathitt Gray 	u8 ttl_in0_7;
7558556204SWilliam Breathitt Gray 	u8 cos0_7;
7658556204SWilliam Breathitt Gray 	u8 cos8_15;
7758556204SWilliam Breathitt Gray 	u8 cos16_23;
7858556204SWilliam Breathitt Gray 	u8 cos_ttl0_7;
7958556204SWilliam Breathitt Gray 	u8 ctl;
8058556204SWilliam Breathitt Gray 	u8 reserved;
8158556204SWilliam Breathitt Gray 	u8 cos_enable;
8258556204SWilliam Breathitt Gray 	u8 soft_reset;
8358556204SWilliam Breathitt Gray };
8458556204SWilliam Breathitt Gray 
8558556204SWilliam Breathitt Gray /**
8658556204SWilliam Breathitt Gray  * struct idio_24_gpio - GPIO device private data structure
8758556204SWilliam Breathitt Gray  * @chip:	instance of the gpio_chip
8858556204SWilliam Breathitt Gray  * @lock:	synchronization lock to prevent I/O race conditions
8958556204SWilliam Breathitt Gray  * @reg:	I/O address offset for the GPIO device registers
9058556204SWilliam Breathitt Gray  * @irq_mask:	I/O bits affected by interrupts
9158556204SWilliam Breathitt Gray  */
9258556204SWilliam Breathitt Gray struct idio_24_gpio {
9358556204SWilliam Breathitt Gray 	struct gpio_chip chip;
9458556204SWilliam Breathitt Gray 	raw_spinlock_t lock;
9558556204SWilliam Breathitt Gray 	struct idio_24_gpio_reg __iomem *reg;
9658556204SWilliam Breathitt Gray 	unsigned long irq_mask;
9758556204SWilliam Breathitt Gray };
9858556204SWilliam Breathitt Gray 
9958556204SWilliam Breathitt Gray static int idio_24_gpio_get_direction(struct gpio_chip *chip,
10058556204SWilliam Breathitt Gray 	unsigned int offset)
10158556204SWilliam Breathitt Gray {
10258556204SWilliam Breathitt Gray 	struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
10358556204SWilliam Breathitt Gray 	const unsigned long out_mode_mask = BIT(1);
10458556204SWilliam Breathitt Gray 
10558556204SWilliam Breathitt Gray 	/* FET Outputs */
10658556204SWilliam Breathitt Gray 	if (offset < 24)
107e42615ecSMatti Vaittinen 		return GPIO_LINE_DIRECTION_OUT;
10858556204SWilliam Breathitt Gray 
10958556204SWilliam Breathitt Gray 	/* Isolated Inputs */
11058556204SWilliam Breathitt Gray 	if (offset < 48)
111e42615ecSMatti Vaittinen 		return GPIO_LINE_DIRECTION_IN;
11258556204SWilliam Breathitt Gray 
11358556204SWilliam Breathitt Gray 	/* TTL/CMOS I/O */
11458556204SWilliam Breathitt Gray 	/* OUT MODE = 1 when TTL/CMOS Output Mode is set */
115e42615ecSMatti Vaittinen 	if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask)
116e42615ecSMatti Vaittinen 		return GPIO_LINE_DIRECTION_OUT;
117e42615ecSMatti Vaittinen 
118e42615ecSMatti Vaittinen 	return GPIO_LINE_DIRECTION_IN;
11958556204SWilliam Breathitt Gray }
12058556204SWilliam Breathitt Gray 
12158556204SWilliam Breathitt Gray static int idio_24_gpio_direction_input(struct gpio_chip *chip,
12258556204SWilliam Breathitt Gray 	unsigned int offset)
12358556204SWilliam Breathitt Gray {
12458556204SWilliam Breathitt Gray 	struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
12558556204SWilliam Breathitt Gray 	unsigned long flags;
12658556204SWilliam Breathitt Gray 	unsigned int ctl_state;
12758556204SWilliam Breathitt Gray 	const unsigned long out_mode_mask = BIT(1);
12858556204SWilliam Breathitt Gray 
12958556204SWilliam Breathitt Gray 	/* TTL/CMOS I/O */
13058556204SWilliam Breathitt Gray 	if (offset > 47) {
13158556204SWilliam Breathitt Gray 		raw_spin_lock_irqsave(&idio24gpio->lock, flags);
13258556204SWilliam Breathitt Gray 
13358556204SWilliam Breathitt Gray 		/* Clear TTL/CMOS Output Mode */
13458556204SWilliam Breathitt Gray 		ctl_state = ioread8(&idio24gpio->reg->ctl) & ~out_mode_mask;
13558556204SWilliam Breathitt Gray 		iowrite8(ctl_state, &idio24gpio->reg->ctl);
13658556204SWilliam Breathitt Gray 
13758556204SWilliam Breathitt Gray 		raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
13858556204SWilliam Breathitt Gray 	}
13958556204SWilliam Breathitt Gray 
14058556204SWilliam Breathitt Gray 	return 0;
14158556204SWilliam Breathitt Gray }
14258556204SWilliam Breathitt Gray 
14358556204SWilliam Breathitt Gray static int idio_24_gpio_direction_output(struct gpio_chip *chip,
14458556204SWilliam Breathitt Gray 	unsigned int offset, int value)
14558556204SWilliam Breathitt Gray {
14658556204SWilliam Breathitt Gray 	struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
14758556204SWilliam Breathitt Gray 	unsigned long flags;
14858556204SWilliam Breathitt Gray 	unsigned int ctl_state;
14958556204SWilliam Breathitt Gray 	const unsigned long out_mode_mask = BIT(1);
15058556204SWilliam Breathitt Gray 
15158556204SWilliam Breathitt Gray 	/* TTL/CMOS I/O */
15258556204SWilliam Breathitt Gray 	if (offset > 47) {
15358556204SWilliam Breathitt Gray 		raw_spin_lock_irqsave(&idio24gpio->lock, flags);
15458556204SWilliam Breathitt Gray 
15558556204SWilliam Breathitt Gray 		/* Set TTL/CMOS Output Mode */
15658556204SWilliam Breathitt Gray 		ctl_state = ioread8(&idio24gpio->reg->ctl) | out_mode_mask;
15758556204SWilliam Breathitt Gray 		iowrite8(ctl_state, &idio24gpio->reg->ctl);
15858556204SWilliam Breathitt Gray 
15958556204SWilliam Breathitt Gray 		raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
16058556204SWilliam Breathitt Gray 	}
16158556204SWilliam Breathitt Gray 
16258556204SWilliam Breathitt Gray 	chip->set(chip, offset, value);
16358556204SWilliam Breathitt Gray 	return 0;
16458556204SWilliam Breathitt Gray }
16558556204SWilliam Breathitt Gray 
16658556204SWilliam Breathitt Gray static int idio_24_gpio_get(struct gpio_chip *chip, unsigned int offset)
16758556204SWilliam Breathitt Gray {
16858556204SWilliam Breathitt Gray 	struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
16958556204SWilliam Breathitt Gray 	const unsigned long offset_mask = BIT(offset % 8);
17058556204SWilliam Breathitt Gray 	const unsigned long out_mode_mask = BIT(1);
17158556204SWilliam Breathitt Gray 
17258556204SWilliam Breathitt Gray 	/* FET Outputs */
17358556204SWilliam Breathitt Gray 	if (offset < 8)
17458556204SWilliam Breathitt Gray 		return !!(ioread8(&idio24gpio->reg->out0_7) & offset_mask);
17558556204SWilliam Breathitt Gray 
17658556204SWilliam Breathitt Gray 	if (offset < 16)
17758556204SWilliam Breathitt Gray 		return !!(ioread8(&idio24gpio->reg->out8_15) & offset_mask);
17858556204SWilliam Breathitt Gray 
17958556204SWilliam Breathitt Gray 	if (offset < 24)
18058556204SWilliam Breathitt Gray 		return !!(ioread8(&idio24gpio->reg->out16_23) & offset_mask);
18158556204SWilliam Breathitt Gray 
18258556204SWilliam Breathitt Gray 	/* Isolated Inputs */
18358556204SWilliam Breathitt Gray 	if (offset < 32)
18458556204SWilliam Breathitt Gray 		return !!(ioread8(&idio24gpio->reg->in0_7) & offset_mask);
18558556204SWilliam Breathitt Gray 
18658556204SWilliam Breathitt Gray 	if (offset < 40)
18758556204SWilliam Breathitt Gray 		return !!(ioread8(&idio24gpio->reg->in8_15) & offset_mask);
18858556204SWilliam Breathitt Gray 
18958556204SWilliam Breathitt Gray 	if (offset < 48)
19058556204SWilliam Breathitt Gray 		return !!(ioread8(&idio24gpio->reg->in16_23) & offset_mask);
19158556204SWilliam Breathitt Gray 
19258556204SWilliam Breathitt Gray 	/* TTL/CMOS Outputs */
19358556204SWilliam Breathitt Gray 	if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask)
19458556204SWilliam Breathitt Gray 		return !!(ioread8(&idio24gpio->reg->ttl_out0_7) & offset_mask);
19558556204SWilliam Breathitt Gray 
19658556204SWilliam Breathitt Gray 	/* TTL/CMOS Inputs */
19758556204SWilliam Breathitt Gray 	return !!(ioread8(&idio24gpio->reg->ttl_in0_7) & offset_mask);
19858556204SWilliam Breathitt Gray }
19958556204SWilliam Breathitt Gray 
200ca370815SWilliam Breathitt Gray static int idio_24_gpio_get_multiple(struct gpio_chip *chip,
201ca370815SWilliam Breathitt Gray 	unsigned long *mask, unsigned long *bits)
202ca370815SWilliam Breathitt Gray {
203ca370815SWilliam Breathitt Gray 	struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
204c586aa8fSWilliam Breathitt Gray 	unsigned long offset;
205c586aa8fSWilliam Breathitt Gray 	unsigned long gpio_mask;
206304440aaSWilliam Breathitt Gray 	void __iomem *ports[] = {
207304440aaSWilliam Breathitt Gray 		&idio24gpio->reg->out0_7, &idio24gpio->reg->out8_15,
208304440aaSWilliam Breathitt Gray 		&idio24gpio->reg->out16_23, &idio24gpio->reg->in0_7,
209304440aaSWilliam Breathitt Gray 		&idio24gpio->reg->in8_15, &idio24gpio->reg->in16_23,
210ca370815SWilliam Breathitt Gray 	};
211c586aa8fSWilliam Breathitt Gray 	size_t index;
212c586aa8fSWilliam Breathitt Gray 	unsigned long port_state;
213ca370815SWilliam Breathitt Gray 	const unsigned long out_mode_mask = BIT(1);
214ca370815SWilliam Breathitt Gray 
215ca370815SWilliam Breathitt Gray 	/* clear bits array to a clean slate */
216ca370815SWilliam Breathitt Gray 	bitmap_zero(bits, chip->ngpio);
217ca370815SWilliam Breathitt Gray 
218c586aa8fSWilliam Breathitt Gray 	for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) {
219c586aa8fSWilliam Breathitt Gray 		index = offset / 8;
220ca370815SWilliam Breathitt Gray 
221ca370815SWilliam Breathitt Gray 		/* read bits from current gpio port (port 6 is TTL GPIO) */
222c586aa8fSWilliam Breathitt Gray 		if (index < 6)
223c586aa8fSWilliam Breathitt Gray 			port_state = ioread8(ports[index]);
224ca370815SWilliam Breathitt Gray 		else if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask)
225ca370815SWilliam Breathitt Gray 			port_state = ioread8(&idio24gpio->reg->ttl_out0_7);
226ca370815SWilliam Breathitt Gray 		else
227ca370815SWilliam Breathitt Gray 			port_state = ioread8(&idio24gpio->reg->ttl_in0_7);
228ca370815SWilliam Breathitt Gray 
229c586aa8fSWilliam Breathitt Gray 		port_state &= gpio_mask;
230c586aa8fSWilliam Breathitt Gray 
231c586aa8fSWilliam Breathitt Gray 		bitmap_set_value8(bits, port_state, offset);
232ca370815SWilliam Breathitt Gray 	}
233ca370815SWilliam Breathitt Gray 
234ca370815SWilliam Breathitt Gray 	return 0;
235ca370815SWilliam Breathitt Gray }
236ca370815SWilliam Breathitt Gray 
23758556204SWilliam Breathitt Gray static void idio_24_gpio_set(struct gpio_chip *chip, unsigned int offset,
23858556204SWilliam Breathitt Gray 	int value)
23958556204SWilliam Breathitt Gray {
24058556204SWilliam Breathitt Gray 	struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
24158556204SWilliam Breathitt Gray 	const unsigned long out_mode_mask = BIT(1);
24258556204SWilliam Breathitt Gray 	void __iomem *base;
24358556204SWilliam Breathitt Gray 	const unsigned int mask = BIT(offset % 8);
24458556204SWilliam Breathitt Gray 	unsigned long flags;
24558556204SWilliam Breathitt Gray 	unsigned int out_state;
24658556204SWilliam Breathitt Gray 
24758556204SWilliam Breathitt Gray 	/* Isolated Inputs */
24858556204SWilliam Breathitt Gray 	if (offset > 23 && offset < 48)
24958556204SWilliam Breathitt Gray 		return;
25058556204SWilliam Breathitt Gray 
25158556204SWilliam Breathitt Gray 	/* TTL/CMOS Inputs */
25258556204SWilliam Breathitt Gray 	if (offset > 47 && !(ioread8(&idio24gpio->reg->ctl) & out_mode_mask))
25358556204SWilliam Breathitt Gray 		return;
25458556204SWilliam Breathitt Gray 
25558556204SWilliam Breathitt Gray 	/* TTL/CMOS Outputs */
25658556204SWilliam Breathitt Gray 	if (offset > 47)
25758556204SWilliam Breathitt Gray 		base = &idio24gpio->reg->ttl_out0_7;
25858556204SWilliam Breathitt Gray 	/* FET Outputs */
25958556204SWilliam Breathitt Gray 	else if (offset > 15)
26058556204SWilliam Breathitt Gray 		base = &idio24gpio->reg->out16_23;
26158556204SWilliam Breathitt Gray 	else if (offset > 7)
26258556204SWilliam Breathitt Gray 		base = &idio24gpio->reg->out8_15;
26358556204SWilliam Breathitt Gray 	else
26458556204SWilliam Breathitt Gray 		base = &idio24gpio->reg->out0_7;
26558556204SWilliam Breathitt Gray 
26658556204SWilliam Breathitt Gray 	raw_spin_lock_irqsave(&idio24gpio->lock, flags);
26758556204SWilliam Breathitt Gray 
26858556204SWilliam Breathitt Gray 	if (value)
26958556204SWilliam Breathitt Gray 		out_state = ioread8(base) | mask;
27058556204SWilliam Breathitt Gray 	else
27158556204SWilliam Breathitt Gray 		out_state = ioread8(base) & ~mask;
27258556204SWilliam Breathitt Gray 
27358556204SWilliam Breathitt Gray 	iowrite8(out_state, base);
27458556204SWilliam Breathitt Gray 
27558556204SWilliam Breathitt Gray 	raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
27658556204SWilliam Breathitt Gray }
27758556204SWilliam Breathitt Gray 
278ca370815SWilliam Breathitt Gray static void idio_24_gpio_set_multiple(struct gpio_chip *chip,
279ca370815SWilliam Breathitt Gray 	unsigned long *mask, unsigned long *bits)
280ca370815SWilliam Breathitt Gray {
281ca370815SWilliam Breathitt Gray 	struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
282c586aa8fSWilliam Breathitt Gray 	unsigned long offset;
283ca370815SWilliam Breathitt Gray 	unsigned long gpio_mask;
284304440aaSWilliam Breathitt Gray 	void __iomem *ports[] = {
285304440aaSWilliam Breathitt Gray 		&idio24gpio->reg->out0_7, &idio24gpio->reg->out8_15,
286304440aaSWilliam Breathitt Gray 		&idio24gpio->reg->out16_23
287ca370815SWilliam Breathitt Gray 	};
288c586aa8fSWilliam Breathitt Gray 	size_t index;
289c586aa8fSWilliam Breathitt Gray 	unsigned long bitmask;
290c586aa8fSWilliam Breathitt Gray 	unsigned long flags;
291c586aa8fSWilliam Breathitt Gray 	unsigned long out_state;
292ca370815SWilliam Breathitt Gray 	const unsigned long out_mode_mask = BIT(1);
293ca370815SWilliam Breathitt Gray 
294c586aa8fSWilliam Breathitt Gray 	for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) {
295c586aa8fSWilliam Breathitt Gray 		index = offset / 8;
296ca370815SWilliam Breathitt Gray 
297c586aa8fSWilliam Breathitt Gray 		bitmask = bitmap_get_value8(bits, offset) & gpio_mask;
298c586aa8fSWilliam Breathitt Gray 
299c586aa8fSWilliam Breathitt Gray 		raw_spin_lock_irqsave(&idio24gpio->lock, flags);
300c586aa8fSWilliam Breathitt Gray 
301c586aa8fSWilliam Breathitt Gray 		/* read bits from current gpio port (port 6 is TTL GPIO) */
302c586aa8fSWilliam Breathitt Gray 		if (index < 6) {
303c586aa8fSWilliam Breathitt Gray 			out_state = ioread8(ports[index]);
304c586aa8fSWilliam Breathitt Gray 		} else if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask) {
305c586aa8fSWilliam Breathitt Gray 			out_state = ioread8(&idio24gpio->reg->ttl_out0_7);
306c586aa8fSWilliam Breathitt Gray 		} else {
307c586aa8fSWilliam Breathitt Gray 			/* skip TTL GPIO if set for input */
308c586aa8fSWilliam Breathitt Gray 			raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
309ca370815SWilliam Breathitt Gray 			continue;
310ca370815SWilliam Breathitt Gray 		}
311ca370815SWilliam Breathitt Gray 
312c586aa8fSWilliam Breathitt Gray 		/* set requested bit states */
313c586aa8fSWilliam Breathitt Gray 		out_state &= ~gpio_mask;
314c586aa8fSWilliam Breathitt Gray 		out_state |= bitmask;
315ca370815SWilliam Breathitt Gray 
316c586aa8fSWilliam Breathitt Gray 		/* write bits for current gpio port (port 6 is TTL GPIO) */
317c586aa8fSWilliam Breathitt Gray 		if (index < 6)
318c586aa8fSWilliam Breathitt Gray 			iowrite8(out_state, ports[index]);
319c586aa8fSWilliam Breathitt Gray 		else
320ca370815SWilliam Breathitt Gray 			iowrite8(out_state, &idio24gpio->reg->ttl_out0_7);
321ca370815SWilliam Breathitt Gray 
322ca370815SWilliam Breathitt Gray 		raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
323ca370815SWilliam Breathitt Gray 	}
324c586aa8fSWilliam Breathitt Gray }
325ca370815SWilliam Breathitt Gray 
32658556204SWilliam Breathitt Gray static void idio_24_irq_ack(struct irq_data *data)
32758556204SWilliam Breathitt Gray {
32858556204SWilliam Breathitt Gray }
32958556204SWilliam Breathitt Gray 
33058556204SWilliam Breathitt Gray static void idio_24_irq_mask(struct irq_data *data)
33158556204SWilliam Breathitt Gray {
33258556204SWilliam Breathitt Gray 	struct gpio_chip *const chip = irq_data_get_irq_chip_data(data);
33358556204SWilliam Breathitt Gray 	struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
33458556204SWilliam Breathitt Gray 	unsigned long flags;
33558556204SWilliam Breathitt Gray 	const unsigned long bit_offset = irqd_to_hwirq(data) - 24;
33658556204SWilliam Breathitt Gray 	unsigned char new_irq_mask;
33758556204SWilliam Breathitt Gray 	const unsigned long bank_offset = bit_offset/8 * 8;
33858556204SWilliam Breathitt Gray 	unsigned char cos_enable_state;
33958556204SWilliam Breathitt Gray 
34058556204SWilliam Breathitt Gray 	raw_spin_lock_irqsave(&idio24gpio->lock, flags);
34158556204SWilliam Breathitt Gray 
34258556204SWilliam Breathitt Gray 	idio24gpio->irq_mask &= BIT(bit_offset);
34358556204SWilliam Breathitt Gray 	new_irq_mask = idio24gpio->irq_mask >> bank_offset;
34458556204SWilliam Breathitt Gray 
34558556204SWilliam Breathitt Gray 	if (!new_irq_mask) {
34658556204SWilliam Breathitt Gray 		cos_enable_state = ioread8(&idio24gpio->reg->cos_enable);
34758556204SWilliam Breathitt Gray 
34858556204SWilliam Breathitt Gray 		/* Disable Rising Edge detection */
34958556204SWilliam Breathitt Gray 		cos_enable_state &= ~BIT(bank_offset);
35058556204SWilliam Breathitt Gray 		/* Disable Falling Edge detection */
35158556204SWilliam Breathitt Gray 		cos_enable_state &= ~BIT(bank_offset + 4);
35258556204SWilliam Breathitt Gray 
35358556204SWilliam Breathitt Gray 		iowrite8(cos_enable_state, &idio24gpio->reg->cos_enable);
35458556204SWilliam Breathitt Gray 	}
35558556204SWilliam Breathitt Gray 
35658556204SWilliam Breathitt Gray 	raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
35758556204SWilliam Breathitt Gray }
35858556204SWilliam Breathitt Gray 
35958556204SWilliam Breathitt Gray static void idio_24_irq_unmask(struct irq_data *data)
36058556204SWilliam Breathitt Gray {
36158556204SWilliam Breathitt Gray 	struct gpio_chip *const chip = irq_data_get_irq_chip_data(data);
36258556204SWilliam Breathitt Gray 	struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
36358556204SWilliam Breathitt Gray 	unsigned long flags;
36458556204SWilliam Breathitt Gray 	unsigned char prev_irq_mask;
36558556204SWilliam Breathitt Gray 	const unsigned long bit_offset = irqd_to_hwirq(data) - 24;
36658556204SWilliam Breathitt Gray 	const unsigned long bank_offset = bit_offset/8 * 8;
36758556204SWilliam Breathitt Gray 	unsigned char cos_enable_state;
36858556204SWilliam Breathitt Gray 
36958556204SWilliam Breathitt Gray 	raw_spin_lock_irqsave(&idio24gpio->lock, flags);
37058556204SWilliam Breathitt Gray 
37158556204SWilliam Breathitt Gray 	prev_irq_mask = idio24gpio->irq_mask >> bank_offset;
37258556204SWilliam Breathitt Gray 	idio24gpio->irq_mask |= BIT(bit_offset);
37358556204SWilliam Breathitt Gray 
37458556204SWilliam Breathitt Gray 	if (!prev_irq_mask) {
37558556204SWilliam Breathitt Gray 		cos_enable_state = ioread8(&idio24gpio->reg->cos_enable);
37658556204SWilliam Breathitt Gray 
37758556204SWilliam Breathitt Gray 		/* Enable Rising Edge detection */
37858556204SWilliam Breathitt Gray 		cos_enable_state |= BIT(bank_offset);
37958556204SWilliam Breathitt Gray 		/* Enable Falling Edge detection */
38058556204SWilliam Breathitt Gray 		cos_enable_state |= BIT(bank_offset + 4);
38158556204SWilliam Breathitt Gray 
38258556204SWilliam Breathitt Gray 		iowrite8(cos_enable_state, &idio24gpio->reg->cos_enable);
38358556204SWilliam Breathitt Gray 	}
38458556204SWilliam Breathitt Gray 
38558556204SWilliam Breathitt Gray 	raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
38658556204SWilliam Breathitt Gray }
38758556204SWilliam Breathitt Gray 
38858556204SWilliam Breathitt Gray static int idio_24_irq_set_type(struct irq_data *data, unsigned int flow_type)
38958556204SWilliam Breathitt Gray {
39058556204SWilliam Breathitt Gray 	/* The only valid irq types are none and both-edges */
39158556204SWilliam Breathitt Gray 	if (flow_type != IRQ_TYPE_NONE &&
39258556204SWilliam Breathitt Gray 		(flow_type & IRQ_TYPE_EDGE_BOTH) != IRQ_TYPE_EDGE_BOTH)
39358556204SWilliam Breathitt Gray 		return -EINVAL;
39458556204SWilliam Breathitt Gray 
39558556204SWilliam Breathitt Gray 	return 0;
39658556204SWilliam Breathitt Gray }
39758556204SWilliam Breathitt Gray 
39858556204SWilliam Breathitt Gray static struct irq_chip idio_24_irqchip = {
39958556204SWilliam Breathitt Gray 	.name = "pcie-idio-24",
40058556204SWilliam Breathitt Gray 	.irq_ack = idio_24_irq_ack,
40158556204SWilliam Breathitt Gray 	.irq_mask = idio_24_irq_mask,
40258556204SWilliam Breathitt Gray 	.irq_unmask = idio_24_irq_unmask,
40358556204SWilliam Breathitt Gray 	.irq_set_type = idio_24_irq_set_type
40458556204SWilliam Breathitt Gray };
40558556204SWilliam Breathitt Gray 
40658556204SWilliam Breathitt Gray static irqreturn_t idio_24_irq_handler(int irq, void *dev_id)
40758556204SWilliam Breathitt Gray {
40858556204SWilliam Breathitt Gray 	struct idio_24_gpio *const idio24gpio = dev_id;
40958556204SWilliam Breathitt Gray 	unsigned long irq_status;
41058556204SWilliam Breathitt Gray 	struct gpio_chip *const chip = &idio24gpio->chip;
41158556204SWilliam Breathitt Gray 	unsigned long irq_mask;
41258556204SWilliam Breathitt Gray 	int gpio;
41358556204SWilliam Breathitt Gray 
41458556204SWilliam Breathitt Gray 	raw_spin_lock(&idio24gpio->lock);
41558556204SWilliam Breathitt Gray 
41658556204SWilliam Breathitt Gray 	/* Read Change-Of-State status */
41758556204SWilliam Breathitt Gray 	irq_status = ioread32(&idio24gpio->reg->cos0_7);
41858556204SWilliam Breathitt Gray 
41958556204SWilliam Breathitt Gray 	raw_spin_unlock(&idio24gpio->lock);
42058556204SWilliam Breathitt Gray 
42158556204SWilliam Breathitt Gray 	/* Make sure our device generated IRQ */
42258556204SWilliam Breathitt Gray 	if (!irq_status)
42358556204SWilliam Breathitt Gray 		return IRQ_NONE;
42458556204SWilliam Breathitt Gray 
42558556204SWilliam Breathitt Gray 	/* Handle only unmasked IRQ */
42658556204SWilliam Breathitt Gray 	irq_mask = idio24gpio->irq_mask & irq_status;
42758556204SWilliam Breathitt Gray 
42858556204SWilliam Breathitt Gray 	for_each_set_bit(gpio, &irq_mask, chip->ngpio - 24)
42958556204SWilliam Breathitt Gray 		generic_handle_irq(irq_find_mapping(chip->irq.domain,
43058556204SWilliam Breathitt Gray 			gpio + 24));
43158556204SWilliam Breathitt Gray 
43258556204SWilliam Breathitt Gray 	raw_spin_lock(&idio24gpio->lock);
43358556204SWilliam Breathitt Gray 
43458556204SWilliam Breathitt Gray 	/* Clear Change-Of-State status */
43558556204SWilliam Breathitt Gray 	iowrite32(irq_status, &idio24gpio->reg->cos0_7);
43658556204SWilliam Breathitt Gray 
43758556204SWilliam Breathitt Gray 	raw_spin_unlock(&idio24gpio->lock);
43858556204SWilliam Breathitt Gray 
43958556204SWilliam Breathitt Gray 	return IRQ_HANDLED;
44058556204SWilliam Breathitt Gray }
44158556204SWilliam Breathitt Gray 
44258556204SWilliam Breathitt Gray #define IDIO_24_NGPIO 56
44358556204SWilliam Breathitt Gray static const char *idio_24_names[IDIO_24_NGPIO] = {
44458556204SWilliam Breathitt Gray 	"OUT0", "OUT1", "OUT2", "OUT3", "OUT4", "OUT5", "OUT6", "OUT7",
44558556204SWilliam Breathitt Gray 	"OUT8", "OUT9", "OUT10", "OUT11", "OUT12", "OUT13", "OUT14", "OUT15",
44658556204SWilliam Breathitt Gray 	"OUT16", "OUT17", "OUT18", "OUT19", "OUT20", "OUT21", "OUT22", "OUT23",
44758556204SWilliam Breathitt Gray 	"IIN0", "IIN1", "IIN2", "IIN3", "IIN4", "IIN5", "IIN6", "IIN7",
44858556204SWilliam Breathitt Gray 	"IIN8", "IIN9", "IIN10", "IIN11", "IIN12", "IIN13", "IIN14", "IIN15",
44958556204SWilliam Breathitt Gray 	"IIN16", "IIN17", "IIN18", "IIN19", "IIN20", "IIN21", "IIN22", "IIN23",
45058556204SWilliam Breathitt Gray 	"TTL0", "TTL1", "TTL2", "TTL3", "TTL4", "TTL5", "TTL6", "TTL7"
45158556204SWilliam Breathitt Gray };
45258556204SWilliam Breathitt Gray 
45358556204SWilliam Breathitt Gray static int idio_24_probe(struct pci_dev *pdev, const struct pci_device_id *id)
45458556204SWilliam Breathitt Gray {
45558556204SWilliam Breathitt Gray 	struct device *const dev = &pdev->dev;
45658556204SWilliam Breathitt Gray 	struct idio_24_gpio *idio24gpio;
45758556204SWilliam Breathitt Gray 	int err;
45858556204SWilliam Breathitt Gray 	const size_t pci_bar_index = 2;
45958556204SWilliam Breathitt Gray 	const char *const name = pci_name(pdev);
460*866e863eSLinus Walleij 	struct gpio_irq_chip *girq;
46158556204SWilliam Breathitt Gray 
46258556204SWilliam Breathitt Gray 	idio24gpio = devm_kzalloc(dev, sizeof(*idio24gpio), GFP_KERNEL);
46358556204SWilliam Breathitt Gray 	if (!idio24gpio)
46458556204SWilliam Breathitt Gray 		return -ENOMEM;
46558556204SWilliam Breathitt Gray 
46658556204SWilliam Breathitt Gray 	err = pcim_enable_device(pdev);
46758556204SWilliam Breathitt Gray 	if (err) {
46858556204SWilliam Breathitt Gray 		dev_err(dev, "Failed to enable PCI device (%d)\n", err);
46958556204SWilliam Breathitt Gray 		return err;
47058556204SWilliam Breathitt Gray 	}
47158556204SWilliam Breathitt Gray 
47258556204SWilliam Breathitt Gray 	err = pcim_iomap_regions(pdev, BIT(pci_bar_index), name);
47358556204SWilliam Breathitt Gray 	if (err) {
47458556204SWilliam Breathitt Gray 		dev_err(dev, "Unable to map PCI I/O addresses (%d)\n", err);
47558556204SWilliam Breathitt Gray 		return err;
47658556204SWilliam Breathitt Gray 	}
47758556204SWilliam Breathitt Gray 
47858556204SWilliam Breathitt Gray 	idio24gpio->reg = pcim_iomap_table(pdev)[pci_bar_index];
47958556204SWilliam Breathitt Gray 
48058556204SWilliam Breathitt Gray 	idio24gpio->chip.label = name;
48158556204SWilliam Breathitt Gray 	idio24gpio->chip.parent = dev;
48258556204SWilliam Breathitt Gray 	idio24gpio->chip.owner = THIS_MODULE;
48358556204SWilliam Breathitt Gray 	idio24gpio->chip.base = -1;
48458556204SWilliam Breathitt Gray 	idio24gpio->chip.ngpio = IDIO_24_NGPIO;
48558556204SWilliam Breathitt Gray 	idio24gpio->chip.names = idio_24_names;
48658556204SWilliam Breathitt Gray 	idio24gpio->chip.get_direction = idio_24_gpio_get_direction;
48758556204SWilliam Breathitt Gray 	idio24gpio->chip.direction_input = idio_24_gpio_direction_input;
48858556204SWilliam Breathitt Gray 	idio24gpio->chip.direction_output = idio_24_gpio_direction_output;
48958556204SWilliam Breathitt Gray 	idio24gpio->chip.get = idio_24_gpio_get;
490ca370815SWilliam Breathitt Gray 	idio24gpio->chip.get_multiple = idio_24_gpio_get_multiple;
49158556204SWilliam Breathitt Gray 	idio24gpio->chip.set = idio_24_gpio_set;
492ca370815SWilliam Breathitt Gray 	idio24gpio->chip.set_multiple = idio_24_gpio_set_multiple;
49358556204SWilliam Breathitt Gray 
494*866e863eSLinus Walleij 	girq = &idio24gpio->chip.irq;
495*866e863eSLinus Walleij 	girq->chip = &idio_24_irqchip;
496*866e863eSLinus Walleij 	/* This will let us handle the parent IRQ in the driver */
497*866e863eSLinus Walleij 	girq->parent_handler = NULL;
498*866e863eSLinus Walleij 	girq->num_parents = 0;
499*866e863eSLinus Walleij 	girq->parents = NULL;
500*866e863eSLinus Walleij 	girq->default_type = IRQ_TYPE_NONE;
501*866e863eSLinus Walleij 	girq->handler = handle_edge_irq;
502*866e863eSLinus Walleij 
50358556204SWilliam Breathitt Gray 	raw_spin_lock_init(&idio24gpio->lock);
50458556204SWilliam Breathitt Gray 
50558556204SWilliam Breathitt Gray 	/* Software board reset */
50658556204SWilliam Breathitt Gray 	iowrite8(0, &idio24gpio->reg->soft_reset);
50758556204SWilliam Breathitt Gray 
50858556204SWilliam Breathitt Gray 	err = devm_gpiochip_add_data(dev, &idio24gpio->chip, idio24gpio);
50958556204SWilliam Breathitt Gray 	if (err) {
51058556204SWilliam Breathitt Gray 		dev_err(dev, "GPIO registering failed (%d)\n", err);
51158556204SWilliam Breathitt Gray 		return err;
51258556204SWilliam Breathitt Gray 	}
51358556204SWilliam Breathitt Gray 
51458556204SWilliam Breathitt Gray 	err = devm_request_irq(dev, pdev->irq, idio_24_irq_handler, IRQF_SHARED,
51558556204SWilliam Breathitt Gray 		name, idio24gpio);
51658556204SWilliam Breathitt Gray 	if (err) {
51758556204SWilliam Breathitt Gray 		dev_err(dev, "IRQ handler registering failed (%d)\n", err);
51858556204SWilliam Breathitt Gray 		return err;
51958556204SWilliam Breathitt Gray 	}
52058556204SWilliam Breathitt Gray 
52158556204SWilliam Breathitt Gray 	return 0;
52258556204SWilliam Breathitt Gray }
52358556204SWilliam Breathitt Gray 
52458556204SWilliam Breathitt Gray static const struct pci_device_id idio_24_pci_dev_id[] = {
52558556204SWilliam Breathitt Gray 	{ PCI_DEVICE(0x494F, 0x0FD0) }, { PCI_DEVICE(0x494F, 0x0BD0) },
52658556204SWilliam Breathitt Gray 	{ PCI_DEVICE(0x494F, 0x07D0) }, { PCI_DEVICE(0x494F, 0x0FC0) },
52758556204SWilliam Breathitt Gray 	{ 0 }
52858556204SWilliam Breathitt Gray };
52958556204SWilliam Breathitt Gray MODULE_DEVICE_TABLE(pci, idio_24_pci_dev_id);
53058556204SWilliam Breathitt Gray 
53158556204SWilliam Breathitt Gray static struct pci_driver idio_24_driver = {
53258556204SWilliam Breathitt Gray 	.name = "pcie-idio-24",
53358556204SWilliam Breathitt Gray 	.id_table = idio_24_pci_dev_id,
53458556204SWilliam Breathitt Gray 	.probe = idio_24_probe
53558556204SWilliam Breathitt Gray };
53658556204SWilliam Breathitt Gray 
53758556204SWilliam Breathitt Gray module_pci_driver(idio_24_driver);
53858556204SWilliam Breathitt Gray 
53958556204SWilliam Breathitt Gray MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
54058556204SWilliam Breathitt Gray MODULE_DESCRIPTION("ACCES PCIe-IDIO-24 GPIO driver");
54158556204SWilliam Breathitt Gray MODULE_LICENSE("GPL v2");
542