OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [drivers/] [scsi/] [atari_dma_emul.c] - Blame information for rev 1774

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
/*
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 void 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;
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;
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;
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
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.