diff --git a/drivers/st/gpio/stm32_gpio.c b/drivers/st/gpio/stm32_gpio.c new file mode 100644 index 0000000..200b473 --- /dev/null +++ b/drivers/st/gpio/stm32_gpio.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2016-2018, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include + +static bool check_gpio(uint32_t bank, uint32_t pin) +{ + if (pin > GPIO_PIN_MAX) { + ERROR("%s: wrong pin number (%d)\n", __func__, pin); + return false; + } + + if ((bank > GPIO_BANK_K) && (bank != GPIO_BANK_Z)) { + ERROR("%s: wrong GPIO bank number (%d)\n", __func__, bank); + return false; + } + + return true; +} + +void set_gpio(uint32_t bank, uint32_t pin, uint32_t mode, uint32_t speed, + uint32_t pull, uint32_t alternate) +{ + volatile uint32_t bank_address; + + if (!check_gpio(bank, pin)) { + return; + } + + if (bank == GPIO_BANK_Z) { + bank_address = STM32_GPIOZ_BANK; + } else { + bank_address = STM32_GPIOA_BANK + + (bank * STM32_GPIO_BANK_OFFSET); + } + + mmio_clrbits_32(bank_address + GPIO_MODE_OFFSET, + ((uint32_t)GPIO_MODE_MASK << (pin << 1))); + mmio_setbits_32(bank_address + GPIO_MODE_OFFSET, + (mode & ~GPIO_OPEN_DRAIN) << (pin << 1)); + + if ((mode & GPIO_OPEN_DRAIN) != 0U) { + mmio_setbits_32(bank_address + GPIO_TYPE_OFFSET, + BIT(pin)); + } + + mmio_clrbits_32(bank_address + GPIO_SPEED_OFFSET, + ((uint32_t)GPIO_SPEED_MASK << (pin << 1))); + mmio_setbits_32(bank_address + GPIO_SPEED_OFFSET, speed << (pin << 1)); + + mmio_clrbits_32(bank_address + GPIO_PUPD_OFFSET, + ((uint32_t)GPIO_PULL_MASK << (pin << 1))); + mmio_setbits_32(bank_address + GPIO_PUPD_OFFSET, pull << (pin << 1)); + + if (pin < GPIO_ALT_LOWER_LIMIT) { + mmio_clrbits_32(bank_address + GPIO_AFRL_OFFSET, + ((uint32_t)GPIO_ALTERNATE_MASK << (pin << 2))); + mmio_setbits_32(bank_address + GPIO_AFRL_OFFSET, + alternate << (pin << 2)); + } else { + mmio_clrbits_32(bank_address + GPIO_AFRH_OFFSET, + ((uint32_t)GPIO_ALTERNATE_MASK << + ((pin - GPIO_ALT_LOWER_LIMIT) << 2))); + mmio_setbits_32(bank_address + GPIO_AFRH_OFFSET, + alternate << ((pin - GPIO_ALT_LOWER_LIMIT) << + 2)); + } + + VERBOSE("GPIO %u mode set to 0x%x\n", bank, + mmio_read_32(bank_address + GPIO_MODE_OFFSET)); + VERBOSE("GPIO %u speed set to 0x%x\n", bank, + mmio_read_32(bank_address + GPIO_SPEED_OFFSET)); + VERBOSE("GPIO %u mode pull to 0x%x\n", bank, + mmio_read_32(bank_address + GPIO_PUPD_OFFSET)); + VERBOSE("GPIO %u mode alternate low to 0x%x\n", bank, + mmio_read_32(bank_address + GPIO_AFRL_OFFSET)); + VERBOSE("GPIO %u mode alternate high to 0x%x\n", bank, + mmio_read_32(bank_address + GPIO_AFRH_OFFSET)); +} diff --git a/include/drivers/st/stm32_gpio.h b/include/drivers/st/stm32_gpio.h new file mode 100644 index 0000000..7a5ccd3 --- /dev/null +++ b/include/drivers/st/stm32_gpio.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2015-2018, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __PLAT_GPIO_H__ +#define __PLAT_GPIO_H__ + +#include + +#define STM32_GPIOA_BANK U(0x50002000) +#define STM32_GPIOZ_BANK U(0x54004000) +#define STM32_GPIO_BANK_OFFSET U(0x1000) + +#define GPIO_MODE_OFFSET U(0x00) +#define GPIO_TYPE_OFFSET U(0x04) +#define GPIO_SPEED_OFFSET U(0x08) +#define GPIO_PUPD_OFFSET U(0x0C) +#define GPIO_BSRR_OFFSET U(0x18) +#define GPIO_AFRL_OFFSET U(0x20) +#define GPIO_AFRH_OFFSET U(0x24) + +#define GPIO_ALT_LOWER_LIMIT U(0x08) + +#define GPIO_BANK_A U(0x00) +#define GPIO_BANK_B U(0x01) +#define GPIO_BANK_C U(0x02) +#define GPIO_BANK_D U(0x03) +#define GPIO_BANK_E U(0x04) +#define GPIO_BANK_F U(0x05) +#define GPIO_BANK_G U(0x06) +#define GPIO_BANK_H U(0x07) +#define GPIO_BANK_I U(0x08) +#define GPIO_BANK_J U(0x09) +#define GPIO_BANK_K U(0x0A) +#define GPIO_BANK_Z U(0x19) + +#define GPIO_PIN_0 U(0x00) +#define GPIO_PIN_1 U(0x01) +#define GPIO_PIN_2 U(0x02) +#define GPIO_PIN_3 U(0x03) +#define GPIO_PIN_4 U(0x04) +#define GPIO_PIN_5 U(0x05) +#define GPIO_PIN_6 U(0x06) +#define GPIO_PIN_7 U(0x07) +#define GPIO_PIN_8 U(0x08) +#define GPIO_PIN_9 U(0x09) +#define GPIO_PIN_10 U(0x0A) +#define GPIO_PIN_11 U(0x0B) +#define GPIO_PIN_12 U(0x0C) +#define GPIO_PIN_13 U(0x0D) +#define GPIO_PIN_14 U(0x0E) +#define GPIO_PIN_15 U(0x0F) +#define GPIO_PIN_MAX GPIO_PIN_15 + +#define GPIO_ALTERNATE_0 0x00 +#define GPIO_ALTERNATE_1 0x01 +#define GPIO_ALTERNATE_2 0x02 +#define GPIO_ALTERNATE_3 0x03 +#define GPIO_ALTERNATE_4 0x04 +#define GPIO_ALTERNATE_5 0x05 +#define GPIO_ALTERNATE_6 0x06 +#define GPIO_ALTERNATE_7 0x07 +#define GPIO_ALTERNATE_8 0x08 +#define GPIO_ALTERNATE_9 0x09 +#define GPIO_ALTERNATE_10 0x0A +#define GPIO_ALTERNATE_11 0x0B +#define GPIO_ALTERNATE_12 0x0C +#define GPIO_ALTERNATE_13 0x0D +#define GPIO_ALTERNATE_14 0x0E +#define GPIO_ALTERNATE_15 0x0F +#define GPIO_ALTERNATE_MASK U(0x0F) + +#define GPIO_MODE_INPUT 0x00 +#define GPIO_MODE_OUTPUT 0x01 +#define GPIO_MODE_ALTERNATE 0x02 +#define GPIO_MODE_ANALOG 0x03 +#define GPIO_MODE_MASK U(0x03) + +#define GPIO_OPEN_DRAIN U(0x10) + +#define GPIO_SPEED_LOW 0x00 +#define GPIO_SPEED_MEDIUM 0x01 +#define GPIO_SPEED_FAST 0x02 +#define GPIO_SPEED_HIGH 0x03 +#define GPIO_SPEED_MASK U(0x03) + +#define GPIO_NO_PULL 0x00 +#define GPIO_PULL_UP 0x01 +#define GPIO_PULL_DOWN 0x02 +#define GPIO_PULL_MASK U(0x03) + +#ifndef __ASSEMBLY__ +#include + +void set_gpio(uint32_t bank, uint32_t pin, uint32_t mode, uint32_t speed, + uint32_t pull, uint32_t alternate); +#endif /*__ASSEMBLY__*/ + +#endif /*__PLAT_GPIO_H__*/ diff --git a/plat/st/stm32mp1/include/stm32mp1_dt.h b/plat/st/stm32mp1/include/stm32mp1_dt.h index da203a7..1e0b722 100644 --- a/plat/st/stm32mp1/include/stm32mp1_dt.h +++ b/plat/st/stm32mp1/include/stm32mp1_dt.h @@ -21,5 +21,6 @@ uint32_t dflt_value); int fdt_read_uint32_array(int node, const char *prop_name, uint32_t *array, uint32_t count); +int dt_set_pinctrl_config(int node); #endif /* __STM32MP1_DT_H__ */ diff --git a/plat/st/stm32mp1/platform.mk b/plat/st/stm32mp1/platform.mk index e26504f..46b7fbd 100644 --- a/plat/st/stm32mp1/platform.mk +++ b/plat/st/stm32mp1/platform.mk @@ -41,6 +41,7 @@ drivers/delay_timer/generic_delay_timer.c \ drivers/st/clk/stm32mp1_clk.c \ drivers/st/clk/stm32mp1_clkfunc.c \ + drivers/st/gpio/stm32_gpio.c \ drivers/st/reset/stm32mp1_reset.c \ plat/st/stm32mp1/stm32mp1_dt.c \ plat/st/stm32mp1/stm32mp1_helper.S diff --git a/plat/st/stm32mp1/stm32mp1_dt.c b/plat/st/stm32mp1/stm32mp1_dt.c index 6098759..fe46ffa 100644 --- a/plat/st/stm32mp1/stm32mp1_dt.c +++ b/plat/st/stm32mp1/stm32mp1_dt.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -23,6 +24,120 @@ static void *fdt = (void *)(uintptr_t)STM32MP1_DTB_BASE; /******************************************************************************* + * This function gets the pin settings from DT information. + * When analyze and parsing is done, set the GPIO registers. + * Return 0 on success, else return a negative FDT_ERR_xxx error code. + ******************************************************************************/ +static int dt_set_gpio_config(int node) +{ + const fdt32_t *cuint, *slewrate; + int len, pinctrl_node, pinctrl_subnode; + uint32_t i; + uint32_t speed = GPIO_SPEED_LOW; + uint32_t pull = GPIO_NO_PULL; + + cuint = fdt_getprop(fdt, node, "pinmux", &len); + if (cuint == NULL) { + return -FDT_ERR_NOTFOUND; + } + + pinctrl_node = fdt_parent_offset(fdt, fdt_parent_offset(fdt, node)); + if (pinctrl_node < 0) { + return -FDT_ERR_NOTFOUND; + } + + slewrate = fdt_getprop(fdt, node, "slew-rate", NULL); + if (slewrate != NULL) { + speed = fdt32_to_cpu(*slewrate); + } + + if (fdt_getprop(fdt, node, "bias-pull-up", NULL) != NULL) { + pull = GPIO_PULL_UP; + } else if (fdt_getprop(fdt, node, "bias-pull-down", NULL) != NULL) { + pull = GPIO_PULL_DOWN; + } else { + VERBOSE("No bias configured in node %d\n", node); + } + + for (i = 0; i < ((uint32_t)len / sizeof(uint32_t)); i++) { + uint32_t pincfg; + uint32_t bank; + uint32_t pin; + uint32_t mode; + uint32_t alternate = GPIO_ALTERNATE_0; + + pincfg = fdt32_to_cpu(*cuint); + cuint++; + + bank = (pincfg & DT_GPIO_BANK_MASK) >> DT_GPIO_BANK_SHIFT; + + pin = (pincfg & DT_GPIO_PIN_MASK) >> DT_GPIO_PIN_SHIFT; + + mode = pincfg & DT_GPIO_MODE_MASK; + + switch (mode) { + case 0: + mode = GPIO_MODE_INPUT; + break; + case 1 ... 16: + alternate = mode - 1U; + mode = GPIO_MODE_ALTERNATE; + break; + case 17: + mode = GPIO_MODE_ANALOG; + break; + default: + mode = GPIO_MODE_OUTPUT; + break; + } + + if (fdt_getprop(fdt, node, "drive-open-drain", NULL) != NULL) { + mode |= GPIO_OPEN_DRAIN; + } + + fdt_for_each_subnode(pinctrl_subnode, fdt, pinctrl_node) { + uint32_t bank_offset; + const fdt32_t *cuint2; + + if (fdt_getprop(fdt, pinctrl_subnode, + "gpio-controller", NULL) == NULL) { + continue; + } + + cuint2 = fdt_getprop(fdt, pinctrl_subnode, "reg", NULL); + if (cuint2 == NULL) { + continue; + } + + if (bank == GPIO_BANK_Z) { + bank_offset = 0; + } else { + bank_offset = bank * STM32_GPIO_BANK_OFFSET; + } + + if (fdt32_to_cpu(*cuint2) == bank_offset) { + int clk_id = fdt_get_clock_id(pinctrl_subnode); + + if (clk_id < 0) { + return -FDT_ERR_NOTFOUND; + } + + if (stm32mp1_clk_enable((unsigned long)clk_id) < + 0) { + return -FDT_ERR_BADVALUE; + } + + break; + } + } + + set_gpio(bank, pin, mode, speed, pull, alternate); + } + + return 0; +} + +/******************************************************************************* * This function checks device tree file with its header. * Returns 0 if success, and a negative value else. ******************************************************************************/ @@ -151,3 +266,46 @@ return 0; } + +/******************************************************************************* + * This function gets the pin settings from DT information. + * When analyze and parsing is done, set the GPIO registers. + * Returns 0 if success, and a negative value else. + ******************************************************************************/ +int dt_set_pinctrl_config(int node) +{ + const fdt32_t *cuint; + int lenp = 0; + uint32_t i; + + if (!fdt_check_status(node)) { + return -FDT_ERR_NOTFOUND; + } + + cuint = fdt_getprop(fdt, node, "pinctrl-0", &lenp); + if (cuint == NULL) { + return -FDT_ERR_NOTFOUND; + } + + for (i = 0; i < ((uint32_t)lenp / 4U); i++) { + int phandle_node, phandle_subnode; + + phandle_node = + fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(*cuint)); + if (phandle_node < 0) { + return -FDT_ERR_NOTFOUND; + } + + fdt_for_each_subnode(phandle_subnode, fdt, phandle_node) { + int ret = dt_set_gpio_config(phandle_subnode); + + if (ret < 0) { + return ret; + } + } + + cuint++; + } + + return 0; +}