summaryrefslogtreecommitdiff
path: root/chip/g/gpio.c
diff options
context:
space:
mode:
authorBill Richardson <wfrichar@chromium.org>2015-02-11 16:33:43 -0800
committerChromeOS Commit Bot <chromeos-commit-bot@chromium.org>2015-02-20 03:00:04 +0000
commit02013f6aa3b5fbc64b15ec060e5fc5e87824353b (patch)
treed0194cb774916ce796e3fc0c14e3ccf81f65fd93 /chip/g/gpio.c
parentc955bd846da9d1d7e4b354da6dcacf8b941fe2e7 (diff)
downloadchrome-ec-02013f6aa3b5fbc64b15ec060e5fc5e87824353b.tar.gz
cr50: Separate ARM core GPIOs from pinmux configuration
This separates the configuration of the ARM core GPIOs from the routing of internal peripherals to external pins. Both are still described in the gpio.inc file, but are less dependent on each other. BUG=chrome-os-partner:33818 BRANCH=none TEST=manual Before this CL, running "sysjump rw" or trying to use more than 8 GPIOs caused hangs and reboots. Now it doesn't. Change-Id: If962a7c5ad4136837b2ea00ae016a440f07d7e23 Signed-off-by: Bill Richardson <wfrichar@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/251015 Reviewed-by: Sheng-liang Song <ssl@chromium.org> Reviewed-by: Randall Spangler <rspangler@chromium.org>
Diffstat (limited to 'chip/g/gpio.c')
-rw-r--r--chip/g/gpio.c224
1 files changed, 122 insertions, 102 deletions
diff --git a/chip/g/gpio.c b/chip/g/gpio.c
index 5f2b682507..0dd6122d54 100644
--- a/chip/g/gpio.c
+++ b/chip/g/gpio.c
@@ -9,118 +9,137 @@
#include "registers.h"
#include "task.h"
-/* 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)
+/*
+ * The Cr50's ARM core has two GPIO ports of 16 bits each. Each GPIO signal
+ * can be routed through a full NxM crossbar to any of a number of external
+ * pins. When setting up GPIOs, both the ARM core and the crossbar must be
+ * configured correctly. This file is only concerned with the ARM core.
+ */
test_mockable int gpio_get_level(enum gpio_signal signal)
{
- uint32_t wi = signal >> GPIO_WORD_SIZE_LOG2;
- uint32_t mask = 1 << (signal & GPIO_WORD_MASK);
- return !!(GR_GPIO_DATAIN(wi) & mask);
+ const struct gpio_info *g = gpio_list + signal;
+ return !!(GR_GPIO_DATAIN(g->port) & g->mask);
}
-void gpio_set_level(enum gpio_signal signal, int value)
+static void set_one_gpio_bit(uint32_t port, uint16_t mask, int value)
{
- uint32_t wi = signal >> GPIO_WORD_SIZE_LOG2;
- uint32_t idx = signal & GPIO_WORD_MASK;
+ if (!mask)
+ return;
- GR_GPIO_MASKBYTE(wi, 1 << idx) = value << idx;
+ /* Assumes mask has one and only one bit set */
+ if (mask & 0x00FF)
+ GR_GPIO_MASKLOWBYTE(port, mask) = value ? mask : 0;
+ else
+ GR_GPIO_MASKHIGHBYTE(port, mask >> 8) = value ? mask : 0;
}
-static void gpio_set_flags_internal(enum gpio_signal signal, uint32_t port,
- uint32_t mask, uint32_t flags)
+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;
- uint32_t di = 31 - __builtin_clz(mask);
+ const struct gpio_info *g = gpio_list + signal;
+ set_one_gpio_bit(g->port, g->mask, value);
+}
- /* 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;
+void gpio_set_flags_by_mask(uint32_t port, uint32_t mask, uint32_t flags)
+{
+ /* Output must be enabled; input is always enabled */
+ if (flags & GPIO_OUTPUT)
+ GR_GPIO_SETDOUTEN(port) = mask;
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;
+ GR_GPIO_CLRDOUTEN(port) = mask;
+
+ /* Only matters for outputs */
+ if (flags & GPIO_LOW)
+ set_one_gpio_bit(port, mask, 0);
+ else if (flags & GPIO_HIGH)
+ set_one_gpio_bit(port, mask, 1);
+
+ /* Interrupt types */
+ if (flags & GPIO_INT_F_LOW) {
+ GR_GPIO_CLRINTTYPE(port) = mask;
+ GR_GPIO_CLRINTPOL(port) = mask;
+ GR_GPIO_SETINTEN(port) = mask;
}
-
- /* 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;
+ if (flags & GPIO_INT_F_HIGH) {
+ GR_GPIO_CLRINTTYPE(port) = mask;
+ GR_GPIO_SETINTPOL(port) = mask;
+ GR_GPIO_SETINTEN(port) = mask;
}
- /* 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;
+ if (flags & GPIO_INT_F_FALLING) {
+ GR_GPIO_SETINTTYPE(port) = mask;
+ GR_GPIO_CLRINTPOL(port) = mask;
+ GR_GPIO_SETINTEN(port) = mask;
+ }
+ if (flags & GPIO_INT_F_RISING) {
+ GR_GPIO_SETINTTYPE(port) = mask;
+ GR_GPIO_SETINTPOL(port) = mask;
+ GR_GPIO_SETINTEN(port) = mask;
}
- /* 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);
+ /* No way to trigger on both rising and falling edges, darn it. */
}
void gpio_set_alternate_function(uint32_t port, uint32_t mask, int func)
{
- uint32_t di = 31 - __builtin_clz(mask);
+ /* This HW feature is not present in the Cr50 ARM core */
+}
- /* 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;
+static void connect_pinmux(uint32_t signal, uint32_t dio, uint16_t flags)
+{
+ if (FIELD_IS_FUNC(signal)) {
+ /* Connect peripheral function to DIO */
+ if (flags & DIO_OUTPUT) {
+ /* drive DIO from peripheral */
+ DIO_SEL_REG(dio) = PERIPH_FUNC(signal);
+ }
+
+ if (flags & DIO_INPUT) {
+ /* drive peripheral from DIO */
+ PERIPH_SEL_REG(signal) = DIO_FUNC(dio);
+ /* enable digital input */
+ REG_WRITE_MLV(DIO_CTL_REG(dio), DIO_CTL_IE_MASK,
+ DIO_CTL_IE_LSB, 1);
+ }
+ } else {
+ /* Connect GPIO to DIO */
+ const struct gpio_info *g = gpio_list + FIELD_GET_GPIO(signal);
+ int bitnum = 31 - __builtin_clz(g->mask);
+
+ if ((g->flags & GPIO_OUTPUT) || (flags & DIO_OUTPUT)) {
+ /* drive DIO output from GPIO */
+ DIO_SEL_REG(dio) = GET_GPIO_FUNC(g->port, bitnum);
+ }
+
+ if ((g->flags & GPIO_INPUT) || (flags & DIO_INPUT)) {
+ /* drive GPIO input from DIO */
+ GET_GPIO_SEL_REG(g->port, bitnum) = DIO_FUNC(dio);
+ /* enable digital input */
+ REG_WRITE_MLV(DIO_CTL_REG(dio), DIO_CTL_IE_MASK,
+ DIO_CTL_IE_LSB, 1);
+ }
+ }
}
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;
-
+ const struct gpio_info *g = gpio_list + signal;
+ GR_GPIO_SETINTEN(g->port) = g->mask;
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;
-
+ const struct gpio_info *g = gpio_list + signal;
+ GR_GPIO_CLRINTEN(g->port) = g->mask;
return EC_SUCCESS;
}
void gpio_pre_init(void)
{
const struct gpio_info *g = gpio_list;
+ const struct gpio_alt_func *af = gpio_alt_funcs;
+
int i;
/* Enable clocks */
@@ -131,22 +150,14 @@ void gpio_pre_init(void)
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);
- }
+ /* Set up the pinmux */
+ for (i = 0; i < gpio_alt_funcs_count; i++, af++)
+ connect_pinmux(af->port, af->mask, af->flags);
+
+ /* Set up ARM core GPIOs */
+ for (i = 0; i < GPIO_COUNT; i++, g++)
+ if (g->mask && !(g->flags & GPIO_DEFAULT))
+ gpio_set_flags_by_mask(g->port, g->mask, g->flags);
}
static void gpio_init(void)
@@ -157,30 +168,39 @@ static void gpio_init(void)
DECLARE_HOOK(HOOK_INIT, gpio_init, HOOK_PRIO_DEFAULT);
/*****************************************************************************/
-/* Interrupt handler */
+/* Interrupt handler stuff */
-static void gpio_interrupt(int wi)
+static void gpio_invoke_handler(uint32_t port, uint32_t mask)
{
- int idx;
const struct gpio_info *g = gpio_list;
- uint32_t pending = GR_GPIO_CLRINTSTAT(wi);
+ int i;
+ for (i = 0; i < GPIO_COUNT; i++, g++)
+ if (g->irq_handler && port == g->port && (mask & g->mask))
+ g->irq_handler(i);
+}
+
+static void gpio_interrupt(int port)
+{
+ int bitnum;
+ uint32_t mask;
+ uint32_t pending = GR_GPIO_CLRINTSTAT(port);
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;
+ bitnum = 31 - __builtin_clz(pending);
+ mask = 1 << bitnum;
+ pending &= ~mask;
+ gpio_invoke_handler(port, mask);
+ GR_GPIO_CLRINTSTAT(port) = mask;
}
}
void _gpio0_interrupt(void)
{
- gpio_interrupt(0);
+ gpio_interrupt(GPIO_0);
}
void _gpio1_interrupt(void)
{
- gpio_interrupt(1);
+ gpio_interrupt(GPIO_1);
}
DECLARE_IRQ(GC_IRQNUM_GPIO0_GPIOCOMBINT, _gpio0_interrupt, 1);
DECLARE_IRQ(GC_IRQNUM_GPIO1_GPIOCOMBINT, _gpio1_interrupt, 1);