diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 73067efd484530d4021a220fecce329b4752b3c0..f456cf4ae3ca4634dc3976fddf4eeed43e3adb3a 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -364,6 +364,7 @@ config ARCH_CNS3XXX config ARCH_CLPS711X bool "Cirrus Logic CLPS711x/EP721x/EP731x-based" + select ARCH_REQUIRE_GPIOLIB select ARCH_USES_GETTIMEOFFSET select CLKDEV_LOOKUP select COMMON_CLK diff --git a/arch/arm/mach-clps711x/include/mach/gpio.h b/arch/arm/mach-clps711x/include/mach/gpio.h new file mode 100644 index 0000000000000000000000000000000000000000..8ac6889fabcdc2543c2cdf714618f404407c5fcd --- /dev/null +++ b/arch/arm/mach-clps711x/include/mach/gpio.h @@ -0,0 +1,13 @@ +/* + * This file contains the CLPS711X GPIO definitions. + * + * Copyright (C) 2012 Alexander Shiyan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +/* Simple helper for convert port & pin to GPIO number */ +#define CLPS711X_GPIO(port, bit) ((port) * 8 + (bit)) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 150eeb705fbcf65c00c4381d2761f35d05ed8dc6..9e3fb3438718ee2ce49fbd14904d6e9ed3012e3f 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -102,6 +102,10 @@ config GPIO_MAX730X comment "Memory mapped GPIO drivers:" +config GPIO_CLPS711X + def_bool y + depends on ARCH_CLPS711X + config GPIO_GENERIC_PLATFORM tristate "Generic memory-mapped GPIO controller support (MMIO platform device)" select GPIO_GENERIC diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index e6f8e379a2ec899f6d08e60a3d761053b5cf2945..1c1b63fcaeb3e25317e401ccd50ef09325bd0f90 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_GPIO_ADP5588) += gpio-adp5588.o obj-$(CONFIG_GPIO_AMD8111) += gpio-amd8111.o obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o +obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o obj-$(CONFIG_GPIO_CS5535) += gpio-cs5535.o obj-$(CONFIG_GPIO_DA9052) += gpio-da9052.o obj-$(CONFIG_GPIO_DA9055) += gpio-da9055.o diff --git a/drivers/gpio/gpio-clps711x.c b/drivers/gpio/gpio-clps711x.c new file mode 100644 index 0000000000000000000000000000000000000000..ea21822b3de97d5e7ab8bd09960e0ac18b77a4fe --- /dev/null +++ b/drivers/gpio/gpio-clps711x.c @@ -0,0 +1,163 @@ +/* + * CLPS711X GPIO driver + * + * Copyright (C) 2012 Alexander Shiyan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#define CLPS711X_GPIO_PORTS 5 +#define CLPS711X_GPIO_NAME "gpio-clps711x" + +struct clps711x_gpio { + struct gpio_chip chip[CLPS711X_GPIO_PORTS]; + spinlock_t lock; +}; + +static void __iomem *clps711x_ports[] = { + CLPS711X_VIRT_BASE + PADR, + CLPS711X_VIRT_BASE + PBDR, + CLPS711X_VIRT_BASE + PCDR, + CLPS711X_VIRT_BASE + PDDR, + CLPS711X_VIRT_BASE + PEDR, +}; + +static void __iomem *clps711x_pdirs[] = { + CLPS711X_VIRT_BASE + PADDR, + CLPS711X_VIRT_BASE + PBDDR, + CLPS711X_VIRT_BASE + PCDDR, + CLPS711X_VIRT_BASE + PDDDR, + CLPS711X_VIRT_BASE + PEDDR, +}; + +#define clps711x_port(x) clps711x_ports[x->base / 8] +#define clps711x_pdir(x) clps711x_pdirs[x->base / 8] + +static int gpio_clps711x_get(struct gpio_chip *chip, unsigned offset) +{ + return !!readb(clps711x_port(chip)) & (1 << offset); +} + +static void gpio_clps711x_set(struct gpio_chip *chip, unsigned offset, + int value) +{ + int tmp; + unsigned long flags; + struct clps711x_gpio *gpio = dev_get_drvdata(chip->dev); + + spin_lock_irqsave(&gpio->lock, flags); + tmp = readb(clps711x_port(chip)) & ~(1 << offset); + if (value) + tmp |= 1 << offset; + writeb(tmp, clps711x_port(chip)); + spin_unlock_irqrestore(&gpio->lock, flags); +} + +static int gpio_clps711x_direction_in(struct gpio_chip *chip, unsigned offset) +{ + int tmp; + unsigned long flags; + struct clps711x_gpio *gpio = dev_get_drvdata(chip->dev); + + spin_lock_irqsave(&gpio->lock, flags); + tmp = readb(clps711x_pdir(chip)) & ~(1 << offset); + writeb(tmp, clps711x_pdir(chip)); + spin_unlock_irqrestore(&gpio->lock, flags); + + return 0; +} + +static int gpio_clps711x_direction_out(struct gpio_chip *chip, unsigned offset, + int value) +{ + int tmp; + unsigned long flags; + struct clps711x_gpio *gpio = dev_get_drvdata(chip->dev); + + spin_lock_irqsave(&gpio->lock, flags); + tmp = readb(clps711x_pdir(chip)) | (1 << offset); + writeb(tmp, clps711x_pdir(chip)); + tmp = readb(clps711x_port(chip)) & ~(1 << offset); + if (value) + tmp |= 1 << offset; + writeb(tmp, clps711x_port(chip)); + spin_unlock_irqrestore(&gpio->lock, flags); + + return 0; +} + +struct clps711x_gpio_port { + char *name; + int nr; +}; + +static const struct clps711x_gpio_port clps711x_gpio_ports[] __initconst = { + { "PORTA", 8, }, + { "PORTB", 8, }, + { "PORTC", 8, }, + { "PORTD", 8, }, + { "PORTE", 3, }, +}; + +static int __init gpio_clps711x_init(void) +{ + int i; + struct platform_device *pdev; + struct clps711x_gpio *gpio; + + pdev = platform_device_alloc(CLPS711X_GPIO_NAME, 0); + if (!pdev) { + pr_err("Cannot create platform device: %s\n", + CLPS711X_GPIO_NAME); + return -ENOMEM; + } + + platform_device_add(pdev); + + gpio = devm_kzalloc(&pdev->dev, sizeof(struct clps711x_gpio), + GFP_KERNEL); + if (!gpio) { + dev_err(&pdev->dev, "GPIO allocating memory error\n"); + platform_device_del(pdev); + platform_device_put(pdev); + return -ENOMEM; + } + + platform_set_drvdata(pdev, gpio); + + spin_lock_init(&gpio->lock); + + for (i = 0; i < CLPS711X_GPIO_PORTS; i++) { + gpio->chip[i].owner = THIS_MODULE; + gpio->chip[i].dev = &pdev->dev; + gpio->chip[i].label = clps711x_gpio_ports[i].name; + gpio->chip[i].base = i * 8; + gpio->chip[i].ngpio = clps711x_gpio_ports[i].nr; + gpio->chip[i].direction_input = gpio_clps711x_direction_in; + gpio->chip[i].get = gpio_clps711x_get; + gpio->chip[i].direction_output = gpio_clps711x_direction_out; + gpio->chip[i].set = gpio_clps711x_set; + WARN_ON(gpiochip_add(&gpio->chip[i])); + } + + dev_info(&pdev->dev, "GPIO driver initialized\n"); + + return 0; +} +arch_initcall(gpio_clps711x_init); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Alexander Shiyan "); +MODULE_DESCRIPTION("CLPS711X GPIO driver");