summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2012-04-18 13:03:38 -0700
committerRandall Spangler <rspangler@chromium.org>2012-04-19 08:39:41 -0700
commit70f3fcaf8648230a5cd27a9da151494d6df3016f (patch)
treec67e9ff911914f7eaef7af6c9e95874cf8d0cce2
parent6ecbb86b6392fa0b11514903a9fb3d3a3b704391 (diff)
downloadchrome-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.c27
-rw-r--r--chip/lm4/hwtimer.c16
-rw-r--r--chip/lm4/i2c.c11
-rw-r--r--chip/lm4/peci.c9
-rw-r--r--chip/lm4/watchdog.c10
-rw-r--r--common/build.mk2
-rw-r--r--common/hooks.c63
-rw-r--r--core/cortex-m/ec.lds.S11
-rw-r--r--include/hooks.h48
-rw-r--r--include/hwtimer.h9
-rw-r--r--include/i2c.h25
-rw-r--r--include/peci.h11
-rw-r--r--include/watchdog.h3
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 */