soc: boards: Add Mediatek MT8186 and MT8188 audio DSPs

These are very similar devices to mt8195, minimal changes needed
beyond boilerplate configuration.

In the process, this reworks the board/soc layout to be HWMv2
compliant, with "adsp" becoming a CPU cluster beneath the SOC.  So the
name of the boards to west become e.g. "mt8195/mt8195/adsp" (which can
be shortened to "mt8195//adsp" if desired).

Note that the cpuclk driver is not yet ported, it works only with 8195
(the clocking/power architecture seems similar between the parts, but
the graph of wells and clocks is different and historically these have
been three separate drivers in SOF).  The biggest changes are in the
image/loader scripts, which needed some rework for cross-device
portability.

Signed-off-by: Andy Ross <andyross@google.com>
This commit is contained in:
Andy Ross 2024-05-22 16:12:40 -07:00 committed by Benjamin Cabé
commit 5364783ba1
39 changed files with 709 additions and 322 deletions

View file

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

View file

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

View file

@ -0,0 +1,80 @@
/* 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(1024)>;
};
dram0: memory@60000000 {
device_type = "memory";
compatible = "mmio-sram";
reg = <0x60000000 DT_SIZE_M(16)>;
};
dram1: memory@61000000 {
device_type = "memory";
compatible = "mmio-sram";
reg = <0x61000000 DT_SIZE_K(1024)>;
};
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>;
};
intc2: intc@10680010 {
compatible = "mediatek,adsp_intc";
interrupt-controller;
#interrupt-cells = <3>;
reg = <0x10680010 4>;
status-reg = <0x10680050>;
interrupts = <2 0 0>;
mask = <0x3f>;
interrupt-parent = <&core_intc>;
};
ostimer64: ostimer64@10683080 {
compatible = "mediatek,ostimer64";
reg = <0x10683080 28>;
};
ostimer0: ostimer@10683000 {
compatible = "mediatek,ostimer";
reg = <0x10683000 16>;
interrupt-parent = <&core_intc>;
interrupts = <18 0 0>;
};
mbox0: mbox@10686100 {
compatible = "mediatek,mbox";
reg = <0x10686100 16>;
interrupt-parent = <&intc2>;
interrupts = <1 0 0>;
};
mbox1: mbox@10687100 {
compatible = "mediatek,mbox";
reg = <0x10687100 16>;
interrupt-parent = <&intc2>;
interrupts = <2 0 0>;
};
}; /* soc */
chosen { };
aliases { };
};

View file

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

View file

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

View file

@ -0,0 +1,81 @@
/* 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@60000000 {
device_type = "memory";
compatible = "mmio-sram";
reg = <0x60000000 DT_SIZE_M(15)>;
};
dram1: memory@61000000 {
device_type = "memory";
compatible = "mmio-sram";
reg = <0x61000000 DT_SIZE_K(1024)>;
};
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>;
};
intc2: intc@10b80010 {
compatible = "mediatek,adsp_intc";
interrupt-controller;
#interrupt-cells = <3>;
reg = <0x10b80010 4>;
status-reg = <0x10b80050>;
interrupts = <2 0 0>;
mask = <0x3f>;
interrupt-parent = <&core_intc>;
};
ostimer64: ostimer64@10b83080 {
compatible = "mediatek,ostimer64";
reg = <0x10b83080 28>;
};
ostimer0: ostimer@10b83000 {
compatible = "mediatek,ostimer";
reg = <0x10b83000 16>;
interrupt-parent = <&core_intc>;
interrupts = <18 0 0>;
};
mbox0: mbox@10b86100 {
compatible = "mediatek,mbox";
reg = <0x10b86100 16>;
interrupt-parent = <&intc2>;
interrupts = <1 0 0>;
};
mbox1: mbox@10b87100 {
compatible = "mediatek,mbox";
reg = <0x10b87100 16>;
interrupt-parent = <&intc2>;
interrupts = <2 0 0>;
};
}; /* soc */
chosen { };
aliases { };
};

View file

@ -1,7 +1,7 @@
# Copyright 2023 The ChromiumOS Authors # Copyright 2023 The ChromiumOS Authors
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
config BOARD_MT8195_ADSP config BOARD_MT8195
select SOC_MT8195_ADSP select SOC_MT8195
help help
Board with Mediatek MT8195 Audio DSP Board with Mediatek MT8195 Audio DSP

View file

@ -1,6 +1,6 @@
boards: boards:
- name: mt8195_adsp - name: mt8195
full_name: MT8195 ADSP full_name: MT8195 ADSP
vendor: mediatek vendor: mediatek
socs: socs:
- name: mt8195_adsp - name: mt8195

View file

