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