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:
parent
e35de00f86
commit
fe5c11db05
13 changed files with 235 additions and 6 deletions
5
boards/mediatek/mt8196/Kconfig.mt8196
Normal file
5
boards/mediatek/mt8196/Kconfig.mt8196
Normal file
|
@ -0,0 +1,5 @@
|
|||
# Copyright 2024 The ChromiumOS Authors
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config BOARD_MT8196
|
||||
select SOC_MT8196
|
5
boards/mediatek/mt8196/board.yml
Normal file
5
boards/mediatek/mt8196/board.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
boards:
|
||||
- name: mt8196
|
||||
vendor: mediatek
|
||||
socs:
|
||||
- name: mt8196
|
109
boards/mediatek/mt8196/mt8196_adsp.dts
Normal file
109
boards/mediatek/mt8196/mt8196_adsp.dts
Normal 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 { };
|
||||
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
18
soc/mediatek/mt8xxx/mt8196/Kconfig.defconfig
Normal file
18
soc/mediatek/mt8xxx/mt8196/Kconfig.defconfig
Normal 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
|
5
soc/mediatek/mt8xxx/mt8196/linker.ld
Normal file
5
soc/mediatek/mt8xxx/mt8196/linker.ld
Normal file
|
@ -0,0 +1,5 @@
|
|||
/* Copyright 2024 The ChromiumOS Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "../linker.ld"
|
10
soc/mediatek/mt8xxx/mt8196/soc.h
Normal file
10
soc/mediatek/mt8xxx/mt8196/soc.h
Normal 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 */
|
|
@ -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()
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -14,3 +14,8 @@ family:
|
|||
- name: mt8188
|
||||
cpuclusters:
|
||||
- name: adsp
|
||||
- name: mt8196
|
||||
socs:
|
||||
- name: mt8196
|
||||
cpuclusters:
|
||||
- name: adsp
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue