mmc: core: Set non-default Drive Strength via platform hook
authorPhilip Rakity <prakity@marvell.com>
Wed, 6 Jul 2011 15:51:32 +0000 (08:51 -0700)
committerChris Ball <cjb@laptop.org>
Wed, 20 Jul 2011 21:21:16 +0000 (17:21 -0400)
Non default Drive Strength cannot be set automatically.  It is a function
of the board design and only if there is a specific platform handler can
it be set.  The platform handler needs to take into account the board
design.  Pass to the platform code the necessary information.

For example:  The card and host controller may indicate they support HIGH
and LOW drive strength.  There is no way to know what should be chosen
without specific board knowledge.  Setting HIGH may lead to reflections
and setting LOW may not suffice.  There is no mechanism (like ethernet
duplex or speed pulses) to determine what should be done automatically.

If no platform handler is defined -- use the default value.

Signed-off-by: Philip Rakity <prakity@marvell.com>
Reviewed-by: Arindam Nath <arindam.nath@amd.com>
Signed-off-by: Chris Ball <cjb@laptop.org>
drivers/mmc/core/sd.c
include/linux/mmc/host.h

index ff2774128aa93b1231538b1f5e014b0df987e7ca..633975ff2bb395f6e2e883c2e7ce05fb51ed8df2 100644 (file)
@@ -409,52 +409,62 @@ out:
 
 static int sd_select_driver_type(struct mmc_card *card, u8 *status)
 {
-       int host_drv_type = 0, card_drv_type = 0;
+       int host_drv_type = SD_DRIVER_TYPE_B;
+       int card_drv_type = SD_DRIVER_TYPE_B;
+       int drive_strength;
        int err;
 
        /*
         * 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->ops->select_drive_strength)
+               return 0;
+
+       if (card->host->caps & MMC_CAP_DRIVER_TYPE_A)
+               host_drv_type |= SD_DRIVER_TYPE_A;
+
+       if (card->host->caps & MMC_CAP_DRIVER_TYPE_C)
+               host_drv_type |= SD_DRIVER_TYPE_C;
+
+       if (card->host->caps & MMC_CAP_DRIVER_TYPE_D)
+               host_drv_type |= SD_DRIVER_TYPE_D;
+
+       if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_A)
+               card_drv_type |= SD_DRIVER_TYPE_A;
+
+       if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_C)
+               card_drv_type |= SD_DRIVER_TYPE_C;
+
+       if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_D)
+               card_drv_type |= SD_DRIVER_TYPE_D;
+
+       /*
+        * The drive strength that the hardware can support
+        * depends on the board design.  Pass the appropriate
+        * information and let the hardware specific code
+        * return what is possible given the options
+        */
+       drive_strength = card->host->ops->select_drive_strength(
+               card->sw_caps.uhs_max_dtr,
+               host_drv_type, card_drv_type);
 
-       err = mmc_sd_switch(card, 1, 2, card_drv_type, status);
+       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",
+       if ((status[15] & 0xF) != drive_strength) {
+               printk(KERN_WARNING "%s: Problem setting drive strength!\n",
                        mmc_hostname(card->host));
                return 0;
        }
 
-       mmc_set_driver_type(card->host, host_drv_type);
+       mmc_set_driver_type(card->host, drive_strength);
 
        return 0;
 }
index 9f9a4c64cde7b42f719c45e7f118c7fbf688e5d2..0f83858147a626c1a0bf463597e4f6ff82adfa3c 100644 (file)
@@ -148,6 +148,7 @@ 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);
+       int     (*select_drive_strength)(unsigned int max_dtr, int host_drv, int card_drv);
 };
 
 struct mmc_card;