@ -0,0 +1,94 @@
/* Copyright 2023 The ChromiumOS Authors
* SPDX-License-Identifier: Apache-2.0
*/
#include <mem.h>
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
sram0: memory@40000000 {
device_type = "memory";
compatible = "mmio-sram";
reg = <0x40000000 DT_SIZE_K(256)>;
};
dram0: memory@60000000 {
device_type = "memory";
compatible = "mmio-sram";
reg = <0x60000000 DT_SIZE_M(17)>;
};
soc {
#address-cells = <1>;
#size-cells = <1>;
cpuclk: cpuclk@10000000 {
compatible = "mediatek,mt8195_cpuclk";
reg = <0x10000000 380>;
cg_reg = <0x10720180>;
pll_ctrl_reg = <0x1000c7e0>;
freqs_mhz = <26 370 540 720>;
};
core_intc: core_intc@0 {
compatible = "cdns,xtensa-core-intc";
reg = <0 4>;
interrupt-controller;
#interrupt-cells = <3>;
};
intc1: intc@10680130 {
compatible = "mediatek,adsp_intc";
interrupt-controller;
#interrupt-cells = <3>;
reg = <0x10680130 4>;
status-reg = <0x10680150>;
interrupts = <1 0 0>;
mask = <0x3ffffff0>;
interrupt-parent = <&core_intc>;
};
intc23: intc@108030f4 {
compatible = "mediatek,adsp_intc";
interrupt-controller;
#interrupt-cells = <3>;
reg = <0x108030f4 4>;
status-reg = <0x108030fc>;
interrupts = <23 0 0>;
mask = <0xffff>;
interrupt-parent = <&core_intc>;
};
ostimer64: ostimer64@1080d080 {
compatible = "mediatek,ostimer64";
reg = <0x1080d080 28>;
};
ostimer0: ostimer@1080d000 {
compatible = "mediatek,ostimer";
reg = <0x1080d000 16>;
interrupt-parent = <&intc23>;
interrupts = <11 0 0>;
};
mbox0: mbox@10816000 {
compatible = "mediatek,mbox";
reg = <0x10816000 56>;
interrupt-parent = <&intc23>;
interrupts = <0 0 0>;
};
mbox1: mbox@10817000 {
compatible = "mediatek,mbox";
reg = <0x10817000 56>;
interrupt-parent = <&intc23>;
interrupts = <1 0 0>;
};
}; /* soc */
chosen { };
aliases { };
};

View file

@ -1,9 +0,0 @@
# Copyright 2023 The ChromiumOS Authors
# SPDX-License-Identifier: Apache-2.0
if BOARD_MT8195_ADSP
config BOARD
default "mt8195_adsp"
endif # BOARD_MT8195_ADSP

View file

@ -1,95 +0,0 @@
/* Copyright 2023 The ChromiumOS Authors
* SPDX-License-Identifier: Apache-2.0
*/
#include <mem.h>
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
sram0: memory@40000000 {
device_type = "memory";
compatible = "mmio-sram";
reg = <0x40000000 DT_SIZE_K(256)>;
};
dram0: memory@60000000 {
device_type = "memory";
compatible = "mmio-sram";
reg = <0x60000000 DT_SIZE_M(17)>;
};
soc {
#address-cells = <1>;
#size-cells = <1>;
cpuclk: cpuclk@10000000 {
compatible = "mediatek,mt8195_cpuclk";
reg = <0x10000000 380>;
cg_reg = <0x10720180>;
pll_ctrl_reg = <0x1000c7e0>;
freqs_mhz = <26 370 540 720>;
};
core_intc: core_intc@0 {
compatible = "cdns,xtensa-core-intc";
reg = <0 4>;
interrupt-controller;
#interrupt-cells = <3>;
};
intc1: intc@10680130 {
compatible = "mediatek,adsp_intc";
interrupt-controller;
#interrupt-cells = <3>;
reg = <0x10680130 4>;
status-reg = <0x10680150>;
interrupts = <1 0 0>;
mask = <0x3ffffff0>;
interrupt-parent = <&core_intc>;
};
intc23: intc@108030f4 {
compatible = "mediatek,adsp_intc";
interrupt-controller;
#interrupt-cells = <3>;
reg = <0x108030f4 4>;
status-reg = <0x108030fc>;
interrupts = <23 0 0>;
mask = <0xffff>;
interrupt-parent = <&core_intc>;
};
ostimer64: ostimer64@1080d080 {
compatible = "mediatek,ostimer64";
reg = <0x1080d080 28>;
};
ostimer0: ostimer@1080d000 {
compatible = "mediatek,ostimer";
reg = <0x1080d000 16>;
interrupt-parent = <&intc23>;
interrupts = <11 0 0>;
};
mbox0: mbox@10816000 {
compatible = "mediatek,mbox";
reg = <0x10816000 56>;
interrupt-parent = <&intc23>;
interrupts = <0 0 0>;
};
mbox1: mbox@10817000 {
compatible = "mediatek,mbox";
reg = <0x10817000 56>;
interrupt-parent = <&intc23>;
interrupts = <1 0 0>;
};
}; /* soc */
chosen { };
aliases { };
};

View file

@ -1,4 +0,0 @@
# Copyright 2023 The ChromiumOS Authors
# SPDX-License-Identifier: Apache-2.0
CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=13000000

View file

@ -1,7 +1,9 @@
# Copyright 2023 The ChromiumOS Authors # Copyright 2023 The ChromiumOS Authors
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
zephyr_library_sources(soc.c irq.c cpuclk.c mbox.c) zephyr_library_sources(soc.c irq.c mbox.c)
zephyr_library_sources_ifdef(CONFIG_SOC_SERIES_MT8195 cpuclk.c)
set(SOC_LINKER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/${CONFIG_SOC}/linker.ld CACHE INTERNAL "") set(SOC_LINKER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/${CONFIG_SOC}/linker.ld CACHE INTERNAL "")

