I2C 26.5 KB
Newer Older
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
; 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.
;
        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
        EXPORT  HAL_Video_IICOp
Jeffrey Lee's avatar
Jeffrey Lee committed
40
        EXPORT  IIC_DoOp_Poll
41 42 43 44 45 46 47 48 49 50

        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.
; 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_Video_IICOp.
; 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
51
I2CDebug        SETL {FALSE} :LAND: Debug
52 53 54 55

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

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

59 60

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

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
84
        MOV     v1, #3
85 86 87 88 89 90
        ADR     v2, I2C_Table
10
 [ I2CDebug
        DebugReg  v1, "remaining busses: "
 ]
        LDR     v3, [v2, #I2C_HW]
91 92
        CMP     v3, #0
        BEQ     %FT30 ; Skip unused busses
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
        ; 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
120
        ; Set up the 12MHz sampling clock
121 122
        MOV     a4, #7
        STRH    a4, [v3, #I2C_PSC]
123
        ; Run at 100kbps for now. TODO - Add support for higher speeds!
124 125 126 127 128 129 130 131 132 133 134 135 136 137
        ; 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]
138 139 140
        ; Enable auto idle, smart idle. Both clocks off when in idle mode?
        MOV     a4, #1+(2<<3)
        STRH    a4, [v3, #I2C_SYSC]
141 142 143 144 145 146 147 148 149 150 151
        ; 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
152
30
153 154 155 156 157 158 159 160 161 162 163 164
 [ 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
165
        LDRB    a1, [sb, #BoardConfig_NumI2C]
166 167 168 169
        MOV     pc, lr

HAL_IICType
        ; todo - set the 'missing flags' alluded to in Kernel.Docs.HAL.MoreEnts? (multi-master & slave operation)
170 171
        LDRB    a2, [sb, #BoardConfig_NumI2C]
        CMP     a1, a2
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
        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
198 199
        LDRB    a3, [sb, #BoardConfig_NumI2C]
        CMP     a2, a3
200
        MOVHS   a2, #-1
201
        ADDLO   a3, sb, #BoardConfig_HALI2CIRQ
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
        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:
;      r0 = E* return code
; 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
230 231
        LDRB      a4, [sb, #BoardConfig_NumI2C]
        CMP       a1, a4
232 233 234
        MOVHS     a1, #EERROR
        MOVHS     pc, lr
        ; Quickly validate the transfer list
235 236 237 238
        ; 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)
239 240 241
        CMP       a2, #0
        MOVLT     a1, #EERROR
        MOVLT     pc, lr
242 243 244 245 246
        LDR       a4, [a3]
        TST       a4, #1:SHL:31 ; First transfer has nostart set!
        MOVNE     a1, #EERROR
        MOVNE     pc, lr
        STMFD     sp!, {v1-v5,lr}
247 248
        ADD       a4, a3, a2, LSL #3
        ADD       a4, a4, a2, LSL #2
249 250 251
        MOV       v1, a3
30
        MOV       v5, #-1
252
10
253 254 255 256 257 258 259 260 261 262 263 264
        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
20
        CMP       v5, #65536
        MOVHS     a1, #EERROR ; Too much (or too little) data
        LDMHSIA   sp! ,{v1-v5,pc}
        CMP       v1, a4
        BNE       %BT30
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
_IICTransfer_Video ; Entry point for HAL_Video_IICOp
 [ I2CDebug
        DebugTX "HAL_IICTransfer"
        DebugReg a1, "bus="
        DebugReg a2, "num="
        DebugReg a3, "iic_transfer="
 ]
        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, #EBUSY              ; if it is, return "BUSY"
 [ I2CDebug
        BEQ %FT10
        DebugReg a4, "BUSY: XStart="
        LDMFD     sp!, {v1-v5,pc}
10
        DebugTX "OK"
 |
        LDMNEFD   sp!, {v1-v5,pc}
 ]
        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]
        ; Make sure controller is enabled, since we don't do any initialisation atm!
        LDRH      a4, [v4, #I2C_CON]
        TST       a4, #1:SHL:15
        LDREQH    v3, [v4, #I2C_STAT]
301 302 303 304 305 306
 [ I2CDebugError
        BNE       %FT10
        DebugTX   "Controller not enabled!"
        B         unexpected_error
10
 |
307
        BEQ       unexpected_error
308
 ]
309 310
        MOV       v1, a3
start_transfer ; Start the transfer in v1
311 312 313 314 315
        ; a1-a4 free
        ; v1 = iic_transfer to start
        ; v2-v3 free
        ; v4 = I2C controller ptr
        ; v5 = I2C state ptr
316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
 [ I2CDebug
        DebugReg v1, "start_transfer: "
 ]
        STR       v1, [v5, #I2C_XCurrent]
        MOV       lr, #0
        STR       lr, [v5, #I2C_XBytes]
        ; Get its info
        LDMIA     v1, {a1-a3}
 [ I2CDebug
        DebugReg a1, "addr="
        DebugReg a2, "data="
        DebugReg a3, "len ="
 ]
        ; If it's a 'checksum-only' read, clear the checksum to 0
        TST       a1, #1:SHL:30
        TSTNE     a1, #1
        MOVNE     a2, #0
        STRNE     a2, [v1, #4]
        ; 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.
335
        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)
336 337 338 339 340 341 342 343 344 345 346 347 348 349
10
        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
 [ I2CDebug
        DebugReg  a4, "ISC_CON timeout: "
 ]
        MOV       a1, #EBUSY
        MOV       v3, #0
        STR       v3, [v5, #I2C_XStart]
        LDMFD     sp!, {v1-v5,pc}
20
350 351 352 353 354 355 356 357 358 359
        ; 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
        LDRH      v2, [v4, #I2C_BUF]
        LDR       v3, =&4040
        ORR       v2, v2, v3
        STRH      v2, [v4, #I2C_BUF]
10
        LDRH      v2, [v4, #I2C_BUF]
        TST       v2, v3
        BNE       %BT10
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
        ; Configure controller
        ; 1. I2C_CON.MST, ISC_CON.TRX
        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]
        ; Clear any pending interrupts, just in case?
        LDRH      v2, [v4, #I2C_STAT]
        STRH      v2, [v4, #I2C_STAT]
        ; 2. I2C_IE.XRDY_IE, I2C_IE.RRDY
        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]
        ; 3. Ignore DMA for now
        ; 4. I2C_SA, I2C_CNT
        MOV       v2, a1, LSR #1
        AND       v2, v2, #&7F
        STRH      v2, [v4, #I2C_SA]
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398
        ; 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
        MOV       v2, v1
        LDR       ip, [v5, #I2C_XEnd]
10
        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
20
 [ I2CDebug
        DebugReg  a3, "I2C_CNT="
 ]
399 400 401 402 403
        STRH      a3, [v4, #I2C_CNT]
        ; 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)
        LDR       v2, [v5, #I2C_XStart]
        CMP       v1, v2 ; v1 = I2C_XCurrent from earlier
        BNE       %FT20
404
        MOV       v2, #50*1024 ; timeout
405 406 407 408 409 410 411 412 413 414 415 416 417
10
        LDRH      v3, [v4, #I2C_STAT]
        TST       v3, #1:SHL:12
        BEQ       %FT20
        SUBS      v2, v2, #1
        BNE       %BT10
 [ I2CDebug
        DebugReg  v3, "BB timeout: I2C_STAT="
 ]
        MOV       a1, #EBUSY
        B         clear_and_return
20
        ; 6. configure I2C_CON.STT=1, I2C_CON.STP=0/1
418 419
        ORR       a4, a4, #1 ; Always send start bit
        ; If required, stop bit will already have been set by the I2C_CNT calculator
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
        STRH      a4, [v4, #I2C_CON]
        ; Now we just sit back and wait for the interrupts?
 [ I2CDebug
        DebugTX   "Transfer started"
 ]
        MOV       a1, #EINPROGRESS
        LDMFD     sp!, {v1-v5,pc}

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

; Return E* state for transfer on bus r0
; Called on appropriate interrupt
HAL_IICMonitorTransfer
        ; Process the interrupts, according to figures 18-31/18-32 in spruf98b
        STMFD     sp!, {v1-v5,lr}
 [ I2CDebug
        DebugReg  a1, "HAL_IICMonitorTransfer: bus "
 ]
        ADR       v5, I2C_Table
        MOV       v4, #I2CBlockSize
        MLA       v5, a1, v4, v5
        LDR       v4, [v5, #I2C_HW]
        LDR       a1, [v5, #I2C_XStart]
 [ I2CDebug
        DebugReg  a1, "XStart="
 ]
        LDRH      v3, [v4, #I2C_STAT]
        TEQ       a1, #0 ; If no transfer, shut off all interrupts
        ASSERT    ECOMPLETED=0
 [ I2CDebug
        BNE       %FT10
        DebugTX   "No XStart!"
        TEQ       a1, #0 ; reset EQ condition
10
 ]
        STREQH    a1, [v4, #I2C_IE]
        STREQH    v3, [v4, #I2C_STAT]
        LDMEQFD   sp!, {v1-v5,pc}
 [ I2CDebug
        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
        ; Did anything actually happen?
481
        BIC       v3, v3, #&1d00 ; Clear XUDF, ROVR, BB, BF - they're status bits and don't indicate anything we care about here
482 483 484 485
        CMP       v3, #0
        MOVEQ     a1, #EINPROGRESS ; If nothing interesting happened, claim everything is OK (required for polling-mode transfers, e.g. HAL_Video_IICOp)
        LDMEQFD   sp!, {v1-v5,pc}
        ; Else bad stuff - unhandled interrupt
486 487 488
 [ I2CDebugError
        DebugReg  v3, "Unhandled IRQ - "
 ]
489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513
unexpected_error
        MOV       a1, #EERROR
clear_and_return
 [ I2CDebug
        DebugReg  a1, "clear_and_return: "
 ]
        STRH      v3, [v4, #I2C_STAT]
        MOV       v3, #0
        STR       v3, [v5, #I2C_XStart]
        LDMFD     sp!, {v1-v5,pc}

i2c_nack
        ; No ack was received - give up and return error
        MOV       a1, #ENOACK
        B         clear_and_return

i2c_al
        ; Arbitration lost - restart the transfer list
        STRH      v3, [v4, #I2C_STAT]
        LDR       v1, [v5, #I2C_XStart]
        B         start_transfer

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

553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570

i2c_xdr
        ; Transfer I2C_BUFSTAT[5:0] bytes
        LDRH      a4, [v4, #I2C_BUFSTAT]
        AND       a4, a4, #&3F
        B         send_bytes

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

i2c_rdr
        ; Read I2C_BUFSTAT[13:8] bytes
        LDRH      a4, [v4, #I2C_BUFSTAT]
        MOV       a4, a4, LSR #8
619 620
        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
621 622 623 624 625 626 627 628 629 630 631 632 633
        B         read_bytes

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


; HAL_Video_IICOp
; Officially:
;  r0 = card<<28 + channel<<24 + I2Ccmnd << 16 + address
;  I2Ccmnd = 1 for read, 0 write
;  r1 = DMA address
;  r2 = byte count
;  Returns:
;  r0 = 0 or error block
;  r1 updated
;  r2 updated
; In reality:
;  r0 = as above. The NVidia driver describes 'address' as being 11-bit, but only seems to handle it as 8-bit. The address is the offset to read from in the EDID data (Only EDID is supported, at I2C address 0x50. DDC, which is apparently at 0x37, isn't supported)
;  r1 = logical address
;  r2 = byte count
;  Returns:
;  r0 = 0 on success, unmodified on error (which also be 0 for a write to 0 on card 0 channel 0?!)
;  r1 = r1+r2 on success, unmodified on error
;  r2 = 0 on success, unmodified on error
;  There appears to be no support for partial transfers; NVidia driver implementation simply writes corrupt data for any failed bytes.
HAL_Video_IICOp
710
        ; Build a set of iic_transfer blocks and call RISCOS_IICOpV
711 712
        ; First let's discard writes, to be equivalent to the NVidia driver
        TST       a1, #1<<16
713 714 715
        ; Also make sure we've got a valid IIC bus to use
        LDRB      a4, [sb, #BoardConfig_VideoI2C]
        CMPNE     a4, #255
716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735
        MOVEQ     pc, lr
        ; Also check we're transferring a valid number of bytes
        SUB       a4, a3, #1
        CMP       a4, #65536
        MOVHS     pc, lr
        Push      "a1-a3,lr"
        ; Now we construct two iic_transfer blocks
        ; - First block is a write to 0x50. Single byte containing EDID start address (lower 8 bits of r0)
        ; - Second block is a read from 0x50. r2 bytes written to r1.
        ; Block 2:
        TST       a1, #1<<16 ; Is this read or write? (although this code isn't guaranteed to work properly for EDID writes!)
        MOVNE     a1, #&a1 ; Read from I2C
        MOVEQ     a1, #&a0 ; Write to I2C
        Push      "a1-a3" ; Push the block on the stack (a2 & a3 are already correct)
        ; Block 1:
        MOV       a1, #&a0
        ADD       a2, sp, #12 ; sp+12 should point to the 8 bit EDID address
        MOV       a3, #1
        Push      "a1-a3"
        ; Now attempt to start the transfer
736 737 738 739 740 741 742 743 744 745 746 747 748 749 750
        LDRB      a2, [sb, #BoardConfig_VideoI2C]
        MOV       a2, a2, LSL #24
        ADD       a2, a2, #2
        MOV       a1, sp
        ; If HAL_Init isn't done yet, we can't use OS_IICOpV
        LDR       a3, HALInitialised
        CMP       a3, #0
        ADREQ     a3, IIC_DoOp_Poll
        LDRNE     a3, OSentries+4*OS_IICOpV
        BLX       a3
        ; On error, return with unmodified regs, ala NVidia driver
        CMP       a1, #0
        ADD       sp, sp, #24
        Pull      "a1-a3,lr"
        ADDEQ     a2, a2, a3
751 752
        MOVEQ     a1, #0
        MOVEQ     a3, #0
753
        MOV       pc, lr
754

Jeffrey Lee's avatar
Jeffrey Lee committed
755 756 757 758 759
IIC_DoOp_Poll
        ; IIC transfer function that performs a polling transfer, similar to HAL_Video_IICOp
        ; 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
760 761
        ; r1 = bits 0-23: iic_transfer count
        ;      bits 24-31: bus number
Jeffrey Lee's avatar
Jeffrey Lee committed
762
        ; Returns E* return code in R0 (0 success, anything else failure)
763
        Push      "v1,lr"
764 765 766 767 768 769
 [ {FALSE}
        ; If IRQs and IIC IRQ are enabled, panic
        Push      "a1-a4"
        MRS       a1, CPSR
        TST       a1, #I32_bit
        BNE       %FT10
770 771
        ADR       a1, BoardConfig_HALI2CIRQ
        LDRB      a1, [a1, a2, LSR #24]
772 773 774 775 776 777 778 779 780
        IMPORT HAL_IRQDisable
        BL        HAL_IRQDisable
        CMP       a1, #0
        BEQ       %FT10
        DebugTX   "Warning - IIC_DoOp_Poll called with IIC IRQ enabled!"
        B         .
10
        Pull      "a1-a4"
 ]
Jeffrey Lee's avatar
Jeffrey Lee committed
781
        MOV       a3, a1
782 783 784
        MOV       a1, a2, LSR #24
        BIC       a2, a2, #&ff000000
        MOV       v1, a1
Jeffrey Lee's avatar
Jeffrey Lee committed
785 786 787 788
        BL        HAL_IICTransfer
        ; Now just poll until we're done
10
        CMP       a1, #EINPROGRESS ; Done?
789
        Pull      "v1,pc", NE
Jeffrey Lee's avatar
Jeffrey Lee committed
790
        ADR       lr, %BT10
791
        MOV       a1, v1
Jeffrey Lee's avatar
Jeffrey Lee committed
792
        B         HAL_IICMonitorTransfer
793 794

        END