This series expands the sdcard init sequence (as detailed in the SDHCI spec). It will probably still only work under QEMU, but it's further along.
-Kevin
Kevin O'Connor (5): sdcard: Check if card is present before sending commands to card sdcard: Implement controller frequency setting according to sdhci spec sdcard: Make sure controller support 3.3V before enabling it sdcard: Set timeout control register during init (to max allowed timeout) sdcard: Improve SD card initialization command sequence
src/hw/sdcard.c | 110 +++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 101 insertions(+), 9 deletions(-)
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/hw/sdcard.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/src/hw/sdcard.c b/src/hw/sdcard.c index d2c3288..7523ab8 100644 --- a/src/hw/sdcard.c +++ b/src/hw/sdcard.c @@ -73,8 +73,9 @@ struct sdhci_s { #define SI_READ_READY (1<<5)
// SDHCI present_state flags -#define SP_CMD_INHIBIT (1<<0) -#define SP_DAT_INHIBIT (1<<1) +#define SP_CMD_INHIBIT (1<<0) +#define SP_DAT_INHIBIT (1<<1) +#define SP_CARD_INSERTED (1<<16)
// SDHCI transfer_mode flags #define ST_BLOCKCOUNT (1<<1) @@ -269,6 +270,10 @@ sdcard_controller_setup(void *data) if (!runningOnQEMU()) // XXX - this init logic will probably only work on qemu! return; + u32 present_state = readl(®s->present_state); + if (!(present_state & SP_CARD_INSERTED)) + // No card present + return; writew(®s->irq_signal, 0); writew(®s->irq_enable, 0xffff); writew(®s->error_signal, 0);
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/hw/sdcard.c | 44 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-)
diff --git a/src/hw/sdcard.c b/src/hw/sdcard.c index 7523ab8..57f44b0 100644 --- a/src/hw/sdcard.c +++ b/src/hw/sdcard.c @@ -43,7 +43,7 @@ struct sdhci_s { u16 error_signal; u16 auto_cmd12; u8 pad_3E[2]; - u64 cap; + u32 cap_lo, cap_hi; u64 max_current; u16 force_auto_cmd12; u16 force_error; @@ -83,6 +83,16 @@ struct sdhci_s { #define ST_READ (1<<4) #define ST_MULTIPLE (1<<5)
+// SDHCI capabilities flags +#define SD_CAPLO_BASECLOCK_SHIFT 8 +#define SD_CAPLO_BASECLOCK_MASK 0x3f + +// SDHCI clock control flags +#define SCC_INTERNAL_ENABLE (1<<0) +#define SCC_STABLE (1<<1) +#define SCC_CLOCK_ENABLE (1<<2) +#define SCC_SDCLK_SHIFT 8 + // SDHCI result flags #define SR_OCR_CCS (1<<30)
@@ -255,6 +265,31 @@ sdcard_card_setup(struct sdhci_s *regs) return card_type; }
+static int +sdcard_set_frequency(struct sdhci_s *regs, u32 khz) +{ + u32 cap = readl(®s->cap_lo); + u32 base_freq = (cap >> SD_CAPLO_BASECLOCK_SHIFT) & SD_CAPLO_BASECLOCK_MASK; + if (!base_freq) { + dprintf(1, "Unknown base frequency for SD controller\n"); + return -1; + } + // Set new frequency + u32 divisor = DIV_ROUND_UP(base_freq * 1000, khz); + divisor = divisor >= 1 ? 1 << (__ffs(divisor-1)+1) : 0; + u16 creg = (divisor << SCC_SDCLK_SHIFT) | SCC_INTERNAL_ENABLE; + writew(®s->clock_control, 0); + writew(®s->clock_control, creg); + // Wait for frequency to become active + u32 end = timer_calc(SDHCI_PIO_TIMEOUT); + int ret = waitw((u16*)®s->clock_control, SCC_STABLE, SCC_STABLE, end); + if (ret) + return ret; + // Enable SD clock + writew(®s->clock_control, creg | SCC_CLOCK_ENABLE); + return 0; +} + // Setup and configure an SD card controller static void sdcard_controller_setup(void *data) @@ -277,13 +312,18 @@ sdcard_controller_setup(void *data) writew(®s->irq_signal, 0); writew(®s->irq_enable, 0xffff); writew(®s->error_signal, 0); + int ret = sdcard_set_frequency(regs, 400); + if (ret) + return; writeb(®s->power_control, 0x0f); - writew(®s->clock_control, 0x0005);
// Initialize card int card_type = sdcard_card_setup(regs); if (card_type < 0) return; + ret = sdcard_set_frequency(regs, 25000); + if (ret) + return;
// Register drive struct sddrive_s *drive = malloc_fseg(sizeof(*drive));
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/hw/sdcard.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/src/hw/sdcard.c b/src/hw/sdcard.c index 57f44b0..d4dea6a 100644 --- a/src/hw/sdcard.c +++ b/src/hw/sdcard.c @@ -84,6 +84,7 @@ struct sdhci_s { #define ST_MULTIPLE (1<<5)
// SDHCI capabilities flags +#define SD_CAPLO_V33 (1<<24) #define SD_CAPLO_BASECLOCK_SHIFT 8 #define SD_CAPLO_BASECLOCK_MASK 0x3f
@@ -93,6 +94,10 @@ struct sdhci_s { #define SCC_CLOCK_ENABLE (1<<2) #define SCC_SDCLK_SHIFT 8
+// SDHCI power control flags +#define SPC_POWER_ON (1<<0) +#define SPC_V33 0x0e + // SDHCI result flags #define SR_OCR_CCS (1<<30)
@@ -274,6 +279,10 @@ sdcard_set_frequency(struct sdhci_s *regs, u32 khz) dprintf(1, "Unknown base frequency for SD controller\n"); return -1; } + if (!(cap & SD_CAPLO_V33)) { + dprintf(1, "SD controller does not support 3.3V power\n"); + return -1; + } // Set new frequency u32 divisor = DIV_ROUND_UP(base_freq * 1000, khz); divisor = divisor >= 1 ? 1 << (__ffs(divisor-1)+1) : 0; @@ -315,7 +324,8 @@ sdcard_controller_setup(void *data) int ret = sdcard_set_frequency(regs, 400); if (ret) return; - writeb(®s->power_control, 0x0f); + writeb(®s->power_control, 0); + writeb(®s->power_control, SPC_V33 | SPC_POWER_ON);
// Initialize card int card_type = sdcard_card_setup(regs);
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/hw/sdcard.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/src/hw/sdcard.c b/src/hw/sdcard.c index d4dea6a..39cb5ba 100644 --- a/src/hw/sdcard.c +++ b/src/hw/sdcard.c @@ -326,6 +326,7 @@ sdcard_controller_setup(void *data) return; writeb(®s->power_control, 0); writeb(®s->power_control, SPC_V33 | SPC_POWER_ON); + writeb(®s->timeout_control, 0x0e); // Set to max timeout
// Initialize card int card_type = sdcard_card_setup(regs);
Send the initialization commands that are specified for SD cards by the "SD Host Controller Simplified Specification Version 2.00" spec.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/hw/sdcard.c | 44 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-)
diff --git a/src/hw/sdcard.c b/src/hw/sdcard.c index 39cb5ba..c12c263 100644 --- a/src/hw/sdcard.c +++ b/src/hw/sdcard.c @@ -56,9 +56,11 @@ struct sdhci_s { } PACKED;
// SDHCI commands +#define SC_GO_IDLE_STATE ((0<<8) | 0x20) #define SC_ALL_SEND_CID ((2<<8) | 0x21) #define SC_SEND_RELATIVE_ADDR ((3<<8) | 0x22) #define SC_SELECT_DESELECT_CARD ((7<<8) | 0x23) +#define SC_SEND_IF_COND ((8<<8) | 0x22) #define SC_READ_SINGLE ((17<<8) | 0x22) #define SC_READ_MULTIPLE ((18<<8) | 0x22) #define SC_WRITE_SINGLE ((24<<8) | 0x22) @@ -99,9 +101,11 @@ struct sdhci_s { #define SPC_V33 0x0e
// SDHCI result flags -#define SR_OCR_CCS (1<<30) +#define SR_OCR_CCS (1<<30) +#define SR_OCR_NOTBUSY (1<<31)
// SDHCI timeouts +#define SDHCI_POWERUP_TIMEOUT 1000 #define SDHCI_PIO_TIMEOUT 1000 // XXX - these are just made up #define SDHCI_TRANSFER_TIMEOUT 10000
@@ -151,6 +155,8 @@ sdcard_pio(struct sdhci_s *regs, int cmd, u32 *param) writew(®s->irq_status, SI_CMD_COMPLETE); // Read response memcpy(param, regs->response, sizeof(regs->response)); + dprintf(9, "sdcard cmd %x response %x %x %x %x\n" + , cmd, param[0], param[1], param[2], param[3]); return 0; }
@@ -249,16 +255,46 @@ static int sdcard_card_setup(struct sdhci_s *regs) { // XXX - works on QEMU; probably wont on real hardware! - u32 param[4] = { 0x01 }; - int ret = sdcard_pio_app(regs, SC_APP_SEND_OP_COND, param); + + // Reset card + u32 param[4] = { }; + int ret = sdcard_pio_app(regs, SC_GO_IDLE_STATE, param); + if (ret) + return ret; + // Let card know SDHC/SDXC is supported and confirm voltage + param[0] = 0x1aa; + ret = sdcard_pio_app(regs, SC_SEND_IF_COND, param); + if (ret) + return ret; + u32 hcs = 0; + if (param[0] == 0x1aa) + hcs = 0x40000000; + // Verify SD card (instead of MMC or SDIO) + param[0] = 0x00; + ret = sdcard_pio_app(regs, SC_APP_SEND_OP_COND, param); if (ret) return ret; + // Init card + u32 end = timer_calc(SDHCI_POWERUP_TIMEOUT); + for (;;) { + param[0] = hcs | (1<<20); // SDHC support and voltage level + ret = sdcard_pio_app(regs, SC_APP_SEND_OP_COND, param); + if (ret) + return ret; + if (param[0] & SR_OCR_NOTBUSY) + break; + if (timer_check(end)) { + warn_timeout(); + return -1; + } + } int card_type = (param[0] & SR_OCR_CCS) ? SF_SDHC : SF_SDSC; param[0] = 0x00; + // Select card ret = sdcard_pio(regs, SC_ALL_SEND_CID, param); if (ret) return ret; - param[0] = 0x01 << 16; + param[0] = 0x00; ret = sdcard_pio(regs, SC_SEND_RELATIVE_ADDR, param); if (ret) return ret;
On Thu, Jul 30, 2015 at 06:36:18PM -0400, Kevin O'Connor wrote:
This series expands the sdcard init sequence (as detailed in the SDHCI spec). It will probably still only work under QEMU, but it's further along.
FYI, I committed this (slightly modified) series.
-Kevin