18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/* mac_esp.c: ESP front-end for Macintosh Quadra systems.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Adapted from jazz_esp.c and the old mac_esp.c.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * The pseudo DMA algorithm is based on the one used in NetBSD.
78c2ecf20Sopenharmony_ci * See sys/arch/mac68k/obio/esp.c for some background information.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Copyright (C) 2007-2008 Finn Thain
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/kernel.h>
138c2ecf20Sopenharmony_ci#include <linux/types.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/init.h>
168c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
178c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
188c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
198c2ecf20Sopenharmony_ci#include <linux/scatterlist.h>
208c2ecf20Sopenharmony_ci#include <linux/delay.h>
218c2ecf20Sopenharmony_ci#include <linux/io.h>
228c2ecf20Sopenharmony_ci#include <linux/nubus.h>
238c2ecf20Sopenharmony_ci#include <linux/slab.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include <asm/irq.h>
268c2ecf20Sopenharmony_ci#include <asm/dma.h>
278c2ecf20Sopenharmony_ci#include <asm/macints.h>
288c2ecf20Sopenharmony_ci#include <asm/macintosh.h>
298c2ecf20Sopenharmony_ci#include <asm/mac_via.h>
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h>
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#include "esp_scsi.h"
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define DRV_MODULE_NAME     "mac_esp"
368c2ecf20Sopenharmony_ci#define PFX                 DRV_MODULE_NAME ": "
378c2ecf20Sopenharmony_ci#define DRV_VERSION         "1.000"
388c2ecf20Sopenharmony_ci#define DRV_MODULE_RELDATE  "Sept 15, 2007"
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#define MAC_ESP_IO_BASE          0x50F00000
418c2ecf20Sopenharmony_ci#define MAC_ESP_REGS_QUADRA      (MAC_ESP_IO_BASE + 0x10000)
428c2ecf20Sopenharmony_ci#define MAC_ESP_REGS_QUADRA2     (MAC_ESP_IO_BASE + 0xF000)
438c2ecf20Sopenharmony_ci#define MAC_ESP_REGS_QUADRA3     (MAC_ESP_IO_BASE + 0x18000)
448c2ecf20Sopenharmony_ci#define MAC_ESP_REGS_SPACING     0x402
458c2ecf20Sopenharmony_ci#define MAC_ESP_PDMA_REG         0xF9800024
468c2ecf20Sopenharmony_ci#define MAC_ESP_PDMA_REG_SPACING 0x4
478c2ecf20Sopenharmony_ci#define MAC_ESP_PDMA_IO_OFFSET   0x100
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci#define esp_read8(REG)		mac_esp_read8(esp, REG)
508c2ecf20Sopenharmony_ci#define esp_write8(VAL, REG)	mac_esp_write8(esp, VAL, REG)
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistruct mac_esp_priv {
538c2ecf20Sopenharmony_ci	struct esp *esp;
548c2ecf20Sopenharmony_ci	void __iomem *pdma_regs;
558c2ecf20Sopenharmony_ci	void __iomem *pdma_io;
568c2ecf20Sopenharmony_ci};
578c2ecf20Sopenharmony_cistatic struct esp *esp_chips[2];
588c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(esp_chips_lock);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci#define MAC_ESP_GET_PRIV(esp) ((struct mac_esp_priv *) \
618c2ecf20Sopenharmony_ci			       dev_get_drvdata((esp)->dev))
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic inline void mac_esp_write8(struct esp *esp, u8 val, unsigned long reg)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	nubus_writeb(val, esp->regs + reg * 16);
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic inline u8 mac_esp_read8(struct esp *esp, unsigned long reg)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	return nubus_readb(esp->regs + reg * 16);
718c2ecf20Sopenharmony_ci}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic void mac_esp_reset_dma(struct esp *esp)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	/* Nothing to do. */
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic void mac_esp_dma_drain(struct esp *esp)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	/* Nothing to do. */
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic void mac_esp_dma_invalidate(struct esp *esp)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	/* Nothing to do. */
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic int mac_esp_dma_error(struct esp *esp)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	return esp->send_cmd_error;
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic inline int mac_esp_wait_for_empty_fifo(struct esp *esp)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	int i = 500000;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	do {
988c2ecf20Sopenharmony_ci		if (!(esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES))
998c2ecf20Sopenharmony_ci			return 0;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci		if (esp_read8(ESP_STATUS) & ESP_STAT_INTR)
1028c2ecf20Sopenharmony_ci			return 1;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci		udelay(2);
1058c2ecf20Sopenharmony_ci	} while (--i);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	printk(KERN_ERR PFX "FIFO is not empty (sreg %02x)\n",
1088c2ecf20Sopenharmony_ci	       esp_read8(ESP_STATUS));
1098c2ecf20Sopenharmony_ci	esp->send_cmd_error = 1;
1108c2ecf20Sopenharmony_ci	return 1;
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic inline int mac_esp_wait_for_dreq(struct esp *esp)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	struct mac_esp_priv *mep = MAC_ESP_GET_PRIV(esp);
1168c2ecf20Sopenharmony_ci	int i = 500000;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	do {
1198c2ecf20Sopenharmony_ci		if (mep->pdma_regs == NULL) {
1208c2ecf20Sopenharmony_ci			if (via2_scsi_drq_pending())
1218c2ecf20Sopenharmony_ci				return 0;
1228c2ecf20Sopenharmony_ci		} else {
1238c2ecf20Sopenharmony_ci			if (nubus_readl(mep->pdma_regs) & 0x200)
1248c2ecf20Sopenharmony_ci				return 0;
1258c2ecf20Sopenharmony_ci		}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci		if (esp_read8(ESP_STATUS) & ESP_STAT_INTR)
1288c2ecf20Sopenharmony_ci			return 1;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci		udelay(2);
1318c2ecf20Sopenharmony_ci	} while (--i);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	printk(KERN_ERR PFX "PDMA timeout (sreg %02x)\n",
1348c2ecf20Sopenharmony_ci	       esp_read8(ESP_STATUS));
1358c2ecf20Sopenharmony_ci	esp->send_cmd_error = 1;
1368c2ecf20Sopenharmony_ci	return 1;
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci#define MAC_ESP_PDMA_LOOP(operands) \
1408c2ecf20Sopenharmony_ci	asm volatile ( \
1418c2ecf20Sopenharmony_ci	     "       tstw %1                   \n" \
1428c2ecf20Sopenharmony_ci	     "       jbeq 20f                  \n" \
1438c2ecf20Sopenharmony_ci	     "1:     movew " operands "        \n" \
1448c2ecf20Sopenharmony_ci	     "2:     movew " operands "        \n" \
1458c2ecf20Sopenharmony_ci	     "3:     movew " operands "        \n" \
1468c2ecf20Sopenharmony_ci	     "4:     movew " operands "        \n" \
1478c2ecf20Sopenharmony_ci	     "5:     movew " operands "        \n" \
1488c2ecf20Sopenharmony_ci	     "6:     movew " operands "        \n" \
1498c2ecf20Sopenharmony_ci	     "7:     movew " operands "        \n" \
1508c2ecf20Sopenharmony_ci	     "8:     movew " operands "        \n" \
1518c2ecf20Sopenharmony_ci	     "9:     movew " operands "        \n" \
1528c2ecf20Sopenharmony_ci	     "10:    movew " operands "        \n" \
1538c2ecf20Sopenharmony_ci	     "11:    movew " operands "        \n" \
1548c2ecf20Sopenharmony_ci	     "12:    movew " operands "        \n" \
1558c2ecf20Sopenharmony_ci	     "13:    movew " operands "        \n" \
1568c2ecf20Sopenharmony_ci	     "14:    movew " operands "        \n" \
1578c2ecf20Sopenharmony_ci	     "15:    movew " operands "        \n" \
1588c2ecf20Sopenharmony_ci	     "16:    movew " operands "        \n" \
1598c2ecf20Sopenharmony_ci	     "       subqw #1,%1               \n" \
1608c2ecf20Sopenharmony_ci	     "       jbne 1b                   \n" \
1618c2ecf20Sopenharmony_ci	     "20:    tstw %2                   \n" \
1628c2ecf20Sopenharmony_ci	     "       jbeq 30f                  \n" \
1638c2ecf20Sopenharmony_ci	     "21:    movew " operands "        \n" \
1648c2ecf20Sopenharmony_ci	     "       subqw #1,%2               \n" \
1658c2ecf20Sopenharmony_ci	     "       jbne 21b                  \n" \
1668c2ecf20Sopenharmony_ci	     "30:    tstw %3                   \n" \
1678c2ecf20Sopenharmony_ci	     "       jbeq 40f                  \n" \
1688c2ecf20Sopenharmony_ci	     "31:    moveb " operands "        \n" \
1698c2ecf20Sopenharmony_ci	     "32:    nop                       \n" \
1708c2ecf20Sopenharmony_ci	     "40:                              \n" \
1718c2ecf20Sopenharmony_ci	     "                                 \n" \
1728c2ecf20Sopenharmony_ci	     "       .section __ex_table,\"a\" \n" \
1738c2ecf20Sopenharmony_ci	     "       .align  4                 \n" \
1748c2ecf20Sopenharmony_ci	     "       .long   1b,40b            \n" \
1758c2ecf20Sopenharmony_ci	     "       .long   2b,40b            \n" \
1768c2ecf20Sopenharmony_ci	     "       .long   3b,40b            \n" \
1778c2ecf20Sopenharmony_ci	     "       .long   4b,40b            \n" \
1788c2ecf20Sopenharmony_ci	     "       .long   5b,40b            \n" \
1798c2ecf20Sopenharmony_ci	     "       .long   6b,40b            \n" \
1808c2ecf20Sopenharmony_ci	     "       .long   7b,40b            \n" \
1818c2ecf20Sopenharmony_ci	     "       .long   8b,40b            \n" \
1828c2ecf20Sopenharmony_ci	     "       .long   9b,40b            \n" \
1838c2ecf20Sopenharmony_ci	     "       .long  10b,40b            \n" \
1848c2ecf20Sopenharmony_ci	     "       .long  11b,40b            \n" \
1858c2ecf20Sopenharmony_ci	     "       .long  12b,40b            \n" \
1868c2ecf20Sopenharmony_ci	     "       .long  13b,40b            \n" \
1878c2ecf20Sopenharmony_ci	     "       .long  14b,40b            \n" \
1888c2ecf20Sopenharmony_ci	     "       .long  15b,40b            \n" \
1898c2ecf20Sopenharmony_ci	     "       .long  16b,40b            \n" \
1908c2ecf20Sopenharmony_ci	     "       .long  21b,40b            \n" \
1918c2ecf20Sopenharmony_ci	     "       .long  31b,40b            \n" \
1928c2ecf20Sopenharmony_ci	     "       .long  32b,40b            \n" \
1938c2ecf20Sopenharmony_ci	     "       .previous                 \n" \
1948c2ecf20Sopenharmony_ci	     : "+a" (addr), "+r" (count32), "+r" (count2) \
1958c2ecf20Sopenharmony_ci	     : "g" (count1), "a" (mep->pdma_io))
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cistatic void mac_esp_send_pdma_cmd(struct esp *esp, u32 addr, u32 esp_count,
1988c2ecf20Sopenharmony_ci				  u32 dma_count, int write, u8 cmd)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	struct mac_esp_priv *mep = MAC_ESP_GET_PRIV(esp);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	esp->send_cmd_error = 0;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	if (!write)
2058c2ecf20Sopenharmony_ci		scsi_esp_cmd(esp, ESP_CMD_FLUSH);
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	esp_write8((esp_count >> 0) & 0xFF, ESP_TCLOW);
2088c2ecf20Sopenharmony_ci	esp_write8((esp_count >> 8) & 0xFF, ESP_TCMED);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	scsi_esp_cmd(esp, cmd);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	do {
2138c2ecf20Sopenharmony_ci		unsigned int count32 = esp_count >> 5;
2148c2ecf20Sopenharmony_ci		unsigned int count2 = (esp_count & 0x1F) >> 1;
2158c2ecf20Sopenharmony_ci		unsigned int count1 = esp_count & 1;
2168c2ecf20Sopenharmony_ci		unsigned int start_addr = addr;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci		if (mac_esp_wait_for_dreq(esp))
2198c2ecf20Sopenharmony_ci			break;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci		if (write) {
2228c2ecf20Sopenharmony_ci			MAC_ESP_PDMA_LOOP("%4@,%0@+");
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci			esp_count -= addr - start_addr;
2258c2ecf20Sopenharmony_ci		} else {
2268c2ecf20Sopenharmony_ci			unsigned int n;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci			MAC_ESP_PDMA_LOOP("%0@+,%4@");
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci			if (mac_esp_wait_for_empty_fifo(esp))
2318c2ecf20Sopenharmony_ci				break;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci			n = (esp_read8(ESP_TCMED) << 8) + esp_read8(ESP_TCLOW);
2348c2ecf20Sopenharmony_ci			addr = start_addr + esp_count - n;
2358c2ecf20Sopenharmony_ci			esp_count = n;
2368c2ecf20Sopenharmony_ci		}
2378c2ecf20Sopenharmony_ci	} while (esp_count);
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_cistatic int mac_esp_irq_pending(struct esp *esp)
2418c2ecf20Sopenharmony_ci{
2428c2ecf20Sopenharmony_ci	if (esp_read8(ESP_STATUS) & ESP_STAT_INTR)
2438c2ecf20Sopenharmony_ci		return 1;
2448c2ecf20Sopenharmony_ci	return 0;
2458c2ecf20Sopenharmony_ci}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_cistatic u32 mac_esp_dma_length_limit(struct esp *esp, u32 dma_addr, u32 dma_len)
2488c2ecf20Sopenharmony_ci{
2498c2ecf20Sopenharmony_ci	return dma_len > 0xFFFF ? 0xFFFF : dma_len;
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_cistatic irqreturn_t mac_scsi_esp_intr(int irq, void *dev_id)
2538c2ecf20Sopenharmony_ci{
2548c2ecf20Sopenharmony_ci	int got_intr;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	/*
2578c2ecf20Sopenharmony_ci	 * This is an edge triggered IRQ, so we have to be careful to
2588c2ecf20Sopenharmony_ci	 * avoid missing a transition when it is shared by two ESP devices.
2598c2ecf20Sopenharmony_ci	 */
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	do {
2628c2ecf20Sopenharmony_ci		got_intr = 0;
2638c2ecf20Sopenharmony_ci		if (esp_chips[0] &&
2648c2ecf20Sopenharmony_ci		    (mac_esp_read8(esp_chips[0], ESP_STATUS) & ESP_STAT_INTR)) {
2658c2ecf20Sopenharmony_ci			(void)scsi_esp_intr(irq, esp_chips[0]);
2668c2ecf20Sopenharmony_ci			got_intr = 1;
2678c2ecf20Sopenharmony_ci		}
2688c2ecf20Sopenharmony_ci		if (esp_chips[1] &&
2698c2ecf20Sopenharmony_ci		    (mac_esp_read8(esp_chips[1], ESP_STATUS) & ESP_STAT_INTR)) {
2708c2ecf20Sopenharmony_ci			(void)scsi_esp_intr(irq, esp_chips[1]);
2718c2ecf20Sopenharmony_ci			got_intr = 1;
2728c2ecf20Sopenharmony_ci		}
2738c2ecf20Sopenharmony_ci	} while (got_intr);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cistatic struct esp_driver_ops mac_esp_ops = {
2798c2ecf20Sopenharmony_ci	.esp_write8       = mac_esp_write8,
2808c2ecf20Sopenharmony_ci	.esp_read8        = mac_esp_read8,
2818c2ecf20Sopenharmony_ci	.irq_pending      = mac_esp_irq_pending,
2828c2ecf20Sopenharmony_ci	.dma_length_limit = mac_esp_dma_length_limit,
2838c2ecf20Sopenharmony_ci	.reset_dma        = mac_esp_reset_dma,
2848c2ecf20Sopenharmony_ci	.dma_drain        = mac_esp_dma_drain,
2858c2ecf20Sopenharmony_ci	.dma_invalidate   = mac_esp_dma_invalidate,
2868c2ecf20Sopenharmony_ci	.send_dma_cmd     = mac_esp_send_pdma_cmd,
2878c2ecf20Sopenharmony_ci	.dma_error        = mac_esp_dma_error,
2888c2ecf20Sopenharmony_ci};
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistatic int esp_mac_probe(struct platform_device *dev)
2918c2ecf20Sopenharmony_ci{
2928c2ecf20Sopenharmony_ci	struct scsi_host_template *tpnt = &scsi_esp_template;
2938c2ecf20Sopenharmony_ci	struct Scsi_Host *host;
2948c2ecf20Sopenharmony_ci	struct esp *esp;
2958c2ecf20Sopenharmony_ci	int err;
2968c2ecf20Sopenharmony_ci	struct mac_esp_priv *mep;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	if (!MACH_IS_MAC)
2998c2ecf20Sopenharmony_ci		return -ENODEV;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	if (dev->id > 1)
3028c2ecf20Sopenharmony_ci		return -ENODEV;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	host = scsi_host_alloc(tpnt, sizeof(struct esp));
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	err = -ENOMEM;
3078c2ecf20Sopenharmony_ci	if (!host)
3088c2ecf20Sopenharmony_ci		goto fail;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	host->max_id = 8;
3118c2ecf20Sopenharmony_ci	host->dma_boundary = PAGE_SIZE - 1;
3128c2ecf20Sopenharmony_ci	esp = shost_priv(host);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	esp->host = host;
3158c2ecf20Sopenharmony_ci	esp->dev = &dev->dev;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	esp->command_block = kzalloc(16, GFP_KERNEL);
3188c2ecf20Sopenharmony_ci	if (!esp->command_block)
3198c2ecf20Sopenharmony_ci		goto fail_unlink;
3208c2ecf20Sopenharmony_ci	esp->command_block_dma = (dma_addr_t)esp->command_block;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	esp->scsi_id = 7;
3238c2ecf20Sopenharmony_ci	host->this_id = esp->scsi_id;
3248c2ecf20Sopenharmony_ci	esp->scsi_id_mask = 1 << esp->scsi_id;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	mep = kzalloc(sizeof(struct mac_esp_priv), GFP_KERNEL);
3278c2ecf20Sopenharmony_ci	if (!mep)
3288c2ecf20Sopenharmony_ci		goto fail_free_command_block;
3298c2ecf20Sopenharmony_ci	mep->esp = esp;
3308c2ecf20Sopenharmony_ci	platform_set_drvdata(dev, mep);
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	switch (macintosh_config->scsi_type) {
3338c2ecf20Sopenharmony_ci	case MAC_SCSI_QUADRA:
3348c2ecf20Sopenharmony_ci		esp->cfreq     = 16500000;
3358c2ecf20Sopenharmony_ci		esp->regs      = (void __iomem *)MAC_ESP_REGS_QUADRA;
3368c2ecf20Sopenharmony_ci		mep->pdma_io   = esp->regs + MAC_ESP_PDMA_IO_OFFSET;
3378c2ecf20Sopenharmony_ci		mep->pdma_regs = NULL;
3388c2ecf20Sopenharmony_ci		break;
3398c2ecf20Sopenharmony_ci	case MAC_SCSI_QUADRA2:
3408c2ecf20Sopenharmony_ci		esp->cfreq     = 25000000;
3418c2ecf20Sopenharmony_ci		esp->regs      = (void __iomem *)(MAC_ESP_REGS_QUADRA2 +
3428c2ecf20Sopenharmony_ci				 dev->id * MAC_ESP_REGS_SPACING);
3438c2ecf20Sopenharmony_ci		mep->pdma_io   = esp->regs + MAC_ESP_PDMA_IO_OFFSET;
3448c2ecf20Sopenharmony_ci		mep->pdma_regs = (void __iomem *)(MAC_ESP_PDMA_REG +
3458c2ecf20Sopenharmony_ci				 dev->id * MAC_ESP_PDMA_REG_SPACING);
3468c2ecf20Sopenharmony_ci		nubus_writel(0x1d1, mep->pdma_regs);
3478c2ecf20Sopenharmony_ci		break;
3488c2ecf20Sopenharmony_ci	case MAC_SCSI_QUADRA3:
3498c2ecf20Sopenharmony_ci		/* These quadras have a real DMA controller (the PSC) but we
3508c2ecf20Sopenharmony_ci		 * don't know how to drive it so we must use PIO instead.
3518c2ecf20Sopenharmony_ci		 */
3528c2ecf20Sopenharmony_ci		esp->cfreq     = 25000000;
3538c2ecf20Sopenharmony_ci		esp->regs      = (void __iomem *)MAC_ESP_REGS_QUADRA3;
3548c2ecf20Sopenharmony_ci		mep->pdma_io   = NULL;
3558c2ecf20Sopenharmony_ci		mep->pdma_regs = NULL;
3568c2ecf20Sopenharmony_ci		break;
3578c2ecf20Sopenharmony_ci	}
3588c2ecf20Sopenharmony_ci	esp->fifo_reg = esp->regs + ESP_FDATA * 16;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	esp->ops = &mac_esp_ops;
3618c2ecf20Sopenharmony_ci	esp->flags = ESP_FLAG_NO_DMA_MAP;
3628c2ecf20Sopenharmony_ci	if (mep->pdma_io == NULL) {
3638c2ecf20Sopenharmony_ci		printk(KERN_INFO PFX "using PIO for controller %d\n", dev->id);
3648c2ecf20Sopenharmony_ci		esp_write8(0, ESP_TCLOW);
3658c2ecf20Sopenharmony_ci		esp_write8(0, ESP_TCMED);
3668c2ecf20Sopenharmony_ci		esp->flags |= ESP_FLAG_DISABLE_SYNC;
3678c2ecf20Sopenharmony_ci		mac_esp_ops.send_dma_cmd = esp_send_pio_cmd;
3688c2ecf20Sopenharmony_ci	} else {
3698c2ecf20Sopenharmony_ci		printk(KERN_INFO PFX "using PDMA for controller %d\n", dev->id);
3708c2ecf20Sopenharmony_ci	}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	host->irq = IRQ_MAC_SCSI;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	/* The request_irq() call is intended to succeed for the first device
3758c2ecf20Sopenharmony_ci	 * and fail for the second device.
3768c2ecf20Sopenharmony_ci	 */
3778c2ecf20Sopenharmony_ci	err = request_irq(host->irq, mac_scsi_esp_intr, 0, "ESP", NULL);
3788c2ecf20Sopenharmony_ci	spin_lock(&esp_chips_lock);
3798c2ecf20Sopenharmony_ci	if (err < 0 && esp_chips[!dev->id] == NULL) {
3808c2ecf20Sopenharmony_ci		spin_unlock(&esp_chips_lock);
3818c2ecf20Sopenharmony_ci		goto fail_free_priv;
3828c2ecf20Sopenharmony_ci	}
3838c2ecf20Sopenharmony_ci	esp_chips[dev->id] = esp;
3848c2ecf20Sopenharmony_ci	spin_unlock(&esp_chips_lock);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	err = scsi_esp_register(esp);
3878c2ecf20Sopenharmony_ci	if (err)
3888c2ecf20Sopenharmony_ci		goto fail_free_irq;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	return 0;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_cifail_free_irq:
3938c2ecf20Sopenharmony_ci	spin_lock(&esp_chips_lock);
3948c2ecf20Sopenharmony_ci	esp_chips[dev->id] = NULL;
3958c2ecf20Sopenharmony_ci	if (esp_chips[!dev->id] == NULL) {
3968c2ecf20Sopenharmony_ci		spin_unlock(&esp_chips_lock);
3978c2ecf20Sopenharmony_ci		free_irq(host->irq, NULL);
3988c2ecf20Sopenharmony_ci	} else
3998c2ecf20Sopenharmony_ci		spin_unlock(&esp_chips_lock);
4008c2ecf20Sopenharmony_cifail_free_priv:
4018c2ecf20Sopenharmony_ci	kfree(mep);
4028c2ecf20Sopenharmony_cifail_free_command_block:
4038c2ecf20Sopenharmony_ci	kfree(esp->command_block);
4048c2ecf20Sopenharmony_cifail_unlink:
4058c2ecf20Sopenharmony_ci	scsi_host_put(host);
4068c2ecf20Sopenharmony_cifail:
4078c2ecf20Sopenharmony_ci	return err;
4088c2ecf20Sopenharmony_ci}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_cistatic int esp_mac_remove(struct platform_device *dev)
4118c2ecf20Sopenharmony_ci{
4128c2ecf20Sopenharmony_ci	struct mac_esp_priv *mep = platform_get_drvdata(dev);
4138c2ecf20Sopenharmony_ci	struct esp *esp = mep->esp;
4148c2ecf20Sopenharmony_ci	unsigned int irq = esp->host->irq;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	scsi_esp_unregister(esp);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	spin_lock(&esp_chips_lock);
4198c2ecf20Sopenharmony_ci	esp_chips[dev->id] = NULL;
4208c2ecf20Sopenharmony_ci	if (esp_chips[!dev->id] == NULL) {
4218c2ecf20Sopenharmony_ci		spin_unlock(&esp_chips_lock);
4228c2ecf20Sopenharmony_ci		free_irq(irq, NULL);
4238c2ecf20Sopenharmony_ci	} else
4248c2ecf20Sopenharmony_ci		spin_unlock(&esp_chips_lock);
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	kfree(mep);
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	kfree(esp->command_block);
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	scsi_host_put(esp->host);
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	return 0;
4338c2ecf20Sopenharmony_ci}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cistatic struct platform_driver esp_mac_driver = {
4368c2ecf20Sopenharmony_ci	.probe    = esp_mac_probe,
4378c2ecf20Sopenharmony_ci	.remove   = esp_mac_remove,
4388c2ecf20Sopenharmony_ci	.driver   = {
4398c2ecf20Sopenharmony_ci		.name	= DRV_MODULE_NAME,
4408c2ecf20Sopenharmony_ci	},
4418c2ecf20Sopenharmony_ci};
4428c2ecf20Sopenharmony_cimodule_platform_driver(esp_mac_driver);
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Mac ESP SCSI driver");
4458c2ecf20Sopenharmony_ciMODULE_AUTHOR("Finn Thain");
4468c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
4478c2ecf20Sopenharmony_ciMODULE_VERSION(DRV_VERSION);
4488c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:" DRV_MODULE_NAME);
449