diff options
Diffstat (limited to 'tools/proxyclient/m1n1/hw/i2c.py')
| -rw-r--r-- | tools/proxyclient/m1n1/hw/i2c.py | 251 |
1 files changed, 251 insertions, 0 deletions
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}>" |
