I2C 25.9 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 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
        MOVHS   a1, #0
        LDRLO   a1, =IICFlag_HighLevel+IICFlag_Fast+IICFlag_Background+IICFlag_HighSpeed+(210:SHL:20)
        MOV     pc, lr

; HAL_IICDevice
; in:
;       r0 = irq_descriptor ptr
;       r1 = bus number
; out:
;       r0 filled in
; typedef struct irq_descriptor
; {
;     int device;
;     union {
;        struct {
;          unsigned char *addr;
;          int maskandpolarity;
;        } bit;
;        struct {
;          int (*forme)(void *handle);
;          void *handle;
;        } func;
;     } sub;
; } irq_descriptor;
;
HAL_IICDevice
199 200
        LDRB    a3, [sb, #BoardConfig_NumI2C]
        CMP     a2, a3
201
        MOVHS   a2, #-1
202
        ADDLO   a3, sb, #BoardConfig_HALI2CIRQ
203 204 205 206 207 208 209 210 211 212 213 214
        LDRLOB  a2, [a3, a2]
        MOV     a3, #0
        MOV     a4, #0
        STMIA   a1, {a2-a4}
        MOV     pc, lr

; HAL_IICTransfer
; in:
;      r0 = bus number
;      r1 = number of transfers
;      r2 = iic_transfer array ptr
; out:
215
;      r0 = IICStatus return code
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
; 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
231 232 233 234
        LDRB    a4, [sb, #BoardConfig_NumI2C]
        CMP     a1, a4
        MOVHS   a1, #IICStatus_Error
        MOVHS   pc, lr
235
        ; Quickly validate the transfer list
236 237 238 239
        ; 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)
240 241 242 243 244 245 246 247 248 249 250
        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
251
30
252
        MOV     v5, #-1
253
10
254 255 256 257 258 259
        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
260
20
261 262 263 264 265
        CMP     v5, #65536
        MOVHS   a1, #IICStatus_Error    ; Too much (or too little) data
        LDMHSIA sp! ,{v1-v5,pc}
        CMP     v1, a4
        BNE     %BT30
266
_IICTransfer_Video ; Entry point for HAL_VideoIICOp
267 268 269 270 271 272
 [ I2CDebug
        DebugTX "HAL_IICTransfer"
        DebugReg a1, "bus="
        DebugReg a2, "num="
        DebugReg a3, "iic_transfer="
 ]
273 274 275 276 277 278 279 280 281 282 283
        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"
284
 [ I2CDebug
285
        BEQ     %FT10
286
        DebugReg a4, "BUSY: XStart="
287
        LDMFD   sp!, {v1-v5,pc}
288 289 290
10
        DebugTX "OK"
 |
291
        LDMNEFD sp!, {v1-v5,pc}
292
 ]
293 294 295 296 297
        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]
298
        ; Make sure controller is enabled, since we don't do any initialisation atm!
