From 4e5ae09e56a205685824ebebc91fc43c8b0b973a Mon Sep 17 00:00:00 2001 From: Tom Warren Date: Fri, 17 Jun 2011 06:27:28 +0000 Subject: [PATCH] GPIO: Tegra2: add GPIO driver for Tegra2 Signed-off-by: Tom Warren --- 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 | 255 ++++++++++++++++++++++++ 4 files changed, 534 insertions(+), 10 deletions(-) create mode 100644 arch/arm/include/asm/gpio.h create mode 100644 drivers/gpio/tegra2_gpio.c diff --git a/arch/arm/include/asm/arch-tegra2/gpio.h b/arch/arm/include/asm/arch-tegra2/gpio.h index 0fb8f0d40f..41e66fe1b1 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 0000000000..eb071d1824 --- /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 +/* + * 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 a5fa2b5d85..1e3ae11347 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 0000000000..f686e80637 --- /dev/null +++ b/drivers/gpio/tegra2_gpio.c @@ -0,0 +1,255 @@ +/* + * NVIDIA Tegra2 GPIO handling. + * (C) Copyright 2010,2011 + * NVIDIA Corporation + * + * 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 +#include +#include +#include +#include + +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); + gpio_names[gp].name[GPIO_NAME_SIZE - 1] = '\0'; + + /* 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; + } +} -- 2.39.5