summaryrefslogtreecommitdiff
path: root/linux/src/drivers/scsi/wd7000.h
blob: e17a69b13aed7c5a9ebc82daa5da47ed3211046e (plain)
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
/* $Id: wd7000.h,v 1.1 1999/04/26 05:55:19 tb Exp $
 *
 * Header file for the WD-7000 driver for Linux
 *
 * John Boyd <boyd@cis.ohio-state.edu>  Jan 1994:
 * This file has been reduced to only the definitions needed for the
 * WD7000 host structure.
 *
 * Revision by Miroslav Zagorac <zaga@fly.cc.fer.hr>  Jun 1997.
 */
#ifndef _WD7000_H

#include <linux/version.h>
#include <linux/types.h>
#include <linux/kdev_t.h>

#ifndef NULL
#define NULL 0L
#endif

/*
 *  In this version, sg_tablesize now defaults to WD7000_SG, and will
 *  be set to SG_NONE for older boards.  This is the reverse of the
 *  previous default, and was changed so that the driver-level
 *  Scsi_Host_Template would reflect the driver's support for scatter/
 *  gather.
 *
 *  Also, it has been reported that boards at Revision 6 support scatter/
 *  gather, so the new definition of an "older" board has been changed
 *  accordingly.
 */
#define WD7000_Q    16
#define WD7000_SG   16

#ifdef WD7000_DEFINES
/*
 *  Mailbox structure sizes.
 *  I prefer to keep the number of ICMBs much larger than the number of
 *  OGMBs.  OGMBs are used very quickly by the driver to start one or
 *  more commands, while ICMBs are used by the host adapter per command.
 */
#define OGMB_CNT	16
#define ICMB_CNT	32

/*
 *  Scb's are shared by all active adapters.  If you'd rather conserve
 *  memory, use a smaller number (> 0, of course) - things will should
 *  still work OK.
 */
#define MAX_SCBS	(4 * WD7000_Q)

/*
 *  WD7000-specific mailbox structure
 */
typedef volatile struct {
    unchar status;
    unchar scbptr[3];		/* SCSI-style - MSB first (big endian) */
} Mailbox;

/*
 *  This structure should contain all per-adapter global data.  I.e., any
 *  new global per-adapter data should put in here.
 */
typedef struct {
    struct Scsi_Host *sh;	/* Pointer to Scsi_Host structure    */
    int iobase;			/* This adapter's I/O base address   */
    int irq;			/* This adapter's IRQ level          */
    int dma;			/* This adapter's DMA channel        */
    int int_counter;		/* This adapter's interrupt counter  */
    int bus_on;			/* This adapter's BUS_ON time        */
    int bus_off;		/* This adapter's BUS_OFF time       */
    struct {			/* This adapter's mailboxes          */
	Mailbox ogmb[OGMB_CNT];	/* Outgoing mailboxes                */
	Mailbox icmb[ICMB_CNT];	/* Incoming mailboxes                */
    } mb;
    int next_ogmb;		/* to reduce contention at mailboxes */
    unchar control;		/* shadows CONTROL port value        */
    unchar rev1;		/* filled in by wd7000_revision      */
    unchar rev2;
} Adapter;


/*
 * possible irq range
 */
#define IRQ_MIN		3
#define IRQ_MAX		15
#define IRQS		(IRQ_MAX - IRQ_MIN + 1)

#define BUS_ON		64	/* x 125ns = 8000ns (BIOS default) */
#define BUS_OFF		15	/* x 125ns = 1875ns (BIOS default) */

/*
 *  Standard Adapter Configurations - used by wd7000_detect
 */
typedef struct {
    short irq;		/* IRQ level                                  */
    short dma;		/* DMA channel                                */
    uint iobase;	/* I/O base address                           */
    short bus_on;	/* Time that WD7000 spends on the AT-bus when */
			/* transferring data. BIOS default is 8000ns. */
    short bus_off;	/* Time that WD7000 spends OFF THE BUS after  */
			/* while it is transferring data.             */
			/* BIOS default is 1875ns                     */
} Config;


