1
linux/drivers/acorn/block/mfm.S
Linus Torvalds 1da177e4c3 Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
2005-04-16 15:20:36 -07:00

163 lines
4.4 KiB
ArmAsm

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