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