Commit 981cfe6d authored by Ben Avison's avatar Ben Avison
Browse files

SD support, and miscellaneous other changes

Detail:
  * Bugfix to HAL_FIQDisableAll - it wasn't clearing the FIQ register (would
    only have caused trouble in practice if the same device was subsequently
    enabled as an IRQ).
  * Added a load of memory barriers to s.Interrupts and s.Timers to conform
    to the requirement stated in 1.3 of the datasheet.
  * Added a HAL device for the Arasan SDHCI controller. Note that this does
    not currently work reliably, and results vary from card to card. High
    speed support is currently disabled until we are able to verify that it
    works reliably.
  * Added a sprinkling of "GET Hdr:ListOpts" because the space reserved for
    the SDHCI HAL device in hdr.StaticWS is determined by including
    Hdr:HALDevice and Hdr:SDHCIDevice, which need it.
  * When support for saving "CMOS" to the SD card is added, the ROM image
    file (kernel.img) is the only one we can count on the bootloader
    installing in memory, so I think we're going to have to work using the
    table in s.CMOS. Broadcom seems to like messing around with the space
    just after the processor vector table, so rather than adding a pointer
    to the table there, I've opted to mark it using a magic word.
Admin:
  Tested on a Raspberry Pi - as noted above, there are reliability issues.

