xref: /openbmc/linux/drivers/scsi/sun_esp.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2cd9ad58dSDavid S. Miller /* sun_esp.c: ESP front-end for Sparc SBUS systems.
3cd9ad58dSDavid S. Miller  *
4334ae614SDavid S. Miller  * Copyright (C) 2007, 2008 David S. Miller (davem@davemloft.net)
5cd9ad58dSDavid S. Miller  */
6cd9ad58dSDavid S. Miller 
7cd9ad58dSDavid S. Miller #include <linux/kernel.h>
8cd9ad58dSDavid S. Miller #include <linux/types.h>
96025dfe5SDavid S. Miller #include <linux/delay.h>
10cd9ad58dSDavid S. Miller #include <linux/module.h>
1127ac792cSAndrea Righi #include <linux/mm.h>
12cd9ad58dSDavid S. Miller #include <linux/init.h>
13738f2b7bSDavid S. Miller #include <linux/dma-mapping.h>
1405bb5e93SDavid S. Miller #include <linux/of.h>
15*109a2a48SRob Herring #include <linux/of_platform.h>
16*109a2a48SRob Herring #include <linux/platform_device.h>
175a0e3ad6STejun Heo #include <linux/gfp.h>
18cd9ad58dSDavid S. Miller 
19cd9ad58dSDavid S. Miller #include <asm/irq.h>
20cd9ad58dSDavid S. Miller #include <asm/io.h>
21cd9ad58dSDavid S. Miller #include <asm/dma.h>
22cd9ad58dSDavid S. Miller 
23cd9ad58dSDavid S. Miller #include <scsi/scsi_host.h>
24cd9ad58dSDavid S. Miller 
25cd9ad58dSDavid S. Miller #include "esp_scsi.h"
26cd9ad58dSDavid S. Miller 
27cd9ad58dSDavid S. Miller #define DRV_MODULE_NAME		"sun_esp"
28cd9ad58dSDavid S. Miller #define PFX DRV_MODULE_NAME	": "
2905bb5e93SDavid S. Miller #define DRV_VERSION		"1.100"
3005bb5e93SDavid S. Miller #define DRV_MODULE_RELDATE	"August 27, 2008"
31cd9ad58dSDavid S. Miller 
32cd9ad58dSDavid S. Miller #define dma_read32(REG) \
33cd9ad58dSDavid S. Miller 	sbus_readl(esp->dma_regs + (REG))
34cd9ad58dSDavid S. Miller #define dma_write32(VAL, REG) \
35cd9ad58dSDavid S. Miller 	sbus_writel((VAL), esp->dma_regs + (REG))
36cd9ad58dSDavid S. Miller 
37334ae614SDavid S. Miller /* DVMA chip revisions */
38334ae614SDavid S. Miller enum dvma_rev {
39334ae614SDavid S. Miller 	dvmarev0,
40334ae614SDavid S. Miller 	dvmaesc1,
41334ae614SDavid S. Miller 	dvmarev1,
42334ae614SDavid S. Miller 	dvmarev2,
43334ae614SDavid S. Miller 	dvmarev3,
44334ae614SDavid S. Miller 	dvmarevplus,
45334ae614SDavid S. Miller 	dvmahme
46334ae614SDavid S. Miller };
47334ae614SDavid S. Miller 
esp_sbus_setup_dma(struct esp * esp,struct platform_device * dma_of)486f039790SGreg Kroah-Hartman static int esp_sbus_setup_dma(struct esp *esp, struct platform_device *dma_of)
49cd9ad58dSDavid S. Miller {
50334ae614SDavid S. Miller 	esp->dma = dma_of;
51cd9ad58dSDavid S. Miller 
52334ae614SDavid S. Miller 	esp->dma_regs = of_ioremap(&dma_of->resource[0], 0,
53334ae614SDavid S. Miller 				   resource_size(&dma_of->resource[0]),
54334ae614SDavid S. Miller 				   "espdma");
55334ae614SDavid S. Miller 	if (!esp->dma_regs)
56334ae614SDavid S. Miller 		return -ENOMEM;
57334ae614SDavid S. Miller 
58334ae614SDavid S. Miller 	switch (dma_read32(DMA_CSR) & DMA_DEVICE_ID) {
59334ae614SDavid S. Miller 	case DMA_VERS0:
60334ae614SDavid S. Miller 		esp->dmarev = dvmarev0;
61334ae614SDavid S. Miller 		break;
62334ae614SDavid S. Miller 	case DMA_ESCV1:
63334ae614SDavid S. Miller 		esp->dmarev = dvmaesc1;
64334ae614SDavid S. Miller 		break;
65334ae614SDavid S. Miller 	case DMA_VERS1:
66334ae614SDavid S. Miller 		esp->dmarev = dvmarev1;
67334ae614SDavid S. Miller 		break;
68334ae614SDavid S. Miller 	case DMA_VERS2:
69334ae614SDavid S. Miller 		esp->dmarev = dvmarev2;
70334ae614SDavid S. Miller 		break;
71334ae614SDavid S. Miller 	case DMA_VERHME:
72334ae614SDavid S. Miller 		esp->dmarev = dvmahme;
73334ae614SDavid S. Miller 		break;
74334ae614SDavid S. Miller 	case DMA_VERSPLUS:
75334ae614SDavid S. Miller 		esp->dmarev = dvmarevplus;
76cd9ad58dSDavid S. Miller 		break;
77cd9ad58dSDavid S. Miller 	}
78cd9ad58dSDavid S. Miller 
79cd9ad58dSDavid S. Miller 	return 0;
80cd9ad58dSDavid S. Miller 
81cd9ad58dSDavid S. Miller }
82cd9ad58dSDavid S. Miller 
esp_sbus_map_regs(struct esp * esp,int hme)836f039790SGreg Kroah-Hartman static int esp_sbus_map_regs(struct esp *esp, int hme)
84cd9ad58dSDavid S. Miller {
8598cda6a2SChristoph Hellwig 	struct platform_device *op = to_platform_device(esp->dev);
86cd9ad58dSDavid S. Miller 	struct resource *res;
87cd9ad58dSDavid S. Miller 
88cd9ad58dSDavid S. Miller 	/* On HME, two reg sets exist, first is DVMA,
89cd9ad58dSDavid S. Miller 	 * second is ESP registers.
90cd9ad58dSDavid S. Miller 	 */
91cd9ad58dSDavid S. Miller 	if (hme)
9205bb5e93SDavid S. Miller 		res = &op->resource[1];
93cd9ad58dSDavid S. Miller 	else
9405bb5e93SDavid S. Miller 		res = &op->resource[0];
95cd9ad58dSDavid S. Miller 
9605bb5e93SDavid S. Miller 	esp->regs = of_ioremap(res, 0, SBUS_ESP_REG_SIZE, "ESP");
97cd9ad58dSDavid S. Miller 	if (!esp->regs)
98cd9ad58dSDavid S. Miller 		return -ENOMEM;
99cd9ad58dSDavid S. Miller 
100cd9ad58dSDavid S. Miller 	return 0;
101cd9ad58dSDavid S. Miller }
102cd9ad58dSDavid S. Miller 
esp_sbus_map_command_block(struct esp * esp)1036f039790SGreg Kroah-Hartman static int esp_sbus_map_command_block(struct esp *esp)
104cd9ad58dSDavid S. Miller {
10598cda6a2SChristoph Hellwig 	esp->command_block = dma_alloc_coherent(esp->dev, 16,
106738f2b7bSDavid S. Miller 						&esp->command_block_dma,
10710c0cd38SChristoph Hellwig 						GFP_KERNEL);
108cd9ad58dSDavid S. Miller 	if (!esp->command_block)
109cd9ad58dSDavid S. Miller 		return -ENOMEM;
110cd9ad58dSDavid S. Miller 	return 0;
111cd9ad58dSDavid S. Miller }
112cd9ad58dSDavid S. Miller 
esp_sbus_register_irq(struct esp * esp)1136f039790SGreg Kroah-Hartman static int esp_sbus_register_irq(struct esp *esp)
114cd9ad58dSDavid S. Miller {
115cd9ad58dSDavid S. Miller 	struct Scsi_Host *host = esp->host;
11698cda6a2SChristoph Hellwig 	struct platform_device *op = to_platform_device(esp->dev);
117cd9ad58dSDavid S. Miller 
1181636f8acSGrant Likely 	host->irq = op->archdata.irqs[0];
119cd9ad58dSDavid S. Miller 	return request_irq(host->irq, scsi_esp_intr, IRQF_SHARED, "ESP", esp);
120cd9ad58dSDavid S. Miller }
121cd9ad58dSDavid S. Miller 
esp_get_scsi_id(struct esp * esp,struct platform_device * espdma)1226f039790SGreg Kroah-Hartman static void esp_get_scsi_id(struct esp *esp, struct platform_device *espdma)
123cd9ad58dSDavid S. Miller {
12498cda6a2SChristoph Hellwig 	struct platform_device *op = to_platform_device(esp->dev);
12505bb5e93SDavid S. Miller 	struct device_node *dp;
126cd9ad58dSDavid S. Miller 
12761c7a080SGrant Likely 	dp = op->dev.of_node;
128cd9ad58dSDavid S. Miller 	esp->scsi_id = of_getintprop_default(dp, "initiator-id", 0xff);
129cd9ad58dSDavid S. Miller 	if (esp->scsi_id != 0xff)
130cd9ad58dSDavid S. Miller 		goto done;
131cd9ad58dSDavid S. Miller 
132cd9ad58dSDavid S. Miller 	esp->scsi_id = of_getintprop_default(dp, "scsi-initiator-id", 0xff);
133cd9ad58dSDavid S. Miller 	if (esp->scsi_id != 0xff)
134cd9ad58dSDavid S. Miller 		goto done;
135cd9ad58dSDavid S. Miller 
13661c7a080SGrant Likely 	esp->scsi_id = of_getintprop_default(espdma->dev.of_node,
137cd9ad58dSDavid S. Miller 					     "scsi-initiator-id", 7);
138cd9ad58dSDavid S. Miller 
139cd9ad58dSDavid S. Miller done:
140cd9ad58dSDavid S. Miller 	esp->host->this_id = esp->scsi_id;
141cd9ad58dSDavid S. Miller 	esp->scsi_id_mask = (1 << esp->scsi_id);
142cd9ad58dSDavid S. Miller }
143cd9ad58dSDavid S. Miller 
esp_get_differential(struct esp * esp)1446f039790SGreg Kroah-Hartman static void esp_get_differential(struct esp *esp)
145cd9ad58dSDavid S. Miller {
14698cda6a2SChristoph Hellwig 	struct platform_device *op = to_platform_device(esp->dev);
14705bb5e93SDavid S. Miller 	struct device_node *dp;
148cd9ad58dSDavid S. Miller 
14961c7a080SGrant Likely 	dp = op->dev.of_node;
15006f8e071SRob Herring 	if (of_property_read_bool(dp, "differential"))
151cd9ad58dSDavid S. Miller 		esp->flags |= ESP_FLAG_DIFFERENTIAL;
152cd9ad58dSDavid S. Miller 	else
153cd9ad58dSDavid S. Miller 		esp->flags &= ~ESP_FLAG_DIFFERENTIAL;
154cd9ad58dSDavid S. Miller }
155cd9ad58dSDavid S. Miller 
esp_get_clock_params(struct esp * esp)1566f039790SGreg Kroah-Hartman static void esp_get_clock_params(struct esp *esp)
157cd9ad58dSDavid S. Miller {
15898cda6a2SChristoph Hellwig 	struct platform_device *op = to_platform_device(esp->dev);
15905bb5e93SDavid S. Miller 	struct device_node *bus_dp, *dp;
160cd9ad58dSDavid S. Miller 	int fmhz;
161cd9ad58dSDavid S. Miller 
16261c7a080SGrant Likely 	dp = op->dev.of_node;
16305bb5e93SDavid S. Miller 	bus_dp = dp->parent;
164cd9ad58dSDavid S. Miller 
165cd9ad58dSDavid S. Miller 	fmhz = of_getintprop_default(dp, "clock-frequency", 0);
166cd9ad58dSDavid S. Miller 	if (fmhz == 0)
16705bb5e93SDavid S. Miller 		fmhz = of_getintprop_default(bus_dp, "clock-frequency", 0);
168cd9ad58dSDavid S. Miller 
169cd9ad58dSDavid S. Miller 	esp->cfreq = fmhz;
170cd9ad58dSDavid S. Miller }
171cd9ad58dSDavid S. Miller 
esp_get_bursts(struct esp * esp,struct platform_device * dma_of)1726f039790SGreg Kroah-Hartman static void esp_get_bursts(struct esp *esp, struct platform_device *dma_of)
173cd9ad58dSDavid S. Miller {
17461c7a080SGrant Likely 	struct device_node *dma_dp = dma_of->dev.of_node;
17598cda6a2SChristoph Hellwig 	struct platform_device *op = to_platform_device(esp->dev);
176334ae614SDavid S. Miller 	struct device_node *dp;
177334ae614SDavid S. Miller 	u8 bursts, val;
178cd9ad58dSDavid S. Miller 
17961c7a080SGrant Likely 	dp = op->dev.of_node;
180cd9ad58dSDavid S. Miller 	bursts = of_getintprop_default(dp, "burst-sizes", 0xff);
181334ae614SDavid S. Miller 	val = of_getintprop_default(dma_dp, "burst-sizes", 0xff);
182cd9ad58dSDavid S. Miller 	if (val != 0xff)
183cd9ad58dSDavid S. Miller 		bursts &= val;
184cd9ad58dSDavid S. Miller 
18505bb5e93SDavid S. Miller 	val = of_getintprop_default(dma_dp->parent, "burst-sizes", 0xff);
186cd9ad58dSDavid S. Miller 	if (val != 0xff)
187cd9ad58dSDavid S. Miller 		bursts &= val;
188cd9ad58dSDavid S. Miller 
189cd9ad58dSDavid S. Miller 	if (bursts == 0xff ||
190cd9ad58dSDavid S. Miller 	    (bursts & DMA_BURST16) == 0 ||
191cd9ad58dSDavid S. Miller 	    (bursts & DMA_BURST32) == 0)
192cd9ad58dSDavid S. Miller 		bursts = (DMA_BURST32 - 1);
193cd9ad58dSDavid S. Miller 
194cd9ad58dSDavid S. Miller 	esp->bursts = bursts;
195cd9ad58dSDavid S. Miller }
196cd9ad58dSDavid S. Miller 
esp_sbus_get_props(struct esp * esp,struct platform_device * espdma)1976f039790SGreg Kroah-Hartman static void esp_sbus_get_props(struct esp *esp, struct platform_device *espdma)
198cd9ad58dSDavid S. Miller {
19905bb5e93SDavid S. Miller 	esp_get_scsi_id(esp, espdma);
200cd9ad58dSDavid S. Miller 	esp_get_differential(esp);
201cd9ad58dSDavid S. Miller 	esp_get_clock_params(esp);
202cd9ad58dSDavid S. Miller 	esp_get_bursts(esp, espdma);
203cd9ad58dSDavid S. Miller }
204cd9ad58dSDavid S. Miller 
sbus_esp_write8(struct esp * esp,u8 val,unsigned long reg)205cd9ad58dSDavid S. Miller static void sbus_esp_write8(struct esp *esp, u8 val, unsigned long reg)
206cd9ad58dSDavid S. Miller {
207cd9ad58dSDavid S. Miller 	sbus_writeb(val, esp->regs + (reg * 4UL));
208cd9ad58dSDavid S. Miller }
209cd9ad58dSDavid S. Miller 
sbus_esp_read8(struct esp * esp,unsigned long reg)210cd9ad58dSDavid S. Miller static u8 sbus_esp_read8(struct esp *esp, unsigned long reg)
211cd9ad58dSDavid S. Miller {
212cd9ad58dSDavid S. Miller 	return sbus_readb(esp->regs + (reg * 4UL));
213cd9ad58dSDavid S. Miller }
214cd9ad58dSDavid S. Miller 
sbus_esp_irq_pending(struct esp * esp)215cd9ad58dSDavid S. Miller static int sbus_esp_irq_pending(struct esp *esp)
216cd9ad58dSDavid S. Miller {
217cd9ad58dSDavid S. Miller 	if (dma_read32(DMA_CSR) & (DMA_HNDL_INTR | DMA_HNDL_ERROR))
218cd9ad58dSDavid S. Miller 		return 1;
219cd9ad58dSDavid S. Miller 	return 0;
220cd9ad58dSDavid S. Miller }
221cd9ad58dSDavid S. Miller 
sbus_esp_reset_dma(struct esp * esp)222cd9ad58dSDavid S. Miller static void sbus_esp_reset_dma(struct esp *esp)
223cd9ad58dSDavid S. Miller {
224cd9ad58dSDavid S. Miller 	int can_do_burst16, can_do_burst32, can_do_burst64;
225cd9ad58dSDavid S. Miller 	int can_do_sbus64, lim;
22698cda6a2SChristoph Hellwig 	struct platform_device *op = to_platform_device(esp->dev);
227cd9ad58dSDavid S. Miller 	u32 val;
228cd9ad58dSDavid S. Miller 
229cd9ad58dSDavid S. Miller 	can_do_burst16 = (esp->bursts & DMA_BURST16) != 0;
230cd9ad58dSDavid S. Miller 	can_do_burst32 = (esp->bursts & DMA_BURST32) != 0;
231cd9ad58dSDavid S. Miller 	can_do_burst64 = 0;
232cd9ad58dSDavid S. Miller 	can_do_sbus64 = 0;
23363237eebSDavid S. Miller 	if (sbus_can_dma_64bit())
234cd9ad58dSDavid S. Miller 		can_do_sbus64 = 1;
23563237eebSDavid S. Miller 	if (sbus_can_burst64())
236cd9ad58dSDavid S. Miller 		can_do_burst64 = (esp->bursts & DMA_BURST64) != 0;
237cd9ad58dSDavid S. Miller 
238cd9ad58dSDavid S. Miller 	/* Put the DVMA into a known state. */
239334ae614SDavid S. Miller 	if (esp->dmarev != dvmahme) {
240cd9ad58dSDavid S. Miller 		val = dma_read32(DMA_CSR);
241cd9ad58dSDavid S. Miller 		dma_write32(val | DMA_RST_SCSI, DMA_CSR);
242cd9ad58dSDavid S. Miller 		dma_write32(val & ~DMA_RST_SCSI, DMA_CSR);
243cd9ad58dSDavid S. Miller 	}
244334ae614SDavid S. Miller 	switch (esp->dmarev) {
245cd9ad58dSDavid S. Miller 	case dvmahme:
246cd9ad58dSDavid S. Miller 		dma_write32(DMA_RESET_FAS366, DMA_CSR);
247cd9ad58dSDavid S. Miller 		dma_write32(DMA_RST_SCSI, DMA_CSR);
248cd9ad58dSDavid S. Miller 
249cd9ad58dSDavid S. Miller 		esp->prev_hme_dmacsr = (DMA_PARITY_OFF | DMA_2CLKS |
250cd9ad58dSDavid S. Miller 					DMA_SCSI_DISAB | DMA_INT_ENAB);
251cd9ad58dSDavid S. Miller 
252cd9ad58dSDavid S. Miller 		esp->prev_hme_dmacsr &= ~(DMA_ENABLE | DMA_ST_WRITE |
253cd9ad58dSDavid S. Miller 					  DMA_BRST_SZ);
254cd9ad58dSDavid S. Miller 
255cd9ad58dSDavid S. Miller 		if (can_do_burst64)
256cd9ad58dSDavid S. Miller 			esp->prev_hme_dmacsr |= DMA_BRST64;
257cd9ad58dSDavid S. Miller 		else if (can_do_burst32)
258cd9ad58dSDavid S. Miller 			esp->prev_hme_dmacsr |= DMA_BRST32;
259cd9ad58dSDavid S. Miller 
260cd9ad58dSDavid S. Miller 		if (can_do_sbus64) {
261cd9ad58dSDavid S. Miller 			esp->prev_hme_dmacsr |= DMA_SCSI_SBUS64;
26205bb5e93SDavid S. Miller 			sbus_set_sbus64(&op->dev, esp->bursts);
263cd9ad58dSDavid S. Miller 		}
264cd9ad58dSDavid S. Miller 
265cd9ad58dSDavid S. Miller 		lim = 1000;
266cd9ad58dSDavid S. Miller 		while (dma_read32(DMA_CSR) & DMA_PEND_READ) {
267cd9ad58dSDavid S. Miller 			if (--lim == 0) {
268cd9ad58dSDavid S. Miller 				printk(KERN_ALERT PFX "esp%d: DMA_PEND_READ "
269cd9ad58dSDavid S. Miller 				       "will not clear!\n",
270cd9ad58dSDavid S. Miller 				       esp->host->unique_id);
271cd9ad58dSDavid S. Miller 				break;
272cd9ad58dSDavid S. Miller 			}
273cd9ad58dSDavid S. Miller 			udelay(1);
274cd9ad58dSDavid S. Miller 		}
275cd9ad58dSDavid S. Miller 
276cd9ad58dSDavid S. Miller 		dma_write32(0, DMA_CSR);
277cd9ad58dSDavid S. Miller 		dma_write32(esp->prev_hme_dmacsr, DMA_CSR);
278cd9ad58dSDavid S. Miller 
279cd9ad58dSDavid S. Miller 		dma_write32(0, DMA_ADDR);
280cd9ad58dSDavid S. Miller 		break;
281cd9ad58dSDavid S. Miller 
282cd9ad58dSDavid S. Miller 	case dvmarev2:
283cd9ad58dSDavid S. Miller 		if (esp->rev != ESP100) {
284cd9ad58dSDavid S. Miller 			val = dma_read32(DMA_CSR);
285cd9ad58dSDavid S. Miller 			dma_write32(val | DMA_3CLKS, DMA_CSR);
286cd9ad58dSDavid S. Miller 		}
287cd9ad58dSDavid S. Miller 		break;
288cd9ad58dSDavid S. Miller 
289cd9ad58dSDavid S. Miller 	case dvmarev3:
290cd9ad58dSDavid S. Miller 		val = dma_read32(DMA_CSR);
291cd9ad58dSDavid S. Miller 		val &= ~DMA_3CLKS;
292cd9ad58dSDavid S. Miller 		val |= DMA_2CLKS;
293cd9ad58dSDavid S. Miller 		if (can_do_burst32) {
294cd9ad58dSDavid S. Miller 			val &= ~DMA_BRST_SZ;
295cd9ad58dSDavid S. Miller 			val |= DMA_BRST32;
296cd9ad58dSDavid S. Miller 		}
297cd9ad58dSDavid S. Miller 		dma_write32(val, DMA_CSR);
298cd9ad58dSDavid S. Miller 		break;
299cd9ad58dSDavid S. Miller 
300cd9ad58dSDavid S. Miller 	case dvmaesc1:
301cd9ad58dSDavid S. Miller 		val = dma_read32(DMA_CSR);
302cd9ad58dSDavid S. Miller 		val |= DMA_ADD_ENABLE;
303cd9ad58dSDavid S. Miller 		val &= ~DMA_BCNT_ENAB;
304cd9ad58dSDavid S. Miller 		if (!can_do_burst32 && can_do_burst16) {
305cd9ad58dSDavid S. Miller 			val |= DMA_ESC_BURST;
306cd9ad58dSDavid S. Miller 		} else {
307cd9ad58dSDavid S. Miller 			val &= ~(DMA_ESC_BURST);
308cd9ad58dSDavid S. Miller 		}
309cd9ad58dSDavid S. Miller 		dma_write32(val, DMA_CSR);
310cd9ad58dSDavid S. Miller 		break;
311cd9ad58dSDavid S. Miller 
312cd9ad58dSDavid S. Miller 	default:
313cd9ad58dSDavid S. Miller 		break;
314cd9ad58dSDavid S. Miller 	}
315cd9ad58dSDavid S. Miller 
316cd9ad58dSDavid S. Miller 	/* Enable interrupts.  */
317cd9ad58dSDavid S. Miller 	val = dma_read32(DMA_CSR);
318cd9ad58dSDavid S. Miller 	dma_write32(val | DMA_INT_ENAB, DMA_CSR);
319cd9ad58dSDavid S. Miller }
320cd9ad58dSDavid S. Miller 
sbus_esp_dma_drain(struct esp * esp)321cd9ad58dSDavid S. Miller static void sbus_esp_dma_drain(struct esp *esp)
322cd9ad58dSDavid S. Miller {
323cd9ad58dSDavid S. Miller 	u32 csr;
324cd9ad58dSDavid S. Miller 	int lim;
325cd9ad58dSDavid S. Miller 
326334ae614SDavid S. Miller 	if (esp->dmarev == dvmahme)
327cd9ad58dSDavid S. Miller 		return;
328cd9ad58dSDavid S. Miller 
329cd9ad58dSDavid S. Miller 	csr = dma_read32(DMA_CSR);
330cd9ad58dSDavid S. Miller 	if (!(csr & DMA_FIFO_ISDRAIN))
331cd9ad58dSDavid S. Miller 		return;
332cd9ad58dSDavid S. Miller 
333334ae614SDavid S. Miller 	if (esp->dmarev != dvmarev3 && esp->dmarev != dvmaesc1)
334cd9ad58dSDavid S. Miller 		dma_write32(csr | DMA_FIFO_STDRAIN, DMA_CSR);
335cd9ad58dSDavid S. Miller 
336cd9ad58dSDavid S. Miller 	lim = 1000;
337cd9ad58dSDavid S. Miller 	while (dma_read32(DMA_CSR) & DMA_FIFO_ISDRAIN) {
338cd9ad58dSDavid S. Miller 		if (--lim == 0) {
339cd9ad58dSDavid S. Miller 			printk(KERN_ALERT PFX "esp%d: DMA will not drain!\n",
340cd9ad58dSDavid S. Miller 			       esp->host->unique_id);
341cd9ad58dSDavid S. Miller 			break;
342cd9ad58dSDavid S. Miller 		}
343cd9ad58dSDavid S. Miller 		udelay(1);
344cd9ad58dSDavid S. Miller 	}
345cd9ad58dSDavid S. Miller }
346cd9ad58dSDavid S. Miller 
sbus_esp_dma_invalidate(struct esp * esp)347cd9ad58dSDavid S. Miller static void sbus_esp_dma_invalidate(struct esp *esp)
348cd9ad58dSDavid S. Miller {
349334ae614SDavid S. Miller 	if (esp->dmarev == dvmahme) {
350cd9ad58dSDavid S. Miller 		dma_write32(DMA_RST_SCSI, DMA_CSR);
351cd9ad58dSDavid S. Miller 
352cd9ad58dSDavid S. Miller 		esp->prev_hme_dmacsr = ((esp->prev_hme_dmacsr |
353cd9ad58dSDavid S. Miller 					 (DMA_PARITY_OFF | DMA_2CLKS |
354cd9ad58dSDavid S. Miller 					  DMA_SCSI_DISAB | DMA_INT_ENAB)) &
355cd9ad58dSDavid S. Miller 					~(DMA_ST_WRITE | DMA_ENABLE));
356cd9ad58dSDavid S. Miller 
357cd9ad58dSDavid S. Miller 		dma_write32(0, DMA_CSR);
358cd9ad58dSDavid S. Miller 		dma_write32(esp->prev_hme_dmacsr, DMA_CSR);
359cd9ad58dSDavid S. Miller 
360cd9ad58dSDavid S. Miller 		/* This is necessary to avoid having the SCSI channel
361cd9ad58dSDavid S. Miller 		 * engine lock up on us.
362cd9ad58dSDavid S. Miller 		 */
363cd9ad58dSDavid S. Miller 		dma_write32(0, DMA_ADDR);
364cd9ad58dSDavid S. Miller 	} else {
365cd9ad58dSDavid S. Miller 		u32 val;
366cd9ad58dSDavid S. Miller 		int lim;
367cd9ad58dSDavid S. Miller 
368cd9ad58dSDavid S. Miller 		lim = 1000;
369cd9ad58dSDavid S. Miller 		while ((val = dma_read32(DMA_CSR)) & DMA_PEND_READ) {
370cd9ad58dSDavid S. Miller 			if (--lim == 0) {
371cd9ad58dSDavid S. Miller 				printk(KERN_ALERT PFX "esp%d: DMA will not "
372cd9ad58dSDavid S. Miller 				       "invalidate!\n", esp->host->unique_id);
373cd9ad58dSDavid S. Miller 				break;
374cd9ad58dSDavid S. Miller 			}
375cd9ad58dSDavid S. Miller 			udelay(1);
376cd9ad58dSDavid S. Miller 		}
377cd9ad58dSDavid S. Miller 
378cd9ad58dSDavid S. Miller 		val &= ~(DMA_ENABLE | DMA_ST_WRITE | DMA_BCNT_ENAB);
379cd9ad58dSDavid S. Miller 		val |= DMA_FIFO_INV;
380cd9ad58dSDavid S. Miller 		dma_write32(val, DMA_CSR);
381cd9ad58dSDavid S. Miller 		val &= ~DMA_FIFO_INV;
382cd9ad58dSDavid S. Miller 		dma_write32(val, DMA_CSR);
383cd9ad58dSDavid S. Miller 	}
384cd9ad58dSDavid S. Miller }
385cd9ad58dSDavid S. Miller 
sbus_esp_send_dma_cmd(struct esp * esp,u32 addr,u32 esp_count,u32 dma_count,int write,u8 cmd)386cd9ad58dSDavid S. Miller static void sbus_esp_send_dma_cmd(struct esp *esp, u32 addr, u32 esp_count,
387cd9ad58dSDavid S. Miller 				  u32 dma_count, int write, u8 cmd)
388cd9ad58dSDavid S. Miller {
389cd9ad58dSDavid S. Miller 	u32 csr;
390cd9ad58dSDavid S. Miller 
391cd9ad58dSDavid S. Miller 	BUG_ON(!(cmd & ESP_CMD_DMA));
392cd9ad58dSDavid S. Miller 
393cd9ad58dSDavid S. Miller 	sbus_esp_write8(esp, (esp_count >> 0) & 0xff, ESP_TCLOW);
394cd9ad58dSDavid S. Miller 	sbus_esp_write8(esp, (esp_count >> 8) & 0xff, ESP_TCMED);
395cd9ad58dSDavid S. Miller 	if (esp->rev == FASHME) {
396cd9ad58dSDavid S. Miller 		sbus_esp_write8(esp, (esp_count >> 16) & 0xff, FAS_RLO);
397cd9ad58dSDavid S. Miller 		sbus_esp_write8(esp, 0, FAS_RHI);
398cd9ad58dSDavid S. Miller 
399cd9ad58dSDavid S. Miller 		scsi_esp_cmd(esp, cmd);
400cd9ad58dSDavid S. Miller 
401cd9ad58dSDavid S. Miller 		csr = esp->prev_hme_dmacsr;
402cd9ad58dSDavid S. Miller 		csr |= DMA_SCSI_DISAB | DMA_ENABLE;
403cd9ad58dSDavid S. Miller 		if (write)
404cd9ad58dSDavid S. Miller 			csr |= DMA_ST_WRITE;
405cd9ad58dSDavid S. Miller 		else
406cd9ad58dSDavid S. Miller 			csr &= ~DMA_ST_WRITE;
407cd9ad58dSDavid S. Miller 		esp->prev_hme_dmacsr = csr;
408cd9ad58dSDavid S. Miller 
409cd9ad58dSDavid S. Miller 		dma_write32(dma_count, DMA_COUNT);
410cd9ad58dSDavid S. Miller 		dma_write32(addr, DMA_ADDR);
411cd9ad58dSDavid S. Miller 		dma_write32(csr, DMA_CSR);
412cd9ad58dSDavid S. Miller 	} else {
413cd9ad58dSDavid S. Miller 		csr = dma_read32(DMA_CSR);
414cd9ad58dSDavid S. Miller 		csr |= DMA_ENABLE;
415cd9ad58dSDavid S. Miller 		if (write)
416cd9ad58dSDavid S. Miller 			csr |= DMA_ST_WRITE;
417cd9ad58dSDavid S. Miller 		else
418cd9ad58dSDavid S. Miller 			csr &= ~DMA_ST_WRITE;
419cd9ad58dSDavid S. Miller 		dma_write32(csr, DMA_CSR);
420334ae614SDavid S. Miller 		if (esp->dmarev == dvmaesc1) {
421cd9ad58dSDavid S. Miller 			u32 end = PAGE_ALIGN(addr + dma_count + 16U);
422cd9ad58dSDavid S. Miller 			dma_write32(end - addr, DMA_COUNT);
423cd9ad58dSDavid S. Miller 		}
424cd9ad58dSDavid S. Miller 		dma_write32(addr, DMA_ADDR);
425cd9ad58dSDavid S. Miller 
426cd9ad58dSDavid S. Miller 		scsi_esp_cmd(esp, cmd);
427cd9ad58dSDavid S. Miller 	}
428cd9ad58dSDavid S. Miller 
429cd9ad58dSDavid S. Miller }
430cd9ad58dSDavid S. Miller 
sbus_esp_dma_error(struct esp * esp)431cd9ad58dSDavid S. Miller static int sbus_esp_dma_error(struct esp *esp)
432cd9ad58dSDavid S. Miller {
433cd9ad58dSDavid S. Miller 	u32 csr = dma_read32(DMA_CSR);
434cd9ad58dSDavid S. Miller 
435cd9ad58dSDavid S. Miller 	if (csr & DMA_HNDL_ERROR)
436cd9ad58dSDavid S. Miller 		return 1;
437cd9ad58dSDavid S. Miller 
438cd9ad58dSDavid S. Miller 	return 0;
439cd9ad58dSDavid S. Miller }
440cd9ad58dSDavid S. Miller 
441cd9ad58dSDavid S. Miller static const struct esp_driver_ops sbus_esp_ops = {
442cd9ad58dSDavid S. Miller 	.esp_write8	=	sbus_esp_write8,
443cd9ad58dSDavid S. Miller 	.esp_read8	=	sbus_esp_read8,
444cd9ad58dSDavid S. Miller 	.irq_pending	=	sbus_esp_irq_pending,
445cd9ad58dSDavid S. Miller 	.reset_dma	=	sbus_esp_reset_dma,
446cd9ad58dSDavid S. Miller 	.dma_drain	=	sbus_esp_dma_drain,
447cd9ad58dSDavid S. Miller 	.dma_invalidate	=	sbus_esp_dma_invalidate,
448cd9ad58dSDavid S. Miller 	.send_dma_cmd	=	sbus_esp_send_dma_cmd,
449cd9ad58dSDavid S. Miller 	.dma_error	=	sbus_esp_dma_error,
450cd9ad58dSDavid S. Miller };
451cd9ad58dSDavid S. Miller 
esp_sbus_probe_one(struct platform_device * op,struct platform_device * espdma,int hme)4526f039790SGreg Kroah-Hartman static int esp_sbus_probe_one(struct platform_device *op,
4536f039790SGreg Kroah-Hartman 			      struct platform_device *espdma, int hme)
454cd9ad58dSDavid S. Miller {
4553b465a14SBart Van Assche 	const struct scsi_host_template *tpnt = &scsi_esp_template;
456cd9ad58dSDavid S. Miller 	struct Scsi_Host *host;
457cd9ad58dSDavid S. Miller 	struct esp *esp;
458cd9ad58dSDavid S. Miller 	int err;
459cd9ad58dSDavid S. Miller 
460cd9ad58dSDavid S. Miller 	host = scsi_host_alloc(tpnt, sizeof(struct esp));
461cd9ad58dSDavid S. Miller 
462cd9ad58dSDavid S. Miller 	err = -ENOMEM;
463cd9ad58dSDavid S. Miller 	if (!host)
464cd9ad58dSDavid S. Miller 		goto fail;
465cd9ad58dSDavid S. Miller 
466cd9ad58dSDavid S. Miller 	host->max_id = (hme ? 16 : 8);
4672b14ec78SChristoph Hellwig 	esp = shost_priv(host);
468cd9ad58dSDavid S. Miller 
469cd9ad58dSDavid S. Miller 	esp->host = host;
47098cda6a2SChristoph Hellwig 	esp->dev = &op->dev;
471cd9ad58dSDavid S. Miller 	esp->ops = &sbus_esp_ops;
472cd9ad58dSDavid S. Miller 
473cd9ad58dSDavid S. Miller 	if (hme)
474cd9ad58dSDavid S. Miller 		esp->flags |= ESP_FLAG_WIDE_CAPABLE;
475cd9ad58dSDavid S. Miller 
476334ae614SDavid S. Miller 	err = esp_sbus_setup_dma(esp, espdma);
477cd9ad58dSDavid S. Miller 	if (err < 0)
478cd9ad58dSDavid S. Miller 		goto fail_unlink;
479cd9ad58dSDavid S. Miller 
480cd9ad58dSDavid S. Miller 	err = esp_sbus_map_regs(esp, hme);
481cd9ad58dSDavid S. Miller 	if (err < 0)
482cd9ad58dSDavid S. Miller 		goto fail_unlink;
483cd9ad58dSDavid S. Miller 
484cd9ad58dSDavid S. Miller 	err = esp_sbus_map_command_block(esp);
485cd9ad58dSDavid S. Miller 	if (err < 0)
486cd9ad58dSDavid S. Miller 		goto fail_unmap_regs;
487cd9ad58dSDavid S. Miller 
488cd9ad58dSDavid S. Miller 	err = esp_sbus_register_irq(esp);
489cd9ad58dSDavid S. Miller 	if (err < 0)
490cd9ad58dSDavid S. Miller 		goto fail_unmap_command_block;
491cd9ad58dSDavid S. Miller 
492cd9ad58dSDavid S. Miller 	esp_sbus_get_props(esp, espdma);
493cd9ad58dSDavid S. Miller 
494cd9ad58dSDavid S. Miller 	/* Before we try to touch the ESP chip, ESC1 dma can
495cd9ad58dSDavid S. Miller 	 * come up with the reset bit set, so make sure that
496cd9ad58dSDavid S. Miller 	 * is clear first.
497cd9ad58dSDavid S. Miller 	 */
498334ae614SDavid S. Miller 	if (esp->dmarev == dvmaesc1) {
499cd9ad58dSDavid S. Miller 		u32 val = dma_read32(DMA_CSR);
500cd9ad58dSDavid S. Miller 
501cd9ad58dSDavid S. Miller 		dma_write32(val & ~DMA_RST_SCSI, DMA_CSR);
502cd9ad58dSDavid S. Miller 	}
503cd9ad58dSDavid S. Miller 
50405bb5e93SDavid S. Miller 	dev_set_drvdata(&op->dev, esp);
505cd9ad58dSDavid S. Miller 
50644b1b4d2SChristoph Hellwig 	err = scsi_esp_register(esp);
507cd9ad58dSDavid S. Miller 	if (err)
508cd9ad58dSDavid S. Miller 		goto fail_free_irq;
509cd9ad58dSDavid S. Miller 
510cd9ad58dSDavid S. Miller 	return 0;
511cd9ad58dSDavid S. Miller 
512cd9ad58dSDavid S. Miller fail_free_irq:
513cd9ad58dSDavid S. Miller 	free_irq(host->irq, esp);
514cd9ad58dSDavid S. Miller fail_unmap_command_block:
51505bb5e93SDavid S. Miller 	dma_free_coherent(&op->dev, 16,
516cd9ad58dSDavid S. Miller 			  esp->command_block,
517cd9ad58dSDavid S. Miller 			  esp->command_block_dma);
518cd9ad58dSDavid S. Miller fail_unmap_regs:
51905bb5e93SDavid S. Miller 	of_iounmap(&op->resource[(hme ? 1 : 0)], esp->regs, SBUS_ESP_REG_SIZE);
520cd9ad58dSDavid S. Miller fail_unlink:
521cd9ad58dSDavid S. Miller 	scsi_host_put(host);
522cd9ad58dSDavid S. Miller fail:
523cd9ad58dSDavid S. Miller 	return err;
524cd9ad58dSDavid S. Miller }
525cd9ad58dSDavid S. Miller 
esp_sbus_probe(struct platform_device * op)5266f039790SGreg Kroah-Hartman static int esp_sbus_probe(struct platform_device *op)
527cd9ad58dSDavid S. Miller {
528334ae614SDavid S. Miller 	struct device_node *dma_node = NULL;
52961c7a080SGrant Likely 	struct device_node *dp = op->dev.of_node;
5302dc11581SGrant Likely 	struct platform_device *dma_of = NULL;
531cd9ad58dSDavid S. Miller 	int hme = 0;
532f62f9ffdSJohan Hovold 	int ret;
533cd9ad58dSDavid S. Miller 
5344b668103SRob Herring 	if (of_node_name_eq(dp->parent, "espdma") ||
5354b668103SRob Herring 	    of_node_name_eq(dp->parent, "dma"))
536334ae614SDavid S. Miller 		dma_node = dp->parent;
5374b668103SRob Herring 	else if (of_node_name_eq(dp, "SUNW,fas")) {
53861c7a080SGrant Likely 		dma_node = op->dev.of_node;
539cd9ad58dSDavid S. Miller 		hme = 1;
540cd9ad58dSDavid S. Miller 	}
541334ae614SDavid S. Miller 	if (dma_node)
542334ae614SDavid S. Miller 		dma_of = of_find_device_by_node(dma_node);
543334ae614SDavid S. Miller 	if (!dma_of)
544334ae614SDavid S. Miller 		return -ENODEV;
545cd9ad58dSDavid S. Miller 
546f62f9ffdSJohan Hovold 	ret = esp_sbus_probe_one(op, dma_of, hme);
547f62f9ffdSJohan Hovold 	if (ret)
548f62f9ffdSJohan Hovold 		put_device(&dma_of->dev);
549f62f9ffdSJohan Hovold 
550f62f9ffdSJohan Hovold 	return ret;
551cd9ad58dSDavid S. Miller }
552cd9ad58dSDavid S. Miller 
esp_sbus_remove(struct platform_device * op)5536f039790SGreg Kroah-Hartman static int esp_sbus_remove(struct platform_device *op)
554cd9ad58dSDavid S. Miller {
55505bb5e93SDavid S. Miller 	struct esp *esp = dev_get_drvdata(&op->dev);
5562dc11581SGrant Likely 	struct platform_device *dma_of = esp->dma;
557cd9ad58dSDavid S. Miller 	unsigned int irq = esp->host->irq;
55805bb5e93SDavid S. Miller 	bool is_hme;
559cd9ad58dSDavid S. Miller 	u32 val;
560cd9ad58dSDavid S. Miller 
561cd9ad58dSDavid S. Miller 	scsi_esp_unregister(esp);
562cd9ad58dSDavid S. Miller 
563cd9ad58dSDavid S. Miller 	/* Disable interrupts.  */
564cd9ad58dSDavid S. Miller 	val = dma_read32(DMA_CSR);
565cd9ad58dSDavid S. Miller 	dma_write32(val & ~DMA_INT_ENAB, DMA_CSR);
566cd9ad58dSDavid S. Miller 
567cd9ad58dSDavid S. Miller 	free_irq(irq, esp);
56805bb5e93SDavid S. Miller 
56905bb5e93SDavid S. Miller 	is_hme = (esp->dmarev == dvmahme);
57005bb5e93SDavid S. Miller 
57105bb5e93SDavid S. Miller 	dma_free_coherent(&op->dev, 16,
572cd9ad58dSDavid S. Miller 			  esp->command_block,
573cd9ad58dSDavid S. Miller 			  esp->command_block_dma);
57405bb5e93SDavid S. Miller 	of_iounmap(&op->resource[(is_hme ? 1 : 0)], esp->regs,
57505bb5e93SDavid S. Miller 		   SBUS_ESP_REG_SIZE);
576334ae614SDavid S. Miller 	of_iounmap(&dma_of->resource[0], esp->dma_regs,
577334ae614SDavid S. Miller 		   resource_size(&dma_of->resource[0]));
578cd9ad58dSDavid S. Miller 
579cd9ad58dSDavid S. Miller 	scsi_host_put(esp->host);
580cd9ad58dSDavid S. Miller 
58105bb5e93SDavid S. Miller 	dev_set_drvdata(&op->dev, NULL);
58205bb5e93SDavid S. Miller 
583f62f9ffdSJohan Hovold 	put_device(&dma_of->dev);
584f62f9ffdSJohan Hovold 
585cd9ad58dSDavid S. Miller 	return 0;
586cd9ad58dSDavid S. Miller }
587cd9ad58dSDavid S. Miller 
588fd098316SDavid S. Miller static const struct of_device_id esp_match[] = {
589cd9ad58dSDavid S. Miller 	{
590cd9ad58dSDavid S. Miller 		.name = "SUNW,esp",
591cd9ad58dSDavid S. Miller 	},
592cd9ad58dSDavid S. Miller 	{
593cd9ad58dSDavid S. Miller 		.name = "SUNW,fas",
594cd9ad58dSDavid S. Miller 	},
595cd9ad58dSDavid S. Miller 	{
596cd9ad58dSDavid S. Miller 		.name = "esp",
597cd9ad58dSDavid S. Miller 	},
598cd9ad58dSDavid S. Miller 	{},
599cd9ad58dSDavid S. Miller };
600cd9ad58dSDavid S. Miller MODULE_DEVICE_TABLE(of, esp_match);
601cd9ad58dSDavid S. Miller 
6024ebb24f7SGrant Likely static struct platform_driver esp_sbus_driver = {
6034018294bSGrant Likely 	.driver = {
604cd9ad58dSDavid S. Miller 		.name = "esp",
6054018294bSGrant Likely 		.of_match_table = esp_match,
6064018294bSGrant Likely 	},
607cd9ad58dSDavid S. Miller 	.probe		= esp_sbus_probe,
6086f039790SGreg Kroah-Hartman 	.remove		= esp_sbus_remove,
609cd9ad58dSDavid S. Miller };
610ea0dc200SLiu Shixin module_platform_driver(esp_sbus_driver);
611cd9ad58dSDavid S. Miller 
612cd9ad58dSDavid S. Miller MODULE_DESCRIPTION("Sun ESP SCSI driver");
613cd9ad58dSDavid S. Miller MODULE_AUTHOR("David S. Miller (davem@davemloft.net)");
614cd9ad58dSDavid S. Miller MODULE_LICENSE("GPL");
615cd9ad58dSDavid S. Miller MODULE_VERSION(DRV_VERSION);
616