View file

@ -1,6 +1,6 @@
# Copyright 2024 The ChromiumOS Authors # Copyright 2024 The ChromiumOS Authors
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
config SOC_FAMILY_MTK_ADSP config SOC_FAMILY_MTK
select XTENSA select XTENSA
select XTENSA_GEN_HANDLERS select XTENSA_GEN_HANDLERS

View file

@ -3,7 +3,7 @@
orsource "*/Kconfig.defconfig" orsource "*/Kconfig.defconfig"
if SOC_FAMILY_MTK_ADSP if SOC_FAMILY_MTK
config INTC_MTK_ADSP config INTC_MTK_ADSP
default y default y
@ -23,11 +23,22 @@ config MAX_IRQ_PER_AGGREGATOR
config 2ND_LVL_ISR_TBL_OFFSET config 2ND_LVL_ISR_TBL_OFFSET
default 32 default 32
# The 8186/8188 core has only one software interrupt that lives at
# level 2, underneath other hardware interrupts like timer, so it
# can't reliably do this. Unselect so the tests don't try to exercise
# it.
config IRQ_OFFLOAD_NESTED
default n if SOC_SERIES_MT818X
default y
config MTK_ADSP_TIMER config MTK_ADSP_TIMER
default y default y
config XTENSA_TIMER config XTENSA_TIMER
default n default n
config SYS_CLOCK_HW_CYCLES_PER_SEC
default 13000000
config MAIN_STACK_SIZE config MAIN_STACK_SIZE
default 2048 default 2048
@ -48,7 +59,8 @@ config XTENSA_HAL
default y default y
config SOC_TOOLCHAIN_NAME config SOC_TOOLCHAIN_NAME
default "mtk_mt8195_adsp" default "mtk_mt8195_adsp" if SOC_SERIES_MT8195
default "mtk_mt818x_adsp" if SOC_SERIES_MT818X
config XTENSA_RESET_VECTOR config XTENSA_RESET_VECTOR
default n default n
@ -69,4 +81,4 @@ config GEN_SW_ISR_TABLE
config GEN_IRQ_VECTOR_TABLE config GEN_IRQ_VECTOR_TABLE
default n default n
endif # SOC_FAMILY_MTK_ADSP endif # SOC_FAMILY_MTK

View file

@ -0,0 +1,34 @@
# Copyright 2024 The ChromiumOS Authors
# SPDX-License-Identifier: Apache-2.0
config SOC_FAMILY_MTK
bool
config SOC_SERIES_MT8195
bool
select SOC_FAMILY_MTK
help
Mediatek MT8195 Audio DSP
config SOC_SERIES_MT818X
bool
select SOC_FAMILY_MTK
help
Mediatek MT818x Audio DSPs
config SOC_MT8195
bool
select SOC_SERIES_MT8195
config SOC_MT8186
bool
select SOC_SERIES_MT818X
config SOC_MT8188
bool
select SOC_SERIES_MT818X
config SOC
default "mt8195" if SOC_MT8195
default "mt8186" if SOC_MT8186
default "mt8188" if SOC_MT8188

View file

@ -21,8 +21,6 @@ import elftools.elf.sections
FILE_MAGIC = 0xe463be95 FILE_MAGIC = 0xe463be95
SRAM_START = 0x40000000
SRAM_END = 0x40040000
DRAM_START = 0x60000000 DRAM_START = 0x60000000
DRAM_END = 0x61100000 DRAM_END = 0x61100000
@ -34,12 +32,31 @@ ef = elftools.elf.elffile.ELFFile(open(elf_file, "rb"))
sram = bytearray() sram = bytearray()
dram = bytearray() dram = bytearray()
# Returns the offset of a segment within the sram region, or -1 if it
# doesn't appear to be SRAM. SRAM is mapped differently for different
# SOCs, but it's always a <=1M region in 0x4xxxxxxx. Just use what we
# get, but validate that it fits.
sram_block = 0
def sram_off(addr):
global sram_block
if addr < 0x40000000 or addr >= 0x50000000:
return -1
block = addr & ~0xfffff
assert sram_block in (0, block)
sram_block = block
off = addr - sram_block
assert off < 0x100000
return off
for seg in ef.iter_segments(): for seg in ef.iter_segments():
h = seg.header h = seg.header
if h.p_type == "PT_LOAD": if h.p_type == "PT_LOAD":
if h.p_paddr in range(SRAM_START, SRAM_END): soff = sram_off(h.p_paddr)
if soff >= 0:
buf = sram buf = sram
off = h.p_paddr - SRAM_START off = soff
elif h.p_paddr in range(DRAM_START, DRAM_END): elif h.p_paddr in range(DRAM_START, DRAM_END):
buf = dram buf = dram
off = h.p_paddr - DRAM_START off = h.p_paddr - DRAM_START
@ -52,6 +69,7 @@ for seg in ef.iter_segments():
if end > len(buf): if end > len(buf):
buf.extend(b'\x00' * (end - len(buf))) buf.extend(b'\x00' * (end - len(buf)))
# pylint: disable=consider-using-enumerate
for i in range(len(dat)): for i in range(len(dat)):
buf[i + off] = dat[i] buf[i + off] = dat[i]
@ -61,9 +79,8 @@ for sec in ef.iter_sections():
if sym.name == "mtk_adsp_boot_entry": if sym.name == "mtk_adsp_boot_entry":
boot_vector = sym.entry['st_value'] boot_vector = sym.entry['st_value']
assert len(sram) < SRAM_END - SRAM_START
assert len(dram) < DRAM_END - DRAM_START assert len(dram) < DRAM_END - DRAM_START
assert (SRAM_START <= boot_vector < SRAM_END) or (DRAM_START <= boot_vector < DRAM_END) assert (sram_off(boot_vector) >= 0) or (DRAM_START <= boot_vector < DRAM_END)
of = open(out_file, "wb") of = open(out_file, "wb")
of.write(struct.pack("<III", FILE_MAGIC, len(sram), boot_vector)) of.write(struct.pack("<III", FILE_MAGIC, len(sram), boot_vector))

