mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-11 13:56:30 +00:00
eceb3f26f4
The HW-defined procedure for booting Tegra requires that some pins be set up as GPIOs immediately at boot in order to avoid glitches on those pins, when the pinmux is programmed. Add a feature to the GPIO driver which executes a GPIO configuration table. Board files will use this to implement the correct HW initialization procedure. Signed-off-by: Stephen Warren <swarren@nvidia.com> Signed-off-by: Tom Warren <twarren@nvidia.com>
266 lines
6.2 KiB
C
266 lines
6.2 KiB
C
/*
|
|
* NVIDIA Tegra20 GPIO handling.
|
|
* (C) Copyright 2010-2012
|
|
* NVIDIA Corporation <www.nvidia.com>
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0+
|
|
*/
|
|
|
|
/*
|
|
* 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/tegra.h>
|
|
#include <asm/gpio.h>
|
|
|
|
enum {
|
|
TEGRA_CMD_INFO,
|
|
TEGRA_CMD_PORT,
|
|
TEGRA_CMD_OUTPUT,
|
|
TEGRA_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 'gpio' as GPIO (1) or SFPIO (0) */
|
|
static int get_config(unsigned gpio)
|
|
{
|
|
struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE;
|
|
struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)];
|
|
u32 u;
|
|
int type;
|
|
|
|
u = readl(&bank->gpio_config[GPIO_PORT(gpio)]);
|
|
type = (u >> GPIO_BIT(gpio)) & 1;
|
|
|
|
debug("get_config: port = %d, bit = %d is %s\n",
|
|
GPIO_FULLPORT(gpio), GPIO_BIT(gpio), type ? "GPIO" : "SFPIO");
|
|
|
|
return type;
|
|
}
|
|
|
|
/* Config pin 'gpio' as GPIO or SFPIO, based on 'type' */
|
|
static void set_config(unsigned gpio, int type)
|
|
{
|
|
struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE;
|
|
struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)];
|
|
u32 u;
|
|
|
|
debug("set_config: port = %d, bit = %d, %s\n",
|
|
GPIO_FULLPORT(gpio), GPIO_BIT(gpio), type ? "GPIO" : "SFPIO");
|
|
|
|
u = readl(&bank->gpio_config[GPIO_PORT(gpio)]);
|
|
if (type) /* GPIO */
|
|
u |= 1 << GPIO_BIT(gpio);
|
|
else
|
|
u &= ~(1 << GPIO_BIT(gpio));
|
|
writel(u, &bank->gpio_config[GPIO_PORT(gpio)]);
|
|
}
|
|
|
|
/* Return GPIO pin 'gpio' direction - 0 = input or 1 = output */
|
|
static int get_direction(unsigned gpio)
|
|
{
|
|
struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE;
|
|
struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)];
|
|
u32 u;
|
|
int dir;
|
|
|
|
u = readl(&bank->gpio_dir_out[GPIO_PORT(gpio)]);
|
|
dir = (u >> GPIO_BIT(gpio)) & 1;
|
|
|
|
debug("get_direction: port = %d, bit = %d, %s\n",
|
|
GPIO_FULLPORT(gpio), GPIO_BIT(gpio), dir ? "OUT" : "IN");
|
|
|
|
return dir;
|
|
}
|
|
|
|
/* Config GPIO pin 'gpio' as input or output (OE) as per 'output' */
|
|
static void set_direction(unsigned gpio, int output)
|
|
{
|
|
struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE;
|
|
struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)];
|
|
u32 u;
|
|
|
|
debug("set_direction: port = %d, bit = %d, %s\n",
|
|
GPIO_FULLPORT(gpio), GPIO_BIT(gpio), output ? "OUT" : "IN");
|
|
|
|
u = readl(&bank->gpio_dir_out[GPIO_PORT(gpio)]);
|
|
if (output)
|
|
u |= 1 << GPIO_BIT(gpio);
|
|
else
|
|
u &= ~(1 << GPIO_BIT(gpio));
|
|
writel(u, &bank->gpio_dir_out[GPIO_PORT(gpio)]);
|
|
}
|
|
|
|
/* set GPIO pin 'gpio' output bit as 0 or 1 as per 'high' */
|
|
static void set_level(unsigned gpio, int high)
|
|
{
|
|
struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE;
|
|
struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)];
|
|
u32 u;
|
|
|
|
debug("set_level: port = %d, bit %d == %d\n",
|
|
GPIO_FULLPORT(gpio), GPIO_BIT(gpio), high);
|
|
|
|
u = readl(&bank->gpio_out[GPIO_PORT(gpio)]);
|
|
if (high)
|
|
u |= 1 << GPIO_BIT(gpio);
|
|
else
|
|
u &= ~(1 << GPIO_BIT(gpio));
|
|
writel(u, &bank->gpio_out[GPIO_PORT(gpio)]);
|
|
}
|
|
|
|
/*
|
|
* Generic_GPIO primitives.
|
|
*/
|
|
|
|
int gpio_request(unsigned gpio, const char *label)
|
|
{
|
|
if (gpio >= MAX_NUM_GPIOS)
|
|
return -1;
|
|
|
|
if (label != NULL) {
|
|
strncpy(gpio_names[gpio].name, label, GPIO_NAME_SIZE);
|
|
gpio_names[gpio].name[GPIO_NAME_SIZE - 1] = '\0';
|
|
}
|
|
|
|
/* Configure as a GPIO */
|
|
set_config(gpio, 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int gpio_free(unsigned gpio)
|
|
{
|
|
if (gpio >= MAX_NUM_GPIOS)
|
|
return -1;
|
|
|
|
gpio_names[gpio].name[0] = '\0';
|
|
/* Do not configure as input or change pin mux here */
|
|
return 0;
|
|
}
|
|
|
|
/* read GPIO OUT value of pin 'gpio' */
|
|
static int gpio_get_output_value(unsigned gpio)
|
|
{
|
|
struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE;
|
|
struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)];
|
|
int val;
|
|
|
|
debug("gpio_get_output_value: pin = %d (port %d:bit %d)\n",
|
|
gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio));
|
|
|
|
val = readl(&bank->gpio_out[GPIO_PORT(gpio)]);
|
|
|
|
return (val >> GPIO_BIT(gpio)) & 1;
|
|
}
|
|
|
|
/* set GPIO pin 'gpio' as an input */
|
|
int gpio_direction_input(unsigned gpio)
|
|
{
|
|
debug("gpio_direction_input: pin = %d (port %d:bit %d)\n",
|
|
gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio));
|
|
|
|
/* Configure GPIO direction as input. */
|
|
set_direction(gpio, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* set GPIO pin 'gpio' as an output, with polarity 'value' */
|
|
int gpio_direction_output(unsigned gpio, int value)
|
|
{
|
|
debug("gpio_direction_output: pin = %d (port %d:bit %d) = %s\n",
|
|
gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio),
|
|
value ? "HIGH" : "LOW");
|
|
|
|
/* Configure GPIO output value. */
|
|
set_level(gpio, value);
|
|
|
|
/* Configure GPIO direction as output. */
|
|
set_direction(gpio, 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* read GPIO IN value of pin 'gpio' */
|
|
int gpio_get_value(unsigned gpio)
|
|
{
|
|
struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE;
|
|
struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)];
|
|
int val;
|
|
|
|
debug("gpio_get_value: pin = %d (port %d:bit %d)\n",
|
|
gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio));
|
|
|
|
val = readl(&bank->gpio_in[GPIO_PORT(gpio)]);
|
|
|
|
return (val >> GPIO_BIT(gpio)) & 1;
|
|
}
|
|
|
|
/* write GPIO OUT value to pin 'gpio' */
|
|
int gpio_set_value(unsigned gpio, int value)
|
|
{
|
|
debug("gpio_set_value: pin = %d (port %d:bit %d), value = %d\n",
|
|
gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio), value);
|
|
|
|
/* Configure GPIO output value. */
|
|
set_level(gpio, value);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void gpio_config_table(const struct tegra_gpio_config *config, int len)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
switch (config[i].init) {
|
|
case TEGRA_GPIO_INIT_IN:
|
|
gpio_direction_input(config[i].gpio);
|
|
break;
|
|
case TEGRA_GPIO_INIT_OUT0:
|
|
gpio_direction_output(config[i].gpio, 0);
|
|
break;
|
|
case TEGRA_GPIO_INIT_OUT1:
|
|
gpio_direction_output(config[i].gpio, 1);
|
|
break;
|
|
}
|
|
set_config(config[i].gpio, 1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Display Tegra GPIO information
|
|
*/
|
|
void gpio_info(void)
|
|
{
|
|
unsigned c;
|
|
int 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;
|
|
}
|
|
}
|