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