I2C 26.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
; Copyright 2009 Castle Technology Ltd
;
; Licensed under the Apache License, Version 2.0 (the "License");
; you may not use this file except in compliance with the License.
; You may obtain a copy of the License at
;
;     http://www.apache.org/licenses/LICENSE-2.0
;
; Unless required by applicable law or agreed to in writing, software
; distributed under the License is distributed on an "AS IS" BASIS,
; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
; See the License for the specific language governing permissions and
; limitations under the License.
;
Robert Sprowson's avatar
Robert Sprowson committed
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
        GET     Hdr:ListOpts
        GET     Hdr:Macros
        GET     Hdr:System
        GET     Hdr:Machine.<Machine>
        GET     Hdr:ImageSize.<ImageSize>
        $GetIO

        GET     Hdr:OSEntries
        GET     Hdr:HALEntries

        GET     hdr.omap3530
        GET     hdr.StaticWS
        GET     hdr.Timers
        GET     hdr.PRCM

        AREA    |Asm$$Code|, CODE, READONLY, PIC

        EXPORT  I2C_Init

        EXPORT  HAL_IICBuses
        EXPORT  HAL_IICType
        EXPORT  HAL_IICDevice
        EXPORT  HAL_IICTransfer
        EXPORT  HAL_IICMonitorTransfer
40
        EXPORT  HAL_VideoIICOp
Jeffrey Lee's avatar
Jeffrey Lee committed
41
        EXPORT  IIC_DoOp_Poll
42 43 44 45 46 47

        IMPORT  HAL_CounterDelay

; The OMAP3530 has 4 I2C controllers:
; I2C1 - On the beagleboard this is connected to the TWL/TPS companion chip. RISC OS sees it as I2C bus 0.
; I2C2 - On the beagleboard this is routed to the expansion connector, and so has no use by default. RISC OS sees it as I2C bus 1.
48
; I2C3 - On the beagleboard this is routed to the HDMI connector. This means its sole use is for reading EDID/DDC data. RISC OS therefore accesses it via HAL_VideoIICOp.
49 50 51
; I2C4 - This is a bus that the OMAP does not fully expose to the programmer. Connected to the TWL/TPS, it's part of the PRCM system. RISC OS does not see this bus at all, and neither does this code.

                GBLL    I2CDebug
52
I2CDebug        SETL {FALSE} :LAND: Debug
53 54 55 56

                GBLL    I2CDebugData ; Display bytes sent & received?
I2CDebugData    SETL {FALSE} :LAND: I2CDebug

57 58
                GBLL    I2CDebugError ; Debug unexpected_error occurences
I2CDebugError   SETL {FALSE} :LAND: Debug
59

60 61

 [ I2CDebug :LOR: I2CDebugError
62 63 64 65
        IMPORT  DebugHALPrint
        IMPORT  DebugHALPrintReg
        IMPORT  DebugMemDump
        IMPORT  DebugHALPrintByte
66
 ]
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84

