diff mbox

[U-Boot,V2,1/2] GPIO: Tegra2: add GPIO driver for Tegra2

Message ID 1304376348-26403-2-git-send-email-twarren@nvidia.com
State Changes Requested
Headers show

Commit Message

Tom Warren May 2, 2011, 10:45 p.m. UTC
Signed-off-by: Tom Warren <twarren@nvidia.com>
---
Changes in V2:
	- use 'gpio_pin' enum in gpio.h (Simon Glass review request)
	- change 'GPIO_PORT8' to 'GPIO_FULLPORT' (Simon Glass request)
	- change 'offset' to 'pin' globally

 arch/arm/include/asm/arch-tegra2/gpio.h |  244 ++++++++++++++++++++++++-
 drivers/gpio/Makefile                   |    1 +
 drivers/gpio/tegra2_gpio.c              |  299 +++++++++++++++++++++++++++++++
 3 files changed, 537 insertions(+), 7 deletions(-)
 create mode 100644 drivers/gpio/tegra2_gpio.c

Comments

Mike Frysinger May 2, 2011, 11:15 p.m. UTC | #1
On Monday, May 02, 2011 18:45:47 Tom Warren wrote:
> +U_BOOT_CMD(
> +	gpio,	5,	1,	do_gpio,

looks to me like you're duplicating a lot of code that already exists in 
common/cmd_gpio.c
-mike
Tom Warren May 2, 2011, 11:16 p.m. UTC | #2
Mike,

On Mon, May 2, 2011 at 4:15 PM, Mike Frysinger <vapier@gentoo.org> wrote:
> On Monday, May 02, 2011 18:45:47 Tom Warren wrote:
>> +U_BOOT_CMD(
>> +     gpio,   5,      1,      do_gpio,
>
> looks to me like you're duplicating a lot of code that already exists in
> common/cmd_gpio.c
> -mike
>

Possible - this was ported from another GPIO driver back in v2009.11
days. I'll take a look at cmd_gpio.c.

Thanks,

Tom
diff mbox

Patch

diff --git a/arch/arm/include/asm/arch-tegra2/gpio.h b/arch/arm/include/asm/arch-tegra2/gpio.h
index 0fb8f0d..e55305a 100644
--- a/arch/arm/include/asm/arch-tegra2/gpio.h
+++ b/arch/arm/include/asm/arch-tegra2/gpio.h
@@ -45,15 +45,245 @@  struct gpio_ctlr {
 	struct gpio_ctlr_bank gpio_bank[TEGRA_GPIO_BANKS];
 };
 
-#define GPIO_BANK(x)	((x) >> 5)
-#define GPIO_PORT(x)	(((x) >> 3) & 0x3)
-#define GPIO_BIT(x)	((x) & 0x7)
+#define GPIO_BANK(x)		((x) >> 5)
+#define GPIO_PORT(x)		(((x) >> 3) & 0x3)
+#define GPIO_FULLPORT(x)	((x) >> 3)
+#define GPIO_BIT(x)		((x) & 0x7)
+
+enum gpio_pin {
+	GPIO_PA0 = 0,	/* pin 0 */
+	GPIO_PA1,
+	GPIO_PA2,
+	GPIO_PA3,
+	GPIO_PA4,
+	GPIO_PA5,
+	GPIO_PA6,
+	GPIO_PA7,
+	GPIO_PB0,	/* pin 8 */
+	GPIO_PB1,
+	GPIO_PB2,
+	GPIO_PB3,
+	GPIO_PB4,
+	GPIO_PB5,
+	GPIO_PB6,
+	GPIO_PB7,
+	GPIO_PC0,	/* pin 16 */
+	GPIO_PC1,
+	GPIO_PC2,
+	GPIO_PC3,
+	GPIO_PC4,
+	GPIO_PC5,
+	GPIO_PC6,
+	GPIO_PC7,
+	GPIO_PD0,	/* pin 24 */
+	GPIO_PD1,
+	GPIO_PD2,
+	GPIO_PD3,
+	GPIO_PD4,
+	GPIO_PD5,
+	GPIO_PD6,
+	GPIO_PD7,
+	GPIO_PE0,	/* pin 32 */
+	GPIO_PE1,
+	GPIO_PE2,
+	GPIO_PE3,
+	GPIO_PE4,
+	GPIO_PE5,
+	GPIO_PE6,
+	GPIO_PE7,
+	GPIO_PF0,	/* pin 40 */
+	GPIO_PF1,
+	GPIO_PF2,
+	GPIO_PF3,
+	GPIO_PF4,
+	GPIO_PF5,
+	GPIO_PF6,
+	GPIO_PF7,
+	GPIO_PG0,	/* pin 48 */
+	GPIO_PG1,
+	GPIO_PG2,
+	GPIO_PG3,
+	GPIO_PG4,
+	GPIO_PG5,
+	GPIO_PG6,
+	GPIO_PG7,
+	GPIO_PH0,	/* pin 56 */
+	GPIO_PH1,
+	GPIO_PH2,
+	GPIO_PH3,
+	GPIO_PH4,
+	GPIO_PH5,
+	GPIO_PH6,
+	GPIO_PH7,
+	GPIO_PI0,	/* pin 64 */
+	GPIO_PI1,
+	GPIO_PI2,
+	GPIO_PI3,
+	GPIO_PI4,
+	GPIO_PI5,
+	GPIO_PI6,
+	GPIO_PI7,
+	GPIO_PJ0,	/* pin 72 */
+	GPIO_PJ1,
+	GPIO_PJ2,
+	GPIO_PJ3,
+	GPIO_PJ4,
+	GPIO_PJ5,
+	GPIO_PJ6,
+	GPIO_PJ7,
+	GPIO_PK0,	/* pin 80 */
+	GPIO_PK1,
+	GPIO_PK2,
+	GPIO_PK3,
+	GPIO_PK4,
+	GPIO_PK5,
+	GPIO_PK6,
+	GPIO_PK7,
+	GPIO_PL0,	/* pin 88 */
+	GPIO_PL1,
+	GPIO_PL2,
+	GPIO_PL3,
+	GPIO_PL4,
+	GPIO_PL5,
+	GPIO_PL6,
+	GPIO_PL7,
+	GPIO_PM0,	/* pin 96 */
+	GPIO_PM1,
+	GPIO_PM2,
+	GPIO_PM3,
+	GPIO_PM4,
+	GPIO_PM5,
+	GPIO_PM6,
+	GPIO_PM7,
+	GPIO_PN0,	/* pin 104 */
+	GPIO_PN1,
+	GPIO_PN2,
+	GPIO_PN3,
+	GPIO_PN4,
+	GPIO_PN5,
+	GPIO_PN6,
+	GPIO_PN7,
+	GPIO_PO0,	/* pin 112 */
+	GPIO_PO1,
+	GPIO_PO2,
+	GPIO_PO3,
+	GPIO_PO4,
+	GPIO_PO5,
+	GPIO_PO6,
+	GPIO_PO7,
+	GPIO_PP0,	/* pin 120 */
+	GPIO_PP1,
+	GPIO_PP2,
+	GPIO_PP3,
+	GPIO_PP4,
+	GPIO_PP5,
+	GPIO_PP6,
+	GPIO_PP7,
+	GPIO_PQ0,	/* pin 128 */
+	GPIO_PQ1,
+	GPIO_PQ2,
+	GPIO_PQ3,
+	GPIO_PQ4,
+	GPIO_PQ5,
+	GPIO_PQ6,
+	GPIO_PQ7,
+	GPIO_PR0,	/* pin 136 */
+	GPIO_PR1,
+	GPIO_PR2,
+	GPIO_PR3,
+	GPIO_PR4,
+	GPIO_PR5,
+	GPIO_PR6,
+	GPIO_PR7,
+	GPIO_PS0,	/* pin 144 */
+	GPIO_PS1,
+	GPIO_PS2,
+	GPIO_PS3,
+	GPIO_PS4,
+	GPIO_PS5,
+	GPIO_PS6,
+	GPIO_PS7,
+	GPIO_PT0,	/* pin 152 */
+	GPIO_PT1,
+	GPIO_PT2,
+	GPIO_PT3,
+	GPIO_PT4,
+	GPIO_PT5,
+	GPIO_PT6,
+	GPIO_PT7,
+	GPIO_PU0,	/* pin 160 */
+	GPIO_PU1,
+	GPIO_PU2,
+	GPIO_PU3,
+	GPIO_PU4,
+	GPIO_PU5,
+	GPIO_PU6,
+	GPIO_PU7,
+	GPIO_PV0,	/* pin 168 */
+	GPIO_PV1,
+	GPIO_PV2,
+	GPIO_PV3,
+	GPIO_PV4,
+	GPIO_PV5,
+	GPIO_PV6,
+	GPIO_PV7,
+	GPIO_PW0,	/* pin 176 */
+	GPIO_PW1,
+	GPIO_PW2,
+	GPIO_PW3,
+	GPIO_PW4,
+	GPIO_PW5,
+	GPIO_PW6,
+	GPIO_PW7,
+	GPIO_PX0,	/* pin 184 */
+	GPIO_PX1,
+	GPIO_PX2,
+	GPIO_PX3,
+	GPIO_PX4,
+	GPIO_PX5,
+	GPIO_PX6,
+	GPIO_PX7,
+	GPIO_PY0,	/* pin 192 */
+	GPIO_PY1,
+	GPIO_PY2,
+	GPIO_PY3,
+	GPIO_PY4,
+	GPIO_PY5,
+	GPIO_PY6,
+	GPIO_PY7,
+	GPIO_PZ0,	/* pin 200 */
+	GPIO_PZ1,
+	GPIO_PZ2,
+	GPIO_PZ3,
+	GPIO_PZ4,
+	GPIO_PZ5,
+	GPIO_PZ6,
+	GPIO_PZ7,
+	GPIO_PAA0,	/* pin 208 */
+	GPIO_PAA1,
+	GPIO_PAA2,
+	GPIO_PAA3,
+	GPIO_PAA4,
+	GPIO_PAA5,
+	GPIO_PAA6,
+	GPIO_PAA7,
+	GPIO_PBB0,	/* pin 216 */
+	GPIO_PBB1,
+	GPIO_PBB2,
+	GPIO_PBB3,
+	GPIO_PBB4,
+	GPIO_PBB5,
+	GPIO_PBB6,
+	GPIO_PBB7,	/* pin 223 */
+};
 
 /*
- * GPIO_PI3 = Port I = 8, bit = 3.
- * Seaboard: used for UART/SPI selection
- * Harmony: not used
+ * Tegra2-specific GPIO API
  */
-#define GPIO_PI3	((8 << 3) | 3)
+
+int gpio_direction_input(int gp);
+int gpio_direction_output(int gp, int value);
+int gpio_get_value(int gp);
+void gpio_set_value(int gp, int value);
 
 #endif	/* TEGRA2_GPIO_H_ */
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index a5fa2b5..1e3ae11 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -31,6 +31,7 @@  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-$(CONFIG_TEGRA2_GPIO)	+= tegra2_gpio.o
 
 COBJS	:= $(COBJS-y)
 SRCS 	:= $(COBJS:.o=.c)
diff --git a/drivers/gpio/tegra2_gpio.c b/drivers/gpio/tegra2_gpio.c
new file mode 100644
index 0000000..9090579
--- /dev/null
+++ b/drivers/gpio/tegra2_gpio.c
@@ -0,0 +1,299 @@ 
+/*
+ * NVIDIA Tegra2 GPIO handling.
+ *  (C) Copyright 2010,2011
+ *  NVIDIA Corporation <www.nvidia.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., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+/*
+ * Based on (mostly copied from) kw_gpio.c based Linux 2.6 kernel driver.
+ * Tom Warren (twarren@nvidia.com)
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <asm/bitops.h>
+#include <asm/arch/tegra2.h>
+#include <asm/arch/gpio.h>
+
+enum {
+	TEGRA2_CMD_INFO,
+	TEGRA2_CMD_PORT,
+	TEGRA2_CMD_OUTPUT,
+	TEGRA2_CMD_INPUT,
+};
+
+/* Config pin 'gp' as GPIO or SFPIO, based on 'type' */
+void __set_config(int gp, int type)
+{
+	struct gpio_ctlr *gpio = (struct gpio_ctlr *)NV_PA_GPIO_BASE;
+	struct gpio_ctlr_bank *bank = &gpio->gpio_bank[GPIO_BANK(gp)];
+	u32 u;
+
+	debug("__set_config: port = %d, bit = %d, %s\n",
+		GPIO_FULLPORT(gp), GPIO_BIT(gp), type ? "GPIO" : "SFPIO");
+
+	u = readl(&bank->gpio_config[GPIO_PORT(gp)]);
+	if (type)				/* GPIO */
+		u |= 1 << GPIO_BIT(gp);
+	else
+		u &= ~(1 << GPIO_BIT(gp));
+	writel(u, &bank->gpio_config[GPIO_PORT(gp)]);
+}
+
+/* Config GPIO pin 'gp' as input or output (OE) as per 'output' */
+void __set_direction(int gp, int output)
+{
+	struct gpio_ctlr *gpio = (struct gpio_ctlr *)NV_PA_GPIO_BASE;
+	struct gpio_ctlr_bank *bank = &gpio->gpio_bank[GPIO_BANK(gp)];
+	u32 u;
+
+	debug("__set_direction: port = %d, bit = %d, %s\n",
+		GPIO_FULLPORT(gp), GPIO_BIT(gp), output ? "OUT" : "IN");
+
+	u = readl(&bank->gpio_dir_out[GPIO_PORT(gp)]);
+	if (output)
+		u |= 1 << GPIO_BIT(gp);
+	else
+		u &= ~(1 << GPIO_BIT(gp));
+	writel(u, &bank->gpio_dir_out[GPIO_PORT(gp)]);
+}
+
+/* set GPIO pin 'gp' output bit as 0 or 1 as per 'high' */
+void __set_level(int gp, int high)
+{
+	struct gpio_ctlr *gpio = (struct gpio_ctlr *)NV_PA_GPIO_BASE;
+	struct gpio_ctlr_bank *bank = &gpio->gpio_bank[GPIO_BANK(gp)];
+	u32 u;
+
+	debug("__set_level: port = %d, bit %d == %d\n",
+		GPIO_FULLPORT(gp), GPIO_BIT(gp), high);
+
+	u = readl(&bank->gpio_out[GPIO_PORT(gp)]);
+	if (high)
+		u |= 1 << GPIO_BIT(gp);
+	else
+		u &= ~(1 << GPIO_BIT(gp));
+	writel(u, &bank->gpio_out[GPIO_PORT(gp)]);
+}
+
+/*
+ * GENERIC_GPIO primitives.
+ */
+
+/* set GPIO pin 'gp' as an input */
+int gpio_direction_input(int gp)
+{
+	debug("gpio_direction_input: pin = %d (port %d:bit %d)\n",
+		gp, GPIO_FULLPORT(gp), GPIO_BIT(gp));
+
+	/* Configure as a GPIO */
+	__set_config(gp, 1);
+
+	/* Configure GPIO direction as input. */
+	__set_direction(gp, 0);
+
+	return 0;
+}
+
+/* set GPIO pin 'gp' as an output, with polarity 'value' */
+int gpio_direction_output(int gp, int value)
+{
+	debug("gpio_direction_output: pin = %d (port %d:bit %d) = %s\n",
+		gp, GPIO_FULLPORT(gp), GPIO_BIT(gp), value ? "HIGH" : "LOW");
+
+	/* Configure as a GPIO */
+	__set_config(gp, 1);
+
+	/* Configure GPIO output value. */
+	__set_level(gp, value);
+
+	/* Configure GPIO direction as output. */
+	__set_direction(gp, 1);
+
+	return 0;
+}
+
+/* read GPIO IN value of pin 'gp' */
+int gpio_get_value(int gp)
+{
+	struct gpio_ctlr *gpio = (struct gpio_ctlr *)NV_PA_GPIO_BASE;
+	struct gpio_ctlr_bank *bank = &gpio->gpio_bank[GPIO_BANK(gp)];
+	int val;
+
+	debug("gpio_get_value: pin = %d (port %d:bit %d)\n",
+		gp, GPIO_FULLPORT(gp), GPIO_BIT(gp));
+
+	val = readl(&bank->gpio_in[GPIO_PORT(gp)]);
+
+	return (val >> GPIO_BIT(gp)) & 1;
+}
+
+/* write GPIO OUT value to pin 'gp' */
+void gpio_set_value(int gp, int value)
+{
+	debug("gpio_set_value: pin = %d (port %d:bit %d), value = %d\n",
+		gp, GPIO_FULLPORT(gp), GPIO_BIT(gp), value);
+
+	/* Configure GPIO output value. */
+	__set_level(gp, value);
+}
+
+#ifdef CONFIG_CMD_TEGRA2_GPIO_INFO
+/*
+ * Display Tegra GPIO information
+ */
+static int gpio_info(int gp)
+{
+	struct gpio_ctlr *gpio = (struct gpio_ctlr *)NV_PA_GPIO_BASE;
+	struct gpio_ctlr_bank *bank = &gpio->gpio_bank[GPIO_BANK(gp)];
+	int i, port;
+	u32 data;
+
+	port = GPIO_FULLPORT(gp);		/* 8-bit port # */
+
+	printf("Tegra2 GPIO port %d:\n\n", port);
+	printf("gpio bits: 76543210\n");
+	printf("-------------------\n");
+
+	if (port < 0 || port > 27)
+		return -1;
+
+	printf("GPIO_CNF:  ");
+	data = readl(&bank->gpio_config[GPIO_PORT(gp)]);
+	for (i = 7; i >= 0; i--)
+		printf("%c", data & (1 << i) ? 'g' : 's');
+	printf("\n");
+
+	printf("GPIO_OE:   ");
+	data = readl(&bank->gpio_dir_out[GPIO_PORT(gp)]);
+	for (i = 7; i >= 0; i--)
+		printf("%c", data & (1 << i) ? 'o' : 'i');
+	printf("\n");
+
+	printf("GPIO_IN:   ");
+	data = readl(&bank->gpio_in[GPIO_PORT(gp)]);
+	for (i = 7; i >= 0; i--)
+		printf("%c", data & (1 << i) ? '1' : '0');
+	printf("\n");
+
+	printf("GPIO_OUT:  ");
+	data = readl(&bank->gpio_out[GPIO_PORT(gp)]);
+	for (i = 7; i >= 0; i--)
+		printf("%c", data & (1 << i) ? '1' : '0');
+	printf("\n");
+
+	return 0;
+}
+#endif /* CONFIG_CMD_TEGRA2_GPIO_INFO */
+
+cmd_tbl_t cmd_gpio[] = {
+	U_BOOT_CMD_MKENT(pin2port, 3, 0, (void *)TEGRA2_CMD_PORT, "", ""),
+	U_BOOT_CMD_MKENT(output, 4, 0, (void *)TEGRA2_CMD_OUTPUT, "", ""),
+	U_BOOT_CMD_MKENT(input, 3, 0, (void *)TEGRA2_CMD_INPUT, "", ""),
+#ifdef CONFIG_CMD_TEGRA2_GPIO_INFO
+	U_BOOT_CMD_MKENT(info, 3, 0, (void *)TEGRA2_CMD_INFO, "", ""),
+#endif
+};
+
+int do_gpio(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	static uint8_t pin;
+	int val, port, bit;
+	ulong ul_arg2, ul_arg3, ul_arg4;
+	cmd_tbl_t *c;
+
+	ul_arg2 = ul_arg3 = ul_arg4 = 0;
+
+	c = find_cmd_tbl(argv[1], cmd_gpio, ARRAY_SIZE(cmd_gpio));
+
+	/* All commands but "port" require 'maxargs' arguments */
+	if (!c || !((argc == (c->maxargs)) ||
+		(((int)c->cmd == TEGRA2_CMD_PORT) &&
+		 (argc == (c->maxargs - 1))))) {
+		cmd_usage(cmdtp);
+		return 1;
+	}
+
+	/* arg2 used as pin */
+	if (argc > 2)
+		ul_arg2 = simple_strtoul(argv[2], NULL, 10);
+
+	/* arg3 used as output value, 0 or 1 */
+	if (argc > 3)
+		ul_arg3 = simple_strtoul(argv[3], NULL, 10) & 0x1;
+
+	switch ((int)c->cmd) {
+#ifdef CONFIG_CMD_TEGRA2_GPIO_INFO
+	case TEGRA2_CMD_INFO:
+		if (argc == 3)
+			pin = (uint8_t)ul_arg2;
+		return gpio_info(pin);
+
+#endif
+	case TEGRA2_CMD_PORT:
+		if (argc == 3)
+			pin = (uint8_t)ul_arg2;
+		port = GPIO_FULLPORT(pin);
+		bit = GPIO_BIT(pin);
+		printf("pin %d = port # %d, bit # %d\n",
+			pin, port, bit);
+		return 0;
+
+	case TEGRA2_CMD_INPUT:
+		/* arg2 = pin */
+		port = GPIO_FULLPORT(pin);
+		bit = GPIO_BIT(pin);
+		gpio_direction_input(ul_arg2);
+		val = gpio_get_value(ul_arg2);
+
+		printf("pin %lu (port:bit %d:%d) = %d\n",
+			ul_arg2, port, bit, val);
+		return 0;
+
+	case TEGRA2_CMD_OUTPUT:
+		/* args = pin, value */
+		port = GPIO_FULLPORT(pin);
+		bit = GPIO_BIT(pin);
+		gpio_direction_output(ul_arg2, ul_arg3);
+		printf("pin %lu (port:bit %d:%d) = %ld\n",
+			ul_arg2, port, bit, ul_arg3);
+		return 0;
+
+	default:
+		/* We should never get here */
+		return 1;
+	}
+}
+
+U_BOOT_CMD(
+	gpio,	5,	1,	do_gpio,
+	"GPIO access",
+	"pin2port pin\n"
+	"	- show GPIO port:bit based on 'pin'\n"
+#ifdef CONFIG_CMD_TEGRA2_GPIO_INFO
+	"     info pin\n"
+	"	- display info for all bits in port based on 'pin'\n"
+#endif
+	"     output pin 0|1\n"
+	"	- using 'pin', set port:bit as output and drive low or high\n"
+	"     input pin\n"
+	"	- using 'pin', set port:bit as input and read value"
+);