From 7a4a571185f4ad491d591d6d5e26e117bf783612 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Tue, 19 Nov 2019 06:44:18 +0800 Subject: [PATCH 1/2] byt-ipc: refine the IPC DONE mask This is aim to harden the robustness to avoid IPC IRQ storm, we only expect to receive ACK/reply IPC from host after we have initiated an IPC to host. Imagine that the host can keep sending ACK IPC(or even storm ACK IPCs) intentiionally or by accident, with today's code the FW will keep receiving this ACK IPC interrupts and looks hang, we hit this similar issue on host side and are doing similar fix on Linux side. To fix that, refine the IPCD DONE mask sequence as below: 1. After initialized, mask the IPCD DONE. 2. Enable/unmask IPCD DONE at new message sending to host. 3. Disable/mask IPCD DONE once the FW initiated IPC handling is done. Signed-off-by: Keyon Jie --- src/drivers/intel/baytrail/ipc.c | 11 ++++++----- src/platform/baytrail/platform.c | 3 +++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/drivers/intel/baytrail/ipc.c b/src/drivers/intel/baytrail/ipc.c index f4643f05a2fb..189555c44bba 100644 --- a/src/drivers/intel/baytrail/ipc.c +++ b/src/drivers/intel/baytrail/ipc.c @@ -55,9 +55,6 @@ static void do_notify(void) /* clear DONE bit - tell Host we have completed */ shim_write(SHIM_IPCDH, shim_read(SHIM_IPCDH) & ~SHIM_IPCDH_DONE); - - /* unmask Done interrupt */ - shim_write(SHIM_IMRD, shim_read(SHIM_IMRD) & ~SHIM_IMRD_DONE); } static void irq_handler(void *arg) @@ -150,6 +147,9 @@ void ipc_platform_send_msg(struct ipc *ipc) ipc->shared_ctx->dsp_msg = msg; tracev_ipc("ipc: msg tx -> 0x%x", msg->header); + /* Unmask Done interrupts first to receive ack */ + shim_write(SHIM_IMRD, shim_read(SHIM_IMRD) & ~SHIM_IMRD_DONE); + /* now interrupt host to tell it we have message sent */ shim_write(SHIM_IPCDL, msg->header); shim_write(SHIM_IPCDH, SHIM_IPCDH_BUSY); @@ -202,9 +202,10 @@ int platform_ipc_init(struct ipc *ipc) interrupt_register(PLATFORM_IPC_INTERRUPT, irq_handler, ipc); interrupt_enable(PLATFORM_IPC_INTERRUPT, ipc); - /* Unmask Busy and Done interrupts */ + /* Unmask Busy and mask Done interrupts */ imrd = shim_read(SHIM_IMRD); - imrd &= ~(SHIM_IMRD_BUSY | SHIM_IMRD_DONE); + imrd &= ~SHIM_IMRD_BUSY; + imrd |= SHIM_IMRD_DONE; shim_write(SHIM_IMRD, imrd); return 0; diff --git a/src/platform/baytrail/platform.c b/src/platform/baytrail/platform.c index d14072bd79c0..66eefd6ae170 100644 --- a/src/platform/baytrail/platform.c +++ b/src/platform/baytrail/platform.c @@ -135,6 +135,9 @@ int platform_boot_complete(uint32_t boot_message) mailbox_dspbox_write(sizeof(ready), &sram_window, sram_window.ext_hdr.hdr.size); + /* Unmask Done interrupts first to receive ack */ + shim_write(SHIM_IMRD, shim_read(SHIM_IMRD) & ~SHIM_IMRD_DONE); + /* now interrupt host to tell it we are done booting */ shim_write(SHIM_IPCDL, SOF_IPC_FW_READY | outbox); shim_write(SHIM_IPCDH, SHIM_IPCDH_BUSY); From 26644bb3e7b6769a3a77ae830722587b5c75ea6f Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 22 Nov 2019 05:34:14 +0800 Subject: [PATCH 2/2] hsw-ipc: refine the IPC DONE mask This is aim to harden the robustness to avoid IPC IRQ storm, we only expect to receive ACK/reply IPC from host after we have initiated an IPC to host. Imagine that the host can keep sending ACK IPC(or even storm ACK IPCs) intentiionally or by accident, with today's code the FW will keep receiving this ACK IPC interrupts and looks hang, we hit this similar issue on host side and are doing similar fix on Linux side. To fix that, refine the IPCD DONE mask sequence as below: 1. After initialized, mask the IPCD DONE. 2. Enable/unmask IPCD DONE at new message sending to host. 3. Disable/mask IPCD DONE once the FW initiated IPC handling is done. Signed-off-by: Keyon Jie --- src/drivers/intel/haswell/ipc.c | 13 +++++++------ src/platform/haswell/platform.c | 3 +++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/drivers/intel/haswell/ipc.c b/src/drivers/intel/haswell/ipc.c index 97365a9233b9..8b590a662c38 100644 --- a/src/drivers/intel/haswell/ipc.c +++ b/src/drivers/intel/haswell/ipc.c @@ -55,9 +55,6 @@ static void do_notify(void) /* clear DONE bit - tell Host we have completed */ shim_write(SHIM_IPCD, 0); - - /* unmask Done interrupt */ - shim_write(SHIM_IMRD, shim_read(SHIM_IMRD) & ~SHIM_IMRD_DONE); } static void irq_handler(void *arg) @@ -70,7 +67,7 @@ static void irq_handler(void *arg) tracev_ipc("ipc: irq isr 0x%x", isr); - if (isr & SHIM_ISRD_DONE) { + if (isr & SHIM_ISRD_DONE && !(imrd & SHIM_IMRD_DONE)) { /* Mask Done interrupt before return */ shim_write(SHIM_IMRD, shim_read(SHIM_IMRD) | SHIM_IMRD_DONE); @@ -141,6 +138,9 @@ void ipc_platform_send_msg(struct ipc *ipc) ipc->shared_ctx->dsp_msg = msg; tracev_ipc("ipc: msg tx -> 0x%x", msg->header); + /* Unmask Done interrupts first to receive ack */ + shim_write(SHIM_IMRD, shim_read(SHIM_IMRD) & ~SHIM_IMRD_DONE); + /* now interrupt host to tell it we have message sent */ shim_write(SHIM_IPCD, SHIM_IPCD_BUSY); @@ -192,9 +192,10 @@ int platform_ipc_init(struct ipc *ipc) interrupt_register(PLATFORM_IPC_INTERRUPT, irq_handler, ipc); interrupt_enable(PLATFORM_IPC_INTERRUPT, ipc); - /* Unmask Busy and Done interrupts */ + /* Unmask Busy and mask Done interrupts */ imrd = shim_read(SHIM_IMRD); - imrd &= ~(SHIM_IMRD_BUSY | SHIM_IMRD_DONE); + imrd &= ~SHIM_IMRD_BUSY; + imrd |= SHIM_IMRD_DONE; shim_write(SHIM_IMRD, imrd); return 0; diff --git a/src/platform/haswell/platform.c b/src/platform/haswell/platform.c index 0fcf3d08fc55..d700d2ebc8b5 100644 --- a/src/platform/haswell/platform.c +++ b/src/platform/haswell/platform.c @@ -131,6 +131,9 @@ int platform_boot_complete(uint32_t boot_message) mailbox_dspbox_write(sizeof(ready), &sram_window, sram_window.ext_hdr.hdr.size); + /* Unmask Done interrupts first to receive ack */ + shim_write(SHIM_IMRD, shim_read(SHIM_IMRD) & ~SHIM_IMRD_DONE); + /* now interrupt host to tell it we are done booting */ shim_write(SHIM_IPCD, outbox | SHIM_IPCD_BUSY);