diff options
Diffstat (limited to 'src/soc/nvidia/tegra/gpio.c')
-rw-r--r-- | src/soc/nvidia/tegra/gpio.c | 240 |
1 files changed, 240 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); +} |