This series further improves the SeaBIOS sdcard support. At the conclusion of the series the sdcard support is enabled on configurations beyond QEMU.
This code was tested on an Asus C300 (a Baytrail style Chromebook) with both an external sdcard card and an internal eMMC drive.
-Kevin
Kevin O'Connor (14): sdcard: Fix typo - use sdcard_pio() instead of sdcard_pio_app() sdcard: Fill command bits according to spec sdcard: Support SDHCI v3.00 spec clock setting sdcard: Move power setup to new function sdcard_set_power() sdcard: Power controller up to maximum voltage supported sdcard: Power down controller on failure sdcard: The card should never be in a busy state at start of sdcard_pio() sdcard: Implement timeout on every block read in sdcard_pio_transfer() sdcard: Rename waitw() to sdcard_waitw() and simplify sdcard: Perform a controller reset at start of init sdcard: Check for error events during sdcard_pio() sdcard: Initial support for MMC cards sdcard: Allow the sdcard driver to run on real hardware sdcard: Allow sdcard addresses to be specified in CBFS files
docs/Runtime_config.md | 1 + src/Kconfig | 2 +- src/hw/sdcard.c | 293 +++++++++++++++++++++++++++++++++---------------- 3 files changed, 202 insertions(+), 94 deletions(-)
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/hw/sdcard.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/hw/sdcard.c b/src/hw/sdcard.c index eacac71..556c7c2 100644 --- a/src/hw/sdcard.c +++ b/src/hw/sdcard.c @@ -261,12 +261,12 @@ sdcard_card_setup(struct sdhci_s *regs)
// Reset card u32 param[4] = { }; - int ret = sdcard_pio_app(regs, SC_GO_IDLE_STATE, param); + int ret = sdcard_pio(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); + ret = sdcard_pio(regs, SC_SEND_IF_COND, param); if (ret) return ret; u32 hcs = 0;
Set the proper command bits for each command according to the SDHCI specification.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/hw/sdcard.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-)
diff --git a/src/hw/sdcard.c b/src/hw/sdcard.c index 556c7c2..287d3d8 100644 --- a/src/hw/sdcard.c +++ b/src/hw/sdcard.c @@ -56,17 +56,23 @@ 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) -#define SC_WRITE_MULTIPLE ((25<<8) | 0x22) -#define SC_APP_CMD ((55<<8) | 0x22) -#define SC_APP_SEND_OP_COND ((41<<8) | 0x22) +#define SCB_R0 0x00 // No response +#define SCB_R48 0x1a // Response R1 (no data), R5, R6, R7 +#define SCB_R48d 0x3a // Response R1 (with data) +#define SCB_R48b 0x1b // Response R1b, R5b +#define SCB_R48o 0x02 // Response R3, R4 +#define SCB_R136 0x09 // Response R2 +#define SC_GO_IDLE_STATE ((0<<8) | SCB_R0) +#define SC_ALL_SEND_CID ((2<<8) | SCB_R136) +#define SC_SEND_RELATIVE_ADDR ((3<<8) | SCB_R48) +#define SC_SELECT_DESELECT_CARD ((7<<8) | SCB_R48b) +#define SC_SEND_IF_COND ((8<<8) | SCB_R48) +#define SC_READ_SINGLE ((17<<8) | SCB_R48d) +#define SC_READ_MULTIPLE ((18<<8) | SCB_R48d) +#define SC_WRITE_SINGLE ((24<<8) | SCB_R48d) +#define SC_WRITE_MULTIPLE ((25<<8) | SCB_R48d) +#define SC_APP_CMD ((55<<8) | SCB_R48) +#define SC_APP_SEND_OP_COND ((41<<8) | SCB_R48o)
// SDHCI irqs #define SI_CMD_COMPLETE (1<<0)
The SDHCI v3 spec supports higher frequency clocks and has a different method to configure the clock divisor.
This also fixes an error in v1/v2 clock calculation (ffs was used instead of fls).
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/hw/sdcard.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-)
diff --git a/src/hw/sdcard.c b/src/hw/sdcard.c index 287d3d8..053f8de 100644 --- a/src/hw/sdcard.c +++ b/src/hw/sdcard.c @@ -42,7 +42,7 @@ struct sdhci_s { u16 irq_signal; u16 error_signal; u16 auto_cmd12; - u8 pad_3E[2]; + u16 host_control2; u32 cap_lo, cap_hi; u64 max_current; u16 force_auto_cmd12; @@ -94,13 +94,16 @@ struct sdhci_s { // SDHCI capabilities flags #define SD_CAPLO_V33 (1<<24) #define SD_CAPLO_BASECLOCK_SHIFT 8 -#define SD_CAPLO_BASECLOCK_MASK 0x3f +#define SD_CAPLO_BASECLOCK_MASK 0xff
// SDHCI clock control flags #define SCC_INTERNAL_ENABLE (1<<0) #define SCC_STABLE (1<<1) #define SCC_CLOCK_ENABLE (1<<2) +#define SCC_SDCLK_MASK 0xff #define SCC_SDCLK_SHIFT 8 +#define SCC_SDCLK_HI_MASK 0x300 +#define SCC_SDCLK_HI_RSHIFT 2
// SDHCI power control flags #define SPC_POWER_ON (1<<0) @@ -318,6 +321,7 @@ sdcard_card_setup(struct sdhci_s *regs) static int sdcard_set_frequency(struct sdhci_s *regs, u32 khz) { + u16 ver = readw(®s->controller_version); u32 cap = readl(®s->cap_lo); u32 base_freq = (cap >> SD_CAPLO_BASECLOCK_SHIFT) & SD_CAPLO_BASECLOCK_MASK; if (!base_freq) { @@ -330,17 +334,25 @@ sdcard_set_frequency(struct sdhci_s *regs, u32 khz) } // Set new frequency u32 divisor = DIV_ROUND_UP(base_freq * 1000, khz); - divisor = divisor > 1 ? 1 << __ffs(divisor-1) : 0; - u16 creg = (divisor << SCC_SDCLK_SHIFT) | SCC_INTERNAL_ENABLE; + u16 creg; + if ((ver & 0xff) <= 0x01) { + divisor = divisor > 1 ? 1 << __fls(divisor-1) : 0; + creg = (divisor & SCC_SDCLK_MASK) << SCC_SDCLK_SHIFT; + } else { + divisor = DIV_ROUND_UP(divisor, 2); + creg = (divisor & SCC_SDCLK_MASK) << SCC_SDCLK_SHIFT; + creg |= (divisor & SCC_SDCLK_HI_MASK) >> SCC_SDCLK_HI_RSHIFT; + } + dprintf(3, "sdcard_set_frequency %d %d %x\n", base_freq, khz, creg); writew(®s->clock_control, 0); - writew(®s->clock_control, creg); + writew(®s->clock_control, creg | SCC_INTERNAL_ENABLE); // 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); + int ret = waitw(®s->clock_control, SCC_STABLE, SCC_STABLE, end); if (ret) return ret; // Enable SD clock - writew(®s->clock_control, creg | SCC_CLOCK_ENABLE); + writew(®s->clock_control, creg | SCC_INTERNAL_ENABLE | SCC_CLOCK_ENABLE); return 0; }
@@ -363,6 +375,9 @@ sdcard_controller_setup(void *data) if (!(present_state & SP_CARD_INSERTED)) // No card present return; + dprintf(3, "sdhci@%p ver=%x cap=%x %x\n", regs + , readw(®s->controller_version) + , readl(®s->cap_lo), readl(®s->cap_hi)); 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 | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-)
diff --git a/src/hw/sdcard.c b/src/hw/sdcard.c index 053f8de..45f19a7 100644 --- a/src/hw/sdcard.c +++ b/src/hw/sdcard.c @@ -264,7 +264,7 @@ sdcard_process_op(struct disk_op_s *op)
// Initialize an SD card static int -sdcard_card_setup(struct sdhci_s *regs) +sdcard_card_setup(struct sdhci_s *regs, int volt) { // XXX - works on QEMU; probably wont on real hardware!
@@ -274,12 +274,13 @@ sdcard_card_setup(struct sdhci_s *regs) if (ret) return ret; // Let card know SDHC/SDXC is supported and confirm voltage - param[0] = 0x1aa; + u32 vrange = (volt >= (1<<15) ? 0x100 : 0x200) | 0xaa; + param[0] = vrange; ret = sdcard_pio(regs, SC_SEND_IF_COND, param); if (ret) return ret; u32 hcs = 0; - if (param[0] == 0x1aa) + if (param[0] == vrange) hcs = 0x40000000; // Verify SD card (instead of MMC or SDIO) param[0] = 0x00; @@ -289,7 +290,7 @@ sdcard_card_setup(struct sdhci_s *regs) // Init card u32 end = timer_calc(SDHCI_POWERUP_TIMEOUT); for (;;) { - param[0] = hcs | (1<<20); // SDHC support and voltage level + param[0] = hcs | volt; // SDHC support and voltage level ret = sdcard_pio_app(regs, SC_APP_SEND_OP_COND, param); if (ret) return ret; @@ -319,6 +320,21 @@ sdcard_card_setup(struct sdhci_s *regs) }
static int +sdcard_set_power(struct sdhci_s *regs) +{ + u32 cap = readl(®s->cap_lo); + if (!(cap & SD_CAPLO_V33)) { + dprintf(1, "SD controller does not support 3.3V power\n"); + return -1; + } + writeb(®s->power_control, 0); + msleep(SDHCI_POWER_OFF_TIME); + writeb(®s->power_control, SPC_V33 | SPC_POWER_ON); + msleep(SDHCI_POWER_ON_TIME); + return 1<<20; +} + +static int sdcard_set_frequency(struct sdhci_s *regs, u32 khz) { u16 ver = readw(®s->controller_version); @@ -328,10 +344,6 @@ 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); u16 creg; @@ -382,17 +394,16 @@ sdcard_controller_setup(void *data) writew(®s->irq_enable, 0xffff); writew(®s->error_signal, 0); writeb(®s->timeout_control, 0x0e); // Set to max timeout - writeb(®s->power_control, 0); - msleep(SDHCI_POWER_OFF_TIME); - writeb(®s->power_control, SPC_V33 | SPC_POWER_ON); - msleep(SDHCI_POWER_ON_TIME); + int volt = sdcard_set_power(regs); + if (volt < 0) + return; int ret = sdcard_set_frequency(regs, 400); if (ret) return; msleep(SDHCI_CLOCK_ON_TIME);
// Initialize card - int card_type = sdcard_card_setup(regs); + int card_type = sdcard_card_setup(regs, volt); if (card_type < 0) return; ret = sdcard_set_frequency(regs, 25000);
Don't error out if the controller does not support 3.3V - instead supply the most voltage the controller supports.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/hw/sdcard.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-)
diff --git a/src/hw/sdcard.c b/src/hw/sdcard.c index 45f19a7..7474c32 100644 --- a/src/hw/sdcard.c +++ b/src/hw/sdcard.c @@ -93,6 +93,8 @@ struct sdhci_s {
// SDHCI capabilities flags #define SD_CAPLO_V33 (1<<24) +#define SD_CAPLO_V30 (1<<25) +#define SD_CAPLO_V18 (1<<26) #define SD_CAPLO_BASECLOCK_SHIFT 8 #define SD_CAPLO_BASECLOCK_MASK 0xff
@@ -107,6 +109,8 @@ struct sdhci_s {
// SDHCI power control flags #define SPC_POWER_ON (1<<0) +#define SPC_V18 0x0a +#define SPC_V30 0x0c #define SPC_V33 0x0e
// SDHCI result flags @@ -323,15 +327,25 @@ static int sdcard_set_power(struct sdhci_s *regs) { u32 cap = readl(®s->cap_lo); - if (!(cap & SD_CAPLO_V33)) { - dprintf(1, "SD controller does not support 3.3V power\n"); + u32 volt, vbits; + if (cap & SD_CAPLO_V33) { + volt = 1<<20; + vbits = SPC_V33; + } else if (cap & SD_CAPLO_V30) { + volt = 1<<18; + vbits = SPC_V30; + } else if (cap & SD_CAPLO_V18) { + volt = 1<<7; + vbits = SPC_V18; + } else { + dprintf(1, "SD controller unsupported volt range (%x)\n", cap); return -1; } writeb(®s->power_control, 0); msleep(SDHCI_POWER_OFF_TIME); - writeb(®s->power_control, SPC_V33 | SPC_POWER_ON); + writeb(®s->power_control, vbits | SPC_POWER_ON); msleep(SDHCI_POWER_ON_TIME); - return 1<<20; + return volt; }
static int
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/hw/sdcard.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/src/hw/sdcard.c b/src/hw/sdcard.c index 7474c32..4dd93cb 100644 --- a/src/hw/sdcard.c +++ b/src/hw/sdcard.c @@ -413,22 +413,22 @@ sdcard_controller_setup(void *data) return; int ret = sdcard_set_frequency(regs, 400); if (ret) - return; + goto fail; msleep(SDHCI_CLOCK_ON_TIME);
// Initialize card int card_type = sdcard_card_setup(regs, volt); if (card_type < 0) - return; + goto fail; ret = sdcard_set_frequency(regs, 25000); if (ret) - return; + goto fail;
// Register drive struct sddrive_s *drive = malloc_fseg(sizeof(*drive)); if (!drive) { warn_noalloc(); - return; + goto fail; } memset(drive, 0, sizeof(*drive)); drive->drive.type = DTYPE_SDCARD; @@ -441,6 +441,10 @@ sdcard_controller_setup(void *data) , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), pci_bdf_to_fn(bdf)); char *desc = znprintf(MAXDESCSIZE, "SD Card"); // XXX boot_add_hd(&drive->drive, desc, bootprio_find_pci_device(pci)); + return; +fail: + writeb(®s->power_control, 0); + writew(®s->clock_control, 0); }
void
Return an error if the controller is busy at the start of a command instead of waiting.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/hw/sdcard.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/src/hw/sdcard.c b/src/hw/sdcard.c index 4dd93cb..4cec423 100644 --- a/src/hw/sdcard.c +++ b/src/hw/sdcard.c @@ -157,15 +157,18 @@ waitw(u16 *reg, u16 mask, u16 value, u32 end) static int sdcard_pio(struct sdhci_s *regs, int cmd, u32 *param) { - u32 end = timer_calc(SDHCI_PIO_TIMEOUT); - u16 busyf = SP_CMD_INHIBIT | ((cmd & 0x03) == 0x03 ? SP_DAT_INHIBIT : 0); - int ret = waitw((u16*)®s->present_state, busyf, 0, end); - if (ret) - return ret; + u32 state = readl(®s->present_state); + dprintf(9, "sdcard_pio cmd %x %x %x\n", cmd, *param, state); + if ((state & SP_CMD_INHIBIT) + || ((cmd & 0x03) == 0x03 && state & SP_DAT_INHIBIT)) { + dprintf(1, "sdcard_pio not ready %x\n", state); + return -1; + } // Send command writel(®s->arg, *param); writew(®s->cmd, cmd); - ret = waitw(®s->irq_status, SI_CMD_COMPLETE, SI_CMD_COMPLETE, end); + u32 end = timer_calc(SDHCI_PIO_TIMEOUT); + int ret = waitw(®s->irq_status, SI_CMD_COMPLETE, SI_CMD_COMPLETE, end); if (ret) return ret; writew(®s->irq_status, SI_CMD_COMPLETE);
The sdcard specs don't specify a maximum time for arbitrary transfers. Instead, each block has a maximum timeout. So, change the code to check for a timeout on each block transferred.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/hw/sdcard.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/src/hw/sdcard.c b/src/hw/sdcard.c index 4cec423..d8e665a 100644 --- a/src/hw/sdcard.c +++ b/src/hw/sdcard.c @@ -122,8 +122,7 @@ struct sdhci_s { #define SDHCI_POWER_ON_TIME 1 #define SDHCI_CLOCK_ON_TIME 1 // 74 clock cycles #define SDHCI_POWERUP_TIMEOUT 1000 -#define SDHCI_PIO_TIMEOUT 1000 // XXX - these are just made up -#define SDHCI_TRANSFER_TIMEOUT 10000 +#define SDHCI_PIO_TIMEOUT 1000 // XXX - this is just made up
// Internal 'struct drive_s' storage for a detected card struct sddrive_s { @@ -197,7 +196,7 @@ sdcard_pio_transfer(struct sddrive_s *drive, int cmd, u32 addr { // Send command writel(&drive->regs->block_size, DISK_SECTOR_SIZE); - writew(&drive->regs->block_count, count); // XXX - SC_SET_BLOCK_COUNT? + writew(&drive->regs->block_count, count); int isread = cmd != SC_WRITE_SINGLE && cmd != SC_WRITE_MULTIPLE; u16 tmode = ((count > 1 ? ST_MULTIPLE|ST_AUTO_CMD12|ST_BLOCKCOUNT : 0) | (isread ? ST_READ : 0)); @@ -209,9 +208,9 @@ sdcard_pio_transfer(struct sddrive_s *drive, int cmd, u32 addr if (ret) return ret; // Read/write data - u32 end = timer_calc(SDHCI_TRANSFER_TIMEOUT); u16 cbit = isread ? SI_READ_READY : SI_WRITE_READY; while (count--) { + u32 end = timer_calc(SDHCI_PIO_TIMEOUT); ret = waitw(&drive->regs->irq_status, cbit, cbit, end); if (ret) return ret; @@ -226,7 +225,7 @@ sdcard_pio_transfer(struct sddrive_s *drive, int cmd, u32 addr } } // Complete command - // XXX - SC_STOP_TRANSMISSION? + u32 end = timer_calc(SDHCI_PIO_TIMEOUT); ret = waitw(&drive->regs->irq_status, SI_TRANS_DONE, SI_TRANS_DONE, end); if (ret) return ret;
Calculate end timeout in code and return if any bit in the provided mask is set.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/hw/sdcard.c | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-)
diff --git a/src/hw/sdcard.c b/src/hw/sdcard.c index d8e665a..10832b1 100644 --- a/src/hw/sdcard.c +++ b/src/hw/sdcard.c @@ -136,14 +136,15 @@ struct sddrive_s { #define SF_SDSC 1 #define SF_SDHC 2
-// Repeatedly read a u16 register until the specific value is found +// Repeatedly read a u16 register until any bit in a given mask is set static int -waitw(u16 *reg, u16 mask, u16 value, u32 end) +sdcard_waitw(u16 *reg, u16 mask) { + u32 end = timer_calc(SDHCI_PIO_TIMEOUT); for (;;) { u16 v = readw(reg); - if ((v & mask) == value) - return 0; + if (v & mask) + return v; if (timer_check(end)) { warn_timeout(); return -1; @@ -166,9 +167,8 @@ sdcard_pio(struct sdhci_s *regs, int cmd, u32 *param) // Send command writel(®s->arg, *param); writew(®s->cmd, cmd); - u32 end = timer_calc(SDHCI_PIO_TIMEOUT); - int ret = waitw(®s->irq_status, SI_CMD_COMPLETE, SI_CMD_COMPLETE, end); - if (ret) + int ret = sdcard_waitw(®s->irq_status, SI_CMD_COMPLETE); + if (ret < 0) return ret; writew(®s->irq_status, SI_CMD_COMPLETE); // Read response @@ -210,9 +210,8 @@ sdcard_pio_transfer(struct sddrive_s *drive, int cmd, u32 addr // Read/write data u16 cbit = isread ? SI_READ_READY : SI_WRITE_READY; while (count--) { - u32 end = timer_calc(SDHCI_PIO_TIMEOUT); - ret = waitw(&drive->regs->irq_status, cbit, cbit, end); - if (ret) + ret = sdcard_waitw(&drive->regs->irq_status, cbit); + if (ret < 0) return ret; writew(&drive->regs->irq_status, cbit); int i; @@ -225,9 +224,8 @@ sdcard_pio_transfer(struct sddrive_s *drive, int cmd, u32 addr } } // Complete command - u32 end = timer_calc(SDHCI_PIO_TIMEOUT); - ret = waitw(&drive->regs->irq_status, SI_TRANS_DONE, SI_TRANS_DONE, end); - if (ret) + ret = sdcard_waitw(&drive->regs->irq_status, SI_TRANS_DONE); + if (ret < 0) return ret; writew(&drive->regs->irq_status, SI_TRANS_DONE); return 0; @@ -375,9 +373,8 @@ sdcard_set_frequency(struct sdhci_s *regs, u32 khz) writew(®s->clock_control, 0); writew(®s->clock_control, creg | SCC_INTERNAL_ENABLE); // Wait for frequency to become active - u32 end = timer_calc(SDHCI_PIO_TIMEOUT); - int ret = waitw(®s->clock_control, SCC_STABLE, SCC_STABLE, end); - if (ret) + int ret = sdcard_waitw(®s->clock_control, SCC_STABLE); + if (ret < 0) return ret; // Enable SD clock writew(®s->clock_control, creg | SCC_INTERNAL_ENABLE | SCC_CLOCK_ENABLE);
Ask the controller to reset itself during controller setup.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/hw/sdcard.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+)
diff --git a/src/hw/sdcard.c b/src/hw/sdcard.c index 10832b1..5f9ce4a 100644 --- a/src/hw/sdcard.c +++ b/src/hw/sdcard.c @@ -113,6 +113,11 @@ struct sdhci_s { #define SPC_V30 0x0c #define SPC_V33 0x0e
+// SDHCI software reset flags +#define SRF_ALL 0x01 +#define SRF_CMD 0x02 +#define SRF_DATA 0x04 + // SDHCI result flags #define SR_OCR_CCS (1<<30) #define SR_OCR_NOTBUSY (1<<31) @@ -153,6 +158,20 @@ sdcard_waitw(u16 *reg, u16 mask) } }
+// Send an sdhci reset +static int +sdcard_reset(struct sdhci_s *regs, int flags) +{ + writeb(®s->software_reset, flags); + u32 end = timer_calc(SDHCI_PIO_TIMEOUT); + while (readb(®s->software_reset)) + if (timer_check(end)) { + warn_timeout(); + return -1; + } + return 0; +} + // Send a command to the card. static int sdcard_pio(struct sdhci_s *regs, int cmd, u32 *param) @@ -403,6 +422,7 @@ sdcard_controller_setup(void *data) dprintf(3, "sdhci@%p ver=%x cap=%x %x\n", regs , readw(®s->controller_version) , readl(®s->cap_lo), readl(®s->cap_hi)); + sdcard_reset(regs, SRF_ALL); writew(®s->irq_signal, 0); writew(®s->irq_enable, 0xffff); writew(®s->error_signal, 0);
Check for command errors and clear the error status if an error is found.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/hw/sdcard.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/src/hw/sdcard.c b/src/hw/sdcard.c index 5f9ce4a..1f26291 100644 --- a/src/hw/sdcard.c +++ b/src/hw/sdcard.c @@ -79,6 +79,7 @@ struct sdhci_s { #define SI_TRANS_DONE (1<<1) #define SI_WRITE_READY (1<<4) #define SI_READ_READY (1<<5) +#define SI_ERROR (1<<15)
// SDHCI present_state flags #define SP_CMD_INHIBIT (1<<0) @@ -186,9 +187,16 @@ sdcard_pio(struct sdhci_s *regs, int cmd, u32 *param) // Send command writel(®s->arg, *param); writew(®s->cmd, cmd); - int ret = sdcard_waitw(®s->irq_status, SI_CMD_COMPLETE); + int ret = sdcard_waitw(®s->irq_status, SI_ERROR|SI_CMD_COMPLETE); if (ret < 0) return ret; + if (ret & SI_ERROR) { + u16 err = readw(®s->error_irq_status); + dprintf(3, "sdcard_pio command stop (code=%x)\n", err); + sdcard_reset(regs, SRF_CMD|SRF_DATA); + writew(®s->error_irq_status, err); + return -1; + } writew(®s->irq_status, SI_CMD_COMPLETE); // Read response memcpy(param, regs->response, sizeof(regs->response)); @@ -424,8 +432,11 @@ sdcard_controller_setup(void *data) , readl(®s->cap_lo), readl(®s->cap_hi)); sdcard_reset(regs, SRF_ALL); writew(®s->irq_signal, 0); - writew(®s->irq_enable, 0xffff); + writew(®s->irq_enable, 0x01ff); + writew(®s->irq_status, readw(®s->irq_status)); writew(®s->error_signal, 0); + writew(®s->error_irq_enable, 0x03ff); + writew(®s->error_irq_status, readw(®s->error_irq_status)); writeb(®s->timeout_control, 0x0e); // Set to max timeout int volt = sdcard_set_power(regs); if (volt < 0)
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/hw/sdcard.c | 43 ++++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 17 deletions(-)
diff --git a/src/hw/sdcard.c b/src/hw/sdcard.c index 1f26291..68888bf 100644 --- a/src/hw/sdcard.c +++ b/src/hw/sdcard.c @@ -63,6 +63,7 @@ struct sdhci_s { #define SCB_R48o 0x02 // Response R3, R4 #define SCB_R136 0x09 // Response R2 #define SC_GO_IDLE_STATE ((0<<8) | SCB_R0) +#define SC_SEND_OP_COND ((1<<8) | SCB_R48o) #define SC_ALL_SEND_CID ((2<<8) | SCB_R136) #define SC_SEND_RELATIVE_ADDR ((3<<8) | SCB_R48) #define SC_SELECT_DESELECT_CARD ((7<<8) | SCB_R48b) @@ -138,9 +139,8 @@ struct sddrive_s { };
// SD card types -#define SF_MMC 0 -#define SF_SDSC 1 -#define SF_SDHC 2 +#define SF_SC 1 +#define SF_HC 2
// Repeatedly read a u16 register until any bit in a given mask is set static int @@ -228,7 +228,7 @@ sdcard_pio_transfer(struct sddrive_s *drive, int cmd, u32 addr u16 tmode = ((count > 1 ? ST_MULTIPLE|ST_AUTO_CMD12|ST_BLOCKCOUNT : 0) | (isread ? ST_READ : 0)); writew(&drive->regs->transfer_mode, tmode); - if (drive->card_type < SF_SDHC) + if (drive->card_type < SF_HC) addr *= DISK_SECTOR_SIZE; u32 param[4] = { addr }; int ret = sdcard_pio(drive->regs, cmd, param); @@ -305,24 +305,32 @@ sdcard_card_setup(struct sdhci_s *regs, int volt) if (ret) return ret; // Let card know SDHC/SDXC is supported and confirm voltage + u32 isMMC = 0, hcs = 0; u32 vrange = (volt >= (1<<15) ? 0x100 : 0x200) | 0xaa; param[0] = vrange; ret = sdcard_pio(regs, SC_SEND_IF_COND, param); - if (ret) - return ret; - u32 hcs = 0; - if (param[0] == vrange) - hcs = 0x40000000; + if (!ret && param[0] == vrange) + hcs = (1<<30); // 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; + if (ret) { + // Check for MMC card + param[0] = 0x00; + ret = sdcard_pio(regs, SC_SEND_OP_COND, param); + if (ret) + return ret; + isMMC = 1; + hcs = (1<<30); + } // Init card u32 end = timer_calc(SDHCI_POWERUP_TIMEOUT); for (;;) { - param[0] = hcs | volt; // SDHC support and voltage level - ret = sdcard_pio_app(regs, SC_APP_SEND_OP_COND, param); + param[0] = hcs | volt; // high-capacity support and voltage level + if (isMMC) + ret = sdcard_pio(regs, SC_SEND_OP_COND, param); + else + ret = sdcard_pio_app(regs, SC_APP_SEND_OP_COND, param); if (ret) return ret; if (param[0] & SR_OCR_NOTBUSY) @@ -331,18 +339,19 @@ sdcard_card_setup(struct sdhci_s *regs, int volt) warn_timeout(); return -1; } + msleep(5); // Avoid flooding log when debugging } - int card_type = (param[0] & SR_OCR_CCS) ? SF_SDHC : SF_SDSC; - param[0] = 0x00; + int card_type = (param[0] & SR_OCR_CCS) ? SF_HC : SF_SC; // Select card + param[0] = 0x00; ret = sdcard_pio(regs, SC_ALL_SEND_CID, param); if (ret) return ret; - param[0] = 0x00; + param[0] = isMMC ? 0x0001 << 16 : 0x00; ret = sdcard_pio(regs, SC_SEND_RELATIVE_ADDR, param); if (ret) return ret; - u16 rca = param[0] >> 16; + u16 rca = isMMC ? 0x0001 : param[0] >> 16; param[0] = rca << 16; ret = sdcard_pio(regs, SC_SELECT_DESELECT_CARD, param); if (ret)
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/Kconfig | 2 +- src/hw/sdcard.c | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-)
diff --git a/src/Kconfig b/src/Kconfig index d70624e..6256242 100644 --- a/src/Kconfig +++ b/src/Kconfig @@ -161,7 +161,7 @@ menu "Hardware support" help Support for AHCI disk code. config SDCARD - depends on DRIVES && QEMU_HARDWARE + depends on DRIVES bool "SD controllers" default y help diff --git a/src/hw/sdcard.c b/src/hw/sdcard.c index 68888bf..540e97c 100644 --- a/src/hw/sdcard.c +++ b/src/hw/sdcard.c @@ -5,7 +5,6 @@ // This file may be distributed under the terms of the GNU LGPLv3 license.
#include "block.h" // struct drive_s -#include "fw/paravirt.h" // runningOnQEMU #include "malloc.h" // malloc_fseg #include "output.h" // znprintf #include "pci.h" // pci_config_readl @@ -297,8 +296,6 @@ sdcard_process_op(struct disk_op_s *op) static int sdcard_card_setup(struct sdhci_s *regs, int volt) { - // XXX - works on QEMU; probably wont on real hardware! - // Reset card u32 param[4] = { }; int ret = sdcard_pio(regs, SC_GO_IDLE_STATE, param); @@ -429,9 +426,6 @@ sdcard_controller_setup(void *data) PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
// Initialize controller - 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
Allow a hardcoded address to be specified in cbfs files with a prefix of "etc/sdcard". Some real-world devices have valid SDHCI controllers that do not show up as PCI devices.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- docs/Runtime_config.md | 1 + src/hw/sdcard.c | 48 ++++++++++++++++++++++++++++++++++++------------ 2 files changed, 37 insertions(+), 12 deletions(-)
diff --git a/docs/Runtime_config.md b/docs/Runtime_config.md index 962a8a9..40b4e73 100644 --- a/docs/Runtime_config.md +++ b/docs/Runtime_config.md @@ -189,3 +189,4 @@ There are several additional configuration options available in the | floppy0 | Set this to the type of the first floppy drive in the system (only type 4 for 3.5 inch drives is supported). | floppy1 | The type of the second floppy drive in the system. See the description of **floppy0** for more info. | threads | By default, SeaBIOS will parallelize hardware initialization during bootup to reduce boot time. Multiple hardware devices can be initialized in parallel between vga initialization and option rom initialization. One can set this file to a value of zero to force hardware initialization to run serially. Alternatively, one can set this file to 2 to enable early hardware initialization that runs in parallel with vga, option rom initialization, and the boot menu. +| sdcard* | One may create one or more files with an "sdcard" prefix (eg, "etc/sdcard0") with the physical memory address of an SDHCI controller (one memory address per file). This may be useful for SDHCI controllers that do not appear as PCI devices, but are mapped to a consistent memory address. diff --git a/src/hw/sdcard.c b/src/hw/sdcard.c index 540e97c..8e7e954 100644 --- a/src/hw/sdcard.c +++ b/src/hw/sdcard.c @@ -10,6 +10,7 @@ #include "pci.h" // pci_config_readl #include "pci_ids.h" // PCI_CLASS_SYSTEM_SDHCI #include "pci_regs.h" // PCI_BASE_ADDRESS_0 +#include "romfile.h" // romfile_findprefix #include "stacks.h" // wait_preempt #include "std/disk.h" // DISK_RET_SUCCESS #include "string.h" // memset @@ -416,15 +417,8 @@ sdcard_set_frequency(struct sdhci_s *regs, u32 khz)
// Setup and configure an SD card controller static void -sdcard_controller_setup(void *data) +sdcard_controller_setup(struct sdhci_s *regs, int prio) { - struct pci_device *pci = data; - u16 bdf = pci->bdf; - wait_preempt(); // Avoid pci_config_readl when preempting - struct sdhci_s *regs = (void*)pci_config_readl(bdf, PCI_BASE_ADDRESS_0); - pci_config_maskw(bdf, PCI_COMMAND, 0, - PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); - // Initialize controller u32 present_state = readl(®s->present_state); if (!(present_state & SP_CARD_INSERTED)) @@ -470,27 +464,57 @@ sdcard_controller_setup(void *data) drive->regs = regs; drive->card_type = card_type;
- dprintf(1, "Found SD Card at %02x:%02x.%x\n" - , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), pci_bdf_to_fn(bdf)); + dprintf(1, "Found SD Card at %p\n", regs); char *desc = znprintf(MAXDESCSIZE, "SD Card"); // XXX - boot_add_hd(&drive->drive, desc, bootprio_find_pci_device(pci)); + boot_add_hd(&drive->drive, desc, prio); return; fail: writeb(®s->power_control, 0); writew(®s->clock_control, 0); }
+static void +sdcard_pci_setup(void *data) +{ + struct pci_device *pci = data; + wait_preempt(); // Avoid pci_config_readl when preempting + // XXX - bars dependent on slot index register in pci config space + u32 regs = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_0); + pci_config_maskw(pci->bdf, PCI_COMMAND, 0, + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + int prio = bootprio_find_pci_device(pci); + sdcard_controller_setup((void*)regs, prio); +} + +static void +sdcard_romfile_setup(void *data) +{ + struct romfile_s *file = data; + int prio = bootprio_find_named_rom(file->name, 0); + u32 addr = romfile_loadint(file->name, 0); + dprintf(1, "Starting sdcard controller check at addr %x\n", addr); + sdcard_controller_setup((void*)addr, prio); +} + void sdcard_setup(void) { if (!CONFIG_SDCARD) return;
+ struct romfile_s *file = NULL; + for (;;) { + file = romfile_findprefix("etc/sdcard", file); + if (!file) + break; + run_thread(sdcard_romfile_setup, file); + } + struct pci_device *pci; foreachpci(pci) { if (pci->class != PCI_CLASS_SYSTEM_SDHCI || pci->prog_if >= 2) // Not an SDHCI controller following SDHCI spec continue; - run_thread(sdcard_controller_setup, pci); + run_thread(sdcard_pci_setup, pci); } }
On Wed, Aug 12, 2015 at 04:03:21PM -0400, Kevin O'Connor wrote:
This series further improves the SeaBIOS sdcard support. At the conclusion of the series the sdcard support is enabled on configurations beyond QEMU.
This code was tested on an Asus C300 (a Baytrail style Chromebook) with both an external sdcard card and an internal eMMC drive.
FYI, I committed patches 1-13 of this series. (I'm going to resend patch 14 as part of another series.)
-Kevin