xref: /linux/drivers/gpio/gpio-ws16c48.c (revision 5238f60feb408efbb5ad212d9b5b98a44d97af3a)
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>
22cc736607SWilliam Breathitt Gray #include <linux/isa.h>
239c26df9bSWilliam Breathitt Gray #include <linux/kernel.h>
249c26df9bSWilliam Breathitt Gray #include <linux/module.h>
259c26df9bSWilliam Breathitt Gray #include <linux/moduleparam.h>
269c26df9bSWilliam Breathitt Gray #include <linux/spinlock.h>
279c26df9bSWilliam Breathitt Gray 
28cc736607SWilliam Breathitt Gray #define WS16C48_EXTENT 16
29cc736607SWilliam Breathitt Gray #define MAX_NUM_WS16C48 max_num_isa_dev(WS16C48_EXTENT)
30cc736607SWilliam Breathitt Gray 
31cc736607SWilliam Breathitt Gray static unsigned int base[MAX_NUM_WS16C48];
32cc736607SWilliam Breathitt Gray static unsigned int num_ws16c48;
33cc736607SWilliam Breathitt Gray module_param_array(base, uint, &num_ws16c48, 0);
34cc736607SWilliam Breathitt Gray MODULE_PARM_DESC(base, "WinSystems WS16C48 base addresses");
35cc736607SWilliam Breathitt Gray 
36cc736607SWilliam Breathitt Gray static unsigned int irq[MAX_NUM_WS16C48];
37cc736607SWilliam Breathitt Gray module_param_array(irq, uint, NULL, 0);
38cc736607SWilliam Breathitt Gray MODULE_PARM_DESC(irq, "WinSystems WS16C48 interrupt line numbers");
399c26df9bSWilliam Breathitt Gray 
409c26df9bSWilliam Breathitt Gray /**
419c26df9bSWilliam Breathitt Gray  * struct ws16c48_gpio - GPIO device private data structure
429c26df9bSWilliam Breathitt Gray  * @chip:	instance of the gpio_chip
439c26df9bSWilliam Breathitt Gray  * @io_state:	bit I/O state (whether bit is set to input or output)
449c26df9bSWilliam Breathitt Gray  * @out_state:	output bits state
459c26df9bSWilliam Breathitt Gray  * @lock:	synchronization lock to prevent I/O race conditions
469c26df9bSWilliam Breathitt Gray  * @irq_mask:	I/O bits affected by interrupts
479c26df9bSWilliam Breathitt Gray  * @flow_mask:	IRQ flow type mask for the respective I/O bits
489c26df9bSWilliam Breathitt Gray  * @base:	base port address of the GPIO device
499c26df9bSWilliam Breathitt Gray  */
509c26df9bSWilliam Breathitt Gray struct ws16c48_gpio {
519c26df9bSWilliam Breathitt Gray 	struct gpio_chip chip;
529c26df9bSWilliam Breathitt Gray 	unsigned char io_state[6];
539c26df9bSWilliam Breathitt Gray 	unsigned char out_state[6];
549c26df9bSWilliam Breathitt Gray 	spinlock_t lock;
559c26df9bSWilliam Breathitt Gray 	unsigned long irq_mask;
569c26df9bSWilliam Breathitt Gray 	unsigned long flow_mask;
579c26df9bSWilliam Breathitt Gray 	unsigned base;
589c26df9bSWilliam Breathitt Gray };
599c26df9bSWilliam Breathitt Gray 
609c26df9bSWilliam Breathitt Gray static int ws16c48_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
619c26df9bSWilliam Breathitt Gray {
629c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
639c26df9bSWilliam Breathitt Gray 	const unsigned port = offset / 8;
649c26df9bSWilliam Breathitt Gray 	const unsigned mask = BIT(offset % 8);
659c26df9bSWilliam Breathitt Gray 
669c26df9bSWilliam Breathitt Gray 	return !!(ws16c48gpio->io_state[port] & mask);
679c26df9bSWilliam Breathitt Gray }
689c26df9bSWilliam Breathitt Gray 
699c26df9bSWilliam Breathitt Gray static int ws16c48_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
709c26df9bSWilliam Breathitt Gray {
719c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
729c26df9bSWilliam Breathitt Gray 	const unsigned port = offset / 8;
739c26df9bSWilliam Breathitt Gray 	const unsigned mask = BIT(offset % 8);
749c26df9bSWilliam Breathitt Gray 	unsigned long flags;
759c26df9bSWilliam Breathitt Gray 
769c26df9bSWilliam Breathitt Gray 	spin_lock_irqsave(&ws16c48gpio->lock, flags);
779c26df9bSWilliam Breathitt Gray 
789c26df9bSWilliam Breathitt Gray 	ws16c48gpio->io_state[port] |= mask;
799c26df9bSWilliam Breathitt Gray 	ws16c48gpio->out_state[port] &= ~mask;
809c26df9bSWilliam Breathitt Gray 	outb(ws16c48gpio->out_state[port], ws16c48gpio->base + port);
819c26df9bSWilliam Breathitt Gray 
829c26df9bSWilliam Breathitt Gray 	spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
839c26df9bSWilliam Breathitt Gray 
849c26df9bSWilliam Breathitt Gray 	return 0;
859c26df9bSWilliam Breathitt Gray }
869c26df9bSWilliam Breathitt Gray 
879c26df9bSWilliam Breathitt Gray static int ws16c48_gpio_direction_output(struct gpio_chip *chip,
889c26df9bSWilliam Breathitt Gray 	unsigned offset, int value)
899c26df9bSWilliam Breathitt Gray {
909c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
919c26df9bSWilliam Breathitt Gray 	const unsigned port = offset / 8;
929c26df9bSWilliam Breathitt Gray 	const unsigned mask = BIT(offset % 8);
939c26df9bSWilliam Breathitt Gray 	unsigned long flags;
949c26df9bSWilliam Breathitt Gray 
959c26df9bSWilliam Breathitt Gray 	spin_lock_irqsave(&ws16c48gpio->lock, flags);
969c26df9bSWilliam Breathitt Gray 
979c26df9bSWilliam Breathitt Gray 	ws16c48gpio->io_state[port] &= ~mask;
989c26df9bSWilliam Breathitt Gray 	if (value)
999c26df9bSWilliam Breathitt Gray 		ws16c48gpio->out_state[port] |= mask;
1009c26df9bSWilliam Breathitt Gray 	else
1019c26df9bSWilliam Breathitt Gray 		ws16c48gpio->out_state[port] &= ~mask;
1029c26df9bSWilliam Breathitt Gray 	outb(ws16c48gpio->out_state[port], ws16c48gpio->base + port);
1039c26df9bSWilliam Breathitt Gray 
1049c26df9bSWilliam Breathitt Gray 	spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
1059c26df9bSWilliam Breathitt Gray 
1069c26df9bSWilliam Breathitt Gray 	return 0;
1079c26df9bSWilliam Breathitt Gray }
1089c26df9bSWilliam Breathitt Gray 
1099c26df9bSWilliam Breathitt Gray static int ws16c48_gpio_get(struct gpio_chip *chip, unsigned offset)
1109c26df9bSWilliam Breathitt Gray {
1119c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
1129c26df9bSWilliam Breathitt Gray 	const unsigned port = offset / 8;
1139c26df9bSWilliam Breathitt Gray 	const unsigned mask = BIT(offset % 8);
1149c26df9bSWilliam Breathitt Gray 	unsigned long flags;
1159c26df9bSWilliam Breathitt Gray 	unsigned port_state;
1169c26df9bSWilliam Breathitt Gray 
1179c26df9bSWilliam Breathitt Gray 	spin_lock_irqsave(&ws16c48gpio->lock, flags);
1189c26df9bSWilliam Breathitt Gray 
1199c26df9bSWilliam Breathitt Gray 	/* ensure that GPIO is set for input */
1209c26df9bSWilliam Breathitt Gray 	if (!(ws16c48gpio->io_state[port] & mask)) {
1219c26df9bSWilliam Breathitt Gray 		spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
1229c26df9bSWilliam Breathitt Gray 		return -EINVAL;
1239c26df9bSWilliam Breathitt Gray 	}
1249c26df9bSWilliam Breathitt Gray 
1259c26df9bSWilliam Breathitt Gray 	port_state = inb(ws16c48gpio->base + port);
1269c26df9bSWilliam Breathitt Gray 
1279c26df9bSWilliam Breathitt Gray 	spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
1289c26df9bSWilliam Breathitt Gray 
1299c26df9bSWilliam Breathitt Gray 	return !!(port_state & mask);
1309c26df9bSWilliam Breathitt Gray }
1319c26df9bSWilliam Breathitt Gray 
1329c26df9bSWilliam Breathitt Gray static void ws16c48_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
1339c26df9bSWilliam Breathitt Gray {
1349c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
1359c26df9bSWilliam Breathitt Gray 	const unsigned port = offset / 8;
1369c26df9bSWilliam Breathitt Gray 	const unsigned mask = BIT(offset % 8);
1379c26df9bSWilliam Breathitt Gray 	unsigned long flags;
1389c26df9bSWilliam Breathitt Gray 
1399c26df9bSWilliam Breathitt Gray 	spin_lock_irqsave(&ws16c48gpio->lock, flags);
1409c26df9bSWilliam Breathitt Gray 
1419c26df9bSWilliam Breathitt Gray 	/* ensure that GPIO is set for output */
1429c26df9bSWilliam Breathitt Gray 	if (ws16c48gpio->io_state[port] & mask) {
1439c26df9bSWilliam Breathitt Gray 		spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
1449c26df9bSWilliam Breathitt Gray 		return;
1459c26df9bSWilliam Breathitt Gray 	}
1469c26df9bSWilliam Breathitt Gray 
1479c26df9bSWilliam Breathitt Gray 	if (value)
1489c26df9bSWilliam Breathitt Gray 		ws16c48gpio->out_state[port] |= mask;
1499c26df9bSWilliam Breathitt Gray 	else
1509c26df9bSWilliam Breathitt Gray 		ws16c48gpio->out_state[port] &= ~mask;
1519c26df9bSWilliam Breathitt Gray 	outb(ws16c48gpio->out_state[port], ws16c48gpio->base + port);
1529c26df9bSWilliam Breathitt Gray 
1539c26df9bSWilliam Breathitt Gray 	spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
1549c26df9bSWilliam Breathitt Gray }
1559c26df9bSWilliam Breathitt Gray 
15699c8ac95SWilliam Breathitt Gray static void ws16c48_gpio_set_multiple(struct gpio_chip *chip,
15799c8ac95SWilliam Breathitt Gray 	unsigned long *mask, unsigned long *bits)
15899c8ac95SWilliam Breathitt Gray {
15999c8ac95SWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
16099c8ac95SWilliam Breathitt Gray 	unsigned int i;
16199c8ac95SWilliam Breathitt Gray 	const unsigned int gpio_reg_size = 8;
16299c8ac95SWilliam Breathitt Gray 	unsigned int port;
16399c8ac95SWilliam Breathitt Gray 	unsigned int iomask;
16499c8ac95SWilliam Breathitt Gray 	unsigned int bitmask;
16599c8ac95SWilliam Breathitt Gray 	unsigned long flags;
16699c8ac95SWilliam Breathitt Gray 
16799c8ac95SWilliam Breathitt Gray 	/* set bits are evaluated a gpio register size at a time */
16899c8ac95SWilliam Breathitt Gray 	for (i = 0; i < chip->ngpio; i += gpio_reg_size) {
16999c8ac95SWilliam Breathitt Gray 		/* no more set bits in this mask word; skip to the next word */
17099c8ac95SWilliam Breathitt Gray 		if (!mask[BIT_WORD(i)]) {
17199c8ac95SWilliam Breathitt Gray 			i = (BIT_WORD(i) + 1) * BITS_PER_LONG - gpio_reg_size;
17299c8ac95SWilliam Breathitt Gray 			continue;
17399c8ac95SWilliam Breathitt Gray 		}
17499c8ac95SWilliam Breathitt Gray 
17599c8ac95SWilliam Breathitt Gray 		port = i / gpio_reg_size;
17699c8ac95SWilliam Breathitt Gray 
17799c8ac95SWilliam Breathitt Gray 		/* mask out GPIO configured for input */
17899c8ac95SWilliam Breathitt Gray 		iomask = mask[BIT_WORD(i)] & ~ws16c48gpio->io_state[port];
17999c8ac95SWilliam Breathitt Gray 		bitmask = iomask & bits[BIT_WORD(i)];
18099c8ac95SWilliam Breathitt Gray 
18199c8ac95SWilliam Breathitt Gray 		spin_lock_irqsave(&ws16c48gpio->lock, flags);
18299c8ac95SWilliam Breathitt Gray 
18399c8ac95SWilliam Breathitt Gray 		/* update output state data and set device gpio register */
18499c8ac95SWilliam Breathitt Gray 		ws16c48gpio->out_state[port] &= ~iomask;
18599c8ac95SWilliam Breathitt Gray 		ws16c48gpio->out_state[port] |= bitmask;
18699c8ac95SWilliam Breathitt Gray 		outb(ws16c48gpio->out_state[port], ws16c48gpio->base + port);
18799c8ac95SWilliam Breathitt Gray 
18899c8ac95SWilliam Breathitt Gray 		spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
18999c8ac95SWilliam Breathitt Gray 
19099c8ac95SWilliam Breathitt Gray 		/* prepare for next gpio register set */
19199c8ac95SWilliam Breathitt Gray 		mask[BIT_WORD(i)] >>= gpio_reg_size;
19299c8ac95SWilliam Breathitt Gray 		bits[BIT_WORD(i)] >>= gpio_reg_size;
19399c8ac95SWilliam Breathitt Gray 	}
19499c8ac95SWilliam Breathitt Gray }
19599c8ac95SWilliam Breathitt Gray 
1969c26df9bSWilliam Breathitt Gray static void ws16c48_irq_ack(struct irq_data *data)
1979c26df9bSWilliam Breathitt Gray {
1989c26df9bSWilliam Breathitt Gray 	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
1999c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
2009c26df9bSWilliam Breathitt Gray 	const unsigned long offset = irqd_to_hwirq(data);
2019c26df9bSWilliam Breathitt Gray 	const unsigned port = offset / 8;
2029c26df9bSWilliam Breathitt Gray 	const unsigned mask = BIT(offset % 8);
2039c26df9bSWilliam Breathitt Gray 	unsigned long flags;
2049c26df9bSWilliam Breathitt Gray 	unsigned port_state;
2059c26df9bSWilliam Breathitt Gray 
2069c26df9bSWilliam Breathitt Gray 	/* only the first 3 ports support interrupts */
2079c26df9bSWilliam Breathitt Gray 	if (port > 2)
2089c26df9bSWilliam Breathitt Gray 		return;
2099c26df9bSWilliam Breathitt Gray 
2109c26df9bSWilliam Breathitt Gray 	spin_lock_irqsave(&ws16c48gpio->lock, flags);
2119c26df9bSWilliam Breathitt Gray 
2129c26df9bSWilliam Breathitt Gray 	port_state = ws16c48gpio->irq_mask >> (8*port);
2139c26df9bSWilliam Breathitt Gray 
2149c26df9bSWilliam Breathitt Gray 	outb(0x80, ws16c48gpio->base + 7);
2159c26df9bSWilliam Breathitt Gray 	outb(port_state & ~mask, ws16c48gpio->base + 8 + port);
2169c26df9bSWilliam Breathitt Gray 	outb(port_state | mask, ws16c48gpio->base + 8 + port);
2179c26df9bSWilliam Breathitt Gray 	outb(0xC0, ws16c48gpio->base + 7);
2189c26df9bSWilliam Breathitt Gray 
2199c26df9bSWilliam Breathitt Gray 	spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
2209c26df9bSWilliam Breathitt Gray }
2219c26df9bSWilliam Breathitt Gray 
2229c26df9bSWilliam Breathitt Gray static void ws16c48_irq_mask(struct irq_data *data)
2239c26df9bSWilliam Breathitt Gray {
2249c26df9bSWilliam Breathitt Gray 	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
2259c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
2269c26df9bSWilliam Breathitt Gray 	const unsigned long offset = irqd_to_hwirq(data);
2279c26df9bSWilliam Breathitt Gray 	const unsigned long mask = BIT(offset);
2289c26df9bSWilliam Breathitt Gray 	const unsigned port = offset / 8;
2299c26df9bSWilliam Breathitt Gray 	unsigned long flags;
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 
2359c26df9bSWilliam Breathitt Gray 	spin_lock_irqsave(&ws16c48gpio->lock, flags);
2369c26df9bSWilliam Breathitt Gray 
2379c26df9bSWilliam Breathitt Gray 	ws16c48gpio->irq_mask &= ~mask;
2389c26df9bSWilliam Breathitt Gray 
2399c26df9bSWilliam Breathitt Gray 	outb(0x80, ws16c48gpio->base + 7);
2409c26df9bSWilliam Breathitt Gray 	outb(ws16c48gpio->irq_mask >> (8*port), ws16c48gpio->base + 8 + port);
2419c26df9bSWilliam Breathitt Gray 	outb(0xC0, ws16c48gpio->base + 7);
2429c26df9bSWilliam Breathitt Gray 
2439c26df9bSWilliam Breathitt Gray 	spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
2449c26df9bSWilliam Breathitt Gray }
2459c26df9bSWilliam Breathitt Gray 
2469c26df9bSWilliam Breathitt Gray static void ws16c48_irq_unmask(struct irq_data *data)
2479c26df9bSWilliam Breathitt Gray {
2489c26df9bSWilliam Breathitt Gray 	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
2499c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
2509c26df9bSWilliam Breathitt Gray 	const unsigned long offset = irqd_to_hwirq(data);
2519c26df9bSWilliam Breathitt Gray 	const unsigned long mask = BIT(offset);
2529c26df9bSWilliam Breathitt Gray 	const unsigned port = offset / 8;
2539c26df9bSWilliam Breathitt Gray 	unsigned long flags;
2549c26df9bSWilliam Breathitt Gray 
2559c26df9bSWilliam Breathitt Gray 	/* only the first 3 ports support interrupts */
2569c26df9bSWilliam Breathitt Gray 	if (port > 2)
2579c26df9bSWilliam Breathitt Gray 		return;
2589c26df9bSWilliam Breathitt Gray 
2599c26df9bSWilliam Breathitt Gray 	spin_lock_irqsave(&ws16c48gpio->lock, flags);
2609c26df9bSWilliam Breathitt Gray 
2619c26df9bSWilliam Breathitt Gray 	ws16c48gpio->irq_mask |= mask;
2629c26df9bSWilliam Breathitt Gray 
2639c26df9bSWilliam Breathitt Gray 	outb(0x80, ws16c48gpio->base + 7);
2649c26df9bSWilliam Breathitt Gray 	outb(ws16c48gpio->irq_mask >> (8*port), ws16c48gpio->base + 8 + port);
2659c26df9bSWilliam Breathitt Gray 	outb(0xC0, ws16c48gpio->base + 7);
2669c26df9bSWilliam Breathitt Gray 
2679c26df9bSWilliam Breathitt Gray 	spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
2689c26df9bSWilliam Breathitt Gray }
2699c26df9bSWilliam Breathitt Gray 
2709c26df9bSWilliam Breathitt Gray static int ws16c48_irq_set_type(struct irq_data *data, unsigned flow_type)
2719c26df9bSWilliam Breathitt Gray {
2729c26df9bSWilliam Breathitt Gray 	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
2739c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
2749c26df9bSWilliam Breathitt Gray 	const unsigned long offset = irqd_to_hwirq(data);
2759c26df9bSWilliam Breathitt Gray 	const unsigned long mask = BIT(offset);
2769c26df9bSWilliam Breathitt Gray 	const unsigned port = offset / 8;
2779c26df9bSWilliam Breathitt Gray 	unsigned long flags;
2789c26df9bSWilliam Breathitt Gray 
2799c26df9bSWilliam Breathitt Gray 	/* only the first 3 ports support interrupts */
2809c26df9bSWilliam Breathitt Gray 	if (port > 2)
2819c26df9bSWilliam Breathitt Gray 		return -EINVAL;
2829c26df9bSWilliam Breathitt Gray 
2839c26df9bSWilliam Breathitt Gray 	spin_lock_irqsave(&ws16c48gpio->lock, flags);
2849c26df9bSWilliam Breathitt Gray 
2859c26df9bSWilliam Breathitt Gray 	switch (flow_type) {
2869c26df9bSWilliam Breathitt Gray 	case IRQ_TYPE_NONE:
2879c26df9bSWilliam Breathitt Gray 		break;
2889c26df9bSWilliam Breathitt Gray 	case IRQ_TYPE_EDGE_RISING:
2899c26df9bSWilliam Breathitt Gray 		ws16c48gpio->flow_mask |= mask;
2909c26df9bSWilliam Breathitt Gray 		break;
2919c26df9bSWilliam Breathitt Gray 	case IRQ_TYPE_EDGE_FALLING:
2929c26df9bSWilliam Breathitt Gray 		ws16c48gpio->flow_mask &= ~mask;
2939c26df9bSWilliam Breathitt Gray 		break;
2949c26df9bSWilliam Breathitt Gray 	default:
2959c26df9bSWilliam Breathitt Gray 		spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
2969c26df9bSWilliam Breathitt Gray 		return -EINVAL;
2979c26df9bSWilliam Breathitt Gray 	}
2989c26df9bSWilliam Breathitt Gray 
2999c26df9bSWilliam Breathitt Gray 	outb(0x40, ws16c48gpio->base + 7);
3009c26df9bSWilliam Breathitt Gray 	outb(ws16c48gpio->flow_mask >> (8*port), ws16c48gpio->base + 8 + port);
3019c26df9bSWilliam Breathitt Gray 	outb(0xC0, ws16c48gpio->base + 7);
3029c26df9bSWilliam Breathitt Gray 
3039c26df9bSWilliam Breathitt Gray 	spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
3049c26df9bSWilliam Breathitt Gray 
3059c26df9bSWilliam Breathitt Gray 	return 0;
3069c26df9bSWilliam Breathitt Gray }
3079c26df9bSWilliam Breathitt Gray 
3089c26df9bSWilliam Breathitt Gray static struct irq_chip ws16c48_irqchip = {
3099c26df9bSWilliam Breathitt Gray 	.name = "ws16c48",
3109c26df9bSWilliam Breathitt Gray 	.irq_ack = ws16c48_irq_ack,
3119c26df9bSWilliam Breathitt Gray 	.irq_mask = ws16c48_irq_mask,
3129c26df9bSWilliam Breathitt Gray 	.irq_unmask = ws16c48_irq_unmask,
3139c26df9bSWilliam Breathitt Gray 	.irq_set_type = ws16c48_irq_set_type
3149c26df9bSWilliam Breathitt Gray };
3159c26df9bSWilliam Breathitt Gray 
3169c26df9bSWilliam Breathitt Gray static irqreturn_t ws16c48_irq_handler(int irq, void *dev_id)
3179c26df9bSWilliam Breathitt Gray {
3189c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *const ws16c48gpio = dev_id;
3199c26df9bSWilliam Breathitt Gray 	struct gpio_chip *const chip = &ws16c48gpio->chip;
3209c26df9bSWilliam Breathitt Gray 	unsigned long int_pending;
3219c26df9bSWilliam Breathitt Gray 	unsigned long port;
3229c26df9bSWilliam Breathitt Gray 	unsigned long int_id;
3239c26df9bSWilliam Breathitt Gray 	unsigned long gpio;
3249c26df9bSWilliam Breathitt Gray 
3259c26df9bSWilliam Breathitt Gray 	int_pending = inb(ws16c48gpio->base + 6) & 0x7;
3269c26df9bSWilliam Breathitt Gray 	if (!int_pending)
3279c26df9bSWilliam Breathitt Gray 		return IRQ_NONE;
3289c26df9bSWilliam Breathitt Gray 
3299c26df9bSWilliam Breathitt Gray 	/* loop until all pending interrupts are handled */
3309c26df9bSWilliam Breathitt Gray 	do {
3319c26df9bSWilliam Breathitt Gray 		for_each_set_bit(port, &int_pending, 3) {
3329c26df9bSWilliam Breathitt Gray 			int_id = inb(ws16c48gpio->base + 8 + port);
3339c26df9bSWilliam Breathitt Gray 			for_each_set_bit(gpio, &int_id, 8)
3349c26df9bSWilliam Breathitt Gray 				generic_handle_irq(irq_find_mapping(
3359c26df9bSWilliam Breathitt Gray 					chip->irqdomain, gpio + 8*port));
3369c26df9bSWilliam Breathitt Gray 		}
3379c26df9bSWilliam Breathitt Gray 
3389c26df9bSWilliam Breathitt Gray 		int_pending = inb(ws16c48gpio->base + 6) & 0x7;
3399c26df9bSWilliam Breathitt Gray 	} while (int_pending);
3409c26df9bSWilliam Breathitt Gray 
3419c26df9bSWilliam Breathitt Gray 	return IRQ_HANDLED;
3429c26df9bSWilliam Breathitt Gray }
3439c26df9bSWilliam Breathitt Gray 
344*5238f60fSWilliam Breathitt Gray #define WS16C48_NGPIO 48
345*5238f60fSWilliam Breathitt Gray static const char *ws16c48_names[WS16C48_NGPIO] = {
346*5238f60fSWilliam Breathitt Gray 	"Port 0 Bit 0", "Port 0 Bit 1", "Port 0 Bit 2", "Port 0 Bit 3",
347*5238f60fSWilliam Breathitt Gray 	"Port 0 Bit 4", "Port 0 Bit 5", "Port 0 Bit 6", "Port 0 Bit 7",
348*5238f60fSWilliam Breathitt Gray 	"Port 1 Bit 0", "Port 1 Bit 1", "Port 1 Bit 2", "Port 1 Bit 3",
349*5238f60fSWilliam Breathitt Gray 	"Port 1 Bit 4", "Port 1 Bit 5", "Port 1 Bit 6", "Port 1 Bit 7",
350*5238f60fSWilliam Breathitt Gray 	"Port 2 Bit 0", "Port 2 Bit 1", "Port 2 Bit 2", "Port 2 Bit 3",
351*5238f60fSWilliam Breathitt Gray 	"Port 2 Bit 4", "Port 2 Bit 5", "Port 2 Bit 6", "Port 2 Bit 7",
352*5238f60fSWilliam Breathitt Gray 	"Port 3 Bit 0", "Port 3 Bit 1", "Port 3 Bit 2", "Port 3 Bit 3",
353*5238f60fSWilliam Breathitt Gray 	"Port 3 Bit 4", "Port 3 Bit 5", "Port 3 Bit 6", "Port 3 Bit 7",
354*5238f60fSWilliam Breathitt Gray 	"Port 4 Bit 0", "Port 4 Bit 1", "Port 4 Bit 2", "Port 4 Bit 3",
355*5238f60fSWilliam Breathitt Gray 	"Port 4 Bit 4", "Port 4 Bit 5", "Port 4 Bit 6", "Port 4 Bit 7",
356*5238f60fSWilliam Breathitt Gray 	"Port 5 Bit 0", "Port 5 Bit 1", "Port 5 Bit 2", "Port 5 Bit 3",
357*5238f60fSWilliam Breathitt Gray 	"Port 5 Bit 4", "Port 5 Bit 5", "Port 5 Bit 6", "Port 5 Bit 7"
358*5238f60fSWilliam Breathitt Gray };
359*5238f60fSWilliam Breathitt Gray 
360cc736607SWilliam Breathitt Gray static int ws16c48_probe(struct device *dev, unsigned int id)
3619c26df9bSWilliam Breathitt Gray {
3629c26df9bSWilliam Breathitt Gray 	struct ws16c48_gpio *ws16c48gpio;
3639c26df9bSWilliam Breathitt Gray 	const char *const name = dev_name(dev);
3649c26df9bSWilliam Breathitt Gray 	int err;
3659c26df9bSWilliam Breathitt Gray 
3669c26df9bSWilliam Breathitt Gray 	ws16c48gpio = devm_kzalloc(dev, sizeof(*ws16c48gpio), GFP_KERNEL);
3679c26df9bSWilliam Breathitt Gray 	if (!ws16c48gpio)
3689c26df9bSWilliam Breathitt Gray 		return -ENOMEM;
3699c26df9bSWilliam Breathitt Gray 
370cc736607SWilliam Breathitt Gray 	if (!devm_request_region(dev, base[id], WS16C48_EXTENT, name)) {
371148ad68bSWilliam Breathitt Gray 		dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
372cc736607SWilliam Breathitt Gray 			base[id], base[id] + WS16C48_EXTENT);
373148ad68bSWilliam Breathitt Gray 		return -EBUSY;
3749c26df9bSWilliam Breathitt Gray 	}
3759c26df9bSWilliam Breathitt Gray 
3769c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.label = name;
3779c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.parent = dev;
3789c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.owner = THIS_MODULE;
3799c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.base = -1;
380*5238f60fSWilliam Breathitt Gray 	ws16c48gpio->chip.ngpio = WS16C48_NGPIO;
381*5238f60fSWilliam Breathitt Gray 	ws16c48gpio->chip.names = ws16c48_names;
3829c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.get_direction = ws16c48_gpio_get_direction;
3839c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.direction_input = ws16c48_gpio_direction_input;
3849c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.direction_output = ws16c48_gpio_direction_output;
3859c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.get = ws16c48_gpio_get;
3869c26df9bSWilliam Breathitt Gray 	ws16c48gpio->chip.set = ws16c48_gpio_set;
38799c8ac95SWilliam Breathitt Gray 	ws16c48gpio->chip.set_multiple = ws16c48_gpio_set_multiple;
388cc736607SWilliam Breathitt Gray 	ws16c48gpio->base = base[id];
3899c26df9bSWilliam Breathitt Gray 
3909c26df9bSWilliam Breathitt Gray 	spin_lock_init(&ws16c48gpio->lock);
3919c26df9bSWilliam Breathitt Gray 
392b4cad1bcSWilliam Breathitt Gray 	err = devm_gpiochip_add_data(dev, &ws16c48gpio->chip, ws16c48gpio);
3939c26df9bSWilliam Breathitt Gray 	if (err) {
3949c26df9bSWilliam Breathitt Gray 		dev_err(dev, "GPIO registering failed (%d)\n", err);
395148ad68bSWilliam Breathitt Gray 		return err;
3969c26df9bSWilliam Breathitt Gray 	}
3979c26df9bSWilliam Breathitt Gray 
3989c26df9bSWilliam Breathitt Gray 	/* Disable IRQ by default */
399cc736607SWilliam Breathitt Gray 	outb(0x80, base[id] + 7);
400cc736607SWilliam Breathitt Gray 	outb(0, base[id] + 8);
401cc736607SWilliam Breathitt Gray 	outb(0, base[id] + 9);
402cc736607SWilliam Breathitt Gray 	outb(0, base[id] + 10);
403cc736607SWilliam Breathitt Gray 	outb(0xC0, base[id] + 7);
4049c26df9bSWilliam Breathitt Gray 
4059c26df9bSWilliam Breathitt Gray 	err = gpiochip_irqchip_add(&ws16c48gpio->chip, &ws16c48_irqchip, 0,
4069c26df9bSWilliam Breathitt Gray 		handle_edge_irq, IRQ_TYPE_NONE);
4079c26df9bSWilliam Breathitt Gray 	if (err) {
4089c26df9bSWilliam Breathitt Gray 		dev_err(dev, "Could not add irqchip (%d)\n", err);
4099c26df9bSWilliam Breathitt Gray 		return err;
4109c26df9bSWilliam Breathitt Gray 	}
4119c26df9bSWilliam Breathitt Gray 
412b4cad1bcSWilliam Breathitt Gray 	err = devm_request_irq(dev, irq[id], ws16c48_irq_handler, IRQF_SHARED,
413b4cad1bcSWilliam Breathitt Gray 		name, ws16c48gpio);
414b4cad1bcSWilliam Breathitt Gray 	if (err) {
415b4cad1bcSWilliam Breathitt Gray 		dev_err(dev, "IRQ handler registering failed (%d)\n", err);
416b4cad1bcSWilliam Breathitt Gray 		return err;
417b4cad1bcSWilliam Breathitt Gray 	}
4189c26df9bSWilliam Breathitt Gray 
4199c26df9bSWilliam Breathitt Gray 	return 0;
4209c26df9bSWilliam Breathitt Gray }
4219c26df9bSWilliam Breathitt Gray 
422cc736607SWilliam Breathitt Gray static struct isa_driver ws16c48_driver = {
423cc736607SWilliam Breathitt Gray 	.probe = ws16c48_probe,
4249c26df9bSWilliam Breathitt Gray 	.driver = {
4259c26df9bSWilliam Breathitt Gray 		.name = "ws16c48"
4269c26df9bSWilliam Breathitt Gray 	},
4279c26df9bSWilliam Breathitt Gray };
4289c26df9bSWilliam Breathitt Gray 
429cc736607SWilliam Breathitt Gray module_isa_driver(ws16c48_driver, num_ws16c48);
4309c26df9bSWilliam Breathitt Gray 
4319c26df9bSWilliam Breathitt Gray MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
4329c26df9bSWilliam Breathitt Gray MODULE_DESCRIPTION("WinSystems WS16C48 GPIO driver");
43322aeddb5SWilliam Breathitt Gray MODULE_LICENSE("GPL v2");
434