summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2013-03-29 16:34:02 -0700
committerChromeBot <chrome-bot@google.com>2013-04-02 14:12:55 -0700
commit5966f22a8a1928583943b57fc22e72f85d69d079 (patch)
treee49232c6d5368219faa63cae0ec5e01a4866f837
parent97bf36c9d3ad3d96ad6dea7cd6e6f3e164297c43 (diff)
downloadchrome-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.c11
-rw-r--r--common/hooks.c114
-rw-r--r--common/main.c8
-rw-r--r--core/cortex-m/ec.lds.S4
-rw-r--r--core/cortex-m/link_defs.h5
-rw-r--r--include/hooks.h41
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 */