/*
 *  The following list defines strings to look for in the BIOS that identify
 *  it as the WD7000-FASST2 SST BIOS.  I suspect that something should be
 *  added for the Future Domain version.
 */
typedef struct {
    const char *sig;		/* String to look for            */
    ulong ofs;			/* offset from BIOS base address */
    uint len;			/* length of string              */
} Signature;

/*
 *  I/O Port Offsets and Bit Definitions
 *  4 addresses are used.  Those not defined here are reserved.
 */
#define ASC_STAT	0	/* Status,  Read          */
#define ASC_COMMAND	0	/* Command, Write         */
#define ASC_INTR_STAT	1	/* Interrupt Status, Read */
#define ASC_INTR_ACK	1	/* Acknowledge, Write     */
#define ASC_CONTROL	2	/* Control, Write         */

/*
 * ASC Status Port
 */
#define INT_IM		0x80	/* Interrupt Image Flag           */
#define CMD_RDY		0x40	/* Command Port Ready             */
#define CMD_REJ		0x20	/* Command Port Byte Rejected     */
#define ASC_INIT        0x10	/* ASC Initialized Flag           */
#define ASC_STATMASK    0xf0	/* The lower 4 Bytes are reserved */

/*
 * COMMAND opcodes
 *
 *  Unfortunately, I have no idea how to properly use some of these commands,
 *  as the OEM manual does not make it clear.  I have not been able to use
 *  enable/disable unsolicited interrupts or the reset commands with any
 *  discernible effect whatsoever.  I think they may be related to certain
 *  ICB commands, but again, the OEM manual doesn't make that clear.
 */
#define NO_OP			0	/* NO-OP toggles CMD_RDY bit in ASC_STAT  */
#define INITIALIZATION		1	/* initialization (10 bytes)              */
#define DISABLE_UNS_INTR	2	/* disable unsolicited interrupts         */
#define ENABLE_UNS_INTR		3	/* enable unsolicited interrupts          */
#define INTR_ON_FREE_OGMB	4	/* interrupt on free OGMB                 */
#define SOFT_RESET		5	/* SCSI bus soft reset                    */
#define HARD_RESET_ACK		6	/* SCSI bus hard reset acknowledge        */
#define START_OGMB		0x80	/* start command in OGMB (n)              */
#define SCAN_OGMBS		0xc0	/* start multiple commands, signature (n) */
					/*    where (n) = lower 6 bits            */
/*
 * For INITIALIZATION:
 */
typedef struct {
    unchar op;			/* command opcode (= 1)                    */
    unchar ID;			/* Adapter's SCSI ID                       */
    unchar bus_on;		/* Bus on time, x 125ns (see below)        */
    unchar bus_off;		/* Bus off time, ""         ""             */
    unchar rsvd;		/* Reserved                                */
    unchar mailboxes[3];	/* Address of Mailboxes, MSB first         */
    unchar ogmbs;		/* Number of outgoing MBs, max 64, 0,1 = 1 */
    unchar icmbs;		/* Number of incoming MBs,   ""       ""   */
} InitCmd;

/*
 * Interrupt Status Port - also returns diagnostic codes at ASC reset
 *
 * if msb is zero, the lower bits are diagnostic status
 * Diagnostics:
 * 01   No diagnostic error occurred
 * 02   RAM failure
 * 03   FIFO R/W failed
 * 04   SBIC register read/write failed
 * 05   Initialization D-FF failed
 * 06   Host IRQ D-FF failed
 * 07   ROM checksum error
 * Interrupt status (bitwise):
 * 10NNNNNN   outgoing mailbox NNNNNN is free
 * 11NNNNNN   incoming mailbox NNNNNN needs service
 */
