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