1 /*
2  * Generic Generic NCR5380 driver
3  *
4  * Copyright 1995-2002, Russell King
5  */
6 #include <linux/module.h>
7 #include <linux/ioport.h>
8 #include <linux/blkdev.h>
9 #include <linux/init.h>
10 
11 #include <asm/ecard.h>
12 #include <asm/io.h>
13 
14 #include <scsi/scsi_host.h>
15 
16 #define priv(host)			((struct NCR5380_hostdata *)(host)->hostdata)
17 #define NCR5380_read(reg)		cumanascsi_read(hostdata, reg)
18 #define NCR5380_write(reg, value)	cumanascsi_write(hostdata, reg, value)
19 
20 #define NCR5380_dma_xfer_len		cumanascsi_dma_xfer_len
21 #define NCR5380_dma_recv_setup		cumanascsi_pread
22 #define NCR5380_dma_send_setup		cumanascsi_pwrite
23 #define NCR5380_dma_residual		NCR5380_dma_residual_none
24 
25 #define NCR5380_intr			cumanascsi_intr
26 #define NCR5380_queue_command		cumanascsi_queue_command
27 #define NCR5380_info			cumanascsi_info
28 
29 #define NCR5380_implementation_fields	\
30 	unsigned ctrl
31 
32 struct NCR5380_hostdata;
33 static u8 cumanascsi_read(struct NCR5380_hostdata *, unsigned int);
34 static void cumanascsi_write(struct NCR5380_hostdata *, unsigned int, u8);
35 
36 #include "../NCR5380.h"
37 
38 #define CTRL	0x16fc
39 #define STAT	0x2004
40 #define L(v)	(((v)<<16)|((v) & 0x0000ffff))
41 #define H(v)	(((v)>>16)|((v) & 0xffff0000))
42 
cumanascsi_pwrite(struct NCR5380_hostdata * hostdata,unsigned char * addr,int len)43 static inline int cumanascsi_pwrite(struct NCR5380_hostdata *hostdata,
44                                     unsigned char *addr, int len)
45 {
46   unsigned long *laddr;
47   u8 __iomem *base = hostdata->io;
48   u8 __iomem *dma = hostdata->pdma_io + 0x2000;
49 
50   if(!len) return 0;
51 
52   writeb(0x02, base + CTRL);
53   laddr = (unsigned long *)addr;
54   while(len >= 32)
55   {
56     unsigned int status;
57     unsigned long v;
58     status = readb(base + STAT);
59     if(status & 0x80)
60       goto end;
61     if(!(status & 0x40))
62       continue;
63     v=*laddr++; writew(L(v), dma); writew(H(v), dma);
64     v=*laddr++; writew(L(v), dma); writew(H(v), dma);
65     v=*laddr++; writew(L(v), dma); writew(H(v), dma);
66     v=*laddr++; writew(L(v), dma); writew(H(v), dma);
67     v=*laddr++; writew(L(v), dma); writew(H(v), dma);
68     v=*laddr++; writew(L(v), dma); writew(H(v), dma);
69     v=*laddr++; writew(L(v), dma); writew(H(v), dma);
70     v=*laddr++; writew(L(v), dma); writew(H(v), dma);
71     len -= 32;
72     if(len == 0)
73       break;
74   }
75 
76   addr = (unsigned char *)laddr;
77   writeb(0x12, base + CTRL);
78 
79   while(len > 0)
80   {
81     unsigned int status;
82     status = readb(base + STAT);
83     if(status & 0x80)
84       goto end;
85     if(status & 0x40)
86     {
87       writeb(*addr++, dma);
88       if(--len == 0)
89         break;
90     }
91 
92     status = readb(base + STAT);
93     if(status & 0x80)
94       goto end;
95     if(status & 0x40)
96     {
97       writeb(*addr++, dma);
98       if(--len == 0)
99         break;
100     }
101   }
102 end:
103   writeb(hostdata->ctrl | 0x40, base + CTRL);
104 
105 	if (len)
106 		return -1;
107 	return 0;
108 }
109 
cumanascsi_pread(struct NCR5380_hostdata * hostdata,unsigned char * addr,int len)110 static inline int cumanascsi_pread(struct NCR5380_hostdata *hostdata,
111                                    unsigned char *addr, int len)
112 {
113   unsigned long *laddr;
114   u8 __iomem *base = hostdata->io;
115   u8 __iomem *dma = hostdata->pdma_io + 0x2000;
116 
117   if(!len) return 0;
118 
119   writeb(0x00, base + CTRL);
120   laddr = (unsigned long *)addr;
121   while(len >= 32)
122   {
123     unsigned int status;
124     status = readb(base + STAT);
125     if(status & 0x80)
126       goto end;
127     if(!(status & 0x40))
128       continue;
129     *laddr++ = readw(dma) | (readw(dma) << 16);
130     *laddr++ = readw(dma) | (readw(dma) << 16);
131     *laddr++ = readw(dma) | (readw(dma) << 16);
132     *laddr++ = readw(dma) | (readw(dma) << 16);
133     *laddr++ = readw(dma) | (readw(dma) << 16);
134     *laddr++ = readw(dma) | (readw(dma) << 16);
135     *laddr++ = readw(dma) | (readw(dma) << 16);
136     *laddr++ = readw(dma) | (readw(dma) << 16);
137     len -= 32;
138     if(len == 0)
139       break;
140   }
141 
142   addr = (unsigned char *)laddr;
143   writeb(0x10, base + CTRL);
144 
145   while(len > 0)
146   {
147     unsigned int status;
148     status = readb(base + STAT);
149     if(status & 0x80)
150       goto end;
151     if(status & 0x40)
152     {
153       *addr++ = readb(dma);
154       if(--len == 0)
155         break;
156     }
157 
158     status = readb(base + STAT);
159     if(status & 0x80)
160       goto end;
161     if(status & 0x40)
162     {
163       *addr++ = readb(dma);
164       if(--len == 0)
165         break;
166     }
167   }
168 end:
169   writeb(hostdata->ctrl | 0x40, base + CTRL);
170 
171 	if (len)
172 		return -1;
173 	return 0;
174 }
175 
cumanascsi_dma_xfer_len(struct NCR5380_hostdata * hostdata,struct scsi_cmnd * cmd)176 static int cumanascsi_dma_xfer_len(struct NCR5380_hostdata *hostdata,
177                                    struct scsi_cmnd *cmd)
178 {
179 	return cmd->transfersize;
180 }
181 
cumanascsi_read(struct NCR5380_hostdata * hostdata,unsigned int reg)182 static u8 cumanascsi_read(struct NCR5380_hostdata *hostdata,
183                           unsigned int reg)
184 {
185 	u8 __iomem *base = hostdata->io;
186 	u8 val;
187 
188 	writeb(0, base + CTRL);
189 
190 	val = readb(base + 0x2100 + (reg << 2));
191 
192 	hostdata->ctrl = 0x40;
193 	writeb(0x40, base + CTRL);
194 
195 	return val;
196 }
197 
cumanascsi_write(struct NCR5380_hostdata * hostdata,unsigned int reg,u8 value)198 static void cumanascsi_write(struct NCR5380_hostdata *hostdata,
199                              unsigned int reg, u8 value)
200 {
201 	u8 __iomem *base = hostdata->io;
202 
203 	writeb(0, base + CTRL);
204 
205 	writeb(value, base + 0x2100 + (reg << 2));
206 
207 	hostdata->ctrl = 0x40;
208 	writeb(0x40, base + CTRL);
209 }
210 
211 #include "../NCR5380.c"
212 
213 static struct scsi_host_template cumanascsi_template = {
214 	.module			= THIS_MODULE,
215 	.name			= "Cumana 16-bit SCSI",
216 	.info			= cumanascsi_info,
217 	.queuecommand		= cumanascsi_queue_command,
218 	.eh_abort_handler	= NCR5380_abort,
219 	.eh_host_reset_handler	= NCR5380_host_reset,
220 	.can_queue		= 16,
221 	.this_id		= 7,
222 	.sg_tablesize		= SG_ALL,
223 	.cmd_per_lun		= 2,
224 	.use_clustering		= DISABLE_CLUSTERING,
225 	.proc_name		= "CumanaSCSI-1",
226 	.cmd_size		= NCR5380_CMD_SIZE,
227 	.max_sectors		= 128,
228 };
229 
cumanascsi1_probe(struct expansion_card * ec,const struct ecard_id * id)230 static int cumanascsi1_probe(struct expansion_card *ec,
231 			     const struct ecard_id *id)
232 {
233 	struct Scsi_Host *host;
234 	int ret;
235 
236 	ret = ecard_request_resources(ec);
237 	if (ret)
238 		goto out;
239 
240 	host = scsi_host_alloc(&cumanascsi_template, sizeof(struct NCR5380_hostdata));
241 	if (!host) {
242 		ret = -ENOMEM;
243 		goto out_release;
244 	}
245 
246 	priv(host)->io = ioremap(ecard_resource_start(ec, ECARD_RES_IOCSLOW),
247 	                         ecard_resource_len(ec, ECARD_RES_IOCSLOW));
248 	priv(host)->pdma_io = ioremap(ecard_resource_start(ec, ECARD_RES_MEMC),
249 	                              ecard_resource_len(ec, ECARD_RES_MEMC));
250 	if (!priv(host)->io || !priv(host)->pdma_io) {
251 		ret = -ENOMEM;
252 		goto out_unmap;
253 	}
254 
255 	host->irq = ec->irq;
256 
257 	ret = NCR5380_init(host, FLAG_DMA_FIXUP | FLAG_LATE_DMA_SETUP);
258 	if (ret)
259 		goto out_unmap;
260 
261 	NCR5380_maybe_reset_bus(host);
262 
263         priv(host)->ctrl = 0;
264         writeb(0, priv(host)->io + CTRL);
265 
266 	ret = request_irq(host->irq, cumanascsi_intr, 0,
267 			  "CumanaSCSI-1", host);
268 	if (ret) {
269 		printk("scsi%d: IRQ%d not free: %d\n",
270 		    host->host_no, host->irq, ret);
271 		goto out_exit;
272 	}
273 
274 	ret = scsi_add_host(host, &ec->dev);
275 	if (ret)
276 		goto out_free_irq;
277 
278 	scsi_scan_host(host);
279 	goto out;
280 
281  out_free_irq:
282 	free_irq(host->irq, host);
283  out_exit:
284 	NCR5380_exit(host);
285  out_unmap:
286 	iounmap(priv(host)->io);
287 	iounmap(priv(host)->pdma_io);
288 	scsi_host_put(host);
289  out_release:
290 	ecard_release_resources(ec);
291  out:
292 	return ret;
293 }
294 
cumanascsi1_remove(struct expansion_card * ec)295 static void cumanascsi1_remove(struct expansion_card *ec)
296 {
297 	struct Scsi_Host *host = ecard_get_drvdata(ec);
298 	void __iomem *base = priv(host)->io;
299 	void __iomem *dma = priv(host)->pdma_io;
300 
301 	ecard_set_drvdata(ec, NULL);
302 
303 	scsi_remove_host(host);
304 	free_irq(host->irq, host);
305 	NCR5380_exit(host);
306 	scsi_host_put(host);
307 	iounmap(base);
308 	iounmap(dma);
309 	ecard_release_resources(ec);
310 }
311 
312 static const struct ecard_id cumanascsi1_cids[] = {
313 	{ MANU_CUMANA, PROD_CUMANA_SCSI_1 },
314 	{ 0xffff, 0xffff }
315 };
316 
317 static struct ecard_driver cumanascsi1_driver = {
318 	.probe		= cumanascsi1_probe,
319 	.remove		= cumanascsi1_remove,
320 	.id_table	= cumanascsi1_cids,
321 	.drv = {
322 		.name		= "cumanascsi1",
323 	},
324 };
325 
cumanascsi_init(void)326 static int __init cumanascsi_init(void)
327 {
328 	return ecard_register_driver(&cumanascsi1_driver);
329 }
330 
cumanascsi_exit(void)331 static void __exit cumanascsi_exit(void)
332 {
333 	ecard_remove_driver(&cumanascsi1_driver);
334 }
335 
336 module_init(cumanascsi_init);
337 module_exit(cumanascsi_exit);
338 
339 MODULE_DESCRIPTION("Cumana SCSI-1 driver for Acorn machines");
340 MODULE_LICENSE("GPL");
341