[PATCH v4 07/12] mmc: sdhci-xenon: Add Marvell Xenon SDHC core functionality
Ziji Hu
huziji at marvell.com
Wed Jan 4 00:51:12 PST 2017
Hi Adrian,
On 2017/1/4 15:26, Adrian Hunter wrote:
> On 13/12/16 19:48, Gregory CLEMENT wrote:
>> From: Hu Ziji <huziji at marvell.com>
>>
>> Add Xenon eMMC/SD/SDIO host controller core functionality.
>> Add Xenon specific intialization process.
>> Add Xenon specific mmc_host_ops APIs.
>> Add Xenon specific register definitions.
>>
>> Add CONFIG_MMC_SDHCI_XENON support in drivers/mmc/host/Kconfig.
>>
>> Marvell Xenon SDHC conforms to SD Physical Layer Specification
>> Version 3.01 and is designed according to the guidelines provided
>> in the SD Host Controller Standard Specification Version 3.00.
>>
>> Signed-off-by: Hu Ziji <huziji at marvell.com>
>> Signed-off-by: Gregory CLEMENT <gregory.clement at free-electrons.com>
<snip>
>> +static void xenon_sdhc_tuning_setup(struct sdhci_host *host)
>> +{
>> + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
>> + struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
>> + u32 reg;
>> +
>> + /* Disable the Re-Tuning Request functionality */
>> + reg = sdhci_readl(host, SDHCI_SLOT_RETUNING_REQ_CTRL);
>> + reg &= ~SDHCI_RETUNING_COMPATIBLE;
>> + sdhci_writel(host, reg, SDHCI_SLOT_RETUNING_REQ_CTRL);
>> +
>> + /* Disable the Re-tuning Event Signal Enable */
>> + reg = sdhci_readl(host, SDHCI_SIGNAL_ENABLE);
>> + reg &= ~SDHCI_INT_RETUNE;
>> + sdhci_writel(host, reg, SDHCI_SIGNAL_ENABLE);
>> +
>> + /* Force to use Tuning Mode 1 */
>> + host->tuning_mode = SDHCI_TUNING_MODE_1;
>> + /* Set re-tuning period */
>> + host->tuning_count = 1 << (priv->tuning_count - 1);
>
> host->tuning_mode and host->tuning_count get overwritten in
> sdhci_setup_host() called by sdhci_add_host()
>
You are correct.
I will move it after sdhci_add_host().
>> +}
>> +
<snip>
>> +/*
>> + * Xenon Specific Operations in mmc_host_ops
>> + */
>> +static void xenon_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
>> +{
>> + struct sdhci_host *host = mmc_priv(mmc);
>> + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
>> + struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
>> + unsigned long flags;
>> + u32 reg;
>> +
>> + /*
>> + * HS400/HS200/eMMC HS doesn't have Preset Value register.
>> + * However, sdhci_set_ios will read HS400/HS200 Preset register.
>> + * Disable Preset Value register for HS400/HS200.
>> + * eMMC HS with preset_enabled set will trigger a bug in
>> + * get_preset_value().
>> + */
>> + spin_lock_irqsave(&host->lock, flags);
>> + if ((ios->timing == MMC_TIMING_MMC_HS400) ||
>> + (ios->timing == MMC_TIMING_MMC_HS200) ||
>> + (ios->timing == MMC_TIMING_MMC_HS)) {
>> + host->preset_enabled = false;
>> + host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
>> +
>> + reg = sdhci_readw(host, SDHCI_HOST_CONTROL2);
>> + reg &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
>> + sdhci_writew(host, reg, SDHCI_HOST_CONTROL2);
>> + } else {
>> + host->quirks2 &= ~SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
>> + }
>> + spin_unlock_irqrestore(&host->lock, flags);
>
> At some point we will have to get rid of SDHCI_QUIRK2_PRESET_VALUE_BROKEN
> and add a callback instead.
>
Thanks for the information.
I would like to keep this workaround here, before the detailed patch is brought up.
Is it OK to you?
>> +
<snip>
>> +static int xenon_start_signal_voltage_switch(struct mmc_host *mmc,
>> + struct mmc_ios *ios)
>> +{
>> + struct sdhci_host *host = mmc_priv(mmc);
>> + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
>> + struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
>> +
>> + /*
>> + * Before SD/SDIO set signal voltage, SD bus clock should be
>> + * disabled. However, sdhci_set_clock will also disable the Internal
>> + * clock in mmc_set_signal_voltage().
>> + * If Internal clock is disabled, the 3.3V/1.8V bit can not be updated.
>> + * Thus here manually enable internal clock.
>> + *
>> + * After switch completes, it is unnecessary to disable internal clock,
>> + * since keeping internal clock active obeys SD spec.
>> + */
>> + enable_xenon_internal_clk(host);
>
> We could try the attached patch.
>
I test your patch. It can work on my platforms.
Thanks a lot.
May I keep this workaround now?
I would like to remove this workaround after your attached patch is applied.
>> +
<snip>
>> +static int sdhci_xenon_remove(struct platform_device *pdev)
>> +{
>> + struct sdhci_host *host = platform_get_drvdata(pdev);
>> + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
>> + int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xFFFFFFFF);
>
> This 'dead' check was originally for PCI I think. Unless you know it makes
> sense for your device, I would leave it out. i.e. just do
> sdhci_remove_host(host, 0);
>
Got it. I will remove it.
>> +
>> + xenon_sdhc_remove(host);
>> +
>> + sdhci_remove_host(host, dead);
>> +
>> + clk_disable_unprepare(pltfm_host->clk);
>> +
>> + sdhci_pltfm_free(pdev);
>> +
>> + return 0;
>> +}
>> +
>> +static const struct of_device_id sdhci_xenon_dt_ids[] = {
>> + { .compatible = "marvell,armada-7000-sdhci",},
>> + { .compatible = "marvell,armada-3700-sdhci",},
>> + {}
>> +};
>> +MODULE_DEVICE_TABLE(of, sdhci_xenon_dt_ids);
>> +
>> +static struct platform_driver sdhci_xenon_driver = {
>> + .driver = {
>> + .name = "xenon-sdhci",
>> + .of_match_table = sdhci_xenon_dt_ids,
>> + .pm = &sdhci_pltfm_pmops,
>> + },
>> + .probe = sdhci_xenon_probe,
>> + .remove = sdhci_xenon_remove,
>> +};
>> +
>> +module_platform_driver(sdhci_xenon_driver);
>> +
>> +MODULE_DESCRIPTION("SDHCI platform driver for Marvell Xenon SDHC");
>> +MODULE_AUTHOR("Hu Ziji <huziji at marvell.com>");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/mmc/host/sdhci-xenon.h b/drivers/mmc/host/sdhci-xenon.h
>> new file mode 100644
>> index 000000000000..d50cd663a265
>> --- /dev/null
>> +++ b/drivers/mmc/host/sdhci-xenon.h
>> @@ -0,0 +1,70 @@
>> +/*
>> + * Copyright (C) 2016 Marvell, All Rights Reserved.
>> + *
>> + * Author: Hu Ziji <huziji at marvell.com>
>> + * Date: 2016-8-24
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License as
>> + * published by the Free Software Foundation version 2.
>> + */
>> +#ifndef SDHCI_XENON_H_
>> +#define SDHCI_XENON_H_
>> +
>> +
>
> Double blank line
>
Will remove it.
>> +/* Register Offset of Xenon SDHC self-defined register */
>> +#define SDHCI_SYS_CFG_INFO 0x0104
>
> A lot of these defines look like they could be just in sdhci-xenon.c or
> sdhci-xenon-phy.c. It is also a little odd that they are prefixed by
> "SDHCI" because they are not standard. "XENON" would be better.
>
Some registers are accessed by bother sdhci-xenon.c and sdhci-xenon-phy.c.
As a result, I list all the registers here in sdhci-xenon.h, for convenience.
Previously, Ulf asked me to prefix them all with "SDHCI".
I would like to know which prefix sounds more reasonable, "XENON_" or "SDHCI_XENON_"?
>> +#define SDHCI_SLOT_TYPE_SDIO_SHIFT 24
>> +#define SDHCI_NR_SUPPORTED_SLOT_MASK 0x7
>> +
>> +#define SDHCI_SYS_OP_CTRL 0x0108
>> +#define SDHCI_AUTO_CLKGATE_DISABLE_MASK BIT(20)
>> +#define SDHCI_SDCLK_IDLEOFF_ENABLE_SHIFT 8
>> +#define SDHCI_SLOT_ENABLE_SHIFT 0
>> +
>> +#define SDHCI_SYS_EXT_OP_CTRL 0x010C
>> +
>> +#define SDHCI_SLOT_EMMC_CTRL 0x0130
>> +#define SDHCI_EMMC_VCCQ_MASK 0x3
>> +#define SDHCI_EMMC_VCCQ_1_8V 0x1
>> +#define SDHCI_EMMC_VCCQ_3_3V 0x3
>> +
>> +#define SDHCI_SLOT_RETUNING_REQ_CTRL 0x0144
>> +/* retuning compatible */
>> +#define SDHCI_RETUNING_COMPATIBLE 0x1
>> +
>> +/* Tuning Parameter */
>> +#define SDHCI_TMR_RETUN_NO_PRESENT 0xF
>> +#define SDHCI_DEF_TUNING_COUNT 0x9
>> +
>> +#define SDHCI_DEFAULT_SDCLK_FREQ (400000)
>
> Unnecessary ()
>
Will fix it.
Thanks a lot for the review.
Best regards,
Hu Ziji
>> +
>> +/* Xenon specific Mode Select value */
>> +#define SDHCI_XENON_CTRL_HS200 0x5
>> +#define SDHCI_XENON_CTRL_HS400 0x6
>> +
>> +/* Indicate Card Type is not clear yet */
>> +#define SDHCI_CARD_TYPE_UNKNOWN 0xF
>> +
>> +struct sdhci_xenon_priv {
>> + unsigned char tuning_count;
>> + /* idx of SDHC */
>> + u8 sdhc_id;
>> +
>> + /*
>> + * eMMC/SD/SDIO require different PHY settings or
>> + * voltage control. It's necessary for Xenon driver to
>> + * recognize card type during, or even before initialization.
>> + * However, mmc_host->card is not available yet at that time.
>> + * This field records the card type during init.
>> + * For eMMC, it is updated in dt parse. For SD/SDIO, it is
>> + * updated in xenon_init_card().
>> + *
>> + * It is only valid during initialization after it is updated.
>> + * Do not access this variable in normal transfers after
>> + * initialization completes.
>> + */
>> + unsigned int init_card_type;
>> +};
>> +
>> +#endif
>>
>
More information about the linux-arm-kernel
mailing list