diff options
Diffstat (limited to 'drivers/gpio/gpio-ge.c')
| -rw-r--r-- | drivers/gpio/gpio-ge.c | 199 | 
1 files changed, 199 insertions, 0 deletions
| diff --git a/drivers/gpio/gpio-ge.c b/drivers/gpio/gpio-ge.c new file mode 100644 index 000000000000..7b95a4a8318c --- /dev/null +++ b/drivers/gpio/gpio-ge.c @@ -0,0 +1,199 @@ +/* + * Driver for GE FPGA based GPIO + * + * Author: Martyn Welch <martyn.welch@ge.com> + * + * 2008 (c) GE Intelligent Platforms Embedded Systems, Inc. + * + * This file is licensed under the terms of the GNU General Public License + * version 2.  This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +/* TODO + * + * Configuration of output modes (totem-pole/open-drain) + * Interrupt configuration - interrupts are always generated the FPGA relies on + * the I/O interrupt controllers mask to stop them propergating + */ + +#include <linux/kernel.h> +#include <linux/compiler.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_platform.h> +#include <linux/of_gpio.h> +#include <linux/gpio.h> +#include <linux/slab.h> +#include <linux/module.h> + +#define GEF_GPIO_DIRECT		0x00 +#define GEF_GPIO_IN		0x04 +#define GEF_GPIO_OUT		0x08 +#define GEF_GPIO_TRIG		0x0C +#define GEF_GPIO_POLAR_A	0x10 +#define GEF_GPIO_POLAR_B	0x14 +#define GEF_GPIO_INT_STAT	0x18 +#define GEF_GPIO_OVERRUN	0x1C +#define GEF_GPIO_MODE		0x20 + +static void _gef_gpio_set(void __iomem *reg, unsigned int offset, int value) +{ +	unsigned int data; + +	data = ioread32be(reg); +	/* value: 0=low; 1=high */ +	if (value & 0x1) +		data = data | (0x1 << offset); +	else +		data = data & ~(0x1 << offset); + +	iowrite32be(data, reg); +} + + +static int gef_gpio_dir_in(struct gpio_chip *chip, unsigned offset) +{ +	unsigned int data; +	struct of_mm_gpio_chip *mmchip = to_of_mm_gpio_chip(chip); + +	data = ioread32be(mmchip->regs + GEF_GPIO_DIRECT); +	data = data | (0x1 << offset); +	iowrite32be(data, mmchip->regs + GEF_GPIO_DIRECT); + +	return 0; +} + +static int gef_gpio_dir_out(struct gpio_chip *chip, unsigned offset, int value) +{ +	unsigned int data; +	struct of_mm_gpio_chip *mmchip = to_of_mm_gpio_chip(chip); + +	/* Set direction before switching to input */ +	_gef_gpio_set(mmchip->regs + GEF_GPIO_OUT, offset, value); + +	data = ioread32be(mmchip->regs + GEF_GPIO_DIRECT); +	data = data & ~(0x1 << offset); +	iowrite32be(data, mmchip->regs + GEF_GPIO_DIRECT); + +	return 0; +} + +static int gef_gpio_get(struct gpio_chip *chip, unsigned offset) +{ +	unsigned int data; +	int state = 0; +	struct of_mm_gpio_chip *mmchip = to_of_mm_gpio_chip(chip); + +	data = ioread32be(mmchip->regs + GEF_GPIO_IN); +	state = (int)((data >> offset) & 0x1); + +	return state; +} + +static void gef_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ +	struct of_mm_gpio_chip *mmchip = to_of_mm_gpio_chip(chip); + +	_gef_gpio_set(mmchip->regs + GEF_GPIO_OUT, offset, value); +} + +static int __init gef_gpio_init(void) +{ +	struct device_node *np; +	int retval; +	struct of_mm_gpio_chip *gef_gpio_chip; + +	for_each_compatible_node(np, NULL, "gef,sbc610-gpio") { + +		pr_debug("%s: Initialising GEF GPIO\n", np->full_name); + +		/* Allocate chip structure */ +		gef_gpio_chip = kzalloc(sizeof(*gef_gpio_chip), GFP_KERNEL); +		if (!gef_gpio_chip) { +			pr_err("%s: Unable to allocate structure\n", +				np->full_name); +			continue; +		} + +		/* Setup pointers to chip functions */ +		gef_gpio_chip->gc.of_gpio_n_cells = 2; +		gef_gpio_chip->gc.ngpio = 19; +		gef_gpio_chip->gc.direction_input = gef_gpio_dir_in; +		gef_gpio_chip->gc.direction_output = gef_gpio_dir_out; +		gef_gpio_chip->gc.get = gef_gpio_get; +		gef_gpio_chip->gc.set = gef_gpio_set; + +		/* This function adds a memory mapped GPIO chip */ +		retval = of_mm_gpiochip_add(np, gef_gpio_chip); +		if (retval) { +			kfree(gef_gpio_chip); +			pr_err("%s: Unable to add GPIO\n", np->full_name); +		} +	} + +	for_each_compatible_node(np, NULL, "gef,sbc310-gpio") { + +		pr_debug("%s: Initialising GEF GPIO\n", np->full_name); + +		/* Allocate chip structure */ +		gef_gpio_chip = kzalloc(sizeof(*gef_gpio_chip), GFP_KERNEL); +		if (!gef_gpio_chip) { +			pr_err("%s: Unable to allocate structure\n", +				np->full_name); +			continue; +		} + +		/* Setup pointers to chip functions */ +		gef_gpio_chip->gc.of_gpio_n_cells = 2; +		gef_gpio_chip->gc.ngpio = 6; +		gef_gpio_chip->gc.direction_input = gef_gpio_dir_in; +		gef_gpio_chip->gc.direction_output = gef_gpio_dir_out; +		gef_gpio_chip->gc.get = gef_gpio_get; +		gef_gpio_chip->gc.set = gef_gpio_set; + +		/* This function adds a memory mapped GPIO chip */ +		retval = of_mm_gpiochip_add(np, gef_gpio_chip); +		if (retval) { +			kfree(gef_gpio_chip); +			pr_err("%s: Unable to add GPIO\n", np->full_name); +		} +	} + +	for_each_compatible_node(np, NULL, "ge,imp3a-gpio") { + +		pr_debug("%s: Initialising GE GPIO\n", np->full_name); + +		/* Allocate chip structure */ +		gef_gpio_chip = kzalloc(sizeof(*gef_gpio_chip), GFP_KERNEL); +		if (!gef_gpio_chip) { +			pr_err("%s: Unable to allocate structure\n", +				np->full_name); +			continue; +		} + +		/* Setup pointers to chip functions */ +		gef_gpio_chip->gc.of_gpio_n_cells = 2; +		gef_gpio_chip->gc.ngpio = 16; +		gef_gpio_chip->gc.direction_input = gef_gpio_dir_in; +		gef_gpio_chip->gc.direction_output = gef_gpio_dir_out; +		gef_gpio_chip->gc.get = gef_gpio_get; +		gef_gpio_chip->gc.set = gef_gpio_set; + +		/* This function adds a memory mapped GPIO chip */ +		retval = of_mm_gpiochip_add(np, gef_gpio_chip); +		if (retval) { +			kfree(gef_gpio_chip); +			pr_err("%s: Unable to add GPIO\n", np->full_name); +		} +	} + +	return 0; +}; +arch_initcall(gef_gpio_init); + +MODULE_DESCRIPTION("GE I/O FPGA GPIO driver"); +MODULE_AUTHOR("Martyn Welch <martyn.welch@ge.com"); +MODULE_LICENSE("GPL"); | 
