; Copyright 2001 Pace Micro Technology plc ; ; 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. ; ; 19-Feb-01 KJB Separated IIC operations from NVMemory and RTC code PollMax * 150 ; Number of times to poll for an Ack (increase if you clock faster - need to ; allow 5ms for write cycle). ; Choose a lower limit on the number of ticks per clock phase based on the ; MaxI2Cspeed variable defined in Hdr:Machine.<Machine> [ MaxI2Cspeed >= 1000 I2Cticks * 1 | [ MaxI2Cspeed >= 400 I2Cticks * 3 | I2Cticks * 10 ] ] IICOpSWI Push LR BL IIC_OpV Pull LR B SLVK_TestV ; ***************************************************************************** ; in: R0 = device address (bit 0 set => read, clear => write) ; R1 -> data block ; R2 = length of data block ; IIC_Op Push "R0-R2,LR" Push "R0-R2" ; soft copy for IIC_OpV to work on MOV R0, R13 MOV R1, #1 BL IIC_OpV ADD R13, R13, #12 ; junk soft copy Pull "R0-R2,PC",VC ADD R13, R13, #4 Pull "R1-R2,PC" ; ***************************************************************************** ; in: R0 -> array of transfer descriptors ; R1 = number of transfers ; ; out: transfer descriptors may be updated (beware) ; ; transfer descriptor is 3 words: word 0 = device address (+direction) ; (bit 29 signifies retry for response) ; (bit 30 signifies checksum read only - ie fill in word 1 with ; sum of bytes read) ; (bit 31 signifies continued transfer - ie no start or address) ; word 1 -> data block ; word 2 = length of data block IIC_OpV ROUT Push "R0-R7,R10,R11,LR" MOV R7, #0 MRS R6, CPSR BIC R5, R6, #I32_bit MSR CPSR_c, R5 ; enable interrupts - this may take some time MOV R4, R0 MOV R5, R1 05 SUBS R5, R5, #1 BCC %FT90 LDMIA R4!, {R0-R2} TST R0, #1:SHL:31 ; skip start? BNE %FT08 TEQ R7,#0 BLEQ Start TEQ R7,#0 BLNE RepeatedStart TST R0, #1:SHL:29 BNE %FT06 BL TXAck ; transmit device address without retries B %FT07 06 BL TXPollAck ; transmit device address with retries 07 BVS %FT80 08 MOV R7, #1 MOV R11, R1 MOVS R10, R2 BEQ %BT05 TST R0, #1 ; Z => write, NZ => read BNE %FT20 ; Write case 10 LDRB R0, [R11], #1 ; read byte from data block BL TXAck ; transmit, checking for ack BVS %FT80 SUBS R10, R10, #1 ; decrement byte count BNE %BT10 ; loop until finished B %BT05 ; then next transfer 20 TST R0, #1:SHL:30 ; checksum? BNE %FT30 ; Read case 21 BL RXByte ; read byte from bus STRB R0, [R11], #1 ; store in data block SUBS R10, R10, #1 ; is it last byte ? MOVNE R0, #0 ; no, then acknowledge with 0 bit MOVEQ R0, #1 ; yes, then don't acknowledge BL ClockData ; but always send ack clock pulse BNE %BT21 B %BT05 ; next transfer ; Checksum case 30 MOV R11, #0 31 BL RXByte ; read byte from bus ADD R11, R11, R0 SUBS R10, R10, #1 MOVNE R0, #0 ; no, then acknowledge with 0 bit MOVEQ R0, #1 ; yes, then don't acknowledge BL ClockData ; but always send ack clock pulse BNE %BT31 STR R11, [R4, #-8] ; store checksum B %BT05 ; next transfer 90 BL Stop CLRV MSR CPSR_c, R6 ; restore interrupts Pull "R0-R7,R10,R11,PC" 80 BL Stop ADR R0, ErrorBlock_IIC_NoAcknowledge [ International BL TranslateError | SETV ] ADD R13, R13, #4 Pull "R1-R7,R10,R11,PC" MakeErrorBlock IIC_NoAcknowledge ; ***************************************************************************** ; ; SetC1C0 - Set clock and data lines to values in R1 and R0 respectively ; ; out: ATPCS ; SetC1C0 ROUT Push "R9,R14" [ HAL AddressHAL MOV R2, R1 MOV R1, R0 MOV R0, #0 CallHAL HAL_IICSetLines | ADD R0, R0, R1, LSL #1 ; R0 := C0 + C1*2 MOV R2, #0 ; prepare to index soft copy LDRB R1, [R2, #IOCControlSoftCopy] ; read soft copy BIC R1, R1, #&03 ; clear clock and data ORR R0, R1, R0 ; put in new clock and data ORR R0, R0, #&C0 ; make sure two test bits are ; always set to 1 ! STRB R0, [R2, #IOCControlSoftCopy] ; store back to soft copy MOV R2, #IOC STRB R0, [R2, #IOCControl] ] [ E2ROMSupport MOV R0, #0 LDRB R0, [R0, #NVRamSpeed] TEQ R0, #0 MOVEQ R0, #10 ; default value if speed not checked yet | MOV R0, #10 ; default to slowest value if we have E2ROMSupport is false ] BL DoMicroDelay Pull "R9,PC" ; ***************************************************************************** ; ; ReadC1C0 - Read clock and data lines to R1 and R0 respectively ; ; out: R0, R1 updated - otherwise ATPCS ; ReadC1C0 ROUT [ HAL Push "sb,lr" AddressHAL CallHAL HAL_IICReadLines Pull "sb,pc" | MOV a3, #IOC LDRB a1, [a3, #IOCControl] MOV a2, a1, LSR #1 AND a1, a1, #1 AND a2, a2, #1 MOV pc, lr ] ; ***************************************************************************** ; ; DoMicroDelay - Delay for >= R0/2 microseconds ; ; in: R0 = time delay in 1/2 microsecond units ; R2 -> IOC ; On ARM600, we may or may not be in a 32-bit mode ; ; out: R0,R1 corrupted ; DoMicroDelay ROUT [ HAL Push "a3,a4,sb,ip,lr" AddressHAL MOVS a1, a1, LSR #1 ADC a1, a1, #0 CallHAL HAL_CounterDelay Pull "a3,a4,sb,ip,pc" | Push R14 STRB R0, [R2, #Timer0LR] ; copy counter into output latch LDRB R1, [R2, #Timer0CL] ; R1 := low output latch 10 STRB R0, [R2, #Timer0LR] ; copy counter into output latch LDRB R14, [R2, #Timer0CL] ; R14 := low output latch TEQ R14, R1 ; unchanged ? BEQ %BT10 ; then loop MOV R1, R14 ; copy anyway SUBS R0, R0, #1 ; decrement count BNE %BT10 ; loop if not finished Pull PC ] LTORG ; ***************************************************************************** ; ; ClockData - Clock a bit of data down the IIC bus ; ; in: R0 = data bit ; ; out: All registers preserved, including PSR ; ClockData ROUT [ No26bitCode Push "R0-R3,R4,R5,R12,R14" MRS R4,CPSR | Push "R0-R3,R5,R12,R14" ] MOV R5, R0 MOV R1, #0 ; Clock lo BL SetC1C0 [ No26bitCode ORR R1,R4,#I32_bit MSR CPSR_c,R1 | ; Disable interrupts to ensure clock hi with data hi is only transient ; This allows BMU to detect idle condition by polling MOV R1,PC ORR R1,R1,#I_bit TEQP PC,R1 ] MOV R0, R5 MOV R1, #1 ; Clock hi BL SetC1C0 ; Delay here must be >= 4.0 microsecs MOV R0, R5 MOV R1, #0 ; Clock lo BL SetC1C0 [ No26bitCode MSR CPSR_cf,R4 ; Restore interrupts Pull "R0-R3,R4,R5,R12,PC" | Pull "R0-R3,R5,R12,PC",,^ ] ClockData0 ROUT ; Clock a zero bit Push "R0, R14" MOV R0, #0 BL ClockData ; preserves PSR Pull "R0, PC" ; ***************************************************************************** ; ; Start - Send the Start signal ; ; out: All registers preserved, including PSR ; Start ROUT [ No26bitCode Push "R0-R3,R4,R12,R14" MRS R4, CPSR | Push "R0-R3,R12,R14" ] MOV R0, #1 ; clock HI, data HI MOV R1, #1 BL SetC1C0 ; Delay here must be >= 4.7 microsecs (1.3 for fast device) MOV R0, #0 ; clock HI, data LO MOV R1, #1 BL SetC1C0 ; Delay here must be >= 4.0 microsecs (0.6 for fast device) MOV R0, #0 ; clock LO, data LO MOV R1, #0 BL SetC1C0 [ No26bitCode MSR CPSR_f, R4 Pull "R0-R3,R4,R12,PC" | Pull "R0-R3,R12,PC",,^ ] ; ***************************************************************************** ; ; RepeatedStart - Send a Repeated Start signal ; ; out: All registers preserved, including PSR ; RepeatedStart ROUT [ No26bitCode Push "R0-R3,R4,R12,R14" MRS R4, CPSR | Push "R0-R3,R12,R14" ] MOV R0, #1 MOV R1, #0 ; clock LO, data HI BL SetC1C0 MOV R0, #1 ; clock HI, data HI MOV R1, #1 BL SetC1C0 ; Delay here must be >= 4.7 microsecs (1.3 for fast device) MOV R0, #0 ; clock HI, data LO MOV R1, #1 BL SetC1C0 ; Delay here must be >= 4.0 microsecs (0.6 for fast device) MOV R0, #0 ; clock LO, data LO MOV R1, #0 BL SetC1C0 [ No26bitCode MSR CPSR_f, R4 Pull "R0-R3,R4,R12,PC" | Pull "R0-R3,R12,PC",,^ ] ; ***************************************************************************** ; ; Acknowledge - Check acknowledge after transmitting a byte ; ; out: All registers preserved ; V=0 => acknowledge received ; V=1 => no acknowledge received ; Acknowledge ROUT Push "R0-R4,R12,R14" MOV R0, #1 ; clock LO, data HI MOV R1, #0 BL SetC1C0 [ {TRUE} ; Disable interrupts to ensure clock hi with data hi is only transient ; This allows BMU to detect idle condition by polling MRS R1,CPSR Push "R1" ORR R1,R1,#I32_bit MSR CPSR_c,R1 ] MOV R0, #1 ; clock HI, data HI MOV R1, #1 BL SetC1C0 ; Delay here must be >= 4.0 microsecs (0.6 for fast device) BL ReadC1C0 MOV R4, R0 ; should be LO for correct acknowledge MOV R0, #1 MOV R1, #0 ; clock LO, data HI BL SetC1C0 [ {TRUE} Pull "R1" MSR CPSR_c,R1 ] TST R4, #1 MRS R2, CPSR BICEQ R2, R2, #V_bit ; clear V if correct acknowledge ORRNE R2, R2, #V_bit ; set V if no acknowledge MSR CPSR_f, R2 Pull "R0-R4,R12,PC" ; ***************************************************************************** ; ; Stop - Send the Stop signal ; ; out: All registers preserved, including PSR ; Stop ROUT [ No26bitCode Push "R0-R3,R4,R12,R14" MRS R4, CPSR | Push "R0-R3,R12,R14" ] MOV R0, #0 ; clock LO, data LO MOV R1, #0 BL SetC1C0 MOV R0, #0 ; clock HI, data LO MOV R1, #1 BL SetC1C0 ; Delay here must be >= 4.0 microsecs (0.6 for fast device) MOV R0, #1 ; clock HI, data HI MOV R1, #1 BL SetC1C0 [ No26bitCode MSR CPSR_f, R2 Pull "R0-R3,R4,R12,PC" | Pull "R0-R3,R12,PC",,^ ] ; ***************************************************************************** ; ; TXByte - Transmit a byte ; ; in: R0 = byte to be transmitted ; ; out: All registers preserved, including PSR ; TXByte ROUT [ No26bitCode Push "R0-R3,R14" MRS R3, CPSR | Push "R0-R2,R14" ] MOV R1, #&80 ; 2^7 the bit mask MOV R2, R0 ; byte goes into R2 10 ANDS R0, R2, R1 ; zero if bit is zero MOVNE R0, #1 BL ClockData ; send the bit MOVS R1, R1, LSR #1 BNE %BT10 [ No26bitCode MSR CPSR_f, R3 Pull "R0-R3,PC" | Pull "R0-R2,PC",,^ ] TXAck ROUT Push R14 BL TXByte Pull R14 B Acknowledge ; ***************************************************************************** ; ; TXPollAck - Transmit a byte and poll for acknowledge ; ; This is intended for devices with a slow internal write cycle which ; don't ack until the write cycle is finished ( eg ATMEL AT24C01A/x ) ; ; in: R0 = byte to be transmitted ; ; out: All registers preserved ; TXPollAck ROUT Push "R1,R14" MOV R1, #1 10 BL TXByte BL Acknowledge Pull "R1,PC",VC ADD R1, R1, #1 TEQ R1, #PollMax BEQ %FT90 [ {FALSE} BREG R1, "i2c tries:" ] BL RepeatedStart B %BT10 90 Pull "R1,PC" ; ***************************************************************************** ; ; RXByte - Receive a byte ; ; out: R0 = byte received ; All other registers preserved, including PSR ; RXByte ROUT [ No26bitCode Push "R1-R6,R12,R14" MRS R5, CPSR | Push "R1-R4, R6, R12, R14" ] MOV R6, #0 ; byte:=0 MOV R4, #7 MOV R0, #1 ; clock LO, data HI MOV R1, #0 BL SetC1C0 10 [ {TRUE} ; Disable interrupts to ensure clock hi with data hi is only transient ; This allows BMU to detect idle condition by polling MRS R1,CPSR Push "R1" ORR R1,R1,#I32_bit MSR CPSR_c,R1 ] MOV R0, #1 ; pulse clock HI MOV R1, #1 BL SetC1C0 BL ReadC1C0 ADD R6, R0, R6, LSL #1 ; byte:=byte*2+ SDA MOV R0, #1 ; return clock LO MOV R1, #0 BL SetC1C0 [ {TRUE} Pull "R1" MSR CPSR_c,R1 ] SUBS R4, R4, #1 BCS %BT10 MOV R0, R6 ; return the result in R0 [ No26bitCode MSR CPSR_f, R5 Pull "R1-R6, R12, PC" | Pull "R1-R4, R6, R12, PC",,^ ] END