|
|
|
@ -2,13 +2,13 @@
@@ -2,13 +2,13 @@
|
|
|
|
|
# Copyright 2023 The ChromiumOS Authors |
|
|
|
|
# SPDX-License-Identifier: Apache-2.0 |
|
|
|
|
import ctypes |
|
|
|
|
import mmap |
|
|
|
|
import os |
|
|
|
|
import re |
|
|
|
|
import struct |
|
|
|
|
import sys |
|
|
|
|
import mmap |
|
|
|
|
import time |
|
|
|
|
import struct |
|
|
|
|
from glob import glob |
|
|
|
|
import re |
|
|
|
|
|
|
|
|
|
# MT8195 audio firmware load/debug gadget |
|
|
|
|
|
|
|
|
@ -34,20 +34,21 @@ import re
@@ -34,20 +34,21 @@ import re
|
|
|
|
|
# phandles pointing to "adsp_mem_region" and "adsp_dma_mem_region" |
|
|
|
|
# nodes under "/reserved-memory"). |
|
|
|
|
|
|
|
|
|
FILE_MAGIC = 0xe463be95 |
|
|
|
|
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") |
|
|
|
|
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 |
|
|
|
@ -69,16 +70,17 @@ def mappings():
@@ -69,16 +70,17 @@ def mappings():
|
|
|
|
|
break |
|
|
|
|
return maps |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Register API for 8195 |
|
|
|
|
class MT8195(): |
|
|
|
|
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.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.EMI_MAP_ADDR = 0x981C # == host SRAM mapping - 0x40000000 (controls MMIO map?) |
|
|
|
|
r.freeze() |
|
|
|
|
self.cfg = r |
|
|
|
|
|
|
|
|
@ -96,51 +98,53 @@ class MT8195():
@@ -96,51 +98,53 @@ class MT8195():
|
|
|
|
|
self.cfg.RESET_SW &= ~3 # Release reset bits |
|
|
|
|
self.cfg.RESET_SW &= ~8 # Clear RUNSTALL: go! |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Register API for 8186/8188 |
|
|
|
|
class MT818x(): |
|
|
|
|
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.IO_CONFIG = 0x0C |
|
|
|
|
self.cfg.freeze() |
|
|
|
|
self.sec = Regs(sec_base) |
|
|
|
|
self.sec.ALTVEC_C0 = 0x04 |
|
|
|
|
self.sec.ALTVECSEL = 0x0c |
|
|
|
|
self.sec.ALTVECSEL = 0x0C |
|
|
|
|
self.sec.freeze() |
|
|
|
|
|
|
|
|
|
def logrange(self): |
|
|
|
|
return range(0x700000, 0x800000) |
|
|
|
|
|
|
|
|
|
def stop(self): |
|
|
|
|
self.cfg.IO_CONFIG |= (1<<31) # Set RUNSTALL to stop core |
|
|
|
|
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.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 |
|
|
|
|
self.cfg.SW_RSTN &= 0xFFFFFFEE # Release reset |
|
|
|
|
self.cfg.IO_CONFIG &= 0x7FFFFFFF # Clear RUNSTALL |
|
|
|
|
|
|
|
|
|
class MT8196(): |
|
|
|
|
|
|
|
|
|
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.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.ALTVECSEL = 0x0C |
|
|
|
|
self.sec.freeze() |
|
|
|
|
|
|
|
|
|
def logrange(self): |
|
|
|
@ -162,6 +166,7 @@ class MT8196():
@@ -162,6 +166,7 @@ class MT8196():
|
|
|
|
|
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 |
|
|
|
@ -182,30 +187,37 @@ def log(dev):
@@ -182,30 +187,37 @@ def log(dev):
|
|
|
|
|
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 |
|
|
|
@ -214,13 +226,18 @@ def main():
@@ -214,13 +226,18 @@ def main():
|
|
|
|
|
mmio = mappings() |
|
|
|
|
|
|
|
|
|
# Open device and establish mappings |
|
|
|
|
devmem_fd = open("/dev/mem", "wb+") |
|
|
|
|
with open("/dev/mem", "wb+") as devmem_fd: |
|
|
|
|
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) |
|
|
|
|
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) |
|
|
|
@ -230,7 +247,9 @@ def main():
@@ -230,7 +247,9 @@ def main():
|
|
|
|
|
dev = MT8196(maps) |
|
|
|
|
|
|
|
|
|
if sys.argv[1] == "load": |
|
|
|
|
dat = open(sys.argv[2], "rb").read() |
|
|
|
|
dat = None |
|
|
|
|
with open(sys.argv[2], "rb") as f: |
|
|
|
|
dat = f.read() |
|
|
|
|
assert le4(dat[0:4]) == FILE_MAGIC |
|
|
|
|
sram_len = le4(dat[4:8]) |
|
|
|
|
boot_vector = le4(dat[8:12]) |
|
|
|
@ -266,5 +285,6 @@ def main():
@@ -266,5 +285,6 @@ def main():
|
|
|
|
|
else: |
|
|
|
|
print(f"Usage: {sys.argv[0]} log | load <file>") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
|
|
main() |
|
|
|
|