xref: /openbmc/linux/drivers/gpio/gpio-gpio-mm.c (revision 65502a12)
16ea5dcdfSWilliam Breathitt Gray /*
26ea5dcdfSWilliam Breathitt Gray  * GPIO driver for the Diamond Systems GPIO-MM
36ea5dcdfSWilliam Breathitt Gray  * Copyright (C) 2016 William Breathitt Gray
46ea5dcdfSWilliam Breathitt Gray  *
56ea5dcdfSWilliam Breathitt Gray  * This program is free software; you can redistribute it and/or modify
66ea5dcdfSWilliam Breathitt Gray  * it under the terms of the GNU General Public License, version 2, as
76ea5dcdfSWilliam Breathitt Gray  * published by the Free Software Foundation.
86ea5dcdfSWilliam Breathitt Gray  *
96ea5dcdfSWilliam Breathitt Gray  * This program is distributed in the hope that it will be useful, but
106ea5dcdfSWilliam Breathitt Gray  * WITHOUT ANY WARRANTY; without even the implied warranty of
116ea5dcdfSWilliam Breathitt Gray  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
126ea5dcdfSWilliam Breathitt Gray  * General Public License for more details.
136ea5dcdfSWilliam Breathitt Gray  *
146ea5dcdfSWilliam Breathitt Gray  * This driver supports the following Diamond Systems devices: GPIO-MM and
156ea5dcdfSWilliam Breathitt Gray  * GPIO-MM-12.
166ea5dcdfSWilliam Breathitt Gray  */
176ea5dcdfSWilliam Breathitt Gray #include <linux/bitops.h>
186ea5dcdfSWilliam Breathitt Gray #include <linux/device.h>
196ea5dcdfSWilliam Breathitt Gray #include <linux/errno.h>
206ea5dcdfSWilliam Breathitt Gray #include <linux/gpio/driver.h>
216ea5dcdfSWilliam Breathitt Gray #include <linux/io.h>
226ea5dcdfSWilliam Breathitt Gray #include <linux/ioport.h>
236ea5dcdfSWilliam Breathitt Gray #include <linux/isa.h>
246ea5dcdfSWilliam Breathitt Gray #include <linux/kernel.h>
256ea5dcdfSWilliam Breathitt Gray #include <linux/module.h>
266ea5dcdfSWilliam Breathitt Gray #include <linux/moduleparam.h>
276ea5dcdfSWilliam Breathitt Gray #include <linux/spinlock.h>
286ea5dcdfSWilliam Breathitt Gray 
296ea5dcdfSWilliam Breathitt Gray #define GPIOMM_EXTENT 8
306ea5dcdfSWilliam Breathitt Gray #define MAX_NUM_GPIOMM max_num_isa_dev(GPIOMM_EXTENT)
316ea5dcdfSWilliam Breathitt Gray 
326ea5dcdfSWilliam Breathitt Gray static unsigned int base[MAX_NUM_GPIOMM];
336ea5dcdfSWilliam Breathitt Gray static unsigned int num_gpiomm;
346ea5dcdfSWilliam Breathitt Gray module_param_array(base, uint, &num_gpiomm, 0);
356ea5dcdfSWilliam Breathitt Gray MODULE_PARM_DESC(base, "Diamond Systems GPIO-MM base addresses");
366ea5dcdfSWilliam Breathitt Gray 
376ea5dcdfSWilliam Breathitt Gray /**
386ea5dcdfSWilliam Breathitt Gray  * struct gpiomm_gpio - GPIO device private data structure
396ea5dcdfSWilliam Breathitt Gray  * @chip:	instance of the gpio_chip
406ea5dcdfSWilliam Breathitt Gray  * @io_state:	bit I/O state (whether bit is set to input or output)
416ea5dcdfSWilliam Breathitt Gray  * @out_state:	output bits state
426ea5dcdfSWilliam Breathitt Gray  * @control:	Control registers state
436ea5dcdfSWilliam Breathitt Gray  * @lock:	synchronization lock to prevent I/O race conditions
446ea5dcdfSWilliam Breathitt Gray  * @base:	base port address of the GPIO device
456ea5dcdfSWilliam Breathitt Gray  */
466ea5dcdfSWilliam Breathitt Gray struct gpiomm_gpio {
476ea5dcdfSWilliam Breathitt Gray 	struct gpio_chip chip;
486ea5dcdfSWilliam Breathitt Gray 	unsigned char io_state[6];
496ea5dcdfSWilliam Breathitt Gray 	unsigned char out_state[6];
506ea5dcdfSWilliam Breathitt Gray 	unsigned char control[2];
516ea5dcdfSWilliam Breathitt Gray 	spinlock_t lock;
526ea5dcdfSWilliam Breathitt Gray 	unsigned int base;
536ea5dcdfSWilliam Breathitt Gray };
546ea5dcdfSWilliam Breathitt Gray 
556ea5dcdfSWilliam Breathitt Gray static int gpiomm_gpio_get_direction(struct gpio_chip *chip,
566ea5dcdfSWilliam Breathitt Gray 	unsigned int offset)
576ea5dcdfSWilliam Breathitt Gray {
586ea5dcdfSWilliam Breathitt Gray 	struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
596ea5dcdfSWilliam Breathitt Gray 	const unsigned int port = offset / 8;
606ea5dcdfSWilliam Breathitt Gray 	const unsigned int mask = BIT(offset % 8);
616ea5dcdfSWilliam Breathitt Gray 
626ea5dcdfSWilliam Breathitt Gray 	return !!(gpiommgpio->io_state[port] & mask);
636ea5dcdfSWilliam Breathitt Gray }
646ea5dcdfSWilliam Breathitt Gray 
656ea5dcdfSWilliam Breathitt Gray static int gpiomm_gpio_direction_input(struct gpio_chip *chip,
666ea5dcdfSWilliam Breathitt Gray 	unsigned int offset)
676ea5dcdfSWilliam Breathitt Gray {
686ea5dcdfSWilliam Breathitt Gray 	struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
696ea5dcdfSWilliam Breathitt Gray 	const unsigned int io_port = offset / 8;
706ea5dcdfSWilliam Breathitt Gray 	const unsigned int control_port = io_port / 3;
716ea5dcdfSWilliam Breathitt Gray 	const unsigned int control_addr = gpiommgpio->base + 3 + control_port*4;
726ea5dcdfSWilliam Breathitt Gray 	unsigned long flags;
736ea5dcdfSWilliam Breathitt Gray 	unsigned int control;
746ea5dcdfSWilliam Breathitt Gray 
756ea5dcdfSWilliam Breathitt Gray 	spin_lock_irqsave(&gpiommgpio->lock, flags);
766ea5dcdfSWilliam Breathitt Gray 
776ea5dcdfSWilliam Breathitt Gray 	/* Check if configuring Port C */
786ea5dcdfSWilliam Breathitt Gray 	if (io_port == 2 || io_port == 5) {
796ea5dcdfSWilliam Breathitt Gray 		/* Port C can be configured by nibble */
806ea5dcdfSWilliam Breathitt Gray 		if (offset % 8 > 3) {
816ea5dcdfSWilliam Breathitt Gray 			gpiommgpio->io_state[io_port] |= 0xF0;
826ea5dcdfSWilliam Breathitt Gray 			gpiommgpio->control[control_port] |= BIT(3);
836ea5dcdfSWilliam Breathitt Gray 		} else {
846ea5dcdfSWilliam Breathitt Gray 			gpiommgpio->io_state[io_port] |= 0x0F;
856ea5dcdfSWilliam Breathitt Gray 			gpiommgpio->control[control_port] |= BIT(0);
866ea5dcdfSWilliam Breathitt Gray 		}
876ea5dcdfSWilliam Breathitt Gray 	} else {
886ea5dcdfSWilliam Breathitt Gray 		gpiommgpio->io_state[io_port] |= 0xFF;
896ea5dcdfSWilliam Breathitt Gray 		if (io_port == 0 || io_port == 3)
906ea5dcdfSWilliam Breathitt Gray 			gpiommgpio->control[control_port] |= BIT(4);
916ea5dcdfSWilliam Breathitt Gray 		else
926ea5dcdfSWilliam Breathitt Gray 			gpiommgpio->control[control_port] |= BIT(1);
936ea5dcdfSWilliam Breathitt Gray 	}
946ea5dcdfSWilliam Breathitt Gray 
956ea5dcdfSWilliam Breathitt Gray 	control = BIT(7) | gpiommgpio->control[control_port];
966ea5dcdfSWilliam Breathitt Gray 	outb(control, control_addr);
976ea5dcdfSWilliam Breathitt Gray 
986ea5dcdfSWilliam Breathitt Gray 	spin_unlock_irqrestore(&gpiommgpio->lock, flags);
996ea5dcdfSWilliam Breathitt Gray 
1006ea5dcdfSWilliam Breathitt Gray 	return 0;
1016ea5dcdfSWilliam Breathitt Gray }
1026ea5dcdfSWilliam Breathitt Gray 
1036ea5dcdfSWilliam Breathitt Gray static int gpiomm_gpio_direction_output(struct gpio_chip *chip,
1046ea5dcdfSWilliam Breathitt Gray 	unsigned int offset, int value)
1056ea5dcdfSWilliam Breathitt Gray {
1066ea5dcdfSWilliam Breathitt Gray 	struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
1076ea5dcdfSWilliam Breathitt Gray 	const unsigned int io_port = offset / 8;
1086ea5dcdfSWilliam Breathitt Gray 	const unsigned int control_port = io_port / 3;
1096ea5dcdfSWilliam Breathitt Gray 	const unsigned int mask = BIT(offset % 8);
1106ea5dcdfSWilliam Breathitt Gray 	const unsigned int control_addr = gpiommgpio->base + 3 + control_port*4;
1116ea5dcdfSWilliam Breathitt Gray 	const unsigned int out_port = (io_port > 2) ? io_port + 1 : io_port;
1126ea5dcdfSWilliam Breathitt Gray 	unsigned long flags;
1136ea5dcdfSWilliam Breathitt Gray 	unsigned int control;
1146ea5dcdfSWilliam Breathitt Gray 
1156ea5dcdfSWilliam Breathitt Gray 	spin_lock_irqsave(&gpiommgpio->lock, flags);
1166ea5dcdfSWilliam Breathitt Gray 
1176ea5dcdfSWilliam Breathitt Gray 	/* Check if configuring Port C */
1186ea5dcdfSWilliam Breathitt Gray 	if (io_port == 2 || io_port == 5) {
1196ea5dcdfSWilliam Breathitt Gray 		/* Port C can be configured by nibble */
1206ea5dcdfSWilliam Breathitt Gray 		if (offset % 8 > 3) {
1216ea5dcdfSWilliam Breathitt Gray 			gpiommgpio->io_state[io_port] &= 0x0F;
1226ea5dcdfSWilliam Breathitt Gray 			gpiommgpio->control[control_port] &= ~BIT(3);
1236ea5dcdfSWilliam Breathitt Gray 		} else {
1246ea5dcdfSWilliam Breathitt Gray 			gpiommgpio->io_state[io_port] &= 0xF0;
1256ea5dcdfSWilliam Breathitt Gray 			gpiommgpio->control[control_port] &= ~BIT(0);
1266ea5dcdfSWilliam Breathitt Gray 		}
1276ea5dcdfSWilliam Breathitt Gray 	} else {
1286ea5dcdfSWilliam Breathitt Gray 		gpiommgpio->io_state[io_port] &= 0x00;
1296ea5dcdfSWilliam Breathitt Gray 		if (io_port == 0 || io_port == 3)
1306ea5dcdfSWilliam Breathitt Gray 			gpiommgpio->control[control_port] &= ~BIT(4);
1316ea5dcdfSWilliam Breathitt Gray 		else
1326ea5dcdfSWilliam Breathitt Gray 			gpiommgpio->control[control_port] &= ~BIT(1);
1336ea5dcdfSWilliam Breathitt Gray 	}
1346ea5dcdfSWilliam Breathitt Gray 
1356ea5dcdfSWilliam Breathitt Gray 	if (value)
1366ea5dcdfSWilliam Breathitt Gray 		gpiommgpio->out_state[io_port] |= mask;
1376ea5dcdfSWilliam Breathitt Gray 	else
1386ea5dcdfSWilliam Breathitt Gray 		gpiommgpio->out_state[io_port] &= ~mask;
1396ea5dcdfSWilliam Breathitt Gray 
1406ea5dcdfSWilliam Breathitt Gray 	control = BIT(7) | gpiommgpio->control[control_port];
1416ea5dcdfSWilliam Breathitt Gray 	outb(control, control_addr);
1426ea5dcdfSWilliam Breathitt Gray 
1436ea5dcdfSWilliam Breathitt Gray 	outb(gpiommgpio->out_state[io_port], gpiommgpio->base + out_port);
1446ea5dcdfSWilliam Breathitt Gray 
1456ea5dcdfSWilliam Breathitt Gray 	spin_unlock_irqrestore(&gpiommgpio->lock, flags);
1466ea5dcdfSWilliam Breathitt Gray 
1476ea5dcdfSWilliam Breathitt Gray 	return 0;
1486ea5dcdfSWilliam Breathitt Gray }
1496ea5dcdfSWilliam Breathitt Gray 
1506ea5dcdfSWilliam Breathitt Gray static int gpiomm_gpio_get(struct gpio_chip *chip, unsigned int offset)
1516ea5dcdfSWilliam Breathitt Gray {
1526ea5dcdfSWilliam Breathitt Gray 	struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
1536ea5dcdfSWilliam Breathitt Gray 	const unsigned int port = offset / 8;
1546ea5dcdfSWilliam Breathitt Gray 	const unsigned int mask = BIT(offset % 8);
1556ea5dcdfSWilliam Breathitt Gray 	const unsigned int in_port = (port > 2) ? port + 1 : port;
1566ea5dcdfSWilliam Breathitt Gray 	unsigned long flags;
1576ea5dcdfSWilliam Breathitt Gray 	unsigned int port_state;
1586ea5dcdfSWilliam Breathitt Gray 
1596ea5dcdfSWilliam Breathitt Gray 	spin_lock_irqsave(&gpiommgpio->lock, flags);
1606ea5dcdfSWilliam Breathitt Gray 
1616ea5dcdfSWilliam Breathitt Gray 	/* ensure that GPIO is set for input */
1626ea5dcdfSWilliam Breathitt Gray 	if (!(gpiommgpio->io_state[port] & mask)) {
1636ea5dcdfSWilliam Breathitt Gray 		spin_unlock_irqrestore(&gpiommgpio->lock, flags);
1646ea5dcdfSWilliam Breathitt Gray 		return -EINVAL;
1656ea5dcdfSWilliam Breathitt Gray 	}
1666ea5dcdfSWilliam Breathitt Gray 
1676ea5dcdfSWilliam Breathitt Gray 	port_state = inb(gpiommgpio->base + in_port);
1686ea5dcdfSWilliam Breathitt Gray 
1696ea5dcdfSWilliam Breathitt Gray 	spin_unlock_irqrestore(&gpiommgpio->lock, flags);
1706ea5dcdfSWilliam Breathitt Gray 
1716ea5dcdfSWilliam Breathitt Gray 	return !!(port_state & mask);
1726ea5dcdfSWilliam Breathitt Gray }
1736ea5dcdfSWilliam Breathitt Gray 
1746ea5dcdfSWilliam Breathitt Gray static void gpiomm_gpio_set(struct gpio_chip *chip, unsigned int offset,
1756ea5dcdfSWilliam Breathitt Gray 	int value)
1766ea5dcdfSWilliam Breathitt Gray {
1776ea5dcdfSWilliam Breathitt Gray 	struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
1786ea5dcdfSWilliam Breathitt Gray 	const unsigned int port = offset / 8;
1796ea5dcdfSWilliam Breathitt Gray 	const unsigned int mask = BIT(offset % 8);
1806ea5dcdfSWilliam Breathitt Gray 	const unsigned int out_port = (port > 2) ? port + 1 : port;
1816ea5dcdfSWilliam Breathitt Gray 	unsigned long flags;
1826ea5dcdfSWilliam Breathitt Gray 
1836ea5dcdfSWilliam Breathitt Gray 	spin_lock_irqsave(&gpiommgpio->lock, flags);
1846ea5dcdfSWilliam Breathitt Gray 
1856ea5dcdfSWilliam Breathitt Gray 	if (value)
1866ea5dcdfSWilliam Breathitt Gray 		gpiommgpio->out_state[port] |= mask;
1876ea5dcdfSWilliam Breathitt Gray 	else
1886ea5dcdfSWilliam Breathitt Gray 		gpiommgpio->out_state[port] &= ~mask;
1896ea5dcdfSWilliam Breathitt Gray 
1906ea5dcdfSWilliam Breathitt Gray 	outb(gpiommgpio->out_state[port], gpiommgpio->base + out_port);
1916ea5dcdfSWilliam Breathitt Gray 
1926ea5dcdfSWilliam Breathitt Gray 	spin_unlock_irqrestore(&gpiommgpio->lock, flags);
1936ea5dcdfSWilliam Breathitt Gray }
1946ea5dcdfSWilliam Breathitt Gray 
19565502a12SWilliam Breathitt Gray static void gpiomm_gpio_set_multiple(struct gpio_chip *chip,
19665502a12SWilliam Breathitt Gray 	unsigned long *mask, unsigned long *bits)
19765502a12SWilliam Breathitt Gray {
19865502a12SWilliam Breathitt Gray 	struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
19965502a12SWilliam Breathitt Gray 	unsigned int i;
20065502a12SWilliam Breathitt Gray 	const unsigned int gpio_reg_size = 8;
20165502a12SWilliam Breathitt Gray 	unsigned int port;
20265502a12SWilliam Breathitt Gray 	unsigned int out_port;
20365502a12SWilliam Breathitt Gray 	unsigned int bitmask;
20465502a12SWilliam Breathitt Gray 	unsigned long flags;
20565502a12SWilliam Breathitt Gray 
20665502a12SWilliam Breathitt Gray 	/* set bits are evaluated a gpio register size at a time */
20765502a12SWilliam Breathitt Gray 	for (i = 0; i < chip->ngpio; i += gpio_reg_size) {
20865502a12SWilliam Breathitt Gray 		/* no more set bits in this mask word; skip to the next word */
20965502a12SWilliam Breathitt Gray 		if (!mask[BIT_WORD(i)]) {
21065502a12SWilliam Breathitt Gray 			i = (BIT_WORD(i) + 1) * BITS_PER_LONG - gpio_reg_size;
21165502a12SWilliam Breathitt Gray 			continue;
21265502a12SWilliam Breathitt Gray 		}
21365502a12SWilliam Breathitt Gray 
21465502a12SWilliam Breathitt Gray 		port = i / gpio_reg_size;
21565502a12SWilliam Breathitt Gray 		out_port = (port > 2) ? port + 1 : port;
21665502a12SWilliam Breathitt Gray 		bitmask = mask[BIT_WORD(i)] & bits[BIT_WORD(i)];
21765502a12SWilliam Breathitt Gray 
21865502a12SWilliam Breathitt Gray 		spin_lock_irqsave(&gpiommgpio->lock, flags);
21965502a12SWilliam Breathitt Gray 
22065502a12SWilliam Breathitt Gray 		/* update output state data and set device gpio register */
22165502a12SWilliam Breathitt Gray 		gpiommgpio->out_state[port] &= ~mask[BIT_WORD(i)];
22265502a12SWilliam Breathitt Gray 		gpiommgpio->out_state[port] |= bitmask;
22365502a12SWilliam Breathitt Gray 		outb(gpiommgpio->out_state[port], gpiommgpio->base + out_port);
22465502a12SWilliam Breathitt Gray 
22565502a12SWilliam Breathitt Gray 		spin_unlock_irqrestore(&gpiommgpio->lock, flags);
22665502a12SWilliam Breathitt Gray 
22765502a12SWilliam Breathitt Gray 		/* prepare for next gpio register set */
22865502a12SWilliam Breathitt Gray 		mask[BIT_WORD(i)] >>= gpio_reg_size;
22965502a12SWilliam Breathitt Gray 		bits[BIT_WORD(i)] >>= gpio_reg_size;
23065502a12SWilliam Breathitt Gray 	}
23165502a12SWilliam Breathitt Gray }
23265502a12SWilliam Breathitt Gray 
2336ea5dcdfSWilliam Breathitt Gray static int gpiomm_probe(struct device *dev, unsigned int id)
2346ea5dcdfSWilliam Breathitt Gray {
2356ea5dcdfSWilliam Breathitt Gray 	struct gpiomm_gpio *gpiommgpio;
2366ea5dcdfSWilliam Breathitt Gray 	const char *const name = dev_name(dev);
2376ea5dcdfSWilliam Breathitt Gray 	int err;
2386ea5dcdfSWilliam Breathitt Gray 
2396ea5dcdfSWilliam Breathitt Gray 	gpiommgpio = devm_kzalloc(dev, sizeof(*gpiommgpio), GFP_KERNEL);
2406ea5dcdfSWilliam Breathitt Gray 	if (!gpiommgpio)
2416ea5dcdfSWilliam Breathitt Gray 		return -ENOMEM;
2426ea5dcdfSWilliam Breathitt Gray 
2436ea5dcdfSWilliam Breathitt Gray 	if (!devm_request_region(dev, base[id], GPIOMM_EXTENT, name)) {
2446ea5dcdfSWilliam Breathitt Gray 		dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
2456ea5dcdfSWilliam Breathitt Gray 			base[id], base[id] + GPIOMM_EXTENT);
2466ea5dcdfSWilliam Breathitt Gray 		return -EBUSY;
2476ea5dcdfSWilliam Breathitt Gray 	}
2486ea5dcdfSWilliam Breathitt Gray 
2496ea5dcdfSWilliam Breathitt Gray 	gpiommgpio->chip.label = name;
2506ea5dcdfSWilliam Breathitt Gray 	gpiommgpio->chip.parent = dev;
2516ea5dcdfSWilliam Breathitt Gray 	gpiommgpio->chip.owner = THIS_MODULE;
2526ea5dcdfSWilliam Breathitt Gray 	gpiommgpio->chip.base = -1;
2536ea5dcdfSWilliam Breathitt Gray 	gpiommgpio->chip.ngpio = 48;
2546ea5dcdfSWilliam Breathitt Gray 	gpiommgpio->chip.get_direction = gpiomm_gpio_get_direction;
2556ea5dcdfSWilliam Breathitt Gray 	gpiommgpio->chip.direction_input = gpiomm_gpio_direction_input;
2566ea5dcdfSWilliam Breathitt Gray 	gpiommgpio->chip.direction_output = gpiomm_gpio_direction_output;
2576ea5dcdfSWilliam Breathitt Gray 	gpiommgpio->chip.get = gpiomm_gpio_get;
2586ea5dcdfSWilliam Breathitt Gray 	gpiommgpio->chip.set = gpiomm_gpio_set;
25965502a12SWilliam Breathitt Gray 	gpiommgpio->chip.set_multiple = gpiomm_gpio_set_multiple;
2606ea5dcdfSWilliam Breathitt Gray 	gpiommgpio->base = base[id];
2616ea5dcdfSWilliam Breathitt Gray 
2626ea5dcdfSWilliam Breathitt Gray 	spin_lock_init(&gpiommgpio->lock);
2636ea5dcdfSWilliam Breathitt Gray 
2646ea5dcdfSWilliam Breathitt Gray 	dev_set_drvdata(dev, gpiommgpio);
2656ea5dcdfSWilliam Breathitt Gray 
2666ea5dcdfSWilliam Breathitt Gray 	err = gpiochip_add_data(&gpiommgpio->chip, gpiommgpio);
2676ea5dcdfSWilliam Breathitt Gray 	if (err) {
2686ea5dcdfSWilliam Breathitt Gray 		dev_err(dev, "GPIO registering failed (%d)\n", err);
2696ea5dcdfSWilliam Breathitt Gray 		return err;
2706ea5dcdfSWilliam Breathitt Gray 	}
2716ea5dcdfSWilliam Breathitt Gray 
2726ea5dcdfSWilliam Breathitt Gray 	/* initialize all GPIO as output */
2736ea5dcdfSWilliam Breathitt Gray 	outb(0x80, base[id] + 3);
2746ea5dcdfSWilliam Breathitt Gray 	outb(0x00, base[id]);
2756ea5dcdfSWilliam Breathitt Gray 	outb(0x00, base[id] + 1);
2766ea5dcdfSWilliam Breathitt Gray 	outb(0x00, base[id] + 2);
2776ea5dcdfSWilliam Breathitt Gray 	outb(0x80, base[id] + 7);
2786ea5dcdfSWilliam Breathitt Gray 	outb(0x00, base[id] + 4);
2796ea5dcdfSWilliam Breathitt Gray 	outb(0x00, base[id] + 5);
2806ea5dcdfSWilliam Breathitt Gray 	outb(0x00, base[id] + 6);
2816ea5dcdfSWilliam Breathitt Gray 
2826ea5dcdfSWilliam Breathitt Gray 	return 0;
2836ea5dcdfSWilliam Breathitt Gray }
2846ea5dcdfSWilliam Breathitt Gray 
2856ea5dcdfSWilliam Breathitt Gray static int gpiomm_remove(struct device *dev, unsigned int id)
2866ea5dcdfSWilliam Breathitt Gray {
2876ea5dcdfSWilliam Breathitt Gray 	struct gpiomm_gpio *const gpiommgpio = dev_get_drvdata(dev);
2886ea5dcdfSWilliam Breathitt Gray 
2896ea5dcdfSWilliam Breathitt Gray 	gpiochip_remove(&gpiommgpio->chip);
2906ea5dcdfSWilliam Breathitt Gray 
2916ea5dcdfSWilliam Breathitt Gray 	return 0;
2926ea5dcdfSWilliam Breathitt Gray }
2936ea5dcdfSWilliam Breathitt Gray 
2946ea5dcdfSWilliam Breathitt Gray static struct isa_driver gpiomm_driver = {
2956ea5dcdfSWilliam Breathitt Gray 	.probe = gpiomm_probe,
2966ea5dcdfSWilliam Breathitt Gray 	.driver = {
2976ea5dcdfSWilliam Breathitt Gray 		.name = "gpio-mm"
2986ea5dcdfSWilliam Breathitt Gray 	},
2996ea5dcdfSWilliam Breathitt Gray 	.remove = gpiomm_remove
3006ea5dcdfSWilliam Breathitt Gray };
3016ea5dcdfSWilliam Breathitt Gray 
3026ea5dcdfSWilliam Breathitt Gray module_isa_driver(gpiomm_driver, num_gpiomm);
3036ea5dcdfSWilliam Breathitt Gray 
3046ea5dcdfSWilliam Breathitt Gray MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
3056ea5dcdfSWilliam Breathitt Gray MODULE_DESCRIPTION("Diamond Systems GPIO-MM GPIO driver");
3066ea5dcdfSWilliam Breathitt Gray MODULE_LICENSE("GPL v2");
307