This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / arch / arm / mach-pnx4008 / gpio.c
diff --git a/arch/arm/mach-pnx4008/gpio.c b/arch/arm/mach-pnx4008/gpio.c
new file mode 100644 (file)
index 0000000..1ab84ce
--- /dev/null
@@ -0,0 +1,329 @@
+/*
+ * arch/arm/mach-pnx4008/gpio.c
+ *
+ * PNX4008 GPIO driver
+ *
+ * Author: Dmitry Chigirev <source@mvista.com>
+ *
+ * Based on reference code by Iwo Mergler and Z.Tabaaloute from Philips:
+ * Copyright (c) 2005 Koninklijke Philips Electronics N.V.
+ *
+ * 2005 (c) MontaVista Software, 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.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <asm/semaphore.h>
+#include <asm/io.h>
+#include <asm/arch/platform.h>
+#include <asm/arch/gpio.h>
+
+/* register definitions */
+#define PIO_VA_BASE    IO_ADDRESS(PNX4008_PIO_BASE)
+
+#define PIO_INP_STATE  (0x00U)
+#define PIO_OUTP_SET   (0x04U)
+#define PIO_OUTP_CLR   (0x08U)
+#define PIO_OUTP_STATE (0x0CU)
+#define PIO_DRV_SET    (0x10U)
+#define PIO_DRV_CLR    (0x14U)
+#define PIO_DRV_STATE  (0x18U)
+#define PIO_SDINP_STATE        (0x1CU)
+#define PIO_SDOUTP_SET (0x20U)
+#define PIO_SDOUTP_CLR (0x24U)
+#define PIO_MUX_SET    (0x28U)
+#define PIO_MUX_CLR    (0x2CU)
+#define PIO_MUX_STATE  (0x30U)
+
+static inline void gpio_lock(void)
+{
+       local_irq_disable();
+}
+
+static inline void gpio_unlock(void)
+{
+       local_irq_enable();
+}
+
+/* Inline functions */
+static inline int gpio_read_bit(u32 reg, int gpio)
+{
+       u32 bit, val;
+       int ret = -EFAULT;
+
+       if (gpio < 0)
+               goto out;
+
+       bit = GPIO_BIT(gpio);
+       if (bit) {
+               val = __raw_readl(PIO_VA_BASE + reg);
+               ret = (val & bit) ? 1 : 0;
+       }
+out:
+       return ret;
+}
+
+static inline int gpio_set_bit(u32 reg, int gpio)
+{
+       u32 bit, val;
+       int ret = -EFAULT;
+
+       if (gpio < 0)
+               goto out;
+
+       bit = GPIO_BIT(gpio);
+       if (bit) {
+               val = __raw_readl(PIO_VA_BASE + reg);
+               val |= bit;
+               __raw_writel(val, PIO_VA_BASE + reg);
+               ret = 0;
+       }
+out:
+       return ret;
+}
+
+/* Very simple access control, bitmap for allocated/free */
+static unsigned long access_map[4];
+#define INP_INDEX      0
+#define OUTP_INDEX     1
+#define GPIO_INDEX     2
+#define MUX_INDEX      3
+
+/*GPIO to Input Mapping */
+static short gpio_to_inp_map[32] = {
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, 10, 11, 12, 13, 14, 24, -1
+};
+
+/*GPIO to Mux Mapping */
+static short gpio_to_mux_map[32] = {
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, 0, 1, 4, 5, -1
+};
+
+/*Output to Mux Mapping */
+static short outp_to_mux_map[32] = {
+       -1, -1, -1, 6, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1,
+       -1, -1, -1, -1, -1, 2, -1, -1,
+       -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+int pnx4008_gpio_register_pin(unsigned short pin)
+{
+       unsigned long bit = GPIO_BIT(pin);
+       int ret = -EBUSY;       /* Already in use */
+
+       gpio_lock();
+
+       if (GPIO_ISBID(pin)) {
+               if (access_map[GPIO_INDEX] & bit)
+                       goto out;
+               access_map[GPIO_INDEX] |= bit;
+
+       } else if (GPIO_ISRAM(pin)) {
+               if (access_map[GPIO_INDEX] & bit)
+                       goto out;
+               access_map[GPIO_INDEX] |= bit;
+
+       } else if (GPIO_ISMUX(pin)) {
+               if (access_map[MUX_INDEX] & bit)
+                       goto out;
+               access_map[MUX_INDEX] |= bit;
+
+       } else if (GPIO_ISOUT(pin)) {
+               if (access_map[OUTP_INDEX] & bit)
+                       goto out;
+               access_map[OUTP_INDEX] |= bit;
+
+       } else if (GPIO_ISIN(pin)) {
+               if (access_map[INP_INDEX] & bit)
+                       goto out;
+               access_map[INP_INDEX] |= bit;
+       } else
+               goto out;
+       ret = 0;
+
+out:
+       gpio_unlock();
+       return ret;
+}
+
+EXPORT_SYMBOL(pnx4008_gpio_register_pin);
+
+int pnx4008_gpio_unregister_pin(unsigned short pin)
+{
+       unsigned long bit = GPIO_BIT(pin);
+       int ret = -EFAULT;      /* Not registered */
+
+       gpio_lock();
+
+       if (GPIO_ISBID(pin)) {
+               if (~access_map[GPIO_INDEX] & bit)
+                       goto out;
+               access_map[GPIO_INDEX] &= ~bit;
+       } else if (GPIO_ISRAM(pin)) {
+               if (~access_map[GPIO_INDEX] & bit)
+                       goto out;
+               access_map[GPIO_INDEX] &= ~bit;
+       } else if (GPIO_ISMUX(pin)) {
+               if (~access_map[MUX_INDEX] & bit)
+                       goto out;
+               access_map[MUX_INDEX] &= ~bit;
+       } else if (GPIO_ISOUT(pin)) {
+               if (~access_map[OUTP_INDEX] & bit)
+                       goto out;
+               access_map[OUTP_INDEX] &= ~bit;
+       } else if (GPIO_ISIN(pin)) {
+               if (~access_map[INP_INDEX] & bit)
+                       goto out;
+               access_map[INP_INDEX] &= ~bit;
+       } else
+               goto out;
+       ret = 0;
+
+out:
+       gpio_unlock();
+       return ret;
+}
+
+EXPORT_SYMBOL(pnx4008_gpio_unregister_pin);
+
+unsigned long pnx4008_gpio_read_pin(unsigned short pin)
+{
+       unsigned long ret = -EFAULT;
+       int gpio = GPIO_BIT_MASK(pin);
+       gpio_lock();
+       if (GPIO_ISOUT(pin)) {
+               ret = gpio_read_bit(PIO_OUTP_STATE, gpio);
+       } else if (GPIO_ISRAM(pin)) {
+               if (gpio_read_bit(PIO_DRV_STATE, gpio) == 0) {
+                       ret = gpio_read_bit(PIO_SDINP_STATE, gpio);
+               }
+       } else if (GPIO_ISBID(pin)) {
+               ret = gpio_read_bit(PIO_DRV_STATE, gpio);
+               if (ret > 0)
+                       ret = gpio_read_bit(PIO_OUTP_STATE, gpio);
+               else if (ret == 0)
+                       ret =
+                           gpio_read_bit(PIO_INP_STATE, gpio_to_inp_map[gpio]);
+       } else if (GPIO_ISIN(pin)) {
+               ret = gpio_read_bit(PIO_INP_STATE, gpio);
+       }
+       gpio_unlock();
+       return ret;
+}
+
+EXPORT_SYMBOL(pnx4008_gpio_read_pin);
+
+/* Write Value to output */
+int pnx4008_gpio_write_pin(unsigned short pin, int output)
+{
+       int gpio = GPIO_BIT_MASK(pin);
+       int ret = -EFAULT;
+
+       gpio_lock();
+       if (GPIO_ISOUT(pin)) {
+               printk( "writing '%x' to '%x'\n",
+                               gpio, output ? PIO_OUTP_SET : PIO_OUTP_CLR );
+               ret = gpio_set_bit(output ? PIO_OUTP_SET : PIO_OUTP_CLR, gpio);
+       } else if (GPIO_ISRAM(pin)) {
+               if (gpio_read_bit(PIO_DRV_STATE, gpio) > 0)
+                       ret = gpio_set_bit(output ? PIO_SDOUTP_SET :
+                                          PIO_SDOUTP_CLR, gpio);
+       } else if (GPIO_ISBID(pin)) {
+               if (gpio_read_bit(PIO_DRV_STATE, gpio) > 0)
+                       ret = gpio_set_bit(output ? PIO_OUTP_SET :
+                                          PIO_OUTP_CLR, gpio);
+       }
+       gpio_unlock();
+       return ret;
+}
+
+EXPORT_SYMBOL(pnx4008_gpio_write_pin);
+
+/* Value = 1 : Set GPIO pin as output */
+/* Value = 0 : Set GPIO pin as input */
+int pnx4008_gpio_set_pin_direction(unsigned short pin, int output)
+{
+       int gpio = GPIO_BIT_MASK(pin);
+       int ret = -EFAULT;
+
+       gpio_lock();
+       if (GPIO_ISBID(pin) || GPIO_ISRAM(pin)) {
+               ret = gpio_set_bit(output ? PIO_DRV_SET : PIO_DRV_CLR, gpio);
+       }
+       gpio_unlock();
+       return ret;
+}
+
+EXPORT_SYMBOL(pnx4008_gpio_set_pin_direction);
+
+/* Read GPIO pin direction: 0= pin used as input, 1= pin used as output*/
+int pnx4008_gpio_read_pin_direction(unsigned short pin)
+{
+       int gpio = GPIO_BIT_MASK(pin);
+       int ret = -EFAULT;
+
+       gpio_lock();
+       if (GPIO_ISBID(pin) || GPIO_ISRAM(pin)) {
+               ret = gpio_read_bit(PIO_DRV_STATE, gpio);
+       }
+       gpio_unlock();
+       return ret;
+}
+
+EXPORT_SYMBOL(pnx4008_gpio_read_pin_direction);
+
+/* Value = 1 : Set pin to muxed function  */
+/* Value = 0 : Set pin as GPIO */
+int pnx4008_gpio_set_pin_mux(unsigned short pin, int output)
+{
+       int gpio = GPIO_BIT_MASK(pin);
+       int ret = -EFAULT;
+
+       gpio_lock();
+       if (GPIO_ISBID(pin)) {
+               ret =
+                   gpio_set_bit(output ? PIO_MUX_SET : PIO_MUX_CLR,
+                                gpio_to_mux_map[gpio]);
+       } else if (GPIO_ISOUT(pin)) {
+               ret =
+                   gpio_set_bit(output ? PIO_MUX_SET : PIO_MUX_CLR,
+                                outp_to_mux_map[gpio]);
+       } else if (GPIO_ISMUX(pin)) {
+               ret = gpio_set_bit(output ? PIO_MUX_SET : PIO_MUX_CLR, gpio);
+       }
+       gpio_unlock();
+       return ret;
+}
+
+EXPORT_SYMBOL(pnx4008_gpio_set_pin_mux);
+
+/* Read pin mux function: 0= pin used as GPIO, 1= pin used for muxed function*/
+int pnx4008_gpio_read_pin_mux(unsigned short pin)
+{
+       int gpio = GPIO_BIT_MASK(pin);
+       int ret = -EFAULT;
+
+       gpio_lock();
+       if (GPIO_ISBID(pin)) {
+               ret = gpio_read_bit(PIO_MUX_STATE, gpio_to_mux_map[gpio]);
+       } else if (GPIO_ISOUT(pin)) {
+               ret = gpio_read_bit(PIO_MUX_STATE, outp_to_mux_map[gpio]);
+       } else if (GPIO_ISMUX(pin)) {
+               ret = gpio_read_bit(PIO_MUX_STATE, gpio);
+       }
+       gpio_unlock();
+       return ret;
+}
+
+EXPORT_SYMBOL(pnx4008_gpio_read_pin_mux);