On May 17, 2011, at 10:49 PM, Nath, Arindam wrote:
> Hi Philip,
>
>
>> -----Original Message-----
>> From: Philip Rakity [mailto:[email protected]]
>> Sent: Wednesday, May 18, 2011 2:29 AM
>> To: [email protected]
>> Cc: Nath, Arindam
>> Subject: [RFC] mmc: Non Default UHS Drive Strength must use board
>> specific code
>>
>>
>> Note: This is being send out for comment. We are still in the process
>> of testing this change
>> but we would like to have community review at this time.
>>
>>
>> SD 3.0 introduced additional drive strengths for UHS.
>> The card and the host can indicate 4 drive strengths as a bit
>> mask. Without local design knowledge of the board it is not
>> possible to select the correct drive strength. Unfortunately
>> there is not the equivalent of ethernet auto-negotiate in the
>> SD 3.0 Physical Spec at this time.
>>
>> We punt the problem by asking any platform specific code to
>> handle the drive strength problem. If non is defined we default
>> the drive strength to the manadory TYPE_B value.
>>
>> Signed-off-by: Philip Rakity <[email protected]>
>> ---
>> drivers/mmc/core/core.c | 15 ++++++++++
>> drivers/mmc/core/core.h | 5 +++
>> drivers/mmc/core/sd.c | 65 ++++++++++++++++++++++++--------------
>> --------
>> drivers/mmc/host/sdhci.c | 16 +++++++++++
>> drivers/mmc/host/sdhci.h | 5 +++
>> include/linux/mmc/host.h | 13 ++++++---
>> 6 files changed, 84 insertions(+), 35 deletions(-)
>>
>> diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
>> index 68091dd..49df27b 100644
>> --- a/drivers/mmc/core/core.c
>> +++ b/drivers/mmc/core/core.c
>> @@ -964,6 +964,21 @@ int mmc_set_signal_voltage(struct mmc_host *host,
>> int signal_voltage, bool cmd11
>> return err;
>> }
>>
>> +unsigned int mmc_select_drive_strength(struct mmc_host *host,
>> + unsigned int bus_mode,
>> + unsigned int max_dtr,
>> + unsigned int host_drv_type,
>> + unsigned int card_drv_type)
>> +{
>> + if (host->ops->select_drive_strength)
>> + return host->ops->select_drive_strength(host,
>> + bus_mode, max_dtr,
>> + host_drv_type, card_drv_type);
>> +
>> + /* return default strength if no handler in driver */
>> + return MMC_SET_DRIVER_TYPE_B;
>> +}
>> +
>> /*
>> * Select timing parameters for host.
>> */
>> diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
>> index d9411ed..8bc289c 100644
>> --- a/drivers/mmc/core/core.h
>> +++ b/drivers/mmc/core/core.h
>> @@ -43,6 +43,11 @@ int mmc_set_signal_voltage(struct mmc_host *host,
>> int signal_voltage,
>> bool cmd11);
>> void mmc_set_timing(struct mmc_host *host, unsigned int timing);
>> void mmc_set_driver_type(struct mmc_host *host, unsigned int
>> drv_type);
>> +unsigned int mmc_select_drive_strength(struct mmc_host *host,
>> + unsigned int bus_mode,
>> + unsigned int max_dtr,
>> + unsigned int host_drv_type,
>> + unsigned int card_drv_type);
>>
>> static inline void mmc_delay(unsigned int ms)
>> {
>> diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
>> index 596d0b9..a268ead 100644
>> --- a/drivers/mmc/core/sd.c
>> +++ b/drivers/mmc/core/sd.c
>> @@ -405,54 +405,57 @@ out:
>> return err;
>> }
>>
>> +
>> static int sd_select_driver_type(struct mmc_card *card, u8 *status)
>> {
>> - int host_drv_type = 0, card_drv_type = 0;
>> + unsigned int host_drv_type = MMC_SET_DRIVER_TYPE_B;
>> + unsigned int card_drv_type = MMC_SET_DRIVER_TYPE_B;
>> int err;
>> + unsigned char drive_strength;
>>
>> /*
>> * If the host doesn't support any of the Driver Types A,C or D,
>> - * default Driver Type B is used.
>> + * or there is no board specific handler then default Driver
>> + * Type B is used.
>> */
>> if (!(card->host->caps & (MMC_CAP_DRIVER_TYPE_A |
>> MMC_CAP_DRIVER_TYPE_C
>> | MMC_CAP_DRIVER_TYPE_D)))
>> return 0;
>>
>> - if (card->host->caps & MMC_CAP_DRIVER_TYPE_A) {
>> - host_drv_type = MMC_SET_DRIVER_TYPE_A;
>> - if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_A)
>> - card_drv_type = MMC_SET_DRIVER_TYPE_A;
>> - else if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_B)
>> - card_drv_type = MMC_SET_DRIVER_TYPE_B;
>> - else if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_C)
>> - card_drv_type = MMC_SET_DRIVER_TYPE_C;
>> - } else if (card->host->caps & MMC_CAP_DRIVER_TYPE_C) {
>> - host_drv_type = MMC_SET_DRIVER_TYPE_C;
>> - if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_C)
>> - card_drv_type = MMC_SET_DRIVER_TYPE_C;
>> - } else if (!(card->host->caps & MMC_CAP_DRIVER_TYPE_D)) {
>> - /*
>> - * If we are here, that means only the default driver type
>> - * B is supported by the host.
>> - */
>> - host_drv_type = MMC_SET_DRIVER_TYPE_B;
>> - if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_B)
>> - card_drv_type = MMC_SET_DRIVER_TYPE_B;
>> - else if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_C)
>> - card_drv_type = MMC_SET_DRIVER_TYPE_C;
>> - }
>> + if (card->host->caps & MMC_CAP_DRIVER_TYPE_A)
>> + host_drv_type |= MMC_SET_DRIVER_TYPE_A;
>> +
>> + if (card->host->caps & MMC_CAP_DRIVER_TYPE_C)
>> + host_drv_type |= MMC_SET_DRIVER_TYPE_C;
>> +
>> + if (card->host->caps & MMC_CAP_DRIVER_TYPE_D)
>> + host_drv_type |= MMC_SET_DRIVER_TYPE_D;
>>
>> - err = mmc_sd_switch(card, 1, 2, card_drv_type, status);
>> + if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_A)
>> + card_drv_type |= MMC_SET_DRIVER_TYPE_A;
>> +
>> + if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_C)
>> + card_drv_type |= MMC_SET_DRIVER_TYPE_C;
>> +
>> + if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_D)
>> + card_drv_type |= MMC_SET_DRIVER_TYPE_D;
>> +
>> + drive_strength = mmc_select_drive_strength(card->host,
>> + card->sw_caps.sd3_bus_mode,
>> + card->sw_caps.uhs_max_dtr,
>> + host_drv_type, card_drv_type);
>> +
>> + err = mmc_sd_switch(card, 1, 2, drive_strength, status);
>> if (err)
>> return err;
>>
>> - if ((status[15] & 0xF) != card_drv_type) {
>> - printk(KERN_WARNING "%s: Problem setting driver
>> strength!\n",
>> - mmc_hostname(card->host));
>> + if ((status[15] & 0xF) != drive_strength) {
>> + printk(KERN_WARNING "%s: Problem setting driver strength
>> %d\n",
>> + mmc_hostname(card->host), drive_strength);
>> return 0;
>> }
>>
>> - mmc_set_driver_type(card->host, host_drv_type);
>> + mmc_set_driver_type(card->host, drive_strength);
>>
>> return 0;
>> }
>> @@ -488,7 +491,7 @@ static int sd_set_bus_speed_mode(struct mmc_card
>> *card, u8 *status)
>> card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
>> } else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
>> MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25)) &&
>> - (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR25)) {
>> + (card->sw_caps.uhs_max_dtr & SD_MODE_UHS_SDR25)) {
>
> I think this line should not be changed, otherwise it will lose its purpose.
typo -- will fix
>
>> bus_speed = UHS_SDR25_BUS_SPEED;
>> timing = MMC_TIMING_UHS_SDR25;
>> card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
>> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
>> index cc63f5e..ebeb986 100644
>> --- a/drivers/mmc/host/sdhci.c
>> +++ b/drivers/mmc/host/sdhci.c
>> @@ -1766,6 +1766,21 @@ static void sdhci_enable_preset_value(struct
>> mmc_host *mmc, bool enable)
>> spin_unlock_irqrestore(&host->lock, flags);
>> }
>>
>> +static unsigned int sdhci_select_drive_strength(struct mmc_host *host,
>> + unsigned int bus_mode,
>> + unsigned int max_dtr,
>> + unsigned int host_drv_type,
>> + unsigned int card_drv_type)
>> +{
>> + if (host->ops->select_drive_strength)
>> + return host->ops->select_drive_strength(host,
>> + bus_mode, max_dtr,
>> + host_drv_type, card_drv_type);
>> +
>> + /* return default strength if no handler in driver */
>> + return MMC_SET_DRIVER_TYPE_B;
>> +}
>> +
>> static const struct mmc_host_ops sdhci_ops = {
>> .request = sdhci_request,
>> .set_ios = sdhci_set_ios,
>> @@ -1774,6 +1789,7 @@ static const struct mmc_host_ops sdhci_ops = {
>> .start_signal_voltage_switch =
>> sdhci_start_signal_voltage_switch,
>> .execute_tuning = sdhci_execute_tuning,
>> .enable_preset_value = sdhci_enable_preset_value,
>> + .select_drive_strength = sdhci_select_drive_strength,
>
> Do we need this here? Host Controllers who want to support this should
> provide their own sdhci_ops function in their respective mmc/host files. For
> other controllers, mmc_select_drive_strength() will always return TYPE_B as
> you mentioned above.
>
without this there is no hook into the platform specific code to handle the
setting of drive strength.
>> };
>>
>>
>> /**********************************************************************
>> *******\
>> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
>> index 7e28eec..8f48aca 100644
>> --- a/drivers/mmc/host/sdhci.h
>> +++ b/drivers/mmc/host/sdhci.h
>> @@ -271,6 +271,11 @@ struct sdhci_ops {
>> void (*platform_reset_enter)(struct sdhci_host *host, u8 mask);
>> void (*platform_reset_exit)(struct sdhci_host *host, u8 mask);
>> int (*set_uhs_signaling)(struct sdhci_host *host, unsigned int
>> uhs);
>> + unsigned int (*select_drive_strength)(struct sdhci_host
>> *host,
>> + unsigned int bus_mode,
>> + unsigned int max_dtr,
>> + unsigned int host_drv_type,
>> + unsigned int card_drv_type);
>>
>> };
>>
>> diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
>> index de32e6a..6c2aeab 100644
>> --- a/include/linux/mmc/host.h
>> +++ b/include/linux/mmc/host.h
>> @@ -70,10 +70,10 @@ struct mmc_ios {
>>
>> unsigned char drv_type; /* driver type (A, B, C, D)
>> */
>>
>> -#define MMC_SET_DRIVER_TYPE_B 0
>> -#define MMC_SET_DRIVER_TYPE_A 1
>> -#define MMC_SET_DRIVER_TYPE_C 2
>> -#define MMC_SET_DRIVER_TYPE_D 3
>> +#define MMC_SET_DRIVER_TYPE_B (1<<0)
>> +#define MMC_SET_DRIVER_TYPE_A (1<<1)
>> +#define MMC_SET_DRIVER_TYPE_C (1<<2)
>> +#define MMC_SET_DRIVER_TYPE_D (1<<3)
>
> These defines if changed will have a different meaning altogether. The
> defines actually correspond to the "Function Name" column of Table 4-11 of
> the Physical Layer spec v3.01, which is used by mmc_sd_switch() to set the
> driver type for the card.
will add the comment about Function Name and rework code.
>
> Thanks,
> Arindam
>
>> };
>>
>> struct mmc_host_ops {
>> @@ -139,6 +139,11 @@ struct mmc_host_ops {
>> int (*start_signal_voltage_switch)(struct mmc_host *host,
>> struct mmc_ios *ios);
>> int (*execute_tuning)(struct mmc_host *host);
>> void (*enable_preset_value)(struct mmc_host *host, bool enable);
>> + unsigned int (*select_drive_strength)(struct mmc_host *host,
>> + unsigned int bus_mode,
>> + unsigned int max_dtr,
>> + unsigned int host_drv_type,
>> + unsigned int card_drv_type);
>> };
>>
>> struct mmc_card;
>> --
>> 1.7.0.4
>>
>>
>
>
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html