summaryrefslogtreecommitdiff
path: root/drivers/acorn/block/mfm.S
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/acorn/block/mfm.S')
-rw-r--r--drivers/acorn/block/mfm.S162
1 files changed, 162 insertions, 0 deletions
diff --git a/drivers/acorn/block/mfm.S b/drivers/acorn/block/mfm.S
new file mode 100644
index 00000000000..c90cbd41ce2
--- /dev/null
+++ b/drivers/acorn/block/mfm.S
@@ -0,0 +1,162 @@
+@ 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)