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

login
register
mail settings
Submitter Tom Warren
Date June 16, 2011, 11:51 p.m.
Message ID <1308268294-14901-2-git-send-email-twarren@nvidia.com>
Download mbox | patch
Permalink /patch/100743/
State Superseded
Headers show

Comments

Tom Warren - June 16, 2011, 11:51 p.m.
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
Changes in V3:
        - use common cmd_gpio; remove redundant cmd processing code
        - add gpio_request, gpio_free and gpio_toggle
        - alias 'gpio_status' to 'gpio_info' for use in cmd_gpio
Changes in V4:
	- update code as per Mike Frysinger's review comments
	- rewrite gpio_info, aka gpio_status

 arch/arm/include/asm/arch-tegra2/gpio.h |  250 +++++++++++++++++++++++++++++--
 arch/arm/include/asm/gpio.h             |   38 +++++
 drivers/gpio/Makefile                   |    1 +
 drivers/gpio/tegra2_gpio.c              |  254 +++++++++++++++++++++++++++++++
 4 files changed, 533 insertions(+), 10 deletions(-)
 create mode 100644 arch/arm/include/asm/gpio.h
 create mode 100644 drivers/gpio/tegra2_gpio.c
Mike Frysinger - June 17, 2011, 12:06 a.m.
On Thursday, June 16, 2011 19:51:33 Tom Warren wrote:
> +int gpio_request(int gp, const char *label)
> +{
> +	if (gp >= MAX_NUM_GPIOS)
> +		return -1;
> +
> +	strncpy(gpio_names[gp].name, label, GPIO_NAME_SIZE);

if the label is >=GPIO_NAME_SIZE, the result will not be NUL terminated.  i'd 
suggest you add something like this to after the strncpy call:
	gpio_names[gp].name[GPIO_NAME_SIZE - 1] = '\0';

> +	return (val >> GPIO_BIT(gp)) & 1;

could be written as !!(val >> GPIO_BIT(gp)), but up to you, or see how the 
code size works out

otherwise, looks fine.  not that i looked too closely as i dont know anything 
about Tegra2 parts.
-mike
Tom Warren - June 17, 2011, 12:09 a.m.
Mike,

On Thu, Jun 16, 2011 at 5:06 PM, Mike Frysinger <vapier@gentoo.org> wrote:
> On Thursday, June 16, 2011 19:51:33 Tom Warren wrote:
>> +int gpio_request(int gp, const char *label)
>> +{
>> +     if (gp >= MAX_NUM_GPIOS)
>> +             return -1;
>> +
>> +     strncpy(gpio_names[gp].name, label, GPIO_NAME_SIZE);
>
> if the label is >=GPIO_NAME_SIZE, the result will not be NUL terminated.  i'd
> suggest you add something like this to after the strncpy call:
>        gpio_names[gp].name[GPIO_NAME_SIZE - 1] = '\0';
Will do. Thanks for the quick response!

>
>> +     return (val >> GPIO_BIT(gp)) & 1;
>
> could be written as !!(val >> GPIO_BIT(gp)), but up to you, or see how the
> code size works out
I'll check it and update if your way is smaller. Thanks.

>
> otherwise, looks fine.  not that i looked too closely as i dont know anything
> about Tegra2 parts.
> -mike
>
Thanks,

Tom

Patch

diff --git a/arch/arm/include/asm/arch-tegra2/gpio.h b/arch/arm/include/asm/arch-tegra2/gpio.h
index 0fb8f0d..41e66fe 100644
--- a/arch/arm/include/asm/arch-tegra2/gpio.h
+++ b/arch/arm/include/asm/arch-tegra2/gpio.h
@@ -23,11 +23,13 @@ 
 #define _TEGRA2_GPIO_H_
 
 /*
- * The Tegra 2x GPIO controller has 222 GPIOs arranged in 8 banks of 4 ports,
+ * The Tegra 2x GPIO controller has 224 GPIOs arranged in 7 banks of 4 ports,
  * each with 8 GPIOs.
  */
-#define TEGRA_GPIO_PORTS 4   /* The number of ports per bank */
-#define TEGRA_GPIO_BANKS 8   /* The number of banks */
+#define TEGRA_GPIO_PORTS	4	/* number of ports per bank */
+#define TEGRA_GPIO_BANKS	7	/* number of banks */
+#define MAX_NUM_GPIOS		(TEGRA_GPIO_PORTS * TEGRA_GPIO_BANKS * 8)
+#define GPIO_NAME_SIZE		20	/* gpio_request max label len */
 
 /* GPIO Controller registers for a single bank */
 struct gpio_ctlr_bank {
@@ -45,15 +47,243 @@  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)
 
+void gpio_info(void);
+
+#define gpio_status()	gpio_info()
 #endif	/* TEGRA2_GPIO_H_ */
diff --git a/arch/arm/include/asm/gpio.h b/arch/arm/include/asm/gpio.h
new file mode 100644
index 0000000..eb071d1
--- /dev/null
+++ b/arch/arm/include/asm/gpio.h
@@ -0,0 +1,38 @@ 
+/*
+ * Copyright (c) 2011, NVIDIA Corp. All rights reserved.
+ * 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
+ */
+
+#ifndef _GPIO_H_
+#define _GPIO_H_
+
+#include <asm/arch/gpio.h>
+/*
+ * Generic GPIO API
+ */
+
+int gpio_request(int gp, const char *label);
+void gpio_free(int gp);
+void gpio_toggle_value(int gp);
+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	/* _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..2cf5737
--- /dev/null
+++ b/drivers/gpio/tegra2_gpio.c
@@ -0,0 +1,254 @@ 
+/*
+ * 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/gpio.h>
+
+enum {
+	TEGRA2_CMD_INFO,
+	TEGRA2_CMD_PORT,
+	TEGRA2_CMD_OUTPUT,
+	TEGRA2_CMD_INPUT,
+};
+
+static struct gpio_names {
+	char name[GPIO_NAME_SIZE];
+} gpio_names[MAX_NUM_GPIOS];
+
+static char *get_name(int i)
+{
+	return *gpio_names[i].name ? gpio_names[i].name : "UNKNOWN";
+}
+
+/* Return config of pin 'gp' as GPIO (1) or SFPIO (0) */
+static int get_config(int gp)
+{
+	struct gpio_ctlr *gpio = (struct gpio_ctlr *)NV_PA_GPIO_BASE;
+	struct gpio_ctlr_bank *bank = &gpio->gpio_bank[GPIO_BANK(gp)];
+	u32 u;
+	int type;
+
+	u = readl(&bank->gpio_config[GPIO_PORT(gp)]);
+	type =  (u >> GPIO_BIT(gp)) & 1;
+
+	debug("get_config: port = %d, bit = %d is %s\n",
+		GPIO_FULLPORT(gp), GPIO_BIT(gp), type ? "GPIO" : "SFPIO");
+
+	return type;
+}
+
+/* Config pin 'gp' as GPIO or SFPIO, based on 'type' */
+static 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)]);
+}
+
+/* Return GPIO pin 'gp' direction - 0 = input or 1 = output */
+static int get_direction(int gp)
+{
+	struct gpio_ctlr *gpio = (struct gpio_ctlr *)NV_PA_GPIO_BASE;
+	struct gpio_ctlr_bank *bank = &gpio->gpio_bank[GPIO_BANK(gp)];
+	u32 u;
+	int dir;
+
+	u = readl(&bank->gpio_dir_out[GPIO_PORT(gp)]);
+	dir =  (u >> GPIO_BIT(gp)) & 1;
+
+	debug("get_direction: port = %d, bit = %d, %s\n",
+		GPIO_FULLPORT(gp), GPIO_BIT(gp), dir ? "OUT" : "IN");
+
+	return dir;
+}
+
+/* Config GPIO pin 'gp' as input or output (OE) as per 'output' */
+static 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' */
+static 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.
+ */
+
+int gpio_request(int gp, const char *label)
+{
+	if (gp >= MAX_NUM_GPIOS)
+		return -1;
+
+	strncpy(gpio_names[gp].name, label, GPIO_NAME_SIZE);
+
+	/* Configure as a GPIO */
+	set_config(gp, 1);
+
+	return 0;
+}
+
+void gpio_free(int gp)
+{
+}
+
+/* read GPIO OUT value of pin 'gp' */
+static int gpio_get_output_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_output_value: pin = %d (port %d:bit %d)\n",
+		gp, GPIO_FULLPORT(gp), GPIO_BIT(gp));
+
+	val = readl(&bank->gpio_out[GPIO_PORT(gp)]);
+
+	return (val >> GPIO_BIT(gp)) & 1;
+}
+
+void gpio_toggle_value(int gp)
+{
+	gpio_set_value(gp, !gpio_get_output_value(gp));
+}
+
+/* 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 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 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);
+}
+
+/*
+ * Display Tegra GPIO information
+ */
+void gpio_info(void)
+{
+	int c, type;
+
+	for (c = 0; c < MAX_NUM_GPIOS; c++) {
+		type = get_config(c);		/* GPIO, not SFPIO */
+		if (type) {
+			printf("GPIO_%d:\t%s is an %s, ", c,
+				get_name(c),
+				get_direction(c) ? "OUTPUT" : "INPUT");
+			if (get_direction(c))
+				printf("value = %d", gpio_get_output_value(c));
+			else
+				printf("value = %d", gpio_get_value(c));
+			printf("\n");
+		} else
+			continue;
+	}
+}