@ 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 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)