On 5 September 2013 18:04, Dong Aisheng <dongas86@xxxxxxxxx> wrote: > Hi Ulf, > > On Thu, Sep 5, 2013 at 3:38 PM, Ulf Hansson <ulf.hansson@xxxxxxxxxx> wrote: >> On 4 September 2013 14:54, Dong Aisheng <b29396@xxxxxxxxxxxxx> wrote: >>> Without proper pinctrl state, the card may not be able to work >>> on high speed stablely. e.g. SDR104. >>> >>> This patch add pinctrl state switch code according to different >>> uhs mode include 100mhz sate, 200mhz sate and normal state >>> (50Mhz and below). >>> >>> Signed-off-by: Dong Aisheng <b29396@xxxxxxxxxxxxx> >>> --- >>> drivers/mmc/host/sdhci-esdhc-imx.c | 110 ++++++++++++++++++++++++++- >>> include/linux/platform_data/mmc-esdhc-imx.h | 4 + >>> 2 files changed, 113 insertions(+), 1 deletions(-) >>> >>> diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c >>> index 36b9f63..3e89863 100644 >>> --- a/drivers/mmc/host/sdhci-esdhc-imx.c >>> +++ b/drivers/mmc/host/sdhci-esdhc-imx.c >>> @@ -49,6 +49,10 @@ >>> >>> #define ESDHC_TUNING_BLOCK_PATTERN_LEN 64 >>> >>> +/* pinctrl state */ >>> +#define ESDHC_PINCTRL_STATE_100MHZ "state_100mhz" >>> +#define ESDHC_PINCTRL_STATE_200MHZ "state_200mhz" >>> + >>> /* >>> * Our interpretation of the SDHCI_HOST_CONTROL register >>> */ >>> @@ -90,6 +94,10 @@ struct pltfm_imx_data { >>> u32 scratchpad; >>> enum imx_esdhc_type devtype; >>> struct pinctrl *pinctrl; >>> + struct pinctrl_state *pins_current; >>> + struct pinctrl_state *pins_default; >>> + struct pinctrl_state *pins_100mhz; >>> + struct pinctrl_state *pins_200mhz; >>> struct esdhc_platform_data boarddata; >>> struct clk *clk_ipg; >>> struct clk *clk_ahb; >>> @@ -642,6 +650,75 @@ static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode) >>> return ret; >>> } >>> >>> +static int esdhc_change_pinstate(struct sdhci_host *host, >>> + unsigned int uhs) >>> +{ >>> + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); >>> + struct pltfm_imx_data *imx_data = pltfm_host->priv; >>> + struct pinctrl_state *pinctrl; >>> + int ret; >>> + >>> + dev_dbg(mmc_dev(host->mmc), "change pinctrl state for uhs %d\n", uhs); >>> + >>> + if (IS_ERR(imx_data->pinctrl) || >>> + IS_ERR(imx_data->pins_default) || >>> + IS_ERR(imx_data->pins_100mhz) || >>> + IS_ERR(imx_data->pins_200mhz)) >>> + return -EINVAL; >>> + >>> + switch (uhs) { >>> + case MMC_TIMING_UHS_SDR12: >>> + case MMC_TIMING_UHS_SDR25: >>> + case MMC_TIMING_UHS_DDR50: >>> + pinctrl = imx_data->pins_default; >>> + break; >>> + case MMC_TIMING_UHS_SDR50: >>> + pinctrl = imx_data->pins_100mhz; >>> + break; >>> + case MMC_TIMING_UHS_SDR104: >>> + pinctrl = imx_data->pins_200mhz; >>> + break; >>> + default: >>> + /* back to default state for other legacy timing */ >>> + pinctrl = imx_data->pins_default; >>> + } >>> + >>> + if (pinctrl == imx_data->pins_current) >>> + return 0; >>> + >>> + ret = pinctrl_select_state(imx_data->pinctrl, pinctrl); >>> + if (!ret) >>> + imx_data->pins_current = pinctrl; >>> + >>> + return ret; >>> +} >>> + >>> +static int esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) >>> +{ >>> + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); >>> + struct pltfm_imx_data *imx_data = pltfm_host->priv; >>> + >>> + switch (uhs) { >>> + case MMC_TIMING_UHS_SDR12: >>> + imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR12; >>> + break; >>> + case MMC_TIMING_UHS_SDR25: >>> + imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR25; >>> + break; >>> + case MMC_TIMING_UHS_SDR50: >>> + imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR50; >>> + break; >>> + case MMC_TIMING_UHS_SDR104: >>> + imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR104; >>> + break; >>> + case MMC_TIMING_UHS_DDR50: >>> + imx_data->uhs_mode = SDHCI_CTRL_UHS_DDR50; >>> + break; >>> + } >>> + >>> + return esdhc_change_pinstate(host, uhs); >>> +} >>> + >>> static const struct sdhci_ops sdhci_esdhc_ops = { >>> .read_l = esdhc_readl_le, >>> .read_w = esdhc_readw_le, >>> @@ -653,6 +730,7 @@ static const struct sdhci_ops sdhci_esdhc_ops = { >>> .get_min_clock = esdhc_pltfm_get_min_clock, >>> .get_ro = esdhc_pltfm_get_ro, >>> .platform_bus_width = esdhc_pltfm_bus_width, >>> + .set_uhs_signaling = esdhc_set_uhs_signaling, >>> .platform_execute_tuning = esdhc_executing_tuning, >>> }; >>> >>> @@ -695,6 +773,11 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev, >>> >>> of_property_read_u32(np, "max-frequency", &boarddata->f_max); >>> >>> + if (of_find_property(np, "no-1-8-v", NULL)) >>> + boarddata->support_vsel = false; >>> + else >>> + boarddata->support_vsel = true; >>> + >>> return 0; >>> } >>> #else >>> @@ -757,12 +840,20 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) >>> clk_prepare_enable(imx_data->clk_ipg); >>> clk_prepare_enable(imx_data->clk_ahb); >>> >>> - imx_data->pinctrl = devm_pinctrl_get_select_default(&pdev->dev); >>> + imx_data->pinctrl = devm_pinctrl_get(&pdev->dev); >>> if (IS_ERR(imx_data->pinctrl)) { >>> err = PTR_ERR(imx_data->pinctrl); >>> goto disable_clk; >>> } >>> >>> + imx_data->pins_default = pinctrl_lookup_state(imx_data->pinctrl, >>> + PINCTRL_STATE_DEFAULT); >> >> I believe you should look into the new pinctrl APIs and the new >> sequence at device driver core probe, which automatically fetches >> default, idle and sleep state pins. Additionally it sets the default >> state. >> >> It should likely simplifies some code in this patch. >> > > After looking into the pinctrl automatically fetch states, it seems > above two line may could be replaced as: > imx_data->pinctrl = (&pdev->dev)->pins->p; > imx_data->pins_default = (&pdev->dev)->pins->default_state; pinctrl_pm_select_default_state(dev) pinctrl_pm_select_ilde_state(dev) pinctrl_pm_select_sleep_state(dev) The states are fetched from device core at probe. Kind regards Ulf Hansson > > However, i'm not sure touching the pinctrl internal implementations in > device core is a good choice. > So i may still like to keep the exist using. > > Regards > Dong Aisheng > >> Kind regards >> Ulf Hansson >> >> >>> + if (IS_ERR(imx_data->pins_default)) { >>> + err = PTR_ERR(imx_data->pins_default); >>> + dev_err(mmc_dev(host->mmc), "could not get default state\n"); >>> + goto disable_clk; >>> + } >>> + >>> host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; >>> >>> if (is_imx25_esdhc(imx_data) || is_imx35_esdhc(imx_data)) >>> @@ -839,6 +930,23 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) >>> break; >>> } >>> >>> + /* sdr50 and sdr104 needs work on 1.8v signal voltage */ >>> + if ((boarddata->support_vsel) && is_imx6q_usdhc(imx_data)) { >>> + imx_data->pins_100mhz = pinctrl_lookup_state(imx_data->pinctrl, >>> + ESDHC_PINCTRL_STATE_100MHZ); >>> + imx_data->pins_200mhz = pinctrl_lookup_state(imx_data->pinctrl, >>> + ESDHC_PINCTRL_STATE_200MHZ); >>> + if (IS_ERR(imx_data->pins_100mhz) || >>> + IS_ERR(imx_data->pins_200mhz)) { >>> + dev_warn(mmc_dev(host->mmc), >>> + "could not get ultra high speed state, work on normal mode\n"); >>> + /* fall back to not support uhs by specify no 1.8v quirk */ >>> + host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V; >>> + } >>> + } else { >>> + host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V; >>> + } >>> + >>> err = sdhci_add_host(host); >>> if (err) >>> goto disable_clk; >>> diff --git a/include/linux/platform_data/mmc-esdhc-imx.h b/include/linux/platform_data/mmc-esdhc-imx.h >>> index d44912d..a0f5a8f 100644 >>> --- a/include/linux/platform_data/mmc-esdhc-imx.h >>> +++ b/include/linux/platform_data/mmc-esdhc-imx.h >>> @@ -10,6 +10,8 @@ >>> #ifndef __ASM_ARCH_IMX_ESDHC_H >>> #define __ASM_ARCH_IMX_ESDHC_H >>> >>> +#include <linux/types.h> >>> + >>> enum wp_types { >>> ESDHC_WP_NONE, /* no WP, neither controller nor gpio */ >>> ESDHC_WP_CONTROLLER, /* mmc controller internal WP */ >>> @@ -32,6 +34,7 @@ enum cd_types { >>> * @cd_gpio: gpio for card_detect interrupt >>> * @wp_type: type of write_protect method (see wp_types enum above) >>> * @cd_type: type of card_detect method (see cd_types enum above) >>> + * @support_vsel: indicate it supports 1.8v switching >>> */ >>> >>> struct esdhc_platform_data { >>> @@ -41,5 +44,6 @@ struct esdhc_platform_data { >>> enum cd_types cd_type; >>> int max_bus_width; >>> unsigned int f_max; >>> + bool support_vsel; >>> }; >>> #endif /* __ASM_ARCH_IMX_ESDHC_H */ >>> -- >>> 1.7.1 >>> >>> >>> -- >>> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in >>> the body of a message to majordomo@xxxxxxxxxxxxxxx >>> More majordomo info at http://vger.kernel.org/majordomo-info.html >> >> _______________________________________________ >> linux-arm-kernel mailing list >> linux-arm-kernel@xxxxxxxxxxxxxxxxxxx >> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html