xref: /openbmc/linux/drivers/gpio/gpio-pcie-idio-24.c (revision e026646c178d8292de563fbecc247bada059c282)
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)
10758556204SWilliam Breathitt Gray 		return 0;
10858556204SWilliam Breathitt Gray 
10958556204SWilliam Breathitt Gray 	/* Isolated Inputs */
11058556204SWilliam Breathitt Gray 	if (offset < 48)
11158556204SWilliam Breathitt Gray 		return 1;
11258556204SWilliam Breathitt Gray 
11358556204SWilliam Breathitt Gray 	/* TTL/CMOS I/O */
11458556204SWilliam Breathitt Gray 	/* OUT MODE = 1 when TTL/CMOS Output Mode is set */
11558556204SWilliam Breathitt Gray 	return !(ioread8(&idio24gpio->reg->ctl) & out_mode_mask);
11658556204SWilliam Breathitt Gray }
11758556204SWilliam Breathitt Gray 
11858556204SWilliam Breathitt Gray static int idio_24_gpio_direction_input(struct gpio_chip *chip,
11958556204SWilliam Breathitt Gray 	unsigned int offset)
12058556204SWilliam Breathitt Gray {
12158556204SWilliam Breathitt Gray 	struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
12258556204SWilliam Breathitt Gray 	unsigned long flags;
12358556204SWilliam Breathitt Gray 	unsigned int ctl_state;
12458556204SWilliam Breathitt Gray 	const unsigned long out_mode_mask = BIT(1);
12558556204SWilliam Breathitt Gray 
12658556204SWilliam Breathitt Gray 	/* TTL/CMOS I/O */
12758556204SWilliam Breathitt Gray 	if (offset > 47) {
12858556204SWilliam Breathitt Gray 		raw_spin_lock_irqsave(&idio24gpio->lock, flags);
12958556204SWilliam Breathitt Gray 
13058556204SWilliam Breathitt Gray 		/* Clear TTL/CMOS Output Mode */
13158556204SWilliam Breathitt Gray 		ctl_state = ioread8(&idio24gpio->reg->ctl) & ~out_mode_mask;
13258556204SWilliam Breathitt Gray 		iowrite8(ctl_state, &idio24gpio->reg->ctl);
13358556204SWilliam Breathitt Gray 
13458556204SWilliam Breathitt Gray 		raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
13558556204SWilliam Breathitt Gray 	}
13658556204SWilliam Breathitt Gray 
13758556204SWilliam Breathitt Gray 	return 0;
13858556204SWilliam Breathitt Gray }
13958556204SWilliam Breathitt Gray 
14058556204SWilliam Breathitt Gray static int idio_24_gpio_direction_output(struct gpio_chip *chip,
14158556204SWilliam Breathitt Gray 	unsigned int offset, int value)
14258556204SWilliam Breathitt Gray {
14358556204SWilliam Breathitt Gray 	struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
14458556204SWilliam Breathitt Gray 	unsigned long flags;
14558556204SWilliam Breathitt Gray 	unsigned int ctl_state;
14658556204SWilliam Breathitt Gray 	const unsigned long out_mode_mask = BIT(1);
14758556204SWilliam Breathitt Gray 
14858556204SWilliam Breathitt Gray 	/* TTL/CMOS I/O */
14958556204SWilliam Breathitt Gray 	if (offset > 47) {
15058556204SWilliam Breathitt Gray 		raw_spin_lock_irqsave(&idio24gpio->lock, flags);
15158556204SWilliam Breathitt Gray 
15258556204SWilliam Breathitt Gray 		/* Set TTL/CMOS Output Mode */
15358556204SWilliam Breathitt Gray 		ctl_state = ioread8(&idio24gpio->reg->ctl) | out_mode_mask;
15458556204SWilliam Breathitt Gray 		iowrite8(ctl_state, &idio24gpio->reg->ctl);
15558556204SWilliam Breathitt Gray 
15658556204SWilliam Breathitt Gray 		raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
15758556204SWilliam Breathitt Gray 	}
15858556204SWilliam Breathitt Gray 
15958556204SWilliam Breathitt Gray 	chip->set(chip, offset, value);
16058556204SWilliam Breathitt Gray 	return 0;
16158556204SWilliam Breathitt Gray }
16258556204SWilliam Breathitt Gray 
16358556204SWilliam Breathitt Gray static int idio_24_gpio_get(struct gpio_chip *chip, unsigned int offset)
16458556204SWilliam Breathitt Gray {
16558556204SWilliam Breathitt Gray 	struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
16658556204SWilliam Breathitt Gray 	const unsigned long offset_mask = BIT(offset % 8);
16758556204SWilliam Breathitt Gray 	const unsigned long out_mode_mask = BIT(1);
16858556204SWilliam Breathitt Gray 
16958556204SWilliam Breathitt Gray 	/* FET Outputs */
17058556204SWilliam Breathitt Gray 	if (offset < 8)
17158556204SWilliam Breathitt Gray 		return !!(ioread8(&idio24gpio->reg->out0_7) & offset_mask);
17258556204SWilliam Breathitt Gray 
17358556204SWilliam Breathitt Gray 	if (offset < 16)
17458556204SWilliam Breathitt Gray 		return !!(ioread8(&idio24gpio->reg->out8_15) & offset_mask);
17558556204SWilliam Breathitt Gray 
17658556204SWilliam Breathitt Gray 	if (offset < 24)
17758556204SWilliam Breathitt Gray 		return !!(ioread8(&idio24gpio->reg->out16_23) & offset_mask);
17858556204SWilliam Breathitt Gray 
17958556204SWilliam Breathitt Gray 	/* Isolated Inputs */
18058556204SWilliam Breathitt Gray 	if (offset < 32)
18158556204SWilliam Breathitt Gray 		return !!(ioread8(&idio24gpio->reg->in0_7) & offset_mask);
18258556204SWilliam Breathitt Gray 
18358556204SWilliam Breathitt Gray 	if (offset < 40)
18458556204SWilliam Breathitt Gray 		return !!(ioread8(&idio24gpio->reg->in8_15) & offset_mask);
18558556204SWilliam Breathitt Gray 
18658556204SWilliam Breathitt Gray 	if (offset < 48)
18758556204SWilliam Breathitt Gray 		return !!(ioread8(&idio24gpio->reg->in16_23) & offset_mask);
18858556204SWilliam Breathitt Gray 
18958556204SWilliam Breathitt Gray 	/* TTL/CMOS Outputs */
19058556204SWilliam Breathitt Gray 	if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask)
19158556204SWilliam Breathitt Gray 		return !!(ioread8(&idio24gpio->reg->ttl_out0_7) & offset_mask);
19258556204SWilliam Breathitt Gray 
19358556204SWilliam Breathitt Gray 	/* TTL/CMOS Inputs */
19458556204SWilliam Breathitt Gray 	return !!(ioread8(&idio24gpio->reg->ttl_in0_7) & offset_mask);
19558556204SWilliam Breathitt Gray }
19658556204SWilliam Breathitt Gray 
197ca370815SWilliam Breathitt Gray static int idio_24_gpio_get_multiple(struct gpio_chip *chip,
198ca370815SWilliam Breathitt Gray 	unsigned long *mask, unsigned long *bits)
199ca370815SWilliam Breathitt Gray {
200ca370815SWilliam Breathitt Gray 	struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
201ca370815SWilliam Breathitt Gray 	size_t i;
202ca370815SWilliam Breathitt Gray 	const unsigned int gpio_reg_size = 8;
203ca370815SWilliam Breathitt Gray 	unsigned int bits_offset;
204ca370815SWilliam Breathitt Gray 	size_t word_index;
205ca370815SWilliam Breathitt Gray 	unsigned int word_offset;
206ca370815SWilliam Breathitt Gray 	unsigned long word_mask;
207ca370815SWilliam Breathitt Gray 	const unsigned long port_mask = GENMASK(gpio_reg_size - 1, 0);
208ca370815SWilliam Breathitt Gray 	unsigned long port_state;
209304440aaSWilliam Breathitt Gray 	void __iomem *ports[] = {
210304440aaSWilliam Breathitt Gray 		&idio24gpio->reg->out0_7, &idio24gpio->reg->out8_15,
211304440aaSWilliam Breathitt Gray 		&idio24gpio->reg->out16_23, &idio24gpio->reg->in0_7,
212304440aaSWilliam Breathitt Gray 		&idio24gpio->reg->in8_15, &idio24gpio->reg->in16_23,
213ca370815SWilliam Breathitt Gray 	};
214ca370815SWilliam Breathitt Gray 	const unsigned long out_mode_mask = BIT(1);
215ca370815SWilliam Breathitt Gray 
216ca370815SWilliam Breathitt Gray 	/* clear bits array to a clean slate */
217ca370815SWilliam Breathitt Gray 	bitmap_zero(bits, chip->ngpio);
218ca370815SWilliam Breathitt Gray 
219ca370815SWilliam Breathitt Gray 	/* get bits are evaluated a gpio port register at a time */
220*e026646cSWilliam Breathitt Gray 	for (i = 0; i < ARRAY_SIZE(ports) + 1; i++) {
221ca370815SWilliam Breathitt Gray 		/* gpio offset in bits array */
222ca370815SWilliam Breathitt Gray 		bits_offset = i * gpio_reg_size;
223ca370815SWilliam Breathitt Gray 
224ca370815SWilliam Breathitt Gray 		/* word index for bits array */
225ca370815SWilliam Breathitt Gray 		word_index = BIT_WORD(bits_offset);
226ca370815SWilliam Breathitt Gray 
227ca370815SWilliam Breathitt Gray 		/* gpio offset within current word of bits array */
228ca370815SWilliam Breathitt Gray 		word_offset = bits_offset % BITS_PER_LONG;
229ca370815SWilliam Breathitt Gray 
230ca370815SWilliam Breathitt Gray 		/* mask of get bits for current gpio within current word */
231ca370815SWilliam Breathitt Gray 		word_mask = mask[word_index] & (port_mask << word_offset);
232ca370815SWilliam Breathitt Gray 		if (!word_mask) {
233ca370815SWilliam Breathitt Gray 			/* no get bits in this port so skip to next one */
234ca370815SWilliam Breathitt Gray 			continue;
235ca370815SWilliam Breathitt Gray 		}
236ca370815SWilliam Breathitt Gray 
237ca370815SWilliam Breathitt Gray 		/* read bits from current gpio port (port 6 is TTL GPIO) */
238ca370815SWilliam Breathitt Gray 		if (i < 6)
239304440aaSWilliam Breathitt Gray 			port_state = ioread8(ports[i]);
240ca370815SWilliam Breathitt Gray 		else if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask)
241ca370815SWilliam Breathitt Gray 			port_state = ioread8(&idio24gpio->reg->ttl_out0_7);
242ca370815SWilliam Breathitt Gray 		else
243ca370815SWilliam Breathitt Gray 			port_state = ioread8(&idio24gpio->reg->ttl_in0_7);
244ca370815SWilliam Breathitt Gray 
245ca370815SWilliam Breathitt Gray 		/* store acquired bits at respective bits array offset */
246ca370815SWilliam Breathitt Gray 		bits[word_index] |= port_state << word_offset;
247ca370815SWilliam Breathitt Gray 	}
248ca370815SWilliam Breathitt Gray 
249ca370815SWilliam Breathitt Gray 	return 0;
250ca370815SWilliam Breathitt Gray }
251ca370815SWilliam Breathitt Gray 
25258556204SWilliam Breathitt Gray static void idio_24_gpio_set(struct gpio_chip *chip, unsigned int offset,
25358556204SWilliam Breathitt Gray 	int value)
25458556204SWilliam Breathitt Gray {
25558556204SWilliam Breathitt Gray 	struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
25658556204SWilliam Breathitt Gray 	const unsigned long out_mode_mask = BIT(1);
25758556204SWilliam Breathitt Gray 	void __iomem *base;
25858556204SWilliam Breathitt Gray 	const unsigned int mask = BIT(offset % 8);
25958556204SWilliam Breathitt Gray 	unsigned long flags;
26058556204SWilliam Breathitt Gray 	unsigned int out_state;
26158556204SWilliam Breathitt Gray 
26258556204SWilliam Breathitt Gray 	/* Isolated Inputs */
26358556204SWilliam Breathitt Gray 	if (offset > 23 && offset < 48)
26458556204SWilliam Breathitt Gray 		return;
26558556204SWilliam Breathitt Gray 
26658556204SWilliam Breathitt Gray 	/* TTL/CMOS Inputs */
26758556204SWilliam Breathitt Gray 	if (offset > 47 && !(ioread8(&idio24gpio->reg->ctl) & out_mode_mask))
26858556204SWilliam Breathitt Gray 		return;
26958556204SWilliam Breathitt Gray 
27058556204SWilliam Breathitt Gray 	/* TTL/CMOS Outputs */
27158556204SWilliam Breathitt Gray 	if (offset > 47)
27258556204SWilliam Breathitt Gray 		base = &idio24gpio->reg->ttl_out0_7;
27358556204SWilliam Breathitt Gray 	/* FET Outputs */
27458556204SWilliam Breathitt Gray 	else if (offset > 15)
27558556204SWilliam Breathitt Gray 		base = &idio24gpio->reg->out16_23;
27658556204SWilliam Breathitt Gray 	else if (offset > 7)
27758556204SWilliam Breathitt Gray 		base = &idio24gpio->reg->out8_15;
27858556204SWilliam Breathitt Gray 	else
27958556204SWilliam Breathitt Gray 		base = &idio24gpio->reg->out0_7;
28058556204SWilliam Breathitt Gray 
28158556204SWilliam Breathitt Gray 	raw_spin_lock_irqsave(&idio24gpio->lock, flags);
28258556204SWilliam Breathitt Gray 
28358556204SWilliam Breathitt Gray 	if (value)
28458556204SWilliam Breathitt Gray 		out_state = ioread8(base) | mask;
28558556204SWilliam Breathitt Gray 	else
28658556204SWilliam Breathitt Gray 		out_state = ioread8(base) & ~mask;
28758556204SWilliam Breathitt Gray 
28858556204SWilliam Breathitt Gray 	iowrite8(out_state, base);
28958556204SWilliam Breathitt Gray 
29058556204SWilliam Breathitt Gray 	raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
29158556204SWilliam Breathitt Gray }
29258556204SWilliam Breathitt Gray 
293ca370815SWilliam Breathitt Gray static void idio_24_gpio_set_multiple(struct gpio_chip *chip,
294ca370815SWilliam Breathitt Gray 	unsigned long *mask, unsigned long *bits)
295ca370815SWilliam Breathitt Gray {
296ca370815SWilliam Breathitt Gray 	struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
297ca370815SWilliam Breathitt Gray 	size_t i;
298ca370815SWilliam Breathitt Gray 	unsigned long bits_offset;
299ca370815SWilliam Breathitt Gray 	unsigned long gpio_mask;
300ca370815SWilliam Breathitt Gray 	const unsigned int gpio_reg_size = 8;
301ca370815SWilliam Breathitt Gray 	const unsigned long port_mask = GENMASK(gpio_reg_size, 0);
302ca370815SWilliam Breathitt Gray 	unsigned long flags;
303ca370815SWilliam Breathitt Gray 	unsigned int out_state;
304304440aaSWilliam Breathitt Gray 	void __iomem *ports[] = {
305304440aaSWilliam Breathitt Gray 		&idio24gpio->reg->out0_7, &idio24gpio->reg->out8_15,
306304440aaSWilliam Breathitt Gray 		&idio24gpio->reg->out16_23
307ca370815SWilliam Breathitt Gray 	};
308ca370815SWilliam Breathitt Gray 	const unsigned long out_mode_mask = BIT(1);
309ca370815SWilliam Breathitt Gray 	const unsigned int ttl_offset = 48;
310ca370815SWilliam Breathitt Gray 	const size_t ttl_i = BIT_WORD(ttl_offset);
311ca370815SWilliam Breathitt Gray 	const unsigned int word_offset = ttl_offset % BITS_PER_LONG;
312ca370815SWilliam Breathitt Gray 	const unsigned long ttl_mask = (mask[ttl_i] >> word_offset) & port_mask;
313ca370815SWilliam Breathitt Gray 	const unsigned long ttl_bits = (bits[ttl_i] >> word_offset) & ttl_mask;
314ca370815SWilliam Breathitt Gray 
315ca370815SWilliam Breathitt Gray 	/* set bits are processed a gpio port register at a time */
316ca370815SWilliam Breathitt Gray 	for (i = 0; i < ARRAY_SIZE(ports); i++) {
317ca370815SWilliam Breathitt Gray 		/* gpio offset in bits array */
318ca370815SWilliam Breathitt Gray 		bits_offset = i * gpio_reg_size;
319ca370815SWilliam Breathitt Gray 
320ca370815SWilliam Breathitt Gray 		/* check if any set bits for current port */
321ca370815SWilliam Breathitt Gray 		gpio_mask = (*mask >> bits_offset) & port_mask;
322ca370815SWilliam Breathitt Gray 		if (!gpio_mask) {
323ca370815SWilliam Breathitt Gray 			/* no set bits for this port so move on to next port */
324ca370815SWilliam Breathitt Gray 			continue;
325ca370815SWilliam Breathitt Gray 		}
326ca370815SWilliam Breathitt Gray 
327ca370815SWilliam Breathitt Gray 		raw_spin_lock_irqsave(&idio24gpio->lock, flags);
328ca370815SWilliam Breathitt Gray 
329ca370815SWilliam Breathitt Gray 		/* process output lines */
330304440aaSWilliam Breathitt Gray 		out_state = ioread8(ports[i]) & ~gpio_mask;
331ca370815SWilliam Breathitt Gray 		out_state |= (*bits >> bits_offset) & gpio_mask;
332304440aaSWilliam Breathitt Gray 		iowrite8(out_state, ports[i]);
333ca370815SWilliam Breathitt Gray 
334ca370815SWilliam Breathitt Gray 		raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
335ca370815SWilliam Breathitt Gray 	}
336ca370815SWilliam Breathitt Gray 
337ca370815SWilliam Breathitt Gray 	/* check if setting TTL lines and if they are in output mode */
338ca370815SWilliam Breathitt Gray 	if (!ttl_mask || !(ioread8(&idio24gpio->reg->ctl) & out_mode_mask))
339ca370815SWilliam Breathitt Gray 		return;
340ca370815SWilliam Breathitt Gray 
341ca370815SWilliam Breathitt Gray 	/* handle TTL output */
342ca370815SWilliam Breathitt Gray 	raw_spin_lock_irqsave(&idio24gpio->lock, flags);
343ca370815SWilliam Breathitt Gray 
344ca370815SWilliam Breathitt Gray 	/* process output lines */
345ca370815SWilliam Breathitt Gray 	out_state = ioread8(&idio24gpio->reg->ttl_out0_7) & ~ttl_mask;
346ca370815SWilliam Breathitt Gray 	out_state |= ttl_bits;
347ca370815SWilliam Breathitt Gray 	iowrite8(out_state, &idio24gpio->reg->ttl_out0_7);
348ca370815SWilliam Breathitt Gray 
349ca370815SWilliam Breathitt Gray 	raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
350ca370815SWilliam Breathitt Gray }
351ca370815SWilliam Breathitt Gray 
35258556204SWilliam Breathitt Gray static void idio_24_irq_ack(struct irq_data *data)
35358556204SWilliam Breathitt Gray {
35458556204SWilliam Breathitt Gray }
35558556204SWilliam Breathitt Gray 
35658556204SWilliam Breathitt Gray static void idio_24_irq_mask(struct irq_data *data)
35758556204SWilliam Breathitt Gray {
35858556204SWilliam Breathitt Gray 	struct gpio_chip *const chip = irq_data_get_irq_chip_data(data);
35958556204SWilliam Breathitt Gray 	struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
36058556204SWilliam Breathitt Gray 	unsigned long flags;
36158556204SWilliam Breathitt Gray 	const unsigned long bit_offset = irqd_to_hwirq(data) - 24;
36258556204SWilliam Breathitt Gray 	unsigned char new_irq_mask;
36358556204SWilliam Breathitt Gray 	const unsigned long bank_offset = bit_offset/8 * 8;
36458556204SWilliam Breathitt Gray 	unsigned char cos_enable_state;
36558556204SWilliam Breathitt Gray 
36658556204SWilliam Breathitt Gray 	raw_spin_lock_irqsave(&idio24gpio->lock, flags);
36758556204SWilliam Breathitt Gray 
36858556204SWilliam Breathitt Gray 	idio24gpio->irq_mask &= BIT(bit_offset);
36958556204SWilliam Breathitt Gray 	new_irq_mask = idio24gpio->irq_mask >> bank_offset;
37058556204SWilliam Breathitt Gray 
37158556204SWilliam Breathitt Gray 	if (!new_irq_mask) {
37258556204SWilliam Breathitt Gray 		cos_enable_state = ioread8(&idio24gpio->reg->cos_enable);
37358556204SWilliam Breathitt Gray 
37458556204SWilliam Breathitt Gray 		/* Disable Rising Edge detection */
37558556204SWilliam Breathitt Gray 		cos_enable_state &= ~BIT(bank_offset);
37658556204SWilliam Breathitt Gray 		/* Disable Falling Edge detection */
37758556204SWilliam Breathitt Gray 		cos_enable_state &= ~BIT(bank_offset + 4);
37858556204SWilliam Breathitt Gray 
37958556204SWilliam Breathitt Gray 		iowrite8(cos_enable_state, &idio24gpio->reg->cos_enable);
38058556204SWilliam Breathitt Gray 	}
38158556204SWilliam Breathitt Gray 
38258556204SWilliam Breathitt Gray 	raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
38358556204SWilliam Breathitt Gray }
38458556204SWilliam Breathitt Gray 
38558556204SWilliam Breathitt Gray static void idio_24_irq_unmask(struct irq_data *data)
38658556204SWilliam Breathitt Gray {
38758556204SWilliam Breathitt Gray 	struct gpio_chip *const chip = irq_data_get_irq_chip_data(data);
38858556204SWilliam Breathitt Gray 	struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
38958556204SWilliam Breathitt Gray 	unsigned long flags;
39058556204SWilliam Breathitt Gray 	unsigned char prev_irq_mask;
39158556204SWilliam Breathitt Gray 	const unsigned long bit_offset = irqd_to_hwirq(data) - 24;
39258556204SWilliam Breathitt Gray 	const unsigned long bank_offset = bit_offset/8 * 8;
39358556204SWilliam Breathitt Gray 	unsigned char cos_enable_state;
39458556204SWilliam Breathitt Gray 
39558556204SWilliam Breathitt Gray 	raw_spin_lock_irqsave(&idio24gpio->lock, flags);
39658556204SWilliam Breathitt Gray 
39758556204SWilliam Breathitt Gray 	prev_irq_mask = idio24gpio->irq_mask >> bank_offset;
39858556204SWilliam Breathitt Gray 	idio24gpio->irq_mask |= BIT(bit_offset);
39958556204SWilliam Breathitt Gray 
40058556204SWilliam Breathitt Gray 	if (!prev_irq_mask) {
40158556204SWilliam Breathitt Gray 		cos_enable_state = ioread8(&idio24gpio->reg->cos_enable);
40258556204SWilliam Breathitt Gray 
40358556204SWilliam Breathitt Gray 		/* Enable Rising Edge detection */
40458556204SWilliam Breathitt Gray 		cos_enable_state |= BIT(bank_offset);
40558556204SWilliam Breathitt Gray 		/* Enable Falling Edge detection */
40658556204SWilliam Breathitt Gray 		cos_enable_state |= BIT(bank_offset + 4);
40758556204SWilliam Breathitt Gray 
40858556204SWilliam Breathitt Gray 		iowrite8(cos_enable_state, &idio24gpio->reg->cos_enable);
40958556204SWilliam Breathitt Gray 	}
41058556204SWilliam Breathitt Gray 
41158556204SWilliam Breathitt Gray 	raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
41258556204SWilliam Breathitt Gray }
41358556204SWilliam Breathitt Gray 
41458556204SWilliam Breathitt Gray static int idio_24_irq_set_type(struct irq_data *data, unsigned int flow_type)
41558556204SWilliam Breathitt Gray {
41658556204SWilliam Breathitt Gray 	/* The only valid irq types are none and both-edges */
41758556204SWilliam Breathitt Gray 	if (flow_type != IRQ_TYPE_NONE &&
41858556204SWilliam Breathitt Gray 		(flow_type & IRQ_TYPE_EDGE_BOTH) != IRQ_TYPE_EDGE_BOTH)
41958556204SWilliam Breathitt Gray 		return -EINVAL;
42058556204SWilliam Breathitt Gray 
42158556204SWilliam Breathitt Gray 	return 0;
42258556204SWilliam Breathitt Gray }
42358556204SWilliam Breathitt Gray 
42458556204SWilliam Breathitt Gray static struct irq_chip idio_24_irqchip = {
42558556204SWilliam Breathitt Gray 	.name = "pcie-idio-24",
42658556204SWilliam Breathitt Gray 	.irq_ack = idio_24_irq_ack,
42758556204SWilliam Breathitt Gray 	.irq_mask = idio_24_irq_mask,
42858556204SWilliam Breathitt Gray 	.irq_unmask = idio_24_irq_unmask,
42958556204SWilliam Breathitt Gray 	.irq_set_type = idio_24_irq_set_type
43058556204SWilliam Breathitt Gray };
43158556204SWilliam Breathitt Gray 
43258556204SWilliam Breathitt Gray static irqreturn_t idio_24_irq_handler(int irq, void *dev_id)
43358556204SWilliam Breathitt Gray {
43458556204SWilliam Breathitt Gray 	struct idio_24_gpio *const idio24gpio = dev_id;
43558556204SWilliam Breathitt Gray 	unsigned long irq_status;
43658556204SWilliam Breathitt Gray 	struct gpio_chip *const chip = &idio24gpio->chip;
43758556204SWilliam Breathitt Gray 	unsigned long irq_mask;
43858556204SWilliam Breathitt Gray 	int gpio;
43958556204SWilliam Breathitt Gray 
44058556204SWilliam Breathitt Gray 	raw_spin_lock(&idio24gpio->lock);
44158556204SWilliam Breathitt Gray 
44258556204SWilliam Breathitt Gray 	/* Read Change-Of-State status */
44358556204SWilliam Breathitt Gray 	irq_status = ioread32(&idio24gpio->reg->cos0_7);
44458556204SWilliam Breathitt Gray 
44558556204SWilliam Breathitt Gray 	raw_spin_unlock(&idio24gpio->lock);
44658556204SWilliam Breathitt Gray 
44758556204SWilliam Breathitt Gray 	/* Make sure our device generated IRQ */
44858556204SWilliam Breathitt Gray 	if (!irq_status)
44958556204SWilliam Breathitt Gray 		return IRQ_NONE;
45058556204SWilliam Breathitt Gray 
45158556204SWilliam Breathitt Gray 	/* Handle only unmasked IRQ */
45258556204SWilliam Breathitt Gray 	irq_mask = idio24gpio->irq_mask & irq_status;
45358556204SWilliam Breathitt Gray 
45458556204SWilliam Breathitt Gray 	for_each_set_bit(gpio, &irq_mask, chip->ngpio - 24)
45558556204SWilliam Breathitt Gray 		generic_handle_irq(irq_find_mapping(chip->irq.domain,
45658556204SWilliam Breathitt Gray 			gpio + 24));
45758556204SWilliam Breathitt Gray 
45858556204SWilliam Breathitt Gray 	raw_spin_lock(&idio24gpio->lock);
45958556204SWilliam Breathitt Gray 
46058556204SWilliam Breathitt Gray 	/* Clear Change-Of-State status */
46158556204SWilliam Breathitt Gray 	iowrite32(irq_status, &idio24gpio->reg->cos0_7);
46258556204SWilliam Breathitt Gray 
46358556204SWilliam Breathitt Gray 	raw_spin_unlock(&idio24gpio->lock);
46458556204SWilliam Breathitt Gray 
46558556204SWilliam Breathitt Gray 	return IRQ_HANDLED;
46658556204SWilliam Breathitt Gray }
46758556204SWilliam Breathitt Gray 
46858556204SWilliam Breathitt Gray #define IDIO_24_NGPIO 56
46958556204SWilliam Breathitt Gray static const char *idio_24_names[IDIO_24_NGPIO] = {
47058556204SWilliam Breathitt Gray 	"OUT0", "OUT1", "OUT2", "OUT3", "OUT4", "OUT5", "OUT6", "OUT7",
47158556204SWilliam Breathitt Gray 	"OUT8", "OUT9", "OUT10", "OUT11", "OUT12", "OUT13", "OUT14", "OUT15",
47258556204SWilliam Breathitt Gray 	"OUT16", "OUT17", "OUT18", "OUT19", "OUT20", "OUT21", "OUT22", "OUT23",
47358556204SWilliam Breathitt Gray 	"IIN0", "IIN1", "IIN2", "IIN3", "IIN4", "IIN5", "IIN6", "IIN7",
47458556204SWilliam Breathitt Gray 	"IIN8", "IIN9", "IIN10", "IIN11", "IIN12", "IIN13", "IIN14", "IIN15",
47558556204SWilliam Breathitt Gray 	"IIN16", "IIN17", "IIN18", "IIN19", "IIN20", "IIN21", "IIN22", "IIN23",
47658556204SWilliam Breathitt Gray 	"TTL0", "TTL1", "TTL2", "TTL3", "TTL4", "TTL5", "TTL6", "TTL7"
47758556204SWilliam Breathitt Gray };
47858556204SWilliam Breathitt Gray 
47958556204SWilliam Breathitt Gray static int idio_24_probe(struct pci_dev *pdev, const struct pci_device_id *id)
48058556204SWilliam Breathitt Gray {
48158556204SWilliam Breathitt Gray 	struct device *const dev = &pdev->dev;
48258556204SWilliam Breathitt Gray 	struct idio_24_gpio *idio24gpio;
48358556204SWilliam Breathitt Gray 	int err;
48458556204SWilliam Breathitt Gray 	const size_t pci_bar_index = 2;
48558556204SWilliam Breathitt Gray 	const char *const name = pci_name(pdev);
48658556204SWilliam Breathitt Gray 
48758556204SWilliam Breathitt Gray 	idio24gpio = devm_kzalloc(dev, sizeof(*idio24gpio), GFP_KERNEL);
48858556204SWilliam Breathitt Gray 	if (!idio24gpio)
48958556204SWilliam Breathitt Gray 		return -ENOMEM;
49058556204SWilliam Breathitt Gray 
49158556204SWilliam Breathitt Gray 	err = pcim_enable_device(pdev);
49258556204SWilliam Breathitt Gray 	if (err) {
49358556204SWilliam Breathitt Gray 		dev_err(dev, "Failed to enable PCI device (%d)\n", err);
49458556204SWilliam Breathitt Gray 		return err;
49558556204SWilliam Breathitt Gray 	}
49658556204SWilliam Breathitt Gray 
49758556204SWilliam Breathitt Gray 	err = pcim_iomap_regions(pdev, BIT(pci_bar_index), name);
49858556204SWilliam Breathitt Gray 	if (err) {
49958556204SWilliam Breathitt Gray 		dev_err(dev, "Unable to map PCI I/O addresses (%d)\n", err);
50058556204SWilliam Breathitt Gray 		return err;
50158556204SWilliam Breathitt Gray 	}
50258556204SWilliam Breathitt Gray 
50358556204SWilliam Breathitt Gray 	idio24gpio->reg = pcim_iomap_table(pdev)[pci_bar_index];
50458556204SWilliam Breathitt Gray 
50558556204SWilliam Breathitt Gray 	idio24gpio->chip.label = name;
50658556204SWilliam Breathitt Gray 	idio24gpio->chip.parent = dev;
50758556204SWilliam Breathitt Gray 	idio24gpio->chip.owner = THIS_MODULE;
50858556204SWilliam Breathitt Gray 	idio24gpio->chip.base = -1;
50958556204SWilliam Breathitt Gray 	idio24gpio->chip.ngpio = IDIO_24_NGPIO;
51058556204SWilliam Breathitt Gray 	idio24gpio->chip.names = idio_24_names;
51158556204SWilliam Breathitt Gray 	idio24gpio->chip.get_direction = idio_24_gpio_get_direction;
51258556204SWilliam Breathitt Gray 	idio24gpio->chip.direction_input = idio_24_gpio_direction_input;
51358556204SWilliam Breathitt Gray 	idio24gpio->chip.direction_output = idio_24_gpio_direction_output;
51458556204SWilliam Breathitt Gray 	idio24gpio->chip.get = idio_24_gpio_get;
515ca370815SWilliam Breathitt Gray 	idio24gpio->chip.get_multiple = idio_24_gpio_get_multiple;
51658556204SWilliam Breathitt Gray 	idio24gpio->chip.set = idio_24_gpio_set;
517ca370815SWilliam Breathitt Gray 	idio24gpio->chip.set_multiple = idio_24_gpio_set_multiple;
51858556204SWilliam Breathitt Gray 
51958556204SWilliam Breathitt Gray 	raw_spin_lock_init(&idio24gpio->lock);
52058556204SWilliam Breathitt Gray 
52158556204SWilliam Breathitt Gray 	/* Software board reset */
52258556204SWilliam Breathitt Gray 	iowrite8(0, &idio24gpio->reg->soft_reset);
52358556204SWilliam Breathitt Gray 
52458556204SWilliam Breathitt Gray 	err = devm_gpiochip_add_data(dev, &idio24gpio->chip, idio24gpio);
52558556204SWilliam Breathitt Gray 	if (err) {
52658556204SWilliam Breathitt Gray 		dev_err(dev, "GPIO registering failed (%d)\n", err);
52758556204SWilliam Breathitt Gray 		return err;
52858556204SWilliam Breathitt Gray 	}
52958556204SWilliam Breathitt Gray 
53058556204SWilliam Breathitt Gray 	err = gpiochip_irqchip_add(&idio24gpio->chip, &idio_24_irqchip, 0,
53158556204SWilliam Breathitt Gray 		handle_edge_irq, IRQ_TYPE_NONE);
53258556204SWilliam Breathitt Gray 	if (err) {
53358556204SWilliam Breathitt Gray 		dev_err(dev, "Could not add irqchip (%d)\n", err);
53458556204SWilliam Breathitt Gray 		return err;
53558556204SWilliam Breathitt Gray 	}
53658556204SWilliam Breathitt Gray 
53758556204SWilliam Breathitt Gray 	err = devm_request_irq(dev, pdev->irq, idio_24_irq_handler, IRQF_SHARED,
53858556204SWilliam Breathitt Gray 		name, idio24gpio);
53958556204SWilliam Breathitt Gray 	if (err) {
54058556204SWilliam Breathitt Gray 		dev_err(dev, "IRQ handler registering failed (%d)\n", err);
54158556204SWilliam Breathitt Gray 		return err;
54258556204SWilliam Breathitt Gray 	}
54358556204SWilliam Breathitt Gray 
54458556204SWilliam Breathitt Gray 	return 0;
54558556204SWilliam Breathitt Gray }
54658556204SWilliam Breathitt Gray 
54758556204SWilliam Breathitt Gray static const struct pci_device_id idio_24_pci_dev_id[] = {
54858556204SWilliam Breathitt Gray 	{ PCI_DEVICE(0x494F, 0x0FD0) }, { PCI_DEVICE(0x494F, 0x0BD0) },
54958556204SWilliam Breathitt Gray 	{ PCI_DEVICE(0x494F, 0x07D0) }, { PCI_DEVICE(0x494F, 0x0FC0) },
55058556204SWilliam Breathitt Gray 	{ 0 }
55158556204SWilliam Breathitt Gray };
55258556204SWilliam Breathitt Gray MODULE_DEVICE_TABLE(pci, idio_24_pci_dev_id);
55358556204SWilliam Breathitt Gray 
55458556204SWilliam Breathitt Gray static struct pci_driver idio_24_driver = {
55558556204SWilliam Breathitt Gray 	.name = "pcie-idio-24",
55658556204SWilliam Breathitt Gray 	.id_table = idio_24_pci_dev_id,
55758556204SWilliam Breathitt Gray 	.probe = idio_24_probe
55858556204SWilliam Breathitt Gray };
55958556204SWilliam Breathitt Gray 
56058556204SWilliam Breathitt Gray module_pci_driver(idio_24_driver);
56158556204SWilliam Breathitt Gray 
56258556204SWilliam Breathitt Gray MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
56358556204SWilliam Breathitt Gray MODULE_DESCRIPTION("ACCES PCIe-IDIO-24 GPIO driver");
56458556204SWilliam Breathitt Gray MODULE_LICENSE("GPL v2");
565