-
Notifications
You must be signed in to change notification settings - Fork 2
/
sdmac.c
2677 lines (2452 loc) · 82.2 KB
/
sdmac.c
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
/*
* SDMAC Version 0.9 2024-10-17
* -----------------------------
* Utility to inspect and test an Amiga 3000's Super DMAC (SDMAC) and
* WD SCSI controller for correct configuration and operation.
*
* Copyright Chris Hooper. This program and source may be used and
* distributed freely, for any purpose which benefits the Amiga community.
* Commercial use of the binary, source, or algorithms requires prior
* written or email approval from Chris Hooper <[email protected]>.
* All redistributions must retain this Copyright notice.
*
* DISCLAIMER: THE SOFTWARE IS PROVIDED "AS-IS", WITHOUT ANY WARRANTY.
* THE AUTHOR ASSUMES NO LIABILITY FOR ANY DAMAGE ARISING OUT OF THE USE
* OR MISUSE OF THIS UTILITY OR INFORMATION REPORTED BY THIS UTILITY.
*/
const char *version = "\0$VER: SDMAC " VER " ("__DATE__") © Chris Hooper";
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <libraries/expansionbase.h>
#include <clib/expansion_protos.h>
#include <inline/exec.h>
#include <inline/expansion.h>
#include <proto/dos.h>
#include <exec/memory.h>
#include <exec/interrupts.h>
#include <exec/execbase.h>
#include <exec/lists.h>
#include <inline/timer.h>
#define ROM_BASE 0x00f80000 // Kickstart ROM base address
#define RAMSEY_CTRL 0x00de0003 // Ramsey control register
#define RAMSEY_VER 0x00de0043 // Ramsey version register
#define SDMAC_BASE 0x00DD0000 // Base address of SDMAC
#define SDMAC_DAWR 0x00DD0003 // Write DACK width register (write)
#define SDMAC_WTC 0x00DD0004 // R/W Word Transfer Count (SDMAC-02 only)
#define SDMAC_CNTR 0x00DD0008 // R/W Control Register (byte) from doc
#define SDMAC_CONTR 0x00DD000b // R/W Control Register (byte)
#define RAMSEY_ACR 0x00DD000C // R/W DMA Control Register (in Ramsey)
#define SDMAC_ST_DMA 0x00DD0013 // Strobe Start DMA (write 0 to start)
#define SDMAC_FLUSH 0x00DD0017 // Strobe Flush DMA FIFO
#define SDMAC_CLR_INT 0x00DD001b // Strobe Clear Interrupts
#define SDMAC_ISTR 0x00DD001f // Read Interrupt Status Register
#define SDMAC_REVISION 0x00DD0020 // Read Revision of ReSDMAC
#define SDMAC_SP_DMA 0x00DD003f // Strobe Stop DMA
#define SDMAC_SASR_L 0x00DD0040 // Write WDC SASR long (obsolete)
#define SDMAC_SASR_B 0x00DD0041 // Read WDC SASR byte (obsolete)
#define SDMAC_SCMD 0x00DD0043 // R/W WDC mapped register (byte)
#define SDMAC_SCMD_B 0x00DD0047 // R/W WDC SCMD (byte)
#define SDMAC_SASRW 0x00DD0048 // Write WDC SCSI register select (long)
#define SDMAC_SASR_B2 0x00DD0049 // R/W WDC SCSI auxiliary status (byte)
#define SDMAC_CI 0x00DD0050 // R/W Coproc. Interface (long)
#define SDMAC_CIDDR 0x00DD0054 // R/W Coproc. Interface Data Dir (long)
#define SDMAC_SSPBDAT 0x00DD0058 // R/W Sync. Serial Periph. Bus Data (long)
#define SDMAC_SSPBCTL 0x00DD005C // R/W Sync. Serial Periph. Bus Ctrl (long)
#define SDMAC_WTC_ALT (SDMAC_WTC + 0x100) // Shadow of SDMAC WTC
#define RAMSEY_ACR_ALT (RAMSEY_ACR + 0x100) // Shadow of Ramsey ACR
#define SDMAC_SSPBDAT_ALT (SDMAC_SSPBDAT + 0x100) // Shadow of SDMAC SSPBDAT
#define WDC_ADDR 0x00 // Write WDC Address Register
#define WDC_OWN_ID 0x00 // R/W Own ID
#define WDC_CONTROL 0x01 // R/W Control
#define WDC_TPERIOD 0x02 // R/W Timeout Period
#define WDC_CDB1 0x03 // R/W CDB1 start (12 bytes)
#define WDC_SECTORS 0x03 // R/W CDB1, Total Sectors
#define WDC_CDB2 0x04 // R/W CDB2
#define WDC_HEADS 0x04 // R/W CDB2, Total Heads
#define WDC_CYLS_H 0x05 // R/W CDB3, Total Cylinders (MSB)
#define WDC_CYLS_L 0x06 // R/W CDB4, Total Cylinders (LSB)
#define WDC_LADDR3 0x07 // R/W CDB5, Logical Address (MSB)
#define WDC_LADDR2 0x08 // R/W CDB6, Logical Address (2nd)
#define WDC_LADDR1 0x09 // R/W CDB7, Logical Address (3rd)
#define WDC_LADDR0 0x0a // R/W CDB8, Logical Address (LSB)
#define WDC_SECTOR 0x0b // R/W CDB9, Sector Number
#define WDC_HEAD 0x0c // R/W CDB10, Head Number
#define WDC_CYL_H 0x0d // R/W CDB11, Cylinder Number (MSB)
#define WDC_CYL_L 0x0e // R/W CDB12, Cylinder Number (LSB)
#define WDC_LUN 0x0f // R/W Target LUN
#define WDC_CMDPHASE 0x10 // R/W Command Phase
#define WDC_SYNC_TX 0x11 // R/W Synchronous Transfer Reg
#define WDC_TCOUNT2 0x12 // R/W Transfer Count (MSB)
#define WDC_TCOUNT1 0x13 // R/W Transfer Count (2nd)
#define WDC_TCOUNT0 0x14 // R/W Transfer Count (LSB)
#define WDC_DST_ID 0x15 // R/W Destination ID
#define WDC_SRC_ID 0x16 // R/W Source ID
#define WDC_SCSI_STAT 0x17 // Read SCSI Status
#define WDC_CMD 0x18 // R/W Command
#define WDC_DATA 0x19 // R/W Data
#define WDC_QUETAG 0x1a // R/W Queue Tag (WD33C93B only)
#define WDC_AUXST 0x1f // Read Auxiliary Status
#define WDC_INVALID_REG 0x1e // Not a real WD register
#define WDC_CMD_RESET 0x00 // Soft reset the WDC controller
#define WDC_CMD_ABORT 0x01 // Abort
#define WDC_CMD_DISCONNECT 0x04 // Disconnect
#define WDC_CMD_SELECT_WITH_ATN 0x06 // Select with Attention
#define WDC_CMD_DISCONNECT_MSG 0x04 // Send Disconnect Message
#define WDC_CMD_TRANSFER_INFO 0x20 // Transfer Info
#define WDC_CMD_GET_REGISTER 0x44 // Read register (CDB1) into CDB2
#define WDC_CMD_SET_REGISTER 0x45 // Write register (CDB1) from CDB2
#define WDC_CONTROL_IDI 0x04 // Intermediate Disconnect Interrupt
#define WDC_CONTROL_EDI 0x08 // Ending Disconnect Interrupt
#define WDC_AUXST_DBR 0x01 // Data Buffer Ready
#define WDC_AUXST_PE 0x02 // Parity Error
#define WDC_AUXST_CIP 0x10 // Command in Progress (interpreting)
#define WDC_AUXST_BSY 0x20 // Busy (Level II command executing)
#define WDC_AUXST_LCI 0x40 // Last Command Ignored
#define WDC_AUXST_INT 0x80 // Interrupt Pending
#define WDC_SSTAT_SEL_COMPLETE 0x11 // Select complete (initiator)
#define WDC_SSTAT_SEL_TIMEOUT 0x42 // Select timeout
#define WDC_PHASE_DATA_OUT 0x00
#define WDC_PHASE_DATA_IN 0x01
#define WDC_PHASE_CMD 0x02
#define WDC_PHASE_STATUS 0x03
#define WDC_PHASE_BUS_FREE 0x04
#define WDC_PHASE_ARB_SEL 0x05
#define WDC_PHASE_MESG_OUT 0x06
#define WDC_PHASE_MESG_IN 0x07
#define WDC_DST_ID_DPD 0x40 // Data phase direction is IN from SCSI
#define WDC_DST_ID_SCC 0x80 // Select Command Chain (send disconnect)
/* Interrupt status register */
#define SDMAC_ISTR_FIFOE 0x01 // FIFO Empty
#define SDMAC_ISTR_FIFOF 0x02 // FIFO Full
// #define SDMAC_ISTR_OVER 0x04 // DMA overrun interrupt
// #define SDMAC_ISTR_UNDER 0x08 // DMA underrun interrupt
#define SDMAC_ISTR_INT_P 0x10 // Enabled interrupt pending
#define SDMAC_ISTR_INT_E 0x20 // DMA done interrupt (end of process)
#define SDMAC_ISTR_INT_S 0x40 // Interrupt SCSI (peripheral interrupt)
#define SDMAC_ISTR_INT_F 0x80 // Interrupt follow
/* Control Register */
#define SDMAC_CONTR_IODX 0x01 // Reserved (0)
#define SDMAC_CONTR_DMADIR 0x02 // DMA Data direction (0=Read, 1=Write)
#define SDMAC_CONTR_INTEN 0x04 // Interrupt enable
//#define SDMAC_CONTR_PMODE 0x08 // Peripheral mode (1=SCSI)
#define SDMAC_CONTR_RESET 0x10 // WDC Peripheral reset (Strobe)
#define SDMAC_CONTR_TCE 0x20 // Terminal count enable
//#define SDMAC_CONTR_0x40 0x40 // Reserved (6)
//#define SDMAC_CONTR_0x80 0x80 // Reserved (7)
#define SDMAC_CONTR_DMAENA 0x100 // DMA Enabled
/* Coprocessor Interface Register */
/* Coprocessor Interface Data Direction Register */
/* Synchronous Serial Peripheral Bus Data Register */
/* Synchronous Serial Peripheral Bus Control Register */
#define SBIC_CLK 14200 // About 14.2 MHz in A3000
#define SBIC_TIMEOUT(val) ((((val) * (SBIC_CLK)) / 80000) + 1)
#define ADDR8(x) (volatile uint8_t *)(x)
#define ADDR16(x) (volatile uint16_t *)(x)
#define ADDR32(x) (volatile uint32_t *)(x)
#define ARRAY_SIZE(x) ((sizeof (x) / sizeof ((x)[0])))
#define BIT(x) (1U << (x))
/* These macros support nesting of interrupt disable state */
#define INTERRUPTS_DISABLE() if (irq_disabled++ == 0) \
Disable() /* Disable interrupts */
#define INTERRUPTS_ENABLE() if (--irq_disabled == 0) \
Enable() /* Enable Interrupts */
#define AMIGA_BERR_DSACK 0x00de0000 // Bit7=1 for BERR on timeout, else DSACK
#define BERR_DSACK_SAVE() \
uint8_t old_berr_dsack = *ADDR8(AMIGA_BERR_DSACK); \
*ADDR8(AMIGA_BERR_DSACK) &= ~BIT(7);
#define BERR_DSACK_RESTORE() \
*ADDR8(AMIGA_BERR_DSACK) = old_berr_dsack;
/* Ramsey-07 requires the processor to be in Supervisor state */
#define USE_SUPERVISOR_STATE
#ifdef USE_SUPERVISOR_STATE
#define SUPERVISOR_STATE_ENTER() { \
APTR old_stack = SuperState()
#define SUPERVISOR_STATE_EXIT() UserState(old_stack); \
}
#else
#define SUPERVISOR_STATE_ENTER()
#define SUPERVISOR_STATE_EXIT()
#endif
#define SCSI_TEST_UNIT_READY 0x00
typedef struct scsi_test_unit_ready {
uint8_t opcode;
uint8_t byte2;
uint8_t reserved[3];
uint8_t control;
} scsi_test_unit_ready_t;
extern struct ExecBase *SysBase;
struct Device *TimerBase = NULL;
typedef unsigned int uint;
static uint8_t irq_disabled = 0;
static uint8_t flag_debug = 0;
static const char *sdmac_fail_reason = "";
static uint wdc_khz;
#define DUMP_WORDS_AND_LONGS
#ifdef DUMP_WORDS_AND_LONGS
static uint32_t regs_l[0x20];
static uint16_t regs_w[0x40];
#endif
static uint8_t regs_b[0x80];
static void
get_raw_regs(void)
{
int pos;
INTERRUPTS_DISABLE();
#ifdef DUMP_WORDS_AND_LONGS
for (pos = 0; pos < ARRAY_SIZE(regs_l); pos++)
regs_l[pos] = *ADDR32(SDMAC_BASE + pos * 4);
for (pos = 0; pos < ARRAY_SIZE(regs_w); pos++)
regs_w[pos] = *ADDR16(SDMAC_BASE + pos * 2);
#endif
for (pos = 0; pos < ARRAY_SIZE(regs_b); pos++)
regs_b[pos] = *ADDR8(SDMAC_BASE + pos * 1);
INTERRUPTS_ENABLE();
}
static void
dump_values(void *addr, uint length)
{
uint32_t *data = (uint32_t *) addr;
uint pos;
for (pos = 0; pos < length / 4; pos++) {
if ((pos & 0x7) == 0) {
if (pos > 0)
printf("\n");
printf("%02x:", pos * 4);
}
printf(" %08x", *(data++));
}
printf("\n");
}
static void
dump_raw_sdmac_regs(void)
{
#ifdef DUMP_WORDS_AND_LONGS
printf("\nSDMAC registers as 32-bit reads\n");
dump_values(regs_l, sizeof (regs_l));
printf("\nSDMAC registers as 16-bit reads\n");
dump_values(regs_w, sizeof (regs_w));
#endif
printf("\nSDMAC registers as 8-bit reads\n");
dump_values(regs_b, sizeof (regs_b));
}
#define RO 1 // Read-only
#define WO 2 // Write-only
#define RW 3 // Read-write
#define BYTE 1
#define WORD 2
#define LONG 4
typedef struct {
uint32_t addr; // Base physical address
uint8_t width; // 1=BYTE, 2=WORD, 4=LONG
uint8_t type; // 1=RO, 2=WO, 3=RW
const char *const name;
const char *const desc;
} reglist_t;
static const reglist_t sdmac_reglist[] = {
{ RAMSEY_CTRL, BYTE, RW, "Ramsey_CTRL", "Ramsey Control" },
{ RAMSEY_VER, BYTE, RW, "Ramsey_VER", "Ramsey Version" },
{ SDMAC_DAWR, BYTE, WO, "SDMAC_DAWR", "DACK width (WO)" },
{ SDMAC_WTC, LONG, RW, "SDMAC_WTC", "Word Transfer Count" },
{ SDMAC_CONTR, BYTE, RW, "SDMAC_CONTR", "Control Register" },
{ RAMSEY_ACR, LONG, RW, "Ramsey_ACR", "DMA Address" },
{ SDMAC_ST_DMA, BYTE, WO, "SDMAC_ST_DMA", "Start DMA" },
{ SDMAC_FLUSH, BYTE, WO, "SDMAC_FLUSH", "Flush DMA FIFO" },
{ SDMAC_CLR_INT, BYTE, WO, "SDMAC_CLR_INT", "Clear Interrupts" },
{ SDMAC_ISTR, BYTE, RO, "SDMAC_ISTR", "Interrupt Status" },
{ SDMAC_REVISION, LONG, RO, "SDMAC_REVISION", "ReSDMAC revision" },
{ SDMAC_SP_DMA, BYTE, WO, "SDMAC_SP_DMA", "Stop DMA" },
{ SDMAC_SASR_L, LONG, WO, "SDMAC_SASR_L", "WDC register index" },
{ SDMAC_SASR_B, BYTE, RO, "SDMAC_SASR_B", "WDC register index" },
{ SDMAC_SCMD, BYTE, RW, "SDMAC_SCMD", "WDC register data" },
{ SDMAC_SASRW, LONG, WO, "SDMAC_SASRW", "WDC register index" },
{ SDMAC_SASR_B2, BYTE, RW, "SDMAC_SASR_B", "WDC register index" },
{ SDMAC_CI, LONG, RW, "SDMAC_CI",
"Coprocessor Interface Register" },
{ SDMAC_CIDDR, LONG, RW, "SDMAC_CIDDR",
"Coprocessor Interface Data Direction" },
{ SDMAC_SSPBCTL, LONG, RW, "SDMAC_SSPBCTL",
"Synchronous Serial Peripheral Bus Control"},
{ SDMAC_SSPBDAT, LONG, RW, "SDMAC_SSPBDAT",
"Synchronous Serial Peripheral Bus Data"},
};
static const reglist_t wd_reglist[] = {
{ WDC_OWN_ID, BYTE, RW, "WDC_OWN_ID", "Own ID" },
{ WDC_CONTROL, BYTE, RW, "WDC_CONTROL", "Control" },
{ WDC_TPERIOD, BYTE, RW, "WDC_TPERIOD", "Timeout Period" },
{ WDC_SECTORS, BYTE, RW, "WDC_SECTORS", "CDB1 Total Sectors" },
{ WDC_HEADS, BYTE, RW, "WDC_HEADS", "CDB2 Total Heads" },
{ WDC_CYLS_H, BYTE, RW, "WDC_CYLS_H", "CDB3 Total Cylinders MSB" },
{ WDC_CYLS_L, BYTE, RW, "WDC_CYLS_L", "CDB4 Total Cylinders LSB" },
{ WDC_LADDR3, BYTE, RW, "WDC_LADDR3", "CDB5 Logical Address MSB" },
{ WDC_LADDR2, BYTE, RW, "WDC_LADDR2", "CDB6 Logical Address 2nd" },
{ WDC_LADDR1, BYTE, RW, "WDC_LADDR1", "CDB7 Logical Address 3rd" },
{ WDC_LADDR0, BYTE, RW, "WDC_LADDR0", "CDB8 Logical Address LSB" },
{ WDC_SECTOR, BYTE, RW, "WDC_SECTOR", "CDB9 Sector Number" },
{ WDC_HEAD, BYTE, RW, "WDC_HEAD", "CDB10 Head Number" },
{ WDC_CYL_H, BYTE, RW, "WDC_CYL_H", "CDB11 Cylinder Number MSB" },
{ WDC_CYL_L, BYTE, RW, "WDC_CYL_L", "CDB12 Cylinder Number LSB" },
{ WDC_LUN, BYTE, RW, "WDC_LUN", "Target LUN" },
{ WDC_CMDPHASE, BYTE, RW, "WDC_CMDPHASE", "Command Phase" },
{ WDC_SYNC_TX, BYTE, RW, "WDC_SYNC_TX", "Synchronous Transfer" },
{ WDC_TCOUNT2, BYTE, RW, "WDC_TCOUNT2", "Transfer Count MSB" },
{ WDC_TCOUNT1, BYTE, RW, "WDC_TCOUNT1", "Transfer Count 2nd" },
{ WDC_TCOUNT0, BYTE, RW, "WDC_TCOUNT0", "Transfer Count LSB" },
{ WDC_DST_ID, BYTE, RW, "WDC_DST_ID", "Destination ID" },
{ WDC_SRC_ID, BYTE, RW, "WDC_SRC_ID", "Source ID" },
{ WDC_SCSI_STAT, BYTE, RO, "WDC_SCSI_STAT", "Status" },
{ WDC_CMD, BYTE, RW, "WDC_CMD", "Command" },
{ WDC_DATA, BYTE, RW, "WDC_DATA", "Data" },
{ WDC_QUETAG, BYTE, RW, "WDC_QUETAG", "Queue Tag" },
{ WDC_AUXST, BYTE, RO, "WDC_AUXST", "Auxiliary Status" },
};
BOOL __check_abort_enabled = 0; // Disable gcc clib2 ^C break handling
void __chkabort(void) { } // Disable gcc libnix ^C break handling
/*
* is_user_abort
* -------------
* Check for user break input (^C)
*/
static BOOL
is_user_abort(void)
{
if (SetSignal(0, 0) & SIGBREAKF_CTRL_C)
return (1);
return (0);
}
/*
* WD33C93B extended registers
*
* 0x50 live data pins D0-D7 (00 = none asserted)
* 0x53 live control pins (ff = none asserted)
* bit 0 - IO
* bit 1 - CD
* bit 2 - MSG
* 0x55 state machine
* bit 3 - REQ
*
* Haven't located ATN, BSY, RST, ACK
*/
static void
set_wdc_index(uint8_t value)
{
#undef USE_LONGWORD_SASR
#ifdef USE_LONGWORD_SASR
*ADDR32(SDMAC_SASRW) = value;
#else
*ADDR8(SDMAC_SASR_B2) = value;
#endif
}
/*
* get_wdc_reg
* -----------
* This function acquires the specified WDC33C93 register, which is available
* via a window register in the A3000 SuperDMAC.
*
* Which window access register to use is messy due to the A3000
* architecture which made properly-designed 68040 cards fail to access
* SDMAC registers as bytes, and a work-around for a 68030 write-allocate
* cache bug.
*/
static uint8_t
get_wdc_reg(uint8_t reg)
{
uint8_t value;
uint8_t oindex;
INTERRUPTS_DISABLE();
oindex = *ADDR8(SDMAC_SASR_B);
set_wdc_index(reg);
value = *ADDR8(SDMAC_SCMD);
set_wdc_index(oindex);
INTERRUPTS_ENABLE();
return (value);
}
/*
* set_wdc_reg
* -----------
* Writes an 8-bit WDC register value.
*/
static void
set_wdc_reg(uint8_t reg, uint8_t value)
{
uint8_t oindex;
INTERRUPTS_DISABLE();
oindex = *ADDR8(SDMAC_SASR_B);
set_wdc_index(reg);
*ADDR8(SDMAC_SCMD) = value;
set_wdc_index(oindex);
INTERRUPTS_ENABLE();
}
/*
* set_wdc_reg24
* -------------
* Writes a 24-bit WDC register value.
*/
static void
set_wdc_reg24(uint8_t reg, uint value)
{
uint8_t oindex;
INTERRUPTS_DISABLE();
oindex = *ADDR8(SDMAC_SASR_B);
set_wdc_index(reg);
*ADDR8(SDMAC_SCMD) = (uint8_t) (value >> 24);
*ADDR8(SDMAC_SCMD) = (uint8_t) (value >> 18);
*ADDR8(SDMAC_SCMD) = (uint8_t) value;
set_wdc_index(oindex);
INTERRUPTS_ENABLE();
}
static const char * const sdmac_istr_bits[] = {
"FIFO Empty",
"FIFO Full",
"RSVD:Overrun",
"RSVD:Underrun",
"Interrupt Pending",
"DMA Done Interrupt",
"SCSI Interrupt",
"Interrupt Follow",
};
static void
decode_sdmac_istr(uint value)
{
uint bit;
uint printed = 0;
for (bit = 0; bit < ARRAY_SIZE(sdmac_istr_bits); bit++) {
if (value & BIT(bit)) {
if (printed++)
printf(",");
else
printf(":");
printf(" %s", sdmac_istr_bits[bit]);
}
}
}
static const char * const scsi_mci_codes[] = {
"Data Out", // 000
"Data In", // 001
"Command", // 010
"Status", // 011
"Unspecified Info Out", // 100
"Unspecified Info In", // 101
"Message Out", // 110
"Message In", // 111
};
static const char * const wdc_cmd_codes[] = {
"Reset", // 0x00
"Abort", // 0x01
"Assert ATN", // 0x02
"Negate ACK", // 0x03
"Disconnect", // 0x04
"Reselect", // 0x05
"Select-with-ATN", // 0x06
"Select-without-ATN", // 0x07
"Select-with-ATN-and-Transfer", // 0x08
"Select-without-ATN-and-Transfer", // 0x09
/* read */
"Reselect-and-Receive-Data", // 0x0a
"Reselect-and-Send-Data", // 0x0b
"Wait-for-Select-and-Receive", // 0x0c
"Send-Status-and-Command-Complete", // 0x0d
"Send-Disconnect-Message", // 0x0e
"Set IDI", // 0x0f
"Receive Command", // 0x10
"Receive Data", // 0x11
"Receive Message Out", // 0x12
"Receive Unspecified Info Out", // 0x13
"Send Status", // 0x14
"Send Data", // 0x15
"Send Message In", // 0x16
"Send Unspecified Info In", // 0x17
"Translate Address", // 0x18
NULL, // 0x19
NULL, // 0x1a
NULL, // 0x1b
NULL, // 0x1c
NULL, // 0x1d
NULL, // 0x1e
NULL, // 0x1f
"Transfer Info", // 0x20
};
typedef struct {
uint8_t phase;
const char *const name;
} phaselist_t;
static const phaselist_t wdc_cmd_phases[] = {
{ 0x00, "No SCSI bus selected: D" },
{ 0x10, "Target selected: I" },
{ 0x20, "Identify message sent to target" },
{ 0x21, "Tag message code sent to target" },
{ 0x22, "Queue tag sent to target" },
{ 0x30, "Command phase stated, %u bytes transferred" },
{ 0x41, "Save-Data-Pointer message received" },
{ 0x42, "Disconnect received; but not free" },
{ 0x43, "Target disconnected after message: D" },
{ 0x44, "Reselected by target: I" },
{ 0x45, "Received matching Identify from target" },
{ 0x46, "Data transfer completed" },
{ 0x47, "Target in Receive Status phase" },
{ 0x50, "Received Status byte is in LUN register" },
{ 0x60, "Received Command-Complete message" },
{ 0x61, "Linked Command Complete" },
{ 0x70, "Received Identify message" },
{ 0x71, "Received Simple-Queue Tag message" },
};
static const char * const wdc_aux_status_bits[] = {
"Data Buffer Ready",
"Parity Error",
"RSVD2",
"RSVD3",
"Command in Progress",
"Busy",
"Last Command Ignored",
"Interrupt Pending"
};
static void
decode_wdc_aux_status(uint8_t stat)
{
uint bit;
uint printed = 0;
for (bit = 0; bit < ARRAY_SIZE(wdc_aux_status_bits); bit++) {
if (stat & BIT(bit)) {
if (printed++)
printf(",");
else
printf(":");
printf(" %s", wdc_aux_status_bits[bit]);
}
}
}
static void
decode_wdc_scsi_status(uint8_t statusreg)
{
printf(": ");
uint code = statusreg & 0xf;
switch (statusreg >> 4) {
case 0:
printf("Reset");
switch (code) {
case 0: // 0000
break;
case 1: // 0001
printf(" with Advanced features");
break;
default:
printf(", Unknown code %x", code);
break;
}
break;
case 1:
printf("Command success, ");
switch (code) {
case 0: // 0000
printf("Reselect as target");
break;
case 1: // 0001
printf("Reselect as initiator");
break;
case 3: // 0011
printf("no ATN");
break;
case 4: // 0100
printf("ATN");
break;
case 5: // 0101
printf("Translate Address");
break;
case 6: // 0110
printf("Select-and-Transfer");
break;
default:
if (code & 0x8) { // 1MCI
printf("Transfer Info: %s phase",
scsi_mci_codes[code & 0x7]);
} else {
printf("Unknown code %x", code);
}
break;
}
break;
case 2:
printf("Command pause/abort, ");
switch (code) {
case 0: // 0000
printf("Transfer Info, ACK");
break;
case 1: // 0001
printf("Save-Data-Pointer during Select-and-Transfer");
break;
case 2: // 0010
printf("Select, Reselect, or Wait-for-Select aborted");
break;
case 3: // 0011
printf("Receive or Send aborted, or Wait-for-select error");
break;
case 4: // 0100
printf("Command aborted, ATN");
break;
case 5: // 0101
printf("Transfer Aborted, protocol violation");
break;
case 6: // 0110
printf("Queue Tag mismatch, ACK");
break;
case 7: // 0111
printf("Dest ID %x LUN %x != resel src %x, ACK",
get_wdc_reg(WDC_DST_ID) & 3,
get_wdc_reg(WDC_LUN) & 3,
get_wdc_reg(WDC_SRC_ID) & 3);
break;
default:
printf("Unknown code %x", code);
break;
}
break;
case 4:
printf("Command error, ");
switch (code) {
case 0: // 0000
printf("Invalid command");
break;
case 1: // 0001
printf("Unexpected disconnect");
break;
case 2: // 0010
printf("Timeout during Select or Reselect");
break;
case 3: // 0011
printf("Parity error, no ATN");
break;
case 4: // 0100
printf("Parity error, ATN");
break;
case 5: // 0101
printf("Translate Address > disk boundary");
break;
case 6: // 0110
printf("Select-and-Transfer reselect Target != Dest %x",
get_wdc_reg(WDC_DST_ID) & 3);
break;
case 7: // 0111
printf("Parity error during Select-and-Transfer");
break;
default:
if (code & 0x8) { // 1MCI
printf("Unexpected change requested: %s phase",
scsi_mci_codes[code & 0x7]);
} else {
printf("Unknown code %x", code);
}
break;
}
break;
case 8:
printf("Bus Svc Required, ");
switch (code) {
case 0: // 0000
printf("WDC reselected as initiator");
break;
case 1: // 0001
printf("WDC reselected in advanced mode, ACK");
break;
case 2: // 0010
printf("WDC selected as target, no ATN");
break;
case 3: // 0011
printf("WDC selected as target, ATN");
break;
case 4: // 0100
printf("ATN");
break;
case 5: // 0101
printf("Target disconnected");
break;
case 7: // 0111
printf("Wait-for-Select paused, unknown target command");
break;
default:
if (code & 0x8) { // 1MCI
printf("REQ during WDC idle initiator: %s phase",
scsi_mci_codes[code & 0x7]);
} else {
printf("Unknown code %x", code);
}
break;
}
break;
default:
printf("Unknown Status 0x%02x", statusreg);
break;
}
}
static void
decode_wdc_command(uint8_t lastcmd)
{
printf(": ");
if ((lastcmd < ARRAY_SIZE(wdc_cmd_codes)) &&
(wdc_cmd_codes[lastcmd] != NULL)) {
printf("%s", wdc_cmd_codes[lastcmd]);
} else if (lastcmd == WDC_CMD_GET_REGISTER) {
printf("Get Register");
} else if (lastcmd == WDC_CMD_SET_REGISTER) {
printf("Set Register");
} else {
printf("Unknown %02x", lastcmd);
}
}
static void
decode_wdc_cmd_phase(uint8_t phase)
{
uint pos;
printf(": ");
for (pos = 0; pos < ARRAY_SIZE(wdc_cmd_phases); pos++) {
if ((phase >= 0x30) && (phase < 0x3f) &&
(wdc_cmd_phases[pos].phase == 0x30)) {
printf(wdc_cmd_phases[pos].name, phase & 0xf);
} else if (wdc_cmd_phases[pos].phase == phase) {
printf("%s", wdc_cmd_phases[pos].name);
break;
}
}
}
static void
show_wdc_pos(uint pos, uint value)
{
printf(" %02x ", wd_reglist[pos].addr & 0xff);
if (value > 0xff)
printf("%.*s", wd_reglist[pos].width * 2, "--------");
else
printf("%0*x", wd_reglist[pos].width * 2, value);
printf("%*s %-14s %s",
8 - wd_reglist[pos].width * 2, "",
wd_reglist[pos].name, wd_reglist[pos].desc);
if (value <= 0xff) {
switch (wd_reglist[pos].addr) {
case WDC_CMD:
decode_wdc_command(value);
break;
case WDC_SCSI_STAT:
decode_wdc_scsi_status(value);
break;
case WDC_AUXST:
decode_wdc_aux_status(value);
break;
case WDC_CMDPHASE:
decode_wdc_cmd_phase(value);
break;
}
}
printf("\n");
}
static void
show_wdc_reg(uint addr, uint value)
{
uint pos;
for (pos = 0; pos < ARRAY_SIZE(wd_reglist); pos++) {
if (wd_reglist[pos].addr == addr) {
show_wdc_pos(pos, value);
return;
}
}
printf(" %02x %02x\n", addr, value);
}
static void
show_regs(uint extended)
{
uint pos;
uint32_t value;
printf("\nREG VALUE NAME DESCRIPTION\n");
for (pos = 0; pos < ARRAY_SIZE(sdmac_reglist); pos++) {
if (sdmac_reglist[pos].type == WO)
continue; // Skip this register
SUPERVISOR_STATE_ENTER(); // Needed for RAMSEY_VER register
switch (sdmac_reglist[pos].width) {
case BYTE:
value = *ADDR8(sdmac_reglist[pos].addr);
break;
case WORD:
value = *ADDR16(sdmac_reglist[pos].addr);
break;
default:
case LONG:
value = *ADDR32(sdmac_reglist[pos].addr);
break;
}
SUPERVISOR_STATE_EXIT(); // Needed for RAMSEY_VER register
printf(" %02x %0*x%*s %-14s %s",
sdmac_reglist[pos].addr & 0xff,
sdmac_reglist[pos].width * 2, value,
8 - sdmac_reglist[pos].width * 2, "",
sdmac_reglist[pos].name, sdmac_reglist[pos].desc);
switch (sdmac_reglist[pos].addr) {
case SDMAC_ISTR:
decode_sdmac_istr(value);
break;
}
printf("\n");
}
printf("REG VALUE NAME DESCRIPTION\n");
for (pos = 0; pos < ARRAY_SIZE(wd_reglist); pos++) {
INTERRUPTS_DISABLE();
if ((wd_reglist[pos].type == WO) ||
(wd_reglist[pos].addr == WDC_DATA)) {
value = 0x100; // Skip this register
} else if ((extended == 0) &&
(wd_reglist[pos].addr == WDC_SCSI_STAT) &&
(get_wdc_reg(WDC_AUXST) & WDC_AUXST_INT)) {
/*
* Skip reading this register when an interrupt is pending
* because the read has a side-effect of clearing that
* interrupt pending status.
*/
value = 0x100; // Skip this register when interrupt pending
} else {
value = get_wdc_reg(wd_reglist[pos].addr);
}
INTERRUPTS_ENABLE();
show_wdc_pos(pos, value);
}
if (extended) {
uint addr;
for (addr = 0x1b; addr <= 0x1e; addr++) {
value = get_wdc_reg(addr);
show_wdc_reg(addr, value);
}
}
}
/* CIA_USEC rounds up 1 value to at least one CIA tick */
#define CIA_USEC(x) (((x) * 715909 + 284091) / 1000000)
#define CIAA_TBLO ADDR8(0x00bfe601)
#define CIAA_TBHI ADDR8(0x00bfe701)
static uint
cia_ticks(void)
{
uint8_t hi1;
uint8_t hi2;
uint8_t lo;
hi1 = *CIAA_TBHI;
lo = *CIAA_TBLO;
hi2 = *CIAA_TBHI;
/*
* The below operation will provide the same effect as:
* if (hi2 != hi1)
* lo = 0xff; // rollover occurred
*/
lo |= (hi2 - hi1); // rollover of hi forces lo to 0xff value
return (lo | (hi2 << 8));
}
void
cia_spin(unsigned int ticks)
{
uint16_t start = cia_ticks();
uint16_t now;
uint16_t diff;
while (ticks != 0) {
now = cia_ticks();
diff = start - now;
if (diff >= ticks)
break;
ticks -= diff;
start = now;
__asm__ __volatile__("nop");
__asm__ __volatile__("nop");
}
}
static uint
scsi_wait(uint8_t cond, uint wait_for_set)
{
uint timeout;
uint8_t auxst;
for (timeout = 25000; timeout > 0; timeout--) { // up to 500ms
cia_spin(10);
auxst = get_wdc_reg(WDC_AUXST);
if (wait_for_set && ((auxst & cond) != 0))
return (auxst);
else if ((wait_for_set == 0) && ((auxst & cond) == 0))
return (auxst);
}
return (0x100); // timeout
}
static uint8_t
scsi_wait_cip(void)
{
uint8_t auxst;
auxst = scsi_wait(WDC_AUXST_CIP, 0);
if (auxst == 0x100)
printf("WDC timeout CIP\n");
return (auxst);
}
/*
* get_wdc_reg_extended
* --------------------
* Acquires the specified WDC33C93 register.
* Access to to internal registers is supported by way of
* the undocumented register get command.
*/
static uint8_t
get_wdc_reg_extended(uint reg)
{
uint8_t reg3;
uint8_t reg4;
uint8_t value;
uint8_t scsi_stat;
if (reg < 0x40)
return (get_wdc_reg(reg));
/* This might only work with the WD33C93B parts */
INTERRUPTS_DISABLE();
scsi_wait_cip();
reg3 = get_wdc_reg(WDC_CDB1);
reg4 = get_wdc_reg(WDC_CDB2);
set_wdc_reg(WDC_CDB1, reg);
(void) get_wdc_reg(WDC_SCSI_STAT);
set_wdc_reg(WDC_CMD, WDC_CMD_GET_REGISTER);
scsi_wait_cip();
scsi_stat = get_wdc_reg(WDC_SCSI_STAT);
value = get_wdc_reg(WDC_CDB2);
set_wdc_reg(WDC_CDB1, reg3);
set_wdc_reg(WDC_CDB2, reg4);
INTERRUPTS_ENABLE();
if (scsi_stat != (WDC_CMD_GET_REGISTER | 0x10))
printf("[fail: %02x]", scsi_stat);
return (value);
}
/*