blob: 34be7dd9f5b9764f79b1cddda9422307a34440c7 [file] [log] [blame]
Thomas Gleixner1802d0b2019-05-27 08:55:21 +02001// SPDX-License-Identifier: GPL-2.0-only
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -05002/*
3 * GPIO driver for the ACCES 104-IDI-48 family
4 * Copyright (C) 2015 William Breathitt Gray
5 *
William Breathitt Gray72bf7442016-05-01 18:44:55 -04006 * This driver supports the following ACCES devices: 104-IDI-48A,
7 * 104-IDI-48AC, 104-IDI-48B, and 104-IDI-48BC.
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -05008 */
William Breathitt Grayf72b1072018-03-22 09:00:31 -04009#include <linux/bitmap.h>
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -050010#include <linux/bitops.h>
11#include <linux/device.h>
12#include <linux/errno.h>
13#include <linux/gpio/driver.h>
14#include <linux/io.h>
15#include <linux/ioport.h>
16#include <linux/interrupt.h>
17#include <linux/irqdesc.h>
William Breathitt Gray72bf7442016-05-01 18:44:55 -040018#include <linux/isa.h>
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -050019#include <linux/kernel.h>
20#include <linux/module.h>
21#include <linux/moduleparam.h>
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -050022#include <linux/spinlock.h>
23
William Breathitt Gray72bf7442016-05-01 18:44:55 -040024#define IDI_48_EXTENT 8
25#define MAX_NUM_IDI_48 max_num_isa_dev(IDI_48_EXTENT)
26
27static unsigned int base[MAX_NUM_IDI_48];
28static unsigned int num_idi_48;
David Howellsd759f902017-04-04 16:54:22 +010029module_param_hw_array(base, uint, ioport, &num_idi_48, 0);
William Breathitt Gray72bf7442016-05-01 18:44:55 -040030MODULE_PARM_DESC(base, "ACCES 104-IDI-48 base addresses");
31
32static unsigned int irq[MAX_NUM_IDI_48];
David Howellsd759f902017-04-04 16:54:22 +010033module_param_hw_array(irq, uint, irq, NULL, 0);
William Breathitt Gray72bf7442016-05-01 18:44:55 -040034MODULE_PARM_DESC(irq, "ACCES 104-IDI-48 interrupt line numbers");
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -050035
36/**
37 * struct idi_48_gpio - GPIO device private data structure
38 * @chip: instance of the gpio_chip
39 * @lock: synchronization lock to prevent I/O race conditions
William Breathitt Gray9ae48212015-12-15 18:52:44 -050040 * @ack_lock: synchronization lock to prevent IRQ handler race conditions
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -050041 * @irq_mask: input bits affected by interrupts
42 * @base: base port address of the GPIO device
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -050043 * @cos_enb: Change-Of-State IRQ enable boundaries mask
44 */
45struct idi_48_gpio {
46 struct gpio_chip chip;
Julia Cartwrighte1e37d62017-03-21 17:43:07 -050047 raw_spinlock_t lock;
William Breathitt Gray9ae48212015-12-15 18:52:44 -050048 spinlock_t ack_lock;
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -050049 unsigned char irq_mask[6];
50 unsigned base;
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -050051 unsigned char cos_enb;
52};
53
54static int idi_48_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
55{
Matti Vaittinene42615e2019-11-06 10:54:12 +020056 return GPIO_LINE_DIRECTION_IN;
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -050057}
58
59static int idi_48_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
60{
61 return 0;
62}
63
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -050064static int idi_48_gpio_get(struct gpio_chip *chip, unsigned offset)
65{
Linus Walleij1f36bec2015-12-03 15:31:48 +010066 struct idi_48_gpio *const idi48gpio = gpiochip_get_data(chip);
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -050067 unsigned i;
Colin Ian Kingac4062a2019-10-06 15:42:56 +010068 static const unsigned int register_offset[6] = { 0, 1, 2, 4, 5, 6 };
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -050069 unsigned base_offset;
70 unsigned mask;
71
72 for (i = 0; i < 48; i += 8)
73 if (offset < i + 8) {
74 base_offset = register_offset[i / 8];
75 mask = BIT(offset - i);
76
77 return !!(inb(idi48gpio->base + base_offset) & mask);
78 }
79
80 /* The following line should never execute since offset < 48 */
81 return 0;
82}
83
William Breathitt Grayf72b1072018-03-22 09:00:31 -040084static int idi_48_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
85 unsigned long *bits)
86{
87 struct idi_48_gpio *const idi48gpio = gpiochip_get_data(chip);
William Breathitt Gray9bfcce02019-12-04 16:51:08 -080088 unsigned long offset;
89 unsigned long gpio_mask;
Colin Ian King413f9e92018-04-18 18:26:34 +010090 static const size_t ports[] = { 0, 1, 2, 4, 5, 6 };
William Breathitt Gray9bfcce02019-12-04 16:51:08 -080091 unsigned int port_addr;
William Breathitt Grayf72b1072018-03-22 09:00:31 -040092 unsigned long port_state;
93
94 /* clear bits array to a clean slate */
95 bitmap_zero(bits, chip->ngpio);
96
William Breathitt Gray9bfcce02019-12-04 16:51:08 -080097 for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) {
98 port_addr = idi48gpio->base + ports[offset / 8];
99 port_state = inb(port_addr) & gpio_mask;
William Breathitt Grayf72b1072018-03-22 09:00:31 -0400100
William Breathitt Gray9bfcce02019-12-04 16:51:08 -0800101 bitmap_set_value8(bits, port_state, offset);
William Breathitt Grayf72b1072018-03-22 09:00:31 -0400102 }
103
104 return 0;
105}
106
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500107static void idi_48_irq_ack(struct irq_data *data)
108{
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500109}
110
111static void idi_48_irq_mask(struct irq_data *data)
112{
113 struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
Linus Walleij1f36bec2015-12-03 15:31:48 +0100114 struct idi_48_gpio *const idi48gpio = gpiochip_get_data(chip);
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500115 const unsigned offset = irqd_to_hwirq(data);
116 unsigned i;
117 unsigned mask;
118 unsigned boundary;
119 unsigned long flags;
120
121 for (i = 0; i < 48; i += 8)
122 if (offset < i + 8) {
123 mask = BIT(offset - i);
124 boundary = i / 8;
125
126 idi48gpio->irq_mask[boundary] &= ~mask;
127
128 if (!idi48gpio->irq_mask[boundary]) {
129 idi48gpio->cos_enb &= ~BIT(boundary);
130
Julia Cartwrighte1e37d62017-03-21 17:43:07 -0500131 raw_spin_lock_irqsave(&idi48gpio->lock, flags);
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500132
133 outb(idi48gpio->cos_enb, idi48gpio->base + 7);
134
Deepak R Varmab9bf9712020-10-14 00:32:12 +0530135 raw_spin_unlock_irqrestore(&idi48gpio->lock, flags);
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500136 }
137
138 return;
139 }
140}
141
142static void idi_48_irq_unmask(struct irq_data *data)
143{
144 struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
Linus Walleij1f36bec2015-12-03 15:31:48 +0100145 struct idi_48_gpio *const idi48gpio = gpiochip_get_data(chip);
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500146 const unsigned offset = irqd_to_hwirq(data);
147 unsigned i;
148 unsigned mask;
149 unsigned boundary;
150 unsigned prev_irq_mask;
151 unsigned long flags;
152
153 for (i = 0; i < 48; i += 8)
154 if (offset < i + 8) {
155 mask = BIT(offset - i);
156 boundary = i / 8;
157 prev_irq_mask = idi48gpio->irq_mask[boundary];
158
159 idi48gpio->irq_mask[boundary] |= mask;
160
161 if (!prev_irq_mask) {
162 idi48gpio->cos_enb |= BIT(boundary);
163
Julia Cartwrighte1e37d62017-03-21 17:43:07 -0500164 raw_spin_lock_irqsave(&idi48gpio->lock, flags);
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500165
166 outb(idi48gpio->cos_enb, idi48gpio->base + 7);
167
Deepak R Varmab9bf9712020-10-14 00:32:12 +0530168 raw_spin_unlock_irqrestore(&idi48gpio->lock, flags);
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500169 }
170
171 return;
172 }
173}
174
175static int idi_48_irq_set_type(struct irq_data *data, unsigned flow_type)
176{
177 /* The only valid irq types are none and both-edges */
178 if (flow_type != IRQ_TYPE_NONE &&
179 (flow_type & IRQ_TYPE_EDGE_BOTH) != IRQ_TYPE_EDGE_BOTH)
180 return -EINVAL;
181
182 return 0;
183}
184
185static struct irq_chip idi_48_irqchip = {
186 .name = "104-idi-48",
187 .irq_ack = idi_48_irq_ack,
188 .irq_mask = idi_48_irq_mask,
189 .irq_unmask = idi_48_irq_unmask,
190 .irq_set_type = idi_48_irq_set_type
191};
192
193static irqreturn_t idi_48_irq_handler(int irq, void *dev_id)
194{
195 struct idi_48_gpio *const idi48gpio = dev_id;
196 unsigned long cos_status;
197 unsigned long boundary;
198 unsigned long irq_mask;
199 unsigned long bit_num;
200 unsigned long gpio;
201 struct gpio_chip *const chip = &idi48gpio->chip;
202
William Breathitt Gray9ae48212015-12-15 18:52:44 -0500203 spin_lock(&idi48gpio->ack_lock);
204
Julia Cartwrighte1e37d62017-03-21 17:43:07 -0500205 raw_spin_lock(&idi48gpio->lock);
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500206
207 cos_status = inb(idi48gpio->base + 7);
208
Julia Cartwrighte1e37d62017-03-21 17:43:07 -0500209 raw_spin_unlock(&idi48gpio->lock);
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500210
211 /* IRQ Status (bit 6) is active low (0 = IRQ generated by device) */
William Breathitt Gray9ae48212015-12-15 18:52:44 -0500212 if (cos_status & BIT(6)) {
213 spin_unlock(&idi48gpio->ack_lock);
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500214 return IRQ_NONE;
William Breathitt Gray9ae48212015-12-15 18:52:44 -0500215 }
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500216
217 /* Bit 0-5 indicate which Change-Of-State boundary triggered the IRQ */
218 cos_status &= 0x3F;
219
220 for_each_set_bit(boundary, &cos_status, 6) {
221 irq_mask = idi48gpio->irq_mask[boundary];
222
223 for_each_set_bit(bit_num, &irq_mask, 8) {
224 gpio = bit_num + boundary * 8;
225
Marc Zyngierdbd1c542021-05-04 17:42:18 +0100226 generic_handle_domain_irq(chip->irq.domain,
227 gpio);
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500228 }
229 }
230
William Breathitt Gray9ae48212015-12-15 18:52:44 -0500231 spin_unlock(&idi48gpio->ack_lock);
232
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500233 return IRQ_HANDLED;
234}
235
William Breathitt Graya71dc252017-01-30 13:33:11 -0500236#define IDI48_NGPIO 48
237static const char *idi48_names[IDI48_NGPIO] = {
238 "Bit 0 A", "Bit 1 A", "Bit 2 A", "Bit 3 A", "Bit 4 A", "Bit 5 A",
239 "Bit 6 A", "Bit 7 A", "Bit 8 A", "Bit 9 A", "Bit 10 A", "Bit 11 A",
240 "Bit 12 A", "Bit 13 A", "Bit 14 A", "Bit 15 A", "Bit 16 A", "Bit 17 A",
241 "Bit 18 A", "Bit 19 A", "Bit 20 A", "Bit 21 A", "Bit 22 A", "Bit 23 A",
242 "Bit 0 B", "Bit 1 B", "Bit 2 B", "Bit 3 B", "Bit 4 B", "Bit 5 B",
243 "Bit 6 B", "Bit 7 B", "Bit 8 B", "Bit 9 B", "Bit 10 B", "Bit 11 B",
244 "Bit 12 B", "Bit 13 B", "Bit 14 B", "Bit 15 B", "Bit 16 B", "Bit 17 B",
245 "Bit 18 B", "Bit 19 B", "Bit 20 B", "Bit 21 B", "Bit 22 B", "Bit 23 B"
246};
247
Linus Walleij44b01cf2020-07-22 12:48:20 +0200248static int idi_48_irq_init_hw(struct gpio_chip *gc)
249{
250 struct idi_48_gpio *const idi48gpio = gpiochip_get_data(gc);
251
252 /* Disable IRQ by default */
253 outb(0, idi48gpio->base + 7);
254 inb(idi48gpio->base + 7);
255
256 return 0;
257}
258
William Breathitt Gray72bf7442016-05-01 18:44:55 -0400259static int idi_48_probe(struct device *dev, unsigned int id)
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500260{
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500261 struct idi_48_gpio *idi48gpio;
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500262 const char *const name = dev_name(dev);
Linus Walleij44b01cf2020-07-22 12:48:20 +0200263 struct gpio_irq_chip *girq;
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500264 int err;
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500265
266 idi48gpio = devm_kzalloc(dev, sizeof(*idi48gpio), GFP_KERNEL);
267 if (!idi48gpio)
268 return -ENOMEM;
269
William Breathitt Gray72bf7442016-05-01 18:44:55 -0400270 if (!devm_request_region(dev, base[id], IDI_48_EXTENT, name)) {
William Breathitt Gray5cfc0572016-02-03 15:15:22 -0500271 dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
William Breathitt Gray72bf7442016-05-01 18:44:55 -0400272 base[id], base[id] + IDI_48_EXTENT);
William Breathitt Gray5cfc0572016-02-03 15:15:22 -0500273 return -EBUSY;
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500274 }
275
276 idi48gpio->chip.label = name;
277 idi48gpio->chip.parent = dev;
278 idi48gpio->chip.owner = THIS_MODULE;
279 idi48gpio->chip.base = -1;
William Breathitt Graya71dc252017-01-30 13:33:11 -0500280 idi48gpio->chip.ngpio = IDI48_NGPIO;
281 idi48gpio->chip.names = idi48_names;
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500282 idi48gpio->chip.get_direction = idi_48_gpio_get_direction;
283 idi48gpio->chip.direction_input = idi_48_gpio_direction_input;
284 idi48gpio->chip.get = idi_48_gpio_get;
William Breathitt Grayf72b1072018-03-22 09:00:31 -0400285 idi48gpio->chip.get_multiple = idi_48_gpio_get_multiple;
William Breathitt Gray72bf7442016-05-01 18:44:55 -0400286 idi48gpio->base = base[id];
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500287
Linus Walleij44b01cf2020-07-22 12:48:20 +0200288 girq = &idi48gpio->chip.irq;
289 girq->chip = &idi_48_irqchip;
290 /* This will let us handle the parent IRQ in the driver */
291 girq->parent_handler = NULL;
292 girq->num_parents = 0;
293 girq->parents = NULL;
294 girq->default_type = IRQ_TYPE_NONE;
295 girq->handler = handle_edge_irq;
296 girq->init_hw = idi_48_irq_init_hw;
297
Julia Cartwrighte1e37d62017-03-21 17:43:07 -0500298 raw_spin_lock_init(&idi48gpio->lock);
Axel Lin053ae642016-06-13 13:48:53 +0800299 spin_lock_init(&idi48gpio->ack_lock);
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500300
William Breathitt Graye43fee62017-01-24 15:00:43 -0500301 err = devm_gpiochip_add_data(dev, &idi48gpio->chip, idi48gpio);
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500302 if (err) {
303 dev_err(dev, "GPIO registering failed (%d)\n", err);
William Breathitt Gray5cfc0572016-02-03 15:15:22 -0500304 return err;
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500305 }
306
William Breathitt Graye43fee62017-01-24 15:00:43 -0500307 err = devm_request_irq(dev, irq[id], idi_48_irq_handler, IRQF_SHARED,
308 name, idi48gpio);
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500309 if (err) {
310 dev_err(dev, "IRQ handler registering failed (%d)\n", err);
William Breathitt Graye43fee62017-01-24 15:00:43 -0500311 return err;
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500312 }
313
314 return 0;
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500315}
316
William Breathitt Gray72bf7442016-05-01 18:44:55 -0400317static struct isa_driver idi_48_driver = {
318 .probe = idi_48_probe,
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500319 .driver = {
320 .name = "104-idi-48"
321 },
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500322};
William Breathitt Gray72bf7442016-05-01 18:44:55 -0400323module_isa_driver(idi_48_driver, num_idi_48);
William Breathitt Gray6ddcf9b2015-11-23 12:54:50 -0500324
325MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
326MODULE_DESCRIPTION("ACCES 104-IDI-48 GPIO driver");
William Breathitt Gray22aeddb2016-02-01 18:51:49 -0500327MODULE_LICENSE("GPL v2");