#define MB_INTR		0xC0	/* Mailbox Service possible/required */
#define IMB_INTR	0x40	/* 1 Incoming / 0 Outgoing           */
#define MB_MASK		0x3f	/* mask for mailbox number           */

/*
 * CONTROL port bits
 */
#define INT_EN		0x08	/* Interrupt Enable */
#define DMA_EN		0x04	/* DMA Enable       */
#define SCSI_RES	0x02	/* SCSI Reset       */
#define ASC_RES		0x01	/* ASC Reset        */

/*
 * Driver data structures:
 *   - mb and scbs are required for interfacing with the host adapter.
 *     An SCB has extra fields not visible to the adapter; mb's
 *     _cannot_ do this, since the adapter assumes they are contiguous in
 *     memory, 4 bytes each, with ICMBs following OGMBs, and uses this fact
 *     to access them.
 *   - An icb is for host-only (non-SCSI) commands.  ICBs are 16 bytes each;
 *     the additional bytes are used only by the driver.
 *   - For now, a pool of SCBs are kept in global storage by this driver,
 *     and are allocated and freed as needed.
 *
 *  The 7000-FASST2 marks OGMBs empty as soon as it has _started_ a command,
 *  not when it has finished.  Since the SCB must be around for completion,
 *  problems arise when SCBs correspond to OGMBs, which may be reallocated
 *  earlier (or delayed unnecessarily until a command completes).
 *  Mailboxes are used as transient data structures, simply for
 *  carrying SCB addresses to/from the 7000-FASST2.
 *
 *  Note also since SCBs are not "permanently" associated with mailboxes,
 *  there is no need to keep a global list of Scsi_Cmnd pointers indexed
 *  by OGMB.   Again, SCBs reference their Scsi_Cmnds directly, so mailbox
 *  indices need not be involved.
 */

/*
 *  WD7000-specific scatter/gather element structure
 */
typedef struct {
    unchar len[3];
    unchar ptr[3];		/* Also SCSI-style - MSB first */
} Sgb;

typedef struct {		/* Command Control Block 5.4.1               */
    unchar op;			/* Command Control Block Operation Code      */
    unchar idlun;		/* op=0,2:Target Id, op=1:Initiator Id       */
				/* Outbound data transfer, length is checked */
				/* Inbound data transfer, length is checked  */
				/* Logical Unit Number                       */
    unchar cdb[12];		/* SCSI Command Block                        */
    volatile unchar status;	/* SCSI Return Status                        */
    volatile unchar vue;	/* Vendor Unique Error Code                  */
    unchar maxlen[3];		/* Maximum Data Transfer Length              */
    unchar dataptr[3];		/* SCSI Data Block Pointer                   */
    unchar linkptr[3];		/* Next Command Link Pointer                 */
    unchar direc;		/* Transfer Direction                        */
    unchar reserved2[6];	/* SCSI Command Descriptor Block             */
				/* end of hardware SCB                       */
    Scsi_Cmnd *SCpnt;		/* Scsi_Cmnd using this SCB                  */
    Sgb sgb[WD7000_SG];		/* Scatter/gather list for this SCB          */
    Adapter *host;		/* host adapter                              */
    unchar used;		/* flag                                      */
} Scb;

/*
 *  This driver is written to allow host-only commands to be executed.
 *  These use a 16-byte block called an ICB.  The format is extended by the
 *  driver to 18 bytes, to support the status returned in the ICMB and
 *  an execution phase code.
 *
 *  There are other formats besides these; these are the ones I've tried
 *  to use.  Formats for some of the defined ICB opcodes are not defined
 *  (notably, get/set unsolicited interrupt status) in my copy of the OEM
 *  manual, and others are ambiguous/hard to follow.
 */
