1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * linux/drivers/scsi/arm/arxescsi.c |
4 | * |
5 | * Copyright (C) 1997-2000 Russell King, Stefan Hanske |
6 | * |
7 | * This driver is based on experimentation. Hence, it may have made |
8 | * assumptions about the particular card that I have available, and |
9 | * may not be reliable! |
10 | * |
11 | * Changelog: |
12 | * 30-08-1997 RMK 0.0.0 Created, READONLY version as cumana_2.c |
13 | * 22-01-1998 RMK 0.0.1 Updated to 2.1.80 |
14 | * 15-04-1998 RMK 0.0.1 Only do PIO if FAS216 will allow it. |
15 | * 11-06-1998 SH 0.0.2 Changed to support ARXE 16-bit SCSI card |
16 | * enabled writing |
17 | * 01-01-2000 SH 0.1.0 Added *real* pseudo dma writing |
18 | * (arxescsi_pseudo_dma_write) |
19 | * 02-04-2000 RMK 0.1.1 Updated for new error handling code. |
20 | * 22-10-2000 SH Updated for new registering scheme. |
21 | */ |
22 | #include <linux/module.h> |
23 | #include <linux/blkdev.h> |
24 | #include <linux/kernel.h> |
25 | #include <linux/string.h> |
26 | #include <linux/ioport.h> |
27 | #include <linux/proc_fs.h> |
28 | #include <linux/unistd.h> |
29 | #include <linux/stat.h> |
30 | #include <linux/delay.h> |
31 | #include <linux/init.h> |
32 | #include <linux/interrupt.h> |
33 | |
34 | #include <asm/dma.h> |
35 | #include <asm/io.h> |
36 | #include <asm/ecard.h> |
37 | |
38 | #include <scsi/scsi.h> |
39 | #include <scsi/scsi_cmnd.h> |
40 | #include <scsi/scsi_device.h> |
41 | #include <scsi/scsi_eh.h> |
42 | #include <scsi/scsi_host.h> |
43 | #include <scsi/scsi_tcq.h> |
44 | #include "fas216.h" |
45 | |
46 | struct arxescsi_info { |
47 | FAS216_Info info; |
48 | struct expansion_card *ec; |
49 | void __iomem *base; |
50 | }; |
51 | |
52 | #define DMADATA_OFFSET (0x200) |
53 | |
54 | #define DMASTAT_OFFSET (0x600) |
55 | #define DMASTAT_DRQ (1 << 0) |
56 | |
57 | #define CSTATUS_IRQ (1 << 0) |
58 | |
59 | #define VERSION "1.10 (23/01/2003 2.5.57)" |
60 | |
61 | /* |
62 | * Function: int arxescsi_dma_setup(host, SCpnt, direction, min_type) |
63 | * Purpose : initialises DMA/PIO |
64 | * Params : host - host |
65 | * SCpnt - command |
66 | * direction - DMA on to/off of card |
67 | * min_type - minimum DMA support that we must have for this transfer |
68 | * Returns : 0 if we should not set CMD_WITHDMA for transfer info command |
69 | */ |
70 | static fasdmatype_t |
71 | arxescsi_dma_setup(struct Scsi_Host *host, struct scsi_pointer *SCp, |
72 | fasdmadir_t direction, fasdmatype_t min_type) |
73 | { |
74 | /* |
75 | * We don't do real DMA |
76 | */ |
77 | return fasdma_pseudo; |
78 | } |
79 | |
80 | static void arxescsi_pseudo_dma_write(unsigned char *addr, void __iomem *base) |
81 | { |
82 | __asm__ __volatile__( |
83 | " stmdb sp!, {r0-r12}\n" |
84 | " mov r3, %0\n" |
85 | " mov r1, %1\n" |
86 | " add r2, r1, #512\n" |
87 | " mov r4, #256\n" |
88 | ".loop_1: ldmia r3!, {r6, r8, r10, r12}\n" |
89 | " mov r5, r6, lsl #16\n" |
90 | " mov r7, r8, lsl #16\n" |
91 | ".loop_2: ldrb r0, [r1, #1536]\n" |
92 | " tst r0, #1\n" |
93 | " beq .loop_2\n" |
94 | " stmia r2, {r5-r8}\n\t" |
95 | " mov r9, r10, lsl #16\n" |
96 | " mov r11, r12, lsl #16\n" |
97 | ".loop_3: ldrb r0, [r1, #1536]\n" |
98 | " tst r0, #1\n" |
99 | " beq .loop_3\n" |
100 | " stmia r2, {r9-r12}\n" |
101 | " subs r4, r4, #16\n" |
102 | " bne .loop_1\n" |
103 | " ldmia sp!, {r0-r12}\n" |
104 | : |
105 | : "r" (addr), "r" (base)); |
106 | } |
107 | |
108 | /* |
109 | * Function: int arxescsi_dma_pseudo(host, SCpnt, direction, transfer) |
110 | * Purpose : handles pseudo DMA |
111 | * Params : host - host |
112 | * SCpnt - command |
113 | * direction - DMA on to/off of card |
114 | * transfer - minimum number of bytes we expect to transfer |
115 | */ |
116 | static void |
117 | arxescsi_dma_pseudo(struct Scsi_Host *host, struct scsi_pointer *SCp, |
118 | fasdmadir_t direction, int transfer) |
119 | { |
120 | struct arxescsi_info *info = (struct arxescsi_info *)host->hostdata; |
121 | unsigned int length, error = 0; |
122 | void __iomem *base = info->info.scsi.io_base; |
123 | unsigned char *addr; |
124 | |
125 | length = SCp->this_residual; |
126 | addr = SCp->ptr; |
127 | |
128 | if (direction == DMA_OUT) { |
129 | unsigned int word; |
130 | while (length > 256) { |
131 | if (readb(addr: base + 0x80) & STAT_INT) { |
132 | error = 1; |
133 | break; |
134 | } |
135 | arxescsi_pseudo_dma_write(addr, base); |
136 | addr += 256; |
137 | length -= 256; |
138 | } |
139 | |
140 | if (!error) |
141 | while (length > 0) { |
142 | if (readb(addr: base + 0x80) & STAT_INT) |
143 | break; |
144 | |
145 | if (!(readb(addr: base + DMASTAT_OFFSET) & DMASTAT_DRQ)) |
146 | continue; |
147 | |
148 | word = *addr | *(addr + 1) << 8; |
149 | |
150 | writew(val: word, addr: base + DMADATA_OFFSET); |
151 | if (length > 1) { |
152 | addr += 2; |
153 | length -= 2; |
154 | } else { |
155 | addr += 1; |
156 | length -= 1; |
157 | } |
158 | } |
159 | } |
160 | else { |
161 | if (transfer && (transfer & 255)) { |
162 | while (length >= 256) { |
163 | if (readb(addr: base + 0x80) & STAT_INT) { |
164 | error = 1; |
165 | break; |
166 | } |
167 | |
168 | if (!(readb(addr: base + DMASTAT_OFFSET) & DMASTAT_DRQ)) |
169 | continue; |
170 | |
171 | readsw(addr: base + DMADATA_OFFSET, buffer: addr, count: 256 >> 1); |
172 | addr += 256; |
173 | length -= 256; |
174 | } |
175 | } |
176 | |
177 | if (!(error)) |
178 | while (length > 0) { |
179 | unsigned long word; |
180 | |
181 | if (readb(addr: base + 0x80) & STAT_INT) |
182 | break; |
183 | |
184 | if (!(readb(addr: base + DMASTAT_OFFSET) & DMASTAT_DRQ)) |
185 | continue; |
186 | |
187 | word = readw(addr: base + DMADATA_OFFSET); |
188 | *addr++ = word; |
189 | if (--length > 0) { |
190 | *addr++ = word >> 8; |
191 | length --; |
192 | } |
193 | } |
194 | } |
195 | } |
196 | |
197 | /* |
198 | * Function: int arxescsi_dma_stop(host, SCpnt) |
199 | * Purpose : stops DMA/PIO |
200 | * Params : host - host |
201 | * SCpnt - command |
202 | */ |
203 | static void arxescsi_dma_stop(struct Scsi_Host *host, struct scsi_pointer *SCp) |
204 | { |
205 | /* |
206 | * no DMA to stop |
207 | */ |
208 | } |
209 | |
210 | /* |
211 | * Function: const char *arxescsi_info(struct Scsi_Host * host) |
212 | * Purpose : returns a descriptive string about this interface, |
213 | * Params : host - driver host structure to return info for. |
214 | * Returns : pointer to a static buffer containing null terminated string. |
215 | */ |
216 | static const char *arxescsi_info(struct Scsi_Host *host) |
217 | { |
218 | struct arxescsi_info *info = (struct arxescsi_info *)host->hostdata; |
219 | static char string[150]; |
220 | |
221 | sprintf(buf: string, fmt: "%s (%s) in slot %d v%s" , |
222 | host->hostt->name, info->info.scsi.type, info->ec->slot_no, |
223 | VERSION); |
224 | |
225 | return string; |
226 | } |
227 | |
228 | static int |
229 | arxescsi_show_info(struct seq_file *m, struct Scsi_Host *host) |
230 | { |
231 | struct arxescsi_info *info; |
232 | info = (struct arxescsi_info *)host->hostdata; |
233 | |
234 | seq_printf(m, fmt: "ARXE 16-bit SCSI driver v%s\n" , VERSION); |
235 | fas216_print_host(info: &info->info, m); |
236 | fas216_print_stats(info: &info->info, m); |
237 | fas216_print_devices(info: &info->info, m); |
238 | return 0; |
239 | } |
240 | |
241 | static const struct scsi_host_template arxescsi_template = { |
242 | .show_info = arxescsi_show_info, |
243 | .name = "ARXE SCSI card" , |
244 | .info = arxescsi_info, |
245 | .queuecommand = fas216_noqueue_command, |
246 | .eh_host_reset_handler = fas216_eh_host_reset, |
247 | .eh_bus_reset_handler = fas216_eh_bus_reset, |
248 | .eh_device_reset_handler = fas216_eh_device_reset, |
249 | .eh_abort_handler = fas216_eh_abort, |
250 | .cmd_size = sizeof(struct fas216_cmd_priv), |
251 | .can_queue = 0, |
252 | .this_id = 7, |
253 | .sg_tablesize = SG_ALL, |
254 | .dma_boundary = PAGE_SIZE - 1, |
255 | .proc_name = "arxescsi" , |
256 | }; |
257 | |
258 | static int arxescsi_probe(struct expansion_card *ec, const struct ecard_id *id) |
259 | { |
260 | struct Scsi_Host *host; |
261 | struct arxescsi_info *info; |
262 | void __iomem *base; |
263 | int ret; |
264 | |
265 | ret = ecard_request_resources(ec); |
266 | if (ret) |
267 | goto out; |
268 | |
269 | base = ecardm_iomap(ec, ECARD_RES_MEMC, 0, 0); |
270 | if (!base) { |
271 | ret = -ENOMEM; |
272 | goto out_region; |
273 | } |
274 | |
275 | host = scsi_host_alloc(&arxescsi_template, sizeof(struct arxescsi_info)); |
276 | if (!host) { |
277 | ret = -ENOMEM; |
278 | goto out_region; |
279 | } |
280 | |
281 | info = (struct arxescsi_info *)host->hostdata; |
282 | info->ec = ec; |
283 | info->base = base; |
284 | |
285 | info->info.scsi.io_base = base + 0x2000; |
286 | info->info.scsi.irq = 0; |
287 | info->info.scsi.dma = NO_DMA; |
288 | info->info.scsi.io_shift = 5; |
289 | info->info.ifcfg.clockrate = 24; /* MHz */ |
290 | info->info.ifcfg.select_timeout = 255; |
291 | info->info.ifcfg.asyncperiod = 200; /* ns */ |
292 | info->info.ifcfg.sync_max_depth = 0; |
293 | info->info.ifcfg.cntl3 = CNTL3_FASTSCSI | CNTL3_FASTCLK; |
294 | info->info.ifcfg.disconnect_ok = 0; |
295 | info->info.ifcfg.wide_max_size = 0; |
296 | info->info.ifcfg.capabilities = FASCAP_PSEUDODMA; |
297 | info->info.dma.setup = arxescsi_dma_setup; |
298 | info->info.dma.pseudo = arxescsi_dma_pseudo; |
299 | info->info.dma.stop = arxescsi_dma_stop; |
300 | |
301 | ec->irqaddr = base; |
302 | ec->irqmask = CSTATUS_IRQ; |
303 | |
304 | ret = fas216_init(instance: host); |
305 | if (ret) |
306 | goto out_unregister; |
307 | |
308 | ret = fas216_add(instance: host, dev: &ec->dev); |
309 | if (ret == 0) |
310 | goto out; |
311 | |
312 | fas216_release(instance: host); |
313 | out_unregister: |
314 | scsi_host_put(t: host); |
315 | out_region: |
316 | ecard_release_resources(ec); |
317 | out: |
318 | return ret; |
319 | } |
320 | |
321 | static void arxescsi_remove(struct expansion_card *ec) |
322 | { |
323 | struct Scsi_Host *host = ecard_get_drvdata(ec); |
324 | |
325 | ecard_set_drvdata(ec, NULL); |
326 | fas216_remove(instance: host); |
327 | |
328 | fas216_release(instance: host); |
329 | scsi_host_put(t: host); |
330 | ecard_release_resources(ec); |
331 | } |
332 | |
333 | static const struct ecard_id arxescsi_cids[] = { |
334 | { MANU_ARXE, PROD_ARXE_SCSI }, |
335 | { 0xffff, 0xffff }, |
336 | }; |
337 | |
338 | static struct ecard_driver arxescsi_driver = { |
339 | .probe = arxescsi_probe, |
340 | .remove = arxescsi_remove, |
341 | .id_table = arxescsi_cids, |
342 | .drv = { |
343 | .name = "arxescsi" , |
344 | }, |
345 | }; |
346 | |
347 | static int __init init_arxe_scsi_driver(void) |
348 | { |
349 | return ecard_register_driver(&arxescsi_driver); |
350 | } |
351 | |
352 | static void __exit exit_arxe_scsi_driver(void) |
353 | { |
354 | ecard_remove_driver(&arxescsi_driver); |
355 | } |
356 | |
357 | module_init(init_arxe_scsi_driver); |
358 | module_exit(exit_arxe_scsi_driver); |
359 | |
360 | MODULE_AUTHOR("Stefan Hanske" ); |
361 | MODULE_DESCRIPTION("ARXESCSI driver for Acorn machines" ); |
362 | MODULE_LICENSE("GPL" ); |
363 | |
364 | |