diff options
author | William Breathitt Gray <vilhelm.gray@gmail.com> | 2016-01-20 13:50:11 -0500 |
---|---|---|
committer | Linus Walleij <linus.walleij@linaro.org> | 2016-01-28 03:51:16 -0500 |
commit | 1b06d64f73746c30ddba43bb57c30ba9a126f53b (patch) | |
tree | adb1e8b2177f6091a873ccd47c06a20d7de88c49 | |
parent | 9c26df9b27b67c607f4881551222f36d8bde865b (diff) |
gpio: Add GPIO support for the ACCES 104-DIO-48E
The ACCES 104-DIO-48E device provides 48 lines digital I/O via two
Programmable Peripheral Interface (PPI) chips of type 82C55. Bit C3 at
each 24-bit Group can be used as an external interrupt, triggered by a
rising edge.
This driver provides GPIO and IRQ support for these 48 channels of
digital I/O. The base port address for the device may be configured via
the dio_48e_base module parameter. The interrupt line number for the
device may be configured via the dio_48e_irq module parameter.
Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
-rw-r--r-- | MAINTAINERS | 6 | ||||
-rw-r--r-- | drivers/gpio/Kconfig | 9 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpio/gpio-104-dio-48e.c | 439 |
4 files changed, 455 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 0ba80b692723..f043e957698d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -240,6 +240,12 @@ L: lm-sensors@lm-sensors.org | |||
240 | S: Maintained | 240 | S: Maintained |
241 | F: drivers/hwmon/abituguru3.c | 241 | F: drivers/hwmon/abituguru3.c |
242 | 242 | ||
243 | ACCES 104-DIO-48E GPIO DRIVER | ||
244 | M: William Breathitt Gray <vilhelm.gray@gmail.com> | ||
245 | L: linux-gpio@vger.kernel.org | ||
246 | S: Maintained | ||
247 | F: drivers/gpio/gpio-104-dio-48e.c | ||
248 | |||
243 | ACCES 104-IDI-48 GPIO DRIVER | 249 | ACCES 104-IDI-48 GPIO DRIVER |
244 | M: "William Breathitt Gray" <vilhelm.gray@gmail.com> | 250 | M: "William Breathitt Gray" <vilhelm.gray@gmail.com> |
245 | L: linux-gpio@vger.kernel.org | 251 | L: linux-gpio@vger.kernel.org |
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index b5337f2e0eb4..5e317486a8cc 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig | |||
@@ -487,6 +487,15 @@ endmenu | |||
487 | menu "Port-mapped I/O GPIO drivers" | 487 | menu "Port-mapped I/O GPIO drivers" |
488 | depends on X86 # Unconditional I/O space access | 488 | depends on X86 # Unconditional I/O space access |
489 | 489 | ||
490 | config GPIO_104_DIO_48E | ||
491 | tristate "ACCES 104-DIO-48E GPIO support" | ||
492 | select GPIOLIB_IRQCHIP | ||
493 | help | ||
494 | Enables GPIO support for the ACCES 104-DIO-48E family. The base port | ||
495 | address for the device may be configured via the dio_48e_base module | ||
496 | parameter. The interrupt line number for the device may be configured | ||
497 | via the dio_48e_irq module parameter. | ||
498 | |||
490 | config GPIO_104_IDIO_16 | 499 | config GPIO_104_IDIO_16 |
491 | tristate "ACCES 104-IDIO-16 GPIO support" | 500 | tristate "ACCES 104-IDIO-16 GPIO support" |
492 | select GPIOLIB_IRQCHIP | 501 | select GPIOLIB_IRQCHIP |
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 73121b1b3b94..d4cc4f3d3674 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile | |||
@@ -12,6 +12,7 @@ obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o | |||
12 | # Device drivers. Generally keep list sorted alphabetically | 12 | # Device drivers. Generally keep list sorted alphabetically |
13 | obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o | 13 | obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o |
14 | 14 | ||
15 | obj-$(CONFIG_GPIO_104_DIO_48E) += gpio-104-dio-48e.o | ||
15 | obj-$(CONFIG_GPIO_104_IDIO_16) += gpio-104-idio-16.o | 16 | obj-$(CONFIG_GPIO_104_IDIO_16) += gpio-104-idio-16.o |
16 | obj-$(CONFIG_GPIO_104_IDI_48) += gpio-104-idi-48.o | 17 | obj-$(CONFIG_GPIO_104_IDI_48) += gpio-104-idi-48.o |
17 | obj-$(CONFIG_GPIO_74X164) += gpio-74x164.o | 18 | obj-$(CONFIG_GPIO_74X164) += gpio-74x164.o |
diff --git a/drivers/gpio/gpio-104-dio-48e.c b/drivers/gpio/gpio-104-dio-48e.c new file mode 100644 index 000000000000..279de392ac6c --- /dev/null +++ b/drivers/gpio/gpio-104-dio-48e.c | |||
@@ -0,0 +1,439 @@ | |||
1 | /* | ||
2 | * GPIO driver for the ACCES 104-DIO-48E | ||
3 | * Copyright (C) 2016 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/io.h> | ||
19 | #include <linux/ioport.h> | ||
20 | #include <linux/interrupt.h> | ||
21 | #include <linux/irqdesc.h> | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/moduleparam.h> | ||
25 | #include <linux/platform_device.h> | ||
26 | #include <linux/spinlock.h> | ||
27 | |||
28 | static unsigned dio_48e_base; | ||
29 | module_param(dio_48e_base, uint, 0); | ||
30 | MODULE_PARM_DESC(dio_48e_base, "ACCES 104-DIO-48E base address"); | ||
31 | static unsigned dio_48e_irq; | ||
32 | module_param(dio_48e_irq, uint, 0); | ||
33 | MODULE_PARM_DESC(dio_48e_irq, "ACCES 104-DIO-48E interrupt line number"); | ||
34 | |||
35 | /** | ||
36 | * struct dio48e_gpio - GPIO device private data structure | ||
37 | * @chip: instance of the gpio_chip | ||
38 | * @io_state: bit I/O state (whether bit is set to input or output) | ||
39 | * @out_state: output bits state | ||
40 | * @control: Control registers state | ||
41 | * @lock: synchronization lock to prevent I/O race conditions | ||
42 | * @base: base port address of the GPIO device | ||
43 | * @extent: extent of port address region of the GPIO device | ||
44 | * @irq: Interrupt line number | ||
45 | * @irq_mask: I/O bits affected by interrupts | ||
46 | */ | ||
47 | struct dio48e_gpio { | ||
48 | struct gpio_chip chip; | ||
49 | unsigned char io_state[6]; | ||
50 | unsigned char out_state[6]; | ||
51 | unsigned char control[2]; | ||
52 | spinlock_t lock; | ||
53 | unsigned base; | ||
54 | unsigned extent; | ||
55 | unsigned irq; | ||
56 | unsigned char irq_mask; | ||
57 | }; | ||
58 | |||
59 | static int dio48e_gpio_get_direction(struct gpio_chip *chip, unsigned offset) | ||
60 | { | ||
61 | struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip); | ||
62 | const unsigned port = offset / 8; | ||
63 | const unsigned mask = BIT(offset % 8); | ||
64 | |||
65 | return !!(dio48egpio->io_state[port] & mask); | ||
66 | } | ||
67 | |||
68 | static int dio48e_gpio_direction_input(struct gpio_chip *chip, unsigned offset) | ||
69 | { | ||
70 | struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip); | ||
71 | const unsigned io_port = offset / 8; | ||
72 | const unsigned control_port = io_port / 2; | ||
73 | const unsigned control_addr = dio48egpio->base + 3 + control_port*4; | ||
74 | unsigned long flags; | ||
75 | unsigned control; | ||
76 | |||
77 | spin_lock_irqsave(&dio48egpio->lock, flags); | ||
78 | |||
79 | /* Check if configuring Port C */ | ||
80 | if (io_port == 2 || io_port == 5) { | ||
81 | /* Port C can be configured by nibble */ | ||
82 | if (offset % 8 > 3) { | ||
83 | dio48egpio->io_state[io_port] |= 0xF0; | ||
84 | dio48egpio->control[control_port] |= BIT(3); | ||
85 | } else { | ||
86 | dio48egpio->io_state[io_port] |= 0x0F; | ||
87 | dio48egpio->control[control_port] |= BIT(0); | ||
88 | } | ||
89 | } else { | ||
90 | dio48egpio->io_state[io_port] |= 0xFF; | ||
91 | if (io_port == 0 || io_port == 3) | ||
92 | dio48egpio->control[control_port] |= BIT(4); | ||
93 | else | ||
94 | dio48egpio->control[control_port] |= BIT(1); | ||
95 | } | ||
96 | |||
97 | control = BIT(7) | dio48egpio->control[control_port]; | ||
98 | outb(control, control_addr); | ||
99 | control &= ~BIT(7); | ||
100 | outb(control, control_addr); | ||
101 | |||
102 | spin_unlock_irqrestore(&dio48egpio->lock, flags); | ||
103 | |||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | static int dio48e_gpio_direction_output(struct gpio_chip *chip, unsigned offset, | ||
108 | int value) | ||
109 | { | ||
110 | struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip); | ||
111 | const unsigned io_port = offset / 8; | ||
112 | const unsigned control_port = io_port / 2; | ||
113 | const unsigned mask = BIT(offset % 8); | ||
114 | const unsigned control_addr = dio48egpio->base + 3 + control_port*4; | ||
115 | const unsigned out_port = (io_port > 2) ? io_port + 1 : io_port; | ||
116 | unsigned long flags; | ||
117 | unsigned control; | ||
118 | |||
119 | spin_lock_irqsave(&dio48egpio->lock, flags); | ||
120 | |||
121 | /* Check if configuring Port C */ | ||
122 | if (io_port == 2 || io_port == 5) { | ||
123 | /* Port C can be configured by nibble */ | ||
124 | if (offset % 8 > 3) { | ||
125 | dio48egpio->io_state[io_port] &= 0x0F; | ||
126 | dio48egpio->control[control_port] &= ~BIT(3); | ||
127 | } else { | ||
128 | dio48egpio->io_state[io_port] &= 0xF0; | ||
129 | dio48egpio->control[control_port] &= ~BIT(0); | ||
130 | } | ||
131 | } else { | ||
132 | dio48egpio->io_state[io_port] &= 0x00; | ||
133 | if (io_port == 0 || io_port == 3) | ||
134 | dio48egpio->control[control_port] &= ~BIT(4); | ||
135 | else | ||
136 | dio48egpio->control[control_port] &= ~BIT(1); | ||
137 | } | ||
138 | |||
139 | if (value) | ||
140 | dio48egpio->out_state[io_port] |= mask; | ||
141 | else | ||
142 | dio48egpio->out_state[io_port] &= ~mask; | ||
143 | |||
144 | control = BIT(7) | dio48egpio->control[control_port]; | ||
145 | outb(control, control_addr); | ||
146 | |||
147 | outb(dio48egpio->out_state[io_port], dio48egpio->base + out_port); | ||
148 | |||
149 | control &= ~BIT(7); | ||
150 | outb(control, control_addr); | ||
151 | |||
152 | spin_unlock_irqrestore(&dio48egpio->lock, flags); | ||
153 | |||
154 | return 0; | ||
155 | } | ||
156 | |||
157 | static int dio48e_gpio_get(struct gpio_chip *chip, unsigned offset) | ||
158 | { | ||
159 | struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip); | ||
160 | const unsigned port = offset / 8; | ||
161 | const unsigned mask = BIT(offset % 8); | ||
162 | const unsigned in_port = (port > 2) ? port + 1 : port; | ||
163 | unsigned long flags; | ||
164 | unsigned port_state; | ||
165 | |||
166 | spin_lock_irqsave(&dio48egpio->lock, flags); | ||
167 | |||
168 | /* ensure that GPIO is set for input */ | ||
169 | if (!(dio48egpio->io_state[port] & mask)) { | ||
170 | spin_unlock_irqrestore(&dio48egpio->lock, flags); | ||
171 | return -EINVAL; | ||
172 | } | ||
173 | |||
174 | port_state = inb(dio48egpio->base + in_port); | ||
175 | |||
176 | spin_unlock_irqrestore(&dio48egpio->lock, flags); | ||
177 | |||
178 | return !!(port_state & mask); | ||
179 | } | ||
180 | |||
181 | static void dio48e_gpio_set(struct gpio_chip *chip, unsigned offset, int value) | ||
182 | { | ||
183 | struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip); | ||
184 | const unsigned port = offset / 8; | ||
185 | const unsigned mask = BIT(offset % 8); | ||
186 | const unsigned out_port = (port > 2) ? port + 1 : port; | ||
187 | unsigned long flags; | ||
188 | |||
189 | spin_lock_irqsave(&dio48egpio->lock, flags); | ||
190 | |||
191 | if (value) | ||
192 | dio48egpio->out_state[port] |= mask; | ||
193 | else | ||
194 | dio48egpio->out_state[port] &= ~mask; | ||
195 | |||
196 | outb(dio48egpio->out_state[port], dio48egpio->base + out_port); | ||
197 | |||
198 | spin_unlock_irqrestore(&dio48egpio->lock, flags); | ||
199 | } | ||
200 | |||
201 | static void dio48e_irq_ack(struct irq_data *data) | ||
202 | { | ||
203 | } | ||
204 | |||
205 | static void dio48e_irq_mask(struct irq_data *data) | ||
206 | { | ||
207 | struct gpio_chip *chip = irq_data_get_irq_chip_data(data); | ||
208 | struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip); | ||
209 | const unsigned long offset = irqd_to_hwirq(data); | ||
210 | unsigned long flags; | ||
211 | |||
212 | /* only bit 3 on each respective Port C supports interrupts */ | ||
213 | if (offset != 19 && offset != 43) | ||
214 | return; | ||
215 | |||
216 | spin_lock_irqsave(&dio48egpio->lock, flags); | ||
217 | |||
218 | if (offset == 19) | ||
219 | dio48egpio->irq_mask &= ~BIT(0); | ||
220 | else | ||
221 | dio48egpio->irq_mask &= ~BIT(1); | ||
222 | |||
223 | if (!dio48egpio->irq_mask) | ||
224 | /* disable interrupts */ | ||
225 | inb(dio48egpio->base + 0xB); | ||
226 | |||
227 | spin_unlock_irqrestore(&dio48egpio->lock, flags); | ||
228 | } | ||
229 | |||
230 | static void dio48e_irq_unmask(struct irq_data *data) | ||
231 | { | ||
232 | struct gpio_chip *chip = irq_data_get_irq_chip_data(data); | ||
233 | struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip); | ||
234 | const unsigned long offset = irqd_to_hwirq(data); | ||
235 | unsigned long flags; | ||
236 | |||
237 | /* only bit 3 on each respective Port C supports interrupts */ | ||
238 | if (offset != 19 && offset != 43) | ||
239 | return; | ||
240 | |||
241 | spin_lock_irqsave(&dio48egpio->lock, flags); | ||
242 | |||
243 | if (!dio48egpio->irq_mask) { | ||
244 | /* enable interrupts */ | ||
245 | outb(0x00, dio48egpio->base + 0xF); | ||
246 | outb(0x00, dio48egpio->base + 0xB); | ||
247 | } | ||
248 | |||
249 | if (offset == 19) | ||
250 | dio48egpio->irq_mask |= BIT(0); | ||
251 | else | ||
252 | dio48egpio->irq_mask |= BIT(1); | ||
253 | |||
254 | spin_unlock_irqrestore(&dio48egpio->lock, flags); | ||
255 | } | ||
256 | |||
257 | static int dio48e_irq_set_type(struct irq_data *data, unsigned flow_type) | ||
258 | { | ||
259 | const unsigned long offset = irqd_to_hwirq(data); | ||
260 | |||
261 | /* only bit 3 on each respective Port C supports interrupts */ | ||
262 | if (offset != 19 && offset != 43) | ||
263 | return -EINVAL; | ||
264 | |||
265 | if (flow_type != IRQ_TYPE_NONE && flow_type != IRQ_TYPE_EDGE_RISING) | ||
266 | return -EINVAL; | ||
267 | |||
268 | return 0; | ||
269 | } | ||
270 | |||
271 | static struct irq_chip dio48e_irqchip = { | ||
272 | .name = "104-dio-48e", | ||
273 | .irq_ack = dio48e_irq_ack, | ||
274 | .irq_mask = dio48e_irq_mask, | ||
275 | .irq_unmask = dio48e_irq_unmask, | ||
276 | .irq_set_type = dio48e_irq_set_type | ||
277 | }; | ||
278 | |||
279 | static irqreturn_t dio48e_irq_handler(int irq, void *dev_id) | ||
280 | { | ||
281 | struct dio48e_gpio *const dio48egpio = dev_id; | ||
282 | struct gpio_chip *const chip = &dio48egpio->chip; | ||
283 | const unsigned long irq_mask = dio48egpio->irq_mask; | ||
284 | unsigned long gpio; | ||
285 | |||
286 | for_each_set_bit(gpio, &irq_mask, 2) | ||
287 | generic_handle_irq(irq_find_mapping(chip->irqdomain, | ||
288 | 19 + gpio*24)); | ||
289 | |||
290 | spin_lock(&dio48egpio->lock); | ||
291 | |||
292 | outb(0x00, dio48egpio->base + 0xF); | ||
293 | |||
294 | spin_unlock(&dio48egpio->lock); | ||
295 | |||
296 | return IRQ_HANDLED; | ||
297 | } | ||
298 | |||
299 | static int __init dio48e_probe(struct platform_device *pdev) | ||
300 | { | ||
301 | struct device *dev = &pdev->dev; | ||
302 | struct dio48e_gpio *dio48egpio; | ||
303 | const unsigned base = dio_48e_base; | ||
304 | const unsigned extent = 16; | ||
305 | const char *const name = dev_name(dev); | ||
306 | int err; | ||
307 | const unsigned irq = dio_48e_irq; | ||
308 | |||
309 | dio48egpio = devm_kzalloc(dev, sizeof(*dio48egpio), GFP_KERNEL); | ||
310 | if (!dio48egpio) | ||
311 | return -ENOMEM; | ||
312 | |||
313 | if (!request_region(base, extent, name)) { | ||
314 | dev_err(dev, "Unable to lock %s port addresses (0x%X-0x%X)\n", | ||
315 | name, base, base + extent); | ||
316 | err = -EBUSY; | ||
317 | goto err_lock_io_port; | ||
318 | } | ||
319 | |||
320 | dio48egpio->chip.label = name; | ||
321 | dio48egpio->chip.parent = dev; | ||
322 | dio48egpio->chip.owner = THIS_MODULE; | ||
323 | dio48egpio->chip.base = -1; | ||
324 | dio48egpio->chip.ngpio = 48; | ||
325 | dio48egpio->chip.get_direction = dio48e_gpio_get_direction; | ||
326 | dio48egpio->chip.direction_input = dio48e_gpio_direction_input; | ||
327 | dio48egpio->chip.direction_output = dio48e_gpio_direction_output; | ||
328 | dio48egpio->chip.get = dio48e_gpio_get; | ||
329 | dio48egpio->chip.set = dio48e_gpio_set; | ||
330 | dio48egpio->base = base; | ||
331 | dio48egpio->extent = extent; | ||
332 | dio48egpio->irq = irq; | ||
333 | |||
334 | spin_lock_init(&dio48egpio->lock); | ||
335 | |||
336 | dev_set_drvdata(dev, dio48egpio); | ||
337 | |||
338 | err = gpiochip_add_data(&dio48egpio->chip, dio48egpio); | ||
339 | if (err) { | ||
340 | dev_err(dev, "GPIO registering failed (%d)\n", err); | ||
341 | goto err_gpio_register; | ||
342 | } | ||
343 | |||
344 | /* initialize all GPIO as output */ | ||
345 | outb(0x80, base + 3); | ||
346 | outb(0x00, base); | ||
347 | outb(0x00, base + 1); | ||
348 | outb(0x00, base + 2); | ||
349 | outb(0x00, base + 3); | ||
350 | outb(0x80, base + 7); | ||
351 | outb(0x00, base + 4); | ||
352 | outb(0x00, base + 5); | ||
353 | outb(0x00, base + 6); | ||
354 | outb(0x00, base + 7); | ||
355 | |||
356 | /* disable IRQ by default */ | ||
357 | inb(base + 0xB); | ||
358 | |||
359 | err = gpiochip_irqchip_add(&dio48egpio->chip, &dio48e_irqchip, 0, | ||
360 | handle_edge_irq, IRQ_TYPE_NONE); | ||
361 | if (err) { | ||
362 | dev_err(dev, "Could not add irqchip (%d)\n", err); | ||
363 | goto err_gpiochip_irqchip_add; | ||
364 | } | ||
365 | |||
366 | err = request_irq(irq, dio48e_irq_handler, 0, name, dio48egpio); | ||
367 | if (err) { | ||
368 | dev_err(dev, "IRQ handler registering failed (%d)\n", err); | ||
369 | goto err_request_irq; | ||
370 | } | ||
371 | |||
372 | return 0; | ||
373 | |||
374 | err_request_irq: | ||
375 | err_gpiochip_irqchip_add: | ||
376 | gpiochip_remove(&dio48egpio->chip); | ||
377 | err_gpio_register: | ||
378 | release_region(base, extent); | ||
379 | err_lock_io_port: | ||
380 | return err; | ||
381 | } | ||
382 | |||
383 | static int dio48e_remove(struct platform_device *pdev) | ||
384 | { | ||
385 | struct dio48e_gpio *const dio48egpio = platform_get_drvdata(pdev); | ||
386 | |||
387 | free_irq(dio48egpio->irq, dio48egpio); | ||
388 | gpiochip_remove(&dio48egpio->chip); | ||
389 | release_region(dio48egpio->base, dio48egpio->extent); | ||
390 | |||
391 | return 0; | ||
392 | } | ||
393 | |||
394 | static struct platform_device *dio48e_device; | ||
395 | |||
396 | static struct platform_driver dio48e_driver = { | ||
397 | .driver = { | ||
398 | .name = "104-dio-48e" | ||
399 | }, | ||
400 | .remove = dio48e_remove | ||
401 | }; | ||
402 | |||
403 | static void __exit dio48e_exit(void) | ||
404 | { | ||
405 | platform_device_unregister(dio48e_device); | ||
406 | platform_driver_unregister(&dio48e_driver); | ||
407 | } | ||
408 | |||
409 | static int __init dio48e_init(void) | ||
410 | { | ||
411 | int err; | ||
412 | |||
413 | dio48e_device = platform_device_alloc(dio48e_driver.driver.name, -1); | ||
414 | if (!dio48e_device) | ||
415 | return -ENOMEM; | ||
416 | |||
417 | err = platform_device_add(dio48e_device); | ||
418 | if (err) | ||
419 | goto err_platform_device; | ||
420 | |||
421 | err = platform_driver_probe(&dio48e_driver, dio48e_probe); | ||
422 | if (err) | ||
423 | goto err_platform_driver; | ||
424 | |||
425 | return 0; | ||
426 | |||
427 | err_platform_driver: | ||
428 | platform_device_del(dio48e_device); | ||
429 | err_platform_device: | ||
430 | platform_device_put(dio48e_device); | ||
431 | return err; | ||
432 | } | ||
433 | |||
434 | module_init(dio48e_init); | ||
435 | module_exit(dio48e_exit); | ||
436 | |||
437 | MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); | ||
438 | MODULE_DESCRIPTION("ACCES 104-DIO-48E GPIO driver"); | ||
439 | MODULE_LICENSE("GPL"); | ||