diff options
Diffstat (limited to 'plat/rockchip/rk3399/drivers/gpio/rk3399_gpio.c')
-rw-r--r-- | plat/rockchip/rk3399/drivers/gpio/rk3399_gpio.c | 417 |
1 files changed, 417 insertions, 0 deletions
diff --git a/plat/rockchip/rk3399/drivers/gpio/rk3399_gpio.c b/plat/rockchip/rk3399/drivers/gpio/rk3399_gpio.c new file mode 100644 index 00000000..e74c4d91 --- /dev/null +++ b/plat/rockchip/rk3399/drivers/gpio/rk3399_gpio.c @@ -0,0 +1,417 @@ +/* + * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include <assert.h> +#include <debug.h> +#include <delay_timer.h> +#include <errno.h> +#include <gpio.h> +#include <mmio.h> +#include <plat_private.h> +#include <platform.h> +#include <platform_def.h> +#include <soc.h> + +uint32_t gpio_port[] = { + GPIO0_BASE, + GPIO1_BASE, + GPIO2_BASE, + GPIO3_BASE, + GPIO4_BASE, +}; + +struct { + uint32_t swporta_dr; + uint32_t swporta_ddr; + uint32_t inten; + uint32_t intmask; + uint32_t inttype_level; + uint32_t int_polarity; + uint32_t debounce; + uint32_t ls_sync; +} store_gpio[3]; + +static uint32_t store_grf_gpio[(GRF_GPIO2D_HE - GRF_GPIO2A_IOMUX) / 4 + 1]; + +#define SWPORTA_DR 0x00 +#define SWPORTA_DDR 0x04 +#define INTEN 0x30 +#define INTMASK 0x34 +#define INTTYPE_LEVEL 0x38 +#define INT_POLARITY 0x3c +#define DEBOUNCE 0x48 +#define LS_SYNC 0x60 + +#define EXT_PORTA 0x50 +#define PMU_GPIO_PORT0 0 +#define PMU_GPIO_PORT1 1 +#define GPIO_PORT2 2 +#define GPIO_PORT3 3 +#define GPIO_PORT4 4 + +#define PMU_GRF_GPIO0A_P 0x40 +#define GRF_GPIO2A_P 0xe040 +#define GPIO_P_MASK 0x03 + +#define GET_GPIO_PORT(pin) (pin / 32) +#define GET_GPIO_NUM(pin) (pin % 32) +#define GET_GPIO_BANK(pin) ((pin % 32) / 8) +#define GET_GPIO_ID(pin) ((pin % 32) % 8) + +/* returns old clock state, enables clock, in order to do GPIO access */ +static int gpio_get_clock(uint32_t gpio_number) +{ + uint32_t port = GET_GPIO_PORT(gpio_number); + uint32_t clock_state = 0; + + assert(port < 5); + + switch (port) { + case PMU_GPIO_PORT0: + clock_state = (mmio_read_32(PMUCRU_BASE + + CRU_PMU_CLKGATE_CON(1)) >> + PCLK_GPIO0_GATE_SHIFT) & 0x01; + mmio_write_32(PMUCRU_BASE + CRU_PMU_CLKGATE_CON(1), + BITS_WITH_WMASK(0, CLK_GATE_MASK, + PCLK_GPIO0_GATE_SHIFT)); + break; + case PMU_GPIO_PORT1: + clock_state = (mmio_read_32(PMUCRU_BASE + + CRU_PMU_CLKGATE_CON(1)) >> + PCLK_GPIO1_GATE_SHIFT) & 0x01; + mmio_write_32(PMUCRU_BASE + CRU_PMU_CLKGATE_CON(1), + BITS_WITH_WMASK(0, CLK_GATE_MASK, + PCLK_GPIO1_GATE_SHIFT)); + break; + case GPIO_PORT2: + clock_state = (mmio_read_32(CRU_BASE + + CRU_CLKGATE_CON(31)) >> + PCLK_GPIO2_GATE_SHIFT) & 0x01; + mmio_write_32(CRU_BASE + CRU_CLKGATE_CON(31), + BITS_WITH_WMASK(0, CLK_GATE_MASK, + PCLK_GPIO2_GATE_SHIFT)); + break; + case GPIO_PORT3: + clock_state = (mmio_read_32(CRU_BASE + + CRU_CLKGATE_CON(31)) >> + PCLK_GPIO3_GATE_SHIFT) & 0x01; + mmio_write_32(CRU_BASE + CRU_CLKGATE_CON(31), + BITS_WITH_WMASK(0, CLK_GATE_MASK, + PCLK_GPIO3_GATE_SHIFT)); + break; + case GPIO_PORT4: + clock_state = (mmio_read_32(CRU_BASE + + CRU_CLKGATE_CON(31)) >> + PCLK_GPIO4_GATE_SHIFT) & 0x01; + mmio_write_32(CRU_BASE + CRU_CLKGATE_CON(31), + BITS_WITH_WMASK(0, CLK_GATE_MASK, + PCLK_GPIO4_GATE_SHIFT)); + break; + default: + break; + } + + return clock_state; +} + +/* restores old state of gpio clock */ +void gpio_put_clock(uint32_t gpio_number, uint32_t clock_state) +{ + uint32_t port = GET_GPIO_PORT(gpio_number); + + switch (port) { + case PMU_GPIO_PORT0: + mmio_write_32(PMUCRU_BASE + CRU_PMU_CLKGATE_CON(1), + BITS_WITH_WMASK(clock_state, CLK_GATE_MASK, + PCLK_GPIO0_GATE_SHIFT)); + break; + case PMU_GPIO_PORT1: + mmio_write_32(PMUCRU_BASE + CRU_PMU_CLKGATE_CON(1), + BITS_WITH_WMASK(clock_state, CLK_GATE_MASK, + PCLK_GPIO1_GATE_SHIFT)); + break; + case GPIO_PORT2: + mmio_write_32(CRU_BASE + CRU_CLKGATE_CON(31), + BITS_WITH_WMASK(clock_state, CLK_GATE_MASK, + PCLK_GPIO2_GATE_SHIFT)); + break; + case GPIO_PORT3: + mmio_write_32(CRU_BASE + CRU_CLKGATE_CON(31), + BITS_WITH_WMASK(clock_state, CLK_GATE_MASK, + PCLK_GPIO3_GATE_SHIFT)); + + break; + case GPIO_PORT4: + mmio_write_32(CRU_BASE + CRU_CLKGATE_CON(31), + BITS_WITH_WMASK(clock_state, CLK_GATE_MASK, + PCLK_GPIO4_GATE_SHIFT)); + break; + default: + break; + } +} + +static int get_pull(int gpio) +{ + uint32_t port = GET_GPIO_PORT(gpio); + uint32_t bank = GET_GPIO_BANK(gpio); + uint32_t id = GET_GPIO_ID(gpio); + uint32_t val, clock_state; + + assert((port < 5) && (bank < 4)); + + clock_state = gpio_get_clock(gpio); + + if (port == PMU_GPIO_PORT0 || port == PMU_GPIO_PORT1) { + val = mmio_read_32(PMUGRF_BASE + PMU_GRF_GPIO0A_P + + port * 16 + bank * 4); + val = (val >> (id * 2)) & GPIO_P_MASK; + } else { + val = mmio_read_32(GRF_BASE + GRF_GPIO2A_P + + (port - 2) * 16 + bank * 4); + val = (val >> (id * 2)) & GPIO_P_MASK; + } + gpio_put_clock(gpio, clock_state); + + /* + * in gpio0a, gpio0b, gpio2c, gpio2d, + * 00: Z + * 01: pull down + * 10: Z + * 11: pull up + * different with other gpio, so need to correct it + */ + if (((port == 0) && (bank < 2)) || ((port == 2) && (bank > 1))) { + if (val == 3) + val = GPIO_PULL_UP; + else if (val == 1) + val = GPIO_PULL_DOWN; + else + val = 0; + } + + return val; +} + +static void set_pull(int gpio, int pull) +{ + uint32_t port = GET_GPIO_PORT(gpio); + uint32_t bank = GET_GPIO_BANK(gpio); + uint32_t id = GET_GPIO_ID(gpio); + uint32_t clock_state; + + assert((port < 5) && (bank < 4)); + + clock_state = gpio_get_clock(gpio); + + /* + * in gpio0a, gpio0b, gpio2c, gpio2d, + * 00: Z + * 01: pull down + * 10: Z + * 11: pull up + * different with other gpio, so need to correct it + */ + if (((port == 0) && (bank < 2)) || ((port == 2) && (bank > 1))) { + if (pull == GPIO_PULL_UP) + pull = 3; + else if (pull == GPIO_PULL_DOWN) + pull = 1; + else + pull = 0; + } + + if (port == PMU_GPIO_PORT0 || port == PMU_GPIO_PORT1) { + mmio_write_32(PMUGRF_BASE + PMU_GRF_GPIO0A_P + + port * 16 + bank * 4, + BITS_WITH_WMASK(pull, GPIO_P_MASK, id * 2)); + } else { + mmio_write_32(GRF_BASE + GRF_GPIO2A_P + + (port - 2) * 16 + bank * 4, + BITS_WITH_WMASK(pull, GPIO_P_MASK, id * 2)); + } + gpio_put_clock(gpio, clock_state); +} + +static void set_direction(int gpio, int direction) +{ + uint32_t port = GET_GPIO_PORT(gpio); + uint32_t num = GET_GPIO_NUM(gpio); + uint32_t clock_state; + + assert((port < 5) && (num < 32)); + + clock_state = gpio_get_clock(gpio); + + /* + * in gpio.h + * #define GPIO_DIR_OUT 0 + * #define GPIO_DIR_IN 1 + * but rk3399 gpio direction 1: output, 0: input + * so need to revert direction value + */ + mmio_setbits_32(gpio_port[port] + SWPORTA_DDR, !direction << num); + gpio_put_clock(gpio, clock_state); +} + +static int get_direction(int gpio) +{ + uint32_t port = GET_GPIO_PORT(gpio); + uint32_t num = GET_GPIO_NUM(gpio); + int direction, clock_state; + + assert((port < 5) && (num < 32)); + + clock_state = gpio_get_clock(gpio); + + /* + * in gpio.h + * #define GPIO_DIR_OUT 0 + * #define GPIO_DIR_IN 1 + * but rk3399 gpio direction 1: output, 0: input + * so need to revert direction value + */ + direction = !((mmio_read_32(gpio_port[port] + + SWPORTA_DDR) >> num) & 0x1); + gpio_put_clock(gpio, clock_state); + + return direction; +} + +static int get_value(int gpio) +{ + uint32_t port = GET_GPIO_PORT(gpio); + uint32_t num = GET_GPIO_NUM(gpio); + int value, clock_state; + + assert((port < 5) && (num < 32)); + + clock_state = gpio_get_clock(gpio); + value = (mmio_read_32(gpio_port[port] + EXT_PORTA) >> num) & 0x1; + gpio_put_clock(gpio, clock_state); + + return value; +} + +static void set_value(int gpio, int value) +{ + uint32_t port = GET_GPIO_PORT(gpio); + uint32_t num = GET_GPIO_NUM(gpio); + uint32_t clock_state; + + assert((port < 5) && (num < 32)); + + clock_state = gpio_get_clock(gpio); + mmio_clrsetbits_32(gpio_port[port] + SWPORTA_DR, 1 << num, + !!value << num); + gpio_put_clock(gpio, clock_state); +} + +void plat_rockchip_save_gpio(void) +{ + int i; + uint32_t cru_gate_save; + + cru_gate_save = mmio_read_32(CRU_BASE + CRU_CLKGATE_CON(31)); + + /* + * when shutdown logic, we need to save gpio2 ~ gpio4 register, + * we need to enable gpio2 ~ gpio4 clock here, since it may be gating, + * and we do not care gpio0 and gpio1 clock gate, since we never + * gating them + */ + mmio_write_32(CRU_BASE + CRU_CLKGATE_CON(31), + BITS_WITH_WMASK(0, 0x07, PCLK_GPIO2_GATE_SHIFT)); + + /* + * since gpio0, gpio1 are pmugpio, they will keep ther value + * when shutdown logic power rail, so only need to save gpio2 ~ gpio4 + * register value + */ + for (i = 2; i < 5; i++) { + store_gpio[i - 2].swporta_dr = + mmio_read_32(gpio_port[i] + SWPORTA_DR); + store_gpio[i - 2].swporta_ddr = + mmio_read_32(gpio_port[i] + SWPORTA_DDR); + store_gpio[i - 2].inten = + mmio_read_32(gpio_port[i] + INTEN); + store_gpio[i - 2].intmask = + mmio_read_32(gpio_port[i] + INTMASK); + store_gpio[i - 2].inttype_level = + mmio_read_32(gpio_port[i] + INTTYPE_LEVEL); + store_gpio[i - 2].int_polarity = + mmio_read_32(gpio_port[i] + INT_POLARITY); + store_gpio[i - 2].debounce = + mmio_read_32(gpio_port[i] + DEBOUNCE); + store_gpio[i - 2].ls_sync = + mmio_read_32(gpio_port[i] + LS_SYNC); + } + mmio_write_32(CRU_BASE + CRU_CLKGATE_CON(31), + cru_gate_save | REG_SOC_WMSK); + + /* + * gpio0, gpio1 in pmuiomux, they will keep ther value + * when shutdown logic power rail, so only need to save gpio2 ~ gpio4 + * iomux register value + */ + for (i = 0; i < ARRAY_SIZE(store_grf_gpio); i++) + store_grf_gpio[i] = + mmio_read_32(GRF_BASE + GRF_GPIO2A_IOMUX + i * 4); +} + +void plat_rockchip_restore_gpio(void) +{ + int i; + uint32_t cru_gate_save; + + for (i = 0; i < ARRAY_SIZE(store_grf_gpio); i++) + mmio_write_32(GRF_BASE + GRF_GPIO2A_IOMUX + i * 4, + REG_SOC_WMSK | store_grf_gpio[i]); + + cru_gate_save = mmio_read_32(CRU_BASE + CRU_CLKGATE_CON(31)); + + /* + * when shutdown logic, we need to save gpio2 ~ gpio4 register, + * we need to enable gpio2 ~ gpio4 clock here, since it may be gating, + * and we do not care gpio0 and gpio1 clock gate, since we never + * gating them + */ + mmio_write_32(CRU_BASE + CRU_CLKGATE_CON(31), + BITS_WITH_WMASK(0, 0x07, PCLK_GPIO2_GATE_SHIFT)); + + for (i = 2; i < 5; i++) { + mmio_write_32(gpio_port[i] + SWPORTA_DR, + store_gpio[i - 2].swporta_dr); + mmio_write_32(gpio_port[i] + SWPORTA_DDR, + store_gpio[i - 2].swporta_ddr); + mmio_write_32(gpio_port[i] + INTEN, store_gpio[i - 2].inten); + mmio_write_32(gpio_port[i] + INTMASK, + store_gpio[i - 2].intmask); + mmio_write_32(gpio_port[i] + INTTYPE_LEVEL, + store_gpio[i - 2].inttype_level); + mmio_write_32(gpio_port[i] + INT_POLARITY, + store_gpio[i - 2].int_polarity); + mmio_write_32(gpio_port[i] + DEBOUNCE, + store_gpio[i - 2].debounce); + mmio_write_32(gpio_port[i] + LS_SYNC, + store_gpio[i - 2].ls_sync); + } + mmio_write_32(CRU_BASE + CRU_CLKGATE_CON(31), + cru_gate_save | REG_SOC_WMSK); +} + +const gpio_ops_t rk3399_gpio_ops = { + .get_direction = get_direction, + .set_direction = set_direction, + .get_value = get_value, + .set_value = set_value, + .set_pull = set_pull, + .get_pull = get_pull, +}; + +void plat_rockchip_gpio_init(void) +{ + gpio_init(&rk3399_gpio_ops); +} |