Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Generic Generic NCR5380 driver | |
3 | * | |
4 | * Copyright 1993, Drew Eckhardt | |
5 | * Visionary Computing | |
6 | * (Unix and Linux consulting and custom programming) | |
7 | * drew@colorado.edu | |
8 | * +1 (303) 440-4894 | |
9 | * | |
10 | * NCR53C400 extensions (c) 1994,1995,1996, Kevin Lentin | |
11 | * K.Lentin@cs.monash.edu.au | |
12 | * | |
13 | * NCR53C400A extensions (c) 1996, Ingmar Baumgart | |
14 | * ingmar@gonzo.schwaben.de | |
15 | * | |
16 | * DTC3181E extensions (c) 1997, Ronald van Cuijlenborg | |
17 | * ronald.van.cuijlenborg@tip.nl or nutty@dds.nl | |
18 | * | |
19 | * Added ISAPNP support for DTC436 adapters, | |
20 | * Thomas Sailer, sailer@ife.ee.ethz.ch | |
1da177e4 | 21 | * |
9c41ab27 | 22 | * See Documentation/scsi/g_NCR5380.txt for more info. |
1da177e4 LT |
23 | */ |
24 | ||
1da177e4 | 25 | #include <asm/io.h> |
1da177e4 | 26 | #include <linux/blkdev.h> |
161c0059 | 27 | #include <linux/module.h> |
1da177e4 LT |
28 | #include <scsi/scsi_host.h> |
29 | #include "g_NCR5380.h" | |
30 | #include "NCR5380.h" | |
1da177e4 LT |
31 | #include <linux/init.h> |
32 | #include <linux/ioport.h> | |
33 | #include <linux/isapnp.h> | |
1da177e4 LT |
34 | #include <linux/interrupt.h> |
35 | ||
c0965e63 FT |
36 | static int ncr_irq; |
37 | static int ncr_dma; | |
38 | static int ncr_addr; | |
39 | static int ncr_5380; | |
40 | static int ncr_53c400; | |
41 | static int ncr_53c400a; | |
42 | static int dtc_3181e; | |
c6084cbc | 43 | static int hp_c2502; |
1da177e4 LT |
44 | |
45 | static struct override { | |
c818cb64 | 46 | NCR5380_map_type NCR5380_map_name; |
1da177e4 LT |
47 | int irq; |
48 | int dma; | |
49 | int board; /* Use NCR53c400, Ricoh, etc. extensions ? */ | |
50 | } overrides | |
51 | #ifdef GENERIC_NCR5380_OVERRIDE | |
52 | [] __initdata = GENERIC_NCR5380_OVERRIDE; | |
53 | #else | |
54 | [1] __initdata = { { 0,},}; | |
55 | #endif | |
56 | ||
6391a113 | 57 | #define NO_OVERRIDES ARRAY_SIZE(overrides) |
1da177e4 | 58 | |
6391a113 | 59 | #ifndef MODULE |
1da177e4 LT |
60 | |
61 | /** | |
62 | * internal_setup - handle lilo command string override | |
63 | * @board: BOARD_* identifier for the board | |
64 | * @str: unused | |
65 | * @ints: numeric parameters | |
66 | * | |
67 | * Do LILO command line initialization of the overrides array. Display | |
68 | * errors when needed | |
69 | * | |
70 | * Locks: none | |
71 | */ | |
72 | ||
73 | static void __init internal_setup(int board, char *str, int *ints) | |
74 | { | |
d5f7e65d | 75 | static int commandline_current; |
1da177e4 LT |
76 | switch (board) { |
77 | case BOARD_NCR5380: | |
78 | if (ints[0] != 2 && ints[0] != 3) { | |
79 | printk(KERN_ERR "generic_NCR5380_setup : usage ncr5380=" STRVAL(NCR5380_map_name) ",irq,dma\n"); | |
80 | return; | |
81 | } | |
82 | break; | |
83 | case BOARD_NCR53C400: | |
84 | if (ints[0] != 2) { | |
85 | printk(KERN_ERR "generic_NCR53C400_setup : usage ncr53c400=" STRVAL(NCR5380_map_name) ",irq\n"); | |
86 | return; | |
87 | } | |
88 | break; | |
89 | case BOARD_NCR53C400A: | |
90 | if (ints[0] != 2) { | |
91 | printk(KERN_ERR "generic_NCR53C400A_setup : usage ncr53c400a=" STRVAL(NCR5380_map_name) ",irq\n"); | |
92 | return; | |
93 | } | |
94 | break; | |
95 | case BOARD_DTC3181E: | |
96 | if (ints[0] != 2) { | |
97 | printk("generic_DTC3181E_setup : usage dtc3181e=" STRVAL(NCR5380_map_name) ",irq\n"); | |
98 | return; | |
99 | } | |
100 | break; | |
101 | } | |
102 | ||
103 | if (commandline_current < NO_OVERRIDES) { | |
104 | overrides[commandline_current].NCR5380_map_name = (NCR5380_map_type) ints[1]; | |
105 | overrides[commandline_current].irq = ints[2]; | |
106 | if (ints[0] == 3) | |
107 | overrides[commandline_current].dma = ints[3]; | |
108 | else | |
109 | overrides[commandline_current].dma = DMA_NONE; | |
110 | overrides[commandline_current].board = board; | |
111 | ++commandline_current; | |
112 | } | |
113 | } | |
114 | ||
115 | ||
116 | /** | |
117 | * do_NCR53C80_setup - set up entry point | |
118 | * @str: unused | |
119 | * | |
120 | * Setup function invoked at boot to parse the ncr5380= command | |
121 | * line. | |
122 | */ | |
123 | ||
124 | static int __init do_NCR5380_setup(char *str) | |
125 | { | |
126 | int ints[10]; | |
127 | ||
6391a113 | 128 | get_options(str, ARRAY_SIZE(ints), ints); |
1da177e4 LT |
129 | internal_setup(BOARD_NCR5380, str, ints); |
130 | return 1; | |
131 | } | |
132 | ||
133 | /** | |
134 | * do_NCR53C400_setup - set up entry point | |
135 | * @str: unused | |
6391a113 | 136 | * @ints: integer parameters from kernel setup code |
1da177e4 LT |
137 | * |
138 | * Setup function invoked at boot to parse the ncr53c400= command | |
139 | * line. | |
140 | */ | |
141 | ||
142 | static int __init do_NCR53C400_setup(char *str) | |
143 | { | |
144 | int ints[10]; | |
145 | ||
6391a113 | 146 | get_options(str, ARRAY_SIZE(ints), ints); |
1da177e4 LT |
147 | internal_setup(BOARD_NCR53C400, str, ints); |
148 | return 1; | |
149 | } | |
150 | ||
151 | /** | |
152 | * do_NCR53C400A_setup - set up entry point | |
153 | * @str: unused | |
6391a113 | 154 | * @ints: integer parameters from kernel setup code |
1da177e4 LT |
155 | * |
156 | * Setup function invoked at boot to parse the ncr53c400a= command | |
157 | * line. | |
158 | */ | |
159 | ||
160 | static int __init do_NCR53C400A_setup(char *str) | |
161 | { | |
162 | int ints[10]; | |
163 | ||
6391a113 | 164 | get_options(str, ARRAY_SIZE(ints), ints); |
1da177e4 LT |
165 | internal_setup(BOARD_NCR53C400A, str, ints); |
166 | return 1; | |
167 | } | |
168 | ||
169 | /** | |
170 | * do_DTC3181E_setup - set up entry point | |
171 | * @str: unused | |
6391a113 | 172 | * @ints: integer parameters from kernel setup code |
1da177e4 LT |
173 | * |
174 | * Setup function invoked at boot to parse the dtc3181e= command | |
175 | * line. | |
176 | */ | |
177 | ||
178 | static int __init do_DTC3181E_setup(char *str) | |
179 | { | |
180 | int ints[10]; | |
181 | ||
6391a113 | 182 | get_options(str, ARRAY_SIZE(ints), ints); |
1da177e4 LT |
183 | internal_setup(BOARD_DTC3181E, str, ints); |
184 | return 1; | |
185 | } | |
186 | ||
187 | #endif | |
188 | ||
c6084cbc OZ |
189 | #ifndef SCSI_G_NCR5380_MEM |
190 | /* | |
191 | * Configure I/O address of 53C400A or DTC436 by writing magic numbers | |
192 | * to ports 0x779 and 0x379. | |
193 | */ | |
194 | static void magic_configure(int idx, u8 irq, u8 magic[]) | |
195 | { | |
196 | u8 cfg = 0; | |
197 | ||
198 | outb(magic[0], 0x779); | |
199 | outb(magic[1], 0x379); | |
200 | outb(magic[2], 0x379); | |
201 | outb(magic[3], 0x379); | |
202 | outb(magic[4], 0x379); | |
203 | ||
204 | /* allowed IRQs for HP C2502 */ | |
205 | if (irq != 2 && irq != 3 && irq != 4 && irq != 5 && irq != 7) | |
206 | irq = 0; | |
207 | if (idx >= 0 && idx <= 7) | |
208 | cfg = 0x80 | idx | (irq << 4); | |
209 | outb(cfg, 0x379); | |
210 | } | |
211 | #endif | |
212 | ||
1da177e4 LT |
213 | /** |
214 | * generic_NCR5380_detect - look for NCR5380 controllers | |
215 | * @tpnt: the scsi template | |
216 | * | |
217 | * Scan for the present of NCR5380, NCR53C400, NCR53C400A, DTC3181E | |
218 | * and DTC436(ISAPnP) controllers. If overrides have been set we use | |
219 | * them. | |
220 | * | |
1da177e4 LT |
221 | * Locks: none |
222 | */ | |
223 | ||
ed8b9e7f | 224 | static int __init generic_NCR5380_detect(struct scsi_host_template *tpnt) |
1da177e4 | 225 | { |
d5f7e65d | 226 | static int current_override; |
702a98c6 | 227 | int count; |
1da177e4 | 228 | unsigned int *ports; |
c6084cbc | 229 | u8 *magic = NULL; |
702a98c6 OZ |
230 | #ifndef SCSI_G_NCR5380_MEM |
231 | int i; | |
c6084cbc | 232 | int port_idx = -1; |
9d376402 | 233 | unsigned long region_size; |
702a98c6 | 234 | #endif |
1da177e4 LT |
235 | static unsigned int __initdata ncr_53c400a_ports[] = { |
236 | 0x280, 0x290, 0x300, 0x310, 0x330, 0x340, 0x348, 0x350, 0 | |
237 | }; | |
238 | static unsigned int __initdata dtc_3181e_ports[] = { | |
239 | 0x220, 0x240, 0x280, 0x2a0, 0x2c0, 0x300, 0x320, 0x340, 0 | |
240 | }; | |
c6084cbc OZ |
241 | static u8 ncr_53c400a_magic[] __initdata = { /* 53C400A & DTC436 */ |
242 | 0x59, 0xb9, 0xc5, 0xae, 0xa6 | |
243 | }; | |
244 | static u8 hp_c2502_magic[] __initdata = { /* HP C2502 */ | |
245 | 0x0f, 0x22, 0xf0, 0x20, 0x80 | |
246 | }; | |
4d8c08c7 | 247 | int flags; |
1da177e4 | 248 | struct Scsi_Host *instance; |
12150797 | 249 | struct NCR5380_hostdata *hostdata; |
702a98c6 | 250 | #ifdef SCSI_G_NCR5380_MEM |
c818cb64 AV |
251 | unsigned long base; |
252 | void __iomem *iomem; | |
9d376402 | 253 | resource_size_t iomem_size; |
c818cb64 | 254 | #endif |
1da177e4 | 255 | |
c0965e63 | 256 | if (ncr_irq) |
1da177e4 | 257 | overrides[0].irq = ncr_irq; |
c0965e63 | 258 | if (ncr_dma) |
1da177e4 | 259 | overrides[0].dma = ncr_dma; |
c0965e63 | 260 | if (ncr_addr) |
1da177e4 | 261 | overrides[0].NCR5380_map_name = (NCR5380_map_type) ncr_addr; |
c0965e63 | 262 | if (ncr_5380) |
1da177e4 | 263 | overrides[0].board = BOARD_NCR5380; |
c0965e63 | 264 | else if (ncr_53c400) |
1da177e4 | 265 | overrides[0].board = BOARD_NCR53C400; |
c0965e63 | 266 | else if (ncr_53c400a) |
1da177e4 | 267 | overrides[0].board = BOARD_NCR53C400A; |
c0965e63 | 268 | else if (dtc_3181e) |
1da177e4 | 269 | overrides[0].board = BOARD_DTC3181E; |
c6084cbc OZ |
270 | else if (hp_c2502) |
271 | overrides[0].board = BOARD_HP_C2502; | |
702a98c6 | 272 | #ifndef SCSI_G_NCR5380_MEM |
1da177e4 LT |
273 | if (!current_override && isapnp_present()) { |
274 | struct pnp_dev *dev = NULL; | |
275 | count = 0; | |
276 | while ((dev = pnp_find_dev(NULL, ISAPNP_VENDOR('D', 'T', 'C'), ISAPNP_FUNCTION(0x436e), dev))) { | |
277 | if (count >= NO_OVERRIDES) | |
278 | break; | |
c94babba | 279 | if (pnp_device_attach(dev) < 0) |
1da177e4 | 280 | continue; |
1da177e4 LT |
281 | if (pnp_activate_dev(dev) < 0) { |
282 | printk(KERN_ERR "dtc436e probe: activate failed\n"); | |
283 | pnp_device_detach(dev); | |
284 | continue; | |
285 | } | |
286 | if (!pnp_port_valid(dev, 0)) { | |
287 | printk(KERN_ERR "dtc436e probe: no valid port\n"); | |
288 | pnp_device_detach(dev); | |
289 | continue; | |
290 | } | |
291 | if (pnp_irq_valid(dev, 0)) | |
292 | overrides[count].irq = pnp_irq(dev, 0); | |
293 | else | |
22f5f10d | 294 | overrides[count].irq = NO_IRQ; |
1da177e4 LT |
295 | if (pnp_dma_valid(dev, 0)) |
296 | overrides[count].dma = pnp_dma(dev, 0); | |
297 | else | |
298 | overrides[count].dma = DMA_NONE; | |
299 | overrides[count].NCR5380_map_name = (NCR5380_map_type) pnp_port_start(dev, 0); | |
300 | overrides[count].board = BOARD_DTC3181E; | |
301 | count++; | |
302 | } | |
303 | } | |
702a98c6 | 304 | #endif |
1da177e4 LT |
305 | |
306 | for (count = 0; current_override < NO_OVERRIDES; ++current_override) { | |
307 | if (!(overrides[current_override].NCR5380_map_name)) | |
308 | continue; | |
309 | ||
310 | ports = NULL; | |
4d8c08c7 | 311 | flags = 0; |
1da177e4 LT |
312 | switch (overrides[current_override].board) { |
313 | case BOARD_NCR5380: | |
1bb46002 | 314 | flags = FLAG_NO_PSEUDO_DMA | FLAG_DMA_FIXUP; |
1da177e4 LT |
315 | break; |
316 | case BOARD_NCR53C400A: | |
1da177e4 | 317 | ports = ncr_53c400a_ports; |
c6084cbc OZ |
318 | magic = ncr_53c400a_magic; |
319 | break; | |
320 | case BOARD_HP_C2502: | |
c6084cbc OZ |
321 | ports = ncr_53c400a_ports; |
322 | magic = hp_c2502_magic; | |
1da177e4 LT |
323 | break; |
324 | case BOARD_DTC3181E: | |
1da177e4 | 325 | ports = dtc_3181e_ports; |
c6084cbc | 326 | magic = ncr_53c400a_magic; |
1da177e4 LT |
327 | break; |
328 | } | |
329 | ||
702a98c6 | 330 | #ifndef SCSI_G_NCR5380_MEM |
c6084cbc | 331 | if (ports && magic) { |
1da177e4 LT |
332 | /* wakeup sequence for the NCR53C400A and DTC3181E */ |
333 | ||
334 | /* Disable the adapter and look for a free io port */ | |
c6084cbc | 335 | magic_configure(-1, 0, magic); |
1da177e4 | 336 | |
9d376402 FT |
337 | region_size = 16; |
338 | ||
1da177e4 LT |
339 | if (overrides[current_override].NCR5380_map_name != PORT_AUTO) |
340 | for (i = 0; ports[i]; i++) { | |
9d376402 | 341 | if (!request_region(ports[i], region_size, "ncr53c80")) |
1da177e4 LT |
342 | continue; |
343 | if (overrides[current_override].NCR5380_map_name == ports[i]) | |
344 | break; | |
9d376402 | 345 | release_region(ports[i], region_size); |
1da177e4 LT |
346 | } else |
347 | for (i = 0; ports[i]; i++) { | |
9d376402 | 348 | if (!request_region(ports[i], region_size, "ncr53c80")) |
1da177e4 LT |
349 | continue; |
350 | if (inb(ports[i]) == 0xff) | |
351 | break; | |
9d376402 | 352 | release_region(ports[i], region_size); |
1da177e4 LT |
353 | } |
354 | if (ports[i]) { | |
355 | /* At this point we have our region reserved */ | |
c6084cbc | 356 | magic_configure(i, 0, magic); /* no IRQ yet */ |
1da177e4 LT |
357 | outb(0xc0, ports[i] + 9); |
358 | if (inb(ports[i] + 9) != 0x80) | |
359 | continue; | |
c6084cbc OZ |
360 | overrides[current_override].NCR5380_map_name = ports[i]; |
361 | port_idx = i; | |
1da177e4 LT |
362 | } else |
363 | continue; | |
364 | } | |
365 | else | |
366 | { | |
367 | /* Not a 53C400A style setup - just grab */ | |
9d376402 FT |
368 | region_size = 8; |
369 | if (!request_region(overrides[current_override].NCR5380_map_name, | |
370 | region_size, "ncr5380")) | |
1da177e4 | 371 | continue; |
1da177e4 LT |
372 | } |
373 | #else | |
c818cb64 | 374 | base = overrides[current_override].NCR5380_map_name; |
9d376402 FT |
375 | iomem_size = NCR53C400_region_size; |
376 | if (!request_mem_region(base, iomem_size, "ncr5380")) | |
c818cb64 | 377 | continue; |
9d376402 | 378 | iomem = ioremap(base, iomem_size); |
c818cb64 | 379 | if (!iomem) { |
9d376402 | 380 | release_mem_region(base, iomem_size); |
1da177e4 | 381 | continue; |
c818cb64 | 382 | } |
1da177e4 LT |
383 | #endif |
384 | instance = scsi_register(tpnt, sizeof(struct NCR5380_hostdata)); | |
0ad0eff9 FT |
385 | if (instance == NULL) |
386 | goto out_release; | |
12150797 | 387 | hostdata = shost_priv(instance); |
1da177e4 | 388 | |
702a98c6 | 389 | #ifndef SCSI_G_NCR5380_MEM |
b01ec348 | 390 | instance->io_port = overrides[current_override].NCR5380_map_name; |
1da177e4 | 391 | instance->n_io_port = region_size; |
aeb51152 | 392 | hostdata->io_width = 1; /* 8-bit PDMA by default */ |
4d8c08c7 FT |
393 | |
394 | /* | |
395 | * On NCR53C400 boards, NCR5380 registers are mapped 8 past | |
396 | * the base address. | |
397 | */ | |
cecf3bee OZ |
398 | switch (overrides[current_override].board) { |
399 | case BOARD_NCR53C400: | |
4d8c08c7 | 400 | instance->io_port += 8; |
12150797 OZ |
401 | hostdata->c400_ctl_status = 0; |
402 | hostdata->c400_blk_cnt = 1; | |
403 | hostdata->c400_host_buf = 4; | |
cecf3bee | 404 | break; |
aeb51152 OZ |
405 | case BOARD_DTC3181E: |
406 | hostdata->io_width = 2; /* 16-bit PDMA */ | |
407 | /* fall through */ | |
cecf3bee | 408 | case BOARD_NCR53C400A: |
c6084cbc | 409 | case BOARD_HP_C2502: |
cecf3bee OZ |
410 | hostdata->c400_ctl_status = 9; |
411 | hostdata->c400_blk_cnt = 10; | |
412 | hostdata->c400_host_buf = 8; | |
413 | break; | |
12150797 | 414 | } |
c818cb64 | 415 | #else |
b01ec348 | 416 | instance->base = overrides[current_override].NCR5380_map_name; |
12150797 | 417 | hostdata->iomem = iomem; |
9d376402 | 418 | hostdata->iomem_size = iomem_size; |
cecf3bee OZ |
419 | switch (overrides[current_override].board) { |
420 | case BOARD_NCR53C400: | |
12150797 OZ |
421 | hostdata->c400_ctl_status = 0x100; |
422 | hostdata->c400_blk_cnt = 0x101; | |
423 | hostdata->c400_host_buf = 0x104; | |
cecf3bee | 424 | break; |
aeb51152 | 425 | case BOARD_DTC3181E: |
cecf3bee | 426 | case BOARD_NCR53C400A: |
c6084cbc | 427 | case BOARD_HP_C2502: |
cecf3bee OZ |
428 | pr_err(DRV_MODULE_NAME ": unknown register offsets\n"); |
429 | goto out_unregister; | |
12150797 | 430 | } |
1da177e4 LT |
431 | #endif |
432 | ||
8053b0ee | 433 | if (NCR5380_init(instance, flags | FLAG_LATE_DMA_SETUP)) |
0ad0eff9 | 434 | goto out_unregister; |
1da177e4 | 435 | |
cecf3bee OZ |
436 | switch (overrides[current_override].board) { |
437 | case BOARD_NCR53C400: | |
aeb51152 | 438 | case BOARD_DTC3181E: |
cecf3bee | 439 | case BOARD_NCR53C400A: |
c6084cbc | 440 | case BOARD_HP_C2502: |
12150797 | 441 | NCR5380_write(hostdata->c400_ctl_status, CSR_BASE); |
cecf3bee | 442 | } |
4d8c08c7 | 443 | |
b6488f97 FT |
444 | NCR5380_maybe_reset_bus(instance); |
445 | ||
1da177e4 LT |
446 | if (overrides[current_override].irq != IRQ_AUTO) |
447 | instance->irq = overrides[current_override].irq; | |
448 | else | |
449 | instance->irq = NCR5380_probe_irq(instance, 0xffff); | |
450 | ||
22f5f10d FT |
451 | /* Compatibility with documented NCR5380 kernel parameters */ |
452 | if (instance->irq == 255) | |
453 | instance->irq = NO_IRQ; | |
454 | ||
c6084cbc OZ |
455 | if (instance->irq != NO_IRQ) { |
456 | #ifndef SCSI_G_NCR5380_MEM | |
457 | /* set IRQ for HP C2502 */ | |
458 | if (overrides[current_override].board == BOARD_HP_C2502) | |
459 | magic_configure(port_idx, instance->irq, magic); | |
460 | #endif | |
1e641664 | 461 | if (request_irq(instance->irq, generic_NCR5380_intr, |
4909cc2b | 462 | 0, "NCR5380", instance)) { |
1da177e4 | 463 | printk(KERN_WARNING "scsi%d : IRQ%d not free, interrupts disabled\n", instance->host_no, instance->irq); |
22f5f10d | 464 | instance->irq = NO_IRQ; |
1da177e4 | 465 | } |
c6084cbc | 466 | } |
1da177e4 | 467 | |
22f5f10d | 468 | if (instance->irq == NO_IRQ) { |
1da177e4 LT |
469 | printk(KERN_INFO "scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no); |
470 | printk(KERN_INFO "scsi%d : please jumper the board for a free IRQ.\n", instance->host_no); | |
471 | } | |
472 | ||
1da177e4 LT |
473 | ++current_override; |
474 | ++count; | |
475 | } | |
476 | return count; | |
0ad0eff9 FT |
477 | |
478 | out_unregister: | |
479 | scsi_unregister(instance); | |
480 | out_release: | |
481 | #ifndef SCSI_G_NCR5380_MEM | |
482 | release_region(overrides[current_override].NCR5380_map_name, region_size); | |
483 | #else | |
484 | iounmap(iomem); | |
9d376402 | 485 | release_mem_region(base, iomem_size); |
0ad0eff9 FT |
486 | #endif |
487 | return count; | |
1da177e4 LT |
488 | } |
489 | ||
1da177e4 LT |
490 | /** |
491 | * generic_NCR5380_release_resources - free resources | |
492 | * @instance: host adapter to clean up | |
493 | * | |
494 | * Free the generic interface resources from this adapter. | |
495 | * | |
496 | * Locks: none | |
497 | */ | |
498 | ||
ed8b9e7f | 499 | static int generic_NCR5380_release_resources(struct Scsi_Host *instance) |
1da177e4 | 500 | { |
22f5f10d | 501 | if (instance->irq != NO_IRQ) |
1e641664 | 502 | free_irq(instance->irq, instance); |
1da177e4 | 503 | NCR5380_exit(instance); |
702a98c6 | 504 | #ifndef SCSI_G_NCR5380_MEM |
b01ec348 | 505 | release_region(instance->io_port, instance->n_io_port); |
1da177e4 | 506 | #else |
9d376402 FT |
507 | { |
508 | struct NCR5380_hostdata *hostdata = shost_priv(instance); | |
1da177e4 | 509 | |
9d376402 FT |
510 | iounmap(hostdata->iomem); |
511 | release_mem_region(instance->base, hostdata->iomem_size); | |
512 | } | |
513 | #endif | |
1da177e4 LT |
514 | return 0; |
515 | } | |
1da177e4 LT |
516 | |
517 | /** | |
6c4b88ca | 518 | * generic_NCR5380_pread - pseudo DMA read |
1da177e4 LT |
519 | * @instance: adapter to read from |
520 | * @dst: buffer to read into | |
521 | * @len: buffer length | |
522 | * | |
25985edc | 523 | * Perform a pseudo DMA mode read from an NCR53C400 or equivalent |
1da177e4 LT |
524 | * controller |
525 | */ | |
526 | ||
6c4b88ca FT |
527 | static inline int generic_NCR5380_pread(struct Scsi_Host *instance, |
528 | unsigned char *dst, int len) | |
1da177e4 | 529 | { |
54d8fe44 | 530 | struct NCR5380_hostdata *hostdata = shost_priv(instance); |
1da177e4 LT |
531 | int blocks = len / 128; |
532 | int start = 0; | |
1da177e4 | 533 | |
12150797 OZ |
534 | NCR5380_write(hostdata->c400_ctl_status, CSR_BASE | CSR_TRANS_DIR); |
535 | NCR5380_write(hostdata->c400_blk_cnt, blocks); | |
1da177e4 | 536 | while (1) { |
12150797 | 537 | if (NCR5380_read(hostdata->c400_blk_cnt) == 0) |
1da177e4 | 538 | break; |
12150797 | 539 | if (NCR5380_read(hostdata->c400_ctl_status) & CSR_GATED_53C80_IRQ) { |
1da177e4 LT |
540 | printk(KERN_ERR "53C400r: Got 53C80_IRQ start=%d, blocks=%d\n", start, blocks); |
541 | return -1; | |
542 | } | |
12150797 OZ |
543 | while (NCR5380_read(hostdata->c400_ctl_status) & CSR_HOST_BUF_NOT_RDY) |
544 | ; /* FIXME - no timeout */ | |
1da177e4 | 545 | |
702a98c6 | 546 | #ifndef SCSI_G_NCR5380_MEM |
aeb51152 OZ |
547 | if (hostdata->io_width == 2) |
548 | insw(instance->io_port + hostdata->c400_host_buf, | |
549 | dst + start, 64); | |
550 | else | |
551 | insb(instance->io_port + hostdata->c400_host_buf, | |
12150797 | 552 | dst + start, 128); |
1da177e4 | 553 | #else |
702a98c6 | 554 | /* implies SCSI_G_NCR5380_MEM */ |
54d8fe44 FT |
555 | memcpy_fromio(dst + start, |
556 | hostdata->iomem + NCR53C400_host_buffer, 128); | |
1da177e4 LT |
557 | #endif |
558 | start += 128; | |
559 | blocks--; | |
560 | } | |
561 | ||
562 | if (blocks) { | |
12150797 OZ |
563 | while (NCR5380_read(hostdata->c400_ctl_status) & CSR_HOST_BUF_NOT_RDY) |
564 | ; /* FIXME - no timeout */ | |
1da177e4 | 565 | |
702a98c6 | 566 | #ifndef SCSI_G_NCR5380_MEM |
aeb51152 OZ |
567 | if (hostdata->io_width == 2) |
568 | insw(instance->io_port + hostdata->c400_host_buf, | |
569 | dst + start, 64); | |
570 | else | |
571 | insb(instance->io_port + hostdata->c400_host_buf, | |
12150797 | 572 | dst + start, 128); |
1da177e4 | 573 | #else |
702a98c6 | 574 | /* implies SCSI_G_NCR5380_MEM */ |
54d8fe44 FT |
575 | memcpy_fromio(dst + start, |
576 | hostdata->iomem + NCR53C400_host_buffer, 128); | |
1da177e4 LT |
577 | #endif |
578 | start += 128; | |
579 | blocks--; | |
580 | } | |
581 | ||
12150797 | 582 | if (!(NCR5380_read(hostdata->c400_ctl_status) & CSR_GATED_53C80_IRQ)) |
1da177e4 LT |
583 | printk("53C400r: no 53C80 gated irq after transfer"); |
584 | ||
42fc6370 OZ |
585 | /* wait for 53C80 registers to be available */ |
586 | while (!(NCR5380_read(hostdata->c400_ctl_status) & CSR_53C80_REG)) | |
1da177e4 | 587 | ; |
42fc6370 | 588 | |
1da177e4 LT |
589 | if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_END_DMA_TRANSFER)) |
590 | printk(KERN_ERR "53C400r: no end dma signal\n"); | |
591 | ||
1da177e4 LT |
592 | return 0; |
593 | } | |
594 | ||
595 | /** | |
6c4b88ca | 596 | * generic_NCR5380_pwrite - pseudo DMA write |
1da177e4 LT |
597 | * @instance: adapter to read from |
598 | * @dst: buffer to read into | |
599 | * @len: buffer length | |
600 | * | |
25985edc | 601 | * Perform a pseudo DMA mode read from an NCR53C400 or equivalent |
1da177e4 LT |
602 | * controller |
603 | */ | |
604 | ||
6c4b88ca FT |
605 | static inline int generic_NCR5380_pwrite(struct Scsi_Host *instance, |
606 | unsigned char *src, int len) | |
1da177e4 | 607 | { |
54d8fe44 | 608 | struct NCR5380_hostdata *hostdata = shost_priv(instance); |
1da177e4 LT |
609 | int blocks = len / 128; |
610 | int start = 0; | |
1da177e4 | 611 | |
12150797 OZ |
612 | NCR5380_write(hostdata->c400_ctl_status, CSR_BASE); |
613 | NCR5380_write(hostdata->c400_blk_cnt, blocks); | |
1da177e4 | 614 | while (1) { |
12150797 | 615 | if (NCR5380_read(hostdata->c400_ctl_status) & CSR_GATED_53C80_IRQ) { |
1da177e4 LT |
616 | printk(KERN_ERR "53C400w: Got 53C80_IRQ start=%d, blocks=%d\n", start, blocks); |
617 | return -1; | |
618 | } | |
619 | ||
12150797 | 620 | if (NCR5380_read(hostdata->c400_blk_cnt) == 0) |
1da177e4 | 621 | break; |
12150797 | 622 | while (NCR5380_read(hostdata->c400_ctl_status) & CSR_HOST_BUF_NOT_RDY) |
1da177e4 | 623 | ; // FIXME - timeout |
702a98c6 | 624 | #ifndef SCSI_G_NCR5380_MEM |
aeb51152 OZ |
625 | if (hostdata->io_width == 2) |
626 | outsw(instance->io_port + hostdata->c400_host_buf, | |
627 | src + start, 64); | |
628 | else | |
629 | outsb(instance->io_port + hostdata->c400_host_buf, | |
12150797 | 630 | src + start, 128); |
1da177e4 | 631 | #else |
702a98c6 | 632 | /* implies SCSI_G_NCR5380_MEM */ |
54d8fe44 FT |
633 | memcpy_toio(hostdata->iomem + NCR53C400_host_buffer, |
634 | src + start, 128); | |
1da177e4 LT |
635 | #endif |
636 | start += 128; | |
637 | blocks--; | |
638 | } | |
639 | if (blocks) { | |
12150797 | 640 | while (NCR5380_read(hostdata->c400_ctl_status) & CSR_HOST_BUF_NOT_RDY) |
1da177e4 LT |
641 | ; // FIXME - no timeout |
642 | ||
702a98c6 | 643 | #ifndef SCSI_G_NCR5380_MEM |
aeb51152 OZ |
644 | if (hostdata->io_width == 2) |
645 | outsw(instance->io_port + hostdata->c400_host_buf, | |
646 | src + start, 64); | |
647 | else | |
648 | outsb(instance->io_port + hostdata->c400_host_buf, | |
12150797 | 649 | src + start, 128); |
1da177e4 | 650 | #else |
702a98c6 | 651 | /* implies SCSI_G_NCR5380_MEM */ |
54d8fe44 FT |
652 | memcpy_toio(hostdata->iomem + NCR53C400_host_buffer, |
653 | src + start, 128); | |
1da177e4 LT |
654 | #endif |
655 | start += 128; | |
656 | blocks--; | |
657 | } | |
658 | ||
42fc6370 OZ |
659 | /* wait for 53C80 registers to be available */ |
660 | while (!(NCR5380_read(hostdata->c400_ctl_status) & CSR_53C80_REG)) { | |
aeb51152 OZ |
661 | udelay(4); /* DTC436 chip hangs without this */ |
662 | /* FIXME - no timeout */ | |
663 | } | |
1da177e4 | 664 | |
1da177e4 LT |
665 | if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_END_DMA_TRANSFER)) { |
666 | printk(KERN_ERR "53C400w: no end dma signal\n"); | |
667 | } | |
42fc6370 | 668 | |
1da177e4 LT |
669 | while (!(NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT)) |
670 | ; // TIMEOUT | |
671 | return 0; | |
672 | } | |
ff3d4578 | 673 | |
7e9ec8d9 FT |
674 | static int generic_NCR5380_dma_xfer_len(struct Scsi_Host *instance, |
675 | struct scsi_cmnd *cmd) | |
ff3d4578 | 676 | { |
7e9ec8d9 | 677 | struct NCR5380_hostdata *hostdata = shost_priv(instance); |
ff3d4578 FT |
678 | int transfersize = cmd->transfersize; |
679 | ||
7e9ec8d9 FT |
680 | if (hostdata->flags & FLAG_NO_PSEUDO_DMA) |
681 | return 0; | |
682 | ||
ff3d4578 FT |
683 | /* Limit transfers to 32K, for xx400 & xx406 |
684 | * pseudoDMA that transfers in 128 bytes blocks. | |
685 | */ | |
686 | if (transfersize > 32 * 1024 && cmd->SCp.this_residual && | |
687 | !(cmd->SCp.this_residual % transfersize)) | |
688 | transfersize = 32 * 1024; | |
689 | ||
f0394621 OZ |
690 | /* 53C400 datasheet: non-modulo-128-byte transfers should use PIO */ |
691 | if (transfersize % 128) | |
692 | transfersize = 0; | |
693 | ||
ff3d4578 FT |
694 | return transfersize; |
695 | } | |
696 | ||
1da177e4 LT |
697 | /* |
698 | * Include the NCR5380 core code that we build our driver around | |
699 | */ | |
700 | ||
701 | #include "NCR5380.c" | |
702 | ||
d0be4a7d | 703 | static struct scsi_host_template driver_template = { |
aa2e2cb1 | 704 | .proc_name = DRV_MODULE_NAME, |
aa2e2cb1 FT |
705 | .name = "Generic NCR5380/NCR53C400 SCSI", |
706 | .detect = generic_NCR5380_detect, | |
707 | .release = generic_NCR5380_release_resources, | |
708 | .info = generic_NCR5380_info, | |
709 | .queuecommand = generic_NCR5380_queue_command, | |
1da177e4 LT |
710 | .eh_abort_handler = generic_NCR5380_abort, |
711 | .eh_bus_reset_handler = generic_NCR5380_bus_reset, | |
aa2e2cb1 FT |
712 | .can_queue = 16, |
713 | .this_id = 7, | |
714 | .sg_tablesize = SG_ALL, | |
715 | .cmd_per_lun = 2, | |
716 | .use_clustering = DISABLE_CLUSTERING, | |
32b26a10 | 717 | .cmd_size = NCR5380_CMD_SIZE, |
0a4e3612 | 718 | .max_sectors = 128, |
1da177e4 | 719 | }; |
161c0059 | 720 | |
1da177e4 LT |
721 | #include "scsi_module.c" |
722 | ||
723 | module_param(ncr_irq, int, 0); | |
724 | module_param(ncr_dma, int, 0); | |
725 | module_param(ncr_addr, int, 0); | |
726 | module_param(ncr_5380, int, 0); | |
727 | module_param(ncr_53c400, int, 0); | |
728 | module_param(ncr_53c400a, int, 0); | |
729 | module_param(dtc_3181e, int, 0); | |
c6084cbc | 730 | module_param(hp_c2502, int, 0); |
1da177e4 LT |
731 | MODULE_LICENSE("GPL"); |
732 | ||
b026e8ed | 733 | #if !defined(SCSI_G_NCR5380_MEM) && defined(MODULE) |
6f039790 | 734 | static struct isapnp_device_id id_table[] = { |
1da177e4 LT |
735 | { |
736 | ISAPNP_ANY_ID, ISAPNP_ANY_ID, | |
737 | ISAPNP_VENDOR('D', 'T', 'C'), ISAPNP_FUNCTION(0x436e), | |
738 | 0}, | |
739 | {0} | |
740 | }; | |
741 | ||
742 | MODULE_DEVICE_TABLE(isapnp, id_table); | |
702a98c6 | 743 | #endif |
1da177e4 LT |
744 | |
745 | __setup("ncr5380=", do_NCR5380_setup); | |
746 | __setup("ncr53c400=", do_NCR53C400_setup); | |
747 | __setup("ncr53c400a=", do_NCR53C400A_setup); | |
748 | __setup("dtc3181e=", do_DTC3181E_setup); |