boards/mediatek: Add mt8196_adsp

Add Zephyr support for the Audio DSP on the MT8196 SOC.  This is a
very similar device to previous designs.  Most of this patch is just
DTS.

The biggest delta is the more complicated second level interrupt
controller, though it is still able to be represented using some
vaguely clever DTS config over the older intc_mtk_adsp driver.

Also the memory layout is slightly different, requiring a little
indirection to set the initial boot stack address and log output
buffer.  And the timer "irq_ack" register bits moved.

Signed-off-by: Andy Ross <andyross@google.com>
This commit is contained in:
Andy Ross 2024-10-30 11:06:05 -07:00 committed by Benjamin Cabé
commit fe5c11db05
13 changed files with 235 additions and 6 deletions

View file

@ -0,0 +1,5 @@
# Copyright 2024 The ChromiumOS Authors
# SPDX-License-Identifier: Apache-2.0
config BOARD_MT8196
select SOC_MT8196

View file

@ -0,0 +1,5 @@
boards:
- name: mt8196
vendor: mediatek
socs:
- name: mt8196

View file

@ -0,0 +1,109 @@
/* Copyright 2024 The ChromiumOS Authors
* SPDX-License-Identifier: Apache-2.0
*/
#include <mem.h>
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
sram0: memory@4e100000 {
device_type = "memory";
compatible = "mmio-sram";
reg = <0x4e100000 DT_SIZE_K(512)>;
};
dram0: memory@90000000 {
device_type = "memory";
compatible = "mmio-sram";
reg = <0x90000000 DT_SIZE_M(6)>;
};
dram1: memory@90700000 {
device_type = "memory";
compatible = "mmio-sram";
reg = <0x90700000 DT_SIZE_M(1)>;
};
soc {
#address-cells = <1>;
#size-cells = <1>;
core_intc: core_intc@0 {
compatible = "cdns,xtensa-core-intc";
reg = <0 4>;
interrupt-controller;
#interrupt-cells = <3>;
};
/* The 8196 interrupt controller is actually more complicated
* than the driver here supports. There are 64 total
* interrupt inputs, each of which is a associated with one of
* 16 "groups", each of which is wired to a separate Xtensa
* architectural interrupt. (Whether the mapping of external
* interrupts to groups is mutable is an open question, the
* values here appear to be hardware defaults). We represent
* each group (strictly each of the high and low 32 interrupts
* of each group) as a separate adsp_intc controller, pointing
* at the same status and enable registers, but with disjoint
* masks. Note that this disallows configurations where a
* single controller needs to manage interrupts in both the
* high and low 32 bits of the set, but no current drivers
* rely on such a configuration.
*/
intc_g1: intc_g1@1a014010 {
compatible = "mediatek,adsp_intc";
interrupt-controller;
#interrupt-cells = <3>;
reg = <0x1a014010 4>;
status-reg = <0x1a014008>;
mask = <0x00007f3f>;
interrupts = <1 0 0>;
interrupt-parent = <&core_intc>;
};
intc_g2: intc_g2@1a014010 {
compatible = "mediatek,adsp_intc";
interrupt-controller;
#interrupt-cells = <3>;
reg = <0x1a014010 4>;
status-reg = <0x1a014008>;
mask = <0x000000c0>;
interrupts = <2 0 0>;
interrupt-parent = <&core_intc>;
};
ostimer64: ostimer64@1a00b080 {
compatible = "mediatek,ostimer64";
reg = <0x1a00b080 28>;
};
ostimer0: ostimer@1a00b000 {
compatible = "mediatek,ostimer";
reg = <0x1a00b000 16>;
interrupt-parent = <&intc_g1>;
interrupts = <8 0 0>;
};
mbox0: mbox@1a360100 {
compatible = "mediatek,mbox";
reg = <0x1a360100 16>;
interrupt-parent = <&intc_g2>;
interrupts = <6 0 0>;
};
mbox1: mbox@1a370100 {
compatible = "mediatek,mbox";
reg = <0x1a370100 16>;
interrupt-parent = <&intc_g2>;
interrupts = <7 0 0>;
};
}; /* soc */
chosen { };
aliases { };
};

