diff options
Diffstat (limited to 'target/linux/rdc/patches-2.6.30/002-rdc321x_gpio.patch')
-rw-r--r-- | target/linux/rdc/patches-2.6.30/002-rdc321x_gpio.patch | 288 |
1 files changed, 288 insertions, 0 deletions
diff --git a/target/linux/rdc/patches-2.6.30/002-rdc321x_gpio.patch b/target/linux/rdc/patches-2.6.30/002-rdc321x_gpio.patch new file mode 100644 index 0000000000..f2b96b32c2 --- /dev/null +++ b/target/linux/rdc/patches-2.6.30/002-rdc321x_gpio.patch @@ -0,0 +1,288 @@ +This patch adds a new GPIO driver for the RDC321x SoC GPIO controller. + +Signed-off-by: Florian Fainelli <florian@openwrt.org> +--- +Changes from v2: +- initialize spinlock earlier +- do not declare and assign gpch variables on the same line +- use the pci_dev pointer passed as platform data +- replaced rdc321x_pci_{read,write} + +Index: linux-2.6.30.10/drivers/gpio/Kconfig +=================================================================== +--- linux-2.6.30.10.orig/drivers/gpio/Kconfig 2009-12-04 07:00:07.000000000 +0100 ++++ linux-2.6.30.10/drivers/gpio/Kconfig 2010-04-28 10:16:52.000000000 +0200 +@@ -161,6 +161,14 @@ + + If unsure, say N. + ++config GPIO_RDC321X ++ tristate "RDC R-321x GPIO support" ++ depends on PCI && GPIOLIB ++ select MFD_RDC321X ++ help ++ Support for the RDC R321x SoC GPIOs over southbridge ++ PCI configuration space. ++ + comment "SPI GPIO expanders:" + + config GPIO_MAX7301 +Index: linux-2.6.30.10/drivers/gpio/Makefile +=================================================================== +--- linux-2.6.30.10.orig/drivers/gpio/Makefile 2009-12-04 07:00:07.000000000 +0100 ++++ linux-2.6.30.10/drivers/gpio/Makefile 2010-04-28 10:17:13.000000000 +0200 +@@ -12,3 +12,4 @@ + obj-$(CONFIG_GPIO_TWL4030) += twl4030-gpio.o + obj-$(CONFIG_GPIO_XILINX) += xilinx_gpio.o + obj-$(CONFIG_GPIO_BT8XX) += bt8xxgpio.o ++obj-$(CONFIG_GPIO_RDC321X) += rdc321x-gpio.o +Index: linux-2.6.30.10/drivers/gpio/rdc321x-gpio.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.30.10/drivers/gpio/rdc321x-gpio.c 2010-04-28 10:16:52.000000000 +0200 +@@ -0,0 +1,245 @@ ++/* ++ * RDC321x GPIO driver ++ * ++ * Copyright (C) 2008, Volker Weiss <dev@tintuc.de> ++ * Copyright (C) 2007-2010 Florian Fainelli <florian@openwrt.org> ++ * ++ * 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. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ */ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/spinlock.h> ++#include <linux/platform_device.h> ++#include <linux/pci.h> ++#include <linux/gpio.h> ++#include <linux/mfd/rdc321x.h> ++ ++struct rdc321x_gpio { ++ spinlock_t lock; ++ struct pci_dev *sb_pdev; ++ u32 data_reg[2]; ++ int reg1_ctrl_base; ++ int reg1_data_base; ++ int reg2_ctrl_base; ++ int reg2_data_base; ++ struct gpio_chip chip; ++}; ++ ++/* read GPIO pin */ ++static int rdc_gpio_get_value(struct gpio_chip *chip, unsigned gpio) ++{ ++ struct rdc321x_gpio *gpch; ++ u32 value = 0; ++ int reg; ++ ++ gpch = container_of(chip, struct rdc321x_gpio, chip); ++ reg = gpio < 32 ? gpch->reg1_data_base : gpch->reg2_data_base; ++ ++ spin_lock(&gpch->lock); ++ pci_write_config_dword(gpch->sb_pdev, reg, ++ gpch->data_reg[gpio < 32 ? 0 : 1]); ++ pci_read_config_dword(gpch->sb_pdev, reg, &value); ++ spin_unlock(&gpch->lock); ++ ++ return (1 << (gpio & 0x1f)) & value ? 1 : 0; ++} ++ ++static void rdc_gpio_set_value_impl(struct gpio_chip *chip, ++ unsigned gpio, int value) ++{ ++ struct rdc321x_gpio *gpch; ++ int reg = (gpio < 32) ? 0 : 1; ++ ++ gpch = container_of(chip, struct rdc321x_gpio, chip); ++ ++ if (value) ++ gpch->data_reg[reg] |= 1 << (gpio & 0x1f); ++ else ++ gpch->data_reg[reg] &= ~(1 << (gpio & 0x1f)); ++ ++ pci_write_config_dword(gpch->sb_pdev, ++ reg ? gpch->reg1_data_base : gpch->reg2_data_base, ++ gpch->data_reg[reg]); ++} ++ ++/* set GPIO pin to value */ ++static void rdc_gpio_set_value(struct gpio_chip *chip, ++ unsigned gpio, int value) ++{ ++ struct rdc321x_gpio *gpch; ++ ++ gpch = container_of(chip, struct rdc321x_gpio, chip); ++ spin_lock(&gpch->lock); ++ rdc_gpio_set_value_impl(chip, gpio, value); ++ spin_unlock(&gpch->lock); ++} ++ ++static int rdc_gpio_config(struct gpio_chip *chip, ++ unsigned gpio, int value) ++{ ++ struct rdc321x_gpio *gpch; ++ int err; ++ u32 reg; ++ ++ gpch = container_of(chip, struct rdc321x_gpio, chip); ++ ++ spin_lock(&gpch->lock); ++ err = pci_read_config_dword(gpch->sb_pdev, gpio < 32 ? ++ gpch->reg1_ctrl_base : gpch->reg2_ctrl_base, ®); ++ if (err) ++ goto unlock; ++ ++ reg |= 1 << (gpio & 0x1f); ++ ++ err = pci_write_config_dword(gpch->sb_pdev, gpio < 32 ? ++ gpch->reg1_ctrl_base : gpch->reg2_ctrl_base, reg); ++ if (err) ++ goto unlock; ++ ++ rdc_gpio_set_value_impl(chip, gpio, value); ++ ++unlock: ++ spin_unlock(&gpch->lock); ++ ++ return err; ++} ++ ++/* configure GPIO pin as input */ ++static int rdc_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) ++{ ++ return rdc_gpio_config(chip, gpio, 1); ++} ++ ++/* ++ * Cache the initial value of both GPIO data registers ++ */ ++static int __devinit rdc321x_gpio_probe(struct platform_device *pdev) ++{ ++ int err; ++ struct resource *r; ++ struct rdc321x_gpio *rdc321x_gpio_dev; ++ struct rdc321x_gpio_pdata *pdata; ++ ++ pdata = pdev->dev.platform_data; ++ if (!pdata) { ++ dev_err(&pdev->dev, "no platform data supplied\n"); ++ return -ENODEV; ++ } ++ ++ rdc321x_gpio_dev = kzalloc(sizeof(struct rdc321x_gpio), GFP_KERNEL); ++ if (!rdc321x_gpio_dev) { ++ dev_err(&pdev->dev, "failed to allocate private data\n"); ++ return -ENOMEM; ++ } ++ ++ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gpio-reg1"); ++ if (!r) { ++ dev_err(&pdev->dev, "failed to get gpio-reg1 resource\n"); ++ err = -ENODEV; ++ goto out_free; ++ } ++ ++ spin_lock_init(&rdc321x_gpio_dev->lock); ++ rdc321x_gpio_dev->sb_pdev = pdata->sb_pdev; ++ rdc321x_gpio_dev->reg1_ctrl_base = r->start; ++ rdc321x_gpio_dev->reg1_data_base = r->start + 0x4; ++ ++ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gpio-reg2"); ++ if (!r) { ++ dev_err(&pdev->dev, "failed to get gpio-reg2 resource\n"); ++ err = -ENODEV; ++ goto out_free; ++ } ++ ++ rdc321x_gpio_dev->reg2_ctrl_base = r->start; ++ rdc321x_gpio_dev->reg2_data_base = r->start + 0x4; ++ ++ rdc321x_gpio_dev->chip.label = "rdc321x-gpio"; ++ rdc321x_gpio_dev->chip.direction_input = rdc_gpio_direction_input; ++ rdc321x_gpio_dev->chip.direction_output = rdc_gpio_config; ++ rdc321x_gpio_dev->chip.get = rdc_gpio_get_value; ++ rdc321x_gpio_dev->chip.set = rdc_gpio_set_value; ++ rdc321x_gpio_dev->chip.base = 0; ++ rdc321x_gpio_dev->chip.ngpio = pdata->max_gpios; ++ ++ platform_set_drvdata(pdev, rdc321x_gpio_dev); ++ ++ /* This might not be, what others (BIOS, bootloader, etc.) ++ wrote to these registers before, but it's a good guess. Still ++ better than just using 0xffffffff. */ ++ err = pci_read_config_dword(rdc321x_gpio_dev->sb_pdev, ++ rdc321x_gpio_dev->reg1_data_base, ++ &rdc321x_gpio_dev->data_reg[0]); ++ if (err) ++ goto out_drvdata; ++ ++ err = pci_read_config_dword(rdc321x_gpio_dev->sb_pdev, ++ rdc321x_gpio_dev->reg2_data_base, ++ &rdc321x_gpio_dev->data_reg[1]); ++ if (err) ++ goto out_drvdata; ++ ++ dev_info(&pdev->dev, "registering %d GPIOs\n", ++ rdc321x_gpio_dev->chip.ngpio); ++ return gpiochip_add(&rdc321x_gpio_dev->chip); ++ ++out_drvdata: ++ platform_set_drvdata(pdev, NULL); ++out_free: ++ kfree(rdc321x_gpio_dev); ++ return err; ++} ++ ++static int __devexit rdc321x_gpio_remove(struct platform_device *pdev) ++{ ++ int ret; ++ struct rdc321x_gpio *rdc321x_gpio_dev = platform_get_drvdata(pdev); ++ ++ ret = gpiochip_remove(&rdc321x_gpio_dev->chip); ++ if (ret) ++ dev_err(&pdev->dev, "failed to unregister chip\n"); ++ ++ kfree(rdc321x_gpio_dev); ++ platform_set_drvdata(pdev, NULL); ++ ++ return ret; ++} ++ ++static struct platform_driver rdc321x_gpio_driver = { ++ .driver.name = "rdc321x-gpio", ++ .driver.owner = THIS_MODULE, ++ .probe = rdc321x_gpio_probe, ++ .remove = __devexit_p(rdc321x_gpio_remove), ++}; ++ ++static int __init rdc321x_gpio_init(void) ++{ ++ return platform_driver_register(&rdc321x_gpio_driver); ++} ++ ++static void __exit rdc321x_gpio_exit(void) ++{ ++ platform_driver_unregister(&rdc321x_gpio_driver); ++} ++ ++module_init(rdc321x_gpio_init); ++module_exit(rdc321x_gpio_exit); ++ ++MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>"); ++MODULE_DESCRIPTION("RDC321x GPIO driver"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:rdc321x-gpio"); |