mirror of https://github.com/thesofproject/sof.git
drivers: mtk: add drivers for mt8195
Add irq, ipc and timer drivers for mtk mt8195 About mt8195: Two domains of IRQ: IRQ_EXT_DOMAIN0 and IRQ_EXT_DOMAIN1 IRQ num: 25 interrupts IPC: use mbx0 and mbx1 to transfer ipc msg Signed-off-by: YC Hung <yc.hung@mediatek.com> Signed-off-by: Allen-KH Cheng <allen-kh.cheng@mediatek.com>
This commit is contained in:
parent
ef37ebaabf
commit
0cad7a04fb
|
@ -0,0 +1,266 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//
|
||||
// Copyright(c) 2021 Mediatek
|
||||
//
|
||||
// Author: Allen-KH Cheng <allen-kh.cheng@mediatek.com>
|
||||
|
||||
#include <sof/common.h>
|
||||
#include <sof/bit.h>
|
||||
#include <sof/drivers/interrupt.h>
|
||||
#include <sof/lib/cpu.h>
|
||||
#include <sof/lib/io.h>
|
||||
#include <sof/lib/memory.h>
|
||||
#include <sof/lib/uuid.h>
|
||||
#include <sof/list.h>
|
||||
#include <sof/spinlock.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* fa00558c-d653-4851-a03a-b21f125a9524 */
|
||||
DECLARE_SOF_UUID("irq-mt8195", irq_mt8195_uuid, 0xfa00558c, 0xd653, 0x4851,
|
||||
0xa0, 0x3a, 0xb2, 0x1f, 0x12, 0x5a, 0x95, 0x24);
|
||||
|
||||
DECLARE_TR_CTX(int_tr, SOF_UUID(irq_mt8195_uuid), LOG_LEVEL_INFO);
|
||||
|
||||
/* os timer reg value * 77ns 13M os timer
|
||||
* 1 ms: 12987* 1.5ms: 19436
|
||||
* 2 ms: 25974* 3 ms: 38961
|
||||
*/
|
||||
|
||||
static void irq_mask_all(void)
|
||||
{
|
||||
io_reg_update_bits(RG_DSP_IRQ_EN, 0xffffffff, 0x0);
|
||||
io_reg_update_bits(DSP_IRQ_EN, 0xffffffff, 0x0);
|
||||
}
|
||||
|
||||
static void mtk_irq_mask(struct irq_desc *desc, uint32_t irq, unsigned int core)
|
||||
{
|
||||
int32_t in_irq, level;
|
||||
|
||||
if (!desc) {
|
||||
level = GET_INTLEVEL(irq);
|
||||
in_irq = GET_INTERRUPT_ID(irq);
|
||||
if (level > MAX_IRQ_NUM) {
|
||||
tr_err(&int_tr, "Invalid interrupt %d", irq);
|
||||
return;
|
||||
}
|
||||
|
||||
io_reg_update_bits(RG_DSP_IRQ_EN, BIT(in_irq), 0x0);
|
||||
} else {
|
||||
switch (desc->irq) {
|
||||
case IRQ_EXT_DOMAIN0:
|
||||
io_reg_update_bits(RG_DSP_IRQ_EN, BIT(irq + IRQ_EXT_DOMAIN0_OFFSET), 0x0);
|
||||
break;
|
||||
case IRQ_EXT_DOMAIN1:
|
||||
io_reg_update_bits(DSP_IRQ_EN, BIT(irq), 0x0);
|
||||
break;
|
||||
default:
|
||||
tr_err(&int_tr, "Invalid interrupt %d", desc->irq);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void mtk_irq_unmask(struct irq_desc *desc, uint32_t irq, unsigned int core)
|
||||
{
|
||||
int32_t in_irq, level;
|
||||
|
||||
if (!desc) {
|
||||
level = GET_INTLEVEL(irq);
|
||||
in_irq = GET_INTERRUPT_ID(irq);
|
||||
if (level > MAX_IRQ_NUM) {
|
||||
tr_err(&int_tr, "Invalid interrupt %d", irq);
|
||||
return;
|
||||
}
|
||||
|
||||
io_reg_update_bits(RG_DSP_IRQ_EN, BIT(in_irq), BIT(in_irq));
|
||||
} else {
|
||||
switch (desc->irq) {
|
||||
case IRQ_EXT_DOMAIN0:
|
||||
io_reg_update_bits(RG_DSP_IRQ_EN, BIT(irq + IRQ_EXT_DOMAIN0_OFFSET),
|
||||
BIT(irq + IRQ_EXT_DOMAIN0_OFFSET));
|
||||
break;
|
||||
case IRQ_EXT_DOMAIN1:
|
||||
io_reg_update_bits(DSP_IRQ_EN, BIT(irq), BIT(irq));
|
||||
break;
|
||||
default:
|
||||
tr_err(&int_tr, "Invalid interrupt %d", desc->irq);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t mtk_get_irq_interrupts(uint32_t irq)
|
||||
{
|
||||
uint32_t irq_status;
|
||||
|
||||
switch (irq) {
|
||||
case IRQ_NUM_EXT_LEVEL23:
|
||||
irq_status = io_reg_read(DSP_IRQ_STATUS);
|
||||
irq_status &= IRQ_EXT_DOMAIN2_MASK;
|
||||
break;
|
||||
default:
|
||||
irq_status = io_reg_read(RG_DSP_IRQ_STATUS);
|
||||
irq_status &= IRQ_EXT_DOMAIN1_MASK;
|
||||
break;
|
||||
}
|
||||
return irq_status;
|
||||
}
|
||||
|
||||
static int get_first_irq(uint64_t ints)
|
||||
{
|
||||
return ffs(ints) - 1;
|
||||
}
|
||||
|
||||
static inline void mtk_handle_irq(struct irq_cascade_desc *cascade,
|
||||
uint32_t line_index, uint64_t status)
|
||||
{
|
||||
int core = cpu_get_id();
|
||||
struct list_item *clist;
|
||||
struct irq_desc *child = NULL;
|
||||
int bit;
|
||||
bool handled;
|
||||
|
||||
while (status) {
|
||||
bit = get_first_irq(status);
|
||||
handled = false;
|
||||
status &= ~(1ull << bit);
|
||||
|
||||
spin_lock(&cascade->lock);
|
||||
|
||||
list_for_item(clist, &cascade->child[bit].list) {
|
||||
child = container_of(clist, struct irq_desc, irq_list);
|
||||
|
||||
if (child->handler && (child->cpu_mask & 1 << core)) {
|
||||
child->handler(child->handler_arg);
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock(&cascade->lock);
|
||||
|
||||
if (!handled) {
|
||||
tr_err(&int_tr, "irq_handler(): not handled, bit %d", bit);
|
||||
if (line_index == IRQ_NUM_EXT_LEVEL23)
|
||||
io_reg_update_bits(DSP_IRQ_EN, BIT(bit), 0x0);
|
||||
else
|
||||
io_reg_update_bits(RG_DSP_IRQ_EN, BIT(bit), 0x0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void irq_handler(void *data, uint32_t line_index)
|
||||
{
|
||||
struct irq_desc *parent = data;
|
||||
struct irq_cascade_desc *cascade =
|
||||
container_of(parent, struct irq_cascade_desc, desc);
|
||||
uint64_t status;
|
||||
|
||||
status = mtk_get_irq_interrupts(line_index);
|
||||
|
||||
if (status)
|
||||
/* Handle current interrupts */
|
||||
mtk_handle_irq(cascade, line_index, status);
|
||||
else
|
||||
tr_err(&int_tr, "invalid interrupt status");
|
||||
}
|
||||
|
||||
#define DEFINE_IRQ_HANDLER(n) \
|
||||
static void irqhandler_##n(void *arg) \
|
||||
{ \
|
||||
irq_handler(arg, n); \
|
||||
}
|
||||
|
||||
DEFINE_IRQ_HANDLER(1)
|
||||
DEFINE_IRQ_HANDLER(23)
|
||||
|
||||
static const char mtk_irq_ext_domain0[] = "mtk_irq_ext_domain0";
|
||||
static const char mtk_irq_ext_domain1[] = "mtk_irq_ext_domain1";
|
||||
|
||||
static const struct irq_cascade_ops irq_ops = {
|
||||
.mask = mtk_irq_mask,
|
||||
.unmask = mtk_irq_unmask,
|
||||
};
|
||||
|
||||
static const struct irq_cascade_tmpl dsp_irq[] = {
|
||||
{
|
||||
.name = mtk_irq_ext_domain0,
|
||||
.irq = IRQ_NUM_EXT_LEVEL01,
|
||||
.handler = irqhandler_1,
|
||||
.ops = &irq_ops,
|
||||
.global_mask = false,
|
||||
},
|
||||
{
|
||||
.name = mtk_irq_ext_domain1,
|
||||
.irq = IRQ_NUM_EXT_LEVEL23,
|
||||
.handler = irqhandler_23,
|
||||
.ops = &irq_ops,
|
||||
.global_mask = false,
|
||||
},
|
||||
};
|
||||
|
||||
uint32_t mtk_get_irq_domain_id(int32_t irq)
|
||||
{
|
||||
uint32_t in_irq = GET_INTERRUPT_ID(irq);
|
||||
int32_t level = GET_INTLEVEL(irq);
|
||||
|
||||
if (in_irq >= DOMAIN1_MAX_IRQ_NUM)
|
||||
in_irq -= DOMAIN1_MAX_IRQ_NUM;
|
||||
|
||||
if (level == IRQ_EXT_DOMAIN0)
|
||||
return interrupt_get_irq(in_irq, dsp_irq[0].name);
|
||||
else
|
||||
return interrupt_get_irq(in_irq, dsp_irq[1].name);
|
||||
}
|
||||
|
||||
void platform_interrupt_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
irq_mask_all();
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(dsp_irq); i++)
|
||||
interrupt_cascade_register(dsp_irq + i);
|
||||
}
|
||||
|
||||
void platform_interrupt_set(uint32_t irq)
|
||||
{
|
||||
if (interrupt_is_dsp_direct(irq))
|
||||
arch_interrupt_set(irq);
|
||||
}
|
||||
|
||||
void platform_interrupt_clear(uint32_t irq, uint32_t mask)
|
||||
{
|
||||
if (interrupt_is_dsp_direct(irq))
|
||||
arch_interrupt_clear(irq);
|
||||
}
|
||||
|
||||
uint32_t platform_interrupt_get_enabled(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void interrupt_mask(uint32_t irq, unsigned int cpu)
|
||||
{
|
||||
struct irq_cascade_desc *cascade = interrupt_get_parent(irq);
|
||||
|
||||
if (cascade && cascade->ops->mask)
|
||||
cascade->ops->mask(&cascade->desc, irq - cascade->irq_base,
|
||||
cpu);
|
||||
else
|
||||
mtk_irq_mask(NULL, irq, 0);
|
||||
}
|
||||
|
||||
void interrupt_unmask(uint32_t irq, unsigned int cpu)
|
||||
{
|
||||
struct irq_cascade_desc *cascade = interrupt_get_parent(irq);
|
||||
|
||||
if (cascade && cascade->ops->unmask)
|
||||
cascade->ops->unmask(&cascade->desc, irq - cascade->irq_base,
|
||||
cpu);
|
||||
else
|
||||
mtk_irq_unmask(NULL, irq, 0);
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//
|
||||
// Copyright(c) 2021 Mediatek
|
||||
//
|
||||
// Author: Allen-KH Cheng <Allen-KH.Cheng@mediatek.com>
|
||||
|
||||
#include <sof/debug/panic.h>
|
||||
#include <sof/drivers/interrupt.h>
|
||||
#include <sof/ipc/driver.h>
|
||||
#include <sof/ipc/msg.h>
|
||||
#include <sof/ipc/schedule.h>
|
||||
#include <sof/lib/alloc.h>
|
||||
#include <sof/lib/dma.h>
|
||||
#include <sof/lib/mailbox.h>
|
||||
#include <sof/lib/memory.h>
|
||||
#include <sof/lib/uuid.h>
|
||||
#include <sof/lib/wait.h>
|
||||
#include <sof/list.h>
|
||||
#include <sof/platform.h>
|
||||
#include <sof/schedule/edf_schedule.h>
|
||||
#include <sof/schedule/schedule.h>
|
||||
#include <sof/schedule/task.h>
|
||||
#include <sof/spinlock.h>
|
||||
#include <ipc/header.h>
|
||||
#include <ipc/topology.h>
|
||||
#include <ipc/trace.h>
|
||||
#include <platform/drivers/mt_reg_base.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define IPC_DSPMBOX_DSP_RSP 0
|
||||
#define IPC_DSPMBOX_DSP_REQ 1
|
||||
|
||||
/* 389c9186-5a7d-4ad1-a02c-a02ecdadfb33 */
|
||||
DECLARE_SOF_UUID("ipc-task", ipc_task_uuid, 0x389c9186, 0x5a7d, 0x4ad1,
|
||||
0xa0, 0x2c, 0xa0, 0x2e, 0xcd, 0xad, 0xfb, 0x33);
|
||||
|
||||
static struct ipc *local_ipc;
|
||||
|
||||
struct ipc_data {
|
||||
struct ipc_data_host_buffer dh_buffer;
|
||||
};
|
||||
|
||||
static void mbox0_handler(void *args)
|
||||
{
|
||||
uint32_t op = io_reg_read(MTK_DSP_MBOX_IN_CMD(0));
|
||||
|
||||
/* clear interrupt */
|
||||
io_reg_write(MTK_DSP_MBOX_IN_CMD_CLR(0), op);
|
||||
ipc_schedule_process(local_ipc);
|
||||
}
|
||||
|
||||
static void mbox1_handler(void *args)
|
||||
{
|
||||
uint32_t op = io_reg_read(MTK_DSP_MBOX_IN_CMD(1));
|
||||
|
||||
/* clear interrupt */
|
||||
io_reg_write(MTK_DSP_MBOX_IN_CMD_CLR(1), op);
|
||||
local_ipc->is_notification_pending = false;
|
||||
}
|
||||
|
||||
void trigger_irq_to_host_rsp(void)
|
||||
{
|
||||
io_reg_write(MTK_DSP_MBOX_OUT_CMD(0), ADSP_IPI_OP_RSP);
|
||||
}
|
||||
|
||||
void trigger_irq_to_host_req(void)
|
||||
{
|
||||
io_reg_write(MTK_DSP_MBOX_OUT_CMD(1), ADSP_IPI_OP_REQ);
|
||||
}
|
||||
|
||||
enum task_state ipc_platform_do_cmd(void *data)
|
||||
{
|
||||
ipc_cmd_hdr *hdr;
|
||||
|
||||
hdr = mailbox_validate();
|
||||
ipc_cmd(hdr);
|
||||
|
||||
return SOF_TASK_STATE_COMPLETED;
|
||||
}
|
||||
|
||||
void ipc_platform_complete_cmd(void *data)
|
||||
{
|
||||
struct ipc *ipc = data;
|
||||
|
||||
trigger_irq_to_host_rsp();
|
||||
while (ipc->pm_prepare_D3)
|
||||
wait_for_interrupt(0);
|
||||
}
|
||||
|
||||
int ipc_platform_send_msg(struct ipc_msg *msg)
|
||||
{
|
||||
struct ipc *ipc = ipc_get();
|
||||
|
||||
if (ipc->is_notification_pending)
|
||||
return -EBUSY;
|
||||
|
||||
/* now send the message */
|
||||
mailbox_dspbox_write(0, msg->tx_data, msg->tx_size);
|
||||
list_item_del(&msg->list);
|
||||
ipc->is_notification_pending = true;
|
||||
|
||||
/* now interrupt host to tell it we have sent a message */
|
||||
trigger_irq_to_host_req();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if CONFIG_HOST_PTABLE
|
||||
struct ipc_data_host_buffer *ipc_platform_get_host_buffer(struct ipc *ipc)
|
||||
{
|
||||
struct ipc_data *iipc = ipc_get_drvdata(ipc);
|
||||
|
||||
return &iipc->dh_buffer;
|
||||
}
|
||||
#endif
|
||||
|
||||
int platform_ipc_init(struct ipc *ipc)
|
||||
{
|
||||
uint32_t mbox_irq0, mbox_irq1;
|
||||
#if CONFIG_HOST_PTABLE
|
||||
struct ipc_data *iipc;
|
||||
|
||||
iipc = rzalloc(SOF_MEM_ZONE_SYS, 0, SOF_MEM_CAPS_RAM, sizeof(*iipc));
|
||||
ipc_set_drvdata(ipc, iipc);
|
||||
#else
|
||||
ipc_set_drvdata(ipc, NULL);
|
||||
#endif
|
||||
|
||||
local_ipc = ipc;
|
||||
|
||||
/* schedule */
|
||||
schedule_task_init_edf(&ipc->ipc_task, SOF_UUID(ipc_task_uuid), &ipc_task_ops, ipc, 0, 0);
|
||||
|
||||
#if CONFIG_HOST_PTABLE
|
||||
/* allocate page table buffer */
|
||||
iipc->dh_buffer.page_table =
|
||||
rzalloc(SOF_MEM_ZONE_SYS, 0, SOF_MEM_CAPS_RAM, PLATFORM_PAGE_TABLE_SIZE);
|
||||
|
||||
iipc->dh_buffer.dmac = dma_get(DMA_DIR_HMEM_TO_LMEM, 0, DMA_DEV_HOST, DMA_ACCESS_SHARED);
|
||||
if (!iipc->dh_buffer.dmac) {
|
||||
tr_err(&ipc_tr, "Unable to find DMA for host page table");
|
||||
panic(SOF_IPC_PANIC_IPC);
|
||||
}
|
||||
#endif
|
||||
|
||||
mbox_irq0 = mtk_get_irq_domain_id(LX_MBOX_IRQ0_B);
|
||||
mbox_irq1 = mtk_get_irq_domain_id(LX_MBOX_IRQ1_B);
|
||||
interrupt_register(mbox_irq0, mbox0_handler, ipc);
|
||||
interrupt_register(mbox_irq1, mbox1_handler, ipc);
|
||||
interrupt_enable(mbox_irq0, ipc);
|
||||
interrupt_enable(mbox_irq1, ipc);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//
|
||||
// Copyright(c) 2021 Mediatek
|
||||
//
|
||||
// Author:Fengquan Chen <fengquan.chen@mediatek.com>
|
||||
|
||||
#include <sof/audio/component_ext.h>
|
||||
#include <sof/drivers/interrupt.h>
|
||||
#include <sof/lib/memory.h>
|
||||
#include <sof/platform.h>
|
||||
#include <sof/drivers/timer.h>
|
||||
#include <ipc/stream.h>
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void platform_timer_start(struct timer *timer)
|
||||
{
|
||||
/* nothing to do for cpu timer */
|
||||
}
|
||||
|
||||
void platform_timer_stop(struct timer *timer)
|
||||
{
|
||||
arch_timer_disable(timer);
|
||||
}
|
||||
|
||||
int64_t platform_timer_set(struct timer *timer, uint64_t ticks)
|
||||
{
|
||||
return arch_timer_set(timer, ticks);
|
||||
}
|
||||
|
||||
void platform_timer_clear(struct timer *timer)
|
||||
{
|
||||
arch_timer_clear(timer);
|
||||
}
|
||||
|
||||
uint64_t platform_timer_get(struct timer *timer)
|
||||
{
|
||||
return arch_timer_get_system(timer);
|
||||
}
|
||||
|
||||
/* IRQs off in arch_timer_get_system() */
|
||||
uint64_t platform_timer_get_atomic(struct timer *timer)
|
||||
{
|
||||
return arch_timer_get_system(timer);
|
||||
}
|
||||
|
||||
/* get timestamp for host stream DMA position */
|
||||
void platform_host_timestamp(struct comp_dev *host,
|
||||
struct sof_ipc_stream_posn *posn)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* get host position */
|
||||
err = comp_position(host, posn);
|
||||
if (err == 0)
|
||||
posn->flags |= SOF_TIME_HOST_VALID | SOF_TIME_HOST_64;
|
||||
}
|
||||
|
||||
/* get timestamp for DAI stream DMA position */
|
||||
void platform_dai_timestamp(struct comp_dev *dai,
|
||||
struct sof_ipc_stream_posn *posn)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* get DAI position */
|
||||
err = comp_position(dai, posn);
|
||||
if (err == 0)
|
||||
posn->flags |= SOF_TIME_DAI_VALID;
|
||||
|
||||
posn->wallclock = timer_get_system(timer_get()) - posn->wallclock;
|
||||
posn->flags |= SOF_TIME_WALL_VALID | SOF_TIME_WALL_64;
|
||||
}
|
||||
|
||||
/* get current wallclock for componnent */
|
||||
void platform_dai_wallclock(struct comp_dev *dai, uint64_t *wallclock)
|
||||
{
|
||||
*wallclock = timer_get_system(timer_get());
|
||||
}
|
||||
|
||||
int timer_register(struct timer *timer, void(*handler)(void *arg), void *arg)
|
||||
{
|
||||
switch (timer->id) {
|
||||
case TIMER0:
|
||||
case TIMER1:
|
||||
return arch_timer_register(timer, handler, arg);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
void timer_unregister(struct timer *timer, void *arg)
|
||||
{
|
||||
interrupt_unregister(timer->irq, arg);
|
||||
}
|
||||
|
||||
void timer_enable(struct timer *timer, void *arg, int core)
|
||||
{
|
||||
interrupt_enable(timer->irq, arg);
|
||||
}
|
||||
|
||||
void timer_disable(struct timer *timer, void *arg, int core)
|
||||
{
|
||||
interrupt_disable(timer->irq, arg);
|
||||
}
|
Loading…
Reference in New Issue