summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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