summaryrefslogtreecommitdiff
path: root/core/nds32
diff options
context:
space:
mode:
authorDino Li <dino.li@ite.com.tw>2015-08-20 05:54:40 +0800
committerChromeOS Commit Bot <chromeos-commit-bot@chromium.org>2015-08-20 01:37:56 +0000
commit911de4d175a2416370abc65b5dbc800a46c3fe3c (patch)
tree0f3d45d185731df7da52f3b6ae4d9e06ca1a9c8b /core/nds32
parent8c633e5af603687d178047930dc502c7deefe0ae (diff)
downloadchrome-ec-911de4d175a2416370abc65b5dbc800a46c3fe3c.tar.gz
nds32: use INT_MASK instead of GIE
When there is an interrupt event, N8 CPU will save PSW register to IPSW register and clear GIE then jump to interrupt service routine. N8 will restore PSW from IPSW after "iret" instruction (the above are purely hardware mechanism). Nested interrupt will occur if we set GIE again in interrupt context. symptom: power button pressed while LID open -> exception or unknown reset. Signed-off-by: Dino Li <dino.li@ite.com.tw> BRANCH=none BUG=none TEST=1. Manually pressed power button x200. 2. Console "eflash" erase and write eflash OK. Change-Id: Ic04a23d473ebc6417dffea814a27583cb8d63a1f Reviewed-on: https://chromium-review.googlesource.com/289437 Reviewed-by: Randall Spangler <rspangler@chromium.org> Commit-Queue: Dino Li <dino.li@ite.com.tw> Tested-by: Dino Li <dino.li@ite.com.tw>
Diffstat (limited to 'core/nds32')
-rw-r--r--core/nds32/atomic.h31
-rw-r--r--core/nds32/init.S13
-rw-r--r--core/nds32/task.c56
3 files changed, 66 insertions, 34 deletions
diff --git a/core/nds32/atomic.h b/core/nds32/atomic.h
index 2b49dfe5c1..4771d31f99 100644
--- a/core/nds32/atomic.h
+++ b/core/nds32/atomic.h
@@ -10,47 +10,48 @@
#include "common.h"
#include "cpu.h"
+#include "task.h"
static inline void atomic_clear(uint32_t volatile *addr, uint32_t bits)
{
- uint32_t psw = get_psw();
- asm volatile ("setgie.d");
+ uint32_t int_mask = get_int_mask();
+ interrupt_disable();
*addr &= ~bits;
- set_psw(psw);
+ set_int_mask(int_mask);
}
static inline void atomic_or(uint32_t volatile *addr, uint32_t bits)
{
- uint32_t psw = get_psw();
- asm volatile ("setgie.d");
+ uint32_t int_mask = get_int_mask();
+ interrupt_disable();
*addr |= bits;
- set_psw(psw);
+ set_int_mask(int_mask);
}
static inline void atomic_add(uint32_t volatile *addr, uint32_t value)
{
- uint32_t psw = get_psw();
- asm volatile ("setgie.d");
+ uint32_t int_mask = get_int_mask();
+ interrupt_disable();
*addr += value;
- set_psw(psw);
+ set_int_mask(int_mask);
}
static inline void atomic_sub(uint32_t volatile *addr, uint32_t value)
{
- uint32_t psw = get_psw();
- asm volatile ("setgie.d");
+ uint32_t int_mask = get_int_mask();
+ interrupt_disable();
*addr -= value;
- set_psw(psw);
+ set_int_mask(int_mask);
}
static inline uint32_t atomic_read_clear(uint32_t volatile *addr)
{
uint32_t val;
- uint32_t psw = get_psw();
- asm volatile ("setgie.d");
+ uint32_t int_mask = get_int_mask();
+ interrupt_disable();
val = *addr;
*addr = 0;
- set_psw(psw);
+ set_int_mask(int_mask);
return val;
}
#endif /* __CROS_EC_ATOMIC_H */
diff --git a/core/nds32/init.S b/core/nds32/init.S
index 23483e59b0..a27fcd8d0e 100644
--- a/core/nds32/init.S
+++ b/core/nds32/init.S
@@ -85,6 +85,19 @@ eflash_sig:
.global reset
reset:
+ /*
+ * GIE (global interrupt) is always disabled here. the first
+ * "iret" instruction of syscall interrupt (triggered by __task_start)
+ * will restore PSW from IPSW, and will enable GIE.
+ * Firmware will not change GIE settings (set/clear) until the next
+ * reset, unless there's an interrupt event.
+ * When there is an interrupt event, N8 CPU will save PSW register to
+ * IPSW register and clear GIE then jump to interrupt service routine.
+ * N8 will restore PSW from IPSW after "iret" instruction.
+ */
+ setgie.d
+ dsb
+
/* GP register is used to access .data and .bss */
la $gp, _SDA_BASE_
diff --git a/core/nds32/task.c b/core/nds32/task.c
index baf87ffbf3..97c1ac55fb 100644
--- a/core/nds32/task.c
+++ b/core/nds32/task.c
@@ -152,17 +152,42 @@ static inline task_ *__task_id_to_ptr(task_id_t id)
return tasks + id;
}
+/*
+ * We use INT_MASK to enable (interrupt_enable)/
+ * disable (interrupt_disable) all maskable interrupts.
+ * And, EC modules share HW2 ~ HW15 interrupts. If corresponding
+ * bit of INT_MASK is set, it will never be cleared
+ * (see chip_disable_irq()). To enable/disable individual
+ * interrupt of EC module, we can use corresponding EXT_IERx registers.
+ *
+ * ------------ -----------
+ * | | | ------- |
+ * |EC modules| | | HW2 | |
+ * | | | ------- |
+ * | INT 0 | | ------- | ------- -------
+ * | ~ | --> | | HW3 | | -> | GIE | -> | CPU |
+ * | INT 167 | | ------- | ------- -------
+ * | | | ... | |
+ * | | | ... | - clear by HW while
+ * | | | ------- | interrupt occur and
+ * | | | | HW15| | restore from IPSW after
+ * | | | ------- | instruction "iret".
+ * | EXT_IERx | | INT_MASK|
+ * ------------ -----------
+ */
void interrupt_disable(void)
{
- /* clear GIE (Global Interrupt Enable) bit */
- asm volatile ("setgie.d");
+ /* Mask all interrupts, only keep division by zero exception */
+ uint32_t val = (1 << 30);
+ asm volatile ("mtsr %0, $INT_MASK" : : "r"(val));
asm volatile ("dsb");
}
void interrupt_enable(void)
{
- /* set GIE (Global Interrupt Enable) bit */
- asm volatile ("setgie.e");
+ /* Enable HW2 ~ HW15 and division by zero exception interrupts */
+ uint32_t val = ((1 << 30) | 0xFFFC);
+ asm volatile ("mtsr %0, $INT_MASK" : : "r"(val));
}
inline int in_interrupt_context(void)
@@ -291,14 +316,14 @@ uint32_t task_wait_event(int timeout_us)
return __wait_evt(timeout_us, TASK_ID_IDLE);
}
-static uint32_t get_int_mask(void)
+uint32_t get_int_mask(void)
{
uint32_t ret;
asm volatile ("mfsr %0, $INT_MASK" : "=r"(ret));
return ret;
}
-static void set_int_mask(uint32_t val)
+void set_int_mask(uint32_t val)
{
asm volatile ("mtsr %0, $INT_MASK" : : "r"(val));
}
@@ -318,16 +343,12 @@ void task_enable_all_tasks(void)
void task_enable_irq(int irq)
{
- int cpu_int = chip_enable_irq(irq);
- if (cpu_int >= 0)
- set_int_mask(get_int_mask() | (1 << cpu_int));
+ chip_enable_irq(irq);
}
void task_disable_irq(int irq)
{
- int cpu_int = chip_disable_irq(irq);
- if (cpu_int >= 0)
- set_int_mask(get_int_mask() & ~(1 << cpu_int));
+ chip_disable_irq(irq);
}
void task_clear_pending_irq(int irq)
@@ -356,9 +377,6 @@ static void ivic_init_irqs(void)
/* chip-specific interrupt controller initialization */
chip_init_irqs();
- /* Mask all interrupts, only keep division by zero exception */
- set_int_mask(1 << 30 /* IDIVZ */);
-
/*
* Re-enable global interrupts in case they're disabled. On a reboot,
* they're already enabled; if we've jumped here from another image,
@@ -382,24 +400,24 @@ void mutex_lock(struct mutex *mtx)
ASSERT(id != TASK_ID_INVALID);
/* critical section with interrupts off */
- asm volatile ("setgie.d ; dsb");
+ interrupt_disable();
mtx->waiters |= id;
while (1) {
if (!mtx->lock) { /* we got it ! */
mtx->lock = 2;
mtx->waiters &= ~id;
/* end of critical section : re-enable interrupts */
- asm volatile ("setgie.e");
+ interrupt_enable();
return;
} else { /* Contention on the mutex */
/* end of critical section : re-enable interrupts */
- asm volatile ("setgie.e");
+ interrupt_enable();
/* Sleep waiting for our turn */
/* TODO(crbug.com/435612, crbug.com/435611)
* This discards any pending events! */
task_wait_event(0);
/* re-enter critical section */
- asm volatile ("setgie.d ; dsb");
+ interrupt_disable();
}
}
}