xref: /openbmc/linux/drivers/scsi/sun3x_esp.c (revision 0b003749)
1 /* sun3x_esp.c: ESP front-end for Sun3x systems.
2  *
3  * Copyright (C) 2007,2008 Thomas Bogendoerfer (tsbogend@alpha.franken.de)
4  */
5 
6 #include <linux/kernel.h>
7 #include <linux/gfp.h>
8 #include <linux/types.h>
9 #include <linux/delay.h>
10 #include <linux/module.h>
11 #include <linux/init.h>
12 #include <linux/platform_device.h>
13 #include <linux/dma-mapping.h>
14 #include <linux/interrupt.h>
15 #include <linux/io.h>
16 
17 #include <asm/sun3x.h>
18 #include <asm/dma.h>
19 #include <asm/dvma.h>
20 
21 /* DMA controller reg offsets */
22 #define DMA_CSR		0x00UL	/* rw  DMA control/status register    0x00   */
23 #define DMA_ADDR        0x04UL	/* rw  DMA transfer address register  0x04   */
24 #define DMA_COUNT       0x08UL	/* rw  DMA transfer count register    0x08   */
25 #define DMA_TEST        0x0cUL	/* rw  DMA test/debug register        0x0c   */
26 
27 #include <scsi/scsi_host.h>
28 
29 #include "esp_scsi.h"
30 
31 #define DRV_MODULE_NAME		"sun3x_esp"
32 #define PFX DRV_MODULE_NAME	": "
33 #define DRV_VERSION		"1.000"
34 #define DRV_MODULE_RELDATE	"Nov 1, 2007"
35 
36 /*
37  * m68k always assumes readl/writel operate on little endian
38  * mmio space; this is wrong at least for Sun3x, so we
39  * need to workaround this until a proper way is found
40  */
41 #if 0
42 #define dma_read32(REG) \
43 	readl(esp->dma_regs + (REG))
44 #define dma_write32(VAL, REG) \
45 	writel((VAL), esp->dma_regs + (REG))
46 #else
47 #define dma_read32(REG) \
48 	*(volatile u32 *)(esp->dma_regs + (REG))
49 #define dma_write32(VAL, REG) \
50 	do { *(volatile u32 *)(esp->dma_regs + (REG)) = (VAL); } while (0)
51 #endif
52 
53 static void sun3x_esp_write8(struct esp *esp, u8 val, unsigned long reg)
54 {
55 	writeb(val, esp->regs + (reg * 4UL));
56 }
57 
58 static u8 sun3x_esp_read8(struct esp *esp, unsigned long reg)
59 {
60 	return readb(esp->regs + (reg * 4UL));
61 }
62 
63 static int sun3x_esp_irq_pending(struct esp *esp)
64 {
65 	if (dma_read32(DMA_CSR) & (DMA_HNDL_INTR | DMA_HNDL_ERROR))
66 		return 1;
67 	return 0;
68 }
69 
70 static void sun3x_esp_reset_dma(struct esp *esp)
71 {
72 	u32 val;
73 
74 	val = dma_read32(DMA_CSR);
75 	dma_write32(val | DMA_RST_SCSI, DMA_CSR);
76 	dma_write32(val & ~DMA_RST_SCSI, DMA_CSR);
77 
78 	/* Enable interrupts.  */
79 	val = dma_read32(DMA_CSR);
80 	dma_write32(val | DMA_INT_ENAB, DMA_CSR);
81 }
82 
83 static void sun3x_esp_dma_drain(struct esp *esp)
84 {
85 	u32 csr;
86 	int lim;
87 
88 	csr = dma_read32(DMA_CSR);
89 	if (!(csr & DMA_FIFO_ISDRAIN))
90 		return;
91 
92 	dma_write32(csr | DMA_FIFO_STDRAIN, DMA_CSR);
93 
94 	lim = 1000;
95 	while (dma_read32(DMA_CSR) & DMA_FIFO_ISDRAIN) {
96 		if (--lim == 0) {
97 			printk(KERN_ALERT PFX "esp%d: DMA will not drain!\n",
98 			       esp->host->unique_id);
99 			break;
100 		}
101 		udelay(1);
102 	}
103 }
104 
105 static void sun3x_esp_dma_invalidate(struct esp *esp)
106 {
107 	u32 val;
108 	int lim;
109 
110 	lim = 1000;
111 	while ((val = dma_read32(DMA_CSR)) & DMA_PEND_READ) {
112 		if (--lim == 0) {
113 			printk(KERN_ALERT PFX "esp%d: DMA will not "
114 			       "invalidate!\n", esp->host->unique_id);
115 			break;
116 		}
117 		udelay(1);
118 	}
119 
120 	val &= ~(DMA_ENABLE | DMA_ST_WRITE | DMA_BCNT_ENAB);
121 	val |= DMA_FIFO_INV;
122 	dma_write32(val, DMA_CSR);
123 	val &= ~DMA_FIFO_INV;
124 	dma_write32(val, DMA_CSR);
125 }
126 
127 static void sun3x_esp_send_dma_cmd(struct esp *esp, u32 addr, u32 esp_count,
128 				  u32 dma_count, int write, u8 cmd)
129 {
130 	u32 csr;
131 
132 	BUG_ON(!(cmd & ESP_CMD_DMA));
133 
134 	sun3x_esp_write8(esp, (esp_count >> 0) & 0xff, ESP_TCLOW);
135 	sun3x_esp_write8(esp, (esp_count >> 8) & 0xff, ESP_TCMED);
136 	csr = dma_read32(DMA_CSR);
137 	csr |= DMA_ENABLE;
138 	if (write)
139 		csr |= DMA_ST_WRITE;
140 	else
141 		csr &= ~DMA_ST_WRITE;
142 	dma_write32(csr, DMA_CSR);
143 	dma_write32(addr, DMA_ADDR);
144 
145 	scsi_esp_cmd(esp, cmd);
146 }
147 
148 static int sun3x_esp_dma_error(struct esp *esp)
149 {
150 	u32 csr = dma_read32(DMA_CSR);
151 
152 	if (csr & DMA_HNDL_ERROR)
153 		return 1;
154 
155 	return 0;
156 }
157 
158 static const struct esp_driver_ops sun3x_esp_ops = {
159 	.esp_write8	=	sun3x_esp_write8,
160 	.esp_read8	=	sun3x_esp_read8,
161 	.irq_pending	=	sun3x_esp_irq_pending,
162 	.reset_dma	=	sun3x_esp_reset_dma,
163 	.dma_drain	=	sun3x_esp_dma_drain,
164 	.dma_invalidate	=	sun3x_esp_dma_invalidate,
165 	.send_dma_cmd	=	sun3x_esp_send_dma_cmd,
166 	.dma_error	=	sun3x_esp_dma_error,
167 };
168 
169 static int esp_sun3x_probe(struct platform_device *dev)
170 {
171 	struct scsi_host_template *tpnt = &scsi_esp_template;
172 	struct Scsi_Host *host;
173 	struct esp *esp;
174 	struct resource *res;
175 	int err = -ENOMEM;
176 
177 	host = scsi_host_alloc(tpnt, sizeof(struct esp));
178 	if (!host)
179 		goto fail;
180 
181 	host->max_id = 8;
182 	esp = shost_priv(host);
183 
184 	esp->host = host;
185 	esp->dev = &dev->dev;
186 	esp->ops = &sun3x_esp_ops;
187 
188 	res = platform_get_resource(dev, IORESOURCE_MEM, 0);
189 	if (!res || !res->start)
190 		goto fail_unlink;
191 
192 	esp->regs = ioremap_nocache(res->start, 0x20);
193 	if (!esp->regs)
194 		goto fail_unmap_regs;
195 
196 	res = platform_get_resource(dev, IORESOURCE_MEM, 1);
197 	if (!res || !res->start)
198 		goto fail_unmap_regs;
199 
200 	esp->dma_regs = ioremap_nocache(res->start, 0x10);
201 
202 	esp->command_block = dma_alloc_coherent(esp->dev, 16,
203 						&esp->command_block_dma,
204 						GFP_KERNEL);
205 	if (!esp->command_block)
206 		goto fail_unmap_regs_dma;
207 
208 	host->irq = platform_get_irq(dev, 0);
209 	err = request_irq(host->irq, scsi_esp_intr, IRQF_SHARED,
210 			  "SUN3X ESP", esp);
211 	if (err < 0)
212 		goto fail_unmap_command_block;
213 
214 	esp->scsi_id = 7;
215 	esp->host->this_id = esp->scsi_id;
216 	esp->scsi_id_mask = (1 << esp->scsi_id);
217 	esp->cfreq = 20000000;
218 
219 	dev_set_drvdata(&dev->dev, esp);
220 
221 	err = scsi_esp_register(esp);
222 	if (err)
223 		goto fail_free_irq;
224 
225 	return 0;
226 
227 fail_free_irq:
228 	free_irq(host->irq, esp);
229 fail_unmap_command_block:
230 	dma_free_coherent(esp->dev, 16,
231 			  esp->command_block,
232 			  esp->command_block_dma);
233 fail_unmap_regs_dma:
234 	iounmap(esp->dma_regs);
235 fail_unmap_regs:
236 	iounmap(esp->regs);
237 fail_unlink:
238 	scsi_host_put(host);
239 fail:
240 	return err;
241 }
242 
243 static int esp_sun3x_remove(struct platform_device *dev)
244 {
245 	struct esp *esp = dev_get_drvdata(&dev->dev);
246 	unsigned int irq = esp->host->irq;
247 	u32 val;
248 
249 	scsi_esp_unregister(esp);
250 
251 	/* Disable interrupts.  */
252 	val = dma_read32(DMA_CSR);
253 	dma_write32(val & ~DMA_INT_ENAB, DMA_CSR);
254 
255 	free_irq(irq, esp);
256 	dma_free_coherent(esp->dev, 16,
257 			  esp->command_block,
258 			  esp->command_block_dma);
259 
260 	scsi_host_put(esp->host);
261 
262 	return 0;
263 }
264 
265 static struct platform_driver esp_sun3x_driver = {
266 	.probe          = esp_sun3x_probe,
267 	.remove         = esp_sun3x_remove,
268 	.driver = {
269 		.name   = "sun3x_esp",
270 	},
271 };
272 
273 static int __init sun3x_esp_init(void)
274 {
275 	return platform_driver_register(&esp_sun3x_driver);
276 }
277 
278 static void __exit sun3x_esp_exit(void)
279 {
280 	platform_driver_unregister(&esp_sun3x_driver);
281 }
282 
283 MODULE_DESCRIPTION("Sun3x ESP SCSI driver");
284 MODULE_AUTHOR("Thomas Bogendoerfer (tsbogend@alpha.franken.de)");
285 MODULE_LICENSE("GPL");
286 MODULE_VERSION(DRV_VERSION);
287 
288 module_init(sun3x_esp_init);
289 module_exit(sun3x_esp_exit);
290 MODULE_ALIAS("platform:sun3x_esp");
291