]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * linux/drivers/acorn/scsi/eesox.c | |
3 | * | |
4 | * Copyright (C) 1997-2005 Russell King | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 as | |
8 | * published by the Free Software Foundation. | |
9 | * | |
10 | * This driver is based on experimentation. Hence, it may have made | |
11 | * assumptions about the particular card that I have available, and | |
12 | * may not be reliable! | |
13 | * | |
14 | * Changelog: | |
15 | * 01-10-1997 RMK Created, READONLY version | |
16 | * 15-02-1998 RMK READ/WRITE version | |
17 | * added DMA support and hardware definitions | |
18 | * 14-03-1998 RMK Updated DMA support | |
19 | * Added terminator control | |
20 | * 15-04-1998 RMK Only do PIO if FAS216 will allow it. | |
21 | * 27-06-1998 RMK Changed asm/delay.h to linux/delay.h | |
22 | * 02-04-2000 RMK 0.0.3 Fixed NO_IRQ/NO_DMA problem, updated for new | |
23 | * error handling code. | |
24 | */ | |
25 | #include <linux/module.h> | |
26 | #include <linux/blkdev.h> | |
27 | #include <linux/kernel.h> | |
28 | #include <linux/string.h> | |
29 | #include <linux/ioport.h> | |
1da177e4 LT |
30 | #include <linux/proc_fs.h> |
31 | #include <linux/delay.h> | |
32 | #include <linux/interrupt.h> | |
33 | #include <linux/init.h> | |
34 | #include <linux/dma-mapping.h> | |
35 | ||
36 | #include <asm/io.h> | |
1da177e4 LT |
37 | #include <asm/dma.h> |
38 | #include <asm/ecard.h> | |
39 | #include <asm/pgtable.h> | |
40 | ||
41 | #include "../scsi.h" | |
42 | #include <scsi/scsi_host.h> | |
43 | #include "fas216.h" | |
44 | #include "scsi.h" | |
45 | ||
46 | #include <scsi/scsicam.h> | |
47 | ||
48 | #define EESOX_FAS216_OFFSET 0x3000 | |
49 | #define EESOX_FAS216_SHIFT 5 | |
50 | ||
51 | #define EESOX_DMASTAT 0x2800 | |
52 | #define EESOX_STAT_INTR 0x01 | |
53 | #define EESOX_STAT_DMA 0x02 | |
54 | ||
55 | #define EESOX_CONTROL 0x2800 | |
56 | #define EESOX_INTR_ENABLE 0x04 | |
57 | #define EESOX_TERM_ENABLE 0x02 | |
58 | #define EESOX_RESET 0x01 | |
59 | ||
60 | #define EESOX_DMADATA 0x3800 | |
61 | ||
62 | #define VERSION "1.10 (17/01/2003 2.5.59)" | |
63 | ||
64 | /* | |
65 | * Use term=0,1,0,0,0 to turn terminators on/off | |
66 | */ | |
67 | static int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 }; | |
68 | ||
69 | #define NR_SG 256 | |
70 | ||
71 | struct eesoxscsi_info { | |
72 | FAS216_Info info; | |
73 | struct expansion_card *ec; | |
74 | void __iomem *base; | |
75 | void __iomem *ctl_port; | |
76 | unsigned int control; | |
77 | struct scatterlist sg[NR_SG]; /* Scatter DMA list */ | |
78 | }; | |
79 | ||
80 | /* Prototype: void eesoxscsi_irqenable(ec, irqnr) | |
81 | * Purpose : Enable interrupts on EESOX SCSI card | |
82 | * Params : ec - expansion card structure | |
83 | * : irqnr - interrupt number | |
84 | */ | |
85 | static void | |
86 | eesoxscsi_irqenable(struct expansion_card *ec, int irqnr) | |
87 | { | |
88 | struct eesoxscsi_info *info = (struct eesoxscsi_info *)ec->irq_data; | |
89 | ||
90 | info->control |= EESOX_INTR_ENABLE; | |
91 | ||
92 | writeb(info->control, info->ctl_port); | |
93 | } | |
94 | ||
95 | /* Prototype: void eesoxscsi_irqdisable(ec, irqnr) | |
96 | * Purpose : Disable interrupts on EESOX SCSI card | |
97 | * Params : ec - expansion card structure | |
98 | * : irqnr - interrupt number | |
99 | */ | |
100 | static void | |
101 | eesoxscsi_irqdisable(struct expansion_card *ec, int irqnr) | |
102 | { | |
103 | struct eesoxscsi_info *info = (struct eesoxscsi_info *)ec->irq_data; | |
104 | ||
105 | info->control &= ~EESOX_INTR_ENABLE; | |
106 | ||
107 | writeb(info->control, info->ctl_port); | |
108 | } | |
109 | ||
110 | static const expansioncard_ops_t eesoxscsi_ops = { | |
111 | .irqenable = eesoxscsi_irqenable, | |
112 | .irqdisable = eesoxscsi_irqdisable, | |
113 | }; | |
114 | ||
115 | /* Prototype: void eesoxscsi_terminator_ctl(*host, on_off) | |
116 | * Purpose : Turn the EESOX SCSI terminators on or off | |
117 | * Params : host - card to turn on/off | |
118 | * : on_off - !0 to turn on, 0 to turn off | |
119 | */ | |
120 | static void | |
121 | eesoxscsi_terminator_ctl(struct Scsi_Host *host, int on_off) | |
122 | { | |
123 | struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata; | |
124 | unsigned long flags; | |
125 | ||
126 | spin_lock_irqsave(host->host_lock, flags); | |
127 | if (on_off) | |
128 | info->control |= EESOX_TERM_ENABLE; | |
129 | else | |
130 | info->control &= ~EESOX_TERM_ENABLE; | |
131 | ||
132 | writeb(info->control, info->ctl_port); | |
133 | spin_unlock_irqrestore(host->host_lock, flags); | |
134 | } | |
135 | ||
136 | /* Prototype: void eesoxscsi_intr(irq, *dev_id, *regs) | |
137 | * Purpose : handle interrupts from EESOX SCSI card | |
138 | * Params : irq - interrupt number | |
139 | * dev_id - user-defined (Scsi_Host structure) | |
1da177e4 LT |
140 | */ |
141 | static irqreturn_t | |
7d12e780 | 142 | eesoxscsi_intr(int irq, void *dev_id) |
1da177e4 LT |
143 | { |
144 | struct eesoxscsi_info *info = dev_id; | |
145 | ||
146 | return fas216_intr(&info->info); | |
147 | } | |
148 | ||
149 | /* Prototype: fasdmatype_t eesoxscsi_dma_setup(host, SCpnt, direction, min_type) | |
150 | * Purpose : initialises DMA/PIO | |
151 | * Params : host - host | |
152 | * SCpnt - command | |
153 | * direction - DMA on to/off of card | |
154 | * min_type - minimum DMA support that we must have for this transfer | |
155 | * Returns : type of transfer to be performed | |
156 | */ | |
157 | static fasdmatype_t | |
0a04137e | 158 | eesoxscsi_dma_setup(struct Scsi_Host *host, struct scsi_pointer *SCp, |
1da177e4 LT |
159 | fasdmadir_t direction, fasdmatype_t min_type) |
160 | { | |
161 | struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata; | |
162 | struct device *dev = scsi_get_device(host); | |
163 | int dmach = info->info.scsi.dma; | |
164 | ||
165 | if (dmach != NO_DMA && | |
166 | (min_type == fasdma_real_all || SCp->this_residual >= 512)) { | |
167 | int bufs, map_dir, dma_dir; | |
168 | ||
169 | bufs = copy_SCp_to_sg(&info->sg[0], SCp, NR_SG); | |
170 | ||
171 | if (direction == DMA_OUT) | |
172 | map_dir = DMA_TO_DEVICE, | |
173 | dma_dir = DMA_MODE_WRITE; | |
174 | else | |
175 | map_dir = DMA_FROM_DEVICE, | |
176 | dma_dir = DMA_MODE_READ; | |
177 | ||
23d046f4 | 178 | dma_map_sg(dev, info->sg, bufs, map_dir); |
1da177e4 LT |
179 | |
180 | disable_dma(dmach); | |
23d046f4 | 181 | set_dma_sg(dmach, info->sg, bufs); |
1da177e4 LT |
182 | set_dma_mode(dmach, dma_dir); |
183 | enable_dma(dmach); | |
184 | return fasdma_real_all; | |
185 | } | |
186 | /* | |
187 | * We don't do DMA, we only do slow PIO | |
188 | * | |
189 | * Some day, we will do Pseudo DMA | |
190 | */ | |
191 | return fasdma_pseudo; | |
192 | } | |
193 | ||
194 | static void eesoxscsi_buffer_in(void *buf, int length, void __iomem *base) | |
195 | { | |
196 | const void __iomem *reg_fas = base + EESOX_FAS216_OFFSET; | |
197 | const void __iomem *reg_dmastat = base + EESOX_DMASTAT; | |
198 | const void __iomem *reg_dmadata = base + EESOX_DMADATA; | |
c5a69d57 | 199 | register const unsigned long mask = 0xffff; |
1da177e4 LT |
200 | |
201 | do { | |
202 | unsigned int status; | |
203 | ||
204 | /* | |
205 | * Interrupt request? | |
206 | */ | |
207 | status = readb(reg_fas + (REG_STAT << EESOX_FAS216_SHIFT)); | |
208 | if (status & STAT_INT) | |
209 | break; | |
210 | ||
211 | /* | |
212 | * DMA request active? | |
213 | */ | |
214 | status = readb(reg_dmastat); | |
215 | if (!(status & EESOX_STAT_DMA)) | |
216 | continue; | |
217 | ||
218 | /* | |
219 | * Get number of bytes in FIFO | |
220 | */ | |
221 | status = readb(reg_fas + (REG_CFIS << EESOX_FAS216_SHIFT)) & CFIS_CF; | |
222 | if (status > 16) | |
223 | status = 16; | |
224 | if (status > length) | |
225 | status = length; | |
226 | ||
227 | /* | |
228 | * Align buffer. | |
229 | */ | |
230 | if (((u32)buf) & 2 && status >= 2) { | |
231 | *(u16 *)buf = readl(reg_dmadata); | |
232 | buf += 2; | |
233 | status -= 2; | |
234 | length -= 2; | |
235 | } | |
236 | ||
237 | if (status >= 8) { | |
238 | unsigned long l1, l2; | |
239 | ||
240 | l1 = readl(reg_dmadata) & mask; | |
241 | l1 |= readl(reg_dmadata) << 16; | |
242 | l2 = readl(reg_dmadata) & mask; | |
243 | l2 |= readl(reg_dmadata) << 16; | |
244 | *(u32 *)buf = l1; | |
245 | buf += 4; | |
246 | *(u32 *)buf = l2; | |
247 | buf += 4; | |
248 | length -= 8; | |
249 | continue; | |
250 | } | |
251 | ||
252 | if (status >= 4) { | |
253 | unsigned long l1; | |
254 | ||
255 | l1 = readl(reg_dmadata) & mask; | |
256 | l1 |= readl(reg_dmadata) << 16; | |
257 | ||
258 | *(u32 *)buf = l1; | |
259 | buf += 4; | |
260 | length -= 4; | |
261 | continue; | |
262 | } | |
263 | ||
264 | if (status >= 2) { | |
265 | *(u16 *)buf = readl(reg_dmadata); | |
266 | buf += 2; | |
267 | length -= 2; | |
268 | } | |
269 | } while (length); | |
270 | } | |
271 | ||
272 | static void eesoxscsi_buffer_out(void *buf, int length, void __iomem *base) | |
273 | { | |
274 | const void __iomem *reg_fas = base + EESOX_FAS216_OFFSET; | |
275 | const void __iomem *reg_dmastat = base + EESOX_DMASTAT; | |
f39f7b7d | 276 | void __iomem *reg_dmadata = base + EESOX_DMADATA; |
1da177e4 LT |
277 | |
278 | do { | |
279 | unsigned int status; | |
280 | ||
281 | /* | |
282 | * Interrupt request? | |
283 | */ | |
284 | status = readb(reg_fas + (REG_STAT << EESOX_FAS216_SHIFT)); | |
285 | if (status & STAT_INT) | |
286 | break; | |
287 | ||
288 | /* | |
289 | * DMA request active? | |
290 | */ | |
291 | status = readb(reg_dmastat); | |
292 | if (!(status & EESOX_STAT_DMA)) | |
293 | continue; | |
294 | ||
295 | /* | |
296 | * Get number of bytes in FIFO | |
297 | */ | |
298 | status = readb(reg_fas + (REG_CFIS << EESOX_FAS216_SHIFT)) & CFIS_CF; | |
299 | if (status > 16) | |
300 | status = 16; | |
301 | status = 16 - status; | |
302 | if (status > length) | |
303 | status = length; | |
304 | status &= ~1; | |
305 | ||
306 | /* | |
307 | * Align buffer. | |
308 | */ | |
309 | if (((u32)buf) & 2 && status >= 2) { | |
310 | writel(*(u16 *)buf << 16, reg_dmadata); | |
311 | buf += 2; | |
312 | status -= 2; | |
313 | length -= 2; | |
314 | } | |
315 | ||
316 | if (status >= 8) { | |
317 | unsigned long l1, l2; | |
318 | ||
319 | l1 = *(u32 *)buf; | |
320 | buf += 4; | |
321 | l2 = *(u32 *)buf; | |
322 | buf += 4; | |
323 | ||
324 | writel(l1 << 16, reg_dmadata); | |
325 | writel(l1, reg_dmadata); | |
326 | writel(l2 << 16, reg_dmadata); | |
327 | writel(l2, reg_dmadata); | |
328 | length -= 8; | |
329 | continue; | |
330 | } | |
331 | ||
332 | if (status >= 4) { | |
333 | unsigned long l1; | |
334 | ||
335 | l1 = *(u32 *)buf; | |
336 | buf += 4; | |
337 | ||
338 | writel(l1 << 16, reg_dmadata); | |
339 | writel(l1, reg_dmadata); | |
340 | length -= 4; | |
341 | continue; | |
342 | } | |
343 | ||
344 | if (status >= 2) { | |
345 | writel(*(u16 *)buf << 16, reg_dmadata); | |
346 | buf += 2; | |
347 | length -= 2; | |
348 | } | |
349 | } while (length); | |
350 | } | |
351 | ||
352 | static void | |
0a04137e | 353 | eesoxscsi_dma_pseudo(struct Scsi_Host *host, struct scsi_pointer *SCp, |
1da177e4 LT |
354 | fasdmadir_t dir, int transfer_size) |
355 | { | |
356 | struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata; | |
357 | if (dir == DMA_IN) { | |
358 | eesoxscsi_buffer_in(SCp->ptr, SCp->this_residual, info->base); | |
359 | } else { | |
360 | eesoxscsi_buffer_out(SCp->ptr, SCp->this_residual, info->base); | |
361 | } | |
362 | } | |
363 | ||
364 | /* Prototype: int eesoxscsi_dma_stop(host, SCpnt) | |
365 | * Purpose : stops DMA/PIO | |
366 | * Params : host - host | |
367 | * SCpnt - command | |
368 | */ | |
369 | static void | |
0a04137e | 370 | eesoxscsi_dma_stop(struct Scsi_Host *host, struct scsi_pointer *SCp) |
1da177e4 LT |
371 | { |
372 | struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata; | |
373 | if (info->info.scsi.dma != NO_DMA) | |
374 | disable_dma(info->info.scsi.dma); | |
375 | } | |
376 | ||
377 | /* Prototype: const char *eesoxscsi_info(struct Scsi_Host * host) | |
378 | * Purpose : returns a descriptive string about this interface, | |
379 | * Params : host - driver host structure to return info for. | |
380 | * Returns : pointer to a static buffer containing null terminated string. | |
381 | */ | |
382 | const char *eesoxscsi_info(struct Scsi_Host *host) | |
383 | { | |
384 | struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata; | |
385 | static char string[150]; | |
386 | ||
387 | sprintf(string, "%s (%s) in slot %d v%s terminators o%s", | |
388 | host->hostt->name, info->info.scsi.type, info->ec->slot_no, | |
389 | VERSION, info->control & EESOX_TERM_ENABLE ? "n" : "ff"); | |
390 | ||
391 | return string; | |
392 | } | |
393 | ||
394 | /* Prototype: int eesoxscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length) | |
395 | * Purpose : Set a driver specific function | |
396 | * Params : host - host to setup | |
397 | * : buffer - buffer containing string describing operation | |
398 | * : length - length of string | |
399 | * Returns : -EINVAL, or 0 | |
400 | */ | |
401 | static int | |
402 | eesoxscsi_set_proc_info(struct Scsi_Host *host, char *buffer, int length) | |
403 | { | |
404 | int ret = length; | |
405 | ||
406 | if (length >= 9 && strncmp(buffer, "EESOXSCSI", 9) == 0) { | |
407 | buffer += 9; | |
408 | length -= 9; | |
409 | ||
410 | if (length >= 5 && strncmp(buffer, "term=", 5) == 0) { | |
411 | if (buffer[5] == '1') | |
412 | eesoxscsi_terminator_ctl(host, 1); | |
413 | else if (buffer[5] == '0') | |
414 | eesoxscsi_terminator_ctl(host, 0); | |
415 | else | |
416 | ret = -EINVAL; | |
417 | } else | |
418 | ret = -EINVAL; | |
419 | } else | |
420 | ret = -EINVAL; | |
421 | ||
422 | return ret; | |
423 | } | |
424 | ||
9d4e5c54 | 425 | static int eesoxscsi_show_info(struct seq_file *m, struct Scsi_Host *host) |
1da177e4 LT |
426 | { |
427 | struct eesoxscsi_info *info; | |
1da177e4 LT |
428 | |
429 | info = (struct eesoxscsi_info *)host->hostdata; | |
430 | ||
9d4e5c54 AV |
431 | seq_printf(m, "EESOX SCSI driver v%s\n", VERSION); |
432 | fas216_print_host(&info->info, m); | |
433 | seq_printf(m, "Term : o%s\n", | |
1da177e4 LT |
434 | info->control & EESOX_TERM_ENABLE ? "n" : "ff"); |
435 | ||
9d4e5c54 AV |
436 | fas216_print_stats(&info->info, m); |
437 | fas216_print_devices(&info->info, m); | |
438 | return 0; | |
1da177e4 LT |
439 | } |
440 | ||
10523b3b | 441 | static ssize_t eesoxscsi_show_term(struct device *dev, struct device_attribute *attr, char *buf) |
1da177e4 LT |
442 | { |
443 | struct expansion_card *ec = ECARD_DEV(dev); | |
444 | struct Scsi_Host *host = ecard_get_drvdata(ec); | |
445 | struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata; | |
446 | ||
447 | return sprintf(buf, "%d\n", info->control & EESOX_TERM_ENABLE ? 1 : 0); | |
448 | } | |
449 | ||
10523b3b | 450 | static ssize_t eesoxscsi_store_term(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) |
1da177e4 LT |
451 | { |
452 | struct expansion_card *ec = ECARD_DEV(dev); | |
453 | struct Scsi_Host *host = ecard_get_drvdata(ec); | |
454 | struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata; | |
455 | unsigned long flags; | |
456 | ||
457 | if (len > 1) { | |
458 | spin_lock_irqsave(host->host_lock, flags); | |
459 | if (buf[0] != '0') { | |
460 | info->control |= EESOX_TERM_ENABLE; | |
461 | } else { | |
462 | info->control &= ~EESOX_TERM_ENABLE; | |
463 | } | |
464 | writeb(info->control, info->ctl_port); | |
465 | spin_unlock_irqrestore(host->host_lock, flags); | |
466 | } | |
467 | ||
468 | return len; | |
469 | } | |
470 | ||
471 | static DEVICE_ATTR(bus_term, S_IRUGO | S_IWUSR, | |
472 | eesoxscsi_show_term, eesoxscsi_store_term); | |
473 | ||
d0be4a7d | 474 | static struct scsi_host_template eesox_template = { |
1da177e4 | 475 | .module = THIS_MODULE, |
9d4e5c54 AV |
476 | .show_info = eesoxscsi_show_info, |
477 | .write_info = eesoxscsi_set_proc_info, | |
1da177e4 LT |
478 | .name = "EESOX SCSI", |
479 | .info = eesoxscsi_info, | |
480 | .queuecommand = fas216_queue_command, | |
481 | .eh_host_reset_handler = fas216_eh_host_reset, | |
482 | .eh_bus_reset_handler = fas216_eh_bus_reset, | |
483 | .eh_device_reset_handler = fas216_eh_device_reset, | |
484 | .eh_abort_handler = fas216_eh_abort, | |
485 | .can_queue = 1, | |
486 | .this_id = 7, | |
65e8617f | 487 | .sg_tablesize = SG_MAX_SEGMENTS, |
5369bea7 | 488 | .dma_boundary = IOMD_DMA_BOUNDARY, |
1da177e4 LT |
489 | .use_clustering = DISABLE_CLUSTERING, |
490 | .proc_name = "eesox", | |
491 | }; | |
492 | ||
6f039790 | 493 | static int eesoxscsi_probe(struct expansion_card *ec, const struct ecard_id *id) |
1da177e4 LT |
494 | { |
495 | struct Scsi_Host *host; | |
496 | struct eesoxscsi_info *info; | |
1da177e4 LT |
497 | void __iomem *base; |
498 | int ret; | |
499 | ||
500 | ret = ecard_request_resources(ec); | |
501 | if (ret) | |
502 | goto out; | |
503 | ||
10bdaaa0 | 504 | base = ecardm_iomap(ec, ECARD_RES_IOCFAST, 0, 0); |
1da177e4 LT |
505 | if (!base) { |
506 | ret = -ENOMEM; | |
507 | goto out_region; | |
508 | } | |
509 | ||
510 | host = scsi_host_alloc(&eesox_template, | |
511 | sizeof(struct eesoxscsi_info)); | |
512 | if (!host) { | |
513 | ret = -ENOMEM; | |
10bdaaa0 | 514 | goto out_region; |
1da177e4 LT |
515 | } |
516 | ||
517 | ecard_set_drvdata(ec, host); | |
518 | ||
519 | info = (struct eesoxscsi_info *)host->hostdata; | |
520 | info->ec = ec; | |
521 | info->base = base; | |
522 | info->ctl_port = base + EESOX_CONTROL; | |
523 | info->control = term[ec->slot_no] ? EESOX_TERM_ENABLE : 0; | |
524 | writeb(info->control, info->ctl_port); | |
525 | ||
526 | info->info.scsi.io_base = base + EESOX_FAS216_OFFSET; | |
527 | info->info.scsi.io_shift = EESOX_FAS216_SHIFT; | |
528 | info->info.scsi.irq = ec->irq; | |
529 | info->info.scsi.dma = ec->dma; | |
530 | info->info.ifcfg.clockrate = 40; /* MHz */ | |
531 | info->info.ifcfg.select_timeout = 255; | |
532 | info->info.ifcfg.asyncperiod = 200; /* ns */ | |
533 | info->info.ifcfg.sync_max_depth = 7; | |
534 | info->info.ifcfg.cntl3 = CNTL3_FASTSCSI | CNTL3_FASTCLK; | |
535 | info->info.ifcfg.disconnect_ok = 1; | |
536 | info->info.ifcfg.wide_max_size = 0; | |
537 | info->info.ifcfg.capabilities = FASCAP_PSEUDODMA; | |
538 | info->info.dma.setup = eesoxscsi_dma_setup; | |
539 | info->info.dma.pseudo = eesoxscsi_dma_pseudo; | |
540 | info->info.dma.stop = eesoxscsi_dma_stop; | |
541 | ||
542 | ec->irqaddr = base + EESOX_DMASTAT; | |
543 | ec->irqmask = EESOX_STAT_INTR; | |
c7b87f3d RK |
544 | |
545 | ecard_setirq(ec, &eesoxscsi_ops, info); | |
1da177e4 LT |
546 | |
547 | device_create_file(&ec->dev, &dev_attr_bus_term); | |
548 | ||
549 | ret = fas216_init(host); | |
550 | if (ret) | |
551 | goto out_free; | |
552 | ||
553 | ret = request_irq(ec->irq, eesoxscsi_intr, 0, "eesoxscsi", info); | |
554 | if (ret) { | |
555 | printk("scsi%d: IRQ%d not free: %d\n", | |
556 | host->host_no, ec->irq, ret); | |
557 | goto out_remove; | |
558 | } | |
559 | ||
560 | if (info->info.scsi.dma != NO_DMA) { | |
561 | if (request_dma(info->info.scsi.dma, "eesox")) { | |
562 | printk("scsi%d: DMA%d not free, DMA disabled\n", | |
563 | host->host_no, info->info.scsi.dma); | |
564 | info->info.scsi.dma = NO_DMA; | |
565 | } else { | |
566 | set_dma_speed(info->info.scsi.dma, 180); | |
567 | info->info.ifcfg.capabilities |= FASCAP_DMA; | |
568 | info->info.ifcfg.cntl3 |= CNTL3_BS8; | |
569 | } | |
570 | } | |
571 | ||
572 | ret = fas216_add(host, &ec->dev); | |
573 | if (ret == 0) | |
574 | goto out; | |
575 | ||
576 | if (info->info.scsi.dma != NO_DMA) | |
577 | free_dma(info->info.scsi.dma); | |
578 | free_irq(ec->irq, host); | |
579 | ||
580 | out_remove: | |
581 | fas216_remove(host); | |
582 | ||
583 | out_free: | |
584 | device_remove_file(&ec->dev, &dev_attr_bus_term); | |
585 | scsi_host_put(host); | |
586 | ||
1da177e4 LT |
587 | out_region: |
588 | ecard_release_resources(ec); | |
589 | ||
590 | out: | |
591 | return ret; | |
592 | } | |
593 | ||
6f039790 | 594 | static void eesoxscsi_remove(struct expansion_card *ec) |
1da177e4 LT |
595 | { |
596 | struct Scsi_Host *host = ecard_get_drvdata(ec); | |
597 | struct eesoxscsi_info *info = (struct eesoxscsi_info *)host->hostdata; | |
598 | ||
599 | ecard_set_drvdata(ec, NULL); | |
600 | fas216_remove(host); | |
601 | ||
602 | if (info->info.scsi.dma != NO_DMA) | |
603 | free_dma(info->info.scsi.dma); | |
604 | free_irq(ec->irq, info); | |
605 | ||
606 | device_remove_file(&ec->dev, &dev_attr_bus_term); | |
607 | ||
1da177e4 LT |
608 | fas216_release(host); |
609 | scsi_host_put(host); | |
610 | ecard_release_resources(ec); | |
611 | } | |
612 | ||
613 | static const struct ecard_id eesoxscsi_cids[] = { | |
614 | { MANU_EESOX, PROD_EESOX_SCSI2 }, | |
615 | { 0xffff, 0xffff }, | |
616 | }; | |
617 | ||
618 | static struct ecard_driver eesoxscsi_driver = { | |
619 | .probe = eesoxscsi_probe, | |
6f039790 | 620 | .remove = eesoxscsi_remove, |
1da177e4 LT |
621 | .id_table = eesoxscsi_cids, |
622 | .drv = { | |
623 | .name = "eesoxscsi", | |
624 | }, | |
625 | }; | |
626 | ||
627 | static int __init eesox_init(void) | |
628 | { | |
629 | return ecard_register_driver(&eesoxscsi_driver); | |
630 | } | |
631 | ||
632 | static void __exit eesox_exit(void) | |
633 | { | |
634 | ecard_remove_driver(&eesoxscsi_driver); | |
635 | } | |
636 | ||
637 | module_init(eesox_init); | |
638 | module_exit(eesox_exit); | |
639 | ||
640 | MODULE_AUTHOR("Russell King"); | |
641 | MODULE_DESCRIPTION("EESOX 'Fast' SCSI driver for Acorn machines"); | |
8d3b33f6 | 642 | module_param_array(term, int, NULL, 0); |
1da177e4 LT |
643 | MODULE_PARM_DESC(term, "SCSI bus termination"); |
644 | MODULE_LICENSE("GPL"); |