This is an implementation of GPIOs for Tegra that uses driver model. It has
been tested on trimslice and also using the new iotrace feature.
The implementation uses a top-level GPIO device (which has no actual GPIOS).
Under this all the banks are created as separate GPIO devices.
The GPIOs are named as per the Tegra datasheet/header files: A0..A7, B0..B7,
..., Z0..Z7, AA0..AA7, etc.
Since driver model is not yet available before relocation, or in SPL, a
special function is provided for seaboard's SPL code.
Signed-off-by: Simon Glass s...@chromium.org
---
Changes in v7:
- Set up pin config in tegra_spl_gpio_direction_output()
Changes in v6:
- Use base_gpio instead of base_port for simplicity
- Add an implementation of the get_function() method
Changes in v5: None
Changes in v4:
- Rename struct device to struct udevice
Changes in v3:
- Move dm command enable to previous patch
- Use gpio number for the internal helper functions
Changes in v2:
- Split out driver model changes into separate patches
- Correct bugs found during testing
arch/arm/include/asm/arch-tegra/gpio.h | 15 +-
board/nvidia/seaboard/seaboard.c | 2 +-
drivers/gpio/tegra_gpio.c | 327 +++--
include/configs/tegra-common.h | 1 +
4 files changed, 283 insertions(+), 62 deletions(-)
diff --git a/arch/arm/include/asm/arch-tegra/gpio.h
b/arch/arm/include/asm/arch-tegra/gpio.h
index 44cd455..7334e0c 100644
--- a/arch/arm/include/asm/arch-tegra/gpio.h
+++ b/arch/arm/include/asm/arch-tegra/gpio.h
@@ -6,6 +6,8 @@
#ifndef _TEGRA_GPIO_H_
#define _TEGRA_GPIO_H_
+#define TEGRA_GPIOS_PER_PORT 8
+#define TEGRA_PORTS_PER_BANK 4
#define MAX_NUM_GPIOS (TEGRA_GPIO_PORTS * TEGRA_GPIO_BANKS * 8)
#define GPIO_NAME_SIZE 20 /* gpio_request max label len */
@@ -25,9 +27,14 @@ struct tegra_gpio_config {
u32 init:2;
};
-/*
- * Tegra-specific GPIO API
+/**
+ * tegra_spl_gpio_direction_output() - set the output value of a GPIO
+ *
+ * This function is only used from SPL on seaboard, which needs to enable a
+ * GPIO to get the UART running. It could be done in U-Boot rather than SPL,
+ * but for now, this gets it working
*/
+int tegra_spl_gpio_direction_output(int gpio, int value);
/**
* Configure a list of GPIOs
@@ -37,8 +44,4 @@ struct tegra_gpio_config {
*/
void gpio_config_table(const struct tegra_gpio_config *config, int len);
-void gpio_info(void);
-
-#define gpio_status() gpio_info()
-
#endif /* TEGRA_GPIO_H_ */
diff --git a/board/nvidia/seaboard/seaboard.c b/board/nvidia/seaboard/seaboard.c
index ce2db40..6a243f0 100644
--- a/board/nvidia/seaboard/seaboard.c
+++ b/board/nvidia/seaboard/seaboard.c
@@ -22,7 +22,7 @@ void gpio_early_init_uart(void)
#ifndef CONFIG_SPL_BUILD
gpio_request(GPIO_PI3, NULL);
#endif
- gpio_direction_output(GPIO_PI3, 0);
+ tegra_spl_gpio_direction_output(GPIO_PI3, 0);
}
#endif
diff --git a/drivers/gpio/tegra_gpio.c b/drivers/gpio/tegra_gpio.c
index fea9d17..1cc4abb 100644
--- a/drivers/gpio/tegra_gpio.c
+++ b/drivers/gpio/tegra_gpio.c
@@ -12,10 +12,17 @@
*/
#include common.h
+#include dm.h
+#include malloc.h
+#include errno.h
+#include fdtdec.h
#include asm/io.h
#include asm/bitops.h
#include asm/arch/tegra.h
#include asm/gpio.h
+#include dm/device-internal.h
+
+DECLARE_GLOBAL_DATA_PTR;
enum {
TEGRA_CMD_INFO,
@@ -24,14 +31,18 @@ enum {
TEGRA_CMD_INPUT,
};
-static struct gpio_names {
- char name[GPIO_NAME_SIZE];
-} gpio_names[MAX_NUM_GPIOS];
+struct tegra_gpio_platdata {
+ struct gpio_ctlr_bank *bank;
+ const char *port_name; /* Name of port, e.g. B */
+ int base_gpio; /* Port number for this port (0, 1,.., n-1) */
+};
-static char *get_name(int i)
-{
- return *gpio_names[i].name ? gpio_names[i].name : UNKNOWN;
-}
+/* Information about each port at run-time */
+struct tegra_port_info {
+ char label[TEGRA_GPIOS_PER_PORT][GPIO_NAME_SIZE];
+ struct gpio_ctlr_bank *bank;
+ int base_gpio; /* Port number for this port (0, 1,.., n-1) */
+};
/* Return config of pin 'gpio' as GPIO (1) or SFPIO (0) */
static int get_config(unsigned gpio)
@@ -121,38 +132,72 @@ static void set_level(unsigned gpio, int high)
writel(u, bank-gpio_out[GPIO_PORT(gpio)]);
}
+static int check_reserved(struct udevice *dev, unsigned offset,
+ const char *func)
+{
+ struct tegra_port_info *state = dev_get_priv(dev);
+ struct gpio_dev_priv *uc_priv = dev-uclass_priv;
+
+ if (!*state-label[offset]) {
+ printf(tegra_gpio: %s: error: gpio %s%d not reserved\n,
+ func, uc_priv-bank_name, offset);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+/* set GPIO pin 'gpio' as an output, with polarity 'value' */
+int tegra_spl_gpio_direction_output(int gpio, int value)
+{
+ /* Configure as a GPIO */
+ set_config(gpio,