xref: /linux/drivers/gpio/gpio-ws16c48.c (revision 9c26df9b27b67c607f4881551222f36d8bde865b)
1*9c26df9bSWilliam Breathitt Gray /*
2*9c26df9bSWilliam Breathitt Gray  * GPIO driver for the WinSystems WS16C48
3*9c26df9bSWilliam Breathitt Gray  * Copyright (C) 2016 William Breathitt Gray
4*9c26df9bSWilliam Breathitt Gray  *
5*9c26df9bSWilliam Breathitt Gray  * This program is free software; you can redistribute it and/or modify
6*9c26df9bSWilliam Breathitt Gray  * it under the terms of the GNU General Public License, version 2, as
7*9c26df9bSWilliam Breathitt Gray  * published by the Free Software Foundation.
8*9c26df9bSWilliam Breathitt Gray  *
9*9c26df9bSWilliam Breathitt Gray  * This program is distributed in the hope that it will be useful, but
10*9c26df9bSWilliam Breathitt Gray  * WITHOUT ANY WARRANTY; without even the implied warranty of
11*9c26df9bSWilliam Breathitt Gray  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12*9c26df9bSWilliam Breathitt Gray  * General Public License for more details.
13*9c26df9bSWilliam Breathitt Gray  */
14*9c26df9bSWilliam Breathitt Gray #include <linux/bitops.h>
15*9c26df9bSWilliam Breathitt Gray #include <linux/device.h>
16*9c26df9bSWilliam Breathitt Gray #include <linux/errno.h>
17*9c26df9bSWilliam Breathitt Gray #include <linux/gpio/driver.h>
18*9c26df9bSWilliam Breathitt Gray #include <linux/io.h>
19*9c26df9bSWilliam Breathitt Gray #include <linux/ioport.h>
20*9c26df9bSWilliam Breathitt Gray #include <linux/interrupt.h>
21*9c26df9bSWilliam Breathitt Gray #include <linux/irqdesc.h>
22*9c26df9bSWilliam Breathitt Gray #include <linux/kernel.h>
23*9c26df9bSWilliam Breathitt Gray #include <linux/module.h>
24*9c26df9bSWilliam Breathitt Gray #include <linux/moduleparam.h>
25*9c26df9bSWilliam Breathitt Gray #include <linux/platform_device.h>
26*9c26df9bSWilliam Breathitt Gray #include <linux/spinlock.h>
27*9c26df9bSWilliam Breathitt Gray 
28*9c26df9bSWilliam Breathitt Gray static unsigned ws16c48_base;
29*9c26df9bSWilliam Breathitt Gray module_param(ws16c48_base, uint, 0);
30*9c26df9bSWilliam Breathitt Gray MODULE_PARM_DESC(ws16c48_base, "WinSystems WS16C48 base address");
31*9c26df9bSWilliam Breathitt Gray static unsigned ws16c48_irq;
32*9c26df9bSWilliam Breathitt Gray module_param(ws16c48_irq, uint, 0);
33*9c26df9bSWilliam Breathitt Gray MODULE_PARM_DESC(ws16c48_irq, "WinSystems WS16C48 interrupt line number");
34*9c26df9bSWilliam Breathitt Gray 
35*9c26df9bSWilliam Breathitt Gray /**
36*9c26df9bSWilliam Breathitt Gray  * struct ws16c48_gpio - GPIO device private data structure
37*9c26df9bSWilliam Breathitt Gray  * @chip:	instance of the gpio_chip
38*9c26df9bSWilliam Breathitt Gray  * @io_state:	bit I/O state (whether bit is set to input or output)
39*9c26df9bSWilliam Breathitt Gray  * @out_state:	output bits state
40*9c26df9bSWilliam Breathitt Gray  * @lock:	synchronization lock to prevent I/O race conditions
41*9c26df9bSWilliam Breathitt Gray  * @irq_mask:	I/O bits affected by interrupts
42*9c26df9bSWilliam Breathitt Gray  * @flow_mask:	IRQ flow type mask for the respective I/O bits
43*9c26df9bSWilliam Breathitt Gray  * @base:	base port address of the GPIO device
44*9c26df9bSWilliam Breathitt Gray  * @extent:	extent of port address region of the GPIO device
45*9c26df9bSWilliam Breathitt Gray  * @irq:	Interrupt line number
46*9c26df9bSWilliam Breathitt Gray  */
47*9c26df9bSWilliam Breathitt Gray struct ws16c48_gpio {
48*9c26df9bSWilliam Breathitt Gray 	struct gpio_chip chip;
49*9c26df9bSWilliam Breathitt Gray 	unsigned char io_state[6];
50*9c26df9bSWilliam Breathitt Gray 	unsigned char out_state[6];
51*9c26df9bSWilliam Breathitt Gray 	spinlock_t lock;
52*9c26df9bSWilliam Breathitt Gray 	unsigned long irq_mask;
53*9c26df9bSWilliam Breathitt Gray 	unsigned long flow_mask;
54*9c26df9bSWilliam Breathitt Gray 	unsigned base;
55*9c26df9bSWilliam Breathitt Gray 	unsigned extent;
56*9c26df9bSWilliam Breathitt Gray 	unsigned irq;
57*9c26df9bSWilliam Breathitt Gray };
58*9c26df9bSWilliam Breathitt Gray 
59*9c26df9bSWilliam Breathitt Gray static int ws16c48_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
60*9c26df9bSWilliam Breathitt Gray {
61*9c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
62*9c26df9bSWilliam Breathitt Gray 	const unsigned port = offset / 8;
63*9c26df9bSWilliam Breathitt Gray 	const unsigned mask = BIT(offset % 8);
64*9c26df9bSWilliam Breathitt Gray 
65*9c26df9bSWilliam Breathitt Gray 	return !!(ws16c48gpio->io_state[port] & mask);
66*9c26df9bSWilliam Breathitt Gray }
67*9c26df9bSWilliam Breathitt Gray 
68*9c26df9bSWilliam Breathitt Gray static int ws16c48_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
69*9c26df9bSWilliam Breathitt Gray {
70*9c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
71*9c26df9bSWilliam Breathitt Gray 	const unsigned port = offset / 8;
72*9c26df9bSWilliam Breathitt Gray 	const unsigned mask = BIT(offset % 8);
73*9c26df9bSWilliam Breathitt Gray 	unsigned long flags;
74*9c26df9bSWilliam Breathitt Gray 
75*9c26df9bSWilliam Breathitt Gray 	spin_lock_irqsave(&ws16c48gpio->lock, flags);
76*9c26df9bSWilliam Breathitt Gray 
77*9c26df9bSWilliam Breathitt Gray 	ws16c48gpio->io_state[port] |= mask;
78*9c26df9bSWilliam Breathitt Gray 	ws16c48gpio->out_state[port] &= ~mask;
79*9c26df9bSWilliam Breathitt Gray 	outb(ws16c48gpio->out_state[port], ws16c48gpio->base + port);
80*9c26df9bSWilliam Breathitt Gray 
81*9c26df9bSWilliam Breathitt Gray 	spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
82*9c26df9bSWilliam Breathitt Gray 
83*9c26df9bSWilliam Breathitt Gray 	return 0;
84*9c26df9bSWilliam Breathitt Gray }
85*9c26df9bSWilliam Breathitt Gray 
86*9c26df9bSWilliam Breathitt Gray static int ws16c48_gpio_direction_output(struct gpio_chip *chip,
87*9c26df9bSWilliam Breathitt Gray 	unsigned offset, int value)
88*9c26df9bSWilliam Breathitt Gray {
89*9c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
90*9c26df9bSWilliam Breathitt Gray 	const unsigned port = offset / 8;
91*9c26df9bSWilliam Breathitt Gray 	const unsigned mask = BIT(offset % 8);
92*9c26df9bSWilliam Breathitt Gray 	unsigned long flags;
93*9c26df9bSWilliam Breathitt Gray 
94*9c26df9bSWilliam Breathitt Gray 	spin_lock_irqsave(&ws16c48gpio->lock, flags);
95*9c26df9bSWilliam Breathitt Gray 
96*9c26df9bSWilliam Breathitt Gray 	ws16c48gpio->io_state[port] &= ~mask;
97*9c26df9bSWilliam Breathitt Gray 	if (value)
98*9c26df9bSWilliam Breathitt Gray 		ws16c48gpio->out_state[port] |= mask;
99*9c26df9bSWilliam Breathitt Gray 	else
100*9c26df9bSWilliam Breathitt Gray 		ws16c48gpio->out_state[port] &= ~mask;
101*9c26df9bSWilliam Breathitt Gray 	outb(ws16c48gpio->out_state[port], ws16c48gpio->base + port);
102*9c26df9bSWilliam Breathitt Gray 
103*9c26df9bSWilliam Breathitt Gray 	spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
104*9c26df9bSWilliam Breathitt Gray 
105*9c26df9bSWilliam Breathitt Gray 	return 0;
106*9c26df9bSWilliam Breathitt Gray }
107*9c26df9bSWilliam Breathitt Gray 
108*9c26df9bSWilliam Breathitt Gray static int ws16c48_gpio_get(struct gpio_chip *chip, unsigned offset)
109*9c26df9bSWilliam Breathitt Gray {
110*9c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
111*9c26df9bSWilliam Breathitt Gray 	const unsigned port = offset / 8;
112*9c26df9bSWilliam Breathitt Gray 	const unsigned mask = BIT(offset % 8);
113*9c26df9bSWilliam Breathitt Gray 	unsigned long flags;
114*9c26df9bSWilliam Breathitt Gray 	unsigned port_state;
115*9c26df9bSWilliam Breathitt Gray 
116*9c26df9bSWilliam Breathitt Gray 	spin_lock_irqsave(&ws16c48gpio->lock, flags);
117*9c26df9bSWilliam Breathitt Gray 
118*9c26df9bSWilliam Breathitt Gray 	/* ensure that GPIO is set for input */
119*9c26df9bSWilliam Breathitt Gray 	if (!(ws16c48gpio->io_state[port] & mask)) {
120*9c26df9bSWilliam Breathitt Gray 		spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
121*9c26df9bSWilliam Breathitt Gray 		return -EINVAL;
122*9c26df9bSWilliam Breathitt Gray 	}
123*9c26df9bSWilliam Breathitt Gray 
124*9c26df9bSWilliam Breathitt Gray 	port_state = inb(ws16c48gpio->base + port);
125*9c26df9bSWilliam Breathitt Gray 
126*9c26df9bSWilliam Breathitt Gray 	spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
127*9c26df9bSWilliam Breathitt Gray 
128*9c26df9bSWilliam Breathitt Gray 	return !!(port_state & mask);
129*9c26df9bSWilliam Breathitt Gray }
130*9c26df9bSWilliam Breathitt Gray 
131*9c26df9bSWilliam Breathitt Gray static void ws16c48_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
132*9c26df9bSWilliam Breathitt Gray {
133*9c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
134*9c26df9bSWilliam Breathitt Gray 	const unsigned port = offset / 8;
135*9c26df9bSWilliam Breathitt Gray 	const unsigned mask = BIT(offset % 8);
136*9c26df9bSWilliam Breathitt Gray 	unsigned long flags;
137*9c26df9bSWilliam Breathitt Gray 
138*9c26df9bSWilliam Breathitt Gray 	spin_lock_irqsave(&ws16c48gpio->lock, flags);
139*9c26df9bSWilliam Breathitt Gray 
140*9c26df9bSWilliam Breathitt Gray 	/* ensure that GPIO is set for output */
141*9c26df9bSWilliam Breathitt Gray 	if (ws16c48gpio->io_state[port] & mask) {
142*9c26df9bSWilliam Breathitt Gray 		spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
143*9c26df9bSWilliam Breathitt Gray 		return;
144*9c26df9bSWilliam Breathitt Gray 	}
145*9c26df9bSWilliam Breathitt Gray 
146*9c26df9bSWilliam Breathitt Gray 	if (value)
147*9c26df9bSWilliam Breathitt Gray 		ws16c48gpio->out_state[port] |= mask;
148*9c26df9bSWilliam Breathitt Gray 	else
149*9c26df9bSWilliam Breathitt Gray 		ws16c48gpio->out_state[port] &= ~mask;
150*9c26df9bSWilliam Breathitt Gray 	outb(ws16c48gpio->out_state[port], ws16c48gpio->base + port);
151*9c26df9bSWilliam Breathitt Gray 
152*9c26df9bSWilliam Breathitt Gray 	spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
153*9c26df9bSWilliam Breathitt Gray }
154*9c26df9bSWilliam Breathitt Gray 
155*9c26df9bSWilliam Breathitt Gray static void ws16c48_irq_ack(struct irq_data *data)
156*9c26df9bSWilliam Breathitt Gray {
157*9c26df9bSWilliam Breathitt Gray 	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
158*9c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
159*9c26df9bSWilliam Breathitt Gray 	const unsigned long offset = irqd_to_hwirq(data);
160*9c26df9bSWilliam Breathitt Gray 	const unsigned port = offset / 8;
161*9c26df9bSWilliam Breathitt Gray 	const unsigned mask = BIT(offset % 8);
162*9c26df9bSWilliam Breathitt Gray 	unsigned long flags;
163*9c26df9bSWilliam Breathitt Gray 	unsigned port_state;
164*9c26df9bSWilliam Breathitt Gray 
165*9c26df9bSWilliam Breathitt Gray 	/* only the first 3 ports support interrupts */
166*9c26df9bSWilliam Breathitt Gray 	if (port > 2)
167*9c26df9bSWilliam Breathitt Gray 		return;
168*9c26df9bSWilliam Breathitt Gray 
169*9c26df9bSWilliam Breathitt Gray 	spin_lock_irqsave(&ws16c48gpio->lock, flags);
170*9c26df9bSWilliam Breathitt Gray 
171*9c26df9bSWilliam Breathitt Gray 	port_state = ws16c48gpio->irq_mask >> (8*port);
172*9c26df9bSWilliam Breathitt Gray 
173*9c26df9bSWilliam Breathitt Gray 	outb(0x80, ws16c48gpio->base + 7);
174*9c26df9bSWilliam Breathitt Gray 	outb(port_state & ~mask, ws16c48gpio->base + 8 + port);
175*9c26df9bSWilliam Breathitt Gray 	outb(port_state | mask, ws16c48gpio->base + 8 + port);
176*9c26df9bSWilliam Breathitt Gray 	outb(0xC0, ws16c48gpio->base + 7);
177*9c26df9bSWilliam Breathitt Gray 
178*9c26df9bSWilliam Breathitt Gray 	spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
179*9c26df9bSWilliam Breathitt Gray }
180*9c26df9bSWilliam Breathitt Gray 
181*9c26df9bSWilliam Breathitt Gray static void ws16c48_irq_mask(struct irq_data *data)
182*9c26df9bSWilliam Breathitt Gray {
183*9c26df9bSWilliam Breathitt Gray 	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
184*9c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
185*9c26df9bSWilliam Breathitt Gray 	const unsigned long offset = irqd_to_hwirq(data);
186*9c26df9bSWilliam Breathitt Gray 	const unsigned long mask = BIT(offset);
187*9c26df9bSWilliam Breathitt Gray 	const unsigned port = offset / 8;
188*9c26df9bSWilliam Breathitt Gray 	unsigned long flags;
189*9c26df9bSWilliam Breathitt Gray 
190*9c26df9bSWilliam Breathitt Gray 	/* only the first 3 ports support interrupts */
191*9c26df9bSWilliam Breathitt Gray 	if (port > 2)
192*9c26df9bSWilliam Breathitt Gray 		return;
193*9c26df9bSWilliam Breathitt Gray 
194*9c26df9bSWilliam Breathitt Gray 	spin_lock_irqsave(&ws16c48gpio->lock, flags);
195*9c26df9bSWilliam Breathitt Gray 
196*9c26df9bSWilliam Breathitt Gray 	ws16c48gpio->irq_mask &= ~mask;
197*9c26df9bSWilliam Breathitt Gray 
198*9c26df9bSWilliam Breathitt Gray 	outb(0x80, ws16c48gpio->base + 7);
199*9c26df9bSWilliam Breathitt Gray 	outb(ws16c48gpio->irq_mask >> (8*port), ws16c48gpio->base + 8 + port);
200*9c26df9bSWilliam Breathitt Gray 	outb(0xC0, ws16c48gpio->base + 7);
201*9c26df9bSWilliam Breathitt Gray 
202*9c26df9bSWilliam Breathitt Gray 	spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
203*9c26df9bSWilliam Breathitt Gray }
204*9c26df9bSWilliam Breathitt Gray 
205*9c26df9bSWilliam Breathitt Gray static void ws16c48_irq_unmask(struct irq_data *data)
206*9c26df9bSWilliam Breathitt Gray {
207*9c26df9bSWilliam Breathitt Gray 	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
208*9c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
209*9c26df9bSWilliam Breathitt Gray 	const unsigned long offset = irqd_to_hwirq(data);
210*9c26df9bSWilliam Breathitt Gray 	const unsigned long mask = BIT(offset);
211*9c26df9bSWilliam Breathitt Gray 	const unsigned port = offset / 8;
212*9c26df9bSWilliam Breathitt Gray 	unsigned long flags;
213*9c26df9bSWilliam Breathitt Gray 
214*9c26df9bSWilliam Breathitt Gray 	/* only the first 3 ports support interrupts */
215*9c26df9bSWilliam Breathitt Gray 	if (port > 2)
216*9c26df9bSWilliam Breathitt Gray 		return;
217*9c26df9bSWilliam Breathitt Gray 
218*9c26df9bSWilliam Breathitt Gray 	spin_lock_irqsave(&ws16c48gpio->lock, flags);
219*9c26df9bSWilliam Breathitt Gray 
220*9c26df9bSWilliam Breathitt Gray 	ws16c48gpio->irq_mask |= mask;
221*9c26df9bSWilliam Breathitt Gray 
222*9c26df9bSWilliam Breathitt Gray 	outb(0x80, ws16c48gpio->base + 7);
223*9c26df9bSWilliam Breathitt Gray 	outb(ws16c48gpio->irq_mask >> (8*port), ws16c48gpio->base + 8 + port);
224*9c26df9bSWilliam Breathitt Gray 	outb(0xC0, ws16c48gpio->base + 7);
225*9c26df9bSWilliam Breathitt Gray 
226*9c26df9bSWilliam Breathitt Gray 	spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
227*9c26df9bSWilliam Breathitt Gray }
228*9c26df9bSWilliam Breathitt Gray 
229*9c26df9bSWilliam Breathitt Gray static int ws16c48_irq_set_type(struct irq_data *data, unsigned flow_type)
230*9c26df9bSWilliam Breathitt Gray {
231*9c26df9bSWilliam Breathitt Gray 	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
232*9c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
233*9c26df9bSWilliam Breathitt Gray 	const unsigned long offset = irqd_to_hwirq(data);
234*9c26df9bSWilliam Breathitt Gray 	const unsigned long mask = BIT(offset);
235*9c26df9bSWilliam Breathitt Gray 	const unsigned port = offset / 8;
236*9c26df9bSWilliam Breathitt Gray 	unsigned long flags;
237*9c26df9bSWilliam Breathitt Gray 
238*9c26df9bSWilliam Breathitt Gray 	/* only the first 3 ports support interrupts */
239*9c26df9bSWilliam Breathitt Gray 	if (port > 2)
240*9c26df9bSWilliam Breathitt Gray 		return -EINVAL;
241*9c26df9bSWilliam Breathitt Gray 
242*9c26df9bSWilliam Breathitt Gray 	spin_lock_irqsave(&ws16c48gpio->lock, flags);
243*9c26df9bSWilliam Breathitt Gray 
244*9c26df9bSWilliam Breathitt Gray 	switch (flow_type) {
245*9c26df9bSWilliam Breathitt Gray 	case IRQ_TYPE_NONE:
246*9c26df9bSWilliam Breathitt Gray 		break;
247*9c26df9bSWilliam Breathitt Gray 	case IRQ_TYPE_EDGE_RISING:
248*9c26df9bSWilliam Breathitt Gray 		ws16c48gpio->flow_mask |= mask;
249*9c26df9bSWilliam Breathitt Gray 		break;
250*9c26df9bSWilliam Breathitt Gray 	case IRQ_TYPE_EDGE_FALLING:
251*9c26df9bSWilliam Breathitt Gray 		ws16c48gpio->flow_mask &= ~mask;
252*9c26df9bSWilliam Breathitt Gray 		break;
253*9c26df9bSWilliam Breathitt Gray 	default:
254*9c26df9bSWilliam Breathitt Gray 		spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
255*9c26df9bSWilliam Breathitt Gray 		return -EINVAL;
256*9c26df9bSWilliam Breathitt Gray 	}
257*9c26df9bSWilliam Breathitt Gray 
258*9c26df9bSWilliam Breathitt Gray 	outb(0x40, ws16c48gpio->base + 7);
259*9c26df9bSWilliam Breathitt Gray 	outb(ws16c48gpio->flow_mask >> (8*port), ws16c48gpio->base + 8 + port);
260*9c26df9bSWilliam Breathitt Gray 	outb(0xC0, ws16c48gpio->base + 7);
261*9c26df9bSWilliam Breathitt Gray 
262*9c26df9bSWilliam Breathitt Gray 	spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
263*9c26df9bSWilliam Breathitt Gray 
264*9c26df9bSWilliam Breathitt Gray 	return 0;
265*9c26df9bSWilliam Breathitt Gray }
266*9c26df9bSWilliam Breathitt Gray 
267*9c26df9bSWilliam Breathitt Gray static struct irq_chip ws16c48_irqchip = {
268*9c26df9bSWilliam Breathitt Gray 	.name = "ws16c48",
269*9c26df9bSWilliam Breathitt Gray 	.irq_ack = ws16c48_irq_ack,
270*9c26df9bSWilliam Breathitt Gray 	.irq_mask = ws16c48_irq_mask,
271*9c26df9bSWilliam Breathitt Gray 	.irq_unmask = ws16c48_irq_unmask,
272*9c26df9bSWilliam Breathitt Gray 	.irq_set_type = ws16c48_irq_set_type
273*9c26df9bSWilliam Breathitt Gray };
274*9c26df9bSWilliam Breathitt Gray 
275*9c26df9bSWilliam Breathitt Gray static irqreturn_t ws16c48_irq_handler(int irq, void *dev_id)
276*9c26df9bSWilliam Breathitt Gray {
277*9c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = dev_id;
278*9c26df9bSWilliam Breathitt Gray 	struct gpio_chip *const chip = &ws16c48gpio->chip;
279*9c26df9bSWilliam Breathitt Gray 	unsigned long int_pending;
280*9c26df9bSWilliam Breathitt Gray 	unsigned long port;
281*9c26df9bSWilliam Breathitt Gray 	unsigned long int_id;
282*9c26df9bSWilliam Breathitt Gray 	unsigned long gpio;
283*9c26df9bSWilliam Breathitt Gray 
284*9c26df9bSWilliam Breathitt Gray 	int_pending = inb(ws16c48gpio->base + 6) & 0x7;
285*9c26df9bSWilliam Breathitt Gray 	if (!int_pending)
286*9c26df9bSWilliam Breathitt Gray 		return IRQ_NONE;
287*9c26df9bSWilliam Breathitt Gray 
288*9c26df9bSWilliam Breathitt Gray 	/* loop until all pending interrupts are handled */
289*9c26df9bSWilliam Breathitt Gray 	do {
290*9c26df9bSWilliam Breathitt Gray 		for_each_set_bit(port, &int_pending, 3) {
291*9c26df9bSWilliam Breathitt Gray 			int_id = inb(ws16c48gpio->base + 8 + port);
292*9c26df9bSWilliam Breathitt Gray 			for_each_set_bit(gpio, &int_id, 8)
293*9c26df9bSWilliam Breathitt Gray 				generic_handle_irq(irq_find_mapping(
294*9c26df9bSWilliam Breathitt Gray 					chip->irqdomain, gpio + 8*port));
295*9c26df9bSWilliam Breathitt Gray 		}
296*9c26df9bSWilliam Breathitt Gray 
297*9c26df9bSWilliam Breathitt Gray 		int_pending = inb(ws16c48gpio->base + 6) & 0x7;
298*9c26df9bSWilliam Breathitt Gray 	} while (int_pending);
299*9c26df9bSWilliam Breathitt Gray 
300*9c26df9bSWilliam Breathitt Gray 	return IRQ_HANDLED;
301*9c26df9bSWilliam Breathitt Gray }
302*9c26df9bSWilliam Breathitt Gray 
303*9c26df9bSWilliam Breathitt Gray static int __init ws16c48_probe(struct platform_device *pdev)
304*9c26df9bSWilliam Breathitt Gray {
305*9c26df9bSWilliam Breathitt Gray 	struct device *dev = &pdev->dev;
306*9c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *ws16c48gpio;
307*9c26df9bSWilliam Breathitt Gray 	const unsigned base = ws16c48_base;
308*9c26df9bSWilliam Breathitt Gray 	const unsigned extent = 16;
309*9c26df9bSWilliam Breathitt Gray 	const char *const name = dev_name(dev);
310*9c26df9bSWilliam Breathitt Gray 	int err;
311*9c26df9bSWilliam Breathitt Gray 	const unsigned irq = ws16c48_irq;
312*9c26df9bSWilliam Breathitt Gray 
313*9c26df9bSWilliam Breathitt Gray 	ws16c48gpio = devm_kzalloc(dev, sizeof(*ws16c48gpio), GFP_KERNEL);
314*9c26df9bSWilliam Breathitt Gray 	if (!ws16c48gpio)
315*9c26df9bSWilliam Breathitt Gray 		return -ENOMEM;
316*9c26df9bSWilliam Breathitt Gray 
317*9c26df9bSWilliam Breathitt Gray 	if (!request_region(base, extent, name)) {
318*9c26df9bSWilliam Breathitt Gray 		dev_err(dev, "Unable to lock %s port addresses (0x%X-0x%X)\n",
319*9c26df9bSWilliam Breathitt Gray 			name, base, base + extent);
320*9c26df9bSWilliam Breathitt Gray 		err = -EBUSY;
321*9c26df9bSWilliam Breathitt Gray 		goto err_lock_io_port;
322*9c26df9bSWilliam Breathitt Gray 	}
323*9c26df9bSWilliam Breathitt Gray 
324*9c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.label = name;
325*9c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.parent = dev;
326*9c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.owner = THIS_MODULE;
327*9c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.base = -1;
328*9c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.ngpio = 48;
329*9c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.get_direction = ws16c48_gpio_get_direction;
330*9c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.direction_input = ws16c48_gpio_direction_input;
331*9c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.direction_output = ws16c48_gpio_direction_output;
332*9c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.get = ws16c48_gpio_get;
333*9c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.set = ws16c48_gpio_set;
334*9c26df9bSWilliam Breathitt Gray 	ws16c48gpio->base = base;
335*9c26df9bSWilliam Breathitt Gray 	ws16c48gpio->extent = extent;
336*9c26df9bSWilliam Breathitt Gray 	ws16c48gpio->irq = irq;
337*9c26df9bSWilliam Breathitt Gray 
338*9c26df9bSWilliam Breathitt Gray 	spin_lock_init(&ws16c48gpio->lock);
339*9c26df9bSWilliam Breathitt Gray 
340*9c26df9bSWilliam Breathitt Gray 	dev_set_drvdata(dev, ws16c48gpio);
341*9c26df9bSWilliam Breathitt Gray 
342*9c26df9bSWilliam Breathitt Gray 	err = gpiochip_add_data(&ws16c48gpio->chip, ws16c48gpio);
343*9c26df9bSWilliam Breathitt Gray 	if (err) {
344*9c26df9bSWilliam Breathitt Gray 		dev_err(dev, "GPIO registering failed (%d)\n", err);
345*9c26df9bSWilliam Breathitt Gray 		goto err_gpio_register;
346*9c26df9bSWilliam Breathitt Gray 	}
347*9c26df9bSWilliam Breathitt Gray 
348*9c26df9bSWilliam Breathitt Gray 	/* Disable IRQ by default */
349*9c26df9bSWilliam Breathitt Gray 	outb(0x80, base + 7);
350*9c26df9bSWilliam Breathitt Gray 	outb(0, base + 8);
351*9c26df9bSWilliam Breathitt Gray 	outb(0, base + 9);
352*9c26df9bSWilliam Breathitt Gray 	outb(0, base + 10);
353*9c26df9bSWilliam Breathitt Gray 	outb(0xC0, base + 7);
354*9c26df9bSWilliam Breathitt Gray 
355*9c26df9bSWilliam Breathitt Gray 	err = gpiochip_irqchip_add(&ws16c48gpio->chip, &ws16c48_irqchip, 0,
356*9c26df9bSWilliam Breathitt Gray 		handle_edge_irq, IRQ_TYPE_NONE);
357*9c26df9bSWilliam Breathitt Gray 	if (err) {
358*9c26df9bSWilliam Breathitt Gray 		dev_err(dev, "Could not add irqchip (%d)\n", err);
359*9c26df9bSWilliam Breathitt Gray 		goto err_gpiochip_irqchip_add;
360*9c26df9bSWilliam Breathitt Gray 	}
361*9c26df9bSWilliam Breathitt Gray 
362*9c26df9bSWilliam Breathitt Gray 	err = request_irq(irq, ws16c48_irq_handler, IRQF_SHARED, name,
363*9c26df9bSWilliam Breathitt Gray 		ws16c48gpio);
364*9c26df9bSWilliam Breathitt Gray 	if (err) {
365*9c26df9bSWilliam Breathitt Gray 		dev_err(dev, "IRQ handler registering failed (%d)\n", err);
366*9c26df9bSWilliam Breathitt Gray 		goto err_request_irq;
367*9c26df9bSWilliam Breathitt Gray 	}
368*9c26df9bSWilliam Breathitt Gray 
369*9c26df9bSWilliam Breathitt Gray 	return 0;
370*9c26df9bSWilliam Breathitt Gray 
371*9c26df9bSWilliam Breathitt Gray err_request_irq:
372*9c26df9bSWilliam Breathitt Gray err_gpiochip_irqchip_add:
373*9c26df9bSWilliam Breathitt Gray 	gpiochip_remove(&ws16c48gpio->chip);
374*9c26df9bSWilliam Breathitt Gray err_gpio_register:
375*9c26df9bSWilliam Breathitt Gray 	release_region(base, extent);
376*9c26df9bSWilliam Breathitt Gray err_lock_io_port:
377*9c26df9bSWilliam Breathitt Gray 	return err;
378*9c26df9bSWilliam Breathitt Gray }
379*9c26df9bSWilliam Breathitt Gray 
380*9c26df9bSWilliam Breathitt Gray static int ws16c48_remove(struct platform_device *pdev)
381*9c26df9bSWilliam Breathitt Gray {
382*9c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = platform_get_drvdata(pdev);
383*9c26df9bSWilliam Breathitt Gray 
384*9c26df9bSWilliam Breathitt Gray 	free_irq(ws16c48gpio->irq, ws16c48gpio);
385*9c26df9bSWilliam Breathitt Gray 	gpiochip_remove(&ws16c48gpio->chip);
386*9c26df9bSWilliam Breathitt Gray 	release_region(ws16c48gpio->base, ws16c48gpio->extent);
387*9c26df9bSWilliam Breathitt Gray 
388*9c26df9bSWilliam Breathitt Gray 	return 0;
389*9c26df9bSWilliam Breathitt Gray }
390*9c26df9bSWilliam Breathitt Gray 
391*9c26df9bSWilliam Breathitt Gray static struct platform_device *ws16c48_device;
392*9c26df9bSWilliam Breathitt Gray 
393*9c26df9bSWilliam Breathitt Gray static struct platform_driver ws16c48_driver = {
394*9c26df9bSWilliam Breathitt Gray 	.driver = {
395*9c26df9bSWilliam Breathitt Gray 		.name = "ws16c48"
396*9c26df9bSWilliam Breathitt Gray 	},
397*9c26df9bSWilliam Breathitt Gray 	.remove = ws16c48_remove
398*9c26df9bSWilliam Breathitt Gray };
399*9c26df9bSWilliam Breathitt Gray 
400*9c26df9bSWilliam Breathitt Gray static void __exit ws16c48_exit(void)
401*9c26df9bSWilliam Breathitt Gray {
402*9c26df9bSWilliam Breathitt Gray 	platform_device_unregister(ws16c48_device);
403*9c26df9bSWilliam Breathitt Gray 	platform_driver_unregister(&ws16c48_driver);
404*9c26df9bSWilliam Breathitt Gray }
405*9c26df9bSWilliam Breathitt Gray 
406*9c26df9bSWilliam Breathitt Gray static int __init ws16c48_init(void)
407*9c26df9bSWilliam Breathitt Gray {
408*9c26df9bSWilliam Breathitt Gray 	int err;
409*9c26df9bSWilliam Breathitt Gray 
410*9c26df9bSWilliam Breathitt Gray 	ws16c48_device = platform_device_alloc(ws16c48_driver.driver.name, -1);
411*9c26df9bSWilliam Breathitt Gray 	if (!ws16c48_device)
412*9c26df9bSWilliam Breathitt Gray 		return -ENOMEM;
413*9c26df9bSWilliam Breathitt Gray 
414*9c26df9bSWilliam Breathitt Gray 	err = platform_device_add(ws16c48_device);
415*9c26df9bSWilliam Breathitt Gray 	if (err)
416*9c26df9bSWilliam Breathitt Gray 		goto err_platform_device;
417*9c26df9bSWilliam Breathitt Gray 
418*9c26df9bSWilliam Breathitt Gray 	err = platform_driver_probe(&ws16c48_driver, ws16c48_probe);
419*9c26df9bSWilliam Breathitt Gray 	if (err)
420*9c26df9bSWilliam Breathitt Gray 		goto err_platform_driver;
421*9c26df9bSWilliam Breathitt Gray 
422*9c26df9bSWilliam Breathitt Gray 	return 0;
423*9c26df9bSWilliam Breathitt Gray 
424*9c26df9bSWilliam Breathitt Gray err_platform_driver:
425*9c26df9bSWilliam Breathitt Gray 	platform_device_del(ws16c48_device);
426*9c26df9bSWilliam Breathitt Gray err_platform_device:
427*9c26df9bSWilliam Breathitt Gray 	platform_device_put(ws16c48_device);
428*9c26df9bSWilliam Breathitt Gray 	return err;
429*9c26df9bSWilliam Breathitt Gray }
430*9c26df9bSWilliam Breathitt Gray 
431*9c26df9bSWilliam Breathitt Gray module_init(ws16c48_init);
432*9c26df9bSWilliam Breathitt Gray module_exit(ws16c48_exit);
433*9c26df9bSWilliam Breathitt Gray 
434*9c26df9bSWilliam Breathitt Gray MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
435*9c26df9bSWilliam Breathitt Gray MODULE_DESCRIPTION("WinSystems WS16C48 GPIO driver");
436*9c26df9bSWilliam Breathitt Gray MODULE_LICENSE("GPL");
437