blob: 7de4f6a2cb494a47f81517fc46ac9fdbcc997ab2 [file] [log] [blame]
William Breathitt Gray02e74fc2017-02-01 15:37:55 -05001/*
2 * GPIO driver for the ACCES PCI-IDIO-16
3 * Copyright (C) 2017 William Breathitt Gray
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License, version 2, as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 */
14#include <linux/bitops.h>
15#include <linux/device.h>
16#include <linux/errno.h>
17#include <linux/gpio/driver.h>
18#include <linux/interrupt.h>
19#include <linux/irqdesc.h>
20#include <linux/kernel.h>
21#include <linux/module.h>
22#include <linux/pci.h>
23#include <linux/spinlock.h>
24#include <linux/types.h>
25
26/**
27 * struct idio_16_gpio_reg - GPIO device registers structure
28 * @out0_7: Read: FET Drive Outputs 0-7
29 * Write: FET Drive Outputs 0-7
30 * @in0_7: Read: Isolated Inputs 0-7
31 * Write: Clear Interrupt
32 * @irq_ctl: Read: Enable IRQ
33 * Write: Disable IRQ
34 * @filter_ctl: Read: Activate Input Filters 0-15
35 * Write: Deactivate Input Filters 0-15
36 * @out8_15: Read: FET Drive Outputs 8-15
37 * Write: FET Drive Outputs 8-15
38 * @in8_15: Read: Isolated Inputs 8-15
39 * Write: Unused
40 * @irq_status: Read: Interrupt status
41 * Write: Unused
42 */
43struct idio_16_gpio_reg {
44 u8 out0_7;
45 u8 in0_7;
46 u8 irq_ctl;
47 u8 filter_ctl;
48 u8 out8_15;
49 u8 in8_15;
50 u8 irq_status;
51};
52
53/**
54 * struct idio_16_gpio - GPIO device private data structure
55 * @chip: instance of the gpio_chip
56 * @lock: synchronization lock to prevent I/O race conditions
57 * @reg: I/O address offset for the GPIO device registers
58 * @irq_mask: I/O bits affected by interrupts
59 */
60struct idio_16_gpio {
61 struct gpio_chip chip;
Julia Cartwrightea38ce02017-03-21 17:43:09 -050062 raw_spinlock_t lock;
William Breathitt Gray02e74fc2017-02-01 15:37:55 -050063 struct idio_16_gpio_reg __iomem *reg;
64 unsigned long irq_mask;
65};
66
67static int idio_16_gpio_get_direction(struct gpio_chip *chip,
68 unsigned int offset)
69{
70 if (offset > 15)
71 return 1;
72
73 return 0;
74}
75
76static int idio_16_gpio_direction_input(struct gpio_chip *chip,
77 unsigned int offset)
78{
79 return 0;
80}
81
82static int idio_16_gpio_direction_output(struct gpio_chip *chip,
83 unsigned int offset, int value)
84{
85 chip->set(chip, offset, value);
86 return 0;
87}
88
89static int idio_16_gpio_get(struct gpio_chip *chip, unsigned int offset)
90{
91 struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
92 unsigned long mask = BIT(offset);
93
94 if (offset < 8)
95 return !!(ioread8(&idio16gpio->reg->out0_7) & mask);
96
97 if (offset < 16)
98 return !!(ioread8(&idio16gpio->reg->out8_15) & (mask >> 8));
99
100 if (offset < 24)
101 return !!(ioread8(&idio16gpio->reg->in0_7) & (mask >> 16));
102
103 return !!(ioread8(&idio16gpio->reg->in8_15) & (mask >> 24));
104}
105
106static void idio_16_gpio_set(struct gpio_chip *chip, unsigned int offset,
107 int value)
108{
109 struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
110 unsigned int mask = BIT(offset);
111 void __iomem *base;
112 unsigned long flags;
113 unsigned int out_state;
114
115 if (offset > 15)
116 return;
117
118 if (offset > 7) {
119 mask >>= 8;
120 base = &idio16gpio->reg->out8_15;
121 } else
122 base = &idio16gpio->reg->out0_7;
123
Julia Cartwrightea38ce02017-03-21 17:43:09 -0500124 raw_spin_lock_irqsave(&idio16gpio->lock, flags);
William Breathitt Gray02e74fc2017-02-01 15:37:55 -0500125
126 if (value)
127 out_state = ioread8(base) | mask;
128 else
129 out_state = ioread8(base) & ~mask;
130
131 iowrite8(out_state, base);
132
Julia Cartwrightea38ce02017-03-21 17:43:09 -0500133 raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
William Breathitt Gray02e74fc2017-02-01 15:37:55 -0500134}
135
136static void idio_16_gpio_set_multiple(struct gpio_chip *chip,
137 unsigned long *mask, unsigned long *bits)
138{
139 struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
140 unsigned long flags;
141 unsigned int out_state;
142
Julia Cartwrightea38ce02017-03-21 17:43:09 -0500143 raw_spin_lock_irqsave(&idio16gpio->lock, flags);
William Breathitt Gray02e74fc2017-02-01 15:37:55 -0500144
145 /* process output lines 0-7 */
146 if (*mask & 0xFF) {
147 out_state = ioread8(&idio16gpio->reg->out0_7) & ~*mask;
148 out_state |= *mask & *bits;
149 iowrite8(out_state, &idio16gpio->reg->out0_7);
150 }
151
152 /* shift to next output line word */
153 *mask >>= 8;
154
155 /* process output lines 8-15 */
156 if (*mask & 0xFF) {
157 *bits >>= 8;
158 out_state = ioread8(&idio16gpio->reg->out8_15) & ~*mask;
159 out_state |= *mask & *bits;
160 iowrite8(out_state, &idio16gpio->reg->out8_15);
161 }
162
Julia Cartwrightea38ce02017-03-21 17:43:09 -0500163 raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
William Breathitt Gray02e74fc2017-02-01 15:37:55 -0500164}
165
166static void idio_16_irq_ack(struct irq_data *data)
167{
168}
169
170static void idio_16_irq_mask(struct irq_data *data)
171{
172 struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
173 struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
174 const unsigned long mask = BIT(irqd_to_hwirq(data));
175 unsigned long flags;
176
177 idio16gpio->irq_mask &= ~mask;
178
179 if (!idio16gpio->irq_mask) {
Julia Cartwrightea38ce02017-03-21 17:43:09 -0500180 raw_spin_lock_irqsave(&idio16gpio->lock, flags);
William Breathitt Gray02e74fc2017-02-01 15:37:55 -0500181
182 iowrite8(0, &idio16gpio->reg->irq_ctl);
183
Julia Cartwrightea38ce02017-03-21 17:43:09 -0500184 raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
William Breathitt Gray02e74fc2017-02-01 15:37:55 -0500185 }
186}
187
188static void idio_16_irq_unmask(struct irq_data *data)
189{
190 struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
191 struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
192 const unsigned long mask = BIT(irqd_to_hwirq(data));
193 const unsigned long prev_irq_mask = idio16gpio->irq_mask;
194 unsigned long flags;
195
196 idio16gpio->irq_mask |= mask;
197
198 if (!prev_irq_mask) {
Julia Cartwrightea38ce02017-03-21 17:43:09 -0500199 raw_spin_lock_irqsave(&idio16gpio->lock, flags);
William Breathitt Gray02e74fc2017-02-01 15:37:55 -0500200
201 ioread8(&idio16gpio->reg->irq_ctl);
202
Julia Cartwrightea38ce02017-03-21 17:43:09 -0500203 raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
William Breathitt Gray02e74fc2017-02-01 15:37:55 -0500204 }
205}
206
207static int idio_16_irq_set_type(struct irq_data *data, unsigned int flow_type)
208{
209 /* The only valid irq types are none and both-edges */
210 if (flow_type != IRQ_TYPE_NONE &&
211 (flow_type & IRQ_TYPE_EDGE_BOTH) != IRQ_TYPE_EDGE_BOTH)
212 return -EINVAL;
213
214 return 0;
215}
216
217static struct irq_chip idio_16_irqchip = {
218 .name = "pci-idio-16",
219 .irq_ack = idio_16_irq_ack,
220 .irq_mask = idio_16_irq_mask,
221 .irq_unmask = idio_16_irq_unmask,
222 .irq_set_type = idio_16_irq_set_type
223};
224
225static irqreturn_t idio_16_irq_handler(int irq, void *dev_id)
226{
227 struct idio_16_gpio *const idio16gpio = dev_id;
228 unsigned int irq_status;
229 struct gpio_chip *const chip = &idio16gpio->chip;
230 int gpio;
231
Julia Cartwrightea38ce02017-03-21 17:43:09 -0500232 raw_spin_lock(&idio16gpio->lock);
William Breathitt Gray02e74fc2017-02-01 15:37:55 -0500233
234 irq_status = ioread8(&idio16gpio->reg->irq_status);
235
Julia Cartwrightea38ce02017-03-21 17:43:09 -0500236 raw_spin_unlock(&idio16gpio->lock);
William Breathitt Gray02e74fc2017-02-01 15:37:55 -0500237
238 /* Make sure our device generated IRQ */
239 if (!(irq_status & 0x3) || !(irq_status & 0x4))
240 return IRQ_NONE;
241
242 for_each_set_bit(gpio, &idio16gpio->irq_mask, chip->ngpio)
243 generic_handle_irq(irq_find_mapping(chip->irqdomain, gpio));
244
Julia Cartwrightea38ce02017-03-21 17:43:09 -0500245 raw_spin_lock(&idio16gpio->lock);
William Breathitt Gray02e74fc2017-02-01 15:37:55 -0500246
247 /* Clear interrupt */
248 iowrite8(0, &idio16gpio->reg->in0_7);
249
Julia Cartwrightea38ce02017-03-21 17:43:09 -0500250 raw_spin_unlock(&idio16gpio->lock);
William Breathitt Gray02e74fc2017-02-01 15:37:55 -0500251
252 return IRQ_HANDLED;
253}
254
255#define IDIO_16_NGPIO 32
256static const char *idio_16_names[IDIO_16_NGPIO] = {
257 "OUT0", "OUT1", "OUT2", "OUT3", "OUT4", "OUT5", "OUT6", "OUT7",
258 "OUT8", "OUT9", "OUT10", "OUT11", "OUT12", "OUT13", "OUT14", "OUT15",
259 "IIN0", "IIN1", "IIN2", "IIN3", "IIN4", "IIN5", "IIN6", "IIN7",
260 "IIN8", "IIN9", "IIN10", "IIN11", "IIN12", "IIN13", "IIN14", "IIN15"
261};
262
263static int idio_16_probe(struct pci_dev *pdev, const struct pci_device_id *id)
264{
265 struct device *const dev = &pdev->dev;
266 struct idio_16_gpio *idio16gpio;
267 int err;
William Breathitt Graydeab2b02017-02-08 08:32:46 -0500268 const size_t pci_bar_index = 2;
William Breathitt Gray02e74fc2017-02-01 15:37:55 -0500269 const char *const name = pci_name(pdev);
270
271 idio16gpio = devm_kzalloc(dev, sizeof(*idio16gpio), GFP_KERNEL);
272 if (!idio16gpio)
273 return -ENOMEM;
274
275 err = pcim_enable_device(pdev);
276 if (err) {
277 dev_err(dev, "Failed to enable PCI device (%d)\n", err);
278 return err;
279 }
280
William Breathitt Graydeab2b02017-02-08 08:32:46 -0500281 err = pcim_iomap_regions(pdev, BIT(pci_bar_index), name);
William Breathitt Gray02e74fc2017-02-01 15:37:55 -0500282 if (err) {
283 dev_err(dev, "Unable to map PCI I/O addresses (%d)\n", err);
284 return err;
285 }
286
William Breathitt Graydeab2b02017-02-08 08:32:46 -0500287 idio16gpio->reg = pcim_iomap_table(pdev)[pci_bar_index];
William Breathitt Gray02e74fc2017-02-01 15:37:55 -0500288
289 /* Deactivate input filters */
290 iowrite8(0, &idio16gpio->reg->filter_ctl);
291
292 idio16gpio->chip.label = name;
293 idio16gpio->chip.parent = dev;
294 idio16gpio->chip.owner = THIS_MODULE;
295 idio16gpio->chip.base = -1;
296 idio16gpio->chip.ngpio = IDIO_16_NGPIO;
297 idio16gpio->chip.names = idio_16_names;
298 idio16gpio->chip.get_direction = idio_16_gpio_get_direction;
299 idio16gpio->chip.direction_input = idio_16_gpio_direction_input;
300 idio16gpio->chip.direction_output = idio_16_gpio_direction_output;
301 idio16gpio->chip.get = idio_16_gpio_get;
302 idio16gpio->chip.set = idio_16_gpio_set;
303 idio16gpio->chip.set_multiple = idio_16_gpio_set_multiple;
304
Julia Cartwrightea38ce02017-03-21 17:43:09 -0500305 raw_spin_lock_init(&idio16gpio->lock);
William Breathitt Gray02e74fc2017-02-01 15:37:55 -0500306
307 err = devm_gpiochip_add_data(dev, &idio16gpio->chip, idio16gpio);
308 if (err) {
309 dev_err(dev, "GPIO registering failed (%d)\n", err);
310 return err;
311 }
312
313 /* Disable IRQ by default and clear any pending interrupt */
314 iowrite8(0, &idio16gpio->reg->irq_ctl);
315 iowrite8(0, &idio16gpio->reg->in0_7);
316
317 err = gpiochip_irqchip_add(&idio16gpio->chip, &idio_16_irqchip, 0,
318 handle_edge_irq, IRQ_TYPE_NONE);
319 if (err) {
320 dev_err(dev, "Could not add irqchip (%d)\n", err);
321 return err;
322 }
323
324 err = devm_request_irq(dev, pdev->irq, idio_16_irq_handler, IRQF_SHARED,
325 name, idio16gpio);
326 if (err) {
327 dev_err(dev, "IRQ handler registering failed (%d)\n", err);
328 return err;
329 }
330
331 return 0;
332}
333
334static const struct pci_device_id idio_16_pci_dev_id[] = {
William Breathitt Grayfd254a22017-02-07 11:51:19 -0500335 { PCI_DEVICE(0x494F, 0x0DC8) }, { 0 }
William Breathitt Gray02e74fc2017-02-01 15:37:55 -0500336};
337MODULE_DEVICE_TABLE(pci, idio_16_pci_dev_id);
338
339static struct pci_driver idio_16_driver = {
340 .name = "pci-idio-16",
341 .id_table = idio_16_pci_dev_id,
342 .probe = idio_16_probe
343};
344
345module_pci_driver(idio_16_driver);
346
347MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
348MODULE_DESCRIPTION("ACCES PCI-IDIO-16 GPIO driver");
349MODULE_LICENSE("GPL v2");