Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * ultrastor.c Copyright (C) 1992 David B. Gentzel | |
3 | * Low-level SCSI driver for UltraStor 14F, 24F, and 34F | |
4 | * by David B. Gentzel, Whitfield Software Services, Carnegie, PA | |
5 | * (gentzel@nova.enet.dec.com) | |
6 | * scatter/gather added by Scott Taylor (n217cg@tamuts.tamu.edu) | |
7 | * 24F and multiple command support by John F. Carr (jfc@athena.mit.edu) | |
8 | * John's work modified by Caleb Epstein (cae@jpmorgan.com) and | |
9 | * Eric Youngdale (ericy@cais.com). | |
10 | * Thanks to UltraStor for providing the necessary documentation | |
11 | * | |
12 | * This is an old driver, for the 14F and 34F you should be using the | |
13 | * u14-34f driver instead. | |
14 | */ | |
15 | ||
16 | /* | |
17 | * TODO: | |
18 | * 1. Find out why scatter/gather is limited to 16 requests per command. | |
19 | * This is fixed, at least on the 24F, as of version 1.12 - CAE. | |
20 | * 2. Look at command linking (mscp.command_link and | |
21 | * mscp.command_link_id). (Does not work with many disks, | |
22 | * and no performance increase. ERY). | |
23 | * 3. Allow multiple adapters. | |
24 | */ | |
25 | ||
26 | /* | |
27 | * NOTES: | |
28 | * The UltraStor 14F, 24F, and 34F are a family of intelligent, high | |
29 | * performance SCSI-2 host adapters. They all support command queueing | |
30 | * and scatter/gather I/O. Some of them can also emulate the standard | |
31 | * WD1003 interface for use with OS's which don't support SCSI. Here | |
32 | * is the scoop on the various models: | |
33 | * 14F - ISA first-party DMA HA with floppy support and WD1003 emulation. | |
34 | * 14N - ISA HA with floppy support. I think that this is a non-DMA | |
35 | * HA. Nothing further known. | |
36 | * 24F - EISA Bus Master HA with floppy support and WD1003 emulation. | |
37 | * 34F - VL-Bus Bus Master HA with floppy support (no WD1003 emulation). | |
38 | * | |
39 | * The 14F, 24F, and 34F are supported by this driver. | |
40 | * | |
41 | * Places flagged with a triple question-mark are things which are either | |
42 | * unfinished, questionable, or wrong. | |
43 | */ | |
44 | ||
45 | /* Changes from version 1.11 alpha to 1.12 | |
46 | * | |
47 | * Increased the size of the scatter-gather list to 33 entries for | |
48 | * the 24F adapter (it was 16). I don't have the specs for the 14F | |
49 | * or the 34F, so they may support larger s-g lists as well. | |
50 | * | |
51 | * Caleb Epstein <cae@jpmorgan.com> | |
52 | */ | |
53 | ||
54 | /* Changes from version 1.9 to 1.11 | |
55 | * | |
56 | * Patches to bring this driver up to speed with the default kernel | |
57 | * driver which supports only the 14F and 34F adapters. This version | |
58 | * should compile cleanly into 0.99.13, 0.99.12 and probably 0.99.11. | |
59 | * | |
60 | * Fixes from Eric Youngdale to fix a few possible race conditions and | |
61 | * several problems with bit testing operations (insufficient | |
62 | * parentheses). | |
63 | * | |
64 | * Removed the ultrastor_abort() and ultrastor_reset() functions | |
65 | * (enclosed them in #if 0 / #endif). These functions, at least on | |
66 | * the 24F, cause the SCSI bus to do odd things and generally lead to | |
67 | * kernel panics and machine hangs. This is like the Adaptec code. | |
68 | * | |
69 | * Use check/snarf_region for 14f, 34f to avoid I/O space address conflicts. | |
70 | */ | |
71 | ||
72 | /* Changes from version 1.8 to version 1.9 | |
73 | * | |
74 | * 0.99.11 patches (cae@jpmorgan.com) */ | |
75 | ||
76 | /* Changes from version 1.7 to version 1.8 | |
77 | * | |
78 | * Better error reporting. | |
79 | */ | |
80 | ||
81 | /* Changes from version 1.6 to version 1.7 | |
82 | * | |
83 | * Removed CSIR command code. | |
84 | * | |
85 | * Better race condition avoidance (xchgb function added). | |
86 | * | |
87 | * Set ICM and OGM status to zero at probe (24F) | |
88 | * | |
89 | * reset sends soft reset to UltraStor adapter | |
90 | * | |
91 | * reset adapter if adapter interrupts with an invalid MSCP address | |
92 | * | |
93 | * handle aborted command interrupt (24F) | |
94 | * | |
95 | */ | |
96 | ||
97 | /* Changes from version 1.5 to version 1.6: | |
98 | * | |
99 | * Read MSCP address from ICM _before_ clearing the interrupt flag. | |
100 | * This fixes a race condition. | |
101 | */ | |
102 | ||
103 | /* Changes from version 1.4 to version 1.5: | |
104 | * | |
105 | * Abort now calls done when multiple commands are enabled. | |
106 | * | |
107 | * Clear busy when aborted command finishes, not when abort is called. | |
108 | * | |
109 | * More debugging messages for aborts. | |
110 | */ | |
111 | ||
112 | /* Changes from version 1.3 to version 1.4: | |
113 | * | |
114 | * Enable automatic request of sense data on error (requires newer version | |
115 | * of scsi.c to be useful). | |
116 | * | |
117 | * Fix PORT_OVERRIDE for 14F. | |
118 | * | |
119 | * Fix abort and reset to work properly (config.aborted wasn't cleared | |
120 | * after it was tested, so after a command abort no further commands would | |
121 | * work). | |
122 | * | |
123 | * Boot time test to enable SCSI bus reset (defaults to not allowing reset). | |
124 | * | |
125 | * Fix test for OGM busy -- the busy bit is in different places on the 24F. | |
126 | * | |
127 | * Release ICM slot by clearing first byte on 24F. | |
128 | */ | |
129 | ||
130 | #include <linux/module.h> | |
131 | #include <linux/blkdev.h> | |
132 | #include <linux/interrupt.h> | |
133 | #include <linux/stddef.h> | |
134 | #include <linux/string.h> | |
135 | #include <linux/kernel.h> | |
136 | #include <linux/ioport.h> | |
137 | #include <linux/proc_fs.h> | |
138 | #include <linux/spinlock.h> | |
139 | #include <linux/stat.h> | |
140 | #include <linux/bitops.h> | |
ca444564 | 141 | #include <linux/delay.h> |
1da177e4 LT |
142 | |
143 | #include <asm/io.h> | |
144 | #include <asm/system.h> | |
145 | #include <asm/dma.h> | |
146 | ||
147 | #define ULTRASTOR_PRIVATE /* Get the private stuff from ultrastor.h */ | |
148 | #include "scsi.h" | |
149 | #include <scsi/scsi_host.h> | |
150 | #include "ultrastor.h" | |
151 | ||
152 | #define FALSE 0 | |
153 | #define TRUE 1 | |
154 | ||
155 | #ifndef ULTRASTOR_DEBUG | |
156 | #define ULTRASTOR_DEBUG (UD_ABORT|UD_CSIR|UD_RESET) | |
157 | #endif | |
158 | ||
159 | #define VERSION "1.12" | |
160 | ||
161 | #define PACKED __attribute__((packed)) | |
162 | #define ALIGNED(x) __attribute__((aligned(x))) | |
163 | ||
164 | ||
165 | /* The 14F uses an array of 4-byte ints for its scatter/gather list. | |
166 | The data can be unaligned, but need not be. It's easier to give | |
167 | the list normal alignment since it doesn't need to fit into a | |
168 | packed structure. */ | |
169 | ||
170 | typedef struct { | |
171 | u32 address; | |
172 | u32 num_bytes; | |
173 | } ultrastor_sg_list; | |
174 | ||
175 | ||
176 | /* MailBox SCSI Command Packet. Basic command structure for communicating | |
177 | with controller. */ | |
178 | struct mscp { | |
179 | unsigned char opcode: 3; /* type of command */ | |
180 | unsigned char xdir: 2; /* data transfer direction */ | |
181 | unsigned char dcn: 1; /* disable disconnect */ | |
182 | unsigned char ca: 1; /* use cache (if available) */ | |
183 | unsigned char sg: 1; /* scatter/gather operation */ | |
184 | unsigned char target_id: 3; /* target SCSI id */ | |
185 | unsigned char ch_no: 2; /* SCSI channel (always 0 for 14f) */ | |
186 | unsigned char lun: 3; /* logical unit number */ | |
187 | unsigned int transfer_data PACKED; /* transfer data pointer */ | |
188 | unsigned int transfer_data_length PACKED; /* length in bytes */ | |
189 | unsigned int command_link PACKED; /* for linking command chains */ | |
190 | unsigned char scsi_command_link_id; /* identifies command in chain */ | |
191 | unsigned char number_of_sg_list; /* (if sg is set) 8 bytes per list */ | |
192 | unsigned char length_of_sense_byte; | |
193 | unsigned char length_of_scsi_cdbs; /* 6, 10, or 12 */ | |
194 | unsigned char scsi_cdbs[12]; /* SCSI commands */ | |
195 | unsigned char adapter_status; /* non-zero indicates HA error */ | |
196 | unsigned char target_status; /* non-zero indicates target error */ | |
197 | u32 sense_data PACKED; | |
198 | /* The following fields are for software only. They are included in | |
199 | the MSCP structure because they are associated with SCSI requests. */ | |
b4620233 HK |
200 | void (*done) (struct scsi_cmnd *); |
201 | struct scsi_cmnd *SCint; | |
1da177e4 LT |
202 | ultrastor_sg_list sglist[ULTRASTOR_24F_MAX_SG]; /* use larger size for 24F */ |
203 | }; | |
204 | ||
205 | ||
206 | /* Port addresses (relative to the base address) */ | |
207 | #define U14F_PRODUCT_ID(port) ((port) + 0x4) | |
208 | #define CONFIG(port) ((port) + 0x6) | |
209 | ||
210 | /* Port addresses relative to the doorbell base address. */ | |
211 | #define LCL_DOORBELL_MASK(port) ((port) + 0x0) | |
212 | #define LCL_DOORBELL_INTR(port) ((port) + 0x1) | |
213 | #define SYS_DOORBELL_MASK(port) ((port) + 0x2) | |
214 | #define SYS_DOORBELL_INTR(port) ((port) + 0x3) | |
215 | ||
216 | ||
217 | /* Used to store configuration info read from config i/o registers. Most of | |
218 | this is not used yet, but might as well save it. | |
219 | ||
220 | This structure also holds port addresses that are not at the same offset | |
221 | on the 14F and 24F. | |
222 | ||
223 | This structure holds all data that must be duplicated to support multiple | |
224 | adapters. */ | |
225 | ||
226 | static struct ultrastor_config | |
227 | { | |
228 | unsigned short port_address; /* base address of card */ | |
229 | unsigned short doorbell_address; /* base address of doorbell CSRs */ | |
230 | unsigned short ogm_address; /* base address of OGM */ | |
231 | unsigned short icm_address; /* base address of ICM */ | |
232 | const void *bios_segment; | |
233 | unsigned char interrupt: 4; | |
234 | unsigned char dma_channel: 3; | |
235 | unsigned char bios_drive_number: 1; | |
236 | unsigned char heads; | |
237 | unsigned char sectors; | |
238 | unsigned char ha_scsi_id: 3; | |
239 | unsigned char subversion: 4; | |
240 | unsigned char revision; | |
241 | /* The slot number is used to distinguish the 24F (slot != 0) from | |
242 | the 14F and 34F (slot == 0). */ | |
243 | unsigned char slot; | |
244 | ||
245 | #ifdef PRINT_U24F_VERSION | |
246 | volatile int csir_done; | |
247 | #endif | |
248 | ||
249 | /* A pool of MSCP structures for this adapter, and a bitmask of | |
250 | busy structures. (If ULTRASTOR_14F_MAX_CMDS == 1, a 1 byte | |
251 | busy flag is used instead.) */ | |
252 | ||
253 | #if ULTRASTOR_MAX_CMDS == 1 | |
254 | unsigned char mscp_busy; | |
255 | #else | |
256 | unsigned long mscp_free; | |
257 | #endif | |
258 | volatile unsigned char aborted[ULTRASTOR_MAX_CMDS]; | |
259 | struct mscp mscp[ULTRASTOR_MAX_CMDS]; | |
260 | } config = {0}; | |
261 | ||
262 | /* Set this to 1 to reset the SCSI bus on error. */ | |
263 | static int ultrastor_bus_reset; | |
264 | ||
265 | ||
266 | /* Allowed BIOS base addresses (NULL indicates reserved) */ | |
267 | static const void *const bios_segment_table[8] = { | |
268 | NULL, (void *)0xC4000, (void *)0xC8000, (void *)0xCC000, | |
269 | (void *)0xD0000, (void *)0xD4000, (void *)0xD8000, (void *)0xDC000, | |
270 | }; | |
271 | ||
272 | /* Allowed IRQs for 14f */ | |
273 | static const unsigned char interrupt_table_14f[4] = { 15, 14, 11, 10 }; | |
274 | ||
275 | /* Allowed DMA channels for 14f (0 indicates reserved) */ | |
276 | static const unsigned char dma_channel_table_14f[4] = { 5, 6, 7, 0 }; | |
277 | ||
278 | /* Head/sector mappings allowed by 14f */ | |
279 | static const struct { | |
280 | unsigned char heads; | |
281 | unsigned char sectors; | |
282 | } mapping_table[4] = { { 16, 63 }, { 64, 32 }, { 64, 63 }, { 64, 32 } }; | |
283 | ||
284 | #ifndef PORT_OVERRIDE | |
285 | /* ??? A probe of address 0x310 screws up NE2000 cards */ | |
286 | static const unsigned short ultrastor_ports_14f[] = { | |
287 | 0x330, 0x340, /*0x310,*/ 0x230, 0x240, 0x210, 0x130, 0x140, | |
288 | }; | |
289 | #endif | |
290 | ||
c7bec5ab | 291 | static void ultrastor_interrupt(void *); |
7d12e780 | 292 | static irqreturn_t do_ultrastor_interrupt(int, void *); |
b4620233 | 293 | static inline void build_sg_list(struct mscp *, struct scsi_cmnd *SCpnt); |
1da177e4 LT |
294 | |
295 | ||
296 | /* Always called with host lock held */ | |
297 | ||
298 | static inline int find_and_clear_bit_16(unsigned long *field) | |
299 | { | |
300 | int rv; | |
301 | ||
1292500b FD |
302 | if (*field == 0) |
303 | panic("No free mscp"); | |
304 | ||
305 | asm volatile ( | |
306 | "xorl %0,%0\n\t" | |
307 | "0: bsfw %1,%w0\n\t" | |
308 | "btr %0,%1\n\t" | |
309 | "jnc 0b" | |
fad4dab5 | 310 | : "=&r" (rv), "+m" (*field) :); |
1292500b | 311 | |
1da177e4 LT |
312 | return rv; |
313 | } | |
314 | ||
315 | /* This has been re-implemented with the help of Richard Earnshaw, | |
316 | <rwe@pegasus.esprit.ec.org> and works with gcc-2.5.8 and gcc-2.6.0. | |
317 | The instability noted by jfc below appears to be a bug in | |
318 | gcc-2.5.x when compiling w/o optimization. --Caleb | |
319 | ||
320 | This asm is fragile: it doesn't work without the casts and it may | |
321 | not work without optimization. Maybe I should add a swap builtin | |
322 | to gcc. --jfc */ | |
323 | static inline unsigned char xchgb(unsigned char reg, | |
324 | volatile unsigned char *mem) | |
325 | { | |
326 | __asm__ ("xchgb %0,%1" : "=q" (reg), "=m" (*mem) : "0" (reg)); | |
327 | return reg; | |
328 | } | |
329 | ||
330 | #if ULTRASTOR_DEBUG & (UD_COMMAND | UD_ABORT) | |
331 | ||
332 | /* Always called with the host lock held */ | |
333 | static void log_ultrastor_abort(struct ultrastor_config *config, | |
334 | int command) | |
335 | { | |
336 | static char fmt[80] = "abort %d (%x); MSCP free pool: %x;"; | |
337 | int i; | |
338 | ||
339 | for (i = 0; i < ULTRASTOR_MAX_CMDS; i++) | |
340 | { | |
341 | fmt[20 + i*2] = ' '; | |
342 | if (! (config->mscp_free & (1 << i))) | |
343 | fmt[21 + i*2] = '0' + config->mscp[i].target_id; | |
344 | else | |
345 | fmt[21 + i*2] = '-'; | |
346 | } | |
347 | fmt[20 + ULTRASTOR_MAX_CMDS * 2] = '\n'; | |
348 | fmt[21 + ULTRASTOR_MAX_CMDS * 2] = 0; | |
349 | printk(fmt, command, &config->mscp[command], config->mscp_free); | |
350 | ||
351 | } | |
352 | #endif | |
353 | ||
d0be4a7d | 354 | static int ultrastor_14f_detect(struct scsi_host_template * tpnt) |
1da177e4 LT |
355 | { |
356 | size_t i; | |
357 | unsigned char in_byte, version_byte = 0; | |
358 | struct config_1 { | |
359 | unsigned char bios_segment: 3; | |
360 | unsigned char removable_disks_as_fixed: 1; | |
361 | unsigned char interrupt: 2; | |
362 | unsigned char dma_channel: 2; | |
363 | } config_1; | |
364 | struct config_2 { | |
365 | unsigned char ha_scsi_id: 3; | |
366 | unsigned char mapping_mode: 2; | |
367 | unsigned char bios_drive_number: 1; | |
368 | unsigned char tfr_port: 2; | |
369 | } config_2; | |
370 | ||
371 | #if (ULTRASTOR_DEBUG & UD_DETECT) | |
372 | printk("US14F: detect: called\n"); | |
373 | #endif | |
374 | ||
375 | /* If a 24F has already been configured, don't look for a 14F. */ | |
376 | if (config.bios_segment) | |
377 | return FALSE; | |
378 | ||
379 | #ifdef PORT_OVERRIDE | |
380 | if(!request_region(PORT_OVERRIDE, 0xc, "ultrastor")) { | |
381 | printk("Ultrastor I/O space already in use\n"); | |
382 | return FALSE; | |
383 | }; | |
384 | config.port_address = PORT_OVERRIDE; | |
385 | #else | |
386 | for (i = 0; i < ARRAY_SIZE(ultrastor_ports_14f); i++) { | |
387 | if(!request_region(ultrastor_ports_14f[i], 0x0c, "ultrastor")) continue; | |
388 | config.port_address = ultrastor_ports_14f[i]; | |
389 | #endif | |
390 | ||
391 | #if (ULTRASTOR_DEBUG & UD_DETECT) | |
392 | printk("US14F: detect: testing port address %03X\n", config.port_address); | |
393 | #endif | |
394 | ||
395 | in_byte = inb(U14F_PRODUCT_ID(config.port_address)); | |
396 | if (in_byte != US14F_PRODUCT_ID_0) { | |
397 | #if (ULTRASTOR_DEBUG & UD_DETECT) | |
398 | # ifdef PORT_OVERRIDE | |
399 | printk("US14F: detect: wrong product ID 0 - %02X\n", in_byte); | |
400 | # else | |
401 | printk("US14F: detect: no adapter at port %03X\n", config.port_address); | |
402 | # endif | |
403 | #endif | |
404 | #ifdef PORT_OVERRIDE | |
405 | goto out_release_port; | |
406 | #else | |
407 | release_region(config.port_address, 0x0c); | |
408 | continue; | |
409 | #endif | |
410 | } | |
411 | in_byte = inb(U14F_PRODUCT_ID(config.port_address) + 1); | |
412 | /* Only upper nibble is significant for Product ID 1 */ | |
413 | if ((in_byte & 0xF0) != US14F_PRODUCT_ID_1) { | |
414 | #if (ULTRASTOR_DEBUG & UD_DETECT) | |
415 | # ifdef PORT_OVERRIDE | |
416 | printk("US14F: detect: wrong product ID 1 - %02X\n", in_byte); | |
417 | # else | |
418 | printk("US14F: detect: no adapter at port %03X\n", config.port_address); | |
419 | # endif | |
420 | #endif | |
421 | #ifdef PORT_OVERRIDE | |
422 | goto out_release_port; | |
423 | #else | |
424 | release_region(config.port_address, 0x0c); | |
425 | continue; | |
426 | #endif | |
427 | } | |
428 | version_byte = in_byte; | |
429 | #ifndef PORT_OVERRIDE | |
430 | break; | |
431 | } | |
432 | if (i == ARRAY_SIZE(ultrastor_ports_14f)) { | |
433 | # if (ULTRASTOR_DEBUG & UD_DETECT) | |
434 | printk("US14F: detect: no port address found!\n"); | |
435 | # endif | |
436 | /* all ports probed already released - we can just go straight out */ | |
437 | return FALSE; | |
438 | } | |
439 | #endif | |
440 | ||
441 | #if (ULTRASTOR_DEBUG & UD_DETECT) | |
442 | printk("US14F: detect: adapter found at port address %03X\n", | |
443 | config.port_address); | |
444 | #endif | |
445 | ||
446 | /* Set local doorbell mask to disallow bus reset unless | |
447 | ultrastor_bus_reset is true. */ | |
448 | outb(ultrastor_bus_reset ? 0xc2 : 0x82, LCL_DOORBELL_MASK(config.port_address)); | |
449 | ||
450 | /* All above tests passed, must be the right thing. Get some useful | |
451 | info. */ | |
452 | ||
453 | /* Register the I/O space that we use */ | |
454 | ||
455 | *(char *)&config_1 = inb(CONFIG(config.port_address + 0)); | |
456 | *(char *)&config_2 = inb(CONFIG(config.port_address + 1)); | |
457 | config.bios_segment = bios_segment_table[config_1.bios_segment]; | |
458 | config.doorbell_address = config.port_address; | |
459 | config.ogm_address = config.port_address + 0x8; | |
460 | config.icm_address = config.port_address + 0xC; | |
461 | config.interrupt = interrupt_table_14f[config_1.interrupt]; | |
462 | config.ha_scsi_id = config_2.ha_scsi_id; | |
463 | config.heads = mapping_table[config_2.mapping_mode].heads; | |
464 | config.sectors = mapping_table[config_2.mapping_mode].sectors; | |
465 | config.bios_drive_number = config_2.bios_drive_number; | |
466 | config.subversion = (version_byte & 0x0F); | |
467 | if (config.subversion == U34F) | |
468 | config.dma_channel = 0; | |
469 | else | |
470 | config.dma_channel = dma_channel_table_14f[config_1.dma_channel]; | |
471 | ||
472 | if (!config.bios_segment) { | |
473 | #if (ULTRASTOR_DEBUG & UD_DETECT) | |
474 | printk("US14F: detect: not detected.\n"); | |
475 | #endif | |
476 | goto out_release_port; | |
477 | } | |
478 | ||
479 | /* Final consistency check, verify previous info. */ | |
480 | if (config.subversion != U34F) | |
481 | if (!config.dma_channel || !(config_2.tfr_port & 0x2)) { | |
482 | #if (ULTRASTOR_DEBUG & UD_DETECT) | |
483 | printk("US14F: detect: consistency check failed\n"); | |
484 | #endif | |
485 | goto out_release_port; | |
486 | } | |
487 | ||
488 | /* If we were TRULY paranoid, we could issue a host adapter inquiry | |
489 | command here and verify the data returned. But frankly, I'm | |
490 | exhausted! */ | |
491 | ||
492 | /* Finally! Now I'm satisfied... */ | |
493 | #if (ULTRASTOR_DEBUG & UD_DETECT) | |
494 | printk("US14F: detect: detect succeeded\n" | |
495 | " Port address: %03X\n" | |
496 | " BIOS segment: %05X\n" | |
497 | " Interrupt: %u\n" | |
498 | " DMA channel: %u\n" | |
499 | " H/A SCSI ID: %u\n" | |
500 | " Subversion: %u\n", | |
501 | config.port_address, config.bios_segment, config.interrupt, | |
502 | config.dma_channel, config.ha_scsi_id, config.subversion); | |
503 | #endif | |
504 | tpnt->this_id = config.ha_scsi_id; | |
505 | tpnt->unchecked_isa_dma = (config.subversion != U34F); | |
506 | ||
507 | #if ULTRASTOR_MAX_CMDS > 1 | |
508 | config.mscp_free = ~0; | |
509 | #endif | |
510 | ||
511 | /* | |
512 | * Brrr, &config.mscp[0].SCint->host) it is something magical.... | |
513 | * XXX and FIXME | |
514 | */ | |
515 | if (request_irq(config.interrupt, do_ultrastor_interrupt, 0, "Ultrastor", &config.mscp[0].SCint->device->host)) { | |
516 | printk("Unable to allocate IRQ%u for UltraStor controller.\n", | |
517 | config.interrupt); | |
518 | goto out_release_port; | |
519 | } | |
520 | if (config.dma_channel && request_dma(config.dma_channel,"Ultrastor")) { | |
521 | printk("Unable to allocate DMA channel %u for UltraStor controller.\n", | |
522 | config.dma_channel); | |
523 | free_irq(config.interrupt, NULL); | |
524 | goto out_release_port; | |
525 | } | |
526 | tpnt->sg_tablesize = ULTRASTOR_14F_MAX_SG; | |
527 | printk("UltraStor driver version" VERSION ". Using %d SG lists.\n", | |
528 | ULTRASTOR_14F_MAX_SG); | |
529 | ||
530 | return TRUE; | |
531 | out_release_port: | |
532 | release_region(config.port_address, 0x0c); | |
533 | return FALSE; | |
534 | } | |
535 | ||
d0be4a7d | 536 | static int ultrastor_24f_detect(struct scsi_host_template * tpnt) |
1da177e4 LT |
537 | { |
538 | int i; | |
539 | struct Scsi_Host * shpnt = NULL; | |
540 | ||
541 | #if (ULTRASTOR_DEBUG & UD_DETECT) | |
542 | printk("US24F: detect"); | |
543 | #endif | |
544 | ||
545 | /* probe each EISA slot at slot address C80 */ | |
546 | for (i = 1; i < 15; i++) | |
547 | { | |
548 | unsigned char config_1, config_2; | |
549 | unsigned short addr = (i << 12) | ULTRASTOR_24F_PORT; | |
550 | ||
551 | if (inb(addr) != US24F_PRODUCT_ID_0 && | |
552 | inb(addr+1) != US24F_PRODUCT_ID_1 && | |
553 | inb(addr+2) != US24F_PRODUCT_ID_2) | |
554 | continue; | |
555 | ||
556 | config.revision = inb(addr+3); | |
557 | config.slot = i; | |
558 | if (! (inb(addr+4) & 1)) | |
559 | { | |
560 | #if (ULTRASTOR_DEBUG & UD_DETECT) | |
561 | printk("U24F: found disabled card in slot %u\n", i); | |
562 | #endif | |
563 | continue; | |
564 | } | |
565 | #if (ULTRASTOR_DEBUG & UD_DETECT) | |
566 | printk("U24F: found card in slot %u\n", i); | |
567 | #endif | |
568 | config_1 = inb(addr + 5); | |
569 | config.bios_segment = bios_segment_table[config_1 & 7]; | |
570 | switch(config_1 >> 4) | |
571 | { | |
572 | case 1: | |
573 | config.interrupt = 15; | |
574 | break; | |
575 | case 2: | |
576 | config.interrupt = 14; | |
577 | break; | |
578 | case 4: | |
579 | config.interrupt = 11; | |
580 | break; | |
581 | case 8: | |
582 | config.interrupt = 10; | |
583 | break; | |
584 | default: | |
585 | printk("U24F: invalid IRQ\n"); | |
586 | return FALSE; | |
587 | } | |
588 | ||
589 | /* BIOS addr set */ | |
590 | /* base port set */ | |
591 | config.port_address = addr; | |
592 | config.doorbell_address = addr + 12; | |
593 | config.ogm_address = addr + 0x17; | |
594 | config.icm_address = addr + 0x1C; | |
595 | config_2 = inb(addr + 7); | |
596 | config.ha_scsi_id = config_2 & 7; | |
597 | config.heads = mapping_table[(config_2 >> 3) & 3].heads; | |
598 | config.sectors = mapping_table[(config_2 >> 3) & 3].sectors; | |
599 | #if (ULTRASTOR_DEBUG & UD_DETECT) | |
600 | printk("US24F: detect: detect succeeded\n" | |
601 | " Port address: %03X\n" | |
602 | " BIOS segment: %05X\n" | |
603 | " Interrupt: %u\n" | |
604 | " H/A SCSI ID: %u\n", | |
605 | config.port_address, config.bios_segment, | |
606 | config.interrupt, config.ha_scsi_id); | |
607 | #endif | |
608 | tpnt->this_id = config.ha_scsi_id; | |
609 | tpnt->unchecked_isa_dma = 0; | |
610 | tpnt->sg_tablesize = ULTRASTOR_24F_MAX_SG; | |
611 | ||
612 | shpnt = scsi_register(tpnt, 0); | |
613 | if (!shpnt) { | |
614 | printk(KERN_WARNING "(ultrastor:) Could not register scsi device. Aborting registration.\n"); | |
615 | free_irq(config.interrupt, do_ultrastor_interrupt); | |
616 | return FALSE; | |
617 | } | |
618 | ||
619 | if (request_irq(config.interrupt, do_ultrastor_interrupt, 0, "Ultrastor", shpnt)) | |
620 | { | |
621 | printk("Unable to allocate IRQ%u for UltraStor controller.\n", | |
622 | config.interrupt); | |
623 | return FALSE; | |
624 | } | |
625 | ||
626 | shpnt->irq = config.interrupt; | |
627 | shpnt->dma_channel = config.dma_channel; | |
628 | shpnt->io_port = config.port_address; | |
629 | ||
630 | #if ULTRASTOR_MAX_CMDS > 1 | |
631 | config.mscp_free = ~0; | |
632 | #endif | |
633 | /* Mark ICM and OGM free */ | |
634 | outb(0, addr + 0x16); | |
635 | outb(0, addr + 0x1B); | |
636 | ||
637 | /* Set local doorbell mask to disallow bus reset unless | |
638 | ultrastor_bus_reset is true. */ | |
639 | outb(ultrastor_bus_reset ? 0xc2 : 0x82, LCL_DOORBELL_MASK(addr+12)); | |
640 | outb(0x02, SYS_DOORBELL_MASK(addr+12)); | |
641 | printk("UltraStor driver version " VERSION ". Using %d SG lists.\n", | |
642 | tpnt->sg_tablesize); | |
643 | return TRUE; | |
644 | } | |
645 | return FALSE; | |
646 | } | |
647 | ||
d0be4a7d | 648 | static int ultrastor_detect(struct scsi_host_template * tpnt) |
1da177e4 LT |
649 | { |
650 | tpnt->proc_name = "ultrastor"; | |
651 | return ultrastor_14f_detect(tpnt) || ultrastor_24f_detect(tpnt); | |
652 | } | |
653 | ||
654 | static int ultrastor_release(struct Scsi_Host *shost) | |
655 | { | |
656 | if (shost->irq) | |
657 | free_irq(shost->irq, NULL); | |
658 | if (shost->dma_channel != 0xff) | |
659 | free_dma(shost->dma_channel); | |
660 | if (shost->io_port && shost->n_io_port) | |
661 | release_region(shost->io_port, shost->n_io_port); | |
662 | scsi_unregister(shost); | |
663 | return 0; | |
664 | } | |
665 | ||
666 | static const char *ultrastor_info(struct Scsi_Host * shpnt) | |
667 | { | |
668 | static char buf[64]; | |
669 | ||
670 | if (config.slot) | |
671 | sprintf(buf, "UltraStor 24F SCSI @ Slot %u IRQ%u", | |
672 | config.slot, config.interrupt); | |
673 | else if (config.subversion) | |
674 | sprintf(buf, "UltraStor 34F SCSI @ Port %03X BIOS %05X IRQ%u", | |
675 | config.port_address, (int)config.bios_segment, | |
676 | config.interrupt); | |
677 | else | |
678 | sprintf(buf, "UltraStor 14F SCSI @ Port %03X BIOS %05X IRQ%u DMA%u", | |
679 | config.port_address, (int)config.bios_segment, | |
680 | config.interrupt, config.dma_channel); | |
681 | return buf; | |
682 | } | |
683 | ||
b4620233 | 684 | static inline void build_sg_list(struct mscp *mscp, struct scsi_cmnd *SCpnt) |
1da177e4 | 685 | { |
d1786713 | 686 | struct scatterlist *sg; |
1da177e4 LT |
687 | long transfer_length = 0; |
688 | int i, max; | |
689 | ||
d1786713 FT |
690 | max = scsi_sg_count(SCpnt); |
691 | scsi_for_each_sg(SCpnt, sg, max, i) { | |
45711f1a | 692 | mscp->sglist[i].address = isa_page_to_bus(sg_page(sg)) + sg->offset; |
d1786713 FT |
693 | mscp->sglist[i].num_bytes = sg->length; |
694 | transfer_length += sg->length; | |
1da177e4 LT |
695 | } |
696 | mscp->number_of_sg_list = max; | |
697 | mscp->transfer_data = isa_virt_to_bus(mscp->sglist); | |
698 | /* ??? May not be necessary. Docs are unclear as to whether transfer | |
699 | length field is ignored or whether it should be set to the total | |
700 | number of bytes of the transfer. */ | |
701 | mscp->transfer_data_length = transfer_length; | |
702 | } | |
703 | ||
f281233d | 704 | static int ultrastor_queuecommand_lck(struct scsi_cmnd *SCpnt, |
b4620233 | 705 | void (*done) (struct scsi_cmnd *)) |
1da177e4 LT |
706 | { |
707 | struct mscp *my_mscp; | |
708 | #if ULTRASTOR_MAX_CMDS > 1 | |
709 | int mscp_index; | |
710 | #endif | |
711 | unsigned int status; | |
712 | ||
713 | /* Next test is for debugging; "can't happen" */ | |
714 | if ((config.mscp_free & ((1U << ULTRASTOR_MAX_CMDS) - 1)) == 0) | |
715 | panic("ultrastor_queuecommand: no free MSCP\n"); | |
716 | mscp_index = find_and_clear_bit_16(&config.mscp_free); | |
717 | ||
718 | /* Has the command been aborted? */ | |
719 | if (xchgb(0xff, &config.aborted[mscp_index]) != 0) | |
720 | { | |
721 | status = DID_ABORT << 16; | |
722 | goto aborted; | |
723 | } | |
724 | ||
725 | my_mscp = &config.mscp[mscp_index]; | |
726 | ||
727 | *(unsigned char *)my_mscp = OP_SCSI | (DTD_SCSI << 3); | |
728 | ||
729 | /* Tape drives don't work properly if the cache is used. The SCSI | |
730 | READ command for a tape doesn't have a block offset, and the adapter | |
731 | incorrectly assumes that all reads from the tape read the same | |
732 | blocks. Results will depend on read buffer size and other disk | |
733 | activity. | |
734 | ||
735 | ??? Which other device types should never use the cache? */ | |
736 | my_mscp->ca = SCpnt->device->type != TYPE_TAPE; | |
737 | my_mscp->target_id = SCpnt->device->id; | |
738 | my_mscp->ch_no = 0; | |
739 | my_mscp->lun = SCpnt->device->lun; | |
d1786713 | 740 | if (scsi_sg_count(SCpnt)) { |
1da177e4 LT |
741 | /* Set scatter/gather flag in SCSI command packet */ |
742 | my_mscp->sg = TRUE; | |
743 | build_sg_list(my_mscp, SCpnt); | |
744 | } else { | |
745 | /* Unset scatter/gather flag in SCSI command packet */ | |
746 | my_mscp->sg = FALSE; | |
d1786713 FT |
747 | my_mscp->transfer_data = isa_virt_to_bus(scsi_sglist(SCpnt)); |
748 | my_mscp->transfer_data_length = scsi_bufflen(SCpnt); | |
1da177e4 LT |
749 | } |
750 | my_mscp->command_link = 0; /*???*/ | |
751 | my_mscp->scsi_command_link_id = 0; /*???*/ | |
b80ca4f7 | 752 | my_mscp->length_of_sense_byte = SCSI_SENSE_BUFFERSIZE; |
1da177e4 LT |
753 | my_mscp->length_of_scsi_cdbs = SCpnt->cmd_len; |
754 | memcpy(my_mscp->scsi_cdbs, SCpnt->cmnd, my_mscp->length_of_scsi_cdbs); | |
755 | my_mscp->adapter_status = 0; | |
756 | my_mscp->target_status = 0; | |
757 | my_mscp->sense_data = isa_virt_to_bus(&SCpnt->sense_buffer); | |
758 | my_mscp->done = done; | |
759 | my_mscp->SCint = SCpnt; | |
760 | SCpnt->host_scribble = (unsigned char *)my_mscp; | |
761 | ||
762 | /* Find free OGM slot. On 24F, look for OGM status byte == 0. | |
763 | On 14F and 34F, wait for local interrupt pending flag to clear. | |
764 | ||
765 | FIXME: now we are using new_eh we should punt here and let the | |
766 | midlayer sort it out */ | |
767 | ||
768 | retry: | |
769 | if (config.slot) | |
770 | while (inb(config.ogm_address - 1) != 0 && config.aborted[mscp_index] == 0xff) | |
771 | barrier(); | |
772 | ||
773 | /* else??? */ | |
774 | ||
775 | while ((inb(LCL_DOORBELL_INTR(config.doorbell_address)) & (config.slot ? 2 : 1)) && config.aborted[mscp_index] == 0xff) | |
776 | barrier(); | |
777 | ||
778 | /* To avoid race conditions, keep the code to write to the adapter | |
779 | atomic. This simplifies the abort code. Right now the | |
780 | scsi mid layer has the host_lock already held | |
781 | */ | |
782 | ||
783 | if (inb(LCL_DOORBELL_INTR(config.doorbell_address)) & (config.slot ? 2 : 1)) | |
784 | goto retry; | |
785 | ||
786 | status = xchgb(0, &config.aborted[mscp_index]); | |
787 | if (status != 0xff) { | |
788 | ||
789 | #if ULTRASTOR_DEBUG & (UD_COMMAND | UD_ABORT) | |
790 | printk("USx4F: queuecommand: aborted\n"); | |
791 | #if ULTRASTOR_MAX_CMDS > 1 | |
792 | log_ultrastor_abort(&config, mscp_index); | |
793 | #endif | |
794 | #endif | |
795 | status <<= 16; | |
796 | ||
797 | aborted: | |
798 | set_bit(mscp_index, &config.mscp_free); | |
799 | /* If the driver queues commands, call the done proc here. Otherwise | |
800 | return an error. */ | |
801 | #if ULTRASTOR_MAX_CMDS > 1 | |
802 | SCpnt->result = status; | |
803 | done(SCpnt); | |
804 | return 0; | |
805 | #else | |
806 | return status; | |
807 | #endif | |
808 | } | |
809 | ||
810 | /* Store pointer in OGM address bytes */ | |
811 | outl(isa_virt_to_bus(my_mscp), config.ogm_address); | |
812 | ||
813 | /* Issue OGM interrupt */ | |
814 | if (config.slot) { | |
815 | /* Write OGM command register on 24F */ | |
816 | outb(1, config.ogm_address - 1); | |
817 | outb(0x2, LCL_DOORBELL_INTR(config.doorbell_address)); | |
818 | } else { | |
819 | outb(0x1, LCL_DOORBELL_INTR(config.doorbell_address)); | |
820 | } | |
821 | ||
822 | #if (ULTRASTOR_DEBUG & UD_COMMAND) | |
823 | printk("USx4F: queuecommand: returning\n"); | |
824 | #endif | |
825 | ||
826 | return 0; | |
827 | } | |
828 | ||
f281233d JG |
829 | static DEF_SCSI_QCMD(ultrastor_queuecommand) |
830 | ||
1da177e4 LT |
831 | /* This code must deal with 2 cases: |
832 | ||
833 | 1. The command has not been written to the OGM. In this case, set | |
834 | the abort flag and return. | |
835 | ||
836 | 2. The command has been written to the OGM and is stuck somewhere in | |
837 | the adapter. | |
838 | ||
839 | 2a. On a 24F, ask the adapter to abort the command. It will interrupt | |
840 | when it does. | |
841 | ||
842 | 2b. Call the command's done procedure. | |
843 | ||
844 | */ | |
845 | ||
b4620233 | 846 | static int ultrastor_abort(struct scsi_cmnd *SCpnt) |
1da177e4 LT |
847 | { |
848 | #if ULTRASTOR_DEBUG & UD_ABORT | |
849 | char out[108]; | |
850 | unsigned char icm_status = 0, ogm_status = 0; | |
851 | unsigned int icm_addr = 0, ogm_addr = 0; | |
852 | #endif | |
853 | unsigned int mscp_index; | |
854 | unsigned char old_aborted; | |
855 | unsigned long flags; | |
b4620233 | 856 | void (*done)(struct scsi_cmnd *); |
1da177e4 LT |
857 | struct Scsi_Host *host = SCpnt->device->host; |
858 | ||
859 | if(config.slot) | |
860 | return FAILED; /* Do not attempt an abort for the 24f */ | |
861 | ||
862 | /* Simple consistency checking */ | |
863 | if(!SCpnt->host_scribble) | |
864 | return FAILED; | |
865 | ||
866 | mscp_index = ((struct mscp *)SCpnt->host_scribble) - config.mscp; | |
867 | if (mscp_index >= ULTRASTOR_MAX_CMDS) | |
868 | panic("Ux4F aborting invalid MSCP"); | |
869 | ||
870 | #if ULTRASTOR_DEBUG & UD_ABORT | |
871 | if (config.slot) | |
872 | { | |
873 | int port0 = (config.slot << 12) | 0xc80; | |
874 | int i; | |
875 | unsigned long flags; | |
876 | ||
877 | spin_lock_irqsave(host->host_lock, flags); | |
878 | strcpy(out, "OGM %d:%x ICM %d:%x ports: "); | |
879 | for (i = 0; i < 16; i++) | |
880 | { | |
881 | unsigned char p = inb(port0 + i); | |
882 | out[28 + i * 3] = "0123456789abcdef"[p >> 4]; | |
883 | out[29 + i * 3] = "0123456789abcdef"[p & 15]; | |
884 | out[30 + i * 3] = ' '; | |
885 | } | |
886 | out[28 + i * 3] = '\n'; | |
887 | out[29 + i * 3] = 0; | |
888 | ogm_status = inb(port0 + 22); | |
889 | ogm_addr = (unsigned int)isa_bus_to_virt(inl(port0 + 23)); | |
890 | icm_status = inb(port0 + 27); | |
891 | icm_addr = (unsigned int)isa_bus_to_virt(inl(port0 + 28)); | |
8fa728a2 | 892 | spin_unlock_irqrestore(host->host_lock, flags); |
1da177e4 LT |
893 | } |
894 | ||
895 | /* First check to see if an interrupt is pending. I suspect the SiS | |
896 | chipset loses interrupts. (I also suspect is mangles data, but | |
897 | one bug at a time... */ | |
898 | if (config.slot ? inb(config.icm_address - 1) == 2 : | |
899 | (inb(SYS_DOORBELL_INTR(config.doorbell_address)) & 1)) | |
900 | { | |
901 | printk("Ux4F: abort while completed command pending\n"); | |
902 | ||
903 | spin_lock_irqsave(host->host_lock, flags); | |
904 | /* FIXME: Ewww... need to think about passing host around properly */ | |
c7bec5ab | 905 | ultrastor_interrupt(NULL); |
1da177e4 LT |
906 | spin_unlock_irqrestore(host->host_lock, flags); |
907 | return SUCCESS; | |
908 | } | |
909 | #endif | |
910 | ||
911 | old_aborted = xchgb(DID_ABORT, &config.aborted[mscp_index]); | |
912 | ||
913 | /* aborted == 0xff is the signal that queuecommand has not yet sent | |
914 | the command. It will notice the new abort flag and fail. */ | |
915 | if (old_aborted == 0xff) | |
916 | return SUCCESS; | |
917 | ||
918 | /* On 24F, send an abort MSCP request. The adapter will interrupt | |
919 | and the interrupt handler will call done. */ | |
920 | if (config.slot && inb(config.ogm_address - 1) == 0) | |
921 | { | |
922 | unsigned long flags; | |
923 | ||
924 | spin_lock_irqsave(host->host_lock, flags); | |
925 | outl(isa_virt_to_bus(&config.mscp[mscp_index]), config.ogm_address); | |
926 | udelay(8); | |
927 | outb(0x80, config.ogm_address - 1); | |
928 | outb(0x2, LCL_DOORBELL_INTR(config.doorbell_address)); | |
929 | #if ULTRASTOR_DEBUG & UD_ABORT | |
930 | log_ultrastor_abort(&config, mscp_index); | |
931 | printk(out, ogm_status, ogm_addr, icm_status, icm_addr); | |
932 | #endif | |
933 | spin_unlock_irqrestore(host->host_lock, flags); | |
934 | /* FIXME: add a wait for the abort to complete */ | |
935 | return SUCCESS; | |
936 | } | |
937 | ||
938 | #if ULTRASTOR_DEBUG & UD_ABORT | |
939 | log_ultrastor_abort(&config, mscp_index); | |
940 | #endif | |
941 | ||
942 | /* Can't request a graceful abort. Either this is not a 24F or | |
943 | the OGM is busy. Don't free the command -- the adapter might | |
944 | still be using it. Setting SCint = 0 causes the interrupt | |
945 | handler to ignore the command. */ | |
946 | ||
947 | /* FIXME - devices that implement soft resets will still be running | |
948 | the command after a bus reset. We would probably rather leave | |
949 | the command in the queue. The upper level code will automatically | |
950 | leave the command in the active state instead of requeueing it. ERY */ | |
951 | ||
952 | #if ULTRASTOR_DEBUG & UD_ABORT | |
953 | if (config.mscp[mscp_index].SCint != SCpnt) | |
954 | printk("abort: command mismatch, %p != %p\n", | |
955 | config.mscp[mscp_index].SCint, SCpnt); | |
956 | #endif | |
172c122d | 957 | if (config.mscp[mscp_index].SCint == NULL) |
69aa3f71 | 958 | return FAILED; |
1da177e4 LT |
959 | |
960 | if (config.mscp[mscp_index].SCint != SCpnt) panic("Bad abort"); | |
961 | config.mscp[mscp_index].SCint = NULL; | |
962 | done = config.mscp[mscp_index].done; | |
963 | config.mscp[mscp_index].done = NULL; | |
964 | SCpnt->result = DID_ABORT << 16; | |
965 | ||
966 | /* Take the host lock to guard against scsi layer re-entry */ | |
1da177e4 | 967 | done(SCpnt); |
1da177e4 LT |
968 | |
969 | /* Need to set a timeout here in case command never completes. */ | |
970 | return SUCCESS; | |
971 | } | |
972 | ||
b4620233 | 973 | static int ultrastor_host_reset(struct scsi_cmnd * SCpnt) |
1da177e4 LT |
974 | { |
975 | unsigned long flags; | |
976 | int i; | |
977 | struct Scsi_Host *host = SCpnt->device->host; | |
978 | ||
979 | #if (ULTRASTOR_DEBUG & UD_RESET) | |
980 | printk("US14F: reset: called\n"); | |
981 | #endif | |
982 | ||
983 | if(config.slot) | |
984 | return FAILED; | |
985 | ||
986 | spin_lock_irqsave(host->host_lock, flags); | |
987 | /* Reset the adapter and SCSI bus. The SCSI bus reset can be | |
988 | inhibited by clearing ultrastor_bus_reset before probe. */ | |
989 | outb(0xc0, LCL_DOORBELL_INTR(config.doorbell_address)); | |
990 | if (config.slot) | |
991 | { | |
992 | outb(0, config.ogm_address - 1); | |
993 | outb(0, config.icm_address - 1); | |
994 | } | |
995 | ||
996 | #if ULTRASTOR_MAX_CMDS == 1 | |
997 | if (config.mscp_busy && config.mscp->done && config.mscp->SCint) | |
998 | { | |
999 | config.mscp->SCint->result = DID_RESET << 16; | |
1000 | config.mscp->done(config.mscp->SCint); | |
1001 | } | |
1002 | config.mscp->SCint = 0; | |
1003 | #else | |
1004 | for (i = 0; i < ULTRASTOR_MAX_CMDS; i++) | |
1005 | { | |
1006 | if (! (config.mscp_free & (1 << i)) && | |
1007 | config.mscp[i].done && config.mscp[i].SCint) | |
1008 | { | |
1009 | config.mscp[i].SCint->result = DID_RESET << 16; | |
1010 | config.mscp[i].done(config.mscp[i].SCint); | |
1011 | config.mscp[i].done = NULL; | |
1012 | } | |
1013 | config.mscp[i].SCint = NULL; | |
1014 | } | |
1015 | #endif | |
1016 | ||
1017 | /* FIXME - if the device implements soft resets, then the command | |
1018 | will still be running. ERY | |
1019 | ||
1020 | Even bigger deal with new_eh! | |
1021 | */ | |
1022 | ||
1023 | memset((unsigned char *)config.aborted, 0, sizeof config.aborted); | |
1024 | #if ULTRASTOR_MAX_CMDS == 1 | |
1025 | config.mscp_busy = 0; | |
1026 | #else | |
1027 | config.mscp_free = ~0; | |
1028 | #endif | |
1029 | ||
1030 | spin_unlock_irqrestore(host->host_lock, flags); | |
2bc474c3 | 1031 | return SUCCESS; |
1da177e4 LT |
1032 | |
1033 | } | |
1034 | ||
1035 | int ultrastor_biosparam(struct scsi_device *sdev, struct block_device *bdev, | |
1036 | sector_t capacity, int * dkinfo) | |
1037 | { | |
1038 | int size = capacity; | |
1039 | unsigned int s = config.heads * config.sectors; | |
1040 | ||
1041 | dkinfo[0] = config.heads; | |
1042 | dkinfo[1] = config.sectors; | |
1043 | dkinfo[2] = size / s; /* Ignore partial cylinders */ | |
1044 | #if 0 | |
1045 | if (dkinfo[2] > 1024) | |
1046 | dkinfo[2] = 1024; | |
1047 | #endif | |
1048 | return 0; | |
1049 | } | |
1050 | ||
c7bec5ab | 1051 | static void ultrastor_interrupt(void *dev_id) |
1da177e4 LT |
1052 | { |
1053 | unsigned int status; | |
1054 | #if ULTRASTOR_MAX_CMDS > 1 | |
1055 | unsigned int mscp_index; | |
1056 | #endif | |
1057 | struct mscp *mscp; | |
b4620233 HK |
1058 | void (*done) (struct scsi_cmnd *); |
1059 | struct scsi_cmnd *SCtmp; | |
1da177e4 LT |
1060 | |
1061 | #if ULTRASTOR_MAX_CMDS == 1 | |
1062 | mscp = &config.mscp[0]; | |
1063 | #else | |
1064 | mscp = (struct mscp *)isa_bus_to_virt(inl(config.icm_address)); | |
1065 | mscp_index = mscp - config.mscp; | |
1066 | if (mscp_index >= ULTRASTOR_MAX_CMDS) { | |
1067 | printk("Ux4F interrupt: bad MSCP address %x\n", (unsigned int) mscp); | |
1068 | /* A command has been lost. Reset and report an error | |
1069 | for all commands. */ | |
1070 | ultrastor_host_reset(dev_id); | |
1071 | return; | |
1072 | } | |
1073 | #endif | |
1074 | ||
1075 | /* Clean ICM slot (set ICMINT bit to 0) */ | |
1076 | if (config.slot) { | |
1077 | unsigned char icm_status = inb(config.icm_address - 1); | |
1078 | #if ULTRASTOR_DEBUG & (UD_INTERRUPT|UD_ERROR|UD_ABORT) | |
1079 | if (icm_status != 1 && icm_status != 2) | |
1080 | printk("US24F: ICM status %x for MSCP %d (%x)\n", icm_status, | |
1081 | mscp_index, (unsigned int) mscp); | |
1082 | #endif | |
1083 | /* The manual says clear interrupt then write 0 to ICM status. | |
1084 | This seems backwards, but I'll do it anyway. --jfc */ | |
1085 | outb(2, SYS_DOORBELL_INTR(config.doorbell_address)); | |
1086 | outb(0, config.icm_address - 1); | |
1087 | if (icm_status == 4) { | |
1088 | printk("UltraStor abort command failed\n"); | |
1089 | return; | |
1090 | } | |
1091 | if (icm_status == 3) { | |
b4620233 | 1092 | void (*done)(struct scsi_cmnd *) = mscp->done; |
1da177e4 LT |
1093 | if (done) { |
1094 | mscp->done = NULL; | |
1095 | mscp->SCint->result = DID_ABORT << 16; | |
1096 | done(mscp->SCint); | |
1097 | } | |
1098 | return; | |
1099 | } | |
1100 | } else { | |
1101 | outb(1, SYS_DOORBELL_INTR(config.doorbell_address)); | |
1102 | } | |
1103 | ||
1104 | SCtmp = mscp->SCint; | |
1105 | mscp->SCint = NULL; | |
1106 | ||
172c122d | 1107 | if (!SCtmp) |
1da177e4 LT |
1108 | { |
1109 | #if ULTRASTOR_DEBUG & (UD_ABORT|UD_INTERRUPT) | |
1110 | printk("MSCP %d (%x): no command\n", mscp_index, (unsigned int) mscp); | |
1111 | #endif | |
1112 | #if ULTRASTOR_MAX_CMDS == 1 | |
1113 | config.mscp_busy = FALSE; | |
1114 | #else | |
1115 | set_bit(mscp_index, &config.mscp_free); | |
1116 | #endif | |
1117 | config.aborted[mscp_index] = 0; | |
1118 | return; | |
1119 | } | |
1120 | ||
1121 | /* Save done locally and zero before calling. This is needed as | |
1122 | once we call done, we may get another command queued before this | |
1123 | interrupt service routine can return. */ | |
1124 | done = mscp->done; | |
1125 | mscp->done = NULL; | |
1126 | ||
1127 | /* Let the higher levels know that we're done */ | |
1128 | switch (mscp->adapter_status) | |
1129 | { | |
1130 | case 0: | |
1131 | status = DID_OK << 16; | |
1132 | break; | |
1133 | case 0x01: /* invalid command */ | |
1134 | case 0x02: /* invalid parameters */ | |
1135 | case 0x03: /* invalid data list */ | |
1136 | default: | |
1137 | status = DID_ERROR << 16; | |
1138 | break; | |
1139 | case 0x84: /* SCSI bus abort */ | |
1140 | status = DID_ABORT << 16; | |
1141 | break; | |
1142 | case 0x91: | |
1143 | status = DID_TIME_OUT << 16; | |
1144 | break; | |
1145 | } | |
1146 | ||
1147 | SCtmp->result = status | mscp->target_status; | |
1148 | ||
1149 | SCtmp->host_scribble = NULL; | |
1150 | ||
1151 | /* Free up mscp block for next command */ | |
1152 | #if ULTRASTOR_MAX_CMDS == 1 | |
1153 | config.mscp_busy = FALSE; | |
1154 | #else | |
1155 | set_bit(mscp_index, &config.mscp_free); | |
1156 | #endif | |
1157 | ||
1158 | #if ULTRASTOR_DEBUG & (UD_ABORT|UD_INTERRUPT) | |
1159 | if (config.aborted[mscp_index]) | |
1160 | printk("Ux4 interrupt: MSCP %d (%x) aborted = %d\n", | |
1161 | mscp_index, (unsigned int) mscp, config.aborted[mscp_index]); | |
1162 | #endif | |
1163 | config.aborted[mscp_index] = 0; | |
1164 | ||
1165 | if (done) | |
1166 | done(SCtmp); | |
1167 | else | |
1168 | printk("US14F: interrupt: unexpected interrupt\n"); | |
1169 | ||
1170 | if (config.slot ? inb(config.icm_address - 1) : | |
1171 | (inb(SYS_DOORBELL_INTR(config.doorbell_address)) & 1)) | |
1172 | #if (ULTRASTOR_DEBUG & UD_MULTI_CMD) | |
1173 | printk("Ux4F: multiple commands completed\n"); | |
1174 | #else | |
1175 | ; | |
1176 | #endif | |
1177 | ||
1178 | #if (ULTRASTOR_DEBUG & UD_INTERRUPT) | |
1179 | printk("USx4F: interrupt: returning\n"); | |
1180 | #endif | |
1181 | } | |
1182 | ||
7d12e780 | 1183 | static irqreturn_t do_ultrastor_interrupt(int irq, void *dev_id) |
1da177e4 LT |
1184 | { |
1185 | unsigned long flags; | |
1186 | struct Scsi_Host *dev = dev_id; | |
1187 | ||
1188 | spin_lock_irqsave(dev->host_lock, flags); | |
c7bec5ab | 1189 | ultrastor_interrupt(dev_id); |
1da177e4 LT |
1190 | spin_unlock_irqrestore(dev->host_lock, flags); |
1191 | return IRQ_HANDLED; | |
1192 | } | |
1193 | ||
1194 | MODULE_LICENSE("GPL"); | |
1195 | ||
d0be4a7d | 1196 | static struct scsi_host_template driver_template = { |
1da177e4 LT |
1197 | .name = "UltraStor 14F/24F/34F", |
1198 | .detect = ultrastor_detect, | |
1199 | .release = ultrastor_release, | |
1200 | .info = ultrastor_info, | |
1201 | .queuecommand = ultrastor_queuecommand, | |
1202 | .eh_abort_handler = ultrastor_abort, | |
1203 | .eh_host_reset_handler = ultrastor_host_reset, | |
1204 | .bios_param = ultrastor_biosparam, | |
1205 | .can_queue = ULTRASTOR_MAX_CMDS, | |
1206 | .sg_tablesize = ULTRASTOR_14F_MAX_SG, | |
1207 | .cmd_per_lun = ULTRASTOR_MAX_CMDS_PER_LUN, | |
1208 | .unchecked_isa_dma = 1, | |
1209 | .use_clustering = ENABLE_CLUSTERING, | |
1210 | }; | |
1211 | #include "scsi_module.c" |