summaryrefslogtreecommitdiff
path: root/tools/proxyclient/m1n1/hw
diff options
context:
space:
mode:
authormagh <magh@maghmogh.com>2023-03-06 18:44:55 -0600
committermagh <magh@maghmogh.com>2023-03-06 18:44:55 -0600
commite80d9d8871b325a04b18f90a9ea4bb7fd148fb25 (patch)
tree79dbdb8506b7ff1e92549188d1b94cfc0b3503ae /tools/proxyclient/m1n1/hw
add m1n1HEADmaster
Diffstat (limited to 'tools/proxyclient/m1n1/hw')
-rw-r--r--tools/proxyclient/m1n1/hw/admac.py416
-rw-r--r--tools/proxyclient/m1n1/hw/aes.py110
-rw-r--r--tools/proxyclient/m1n1/hw/agx.py111
-rw-r--r--tools/proxyclient/m1n1/hw/asc.py121
-rw-r--r--tools/proxyclient/m1n1/hw/atc.py455
-rw-r--r--tools/proxyclient/m1n1/hw/codecs/__init__.py109
-rw-r--r--tools/proxyclient/m1n1/hw/codecs/cs42l84.py365
-rw-r--r--tools/proxyclient/m1n1/hw/dart.py102
-rw-r--r--tools/proxyclient/m1n1/hw/dart8020.py381
-rw-r--r--tools/proxyclient/m1n1/hw/dart8110.py541
-rw-r--r--tools/proxyclient/m1n1/hw/dockchannel.py120
-rw-r--r--tools/proxyclient/m1n1/hw/dwc3.py268
-rw-r--r--tools/proxyclient/m1n1/hw/i2c.py251
-rw-r--r--tools/proxyclient/m1n1/hw/isp.py507
-rw-r--r--tools/proxyclient/m1n1/hw/jpeg.py334
-rw-r--r--tools/proxyclient/m1n1/hw/mca.py110
-rw-r--r--tools/proxyclient/m1n1/hw/nco.py89
-rw-r--r--tools/proxyclient/m1n1/hw/pmgr.py71
-rw-r--r--tools/proxyclient/m1n1/hw/pmu.py36
-rw-r--r--tools/proxyclient/m1n1/hw/prores.py250
-rw-r--r--tools/proxyclient/m1n1/hw/scaler.py212
-rw-r--r--tools/proxyclient/m1n1/hw/sep.py169
-rw-r--r--tools/proxyclient/m1n1/hw/spi.py147
-rw-r--r--tools/proxyclient/m1n1/hw/spmi.py98
-rw-r--r--tools/proxyclient/m1n1/hw/uat.py571
25 files changed, 5944 insertions, 0 deletions
diff --git a/tools/proxyclient/m1n1/hw/admac.py b/tools/proxyclient/m1n1/hw/admac.py
new file mode 100644
index 0000000..3fb8032
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/admac.py
@@ -0,0 +1,416 @@
+# SPDX-License-Identifier: MIT
+import sys, time
+from enum import IntEnum
+from ..utils import *
+
+__all__ = ["ADMACRegs", "ADMAC", "E_BUSWIDTH", "E_FRAME"]
+
+
+class R_RING(Register32):
+ # overflow/underflow counter
+ OF_UF = 31, 16
+
+ # goes through 0, 1, 2, 3 as the pieces of a report/descriptor
+ # are being read/written through REPORT_READ/DESC_WRITE
+ READOUT_PROGRESS = 13, 12
+
+ # when READ_SLOT==WRITE_SLOT one of the two is set
+ EMPTY = 8
+ FULL = 9
+
+ ERR = 10
+
+ # next slot to read
+ READ_SLOT = 5, 4
+
+ # next slot to be written to
+ WRITE_SLOT = 1, 0
+
+class R_CHAN_STATUS(Register32):
+ # only raised if the descriptor had NOTIFY set
+ DESC_DONE = 0
+
+ DESC_RING_EMPTY = 4
+ REPORT_RING_FULL = 5
+
+ # cleared by writing ERR=1 either to TX_DESC_RING or TX_REPORT_RING
+ RING_ERR = 6
+
+ UNK0 = 1
+ UNK3 = 8
+ UNK4 = 9
+ UNK5 = 10
+
+class R_CHAN_CONTROL(Register32):
+ RESET_RINGS = 0
+ CLEAR_OF_UF_COUNTERS = 1
+ UNK1 = 3
+
+class E_BUSWIDTH(IntEnum):
+ W_8BIT = 0
+ W_16BIT = 1
+ W_32BIT = 2
+
+class E_FRAME(IntEnum):
+ F_1_WORD = 0
+ F_2_WORDS = 1
+ F_4_WORDS = 2
+
+class R_BUSWIDTH(Register32):
+ WORD = 2, 0, E_BUSWIDTH
+ FRAME = 6, 4, E_FRAME
+
+class R_CARVEOUT(Register32):
+ SIZE = 31, 16
+ BASE = 15, 0
+
+class ADMACRegs(RegMap):
+ TX_EN = 0x0, Register32 # one bit per channel
+ TX_EN_CLR = 0x4, Register32
+
+ RX_EN = 0x8, Register32
+ RX_EN_CLR = 0xc, Register32
+
+ UNK_CTL = 0x10, Register32
+
+ # each of the four registers represents an internal interrupt line,
+ # bits represent DMA channels which at the moment raise that particular line
+ #
+ # the irq-destination-index prop in ADT maybe selects the line which
+ # is actually wired out
+ #
+ TX_INTSTATE = irange(0x30, 4, 0x4), Register32
+ RX_INTSTATE = irange(0x40, 4, 0x4), Register32
+
+ # a 24 MHz always-running counter, top bit is always set
+ COUNTER = 0x70, Register64
+
+ TX_SRAM_SIZE = 0x94, Register32
+ RX_SRAM_SIZE = 0x98, Register32
+
+ # -- per-channel registers --
+
+ CHAN_CTL = (irange(0x8000, 32, 0x200)), R_CHAN_CONTROL
+
+ CHAN_BUSWIDTH = (irange(0x8040, 32, 0x200)), R_BUSWIDTH
+ CHAN_SRAM_CARVEOUT = (irange(0x8050, 32, 0x200)), R_CARVEOUT
+ CHAN_BURSTSIZE = (irange(0x8054, 32, 0x200)), Register32
+
+ CHAN_RESIDUE = irange(0x8064, 32, 0x200), Register32
+
+ CHAN_DESC_RING = irange(0x8070, 32, 0x200), R_RING
+ CHAN_REPORT_RING = irange(0x8074, 32, 0x200), R_RING
+
+ TX_DESC_WRITE = irange(0x10000, 16, 4), Register32
+ TX_REPORT_READ = irange(0x10100, 16, 4), Register32
+
+ RX_DESC_WRITE = irange(0x14000, 16, 4), Register32
+ RX_REPORT_READ = irange(0x14100, 16, 4), Register32
+
+ # per-channel, per-internal-line
+ CHAN_STATUS = (irange(0x8010, 32, 0x200), irange(0x0, 4, 0x4)), R_CHAN_STATUS
+ CHAN_INTMASK = (irange(0x8020, 32, 0x200), irange(0x0, 4, 0x4)), R_CHAN_STATUS
+
+
+class ADMACDescriptorFlags(Register32):
+ # whether to raise DESC_DONE in CHAN_STATUS
+ NOTIFY = 16
+
+ # whether to repeat this descriptor ad infinitum
+ #
+ # once a descriptor with this flag is loaded, any descriptors loaded
+ # afterwards are also repeated and nothing short of full power domain reset
+ # seems to revoke that behaviour. this looks like a HW bug.
+ REPEAT = 17
+
+ # arbitrary ID propagated into reports
+ DESC_ID = 7, 0
+
+class ADMACDescriptor(Reloadable):
+ def __init__(self, addr, length, **flags):
+ self.addr = addr
+ self.length = length
+ self.flags = ADMACDescriptorFlags(**flags)
+
+ def __repr__(self):
+ return f"<descriptor: addr=0x{self.addr:x} len=0x{self.length:x} flags={self.flags}>"
+
+ def ser(self):
+ return [
+ self.addr & (1<<32)-1,
+ self.addr>>32 & (1<<32)-1,
+ self.length & (1<<32)-1,
+ int(self.flags)
+ ]
+
+ @classmethod
+ def deser(self, seq):
+ if not len(seq) == 4:
+ raise ValueError
+ return ADMACDescriptor(
+ seq[0] | seq[1] << 32, # addr
+ seq[2], # length (in bytes)
+ **ADMACDescriptorFlags(seq[3]).fields
+ )
+
+
+class ADMACReportFlags(Register32):
+ UNK1 = 24
+ UNK2 = 25
+ UNK4 = 26 # memory access fault?
+ UNK3 = 27
+ DESC_ID = 7, 0
+
+class ADMACReport(Reloadable):
+ def __init__(self, countval, unk1, flags):
+ self.countval, self.unk1, self.flags = countval, unk1, ADMACReportFlags(flags)
+
+ def __repr__(self):
+ return f"<report: countval=0x{self.countval:x} unk1=0x{self.unk1:x} flags={self.flags}>"
+
+ def ser(self):
+ return [
+ self.countval & (1<<32)-1,
+ self.countval>>32 & (1<<32)-1,
+ self.unk1 & (1<<32)-1,
+ int(self.flags)
+ ]
+
+ @classmethod
+ def deser(self, seq):
+ if not len(seq) == 4:
+ raise ValueError
+ return ADMACReport(
+ seq[0] | seq[1] << 32, # countval
+ seq[2], # unk1
+ seq[3] # flags
+ )
+
+
+class ADMACChannel(Reloadable):
+ def __init__(self, parent, channo):
+ self.p = parent
+ self.iface = parent.p.iface
+ self.dart = parent.dart
+ self.regs = parent.regs
+ self.tx = (channo % 2) == 0
+ self.rx = not self.tx
+ self.ch = channo
+
+ self._desc_id = 0
+ self._submitted = {}
+ self._last_report = None
+ self._est_byte_rate = None
+
+ def reset(self):
+ self.regs.CHAN_CTL[self.ch].set(RESET_RINGS=1, CLEAR_OF_UF_COUNTERS=1)
+ self.regs.CHAN_CTL[self.ch].set(RESET_RINGS=0, CLEAR_OF_UF_COUNTERS=0)
+
+ self.burstsize = 0xc0_0060
+ self.buswidth = E_BUSWIDTH.W_32BIT
+ self.framesize = E_FRAME.F_1_WORD
+
+ def enable(self):
+ self.regs.CHAN_INTMASK[self.ch, 0].reg = \
+ R_CHAN_STATUS(DESC_DONE=1, DESC_RING_EMPTY=1,
+ REPORT_RING_FULL=1, RING_ERR=1)
+
+ if self.tx:
+ self.regs.TX_EN.val = 1 << (self.ch//2)
+ else:
+ self.regs.RX_EN.val = 1 << (self.ch//2)
+
+ def disable(self):
+ if self.tx:
+ self.regs.TX_EN_CLR.val = 1 << (self.ch//2)
+ else:
+ self.regs.RX_EN_CLR.val = 1 << (self.ch//2)
+
+ @property
+ def buswidth(self):
+ self.regs.CHAN_BUSWIDTH[self.ch].reg.WORD
+
+ @buswidth.setter
+ def buswidth(self, wordsize):
+ return self.regs.CHAN_BUSWIDTH[self.ch].set(WORD=wordsize)
+
+ @property
+ def framesize(self):
+ self.regs.CHAN_BUSWIDTH[self.ch].reg.FRAME
+
+ @framesize.setter
+ def framesize(self, framesize):
+ return self.regs.CHAN_BUSWIDTH[self.ch].set(FRAME=framesize)
+
+ @property
+ def burstsize(self):
+ return self.regs.CHAN_BURSTSIZE[self.ch].val
+
+ @burstsize.setter
+ def burstsize(self, size):
+ self.regs.CHAN_BURSTSIZE[self.ch].val = size
+
+ @property
+ def sram_carveout(self):
+ reg = self.regs.CHAN_SRAM_CARVEOUT[self.ch].reg
+ return (reg.BASE, reg.SIZE)
+
+ @sram_carveout.setter
+ def sram_carveout(self, carveout):
+ base, size = carveout
+ self.regs.CHAN_SRAM_CARVEOUT[self.ch].reg = \
+ R_CARVEOUT(BASE=base, SIZE=size)
+
+ @property
+ def DESC_WRITE(self):
+ if self.tx:
+ return self.regs.TX_DESC_WRITE[self.ch//2]
+ else:
+ return self.regs.RX_DESC_WRITE[self.ch//2]
+
+ @property
+ def REPORT_READ(self):
+ if self.tx:
+ return self.regs.TX_REPORT_READ[self.ch//2]
+ else:
+ return self.regs.RX_REPORT_READ[self.ch//2]
+
+ def can_submit(self):
+ return not self.regs.CHAN_DESC_RING[self.ch].reg.FULL
+
+ def submit_desc(self, desc):
+ if self.regs.CHAN_DESC_RING[self.ch].reg.FULL:
+ raise Exception(f"ch{self.ch} descriptor ring full")
+
+ if self.p.debug:
+ print(f"admac: submitting (ch{self.ch}): {desc}", file=sys.stderr)
+
+ for piece in desc.ser():
+ self.DESC_WRITE.val = piece
+
+ self._submitted[desc.flags.DESC_ID] = desc
+
+ def submit(self, data=None, buflen=None, **kwargs):
+ if self.tx:
+ assert data is not None
+ buflen = len(data)
+ else:
+ assert buflen is not None
+
+ iova = self.p.get_buffer(buflen)
+ if self.tx:
+ self.p.iowrite(iova, data)
+ self.submit_desc(ADMACDescriptor(
+ iova, buflen, DESC_ID=self._desc_id, NOTIFY=1, **kwargs
+ ))
+ self._desc_id = (self._desc_id + 1) % 256
+
+ def read_reports(self):
+ data = bytearray()
+
+ while not self.regs.CHAN_REPORT_RING[self.ch].reg.EMPTY:
+ pieces = []
+ for _ in range(4):
+ pieces.append(self.REPORT_READ.val)
+ report = ADMACReport.deser(pieces)
+
+ if report.flags.DESC_ID in self._submitted:
+ desc = self._submitted[report.flags.DESC_ID]
+ else:
+ print(f"admac: stray report (ch{self.ch}): {report}", file=sys.stderr)
+ desc = None
+
+ if self.rx and desc and self.p.dart:
+ data.extend(self.p.ioread(desc.addr, desc.length))
+
+ if self.p.debug:
+ if self._last_report and desc:
+ countval_delta = report.countval - self._last_report.countval
+ est_rate = 24e6*desc.length/countval_delta/4
+ est = f"(estimated rate: {est_rate:.2f} dwords/s)"
+ else:
+ est = ""
+
+ print(f"admac: picked up (ch{self.ch}): {report} {est}", file=sys.stderr)
+
+ self._last_report = report
+
+ return data if self.rx else None
+
+ @property
+ def status(self):
+ return self.regs.CHAN_STATUS[self.ch, 0].reg
+
+ def poll(self, wait=True):
+ while not (self.status.DESC_DONE or self.status.RING_ERR):
+ time.sleep(0.001)
+
+ if not wait:
+ break
+
+ self.regs.CHAN_STATUS[self.ch,0].reg = R_CHAN_STATUS(DESC_DONE=1)
+
+ if self.status.RING_ERR:
+ if self.p.debug:
+ print(f"STATUS={self.regs.CHAN_STATUS[self.ch,1].reg} " + \
+ f"REPORT_RING={self.regs.CHAN_DESC_RING[self.ch]} " + \
+ f"DESC_RING={self.regs.CHAN_REPORT_RING[self.ch]}",
+ file=sys.stderr)
+ self.regs.CHAN_DESC_RING[self.ch].set(ERR=1)
+ self.regs.CHAN_REPORT_RING[self.ch].set(ERR=1)
+
+ return self.read_reports()
+
+
+class ADMAC(Reloadable):
+ def __init__(self, u, devpath, dart=None, dart_stream=2,
+ reserved_size=4*1024*1024, debug=False):
+ self.u = u
+ self.p = u.proxy
+ self.debug = debug
+
+ if type(devpath) is str:
+ adt_node = u.adt[devpath]
+ # ADT's #dma-channels counts pairs of RX/TX channel, so multiply by two
+ self.nchans = adt_node._properties["#dma-channels"] * 2
+ self.base, _ = adt_node.get_reg(0)
+ else:
+ self.base = devpath
+ self.nchans = 26
+
+ self.regs = ADMACRegs(u, self.base)
+ self.dart, self.dart_stream = dart, dart_stream
+
+ if dart is not None:
+ resmem_phys = u.heap.memalign(128*1024, reserved_size)
+ self.resmem_iova = self.dart.iomap(dart_stream, resmem_phys, reserved_size)
+ self.resmem_size = reserved_size
+ self.resmem_pos = 0
+ self.dart.invalidate_streams(1 << dart_stream)
+
+ self.chans = [ADMACChannel(self, no) for no in range(self.nchans)]
+
+ def ioread(self, base, size):
+ assert self.dart is not None
+ return self.dart.ioread(self.dart_stream, base, size)
+
+ def iowrite(self, base, data):
+ assert self.dart is not None
+ self.dart.iowrite(self.dart_stream, base, data)
+
+ def fill_canary(self):
+ ranges = self.dart.iotranslate(self.dart_stream,
+ self.resmem_iova, self.resmem_size)
+ assert len(ranges) == 1
+ start, size = ranges[0]
+ self.p.memset8(start, 0xba, size)
+
+ def get_buffer(self, size):
+ assert size < self.resmem_size
+
+ if self.resmem_pos + size > self.resmem_size:
+ self.resmem_pos = 0
+
+ bufptr = self.resmem_iova + self.resmem_pos
+ self.resmem_pos += size
+ return bufptr
diff --git a/tools/proxyclient/m1n1/hw/aes.py b/tools/proxyclient/m1n1/hw/aes.py
new file mode 100644
index 0000000..7e09335
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/aes.py
@@ -0,0 +1,110 @@
+# SPDX-License-Identifier: MIT
+from ..utils import *
+from enum import IntEnum
+from .dart import DART, DARTRegs
+import struct
+from enum import IntEnum
+
+
+class AES_OPCODE(IntEnum):
+ # 0 triggers an invalid command interrupt
+ SET_KEY = 1
+ SET_IV = 2
+ # 0x03 seems to take three additional argument, function unknown
+ # 0x04 seems to take one additional argument, function unknown
+ CRYPT = 5
+ GET_IV = 6
+ # 0x07 takes one additional argument, function unknown
+ BARRIER = 8 # can be used to trigger an IRQ but possibly also does more
+ # > 8 trigger an invalid command interrupt
+
+
+class AES_SET_KEY_LEN(IntEnum):
+ AES128 = 0
+ AES192 = 1
+ AES256 = 2
+
+
+class AES_SET_KEY_BLOCK_MODE(IntEnum):
+ ECB = 0
+ CBC = 1
+ CTR = 2
+
+
+class AESCommandBase(Register32):
+ OPCODE = 31, 28, AES_OPCODE
+
+
+class AESHwKey(IntEnum):
+ SOFTWARE = 0
+ UID = 1 # unique key for each chip
+ GID0 = 2 # (probably) globally unique key within a chip family
+ GID1 = 3 # globally unique key within a chip family
+ # 4-7 are probably empty / reserved for future use
+
+
+class AESSetKeyCommand(AESCommandBase):
+ OPCODE = 31, 28, Constant(AES_OPCODE.SET_KEY)
+ SLOT = 27, 27
+ KEY_SELECT = 26, 24
+ KEYLEN = 23, 22, AES_SET_KEY_LEN
+ # setting bit 21 breaks the engine and sets two bits in the IRQ status
+ ENCRYPT = 20, 20
+ KEYGEN = 19, 18
+ BLOCK_MODE = 17, 16, AES_SET_KEY_BLOCK_MODE
+ # 15, 0 doesn't seem to have any effect
+
+
+class AESCryptCommand(AESCommandBase):
+ OPCODE = 31, 28, Constant(AES_OPCODE.CRYPT)
+ KEY_SLOT = 27, 27
+ IV_SLOT = 26, 25
+ LEN = 24, 0
+
+
+class AESBarrierCommand(AESCommandBase):
+ OPCODE = 31, 28, Constant(AES_OPCODE.BARRIER)
+ IRQ = 27, 27
+
+
+class AESGetIVCommand(AESCommandBase):
+ OPCODE = 31, 28, Constant(AES_OPCODE.GET_IV)
+
+
+class AESSetIVCommand(AESCommandBase):
+ OPCODE = 31, 28, Constant(AES_OPCODE.SET_IV)
+ SLOT = 27, 26
+
+
+class AESIrqReg(Register32):
+ KEY1_EMPTY = 17, 17
+ KEY1_INVALID = 13, 13
+ KEY0_EMPTY = 11, 11
+ KEY0_INVALID = 7, 7
+ FLAG = 5, 5
+ UNKNOWN_COMMAND = 2, 2
+ FIFO_OVERFLOW = 1, 1
+
+
+class AESControlReg(Register32):
+ START = 0, 0
+ STOP = 1, 1
+ CLEAR_FIFO = 2, 2
+ # TOOD: not convinced about RESET anymore, I remember this un-broke the engine once but I can't reproduce that anymore
+ RESET = 3, 3
+
+
+class AESFifoStatusReg(Register32):
+ FIFO_WRITE_PTR = 31, 24
+ FIFO_READ_PTR = 23, 16
+ FIFO_LEVEL = 15, 8
+ FIFO_FULL = 2, 2
+ FIFO_EMPTY = 1, 1
+
+
+class AESRegs(RegMap):
+ R_CONTROL = 0x08, AESControlReg
+ R_IRQ_STATUS = 0x18, AESIrqReg
+ R_IRQ_ENABLE = 0x1C, AESIrqReg
+ R_FIFO_STATUS = 0x24, AESFifoStatusReg
+ R_CMD_FIFO = 0x200, Register32
diff --git a/tools/proxyclient/m1n1/hw/agx.py b/tools/proxyclient/m1n1/hw/agx.py
new file mode 100644
index 0000000..e5c2daa
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/agx.py
@@ -0,0 +1,111 @@
+# SPDX-License-Identifier: MIT
+from ..utils import *
+from enum import IntEnum
+
+__all__ = ["SGXRegs", "SGXInfoRegs", "agx_decode_unit", "R_FAULT_INFO"]
+
+class FAULT_REASON(IntEnum):
+ INVALID = 0
+ AF_FAULT = 1
+ WRITE_ONLY = 2
+ READ_ONLY = 3
+ NO_ACCESS = 4
+ UNK = 5
+
+class R_FAULT_INFO(Register64):
+ ADDR = 63, 24
+ WRITE = 23
+ CONTEXT = 22, 17
+ UNIT = 16, 9
+ UNK_8 = 8
+ REASON = 3, 1, FAULT_REASON
+ FAULTED = 0
+
+class SGXRegs(RegMap):
+ FAULT_INFO = 0x17030, R_FAULT_INFO
+
+class SGXInfoRegs(RegMap):
+ CORE_MASK_0 = 0x1500, Register32,
+ CORE_MASK_1 = 0x1514, Register32,
+
+ ID_00 = 0x4000, Register32,
+ ID_04 = 0x4004, Register32,
+ ID_08 = 0x4008, Register32,
+ ID_0c = 0x400c, Register32,
+ ID_10 = 0x4010, Register32,
+ ID_14 = 0x4014, Register32,
+ ID_18 = 0x4018, Register32,
+ ID_1c = 0x401c, Register32,
+
+ ID_8024 = 0x8024, Register32,
+
+class UNIT_00(IntEnum):
+ DCMPn = 0x00
+ UL1Cn = 0x01
+ CMPn = 0x02
+ GSL1_n = 0x03
+ IAPn = 0x04
+ VCEn = 0x05
+ TEn = 0x06
+ RASn = 0x07
+ VDMn = 0x08
+ PPPn = 0x09
+ IPFn = 0x0a
+ IPF_CPFn = 0x0b
+ VFn = 0x0c
+ VF_CPFn = 0x0d
+ ZLSn = 0x0e
+
+class UNIT_A0(IntEnum):
+ dPM = 0xa1
+ dCDM_KS0 = 0xa2
+ dCDM_KS1 = 0xa3
+ dCDM_KS2 = 0xa4
+ dIPP = 0xa5
+ dIPP_CS = 0xa6
+ dVDM_CSD = 0xa7
+ dVDM_SSD = 0xa8
+ dVDM_ILF = 0xa9
+ dVDM_ILD = 0xaa
+ dRDE0 = 0xab
+ dRDE1 = 0xac
+ FC = 0xad
+ GSL2 = 0xae
+
+ GL2CC_META0 = 0xb0
+ GL2CC_META1 = 0xb1
+ GL2CC_META2 = 0xb2
+ GL2CC_META3 = 0xb3
+ GL2CC_META4 = 0xb4
+ GL2CC_META5 = 0xb5
+ GL2CC_META6 = 0xb6
+ GL2CC_META7 = 0xb7
+ GL2CC_MB = 0xb8
+
+class UNIT_E0(IntEnum):
+ gPM_SPn = 0xe0
+ gVDM_CSD_SPn = 0xe1
+ gVDM_SSD_SPn = 0xe2
+ gVDM_ILF_SPn = 0xe3
+ gVDM_TFP_SPn = 0xe4
+ gVDM_MMB_SPn = 0xe5
+ gCDM_CS_SPn_KS0 = 0xe6
+ gCDM_CS_SPn_KS1 = 0xe7
+ gCDM_CS_SPn_KS2 = 0xe8
+ gCDM_SPn_KS0 = 0xe9
+ gCDM_SPn_KS1 = 0xea
+ gCDM_SPn_KS2 = 0xeb
+ gIPP_SPn = 0xec
+ gIPP_CS_SPn = 0xed
+ gRDE0_SPn = 0xee
+ gRDE1_SPn = 0xef
+
+def agx_decode_unit(v):
+ if v < 0xa0:
+ group = v >> 4
+ return UNIT_00(v & 0x0f).name.replace("n", str(group))
+ elif v < 0xe0:
+ return UNIT_A0(v).name
+ else:
+ group = (v >> 4) & 1
+ return UNIT_E0(v & 0xef).name.replace("n", str(group))
diff --git a/tools/proxyclient/m1n1/hw/asc.py b/tools/proxyclient/m1n1/hw/asc.py
new file mode 100644
index 0000000..f0923f8
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/asc.py
@@ -0,0 +1,121 @@
+# SPDX-License-Identifier: MIT
+from ..utils import *
+import time
+
+class R_MBOX_CTRL(Register32):
+ FIFOCNT = 23, 20
+ OVERFLOW = 18
+ EMPTY = 17
+ FULL = 16
+ RPTR = 15, 12
+ WPTR = 11, 8
+ ENABLE = 0
+
+class R_CPU_CONTROL(Register32):
+ RUN = 4
+
+class R_CPU_STATUS(Register32):
+ IDLE = 5
+ FIQ_NOT_PEND = 3 # guess
+ IRQ_NOT_PEND = 2 # guess
+ STOPPED = 1
+ RUNNING = 0
+
+class R_INBOX1(Register64):
+ EP = 7, 0
+
+class R_OUTBOX1(Register64):
+ OUTCNT = 56, 52
+ INCNT = 51, 48
+ OUTPTR = 47, 44
+ INPTR = 43, 40
+ EP = 7, 0
+
+class ASCRegs(RegMap):
+ CPU_CONTROL = 0x0044, R_CPU_CONTROL
+ CPU_STATUS = 0x0048, R_CPU_STATUS
+
+ INBOX_CTRL = 0x8110, R_MBOX_CTRL
+ OUTBOX_CTRL = 0x8114, R_MBOX_CTRL
+ INBOX0 = 0x8800, Register64
+ INBOX1 = 0x8808, R_INBOX1
+ OUTBOX0 = 0x8830, Register64
+ OUTBOX1 = 0x8838, R_OUTBOX1
+
+class ASC:
+ def __init__(self, u, asc_base):
+ self.u = u
+ self.p = u.proxy
+ self.iface = u.iface
+ self.asc = ASCRegs(u, asc_base)
+ self.verbose = 0
+ self.epmap = {}
+
+ def recv(self):
+ if self.asc.OUTBOX_CTRL.reg.EMPTY:
+ return None, None
+
+ msg0 = self.asc.OUTBOX0.val
+ msg1 = R_INBOX1(self.asc.OUTBOX1.val)
+ if self.verbose >= 3:
+ print(f"< {msg1.EP:02x}:{msg0:#x}")
+ return msg0, msg1
+
+ def send(self, msg0, msg1):
+ self.asc.INBOX0.val = msg0
+ self.asc.INBOX1.val = msg1
+
+ if self.verbose >= 3:
+ if isinstance(msg0, Register):
+ print(f"> {msg1.EP:02x}:{msg0}")
+ else:
+ print(f"> {msg1.EP:02x}:{msg0:#x}")
+
+ while self.asc.INBOX_CTRL.reg.FULL:
+ pass
+
+ def is_running(self):
+ return not self.asc.CPU_STATUS.reg.STOPPED
+
+ def boot(self):
+ self.asc.CPU_CONTROL.set(RUN=1)
+
+ def shutdown(self):
+ self.asc.CPU_CONTROL.set(RUN=0)
+
+ def add_ep(self, idx, ep):
+ self.epmap[idx] = ep
+ setattr(self, ep.SHORT, ep)
+
+ def has_messages(self):
+ return not self.asc.OUTBOX_CTRL.reg.EMPTY
+
+ def work_pending(self):
+ while self.has_messages():
+ self.work()
+
+ def work(self):
+ if self.asc.OUTBOX_CTRL.reg.EMPTY:
+ return True
+
+ msg0, msg1 = self.recv()
+
+ handled = False
+
+ ep = self.epmap.get(msg1.EP, None)
+ if ep:
+ handled = ep.handle_msg(msg0, msg1)
+
+ if not handled:
+ print(f"unknown message: {msg0:#16x} / {msg1}")
+
+ return handled
+
+ def work_forever(self):
+ while self.work():
+ pass
+
+ def work_for(self, timeout):
+ deadline = time.time() + timeout
+ while time.time() < deadline:
+ self.work()
diff --git a/tools/proxyclient/m1n1/hw/atc.py b/tools/proxyclient/m1n1/hw/atc.py
new file mode 100644
index 0000000..7ae92c0
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/atc.py
@@ -0,0 +1,455 @@
+# SPDX-License-Identifier: MIT
+
+from enum import IntEnum
+from m1n1.utils import *
+
+
+class R_USB2PHY_USBCTL(Register32):
+ MODE_HOST = 1
+ MODE_ISOLATION = 2
+
+
+class R_USB2PHY_CTL(Register32):
+ RESET = 0
+ PORT_RESET = 1
+ APB_RESETN = 2
+ SIDDQ = 3
+
+
+class R_USB2PHY_SIG(Register32):
+ VBUSDET_FORCE_VAL = 0
+ VBUSDET_FORCE_EN = 1
+ VBUSVLDEXT_FORCE_VAL = 2
+ VBUSVLDEXT_FORCE_EN = 3
+ MODE_HOST = 19, 12
+
+
+class R_USB2PHY_MISCTUNE(Register32):
+ APBCLK_GATE_OFF = 29
+ REFCLK_GATE_OFF = 30
+
+
+class Usb2PhyRegs(RegMap):
+ USB2PHY_USBCTL = 0x00, R_USB2PHY_USBCTL
+ USB2PHY_CTL = 0x04, R_USB2PHY_CTL
+ USB2PHY_SIG = 0x08, R_USB2PHY_SIG
+ USB2PHY_MISCTUNE = 0x1C, R_USB2PHY_MISCTUNE
+
+
+class R_AUSPLL_DCO_EFUSE_SPARE(Register32):
+ RODCO_ENCAP_EFUSE = 10, 9
+ RODCO_BIAS_ADJUST_EFUSE = 14, 12
+
+
+class R_AUSPLL_FRACN_CAN(Register32):
+ DLL_START_CAPCODE = 18, 17
+
+
+class R_AUSPLL_FSM_CTRL(Register32):
+ APBREQ_OVSEL = 21, 13
+
+
+class R_AUSPLL_CMD_OVERRIDE(Register32):
+ APB_OVERRIDE = 28
+
+
+class R_AUSPLL_CLKOUT_DTC_VREG(Register32):
+ DTC_VREG_ADJUST = 16, 14
+
+
+class R_AUS_COMMON_SHIM_BLK_VREG(Register32):
+ VREG_TRIM = 6, 2
+
+
+class R_CIO3PLL_CLK_CTRL(Register32):
+ PCLK_EN = 1
+ REFCLK_EN = 5
+
+
+class R_CIO3PLL_DCO_NCTRL(Register32):
+ DCO_COARSEBIN_EFUSE0 = 6, 0
+ DCO_COARSEBIN_EFUSE1 = 23, 17
+
+
+class R_CIO3PLL_FRACN_CAN(Register32):
+ DLL_CAL_START_CAPCODE = 18, 17
+
+
+class R_CIO3PLL_DTC_VREG(Register32):
+ DTC_VREG_ADJUST = 16, 14
+
+
+class E_ACIOPHY_CROSSBAR_PROTOCOL(IntEnum):
+ USB4 = 0
+ USB3 = 5
+ USB3_DP = 8
+ DP = 10
+
+
+class R_ACIOPHY_CROSSBAR(Register32):
+ PROTOCOL_SWAPPED = 0
+ PROTOCOL = 4, 1, E_ACIOPHY_CROSSBAR_PROTOCOL
+ DPMODE = 17, 5
+
+
+class E_ACIOPHY_LANE_MODE(IntEnum):
+ USB4 = 0
+ USB3 = 1
+ DP = 2
+ OFF = 3
+
+
+class R_ACIOPHY_LANE_MODE(Register32):
+ RX0 = 2, 0, E_ACIOPHY_LANE_MODE
+ TX0 = 5, 3, E_ACIOPHY_LANE_MODE
+ RX1 = 8, 6, E_ACIOPHY_LANE_MODE
+ TX1 = 11, 9, E_ACIOPHY_LANE_MODE
+
+
+class R_ATCPHY_POWER(Register32):
+ SLEEP_SMALL = 0
+ SLEEP_BIG = 1
+ CLAMP_EN = 2
+ APB_RESET_N = 3
+ PHY_RESET_N = 4
+
+
+class R_ATCPHY_MISC(Register32):
+ RESET_N = 0
+ LANE_SWAP = 2
+
+
+class R_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG0(Register32):
+ PMA_TXA_BYTECLK_RESET_SYNC_EN_OV = 2
+ PMA_TXA_BYTECLK_RESET_SYNC_EN = 3
+ PMA_TXA_BYTECLK_RESET_SYNC_CLR_OV = 4
+ PMA_TXA_BYTECLK_RESET_SYNC_CLR = 5
+ PMA_TXA_BYTECLK_RESET_SYNC_SEL_OV = 6
+
+
+class R_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG1(Register32):
+ PMA_TXA_DIV2_EN_OV = 8
+ PMA_TXA_DIV2_EN = 9
+ PMA_TXA_DIV2_RESET_OV = 10
+ PMA_TXA_DIV2_RESET = 11
+ PMA_TXA_CLK_EN_OV = 22
+ PMA_TXA_CLK_EN = 23
+
+
+class R_AUSPMA_TX_SHM_TXA_IMP_REG0(Register32):
+ PMA_TXA_CAL_CTRL_OV = 0
+ PMA_TXA_CAL_CTRL = 18, 1
+ PMA_TXA_CAL_CTRL_BASE_OV = 19
+ PMA_TXA_CAL_CTRL_BASE = 23, 20
+ PMA_TXA_HIZ_OV = 29
+ PMA_TXA_HIZ = 30
+
+
+class R_AUSPMA_TX_SHM_TXA_IMP_REG2(Register32):
+ PMA_TXA_MARGIN_OV = 0
+ PMA_TXA_MARGIN = 18, 1
+ PMA_TXA_MARGIN_2R_OV = 19
+ PMA_TXA_MARGIN_2R = 20
+
+
+class R_AUSPMA_TX_SHM_TXA_IMP_REG3(Register32):
+ PMA_TXA_MARGIN_POST_OV = 0
+ PMA_TXA_MARGIN_POST = 10, 1
+ PMA_TXA_MARGIN_POST_2R_OV = 11
+ PMA_TXA_MARGIN_POST_2R = 12
+ PMA_TXA_MARGIN_POST_4R_OV = 13
+ PMA_TXA_MARGIN_POST_4R = 14
+ PMA_TXA_MARGIN_PRE_OV = 15
+ PMA_TXA_MARGIN_PRE = 21, 16
+ PMA_TXA_MARGIN_PRE_2R_OV = 22
+ PMA_TXA_MARGIN_PRE_2R = 23
+ PMA_TXA_MARGIN_PRE_4R_OV = 24
+ PMA_TXA_MARGIN_PRE_4R = 25
+
+
+class R_AUSPMA_TX_SHM_TXA_LDOCLK(Register32):
+ PMA_TXA_LDOCLK_BYPASS_SML_OV = 8
+ PMA_TXA_LDOCLK_BYPASS_SML = 9
+ PMA_TXA_LDOCLK_BYPASS_BIG_OV = 10
+ PMA_TXA_LDOCLK_BYPASS_BIG = 11
+ PMA_TXA_LDOCLK_EN_SML_OV = 12
+ PMA_TXA_LDOCLK_EN_SML = 13
+ PMA_TXA_LDOCLK_EN_BIG_OV = 14
+ PMA_TXA_LDOCLK_EN_BIG = 15
+
+
+class R_AUSPMA_RX_SHM_TJ_RXA_CTLE_CTRL0(Register32):
+ PMA_RXA_TX_CLK_EN = 20
+ PMA_RXA_TX_CLK_EN_OV = 21
+
+
+class R_AUSPMA_RX_SHM_TJ_RXA_AFE_CTRL1(Register32):
+ CLK_LANE_RX_DIV20_SYNC_RESET_N_OV = 29
+ CLK_LANE_RX_DIV20_SYNC_RESET_N_VAL = 30
+
+
+class R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL10(Register32):
+ PMA_RXA_DTVREG_ADJUST = 31, 27
+
+
+class R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL11(Register32):
+ PMA_RXA_DTVREG_BIG_EN = 23
+ PMA_RXA_DTVREG_BIG_EN_OV = 24
+ PMA_RXA_DTVREG_SML_EN = 25
+ PMA_RXA_DTVREG_SML_EN_OV = 26
+
+
+class R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL12(Register32):
+ PMA_RXA_TX_BYTECLK_RESET_SYNC_CLR = 22
+ PMA_RXA_TX_BYTECLK_RESET_SYNC_CLR_OV = 23
+ PMA_RXA_TX_BYTECLK_RESET_SYNC_EN = 24
+ PMA_RXA_TX_BYTECLK_RESET_SYNC_EN_OV = 25
+ PMA_RXA_TX_HRCLK_SEL = 28
+ PMA_RXA_TX_HRCLK_SEL_OV = 29
+ PMA_RXA_TX_PBIAS_EN = 30
+ PMA_RXA_TX_PBIAS_EN_OV = 31
+
+
+class R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL13(Register32):
+ PMA_RXA_TX_PRE_EN = 0
+ PMA_RXA_TX_PRE_EN_OV = 1
+ PMA_RXA_TX_PST1_EN = 2
+ PMA_RXA_TX_PST1_EN_OV = 3
+ PMA_RXA_DTVREG_ADJUST_OV = 15
+
+
+class R_AUSPMA_RX_SHM_TJ_RXA_SAVOS_CTRL16(Register32):
+ PMA_RXA_RXTERM_EN = 21
+ PMA_RXA_RXTERM_EN_OV = 22
+ PMA_RXA_RXTERM_PULLUP_LEAK_EN = 23
+ PMA_RXA_RXTERM_PULLUP_LEAK_EN_OV = 24
+ PMA_RXA_TX_CAL_CODE = 29, 25
+ PMA_RXA_TX_CAL_CODE_OV = 30
+
+
+class R_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL17(Register32):
+ PMA_RXA_TX_MARGIN = 19, 15
+ PMA_RXA_TX_MARGIN_OV = 20
+ PMA_RXA_TX_MARGIN_LSB = 21
+ PMA_RXA_TX_MARGIN_LSB_OV = 22
+ PMA_RXA_TX_MARGIN_P1 = 26, 23
+ PMA_RXA_TX_MARGIN_P1_OV = 27
+ PMA_RXA_TX_MARGIN_P1_LSB = 29, 28
+ PMA_RXA_TX_MARGIN_P1_LSB_OV = 30
+
+
+class R_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL18(Register32):
+ PMA_RXA_TX_P1_CODE = 3, 0
+ PMA_RXA_TX_P1_CODE_OV = 4
+ PMA_RXA_TX_P1_LSB_CODE = 6, 5
+ PMA_RXA_TX_P1_LSB_CODE_OV = 7
+ PMA_RXA_TX_MARGIN_PRE = 10, 8
+ PMA_RXA_TX_MARGIN_PRE_OV = 11
+ PMA_RXA_TX_MARGIN_PRE_LSB = 13, 12
+ PMA_RXA_TX_MARGIN_PRE_LSB_OV = 14
+ PMA_RXA_TX_PRE_LSB_CODE = 16, 15
+ PMA_RXA_TX_PRE_LSB_CODE_OV = 17
+ PMA_RXA_TX_PRE_CODE = 21, 18
+ PMA_RXA_TX_PRE_CODE_OV = 22
+
+
+class R_AUSPMA_RX_SHM_TJ_RXA_TERM_CTRL19(Register32):
+ PMA_RXA_TX_TEST_EN = 21
+ PMA_RXA_TX_TEST_EN_OV = 22
+ PMA_RXA_TX_EN = 23
+ PMA_RXA_TX_EN_OV = 24
+ PMA_RXA_TX_CLK_DLY_CTRL_TAPGEN = 27, 25
+ PMA_RXA_TX_CLK_DIV2_EN = 28
+ PMA_RXA_TX_CLK_DIV2_EN_OV = 29
+ PMA_RXA_TX_CLK_DIV2_RST = 30
+ PMA_RXA_TX_CLK_DIV2_RST_OV = 31
+
+
+class R_AUSPMA_RX_SHM_TJ_RXA_VREF_CTRL22(Register32):
+ PMA_RXA_VREF_ADJUST_GRAY = 11, 7
+ PMA_RXA_VREF_ADJUST_GRAY_OV = 12
+ PMA_RXA_VREF_BIAS_SEL = 14, 13
+ PMA_RXA_VREF_BIAS_SEL_OV = 15
+ PMA_RXA_VREF_BOOST_EN = 16
+ PMA_RXA_VREF_BOOST_EN_OV = 17
+ PMA_RXA_VREF_EN = 18
+ PMA_RXA_VREF_EN_OV = 19
+ LPBKIN_RECOVERED_DATA = 29, 28
+ PMA_RXA_TEST_RXLPBKDT_EN = 30
+ PMA_RXA_TEST_RXLPBKDT_EN_OV = 31
+
+
+class R_AUSPMA_RX_TOP_TJ_CFG_RX_TXMODE(Register32):
+ RX_TXMODE = 0
+
+class R_ACIOPHY_LANE_DP_CFG_BLK_TX_DP_CTRL0(Register32):
+ DP_PMA_BYTECLK_RESET = 0
+ DP_MAC_DIV20_CLK_SEL = 1
+ DPTXPHY_PMA_LANE_RESET_N = 2
+ DPTXPHY_PMA_LANE_RESET_N_OV = 3
+ DPTX_PCLK1_SELECT = 6, 4
+ DPTX_PCLK2_SELECT = 9, 7
+ DPRX_PCLK_SELECT = 12, 10
+ DPTX_PCLK1_ENABLE = 13
+ DPTX_PCLK2_ENABLE = 14
+ DPRX_PCLK_ENABLE = 15
+
+
+class AtcPhyRegs(RegMap):
+ ACIOPHY_CFG0 = 0x08, Register32
+ ACIOPHY_LANE_MODE = 0x48, R_ACIOPHY_LANE_MODE
+ ACIOPHY_CROSSBAR = 0x4C, R_ACIOPHY_CROSSBAR
+ ACIOPHY_BIST_EN = 0x84, Register32
+ ACIOPHY_BIST_OV = 0x8C, Register32
+ ACIOPHY_BIST_CFG0 = 0x90, Register32
+ ACIOPHY_BIST_STAT = 0x9C, Register32
+ ACIOPHY_BIST_RESET = 0xA8, Register32
+ ACIOPHY_BIST_CFG1 = 0xAC, Register32
+ ACIOPHY_SLEEP_CTRL = 0x1B0, Register32
+
+ AUS_COMMON_SHIM_BLK_VREG = 0x0A04, R_AUS_COMMON_SHIM_BLK_VREG
+
+ AUSPLL_FSM_CTRL = 0x1014, R_AUSPLL_FSM_CTRL
+ AUSPLL_CMD_OVERRIDE = 0x2000, R_AUSPLL_CMD_OVERRIDE
+ AUSPLL_CLKOUT_DTC_VREG = 0x2220, R_AUSPLL_CLKOUT_DTC_VREG
+ AUSPLL_DCO_EFUSE_SPARE = 0x222C, R_AUSPLL_DCO_EFUSE_SPARE
+ AUSPLL_FRACN_CAN = 0x22A4, R_AUSPLL_FRACN_CAN
+
+ CIO3PLL_CLK_CTRL = 0x2A00, R_CIO3PLL_CLK_CTRL
+ CIO3PLL_DTC_VREG = 0x2A20, R_CIO3PLL_DTC_VREG
+ CIO3PLL_DCO_NCTRL = 0x2A38, R_CIO3PLL_DCO_NCTRL
+ CIO3PLL_FRACN_CAN = 0x2AA4, R_CIO3PLL_FRACN_CAN
+
+ ATCPHY_POWER_CTRL = 0x20000, R_ATCPHY_POWER
+ ATCPHY_POWER_STAT = 0x20004, R_ATCPHY_POWER
+ ATCPHY_MISC = 0x20008, R_ATCPHY_MISC
+
+ ACIOPHY_LANE_DP_CFG_BLK_TX_DP_CTRL0 = 0x7000, R_ACIOPHY_LANE_DP_CFG_BLK_TX_DP_CTRL0
+ DPPHY_UNK_1028 = 0x1028, Register32
+ USB2PHY_AUX_CFG_BLK_AUX_POWER_DOWN_CONTROL_0 = 0x54000, Register32
+
+ FABRIC_TUNABLES = irange(0x45000, 0x1000 // 4, 4), Register32
+
+ LPDPTX_AUX_CFG_BLK = irange(0x50000, 0x1000 // 4, 4), Register32
+ LPDPTX_AUX_CFG_BLK_AUX_CTRL = 0x50000, Register32
+ LPDPTX_AUX_CFG_BLK_AUX_LDO_CTRL = 0x50008, Register32
+ LPDPTX_AUX_CFG_BLK_AUX_MARGIN = 0x5000c, Register32
+ LPDPTX_AUX_SHM_CFG_BLK_AUX_CTRL_REG0 = 0x50204, Register32
+ LPDPTX_AUX_SHM_CFG_BLK_AUX_CTRL_REG1 = 0x50208, Register32
+
+ LN0_AUSPMA_RX_TOP = irange(0x9000, 0x1000 // 4, 4), Register32
+ LN0_AUSPMA_RX_TOP_TJ_CFG_RX_TXMODE = 0x90F0, R_AUSPMA_RX_TOP_TJ_CFG_RX_TXMODE
+
+ LN0_AUSPMA_RX_EQ = irange(0xA000, 0x1000 // 4, 4), Register32
+
+ LN0_AUSPMA_RX_SHM = irange(0xB000, 0x1000 // 4, 4), Register32
+ LN0_AUSPMA_RX_SHM_TJ_RXA_CTLE_CTRL0 = 0xB000, R_AUSPMA_RX_SHM_TJ_RXA_CTLE_CTRL0
+ LN0_AUSPMA_RX_SHM_TJ_RXA_AFE_CTRL1 = 0xB004, R_AUSPMA_RX_SHM_TJ_RXA_AFE_CTRL1
+ LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL2 = 0xB008, Register32
+ LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL3 = 0xB00C, Register32
+ LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL4 = 0xB010, Register32
+ LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL5 = 0xB014, Register32
+ LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL6 = 0xB018, Register32
+ LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL7 = 0xB01C, Register32
+ LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL8 = 0xB020, Register32
+ LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL9 = 0xB024, Register32
+ LN0_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL10 = 0xB028, R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL10
+ LN0_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL11 = 0xB02C, R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL11
+ LN0_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL12 = 0xB030, R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL12
+ LN0_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL13 = 0xB034, R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL13
+ LN0_AUSPMA_RX_SHM_TJ_UNK_CTRL14A = 0xB038, Register32
+ LN0_AUSPMA_RX_SHM_TJ_UNK_CTRL14B = 0xB03C, Register32
+ LN0_AUSPMA_RX_SHM_TJ_UNK_CTRL15A = 0xB040, Register32
+ LN0_AUSPMA_RX_SHM_TJ_UNK_CTRL15B = 0xB044, Register32
+ LN0_AUSPMA_RX_SHM_TJ_RXA_SAVOS_CTRL16 = 0xB048, R_AUSPMA_RX_SHM_TJ_RXA_SAVOS_CTRL16
+ LN0_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL17 = 0xB04C, R_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL17
+ LN0_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL18 = 0xB050, R_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL18
+ LN0_AUSPMA_RX_SHM_TJ_RXA_TERM_CTRL19 = 0xB054, R_AUSPMA_RX_SHM_TJ_RXA_TERM_CTRL19
+ LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL20 = 0xB058, Register32
+ LN0_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL21 = 0xB05C, Register32
+ LN0_AUSPMA_RX_SHM_TJ_RXA_VREF_CTRL22 = 0xB060, R_AUSPMA_RX_SHM_TJ_RXA_VREF_CTRL22
+
+ LN0_AUSPMA_TX_TOP = irange(0xC000, 0x1000 // 4, 4), Register32
+
+ LN0_AUSPMA_TX_SHM = irange(0xD000, 0x1000 // 4, 4), Register32
+ LN0_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG0 = 0xD000, R_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG0
+ LN0_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG1 = 0xD004, R_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG1
+ LN0_AUSPMA_TX_SHM_TXA_IMP_REG0 = 0xD008, R_AUSPMA_TX_SHM_TXA_IMP_REG0
+ LN0_AUSPMA_TX_SHM_TXA_IMP_REG1 = 0xD00C, Register32
+ LN0_AUSPMA_TX_SHM_TXA_IMP_REG2 = 0xD010, R_AUSPMA_TX_SHM_TXA_IMP_REG2
+ LN0_AUSPMA_TX_SHM_TXA_IMP_REG3 = 0xD014, R_AUSPMA_TX_SHM_TXA_IMP_REG3
+ LN0_AUSPMA_TX_SHM_TXA_UNK_REG0 = 0xD018, Register32
+ LN0_AUSPMA_TX_SHM_TXA_UNK_REG1 = 0xD01C, Register32
+ LN0_AUSPMA_TX_SHM_TXA_UNK_REG2 = 0xD020, Register32
+ LN0_AUSPMA_TX_SHM_TXA_LDOCLK = 0xD024, R_AUSPMA_TX_SHM_TXA_LDOCLK
+
+ LN1_AUSPMA_RX_TOP = irange(0x10000, 0x1000 // 4, 4), Register32
+ LN1_AUSPMA_RX_TOP_TJ_CFG_RX_TXMODE = 0x100F0, R_AUSPMA_RX_TOP_TJ_CFG_RX_TXMODE
+
+ LN1_AUSPMA_RX_EQ = irange(0x11000, 0x1000 // 4, 4), Register32
+
+ LN1_AUSPMA_RX_SHM = irange(0x12000, 0x1000 // 4, 4), Register32
+ LN1_AUSPMA_RX_SHM_TJ_RXA_CTLE_CTRL0 = 0x12000, R_AUSPMA_RX_SHM_TJ_RXA_CTLE_CTRL0
+ LN1_AUSPMA_RX_SHM_TJ_RXA_AFE_CTRL1 = 0x12004, R_AUSPMA_RX_SHM_TJ_RXA_AFE_CTRL1
+ LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL2 = 0x12008, Register32
+ LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL3 = 0x1200C, Register32
+ LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL4 = 0x12010, Register32
+ LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL5 = 0x12014, Register32
+ LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL6 = 0x12018, Register32
+ LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL7 = 0x1201C, Register32
+ LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL8 = 0x12020, Register32
+ LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL9 = 0x12024, Register32
+ LN1_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL10 = 0x12028, R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL10
+ LN1_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL11 = 0x1202C, R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL11
+ LN1_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL12 = 0x12030, R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL12
+ LN1_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL13 = 0x12034, R_AUSPMA_RX_SHM_TJ_RXA_DFE_CTRL13
+ LN1_AUSPMA_RX_SHM_TJ_UNK_CTRL14A = 0x12038, Register32
+ LN1_AUSPMA_RX_SHM_TJ_UNK_CTRL14B = 0x1203C, Register32
+ LN1_AUSPMA_RX_SHM_TJ_UNK_CTRL15A = 0x12040, Register32
+ LN1_AUSPMA_RX_SHM_TJ_UNK_CTRL15B = 0x12044, Register32
+ LN1_AUSPMA_RX_SHM_TJ_RXA_SAVOS_CTRL16 = 0x12048, R_AUSPMA_RX_SHM_TJ_RXA_SAVOS_CTRL16
+ LN1_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL17 = 0x1204C, R_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL17
+ LN1_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL18 = 0x12050, R_AUSPMA_RX_SHM_TJ_RXA_TX_CTRL18
+ LN1_AUSPMA_RX_SHM_TJ_RXA_TERM_CTRL19 = 0x12054, R_AUSPMA_RX_SHM_TJ_RXA_TERM_CTRL19
+ LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL20 = 0x12058, Register32
+ LN1_AUSPMA_RX_SHM_TJ_RXA_UNK_CTRL21 = 0x1205C, Register32
+ LN1_AUSPMA_RX_SHM_TJ_RXA_VREF_CTRL22 = 0x12060, R_AUSPMA_RX_SHM_TJ_RXA_VREF_CTRL22
+
+ LN1_AUSPMA_TX_TOP = irange(0x13000, 0x1000 // 4, 4), Register32
+
+ LN1_AUSPMA_TX_SHM = irange(0x14000, 0x1000 // 4, 4), Register32
+ LN1_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG0 = 0x14000, R_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG0
+ LN1_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG1 = 0x14004, R_AUSPMA_TX_SHM_TXA_CFG_MAIN_REG1
+ LN1_AUSPMA_TX_SHM_TXA_IMP_REG0 = 0x14008, R_AUSPMA_TX_SHM_TXA_IMP_REG0
+ LN1_AUSPMA_TX_SHM_TXA_IMP_REG1 = 0x1400C, Register32
+ LN1_AUSPMA_TX_SHM_TXA_IMP_REG2 = 0x14010, R_AUSPMA_TX_SHM_TXA_IMP_REG2
+ LN1_AUSPMA_TX_SHM_TXA_IMP_REG3 = 0x14014, R_AUSPMA_TX_SHM_TXA_IMP_REG3
+ LN1_AUSPMA_TX_SHM_TXA_UNK_REG0 = 0x14018, Register32
+ LN1_AUSPMA_TX_SHM_TXA_UNK_REG1 = 0x1401C, Register32
+ LN1_AUSPMA_TX_SHM_TXA_UNK_REG2 = 0x14020, Register32
+ LN1_AUSPMA_TX_SHM_TXA_LDOCLK = 0x14024, R_AUSPMA_TX_SHM_TXA_LDOCLK
+
+ ACIOPHY_TOP_TUNABLE_118 = 0x118, Register32
+ ACIOPHY_TOP_TUNABLE_11c = 0x11C, Register32
+ ACIOPHY_TOP_TUNABLE_124 = 0x124, Register32
+ ACIOPHY_TOP_TUNABLE_a00 = 0xA00, Register32
+ ACIOPHY_TOP_TUNABLE_808 = 0x808, Register32
+
+ AUSPLL_TOP_FREQ_DESC_0A = 0x2080, Register32
+ AUSPLL_TOP_FREQ_DESC_0B = 0x2084, Register32
+ AUSPLL_TOP_FREQ_DESC_0C = 0x2088, Register32
+
+ AUSPLL_TOP_TUNABLE_2094 = 0x2094, Register32
+ AUSPLL_TOP_TUNABLE_20a0 = 0x20A0, Register32
+ AUSPLL_TOP_TUNABLE_20ac = 0x20AC, Register32
+ AUSPLL_TOP_TUNABLE_20b8 = 0x20B8, Register32
+
+ CIO3PLL_TOP_TUNABLE_10 = 0x2810, Register32
+ CIO3PLL_TOP_TUNABLE_88 = 0x2888, Register32
+ CIO3PLL_TOP_TUNABLE_94 = 0x2894, Register32
+
+ CIO3PLL_CORE_TUNABLE_1c = 0x2A1C, Register32
+ CIO3PLL_CORE_TUNABLE_28 = 0x2A28, Register32
+ CIO3PLL_CORE_TUNABLE_9c = 0x2A9C, Register32
+
+ AUSPLL_CORE_TUNABLE_78 = 0x2278, Register32
diff --git a/tools/proxyclient/m1n1/hw/codecs/__init__.py b/tools/proxyclient/m1n1/hw/codecs/__init__.py
new file mode 100644
index 0000000..46bb5ee
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/codecs/__init__.py
@@ -0,0 +1,109 @@
+from m1n1.utils import RegMap, Register8
+from enum import IntEnum
+from .cs42l84 import *
+
+class E_PWR_MODE(IntEnum):
+ ACTIVE = 0
+ MUTE = 1
+ SHUTDOWN = 2
+
+class R_PWR_CTL(Register8):
+ ISNS_PD = 3
+ VSNS_PD = 2
+ MODE = 1, 0, E_PWR_MODE
+
+class R_PB_CFG0(Register8):
+ PDM_MAP = 7
+ PB_PDM_SRC = 6
+ PB_SRC = 5
+ AMP_LEVEL = 4, 0
+
+class R_PB_CFG2(Register8):
+ DVC_PCM = 7, 0
+
+class R_PB_CFG3(Register8):
+ DVC_PDM = 7, 0
+
+class E_RX_SCFG(IntEnum):
+ I2C_OFFSET = 0b00
+ LEFT = 0b01
+ RIGHT = 0b10
+ DOWNMIX = 0b11
+
+class E_RX_WLEN(IntEnum):
+ W_16BIT = 0b00
+ W_20BIT = 0b01
+ W_24BIT = 0b10
+ W_32BIT = 0b11
+
+class E_RX_SLEN(IntEnum):
+ W_16BIT = 0b00
+ W_24BIT = 0b01
+ W_32BIT = 0b10
+
+class R_TDM_CFG2(Register8):
+ RX_SCFG = 5, 4, E_RX_SCFG
+ RX_WLEN = 3, 2, E_RX_WLEN
+ RX_SLEN = 1, 0, E_RX_SLEN
+
+class R_TDM_CFG3(Register8):
+ RX_SLOT_R = 7, 4
+ RX_SLOT_L = 3, 0
+
+class TAS5770Regs(RegMap):
+ PWR_CTL = 0x002, R_PWR_CTL
+ PB_CFG0 = 0x003, R_PB_CFG0
+ PB_CFG2 = 0x005, R_PB_CFG2
+ PB_CFG3 = 0x006, R_PB_CFG3
+ TDM_CFG2 = 0x00c, R_TDM_CFG2
+ TDM_CFG3 = 0x00d, R_TDM_CFG3
+
+class R_MODE_CTRL(Register8):
+ BOP_SRC = 7
+ ISNS_PD = 3
+ VSNS_PD = 2
+ MODE = 1, 0, E_PWR_MODE
+
+class R_CHNL_0(Register8):
+ CDS_MODE = 7, 6
+ AMP_LEVEL = 5, 1
+
+class R_DVC(Register8):
+ DVC_LVL = 7, 0
+
+class R_INT_MASK0(Register8):
+ BOPM = 7
+ BOPIH = 6
+ LIMMA = 5
+ PBIP = 4
+ LIMA = 3
+ TDMCE = 2
+ OC = 1
+ OT = 0
+
+class R_INT_CLK_CFG(Register8):
+ CLK_ERR_PWR_EN = 7
+ DIS_CLK_HAT = 6
+ CLK_HALT_TIMER = 5, 3
+ IRQZ_CLR = 2
+ IRQZ_PIN_CFG = 1, 0
+
+class SN012776Regs(RegMap):
+ MODE_CTRL = 0x002, R_MODE_CTRL
+ CHNL_0 = 0x003, R_CHNL_0
+ DVC = 0x01a, R_DVC
+
+ INT_MASK0 = 0x03b, R_INT_MASK0
+ INT_MASK1 = 0x03c, Register8
+ INT_MASK2 = 0x040, Register8
+ INT_MASK3 = 0x041, Register8
+ INT_MASK4 = 0x03d, Register8
+
+ INT_LTCH0 = 0x049, R_INT_MASK0
+ INT_LTCH1 = 0x04a, Register8
+ INT_LTCH1_0 = 0x04b, Register8
+ INT_LTCH2 = 0x04f, Register8
+ INT_LTCH3 = 0x050, Register8
+ INT_LTCH4 = 0x051, Register8
+
+ INT_CLK_CFG = 0x05c, R_INT_CLK_CFG
diff --git a/tools/proxyclient/m1n1/hw/codecs/cs42l84.py b/tools/proxyclient/m1n1/hw/codecs/cs42l84.py
new file mode 100644
index 0000000..095b421
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/codecs/cs42l84.py
@@ -0,0 +1,365 @@
+from m1n1.utils import Register8, Register16, Register32, RegMap, irange
+from enum import IntEnum
+
+class R_IRQ_MASK1(Register8):
+ RING_PLUG = 0
+ RING_UNPLUG = 1
+ TIP_PLUG = 2
+ TIP_UNPLUG = 3
+
+class R_IRQ_MASK3(Register8):
+ HSDET_AUTO_DONE = 7
+
+class E_DCID_GND_SEL(IntEnum):
+ NONE = 0
+ HS3 = 1
+ HS4 = 2
+
+class E_DCID_Z_RANGE(IntEnum):
+ NONE = 0
+ UNK2 = 2
+ UNK3 = 3
+
+class R_DCID_CTRL1(Register8):
+ Z_RANGE = 2, 0, E_DCID_Z_RANGE
+
+class R_DCID_CTRL2(Register8):
+ GND_SEL = 6, 4, E_DCID_GND_SEL
+
+class R_DCID_CTRL3(Register8):
+ START = 0
+
+class R_DCID_STATUS(Register32):
+ OVERALL = 9, 0
+ DONE = 10
+ U = 20, 11
+ D = 30, 21
+
+class E_DEBOUNCE_TIME(IntEnum):
+ T_0MS = 0b000
+ T_125MS = 0b001
+ T_250MS = 0b010
+ T_500MS = 0b011
+ T_750MS = 0b100
+ T_1S = 0b101
+
+class R_TR_SENSE_CTRL(Register8):
+ INV = 7
+ UNK1 = 6
+ FALLTIME = 5, 3, E_DEBOUNCE_TIME
+ RISETIME = 2, 0, E_DEBOUNCE_TIME
+
+class R_TR_SENSE_STATUS(Register8):
+ RING_PLUG = 0
+ RING_UNPLUG = 1
+ TIP_PLUG = 2
+ TIP_UNPLUG = 3
+
+class R_HS_DET_STATUS2(Register8):
+ HS_TRUE = 1
+ SHORT_TRUE = 0
+
+class R_MSM_BLOCK_EN1(Register8):
+ pass
+
+class R_MSM_BLOCK_EN2(Register8):
+ ASP_EN = 6
+ BUS_EN = 5
+ DAC_EN = 4
+ ADC_EN = 3
+
+class R_MSM_BLOCK_EN3(Register8):
+ TR_SENSE_EN = 3
+ DCID_EN = 4
+
+class R_HS_CLAMP_DISABLE(Register8):
+ HS_CLAMP_DISABLE = 0
+
+class E_SAMP_RATE(IntEnum):
+ S_16KHZ = 1
+ S_24KHZ = 2
+ S_32KHZ = 3
+ S_48KHZ = 4
+ S_96KHZ = 5
+ S_192KHZ = 6
+ S_22K05HZ = 10
+ S_44K1HZ = 12
+ S_88K2HZ = 13
+ S_176K4HZ = 14
+
+class E_MCLK_SRC(IntEnum):
+ RCO = 0b00
+ MCLK_PIN = 0b01
+ BCLK = 0b10
+ PLL = 0b11
+
+class E_MCLK_FREQ(IntEnum):
+ F_12MHZ = 0b00
+ F_24MHZ = 0b01
+ F_12_288KHZ = 0b10
+ F_24_576KHZ = 0b11
+
+class R_CCM_CTRL1(Register8):
+ MCLK_SRC = 1, 0, E_MCLK_SRC
+ MCLK_FREQ = 3, 2, E_MCLK_FREQ
+
+class E_REFCLK_DIV(IntEnum):
+ DIV1 = 0b00
+ DIV2 = 0b01
+ DIV4 = 0b10
+ DIV8 = 0b11
+
+class R_CCM_CTRL3(Register8):
+ REFCLK_DIV = 2, 1, E_REFCLK_DIV
+ REFCLK_IS_MCLK = 0 # BLCK otherwise
+
+class R_CCM_CTRL4(Register8):
+ REFCLK_EN = 0
+
+class R_CCM_SAMP_RATE(Register8):
+ RATE = 7, 0, E_SAMP_RATE
+
+class E_PLL_MODE(IntEnum):
+ UNSUPP = 0b00
+ BYPASS_512 = 0b01
+ BYPASS_1024 = 0b10
+ BYPASS_BOTH = 0b11
+
+class R_PLL_CTRL(Register8):
+ MODE = 2, 1, E_PLL_MODE
+ EN = 0
+
+class E_WNF_CF(IntEnum):
+ F_UNK = 0b00
+ F_300HZ = 0b11
+
+class R_ADC_CTRL1(Register8):
+ PREAMP_GAIN = 7, 6
+ PGA_GAIN = 5, 0
+
+class R_ADC_CTRL4(Register8): # maybe
+ WNF_CF = 5, 4, E_WNF_CF
+ WNF_EN = 3
+
+class R_DAC_CTRL1(Register8):
+ UNMUTE = 0
+
+ HP_LOAD = 2 # maybe
+ UNK1 = 3
+ UNK2 = 4
+ UNK3 = 5
+ HIGH_V = 6
+
+class E_PULLDOWN_R(IntEnum):
+ NONE = 0x0
+ R_UNK8 = 0x8
+ R_1K1OHMS = 0xc
+
+class R_DAC_CTRL2(Register8):
+ PULLDOWN_R = 3, 0, E_PULLDOWN_R
+
+class R_HP_VOL_CTRL(Register8):
+ ZERO_CROSS = 1
+ SOFT = 0
+
+class E_BUS_SOURCE(IntEnum):
+ EMPTY = 0b0000
+ ADC = 0b0111
+ ASP_RX_CH1 = 0b1101
+ ASP_RX_CH2 = 0b1110
+
+class R_BUS_DAC_SRC(Register8):
+ CHB = 7, 4, E_BUS_SOURCE
+ CHA = 3, 0, E_BUS_SOURCE
+
+class R_BUS_ASP_TX_SRC(Register8):
+ CH2 = 7, 4, E_BUS_SOURCE
+ CH1 = 3, 0, E_BUS_SOURCE
+
+class E_HSBIAS_SENSE_TRIP(IntEnum):
+ C_12UA = 0b000
+ C_23UA = 0b001
+ C_41UA = 0b010
+ C_52UA = 0b011
+ C_64UA = 0b100
+ C_75UA = 0b101
+ C_93UA = 0b110
+ C_104UA = 0b111
+
+class R_HSBIAS_SC_AUTOCTL(Register8):
+ HSBIAS_SENSE_EN = 7
+ AUTO_HSBIAS_HIZ = 6
+ TIP_SENSE_EN = 5
+ SENSE_TRIP = 2, 0, E_HSBIAS_SENSE_TRIP
+
+class E_TIP_SENSE_CTRL(IntEnum):
+ DISABLED = 0b00
+ DIG_INPUT = 0b01
+ SHORT_DET = 0b11
+
+class R_TIP_SENSE_CTRL2(Register8):
+ CTRL = 7, 6, E_TIP_SENSE_CTRL
+ INV = 5
+
+class E_HSBIAS_DET_MODE(IntEnum):
+ DISABLED = 0b00
+ SHORT_DET = 0b01
+ NORMAL = 0b11
+
+class E_HSBIAS_CTRL(IntEnum):
+ HI_Z = 0b00
+ U_0V0 = 0b01
+ U_2V0 = 0b10
+ U_2V7 = 0b11
+
+class R_MISC_DET_CTRL(Register8):
+ UNK1 = 7
+ DETECT_MODE = 4, 3, E_HSBIAS_DET_MODE
+ HSBIAS_CTRL = 2, 1, E_HSBIAS_CTRL
+ PDN_MIC_LVL_DET = 0
+
+class E_S0_DEBOUNCE_TIME(IntEnum):
+ T_10MS = 0b000
+ T_20MS = 0b001
+ T_30MS = 0b010
+ T_40MS = 0b011
+ T_50MS = 0b100
+ T_60MS = 0b101
+ T_70MS = 0b110
+ T_80MS = 0b111
+
+class R_MIC_DET_CTRL2(Register8):
+ DEBOUNCE_TIME = 7, 5, E_S0_DEBOUNCE_TIME
+
+class R_MIC_DET_CTRL4(Register8):
+ LATCH_TO_VP = 1
+
+class R_HS_DET_CTRL2(Register8):
+ CTRL = 7, 6
+ SET = 5, 4
+ REF = 3
+ AUTO_TIME = 1, 0
+
+class R_HS_SWITCH_CTRL(Register8):
+ REF_HS3 = 7
+ REF_HS4 = 6
+ HSB_FILT_HS3 = 5
+ HSB_FILT_HS4 = 4
+ HSB_HS3 = 3
+ HSB_HS4 = 2
+ GNDHS_HS3 = 1
+ GNDHS_HS4 = 0
+
+class R_ASP_CTRL(Register8):
+ TDM_MODE = 2
+ BCLK_EN = 1
+
+class R_ASP_FSYNC_CTRL23(Register16):
+ BCLK_PERIOD = 12, 1
+
+class R_ASP_TX_HIZ_DLY_CTRL(Register8):
+ DRV_Z = 5, 4
+ HIZ_DELAY = 3, 2
+ FS = 1
+ UNK1 = 0
+
+class R_ASP_RX_EN(Register8):
+ CH2_EN = 1
+ CH1_EN = 0
+
+class R_ASP_CH_CTRL(Register32):
+ WIDTH = 23, 16
+ SLOT_START = 10, 1
+ EDGE = 0 # set for rising edge
+
+class CS42L84Regs(RegMap):
+ DEVID = irange(0x0, 5), Register8
+ FREEZE = 0x6, Register8
+
+ SW_RESET = 0x203, Register8
+
+ IRQ_STATUS1 = 0x400, R_IRQ_MASK1
+ IRQ_STATUS2 = 0x401, Register8
+ IRQ_STATUS3 = 0x402, R_IRQ_MASK3
+ PLL_LOCK_STATUS = 0x40e, Register8 # bit 0x10
+
+ IRQ_MASK1 = 0x418, R_IRQ_MASK1
+ IRQ_MASK2 = 0x419, Register8
+ IRQ_MASK3 = 0x41a, R_IRQ_MASK3
+
+ CCM_CTRL1 = 0x600, R_CCM_CTRL1
+ CCM_SAMP_RATE = 0x601, R_CCM_SAMP_RATE
+ CCM_CTRL3 = 0x602, R_CCM_CTRL3
+ CCM_CTRL4 = 0x603, R_CCM_CTRL4
+ CCM_ASP_CLK_CTRL = 0x608, Register8
+
+ PLL_CTRL = 0x800, R_PLL_CTRL
+ PLL_DIV_FRAC = irange(0x804, 3), Register8
+ PLL_DIV_INT = 0x807, Register8
+ PLL_DIVOUT = 0x808, Register8
+
+ DCID_CTRL1 = 0x1200, R_DCID_CTRL1
+ DCID_CTRL2 = 0x1201, R_DCID_CTRL2
+ DCID_CTRL3 = 0x1202, R_DCID_CTRL3
+ DCID_TRIM_OFFSET = 0x1207, Register8
+ DCID_TRIM_SLOPE = 0x120a, Register8
+
+ # R_pull = 1100 - (regval - 128)*2
+ DCID_PULLDOWN_TRIM = 0x120b, Register8
+ DCID_STATUS = 0x120c, R_DCID_STATUS
+
+ # tip/ring sense
+ TR_SENSE_CTRL1 = 0x1280, Register8
+ TR_SENSE_CTRL2 = 0x1281, Register8
+ RING_SENSE_CTRL = 0x1282, R_TR_SENSE_CTRL
+ TIP_SENSE_CTRL = 0x1283, R_TR_SENSE_CTRL
+ TR_SENSE_STATUS = 0x1288, R_TR_SENSE_STATUS
+
+ HSBIAS_SC_AUTOCTL = 0x1470, R_HSBIAS_SC_AUTOCTL
+ WAKE_CTRL = 0x1471, Register8
+ TIP_SENSE_CTRL2 = 0x1473, R_TIP_SENSE_CTRL2
+ MISC_DET_CTRL = 0x1474, R_MISC_DET_CTRL
+ MIC_DET_CTRL2 = 0x1478, R_MIC_DET_CTRL2
+ MIC_DET_CTRL4 = 0x1477, R_MIC_DET_CTRL4
+
+ HS_DET_STATUS1 = 0x147c, Register8
+ HS_DET_STATUS2 = 0x147d, R_HS_DET_STATUS2
+ HS_DET_IRQ_MASK = irange(0x1480, 2), Register8
+ HS_DET_IRQ_STATUS = irange(0x1484, 2), Register8
+
+ MSM_BLOCK_EN1 = 0x1800, R_MSM_BLOCK_EN1
+ MSM_BLOCK_EN2 = 0x1801, R_MSM_BLOCK_EN2
+ MSM_BLOCK_EN3 = 0x1802, R_MSM_BLOCK_EN3
+
+ HS_DET_CTRL1 = 0x1810, Register8
+ HS_DET_CTRL2 = 0x1811, R_HS_DET_CTRL2
+ HS_SWITCH_CTRL = 0x1812, R_HS_SWITCH_CTRL
+ HS_CLAMP_DISABLE = 0x1813, R_HS_CLAMP_DISABLE
+ ADC_CTRL1 = 0x2000, R_ADC_CTRL1
+ ADC_CTRL2 = 0x2001, Register8 # volume
+ ADC_CTRL3 = 0x2002, Register8
+ ADC_CTRL4 = 0x2003, R_ADC_CTRL4
+
+ DAC_CTRL1 = 0x3000, R_DAC_CTRL1
+ DAC_CTRL2 = 0x3001, R_DAC_CTRL2
+ DACA_VOL_LSB = 0x3004, Register8
+ DACA_VOL_MSB = 0x3005, Register8 # sign bit
+ DACB_VOL_LSB = 0x3006, Register8
+ DACB_VOL_MSB = 0x3007, Register8 # sign bit
+ HP_VOL_CTRL = 0x3020, R_HP_VOL_CTRL
+ HP_CLAMP_CTRL = 0x3123, Register8
+
+ BUS_ASP_TX_SRC = 0x4000, R_BUS_ASP_TX_SRC
+ BUS_DAC_SRC = 0x4001, R_BUS_DAC_SRC
+
+ ASP_CTRL = 0x5000, R_ASP_CTRL
+ ASP_FSYNC_CTRL23 = 0x5010, R_ASP_FSYNC_CTRL23
+ ASP_DATA_CTRL = 0x5018, R_ASP_TX_HIZ_DLY_CTRL
+
+ ASP_RX_EN = 0x5020, R_ASP_RX_EN
+ ASP_TX_EN = 0x5024, Register8
+
+ ASP_RX1_CTRL = 0x5028, R_ASP_CH_CTRL # 32bit
+ ASP_RX2_CTRL = 0x502c, R_ASP_CH_CTRL # 32bit
+ ASP_TX1_CTRL = 0x5068, R_ASP_CH_CTRL
+ ASP_TX2_CTRL = 0x506c, R_ASP_CH_CTRL
diff --git a/tools/proxyclient/m1n1/hw/dart.py b/tools/proxyclient/m1n1/hw/dart.py
new file mode 100644
index 0000000..cd3d8d7
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/dart.py
@@ -0,0 +1,102 @@
+# SPDX-License-Identifier: MIT
+
+import struct
+
+from enum import IntEnum
+from ..utils import *
+from ..malloc import Heap
+
+from .dart8020 import DART8020, DART8020Regs
+from .dart8110 import DART8110, DART8110Regs
+
+__all__ = ["DART"]
+
+class DART(Reloadable):
+ PAGE_BITS = 14
+ PAGE_SIZE = 1 << PAGE_BITS
+
+ def __init__(self, iface, regs, util=None, compat="dart,t8020", iova_range=(0x80000000, 0x90000000)):
+ self.iface = iface
+ self.iova_allocator = [Heap(iova_range[0], iova_range[1], self.PAGE_SIZE)
+ for i in range(16)]
+ if compat in ["dart,t8020", "dart,t6000"]:
+ self.dart = DART8020(iface, regs, util, compat)
+ elif compat in ["dart,t8110"]:
+ self.dart = DART8110(iface, regs, util)
+ else:
+ raise TypeError(compat)
+
+ @classmethod
+ def from_adt(cls, u, path, instance=0, **kwargs):
+ dart_addr = u.adt[path].get_reg(instance)[0]
+ compat = u.adt[path].compatible[0]
+ if compat in ["dart,t8020", "dart,t6000"]:
+ regs = DART8020Regs(u, dart_addr)
+ elif compat in ["dart,t8110"]:
+ regs = DART8110Regs(u, dart_addr)
+ return cls(u.iface, regs, u, compat, **kwargs)
+
+ def ioread(self, stream, base, size):
+ if size == 0:
+ return b""
+
+ ranges = self.iotranslate(stream, base, size)
+
+ iova = base
+ data = []
+ for addr, size in ranges:
+ if addr is None:
+ raise Exception(f"Unmapped page at iova {iova:#x}")
+ data.append(self.iface.readmem(addr, size))
+ iova += size
+
+ return b"".join(data)
+
+ def iowrite(self, stream, base, data):
+ if len(data) == 0:
+ return
+
+ ranges = self.iotranslate(stream, base, len(data))
+
+ iova = base
+ p = 0
+ for addr, size in ranges:
+ if addr is None:
+ raise Exception(f"Unmapped page at iova {iova:#x}")
+ self.iface.writemem(addr, data[p:p + size])
+ p += size
+ iova += size
+
+ def iomap(self, stream, addr, size):
+ iova = self.iova_allocator[stream].malloc(size)
+
+ self.iomap_at(stream, iova, addr, size)
+ return iova
+
+ def iomap_at(self, stream, iova, addr, size):
+ self.dart.iomap_at(stream, iova, addr, size)
+
+ def iotranslate(self, stream, start, size):
+ return self.dart.iotranslate(stream, start, size)
+
+ def initialize(self):
+ self.dart.initialize()
+
+ def show_error(self):
+ self.dart.show_error()
+
+ def invalidate_streams(self, streams=0xffffffff):
+ self.dart.invalidate_streams(streams)
+
+ def invalidate_cache(self):
+ self.dart.invalidate_cache()
+
+ def dump_device(self, idx):
+ self.dart.dump_device(idx)
+
+ def dump_all(self):
+ for i in range(16):
+ self.dump_device(i)
+
+ def dump_params(self):
+ self.dart.dump_params()
diff --git a/tools/proxyclient/m1n1/hw/dart8020.py b/tools/proxyclient/m1n1/hw/dart8020.py
new file mode 100644
index 0000000..9954d3c
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/dart8020.py
@@ -0,0 +1,381 @@
+# SPDX-License-Identifier: MIT
+
+import struct
+
+from enum import IntEnum
+from ..utils import *
+from ..malloc import Heap
+
+__all__ = ["DART8020Regs", "DART8020"]
+
+class R_ERROR(Register32):
+ FLAG = 31
+ STREAM = 27, 24
+ CODE = 23, 0
+ NO_DAPF_MATCH = 11
+ WRITE = 10
+ SUBPAGE_PROT = 7
+ PTE_READ_FAULT = 6
+ READ_FAULT = 4
+ WRITE_FAULT = 3
+ NO_PTE = 2
+ NO_PMD = 1
+ NO_TTBR = 0
+
+class R_STREAM_COMMAND(Register32):
+ INVALIDATE = 20
+ BUSY = 2
+
+class R_TCR(Register32):
+ BYPASS_DAPF = 12
+ BYPASS_DART = 8
+ TRANSLATE_ENABLE = 7
+
+class R_TTBR(Register32):
+ VALID = 31
+ ADDR = 30, 0
+
+class R_REMAP(Register32):
+ MAP3 = 31, 24
+ MAP2 = 23, 16
+ MAP1 = 15, 8
+ MAP0 = 7, 0
+
+class PTE_T8020(Register64):
+ SP_START = 63, 52
+ SP_END = 51, 40
+ OFFSET = 39, 14
+ SP_PROT_DIS = 1
+ VALID = 0
+
+class PTE_T6000(Register64):
+ SP_START = 63, 52
+ SP_END = 51, 40
+ OFFSET = 39, 10
+ SP_PROT_DIS = 1
+ VALID = 0
+
+class R_CONFIG(Register32):
+ LOCK = 15
+
+class R_DAPF_LOCK(Register32):
+ LOCK = 0
+
+class DART8020Regs(RegMap):
+ STREAM_COMMAND = 0x20, R_STREAM_COMMAND
+ STREAM_SELECT = 0x34, Register32
+ ERROR = 0x40, R_ERROR
+ ERROR_ADDR_LO = 0x50, Register32
+ ERROR_ADDR_HI = 0x54, Register32
+ CONFIG = 0x60, R_CONFIG
+ REMAP = irange(0x80, 4, 4), R_REMAP
+
+ DAPF_LOCK = 0xf0, R_DAPF_LOCK
+ UNK1 = 0xf8, Register32
+ ENABLED_STREAMS = 0xfc, Register32
+
+ TCR = irange(0x100, 16, 4), R_TCR
+ TTBR = (irange(0x200, 16, 16), range(0, 16, 4)), R_TTBR
+
+PTE_TYPES = {
+ "dart,t8020": PTE_T8020,
+ "dart,t6000": PTE_T6000,
+}
+
+class DART8020(Reloadable):
+ PAGE_BITS = 14
+ PAGE_SIZE = 1 << PAGE_BITS
+
+ L0_SIZE = 4 # TTBR count
+ L0_OFF = 36
+ L1_OFF = 25
+ L2_OFF = 14
+
+ IDX_BITS = 11
+ Lx_SIZE = (1 << IDX_BITS)
+ IDX_MASK = Lx_SIZE - 1
+
+ def __init__(self, iface, regs, util=None, compat="dart,t8020"):
+ self.iface = iface
+ self.regs = regs
+ self.u = util
+ self.pt_cache = {}
+ self.enabled_streams = regs.ENABLED_STREAMS.val
+ self.ptecls = PTE_TYPES[compat]
+
+ @classmethod
+ def from_adt(cls, u, path, instance=0, **kwargs):
+ dart_addr = u.adt[path].get_reg(instance)[0]
+ dart = cls(u.iface, dart_addr, u)
+ dart.ptecls = PTE_TYPES[u.adt[path].compatible[0]]
+ return dart
+
+ def iomap_at(self, stream, iova, addr, size):
+ if size == 0:
+ return
+
+ if not (self.enabled_streams & (1 << stream)):
+ self.enabled_streams |= (1 << stream)
+ self.regs.ENABLED_STREAMS.val |= self.enabled_streams
+
+ tcr = self.regs.TCR[stream].reg
+
+ if tcr.BYPASS_DART and not tcr.TRANSLATE_ENABLE:
+ raise Exception("Stream is bypassed in DART")
+
+ if tcr.BYPASS_DART or not tcr.TRANSLATE_ENABLE:
+ raise Exception(f"Unknown DART mode {tcr}")
+
+ if addr & (self.PAGE_SIZE - 1):
+ raise Exception(f"Unaligned PA {addr:#x}")
+
+ if iova & (self.PAGE_SIZE - 1):
+ raise Exception(f"Unaligned IOVA {iova:#x}")
+
+ start_page = align_down(iova, self.PAGE_SIZE)
+ end = iova + size
+ end_page = align_up(end, self.PAGE_SIZE)
+
+ dirty = set()
+
+ for page in range(start_page, end_page, self.PAGE_SIZE):
+ paddr = addr + page - start_page
+
+ l0 = page >> self.L0_OFF
+ assert l0 < self.L0_SIZE
+ ttbr = self.regs.TTBR[stream, l0].reg
+ if not ttbr.VALID:
+ l1addr = self.u.memalign(self.PAGE_SIZE, self.PAGE_SIZE)
+ self.pt_cache[l1addr] = [0] * self.Lx_SIZE
+ ttbr.VALID = 1
+ ttbr.ADDR = l1addr >> 12
+ self.regs.TTBR[stream, l0].reg = ttbr
+
+ cached, l1 = self.get_pt(ttbr.ADDR << 12)
+ l1idx = (page >> self.L1_OFF) & self.IDX_MASK
+ l1pte = self.ptecls(l1[l1idx])
+ if not l1pte.VALID:
+ l2addr = self.u.memalign(self.PAGE_SIZE, self.PAGE_SIZE)
+ self.pt_cache[l2addr] = [0] * self.Lx_SIZE
+ l1pte = self.ptecls(
+ OFFSET=l2addr >> self.PAGE_BITS, VALID=1, SP_PROT_DIS=1)
+ l1[l1idx] = l1pte.value
+ dirty.add(ttbr.ADDR << 12)
+ else:
+ l2addr = l1pte.OFFSET << self.PAGE_BITS
+
+ dirty.add(l1pte.OFFSET << self.PAGE_BITS)
+ cached, l2 = self.get_pt(l2addr)
+ l2idx = (page >> self.L2_OFF) & self.IDX_MASK
+ self.pt_cache[l2addr][l2idx] = self.ptecls(
+ SP_START=0, SP_END=0xfff,
+ OFFSET=paddr >> self.PAGE_BITS, VALID=1, SP_PROT_DIS=1).value
+
+ for page in dirty:
+ self.flush_pt(page)
+
+ def iotranslate(self, stream, start, size):
+ if size == 0:
+ return []
+
+ tcr = self.regs.TCR[stream].reg
+
+ if tcr.BYPASS_DART and not tcr.TRANSLATE_ENABLE:
+ return [(start, size)]
+
+ if tcr.BYPASS_DART or not tcr.TRANSLATE_ENABLE:
+ raise Exception(f"Unknown DART mode {tcr}")
+
+ start = start & 0xffffffff
+
+ start_page = align_down(start, self.PAGE_SIZE)
+ start_off = start - start_page
+ end = start + size
+ end_page = align_up(end, self.PAGE_SIZE)
+ end_size = end - (end_page - self.PAGE_SIZE)
+
+ pages = []
+
+ for page in range(start_page, end_page, self.PAGE_SIZE):
+ l0 = page >> self.L0_OFF
+ assert l0 < self.L0_SIZE
+ ttbr = self.regs.TTBR[stream, l0].reg
+ if not ttbr.VALID:
+ pages.append(None)
+ continue
+
+ cached, l1 = self.get_pt(ttbr.ADDR << 12)
+ l1pte = self.ptecls(l1[(page >> self.L1_OFF) & self.IDX_MASK])
+ if not l1pte.VALID and cached:
+ cached, l1 = self.get_pt(ttbr.ADDR << 12, uncached=True)
+ l1pte = self.ptecls(l1[(page >> self.L1_OFF) & self.IDX_MASK])
+ if not l1pte.VALID:
+ pages.append(None)
+ continue
+
+ cached, l2 = self.get_pt(l1pte.OFFSET << self.PAGE_BITS)
+ l2pte = self.ptecls(l2[(page >> self.L2_OFF) & self.IDX_MASK])
+ if not l2pte.VALID and cached:
+ cached, l2 = self.get_pt(l1pte.OFFSET << self.PAGE_BITS, uncached=True)
+ l2pte = self.ptecls(l2[(page >> self.L2_OFF) & self.IDX_MASK])
+ if not l2pte.VALID:
+ pages.append(None)
+ continue
+
+ pages.append(l2pte.OFFSET << self.PAGE_BITS)
+
+ ranges = []
+
+ for page in pages:
+ if not ranges:
+ ranges.append((page, self.PAGE_SIZE))
+ continue
+ laddr, lsize = ranges[-1]
+ if ((page is None and laddr is None) or
+ (page is not None and laddr == (page - lsize))):
+ ranges[-1] = laddr, lsize + self.PAGE_SIZE
+ else:
+ ranges.append((page, self.PAGE_SIZE))
+
+ ranges[-1] = (ranges[-1][0], ranges[-1][1] - self.PAGE_SIZE + end_size)
+
+ if start_off:
+ ranges[0] = (ranges[0][0] + start_off if ranges[0][0] else None,
+ ranges[0][1] - start_off)
+
+ return ranges
+
+ def get_pt(self, addr, uncached=False):
+ cached = True
+ if addr not in self.pt_cache or uncached:
+ cached = False
+ self.pt_cache[addr] = list(
+ struct.unpack(f"<{self.Lx_SIZE}Q", self.iface.readmem(addr, self.PAGE_SIZE)))
+
+ return cached, self.pt_cache[addr]
+
+ def flush_pt(self, addr):
+ assert addr in self.pt_cache
+ self.iface.writemem(addr, struct.pack(f"<{self.Lx_SIZE}Q", *self.pt_cache[addr]))
+
+ def initialize(self):
+ for i in range(15):
+ self.regs.TCR[i].reg = R_TCR(TRANSLATE_ENABLE=1)
+ self.regs.TCR[15].reg = R_TCR(BYPASS_DART=1)
+
+ for i in range(16):
+ for j in range(4):
+ self.regs.TTBR[i, j].reg = R_TTBR(VALID = 0)
+
+ self.regs.ERROR.val = 0xffffffff
+ self.regs.UNK1.val = 0
+ self.regs.ENABLED_STREAMS.val = 0
+ self.enabled_streams = 0
+
+ self.invalidate_streams()
+
+ def show_error(self):
+ if self.regs.ERROR.reg.FLAG:
+ print(f"ERROR: {self.regs.ERROR.reg!s}")
+ print(f"ADDR: {self.regs.ERROR_ADDR_HI.val:#x}:{self.regs.ERROR_ADDR_LO.val:#x}")
+ self.regs.ERROR.val = 0xffffffff
+
+ def invalidate_streams(self, streams=0xffffffff):
+ self.regs.STREAM_SELECT.val = streams
+ self.regs.STREAM_COMMAND.val = R_STREAM_COMMAND(INVALIDATE=1)
+ while self.regs.STREAM_COMMAND.reg.BUSY:
+ pass
+
+ def invalidate_cache(self):
+ self.pt_cache = {}
+
+ def dump_table2(self, base, l1_addr):
+
+ def print_block(base, pte, start, last):
+ pgcount = last - start
+ pte.OFFSET -= pgcount
+ print(" page (%4d): %08x ... %08x -> %016x [%d%d]" % (
+ start, base + start*0x4000, base + (start+1)*0x4000,
+ pte.OFFSET << self.PAGE_BITS, pte.SP_PROT_DIS, pte.VALID))
+ if start < last:
+ print(" ==> (%4d): ... %08x -> %016x size: %08x" % (
+ last, base + (last+1)*0x4000,
+ (pte.OFFSET + pgcount - 1) << self.PAGE_BITS, pgcount << self.PAGE_BITS))
+
+ cached, tbl = self.get_pt(l1_addr)
+
+ unmapped = False
+ start = 0
+ next_pte = self.ptecls(VALID=0)
+
+ for i, pte in enumerate(tbl):
+ pte = self.ptecls(pte)
+ if not pte.VALID:
+ if not unmapped:
+ if next_pte.VALID:
+ print_block(base, next_pte, start, i)
+ print(" ...")
+ unmapped = True
+ next_pte = pte
+ continue
+
+ unmapped = False
+
+ if int(pte) != int(next_pte):
+ if next_pte.VALID:
+ print_block(base, next_pte, start, i)
+ start = i
+
+ next_pte = pte
+ next_pte.OFFSET += 1
+
+ if next_pte.VALID:
+ print_block(base, next_pte, start, 2048)
+
+ def dump_table(self, base, l1_addr):
+ cached, tbl = self.get_pt(l1_addr)
+
+ unmapped = False
+ for i, pte in enumerate(tbl):
+ pte = self.ptecls(pte)
+ if not pte.VALID:
+ if not unmapped:
+ print(" ...")
+ unmapped = True
+ continue
+
+ unmapped = False
+
+ print(" table (%d): %08x ... %08x -> %016x [%d%d]" % (
+ i, base + i*0x2000000, base + (i+1)*0x2000000,
+ pte.OFFSET << self.PAGE_BITS, pte.SP_PROT_DIS, pte.VALID))
+ self.dump_table2(base + i*0x2000000, pte.OFFSET << self.PAGE_BITS)
+
+ def dump_ttbr(self, idx, ttbr):
+ if not ttbr.VALID:
+ return
+
+ l1_addr = (ttbr.ADDR) << 12
+ print(" TTBR%d: %09x" % (idx, l1_addr))
+
+ self.dump_table(0, l1_addr)
+
+ def dump_device(self, idx):
+ tcr = self.regs.TCR[idx].reg
+ ttbrs = self.regs.TTBR[idx, :]
+ print(f"dev {idx:02x}: TCR={tcr!s} TTBRs = [{', '.join(map(str, ttbrs))}]")
+
+ if tcr.TRANSLATE_ENABLE and tcr.BYPASS_DART:
+ print(" mode: INVALID")
+ elif tcr.TRANSLATE_ENABLE:
+ print(" mode: TRANSLATE")
+
+ for idx, ttbr in enumerate(ttbrs):
+ self.dump_ttbr(idx, ttbr.reg)
+ elif tcr.BYPASS_DART:
+ print(" mode: BYPASS")
+ else:
+ print(" mode: UNKNOWN")
+
+ def dump_params(self):
+ pass
diff --git a/tools/proxyclient/m1n1/hw/dart8110.py b/tools/proxyclient/m1n1/hw/dart8110.py
new file mode 100644
index 0000000..1655182
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/dart8110.py
@@ -0,0 +1,541 @@
+# SPDX-License-Identifier: MIT
+
+import struct
+
+from enum import IntEnum
+from ..utils import *
+from ..malloc import Heap
+
+__all__ = ["DART8110Regs", "DART8110"]
+
+class R_PARAMS_0(Register32):
+ CLIENT_PARTITIONS_SUPPORTED = 29
+ LOG2_PGSZ = 27, 24
+ LOG2_TE_COUNT = 22, 20
+ TLB_SET_COUNT = 11, 0
+
+class R_PARAMS_4(Register32):
+ LOG2_NUM_WAYS = 30, 28
+ NUM_ASCS = 25, 24
+ NUM_W_PORTS = 22, 20
+ NUM_R_PORTS = 18, 16
+ NUM_APFS = 15, 8
+ SUPPORT_STT_PREFETCH = 6
+ SUPPORT_TLB_PREFETCH = 5
+ SUPPORT_CTC_PREFETCH = 4
+ SUPPORT_HW_FLUSH = 3
+ SUPPORT_TZ_TAGGER = 2
+ SUPPORT_REG_LOCK = 1
+ SUPPORT_FULL_BYPASS = 0
+
+class R_PARAMS_8(Register32):
+ PA_WIDTH = 29, 24
+ VA_WIDTH = 21, 16
+ VERS_MAJ = 15, 8
+ VERS_MIN = 7, 0
+
+class R_PARAMS_C(Register32):
+ NUM_CLIENTS = 24, 16
+ NUM_SIDS = 8, 0
+
+class R_ERROR(Register32):
+ FLAG = 31
+ SMMU = 30
+ REGION_PROTECT = 29
+ WRITE_nREAD = 28
+ SID = 27, 20
+ SECONDARY = 19
+ FILL_REGION = 18
+ BPF_REJECT = 14
+ EXTERNAL = 13
+ STT_FLUSH = 12
+ STT_MISMATCH = 11
+ APF_REJECT = 10
+ DROP_PROTECT = 9
+ CTRR_WRITE_PROTECT = 8
+ AXI_ERROR = 7
+ AXI_DECODE = 6
+ READ_FAULT = 5
+ WRITE_FAULT = 4
+ NO_PTE = 3
+ NO_PMD = 2 # "STE"
+ NO_PGD = 1 # "CTE"
+ NO_TTBR = 0
+
+class R_TLB_OP(Register32):
+ BUSY = 31
+
+ # None of these bits are supported on hwrev 1
+ HARDWARE_FLUSH = 30
+ FLUSH_VA_RANGE = 14
+ ENABLE_STT_FLUSH = 13
+ DISABLE_STC_FLUSH = 12
+
+ # 0 = flush all
+ # 1 = flush SID
+ # 2 = TLB read
+ # 3 = TLB write????
+ # 4 = flush unlock, definitely not supported on hwrev 1
+ OP = 10, 8
+ STREAM = 7, 0
+
+class R_TLB_OP_IDX(Register32):
+ SET = 13, 8
+ WAY = 6, 4
+ TE = 2, 0
+
+class R_PROTECT(Register32):
+ LOCK_TZ_SELECT = 4
+ LOCK_TZ_CONFIG = 3
+ # This bit can be set, but unknown what it protects
+ _BIT2 = 2
+ LOCK_REG_4xx = 1
+ LOCK_TCR_TTBR = 0
+
+class R_DIAG_LOCK(Register32):
+ # FIXME: how does this work exactly?
+ LOCK_ON_ERR = 1
+ LOCK = 0
+
+class R_TCR(Register32):
+ REMAP = 11, 8
+ REMAP_EN = 7
+ FOUR_LEVELS = 3 # not supported on hwrev 1
+ BYPASS_DAPF = 2
+ BYPASS_DART = 1
+ TRANSLATE_ENABLE = 0
+
+class R_TTBR(Register32):
+ ADDR = 29, 2
+ VALID = 0
+
+class PTE(Register64):
+ SP_START = 63, 52
+ SP_END = 51, 40
+ OFFSET = 37, 10
+ RDPROT = 3
+ WRPROT = 2
+ UNCACHABLE = 1
+ VALID = 0
+
+class DART8110Regs(RegMap):
+ PARAMS_0 = 0x000, R_PARAMS_0
+ PARAMS_4 = 0x004, R_PARAMS_4
+ PARAMS_8 = 0x008, R_PARAMS_8
+ PARAMS_C = 0x00C, R_PARAMS_C
+ # Unknown RO
+ REG_0x10 = 0x010, Register32
+ REG_0x14 = 0x014, Register32 # hwrev 2 only
+
+ TLB_OP = 0x080, R_TLB_OP
+ TLP_OP_IDX = 0x084, R_TLB_OP_IDX
+ TLB_TAG_LO = 0x088, Register32
+ TLB_TAG_HI = 0x08c, Register32 # hwrev 2 only
+ TLB_PA_LO = 0x090, Register32
+ TLB_PA_HI = 0x094, Register32
+ TLB_START_DVA_PAGE = 0x098, Register32 # hwrev 2 only
+ TLB_END_DVA_PAGE = 0x0a0, Register32 # hwrev 2 only
+
+ ERROR = 0x100, R_ERROR
+ ERROR_DISABLE = 0x104, R_ERROR
+
+ # Found via register bruteforcing
+ STREAM_UNK_SET = irange(0x120, 8, 4), Register32
+ STREAM_UNK_CLR = irange(0x140, 8, 4), Register32
+
+ # these are all accessed by error interrupt handler
+ REG_0x160 = 0x160, Register32
+ REG_0x164 = 0x164, Register32
+ ERROR_ADDR_LO = 0x170, Register32
+ ERROR_ADDR_HI = 0x174, Register32
+ REG_0x178 = 0x178, Register32 # hwrev 2 only
+ REG_0x180 = irange(0x180, 4, 4), Register32
+ REG_0x1a0 = irange(0x1a0, 8, 4), Register32
+ ERR_SECONDARY = irange(0x1c0, 8, 4), Register32
+
+ # Write bits to _PROTECT to protect them.
+ # They can be unprotected by writing to _UNPROTECT unless _LOCK is written.
+ # If _LOCK is written, protection can be enabled but not disabled.
+ REG_PROTECT = 0x200, R_PROTECT
+ REG_UNPROTECT = 0x204, R_PROTECT
+ REG_PROTECT_LOCK = 0x208, R_PROTECT
+
+ # Tunables touch this, can set bits FF00001F, RW
+ REG_0x20c = 0x20c, Register32
+
+ DIAG_LOCK = 0x210, R_DIAG_LOCK
+
+ # All unknown, related to transaction queueing???
+
+ # can set bits 3FFFFFFC, RW
+ REG_0x218 = 0x218, Register32
+ # Tunables touch this, can set bits 000F0F0F, RW
+ REG_0x220 = 0x220, Register32
+ # Tunables touch this, can set bits 00FFFFFF, RW
+ REG_0x224 = 0x224, Register32
+ # can set bits 3F3F3F3F
+ TLIMIT = 0x228, Register32
+ # can set bits 07070707
+ TEQRESERVE = 0x22c, Register32
+ # RO, outstanding transaction count???
+ TRANS = irange(0x230, 4, 4), Register32
+
+ # hwrev 2 only for all of these
+ REG_0x300 = 0x300, Register32
+ REG_0x308 = 0x308, Register32
+ REG_0x310 = 0x310, Register32
+ REG_0x318 = 0x318, Register32
+ REG_0x320 = 0x320, Register32
+ REG_0x328 = 0x328, Register32
+ REG_0x330 = 0x330, Register32
+ REG_0x338 = 0x338, Register32
+ REG_0x340 = 0x340, Register32
+ REG_0x348 = 0x348, Register32
+ REG_0x350 = 0x350, Register32
+ REG_0x358 = 0x358, Register32
+
+ # Unknown
+ REG_0x400 = 0x400, Register32 # can set 00000003
+ REG_0x404 = 0x404, Register32 # can set 001FFFFF
+ REG_0x408 = 0x408, Register32 # can set 00FFFFFC
+ REG_0x410 = 0x410, Register32 # can set 3FFFFFFC
+
+ # These registers exist even though it's "not supported"
+ TZ_CONFIG = 0x500, Register32 # 3 bits
+ TZ_SELECT = 0x504, Register32 # 1 bit
+ TZ_REGION0_START = 0x508, Register32
+ TZ_REGION0_END = 0x510, Register32
+ TZ_REGION0_OFFSET = 0x518, Register32
+ TZ_REGION1_START = 0x520, Register32
+ TZ_REGION1_END = 0x528, Register32
+ TZ_REGION1_OFFSET = 0x530, Register32
+ TZ_REGION2_START = 0x538, Register32
+ TZ_REGION2_END = 0x540, Register32
+ TZ_REGION2_OFFSET = 0x548, Register32
+
+ # completely guessed, unverified, can set bits 0F077077
+ PERF_INTR_ENABLE = 0x700, Register32
+ PERF_INTR_STATUS = 0x704, Register32
+
+ PERF_UNK1 = irange(0x720, 8, 4), Register32
+ PERF_UNK2 = irange(0x740, 8, 4), Register32
+
+ PERF_TLB_MISS = 0x760, Register32
+ PERF_TLB_FILL = 0x764, Register32
+ PERF_TLB_HIT = 0x768, Register32
+ PERF_ST_MISS = 0x770, Register32
+ PERF_ST_FILL = 0x774, Register32
+ PERF_ST_HIT = 0x778, Register32
+ # hwrev 1 doesn't have these
+ PERF_CTC_MISS = 0x780, Register32
+ PERF_CTC_FILL = 0x784, Register32
+ PERF_CTC_HIT = 0x788, Register32
+
+ UNK_TUNABLES = irange(0x800, 256, 4), Register32
+
+ ENABLE_STREAMS = irange(0xc00, 8, 4), Register32
+ DISABLE_STREAMS = irange(0xc20, 8, 4), Register32
+
+ TCR = irange(0x1000, 256, 4), R_TCR
+ TTBR = irange(0x1400, 256, 4), R_TTBR
+
+
+class DART8110(Reloadable):
+ PAGE_BITS = 14
+ PAGE_SIZE = 1 << PAGE_BITS
+
+ L1_OFF = 25
+ L2_OFF = 14
+
+ IDX_BITS = 11
+ Lx_SIZE = (1 << IDX_BITS)
+ IDX_MASK = Lx_SIZE - 1
+
+ def __init__(self, iface, regs, util=None):
+ self.iface = iface
+ self.regs = regs
+ self.u = util
+ self.pt_cache = {}
+
+ enabled_streams = 0
+ for i in range(8):
+ enabled_streams |= regs.ENABLE_STREAMS[i].val << 32*i
+ self.enabled_streams = enabled_streams
+
+ @classmethod
+ def from_adt(cls, u, path, instance=0, **kwargs):
+ dart_addr = u.adt[path].get_reg(instance)[0]
+ regs = DART8110Regs(u, dart_addr)
+ dart = cls(u.iface, regs, u, **kwargs)
+ return dart
+
+ def iomap_at(self, stream, iova, addr, size):
+ if size == 0:
+ return
+
+ if not (self.enabled_streams & (1 << stream)):
+ self.enabled_streams |= (1 << stream)
+ self.regs.ENABLE_STREAMS[stream // 32].val |= (1 << (stream % 32))
+
+ tcr = self.regs.TCR[stream].reg
+
+ if tcr.BYPASS_DART and not tcr.TRANSLATE_ENABLE:
+ raise Exception("Stream is bypassed in DART")
+
+ if tcr.BYPASS_DART or not tcr.TRANSLATE_ENABLE:
+ raise Exception(f"Unknown DART mode {tcr}")
+
+ if addr & (self.PAGE_SIZE - 1):
+ raise Exception(f"Unaligned PA {addr:#x}")
+
+ if iova & (self.PAGE_SIZE - 1):
+ raise Exception(f"Unaligned IOVA {iova:#x}")
+
+ start_page = align_down(iova, self.PAGE_SIZE)
+ end = iova + size
+ end_page = align_up(end, self.PAGE_SIZE)
+
+ dirty = set()
+
+ for page in range(start_page, end_page, self.PAGE_SIZE):
+ paddr = addr + page - start_page
+
+ ttbr = self.regs.TTBR[stream].reg
+ if not ttbr.VALID:
+ l1addr = self.u.memalign(self.PAGE_SIZE, self.PAGE_SIZE)
+ self.pt_cache[l1addr] = [0] * self.Lx_SIZE
+ ttbr.VALID = 1
+ ttbr.ADDR = l1addr >> self.PAGE_BITS
+ self.regs.TTBR[stream].reg = ttbr
+
+ cached, l1 = self.get_pt(ttbr.ADDR << self.PAGE_BITS)
+ l1idx = (page >> self.L1_OFF) & self.IDX_MASK
+ l1pte = PTE(l1[l1idx])
+ if not l1pte.VALID:
+ l2addr = self.u.memalign(self.PAGE_SIZE, self.PAGE_SIZE)
+ self.pt_cache[l2addr] = [0] * self.Lx_SIZE
+ l1pte = PTE(
+ OFFSET=l2addr >> self.PAGE_BITS, VALID=1)
+ l1[l1idx] = l1pte.value
+ dirty.add(ttbr.ADDR << self.PAGE_BITS)
+ else:
+ l2addr = l1pte.OFFSET << self.PAGE_BITS
+
+ dirty.add(l1pte.OFFSET << self.PAGE_BITS)
+ cached, l2 = self.get_pt(l2addr)
+ l2idx = (page >> self.L2_OFF) & self.IDX_MASK
+ self.pt_cache[l2addr][l2idx] = PTE(
+ SP_START=0, SP_END=0xfff,
+ OFFSET=paddr >> self.PAGE_BITS, VALID=1).value
+
+ for page in dirty:
+ self.flush_pt(page)
+
+ def iotranslate(self, stream, start, size):
+ if size == 0:
+ return []
+
+ tcr = self.regs.TCR[stream].reg
+
+ if tcr.BYPASS_DART and not tcr.TRANSLATE_ENABLE:
+ # FIXME this may not be correct
+ return [(start, size)]
+
+ if tcr.BYPASS_DART or not tcr.TRANSLATE_ENABLE:
+ raise Exception(f"Unknown DART mode {tcr}")
+
+ start = start & 0xfffffffff
+
+ start_page = align_down(start, self.PAGE_SIZE)
+ start_off = start - start_page
+ end = start + size
+ end_page = align_up(end, self.PAGE_SIZE)
+ end_size = end - (end_page - self.PAGE_SIZE)
+
+ pages = []
+
+ for page in range(start_page, end_page, self.PAGE_SIZE):
+ ttbr = self.regs.TTBR[stream].reg
+ if not ttbr.VALID:
+ pages.append(None)
+ continue
+
+ cached, l1 = self.get_pt(ttbr.ADDR << self.PAGE_BITS)
+ l1pte = PTE(l1[(page >> self.L1_OFF) & self.IDX_MASK])
+ if not l1pte.VALID and cached:
+ cached, l1 = self.get_pt(ttbr.ADDR << self.PAGE_BITS, uncached=True)
+ l1pte = PTE(l1[(page >> self.L1_OFF) & self.IDX_MASK])
+ if not l1pte.VALID:
+ pages.append(None)
+ continue
+
+ cached, l2 = self.get_pt(l1pte.OFFSET << self.PAGE_BITS)
+ l2pte = PTE(l2[(page >> self.L2_OFF) & self.IDX_MASK])
+ if not l2pte.VALID and cached:
+ cached, l2 = self.get_pt(l1pte.OFFSET << self.PAGE_BITS, uncached=True)
+ l2pte = PTE(l2[(page >> self.L2_OFF) & self.IDX_MASK])
+ if not l2pte.VALID:
+ pages.append(None)
+ continue
+
+ pages.append(l2pte.OFFSET << self.PAGE_BITS)
+
+ ranges = []
+
+ for page in pages:
+ if not ranges:
+ ranges.append((page, self.PAGE_SIZE))
+ continue
+ laddr, lsize = ranges[-1]
+ if ((page is None and laddr is None) or
+ (page is not None and laddr == (page - lsize))):
+ ranges[-1] = laddr, lsize + self.PAGE_SIZE
+ else:
+ ranges.append((page, self.PAGE_SIZE))
+
+ ranges[-1] = (ranges[-1][0], ranges[-1][1] - self.PAGE_SIZE + end_size)
+
+ if start_off:
+ ranges[0] = (ranges[0][0] + start_off if ranges[0][0] else None,
+ ranges[0][1] - start_off)
+
+ return ranges
+
+ def get_pt(self, addr, uncached=False):
+ cached = True
+ if addr not in self.pt_cache or uncached:
+ cached = False
+ self.pt_cache[addr] = list(
+ struct.unpack(f"<{self.Lx_SIZE}Q", self.iface.readmem(addr, self.PAGE_SIZE)))
+
+ return cached, self.pt_cache[addr]
+
+ def flush_pt(self, addr):
+ assert addr in self.pt_cache
+ self.iface.writemem(addr, struct.pack(f"<{self.Lx_SIZE}Q", *self.pt_cache[addr]))
+
+ def initialize(self):
+ for i in range(15):
+ self.regs.TCR[i].reg = R_TCR(TRANSLATE_ENABLE=1)
+ self.regs.TCR[15].reg = R_TCR(BYPASS_DART=1)
+
+ for i in range(16):
+ self.regs.TTBR[i].reg = R_TTBR(VALID = 0)
+
+ # self.regs.ERROR.val = 0xffffffff
+ # self.regs.UNK1.val = 0
+ self.regs.DISABLE_STREAMS[0].val = 0xffff
+ self.enabled_streams = 0
+
+ self.invalidate_streams()
+
+ def show_error(self):
+ if self.regs.ERROR.reg.FLAG:
+ print(f"ERROR: {self.regs.ERROR.reg!s}")
+ print(f"ADDR: {self.regs.ERROR_ADDR_HI.val:#x}:{self.regs.ERROR_ADDR_LO.val:#x}")
+ self.regs.ERROR.val = 0x80000004
+
+ def invalidate_streams(self, streams=0xffff):
+ for sid in range(256):
+ if streams & (1 << sid):
+ self.regs.TLB_OP.val = R_TLB_OP(STREAM=sid, OP=1)
+ while self.regs.TLB_OP.reg.BUSY:
+ pass
+
+ def invalidate_cache(self):
+ self.pt_cache = {}
+
+ def dump_table2(self, base, l1_addr):
+
+ def print_block(base, pte, start, last):
+ pgcount = last - start
+ pte.OFFSET -= pgcount
+ print(" page (%4d): %09x ... %09x -> %016x [%d%d%d%d]" % (
+ start, base + start*0x4000, base + (start+1)*0x4000,
+ pte.OFFSET << self.PAGE_BITS,
+ pte.RDPROT, pte.WRPROT, pte.UNCACHABLE, pte.VALID))
+ if start < last:
+ print(" ==> (%4d): ... %09x -> %016x size: %08x" % (
+ last, base + (last+1)*0x4000,
+ (pte.OFFSET + pgcount - 1) << self.PAGE_BITS, pgcount << self.PAGE_BITS))
+
+ cached, tbl = self.get_pt(l1_addr)
+
+ unmapped = False
+ start = 0
+ next_pte = PTE(VALID=0)
+
+ for i, pte in enumerate(tbl):
+ pte = PTE(pte)
+ if not pte.VALID:
+ if not unmapped:
+ if next_pte.VALID:
+ print_block(base, next_pte, start, i)
+ print(" ...")
+ unmapped = True
+ next_pte = pte
+ continue
+
+ unmapped = False
+
+ if int(pte) != int(next_pte):
+ if next_pte.VALID:
+ print_block(base, next_pte, start, i)
+ start = i
+
+ next_pte = pte
+ next_pte.OFFSET += 1
+
+ if next_pte.VALID:
+ print_block(base, next_pte, start, 2048)
+
+ def dump_table(self, base, l1_addr):
+ cached, tbl = self.get_pt(l1_addr)
+
+ unmapped = False
+ for i, pte in enumerate(tbl):
+ pte = PTE(pte)
+ if not pte.VALID:
+ if not unmapped:
+ print(" ...")
+ unmapped = True
+ continue
+
+ unmapped = False
+
+ print(" table (%d): %09x ... %09x -> %016x [%d%d%d%d]" % (
+ i, base + i*0x2000000, base + (i+1)*0x2000000,
+ pte.OFFSET << self.PAGE_BITS,
+ pte.RDPROT, pte.WRPROT, pte.UNCACHABLE, pte.VALID))
+ self.dump_table2(base + i*0x2000000, pte.OFFSET << self.PAGE_BITS)
+
+ def dump_ttbr(self, ttbr):
+ if not ttbr.VALID:
+ return
+
+ l1_addr = (ttbr.ADDR) << self.PAGE_BITS
+ print(" TTBR: %011x" % (l1_addr))
+
+ self.dump_table(0, l1_addr)
+
+ def dump_device(self, idx):
+ tcr = self.regs.TCR[idx].reg
+ ttbr = self.regs.TTBR[idx]
+ print(f"dev {idx:02x}: TCR={tcr!s} TTBR = {ttbr!s}")
+
+ if tcr.TRANSLATE_ENABLE and tcr.BYPASS_DART:
+ print(" mode: INVALID")
+ elif tcr.TRANSLATE_ENABLE:
+ print(" mode: TRANSLATE")
+
+ self.dump_ttbr(ttbr.reg)
+ elif tcr.BYPASS_DART:
+ print(" mode: BYPASS")
+ else:
+ print(" mode: UNKNOWN")
+
+ def dump_params(self):
+ print(self.regs.PARAMS_0.reg)
+ print(self.regs.PARAMS_4.reg)
+ print(self.regs.PARAMS_8.reg)
+ print(self.regs.PARAMS_C.reg)
diff --git a/tools/proxyclient/m1n1/hw/dockchannel.py b/tools/proxyclient/m1n1/hw/dockchannel.py
new file mode 100644
index 0000000..1a9443e
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/dockchannel.py
@@ -0,0 +1,120 @@
+# SPDX-License-Identifier: MIT
+import struct
+
+from ..utils import *
+
+__all__ = ["DockChannel"]
+
+# DockChannel layout:
+# 00000 : Global regs
+
+# 08000 : IRQ regs (0)
+# 0c000 : IRQ regs (1)
+# 10000 : IRQ regs (2)
+# 14000 : IRQ regs (3) -> AIC #0
+# 18000 : IRQ regs (4) -> AIC #1
+# 1c000 : IRQ regs (5) (not always present)
+
+# 28000 : FIFO regs (1A)
+# 2c000 : Data regs (1A)
+# 30000 : FIFO regs (1B)
+# 34000 : Data regs (1B)
+# 38000 : FIFO regs (2A)
+# 3c000 : Data regs (2A)
+# 40000 : FIFO regs (2B)
+# 44000 : Data regs (2B)
+# (possibly more)
+
+class R_RX_DATA(Register32):
+ DATA = 31, 8
+ COUNT = 7, 0
+
+class DockChannelIRQRegs(RegMap):
+ IRQ_MASK = 0x0, Register32
+ IRQ_FLAG = 0x4, Register32
+
+class DockChannelConfigRegs(RegMap):
+ TX_THRESH = 0x0, Register32
+ RX_THRESH = 0x4, Register32
+
+class DockChannelDataRegs(RegMap):
+ TX_8 = 0x4, Register32
+ TX_16 = 0x8, Register32
+ TX_24 = 0xc, Register32
+ TX_32 = 0x10, Register32
+ TX_FREE = 0x14, Register32
+ RX_8 = 0x1c, R_RX_DATA
+ RX_16 = 0x20, R_RX_DATA
+ RX_24 = 0x24, R_RX_DATA
+ RX_32 = 0x28, Register32
+ RX_COUNT = 0x2c, Register32
+
+class DockChannel:
+ def __init__(self, u, irq_base, fifo_base, irq_idx):
+ self.u = u
+ self.p = u.proxy
+ self.iface = u.iface
+ self.config = DockChannelConfigRegs(u, fifo_base)
+ self.data = DockChannelDataRegs(u, fifo_base + 0x4000)
+ self.irq = DockChannelIRQRegs(u, irq_base)
+ self.irq_idx = irq_idx
+ self.irq.IRQ_MASK.val = 3 << (irq_idx * 2)
+
+ @property
+ def tx_irq(self):
+ self.irq.IRQ_FLAG.val = 1 << (self.irq_idx * 2)
+ return self.irq.IRQ_FLAG.val & (1 << (self.irq_idx * 2))
+
+ @property
+ def rx_irq(self):
+ self.irq.IRQ_FLAG.val = 2 << (self.irq_idx * 2)
+ return self.irq.IRQ_FLAG.val & (2 << (self.irq_idx * 2))
+
+ @property
+ def rx_count(self):
+ return self.data.RX_COUNT.val
+
+ @property
+ def tx_free(self):
+ return self.data.TX_FREE.val
+
+ def set_tx_thresh(self, v):
+ self.config.TX_THRESH.val = v
+
+ def set_rx_thresh(self, v):
+ self.config.RX_THRESH.val = v
+
+ def write(self, data):
+ p = 0
+ left = len(data)
+ while left >= 4:
+ while self.tx_free < 4:
+ pass
+ d = struct.unpack("<I", data[p:p+4])[0]
+ self.data.TX_32.val = d
+ p += 4
+ left -= 4
+ while left >= 1:
+ while self.tx_free < 1:
+ pass
+ self.data.TX_8.val = data[p]
+ p += 1
+ left -= 1
+
+ def read(self, count):
+ data = []
+ left = count
+ while left >= 4:
+ while self.rx_count < 4:
+ pass
+ data.append(struct.pack("<I", self.data.RX_32.val))
+ left -= 4
+ while left >= 1:
+ while self.rx_count < 1:
+ pass
+ data.append(bytes([self.data.RX_8.DATA]))
+ left -= 1
+ return b"".join(data)
+
+ def read_all(self):
+ return self.read(self.rx_count)
diff --git a/tools/proxyclient/m1n1/hw/dwc3.py b/tools/proxyclient/m1n1/hw/dwc3.py
new file mode 100644
index 0000000..41948f4
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/dwc3.py
@@ -0,0 +1,268 @@
+# SPDX-License-Identifier: MIT
+
+from enum import IntEnum
+from m1n1.utils import *
+
+
+class R_XHCI_USBCMD(Register32):
+ EU3S = 11
+ EWE = 10
+ CRS = 9
+ CSS = 8
+ LHCRST = 7
+ HSEE = 3
+ INTE = 2
+ HCRST = 1
+ RS = 0
+
+
+class R_XHCI_USBSTS(Register32):
+ HCE = 12
+ CNR = 11
+ SRE = 10
+ RSS = 9
+ SSS = 8
+ PCD = 4
+ EINT = 3
+ HSE = 2
+ HCH = 0
+
+
+class R_XHCI_CRCR_LO(Register32):
+ CRP = 31, 6
+ CRR = 3
+ CA = 2
+ CS = 1
+ RCS = 0
+
+
+class R_XHCI_DNCTRL(Register32):
+ N0_N15 = 15, 0
+
+
+class R_XHCI_DOORBELL(Register32):
+ TASK_ID = 31, 16
+ RSVD = 15, 8
+ TARGET = 7, 0
+
+
+class R_XHCI_PORTSC(Register32):
+ CCS = 0
+ PED = 1
+ OCA = 3
+ RESET = 4
+ PLS = 8, 5
+ PP = 9
+ SPEED = 13, 10
+ PIC = 15, 14
+ LWS = 16
+ CSC = 17
+ PEC = 18
+ WRC = 19
+ OCC = 20
+ PRC = 21
+ PLC = 22
+ CEC = 23
+ CAS = 24
+ WCE = 25
+ WDE = 26
+ WOE = 27
+ DR = 30
+ WPR = 31
+
+
+class R_XHCI_PORTLI(Register32):
+ ERROR_CNT = 15, 0
+ RLC = 19, 16
+ TLC = 23, 20
+ RSV = 31, 24
+
+
+class R_XHCI_IMAN(Register32):
+ IP = 0
+ IE = 1
+
+
+class XhciRegs(RegMap):
+ HCSPARAMS1 = 0x04, Register32
+ HCSPARAMS2 = 0x08, Register32
+ HCSPARAMS3 = 0x0C, Register32
+ HCCPARAMS1 = 0x10, Register32
+ DBOFF = 0x14, Register32
+ RTSOFF = 0x18, Register32
+ HCCPARAMS2 = 0x1C, Register32
+ USBCMD = 0x20, R_XHCI_USBCMD
+ USBSTS = 0x24, R_XHCI_USBSTS
+ DNCTRL = 0x34, R_XHCI_DNCTRL
+ CRCR_LO = 0x38, R_XHCI_CRCR_LO
+ CRCR_HI = 0x3C, Register32
+ DCBAAP_LO = 0x50, Register32
+ DCBAAP_HI = 0x54, Register32
+
+ PORTSC0 = 0x420, R_XHCI_PORTSC
+ PORTPMSC0 = 0x424, Register32
+ PORTLI0 = 0x428, R_XHCI_PORTLI
+ PORTHLPMC0 = 0x42C, Register32
+
+ PORTSC1 = 0x430, R_XHCI_PORTSC
+ PORTPMSC1 = 0x434, Register32
+ PORTLI1 = 0x438, R_XHCI_PORTLI
+ PORTHLPMC1 = 0x43C, Register32
+
+ MFINDEX = 0x440, Register32
+ IMAN0 = 0x460 + 0x00, R_XHCI_IMAN
+ IMOD0 = 0x460 + 0x04, Register32
+ ERSTSZ0 = 0x460 + 0x08, Register32
+ RSVD0 = 0x460 + 0x0C, Register32
+ ERSTBA0 = 0x460 + 0x10, Register64
+ ERDP0 = 0x460 + 0x18, Register64
+
+ IMAN1 = 0x480 + 0x00, R_XHCI_IMAN
+ IMOD1 = 0x480 + 0x04, Register32
+ ERSTSZ1 = 0x480 + 0x08, Register32
+ RSVD1 = 0x480 + 0x0C, Register32
+ ERSTBA1 = 0x480 + 0x10, Register64
+ ERDP1 = 0x480 + 0x18, Register64
+
+ IMAN2 = 0x4A0 + 0x00, R_XHCI_IMAN
+ IMOD2 = 0x4A0 + 0x04, Register32
+ ERSTSZ2 = 0x4A0 + 0x08, Register32
+ RSVD0 = 0x4A0 + 0x0C, Register32
+ ERSTBA2 = 0x4A0 + 0x10, Register64
+ ERDP2 = 0x4A0 + 0x18, Register64
+
+ IMAN3 = 0x4C0 + 0x00, R_XHCI_IMAN
+ IMOD3 = 0x4C0 + 0x04, Register32
+ ERSTSZ3 = 0x4C0 + 0x08, Register32
+ RSVD0 = 0x4C0 + 0x0C, Register32
+ ERSTBA3 = 0x4C0 + 0x10, Register64
+ ERDP3 = 0x4C0 + 0x18, Register64
+
+ DOORBELL = irange(0x4E0, 256, 4), R_XHCI_DOORBELL
+
+
+class R_GUSB3PIPECTL(Register32):
+ PHYSOFTRST = 31
+ U2SSINP3OK = 29
+ DISRXDETINP3 = 28
+ UX_EXIT_PX = 27
+ REQP1P2P3 = 24
+ DEPOCHANGE = 18
+ SUSPHY = 17
+ LFPSFILT = 9
+ RX_DETOPOLL = 8
+
+
+class R_GUSB2PHYCFG(Register32):
+ PHYSOFTRST = 31
+ U2_FREECLK_EXISTS = 30
+ SUSPHY = 6
+ ULPI_UTMI = 4
+ ENBLSLPM = 8
+
+
+class R_GCTL(Register32):
+ U2RSTECN = 16
+ PRTCAP = 14, 12
+ CORESOFTRESET = 11
+ SOFITPSYNC = 10
+ SCALEDOWN = 6, 4
+ DISSCRAMBLE = 3
+ U2EXIT_LFPS = 2
+ GBLHIBERNATIONEN = 1
+ DSBLCLKGTNG = 0
+
+
+class Dwc3CoreRegs(RegMap):
+ GSBUSCFG0 = 0x100, Register32
+ GSBUSCFG1 = 0x104, Register32
+ GTXTHRCFG = 0x108, Register32
+ GRXTHRCFG = 0x10C, Register32
+ GCTL = 0x110, R_GCTL
+ GEVTEN = 0x114, Register32
+ GSTS = 0x118, Register32
+ GUCTL1 = 0x11C, Register32
+ GSNPSID = 0x120, Register32
+ GGPIO = 0x124, Register32
+ GUID = 0x128, Register32
+ GUCTL = 0x12C, Register32
+ GBUSERRADDR0 = 0x130, Register32
+ GBUSERRADDR1 = 0x134, Register32
+ GPRTBIMAP0 = 0x138, Register32
+ GPRTBIMAP1 = 0x13C, Register32
+ GHWPARAMS0 = 0x140, Register32
+ GHWPARAMS1 = 0x144, Register32
+ GHWPARAMS2 = 0x148, Register32
+ GHWPARAMS3 = 0x14C, Register32
+ GHWPARAMS4 = 0x150, Register32
+ GHWPARAMS5 = 0x154, Register32
+ GHWPARAMS6 = 0x158, Register32
+ GHWPARAMS7 = 0x15C, Register32
+ GDBGFIFOSPACE = 0x160, Register32
+ GDBGLTSSM = 0x164, Register32
+ GDBGBMU = 0x16C, Register32
+ GDBGLSPMUX = 0x170, Register32
+ GDBGLSP = 0x174, Register32
+ GDBGEPINFO0 = 0x178, Register32
+ GDBGEPINFO1 = 0x17C, Register32
+ GPRTBIMAP_HS0 = 0x180, Register32
+ GPRTBIMAP_HS1 = 0x184, Register32
+ GPRTBIMAP_FS0 = 0x188, Register32
+ GPRTBIMAP_FS1 = 0x18C, Register32
+ GUCTL2 = 0x19C, Register32
+ GUSB2PHYCFG = 0x200, R_GUSB2PHYCFG
+ GUSB2I2CCTL = 0x240, Register32
+ GUSB2PHYACC = 0x280, Register32
+ GUSB3PIPECTL = 0x2C0, R_GUSB3PIPECTL
+ DWC3_GHWPARAMS8 = 0x600, Register32
+ DWC3_GUCTL3 = 0x60C, Register32
+ DWC3_GFLADJ = 0x630, Register32
+ DWC3_GHWPARAMS9 = 0x680, Register32
+
+
+class R_PIPEHANDLER_OVERRIDE(Register32):
+ RXVALID = 0
+ RXDETECT = 2
+
+
+class E_PIPEHANDLER_MUX_MODE(IntEnum):
+ USB3_PHY = 0
+ DUMMY_PHY = 1
+ UNK2 = 2
+
+
+class E_PIPEHANDLER_CLK_SELECT(IntEnum):
+ UNK0 = 0
+ USB3_PHY = 1
+ DUMMY_PHY = 2
+ UNK4 = 4
+
+
+class R_PIPEHANDLER_MUX_CTRL(Register32):
+ MUX_MODE = 1, 0, E_PIPEHANDLER_MUX_MODE
+ CLK_SELECT = 5, 3, E_PIPEHANDLER_CLK_SELECT
+
+
+class R_PIPEHANDLER_LOCK(Register32):
+ LOCK_EN = 0
+
+
+class R_PIPEHANDLER_AON_GEN(Register32):
+ DWC3_FORCE_CLAMP_EN = 4
+ DWC3_RESET_N = 0
+
+
+class R_PIPEHANDLER_NONSELECTED_OVERRIDE(Register32):
+ NATIVE_POWER_DOWN = 3, 0
+ NATIVE_RESET = 12
+ DUMMY_PHY_EN = 15
+
+
+class PipehandlerRegs(RegMap):
+ PIPEHANDLER_OVERRIDE = 0x00, R_PIPEHANDLER_OVERRIDE
+ PIPEHANDLER_OVERRIDE_VALUES = 0x04, R_PIPEHANDLER_OVERRIDE
+ PIPEHANDLER_MUX_CTRL = 0x0C, R_PIPEHANDLER_MUX_CTRL
+ PIPEHANDLER_LOCK_REQ = 0x10, R_PIPEHANDLER_LOCK
+ PIPEHANDLER_LOCK_ACK = 0x14, R_PIPEHANDLER_LOCK
+ PIPEHANDLER_AON_GEN = 0x1C, R_PIPEHANDLER_AON_GEN
+ PIPEHANDLER_NONSELECTED_OVERRIDE = 0x20, R_PIPEHANDLER_NONSELECTED_OVERRIDE
diff --git a/tools/proxyclient/m1n1/hw/i2c.py b/tools/proxyclient/m1n1/hw/i2c.py
new file mode 100644
index 0000000..e2bda7a
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/i2c.py
@@ -0,0 +1,251 @@
+# SPDX-License-Identifier: MIT
+from ..utils import *
+from enum import IntEnum
+
+__all__ = ["I2C", "I2CRegs"]
+
+
+class R_MTXFIFO(Register32):
+ READ = 10 # Read (DATA=count)
+ STOP = 9 # Issue START before
+ START = 8 # Issue STOP after
+ DATA = 7, 0 # Byte to send or count
+
+class R_MRXFIFO(Register32):
+ EMPTY = 8 # FIFO empty
+ DATA = 7, 0 # FIFO data
+
+class R_MCNT(Register32):
+ S_RXCNT = 31, 24 # Slave RX count
+ S_TXCNT = 23, 16 # Slave TX count
+ M_RXCNT = 15, 8 # Master RX count
+ M_TXCNT = 7, 0 # Master TX count
+
+class E_MST(IntEnum):
+ IDLE = 0
+ FRD1 = 1
+ FRD2 = 2
+ COMMAND = 3
+ START = 4
+ WRITE = 5
+ READ = 6
+ ACK = 7
+ STOP = 8
+ BAD = 15
+
+class E_SST(IntEnum):
+ IDLE = 0
+ START = 1
+ ST_ACK = 2
+ DATA = 3
+ ACK = 4
+
+class R_XFSTA(Register32):
+ MST = 27, 24, E_MST # Master controller state
+ SRD = 20 # Slave read in progress
+ SWR = 19 # Slave write in progress
+ SST = 18, 16, E_SST # Slave controller state
+ XFIFO = 9, 8 # FIFO number for error
+ XFCNT = 7, 0 # Number of bytes in current xfer
+
+class R_SADDR(Register32):
+ DEB = 31 # Enable SDA/SCL read debug
+ DIR = 30 # Direct (bitbang) mode
+ ENS = 29 # Enable slave interface
+ RST_STX = 28 # Reset slave TX FIFO
+ RST_SRX = 27 # Reset master RX fifo (if ^ both, controller too)
+ PEN = 26 # Promiscuous mode (slave)
+ AAE = 25 # SALT/ALTMASK enable
+ SAE = 24 # SADDR enable
+ ALTMASK = 23, 16 # MASK for SALT bits
+ SALT = 15, 8 # Alt slave address
+ SADDR = 7, 0 # Slave address
+
+class R_SMSTA(Register32):
+ XIP = 28 # Xaction in progress
+ XEN = 27 # Xaction ended
+ UJF = 26 # UnJam failure
+ JMD = 25 # Jam ocurred
+ JAM = 24 # Currently jammed
+ MTO = 23 # Master timeout
+ MTA = 22 # Master arb lost
+ MTN = 21 # Master received NACK
+ MRF = 20 # Master RX fifo full
+ MRNE = 19 # Master RX fifo not empty
+ MTF = 17 # Master TX fifo full
+ MTE = 16 # Master RX fifo empty
+ STO = 15 # Slave timeout
+ STA = 14 # Slave arb lost
+ STN = 13 # Slave received NACK
+ SRF = 12 # Slave RX fifo full
+ SRNE = 11 # Slave RX fifo not empty
+ STR = 10 # Slave transmit required
+ STF = 9 # Slave TX fifo full
+ STE = 8 # Slave TX fifo empty
+ TOS = 7 # Timeout due to slave FIFO
+ TOM = 6 # Timeout due to master FIFO
+ TOE = 5 # Slave timeout due to ext clock stretch
+ DCI = 4 # Direct clock in
+ DDI = 3 # Direct data in
+ DCO = 2 # Direct clock out
+ DDO = 1 # Direct data out
+ NN = 0 # NACK next (slave)
+
+class R_CTL(Register32):
+ MSW = 26, 16 # Maximum slave write size
+ ENABLE = 11 # Unknown enable bit (clock sel? Apple thing)
+ MRR = 10 # Master receive FIFO reset
+ MTR = 9 # Master transmit FIFO reset
+ UJM = 8 # Enable auto unjam machine
+ CLK = 7, 0 # Clock divider
+
+class R_STXFIFO(Register32):
+ DATA = 7, 0 # Data
+
+class R_SRXFIFO(Register32):
+ N = 12 # NACK received after this byte
+ P = 11 # Stop received, data not valid
+ S = 10 # Start received before
+ O = 9 # Overflow (promisc only)
+ E = 8 # Empty (data not valid)
+ DATA = 7, 0 # Data
+
+# Apple reg
+class R_FIFOCTL(Register32):
+ HALT = 0 # Halt machinery
+
+class I2CRegs(RegMap):
+ MTXFIFO = 0x00, R_MTXFIFO
+ MRXFIFO = 0x04, R_MRXFIFO
+ MCNT = 0x08, R_MCNT
+ XFSTA = 0x0c, R_XFSTA
+ SADDR = 0x10, R_SADDR
+ SMSTA = 0x14, R_SMSTA
+ IMASK = 0x18, R_SMSTA
+ CTL = 0x1c, R_CTL
+ STXFIFO = 0x20, R_STXFIFO
+ SRXFIFO = 0x20, R_SRXFIFO
+ FIFOCTL = 0x44, R_FIFOCTL
+
+
+class I2C:
+ def __init__(self, u, adt_path):
+ self.u = u
+ self.p = u.proxy
+ self.iface = u.iface
+ self.base = u.adt[adt_path].get_reg(0)[0]
+ self.regs = I2CRegs(u, self.base)
+ self.devs = []
+
+ def clear_fifos(self):
+ self.regs.CTL.set(MTR=1, MRR=1)
+
+ def clear_status(self):
+ self.regs.SMSTA.val = 0xffffffff
+
+ def _fifo_read(self, nbytes):
+ read = []
+ for _ in range(nbytes):
+ val = self.regs.MRXFIFO.reg
+ timeout = 10000
+ while val.EMPTY and timeout > 0:
+ val = self.regs.MRXFIFO.reg
+ timeout -= 1
+ if timeout == 0:
+ raise Exception("timeout")
+ read.append(int(val) & 0xff)
+ return bytes(read)
+
+ def _fifo_write(self, buf, stop=False):
+ for no, byte in enumerate(buf):
+ sending_stop = stop and no == len(buf) - 1
+ self.regs.MTXFIFO.set(DATA=byte, STOP=int(sending_stop))
+
+ if not stop:
+ return
+
+ timeout = 10000
+ while not self.regs.SMSTA.reg.XEN and timeout > 0:
+ timeout -= 1
+ if timeout == 0:
+ raise Exception("timeout")
+
+ def write_reg(self, addr, reg, data, regaddrlen=1):
+ self.clear_fifos()
+ self.clear_status()
+
+ self.regs.CTL.set(ENABLE=1, CLK=0x4)
+ self.regs.MTXFIFO.set(DATA=addr << 1, START=1)
+ regbytes = int.to_bytes(reg, regaddrlen, byteorder="big")
+ self._fifo_write(regbytes + bytes(data), stop=True)
+ self.regs.CTL.set(ENABLE=0, CLK=0x4)
+
+ def read_reg(self, addr, reg, nbytes, regaddrlen=1):
+ self.clear_fifos()
+ self.clear_status()
+
+ self.regs.CTL.set(ENABLE=1, CLK=0x4)
+ self.regs.MTXFIFO.set(DATA=addr << 1, START=1)
+ regbytes = int.to_bytes(reg, regaddrlen, byteorder="big")
+ self._fifo_write(regbytes, stop=False)
+ self.regs.MTXFIFO.set(DATA=(addr << 1) | 1, START=1)
+ self.regs.MTXFIFO.set(DATA=nbytes, STOP=1, READ=1)
+ data = self._fifo_read(nbytes)
+ self.regs.CTL.set(ENABLE=0, CLK=0x4)
+ return data
+
+class I2CRegMapDev:
+ REGMAP = None
+ ADDRESSING = (0, 1)
+
+ def __init__(self, bus, addr, name=None):
+ self.bus = bus
+ self.addr = addr
+ self.curr_page = None
+ self.name = name
+
+ self.paged, self.regimmbytes = self.ADDRESSING
+ if self.REGMAP is not None:
+ self.regs = self.REGMAP(self, 0)
+
+ @classmethod
+ def from_adt(cls, bus, path):
+ node = bus.u.adt[path]
+ addr = node.reg[0] & 0xff
+ return cls(bus, addr, node.name)
+
+ def _switch_page(self, page):
+ assert self.paged
+ self.bus.write_reg(self.addr, 0, bytes([page]),
+ regaddrlen=self.regimmbytes)
+ self.curr_page = page
+
+ def _snip_regaddr(self, addr):
+ pageshift = self.regimmbytes * 8
+ page = addr >> pageshift
+ immediate = addr & ~(~0 << pageshift)
+ return (page, immediate)
+
+ def write(self, reg, val, width=8):
+ page, imm = self._snip_regaddr(reg)
+
+ if self.paged and page != self.curr_page:
+ self._switch_page(page)
+
+ valbytes = val.to_bytes(width//8, byteorder="little")
+ self.bus.write_reg(self.addr, imm, valbytes,
+ regaddrlen=self.regimmbytes)
+
+ def read(self, reg, width=8):
+ page, imm = self._snip_regaddr(reg)
+
+ if self.paged and page != self.curr_page:
+ self._switch_page(page)
+
+ data = self.bus.read_reg(self.addr, imm, width//8,
+ regaddrlen=self.regimmbytes)
+ return int.from_bytes(data, byteorder='little')
+
+ def __repr__(self):
+ label = self.name or f"@ {self.addr:02x}"
+ return f"<{type(self).__name__} {label}>"
diff --git a/tools/proxyclient/m1n1/hw/isp.py b/tools/proxyclient/m1n1/hw/isp.py
new file mode 100644
index 0000000..4d8d0fd
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/isp.py
@@ -0,0 +1,507 @@
+import struct
+from enum import IntEnum
+from ..utils import *
+
+class ISPCommandDirection(IntEnum):
+ RX = 0
+ TX = 1
+
+class ISPCommand:
+ """ Represents a command in any IPC channel """
+
+ def __init__(self, channel, message, direction):
+ value, u0, u1 = struct.unpack('<3q40x', message.data)
+ self.message = message
+ self.channel = channel
+ self.direction = direction
+ self.tracer = channel.tracer
+ self.raw_value = value
+ self.value = value & 0xFFFFFFFFFFFFFFFC
+ self.arg0 = u0
+ self.arg1 = u1
+
+ def dump(self):
+ self.log(f"[CMD Value: {hex(self.value)}, U0: {hex(self.arg0)}, U1: {hex(self.arg1)}]")
+
+ def read_iova(self, address, length):
+ return self.tracer.dart.ioread(0, address, length)
+
+ def valid(self):
+ return True
+
+ def log(self, message):
+ if self.direction is ISPCommandDirection.RX:
+ self.tracer.log(f"<== [{self.channel.name}]({self.message.index}): {message}")
+ else:
+ self.tracer.log(f"==> [{self.channel.name}]({self.message.index}): {message}")
+
+class ISPTerminalCommand(ISPCommand):
+ """ Represents a command in TERMINAL channel
+
+ A command arguments include a pointer to a buffer that contains log line
+ and the length of the buffer. Buffers are 0x80 bytes wide.
+ """
+ # ISP sends buffer address at beginning
+ BUFFER_ADDRESS = None
+ # It seems messages are capped to 100 bytes
+ MAX_BUFFER_SIZE = 0x80
+
+ @staticmethod
+ def set_address(address):
+ if address != 0:
+ ISPTerminalCommand.BUFFER_ADDRESS = address
+
+ @staticmethod
+ def move_cursor():
+ if ISPTerminalCommand.BUFFER_ADDRESS:
+ ISPTerminalCommand.BUFFER_ADDRESS += ISPTerminalCommand.MAX_BUFFER_SIZE
+ else:
+ return None
+
+ def __init__(self, channel, message, direction):
+ super().__init__(channel, message, direction)
+
+ ## Set buffer address
+ ISPTerminalCommand.set_address(self.value)
+
+ ## Read contents
+ self.buffer_message = self.read_iova(ISPTerminalCommand.BUFFER_ADDRESS, self.arg0)
+
+ ## Move cursor
+ ISPTerminalCommand.move_cursor()
+
+ def dump(self):
+ self.log(f"ISPCPU: {self.buffer_message}]")
+
+ def log(self, message):
+ self.tracer.log(f"[{self.channel.name}]({str(self.message.index).ljust(3)}): {message}")
+
+class ISPIOCommand(ISPCommand):
+ """ Represents a command in IO channel
+
+ An IO command is used to request ISP to perform some operations. The command
+ contains a pointer to a command struct which contains a OPCODE. The OPCODE
+ is used to differentate commands.
+ """
+
+ def __init__(self, channel, message, direction):
+ super().__init__(channel, message, direction)
+ self.iova = self.value
+ if self.iova != 0:
+ contents = self.read_iova(self.iova, 0x8)
+ self.contents = int.from_bytes(contents, byteorder="little")
+ else:
+ self.contents = None
+
+ def dump(self):
+ if self.iova != 0:
+ self.log(f"[IO Addr: {hex(self.iova)}, Size: {hex(self.arg0)}, U1: {hex(self.arg1)} -> Opcode: {hex(self.contents >> 32)}]")
+
+class ISPT2HBufferCommand(ISPCommand):
+ """ Represents a command in BUF_T2H channel """
+ def __init__(self, channel, message, direction):
+ super().__init__(channel, message, direction)
+ self.iova = self.value
+ if self.iova != 0:
+ self.contents = self.read_iova(self.iova, 0x280)
+
+ def dump(self):
+ super().dump()
+ if self.iova != 0:
+ chexdump(self.contents)
+
+class ISPH2TBufferCommand(ISPCommand):
+ """ Represents a command in BUF_H2T channel """
+ def __init__(self, channel, message, direction):
+ super().__init__(channel, message, direction)
+ self.iova = self.value
+ if self.iova != 0:
+ # Dumping first 0x20 bytes after iova translation, but no idea how internal struct
+ self.contents = self.read_iova(self.iova, 0x20)
+
+ def dump(self):
+ super().dump()
+ if self.iova != 0:
+ chexdump(self.contents)
+
+class ISPT2HIOCommand(ISPCommand):
+ """ Represents a command in IO_T2H channel """
+ def __init__(self, channel, message, direction):
+ super().__init__(channel, message, direction)
+ self.iova = self.value
+ if self.iova != 0:
+ # Dumping first 0x20 bytes after iova translation, but no idea how internal struct
+ self.contents = self.read_iova(self.iova, 0x20)
+
+ def dump(self):
+ super().dump()
+ if self.iova != 0:
+ chexdump(self.contents)
+
+class ISPSharedMallocCommand(ISPCommand):
+ """ Represents a command in SHAREDMALLOC channel
+
+ A command of this type can either request memory allocation or memory free
+ depending the arguments. When ISP needs to allocate memory, it puts a
+ message in the SHAREDMALLOC channel, message arguments are length of buffer
+ and type of allocation.
+
+ CPU detects the new message, perform memory allocation and mutate the
+ original message to indicate the address of the allocated memory block.
+ """
+
+ def __init__(self, channel, message, direction):
+ super().__init__(channel, message, direction)
+ self.address = self.value
+ self.size = self.arg0
+ self.type = self.arg1 #.to_bytes(8, byteorder="little")
+
+ def dump(self):
+ if self.direction == ISPCommandDirection.RX:
+ if self.address is 0:
+ self.log(f"[FW Malloc, Length: {hex(self.size)}, Type: {hex(self.type)}]")
+ else:
+ self.log(f"[FW Free, Address: {hex(self.value)}, Length: {hex(self.size)}, Type: {hex(self.type)})]")
+ else:
+ if self.address is 0:
+ self.log(f"[FW Free]")
+ else:
+ self.log(f"[FW Malloc, Address: {hex(self.value)}, Type: {hex(self.type)})]")
+
+class ISPChannelTable:
+ """ A class used to present IPC table.
+
+ The Channel Table describes the IPC channels available to communicate with
+ the ISP.
+
+ In the M1 processor (tonga), the list of channels exposed by ISP are:
+ [CH - TERMINAL] (src = 0, type = 2, entries = 768, iova = 0x1804700)
+ [CH - IO] (src = 1, type = 0, entries = 8, iova = 0x1810700)
+ [CH - BUF_H2T] (src = 2, type = 0, entries = 64, iova = 0x1810b00)
+ [CH - BUF_T2H] (src = 3, type = 1, entries = 64, iova = 0x1811b00)
+ [CH - SHAREDMALLOC] (src = 3, type = 1, entries = 8, iova = 0x1812b00)
+ [CH - IO_T2H] (src = 3, type = 1, entries = 8, iova = 0x1812d00)
+
+ Each entry in the table is 256 bytes wide. Here is the layout of each entry:
+ 0x00 - 0x1F = Name (NULL terminated string)
+ 0x20 - 0x3F = Padding
+ 0x40 - 0x43 = Type (DWORD)
+ 0x44 - 0x47 = Source (DWORD)
+ 0x48 - 0x4F = Entries (QWORD)
+ 0x50 - 0x58 = Address (QWORD)
+ """
+
+ ENTRY_LENGTH = 256
+
+ def __init__(self, tracer, number_of_channels, table_address):
+ self.tracer = tracer
+ self.address = table_address
+ self.count = number_of_channels
+ self.size = number_of_channels * self.ENTRY_LENGTH
+ self.channels = []
+
+ _table = self.ioread(self.address & 0xFFFFFFFF, self.size)
+ for offset in range(0, self.size, self.ENTRY_LENGTH):
+ _entry = _table[offset: offset + self.ENTRY_LENGTH]
+ _name, _type, _source, _entries, _address = struct.unpack('<32s32x2I2q168x', _entry)
+ _channel = ISPChannel(self, _name, _type, _source, _entries, _address)
+ # We want to process terminal logs as fast as possible before they are processed by CPU
+ # So we use a special implementation for TERMINAL channel that fetches all logs
+ if _channel.name == "TERMINAL":
+ _channel = ISPTerminalChannel(self, _name, _type, _source, _entries, _address)
+ self.channels.append(_channel)
+
+ def get_last_write_command(self, doorbell_value):
+ """ Gets last written message given a Doorbell value """
+ if self.channels and len(self.channels) > 0:
+ names = []
+ channel_cmds = []
+ for channel in self.channels:
+ # We want to process terminal logs as fast as possible before they are processed by CPU
+ if (channel.doorbell == doorbell_value) or channel.name == "TERMINAL":
+ names.append(channel.name)
+ for cmd in channel.get_commands(ISPCommandDirection.TX):
+ channel_cmds.append(cmd)
+
+ self.log(f"CHs: [{(','.join(names))}]")
+ for cmd in channel_cmds:
+ cmd.dump()
+
+ def get_last_read_command(self, pending_irq):
+ """ Gets last read message given a IRQ value """
+ cmds = []
+ scanned_channels = []
+ if self.channels and len(self.channels) > 0:
+ cidx = 0
+ for channel in self.channels:
+ if (pending_irq >> channel.source & 1) != 0:
+ scanned_channels.append(channel.name)
+ for cmd in channel.get_commands(ISPCommandDirection.RX):
+ cmds.append(cmd)
+ cidx = cidx + 1
+
+ if len(scanned_channels) > 0:
+ self.log(f"CHs: [{(','.join(scanned_channels))}]")
+ for cmd in cmds:
+ cmd.dump()
+
+ def dump(self):
+ """ Dumps the content of each channel """
+ if self.channels and len(self.channels) > 0:
+ for channel in self.channels:
+ channel.dump()
+
+ def ioread(self, address, size):
+ return self.tracer.ioread(address, size)
+
+ def log(self, message):
+ self.tracer.log(message)
+
+ def __str__(self):
+ s = "======== CHANNEL TABLE ========\n"
+ for channel in self.channels:
+ s += f"\t{str(channel)}\n"
+ return s
+
+class ISPChannel:
+ """ A class used to represent IPC channel
+
+ ISP channels are ring buffers used by communication between CPU and ISP.
+ channel length is measured in number of entries, each entry is 64 bytes,
+ so channel size is '(entries * 64)' bytes.
+
+ Channel Source is used to filter out channels when processing interrupts
+ and doorbell. Each time CPU wants to notify ISP about a new message it
+ writes doorbell register. In the other hand, when ISP wants to notify CPU
+ about a new message it triggers a hardware interrupt.
+
+ Channel Type is a mistery, but it seems to have a connection with cmd bit
+ mask.
+ """
+
+ ENTRY_LENGTH = 64
+
+ def __init__(self, table, name, _type, source, number_of_entries, address):
+ self.table = table
+ self.tracer = table.tracer
+ self.name = str(name, "ascii").rstrip('\x00')
+ self.source = source
+ self.type = _type
+ self.number_of_entries = number_of_entries
+ self.entry_size = self.ENTRY_LENGTH
+ self.size = self.number_of_entries * self.entry_size
+ self.address = address
+ self.doorbell = 1 << source
+ self.last_message_sent = None
+ self.last_message_received = None
+
+ def get_commands(self, direction):
+ """ Gets a command from the channel"""
+ commands = []
+ message = self.get_message(direction)
+ if message:
+ command = self.__convert2command__(message, direction)
+ if command:
+ commands.append(command)
+ return commands
+
+ def get_message(self, direction):
+ """ Gets a message from the channel and increase the associated index """
+ last_message = self.last_message_sent if direction is ISPCommandDirection.TX else self.last_message_received
+ index = (last_message.index + 1) if last_message else 0
+ new_index, message = self.__read_message__(index)
+ if message:
+ if last_message and last_message == message:
+ return
+
+ last_message = message
+ if direction is ISPCommandDirection.TX:
+ self.last_message_sent = last_message
+ else:
+ self.last_message_received = last_message
+ return message
+
+ def dump(self):
+ """ Dumps the content of the channel """
+ s = f"[{self.name}] Channel messages: \n"
+ for index in range(self.number_of_entries):
+ _, message = self.__read_message__(index)
+ s = s + "\t" + str(message) + "\n"
+ self.table.log(s)
+
+ def __convert2command__(self, message, direction):
+ """ Converts a channel message into a command """
+ if self.name == "TERMINAL":
+ return ISPTerminalCommand(self, message, direction)
+ elif self.name == "IO" or self.name == "DEBUG":
+ return ISPIOCommand(self, message, direction)
+ elif self.name == "SHAREDMALLOC":
+ return ISPSharedMallocCommand(self, message, direction)
+ elif self.name == "BUF_T2H":
+ return ISPT2HBufferCommand(self, message, direction)
+ elif self.name == "BUF_H2T":
+ return ISPH2TBufferCommand(self, message, direction)
+ elif self.name == "IO_T2H":
+ return ISPT2HIOCommand(self, message, direction)
+ else:
+ return ISPCommand(self, message, direction)
+
+ def __read_message__(self, index):
+ message_data = self.__read_by_index__(index)
+ message = ISPChannelMessage(index, message_data)
+ if message.valid():
+ index += 1
+ if index >= self.number_of_entries:
+ index = 0
+ return index, message
+ return 0, None
+
+ def __read_by_index__(self, index):
+ return self.table.ioread(self.address + (self.entry_size * index), self.entry_size)
+
+ def __str__(self):
+ return f"[CH - {str(self.name)}] (src = {self.source!s}, type = {self.type!s}, size = {self.number_of_entries!s}, iova = {hex(self.address)!s})"
+
+class ISPTerminalChannel(ISPChannel):
+ """ Special channel implementation for TERMINAL channel
+ Addresses of log buffers are removed from memory after MacOS processes them,
+ hence we want to be a little bit ahead of MacOS and fetch all entries if
+ possible.
+ """
+
+ def __init__(self, table, name, _type, source, number_of_entries, address):
+ super().__init__(table, name, _type, source, number_of_entries, address)
+ self.last_index = 0
+
+ def get_commands(self, direction):
+ """ Gets a command from the channel"""
+ commands = []
+ for i in range(self.number_of_entries):
+ index = (self.last_index + i) % self.number_of_entries
+ _, message = self.__read_message__(index)
+ if message and message.valid():
+ command = self.__convert2command__(message, ISPCommandDirection.RX)
+ if command:
+ commands.append(command)
+ else:
+ self.last_index = index
+ break
+ return commands
+
+class ISPChannelMessage:
+ """ A class used to represent IPC channel message or entry
+
+ Each entry is 64 bytes, however only 24 bytes seems to be used. These 24
+ bytes are divided in three qwords (8-bytes).
+ """
+
+ def __init__(self, index, data):
+ self.index = index
+ self.data = data
+ idx = 0
+ for arg in struct.unpack('<8q', self.data):
+ setattr(self, f"arg{idx}", arg)
+ idx += 1
+
+ def valid(self):
+ """ Checks if a message seems to be valid
+
+ So far I have observed that invalid messages or empty slots
+ are usually marked as 0x1 (or 0x3 in case of TERMINAL msgs)
+ """
+ return (self.arg0 is not 0x1) and (self.arg0 is not 0x3)
+
+ def __str__(self):
+ s = "ISP Message: {"
+ idx = 0
+ for arg in struct.unpack('<8q', self.data):
+ s = s + f"Arg{idx}: {hex(arg)}, "
+ idx = idx + 1
+ s = s + "}"
+ return s
+
+ def __eq__(self, other):
+ return self.data == other.data
+
+class ISP_REVISION(Register32):
+ REVISION = 15, 0
+
+class ISP_PMU(Register32):
+ STATUS = 7, 0
+ OTHER = 63, 8
+
+class ISP_PMU_SPECIAL_STATUS(Register32):
+ STATUS = 7, 0
+ OTHER = 63, 8
+
+class ISPRegs(RegMap):
+ ISP_CPU_CONTROL = 0x0000, Register32
+ ISP_CPU_STATUS = 0x0004, Register32
+ ISP_REVISION = 0x1800000, ISP_REVISION
+ ISP_POWER_UNKNOWN = 0x20e0080, Register32
+ ISP_IRQ_INTERRUPT = 0x2104000, Register32
+ ISP_IRQ_INTERRUPT_2 = 0x2104004, Register32
+ ISP_SENSOR_REF_CLOCK = irange(0x2104190, 3, 4), Register32
+ ISP_GPR0 = 0x2104170, Register32
+ ISP_GPR1 = 0x2104174, Register32
+ ISP_GPR2 = 0x2104178, Register32
+ ISP_GPR3 = 0x210417c, Register32
+ ISP_GPR4 = 0x2104180, Register32
+ ISP_GPR5 = 0x2104184, Register32
+ ISP_GPR6 = 0x2104188, Register32
+ ISP_GPR7 = 0x210418c, Register32
+
+ ISP_DOORBELL_RING0 = 0x21043f0, Register32
+ ISP_IRQ_INTERRUPT_ACK = 0x21043fc, Register32
+
+ ISP_SMBUS_REG_MTXFIFO = irange(0x2110000, 4, 0x1000), Register32
+ ISP_SMBUS_REG_MRXFIFO = irange(0x2110004, 4, 0x1000), Register32
+ ISP_SMBUS_REG_UNK_1 = irange(0x2110008, 4, 0x1000), Register32
+ ISP_SMBUS_REG_UNK_2 = irange(0x211000c, 4, 0x1000), Register32
+ ISP_SMBUS_REG_UNK_3 = irange(0x2110010, 4, 0x1000), Register32
+ ISP_SMBUS_REG_SMSTA = irange(0x2110014, 4, 0x1000), Register32
+ ISP_SMBUS_REG_UNK_4 = irange(0x2110018, 4, 0x1000), Register32
+ ISP_SMBUS_REG_CTL = irange(0x211001c, 4, 0x1000), Register32
+ ISP_SMBUS_REG_UNK_5 = irange(0x2110020, 4, 0x1000), Register32
+ ISP_SMBUS_REG_UNK_6 = irange(0x2110024, 4, 0x1000), Register32
+ ISP_SMBUS_REG_REV = irange(0x2110028, 4, 0x1000), Register32
+ ISP_SMBUS_REG_UNK_7 = irange(0x211002c, 4, 0x1000), Register32
+ ISP_SMBUS_REG_UNK_8 = irange(0x2110030, 4, 0x1000), Register32
+ ISP_SMBUS_REG_UNK_9 = irange(0x2110034, 4, 0x1000), Register32
+ ISP_SMBUS_REG_UNK_A = irange(0x2110038, 4, 0x1000), Register32
+ ISP_SMBUS_REG_UNK_B = irange(0x211003c, 4, 0x1000), Register32
+
+ ISP_DPE_REG_UNK1 = 0x2504000, Register32
+ ISP_DPE_REG_UNK2 = 0x2508000, Register32
+
+ ISP_CPU_BUFFER = 0x1050000, Register32
+
+ ISP_SPMI0_REGISTER_BASE = 0x2900000, Register32
+ ISP_SPMI1_REGISTER_BASE = 0x2920000, Register32
+ ISP_SPMI2_REGISTER_BASE = 0x2940000, Register32
+
+class PSReg(RegMap):
+ PMU_UNKNOWN0 = 0x4000, ISP_PMU
+ PMU_UNKNOWN1 = 0x4008, ISP_PMU
+ PMU_UNKNOWN2 = 0x4010, ISP_PMU
+ PMU_UNKNOWN3 = 0x4018, ISP_PMU
+ PMU_UNKNOWN4 = 0x4020, ISP_PMU
+ PMU_UNKNOWN5 = 0x4028, ISP_PMU
+ PMU_UNKNOWN6 = 0x4030, ISP_PMU
+ PMU_UNKNOWN7 = 0x4038, ISP_PMU
+ PMU_UNKNOWN8 = 0x4040, ISP_PMU
+ PMU_UNKNOWN9 = 0x4048, ISP_PMU
+ PMU_UNKNOWNA = 0x4050, ISP_PMU
+ PMU_UNKNOWNB = 0x4058, ISP_PMU
+ PMU_SPECIAL_STATUS = 0x4060, ISP_PMU_SPECIAL_STATUS
+ CLOCK_TICK_LOW = 0x34004, Register32
+ CLOCK_TICK_HIGH = 0x34008, Register32
+ RT_BANDWIDTH_SCRATCH1 = 0x38014, Register32
+ RT_BANDWIDTH_SCRATCH2 = 0x38018, Register32
+
+class SPMIReg(RegMap):
+ SPMI_UNKNOWN0 = 0x28, Register32
+ SPMI_UNKNOWN1 = 0x40, Register32
+ SPMI_UNKNOWN2 = 0x90, Register32
+ SPMI_UNKNOWN3 = 0x80a0, Register32
+ SPMI_UNKNOWN4 = 0x80a4, Register32 \ No newline at end of file
diff --git a/tools/proxyclient/m1n1/hw/jpeg.py b/tools/proxyclient/m1n1/hw/jpeg.py
new file mode 100644
index 0000000..c92bfe0
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/jpeg.py
@@ -0,0 +1,334 @@
+# SPDX-License-Identifier: MIT
+from ..utils import *
+from enum import IntEnum
+
+
+class R_STATUS(Register32):
+ DONE = 0
+ TIMEOUT = 1
+ RD_BUF_OVERFLOW = 2
+ WR_BUF_OVERFLOW = 3
+ CODEC_BUF_OVERFLOW = 4
+ SOME_KIND_OF_MACROBLOCK_SIZE_ERROR = 5
+ AXI_ERROR = 6
+ UNKNOWN_FLAG = 7
+
+
+class E_CODEC(IntEnum):
+ _444 = 0
+ _422 = 1
+ _411 = 2
+ _420 = 3
+ _400 = 4
+
+
+class R_CODEC(Register32):
+ CODEC = 2, 0, E_CODEC
+
+
+class E_ENCODE_PIXEL_FORMAT(IntEnum):
+ RGB101010 = 0
+ YUV10_linear = 1
+ RGB888 = 2
+ RGB565 = 3
+ YUV_planar = 4 # partially tested, details not understood
+ YUV_linear = 5 # partially tested, details not understood
+
+
+class R_ENCODE_PIXEL_FORMAT(Register32):
+ FORMAT = 4, 0, E_ENCODE_PIXEL_FORMAT
+
+
+class E_SCALE(IntEnum):
+ DIV1 = 0
+ DIV2 = 1
+ DIV4 = 2
+ DIV8 = 3
+
+
+class R_SCALE_FACTOR(Register32):
+ SCALE = 1, 0, E_SCALE
+
+
+class E_DECODE_PIXEL_FORMAT(IntEnum):
+ YUV444_planar = 0
+ YUV422_planar = 1
+ YUV420_planar = 2
+ YUV422_linear = 3
+ _YUV10_broken_doesnt_work = 4
+ RGBA8888 = 5
+ RGB565 = 6
+ _RGB101010_broken_doesnt_work = 7
+
+
+class R_DECODE_PIXEL_FORMAT(Register32):
+ FORMAT = 3, 0, E_DECODE_PIXEL_FORMAT
+
+
+class E_JPEG_IO_FLAGS_SUBSAMPLING(IntEnum):
+ _444 = 0
+ _422 = 1
+ _420 = 2
+ _400 = 3
+ FOUR_COMPONENTS_MODE = 4
+ _411_BROKEN = 6
+
+
+class R_JPEG_IO_FLAGS(Register32):
+ SUBSAMPLING_MODE = 2, 0, E_JPEG_IO_FLAGS_SUBSAMPLING
+ # not sure what this is supposed to do
+ MAKE_DECODE_WORK_BREAK_ENCODE = 3
+ OUTPUT_MACROBLOCKS_UNFLIPPED_H = 4
+ OUTPUT_8BYTE_CHUNKS_CORRECTLY = 5
+
+
+class R_JPEG_OUTPUT_FLAGS(Register32):
+ # bit0 doesn't seem to do anything
+ SKIP_HEADERS = 1 # output only SOS/EOI, no SOI/DQT/SOF0/DHT
+ OUTPUT_SOF0_AFTER_DHT = 2 # output SOF0 after DHT instead of before it
+ # bit3 doesn't seem to do anything
+ COMPRESS_WORSE = 4 # not sure exactly what this does
+
+
+class R_QTBL_SEL(Register32):
+ COMPONENT0 = 1, 0
+ COMPONENT1 = 3, 2
+ COMPONENT2 = 5, 4
+ COMPONENT3 = 7, 6 # guessed
+
+
+class JPEGRegs(RegMap):
+ REG_0x0 = 0x0, Register32
+ REG_0x4 = 0x4, Register32
+ MODE = 0x8, Register32
+ REG_0xc = 0xc, Register32
+
+ REG_0x10 = 0x10, Register32
+ REG_0x14 = 0x14, Register32
+ REG_0x18 = 0x18, Register32
+ # REG_0x1c = 0x1c, Register32
+
+ REG_0x20 = 0x20, Register32
+ STATUS = 0x24, R_STATUS
+
+ CODEC = 0x28, R_CODEC
+
+ REG_0x2c = 0x2c, Register32
+ REG_0x30 = 0x30, Register32
+ REG_0x34 = 0x34, Register32
+ # this changes the output drastically if set to 1 for decode
+ # breaks encode if not set to 1
+ REG_0x38 = 0x38, Register32
+
+ # not sure what the difference is. siting? type2 seems to win over type1
+ CHROMA_HALVE_H_TYPE1 = 0x3c, Register32
+ CHROMA_HALVE_H_TYPE2 = 0x40, Register32
+ CHROMA_HALVE_V_TYPE1 = 0x44, Register32
+ CHROMA_HALVE_V_TYPE2 = 0x48, Register32
+
+ # if double and quadruple both set --> double
+ CHROMA_DOUBLE_H = 0x4c, Register32
+ CHROMA_QUADRUPLE_H = 0x50, Register32
+ CHROMA_DOUBLE_V = 0x54, Register32
+
+ # details not fully understood yet
+ PX_USE_PLANE1 = 0x58, Register32
+ PX_TILES_W = 0x5c, Register32
+ PX_TILES_H = 0x60, Register32
+ PX_PLANE0_WIDTH = 0x64, Register32
+ PX_PLANE0_HEIGHT = 0x68, Register32
+ PX_PLANE0_TILING_H = 0x6c, Register32
+ PX_PLANE0_TILING_V = 0x70, Register32
+ PX_PLANE0_STRIDE = 0x74, Register32
+ PX_PLANE1_WIDTH = 0x78, Register32
+ PX_PLANE1_HEIGHT = 0x7c, Register32
+ PX_PLANE1_TILING_H = 0x80, Register32
+ PX_PLANE1_TILING_V = 0x84, Register32
+ PX_PLANE1_STRIDE = 0x88, Register32
+
+ INPUT_START1 = 0x8c, Register32
+ INPUT_START2 = 0x90, Register32
+ REG_0x94 = 0x94, Register32
+ REG_0x98 = 0x98, Register32
+ INPUT_END = 0x9c, Register32
+
+ OUTPUT_START1 = 0xa0, Register32
+ OUTPUT_START2 = 0xa4, Register32
+ OUTPUT_END = 0xa8, Register32
+
+ MATRIX_MULT = irange(0xAC, 11, 4), Register32
+ DITHER = irange(0xD8, 10, 4), Register32
+
+ ENCODE_PIXEL_FORMAT = 0x100, R_ENCODE_PIXEL_FORMAT
+ # RGB888: R, G, B = byte pos
+ # RGB101010: R, G, B = 0/1/2 = low/mid/high bits
+ # RGB565: R, G, B = 0/1/2 = low/mid/high bits
+ # YUV10: Y, U, V = 0/1/2 = low/mid/high bits
+ # YUV linear: Y0 Cb Cr Y1 = byte pos
+ # YUV planar: Y U V = 0 for Y, 0/1 for U/V indicating position somehow
+ ENCODE_COMPONENT0_POS = 0x104, Register32
+ ENCODE_COMPONENT1_POS = 0x108, Register32
+ ENCODE_COMPONENT2_POS = 0x10c, Register32
+ ENCODE_COMPONENT3_POS = 0x110, Register32
+
+ CONVERT_COLOR_SPACE = 0x114, Register32
+
+ REG_0x118 = 0x118, Register32
+ REG_0x11c = 0x11c, Register32
+
+ REG_0x120 = 0x120, Register32
+
+ # details not understood yet
+ TILING_ENABLE = 0x124, Register32
+ TILING_PLANE0 = 0x128, Register32
+ TILING_PLANE1 = 0x12c, Register32
+
+ DECODE_MACROBLOCKS_W = 0x130, Register32
+ DECODE_MACROBLOCKS_H = 0x134, Register32
+ RIGHT_EDGE_PIXELS = 0x138, Register32
+ BOTTOM_EDGE_PIXELS = 0x13c, Register32
+ RIGHT_EDGE_SAMPLES = 0x140, Register32
+ BOTTOM_EDGE_SAMPLES = 0x144, Register32
+
+ SCALE_FACTOR = 0x148, R_SCALE_FACTOR
+
+ DECODE_PIXEL_FORMAT = 0x14c, R_DECODE_PIXEL_FORMAT
+ # 0 = Cb Y'0 Cr Y'1 1 = Y'0 Cb Y'1 Cr
+ YUV422_ORDER = 0x150, Register32
+ # 0 = BGRA 1 = RGBA
+ RGBA_ORDER = 0x154, Register32
+ RGBA_ALPHA = 0x158, Register32
+
+ PLANAR_CHROMA_HALVING = 0x15c, Register32
+
+ REG_0x160 = 0x160, Register32
+ REG_0x164 = 0x164, Register32
+ # REG_0x168 = 0x168, Register32
+ REG_0x16c = 0x16c, Register32
+
+ REG_0x170 = 0x170, Register32
+ # REG_0x174 = 0x174, Register32
+ PERFCOUNTER = 0x178, Register32
+ # REG_0x17c = 0x17c, Register32
+
+ # REG_0x180 = 0x180, Register32
+ TIMEOUT = 0x184, Register32
+ HWREV = 0x188, Register32
+
+ REG_0x18c = 0x18c, Register32
+ REG_0x190 = 0x190, Register32
+ REG_0x194 = 0x194, Register32
+ REG_0x198 = 0x198, Register32
+ REG_0x19c = 0x19c, Register32
+
+ ENABLE_RST_LOGGING = 0x1a0, Register32
+ RST_LOG_ENTRIES = 0x1a4, Register32
+
+ REG_0x1a8 = 0x1a8, Register32
+ REG_0x1ac = 0x1ac, Register32
+ REG_0x1b0 = 0x1b0, Register32
+
+ REG_0x1b4 = 0x1b4, Register32
+ REG_0x1b8 = 0x1b8, Register32
+ REG_0x1bc = 0x1bc, Register32
+
+ REG_0x1c0 = 0x1c0, Register32
+ REG_0x1c4 = 0x1c4, Register32
+
+ REG_0x1c8 = 0x1c8, Register32
+
+ REG_0x1cc = 0x1cc, Register32
+ REG_0x1d0 = 0x1d0, Register32
+ REG_0x1d4 = 0x1d4, Register32
+ REG_0x1d8 = 0x1d8, Register32
+
+ REG_0x1dc = 0x1dc, Register32
+ REG_0x1e0 = 0x1e0, Register32
+ REG_0x1e4 = 0x1e4, Register32
+ REG_0x1e8 = 0x1e8, Register32
+
+ REG_0x1ec = 0x1ec, Register32
+ REG_0x1f0 = 0x1f0, Register32
+ REG_0x1f4 = 0x1f4, Register32
+ REG_0x1f8 = 0x1f8, Register32
+
+ REG_0x1fc = 0x1fc, Register32
+ REG_0x200 = 0x200, Register32
+
+ REG_0x204 = 0x204, Register32
+ REG_0x208 = 0x208, Register32
+
+ REG_0x20c = 0x20c, Register32
+ REG_0x210 = 0x210, Register32
+ REG_0x214 = 0x214, Register32
+ REG_0x218 = 0x218, Register32
+
+ REG_0x21c = 0x21c, Register32
+ REG_0x220 = 0x220, Register32
+
+ REG_0x224 = 0x224, Register32
+ REG_0x228 = 0x228, Register32
+
+ REG_0x22c = 0x22c, Register32
+ REG_0x230 = 0x230, Register32
+ REG_0x234 = 0x234, Register32
+
+ REG_0x238 = 0x238, Register32
+ REG_0x23c = 0x23c, Register32
+ REG_0x240 = 0x240, Register32
+ REG_0x244 = 0x244, Register32
+ REG_0x248 = 0x248, Register32
+
+ REG_0x24c = 0x24c, Register32
+ REG_0x250 = 0x250, Register32
+ REG_0x254 = 0x254, Register32
+ REG_0x258 = 0x258, Register32
+ REG_0x25c = 0x25c, Register32
+
+ REG_0x260 = 0x260, Register32
+ REG_0x264 = 0x264, Register32
+ REG_0x268 = 0x268, Register32
+ REG_0x26c = 0x26c, Register32
+
+ REG_0x280 = 0x280, Register32
+
+ JPEG_IO_FLAGS = 0x1000, R_JPEG_IO_FLAGS
+ REG_0x1004 = 0x1004, Register32
+ REG_0x1008 = 0x1008, Register32
+ QTBL_SEL = 0x100c, R_QTBL_SEL
+
+ # fixme what _exactly_ does this control
+ HUFFMAN_TABLE = 0x1010, Register32
+ RST_INTERVAL = 0x1014, Register32 # 16 bits effective
+ JPEG_HEIGHT = 0x1018, Register32
+ JPEG_WIDTH = 0x101c, Register32
+
+ COMPRESSED_BYTES = 0x1020, Register32
+ JPEG_OUTPUT_FLAGS = 0x1024, R_JPEG_OUTPUT_FLAGS
+ REG_0x1028 = 0x1028, Register32
+ REG_0x102c = 0x102c, Register32
+
+ BITSTREAM_CORRUPTION = 0x1030, Register32
+ # REG_0x1034 = 0x1034, Register32
+ # REG_0x1038 = 0x1038, Register32
+ # REG_0x103c = 0x103c, Register32
+
+ REG_0x1080 = 0x1080, Register32
+ REG_0x1084 = 0x1084, Register32
+ # REG_0x1088 = 0x1088, Register32
+ REG_0x108c = 0x108c, Register32
+ REG_0x1090 = 0x1090, Register32
+
+ SHIKINO_VERSION_MAGIC0 = 0x10e0, Register32
+ SHIKINO_VERSION_MAGIC1 = 0x10e4, Register32
+ SHIKINO_VERSION_MAGIC2 = 0x10e8, Register32
+ SHIKINO_VERSION_MAGIC3 = 0x10ec, Register32
+ SHIKINO_VERSION_MAGIC4 = 0x10f0, Register32
+ # REG_0x10f4 = 0x10f4, Register32
+ # REG_0x10f8 = 0x10f8, Register32
+ # REG_0x10fc = 0x10fc, Register32
+
+ QTBL = irange(0x1100, 64, 4), Register32
+
+ # todo what's the format?
+ RSTLOG = irange(0x2000, 1024, 4), Register32
diff --git a/tools/proxyclient/m1n1/hw/mca.py b/tools/proxyclient/m1n1/hw/mca.py
new file mode 100644
index 0000000..4c68a39
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/mca.py
@@ -0,0 +1,110 @@
+# SPDX-License-Identifier: MIT
+from ..utils import *
+from enum import IntEnum
+
+class R_STATUS(Register32):
+ EN = 0
+ RST = 1
+
+class R_MCLK_CONF(Register32):
+ SEL = 3, 0
+
+class R_PORT_ENABLES(Register32):
+ CLOCK1 = 1
+ CLOCK2 = 2
+ DATA = 3
+
+class R_PORT_CLKSEL(Register32):
+ SEL = 11, 8
+
+class R_PORT_DATASEL(Register32):
+ TXA0 = 0
+ TXA1 = 2
+ TXA2 = 4
+ TXA3 = 6
+ TXA4 = 8
+ TXA5 = 10
+
+ TXB0 = 1
+ TXB1 = 3
+ TXB2 = 5
+ TXB3 = 7
+ TXB4 = 9
+ TXB5 = 11
+
+class E_SLOT_WIDTH(IntEnum):
+ NONE = 0
+
+ W_16BIT = 0x4
+ W_20BIT = 0x8
+ W_24BIT = 0xc
+ W_32BIT = 0x10
+
+class R_SERDES_CONF(Register32):
+ NSLOTS = 3, 0
+ SLOT_WIDTH = 8, 4, E_SLOT_WIDTH
+
+ BCLK_POL = 10
+ LSB_FIRST = 11
+
+ UNK1 = 12
+ UNK2 = 13
+ IDLE_UNDRIVEN = 14 # TX only
+ NO_DATA_FEEDBACK = 15 # RX only
+
+ SYNC_SEL = 18, 16
+
+class R_INTMASK(Register32):
+ # macOS interested in 0x823c
+ UNK1 = 2 # m
+ UNK2 = 3 # m
+ UNK3 = 4 # m
+ TX_UNDERFLOW = 5 # m
+
+ UNK4 = 9 # m
+ READ_SENSITIVE_UNK1 = 11
+ READ_SENSITIVE_UNK2 = 15 # m
+
+class MCAClusterRegs(RegMap):
+ MCLK_STATUS = 0x0, R_STATUS
+ MCLK_CONF = 0x4, R_MCLK_CONF
+
+ SYNCGEN_STATUS = 0x100, R_STATUS
+ SYNCGEN_MCLK_SEL = 0x104, Register32
+ SYNCGEN_HI_PERIOD = 0x108, Register32
+ SYNCGEN_LO_PERIOD = 0x10c, Register32
+
+ PORT_ENABLES = 0x600, R_PORT_ENABLES
+ PORT_CLK_SEL = 0x604, R_PORT_CLKSEL
+ PORT_DATA_SEL = 0x608, R_PORT_DATASEL
+
+ INTSTATE = 0x700, R_INTMASK
+ INTMASK = 0x704, R_INTMASK
+
+class MCATXSerdesRegs(RegMap):
+ STATUS = 0x0, R_STATUS
+ CONF = 0x4, R_SERDES_CONF
+ BITDELAY = 0x8, Register32
+ CHANMASK = irange(0xc, 4, 4), Register32
+
+class MCARXSerdesRegs(RegMap):
+ STATUS = 0x0, R_STATUS
+ UNK1 = 0x4, Register32
+ CONF = 0x8, R_SERDES_CONF
+ BITDELAY = 0xc, Register32
+ CHANMASK = irange(0x10, 4, 4), Register32
+
+
+class MCACluster:
+ def __init__(self, u, base):
+ self.regs = MCAClusterRegs(u, base)
+ self.txa = MCATXSerdesRegs(u, base + 0x300)
+ self.txb = MCATXSerdesRegs(u, base + 0x500)
+ self.rxa = MCARXSerdesRegs(u, base + 0x200)
+ self.rxb = MCARXSerdesRegs(u, base + 0x400)
+ self.all_regs = [
+ self.regs,
+ self.txa, self.txb,
+ self.rxa, self.rxb
+ ]
+
diff --git a/tools/proxyclient/m1n1/hw/nco.py b/tools/proxyclient/m1n1/hw/nco.py
new file mode 100644
index 0000000..3d1840d
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/nco.py
@@ -0,0 +1,89 @@
+# SPDX-License-Identifier: MIT
+
+__all__ = ["NCO"]
+
+def galois_lfsr(init, poly):
+ state = init
+ for i in range((1 << poly.bit_length() - 1) - 1):
+ if state & 1:
+ state = (state >> 1) ^ (poly >> 1)
+ else:
+ state = (state >> 1)
+ yield state
+
+def gen_lookup_tables():
+ fwd, inv = dict(), dict()
+ lfsr_states = [0] + list(reversed(list(galois_lfsr(0x7ff, 0xa01))))
+ for cycle, sr_state in enumerate(lfsr_states):
+ fwd[cycle + 2] = sr_state
+ inv[sr_state] = cycle + 2
+ return fwd, inv
+
+
+class NCOChannel:
+ def __init__(self, parent, base):
+ self.parent = parent
+ self.base = base
+ self.p = parent.u.proxy
+
+ def enabled(self):
+ return bool(self.p.read32(self.base) & (1<<31))
+
+ def enable(self):
+ self.p.set32(self.base, 1<<31)
+
+ def disable(self):
+ self.p.clear32(self.base, 1<<31)
+
+ def set_rate(self, target):
+ was_enabled = self.enabled()
+ for off, val in enumerate(NCO.calc_regvals(self.parent.fin, target)):
+ self.p.write32(self.base + off*4, val)
+ if was_enabled:
+ self.enable()
+
+ def get_rate(self):
+ return NCO.calc_rate(self.parent.fin,
+ [self.p.read32(self.base + off*4) for off in range(4)]
+ )
+
+ def __repr__(self):
+ return f"<NCO channel @ 0x{self.base:x}>"
+
+
+class NCO:
+ TBL, TBL_INV = gen_lookup_tables()
+
+ @classmethod
+ def calc_rate(self, fin, regvals):
+ try:
+ div = self.TBL_INV[regvals[1] >> 2] << 2 | regvals[1] & 3
+ except KeyError:
+ raise ValueError("bad configuration")
+ inc1 = regvals[2]
+ inc2 = regvals[3] - 0x1_0000_0000
+ return 2 * fin * (inc1 - inc2) // (div * (inc1 - inc2) + inc1)
+
+ @classmethod
+ def calc_regvals(self, fin, fout):
+ div = 2 * fin // fout
+ inc1 = (2 * fin - div * fout)
+ inc2 = inc1 - fout
+ try:
+ return [0, self.TBL[div >> 2] << 2 | div & 3, inc1, inc2 + 0x1_0000_0000]
+ except KeyError:
+ raise ValueError("target rate out of range")
+
+ def __init__(self, u, devpath, stride=0x4000):
+ self.u = u
+ node = u.adt[devpath]
+ self.fin = u.adt["/arm-io"].clock_frequencies[node.clock_ids[0] - 256]
+
+ reg = node.get_reg(0)
+ self.chans = [
+ NCOChannel(self, base)
+ for base in range(reg[0], reg[0] + reg[1], stride)
+ ]
+
+ def __getitem__(self, idx):
+ return self.chans[idx]
diff --git a/tools/proxyclient/m1n1/hw/pmgr.py b/tools/proxyclient/m1n1/hw/pmgr.py
new file mode 100644
index 0000000..85ba247
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/pmgr.py
@@ -0,0 +1,71 @@
+# SPDX-License-Identifier: MIT
+from ..utils import *
+
+class R_PSTATE(Register32):
+ RESET = 31
+ AUTO_ENABLE = 28
+ AUTO_STATE = 27, 24
+ PARENT_MISSING = 11
+ DEV_DISABLE = 10
+ WAS_CLKGATED = 9
+ WAS_PWRGATED = 8
+ ACTUAL = 7, 4
+ DESIRED = 3, 0
+
+class R_PWRGATE(Register32):
+ GATE = 31
+
+class R_CLK_CFG(Register32):
+ UNK31 = 31
+ SRC = 30, 24
+ UNK20 = 20
+ UNK8 = 8
+ UNK0 = 7, 0
+
+class PMGRRegs0(RegMap):
+ PS3 = irange(0x0000, 10, 8), R_PSTATE
+ PS4 = irange(0x0200, 32, 8), R_PSTATE
+ PS5 = irange(0x0300, 32, 8), R_PSTATE
+ PS6 = irange(0x0c00, 2, 8), R_PSTATE
+ PS7 = irange(0x4000, 13, 8), R_PSTATE
+ PS8 = irange(0x8000, 5, 8), R_PSTATE
+ PS9 = irange(0xc000, 7, 8), R_PSTATE
+ PS10 = irange(0x10000, 10, 8), R_PSTATE
+ PS11 = irange(0x100, 32, 8), R_PSTATE
+ PS12 = irange(0x400, 15, 8), R_PSTATE
+
+ PG1 = irange(0x1c010, 16, 8), R_PWRGATE
+ PG1CFG = (irange(0x1c090, 69, 24), irange(0, 6, 4)), Register32
+
+ CPUTVM0 = 0x48000, Register32
+ CPUTVM1 = 0x48c00, Register32
+ CPUTVM2 = 0x48800, Register32
+ CPUTVM3 = 0x48400, Register32
+
+class PMGRRegs1(RegMap):
+ PS0 = irange(0x58, 32, 8), R_PSTATE
+ PS1 = irange(0x4000, 32, 8), R_PSTATE
+ PS2 = irange(0x8000, 32, 8), R_PSTATE
+
+ PG0 = irange(0x1c010, 32, 8), R_PWRGATE
+
+class PMGRRegs2(RegMap):
+ CLK_CFG0 = irange(0x40000, 86, 4), R_CLK_CFG
+ CLK_CFG1 = irange(0x40200, 8, 4), R_CLK_CFG
+ CLK_CFG2 = irange(0x40280, 2, 4), R_CLK_CFG
+
+class PMGR:
+ def __init__(self, u):
+ self.u = u
+ self.p = u.proxy
+ self.iface = u.iface
+ self.node = u.adt["/arm-io/pmgr"]
+ self.regs = [
+ PMGRRegs0(u, self.node.get_reg(0)[0]),
+ PMGRRegs1(u, self.node.get_reg(1)[0]),
+ PMGRRegs2(u, self.node.get_reg(2)[0]),
+ ]
+
+ def dump_all(self):
+ for i in self.regs:
+ i.dump_regs()
diff --git a/tools/proxyclient/m1n1/hw/pmu.py b/tools/proxyclient/m1n1/hw/pmu.py
new file mode 100644
index 0000000..b545df4
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/pmu.py
@@ -0,0 +1,36 @@
+# SPDX-License-Identifier: MIT
+import struct
+
+from ..utils import *
+from .spmi import SPMI
+
+__all__ = ["PMU"]
+
+class PMU:
+
+ def __init__(self, u, adt_path=None):
+ self.u = u
+ if adt_path is None:
+ adt_path = PMU.find_primary_pmu(u.adt)
+
+ self.node = u.adt[adt_path]
+ self.spmi = SPMI(u, adt_path.rpartition('/')[0])
+ self.adt_path = adt_path
+ self.primary = u.adt[adt_path].is_primary == 1
+ self.reg = u.adt[adt_path].reg[0]
+
+ def reset_panic_counter(self):
+ if self.primary:
+ leg_scrpad = self.node.info_leg__scrpad[0]
+ self.spmi.write8(self.reg, leg_scrpad + 2, 0) # error counts
+
+ @staticmethod
+ def find_primary_pmu(adt):
+ for child in adt["/arm-io"]:
+ if child.name.startswith("nub-spmi"):
+ for pmu in child:
+ compat = getattr(pmu, "compatible")[0] if hasattr(pmu, "compatible") else "unset"
+ primary = (getattr(pmu, "is-primary") == 1) if hasattr(pmu, "is-primary") else False
+ if compat == "pmu,spmi" and primary:
+ return pmu._path.removeprefix('/device-tree')
+ raise KeyError(f"primary 'pmu,spmi' node not found")
diff --git a/tools/proxyclient/m1n1/hw/prores.py b/tools/proxyclient/m1n1/hw/prores.py
new file mode 100644
index 0000000..5985e72
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/prores.py
@@ -0,0 +1,250 @@
+# SPDX-License-Identifier: MIT
+from ..utils import *
+from collections import namedtuple
+from enum import IntEnum
+
+
+EncodeNotRawDescriptor = namedtuple('EncodeNotRawDescriptor', [
+ 'flags', # +0x000
+ # [31:16] ?????
+ # 13 ?????
+ # 12 ?????
+ # [11:9] ?????
+ # 8 - enable alpha
+ # 7 - alpha channel bpp
+ # 0 -> 8bpp
+ # 1 -> 16bpp
+ # 6 - something unknown about tiling
+ # [5:4] ?????
+ # [3:2] - chroma subsampling
+ # 00 -> broken?
+ # 01 -> broken?
+ # 10 -> 4:2:2
+ # 11 -> 4:4:4
+ # [1:0] - input bpp
+ # 00 -> 8bpp
+ # 01 -> 16bpp?
+ # 10 -> 16bpp?
+ # 11 -> 16bpp?
+ # the last three all produce slightly differnet outputs
+ # so might be 10/12/14/16?????
+ 'flags2', # +0x004
+ 'output_iova', # +0x008
+ 'max_out_sz', # +0x010
+ 'offset_x', # +0x014
+ 'offset_y', # +0x016
+ 'pix_surface_w_2_', # +0x018
+ 'pix_surface_h_2_', # +0x01a
+ 'pix_surface_w', # +0x01c
+ 'pix_surface_h', # +0x01e
+ 'luma_stride', # +0x020
+ 'chroma_stride', # +0x022
+ 'alpha_stride', # +0x024
+ 'unk_pad_0x26_', # +0x026
+ 'luma_iova', # +0x028
+ 'pix_plane0_tileheader_thing_', # +0x030
+ 'chroma_iova', # +0x038
+ 'pix_plane1_tileheader_thing_', # +0x040
+ 'alpha_iova', # +0x048
+ 'pix_plane2_tileheader_thing_', # +0x050
+ 'frame_header_sz', # +0x058
+ 'unk_pad_0x5a_', # +0x05a
+ 'bitstream_version', # +0x05b
+ 'encoder_identifier', # +0x05c
+ 'pix_surface_w_byteswap_', # +0x060
+ 'pix_surface_h_byteswap_', # +0x062
+ 'chroma_format_interlace_mode', # +0x064
+ 'aspect_ratio_frame_rate', # +0x065
+ 'color_primaries', # +0x066
+ 'transfer_characteristic', # +0x067
+ 'matrix_coefficients', # +0x068
+ 'alpha_channel_type', # +0x069
+ 'frame_hdr_reserved14', # +0x06a
+ 'unk_pad_0x6c_', # +0x06c
+ 'deprecated_number_of_slices', # +0x0ec
+ 'log2_desired_slice_size_in_mb', # +0x0ee
+ 'quantization_index', # +0x0ef
+ 'unk_0xf0_', # +0x0f0
+ 'unk_0xf2_', # +0x0f2
+ 'unk_0xf4_', # +0x0f4
+ 'unk_0xfc_', # +0x0fc
+ 'unk_0x100_0_', # +0x100
+ 'unk_0x100_1_', # +0x104
+ 'unk_0x100_2_', # +0x108
+ 'unk_0x100_3_', # +0x10c
+ 'unk_0x110_0_', # +0x110
+ 'unk_0x110_1_', # +0x114
+ 'unk_0x110_2_', # +0x118
+ 'unk_0x110_3_', # +0x11c
+ 'unk_0x110_4_', # +0x120
+ 'unk_0x110_5_', # +0x124
+ 'unk_0x110_6_', # +0x128
+ 'unk_0x110_7_', # +0x12c
+ 'unk_0x110_8_', # +0x130
+ 'unk_0x110_9_', # +0x134
+ 'unk_0x110_10_', # +0x138
+ 'unk_0x110_11_', # +0x13c
+ 'unk_0x110_12_', # +0x140
+ 'unk_0x110_13_', # +0x144
+ 'unk_0x110_14_', # +0x148
+ 'unk_0x110_15_', # +0x14c
+ 'quant_table_sel', # +0x150
+ # upper nibble: quality / table index
+ # lower nibble UNKNOWN!
+ 'unk_pad_0x154_', # +0x154
+])
+ENCODE_NOT_RAW_STRUCT = "<IIQIHHHHHHHHH2sQQQQQQH1sBIHHBBBBBB2s128sHBBHHQIIIIIIIIIIIIIIIIIIIIII44s"
+
+
+class ProResRegs(RegMap):
+ # something reads
+ REG_0x0 = 0x000, Register32
+ MODE = 0x008, Register32 # 4 bits
+ IRQ_ENABLE = 0x00c, Register32 # 2 bits
+ IRQ_STATUS = 0x010, Register32
+
+ ST0 = 0x014, Register32 # interrupt handler reads
+ ST1 = 0x018, Register32 # interrupt handler reads
+ REG_0x1c = 0x01c, Register32 # interrupt handler reads
+ REG_0x38 = 0x038, Register32 # exists, maybe RO
+ REG_0x3c = 0x03c, Register32 # interrupt handler reads
+ REG_0x40 = 0x040, Register32 # exists, maybe RO, looks like 0x44
+ REG_0x44 = 0x044, Register32 # interrupt handler reads
+ REG_0x48 = 0x048, Register32 # exists, maybe RO, looks like 0x44
+ REG_0x4c = 0x04c, Register32 # exists, maybe RO, looks like 0x44
+ REG_0x50 = 0x050, Register32 # exists, maybe RO, looks like 0x44
+ REG_0x54 = 0x054, Register32 # exists, maybe RO, looks like 0x44
+
+ DR_SIZE = 0x100, Register32
+ DR_ADDR_LO = 0x104, Register32
+ DR_ADDR_HI = 0x108, Register32
+ DR_HEAD = 0x10c, Register32 # bit24 is special, something about wrapping around?
+ DR_TAIL = 0x110, Register32
+
+ # This giant block may or may not be touched by tunables
+ # Function is all unknown
+ REG_0x114 = 0x114, Register32 # can set bits 0000FFFF
+ REG_0x118 = 0x118, Register32 # can set bits 07FF07FF
+
+ REG_0x134 = 0x134, Register32 # can set bits 00000003
+
+ REG_0x144 = 0x144, Register32 # can set bits 00000001
+ REG_0x148 = 0x148, Register32 # can set bits 00000001
+
+ REG_0x160 = 0x160, Register32 # can set bits BFFF3FFF
+ REG_0x164 = 0x164, Register32 # can set bits 07FF07FF
+
+ REG_0x170 = 0x170, Register32 # can set bits BFFF3FFF
+ REG_0x174 = 0x174, Register32 # can set bits 07FF07FF
+
+ REG_0x180 = 0x180, Register32 # can set bits BFFF3FFF
+ REG_0x184 = 0x184, Register32 # can set bits 07FF07FF
+
+ REG_0x190 = 0x190, Register32 # can set bits BFFF3FFF
+ REG_0x194 = 0x194, Register32 # can set bits 000000FF
+ REG_0x198 = 0x198, Register32 # RO? init value 07FB066F
+
+ REG_0x1a0 = 0x1a0, Register32 # can set bits BFFF3FFF
+ REG_0x1a4 = 0x1a4, Register32 # can set bits 000000FF
+ REG_0x1a8 = 0x1a8, Register32 # RO? init value 037C03EE
+
+ REG_0x1b0 = 0x1b0, Register32 # can set bits BFFF3FFF
+ REG_0x1b4 = 0x1b4, Register32 # can set bits 000000FF
+ REG_0x1b8 = 0x1b8, Register32 # RO? init value 04E00377
+
+ REG_0x1c0 = 0x1c0, Register32 # can set bits BFFF3FFF
+ REG_0x1c4 = 0x1c4, Register32 # can set bits 000000FF
+ REG_0x1c8 = 0x1c8, Register32 # RO? init value 051C00DA
+
+ REG_0x1d0 = 0x1d0, Register32 # can set bits BFFF3FFF
+ REG_0x1d4 = 0x1d4, Register32 # can set bits 000000FF
+ REG_0x1d8 = 0x1d8, Register32 # can set bits 000000FF
+ REG_0x1dc = 0x1dc, Register32 # can set bits 00FFFFFF
+
+ REG_0x1ec = 0x1ec, Register32 # can set bits FFFFFFFF
+
+ REG_0x270 = 0x270, Register32 # can set bits BFFF3FFF
+ REG_0x274 = 0x274, Register32 # can set bits 07FF07FF
+ REG_0x278 = 0x278, Register32 # can set bits FFFFFFC0
+ REG_0x27c = 0x27c, Register32 # can set bits 000003FF
+ REG_0x280 = 0x280, Register32 # can set bits FFFFFFC0
+ REG_0x284 = 0x284, Register32 # can set bits FFFFFFC0
+ REG_0x28c = 0x28c, Register32 # can set bits FFFFFFC0
+
+ REG_0x290 = 0x290, Register32 # can set bits BFFF3FFF
+ REG_0x294 = 0x294, Register32 # can set bits 000000FF
+ REG_0x298 = 0x298, Register32 # RO? init value 07FB066F
+
+ REG_0x2a0 = 0x2a0, Register32 # can set bits BFFF3FFF
+ REG_0x2a4 = 0x2a4, Register32 # can set bits 000000FF
+ REG_0x2a8 = 0x2a8, Register32 # RO? init value 037C03EE
+
+ REG_0x2b0 = 0x2b0, Register32 # can set bits BFFF3FFF
+ REG_0x2b4 = 0x2b4, Register32 # can set bits 000000FF
+ REG_0x2b8 = 0x2b8, Register32 # RO? init value 04E00377
+
+ REG_0x2c0 = 0x2c0, Register32 # can set bits BFFF3FFF
+ REG_0x2c4 = 0x2c4, Register32 # can set bits 000000FF
+ REG_0x2c8 = 0x2c8, Register32 # RO? init value 051C00DA
+
+ REG_0x2d0 = 0x2d0, Register32 # can set bits FFFFFFFD, CANNOT clear 00000011
+ REG_0x2d4 = 0x2d4, Register32 # can set bits 00000001
+ REG_0x2d8 = 0x2d8, Register32 # can set bits FFFF0007
+ REG_0x2dc = 0x2dc, Register32 # RO? init value 07FB066F
+ REG_0x2e0 = 0x2e0, Register32 # can set bits 07FF07FF
+
+ REG_0x2f0 = 0x2f0, Register32 # can set bits FFFFFFFF
+
+ REG_0x2f8 = 0x2f8, Register32 # can set bits FFFFFFFD, CANNOT clear 00000011
+ REG_0x2fc = 0x2fc, Register32 # can set bits 00000001
+ REG_0x300 = 0x300, Register32 # can set bits FFFF0007
+ REG_0x304 = 0x304, Register32 # RO? init value 037C03EE
+ REG_0x308 = 0x308, Register32 # can set bits 07FF07FF
+
+ REG_0x318 = 0x318, Register32 # can set bits FFFFFFFF
+
+ REG_0x320 = 0x320, Register32 # can set bits FFFFFFFD, CANNOT clear 00000011
+ REG_0x324 = 0x324, Register32 # can set bits 00000001
+ REG_0x328 = 0x328, Register32 # can set bits FFFF0007
+ REG_0x32c = 0x32c, Register32 # RO? init value 04E00377
+ REG_0x330 = 0x330, Register32 # can set bits 07FF07FF
+
+ REG_0x340 = 0x340, Register32 # can set bits FFFFFFFF
+
+ REG_0x350 = 0x350, Register32 # can set bits BFFF3FFF
+ REG_0x354 = 0x354, Register32 # can set bits 07FF07FF
+ REG_0x358 = 0x358, Register32 # can set bits FFFFFFC0
+ REG_0x35c = 0x35c, Register32 # can set bits 000003FF
+ REG_0x360 = 0x360, Register32 # can set bits FFFFFFC0
+ REG_0x364 = 0x364, Register32 # can set bits FFFFFFC0
+ REG_0x368 = 0x368, Register32 # can set bits FFFFFFC0
+
+ REG_0x370 = 0x370, Register32 # can set bits BFFF3FFF
+ REG_0x374 = 0x374, Register32 # can set bits 07FF07FF
+
+
+ QUANT_LUMA_EHQ = irange(0x0800, 32, 4), Register32
+ QUANT_LUMA_HQ = irange(0x0880, 32, 4), Register32
+ QUANT_LUMA_NQ = irange(0x0900, 32, 4), Register32
+ QUANT_LUMA_LT = irange(0x0980, 32, 4), Register32
+ QUANT_LUMA_PROXY = irange(0x0A00, 32, 4), Register32
+ QUANT_CHROMA_EHQ = irange(0x1000, 32, 4), Register32
+ QUANT_CHROMA_HQ = irange(0x1080, 32, 4), Register32
+ QUANT_CHROMA_NQ = irange(0x1100, 32, 4), Register32
+ QUANT_CHROMA_LT = irange(0x1180, 32, 4), Register32
+ QUANT_CHROMA_PROXY = irange(0x1200, 32, 4), Register32
+
+ # wtf, writing to this doesn't actually work? do we have to enable it?
+ DC_QUANT_SCALE = irange(0x1800, 112, 4), Register32
+
+ REG_0x19c0 = 0x19c0, Register32 # unknown, all 1s, RO?
+ REG_0x19c4 = 0x19c4, Register32 # unknown, all 1s, RO?
+ REG_0x19c8 = 0x19c8, Register32 # unknown, all 1s, RO?
+ REG_0x19cc = 0x19cc, Register32 # unknown, all 1s, RO?
+ REG_0x19d0 = 0x19d0, Register32 # unknown, all 1s, RO?
+ REG_0x19d4 = 0x19d4, Register32 # unknown, all 1s, RO?
+ REG_0x19d8 = 0x19d8, Register32 # unknown, all 1s, RO?
+ REG_0x19dc = 0x19dc, Register32 # unknown, can set bits 00000001
+
+ # Unknown, inits to 0x12345678, can R/W
+ REG_0x1A00 = 0x1a00, Register32
diff --git a/tools/proxyclient/m1n1/hw/scaler.py b/tools/proxyclient/m1n1/hw/scaler.py
new file mode 100644
index 0000000..714f366
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/scaler.py
@@ -0,0 +1,212 @@
+# SPDX-License-Identifier: MIT
+from ..utils import *
+from enum import IntEnum
+
+
+### NOTE: This is "MSR10j" (M1 Max), and there definitely *ARE* differences from M1
+
+class R_IRQS(Register32):
+ DONE = 0
+ DBGSTS_ERROR = 1
+ READ_ERROR = 3
+ # This doesn't actually trigger on bad IOVAs?
+ WRITE_ERROR_MAYBE = 4
+ DECOMPRESSION_ERROR = 9
+ CONTEXT_SWITCH = 10
+ _BIT11 = 11
+ AXI_ERROR = 12
+ _BIT13 = 13
+
+
+class E_ROTATE(IntEnum):
+ # clockwise rotations
+ ROT_0 = 0
+ ROT_90 = 1
+ ROT_180 = 2
+ ROT_270 = 3
+
+
+class R_FLIP_ROTATE(Register32):
+ ROTATE = 1, 0, E_ROTATE
+ FLIP_UPDOWN = 2
+ FLIP_LEFTRIGHT = 3
+
+
+class R_SCALE_FLAGS(Register32):
+ EN = 0
+ MAKE_THE_OUTPUT_YELLOW = 1
+ # only when bit1 is set, only on H scaling
+ MAKE_A_BLUE_LINE_APPEAR = 4
+
+
+class ScalerMainRegs(RegMap):
+ # on startup 1 will be written followed by 0
+ # but it's not clear if that actually does anything
+ HW_VERSION = 0x00000, Register32
+ # bit0 = normal, bit1 = apiodma related
+ # if things are reset here, reading all other regs will *HANG*
+ RESET = 0x00004, Register32
+ # can set 0x1f00
+ RESET_APIODMA_RELATED = 0x00008, Register32
+ IS_RUNNING = 0x0000c, Register32
+ # writable, can set to 0xfff
+ REG_0x10 = 0x00010, Register32
+ REGISTER_FIFO_AVAILABILITY = 0x00014, Register32
+ # XNU sets 0x121b, we can at most set 0x3e1b
+ IRQ_ENABLE = 0x00018, R_IRQS
+ MSR_GLBL_IRQSTS = 0x0001c, R_IRQS
+ FRAME_COUNT = 0x00020, Register32
+
+ # can set 7
+ REG_0x58 = 0x00058, Register32
+
+ # can set 0xffff
+ REG_0x74 = 0x00074, Register32
+
+ # 1, or 3 if readonly??
+ START = 0x00080, Register32
+ # can set all bits
+ REG_0x84 = 0x00084, Register32
+ # can set all bits
+ REG_0x88 = 0x00088, Register32
+ # can set 0x8000ffff
+ REG_0x8c = 0x0008c, Register32
+
+ # can set all bits
+ REG_0x98 = 0x00098, Register32
+ # 0x3f3d?
+ MSR_CTRL_DBGSTS = 0x0009c, Register32
+ # can set 3
+ REG_0xa0 = 0x000a0, Register32
+ PROFILING_RELATED = 0x000a4, Register32
+
+ # Can set bits 0/1/2
+ # Does something breaking horizontal scaling
+ # bit2 seems to affect alpha output
+ PIXEL_AVERAGING = 0x000e4, Register32
+
+ TRANSFORM_ID = 0x00110, Register32
+
+ RDMA_THING0 = 0x00180, Register32
+ RDMA_THING1 = 0x00184, Register32
+ RDMA_THING2 = 0x00188, Register32
+ RDMA_THING3 = 0x0018c, Register32
+ RDMA_THING4 = 0x00190, Register32
+
+ # there's probably another source plane existing?
+ SRC_PLANE0_LO = 0x00198, Register32
+ SRC_PLANE0_HI = 0x0019c, Register32
+ SRC_PLANE1_LO = 0x001a0, Register32
+ SRC_PLANE1_HI = 0x001a4, Register32
+ SRC_PLANE2_LO = 0x001a8, Register32
+ SRC_PLANE2_HI = 0x001ac, Register32
+
+ SRC_PLANE0_COMPRESSEDTHING_LO = 0x001b8, Register32
+ SRC_PLANE0_COMPRESSEDTHING_HI = 0x001bc, Register32
+ SRC_PLANE1_COMPRESSEDTHING_LO = 0x001c0, Register32
+ SRC_PLANE1_COMPRESSEDTHING_HI = 0x001c4, Register32
+ SRC_PLANE2_COMPRESSEDTHING_LO = 0x001c8, Register32
+ SRC_PLANE2_COMPRESSEDTHING_HI = 0x001cc, Register32
+
+ SRC_PLANE0_STRIDE = 0x001d8, Register32
+ SRC_PLANE1_STRIDE = 0x001dc, Register32
+ SRC_PLANE2_STRIDE = 0x001e0, Register32
+
+ # seems to be in "pixels"
+ SRC_PLANE0_OFFSET = 0x001e8, Register32
+ SRC_PLANE1_OFFSET = 0x001ec, Register32
+ SRC_PLANE2_OFFSET = 0x001f0, Register32
+
+ SRC_SWIZZLE = 0x001f8, Register32
+ SRC_W = 0x001fc, Register32
+ SRC_H = 0x00200, Register32
+ CACHE_HINTS_THING0 = irange(0x00204, 4, 4), Register32
+ CACHE_HINTS_THING1 = irange(0x00214, 4, 4), Register32
+ TUNABLES_THING0 = irange(0x00224, 4, 4), Register32
+ SRC_SIZE_THING2 = 0x00234, Register32
+ SRC_SIZE_THING3 = 0x00238, Register32
+ SRC_SIZE_THING4 = 0x0023c, Register32
+
+ SRC_SIZE_THING5 = 0x00244, Register32
+ SRC_SIZE_THING6 = 0x00248, Register32
+ SRC_SIZE_THING7 = 0x0024c, Register32
+
+ WDMA_THING0 = 0x00280, Register32
+ WDMA_THING1 = 0x00284, Register32
+ WDMA_THING2 = 0x00288, Register32
+ WDMA_THING3 = 0x0028c, Register32
+ DST_PLANE0_LO = 0x00290, Register32
+ DST_PLANE0_HI = 0x00294, Register32
+ DST_PLANE1_LO = 0x00298, Register32
+ DST_PLANE1_HI = 0x0029c, Register32
+ DST_PLANE2_LO = 0x002a0, Register32
+ DST_PLANE2_HI = 0x002a4, Register32
+ DST_PLANE0_COMPRESSEDTHING_LO = 0x002a8, Register32
+ DST_PLANE0_COMPRESSEDTHING_HI = 0x002ac, Register32
+ DST_PLANE1_COMPRESSEDTHING_LO = 0x002b0, Register32
+ DST_PLANE1_COMPRESSEDTHING_HI = 0x002b4, Register32
+ DST_PLANE2_COMPRESSEDTHING_LO = 0x002b8, Register32
+ DST_PLANE2_COMPRESSEDTHING_HI = 0x002bc, Register32
+ DST_PLANE0_STRIDE = 0x002c0, Register32
+ DST_PLANE1_STRIDE = 0x002c4, Register32
+ DST_PLANE2_STRIDE = 0x002c8, Register32
+ DST_PLANE0_OFFSET = 0x002cc, Register32
+ DST_PLANE1_OFFSET = 0x002d0, Register32
+ DST_PLANE2_OFFSET = 0x002d4, Register32
+ DST_SWIZZLE = 0x002d8, Register32
+ DST_W = 0x002dc, Register32
+ DST_H = 0x002e0, Register32
+ # uhh is there a macos bug with these? last val always overwritten
+ CACHE_HINTS_THING2 = irange(0x002e4, 3, 4), Register32
+ CACHE_HINTS_THING3 = irange(0x002f0, 3, 4), Register32
+ TUNABLES_THING1 = irange(0x002fc, 3, 4), Register32
+ DST_SIZE_THING2 = 0x00308, Register32
+ DST_SIZE_THING3 = 0x0030c, Register32
+ DST_SIZE_THING4 = 0x00310, Register32
+ DST_SIZE_THING5 = 0x00314, Register32
+ DST_SIZE_THING6 = 0x00318, Register32
+ DST_SIZE_THING7 = 0x0031c, Register32
+
+ FLIP_ROTATE = 0x00380, R_FLIP_ROTATE
+
+ # can set bit 0/1
+ # the output obviously changes when this is set
+ PSEUDO_LINEAR_SCALING = 0x00480, Register32
+
+ SCALE_V_FLAGS = 0x01000, R_SCALE_FLAGS
+ # No idea what a DDA is? Q1.22 or U1.22 (23 bits total)
+ # Also macOS doesn't touch a bunch of the V ones (uses H instead???)
+ SCALE_V_DDA_THING0 = 0x01004, Register32
+ SCALE_V_DDA_THING1 = 0x01008, Register32
+ # Q4.22 or U4.22 (26 bits total)
+ SCALE_V_RATIO_0 = 0x0100c, Register32
+ SCALE_V_RATIO_1 = 0x01010, Register32
+ SCALE_V_RATIO_2 = 0x01014, Register32
+ SCALE_V_RATIO_3 = 0x01018, Register32
+ SCALE_V_DDA_THING2 = 0x0101c, Register32
+ SCALE_V_RATIO_4 = 0x01020, Register32
+ SCALE_V_RATIO_5 = 0x01024, Register32
+
+ # 9 taps, 32 phases polyphase resampling filter
+ # Q4.12 (16-bit total) fixed point filter coeffs
+ # packed into 32-bit registers, 3 sets of filters total (chroma/luma/alpha??)
+ # exact ordering and arithmetic performed not yet clear
+ SCALE_FILTER_V_BLOCK0 = irange(0x01400, 9 * 32, 4), Register32
+ SCALE_FILTER_V_BLOCK1 = irange(0x01c00, 9 * 32 // 2, 4), Register32
+
+ SCALE_H_FLAGS = 0x02000, R_SCALE_FLAGS
+ # No idea what a DDA is? Q1.22 or U1.22 (23 bits total)
+ SCALE_H_DDA_THING0 = 0x02004, Register32
+ SCALE_H_DDA_THING1 = 0x02008, Register32
+ # Q4.22 or U4.22 (26 bits total)
+ SCALE_H_RATIO_0 = 0x0200c, Register32
+ SCALE_H_RATIO_1 = 0x02010, Register32
+ SCALE_H_RATIO_2 = 0x02014, Register32
+ SCALE_H_RATIO_3 = 0x02018, Register32
+ SCALE_H_DDA_THING2 = 0x0201c, Register32
+ SCALE_H_RATIO_4 = 0x02020, Register32
+ SCALE_H_RATIO_5 = 0x02024, Register32
+
+ # 15 taps, 32 phases polyphase resampling filter
+ SCALE_FILTER_H_BLOCK0 = irange(0x02400, 15 * 32, 4), Register32
+ SCALE_FILTER_H_BLOCK1 = irange(0x02c00, 15 * 32 // 2, 4), Register32
diff --git a/tools/proxyclient/m1n1/hw/sep.py b/tools/proxyclient/m1n1/hw/sep.py
new file mode 100644
index 0000000..f206a71
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/sep.py
@@ -0,0 +1,169 @@
+# SPDX-License-Identifier: MIT
+import struct
+from collections import defaultdict, deque
+from enum import IntEnum
+
+from ..trace.asc import ASCRegs
+from ..utils import *
+
+
+class BootRomMsg(IntEnum):
+ GET_STATUS = 2
+ BOOT_TZ0 = 5
+ BOOT_IMG4 = 6
+ SET_SHMEM = 0x18
+
+
+class BootRomStatus(IntEnum):
+ STATUS_OK = 0x66
+ STATUS_BOOT_TZ0_DONE = 0x69
+ STATUS_BOOT_IMG4_DONE = 0x6A
+ STATUS_BOOT_UNK_DONE = 0xD2
+
+
+class SEPMessage(Register64):
+ EP = 7, 0
+ TAG = 15, 8
+ TYPE = 23, 16
+ PARAM = 31, 24
+ DATA = 63, 32
+
+
+# TODO: make this class actually own the shared memory instead of just
+# generating a static buffer if we actually need to read/write to
+# individual items inside the shmem buffer
+class SEPShMem:
+ def __init__(self):
+ self.items = []
+ self.offset = 0x4000
+
+ def add_item(self, name, data, min_size=0):
+ sz = align_up(len(data) + 4, 0x4000)
+ sz = max(sz, min_size)
+ self.items.append((name, self.offset, sz, struct.pack("<I", len(data)) + data))
+ self.offset += sz
+
+ def finalize(self):
+ bfr = bytearray(b"\x00" * self.offset)
+ for i, (name, offset, sz, data) in enumerate(self.items):
+ bfr[i * 16 : i * 16 + 12] = struct.pack("<4sII", name, sz, offset)
+ bfr[offset : offset + len(data)] = data
+
+ cnt = len(self.items)
+ bfr[cnt * 16 : cnt * 16 + 4] = b"llun" # null
+
+ return bfr
+
+
+class SEP:
+ SHMEM_IOVA = 0xBEEF0000
+ FW_IOVA = 0xDEAD0000
+
+ def __init__(self, proxy, iface, utils):
+ self.i = iface
+ self.p = proxy
+ self.u = utils
+
+ self.sep_base = self.u.adt["/arm-io/sep"].get_reg(0)[0]
+ self.dart_base = self.u.adt["/arm-io/dart-sep"].get_reg(0)[0]
+
+ self.asc = ASCRegs(self.u, self.sep_base)
+
+ self.dart_handle = self.p.dart_init(self.dart_base, 0)
+
+ self.epnum2name = {}
+ self.epname2num = {}
+ self.msgs = defaultdict(deque)
+
+ def map_sepfw(self):
+ sepfw_addr, sepfw_size = self.u.adt["/chosen/memory-map"].SEPFW
+ self.p.dart_map(self.dart_handle, self.FW_IOVA, sepfw_addr, sepfw_size)
+
+ def unmap_sepfw(self):
+ _, sepfw_size = self.u.adt["/chosen/memory-map"].SEPFW
+ self.p.dart_unmap(self.dart_handle, self.FW_IOVA, sepfw_size)
+
+ def create_shmem(self):
+ shmem = SEPShMem()
+
+ # PNIC - panic buffer
+ shmem.add_item(b"CINP", b"\x00", 0x8000)
+
+ # ALPO / SIPS - unknown img4-like blobs from the ADT
+ addr, sz = self.u.adt["/chosen/boot-object-manifests"].lpol
+ shmem.add_item(b"OPLA", self.i.readmem(addr, sz))
+ addr, sz = self.u.adt["/chosen/boot-object-manifests"].ibot
+ shmem.add_item(b"IPIS", self.i.readmem(addr, sz))
+
+ bfr = shmem.finalize()
+ sz = align_up(len(bfr), 0x4000)
+ self.shmem = self.u.heap.memalign(0x4000, 0x30000)
+ self.i.writemem(self.shmem, bfr)
+ self.p.dart_map(self.dart_handle, self.SHMEM_IOVA, self.shmem, 0x30000)
+
+ def boot(self):
+ self.create_shmem()
+ self.map_sepfw()
+
+ self.send_msg(SEPMessage(EP=0xFF, TYPE=BootRomMsg.GET_STATUS))
+ self.expect_msg(0xFF, BootRomStatus.STATUS_OK)
+
+ self.send_msg(SEPMessage(EP=0xFF, TYPE=BootRomMsg.BOOT_TZ0))
+ self.expect_msg(0xFF, BootRomStatus.STATUS_BOOT_TZ0_DONE)
+ self.expect_msg(0xFF, BootRomStatus.STATUS_BOOT_UNK_DONE)
+
+ self.send_msg(SEPMessage(EP=0xFF, TYPE=BootRomMsg.GET_STATUS))
+ self.expect_msg(0xFF, BootRomStatus.STATUS_OK)
+
+ self.send_msg(
+ SEPMessage(EP=0xFF, TYPE=BootRomMsg.BOOT_IMG4, DATA=self.FW_IOVA >> 0xC)
+ )
+ self.send_msg(
+ SEPMessage(EP=0xFE, TYPE=BootRomMsg.SET_SHMEM, DATA=self.SHMEM_IOVA >> 0xC)
+ )
+
+ self.expect_msg(0xFF, BootRomStatus.STATUS_BOOT_IMG4_DONE)
+
+ self.unmap_sepfw()
+
+ def expect_msg(self, ep, type):
+ msg = self.recv_msg(ep, block=True)
+ if msg.TYPE != type:
+ raise ValueError(
+ f"Expected type 0x{type:x} but got message with type 0x{msg.TYPE:x}"
+ )
+
+ def send_msg(self, msg):
+ self.asc.INBOX0 = msg.value
+ self.asc.INBOX1 = 0
+
+ def _recv_single_msg(self):
+ msg = SEPMessage(self.asc.OUTBOX0.val)
+ _ = self.asc.OUTBOX1.val
+ return msg
+
+ def _try_recv_msgs(self):
+ while not self.asc.OUTBOX_CTRL.reg.EMPTY:
+ msg = self._recv_single_msg()
+ self.msgs[msg.EP].append(msg)
+ self._handle_ep_discovery()
+
+ def _handle_ep_discovery(self):
+ while len(self.msgs[0xFD]):
+ msg = self.msgs[0xFD].popleft()
+ if msg.TYPE == 0:
+ cs = "".join(
+ [chr((msg.DATA >> (i * 8)) & 0xFF) for i in range(3, -1, -1)]
+ )
+ self.epnum2name[msg.PARAM] = cs
+ self.epname2num[cs] = msg.PARAM
+
+ def recv_msg(self, ep, block=False):
+ self._try_recv_msgs()
+ while block and len(self.msgs[ep]) < 1:
+ self._try_recv_msgs()
+
+ if len(self.msgs[ep]):
+ return self.msgs[ep].popleft()
+ else:
+ return None
diff --git a/tools/proxyclient/m1n1/hw/spi.py b/tools/proxyclient/m1n1/hw/spi.py
new file mode 100644
index 0000000..1fa9be5
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/spi.py
@@ -0,0 +1,147 @@
+# SPDX-License-Identifier: MIT
+from ..utils import *
+
+__all__ = ["SPIRegs"]
+
+class R_CTRL(Register32):
+ RX_FIFO_RESET = 3
+ TX_FIFO_RESET = 2
+ RUN = 0
+
+class R_CFG(Register32):
+ # impl: 002fb1e6
+ IE_TX_COMPLETE = 21
+ b19 = 19
+ FIFO_THRESH = 18, 17
+ # 0 = 8 bytes
+ # 1 = 4 bytes
+ # 2 = 1 byte
+ # 3 = disabled
+ WORD_SIZE = 16, 15
+ # 0 = 8bit
+ # 1 = 16bit
+ # 2 = 32bit
+ LSB_FIRST = 13
+ b12 = 12
+ IE_RX_THRESH = 8
+ IE_RX_COMPLETE = 7
+ MODE = 6, 5
+ # 0 = polled
+ # 1 = irq
+ CPOL = 2
+ CPHA = 1
+
+class R_STATUS(Register32):
+ TX_COMPLETE = 22
+ TXRX_THRESH = 1 # updated if MODE == 1
+ RX_COMPLETE = 0
+
+class R_PIN(Register32):
+ CS = 1
+ KEEP_MOSI = 0
+
+class R_CLKDIV(Register32):
+ DIVIDER = 10, 0 # SPI freq = CLK / (DIVIDER + 1)
+
+class R_INTER_DELAY(Register32):
+ DELAY = 15, 0
+
+class R_FIFOSTAT(Register32):
+ LEVEL_RX = 31, 24
+ RX_EMPTY = 20
+ LEVEL_TX = 15, 8
+ TX_FULL = 4
+
+class R_IRQ_XFER(Register32):
+ TX_XFER_DONE = 1
+ RX_XFER_DONE = 0
+
+class R_IRQ_FIFO(Register32):
+ TX_OVERFLOW = 17
+ RX_UNDERRUN = 16
+ TX_EMPTY = 9
+ RX_FULL = 8
+ TX_THRESH = 5
+ RX_THRESH = 4
+
+class R_XFSTATUS(Register32):
+ SR_FULL = 26
+ SHIFTING = 20
+ STATE = 17, 16
+ UNK = 0
+
+class R_DIVSTATUS(Register32):
+ COUNT2 = 31, 16
+ COUNT1 = 15, 0
+
+class R_SHIFTCFG(Register32):
+ OVERRIDE_CS = 24
+ BITS = 21, 16
+ RX_ENABLE = 11
+ TX_ENABLE = 10
+ CS_AS_DATA = 9
+ AND_CLK_DATA = 8
+ #? = 2 # needs to be 1 for RX to not break
+ CS_ENABLE = 1
+ CLK_ENABLE = 0
+
+class R_PINCFG(Register32):
+ MOSI_INIT_VAL = 10
+ CS_INIT_VAL = 9
+ CLK_INIT_VAL = 8
+ KEEP_MOSI = 2
+ KEEP_CS = 1
+ KEEP_CLK = 0
+
+class R_DELAY(Register32):
+ DELAY = 31, 16
+ MOSI_VAL = 12
+ CS_VAL = 10
+ SCK_VAL = 8
+ SET_MOSI = 6
+ SET_CS = 5
+ SET_SCK = 4
+ NO_INTERBYTE = 1
+ ENABLE = 0
+
+class R_SCKCFG(Register32):
+ PERIOD = 31, 16
+ PHASE1 = 9
+ PHASE0 = 8
+ RESET_TO_IDLE = 4
+
+class R_SCKPHASES(Register32):
+ PHASE1_START = 31, 16
+ PHASE0_START = 15, 0
+
+class SPIRegs(RegMap):
+ CTRL = 0x00, R_CTRL
+ CFG = 0x04, R_CFG
+ STATUS = 0x08, R_STATUS
+ PIN = 0x0C, R_PIN
+ TXDATA = 0x10, Register32
+ RXDATA = 0x20, Register32
+ CLKDIV = 0x30, R_CLKDIV
+ RXCNT = 0x34, Register32
+ INTER_DELAY = 0x38, R_INTER_DELAY
+ TXCNT = 0x4C, Register32
+ FIFOSTAT = 0x10C, R_FIFOSTAT
+
+ IE_XFER = 0x130, R_IRQ_XFER
+ IF_XFER = 0x134, R_IRQ_XFER
+ IE_FIFO = 0x138, R_IRQ_FIFO
+ IF_FIFO = 0x13c, R_IRQ_FIFO
+
+ SHIFTCFG = 0x150, R_SHIFTCFG
+ PINCFG = 0x154, R_PINCFG
+
+ DELAY_PRE = 0x160, R_DELAY
+ SCKCFG = 0x164, R_SCKCFG
+ DELAY_POST = 0x168, R_DELAY
+
+ SCKPHASES = 0x180, R_SCKPHASES
+
+ UNK_PHASE = 0x18c, Register32 # probably MISO sample point
+
+ XFSTATUS = 0x1c0, R_XFSTATUS
+ DIVSTATUS = 0x1e0, R_DIVSTATUS
diff --git a/tools/proxyclient/m1n1/hw/spmi.py b/tools/proxyclient/m1n1/hw/spmi.py
new file mode 100644
index 0000000..a312f61
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/spmi.py
@@ -0,0 +1,98 @@
+# SPDX-License-Identifier: MIT
+import struct
+
+from ..utils import *
+
+__all__ = ["SPMI"]
+
+CMD_EXT_WRITE = 0x00
+CMD_EXT_READ = 0x20
+CMD_EXT_WRITEL = 0x30
+CMD_EXT_READL = 0x38
+CMD_WRITE = 0x40
+CMD_READ = 0x60
+CMD_ZERO_WRITE = 0x80
+
+class R_CMD(Register32):
+ REG = 31, 16
+ ACTIVE = 15
+ SLAVE_ID = 14, 8
+ CMD = 7, 0
+
+class R_STATUS(Register32):
+ RX_EMPTY = 24
+ RX_COUNT = 23, 16
+ TX_EMPTY = 8
+ TX_COUNT = 7, 0
+
+class SPMIRegs(RegMap):
+ STATUS = 0x00, R_STATUS
+ CMD = 0x04, R_CMD
+ REPLY = 0x08, Register32
+ IRQ_FLAG = 0x80, Register32
+
+class SPMI:
+ def __init__(self, u, adt_path):
+ self.u = u
+ self.p = u.proxy
+ self.iface = u.iface
+ self.base = u.adt[adt_path].get_reg(0)[0]
+ self.regs = SPMIRegs(u, self.base)
+
+ def read(self, slave, reg, size):
+ while not self.regs.STATUS.reg.RX_EMPTY:
+ print(">", self.regs.REPLY.val)
+
+ self.regs.CMD.reg = R_CMD(REG = reg, ACTIVE=1, SLAVE_ID = slave, CMD = CMD_EXT_READL | (size - 1))
+
+ buf = b""
+
+ left = size + 4
+ while left > 0:
+ while self.regs.STATUS.reg.RX_EMPTY:
+ pass
+ v = self.regs.REPLY.val
+ buf += struct.pack("<I", v)
+ left -= 4
+
+ return buf[4:4+size]
+
+ def write(self, slave, reg, data):
+ while not self.regs.STATUS.reg.RX_EMPTY:
+ self.regs.REPLY.val
+
+ size = len(data)
+ self.regs.CMD.reg = R_CMD(REG = reg, ACTIVE=1, SLAVE_ID = slave, CMD = CMD_EXT_WRITEL | (size - 1))
+
+ while data:
+ blk = (data[:4] + b"\0\0\0")[:4]
+ self.regs.CMD.val = struct.unpack("<I", blk)[0]
+ data = data[4:]
+
+ while self.regs.STATUS.reg.RX_EMPTY:
+ pass
+ return self.regs.REPLY.val
+
+ def read8(self, slave, reg):
+ return struct.unpack("<B", self.read(slave, reg, 1))[0]
+
+ def read16(self, slave, reg):
+ return struct.unpack("<H", self.read(slave, reg, 2))[0]
+
+ def read32(self, slave, reg):
+ return struct.unpack("<I", self.read(slave, reg, 4))[0]
+
+ def read64(self, slave, reg):
+ return struct.unpack("<Q", self.read(slave, reg, 8))[0]
+
+ def write8(self, slave, reg, val):
+ return self.write(slave, reg, struct.pack("<B", val))
+
+ def write16(self, slave, reg, val):
+ return self.write(slave, reg, struct.pack("<H", val))
+
+ def write32(self, slave, reg, val):
+ return self.write(slave, reg, struct.pack("<I", val))
+
+ def write64(self, slave, reg, val):
+ return self.write(slave, reg, struct.pack("<Q", val))
diff --git a/tools/proxyclient/m1n1/hw/uat.py b/tools/proxyclient/m1n1/hw/uat.py
new file mode 100644
index 0000000..f5dce9f
--- /dev/null
+++ b/tools/proxyclient/m1n1/hw/uat.py
@@ -0,0 +1,571 @@
+"""
+ UAT is just regular ARMv8 pagetables, shared between the gfx-asc firmware
+ and the actual AGX hardware.
+
+ The OS doesn't have direct control over it, TTBR0 and TTBR1 entries are placed at
+ gpu-region-base, one pair for each context. The firmware automatically loads TTBR0/TTBR1
+ on boot and whenever the context changes.
+"""
+
+
+import struct
+from ..fw.agx.handoff import GFXHandoff
+from ..utils import *
+from ..malloc import Heap
+from enum import IntEnum
+import traceback
+
+__all__ = []
+
+class MemoryAttr(IntEnum):
+ # ff = Normal, Outer Writeback RW, Inner Writeback RW
+ Normal = 0 # Only accessed by the gfx-asc coprocessor
+ # 00 = Device nGnRnE
+ Device = 1
+ # f4 = Normal, Outer Writeback RW, Inner NC
+ Shared = 2 # Probally Outer-shareable. Shared with either the main cpu or AGX hardware
+ # 4f = Normal, Outer NC, Inner Writeback RW
+ UNK3 = 3
+ # 00 = Device nGnRnE
+ UNK4 = 4
+ # ff = Normal, Outer Writeback RW, Inner Writeback RW
+ UNK5 = 5
+ # 00 = Device nGnRnE
+ UNK6 = 6
+ # 00 = Device nGnRnE
+ UNK7 = 7
+
+
+class TTBR(Register64):
+ ASID = 63, 48
+ BADDR = 47, 1
+ VALID = 0
+
+ def valid(self):
+ return self.VALID == 1
+
+ def offset(self):
+ return self.BADDR << 1
+
+ def set_offset(self, offset):
+ self.BADDR = offset >> 1
+
+ def describe(self):
+ return f"{self.offset():x} [ASID={self.ASID}, VALID={self.VALID}]"
+
+class PTE(Register64):
+ OFFSET = 47, 14
+ UNK0 = 10 # probally an ownership flag, seems to be 1 for FW created PTEs and 0 for OS PTEs
+ TYPE = 1
+ VALID = 0
+
+ def valid(self):
+ return self.VALID == 1 and self.TYPE == 1
+
+ def offset(self):
+ return self.OFFSET << 14
+
+ def set_offset(self, offset):
+ self.OFFSET = offset >> 14
+
+ def describe(self):
+ if not self.valid():
+ return f"<invalid> [{int(self)}:x]"
+ return f"{self.offset():x}, UNK={self.UNK0}"
+
+class Page_PTE(Register64):
+ OS = 55 # Owned by host os or firmware
+ UXN = 54
+ PXN = 53
+ OFFSET = 47, 14
+ nG = 11 # global or local TLB caching
+ AF = 10
+ SH = 9, 8
+ AP = 7, 6
+ AttrIndex = 4, 2
+ TYPE = 1
+ VALID = 0
+
+ def valid(self):
+ return self.VALID == 1 and self.TYPE == 1
+
+ def offset(self):
+ return self.OFFSET << 14
+
+ def set_offset(self, offset):
+ self.OFFSET = offset >> 14
+
+ def access_fw(self, gl=False):
+ if not self.OS:
+ return [[
+ ["--", "--", "--", "--"],
+ ["--", "RW", "--", "RW"],
+ ["--", "RX", "--", "--"],
+ ["RX", "R-", "--", "R-"],
+ ], [
+ ["--", "--", "--", "RW"],
+ ["--", "--", "--", "RW"],
+ ["RX", "--", "--", "R-"],
+ ["RX", "RW", "--", "R-"],
+ ]][gl][self.AP][(self.UXN << 1) | self.PXN]
+ else:
+ return [
+ ["--", "R-", "-?", "RW"],
+ ["R-", "--", "RW", "RW"],
+ ["--", "--", "--", "--"],
+ ["--", "--", "--", "--"],
+ ][self.AP][(self.UXN << 1) | self.PXN]
+
+ def access_gpu(self):
+ if not self.OS:
+ return "--"
+
+ return [
+ ["--", "R-", "-W", "RW"],
+ ["--", "--", "--", "R-"],
+ ["R-", "-W", "RW", "--"],
+ ["--", "--", "--", "--"],
+ ][self.AP][(self.UXN << 1) | self.PXN]
+
+ def describe(self):
+ if not self.valid():
+ return f"<invalid> [{int(self)}:x]"
+
+ return (
+ f"{self.offset():x} [GPU={self.access_gpu()}, EL1={self.access_fw(0)}, GL1={self.access_fw(1)}, " +
+ f"perm={self.OS}{self.AP:02b}{self.UXN}{self.PXN}, " +
+ f"{MemoryAttr(self.AttrIndex).name}, {['Global', 'Local'][self.nG]}, " +
+ f"Owner={['FW', 'OS'][self.OS]}, AF={self.AF}, SH={self.SH}] ({self.value:#x})"
+ )
+
+class UatAccessor(Reloadable):
+ def __init__(self, uat, ctx=0):
+ self.uat = uat
+ self.ctx = ctx
+
+ def translate(self, addr, width):
+ paddr, _ = self.uat.iotranslate(self.ctx, addr, width)[0]
+ if paddr is None:
+ raise Exception(f"UAT Failed to translate {addr:#x}")
+ return paddr
+
+ def read(self, addr, width):
+ return self.uat.u.read(self.translate(addr, width), width)
+ def read8(self, addr):
+ return self.uat.p.read8(self.translate(addr, 1))
+ def read16(self, addr):
+ return self.uat.p.read16(self.translate(addr, 2))
+ def read32(self, addr):
+ return self.uat.p.read32(self.translate(addr, 4))
+ def read64(self, addr):
+ return self.uat.p.read64(self.translate(addr, 8))
+
+ def write(self, addr, data, width):
+ self.uat.u.write(self.translate(addr, width), data, width)
+ def write8(self, addr, data):
+ self.uat.p.write8(self.translate(addr, 1), daat)
+ def write16(self, addr, data):
+ self.uat.p.write6(self.translate(addr, 2), data)
+ def write32(self, addr, data):
+ self.uat.p.write32(self.translate(addr, 4), data)
+ def write64(self, addr, data):
+ self.uat.p.write64(self.translate(addr, 8), data)
+
+class UatStream(Reloadable):
+ CACHE_SIZE = 0x1000
+
+ def __init__(self, uat, ctx, addr, recurse=True):
+ self.uat = uat
+ self.ctx = ctx
+ self.pos = addr
+ self.cache = None
+ self.meta_fn = None
+ self.recurse = recurse
+
+ def to_accessor(self):
+ return UatAccessor(self.uat, self.ctx)
+
+ def read(self, size):
+ assert size >= 0
+
+ data = b""
+ if self.cache:
+ data = self.cache[:size]
+ cached = len(self.cache)
+ self.pos += min(cached, size)
+ if cached > size:
+ self.cache = self.cache[size:]
+ return data
+ self.cache = None
+ if cached == size:
+ return data
+
+ size -= cached
+
+ # align any cache overreads to the next page boundary
+ remaining_in_page = self.uat.PAGE_SIZE - (self.pos % self.uat.PAGE_SIZE)
+ to_cache = min(remaining_in_page, self.CACHE_SIZE)
+
+ try:
+ self.cache = self.uat.ioread(self.ctx, self.pos, max(size, to_cache))
+ except:
+ traceback.print_exc()
+ raise
+ return data + self.read(size)
+
+ def readable(self):
+ return True
+
+ def write(self, bytes):
+ self.uat.iowrite(self.ctx, self.pos, bytes)
+ self.pos += len(bytes)
+ self.cache = None
+ return len(bytes)
+
+ def writable(self):
+ return True
+
+ def flush(self):
+ self.cache = None
+
+ def seek(self, n, wherenc=0):
+ self.cache = None
+ if wherenc == 0:
+ self.pos = n
+ elif wherenc == 2:
+ self.pos += n
+
+ def seekable(self):
+ return True
+
+ def tell(self):
+ return self.pos
+
+ def closed(self):
+ return False
+
+
+class UAT(Reloadable):
+ NUM_CONTEXTS = 64
+
+ PAGE_BITS = 14
+ PAGE_SIZE = 1 << PAGE_BITS
+
+ L0_SIZE = 2
+ L0_OFF = 39
+ L1_SIZE = 8
+ L1_OFF = 36
+ L2_OFF = 25
+ L3_OFF = 14
+
+ IDX_BITS = 11
+ Lx_SIZE = (1 << IDX_BITS)
+
+ LEVELS = [
+ (L0_OFF, L0_SIZE, TTBR),
+ (L1_OFF, L1_SIZE, PTE),
+ (L2_OFF, Lx_SIZE, PTE),
+ (L3_OFF, Lx_SIZE, Page_PTE),
+ ]
+
+ def __init__(self, iface, util=None, hv=None):
+ self.iface = iface
+ self.u = util
+ self.p = util.proxy
+ self.hv = hv
+ self.pt_cache = {}
+ self.dirty = set()
+ self.dirty_ranges = {}
+ self.allocator = None
+ self.ttbr = None
+ self.initialized = False
+ self.sgx_dev = self.u.adt["/arm-io/sgx"]
+ self.shared_region = self.sgx_dev.gfx_shared_region_base
+ self.gpu_region = self.sgx_dev.gpu_region_base
+ self.ttbr0_base = self.u.memalign(self.PAGE_SIZE, self.PAGE_SIZE)
+ self.ttbr1_base = self.sgx_dev.gfx_shared_region_base
+ self.handoff = GFXHandoff(self.u)
+
+ self.VA_MASK = 0
+ for (off, size, _) in self.LEVELS:
+ self.VA_MASK |= (size - 1) << off
+ self.VA_MASK |= self.PAGE_SIZE - 1
+
+
+ def set_l0(self, ctx, off, base, asid=0):
+ ttbr = TTBR(BADDR = base >> 1, ASID = asid, VALID=(base != 0))
+ print(f"[UAT] Set L0 ctx={ctx} off={off:#x} base={base:#x} asid={asid} ({ttbr})")
+ self.write_pte(self.gpu_region + ctx * 16, off, 2, ttbr)
+
+ def ioread(self, ctx, base, size):
+ if size == 0:
+ return b""
+
+ ranges = self.iotranslate(ctx, base, size)
+
+ iova = base
+ data = []
+ for addr, size in ranges:
+ if addr is None:
+ raise Exception(f"Unmapped page at iova {ctx}:{iova:#x}")
+ data.append(self.iface.readmem(addr, size))
+ iova += size
+
+ return b"".join(data)
+
+ def iowrite(self, ctx, base, data):
+ if len(data) == 0:
+ return
+
+ ranges = self.iotranslate(ctx, base, len(data))
+
+ iova = base
+ p = 0
+ for addr, size in ranges:
+ if addr is None:
+ raise Exception(f"Unmapped page at iova {ctx}:{iova:#x}")
+ self.iface.writemem(addr, data[p:p + size])
+ p += size
+ iova += size
+
+ # A stream interface that can be used for random access by Construct
+ def iostream(self, ctx, base, recurse=True):
+ return UatStream(self, ctx, base, recurse)
+
+ # A read/write register interface like proxy/utils objects that can be used by RegMap
+ def ioaccessor(self, ctx):
+ return UatAccessor(self, ctx)
+
+ def iomap(self, ctx, addr, size, **flags):
+ iova = self.allocator.malloc(size)
+
+ self.iomap_at(ctx, iova, addr, size, **flags)
+ self.flush_dirty()
+ return iova
+
+ def iomap_at(self, ctx, iova, addr, size, **flags):
+ if size == 0:
+ return
+
+ if addr & (self.PAGE_SIZE - 1):
+ raise Exception(f"Unaligned PA {addr:#x}")
+
+ if iova & (self.PAGE_SIZE - 1):
+ raise Exception(f"Unaligned IOVA {iova:#x}")
+
+ self.init()
+
+ map_flags = {'OS': 1, 'AttrIndex': MemoryAttr.Normal, 'VALID': 1, 'TYPE': 1, 'AP': 1, 'AF': 1, 'UXN': 1}
+ map_flags.update(flags)
+
+ start_page = align_down(iova, self.PAGE_SIZE)
+ end = iova + size
+ end_page = align_up(end, self.PAGE_SIZE)
+
+ for page in range(start_page, end_page, self.PAGE_SIZE):
+ table_addr = self.gpu_region + ctx * 16
+ for (offset, size, ptecls) in self.LEVELS:
+ if ptecls is Page_PTE:
+ pte = Page_PTE(**map_flags)
+ pte.set_offset(addr)
+ self.write_pte(table_addr, page >> offset, size, pte)
+ addr += self.PAGE_SIZE
+ else:
+ pte = self.fetch_pte(table_addr, page >> offset, size, ptecls)
+ if not pte.valid():
+ table = self.u.memalign(self.PAGE_SIZE, self.PAGE_SIZE)
+ self.p.memset32(table, 0, self.PAGE_SIZE)
+ pte.set_offset(table)
+ if ptecls is not TTBR:
+ pte.VALID = 1
+ pte.TYPE = 1
+ #pte.UNK0 = 1
+ self.write_pte(table_addr, page >> offset, size, pte)
+ table_addr = pte.offset()
+
+ self.dirty_ranges.setdefault(ctx, []).append((start_page, end_page - start_page))
+ #self.flush_dirty()
+
+
+ def fetch_pte(self, offset, idx, size, ptecls):
+ idx = idx & (size - 1)
+
+ cached, table = self.get_pt(offset, size=size)
+ pte = ptecls(table[idx])
+ if not pte.valid() and cached:
+ self.flush_dirty()
+ cached, table = self.get_pt(offset, size=size, uncached=True)
+ pte = ptecls(table[idx])
+
+ return pte
+
+ def write_pte(self, offset, idx, size, pte):
+ idx = idx & (size - 1)
+
+ cached, table = self.get_pt(offset, size=size)
+
+ table[idx] = pte.value
+ self.dirty.add(offset)
+
+ def iotranslate(self, ctx, start, size):
+ if size == 0:
+ return []
+
+ start = start & self.VA_MASK
+
+ start_page = align_down(start, self.PAGE_SIZE)
+ start_off = start - start_page
+ end = start + size
+ end_page = align_up(end, self.PAGE_SIZE)
+ end_size = end - (end_page - self.PAGE_SIZE)
+
+ pages = []
+
+ for page in range(start_page, end_page, self.PAGE_SIZE):
+ table_addr = self.gpu_region + ctx * 16
+ for (offset, size, ptecls) in self.LEVELS:
+ pte = self.fetch_pte(table_addr, page >> offset, size, ptecls)
+ if not pte.valid():
+ break
+ table_addr = pte.offset()
+
+ if pte.valid():
+ pages.append(pte.offset())
+ else:
+ pages.append(None)
+
+ ranges = []
+
+ for page in pages:
+ if not ranges:
+ ranges.append((page, self.PAGE_SIZE))
+ continue
+ laddr, lsize = ranges[-1]
+ if ((page is None and laddr is None) or
+ (page is not None and laddr == (page - lsize))):
+ ranges[-1] = laddr, lsize + self.PAGE_SIZE
+ else:
+ ranges.append((page, self.PAGE_SIZE))
+
+ ranges[-1] = (ranges[-1][0], ranges[-1][1] - self.PAGE_SIZE + end_size)
+
+ if start_off:
+ ranges[0] = (ranges[0][0] + start_off if ranges[0][0] else None,
+ ranges[0][1] - start_off)
+
+ return ranges
+
+ def get_pt(self, addr, size=None, uncached=False):
+ if size is None:
+ size = self.Lx_SIZE
+ cached = True
+ if addr not in self.pt_cache or uncached:
+ cached = False
+ self.pt_cache[addr] = list(
+ struct.unpack(f"<{size}Q", self.iface.readmem(addr, size * 8)))
+
+ return cached, self.pt_cache[addr]
+
+ def flush_pt(self, addr):
+ assert addr in self.pt_cache
+ table = self.pt_cache[addr]
+ self.iface.writemem(addr, struct.pack(f"<{len(table)}Q", *table))
+ #self.p.dc_civac(addr, 0x4000)
+
+ def flush_dirty(self):
+ inval = False
+
+ for page in self.dirty:
+ self.flush_pt(page)
+ inval = True
+
+ self.dirty.clear()
+
+ for ctx, ranges in self.dirty_ranges.items():
+ asid = ctx << 48
+ self.u.inst("tlbi aside1os, x0", asid)
+
+ def invalidate_cache(self):
+ self.pt_cache = {}
+
+ def recurse_level(self, level, base, table, page_fn=None, table_fn=None):
+ def extend(addr):
+ if addr >= 0x80_00000000:
+ addr |= 0xf00_00000000
+ return addr
+
+ offset, size, ptecls = self.LEVELS[level]
+
+ cached, tbl = self.get_pt(table, size)
+ sparse = False
+ for i, pte in enumerate(tbl):
+ pte = ptecls(pte)
+ if not pte.valid():
+ sparse = True
+ continue
+
+ range_size = 1 << offset
+ start = extend(base + i * range_size)
+ end = start + range_size - 1
+
+ if level + 1 == len(self.LEVELS):
+ if page_fn:
+ page_fn(start, end, i, pte, level, sparse=sparse)
+ else:
+ if table_fn:
+ table_fn(start, end, i, pte, level, sparse=sparse)
+ self.recurse_level(level + 1, start, pte.offset(), page_fn, table_fn)
+
+ sparse = False
+
+ def foreach_page(self, ctx, page_fn):
+ self.recurse_level(0, 0, self.gpu_region + ctx * 16, page_fn)
+
+ def foreach_table(self, ctx, table_fn):
+ self.recurse_level(0, 0, self.gpu_region + ctx * 16, table_fn=table_fn)
+
+ def init(self):
+ if self.initialized:
+ return
+
+ print("[UAT] Initializing...")
+
+ # Clear out any stale kernel page tables
+ self.p.memset64(self.ttbr1_base + 0x10, 0, 0x3ff0)
+ self.u.inst("tlbi vmalle1os")
+
+ self.handoff.initialize()
+
+ with self.handoff.lock():
+ print(f"[UAT] TTBR0[0] = {self.ttbr0_base:#x}")
+ print(f"[UAT] TTBR1[0] = {self.ttbr1_base:#x}")
+ self.set_l0(0, 0, self.ttbr0_base)
+ self.set_l0(0, 1, self.ttbr1_base)
+ self.flush_dirty()
+ self.invalidate_cache()
+
+ print("[UAT] Init complete")
+
+ self.initialized = True
+
+ def bind_context(self, ctx, ttbr0_base):
+ assert ctx != 0
+
+ with self.handoff.lock():
+ self.set_l0(ctx, 0, ttbr0_base, ctx)
+ self.set_l0(ctx, 1, self.ttbr1_base, ctx)
+ self.flush_dirty()
+ self.invalidate_cache()
+
+ def dump(self, ctx, log=print):
+ def print_fn(start, end, i, pte, level, sparse):
+ type = "page" if level+1 == len(self.LEVELS) else "table"
+ if sparse:
+ log(f"{' ' * level}...")
+ log(f"{' ' * level}{type}({i:03}): {start:011x} ... {end:011x}"
+ f" -> {pte.describe()}")
+
+ self.recurse_level(0, 0, self.gpu_region + ctx * 16, print_fn, print_fn)
+
+__all__.extend(k for k, v in globals().items()
+ if (callable(v) or isinstance(v, type)) and v.__module__ == __name__)