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