11ceacea2SWilliam Breathitt Gray /*
21ceacea2SWilliam Breathitt Gray  * GPIO driver for the ACCES 104-IDIO-16 family
31ceacea2SWilliam Breathitt Gray  * Copyright (C) 2015 William Breathitt Gray
41ceacea2SWilliam Breathitt Gray  *
51ceacea2SWilliam Breathitt Gray  * This program is free software; you can redistribute it and/or modify
61ceacea2SWilliam Breathitt Gray  * it under the terms of the GNU General Public License, version 2, as
71ceacea2SWilliam Breathitt Gray  * published by the Free Software Foundation.
81ceacea2SWilliam Breathitt Gray  *
91ceacea2SWilliam Breathitt Gray  * This program is distributed in the hope that it will be useful, but
101ceacea2SWilliam Breathitt Gray  * WITHOUT ANY WARRANTY; without even the implied warranty of
111ceacea2SWilliam Breathitt Gray  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
121ceacea2SWilliam Breathitt Gray  * General Public License for more details.
131ceacea2SWilliam Breathitt Gray  */
14a1184147SWilliam Breathitt Gray #include <linux/bitops.h>
151ceacea2SWilliam Breathitt Gray #include <linux/device.h>
161ceacea2SWilliam Breathitt Gray #include <linux/errno.h>
171ceacea2SWilliam Breathitt Gray #include <linux/gpio/driver.h>
181ceacea2SWilliam Breathitt Gray #include <linux/io.h>
191ceacea2SWilliam Breathitt Gray #include <linux/ioport.h>
20a1184147SWilliam Breathitt Gray #include <linux/interrupt.h>
21a1184147SWilliam Breathitt Gray #include <linux/irqdesc.h>
221ceacea2SWilliam Breathitt Gray #include <linux/kernel.h>
231ceacea2SWilliam Breathitt Gray #include <linux/module.h>
241ceacea2SWilliam Breathitt Gray #include <linux/moduleparam.h>
251ceacea2SWilliam Breathitt Gray #include <linux/platform_device.h>
261ceacea2SWilliam Breathitt Gray #include <linux/spinlock.h>
271ceacea2SWilliam Breathitt Gray 
281ceacea2SWilliam Breathitt Gray static unsigned idio_16_base;
291ceacea2SWilliam Breathitt Gray module_param(idio_16_base, uint, 0);
301ceacea2SWilliam Breathitt Gray MODULE_PARM_DESC(idio_16_base, "ACCES 104-IDIO-16 base address");
31a1184147SWilliam Breathitt Gray static unsigned idio_16_irq;
32a1184147SWilliam Breathitt Gray module_param(idio_16_irq, uint, 0);
33a1184147SWilliam Breathitt Gray MODULE_PARM_DESC(idio_16_irq, "ACCES 104-IDIO-16 interrupt line number");
341ceacea2SWilliam Breathitt Gray 
351ceacea2SWilliam Breathitt Gray /**
361ceacea2SWilliam Breathitt Gray  * struct idio_16_gpio - GPIO device private data structure
371ceacea2SWilliam Breathitt Gray  * @chip:	instance of the gpio_chip
38a1184147SWilliam Breathitt Gray  * @lock:	synchronization lock to prevent I/O race conditions
39a1184147SWilliam Breathitt Gray  * @irq_mask:	I/O bits affected by interrupts
401ceacea2SWilliam Breathitt Gray  * @base:	base port address of the GPIO device
411ceacea2SWilliam Breathitt Gray  * @extent:	extent of port address region of the GPIO device
42a1184147SWilliam Breathitt Gray  * @irq:	Interrupt line number
431ceacea2SWilliam Breathitt Gray  * @out_state:	output bits state
441ceacea2SWilliam Breathitt Gray  */
451ceacea2SWilliam Breathitt Gray struct idio_16_gpio {
461ceacea2SWilliam Breathitt Gray 	struct gpio_chip chip;
471ceacea2SWilliam Breathitt Gray 	spinlock_t lock;
48a1184147SWilliam Breathitt Gray 	unsigned long irq_mask;
491ceacea2SWilliam Breathitt Gray 	unsigned base;
501ceacea2SWilliam Breathitt Gray 	unsigned extent;
51a1184147SWilliam Breathitt Gray 	unsigned irq;
521ceacea2SWilliam Breathitt Gray 	unsigned out_state;
531ceacea2SWilliam Breathitt Gray };
541ceacea2SWilliam Breathitt Gray 
551ceacea2SWilliam Breathitt Gray static int idio_16_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
561ceacea2SWilliam Breathitt Gray {
571ceacea2SWilliam Breathitt Gray 	if (offset > 15)
581ceacea2SWilliam Breathitt Gray 		return 1;
591ceacea2SWilliam Breathitt Gray 
601ceacea2SWilliam Breathitt Gray 	return 0;
611ceacea2SWilliam Breathitt Gray }
621ceacea2SWilliam Breathitt Gray 
631ceacea2SWilliam Breathitt Gray static int idio_16_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
641ceacea2SWilliam Breathitt Gray {
651ceacea2SWilliam Breathitt Gray 	return 0;
661ceacea2SWilliam Breathitt Gray }
671ceacea2SWilliam Breathitt Gray 
681ceacea2SWilliam Breathitt Gray static int idio_16_gpio_direction_output(struct gpio_chip *chip,
691ceacea2SWilliam Breathitt Gray 	unsigned offset, int value)
701ceacea2SWilliam Breathitt Gray {
711ceacea2SWilliam Breathitt Gray 	chip->set(chip, offset, value);
721ceacea2SWilliam Breathitt Gray 	return 0;
731ceacea2SWilliam Breathitt Gray }
741ceacea2SWilliam Breathitt Gray 
751ceacea2SWilliam Breathitt Gray static struct idio_16_gpio *to_idio16gpio(struct gpio_chip *gc)
761ceacea2SWilliam Breathitt Gray {
771ceacea2SWilliam Breathitt Gray 	return container_of(gc, struct idio_16_gpio, chip);
781ceacea2SWilliam Breathitt Gray }
791ceacea2SWilliam Breathitt Gray 
801ceacea2SWilliam Breathitt Gray static int idio_16_gpio_get(struct gpio_chip *chip, unsigned offset)
811ceacea2SWilliam Breathitt Gray {
821ceacea2SWilliam Breathitt Gray 	struct idio_16_gpio *const idio16gpio = to_idio16gpio(chip);
836e0171b4SWilliam Breathitt Gray 	const unsigned mask = BIT(offset-16);
841ceacea2SWilliam Breathitt Gray 
851ceacea2SWilliam Breathitt Gray 	if (offset < 16)
861ceacea2SWilliam Breathitt Gray 		return -EINVAL;
871ceacea2SWilliam Breathitt Gray 
881ceacea2SWilliam Breathitt Gray 	if (offset < 24)
896e0171b4SWilliam Breathitt Gray 		return !!(inb(idio16gpio->base + 1) & mask);
901ceacea2SWilliam Breathitt Gray 
916e0171b4SWilliam Breathitt Gray 	return !!(inb(idio16gpio->base + 5) & (mask>>8));
921ceacea2SWilliam Breathitt Gray }
931ceacea2SWilliam Breathitt Gray 
941ceacea2SWilliam Breathitt Gray static void idio_16_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
951ceacea2SWilliam Breathitt Gray {
961ceacea2SWilliam Breathitt Gray 	struct idio_16_gpio *const idio16gpio = to_idio16gpio(chip);
976e0171b4SWilliam Breathitt Gray 	const unsigned mask = BIT(offset);
981ceacea2SWilliam Breathitt Gray 	unsigned long flags;
991ceacea2SWilliam Breathitt Gray 
1001ceacea2SWilliam Breathitt Gray 	if (offset > 15)
1011ceacea2SWilliam Breathitt Gray 		return;
1021ceacea2SWilliam Breathitt Gray 
1031ceacea2SWilliam Breathitt Gray 	spin_lock_irqsave(&idio16gpio->lock, flags);
1041ceacea2SWilliam Breathitt Gray 
1051ceacea2SWilliam Breathitt Gray 	if (value)
1066e0171b4SWilliam Breathitt Gray 		idio16gpio->out_state |= mask;
1071ceacea2SWilliam Breathitt Gray 	else
1086e0171b4SWilliam Breathitt Gray 		idio16gpio->out_state &= ~mask;
1091ceacea2SWilliam Breathitt Gray 
1101ceacea2SWilliam Breathitt Gray 	if (offset > 7)
1111ceacea2SWilliam Breathitt Gray 		outb(idio16gpio->out_state >> 8, idio16gpio->base + 4);
1121ceacea2SWilliam Breathitt Gray 	else
1131ceacea2SWilliam Breathitt Gray 		outb(idio16gpio->out_state, idio16gpio->base);
1141ceacea2SWilliam Breathitt Gray 
1151ceacea2SWilliam Breathitt Gray 	spin_unlock_irqrestore(&idio16gpio->lock, flags);
1161ceacea2SWilliam Breathitt Gray }
1171ceacea2SWilliam Breathitt Gray 
118a1184147SWilliam Breathitt Gray static void idio_16_irq_ack(struct irq_data *data)
119a1184147SWilliam Breathitt Gray {
120a1184147SWilliam Breathitt Gray 	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
121a1184147SWilliam Breathitt Gray 	struct idio_16_gpio *const idio16gpio = to_idio16gpio(chip);
122a1184147SWilliam Breathitt Gray 	unsigned long flags;
123a1184147SWilliam Breathitt Gray 
124a1184147SWilliam Breathitt Gray 	spin_lock_irqsave(&idio16gpio->lock, flags);
125a1184147SWilliam Breathitt Gray 
126a1184147SWilliam Breathitt Gray 	outb(0, idio16gpio->base + 1);
127a1184147SWilliam Breathitt Gray 
128a1184147SWilliam Breathitt Gray 	spin_unlock_irqrestore(&idio16gpio->lock, flags);
129a1184147SWilliam Breathitt Gray }
130a1184147SWilliam Breathitt Gray 
131a1184147SWilliam Breathitt Gray static void idio_16_irq_mask(struct irq_data *data)
132a1184147SWilliam Breathitt Gray {
133a1184147SWilliam Breathitt Gray 	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
134a1184147SWilliam Breathitt Gray 	struct idio_16_gpio *const idio16gpio = to_idio16gpio(chip);
135a1184147SWilliam Breathitt Gray 	const unsigned long mask = BIT(irqd_to_hwirq(data));
136a1184147SWilliam Breathitt Gray 	unsigned long flags;
137a1184147SWilliam Breathitt Gray 
138a1184147SWilliam Breathitt Gray 	idio16gpio->irq_mask &= ~mask;
139a1184147SWilliam Breathitt Gray 
140a1184147SWilliam Breathitt Gray 	if (!idio16gpio->irq_mask) {
141a1184147SWilliam Breathitt Gray 		spin_lock_irqsave(&idio16gpio->lock, flags);
142a1184147SWilliam Breathitt Gray 
143a1184147SWilliam Breathitt Gray 		outb(0, idio16gpio->base + 2);
144a1184147SWilliam Breathitt Gray 
145a1184147SWilliam Breathitt Gray 		spin_unlock_irqrestore(&idio16gpio->lock, flags);
146a1184147SWilliam Breathitt Gray 	}
147a1184147SWilliam Breathitt Gray }
148a1184147SWilliam Breathitt Gray 
149a1184147SWilliam Breathitt Gray static void idio_16_irq_unmask(struct irq_data *data)
150a1184147SWilliam Breathitt Gray {
151a1184147SWilliam Breathitt Gray 	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
152a1184147SWilliam Breathitt Gray 	struct idio_16_gpio *const idio16gpio = to_idio16gpio(chip);
153a1184147SWilliam Breathitt Gray 	const unsigned long mask = BIT(irqd_to_hwirq(data));
154a1184147SWilliam Breathitt Gray 	const unsigned long prev_irq_mask = idio16gpio->irq_mask;
155a1184147SWilliam Breathitt Gray 	unsigned long flags;
156a1184147SWilliam Breathitt Gray 
157a1184147SWilliam Breathitt Gray 	idio16gpio->irq_mask |= mask;
158a1184147SWilliam Breathitt Gray 
159a1184147SWilliam Breathitt Gray 	if (!prev_irq_mask) {
160a1184147SWilliam Breathitt Gray 		spin_lock_irqsave(&idio16gpio->lock, flags);
161a1184147SWilliam Breathitt Gray 
162a1184147SWilliam Breathitt Gray 		outb(0, idio16gpio->base + 1);
163a1184147SWilliam Breathitt Gray 		inb(idio16gpio->base + 2);
164a1184147SWilliam Breathitt Gray 
165a1184147SWilliam Breathitt Gray 		spin_unlock_irqrestore(&idio16gpio->lock, flags);
166a1184147SWilliam Breathitt Gray 	}
167a1184147SWilliam Breathitt Gray }
168a1184147SWilliam Breathitt Gray 
169a1184147SWilliam Breathitt Gray static int idio_16_irq_set_type(struct irq_data *data, unsigned flow_type)
170a1184147SWilliam Breathitt Gray {
171a1184147SWilliam Breathitt Gray 	/* The only valid irq types are none and both-edges */
172a1184147SWilliam Breathitt Gray 	if (flow_type != IRQ_TYPE_NONE &&
173a1184147SWilliam Breathitt Gray 		(flow_type & IRQ_TYPE_EDGE_BOTH) != IRQ_TYPE_EDGE_BOTH)
174a1184147SWilliam Breathitt Gray 		return -EINVAL;
175a1184147SWilliam Breathitt Gray 
176a1184147SWilliam Breathitt Gray 	return 0;
177a1184147SWilliam Breathitt Gray }
178a1184147SWilliam Breathitt Gray 
179a1184147SWilliam Breathitt Gray static struct irq_chip idio_16_irqchip = {
180a1184147SWilliam Breathitt Gray 	.name = "104-idio-16",
181a1184147SWilliam Breathitt Gray 	.irq_ack = idio_16_irq_ack,
182a1184147SWilliam Breathitt Gray 	.irq_mask = idio_16_irq_mask,
183a1184147SWilliam Breathitt Gray 	.irq_unmask = idio_16_irq_unmask,
184a1184147SWilliam Breathitt Gray 	.irq_set_type = idio_16_irq_set_type
185a1184147SWilliam Breathitt Gray };
186a1184147SWilliam Breathitt Gray 
187a1184147SWilliam Breathitt Gray static irqreturn_t idio_16_irq_handler(int irq, void *dev_id)
188a1184147SWilliam Breathitt Gray {
189a1184147SWilliam Breathitt Gray 	struct idio_16_gpio *const idio16gpio = dev_id;
190a1184147SWilliam Breathitt Gray 	struct gpio_chip *const chip = &idio16gpio->chip;
191a1184147SWilliam Breathitt Gray 	int gpio;
192a1184147SWilliam Breathitt Gray 
193a1184147SWilliam Breathitt Gray 	for_each_set_bit(gpio, &idio16gpio->irq_mask, chip->ngpio)
194a1184147SWilliam Breathitt Gray 		generic_handle_irq(irq_find_mapping(chip->irqdomain, gpio));
195a1184147SWilliam Breathitt Gray 
196a1184147SWilliam Breathitt Gray 	return IRQ_HANDLED;
197a1184147SWilliam Breathitt Gray }
198a1184147SWilliam Breathitt Gray 
1991ceacea2SWilliam Breathitt Gray static int __init idio_16_probe(struct platform_device *pdev)
2001ceacea2SWilliam Breathitt Gray {
2011ceacea2SWilliam Breathitt Gray 	struct device *dev = &pdev->dev;
2021ceacea2SWilliam Breathitt Gray 	struct idio_16_gpio *idio16gpio;
2036e0171b4SWilliam Breathitt Gray 	const unsigned base = idio_16_base;
2046e0171b4SWilliam Breathitt Gray 	const unsigned extent = 8;
2056e0171b4SWilliam Breathitt Gray 	const char *const name = dev_name(dev);
2061ceacea2SWilliam Breathitt Gray 	int err;
2076e0171b4SWilliam Breathitt Gray 	const unsigned irq = idio_16_irq;
2081ceacea2SWilliam Breathitt Gray 
2091ceacea2SWilliam Breathitt Gray 	idio16gpio = devm_kzalloc(dev, sizeof(*idio16gpio), GFP_KERNEL);
2101ceacea2SWilliam Breathitt Gray 	if (!idio16gpio)
2111ceacea2SWilliam Breathitt Gray 		return -ENOMEM;
2121ceacea2SWilliam Breathitt Gray 
2136e0171b4SWilliam Breathitt Gray 	if (!request_region(base, extent, name)) {
2141ceacea2SWilliam Breathitt Gray 		dev_err(dev, "Unable to lock %s port addresses (0x%X-0x%X)\n",
2156e0171b4SWilliam Breathitt Gray 			name, base, base + extent);
2161ceacea2SWilliam Breathitt Gray 		err = -EBUSY;
2171ceacea2SWilliam Breathitt Gray 		goto err_lock_io_port;
2181ceacea2SWilliam Breathitt Gray 	}
2191ceacea2SWilliam Breathitt Gray 
2206e0171b4SWilliam Breathitt Gray 	idio16gpio->chip.label = name;
22158383c78SLinus Walleij 	idio16gpio->chip.parent = dev;
2221ceacea2SWilliam Breathitt Gray 	idio16gpio->chip.owner = THIS_MODULE;
2231ceacea2SWilliam Breathitt Gray 	idio16gpio->chip.base = -1;
2241ceacea2SWilliam Breathitt Gray 	idio16gpio->chip.ngpio = 32;
2251ceacea2SWilliam Breathitt Gray 	idio16gpio->chip.get_direction = idio_16_gpio_get_direction;
2261ceacea2SWilliam Breathitt Gray 	idio16gpio->chip.direction_input = idio_16_gpio_direction_input;
2271ceacea2SWilliam Breathitt Gray 	idio16gpio->chip.direction_output = idio_16_gpio_direction_output;
2281ceacea2SWilliam Breathitt Gray 	idio16gpio->chip.get = idio_16_gpio_get;
2291ceacea2SWilliam Breathitt Gray 	idio16gpio->chip.set = idio_16_gpio_set;
2306e0171b4SWilliam Breathitt Gray 	idio16gpio->base = base;
2316e0171b4SWilliam Breathitt Gray 	idio16gpio->extent = extent;
2326e0171b4SWilliam Breathitt Gray 	idio16gpio->irq = irq;
2331ceacea2SWilliam Breathitt Gray 	idio16gpio->out_state = 0xFFFF;
2341ceacea2SWilliam Breathitt Gray 
2351ceacea2SWilliam Breathitt Gray 	spin_lock_init(&idio16gpio->lock);
2361ceacea2SWilliam Breathitt Gray 
2371ceacea2SWilliam Breathitt Gray 	dev_set_drvdata(dev, idio16gpio);
2381ceacea2SWilliam Breathitt Gray 
2391ceacea2SWilliam Breathitt Gray 	err = gpiochip_add(&idio16gpio->chip);
2401ceacea2SWilliam Breathitt Gray 	if (err) {
2411ceacea2SWilliam Breathitt Gray 		dev_err(dev, "GPIO registering failed (%d)\n", err);
2421ceacea2SWilliam Breathitt Gray 		goto err_gpio_register;
2431ceacea2SWilliam Breathitt Gray 	}
2441ceacea2SWilliam Breathitt Gray 
245a1184147SWilliam Breathitt Gray 	err = gpiochip_irqchip_add(&idio16gpio->chip, &idio_16_irqchip, 0,
246a1184147SWilliam Breathitt Gray 		handle_edge_irq, IRQ_TYPE_NONE);
247a1184147SWilliam Breathitt Gray 	if (err) {
248a1184147SWilliam Breathitt Gray 		dev_err(dev, "Could not add irqchip (%d)\n", err);
249a1184147SWilliam Breathitt Gray 		goto err_gpiochip_irqchip_add;
250a1184147SWilliam Breathitt Gray 	}
251a1184147SWilliam Breathitt Gray 
2526e0171b4SWilliam Breathitt Gray 	err = request_irq(irq, idio_16_irq_handler, 0, name, idio16gpio);
253a1184147SWilliam Breathitt Gray 	if (err) {
254a1184147SWilliam Breathitt Gray 		dev_err(dev, "IRQ handler registering failed (%d)\n", err);
255a1184147SWilliam Breathitt Gray 		goto err_request_irq;
256a1184147SWilliam Breathitt Gray 	}
257a1184147SWilliam Breathitt Gray 
2581ceacea2SWilliam Breathitt Gray 	return 0;
2591ceacea2SWilliam Breathitt Gray 
260a1184147SWilliam Breathitt Gray err_request_irq:
261a1184147SWilliam Breathitt Gray err_gpiochip_irqchip_add:
262a1184147SWilliam Breathitt Gray 	gpiochip_remove(&idio16gpio->chip);
2631ceacea2SWilliam Breathitt Gray err_gpio_register:
2646e0171b4SWilliam Breathitt Gray 	release_region(base, extent);
2651ceacea2SWilliam Breathitt Gray err_lock_io_port:
2661ceacea2SWilliam Breathitt Gray 	return err;
2671ceacea2SWilliam Breathitt Gray }
2681ceacea2SWilliam Breathitt Gray 
2691ceacea2SWilliam Breathitt Gray static int idio_16_remove(struct platform_device *pdev)
2701ceacea2SWilliam Breathitt Gray {
2711ceacea2SWilliam Breathitt Gray 	struct idio_16_gpio *const idio16gpio = platform_get_drvdata(pdev);
2721ceacea2SWilliam Breathitt Gray 
273a1184147SWilliam Breathitt Gray 	free_irq(idio16gpio->irq, idio16gpio);
2741ceacea2SWilliam Breathitt Gray 	gpiochip_remove(&idio16gpio->chip);
2751ceacea2SWilliam Breathitt Gray 	release_region(idio16gpio->base, idio16gpio->extent);
2761ceacea2SWilliam Breathitt Gray 
2771ceacea2SWilliam Breathitt Gray 	return 0;
2781ceacea2SWilliam Breathitt Gray }
2791ceacea2SWilliam Breathitt Gray 
2801ceacea2SWilliam Breathitt Gray static struct platform_device *idio_16_device;
2811ceacea2SWilliam Breathitt Gray 
2821ceacea2SWilliam Breathitt Gray static struct platform_driver idio_16_driver = {
2831ceacea2SWilliam Breathitt Gray 	.driver = {
2841ceacea2SWilliam Breathitt Gray 		.name = "104-idio-16"
2851ceacea2SWilliam Breathitt Gray 	},
2861ceacea2SWilliam Breathitt Gray 	.remove = idio_16_remove
2871ceacea2SWilliam Breathitt Gray };
2881ceacea2SWilliam Breathitt Gray 
2891ceacea2SWilliam Breathitt Gray static void __exit idio_16_exit(void)
2901ceacea2SWilliam Breathitt Gray {
2911ceacea2SWilliam Breathitt Gray 	platform_device_unregister(idio_16_device);
2921ceacea2SWilliam Breathitt Gray 	platform_driver_unregister(&idio_16_driver);
2931ceacea2SWilliam Breathitt Gray }
2941ceacea2SWilliam Breathitt Gray 
2951ceacea2SWilliam Breathitt Gray static int __init idio_16_init(void)
2961ceacea2SWilliam Breathitt Gray {
2971ceacea2SWilliam Breathitt Gray 	int err;
2981ceacea2SWilliam Breathitt Gray 
2991ceacea2SWilliam Breathitt Gray 	idio_16_device = platform_device_alloc(idio_16_driver.driver.name, -1);
3001ceacea2SWilliam Breathitt Gray 	if (!idio_16_device)
3011ceacea2SWilliam Breathitt Gray 		return -ENOMEM;
3021ceacea2SWilliam Breathitt Gray 
3031ceacea2SWilliam Breathitt Gray 	err = platform_device_add(idio_16_device);
3041ceacea2SWilliam Breathitt Gray 	if (err)
3051ceacea2SWilliam Breathitt Gray 		goto err_platform_device;
3061ceacea2SWilliam Breathitt Gray 
3071ceacea2SWilliam Breathitt Gray 	err = platform_driver_probe(&idio_16_driver, idio_16_probe);
3081ceacea2SWilliam Breathitt Gray 	if (err)
3091ceacea2SWilliam Breathitt Gray 		goto err_platform_driver;
3101ceacea2SWilliam Breathitt Gray 
3111ceacea2SWilliam Breathitt Gray 	return 0;
3121ceacea2SWilliam Breathitt Gray 
3131ceacea2SWilliam Breathitt Gray err_platform_driver:
3141ceacea2SWilliam Breathitt Gray 	platform_device_del(idio_16_device);
3151ceacea2SWilliam Breathitt Gray err_platform_device:
3161ceacea2SWilliam Breathitt Gray 	platform_device_put(idio_16_device);
3171ceacea2SWilliam Breathitt Gray 	return err;
3181ceacea2SWilliam Breathitt Gray }
3191ceacea2SWilliam Breathitt Gray 
3201ceacea2SWilliam Breathitt Gray module_init(idio_16_init);
3211ceacea2SWilliam Breathitt Gray module_exit(idio_16_exit);
3221ceacea2SWilliam Breathitt Gray 
3231ceacea2SWilliam Breathitt Gray MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
3241ceacea2SWilliam Breathitt Gray MODULE_DESCRIPTION("ACCES 104-IDIO-16 GPIO driver");
3251ceacea2SWilliam Breathitt Gray MODULE_LICENSE("GPL");
326