I2C_Init
        Push    "v1-v3,lr"
 [ I2CDebug
        DebugTX "I2C_Init"
 ]
        ; 1. Make sure clocks are enabled
        LDR     a1, L4_ClockMan_Log
        LDR     a2, [a1, #CM_ICLKEN1_CORE]
        ORR     a2, a2, #7:SHL:15
        STR     a2, [a1, #CM_ICLKEN1_CORE]
        LDR     a2, [a1, #CM_FCLKEN1_CORE]
        ORR     a2, a2, #7:SHL:15
        STR     a2, [a1, #CM_FCLKEN1_CORE]
        ; Wait for power?
        MOV     a1, #50*1024 ; 50msec ish
        BL      HAL_CounterDelay
        ; 2. Initialise each I2C controller
85
        MOV     v1, #3
86 87 88 89 90 91
        ADR     v2, I2C_Table
10
 [ I2CDebug
        DebugReg  v1, "remaining busses: "
 ]
        LDR     v3, [v2, #I2C_HW]
92 93
        CMP     v3, #0
        BEQ     %FT30 ; Skip unused busses
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
        ; First we'll reset the controller
        LDRH    a4, [v3, #I2C_CON]
        TST     a4, #1:SHL:15
        BEQ     %FT20
        MOV     a4, #0
        STRH    a4, [v3, #I2C_CON]
        ; The manual makes no mention of it, but u-boot waits for a while after enabling & disabling each controller. So to play it safe I'll follow u-boot's lead.
        MOV     a1, #50*1024 ; 50msec ish
        BL      HAL_CounterDelay
20
        MOV     a4, #2
        STRH    a4, [v3, #I2C_SYSC]
        MOV     a4, #1:SHL:15
        STRH    a4, [v3, #I2C_CON]
        ; Wait
        MOV     a1, #10*1024 ; 10msec ish
        BL      HAL_CounterDelay
20
        LDRH    a4, [v3, #I2C_SYSS]
        TST     a4, #1
        BEQ     %BT20
        ; Now disable the controller again so we can program it properly
        MOV     a4, #0
        STRH    a4, [v3, #I2C_CON]
        ; Wait
        MOV     a1, #50*1024 ; 50msec ish
        BL      HAL_CounterDelay
121
        ; Set up the 12MHz sampling clock
122 123
        MOV     a4, #7
        STRH    a4, [v3, #I2C_PSC]
124
        ; Run at 100kbps for now. TODO - Add support for higher speeds!
125 126 127 128 129 130 131 132 133 134 135 136 137 138
        ; i.e. SCLL, SCLH = 12000000/(2*100000) = 60 (-7 for SCLL, -5 for SCLH)
        MOV     a4, #60-7
        STRH    a4, [v3, #I2C_SCLL]
        MOV     a4, #60-5
        STRH    a4, [v3, #I2C_SCLH]
        ; Program own address
        MOV     a4, #1
        STRH    a4, [v3, #I2C_OA0]
        ; Set FIFO thresholds
        LDRH    a4, [v3, #I2C_BUFSTAT]
        ADR     a3, fifo_thresholds
        AND     a4, a4, #&C000 ; Get FIFO size
        LDR     a4, [a3, a4, LSR #12]
        STRH    a4, [v3, #I2C_BUF]
139 140 141
        ; Enable auto idle, smart idle. Both clocks off when in idle mode?
        MOV     a4, #1+(2<<3)
        STRH    a4, [v3, #I2C_SYSC]
142 143 144 145 146 147 148 149 150 151 152
        ; Enable the controller
        MOV     a4, #1:SHL:15
        STRH    a4, [v3, #I2C_CON]
        ; Next!
        SUBS    v1, v1, #1
        ADD     v2, v2, #I2CBlockSize
        BNE     %BT10
        ; Wait for last controller to init fully
        MOV     a1, #10*1024 ; 10msec ish
        BL      HAL_CounterDelay
        ; Done
153
30
154 155 156 157 158 159 160 161 162 163 164 165
 [ I2CDebug
        DebugTX "I2C_Init complete"
 ]
        Pull    "v1-v3,pc"

fifo_thresholds
        DCD     &0606 ; 8 byte FIFO: Threshold at 7 bytes
        DCD     &0B0B ; 16 byte FIFO: Threshold at 12 bytes
        DCD     &1B1B ; 32 byte FIFO: Threshold at 28 bytes
        DCD     &3B3B ; 64 byte FIFO: Threshold at 60 bytes

HAL_IICBuses
166
        LDRB    a1, [sb, #BoardConfig_NumI2C]
167 168 169 170
        MOV     pc, lr

HAL_IICType
        ; todo - set the 'missing flags' alluded to in Kernel.Docs.HAL.MoreEnts? (multi-master & slave operation)
171 172
        LDRB    a2, [sb, #BoardConfig_NumI2C]
        CMP     a1, a2
173 174 175 176 177 178
        MOVHS   a1, #0
        LDRLO   a1, =IICFlag_HighLevel+IICFlag_Fast+IICFlag_Background+IICFlag_HighSpeed+(210:SHL:20)
        MOV     pc, lr

; HAL_IICDevice
; in:
179
;       r0 = bus number
180
; out:
181 182
;       r0 = device number

183
HAL_IICDevice
184
        LDRB    a3, [sb, #BoardConfig_NumI2C]
185 186
        CMP     a1, a3
        MOVHS   a1, #-1
187
        ADDLO   a3, sb, #BoardConfig_HALI2CIRQ
188
        LDRLOB  a1, [a3, a1]
189 190 191 192 193 194 195 196
        MOV     pc, lr

; HAL_IICTransfer
; in:
;      r0 = bus number
;      r1 = number of transfers
;      r2 = iic_transfer array ptr
; out:
197
;      r0 = IICStatus return code
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
; Transfer list format:
;      typedef struct iic_transfer
;      {
;        unsigned addr:8;
;        unsigned :22;
;        unsigned checksumonly:1;
;        unsigned nostart:1;
;        union
;        {   unsigned checksum;
;            void *data;
;        } d;
;        unsigned len;
;      } iic_transfer;

HAL_IICTransfer
213 214 215 216
        LDRB    a4, [sb, #BoardConfig_NumI2C]
        CMP     a1, a4
        MOVHS   a1, #IICStatus_Error
        MOVHS   pc, lr
217
        ; Quickly validate the transfer list
218 219 220 221
        ; We have several constraints:
        ; 1. Must have 1 or more iic_transfers
        ; 2. First transfer must not have nostart bit set
        ; 3. Between start bits (and between the last start bit and the end of  the list) there must be between 1 and 65536 bytes of data (but for the moment we do allow individual iic_transfers to be 0-length)
222 223 224 225 226 227 228 229 230 231 232
        CMP     a2, #0
        MOVLT   a1, #IICStatus_Error
        MOVLT   pc, lr
        LDR     a4, [a3]
        TST     a4, #1:SHL:31 ; First transfer has nostart set!
        MOVNE   a1, #IICStatus_Error
        MOVNE   pc, lr
        STMFD   sp!, {v1-v5,lr}
        ADD     a4, a3, a2, LSL #3
        ADD     a4, a4, a2, LSL #2
        MOV     v1, a3
233
30
234
        MOV     v5, #-1
235
10
236 237 238 239 240 241
        LDMIB   v1!, {v2-v4}    ; data ptr & length from current transfer, flags from next
        ADD     v5, v5, v3
        CMP     v1, a4
        BEQ     %FT20           ; Last transfer just read
        TST     v4, #1:SHL:31
        BNE     %BT10           ; Still more data in this transfer
242
20
243 244 245 246 247
        CMP     v5, #65536
        MOVHS   a1, #IICStatus_Error    ; Too much (or too little) data
        LDMHSIA sp! ,{v1-v5,pc}
        CMP     v1, a4
        BNE     %BT30
248
_IICTransfer_Video ; Entry point for HAL_VideoIICOp
249 250 251 252 253 254
 [ I2CDebug
        DebugTX "HAL_IICTransfer"
        DebugReg a1, "bus="
        DebugReg a2, "num="
        DebugReg a3, "iic_transfer="
 ]
255 256 257 258 259 260 261 262 263 264 265
        ADR     v5, I2C_Table
        MOV     v4, #I2CBlockSize
        MLA     v5, a1, v4, v5
        MRS     ip, CPSR
        ORR     a4, ip, #I32_bit
        MSR     CPSR_c, a4              ; disable interrupts for atomic claim
        LDR     a4, [v5, #I2C_XStart]
        TEQ     a4, #0                  ; in use already?
        STREQ   a3, [v5, #I2C_XStart]   ; if not, claim it
        MSR     CPSR_c, ip
        MOVNE   a1, #IICStatus_Busy     ; if it is, return "BUSY"
266
 [ I2CDebug
267
        BEQ     %FT10
268
        DebugReg a4, "BUSY: XStart="
269
        LDMFD   sp!, {v1-v5,pc}
270 271 272
10
        DebugTX "OK"
 |
273
        LDMNEFD sp!, {v1-v5,pc}
274
 ]
275 276 277 278 279
        SUB     a2, a2, #1              ; a2 = transfers - 1 (needed below)
        ADD     v1, a3, a2, LSL #3
        ADD     v1, v1, a2, LSL #2
        STR     v1, [v5, #I2C_XEnd]
        LDR     v4, [v5, #I2C_HW]
280
        ; Make sure controller is enabled, since we don't do any initialisation atm!
281 282 283
        LDRH    a4, [v4, #I2C_CON]
        TST     a4, #1:SHL:15
        LDREQH  v3, [v4, #I2C_STAT]
284
 [ I2CDebugError
285 286 287
        BNE     %FT10
        DebugTX "Controller not enabled!"
        B       unexpected_error
288 289
10
 |
290
        BEQ     unexpected_error
291
 ]
292
        MOV     v1, a3
293
start_transfer ; Start the transfer in v1
294 295 296 297 298
        ; a1-a4 free
        ; v1 = iic_transfer to start
        ; v2-v3 free
        ; v4 = I2C controller ptr
        ; v5 = I2C state ptr
299 300 301
 [ I2CDebug
        DebugReg v1, "start_transfer: "
 ]
302 303 304
        STR     v1, [v5, #I2C_XCurrent]
        MOV     lr, #0
        STR     lr, [v5, #I2C_XBytes]
305
        ; Get its info
306
        LDMIA   v1, {a1-a3}
307 308 309 310 311 312
 [ I2CDebug
        DebugReg a1, "addr="
        DebugReg a2, "data="
        DebugReg a3, "len ="
 ]
        ; If it's a 'checksum-only' read, clear the checksum to 0
313 314 315 316
        TST     a1, #1:SHL:30
        TSTNE   a1, #1
        MOVNE   a2, #0
        STRNE   a2, [v1, #4]
317
        ; Wait for the controller to be fully idle - if two iic_transfer lists are executed back-to-back then there's a chance the controller won't have yet sent the stop bit from the previous transfer. Thus we must wait here for the bit to be sent.
318
        MOV     v2, #100*1024 ; timeout - this should be more than adequate (with a CPU of 1GHz, there'd be 10K CPU clock cycles per 100kbps I2C clock cycle)
319
10
320 321 322 323 324
        LDRH    a4, [v4, #I2C_CON]
        TST     a4, #3 ; Check STT and STP (Although we're primarily interested in STP, the manual warns about bad things happening if STT is cleared)
        BEQ     %FT20
        SUBS    v2, v2, #1
        BNE     %BT10
325
 [ I2CDebug
326
        DebugReg a4, "ISC_CON timeout: "
327
 ]
328 329 330 331
        MOV     a1, #IICStatus_Busy
        MOV     v3, #0
        STR     v3, [v5, #I2C_XStart]
        LDMFD   sp!, {v1-v5,pc}
332
20
333 334
        ; Flush the RX/TX FIFOs
        ; The manual says we only need to do this on NACK errors, but to play it safe I'm going to do it at the start of each transfer
335 336 337 338
        LDRH    v2, [v4, #I2C_BUF]
        LDR     v3, =&4040
        ORR     v2, v2, v3
        STRH    v2, [v4, #I2C_BUF]
339
10
340 341 342
        LDRH    v2, [v4, #I2C_BUF]
        TST     v2, v3
        BNE     %BT10
343 344
        ; Configure controller
        ; 1. I2C_CON.MST, ISC_CON.TRX
345 346 347 348 349 350
        BIC     a4, a4, #&FF ; Clear STT, STP, XOA0-XOA3
        BIC     a4, a4, #&F00 ; Clear STB, MST, TRX, XSA
        ORR     a4, a4, #&400 ; Set MST (slave mode not supported ATM)
        TST     a1, #1
        ORREQ   a4, a4, #&200 ; Set transmitter mode if appropriate
        STRH    a4, [v4, #I2C_CON]
351
        ; Clear any pending interrupts, just in case?
352 353
        LDRH    v2, [v4, #I2C_STAT]
        STRH    v2, [v4, #I2C_STAT]
354
        ; 2. I2C_IE.XRDY_IE, I2C_IE.RRDY
355 356 357 358 359
        MOVEQ   v2, #&17 ; AL_IE, NACK_IE, ARDY_IE, XRDY_IE
        MOVNE   v2, #&0F ; AL_IE, NACK_IE, ARDY_IE, RRDY_IE
        ORREQ   v2, v2, #&4000 ; XDR_IE
        ORRNE   v2, v2, #&2000 ; RDR_IE
        STRH    v2, [v4, #I2C_IE]
360 361
        ; 3. Ignore DMA for now
        ; 4. I2C_SA, I2C_CNT
362 363 364
        MOV     v2, a1, LSR #1
        AND     v2, v2, #&7F
        STRH    v2, [v4, #I2C_SA]
365 366
        ; The I2C controller doesn't seem to like it if we don't send a start bit - it simply ignores I2C_CNT and goes straight to sending the stop bit (or sits there and does nothing if no stop bit was wanted).
        ; So to get around this we scan forward through the iic_transfer list and set I2C_CNT to the number of bytes to transmit before the next start/stop bit is required
367 368
        MOV     v2, v1
        LDR     ip, [v5, #I2C_XEnd]
369
10
370 371 372 373 374 375 376 377
        CMP     ip, v2
        ORREQ   a4, a4, #2 ; Last transfer in list; set stop bit
        BEQ     %FT20
        ADD     v2, v2, #12 ; Increment after compare, just in case some crazy person creates an iic_transfer that wraps from &FFF.... to &000...
        LDMIA   v2,{a1-a2,v3} ; Get transfer info
        TST     a1, #1:SHL:31
        ADDNE   a3, a3, v3 ; nostart is set; increment length and loop around
        BNE     %BT10
378 379
20
 [ I2CDebug
380
        DebugReg a3, "I2C_CNT="
381
 ]
382
        STRH    a3, [v4, #I2C_CNT]
383
        ; 5. Wait for I2C_STAT.BB to 0 (but only if this is the first iic_transfer of a sequence - otherwise we'll be stuck waiting to unlock a bus we already own)
384 385 386 387
        LDR     v2, [v5, #I2C_XStart]
        CMP     v1, v2 ; v1 = I2C_XCurrent from earlier
        BNE     %FT20
        MOV     v2, #50*1024 ; timeout
388
10
389 390 391 392 393
        LDRH    v3, [v4, #I2C_STAT]
        TST     v3, #1:SHL:12
        BEQ     %FT20
        SUBS    v2, v2, #1
        BNE     %BT10
394
 [ I2CDebug
395
        DebugReg v3, "BB timeout: I2C_STAT="
396
 ]
397 398
        MOV     a1, #IICStatus_Busy
        B       clear_and_return
399 400
20
        ; 6. configure I2C_CON.STT=1, I2C_CON.STP=0/1
401
        ORR     a4, a4, #1 ; Always send start bit
402
        ; If required, stop bit will already have been set by the I2C_CNT calculator
403
        STRH    a4, [v4, #I2C_CON]
404 405
        ; Now we just sit back and wait for the interrupts?
 [ I2CDebug
406
        DebugTX "Transfer started"
407
 ]
408 409
        MOV     a1, #IICStatus_InProgress
        LDMFD   sp!, {v1-v5,pc}
410 411 412 413 414 415 416 417

        ; For receive:
        ; 1. Use I2C_IE.RRDY_IE
        ; 2. Except we use I2C_IE.RDR_IE if the receive size doesn't match the RX FIFO threshold?
        ; For transmit:
        ; 1. Use I2C_IE.XRDY_IE
        ; 2. Except we use I2C_IE.XDR_IE if the length doesn't match the TX FIFO threshold?

418
; Return IICStatus state for transfer on bus r0
419 420 421
; Called on appropriate interrupt
HAL_IICMonitorTransfer
        ; Process the interrupts, according to figures 18-31/18-32 in spruf98b
422
        STMFD   sp!, {v1-v5,lr}
423
 [ I2CDebug
424
        DebugReg a1, "HAL_IICMonitorTransfer: bus "
425
 ]
426 427 428 429 430
        ADR     v5, I2C_Table
        MOV     v4, #I2CBlockSize
        MLA     v5, a1, v4, v5
        LDR     v4, [v5, #I2C_HW]
        LDR     a1, [v5, #I2C_XStart]
431
 [ I2CDebug
432
        DebugReg a1, "XStart="
433
 ]
434 435 436
        LDRH    v3, [v4, #I2C_STAT]
        TEQ     a1, #0 ; If no transfer, shut off all interrupts
        ASSERT  IICStatus_Completed=0
437
 [ I2CDebug
438 439 440
        BNE     %FT10
        DebugTX "No XStart!"
        TEQ     a1, #0 ; reset EQ condition
441 442
10
 ]
443 444 445
        STREQH  a1, [v4, #I2C_IE]
        STREQH  v3, [v4, #I2C_STAT]
        LDMEQFD sp!, {v1-v5,pc}
446
 [ I2CDebug
447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
        DebugReg v3, "I2C_STAT="
 ]
        TST     v3, #2
        BNE     i2c_nack
        TST     v3, #1
        BNE     i2c_al
        TST     v3, #4
        BNE     i2c_ardy
        TST     v3, #1:SHL:14
        BNE     i2c_xdr
        TST     v3, #1:SHL:4
        BNE     i2c_xrdy
        TST     v3, #1:SHL:13
        BNE     i2c_rdr
        TST     v3, #1:SHL:3
        BNE     i2c_rrdy
463
        ; Did anything actually happen?
464 465 466 467
        BIC     v3, v3, #&1d00 ; Clear XUDF, ROVR, BB, BF - they're status bits and don't indicate anything we care about here
        CMP     v3, #0
        MOVEQ   a1, #IICStatus_InProgress ; If nothing interesting happened, claim everything is OK (required for polling-mode transfers, e.g. HAL_VideoIICOp)
        LDMEQFD sp!, {v1-v5,pc}
468
        ; Else bad stuff - unhandled interrupt
469
 [ I2CDebugError
470
        DebugReg v3, "Unhandled IRQ - "
471
 ]
472
unexpected_error
473
        MOV     a1, #IICStatus_Error
474 475
clear_and_return
 [ I2CDebug
476
        DebugReg a1, "clear_and_return: "
477
 ]
478 479 480 481
        STRH    v3, [v4, #I2C_STAT]
        MOV     v3, #0
        STR     v3, [v5, #I2C_XStart]
        LDMFD   sp!, {v1-v5,pc}
482 483 484

i2c_nack
        ; No ack was received - give up and return error
485 486
        MOV     a1, #IICStatus_NoACK
        B       clear_and_return
487 488 489

i2c_al
        ; Arbitration lost - restart the transfer list
490 491 492
        STRH    v3, [v4, #I2C_STAT]
        LDR     v1, [v5, #I2C_XStart]
        B       start_transfer
493 494 495

i2c_ardy
        ; Previous transfer has completed successfully; start a new one
496 497 498 499
        LDR     a3, [v5, #I2C_XCurrent]
        LDR     a2, [v5, #I2C_XBytes]
        LDR     ip, [a3, #8]
        CMP     a2, ip
500
 [ I2CDebugError
501 502 503 504 505
        BEQ     %FT10
        DebugTX "HW requested wrong byte count"
        DebugReg a2, "I2C_XBytes="
        DebugReg ip, "xfer len="
        B       unexpected_error
506 507
10
 |
508
        BNE     unexpected_error ; Hardware hasn't requested the full number of bytes
509
 ]
510 511 512 513
        LDR     a4, [v5, #I2C_XEnd]
        CMP     a3, a4
        MOVEQ   a1, #IICStatus_Completed
        BEQ     clear_and_return
514
        ; Skip any zero-length nostart transfers that follow this one
515
        ADD     v1, a3, #12
516
10
517 518 519 520 521
        LDMIA   v1, {a1-a3}
        TST     a1, #1:SHL:31
        STREQH  v3, [v4, #I2C_STAT]
        BEQ     start_transfer
        CMP     a3, #0
522
 [ I2CDebugError
523 524 525
        BEQ     %FT5
        DebugReg a3, "nostart transfer with nonzero length, length="
        B       unexpected_error
526 527
5
 |
528
        BNE     unexpected_error ; nostart transfer with nonzero length = hardware hasn't requested full number of bytes
529
 ]
530 531 532 533 534
        CMP     v1, a4
        MOVEQ   a1, #IICStatus_Completed
        BEQ     clear_and_return
        ADD     v1, v1, #12
        B       %BT10
535

536 537 538

i2c_xdr
        ; Transfer I2C_BUFSTAT[5:0] bytes
539 540 541
        LDRH    a4, [v4, #I2C_BUFSTAT]
        AND     a4, a4, #&3F
        B       send_bytes
542 543 544

i2c_xrdy
        ; Transfer I2C_BUF[5:0]+1 bytes
545 546 547
        LDRH    a4, [v4, #I2C_BUF]
        AND     a4, a4, #&3F
        ADD     a4, a4, #1
548 549 550 551
send_bytes
 [ I2CDebug
        DebugReg  a4, "send_bytes: "
 ]
552 553 554
        LDR     v2, [v5, #I2C_XCurrent]
        LDR     a3, [v5, #I2C_XBytes] ; Get bytes sent
        LDMIB   v2, {a2,ip} ; Get data ptr, transfer length
555
10
556
        ; Check if we need to advance to the next iic_transfer
557 558 559
        CMP     a3, ip
        BEQ     %FT20
        LDRB    v1, [a2,a3]
560
 [ I2CDebugData
561
        DebugRegByte v1
562
 ]
563 564 565 566
        ADD     a3, a3, #1
        SUBS    a4, a4, #1
        STRB    v1, [v4, #I2C_DATA]
        BNE     %BT10
567 568 569
 [ I2CDebugData
        DebugTX ""
 ]
570 571 572 573
        STR     a3, [v5, #I2C_XBytes]
        MOV     a1, #IICStatus_InProgress
        STRH    v3, [v4, #I2C_STAT]
        LDMIA   sp!, {v1-v5,pc}
574 575
20
        ; Advance to next iic_transfer
576 577
        LDR     a3, [v5, #I2C_XEnd]
        CMP     a3, v2
578
 [ I2CDebugError
579 580 581
        BNE     %FT5
        DebugTX "End of transfer list but hardware wants more"
        B       unexpected_error
582 583
5
 |
584 585 586 587 588 589 590 591 592
        BEQ     unexpected_error ; Hardware is asking for more data than we can give
 ]
        ADD     v2, v2, #12
        STR     v2, [v5, #I2C_XCurrent]
        MOV     a3, #0
        STR     a3, [v5, #I2C_XBytes]
        LDMIA   v2, {a1-a2,ip}
        TST     a1, #1:SHL:31
        BNE     %BT10
593
 [ I2CDebugError
594
        DebugTX "nostart reached but hardware wants more"
595
 ]
596
        B       unexpected_error ; Hardware is asking for more data than we can give
597 598 599

i2c_rdr
        ; Read I2C_BUFSTAT[13:8] bytes
600 601 602 603 604
        LDRH    a4, [v4, #I2C_BUFSTAT]
        MOV     a4, a4, LSR #8
        ANDS    a4, a4, #&3F ; ERRATA - sometimes RDR is set when there's no data. So, don't attempt to read from the empty buffer.
        BEQ     %FT15
        B       read_bytes
605 606 607

i2c_rrdy
        ; Read I2C_BUF[13:8]+1 bytes
608 609 610 611
        LDRH    a4, [v4, #I2C_BUF]
        MOV     a4, a4, LSR #8
        AND     a4, a4, #&3F
        ADD     a4, a4, #1
612 613 614 615
read_bytes
 [ I2CDebug
        DebugReg  a4, "read_bytes: "
 ]
616 617 618
        LDR     v2, [v5, #I2C_XCurrent]
        LDMIA   v2, {a1-a2,ip} ; Get checksum flag, data ptr/checksum, transfer length
        LDR     a3, [v5, #I2C_XBytes] ; Get bytes received
619
10
620
        ; Check if we need to advance to the next iic_transfer
621 622 623 624 625 626
        CMP     a3, ip
        BEQ     %FT20
        TST     a1, #1:SHL:30 ; Checksum mode?
        LDRB    v1, [v4, #I2C_DATA]
        ADDNE   a2, a2, v1 ; adjust checksum
        STREQB  v1, [a2, a3]
627
 [ I2CDebugData
628
        DebugRegByte    v1
629
 ]
630 631 632
        SUBS    a4, a4, #1
        ADD     a3, a3, #1
        BNE     %BT10
633 634 635
 [ I2CDebugData
        DebugTX ""
 ]
636 637
        STR     a2, [v2, #4] ; Update checksum
        STR     a3, [v5, #I2C_XBytes]
638
15
639 640 641
        MOV     a1, #IICStatus_InProgress
        STRH    v3, [v4, #I2C_STAT]
        LDMIA   sp!, {v1-v5,pc}
642 643
20
        ; Advance to next iic_transfer
644 645
        LDR     a3, [v5, #I2C_XEnd]
        CMP     a3, v2
646
 [ I2CDebugError
647 648 649
        BNE     %FT5
        DebugTX "End of transfer list but hardware received more data"
        B       unexpected_error
650 651
5
 |
652 653 654 655 656 657 658 659
        BEQ     unexpected_error ; Hardware is receiving more data than we want
 ]
        ADD     v2, v2, #12
        STR     v2, [v5, #I2C_XCurrent]
        MOV     a3, #0
        STR     a3, [v5, #I2C_XBytes]
        LDMIA   v2, {a1-a2,ip}
        TST     a1, #1:SHL:31
660
 [ I2CDebugError
661 662 663
        BNE     %FT5
        DebugTX "nostart reached but hardware received more data"
        B       unexpected_error
664 665
5
 |
666
        BEQ     unexpected_error ; Hardware is receiving more data than we want
667
 ]
668 669 670
        TST     a1, #1:SHL:30 ; Checksum mode?
        MOVNE   a2, #0 ; Start with zero checksum (as per start_transfer)
        B       %BT10
671 672


673 674
; int HAL_VideoIICOp(uint32_t op, uint8_t *buffer, uint32_t *size)
; in:
675
;      r0 = b0-15 offset within IIC device to start at
676 677 678 679 680 681 682 683 684 685
;           b16-23 base IICAddress
;           b24-31 zero
;      r1 = buffer to read from/write to
;      r2 = pointer to number of bytes to transfer
; returns:
;      r0 = IICStatus return code
;      size = bytes successfully transferred (prior to any error)

HAL_VideoIICOp
        ; Make sure we've got a valid IIC bus to use
686 687 688 689 690 691
        LDRB    a4, [sb, #BoardConfig_VideoI2C]
        CMP     a4, #255
        MOV     ip, #0
        STREQ   ip, [a3]
        MOVEQ   a1, #IICStatus_Error
        MOVEQ   pc, lr
692 693 694 695 696 697 698 699 700 701 702
        ; Check if this is an EDID read or write
        UBFX    a4, a1, #16, #8
        TEQ     a4, #&a0 ; Don't allow writing to EDID for safety reasons
        STREQ   ip, [a3]
        MOVEQ   a1, #IICStatus_Error
        MOVEQ   pc, lr
        TEQ     a4, #&a1
        TSTNE   a1, #&ff00 ; If not EDID read, limit to 0-255 offset in device
        STRNE   ip, [a3]
        MOVNE   a1, #IICStatus_Completed
        MOVNE   pc, lr
703 704
        Push    "a1-a3,lr"
        LDR     a3, [a3]
705 706

        ; Build a set of iic_transfer blocks and call RISCOS_IICOpV
707 708 709 710 711 712
        ; We construct (up to) three iic_transfer blocks
        ; - First block is an (optional) single byte write to the EDID segment pointer
        ; - Second block is a single byte write containing the start address (lower 8 bits of r0)
        ; - Third block is a read. r2 bytes written to r1.
        ; The E-EDID EEPROM spec says that the segment pointer should auto-increment when a sequential (i.e. block) read occurs, so we shouldn't have to worry about splitting requests into 256 byte blocks and manually writing the pointer each time.
        ; Block 3:
713 714
        UBFX    a1, a1, #16, #8 ; Extract base IICAddress
        Push    "a1-a3"         ; Push the block on the stack (a2 & a3 are already correct)
715
        ; Block 2:
716 717 718 719
        BIC     a1, a1, #1      ; Clear RnW of base address
        ADD     a2, sp, #12     ; sp+12 should point to the 8 bit offset
        MOV     a3, #1
        Push    "a1-a3"
720 721 722 723 724 725 726 727 728 729
        ; Block 1:
        MOV     a1, #&60        ; Write to segment pointer
        ADD     a2, a2, #1      ; With bits 8-15 of the offset
        Push    "a1-a3"
        ; Work out if block 1 is needed or not
        LDR     a2, [sp, #36]   ; Get r0
        TST     a2, #&ff00      ; If segment == 0
        MOVEQ   a2, #0          ; ... then avoid matching address &A0/&A1
        AND     a2, a2, #&fe0000
        TEQ     a2, #&a00000
730
        ; Now attempt to start the transfer
731 732
        LDRB    a2, [sb, #BoardConfig_VideoI2C]
        MOV     a2, a2, LSL #24
733
        ADD     a2, a2, #3
734
        MOV     a1, sp
735 736
        SUBNE   a2, a2, #1      ; Skip block 1 if segment == 0 or not EDID addr
        ADDNE   a1, a1, #12
737
        ; If HAL_Init isn't done yet, we can't use RISCOS_IICOpV
738 739 740 741 742 743
        LDR     a3, HALInitialised
        CMP     a3, #0
        BEQ     %FT10
        LDR     a3, OSentries+4*OS_IICOpV
        BLX     a3
        B       %FT20
744
10
745
        BL      IIC_DoOp_Poll
746 747
20
        ; In case of error, assume nothing got transferred at all
748
        CMP     a1, #IICStatus_Completed
749
        LDREQ   a4, [sp, #(12*2)+(2*4)] ; Block 3 request size
750
        MOVNE   a4, #0
751
        ADD     sp, sp, #12*3           ; Junk the iic_transfer blocks
752 753 754 755
        STR     a1, [sp, #0]            ; Propagate return code
        LDR     a3, [sp, #8]
        STR     a4, [a3]                ; Actual transfer size
        Pull    "a1-a3,pc"
756

Jeffrey Lee's avatar
Jeffrey Lee committed
757
IIC_DoOp_Poll
758
        ; IIC transfer function that performs a polling transfer, similar to HAL_VideoIICOp
Jeffrey Lee's avatar
Jeffrey Lee committed
759 760 761
        ; This allows us to do IIC transfers before RISC OS is fully initialised (e.g. from inside HAL_Init)
        ; Parameters are identical to RISCOS_IICOpV:
        ; r0 = iic_transfer array ptr
762 763
        ; r1 = bits 0-23: iic_transfer count
        ;      bits 24-31: bus number
764
        ; Returns IICStatus return code in R0 (0 success, anything else failure)
765
        Push    "v1,lr"
766 767
 [ {FALSE}
        ; If IRQs and IIC IRQ are enabled, panic
768 769 770 771 772 773 774 775 776 777 778 779
        Push    "a1-a4"
        MRS     a1, CPSR
        TST     a1, #I32_bit
        BNE     %FT10
        ADR     a1, BoardConfig_HALI2CIRQ
        LDRB    a1, [a1, a2, LSR #24]
        IMPORT  HAL_IRQDisable
        BL      HAL_IRQDisable
        CMP     a1, #0
        BEQ     %FT10
        DebugTX "Warning - IIC_DoOp_Poll called with IIC IRQ enabled!"
        B       .
780
10
781
        Pull    "a1-a4"
782
 ]
783 784 785 786 787
        MOV     a3, a1
        MOV     a1, a2, LSR #24
        BIC     a2, a2, #&ff000000
        MOV     v1, a1
        BL      HAL_IICTransfer
Jeffrey Lee's avatar
Jeffrey Lee committed
788 789
        ; Now just poll until we're done
10
790 791 792 793 794
        CMP     a1, #IICStatus_InProgress ; Done?
        Pull    "v1,pc", NE
        ADR     lr, %BT10
        MOV     a1, v1
        B       HAL_IICMonitorTransfer
795 796

        END