#define ICB_OP_MASK		0x80	/* distinguishes scbs from icbs        */
#define ICB_OP_OPEN_RBUF	0x80	/* open receive buffer                 */
#define ICB_OP_RECV_CMD		0x81	/* receive command from initiator      */
#define ICB_OP_RECV_DATA	0x82	/* receive data from initiator         */
#define ICB_OP_RECV_SDATA	0x83	/* receive data with status from init. */
#define ICB_OP_SEND_DATA	0x84	/* send data with status to initiator  */
#define ICB_OP_SEND_STAT	0x86	/* send command status to initiator    */
					/* 0x87 is reserved                    */
#define ICB_OP_READ_INIT	0x88	/* read initialization bytes           */
#define ICB_OP_READ_ID		0x89	/* read adapter's SCSI ID              */
#define ICB_OP_SET_UMASK	0x8A	/* set unsolicited interrupt mask      */
#define ICB_OP_GET_UMASK	0x8B	/* read unsolicited interrupt mask     */
#define ICB_OP_GET_REVISION	0x8C	/* read firmware revision level        */
#define ICB_OP_DIAGNOSTICS	0x8D	/* execute diagnostics                 */
#define ICB_OP_SET_EPARMS	0x8E	/* set execution parameters            */
#define ICB_OP_GET_EPARMS	0x8F	/* read execution parameters           */

typedef struct {
    unchar op;
    unchar IDlun;		/* Initiator SCSI ID/lun     */
    unchar len[3];		/* command buffer length     */
    unchar ptr[3];		/* command buffer address    */
    unchar rsvd[7];		/* reserved                  */
    volatile unchar vue;	/* vendor-unique error code  */
    volatile unchar status;	/* returned (icmb) status    */
    volatile unchar phase;	/* used by interrupt handler */
} IcbRecvCmd;

typedef struct {
    unchar op;
    unchar IDlun;		/* Target SCSI ID/lun                  */
    unchar stat;		/* (outgoing) completion status byte 1 */
    unchar rsvd[12];		/* reserved                            */
    volatile unchar vue;	/* vendor-unique error code            */
    volatile unchar status;	/* returned (icmb) status              */
    volatile unchar phase;	/* used by interrupt handler           */
} IcbSendStat;

typedef struct {
    unchar op;
    volatile unchar primary;	/* primary revision level (returned)   */
    volatile unchar secondary;	/* secondary revision level (returned) */
    unchar rsvd[12];		/* reserved                            */
    volatile unchar vue;	/* vendor-unique error code            */
    volatile unchar status;	/* returned (icmb) status              */
    volatile unchar phase;	/* used by interrupt handler           */
} IcbRevLvl;

typedef struct {	/* I'm totally guessing here */
    unchar op;
    volatile unchar mask[14];	/* mask bits                 */
#if 0
    unchar rsvd[12];		/* reserved                  */
#endif
    volatile unchar vue;	/* vendor-unique error code  */
    volatile unchar status;	/* returned (icmb) status    */
    volatile unchar phase;	/* used by interrupt handler */
} IcbUnsMask;

typedef struct {
    unchar op;
    unchar type;		/* diagnostics type code (0-3) */
    unchar len[3];		/* buffer length               */
    unchar ptr[3];		/* buffer address              */
    unchar rsvd[7];		/* reserved                    */
    volatile unchar vue;	/* vendor-unique error code    */
    volatile unchar status;	/* returned (icmb) status      */
    volatile unchar phase;	/* used by interrupt handler   */
} IcbDiag;

#define ICB_DIAG_POWERUP	0	/* Power-up diags only       */
#define ICB_DIAG_WALKING	1	/* walking 1's pattern       */
#define ICB_DIAG_DMA		2	/* DMA - system memory diags */
#define ICB_DIAG_FULL		3	/* do both 1 & 2             */

typedef struct {
    unchar op;
    unchar rsvd1;		/* reserved                  */
    unchar len[3];		/* parms buffer length       */
    unchar ptr[3];		/* parms buffer address      */
    unchar idx[2];		/* index (MSB-LSB)           */
    unchar rsvd2[5];		/* reserved                  */
    volatile unchar vue;	/* vendor-unique error code  */
    volatile unchar status;	/* returned (icmb) status    */
    volatile unchar phase;	/* used by interrupt handler */
} IcbParms;

