forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy path53c7xx.c
6103 lines (5524 loc) · 191 KB
/
53c7xx.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
/*
* 53c710 driver. Modified from Drew Eckhardts driver
* for 53c810 by Richard Hirst [richard@sleepie.demon.co.uk]
* Check out PERM_OPTIONS and EXPECTED_CLOCK, which may be defined in the
* relevant machine specific file (eg. mvme16x.[ch], amiga7xx.[ch]).
* There are also currently some defines at the top of 53c7xx.scr.
* The chip type is #defined in script_asm.pl, as well as the Makefile.
* Host scsi ID expected to be 7 - see NCR53c7x0_init().
*
* I have removed the PCI code and some of the 53c8xx specific code -
* simply to make this file smaller and easier to manage.
*
* MVME16x issues:
* Problems trying to read any chip registers in NCR53c7x0_init(), as they
* may never have been set by 16xBug (eg. If kernel has come in over tftp).
*/
/*
* Adapted for Linux/m68k Amiga platforms for the A4000T/A4091 and
* WarpEngine SCSI controllers.
* By Alan Hourihane <alanh@fairlite.demon.co.uk>
* Thanks to Richard Hirst for making it possible with the MVME additions
*/
/*
* 53c710 rev 0 doesn't support add with carry. Rev 1 and 2 does. To
* overcome this problem you can define FORCE_DSA_ALIGNMENT, which ensures
* that the DSA address is always xxxxxx00. If disconnection is not allowed,
* then the script only ever tries to add small (< 256) positive offsets to
* DSA, so lack of carry isn't a problem. FORCE_DSA_ALIGNMENT can, of course,
* be defined for all chip revisions at a small cost in memory usage.
*/
#define FORCE_DSA_ALIGNMENT
/*
* Selection timer does not always work on the 53c710, depending on the
* timing at the last disconnect, if this is a problem for you, try
* using validids as detailed below.
*
* Options for the NCR7xx driver
*
* noasync:0 - disables sync and asynchronous negotiation
* nosync:0 - disables synchronous negotiation (does async)
* nodisconnect:0 - disables disconnection
* validids:0x?? - Bitmask field that disallows certain ID's.
* - e.g. 0x03 allows ID 0,1
* - 0x1F allows ID 0,1,2,3,4
* opthi:n - replace top word of options with 'n'
* optlo:n - replace bottom word of options with 'n'
* - ALWAYS SPECIFY opthi THEN optlo <<<<<<<<<<
*/
/*
* PERM_OPTIONS are driver options which will be enabled for all NCR boards
* in the system at driver initialization time.
*
* Don't THINK about touching these in PERM_OPTIONS :
* OPTION_MEMORY_MAPPED
* 680x0 doesn't have an IO map!
*
* OPTION_DEBUG_TEST1
* Test 1 does bus mastering and interrupt tests, which will help weed
* out brain damaged main boards.
*
* Other PERM_OPTIONS settings are listed below. Note the actual options
* required are set in the relevant file (mvme16x.c, amiga7xx.c, etc):
*
* OPTION_NO_ASYNC
* Don't negotiate for asynchronous transfers on the first command
* when OPTION_ALWAYS_SYNCHRONOUS is set. Useful for dain bramaged
* devices which do something bad rather than sending a MESSAGE
* REJECT back to us like they should if they can't cope.
*
* OPTION_SYNCHRONOUS
* Enable support for synchronous transfers. Target negotiated
* synchronous transfers will be responded to. To initiate
* a synchronous transfer request, call
*
* request_synchronous (hostno, target)
*
* from within KGDB.
*
* OPTION_ALWAYS_SYNCHRONOUS
* Negotiate for synchronous transfers with every target after
* driver initialization or a SCSI bus reset. This is a bit dangerous,
* since there are some dain bramaged SCSI devices which will accept
* SDTR messages but keep talking asynchronously.
*
* OPTION_DISCONNECT
* Enable support for disconnect/reconnect. To change the
* default setting on a given host adapter, call
*
* request_disconnect (hostno, allow)
*
* where allow is non-zero to allow, 0 to disallow.
*
* If you really want to run 10MHz FAST SCSI-II transfers, you should
* know that the NCR driver currently ignores parity information. Most
* systems do 5MHz SCSI fine. I've seen a lot that have problems faster
* than 8MHz. To play it safe, we only request 5MHz transfers.
*
* If you'd rather get 10MHz transfers, edit sdtr_message and change
* the fourth byte from 50 to 25.
*/
/*
* Sponsored by
* iX Multiuser Multitasking Magazine
* Hannover, Germany
* hm@ix.de
*
* Copyright 1993, 1994, 1995 Drew Eckhardt
* Visionary Computing
* (Unix and Linux consulting and custom programming)
* drew@PoohSticks.ORG
* +1 (303) 786-7975
*
* TolerANT and SCSI SCRIPTS are registered trademarks of NCR Corporation.
*
* For more information, please consult
*
* NCR53C810
* SCSI I/O Processor
* Programmer's Guide
*
* NCR 53C810
* PCI-SCSI I/O Processor
* Data Manual
*
* NCR 53C810/53C820
* PCI-SCSI I/O Processor Design In Guide
*
* For literature on Symbios Logic Inc. formerly NCR, SCSI,
* and Communication products please call (800) 334-5454 or
* (719) 536-3300.
*
* PCI BIOS Specification Revision
* PCI Local Bus Specification
* PCI System Design Guide
*
* PCI Special Interest Group
* M/S HF3-15A
* 5200 N.E. Elam Young Parkway
* Hillsboro, Oregon 97124-6497
* +1 (503) 696-2000
* +1 (800) 433-5177
*/
/*
* Design issues :
* The cumulative latency needed to propagate a read/write request
* through the file system, buffer cache, driver stacks, SCSI host, and
* SCSI device is ultimately the limiting factor in throughput once we
* have a sufficiently fast host adapter.
*
* So, to maximize performance we want to keep the ratio of latency to data
* transfer time to a minimum by
* 1. Minimizing the total number of commands sent (typical command latency
* including drive and bus mastering host overhead is as high as 4.5ms)
* to transfer a given amount of data.
*
* This is accomplished by placing no arbitrary limit on the number
* of scatter/gather buffers supported, since we can transfer 1K
* per scatter/gather buffer without Eric's cluster patches,
* 4K with.
*
* 2. Minimizing the number of fatal interrupts serviced, since
* fatal interrupts halt the SCSI I/O processor. Basically,
* this means offloading the practical maximum amount of processing
* to the SCSI chip.
*
* On the NCR53c810/820/720, this is accomplished by using
* interrupt-on-the-fly signals when commands complete,
* and only handling fatal errors and SDTR / WDTR messages
* in the host code.
*
* On the NCR53c710, interrupts are generated as on the NCR53c8x0,
* only the lack of a interrupt-on-the-fly facility complicates
* things. Also, SCSI ID registers and commands are
* bit fielded rather than binary encoded.
*
* On the NCR53c700 and NCR53c700-66, operations that are done via
* indirect, table mode on the more advanced chips must be
* replaced by calls through a jump table which
* acts as a surrogate for the DSA. Unfortunately, this
* will mean that we must service an interrupt for each
* disconnect/reconnect.
*
* 3. Eliminating latency by pipelining operations at the different levels.
*
* This driver allows a configurable number of commands to be enqueued
* for each target/lun combination (experimentally, I have discovered
* that two seems to work best) and will ultimately allow for
* SCSI-II tagged queuing.
*
*
* Architecture :
* This driver is built around a Linux queue of commands waiting to
* be executed, and a shared Linux/NCR array of commands to start. Commands
* are transferred to the array by the run_process_issue_queue() function
* which is called whenever a command completes.
*
* As commands are completed, the interrupt routine is triggered,
* looks for commands in the linked list of completed commands with
* valid status, removes these commands from a list of running commands,
* calls the done routine, and flags their target/luns as not busy.
*
* Due to limitations in the intelligence of the NCR chips, certain
* concessions are made. In many cases, it is easier to dynamically
* generate/fix-up code rather than calculate on the NCR at run time.
* So, code is generated or fixed up for
*
* - Handling data transfers, using a variable number of MOVE instructions
* interspersed with CALL MSG_IN, WHEN MSGIN instructions.
*
* The DATAIN and DATAOUT routines are separate, so that an incorrect
* direction can be trapped, and space isn't wasted.
*
* It may turn out that we're better off using some sort
* of table indirect instruction in a loop with a variable
* sized table on the NCR53c710 and newer chips.
*
* - Checking for reselection (NCR53c710 and better)
*
* - Handling the details of SCSI context switches (NCR53c710 and better),
* such as reprogramming appropriate synchronous parameters,
* removing the dsa structure from the NCR's queue of outstanding
* commands, etc.
*
*/
#include <linux/module.h>
#include <linux/config.h>
#include <linux/types.h>
#include <asm/setup.h>
#include <asm/dma.h>
#include <asm/io.h>
#include <asm/system.h>
#include <linux/delay.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include <linux/ioport.h>
#include <linux/time.h>
#include <linux/blkdev.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <asm/pgtable.h>
#ifdef CONFIG_AMIGA
#include <asm/amigahw.h>
#include <asm/amigaints.h>
#include <asm/irq.h>
#define BIG_ENDIAN
#define NO_IO_SPACE
#endif
#ifdef CONFIG_MVME16x
#include <asm/mvme16xhw.h>
#define BIG_ENDIAN
#define NO_IO_SPACE
#define VALID_IDS
#endif
#ifdef CONFIG_BVME6000
#include <asm/bvme6000hw.h>
#define BIG_ENDIAN
#define NO_IO_SPACE
#define VALID_IDS
#endif
#include "scsi.h"
#include <scsi/scsi_dbg.h>
#include <scsi/scsi_host.h>
#include "53c7xx.h"
#include <linux/stat.h>
#include <linux/stddef.h>
#ifdef NO_IO_SPACE
/*
* The following make the definitions in 53c7xx.h (write8, etc) smaller,
* we don't have separate i/o space anyway.
*/
#undef inb
#undef outb
#undef inw
#undef outw
#undef inl
#undef outl
#define inb(x) 1
#define inw(x) 1
#define inl(x) 1
#define outb(x,y) 1
#define outw(x,y) 1
#define outl(x,y) 1
#endif
static int check_address (unsigned long addr, int size);
static void dump_events (struct Scsi_Host *host, int count);
static Scsi_Cmnd * return_outstanding_commands (struct Scsi_Host *host,
int free, int issue);
static void hard_reset (struct Scsi_Host *host);
static void ncr_scsi_reset (struct Scsi_Host *host);
static void print_lots (struct Scsi_Host *host);
static void set_synchronous (struct Scsi_Host *host, int target, int sxfer,
int scntl3, int now_connected);
static int datapath_residual (struct Scsi_Host *host);
static const char * sbcl_to_phase (int sbcl);
static void print_progress (Scsi_Cmnd *cmd);
static void print_queues (struct Scsi_Host *host);
static void process_issue_queue (unsigned long flags);
static int shutdown (struct Scsi_Host *host);
static void abnormal_finished (struct NCR53c7x0_cmd *cmd, int result);
static int disable (struct Scsi_Host *host);
static int NCR53c7xx_run_tests (struct Scsi_Host *host);
static irqreturn_t NCR53c7x0_intr(int irq, void *dev_id, struct pt_regs * regs);
static void NCR53c7x0_intfly (struct Scsi_Host *host);
static int ncr_halt (struct Scsi_Host *host);
static void intr_phase_mismatch (struct Scsi_Host *host, struct NCR53c7x0_cmd
*cmd);
static void intr_dma (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd);
static void print_dsa (struct Scsi_Host *host, u32 *dsa,
const char *prefix);
static int print_insn (struct Scsi_Host *host, const u32 *insn,
const char *prefix, int kernel);
static void NCR53c7xx_dsa_fixup (struct NCR53c7x0_cmd *cmd);
static void NCR53c7x0_init_fixup (struct Scsi_Host *host);
static int NCR53c7x0_dstat_sir_intr (struct Scsi_Host *host, struct
NCR53c7x0_cmd *cmd);
static void NCR53c7x0_soft_reset (struct Scsi_Host *host);
/* Size of event list (per host adapter) */
static int track_events = 0;
static struct Scsi_Host *first_host = NULL; /* Head of list of NCR boards */
static Scsi_Host_Template *the_template = NULL;
/* NCR53c710 script handling code */
#include "53c7xx_d.h"
#ifdef A_int_debug_sync
#define DEBUG_SYNC_INTR A_int_debug_sync
#endif
int NCR53c7xx_script_len = sizeof (SCRIPT);
int NCR53c7xx_dsa_len = A_dsa_end + Ent_dsa_zero - Ent_dsa_code_template;
#ifdef FORCE_DSA_ALIGNMENT
int CmdPageStart = (0 - Ent_dsa_zero - sizeof(struct NCR53c7x0_cmd)) & 0xff;
#endif
static char *setup_strings[] =
{"","","","","","","",""};
#define MAX_SETUP_STRINGS (sizeof(setup_strings) / sizeof(char *))
#define SETUP_BUFFER_SIZE 200
static char setup_buffer[SETUP_BUFFER_SIZE];
static char setup_used[MAX_SETUP_STRINGS];
void ncr53c7xx_setup (char *str, int *ints)
{
int i;
char *p1, *p2;
p1 = setup_buffer;
*p1 = '\0';
if (str)
strncpy(p1, str, SETUP_BUFFER_SIZE - strlen(setup_buffer));
setup_buffer[SETUP_BUFFER_SIZE - 1] = '\0';
p1 = setup_buffer;
i = 0;
while (*p1 && (i < MAX_SETUP_STRINGS)) {
p2 = strchr(p1, ',');
if (p2) {
*p2 = '\0';
if (p1 != p2)
setup_strings[i] = p1;
p1 = p2 + 1;
i++;
}
else {
setup_strings[i] = p1;
break;
}
}
for (i=0; i<MAX_SETUP_STRINGS; i++)
setup_used[i] = 0;
}
/* check_setup_strings() returns index if key found, 0 if not
*/
static int check_setup_strings(char *key, int *flags, int *val, char *buf)
{
int x;
char *cp;
for (x=0; x<MAX_SETUP_STRINGS; x++) {
if (setup_used[x])
continue;
if (!strncmp(setup_strings[x], key, strlen(key)))
break;
if (!strncmp(setup_strings[x], "next", strlen("next")))
return 0;
}
if (x == MAX_SETUP_STRINGS)
return 0;
setup_used[x] = 1;
cp = setup_strings[x] + strlen(key);
*val = -1;
if (*cp != ':')
return ++x;
cp++;
if ((*cp >= '0') && (*cp <= '9')) {
*val = simple_strtoul(cp,NULL,0);
}
return ++x;
}
/*
* KNOWN BUGS :
* - There is some sort of conflict when the PPP driver is compiled with
* support for 16 channels?
*
* - On systems which predate the 1.3.x initialization order change,
* the NCR driver will cause Cannot get free page messages to appear.
* These are harmless, but I don't know of an easy way to avoid them.
*
* - With OPTION_DISCONNECT, on two systems under unknown circumstances,
* we get a PHASE MISMATCH with DSA set to zero (suggests that we
* are occurring somewhere in the reselection code) where
* DSP=some value DCMD|DBC=same value.
*
* Closer inspection suggests that we may be trying to execute
* some portion of the DSA?
* scsi0 : handling residual transfer (+ 0 bytes from DMA FIFO)
* scsi0 : handling residual transfer (+ 0 bytes from DMA FIFO)
* scsi0 : no current command : unexpected phase MSGIN.
* DSP=0x1c46cc, DCMD|DBC=0x1c46ac, DSA=0x0
* DSPS=0x0, TEMP=0x1c3e70, DMODE=0x80
* scsi0 : DSP->
* 001c46cc : 0x001c46cc 0x00000000
* 001c46d4 : 0x001c5ea0 0x000011f8
*
* Changed the print code in the phase_mismatch handler so
* that we call print_lots to try to diagnose this.
*
*/
/*
* Possible future direction of architecture for max performance :
*
* We're using a single start array for the NCR chip. This is
* sub-optimal, because we cannot add a command which would conflict with
* an executing command to this start queue, and therefore must insert the
* next command for a given I/T/L combination after the first has completed;
* incurring our interrupt latency between SCSI commands.
*
* To allow further pipelining of the NCR and host CPU operation, we want
* to set things up so that immediately on termination of a command destined
* for a given LUN, we get that LUN busy again.
*
* To do this, we need to add a 32 bit pointer to which is jumped to
* on completion of a command. If no new command is available, this
* would point to the usual DSA issue queue select routine.
*
* If one were, it would point to a per-NCR53c7x0_cmd select routine
* which starts execution immediately, inserting the command at the head
* of the start queue if the NCR chip is selected or reselected.
*
* We would change so that we keep a list of outstanding commands
* for each unit, rather than a single running_list. We'd insert
* a new command into the right running list; if the NCR didn't
* have something running for that yet, we'd put it in the
* start queue as well. Some magic needs to happen to handle the
* race condition between the first command terminating before the
* new one is written.
*
* Potential for profiling :
* Call do_gettimeofday(struct timeval *tv) to get 800ns resolution.
*/
/*
* TODO :
* 1. To support WIDE transfers, not much needs to happen. We
* should do CHMOVE instructions instead of MOVEs when
* we have scatter/gather segments of uneven length. When
* we do this, we need to handle the case where we disconnect
* between segments.
*
* 2. Currently, when Icky things happen we do a FATAL(). Instead,
* we want to do an integrity check on the parts of the NCR hostdata
* structure which were initialized at boot time; FATAL() if that
* fails, and otherwise try to recover. Keep track of how many
* times this has happened within a single SCSI command; if it
* gets excessive, then FATAL().
*
* 3. Parity checking is currently disabled, and a few things should
* happen here now that we support synchronous SCSI transfers :
* 1. On soft-reset, we shoould set the EPC (Enable Parity Checking)
* and AAP (Assert SATN/ on parity error) bits in SCNTL0.
*
* 2. We should enable the parity interrupt in the SIEN0 register.
*
* 3. intr_phase_mismatch() needs to believe that message out is
* always an "acceptable" phase to have a mismatch in. If
* the old phase was MSG_IN, we should send a MESSAGE PARITY
* error. If the old phase was something else, we should send
* a INITIATOR_DETECTED_ERROR message. Note that this could
* cause a RESTORE POINTERS message; so we should handle that
* correctly first. Instead, we should probably do an
* initiator_abort.
*
* 4. MPEE bit of CTEST4 should be set so we get interrupted if
* we detect an error.
*
*
* 5. The initial code has been tested on the NCR53c810. I don't
* have access to NCR53c700, 700-66 (Forex boards), NCR53c710
* (NCR Pentium systems), NCR53c720, NCR53c820, or NCR53c825 boards to
* finish development on those platforms.
*
* NCR53c820/825/720 - need to add wide transfer support, including WDTR
* negotiation, programming of wide transfer capabilities
* on reselection and table indirect selection.
*
* NCR53c710 - need to add fatal interrupt or GEN code for
* command completion signaling. Need to modify all
* SDID, SCID, etc. registers, and table indirect select code
* since these use bit fielded (ie 1<<target) instead of
* binary encoded target ids. Need to accommodate
* different register mappings, probably scan through
* the SCRIPT code and change the non SFBR register operand
* of all MOVE instructions.
*
* It is rather worse than this actually, the 710 corrupts
* both TEMP and DSA when you do a MOVE MEMORY. This
* screws you up all over the place. MOVE MEMORY 4 with a
* destination of DSA seems to work OK, which helps some.
* Richard Hirst richard@sleepie.demon.co.uk
*
* NCR53c700/700-66 - need to add code to refix addresses on
* every nexus change, eliminate all table indirect code,
* very messy.
*
* 6. The NCR53c7x0 series is very popular on other platforms that
* could be running Linux - ie, some high performance AMIGA SCSI
* boards use it.
*
* So, I should include #ifdef'd code so that it is
* compatible with these systems.
*
* Specifically, the little Endian assumptions I made in my
* bit fields need to change, and if the NCR doesn't see memory
* the right way, we need to provide options to reverse words
* when the scripts are relocated.
*
* 7. Use vremap() to access memory mapped boards.
*/
/*
* Allow for simultaneous existence of multiple SCSI scripts so we
* can have a single driver binary for all of the family.
*
* - one for NCR53c700 and NCR53c700-66 chips (not yet supported)
* - one for rest (only the NCR53c810, 815, 820, and 825 are currently
* supported)
*
* So that we only need two SCSI scripts, we need to modify things so
* that we fixup register accesses in READ/WRITE instructions, and
* we'll also have to accommodate the bit vs. binary encoding of IDs
* with the 7xx chips.
*/
#define ROUNDUP(adr,type) \
((void *) (((long) (adr) + sizeof(type) - 1) & ~(sizeof(type) - 1)))
/*
* Function: issue_to_cmd
*
* Purpose: convert jump instruction in issue array to NCR53c7x0_cmd
* structure pointer.
*
* Inputs; issue - pointer to start of NOP or JUMP instruction
* in issue array.
*
* Returns: pointer to command on success; 0 if opcode is NOP.
*/
static inline struct NCR53c7x0_cmd *
issue_to_cmd (struct Scsi_Host *host, struct NCR53c7x0_hostdata *hostdata,
u32 *issue)
{
return (issue[0] != hostdata->NOP_insn) ?
/*
* If the IF TRUE bit is set, it's a JUMP instruction. The
* operand is a bus pointer to the dsa_begin routine for this DSA. The
* dsa field of the NCR53c7x0_cmd structure starts with the
* DSA code template. By converting to a virtual address,
* subtracting the code template size, and offset of the
* dsa field, we end up with a pointer to the start of the
* structure (alternatively, we could use the
* dsa_cmnd field, an anachronism from when we weren't
* sure what the relationship between the NCR structures
* and host structures were going to be.
*/
(struct NCR53c7x0_cmd *) ((char *) bus_to_virt (issue[1]) -
(hostdata->E_dsa_code_begin - hostdata->E_dsa_code_template) -
offsetof(struct NCR53c7x0_cmd, dsa))
/* If the IF TRUE bit is not set, it's a NOP */
: NULL;
}
/*
* FIXME: we should junk these, in favor of synchronous_want and
* wide_want in the NCR53c7x0_hostdata structure.
*/
/* Template for "preferred" synchronous transfer parameters. */
static const unsigned char sdtr_message[] = {
#ifdef CONFIG_SCSI_NCR53C7xx_FAST
EXTENDED_MESSAGE, 3 /* length */, EXTENDED_SDTR, 25 /* *4ns */, 8 /* off */
#else
EXTENDED_MESSAGE, 3 /* length */, EXTENDED_SDTR, 50 /* *4ns */, 8 /* off */
#endif
};
/* Template to request asynchronous transfers */
static const unsigned char async_message[] = {
EXTENDED_MESSAGE, 3 /* length */, EXTENDED_SDTR, 0, 0 /* asynchronous */
};
/* Template for "preferred" WIDE transfer parameters */
static const unsigned char wdtr_message[] = {
EXTENDED_MESSAGE, 2 /* length */, EXTENDED_WDTR, 1 /* 2^1 bytes */
};
#if 0
/*
* Function : struct Scsi_Host *find_host (int host)
*
* Purpose : KGDB support function which translates a host number
* to a host structure.
*
* Inputs : host - number of SCSI host
*
* Returns : NULL on failure, pointer to host structure on success.
*/
static struct Scsi_Host *
find_host (int host) {
struct Scsi_Host *h;
for (h = first_host; h && h->host_no != host; h = h->next);
if (!h) {
printk (KERN_ALERT "scsi%d not found\n", host);
return NULL;
} else if (h->hostt != the_template) {
printk (KERN_ALERT "scsi%d is not a NCR board\n", host);
return NULL;
}
return h;
}
#if 0
/*
* Function : request_synchronous (int host, int target)
*
* Purpose : KGDB interface which will allow us to negotiate for
* synchronous transfers. This ill be replaced with a more
* integrated function; perhaps a new entry in the scsi_host
* structure, accessible via an ioctl() or perhaps /proc/scsi.
*
* Inputs : host - number of SCSI host; target - number of target.
*
* Returns : 0 when negotiation has been setup for next SCSI command,
* -1 on failure.
*/
static int
request_synchronous (int host, int target) {
struct Scsi_Host *h;
struct NCR53c7x0_hostdata *hostdata;
unsigned long flags;
if (target < 0) {
printk (KERN_ALERT "target %d is bogus\n", target);
return -1;
}
if (!(h = find_host (host)))
return -1;
else if (h->this_id == target) {
printk (KERN_ALERT "target %d is host ID\n", target);
return -1;
}
else if (target > h->max_id) {
printk (KERN_ALERT "target %d exceeds maximum of %d\n", target,
h->max_id);
return -1;
}
hostdata = (struct NCR53c7x0_hostdata *)h->hostdata[0];
local_irq_save(flags);
if (hostdata->initiate_sdtr & (1 << target)) {
local_irq_restore(flags);
printk (KERN_ALERT "target %d already doing SDTR\n", target);
return -1;
}
hostdata->initiate_sdtr |= (1 << target);
local_irq_restore(flags);
return 0;
}
#endif
/*
* Function : request_disconnect (int host, int on_or_off)
*
* Purpose : KGDB support function, tells us to allow or disallow
* disconnections.
*
* Inputs : host - number of SCSI host; on_or_off - non-zero to allow,
* zero to disallow.
*
* Returns : 0 on success, * -1 on failure.
*/
static int
request_disconnect (int host, int on_or_off) {
struct Scsi_Host *h;
struct NCR53c7x0_hostdata *hostdata;
if (!(h = find_host (host)))
return -1;
hostdata = (struct NCR53c7x0_hostdata *) h->hostdata[0];
if (on_or_off)
hostdata->options |= OPTION_DISCONNECT;
else
hostdata->options &= ~OPTION_DISCONNECT;
return 0;
}
#endif
/*
* Function : static void NCR53c7x0_driver_init (struct Scsi_Host *host)
*
* Purpose : Initialize internal structures, as required on startup, or
* after a SCSI bus reset.
*
* Inputs : host - pointer to this host adapter's structure
*/
static void
NCR53c7x0_driver_init (struct Scsi_Host *host) {
struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
host->hostdata[0];
int i, j;
u32 *ncrcurrent;
for (i = 0; i < 16; ++i) {
hostdata->request_sense[i] = 0;
for (j = 0; j < 8; ++j)
hostdata->busy[i][j] = 0;
set_synchronous (host, i, /* sxfer */ 0, hostdata->saved_scntl3, 0);
}
hostdata->issue_queue = NULL;
hostdata->running_list = hostdata->finished_queue =
hostdata->ncrcurrent = NULL;
for (i = 0, ncrcurrent = (u32 *) hostdata->schedule;
i < host->can_queue; ++i, ncrcurrent += 2) {
ncrcurrent[0] = hostdata->NOP_insn;
ncrcurrent[1] = 0xdeadbeef;
}
ncrcurrent[0] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) << 24) | DBC_TCI_TRUE;
ncrcurrent[1] = (u32) virt_to_bus (hostdata->script) +
hostdata->E_wait_reselect;
hostdata->reconnect_dsa_head = 0;
hostdata->addr_reconnect_dsa_head = (u32)
virt_to_bus((void *) &(hostdata->reconnect_dsa_head));
hostdata->expecting_iid = 0;
hostdata->expecting_sto = 0;
if (hostdata->options & OPTION_ALWAYS_SYNCHRONOUS)
hostdata->initiate_sdtr = 0xffff;
else
hostdata->initiate_sdtr = 0;
hostdata->talked_to = 0;
hostdata->idle = 1;
}
/*
* Function : static int clock_to_ccf_710 (int clock)
*
* Purpose : Return the clock conversion factor for a given SCSI clock.
*
* Inputs : clock - SCSI clock expressed in Hz.
*
* Returns : ccf on success, -1 on failure.
*/
static int
clock_to_ccf_710 (int clock) {
if (clock <= 16666666)
return -1;
if (clock <= 25000000)
return 2; /* Divide by 1.0 */
else if (clock <= 37500000)
return 1; /* Divide by 1.5 */
else if (clock <= 50000000)
return 0; /* Divide by 2.0 */
else if (clock <= 66000000)
return 3; /* Divide by 3.0 */
else
return -1;
}
/*
* Function : static int NCR53c7x0_init (struct Scsi_Host *host)
*
* Purpose : initialize the internal structures for a given SCSI host
*
* Inputs : host - pointer to this host adapter's structure
*
* Preconditions : when this function is called, the chip_type
* field of the hostdata structure MUST have been set.
*
* Returns : 0 on success, -1 on failure.
*/
int
NCR53c7x0_init (struct Scsi_Host *host) {
NCR53c7x0_local_declare();
int i, ccf;
unsigned char revision;
struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
host->hostdata[0];
/*
* There are some things which we need to know about in order to provide
* a semblance of support. Print 'em if they aren't what we expect,
* otherwise don't add to the noise.
*
* -1 means we don't know what to expect.
*/
int val, flags;
char buf[32];
int expected_id = -1;
int expected_clock = -1;
int uninitialized = 0;
#ifdef NO_IO_SPACE
int expected_mapping = OPTION_MEMORY_MAPPED;
#else
int expected_mapping = OPTION_IO_MAPPED;
#endif
for (i=0;i<7;i++)
hostdata->valid_ids[i] = 1; /* Default all ID's to scan */
/* Parse commandline flags */
if (check_setup_strings("noasync",&flags,&val,buf))
{
hostdata->options |= OPTION_NO_ASYNC;
hostdata->options &= ~(OPTION_SYNCHRONOUS | OPTION_ALWAYS_SYNCHRONOUS);
}
if (check_setup_strings("nosync",&flags,&val,buf))
{
hostdata->options &= ~(OPTION_SYNCHRONOUS | OPTION_ALWAYS_SYNCHRONOUS);
}
if (check_setup_strings("nodisconnect",&flags,&val,buf))
hostdata->options &= ~OPTION_DISCONNECT;
if (check_setup_strings("validids",&flags,&val,buf))
{
for (i=0;i<7;i++)
hostdata->valid_ids[i] = val & (1<<i);
}
if ((i = check_setup_strings("next",&flags,&val,buf)))
{
while (i)
setup_used[--i] = 1;
}
if (check_setup_strings("opthi",&flags,&val,buf))
hostdata->options = (long long)val << 32;
if (check_setup_strings("optlo",&flags,&val,buf))
hostdata->options |= val;
NCR53c7x0_local_setup(host);
switch (hostdata->chip) {
case 710:
case 770:
hostdata->dstat_sir_intr = NCR53c7x0_dstat_sir_intr;
hostdata->init_save_regs = NULL;
hostdata->dsa_fixup = NCR53c7xx_dsa_fixup;
hostdata->init_fixup = NCR53c7x0_init_fixup;
hostdata->soft_reset = NCR53c7x0_soft_reset;
hostdata->run_tests = NCR53c7xx_run_tests;
expected_clock = hostdata->scsi_clock;
expected_id = 7;
break;
default:
printk ("scsi%d : chip type of %d is not supported yet, detaching.\n",
host->host_no, hostdata->chip);
scsi_unregister (host);
return -1;
}
/* Assign constants accessed by NCR */
hostdata->NCR53c7xx_zero = 0;
hostdata->NCR53c7xx_msg_reject = MESSAGE_REJECT;
hostdata->NCR53c7xx_msg_abort = ABORT;
hostdata->NCR53c7xx_msg_nop = NOP;
hostdata->NOP_insn = (DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) << 24;
if (expected_mapping == -1 ||
(hostdata->options & (OPTION_MEMORY_MAPPED)) !=
(expected_mapping & OPTION_MEMORY_MAPPED))
printk ("scsi%d : using %s mapped access\n", host->host_no,
(hostdata->options & OPTION_MEMORY_MAPPED) ? "memory" :
"io");
hostdata->dmode = (hostdata->chip == 700 || hostdata->chip == 70066) ?
DMODE_REG_00 : DMODE_REG_10;
hostdata->istat = ((hostdata->chip / 100) == 8) ?
ISTAT_REG_800 : ISTAT_REG_700;
/* We have to assume that this may be the first access to the chip, so
* we must set EA in DCNTL. */
NCR53c7x0_write8 (DCNTL_REG, DCNTL_10_EA|DCNTL_10_COM);
/* Only the ISTAT register is readable when the NCR is running, so make
sure it's halted. */
ncr_halt(host);
/*
* XXX - the NCR53c700 uses bitfielded registers for SCID, SDID, etc,
* as does the 710 with one bit per SCSI ID. Conversely, the NCR
* uses a normal, 3 bit binary representation of these values.
*
* Get the rest of the NCR documentation, and FIND OUT where the change
* was.
*/
#if 0
/* May not be able to do this - chip my not have been set up yet */
tmp = hostdata->this_id_mask = NCR53c7x0_read8(SCID_REG);
for (host->this_id = 0; tmp != 1; tmp >>=1, ++host->this_id);
#else
host->this_id = 7;
#endif
/*
* Note : we should never encounter a board setup for ID0. So,
* if we see ID0, assume that it was uninitialized and set it
* to the industry standard 7.
*/
if (!host->this_id) {
printk("scsi%d : initiator ID was %d, changing to 7\n",
host->host_no, host->this_id);
host->this_id = 7;
hostdata->this_id_mask = 1 << 7;
uninitialized = 1;
};
if (expected_id == -1 || host->this_id != expected_id)
printk("scsi%d : using initiator ID %d\n", host->host_no,
host->this_id);
/*
* Save important registers to allow a soft reset.
*/
/*
* CTEST7 controls cache snooping, burst mode, and support for
* external differential drivers. This isn't currently used - the
* default value may not be optimal anyway.
* Even worse, it may never have been set up since reset.
*/
hostdata->saved_ctest7 = NCR53c7x0_read8(CTEST7_REG) & CTEST7_SAVE;
revision = (NCR53c7x0_read8(CTEST8_REG) & 0xF0) >> 4;
switch (revision) {
case 1: revision = 0; break;
case 2: revision = 1; break;
case 4: revision = 2; break;
case 8: revision = 3; break;
default: revision = 255; break;