162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* mac_esp.c: ESP front-end for Macintosh Quadra systems.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Adapted from jazz_esp.c and the old mac_esp.c.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * The pseudo DMA algorithm is based on the one used in NetBSD.
762306a36Sopenharmony_ci * See sys/arch/mac68k/obio/esp.c for some background information.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Copyright (C) 2007-2008 Finn Thain
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/types.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/init.h>
1662306a36Sopenharmony_ci#include <linux/interrupt.h>
1762306a36Sopenharmony_ci#include <linux/platform_device.h>
1862306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1962306a36Sopenharmony_ci#include <linux/scatterlist.h>
2062306a36Sopenharmony_ci#include <linux/delay.h>
2162306a36Sopenharmony_ci#include <linux/io.h>
2262306a36Sopenharmony_ci#include <linux/nubus.h>
2362306a36Sopenharmony_ci#include <linux/slab.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include <asm/irq.h>
2662306a36Sopenharmony_ci#include <asm/dma.h>
2762306a36Sopenharmony_ci#include <asm/macints.h>
2862306a36Sopenharmony_ci#include <asm/macintosh.h>
2962306a36Sopenharmony_ci#include <asm/mac_via.h>
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#include <scsi/scsi_host.h>
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#include "esp_scsi.h"
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define DRV_MODULE_NAME     "mac_esp"
3662306a36Sopenharmony_ci#define PFX                 DRV_MODULE_NAME ": "
3762306a36Sopenharmony_ci#define DRV_VERSION         "1.000"
3862306a36Sopenharmony_ci#define DRV_MODULE_RELDATE  "Sept 15, 2007"
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#define MAC_ESP_IO_BASE          0x50F00000
4162306a36Sopenharmony_ci#define MAC_ESP_REGS_QUADRA      (MAC_ESP_IO_BASE + 0x10000)
4262306a36Sopenharmony_ci#define MAC_ESP_REGS_QUADRA2     (MAC_ESP_IO_BASE + 0xF000)
4362306a36Sopenharmony_ci#define MAC_ESP_REGS_QUADRA3     (MAC_ESP_IO_BASE + 0x18000)
4462306a36Sopenharmony_ci#define MAC_ESP_REGS_SPACING     0x402
4562306a36Sopenharmony_ci#define MAC_ESP_PDMA_REG         0xF9800024
4662306a36Sopenharmony_ci#define MAC_ESP_PDMA_REG_SPACING 0x4
4762306a36Sopenharmony_ci#define MAC_ESP_PDMA_IO_OFFSET   0x100
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#define esp_read8(REG)		mac_esp_read8(esp, REG)
5062306a36Sopenharmony_ci#define esp_write8(VAL, REG)	mac_esp_write8(esp, VAL, REG)
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistruct mac_esp_priv {
5362306a36Sopenharmony_ci	struct esp *esp;
5462306a36Sopenharmony_ci	void __iomem *pdma_regs;
5562306a36Sopenharmony_ci	void __iomem *pdma_io;
5662306a36Sopenharmony_ci};
5762306a36Sopenharmony_cistatic struct esp *esp_chips[2];
5862306a36Sopenharmony_cistatic DEFINE_SPINLOCK(esp_chips_lock);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci#define MAC_ESP_GET_PRIV(esp) ((struct mac_esp_priv *) \
6162306a36Sopenharmony_ci			       dev_get_drvdata((esp)->dev))
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic inline void mac_esp_write8(struct esp *esp, u8 val, unsigned long reg)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	nubus_writeb(val, esp->regs + reg * 16);
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic inline u8 mac_esp_read8(struct esp *esp, unsigned long reg)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	return nubus_readb(esp->regs + reg * 16);
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic void mac_esp_reset_dma(struct esp *esp)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	/* Nothing to do. */
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic void mac_esp_dma_drain(struct esp *esp)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	/* Nothing to do. */
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic void mac_esp_dma_invalidate(struct esp *esp)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	/* Nothing to do. */
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic int mac_esp_dma_error(struct esp *esp)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	return esp->send_cmd_error;
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic inline int mac_esp_wait_for_empty_fifo(struct esp *esp)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	int i = 500000;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	do {
9862306a36Sopenharmony_ci		if (!(esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES))
9962306a36Sopenharmony_ci			return 0;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci		if (esp_read8(ESP_STATUS) & ESP_STAT_INTR)
10262306a36Sopenharmony_ci			return 1;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci		udelay(2);
10562306a36Sopenharmony_ci	} while (--i);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	printk(KERN_ERR PFX "FIFO is not empty (sreg %02x)\n",
10862306a36Sopenharmony_ci	       esp_read8(ESP_STATUS));
10962306a36Sopenharmony_ci	esp->send_cmd_error = 1;
11062306a36Sopenharmony_ci	return 1;
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic inline int mac_esp_wait_for_dreq(struct esp *esp)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	struct mac_esp_priv *mep = MAC_ESP_GET_PRIV(esp);
11662306a36Sopenharmony_ci	int i = 500000;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	do {
11962306a36Sopenharmony_ci		if (mep->pdma_regs == NULL) {
12062306a36Sopenharmony_ci			if (via2_scsi_drq_pending())
12162306a36Sopenharmony_ci				return 0;
12262306a36Sopenharmony_ci		} else {
12362306a36Sopenharmony_ci			if (nubus_readl(mep->pdma_regs) & 0x200)
12462306a36Sopenharmony_ci				return 0;
12562306a36Sopenharmony_ci		}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci		if (esp_read8(ESP_STATUS) & ESP_STAT_INTR)
12862306a36Sopenharmony_ci			return 1;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci		udelay(2);
13162306a36Sopenharmony_ci	} while (--i);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	printk(KERN_ERR PFX "PDMA timeout (sreg %02x)\n",
13462306a36Sopenharmony_ci	       esp_read8(ESP_STATUS));
13562306a36Sopenharmony_ci	esp->send_cmd_error = 1;
13662306a36Sopenharmony_ci	return 1;
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci#define MAC_ESP_PDMA_LOOP(operands) \
14062306a36Sopenharmony_ci	asm volatile ( \
14162306a36Sopenharmony_ci	     "       tstw %1                   \n" \
14262306a36Sopenharmony_ci	     "       jbeq 20f                  \n" \
14362306a36Sopenharmony_ci	     "1:     movew " operands "        \n" \
14462306a36Sopenharmony_ci	     "2:     movew " operands "        \n" \
14562306a36Sopenharmony_ci	     "3:     movew " operands "        \n" \
14662306a36Sopenharmony_ci	     "4:     movew " operands "        \n" \
14762306a36Sopenharmony_ci	     "5:     movew " operands "        \n" \
14862306a36Sopenharmony_ci	     "6:     movew " operands "        \n" \
14962306a36Sopenharmony_ci	     "7:     movew " operands "        \n" \
15062306a36Sopenharmony_ci	     "8:     movew " operands "        \n" \
15162306a36Sopenharmony_ci	     "9:     movew " operands "        \n" \
15262306a36Sopenharmony_ci	     "10:    movew " operands "        \n" \
15362306a36Sopenharmony_ci	     "11:    movew " operands "        \n" \
15462306a36Sopenharmony_ci	     "12:    movew " operands "        \n" \
15562306a36Sopenharmony_ci	     "13:    movew " operands "        \n" \
15662306a36Sopenharmony_ci	     "14:    movew " operands "        \n" \
15762306a36Sopenharmony_ci	     "15:    movew " operands "        \n" \
15862306a36Sopenharmony_ci	     "16:    movew " operands "        \n" \
15962306a36Sopenharmony_ci	     "       subqw #1,%1               \n" \
16062306a36Sopenharmony_ci	     "       jbne 1b                   \n" \
16162306a36Sopenharmony_ci	     "20:    tstw %2                   \n" \
16262306a36Sopenharmony_ci	     "       jbeq 30f                  \n" \
16362306a36Sopenharmony_ci	     "21:    movew " operands "        \n" \
16462306a36Sopenharmony_ci	     "       subqw #1,%2               \n" \
16562306a36Sopenharmony_ci	     "       jbne 21b                  \n" \
16662306a36Sopenharmony_ci	     "30:    tstw %3                   \n" \
16762306a36Sopenharmony_ci	     "       jbeq 40f                  \n" \
16862306a36Sopenharmony_ci	     "31:    moveb " operands "        \n" \
16962306a36Sopenharmony_ci	     "32:    nop                       \n" \
17062306a36Sopenharmony_ci	     "40:                              \n" \
17162306a36Sopenharmony_ci	     "                                 \n" \
17262306a36Sopenharmony_ci	     "       .section __ex_table,\"a\" \n" \
17362306a36Sopenharmony_ci	     "       .align  4                 \n" \
17462306a36Sopenharmony_ci	     "       .long   1b,40b            \n" \
17562306a36Sopenharmony_ci	     "       .long   2b,40b            \n" \
17662306a36Sopenharmony_ci	     "       .long   3b,40b            \n" \
17762306a36Sopenharmony_ci	     "       .long   4b,40b            \n" \
17862306a36Sopenharmony_ci	     "       .long   5b,40b            \n" \
17962306a36Sopenharmony_ci	     "       .long   6b,40b            \n" \
18062306a36Sopenharmony_ci	     "       .long   7b,40b            \n" \
18162306a36Sopenharmony_ci	     "       .long   8b,40b            \n" \
18262306a36Sopenharmony_ci	     "       .long   9b,40b            \n" \
18362306a36Sopenharmony_ci	     "       .long  10b,40b            \n" \
18462306a36Sopenharmony_ci	     "       .long  11b,40b            \n" \
18562306a36Sopenharmony_ci	     "       .long  12b,40b            \n" \
18662306a36Sopenharmony_ci	     "       .long  13b,40b            \n" \
18762306a36Sopenharmony_ci	     "       .long  14b,40b            \n" \
18862306a36Sopenharmony_ci	     "       .long  15b,40b            \n" \
18962306a36Sopenharmony_ci	     "       .long  16b,40b            \n" \
19062306a36Sopenharmony_ci	     "       .long  21b,40b            \n" \
19162306a36Sopenharmony_ci	     "       .long  31b,40b            \n" \
19262306a36Sopenharmony_ci	     "       .long  32b,40b            \n" \
19362306a36Sopenharmony_ci	     "       .previous                 \n" \
19462306a36Sopenharmony_ci	     : "+a" (addr), "+r" (count32), "+r" (count2) \
19562306a36Sopenharmony_ci	     : "g" (count1), "a" (mep->pdma_io))
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic void mac_esp_send_pdma_cmd(struct esp *esp, u32 addr, u32 esp_count,
19862306a36Sopenharmony_ci				  u32 dma_count, int write, u8 cmd)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	struct mac_esp_priv *mep = MAC_ESP_GET_PRIV(esp);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	esp->send_cmd_error = 0;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	if (!write)
20562306a36Sopenharmony_ci		scsi_esp_cmd(esp, ESP_CMD_FLUSH);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	esp_write8((esp_count >> 0) & 0xFF, ESP_TCLOW);
20862306a36Sopenharmony_ci	esp_write8((esp_count >> 8) & 0xFF, ESP_TCMED);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	scsi_esp_cmd(esp, cmd);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	do {
21362306a36Sopenharmony_ci		unsigned int count32 = esp_count >> 5;
21462306a36Sopenharmony_ci		unsigned int count2 = (esp_count & 0x1F) >> 1;
21562306a36Sopenharmony_ci		unsigned int count1 = esp_count & 1;
21662306a36Sopenharmony_ci		unsigned int start_addr = addr;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci		if (mac_esp_wait_for_dreq(esp))
21962306a36Sopenharmony_ci			break;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci		if (write) {
22262306a36Sopenharmony_ci			MAC_ESP_PDMA_LOOP("%4@,%0@+");
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci			esp_count -= addr - start_addr;
22562306a36Sopenharmony_ci		} else {
22662306a36Sopenharmony_ci			unsigned int n;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci			MAC_ESP_PDMA_LOOP("%0@+,%4@");
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci			if (mac_esp_wait_for_empty_fifo(esp))
23162306a36Sopenharmony_ci				break;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci			n = (esp_read8(ESP_TCMED) << 8) + esp_read8(ESP_TCLOW);
23462306a36Sopenharmony_ci			addr = start_addr + esp_count - n;
23562306a36Sopenharmony_ci			esp_count = n;
23662306a36Sopenharmony_ci		}
23762306a36Sopenharmony_ci	} while (esp_count);
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cistatic int mac_esp_irq_pending(struct esp *esp)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	if (esp_read8(ESP_STATUS) & ESP_STAT_INTR)
24362306a36Sopenharmony_ci		return 1;
24462306a36Sopenharmony_ci	return 0;
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cistatic u32 mac_esp_dma_length_limit(struct esp *esp, u32 dma_addr, u32 dma_len)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	return dma_len > 0xFFFF ? 0xFFFF : dma_len;
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic irqreturn_t mac_scsi_esp_intr(int irq, void *dev_id)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	int got_intr;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	/*
25762306a36Sopenharmony_ci	 * This is an edge triggered IRQ, so we have to be careful to
25862306a36Sopenharmony_ci	 * avoid missing a transition when it is shared by two ESP devices.
25962306a36Sopenharmony_ci	 */
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	do {
26262306a36Sopenharmony_ci		got_intr = 0;
26362306a36Sopenharmony_ci		if (esp_chips[0] &&
26462306a36Sopenharmony_ci		    (mac_esp_read8(esp_chips[0], ESP_STATUS) & ESP_STAT_INTR)) {
26562306a36Sopenharmony_ci			(void)scsi_esp_intr(irq, esp_chips[0]);
26662306a36Sopenharmony_ci			got_intr = 1;
26762306a36Sopenharmony_ci		}
26862306a36Sopenharmony_ci		if (esp_chips[1] &&
26962306a36Sopenharmony_ci		    (mac_esp_read8(esp_chips[1], ESP_STATUS) & ESP_STAT_INTR)) {
27062306a36Sopenharmony_ci			(void)scsi_esp_intr(irq, esp_chips[1]);
27162306a36Sopenharmony_ci			got_intr = 1;
27262306a36Sopenharmony_ci		}
27362306a36Sopenharmony_ci	} while (got_intr);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	return IRQ_HANDLED;
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_cistatic struct esp_driver_ops mac_esp_ops = {
27962306a36Sopenharmony_ci	.esp_write8       = mac_esp_write8,
28062306a36Sopenharmony_ci	.esp_read8        = mac_esp_read8,
28162306a36Sopenharmony_ci	.irq_pending      = mac_esp_irq_pending,
28262306a36Sopenharmony_ci	.dma_length_limit = mac_esp_dma_length_limit,
28362306a36Sopenharmony_ci	.reset_dma        = mac_esp_reset_dma,
28462306a36Sopenharmony_ci	.dma_drain        = mac_esp_dma_drain,
28562306a36Sopenharmony_ci	.dma_invalidate   = mac_esp_dma_invalidate,
28662306a36Sopenharmony_ci	.send_dma_cmd     = mac_esp_send_pdma_cmd,
28762306a36Sopenharmony_ci	.dma_error        = mac_esp_dma_error,
28862306a36Sopenharmony_ci};
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistatic int esp_mac_probe(struct platform_device *dev)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	const struct scsi_host_template *tpnt = &scsi_esp_template;
29362306a36Sopenharmony_ci	struct Scsi_Host *host;
29462306a36Sopenharmony_ci	struct esp *esp;
29562306a36Sopenharmony_ci	int err;
29662306a36Sopenharmony_ci	struct mac_esp_priv *mep;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	if (!MACH_IS_MAC)
29962306a36Sopenharmony_ci		return -ENODEV;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	if (dev->id > 1)
30262306a36Sopenharmony_ci		return -ENODEV;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	host = scsi_host_alloc(tpnt, sizeof(struct esp));
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	err = -ENOMEM;
30762306a36Sopenharmony_ci	if (!host)
30862306a36Sopenharmony_ci		goto fail;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	host->max_id = 8;
31162306a36Sopenharmony_ci	host->dma_boundary = PAGE_SIZE - 1;
31262306a36Sopenharmony_ci	esp = shost_priv(host);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	esp->host = host;
31562306a36Sopenharmony_ci	esp->dev = &dev->dev;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	esp->command_block = kzalloc(16, GFP_KERNEL);
31862306a36Sopenharmony_ci	if (!esp->command_block)
31962306a36Sopenharmony_ci		goto fail_unlink;
32062306a36Sopenharmony_ci	esp->command_block_dma = (dma_addr_t)esp->command_block;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	esp->scsi_id = 7;
32362306a36Sopenharmony_ci	host->this_id = esp->scsi_id;
32462306a36Sopenharmony_ci	esp->scsi_id_mask = 1 << esp->scsi_id;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	mep = kzalloc(sizeof(struct mac_esp_priv), GFP_KERNEL);
32762306a36Sopenharmony_ci	if (!mep)
32862306a36Sopenharmony_ci		goto fail_free_command_block;
32962306a36Sopenharmony_ci	mep->esp = esp;
33062306a36Sopenharmony_ci	platform_set_drvdata(dev, mep);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	switch (macintosh_config->scsi_type) {
33362306a36Sopenharmony_ci	case MAC_SCSI_QUADRA:
33462306a36Sopenharmony_ci		esp->cfreq     = 16500000;
33562306a36Sopenharmony_ci		esp->regs      = (void __iomem *)MAC_ESP_REGS_QUADRA;
33662306a36Sopenharmony_ci		mep->pdma_io   = esp->regs + MAC_ESP_PDMA_IO_OFFSET;
33762306a36Sopenharmony_ci		mep->pdma_regs = NULL;
33862306a36Sopenharmony_ci		break;
33962306a36Sopenharmony_ci	case MAC_SCSI_QUADRA2:
34062306a36Sopenharmony_ci		esp->cfreq     = 25000000;
34162306a36Sopenharmony_ci		esp->regs      = (void __iomem *)(MAC_ESP_REGS_QUADRA2 +
34262306a36Sopenharmony_ci				 dev->id * MAC_ESP_REGS_SPACING);
34362306a36Sopenharmony_ci		mep->pdma_io   = esp->regs + MAC_ESP_PDMA_IO_OFFSET;
34462306a36Sopenharmony_ci		mep->pdma_regs = (void __iomem *)(MAC_ESP_PDMA_REG +
34562306a36Sopenharmony_ci				 dev->id * MAC_ESP_PDMA_REG_SPACING);
34662306a36Sopenharmony_ci		nubus_writel(0x1d1, mep->pdma_regs);
34762306a36Sopenharmony_ci		break;
34862306a36Sopenharmony_ci	case MAC_SCSI_QUADRA3:
34962306a36Sopenharmony_ci		/* These quadras have a real DMA controller (the PSC) but we
35062306a36Sopenharmony_ci		 * don't know how to drive it so we must use PIO instead.
35162306a36Sopenharmony_ci		 */
35262306a36Sopenharmony_ci		esp->cfreq     = 25000000;
35362306a36Sopenharmony_ci		esp->regs      = (void __iomem *)MAC_ESP_REGS_QUADRA3;
35462306a36Sopenharmony_ci		mep->pdma_io   = NULL;
35562306a36Sopenharmony_ci		mep->pdma_regs = NULL;
35662306a36Sopenharmony_ci		break;
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci	esp->fifo_reg = esp->regs + ESP_FDATA * 16;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	esp->ops = &mac_esp_ops;
36162306a36Sopenharmony_ci	esp->flags = ESP_FLAG_NO_DMA_MAP;
36262306a36Sopenharmony_ci	if (mep->pdma_io == NULL) {
36362306a36Sopenharmony_ci		printk(KERN_INFO PFX "using PIO for controller %d\n", dev->id);
36462306a36Sopenharmony_ci		esp_write8(0, ESP_TCLOW);
36562306a36Sopenharmony_ci		esp_write8(0, ESP_TCMED);
36662306a36Sopenharmony_ci		esp->flags |= ESP_FLAG_DISABLE_SYNC;
36762306a36Sopenharmony_ci		mac_esp_ops.send_dma_cmd = esp_send_pio_cmd;
36862306a36Sopenharmony_ci	} else {
36962306a36Sopenharmony_ci		printk(KERN_INFO PFX "using PDMA for controller %d\n", dev->id);
37062306a36Sopenharmony_ci	}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	host->irq = IRQ_MAC_SCSI;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	/* The request_irq() call is intended to succeed for the first device
37562306a36Sopenharmony_ci	 * and fail for the second device.
37662306a36Sopenharmony_ci	 */
37762306a36Sopenharmony_ci	err = request_irq(host->irq, mac_scsi_esp_intr, 0, "ESP", NULL);
37862306a36Sopenharmony_ci	spin_lock(&esp_chips_lock);
37962306a36Sopenharmony_ci	if (err < 0 && esp_chips[!dev->id] == NULL) {
38062306a36Sopenharmony_ci		spin_unlock(&esp_chips_lock);
38162306a36Sopenharmony_ci		goto fail_free_priv;
38262306a36Sopenharmony_ci	}
38362306a36Sopenharmony_ci	esp_chips[dev->id] = esp;
38462306a36Sopenharmony_ci	spin_unlock(&esp_chips_lock);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	err = scsi_esp_register(esp);
38762306a36Sopenharmony_ci	if (err)
38862306a36Sopenharmony_ci		goto fail_free_irq;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	return 0;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_cifail_free_irq:
39362306a36Sopenharmony_ci	spin_lock(&esp_chips_lock);
39462306a36Sopenharmony_ci	esp_chips[dev->id] = NULL;
39562306a36Sopenharmony_ci	if (esp_chips[!dev->id] == NULL) {
39662306a36Sopenharmony_ci		spin_unlock(&esp_chips_lock);
39762306a36Sopenharmony_ci		free_irq(host->irq, NULL);
39862306a36Sopenharmony_ci	} else
39962306a36Sopenharmony_ci		spin_unlock(&esp_chips_lock);
40062306a36Sopenharmony_cifail_free_priv:
40162306a36Sopenharmony_ci	kfree(mep);
40262306a36Sopenharmony_cifail_free_command_block:
40362306a36Sopenharmony_ci	kfree(esp->command_block);
40462306a36Sopenharmony_cifail_unlink:
40562306a36Sopenharmony_ci	scsi_host_put(host);
40662306a36Sopenharmony_cifail:
40762306a36Sopenharmony_ci	return err;
40862306a36Sopenharmony_ci}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_cistatic int esp_mac_remove(struct platform_device *dev)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	struct mac_esp_priv *mep = platform_get_drvdata(dev);
41362306a36Sopenharmony_ci	struct esp *esp = mep->esp;
41462306a36Sopenharmony_ci	unsigned int irq = esp->host->irq;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	scsi_esp_unregister(esp);
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	spin_lock(&esp_chips_lock);
41962306a36Sopenharmony_ci	esp_chips[dev->id] = NULL;
42062306a36Sopenharmony_ci	if (esp_chips[!dev->id] == NULL) {
42162306a36Sopenharmony_ci		spin_unlock(&esp_chips_lock);
42262306a36Sopenharmony_ci		free_irq(irq, NULL);
42362306a36Sopenharmony_ci	} else
42462306a36Sopenharmony_ci		spin_unlock(&esp_chips_lock);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	kfree(mep);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	kfree(esp->command_block);
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	scsi_host_put(esp->host);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	return 0;
43362306a36Sopenharmony_ci}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_cistatic struct platform_driver esp_mac_driver = {
43662306a36Sopenharmony_ci	.probe    = esp_mac_probe,
43762306a36Sopenharmony_ci	.remove   = esp_mac_remove,
43862306a36Sopenharmony_ci	.driver   = {
43962306a36Sopenharmony_ci		.name	= DRV_MODULE_NAME,
44062306a36Sopenharmony_ci	},
44162306a36Sopenharmony_ci};
44262306a36Sopenharmony_cimodule_platform_driver(esp_mac_driver);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ciMODULE_DESCRIPTION("Mac ESP SCSI driver");
44562306a36Sopenharmony_ciMODULE_AUTHOR("Finn Thain");
44662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
44762306a36Sopenharmony_ciMODULE_VERSION(DRV_VERSION);
44862306a36Sopenharmony_ciMODULE_ALIAS("platform:" DRV_MODULE_NAME);
449