diff options
author | Gabe Black <gabeblack@google.com> | 2013-10-03 04:35:01 -0700 |
---|---|---|
committer | Isaac Christensen <isaac.christensen@se-eng.com> | 2014-08-19 22:46:52 +0200 |
commit | 08d5a89fd0a64740b81d1319551ac580068d835d (patch) | |
tree | 0d79f67693b6672f75d216c4fa2b1b57b4848f3a /src/soc/nvidia/tegra | |
parent | f40785c0c26c481fa19345dd5467045cf52876c5 (diff) | |
download | coreboot-08d5a89fd0a64740b81d1319551ac580068d835d.tar.xz |
tegra124: Implement driver code for the pinmux, pingroup controls, and GPIOs.
The pins on tegra are controlled by three different units, the pinmux, the
pin group controls, and the GPIO banks. Each of these units controls some
aspect of the pins, and they layer together and interact in interesting ways.
By default, the GPIOs are configured to pass through the special purpose IO
that the pinmux is configured to and so can be ignored unless a GPIO is needed.
The pinmux controls which special purpose signal passes through, along with
pull ups, downs, and whether the output is tristated. The pingroup controls
change the parameters of a group of pins which all have to do with a related
function unit.
The enum which holds constants related to the pinmux is relatively involved
and may not be entirely complete or correct due to slightly inconsistent,
incomplete, or missing documentation related to the pinmux. Considerable
effort has been made to make it as accurate as possible. It includes a
constant which is the index into the pinmux control registers for that pin,
what each of the functions supported by that pin are, and which GPIO it
corresponds to. The GPIO constant is named after the GPIO and is the pinmux
register index for the pin for that GPIO. That way, when you need to turn on
a GPIO, you can use that constant along with the pinmux manipulating functions
to enable its tristate and pull up/down mode in addition to setting up the
GPIO controls.
Also, while in general I prefer not to use macros or the preprocessor when
writing C code, in this case the set of constants in the enums was too large
and cumbersome to manage without them. Since they're being used to construct
a table in a straightforward way, hopefully their negative aspects will be
minimized.
In addition to the low level functions in each driver, the GPIO code also
includes some high level functions to set up input or output GPIOs since that
will probably be a very common thing to want to do.
Old-Change-Id: I48efa58d1b5520c0367043cef76b6d3a7a18530d
Signed-off-by: Gabe Black <gabeblack@google.com>
Reviewed-on: https://chromium-review.googlesource.com/171806
Reviewed-by: Ronald Minnich <rminnich@chromium.org>
Reviewed-by: David Hendricks <dhendrix@chromium.org>
Commit-Queue: Gabe Black <gabeblack@chromium.org>
Tested-by: Gabe Black <gabeblack@chromium.org>
(cherry picked from commit 5cd9f17fe0196d13c1e10b8cde0f2d3989b5ae1a)
tegra124: Add base address for the pinmux and pingroup registers.
There weren't any constants for the pinmux or pingroup registers in the
address map header.
Old-Change-Id: I52b9042c7506cab0bedd7a734f346cc9fe4ac3fe
Signed-off-by: Gabe Black <gabeblack@google.com>
Reviewed-on: https://chromium-review.googlesource.com/172081
Reviewed-by: Julius Werner <jwerner@chromium.org>
Tested-by: Gabe Black <gabeblack@chromium.org>
Commit-Queue: Gabe Black <gabeblack@chromium.org>
(cherry picked from commit 79b61016bfd702b0ea5221658305d8bd359f4f62)
Squashed two related commits.
Change-Id: Ifeb6085128bd53f0ef5f82c930eda66a2b59499b
Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com>
Reviewed-on: http://review.coreboot.org/6702
Tested-by: build bot (Jenkins)
Diffstat (limited to 'src/soc/nvidia/tegra')
-rw-r--r-- | src/soc/nvidia/tegra/gpio.c | 240 | ||||
-rw-r--r-- | src/soc/nvidia/tegra/gpio.h | 63 | ||||
-rw-r--r-- | src/soc/nvidia/tegra/pingroup.c | 35 | ||||
-rw-r--r-- | src/soc/nvidia/tegra/pingroup.h | 43 | ||||
-rw-r--r-- | src/soc/nvidia/tegra/pinmux.c | 35 | ||||
-rw-r--r-- | src/soc/nvidia/tegra/pinmux.h | 44 |
6 files changed, 460 insertions, 0 deletions
diff --git a/src/soc/nvidia/tegra/gpio.c b/src/soc/nvidia/tegra/gpio.c new file mode 100644 index 0000000000..d4b5bddd49 --- /dev/null +++ b/src/soc/nvidia/tegra/gpio.c @@ -0,0 +1,240 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * + * 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; version 2 of the License. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <arch/io.h> +#include <console/console.h> +#include <soc/addressmap.h> +#include <stddef.h> +#include <stdint.h> + +#include "gpio.h" +#include "pinmux.h" + +static void gpio_input_common(int gpio_index, int pinmux_index, + uint32_t pconfig) +{ + pconfig |= PINMUX_INPUT_ENABLE; + gpio_set_int_enable(gpio_index, 0); + gpio_set_mode(gpio_index, GPIO_MODE_GPIO); + gpio_set_out_enable(gpio_index, 0); + pinmux_set_config(pinmux_index, pconfig); +} + +void gpio_input(int gpio_index, int pinmux_index) +{ + gpio_input_common(gpio_index, pinmux_index, PINMUX_PULL_NONE); +} + +void gpio_input_pullup(int gpio_index, int pinmux_index) +{ + gpio_input_common(gpio_index, pinmux_index, PINMUX_PULL_UP); +} + +void gpio_input_pulldown(int gpio_index, int pinmux_index) +{ + gpio_input_common(gpio_index, pinmux_index, PINMUX_PULL_DOWN); +} + +void gpio_output(int gpio_index, int pinmux_index, int value) +{ + uint32_t pconfig = PINMUX_PULL_NONE; + + pinmux_set_config(pinmux_index, pconfig | PINMUX_TRISTATE); + gpio_set_int_enable(gpio_index, 0); + gpio_set_mode(gpio_index, GPIO_MODE_GPIO); + gpio_set_out_enable(gpio_index, 1); + gpio_set_out_value(gpio_index, value); + pinmux_set_config(pinmux_index, pconfig); +} + +enum { + GPIO_GPIOS_PER_PORT = 8, + GPIO_PORTS_PER_BANK = 4, + GPIO_BANKS = 8, + + GPIO_GPIOS_PER_BANK = GPIO_GPIOS_PER_PORT * GPIO_PORTS_PER_BANK, + GPIO_GPIOS = GPIO_BANKS * GPIO_GPIOS_PER_BANK +}; + +struct gpio_bank { + // Values + uint32_t config[GPIO_PORTS_PER_BANK]; + uint32_t out_enable[GPIO_PORTS_PER_BANK]; + uint32_t out_value[GPIO_PORTS_PER_BANK]; + uint32_t in_value[GPIO_PORTS_PER_BANK]; + uint32_t int_status[GPIO_PORTS_PER_BANK]; + uint32_t int_enable[GPIO_PORTS_PER_BANK]; + uint32_t int_level[GPIO_PORTS_PER_BANK]; + uint32_t int_clear[GPIO_PORTS_PER_BANK]; + + // Masks + uint32_t config_mask[GPIO_PORTS_PER_BANK]; + uint32_t out_enable_mask[GPIO_PORTS_PER_BANK]; + uint32_t out_value_mask[GPIO_PORTS_PER_BANK]; + uint32_t in_value_mask[GPIO_PORTS_PER_BANK]; + uint32_t int_status_mask[GPIO_PORTS_PER_BANK]; + uint32_t int_enable_mask[GPIO_PORTS_PER_BANK]; + uint32_t int_level_mask[GPIO_PORTS_PER_BANK]; + uint32_t int_clear_mask[GPIO_PORTS_PER_BANK]; +}; + +static const struct gpio_bank *gpio_banks = (void *)TEGRA_GPIO_BASE; + +static uint32_t gpio_read_port(int index, size_t offset) +{ + int bank = index / GPIO_GPIOS_PER_BANK; + int port = (index - bank * GPIO_GPIOS_PER_BANK) / GPIO_GPIOS_PER_PORT; + + return read32((uint8_t *)&gpio_banks[bank] + offset + + port * sizeof(uint32_t)); +} + +static void gpio_write_port(int index, size_t offset, + uint32_t mask, uint32_t value) +{ + int bank = index / GPIO_GPIOS_PER_BANK; + int port = (index - bank * GPIO_GPIOS_PER_BANK) / GPIO_GPIOS_PER_PORT; + + uint32_t reg = read32((uint8_t *)&gpio_banks[bank] + offset + + port * sizeof(uint32_t)); + uint32_t new_reg = (reg & ~mask) | (value & mask); + + if (new_reg != reg) { + write32(new_reg, (uint8_t *)&gpio_banks[bank] + offset + + port * sizeof(uint32_t)); + } +} + +void gpio_set_mode(int gpio_index, enum gpio_mode mode) +{ + int bit = gpio_index % GPIO_GPIOS_PER_PORT; + gpio_write_port(gpio_index, offsetof(struct gpio_bank, config), + 1 << bit, mode ? (1 << bit) : 0); +} + +int gpio_get_mode(int gpio_index) +{ + int bit = gpio_index % GPIO_GPIOS_PER_PORT; + uint32_t port = gpio_read_port(gpio_index, + offsetof(struct gpio_bank, config)); + return (port & (1 << bit)) != 0; +} + +void gpio_set_lock(int gpio_index) +{ + int bit = gpio_index % GPIO_GPIOS_PER_PORT + GPIO_GPIOS_PER_PORT; + gpio_write_port(gpio_index, offsetof(struct gpio_bank, config), + 1 << bit, 1 << bit); +} + +int gpio_get_lock(int gpio_index) +{ + int bit = gpio_index % GPIO_GPIOS_PER_PORT + GPIO_GPIOS_PER_PORT; + uint32_t port = gpio_read_port(gpio_index, + offsetof(struct gpio_bank, config)); + return (port & (1 << bit)) != 0; +} + +void gpio_set_out_enable(int gpio_index, int enable) +{ + int bit = gpio_index % GPIO_GPIOS_PER_PORT; + gpio_write_port(gpio_index, offsetof(struct gpio_bank, out_enable), + 1 << bit, enable ? (1 << bit) : 0); +} + +int gpio_get_out_enable(int gpio_index) +{ + int bit = gpio_index % GPIO_GPIOS_PER_PORT; + uint32_t port = gpio_read_port(gpio_index, + offsetof(struct gpio_bank, out_enable)); + return (port & (1 << bit)) != 0; +} + +void gpio_set_out_value(int gpio_index, int value) +{ + int bit = gpio_index % GPIO_GPIOS_PER_PORT; + gpio_write_port(gpio_index, offsetof(struct gpio_bank, out_value), + 1 << bit, value ? (1 << bit) : 0); +} + +int gpio_get_out_value(int gpio_index) +{ + int bit = gpio_index % GPIO_GPIOS_PER_PORT; + uint32_t port = gpio_read_port(gpio_index, + offsetof(struct gpio_bank, out_value)); + return (port & (1 << bit)) != 0; +} + +int gpio_get_in_value(int gpio_index) +{ + int bit = gpio_index % GPIO_GPIOS_PER_PORT; + uint32_t port = gpio_read_port(gpio_index, + offsetof(struct gpio_bank, in_value)); + return (port & (1 << bit)) != 0; +} + +int gpio_get_int_status(int gpio_index) +{ + int bit = gpio_index % GPIO_GPIOS_PER_PORT; + uint32_t port = gpio_read_port(gpio_index, + offsetof(struct gpio_bank, int_status)); + return (port & (1 << bit)) != 0; +} + +void gpio_set_int_enable(int gpio_index, int enable) +{ + int bit = gpio_index % GPIO_GPIOS_PER_PORT; + gpio_write_port(gpio_index, offsetof(struct gpio_bank, int_enable), + 1 << bit, enable ? (1 << bit) : 0); +} + +int gpio_get_int_enable(int gpio_index) +{ + int bit = gpio_index % GPIO_GPIOS_PER_PORT; + uint32_t port = gpio_read_port(gpio_index, + offsetof(struct gpio_bank, int_enable)); + return (port & (1 << bit)) != 0; +} + +void gpio_set_int_level(int gpio_index, int high_rise, int edge, int delta) +{ + int bit = gpio_index % GPIO_GPIOS_PER_PORT; + uint32_t value = (high_rise ? (0x000001 << bit) : 0) | + (edge ? (0x000100 << bit) : 0) | + (delta ? (0x010000 << bit) : 0); + gpio_write_port(gpio_index, offsetof(struct gpio_bank, config), + 0x010101 << bit, value); +} + +void gpio_get_int_level(int gpio_index, int *high_rise, int *edge, int *delta) +{ + int bit = gpio_index % GPIO_GPIOS_PER_PORT; + uint32_t port = gpio_read_port(gpio_index, + offsetof(struct gpio_bank, int_level)); + *high_rise = ((port & (0x000001 << bit)) != 0); + *edge = ((port & (0x000100 << bit)) != 0); + *delta = ((port & (0x010000 << bit)) != 0); +} + +void gpio_set_int_clear(int gpio_index) +{ + int bit = gpio_index % GPIO_GPIOS_PER_PORT; + gpio_write_port(gpio_index, offsetof(struct gpio_bank, int_clear), + 1 << bit, 1 << bit); +} diff --git a/src/soc/nvidia/tegra/gpio.h b/src/soc/nvidia/tegra/gpio.h new file mode 100644 index 0000000000..b62dc90906 --- /dev/null +++ b/src/soc/nvidia/tegra/gpio.h @@ -0,0 +1,63 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * + * 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; version 2 of the License. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __SOC_NVIDIA_TEGRA_GPIO_H__ +#define __SOC_NVIDIA_TEGRA_GPIO_H__ + +#include <stdint.h> + +/* Higher level functions for common GPIO configurations. */ + +void gpio_input(int gpio_index, int pinmux_index); +void gpio_input_pullup(int gpio_index, int pinmux_index); +void gpio_input_pulldown(int gpio_index, int pinmux_index); +void gpio_output(int gpio_index, int pinmux_index, int value); + +/* Functions to modify specific GPIO control values. */ + +enum gpio_mode { + GPIO_MODE_SPIO = 0, + GPIO_MODE_GPIO = 1 +}; +void gpio_set_mode(int gpio_index, enum gpio_mode); +int gpio_get_mode(int gpio_index); + +// Lock a GPIO with extreme caution since they can't be unlocked. +void gpio_set_lock(int gpio_index); +int gpio_get_lock(int gpio_index); + +void gpio_set_out_enable(int gpio_index, int enable); +int gpio_get_out_enable(int gpio_index); + +void gpio_set_out_value(int gpio_index, int value); +int gpio_get_out_value(int gpio_index); + +int gpio_get_in_value(int gpio_index); + +int gpio_get_int_status(int gpio_index); + +void gpio_set_int_enable(int gpio_index, int enable); +int gpio_get_int_enable(int gpio_index); + +void gpio_set_int_level(int gpio_index, int high_rise, int edge, int delta); +void gpio_get_int_level(int gpio_index, int *high_rise, int *edge, int *delta); + +void gpio_set_int_clear(int gpio_index); + +#endif /* __SOC_NVIDIA_TEGRA_GPIO_H__ */ diff --git a/src/soc/nvidia/tegra/pingroup.c b/src/soc/nvidia/tegra/pingroup.c new file mode 100644 index 0000000000..858cb44e34 --- /dev/null +++ b/src/soc/nvidia/tegra/pingroup.c @@ -0,0 +1,35 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * + * 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; version 2 of the License. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <arch/io.h> +#include <soc/addressmap.h> + +#include "pingroup.h" + +static uint32_t *pingroup_regs = (void *)TEGRA_APB_PINGROUP_BASE; + +void pingroup_set_config(int group_index, uint32_t config) +{ + write32(config, &pingroup_regs[group_index]); +} + +uint32_t pingroup_get_config(int group_index) +{ + return read32(&pingroup_regs[group_index]); +} diff --git a/src/soc/nvidia/tegra/pingroup.h b/src/soc/nvidia/tegra/pingroup.h new file mode 100644 index 0000000000..f04b66541d --- /dev/null +++ b/src/soc/nvidia/tegra/pingroup.h @@ -0,0 +1,43 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * + * 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; version 2 of the License. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __SOC_NVIDIA_TEGRA_PINGROUP_H__ +#define __SOC_NVIDIA_TEGRA_PINGROUP_H__ + +#include <stdint.h> + +void pingroup_set_config(int group_index, uint32_t config); +uint32_t pingroup_get_config(int group_index); + +enum { + PINGROUP_HSM = 1 << 2, + PINGROUP_SCHMT = 1 << 3, + PINGROUP_LPMD_SHIFT = 4, + PINGROUP_LPMD_MASK = 3 << 4, + PINGROUP_DRVDN_SHIFT = 12, + PINGROUP_DRVDN_MASK = 0x7f << 12, + PINGROUP_DRVUP_SHIFT = 20, + PINGROUP_DRVUP_MASK = 0x7f << 20, + PINGROUP_SLWR_SHIFT = 28, + PINGROUP_SLWR_MASK = 0x3 << 28, + PINGROUP_SLWF_SHIFT = 30, + PINGROUP_SLWF_MASK = 0x3 << 30 +}; + +#endif /* __SOC_NVIDIA_TEGRA_PINGROUP_H__ */ diff --git a/src/soc/nvidia/tegra/pinmux.c b/src/soc/nvidia/tegra/pinmux.c new file mode 100644 index 0000000000..6e4b3ff195 --- /dev/null +++ b/src/soc/nvidia/tegra/pinmux.c @@ -0,0 +1,35 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * + * 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; version 2 of the License. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <arch/io.h> +#include <soc/addressmap.h> + +#include "pinmux.h" + +static uint32_t *pinmux_regs = (void *)TEGRA_APB_PINMUX_BASE; + +void pinmux_set_config(int pin_index, uint32_t config) +{ + write32(config, &pinmux_regs[pin_index]); +} + +uint32_t pinmux_get_config(int pin_index) +{ + return read32(&pinmux_regs[pin_index]); +} diff --git a/src/soc/nvidia/tegra/pinmux.h b/src/soc/nvidia/tegra/pinmux.h new file mode 100644 index 0000000000..e42135d9a4 --- /dev/null +++ b/src/soc/nvidia/tegra/pinmux.h @@ -0,0 +1,44 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2013 Google Inc. + * + * 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; version 2 of the License. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __SOC_NVIDIA_TEGRA_PINMUX_H__ +#define __SOC_NVIDIA_TEGRA_PINMUX_H__ + +#include <stdint.h> + +void pinmux_set_config(int pin_index, uint32_t config); +uint32_t pinmux_get_config(int pin_index); + +enum { + PINMUX_FUNC_MASK = 3 << 0, + + PINMUX_PULL_MASK = 3 << 2, + PINMUX_PULL_NONE = 0 << 2, + PINMUX_PULL_DOWN = 1 << 2, + PINMUX_PULL_UP = 2 << 2, + + PINMUX_TRISTATE = 1 << 4, + PINMUX_INPUT_ENABLE = 1 << 5, + PINMUX_OPEN_DRAIN = 1 << 6, + PINMUX_LOCK = 1 << 7, + PINMUX_IO_RESET = 1 << 8, + PINMUX_RCV_SEL = 1 << 9 +}; + +#endif /* __SOC_NVIDIA_TEGRA_PINMUX_H__ */ |