diff options
author | Randall Spangler <rspangler@chromium.org> | 2013-03-29 16:34:02 -0700 |
---|---|---|
committer | ChromeBot <chrome-bot@google.com> | 2013-04-02 14:12:55 -0700 |
commit | 5966f22a8a1928583943b57fc22e72f85d69d079 (patch) | |
tree | e49232c6d5368219faa63cae0ec5e01a4866f837 | |
parent | 97bf36c9d3ad3d96ad6dea7cd6e6f3e164297c43 (diff) | |
download | chrome-ec-5966f22a8a1928583943b57fc22e72f85d69d079.tar.gz |
Add support for calling deferred functions
This is a cleaner way of deferring work from interrupt-time to
task-time without requiring a task for each module which needs this.
Replaces/supersedes delayed hook notification, which didn't scale well
(since every function would have needed to be its own hook type).
BUG=chrome-os-partner:18473
BRANCH=none
TEST=boot system. plug/unplug AC power; notifies the host properly
Change-Id: I50263fe1ce37e74c1ef8db3671379098997102ed
Signed-off-by: Randall Spangler <rspangler@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/46953
Reviewed-by: Bill Richardson <wfrichar@chromium.org>
-rw-r--r-- | common/extpower_gpio.c | 11 | ||||
-rw-r--r-- | common/hooks.c | 114 | ||||
-rw-r--r-- | common/main.c | 8 | ||||
-rw-r--r-- | core/cortex-m/ec.lds.S | 4 | ||||
-rw-r--r-- | core/cortex-m/link_defs.h | 5 | ||||
-rw-r--r-- | include/hooks.h | 41 |
6 files changed, 147 insertions, 36 deletions
diff --git a/common/extpower_gpio.c b/common/extpower_gpio.c index 218905f91d..4643b245de 100644 --- a/common/extpower_gpio.c +++ b/common/extpower_gpio.c @@ -17,19 +17,22 @@ int extpower_is_present(void) } /** - * Handle notification of external power change; forward it to host. + * Deferred function to handle external power change */ -static void extpower_changed(void) +static void extpower_deferred(void) { + hook_notify(HOOK_AC_CHANGE); + + /* Forward notification to host */ if (extpower_is_present()) host_set_single_event(EC_HOST_EVENT_AC_CONNECTED); else host_set_single_event(EC_HOST_EVENT_AC_DISCONNECTED); } -DECLARE_HOOK(HOOK_AC_CHANGE, extpower_changed, HOOK_PRIO_DEFAULT); +DECLARE_DEFERRED(extpower_deferred); void extpower_interrupt(enum gpio_signal signal) { /* Trigger deferred notification of external power change */ - hook_notify(HOOK_AC_CHANGE); + hook_call_deferred(extpower_deferred, 0); } diff --git a/common/hooks.c b/common/hooks.c index 97b2da107c..6ccfaf56e5 100644 --- a/common/hooks.c +++ b/common/hooks.c @@ -12,6 +12,19 @@ #include "timer.h" #include "util.h" +#ifdef HOOK_DEBUG +#define CPUTS(outstr) cputs(CC_HOOK, outstr) +#define CPRINTF(format, args...) cprintf(CC_HOOK, format, ## args) +#else +#define CPUTS(outstr) +#define CPRINTF(format, args...) +#endif + +/* Maximum number of deferrable functions */ +#ifndef DEFERRABLE_MAX_COUNT +#define DEFERRABLE_MAX_COUNT 8 +#endif + struct hook_ptrs { const struct hook_data *start; const struct hook_data *end; @@ -36,17 +49,18 @@ static const struct hook_ptrs hook_list[] = { {__hooks_second, __hooks_second_end}, }; -static uint32_t pending_hooks; +/* Times for deferrable functions */ +static uint64_t defer_until[DEFERRABLE_MAX_COUNT]; +static int defer_count; -/** - * Actual notification function - */ -static void notify(enum hook_type type) +void hook_notify(enum hook_type type) { const struct hook_data *start, *end, *p; int count, called = 0; int last_prio = HOOK_PRIO_FIRST - 1, prio; + CPRINTF("[%T hook notify %d]\n", type); + start = hook_list[type].start; end = hook_list[type].end; count = ((uint32_t)end - (uint32_t)start) / sizeof(struct hook_data); @@ -70,16 +84,45 @@ static void notify(enum hook_type type) } } -void hook_notify(enum hook_type type) +void hook_init(void) { - if (type == HOOK_AC_CHANGE) { - /* Store deferred hook and wake task */ - atomic_or(&pending_hooks, 1 << type); - task_wake(TASK_ID_HOOKS); + defer_count = __deferred_funcs_end - __deferred_funcs; + ASSERT(defer_count <= DEFERRABLE_MAX_COUNT); + + hook_notify(HOOK_INIT); +} + +int hook_call_deferred(void (*routine)(void), int us) +{ + const struct deferred_data *p; + int i; + + /* Find the index of the routine */ + for (p = __deferred_funcs; p < __deferred_funcs_end; p++) { + if (p->routine == routine) + break; + } + if (p >= __deferred_funcs_end) + return EC_ERROR_INVAL; /* Routine not registered */ + + /* Convert to index */ + i = p - __deferred_funcs; + if (i >= DEFERRABLE_MAX_COUNT) + return EC_ERROR_UNKNOWN; /* No space to hold time */ + + if (us == -1) { + /* Cancel */ + defer_until[i] = 0; } else { - /* Notify now */ - notify(type); + /* + * Set alarm, and wake task so it can re-sleep for the + * proper time. + */ + defer_until[i] = get_time().val + us; + task_wake(TASK_ID_HOOKS); } + + return EC_SUCCESS; } void hook_task(void) @@ -90,32 +133,51 @@ void hook_task(void) while (1) { uint64_t t = get_time().val; - uint32_t pending = atomic_read_clear(&pending_hooks); + int next = 0; int i; - /* Call pending hooks, if any */ - for (i = 0; pending && i < 32; i++) { - const uint32_t mask = 1 << i; - - if (pending & mask) { - notify(i); - pending ^= mask; + /* Handle deferred routines */ + for (i = 0; i < defer_count; i++) { + if (defer_until[i] && defer_until[i] < t) { + CPRINTF("[%T hook call deferred 0x%p]\n", + __deferred_funcs[i].routine); + /* + * Call deferred function. Clear timer first, + * so it can request itself be called later. + */ + defer_until[i] = 0; + __deferred_funcs[i].routine(); } } if (t - last_tick >= HOOK_TICK_INTERVAL) { - notify(HOOK_TICK); + hook_notify(HOOK_TICK); last_tick = t; } if (t - last_second >= SECOND) { - notify(HOOK_SECOND); + hook_notify(HOOK_SECOND); last_second = t; } - /* Use up the rest of our hook tick interval */ - t = get_time().val - t; - if (t < HOOK_TICK_INTERVAL) - usleep(HOOK_TICK_INTERVAL - t); + /* Calculate when next tick needs to occur */ + t = get_time().val; + if (last_tick + HOOK_TICK_INTERVAL > t) + next = last_tick + HOOK_TICK_INTERVAL - t; + + /* Wake earlier if needed by a deferred routine */ + for (i = 0; i < defer_count && next > 0; i++) { + if (!defer_until[i]) + continue; + + if (defer_until[i] < t) + next = 0; + else if (defer_until[i] - t < next) + next = defer_until[i] - t; + } + + /* Sleep until the next event */ + if (next > 0) + task_wait_event(next); } } diff --git a/common/main.c b/common/main.c index cb6b0b8bbb..6ee477931f 100644 --- a/common/main.c +++ b/common/main.c @@ -120,12 +120,8 @@ int main(void) keyboard_scan_init(); #endif - /* - * Initialize other driver modules. These can occur in any order. - * Non-driver modules with tasks do their inits from their task - * functions, not here. - */ - hook_notify(HOOK_INIT); + /* Initialize the hook library. This calls HOOK_INIT hooks. */ + hook_init(); /* * Print the init time. Not completely accurate because it can't take diff --git a/core/cortex-m/ec.lds.S b/core/cortex-m/ec.lds.S index 29e63bf4a6..cb0f38f35a 100644 --- a/core/cortex-m/ec.lds.S +++ b/core/cortex-m/ec.lds.S @@ -104,6 +104,10 @@ SECTIONS *(.rodata.HOOK_SECOND) __hooks_second_end = .; + __deferred_funcs = .; + *(.rodata.deferred) + __deferred_funcs_end = .; + . = ALIGN(4); *(.rodata*) diff --git a/core/cortex-m/link_defs.h b/core/cortex-m/link_defs.h index 2b5a4a4d77..dbf2757b21 100644 --- a/core/cortex-m/link_defs.h +++ b/core/cortex-m/link_defs.h @@ -43,6 +43,11 @@ extern const struct hook_data __hooks_tick_end[]; extern const struct hook_data __hooks_second[]; extern const struct hook_data __hooks_second_end[]; +/* Deferrable functions */ +static const struct deferred_data __deferred_funcs[]; +static const struct deferred_data __deferred_funcs_end[]; + + /* Host commands */ extern const struct host_command __hcmds[]; extern const struct host_command __hcmds_end[]; diff --git a/include/hooks.h b/include/hooks.h index c07877feca..6a6bd797cc 100644 --- a/include/hooks.h +++ b/include/hooks.h @@ -124,6 +124,11 @@ struct hook_data { }; /** + * Initialize the hooks library. + */ +void hook_init(void); + +/** * Call all the hook routines of a specified type. * * @param type Type of hook routines to call. @@ -131,6 +136,23 @@ struct hook_data { void hook_notify(enum hook_type type); /** + * Start a timer to call a deferred routine. + * + * The routine will be called after at least the specified delay, in the + * context of the hook task. + * + * @param routine Routine to call; must have been declared with + * DECLARE_DEFERRED(). + * @param us Delay in microseconds until routine will be called. + * If the routine is already pending, subsequent calls + * will change the delay. Pass us=0 to call as soon as + * possible, or -1 to cancel the deferred call. + * + * @return non-zero if error. + */ +int hook_call_deferred(void (*routine)(void), int us); + +/** * Register a hook routine. * * @param hooktype Type of hook for routine (enum hook_type) @@ -146,4 +168,23 @@ void hook_notify(enum hook_type type); __attribute__((section(".rodata." #hooktype))) \ = {routine, priority} + +struct deferred_data { + /* Deferred function pointer */ + void (*routine)(void); +}; + +/** + * Register a deferred function call. + * + * Note that if you declare a bunch of these, you may need to override + * DEFERRABLE_MAX_COUNT in your board.h. + * + * @param routine Function pointer, with prototype void routine(void) + */ +#define DECLARE_DEFERRED(routine) \ + const struct deferred_data __deferred_##routine \ + __attribute__((section(".rodata.deferred"))) \ + = {routine} + #endif /* __CROS_EC_HOOKS_H */ |