[PATCH 1/3] mmc: sdhci-s3c: Remove usage of clk_type member in platform data

Kyungmin Park kmpark at infradead.org
Tue Oct 4 03:53:24 EDT 2011


Hi,

On Tue, Oct 4, 2011 at 4:28 PM, Thomas Abraham
<thomas.abraham at linaro.org> wrote:
> SDHCI controllers on Exynos4 do not include the sdclk divider as per the
> sdhci controller specification. This case can be represented using the
> sdhci quirk SDHCI_QUIRK_NONSTANDARD_CLOCK instead of using an additional
> enum type definition 'clk_types'.
>
> Hence, usage of clk_type member in platform data is removed and the sdhci
> quirk is used. In addition to that, since this qurik is SoC specific,
> driver data is introduced to represent controllers on SoC's that require
> this quirk.
Right.
>
> Cc: Ben Dooks <ben-linux at fluff.org>
> Cc: Jeongbae Seo <jeongbae.seo at samsung.com>
> Signed-off-by: Thomas Abraham <thomas.abraham at linaro.org>
> ---
> In the function 'sdhci_cmu_set_clock', the code that waits for a stable
> sdclk clock is replicated from 'sdhci_set_clock' function. This portion
> of code is replicated in sdhci-cns3xxx driver as well. This duplication
> could have been avoid by adding a new function in sdhci host controller
> driver that waits for stable sdclk and sdhci-s3c/sdhci-cns3xxx can be
> the users of the new function. If adding a new function in sdhci host
> controller driver is acceptable, an additional patch in this series
> can be added that creates the new function that waits for stable sdclk.
>
>  drivers/mmc/host/sdhci-s3c.c |   74 +++++++++++++++++++++++++++++++++++++++--
>  1 files changed, 70 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c
> index 82709b6..b29e734 100644
> --- a/drivers/mmc/host/sdhci-s3c.c
> +++ b/drivers/mmc/host/sdhci-s3c.c
> @@ -53,6 +53,18 @@ struct sdhci_s3c {
>        struct clk              *clk_bus[MAX_BUS_CLK];
>  };
>
> +/**
> + * struct sdhci_s3c_driver_data - S3C SDHCI platform specific driver data
> + * @sdhci_quirks: sdhci host specific quirks.
> + *
> + * Specifies platform specific configuration of sdhci controller.
> + * Note: A structure for driver specific platform data is used for future
> + * expansion of its usage.
> + */
> +struct sdhci_s3c_drv_data {
> +       unsigned int    sdhci_quirks;
> +};
> +
>  static inline struct sdhci_s3c *to_s3c(struct sdhci_host *host)
>  {
>        return sdhci_priv(host);
> @@ -132,10 +144,10 @@ static unsigned int sdhci_s3c_consider_clock(struct sdhci_s3c *ourhost,
>                return UINT_MAX;
>
>        /*
> -        * Clock divider's step is different as 1 from that of host controller
> -        * when 'clk_type' is S3C_SDHCI_CLK_DIV_EXTERNAL.
> +        * If controller uses a non-standard clock division, find the best clock
> +        * speed possible with selected clock source and skip the division.
>         */
> -       if (ourhost->pdata->clk_type) {
> +       if (ourhost->host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) {
>                rate = clk_round_rate(clksrc, wanted);
>                return wanted - rate;
>        }
> @@ -272,6 +284,8 @@ static unsigned int sdhci_cmu_get_min_clock(struct sdhci_host *host)
>  static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock)
>  {
>        struct sdhci_s3c *ourhost = to_s3c(host);
> +       unsigned long timeout;
> +       u16 clk = 0;
>
>        /* don't bother if the clock is going off */
>        if (clock == 0)
> @@ -282,6 +296,25 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock)
>        clk_set_rate(ourhost->clk_bus[ourhost->cur_clk], clock);
>
>        host->clock = clock;
> +
> +       clk = SDHCI_CLOCK_INT_EN;
> +       sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
> +
> +       /* Wait max 20 ms */
> +       timeout = 20;
> +       while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
> +               & SDHCI_CLOCK_INT_STABLE)) {
> +               if (timeout == 0) {
> +                       printk(KERN_ERR "%s: Internal clock never "
> +                               "stabilised.\n", mmc_hostname(host->mmc));
> +                       return;
> +               }
> +               timeout--;
> +               mdelay(1);
> +       }
> +
> +       clk |= SDHCI_CLOCK_CARD_EN;
> +       sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
>  }
>
>  /**
> @@ -382,9 +415,17 @@ static void sdhci_s3c_setup_card_detect_gpio(struct sdhci_s3c *sc)
>        }
>  }
>
> +static inline struct sdhci_s3c_drv_data *sdhci_s3c_get_driver_data(
> +                       struct platform_device *pdev)
> +{
> +       return (struct sdhci_s3c_drv_data *)
> +                       platform_get_device_id(pdev)->driver_data;
> +}
> +
>  static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
>  {
>        struct s3c_sdhci_platdata *pdata = pdev->dev.platform_data;
> +       struct sdhci_s3c_drv_data *drv_data;
>        struct device *dev = &pdev->dev;
>        struct sdhci_host *host;
>        struct sdhci_s3c *sc;
> @@ -414,6 +455,7 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
>                return PTR_ERR(host);
>        }
>
> +       drv_data = sdhci_s3c_get_driver_data(pdev);
>        sc = sdhci_priv(host);
>
>        sc->host = host;
> @@ -494,6 +536,8 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
>        /* Setup quirks for the controller */
>        host->quirks |= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC;
>        host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT;
> +       if (drv_data)
> +               host->quirks |= drv_data->sdhci_quirks;
>
>  #ifndef CONFIG_MMC_SDHCI_S3C_DMA
>
> @@ -534,7 +578,7 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
>         * If controller does not have internal clock divider,
>         * we can use overriding functions instead of default.
>         */
> -       if (pdata->clk_type) {
> +       if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) {
>                sdhci_s3c_ops.set_clock = sdhci_cmu_set_clock;
>                sdhci_s3c_ops.get_min_clock = sdhci_cmu_get_min_clock;
>                sdhci_s3c_ops.get_max_clock = sdhci_cmu_get_max_clock;
> @@ -639,9 +683,31 @@ static int sdhci_s3c_resume(struct platform_device *dev)
>  #define sdhci_s3c_resume NULL
>  #endif
>
> +#if defined(CONFIG_CPU_EXYNOS4210) || defined(CONFIG_SOC_EXYNOS4212)
> +static struct sdhci_s3c_drv_data exynos4_sdhci_drv_data = {
> +       .sdhci_quirks = SDHCI_QUIRK_NONSTANDARD_CLOCK,
> +};
> +#define EXYNOS4_SDHCI_DRV_DATA ((kernel_ulong_t)&exynos4_sdhci_drv_data)
> +#else
> +#define EXYNOS4_SDHCI_DRV_DATA (kernel_ulong_t)NULL
> +#endif
It's too small to save the memory. I think no need to guard the exynos
driver data with ifdef/endif.
> +
> +static struct platform_device_id sdhci_s3c_driver_ids[] = {
> +       {
> +               .name           = "s3c-sdhci",
> +               .driver_data    = (kernel_ulong_t)NULL,
> +       },
> +       {
> +               .name           = "exynos4-sdhci",
> +               .driver_data    = EXYNOS4_SDHCI_DRV_DATA,
> +       },
> +};
> +MODULE_DEVICE_TABLE(platform, sdhci_s3c_driver_ids);

Do you have a plan to use the sdhci-pltfm.c as other drivers did?
some drivers are already use it and support OF features.

Thank you,
Kyungmin Park
> +
>  static struct platform_driver sdhci_s3c_driver = {
>        .probe          = sdhci_s3c_probe,
>        .remove         = __devexit_p(sdhci_s3c_remove),
> +       .id_table       = sdhci_s3c_driver_ids,
>        .suspend        = sdhci_s3c_suspend,
>        .resume         = sdhci_s3c_resume,
>        .driver         = {
> --
> 1.6.6.rc2
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>



More information about the linux-arm-kernel mailing list