Commit | Line | Data |
---|---|---|
15637ed4 RG |
1 | /* |
2 | * (Mostly) Written by Julian Elischer (julian@tfs.com) | |
3 | * for TRW Financial Systems for use under the MACH(2.5) operating system. | |
4 | * | |
5 | * TRW Financial Systems, in accordance with their agreement with Carnegie | |
6 | * Mellon University, makes this software available to CMU to distribute | |
7 | * or use in any manner that they see fit as long as this message is kept with | |
8 | * the software. For this reason TFS also grants any other persons or | |
9 | * organisations permission to use or modify this software. | |
10 | * | |
11 | * TFS supplies this software to be publicly redistributed | |
12 | * on the understanding that TFS is not responsible for the correct | |
13 | * functioning of this software in any circumstances. | |
14 | * | |
519fb2b7 | 15 | * $Id: aha1542.c,v 2.8 93/10/24 12:55:08 julian Exp Locker: julian $ |
15637ed4 RG |
16 | */ |
17 | ||
18 | /* | |
19 | * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 | |
20 | */ | |
21 | ||
15637ed4 | 22 | #include <sys/types.h> |
519fb2b7 | 23 | #ifdef KERNEL /* don't laugh.. look for main() */ |
15637ed4 RG |
24 | #include <aha.h> |
25 | ||
26 | #include <sys/param.h> | |
27 | #include <sys/systm.h> | |
28 | #include <sys/errno.h> | |
29 | #include <sys/ioctl.h> | |
519fb2b7 | 30 | #include <sys/malloc.h> |
15637ed4 RG |
31 | #include <sys/buf.h> |
32 | #include <sys/proc.h> | |
33 | #include <sys/user.h> | |
15637ed4 | 34 | #include <i386/isa/isa_device.h> |
519fb2b7 | 35 | #endif /* KERNEL */ |
15637ed4 RG |
36 | #include <scsi/scsi_all.h> |
37 | #include <scsi/scsiconf.h> | |
15637ed4 | 38 | |
519fb2b7 | 39 | #ifdef KERNEL |
15637ed4 RG |
40 | #include "ddb.h" |
41 | #if NDDB > 0 | |
519fb2b7 RG |
42 | int Debugger(); |
43 | #else /* NDDB */ | |
15637ed4 | 44 | #define Debugger() panic("should call debugger here (adaptec.c)") |
519fb2b7 RG |
45 | #endif /* NDDB */ |
46 | extern int hz; | |
47 | #else /*KERNEL */ | |
48 | #define NAHA 1 | |
49 | #endif /*KERNEL */ | |
15637ed4 RG |
50 | |
51 | /************************** board definitions *******************************/ | |
519fb2b7 | 52 | |
15637ed4 RG |
53 | /* |
54 | * I/O Port Interface | |
55 | */ | |
56 | ||
519fb2b7 | 57 | #define AHA_BASE aha->aha_base |
15637ed4 RG |
58 | #define AHA_CTRL_STAT_PORT (AHA_BASE + 0x0) /* control & status */ |
59 | #define AHA_CMD_DATA_PORT (AHA_BASE + 0x1) /* cmds and datas */ | |
60 | #define AHA_INTR_PORT (AHA_BASE + 0x2) /* Intr. stat */ | |
61 | ||
62 | /* | |
63 | * AHA_CTRL_STAT bits (write) | |
64 | */ | |
65 | ||
66 | #define AHA_HRST 0x80 /* Hardware reset */ | |
67 | #define AHA_SRST 0x40 /* Software reset */ | |
68 | #define AHA_IRST 0x20 /* Interrupt reset */ | |
69 | #define AHA_SCRST 0x10 /* SCSI bus reset */ | |
70 | ||
71 | /* | |
72 | * AHA_CTRL_STAT bits (read) | |
73 | */ | |
74 | ||
75 | #define AHA_STST 0x80 /* Self test in Progress */ | |
76 | #define AHA_DIAGF 0x40 /* Diagnostic Failure */ | |
77 | #define AHA_INIT 0x20 /* Mbx Init required */ | |
78 | #define AHA_IDLE 0x10 /* Host Adapter Idle */ | |
79 | #define AHA_CDF 0x08 /* cmd/data out port full */ | |
80 | #define AHA_DF 0x04 /* Data in port full */ | |
81 | #define AHA_INVDCMD 0x01 /* Invalid command */ | |
82 | ||
83 | /* | |
84 | * AHA_CMD_DATA bits (write) | |
85 | */ | |
86 | ||
87 | #define AHA_NOP 0x00 /* No operation */ | |
88 | #define AHA_MBX_INIT 0x01 /* Mbx initialization */ | |
89 | #define AHA_START_SCSI 0x02 /* start scsi command */ | |
90 | #define AHA_START_BIOS 0x03 /* start bios command */ | |
91 | #define AHA_INQUIRE 0x04 /* Adapter Inquiry */ | |
92 | #define AHA_MBO_INTR_EN 0x05 /* Enable MBO available interrupt */ | |
93 | #define AHA_SEL_TIMEOUT_SET 0x06 /* set selection time-out */ | |
94 | #define AHA_BUS_ON_TIME_SET 0x07 /* set bus-on time */ | |
95 | #define AHA_BUS_OFF_TIME_SET 0x08 /* set bus-off time */ | |
96 | #define AHA_SPEED_SET 0x09 /* set transfer speed */ | |
97 | #define AHA_DEV_GET 0x0a /* return installed devices */ | |
98 | #define AHA_CONF_GET 0x0b /* return configuration data */ | |
99 | #define AHA_TARGET_EN 0x0c /* enable target mode */ | |
100 | #define AHA_SETUP_GET 0x0d /* return setup data */ | |
101 | #define AHA_WRITE_CH2 0x1a /* write channel 2 buffer */ | |
102 | #define AHA_READ_CH2 0x1b /* read channel 2 buffer */ | |
103 | #define AHA_WRITE_FIFO 0x1c /* write fifo buffer */ | |
104 | #define AHA_READ_FIFO 0x1d /* read fifo buffer */ | |
105 | #define AHA_ECHO 0x1e /* Echo command data */ | |
b9f44595 RG |
106 | #define AHA_EXT_BIOS 0x28 /* return extended bios info */ |
107 | #define AHA_MBX_ENABLE 0x29 /* enable mail box interface */ | |
15637ed4 RG |
108 | |
109 | struct aha_cmd_buf { | |
519fb2b7 | 110 | u_char byte[16]; |
15637ed4 RG |
111 | }; |
112 | ||
113 | /* | |
114 | * AHA_INTR_PORT bits (read) | |
115 | */ | |
116 | ||
117 | #define AHA_ANY_INTR 0x80 /* Any interrupt */ | |
118 | #define AHA_SCRD 0x08 /* SCSI reset detected */ | |
119 | #define AHA_HACC 0x04 /* Command complete */ | |
120 | #define AHA_MBOA 0x02 /* MBX out empty */ | |
121 | #define AHA_MBIF 0x01 /* MBX in full */ | |
122 | ||
123 | /* | |
124 | * Mail box defs | |
125 | */ | |
126 | ||
127 | #define AHA_MBX_SIZE 16 /* mail box size */ | |
128 | ||
129 | struct aha_mbx { | |
130 | struct aha_mbx_out { | |
131 | unsigned char cmd; | |
132 | unsigned char ccb_addr[3]; | |
519fb2b7 RG |
133 | } mbo[AHA_MBX_SIZE]; |
134 | struct aha_mbx_in { | |
15637ed4 RG |
135 | unsigned char stat; |
136 | unsigned char ccb_addr[3]; | |
137 | } mbi[AHA_MBX_SIZE]; | |
138 | }; | |
139 | ||
140 | /* | |
141 | * mbo.cmd values | |
142 | */ | |
143 | ||
144 | #define AHA_MBO_FREE 0x0 /* MBO entry is free */ | |
145 | #define AHA_MBO_START 0x1 /* MBO activate entry */ | |
146 | #define AHA_MBO_ABORT 0x2 /* MBO abort entry */ | |
147 | ||
519fb2b7 RG |
148 | /* |
149 | * mbi.stat values | |
150 | */ | |
151 | ||
15637ed4 RG |
152 | #define AHA_MBI_FREE 0x0 /* MBI entry is free */ |
153 | #define AHA_MBI_OK 0x1 /* completed without error */ | |
154 | #define AHA_MBI_ABORT 0x2 /* aborted ccb */ | |
155 | #define AHA_MBI_UNKNOWN 0x3 /* Tried to abort invalid CCB */ | |
156 | #define AHA_MBI_ERROR 0x4 /* Completed with error */ | |
157 | ||
15637ed4 RG |
158 | /* FOR OLD VERSIONS OF THE !%$@ this may have to be 16 (yuk) */ |
159 | #define AHA_NSEG 17 /* Number of scatter gather segments <= 16 */ | |
160 | /* allow 64 K i/o (min) */ | |
161 | ||
162 | struct aha_ccb { | |
519fb2b7 RG |
163 | unsigned char opcode; |
164 | unsigned char lun:3; | |
165 | unsigned char data_in:1; /* must be 0 */ | |
166 | unsigned char data_out:1; /* must be 0 */ | |
167 | unsigned char target:3; | |
168 | unsigned char scsi_cmd_length; | |
169 | unsigned char req_sense_length; | |
170 | unsigned char data_length[3]; | |
171 | unsigned char data_addr[3]; | |
172 | unsigned char link_addr[3]; | |
173 | unsigned char link_id; | |
174 | unsigned char host_stat; | |
175 | unsigned char target_stat; | |
176 | unsigned char reserved[2]; | |
177 | struct scsi_generic scsi_cmd; | |
178 | struct scsi_sense_data scsi_sense; | |
179 | struct aha_scat_gath { | |
15637ed4 RG |
180 | unsigned char seg_len[3]; |
181 | unsigned char seg_addr[3]; | |
182 | } scat_gath[AHA_NSEG]; | |
519fb2b7 RG |
183 | struct aha_ccb *next; |
184 | struct scsi_xfer *xfer; /* the scsi_xfer for this cmd */ | |
185 | struct aha_mbx_out *mbx; /* pointer to mail box */ | |
186 | int flags; | |
15637ed4 RG |
187 | #define CCB_FREE 0 |
188 | #define CCB_ACTIVE 1 | |
189 | #define CCB_ABORTED 2 | |
15637ed4 RG |
190 | }; |
191 | ||
15637ed4 RG |
192 | /* |
193 | * opcode fields | |
194 | */ | |
195 | ||
196 | #define AHA_INITIATOR_CCB 0x00 /* SCSI Initiator CCB */ | |
197 | #define AHA_TARGET_CCB 0x01 /* SCSI Target CCB */ | |
519fb2b7 | 198 | #define AHA_INIT_SCAT_GATH_CCB 0x02 /* SCSI Initiator with scatter gather */ |
15637ed4 RG |
199 | #define AHA_RESET_CCB 0x81 /* SCSI Bus reset */ |
200 | ||
15637ed4 RG |
201 | /* |
202 | * aha_ccb.host_stat values | |
203 | */ | |
204 | ||
205 | #define AHA_OK 0x00 /* cmd ok */ | |
206 | #define AHA_LINK_OK 0x0a /* Link cmd ok */ | |
207 | #define AHA_LINK_IT 0x0b /* Link cmd ok + int */ | |
208 | #define AHA_SEL_TIMEOUT 0x11 /* Selection time out */ | |
209 | #define AHA_OVER_UNDER 0x12 /* Data over/under run */ | |
210 | #define AHA_BUS_FREE 0x13 /* Bus dropped at unexpected time */ | |
211 | #define AHA_INV_BUS 0x14 /* Invalid bus phase/sequence */ | |
212 | #define AHA_BAD_MBO 0x15 /* Incorrect MBO cmd */ | |
213 | #define AHA_BAD_CCB 0x16 /* Incorrect ccb opcode */ | |
214 | #define AHA_BAD_LINK 0x17 /* Not same values of LUN for links */ | |
215 | #define AHA_INV_TARGET 0x18 /* Invalid target direction */ | |
216 | #define AHA_CCB_DUP 0x19 /* Duplicate CCB received */ | |
217 | #define AHA_INV_CCB 0x1a /* Invalid CCB or segment list */ | |
218 | #define AHA_ABORTED 42 | |
219 | ||
519fb2b7 RG |
220 | struct aha_setup { |
221 | u_char sync_neg:1; | |
222 | u_char parity:1; | |
223 | u_char:6; | |
224 | u_char speed; | |
225 | u_char bus_on; | |
226 | u_char bus_off; | |
227 | u_char num_mbx; | |
228 | u_char mbx[3]; | |
229 | struct { | |
230 | u_char offset:4; | |
231 | u_char period:3; | |
232 | u_char valid:1; | |
233 | } sync[8]; | |
234 | u_char disc_sts; | |
15637ed4 RG |
235 | }; |
236 | ||
519fb2b7 RG |
237 | struct aha_config { |
238 | u_char chan; | |
239 | u_char intr; | |
240 | u_char scsi_dev:3; | |
241 | u_char:5; | |
15637ed4 RG |
242 | }; |
243 | ||
b9f44595 RG |
244 | struct aha_inquire |
245 | { | |
246 | u_char boardid; /* type of board */ | |
247 | /* 0x20 = BusLogic 545, but it gets | |
248 | the command wrong, only returns | |
249 | one byte */ | |
250 | /* 0x31 = AHA-1540 */ | |
251 | /* 0x41 = AHA-1540A/1542A/1542B */ | |
252 | /* 0x42 = AHA-1640 */ | |
253 | /* 0x43 = AHA-1542C */ | |
254 | /* 0x44 = AHA-1542CF */ | |
255 | u_char spec_opts; /* special options ID */ | |
256 | /* 0x41 = Board is standard model */ | |
257 | u_char revision_1; /* firmware revision [0-9A-Z] */ | |
258 | u_char revision_2; /* firmware revision [0-9A-Z] */ | |
259 | }; | |
260 | ||
261 | struct aha_extbios | |
262 | { | |
263 | u_char flags; /* Bit 3 == 1 extended bios enabled */ | |
264 | u_char mailboxlock; /* mail box lock code to unlock it */ | |
265 | }; | |
266 | ||
15637ed4 RG |
267 | #define INT9 0x01 |
268 | #define INT10 0x02 | |
269 | #define INT11 0x04 | |
270 | #define INT12 0x08 | |
271 | #define INT14 0x20 | |
272 | #define INT15 0x40 | |
273 | ||
274 | #define CHAN0 0x01 | |
275 | #define CHAN5 0x20 | |
276 | #define CHAN6 0x40 | |
277 | #define CHAN7 0x80 | |
278 | ||
15637ed4 RG |
279 | /*********************************** end of board definitions***************/ |
280 | ||
519fb2b7 | 281 | #define PHYSTOKV(x) (((long int)(x)) ^ aha->kv_phys_xor) |
15637ed4 | 282 | #define KVTOPHYS(x) vtophys(x) |
15637ed4 RG |
283 | #define AHA_DMA_PAGES AHA_NSEG |
284 | ||
285 | #define PAGESIZ 4096 | |
286 | #define INVALIDATE_CACHE {asm volatile( ".byte 0x0F ;.byte 0x08" ); } | |
287 | ||
519fb2b7 | 288 | u_char aha_scratch_buf[256]; |
b33502d5 | 289 | #ifdef AHADEBUG |
519fb2b7 RG |
290 | int aha_debug = 1; |
291 | #endif /*AHADEBUG */ | |
15637ed4 | 292 | |
519fb2b7 RG |
293 | struct aha_data { |
294 | short aha_base; /* base port for each board */ | |
295 | /* | |
296 | * xor this with a physaddr to get a kv addr and visa versa | |
297 | * for items in THIS STRUCT only. | |
298 | * Used to get the CCD's physical and kv addresses from each | |
299 | * other. | |
300 | */ | |
301 | long int kv_phys_xor; | |
302 | struct aha_mbx aha_mbx; /* all the mailboxes */ | |
303 | struct aha_ccb *aha_ccb_free; /* the next free ccb */ | |
304 | struct aha_ccb aha_ccb[AHA_MBX_SIZE]; /* all the CCBs */ | |
305 | int aha_int; /* our irq level */ | |
306 | int aha_dma; /* out DMA req channel */ | |
307 | int aha_scsi_dev; /* ourscsi bus address */ | |
308 | struct scsi_link sc_link; /* prototype for subdevs */ | |
309 | } *ahadata[NAHA]; | |
310 | ||
311 | struct aha_ccb *aha_get_ccb(); | |
312 | int ahaprobe(); | |
313 | void aha_done(); | |
314 | int ahaattach(); | |
315 | int ahaintr(); | |
316 | int32 aha_scsi_cmd(); | |
317 | void aha_timeout(); | |
318 | void ahaminphys(); | |
319 | u_int32 aha_adapter_info(); | |
320 | ||
321 | #ifdef KERNEL | |
322 | struct scsi_adapter aha_switch = | |
323 | { | |
324 | aha_scsi_cmd, | |
325 | ahaminphys, | |
326 | 0, | |
327 | 0, | |
328 | aha_adapter_info, | |
329 | "aha", | |
330 | 0, 0 | |
331 | }; | |
332 | ||
333 | /* the below structure is so we have a default dev struct for out link struct */ | |
334 | struct scsi_device aha_dev = | |
335 | { | |
336 | NULL, /* Use default error handler */ | |
337 | NULL, /* have a queue, served by this */ | |
338 | NULL, /* have no async handler */ | |
339 | NULL, /* Use default 'done' routine */ | |
340 | "aha", | |
341 | 0, | |
342 | 0, 0 | |
343 | }; | |
15637ed4 | 344 | |
519fb2b7 RG |
345 | struct isa_driver ahadriver = |
346 | { | |
347 | ahaprobe, | |
348 | ahaattach, | |
349 | "aha" | |
350 | }; | |
351 | ||
352 | #endif /* KERNEL */ | |
353 | ||
354 | static int ahaunit = 0; | |
15637ed4 RG |
355 | |
356 | #define aha_abortmbx(mbx) \ | |
357 | (mbx)->cmd = AHA_MBO_ABORT; \ | |
358 | outb(AHA_CMD_DATA_PORT, AHA_START_SCSI); | |
359 | #define aha_startmbx(mbx) \ | |
360 | (mbx)->cmd = AHA_MBO_START; \ | |
361 | outb(AHA_CMD_DATA_PORT, AHA_START_SCSI); | |
362 | ||
519fb2b7 RG |
363 | #define AHA_RESET_TIMEOUT 1000 /* time to wait for reset (mSec) */ |
364 | #ifndef KERNEL | |
365 | main() | |
366 | { | |
367 | printf("size of aha_data is %d\n", sizeof(struct aha_data)); | |
368 | printf("size of aha_ccb is %d\n", sizeof(struct aha_ccb)); | |
369 | printf("size of aha_mbx is %d\n", sizeof(struct aha_mbx)); | |
370 | } | |
15637ed4 | 371 | |
519fb2b7 | 372 | #else /*KERNEL */ |
15637ed4 | 373 | |
519fb2b7 RG |
374 | /* |
375 | * aha_cmd(unit,icnt, ocnt,wait, retval, opcode, args) | |
376 | * Activate Adapter command | |
377 | * icnt: number of args (outbound bytes written after opcode) | |
378 | * ocnt: number of expected returned bytes | |
379 | * wait: number of seconds to wait for response | |
380 | * retval: buffer where to place returned bytes | |
381 | * opcode: opcode AHA_NOP, AHA_MBX_INIT, AHA_START_SCSI ... | |
382 | * args: parameters | |
383 | * | |
384 | * Performs an adapter command through the ports. Not to be confused | |
385 | * with a scsi command, which is read in via the dma. One of the adapter | |
386 | * commands tells it to read in a scsi command but that one is done | |
387 | * separately. This is only called during set-up. | |
388 | */ | |
389 | int | |
390 | aha_cmd(unit, icnt, ocnt, wait, retval, opcode, args) | |
391 | u_char *retval; | |
392 | unsigned opcode; | |
393 | u_char args; | |
15637ed4 | 394 | { |
519fb2b7 | 395 | struct aha_data *aha = ahadata[unit]; |
15637ed4 | 396 | unsigned *ic = &opcode; |
519fb2b7 | 397 | u_char oc; |
15637ed4 | 398 | register i; |
519fb2b7 RG |
399 | int sts; |
400 | ||
401 | /* | |
402 | * multiply the wait argument by a big constant | |
403 | * zero defaults to 1 sec.. | |
404 | * all wait loops are in 50uSec cycles | |
405 | */ | |
406 | if (wait) | |
407 | wait *= 20000; | |
15637ed4 | 408 | else |
519fb2b7 RG |
409 | wait = 20000; |
410 | /* | |
411 | * Wait for the adapter to go idle, unless it's one of | |
412 | * the commands which don't need this | |
413 | */ | |
414 | if (opcode != AHA_MBX_INIT && opcode != AHA_START_SCSI) { | |
415 | i = 20000; /*do this for upto about a second */ | |
416 | while (--i) { | |
15637ed4 | 417 | sts = inb(AHA_CTRL_STAT_PORT); |
519fb2b7 | 418 | if (sts & AHA_IDLE) { |
15637ed4 RG |
419 | break; |
420 | } | |
519fb2b7 | 421 | DELAY(50); |
15637ed4 | 422 | } |
519fb2b7 | 423 | if (!i) { |
98639498 | 424 | printf("aha%d: aha_cmd, host not idle(0x%x)\n", |
519fb2b7 RG |
425 | unit, sts); |
426 | return (ENXIO); | |
15637ed4 RG |
427 | } |
428 | } | |
519fb2b7 RG |
429 | /* |
430 | * Now that it is idle, if we expect output, preflush the | |
431 | * queue feeding to us. | |
432 | */ | |
433 | if (ocnt) { | |
434 | while ((inb(AHA_CTRL_STAT_PORT)) & AHA_DF) | |
15637ed4 RG |
435 | inb(AHA_CMD_DATA_PORT); |
436 | } | |
519fb2b7 RG |
437 | /* |
438 | * Output the command and the number of arguments given | |
439 | * for each byte, first check the port is empty. | |
440 | */ | |
441 | icnt++; | |
442 | /* include the command */ | |
443 | while (icnt--) { | |
15637ed4 | 444 | sts = inb(AHA_CTRL_STAT_PORT); |
519fb2b7 | 445 | for (i = wait; i; i--) { |
15637ed4 RG |
446 | sts = inb(AHA_CTRL_STAT_PORT); |
447 | if (!(sts & AHA_CDF)) | |
448 | break; | |
519fb2b7 | 449 | DELAY(50); |
15637ed4 | 450 | } |
519fb2b7 RG |
451 | if (i == 0) { |
452 | printf("aha%d: aha_cmd, cmd/data port full\n", unit); | |
453 | outb(AHA_CTRL_STAT_PORT, AHA_SRST); | |
454 | return (ENXIO); | |
15637ed4 | 455 | } |
519fb2b7 | 456 | outb(AHA_CMD_DATA_PORT, (u_char) (*ic++)); |
15637ed4 | 457 | } |
519fb2b7 RG |
458 | /* |
459 | * If we expect input, loop that many times, each time, | |
460 | * looking for the data register to have valid data | |
461 | */ | |
462 | while (ocnt--) { | |
15637ed4 | 463 | sts = inb(AHA_CTRL_STAT_PORT); |
519fb2b7 | 464 | for (i = wait; i; i--) { |
15637ed4 | 465 | sts = inb(AHA_CTRL_STAT_PORT); |
519fb2b7 | 466 | if (sts & AHA_DF) |
15637ed4 | 467 | break; |
519fb2b7 | 468 | DELAY(50); |
15637ed4 | 469 | } |
519fb2b7 | 470 | if (i == 0) { |
98639498 | 471 | printf("aha%d: aha_cmd, cmd/data port empty %d\n", |
519fb2b7 RG |
472 | unit, ocnt); |
473 | return (ENXIO); | |
15637ed4 RG |
474 | } |
475 | oc = inb(AHA_CMD_DATA_PORT); | |
476 | if (retval) | |
477 | *retval++ = oc; | |
478 | } | |
519fb2b7 RG |
479 | /* |
480 | * Wait for the board to report a finised instruction | |
481 | */ | |
482 | i = 20000; | |
483 | while (--i) { | |
15637ed4 | 484 | sts = inb(AHA_INTR_PORT); |
519fb2b7 | 485 | if (sts & AHA_HACC) { |
15637ed4 RG |
486 | break; |
487 | } | |
519fb2b7 | 488 | DELAY(50); |
15637ed4 | 489 | } |
519fb2b7 RG |
490 | if (i == 0) { |
491 | printf("aha%d: aha_cmd, host not finished(0x%x)\n", unit, sts); | |
492 | return (ENXIO); | |
15637ed4 RG |
493 | } |
494 | outb(AHA_CTRL_STAT_PORT, AHA_IRST); | |
519fb2b7 | 495 | return 0; |
15637ed4 RG |
496 | } |
497 | ||
519fb2b7 RG |
498 | /* |
499 | * Check if the device can be found at the port given | |
500 | * and if so, set it up ready for further work | |
501 | * as an argument, takes the isa_device structure from | |
502 | * autoconf.c | |
503 | */ | |
504 | int | |
15637ed4 | 505 | ahaprobe(dev) |
519fb2b7 | 506 | struct isa_device *dev; |
15637ed4 | 507 | { |
519fb2b7 RG |
508 | int unit = ahaunit; |
509 | struct aha_data *aha; | |
510 | ||
511 | /* | |
512 | * find unit and check we have that many defined | |
513 | */ | |
514 | if (unit >= NAHA) { | |
515 | printf("aha%d: unit number too high\n", unit); | |
516 | return 0; | |
15637ed4 | 517 | } |
519fb2b7 RG |
518 | dev->id_unit = unit; |
519 | ||
520 | /* | |
521 | * a quick safety check so we can be sleazy later | |
522 | */ | |
523 | if (sizeof(struct aha_data) > PAGESIZ) { | |
524 | printf("aha struct > pagesize\n"); | |
525 | return 0; | |
15637ed4 | 526 | } |
519fb2b7 RG |
527 | /* |
528 | * Allocate a storage area for us | |
529 | */ | |
530 | if (ahadata[unit]) { | |
531 | printf("aha%d: memory already allocated\n", unit); | |
532 | return 0; | |
533 | } | |
534 | aha = malloc(sizeof(struct aha_data), M_TEMP, M_NOWAIT); | |
535 | if (!aha) { | |
536 | printf("aha%d: cannot malloc!\n", unit); | |
537 | return 0; | |
538 | } | |
539 | ahadata[unit] = aha; | |
540 | aha->aha_base = dev->id_iobase; | |
541 | /* | |
542 | * Try initialise a unit at this location | |
543 | * sets up dma and bus speed, loads aha->aha_int | |
544 | */ | |
545 | if (aha_init(unit) != 0) { | |
546 | ahadata[unit] = NULL; | |
547 | free(aha, M_TEMP); | |
548 | return 0; | |
549 | } | |
550 | /* | |
551 | * Calculate the xor product of the aha struct's | |
552 | * physical and virtual address. This allows us | |
553 | * to change addresses within the structure | |
554 | * from physical to virtual easily, as long as | |
555 | * the structure is less than 1 page in size. | |
556 | * This is used to recognise CCBs which are in | |
557 | * this struct and which are refered to by the | |
558 | * hardware using physical addresses. | |
559 | * (assumes malloc returns a chunk that doesn't | |
560 | * span pages) | |
561 | * eventually use the hash table in aha1742.c | |
562 | */ | |
563 | aha->kv_phys_xor = (long int) aha ^ (KVTOPHYS(aha)); | |
15637ed4 | 564 | |
519fb2b7 RG |
565 | /* |
566 | * If it's there, put in it's interrupt vectors | |
567 | */ | |
568 | dev->id_irq = (1 << aha->aha_int); | |
569 | dev->id_drq = aha->aha_dma; | |
570 | ahaunit++; | |
571 | return 0x4; | |
15637ed4 RG |
572 | } |
573 | ||
519fb2b7 RG |
574 | /* |
575 | * Attach all the sub-devices we can find | |
576 | */ | |
577 | int | |
15637ed4 | 578 | ahaattach(dev) |
519fb2b7 | 579 | struct isa_device *dev; |
15637ed4 | 580 | { |
519fb2b7 RG |
581 | int unit = dev->id_unit; |
582 | struct aha_data *aha = ahadata[unit]; | |
583 | ||
584 | /* | |
585 | * fill in the prototype scsi_link. | |
586 | */ | |
587 | aha->sc_link.adapter_unit = unit; | |
588 | aha->sc_link.adapter_targ = aha->aha_scsi_dev; | |
589 | aha->sc_link.adapter = &aha_switch; | |
590 | aha->sc_link.device = &aha_dev; | |
591 | ||
592 | /* | |
593 | * ask the adapter what subunits are present | |
594 | */ | |
595 | scsi_attachdevs(&(aha->sc_link)); | |
596 | ||
597 | return 1; | |
15637ed4 RG |
598 | } |
599 | ||
519fb2b7 RG |
600 | /* |
601 | * Return some information to the caller about the adapter and its | |
602 | * capabilities. | |
603 | */ | |
604 | u_int32 | |
605 | aha_adapter_info(unit) | |
606 | int unit; | |
15637ed4 | 607 | { |
519fb2b7 | 608 | return (2); /* 2 outstanding requests at a time per device */ |
15637ed4 RG |
609 | } |
610 | ||
519fb2b7 RG |
611 | /* |
612 | * Catch an interrupt from the adaptor | |
613 | */ | |
614 | int | |
15637ed4 RG |
615 | ahaintr(unit) |
616 | { | |
617 | struct aha_ccb *ccb; | |
618 | unsigned char stat; | |
619 | register i; | |
519fb2b7 | 620 | struct aha_data *aha = ahadata[unit]; |
15637ed4 | 621 | |
b33502d5 | 622 | #ifdef AHADEBUG |
519fb2b7 RG |
623 | printf("ahaintr "); |
624 | #endif /*AHADEBUG */ | |
625 | /* | |
626 | * First acknowlege the interrupt, Then if it's not telling about | |
627 | * a completed operation just return. | |
628 | */ | |
15637ed4 RG |
629 | stat = inb(AHA_INTR_PORT); |
630 | outb(AHA_CTRL_STAT_PORT, AHA_IRST); | |
519fb2b7 RG |
631 | if (!(stat & AHA_MBIF)) |
632 | return 1; | |
b33502d5 | 633 | #ifdef AHADEBUG |
519fb2b7 RG |
634 | printf("mbxin "); |
635 | #endif /*AHADEBUG */ | |
636 | /* | |
637 | * If it IS then process the competed operation | |
638 | */ | |
639 | for (i = 0; i < AHA_MBX_SIZE; i++) { | |
640 | if (aha->aha_mbx.mbi[i].stat != AHA_MBI_FREE) { | |
641 | ccb = (struct aha_ccb *) PHYSTOKV( | |
642 | (_3btol(aha->aha_mbx.mbi[i].ccb_addr))); | |
643 | ||
644 | if ((stat = aha->aha_mbx.mbi[i].stat) != AHA_MBI_OK) { | |
645 | switch (stat) { | |
646 | case AHA_MBI_ABORT: | |
b33502d5 | 647 | #ifdef AHADEBUG |
519fb2b7 RG |
648 | if (aha_debug) |
649 | printf("abort"); | |
650 | #endif /*AHADEBUG */ | |
15637ed4 RG |
651 | ccb->host_stat = AHA_ABORTED; |
652 | break; | |
653 | ||
519fb2b7 RG |
654 | case AHA_MBI_UNKNOWN: |
655 | ccb = (struct aha_ccb *) 0; | |
b33502d5 | 656 | #ifdef AHADEBUG |
519fb2b7 RG |
657 | if (aha_debug) |
658 | printf("unknown ccb for abort "); | |
659 | #endif /*AHADEBUG */ | |
15637ed4 RG |
660 | /* may have missed it */ |
661 | /* no such ccb known for abort */ | |
662 | ||
519fb2b7 | 663 | case AHA_MBI_ERROR: |
15637ed4 RG |
664 | break; |
665 | ||
666 | default: | |
667 | panic("Impossible mbxi status"); | |
668 | ||
669 | } | |
b33502d5 | 670 | #ifdef AHADEBUG |
519fb2b7 RG |
671 | if (aha_debug && ccb) { |
672 | u_char *cp; | |
673 | cp = (u_char *) (&(ccb->scsi_cmd)); | |
674 | printf("op=%x %x %x %x %x %x\n", | |
675 | cp[0], cp[1], cp[2], | |
676 | cp[3], cp[4], cp[5]); | |
15637ed4 | 677 | printf("stat %x for mbi[%d]\n" |
519fb2b7 | 678 | ,aha->aha_mbx.mbi[i].stat, i); |
15637ed4 RG |
679 | printf("addr = 0x%x\n", ccb); |
680 | } | |
519fb2b7 | 681 | #endif /*AHADEBUG */ |
15637ed4 | 682 | } |
519fb2b7 RG |
683 | if (ccb) { |
684 | untimeout(aha_timeout, ccb); | |
685 | aha_done(unit, ccb); | |
15637ed4 | 686 | } |
519fb2b7 | 687 | aha->aha_mbx.mbi[i].stat = AHA_MBI_FREE; |
15637ed4 RG |
688 | } |
689 | } | |
519fb2b7 | 690 | return 1; |
15637ed4 RG |
691 | } |
692 | ||
519fb2b7 RG |
693 | /* |
694 | * A ccb (and hence a mbx-out is put onto the | |
695 | * free list. | |
696 | */ | |
697 | void | |
698 | aha_free_ccb(unit, ccb, flags) | |
699 | struct aha_ccb *ccb; | |
15637ed4 | 700 | { |
519fb2b7 | 701 | struct aha_data *aha = ahadata[unit]; |
15637ed4 | 702 | unsigned int opri; |
519fb2b7 RG |
703 | |
704 | if (!(flags & SCSI_NOMASK)) | |
705 | opri = splbio(); | |
706 | ||
707 | ccb->next = aha->aha_ccb_free; | |
708 | aha->aha_ccb_free = ccb; | |
15637ed4 | 709 | ccb->flags = CCB_FREE; |
519fb2b7 RG |
710 | /* |
711 | * If there were none, wake anybody waiting for | |
712 | * one to come free, starting with queued entries | |
713 | */ | |
15637ed4 | 714 | if (!ccb->next) { |
519fb2b7 | 715 | wakeup(&aha->aha_ccb_free); |
15637ed4 | 716 | } |
519fb2b7 | 717 | if (!(flags & SCSI_NOMASK)) |
15637ed4 RG |
718 | splx(opri); |
719 | } | |
720 | ||
519fb2b7 RG |
721 | /* |
722 | * Get a free ccb (and hence mbox-out entry) | |
723 | */ | |
15637ed4 | 724 | struct aha_ccb * |
519fb2b7 | 725 | aha_get_ccb(unit, flags) |
15637ed4 | 726 | { |
519fb2b7 | 727 | struct aha_data *aha = ahadata[unit]; |
15637ed4 RG |
728 | unsigned opri; |
729 | struct aha_ccb *rc; | |
730 | ||
519fb2b7 RG |
731 | if (!(flags & SCSI_NOMASK)) |
732 | opri = splbio(); | |
733 | /* | |
734 | * If we can and have to, sleep waiting for one | |
735 | * to come free | |
736 | */ | |
737 | while ((!(rc = aha->aha_ccb_free)) && (!(flags & SCSI_NOSLEEP))) { | |
738 | sleep(&aha->aha_ccb_free, PRIBIO); | |
15637ed4 | 739 | } |
519fb2b7 RG |
740 | if (rc) { |
741 | aha->aha_ccb_free = aha->aha_ccb_free->next; | |
15637ed4 RG |
742 | rc->flags = CCB_ACTIVE; |
743 | } | |
519fb2b7 | 744 | if (!(flags & SCSI_NOMASK)) |
15637ed4 | 745 | splx(opri); |
519fb2b7 | 746 | return (rc); |
15637ed4 | 747 | } |
519fb2b7 RG |
748 | |
749 | /* | |
750 | * We have a ccb which has been processed by the | |
751 | * adaptor, now we look to see how the operation | |
752 | * went. Wake up the owner if waiting | |
753 | */ | |
754 | void | |
755 | aha_done(unit, ccb) | |
756 | int unit; | |
757 | struct aha_ccb *ccb; | |
15637ed4 | 758 | { |
519fb2b7 RG |
759 | struct aha_data *aha = ahadata[unit]; |
760 | struct scsi_sense_data *s1, *s2; | |
761 | struct scsi_xfer *xs = ccb->xfer; | |
15637ed4 | 762 | |
519fb2b7 RG |
763 | SC_DEBUG(xs->sc_link, SDEV_DB2, ("aha_done\n")); |
764 | /* | |
765 | * Otherwise, put the results of the operation | |
766 | * into the xfer and call whoever started it | |
767 | */ | |
768 | if (!(xs->flags & INUSE)) { | |
769 | printf("aha%d: exiting but not in use!\n", unit); | |
15637ed4 RG |
770 | Debugger(); |
771 | } | |
519fb2b7 RG |
772 | if (((ccb->host_stat != AHA_OK) || (ccb->target_stat != SCSI_OK)) |
773 | && ((xs->flags & SCSI_ERR_OK) == 0)) { | |
774 | /* | |
775 | * We have an error, that we cannot ignore. | |
776 | */ | |
777 | s1 = (struct scsi_sense_data *) (((char *) (&ccb->scsi_cmd)) | |
778 | + ccb->scsi_cmd_length); | |
15637ed4 RG |
779 | s2 = &(xs->sense); |
780 | ||
519fb2b7 RG |
781 | if (ccb->host_stat) { |
782 | SC_DEBUG(xs->sc_link, SDEV_DB3, ("host err 0x%x\n", | |
783 | ccb->host_stat)); | |
784 | switch (ccb->host_stat) { | |
785 | case AHA_ABORTED: | |
786 | case AHA_SEL_TIMEOUT: /* No response */ | |
15637ed4 RG |
787 | xs->error = XS_TIMEOUT; |
788 | break; | |
789 | default: /* Other scsi protocol messes */ | |
790 | xs->error = XS_DRIVER_STUFFUP; | |
519fb2b7 RG |
791 | printf("aha%d:host_stat%x\n", |
792 | unit, ccb->host_stat); | |
15637ed4 | 793 | } |
519fb2b7 RG |
794 | } else { |
795 | SC_DEBUG(xs->sc_link, SDEV_DB3, ("target err 0x%x\n", | |
796 | ccb->target_stat)); | |
797 | switch (ccb->target_stat) { | |
15637ed4 | 798 | case 0x02: |
519fb2b7 RG |
799 | /* structure copy!!!!! */ |
800 | *s2 = *s1; | |
15637ed4 RG |
801 | xs->error = XS_SENSE; |
802 | break; | |
803 | case 0x08: | |
804 | xs->error = XS_BUSY; | |
805 | break; | |
806 | default: | |
519fb2b7 RG |
807 | printf("aha%d:target_stat%x\n", |
808 | unit, ccb->target_stat); | |
15637ed4 RG |
809 | xs->error = XS_DRIVER_STUFFUP; |
810 | } | |
811 | } | |
519fb2b7 RG |
812 | } else { |
813 | /* All went correctly OR errors expected */ | |
15637ed4 RG |
814 | xs->resid = 0; |
815 | } | |
816 | xs->flags |= ITSDONE; | |
519fb2b7 RG |
817 | aha_free_ccb(unit, ccb, xs->flags); |
818 | scsi_done(xs); | |
15637ed4 RG |
819 | } |
820 | ||
519fb2b7 RG |
821 | /* |
822 | * Start the board, ready for normal operation | |
823 | */ | |
824 | int | |
15637ed4 | 825 | aha_init(unit) |
519fb2b7 | 826 | int unit; |
15637ed4 | 827 | { |
519fb2b7 | 828 | struct aha_data *aha = ahadata[unit]; |
15637ed4 | 829 | unsigned char ad[3]; |
519fb2b7 | 830 | volatile int i, sts; |
15637ed4 | 831 | struct aha_config conf; |
b9f44595 RG |
832 | struct aha_inquire inquire; |
833 | struct aha_extbios extbios; | |
15637ed4 | 834 | |
519fb2b7 RG |
835 | /* |
836 | * reset board, If it doesn't respond, assume | |
837 | * that it's not there.. good for the probe | |
838 | */ | |
15637ed4 | 839 | |
519fb2b7 | 840 | outb(AHA_CTRL_STAT_PORT, AHA_HRST | AHA_SRST); |
15637ed4 | 841 | |
519fb2b7 RG |
842 | for (i = AHA_RESET_TIMEOUT; i; i--) { |
843 | sts = inb(AHA_CTRL_STAT_PORT); | |
844 | if (sts == (AHA_IDLE | AHA_INIT)) | |
15637ed4 | 845 | break; |
519fb2b7 | 846 | DELAY(1000); /* calibrated in msec */ |
15637ed4 | 847 | } |
519fb2b7 | 848 | if (i == 0) { |
b33502d5 | 849 | #ifdef AHADEBUG |
15637ed4 RG |
850 | if (aha_debug) |
851 | printf("aha_init: No answer from adaptec board\n"); | |
519fb2b7 RG |
852 | #endif /*AHADEBUG */ |
853 | return (ENXIO); | |
15637ed4 | 854 | } |
519fb2b7 | 855 | |
b9f44595 RG |
856 | /* |
857 | * Assume we have a board at this stage, do an adapter inquire | |
858 | * to find out what type of controller it is | |
859 | */ | |
860 | aha_cmd(unit, 0, sizeof(inquire), 1 ,&inquire, AHA_INQUIRE); | |
861 | #ifdef AHADEBUG | |
862 | printf("aha%d: inquire %x, %x, %x, %x\n", | |
863 | unit, | |
864 | inquire.boardid, inquire.spec_opts, | |
865 | inquire.revision_1, inquire.revision_2); | |
866 | #endif /* AHADEBUG */ | |
867 | /* | |
868 | * XXX The Buslogic 545S gets the AHA_INQUIRE command wrong, | |
869 | * they only return one byte which causes us to print an error, | |
870 | * so if the boardid comes back as 0x20, tell the user why they | |
871 | * get the "cmd/data port empty" message | |
872 | */ | |
873 | if (inquire.boardid == 0x20) { | |
874 | /* looks like a Buslogic 545 */ | |
875 | printf ("aha%d: above cmd/data port empty do to Buslogic 545\n", | |
876 | unit); | |
877 | } | |
878 | /* | |
6ebbc403 RG |
879 | * If we are a 1542C or 1542CF disable the extended bios so that the |
880 | * mailbox interface is unlocked. | |
881 | * No need to check the extended bios flags as some of the | |
882 | * extensions that cause us problems are not flagged in that byte. | |
b9f44595 RG |
883 | */ |
884 | if ((inquire.boardid == 0x43) || (inquire.boardid == 0x44)) { | |
885 | aha_cmd(unit, 0, sizeof(extbios), 0, &extbios, AHA_EXT_BIOS); | |
886 | #ifdef AHADEBUG | |
887 | printf("aha%d: extended bios flags %x\n", unit, extbios.flags); | |
888 | #endif /* AHADEBUG */ | |
6ebbc403 RG |
889 | printf("aha%d: 1542C/CF detected, unlocking mailbox\n"); |
890 | aha_cmd(unit, 2, 0, 0, 0, AHA_MBX_ENABLE, | |
891 | 0, extbios.mailboxlock); | |
b9f44595 | 892 | } |
519fb2b7 RG |
893 | |
894 | /* | |
895 | * setup dma channel from jumpers and save int | |
896 | * level | |
897 | */ | |
898 | printf("aha%d: reading board settings, ", unit); | |
15637ed4 | 899 | #define PRNT(x) printf(x) |
519fb2b7 RG |
900 | DELAY(1000); /* for Bustek 545 */ |
901 | aha_cmd(unit, 0, sizeof(conf), 0, &conf, AHA_CONF_GET); | |
902 | switch (conf.chan) { | |
903 | case CHAN0: | |
15637ed4 RG |
904 | outb(0x0b, 0x0c); |
905 | outb(0x0a, 0x00); | |
519fb2b7 | 906 | aha->aha_dma = 0; |
15637ed4 RG |
907 | PRNT("dma=0 "); |
908 | break; | |
519fb2b7 | 909 | case CHAN5: |
15637ed4 RG |
910 | outb(0xd6, 0xc1); |
911 | outb(0xd4, 0x01); | |
519fb2b7 | 912 | aha->aha_dma = 5; |
15637ed4 RG |
913 | PRNT("dma=5 "); |
914 | break; | |
519fb2b7 | 915 | case CHAN6: |
15637ed4 RG |
916 | outb(0xd6, 0xc2); |
917 | outb(0xd4, 0x02); | |
519fb2b7 | 918 | aha->aha_dma = 6; |
15637ed4 RG |
919 | PRNT("dma=6 "); |
920 | break; | |
519fb2b7 | 921 | case CHAN7: |
15637ed4 RG |
922 | outb(0xd6, 0xc3); |
923 | outb(0xd4, 0x03); | |
519fb2b7 | 924 | aha->aha_dma = 7; |
15637ed4 RG |
925 | PRNT("dma=7 "); |
926 | break; | |
927 | default: | |
928 | printf("illegal dma jumper setting\n"); | |
519fb2b7 | 929 | return (EIO); |
15637ed4 | 930 | } |
519fb2b7 RG |
931 | switch (conf.intr) { |
932 | case INT9: | |
933 | aha->aha_int = 9; | |
15637ed4 RG |
934 | PRNT("int=9 "); |
935 | break; | |
519fb2b7 RG |
936 | case INT10: |
937 | aha->aha_int = 10; | |
15637ed4 RG |
938 | PRNT("int=10 "); |
939 | break; | |
519fb2b7 RG |
940 | case INT11: |
941 | aha->aha_int = 11; | |
15637ed4 RG |
942 | PRNT("int=11 "); |
943 | break; | |
519fb2b7 RG |
944 | case INT12: |
945 | aha->aha_int = 12; | |
15637ed4 RG |
946 | PRNT("int=12 "); |
947 | break; | |
519fb2b7 RG |
948 | case INT14: |
949 | aha->aha_int = 14; | |
15637ed4 RG |
950 | PRNT("int=14 "); |
951 | break; | |
519fb2b7 RG |
952 | case INT15: |
953 | aha->aha_int = 15; | |
15637ed4 RG |
954 | PRNT("int=15 "); |
955 | break; | |
956 | default: | |
957 | printf("illegal int jumper setting\n"); | |
519fb2b7 | 958 | return (EIO); |
15637ed4 | 959 | } |
15637ed4 | 960 | |
519fb2b7 RG |
961 | /* who are we on the scsi bus? */ |
962 | aha->aha_scsi_dev = conf.scsi_dev; | |
15637ed4 | 963 | |
519fb2b7 RG |
964 | /* |
965 | * Change the bus on/off times to not clash with other dma users. | |
966 | */ | |
967 | aha_cmd(unit, 1, 0, 0, 0, AHA_BUS_ON_TIME_SET, 7); | |
968 | aha_cmd(unit, 1, 0, 0, 0, AHA_BUS_OFF_TIME_SET, 4); | |
969 | ||
970 | #ifdef TUNE_1542 | |
971 | /* | |
972 | * Initialize memory transfer speed | |
973 | * Not compiled in by default because it breaks some machines | |
974 | */ | |
975 | if (!(aha_set_bus_speed(unit))) { | |
976 | return (EIO); | |
15637ed4 | 977 | } |
9deb2487 RG |
978 | #else |
979 | printf ("\n"); | |
519fb2b7 RG |
980 | #endif /*TUNE_1542*/ |
981 | /* | |
982 | * Initialize mail box | |
983 | */ | |
984 | lto3b(KVTOPHYS(&aha->aha_mbx), ad); | |
15637ed4 | 985 | |
519fb2b7 RG |
986 | aha_cmd(unit, 4, 0, 0, 0, AHA_MBX_INIT, |
987 | AHA_MBX_SIZE, | |
988 | ad[0], | |
989 | ad[1], | |
990 | ad[2]); | |
15637ed4 | 991 | |
519fb2b7 RG |
992 | /* |
993 | * link the ccb's with the mbox-out entries and | |
994 | * into a free-list | |
995 | * this is a kludge but it works | |
996 | */ | |
997 | for (i = 0; i < AHA_MBX_SIZE; i++) { | |
998 | aha->aha_ccb[i].next = aha->aha_ccb_free; | |
999 | aha->aha_ccb_free = &aha->aha_ccb[i]; | |
1000 | aha->aha_ccb_free->flags = CCB_FREE; | |
1001 | aha->aha_ccb_free->mbx = &aha->aha_mbx.mbo[i]; | |
1002 | lto3b(KVTOPHYS(aha->aha_ccb_free), aha->aha_mbx.mbo[i].ccb_addr); | |
1003 | } | |
1004 | /* | |
1005 | * Note that we are going and return (to probe) | |
1006 | */ | |
1007 | return 0; | |
15637ed4 RG |
1008 | } |
1009 | ||
519fb2b7 RG |
1010 | void |
1011 | ahaminphys(bp) | |
1012 | struct buf *bp; | |
15637ed4 | 1013 | { |
519fb2b7 RG |
1014 | /* aha seems to explode with 17 segs (64k may require 17 segs) */ |
1015 | /* on old boards so use a max of 16 segs if you have problems here */ | |
1016 | if (bp->b_bcount > ((AHA_NSEG - 1) * PAGESIZ)) { | |
15637ed4 RG |
1017 | bp->b_bcount = ((AHA_NSEG - 1) * PAGESIZ); |
1018 | } | |
1019 | } | |
519fb2b7 RG |
1020 | |
1021 | /* | |
1022 | * start a scsi operation given the command and | |
1023 | * the data address. Also needs the unit, target | |
1024 | * and lu | |
1025 | */ | |
1026 | int32 | |
1027 | aha_scsi_cmd(xs) | |
1028 | struct scsi_xfer *xs; | |
15637ed4 | 1029 | { |
519fb2b7 RG |
1030 | struct scsi_link *sc_link = xs->sc_link; |
1031 | int unit = sc_link->adapter_unit; | |
1032 | struct aha_data *aha = ahadata[unit]; | |
1033 | struct scsi_sense_data *s1, *s2; | |
15637ed4 RG |
1034 | struct aha_ccb *ccb; |
1035 | struct aha_scat_gath *sg; | |
519fb2b7 RG |
1036 | int seg; /* scatter gather seg being worked on */ |
1037 | int i = 0; | |
1038 | int rc = 0; | |
1039 | int thiskv; | |
1040 | int thisphys, nextphys; | |
1041 | int bytes_this_seg, bytes_this_page, datalen, flags; | |
1042 | struct iovec *iovp; | |
1043 | int s; | |
1044 | ||
1045 | SC_DEBUG(xs->sc_link, SDEV_DB2, ("aha_scsi_cmd\n")); | |
1046 | /* | |
1047 | * get a ccb (mbox-out) to use. If the transfer | |
1048 | * is from a buf (possibly from interrupt time) | |
1049 | * then we can't allow it to sleep | |
1050 | */ | |
15637ed4 | 1051 | flags = xs->flags; |
519fb2b7 | 1052 | if (!(ccb = aha_get_ccb(unit, flags))) { |
15637ed4 | 1053 | xs->error = XS_DRIVER_STUFFUP; |
519fb2b7 | 1054 | return (TRY_AGAIN_LATER); |
15637ed4 | 1055 | } |
15637ed4 | 1056 | if (ccb->mbx->cmd != AHA_MBO_FREE) |
519fb2b7 | 1057 | printf("aha%d: MBO not free\n", unit); |
15637ed4 | 1058 | |
519fb2b7 RG |
1059 | /* |
1060 | * Put all the arguments for the xfer in the ccb | |
1061 | */ | |
1062 | ccb->xfer = xs; | |
1063 | if (flags & SCSI_RESET) { | |
1064 | ccb->opcode = AHA_RESET_CCB; | |
1065 | } else { | |
15637ed4 | 1066 | /* can't use S/G if zero length */ |
519fb2b7 RG |
1067 | ccb->opcode = (xs->datalen ? |
1068 | AHA_INIT_SCAT_GATH_CCB | |
1069 | : AHA_INITIATOR_CCB); | |
15637ed4 | 1070 | } |
519fb2b7 RG |
1071 | ccb->target = sc_link->target; |
1072 | ccb->data_out = 0; | |
1073 | ccb->data_in = 0; | |
1074 | ccb->lun = sc_link->lun; | |
1075 | ccb->scsi_cmd_length = xs->cmdlen; | |
1076 | ccb->req_sense_length = sizeof(ccb->scsi_sense); | |
1077 | ||
1078 | if ((xs->datalen) && (!(flags & SCSI_RESET))) { | |
1079 | /* can use S/G only if not zero length */ | |
1080 | lto3b(KVTOPHYS(ccb->scat_gath), ccb->data_addr); | |
1081 | sg = ccb->scat_gath; | |
1082 | seg = 0; | |
1083 | #ifdef TFS_ONLY | |
1084 | if (flags & SCSI_DATA_UIO) { | |
1085 | iovp = ((struct uio *) xs->data)->uio_iov; | |
1086 | datalen = ((struct uio *) xs->data)->uio_iovcnt; | |
1087 | while ((datalen) && (seg < AHA_NSEG)) { | |
1088 | lto3b(iovp->iov_base, sg->seg_addr); | |
1089 | lto3b(iovp->iov_len, sg->seg_len); | |
1090 | SC_DEBUGN(xs->sc_link, SDEV_DB4, ("UIO(0x%x@0x%x)" | |
1091 | ,iovp->iov_len | |
1092 | ,iovp->iov_base)); | |
15637ed4 RG |
1093 | sg++; |
1094 | iovp++; | |
1095 | seg++; | |
1096 | datalen--; | |
1097 | } | |
519fb2b7 RG |
1098 | } else |
1099 | #endif /*TFS_ONLY */ | |
15637ed4 | 1100 | { |
519fb2b7 RG |
1101 | /* |
1102 | * Set up the scatter gather block | |
1103 | */ | |
1104 | ||
1105 | SC_DEBUG(xs->sc_link, SDEV_DB4, | |
1106 | ("%d @0x%x:- ", xs->datalen, xs->data)); | |
1107 | datalen = xs->datalen; | |
1108 | thiskv = (int) xs->data; | |
1109 | thisphys = KVTOPHYS(thiskv); | |
1110 | ||
1111 | while ((datalen) && (seg < AHA_NSEG)) { | |
1112 | bytes_this_seg = 0; | |
1113 | ||
15637ed4 | 1114 | /* put in the base address */ |
519fb2b7 RG |
1115 | lto3b(thisphys, sg->seg_addr); |
1116 | ||
1117 | SC_DEBUGN(xs->sc_link, SDEV_DB4, | |
1118 | ("0x%x", thisphys)); | |
1119 | ||
15637ed4 | 1120 | /* do it at least once */ |
519fb2b7 RG |
1121 | nextphys = thisphys; |
1122 | while ((datalen) && (thisphys == nextphys)) { | |
1123 | /* | |
1124 | * This page is contiguous (physically) | |
1125 | * with the the last, just extend the | |
1126 | * length | |
1127 | */ | |
1128 | /* check it fits on the ISA bus */ | |
1129 | if (thisphys > 0xFFFFFF) | |
1130 | { | |
1131 | printf("aha%d: DMA beyond" | |
1132 | " end Of ISA\n", unit); | |
1133 | xs->error = XS_DRIVER_STUFFUP; | |
1134 | aha_free_ccb(unit, ccb, flags); | |
1135 | return (HAD_ERROR); | |
1136 | } | |
15637ed4 RG |
1137 | /** how far to the end of the page ***/ |
1138 | nextphys = (thisphys & (~(PAGESIZ - 1))) | |
519fb2b7 RG |
1139 | + PAGESIZ; |
1140 | bytes_this_page = nextphys - thisphys; | |
15637ed4 | 1141 | /**** or the data ****/ |
519fb2b7 RG |
1142 | bytes_this_page = min(bytes_this_page |
1143 | ,datalen); | |
1144 | bytes_this_seg += bytes_this_page; | |
1145 | datalen -= bytes_this_page; | |
1146 | ||
15637ed4 | 1147 | /**** get more ready for the next page ****/ |
519fb2b7 RG |
1148 | thiskv = (thiskv & (~(PAGESIZ - 1))) |
1149 | + PAGESIZ; | |
1150 | if (datalen) | |
15637ed4 RG |
1151 | thisphys = KVTOPHYS(thiskv); |
1152 | } | |
519fb2b7 RG |
1153 | /* |
1154 | * next page isn't contiguous, finish the seg | |
1155 | */ | |
1156 | SC_DEBUGN(xs->sc_link, SDEV_DB4, | |
1157 | ("(0x%x)", bytes_this_seg)); | |
1158 | lto3b(bytes_this_seg, sg->seg_len); | |
15637ed4 RG |
1159 | sg++; |
1160 | seg++; | |
1161 | } | |
1162 | } | |
519fb2b7 RG |
1163 | lto3b(seg * sizeof(struct aha_scat_gath), ccb->data_length); |
1164 | SC_DEBUGN(xs->sc_link, SDEV_DB4, ("\n")); | |
1165 | ||
1166 | if (datalen) { /* there's still data, must have run out of segs! */ | |
98639498 | 1167 | printf("aha%d: aha_scsi_cmd, more than %d DMA segs\n", |
519fb2b7 | 1168 | unit, AHA_NSEG); |
15637ed4 | 1169 | xs->error = XS_DRIVER_STUFFUP; |
519fb2b7 RG |
1170 | aha_free_ccb(unit, ccb, flags); |
1171 | return (HAD_ERROR); | |
15637ed4 | 1172 | } |
519fb2b7 RG |
1173 | } else { /* No data xfer, use non S/G values */ |
1174 | lto3b(0, ccb->data_addr); | |
1175 | lto3b(0, ccb->data_length); | |
15637ed4 | 1176 | } |
519fb2b7 RG |
1177 | lto3b(0, ccb->link_addr); |
1178 | /* | |
1179 | * Put the scsi command in the ccb and start it | |
1180 | */ | |
1181 | if (!(flags & SCSI_RESET)) | |
15637ed4 | 1182 | bcopy(xs->cmd, &ccb->scsi_cmd, ccb->scsi_cmd_length); |
519fb2b7 RG |
1183 | if (!(flags & SCSI_NOMASK)) { |
1184 | s = splbio(); /* stop instant timeouts */ | |
1185 | timeout(aha_timeout, ccb, (xs->timeout * hz) / 1000); | |
15637ed4 | 1186 | aha_startmbx(ccb->mbx); |
519fb2b7 RG |
1187 | /* |
1188 | * Usually return SUCCESSFULLY QUEUED | |
1189 | */ | |
15637ed4 | 1190 | splx(s); |
519fb2b7 RG |
1191 | SC_DEBUG(xs->sc_link, SDEV_DB3, ("sent\n")); |
1192 | return (SUCCESSFULLY_QUEUED); | |
15637ed4 RG |
1193 | } |
1194 | aha_startmbx(ccb->mbx); | |
519fb2b7 | 1195 | SC_DEBUG(xs->sc_link, SDEV_DB3, ("cmd sent, waiting\n")); |
15637ed4 | 1196 | |
519fb2b7 RG |
1197 | /* |
1198 | * If we can't use interrupts, poll on completion | |
1199 | */ | |
1200 | return (aha_poll(unit, xs, ccb)); /* only during boot */ | |
15637ed4 | 1201 | } |
15637ed4 | 1202 | |
519fb2b7 RG |
1203 | /* |
1204 | * Poll a particular unit, looking for a particular xs | |
1205 | */ | |
1206 | int | |
1207 | aha_poll(unit, xs, ccb) | |
1208 | int unit; | |
1209 | struct scsi_xfer *xs; | |
1210 | struct aha_ccb *ccb; | |
1211 | { | |
1212 | struct aha_data *aha = ahadata[unit]; | |
1213 | int done = 0; | |
1214 | int count = xs->timeout; | |
1215 | u_char stat; | |
1216 | ||
1217 | /*timeouts are in msec, so we loop in 1000uSec cycles */ | |
1218 | while (count) { | |
1219 | /* | |
1220 | * If we had interrupts enabled, would we | |
1221 | * have got an interrupt? | |
1222 | */ | |
1223 | stat = inb(AHA_INTR_PORT); | |
1224 | if (stat & AHA_ANY_INTR) { | |
1225 | ahaintr(unit); | |
15637ed4 | 1226 | } |
519fb2b7 RG |
1227 | if (xs->flags & ITSDONE) { |
1228 | break; | |
15637ed4 | 1229 | } |
519fb2b7 RG |
1230 | DELAY(1000); /* only happens in boot so ok */ |
1231 | count--; | |
1232 | } | |
1233 | if (count == 0) { | |
1234 | /* | |
1235 | * We timed out, so call the timeout handler | |
1236 | * manually, accout for the fact that the | |
1237 | * clock is not running yet by taking out the | |
1238 | * clock queue entry it makes | |
1239 | */ | |
1240 | aha_timeout(ccb); | |
1241 | ||
1242 | /* | |
1243 | * because we are polling, | |
1244 | * take out the timeout entry aha_timeout made | |
1245 | */ | |
1246 | untimeout(aha_timeout, ccb); | |
1247 | count = 2000; | |
1248 | while (count) { | |
1249 | /* | |
1250 | * Once again, wait for the int bit | |
1251 | */ | |
1252 | stat = inb(AHA_INTR_PORT); | |
1253 | if (stat & AHA_ANY_INTR) { | |
1254 | ahaintr(unit); | |
15637ed4 | 1255 | } |
519fb2b7 RG |
1256 | if (xs->flags & ITSDONE) { |
1257 | break; | |
15637ed4 | 1258 | } |
519fb2b7 RG |
1259 | DELAY(1000); /* only happens in boot so ok */ |
1260 | count--; | |
1261 | } | |
1262 | if (count == 0) { | |
1263 | /* | |
1264 | * We timed out again.. this is bad | |
1265 | * Notice that this time there is no | |
1266 | * clock queue entry to remove | |
1267 | */ | |
1268 | aha_timeout(ccb); | |
15637ed4 RG |
1269 | } |
1270 | } | |
519fb2b7 RG |
1271 | if (xs->error) |
1272 | return (HAD_ERROR); | |
1273 | return (COMPLETE); | |
1274 | ||
15637ed4 RG |
1275 | } |
1276 | ||
519fb2b7 RG |
1277 | #ifdef TUNE_1542 |
1278 | /* | |
1279 | * Try all the speeds from slowest to fastest.. if it finds a | |
1280 | * speed that fails, back off one notch from the last working | |
1281 | * speed (unless there is no other notch). | |
1282 | * Returns the nSEC value of the time used | |
1283 | * or 0 if it could get a working speed (or the NEXT speed | |
1284 | * failed) | |
1285 | */ | |
15637ed4 RG |
1286 | static struct bus_speed |
1287 | { | |
1288 | char arg; | |
1289 | int nsecs; | |
1290 | }aha_bus_speeds[] = | |
1291 | { | |
1292 | {0x88,100}, | |
1293 | {0x99,150}, | |
1294 | {0xaa,200}, | |
1295 | {0xbb,250}, | |
1296 | {0xcc,300}, | |
1297 | {0xdd,350}, | |
1298 | {0xee,400}, | |
1299 | {0xff,450} | |
1300 | }; | |
15637ed4 | 1301 | |
519fb2b7 RG |
1302 | int |
1303 | aha_set_bus_speed(unit) | |
1304 | int unit; | |
15637ed4 | 1305 | { |
519fb2b7 RG |
1306 | int speed; |
1307 | int lastworking; | |
1308 | int retval,retval2; | |
1309 | struct aha_data *aha = ahadata[unit]; | |
15637ed4 | 1310 | |
519fb2b7 RG |
1311 | lastworking = -1; |
1312 | speed = 7; | |
1313 | while (1) { | |
1314 | retval = aha_bus_speed_check(unit,speed); | |
1315 | if(retval != 0) { | |
1316 | lastworking = speed; | |
1317 | } | |
1318 | if((retval == 0) || (speed == 0)) { | |
1319 | if(lastworking == -1) { | |
1320 | printf("No working bus speed for aha154X\n"); | |
1321 | return 0; | |
1322 | } | |
1323 | printf("%d nSEC ok, using " | |
1324 | ,aha_bus_speeds[lastworking].nsecs); | |
1325 | if(lastworking == 7) { /* is slowest already */ | |
1326 | printf("marginal "); | |
1327 | } else { | |
1328 | lastworking++; | |
1329 | } | |
1330 | retval2 = aha_bus_speed_check(unit,lastworking); | |
1331 | if(retval2 == 0) { | |
1332 | printf("test retry failed.. aborting.\n"); | |
1333 | return 0; | |
1334 | } | |
1335 | printf("%d nSEC\n",retval2); | |
1336 | return retval2 ; | |
1337 | ||
1338 | } | |
1339 | speed--; | |
15637ed4 RG |
1340 | } |
1341 | } | |
1342 | ||
519fb2b7 RG |
1343 | /* |
1344 | * Set the DMA speed to the Nth speed and try an xfer. If it | |
1345 | * fails return 0, if it succeeds return the nSec value selected | |
1346 | * If there is no such speed return HAD_ERROR. | |
1347 | */ | |
1348 | static char aha_test_string[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyz!@"; | |
15637ed4 | 1349 | |
519fb2b7 RG |
1350 | int |
1351 | aha_bus_speed_check(unit, speed) | |
1352 | int unit, speed; | |
15637ed4 | 1353 | { |
519fb2b7 RG |
1354 | int numspeeds = sizeof(aha_bus_speeds) / sizeof(struct bus_speed); |
1355 | int loopcount; | |
1356 | u_char ad[3]; | |
1357 | struct aha_data *aha = ahadata[unit]; | |
1358 | ||
1359 | /* | |
1360 | * Check we have such an entry | |
1361 | */ | |
1362 | if (speed >= numspeeds) | |
1363 | return (HAD_ERROR); /* illegal speed */ | |
15637ed4 | 1364 | |
519fb2b7 RG |
1365 | /* |
1366 | * Set the dma-speed | |
1367 | */ | |
1368 | aha_cmd(unit, 1, 0, 0, 0, AHA_SPEED_SET, aha_bus_speeds[speed].arg); | |
b33502d5 | 1369 | |
519fb2b7 RG |
1370 | /* |
1371 | * put the test data into the buffer and calculate | |
1372 | * it's address. Read it onto the board | |
1373 | */ | |
1374 | lto3b(KVTOPHYS(aha_scratch_buf), ad); | |
1375 | for(loopcount = 2000;loopcount;loopcount--) | |
15637ed4 | 1376 | { |
519fb2b7 RG |
1377 | strcpy(aha_scratch_buf, aha_test_string); |
1378 | ||
1379 | aha_cmd(unit, 3, 0, 0, 0, AHA_WRITE_FIFO, ad[0], ad[1], ad[2]); | |
1380 | ||
1381 | /* | |
1382 | * clear the buffer then copy the contents back from the | |
1383 | * board. | |
1384 | */ | |
1385 | bzero(aha_scratch_buf, 54); /* 54 bytes transfered by test */ | |
1386 | ||
1387 | aha_cmd(unit, 3, 0, 0, 0, AHA_READ_FIFO, ad[0], ad[1], ad[2]); | |
1388 | ||
1389 | /* | |
1390 | * Compare the original data and the final data and | |
1391 | * return the correct value depending upon the result | |
1392 | */ | |
1393 | if (strcmp(aha_test_string, aha_scratch_buf)) | |
1394 | return 0; /* failed test */ | |
1395 | } | |
1396 | /* copy succeded assume speed ok */ | |
1397 | ||
1398 | return (aha_bus_speeds[speed].nsecs); | |
1399 | ||
1400 | } | |
1401 | #endif /*TUNE_1542*/ | |
1402 | ||
1403 | void | |
1404 | aha_timeout(struct aha_ccb * ccb) | |
1405 | { | |
1406 | int unit; | |
1407 | int s = splbio(); | |
1408 | struct aha_data *aha; | |
1409 | ||
1410 | unit = ccb->xfer->sc_link->adapter_unit; | |
1411 | aha = ahadata[unit]; | |
1412 | sc_print_addr(ccb->xfer->sc_link); | |
1413 | printf("timed out "); | |
1414 | ||
1415 | /* | |
1416 | * If The ccb's mbx is not free, then | |
1417 | * the board has gone south | |
1418 | */ | |
1419 | if (ccb->mbx->cmd != AHA_MBO_FREE) { | |
1420 | printf("\nadapter not taking commands.. frozen?!\n"); | |
b33502d5 | 1421 | Debugger(); |
15637ed4 | 1422 | } |
519fb2b7 RG |
1423 | /* |
1424 | * If it has been through before, then | |
1425 | * a previous abort has failed, don't | |
1426 | * try abort again | |
1427 | */ | |
1428 | if (ccb->flags == CCB_ABORTED) { | |
1429 | /* abort timed out */ | |
b33502d5 | 1430 | printf(" AGAIN\n"); |
519fb2b7 | 1431 | ccb->xfer->retries = 0; /* I MEAN IT ! */ |
b33502d5 | 1432 | ccb->host_stat = AHA_ABORTED; |
519fb2b7 RG |
1433 | aha_done(unit, ccb); |
1434 | } else { | |
1435 | /* abort the operation that has timed out */ | |
b33502d5 RG |
1436 | printf("\n"); |
1437 | aha_abortmbx(ccb->mbx); | |
519fb2b7 RG |
1438 | /* 4 secs for the abort */ |
1439 | timeout(aha_timeout, ccb, 4 * hz); | |
b33502d5 | 1440 | ccb->flags = CCB_ABORTED; |
519fb2b7 | 1441 | } splx(s); |
15637ed4 | 1442 | } |
519fb2b7 | 1443 | #endif /* KERNEL */ |