Merge pull request #1773 from grandpaul/rpi3-gpio-driver

Rpi3 gpio driver
diff --git a/drivers/rpi3/gpio/rpi3_gpio.c b/drivers/rpi3/gpio/rpi3_gpio.c
new file mode 100644
index 0000000..b39808f
--- /dev/null
+++ b/drivers/rpi3/gpio/rpi3_gpio.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2019, Linaro Limited
+ * Copyright (c) 2019, Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org>
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <string.h>
+#include <assert.h>
+#include <lib/mmio.h>
+#include <drivers/delay_timer.h>
+#include <drivers/rpi3/gpio/rpi3_gpio.h>
+
+static struct rpi3_gpio_params rpi3_gpio_params;
+
+static int rpi3_gpio_get_direction(int gpio);
+static void rpi3_gpio_set_direction(int gpio, int direction);
+static int rpi3_gpio_get_value(int gpio);
+static void rpi3_gpio_set_value(int gpio, int value);
+static void rpi3_gpio_set_pull(int gpio, int pull);
+
+static const gpio_ops_t rpi3_gpio_ops = {
+	.get_direction  = rpi3_gpio_get_direction,
+	.set_direction  = rpi3_gpio_set_direction,
+	.get_value      = rpi3_gpio_get_value,
+	.set_value      = rpi3_gpio_set_value,
+	.set_pull       = rpi3_gpio_set_pull,
+};
+
+/**
+ * Get selection of GPIO pinmux settings.
+ *
+ * @param gpio The pin number of GPIO. From 0 to 53.
+ * @return The selection of pinmux. RPI3_GPIO_FUNC_INPUT: input,
+ *                                  RPI3_GPIO_FUNC_OUTPUT: output,
+ *                                  RPI3_GPIO_FUNC_ALT0: alt-0,
+ *                                  RPI3_GPIO_FUNC_ALT1: alt-1,
+ *                                  RPI3_GPIO_FUNC_ALT2: alt-2,
+ *                                  RPI3_GPIO_FUNC_ALT3: alt-3,
+ *                                  RPI3_GPIO_FUNC_ALT4: alt-4,
+ *                                  RPI3_GPIO_FUNC_ALT5: alt-5
+ */
+int rpi3_gpio_get_select(int gpio)
+{
+	int ret;
+	uintptr_t reg_base = rpi3_gpio_params.reg_base;
+	int regN = gpio / 10;
+	int shift = 3 * (gpio % 10);
+	uintptr_t reg_sel = reg_base + RPI3_GPIO_GPFSEL(regN);
+	uint32_t sel = mmio_read_32(reg_sel);
+
+	ret = (sel >> shift) & 0x07;
+
+	return ret;
+}
+
+/**
+ * Set selection of GPIO pinmux settings.
+ *
+ * @param gpio The pin number of GPIO. From 0 to 53.
+ * @param fsel The selection of pinmux. RPI3_GPIO_FUNC_INPUT: input,
+ *                                      RPI3_GPIO_FUNC_OUTPUT: output,
+ *                                      RPI3_GPIO_FUNC_ALT0: alt-0,
+ *                                      RPI3_GPIO_FUNC_ALT1: alt-1,
+ *                                      RPI3_GPIO_FUNC_ALT2: alt-2,
+ *                                      RPI3_GPIO_FUNC_ALT3: alt-3,
+ *                                      RPI3_GPIO_FUNC_ALT4: alt-4,
+ *                                      RPI3_GPIO_FUNC_ALT5: alt-5
+ */
+void rpi3_gpio_set_select(int gpio, int fsel)
+{
+	uintptr_t reg_base = rpi3_gpio_params.reg_base;
+	int regN = gpio / 10;
+	int shift = 3 * (gpio % 10);
+	uintptr_t reg_sel = reg_base + RPI3_GPIO_GPFSEL(regN);
+	uint32_t sel = mmio_read_32(reg_sel);
+	uint32_t mask = U(0x07) << shift;
+
+	sel = (sel & (~mask)) | ((fsel << shift) & mask);
+	mmio_write_32(reg_sel, sel);
+}
+
+static int rpi3_gpio_get_direction(int gpio)
+{
+	int result = rpi3_gpio_get_select(gpio);
+
+	if (result == RPI3_GPIO_FUNC_INPUT)
+		return GPIO_DIR_IN;
+	else if (result == RPI3_GPIO_FUNC_OUTPUT)
+		return GPIO_DIR_OUT;
+
+	return GPIO_DIR_IN;
+}
+
+static void rpi3_gpio_set_direction(int gpio, int direction)
+{
+	switch (direction) {
+	case GPIO_DIR_IN:
+		rpi3_gpio_set_select(gpio, RPI3_GPIO_FUNC_INPUT);
+		break;
+	case GPIO_DIR_OUT:
+		rpi3_gpio_set_select(gpio, RPI3_GPIO_FUNC_OUTPUT);
+		break;
+	}
+}
+
+static int rpi3_gpio_get_value(int gpio)
+{
+	uintptr_t reg_base = rpi3_gpio_params.reg_base;
+	int regN = gpio / 32;
+	int shift = gpio % 32;
+	uintptr_t reg_lev = reg_base + RPI3_GPIO_GPLEV(regN);
+	uint32_t value = mmio_read_32(reg_lev);
+
+	if ((value >> shift) & 0x01)
+		return GPIO_LEVEL_HIGH;
+	return GPIO_LEVEL_LOW;
+}
+
+static void rpi3_gpio_set_value(int gpio, int value)
+{
+	uintptr_t reg_base = rpi3_gpio_params.reg_base;
+	int regN = gpio / 32;
+	int shift = gpio % 32;
+	uintptr_t reg_set = reg_base + RPI3_GPIO_GPSET(regN);
+	uintptr_t reg_clr = reg_base + RPI3_GPIO_GPSET(regN);
+
+	switch (value) {
+	case GPIO_LEVEL_LOW:
+		mmio_write_32(reg_clr, U(1) << shift);
+		break;
+	case GPIO_LEVEL_HIGH:
+		mmio_write_32(reg_set, U(1) << shift);
+		break;
+	}
+}
+
+static void rpi3_gpio_set_pull(int gpio, int pull)
+{
+	uintptr_t reg_base = rpi3_gpio_params.reg_base;
+	int regN = gpio / 32;
+	int shift = gpio % 32;
+	uintptr_t reg_pud = reg_base + RPI3_GPIO_GPPUD;
+	uintptr_t reg_clk = reg_base + RPI3_GPIO_GPPUDCLK(regN);
+
+	switch (pull) {
+	case GPIO_PULL_NONE:
+		mmio_write_32(reg_pud, 0x0);
+		break;
+	case GPIO_PULL_UP:
+		mmio_write_32(reg_pud, 0x2);
+		break;
+	case GPIO_PULL_DOWN:
+		mmio_write_32(reg_pud, 0x1);
+		break;
+	}
+	mdelay(150);
+	mmio_write_32(reg_clk, U(1) << shift);
+	mdelay(150);
+	mmio_write_32(reg_clk, 0x0);
+	mmio_write_32(reg_pud, 0x0);
+}
+
+void rpi3_gpio_init(struct rpi3_gpio_params *params)
+{
+	assert(params != 0);
+	memcpy(&rpi3_gpio_params, params, sizeof(struct rpi3_gpio_params));
+	gpio_init(&rpi3_gpio_ops);
+}
diff --git a/include/drivers/rpi3/gpio/rpi3_gpio.h b/include/drivers/rpi3/gpio/rpi3_gpio.h
new file mode 100644
index 0000000..159a2e0
--- /dev/null
+++ b/include/drivers/rpi3/gpio/rpi3_gpio.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2019, Linaro Limited
+ * Copyright (c) 2019, Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org>
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef RPI3_GPIO_H
+#define RPI3_GPIO_H
+
+#include <stdint.h>
+#include <drivers/gpio.h>
+
+struct rpi3_gpio_params {
+	uintptr_t       reg_base;
+};
+
+void rpi3_gpio_init(struct rpi3_gpio_params *params);
+int rpi3_gpio_get_select(int gpio);
+void rpi3_gpio_set_select(int gpio, int fsel);
+
+#define RPI3_GPIO_GPFSEL(n)	((n) * U(0x04))
+#define RPI3_GPIO_GPSET(n)	(((n) * U(0x04)) + U(0x1C))
+#define RPI3_GPIO_GPCLR(n)	(((n) * U(0x04)) + U(0x28))
+#define RPI3_GPIO_GPLEV(n) 	(((n) * U(0x04)) + U(0x34))
+#define RPI3_GPIO_GPPUD		U(0x94)
+#define RPI3_GPIO_GPPUDCLK(n)	(((n) * U(0x04)) + U(0x98))
+
+#define RPI3_GPIO_FUNC_INPUT	U(0)
+#define RPI3_GPIO_FUNC_OUTPUT	U(1)
+#define RPI3_GPIO_FUNC_ALT0	U(4)
+#define RPI3_GPIO_FUNC_ALT1	U(5)
+#define RPI3_GPIO_FUNC_ALT2	U(6)
+#define RPI3_GPIO_FUNC_ALT3	U(7)
+#define RPI3_GPIO_FUNC_ALT4	U(3)
+#define RPI3_GPIO_FUNC_ALT5	U(2)
+
+#endif  /* RPI3_GPIO_H */
diff --git a/plat/rpi3/platform.mk b/plat/rpi3/platform.mk
index 5e76345..ded92bd 100644
--- a/plat/rpi3/platform.mk
+++ b/plat/rpi3/platform.mk
@@ -27,6 +27,10 @@
 				drivers/io/io_fip.c			\
 				drivers/io/io_memmap.c			\
 				drivers/io/io_storage.c			\
+				drivers/gpio/gpio.c			\
+				drivers/delay_timer/delay_timer.c	\
+				drivers/delay_timer/generic_delay_timer.c \
+				drivers/rpi3/gpio/rpi3_gpio.c		\
 				plat/common/aarch64/platform_mp_stack.S	\
 				plat/rpi3/aarch64/plat_helpers.S	\
 				plat/rpi3/aarch64/rpi3_bl2_mem_params_desc.c \
diff --git a/plat/rpi3/rpi3_bl2_setup.c b/plat/rpi3/rpi3_bl2_setup.c
index 53a2c72..09f0562 100644
--- a/plat/rpi3/rpi3_bl2_setup.c
+++ b/plat/rpi3/rpi3_bl2_setup.c
@@ -15,12 +15,25 @@
 #include <lib/optee_utils.h>
 #include <lib/xlat_tables/xlat_mmu_helpers.h>
 #include <lib/xlat_tables/xlat_tables_defs.h>
+#include <drivers/generic_delay_timer.h>
+#include <drivers/rpi3/gpio/rpi3_gpio.h>
 
 #include "rpi3_private.h"
 
 /* Data structure which holds the extents of the trusted SRAM for BL2 */
 static meminfo_t bl2_tzram_layout __aligned(CACHE_WRITEBACK_GRANULE);
 