View file

@ -14,3 +14,5 @@ variants:
name: MediaTek MT8188 Audio DSP
mt8186/mt8186/adsp:
name: MediaTek MT8186 Audio DSP
mt8196/mt8196/adsp:
name: MediaTek MT8196 Audio DSP

View file

@ -56,8 +56,13 @@ struct mtk_ostimer64 {
#define OSTIMER_CON_CLKSRC_BCLK 0x20 /* CPU speed, 720 MHz */
#define OSTIMER_CON_CLKSRC_PCLK 0x30 /* ~312 MHz experimentally */
#ifndef CONFIG_SOC_MT8196
#define OSTIMER_IRQ_ACK_ENABLE BIT(4) /* read = status, write = enable */
#define OSTIMER_IRQ_ACK_CLEAR BIT(5)
#else
#define OSTIMER_IRQ_ACK_ENABLE BIT(0)
#define OSTIMER_IRQ_ACK_CLEAR BIT(5)
#endif
#define OST64_HZ 13000000U
#define OST_HZ 26000000U

View file

@ -16,6 +16,12 @@ config SOC_SERIES_MT818X
help
Mediatek MT818x Audio DSPs
config SOC_SERIES_MT8196
bool
select SOC_FAMILY_MTK
help
Mediatek MT8196 Audio DSPs
config SOC_MT8195
bool
select SOC_SERIES_MT8195
@ -28,7 +34,12 @@ config SOC_MT8188
bool
select SOC_SERIES_MT818X
config SOC_MT8196
bool
select SOC_SERIES_MT8196
config SOC
default "mt8195" if SOC_MT8195
default "mt8186" if SOC_MT8186
default "mt8188" if SOC_MT8188
default "mt8196" if SOC_MT8196

View file

@ -25,6 +25,16 @@ static const struct device *irq_dev(unsigned int *irq_inout)
__ASSERT_NO_MSG((*irq_inout & 0xff) == 23);
*irq_inout = (*irq_inout >> 8) - 1;
return DEVICE_DT_GET(DT_INST(1, mediatek_adsp_intc));
#elif defined(CONFIG_SOC_SERIES_MT8196)
/* Two subcontrollers on core IRQs 1 and 2 */
uint32_t lvl1 = *irq_inout & 0xff;
*irq_inout = (*irq_inout >> 8) - 1;
if (lvl1 == 1) {
return DEVICE_DT_GET(DT_INST(0, mediatek_adsp_intc));
}
__ASSERT_NO_MSG(lvl1 == 2);
return DEVICE_DT_GET(DT_INST(1, mediatek_adsp_intc));
#else
/* Only one on 818x */
return DEVICE_DT_GET(DT_INST(0, mediatek_adsp_intc));

View file

@ -0,0 +1,18 @@
# Copyright 2024 The ChromiumOS Authors
# SPDX-License-Identifier: Apache-2.0
if SOC_MT8196
config LEGACY_MULTI_LEVEL_TABLE_GENERATION
default n
config NUM_2ND_LEVEL_AGGREGATORS
default 2
config 2ND_LVL_INTR_00_OFFSET
default 1
config 2ND_LVL_INTR_01_OFFSET
default 2
endif

View file

@ -0,0 +1,5 @@
/* Copyright 2024 The ChromiumOS Authors
* SPDX-License-Identifier: Apache-2.0
*/
#include "../linker.ld"

View file

@ -0,0 +1,10 @@
/* Copyright 2024 The ChromiumOS Authors
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_SOC_MT8196_SOC_H
#define ZEPHYR_SOC_MT8196_SOC_H
#include "../soc.h"
#endif /* ZEPHYR_SOC_MT8196_SOC_H */

View file

@ -129,6 +129,39 @@ class MT818x():
self.cfg.SW_RSTN &= 0xffffffee # Release reset
self.cfg.IO_CONFIG &= 0x7fffffff # Clear RUNSTALL
class MT8196():
def __init__(self, maps):
cfg_base = ctypes.addressof(ctypes.c_int.from_buffer(maps["cfg"]))
sec_base = ctypes.addressof(ctypes.c_int.from_buffer(maps["sec"]))
self.cfg = Regs(cfg_base)
self.cfg.CFGREG_SW_RSTN = 0x0000
self.cfg.MBOX_IRQ_EN = 0x009c
self.cfg.HIFI_RUNSTALL = 0x0108
self.cfg.freeze()
self.sec = Regs(sec_base)
self.sec.ALTVEC_C0 = 0x04
self.sec.ALTVECSEL = 0x0c
self.sec.freeze()
def logrange(self):
return range(0x580000, 0x600000)
def stop(self):
self.cfg.HIFI_RUNSTALL |= 0x1000
self.cfg.CFGREG_SW_RSTN |= 0x11
def start(self, boot_vector):
self.sec.ALTVEC_C0 = 0
self.sec.ALTVECSEL = 0
self.sec.ALTVEC_C0 = boot_vector
self.sec.ALTVECSEL = 1
self.cfg.HIFI_RUNSTALL |= 0x1000
self.cfg.MBOX_IRQ_EN |= 3
self.cfg.CFGREG_SW_RSTN |= 0x11
time.sleep(0.1)
self.cfg.CFGREG_SW_RSTN &= ~0x11
self.cfg.HIFI_RUNSTALL &= ~0x1000
# Temporary logging protocol: watch the 1M null-terminated log
# stream at 0x60700000 -- the top of the linkable region of
# existing SOF firmware, before the heap. Nothing uses this
@ -193,6 +226,8 @@ def main():
dev = MT8195(maps)
elif dsp in ("mt8186", "mt8188"):
dev = MT818x(maps)
elif dsp == "mt8196":
dev = MT8196(maps)
if sys.argv[1] == "load":
dat = open(sys.argv[2], "rb").read()

View file

@ -19,14 +19,23 @@ extern char _mtk_adsp_dram_end[];
#define DRAM_SIZE DT_REG_SIZE(DT_NODELABEL(dram0))
#define DRAM_END (DRAM_START + DRAM_SIZE)
#ifdef CONFIG_SOC_MT8196
#define INIT_STACK "0x90400000"
#define LOG_BASE 0x90580000
#define LOG_LEN 0x80000
#else
#define INIT_STACK "0x60e00000"
#define LOG_BASE 0x60700000
#define LOG_LEN 0x100000
#endif
/* This is the true boot vector. This device allows for direct
* setting of the alternate reset vector, so we let it link wherever
* it lands and extract its address in the loader. This represents
* the minimum amount of effort required to successfully call a C
* function (and duplicates a few versions elsewhere in the tree:
* really this should move to the arch layer). Note that the stack
* used is 15MB into the DRAM region and safe/unused by all devices.
* But really this should be z_interrupt_stacks[0].
* really this should move to the arch layer). The initial stack
* really should be the end of _interrupt_stacks[0]
*/
__asm__(".align 4\n\t"
".global mtk_adsp_boot_entry\n\t"
@ -38,7 +47,7 @@ __asm__(".align 4\n\t"
" movi a0, 1\n\t"
" wsr a0, WINDOWSTART\n\t"
" rsync\n\t"
" movi a1, 0x60e00000\n\t"
" movi a1, " INIT_STACK "\n\t"
" call4 c_boot\n\t");
/* Initial MPU configuration, needed to enable caching */
@ -109,8 +118,8 @@ static void enable_mpu(void)
*/
int arch_printk_char_out(int c)
{
char volatile * const buf = (void *)0x60700000;
const size_t max = 0x100000 - 4;
char volatile * const buf = (void *)LOG_BASE;
const size_t max = LOG_LEN - 4;
int volatile * const len = (int *)&buf[max];
if (*len < max) {

View file

@ -14,3 +14,8 @@ family:
- name: mt8188
cpuclusters:
- name: adsp
- name: mt8196
socs:
- name: mt8196
cpuclusters:
- name: adsp