xref: /linux/drivers/gpio/gpio-ws16c48.c (revision 22aeddb58dcc920cf2f78652c01272d9dff3d30a)
19c26df9bSWilliam Breathitt Gray /*
29c26df9bSWilliam Breathitt Gray  * GPIO driver for the WinSystems WS16C48
39c26df9bSWilliam Breathitt Gray  * Copyright (C) 2016 William Breathitt Gray
49c26df9bSWilliam Breathitt Gray  *
59c26df9bSWilliam Breathitt Gray  * This program is free software; you can redistribute it and/or modify
69c26df9bSWilliam Breathitt Gray  * it under the terms of the GNU General Public License, version 2, as
79c26df9bSWilliam Breathitt Gray  * published by the Free Software Foundation.
89c26df9bSWilliam Breathitt Gray  *
99c26df9bSWilliam Breathitt Gray  * This program is distributed in the hope that it will be useful, but
109c26df9bSWilliam Breathitt Gray  * WITHOUT ANY WARRANTY; without even the implied warranty of
119c26df9bSWilliam Breathitt Gray  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
129c26df9bSWilliam Breathitt Gray  * General Public License for more details.
139c26df9bSWilliam Breathitt Gray  */
149c26df9bSWilliam Breathitt Gray #include <linux/bitops.h>
159c26df9bSWilliam Breathitt Gray #include <linux/device.h>
169c26df9bSWilliam Breathitt Gray #include <linux/errno.h>
179c26df9bSWilliam Breathitt Gray #include <linux/gpio/driver.h>
189c26df9bSWilliam Breathitt Gray #include <linux/io.h>
199c26df9bSWilliam Breathitt Gray #include <linux/ioport.h>
209c26df9bSWilliam Breathitt Gray #include <linux/interrupt.h>
219c26df9bSWilliam Breathitt Gray #include <linux/irqdesc.h>
229c26df9bSWilliam Breathitt Gray #include <linux/kernel.h>
239c26df9bSWilliam Breathitt Gray #include <linux/module.h>
249c26df9bSWilliam Breathitt Gray #include <linux/moduleparam.h>
259c26df9bSWilliam Breathitt Gray #include <linux/platform_device.h>
269c26df9bSWilliam Breathitt Gray #include <linux/spinlock.h>
279c26df9bSWilliam Breathitt Gray 
289c26df9bSWilliam Breathitt Gray static unsigned ws16c48_base;
299c26df9bSWilliam Breathitt Gray module_param(ws16c48_base, uint, 0);
309c26df9bSWilliam Breathitt Gray MODULE_PARM_DESC(ws16c48_base, "WinSystems WS16C48 base address");
319c26df9bSWilliam Breathitt Gray static unsigned ws16c48_irq;
329c26df9bSWilliam Breathitt Gray module_param(ws16c48_irq, uint, 0);
339c26df9bSWilliam Breathitt Gray MODULE_PARM_DESC(ws16c48_irq, "WinSystems WS16C48 interrupt line number");
349c26df9bSWilliam Breathitt Gray 
359c26df9bSWilliam Breathitt Gray /**
369c26df9bSWilliam Breathitt Gray  * struct ws16c48_gpio - GPIO device private data structure
379c26df9bSWilliam Breathitt Gray  * @chip:	instance of the gpio_chip
389c26df9bSWilliam Breathitt Gray  * @io_state:	bit I/O state (whether bit is set to input or output)
399c26df9bSWilliam Breathitt Gray  * @out_state:	output bits state
409c26df9bSWilliam Breathitt Gray  * @lock:	synchronization lock to prevent I/O race conditions
419c26df9bSWilliam Breathitt Gray  * @irq_mask:	I/O bits affected by interrupts
429c26df9bSWilliam Breathitt Gray  * @flow_mask:	IRQ flow type mask for the respective I/O bits
439c26df9bSWilliam Breathitt Gray  * @base:	base port address of the GPIO device
449c26df9bSWilliam Breathitt Gray  * @extent:	extent of port address region of the GPIO device
459c26df9bSWilliam Breathitt Gray  * @irq:	Interrupt line number
469c26df9bSWilliam Breathitt Gray  */
479c26df9bSWilliam Breathitt Gray struct ws16c48_gpio {
489c26df9bSWilliam Breathitt Gray 	struct gpio_chip chip;
499c26df9bSWilliam Breathitt Gray 	unsigned char io_state[6];
509c26df9bSWilliam Breathitt Gray 	unsigned char out_state[6];
519c26df9bSWilliam Breathitt Gray 	spinlock_t lock;
529c26df9bSWilliam Breathitt Gray 	unsigned long irq_mask;
539c26df9bSWilliam Breathitt Gray 	unsigned long flow_mask;
549c26df9bSWilliam Breathitt Gray 	unsigned base;
559c26df9bSWilliam Breathitt Gray 	unsigned extent;
569c26df9bSWilliam Breathitt Gray 	unsigned irq;
579c26df9bSWilliam Breathitt Gray };
589c26df9bSWilliam Breathitt Gray 
599c26df9bSWilliam Breathitt Gray static int ws16c48_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
609c26df9bSWilliam Breathitt Gray {
619c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
629c26df9bSWilliam Breathitt Gray 	const unsigned port = offset / 8;
639c26df9bSWilliam Breathitt Gray 	const unsigned mask = BIT(offset % 8);
649c26df9bSWilliam Breathitt Gray 
659c26df9bSWilliam Breathitt Gray 	return !!(ws16c48gpio->io_state[port] & mask);
669c26df9bSWilliam Breathitt Gray }
679c26df9bSWilliam Breathitt Gray 
689c26df9bSWilliam Breathitt Gray static int ws16c48_gpio_direction_input(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 	unsigned long flags;
749c26df9bSWilliam Breathitt Gray 
759c26df9bSWilliam Breathitt Gray 	spin_lock_irqsave(&ws16c48gpio->lock, flags);
769c26df9bSWilliam Breathitt Gray 
779c26df9bSWilliam Breathitt Gray 	ws16c48gpio->io_state[port] |= mask;
789c26df9bSWilliam Breathitt Gray 	ws16c48gpio->out_state[port] &= ~mask;
799c26df9bSWilliam Breathitt Gray 	outb(ws16c48gpio->out_state[port], ws16c48gpio->base + port);
809c26df9bSWilliam Breathitt Gray 
819c26df9bSWilliam Breathitt Gray 	spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
829c26df9bSWilliam Breathitt Gray 
839c26df9bSWilliam Breathitt Gray 	return 0;
849c26df9bSWilliam Breathitt Gray }
859c26df9bSWilliam Breathitt Gray 
869c26df9bSWilliam Breathitt Gray static int ws16c48_gpio_direction_output(struct gpio_chip *chip,
879c26df9bSWilliam Breathitt Gray 	unsigned offset, int value)
889c26df9bSWilliam Breathitt Gray {
899c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
909c26df9bSWilliam Breathitt Gray 	const unsigned port = offset / 8;
919c26df9bSWilliam Breathitt Gray 	const unsigned mask = BIT(offset % 8);
929c26df9bSWilliam Breathitt Gray 	unsigned long flags;
939c26df9bSWilliam Breathitt Gray 
949c26df9bSWilliam Breathitt Gray 	spin_lock_irqsave(&ws16c48gpio->lock, flags);
959c26df9bSWilliam Breathitt Gray 
969c26df9bSWilliam Breathitt Gray 	ws16c48gpio->io_state[port] &= ~mask;
979c26df9bSWilliam Breathitt Gray 	if (value)
989c26df9bSWilliam Breathitt Gray 		ws16c48gpio->out_state[port] |= mask;
999c26df9bSWilliam Breathitt Gray 	else
1009c26df9bSWilliam Breathitt Gray 		ws16c48gpio->out_state[port] &= ~mask;
1019c26df9bSWilliam Breathitt Gray 	outb(ws16c48gpio->out_state[port], ws16c48gpio->base + port);
1029c26df9bSWilliam Breathitt Gray 
1039c26df9bSWilliam Breathitt Gray 	spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
1049c26df9bSWilliam Breathitt Gray 
1059c26df9bSWilliam Breathitt Gray 	return 0;
1069c26df9bSWilliam Breathitt Gray }
1079c26df9bSWilliam Breathitt Gray 
1089c26df9bSWilliam Breathitt Gray static int ws16c48_gpio_get(struct gpio_chip *chip, unsigned offset)
1099c26df9bSWilliam Breathitt Gray {
1109c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
1119c26df9bSWilliam Breathitt Gray 	const unsigned port = offset / 8;
1129c26df9bSWilliam Breathitt Gray 	const unsigned mask = BIT(offset % 8);
1139c26df9bSWilliam Breathitt Gray 	unsigned long flags;
1149c26df9bSWilliam Breathitt Gray 	unsigned port_state;
1159c26df9bSWilliam Breathitt Gray 
1169c26df9bSWilliam Breathitt Gray 	spin_lock_irqsave(&ws16c48gpio->lock, flags);
1179c26df9bSWilliam Breathitt Gray 
1189c26df9bSWilliam Breathitt Gray 	/* ensure that GPIO is set for input */
1199c26df9bSWilliam Breathitt Gray 	if (!(ws16c48gpio->io_state[port] & mask)) {
1209c26df9bSWilliam Breathitt Gray 		spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
1219c26df9bSWilliam Breathitt Gray 		return -EINVAL;
1229c26df9bSWilliam Breathitt Gray 	}
1239c26df9bSWilliam Breathitt Gray 
1249c26df9bSWilliam Breathitt Gray 	port_state = inb(ws16c48gpio->base + port);
1259c26df9bSWilliam Breathitt Gray 
1269c26df9bSWilliam Breathitt Gray 	spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
1279c26df9bSWilliam Breathitt Gray 
1289c26df9bSWilliam Breathitt Gray 	return !!(port_state & mask);
1299c26df9bSWilliam Breathitt Gray }
1309c26df9bSWilliam Breathitt Gray 
1319c26df9bSWilliam Breathitt Gray static void ws16c48_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
1329c26df9bSWilliam Breathitt Gray {
1339c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
1349c26df9bSWilliam Breathitt Gray 	const unsigned port = offset / 8;
1359c26df9bSWilliam Breathitt Gray 	const unsigned mask = BIT(offset % 8);
1369c26df9bSWilliam Breathitt Gray 	unsigned long flags;
1379c26df9bSWilliam Breathitt Gray 
1389c26df9bSWilliam Breathitt Gray 	spin_lock_irqsave(&ws16c48gpio->lock, flags);
1399c26df9bSWilliam Breathitt Gray 
1409c26df9bSWilliam Breathitt Gray 	/* ensure that GPIO is set for output */
1419c26df9bSWilliam Breathitt Gray 	if (ws16c48gpio->io_state[port] & mask) {
1429c26df9bSWilliam Breathitt Gray 		spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
1439c26df9bSWilliam Breathitt Gray 		return;
1449c26df9bSWilliam Breathitt Gray 	}
1459c26df9bSWilliam Breathitt Gray 
1469c26df9bSWilliam Breathitt Gray 	if (value)
1479c26df9bSWilliam Breathitt Gray 		ws16c48gpio->out_state[port] |= mask;
1489c26df9bSWilliam Breathitt Gray 	else
1499c26df9bSWilliam Breathitt Gray 		ws16c48gpio->out_state[port] &= ~mask;
1509c26df9bSWilliam Breathitt Gray 	outb(ws16c48gpio->out_state[port], ws16c48gpio->base + port);
1519c26df9bSWilliam Breathitt Gray 
1529c26df9bSWilliam Breathitt Gray 	spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
1539c26df9bSWilliam Breathitt Gray }
1549c26df9bSWilliam Breathitt Gray 
1559c26df9bSWilliam Breathitt Gray static void ws16c48_irq_ack(struct irq_data *data)
1569c26df9bSWilliam Breathitt Gray {
1579c26df9bSWilliam Breathitt Gray 	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
1589c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
1599c26df9bSWilliam Breathitt Gray 	const unsigned long offset = irqd_to_hwirq(data);
1609c26df9bSWilliam Breathitt Gray 	const unsigned port = offset / 8;
1619c26df9bSWilliam Breathitt Gray 	const unsigned mask = BIT(offset % 8);
1629c26df9bSWilliam Breathitt Gray 	unsigned long flags;
1639c26df9bSWilliam Breathitt Gray 	unsigned port_state;
1649c26df9bSWilliam Breathitt Gray 
1659c26df9bSWilliam Breathitt Gray 	/* only the first 3 ports support interrupts */
1669c26df9bSWilliam Breathitt Gray 	if (port > 2)
1679c26df9bSWilliam Breathitt Gray 		return;
1689c26df9bSWilliam Breathitt Gray 
1699c26df9bSWilliam Breathitt Gray 	spin_lock_irqsave(&ws16c48gpio->lock, flags);
1709c26df9bSWilliam Breathitt Gray 
1719c26df9bSWilliam Breathitt Gray 	port_state = ws16c48gpio->irq_mask >> (8*port);
1729c26df9bSWilliam Breathitt Gray 
1739c26df9bSWilliam Breathitt Gray 	outb(0x80, ws16c48gpio->base + 7);
1749c26df9bSWilliam Breathitt Gray 	outb(port_state & ~mask, ws16c48gpio->base + 8 + port);
1759c26df9bSWilliam Breathitt Gray 	outb(port_state | mask, ws16c48gpio->base + 8 + port);
1769c26df9bSWilliam Breathitt Gray 	outb(0xC0, ws16c48gpio->base + 7);
1779c26df9bSWilliam Breathitt Gray 
1789c26df9bSWilliam Breathitt Gray 	spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
1799c26df9bSWilliam Breathitt Gray }
1809c26df9bSWilliam Breathitt Gray 
1819c26df9bSWilliam Breathitt Gray static void ws16c48_irq_mask(struct irq_data *data)
1829c26df9bSWilliam Breathitt Gray {
1839c26df9bSWilliam Breathitt Gray 	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
1849c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
1859c26df9bSWilliam Breathitt Gray 	const unsigned long offset = irqd_to_hwirq(data);
1869c26df9bSWilliam Breathitt Gray 	const unsigned long mask = BIT(offset);
1879c26df9bSWilliam Breathitt Gray 	const unsigned port = offset / 8;
1889c26df9bSWilliam Breathitt Gray 	unsigned long flags;
1899c26df9bSWilliam Breathitt Gray 
1909c26df9bSWilliam Breathitt Gray 	/* only the first 3 ports support interrupts */
1919c26df9bSWilliam Breathitt Gray 	if (port > 2)
1929c26df9bSWilliam Breathitt Gray 		return;
1939c26df9bSWilliam Breathitt Gray 
1949c26df9bSWilliam Breathitt Gray 	spin_lock_irqsave(&ws16c48gpio->lock, flags);
1959c26df9bSWilliam Breathitt Gray 
1969c26df9bSWilliam Breathitt Gray 	ws16c48gpio->irq_mask &= ~mask;
1979c26df9bSWilliam Breathitt Gray 
1989c26df9bSWilliam Breathitt Gray 	outb(0x80, ws16c48gpio->base + 7);
1999c26df9bSWilliam Breathitt Gray 	outb(ws16c48gpio->irq_mask >> (8*port), ws16c48gpio->base + 8 + port);
2009c26df9bSWilliam Breathitt Gray 	outb(0xC0, ws16c48gpio->base + 7);
2019c26df9bSWilliam Breathitt Gray 
2029c26df9bSWilliam Breathitt Gray 	spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
2039c26df9bSWilliam Breathitt Gray }
2049c26df9bSWilliam Breathitt Gray 
2059c26df9bSWilliam Breathitt Gray static void ws16c48_irq_unmask(struct irq_data *data)
2069c26df9bSWilliam Breathitt Gray {
2079c26df9bSWilliam Breathitt Gray 	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
2089c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
2099c26df9bSWilliam Breathitt Gray 	const unsigned long offset = irqd_to_hwirq(data);
2109c26df9bSWilliam Breathitt Gray 	const unsigned long mask = BIT(offset);
2119c26df9bSWilliam Breathitt Gray 	const unsigned port = offset / 8;
2129c26df9bSWilliam Breathitt Gray 	unsigned long flags;
2139c26df9bSWilliam Breathitt Gray 
2149c26df9bSWilliam Breathitt Gray 	/* only the first 3 ports support interrupts */
2159c26df9bSWilliam Breathitt Gray 	if (port > 2)
2169c26df9bSWilliam Breathitt Gray 		return;
2179c26df9bSWilliam Breathitt Gray 
2189c26df9bSWilliam Breathitt Gray 	spin_lock_irqsave(&ws16c48gpio->lock, flags);
2199c26df9bSWilliam Breathitt Gray 
2209c26df9bSWilliam Breathitt Gray 	ws16c48gpio->irq_mask |= mask;
2219c26df9bSWilliam Breathitt Gray 
2229c26df9bSWilliam Breathitt Gray 	outb(0x80, ws16c48gpio->base + 7);
2239c26df9bSWilliam Breathitt Gray 	outb(ws16c48gpio->irq_mask >> (8*port), ws16c48gpio->base + 8 + port);
2249c26df9bSWilliam Breathitt Gray 	outb(0xC0, ws16c48gpio->base + 7);
2259c26df9bSWilliam Breathitt Gray 
2269c26df9bSWilliam Breathitt Gray 	spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
2279c26df9bSWilliam Breathitt Gray }
2289c26df9bSWilliam Breathitt Gray 
2299c26df9bSWilliam Breathitt Gray static int ws16c48_irq_set_type(struct irq_data *data, unsigned flow_type)
2309c26df9bSWilliam Breathitt Gray {
2319c26df9bSWilliam Breathitt Gray 	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
2329c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
2339c26df9bSWilliam Breathitt Gray 	const unsigned long offset = irqd_to_hwirq(data);
2349c26df9bSWilliam Breathitt Gray 	const unsigned long mask = BIT(offset);
2359c26df9bSWilliam Breathitt Gray 	const unsigned port = offset / 8;
2369c26df9bSWilliam Breathitt Gray 	unsigned long flags;
2379c26df9bSWilliam Breathitt Gray 
2389c26df9bSWilliam Breathitt Gray 	/* only the first 3 ports support interrupts */
2399c26df9bSWilliam Breathitt Gray 	if (port > 2)
2409c26df9bSWilliam Breathitt Gray 		return -EINVAL;
2419c26df9bSWilliam Breathitt Gray 
2429c26df9bSWilliam Breathitt Gray 	spin_lock_irqsave(&ws16c48gpio->lock, flags);
2439c26df9bSWilliam Breathitt Gray 
2449c26df9bSWilliam Breathitt Gray 	switch (flow_type) {
2459c26df9bSWilliam Breathitt Gray 	case IRQ_TYPE_NONE:
2469c26df9bSWilliam Breathitt Gray 		break;
2479c26df9bSWilliam Breathitt Gray 	case IRQ_TYPE_EDGE_RISING:
2489c26df9bSWilliam Breathitt Gray 		ws16c48gpio->flow_mask |= mask;
2499c26df9bSWilliam Breathitt Gray 		break;
2509c26df9bSWilliam Breathitt Gray 	case IRQ_TYPE_EDGE_FALLING:
2519c26df9bSWilliam Breathitt Gray 		ws16c48gpio->flow_mask &= ~mask;
2529c26df9bSWilliam Breathitt Gray 		break;
2539c26df9bSWilliam Breathitt Gray 	default:
2549c26df9bSWilliam Breathitt Gray 		spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
2559c26df9bSWilliam Breathitt Gray 		return -EINVAL;
2569c26df9bSWilliam Breathitt Gray 	}
2579c26df9bSWilliam Breathitt Gray 
2589c26df9bSWilliam Breathitt Gray 	outb(0x40, ws16c48gpio->base + 7);
2599c26df9bSWilliam Breathitt Gray 	outb(ws16c48gpio->flow_mask >> (8*port), ws16c48gpio->base + 8 + port);
2609c26df9bSWilliam Breathitt Gray 	outb(0xC0, ws16c48gpio->base + 7);
2619c26df9bSWilliam Breathitt Gray 
2629c26df9bSWilliam Breathitt Gray 	spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
2639c26df9bSWilliam Breathitt Gray 
2649c26df9bSWilliam Breathitt Gray 	return 0;
2659c26df9bSWilliam Breathitt Gray }
2669c26df9bSWilliam Breathitt Gray 
2679c26df9bSWilliam Breathitt Gray static struct irq_chip ws16c48_irqchip = {
2689c26df9bSWilliam Breathitt Gray 	.name = "ws16c48",
2699c26df9bSWilliam Breathitt Gray 	.irq_ack = ws16c48_irq_ack,
2709c26df9bSWilliam Breathitt Gray 	.irq_mask = ws16c48_irq_mask,
2719c26df9bSWilliam Breathitt Gray 	.irq_unmask = ws16c48_irq_unmask,
2729c26df9bSWilliam Breathitt Gray 	.irq_set_type = ws16c48_irq_set_type
2739c26df9bSWilliam Breathitt Gray };
2749c26df9bSWilliam Breathitt Gray 
2759c26df9bSWilliam Breathitt Gray static irqreturn_t ws16c48_irq_handler(int irq, void *dev_id)
2769c26df9bSWilliam Breathitt Gray {
2779c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = dev_id;
2789c26df9bSWilliam Breathitt Gray 	struct gpio_chip *const chip = &ws16c48gpio->chip;
2799c26df9bSWilliam Breathitt Gray 	unsigned long int_pending;
2809c26df9bSWilliam Breathitt Gray 	unsigned long port;
2819c26df9bSWilliam Breathitt Gray 	unsigned long int_id;
2829c26df9bSWilliam Breathitt Gray 	unsigned long gpio;
2839c26df9bSWilliam Breathitt Gray 
2849c26df9bSWilliam Breathitt Gray 	int_pending = inb(ws16c48gpio->base + 6) & 0x7;
2859c26df9bSWilliam Breathitt Gray 	if (!int_pending)
2869c26df9bSWilliam Breathitt Gray 		return IRQ_NONE;
2879c26df9bSWilliam Breathitt Gray 
2889c26df9bSWilliam Breathitt Gray 	/* loop until all pending interrupts are handled */
2899c26df9bSWilliam Breathitt Gray 	do {
2909c26df9bSWilliam Breathitt Gray 		for_each_set_bit(port, &int_pending, 3) {
2919c26df9bSWilliam Breathitt Gray 			int_id = inb(ws16c48gpio->base + 8 + port);
2929c26df9bSWilliam Breathitt Gray 			for_each_set_bit(gpio, &int_id, 8)
2939c26df9bSWilliam Breathitt Gray 				generic_handle_irq(irq_find_mapping(
2949c26df9bSWilliam Breathitt Gray 					chip->irqdomain, gpio + 8*port));
2959c26df9bSWilliam Breathitt Gray 		}
2969c26df9bSWilliam Breathitt Gray 
2979c26df9bSWilliam Breathitt Gray 		int_pending = inb(ws16c48gpio->base + 6) & 0x7;
2989c26df9bSWilliam Breathitt Gray 	} while (int_pending);
2999c26df9bSWilliam Breathitt Gray 
3009c26df9bSWilliam Breathitt Gray 	return IRQ_HANDLED;
3019c26df9bSWilliam Breathitt Gray }
3029c26df9bSWilliam Breathitt Gray 
3039c26df9bSWilliam Breathitt Gray static int __init ws16c48_probe(struct platform_device *pdev)
3049c26df9bSWilliam Breathitt Gray {
3059c26df9bSWilliam Breathitt Gray 	struct device *dev = &pdev->dev;
3069c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *ws16c48gpio;
3079c26df9bSWilliam Breathitt Gray 	const unsigned base = ws16c48_base;
3089c26df9bSWilliam Breathitt Gray 	const unsigned extent = 16;
3099c26df9bSWilliam Breathitt Gray 	const char *const name = dev_name(dev);
3109c26df9bSWilliam Breathitt Gray 	int err;
3119c26df9bSWilliam Breathitt Gray 	const unsigned irq = ws16c48_irq;
3129c26df9bSWilliam Breathitt Gray 
3139c26df9bSWilliam Breathitt Gray 	ws16c48gpio = devm_kzalloc(dev, sizeof(*ws16c48gpio), GFP_KERNEL);
3149c26df9bSWilliam Breathitt Gray 	if (!ws16c48gpio)
3159c26df9bSWilliam Breathitt Gray 		return -ENOMEM;
3169c26df9bSWilliam Breathitt Gray 
3179c26df9bSWilliam Breathitt Gray 	if (!request_region(base, extent, name)) {
3189c26df9bSWilliam Breathitt Gray 		dev_err(dev, "Unable to lock %s port addresses (0x%X-0x%X)\n",
3199c26df9bSWilliam Breathitt Gray 			name, base, base + extent);
3209c26df9bSWilliam Breathitt Gray 		err = -EBUSY;
3219c26df9bSWilliam Breathitt Gray 		goto err_lock_io_port;
3229c26df9bSWilliam Breathitt Gray 	}
3239c26df9bSWilliam Breathitt Gray 
3249c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.label = name;
3259c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.parent = dev;
3269c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.owner = THIS_MODULE;
3279c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.base = -1;
3289c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.ngpio = 48;
3299c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.get_direction = ws16c48_gpio_get_direction;
3309c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.direction_input = ws16c48_gpio_direction_input;
3319c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.direction_output = ws16c48_gpio_direction_output;
3329c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.get = ws16c48_gpio_get;
3339c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.set = ws16c48_gpio_set;
3349c26df9bSWilliam Breathitt Gray 	ws16c48gpio->base = base;
3359c26df9bSWilliam Breathitt Gray 	ws16c48gpio->extent = extent;
3369c26df9bSWilliam Breathitt Gray 	ws16c48gpio->irq = irq;
3379c26df9bSWilliam Breathitt Gray 
3389c26df9bSWilliam Breathitt Gray 	spin_lock_init(&ws16c48gpio->lock);
3399c26df9bSWilliam Breathitt Gray 
3409c26df9bSWilliam Breathitt Gray 	dev_set_drvdata(dev, ws16c48gpio);
3419c26df9bSWilliam Breathitt Gray 
3429c26df9bSWilliam Breathitt Gray 	err = gpiochip_add_data(&ws16c48gpio->chip, ws16c48gpio);
3439c26df9bSWilliam Breathitt Gray 	if (err) {
3449c26df9bSWilliam Breathitt Gray 		dev_err(dev, "GPIO registering failed (%d)\n", err);
3459c26df9bSWilliam Breathitt Gray 		goto err_gpio_register;
3469c26df9bSWilliam Breathitt Gray 	}
3479c26df9bSWilliam Breathitt Gray 
3489c26df9bSWilliam Breathitt Gray 	/* Disable IRQ by default */
3499c26df9bSWilliam Breathitt Gray 	outb(0x80, base + 7);
3509c26df9bSWilliam Breathitt Gray 	outb(0, base + 8);
3519c26df9bSWilliam Breathitt Gray 	outb(0, base + 9);
3529c26df9bSWilliam Breathitt Gray 	outb(0, base + 10);
3539c26df9bSWilliam Breathitt Gray 	outb(0xC0, base + 7);
3549c26df9bSWilliam Breathitt Gray 
3559c26df9bSWilliam Breathitt Gray 	err = gpiochip_irqchip_add(&ws16c48gpio->chip, &ws16c48_irqchip, 0,
3569c26df9bSWilliam Breathitt Gray 		handle_edge_irq, IRQ_TYPE_NONE);
3579c26df9bSWilliam Breathitt Gray 	if (err) {
3589c26df9bSWilliam Breathitt Gray 		dev_err(dev, "Could not add irqchip (%d)\n", err);
3599c26df9bSWilliam Breathitt Gray 		goto err_gpiochip_irqchip_add;
3609c26df9bSWilliam Breathitt Gray 	}
3619c26df9bSWilliam Breathitt Gray 
3629c26df9bSWilliam Breathitt Gray 	err = request_irq(irq, ws16c48_irq_handler, IRQF_SHARED, name,
3639c26df9bSWilliam Breathitt Gray 		ws16c48gpio);
3649c26df9bSWilliam Breathitt Gray 	if (err) {
3659c26df9bSWilliam Breathitt Gray 		dev_err(dev, "IRQ handler registering failed (%d)\n", err);
3669c26df9bSWilliam Breathitt Gray 		goto err_request_irq;
3679c26df9bSWilliam Breathitt Gray 	}
3689c26df9bSWilliam Breathitt Gray 
3699c26df9bSWilliam Breathitt Gray 	return 0;
3709c26df9bSWilliam Breathitt Gray 
3719c26df9bSWilliam Breathitt Gray err_request_irq:
3729c26df9bSWilliam Breathitt Gray err_gpiochip_irqchip_add:
3739c26df9bSWilliam Breathitt Gray 	gpiochip_remove(&ws16c48gpio->chip);
3749c26df9bSWilliam Breathitt Gray err_gpio_register:
3759c26df9bSWilliam Breathitt Gray 	release_region(base, extent);
3769c26df9bSWilliam Breathitt Gray err_lock_io_port:
3779c26df9bSWilliam Breathitt Gray 	return err;
3789c26df9bSWilliam Breathitt Gray }
3799c26df9bSWilliam Breathitt Gray 
3809c26df9bSWilliam Breathitt Gray static int ws16c48_remove(struct platform_device *pdev)
3819c26df9bSWilliam Breathitt Gray {
3829c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = platform_get_drvdata(pdev);
3839c26df9bSWilliam Breathitt Gray 
3849c26df9bSWilliam Breathitt Gray 	free_irq(ws16c48gpio->irq, ws16c48gpio);
3859c26df9bSWilliam Breathitt Gray 	gpiochip_remove(&ws16c48gpio->chip);
3869c26df9bSWilliam Breathitt Gray 	release_region(ws16c48gpio->base, ws16c48gpio->extent);
3879c26df9bSWilliam Breathitt Gray 
3889c26df9bSWilliam Breathitt Gray 	return 0;
3899c26df9bSWilliam Breathitt Gray }
3909c26df9bSWilliam Breathitt Gray 
3919c26df9bSWilliam Breathitt Gray static struct platform_device *ws16c48_device;
3929c26df9bSWilliam Breathitt Gray 
3939c26df9bSWilliam Breathitt Gray static struct platform_driver ws16c48_driver = {
3949c26df9bSWilliam Breathitt Gray 	.driver = {
3959c26df9bSWilliam Breathitt Gray 		.name = "ws16c48"
3969c26df9bSWilliam Breathitt Gray 	},
3979c26df9bSWilliam Breathitt Gray 	.remove = ws16c48_remove
3989c26df9bSWilliam Breathitt Gray };
3999c26df9bSWilliam Breathitt Gray 
4009c26df9bSWilliam Breathitt Gray static void __exit ws16c48_exit(void)
4019c26df9bSWilliam Breathitt Gray {
4029c26df9bSWilliam Breathitt Gray 	platform_device_unregister(ws16c48_device);
4039c26df9bSWilliam Breathitt Gray 	platform_driver_unregister(&ws16c48_driver);
4049c26df9bSWilliam Breathitt Gray }
4059c26df9bSWilliam Breathitt Gray 
4069c26df9bSWilliam Breathitt Gray static int __init ws16c48_init(void)
4079c26df9bSWilliam Breathitt Gray {
4089c26df9bSWilliam Breathitt Gray 	int err;
4099c26df9bSWilliam Breathitt Gray 
4109c26df9bSWilliam Breathitt Gray 	ws16c48_device = platform_device_alloc(ws16c48_driver.driver.name, -1);
4119c26df9bSWilliam Breathitt Gray 	if (!ws16c48_device)
4129c26df9bSWilliam Breathitt Gray 		return -ENOMEM;
4139c26df9bSWilliam Breathitt Gray 
4149c26df9bSWilliam Breathitt Gray 	err = platform_device_add(ws16c48_device);
4159c26df9bSWilliam Breathitt Gray 	if (err)
4169c26df9bSWilliam Breathitt Gray 		goto err_platform_device;
4179c26df9bSWilliam Breathitt Gray 
4189c26df9bSWilliam Breathitt Gray 	err = platform_driver_probe(&ws16c48_driver, ws16c48_probe);
4199c26df9bSWilliam Breathitt Gray 	if (err)
4209c26df9bSWilliam Breathitt Gray 		goto err_platform_driver;
4219c26df9bSWilliam Breathitt Gray 
4229c26df9bSWilliam Breathitt Gray 	return 0;
4239c26df9bSWilliam Breathitt Gray 
4249c26df9bSWilliam Breathitt Gray err_platform_driver:
4259c26df9bSWilliam Breathitt Gray 	platform_device_del(ws16c48_device);
4269c26df9bSWilliam Breathitt Gray err_platform_device:
4279c26df9bSWilliam Breathitt Gray 	platform_device_put(ws16c48_device);
4289c26df9bSWilliam Breathitt Gray 	return err;
4299c26df9bSWilliam Breathitt Gray }
4309c26df9bSWilliam Breathitt Gray 
4319c26df9bSWilliam Breathitt Gray module_init(ws16c48_init);
4329c26df9bSWilliam Breathitt Gray module_exit(ws16c48_exit);
4339c26df9bSWilliam Breathitt Gray 
4349c26df9bSWilliam Breathitt Gray MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
4359c26df9bSWilliam Breathitt Gray MODULE_DESCRIPTION("WinSystems WS16C48 GPIO driver");
436*22aeddb5SWilliam Breathitt Gray MODULE_LICENSE("GPL v2");
437