From 3b966f3d578861879dceb2c1b5d6193c222e5bc4 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Mon, 10 Sep 2018 14:54:41 +0800 Subject: [PATCH 1/5] ipc: remove waiti running in IRQ level higher than passive level waiti would cause FW panic if it is called in irq level higher than passive level Signed-off-by: Rander Wang --- src/include/sof/wait.h | 28 ++++++++++++++++++++++++++++ src/ipc/ipc.c | 5 +++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/include/sof/wait.h b/src/include/sof/wait.h index 9016da8aa730..3bf7c2995b39 100644 --- a/src/include/sof/wait.h +++ b/src/include/sof/wait.h @@ -42,8 +42,11 @@ #include #include #include +#include +#include #include #include +#include #if DEBUG_LOCKS #define wait_atomic_check \ @@ -54,6 +57,8 @@ #define wait_atomic_check #endif +#define DEFAULT_TRY_TIMES 8 + typedef struct { uint32_t complete; struct work work; @@ -158,4 +163,27 @@ static inline void wait_delay(uint64_t number_of_clks) idelay(PLATFORM_DEFAULT_DELAY); } +static inline int poll_for_completion_delay(completion_t *comp, uint64_t us) +{ + uint64_t tick = clock_us_to_ticks(CLK_CPU, us); + uint32_t tries = DEFAULT_TRY_TIMES; + uint64_t delta = tick / tries; + + if (!delta) { + delta = us; + tries = 1; + } + + while (!wait_is_completed(comp)) { + if (!tries--) { + trace_error(TRACE_CLASS_WAIT, "ewt"); + return -EIO; + } + + wait_delay(delta); + } + + return 0; +} + #endif diff --git a/src/ipc/ipc.c b/src/ipc/ipc.c index 22951e9b80bd..fb1db34faa9c 100644 --- a/src/ipc/ipc.c +++ b/src/ipc/ipc.c @@ -501,8 +501,9 @@ int ipc_get_page_descriptors(struct dma *dmac, uint8_t *page_table, } /* wait for DMA to complete */ - complete.timeout = PLATFORM_HOST_DMA_TIMEOUT; - ret = wait_for_completion_timeout(&complete); + ret = poll_for_completion_delay(&complete, PLATFORM_DMA_TIMEOUT); + if (ret < 0) + trace_ipc_error("eDt"); /* compressed page tables now in buffer at _ipc->page_table */ out: From a2f0dd0931de8e210f522d98341eab6d5f99d946 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Mon, 17 Sep 2018 17:22:45 +0800 Subject: [PATCH 2/5] wait: add a function to wait for a register ready by pulling method The register status is checked by polling its value in request time Signed-off-by: Rander Wang --- src/include/sof/wait.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/include/sof/wait.h b/src/include/sof/wait.h index 3bf7c2995b39..9746e96a4370 100644 --- a/src/include/sof/wait.h +++ b/src/include/sof/wait.h @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -186,4 +187,27 @@ static inline int poll_for_completion_delay(completion_t *comp, uint64_t us) return 0; } +static inline int poll_for_register_delay(uint32_t reg, + uint32_t mask, + uint32_t val, uint64_t us) +{ + uint64_t tick = clock_us_to_ticks(CLK_CPU, us); + uint32_t tries = DEFAULT_TRY_TIMES; + uint64_t delta = tick / tries; + + if (!delta) { + delta = us; + tries = 1; + } + + while ((io_reg_read(reg) & mask) != val) { + if (!tries--) { + trace_error(TRACE_CLASS_WAIT, "ewt"); + return -EIO; + } + wait_delay(delta); + } + return 0; +} + #endif From edef72f3140f8e31f029648e883dd2590c6d1122 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Mon, 10 Sep 2018 14:34:37 +0800 Subject: [PATCH 3/5] dma: fix dma issue on BYT It is caused by the change of removing dai waiting after stop. dma should not be stopped in dai_comp_trigger if HW LLI is not enable. At this time DMA is still working and it can't be stopped DMA engine would stop the channel automatically after each transfer. So just query channel status to stop dma on BYT Signed-off-by: Pan Xiuli Signed-off-by: Rander Wang --- src/drivers/intel/dw-dma.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/drivers/intel/dw-dma.c b/src/drivers/intel/dw-dma.c index b94917ec5ec4..d42e7d67f113 100644 --- a/src/drivers/intel/dw-dma.c +++ b/src/drivers/intel/dw-dma.c @@ -120,6 +120,7 @@ #define INT_UNMASK_ALL 0xFFFF #define CHAN_ENABLE(chan) (0x101 << chan) #define CHAN_DISABLE(chan) (0x100 << chan) +#define CHAN_MASK(chan) (0x1 << chan) #define DW_CFG_CH_SUSPEND 0x100 #define DW_CFG_CH_FIFO_EMPTY 0x200 @@ -491,6 +492,31 @@ static int dw_dma_pause(struct dma *dma, int channel) return 0; } +#if defined CONFIG_BAYTRAIL || defined CONFIG_CHERRYTRAIL +static int dw_dma_stop(struct dma *dma, int channel) +{ + struct dma_pdata *p = dma_get_drvdata(dma); + int ret = 0; + uint32_t flags; + uint32_t val = 0; + + spin_lock_irq(&dma->lock, flags); + + trace_dma("DDi"); + + ret = poll_for_register_delay(dma_base(dma) + DW_DMA_CHAN_EN, + CHAN_MASK(channel), val, + PLATFORM_DMA_TIMEOUT); + if (ret < 0) + trace_dma_error("esp"); + + dw_write(dma, DW_CLEAR_BLOCK, 0x1 << channel); + p->chan[channel].status = COMP_STATE_PREPARE; + + spin_unlock_irq(&dma->lock, flags); + return ret; +} +#else static int dw_dma_stop(struct dma *dma, int channel) { struct dma_pdata *p = dma_get_drvdata(dma); @@ -528,6 +554,7 @@ static int dw_dma_stop(struct dma *dma, int channel) spin_unlock_irq(&dma->lock, flags); return ret; } +#endif /* fill in "status" with current DMA channel state and position */ static int dw_dma_status(struct dma *dma, int channel, From cc4e8c82052e4c66f86c4bc562d1145ed2ec528e Mon Sep 17 00:00:00 2001 From: Pan Xiuli Date: Mon, 17 Sep 2018 14:22:32 +0800 Subject: [PATCH 4/5] task: clear interrupt earlier list_item_for_safe is safe for item deletion, but when a new item is appended to the list. It seems the item could not be go through. To fix this limitation, move interrupt clear to make sure every IRQ is handled. Do not try to handle two task with one IRQ handler. Signed-off-by: Pan Xiuli --- src/arch/xtensa/include/arch/task.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/arch/xtensa/include/arch/task.h b/src/arch/xtensa/include/arch/task.h index 9f1206b26a8b..fb4c98b9241c 100644 --- a/src/arch/xtensa/include/arch/task.h +++ b/src/arch/xtensa/include/arch/task.h @@ -138,6 +138,9 @@ static void _irq_task(void *arg) uint32_t flags; spin_lock_irq(&irq_task->lock, flags); + + interrupt_clear(irq_task->irq); + list_for_item_safe(clist, tlist, &irq_task->list) { task = container_of(clist, struct task, irq_list); @@ -152,8 +155,6 @@ static void _irq_task(void *arg) spin_lock_irq(&irq_task->lock, flags); } - interrupt_clear(irq_task->irq); - spin_unlock_irq(&irq_task->lock, flags); } From 5b39370e1e198acd0667666abfb242d88de74319 Mon Sep 17 00:00:00 2001 From: Pan Xiuli Date: Tue, 18 Sep 2018 11:44:10 +0800 Subject: [PATCH 5/5] scheduler: refine schedule_edf function to handle multiple task There will be new task added in schedule_edf function, refine the function to handle multiple task. Signed-off-by: Pan Xiuli --- src/lib/schedule.c | 56 +++++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/lib/schedule.c b/src/lib/schedule.c index d2bea721403f..e1e5eaf93150 100644 --- a/src/lib/schedule.c +++ b/src/lib/schedule.c @@ -102,13 +102,9 @@ static inline struct task *edf_get_next(uint64_t current, uint64_t delta; uint64_t deadline; int reschedule = 0; - uint32_t flags; - spin_lock_irq(&sch->lock, flags); - /* any tasks in the scheduler ? */ if (list_is_empty(&sch->list)) { - spin_unlock_irq(&sch->lock, flags); return NULL; } @@ -159,7 +155,6 @@ static inline struct task *edf_get_next(uint64_t current, } } - spin_unlock_irq(&sch->lock, flags); return next_task; } @@ -187,35 +182,40 @@ static struct task *schedule_edf(void) tracev_pipe("edf"); - /* get the current time */ - current = platform_timer_get(platform_timer); - - /* get next task to be scheduled */ - task = edf_get_next(current, NULL); - interrupt_clear(PLATFORM_SCHEDULE_IRQ); - /* any tasks ? */ - if (task == NULL) - return NULL; + while (!list_is_empty(&sch->list)) { + spin_lock_irq(&sch->lock, flags); - /* can task be started now ? */ - if (task->start > current) { - /* no, then schedule wake up */ - future_task = task; - } else { - /* yes, run current task */ - task->start = current; + /* get the current time */ + current = platform_timer_get(platform_timer); - /* init task for running */ - wait_init(&task->complete); - spin_lock_irq(&sch->lock, flags); - task->state = TASK_STATE_RUNNING; - list_item_del(&task->list); + /* get next task to be scheduled */ + task = edf_get_next(current, NULL); spin_unlock_irq(&sch->lock, flags); - /* now run task at correct run level */ - run_task(task); + /* any tasks ? */ + if (!task) + return NULL; + + /* can task be started now ? */ + if (task->start <= current) { + /* yes, run current task */ + task->start = current; + + /* init task for running */ + spin_lock_irq(&sch->lock, flags); + task->state = TASK_STATE_RUNNING; + list_item_del(&task->list); + spin_unlock_irq(&sch->lock, flags); + + /* now run task at correct run level */ + run_task(task); + } else { + /* no, then schedule wake up */ + future_task = task; + break; + } } /* tell caller about future task */