diff options
author | Vic (Chun-Ju) Yang <victoryang@chromium.org> | 2013-11-21 15:34:07 +0800 |
---|---|---|
committer | chrome-internal-fetch <chrome-internal-fetch@google.com> | 2013-11-26 19:35:27 +0000 |
commit | 009dd17588c8b1f0160e1e7c2acf3a4a4bb9dd80 (patch) | |
tree | 4511831edd2bd11d22b9f503b2e140995ec7bded /chip/mec1322 | |
parent | 0892ff6418e615b6f52a4010cf94a6f28b675511 (diff) | |
download | chrome-ec-009dd17588c8b1f0160e1e7c2acf3a4a4bb9dd80.tar.gz |
mec1322: Add GPIO interrupt support
With this, we can now define and trigger interrupt on GPIO status.
BUG=chrome-os-partner:24107
TEST=Test GPIO036 with following cases:
- Pulled up and rising edge trigger. Pull down externally and
then release.
- Pulled up and falling edge trigger. Pull down externally.
- Pulled up and both edge trigger. Pull down and then release.
- Pulled up and low level trigger. Pull down externally.
BRANCH=None
Change-Id: Id9bfd2ba9dd8a75bcf2c5691ffe2aa6518076925
Signed-off-by: Vic (Chun-Ju) Yang <victoryang@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/177560
Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
Diffstat (limited to 'chip/mec1322')
-rw-r--r-- | chip/mec1322/gpio.c | 119 |
1 files changed, 116 insertions, 3 deletions
diff --git a/chip/mec1322/gpio.c b/chip/mec1322/gpio.c index 065b4383ec..6569985c67 100644 --- a/chip/mec1322/gpio.c +++ b/chip/mec1322/gpio.c @@ -7,10 +7,27 @@ #include "common.h" #include "gpio.h" +#include "hooks.h" #include "registers.h" +#include "task.h" #include "timer.h" #include "util.h" +struct gpio_int_mapping { + int8_t girq_id; + int8_t port_offset; +}; + +/* Mapping from GPIO port to GIRQ info */ +static const struct gpio_int_mapping int_map[22] = { + {11, 0}, {11, 0}, {11, 0}, {11, 0}, + {10, 4}, {10, 4}, {10, 4}, {-1, -1}, + {-1, -1}, {-1, -1}, {9, 10}, {9, 10}, + {9, 10}, {9, 10}, {8, 14}, {8, 14}, + {8, 14}, {-1, -1}, {-1, -1}, {-1, -1}, + {20, 20}, {20, 20} +}; + void gpio_set_alternate_function(uint32_t port, uint32_t mask, int func) { int i; @@ -82,7 +99,24 @@ void gpio_set_flags_by_mask(uint32_t port, uint32_t mask, uint32_t flags) else val &= ~0x3; - /* TODO(crosbug.com/p/24107): Set up interrupt */ + /* Set up interrupt */ + if (flags & (GPIO_INT_F_RISING | GPIO_INT_F_FALLING)) + val |= (1 << 7); + else + val &= ~(1 << 7); + + val &= ~(0x7 << 4); + + if ((flags & GPIO_INT_F_RISING) && (flags & GPIO_INT_F_FALLING)) + val |= 0x7 << 4; + else if (flags & GPIO_INT_F_RISING) + val |= 0x5 << 4; + else if (flags & GPIO_INT_F_FALLING) + val |= 0x6 << 4; + else if (flags & GPIO_INT_F_HIGH) + val |= 0x1 << 4; + else if (!(flags & GPIO_INT_F_LOW)) /* No interrupt flag set */ + val |= 0x4 << 4; /* Use as GPIO */ val &= ~((1 << 12) | (1 << 13)); @@ -98,12 +132,27 @@ void gpio_set_flags_by_mask(uint32_t port, uint32_t mask, uint32_t flags) int gpio_enable_interrupt(enum gpio_signal signal) { - return EC_ERROR_UNIMPLEMENTED; + int i = 31 - __builtin_clz(gpio_list[signal].mask); + int port = gpio_list[signal].port; + int girq_id = int_map[port].girq_id; + int bit_id = (port - int_map[port].port_offset) * 8 + i; + + MEC1322_INT_ENABLE(girq_id) |= (1 << bit_id); + MEC1322_INT_BLK_EN |= (1 << girq_id); + + return EC_SUCCESS; } int gpio_disable_interrupt(enum gpio_signal signal) { - return EC_ERROR_UNIMPLEMENTED; + int i = 31 - __builtin_clz(gpio_list[signal].mask); + int port = gpio_list[signal].port; + int girq_id = int_map[port].girq_id; + int bit_id = (port - int_map[port].port_offset) * 8 + i; + + MEC1322_INT_DISABLE(girq_id) |= (1 << bit_id); + + return EC_SUCCESS; } void gpio_pre_init(void) @@ -114,3 +163,67 @@ void gpio_pre_init(void) for (i = 0; i < GPIO_COUNT; i++, g++) gpio_set_flags_by_mask(g->port, g->mask, g->flags); } + +static void gpio_init(void) +{ + task_enable_irq(MEC1322_IRQ_GIRQ8); + task_enable_irq(MEC1322_IRQ_GIRQ9); + task_enable_irq(MEC1322_IRQ_GIRQ10); + task_enable_irq(MEC1322_IRQ_GIRQ11); + task_enable_irq(MEC1322_IRQ_GIRQ20); +} +DECLARE_HOOK(HOOK_INIT, gpio_init, HOOK_PRIO_DEFAULT); + +/*****************************************************************************/ +/* Interrupt handlers */ + + +/** + * Handler for each GIRQ interrupt. This reads and clears the interrupt bits for + * the GIRQ interrupt, then finds and calls the corresponding GPIO interrupt + * handlers. + * + * @param girq GIRQ index + * @param port_offset GPIO port offset for the given GIRQ + */ +static void gpio_interrupt(int girq, int port_offset) +{ + int i, bit; + const struct gpio_info *g = gpio_list; + uint32_t sts = MEC1322_INT_RESULT(girq); + + MEC1322_INT_SOURCE(girq) |= sts; + + for (i = 0; i < GPIO_COUNT && sts; ++i, ++g) { + if (!g->irq_handler) + continue; + bit = (g->port - port_offset) * 8 + __builtin_ffs(g->mask) - 1; + if (sts & (1 << bit)) + g->irq_handler(i); + sts &= ~(1 << bit); + } +} + +#define GPIO_IRQ_FUNC(irqfunc, girq, port_offset) \ + static void irqfunc(void) \ + { \ + gpio_interrupt(girq, port_offset); \ + } + +GPIO_IRQ_FUNC(__girq_8_interrupt, 8, 14); +GPIO_IRQ_FUNC(__girq_9_interrupt, 9, 10); +GPIO_IRQ_FUNC(__girq_10_interrupt, 10, 4); +GPIO_IRQ_FUNC(__girq_11_interrupt, 11, 0); +GPIO_IRQ_FUNC(__girq_20_interrupt, 20, 20); + +#undef GPIO_IRQ_FUNC + +/* + * Declare IRQs. Nesting this macro inside the GPIO_IRQ_FUNC macro works + * poorly because DECLARE_IRQ() stringizes its inputs. + */ +DECLARE_IRQ(MEC1322_IRQ_GIRQ8, __girq_8_interrupt, 1); +DECLARE_IRQ(MEC1322_IRQ_GIRQ9, __girq_9_interrupt, 1); +DECLARE_IRQ(MEC1322_IRQ_GIRQ10, __girq_10_interrupt, 1); +DECLARE_IRQ(MEC1322_IRQ_GIRQ11, __girq_11_interrupt, 1); +DECLARE_IRQ(MEC1322_IRQ_GIRQ20, __girq_20_interrupt, 1); |