diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/scsi/atari_dma_emul.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/scsi/atari_dma_emul.c')
-rw-r--r-- | drivers/scsi/atari_dma_emul.c | 466 |
1 files changed, 466 insertions, 0 deletions
diff --git a/drivers/scsi/atari_dma_emul.c b/drivers/scsi/atari_dma_emul.c new file mode 100644 index 000000000000..7026045527fd --- /dev/null +++ b/drivers/scsi/atari_dma_emul.c | |||
@@ -0,0 +1,466 @@ | |||
1 | /* | ||
2 | * atari_dma_emul.c -- TT SCSI DMA emulator for the Hades. | ||
3 | * | ||
4 | * Copyright 1997 Wout Klaren <W.Klaren@inter.nl.net> | ||
5 | * | ||
6 | * This file is subject to the terms and conditions of the GNU General Public | ||
7 | * License. See the file COPYING in the main directory of this archive | ||
8 | * for more details. | ||
9 | * | ||
10 | * This code was written using the Hades TOS source code as a | ||
11 | * reference. This source code can be found on the home page | ||
12 | * of Medusa Computer Systems. | ||
13 | * | ||
14 | * Version 0.1, 1997-09-24. | ||
15 | * | ||
16 | * This code should be considered experimental. It has only been | ||
17 | * tested on a Hades with a 68060. It might not work on a Hades | ||
18 | * with a 68040. Make backups of your hard drives before using | ||
19 | * this code. | ||
20 | */ | ||
21 | |||
22 | #include <asm/uaccess.h> | ||
23 | |||
24 | #define hades_dma_ctrl (*(unsigned char *) 0xffff8717) | ||
25 | #define hades_psdm_reg (*(unsigned char *) 0xffff8741) | ||
26 | |||
27 | #define TRANSFER_SIZE 16 | ||
28 | |||
29 | struct m68040_frame { | ||
30 | unsigned long effaddr; /* effective address */ | ||
31 | unsigned short ssw; /* special status word */ | ||
32 | unsigned short wb3s; /* write back 3 status */ | ||
33 | unsigned short wb2s; /* write back 2 status */ | ||
34 | unsigned short wb1s; /* write back 1 status */ | ||
35 | unsigned long faddr; /* fault address */ | ||
36 | unsigned long wb3a; /* write back 3 address */ | ||
37 | unsigned long wb3d; /* write back 3 data */ | ||
38 | unsigned long wb2a; /* write back 2 address */ | ||
39 | unsigned long wb2d; /* write back 2 data */ | ||
40 | unsigned long wb1a; /* write back 1 address */ | ||
41 | unsigned long wb1dpd0; /* write back 1 data/push data 0*/ | ||
42 | unsigned long pd1; /* push data 1*/ | ||
43 | unsigned long pd2; /* push data 2*/ | ||
44 | unsigned long pd3; /* push data 3*/ | ||
45 | }; | ||
46 | |||
47 | static void writeback (unsigned short wbs, unsigned long wba, | ||
48 | unsigned long wbd, void *old_buserr) | ||
49 | { | ||
50 | mm_segment_t fs = get_fs(); | ||
51 | static void *save_buserr; | ||
52 | |||
53 | __asm__ __volatile__ ("movec.l %%vbr,%%a0\n\t" | ||
54 | "move.l %0,8(%%a0)\n\t" | ||
55 | : | ||
56 | : "r" (&&bus_error) | ||
57 | : "a0" ); | ||
58 | |||
59 | save_buserr = old_buserr; | ||
60 | |||
61 | set_fs (MAKE_MM_SEG(wbs & WBTM_040)); | ||
62 | |||
63 | switch (wbs & WBSIZ_040) { | ||
64 | case BA_SIZE_BYTE: | ||
65 | put_user (wbd & 0xff, (char *)wba); | ||
66 | break; | ||
67 | case BA_SIZE_WORD: | ||
68 | put_user (wbd & 0xffff, (short *)wba); | ||
69 | break; | ||
70 | case BA_SIZE_LONG: | ||
71 | put_user (wbd, (int *)wba); | ||
72 | break; | ||
73 | } | ||
74 | |||
75 | set_fs (fs); | ||
76 | return; | ||
77 | |||
78 | bus_error: | ||
79 | __asm__ __volatile__ ("cmp.l %0,2(%%sp)\n\t" | ||
80 | "bcs.s .jump_old\n\t" | ||
81 | "cmp.l %1,2(%%sp)\n\t" | ||
82 | "bls.s .restore_old\n" | ||
83 | ".jump_old:\n\t" | ||
84 | "move.l %2,-(%%sp)\n\t" | ||
85 | "rts\n" | ||
86 | ".restore_old:\n\t" | ||
87 | "move.l %%a0,-(%%sp)\n\t" | ||
88 | "movec.l %%vbr,%%a0\n\t" | ||
89 | "move.l %2,8(%%a0)\n\t" | ||
90 | "move.l (%%sp)+,%%a0\n\t" | ||
91 | "rte\n\t" | ||
92 | : | ||
93 | : "i" (writeback), "i" (&&bus_error), | ||
94 | "m" (save_buserr) ); | ||
95 | } | ||
96 | |||
97 | /* | ||
98 | * static inline void set_restdata_reg(unsigned char *cur_addr) | ||
99 | * | ||
100 | * Set the rest data register if necessary. | ||
101 | */ | ||
102 | |||
103 | static inline void set_restdata_reg(unsigned char *cur_addr) | ||
104 | { | ||
105 | if (((long) cur_addr & ~3) != 0) | ||
106 | tt_scsi_dma.dma_restdata = | ||
107 | *((unsigned long *) ((long) cur_addr & ~3)); | ||
108 | } | ||
109 | |||
110 | /* | ||
111 | * void hades_dma_emulator(int irq, void *dummy, struct pt_regs *fp) | ||
112 | * | ||
113 | * This code emulates TT SCSI DMA on the Hades. | ||
114 | * | ||
115 | * Note the following: | ||
116 | * | ||
117 | * 1. When there is no byte available to read from the SCSI bus, or | ||
118 | * when a byte cannot yet bet written to the SCSI bus, a bus | ||
119 | * error occurs when reading or writing the pseudo DMA data | ||
120 | * register (hades_psdm_reg). We have to catch this bus error | ||
121 | * and try again to read or write the byte. If after several tries | ||
122 | * we still get a bus error, the interrupt handler is left. When | ||
123 | * the byte can be read or written, the interrupt handler is | ||
124 | * called again. | ||
125 | * | ||
126 | * 2. The SCSI interrupt must be disabled in this interrupt handler. | ||
127 | * | ||
128 | * 3. If we set the EOP signal, the SCSI controller still expects one | ||
129 | * byte to be read or written. Therefore the last byte is transferred | ||
130 | * separately, after setting the EOP signal. | ||
131 | * | ||
132 | * 4. When this function is left, the address pointer (start_addr) is | ||
133 | * converted to a physical address. Because it points one byte | ||
134 | * further than the last transferred byte, it can point outside the | ||
135 | * current page. If virt_to_phys() is called with this address we | ||
136 | * might get an access error. Therefore virt_to_phys() is called with | ||
137 | * start_addr - 1 if the count has reached zero. The result is | ||
138 | * increased with one. | ||
139 | */ | ||
140 | |||
141 | static irqreturn_t hades_dma_emulator(int irq, void *dummy, struct pt_regs *fp) | ||
142 | { | ||
143 | unsigned long dma_base; | ||
144 | register unsigned long dma_cnt asm ("d3"); | ||
145 | static long save_buserr; | ||
146 | register unsigned long save_sp asm ("d4"); | ||
147 | register int tries asm ("d5"); | ||
148 | register unsigned char *start_addr asm ("a3"), *end_addr asm ("a4"); | ||
149 | register unsigned char *eff_addr; | ||
150 | register unsigned char *psdm_reg; | ||
151 | unsigned long rem; | ||
152 | |||
153 | atari_disable_irq(IRQ_TT_MFP_SCSI); | ||
154 | |||
155 | /* | ||
156 | * Read the dma address and count registers. | ||
157 | */ | ||
158 | |||
159 | dma_base = SCSI_DMA_READ_P(dma_addr); | ||
160 | dma_cnt = SCSI_DMA_READ_P(dma_cnt); | ||
161 | |||
162 | /* | ||
163 | * Check if DMA is still enabled. | ||
164 | */ | ||
165 | |||
166 | if ((tt_scsi_dma.dma_ctrl & 2) == 0) | ||
167 | { | ||
168 | atari_enable_irq(IRQ_TT_MFP_SCSI); | ||
169 | return IRQ_HANDLED; | ||
170 | } | ||
171 | |||
172 | if (dma_cnt == 0) | ||
173 | { | ||
174 | printk(KERN_NOTICE "DMA emulation: count is zero.\n"); | ||
175 | tt_scsi_dma.dma_ctrl &= 0xfd; /* DMA ready. */ | ||
176 | atari_enable_irq(IRQ_TT_MFP_SCSI); | ||
177 | return IRQ_HANDLED; | ||
178 | } | ||
179 | |||
180 | /* | ||
181 | * Install new bus error routine. | ||
182 | */ | ||
183 | |||
184 | __asm__ __volatile__ ("movec.l %%vbr,%%a0\n\t" | ||
185 | "move.l 8(%%a0),%0\n\t" | ||
186 | "move.l %1,8(%%a0)\n\t" | ||
187 | : "=&r" (save_buserr) | ||
188 | : "r" (&&scsi_bus_error) | ||
189 | : "a0" ); | ||
190 | |||
191 | hades_dma_ctrl &= 0xfc; /* Bus error and EOP off. */ | ||
192 | |||
193 | /* | ||
194 | * Save the stack pointer. | ||
195 | */ | ||
196 | |||
197 | __asm__ __volatile__ ("move.l %%sp,%0\n\t" | ||
198 | : "=&r" (save_sp) ); | ||
199 | |||
200 | tries = 100; /* Maximum number of bus errors. */ | ||
201 | start_addr = phys_to_virt(dma_base); | ||
202 | end_addr = start_addr + dma_cnt; | ||
203 | |||
204 | scsi_loop: | ||
205 | dma_cnt--; | ||
206 | rem = dma_cnt & (TRANSFER_SIZE - 1); | ||
207 | dma_cnt &= ~(TRANSFER_SIZE - 1); | ||
208 | psdm_reg = &hades_psdm_reg; | ||
209 | |||
210 | if (tt_scsi_dma.dma_ctrl & 1) /* Read or write? */ | ||
211 | { | ||
212 | /* | ||
213 | * SCSI write. Abort when count is zero. | ||
214 | */ | ||
215 | |||
216 | switch (rem) | ||
217 | { | ||
218 | case 0: | ||
219 | while (dma_cnt > 0) | ||
220 | { | ||
221 | dma_cnt -= TRANSFER_SIZE; | ||
222 | |||
223 | *psdm_reg = *start_addr++; | ||
224 | case 15: | ||
225 | *psdm_reg = *start_addr++; | ||
226 | case 14: | ||
227 | *psdm_reg = *start_addr++; | ||
228 | case 13: | ||
229 | *psdm_reg = *start_addr++; | ||
230 | case 12: | ||
231 | *psdm_reg = *start_addr++; | ||
232 | case 11: | ||
233 | *psdm_reg = *start_addr++; | ||
234 | case 10: | ||
235 | *psdm_reg = *start_addr++; | ||
236 | case 9: | ||
237 | *psdm_reg = *start_addr++; | ||
238 | case 8: | ||
239 | *psdm_reg = *start_addr++; | ||
240 | case 7: | ||
241 | *psdm_reg = *start_addr++; | ||
242 | case 6: | ||
243 | *psdm_reg = *start_addr++; | ||
244 | case 5: | ||
245 | *psdm_reg = *start_addr++; | ||
246 | case 4: | ||
247 | *psdm_reg = *start_addr++; | ||
248 | case 3: | ||
249 | *psdm_reg = *start_addr++; | ||
250 | case 2: | ||
251 | *psdm_reg = *start_addr++; | ||
252 | case 1: | ||
253 | *psdm_reg = *start_addr++; | ||
254 | } | ||
255 | } | ||
256 | |||
257 | hades_dma_ctrl |= 1; /* Set EOP. */ | ||
258 | udelay(10); | ||
259 | *psdm_reg = *start_addr++; /* Dummy byte. */ | ||
260 | tt_scsi_dma.dma_ctrl &= 0xfd; /* DMA ready. */ | ||
261 | } | ||
262 | else | ||
263 | { | ||
264 | /* | ||
265 | * SCSI read. Abort when count is zero. | ||
266 | */ | ||
267 | |||
268 | switch (rem) | ||
269 | { | ||
270 | case 0: | ||
271 | while (dma_cnt > 0) | ||
272 | { | ||
273 | dma_cnt -= TRANSFER_SIZE; | ||
274 | |||
275 | *start_addr++ = *psdm_reg; | ||
276 | case 15: | ||
277 | *start_addr++ = *psdm_reg; | ||
278 | case 14: | ||
279 | *start_addr++ = *psdm_reg; | ||
280 | case 13: | ||
281 | *start_addr++ = *psdm_reg; | ||
282 | case 12: | ||
283 | *start_addr++ = *psdm_reg; | ||
284 | case 11: | ||
285 | *start_addr++ = *psdm_reg; | ||
286 | case 10: | ||
287 | *start_addr++ = *psdm_reg; | ||
288 | case 9: | ||
289 | *start_addr++ = *psdm_reg; | ||
290 | case 8: | ||
291 | *start_addr++ = *psdm_reg; | ||
292 | case 7: | ||
293 | *start_addr++ = *psdm_reg; | ||
294 | case 6: | ||
295 | *start_addr++ = *psdm_reg; | ||
296 | case 5: | ||
297 | *start_addr++ = *psdm_reg; | ||
298 | case 4: | ||
299 | *start_addr++ = *psdm_reg; | ||
300 | case 3: | ||
301 | *start_addr++ = *psdm_reg; | ||
302 | case 2: | ||
303 | *start_addr++ = *psdm_reg; | ||
304 | case 1: | ||
305 | *start_addr++ = *psdm_reg; | ||
306 | } | ||
307 | } | ||
308 | |||
309 | hades_dma_ctrl |= 1; /* Set EOP. */ | ||
310 | udelay(10); | ||
311 | *start_addr++ = *psdm_reg; | ||
312 | tt_scsi_dma.dma_ctrl &= 0xfd; /* DMA ready. */ | ||
313 | |||
314 | set_restdata_reg(start_addr); | ||
315 | } | ||
316 | |||
317 | if (start_addr != end_addr) | ||
318 | printk(KERN_CRIT "DMA emulation: FATAL: Count is not zero at end of transfer.\n"); | ||
319 | |||
320 | dma_cnt = end_addr - start_addr; | ||
321 | |||
322 | scsi_end: | ||
323 | dma_base = (dma_cnt == 0) ? virt_to_phys(start_addr - 1) + 1 : | ||
324 | virt_to_phys(start_addr); | ||
325 | |||
326 | SCSI_DMA_WRITE_P(dma_addr, dma_base); | ||
327 | SCSI_DMA_WRITE_P(dma_cnt, dma_cnt); | ||
328 | |||
329 | /* | ||
330 | * Restore old bus error routine. | ||
331 | */ | ||
332 | |||
333 | __asm__ __volatile__ ("movec.l %%vbr,%%a0\n\t" | ||
334 | "move.l %0,8(%%a0)\n\t" | ||
335 | : | ||
336 | : "r" (save_buserr) | ||
337 | : "a0" ); | ||
338 | |||
339 | atari_enable_irq(IRQ_TT_MFP_SCSI); | ||
340 | |||
341 | return IRQ_HANDLED; | ||
342 | |||
343 | scsi_bus_error: | ||
344 | /* | ||
345 | * First check if the bus error is caused by our code. | ||
346 | * If not, call the original handler. | ||
347 | */ | ||
348 | |||
349 | __asm__ __volatile__ ("cmp.l %0,2(%%sp)\n\t" | ||
350 | "bcs.s .old_vector\n\t" | ||
351 | "cmp.l %1,2(%%sp)\n\t" | ||
352 | "bls.s .scsi_buserr\n" | ||
353 | ".old_vector:\n\t" | ||
354 | "move.l %2,-(%%sp)\n\t" | ||
355 | "rts\n" | ||
356 | ".scsi_buserr:\n\t" | ||
357 | : | ||
358 | : "i" (&&scsi_loop), "i" (&&scsi_end), | ||
359 | "m" (save_buserr) ); | ||
360 | |||
361 | if (CPU_IS_060) | ||
362 | { | ||
363 | /* | ||
364 | * Get effective address and restore the stack. | ||
365 | */ | ||
366 | |||
367 | __asm__ __volatile__ ("move.l 8(%%sp),%0\n\t" | ||
368 | "move.l %1,%%sp\n\t" | ||
369 | : "=a&" (eff_addr) | ||
370 | : "r" (save_sp) ); | ||
371 | } | ||
372 | else | ||
373 | { | ||
374 | register struct m68040_frame *frame; | ||
375 | |||
376 | __asm__ __volatile__ ("lea 8(%%sp),%0\n\t" | ||
377 | : "=a&" (frame) ); | ||
378 | |||
379 | if (tt_scsi_dma.dma_ctrl & 1) | ||
380 | { | ||
381 | /* | ||
382 | * Bus error while writing. | ||
383 | */ | ||
384 | |||
385 | if (frame->wb3s & WBV_040) | ||
386 | { | ||
387 | if (frame->wb3a == (long) &hades_psdm_reg) | ||
388 | start_addr--; | ||
389 | else | ||
390 | writeback(frame->wb3s, frame->wb3a, | ||
391 | frame->wb3d, &&scsi_bus_error); | ||
392 | } | ||
393 | |||
394 | if (frame->wb2s & WBV_040) | ||
395 | { | ||
396 | if (frame->wb2a == (long) &hades_psdm_reg) | ||
397 | start_addr--; | ||
398 | else | ||
399 | writeback(frame->wb2s, frame->wb2a, | ||
400 | frame->wb2d, &&scsi_bus_error); | ||
401 | } | ||
402 | |||
403 | if (frame->wb1s & WBV_040) | ||
404 | { | ||
405 | if (frame->wb1a == (long) &hades_psdm_reg) | ||
406 | start_addr--; | ||
407 | } | ||
408 | } | ||
409 | else | ||
410 | { | ||
411 | /* | ||
412 | * Bus error while reading. | ||
413 | */ | ||
414 | |||
415 | if (frame->wb3s & WBV_040) | ||
416 | writeback(frame->wb3s, frame->wb3a, | ||
417 | frame->wb3d, &&scsi_bus_error); | ||
418 | } | ||
419 | |||
420 | eff_addr = (unsigned char *) frame->faddr; | ||
421 | |||
422 | __asm__ __volatile__ ("move.l %0,%%sp\n\t" | ||
423 | : | ||
424 | : "r" (save_sp) ); | ||
425 | } | ||
426 | |||
427 | dma_cnt = end_addr - start_addr; | ||
428 | |||
429 | if (eff_addr == &hades_psdm_reg) | ||
430 | { | ||
431 | /* | ||
432 | * Bus error occurred while reading the pseudo | ||
433 | * DMA register. Time out. | ||
434 | */ | ||
435 | |||
436 | tries--; | ||
437 | |||
438 | if (tries <= 0) | ||
439 | { | ||
440 | if ((tt_scsi_dma.dma_ctrl & 1) == 0) /* Read or write? */ | ||
441 | set_restdata_reg(start_addr); | ||
442 | |||
443 | if (dma_cnt <= 1) | ||
444 | printk(KERN_CRIT "DMA emulation: Fatal " | ||
445 | "error while %s the last byte.\n", | ||
446 | (tt_scsi_dma.dma_ctrl & 1) | ||
447 | ? "writing" : "reading"); | ||
448 | |||
449 | goto scsi_end; | ||
450 | } | ||
451 | else | ||
452 | goto scsi_loop; | ||
453 | } | ||
454 | else | ||
455 | { | ||
456 | /* | ||
457 | * Bus error during pseudo DMA transfer. | ||
458 | * Terminate the DMA transfer. | ||
459 | */ | ||
460 | |||
461 | hades_dma_ctrl |= 3; /* Set EOP and bus error. */ | ||
462 | if ((tt_scsi_dma.dma_ctrl & 1) == 0) /* Read or write? */ | ||
463 | set_restdata_reg(start_addr); | ||
464 | goto scsi_end; | ||
465 | } | ||
466 | } | ||