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 | |
33 | struct NCR5380_hostdata; |
34 | static u8 cumanascsi_read(struct NCR5380_hostdata *, unsigned int); |
35 | static 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 | |
44 | static 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(val: 0x02, addr: base + CTRL); |
54 | laddr = (unsigned long *)addr; |
55 | while(len >= 32) |
56 | { |
57 | unsigned int status; |
58 | unsigned long v; |
59 | status = readb(addr: base + STAT); |
60 | if(status & 0x80) |
61 | goto end; |
62 | if(!(status & 0x40)) |
63 | continue; |
64 | v=*laddr++; writew(L(v), addr: dma); writew(H(v), addr: dma); |
65 | v=*laddr++; writew(L(v), addr: dma); writew(H(v), addr: dma); |
66 | v=*laddr++; writew(L(v), addr: dma); writew(H(v), addr: dma); |
67 | v=*laddr++; writew(L(v), addr: dma); writew(H(v), addr: dma); |
68 | v=*laddr++; writew(L(v), addr: dma); writew(H(v), addr: dma); |
69 | v=*laddr++; writew(L(v), addr: dma); writew(H(v), addr: dma); |
70 | v=*laddr++; writew(L(v), addr: dma); writew(H(v), addr: dma); |
71 | v=*laddr++; writew(L(v), addr: dma); writew(H(v), addr: dma); |
72 | len -= 32; |
73 | if(len == 0) |
74 | break; |
75 | } |
76 | |
77 | addr = (unsigned char *)laddr; |
78 | writeb(val: 0x12, addr: base + CTRL); |
79 | |
80 | while(len > 0) |
81 | { |
82 | unsigned int status; |
83 | status = readb(addr: base + STAT); |
84 | if(status & 0x80) |
85 | goto end; |
86 | if(status & 0x40) |
87 | { |
88 | writeb(val: *addr++, addr: dma); |
89 | if(--len == 0) |
90 | break; |
91 | } |
92 | |
93 | status = readb(addr: base + STAT); |
94 | if(status & 0x80) |
95 | goto end; |
96 | if(status & 0x40) |
97 | { |
98 | writeb(val: *addr++, addr: dma); |
99 | if(--len == 0) |
100 | break; |
101 | } |
102 | } |
103 | end: |
104 | writeb(val: hostdata->ctrl | 0x40, addr: base + CTRL); |
105 | |
106 | if (len) |
107 | return -1; |
108 | return 0; |
109 | } |
110 | |
111 | static 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(val: 0x00, addr: base + CTRL); |
121 | laddr = (unsigned long *)addr; |
122 | while(len >= 32) |
123 | { |
124 | unsigned int status; |
125 | status = readb(addr: base + STAT); |
126 | if(status & 0x80) |
127 | goto end; |
128 | if(!(status & 0x40)) |
129 | continue; |
130 | *laddr++ = readw(addr: dma) | (readw(addr: dma) << 16); |
131 | *laddr++ = readw(addr: dma) | (readw(addr: dma) << 16); |
132 | *laddr++ = readw(addr: dma) | (readw(addr: dma) << 16); |
133 | *laddr++ = readw(addr: dma) | (readw(addr: dma) << 16); |
134 | *laddr++ = readw(addr: dma) | (readw(addr: dma) << 16); |
135 | *laddr++ = readw(addr: dma) | (readw(addr: dma) << 16); |
136 | *laddr++ = readw(addr: dma) | (readw(addr: dma) << 16); |
137 | *laddr++ = readw(addr: dma) | (readw(addr: dma) << 16); |
138 | len -= 32; |
139 | if(len == 0) |
140 | break; |
141 | } |
142 | |
143 | addr = (unsigned char *)laddr; |
144 | writeb(val: 0x10, addr: base + CTRL); |
145 | |
146 | while(len > 0) |
147 | { |
148 | unsigned int status; |
149 | status = readb(addr: base + STAT); |
150 | if(status & 0x80) |
151 | goto end; |
152 | if(status & 0x40) |
153 | { |
154 | *addr++ = readb(addr: dma); |
155 | if(--len == 0) |
156 | break; |
157 | } |
158 | |
159 | status = readb(addr: base + STAT); |
160 | if(status & 0x80) |
161 | goto end; |
162 | if(status & 0x40) |
163 | { |
164 | *addr++ = readb(addr: dma); |
165 | if(--len == 0) |
166 | break; |
167 | } |
168 | } |
169 | end: |
170 | writeb(val: hostdata->ctrl | 0x40, addr: base + CTRL); |
171 | |
172 | if (len) |
173 | return -1; |
174 | return 0; |
175 | } |
176 | |
177 | static int cumanascsi_dma_xfer_len(struct NCR5380_hostdata *hostdata, |
178 | struct scsi_cmnd *cmd) |
179 | { |
180 | return cmd->transfersize; |
181 | } |
182 | |
183 | static u8 cumanascsi_read(struct NCR5380_hostdata *hostdata, |
184 | unsigned int reg) |
185 | { |
186 | u8 __iomem *base = hostdata->io; |
187 | u8 val; |
188 | |
189 | writeb(val: 0, addr: base + CTRL); |
190 | |
191 | val = readb(addr: base + 0x2100 + (reg << 2)); |
192 | |
193 | hostdata->ctrl = 0x40; |
194 | writeb(val: 0x40, addr: base + CTRL); |
195 | |
196 | return val; |
197 | } |
198 | |
199 | static void cumanascsi_write(struct NCR5380_hostdata *hostdata, |
200 | unsigned int reg, u8 value) |
201 | { |
202 | u8 __iomem *base = hostdata->io; |
203 | |
204 | writeb(val: 0, addr: base + CTRL); |
205 | |
206 | writeb(val: value, addr: base + 0x2100 + (reg << 2)); |
207 | |
208 | hostdata->ctrl = 0x40; |
209 | writeb(val: 0x40, addr: base + CTRL); |
210 | } |
211 | |
212 | #include "../NCR5380.c" |
213 | |
214 | static 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 | |
231 | static 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(offset: ecard_resource_start(ec, ECARD_RES_IOCSLOW), |
248 | size: ecard_resource_len(ec, ECARD_RES_IOCSLOW)); |
249 | priv(host)->pdma_io = ioremap(offset: ecard_resource_start(ec, ECARD_RES_MEMC), |
250 | size: 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(instance: host, FLAG_DMA_FIXUP | FLAG_LATE_DMA_SETUP); |
259 | if (ret) |
260 | goto out_unmap; |
261 | |
262 | NCR5380_maybe_reset_bus(instance: host); |
263 | |
264 | priv(host)->ctrl = 0; |
265 | writeb(val: 0, priv(host)->io + CTRL); |
266 | |
267 | ret = request_irq(irq: host->irq, handler: cumanascsi_intr, flags: 0, |
268 | name: "CumanaSCSI-1" , dev: 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, dev: &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(instance: host); |
286 | out_unmap: |
287 | iounmap(priv(host)->io); |
288 | iounmap(priv(host)->pdma_io); |
289 | scsi_host_put(t: host); |
290 | out_release: |
291 | ecard_release_resources(ec); |
292 | out: |
293 | return ret; |
294 | } |
295 | |
296 | static 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(instance: host); |
307 | scsi_host_put(t: host); |
308 | iounmap(addr: base); |
309 | iounmap(addr: dma); |
310 | ecard_release_resources(ec); |
311 | } |
312 | |
313 | static const struct ecard_id cumanascsi1_cids[] = { |
314 | { MANU_CUMANA, PROD_CUMANA_SCSI_1 }, |
315 | { 0xffff, 0xffff } |
316 | }; |
317 | |
318 | static 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 | |
327 | static int __init cumanascsi_init(void) |
328 | { |
329 | return ecard_register_driver(&cumanascsi1_driver); |
330 | } |
331 | |
332 | static void __exit cumanascsi_exit(void) |
333 | { |
334 | ecard_remove_driver(&cumanascsi1_driver); |
335 | } |
336 | |
337 | module_init(cumanascsi_init); |
338 | module_exit(cumanascsi_exit); |
339 | |
340 | MODULE_DESCRIPTION("Cumana SCSI-1 driver for Acorn machines" ); |
341 | MODULE_LICENSE("GPL" ); |
342 | |