diff options
Diffstat (limited to 'target/linux/rdc/files-2.6.30/arch/x86/mach-rdc321x/gpio.c')
-rw-r--r-- | target/linux/rdc/files-2.6.30/arch/x86/mach-rdc321x/gpio.c | 192 |
1 files changed, 104 insertions, 88 deletions
diff --git a/target/linux/rdc/files-2.6.30/arch/x86/mach-rdc321x/gpio.c b/target/linux/rdc/files-2.6.30/arch/x86/mach-rdc321x/gpio.c index c99b3b223..408a4158d 100644 --- a/target/linux/rdc/files-2.6.30/arch/x86/mach-rdc321x/gpio.c +++ b/target/linux/rdc/files-2.6.30/arch/x86/mach-rdc321x/gpio.c @@ -1,8 +1,8 @@ /* - * GPIO support for RDC SoC R3210/R8610 + * RDC321x GPIO driver * - * Copyright (C) 2007, Florian Fainelli <florian@openwrt.org> - * Copyright (C) 2008, Volker Weiss <dev@tintuc.de> + * 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 @@ -19,121 +19,100 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ - - -#include <linux/spinlock.h> -#include <linux/io.h> -#include <linux/types.h> #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 <asm/rdc321x_gpio.h> #include <asm/rdc321x_defs.h> +struct rdc321x_gpio { + spinlock_t lock; + u32 data_reg[2]; +} rdc321x_gpio_dev; -/* spin lock to protect our private copy of GPIO data register plus - the access to PCI conf registers. */ -static DEFINE_SPINLOCK(gpio_lock); +extern int rdc321x_pci_write(int reg, u32 val); +extern int rdc321x_pci_read(int reg, u32 *val); -/* copy of GPIO data registers */ -static u32 gpio_data_reg1; -static u32 gpio_data_reg2; - -static inline void rdc321x_conf_write(unsigned addr, u32 value) -{ - outl((1 << 31) | (7 << 11) | addr, RDC3210_CFGREG_ADDR); - outl(value, RDC3210_CFGREG_DATA); -} - -static inline void rdc321x_conf_or(unsigned addr, u32 value) -{ - outl((1 << 31) | (7 << 11) | addr, RDC3210_CFGREG_ADDR); - value |= inl(RDC3210_CFGREG_DATA); - outl(value, RDC3210_CFGREG_DATA); -} - -static inline u32 rdc321x_conf_read(unsigned addr) +/* read GPIO pin */ +static int rdc_gpio_get_value(struct gpio_chip *chip, unsigned gpio) { - outl((1 << 31) | (7 << 11) | addr, RDC3210_CFGREG_ADDR); + u32 value = 0; + int reg; - return inl(RDC3210_CFGREG_DATA); -} + reg = gpio < 32 ? RDC321X_GPIO_DATA_REG1 : RDC321X_GPIO_DATA_REG2; -/* configure pin as GPIO */ -static void rdc321x_configure_gpio(unsigned gpio) -{ - unsigned long flags; + spin_lock(&rdc321x_gpio_dev.lock); + rdc321x_pci_write(reg, rdc321x_gpio_dev.data_reg[gpio < 32 ? 0 : 1]); + rdc321x_pci_read(reg, &value); + spin_unlock(&rdc321x_gpio_dev.lock); - spin_lock_irqsave(&gpio_lock, flags); - rdc321x_conf_or(gpio < 32 - ? RDC321X_GPIO_CTRL_REG1 : RDC321X_GPIO_CTRL_REG2, - 1 << (gpio & 0x1f)); - spin_unlock_irqrestore(&gpio_lock, flags); + return (1 << (gpio & 0x1f)) & value ? 1 : 0; } -/* read GPIO pin */ -static int rdc_gpio_get_value(struct gpio_chip *chip, unsigned gpio) +static void rdc_gpio_set_value_impl(struct gpio_chip *chip, + unsigned gpio, int value) { - u32 reg; - unsigned long flags; + int reg = (gpio < 32) ? 0 : 1; - spin_lock_irqsave(&gpio_lock, flags); - reg = rdc321x_conf_read(gpio < 32 - ? RDC321X_GPIO_DATA_REG1 : RDC321X_GPIO_DATA_REG2); - spin_unlock_irqrestore(&gpio_lock, flags); + if (value) + rdc321x_gpio_dev.data_reg[reg] |= 1 << (gpio & 0x1f); + else + rdc321x_gpio_dev.data_reg[reg] &= ~(1 << (gpio & 0x1f)); - return (1 << (gpio & 0x1f)) & reg ? 1 : 0; + rdc321x_pci_write(reg ? RDC321X_GPIO_DATA_REG2 : RDC321X_GPIO_DATA_REG1, + rdc321x_gpio_dev.data_reg[reg]); } /* set GPIO pin to value */ static void rdc_gpio_set_value(struct gpio_chip *chip, unsigned gpio, int value) { - unsigned long flags; - u32 reg; - - reg = 1 << (gpio & 0x1f); - if (gpio < 32) { - spin_lock_irqsave(&gpio_lock, flags); - if (value) - gpio_data_reg1 |= reg; - else - gpio_data_reg1 &= ~reg; - rdc321x_conf_write(RDC321X_GPIO_DATA_REG1, gpio_data_reg1); - spin_unlock_irqrestore(&gpio_lock, flags); - } else { - spin_lock_irqsave(&gpio_lock, flags); - if (value) - gpio_data_reg2 |= reg; - else - gpio_data_reg2 &= ~reg; - rdc321x_conf_write(RDC321X_GPIO_DATA_REG2, gpio_data_reg2); - spin_unlock_irqrestore(&gpio_lock, flags); - } + spin_lock(&rdc321x_gpio_dev.lock); + rdc_gpio_set_value_impl(chip, gpio, value); + spin_unlock(&rdc321x_gpio_dev.lock); } -/* configure GPIO pin as input */ -static int rdc_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) +static int rdc_gpio_config(struct gpio_chip *chip, + unsigned gpio, int value) { - rdc321x_configure_gpio(gpio); + int err; + u32 reg; - return 0; + spin_lock(&rdc321x_gpio_dev.lock); + err = rdc321x_pci_read(gpio < 32 ? RDC321X_GPIO_CTRL_REG1 : RDC321X_GPIO_CTRL_REG2, + ®); + if (err) + goto unlock; + + reg |= 1 << (gpio & 0x1f); + + err = rdc321x_pci_write(gpio < 32 ? RDC321X_GPIO_CTRL_REG1 : RDC321X_GPIO_CTRL_REG2, + reg); + if (err) + goto unlock; + + rdc_gpio_set_value_impl(chip, gpio, value); + +unlock: + spin_unlock(&rdc321x_gpio_dev.lock); + + return err; } -/* configure GPIO pin as output and set value */ -static int rdc_gpio_direction_output(struct gpio_chip *chip, - unsigned gpio, int value) +/* configure GPIO pin as input */ +static int rdc_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) { - rdc321x_configure_gpio(gpio); - gpio_set_value(gpio, value); - - return 0; + return rdc_gpio_config(chip, gpio, 1); } static struct gpio_chip rdc321x_gpio_chip = { .label = "rdc321x-gpio", .direction_input = rdc_gpio_direction_input, - .direction_output = rdc_gpio_direction_output, + .direction_output = rdc_gpio_config, .get = rdc_gpio_get_value, .set = rdc_gpio_set_value, .base = 0, @@ -142,17 +121,54 @@ static struct gpio_chip rdc321x_gpio_chip = { /* initially setup the 2 copies of the gpio data registers. This function is called before the platform setup code. */ -static int __init rdc321x_gpio_setup(void) +static int __devinit rdc321x_gpio_probe(struct platform_device *pdev) { + int err; + /* 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 = rdc321x_pci_read(RDC321X_GPIO_DATA_REG1, &rdc321x_gpio_dev.data_reg[0]); + if (err) + return err; + + err = rdc321x_pci_read(RDC321X_GPIO_DATA_REG2, &rdc321x_gpio_dev.data_reg[1]); + if (err) + return err; - gpio_data_reg1 = rdc321x_conf_read(RDC321X_GPIO_DATA_REG1); - gpio_data_reg2 = rdc321x_conf_read(RDC321X_GPIO_DATA_REG2); + spin_lock_init(&rdc321x_gpio_dev.lock); printk(KERN_INFO "rdc321x: registering %d GPIOs\n", rdc321x_gpio_chip.ngpio); return gpiochip_add(&rdc321x_gpio_chip); } -arch_initcall(rdc321x_gpio_setup); +static int __devexit rdc321x_gpio_remove(struct platform_device *pdev) +{ + gpiochip_remove(&rdc321x_gpio_chip); + return 0; +} + +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"); |