11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21ceacea2SWilliam Breathitt Gray /*
31ceacea2SWilliam Breathitt Gray  * GPIO driver for the ACCES 104-IDIO-16 family
41ceacea2SWilliam Breathitt Gray  * Copyright (C) 2015 William Breathitt Gray
51ceacea2SWilliam Breathitt Gray  *
686ea8a95SWilliam Breathitt Gray  * This driver supports the following ACCES devices: 104-IDIO-16,
786ea8a95SWilliam Breathitt Gray  * 104-IDIO-16E, 104-IDO-16, 104-IDIO-8, 104-IDIO-8E, and 104-IDO-8.
81ceacea2SWilliam Breathitt Gray  */
9a1184147SWilliam Breathitt Gray #include <linux/bitops.h>
101ceacea2SWilliam Breathitt Gray #include <linux/device.h>
111ceacea2SWilliam Breathitt Gray #include <linux/errno.h>
121ceacea2SWilliam Breathitt Gray #include <linux/gpio/driver.h>
131ceacea2SWilliam Breathitt Gray #include <linux/io.h>
141ceacea2SWilliam Breathitt Gray #include <linux/ioport.h>
15a1184147SWilliam Breathitt Gray #include <linux/interrupt.h>
16a1184147SWilliam Breathitt Gray #include <linux/irqdesc.h>
1786ea8a95SWilliam Breathitt Gray #include <linux/isa.h>
181ceacea2SWilliam Breathitt Gray #include <linux/kernel.h>
191ceacea2SWilliam Breathitt Gray #include <linux/module.h>
201ceacea2SWilliam Breathitt Gray #include <linux/moduleparam.h>
211ceacea2SWilliam Breathitt Gray #include <linux/spinlock.h>
221ceacea2SWilliam Breathitt Gray 
2386ea8a95SWilliam Breathitt Gray #define IDIO_16_EXTENT 8
2486ea8a95SWilliam Breathitt Gray #define MAX_NUM_IDIO_16 max_num_isa_dev(IDIO_16_EXTENT)
2586ea8a95SWilliam Breathitt Gray 
2686ea8a95SWilliam Breathitt Gray static unsigned int base[MAX_NUM_IDIO_16];
2786ea8a95SWilliam Breathitt Gray static unsigned int num_idio_16;
28d759f906SDavid Howells module_param_hw_array(base, uint, ioport, &num_idio_16, 0);
2986ea8a95SWilliam Breathitt Gray MODULE_PARM_DESC(base, "ACCES 104-IDIO-16 base addresses");
3086ea8a95SWilliam Breathitt Gray 
3186ea8a95SWilliam Breathitt Gray static unsigned int irq[MAX_NUM_IDIO_16];
32d759f906SDavid Howells module_param_hw_array(irq, uint, irq, NULL, 0);
3386ea8a95SWilliam Breathitt Gray MODULE_PARM_DESC(irq, "ACCES 104-IDIO-16 interrupt line numbers");
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  * @out_state:	output bits state
421ceacea2SWilliam Breathitt Gray  */
431ceacea2SWilliam Breathitt Gray struct idio_16_gpio {
441ceacea2SWilliam Breathitt Gray 	struct gpio_chip chip;
453906e808SJulia Cartwright 	raw_spinlock_t lock;
46a1184147SWilliam Breathitt Gray 	unsigned long irq_mask;
471ceacea2SWilliam Breathitt Gray 	unsigned base;
481ceacea2SWilliam Breathitt Gray 	unsigned out_state;
491ceacea2SWilliam Breathitt Gray };
501ceacea2SWilliam Breathitt Gray 
511ceacea2SWilliam Breathitt Gray static int idio_16_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
521ceacea2SWilliam Breathitt Gray {
531ceacea2SWilliam Breathitt Gray 	if (offset > 15)
541ceacea2SWilliam Breathitt Gray 		return 1;
551ceacea2SWilliam Breathitt Gray 
561ceacea2SWilliam Breathitt Gray 	return 0;
571ceacea2SWilliam Breathitt Gray }
581ceacea2SWilliam Breathitt Gray 
591ceacea2SWilliam Breathitt Gray static int idio_16_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
601ceacea2SWilliam Breathitt Gray {
611ceacea2SWilliam Breathitt Gray 	return 0;
621ceacea2SWilliam Breathitt Gray }
631ceacea2SWilliam Breathitt Gray 
641ceacea2SWilliam Breathitt Gray static int idio_16_gpio_direction_output(struct gpio_chip *chip,
651ceacea2SWilliam Breathitt Gray 	unsigned offset, int value)
661ceacea2SWilliam Breathitt Gray {
671ceacea2SWilliam Breathitt Gray 	chip->set(chip, offset, value);
681ceacea2SWilliam Breathitt Gray 	return 0;
691ceacea2SWilliam Breathitt Gray }
701ceacea2SWilliam Breathitt Gray 
711ceacea2SWilliam Breathitt Gray static int idio_16_gpio_get(struct gpio_chip *chip, unsigned offset)
721ceacea2SWilliam Breathitt Gray {
73d602ae90SLinus Walleij 	struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
746e0171b4SWilliam Breathitt Gray 	const unsigned mask = BIT(offset-16);
751ceacea2SWilliam Breathitt Gray 
761ceacea2SWilliam Breathitt Gray 	if (offset < 16)
771ceacea2SWilliam Breathitt Gray 		return -EINVAL;
781ceacea2SWilliam Breathitt Gray 
791ceacea2SWilliam Breathitt Gray 	if (offset < 24)
806e0171b4SWilliam Breathitt Gray 		return !!(inb(idio16gpio->base + 1) & mask);
811ceacea2SWilliam Breathitt Gray 
826e0171b4SWilliam Breathitt Gray 	return !!(inb(idio16gpio->base + 5) & (mask>>8));
831ceacea2SWilliam Breathitt Gray }
841ceacea2SWilliam Breathitt Gray 
8515f59cffSWilliam Breathitt Gray static int idio_16_gpio_get_multiple(struct gpio_chip *chip,
8615f59cffSWilliam Breathitt Gray 	unsigned long *mask, unsigned long *bits)
8715f59cffSWilliam Breathitt Gray {
8815f59cffSWilliam Breathitt Gray 	struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
8915f59cffSWilliam Breathitt Gray 
9015f59cffSWilliam Breathitt Gray 	*bits = 0;
9115f59cffSWilliam Breathitt Gray 	if (*mask & GENMASK(23, 16))
9215f59cffSWilliam Breathitt Gray 		*bits |= (unsigned long)inb(idio16gpio->base + 1) << 16;
9315f59cffSWilliam Breathitt Gray 	if (*mask & GENMASK(31, 24))
9415f59cffSWilliam Breathitt Gray 		*bits |= (unsigned long)inb(idio16gpio->base + 5) << 24;
9515f59cffSWilliam Breathitt Gray 
9615f59cffSWilliam Breathitt Gray 	return 0;
9715f59cffSWilliam Breathitt Gray }
9815f59cffSWilliam Breathitt Gray 
991ceacea2SWilliam Breathitt Gray static void idio_16_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
1001ceacea2SWilliam Breathitt Gray {
101d602ae90SLinus Walleij 	struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
1026e0171b4SWilliam Breathitt Gray 	const unsigned mask = BIT(offset);
1031ceacea2SWilliam Breathitt Gray 	unsigned long flags;
1041ceacea2SWilliam Breathitt Gray 
1051ceacea2SWilliam Breathitt Gray 	if (offset > 15)
1061ceacea2SWilliam Breathitt Gray 		return;
1071ceacea2SWilliam Breathitt Gray 
1083906e808SJulia Cartwright 	raw_spin_lock_irqsave(&idio16gpio->lock, flags);
1091ceacea2SWilliam Breathitt Gray 
1101ceacea2SWilliam Breathitt Gray 	if (value)
1116e0171b4SWilliam Breathitt Gray 		idio16gpio->out_state |= mask;
1121ceacea2SWilliam Breathitt Gray 	else
1136e0171b4SWilliam Breathitt Gray 		idio16gpio->out_state &= ~mask;
1141ceacea2SWilliam Breathitt Gray 
1151ceacea2SWilliam Breathitt Gray 	if (offset > 7)
1161ceacea2SWilliam Breathitt Gray 		outb(idio16gpio->out_state >> 8, idio16gpio->base + 4);
1171ceacea2SWilliam Breathitt Gray 	else
1181ceacea2SWilliam Breathitt Gray 		outb(idio16gpio->out_state, idio16gpio->base);
1191ceacea2SWilliam Breathitt Gray 
1203906e808SJulia Cartwright 	raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
1211ceacea2SWilliam Breathitt Gray }
1221ceacea2SWilliam Breathitt Gray 
1239d7ae812SWilliam Breathitt Gray static void idio_16_gpio_set_multiple(struct gpio_chip *chip,
1249d7ae812SWilliam Breathitt Gray 	unsigned long *mask, unsigned long *bits)
1259d7ae812SWilliam Breathitt Gray {
1269d7ae812SWilliam Breathitt Gray 	struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
1279d7ae812SWilliam Breathitt Gray 	unsigned long flags;
1289d7ae812SWilliam Breathitt Gray 
1293906e808SJulia Cartwright 	raw_spin_lock_irqsave(&idio16gpio->lock, flags);
1309d7ae812SWilliam Breathitt Gray 
1319d7ae812SWilliam Breathitt Gray 	idio16gpio->out_state &= ~*mask;
1329d7ae812SWilliam Breathitt Gray 	idio16gpio->out_state |= *mask & *bits;
1339d7ae812SWilliam Breathitt Gray 
1349d7ae812SWilliam Breathitt Gray 	if (*mask & 0xFF)
1359d7ae812SWilliam Breathitt Gray 		outb(idio16gpio->out_state, idio16gpio->base);
1369d7ae812SWilliam Breathitt Gray 	if ((*mask >> 8) & 0xFF)
1379d7ae812SWilliam Breathitt Gray 		outb(idio16gpio->out_state >> 8, idio16gpio->base + 4);
1389d7ae812SWilliam Breathitt Gray 
1393906e808SJulia Cartwright 	raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
1409d7ae812SWilliam Breathitt Gray }
1419d7ae812SWilliam Breathitt Gray 
142a1184147SWilliam Breathitt Gray static void idio_16_irq_ack(struct irq_data *data)
143a1184147SWilliam Breathitt Gray {
144a1184147SWilliam Breathitt Gray }
145a1184147SWilliam Breathitt Gray 
146a1184147SWilliam Breathitt Gray static void idio_16_irq_mask(struct irq_data *data)
147a1184147SWilliam Breathitt Gray {
148a1184147SWilliam Breathitt Gray 	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
149d602ae90SLinus Walleij 	struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
150a1184147SWilliam Breathitt Gray 	const unsigned long mask = BIT(irqd_to_hwirq(data));
151a1184147SWilliam Breathitt Gray 	unsigned long flags;
152a1184147SWilliam Breathitt Gray 
153a1184147SWilliam Breathitt Gray 	idio16gpio->irq_mask &= ~mask;
154a1184147SWilliam Breathitt Gray 
155a1184147SWilliam Breathitt Gray 	if (!idio16gpio->irq_mask) {
1563906e808SJulia Cartwright 		raw_spin_lock_irqsave(&idio16gpio->lock, flags);
157a1184147SWilliam Breathitt Gray 
158a1184147SWilliam Breathitt Gray 		outb(0, idio16gpio->base + 2);
159a1184147SWilliam Breathitt Gray 
1603906e808SJulia Cartwright 		raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
161a1184147SWilliam Breathitt Gray 	}
162a1184147SWilliam Breathitt Gray }
163a1184147SWilliam Breathitt Gray 
164a1184147SWilliam Breathitt Gray static void idio_16_irq_unmask(struct irq_data *data)
165a1184147SWilliam Breathitt Gray {
166a1184147SWilliam Breathitt Gray 	struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
167d602ae90SLinus Walleij 	struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
168a1184147SWilliam Breathitt Gray 	const unsigned long mask = BIT(irqd_to_hwirq(data));
169a1184147SWilliam Breathitt Gray 	const unsigned long prev_irq_mask = idio16gpio->irq_mask;
170a1184147SWilliam Breathitt Gray 	unsigned long flags;
171a1184147SWilliam Breathitt Gray 
172a1184147SWilliam Breathitt Gray 	idio16gpio->irq_mask |= mask;
173a1184147SWilliam Breathitt Gray 
174a1184147SWilliam Breathitt Gray 	if (!prev_irq_mask) {
1753906e808SJulia Cartwright 		raw_spin_lock_irqsave(&idio16gpio->lock, flags);
176a1184147SWilliam Breathitt Gray 
177a1184147SWilliam Breathitt Gray 		inb(idio16gpio->base + 2);
178a1184147SWilliam Breathitt Gray 
1793906e808SJulia Cartwright 		raw_spin_unlock_irqrestore(&idio16gpio->lock, flags);
180a1184147SWilliam Breathitt Gray 	}
181a1184147SWilliam Breathitt Gray }
182a1184147SWilliam Breathitt Gray 
183a1184147SWilliam Breathitt Gray static int idio_16_irq_set_type(struct irq_data *data, unsigned flow_type)
184a1184147SWilliam Breathitt Gray {
185a1184147SWilliam Breathitt Gray 	/* The only valid irq types are none and both-edges */
186a1184147SWilliam Breathitt Gray 	if (flow_type != IRQ_TYPE_NONE &&
187a1184147SWilliam Breathitt Gray 		(flow_type & IRQ_TYPE_EDGE_BOTH) != IRQ_TYPE_EDGE_BOTH)
188a1184147SWilliam Breathitt Gray 		return -EINVAL;
189a1184147SWilliam Breathitt Gray 
190a1184147SWilliam Breathitt Gray 	return 0;
191a1184147SWilliam Breathitt Gray }
192a1184147SWilliam Breathitt Gray 
193a1184147SWilliam Breathitt Gray static struct irq_chip idio_16_irqchip = {
194a1184147SWilliam Breathitt Gray 	.name = "104-idio-16",
195a1184147SWilliam Breathitt Gray 	.irq_ack = idio_16_irq_ack,
196a1184147SWilliam Breathitt Gray 	.irq_mask = idio_16_irq_mask,
197a1184147SWilliam Breathitt Gray 	.irq_unmask = idio_16_irq_unmask,
198a1184147SWilliam Breathitt Gray 	.irq_set_type = idio_16_irq_set_type
199a1184147SWilliam Breathitt Gray };
200a1184147SWilliam Breathitt Gray 
201a1184147SWilliam Breathitt Gray static irqreturn_t idio_16_irq_handler(int irq, void *dev_id)
202a1184147SWilliam Breathitt Gray {
203a1184147SWilliam Breathitt Gray 	struct idio_16_gpio *const idio16gpio = dev_id;
204a1184147SWilliam Breathitt Gray 	struct gpio_chip *const chip = &idio16gpio->chip;
205a1184147SWilliam Breathitt Gray 	int gpio;
206a1184147SWilliam Breathitt Gray 
207a1184147SWilliam Breathitt Gray 	for_each_set_bit(gpio, &idio16gpio->irq_mask, chip->ngpio)
208f0fbe7bcSThierry Reding 		generic_handle_irq(irq_find_mapping(chip->irq.domain, gpio));
209a1184147SWilliam Breathitt Gray 
2103906e808SJulia Cartwright 	raw_spin_lock(&idio16gpio->lock);
21112b61c9dSWilliam Breathitt Gray 
21212b61c9dSWilliam Breathitt Gray 	outb(0, idio16gpio->base + 1);
21312b61c9dSWilliam Breathitt Gray 
2143906e808SJulia Cartwright 	raw_spin_unlock(&idio16gpio->lock);
21512b61c9dSWilliam Breathitt Gray 
216a1184147SWilliam Breathitt Gray 	return IRQ_HANDLED;
217a1184147SWilliam Breathitt Gray }
218a1184147SWilliam Breathitt Gray 
219e0af4b5eSWilliam Breathitt Gray #define IDIO_16_NGPIO 32
220e0af4b5eSWilliam Breathitt Gray static const char *idio_16_names[IDIO_16_NGPIO] = {
221e0af4b5eSWilliam Breathitt Gray 	"OUT0", "OUT1", "OUT2", "OUT3", "OUT4", "OUT5", "OUT6", "OUT7",
222e0af4b5eSWilliam Breathitt Gray 	"OUT8", "OUT9", "OUT10", "OUT11", "OUT12", "OUT13", "OUT14", "OUT15",
223e0af4b5eSWilliam Breathitt Gray 	"IIN0", "IIN1", "IIN2", "IIN3", "IIN4", "IIN5", "IIN6", "IIN7",
224e0af4b5eSWilliam Breathitt Gray 	"IIN8", "IIN9", "IIN10", "IIN11", "IIN12", "IIN13", "IIN14", "IIN15"
225e0af4b5eSWilliam Breathitt Gray };
226e0af4b5eSWilliam Breathitt Gray 
22786ea8a95SWilliam Breathitt Gray static int idio_16_probe(struct device *dev, unsigned int id)
2281ceacea2SWilliam Breathitt Gray {
2291ceacea2SWilliam Breathitt Gray 	struct idio_16_gpio *idio16gpio;
2306e0171b4SWilliam Breathitt Gray 	const char *const name = dev_name(dev);
2311ceacea2SWilliam Breathitt Gray 	int err;
2321ceacea2SWilliam Breathitt Gray 
2331ceacea2SWilliam Breathitt Gray 	idio16gpio = devm_kzalloc(dev, sizeof(*idio16gpio), GFP_KERNEL);
2341ceacea2SWilliam Breathitt Gray 	if (!idio16gpio)
2351ceacea2SWilliam Breathitt Gray 		return -ENOMEM;
2361ceacea2SWilliam Breathitt Gray 
23786ea8a95SWilliam Breathitt Gray 	if (!devm_request_region(dev, base[id], IDIO_16_EXTENT, name)) {
238cb32389cSWilliam Breathitt Gray 		dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
23986ea8a95SWilliam Breathitt Gray 			base[id], base[id] + IDIO_16_EXTENT);
240cb32389cSWilliam Breathitt Gray 		return -EBUSY;
2411ceacea2SWilliam Breathitt Gray 	}
2421ceacea2SWilliam Breathitt Gray 
2436e0171b4SWilliam Breathitt Gray 	idio16gpio->chip.label = name;
24458383c78SLinus Walleij 	idio16gpio->chip.parent = dev;
2451ceacea2SWilliam Breathitt Gray 	idio16gpio->chip.owner = THIS_MODULE;
2461ceacea2SWilliam Breathitt Gray 	idio16gpio->chip.base = -1;
247e0af4b5eSWilliam Breathitt Gray 	idio16gpio->chip.ngpio = IDIO_16_NGPIO;
248e0af4b5eSWilliam Breathitt Gray 	idio16gpio->chip.names = idio_16_names;
2491ceacea2SWilliam Breathitt Gray 	idio16gpio->chip.get_direction = idio_16_gpio_get_direction;
2501ceacea2SWilliam Breathitt Gray 	idio16gpio->chip.direction_input = idio_16_gpio_direction_input;
2511ceacea2SWilliam Breathitt Gray 	idio16gpio->chip.direction_output = idio_16_gpio_direction_output;
2521ceacea2SWilliam Breathitt Gray 	idio16gpio->chip.get = idio_16_gpio_get;
25315f59cffSWilliam Breathitt Gray 	idio16gpio->chip.get_multiple = idio_16_gpio_get_multiple;
2541ceacea2SWilliam Breathitt Gray 	idio16gpio->chip.set = idio_16_gpio_set;
2559d7ae812SWilliam Breathitt Gray 	idio16gpio->chip.set_multiple = idio_16_gpio_set_multiple;
25686ea8a95SWilliam Breathitt Gray 	idio16gpio->base = base[id];
2571ceacea2SWilliam Breathitt Gray 	idio16gpio->out_state = 0xFFFF;
2581ceacea2SWilliam Breathitt Gray 
2593906e808SJulia Cartwright 	raw_spin_lock_init(&idio16gpio->lock);
2601ceacea2SWilliam Breathitt Gray 
261837143d3SWilliam Breathitt Gray 	err = devm_gpiochip_add_data(dev, &idio16gpio->chip, idio16gpio);
2621ceacea2SWilliam Breathitt Gray 	if (err) {
2631ceacea2SWilliam Breathitt Gray 		dev_err(dev, "GPIO registering failed (%d)\n", err);
264cb32389cSWilliam Breathitt Gray 		return err;
2651ceacea2SWilliam Breathitt Gray 	}
2661ceacea2SWilliam Breathitt Gray 
267fb50cdfeSWilliam Breathitt Gray 	/* Disable IRQ by default */
26886ea8a95SWilliam Breathitt Gray 	outb(0, base[id] + 2);
26986ea8a95SWilliam Breathitt Gray 	outb(0, base[id] + 1);
270fb50cdfeSWilliam Breathitt Gray 
271a1184147SWilliam Breathitt Gray 	err = gpiochip_irqchip_add(&idio16gpio->chip, &idio_16_irqchip, 0,
272a1184147SWilliam Breathitt Gray 		handle_edge_irq, IRQ_TYPE_NONE);
273a1184147SWilliam Breathitt Gray 	if (err) {
274a1184147SWilliam Breathitt Gray 		dev_err(dev, "Could not add irqchip (%d)\n", err);
2751ceacea2SWilliam Breathitt Gray 		return err;
2761ceacea2SWilliam Breathitt Gray 	}
2771ceacea2SWilliam Breathitt Gray 
278837143d3SWilliam Breathitt Gray 	err = devm_request_irq(dev, irq[id], idio_16_irq_handler, 0, name,
279837143d3SWilliam Breathitt Gray 		idio16gpio);
280837143d3SWilliam Breathitt Gray 	if (err) {
281837143d3SWilliam Breathitt Gray 		dev_err(dev, "IRQ handler registering failed (%d)\n", err);
282837143d3SWilliam Breathitt Gray 		return err;
283837143d3SWilliam Breathitt Gray 	}
2841ceacea2SWilliam Breathitt Gray 
2851ceacea2SWilliam Breathitt Gray 	return 0;
2861ceacea2SWilliam Breathitt Gray }
2871ceacea2SWilliam Breathitt Gray 
28886ea8a95SWilliam Breathitt Gray static struct isa_driver idio_16_driver = {
28986ea8a95SWilliam Breathitt Gray 	.probe = idio_16_probe,
2901ceacea2SWilliam Breathitt Gray 	.driver = {
2911ceacea2SWilliam Breathitt Gray 		.name = "104-idio-16"
2921ceacea2SWilliam Breathitt Gray 	},
2931ceacea2SWilliam Breathitt Gray };
2941ceacea2SWilliam Breathitt Gray 
29586ea8a95SWilliam Breathitt Gray module_isa_driver(idio_16_driver, num_idio_16);
2961ceacea2SWilliam Breathitt Gray 
2971ceacea2SWilliam Breathitt Gray MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
2981ceacea2SWilliam Breathitt Gray MODULE_DESCRIPTION("ACCES 104-IDIO-16 GPIO driver");
29922aeddb5SWilliam Breathitt Gray MODULE_LICENSE("GPL v2");
300