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); } 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, diff --git a/src/include/sof/wait.h b/src/include/sof/wait.h index 9016da8aa730..9746e96a4370 100644 --- a/src/include/sof/wait.h +++ b/src/include/sof/wait.h @@ -36,14 +36,18 @@ #include #include #include +#include #include #include #include #include #include #include +#include +#include #include #include +#include #if DEBUG_LOCKS #define wait_atomic_check \ @@ -54,6 +58,8 @@ #define wait_atomic_check #endif +#define DEFAULT_TRY_TIMES 8 + typedef struct { uint32_t complete; struct work work; @@ -158,4 +164,50 @@ 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; +} + +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 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: 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 */