]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Generic Macintosh NCR5380 driver | |
3 | * | |
4 | * Copyright 1998, Michael Schmitz <mschmitz@lbl.gov> | |
5 | * | |
71c204ad FT |
6 | * Copyright 2019 Finn Thain |
7 | * | |
1da177e4 LT |
8 | * derived in part from: |
9 | */ | |
10 | /* | |
11 | * Generic Generic NCR5380 driver | |
12 | * | |
13 | * Copyright 1995, Russell King | |
1da177e4 LT |
14 | */ |
15 | ||
71c204ad | 16 | #include <linux/delay.h> |
1da177e4 | 17 | #include <linux/types.h> |
1da177e4 | 18 | #include <linux/module.h> |
1da177e4 LT |
19 | #include <linux/ioport.h> |
20 | #include <linux/init.h> | |
21 | #include <linux/blkdev.h> | |
22 | #include <linux/interrupt.h> | |
cbad48de | 23 | #include <linux/platform_device.h> |
1da177e4 | 24 | |
cbad48de | 25 | #include <asm/hwtest.h> |
1da177e4 | 26 | #include <asm/io.h> |
1da177e4 | 27 | #include <asm/macints.h> |
cbad48de | 28 | #include <asm/setup.h> |
1da177e4 | 29 | |
1da177e4 | 30 | #include <scsi/scsi_host.h> |
92de3831 FT |
31 | |
32 | /* Definitions for the core NCR5380 driver. */ | |
e744fdea | 33 | |
820682b1 | 34 | #define NCR5380_implementation_fields int pdma_residual |
92de3831 | 35 | |
61e1ce58 FT |
36 | #define NCR5380_read(reg) in_8(hostdata->io + ((reg) << 4)) |
37 | #define NCR5380_write(reg, value) out_8(hostdata->io + ((reg) << 4), value) | |
92de3831 | 38 | |
4a98f896 | 39 | #define NCR5380_dma_xfer_len macscsi_dma_xfer_len |
6c4b88ca FT |
40 | #define NCR5380_dma_recv_setup macscsi_pread |
41 | #define NCR5380_dma_send_setup macscsi_pwrite | |
4a98f896 | 42 | #define NCR5380_dma_residual macscsi_dma_residual |
92de3831 FT |
43 | |
44 | #define NCR5380_intr macscsi_intr | |
45 | #define NCR5380_queue_command macscsi_queue_command | |
46 | #define NCR5380_abort macscsi_abort | |
12e5fc66 | 47 | #define NCR5380_host_reset macscsi_host_reset |
92de3831 | 48 | #define NCR5380_info macscsi_info |
92de3831 | 49 | |
1da177e4 LT |
50 | #include "NCR5380.h" |
51 | ||
1da177e4 | 52 | static int setup_can_queue = -1; |
6e9ae6d5 | 53 | module_param(setup_can_queue, int, 0); |
1da177e4 | 54 | static int setup_cmd_per_lun = -1; |
6e9ae6d5 | 55 | module_param(setup_cmd_per_lun, int, 0); |
1da177e4 | 56 | static int setup_sg_tablesize = -1; |
6e9ae6d5 | 57 | module_param(setup_sg_tablesize, int, 0); |
4553b6a5 | 58 | static int setup_use_pdma = 512; |
6e9ae6d5 | 59 | module_param(setup_use_pdma, int, 0); |
1da177e4 | 60 | static int setup_hostid = -1; |
6e9ae6d5 | 61 | module_param(setup_hostid, int, 0); |
9c3f0e2b FT |
62 | static int setup_toshiba_delay = -1; |
63 | module_param(setup_toshiba_delay, int, 0); | |
1da177e4 | 64 | |
6e9ae6d5 FT |
65 | #ifndef MODULE |
66 | static int __init mac_scsi_setup(char *str) | |
67 | { | |
9c3f0e2b | 68 | int ints[8]; |
6e9ae6d5 FT |
69 | |
70 | (void)get_options(str, ARRAY_SIZE(ints), ints); | |
71 | ||
9c3f0e2b FT |
72 | if (ints[0] < 1) { |
73 | pr_err("Usage: mac5380=<can_queue>[,<cmd_per_lun>[,<sg_tablesize>[,<hostid>[,<use_tags>[,<use_pdma>[,<toshiba_delay>]]]]]]\n"); | |
6e9ae6d5 | 74 | return 0; |
1da177e4 | 75 | } |
6e9ae6d5 FT |
76 | if (ints[0] >= 1) |
77 | setup_can_queue = ints[1]; | |
78 | if (ints[0] >= 2) | |
79 | setup_cmd_per_lun = ints[2]; | |
80 | if (ints[0] >= 3) | |
81 | setup_sg_tablesize = ints[3]; | |
82 | if (ints[0] >= 4) | |
83 | setup_hostid = ints[4]; | |
c4ec6f92 | 84 | /* ints[5] (use_tagged_queuing) is ignored */ |
6e9ae6d5 | 85 | if (ints[0] >= 6) |
1da177e4 | 86 | setup_use_pdma = ints[6]; |
9c3f0e2b FT |
87 | if (ints[0] >= 7) |
88 | setup_toshiba_delay = ints[7]; | |
1da177e4 LT |
89 | return 1; |
90 | } | |
91 | ||
92 | __setup("mac5380=", mac_scsi_setup); | |
6e9ae6d5 | 93 | #endif /* !MODULE */ |
1da177e4 | 94 | |
71c204ad FT |
95 | /* |
96 | * According to "Inside Macintosh: Devices", Mac OS requires disk drivers to | |
97 | * specify the number of bytes between the delays expected from a SCSI target. | |
98 | * This allows the operating system to "prevent bus errors when a target fails | |
99 | * to deliver the next byte within the processor bus error timeout period." | |
100 | * Linux SCSI drivers lack knowledge of the timing behaviour of SCSI targets | |
101 | * so bus errors are unavoidable. | |
102 | * | |
103 | * If a MOVE.B instruction faults, we assume that zero bytes were transferred | |
104 | * and simply retry. That assumption probably depends on target behaviour but | |
105 | * seems to hold up okay. The NOP provides synchronization: without it the | |
106 | * fault can sometimes occur after the program counter has moved past the | |
107 | * offending instruction. Post-increment addressing can't be used. | |
108 | */ | |
109 | ||
110 | #define MOVE_BYTE(operands) \ | |
111 | asm volatile ( \ | |
112 | "1: moveb " operands " \n" \ | |
113 | "11: nop \n" \ | |
114 | " addq #1,%0 \n" \ | |
115 | " subq #1,%1 \n" \ | |
116 | "40: \n" \ | |
117 | " \n" \ | |
118 | ".section .fixup,\"ax\" \n" \ | |
119 | ".even \n" \ | |
120 | "90: movel #1, %2 \n" \ | |
121 | " jra 40b \n" \ | |
122 | ".previous \n" \ | |
123 | " \n" \ | |
124 | ".section __ex_table,\"a\" \n" \ | |
125 | ".align 4 \n" \ | |
126 | ".long 1b,90b \n" \ | |
127 | ".long 11b,90b \n" \ | |
128 | ".previous \n" \ | |
129 | : "+a" (addr), "+r" (n), "+r" (result) : "a" (io)) | |
130 | ||
131 | /* | |
132 | * If a MOVE.W (or MOVE.L) instruction faults, it cannot be retried because | |
133 | * the residual byte count would be uncertain. In that situation the MOVE_WORD | |
134 | * macro clears n in the fixup section to abort the transfer. | |
135 | */ | |
136 | ||
137 | #define MOVE_WORD(operands) \ | |
138 | asm volatile ( \ | |
139 | "1: movew " operands " \n" \ | |
140 | "11: nop \n" \ | |
141 | " subq #2,%1 \n" \ | |
142 | "40: \n" \ | |
143 | " \n" \ | |
144 | ".section .fixup,\"ax\" \n" \ | |
145 | ".even \n" \ | |
146 | "90: movel #0, %1 \n" \ | |
147 | " movel #2, %2 \n" \ | |
148 | " jra 40b \n" \ | |
149 | ".previous \n" \ | |
150 | " \n" \ | |
151 | ".section __ex_table,\"a\" \n" \ | |
152 | ".align 4 \n" \ | |
153 | ".long 1b,90b \n" \ | |
154 | ".long 11b,90b \n" \ | |
155 | ".previous \n" \ | |
156 | : "+a" (addr), "+r" (n), "+r" (result) : "a" (io)) | |
157 | ||
158 | #define MOVE_16_WORDS(operands) \ | |
159 | asm volatile ( \ | |
160 | "1: movew " operands " \n" \ | |
161 | "2: movew " operands " \n" \ | |
162 | "3: movew " operands " \n" \ | |
163 | "4: movew " operands " \n" \ | |
164 | "5: movew " operands " \n" \ | |
165 | "6: movew " operands " \n" \ | |
166 | "7: movew " operands " \n" \ | |
167 | "8: movew " operands " \n" \ | |
168 | "9: movew " operands " \n" \ | |
169 | "10: movew " operands " \n" \ | |
170 | "11: movew " operands " \n" \ | |
171 | "12: movew " operands " \n" \ | |
172 | "13: movew " operands " \n" \ | |
173 | "14: movew " operands " \n" \ | |
174 | "15: movew " operands " \n" \ | |
175 | "16: movew " operands " \n" \ | |
176 | "17: nop \n" \ | |
177 | " subl #32,%1 \n" \ | |
178 | "40: \n" \ | |
179 | " \n" \ | |
180 | ".section .fixup,\"ax\" \n" \ | |
181 | ".even \n" \ | |
182 | "90: movel #0, %1 \n" \ | |
183 | " movel #2, %2 \n" \ | |
184 | " jra 40b \n" \ | |
185 | ".previous \n" \ | |
186 | " \n" \ | |
187 | ".section __ex_table,\"a\" \n" \ | |
188 | ".align 4 \n" \ | |
189 | ".long 1b,90b \n" \ | |
190 | ".long 2b,90b \n" \ | |
191 | ".long 3b,90b \n" \ | |
192 | ".long 4b,90b \n" \ | |
193 | ".long 5b,90b \n" \ | |
194 | ".long 6b,90b \n" \ | |
195 | ".long 7b,90b \n" \ | |
196 | ".long 8b,90b \n" \ | |
197 | ".long 9b,90b \n" \ | |
198 | ".long 10b,90b \n" \ | |
199 | ".long 11b,90b \n" \ | |
200 | ".long 12b,90b \n" \ | |
201 | ".long 13b,90b \n" \ | |
202 | ".long 14b,90b \n" \ | |
203 | ".long 15b,90b \n" \ | |
204 | ".long 16b,90b \n" \ | |
205 | ".long 17b,90b \n" \ | |
206 | ".previous \n" \ | |
207 | : "+a" (addr), "+r" (n), "+r" (result) : "a" (io)) | |
208 | ||
209 | #define MAC_PDMA_DELAY 32 | |
210 | ||
211 | static inline int mac_pdma_recv(void __iomem *io, unsigned char *start, int n) | |
212 | { | |
213 | unsigned char *addr = start; | |
214 | int result = 0; | |
215 | ||
216 | if (n >= 1) { | |
217 | MOVE_BYTE("%3@,%0@"); | |
218 | if (result) | |
219 | goto out; | |
220 | } | |
221 | if (n >= 1 && ((unsigned long)addr & 1)) { | |
222 | MOVE_BYTE("%3@,%0@"); | |
223 | if (result) | |
224 | goto out; | |
225 | } | |
226 | while (n >= 32) | |
227 | MOVE_16_WORDS("%3@,%0@+"); | |
228 | while (n >= 2) | |
229 | MOVE_WORD("%3@,%0@+"); | |
230 | if (result) | |
231 | return start - addr; /* Negated to indicate uncertain length */ | |
232 | if (n == 1) | |
233 | MOVE_BYTE("%3@,%0@"); | |
234 | out: | |
235 | return addr - start; | |
236 | } | |
237 | ||
238 | static inline int mac_pdma_send(unsigned char *start, void __iomem *io, int n) | |
239 | { | |
240 | unsigned char *addr = start; | |
241 | int result = 0; | |
242 | ||
243 | if (n >= 1) { | |
244 | MOVE_BYTE("%0@,%3@"); | |
245 | if (result) | |
246 | goto out; | |
247 | } | |
248 | if (n >= 1 && ((unsigned long)addr & 1)) { | |
249 | MOVE_BYTE("%0@,%3@"); | |
250 | if (result) | |
251 | goto out; | |
252 | } | |
253 | while (n >= 32) | |
254 | MOVE_16_WORDS("%0@+,%3@"); | |
255 | while (n >= 2) | |
256 | MOVE_WORD("%0@+,%3@"); | |
257 | if (result) | |
258 | return start - addr; /* Negated to indicate uncertain length */ | |
259 | if (n == 1) | |
260 | MOVE_BYTE("%0@,%3@"); | |
261 | out: | |
262 | return addr - start; | |
263 | } | |
1da177e4 | 264 | |
4a98f896 FT |
265 | static inline int macscsi_pread(struct NCR5380_hostdata *hostdata, |
266 | unsigned char *dst, int len) | |
1da177e4 | 267 | { |
4ab2a787 | 268 | u8 __iomem *s = hostdata->pdma_io + (INPUT_DATA_REG << 4); |
3a0f64bf | 269 | unsigned char *d = dst; |
71c204ad FT |
270 | |
271 | hostdata->pdma_residual = len; | |
3a0f64bf | 272 | |
d5d37a0a | 273 | while (!NCR5380_poll_politely(hostdata, BUS_AND_STATUS_REG, |
3a0f64bf FT |
274 | BASR_DRQ | BASR_PHASE_MATCH, |
275 | BASR_DRQ | BASR_PHASE_MATCH, HZ / 64)) { | |
71c204ad | 276 | int bytes; |
3a0f64bf | 277 | |
71c204ad | 278 | bytes = mac_pdma_recv(s, d, min(hostdata->pdma_residual, 512)); |
3a0f64bf | 279 | |
71c204ad FT |
280 | if (bytes > 0) { |
281 | d += bytes; | |
282 | hostdata->pdma_residual -= bytes; | |
283 | } | |
284 | ||
285 | if (hostdata->pdma_residual == 0) | |
3a0f64bf FT |
286 | return 0; |
287 | ||
d5d37a0a | 288 | if (NCR5380_poll_politely2(hostdata, STATUS_REG, SR_REQ, SR_REQ, |
71c204ad FT |
289 | BUS_AND_STATUS_REG, BASR_ACK, |
290 | BASR_ACK, HZ / 64) < 0) | |
291 | scmd_printk(KERN_DEBUG, hostdata->connected, | |
3a0f64bf FT |
292 | "%s: !REQ and !ACK\n", __func__); |
293 | if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_PHASE_MATCH)) | |
294 | return 0; | |
295 | ||
71c204ad FT |
296 | if (bytes == 0) |
297 | udelay(MAC_PDMA_DELAY); | |
298 | ||
299 | if (bytes >= 0) | |
300 | continue; | |
301 | ||
4a98f896 | 302 | dsprintk(NDEBUG_PSEUDO_DMA, hostdata->host, |
71c204ad | 303 | "%s: bus error (%d/%d)\n", __func__, d - dst, len); |
4a98f896 | 304 | NCR5380_dprint(NDEBUG_PSEUDO_DMA, hostdata->host); |
71c204ad | 305 | return -1; |
ffdede67 FT |
306 | } |
307 | ||
3a0f64bf FT |
308 | scmd_printk(KERN_ERR, hostdata->connected, |
309 | "%s: phase mismatch or !DRQ\n", __func__); | |
4a98f896 | 310 | NCR5380_dprint(NDEBUG_PSEUDO_DMA, hostdata->host); |
3a0f64bf | 311 | return -1; |
1da177e4 LT |
312 | } |
313 | ||
4a98f896 FT |
314 | static inline int macscsi_pwrite(struct NCR5380_hostdata *hostdata, |
315 | unsigned char *src, int len) | |
1da177e4 | 316 | { |
3a0f64bf | 317 | unsigned char *s = src; |
4ab2a787 | 318 | u8 __iomem *d = hostdata->pdma_io + (OUTPUT_DATA_REG << 4); |
71c204ad FT |
319 | |
320 | hostdata->pdma_residual = len; | |
3a0f64bf | 321 | |
d5d37a0a | 322 | while (!NCR5380_poll_politely(hostdata, BUS_AND_STATUS_REG, |
3a0f64bf FT |
323 | BASR_DRQ | BASR_PHASE_MATCH, |
324 | BASR_DRQ | BASR_PHASE_MATCH, HZ / 64)) { | |
71c204ad | 325 | int bytes; |
3a0f64bf | 326 | |
71c204ad | 327 | bytes = mac_pdma_send(s, d, min(hostdata->pdma_residual, 512)); |
3a0f64bf | 328 | |
71c204ad FT |
329 | if (bytes > 0) { |
330 | s += bytes; | |
331 | hostdata->pdma_residual -= bytes; | |
332 | } | |
3a0f64bf | 333 | |
71c204ad | 334 | if (hostdata->pdma_residual == 0) { |
d5d37a0a | 335 | if (NCR5380_poll_politely(hostdata, TARGET_COMMAND_REG, |
3a0f64bf FT |
336 | TCR_LAST_BYTE_SENT, |
337 | TCR_LAST_BYTE_SENT, HZ / 64) < 0) | |
338 | scmd_printk(KERN_ERR, hostdata->connected, | |
339 | "%s: Last Byte Sent timeout\n", __func__); | |
340 | return 0; | |
341 | } | |
342 | ||
71c204ad FT |
343 | if (NCR5380_poll_politely2(hostdata, STATUS_REG, SR_REQ, SR_REQ, |
344 | BUS_AND_STATUS_REG, BASR_ACK, | |
345 | BASR_ACK, HZ / 64) < 0) | |
346 | scmd_printk(KERN_DEBUG, hostdata->connected, | |
347 | "%s: !REQ and !ACK\n", __func__); | |
348 | if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_PHASE_MATCH)) | |
349 | return 0; | |
350 | ||
351 | if (bytes == 0) | |
352 | udelay(MAC_PDMA_DELAY); | |
353 | ||
354 | if (bytes >= 0) | |
355 | continue; | |
356 | ||
4a98f896 | 357 | dsprintk(NDEBUG_PSEUDO_DMA, hostdata->host, |
71c204ad | 358 | "%s: bus error (%d/%d)\n", __func__, s - src, len); |
4a98f896 | 359 | NCR5380_dprint(NDEBUG_PSEUDO_DMA, hostdata->host); |
71c204ad | 360 | return -1; |
ffdede67 FT |
361 | } |
362 | ||
3a0f64bf FT |
363 | scmd_printk(KERN_ERR, hostdata->connected, |
364 | "%s: phase mismatch or !DRQ\n", __func__); | |
4a98f896 | 365 | NCR5380_dprint(NDEBUG_PSEUDO_DMA, hostdata->host); |
3a0f64bf | 366 | return -1; |
ffdede67 | 367 | } |
1da177e4 | 368 | |
4a98f896 | 369 | static int macscsi_dma_xfer_len(struct NCR5380_hostdata *hostdata, |
7e9ec8d9 FT |
370 | struct scsi_cmnd *cmd) |
371 | { | |
3a0f64bf | 372 | if (hostdata->flags & FLAG_NO_PSEUDO_DMA || |
4553b6a5 | 373 | cmd->SCp.this_residual < setup_use_pdma) |
7e9ec8d9 FT |
374 | return 0; |
375 | ||
3a0f64bf | 376 | return cmd->SCp.this_residual; |
7e9ec8d9 FT |
377 | } |
378 | ||
4a98f896 FT |
379 | static int macscsi_dma_residual(struct NCR5380_hostdata *hostdata) |
380 | { | |
381 | return hostdata->pdma_residual; | |
382 | } | |
383 | ||
1da177e4 LT |
384 | #include "NCR5380.c" |
385 | ||
cbad48de FT |
386 | #define DRV_MODULE_NAME "mac_scsi" |
387 | #define PFX DRV_MODULE_NAME ": " | |
388 | ||
389 | static struct scsi_host_template mac_scsi_template = { | |
aa2e2cb1 FT |
390 | .module = THIS_MODULE, |
391 | .proc_name = DRV_MODULE_NAME, | |
aa2e2cb1 FT |
392 | .name = "Macintosh NCR5380 SCSI", |
393 | .info = macscsi_info, | |
394 | .queuecommand = macscsi_queue_command, | |
395 | .eh_abort_handler = macscsi_abort, | |
12e5fc66 | 396 | .eh_host_reset_handler = macscsi_host_reset, |
aa2e2cb1 FT |
397 | .can_queue = 16, |
398 | .this_id = 7, | |
3a0f64bf | 399 | .sg_tablesize = 1, |
aa2e2cb1 FT |
400 | .cmd_per_lun = 2, |
401 | .use_clustering = DISABLE_CLUSTERING, | |
32b26a10 | 402 | .cmd_size = NCR5380_CMD_SIZE, |
0a4e3612 | 403 | .max_sectors = 128, |
1da177e4 LT |
404 | }; |
405 | ||
cbad48de FT |
406 | static int __init mac_scsi_probe(struct platform_device *pdev) |
407 | { | |
408 | struct Scsi_Host *instance; | |
820682b1 | 409 | struct NCR5380_hostdata *hostdata; |
cbad48de FT |
410 | int error; |
411 | int host_flags = 0; | |
412 | struct resource *irq, *pio_mem, *pdma_mem = NULL; | |
413 | ||
414 | pio_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
415 | if (!pio_mem) | |
416 | return -ENODEV; | |
417 | ||
cbad48de | 418 | pdma_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); |
cbad48de FT |
419 | |
420 | irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | |
421 | ||
422 | if (!hwreg_present((unsigned char *)pio_mem->start + | |
423 | (STATUS_REG << 4))) { | |
424 | pr_info(PFX "no device detected at %pap\n", &pio_mem->start); | |
425 | return -ENODEV; | |
426 | } | |
427 | ||
428 | if (setup_can_queue > 0) | |
429 | mac_scsi_template.can_queue = setup_can_queue; | |
430 | if (setup_cmd_per_lun > 0) | |
431 | mac_scsi_template.cmd_per_lun = setup_cmd_per_lun; | |
432 | if (setup_sg_tablesize >= 0) | |
433 | mac_scsi_template.sg_tablesize = setup_sg_tablesize; | |
434 | if (setup_hostid >= 0) | |
435 | mac_scsi_template.this_id = setup_hostid & 7; | |
cbad48de FT |
436 | |
437 | instance = scsi_host_alloc(&mac_scsi_template, | |
438 | sizeof(struct NCR5380_hostdata)); | |
439 | if (!instance) | |
440 | return -ENOMEM; | |
441 | ||
cbad48de FT |
442 | if (irq) |
443 | instance->irq = irq->start; | |
444 | else | |
445 | instance->irq = NO_IRQ; | |
446 | ||
820682b1 FT |
447 | hostdata = shost_priv(instance); |
448 | hostdata->base = pio_mem->start; | |
4ab2a787 | 449 | hostdata->io = (u8 __iomem *)pio_mem->start; |
cbad48de | 450 | |
820682b1 | 451 | if (pdma_mem && setup_use_pdma) |
4ab2a787 | 452 | hostdata->pdma_io = (u8 __iomem *)pdma_mem->start; |
820682b1 | 453 | else |
cbad48de FT |
454 | host_flags |= FLAG_NO_PSEUDO_DMA; |
455 | ||
9c3f0e2b | 456 | host_flags |= setup_toshiba_delay > 0 ? FLAG_TOSHIBA_DELAY : 0; |
ca513fc9 | 457 | |
8053b0ee | 458 | error = NCR5380_init(instance, host_flags | FLAG_LATE_DMA_SETUP); |
0ad0eff9 FT |
459 | if (error) |
460 | goto fail_init; | |
cbad48de FT |
461 | |
462 | if (instance->irq != NO_IRQ) { | |
463 | error = request_irq(instance->irq, macscsi_intr, IRQF_SHARED, | |
464 | "NCR5380", instance); | |
465 | if (error) | |
466 | goto fail_irq; | |
467 | } | |
468 | ||
9c3f0e2b FT |
469 | NCR5380_maybe_reset_bus(instance); |
470 | ||
cbad48de FT |
471 | error = scsi_add_host(instance, NULL); |
472 | if (error) | |
473 | goto fail_host; | |
474 | ||
475 | platform_set_drvdata(pdev, instance); | |
476 | ||
477 | scsi_scan_host(instance); | |
478 | return 0; | |
479 | ||
480 | fail_host: | |
481 | if (instance->irq != NO_IRQ) | |
482 | free_irq(instance->irq, instance); | |
483 | fail_irq: | |
484 | NCR5380_exit(instance); | |
0ad0eff9 | 485 | fail_init: |
cbad48de FT |
486 | scsi_host_put(instance); |
487 | return error; | |
488 | } | |
489 | ||
490 | static int __exit mac_scsi_remove(struct platform_device *pdev) | |
491 | { | |
492 | struct Scsi_Host *instance = platform_get_drvdata(pdev); | |
493 | ||
494 | scsi_remove_host(instance); | |
495 | if (instance->irq != NO_IRQ) | |
496 | free_irq(instance->irq, instance); | |
497 | NCR5380_exit(instance); | |
498 | scsi_host_put(instance); | |
499 | return 0; | |
500 | } | |
501 | ||
502 | static struct platform_driver mac_scsi_driver = { | |
503 | .remove = __exit_p(mac_scsi_remove), | |
504 | .driver = { | |
505 | .name = DRV_MODULE_NAME, | |
cbad48de FT |
506 | }, |
507 | }; | |
1da177e4 | 508 | |
cbad48de | 509 | module_platform_driver_probe(mac_scsi_driver, mac_scsi_probe); |
6e9ae6d5 | 510 | |
cbad48de | 511 | MODULE_ALIAS("platform:" DRV_MODULE_NAME); |
6e9ae6d5 | 512 | MODULE_LICENSE("GPL"); |