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