blob: fa5a372b345a96f22f1a94f5b357855c9d363810 [file] [log] [blame]
/*
* Copyright (c) 2017, Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Intel Corporation nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
*
*/
/**
* \file arch/xtensa/include/arch/task.h
* \brief Arch task header file
* \authors Liam Girdwood <liam.r.girdwood@linux.intel.com>
*/
#ifndef __ARCH_TASK_H_
#define __ARCH_TASK_H_
#include <sof/schedule.h>
#include <sof/interrupt.h>
#include <platform/platform.h>
#include <sof/debug.h>
#include <sof/alloc.h>
/** \brief IRQ task data. */
struct irq_task {
spinlock_t lock; /**< lock */
struct list_item list; /**< list of tasks */
uint32_t irq; /**< IRQ level */
};
/**
* \brief Returns IRQ low task data.
* \return Pointer to pointer of IRQ low task data.
*/
struct irq_task **task_irq_low_get(void);
/**
* \brief Returns IRQ medium task data.
* \return Pointer to pointer of IRQ medium task data.
*/
struct irq_task **task_irq_med_get(void);
/**
* \brief Returns IRQ high task data.
* \return Pointer to pointer of IRQ high task data.
*/
struct irq_task **task_irq_high_get(void);
/**
* \brief Retrieves task IRQ level.
* \param[in,out] task Task data.
* \return IRQ level.
*/
static inline uint32_t task_get_irq(struct task *task)
{
uint32_t irq;
switch (task->priority) {
case TASK_PRI_MED + 1 ... TASK_PRI_LOW:
irq = PLATFORM_IRQ_TASK_LOW;
break;
case TASK_PRI_HIGH ... TASK_PRI_MED - 1:
irq = PLATFORM_IRQ_TASK_HIGH;
break;
case TASK_PRI_MED:
default:
irq = PLATFORM_IRQ_TASK_MED;
break;
}
return irq;
}
/**
* \brief Adds task to the list per IRQ level.
* \param[in,out] task Task data.
*/
static inline void task_set_data(struct task *task)
{
struct list_item *dst = NULL;
switch (task->priority) {
case TASK_PRI_MED + 1 ... TASK_PRI_LOW:
dst = &((*task_irq_low_get())->list);
break;
case TASK_PRI_HIGH ... TASK_PRI_MED - 1:
dst = &((*task_irq_high_get())->list);
break;
case TASK_PRI_MED:
default:
dst = &((*task_irq_med_get())->list);
break;
}
list_item_append(&task->irq_list, dst);
}
/**
* \brief Interrupt handler for the IRQ task.
* \param[in,out] arg IRQ task data.
*/
static void _irq_task(void *arg)
{
struct irq_task *irq_task = *(struct irq_task **)arg;
struct list_item *tlist;
struct list_item *clist;
struct task *task;
uint32_t flags;
/* intentionally don't lock list to have task added from schedule irq */
list_for_item(tlist, &irq_task->list) {
task = container_of(tlist, struct task, irq_list);
if (task->func)
task->func(task->data);
schedule_task_complete(task);
}
spin_lock_irq(&irq_task->lock, flags);
list_for_item_safe(clist, tlist, &irq_task->list) {
task = container_of(clist, struct task, irq_list);
list_item_del(&task->irq_list);
}
interrupt_clear(irq_task->irq);
spin_unlock_irq(&irq_task->lock, flags);
}
/**
* \brief Runs task.
* \param[in,out] task Task data.
*/
static inline void arch_run_task(struct task *task)
{
uint32_t irq;
task_set_data(task);
irq = task_get_irq(task);
interrupt_set(irq);
}
/**
* \brief Allocates IRQ tasks.
*/
static inline int arch_allocate_tasks(void)
{
/* irq low */
struct irq_task **low = task_irq_low_get();
*low = rzalloc(RZONE_RUNTIME, SOF_MEM_CAPS_RAM, sizeof(**low));
if (!*low)
return -ENOMEM;
list_init(&((*low)->list));
spinlock_init(&((*low)->lock));
(*low)->irq = PLATFORM_IRQ_TASK_LOW;
/* irq medium */
struct irq_task **med = task_irq_med_get();
*med = rzalloc(RZONE_RUNTIME, SOF_MEM_CAPS_RAM, sizeof(**med));
if (!*med)
return -ENOMEM;
list_init(&((*med)->list));
spinlock_init(&((*med)->lock));
(*med)->irq = PLATFORM_IRQ_TASK_MED;
/* irq high */
struct irq_task **high = task_irq_high_get();
*high = rzalloc(RZONE_RUNTIME, SOF_MEM_CAPS_RAM, sizeof(**high));
if (!*high)
return -ENOMEM;
list_init(&((*high)->list));
spinlock_init(&((*high)->lock));
(*high)->irq = PLATFORM_IRQ_TASK_HIGH;
return 0;
}
/**
* \brief Frees IRQ tasks.
*/
static inline void arch_free_tasks(void)
{
uint32_t flags;
/* free IRQ low task */
struct irq_task **low = task_irq_low_get();
spin_lock_irq(&(*low)->lock, flags);
interrupt_disable(PLATFORM_IRQ_TASK_LOW);
interrupt_unregister(PLATFORM_IRQ_TASK_LOW);
list_item_del(&(*low)->list);
spin_unlock_irq(&(*low)->lock, flags);
rfree(*low);
/* free IRQ medium task */
struct irq_task **med = task_irq_med_get();
spin_lock_irq(&(*med)->lock, flags);
interrupt_disable(PLATFORM_IRQ_TASK_MED);
interrupt_unregister(PLATFORM_IRQ_TASK_MED);
list_item_del(&(*med)->list);
spin_unlock_irq(&(*med)->lock, flags);
rfree(*med);
/* free IRQ high task */
struct irq_task **high = task_irq_high_get();
spin_lock_irq(&(*high)->lock, flags);
interrupt_disable(PLATFORM_IRQ_TASK_HIGH);
interrupt_unregister(PLATFORM_IRQ_TASK_HIGH);
list_item_del(&(*high)->list);
spin_unlock_irq(&(*high)->lock, flags);
rfree(*high);
}
/**
* \brief Assigns IRQ tasks to interrupts.
*/
static inline int arch_assign_tasks(void)
{
/* irq low */
interrupt_register(PLATFORM_IRQ_TASK_LOW, _irq_task,
task_irq_low_get());
interrupt_enable(PLATFORM_IRQ_TASK_LOW);
/* irq medium */
interrupt_register(PLATFORM_IRQ_TASK_MED, _irq_task,
task_irq_med_get());
interrupt_enable(PLATFORM_IRQ_TASK_MED);
/* irq high */
interrupt_register(PLATFORM_IRQ_TASK_HIGH, _irq_task,
task_irq_high_get());
interrupt_enable(PLATFORM_IRQ_TASK_HIGH);
return 0;
}
#endif