/* * According to the standard specification, BIT(3) of SDHCI_SOFTWARE_RESET is * reserved, and only used on Spreadtrum's design, the hardware cannot work * if this bit is cleared. * 1 : normal work * 0 : hardware reset
*/ #define SDHCI_HW_RESET_CARD BIT(3)
/* set dll backup mode */
val = sdhci_readl(host, SDHCI_SPRD_REG_DEBOUNCE);
val |= SDHCI_SPRD_BIT_DLL_BAK | SDHCI_SPRD_BIT_DLL_VAL;
sdhci_writel(host, val, SDHCI_SPRD_REG_DEBOUNCE);
}
staticinline u32 sdhci_sprd_readl(struct sdhci_host *host, int reg)
{ if (unlikely(reg == SDHCI_MAX_CURRENT)) return SDHCI_SPRD_MAX_CUR;
return readl_relaxed(host->ioaddr + reg);
}
staticinlinevoid sdhci_sprd_writel(struct sdhci_host *host, u32 val, int reg)
{ /* SDHCI_MAX_CURRENT is reserved on Spreadtrum's platform */ if (unlikely(reg == SDHCI_MAX_CURRENT)) return;
if (unlikely(reg == SDHCI_SIGNAL_ENABLE || reg == SDHCI_INT_ENABLE))
val = val & SDHCI_SPRD_INT_SIGNAL_MASK;
writel_relaxed(val, host->ioaddr + reg);
}
staticinlinevoid sdhci_sprd_writew(struct sdhci_host *host, u16 val, int reg)
{ /* SDHCI_BLOCK_COUNT is Read Only on Spreadtrum's platform */ if (unlikely(reg == SDHCI_BLOCK_COUNT)) return;
writew_relaxed(val, host->ioaddr + reg);
}
staticinlinevoid sdhci_sprd_writeb(struct sdhci_host *host, u8 val, int reg)
{ /* * Since BIT(3) of SDHCI_SOFTWARE_RESET is reserved according to the * standard specification, sdhci_reset() write this register directly * without checking other reserved bits, that will clear BIT(3) which * is defined as hardware reset on Spreadtrum's platform and clearing * it by mistake will lead the card not work. So here we need to work * around it.
*/ if (unlikely(reg == SDHCI_SOFTWARE_RESET)) { if (readb_relaxed(host->ioaddr + reg) & SDHCI_HW_RESET_CARD)
val |= SDHCI_HW_RESET_CARD;
}
/* * According to the Spreadtrum SD host specification, when we changed * the clock to be more than 52M, we should enable the PHY DLL which * is used to track the clock frequency to make the clock work more * stable. Otherwise deviation may occur of the higher clock.
*/ if (clk_changed && clock > SDHCI_SPRD_PHY_DLL_CLK)
sdhci_sprd_enable_phy_dll(host);
}
ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); /* Select Bus Speed Mode for host */
ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; switch (timing) { case MMC_TIMING_UHS_SDR12:
ctrl_2 |= SDHCI_CTRL_UHS_SDR12; break; case MMC_TIMING_MMC_HS: case MMC_TIMING_SD_HS: case MMC_TIMING_UHS_SDR25:
ctrl_2 |= SDHCI_CTRL_UHS_SDR25; break; case MMC_TIMING_UHS_SDR50:
ctrl_2 |= SDHCI_CTRL_UHS_SDR50; break; case MMC_TIMING_UHS_SDR104:
ctrl_2 |= SDHCI_CTRL_UHS_SDR104; break; case MMC_TIMING_UHS_DDR50: case MMC_TIMING_MMC_DDR52:
ctrl_2 |= SDHCI_CTRL_UHS_DDR50; break; case MMC_TIMING_MMC_HS200:
ctrl_2 |= SDHCI_SPRD_CTRL_HS200; break; case MMC_TIMING_MMC_HS400:
ctrl_2 |= SDHCI_SPRD_CTRL_HS400; break; default: break;
}
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
if (!mmc->ios.enhanced_strobe)
sdhci_writel(host, p[timing], SDHCI_SPRD_REG_32_DLL_DLY);
}
staticvoid sdhci_sprd_hw_reset(struct sdhci_host *host)
{ int val;
/* * Note: don't use sdhci_writeb() API here since it is redirected to * sdhci_sprd_writeb() in which we have a workaround for * SDHCI_SOFTWARE_RESET which would make bit SDHCI_HW_RESET_CARD can * not be cleared.
*/
val = readb_relaxed(host->ioaddr + SDHCI_SOFTWARE_RESET);
val &= ~SDHCI_HW_RESET_CARD;
writeb_relaxed(val, host->ioaddr + SDHCI_SOFTWARE_RESET); /* wait for 10 us */
usleep_range(10, 20);
val |= SDHCI_HW_RESET_CARD;
writeb_relaxed(val, host->ioaddr + SDHCI_SOFTWARE_RESET);
usleep_range(300, 500);
}
staticunsignedint sdhci_sprd_get_max_timeout_count(struct sdhci_host *host)
{ /* The Spredtrum controller actual maximum timeout count is 1 << 31 */ return 1 << 31;
}
staticvoid sdhci_sprd_request_done(struct sdhci_host *host, struct mmc_request *mrq)
{ /* Validate if the request was from software queue firstly. */ if (mmc_hsq_finalize_request(host->mmc, mrq)) return;
/* * From version 4.10 onward, ARGUMENT2 register is also as 32-bit * block count register which doesn't support stuff bits of * CMD23 argument on Spreadtrum's sd host controller.
*/ if (host->version >= SDHCI_SPEC_410 &&
mrq->sbc && (mrq->sbc->arg & SDHCI_SPRD_ARG2_STUFF) &&
(host->flags & SDHCI_AUTO_CMD23))
host->flags &= ~SDHCI_AUTO_CMD23;
}
staticint mmc_send_tuning_data(struct mmc_card *card)
{
u8 *status; int ret;
status = kmalloc(64, GFP_KERNEL); if (!status) return -ENOMEM;
ret = mmc_sd_switch(card, 0, 0, 0, status);
kfree(status);
return ret;
}
staticint sdhci_sprd_get_best_clk_sample(struct mmc_host *mmc, u8 *value)
{ int range_end = SDHCI_SPRD_MAX_RANGE; int range_length = 0; int middle_range = 0; int count = 0; int i;
for (i = 0; i <= SDHCI_SPRD_MAX_RANGE; i++) { if (value[i]) {
pr_debug("%s: tuning ok: %d\n", mmc_hostname(mmc), i);
count++;
} else {
pr_debug("%s: tuning fail: %d\n", mmc_hostname(mmc), i); if (range_length < count) {
range_length = count;
range_end = i - 1;
count = 0;
}
}
}
if (!count) return -EIO;
if (count > range_length) {
range_length = count;
range_end = i - 1;
}
staticvoid sdhci_sprd_phy_param_parse(struct sdhci_sprd_host *sprd_host, struct device_node *np)
{
u32 *p = sprd_host->phy_delay; int ret, i, index;
u32 val[4];
for (i = 0; i < ARRAY_SIZE(sdhci_sprd_phy_cfgs); i++) {
ret = of_property_read_u32_array(np,
sdhci_sprd_phy_cfgs[i].property, val, 4); if (ret) continue;
/* * We can not use the standard ops to change and detect the voltage * signal for Spreadtrum SD host controller, since our voltage regulator * for I/O is fixed in hardware, that means we do not need control * the standard SD host controller to change the I/O voltage.
*/
host->mmc_host_ops.start_signal_voltage_switch =
sdhci_sprd_voltage_switch;
/* * Supply the existing CAPS, but clear the UHS-I modes. This * will allow these modes to be specified only by device * tree properties through mmc_of_parse().
*/
sdhci_read_caps(host);
host->caps1 &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_SDR104 |
SDHCI_SUPPORT_DDR50);
ret = mmc_regulator_get_supply(host->mmc); if (ret) goto pm_runtime_disable;
ret = sdhci_setup_host(host); if (ret) goto pm_runtime_disable;
sprd_host->flags = host->flags;
hsq = devm_kzalloc(&pdev->dev, sizeof(*hsq), GFP_KERNEL); if (!hsq) {
ret = -ENOMEM; goto err_cleanup_host;
}
ret = mmc_hsq_init(hsq, host->mmc); if (ret) goto err_cleanup_host;
ret = __sdhci_add_host(host); if (ret) goto err_cleanup_host;
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.