struct sdmmc_tuning_ops { int (*dlyb_enable)(struct sdmmc_dlyb *dlyb); void (*set_input_ck)(struct sdmmc_dlyb *dlyb); int (*tuning_prepare)(struct mmci_host *host); int (*set_cfg)(struct sdmmc_dlyb *dlyb, int unit __maybe_unused, int phase, bool sampler __maybe_unused);
};
/* * idma has constraints on idmabase & idmasize for each element * excepted the last element which has no constraint on idmasize
*/
idma->use_bounce_buffer = false;
for_each_sg(data->sg, sg, data->sg_len - 1, i) { if (!IS_ALIGNED(sg->offset, sizeof(u32)) ||
!IS_ALIGNED(sg->length,
host->variant->stm32_idmabsize_align)) {
dev_dbg(mmc_dev(host->mmc), "unaligned scatterlist: ofst:%x length:%d\n",
data->sg->offset, data->sg->length); goto use_bounce_buffer;
}
}
if (!IS_ALIGNED(sg->offset, sizeof(u32))) {
dev_dbg(mmc_dev(host->mmc), "unaligned last scatterlist: ofst:%x length:%d\n",
data->sg->offset, data->sg->length); goto use_bounce_buffer;
}
return 0;
use_bounce_buffer: if (!idma->bounce_buf) {
idma->bounce_buf = dmam_alloc_coherent(dev,
host->mmc->max_req_size,
&idma->bounce_dma_addr,
GFP_KERNEL); if (!idma->bounce_buf) {
dev_err(dev, "Unable to map allocate DMA bounce buffer.\n"); return -ENOMEM;
}
}
/* * cclk = mclk / (2 * clkdiv) * clkdiv 0 => bypass * in ddr mode bypass is not possible
*/ if (desired) { if (desired >= host->mclk && !ddr) {
host->cclk = host->mclk;
} else {
clk = DIV_ROUND_UP(host->mclk, 2 * desired); if (clk > MCI_STM32_CLK_CLKDIV_MSK)
clk = MCI_STM32_CLK_CLKDIV_MSK;
host->cclk = host->mclk / (2 * clk);
}
} else { /* * while power-on phase the clock can't be define to 0, * Only power-off and power-cyc deactivate the clock. * if desired clock is 0, set max divider
*/
clk = MCI_STM32_CLK_CLKDIV_MSK;
host->cclk = host->mclk / (2 * clk);
}
/* Set actual clock for debug */ if (host->mmc->ios.power_mode == MMC_POWER_ON)
host->mmc->actual_clock = host->cclk; else
host->mmc->actual_clock = 0;
if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_4)
clk |= MCI_STM32_CLK_WIDEBUS_4; if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_8)
clk |= MCI_STM32_CLK_WIDEBUS_8;
if (dlyb && dlyb->ops->set_input_ck)
dlyb->ops->set_input_ck(dlyb);
if (ios.power_mode == MMC_POWER_OFF) { /* Only a reset could power-off sdmmc */
reset_control_assert(host->rst);
udelay(2);
reset_control_deassert(host->rst);
/* * Set the SDMMC in Power-cycle state. * This will make that the SDMMC_D[7:0], SDMMC_CMD and SDMMC_CK * are driven low, to prevent the Card from being supplied * through the signal lines.
*/
mmci_write_pwrreg(host, MCI_STM32_PWR_CYC | pwr);
} elseif (ios.power_mode == MMC_POWER_ON) { /* * After power-off (reset): the irq mask defined in probe * functionis lost * ault irq mask (probe) must be activated
*/
writel(MCI_IRQENABLE | host->variant->start_err,
host->base + MMCIMASK0);
/* * After a power-cycle state, we must set the SDMMC in * Power-off. The SDMMC_D[7:0], SDMMC_CMD and SDMMC_CK are * driven high. Then we can set the SDMMC to Power-on state
*/
mmci_write_pwrreg(host, MCI_PWR_OFF | pwr);
mdelay(1);
mmci_write_pwrreg(host, MCI_PWR_ON | pwr);
}
}
/* complete if there is an error or busy_d0end */ if ((status & err_msk) || busy_d0end) goto complete;
/* * On response the busy signaling is reflected in the BUSYD0 flag. * if busy_d0 is in-progress we must activate busyd0end interrupt * to wait this completion. Else this request has no busy step.
*/ if (busy_d0) { if (!host->busy_status) {
writel_relaxed(mask | host->variant->busy_detect_mask,
base + MMCIMASK0);
host->busy_status = status &
(MCI_CMDSENT | MCI_CMDRESPEND);
} returnfalse;
}
complete: if (host->busy_status) {
writel_relaxed(mask & ~host->variant->busy_detect_mask,
base + MMCIMASK0);
host->busy_status = 0;
}
writel_relaxed(host->variant->busy_detect_mask, base + MMCICLEAR);
ret = dlyb->ops->dlyb_enable(dlyb); if (ret) return ret;
/* * SDMMC_FBCK is selected when an external Delay Block is needed * with SDR104 or HS200.
*/
clk = host->clk_reg;
clk &= ~MCI_STM32_CLK_SEL_MSK;
clk |= MCI_STM32_CLK_SELFBCK;
mmci_write_clkreg(host, clk);
ret = dlyb->ops->tuning_prepare(host); if (ret) return ret;
return sdmmc_dlyb_phase_tuning(host, opcode);
}
staticvoid sdmmc_pre_sig_volt_vswitch(struct mmci_host *host)
{ /* clear the voltage switch completion flag */
writel_relaxed(MCI_STM32_VSWENDC, host->base + MMCICLEAR); /* enable Voltage switch procedure */
mmci_write_pwrreg(host, host->pwr_reg | MCI_STM32_VSWITCHEN);
}
staticint sdmmc_post_sig_volt_switch(struct mmci_host *host, struct mmc_ios *ios)
{ unsignedlong flags;
u32 status; int ret = 0;
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.