View file

@ -16,6 +16,7 @@ void intc_mtk_adsp_set_enable(const struct device *dev, int irq, bool val);
*/ */
static const struct device *irq_dev(unsigned int *irq_inout) static const struct device *irq_dev(unsigned int *irq_inout)
{ {
#ifdef CONFIG_SOC_SERIES_MT8195
/* Controller 0 is on Xtensa vector 1, controller 1 on vector 23. */ /* Controller 0 is on Xtensa vector 1, controller 1 on vector 23. */
if ((*irq_inout & 0xff) == 1) { if ((*irq_inout & 0xff) == 1) {
*irq_inout >>= 8; *irq_inout >>= 8;
@ -24,6 +25,10 @@ static const struct device *irq_dev(unsigned int *irq_inout)
__ASSERT_NO_MSG((*irq_inout & 0xff) == 23); __ASSERT_NO_MSG((*irq_inout & 0xff) == 23);
*irq_inout = (*irq_inout >> 8) - 1; *irq_inout = (*irq_inout >> 8) - 1;
return DEVICE_DT_GET(DT_INST(1, mediatek_adsp_intc)); 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));
#endif
} }
void z_soc_irq_enable(unsigned int irq) void z_soc_irq_enable(unsigned int irq)

View file

@ -1,4 +1,4 @@
/* Copyright 2023 The ChromiumOS Authors /* Copyright 2024 The ChromiumOS Authors
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@ -139,4 +139,5 @@ SECTIONS {
#ifdef CONFIG_LLEXT #ifdef CONFIG_LLEXT
#include <zephyr/linker/llext-sections.ld> #include <zephyr/linker/llext-sections.ld>
#endif #endif
} /* SECTIONS */ } /* SECTIONS */

View file