+/* rpi3 GPIO setup function. */
+static void rpi3_gpio_setup(void)
+{
+	struct rpi3_gpio_params params;
+
+	memset(&params, 0, sizeof(struct rpi3_gpio_params));
+	params.reg_base = RPI3_GPIO_BASE;
+
+	rpi3_gpio_init(&params);
+}
+
 /*******************************************************************************
  * BL1 has passed the extents of the trusted SRAM that should be visible to BL2
  * in x0. This memory layout is sitting at the base of the free trusted SRAM.
@@ -35,6 +48,12 @@
 	/* Initialize the console to provide early debug support */
 	rpi3_console_init();
 
+	/* Enable arch timer */
+	generic_delay_timer_init();
+
+	/* Setup GPIO driver */
+	rpi3_gpio_setup();
+
 	/* Setup the BL2 memory layout */
 	bl2_tzram_layout = *mem_layout;
 
diff --git a/plat/rpi3/rpi3_hw.h b/plat/rpi3/rpi3_hw.h
index 9d86eb8..61d1837 100644
--- a/plat/rpi3/rpi3_hw.h
+++ b/plat/rpi3/rpi3_hw.h
@@ -84,6 +84,12 @@
 #define RPI3_MINI_UART_CLK_IN_HZ	ULL(500000000)
 
 /*
+ * GPIO controller
+ */
+#define RPI3_IO_GPIO_OFFSET		ULL(0x00200000)
+#define RPI3_GPIO_BASE			(RPI3_IO_BASE + RPI3_IO_GPIO_OFFSET)
+
+/*
  * Local interrupt controller
  */
 #define RPI3_INTC_BASE_ADDRESS			ULL(0x40000000)