-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy patha4091.c
5047 lines (4581 loc) · 163 KB
/
a4091.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
/*
* A4091 Test Utility
* ------------------
* Utility to inspect and test an installed A4091 SCSI controller card
* for correct operation.
*
* Copyright 2023 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: A4091 1.2 ("AMIGA_DATE") © Chris Hooper";
#include <stdio.h>
#include <stdlib.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/alib.h>
#include <sys/time.h>
#include "ndkcompat.h"
#include "a4091.h"
/*
* gcc clib2 headers are bad (for example, no stdint definitions) and are
* not being included by our build. Because of that, we need to fix up
* some stdio definitions.
*/
extern struct iob ** __iob;
#undef stdout
#define stdout ((FILE *)__iob[1])
#define CACHE_LINE_WRITE(addr, len) CacheClearE((void *)(addr), len, \
CACRF_ClearD)
#define CACHE_LINE_DISCARD(addr, len) CacheClearE((void *)(addr), len, \
CACRF_InvalidateD)
extern struct ExecBase *SysBase;
#define ADDR8(x) (volatile uint8_t *)(x)
#define ADDR32(x) (volatile uint32_t *)(x)
#define ARRAY_SIZE(x) ((sizeof (x) / sizeof ((x)[0])))
#define BIT(x) (1 << (x))
#define FLAG_DEBUG 0x01 /* Debug output */
#define FLAG_MORE_DEBUG 0x02 /* More debug output */
#define FLAG_IS_A4000T 0x04 /* A4000T onboard SCSI controller */
#define SUPERVISOR_STATE_ENTER() { \
APTR old_stack = SuperState()
#define SUPERVISOR_STATE_EXIT() UserState(old_stack); \
}
#define INTERRUPTS_DISABLE() Disable() /* Disable Interrupts */
#define INTERRUPTS_ENABLE() Enable() /* Enable Interrupts */
#define FIRST(n) (struct Interrupt *)((n)->lh_Head)
#define EMPTY(n) ((n)->lh_Head == (struct Node *)&n->lh_Tail)
#define NEXTINT(i) (struct Interrupt *)((i)->is_Node.ln_Succ)
#define LAST(i) (((i)->is_Node.ln_Succ)->ln_Succ == 0)
/* NCR53C710 registers */
#define REG_SCNTL0 0x03 // SCSI control 0
#define REG_SCNTL1 0x02 // SCSI control 1
#define REG_SDID 0x01 // SCSI destination ID
#define REG_SIEN 0x00 // SCSI interrupt enable
#define REG_SCID 0x07 // SCSI chip ID
#define REG_SCFER 0x06 // SCSI transfer
#define REG_SODL 0x05 // SCSI output data latch
#define REG_SOCL 0x04 // SCSI output control latch
#define REG_SFBR 0x0b // SCSI first byte received
#define REG_SIDL 0x0a // SCSI input data latch
#define REG_SBDL 0x09 // SCSI bus data lines
#define REG_SBCL 0x08 // SCSI bus control lines
#define REG_DSTAT 0x0f // DMA status
#define REG_SSTAT0 0x0e // SCSI status 0
#define REG_SSTAT1 0x0d // SCSI status 1
#define REG_SSTAT2 0x0c // SCSI status 2
#define REG_DSA 0x10 // Data structure address
#define REG_CTEST0 0x17 // Chip test 0
#define REG_CTEST1 0x16 // Chip test 1
#define REG_CTEST2 0x15 // Chip test 2
#define REG_CTEST3 0x14 // Chip test 3
#define REG_CTEST4 0x1b // Chip test 4: MUX ZMOD SZM SLBE SFWR FBL2-FBL0
#define REG_CTEST5 0x1a // Chip test 5
#define REG_CTEST6 0x19 // Chip test 6: DMA FIFO
#define REG_CTEST7 0x18 // Chip test 7
#define REG_TEMP 0x1c // Temporary stack
#define REG_DFIFO 0x23 // DMA FIFO
#define REG_ISTAT 0x22 // Interrupt status
#define REG_CTEST8 0x21 // Chip test 8
#define REG_LCRC 0x20 // Longitudinal parity
#define REG_DBC 0x25 // DMA byte counter
#define REG_DCMD 0x24 // DMA command
#define REG_DNAD 0x28 // DMA next address for data
#define REG_DSP 0x2c // DMA SCRIPTS pointer
#define REG_DSPS 0x30 // DMA SCRIPTS pointer save
#define REG_SCRATCH 0x34 // General purpose scratch pad
#define REG_DMODE 0x3b // DMA mode
#define REG_DIEN 0x3a // DMA interrupt enable
#define REG_DWT 0x39 // DMA watchdog timer
#define REG_DCNTL 0x38 // DMA control
#define REG_ADDER 0x3c // Sum output of internal adder
#define REG_SCNTL0_TRG BIT(0) // Enable Target mode as default
#define REG_SCNTL0_EPG BIT(2) // Generate parity on the SCSI bus
#define REG_SIEN_PAR BIT(0) // Interrupt on parity error
#define REG_SIEN_RST BIT(1) // Interrupt on SCSI reset received
#define REG_SIEN_UDC BIT(2) // Interrupt on Unexpected disconnect
#define REG_SIEN_SGE BIT(3) // Interrupt on SCSI gross error
#define REG_SIEN_SEL BIT(4) // Interrupt on Selected or reselected
#define REG_SIEN_STO BIT(5) // Interrupt on SCSI bus timeout
#define REG_SIEN_FCMP BIT(6) // Interrupt on Function complete
#define REG_SIEN_PM BIT(7) // Interrupt on Unexpected Phase mismatch
#define REG_DIEN_DFE BIT(7) // DMA interrupt on FIFO empty
#define REG_DIEN_BF BIT(5) // DMA interrupt on Bus Fault
#define REG_DIEN_ABRT BIT(4) // DMA interrupt on Aborted
#define REG_DIEN_SSI BIT(3) // DMA interrupt on SCRIPT Step Interrupt
#define REG_DIEN_SIR BIT(2) // DMA interrupt on SCRIPT Interrupt Instruction
#define REG_DIEN_WTD BIT(1) // DMA interrupt on Watchdog Timeout Detected
#define REG_DIEN_ILD BIT(0) // DMA interrupt on Illegal Instruction Detected
#define REG_ISTAT_DIP BIT(0) // DMA interrupt pending
#define REG_ISTAT_SIP BIT(1) // SCSI interrupt pending
#define REG_ISTAT_RST BIT(6) // Reset the 53C710
#define REG_ISTAT_ABRT BIT(7) // Abort
#define REG_DMODE_MAN BIT(0) // DMA Manual start mode
#define REG_DMODE_U0 BIT(1) // DMA User programmable transfer type
#define REG_DMODE_FAM BIT(2) // DMA Fixed Address mode (set avoids DNAD inc)
#define REG_DMODE_PD BIT(3) // When set: FC0=0 for data & FC0=1 for program
#define REG_DMODE_FC1 BIT(4) // Value driven on FC1 when bus mastering
#define REG_DMODE_FC2 BIT(5) // Value driven on FC2 when bus mastering
#define REG_DMODE_BLE0 0 // Burst length 1-transfer
#define REG_DMODE_BLE1 BIT(6) // Burst length 2-transfers
#define REG_DMODE_BLE2 BIT(7) // Burst length 4-transfers
#define REG_DMODE_BLE3 (BIT(6) | BIT(7)) // Burst length 8-transfers
#define REG_DCNTL_COM BIT(0) // Enable 53C710 mode
#define REG_DCNTL_FA BIT(1) // Enable fast arbitration (faster DMA start)
#define REG_DCNTL_STD BIT(2) // Start DMA operation (execute SCRIPT)
#define REG_DCNTL_LLM BIT(3) // Low level mode (no DMA or SCRIPTS)
#define REG_DCNTL_SSM BIT(4) // SCRIPTS single-step mode
#define REG_DCNTL_EA BIT(5) // Enable Ack
#define REG_DCNTL_CFD0 BIT(7) // SCLK 16.67-25.00 MHz
#define REG_DCNTL_CFD1 BIT(6) // SCLK 25.01-37.50 MHz
#define REG_DCNTL_CFD2 0 // SCLK 37.50-50.00 MHz
#define REG_DCNTL_CFD3 (BIT(7) | BIT(6)) // SCLK 50.01-66.67 MHz
#define REG_DSTAT_IID BIT(0) // Illegal instruction
#define REG_DSTAT_WDT BIT(1) // Watchdog timeout
#define REG_DSTAT_SSI BIT(3) // SCRIPTS single-step interrupt
#define REG_DSTAT_ABRT BIT(4) // Abort condition
#define REG_DSTAT_BF BIT(5) // Bus fault
#define REG_DSTAT_DFE BIT(7) // DMA FIFO empty
#define REG_SCNTL1_AESP BIT(2) // Assert even SCSI data parity
#define REG_SCNTL1_RST BIT(3) // Assert reset on SCSI bus
#define REG_SCNTL1_ESR BIT(5) // Respond to selection and reselection
#define REG_SCNTL1_ADB BIT(6) // Assert SCSI data bus (SODL/SOCL registers)
#define REG_SSTAT1_PAR BIT(0) // SCSI parity state
#define REG_SSTAT1_RST BIT(1) // SCSI bus reset is asserted
#define REG_CTEST2_SFP BIT(4) // SCSI FIFO parity
#define REG_CTEST4_FBL2 BIT(2) // Send CTEST6 register to lane of the DMA FIFO
#define REG_CTEST4_SFWR BIT(3) // Send SODL register writes to SCSI FIFO
#define REG_CTEST4_SLBE BIT(4) // SCSI loopback mode enable
#define REG_CTEST4_CDIS BIT(7) // Cache burst disable
#define REG_CTEST5_DACK BIT(0) // Data acknowledge (1=DMA acks SCSI DMA req)
#define REG_CTEST5_DREQ BIT(1) // Data request (1=SCSI requests DMA transfer)
#define REG_CTEST5_DDIR BIT(3) // DMA direction (1=SCSI->host, 0=host->SCSI)
#define REG_CTEST5_BBCK BIT(6) // Decrement DBC by 1, 2, or 4
#define REG_CTEST5_ADCK BIT(7) // Increment DNAD by 1, 2, or 4
#define REG_CTEST8_CLF BIT(2) // Clear DMA and SCSI FIFOs
#define REG_CTEST8_FLF BIT(3) // Flush DMA FIFO
#define NCR_DMA_FIFO_SIZE 16 // DMA FIFO talks to CPU bus
#define NCR_SCSI_FIFO_SIZE 8 // SCSI FIFO talks to SCSI bus
#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;
#define DMA_LEN_BIT 12 // 4K DMA
#define DMA_COPY_LEN BIT(DMA_LEN_BIT)
/* Modern stdint types */
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
typedef volatile APTR VAPTR;
typedef unsigned int uint;
static void a4091_state_restore(int skip_reset);
static uint runtime_flags = 0;
static const char * const expansion_library_name = "expansion.library";
typedef struct {
struct Interrupt *local_isr; // Temporary interrupt server
uint32_t reg_addr; // Base address of device registers
volatile uint32_t intcount; // Total interrupts
volatile uint8_t ireg_istat; // ISTAT captured by interrupt handler
volatile uint8_t ireg_sien; // SIEN captured by interrupt handler
volatile uint8_t ireg_sstat0; // SSTAT0 captured by interrupt handler
volatile uint8_t ireg_dstat; // DSTAT captured by interrupt handler
uint8_t card_owned; // Task current owns device interrupts
uint8_t cleanup_installed; // Cleanup on exit code installed
uint32_t driver_isr_count; // Number of driver ISRs captured
uint32_t driver_task_count; // Number of driver tasks captured
struct List driver_isrs; // SCSI driver interrupt server list
struct List driver_rtask; // SCSI driver ready task list
struct List driver_wtask; // SCSI driver waiting task list
} a4091_save_t;
static a4091_save_t a4091_save;
extern BOOL __check_abort_enabled;
static uint
check_break(void)
{
if (SetSignal(0, 0) & SIGBREAKF_CTRL_C) {
printf("^C Abort\n");
return (1);
}
return (0);
}
static uint64_t
read_system_ticks(void)
{
struct DateStamp ds;
DateStamp(&ds); /* Measured latency is ~250us on A3000 A3640 */
return ((uint64_t) (ds.ds_Days * 24 * 60 +
ds.ds_Minute) * 60 * TICKS_PER_SECOND + ds.ds_Tick);
}
static void
print_system_ticks(void)
{
struct DateStamp ds;
DateStamp(&ds); /* Measured latency is ~250us on A3000 A3640 */
printf("d=%d m=%d t=%d\n", ds.ds_Days, ds.ds_Minute, ds.ds_Tick);
}
static uint64_t
read_system_ticks_sync(void)
{
uint64_t stick = read_system_ticks();
uint64_t tick;
while ((tick = read_system_ticks()) == stick)
;
return (tick);
}
/*
* get_milli_ticks
* ---------------
* Returns the number of fractional ticks (thousandths) which have elapsed
* past the specified tick. This is done by counting the number of OS
* calls to DateStamp() before the tick counter rolls over. The resolution
* is likely not accurate past the tenths place, as on an A4000T with A3640,
* the Amiga can only do about 58 calls per tick.
*/
uint
get_milli_ticks(uint64_t ltick)
{
uint64_t ctick;
uint64_t etick;
uint left_count = 0;
uint full_count = 0;
while ((ctick = read_system_ticks()) == ltick)
left_count++;
while ((etick = read_system_ticks()) == ctick)
full_count++;
if (full_count == 0)
full_count = 1;
if (left_count > full_count)
left_count = full_count;
return ((full_count * (ctick - ltick) - left_count) * 1000 / full_count);
}
static const char * const z2_config_sizes[] =
{
"8 MB", "64 KB", "128 KB", "256 KB", "512KB", "1MB", "2MB", "4MB"
};
static const char * const z3_config_sizes[] =
{
"16 MB", "32 MB", "64 MB", "128 MB", "256 MB", "512 MB", "1 GB", "RSVD"
};
static const char * const config_subsizes[] =
{
"Same-as-Physical", "Automatically-sized", "64 KB", "128 KB",
"256 KB", "512 KB", "1MB", "2MB",
"4MB", "6MB", "8MB", "10MB", "12MB", "14MB", "Rsvd1", "Rsvd2"
};
static uint flag_verbose = 1; // Run in verbose mode
static uint32_t a4091_base; // Base address for current card
static uint32_t a4091_rom_base; // ROM base address
static uint32_t a4091_reg_base; // Registers base address
static uint32_t a4091_switch_base; // Switches base address
static uint8_t
get_rom(uint off)
{
if (runtime_flags & FLAG_IS_A4000T) {
return (0); // No Zorro config for 53C710 in A4000T
} else {
/*
* ROM byte bits 7:4 are in base[addr] 31:28
* ROM byte bits 3:0 are in base[addr] 15:12
*/
uint32_t hilo = *ADDR32(a4091_base + A4091_OFFSET_ROM + off * 4);
return (((hilo >> 24) & 0xf0) | ((hilo >> 12) & 0xf));
}
}
static uint8_t
get_creg(uint reg)
{
uint8_t hi;
uint8_t lo;
if (runtime_flags & FLAG_IS_A4000T) {
return (0); // No Zorro config for 53C710 in A4000T
} else {
/*
* CREG byte bits 7:4 are in base[addr + 0x000] 7:4
* CREG byte bits 3:0 are in base[addr + 0x100] 7:4
*/
hi = ~*ADDR8(a4091_base + A4091_OFFSET_AUTOCONFIG + reg * 4);
lo = ~*ADDR8(a4091_base + A4091_OFFSET_AUTOCONFIG + reg * 4 + 0x100);
return ((hi & 0xf0) | (lo >> 4));
}
}
#if 0
static void
set_creg(uint32_t addr, uint reg, uint8_t value)
{
*ADDR8(addr + A4091_OFFSET_AUTOCONFIG + reg * 4) = value;
*ADDR8(addr + A4091_OFFSET_AUTOCONFIG + reg * 4 + 0x100) = value << 4;
}
#endif
static void
show_creg_value(uint reg, uint8_t value)
{
printf(" %02x %02x", reg, value);
}
static uint8_t
show_creg(uint reg)
{
uint8_t value = get_creg(reg);
show_creg_value(reg, value);
return (value);
}
static int
autoconfig_reserved(uint reg)
{
uint8_t value = get_creg(reg);
if (value != 0x00) {
show_creg_value(reg, value);
printf(" Reserved: should be 0x00\n");
return (1);
}
return (0);
}
static int
decode_autoconfig(void)
{
uint8_t value;
uint32_t value32;
int rc = 0;
int is_z3 = 0;
int is_autoboot = 0;
int byte;
const char * const *sizes = z2_config_sizes;
printf("A4091 Autoconfig area\n"
" Reg Data Decode\n");
value = ~show_creg(0x00);
switch (value >> 6) {
case 0:
case 1:
printf(" Zorro_Reserved");
break;
case 2:
printf(" ZorroIII");
is_z3 = 1;
break;
case 3:
printf(" ZorroII");
break;
}
if (value & BIT(5))
printf(" Memory");
if (is_z3 && ((~get_creg(0x08)) & BIT(5)))
sizes = z3_config_sizes;
printf(" Size=%s", sizes[value & 0x7]);
if (value & BIT(4)) {
printf(" Autoboot");
is_autoboot = 1;
}
if (value & BIT(3))
printf(" Link-to-next");
printf("\n");
printf(" Product=0x%02x\n", show_creg(0x01) & 0xff);
value = show_creg(0x02);
if (is_z3) {
if (value & BIT(7)) {
printf(" Device-Memory");
rc++; // Unexpected for A4091
} else {
printf(" Device-IO");
}
} else {
rc++; // Unexpected for A4091
if (value & BIT(7))
printf(" Fit-ZorroII");
else
printf(" Fit-anywhere");
}
if (value & BIT(5))
printf(" NoShutup");
if (is_z3 && ((value & BIT(4)) == 0))
printf(" Invalid_RSVD");
if (value & BIT(5))
printf(" SizeExt");
printf(" %s\n", config_subsizes[value & 0x0f]);
if (autoconfig_reserved(0x03))
rc = 1;
value32 = show_creg(0x04) << 8;
printf(" Mfg Number high byte\n");
value32 |= show_creg(0x05);
printf(" Mfg Number low byte Manufacturer=0x%04x\n", value32);
value32 = 0;
for (byte = 0; byte < 4; byte++) {
value32 <<= 8;
value32 |= show_creg(0x06 + byte);
printf(" Serial number byte %d", byte);
if (byte == 3)
printf(" Serial=0x%08x", value32);
printf("\n");
}
if (is_autoboot) {
value32 = show_creg(0x10) << 8;
printf(" Option ROM vector high\n");
value32 |= show_creg(0x11);
printf(" Option ROM vector low Offset=0x%04x\n", value32);
}
for (byte = 0x0c; byte <= 0x0f; byte++)
rc += autoconfig_reserved(byte);
for (byte = 0x14; byte <= 0x31; byte++)
rc += autoconfig_reserved(byte);
return (rc);
}
typedef const char * const bitdesc_t;
static bitdesc_t bits_scntl0[] = {
"TRG", "AAP", "EPG", "EPC", "WATN/", "START", "ARB0", "ARB1"
};
static bitdesc_t bits_scntl1[] = {
"RES0", "RES1", "AESP", "RST", "CON", "FSR", "ADB", "EXC"
};
static bitdesc_t bits_sien[] = {
"PAR", "RST/", "UDC", "SGE", "SEL", "STO", "FCMP", "M/A"
};
static bitdesc_t bits_sbcl[] = {
"I/O", "C/D", "MSG", "ATN", "SEL", "BSY", "ACK", "REQ"
};
static bitdesc_t bits_dstat[] = {
"IID", "WTD", "SIR", "SSI", "ABRT", "RF", "RES6", "DFE"
};
static bitdesc_t bits_sstat0[] = {
"PAR", "RST/", "UDC", "SGE", "SEL", "STO", "FCMP", "M/A"
};
static bitdesc_t bits_sstat1[] = {
"SDP/", "RST/", "WOA", "LOA", "AIP", "OLF", "ORF", "ILF"
};
static bitdesc_t bits_sstat2[] = {
"I/O", "C/D", "MSG", "SDP", "FF0", "FF1", "FF2", "FF3"
};
static bitdesc_t bits_ctest0[] = {
"DDIR", "RES1", "ERF", "HSC", "EAN", "GRP", "BTD", "RES7"
};
static bitdesc_t bits_ctest2[] = {
"DACK", "DREQ", "TEOP", "DFP", "SFP", "SOFF", "SIGP", "RES7"
};
static bitdesc_t bits_ctest4[] = {
"FBL0", "FBL1", "FBL2", "SFWR", "SLBE", "SZM", "ZMOD", "MUX"
};
static bitdesc_t bits_ctest5[] = {
"DACK", "DREQ", "EOP", "DDIR", "MASR", "ROFF", "BBCK", "ADCK"
};
static bitdesc_t bits_ctest7[] = {
"DIFF", "TT1", "EVP", "DFP", "NOTIME", "SC0", "SC1", "CDIS"
};
static bitdesc_t bits_istat[] = {
"DIP", "SIP", "RSV2", "CON", "RSV4", "SIOP", "RST", "ABRT"
};
static bitdesc_t bits_ctest8[] = {
"SM", "FM", "CLF", "FLF", "V0", "V1", "V2", "V3"
};
static bitdesc_t bits_dmode[] = {
"MAN", "U0", "FAM", "PD", "FC1", "FC2", "BL0", "BL1"
};
static bitdesc_t bits_dien[] = {
"HD", "WTD", "SIR", "SSI", "ABRT", "BF", "RES6", "RES7"
};
static bitdesc_t bits_dcntl[] = {
"COM", "FA", "STD", "LLM", "SSM", "EA", "CF0", "CF1"
};
typedef struct {
uint8_t reg_loc;
uint8_t reg_size; // size in bytes
uint8_t show; // safe to read/display this register
uint8_t pad; // structure padding
const char reg_name[8]; // Register name
const char * const reg_desc; // Long description
bitdesc_t *reg_bits; // Individual bit names
} ncr_regdefs_t;
static const ncr_regdefs_t ncr_regdefs[] =
{
{ 0x03, 1, 1, 0, "SCNTL0", "SCSI control 0", bits_scntl0 },
{ 0x02, 1, 1, 0, "SCNTL1", "SCSI control 1", bits_scntl1 },
{ 0x01, 1, 1, 0, "SDID", "SCSI destination ID" },
{ 0x00, 1, 1, 0, "SIEN", "SCSI IRQ enable", bits_sien },
{ 0x07, 1, 1, 0, "SCID", "SCSI chip ID" },
{ 0x06, 1, 1, 0, "SXFER", "SCSI transfer" },
{ 0x05, 1, 1, 0, "SODL", "SCSI output data latch" },
{ 0x04, 1, 1, 0, "SOCL", "SCSI output control latch", bits_sbcl },
{ 0x0b, 1, 1, 0, "SFBR", "SCSI first byte received" },
{ 0x0a, 1, 1, 0, "SIDL", "SCSI input data latch" },
{ 0x09, 1, 1, 0, "SBDL", "SCSI bus data lines" },
{ 0x08, 1, 1, 0, "SBCL", "SCSI bus control lines", bits_sbcl },
{ 0x0f, 1, 1, 0, "DSTAT", "DMA status", bits_dstat },
{ 0x0e, 1, 1, 0, "SSTAT0", "SCSI status 0", bits_sstat0 },
{ 0x0d, 1, 1, 0, "SSTAT1", "SCSI status 1", bits_sstat1 },
{ 0x0c, 1, 1, 0, "SSTAT2", "SCSI status 2", bits_sstat2 },
{ 0x10, 4, 1, 0, "DSA", "Data structure address" },
{ 0x17, 1, 1, 0, "CTEST0", "Chip test 0", bits_ctest0 },
{ 0x16, 1, 1, 0, "CTEST1", "Chip test 1 7-4=FIFO_Empty 3-0=FIFO_Full" },
{ 0x15, 1, 1, 0, "CTEST2", "Chip test 2", bits_ctest2 },
{ 0x14, 1, 0, 0, "CTEST3", "Chip test 3 SCSI FIFO" },
{ 0x1b, 1, 1, 0, "CTEST4", "Chip test 4", bits_ctest4 },
{ 0x1a, 1, 1, 0, "CTEST5", "Chip test 5", bits_ctest5 },
{ 0x19, 1, 0, 0, "CTEST6", "Chip test 6 DMA FIFO" },
{ 0x18, 1, 1, 0, "CTEST7", "Chip test 7", bits_ctest7 },
{ 0x1c, 4, 1, 0, "TEMP", "Temporary Stack" },
{ 0x23, 1, 1, 0, "DFIFO", "DMA FIFO" },
{ 0x22, 1, 1, 0, "ISTAT", "Interrupt Status", bits_istat },
{ 0x21, 1, 1, 0, "CTEST8", "Chip test 8", bits_ctest8 },
{ 0x20, 1, 1, 0, "LCRC", "Longitudinal parity" },
{ 0x25, 3, 1, 0, "DBC", "DMA byte counter" },
{ 0x24, 1, 1, 0, "DCMD", "DMA command" },
{ 0x28, 4, 1, 0, "DNAD", "DMA next address for data" },
{ 0x2c, 4, 1, 0, "DSP", "DMA SCRIPTS pointer" },
{ 0x30, 4, 1, 0, "DSPS", "DMA SCRIPTS pointer save" },
{ 0x34, 4, 1, 0, "SCRATCH", "General purpose scratch pad" },
{ 0x3b, 1, 1, 0, "DMODE", "DMA mode", bits_dmode },
{ 0x3a, 1, 1, 0, "DIEN", "DMA interrupt enable", bits_dien },
{ 0x39, 1, 1, 0, "DWT", "DMA watchdog timer" }, // No support in FS-UAE
{ 0x38, 1, 1, 0, "DCNTL", "DMA control", bits_dcntl },
{ 0x3c, 4, 1, 0, "ADDER", "Sum output of internal adder" },
};
static uint8_t
get_ncrreg8_noglob(uint32_t a4091_regs, uint reg)
{
uint8_t value;
value = *ADDR8(a4091_regs + reg);
return (value);
}
static void
set_ncrreg8_noglob(uint32_t a4091_regs, uint reg, uint8_t value)
{
*ADDR8(a4091_regs + 0x40 + reg) = value;
}
static uint8_t
get_ncrreg8(uint reg)
{
uint8_t value = *ADDR8(a4091_reg_base + reg);
if (runtime_flags & FLAG_DEBUG)
printf("[%08x] R %02x\n", a4091_reg_base + reg, value);
return (value);
}
static uint32_t
get_ncrreg32(uint reg)
{
#if 1
uint32_t value = *ADDR32(a4091_reg_base + reg);
#else
/*
* Work around 68030 cache line allocation bug.
* Not sure this is needed, so the code is disabled at this time.
*/
APTR addr = (APTR) (a4091_reg_base + reg);
uint32_t value;
ULONG buf_handled = 4;
CachePreDMA(addr, &buf_handled, DMA_ReadFromRAM);
value = *ADDR32(a4091_reg_base + reg);
CachePostDMA(addr, &buf_handled, DMA_ReadFromRAM);
#endif
if (runtime_flags & FLAG_DEBUG)
printf("[%08x] R %08x\n", a4091_reg_base + reg, value);
return (value);
}
static uint32_t
get_ncrreg32b(uint reg)
{
uint32_t value = (*ADDR8(a4091_reg_base + reg + 0) << 24) |
(*ADDR8(a4091_reg_base + reg + 1) << 16) |
(*ADDR8(a4091_reg_base + reg + 2) << 8) |
(*ADDR8(a4091_reg_base + reg + 3));
if (runtime_flags & FLAG_DEBUG)
printf("[%08x] R %08x\n", a4091_reg_base + reg, value);
return (value);
}
/* Write at shadow register (+0x40) to avoid 68030 write-allocate bug */
static void
set_ncrreg8(uint reg, uint8_t value)
{
if (runtime_flags & FLAG_DEBUG)
printf("[%08x] W %02x\n", a4091_reg_base + reg, value);
*ADDR8(a4091_reg_base + 0x40 + reg) = value;
}
static void
set_ncrreg32(uint reg, uint32_t value)
{
if (runtime_flags & FLAG_DEBUG)
printf("[%08x] W %08x\n", a4091_reg_base + reg, value);
*ADDR32(a4091_reg_base + 0x40 + reg) = value;
}
static void
set_ncrreg32b(uint reg, uint32_t value)
{
if (runtime_flags & FLAG_DEBUG)
printf("[%08x] W %08x\n", a4091_reg_base + reg, value);
*ADDR8(a4091_reg_base + 0x40 + reg + 0) = value >> 24;
*ADDR8(a4091_reg_base + 0x40 + reg + 1) = value >> 16;
*ADDR8(a4091_reg_base + 0x40 + reg + 2) = value >> 8;
*ADDR8(a4091_reg_base + 0x40 + reg + 3) = value;
}
/*
* access_timeout
* --------------
* Returns non-zero if the number of ticks has elapsed since the specified
* tick_start.
*/
static int
access_timeout(const char *msg, uint32_t ticks, uint64_t tick_start)
{
uint64_t tick_end = read_system_ticks();
if (tick_end < tick_start) {
printf("Invalid time comparison: %08x:%08x < %08x:%08x\n",
(uint32_t) (tick_end >> 32), (uint32_t) tick_end,
(uint32_t) (tick_start >> 32), (uint32_t) tick_start);
return (FALSE); /* Should not occur */
}
/* Signed integer compare to avoid wrap */
if ((int) (tick_end - tick_start) > (int) ticks) {
uint64_t diff;
printf("%s: %d ticks", msg, (uint32_t) (tick_end - tick_start));
diff = tick_end - tick_start;
if (diff > TICKS_PER_SECOND * 10) {
printf(": bug? %08x%08x %08x%08x\n",
(uint32_t) (tick_start >> 32), (uint32_t) tick_start,
(uint32_t) (tick_end >> 32), (uint32_t) tick_end);
print_system_ticks();
}
printf("\n");
return (TRUE);
}
return (FALSE);
}
/*
* a4091_reset
* -----------
* Resets the A4091's 53C710 SCSI controller.
*/
static void
a4091_reset(void)
{
if (runtime_flags & FLAG_IS_A4000T)
set_ncrreg8(REG_DCNTL, REG_DCNTL_EA); // Enable Ack: allow reg writes
set_ncrreg8(REG_ISTAT, REG_ISTAT_RST); // Reset
(void) get_ncrreg8(REG_ISTAT); // Push out write
set_ncrreg8(REG_ISTAT, 0); // Clear reset
(void) get_ncrreg8(REG_ISTAT); // Push out write
Delay(1);
/* SCSI Core clock (37.51-50 MHz) */
if (runtime_flags & FLAG_IS_A4000T)
set_ncrreg8(REG_DCNTL, REG_DCNTL_COM | REG_DCNTL_CFD2 | REG_DCNTL_EA);
else
set_ncrreg8(REG_DCNTL, REG_DCNTL_COM | REG_DCNTL_CFD2);
set_ncrreg8(REG_SCID, BIT(7)); // Set SCSI ID
set_ncrreg8(REG_DWT, 60); // 25MHz DMA timeout: 640ns * 60
const int burst_mode = 8;
switch (burst_mode) {
default:
case 1:
/* 1-transfer burst, FC = 101 -- works on A3000 */
set_ncrreg8(REG_DMODE, REG_DMODE_BLE0 | REG_DMODE_FC2);
break;
case 2:
/* 2-transfer burst, FC = 101 */
set_ncrreg8(REG_DMODE, REG_DMODE_BLE1 | REG_DMODE_FC2);
break;
case 4:
/* 4-transfer burst, FC = 101 */
set_ncrreg8(REG_DMODE, REG_DMODE_BLE2 | REG_DMODE_FC2);
break;
case 8:
/* 8-transfer burst, FC = 101 */
set_ncrreg8(REG_DMODE, REG_DMODE_BLE3 | REG_DMODE_FC2);
break;
}
if ((runtime_flags & FLAG_IS_A4000T) == 0) {
/* Disable cache line bursts */
set_ncrreg8(REG_CTEST7, get_ncrreg8(REG_CTEST7) | REG_CTEST4_CDIS);
}
/* Disable interrupts */
set_ncrreg8(REG_DIEN, 0);
}
#if 0
/*
* a4091_abort
* -----------
* Abort the current SCRIPTS operation, stopping the SCRIPTS processor.
*/
static void
a4091_abort(void)
{
uint8_t istat;
uint64_t tick_start;
istat = get_ncrreg8(REG_ISTAT);
set_ncrreg8(REG_ISTAT, istat | REG_ISTAT_ABRT);
(void) get_ncrreg8(REG_ISTAT);
tick_start = read_system_ticks();
while ((get_ncrreg8(REG_DSTAT) & REG_DSTAT_ABRT) == 0) {
if (access_timeout("DSTAT_ABRT timeout", 2, tick_start))
break;
}
}
#endif
/*
* a4091_irq_handler
* -----------------
* Handle interrupts from the 53C710 SCSI controller
*/
LONG
a4091_irq_handler(register a4091_save_t *save asm("a1"))
{
uint8_t istat = get_ncrreg8_noglob(save->reg_addr, REG_ISTAT);
if (istat == 0) {
return (0);
}
if (istat & REG_ISTAT_ABRT)
set_ncrreg8_noglob(save->reg_addr, REG_ISTAT, 0);
if ((istat & (REG_ISTAT_DIP | REG_ISTAT_SIP)) != 0) {
uint prev_istat = save->ireg_istat;
/*
* If ISTAT_SIP is set, read SSTAT0 register to determine cause
* If ISTAT_DIP is set, read DSTAT register to determine cause
*/
save->ireg_istat = istat;
save->ireg_sien = get_ncrreg8_noglob(save->reg_addr, REG_SIEN);
save->ireg_sstat0 = get_ncrreg8_noglob(save->reg_addr, REG_SSTAT0);
save->ireg_dstat = get_ncrreg8_noglob(save->reg_addr, REG_DSTAT);
save->intcount++;
if (prev_istat == 0) {
return (1); // Handled
}
}
return (0); // Not handled
}
static void
a4091_add_local_irq_handler(void)
{
if (a4091_save.local_isr == NULL) {
a4091_save.intcount = 0;
a4091_save.local_isr = AllocMem(sizeof (*a4091_save.local_isr),
MEMF_CLEAR | MEMF_PUBLIC);
a4091_save.local_isr->is_Node.ln_Type = NT_INTERRUPT;
/*
* set higher priority so that the test utility can steal interrupts
* from the driver when it is running.
*/
a4091_save.local_isr->is_Node.ln_Pri = A4091_INTPRI + 1;
a4091_save.local_isr->is_Node.ln_Name = "A4091 test";
a4091_save.local_isr->is_Data = &a4091_save;
a4091_save.local_isr->is_Code = (void (*)()) a4091_irq_handler;
if (runtime_flags & FLAG_DEBUG)
printf("my irq handler=%x %x\n",
(uint32_t) &a4091_save, (uint32_t) a4091_save.local_isr);
AddIntServer(A4091_IRQ, a4091_save.local_isr);
}
}
static void
a4091_remove_local_irq_handler(void)
{
if (a4091_save.local_isr != NULL) {
RemIntServer(A4091_IRQ, a4091_save.local_isr);
FreeMem(a4091_save.local_isr, sizeof (*a4091_save.local_isr));
a4091_save.local_isr = NULL;
}
}
#if 0
/*
* Once quick interrupts are enabled in the A4091, they override
* conventional signal interrupts until the controller has been reset.
*/
#include <hardware/custom.h>
#include <hardware/intbits.h>
#define STRX(x) #x
#define STR(x) STRX(x)
/* intenar is one of the Chipset custom registers */
#define cust_intenar 0x00dff01c /* Interrupt enable state */
void
a4091_quick_irq_handler(void)
{
register a4091_save_t *save asm("a1");
save->qintcount++;
}
void a4091_quickint(void);
// asm("
_a4091_quickint:
movem.l a0/d0,-(sp) | Save a0 and d0
move.w "STR(cust_intenar)",d0 | Get interrupt enable state
btst.l #"STR(INTB_INTEN)",d0 | Check if pending disable
beq.s a4091_quickint_exit | Nothing to do
movem.l a1/d1,-(sp) | Save other C ABI registers
lea _a4091_save,a1 | global struct
bsr _a4091_quick_irq_handler
movem.l (sp)+,a1/d1
a4091_quickint_exit:
movem.l (sp)+,a0/d0 | Restore a0 and d0
rte | Return from int
// ");
ULONG intnum = ObtainQuickVector(a4091_quickint);
*ADDR8(a4091_reg_base + A4091_OFFSET_QUICKINT) = intnum;
/* There is no way to release/free a quick interrupt vector?? */
#endif
static char *
GetNodeName(struct Node *node)
{
if (node == NULL)
return ("");
return ((node->ln_Name == NULL) ? "(missing)" : node->ln_Name);
}
static int
a4091_show_or_disable_driver_irq_handler(int disable, int show)
{
struct IntVector *iv = &SysBase->IntVects[A4091_IRQ];
struct List *slist = iv->iv_Data;
struct Interrupt *s;
uint count = 0;
const char *suspend_name = "";
if (EMPTY(slist))
return (0);
Disable();
for (s = FIRST(slist); NEXTINT(s); s = NEXTINT(s)) {
const char *name = GetNodeName((struct Node *) s);
if (runtime_flags & FLAG_IS_A4000T) {
if ((strcmp(name, "NCR SCSI") != 0) &&
(strcmp(name, "A4091 test") != 0))
continue; // No match
} else {
if ((strcmp(name, "NCR SCSI") != 0) &&
(strcmp(name, "A4091 test") != 0) &&
(strcmp(name, "a4091.device") != 0))
continue; // No match
if (((uint32_t) s->is_Code >= 0x00f00000) &&
((uint32_t) s->is_Code <= 0x00ffffff))
continue; // Match, but driver is in Kickstart ROM
}
if (s == a4091_save.local_isr)
continue; /* Don't show or clobber our own ISR handler */
count++;
if (show || (runtime_flags & FLAG_DEBUG)) {
Enable();
printf(" INT%x \"%s\" %08x %08x %08x\n",
A4091_IRQ, name, (uint32_t) s->is_Code,
(uint32_t) s->is_Data, (uint32_t) &s->is_Node);
Disable();
}
if (disable) {
/* Found A4091 SCSI driver's interrupt server -- disable it */
struct Node *node = &s->is_Node;
RemIntServer(A4091_IRQ, (struct Interrupt *) node);
AddHead(&a4091_save.driver_isrs, node);
a4091_save.driver_isr_count++;
suspend_name = name;
}
}
Enable();
if ((count > 0) && disable) {
printf("%s driver IRQ handler%s %s\n",
(disable & 2) ? "Killed" : "Suspended",
(count > 1) ? "s" : "", suspend_name);
}
return (count);
}
static void
a4091_enable_driver_irq_handler(void)
{
if (a4091_save.driver_isr_count > 0) {
struct Node *node;
struct Node *next;
if (runtime_flags & FLAG_DEBUG)