aboutsummaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorSandeep Tripathy <sandeep.tripathy@broadcom.com>2019-04-19 15:34:48 +0530
committerJoakim Bech <joakim.bech@linaro.org>2019-04-23 16:58:16 +0700
commite61fc00f9643fb55f2b19d1168d86b9b15d8d9c9 (patch)
treeed4af888ca754f1f6056042435d4dcae4bf414c0 /core
parent7695df0549f966b3775d8b8dba8c4b190349c84f (diff)
drivers: bcm_gpio: add IPROC GPIO driver
low level driver for Broadcom IPROC GPIO controller. Signed-off-by: Sandeep Tripathy <sandeep.tripathy@broadcom.com> Acked-by: Etienne Carriere <etienne.carriere@linaro.org> Acked-by: Victor Chong <victor.chong@linaro.org>
Diffstat (limited to 'core')
-rw-r--r--core/arch/arm/plat-bcm/conf.mk1
-rw-r--r--core/drivers/bcm_gpio.c184
-rw-r--r--core/drivers/sub.mk1
-rw-r--r--core/include/drivers/bcm_gpio.h31
4 files changed, 217 insertions, 0 deletions
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 <assert.h>
+#include <drivers/bcm_gpio.h>
+#include <initcall.h>
+#include <io.h>
+#include <mm/core_memprot.h>
+#include <platform_config.h>
+#include <trace.h>
+
+#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 <gpio.h>
+#include <stdlib.h>
+#include <sys/queue.h>
+
+/**
+ * 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 */