Loading...
1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * GPIO driver for the ACCES PCI-IDIO-16
4 * Copyright (C) 2017 William Breathitt Gray
5 */
6#include <linux/bits.h>
7#include <linux/device.h>
8#include <linux/errno.h>
9#include <linux/gpio/driver.h>
10#include <linux/interrupt.h>
11#include <linux/irqdesc.h>
12#include <linux/kernel.h>
13#include <linux/module.h>
14#include <linux/pci.h>
15#include <linux/spinlock.h>
16#include <linux/types.h>
17
18#include "gpio-idio-16.h"
19
20/**
21 * struct idio_16_gpio - GPIO device private data structure
22 * @chip: instance of the gpio_chip
23 * @lock: synchronization lock to prevent I/O race conditions
24 * @reg: I/O address offset for the GPIO device registers
25 * @state: ACCES IDIO-16 device state
26 * @irq_mask: I/O bits affected by interrupts
27 */
28struct idio_16_gpio {
29 struct gpio_chip chip;
30 raw_spinlock_t lock;
31 struct idio_16 __iomem *reg;
32 struct idio_16_state state;
33 unsigned long irq_mask;
34};
35
36static int idio_16_gpio_get_direction(struct gpio_chip *chip,
37 unsigned int offset)
38{
39 if (idio_16_get_direction(offset))
40 return GPIO_LINE_DIRECTION_IN;
41
42 return GPIO_LINE_DIRECTION_OUT;
43}
44
45static int idio_16_gpio_direction_input(struct gpio_chip *chip,
46 unsigned int offset)
47{
48 return 0;
49}
50
51static int idio_16_gpio_direction_output(struct gpio_chip *chip,
52 unsigned int offset, int value)
53{
54 chip->set(chip, offset, value);
55 return 0;
56}
57
58static int idio_16_gpio_get(struct gpio_chip *chip, unsigned int offset)
59{
60 struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
61
62 return idio_16_get(idio16gpio->reg, &idio16gpio->state, offset);
63}
64
65static int idio_16_gpio_get_multiple(struct gpio_chip *chip,
66 unsigned long *mask, unsigned long *bits)
67{
68 struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
69
70 idio_16_get_multiple(idio16gpio->reg, &idio16gpio->state, mask, bits);
71 return 0;
72}
73
74static void idio_16_gpio_set(struct gpio_chip *chip, unsigned int offset,
75 int value)
76{
77 struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
78
79 idio_16_set(idio16gpio->reg, &idio16gpio->state, offset, value);
80}
81
82static void idio_16_gpio_set_multiple(struct gpio_chip *chip,
83 unsigned long *mask, unsigned long *bits)
84{
85 struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
86
87 idio_16_set_multiple(idio16gpio->reg, &idio16gpio->state, mask, bits);
88}
89
90static void idio_16_irq_ack(struct irq_data *data)
91{
92}
93
94static void idio_16_irq_mask(struct irq_data *data)
95{
96 struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
97 struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
98 const unsigned long mask = BIT(irqd_to_hwirq(data));
99 unsigned long flags;
100
101 idio16gpio->irq_mask &= ~mask;
102
103 if (!idio16gpio->irq_mask) {
104 raw_spin_lock_irqsave(&idio16gpio->lock, flags);
105
106 iowrite8(0, &idio16gpio->reg->irq_ctl);
107
108 raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
109 }
110}
111
112static void idio_16_irq_unmask(struct irq_data *data)
113{
114 struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
115 struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
116 const unsigned long mask = BIT(irqd_to_hwirq(data));
117 const unsigned long prev_irq_mask = idio16gpio->irq_mask;
118 unsigned long flags;
119
120 idio16gpio->irq_mask |= mask;
121
122 if (!prev_irq_mask) {
123 raw_spin_lock_irqsave(&idio16gpio->lock, flags);
124
125 ioread8(&idio16gpio->reg->irq_ctl);
126
127 raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
128 }
129}
130
131static int idio_16_irq_set_type(struct irq_data *data, unsigned int flow_type)
132{
133 /* The only valid irq types are none and both-edges */
134 if (flow_type != IRQ_TYPE_NONE &&
135 (flow_type & IRQ_TYPE_EDGE_BOTH) != IRQ_TYPE_EDGE_BOTH)
136 return -EINVAL;
137
138 return 0;
139}
140
141static struct irq_chip idio_16_irqchip = {
142 .name = "pci-idio-16",
143 .irq_ack = idio_16_irq_ack,
144 .irq_mask = idio_16_irq_mask,
145 .irq_unmask = idio_16_irq_unmask,
146 .irq_set_type = idio_16_irq_set_type
147};
148
149static irqreturn_t idio_16_irq_handler(int irq, void *dev_id)
150{
151 struct idio_16_gpio *const idio16gpio = dev_id;
152 unsigned int irq_status;
153 struct gpio_chip *const chip = &idio16gpio->chip;
154 int gpio;
155
156 raw_spin_lock(&idio16gpio->lock);
157
158 irq_status = ioread8(&idio16gpio->reg->irq_status);
159
160 raw_spin_unlock(&idio16gpio->lock);
161
162 /* Make sure our device generated IRQ */
163 if (!(irq_status & 0x3) || !(irq_status & 0x4))
164 return IRQ_NONE;
165
166 for_each_set_bit(gpio, &idio16gpio->irq_mask, chip->ngpio)
167 generic_handle_domain_irq(chip->irq.domain, gpio);
168
169 raw_spin_lock(&idio16gpio->lock);
170
171 /* Clear interrupt */
172 iowrite8(0, &idio16gpio->reg->in0_7);
173
174 raw_spin_unlock(&idio16gpio->lock);
175
176 return IRQ_HANDLED;
177}
178
179#define IDIO_16_NGPIO 32
180static const char *idio_16_names[IDIO_16_NGPIO] = {
181 "OUT0", "OUT1", "OUT2", "OUT3", "OUT4", "OUT5", "OUT6", "OUT7",
182 "OUT8", "OUT9", "OUT10", "OUT11", "OUT12", "OUT13", "OUT14", "OUT15",
183 "IIN0", "IIN1", "IIN2", "IIN3", "IIN4", "IIN5", "IIN6", "IIN7",
184 "IIN8", "IIN9", "IIN10", "IIN11", "IIN12", "IIN13", "IIN14", "IIN15"
185};
186
187static int idio_16_irq_init_hw(struct gpio_chip *gc)
188{
189 struct idio_16_gpio *const idio16gpio = gpiochip_get_data(gc);
190
191 /* Disable IRQ by default and clear any pending interrupt */
192 iowrite8(0, &idio16gpio->reg->irq_ctl);
193 iowrite8(0, &idio16gpio->reg->in0_7);
194
195 return 0;
196}
197
198static int idio_16_probe(struct pci_dev *pdev, const struct pci_device_id *id)
199{
200 struct device *const dev = &pdev->dev;
201 struct idio_16_gpio *idio16gpio;
202 int err;
203 const size_t pci_bar_index = 2;
204 const char *const name = pci_name(pdev);
205 struct gpio_irq_chip *girq;
206
207 idio16gpio = devm_kzalloc(dev, sizeof(*idio16gpio), GFP_KERNEL);
208 if (!idio16gpio)
209 return -ENOMEM;
210
211 err = pcim_enable_device(pdev);
212 if (err) {
213 dev_err(dev, "Failed to enable PCI device (%d)\n", err);
214 return err;
215 }
216
217 err = pcim_iomap_regions(pdev, BIT(pci_bar_index), name);
218 if (err) {
219 dev_err(dev, "Unable to map PCI I/O addresses (%d)\n", err);
220 return err;
221 }
222
223 idio16gpio->reg = pcim_iomap_table(pdev)[pci_bar_index];
224
225 /* Deactivate input filters */
226 iowrite8(0, &idio16gpio->reg->filter_ctl);
227
228 idio16gpio->chip.label = name;
229 idio16gpio->chip.parent = dev;
230 idio16gpio->chip.owner = THIS_MODULE;
231 idio16gpio->chip.base = -1;
232 idio16gpio->chip.ngpio = IDIO_16_NGPIO;
233 idio16gpio->chip.names = idio_16_names;
234 idio16gpio->chip.get_direction = idio_16_gpio_get_direction;
235 idio16gpio->chip.direction_input = idio_16_gpio_direction_input;
236 idio16gpio->chip.direction_output = idio_16_gpio_direction_output;
237 idio16gpio->chip.get = idio_16_gpio_get;
238 idio16gpio->chip.get_multiple = idio_16_gpio_get_multiple;
239 idio16gpio->chip.set = idio_16_gpio_set;
240 idio16gpio->chip.set_multiple = idio_16_gpio_set_multiple;
241
242 idio_16_state_init(&idio16gpio->state);
243
244 girq = &idio16gpio->chip.irq;
245 girq->chip = &idio_16_irqchip;
246 /* This will let us handle the parent IRQ in the driver */
247 girq->parent_handler = NULL;
248 girq->num_parents = 0;
249 girq->parents = NULL;
250 girq->default_type = IRQ_TYPE_NONE;
251 girq->handler = handle_edge_irq;
252 girq->init_hw = idio_16_irq_init_hw;
253
254 raw_spin_lock_init(&idio16gpio->lock);
255
256 err = devm_gpiochip_add_data(dev, &idio16gpio->chip, idio16gpio);
257 if (err) {
258 dev_err(dev, "GPIO registering failed (%d)\n", err);
259 return err;
260 }
261
262 err = devm_request_irq(dev, pdev->irq, idio_16_irq_handler, IRQF_SHARED,
263 name, idio16gpio);
264 if (err) {
265 dev_err(dev, "IRQ handler registering failed (%d)\n", err);
266 return err;
267 }
268
269 return 0;
270}
271
272static const struct pci_device_id idio_16_pci_dev_id[] = {
273 { PCI_DEVICE(0x494F, 0x0DC8) }, { 0 }
274};
275MODULE_DEVICE_TABLE(pci, idio_16_pci_dev_id);
276
277static struct pci_driver idio_16_driver = {
278 .name = "pci-idio-16",
279 .id_table = idio_16_pci_dev_id,
280 .probe = idio_16_probe
281};
282
283module_pci_driver(idio_16_driver);
284
285MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
286MODULE_DESCRIPTION("ACCES PCI-IDIO-16 GPIO driver");
287MODULE_LICENSE("GPL v2");
288MODULE_IMPORT_NS(GPIO_IDIO_16);
1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * GPIO driver for the ACCES PCI-IDIO-16
4 * Copyright (C) 2017 William Breathitt Gray
5 */
6#include <linux/bitmap.h>
7#include <linux/bitops.h>
8#include <linux/device.h>
9#include <linux/errno.h>
10#include <linux/gpio/driver.h>
11#include <linux/interrupt.h>
12#include <linux/irqdesc.h>
13#include <linux/kernel.h>
14#include <linux/module.h>
15#include <linux/pci.h>
16#include <linux/spinlock.h>
17#include <linux/types.h>
18
19/**
20 * struct idio_16_gpio_reg - GPIO device registers structure
21 * @out0_7: Read: FET Drive Outputs 0-7
22 * Write: FET Drive Outputs 0-7
23 * @in0_7: Read: Isolated Inputs 0-7
24 * Write: Clear Interrupt
25 * @irq_ctl: Read: Enable IRQ
26 * Write: Disable IRQ
27 * @filter_ctl: Read: Activate Input Filters 0-15
28 * Write: Deactivate Input Filters 0-15
29 * @out8_15: Read: FET Drive Outputs 8-15
30 * Write: FET Drive Outputs 8-15
31 * @in8_15: Read: Isolated Inputs 8-15
32 * Write: Unused
33 * @irq_status: Read: Interrupt status
34 * Write: Unused
35 */
36struct idio_16_gpio_reg {
37 u8 out0_7;
38 u8 in0_7;
39 u8 irq_ctl;
40 u8 filter_ctl;
41 u8 out8_15;
42 u8 in8_15;
43 u8 irq_status;
44};
45
46/**
47 * struct idio_16_gpio - GPIO device private data structure
48 * @chip: instance of the gpio_chip
49 * @lock: synchronization lock to prevent I/O race conditions
50 * @reg: I/O address offset for the GPIO device registers
51 * @irq_mask: I/O bits affected by interrupts
52 */
53struct idio_16_gpio {
54 struct gpio_chip chip;
55 raw_spinlock_t lock;
56 struct idio_16_gpio_reg __iomem *reg;
57 unsigned long irq_mask;
58};
59
60static int idio_16_gpio_get_direction(struct gpio_chip *chip,
61 unsigned int offset)
62{
63 if (offset > 15)
64 return GPIO_LINE_DIRECTION_IN;
65
66 return GPIO_LINE_DIRECTION_OUT;
67}
68
69static int idio_16_gpio_direction_input(struct gpio_chip *chip,
70 unsigned int offset)
71{
72 return 0;
73}
74
75static int idio_16_gpio_direction_output(struct gpio_chip *chip,
76 unsigned int offset, int value)
77{
78 chip->set(chip, offset, value);
79 return 0;
80}
81
82static int idio_16_gpio_get(struct gpio_chip *chip, unsigned int offset)
83{
84 struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
85 unsigned long mask = BIT(offset);
86
87 if (offset < 8)
88 return !!(ioread8(&idio16gpio->reg->out0_7) & mask);
89
90 if (offset < 16)
91 return !!(ioread8(&idio16gpio->reg->out8_15) & (mask >> 8));
92
93 if (offset < 24)
94 return !!(ioread8(&idio16gpio->reg->in0_7) & (mask >> 16));
95
96 return !!(ioread8(&idio16gpio->reg->in8_15) & (mask >> 24));
97}
98
99static int idio_16_gpio_get_multiple(struct gpio_chip *chip,
100 unsigned long *mask, unsigned long *bits)
101{
102 struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
103 unsigned long offset;
104 unsigned long gpio_mask;
105 void __iomem *ports[] = {
106 &idio16gpio->reg->out0_7, &idio16gpio->reg->out8_15,
107 &idio16gpio->reg->in0_7, &idio16gpio->reg->in8_15,
108 };
109 void __iomem *port_addr;
110 unsigned long port_state;
111
112 /* clear bits array to a clean slate */
113 bitmap_zero(bits, chip->ngpio);
114
115 for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) {
116 port_addr = ports[offset / 8];
117 port_state = ioread8(port_addr) & gpio_mask;
118
119 bitmap_set_value8(bits, port_state, offset);
120 }
121
122 return 0;
123}
124
125static void idio_16_gpio_set(struct gpio_chip *chip, unsigned int offset,
126 int value)
127{
128 struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
129 unsigned int mask = BIT(offset);
130 void __iomem *base;
131 unsigned long flags;
132 unsigned int out_state;
133
134 if (offset > 15)
135 return;
136
137 if (offset > 7) {
138 mask >>= 8;
139 base = &idio16gpio->reg->out8_15;
140 } else
141 base = &idio16gpio->reg->out0_7;
142
143 raw_spin_lock_irqsave(&idio16gpio->lock, flags);
144
145 if (value)
146 out_state = ioread8(base) | mask;
147 else
148 out_state = ioread8(base) & ~mask;
149
150 iowrite8(out_state, base);
151
152 raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
153}
154
155static void idio_16_gpio_set_multiple(struct gpio_chip *chip,
156 unsigned long *mask, unsigned long *bits)
157{
158 struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
159 unsigned long offset;
160 unsigned long gpio_mask;
161 void __iomem *ports[] = {
162 &idio16gpio->reg->out0_7, &idio16gpio->reg->out8_15,
163 };
164 size_t index;
165 void __iomem *port_addr;
166 unsigned long bitmask;
167 unsigned long flags;
168 unsigned long out_state;
169
170 for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) {
171 index = offset / 8;
172 port_addr = ports[index];
173
174 bitmask = bitmap_get_value8(bits, offset) & gpio_mask;
175
176 raw_spin_lock_irqsave(&idio16gpio->lock, flags);
177
178 out_state = ioread8(port_addr) & ~gpio_mask;
179 out_state |= bitmask;
180 iowrite8(out_state, port_addr);
181
182 raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
183 }
184}
185
186static void idio_16_irq_ack(struct irq_data *data)
187{
188}
189
190static void idio_16_irq_mask(struct irq_data *data)
191{
192 struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
193 struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
194 const unsigned long mask = BIT(irqd_to_hwirq(data));
195 unsigned long flags;
196
197 idio16gpio->irq_mask &= ~mask;
198
199 if (!idio16gpio->irq_mask) {
200 raw_spin_lock_irqsave(&idio16gpio->lock, flags);
201
202 iowrite8(0, &idio16gpio->reg->irq_ctl);
203
204 raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
205 }
206}
207
208static void idio_16_irq_unmask(struct irq_data *data)
209{
210 struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
211 struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
212 const unsigned long mask = BIT(irqd_to_hwirq(data));
213 const unsigned long prev_irq_mask = idio16gpio->irq_mask;
214 unsigned long flags;
215
216 idio16gpio->irq_mask |= mask;
217
218 if (!prev_irq_mask) {
219 raw_spin_lock_irqsave(&idio16gpio->lock, flags);
220
221 ioread8(&idio16gpio->reg->irq_ctl);
222
223 raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
224 }
225}
226
227static int idio_16_irq_set_type(struct irq_data *data, unsigned int flow_type)
228{
229 /* The only valid irq types are none and both-edges */
230 if (flow_type != IRQ_TYPE_NONE &&
231 (flow_type & IRQ_TYPE_EDGE_BOTH) != IRQ_TYPE_EDGE_BOTH)
232 return -EINVAL;
233
234 return 0;
235}
236
237static struct irq_chip idio_16_irqchip = {
238 .name = "pci-idio-16",
239 .irq_ack = idio_16_irq_ack,
240 .irq_mask = idio_16_irq_mask,
241 .irq_unmask = idio_16_irq_unmask,
242 .irq_set_type = idio_16_irq_set_type
243};
244
245static irqreturn_t idio_16_irq_handler(int irq, void *dev_id)
246{
247 struct idio_16_gpio *const idio16gpio = dev_id;
248 unsigned int irq_status;
249 struct gpio_chip *const chip = &idio16gpio->chip;
250 int gpio;
251
252 raw_spin_lock(&idio16gpio->lock);
253
254 irq_status = ioread8(&idio16gpio->reg->irq_status);
255
256 raw_spin_unlock(&idio16gpio->lock);
257
258 /* Make sure our device generated IRQ */
259 if (!(irq_status & 0x3) || !(irq_status & 0x4))
260 return IRQ_NONE;
261
262 for_each_set_bit(gpio, &idio16gpio->irq_mask, chip->ngpio)
263 generic_handle_irq(irq_find_mapping(chip->irq.domain, gpio));
264
265 raw_spin_lock(&idio16gpio->lock);
266
267 /* Clear interrupt */
268 iowrite8(0, &idio16gpio->reg->in0_7);
269
270 raw_spin_unlock(&idio16gpio->lock);
271
272 return IRQ_HANDLED;
273}
274
275#define IDIO_16_NGPIO 32
276static const char *idio_16_names[IDIO_16_NGPIO] = {
277 "OUT0", "OUT1", "OUT2", "OUT3", "OUT4", "OUT5", "OUT6", "OUT7",
278 "OUT8", "OUT9", "OUT10", "OUT11", "OUT12", "OUT13", "OUT14", "OUT15",
279 "IIN0", "IIN1", "IIN2", "IIN3", "IIN4", "IIN5", "IIN6", "IIN7",
280 "IIN8", "IIN9", "IIN10", "IIN11", "IIN12", "IIN13", "IIN14", "IIN15"
281};
282
283static int idio_16_irq_init_hw(struct gpio_chip *gc)
284{
285 struct idio_16_gpio *const idio16gpio = gpiochip_get_data(gc);
286
287 /* Disable IRQ by default and clear any pending interrupt */
288 iowrite8(0, &idio16gpio->reg->irq_ctl);
289 iowrite8(0, &idio16gpio->reg->in0_7);
290
291 return 0;
292}
293
294static int idio_16_probe(struct pci_dev *pdev, const struct pci_device_id *id)
295{
296 struct device *const dev = &pdev->dev;
297 struct idio_16_gpio *idio16gpio;
298 int err;
299 const size_t pci_bar_index = 2;
300 const char *const name = pci_name(pdev);
301 struct gpio_irq_chip *girq;
302
303 idio16gpio = devm_kzalloc(dev, sizeof(*idio16gpio), GFP_KERNEL);
304 if (!idio16gpio)
305 return -ENOMEM;
306
307 err = pcim_enable_device(pdev);
308 if (err) {
309 dev_err(dev, "Failed to enable PCI device (%d)\n", err);
310 return err;
311 }
312
313 err = pcim_iomap_regions(pdev, BIT(pci_bar_index), name);
314 if (err) {
315 dev_err(dev, "Unable to map PCI I/O addresses (%d)\n", err);
316 return err;
317 }
318
319 idio16gpio->reg = pcim_iomap_table(pdev)[pci_bar_index];
320
321 /* Deactivate input filters */
322 iowrite8(0, &idio16gpio->reg->filter_ctl);
323
324 idio16gpio->chip.label = name;
325 idio16gpio->chip.parent = dev;
326 idio16gpio->chip.owner = THIS_MODULE;
327 idio16gpio->chip.base = -1;
328 idio16gpio->chip.ngpio = IDIO_16_NGPIO;
329 idio16gpio->chip.names = idio_16_names;
330 idio16gpio->chip.get_direction = idio_16_gpio_get_direction;
331 idio16gpio->chip.direction_input = idio_16_gpio_direction_input;
332 idio16gpio->chip.direction_output = idio_16_gpio_direction_output;
333 idio16gpio->chip.get = idio_16_gpio_get;
334 idio16gpio->chip.get_multiple = idio_16_gpio_get_multiple;
335 idio16gpio->chip.set = idio_16_gpio_set;
336 idio16gpio->chip.set_multiple = idio_16_gpio_set_multiple;
337
338 girq = &idio16gpio->chip.irq;
339 girq->chip = &idio_16_irqchip;
340 /* This will let us handle the parent IRQ in the driver */
341 girq->parent_handler = NULL;
342 girq->num_parents = 0;
343 girq->parents = NULL;
344 girq->default_type = IRQ_TYPE_NONE;
345 girq->handler = handle_edge_irq;
346 girq->init_hw = idio_16_irq_init_hw;
347
348 raw_spin_lock_init(&idio16gpio->lock);
349
350 err = devm_gpiochip_add_data(dev, &idio16gpio->chip, idio16gpio);
351 if (err) {
352 dev_err(dev, "GPIO registering failed (%d)\n", err);
353 return err;
354 }
355
356 err = devm_request_irq(dev, pdev->irq, idio_16_irq_handler, IRQF_SHARED,
357 name, idio16gpio);
358 if (err) {
359 dev_err(dev, "IRQ handler registering failed (%d)\n", err);
360 return err;
361 }
362
363 return 0;
364}
365
366static const struct pci_device_id idio_16_pci_dev_id[] = {
367 { PCI_DEVICE(0x494F, 0x0DC8) }, { 0 }
368};
369MODULE_DEVICE_TABLE(pci, idio_16_pci_dev_id);
370
371static struct pci_driver idio_16_driver = {
372 .name = "pci-idio-16",
373 .id_table = idio_16_pci_dev_id,
374 .probe = idio_16_probe
375};
376
377module_pci_driver(idio_16_driver);
378
379MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
380MODULE_DESCRIPTION("ACCES PCI-IDIO-16 GPIO driver");
381MODULE_LICENSE("GPL v2");