Commit 60ebea68 authored by Ben Avison's avatar Ben Avison
Browse files

Fixes for SD support

Detail:
  * Engage the GPIO controller's pull-up resistors on SDCLK, CMD and DAT0-DAT3.
    In tests, this seems to address the worst of the unreliability we have
    seen previously.
  * Remove the entry to change the bus between push-pull and open-drain modes.
    The BCM2835 simply doesn't seem to be able to do this. Fortunately, all
    the cards I have tested seem to be OK with the GPIO controller's pull-up
    on the CMD line (however strong that is - it's undocumented) engaged at
    all times.
  * Time a dummy command in order to calculate the speed of the input clock
    to the SD controller block (there doesn't appear to be any way to read
    its speed directly!) This is necessary because recent versions of the
    firmware have not only changed the default clock speed, but even made it
    a user-configurable option in config.txt. It's very important that we
    know how fast it is - if we set the dividers so SDCLK is too slow, then
    the workaround for the register write bug won't work, too fast and we
    overclock the cards, potentially damaging them.
  * Re-enable high speed mode. As long as we don't use the High Speed Enable
    bit in Host Control 1 (see change in SDIODriver 0.03) this seems to work
    for me.
Admin:
  Tested against my collection of test cards on a Raspberry Pi with the
  firmware from the 2012-06-22 commit on github, and with
  init_emmc_clock=100000000 in config.txt (though other values, or the
  absence of that line, or the entire file, should also work). The only
  issues I had appeared to be due to mechanical problems with the SD socket,
  and went away after the card was reseated one or more times.

Version 0.10. Tagged as 'BCM2835-0_10'
parent 981cfe6d
......@@ -11,13 +11,13 @@
GBLS Module_HelpVersion
GBLS Module_ComponentName
GBLS Module_ComponentPath
Module_MajorVersion SETS "0.09"
Module_Version SETA 9
Module_MajorVersion SETS "0.10"
Module_Version SETA 10
Module_MinorVersion SETS ""
Module_Date SETS "15 Jun 2012"
Module_ApplicationDate SETS "15-Jun-12"
Module_Date SETS "01 Jul 2012"
Module_ApplicationDate SETS "01-Jul-12"
Module_ComponentName SETS "BCM2835"
Module_ComponentPath SETS "mixed/RiscOS/Sources/HAL/BCM2835"
Module_FullVersion SETS "0.09"
Module_HelpVersion SETS "0.09 (15 Jun 2012)"
Module_FullVersion SETS "0.10"
Module_HelpVersion SETS "0.10 (01 Jul 2012)"
END
/* (0.09)
/* (0.10)
*
* This file is automatically maintained by srccommit, do not edit manually.
* Last processed by srccommit version: 1.1.
*
*/
#define Module_MajorVersion_CMHG 0.09
#define Module_MajorVersion_CMHG 0.10
#define Module_MinorVersion_CMHG
#define Module_Date_CMHG 15 Jun 2012
#define Module_Date_CMHG 01 Jul 2012
#define Module_MajorVersion "0.09"
#define Module_Version 9
#define Module_MajorVersion "0.10"
#define Module_Version 10
#define Module_MinorVersion ""
#define Module_Date "15 Jun 2012"
#define Module_Date "01 Jul 2012"
#define Module_ApplicationDate "15-Jun-12"
#define Module_ApplicationDate "01-Jul-12"
#define Module_ComponentName "BCM2835"
#define Module_ComponentPath "mixed/RiscOS/Sources/HAL/BCM2835"
#define Module_FullVersion "0.09"
#define Module_HelpVersion "0.09 (15 Jun 2012)"
#define Module_LibraryVersionInfo "0:9"
#define Module_FullVersion "0.10"
#define Module_HelpVersion "0.10 (01 Jul 2012)"
#define Module_LibraryVersionInfo "0:10"
......@@ -110,8 +110,10 @@ WSPhysAddr # 4 ;physical address of HAL workspace
OSheader # 4
OSentries # 4*(HighestOSEntry+1)
! 0,"SDHCI at ":CC::STR:(&FC0001D8+:INDEX:@),0
SDHCIWriteInterval # 4 ; minimum counter ticks between writes
SDHCILastWriteCount # 4 ; counter value at last write
SDHCIInputClock # 4 ; estimated speed of input clock to SDHCI block
# (16-:INDEX:@):AND:15 ; align nicely
SDHCIDevice # HALDevice_SDHCISize ; see Hdr:SDHCIDevice
SDHCISlotInfo # HALDeviceSDHCI_SlotInfo_Size ; the controller has just the 1 slot
......
......@@ -49,9 +49,6 @@ 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
......@@ -90,6 +87,11 @@ GPIO_PUD_DOWN * 1
GPIO_PUD_UP * 2
; Selected SD controller registers and bits
ARG1 * &08
CMDTM * &0C
CMDTM_CMD_INDEX_SHIFT * 24
STATUS * &24
STATUS_CMD_INHIBIT * 1 :SHL: 0
CONTROL1 * &2C
CONTROL1_CLK_FREQ8_SHIFT * 8
CONTROL1_CLK_FREQ8_MASK * &FF :SHL: CONTROL1_CLK_FREQ8_SHIFT
......@@ -99,6 +101,9 @@ CONTROL1_CLK_GENSEL * 1 :SHL: 5
CONTROL1_CLK_EN * 1 :SHL: 2
CONTROL1_CLK_STABLE * 1 :SHL: 1
CONTROL1_CLK_INTLEN * 1 :SHL: 0
INTERRUPT * &30
IRPT_MASK * &34
IRQ_CC * 1 :SHL: 0
; Capabilities bits
CAP_DDR50S * 1 :SHL: 34
......@@ -117,6 +122,142 @@ CAP_MBL_MASK * 3 :SHL: CAP_MBL_SHIFT
AREA |Asm$$Code|, CODE, READONLY, PIC
; Delay in microseconds between writes to SDHCI registers during speed
; measurement. At this point we don't know what the delay actually needs to be
; because we haven't figured out the clock speed yet, but assuming the GPU boot
; code never sets it slower than 25 MHz, this should be long enough for all cases.
MEASURE_DELAY * 5
; Delay to use when waiting for SDCLK to settle. 20 ms is the longest it's
; supposed to take, and because this is a simplified implementation, we always
; wait this long.
MEASURE_LONG_DELAY * 20000
; If the input clock is 25 MHz, this will set SDCLK to approximately 400 kHz,
; and proportionately higher for other inputs.
MEASURE_DIVIDER * 63
; Command to time is CMD7_SELECT_DESELECT_CARD
MEASURE_COMMAND * 7
; Empirically, I have determined that it takes the controller this many cycles
; of SDCLK to complete a deselect card command
MEASURE_SDCLKS * 56
; Round the estimate of the input clock to nearest multiple of this many MHz
MEASURE_ACCURACY * 5
; Routine to estimate the input clock speed to the SDHCI block based upon how
; long it takes to issue a deselect-all-cards command. This command has no
; response, so it doesn't matter that lots of things aren't set up properly yet.
MeasureSpeed ROUT
Push "v1-v3,lr"
LDR v1, PeriBase
ADD v1, v1, #EMMC_Base
; Switch the clock divider to a known value.
; This is a simplified version of the algorithm in SetSDCLK.
LDR a1, =MEASURE_DELAY
BL HAL_CounterDelay
LDR v2, [v1, #CONTROL1]
BIC v2, v2, #CONTROL1_CLK_EN ; stop clock going to card
STR v2, [v1, #CONTROL1]
LDR a1, =MEASURE_DELAY
BL HAL_CounterDelay
BIC v2, v2, #CONTROL1_CLK_INTLEN ; stop clock
STR v2, [v1, #CONTROL1]
LDR a1, =MEASURE_DELAY
BL HAL_CounterDelay
BIC v2, v2, #CONTROL1_CLK_FREQ8_MASK
BIC v2, v2, #CONTROL1_CLK_FREQ_MS2_MASK
ORR v2, v2, #MEASURE_DIVIDER :SHL: CONTROL1_CLK_FREQ8_SHIFT
STR v2, [v1, #CONTROL1]
LDR a1, =MEASURE_DELAY
BL HAL_CounterDelay
ORR v2, v2, #CONTROL1_CLK_INTLEN ; start clock
STR v2, [v1, #CONTROL1]
LDR a1, =MEASURE_LONG_DELAY
BL HAL_CounterDelay
LDR v2, [v1, #CONTROL1]
ORR v2, v2, #CONTROL1_CLK_EN ; let clock go to card
STR v2, [v1, #CONTROL1]
LDR a1, =MEASURE_DELAY
BL HAL_CounterDelay
; Wait for command inhibit to be unset
; It's really important that this works, we won't be able to boot if
; this never happens, so don't bother with a timeout.
01 LDR v2, [v1, #STATUS]
TST v2, #STATUS_CMD_INHIBIT
BNE %B01
; Ensure no interrupts are pending
MOV lr, #-1
STR lr, [v1, #INTERRUPT]
LDR a1, =MEASURE_DELAY
BL HAL_CounterDelay
; Enable the command-complete "interrupt" status bit.
LDR v2, =IRQ_CC
STR v2, [v1, #IRPT_MASK]
LDR a1, =MEASURE_DELAY
BL HAL_CounterDelay
; Use argument 0 to deselect all cards
MOV v2, #0
STR v2, [v1, #ARG1]
LDR a1, =MEASURE_DELAY
BL HAL_CounterDelay
; Wait for the timer to wrap, so we don't have to deal with wrap later
BL HAL_CounterRead
02 MOV v2, a1
BL HAL_CounterRead
CMP a1, v2
BLS %B02
MRS v3, CPSR
ORR lr, v3, #&80
MSR CPSR_c, lr ; IRQs off
; Read start time
BL HAL_CounterRead
MOV v2, a1
; Trigger the command (all the other bits in CMDTM are 0)
MOV lr, #MEASURE_COMMAND :SHL: CMDTM_CMD_INDEX_SHIFT
STR lr, [v1, #CMDTM]
; Wait for completion
03 LDR lr, [v1, #INTERRUPT]
TST lr, #IRQ_CC
BEQ %B03
; Read finish time
BL HAL_CounterRead
; Acknowledge interrupt
MOV lr, #IRQ_CC
STR lr, [v1, #INTERRUPT]
MSR CPSR_c, v3 ; IRQs restored
; Calculate elapsed time
SUB v2, v2, a1
LDR a1, =MEASURE_DELAY
BL HAL_CounterDelay
; Calculate input clock frequency
LDR a1, =(MEASURE_SDCLKS * MEASURE_DIVIDER) / MEASURE_ACCURACY
ADD a1, a1, v2, LSR #1 ; round to nearest
DivRem a2, a1, v2, lr
LDR a3, =MEASURE_ACCURACY * 1000
MUL a4, a2, a3
STR a4, SDHCIInputClock
Pull "v1-v3,pc"
MACRO
$class HALDeviceField $field, $value
LCLS myvalue
......@@ -161,7 +302,7 @@ SDHCI HALDeviceField WriteRegister
SDHCI HALDeviceField GetCapabilities
SDHCI HALDeviceField GetVddCapabilities, 0 ; our fake capabilities register suffices already
SDHCI HALDeviceField SetVdd
SDHCI HALDeviceField SetBusMode
SDHCI HALDeviceField SetBusMode, 0
SDHCI HALDeviceField PostPowerOn, 0
SDHCI HALDeviceField SetBusWidth, 0
SDHCI HALDeviceField GetMaxCurrent
......@@ -175,8 +316,9 @@ SDHCI HALDeviceField GetWriteProtect
; Init the SDHCI HAL device
SDIO_InitDevices ROUT
Push "lr"
BL MeasureSpeed
ADR a1, SDHCIDevice
ADR a2, Template
MOV a3, #HALDevice_SDHCISize
......@@ -324,6 +466,25 @@ Activate ROUT
MOV a2, #GPIO_PUD_OFF
BL GPIO_SetPull
MOV a1, #GPIO_DAT3
MOV a2, #GPIO_PUD_UP
BL GPIO_SetPull
MOV a1, #GPIO_DAT2
MOV a2, #GPIO_PUD_UP
BL GPIO_SetPull
MOV a1, #GPIO_DAT1
MOV a2, #GPIO_PUD_UP
BL GPIO_SetPull
MOV a1, #GPIO_DAT0
MOV a2, #GPIO_PUD_UP
BL GPIO_SetPull
MOV a1, #GPIO_CMD
MOV a2, #GPIO_PUD_UP
BL GPIO_SetPull
MOV a1, #GPIO_CLK
MOV a2, #GPIO_PUD_UP
BL GPIO_SetPull
DoMemBarrier a1 ; back to SDHCI peripheral
MOV a1, #1 ; always succeed
......@@ -391,26 +552,11 @@ GetCapabilities ROUT
; Vss is fixed in hardware - see schematics
; Eben says high bus speed doesn't work, but Linux seems to manage?
; Buffer size is mentioned in the prose in the BCM2835 ARM peripherals datasheet
; LDR a1, =CAP_VS33 :OR: CAP_HSS :OR: CAP_MBL_1024
LDR a1, =CAP_VS33 :OR: CAP_MBL_1024 ; until we're confident HS works, stay at standard speed
LDR a1, =CAP_VS33 :OR: CAP_HSS :OR: CAP_MBL_1024
MOV pc, lr
SetVdd * NOPEntry ; There is no software control of Vdd for this board
SetBusMode ROUT
Push "sb,lr"
DoMemBarrier lr ; switch to GPIO peripheral
SUB sb, a1, #:INDEX:SDHCIDevice
TEQ a3, #0
MOVEQ a2, #GPIO_PUD_OFF
MOVNE a2, #GPIO_PUD_UP
MOV a1, #GPIO_CMD
BL GPIO_SetPull
DoMemBarrier lr ; back to SDHCI peripheral
Pull "sb,pc"
GetMaxCurrent ROUT
; Can't find any information on this, so just stick with a low value
MOV a1, #100
......@@ -424,8 +570,9 @@ GetMaxCurrent ROUT
CalcTimings ROUT
; By experimentation, I've determined that the 40 MHz input clock
; can be divided by any integer from 1 to 1023.
LDR ip, =INPUT_CLK_FREQ_KHZ - 1
LDR ip, SDHCIInputClock
ADD ip, ip, a1 ; input clock, adjusted to ensure round up
SUB ip, ip, #1
DivRem v2, ip, a1, a2
CMP v2, #1
MOVLO v2, #1
......@@ -435,7 +582,7 @@ CalcTimings ROUT
; v2 now contains the final divisor. Work out what frequency that
; corresponds to.
LDR ip, =INPUT_CLK_FREQ_KHZ
LDR ip, SDHCIInputClock
DivRem v3, ip, v2, a2
; Work out how many counter ticks we have to see to guarantee 2 SDCLK cycles
......@@ -541,7 +688,7 @@ GetTMCLK ROUT
AND a4, a2, #CONTROL1_CLK_FREQ_MS2_MASK
MOV a3, a3, LSR #CONTROL1_CLK_FREQ8_SHIFT - 0
ORR a3, a3, a4, LSL #8 - CONTROL1_CLK_FREQ_MS2_SHIFT
LDR a2, =INPUT_CLK_FREQ_KHZ
LDR a2, SDHCIInputClock
DivRem a1, a2, a3, a4
Pull "sb,pc"
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment