xref: /linux/drivers/gpio/gpio-ws16c48.c (revision 33f83d13ded164cd49ce2a3bd2770115abc64e6f)
11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
29c26df9bSWilliam Breathitt Gray /*
39c26df9bSWilliam Breathitt Gray  * GPIO driver for the WinSystems WS16C48
49c26df9bSWilliam Breathitt Gray  * Copyright (C) 2016 William Breathitt Gray
59c26df9bSWilliam Breathitt Gray  */
6a8ff510dSWilliam Breathitt Gray #include <linux/bitmap.h>
79c26df9bSWilliam Breathitt Gray #include <linux/device.h>
89c26df9bSWilliam Breathitt Gray #include <linux/errno.h>
99c26df9bSWilliam Breathitt Gray #include <linux/gpio/driver.h>
109c26df9bSWilliam Breathitt Gray #include <linux/io.h>
119c26df9bSWilliam Breathitt Gray #include <linux/ioport.h>
129c26df9bSWilliam Breathitt Gray #include <linux/interrupt.h>
139c26df9bSWilliam Breathitt Gray #include <linux/irqdesc.h>
14cc736607SWilliam Breathitt Gray #include <linux/isa.h>
159c26df9bSWilliam Breathitt Gray #include <linux/kernel.h>
169c26df9bSWilliam Breathitt Gray #include <linux/module.h>
179c26df9bSWilliam Breathitt Gray #include <linux/moduleparam.h>
189c26df9bSWilliam Breathitt Gray #include <linux/spinlock.h>
192c05a0f2SWilliam Breathitt Gray #include <linux/types.h>
209c26df9bSWilliam Breathitt Gray 
21*33f83d13SWilliam Breathitt Gray #define WS16C48_EXTENT 11
22cc736607SWilliam Breathitt Gray #define MAX_NUM_WS16C48 max_num_isa_dev(WS16C48_EXTENT)
23cc736607SWilliam Breathitt Gray 
24cc736607SWilliam Breathitt Gray static unsigned int base[MAX_NUM_WS16C48];
25cc736607SWilliam Breathitt Gray static unsigned int num_ws16c48;
26d759f906SDavid Howells module_param_hw_array(base, uint, ioport, &num_ws16c48, 0);
27cc736607SWilliam Breathitt Gray MODULE_PARM_DESC(base, "WinSystems WS16C48 base addresses");
28cc736607SWilliam Breathitt Gray 
29cc736607SWilliam Breathitt Gray static unsigned int irq[MAX_NUM_WS16C48];
30c95671a3SWilliam Breathitt Gray static unsigned int num_irq;
31c95671a3SWilliam Breathitt Gray module_param_hw_array(irq, uint, irq, &num_irq, 0);
32cc736607SWilliam Breathitt Gray MODULE_PARM_DESC(irq, "WinSystems WS16C48 interrupt line numbers");
339c26df9bSWilliam Breathitt Gray 
349c26df9bSWilliam Breathitt Gray /**
352c05a0f2SWilliam Breathitt Gray  * struct ws16c48_reg - device register structure
362c05a0f2SWilliam Breathitt Gray  * @port:		Port 0 through 5 I/O
372c05a0f2SWilliam Breathitt Gray  * @int_pending:	Interrupt Pending
382c05a0f2SWilliam Breathitt Gray  * @page_lock:		Register page (Bits 7-6) and I/O port lock (Bits 5-0)
392c05a0f2SWilliam Breathitt Gray  * @pol_enab_int_id:	Interrupt polarity, enable, and ID
402c05a0f2SWilliam Breathitt Gray  */
412c05a0f2SWilliam Breathitt Gray struct ws16c48_reg {
422c05a0f2SWilliam Breathitt Gray 	u8 port[6];
432c05a0f2SWilliam Breathitt Gray 	u8 int_pending;
442c05a0f2SWilliam Breathitt Gray 	u8 page_lock;
452c05a0f2SWilliam Breathitt Gray 	u8 pol_enab_int_id[3];
462c05a0f2SWilliam Breathitt Gray };
472c05a0f2SWilliam Breathitt Gray 
482c05a0f2SWilliam Breathitt Gray /**
499c26df9bSWilliam Breathitt Gray  * struct ws16c48_gpio - GPIO device private data structure
509c26df9bSWilliam Breathitt Gray  * @chip:	instance of the gpio_chip
519c26df9bSWilliam Breathitt Gray  * @io_state:	bit I/O state (whether bit is set to input or output)
529c26df9bSWilliam Breathitt Gray  * @out_state:	output bits state
539c26df9bSWilliam Breathitt Gray  * @lock:	synchronization lock to prevent I/O race conditions
549c26df9bSWilliam Breathitt Gray  * @irq_mask:	I/O bits affected by interrupts
559c26df9bSWilliam Breathitt Gray  * @flow_mask:	IRQ flow type mask for the respective I/O bits
562c05a0f2SWilliam Breathitt Gray  * @reg:	I/O address offset for the device registers
579c26df9bSWilliam Breathitt Gray  */
589c26df9bSWilliam Breathitt Gray struct ws16c48_gpio {
599c26df9bSWilliam Breathitt Gray 	struct gpio_chip chip;
609c26df9bSWilliam Breathitt Gray 	unsigned char io_state[6];
619c26df9bSWilliam Breathitt Gray 	unsigned char out_state[6];
62a0a584f0SJulia Cartwright 	raw_spinlock_t lock;
639c26df9bSWilliam Breathitt Gray 	unsigned long irq_mask;
649c26df9bSWilliam Breathitt Gray 	unsigned long flow_mask;
652c05a0f2SWilliam Breathitt Gray 	struct ws16c48_reg __iomem *reg;
669c26df9bSWilliam Breathitt Gray };
679c26df9bSWilliam Breathitt Gray 
689c26df9bSWilliam Breathitt Gray static int ws16c48_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
699c26df9bSWilliam Breathitt Gray {
709c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
719c26df9bSWilliam Breathitt Gray 	const unsigned port = offset / 8;
729c26df9bSWilliam Breathitt Gray 	const unsigned mask = BIT(offset % 8);
739c26df9bSWilliam Breathitt Gray 
74e42615ecSMatti Vaittinen 	if (ws16c48gpio->io_state[port] & mask)
75e42615ecSMatti Vaittinen 		return GPIO_LINE_DIRECTION_IN;
76e42615ecSMatti Vaittinen 
77e42615ecSMatti Vaittinen 	return GPIO_LINE_DIRECTION_OUT;
789c26df9bSWilliam Breathitt Gray }
799c26df9bSWilliam Breathitt Gray 
809c26df9bSWilliam Breathitt Gray static int ws16c48_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
819c26df9bSWilliam Breathitt Gray {
829c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
839c26df9bSWilliam Breathitt Gray 	const unsigned port = offset / 8;
849c26df9bSWilliam Breathitt Gray 	const unsigned mask = BIT(offset % 8);
859c26df9bSWilliam Breathitt Gray 	unsigned long flags;
869c26df9bSWilliam Breathitt Gray 
87a0a584f0SJulia Cartwright 	raw_spin_lock_irqsave(&ws16c48gpio->lock, flags);
889c26df9bSWilliam Breathitt Gray 
899c26df9bSWilliam Breathitt Gray 	ws16c48gpio->io_state[port] |= mask;
909c26df9bSWilliam Breathitt Gray 	ws16c48gpio->out_state[port] &= ~mask;
912c05a0f2SWilliam Breathitt Gray 	iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->reg->port + port);
929c26df9bSWilliam Breathitt Gray 
93a0a584f0SJulia Cartwright 	raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
949c26df9bSWilliam Breathitt Gray 
959c26df9bSWilliam Breathitt Gray 	return 0;
969c26df9bSWilliam Breathitt Gray }
979c26df9bSWilliam Breathitt Gray 
989c26df9bSWilliam Breathitt Gray static int ws16c48_gpio_direction_output(struct gpio_chip *chip,
999c26df9bSWilliam Breathitt Gray 	unsigned offset, int value)
1009c26df9bSWilliam Breathitt Gray {
1019c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
1029c26df9bSWilliam Breathitt Gray 	const unsigned port = offset / 8;
1039c26df9bSWilliam Breathitt Gray 	const unsigned mask = BIT(offset % 8);
1049c26df9bSWilliam Breathitt Gray 	unsigned long flags;
1059c26df9bSWilliam Breathitt Gray 
106a0a584f0SJulia Cartwright 	raw_spin_lock_irqsave(&ws16c48gpio->lock, flags);
1079c26df9bSWilliam Breathitt Gray 
1089c26df9bSWilliam Breathitt Gray 	ws16c48gpio->io_state[port] &= ~mask;
1099c26df9bSWilliam Breathitt Gray 	if (value)
1109c26df9bSWilliam Breathitt Gray 		ws16c48gpio->out_state[port] |= mask;
1119c26df9bSWilliam Breathitt Gray 	else
1129c26df9bSWilliam Breathitt Gray 		ws16c48gpio->out_state[port] &= ~mask;
1132c05a0f2SWilliam Breathitt Gray 	iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->reg->port + port);
1149c26df9bSWilliam Breathitt Gray 
115a0a584f0SJulia Cartwright 	raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
1169c26df9bSWilliam Breathitt Gray 
1179c26df9bSWilliam Breathitt Gray 	return 0;
1189c26df9bSWilliam Breathitt Gray }
1199c26df9bSWilliam Breathitt Gray 
1209c26df9bSWilliam Breathitt Gray static int ws16c48_gpio_get(struct gpio_chip *chip, unsigned offset)
1219c26df9bSWilliam Breathitt Gray {
1229c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
1239c26df9bSWilliam Breathitt Gray 	const unsigned port = offset / 8;
1249c26df9bSWilliam Breathitt Gray 	const unsigned mask = BIT(offset % 8);
1259c26df9bSWilliam Breathitt Gray 	unsigned long flags;
1269c26df9bSWilliam Breathitt Gray 	unsigned port_state;
1279c26df9bSWilliam Breathitt Gray 
128a0a584f0SJulia Cartwright 	raw_spin_lock_irqsave(&ws16c48gpio->lock, flags);
1299c26df9bSWilliam Breathitt Gray 
1309c26df9bSWilliam Breathitt Gray 	/* ensure that GPIO is set for input */
1319c26df9bSWilliam Breathitt Gray 	if (!(ws16c48gpio->io_state[port] & mask)) {
132a0a584f0SJulia Cartwright 		raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
1339c26df9bSWilliam Breathitt Gray 		return -EINVAL;
1349c26df9bSWilliam Breathitt Gray 	}
1359c26df9bSWilliam Breathitt Gray 
1362c05a0f2SWilliam Breathitt Gray 	port_state = ioread8(ws16c48gpio->reg->port + port);
1379c26df9bSWilliam Breathitt Gray 
138a0a584f0SJulia Cartwright 	raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
1399c26df9bSWilliam Breathitt Gray 
1409c26df9bSWilliam Breathitt Gray 	return !!(port_state & mask);
1419c26df9bSWilliam Breathitt Gray }
1429c26df9bSWilliam Breathitt Gray 
143a8ff510dSWilliam Breathitt Gray static int ws16c48_gpio_get_multiple(struct gpio_chip *chip,
144a8ff510dSWilliam Breathitt Gray 	unsigned long *mask, unsigned long *bits)
145a8ff510dSWilliam Breathitt Gray {
146a8ff510dSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
147acebb82fSWilliam Breathitt Gray 	unsigned long offset;
148acebb82fSWilliam Breathitt Gray 	unsigned long gpio_mask;
1492c05a0f2SWilliam Breathitt Gray 	size_t index;
1502c05a0f2SWilliam Breathitt Gray 	u8 __iomem *port_addr;
151a8ff510dSWilliam Breathitt Gray 	unsigned long port_state;
152a8ff510dSWilliam Breathitt Gray 
153a8ff510dSWilliam Breathitt Gray 	/* clear bits array to a clean slate */
154a8ff510dSWilliam Breathitt Gray 	bitmap_zero(bits, chip->ngpio);
155a8ff510dSWilliam Breathitt Gray 
156acebb82fSWilliam Breathitt Gray 	for_each_set_clump8(offset, gpio_mask, mask, chip->ngpio) {
1572c05a0f2SWilliam Breathitt Gray 		index = offset / 8;
1582c05a0f2SWilliam Breathitt Gray 		port_addr = ws16c48gpio->reg->port + index;
1595561a2b0SWilliam Breathitt Gray 		port_state = ioread8(port_addr) & gpio_mask;
160a8ff510dSWilliam Breathitt Gray 
161acebb82fSWilliam Breathitt Gray 		bitmap_set_value8(bits, port_state, offset);
162a8ff510dSWilliam Breathitt Gray 	}
163a8ff510dSWilliam Breathitt Gray 
164a8ff510dSWilliam Breathitt Gray 	return 0;
165a8ff510dSWilliam Breathitt Gray }
166a8ff510dSWilliam Breathitt Gray 
1679c26df9bSWilliam Breathitt Gray static void ws16c48_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
1689c26df9bSWilliam Breathitt Gray {
1699c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
1709c26df9bSWilliam Breathitt Gray 	const unsigned port = offset / 8;
1719c26df9bSWilliam Breathitt Gray 	const unsigned mask = BIT(offset % 8);
1729c26df9bSWilliam Breathitt Gray 	unsigned long flags;
1739c26df9bSWilliam Breathitt Gray 
174a0a584f0SJulia Cartwright 	raw_spin_lock_irqsave(&ws16c48gpio->lock, flags);
1759c26df9bSWilliam Breathitt Gray 
1769c26df9bSWilliam Breathitt Gray 	/* ensure that GPIO is set for output */
1779c26df9bSWilliam Breathitt Gray 	if (ws16c48gpio->io_state[port] & mask) {
178a0a584f0SJulia Cartwright 		raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
1799c26df9bSWilliam Breathitt Gray 		return;
1809c26df9bSWilliam Breathitt Gray 	}
1819c26df9bSWilliam Breathitt Gray 
1829c26df9bSWilliam Breathitt Gray 	if (value)
1839c26df9bSWilliam Breathitt Gray 		ws16c48gpio->out_state[port] |= mask;
1849c26df9bSWilliam Breathitt Gray 	else
1859c26df9bSWilliam Breathitt Gray 		ws16c48gpio->out_state[port] &= ~mask;
1862c05a0f2SWilliam Breathitt Gray 	iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->reg->port + port);
1879c26df9bSWilliam Breathitt Gray 
188a0a584f0SJulia Cartwright 	raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
1899c26df9bSWilliam Breathitt Gray }
1909c26df9bSWilliam Breathitt Gray 
19199c8ac95SWilliam Breathitt Gray static void ws16c48_gpio_set_multiple(struct gpio_chip *chip,
19299c8ac95SWilliam Breathitt Gray 	unsigned long *mask, unsigned long *bits)
19399c8ac95SWilliam Breathitt Gray {
19499c8ac95SWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
195acebb82fSWilliam Breathitt Gray 	unsigned long offset;
196acebb82fSWilliam Breathitt Gray 	unsigned long gpio_mask;
197acebb82fSWilliam Breathitt Gray 	size_t index;
1982c05a0f2SWilliam Breathitt Gray 	u8 __iomem *port_addr;
199acebb82fSWilliam Breathitt Gray 	unsigned long bitmask;
20099c8ac95SWilliam Breathitt Gray 	unsigned long flags;
20199c8ac95SWilliam Breathitt Gray 
202acebb82fSWilliam Breathitt Gray 	for_each_set_clump8(offset, gpio_mask, mask, chip->ngpio) {
203acebb82fSWilliam Breathitt Gray 		index = offset / 8;
2042c05a0f2SWilliam Breathitt Gray 		port_addr = ws16c48gpio->reg->port + index;
20599c8ac95SWilliam Breathitt Gray 
20699c8ac95SWilliam Breathitt Gray 		/* mask out GPIO configured for input */
207acebb82fSWilliam Breathitt Gray 		gpio_mask &= ~ws16c48gpio->io_state[index];
208acebb82fSWilliam Breathitt Gray 		bitmask = bitmap_get_value8(bits, offset) & gpio_mask;
20999c8ac95SWilliam Breathitt Gray 
210a0a584f0SJulia Cartwright 		raw_spin_lock_irqsave(&ws16c48gpio->lock, flags);
21199c8ac95SWilliam Breathitt Gray 
21299c8ac95SWilliam Breathitt Gray 		/* update output state data and set device gpio register */
213acebb82fSWilliam Breathitt Gray 		ws16c48gpio->out_state[index] &= ~gpio_mask;
214acebb82fSWilliam Breathitt Gray 		ws16c48gpio->out_state[index] |= bitmask;
2155561a2b0SWilliam Breathitt Gray 		iowrite8(ws16c48gpio->out_state[index], port_addr);
21699c8ac95SWilliam Breathitt Gray 
217a0a584f0SJulia Cartwright 		raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
21899c8ac95SWilliam Breathitt Gray 	}
21999c8ac95SWilliam Breathitt Gray }
22099c8ac95SWilliam Breathitt Gray 
2219c26df9bSWilliam Breathitt Gray static void ws16c48_irq_ack(struct irq_data *data)
2229c26df9bSWilliam Breathitt Gray {
2239c26df9bSWilliam Breathitt Gray 	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
2249c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
2259c26df9bSWilliam Breathitt Gray 	const unsigned long offset = irqd_to_hwirq(data);
2269c26df9bSWilliam Breathitt Gray 	const unsigned port = offset / 8;
2279c26df9bSWilliam Breathitt Gray 	const unsigned mask = BIT(offset % 8);
2289c26df9bSWilliam Breathitt Gray 	unsigned long flags;
2299c26df9bSWilliam Breathitt Gray 	unsigned port_state;
2309c26df9bSWilliam Breathitt Gray 
2319c26df9bSWilliam Breathitt Gray 	/* only the first 3 ports support interrupts */
2329c26df9bSWilliam Breathitt Gray 	if (port > 2)
2339c26df9bSWilliam Breathitt Gray 		return;
2349c26df9bSWilliam Breathitt Gray 
235a0a584f0SJulia Cartwright 	raw_spin_lock_irqsave(&ws16c48gpio->lock, flags);
2369c26df9bSWilliam Breathitt Gray 
2379c26df9bSWilliam Breathitt Gray 	port_state = ws16c48gpio->irq_mask >> (8*port);
2389c26df9bSWilliam Breathitt Gray 
2392c05a0f2SWilliam Breathitt Gray 	/* Select Register Page 2; Unlock all I/O ports */
2402c05a0f2SWilliam Breathitt Gray 	iowrite8(0x80, &ws16c48gpio->reg->page_lock);
2412c05a0f2SWilliam Breathitt Gray 
2422c05a0f2SWilliam Breathitt Gray 	/* Clear pending interrupt */
2432c05a0f2SWilliam Breathitt Gray 	iowrite8(port_state & ~mask, ws16c48gpio->reg->pol_enab_int_id + port);
2442c05a0f2SWilliam Breathitt Gray 	iowrite8(port_state | mask, ws16c48gpio->reg->pol_enab_int_id + port);
2452c05a0f2SWilliam Breathitt Gray 
2462c05a0f2SWilliam Breathitt Gray 	/* Select Register Page 3; Unlock all I/O ports */
2472c05a0f2SWilliam Breathitt Gray 	iowrite8(0xC0, &ws16c48gpio->reg->page_lock);
2489c26df9bSWilliam Breathitt Gray 
249a0a584f0SJulia Cartwright 	raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
2509c26df9bSWilliam Breathitt Gray }
2519c26df9bSWilliam Breathitt Gray 
2529c26df9bSWilliam Breathitt Gray static void ws16c48_irq_mask(struct irq_data *data)
2539c26df9bSWilliam Breathitt Gray {
2549c26df9bSWilliam Breathitt Gray 	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
2559c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
2569c26df9bSWilliam Breathitt Gray 	const unsigned long offset = irqd_to_hwirq(data);
2579c26df9bSWilliam Breathitt Gray 	const unsigned long mask = BIT(offset);
2589c26df9bSWilliam Breathitt Gray 	const unsigned port = offset / 8;
2599c26df9bSWilliam Breathitt Gray 	unsigned long flags;
2602c05a0f2SWilliam Breathitt Gray 	unsigned long port_state;
2619c26df9bSWilliam Breathitt Gray 
2629c26df9bSWilliam Breathitt Gray 	/* only the first 3 ports support interrupts */
2639c26df9bSWilliam Breathitt Gray 	if (port > 2)
2649c26df9bSWilliam Breathitt Gray 		return;
2659c26df9bSWilliam Breathitt Gray 
266a0a584f0SJulia Cartwright 	raw_spin_lock_irqsave(&ws16c48gpio->lock, flags);
2679c26df9bSWilliam Breathitt Gray 
2689c26df9bSWilliam Breathitt Gray 	ws16c48gpio->irq_mask &= ~mask;
26968903817SWilliam Breathitt Gray 	gpiochip_disable_irq(chip, offset);
2702c05a0f2SWilliam Breathitt Gray 	port_state = ws16c48gpio->irq_mask >> (8 * port);
2719c26df9bSWilliam Breathitt Gray 
2722c05a0f2SWilliam Breathitt Gray 	/* Select Register Page 2; Unlock all I/O ports */
2732c05a0f2SWilliam Breathitt Gray 	iowrite8(0x80, &ws16c48gpio->reg->page_lock);
2742c05a0f2SWilliam Breathitt Gray 
2752c05a0f2SWilliam Breathitt Gray 	/* Disable interrupt */
2762c05a0f2SWilliam Breathitt Gray 	iowrite8(port_state, ws16c48gpio->reg->pol_enab_int_id + port);
2772c05a0f2SWilliam Breathitt Gray 
2782c05a0f2SWilliam Breathitt Gray 	/* Select Register Page 3; Unlock all I/O ports */
2792c05a0f2SWilliam Breathitt Gray 	iowrite8(0xC0, &ws16c48gpio->reg->page_lock);
2809c26df9bSWilliam Breathitt Gray 
281a0a584f0SJulia Cartwright 	raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
2829c26df9bSWilliam Breathitt Gray }
2839c26df9bSWilliam Breathitt Gray 
2849c26df9bSWilliam Breathitt Gray static void ws16c48_irq_unmask(struct irq_data *data)
2859c26df9bSWilliam Breathitt Gray {
2869c26df9bSWilliam Breathitt Gray 	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
2879c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
2889c26df9bSWilliam Breathitt Gray 	const unsigned long offset = irqd_to_hwirq(data);
2899c26df9bSWilliam Breathitt Gray 	const unsigned long mask = BIT(offset);
2909c26df9bSWilliam Breathitt Gray 	const unsigned port = offset / 8;
2919c26df9bSWilliam Breathitt Gray 	unsigned long flags;
2922c05a0f2SWilliam Breathitt Gray 	unsigned long port_state;
2939c26df9bSWilliam Breathitt Gray 
2949c26df9bSWilliam Breathitt Gray 	/* only the first 3 ports support interrupts */
2959c26df9bSWilliam Breathitt Gray 	if (port > 2)
2969c26df9bSWilliam Breathitt Gray 		return;
2979c26df9bSWilliam Breathitt Gray 
298a0a584f0SJulia Cartwright 	raw_spin_lock_irqsave(&ws16c48gpio->lock, flags);
2999c26df9bSWilliam Breathitt Gray 
30068903817SWilliam Breathitt Gray 	gpiochip_enable_irq(chip, offset);
3019c26df9bSWilliam Breathitt Gray 	ws16c48gpio->irq_mask |= mask;
3022c05a0f2SWilliam Breathitt Gray 	port_state = ws16c48gpio->irq_mask >> (8 * port);
3039c26df9bSWilliam Breathitt Gray 
3042c05a0f2SWilliam Breathitt Gray 	/* Select Register Page 2; Unlock all I/O ports */
3052c05a0f2SWilliam Breathitt Gray 	iowrite8(0x80, &ws16c48gpio->reg->page_lock);
3062c05a0f2SWilliam Breathitt Gray 
3072c05a0f2SWilliam Breathitt Gray 	/* Enable interrupt */
3082c05a0f2SWilliam Breathitt Gray 	iowrite8(port_state, ws16c48gpio->reg->pol_enab_int_id + port);
3092c05a0f2SWilliam Breathitt Gray 
3102c05a0f2SWilliam Breathitt Gray 	/* Select Register Page 3; Unlock all I/O ports */
3112c05a0f2SWilliam Breathitt Gray 	iowrite8(0xC0, &ws16c48gpio->reg->page_lock);
3129c26df9bSWilliam Breathitt Gray 
313a0a584f0SJulia Cartwright 	raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
3149c26df9bSWilliam Breathitt Gray }
3159c26df9bSWilliam Breathitt Gray 
3169c26df9bSWilliam Breathitt Gray static int ws16c48_irq_set_type(struct irq_data *data, unsigned flow_type)
3179c26df9bSWilliam Breathitt Gray {
3189c26df9bSWilliam Breathitt Gray 	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
3199c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
3209c26df9bSWilliam Breathitt Gray 	const unsigned long offset = irqd_to_hwirq(data);
3219c26df9bSWilliam Breathitt Gray 	const unsigned long mask = BIT(offset);
3229c26df9bSWilliam Breathitt Gray 	const unsigned port = offset / 8;
3239c26df9bSWilliam Breathitt Gray 	unsigned long flags;
3242c05a0f2SWilliam Breathitt Gray 	unsigned long port_state;
3259c26df9bSWilliam Breathitt Gray 
3269c26df9bSWilliam Breathitt Gray 	/* only the first 3 ports support interrupts */
3279c26df9bSWilliam Breathitt Gray 	if (port > 2)
3289c26df9bSWilliam Breathitt Gray 		return -EINVAL;
3299c26df9bSWilliam Breathitt Gray 
330a0a584f0SJulia Cartwright 	raw_spin_lock_irqsave(&ws16c48gpio->lock, flags);
3319c26df9bSWilliam Breathitt Gray 
3329c26df9bSWilliam Breathitt Gray 	switch (flow_type) {
3339c26df9bSWilliam Breathitt Gray 	case IRQ_TYPE_NONE:
3349c26df9bSWilliam Breathitt Gray 		break;
3359c26df9bSWilliam Breathitt Gray 	case IRQ_TYPE_EDGE_RISING:
3369c26df9bSWilliam Breathitt Gray 		ws16c48gpio->flow_mask |= mask;
3379c26df9bSWilliam Breathitt Gray 		break;
3389c26df9bSWilliam Breathitt Gray 	case IRQ_TYPE_EDGE_FALLING:
3399c26df9bSWilliam Breathitt Gray 		ws16c48gpio->flow_mask &= ~mask;
3409c26df9bSWilliam Breathitt Gray 		break;
3419c26df9bSWilliam Breathitt Gray 	default:
342a0a584f0SJulia Cartwright 		raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
3439c26df9bSWilliam Breathitt Gray 		return -EINVAL;
3449c26df9bSWilliam Breathitt Gray 	}
3459c26df9bSWilliam Breathitt Gray 
3462c05a0f2SWilliam Breathitt Gray 	port_state = ws16c48gpio->flow_mask >> (8 * port);
3472c05a0f2SWilliam Breathitt Gray 
3482c05a0f2SWilliam Breathitt Gray 	/* Select Register Page 1; Unlock all I/O ports */
3492c05a0f2SWilliam Breathitt Gray 	iowrite8(0x40, &ws16c48gpio->reg->page_lock);
3502c05a0f2SWilliam Breathitt Gray 
3512c05a0f2SWilliam Breathitt Gray 	/* Set interrupt polarity */
3522c05a0f2SWilliam Breathitt Gray 	iowrite8(port_state, ws16c48gpio->reg->pol_enab_int_id + port);
3532c05a0f2SWilliam Breathitt Gray 
3542c05a0f2SWilliam Breathitt Gray 	/* Select Register Page 3; Unlock all I/O ports */
3552c05a0f2SWilliam Breathitt Gray 	iowrite8(0xC0, &ws16c48gpio->reg->page_lock);
3569c26df9bSWilliam Breathitt Gray 
357a0a584f0SJulia Cartwright 	raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
3589c26df9bSWilliam Breathitt Gray 
3599c26df9bSWilliam Breathitt Gray 	return 0;
3609c26df9bSWilliam Breathitt Gray }
3619c26df9bSWilliam Breathitt Gray 
36268903817SWilliam Breathitt Gray static const struct irq_chip ws16c48_irqchip = {
3639c26df9bSWilliam Breathitt Gray 	.name = "ws16c48",
3649c26df9bSWilliam Breathitt Gray 	.irq_ack = ws16c48_irq_ack,
3659c26df9bSWilliam Breathitt Gray 	.irq_mask = ws16c48_irq_mask,
3669c26df9bSWilliam Breathitt Gray 	.irq_unmask = ws16c48_irq_unmask,
36768903817SWilliam Breathitt Gray 	.irq_set_type = ws16c48_irq_set_type,
36868903817SWilliam Breathitt Gray 	.flags = IRQCHIP_IMMUTABLE,
36968903817SWilliam Breathitt Gray 	GPIOCHIP_IRQ_RESOURCE_HELPERS,
3709c26df9bSWilliam Breathitt Gray };
3719c26df9bSWilliam Breathitt Gray 
3729c26df9bSWilliam Breathitt Gray static irqreturn_t ws16c48_irq_handler(int irq, void *dev_id)
3739c26df9bSWilliam Breathitt Gray {
3749c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = dev_id;
3759c26df9bSWilliam Breathitt Gray 	struct gpio_chip *const chip = &ws16c48gpio->chip;
3762c05a0f2SWilliam Breathitt Gray 	struct ws16c48_reg __iomem *const reg = ws16c48gpio->reg;
3779c26df9bSWilliam Breathitt Gray 	unsigned long int_pending;
3789c26df9bSWilliam Breathitt Gray 	unsigned long port;
3799c26df9bSWilliam Breathitt Gray 	unsigned long int_id;
3809c26df9bSWilliam Breathitt Gray 	unsigned long gpio;
3819c26df9bSWilliam Breathitt Gray 
3822c05a0f2SWilliam Breathitt Gray 	int_pending = ioread8(&reg->int_pending) & 0x7;
3839c26df9bSWilliam Breathitt Gray 	if (!int_pending)
3849c26df9bSWilliam Breathitt Gray 		return IRQ_NONE;
3859c26df9bSWilliam Breathitt Gray 
3869c26df9bSWilliam Breathitt Gray 	/* loop until all pending interrupts are handled */
3879c26df9bSWilliam Breathitt Gray 	do {
3889c26df9bSWilliam Breathitt Gray 		for_each_set_bit(port, &int_pending, 3) {
3892c05a0f2SWilliam Breathitt Gray 			int_id = ioread8(reg->pol_enab_int_id + port);
3909c26df9bSWilliam Breathitt Gray 			for_each_set_bit(gpio, &int_id, 8)
391dbd1c54fSMarc Zyngier 				generic_handle_domain_irq(chip->irq.domain,
392dbd1c54fSMarc Zyngier 							  gpio + 8*port);
3939c26df9bSWilliam Breathitt Gray 		}
3949c26df9bSWilliam Breathitt Gray 
3952c05a0f2SWilliam Breathitt Gray 		int_pending = ioread8(&reg->int_pending) & 0x7;
3969c26df9bSWilliam Breathitt Gray 	} while (int_pending);
3979c26df9bSWilliam Breathitt Gray 
3989c26df9bSWilliam Breathitt Gray 	return IRQ_HANDLED;
3999c26df9bSWilliam Breathitt Gray }
4009c26df9bSWilliam Breathitt Gray 
4015238f60fSWilliam Breathitt Gray #define WS16C48_NGPIO 48
4025238f60fSWilliam Breathitt Gray static const char *ws16c48_names[WS16C48_NGPIO] = {
4035238f60fSWilliam Breathitt Gray 	"Port 0 Bit 0", "Port 0 Bit 1", "Port 0 Bit 2", "Port 0 Bit 3",
4045238f60fSWilliam Breathitt Gray 	"Port 0 Bit 4", "Port 0 Bit 5", "Port 0 Bit 6", "Port 0 Bit 7",
4055238f60fSWilliam Breathitt Gray 	"Port 1 Bit 0", "Port 1 Bit 1", "Port 1 Bit 2", "Port 1 Bit 3",
4065238f60fSWilliam Breathitt Gray 	"Port 1 Bit 4", "Port 1 Bit 5", "Port 1 Bit 6", "Port 1 Bit 7",
4075238f60fSWilliam Breathitt Gray 	"Port 2 Bit 0", "Port 2 Bit 1", "Port 2 Bit 2", "Port 2 Bit 3",
4085238f60fSWilliam Breathitt Gray 	"Port 2 Bit 4", "Port 2 Bit 5", "Port 2 Bit 6", "Port 2 Bit 7",
4095238f60fSWilliam Breathitt Gray 	"Port 3 Bit 0", "Port 3 Bit 1", "Port 3 Bit 2", "Port 3 Bit 3",
4105238f60fSWilliam Breathitt Gray 	"Port 3 Bit 4", "Port 3 Bit 5", "Port 3 Bit 6", "Port 3 Bit 7",
4115238f60fSWilliam Breathitt Gray 	"Port 4 Bit 0", "Port 4 Bit 1", "Port 4 Bit 2", "Port 4 Bit 3",
4125238f60fSWilliam Breathitt Gray 	"Port 4 Bit 4", "Port 4 Bit 5", "Port 4 Bit 6", "Port 4 Bit 7",
4135238f60fSWilliam Breathitt Gray 	"Port 5 Bit 0", "Port 5 Bit 1", "Port 5 Bit 2", "Port 5 Bit 3",
4145238f60fSWilliam Breathitt Gray 	"Port 5 Bit 4", "Port 5 Bit 5", "Port 5 Bit 6", "Port 5 Bit 7"
4155238f60fSWilliam Breathitt Gray };
4165238f60fSWilliam Breathitt Gray 
417fceb7ab3SLinus Walleij static int ws16c48_irq_init_hw(struct gpio_chip *gc)
418fceb7ab3SLinus Walleij {
419fceb7ab3SLinus Walleij 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(gc);
420fceb7ab3SLinus Walleij 
4212c05a0f2SWilliam Breathitt Gray 	/* Select Register Page 2; Unlock all I/O ports */
4222c05a0f2SWilliam Breathitt Gray 	iowrite8(0x80, &ws16c48gpio->reg->page_lock);
4232c05a0f2SWilliam Breathitt Gray 
4242c05a0f2SWilliam Breathitt Gray 	/* Disable interrupts for all lines */
4252c05a0f2SWilliam Breathitt Gray 	iowrite8(0, &ws16c48gpio->reg->pol_enab_int_id[0]);
4262c05a0f2SWilliam Breathitt Gray 	iowrite8(0, &ws16c48gpio->reg->pol_enab_int_id[1]);
4272c05a0f2SWilliam Breathitt Gray 	iowrite8(0, &ws16c48gpio->reg->pol_enab_int_id[2]);
4282c05a0f2SWilliam Breathitt Gray 
4292c05a0f2SWilliam Breathitt Gray 	/* Select Register Page 3; Unlock all I/O ports */
4302c05a0f2SWilliam Breathitt Gray 	iowrite8(0xC0, &ws16c48gpio->reg->page_lock);
431fceb7ab3SLinus Walleij 
432fceb7ab3SLinus Walleij 	return 0;
433fceb7ab3SLinus Walleij }
434fceb7ab3SLinus Walleij 
435cc736607SWilliam Breathitt Gray static int ws16c48_probe(struct device *dev, unsigned int id)
4369c26df9bSWilliam Breathitt Gray {
4379c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *ws16c48gpio;
4389c26df9bSWilliam Breathitt Gray 	const char *const name = dev_name(dev);
439fceb7ab3SLinus Walleij 	struct gpio_irq_chip *girq;
4409c26df9bSWilliam Breathitt Gray 	int err;
4419c26df9bSWilliam Breathitt Gray 
4429c26df9bSWilliam Breathitt Gray 	ws16c48gpio = devm_kzalloc(dev, sizeof(*ws16c48gpio), GFP_KERNEL);
4439c26df9bSWilliam Breathitt Gray 	if (!ws16c48gpio)
4449c26df9bSWilliam Breathitt Gray 		return -ENOMEM;
4459c26df9bSWilliam Breathitt Gray 
446cc736607SWilliam Breathitt Gray 	if (!devm_request_region(dev, base[id], WS16C48_EXTENT, name)) {
447148ad68bSWilliam Breathitt Gray 		dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
448cc736607SWilliam Breathitt Gray 			base[id], base[id] + WS16C48_EXTENT);
449148ad68bSWilliam Breathitt Gray 		return -EBUSY;
4509c26df9bSWilliam Breathitt Gray 	}
4519c26df9bSWilliam Breathitt Gray 
4522c05a0f2SWilliam Breathitt Gray 	ws16c48gpio->reg = devm_ioport_map(dev, base[id], WS16C48_EXTENT);
4532c05a0f2SWilliam Breathitt Gray 	if (!ws16c48gpio->reg)
4545561a2b0SWilliam Breathitt Gray 		return -ENOMEM;
4555561a2b0SWilliam Breathitt Gray 
4569c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.label = name;
4579c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.parent = dev;
4589c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.owner = THIS_MODULE;
4599c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.base = -1;
4605238f60fSWilliam Breathitt Gray 	ws16c48gpio->chip.ngpio = WS16C48_NGPIO;
4615238f60fSWilliam Breathitt Gray 	ws16c48gpio->chip.names = ws16c48_names;
4629c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.get_direction = ws16c48_gpio_get_direction;
4639c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.direction_input = ws16c48_gpio_direction_input;
4649c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.direction_output = ws16c48_gpio_direction_output;
4659c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.get = ws16c48_gpio_get;
466a8ff510dSWilliam Breathitt Gray 	ws16c48gpio->chip.get_multiple = ws16c48_gpio_get_multiple;
4679c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.set = ws16c48_gpio_set;
46899c8ac95SWilliam Breathitt Gray 	ws16c48gpio->chip.set_multiple = ws16c48_gpio_set_multiple;
4699c26df9bSWilliam Breathitt Gray 
470fceb7ab3SLinus Walleij 	girq = &ws16c48gpio->chip.irq;
47168903817SWilliam Breathitt Gray 	gpio_irq_chip_set_chip(girq, &ws16c48_irqchip);
472fceb7ab3SLinus Walleij 	/* This will let us handle the parent IRQ in the driver */
473fceb7ab3SLinus Walleij 	girq->parent_handler = NULL;
474fceb7ab3SLinus Walleij 	girq->num_parents = 0;
475fceb7ab3SLinus Walleij 	girq->parents = NULL;
476fceb7ab3SLinus Walleij 	girq->default_type = IRQ_TYPE_NONE;
477fceb7ab3SLinus Walleij 	girq->handler = handle_edge_irq;
478fceb7ab3SLinus Walleij 	girq->init_hw = ws16c48_irq_init_hw;
479fceb7ab3SLinus Walleij 
480a0a584f0SJulia Cartwright 	raw_spin_lock_init(&ws16c48gpio->lock);
4819c26df9bSWilliam Breathitt Gray 
482b4cad1bcSWilliam Breathitt Gray 	err = devm_gpiochip_add_data(dev, &ws16c48gpio->chip, ws16c48gpio);
4839c26df9bSWilliam Breathitt Gray 	if (err) {
4849c26df9bSWilliam Breathitt Gray 		dev_err(dev, "GPIO registering failed (%d)\n", err);
485148ad68bSWilliam Breathitt Gray 		return err;
4869c26df9bSWilliam Breathitt Gray 	}
4879c26df9bSWilliam Breathitt Gray 
488b4cad1bcSWilliam Breathitt Gray 	err = devm_request_irq(dev, irq[id], ws16c48_irq_handler, IRQF_SHARED,
489b4cad1bcSWilliam Breathitt Gray 		name, ws16c48gpio);
490b4cad1bcSWilliam Breathitt Gray 	if (err) {
491b4cad1bcSWilliam Breathitt Gray 		dev_err(dev, "IRQ handler registering failed (%d)\n", err);
492b4cad1bcSWilliam Breathitt Gray 		return err;
493b4cad1bcSWilliam Breathitt Gray 	}
4949c26df9bSWilliam Breathitt Gray 
4959c26df9bSWilliam Breathitt Gray 	return 0;
4969c26df9bSWilliam Breathitt Gray }
4979c26df9bSWilliam Breathitt Gray 
498cc736607SWilliam Breathitt Gray static struct isa_driver ws16c48_driver = {
499cc736607SWilliam Breathitt Gray 	.probe = ws16c48_probe,
5009c26df9bSWilliam Breathitt Gray 	.driver = {
5019c26df9bSWilliam Breathitt Gray 		.name = "ws16c48"
5029c26df9bSWilliam Breathitt Gray 	},
5039c26df9bSWilliam Breathitt Gray };
5049c26df9bSWilliam Breathitt Gray 
505c95671a3SWilliam Breathitt Gray module_isa_driver_with_irq(ws16c48_driver, num_ws16c48, num_irq);
5069c26df9bSWilliam Breathitt Gray 
5079c26df9bSWilliam Breathitt Gray MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
5089c26df9bSWilliam Breathitt Gray MODULE_DESCRIPTION("WinSystems WS16C48 GPIO driver");
50922aeddb5SWilliam Breathitt Gray MODULE_LICENSE("GPL v2");
510