1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
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
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
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
481
482
483
484
485
486
487
488
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
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
; 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
; 05-Feb-02 BJGA Added re-entrant capability
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
]
]
IICStackAlignment * 7 ; log2 of stack size, also stack alignment
; current requirement is 19 words = 2_01001100 bytes
^ 0
IICLink_Next # 4
IICLink_Error # 4
IICLink_Array # 4
IICLink_Size # 4
; SVC stack format, in descending address order:
; 16 bytes first link (also bottom of stacked registers)
; n bytes align to address with bottom x bits set
; 2^x-4 bytes align to address with bottom x bits clear (the local stack)
; 4 bytes linked list head
; 4 bytes linked list tail
; 4 bytes original sp
; .
; .
; .
; 16 bytes another link (also bottom of stacked registers)
; 4 bytes original sp
; IRQ stack format, in descending address order, for reference:
; 4 bytes lr_irq-4 (interrupted PC)
; 4 bytes r0
; 4 bytes spsr_irq (interrupted CPSR)
; 20 bytes r1-r3, r11, r12
; 4 bytes IRQsema link
iicsp RN 11
iiclr RN 12
MACRO
$label iicBL $destination, $cond
$label MOV$cond iiclr, pc
B$cond $destination
MEND
MACRO
$label iicPull $reglist, $cond, $hat
LCLS temps
LCLL onereg
temps SETS "$reglist"
onereg SETL "$hat" = ""
WHILE onereg :LAND: :LEN: temps > 0
[ temps :LEFT: 1 = "," :LOR: temps :LEFT: 1 = "-"
onereg SETL {FALSE}
]
temps SETS temps :RIGHT: (:LEN: temps - 1)
WEND
[ onereg
$label LDR$cond $reglist, [iicsp], #4
|
$label LDM$cond.FD iicsp!, {$reglist}$hat
]
MEND
MACRO
$label iicPush $reglist, $cond
LCLS temps
LCLL onereg
temps SETS "$reglist"
onereg SETL {TRUE}
WHILE onereg :LAND: :LEN: temps > 0
[ temps :LEFT: 1 = "," :LOR: temps :LEFT: 1 = "-"
onereg SETL {FALSE}
]
temps SETS temps :RIGHT: (:LEN: temps - 1)
WEND
[ onereg
$label STR$cond $reglist, [iicsp, #-4]!
|
$label STM$cond.FD iicsp!, {$reglist}
]
MEND
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"
; *****************************************************************************
;
; IIC_OpV - perform IIC operations based on a list of descriptors
;
; in: R0 -> array of transfer descriptors
; R1 = bits 0-23: number of transfers
; bits 24-31: bus number
;
; 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-r3,r6-r12,lr"
MOV lr, #0
STR lr, [sp, #-8]!
MOV r3, r1, LSR #24
MRS r10, CPSR
BIC r7, r10, #I32_bit :OR: F32_bit
ORR r8, r7, #I32_bit
[ HAL
AddressHAL
|
MOV r9, #IOC
]
LDR r2, =ZeroPage+IRQsema
MOV r12, sp ; original sp, also pointer to link
ORR lr, r8, #2_10000
MSR CPSR_c, lr ; IRQs off, force 32-bit mode
01 LDR r2, [r2]
TEQ r2, #0
BEQ %FT50 ; IC code not in IRQ stack
LDR r6, [r2, #4*8] ; interrupted PC
IIC_OpV_PCReference
RSB lr, pc, r6
LDR r0, =(interrupt_protected_end-4) - (IIC_OpV_PCReference+8)
CMP lr, r0
RSBLES lr, lr, #interrupt_protected_start - (IIC_OpV_PCReference+8)
BGT %BT01
; IC code is already threaded
LDR r0, [r2, #4*4] ; retrieve interrupted iicsp
BIC r0, r0, #(1:SHL:IICStackAlignment)-1
LDR r1, [r0, #-8] ; old list tail
LDRB r11, [r1, #IICLink_Size+3] ; get bus number
CMP r11, r3
BNE %BT01 ; wrong bus, don't add ourselves to this list
Push "r12" ; put original sp on stack for our exit routine
STR r12, [r0, #-8] ; new list tail
STR r12, [r1, #IICLink_Next] ; point old link to new link
ADR r0, IIC_OpV_CommonExit
STR r0, [r2, #4*8] ; poke IRQ stack so previous operation returns as though completed
LDR r0, [r2, #4*6] ; get interrupted CPSR
MSR SPSR_cxsf, r0 ; stick it in SPSR (okay, because IRQs are off)
LDR r0, [r2, #4*7]
LDMIB r2, {r1-r3,r11,r12}
MOVS pc, r6 ; copy SPSR to CPSR and resume execution
50 ; IC code not currently threaded - create new environment
ADD iicsp, sp, #4
BIC iicsp, iicsp, #(1:SHL:IICStackAlignment)-1
SUB iicsp, iicsp, #4
BIC sp, iicsp, #(1:SHL:IICStackAlignment)-1
Push "r12" ; list head pointer
Push "r12" ; list tail pointer
Push "r12" ; original sp
LDR r0, [r12, #IICLink_Array]
LDR r1, [r12, #IICLink_Size]
B IICStart ; start working through list
IIC_OpV_CommonExit
MSR CPSR_c, r10 ; restore original IRQ disable state
LDR sp, [sp]
ADD sp, sp, #4 ; skip next pointer
Pull "r0"
CMP r0, #0
Pull "r0-r3,r6-r12,pc", EQ
SETV
ADD sp, sp, #4
Pull "r1-r3,r6-r12,pc"
interrupt_protected_start
; Protected routines register usage:
; r0-r3 general purpose
; r7 MRS style PSR with c bits = SVC26/32, IRQs/FIQs enabled
; r8 MRS style PSR with c bits = SVC26/32, IRQs disabled, FIQs enabled
; r9 IOC, or base of HAL workspace, depending on HAL switch
; r11 stack pointer
; r12 link register / general purpose
; CPSR is also non-volatile
IICStart
MSR CPSR_c, r7 ; enable IRQs (inside protected code) - this may take some time
; drop through...
; *****************************************************************************
;
; IICLoop - serial-execution outermost loop, stepping along pending IIC operations
;
IICLoop
iicBL IICDoOp
MOVVC r0, #0
BIC r1, iicsp, #(1:SHL:IICStackAlignment)-1
LDR r2, [r1, #-4] ; list head
STR r0, [r2, #IICLink_Error] ; set up return value
MSR CPSR_c, r8 ; disable IRQs while we work on the list
LDR r2, [r2]
TEQ r2, #0 ; end of list?
BEQ IIC_OpV_CommonExit ; finished!
STR r2, [r1, #-4] ; update list head
MSR CPSR_c, r7 ; IRQs back on
LDR r0, [r2, #IICLink_Array]
LDR r1, [r2, #IICLink_Size] ; get next array
B IICLoop ; and loop
; *****************************************************************************
;
; IICDoOp - main serial-execution entry point
;
; in: R0 -> array of transfer descriptors
; R1 = bits 0-23: number of transfers
; bits 24-31: bus number
;
; out: if V set, r0 -> error block
; otherwise r0-r3,r12 may be corrupted
;
IICDoOp ROUT
MOV R1, R1, ROR #24 ; Move xfer count to low byte
MOV R2, #0
iicPush "R1,R2,iiclr" ; two words on stack are RepeatedStart flag and transfers remaining
; Get correct IICBus ptr in R2
AND R3, R1, #255
MOV iiclr, #IICBus_Size
LDR R2, =ZeroPage+IICBus_Base
MLA R2, R3, iiclr, R2
MOV R3, R0
LDR iiclr, [R2, #IICBus_Type]
TST iiclr, #IICFlag_HighLevel
BNE IIC_OpV_HAL ; HAL can make use of a hardware IIC engine
05 LDR R0, [iicsp]
SUBS R0, R0, #256
BLT %FT90
STR R0, [iicsp]
LDMIA R3!, {R0-R2}
TST R0, #1:SHL:31 ; skip start?
BNE %FT08
LDR iiclr, [iicsp, #4]
TEQ iiclr, #0
MOV iiclr, pc
ADD iiclr, iiclr, #8
BEQ Start
BNE RepeatedStart ; these are effectively conditional BL's
TST R0, #1:SHL:29
BNE %FT06
iicBL TXAck ; transmit device address without retries
B %FT07
06
iicBL TXPollAck ; transmit device address with retries
07 BVS %FT80
08 MOV iiclr, #1
STR iiclr, [iicsp, #4]
TEQ R2, #0
BEQ %BT05
TST R0, #1 ; Z => write, NZ => read
BNE %FT20
; Write case
10 LDRB R0, [R1], #1 ; read byte from data block
iicBL TXAck ; transmit, checking for ack
BVS %FT80
SUBS R2, R2, #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
iicBL RXByte ; read byte from bus
STRB R0, [R1], #1 ; store in data block
MOV R0, #1 ; start with the assumption that it's the last byte, and so shouldn't be acknowledged
SUBS R2, R2, #1 ; is it last byte in this descriptor?
MOVNES R0, R0, LSR #2 ; no, so definitely needs acknowledging (with 0 bit)
; now Z is set, and C set => just read last byte for this descriptor
LDRCS iiclr, [iicsp]
MOVCS iiclr, iiclr, LSR #8
TEQCS iiclr, #0 ; if we've finished this descriptor, check for another transfer descriptor
; Z clear => last byte, and there is another descriptor
LDRNE iiclr, [R3]
TSTNE iiclr, #1:SHL:31 ; if appropriate, check if next descriptor is a continuation
MOVNE R0, #0 ; if read is going to continue, we need to acknowledge
iicBL ClockData ; but always send ack clock pulse
BCC %BT21
B %BT05 ; next transfer
; Checksum case
30 MOV R1, #0
31
iicBL RXByte ; read byte from bus
ADD R1, R1, R0
MOV R0, #1 ; start with the assumption that it's the last byte, and so shouldn't be acknowledged
SUBS R2, R2, #1 ; is it last byte in this descriptor?
MOVNES R0, R0, LSR #2 ; no, so definitely needs acknowledging (with 0 bit)
; now Z is set, and C set => just read last byte for this descriptor
LDRCS iiclr, [iicsp]
MOVCS iiclr, iiclr, LSR #8
TEQCS iiclr, #0 ; if we've finished this descriptor, check for another transfer descriptor
; Z clear => last byte, and there is another descriptor
LDRNE iiclr, [R3]
TSTNE iiclr, #1:SHL:31 ; if appropriate, check if next descriptor is a continuation
MOVNE R0, #0 ; if read is going to continue, we need to acknowledge
iicBL ClockData ; but always send ack clock pulse
BCC %BT31
STR R1, [R3, #-8] ; store checksum
B %BT05 ; next transfer
90
iicBL Stop
IIC_ExitOK
CLRV
ADD iicsp, iicsp, #8 ; skip junk on stack
iicPull "pc"
80
iicBL Stop
LDRB R0, [iicsp]
MOV R1, #IICBus_Size
LDR R2, =ZeroPage+IICBus_Base
MLA R2, R0, R1, R2
IIC_ExitNoAck
MOV R0, #0
STR R0, [R2, #IICBus_Status]
ADR R0, ErrorBlock_IIC_NoAcknowledge
IIC_ExitError
[ International :LAND: {FALSE}
; KJB - problematical - this may be done very early, before SWI dispatch
; is ready, so we can't call MessageTrans. Think about this.
; BJGA - we also can't use TranslateError, because it doesn't conform to
; our calling standard (unless we turn interrupts off)
BL TranslateError
ADD iicsp, iicsp, #8 ; skip junk on stack
iicPull "pc"
MakeInternatErrorBlock IIC_NoAcknowledge,, "NoAck:No acknowledge from IIC device"
|
SETV
ADD iicsp, iicsp, #8 ; skip junk on stack
iicPull "pc"
MakeErrorBlock IIC_NoAcknowledge
]
; *****************************************************************************
;
; SetC1C0 - Set clock and data lines to values in R1 and R0 respectively
;
; out: r0,r1 corrupted
;
SetC1C0 ROUT
iicPush "r2,r3,iiclr"
[ HAL
MOV R2, R1
MOV R1, R0
BIC r0, iicsp, #(1:SHL:IICStackAlignment)-1
LDR r0, [r0, #-4] ; list head
LDRB r0, [r0, #IICLink_Size+3] ; bus number
MSR CPSR_c, r8 ; IRQs off for use of ATPCS
Push "lr"
CallHAL HAL_IICSetLines
Pull "lr"
MSR CPSR_c, r7 ; IRQs back on
|
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
LDR R0, =ZeroPage
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
]
iicBL iicDoMicroDelay
iicPull "r2,r3,pc"
; *****************************************************************************
;
; ReadC1C0 - Read clock and data lines to R1 and R0 respectively
;
; out: R0, R1 updated
;
ReadC1C0 ROUT
[ HAL
iicPush "r2,r3,iiclr"
BIC r0, iicsp, #(1:SHL:IICStackAlignment)-1
LDR r0, [r0, #-4] ; list head
LDRB r0, [r0, #IICLink_Size+3] ; bus number
MSR CPSR_c, r8 ; IRQs off for use of ATPCS
Push "lr"
CallHAL HAL_IICReadLines
Pull "lr"
MSR CPSR_c, r7 ; IRQs back on
iicPull "r2,r3,pc"
|
LDRB a1, [r9, #IOCControl]
MOV a2, a1, LSR #1
AND a1, a1, #1
AND a2, a2, #1
MOV pc, iiclr
]
; *****************************************************************************
;
; iicDoMicroDelay - Delay for >= R0/2 microseconds, IIC calling standard
;
; in: R0 = time delay in 1/2 microsecond units
; On ARM600, we may or may not be in a 32-bit mode
;
; out: R0,R1 corrupted
;
iicDoMicroDelay ROUT
[ HAL
iicPush "a3,a4,iiclr"
MOVS a1, a1, LSR #1
ADC a1, a1, #0
MSR CPSR_c, r8 ; IRQs off for use of ATPCS
Push "lr"
CallHAL HAL_CounterDelay
Pull "lr"
MSR CPSR_c, r7 ; IRQs back on
iicPull "a3,a4,pc"
|
iicPush "iiclr"
STRB R0, [R9, #Timer0LR] ; copy counter into output latch
LDRB R1, [R9, #Timer0CL] ; R1 := low output latch
10
STRB R0, [R9, #Timer0LR] ; copy counter into output latch
LDRB iiclr, [R9, #Timer0CL] ; iiclr := low output latch
TEQ iiclr, R1 ; unchanged ?
BEQ %BT10 ; then loop
MOV R1, iiclr ; copy anyway
SUBS R0, R0, #1 ; decrement count
BNE %BT10 ; loop if not finished
iicPull "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
iicPush "R0-R3,iiclr"
MRS R2,CPSR
|
iicPush "R0-R1,R3,iiclr"
]
MOV R3, R0
MOV R1, #0 ; Clock lo
iicBL SetC1C0
; Disable interrupts to ensure clock hi with data hi is only transient
; This allows BMU to detect idle condition by polling
MSR CPSR_c, r8
MOV R0, R3
MOV R1, #1 ; Clock hi
iicBL SetC1C0
; Delay here must be >= 4.0 microsecs
MOV R0, R3
MOV R1, #0 ; Clock lo
iicBL SetC1C0
[ No26bitCode
MSR CPSR_cf,R2 ; Restore interrupts and flags
iicPull "R0-R3,PC"
|
iicPull "R0-R1,R3,PC",,^
]
; *****************************************************************************
;
; Start - Send the Start signal
;
; out: All registers preserved, PSR corrupted
;
Start ROUT
iicPush "R0-R1,iiclr"
MOV R0, #1 ; clock HI, data HI
MOV R1, #1
iicBL SetC1C0
; Delay here must be >= 4.7 microsecs (1.3 for fast device)
MOV R0, #0 ; clock HI, data LO
MOV R1, #1
iicBL SetC1C0
; Delay here must be >= 4.0 microsecs (0.6 for fast device)
MOV R0, #0 ; clock LO, data LO
MOV R1, #0
iicBL SetC1C0
iicPull "R0-R1,PC"
; *****************************************************************************
;
; RepeatedStart - Send a Repeated Start signal
;
; out: All registers preserved, PSR corrupted
;
RepeatedStart ROUT
iicPush "R0-R1,iiclr"
MOV R0, #1
MOV R1, #0 ; clock LO, data HI
iicBL SetC1C0
MOV R0, #1 ; clock HI, data HI
MOV R1, #1
iicBL SetC1C0
; Delay here must be >= 4.7 microsecs (1.3 for fast device)
MOV R0, #0 ; clock HI, data LO
MOV R1, #1
iicBL SetC1C0
; Delay here must be >= 4.0 microsecs (0.6 for fast device)
MOV R0, #0 ; clock LO, data LO
MOV R1, #0
iicBL SetC1C0
iicPull "R0-R1,PC"
; *****************************************************************************
;
; Acknowledge - Check acknowledge after transmitting a byte
;
; out: All registers preserved
; V=0 => acknowledge received
; V=1 => no acknowledge received
;
Acknowledge ROUT
iicPush "R0-R2,iiclr"
MOV R0, #1 ; clock LO, data HI
MOV R1, #0
iicBL SetC1C0
[ {TRUE}
; Disable interrupts to ensure clock hi with data hi is only transient
; This allows BMU to detect idle condition by polling
MSR CPSR_c, R8
]
MOV R0, #1 ; clock HI, data HI
MOV R1, #1
iicBL SetC1C0
; Delay here must be >= 4.0 microsecs (0.6 for fast device)
iicBL ReadC1C0
MOV R2, R0 ; should be LO for correct acknowledge
MOV R0, #1
MOV R1, #0 ; clock LO, data HI
iicBL SetC1C0
[ {TRUE}
MSR CPSR_c, R7
]
TST R2, #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
iicPull "R0-R2,PC"
; *****************************************************************************
;
; Stop - Send the Stop signal
;
; out: All registers preserved, PSR corrupted
;
Stop ROUT
iicPush "R0-R1,iiclr"
MOV R0, #0 ; clock LO, data LO
MOV R1, #0
iicBL SetC1C0
MOV R0, #0 ; clock HI, data LO
MOV R1, #1
iicBL SetC1C0
; Delay here must be >= 4.0 microsecs (0.6 for fast device)
MOV R0, #1 ; clock HI, data HI
MOV R1, #1
iicBL SetC1C0
iicPull "R0-R1,PC"
; *****************************************************************************
;
; TXByte - Transmit a byte
;
; in: R0 = byte to be transmitted
;
; out: All registers preserved, PSR corrupted
;
TXByte ROUT
iicPush "R0-R2,iiclr"
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
iicBL ClockData ; send the bit
MOVS R1, R1, LSR #1
BNE %BT10
iicPull "R0-R2,PC"
TXAck ROUT
iicPush iiclr
iicBL TXByte
iicPull iiclr
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
iicPush "R1,iiclr"
MOV R1, #1
10
iicBL TXByte
iicBL Acknowledge
iicPull "R1,PC",VC
ADD R1, R1, #1
TEQ R1, #PollMax
BEQ %FT90
[ {FALSE}
BREG R1, "i2c tries:"
]
iicBL RepeatedStart
B %BT10
90
iicPull "R1,PC"
; *****************************************************************************
;
; RXByte - Receive a byte
;
; out: R0 = byte received
; All other registers preserved, PSR corrupted
;
RXByte ROUT
iicPush "R1-R3,iiclr"
MOV R3, #0 ; byte:=0
MOV R2, #7
MOV R0, #1 ; clock LO, data HI
MOV R1, #0
iicBL SetC1C0
10
[ {TRUE}
; Disable interrupts to ensure clock hi with data hi is only transient
; This allows BMU to detect idle condition by polling
MSR CPSR_c, R8
]
MOV R0, #1 ; pulse clock HI
MOV R1, #1
iicBL SetC1C0
iicBL ReadC1C0
ADD R3, R0, R3, LSL #1 ; byte:=byte*2+ SDA
MOV R0, #1 ; return clock LO
MOV R1, #0
iicBL SetC1C0
[ {TRUE}
MSR CPSR_c, R7
]
SUBS R2, R2, #1
BCS %BT10
MOV R0, R3 ; return the result in R0
iicPull "R1-R3,PC"
; *****************************************************************************
IIC_OpV_HAL
; R2 = IICBus ptr
; R3 -> array of transfer descriptors
; [iicsp, #0] = bits 0-7: bus number
; bits 8-31: number of transfers
; [iicsp, #4] unused on entry, used to hold number of retries remaining
; [iicsp, #8] = return address
LDR iiclr, [R2, #IICBus_Status]
TEQ iiclr, #0
BNE IIC_Busy
LDR iiclr, [R3]
TST iiclr, #1:SHL:29 ; retries reqd?
MOVNE iiclr, #PollMax
MOVEQ iiclr, #1 ; no,just try once
STR iiclr, [iicsp, #4]
IIC_OpV_HAL_Retry
MOV iiclr, #1
STR iiclr, [R2, #IICBus_Status]
iicPush "R2,R3"
LDR R1, [iicsp, #8]
MOV R2, R3
AND R0, R1, #255
MOV R1, R1, LSR #8
MSR CPSR_c, R8 ; IRQs off for use of ATPCS
Push "lr"
CallHAL HAL_IICTransfer
Pull "lr"
MSR CPSR_c, R7 ; IRQs back on
iicPull "R2,R3"
20 TEQ R0, #2
BEQ IIC_NoAck
TEQ R0, #3
BEQ IIC_Busy
TEQ R0, #0
STREQ R0, [R2, #IICBus_Status] ; mark IIC system as free
BEQ IIC_ExitOK
TEQ R0, #1
BNE IIC_Error
LDR R0, [R2, #IICBus_Status]
B %BT20
IIC_NoAck
LDR iiclr, [iicsp, #4]
SUBS iiclr, iiclr, #1
STRNE iiclr, [iicsp, #4]
BNE IIC_OpV_HAL_Retry ; worth another go?
B IIC_ExitNoAck
IIC_Busy
ADR R0,IICBusy_Error
B IIC_ExitError
IICBusy_Error
MakeErrorBlock IIC_Busy
IIC_Error
MOV R0, #0
STR R0, [R2, #IICBus_Status]
ADR R0,IICError_Error
B IIC_ExitError
IICError_Error
MakeErrorBlock IIC_Error
interrupt_protected_end
IICIRQ
; R12 = bus number
Push "R8-R9,R14"
LDR R14,=ZeroPage
AddressHAL R14
; Get IICBus ptr
MOV R0, #IICBus_Size
ADD R1, R14, #IICBus_Base
MLA R8, R12, R0, R1
MOV R0, R12
CallHAL HAL_IICMonitorTransfer
STR R0, [R8, #IICBus_Status]
LDR R0, [R8, #IICBus_Device]
CallHAL HAL_IRQClear
Pull "R8-R9,PC"
IICAbort
Push "R0-R3,R7,R8,R9,R11,R12,R14"
MOV R11,R13
SUB R13,R13,#(1:SHL:IICStackAlignment)+4
MRS R7,CPSR
ORR R8,R7,#I32_bit
[ HAL
AddressHAL
|
MOV R9,#IOC
]
CallHAL HAL_IICBuses
SUBS R0,R0,#1
BLT %FT20
LDR R1,=ZeroPage+IICBus_Base
MOV R12,#IICBus_Size
MLA R1,R12,R0,R1
LDR R12,[R1,#IICBus_Type]
MOV R0,R0,LSL #24
05
TST R12,#IICFlag_HighLevel
BNE %FT10
; Set up a fake transfer list for Start/Stop/TXAck to read the bus number from
Push "R0,R1"
BIC R0, iicsp, #(1:SHL:IICStackAlignment)-1
STR R13,[R0,#-4]
[ {FALSE}
MOV R1,#16 ; Two bytes in case RTC transmitting
35
iicBL Start ; Start/clock edge
iicBL Stop
SUBS R1,R1,#1
BNE %BT35
|
iicBL Start
MOV R0, #1
iicBL TXAck
iicBL Stop
]
Pull "R0,R1"
10
SUBS R0,R0,#1<<24
SUB R1,R1,#IICBus_Size
BGE %BT05
20
ADD R13,R13,#(1:SHL:IICStackAlignment)+4
Pull "R0-R3,R7,R8,R9,R11,R12,PC"
IICInit
Push "R7-R9,R14"
AddressHAL
MOV R7, #0
LDR R8, =ZeroPage+IICBus_Base
10
CallHAL HAL_IICBuses
CMP R7, R0
Pull "R7-R9,R14",HS
BHS IICAbort ; Ensure any CMOS operation aborted
MOV a1, R7
CallHAL HAL_IICType
STR a1, [R8, #IICBus_Type]
TST a1, #IICFlag_Background
BEQ %FT20
SUB sp, sp, #12
MOV a1, sp
MOV a2, R7
CallHAL HAL_IICDevice
LDR a1, [sp]
STR a1, [R8, #IICBus_Device]
CallHAL HAL_IRQEnable
ADD sp, sp, #12
20
ADD R7, R7, #1
ADD R8, R8, #IICBus_Size
B %BT10
; We need to retain a version of DoMicroDelay with standard calling conventions, because
; it is called from elsewhere in the kernel. But it can't live inside the protected
; region above in case it's interrupted by a routine that does an IIC operation.
; *****************************************************************************
;
; 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
]
END