From e61fc00f9643fb55f2b19d1168d86b9b15d8d9c9 Mon Sep 17 00:00:00 2001 From: Sandeep Tripathy Date: Fri, 19 Apr 2019 15:34:48 +0530 Subject: drivers: bcm_gpio: add IPROC GPIO driver low level driver for Broadcom IPROC GPIO controller. Signed-off-by: Sandeep Tripathy Acked-by: Etienne Carriere Acked-by: Victor Chong --- core/arch/arm/plat-bcm/conf.mk | 1 + core/drivers/bcm_gpio.c | 184 ++++++++++++++++++++++++++++++++++++++++ core/drivers/sub.mk | 1 + core/include/drivers/bcm_gpio.h | 31 +++++++ 4 files changed, 217 insertions(+) create mode 100644 core/drivers/bcm_gpio.c create mode 100644 core/include/drivers/bcm_gpio.h diff --git a/core/arch/arm/plat-bcm/conf.mk b/core/arch/arm/plat-bcm/conf.mk index 5c9bafa4..b6d2ca5d 100644 --- a/core/arch/arm/plat-bcm/conf.mk +++ b/core/arch/arm/plat-bcm/conf.mk @@ -25,6 +25,7 @@ ifeq ($(PLATFORM_FLAVOR),ns3) $(call force,CFG_PL022,y) $(call force,CFG_BCM_HWRNG,y) $(call force,CFG_BCM_SOTP,y) +$(call force,CFG_BCM_GPIO,y) endif ifeq ($(DEBUG),1) diff --git a/core/drivers/bcm_gpio.c b/core/drivers/bcm_gpio.c new file mode 100644 index 00000000..58d34d6a --- /dev/null +++ b/core/drivers/bcm_gpio.c @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright 2019 Broadcom. + */ +#include +#include +#include +#include +#include +#include +#include + +#define IPROC_GPIO_DATA_IN_OFFSET 0x00 +#define IPROC_GPIO_DATA_OUT_OFFSET 0x04 +#define IPROC_GPIO_OUT_EN_OFFSET 0x08 +#define IPROC_GPIO_INT_MSK_OFFSET 0x18 + +#define GPIO_BANK_SIZE 0x200 +#define NGPIOS_PER_BANK 32 +#define GPIO_BANK(pin) ((pin) / NGPIOS_PER_BANK) + +#define IPROC_GPIO_REG(pin, reg) ((reg) + \ + GPIO_BANK(pin) * GPIO_BANK_SIZE) + +#define IPROC_GPIO_SHIFT(pin) ((pin) % NGPIOS_PER_BANK) + +static SLIST_HEAD(, bcm_gpio_chip) gclist = SLIST_HEAD_INITIALIZER(gclist); + +struct bcm_gpio_chip *bcm_gpio_pin_to_chip(unsigned int pin) +{ + struct bcm_gpio_chip *gc = NULL; + + SLIST_FOREACH(gc, &gclist, link) + if ((pin >= gc->gpio_base) && + (pin < (gc->gpio_base + gc->ngpios))) + return gc; + return NULL; +} + +static bool __maybe_unused gpio_is_range_overlap(unsigned int start, + unsigned int end) +{ + struct bcm_gpio_chip *gc = NULL; + + SLIST_FOREACH(gc, &gclist, link) + if ((start < (gc->gpio_base + gc->ngpios)) && + (end > gc->gpio_base)) + return true; + return false; +} + +static void iproc_set_bit(unsigned int reg, unsigned int gpio) +{ + unsigned int offset = IPROC_GPIO_REG(gpio, reg); + unsigned int shift = IPROC_GPIO_SHIFT(gpio); + struct bcm_gpio_chip *gc = bcm_gpio_pin_to_chip(gpio); + + assert(gc); + io_setbits32(gc->base + offset, BIT(shift)); +} + +static void iproc_clr_bit(unsigned int reg, unsigned int gpio) +{ + unsigned int offset = IPROC_GPIO_REG(gpio, reg); + unsigned int shift = IPROC_GPIO_SHIFT(gpio); + struct bcm_gpio_chip *gc = bcm_gpio_pin_to_chip(gpio); + + assert(gc); + io_clrbits32(gc->base + offset, BIT(shift)); +} + +static void iproc_gpio_set(unsigned int gpio, enum gpio_level val) +{ + if (val == GPIO_LEVEL_HIGH) + iproc_set_bit(IPROC_GPIO_DATA_OUT_OFFSET, gpio); + else + iproc_clr_bit(IPROC_GPIO_DATA_OUT_OFFSET, gpio); +} + +static enum gpio_level iproc_gpio_get(unsigned int gpio) +{ + unsigned int offset = IPROC_GPIO_REG(gpio, IPROC_GPIO_DATA_IN_OFFSET); + unsigned int shift = IPROC_GPIO_SHIFT(gpio); + struct bcm_gpio_chip *gc = bcm_gpio_pin_to_chip(gpio); + + assert(gc); + + if (io_read32(gc->base + offset) & BIT(shift)) + return GPIO_LEVEL_HIGH; + else + return GPIO_LEVEL_LOW; +} + +static void iproc_gpio_set_dir(unsigned int gpio, enum gpio_dir dir) +{ + if (dir == GPIO_DIR_OUT) + iproc_set_bit(IPROC_GPIO_OUT_EN_OFFSET, gpio); + else + iproc_clr_bit(IPROC_GPIO_OUT_EN_OFFSET, gpio); +} + +static enum gpio_dir iproc_gpio_get_dir(unsigned int gpio) +{ + unsigned int offset = IPROC_GPIO_REG(gpio, IPROC_GPIO_OUT_EN_OFFSET); + unsigned int shift = IPROC_GPIO_SHIFT(gpio); + struct bcm_gpio_chip *gc = bcm_gpio_pin_to_chip(gpio); + + assert(gc); + + if (io_read32(gc->base + offset) & BIT(shift)) + return GPIO_DIR_OUT; + else + return GPIO_DIR_IN; +} + +static enum gpio_interrupt iproc_gpio_get_itr(unsigned int gpio) +{ + unsigned int offset = IPROC_GPIO_REG(gpio, IPROC_GPIO_INT_MSK_OFFSET); + unsigned int shift = IPROC_GPIO_SHIFT(gpio); + struct bcm_gpio_chip *gc = bcm_gpio_pin_to_chip(gpio); + + assert(gc); + + if (io_read32(gc->base + offset) & BIT(shift)) + return GPIO_INTERRUPT_ENABLE; + else + return GPIO_INTERRUPT_DISABLE; +} + +static void iproc_gpio_set_itr(unsigned int gpio, + enum gpio_interrupt ena_dis) +{ + if (ena_dis == GPIO_INTERRUPT_ENABLE) + iproc_set_bit(IPROC_GPIO_OUT_EN_OFFSET, gpio); + else + iproc_clr_bit(IPROC_GPIO_OUT_EN_OFFSET, gpio); +} + +static const struct gpio_ops bcm_gpio_ops = { + .get_direction = iproc_gpio_get_dir, + .set_direction = iproc_gpio_set_dir, + .get_value = iproc_gpio_get, + .set_value = iproc_gpio_set, + .get_interrupt = iproc_gpio_get_itr, + .set_interrupt = iproc_gpio_set_itr, +}; +KEEP_PAGER(bcm_gpio_ops); + +static void iproc_gpio_init(struct bcm_gpio_chip *gc, unsigned int paddr, + unsigned int gpio_base, unsigned int ngpios) +{ + assert(!gpio_is_range_overlap(gpio_base, gpio_base + gc->ngpios)); + + gc->base = core_mmu_get_va(paddr, MEM_AREA_IO_SEC); + gc->chip.ops = &bcm_gpio_ops; + gc->gpio_base = gpio_base; + gc->ngpios = ngpios; + + SLIST_INSERT_HEAD(&gclist, gc, link); + + DMSG("gpio chip for <%u - %u>", gpio_base, gpio_base + ngpios); +} + +static TEE_Result bcm_gpio_init(void) +{ + struct bcm_gpio_chip *gc = NULL; + +#ifdef SECURE_GPIO_BASE0 + gc = malloc(sizeof(*gc)); + if (gc == NULL) + return TEE_ERROR_OUT_OF_MEMORY; + + iproc_gpio_init(gc, SECURE_GPIO_BASE0, GPIO_NUM_START0, NUM_GPIOS0); +#endif +#ifdef SECURE_GPIO_BASE1 + gc = malloc(sizeof(*gc)); + if (gc == NULL) + return TEE_ERROR_OUT_OF_MEMORY; + + iproc_gpio_init(gc, SECURE_GPIO_BASE1, GPIO_NUM_START1, NUM_GPIOS1); +#endif + return TEE_SUCCESS; +} +driver_init(bcm_gpio_init); diff --git a/core/drivers/sub.mk b/core/drivers/sub.mk index bbe5cef0..d7bffc39 100644 --- a/core/drivers/sub.mk +++ b/core/drivers/sub.mk @@ -27,3 +27,4 @@ srcs-$(CFG_STM32_UART) += stm32_uart.c srcs-$(CFG_STM32_I2C) += stm32_i2c.c srcs-$(CFG_BCM_HWRNG) += bcm_hwrng.c srcs-$(CFG_BCM_SOTP) += bcm_sotp.c +srcs-$(CFG_BCM_GPIO) += bcm_gpio.c diff --git a/core/include/drivers/bcm_gpio.h b/core/include/drivers/bcm_gpio.h new file mode 100644 index 00000000..348592a0 --- /dev/null +++ b/core/include/drivers/bcm_gpio.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright 2019 Broadcom. + */ + +#ifndef BCM_GPIO_H +#define BCM_GPIO_H + +#include +#include +#include + +/** + * struct bcm_gpio_chip describes GPIO controller chip instance + * @chip: generic GPIO chip handle. + * @gpio_base: starting GPIO number managed by this GPIO controller. + * @ngpios: number of GPIOs managed by this GPIO controller. + * @base: virtual base address of the GPIO controller registers. + */ +struct bcm_gpio_chip { + struct gpio_chip chip; + unsigned int gpio_base; + unsigned int ngpios; + vaddr_t base; + + SLIST_ENTRY(bcm_gpio_chip) link; +}; + +/* returns bcm_gpio_chip handle for a GPIO pin */ +struct bcm_gpio_chip *bcm_gpio_pin_to_chip(unsigned int pin); +#endif /* BCM_GPIO_H */ -- cgit v1.2.3