xref: /linux/drivers/gpio/gpio-ws16c48.c (revision c95671a3e77ca6f1ed42e891d1fef2ef166a7fdc)
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 
212c05a0f2SWilliam Breathitt Gray #define WS16C48_EXTENT 10
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];
30*c95671a3SWilliam Breathitt Gray static unsigned int num_irq;
31*c95671a3SWilliam 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;
2692c05a0f2SWilliam Breathitt Gray 	port_state = ws16c48gpio->irq_mask >> (8 * port);
2709c26df9bSWilliam Breathitt Gray 
2712c05a0f2SWilliam Breathitt Gray 	/* Select Register Page 2; Unlock all I/O ports */
2722c05a0f2SWilliam Breathitt Gray 	iowrite8(0x80, &ws16c48gpio->reg->page_lock);
2732c05a0f2SWilliam Breathitt Gray 
2742c05a0f2SWilliam Breathitt Gray 	/* Disable interrupt */
2752c05a0f2SWilliam Breathitt Gray 	iowrite8(port_state, ws16c48gpio->reg->pol_enab_int_id + port);
2762c05a0f2SWilliam Breathitt Gray 
2772c05a0f2SWilliam Breathitt Gray 	/* Select Register Page 3; Unlock all I/O ports */
2782c05a0f2SWilliam Breathitt Gray 	iowrite8(0xC0, &ws16c48gpio->reg->page_lock);
2799c26df9bSWilliam Breathitt Gray 
280a0a584f0SJulia Cartwright 	raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
2819c26df9bSWilliam Breathitt Gray }
2829c26df9bSWilliam Breathitt Gray 
2839c26df9bSWilliam Breathitt Gray static void ws16c48_irq_unmask(struct irq_data *data)
2849c26df9bSWilliam Breathitt Gray {
2859c26df9bSWilliam Breathitt Gray 	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
2869c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
2879c26df9bSWilliam Breathitt Gray 	const unsigned long offset = irqd_to_hwirq(data);
2889c26df9bSWilliam Breathitt Gray 	const unsigned long mask = BIT(offset);
2899c26df9bSWilliam Breathitt Gray 	const unsigned port = offset / 8;
2909c26df9bSWilliam Breathitt Gray 	unsigned long flags;
2912c05a0f2SWilliam Breathitt Gray 	unsigned long port_state;
2929c26df9bSWilliam Breathitt Gray 
2939c26df9bSWilliam Breathitt Gray 	/* only the first 3 ports support interrupts */
2949c26df9bSWilliam Breathitt Gray 	if (port > 2)
2959c26df9bSWilliam Breathitt Gray 		return;
2969c26df9bSWilliam Breathitt Gray 
297a0a584f0SJulia Cartwright 	raw_spin_lock_irqsave(&ws16c48gpio->lock, flags);
2989c26df9bSWilliam Breathitt Gray 
2999c26df9bSWilliam Breathitt Gray 	ws16c48gpio->irq_mask |= mask;
3002c05a0f2SWilliam Breathitt Gray 	port_state = ws16c48gpio->irq_mask >> (8 * port);
3019c26df9bSWilliam Breathitt Gray 
3022c05a0f2SWilliam Breathitt Gray 	/* Select Register Page 2; Unlock all I/O ports */
3032c05a0f2SWilliam Breathitt Gray 	iowrite8(0x80, &ws16c48gpio->reg->page_lock);
3042c05a0f2SWilliam Breathitt Gray 
3052c05a0f2SWilliam Breathitt Gray 	/* Enable interrupt */
3062c05a0f2SWilliam Breathitt Gray 	iowrite8(port_state, ws16c48gpio->reg->pol_enab_int_id + port);
3072c05a0f2SWilliam Breathitt Gray 
3082c05a0f2SWilliam Breathitt Gray 	/* Select Register Page 3; Unlock all I/O ports */
3092c05a0f2SWilliam Breathitt Gray 	iowrite8(0xC0, &ws16c48gpio->reg->page_lock);
3109c26df9bSWilliam Breathitt Gray 
311a0a584f0SJulia Cartwright 	raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
3129c26df9bSWilliam Breathitt Gray }
3139c26df9bSWilliam Breathitt Gray 
3149c26df9bSWilliam Breathitt Gray static int ws16c48_irq_set_type(struct irq_data *data, unsigned flow_type)
3159c26df9bSWilliam Breathitt Gray {
3169c26df9bSWilliam Breathitt Gray 	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
3179c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
3189c26df9bSWilliam Breathitt Gray 	const unsigned long offset = irqd_to_hwirq(data);
3199c26df9bSWilliam Breathitt Gray 	const unsigned long mask = BIT(offset);
3209c26df9bSWilliam Breathitt Gray 	const unsigned port = offset / 8;
3219c26df9bSWilliam Breathitt Gray 	unsigned long flags;
3222c05a0f2SWilliam Breathitt Gray 	unsigned long port_state;
3239c26df9bSWilliam Breathitt Gray 
3249c26df9bSWilliam Breathitt Gray 	/* only the first 3 ports support interrupts */
3259c26df9bSWilliam Breathitt Gray 	if (port > 2)
3269c26df9bSWilliam Breathitt Gray 		return -EINVAL;
3279c26df9bSWilliam Breathitt Gray 
328a0a584f0SJulia Cartwright 	raw_spin_lock_irqsave(&ws16c48gpio->lock, flags);
3299c26df9bSWilliam Breathitt Gray 
3309c26df9bSWilliam Breathitt Gray 	switch (flow_type) {
3319c26df9bSWilliam Breathitt Gray 	case IRQ_TYPE_NONE:
3329c26df9bSWilliam Breathitt Gray 		break;
3339c26df9bSWilliam Breathitt Gray 	case IRQ_TYPE_EDGE_RISING:
3349c26df9bSWilliam Breathitt Gray 		ws16c48gpio->flow_mask |= mask;
3359c26df9bSWilliam Breathitt Gray 		break;
3369c26df9bSWilliam Breathitt Gray 	case IRQ_TYPE_EDGE_FALLING:
3379c26df9bSWilliam Breathitt Gray 		ws16c48gpio->flow_mask &= ~mask;
3389c26df9bSWilliam Breathitt Gray 		break;
3399c26df9bSWilliam Breathitt Gray 	default:
340a0a584f0SJulia Cartwright 		raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
3419c26df9bSWilliam Breathitt Gray 		return -EINVAL;
3429c26df9bSWilliam Breathitt Gray 	}
3439c26df9bSWilliam Breathitt Gray 
3442c05a0f2SWilliam Breathitt Gray 	port_state = ws16c48gpio->flow_mask >> (8 * port);
3452c05a0f2SWilliam Breathitt Gray 
3462c05a0f2SWilliam Breathitt Gray 	/* Select Register Page 1; Unlock all I/O ports */
3472c05a0f2SWilliam Breathitt Gray 	iowrite8(0x40, &ws16c48gpio->reg->page_lock);
3482c05a0f2SWilliam Breathitt Gray 
3492c05a0f2SWilliam Breathitt Gray 	/* Set interrupt polarity */
3502c05a0f2SWilliam Breathitt Gray 	iowrite8(port_state, ws16c48gpio->reg->pol_enab_int_id + port);
3512c05a0f2SWilliam Breathitt Gray 
3522c05a0f2SWilliam Breathitt Gray 	/* Select Register Page 3; Unlock all I/O ports */
3532c05a0f2SWilliam Breathitt Gray 	iowrite8(0xC0, &ws16c48gpio->reg->page_lock);
3549c26df9bSWilliam Breathitt Gray 
355a0a584f0SJulia Cartwright 	raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
3569c26df9bSWilliam Breathitt Gray 
3579c26df9bSWilliam Breathitt Gray 	return 0;
3589c26df9bSWilliam Breathitt Gray }
3599c26df9bSWilliam Breathitt Gray 
3609c26df9bSWilliam Breathitt Gray static struct irq_chip ws16c48_irqchip = {
3619c26df9bSWilliam Breathitt Gray 	.name = "ws16c48",
3629c26df9bSWilliam Breathitt Gray 	.irq_ack = ws16c48_irq_ack,
3639c26df9bSWilliam Breathitt Gray 	.irq_mask = ws16c48_irq_mask,
3649c26df9bSWilliam Breathitt Gray 	.irq_unmask = ws16c48_irq_unmask,
3659c26df9bSWilliam Breathitt Gray 	.irq_set_type = ws16c48_irq_set_type
3669c26df9bSWilliam Breathitt Gray };
3679c26df9bSWilliam Breathitt Gray 
3689c26df9bSWilliam Breathitt Gray static irqreturn_t ws16c48_irq_handler(int irq, void *dev_id)
3699c26df9bSWilliam Breathitt Gray {
3709c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = dev_id;
3719c26df9bSWilliam Breathitt Gray 	struct gpio_chip *const chip = &ws16c48gpio->chip;
3722c05a0f2SWilliam Breathitt Gray 	struct ws16c48_reg __iomem *const reg = ws16c48gpio->reg;
3739c26df9bSWilliam Breathitt Gray 	unsigned long int_pending;
3749c26df9bSWilliam Breathitt Gray 	unsigned long port;
3759c26df9bSWilliam Breathitt Gray 	unsigned long int_id;
3769c26df9bSWilliam Breathitt Gray 	unsigned long gpio;
3779c26df9bSWilliam Breathitt Gray 
3782c05a0f2SWilliam Breathitt Gray 	int_pending = ioread8(&reg->int_pending) & 0x7;
3799c26df9bSWilliam Breathitt Gray 	if (!int_pending)
3809c26df9bSWilliam Breathitt Gray 		return IRQ_NONE;
3819c26df9bSWilliam Breathitt Gray 
3829c26df9bSWilliam Breathitt Gray 	/* loop until all pending interrupts are handled */
3839c26df9bSWilliam Breathitt Gray 	do {
3849c26df9bSWilliam Breathitt Gray 		for_each_set_bit(port, &int_pending, 3) {
3852c05a0f2SWilliam Breathitt Gray 			int_id = ioread8(reg->pol_enab_int_id + port);
3869c26df9bSWilliam Breathitt Gray 			for_each_set_bit(gpio, &int_id, 8)
387dbd1c54fSMarc Zyngier 				generic_handle_domain_irq(chip->irq.domain,
388dbd1c54fSMarc Zyngier 							  gpio + 8*port);
3899c26df9bSWilliam Breathitt Gray 		}
3909c26df9bSWilliam Breathitt Gray 
3912c05a0f2SWilliam Breathitt Gray 		int_pending = ioread8(&reg->int_pending) & 0x7;
3929c26df9bSWilliam Breathitt Gray 	} while (int_pending);
3939c26df9bSWilliam Breathitt Gray 
3949c26df9bSWilliam Breathitt Gray 	return IRQ_HANDLED;
3959c26df9bSWilliam Breathitt Gray }
3969c26df9bSWilliam Breathitt Gray 
3975238f60fSWilliam Breathitt Gray #define WS16C48_NGPIO 48
3985238f60fSWilliam Breathitt Gray static const char *ws16c48_names[WS16C48_NGPIO] = {
3995238f60fSWilliam Breathitt Gray 	"Port 0 Bit 0", "Port 0 Bit 1", "Port 0 Bit 2", "Port 0 Bit 3",
4005238f60fSWilliam Breathitt Gray 	"Port 0 Bit 4", "Port 0 Bit 5", "Port 0 Bit 6", "Port 0 Bit 7",
4015238f60fSWilliam Breathitt Gray 	"Port 1 Bit 0", "Port 1 Bit 1", "Port 1 Bit 2", "Port 1 Bit 3",
4025238f60fSWilliam Breathitt Gray 	"Port 1 Bit 4", "Port 1 Bit 5", "Port 1 Bit 6", "Port 1 Bit 7",
4035238f60fSWilliam Breathitt Gray 	"Port 2 Bit 0", "Port 2 Bit 1", "Port 2 Bit 2", "Port 2 Bit 3",
4045238f60fSWilliam Breathitt Gray 	"Port 2 Bit 4", "Port 2 Bit 5", "Port 2 Bit 6", "Port 2 Bit 7",
4055238f60fSWilliam Breathitt Gray 	"Port 3 Bit 0", "Port 3 Bit 1", "Port 3 Bit 2", "Port 3 Bit 3",
4065238f60fSWilliam Breathitt Gray 	"Port 3 Bit 4", "Port 3 Bit 5", "Port 3 Bit 6", "Port 3 Bit 7",
4075238f60fSWilliam Breathitt Gray 	"Port 4 Bit 0", "Port 4 Bit 1", "Port 4 Bit 2", "Port 4 Bit 3",
4085238f60fSWilliam Breathitt Gray 	"Port 4 Bit 4", "Port 4 Bit 5", "Port 4 Bit 6", "Port 4 Bit 7",
4095238f60fSWilliam Breathitt Gray 	"Port 5 Bit 0", "Port 5 Bit 1", "Port 5 Bit 2", "Port 5 Bit 3",
4105238f60fSWilliam Breathitt Gray 	"Port 5 Bit 4", "Port 5 Bit 5", "Port 5 Bit 6", "Port 5 Bit 7"
4115238f60fSWilliam Breathitt Gray };
4125238f60fSWilliam Breathitt Gray 
413fceb7ab3SLinus Walleij static int ws16c48_irq_init_hw(struct gpio_chip *gc)
414fceb7ab3SLinus Walleij {
415fceb7ab3SLinus Walleij 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(gc);
416fceb7ab3SLinus Walleij 
4172c05a0f2SWilliam Breathitt Gray 	/* Select Register Page 2; Unlock all I/O ports */
4182c05a0f2SWilliam Breathitt Gray 	iowrite8(0x80, &ws16c48gpio->reg->page_lock);
4192c05a0f2SWilliam Breathitt Gray 
4202c05a0f2SWilliam Breathitt Gray 	/* Disable interrupts for all lines */
4212c05a0f2SWilliam Breathitt Gray 	iowrite8(0, &ws16c48gpio->reg->pol_enab_int_id[0]);
4222c05a0f2SWilliam Breathitt Gray 	iowrite8(0, &ws16c48gpio->reg->pol_enab_int_id[1]);
4232c05a0f2SWilliam Breathitt Gray 	iowrite8(0, &ws16c48gpio->reg->pol_enab_int_id[2]);
4242c05a0f2SWilliam Breathitt Gray 
4252c05a0f2SWilliam Breathitt Gray 	/* Select Register Page 3; Unlock all I/O ports */
4262c05a0f2SWilliam Breathitt Gray 	iowrite8(0xC0, &ws16c48gpio->reg->page_lock);
427fceb7ab3SLinus Walleij 
428fceb7ab3SLinus Walleij 	return 0;
429fceb7ab3SLinus Walleij }
430fceb7ab3SLinus Walleij 
431cc736607SWilliam Breathitt Gray static int ws16c48_probe(struct device *dev, unsigned int id)
4329c26df9bSWilliam Breathitt Gray {
4339c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *ws16c48gpio;
4349c26df9bSWilliam Breathitt Gray 	const char *const name = dev_name(dev);
435fceb7ab3SLinus Walleij 	struct gpio_irq_chip *girq;
4369c26df9bSWilliam Breathitt Gray 	int err;
4379c26df9bSWilliam Breathitt Gray 
4389c26df9bSWilliam Breathitt Gray 	ws16c48gpio = devm_kzalloc(dev, sizeof(*ws16c48gpio), GFP_KERNEL);
4399c26df9bSWilliam Breathitt Gray 	if (!ws16c48gpio)
4409c26df9bSWilliam Breathitt Gray 		return -ENOMEM;
4419c26df9bSWilliam Breathitt Gray 
442cc736607SWilliam Breathitt Gray 	if (!devm_request_region(dev, base[id], WS16C48_EXTENT, name)) {
443148ad68bSWilliam Breathitt Gray 		dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
444cc736607SWilliam Breathitt Gray 			base[id], base[id] + WS16C48_EXTENT);
445148ad68bSWilliam Breathitt Gray 		return -EBUSY;
4469c26df9bSWilliam Breathitt Gray 	}
4479c26df9bSWilliam Breathitt Gray 
4482c05a0f2SWilliam Breathitt Gray 	ws16c48gpio->reg = devm_ioport_map(dev, base[id], WS16C48_EXTENT);
4492c05a0f2SWilliam Breathitt Gray 	if (!ws16c48gpio->reg)
4505561a2b0SWilliam Breathitt Gray 		return -ENOMEM;
4515561a2b0SWilliam Breathitt Gray 
4529c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.label = name;
4539c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.parent = dev;
4549c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.owner = THIS_MODULE;
4559c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.base = -1;
4565238f60fSWilliam Breathitt Gray 	ws16c48gpio->chip.ngpio = WS16C48_NGPIO;
4575238f60fSWilliam Breathitt Gray 	ws16c48gpio->chip.names = ws16c48_names;
4589c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.get_direction = ws16c48_gpio_get_direction;
4599c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.direction_input = ws16c48_gpio_direction_input;
4609c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.direction_output = ws16c48_gpio_direction_output;
4619c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.get = ws16c48_gpio_get;
462a8ff510dSWilliam Breathitt Gray 	ws16c48gpio->chip.get_multiple = ws16c48_gpio_get_multiple;
4639c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.set = ws16c48_gpio_set;
46499c8ac95SWilliam Breathitt Gray 	ws16c48gpio->chip.set_multiple = ws16c48_gpio_set_multiple;
4659c26df9bSWilliam Breathitt Gray 
466fceb7ab3SLinus Walleij 	girq = &ws16c48gpio->chip.irq;
467fceb7ab3SLinus Walleij 	girq->chip = &ws16c48_irqchip;
468fceb7ab3SLinus Walleij 	/* This will let us handle the parent IRQ in the driver */
469fceb7ab3SLinus Walleij 	girq->parent_handler = NULL;
470fceb7ab3SLinus Walleij 	girq->num_parents = 0;
471fceb7ab3SLinus Walleij 	girq->parents = NULL;
472fceb7ab3SLinus Walleij 	girq->default_type = IRQ_TYPE_NONE;
473fceb7ab3SLinus Walleij 	girq->handler = handle_edge_irq;
474fceb7ab3SLinus Walleij 	girq->init_hw = ws16c48_irq_init_hw;
475fceb7ab3SLinus Walleij 
476a0a584f0SJulia Cartwright 	raw_spin_lock_init(&ws16c48gpio->lock);
4779c26df9bSWilliam Breathitt Gray 
478b4cad1bcSWilliam Breathitt Gray 	err = devm_gpiochip_add_data(dev, &ws16c48gpio->chip, ws16c48gpio);
4799c26df9bSWilliam Breathitt Gray 	if (err) {
4809c26df9bSWilliam Breathitt Gray 		dev_err(dev, "GPIO registering failed (%d)\n", err);
481148ad68bSWilliam Breathitt Gray 		return err;
4829c26df9bSWilliam Breathitt Gray 	}
4839c26df9bSWilliam Breathitt Gray 
484b4cad1bcSWilliam Breathitt Gray 	err = devm_request_irq(dev, irq[id], ws16c48_irq_handler, IRQF_SHARED,
485b4cad1bcSWilliam Breathitt Gray 		name, ws16c48gpio);
486b4cad1bcSWilliam Breathitt Gray 	if (err) {
487b4cad1bcSWilliam Breathitt Gray 		dev_err(dev, "IRQ handler registering failed (%d)\n", err);
488b4cad1bcSWilliam Breathitt Gray 		return err;
489b4cad1bcSWilliam Breathitt Gray 	}
4909c26df9bSWilliam Breathitt Gray 
4919c26df9bSWilliam Breathitt Gray 	return 0;
4929c26df9bSWilliam Breathitt Gray }
4939c26df9bSWilliam Breathitt Gray 
494cc736607SWilliam Breathitt Gray static struct isa_driver ws16c48_driver = {
495cc736607SWilliam Breathitt Gray 	.probe = ws16c48_probe,
4969c26df9bSWilliam Breathitt Gray 	.driver = {
4979c26df9bSWilliam Breathitt Gray 		.name = "ws16c48"
4989c26df9bSWilliam Breathitt Gray 	},
4999c26df9bSWilliam Breathitt Gray };
5009c26df9bSWilliam Breathitt Gray 
501*c95671a3SWilliam Breathitt Gray module_isa_driver_with_irq(ws16c48_driver, num_ws16c48, num_irq);
5029c26df9bSWilliam Breathitt Gray 
5039c26df9bSWilliam Breathitt Gray MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
5049c26df9bSWilliam Breathitt Gray MODULE_DESCRIPTION("WinSystems WS16C48 GPIO driver");
50522aeddb5SWilliam Breathitt Gray MODULE_LICENSE("GPL v2");
506