summaryrefslogtreecommitdiffstats
path: root/u-boot/drivers/gpio
diff options
context:
space:
mode:
Diffstat (limited to 'u-boot/drivers/gpio')
-rw-r--r--u-boot/drivers/gpio/Makefile52
-rw-r--r--u-boot/drivers/gpio/at91_gpio.c214
-rw-r--r--u-boot/drivers/gpio/kw_gpio.c165
-rw-r--r--u-boot/drivers/gpio/mvmfp.c89
-rw-r--r--u-boot/drivers/gpio/mxc_gpio.c112
-rw-r--r--u-boot/drivers/gpio/pca953x.c294
-rw-r--r--u-boot/drivers/gpio/s5p_gpio.c143
7 files changed, 1069 insertions, 0 deletions
diff --git a/u-boot/drivers/gpio/Makefile b/u-boot/drivers/gpio/Makefile
new file mode 100644
index 0000000..a5fa2b5
--- /dev/null
+++ b/u-boot/drivers/gpio/Makefile
@@ -0,0 +1,52 @@
+#
+# Copyright 2000-2008
+# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+#
+# See file CREDITS for list of people who contributed to this
+# project.
+#
+# 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., 59 Temple Place, Suite 330, Boston,
+# MA 02111-1307 USA
+#
+
+include $(TOPDIR)/config.mk
+
+LIB := $(obj)libgpio.o
+
+COBJS-$(CONFIG_AT91_GPIO) += at91_gpio.o
+COBJS-$(CONFIG_KIRKWOOD_GPIO) += kw_gpio.o
+COBJS-$(CONFIG_MARVELL_MFP) += mvmfp.o
+COBJS-$(CONFIG_MXC_GPIO) += mxc_gpio.o
+COBJS-$(CONFIG_PCA953X) += pca953x.o
+COBJS-$(CONFIG_S5P) += s5p_gpio.o
+
+COBJS := $(COBJS-y)
+SRCS := $(COBJS:.o=.c)
+OBJS := $(addprefix $(obj),$(COBJS))
+
+all: $(LIB)
+
+$(LIB): $(obj).depend $(OBJS)
+ $(call cmd_link_o_target, $(OBJS))
+
+
+#########################################################################
+
+# defines $(obj).depend target
+include $(SRCTREE)/rules.mk
+
+sinclude $(obj).depend
+
+########################################################################
diff --git a/u-boot/drivers/gpio/at91_gpio.c b/u-boot/drivers/gpio/at91_gpio.c
new file mode 100644
index 0000000..c0a97bc
--- /dev/null
+++ b/u-boot/drivers/gpio/at91_gpio.c
@@ -0,0 +1,214 @@
+/*
+ * Memory Setup stuff - taken from blob memsetup.S
+ *
+ * Copyright (C) 2009 Jens Scharsig (js_at_ng@scharsoft.de)
+ *
+ * Copyright (C) 2005 HP Labs
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <config.h>
+#include <common.h>
+#include <asm/sizes.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/io.h>
+#include <asm/arch/at91_pio.h>
+
+int at91_set_pio_pullup(unsigned port, unsigned pin, int use_pullup)
+{
+ at91_pio_t *pio = (at91_pio_t *) AT91_PIO_BASE;
+ u32 mask;
+
+ if ((port < AT91_PIO_PORTS) && (pin < 32)) {
+ mask = 1 << pin;
+ if (use_pullup)
+ writel(1 << pin, &pio->port[port].puer);
+ else
+ writel(1 << pin, &pio->port[port].pudr);
+ writel(mask, &pio->port[port].per);
+ }
+ return 0;
+}
+
+/*
+ * mux the pin to the "GPIO" peripheral role.
+ */
+int at91_set_pio_periph(unsigned port, unsigned pin, int use_pullup)
+{
+ at91_pio_t *pio = (at91_pio_t *) AT91_PIO_BASE;
+ u32 mask;
+
+ if ((port < AT91_PIO_PORTS) && (pin < 32)) {
+ mask = 1 << pin;
+ writel(mask, &pio->port[port].idr);
+ at91_set_pio_pullup(port, pin, use_pullup);
+ writel(mask, &pio->port[port].per);
+ }
+ return 0;
+}
+
+/*
+ * mux the pin to the "A" internal peripheral role.
+ */
+int at91_set_a_periph(unsigned port, unsigned pin, int use_pullup)
+{
+ at91_pio_t *pio = (at91_pio_t *) AT91_PIO_BASE;
+ u32 mask;
+
+ if ((port < AT91_PIO_PORTS) && (pin < 32)) {
+ mask = 1 << pin;
+ writel(mask, &pio->port[port].idr);
+ at91_set_pio_pullup(port, pin, use_pullup);
+ writel(mask, &pio->port[port].asr);
+ writel(mask, &pio->port[port].pdr);
+ }
+ return 0;
+}
+
+/*
+ * mux the pin to the "B" internal peripheral role.
+ */
+int at91_set_b_periph(unsigned port, unsigned pin, int use_pullup)
+{
+ at91_pio_t *pio = (at91_pio_t *) AT91_PIO_BASE;
+ u32 mask;
+
+ if ((port < AT91_PIO_PORTS) && (pin < 32)) {
+ mask = 1 << pin;
+ writel(mask, &pio->port[port].idr);
+ at91_set_pio_pullup(port, pin, use_pullup);
+ writel(mask, &pio->port[port].bsr);
+ writel(mask, &pio->port[port].pdr);
+ }
+ return 0;
+}
+
+/*
+ * mux the pin to the gpio controller (instead of "A" or "B" peripheral), and
+ * configure it for an input.
+ */
+int at91_set_pio_input(unsigned port, u32 pin, int use_pullup)
+{
+ at91_pio_t *pio = (at91_pio_t *) AT91_PIO_BASE;
+ u32 mask;
+
+ if ((port < AT91_PIO_PORTS) && (pin < 32)) {
+ mask = 1 << pin;
+ writel(mask, &pio->port[port].idr);
+ at91_set_pio_pullup(port, pin, use_pullup);
+ writel(mask, &pio->port[port].odr);
+ writel(mask, &pio->port[port].per);
+ }
+ return 0;
+}
+
+/*
+ * mux the pin to the gpio controller (instead of "A" or "B" peripheral),
+ * and configure it for an output.
+ */
+int at91_set_pio_output(unsigned port, u32 pin, int value)
+{
+ at91_pio_t *pio = (at91_pio_t *) AT91_PIO_BASE;
+ u32 mask;
+
+ if ((port < AT91_PIO_PORTS) && (pin < 32)) {
+ mask = 1 << pin;
+ writel(mask, &pio->port[port].idr);
+ writel(mask, &pio->port[port].pudr);
+ if (value)
+ writel(mask, &pio->port[port].sodr);
+ else
+ writel(mask, &pio->port[port].codr);
+ writel(mask, &pio->port[port].oer);
+ writel(mask, &pio->port[port].per);
+ }
+ return 0;
+}
+
+/*
+ * enable/disable the glitch filter. mostly used with IRQ handling.
+ */
+int at91_set_pio_deglitch(unsigned port, unsigned pin, int is_on)
+{
+ at91_pio_t *pio = (at91_pio_t *) AT91_PIO_BASE;
+ u32 mask;
+
+ if ((port < AT91_PIO_PORTS) && (pin < 32)) {
+ mask = 1 << pin;
+ if (is_on)
+ writel(mask, &pio->port[port].ifer);
+ else
+ writel(mask, &pio->port[port].ifdr);
+ }
+ return 0;
+}
+
+/*
+ * enable/disable the multi-driver. This is only valid for output and
+ * allows the output pin to run as an open collector output.
+ */
+int at91_set_pio_multi_drive(unsigned port, unsigned pin, int is_on)
+{
+ at91_pio_t *pio = (at91_pio_t *) AT91_PIO_BASE;
+ u32 mask;
+
+ if ((port < AT91_PIO_PORTS) && (pin < 32)) {
+ mask = 1 << pin;
+ if (is_on)
+ writel(mask, &pio->port[port].mder);
+ else
+ writel(mask, &pio->port[port].mddr);
+ }
+ return 0;
+}
+
+/*
+ * assuming the pin is muxed as a gpio output, set its value.
+ */
+int at91_set_pio_value(unsigned port, unsigned pin, int value)
+{
+ at91_pio_t *pio = (at91_pio_t *) AT91_PIO_BASE;
+ u32 mask;
+
+ if ((port < AT91_PIO_PORTS) && (pin < 32)) {
+ mask = 1 << pin;
+ if (value)
+ writel(mask, &pio->port[port].sodr);
+ else
+ writel(mask, &pio->port[port].codr);
+ }
+ return 0;
+}
+
+/*
+ * read the pin's value (works even if it's not muxed as a gpio).
+ */
+int at91_get_pio_value(unsigned port, unsigned pin)
+{
+ u32 pdsr = 0;
+ at91_pio_t *pio = (at91_pio_t *) AT91_PIO_BASE;
+ u32 mask;
+
+ if ((port < AT91_PIO_PORTS) && (pin < 32)) {
+ mask = 1 << pin;
+ pdsr = readl(&pio->port[port].pdsr) & mask;
+ }
+ return pdsr != 0;
+}
diff --git a/u-boot/drivers/gpio/kw_gpio.c b/u-boot/drivers/gpio/kw_gpio.c
new file mode 100644
index 0000000..56383c2
--- /dev/null
+++ b/u-boot/drivers/gpio/kw_gpio.c
@@ -0,0 +1,165 @@
+/*
+ * arch/arm/plat-orion/gpio.c
+ *
+ * Marvell Orion SoC GPIO handling.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+/*
+ * Based on (mostly copied from) plat-orion based Linux 2.6 kernel driver.
+ * Removed orion_gpiochip struct and kernel level irq handling.
+ *
+ * Dieter Kiermaier dk-arm-linux@gmx.de
+ */
+
+#include <common.h>
+#include <asm/bitops.h>
+#include <asm/arch/kirkwood.h>
+#include <asm/arch/gpio.h>
+
+static unsigned long gpio_valid_input[BITS_TO_LONGS(GPIO_MAX)];
+static unsigned long gpio_valid_output[BITS_TO_LONGS(GPIO_MAX)];
+
+void __set_direction(unsigned pin, int input)
+{
+ u32 u;
+
+ u = readl(GPIO_IO_CONF(pin));
+ if (input)
+ u |= 1 << (pin & 31);
+ else
+ u &= ~(1 << (pin & 31));
+ writel(u, GPIO_IO_CONF(pin));
+
+ u = readl(GPIO_IO_CONF(pin));
+}
+
+void __set_level(unsigned pin, int high)
+{
+ u32 u;
+
+ u = readl(GPIO_OUT(pin));
+ if (high)
+ u |= 1 << (pin & 31);
+ else
+ u &= ~(1 << (pin & 31));
+ writel(u, GPIO_OUT(pin));
+}
+
+void __set_blinking(unsigned pin, int blink)
+{
+ u32 u;
+
+ u = readl(GPIO_BLINK_EN(pin));
+ if (blink)
+ u |= 1 << (pin & 31);
+ else
+ u &= ~(1 << (pin & 31));
+ writel(u, GPIO_BLINK_EN(pin));
+}
+
+int kw_gpio_is_valid(unsigned pin, int mode)
+{
+ if (pin < GPIO_MAX) {
+ if ((mode & GPIO_INPUT_OK) && !test_bit(pin, gpio_valid_input))
+ goto err_out;
+
+ if ((mode & GPIO_OUTPUT_OK) && !test_bit(pin, gpio_valid_output))
+ goto err_out;
+ return 0;
+ }
+
+err_out:
+ printf("%s: invalid GPIO %d\n", __func__, pin);
+ return 1;
+}
+
+void kw_gpio_set_valid(unsigned pin, int mode)
+{
+ if (mode == 1)
+ mode = GPIO_INPUT_OK | GPIO_OUTPUT_OK;
+ if (mode & GPIO_INPUT_OK)
+ __set_bit(pin, gpio_valid_input);
+ else
+ __clear_bit(pin, gpio_valid_input);
+ if (mode & GPIO_OUTPUT_OK)
+ __set_bit(pin, gpio_valid_output);
+ else
+ __clear_bit(pin, gpio_valid_output);
+}
+/*
+ * GENERIC_GPIO primitives.
+ */
+int kw_gpio_direction_input(unsigned pin)
+{
+ if (!kw_gpio_is_valid(pin, GPIO_INPUT_OK))
+ return 1;
+
+ /* Configure GPIO direction. */
+ __set_direction(pin, 1);
+
+ return 0;
+}
+
+int kw_gpio_direction_output(unsigned pin, int value)
+{
+ if (kw_gpio_is_valid(pin, GPIO_OUTPUT_OK) != 0)
+ {
+ printf("%s: invalid GPIO %d\n", __func__, pin);
+ return 1;
+ }
+
+ __set_blinking(pin, 0);
+
+ /* Configure GPIO output value. */
+ __set_level(pin, value);
+
+ /* Configure GPIO direction. */
+ __set_direction(pin, 0);
+
+ return 0;
+}
+
+int kw_gpio_get_value(unsigned pin)
+{
+ int val;
+
+ if (readl(GPIO_IO_CONF(pin)) & (1 << (pin & 31)))
+ val = readl(GPIO_DATA_IN(pin)) ^ readl(GPIO_IN_POL(pin));
+ else
+ val = readl(GPIO_OUT(pin));
+
+ return (val >> (pin & 31)) & 1;
+}
+
+void kw_gpio_set_value(unsigned pin, int value)
+{
+ /* Configure GPIO output value. */
+ __set_level(pin, value);
+}
+
+void kw_gpio_set_blink(unsigned pin, int blink)
+{
+ /* Set output value to zero. */
+ __set_level(pin, 0);
+
+ /* Set blinking. */
+ __set_blinking(pin, blink);
+}
diff --git a/u-boot/drivers/gpio/mvmfp.c b/u-boot/drivers/gpio/mvmfp.c
new file mode 100644
index 0000000..e7830c6
--- /dev/null
+++ b/u-boot/drivers/gpio/mvmfp.c
@@ -0,0 +1,89 @@
+/*
+ * (C) Copyright 2010
+ * Marvell Semiconductor <www.marvell.com>
+ * Written-by: Prafulla Wadaskar <prafulla@marvell.com>,
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <mvmfp.h>
+#include <asm/arch/mfp.h>
+#ifdef CONFIG_ARMADA100
+#include <asm/arch/armada100.h>
+#elif defined(CONFIG_PANTHEON)
+#include <asm/arch/pantheon.h>
+#else
+#error Unsupported SoC...
+#endif
+
+/*
+ * mfp_config
+ *
+ * On most of Marvell SoCs (ex. ARMADA100) there is Multi-Funtion-Pin
+ * configuration registers to configure each GPIO/Function pin on the
+ * SoC.
+ *
+ * This function reads the array of values for
+ * MFPR_X registers and programms them into respective
+ * Multi-Function Pin registers.
+ * It supports - Alternate Function Selection programming.
+ *
+ * Whereas,
+ * The Configureation value is constructed using MFP()
+ * array consists of 32bit values as defined in MFP(xx,xx..) macro
+ */
+void mfp_config(u32 *mfp_cfgs)
+{
+ u32 *p_mfpr = NULL;
+ u32 cfg_val, val;
+
+ do {
+ cfg_val = *mfp_cfgs++;
+ /* exit if End of configuration table detected */
+ if (cfg_val == MFP_EOC)
+ break;
+
+ p_mfpr = (u32 *)(MV_MFPR_BASE
+ + MFP_REG_GET_OFFSET(cfg_val));
+
+ /* Write a mfg register as per configuration */
+ val = 0;
+ if (cfg_val & MFP_AF_FLAG)
+ /* Abstract and program Afternate-Func Selection */
+ val |= cfg_val & MFP_AF_MASK;
+ if (cfg_val & MFP_EDGE_FLAG)
+ /* Abstract and program Edge configuration */
+ val |= cfg_val & MFP_LPM_EDGE_MASK;
+ if (cfg_val & MFP_DRIVE_FLAG)
+ /* Abstract and program Drive configuration */
+ val |= cfg_val & MFP_DRIVE_MASK;
+ if (cfg_val & MFP_PULL_FLAG)
+ /* Abstract and program Pullup/down configuration */
+ val |= cfg_val & MFP_PULL_MASK;
+
+ writel(val, p_mfpr);
+ } while (1);
+ /*
+ * perform a read-back of any MFPR register to make sure the
+ * previous writings are finished
+ */
+ readl(p_mfpr);
+}
diff --git a/u-boot/drivers/gpio/mxc_gpio.c b/u-boot/drivers/gpio/mxc_gpio.c
new file mode 100644
index 0000000..53a0673
--- /dev/null
+++ b/u-boot/drivers/gpio/mxc_gpio.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2009
+ * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+#include <common.h>
+#ifdef CONFIG_MX31
+#include <asm/arch/mx31-regs.h>
+#endif
+#if defined(CONFIG_MX51) || defined(CONFIG_MX53)
+#include <asm/arch/imx-regs.h>
+#endif
+#include <asm/io.h>
+#include <mxc_gpio.h>
+
+/* GPIO port description */
+static unsigned long gpio_ports[] = {
+ [0] = GPIO1_BASE_ADDR,
+ [1] = GPIO2_BASE_ADDR,
+ [2] = GPIO3_BASE_ADDR,
+#if defined(CONFIG_MX51) || defined(CONFIG_MX53)
+ [3] = GPIO4_BASE_ADDR,
+#endif
+#if defined(CONFIG_MX53)
+ [4] = GPIO5_BASE_ADDR,
+ [5] = GPIO6_BASE_ADDR,
+ [6] = GPIO7_BASE_ADDR,
+#endif
+};
+
+int mxc_gpio_direction(unsigned int gpio, enum mxc_gpio_direction direction)
+{
+ unsigned int port = gpio >> 5;
+ struct gpio_regs *regs;
+ u32 l;
+
+ if (port >= ARRAY_SIZE(gpio_ports))
+ return 1;
+
+ gpio &= 0x1f;
+
+ regs = (struct gpio_regs *)gpio_ports[port];
+
+ l = readl(&regs->gpio_dir);
+
+ switch (direction) {
+ case MXC_GPIO_DIRECTION_OUT:
+ l |= 1 << gpio;
+ break;
+ case MXC_GPIO_DIRECTION_IN:
+ l &= ~(1 << gpio);
+ }
+ writel(l, &regs->gpio_dir);
+
+ return 0;
+}
+
+void mxc_gpio_set(unsigned int gpio, unsigned int value)
+{
+ unsigned int port = gpio >> 5;
+ struct gpio_regs *regs;
+ u32 l;
+
+ if (port >= ARRAY_SIZE(gpio_ports))
+ return;
+
+ gpio &= 0x1f;
+
+ regs = (struct gpio_regs *)gpio_ports[port];
+
+ l = readl(&regs->gpio_dr);
+ if (value)
+ l |= 1 << gpio;
+ else
+ l &= ~(1 << gpio);
+ writel(l, &regs->gpio_dr);
+}
+
+int mxc_gpio_get(unsigned int gpio)
+{
+ unsigned int port = gpio >> 5;
+ struct gpio_regs *regs;
+ u32 l;
+
+ if (port >= ARRAY_SIZE(gpio_ports))
+ return -1;
+
+ gpio &= 0x1f;
+
+ regs = (struct gpio_regs *)gpio_ports[port];
+
+ l = (readl(&regs->gpio_dr) >> gpio) & 0x01;
+
+ return l;
+}
diff --git a/u-boot/drivers/gpio/pca953x.c b/u-boot/drivers/gpio/pca953x.c
new file mode 100644
index 0000000..359fdee
--- /dev/null
+++ b/u-boot/drivers/gpio/pca953x.c
@@ -0,0 +1,294 @@
+/*
+ * Copyright 2008 Extreme Engineering Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * Version 2 as published by the Free Software Foundation.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+/*
+ * Driver for NXP's 4, 8 and 16 bit I2C gpio expanders (eg pca9537, pca9557,
+ * pca9539, etc)
+ */
+
+#include <common.h>
+#include <i2c.h>
+#include <pca953x.h>
+
+/* Default to an address that hopefully won't corrupt other i2c devices */
+#ifndef CONFIG_SYS_I2C_PCA953X_ADDR
+#define CONFIG_SYS_I2C_PCA953X_ADDR (~0)
+#endif
+
+enum {
+ PCA953X_CMD_INFO,
+ PCA953X_CMD_DEVICE,
+ PCA953X_CMD_OUTPUT,
+ PCA953X_CMD_INPUT,
+ PCA953X_CMD_INVERT,
+};
+
+#ifdef CONFIG_SYS_I2C_PCA953X_WIDTH
+struct pca953x_chip_ngpio {
+ uint8_t chip;
+ uint8_t ngpio;
+};
+
+static struct pca953x_chip_ngpio pca953x_chip_ngpios[] =
+ CONFIG_SYS_I2C_PCA953X_WIDTH;
+
+#define NUM_CHIP_GPIOS (sizeof(pca953x_chip_ngpios) / \
+ sizeof(struct pca953x_chip_ngpio))
+
+/*
+ * Determine the number of GPIO pins supported. If we don't know we assume
+ * 8 pins.
+ */
+static int pca953x_ngpio(uint8_t chip)
+{
+ int i;
+
+ for (i = 0; i < NUM_CHIP_GPIOS; i++)
+ if (pca953x_chip_ngpios[i].chip == chip)
+ return pca953x_chip_ngpios[i].ngpio;
+
+ return 8;
+}
+#else
+static int pca953x_ngpio(uint8_t chip)
+{
+ return 8;
+}
+#endif
+
+/*
+ * Modify masked bits in register
+ */
+static int pca953x_reg_write(uint8_t chip, uint addr, uint mask, uint data)
+{
+ uint8_t valb;
+ uint16_t valw;
+
+ if (pca953x_ngpio(chip) <= 8) {
+ if (i2c_read(chip, addr, 1, &valb, 1))
+ return -1;
+
+ valb &= ~mask;
+ valb |= data;
+
+ return i2c_write(chip, addr, 1, &valb, 1);
+ } else {
+ if (i2c_read(chip, addr << 1, 1, (u8*)&valw, 2))
+ return -1;
+
+ valw &= ~mask;
+ valw |= data;
+
+ return i2c_write(chip, addr << 1, 1, (u8*)&valw, 2);
+ }
+}
+
+static int pca953x_reg_read(uint8_t chip, uint addr, uint *data)
+{
+ uint8_t valb;
+ uint16_t valw;
+
+ if (pca953x_ngpio(chip) <= 8) {
+ if (i2c_read(chip, addr, 1, &valb, 1))
+ return -1;
+ *data = (int)valb;
+ } else {
+ if (i2c_read(chip, addr << 1, 1, (u8*)&valw, 2))
+ return -1;
+ *data = (int)valw;
+ }
+ return 0;
+}
+
+/*
+ * Set output value of IO pins in 'mask' to corresponding value in 'data'
+ * 0 = low, 1 = high
+ */
+int pca953x_set_val(uint8_t chip, uint mask, uint data)
+{
+ return pca953x_reg_write(chip, PCA953X_OUT, mask, data);
+}
+
+/*
+ * Set read polarity of IO pins in 'mask' to corresponding value in 'data'
+ * 0 = read pin value, 1 = read inverted pin value
+ */
+int pca953x_set_pol(uint8_t chip, uint mask, uint data)
+{
+ return pca953x_reg_write(chip, PCA953X_POL, mask, data);
+}
+
+/*
+ * Set direction of IO pins in 'mask' to corresponding value in 'data'
+ * 0 = output, 1 = input
+ */
+int pca953x_set_dir(uint8_t chip, uint mask, uint data)
+{
+ return pca953x_reg_write(chip, PCA953X_CONF, mask, data);
+}
+
+/*
+ * Read current logic level of all IO pins
+ */
+int pca953x_get_val(uint8_t chip)
+{
+ uint val;
+
+ if (pca953x_reg_read(chip, PCA953X_IN, &val) < 0)
+ return -1;
+
+ return (int)val;
+}
+
+#ifdef CONFIG_CMD_PCA953X
+#ifdef CONFIG_CMD_PCA953X_INFO
+/*
+ * Display pca953x information
+ */
+static int pca953x_info(uint8_t chip)
+{
+ int i;
+ uint data;
+ int nr_gpio = pca953x_ngpio(chip);
+ int msb = nr_gpio - 1;
+
+ printf("pca953x@ 0x%x (%d pins):\n\n", chip, nr_gpio);
+ printf("gpio pins: ");
+ for (i = msb; i >= 0; i--)
+ printf("%x", i);
+ printf("\n");
+ for (i = 11 + nr_gpio; i > 0; i--)
+ printf("-");
+ printf("\n");
+
+ if (pca953x_reg_read(chip, PCA953X_CONF, &data) < 0)
+ return -1;
+ printf("conf: ");
+ for (i = msb; i >= 0; i--)
+ printf("%c", data & (1 << i) ? 'i' : 'o');
+ printf("\n");
+
+ if (pca953x_reg_read(chip, PCA953X_POL, &data) < 0)
+ return -1;
+ printf("invert: ");
+ for (i = msb; i >= 0; i--)
+ printf("%c", data & (1 << i) ? '1' : '0');
+ printf("\n");
+
+ if (pca953x_reg_read(chip, PCA953X_IN, &data) < 0)
+ return -1;
+ printf("input: ");
+ for (i = msb; i >= 0; i--)
+ printf("%c", data & (1 << i) ? '1' : '0');
+ printf("\n");
+
+ if (pca953x_reg_read(chip, PCA953X_OUT, &data) < 0)
+ return -1;
+ printf("output: ");
+ for (i = msb; i >= 0; i--)
+ printf("%c", data & (1 << i) ? '1' : '0');
+ printf("\n");
+
+ return 0;
+}
+#endif /* CONFIG_CMD_PCA953X_INFO */
+
+cmd_tbl_t cmd_pca953x[] = {
+ U_BOOT_CMD_MKENT(device, 3, 0, (void *)PCA953X_CMD_DEVICE, "", ""),
+ U_BOOT_CMD_MKENT(output, 4, 0, (void *)PCA953X_CMD_OUTPUT, "", ""),
+ U_BOOT_CMD_MKENT(input, 3, 0, (void *)PCA953X_CMD_INPUT, "", ""),
+ U_BOOT_CMD_MKENT(invert, 4, 0, (void *)PCA953X_CMD_INVERT, "", ""),
+#ifdef CONFIG_CMD_PCA953X_INFO
+ U_BOOT_CMD_MKENT(info, 2, 0, (void *)PCA953X_CMD_INFO, "", ""),
+#endif
+};
+
+int do_pca953x(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+ static uint8_t chip = CONFIG_SYS_I2C_PCA953X_ADDR;
+ int val;
+ ulong ul_arg2 = 0;
+ ulong ul_arg3 = 0;
+ cmd_tbl_t *c;
+
+ c = find_cmd_tbl(argv[1], cmd_pca953x, ARRAY_SIZE(cmd_pca953x));
+
+ /* All commands but "device" require 'maxargs' arguments */
+ if (!c || !((argc == (c->maxargs)) ||
+ (((int)c->cmd == PCA953X_CMD_DEVICE) &&
+ (argc == (c->maxargs - 1))))) {
+ return cmd_usage(cmdtp);
+ }
+
+ /* arg2 used as chip number or pin number */
+ if (argc > 2)
+ ul_arg2 = simple_strtoul(argv[2], NULL, 16);
+
+ /* arg3 used as pin or invert value */
+ if (argc > 3)
+ ul_arg3 = simple_strtoul(argv[3], NULL, 16) & 0x1;
+
+ switch ((int)c->cmd) {
+#ifdef CONFIG_CMD_PCA953X_INFO
+ case PCA953X_CMD_INFO:
+ return pca953x_info(chip);
+#endif
+ case PCA953X_CMD_DEVICE:
+ if (argc == 3)
+ chip = (uint8_t)ul_arg2;
+ printf("Current device address: 0x%x\n", chip);
+ return 0;
+ case PCA953X_CMD_INPUT:
+ pca953x_set_dir(chip, (1 << ul_arg2),
+ PCA953X_DIR_IN << ul_arg2);
+ val = (pca953x_get_val(chip) & (1 << ul_arg2)) != 0;
+
+ printf("chip 0x%02x, pin 0x%lx = %d\n", chip, ul_arg2, val);
+ return val;
+ case PCA953X_CMD_OUTPUT:
+ pca953x_set_dir(chip, (1 << ul_arg2),
+ (PCA953X_DIR_OUT << ul_arg2));
+ return pca953x_set_val(chip, (1 << ul_arg2),
+ (ul_arg3 << ul_arg2));
+ case PCA953X_CMD_INVERT:
+ return pca953x_set_pol(chip, (1 << ul_arg2),
+ (ul_arg3 << ul_arg2));
+ default:
+ /* We should never get here */
+ return 1;
+ }
+}
+
+U_BOOT_CMD(
+ pca953x, 5, 1, do_pca953x,
+ "pca953x gpio access",
+ "device [dev]\n"
+ " - show or set current device address\n"
+#ifdef CONFIG_CMD_PCA953X_INFO
+ "pca953x info\n"
+ " - display info for current chip\n"
+#endif
+ "pca953x output pin 0|1\n"
+ " - set pin as output and drive low or high\n"
+ "pca953x invert pin 0|1\n"
+ " - disable/enable polarity inversion for reads\n"
+ "pca953x intput pin\n"
+ " - set pin as input and read value"
+);
+
+#endif /* CONFIG_CMD_PCA953X */
diff --git a/u-boot/drivers/gpio/s5p_gpio.c b/u-boot/drivers/gpio/s5p_gpio.c
new file mode 100644
index 0000000..a1bcddc
--- /dev/null
+++ b/u-boot/drivers/gpio/s5p_gpio.c
@@ -0,0 +1,143 @@
+/*
+ * (C) Copyright 2009 Samsung Electronics
+ * Minkyu Kang <mk7.kang@samsung.com>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <asm/arch/gpio.h>
+
+#define CON_MASK(x) (0xf << ((x) << 2))
+#define CON_SFR(x, v) ((v) << ((x) << 2))
+
+#define DAT_MASK(x) (0x1 << (x))
+#define DAT_SET(x) (0x1 << (x))
+
+#define PULL_MASK(x) (0x3 << ((x) << 1))
+#define PULL_MODE(x, v) ((v) << ((x) << 1))
+
+#define DRV_MASK(x) (0x3 << ((x) << 1))
+#define DRV_SET(x, m) ((m) << ((x) << 1))
+#define RATE_MASK(x) (0x1 << (x + 16))
+#define RATE_SET(x) (0x1 << (x + 16))
+
+void gpio_cfg_pin(struct s5p_gpio_bank *bank, int gpio, int cfg)
+{
+ unsigned int value;
+
+ value = readl(&bank->con);
+ value &= ~CON_MASK(gpio);
+ value |= CON_SFR(gpio, cfg);
+ writel(value, &bank->con);
+}
+
+void gpio_direction_output(struct s5p_gpio_bank *bank, int gpio, int en)
+{
+ unsigned int value;
+
+ gpio_cfg_pin(bank, gpio, GPIO_OUTPUT);
+
+ value = readl(&bank->dat);
+ value &= ~DAT_MASK(gpio);
+ if (en)
+ value |= DAT_SET(gpio);
+ writel(value, &bank->dat);
+}
+
+void gpio_direction_input(struct s5p_gpio_bank *bank, int gpio)
+{
+ gpio_cfg_pin(bank, gpio, GPIO_INPUT);
+}
+
+void gpio_set_value(struct s5p_gpio_bank *bank, int gpio, int en)
+{
+ unsigned int value;
+
+ value = readl(&bank->dat);
+ value &= ~DAT_MASK(gpio);
+ if (en)
+ value |= DAT_SET(gpio);
+ writel(value, &bank->dat);
+}
+
+unsigned int gpio_get_value(struct s5p_gpio_bank *bank, int gpio)
+{
+ unsigned int value;
+
+ value = readl(&bank->dat);
+ return !!(value & DAT_MASK(gpio));
+}
+
+void gpio_set_pull(struct s5p_gpio_bank *bank, int gpio, int mode)
+{
+ unsigned int value;
+
+ value = readl(&bank->pull);
+ value &= ~PULL_MASK(gpio);
+
+ switch (mode) {
+ case GPIO_PULL_DOWN:
+ case GPIO_PULL_UP:
+ value |= PULL_MODE(gpio, mode);
+ break;
+ default:
+ break;
+ }
+
+ writel(value, &bank->pull);
+}
+
+void gpio_set_drv(struct s5p_gpio_bank *bank, int gpio, int mode)
+{
+ unsigned int value;
+
+ value = readl(&bank->drv);
+ value &= ~DRV_MASK(gpio);
+
+ switch (mode) {
+ case GPIO_DRV_1X:
+ case GPIO_DRV_2X:
+ case GPIO_DRV_3X:
+ case GPIO_DRV_4X:
+ value |= DRV_SET(gpio, mode);
+ break;
+ default:
+ return;
+ }
+
+ writel(value, &bank->drv);
+}
+
+void gpio_set_rate(struct s5p_gpio_bank *bank, int gpio, int mode)
+{
+ unsigned int value;
+
+ value = readl(&bank->drv);
+ value &= ~RATE_MASK(gpio);
+
+ switch (mode) {
+ case GPIO_DRV_FAST:
+ case GPIO_DRV_SLOW:
+ value |= RATE_SET(gpio);
+ break;
+ default:
+ return;
+ }
+
+ writel(value, &bank->drv);
+}