[PATCH 1/3] mmc: sdhci-s3c: Remove usage of clk_type member in platform data
Thomas Abraham
thomas.abraham at linaro.org
Tue Oct 4 04:15:06 EDT 2011
Hi Mr. Park,
On 4 October 2011 13:23, Kyungmin Park <kmpark at infradead.org> wrote:
> 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.
There could be other elements added to the 'sdhci_s3c_drv_data'
structure as new variants of sdhci-s3c controller would appear in
future SoC. I am not aware of any such variations in the upcoming
SoC's but this was done taking into consideration that there could be
slight variations which the driver would have to support.
>> +
>> +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.
I did check few months back if sdhci-pltfm.c can be used for sdhci-s3c
drivers but due to slightly complex operations which sdhci-s3c
handles, I did not try to base samsung sdhci driver support on
sdhci-pltfm.c. So, at present, I do not have a plan to move to
sdhci-pltfm.c.
>
> Thank you,
> Kyungmin Park
Thanks for your review and comments.
Regards,
Thomas.
>> +
>> 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