diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/scsi/mca_53c9x.c |
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/scsi/mca_53c9x.c')
-rw-r--r-- | drivers/scsi/mca_53c9x.c | 520 |
1 files changed, 520 insertions, 0 deletions
diff --git a/drivers/scsi/mca_53c9x.c b/drivers/scsi/mca_53c9x.c new file mode 100644 index 000000000000..194c75451faf --- /dev/null +++ b/drivers/scsi/mca_53c9x.c | |||
@@ -0,0 +1,520 @@ | |||
1 | /* mca_53c9x.c: Driver for the SCSI adapter found on NCR 35xx | ||
2 | * (and maybe some other) Microchannel machines | ||
3 | * | ||
4 | * Code taken mostly from Cyberstorm SCSI drivers | ||
5 | * Copyright (C) 1996 Jesper Skov (jskov@cygnus.co.uk) | ||
6 | * | ||
7 | * Hacked to work with the NCR MCA stuff by Tymm Twillman (tymm@computer.org) | ||
8 | * | ||
9 | * The CyberStorm SCSI driver (and this driver) is based on David S. Miller's | ||
10 | * ESP driver * for the Sparc computers. | ||
11 | * | ||
12 | * Special thanks to Ken Stewart at Symbios (LSI) for helping with info on | ||
13 | * the 86C01. I was on the brink of going ga-ga... | ||
14 | * | ||
15 | * Also thanks to Jesper Skov for helping me with info on how the Amiga | ||
16 | * does things... | ||
17 | */ | ||
18 | |||
19 | /* | ||
20 | * This is currently only set up to use one 53c9x card at a time; it could be | ||
21 | * changed fairly easily to detect/use more than one, but I'm not too sure how | ||
22 | * many cards that use the 53c9x on MCA systems there are (if, in fact, there | ||
23 | * are cards that use them, other than the one built into some NCR systems)... | ||
24 | * If anyone requests this, I'll throw it in, otherwise it's not worth the | ||
25 | * effort. | ||
26 | */ | ||
27 | |||
28 | /* | ||
29 | * Info on the 86C01 MCA interface chip at the bottom, if you care enough to | ||
30 | * look. | ||
31 | */ | ||
32 | |||
33 | #include <linux/delay.h> | ||
34 | #include <linux/interrupt.h> | ||
35 | #include <linux/kernel.h> | ||
36 | #include <linux/mca.h> | ||
37 | #include <linux/types.h> | ||
38 | #include <linux/string.h> | ||
39 | #include <linux/slab.h> | ||
40 | #include <linux/blkdev.h> | ||
41 | #include <linux/proc_fs.h> | ||
42 | #include <linux/stat.h> | ||
43 | #include <linux/mca-legacy.h> | ||
44 | |||
45 | #include "scsi.h" | ||
46 | #include <scsi/scsi_host.h> | ||
47 | #include "NCR53C9x.h" | ||
48 | |||
49 | #include <asm/dma.h> | ||
50 | #include <asm/irq.h> | ||
51 | #include <asm/mca_dma.h> | ||
52 | #include <asm/pgtable.h> | ||
53 | |||
54 | /* | ||
55 | * From ibmmca.c (IBM scsi controller card driver) -- used for turning PS2 disk | ||
56 | * activity LED on and off | ||
57 | */ | ||
58 | |||
59 | #define PS2_SYS_CTR 0x92 | ||
60 | |||
61 | /* Ports the ncr's 53c94 can be put at; indexed by pos register value */ | ||
62 | |||
63 | #define MCA_53C9X_IO_PORTS { \ | ||
64 | 0x0000, 0x0240, 0x0340, 0x0400, \ | ||
65 | 0x0420, 0x3240, 0x8240, 0xA240, \ | ||
66 | } | ||
67 | |||
68 | /* | ||
69 | * Supposedly there were some cards put together with the 'c9x and 86c01. If | ||
70 | * they have different ID's from the ones on the 3500 series machines, | ||
71 | * you can add them here and hopefully things will work out. | ||
72 | */ | ||
73 | |||
74 | #define MCA_53C9X_IDS { \ | ||
75 | 0x7F4C, \ | ||
76 | 0x0000, \ | ||
77 | } | ||
78 | |||
79 | static int dma_bytes_sent(struct NCR_ESP *, int); | ||
80 | static int dma_can_transfer(struct NCR_ESP *, Scsi_Cmnd *); | ||
81 | static void dma_dump_state(struct NCR_ESP *); | ||
82 | static void dma_init_read(struct NCR_ESP *, __u32, int); | ||
83 | static void dma_init_write(struct NCR_ESP *, __u32, int); | ||
84 | static void dma_ints_off(struct NCR_ESP *); | ||
85 | static void dma_ints_on(struct NCR_ESP *); | ||
86 | static int dma_irq_p(struct NCR_ESP *); | ||
87 | static int dma_ports_p(struct NCR_ESP *); | ||
88 | static void dma_setup(struct NCR_ESP *, __u32, int, int); | ||
89 | static void dma_led_on(struct NCR_ESP *); | ||
90 | static void dma_led_off(struct NCR_ESP *); | ||
91 | |||
92 | /* This is where all commands are put before they are trasfered to the | ||
93 | * 53c9x via PIO. | ||
94 | */ | ||
95 | |||
96 | static volatile unsigned char cmd_buffer[16]; | ||
97 | |||
98 | /* | ||
99 | * We keep the structure that is used to access the registers on the 53c9x | ||
100 | * here. | ||
101 | */ | ||
102 | |||
103 | static struct ESP_regs eregs; | ||
104 | |||
105 | /***************************************************************** Detection */ | ||
106 | static int mca_esp_detect(Scsi_Host_Template *tpnt) | ||
107 | { | ||
108 | struct NCR_ESP *esp; | ||
109 | static int io_port_by_pos[] = MCA_53C9X_IO_PORTS; | ||
110 | int mca_53c9x_ids[] = MCA_53C9X_IDS; | ||
111 | int *id_to_check = mca_53c9x_ids; | ||
112 | int slot; | ||
113 | int pos[3]; | ||
114 | unsigned int tmp_io_addr; | ||
115 | unsigned char tmp_byte; | ||
116 | |||
117 | |||
118 | if (!MCA_bus) | ||
119 | return 0; | ||
120 | |||
121 | while (*id_to_check) { | ||
122 | if ((slot = mca_find_adapter(*id_to_check, 0)) != | ||
123 | MCA_NOTFOUND) | ||
124 | { | ||
125 | esp = esp_allocate(tpnt, (void *) NULL); | ||
126 | |||
127 | pos[0] = mca_read_stored_pos(slot, 2); | ||
128 | pos[1] = mca_read_stored_pos(slot, 3); | ||
129 | pos[2] = mca_read_stored_pos(slot, 4); | ||
130 | |||
131 | esp->eregs = &eregs; | ||
132 | |||
133 | /* | ||
134 | * IO port base is given in the first (non-ID) pos | ||
135 | * register, like so: | ||
136 | * | ||
137 | * Bits 3 2 1 IO base | ||
138 | * ---------------------------- | ||
139 | * 0 0 0 <disabled> | ||
140 | * 0 0 1 0x0240 | ||
141 | * 0 1 0 0x0340 | ||
142 | * 0 1 1 0x0400 | ||
143 | * 1 0 0 0x0420 | ||
144 | * 1 0 1 0x3240 | ||
145 | * 1 1 0 0x8240 | ||
146 | * 1 1 1 0xA240 | ||
147 | */ | ||
148 | |||
149 | tmp_io_addr = | ||
150 | io_port_by_pos[(pos[0] & 0x0E) >> 1]; | ||
151 | |||
152 | esp->eregs->io_addr = tmp_io_addr + 0x10; | ||
153 | |||
154 | if (esp->eregs->io_addr == 0x0000) { | ||
155 | printk("Adapter is disabled.\n"); | ||
156 | break; | ||
157 | } | ||
158 | |||
159 | /* | ||
160 | * IRQ is specified in bits 4 and 5: | ||
161 | * | ||
162 | * Bits 4 5 IRQ | ||
163 | * ----------------------- | ||
164 | * 0 0 3 | ||
165 | * 0 1 5 | ||
166 | * 1 0 7 | ||
167 | * 1 1 9 | ||
168 | */ | ||
169 | |||
170 | esp->irq = ((pos[0] & 0x30) >> 3) + 3; | ||
171 | |||
172 | /* | ||
173 | * DMA channel is in the low 3 bits of the second | ||
174 | * POS register | ||
175 | */ | ||
176 | |||
177 | esp->dma = pos[1] & 7; | ||
178 | esp->slot = slot; | ||
179 | |||
180 | if (request_irq(esp->irq, esp_intr, 0, | ||
181 | "NCR 53c9x SCSI", esp->ehost)) | ||
182 | { | ||
183 | printk("Unable to request IRQ %d.\n", esp->irq); | ||
184 | esp_deallocate(esp); | ||
185 | scsi_unregister(esp->ehost); | ||
186 | return 0; | ||
187 | } | ||
188 | |||
189 | if (request_dma(esp->dma, "NCR 53c9x SCSI")) { | ||
190 | printk("Unable to request DMA channel %d.\n", | ||
191 | esp->dma); | ||
192 | free_irq(esp->irq, esp_intr); | ||
193 | esp_deallocate(esp); | ||
194 | scsi_unregister(esp->ehost); | ||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | request_region(tmp_io_addr, 32, "NCR 53c9x SCSI"); | ||
199 | |||
200 | /* | ||
201 | * 86C01 handles DMA, IO mode, from address | ||
202 | * (base + 0x0a) | ||
203 | */ | ||
204 | |||
205 | mca_disable_dma(esp->dma); | ||
206 | mca_set_dma_io(esp->dma, tmp_io_addr + 0x0a); | ||
207 | mca_enable_dma(esp->dma); | ||
208 | |||
209 | /* Tell the 86C01 to give us interrupts */ | ||
210 | |||
211 | tmp_byte = inb(tmp_io_addr + 0x02) | 0x40; | ||
212 | outb(tmp_byte, tmp_io_addr + 0x02); | ||
213 | |||
214 | /* | ||
215 | * Scsi ID -- general purpose register, hi | ||
216 | * 2 bits; add 4 to this number to get the | ||
217 | * ID | ||
218 | */ | ||
219 | |||
220 | esp->scsi_id = ((pos[2] & 0xC0) >> 6) + 4; | ||
221 | |||
222 | /* Do command transfer with programmed I/O */ | ||
223 | |||
224 | esp->do_pio_cmds = 1; | ||
225 | |||
226 | /* Required functions */ | ||
227 | |||
228 | esp->dma_bytes_sent = &dma_bytes_sent; | ||
229 | esp->dma_can_transfer = &dma_can_transfer; | ||
230 | esp->dma_dump_state = &dma_dump_state; | ||
231 | esp->dma_init_read = &dma_init_read; | ||
232 | esp->dma_init_write = &dma_init_write; | ||
233 | esp->dma_ints_off = &dma_ints_off; | ||
234 | esp->dma_ints_on = &dma_ints_on; | ||
235 | esp->dma_irq_p = &dma_irq_p; | ||
236 | esp->dma_ports_p = &dma_ports_p; | ||
237 | esp->dma_setup = &dma_setup; | ||
238 | |||
239 | /* Optional functions */ | ||
240 | |||
241 | esp->dma_barrier = NULL; | ||
242 | esp->dma_drain = NULL; | ||
243 | esp->dma_invalidate = NULL; | ||
244 | esp->dma_irq_entry = NULL; | ||
245 | esp->dma_irq_exit = NULL; | ||
246 | esp->dma_led_on = dma_led_on; | ||
247 | esp->dma_led_off = dma_led_off; | ||
248 | esp->dma_poll = NULL; | ||
249 | esp->dma_reset = NULL; | ||
250 | |||
251 | /* Set the command buffer */ | ||
252 | |||
253 | esp->esp_command = (volatile unsigned char*) | ||
254 | cmd_buffer; | ||
255 | esp->esp_command_dvma = isa_virt_to_bus(cmd_buffer); | ||
256 | |||
257 | /* SCSI chip speed */ | ||
258 | |||
259 | esp->cfreq = 25000000; | ||
260 | |||
261 | /* Differential SCSI? I think not. */ | ||
262 | |||
263 | esp->diff = 0; | ||
264 | |||
265 | esp_initialize(esp); | ||
266 | |||
267 | printk(" Adapter found in slot %2d: io port 0x%x " | ||
268 | "irq %d dma channel %d\n", slot + 1, tmp_io_addr, | ||
269 | esp->irq, esp->dma); | ||
270 | |||
271 | mca_set_adapter_name(slot, "NCR 53C9X SCSI Adapter"); | ||
272 | mca_mark_as_used(slot); | ||
273 | |||
274 | break; | ||
275 | } | ||
276 | |||
277 | id_to_check++; | ||
278 | } | ||
279 | |||
280 | return esps_in_use; | ||
281 | } | ||
282 | |||
283 | |||
284 | /******************************************************************* Release */ | ||
285 | |||
286 | static int mca_esp_release(struct Scsi_Host *host) | ||
287 | { | ||
288 | struct NCR_ESP *esp = (struct NCR_ESP *)host->hostdata; | ||
289 | unsigned char tmp_byte; | ||
290 | |||
291 | esp_deallocate(esp); | ||
292 | /* | ||
293 | * Tell the 86C01 to stop sending interrupts | ||
294 | */ | ||
295 | |||
296 | tmp_byte = inb(esp->eregs->io_addr - 0x0E); | ||
297 | tmp_byte &= ~0x40; | ||
298 | outb(tmp_byte, esp->eregs->io_addr - 0x0E); | ||
299 | |||
300 | free_irq(esp->irq, esp_intr); | ||
301 | free_dma(esp->dma); | ||
302 | |||
303 | mca_mark_as_unused(esp->slot); | ||
304 | |||
305 | return 0; | ||
306 | } | ||
307 | |||
308 | /************************************************************* DMA Functions */ | ||
309 | static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count) | ||
310 | { | ||
311 | /* Ask the 53c9x. It knows. */ | ||
312 | |||
313 | return fifo_count; | ||
314 | } | ||
315 | |||
316 | static int dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp) | ||
317 | { | ||
318 | /* | ||
319 | * The MCA dma channels can only do up to 128K bytes at a time. | ||
320 | * (16 bit mode) | ||
321 | */ | ||
322 | |||
323 | unsigned long sz = sp->SCp.this_residual; | ||
324 | if(sz > 0x20000) | ||
325 | sz = 0x20000; | ||
326 | return sz; | ||
327 | } | ||
328 | |||
329 | static void dma_dump_state(struct NCR_ESP *esp) | ||
330 | { | ||
331 | /* | ||
332 | * Doesn't quite match up to the other drivers, but we do what we | ||
333 | * can. | ||
334 | */ | ||
335 | |||
336 | ESPLOG(("esp%d: dma channel <%d>\n", esp->esp_id, esp->dma)); | ||
337 | ESPLOG(("bytes left to dma: %d\n", mca_get_dma_residue(esp->dma))); | ||
338 | } | ||
339 | |||
340 | static void dma_init_read(struct NCR_ESP *esp, __u32 addr, int length) | ||
341 | { | ||
342 | unsigned long flags; | ||
343 | |||
344 | |||
345 | save_flags(flags); | ||
346 | cli(); | ||
347 | |||
348 | mca_disable_dma(esp->dma); | ||
349 | mca_set_dma_mode(esp->dma, MCA_DMA_MODE_XFER | MCA_DMA_MODE_16 | | ||
350 | MCA_DMA_MODE_IO); | ||
351 | mca_set_dma_addr(esp->dma, addr); | ||
352 | mca_set_dma_count(esp->dma, length / 2); /* !!! */ | ||
353 | mca_enable_dma(esp->dma); | ||
354 | |||
355 | restore_flags(flags); | ||
356 | } | ||
357 | |||
358 | static void dma_init_write(struct NCR_ESP *esp, __u32 addr, int length) | ||
359 | { | ||
360 | unsigned long flags; | ||
361 | |||
362 | |||
363 | save_flags(flags); | ||
364 | cli(); | ||
365 | |||
366 | mca_disable_dma(esp->dma); | ||
367 | mca_set_dma_mode(esp->dma, MCA_DMA_MODE_XFER | MCA_DMA_MODE_WRITE | | ||
368 | MCA_DMA_MODE_16 | MCA_DMA_MODE_IO); | ||
369 | mca_set_dma_addr(esp->dma, addr); | ||
370 | mca_set_dma_count(esp->dma, length / 2); /* !!! */ | ||
371 | mca_enable_dma(esp->dma); | ||
372 | |||
373 | restore_flags(flags); | ||
374 | } | ||
375 | |||
376 | static void dma_ints_off(struct NCR_ESP *esp) | ||
377 | { | ||
378 | /* | ||
379 | * Tell the 'C01 to shut up. All interrupts are routed through it. | ||
380 | */ | ||
381 | |||
382 | outb(inb(esp->eregs->io_addr - 0x0E) & ~0x40, | ||
383 | esp->eregs->io_addr - 0x0E); | ||
384 | } | ||
385 | |||
386 | static void dma_ints_on(struct NCR_ESP *esp) | ||
387 | { | ||
388 | /* | ||
389 | * Ok. You can speak again. | ||
390 | */ | ||
391 | |||
392 | outb(inb(esp->eregs->io_addr - 0x0E) | 0x40, | ||
393 | esp->eregs->io_addr - 0x0E); | ||
394 | } | ||
395 | |||
396 | static int dma_irq_p(struct NCR_ESP *esp) | ||
397 | { | ||
398 | /* | ||
399 | * DaveM says that this should return a "yes" if there is an interrupt | ||
400 | * or a DMA error occurred. I copied the Amiga driver's semantics, | ||
401 | * though, because it seems to work and we can't really tell if | ||
402 | * a DMA error happened. This gives the "yes" if the scsi chip | ||
403 | * is sending an interrupt and no DMA activity is taking place | ||
404 | */ | ||
405 | |||
406 | return (!(inb(esp->eregs->io_addr - 0x04) & 1) && | ||
407 | !(inb(esp->eregs->io_addr - 0x04) & 2) ); | ||
408 | } | ||
409 | |||
410 | static int dma_ports_p(struct NCR_ESP *esp) | ||
411 | { | ||
412 | /* | ||
413 | * Check to see if interrupts are enabled on the 'C01 (in case abort | ||
414 | * is entered multiple times, so we only do the abort once) | ||
415 | */ | ||
416 | |||
417 | return (inb(esp->eregs->io_addr - 0x0E) & 0x40) ? 1:0; | ||
418 | } | ||
419 | |||
420 | static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write) | ||
421 | { | ||
422 | if(write){ | ||
423 | dma_init_write(esp, addr, count); | ||
424 | } else { | ||
425 | dma_init_read(esp, addr, count); | ||
426 | } | ||
427 | } | ||
428 | |||
429 | /* | ||
430 | * These will not play nicely with other disk controllers that try to use the | ||
431 | * disk active LED... but what can you do? Don't answer that. | ||
432 | * | ||
433 | * Stolen shamelessly from ibmmca.c -- IBM Microchannel SCSI adapter driver | ||
434 | * | ||
435 | */ | ||
436 | |||
437 | static void dma_led_on(struct NCR_ESP *esp) | ||
438 | { | ||
439 | outb(inb(PS2_SYS_CTR) | 0xc0, PS2_SYS_CTR); | ||
440 | } | ||
441 | |||
442 | static void dma_led_off(struct NCR_ESP *esp) | ||
443 | { | ||
444 | outb(inb(PS2_SYS_CTR) & 0x3f, PS2_SYS_CTR); | ||
445 | } | ||
446 | |||
447 | static Scsi_Host_Template driver_template = { | ||
448 | .proc_name = "mca_53c9x", | ||
449 | .name = "NCR 53c9x SCSI", | ||
450 | .detect = mca_esp_detect, | ||
451 | .slave_alloc = esp_slave_alloc, | ||
452 | .slave_destroy = esp_slave_destroy, | ||
453 | .release = mca_esp_release, | ||
454 | .queuecommand = esp_queue, | ||
455 | .eh_abort_handler = esp_abort, | ||
456 | .eh_bus_reset_handler = esp_reset, | ||
457 | .can_queue = 7, | ||
458 | .sg_tablesize = SG_ALL, | ||
459 | .cmd_per_lun = 1, | ||
460 | .unchecked_isa_dma = 1, | ||
461 | .use_clustering = DISABLE_CLUSTERING | ||
462 | }; | ||
463 | |||
464 | |||
465 | #include "scsi_module.c" | ||
466 | |||
467 | /* | ||
468 | * OK, here's the goods I promised. The NCR 86C01 is an MCA interface chip | ||
469 | * that handles enabling/diabling IRQ, dma interfacing, IO port selection | ||
470 | * and other fun stuff. It takes up 16 addresses, and the chip it is | ||
471 | * connnected to gets the following 16. Registers are as follows: | ||
472 | * | ||
473 | * Offsets 0-1 : Card ID | ||
474 | * | ||
475 | * Offset 2 : Mode enable register -- | ||
476 | * Bit 7 : Data Word width (1 = 16, 0 = 8) | ||
477 | * Bit 6 : IRQ enable (1 = enabled) | ||
478 | * Bits 5,4 : IRQ select | ||
479 | * 0 0 : IRQ 3 | ||
480 | * 0 1 : IRQ 5 | ||
481 | * 1 0 : IRQ 7 | ||
482 | * 1 1 : IRQ 9 | ||
483 | * Bits 3-1 : Base Address | ||
484 | * 0 0 0 : <disabled> | ||
485 | * 0 0 1 : 0x0240 | ||
486 | * 0 1 0 : 0x0340 | ||
487 | * 0 1 1 : 0x0400 | ||
488 | * 1 0 0 : 0x0420 | ||
489 | * 1 0 1 : 0x3240 | ||
490 | * 1 1 0 : 0x8240 | ||
491 | * 1 1 1 : 0xA240 | ||
492 | * Bit 0 : Card enable (1 = enabled) | ||
493 | * | ||
494 | * Offset 3 : DMA control register -- | ||
495 | * Bit 7 : DMA enable (1 = enabled) | ||
496 | * Bits 6,5 : Preemt Count Select (transfers to complete after | ||
497 | * 'C01 has been preempted on MCA bus) | ||
498 | * 0 0 : 0 | ||
499 | * 0 1 : 1 | ||
500 | * 1 0 : 3 | ||
501 | * 1 1 : 7 | ||
502 | * (all these wacky numbers; I'm sure there's a reason somewhere) | ||
503 | * Bit 4 : Fairness enable (1 = fair bus priority) | ||
504 | * Bits 3-0 : Arbitration level (0-15 consecutive) | ||
505 | * | ||
506 | * Offset 4 : General purpose register | ||
507 | * Bits 7-3 : User definable (here, 7,6 are SCSI ID) | ||
508 | * Bits 2-0 : reserved | ||
509 | * | ||
510 | * Offset 10 : DMA decode register (used for IO based DMA; also can do | ||
511 | * PIO through this port) | ||
512 | * | ||
513 | * Offset 12 : Status | ||
514 | * Bits 7-2 : reserved | ||
515 | * Bit 1 : DMA pending (1 = pending) | ||
516 | * Bit 0 : IRQ pending (0 = pending) | ||
517 | * | ||
518 | * Exciting, huh? | ||
519 | * | ||
520 | */ | ||