summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVincent Palatin <vpalatin@chromium.org>2014-12-04 14:37:16 -0800
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2014-12-10 22:14:07 +0000
commit1b1c3089afada7b53e7836ce2b98c157f36a44a8 (patch)
treeb741f174426d280347de3d0c87032ec241095c97
parent8decf722c52cd03681d4c6a900722a11388b59f3 (diff)
downloadchrome-ec-1b1c3089afada7b53e7836ce2b98c157f36a44a8.tar.gz
g: implement GPIOs
Add a driver for the GPIO controller. Signed-off-by: Vincent Palatin <vpalatin@chromium.org> BRANCH=none BUG=chrome-os-partner:33816 TEST=press the push buttons on the board and see the console text message and the LEDs lighting up. Change-Id: Idb408fe1c341beb8a97f2047ba6740e0d40cedf5 Reviewed-on: https://chromium-review.googlesource.com/233307 Trybot-Ready: Vincent Palatin <vpalatin@chromium.org> Tested-by: Vincent Palatin <vpalatin@chromium.org> Reviewed-by: Bill Richardson <wfrichar@chromium.org> Commit-Queue: Vincent Palatin <vpalatin@chromium.org>
-rw-r--r--board/cr50/board.c16
-rw-r--r--board/cr50/board.h7
-rw-r--r--board/cr50/gpio.inc18
-rw-r--r--chip/g/gpio.c162
-rw-r--r--chip/g/registers.h57
-rw-r--r--chip/g/uart.c12
-rw-r--r--include/config.h10
-rw-r--r--include/gpio.h14
-rw-r--r--include/gpio_list.h2
9 files changed, 271 insertions, 27 deletions
diff --git a/board/cr50/board.c b/board/cr50/board.c
index cc5774117c..31bfdf036a 100644
--- a/board/cr50/board.c
+++ b/board/cr50/board.c
@@ -4,17 +4,29 @@
*/
#include "common.h"
+#include "console.h"
#include "gpio.h"
#include "hooks.h"
+#include "registers.h"
#include "task.h"
#include "util.h"
#include "gpio_list.h"
+void button_event(enum gpio_signal signal)
+{
+ int val = gpio_get_level(signal);
+ ccprintf("Button %d = %d\n", signal, gpio_get_level(signal));
+
+ gpio_set_level(GPIO_LED_4, val);
+}
+
/* Initialize board. */
static void board_init(void)
{
- /* TODO(crosbug.com/p/33812): Try enabling this */
- /* gpio_enable_interrupt(GPIO_CAMO0_BREACH_INT); */
+ gpio_enable_interrupt(GPIO_SW_N);
+ gpio_enable_interrupt(GPIO_SW_S);
+ gpio_enable_interrupt(GPIO_SW_W);
+ gpio_enable_interrupt(GPIO_SW_E);
}
DECLARE_HOOK(HOOK_INIT, board_init, HOOK_PRIO_DEFAULT);
diff --git a/board/cr50/board.h b/board/cr50/board.h
index 4bcaaa99e6..83f871c8f6 100644
--- a/board/cr50/board.h
+++ b/board/cr50/board.h
@@ -15,8 +15,8 @@
#undef CONFIG_HIBERNATE
#undef CONFIG_LID_SWITCH
-/* How do I identify nonexistant GPIOs? */
-#define DUMMY_GPIO_BANK -1
+/* Need space to store the pin muxing configuration */
+#define CONFIG_GPIO_LARGE_ALT_INFO
/*
* Allow dangerous commands all the time, since we don't have a write protect
@@ -28,6 +28,9 @@
#include "gpio_signal.h"
+/* user button interrupt handler */
+void button_event(enum gpio_signal signal);
+
#endif /* !__ASSEMBLER__ */
#endif /* __BOARD_H */
diff --git a/board/cr50/gpio.inc b/board/cr50/gpio.inc
index 11808fb852..69bfe4aec5 100644
--- a/board/cr50/gpio.inc
+++ b/board/cr50/gpio.inc
@@ -5,12 +5,26 @@
*/
/* Inputs with interrupt handlers are first for efficiency */
-/* TODO(crosbug.com/p/33816): try enabling this */
-/* GPIO(CAMO0_BREACH_INT, A, 0, GPIO_INT_BOTH, button_event) */
+
+/* User Push buttons */
+GPIO(SW_N, M, 0, GPIO_INT_BOTH, button_event)
+GPIO(SW_S, M, 1, GPIO_INT_BOTH, button_event)
+GPIO(SW_W, M, 2, GPIO_INT_BOTH, button_event)
+GPIO(SW_E, M, 3, GPIO_INT_BOTH, button_event)
/* Outputs */
+/* User GPIO LEDs */
+GPIO(LED_2, A, 9, GPIO_OUT_HIGH, NULL)
+GPIO(LED_3, A, 10, GPIO_OUT_LOW, NULL)
+GPIO(LED_4, A, 11, GPIO_OUT_LOW, NULL)
+GPIO(LED_5, A, 12, GPIO_OUT_LOW, NULL)
+GPIO(LED_6, A, 13, GPIO_OUT_LOW, NULL)
+/*GPIO(LED_7, A, 14, GPIO_OUT_LOW, NULL)*/
+
/* Unimplemented signals which we need to emulate for now */
UNIMPLEMENTED(ENTERING_RW)
/* Alternate functions */
+ALTERNATE(A, 0x0001, PINMUX(UART0_TX), MODULE_UART, 0) /* UART0 TX: DIOA0 */
+ALTERNATE(A, 0x0002, PINMUX(UART0_RX), MODULE_UART, 0) /* UART0 RX: DIOA1 */
diff --git a/chip/g/gpio.c b/chip/g/gpio.c
index 01f73e5344..5f2b682507 100644
--- a/chip/g/gpio.c
+++ b/chip/g/gpio.c
@@ -6,23 +6,181 @@
#include "common.h"
#include "gpio.h"
#include "hooks.h"
+#include "registers.h"
+#include "task.h"
-/* TODO(crosbug.com/p/33816): We don't have any GPIOs defined yet! */
+/* GPIOs are split into 2 words of 16 GPIOs */
+#define GPIO_WORD_SIZE_LOG2 4
+#define GPIO_WORD_SIZE (1 << GPIO_WORD_SIZE_LOG2)
+#define GPIO_WORD_MASK (GPIO_WORD_SIZE - 1)
test_mockable int gpio_get_level(enum gpio_signal signal)
{
- return 0;
+ uint32_t wi = signal >> GPIO_WORD_SIZE_LOG2;
+ uint32_t mask = 1 << (signal & GPIO_WORD_MASK);
+ return !!(GR_GPIO_DATAIN(wi) & mask);
}
void gpio_set_level(enum gpio_signal signal, int value)
{
+ uint32_t wi = signal >> GPIO_WORD_SIZE_LOG2;
+ uint32_t idx = signal & GPIO_WORD_MASK;
+
+ GR_GPIO_MASKBYTE(wi, 1 << idx) = value << idx;
+}
+
+static void gpio_set_flags_internal(enum gpio_signal signal, uint32_t port,
+ uint32_t mask, uint32_t flags)
+{
+ uint32_t wi = signal >> GPIO_WORD_SIZE_LOG2;
+ uint32_t idx = signal & GPIO_WORD_MASK;
+ uint32_t di = 31 - __builtin_clz(mask);
+
+ /* Set up pullup / pulldown */
+ if (flags & GPIO_PULL_UP)
+ GR_PINMUX_DIO_CTL(port, di) |= PINMUX_DIO_CTL_PU;
+ else
+ GR_PINMUX_DIO_CTL(port, di) &= ~PINMUX_DIO_CTL_PU;
+ if (flags & GPIO_PULL_DOWN)
+ GR_PINMUX_DIO_CTL(port, di) |= PINMUX_DIO_CTL_PD;
+ else
+ GR_PINMUX_DIO_CTL(port, di) &= ~PINMUX_DIO_CTL_PD;
+
+ if (flags & GPIO_OUTPUT) {
+ /* Set pin level first to avoid glitching. */
+ GR_GPIO_MASKBYTE(wi, 1 << idx) = !!(flags & GPIO_HIGH) << idx;
+ /* Switch Output Enable */
+ GR_GPIO_SETDOUTEN(wi) = 1 << idx;
+ } else if (flags & GPIO_INPUT) {
+ GR_GPIO_CLRDOUTEN(wi) = 1 << idx;
+ }
+
+ /* Edge-triggered interrupt */
+ if (flags & (GPIO_INT_F_FALLING | GPIO_INT_F_RISING)) {
+ GR_GPIO_SETINTTYPE(wi) = 1 << idx;
+ if (flags & GPIO_INT_F_RISING)
+ GR_GPIO_SETINTPOL(wi) = 1 << idx;
+ else
+ GR_GPIO_CLRINTPOL(wi) = 1 << idx;
+ }
+ /* Level-triggered interrupt */
+ if (flags & (GPIO_INT_F_LOW | GPIO_INT_F_HIGH)) {
+ GR_GPIO_CLRINTTYPE(wi) = 1 << idx;
+ if (flags & GPIO_INT_F_HIGH)
+ GR_GPIO_SETINTPOL(wi) = 1 << idx;
+ else
+ GR_GPIO_CLRINTPOL(wi) = 1 << idx;
+ }
+ /* Interrupt is enabled by gpio_enable_interrupt() */
+}
+
+void gpio_set_flags_by_mask(uint32_t port, uint32_t mask, uint32_t flags)
+{
+ const struct gpio_info *g = gpio_list;
+ int i;
+
+ for (i = 0; i < GPIO_COUNT; i++, g++)
+ if ((g->port == port) && (g->mask & mask))
+ gpio_set_flags_internal(i, port, mask, flags);
+}
+
+void gpio_set_alternate_function(uint32_t port, uint32_t mask, int func)
+{
+ uint32_t di = 31 - __builtin_clz(mask);
+
+ /* Connect the DIO pad to a specific function */
+ GR_PINMUX_DIO_SEL(port, di) = PINMUX_FUNC(func);
+ /* Connect the specific function to the DIO pad */
+ PINMUX_SEL_REG(func) = PINMUX_DIO_SEL(port, di);
+
+ /* Input Enable (if pad used for digital function) */
+ GR_PINMUX_DIO_CTL(port, di) |= PINMUX_DIO_CTL_IE;
+}
+
+int gpio_enable_interrupt(enum gpio_signal signal)
+{
+ uint32_t wi = signal >> GPIO_WORD_SIZE_LOG2;
+ uint32_t idx = signal & GPIO_WORD_MASK;
+
+ GR_GPIO_SETINTEN(wi) = 1 << idx;
+
+ return EC_SUCCESS;
+}
+
+int gpio_disable_interrupt(enum gpio_signal signal)
+{
+ uint32_t wi = signal >> GPIO_WORD_SIZE_LOG2;
+ uint32_t idx = signal & GPIO_WORD_MASK;
+
+ GR_GPIO_CLRINTEN(wi) = 1 << idx;
+ GR_GPIO_CLRINTSTAT(wi) = 1 << idx;
+
+ return EC_SUCCESS;
}
void gpio_pre_init(void)
{
+ const struct gpio_info *g = gpio_list;
+ int i;
+
+ /* Enable clocks */
+ REG_WRITE_MLV(GR_PMU_PERICLKSET0,
+ GC_PMU_PERICLKSET0_DGPIO0_MASK,
+ GC_PMU_PERICLKSET0_DGPIO0_LSB, 1);
+ REG_WRITE_MLV(GR_PMU_PERICLKSET0,
+ GC_PMU_PERICLKSET0_DGPIO1_MASK,
+ GC_PMU_PERICLKSET0_DGPIO1_LSB, 1);
+
+ /* Set all GPIOs to defaults */
+ for (i = 0; i < GPIO_COUNT; i++, g++) {
+ uint32_t di = 31 - __builtin_clz(g->mask);
+ uint32_t wi = i >> GPIO_WORD_SIZE_LOG2;
+ uint32_t idx = i & GPIO_WORD_MASK;
+
+ if (g->flags & GPIO_DEFAULT)
+ continue;
+
+ /* Set up GPIO based on flags */
+ gpio_set_flags_internal(i, g->port, g->mask, g->flags);
+ /* Connect the GPIO to the DIO pin through the muxing matrix */
+ GR_PINMUX_DIO_CTL(g->port, di) |= PINMUX_DIO_CTL_IE;
+ GR_PINMUX_DIO_SEL(g->port, di) = PINMUX_GPIO_SEL(wi, idx);
+ GR_PINMUX_GPIO_SEL(wi, idx) = PINMUX_DIO_SEL(g->port, di);
+ }
}
static void gpio_init(void)
{
+ task_enable_irq(GC_IRQNUM_GPIO0_GPIOCOMBINT);
+ task_enable_irq(GC_IRQNUM_GPIO1_GPIOCOMBINT);
}
DECLARE_HOOK(HOOK_INIT, gpio_init, HOOK_PRIO_DEFAULT);
+
+/*****************************************************************************/
+/* Interrupt handler */
+
+static void gpio_interrupt(int wi)
+{
+ int idx;
+ const struct gpio_info *g = gpio_list;
+ uint32_t pending = GR_GPIO_CLRINTSTAT(wi);
+
+ while (pending) {
+ idx = get_next_bit(&pending) + wi * 16;
+ if (g[idx].irq_handler)
+ g[idx].irq_handler(idx);
+ /* clear the interrupt */
+ GR_GPIO_CLRINTSTAT(wi) = 1 << idx;
+ }
+}
+
+void _gpio0_interrupt(void)
+{
+ gpio_interrupt(0);
+}
+void _gpio1_interrupt(void)
+{
+ gpio_interrupt(1);
+}
+DECLARE_IRQ(GC_IRQNUM_GPIO0_GPIOCOMBINT, _gpio0_interrupt, 1);
+DECLARE_IRQ(GC_IRQNUM_GPIO1_GPIOCOMBINT, _gpio1_interrupt, 1);
diff --git a/chip/g/registers.h b/chip/g/registers.h
index 59538a5a5e..51c3ee0c35 100644
--- a/chip/g/registers.h
+++ b/chip/g/registers.h
@@ -19,13 +19,6 @@
#define GR_SWDP_BUILD_DATE REG32(GC_SWDP0_BASE_ADDR + GC_SWDP_BUILD_DATE_OFFSET)
#define GR_SWDP_BUILD_TIME REG32(GC_SWDP0_BASE_ADDR + GC_SWDP_BUILD_TIME_OFFSET)
-#define GR_PINMUX_DIOA0_SEL REG32(GC_PINMUX_BASE_ADDR + GC_PINMUX_DIOA0_SEL_OFFSET)
-#define GR_PINMUX_DIOA0_CTL REG32(GC_PINMUX_BASE_ADDR + GC_PINMUX_DIOA0_CTL_OFFSET)
-#define GR_PINMUX_DIOA1_SEL REG32(GC_PINMUX_BASE_ADDR + GC_PINMUX_DIOA1_SEL_OFFSET)
-#define GR_PINMUX_DIOA1_CTL REG32(GC_PINMUX_BASE_ADDR + GC_PINMUX_DIOA1_CTL_OFFSET)
-#define GR_PINMUX_UART0_RX_SEL REG32(GC_PINMUX_BASE_ADDR + GC_PINMUX_UART0_RX_SEL_OFFSET)
-#define GR_PINMUX_UART0_TX_SEL REG32(GC_PINMUX_BASE_ADDR + GC_PINMUX_UART0_TX_SEL_OFFSET)
-
/* Power Management Unit */
#define GR_PMU_REG(off) REG32(GC_PMU_BASE_ADDR + (off))
@@ -110,6 +103,56 @@ static inline int x_uart_addr(int ch, int offset)
#define GR_UART_FIFO(ch) X_UARTREG(ch, GC_UART_FIFO_OFFSET)
#define GR_UART_RFIFO(ch) X_UARTREG(ch, GC_UART_RFIFO_OFFSET)
+/* GPIOs & PIN muxing */
+#define GPIO_M_COUNT 5
+#define GPIO_A_COUNT 15
+#define GPIO_B_COUNT 9
+
+/* GPIO bank index is the number of the first GPIO of the bank */
+#define GPIO_M 0
+#define GPIO_A GPIO_M_COUNT
+#define GPIO_B (GPIO_M_COUNT + GPIO_A_COUNT)
+#define DUMMY_GPIO_BANK GPIO_M
+
+#define GR_PINMUX_DIO_SEL(bank, di) REG32(GC_PINMUX_BASE_ADDR + ((bank) + (di))*8)
+#define GR_PINMUX_DIO_CTL(bank, di) REG32(GC_PINMUX_BASE_ADDR + ((bank) + (di))*8 + 4)
+
+#define GR_PINMUX_GPIO_SEL(wi, idx) REG32(GC_PINMUX_BASE_ADDR + GC_PINMUX_GPIO0_GPIO0_SEL_OFFSET + ((wi)*16 + (idx))*4)
+
+#define PINMUX_DIO_SEL(bank, di) (GC_PINMUX_DIOM0_SEL + (di) + (bank))
+#define PINMUX_GPIO_SEL(wi, idx) (GC_PINMUX_GPIO0_GPIO0_SEL + (idx) + (wi)*16)
+
+#define PINMUX_DIO_CTL_DS(ds) (((ds) & PINMUX_DIOA0_CTL_DS_GC_PINMUX_DIOA0_CTL_DS_MASK) << GC_PINMUX_DIOA0_CTL_DS_LSB)
+#define PINMUX_DIO_CTL_IE (1 << GC_PINMUX_DIOA0_CTL_IE_LSB)
+#define PINMUX_DIO_CTL_PD (1 << GC_PINMUX_DIOA0_CTL_PD_LSB)
+#define PINMUX_DIO_CTL_PU (1 << GC_PINMUX_DIOA0_CTL_PU_LSB)
+#define PINMUX_DIO_CTL_INV (1 << GC_PINMUX_DIOA0_CTL_INV_LSB)
+
+/*
+ * To store the alternate function pin muxing in a 32-bit integer :
+ * put the function index selector in the low 16 bits,
+ * and the selector register offset in the high 16 bits.
+ */
+#define PINMUX(func) (int)(CONCAT3(GC_PINMUX_, func, _SEL) | \
+ (CONCAT3(GC_PINMUX_, func, _SEL_OFFSET) << 16))
+#define PINMUX_SEL_REG(word) REG32(GC_PINMUX_BASE_ADDR + ((uint32_t)(word) >> 16))
+#define PINMUX_FUNC(word) ((uint32_t)(word) & 0xffff)
+
+
+#define GR_GPIO_REG(n, off) REG16(GC_GPIO0_BASE_ADDR + (n)*0x10000 + (off))
+#define GR_GPIO_DATAIN(n) GR_GPIO_REG(n, GC_GPIO_DATAIN_OFFSET)
+#define GR_GPIO_DOUT(n) GR_GPIO_REG(n, GC_GPIO_DOUT_OFFSET)
+#define GR_GPIO_SETDOUTEN(n) GR_GPIO_REG(n, GC_GPIO_SETDOUTEN_OFFSET)
+#define GR_GPIO_CLRDOUTEN(n) GR_GPIO_REG(n, GC_GPIO_CLRDOUTEN_OFFSET)
+#define GR_GPIO_SETINTEN(n) GR_GPIO_REG(n, GC_GPIO_SETINTEN_OFFSET)
+#define GR_GPIO_CLRINTEN(n) GR_GPIO_REG(n, GC_GPIO_CLRINTEN_OFFSET)
+#define GR_GPIO_SETINTTYPE(n) GR_GPIO_REG(n, GC_GPIO_SETINTTYPE_OFFSET)
+#define GR_GPIO_CLRINTTYPE(n) GR_GPIO_REG(n, GC_GPIO_CLRINTTYPE_OFFSET)
+#define GR_GPIO_SETINTPOL(n) GR_GPIO_REG(n, GC_GPIO_SETINTPOL_OFFSET)
+#define GR_GPIO_CLRINTPOL(n) GR_GPIO_REG(n, GC_GPIO_CLRINTPOL_OFFSET)
+#define GR_GPIO_CLRINTSTAT(n) GR_GPIO_REG(n, GC_GPIO_CLRINTSTAT_OFFSET)
+
+#define GR_GPIO_MASKBYTE(n, m) GR_GPIO_REG(n, GC_GPIO_MASKLOWBYTE_400_OFFSET + (m)*4)
/*
* High-speed timers. Two modules with two timers each; four timers total.
diff --git a/chip/g/uart.c b/chip/g/uart.c
index 639c3d7b50..d43c4d4fb1 100644
--- a/chip/g/uart.c
+++ b/chip/g/uart.c
@@ -5,6 +5,7 @@
#include "clock.h"
#include "common.h"
+#include "gpio.h"
#include "registers.h"
#include "system.h"
#include "task.h"
@@ -137,16 +138,7 @@ void uart_init(void)
clock_enable_module(MODULE_UART, 1);
/* set up pinmux */
- GR_PINMUX_DIOA0_SEL = GC_PINMUX_UART0_TX_SEL;
- GR_PINMUX_UART0_RX_SEL = GC_PINMUX_DIOA1_SEL;
-
- /* IE must be set to 1 to work as a digital pad (for any direction) */
- /* turn on input driver (IE field) */
- REG_WRITE_MLV(GR_PINMUX_DIOA0_CTL, GC_PINMUX_DIOA0_CTL_IE_MASK,
- GC_PINMUX_DIOA0_CTL_IE_LSB, 1);
- /* turn on input driver (IE field) */
- REG_WRITE_MLV(GR_PINMUX_DIOA1_CTL, GC_PINMUX_DIOA1_CTL_IE_MASK,
- GC_PINMUX_DIOA1_CTL_IE_LSB, 1);
+ gpio_config_module(MODULE_UART, 1);
/* set frequency */
GR_UART_NCO(0) = setting;
diff --git a/include/config.h b/include/config.h
index 1eafd6368a..084b75be0d 100644
--- a/include/config.h
+++ b/include/config.h
@@ -588,6 +588,16 @@
#endif
+/*****************************************************************************/
+
+/*
+ * Use a larger word (32 bits) to store the pin muxing configuration
+ * (aka alternate function).
+ *
+ * Less optimal for storage size and alignment, but might be required for
+ * platforms with very flexible pin muxing.
+ */
+#undef CONFIG_GPIO_LARGE_ALT_INFO
/*****************************************************************************/
diff --git a/include/gpio.h b/include/gpio.h
index 3f76d43bfd..7d460a27f5 100644
--- a/include/gpio.h
+++ b/include/gpio.h
@@ -69,6 +69,18 @@ struct gpio_info {
/* Signal information from board.c. Must match order from enum gpio_signal. */
extern const struct gpio_info gpio_list[];
+/*
+ * Define the storage size for the pin muxing information.
+ *
+ * int8_t is more optimal for storage size and alignment,
+ * but some chips require to store more information.
+ */
+#ifdef CONFIG_GPIO_LARGE_ALT_INFO
+typedef uint32_t alt_func_t;
+#else
+typedef int8_t alt_func_t;
+#endif
+
/* GPIO alternate function structure, for use by board.c */
struct gpio_alt_func {
/* Port base address */
@@ -78,7 +90,7 @@ struct gpio_alt_func {
uint32_t mask;
/* Alternate function number */
- int8_t func;
+ alt_func_t func;
/* Module ID (as uint8_t, since enum would be 32-bit) */
uint8_t module_id;
diff --git a/include/gpio_list.h b/include/gpio_list.h
index 9077c17fd2..4ea1da27a3 100644
--- a/include/gpio_list.h
+++ b/include/gpio_list.h
@@ -7,7 +7,7 @@
{#name, GPIO_##port, (1 << pin), flags, signal},
#define UNIMPLEMENTED(name) \
- {#name, DUMMY_GPIO_BANK, 0, 0, NULL},
+ {#name, DUMMY_GPIO_BANK, 0, GPIO_DEFAULT, NULL},
/* GPIO signal list. */
const struct gpio_info gpio_list[] = {