| @ Read/Write DMA code for the ST506/MFM hard drive controllers on the A400 Acorn Archimedes |
| @ motherboard and on ST506 expansion podules. |
| @ (c) David Alan Gilbert (linux@treblig.org) 1996-1999 |
| |
| #include <asm/assembler.h> |
| |
| hdc63463_irqdata: |
| @ Controller base address |
| .global hdc63463_baseaddress |
| hdc63463_baseaddress: |
| .word 0 |
| |
| .global hdc63463_irqpolladdress |
| hdc63463_irqpolladdress: |
| .word 0 |
| |
| .global hdc63463_irqpollmask |
| hdc63463_irqpollmask: |
| .word 0 |
| |
| @ where to read/write data from the kernel data space |
| .global hdc63463_dataptr |
| hdc63463_dataptr: |
| .word 0 |
| |
| @ Number of bytes left to transfer |
| .global hdc63463_dataleft |
| hdc63463_dataleft: |
| .word 0 |
| |
| @ ------------------------------------------------------------------------- |
| @ hdc63463_writedma: DMA from host to controller |
| @ internal reg usage: r0=hdc base address, r1=irq poll address, r2=poll mask |
| @ r3=data ptr, r4=data left, r5,r6=temporary |
| .global hdc63463_writedma |
| hdc63463_writedma: |
| stmfd sp!,{r4-r7} |
| adr r5,hdc63463_irqdata |
| ldmia r5,{r0,r1,r2,r3,r4} |
| |
| writedma_again: |
| |
| @ test number of remaining bytes to transfer |
| cmp r4,#0 |
| beq writedma_end |
| bmi writedma_end |
| |
| @ Check the hdc is interrupting |
| ldrb r5,[r1,#0] |
| tst r5,r2 |
| beq writedma_end |
| |
| @ Transfer a block of upto 256 bytes |
| cmp r4,#256 |
| movlt r7,r4 |
| movge r7,#256 |
| |
| @ Check the hdc is still busy and command has not ended and no errors |
| ldr r5,[r0,#32] @ Status reg - 16 bit - its the top few bits which are status |
| @ think we should continue DMA until it drops busy - perhaps this was |
| @ the main problem with corrected errors causing a hang |
| @tst r5,#0x3c00 @ Test for things which should be off |
| @bne writedma_end |
| and r5,r5,#0x8000 @ This is test for things which should be on: Busy |
| cmp r5,#0x8000 |
| bne writedma_end |
| |
| @ Bytes remaining at end |
| sub r4,r4,r7 |
| |
| @ HDC Write register location |
| add r0,r0,#32+8 |
| |
| writedma_loop: |
| @ OK - pretty sure we should be doing this |
| |
| ldr r5,[r3],#4 @ Get a word to be written |
| @ get bottom half to be sent first |
| mov r6,r5,lsl#16 @ Separate the first 2 bytes |
| orr r2,r6,r6,lsr #16 @ Duplicate them in the bottom half of the word |
| @ now the top half |
| mov r6,r5,lsr#16 @ Get 2nd 2 bytes |
| orr r6,r6,r6,lsl#16 @ Duplicate |
| @str r6,[r0] @ to hdc |
| stmia r0,{r2,r6} |
| subs r7,r7,#4 @ Dec. number of bytes left |
| bne writedma_loop |
| |
| @ If we were too slow we had better go through again - DAG - took out with new interrupt routine |
| @ sub r0,r0,#32+8 |
| @ adr r2,hdc63463_irqdata |
| @ ldr r2,[r2,#8] |
| @ b writedma_again |
| |
| writedma_end: |
| adr r5,hdc63463_irqdata+12 |
| stmia r5,{r3,r4} |
| ldmfd sp!,{r4-r7} |
| RETINSTR(mov,pc,lr) |
| |
| @ ------------------------------------------------------------------------- |
| @ hdc63463_readdma: DMA from controller to host |
| @ internal reg usage: r0=hdc base address, r1=irq poll address, r2=poll mask |
| @ r3=data ptr, r4=data left, r5,r6=temporary |
| .global hdc63463_readdma |
| hdc63463_readdma: |
| stmfd sp!,{r4-r7} |
| adr r5,hdc63463_irqdata |
| ldmia r5,{r0,r1,r2,r3,r4} |
| |
| readdma_again: |
| @ test number of remaining bytes to transfer |
| cmp r4,#0 |
| beq readdma_end |
| bmi readdma_end |
| |
| @ Check the hdc is interrupting |
| ldrb r5,[r1,#0] |
| tst r5,r2 |
| beq readdma_end |
| |
| @ Check the hdc is still busy and command has not ended and no errors |
| ldr r5,[r0,#32] @ Status reg - 16 bit - its the top few bits which are status |
| @ think we should continue DMA until it drops busy - perhaps this was |
| @ the main problem with corrected errors causing a hang |
| @tst r5,#0x3c00 @ Test for things which should be off |
| @bne readdma_end |
| and r5,r5,#0x8000 @ This is test for things which should be on: Busy |
| cmp r5,#0x8000 |
| bne readdma_end |
| |
| @ Transfer a block of upto 256 bytes |
| cmp r4,#256 |
| movlt r7,r4 |
| movge r7,#256 |
| |
| @ Bytes remaining at end |
| sub r4,r4,r7 |
| |
| @ Set a pointer to the data register in the HDC |
| add r0,r0,#8 |
| readdma_loop: |
| @ OK - pretty sure we should be doing this |
| ldmia r0,{r5,r6} |
| mov r5,r5,lsl#16 |
| mov r6,r6,lsl#16 |
| orr r6,r6,r5,lsr #16 |
| str r6,[r3],#4 |
| subs r7,r7,#4 @ Decrement bytes to go |
| bne readdma_loop |
| |
| @ Try reading multiple blocks - if this was fast enough then I do not think |
| @ this should help - NO taken out DAG - new interrupt handler has |
| @ non-consecutive memory blocks |
| @ sub r0,r0,#8 |
| @ b readdma_again |
| |
| readdma_end: |
| adr r5,hdc63463_irqdata+12 |
| stmia r5,{r3,r4} |
| ldmfd sp!,{r4-r7} |
| RETINSTR(mov,pc,lr) |