forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathatari_NCR5380.c
2986 lines (2667 loc) · 90.4 KB
/
atari_NCR5380.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
/*
* NCR 5380 generic driver routines. These should make it *trivial*
* to implement 5380 SCSI drivers under Linux with a non-trantor
* architecture.
*
* Note that these routines also work with NR53c400 family chips.
*
* Copyright 1993, Drew Eckhardt
* Visionary Computing
* (Unix and Linux consulting and custom programming)
* +1 (303) 666-5836
*
* DISTRIBUTION RELEASE 6.
*
* For more information, please consult
*
* NCR 5380 Family
* SCSI Protocol Controller
* Databook
*
* NCR Microelectronics
* 1635 Aeroplaza Drive
* Colorado Springs, CO 80916
* 1+ (719) 578-3400
* 1+ (800) 334-5454
*/
/*
* ++roman: To port the 5380 driver to the Atari, I had to do some changes in
* this file, too:
*
* - Some of the debug statements were incorrect (undefined variables and the
* like). I fixed that.
*
* - In information_transfer(), I think a #ifdef was wrong. Looking at the
* possible DMA transfer size should also happen for REAL_DMA. I added this
* in the #if statement.
*
* - When using real DMA, information_transfer() should return in a DATAOUT
* phase after starting the DMA. It has nothing more to do.
*
* - The interrupt service routine should run main after end of DMA, too (not
* only after RESELECTION interrupts). Additionally, it should _not_ test
* for more interrupts after running main, since a DMA process may have
* been started and interrupts are turned on now. The new int could happen
* inside the execution of NCR5380_intr(), leading to recursive
* calls.
*
* - I've added a function merge_contiguous_buffers() that tries to
* merge scatter-gather buffers that are located at contiguous
* physical addresses and can be processed with the same DMA setup.
* Since most scatter-gather operations work on a page (4K) of
* 4 buffers (1K), in more than 90% of all cases three interrupts and
* DMA setup actions are saved.
*
* - I've deleted all the stuff for AUTOPROBE_IRQ, REAL_DMA_POLL, PSEUDO_DMA
* and USLEEP, because these were messing up readability and will never be
* needed for Atari SCSI.
*
* - I've revised the NCR5380_main() calling scheme (relax the 'main_running'
* stuff), and 'main' is executed in a bottom half if awoken by an
* interrupt.
*
* - The code was quite cluttered up by "#if (NDEBUG & NDEBUG_*) printk..."
* constructs. In my eyes, this made the source rather unreadable, so I
* finally replaced that by the *_PRINTK() macros.
*
*/
/*
* Further development / testing that should be done :
* 1. Test linked command handling code after Eric is ready with
* the high level code.
*/
#include <scsi/scsi_dbg.h>
#include <scsi/scsi_transport_spi.h>
#if (NDEBUG & NDEBUG_LISTS)
#define LIST(x, y) \
do { \
printk("LINE:%d Adding %p to %p\n", \
__LINE__, (void*)(x), (void*)(y)); \
if ((x) == (y)) \
udelay(5); \
} while (0)
#define REMOVE(w, x, y, z) \
do { \
printk("LINE:%d Removing: %p->%p %p->%p \n", \
__LINE__, (void*)(w), (void*)(x), \
(void*)(y), (void*)(z)); \
if ((x) == (y)) \
udelay(5); \
} while (0)
#else
#define LIST(x,y)
#define REMOVE(w,x,y,z)
#endif
#ifndef notyet
#undef LINKED
#endif
/*
* Design
* Issues :
*
* The other Linux SCSI drivers were written when Linux was Intel PC-only,
* and specifically for each board rather than each chip. This makes their
* adaptation to platforms like the Mac (Some of which use NCR5380's)
* more difficult than it has to be.
*
* Also, many of the SCSI drivers were written before the command queuing
* routines were implemented, meaning their implementations of queued
* commands were hacked on rather than designed in from the start.
*
* When I designed the Linux SCSI drivers I figured that
* while having two different SCSI boards in a system might be useful
* for debugging things, two of the same type wouldn't be used.
* Well, I was wrong and a number of users have mailed me about running
* multiple high-performance SCSI boards in a server.
*
* Finally, when I get questions from users, I have no idea what
* revision of my driver they are running.
*
* This driver attempts to address these problems :
* This is a generic 5380 driver. To use it on a different platform,
* one simply writes appropriate system specific macros (ie, data
* transfer - some PC's will use the I/O bus, 68K's must use
* memory mapped) and drops this file in their 'C' wrapper.
*
* As far as command queueing, two queues are maintained for
* each 5380 in the system - commands that haven't been issued yet,
* and commands that are currently executing. This means that an
* unlimited number of commands may be queued, letting
* more commands propagate from the higher driver levels giving higher
* throughput. Note that both I_T_L and I_T_L_Q nexuses are supported,
* allowing multiple commands to propagate all the way to a SCSI-II device
* while a command is already executing.
*
* To solve the multiple-boards-in-the-same-system problem,
* there is a separate instance structure for each instance
* of a 5380 in the system. So, multiple NCR5380 drivers will
* be able to coexist with appropriate changes to the high level
* SCSI code.
*
* A NCR5380_PUBLIC_REVISION macro is provided, with the release
* number (updated for each public release) printed by the
* NCR5380_print_options command, which should be called from the
* wrapper detect function, so that I know what release of the driver
* users are using.
*
* Issues specific to the NCR5380 :
*
* When used in a PIO or pseudo-dma mode, the NCR5380 is a braindead
* piece of hardware that requires you to sit in a loop polling for
* the REQ signal as long as you are connected. Some devices are
* brain dead (ie, many TEXEL CD ROM drives) and won't disconnect
* while doing long seek operations.
*
* The workaround for this is to keep track of devices that have
* disconnected. If the device hasn't disconnected, for commands that
* should disconnect, we do something like
*
* while (!REQ is asserted) { sleep for N usecs; poll for M usecs }
*
* Some tweaking of N and M needs to be done. An algorithm based
* on "time to data" would give the best results as long as short time
* to datas (ie, on the same track) were considered, however these
* broken devices are the exception rather than the rule and I'd rather
* spend my time optimizing for the normal case.
*
* Architecture :
*
* At the heart of the design is a coroutine, NCR5380_main,
* which is started when not running by the interrupt handler,
* timer, and queue command function. It attempts to establish
* I_T_L or I_T_L_Q nexuses by removing the commands from the
* issue queue and calling NCR5380_select() if a nexus
* is not established.
*
* Once a nexus is established, the NCR5380_information_transfer()
* phase goes through the various phases as instructed by the target.
* if the target goes into MSG IN and sends a DISCONNECT message,
* the command structure is placed into the per instance disconnected
* queue, and NCR5380_main tries to find more work. If USLEEP
* was defined, and the target is idle for too long, the system
* will try to sleep.
*
* If a command has disconnected, eventually an interrupt will trigger,
* calling NCR5380_intr() which will in turn call NCR5380_reselect
* to reestablish a nexus. This will run main if necessary.
*
* On command termination, the done function will be called as
* appropriate.
*
* SCSI pointers are maintained in the SCp field of SCSI command
* structures, being initialized after the command is connected
* in NCR5380_select, and set as appropriate in NCR5380_information_transfer.
* Note that in violation of the standard, an implicit SAVE POINTERS operation
* is done, since some BROKEN disks fail to issue an explicit SAVE POINTERS.
*/
/*
* Using this file :
* This file a skeleton Linux SCSI driver for the NCR 5380 series
* of chips. To use it, you write an architecture specific functions
* and macros and include this file in your driver.
*
* These macros control options :
* AUTOSENSE - if defined, REQUEST SENSE will be performed automatically
* for commands that return with a CHECK CONDITION status.
*
* LINKED - if defined, linked commands are supported.
*
* REAL_DMA - if defined, REAL DMA is used during the data transfer phases.
*
* SUPPORT_TAGS - if defined, SCSI-2 tagged queuing is used where possible
*
* These macros MUST be defined :
*
* NCR5380_read(register) - read from the specified register
*
* NCR5380_write(register, value) - write to the specific register
*
* Either real DMA *or* pseudo DMA may be implemented
* REAL functions :
* NCR5380_REAL_DMA should be defined if real DMA is to be used.
* Note that the DMA setup functions should return the number of bytes
* that they were able to program the controller for.
*
* Also note that generic i386/PC versions of these macros are
* available as NCR5380_i386_dma_write_setup,
* NCR5380_i386_dma_read_setup, and NCR5380_i386_dma_residual.
*
* NCR5380_dma_write_setup(instance, src, count) - initialize
* NCR5380_dma_read_setup(instance, dst, count) - initialize
* NCR5380_dma_residual(instance); - residual count
*
* PSEUDO functions :
* NCR5380_pwrite(instance, src, count)
* NCR5380_pread(instance, dst, count);
*
* If nothing specific to this implementation needs doing (ie, with external
* hardware), you must also define
*
* NCR5380_queue_command
* NCR5380_reset
* NCR5380_abort
* NCR5380_proc_info
*
* to be the global entry points into the specific driver, ie
* #define NCR5380_queue_command t128_queue_command.
*
* If this is not done, the routines will be defined as static functions
* with the NCR5380* names and the user must provide a globally
* accessible wrapper function.
*
* The generic driver is initialized by calling NCR5380_init(instance),
* after setting the appropriate host specific fields and ID. If the
* driver wishes to autoprobe for an IRQ line, the NCR5380_probe_irq(instance,
* possible) function may be used. Before the specific driver initialization
* code finishes, NCR5380_print_options should be called.
*/
static struct Scsi_Host *first_instance = NULL;
static struct scsi_host_template *the_template = NULL;
/* Macros ease life... :-) */
#define SETUP_HOSTDATA(in) \
struct NCR5380_hostdata *hostdata = \
(struct NCR5380_hostdata *)(in)->hostdata
#define HOSTDATA(in) ((struct NCR5380_hostdata *)(in)->hostdata)
#define NEXT(cmd) ((Scsi_Cmnd *)(cmd)->host_scribble)
#define SET_NEXT(cmd,next) ((cmd)->host_scribble = (void *)(next))
#define NEXTADDR(cmd) ((Scsi_Cmnd **)&(cmd)->host_scribble)
#define HOSTNO instance->host_no
#define H_NO(cmd) (cmd)->device->host->host_no
#ifdef SUPPORT_TAGS
/*
* Functions for handling tagged queuing
* =====================================
*
* ++roman (01/96): Now I've implemented SCSI-2 tagged queuing. Some notes:
*
* Using consecutive numbers for the tags is no good idea in my eyes. There
* could be wrong re-usings if the counter (8 bit!) wraps and some early
* command has been preempted for a long time. My solution: a bitfield for
* remembering used tags.
*
* There's also the problem that each target has a certain queue size, but we
* cannot know it in advance :-( We just see a QUEUE_FULL status being
* returned. So, in this case, the driver internal queue size assumption is
* reduced to the number of active tags if QUEUE_FULL is returned by the
* target. The command is returned to the mid-level, but with status changed
* to BUSY, since --as I've seen-- the mid-level can't handle QUEUE_FULL
* correctly.
*
* We're also not allowed running tagged commands as long as an untagged
* command is active. And REQUEST SENSE commands after a contingent allegiance
* condition _must_ be untagged. To keep track whether an untagged command has
* been issued, the host->busy array is still employed, as it is without
* support for tagged queuing.
*
* One could suspect that there are possible race conditions between
* is_lun_busy(), cmd_get_tag() and cmd_free_tag(). But I think this isn't the
* case: is_lun_busy() and cmd_get_tag() are both called from NCR5380_main(),
* which already guaranteed to be running at most once. It is also the only
* place where tags/LUNs are allocated. So no other allocation can slip
* between that pair, there could only happen a reselection, which can free a
* tag, but that doesn't hurt. Only the sequence in cmd_free_tag() becomes
* important: the tag bit must be cleared before 'nr_allocated' is decreased.
*/
/* -1 for TAG_NONE is not possible with unsigned char cmd->tag */
#undef TAG_NONE
#define TAG_NONE 0xff
typedef struct {
DECLARE_BITMAP(allocated, MAX_TAGS);
int nr_allocated;
int queue_size;
} TAG_ALLOC;
static TAG_ALLOC TagAlloc[8][8]; /* 8 targets and 8 LUNs */
static void __init init_tags(void)
{
int target, lun;
TAG_ALLOC *ta;
if (!setup_use_tagged_queuing)
return;
for (target = 0; target < 8; ++target) {
for (lun = 0; lun < 8; ++lun) {
ta = &TagAlloc[target][lun];
bitmap_zero(ta->allocated, MAX_TAGS);
ta->nr_allocated = 0;
/* At the beginning, assume the maximum queue size we could
* support (MAX_TAGS). This value will be decreased if the target
* returns QUEUE_FULL status.
*/
ta->queue_size = MAX_TAGS;
}
}
}
/* Check if we can issue a command to this LUN: First see if the LUN is marked
* busy by an untagged command. If the command should use tagged queuing, also
* check that there is a free tag and the target's queue won't overflow. This
* function should be called with interrupts disabled to avoid race
* conditions.
*/
static int is_lun_busy(Scsi_Cmnd *cmd, int should_be_tagged)
{
SETUP_HOSTDATA(cmd->device->host);
if (hostdata->busy[cmd->device->id] & (1 << cmd->device->lun))
return 1;
if (!should_be_tagged ||
!setup_use_tagged_queuing || !cmd->device->tagged_supported)
return 0;
if (TagAlloc[cmd->device->id][cmd->device->lun].nr_allocated >=
TagAlloc[cmd->device->id][cmd->device->lun].queue_size) {
TAG_PRINTK("scsi%d: target %d lun %d: no free tags\n",
H_NO(cmd), cmd->device->id, cmd->device->lun);
return 1;
}
return 0;
}
/* Allocate a tag for a command (there are no checks anymore, check_lun_busy()
* must be called before!), or reserve the LUN in 'busy' if the command is
* untagged.
*/
static void cmd_get_tag(Scsi_Cmnd *cmd, int should_be_tagged)
{
SETUP_HOSTDATA(cmd->device->host);
/* If we or the target don't support tagged queuing, allocate the LUN for
* an untagged command.
*/
if (!should_be_tagged ||
!setup_use_tagged_queuing || !cmd->device->tagged_supported) {
cmd->tag = TAG_NONE;
hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun);
TAG_PRINTK("scsi%d: target %d lun %d now allocated by untagged "
"command\n", H_NO(cmd), cmd->device->id, cmd->device->lun);
} else {
TAG_ALLOC *ta = &TagAlloc[cmd->device->id][cmd->device->lun];
cmd->tag = find_first_zero_bit(ta->allocated, MAX_TAGS);
set_bit(cmd->tag, ta->allocated);
ta->nr_allocated++;
TAG_PRINTK("scsi%d: using tag %d for target %d lun %d "
"(now %d tags in use)\n",
H_NO(cmd), cmd->tag, cmd->device->id,
cmd->device->lun, ta->nr_allocated);
}
}
/* Mark the tag of command 'cmd' as free, or in case of an untagged command,
* unlock the LUN.
*/
static void cmd_free_tag(Scsi_Cmnd *cmd)
{
SETUP_HOSTDATA(cmd->device->host);
if (cmd->tag == TAG_NONE) {
hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);
TAG_PRINTK("scsi%d: target %d lun %d untagged cmd finished\n",
H_NO(cmd), cmd->device->id, cmd->device->lun);
} else if (cmd->tag >= MAX_TAGS) {
printk(KERN_NOTICE "scsi%d: trying to free bad tag %d!\n",
H_NO(cmd), cmd->tag);
} else {
TAG_ALLOC *ta = &TagAlloc[cmd->device->id][cmd->device->lun];
clear_bit(cmd->tag, ta->allocated);
ta->nr_allocated--;
TAG_PRINTK("scsi%d: freed tag %d for target %d lun %d\n",
H_NO(cmd), cmd->tag, cmd->device->id, cmd->device->lun);
}
}
static void free_all_tags(void)
{
int target, lun;
TAG_ALLOC *ta;
if (!setup_use_tagged_queuing)
return;
for (target = 0; target < 8; ++target) {
for (lun = 0; lun < 8; ++lun) {
ta = &TagAlloc[target][lun];
bitmap_zero(ta->allocated, MAX_TAGS);
ta->nr_allocated = 0;
}
}
}
#endif /* SUPPORT_TAGS */
/*
* Function: void merge_contiguous_buffers( Scsi_Cmnd *cmd )
*
* Purpose: Try to merge several scatter-gather requests into one DMA
* transfer. This is possible if the scatter buffers lie on
* physical contiguous addresses.
*
* Parameters: Scsi_Cmnd *cmd
* The command to work on. The first scatter buffer's data are
* assumed to be already transfered into ptr/this_residual.
*/
static void merge_contiguous_buffers(Scsi_Cmnd *cmd)
{
unsigned long endaddr;
#if (NDEBUG & NDEBUG_MERGING)
unsigned long oldlen = cmd->SCp.this_residual;
int cnt = 1;
#endif
for (endaddr = virt_to_phys(cmd->SCp.ptr + cmd->SCp.this_residual - 1) + 1;
cmd->SCp.buffers_residual &&
virt_to_phys(sg_virt(&cmd->SCp.buffer[1])) == endaddr;) {
MER_PRINTK("VTOP(%p) == %08lx -> merging\n",
page_address(sg_page(&cmd->SCp.buffer[1])), endaddr);
#if (NDEBUG & NDEBUG_MERGING)
++cnt;
#endif
++cmd->SCp.buffer;
--cmd->SCp.buffers_residual;
cmd->SCp.this_residual += cmd->SCp.buffer->length;
endaddr += cmd->SCp.buffer->length;
}
#if (NDEBUG & NDEBUG_MERGING)
if (oldlen != cmd->SCp.this_residual)
MER_PRINTK("merged %d buffers from %p, new length %08x\n",
cnt, cmd->SCp.ptr, cmd->SCp.this_residual);
#endif
}
/*
* Function : void initialize_SCp(Scsi_Cmnd *cmd)
*
* Purpose : initialize the saved data pointers for cmd to point to the
* start of the buffer.
*
* Inputs : cmd - Scsi_Cmnd structure to have pointers reset.
*/
static inline void initialize_SCp(Scsi_Cmnd *cmd)
{
/*
* Initialize the Scsi Pointer field so that all of the commands in the
* various queues are valid.
*/
if (scsi_bufflen(cmd)) {
cmd->SCp.buffer = scsi_sglist(cmd);
cmd->SCp.buffers_residual = scsi_sg_count(cmd) - 1;
cmd->SCp.ptr = sg_virt(cmd->SCp.buffer);
cmd->SCp.this_residual = cmd->SCp.buffer->length;
/* ++roman: Try to merge some scatter-buffers if they are at
* contiguous physical addresses.
*/
merge_contiguous_buffers(cmd);
} else {
cmd->SCp.buffer = NULL;
cmd->SCp.buffers_residual = 0;
cmd->SCp.ptr = NULL;
cmd->SCp.this_residual = 0;
}
}
#include <linux/delay.h>
#if NDEBUG
static struct {
unsigned char mask;
const char *name;
} signals[] = {
{ SR_DBP, "PARITY"}, { SR_RST, "RST" }, { SR_BSY, "BSY" },
{ SR_REQ, "REQ" }, { SR_MSG, "MSG" }, { SR_CD, "CD" }, { SR_IO, "IO" },
{ SR_SEL, "SEL" }, {0, NULL}
}, basrs[] = {
{BASR_ATN, "ATN"}, {BASR_ACK, "ACK"}, {0, NULL}
}, icrs[] = {
{ICR_ASSERT_RST, "ASSERT RST"},{ICR_ASSERT_ACK, "ASSERT ACK"},
{ICR_ASSERT_BSY, "ASSERT BSY"}, {ICR_ASSERT_SEL, "ASSERT SEL"},
{ICR_ASSERT_ATN, "ASSERT ATN"}, {ICR_ASSERT_DATA, "ASSERT DATA"},
{0, NULL}
}, mrs[] = {
{MR_BLOCK_DMA_MODE, "MODE BLOCK DMA"}, {MR_TARGET, "MODE TARGET"},
{MR_ENABLE_PAR_CHECK, "MODE PARITY CHECK"}, {MR_ENABLE_PAR_INTR,
"MODE PARITY INTR"}, {MR_ENABLE_EOP_INTR,"MODE EOP INTR"},
{MR_MONITOR_BSY, "MODE MONITOR BSY"},
{MR_DMA_MODE, "MODE DMA"}, {MR_ARBITRATE, "MODE ARBITRATION"},
{0, NULL}
};
/*
* Function : void NCR5380_print(struct Scsi_Host *instance)
*
* Purpose : print the SCSI bus signals for debugging purposes
*
* Input : instance - which NCR5380
*/
static void NCR5380_print(struct Scsi_Host *instance)
{
unsigned char status, data, basr, mr, icr, i;
unsigned long flags;
local_irq_save(flags);
data = NCR5380_read(CURRENT_SCSI_DATA_REG);
status = NCR5380_read(STATUS_REG);
mr = NCR5380_read(MODE_REG);
icr = NCR5380_read(INITIATOR_COMMAND_REG);
basr = NCR5380_read(BUS_AND_STATUS_REG);
local_irq_restore(flags);
printk("STATUS_REG: %02x ", status);
for (i = 0; signals[i].mask; ++i)
if (status & signals[i].mask)
printk(",%s", signals[i].name);
printk("\nBASR: %02x ", basr);
for (i = 0; basrs[i].mask; ++i)
if (basr & basrs[i].mask)
printk(",%s", basrs[i].name);
printk("\nICR: %02x ", icr);
for (i = 0; icrs[i].mask; ++i)
if (icr & icrs[i].mask)
printk(",%s", icrs[i].name);
printk("\nMODE: %02x ", mr);
for (i = 0; mrs[i].mask; ++i)
if (mr & mrs[i].mask)
printk(",%s", mrs[i].name);
printk("\n");
}
static struct {
unsigned char value;
const char *name;
} phases[] = {
{PHASE_DATAOUT, "DATAOUT"}, {PHASE_DATAIN, "DATAIN"}, {PHASE_CMDOUT, "CMDOUT"},
{PHASE_STATIN, "STATIN"}, {PHASE_MSGOUT, "MSGOUT"}, {PHASE_MSGIN, "MSGIN"},
{PHASE_UNKNOWN, "UNKNOWN"}
};
/*
* Function : void NCR5380_print_phase(struct Scsi_Host *instance)
*
* Purpose : print the current SCSI phase for debugging purposes
*
* Input : instance - which NCR5380
*/
static void NCR5380_print_phase(struct Scsi_Host *instance)
{
unsigned char status;
int i;
status = NCR5380_read(STATUS_REG);
if (!(status & SR_REQ))
printk(KERN_DEBUG "scsi%d: REQ not asserted, phase unknown.\n", HOSTNO);
else {
for (i = 0; (phases[i].value != PHASE_UNKNOWN) &&
(phases[i].value != (status & PHASE_MASK)); ++i)
;
printk(KERN_DEBUG "scsi%d: phase %s\n", HOSTNO, phases[i].name);
}
}
#else /* !NDEBUG */
/* dummies... */
static inline void NCR5380_print(struct Scsi_Host *instance)
{
};
static inline void NCR5380_print_phase(struct Scsi_Host *instance)
{
};
#endif
/*
* ++roman: New scheme of calling NCR5380_main()
*
* If we're not in an interrupt, we can call our main directly, it cannot be
* already running. Else, we queue it on a task queue, if not 'main_running'
* tells us that a lower level is already executing it. This way,
* 'main_running' needs not be protected in a special way.
*
* queue_main() is a utility function for putting our main onto the task
* queue, if main_running is false. It should be called only from a
* interrupt or bottom half.
*/
#include <linux/workqueue.h>
#include <linux/interrupt.h>
static volatile int main_running;
static DECLARE_WORK(NCR5380_tqueue, NCR5380_main);
static inline void queue_main(void)
{
if (!main_running) {
/* If in interrupt and NCR5380_main() not already running,
queue it on the 'immediate' task queue, to be processed
immediately after the current interrupt processing has
finished. */
schedule_work(&NCR5380_tqueue);
}
/* else: nothing to do: the running NCR5380_main() will pick up
any newly queued command. */
}
static inline void NCR5380_all_init(void)
{
static int done = 0;
if (!done) {
INI_PRINTK("scsi : NCR5380_all_init()\n");
done = 1;
}
}
/*
* Function : void NCR58380_print_options (struct Scsi_Host *instance)
*
* Purpose : called by probe code indicating the NCR5380 driver
* options that were selected.
*
* Inputs : instance, pointer to this instance. Unused.
*/
static void __init NCR5380_print_options(struct Scsi_Host *instance)
{
printk(" generic options"
#ifdef AUTOSENSE
" AUTOSENSE"
#endif
#ifdef REAL_DMA
" REAL DMA"
#endif
#ifdef PARITY
" PARITY"
#endif
#ifdef SUPPORT_TAGS
" SCSI-2 TAGGED QUEUING"
#endif
);
printk(" generic release=%d", NCR5380_PUBLIC_RELEASE);
}
/*
* Function : void NCR5380_print_status (struct Scsi_Host *instance)
*
* Purpose : print commands in the various queues, called from
* NCR5380_abort and NCR5380_debug to aid debugging.
*
* Inputs : instance, pointer to this instance.
*/
static void NCR5380_print_status(struct Scsi_Host *instance)
{
char *pr_bfr;
char *start;
int len;
NCR_PRINT(NDEBUG_ANY);
NCR_PRINT_PHASE(NDEBUG_ANY);
pr_bfr = (char *)__get_free_page(GFP_ATOMIC);
if (!pr_bfr) {
printk("NCR5380_print_status: no memory for print buffer\n");
return;
}
len = NCR5380_proc_info(instance, pr_bfr, &start, 0, PAGE_SIZE, 0);
pr_bfr[len] = 0;
printk("\n%s\n", pr_bfr);
free_page((unsigned long)pr_bfr);
}
/******************************************/
/*
* /proc/scsi/[dtc pas16 t128 generic]/[0-ASC_NUM_BOARD_SUPPORTED]
*
* *buffer: I/O buffer
* **start: if inout == FALSE pointer into buffer where user read should start
* offset: current offset
* length: length of buffer
* hostno: Scsi_Host host_no
* inout: TRUE - user is writing; FALSE - user is reading
*
* Return the number of bytes read from or written
*/
#undef SPRINTF
#define SPRINTF(fmt,args...) \
do { \
if (pos + strlen(fmt) + 20 /* slop */ < buffer + length) \
pos += sprintf(pos, fmt , ## args); \
} while(0)
static char *lprint_Scsi_Cmnd(Scsi_Cmnd *cmd, char *pos, char *buffer, int length);
static int NCR5380_proc_info(struct Scsi_Host *instance, char *buffer,
char **start, off_t offset, int length, int inout)
{
char *pos = buffer;
struct NCR5380_hostdata *hostdata;
Scsi_Cmnd *ptr;
unsigned long flags;
off_t begin = 0;
#define check_offset() \
do { \
if (pos - buffer < offset - begin) { \
begin += pos - buffer; \
pos = buffer; \
} \
} while (0)
hostdata = (struct NCR5380_hostdata *)instance->hostdata;
if (inout) /* Has data been written to the file ? */
return -ENOSYS; /* Currently this is a no-op */
SPRINTF("NCR5380 core release=%d.\n", NCR5380_PUBLIC_RELEASE);
check_offset();
local_irq_save(flags);
SPRINTF("NCR5380: coroutine is%s running.\n",
main_running ? "" : "n't");
check_offset();
if (!hostdata->connected)
SPRINTF("scsi%d: no currently connected command\n", HOSTNO);
else
pos = lprint_Scsi_Cmnd((Scsi_Cmnd *) hostdata->connected,
pos, buffer, length);
SPRINTF("scsi%d: issue_queue\n", HOSTNO);
check_offset();
for (ptr = (Scsi_Cmnd *)hostdata->issue_queue; ptr; ptr = NEXT(ptr)) {
pos = lprint_Scsi_Cmnd(ptr, pos, buffer, length);
check_offset();
}
SPRINTF("scsi%d: disconnected_queue\n", HOSTNO);
check_offset();
for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr;
ptr = NEXT(ptr)) {
pos = lprint_Scsi_Cmnd(ptr, pos, buffer, length);
check_offset();
}
local_irq_restore(flags);
*start = buffer + (offset - begin);
if (pos - buffer < offset - begin)
return 0;
else if (pos - buffer - (offset - begin) < length)
return pos - buffer - (offset - begin);
return length;
}
static char *lprint_Scsi_Cmnd(Scsi_Cmnd *cmd, char *pos, char *buffer, int length)
{
int i, s;
unsigned char *command;
SPRINTF("scsi%d: destination target %d, lun %d\n",
H_NO(cmd), cmd->device->id, cmd->device->lun);
SPRINTF(" command = ");
command = cmd->cmnd;
SPRINTF("%2d (0x%02x)", command[0], command[0]);
for (i = 1, s = COMMAND_SIZE(command[0]); i < s; ++i)
SPRINTF(" %02x", command[i]);
SPRINTF("\n");
return pos;
}
/*
* Function : void NCR5380_init (struct Scsi_Host *instance)
*
* Purpose : initializes *instance and corresponding 5380 chip.
*
* Inputs : instance - instantiation of the 5380 driver.
*
* Notes : I assume that the host, hostno, and id bits have been
* set correctly. I don't care about the irq and other fields.
*
*/
static int __init NCR5380_init(struct Scsi_Host *instance, int flags)
{
int i;
SETUP_HOSTDATA(instance);
NCR5380_all_init();
hostdata->aborted = 0;
hostdata->id_mask = 1 << instance->this_id;
hostdata->id_higher_mask = 0;
for (i = hostdata->id_mask; i <= 0x80; i <<= 1)
if (i > hostdata->id_mask)
hostdata->id_higher_mask |= i;
for (i = 0; i < 8; ++i)
hostdata->busy[i] = 0;
#ifdef SUPPORT_TAGS
init_tags();
#endif
#if defined (REAL_DMA)
hostdata->dma_len = 0;
#endif
hostdata->targets_present = 0;
hostdata->connected = NULL;
hostdata->issue_queue = NULL;
hostdata->disconnected_queue = NULL;
hostdata->flags = FLAG_CHECK_LAST_BYTE_SENT;
if (!the_template) {
the_template = instance->hostt;
first_instance = instance;
}
#ifndef AUTOSENSE
if ((instance->cmd_per_lun > 1) || (instance->can_queue > 1))
printk("scsi%d: WARNING : support for multiple outstanding commands enabled\n"
" without AUTOSENSE option, contingent allegiance conditions may\n"
" be incorrectly cleared.\n", HOSTNO);
#endif /* def AUTOSENSE */
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
NCR5380_write(MODE_REG, MR_BASE);
NCR5380_write(TARGET_COMMAND_REG, 0);
NCR5380_write(SELECT_ENABLE_REG, 0);
return 0;
}
/*
* Function : int NCR5380_queue_command (Scsi_Cmnd *cmd,
* void (*done)(Scsi_Cmnd *))
*
* Purpose : enqueues a SCSI command
*
* Inputs : cmd - SCSI command, done - function called on completion, with
* a pointer to the command descriptor.
*
* Returns : 0
*
* Side effects :
* cmd is added to the per instance issue_queue, with minor
* twiddling done to the host specific fields of cmd. If the
* main coroutine is not running, it is restarted.
*
*/
static int NCR5380_queue_command(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
{
SETUP_HOSTDATA(cmd->device->host);
Scsi_Cmnd *tmp;
int oldto;
unsigned long flags;
#if (NDEBUG & NDEBUG_NO_WRITE)
switch (cmd->cmnd[0]) {
case WRITE_6:
case WRITE_10:
printk(KERN_NOTICE "scsi%d: WRITE attempted with NO_WRITE debugging flag set\n",
H_NO(cmd));
cmd->result = (DID_ERROR << 16);
done(cmd);
return 0;
}
#endif /* (NDEBUG & NDEBUG_NO_WRITE) */
#ifdef NCR5380_STATS
# if 0
if (!hostdata->connected && !hostdata->issue_queue &&
!hostdata->disconnected_queue) {
hostdata->timebase = jiffies;
}
# endif
# ifdef NCR5380_STAT_LIMIT
if (scsi_bufflen(cmd) > NCR5380_STAT_LIMIT)
# endif
switch (cmd->cmnd[0]) {
case WRITE:
case WRITE_6:
case WRITE_10:
hostdata->time_write[cmd->device->id] -= (jiffies - hostdata->timebase);
hostdata->bytes_write[cmd->device->id] += scsi_bufflen(cmd);
hostdata->pendingw++;
break;
case READ:
case READ_6:
case READ_10:
hostdata->time_read[cmd->device->id] -= (jiffies - hostdata->timebase);
hostdata->bytes_read[cmd->device->id] += scsi_bufflen(cmd);
hostdata->pendingr++;
break;
}
#endif
/*
* We use the host_scribble field as a pointer to the next command
* in a queue
*/
SET_NEXT(cmd, NULL);
cmd->scsi_done = done;
cmd->result = 0;
/*
* Insert the cmd into the issue queue. Note that REQUEST SENSE
* commands are added to the head of the queue since any command will
* clear the contingent allegiance condition that exists and the
* sense data is only guaranteed to be valid while the condition exists.
*/
local_irq_save(flags);
/* ++guenther: now that the issue queue is being set up, we can lock ST-DMA.
* Otherwise a running NCR5380_main may steal the lock.
* Lock before actually inserting due to fairness reasons explained in
* atari_scsi.c. If we insert first, then it's impossible for this driver
* to release the lock.
* Stop timer for this command while waiting for the lock, or timeouts
* may happen (and they really do), and it's no good if the command doesn't
* appear in any of the queues.
* ++roman: Just disabling the NCR interrupt isn't sufficient here,
* because also a timer int can trigger an abort or reset, which would
* alter queues and touch the lock.
*/
if (!IS_A_TT()) {
/* perhaps stop command timer here */
falcon_get_lock();
/* perhaps restart command timer here */
}
if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) {
LIST(cmd, hostdata->issue_queue);
SET_NEXT(cmd, hostdata->issue_queue);
hostdata->issue_queue = cmd;
} else {
for (tmp = (Scsi_Cmnd *)hostdata->issue_queue;
NEXT(tmp); tmp = NEXT(tmp))