299 300 301
        LDRH    a4, [v4, #I2C_CON]
        TST     a4, #1:SHL:15
        LDREQH  v3, [v4, #I2C_STAT]
302
 [ I2CDebugError
303 304 305
        BNE     %FT10
        DebugTX "Controller not enabled!"
        B       unexpected_error
306 307
10
 |
308
        BEQ     unexpected_error
309
 ]
310
        MOV     v1, a3
311
start_transfer ; Start the transfer in v1
312 313 314 315 316
        ; a1-a4 free
        ; v1 = iic_transfer to start
        ; v2-v3 free
        ; v4 = I2C controller ptr
        ; v5 = I2C state ptr
317 318 319
 [ I2CDebug
        DebugReg v1, "start_transfer: "
 ]
320 321 322
        STR     v1, [v5, #I2C_XCurrent]
        MOV     lr, #0
        STR     lr, [v5, #I2C_XBytes]
323
        ; Get its info
324
        LDMIA   v1, {a1-a3}
325 326 327 328 329 330
 [ I2CDebug
        DebugReg a1, "addr="
        DebugReg a2, "data="
        DebugReg a3, "len ="
 ]
        ; If it's a 'checksum-only' read, clear the checksum to 0
331 332 333 334
        TST     a1, #1:SHL:30
        TSTNE   a1, #1
        MOVNE   a2, #0
        STRNE   a2, [v1, #4]
335
        ; 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.
336
        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)
337
10
338 339 340 341 342
        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
343
 [ I2CDebug
344
        DebugReg a4, "ISC_CON timeout: "
345
 ]
346 347 348 349
        MOV     a1, #IICStatus_Busy
        MOV     v3, #0
        STR     v3, [v5, #I2C_XStart]
        LDMFD   sp!, {v1-v5,pc}
350
20
351 352
        ; 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
353 354 355 356
        LDRH    v2, [v4, #I2C_BUF]
        LDR     v3, =&4040
        ORR     v2, v2, v3
        STRH    v2, [v4, #I2C_BUF]
357
10
358 359 360
        LDRH    v2, [v4, #I2C_BUF]
        TST     v2, v3
        BNE     %BT10
361 362
        ; Configure controller
        ; 1. I2C_CON.MST, ISC_CON.TRX
363 364 365 366 367 368
        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]
369
        ; Clear any pending interrupts, just in case?
370 371
        LDRH    v2, [v4, #I2C_STAT]
        STRH    v2, [v4, #I2C_STAT]
372
        ; 2. I2C_IE.XRDY_IE, I2C_IE.RRDY
373 374 375 376 377
        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]
378 379
        ; 3. Ignore DMA for now
        ; 4. I2C_SA, I2C_CNT
380 381 382
        MOV     v2, a1, LSR #1
        AND     v2, v2, #&7F
        STRH    v2, [v4, #I2C_SA]
383 384
        ; 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
385 386
        MOV     v2, v1
        LDR     ip, [v5, #I2C_XEnd]
387
10
388 389 390 391 392 393 394 395
        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
396 397
20
 [ I2CDebug
398
        DebugReg a3, "I2C_CNT="
399
 ]
400
        STRH    a3, [v4, #I2C_CNT]
401
        ; 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)
402 403 404 405
        LDR     v2, [v5, #I2C_XStart]
        CMP     v1, v2 ; v1 = I2C_XCurrent from earlier
        BNE     %FT20
        MOV     v2, #50*1024 ; timeout
406
10
407 408 409 410 411
        LDRH    v3, [v4, #I2C_STAT]
        TST     v3, #1:SHL:12
        BEQ     %FT20
        SUBS    v2, v2, #1
        BNE     %BT10
412
 [ I2CDebug
413
        DebugReg v3, "BB timeout: I2C_STAT="
414
 ]
415 416
        MOV     a1, #IICStatus_Busy
        B       clear_and_return
417 418
20
        ; 6. configure I2C_CON.STT=1, I2C_CON.STP=0/1
419
        ORR     a4, a4, #1 ; Always send start bit
420
        ; If required, stop bit will already have been set by the I2C_CNT calculator
421
        STRH    a4, [v4, #I2C_CON]
422 423
        ; Now we just sit back and wait for the interrupts?
 [ I2CDebug
424
        DebugTX "Transfer started"
425
 ]
426 427
        MOV     a1, #IICStatus_InProgress
        LDMFD   sp!, {v1-v5,pc}
428 429 430 431 432 433 434 435

        ; 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?

436
; Return IICStatus state for transfer on bus r0
437 438 439
; Called on appropriate interrupt
HAL_IICMonitorTransfer
        ; Process the interrupts, according to figures 18-31/18-32 in spruf98b
440
        STMFD   sp!, {v1-v5,lr}
441
 [ I2CDebug
442
        DebugReg a1, "HAL_IICMonitorTransfer: bus "
443
 ]
444 445 446 447 448
        ADR     v5, I2C_Table
        MOV     v4, #I2CBlockSize
        MLA     v5, a1, v4, v5
        LDR     v4, [v5, #I2C_HW]
        LDR     a1, [v5, #I2C_XStart]
449
 [ I2CDebug
450
        DebugReg a1, "XStart="
451
 ]
452 453 454
        LDRH    v3, [v4, #I2C_STAT]
        TEQ     a1, #0 ; If no transfer, shut off all interrupts
        ASSERT  IICStatus_Completed=0
455
 [ I2CDebug
456 457 458
        BNE     %FT10
        DebugTX "No XStart!"
        TEQ     a1, #0 ; reset EQ condition
459 460
10
 ]
461 462 463
        STREQH  a1, [v4, #I2C_IE]
        STREQH  v3, [v4, #I2C_STAT]
        LDMEQFD sp!, {v1-v5,pc}
464
 [ I2CDebug
465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
        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
481
        ; Did anything actually happen?
482 483 484 485
        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}
486
        ; Else bad stuff - unhandled interrupt
487
 [ I2CDebugError
488
        DebugReg v3, "Unhandled IRQ - "
489
 ]
490
unexpected_error
491
        MOV     a1, #IICStatus_Error
492 493
clear_and_return
 [ I2CDebug
494
        DebugReg a1, "clear_and_return: "
495
 ]
496 497 498 499
        STRH    v3, [v4, #I2C_STAT]
        MOV     v3, #0
        STR     v3, [v5, #I2C_XStart]
        LDMFD   sp!, {v1-v5,pc}
500 501 502

i2c_nack
        ; No ack was received - give up and return error
503 504
        MOV     a1, #IICStatus_NoACK
        B       clear_and_return
505 506 507

i2c_al
        ; Arbitration lost - restart the transfer list
508 509 510
        STRH    v3, [v4, #I2C_STAT]
        LDR     v1, [v5, #I2C_XStart]
        B       start_transfer
511 512 513

i2c_ardy
        ; Previous transfer has completed successfully; start a new one
514 515 516 517
        LDR     a3, [v5, #I2C_XCurrent]
        LDR     a2, [v5, #I2C_XBytes]
        LDR     ip, [a3, #8]
        CMP     a2, ip
518
 [ I2CDebugError
519 520 521 522 523
        BEQ     %FT10
        DebugTX "HW requested wrong byte count"
        DebugReg a2, "I2C_XBytes="
        DebugReg ip, "xfer len="
        B       unexpected_error
524 525
10
 |
526
        BNE     unexpected_error ; Hardware hasn't requested the full number of bytes
527
 ]
528 529 530 531
        LDR     a4, [v5, #I2C_XEnd]
        CMP     a3, a4
        MOVEQ   a1, #IICStatus_Completed
        BEQ     clear_and_return
532
        ; Skip any zero-length nostart transfers that follow this one
533
        ADD     v1, a3, #12
534
10
535 536 537 538 539
        LDMIA   v1, {a1-a3}
        TST     a1, #1:SHL:31
        STREQH  v3, [v4, #I2C_STAT]
        BEQ     start_transfer
        CMP     a3, #0
540
 [ I2CDebugError
541 542 543
        BEQ     %FT5
        DebugReg a3, "nostart transfer with nonzero length, length="
        B       unexpected_error
544 545
5
 |
546
        BNE     unexpected_error ; nostart transfer with nonzero length = hardware hasn't requested full number of bytes
547
 ]
548 549 550 551 552
        CMP     v1, a4
        MOVEQ   a1, #IICStatus_Completed
        BEQ     clear_and_return
        ADD     v1, v1, #12
        B       %BT10
553

554 555 556

i2c_xdr
        ; Transfer I2C_BUFSTAT[5:0] bytes
557 558 559
        LDRH    a4, [v4, #I2C_BUFSTAT]
        AND     a4, a4, #&3F
        B       send_bytes
560 561 562

i2c_xrdy
        ; Transfer I2C_BUF[5:0]+1 bytes
563 564 565
        LDRH    a4, [v4, #I2C_BUF]
        AND     a4, a4, #&3F
        ADD     a4, a4, #1
566 567 568 569
send_bytes
 [ I2CDebug
        DebugReg  a4, "send_bytes: "
 ]
570 571 572
        LDR     v2, [v5, #I2C_XCurrent]
        LDR     a3, [v5, #I2C_XBytes] ; Get bytes sent
        LDMIB   v2, {a2,ip} ; Get data ptr, transfer length
573
10
574
        ; Check if we need to advance to the next iic_transfer
575 576 577
        CMP     a3, ip
        BEQ     %FT20
        LDRB    v1, [a2,a3]
578
 [ I2CDebugData
579
        DebugRegByte v1
580
 ]
581 582 583 584
        ADD     a3, a3, #1
        SUBS    a4, a4, #1
        STRB    v1, [v4, #I2C_DATA]
        BNE     %BT10
585 586 587
 [ I2CDebugData
        DebugTX ""
 ]
588 589 590 591
        STR     a3, [v5, #I2C_XBytes]
        MOV     a1, #IICStatus_InProgress
        STRH    v3, [v4, #I2C_STAT]
        LDMIA   sp!, {v1-v5,pc}
592 593
20
        ; Advance to next iic_transfer
594 595
        LDR     a3, [v5, #I2C_XEnd]
        CMP     a3, v2
596
 [ I2CDebugError
597 598 599
        BNE     %FT5
        DebugTX "End of transfer list but hardware wants more"
        B       unexpected_error
600 601
5
 |
602 603 604 605 606 607 608 609 610
        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
611
 [ I2CDebugError
612
        DebugTX "nostart reached but hardware wants more"
613
 ]
614
        B       unexpected_error ; Hardware is asking for more data than we can give
615 616 617

i2c_rdr
        ; Read I2C_BUFSTAT[13:8] bytes
618 619 620 621 622
        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
623 624 625

i2c_rrdy
        ; Read I2C_BUF[13:8]+1 bytes
626 627 628 629
        LDRH    a4, [v4, #I2C_BUF]
        MOV     a4, a4, LSR #8
        AND     a4, a4, #&3F
        ADD     a4, a4, #1
630 631 632 633
read_bytes
 [ I2CDebug
        DebugReg  a4, "read_bytes: "
 ]
634 635 636
        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
637
10
638
        ; Check if we need to advance to the next iic_transfer
639 640 641 642 643 644
        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]
645
 [ I2CDebugData
646
        DebugRegByte    v1
647
 ]
648 649 650
        SUBS    a4, a4, #1
        ADD     a3, a3, #1
        BNE     %BT10
651 652 653
 [ I2CDebugData
        DebugTX ""
 ]
654 655
        STR     a2, [v2, #4] ; Update checksum
        STR     a3, [v5, #I2C_XBytes]
656
15
657 658 659
        MOV     a1, #IICStatus_InProgress
        STRH    v3, [v4, #I2C_STAT]
        LDMIA   sp!, {v1-v5,pc}
660 661
20
        ; Advance to next iic_transfer
662 663
        LDR     a3, [v5, #I2C_XEnd]
        CMP     a3, v2
664
 [ I2CDebugError
665 666 667
        BNE     %FT5
        DebugTX "End of transfer list but hardware received more data"
        B       unexpected_error
668 669
5
 |
670 671 672 673 674 675 676 677
        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
678
 [ I2CDebugError
679 680 681
        BNE     %FT5
        DebugTX "nostart reached but hardware received more data"
        B       unexpected_error
682 683
5
 |
684
        BEQ     unexpected_error ; Hardware is receiving more data than we want
685
 ]
686 687 688
        TST     a1, #1:SHL:30 ; Checksum mode?
        MOVNE   a2, #0 ; Start with zero checksum (as per start_transfer)
        B       %BT10
689 690


691 692 693 694 695 696 697 698 699 700 701 702 703
; int HAL_VideoIICOp(uint32_t op, uint8_t *buffer, uint32_t *size)
; in:
;      r0 = b0-15 offset within IIC device to start at (currently assumed 8 bit)
;           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
704 705 706 707 708 709
        LDRB    a4, [sb, #BoardConfig_VideoI2C]
        CMP     a4, #255
        MOV     ip, #0
        STREQ   ip, [a3]
        MOVEQ   a1, #IICStatus_Error
        MOVEQ   pc, lr
710
        ; Check for input passed end
711 712 713 714 715
        UBFX    a4, a1, #0, #16
        CMP     a4, #256
        STRCS   ip, [a3]
        MOVCS   a1, #IICStatus_Completed
        MOVCS   pc, lr
716
        ; Clip request at end
717 718 719 720 721
        Push    "a1-a3,lr"
        LDR     a3, [a3]
        ADD     ip, a4, a3
        CMP     ip, #256
        RSBHI   a3, a4, #256
722 723 724 725 726

        ; Build a set of iic_transfer blocks and call RISCOS_IICOpV
        ; We construct two iic_transfer blocks
        ; - First block is a single byte write containing the start address (lower 8 bits of r0)
        ; - Second block is a read. r2 bytes written to r1.
727
        ; Block 2:
728 729
        UBFX    a1, a1, #16, #8 ; Extract base IICAddress
        Push    "a1-a3"         ; Push the block on the stack (a2 & a3 are already correct)
730
        ; Block 1:
731 732 733 734
        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"
735

736
        ; Now attempt to start the transfer
737 738 739 740
        LDRB    a2, [sb, #BoardConfig_VideoI2C]
        MOV     a2, a2, LSL #24
        ADD     a2, a2, #2
        MOV     a1, sp
741
        ; If HAL_Init isn't done yet, we can't use RISCOS_IICOpV
742 743 744 745 746 747
        LDR     a3, HALInitialised
        CMP     a3, #0
        BEQ     %FT10
        LDR     a3, OSentries+4*OS_IICOpV
        BLX     a3
        B       %FT20
748
10
749
        BL      IIC_DoOp_Poll
750 751
20
        ; In case of error, assume nothing got transferred at all
752 753 754 755 756 757 758 759
        CMP     a1, #IICStatus_Completed
        LDREQ   a4, [sp, #(3*4)+(2*4)]  ; Clipped block 2 request size
        MOVNE   a4, #0
        ADD     sp, sp, #24             ; Junk the iic_transfer blocks
        STR     a1, [sp, #0]            ; Propagate return code
        LDR     a3, [sp, #8]
        STR     a4, [a3]                ; Actual transfer size
        Pull    "a1-a3,pc"
760

Jeffrey Lee's avatar
Jeffrey Lee committed
761
IIC_DoOp_Poll
762
        ; IIC transfer function that performs a polling transfer, similar to HAL_VideoIICOp
Jeffrey Lee's avatar
Jeffrey Lee committed
763 764 765
        ; 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
766 767
        ; r1 = bits 0-23: iic_transfer count
        ;      bits 24-31: bus number
768
        ; Returns IICStatus return code in R0 (0 success, anything else failure)
769
        Push    "v1,lr"
770 771
 [ {FALSE}
        ; If IRQs and IIC IRQ are enabled, panic
772 773 774 775 776 777 778 779 780 781 782 783
        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       .
784
10
785
        Pull    "a1-a4"
786
 ]
787 788 789 790 791
        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
792 793
        ; Now just poll until we're done
10
794 795 796 797 798
        CMP     a1, #IICStatus_InProgress ; Done?
        Pull    "v1,pc", NE
        ADR     lr, %BT10
        MOV     a1, v1
        B       HAL_IICMonitorTransfer
799 800

        END