summaryrefslogtreecommitdiff
path: root/tools/proxyclient/m1n1/hw/i2c.py
blob: e2bda7add04b0021921b8832702268530159c9af (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
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}>"