diff options
author | Vincent Palatin <vpalatin@chromium.org> | 2014-12-04 14:37:16 -0800 |
---|---|---|
committer | chrome-internal-fetch <chrome-internal-fetch@google.com> | 2014-12-10 22:14:07 +0000 |
commit | 1b1c3089afada7b53e7836ce2b98c157f36a44a8 (patch) | |
tree | b741f174426d280347de3d0c87032ec241095c97 | |
parent | 8decf722c52cd03681d4c6a900722a11388b59f3 (diff) | |
download | chrome-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.c | 16 | ||||
-rw-r--r-- | board/cr50/board.h | 7 | ||||
-rw-r--r-- | board/cr50/gpio.inc | 18 | ||||
-rw-r--r-- | chip/g/gpio.c | 162 | ||||
-rw-r--r-- | chip/g/registers.h | 57 | ||||
-rw-r--r-- | chip/g/uart.c | 12 | ||||
-rw-r--r-- | include/config.h | 10 | ||||
-rw-r--r-- | include/gpio.h | 14 | ||||
-rw-r--r-- | include/gpio_list.h | 2 |
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[] = { |