Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.12-rc5 466 lines 11 kB view raw
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 29struct 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 47static 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 78bus_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 103static 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 141static 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 204scsi_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 322scsi_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 343scsi_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}