typedef struct {
    unchar op;
    unchar data[14];		/* format-specific data      */
    volatile unchar vue;	/* vendor-unique error code  */
    volatile unchar status;	/* returned (icmb) status    */
    volatile unchar phase;	/* used by interrupt handler */
} IcbAny;

typedef union {
    unchar op;			/* ICB opcode                     */
    IcbRecvCmd recv_cmd;	/* format for receive command     */
    IcbSendStat send_stat;	/* format for send status         */
    IcbRevLvl rev_lvl;		/* format for get revision level  */
    IcbDiag diag;		/* format for execute diagnostics */
    IcbParms eparms;		/* format for get/set exec parms  */
    IcbAny icb;			/* generic format                 */
    unchar data[18];
} Icb;

#define WAITnexttimeout		200	/* 2 seconds */

typedef union {			/* let's cheat... */
    int i;
    unchar u[sizeof (int)];	/* the sizeof(int) makes it more portable */
} i_u;

#endif /* WD7000_DEFINES */


#if (LINUX_VERSION_CODE >= 0x020100)

#define WD7000 {					\
    proc_dir:		&proc_scsi_wd7000,		\
    proc_info:		wd7000_proc_info,		\
    name:		"Western Digital WD-7000",	\
    detect:		wd7000_detect,			\
    command:		wd7000_command,			\
    queuecommand:	wd7000_queuecommand,		\
    abort:		wd7000_abort,			\
    reset:		wd7000_reset,			\
    bios_param:		wd7000_biosparam,		\
    can_queue:		WD7000_Q,			\
    this_id:		7,				\
    sg_tablesize:	WD7000_SG,			\
    cmd_per_lun:	1,				\
    unchecked_isa_dma:	1,				\
    use_clustering:	ENABLE_CLUSTERING,		\
    use_new_eh_code:	0				\
}

#else /* Use old scsi code */

#define WD7000 {					\
    proc_dir:		&proc_scsi_wd7000,		\
    proc_info:		wd7000_proc_info,		\
    name:		"Western Digital WD-7000",	\
    detect:		wd7000_detect,			\
    command:		wd7000_command,			\
    queuecommand:	wd7000_queuecommand,		\
    abort:		wd7000_abort,			\
    reset:		wd7000_reset,			\
    bios_param:		wd7000_biosparam,		\
    can_queue:		WD7000_Q,			\
    this_id:		7,				\
    sg_tablesize:	WD7000_SG,			\
    cmd_per_lun:	1,				\
    unchecked_isa_dma:	1,				\
    use_clustering:	ENABLE_CLUSTERING,		\
}

#endif /* LINUX_VERSION_CODE */


extern struct proc_dir_entry proc_scsi_wd7000;


#ifdef WD7000_DEFINES
int  wd7000_diagnostics    (Adapter *, int);
int  wd7000_init           (Adapter *);
void wd7000_revision       (Adapter *);
#endif /* WD7000_DEFINES */

void wd7000_setup          (char *, int *);
int  make_code             (uint, uint);
void wd7000_intr_handle    (int, void *, struct pt_regs *);
void do_wd7000_intr_handle (int, void *, struct pt_regs *);
int  wd7000_queuecommand   (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
int  wd7000_command        (Scsi_Cmnd *);
int  wd7000_set_info       (char *, int, struct Scsi_Host *);
int  wd7000_proc_info      (char *, char **, off_t, int, int, int);
int  wd7000_detect         (Scsi_Host_Template *);
int  wd7000_abort          (Scsi_Cmnd *);
int  wd7000_reset          (Scsi_Cmnd *, uint);
int  wd7000_biosparam      (Disk *, kdev_t, int *);

#endif /* _WD7000_H */