Version 0.09. Tagged as 'BCM2835-0_09'
parent 35ce9ea3
......@@ -17,7 +17,7 @@
COMPONENT = BCM2835 HAL
TARGET = BCM2835
OBJS = Top CMOS Debug Display Interrupts Stubs Timers UART USB Video
OBJS = Top CLib CMOS Debug Display Interrupts SDIO Stubs Timers UART USB Video
HDRS =
CMHGFILE =
......
......@@ -11,13 +11,13 @@
GBLS Module_HelpVersion
GBLS Module_ComponentName
GBLS Module_ComponentPath
Module_MajorVersion SETS "0.08"
Module_Version SETA 8
Module_MajorVersion SETS "0.09"
Module_Version SETA 9
Module_MinorVersion SETS ""
Module_Date SETS "07 Jun 2012"
Module_ApplicationDate SETS "07-Jun-12"
Module_Date SETS "15 Jun 2012"
Module_ApplicationDate SETS "15-Jun-12"
Module_ComponentName SETS "BCM2835"
Module_ComponentPath SETS "mixed/RiscOS/Sources/HAL/BCM2835"
Module_FullVersion SETS "0.08"
Module_HelpVersion SETS "0.08 (07 Jun 2012)"
Module_FullVersion SETS "0.09"
Module_HelpVersion SETS "0.09 (15 Jun 2012)"
END
/* (0.08)
/* (0.09)
*
* This file is automatically maintained by srccommit, do not edit manually.
* Last processed by srccommit version: 1.1.
*
*/
#define Module_MajorVersion_CMHG 0.08
#define Module_MajorVersion_CMHG 0.09
#define Module_MinorVersion_CMHG
#define Module_Date_CMHG 07 Jun 2012
#define Module_Date_CMHG 15 Jun 2012
#define Module_MajorVersion "0.08"
#define Module_Version 8
#define Module_MajorVersion "0.09"
#define Module_Version 9
#define Module_MinorVersion ""
#define Module_Date "07 Jun 2012"
#define Module_Date "15 Jun 2012"
#define Module_ApplicationDate "07-Jun-12"
#define Module_ApplicationDate "15-Jun-12"
#define Module_ComponentName "BCM2835"
#define Module_ComponentPath "mixed/RiscOS/Sources/HAL/BCM2835"
#define Module_FullVersion "0.08"
#define Module_HelpVersion "0.08 (07 Jun 2012)"
#define Module_LibraryVersionInfo "0:8"
#define Module_FullVersion "0.09"
#define Module_HelpVersion "0.09 (15 Jun 2012)"
#define Module_LibraryVersionInfo "0:9"
......@@ -31,6 +31,8 @@
;
GET Hdr:OSEntries
GET Hdr:HALDevice
GET Hdr:SDHCIDevice
GET hdr.BCM2835
; Per-timer workspace layout
......@@ -108,6 +110,12 @@ WSPhysAddr # 4 ;physical address of HAL workspace
OSheader # 4
OSentries # 4*(HighestOSEntry+1)
SDHCIWriteInterval # 4 ; minimum counter ticks between writes
SDHCILastWriteCount # 4 ; counter value at last write
# (16-:INDEX:@):AND:15 ; align nicely
SDHCIDevice # HALDevice_SDHCISize ; see Hdr:SDHCIDevice
SDHCISlotInfo # HALDeviceSDHCI_SlotInfo_Size ; the controller has just the 1 slot
CurUnder # 32*4*32
CurShape # 32/4*32
......
;
; Copyright (c) 2012, RISC OS Open Ltd
; All rights reserved.
;
; Redistribution and use in source and binary forms, with or without
; modification, are permitted provided that the following conditions are met:
; * Redistributions of source code must retain the above copyright
; notice, this list of conditions and the following disclaimer.
; * Redistributions in binary form must reproduce the above copyright
; notice, this list of conditions and the following disclaimer in the
; documentation and/or other materials provided with the distribution.
; * Neither the name of RISC OS Open Ltd nor the names of its contributors
; may be used to endorse or promote products derived from this software
; without specific prior written permission.
;
; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
; ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
; LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
; SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
; CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
; ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
; POSSIBILITY OF SUCH DAMAGE.
;
EXPORT memcpy
AREA |Asm$$Code|, CODE, READONLY, PIC
; A really simple implementation, just so it has a consistent licence
; In:
; a1 = dest
; a2 = source
; a3 = size in bytes (assumed a multiple of 4)
; Out:
; a1 preserved
memcpy ROUT
MOV a4, a1
B %F10
01 LDR ip, [a2], #4
STR ip, [a4], #4
10 SUBS a3, a3, #4
BCS %B01
MOV pc, lr
END
......@@ -34,6 +34,8 @@
EXPORT cmos
UND #&C305 ; get it?
DCD cmos_end - cmos
cmos
DCD &eb00fe00
DCD &00001a00
......@@ -547,5 +549,6 @@ cmos
DCD &00000000
DCD &00000000
DCD &00000000
cmos_end
END
......@@ -37,6 +37,7 @@
AREA |ARM$$code|, CODE, READONLY, PIC
GET Hdr:ListOpts
GET hdr.BCM2835
[ HALDebug
......
......@@ -64,6 +64,7 @@ HAL_IRQEnable
CMN a1,#1
MOVEQ pc,lr
DoMemBarrier ip
LDR ip, IRQ_Base_Address
ADD ip, ip, #IRQ_EN1
MOV a2, #1
......@@ -73,12 +74,14 @@ HAL_IRQEnable
LDR a4, [ip, a3, LSL #2] ; get old enable mask
STR a2, [ip, a3, LSL #2] ; enable our bit
AND a1, a2, a4 ; test our bit in old mask
DoMemBarrier ip
MOV pc, lr
HAL_IRQDisable
CMN a1,#1
MOVEQ pc,lr
DoMemBarrier ip
LDR ip, IRQ_Base_Address
ADD ip, ip, #IRQ_DIS1
MOV a2, #1
......@@ -88,6 +91,7 @@ HAL_IRQDisable
LDR a4, [ip, a3, LSL #2] ; get old enable mask
STR a2, [ip, a3, LSL #2] ; disable our bit
AND a1, a2, a4 ; test our bit in old mask
DoMemBarrier ip
MOV pc, lr
HAL_IRQClear
......@@ -97,6 +101,7 @@ HAL_FIQClear
MOV pc, lr
HAL_IRQSource
DoMemBarrier ip
LDR a2, IRQ_Base_Address
LDRB a1, [a2, #IRQ_PENDB] ; note, LDRB so we ignore bits 8-31
CLZ a1, a1
......@@ -111,6 +116,7 @@ HAL_IRQSource
LDR a1, [a2, #IRQ_PEND1]
CLZ a1, a1
RSB a1, a1, #31
DoMemBarrier ip
MOV pc, lr
HAL_IRQStatus
......@@ -121,6 +127,7 @@ HAL_FIQStatus
CMN a1,#1
MOVEQ pc,lr
DoMemBarrier ip
LDR ip, IRQ_Base_Address
ASSERT IRQ_PENDB = 0
MOV a2, #1
......@@ -131,6 +138,7 @@ HAL_FIQStatus
ADDMI a3, a3, #3
LDR a4, [ip, a3, LSL #2] ; get pending mask
AND a1, a2, a4 ; test our bit
DoMemBarrier ip
MOV pc, lr
FIQEnable * 1<<7
......@@ -140,6 +148,7 @@ HAL_FIQEnable
CMN a1,#1
MOVEQ pc,lr
DoMemBarrier ip
LDR ip, IRQ_Base_Address
LDRB a3, [ip, #IRQ_FIQCTL] ; LDRB helpfully masks out bits 8-31 for us
......@@ -156,12 +165,14 @@ HAL_FIQEnable
MOVEQ a1, #1
MOVNE a1, #0
DoMemBarrier ip
MOV pc, lr
HAL_FIQDisable
CMN a1,#1
MOVEQ pc,lr
DoMemBarrier ip
LDR ip, IRQ_Base_Address
ADD ip, ip, #IRQ_DIS1
......@@ -179,11 +190,14 @@ HAL_FIQDisable
MOVEQ a1, #1
MOVNE a1, #0
DoMemBarrier ip
MOV pc, lr
HAL_FIQDisableAll
DoMemBarrier ip
LDR ip, IRQ_Base_Address
LDR a1, [ip, #IRQ_FIQCTL]
DoMemBarrier a2
TST a1, #FIQEnable
MOVEQ pc, lr ; FIQs weren't enabled
......@@ -194,13 +208,18 @@ HAL_FIQDisableAll
MOV a2, a2, LSL a3 ; bitmask
MOV a3, a1, LSR #5 ; shift to get relevant register
STR a2, [ip, a3, LSL #2] ; disable our bit
MOV a4, #0
STR a4, [ip, #IRQ_FIQCTL-IRQ_DIS1]
DoMemBarrier ip
MOV pc, lr
HAL_FIQSource
; There can only be one, the configured FIQ device
DoMemBarrier ip
LDR ip, IRQ_Base_Address
LDR a1, [ip, #IRQ_FIQCTL]
AND a1, a1, #FIQSourceMask
DoMemBarrier ip
MOV pc, lr
......
;
; Copyright (c) 2012, RISC OS Open Ltd
; All rights reserved.
;
; Redistribution and use in source and binary forms, with or without
; modification, are permitted provided that the following conditions are met:
; * Redistributions of source code must retain the above copyright
; notice, this list of conditions and the following disclaimer.
; * Redistributions in binary form must reproduce the above copyright
; notice, this list of conditions and the following disclaimer in the
; documentation and/or other materials provided with the distribution.
; * Neither the name of RISC OS Open Ltd nor the names of its contributors
; may be used to endorse or promote products derived from this software
; without specific prior written permission.
;
; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
; ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
; LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
; SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
; CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
; ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
; POSSIBILITY OF SUCH DAMAGE.
;
EXPORT SDIO_InitDevices
IMPORT memcpy
IMPORT HAL_CounterDelay
IMPORT HAL_CounterRead
; KEEP ; for debugging
GET Hdr:ListOpts
GET Hdr:Macros
GET hdr.BCM2835
GET hdr.StaticWS
sb RN 9
; Timings
; How long to wait during GPIO pullup/down register procedure
; This is documented as "150 cycles" but not what these are cycles of!
PUD_DELAY * 150 ; in us - wild guess
; How long to wait for SDCLK to stabilise, in ms
CLOCKSET_TIMEOUT * 20 ; in ms
; Frequency of the clock which we divide to produce SDCLK - found experimentally
INPUT_CLK_FREQ_KHZ * 40000 ; in kHz (period = 25 ns)
; Number of SDCLK cycles that must elapse between writes to registers (chip bug)
REGISTER_WRITE_PACE * 2
; Minimum value SDCLK will be set to, in kHz. Is used to set a safe gap between
; writes while reprogramming SDCLK
MIN_SDCLK * 400
; Base address of controller as an offset from PeriBase
EMMC_Base * &00300000
; GPIO pin assignments
GPIO_DAT3 * 53
GPIO_DAT2 * 52
GPIO_DAT1 * 51
GPIO_DAT0 * 50
GPIO_CMD * 49 ; needs pull-up iff in ID mode
GPIO_CLK * 48
GPIO_CD * 47 ; needs pull-up; active low
GPIO_STATUS_LED * 16 ; active low
; GPIO function select bitfields
GPIO_FSEL_INPUT * 0
GPIO_FSEL_OUTPUT * 1
GPIO_FSEL_ALT0 * 2
GPIO_FSEL_ALT1 * 3
GPIO_FSEL_ALT2 * 4
GPIO_FSEL_ALT3 * 5
GPIO_FSEL_ALT4 * 6
GPIO_FSEL_ALT5 * 7
GPIO_FSEL_SHIFT * 3
GPIO_FSEL_MASK * 7
; GPIO pull-up/down states
GPIO_PUD_OFF * 0
GPIO_PUD_DOWN * 1
GPIO_PUD_UP * 2
; Selected SD controller registers and bits
CONTROL1 * &2C
CONTROL1_CLK_FREQ8_SHIFT * 8
CONTROL1_CLK_FREQ8_MASK * &FF :SHL: CONTROL1_CLK_FREQ8_SHIFT
CONTROL1_CLK_FREQ_MS2_SHIFT * 6
CONTROL1_CLK_FREQ_MS2_MASK * 3 :SHL: CONTROL1_CLK_FREQ_MS2_SHIFT
CONTROL1_CLK_GENSEL * 1 :SHL: 5
CONTROL1_CLK_EN * 1 :SHL: 2
CONTROL1_CLK_STABLE * 1 :SHL: 1
CONTROL1_CLK_INTLEN * 1 :SHL: 0
; Capabilities bits
CAP_DDR50S * 1 :SHL: 34
CAP_SDR104S * 1 :SHL: 33
CAP_SDR50S * 1 :SHL: 32
CAP_VS18 * 1 :SHL: 26 ; Voltage support 1.8V
CAP_VS30 * 1 :SHL: 25 ; Voltage support 3.0V
CAP_VS33 * 1 :SHL: 24 ; Voltage support 3.3V
CAP_HSS * 1 :SHL: 21 ; High speed (52MHz) support
CAP_MBL_SHIFT * 16
CAP_MBL_512 * 0 :SHL: CAP_MBL_SHIFT ; 512-byte blocks
CAP_MBL_1024 * 1 :SHL: CAP_MBL_SHIFT ; 1024-byte blocks
CAP_MBL_2048 * 2 :SHL: CAP_MBL_SHIFT ; 2048-byte blocks
CAP_MBL_MASK * 3 :SHL: CAP_MBL_SHIFT
AREA |Asm$$Code|, CODE, READONLY, PIC
MACRO
$class HALDeviceField $field, $value
LCLS myvalue
[ "$value" = ""
myvalue SETS "$field"
|
myvalue SETS "$value"
]
ASSERT . - %A0 = HALDevice_$class$field
[ ?HALDevice_$class$field = 2
DCW $myvalue
ELIF ?HALDevice_$class$field = 4
DCD $myvalue
|
% ?HALDevice_$class$field
]
MEND
; Template for device block
Template
0
HALDeviceField Type, HALDeviceType_ExpCtl + HALDeviceExpCtl_SDIO
HALDeviceField ID, HALDeviceID_SDIO_SDHCI
HALDeviceField Location, HALDeviceBus_Sys + HALDeviceSysBus_AHB ; judging from the name of the unobtainable Arasan spec!
HALDeviceField Version, 0
HALDeviceField Description
HALDeviceField Address, 0 ; patched up at initialisation
HALDeviceField Reserved1, 0
HALDeviceField Activate
HALDeviceField Deactivate
HALDeviceField Reset
HALDeviceField Sleep
HALDeviceField Device, iDev_GPU_SDIO ; unconfirmed
HALDeviceField TestIRQ
HALDeviceField ClearIRQ, 0
HALDeviceField Reserved2, 0
SDHCI HALDeviceField Flags, HALDeviceSDHCI_Flag_32bit
SDHCI HALDeviceField Slots, 1
SDHCI HALDeviceField SlotInfo, 0 ; patched up at initialisation
SDHCI HALDeviceField WriteRegister
SDHCI HALDeviceField GetCapabilities
SDHCI HALDeviceField GetVddCapabilities, 0 ; our fake capabilities register suffices already
SDHCI HALDeviceField SetVdd
SDHCI HALDeviceField SetBusMode
SDHCI HALDeviceField PostPowerOn, 0
SDHCI HALDeviceField SetBusWidth, 0
SDHCI HALDeviceField GetMaxCurrent
SDHCI HALDeviceField SetSDCLK
SDHCI HALDeviceField GetTMCLK
SDHCI HALDeviceField SetActivity
SDHCI HALDeviceField GetCardDetect
SDHCI HALDeviceField GetWriteProtect
ASSERT . - %A0 = HALDevice_SDHCISize
; Init the SDHCI HAL device
SDIO_InitDevices ROUT
Push "lr"
ADR a1, SDHCIDevice
ADR a2, Template
MOV a3, #HALDevice_SDHCISize
BL memcpy
; Address and SlotInfo_StdRegs
LDR a1, PeriBase
ADD a1, a1, #EMMC_Base
STR a1, SDHCIDevice + HALDevice_Address
STR a1, SDHCISlotInfo + HALDeviceSDHCI_SlotInfo_StdRegs
; SlotInfo
ADR a1, SDHCISlotInfo
STR a1, SDHCIDevice + HALDevice_SDHCISlotInfo
; SlotInfo_Flags
MOV a1, #HALDeviceSDHCI_SlotFlag_Bus4Bit
STR a1, SDHCISlotInfo + HALDeviceSDHCI_SlotInfo_Flags
MOV a1, #0 ; flags
ADR a2, SDHCIDevice
Pull "lr"
LDR pc, OSentries+4*OS_AddDevice ; tail call
; a1 = GPIO pin
; a2 = function select number
PinMux_SetFunction ROUT
Push "lr"
MOV ip, #32 / GPIO_FSEL_SHIFT
DivRem a3, a1, ip, lr ; a3 = register, a1 = field in register
ASSERT GPIO_FSEL_SHIFT = 3
ADD a1, a1, a1, LSL #1
LDR a4, PeriBase
ADD a4, a4, #GPIO_Base
ADD a4, a4, #GPFSel0
MOV ip, #GPIO_FSEL_MASK
MOV ip, ip, LSL a1
LDR lr, [a4, a3, LSL #2]
BIC lr, lr, ip
ORR lr, lr, a2, LSL a1
STR lr, [a4, a3, LSL #2]
Pull "pc"
; a1 = GPIO pin
; a2 = pull type
GPIO_SetPull ROUT
Push "v1,lr"
MOV v1, a1
LDR ip, PeriBase
ADD ip, ip, #GPIO_Base
STR a2, [ip, #GPPUPDEN]
MOV a1, #PUD_DELAY
BL HAL_CounterDelay
LDR ip, PeriBase
MOV lr, #1
ADD ip, ip, #GPIO_Base
ADD ip, ip, #GPPUDCK0
AND a1, v1, #&1F
MOV a2, v1, LSR #5
MOV lr, lr, LSL a1
STR lr, [ip, a2, LSL #2]
MOV a1, #PUD_DELAY
BL HAL_CounterDelay
LDR ip, PeriBase
MOV a1, #0
ADD ip, ip, #GPIO_Base
ADD ip, ip, #GPPUDCK0
MOV a2, v1, LSR #5
; Manual recommends unsetting registers in this order, but I'm paranoid about an interrupt going off in between
MRS lr, CPSR
ORR v1, lr, #&C0 ; I and F bits
MSR CPSR_c, v1
STR a1, [ip, #GPPUPDEN - GPPUDCK0]
STR a1, [ip, a2, LSL #2]
MSR CPSR_c, lr
Pull "v1,pc"
; a1 = GPIO pin
; a2 = value to set (0 or 1)
GPIO_Output ROUT
LDR ip, PeriBase
TEQ a2, #0
ADD ip, ip, #GPIO_Base
ADDEQ ip, ip, #GPClr0
ADDNE ip, ip, #GPSet0
MOV a4, #1
MOV a2, a1, LSR #5
AND a1, a1, #&1F
MOV a4, a4, LSL a1
STR a4, [ip, a2, LSL #2]
MOV pc, lr
; a1 = GPIO pin
; on exit, a1 = 0 or 1
GPIO_Input ROUT
LDR ip, PeriBase
MOV a4, #1
MOV a2, a1, LSR #5
AND a1, a1, #&1F
ADD ip, ip, #GPIO_Base
ADD ip, ip, #GPLev0
LDR a3, [ip, a2, LSL #2]
AND a1, a4, a3, LSR a1
MOV pc, lr
Description DATA
= "Arasan SD host controller", 0
ALIGN
NOPEntry ROUT
MOV pc, lr
Activate ROUT
Push "sb,lr"
SUB sb, a1, #:INDEX:SDHCIDevice
MOV lr, #0
STR lr, SDHCILastWriteCount
; SDCLK has to be set up before any register writes are done - it's not
; reasonable to expect SDIODriver to know this requirement
MOV a3, #MIN_SDCLK
BL SetSDCLK
DoMemBarrier a1 ; switch to GPIO peripheral
; Set up pin mux registers and GPIO directions
MOV a1, #GPIO_CD
MOV a2, #GPIO_FSEL_INPUT
BL PinMux_SetFunction
MOV a1, #GPIO_CD
MOV a2, #GPIO_PUD_UP
BL GPIO_SetPull
MOV a1, #GPIO_STATUS_LED
MOV a2, #GPIO_FSEL_OUTPUT
BL PinMux_SetFunction
MOV a1, #GPIO_STATUS_LED
MOV a2, #GPIO_PUD_OFF
BL GPIO_SetPull
DoMemBarrier a1 ; back to SDHCI peripheral
MOV a1, #1 ; always succeed
Pull "sb,pc"
Deactivate * NOPEntry
Reset * NOPEntry ; I don't think there's any way for us to reset the controller
Sleep ROUT
MOV a1, #0
MOV pc, lr
TestIRQ ROUT
; Not a shared interrupt, so it must be our fault
MOV a1, #1
MOV pc, lr
WriteRegister ROUT
; The BCM2835 has a bug which means that we have to leave 2 SDCLK cycles
; between each SDHCI register write, except for those to the data port.
; This entry point isn't used for data port writes, so there is no need
; to filter them out.
Push "v1-v4,sb,lr"
SUB sb, a1, #:INDEX:SDHCIDevice
MOV v3, a3
MOV v4, a4
BL HAL_CounterRead
LDR v1, SDHCILastWriteCount ; v1 = upper limit of pause period
LDR v2, SDHCIWriteInterval
SUBS v2, v1, v2 ; v2 = lower limit of pause period
BPL %F30
; The precise value of the counter frequency is liable to change over
; time to correct for clock drift, so if our delay includes the wrap
; point then we have to be careful to avoid proportionately large errors.
; The approach we take is to wait until a wrap happens, then do an