summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--chip/mt_scp/ipi.c39
-rw-r--r--chip/mt_scp/ipi_chip.h13
2 files changed, 48 insertions, 4 deletions
diff --git a/chip/mt_scp/ipi.c b/chip/mt_scp/ipi.c
index 26f07baf71..1afdf108ea 100644
--- a/chip/mt_scp/ipi.c
+++ b/chip/mt_scp/ipi.c
@@ -39,6 +39,8 @@
#define HOSTCMD_TYPE_HOSTCMD 1
#define HOSTCMD_TYPE_HOSTEVENT 2
+static volatile int16_t ipc0_enabled_count;
+static struct mutex ipc0_lock;
static struct mutex ipi_lock;
/* IPC0 shared objects, including send object and receive object. */
static struct ipc_shared_obj *const scp_send_obj =
@@ -47,6 +49,7 @@ static struct ipc_shared_obj *const scp_recv_obj =
(struct ipc_shared_obj *)(CONFIG_IPC_SHARED_OBJ_ADDR +
sizeof(struct ipc_shared_obj));
static char ipi_ready;
+
#ifdef HAS_TASK_HOSTCMD
/*
* hostcmd and hostevent share the same IPI ID, and use first byte type to
@@ -80,6 +83,34 @@ static inline void try_to_wakeup_ap(int32_t id)
SCP_SPM_INT = SPM_INT_A2SPM;
}
+void ipi_disable_irq(int irq)
+{
+ /* Only support SCP_IRQ_IPC0 for now. */
+ if (irq != SCP_IRQ_IPC0)
+ return;
+
+ mutex_lock(&ipc0_lock);
+
+ if ((--ipc0_enabled_count) == 0)
+ task_disable_irq(irq);
+
+ mutex_unlock(&ipc0_lock);
+}
+
+void ipi_enable_irq(int irq)
+{
+ /* Only support SCP_IRQ_IPC0 for now. */
+ if (irq != SCP_IRQ_IPC0)
+ return;
+
+ mutex_lock(&ipc0_lock);
+
+ if ((++ipc0_enabled_count) == 1)
+ task_enable_irq(irq);
+
+ mutex_unlock(&ipc0_lock);
+}
+
/* Send data from SCP to AP. */
int ipi_send(int32_t id, const void *buf, uint32_t len, int wait)
{
@@ -95,7 +126,7 @@ int ipi_send(int32_t id, const void *buf, uint32_t len, int wait)
if (len > sizeof(scp_send_obj->buffer))
return EC_ERROR_INVAL;
- task_disable_irq(SCP_IRQ_IPC0);
+ ipi_disable_irq(SCP_IRQ_IPC0);
mutex_lock(&ipi_lock);
/* Check if there is already an IPI pending in AP. */
@@ -112,7 +143,7 @@ int ipi_send(int32_t id, const void *buf, uint32_t len, int wait)
try_to_wakeup_ap(id);
mutex_unlock(&ipi_lock);
- task_enable_irq(SCP_IRQ_IPC0);
+ ipi_enable_irq(SCP_IRQ_IPC0);
return EC_ERROR_BUSY;
}
@@ -130,7 +161,7 @@ int ipi_send(int32_t id, const void *buf, uint32_t len, int wait)
;
mutex_unlock(&ipi_lock);
- task_enable_irq(SCP_IRQ_IPC0);
+ ipi_enable_irq(SCP_IRQ_IPC0);
return EC_SUCCESS;
}
@@ -273,7 +304,7 @@ static void ipi_enable_ipc0_deferred(void)
/* All tasks are up, we can safely enable IPC0 IRQ now. */
SCP_INTC_IRQ_ENABLE |= IPC0_IRQ_EN;
- task_enable_irq(SCP_IRQ_IPC0);
+ ipi_enable_irq(SCP_IRQ_IPC0);
ipi_ready = 1;
diff --git a/chip/mt_scp/ipi_chip.h b/chip/mt_scp/ipi_chip.h
index ea05424804..b4178ae7e0 100644
--- a/chip/mt_scp/ipi_chip.h
+++ b/chip/mt_scp/ipi_chip.h
@@ -68,6 +68,19 @@ struct rpmsg_ns_msg {
*/
void ipc_handler(void);
+/*
+ * An IPC IRQ could be shared across many IPI handlers.
+ * Those handlers would usually operate on disabling or enabling the IPC IRQ.
+ * This may disorder the actual timing to on/off the IRQ when there are many
+ * tasks try to operate on it. As a result, any access to the SCP_IRQ_*
+ * should go through ipi_{en,dis}able_irq(), which support a counter to
+ * enable/disable the IRQ at correct timeing.
+ */
+/* Disable IPI IRQ. */
+void ipi_disable_irq(int irq);
+/* Enable IPI IRQ. */
+void ipi_enable_irq(int irq);
+
/* IPI tables */
extern void (*ipi_handler_table[])(int32_t, void *, uint32_t);
extern int *ipi_wakeup_table[];