diff options
author | Randall Spangler <rspangler@chromium.org> | 2012-04-18 13:03:38 -0700 |
---|---|---|
committer | Randall Spangler <rspangler@chromium.org> | 2012-04-19 08:39:41 -0700 |
commit | 70f3fcaf8648230a5cd27a9da151494d6df3016f (patch) | |
tree | c67e9ff911914f7eaef7af6c9e95874cf8d0cce2 | |
parent | 6ecbb86b6392fa0b11514903a9fb3d3a3b704391 (diff) | |
download | chrome-ec-70f3fcaf8648230a5cd27a9da151494d6df3016f.tar.gz |
Add hooks module so modules can be notified of system-level events.
This will be used for sleep/wake/sysjump/etc. For now it's just wired
up to clock frequency changing.
Signed-off-by: Randall Spangler <rspangler@chromium.org>
BUG=none
TEST=manual: use nopll command, should still work
Change-Id: Iedcea5830bc18eacfd955c29b8f793aba8905dd8
-rw-r--r-- | chip/lm4/clock.c | 27 | ||||
-rw-r--r-- | chip/lm4/hwtimer.c | 16 | ||||
-rw-r--r-- | chip/lm4/i2c.c | 11 | ||||
-rw-r--r-- | chip/lm4/peci.c | 9 | ||||
-rw-r--r-- | chip/lm4/watchdog.c | 10 | ||||
-rw-r--r-- | common/build.mk | 2 | ||||
-rw-r--r-- | common/hooks.c | 63 | ||||
-rw-r--r-- | core/cortex-m/ec.lds.S | 11 | ||||
-rw-r--r-- | include/hooks.h | 48 | ||||
-rw-r--r-- | include/hwtimer.h | 9 | ||||
-rw-r--r-- | include/i2c.h | 25 | ||||
-rw-r--r-- | include/peci.h | 11 | ||||
-rw-r--r-- | include/watchdog.h | 3 |
13 files changed, 174 insertions, 71 deletions
diff --git a/chip/lm4/clock.c b/chip/lm4/clock.c index 694f1bc1b9..12a1896ff7 100644 --- a/chip/lm4/clock.c +++ b/chip/lm4/clock.c @@ -10,6 +10,7 @@ #include "config.h" #include "console.h" #include "gpio.h" +#include "hooks.h" #include "registers.h" #include "system.h" #include "task.h" @@ -178,34 +179,10 @@ static int command_sleep(int argc, char **argv) DECLARE_CONSOLE_COMMAND(sleep, command_sleep); -/* TODO: temporary holding place for notifying modules of clock change. Should - * be moved to main.c after we finish measuring the power savings, so the clock - * frequency is automatically dropped after verified boot. */ -#include "i2c.h" -#include "hwtimer.h" -#include "peci.h" -#include "watchdog.h" - static int command_disable_pll(int argc, char **argv) { - int freq; - - clock_disable_pll(); - /* Notify modules of frequency change */ - freq = clock_get_freq(); - hwtimer_clock_changed(freq); -#ifdef CONFIG_TASK_WATCHDOG - watchdog_clock_changed(freq); -#endif -#ifdef CONFIG_I2C - i2c_clock_changed(freq); -#endif -#ifdef CONFIG_PECI - peci_clock_changed(freq); -#endif - - return EC_SUCCESS; + return hook_notify(HOOK_FREQ_CHANGE, 0); } DECLARE_CONSOLE_COMMAND(nopll, command_disable_pll); diff --git a/chip/lm4/hwtimer.c b/chip/lm4/hwtimer.c index a45f74705d..41e737b10d 100644 --- a/chip/lm4/hwtimer.c +++ b/chip/lm4/hwtimer.c @@ -7,6 +7,7 @@ #include "board.h" #include "clock.h" +#include "hooks.h" #include "hwtimer.h" #include "registers.h" #include "task.h" @@ -21,22 +22,26 @@ void __hw_clock_event_set(uint32_t deadline) LM4_TIMER_IMR(6) |= 0x10; } + uint32_t __hw_clock_event_get(void) { return 0xffffffff - LM4_TIMER_TAMATCHR(6); } + void __hw_clock_event_clear(void) { /* Disable the match interrupt */ LM4_TIMER_IMR(6) &= ~0x10; } + uint32_t __hw_clock_source_read(void) { return 0xffffffff - LM4_TIMER_TAV(6); } + static void __hw_clock_source_irq(void) { uint32_t status = LM4_TIMER_RIS(6); @@ -53,12 +58,15 @@ static void __hw_clock_source_irq(void) DECLARE_IRQ(LM4_IRQ_TIMERW0A, __hw_clock_source_irq, 1); -void hwtimer_clock_changed(int freq) +static int update_prescaler(void) { /* Set the prescaler to increment every microsecond. This takes * effect immediately, because the TAILD bit in TAMR is clear. */ - LM4_TIMER_TAPR(6) = freq / US_PER_SECOND; + LM4_TIMER_TAPR(6) = clock_get_freq() / US_PER_SECOND; + + return EC_SUCCESS; } +DECLARE_HOOK(HOOK_FREQ_CHANGE, update_prescaler, HOOK_PRIO_DEFAULT); int __hw_clock_source_init(void) @@ -80,8 +88,8 @@ int __hw_clock_source_init(void) /* 32-bit timer mode */ LM4_TIMER_CFG(6) = 4; - /* Set initial clock frequency */ - hwtimer_clock_changed(clock_get_freq()); + /* Set initial prescaler */ + update_prescaler(); /* Periodic mode, counting down */ LM4_TIMER_TAMR(6) = 0x22; diff --git a/chip/lm4/i2c.c b/chip/lm4/i2c.c index 131a84a584..c712adf641 100644 --- a/chip/lm4/i2c.c +++ b/chip/lm4/i2c.c @@ -9,6 +9,7 @@ #include "clock.h" #include "console.h" #include "gpio.h" +#include "hooks.h" #include "i2c.h" #include "task.h" #include "timer.h" @@ -260,8 +261,10 @@ exit: } -void i2c_clock_changed(int freq) +static int i2c_freq_changed(void) { + int freq = clock_get_freq(); + LM4_I2C_MTPR(I2C_PORT_THERMAL) = (freq / (I2C_SPEED_THERMAL * 10 * 2)) - 1; LM4_I2C_MTPR(I2C_PORT_BATTERY) = @@ -270,7 +273,10 @@ void i2c_clock_changed(int freq) (freq / (I2C_SPEED_CHARGER * 10 * 2)) - 1; LM4_I2C_MTPR(I2C_PORT_LIGHTBAR) = (freq / (I2C_SPEED_LIGHTBAR * 10 * 2)) - 1; + + return EC_SUCCESS; } +DECLARE_HOOK(HOOK_FREQ_CHANGE, i2c_freq_changed, HOOK_PRIO_DEFAULT + 1); /*****************************************************************************/ /* Interrupt handlers */ @@ -307,7 +313,6 @@ DECLARE_IRQ(LM4_IRQ_I2C5, i2c5_interrupt, 2); /*****************************************************************************/ /* Console commands */ - static void scan_bus(int port, char *desc) { int rv; @@ -457,7 +462,7 @@ int i2c_init(void) LM4_I2C_MCR(I2C_PORT_LIGHTBAR) = 0x10; /* Set initial clock frequency */ - i2c_clock_changed(clock_get_freq()); + i2c_freq_changed(); /* Enable irqs */ task_enable_irq(LM4_IRQ_I2C0); diff --git a/chip/lm4/peci.c b/chip/lm4/peci.c index 2dbbd914b3..0e7540fa10 100644 --- a/chip/lm4/peci.c +++ b/chip/lm4/peci.c @@ -9,6 +9,7 @@ #include "clock.h" #include "console.h" #include "gpio.h" +#include "hooks.h" #include "peci.h" #include "registers.h" #include "temp_sensor.h" @@ -70,8 +71,9 @@ int peci_temp_sensor_get_val(int idx) } -void peci_clock_changed(int freq) +static int peci_freq_changed(void) { + int freq = clock_get_freq(); int baud; /* Disable polling while reconfiguring */ @@ -88,7 +90,10 @@ void peci_clock_changed(int freq) /* Set up temperature monitoring to report in degrees K */ LM4_PECI_CTL = ((PECI_TJMAX + 273) << 22) | 0x2001; + + return EC_SUCCESS; } +DECLARE_HOOK(HOOK_FREQ_CHANGE, peci_freq_changed, HOOK_PRIO_DEFAULT - 1); /*****************************************************************************/ /* Console commands */ @@ -121,7 +126,7 @@ int peci_init(void) configure_gpios(); /* Set initial clock frequency */ - peci_clock_changed(clock_get_freq()); + peci_freq_changed(); return EC_SUCCESS; } diff --git a/chip/lm4/watchdog.c b/chip/lm4/watchdog.c index 65c5ad2d2a..c13414c20e 100644 --- a/chip/lm4/watchdog.c +++ b/chip/lm4/watchdog.c @@ -11,6 +11,7 @@ #include "config.h" #include "registers.h" #include "gpio.h" +#include "hooks.h" #include "task.h" #include "timer.h" #include "uart.h" @@ -76,6 +77,7 @@ void watchdog_trace(uint32_t excep_lr, uint32_t excep_sp) uart_emergency_flush(); } + void IRQ_HANDLER(LM4_IRQ_WATCHDOG)(void) __attribute__((naked)); void IRQ_HANDLER(LM4_IRQ_WATCHDOG)(void) { @@ -118,11 +120,13 @@ void watchdog_reload(void) } -void watchdog_clock_changed(int freq) +static int watchdog_freq_changed(void) { /* Set the timeout period */ - watchdog_period = WATCHDOG_PERIOD_MS * (freq / 1000); + watchdog_period = WATCHDOG_PERIOD_MS * (clock_get_freq() / 1000); + return EC_SUCCESS; } +DECLARE_HOOK(HOOK_FREQ_CHANGE, watchdog_freq_changed, HOOK_PRIO_DEFAULT); int watchdog_init(void) @@ -138,7 +142,7 @@ int watchdog_init(void) LM4_WATCHDOG_LOCK(0) = LM4_WATCHDOG_MAGIC_WORD; /* Set initial timeout period */ - watchdog_clock_changed(clock_get_freq()); + watchdog_freq_changed(); LM4_WATCHDOG_LOAD(0) = watchdog_period; /* De-activate the watchdog when the JTAG stops the CPU */ diff --git a/common/build.mk b/common/build.mk index 2144973ba1..dcece87b59 100644 --- a/common/build.mk +++ b/common/build.mk @@ -6,7 +6,7 @@ # common-y=main.o message.o util.o console.o vboot.o uart_buffering.o -common-y+=memory_commands.o shared_mem.o system_common.o +common-y+=memory_commands.o shared_mem.o system_common.o hooks.o common-y+=gpio_commands.o version.o common-$(CONFIG_BATTERY_ATL706486)+=battery_atl706486.o common-$(CONFIG_CHARGER_BQ24725)+=charger_bq24725.o diff --git a/common/hooks.c b/common/hooks.c new file mode 100644 index 0000000000..94bf2627fb --- /dev/null +++ b/common/hooks.c @@ -0,0 +1,63 @@ +/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* System hooks for Chrome EC */ + +#include "hooks.h" +#include "uart.h" +#include "util.h" + +/* Hooks are described in special sections */ +extern const struct hook_data __hooks_freq_change[]; +extern const struct hook_data __hooks_freq_change_end[]; + + +int hook_notify(enum hook_type type, int stop_on_error) +{ + const struct hook_data *start, *end, *p; + int count, called = 0; + int last_prio = HOOK_PRIO_FIRST - 1, prio; + int rv_error = EC_SUCCESS, rv; + + /* Get the start and end pointers for the hook type */ + switch (type) { + case HOOK_FREQ_CHANGE: + start = __hooks_freq_change; + end = __hooks_freq_change_end; + break; + default: + /* Unhandled hook type */ + return EC_ERROR_UNKNOWN; + } + + count = ((uint32_t)end - (uint32_t)start) / sizeof(struct hook_data); + + /* Call all the hooks in priority order */ + while (called < count) { + /* Find the lowest remaining priority */ + for (p = start, prio = HOOK_PRIO_LAST + 1; p < end; p++) { + if (p->priority < prio && p->priority > last_prio) + prio = p->priority; + } + last_prio = prio; + + /* Call all the hooks with that priority */ + for (p = start; p < end; p++) { + if (p->priority == prio) { + called++; + rv = p->routine(); + if (rv != EC_SUCCESS) { + if (stop_on_error) + return rv; + else if (rv_error == EC_SUCCESS) + rv_error = rv; + } + } + } + } + + /* Return the first error seen, if any */ + return rv_error; +} diff --git a/core/cortex-m/ec.lds.S b/core/cortex-m/ec.lds.S index 04c95f85e4..21fddb3f5e 100644 --- a/core/cortex-m/ec.lds.S +++ b/core/cortex-m/ec.lds.S @@ -34,13 +34,24 @@ SECTIONS __irqprio = .; *(.rodata.irqprio) __irqprio_end = .; + . = ALIGN(4); __cmds = .; *(.rodata.cmds) __cmds_end = .; + + . = ALIGN(4); __hcmds = .; *(.rodata.hcmds) __hcmds_end = .; + + . = ALIGN(4); + __hooks_freq_change = .; + *(.rodata.HOOK_FREQ_CHANGE) + __hooks_freq_change_end = .; + + + . = ALIGN(4); *(.rodata*) . = ALIGN(4); #ifdef COMPILE_FOR_RAM diff --git a/include/hooks.h b/include/hooks.h new file mode 100644 index 0000000000..3d3a834e25 --- /dev/null +++ b/include/hooks.h @@ -0,0 +1,48 @@ +/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* System hooks for Chrome EC */ + +#ifndef __CROS_EC_HOOKS_H +#define __CROS_EC_HOOKS_H + +#include "common.h" + +enum hook_priority { + HOOK_PRIO_FIRST = 1, /* Highest priority */ + HOOK_PRIO_DEFAULT = 5000, /* Default priority */ + HOOK_PRIO_LAST = 9999 /* Lowest priority */ +}; + +enum hook_type { + HOOK_FREQ_CHANGE, /* System clock changed frequency */ +}; + + +struct hook_data { + /* Hook processing routine; returns EC error code. */ + int (*routine)(void); + /* Priority; low numbers = higher priority. */ + int priority; +}; + + +/* Call all the hook routines of a specified type. If stop_on_error, stops on + * the first non-EC_SUCCESS return code. Returns the first non-EC_SUCCESS + * return code, if any, or EC_SUCCESS if all hooks returned EC_SUCCESS. */ +int hook_notify(enum hook_type type, int stop_on_error); + + +/* Register a hook routine. <hooktype> should be one of enum hook_type. + * <routine> should be int routine(void), and should return an error code or + * EC_SUCCESS if no error. <priority> should be between HOOK_PRIO_FIRST and + * HOOK_PRIO_LAST, and should be HOOK_PRIO_DEFAULT unless there's a compelling + * reason to care about the order in which hooks are called. */ +#define DECLARE_HOOK(hooktype, routine, priority) \ + const struct hook_data __hook_##hooktype##_##routine \ + __attribute__((section(".rodata." #hooktype))) \ + = {routine, priority} + +#endif /* __CROS_EC_HOOKS_H */ diff --git a/include/hwtimer.h b/include/hwtimer.h index 5fee466b7c..ae0c634163 100644 --- a/include/hwtimer.h +++ b/include/hwtimer.h @@ -5,8 +5,8 @@ /* Hardware timer driver API */ -#ifndef __EC_HWTIMER_H -#define __EC_HWTIMER_H +#ifndef __CROS_EC_HWTIMER_H +#define __CROS_EC_HWTIMER_H /** * Programs when the next timer should fire an interrupt. @@ -30,9 +30,6 @@ uint32_t __hw_clock_source_read(void); */ int __hw_clock_source_init(void); -/* Notifies the module the system clock frequency has changed to <freq>. */ -void hwtimer_clock_changed(int freq); - /** * Searches the next deadline and program it in the timer hardware. * @@ -43,4 +40,4 @@ void hwtimer_clock_changed(int freq); */ void process_timers(int overflow); -#endif /* __EC_HWTIMER_H */ +#endif /* __CROS_EC_HWTIMER_H */ diff --git a/include/i2c.h b/include/i2c.h index 22ab18adcf..7543d8cb25 100644 --- a/include/i2c.h +++ b/include/i2c.h @@ -13,30 +13,23 @@ /* Flags for slave address field, in addition to the 8-bit address */ #define I2C_FLAG_BIG_ENDIAN 0x100 /* 16 byte values are MSB-first */ -/* Initializes the module. */ +/* Initialize the module. */ int i2c_init(void); -/* Notifies the module the system clock frequency has changed to <freq>. */ -void i2c_clock_changed(int freq); - -/* Reads a 16-bit register from the slave at 8-bit slave address - * <slaveaddr>, at the specified 8-bit <offset> in the slave's address - * space. */ +/* Read a 16-bit register from the slave at 8-bit slave address <slaveaddr>, at + * the specified 8-bit <offset> in the slave's address space. */ int i2c_read16(int port, int slave_addr, int offset, int* data); -/* Writes a 16-bit register to the slave at 8-bit slave address - * <slaveaddr>, at the specified 8-bit <offset> in the slave's address - * space. */ +/* Write a 16-bit register to the slave at 8-bit slave address <slaveaddr>, at + * the specified 8-bit <offset> in the slave's address space. */ int i2c_write16(int port, int slave_addr, int offset, int data); -/* Reads an 8-bit register from the slave at 8-bit slave address - * <slaveaddr>, at the specified 8-bit <offset> in the slave's address - * space. */ +/* Read an 8-bit register from the slave at 8-bit slave address <slaveaddr>, at + * the specified 8-bit <offset> in the slave's address space. */ int i2c_read8(int port, int slave_addr, int offset, int* data); -/* Writes an 8-bit register to the slave at 8-bit slave address - * <slaveaddr>, at the specified 8-bit <offset> in the slave's address - * space. */ +/* Write an 8-bit register to the slave at 8-bit slave address <slaveaddr>, at + * the specified 8-bit <offset> in the slave's address space. */ int i2c_write8(int port, int slave_addr, int offset, int data); /* Read ascii string using smbus read block protocol. diff --git a/include/peci.h b/include/peci.h index 7a98a24991..1ad0d92bad 100644 --- a/include/peci.h +++ b/include/peci.h @@ -10,21 +10,16 @@ #include "common.h" -struct temp_sensor_t; - -/* Initializes the module. */ +/* Initialize the module. */ int peci_init(void); -/* Notifies the module the system clock frequency has changed to <freq>. */ -void peci_clock_changed(int freq); - -/* Returns the current CPU temperature in degrees K, or -1 if error. +/* Return the current CPU temperature in degrees K, or -1 if error. * * Note that the PECI interface is currently a little flaky; if you get an * error, retry a bit later. */ int peci_get_cpu_temp(void); -/* Reads the CPU temperature sensor via PECI. This interface is for the +/* Read the CPU temperature sensor via PECI. This interface is for the * temperature sensor module. Returns the temperature in degrees K, or -1 if * error. */ int peci_temp_sensor_get_val(int idx); diff --git a/include/watchdog.h b/include/watchdog.h index 569352bc6a..68ac7b917e 100644 --- a/include/watchdog.h +++ b/include/watchdog.h @@ -15,7 +15,4 @@ int watchdog_init(int period_ms); /* Reload the watchdog counter */ void watchdog_reload(void); -/* Notifies the module the system clock frequency has changed to <freq>. */ -void watchdog_clock_changed(int freq); - #endif /* __CROS_EC_WATCHDOG_H */ |