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