@ -28,20 +28,31 @@
* *
* In practice: The first device (mbox0) is for IPC commands in both * In practice: The first device (mbox0) is for IPC commands in both
* directions. The cmd register is written with a 1 ("IPI_OP_REQ") * directions. The cmd register is written with a 1 ("IPI_OP_REQ")
* and the command is placed in shared DRAM. The message registers * and the command is placed in shared DRAM.
* are ignored. The second device (mbox1) is for responses to IPC *
* commands, writing a 2 (IPI_OP_RSP) to the command register. (Yes, * Note that the 8195 has ten "msg" registers. These only exist on
* this is redundant, and the actual value is ignored by the ISRs on * this version of the hardware, and act as uninspected r/w scratch
* both sides). * registers to both the host and DSP, with no other behavior. They
* aren't used by the Linux kernel at all (which has a single driver
* for all these DSPs), so are described here for completeness but
* otherwise ignored. In practice they don't do anything that simple
* shared memory can't.
*/ */
struct mtk_mbox { struct mtk_mbox {
#ifdef SOC_SERIES_MT8195
uint32_t in_cmd; uint32_t in_cmd;
uint32_t in_cmd_clr; uint32_t in_cmd_clr;
uint32_t in_msg[5]; uint32_t in_msg[5];
uint32_t out_cmd; uint32_t out_cmd;
uint32_t out_cmd_clr; uint32_t out_cmd_clr;
uint32_t out_msg[5]; uint32_t out_msg[5];
#else
uint32_t in_cmd;
uint32_t out_cmd;
uint32_t in_cmd_clr;
uint32_t out_cmd_clr;
#endif
}; };
struct mbox_cfg { struct mbox_cfg {

View file

@ -0,0 +1,13 @@
# Copyright 2024 The ChromiumOS Authors
# SPDX-License-Identifier: Apache-2.0
if SOC_MT8186
# FIXME: move this to 818x series code to share w/8188
config NUM_2ND_LEVEL_AGGREGATORS
default 1
config 2ND_LVL_INTR_00_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_MT8186_SOC_H
#define ZEPHYR_SOC_MT8186_SOC_H
#include "../soc.h"
#endif /* ZEPHYR_SOC_MT8186_SOC_H */

View file

@ -0,0 +1,12 @@
# Copyright 2024 The ChromiumOS Authors
# SPDX-License-Identifier: Apache-2.0
if SOC_MT8188
config NUM_2ND_LEVEL_AGGREGATORS
default 1
config 2ND_LVL_INTR_00_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_MT8188_SOC_H
#define ZEPHYR_SOC_MT8188_SOC_H
#include "../soc.h"
#endif /* ZEPHYR_SOC_MT8188_SOC_H */

View file

@ -1,7 +1,7 @@
# Copyright 2023 The ChromiumOS Authors # Copyright 2023 The ChromiumOS Authors
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
if SOC_SERIES_MT8195_ADSP if SOC_SERIES_MT8195
config NUM_2ND_LEVEL_AGGREGATORS config NUM_2ND_LEVEL_AGGREGATORS
default 2 default 2
@ -10,4 +10,4 @@ config 2ND_LVL_INTR_00_OFFSET
config 2ND_LVL_INTR_01_OFFSET config 2ND_LVL_INTR_01_OFFSET
default 23 default 23
endif # SOC_SERIES_MT8195_ADSP endif # SOC_SERIES_MT8195

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 2023 The ChromiumOS Authors
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_SOC_MT8195_SOC_H
#define ZEPHYR_SOC_MT8195_SOC_H
#include "../soc.h"
#endif /* ZEPHYR_SOC_MT8195_SOC_H */

View file

@ -0,0 +1,227 @@
#!/usr/bin/env python
# Copyright 2023 The ChromiumOS Authors
# SPDX-License-Identifier: Apache-2.0
import ctypes
import sys
import mmap
import time
import struct
from glob import glob
import re
# MT8195 audio firmware load/debug gadget
# Note that the hardware handling here is only partial: in practice
# the audio DSP depends on clock and power well devices whose drivers
# live elsewhere in the kernel. Those aren't duplicated here. Make
# sure the DSP has been started by a working kernel driver first.
#
# See gen_img.py for docs on the image format itself. The way this
# script works is to map the device memory regions and registers via
# /dev/mem and copy the two segments while resetting the DSP.
#
# In the kernel driver, the address/size values come from devicetree.
# But currently the MediaTek architecture is one kernel driver per SOC
# (i.e. the devicetree values in the kenrel source are tied to the
# specific SOC anyway), so it really doesn't matter and we hard-code
# the addresses for simplicity.
#
# (For future reference: in /proc/device-tree on current ChromeOS
# kernels, the host registers are a "cfg" platform resource on the
# "adsp@10803000" node. The sram is likewise the "sram" resource on
# that device node, and the two dram areas are "memory-region"
# phandles pointing to "adsp_mem_region" and "adsp_dma_mem_region"
# nodes under "/reserved-memory").
FILE_MAGIC = 0xe463be95
# Runtime mmap objects for each MAPPINGS entry
maps = {}
# Returns a string (e.g. "mt8195", "mt8186", "mt8188") if a supported
# adsp is detected, or None if not
def detect():
compat = readfile(glob("/proc/device-tree/**/adsp@*/compatible",
recursive=True)[0], "r")
m = re.match(r'.*(mt\d{4})-dsp', compat)
if m:
return m.group(1)
# Parse devicetree to find the MMIO mappings: there is an "adsp" node
# (in various locations) with an array of named "reg" mappings. It
# also refers by reference to reserved-memory regions of system
# DRAM. Those don't have names, call them dram0/1 (dram1 is the main
# region to which code is linked, dram0 is presumably a dma pool but
# unused by current firmware). Returns a dict mapping name to a
# (addr, size) tuple.
def mappings():
path = glob("/proc/device-tree/**/adsp@*/", recursive=True)[0]
rnames = readfile(path + "reg-names", "r").split('\0')[:-1]
regs = struct.unpack(f">{2 * len(rnames)}Q", readfile(path + "reg"))
maps = { n : (regs[2*i], regs[2*i+1]) for i, n in enumerate(rnames) }
for i, ph in enumerate(struct.unpack(">II", readfile(path + "memory-region"))):
for rmem in glob("/proc/device-tree/reserved-memory/*/"):
if struct.unpack(">I", readfile(rmem + "phandle"))[0] == ph:
(addr, sz) = struct.unpack(">QQ", readfile(rmem + "reg"))
maps[f"dram{i}"] = (addr, sz)
break
return maps
# Register API for 8195
class MT8195():
def __init__(self, maps):
# Create a Regs object for the registers
r = Regs(ctypes.addressof(ctypes.c_int.from_buffer(maps["cfg"])))
r.ALTRESETVEC = 0x0004 # Xtensa boot address
r.RESET_SW = 0x0024 # Xtensa halt/reset/boot control
r.PDEBUGBUS0 = 0x000c # Unclear, enabled by host, unused by SOF?
r.SRAM_POOL_CON = 0x0930 # SRAM power control: low 4 bits (banks?) enable
r.EMI_MAP_ADDR = 0x981c # == host SRAM mapping - 0x40000000 (controls MMIO map?)
r.freeze()
self.cfg = r
def stop(self):
self.cfg.RESET_SW |= 8 # Set RUNSTALL: halt CPU
self.cfg.RESET_SW |= 3 # Set low two bits: "BRESET|DRESET"
def start(self, boot_vector):
self.stop()
self.cfg.RESET_SW |= 0x10 # Enable "alternate reset" boot vector
self.cfg.ALTRESETVEC = boot_vector
self.cfg.RESET_SW &= ~3 # Release reset bits
self.cfg.RESET_SW &= ~8 # Clear RUNSTALL: go!
# Register API for 8186/8188
class MT818x():
def __init__(self, maps):
# These have registers spread across two blocks
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.SW_RSTN = 0x00
self.cfg.IO_CONFIG = 0x0c
self.cfg.freeze()
self.sec = Regs(sec_base)
self.sec.ALTVEC_C0 = 0x04
self.sec.ALTVECSEL = 0x0c
self.sec.freeze()
def stop(self):
self.cfg.IO_CONFIG |= (1<<31) # Set RUNSTALL to stop core
time.sleep(0.1)
self.cfg.SW_RSTN |= 0x11 # Assert reset: SW_RSTN_C0|SW_DBG_RSTN_C0
# Note: 8186 and 8188 use different bits in ALTVECSEC, but
# it's safe to write both to enable the alternate boot vector
def start(self, boot_vector):
self.cfg.IO_CONFIG |= (1<<31) # Set RUNSTALL
self.sec.ALTVEC_C0 = boot_vector
self.sec.ALTVECSEL = 0x03 # Enable alternate vector
self.cfg.SW_RSTN |= 0x00000011 # Assert reset
self.cfg.SW_RSTN &= 0xffffffee # Release reset
self.cfg.IO_CONFIG &= 0x7fffffff # Clear RUNSTALL
# 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
# currently. Will be replaced by winstream very soon.
def log():
msg = b''
dram = maps["dram1"]
for i in range(0x700000, 0x800000):
x = dram[i]
if x == 0:
sys.stdout.buffer.write(msg)
sys.stdout.buffer.flush()
msg = b''
while x == 0:
time.sleep(0.1)
x = dram[i]
msg += x.to_bytes(1, "little")
sys.stdout.buffer.write(msg)
sys.stdout.buffer.flush()
# (Cribbed from cavstool.py)
class Regs:
def __init__(self, base_addr):
vars(self)["base_addr"] = base_addr
vars(self)["ptrs"] = {}
vars(self)["frozen"] = False
def freeze(self):
vars(self)["frozen"] = True
def __setattr__(self, name, val):
if not self.frozen and name not in self.ptrs:
addr = self.base_addr + val
self.ptrs[name] = ctypes.c_uint32.from_address(addr)
else:
self.ptrs[name].value = val
def __getattr__(self, name):
return self.ptrs[name].value
def readfile(f, mode="rb"):
return open(f, mode).read()
def le4(bstr):
assert len(bstr) == 4
return struct.unpack("<I", bstr)[0]
def main():
dsp = detect()
assert dsp
# Probe devicetree for mappable memory regions
mmio = mappings()
# Open device and establish mappings
devmem_fd = open("/dev/mem", "wb+")
for mp in mmio:
paddr = mmio[mp][0]
mapsz = mmio[mp][1]
mapsz = int((mapsz + 4095) / 4096) * 4096
maps[mp] = mmap.mmap(devmem_fd.fileno(), mapsz, offset=paddr,
flags=mmap.MAP_SHARED, prot=mmap.PROT_WRITE|mmap.PROT_READ)
if dsp == "mt8195":
dev = MT8195(maps)
elif dsp in ("mt8186", "mt8188"):
dev = MT818x(maps)
if sys.argv[1] == "load":
dat = open(sys.argv[2], "rb").read()
assert le4(dat[0:4])== FILE_MAGIC
sram_len = le4(dat[4:8])
boot_vector = le4(dat[8:12])
sram = dat[12:12+sram_len]
dram = dat[12 + sram_len:]
assert len(sram) <= mmio["sram"][1]
assert len(dram) <= mmio["dram1"][1]
# Stop the device and write the regions. Note that we don't
# zero-fill SRAM, as that's been observed to reboot the host
# (!!) on mt8186 when the writes near the end of the 512k
# region.
# pylint: disable=consider-using-enumerate
for i in range(sram_len):
maps["sram"][i] = sram[i]
#for i in range(sram_len, mmio["sram"][1]):
# maps["sram"][i] = 0
for i in range(len(dram)):
maps["dram1"][i] = dram[i]
for i in range(len(dram), mmio["dram1"][1]):
maps["dram1"][i] = 0
dev.start(boot_vector)
log()
elif sys.argv[1] == "log":
log()
elif sys.argv[1] == "dump":
sz = mmio[sys.argv[2]][1]
mm = maps[sys.argv[2]]
sys.stdout.buffer.write(mm[0:sz])
else:
print(f"Usage: {sys.argv[0]} log | load <file>")
if __name__ == "__main__":
main()

View file

@ -24,7 +24,9 @@ extern char _mtk_adsp_dram_end[];
* it lands and extract its address in the loader. This represents * it lands and extract its address in the loader. This represents
* the minimum amount of effort required to successfully call a C * the minimum amount of effort required to successfully call a C
* function (and duplicates a few versions elsewhere in the tree: * function (and duplicates a few versions elsewhere in the tree:
* really this should move to the arch layer). * 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].
*/ */
__asm__(".align 4\n\t" __asm__(".align 4\n\t"
".global mtk_adsp_boot_entry\n\t" ".global mtk_adsp_boot_entry\n\t"
@ -36,7 +38,7 @@ __asm__(".align 4\n\t"
" movi a0, 1\n\t" " movi a0, 1\n\t"
" wsr a0, WINDOWSTART\n\t" " wsr a0, WINDOWSTART\n\t"
" rsync\n\t" " rsync\n\t"
" movi a1, 0x40040000\n\t" " movi a1, 0x60e00000\n\t"
" call4 c_boot\n\t"); " call4 c_boot\n\t");
/* Initial MPU configuration, needed to enable caching */ /* Initial MPU configuration, needed to enable caching */
@ -148,7 +150,9 @@ void c_boot(void)
*/ */
__asm__ volatile("wsr %0, VECBASE; rsync" :: "r"(&z_xtensa_vecbase)); __asm__ volatile("wsr %0, VECBASE; rsync" :: "r"(&z_xtensa_vecbase));
#ifdef CONFIG_SOC_SERIES_MT8195
mtk_adsp_cpu_freq_init(); mtk_adsp_cpu_freq_init();
#endif
/* Likewise, memory power is external to the device, and the /* Likewise, memory power is external to the device, and the
* kernel SOF loader doesn't zero it, so zero our unlinked * kernel SOF loader doesn't zero it, so zero our unlinked

View file

@ -2,8 +2,8 @@
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
#ifndef ZEPHYR_SOC_MTK_ADSP_SOC_H #ifndef ZEPHYR_SOC_MTK_SOC_H
#define ZEPHYR_SOC_MTK_ADSP_SOC_H #define ZEPHYR_SOC_MTK_SOC_H
#include <zephyr/device.h> #include <zephyr/device.h>
@ -26,4 +26,4 @@ void mtk_adsp_mbox_set_handler(const struct device *mbox, uint32_t chan,
/* Signal an interrupt on the specified channel for the other side */ /* Signal an interrupt on the specified channel for the other side */
void mtk_adsp_mbox_signal(const struct device *mbox, uint32_t chan); void mtk_adsp_mbox_signal(const struct device *mbox, uint32_t chan);
#endif /* ZEPHYR_SOC_MTK_ADSP_SOC_H */ #endif /* ZEPHYR_SOC_MTK_SOC_H */

View file

@ -0,0 +1,16 @@
family:
- name: mt8xxx
series:
- name: mt8195
socs:
- name: mt8195
cpuclusters:
- name: adsp
- name: mt818x
socs:
- name: mt8186
cpuclusters:
- name: adsp
- name: mt8188
cpuclusters:
- name: adsp

View file

@ -1,26 +0,0 @@
# Copyright 2024 The ChromiumOS Authors
# SPDX-License-Identifier: Apache-2.0
config SOC_FAMILY_MTK_ADSP
bool
help
Mediatek MT8xxx Series Audio DSPs
config SOC_SERIES_MT8195_ADSP
bool
select SOC_FAMILY_MTK_ADSP
help
Mediatek MT8195 Audio DSP
config SOC_MT8195_ADSP
bool
select SOC_SERIES_MT8195_ADSP
config SOC_FAMILY
default "mtk_adsp" if SOC_FAMILY_MTK_ADSP
config SOC_SERIES
default "mt8195_adsp" if SOC_SERIES_MT8195_ADSP
config SOC
default "mt8195_adsp" if SOC_MT8195_ADSP

View file

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

View file

@ -1,141 +0,0 @@
#!/usr/bin/env python
# Copyright 2023 The ChromiumOS Authors
# SPDX-License-Identifier: Apache-2.0
import ctypes
import sys
import mmap
import time
import struct
# MT8195 audio firmware load/debug gadget
# Note that the hardware handling here is only partial: in practice
# the audio DSP depends on clock and power well devices whose drivers
# live elsewhere in the kernel. Those aren't duplicated here. Make
# sure the DSP has been started by a working kernel driver first.
#
# See gen_img.py for docs on the image format itself. The way this
# script works is to map the device memory regions and registers via
# /dev/mem and copy the two segments while resetting the DSP.
#
# In the kernel driver, the address/size values come from devicetree.
# But currently the MediaTek architecture is one kernel driver per SOC
# (i.e. the devicetree values in the kenrel source are tied to the
# specific SOC anyway), so it really doesn't matter and we hard-code
# the addresses for simplicity.
#
# (For future reference: in /proc/device-tree on current ChromeOS
# kernels, the host registers are a "cfg" platform resource on the
# "adsp@10803000" node. The sram is likewise the "sram" resource on
# that device node, and the two dram areas are "memory-region"
# phandles pointing to "adsp_mem_region" and "adsp_dma_mem_region"
# nodes under "/reserved-memory").
FILE_MAGIC = 0xe463be95
MAPPINGS = { "regs" : (0x10803000, 0xa000),
"sram" : (0x10840000, 0x40000),
"dram" : (0x60000000, 0x1000000) }
# Runtime mmap objects for each MAPPINGS entry
maps = {}
def stop(cfg):
cfg.RESET_SW |= 8 # Set RUNSTALL: halt CPU
cfg.RESET_SW |= 3 # Set low two bits: "BRESET|DRESET"
def start(cfg, boot_vector):
stop(cfg)
cfg.RESET_SW |= 0x10 # Enable "alternate reset" boot vector
cfg.ALTRESETVEC = boot_vector
cfg.RESET_SW &= ~3 # Release reset bits
cfg.RESET_SW &= ~8 # Clear RUNSTALL: go!
# 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
# currently. Will be replaced by winstream very soon.
def log():
msg = b''
dram = maps["dram"]
for i in range(0x700000, 0x800000):
x = dram[i]
if x == 0:
sys.stdout.buffer.write(msg)
sys.stdout.buffer.flush()
msg = b''
while x == 0:
time.sleep(0.1)
x = dram[i]
msg += x.to_bytes(1, "little")
sys.stdout.buffer.write(msg)
sys.stdout.buffer.flush()
def le4(bstr):
assert len(bstr) == 4
return struct.unpack("<I", bstr)[0]
def main():
# Open device and establish mappings
devmem_fd = open("/dev/mem", "wb+")
for mp in MAPPINGS:
paddr = MAPPINGS[mp][0]
mapsz = MAPPINGS[mp][1]
maps[mp] = mmap.mmap(devmem_fd.fileno(), mapsz, offset=paddr,
flags=mmap.MAP_SHARED, prot=mmap.PROT_WRITE|mmap.PROT_READ)
# Create a Regs object for the registers
cfg = Regs(ctypes.addressof(ctypes.c_int.from_buffer(maps["regs"])))
cfg.ALTRESETVEC = 0x0004 # Xtensa boot address
cfg.RESET_SW = 0x0024 # Xtensa halt/reset/boot control
cfg.PDEBUGBUS0 = 0x000c # Unclear, enabled by host, unused by SOF?
cfg.SRAM_POOL_CON = 0x0930 # SRAM power control: low 4 bits (banks?) enable
cfg.EMI_MAP_ADDR = 0x981c # == host SRAM mapping - 0x40000000 (controls MMIO map?)
cfg.freeze()
if sys.argv[1] == "load":
dat = open(sys.argv[2], "rb").read()
assert le4(dat[0:4])== FILE_MAGIC
sram_len = le4(dat[4:8])
boot_vector = le4(dat[8:12])
sram = dat[12:12+sram_len]
dram = dat[12 + sram_len:]
assert len(sram) <= MAPPINGS["sram"][1]
assert len(dram) <= MAPPINGS["dram"][1]
for i in range(sram_len):
maps["sram"][i] = sram[i]
for i in range(sram_len, MAPPINGS["sram"][1]):
maps["sram"][i] = 0
for i in range(len(dram)):
maps["dram"][i] = dram[i]
for i in range(len(dram), MAPPINGS["dram"][1]):
maps["dram"][i] = 0
start(cfg, boot_vector)
elif sys.argv[1] == "log":
log()
else:
print(f"Usage: {sys.argv[0]} log | load <file>")
# (Cribbed from cavstool.py)
class Regs:
def __init__(self, base_addr):
vars(self)["base_addr"] = base_addr
vars(self)["ptrs"] = {}
vars(self)["frozen"] = False
def freeze(self):
vars(self)["frozen"] = True
def __setattr__(self, name, val):
if not self.frozen and name not in self.ptrs:
addr = self.base_addr + val
self.ptrs[name] = ctypes.c_uint32.from_address(addr)
else:
self.ptrs[name].value = val
def __getattr__(self, name):
return self.ptrs[name].value
if __name__ == "__main__":
main()

View file

@ -1,6 +0,0 @@
family:
- name: mtk_adsp
series:
- name: mtk_adsp
socs:
- name: mt8195_adsp

View file

@ -35,6 +35,7 @@ static uint32_t cpu_hz(void)
ZTEST(mtk_adsp, cpu_freq) ZTEST(mtk_adsp, cpu_freq)
{ {
#ifdef CONFIG_SOC_SERIES_MT8195
int freqs[] = { 26, 370, 540, 720 }; int freqs[] = { 26, 370, 540, 720 };
for (int i = 0; i < ARRAY_SIZE(freqs); i++) { for (int i = 0; i < ARRAY_SIZE(freqs); i++) {
@ -49,6 +50,9 @@ ZTEST(mtk_adsp, cpu_freq)
zassert_true(err > 200); zassert_true(err > 200);
} }
#else
(void)cpu_hz();
#endif
} }
#define MBOX0 DEVICE_DT_GET(DT_INST(0, mediatek_mbox)) #define MBOX0 DEVICE_DT_GET(DT_INST(0, mediatek_mbox))
@ -71,9 +75,9 @@ static void mbox_fn(const struct device *mbox, void *arg)
* on mbox1 after receiving a "command" on mbox0 (you can also see it * on mbox1 after receiving a "command" on mbox0 (you can also see it
* whine about the invalid IPC message in the kernel logs). * whine about the invalid IPC message in the kernel logs).
* *
* Note that there's a catch: SOF's "reply" comes after a timeout * Note that there's a catch: on older kernels, SOF's "reply" comes
* (it's an invalid command, afterall) which is 165 seconds! But the * after a timeout (it's an invalid command, afterall) which is 165
* test does pass. * seconds! But the test does pass.
*/ */
ZTEST(mtk_adsp, mbox) ZTEST(mtk_adsp, mbox)
{ {