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:
Allen-KH Cheng 2021-08-27 14:21:40 +08:00 committed by Liam Girdwood
parent ef37ebaabf
commit 0cad7a04fb
3 changed files with 526 additions and 0 deletions

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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);
}