summaryrefslogtreecommitdiff
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
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>
-rw-r--r--chip/it83xx/flash.c12
-rw-r--r--chip/it83xx/gpio.c6
-rw-r--r--core/nds32/atomic.h31
-rw-r--r--core/nds32/init.S13
-rw-r--r--core/nds32/task.c56
-rw-r--r--include/task.h12
6 files changed, 84 insertions, 46 deletions
diff --git a/chip/it83xx/flash.c b/chip/it83xx/flash.c
index 9d2f7a7c39..5155e94332 100644
--- a/chip/it83xx/flash.c
+++ b/chip/it83xx/flash.c
@@ -347,7 +347,6 @@ int FLASH_DMA_CODE flash_physical_read(int offset, int size, char *data)
*/
int FLASH_DMA_CODE flash_physical_write(int offset, int size, const char *data)
{
- uint32_t psw = get_psw();
if (flash_dma_code_enabled == 0)
return EC_ERROR_ACCESS_DENIED;
@@ -367,8 +366,7 @@ int FLASH_DMA_CODE flash_physical_write(int offset, int size, const char *data)
dma_flash_aai_write(offset, size, data);
dma_reset_immu();
- if (psw & PSW_GIE)
- interrupt_enable();
+ interrupt_enable();
return dma_flash_verify(offset, size, data);
}
@@ -384,7 +382,6 @@ int FLASH_DMA_CODE flash_physical_write(int offset, int size, const char *data)
int FLASH_DMA_CODE flash_physical_erase(int offset, int size)
{
int v_size = size, v_addr = offset;
- uint32_t psw = get_psw();
if (flash_dma_code_enabled == 0)
return EC_ERROR_ACCESS_DENIED;
@@ -406,8 +403,7 @@ int FLASH_DMA_CODE flash_physical_erase(int offset, int size)
}
dma_reset_immu();
- if (psw & PSW_GIE)
- interrupt_enable();
+ interrupt_enable();
return dma_flash_verify(v_addr, v_size, NULL);
}
@@ -518,7 +514,6 @@ uint32_t flash_physical_get_writable_flags(uint32_t cur_flags)
static void flash_code_static_dma(void)
{
- uint32_t psw = get_psw();
/* Make sure no interrupt while enable static DMA */
interrupt_disable();
@@ -548,8 +543,7 @@ static void flash_code_static_dma(void)
flash_dma_code_enabled = 0x01;
- if (psw & PSW_GIE)
- interrupt_enable();
+ interrupt_enable();
}
/**
diff --git a/chip/it83xx/gpio.c b/chip/it83xx/gpio.c
index 7ad348ce4b..f00c954f1c 100644
--- a/chip/it83xx/gpio.c
+++ b/chip/it83xx/gpio.c
@@ -440,9 +440,6 @@ static void __gpio_irq(void)
}
#endif
- /* Run the GPIO master handler above with corresponding port/mask. */
- gpio_interrupt(gpio_irqs[irq].gpio_port, gpio_irqs[irq].gpio_mask);
-
/*
* Clear the WUC status register. Note the external pin first goes
* to the WUC module and is always edge triggered.
@@ -454,6 +451,9 @@ static void __gpio_irq(void)
* controller is level triggered from the WUC status.
*/
task_clear_pending_irq(irq);
+
+ /* Run the GPIO master handler above with corresponding port/mask. */
+ gpio_interrupt(gpio_irqs[irq].gpio_port, gpio_irqs[irq].gpio_mask);
}
/* Route all WKO interrupts coming from INT#2 into __gpio_irq. */
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();
}
}
}
diff --git a/include/task.h b/include/task.h
index d7d6dd1ca3..ed635686bc 100644
--- a/include/task.h
+++ b/include/task.h
@@ -52,6 +52,18 @@ void interrupt_enable(void);
inline int in_interrupt_context(void);
/**
+ * Return current interrupt mask. Meaning is chip-specific and
+ * should not be examined; just pass it to set_int_mask() to
+ * restore a previous interrupt state after interrupt_disable().
+ */
+uint32_t get_int_mask(void);
+
+/**
+ * Set interrupt mask. As with interrupt_disable(), use with care.
+ */
+void set_int_mask(uint32_t val);
+
+/**
* Set a task event.
*
* If the task is higher priority than the current task, this will cause an