/* * Starting point for SDIO card init. */ int mmc_attach_sdio(void *pCard) { SDM_CARD_INFO* card; int err, i, funcs; if (pCard == NULL) return -EPERM; card = (SDM_CARD_INFO*)pCard; /* * Detect and init the card. */ err = mmc_sdio_init_card(pCard); if (err) goto remove; /* * The number of functions on the card is encoded inside * the ocr. */ funcs = (card->ocr & 0x70000000) >> 28; card->sdio_funcs = 0; /* * Initialize (but don't add) all present functions. */ for (i = 0; i < funcs; i++, card->sdio_funcs++) { err = sdio_init_func(pCard, i + 1); if (err) goto remove; } card->type = SDIO; return 0; remove: return err; }
zx_status_t sdmmc_probe_sdio(sdmmc_device_t* dev) { zx_status_t st = sdmmc_sdio_reset(dev); if ((st = sdmmc_go_idle(dev)) != ZX_OK) { zxlogf(ERROR, "sdmmc: SDMMC_GO_IDLE_STATE failed, retcode = %d\n", st); return st; } uint32_t ocr; if ((st = sdio_send_op_cond(dev, 0, &ocr)) != ZX_OK) { zxlogf(TRACE, "sdmmc_probe_sdio: SDIO_SEND_OP_COND failed, retcode = %d\n", st); return st; } //Select voltage 3.3 V. Also request for 1.8V. Section 3.2 SDIO spec if (ocr & SDIO_SEND_OP_COND_IO_OCR_33V) { uint32_t new_ocr = SDIO_SEND_OP_COND_IO_OCR_33V | SDIO_SEND_OP_COND_CMD_S18R; if ((st = sdio_send_op_cond(dev, new_ocr, &ocr)) != ZX_OK) { zxlogf(ERROR, "sdmmc_probe_sdio: SDIO_SEND_OP_COND failed, retcode = %d\n", st); return st; } } if (ocr & SDIO_SEND_OP_COND_RESP_MEM_PRESENT) { //Combo cards not supported zxlogf(ERROR, "sdmmc_probe_sdio: Combo card not supported\n"); return ZX_ERR_NOT_SUPPORTED; } dev->type = SDMMC_TYPE_SDIO; dev->signal_voltage = SDMMC_VOLTAGE_V180; dev->sdio_dev.hw_info.num_funcs = get_bits(ocr, SDIO_SEND_OP_COND_RESP_NUM_FUNC_MASK, SDIO_SEND_OP_COND_RESP_NUM_FUNC_LOC); uint16_t addr = 0; if ((st = sd_send_relative_addr(dev, &addr)) != ZX_OK) { zxlogf(ERROR, "sdmcc_probe_sdio: SD_SEND_RELATIVE_ADDR failed, retcode = %d\n", st); return st; } dev->rca = addr; if ((st = mmc_select_card(dev)) != ZX_OK) { zxlogf(ERROR, "sdmmc_probe_sdio: MMC_SELECT_CARD failed, retcode = %d\n", st); return st; } if ((st = sdio_process_cccr(dev)) != ZX_OK) { zxlogf(ERROR, "sdmmc_probe_sdio: Read CCCR failed, retcode = %d\n", st); return st; } //Read CIS to get max block size if ((st = sdio_process_cis(dev, 0)) != ZX_OK) { zxlogf(ERROR, "sdmmc_probe_sdio: Read CIS failed, retcode = %d\n", st); return st; } if (ocr & SDIO_SEND_OP_COND_RESP_S18A) { if ((st = sd_switch_uhs_voltage(dev, ocr)) != ZX_OK) { zxlogf(INFO, "Failed to switch voltage to 1.8V\n"); return st; } } // BCM43458 includes function 0 in its OCR register. This violates the SDIO specification and // the assumptions made here. Check the manufacturer ID to account for this quirk. if (dev->sdio_dev.funcs[0].hw_info.manufacturer_id != bcm_manufacturer_id) { dev->sdio_dev.hw_info.num_funcs++; } //TODO(ravoorir):Re-enable ultra high speed when wifi stack is more stable. /* if (sdio_is_uhs_supported(dev->sdio_dev.hw_info.caps)) { if ((st = sdio_switch_bus_width(dev, SDIO_BW_4BIT)) != ZX_OK) { zxlogf(ERROR, "sdmmc_probe_sdio: Swtiching to 4-bit bus width failed, retcode = %d\n", st); goto high_speed; } if ((st = sdio_switch_uhs(dev)) != ZX_OK) { zxlogf(ERROR, "sdmmc_probe_sdio: Switching to high speed failed, retcode = %d\n", st); goto high_speed; } uint32_t hw_caps = dev->sdio_dev.hw_info.caps; if ((hw_caps & SDIO_CARD_UHS_SDR104) || (hw_caps & SDIO_CARD_UHS_SDR50)) { st = sdmmc_perform_tuning(&dev->host, SD_SEND_TUNING_BLOCK); if (st != ZX_OK) { zxlogf(ERROR, "mmc: tuning failed %d\n", st); goto high_speed; } } goto complete; } high_speed: */ if (dev->sdio_dev.hw_info.caps & SDIO_CARD_HIGH_SPEED) { if ((st = sdio_switch_hs(dev)) != ZX_OK) { zxlogf(ERROR, "sdmmc_probe_sdio: Switching to high speed failed, retcode = %d\n", st); goto default_speed; } if ((st = sdio_switch_bus_width(dev, SDIO_BW_4BIT)) != ZX_OK) { zxlogf(ERROR, "sdmmc_probe_sdio: Swtiching to 4-bit bus width failed, retcode = %d\n", st); goto default_speed; } goto complete; } default_speed: if ((st = sdio_switch_freq(dev, SDIO_DEFAULT_FREQ)) != ZX_OK) { zxlogf(ERROR, "sdmmc_probe_sdio: Switch freq retcode = %d\n", st); return st; } complete: sdio_modify_block_size(dev, 0, 0, true); // 0 is the common function. Already initialized for (size_t i = 1; i < dev->sdio_dev.hw_info.num_funcs; i++) { st = sdio_init_func(dev, i); } zxlogf(INFO, "sdmmc_probe_sdio: sdio device initialized successfully\n"); zxlogf(INFO, " Manufacturer: 0x%x\n", dev->sdio_dev.funcs[0].hw_info.manufacturer_id); zxlogf(INFO, " Product: 0x%x\n", dev->sdio_dev.funcs[0].hw_info.product_id); zxlogf(INFO, " cccr vsn: 0x%x\n", dev->sdio_dev.hw_info.cccr_vsn); zxlogf(INFO, " SDIO vsn: 0x%x\n", dev->sdio_dev.hw_info.sdio_vsn); zxlogf(INFO, " num funcs: %d\n", dev->sdio_dev.hw_info.num_funcs); return ZX_OK; }