xref: /openbmc/linux/drivers/gpio/gpio-pcie-idio-24.c (revision e42615ec233b30dfaf117b108d4cb49455b4df1d)
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)
107*e42615ecSMatti Vaittinen 		return GPIO_LINE_DIRECTION_OUT;
10858556204SWilliam Breathitt Gray 
10958556204SWilliam Breathitt Gray 	/* Isolated Inputs */
11058556204SWilliam Breathitt Gray 	if (offset < 48)
111*e42615ecSMatti 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 */
115*e42615ecSMatti Vaittinen 	if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask)
116*e42615ecSMatti Vaittinen 		return GPIO_LINE_DIRECTION_OUT;
117*e42615ecSMatti Vaittinen 
118*e42615ecSMatti 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);
204ca370815SWilliam Breathitt Gray 	size_t i;
205ca370815SWilliam Breathitt Gray 	const unsigned int gpio_reg_size = 8;
206ca370815SWilliam Breathitt Gray 	unsigned int bits_offset;
207ca370815SWilliam Breathitt Gray 	size_t word_index;
208ca370815SWilliam Breathitt Gray 	unsigned int word_offset;
209ca370815SWilliam Breathitt Gray 	unsigned long word_mask;
210ca370815SWilliam Breathitt Gray 	const unsigned long port_mask = GENMASK(gpio_reg_size - 1, 0);
211ca370815SWilliam Breathitt Gray 	unsigned long port_state;
212304440aaSWilliam Breathitt Gray 	void __iomem *ports[] = {
213304440aaSWilliam Breathitt Gray 		&idio24gpio->reg->out0_7, &idio24gpio->reg->out8_15,
214304440aaSWilliam Breathitt Gray 		&idio24gpio->reg->out16_23, &idio24gpio->reg->in0_7,
215304440aaSWilliam Breathitt Gray 		&idio24gpio->reg->in8_15, &idio24gpio->reg->in16_23,
216ca370815SWilliam Breathitt Gray 	};
217ca370815SWilliam Breathitt Gray 	const unsigned long out_mode_mask = BIT(1);
218ca370815SWilliam Breathitt Gray 
219ca370815SWilliam Breathitt Gray 	/* clear bits array to a clean slate */
220ca370815SWilliam Breathitt Gray 	bitmap_zero(bits, chip->ngpio);
221ca370815SWilliam Breathitt Gray 
222ca370815SWilliam Breathitt Gray 	/* get bits are evaluated a gpio port register at a time */
223e026646cSWilliam Breathitt Gray 	for (i = 0; i < ARRAY_SIZE(ports) + 1; i++) {
224ca370815SWilliam Breathitt Gray 		/* gpio offset in bits array */
225ca370815SWilliam Breathitt Gray 		bits_offset = i * gpio_reg_size;
226ca370815SWilliam Breathitt Gray 
227ca370815SWilliam Breathitt Gray 		/* word index for bits array */
228ca370815SWilliam Breathitt Gray 		word_index = BIT_WORD(bits_offset);
229ca370815SWilliam Breathitt Gray 
230ca370815SWilliam Breathitt Gray 		/* gpio offset within current word of bits array */
231ca370815SWilliam Breathitt Gray 		word_offset = bits_offset % BITS_PER_LONG;
232ca370815SWilliam Breathitt Gray 
233ca370815SWilliam Breathitt Gray 		/* mask of get bits for current gpio within current word */
234ca370815SWilliam Breathitt Gray 		word_mask = mask[word_index] & (port_mask << word_offset);
235ca370815SWilliam Breathitt Gray 		if (!word_mask) {
236ca370815SWilliam Breathitt Gray 			/* no get bits in this port so skip to next one */
237ca370815SWilliam Breathitt Gray 			continue;
238ca370815SWilliam Breathitt Gray 		}
239ca370815SWilliam Breathitt Gray 
240ca370815SWilliam Breathitt Gray 		/* read bits from current gpio port (port 6 is TTL GPIO) */
241ca370815SWilliam Breathitt Gray 		if (i < 6)
242304440aaSWilliam Breathitt Gray 			port_state = ioread8(ports[i]);
243ca370815SWilliam Breathitt Gray 		else if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask)
244ca370815SWilliam Breathitt Gray 			port_state = ioread8(&idio24gpio->reg->ttl_out0_7);
245ca370815SWilliam Breathitt Gray 		else
246ca370815SWilliam Breathitt Gray 			port_state = ioread8(&idio24gpio->reg->ttl_in0_7);
247ca370815SWilliam Breathitt Gray 
248ca370815SWilliam Breathitt Gray 		/* store acquired bits at respective bits array offset */
24925451945SWilliam Breathitt Gray 		bits[word_index] |= (port_state << word_offset) & word_mask;
250ca370815SWilliam Breathitt Gray 	}
251ca370815SWilliam Breathitt Gray 
252ca370815SWilliam Breathitt Gray 	return 0;
253ca370815SWilliam Breathitt Gray }
254ca370815SWilliam Breathitt Gray 
25558556204SWilliam Breathitt Gray static void idio_24_gpio_set(struct gpio_chip *chip, unsigned int offset,
25658556204SWilliam Breathitt Gray 	int value)
25758556204SWilliam Breathitt Gray {
25858556204SWilliam Breathitt Gray 	struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
25958556204SWilliam Breathitt Gray 	const unsigned long out_mode_mask = BIT(1);
26058556204SWilliam Breathitt Gray 	void __iomem *base;
26158556204SWilliam Breathitt Gray 	const unsigned int mask = BIT(offset % 8);
26258556204SWilliam Breathitt Gray 	unsigned long flags;
26358556204SWilliam Breathitt Gray 	unsigned int out_state;
26458556204SWilliam Breathitt Gray 
26558556204SWilliam Breathitt Gray 	/* Isolated Inputs */
26658556204SWilliam Breathitt Gray 	if (offset > 23 && offset < 48)
26758556204SWilliam Breathitt Gray 		return;
26858556204SWilliam Breathitt Gray 
26958556204SWilliam Breathitt Gray 	/* TTL/CMOS Inputs */
27058556204SWilliam Breathitt Gray 	if (offset > 47 && !(ioread8(&idio24gpio->reg->ctl) & out_mode_mask))
27158556204SWilliam Breathitt Gray 		return;
27258556204SWilliam Breathitt Gray 
27358556204SWilliam Breathitt Gray 	/* TTL/CMOS Outputs */
27458556204SWilliam Breathitt Gray 	if (offset > 47)
27558556204SWilliam Breathitt Gray 		base = &idio24gpio->reg->ttl_out0_7;
27658556204SWilliam Breathitt Gray 	/* FET Outputs */
27758556204SWilliam Breathitt Gray 	else if (offset > 15)
27858556204SWilliam Breathitt Gray 		base = &idio24gpio->reg->out16_23;
27958556204SWilliam Breathitt Gray 	else if (offset > 7)
28058556204SWilliam Breathitt Gray 		base = &idio24gpio->reg->out8_15;
28158556204SWilliam Breathitt Gray 	else
28258556204SWilliam Breathitt Gray 		base = &idio24gpio->reg->out0_7;
28358556204SWilliam Breathitt Gray 
28458556204SWilliam Breathitt Gray 	raw_spin_lock_irqsave(&idio24gpio->lock, flags);
28558556204SWilliam Breathitt Gray 
28658556204SWilliam Breathitt Gray 	if (value)
28758556204SWilliam Breathitt Gray 		out_state = ioread8(base) | mask;
28858556204SWilliam Breathitt Gray 	else
28958556204SWilliam Breathitt Gray 		out_state = ioread8(base) & ~mask;
29058556204SWilliam Breathitt Gray 
29158556204SWilliam Breathitt Gray 	iowrite8(out_state, base);
29258556204SWilliam Breathitt Gray 
29358556204SWilliam Breathitt Gray 	raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
29458556204SWilliam Breathitt Gray }
29558556204SWilliam Breathitt Gray 
296ca370815SWilliam Breathitt Gray static void idio_24_gpio_set_multiple(struct gpio_chip *chip,
297ca370815SWilliam Breathitt Gray 	unsigned long *mask, unsigned long *bits)
298ca370815SWilliam Breathitt Gray {
299ca370815SWilliam Breathitt Gray 	struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
300ca370815SWilliam Breathitt Gray 	size_t i;
301ca370815SWilliam Breathitt Gray 	unsigned long bits_offset;
302ca370815SWilliam Breathitt Gray 	unsigned long gpio_mask;
303ca370815SWilliam Breathitt Gray 	const unsigned int gpio_reg_size = 8;
304ca370815SWilliam Breathitt Gray 	const unsigned long port_mask = GENMASK(gpio_reg_size, 0);
305ca370815SWilliam Breathitt Gray 	unsigned long flags;
306ca370815SWilliam Breathitt Gray 	unsigned int out_state;
307304440aaSWilliam Breathitt Gray 	void __iomem *ports[] = {
308304440aaSWilliam Breathitt Gray 		&idio24gpio->reg->out0_7, &idio24gpio->reg->out8_15,
309304440aaSWilliam Breathitt Gray 		&idio24gpio->reg->out16_23
310ca370815SWilliam Breathitt Gray 	};
311ca370815SWilliam Breathitt Gray 	const unsigned long out_mode_mask = BIT(1);
312ca370815SWilliam Breathitt Gray 	const unsigned int ttl_offset = 48;
313ca370815SWilliam Breathitt Gray 	const size_t ttl_i = BIT_WORD(ttl_offset);
314ca370815SWilliam Breathitt Gray 	const unsigned int word_offset = ttl_offset % BITS_PER_LONG;
315ca370815SWilliam Breathitt Gray 	const unsigned long ttl_mask = (mask[ttl_i] >> word_offset) & port_mask;
316ca370815SWilliam Breathitt Gray 	const unsigned long ttl_bits = (bits[ttl_i] >> word_offset) & ttl_mask;
317ca370815SWilliam Breathitt Gray 
318ca370815SWilliam Breathitt Gray 	/* set bits are processed a gpio port register at a time */
319ca370815SWilliam Breathitt Gray 	for (i = 0; i < ARRAY_SIZE(ports); i++) {
320ca370815SWilliam Breathitt Gray 		/* gpio offset in bits array */
321ca370815SWilliam Breathitt Gray 		bits_offset = i * gpio_reg_size;
322ca370815SWilliam Breathitt Gray 
323ca370815SWilliam Breathitt Gray 		/* check if any set bits for current port */
324ca370815SWilliam Breathitt Gray 		gpio_mask = (*mask >> bits_offset) & port_mask;
325ca370815SWilliam Breathitt Gray 		if (!gpio_mask) {
326ca370815SWilliam Breathitt Gray 			/* no set bits for this port so move on to next port */
327ca370815SWilliam Breathitt Gray 			continue;
328ca370815SWilliam Breathitt Gray 		}
329ca370815SWilliam Breathitt Gray 
330ca370815SWilliam Breathitt Gray 		raw_spin_lock_irqsave(&idio24gpio->lock, flags);
331ca370815SWilliam Breathitt Gray 
332ca370815SWilliam Breathitt Gray 		/* process output lines */
333304440aaSWilliam Breathitt Gray 		out_state = ioread8(ports[i]) & ~gpio_mask;
334ca370815SWilliam Breathitt Gray 		out_state |= (*bits >> bits_offset) & gpio_mask;
335304440aaSWilliam Breathitt Gray 		iowrite8(out_state, ports[i]);
336ca370815SWilliam Breathitt Gray 
337ca370815SWilliam Breathitt Gray 		raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
338ca370815SWilliam Breathitt Gray 	}
339ca370815SWilliam Breathitt Gray 
340ca370815SWilliam Breathitt Gray 	/* check if setting TTL lines and if they are in output mode */
341ca370815SWilliam Breathitt Gray 	if (!ttl_mask || !(ioread8(&idio24gpio->reg->ctl) & out_mode_mask))
342ca370815SWilliam Breathitt Gray 		return;
343ca370815SWilliam Breathitt Gray 
344ca370815SWilliam Breathitt Gray 	/* handle TTL output */
345ca370815SWilliam Breathitt Gray 	raw_spin_lock_irqsave(&idio24gpio->lock, flags);
346ca370815SWilliam Breathitt Gray 
347ca370815SWilliam Breathitt Gray 	/* process output lines */
348ca370815SWilliam Breathitt Gray 	out_state = ioread8(&idio24gpio->reg->ttl_out0_7) & ~ttl_mask;
349ca370815SWilliam Breathitt Gray 	out_state |= ttl_bits;
350ca370815SWilliam Breathitt Gray 	iowrite8(out_state, &idio24gpio->reg->ttl_out0_7);
351ca370815SWilliam Breathitt Gray 
352ca370815SWilliam Breathitt Gray 	raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
353ca370815SWilliam Breathitt Gray }
354ca370815SWilliam Breathitt Gray 
35558556204SWilliam Breathitt Gray static void idio_24_irq_ack(struct irq_data *data)
35658556204SWilliam Breathitt Gray {
35758556204SWilliam Breathitt Gray }
35858556204SWilliam Breathitt Gray 
35958556204SWilliam Breathitt Gray static void idio_24_irq_mask(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 	const unsigned long bit_offset = irqd_to_hwirq(data) - 24;
36558556204SWilliam Breathitt Gray 	unsigned char new_irq_mask;
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 	idio24gpio->irq_mask &= BIT(bit_offset);
37258556204SWilliam Breathitt Gray 	new_irq_mask = idio24gpio->irq_mask >> bank_offset;
37358556204SWilliam Breathitt Gray 
37458556204SWilliam Breathitt Gray 	if (!new_irq_mask) {
37558556204SWilliam Breathitt Gray 		cos_enable_state = ioread8(&idio24gpio->reg->cos_enable);
37658556204SWilliam Breathitt Gray 
37758556204SWilliam Breathitt Gray 		/* Disable Rising Edge detection */
37858556204SWilliam Breathitt Gray 		cos_enable_state &= ~BIT(bank_offset);
37958556204SWilliam Breathitt Gray 		/* Disable 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 void idio_24_irq_unmask(struct irq_data *data)
38958556204SWilliam Breathitt Gray {
39058556204SWilliam Breathitt Gray 	struct gpio_chip *const chip = irq_data_get_irq_chip_data(data);
39158556204SWilliam Breathitt Gray 	struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
39258556204SWilliam Breathitt Gray 	unsigned long flags;
39358556204SWilliam Breathitt Gray 	unsigned char prev_irq_mask;
39458556204SWilliam Breathitt Gray 	const unsigned long bit_offset = irqd_to_hwirq(data) - 24;
39558556204SWilliam Breathitt Gray 	const unsigned long bank_offset = bit_offset/8 * 8;
39658556204SWilliam Breathitt Gray 	unsigned char cos_enable_state;
39758556204SWilliam Breathitt Gray 
39858556204SWilliam Breathitt Gray 	raw_spin_lock_irqsave(&idio24gpio->lock, flags);
39958556204SWilliam Breathitt Gray 
40058556204SWilliam Breathitt Gray 	prev_irq_mask = idio24gpio->irq_mask >> bank_offset;
40158556204SWilliam Breathitt Gray 	idio24gpio->irq_mask |= BIT(bit_offset);
40258556204SWilliam Breathitt Gray 
40358556204SWilliam Breathitt Gray 	if (!prev_irq_mask) {
40458556204SWilliam Breathitt Gray 		cos_enable_state = ioread8(&idio24gpio->reg->cos_enable);
40558556204SWilliam Breathitt Gray 
40658556204SWilliam Breathitt Gray 		/* Enable Rising Edge detection */
40758556204SWilliam Breathitt Gray 		cos_enable_state |= BIT(bank_offset);
40858556204SWilliam Breathitt Gray 		/* Enable Falling Edge detection */
40958556204SWilliam Breathitt Gray 		cos_enable_state |= BIT(bank_offset + 4);
41058556204SWilliam Breathitt Gray 
41158556204SWilliam Breathitt Gray 		iowrite8(cos_enable_state, &idio24gpio->reg->cos_enable);
41258556204SWilliam Breathitt Gray 	}
41358556204SWilliam Breathitt Gray 
41458556204SWilliam Breathitt Gray 	raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
41558556204SWilliam Breathitt Gray }
41658556204SWilliam Breathitt Gray 
41758556204SWilliam Breathitt Gray static int idio_24_irq_set_type(struct irq_data *data, unsigned int flow_type)
41858556204SWilliam Breathitt Gray {
41958556204SWilliam Breathitt Gray 	/* The only valid irq types are none and both-edges */
42058556204SWilliam Breathitt Gray 	if (flow_type != IRQ_TYPE_NONE &&
42158556204SWilliam Breathitt Gray 		(flow_type & IRQ_TYPE_EDGE_BOTH) != IRQ_TYPE_EDGE_BOTH)
42258556204SWilliam Breathitt Gray 		return -EINVAL;
42358556204SWilliam Breathitt Gray 
42458556204SWilliam Breathitt Gray 	return 0;
42558556204SWilliam Breathitt Gray }
42658556204SWilliam Breathitt Gray 
42758556204SWilliam Breathitt Gray static struct irq_chip idio_24_irqchip = {
42858556204SWilliam Breathitt Gray 	.name = "pcie-idio-24",
42958556204SWilliam Breathitt Gray 	.irq_ack = idio_24_irq_ack,
43058556204SWilliam Breathitt Gray 	.irq_mask = idio_24_irq_mask,
43158556204SWilliam Breathitt Gray 	.irq_unmask = idio_24_irq_unmask,
43258556204SWilliam Breathitt Gray 	.irq_set_type = idio_24_irq_set_type
43358556204SWilliam Breathitt Gray };
43458556204SWilliam Breathitt Gray 
43558556204SWilliam Breathitt Gray static irqreturn_t idio_24_irq_handler(int irq, void *dev_id)
43658556204SWilliam Breathitt Gray {
43758556204SWilliam Breathitt Gray 	struct idio_24_gpio *const idio24gpio = dev_id;
43858556204SWilliam Breathitt Gray 	unsigned long irq_status;
43958556204SWilliam Breathitt Gray 	struct gpio_chip *const chip = &idio24gpio->chip;
44058556204SWilliam Breathitt Gray 	unsigned long irq_mask;
44158556204SWilliam Breathitt Gray 	int gpio;
44258556204SWilliam Breathitt Gray 
44358556204SWilliam Breathitt Gray 	raw_spin_lock(&idio24gpio->lock);
44458556204SWilliam Breathitt Gray 
44558556204SWilliam Breathitt Gray 	/* Read Change-Of-State status */
44658556204SWilliam Breathitt Gray 	irq_status = ioread32(&idio24gpio->reg->cos0_7);
44758556204SWilliam Breathitt Gray 
44858556204SWilliam Breathitt Gray 	raw_spin_unlock(&idio24gpio->lock);
44958556204SWilliam Breathitt Gray 
45058556204SWilliam Breathitt Gray 	/* Make sure our device generated IRQ */
45158556204SWilliam Breathitt Gray 	if (!irq_status)
45258556204SWilliam Breathitt Gray 		return IRQ_NONE;
45358556204SWilliam Breathitt Gray 
45458556204SWilliam Breathitt Gray 	/* Handle only unmasked IRQ */
45558556204SWilliam Breathitt Gray 	irq_mask = idio24gpio->irq_mask & irq_status;
45658556204SWilliam Breathitt Gray 
45758556204SWilliam Breathitt Gray 	for_each_set_bit(gpio, &irq_mask, chip->ngpio - 24)
45858556204SWilliam Breathitt Gray 		generic_handle_irq(irq_find_mapping(chip->irq.domain,
45958556204SWilliam Breathitt Gray 			gpio + 24));
46058556204SWilliam Breathitt Gray 
46158556204SWilliam Breathitt Gray 	raw_spin_lock(&idio24gpio->lock);
46258556204SWilliam Breathitt Gray 
46358556204SWilliam Breathitt Gray 	/* Clear Change-Of-State status */
46458556204SWilliam Breathitt Gray 	iowrite32(irq_status, &idio24gpio->reg->cos0_7);
46558556204SWilliam Breathitt Gray 
46658556204SWilliam Breathitt Gray 	raw_spin_unlock(&idio24gpio->lock);
46758556204SWilliam Breathitt Gray 
46858556204SWilliam Breathitt Gray 	return IRQ_HANDLED;
46958556204SWilliam Breathitt Gray }
47058556204SWilliam Breathitt Gray 
47158556204SWilliam Breathitt Gray #define IDIO_24_NGPIO 56
47258556204SWilliam Breathitt Gray static const char *idio_24_names[IDIO_24_NGPIO] = {
47358556204SWilliam Breathitt Gray 	"OUT0", "OUT1", "OUT2", "OUT3", "OUT4", "OUT5", "OUT6", "OUT7",
47458556204SWilliam Breathitt Gray 	"OUT8", "OUT9", "OUT10", "OUT11", "OUT12", "OUT13", "OUT14", "OUT15",
47558556204SWilliam Breathitt Gray 	"OUT16", "OUT17", "OUT18", "OUT19", "OUT20", "OUT21", "OUT22", "OUT23",
47658556204SWilliam Breathitt Gray 	"IIN0", "IIN1", "IIN2", "IIN3", "IIN4", "IIN5", "IIN6", "IIN7",
47758556204SWilliam Breathitt Gray 	"IIN8", "IIN9", "IIN10", "IIN11", "IIN12", "IIN13", "IIN14", "IIN15",
47858556204SWilliam Breathitt Gray 	"IIN16", "IIN17", "IIN18", "IIN19", "IIN20", "IIN21", "IIN22", "IIN23",
47958556204SWilliam Breathitt Gray 	"TTL0", "TTL1", "TTL2", "TTL3", "TTL4", "TTL5", "TTL6", "TTL7"
48058556204SWilliam Breathitt Gray };
48158556204SWilliam Breathitt Gray 
48258556204SWilliam Breathitt Gray static int idio_24_probe(struct pci_dev *pdev, const struct pci_device_id *id)
48358556204SWilliam Breathitt Gray {
48458556204SWilliam Breathitt Gray 	struct device *const dev = &pdev->dev;
48558556204SWilliam Breathitt Gray 	struct idio_24_gpio *idio24gpio;
48658556204SWilliam Breathitt Gray 	int err;
48758556204SWilliam Breathitt Gray 	const size_t pci_bar_index = 2;
48858556204SWilliam Breathitt Gray 	const char *const name = pci_name(pdev);
48958556204SWilliam Breathitt Gray 
49058556204SWilliam Breathitt Gray 	idio24gpio = devm_kzalloc(dev, sizeof(*idio24gpio), GFP_KERNEL);
49158556204SWilliam Breathitt Gray 	if (!idio24gpio)
49258556204SWilliam Breathitt Gray 		return -ENOMEM;
49358556204SWilliam Breathitt Gray 
49458556204SWilliam Breathitt Gray 	err = pcim_enable_device(pdev);
49558556204SWilliam Breathitt Gray 	if (err) {
49658556204SWilliam Breathitt Gray 		dev_err(dev, "Failed to enable PCI device (%d)\n", err);
49758556204SWilliam Breathitt Gray 		return err;
49858556204SWilliam Breathitt Gray 	}
49958556204SWilliam Breathitt Gray 
50058556204SWilliam Breathitt Gray 	err = pcim_iomap_regions(pdev, BIT(pci_bar_index), name);
50158556204SWilliam Breathitt Gray 	if (err) {
50258556204SWilliam Breathitt Gray 		dev_err(dev, "Unable to map PCI I/O addresses (%d)\n", err);
50358556204SWilliam Breathitt Gray 		return err;
50458556204SWilliam Breathitt Gray 	}
50558556204SWilliam Breathitt Gray 
50658556204SWilliam Breathitt Gray 	idio24gpio->reg = pcim_iomap_table(pdev)[pci_bar_index];
50758556204SWilliam Breathitt Gray 
50858556204SWilliam Breathitt Gray 	idio24gpio->chip.label = name;
50958556204SWilliam Breathitt Gray 	idio24gpio->chip.parent = dev;
51058556204SWilliam Breathitt Gray 	idio24gpio->chip.owner = THIS_MODULE;
51158556204SWilliam Breathitt Gray 	idio24gpio->chip.base = -1;
51258556204SWilliam Breathitt Gray 	idio24gpio->chip.ngpio = IDIO_24_NGPIO;
51358556204SWilliam Breathitt Gray 	idio24gpio->chip.names = idio_24_names;
51458556204SWilliam Breathitt Gray 	idio24gpio->chip.get_direction = idio_24_gpio_get_direction;
51558556204SWilliam Breathitt Gray 	idio24gpio->chip.direction_input = idio_24_gpio_direction_input;
51658556204SWilliam Breathitt Gray 	idio24gpio->chip.direction_output = idio_24_gpio_direction_output;
51758556204SWilliam Breathitt Gray 	idio24gpio->chip.get = idio_24_gpio_get;
518ca370815SWilliam Breathitt Gray 	idio24gpio->chip.get_multiple = idio_24_gpio_get_multiple;
51958556204SWilliam Breathitt Gray 	idio24gpio->chip.set = idio_24_gpio_set;
520ca370815SWilliam Breathitt Gray 	idio24gpio->chip.set_multiple = idio_24_gpio_set_multiple;
52158556204SWilliam Breathitt Gray 
52258556204SWilliam Breathitt Gray 	raw_spin_lock_init(&idio24gpio->lock);
52358556204SWilliam Breathitt Gray 
52458556204SWilliam Breathitt Gray 	/* Software board reset */
52558556204SWilliam Breathitt Gray 	iowrite8(0, &idio24gpio->reg->soft_reset);
52658556204SWilliam Breathitt Gray 
52758556204SWilliam Breathitt Gray 	err = devm_gpiochip_add_data(dev, &idio24gpio->chip, idio24gpio);
52858556204SWilliam Breathitt Gray 	if (err) {
52958556204SWilliam Breathitt Gray 		dev_err(dev, "GPIO registering failed (%d)\n", err);
53058556204SWilliam Breathitt Gray 		return err;
53158556204SWilliam Breathitt Gray 	}
53258556204SWilliam Breathitt Gray 
53358556204SWilliam Breathitt Gray 	err = gpiochip_irqchip_add(&idio24gpio->chip, &idio_24_irqchip, 0,
53458556204SWilliam Breathitt Gray 		handle_edge_irq, IRQ_TYPE_NONE);
53558556204SWilliam Breathitt Gray 	if (err) {
53658556204SWilliam Breathitt Gray 		dev_err(dev, "Could not add irqchip (%d)\n", err);
53758556204SWilliam Breathitt Gray 		return err;
53858556204SWilliam Breathitt Gray 	}
53958556204SWilliam Breathitt Gray 
54058556204SWilliam Breathitt Gray 	err = devm_request_irq(dev, pdev->irq, idio_24_irq_handler, IRQF_SHARED,
54158556204SWilliam Breathitt Gray 		name, idio24gpio);
54258556204SWilliam Breathitt Gray 	if (err) {
54358556204SWilliam Breathitt Gray 		dev_err(dev, "IRQ handler registering failed (%d)\n", err);
54458556204SWilliam Breathitt Gray 		return err;
54558556204SWilliam Breathitt Gray 	}
54658556204SWilliam Breathitt Gray 
54758556204SWilliam Breathitt Gray 	return 0;
54858556204SWilliam Breathitt Gray }
54958556204SWilliam Breathitt Gray 
55058556204SWilliam Breathitt Gray static const struct pci_device_id idio_24_pci_dev_id[] = {
55158556204SWilliam Breathitt Gray 	{ PCI_DEVICE(0x494F, 0x0FD0) }, { PCI_DEVICE(0x494F, 0x0BD0) },
55258556204SWilliam Breathitt Gray 	{ PCI_DEVICE(0x494F, 0x07D0) }, { PCI_DEVICE(0x494F, 0x0FC0) },
55358556204SWilliam Breathitt Gray 	{ 0 }
55458556204SWilliam Breathitt Gray };
55558556204SWilliam Breathitt Gray MODULE_DEVICE_TABLE(pci, idio_24_pci_dev_id);
55658556204SWilliam Breathitt Gray 
55758556204SWilliam Breathitt Gray static struct pci_driver idio_24_driver = {
55858556204SWilliam Breathitt Gray 	.name = "pcie-idio-24",
55958556204SWilliam Breathitt Gray 	.id_table = idio_24_pci_dev_id,
56058556204SWilliam Breathitt Gray 	.probe = idio_24_probe
56158556204SWilliam Breathitt Gray };
56258556204SWilliam Breathitt Gray 
56358556204SWilliam Breathitt Gray module_pci_driver(idio_24_driver);
56458556204SWilliam Breathitt Gray 
56558556204SWilliam Breathitt Gray MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
56658556204SWilliam Breathitt Gray MODULE_DESCRIPTION("ACCES PCIe-IDIO-24 GPIO driver");
56758556204SWilliam Breathitt Gray MODULE_LICENSE("GPL v2");
568