162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Generic Macintosh NCR5380 driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 1998, Michael Schmitz <mschmitz@lbl.gov>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright 2019 Finn Thain
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * derived in part from:
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci/*
1262306a36Sopenharmony_ci * Generic Generic NCR5380 driver
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * Copyright 1995, Russell King
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <linux/delay.h>
1862306a36Sopenharmony_ci#include <linux/types.h>
1962306a36Sopenharmony_ci#include <linux/module.h>
2062306a36Sopenharmony_ci#include <linux/ioport.h>
2162306a36Sopenharmony_ci#include <linux/init.h>
2262306a36Sopenharmony_ci#include <linux/blkdev.h>
2362306a36Sopenharmony_ci#include <linux/interrupt.h>
2462306a36Sopenharmony_ci#include <linux/platform_device.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include <asm/hwtest.h>
2762306a36Sopenharmony_ci#include <asm/io.h>
2862306a36Sopenharmony_ci#include <asm/macintosh.h>
2962306a36Sopenharmony_ci#include <asm/macints.h>
3062306a36Sopenharmony_ci#include <asm/setup.h>
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#include <scsi/scsi_host.h>
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/* Definitions for the core NCR5380 driver. */
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#define NCR5380_implementation_fields   int pdma_residual
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define NCR5380_read(reg)           in_8(hostdata->io + ((reg) << 4))
3962306a36Sopenharmony_ci#define NCR5380_write(reg, value)   out_8(hostdata->io + ((reg) << 4), value)
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#define NCR5380_dma_xfer_len            macscsi_dma_xfer_len
4262306a36Sopenharmony_ci#define NCR5380_dma_recv_setup          macscsi_pread
4362306a36Sopenharmony_ci#define NCR5380_dma_send_setup          macscsi_pwrite
4462306a36Sopenharmony_ci#define NCR5380_dma_residual            macscsi_dma_residual
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#define NCR5380_intr                    macscsi_intr
4762306a36Sopenharmony_ci#define NCR5380_queue_command           macscsi_queue_command
4862306a36Sopenharmony_ci#define NCR5380_abort                   macscsi_abort
4962306a36Sopenharmony_ci#define NCR5380_host_reset              macscsi_host_reset
5062306a36Sopenharmony_ci#define NCR5380_info                    macscsi_info
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#include "NCR5380.h"
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic int setup_can_queue = -1;
5562306a36Sopenharmony_cimodule_param(setup_can_queue, int, 0);
5662306a36Sopenharmony_cistatic int setup_cmd_per_lun = -1;
5762306a36Sopenharmony_cimodule_param(setup_cmd_per_lun, int, 0);
5862306a36Sopenharmony_cistatic int setup_sg_tablesize = -1;
5962306a36Sopenharmony_cimodule_param(setup_sg_tablesize, int, 0);
6062306a36Sopenharmony_cistatic int setup_use_pdma = 512;
6162306a36Sopenharmony_cimodule_param(setup_use_pdma, int, 0);
6262306a36Sopenharmony_cistatic int setup_hostid = -1;
6362306a36Sopenharmony_cimodule_param(setup_hostid, int, 0);
6462306a36Sopenharmony_cistatic int setup_toshiba_delay = -1;
6562306a36Sopenharmony_cimodule_param(setup_toshiba_delay, int, 0);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci#ifndef MODULE
6862306a36Sopenharmony_cistatic int __init mac_scsi_setup(char *str)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	int ints[8];
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	(void)get_options(str, ARRAY_SIZE(ints), ints);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	if (ints[0] < 1) {
7562306a36Sopenharmony_ci		pr_err("Usage: mac5380=<can_queue>[,<cmd_per_lun>[,<sg_tablesize>[,<hostid>[,<use_tags>[,<use_pdma>[,<toshiba_delay>]]]]]]\n");
7662306a36Sopenharmony_ci		return 0;
7762306a36Sopenharmony_ci	}
7862306a36Sopenharmony_ci	if (ints[0] >= 1)
7962306a36Sopenharmony_ci		setup_can_queue = ints[1];
8062306a36Sopenharmony_ci	if (ints[0] >= 2)
8162306a36Sopenharmony_ci		setup_cmd_per_lun = ints[2];
8262306a36Sopenharmony_ci	if (ints[0] >= 3)
8362306a36Sopenharmony_ci		setup_sg_tablesize = ints[3];
8462306a36Sopenharmony_ci	if (ints[0] >= 4)
8562306a36Sopenharmony_ci		setup_hostid = ints[4];
8662306a36Sopenharmony_ci	/* ints[5] (use_tagged_queuing) is ignored */
8762306a36Sopenharmony_ci	if (ints[0] >= 6)
8862306a36Sopenharmony_ci		setup_use_pdma = ints[6];
8962306a36Sopenharmony_ci	if (ints[0] >= 7)
9062306a36Sopenharmony_ci		setup_toshiba_delay = ints[7];
9162306a36Sopenharmony_ci	return 1;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci__setup("mac5380=", mac_scsi_setup);
9562306a36Sopenharmony_ci#endif /* !MODULE */
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci/*
9862306a36Sopenharmony_ci * According to "Inside Macintosh: Devices", Mac OS requires disk drivers to
9962306a36Sopenharmony_ci * specify the number of bytes between the delays expected from a SCSI target.
10062306a36Sopenharmony_ci * This allows the operating system to "prevent bus errors when a target fails
10162306a36Sopenharmony_ci * to deliver the next byte within the processor bus error timeout period."
10262306a36Sopenharmony_ci * Linux SCSI drivers lack knowledge of the timing behaviour of SCSI targets
10362306a36Sopenharmony_ci * so bus errors are unavoidable.
10462306a36Sopenharmony_ci *
10562306a36Sopenharmony_ci * If a MOVE.B instruction faults, we assume that zero bytes were transferred
10662306a36Sopenharmony_ci * and simply retry. That assumption probably depends on target behaviour but
10762306a36Sopenharmony_ci * seems to hold up okay. The NOP provides synchronization: without it the
10862306a36Sopenharmony_ci * fault can sometimes occur after the program counter has moved past the
10962306a36Sopenharmony_ci * offending instruction. Post-increment addressing can't be used.
11062306a36Sopenharmony_ci */
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci#define MOVE_BYTE(operands) \
11362306a36Sopenharmony_ci	asm volatile ( \
11462306a36Sopenharmony_ci		"1:     moveb " operands "     \n" \
11562306a36Sopenharmony_ci		"11:    nop                    \n" \
11662306a36Sopenharmony_ci		"       addq #1,%0             \n" \
11762306a36Sopenharmony_ci		"       subq #1,%1             \n" \
11862306a36Sopenharmony_ci		"40:                           \n" \
11962306a36Sopenharmony_ci		"                              \n" \
12062306a36Sopenharmony_ci		".section .fixup,\"ax\"        \n" \
12162306a36Sopenharmony_ci		".even                         \n" \
12262306a36Sopenharmony_ci		"90:    movel #1, %2           \n" \
12362306a36Sopenharmony_ci		"       jra 40b                \n" \
12462306a36Sopenharmony_ci		".previous                     \n" \
12562306a36Sopenharmony_ci		"                              \n" \
12662306a36Sopenharmony_ci		".section __ex_table,\"a\"     \n" \
12762306a36Sopenharmony_ci		".align  4                     \n" \
12862306a36Sopenharmony_ci		".long   1b,90b                \n" \
12962306a36Sopenharmony_ci		".long  11b,90b                \n" \
13062306a36Sopenharmony_ci		".previous                     \n" \
13162306a36Sopenharmony_ci		: "+a" (addr), "+r" (n), "+r" (result) : "a" (io))
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci/*
13462306a36Sopenharmony_ci * If a MOVE.W (or MOVE.L) instruction faults, it cannot be retried because
13562306a36Sopenharmony_ci * the residual byte count would be uncertain. In that situation the MOVE_WORD
13662306a36Sopenharmony_ci * macro clears n in the fixup section to abort the transfer.
13762306a36Sopenharmony_ci */
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci#define MOVE_WORD(operands) \
14062306a36Sopenharmony_ci	asm volatile ( \
14162306a36Sopenharmony_ci		"1:     movew " operands "     \n" \
14262306a36Sopenharmony_ci		"11:    nop                    \n" \
14362306a36Sopenharmony_ci		"       subq #2,%1             \n" \
14462306a36Sopenharmony_ci		"40:                           \n" \
14562306a36Sopenharmony_ci		"                              \n" \
14662306a36Sopenharmony_ci		".section .fixup,\"ax\"        \n" \
14762306a36Sopenharmony_ci		".even                         \n" \
14862306a36Sopenharmony_ci		"90:    movel #0, %1           \n" \
14962306a36Sopenharmony_ci		"       movel #2, %2           \n" \
15062306a36Sopenharmony_ci		"       jra 40b                \n" \
15162306a36Sopenharmony_ci		".previous                     \n" \
15262306a36Sopenharmony_ci		"                              \n" \
15362306a36Sopenharmony_ci		".section __ex_table,\"a\"     \n" \
15462306a36Sopenharmony_ci		".align  4                     \n" \
15562306a36Sopenharmony_ci		".long   1b,90b                \n" \
15662306a36Sopenharmony_ci		".long  11b,90b                \n" \
15762306a36Sopenharmony_ci		".previous                     \n" \
15862306a36Sopenharmony_ci		: "+a" (addr), "+r" (n), "+r" (result) : "a" (io))
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci#define MOVE_16_WORDS(operands) \
16162306a36Sopenharmony_ci	asm volatile ( \
16262306a36Sopenharmony_ci		"1:     movew " operands "     \n" \
16362306a36Sopenharmony_ci		"2:     movew " operands "     \n" \
16462306a36Sopenharmony_ci		"3:     movew " operands "     \n" \
16562306a36Sopenharmony_ci		"4:     movew " operands "     \n" \
16662306a36Sopenharmony_ci		"5:     movew " operands "     \n" \
16762306a36Sopenharmony_ci		"6:     movew " operands "     \n" \
16862306a36Sopenharmony_ci		"7:     movew " operands "     \n" \
16962306a36Sopenharmony_ci		"8:     movew " operands "     \n" \
17062306a36Sopenharmony_ci		"9:     movew " operands "     \n" \
17162306a36Sopenharmony_ci		"10:    movew " operands "     \n" \
17262306a36Sopenharmony_ci		"11:    movew " operands "     \n" \
17362306a36Sopenharmony_ci		"12:    movew " operands "     \n" \
17462306a36Sopenharmony_ci		"13:    movew " operands "     \n" \
17562306a36Sopenharmony_ci		"14:    movew " operands "     \n" \
17662306a36Sopenharmony_ci		"15:    movew " operands "     \n" \
17762306a36Sopenharmony_ci		"16:    movew " operands "     \n" \
17862306a36Sopenharmony_ci		"17:    nop                    \n" \
17962306a36Sopenharmony_ci		"       subl  #32,%1           \n" \
18062306a36Sopenharmony_ci		"40:                           \n" \
18162306a36Sopenharmony_ci		"                              \n" \
18262306a36Sopenharmony_ci		".section .fixup,\"ax\"        \n" \
18362306a36Sopenharmony_ci		".even                         \n" \
18462306a36Sopenharmony_ci		"90:    movel #0, %1           \n" \
18562306a36Sopenharmony_ci		"       movel #2, %2           \n" \
18662306a36Sopenharmony_ci		"       jra 40b                \n" \
18762306a36Sopenharmony_ci		".previous                     \n" \
18862306a36Sopenharmony_ci		"                              \n" \
18962306a36Sopenharmony_ci		".section __ex_table,\"a\"     \n" \
19062306a36Sopenharmony_ci		".align  4                     \n" \
19162306a36Sopenharmony_ci		".long   1b,90b                \n" \
19262306a36Sopenharmony_ci		".long   2b,90b                \n" \
19362306a36Sopenharmony_ci		".long   3b,90b                \n" \
19462306a36Sopenharmony_ci		".long   4b,90b                \n" \
19562306a36Sopenharmony_ci		".long   5b,90b                \n" \
19662306a36Sopenharmony_ci		".long   6b,90b                \n" \
19762306a36Sopenharmony_ci		".long   7b,90b                \n" \
19862306a36Sopenharmony_ci		".long   8b,90b                \n" \
19962306a36Sopenharmony_ci		".long   9b,90b                \n" \
20062306a36Sopenharmony_ci		".long  10b,90b                \n" \
20162306a36Sopenharmony_ci		".long  11b,90b                \n" \
20262306a36Sopenharmony_ci		".long  12b,90b                \n" \
20362306a36Sopenharmony_ci		".long  13b,90b                \n" \
20462306a36Sopenharmony_ci		".long  14b,90b                \n" \
20562306a36Sopenharmony_ci		".long  15b,90b                \n" \
20662306a36Sopenharmony_ci		".long  16b,90b                \n" \
20762306a36Sopenharmony_ci		".long  17b,90b                \n" \
20862306a36Sopenharmony_ci		".previous                     \n" \
20962306a36Sopenharmony_ci		: "+a" (addr), "+r" (n), "+r" (result) : "a" (io))
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci#define MAC_PDMA_DELAY		32
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic inline int mac_pdma_recv(void __iomem *io, unsigned char *start, int n)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	unsigned char *addr = start;
21662306a36Sopenharmony_ci	int result = 0;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	if (n >= 1) {
21962306a36Sopenharmony_ci		MOVE_BYTE("%3@,%0@");
22062306a36Sopenharmony_ci		if (result)
22162306a36Sopenharmony_ci			goto out;
22262306a36Sopenharmony_ci	}
22362306a36Sopenharmony_ci	if (n >= 1 && ((unsigned long)addr & 1)) {
22462306a36Sopenharmony_ci		MOVE_BYTE("%3@,%0@");
22562306a36Sopenharmony_ci		if (result)
22662306a36Sopenharmony_ci			goto out;
22762306a36Sopenharmony_ci	}
22862306a36Sopenharmony_ci	while (n >= 32)
22962306a36Sopenharmony_ci		MOVE_16_WORDS("%3@,%0@+");
23062306a36Sopenharmony_ci	while (n >= 2)
23162306a36Sopenharmony_ci		MOVE_WORD("%3@,%0@+");
23262306a36Sopenharmony_ci	if (result)
23362306a36Sopenharmony_ci		return start - addr; /* Negated to indicate uncertain length */
23462306a36Sopenharmony_ci	if (n == 1)
23562306a36Sopenharmony_ci		MOVE_BYTE("%3@,%0@");
23662306a36Sopenharmony_ciout:
23762306a36Sopenharmony_ci	return addr - start;
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cistatic inline int mac_pdma_send(unsigned char *start, void __iomem *io, int n)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	unsigned char *addr = start;
24362306a36Sopenharmony_ci	int result = 0;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	if (n >= 1) {
24662306a36Sopenharmony_ci		MOVE_BYTE("%0@,%3@");
24762306a36Sopenharmony_ci		if (result)
24862306a36Sopenharmony_ci			goto out;
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci	if (n >= 1 && ((unsigned long)addr & 1)) {
25162306a36Sopenharmony_ci		MOVE_BYTE("%0@,%3@");
25262306a36Sopenharmony_ci		if (result)
25362306a36Sopenharmony_ci			goto out;
25462306a36Sopenharmony_ci	}
25562306a36Sopenharmony_ci	while (n >= 32)
25662306a36Sopenharmony_ci		MOVE_16_WORDS("%0@+,%3@");
25762306a36Sopenharmony_ci	while (n >= 2)
25862306a36Sopenharmony_ci		MOVE_WORD("%0@+,%3@");
25962306a36Sopenharmony_ci	if (result)
26062306a36Sopenharmony_ci		return start - addr; /* Negated to indicate uncertain length */
26162306a36Sopenharmony_ci	if (n == 1)
26262306a36Sopenharmony_ci		MOVE_BYTE("%0@,%3@");
26362306a36Sopenharmony_ciout:
26462306a36Sopenharmony_ci	return addr - start;
26562306a36Sopenharmony_ci}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci/* The "SCSI DMA" chip on the IIfx implements this register. */
26862306a36Sopenharmony_ci#define CTRL_REG                0x8
26962306a36Sopenharmony_ci#define CTRL_INTERRUPTS_ENABLE  BIT(1)
27062306a36Sopenharmony_ci#define CTRL_HANDSHAKE_MODE     BIT(3)
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic inline void write_ctrl_reg(struct NCR5380_hostdata *hostdata, u32 value)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	out_be32(hostdata->io + (CTRL_REG << 4), value);
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_cistatic inline int macscsi_pread(struct NCR5380_hostdata *hostdata,
27862306a36Sopenharmony_ci                                unsigned char *dst, int len)
27962306a36Sopenharmony_ci{
28062306a36Sopenharmony_ci	u8 __iomem *s = hostdata->pdma_io + (INPUT_DATA_REG << 4);
28162306a36Sopenharmony_ci	unsigned char *d = dst;
28262306a36Sopenharmony_ci	int result = 0;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	hostdata->pdma_residual = len;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	while (!NCR5380_poll_politely(hostdata, BUS_AND_STATUS_REG,
28762306a36Sopenharmony_ci	                              BASR_DRQ | BASR_PHASE_MATCH,
28862306a36Sopenharmony_ci	                              BASR_DRQ | BASR_PHASE_MATCH, 0)) {
28962306a36Sopenharmony_ci		int bytes;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci		if (macintosh_config->ident == MAC_MODEL_IIFX)
29262306a36Sopenharmony_ci			write_ctrl_reg(hostdata, CTRL_HANDSHAKE_MODE |
29362306a36Sopenharmony_ci			                         CTRL_INTERRUPTS_ENABLE);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci		bytes = mac_pdma_recv(s, d, min(hostdata->pdma_residual, 512));
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci		if (bytes > 0) {
29862306a36Sopenharmony_ci			d += bytes;
29962306a36Sopenharmony_ci			hostdata->pdma_residual -= bytes;
30062306a36Sopenharmony_ci		}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci		if (hostdata->pdma_residual == 0)
30362306a36Sopenharmony_ci			goto out;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci		if (NCR5380_poll_politely2(hostdata, STATUS_REG, SR_REQ, SR_REQ,
30662306a36Sopenharmony_ci		                           BUS_AND_STATUS_REG, BASR_ACK,
30762306a36Sopenharmony_ci		                           BASR_ACK, 0) < 0)
30862306a36Sopenharmony_ci			scmd_printk(KERN_DEBUG, hostdata->connected,
30962306a36Sopenharmony_ci			            "%s: !REQ and !ACK\n", __func__);
31062306a36Sopenharmony_ci		if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_PHASE_MATCH))
31162306a36Sopenharmony_ci			goto out;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci		if (bytes == 0)
31462306a36Sopenharmony_ci			udelay(MAC_PDMA_DELAY);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci		if (bytes >= 0)
31762306a36Sopenharmony_ci			continue;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci		dsprintk(NDEBUG_PSEUDO_DMA, hostdata->host,
32062306a36Sopenharmony_ci		         "%s: bus error (%d/%d)\n", __func__, d - dst, len);
32162306a36Sopenharmony_ci		NCR5380_dprint(NDEBUG_PSEUDO_DMA, hostdata->host);
32262306a36Sopenharmony_ci		result = -1;
32362306a36Sopenharmony_ci		goto out;
32462306a36Sopenharmony_ci	}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	scmd_printk(KERN_ERR, hostdata->connected,
32762306a36Sopenharmony_ci	            "%s: phase mismatch or !DRQ\n", __func__);
32862306a36Sopenharmony_ci	NCR5380_dprint(NDEBUG_PSEUDO_DMA, hostdata->host);
32962306a36Sopenharmony_ci	result = -1;
33062306a36Sopenharmony_ciout:
33162306a36Sopenharmony_ci	if (macintosh_config->ident == MAC_MODEL_IIFX)
33262306a36Sopenharmony_ci		write_ctrl_reg(hostdata, CTRL_INTERRUPTS_ENABLE);
33362306a36Sopenharmony_ci	return result;
33462306a36Sopenharmony_ci}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_cistatic inline int macscsi_pwrite(struct NCR5380_hostdata *hostdata,
33762306a36Sopenharmony_ci                                 unsigned char *src, int len)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	unsigned char *s = src;
34062306a36Sopenharmony_ci	u8 __iomem *d = hostdata->pdma_io + (OUTPUT_DATA_REG << 4);
34162306a36Sopenharmony_ci	int result = 0;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	hostdata->pdma_residual = len;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	while (!NCR5380_poll_politely(hostdata, BUS_AND_STATUS_REG,
34662306a36Sopenharmony_ci	                              BASR_DRQ | BASR_PHASE_MATCH,
34762306a36Sopenharmony_ci	                              BASR_DRQ | BASR_PHASE_MATCH, 0)) {
34862306a36Sopenharmony_ci		int bytes;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci		if (macintosh_config->ident == MAC_MODEL_IIFX)
35162306a36Sopenharmony_ci			write_ctrl_reg(hostdata, CTRL_HANDSHAKE_MODE |
35262306a36Sopenharmony_ci			                         CTRL_INTERRUPTS_ENABLE);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci		bytes = mac_pdma_send(s, d, min(hostdata->pdma_residual, 512));
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci		if (bytes > 0) {
35762306a36Sopenharmony_ci			s += bytes;
35862306a36Sopenharmony_ci			hostdata->pdma_residual -= bytes;
35962306a36Sopenharmony_ci		}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci		if (hostdata->pdma_residual == 0) {
36262306a36Sopenharmony_ci			if (NCR5380_poll_politely(hostdata, TARGET_COMMAND_REG,
36362306a36Sopenharmony_ci			                          TCR_LAST_BYTE_SENT,
36462306a36Sopenharmony_ci			                          TCR_LAST_BYTE_SENT,
36562306a36Sopenharmony_ci			                          0) < 0) {
36662306a36Sopenharmony_ci				scmd_printk(KERN_ERR, hostdata->connected,
36762306a36Sopenharmony_ci				            "%s: Last Byte Sent timeout\n", __func__);
36862306a36Sopenharmony_ci				result = -1;
36962306a36Sopenharmony_ci			}
37062306a36Sopenharmony_ci			goto out;
37162306a36Sopenharmony_ci		}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci		if (NCR5380_poll_politely2(hostdata, STATUS_REG, SR_REQ, SR_REQ,
37462306a36Sopenharmony_ci		                           BUS_AND_STATUS_REG, BASR_ACK,
37562306a36Sopenharmony_ci		                           BASR_ACK, 0) < 0)
37662306a36Sopenharmony_ci			scmd_printk(KERN_DEBUG, hostdata->connected,
37762306a36Sopenharmony_ci			            "%s: !REQ and !ACK\n", __func__);
37862306a36Sopenharmony_ci		if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_PHASE_MATCH))
37962306a36Sopenharmony_ci			goto out;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci		if (bytes == 0)
38262306a36Sopenharmony_ci			udelay(MAC_PDMA_DELAY);
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci		if (bytes >= 0)
38562306a36Sopenharmony_ci			continue;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci		dsprintk(NDEBUG_PSEUDO_DMA, hostdata->host,
38862306a36Sopenharmony_ci		         "%s: bus error (%d/%d)\n", __func__, s - src, len);
38962306a36Sopenharmony_ci		NCR5380_dprint(NDEBUG_PSEUDO_DMA, hostdata->host);
39062306a36Sopenharmony_ci		result = -1;
39162306a36Sopenharmony_ci		goto out;
39262306a36Sopenharmony_ci	}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	scmd_printk(KERN_ERR, hostdata->connected,
39562306a36Sopenharmony_ci	            "%s: phase mismatch or !DRQ\n", __func__);
39662306a36Sopenharmony_ci	NCR5380_dprint(NDEBUG_PSEUDO_DMA, hostdata->host);
39762306a36Sopenharmony_ci	result = -1;
39862306a36Sopenharmony_ciout:
39962306a36Sopenharmony_ci	if (macintosh_config->ident == MAC_MODEL_IIFX)
40062306a36Sopenharmony_ci		write_ctrl_reg(hostdata, CTRL_INTERRUPTS_ENABLE);
40162306a36Sopenharmony_ci	return result;
40262306a36Sopenharmony_ci}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_cistatic int macscsi_dma_xfer_len(struct NCR5380_hostdata *hostdata,
40562306a36Sopenharmony_ci                                struct scsi_cmnd *cmd)
40662306a36Sopenharmony_ci{
40762306a36Sopenharmony_ci	int resid = NCR5380_to_ncmd(cmd)->this_residual;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	if (hostdata->flags & FLAG_NO_PSEUDO_DMA || resid < setup_use_pdma)
41062306a36Sopenharmony_ci		return 0;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	return resid;
41362306a36Sopenharmony_ci}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_cistatic int macscsi_dma_residual(struct NCR5380_hostdata *hostdata)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	return hostdata->pdma_residual;
41862306a36Sopenharmony_ci}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci#include "NCR5380.c"
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci#define DRV_MODULE_NAME         "mac_scsi"
42362306a36Sopenharmony_ci#define PFX                     DRV_MODULE_NAME ": "
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_cistatic struct scsi_host_template mac_scsi_template = {
42662306a36Sopenharmony_ci	.module			= THIS_MODULE,
42762306a36Sopenharmony_ci	.proc_name		= DRV_MODULE_NAME,
42862306a36Sopenharmony_ci	.name			= "Macintosh NCR5380 SCSI",
42962306a36Sopenharmony_ci	.info			= macscsi_info,
43062306a36Sopenharmony_ci	.queuecommand		= macscsi_queue_command,
43162306a36Sopenharmony_ci	.eh_abort_handler	= macscsi_abort,
43262306a36Sopenharmony_ci	.eh_host_reset_handler	= macscsi_host_reset,
43362306a36Sopenharmony_ci	.can_queue		= 16,
43462306a36Sopenharmony_ci	.this_id		= 7,
43562306a36Sopenharmony_ci	.sg_tablesize		= 1,
43662306a36Sopenharmony_ci	.cmd_per_lun		= 2,
43762306a36Sopenharmony_ci	.dma_boundary		= PAGE_SIZE - 1,
43862306a36Sopenharmony_ci	.cmd_size		= sizeof(struct NCR5380_cmd),
43962306a36Sopenharmony_ci	.max_sectors		= 128,
44062306a36Sopenharmony_ci};
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cistatic int __init mac_scsi_probe(struct platform_device *pdev)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	struct Scsi_Host *instance;
44562306a36Sopenharmony_ci	struct NCR5380_hostdata *hostdata;
44662306a36Sopenharmony_ci	int error;
44762306a36Sopenharmony_ci	int host_flags = 0;
44862306a36Sopenharmony_ci	struct resource *irq, *pio_mem, *pdma_mem = NULL;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	pio_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
45162306a36Sopenharmony_ci	if (!pio_mem)
45262306a36Sopenharmony_ci		return -ENODEV;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	pdma_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	if (!hwreg_present((unsigned char *)pio_mem->start +
45962306a36Sopenharmony_ci	                   (STATUS_REG << 4))) {
46062306a36Sopenharmony_ci		pr_info(PFX "no device detected at %pap\n", &pio_mem->start);
46162306a36Sopenharmony_ci		return -ENODEV;
46262306a36Sopenharmony_ci	}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	if (setup_can_queue > 0)
46562306a36Sopenharmony_ci		mac_scsi_template.can_queue = setup_can_queue;
46662306a36Sopenharmony_ci	if (setup_cmd_per_lun > 0)
46762306a36Sopenharmony_ci		mac_scsi_template.cmd_per_lun = setup_cmd_per_lun;
46862306a36Sopenharmony_ci	if (setup_sg_tablesize > 0)
46962306a36Sopenharmony_ci		mac_scsi_template.sg_tablesize = setup_sg_tablesize;
47062306a36Sopenharmony_ci	if (setup_hostid >= 0)
47162306a36Sopenharmony_ci		mac_scsi_template.this_id = setup_hostid & 7;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	instance = scsi_host_alloc(&mac_scsi_template,
47462306a36Sopenharmony_ci	                           sizeof(struct NCR5380_hostdata));
47562306a36Sopenharmony_ci	if (!instance)
47662306a36Sopenharmony_ci		return -ENOMEM;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	if (irq)
47962306a36Sopenharmony_ci		instance->irq = irq->start;
48062306a36Sopenharmony_ci	else
48162306a36Sopenharmony_ci		instance->irq = NO_IRQ;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	hostdata = shost_priv(instance);
48462306a36Sopenharmony_ci	hostdata->base = pio_mem->start;
48562306a36Sopenharmony_ci	hostdata->io = (u8 __iomem *)pio_mem->start;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	if (pdma_mem && setup_use_pdma)
48862306a36Sopenharmony_ci		hostdata->pdma_io = (u8 __iomem *)pdma_mem->start;
48962306a36Sopenharmony_ci	else
49062306a36Sopenharmony_ci		host_flags |= FLAG_NO_PSEUDO_DMA;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	host_flags |= setup_toshiba_delay > 0 ? FLAG_TOSHIBA_DELAY : 0;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	error = NCR5380_init(instance, host_flags | FLAG_LATE_DMA_SETUP);
49562306a36Sopenharmony_ci	if (error)
49662306a36Sopenharmony_ci		goto fail_init;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	if (instance->irq != NO_IRQ) {
49962306a36Sopenharmony_ci		error = request_irq(instance->irq, macscsi_intr, IRQF_SHARED,
50062306a36Sopenharmony_ci		                    "NCR5380", instance);
50162306a36Sopenharmony_ci		if (error)
50262306a36Sopenharmony_ci			goto fail_irq;
50362306a36Sopenharmony_ci	}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	NCR5380_maybe_reset_bus(instance);
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	error = scsi_add_host(instance, NULL);
50862306a36Sopenharmony_ci	if (error)
50962306a36Sopenharmony_ci		goto fail_host;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	platform_set_drvdata(pdev, instance);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	scsi_scan_host(instance);
51462306a36Sopenharmony_ci	return 0;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_cifail_host:
51762306a36Sopenharmony_ci	if (instance->irq != NO_IRQ)
51862306a36Sopenharmony_ci		free_irq(instance->irq, instance);
51962306a36Sopenharmony_cifail_irq:
52062306a36Sopenharmony_ci	NCR5380_exit(instance);
52162306a36Sopenharmony_cifail_init:
52262306a36Sopenharmony_ci	scsi_host_put(instance);
52362306a36Sopenharmony_ci	return error;
52462306a36Sopenharmony_ci}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_cistatic int __exit mac_scsi_remove(struct platform_device *pdev)
52762306a36Sopenharmony_ci{
52862306a36Sopenharmony_ci	struct Scsi_Host *instance = platform_get_drvdata(pdev);
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	scsi_remove_host(instance);
53162306a36Sopenharmony_ci	if (instance->irq != NO_IRQ)
53262306a36Sopenharmony_ci		free_irq(instance->irq, instance);
53362306a36Sopenharmony_ci	NCR5380_exit(instance);
53462306a36Sopenharmony_ci	scsi_host_put(instance);
53562306a36Sopenharmony_ci	return 0;
53662306a36Sopenharmony_ci}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_cistatic struct platform_driver mac_scsi_driver = {
53962306a36Sopenharmony_ci	.remove = __exit_p(mac_scsi_remove),
54062306a36Sopenharmony_ci	.driver = {
54162306a36Sopenharmony_ci		.name	= DRV_MODULE_NAME,
54262306a36Sopenharmony_ci	},
54362306a36Sopenharmony_ci};
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_cimodule_platform_driver_probe(mac_scsi_driver, mac_scsi_probe);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ciMODULE_ALIAS("platform:" DRV_MODULE_NAME);
54862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
549