ret = regmap_read_poll_timeout(host->regmap, MESON_SDHC_STAT, stat,
!(stat & MESON_SDHC_STAT_CMD_BUSY),
MESON_SDHC_WAIT_CMD_READY_SLEEP_US,
MESON_SDHC_WAIT_CMD_READY_TIMEOUT_US); if (ret) {
dev_warn(mmc_dev(mmc), "Failed to poll for CMD_BUSY while processing CMD%d\n",
host->cmd->opcode);
meson_mx_sdhc_reset(host);
}
ret = regmap_read_poll_timeout(host->regmap, MESON_SDHC_ESTA, esta,
!(esta & MESON_SDHC_ESTA_11_13),
MESON_SDHC_WAIT_CMD_READY_SLEEP_US,
MESON_SDHC_WAIT_CMD_READY_TIMEOUT_US); if (ret) {
dev_warn(mmc_dev(mmc), "Failed to poll for ESTA[13:11] while processing CMD%d\n",
host->cmd->opcode);
meson_mx_sdhc_reset(host);
}
}
if (cmd->data->flags & MMC_DATA_WRITE)
send |= MESON_SDHC_SEND_DATA_DIR;
/* * If command with no data, just wait response done * interrupt(int[0]), and if command with data transfer, just * wait dma done interrupt(int[11]), don't need care about * dat0 busy or not.
*/ if (host->platform->hardware_flush_all_cmds ||
cmd->data->flags & MMC_DATA_WRITE) /* hardware flush: */
ictl |= MESON_SDHC_ICTL_DMA_DONE; else /* software flush: */
ictl |= MESON_SDHC_ICTL_DATA_XFER_OK;
/* * Mimic the logic from the vendor driver where (only) * SD_IO_RW_EXTENDED commands with more than one block set the * MESON_SDHC_MISC_MANUAL_STOP bit. This fixes the firmware * download in the brcmfmac driver for a BCM43362/1 card. * Without this sdio_memcpy_toio() (with a size of 219557 * bytes) times out if MESON_SDHC_MISC_MANUAL_STOP is not set.
*/
manual_stop = cmd->data->blocks > 1 &&
cmd->opcode == SD_IO_RW_EXTENDED;
} else {
pack_len = 0;
if (!(cmd->flags & MMC_RSP_CRC))
send |= MESON_SDHC_SEND_RESP_NO_CRC;
if (cmd->flags & MMC_RSP_BUSY)
send |= MESON_SDHC_SEND_R1B;
/* enable the new IRQs and mask all pending ones */
regmap_write(host->regmap, MESON_SDHC_ICTL, ictl);
regmap_write(host->regmap, MESON_SDHC_ISTA, MESON_SDHC_ISTA_ALL_IRQS);
if (ios->clock) {
ret = clk_set_rate(host->sd_clk, ios->clock); if (ret) {
dev_warn(mmc_dev(mmc), "Failed to set MMC clock to %uHz: %d\n",
ios->clock, host->error); return ret;
}
ret = meson_mx_sdhc_enable_clks(mmc); if (ret) return ret;
mmc->actual_clock = clk_get_rate(host->sd_clk);
/* * Phase 90 should work in most cases. For data transmission, * meson_mx_sdhc_execute_tuning() will find a accurate value
*/
regmap_read(host->regmap, MESON_SDHC_CLKC, &val);
rx_clk_phase = FIELD_GET(MESON_SDHC_CLKC_CLK_DIV, val) / 4;
regmap_update_bits(host->regmap, MESON_SDHC_CLK2,
MESON_SDHC_CLK2_RX_CLK_PHASE,
FIELD_PREP(MESON_SDHC_CLK2_RX_CLK_PHASE,
rx_clk_phase));
} else {
mmc->actual_clock = 0;
}
dev_dbg(mmc_dev(mmc), "New best RX phase window: %u - %u\n",
best_start, best_start + best_len);
}
/* reset the current window */
len = 0;
}
}
if (len > best_len) /* the last window is the best (or possibly only) window */
new_phase = start + (len / 2); elseif (best_len) /* there was a better window than the last */
new_phase = best_start + (best_len / 2); else /* no window was found at all, reset to the original phase */
new_phase = old_phase;
cmd = host->cmd; if (WARN_ON(!cmd)) return IRQ_HANDLED;
if (cmd->data && !cmd->data->error) { if (!host->platform->hardware_flush_all_cmds &&
cmd->data->flags & MMC_DATA_READ) {
meson_mx_sdhc_wait_cmd_ready(host->mmc);
/* * If MESON_SDHC_PDMA_RXFIFO_MANUAL_FLUSH was * previously 0x1 then it has to be set to 0x3. If it * was 0x0 before then it has to be set to 0x2. Without * this reading SD cards sometimes transfers garbage, * which results in cards not being detected due to: * unrecognised SCR structure version <random number>
*/
val = FIELD_PREP(MESON_SDHC_PDMA_RXFIFO_MANUAL_FLUSH,
2);
regmap_update_bits(host->regmap, MESON_SDHC_PDMA, val,
val);
}
if (cmd->error == -EIO || cmd->error == -ETIMEDOUT)
meson_mx_sdhc_reset(host); elseif (cmd->data) /* * Clear the FIFOs after completing data transfers to prevent * corrupting data on write access. It's not clear why this is * needed (for reads and writes), but it mimics what the BSP * kernel did.
*/
meson_mx_sdhc_clear_fifo(host->mmc);
ret = regmap_read_poll_timeout(host->regmap, MESON_SDHC_ESTA, val,
val == 0,
MESON_SDHC_WAIT_BEFORE_SEND_SLEEP_US,
MESON_SDHC_WAIT_BEFORE_SEND_TIMEOUT_US); if (ret)
dev_warn(mmc_dev(mmc), "Failed to wait for ESTA to clear: 0x%08x\n", val);
if (host->cmd->data && host->cmd->data->flags & MMC_DATA_WRITE) {
ret = regmap_read_poll_timeout(host->regmap, MESON_SDHC_STAT,
val, val & MESON_SDHC_STAT_TXFIFO_CNT,
MESON_SDHC_WAIT_BEFORE_SEND_SLEEP_US,
MESON_SDHC_WAIT_BEFORE_SEND_TIMEOUT_US); if (ret)
dev_warn(mmc_dev(mmc), "Failed to wait for TX FIFO to fill\n");
}
}
/* * start with a valid divider and enable the memory (un-setting * MESON_SDHC_CLKC_MEM_PWR_OFF).
*/
regmap_write(host->regmap, MESON_SDHC_CLKC, MESON_SDHC_CLKC_CLK_DIV);
mmc = devm_mmc_alloc_host(dev, sizeof(*host)); if (!mmc) return -ENOMEM;
host = mmc_priv(mmc);
host->mmc = mmc;
platform_set_drvdata(pdev, host);
host->platform = device_get_match_data(dev); if (!host->platform) return -EINVAL;
base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base);
host->regmap = devm_regmap_init_mmio(dev, base,
&meson_mx_sdhc_regmap_config); if (IS_ERR(host->regmap)) return PTR_ERR(host->regmap);
host->pclk = devm_clk_get(dev, "pclk"); if (IS_ERR(host->pclk)) return PTR_ERR(host->pclk);
/* accessing any register requires the module clock to be enabled: */
ret = clk_prepare_enable(host->pclk); if (ret) {
dev_err(dev, "Failed to enable 'pclk' clock\n"); return ret;
}
meson_mx_sdhc_init_hw(mmc);
ret = meson_mx_sdhc_register_clkc(dev, base, host->bulk_clks); if (ret) goto err_disable_pclk;
host->sd_clk = host->bulk_clks[1].clk;
/* Get regulators and the supported OCR mask */
ret = mmc_regulator_get_supply(mmc); if (ret) goto err_disable_pclk;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.