mmc: sdio: support SDIO UHS cards
Pavan Kunapuli [Wed, 4 Jan 2012 11:56:44 +0000 (16:56 +0530)]
This patch adds support for sdio UHS cards per the version 3.0
spec.

UHS mode is only enabled for version 3.0 cards when both the
host and the controller support UHS modes.

1.8v signaling support is removed if both the card and the
host do not support UHS.  This is done to maintain
compatibility and some system/card combinations break when
1.8v signaling is enabled when the host does not support UHS.

Reviewed-on: http://git-master/r/72876

Change-Id: I8d7dbaf1d1cbff8e9f13526d39e69b2a00eca2fa
Signed-off-by: Philip Rakity <prakity@marvell.com>
Signed-off-by: Aaron Lu <Aaron.lu@amd.com>
Reviewed-by: Arindam Nath <arindam.nath@amd.com>
Tested-by: Bing Zhao <bzhao@marvell.com>
Signed-off-by: Chris Ball <cjb@laptop.org>
Signed-off-by: Pavan Kunapuli <pkunapuli@nvidia.com>
Reviewed-by: Simone Willett <swillett@nvidia.com>
Tested-by: Simone Willett <swillett@nvidia.com>
Signed-off-by: Varun Wadekar <vwadekar@nvidia.com>
Reviewed-on: http://git-master/r/74580
Reviewed-by: Automatic_Commit_Validation_User

drivers/mmc/core/sdio.c
include/linux/mmc/card.h
include/linux/mmc/sdio.h

index a2c1c4d..fce904b 100644 (file)
@@ -106,6 +106,7 @@ static int sdio_read_cccr(struct mmc_card *card)
        int ret;
        int cccr_vsn;
        unsigned char data;
+       unsigned char speed;
 
        memset(&card->cccr, 0, sizeof(struct sdio_cccr));
 
@@ -144,12 +145,60 @@ static int sdio_read_cccr(struct mmc_card *card)
        }
 
        if (cccr_vsn >= SDIO_CCCR_REV_1_20) {
-               ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_SPEED, 0, &data);
+               ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_SPEED, 0, &speed);
                if (ret)
                        goto out;
 
-               if (data & SDIO_SPEED_SHS)
-                       card->cccr.high_speed = 1;
+               card->scr.sda_spec3 = 0;
+               card->sw_caps.sd3_bus_mode = 0;
+               card->sw_caps.sd3_drv_type = 0;
+               if (cccr_vsn >= SDIO_CCCR_REV_3_00) {
+                       card->scr.sda_spec3 = 1;
+                       ret = mmc_io_rw_direct(card, 0, 0,
+                               SDIO_CCCR_UHS, 0, &data);
+                       if (ret)
+                               goto out;
+
+                       if (card->host->caps &
+                               (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
+                                MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
+                                MMC_CAP_UHS_DDR50)) {
+                               if (data & SDIO_UHS_DDR50)
+                                       card->sw_caps.sd3_bus_mode
+                                               |= SD_MODE_UHS_DDR50;
+
+                               if (data & SDIO_UHS_SDR50)
+                                       card->sw_caps.sd3_bus_mode
+                                               |= SD_MODE_UHS_SDR50;
+
+                               if (data & SDIO_UHS_SDR104)
+                                       card->sw_caps.sd3_bus_mode
+                                               |= SD_MODE_UHS_SDR104;
+                       }
+
+                       ret = mmc_io_rw_direct(card, 0, 0,
+                               SDIO_CCCR_DRIVE_STRENGTH, 0, &data);
+                       if (ret)
+                               goto out;
+
+                       if (data & SDIO_DRIVE_SDTA)
+                               card->sw_caps.sd3_drv_type |= SD_DRIVER_TYPE_A;
+                       if (data & SDIO_DRIVE_SDTC)
+                               card->sw_caps.sd3_drv_type |= SD_DRIVER_TYPE_C;
+                       if (data & SDIO_DRIVE_SDTD)
+                               card->sw_caps.sd3_drv_type |= SD_DRIVER_TYPE_D;
+               }
+
+               /* if no uhs mode ensure we check for high speed */
+               if (!card->sw_caps.sd3_bus_mode) {
+                       if (speed & SDIO_SPEED_SHS) {
+                               card->cccr.high_speed = 1;
+                               card->sw_caps.hs_max_dtr = 50000000;
+                       } else {
+                               card->cccr.high_speed = 0;
+                               card->sw_caps.hs_max_dtr = 25000000;
+                       }
+               }
        }
 
 out:
@@ -331,6 +380,193 @@ static unsigned mmc_sdio_get_max_clock(struct mmc_card *card)
        return max_dtr;
 }
 
+static unsigned char host_drive_to_sdio_drive(int host_strength)
+{
+       switch (host_strength) {
+       case MMC_SET_DRIVER_TYPE_A:
+               return SDIO_DTSx_SET_TYPE_A;
+       case MMC_SET_DRIVER_TYPE_B:
+               return SDIO_DTSx_SET_TYPE_B;
+       case MMC_SET_DRIVER_TYPE_C:
+               return SDIO_DTSx_SET_TYPE_C;
+       case MMC_SET_DRIVER_TYPE_D:
+               return SDIO_DTSx_SET_TYPE_D;
+       default:
+               return SDIO_DTSx_SET_TYPE_B;
+       }
+}
+
+static void sdio_select_driver_type(struct mmc_card *card)
+{
+       int host_drv_type = SD_DRIVER_TYPE_B;
+       int card_drv_type = SD_DRIVER_TYPE_B;
+       int drive_strength;
+       unsigned char card_strength;
+       int err;
+
+       /*
+        * If the host doesn't support any of the Driver Types A,C or D,
+        * 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;
+
+       if (!card->host->ops->select_drive_strength)
+               return;
+
+       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);
+
+       /* if error just use default for drive strength B */
+       err = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_DRIVE_STRENGTH, 0,
+               &card_strength);
+       if (err)
+               return;
+
+       card_strength &= ~(SDIO_DRIVE_DTSx_MASK<<SDIO_DRIVE_DTSx_SHIFT);
+       card_strength |= host_drive_to_sdio_drive(drive_strength);
+
+       err = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_DRIVE_STRENGTH,
+               card_strength, NULL);
+
+       /* if error default to drive strength B */
+       if (!err)
+               mmc_set_driver_type(card->host, drive_strength);
+}
+
+
+static int sdio_set_bus_speed_mode(struct mmc_card *card)
+{
+       unsigned int bus_speed, timing;
+       int err;
+       unsigned char speed;
+
+       /*
+        * If the host doesn't support any of the UHS-I modes, fallback on
+        * default speed.
+        */
+       if (!(card->host->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
+           MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50)))
+               return 0;
+
+       bus_speed = SDIO_SPEED_SDR12;
+       timing = MMC_TIMING_UHS_SDR12;
+       if ((card->host->caps & MMC_CAP_UHS_SDR104) &&
+           (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR104)) {
+                       bus_speed = SDIO_SPEED_SDR104;
+                       timing = MMC_TIMING_UHS_SDR104;
+                       card->sw_caps.uhs_max_dtr = UHS_SDR104_MAX_DTR;
+       } else if ((card->host->caps & MMC_CAP_UHS_DDR50) &&
+                  (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_DDR50)) {
+                       bus_speed = SDIO_SPEED_DDR50;
+                       timing = MMC_TIMING_UHS_DDR50;
+                       card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
+       } else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
+                   MMC_CAP_UHS_SDR50)) && (card->sw_caps.sd3_bus_mode &
+                   SD_MODE_UHS_SDR50)) {
+                       bus_speed = SDIO_SPEED_SDR50;
+                       timing = MMC_TIMING_UHS_SDR50;
+                       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)) {
+                       bus_speed = SDIO_SPEED_SDR25;
+                       timing = MMC_TIMING_UHS_SDR25;
+                       card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
+       } else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
+                   MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25 |
+                   MMC_CAP_UHS_SDR12)) && (card->sw_caps.sd3_bus_mode &
+                   SD_MODE_UHS_SDR12)) {
+                       bus_speed = SDIO_SPEED_SDR12;
+                       timing = MMC_TIMING_UHS_SDR12;
+                       card->sw_caps.uhs_max_dtr = UHS_SDR12_MAX_DTR;
+       }
+
+       err = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_SPEED, 0, &speed);
+       if (err)
+               return err;
+
+       speed &= ~SDIO_SPEED_BSS_MASK;
+       speed |= bus_speed;
+       err = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_SPEED, speed, NULL);
+       if (err)
+               return err;
+
+       if (bus_speed) {
+               mmc_set_timing(card->host, timing);
+               mmc_set_clock(card->host, card->sw_caps.uhs_max_dtr);
+       }
+
+       return 0;
+}
+
+/*
+ * UHS-I specific initialization procedure
+ */
+static int mmc_sdio_init_uhs_card(struct mmc_card *card)
+{
+       int err;
+
+       if (!card->scr.sda_spec3)
+               return 0;
+
+       /*
+        * Switch to wider bus (if supported).
+        */
+       if (card->host->caps & MMC_CAP_4_BIT_DATA) {
+               err = sdio_enable_4bit_bus(card);
+               if (err > 0) {
+                       mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
+                       err = 0;
+               }
+       }
+
+       /* Set the driver strength for the card */
+       sdio_select_driver_type(card);
+
+       /* Set bus speed mode of the card */
+       err = sdio_set_bus_speed_mode(card);
+       if (err)
+               goto out;
+
+       /* Initialize and start re-tuning timer */
+       if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning)
+               err = card->host->ops->execute_tuning(card->host);
+
+out:
+
+       return err;
+}
+
 /*
  * Handle the detection and initialisation of a card.
  *
@@ -398,6 +634,29 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
                host->ops->init_card(host, card);
 
        /*
+        * If the host and card support UHS-I mode request the card
+        * to switch to 1.8V signaling level.  No 1.8v signalling if
+        * UHS mode is not enabled to maintain compatibilty and some
+        * systems that claim 1.8v signalling in fact do not support
+        * it.
+        */
+       if ((ocr & R4_18V_PRESENT) &&
+               (host->caps &
+                       (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
+                        MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
+                        MMC_CAP_UHS_DDR50))) {
+               err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
+               if (err) {
+                       ocr &= ~R4_18V_PRESENT;
+                       host->ocr &= ~R4_18V_PRESENT;
+               }
+               err = 0;
+       } else {
+               ocr &= ~R4_18V_PRESENT;
+               host->ocr &= ~R4_18V_PRESENT;
+       }
+
+       /*
         * For native busses:  set card RCA and quit open drain mode.
         */
        if (!powered_resume && !mmc_host_is_spi(host)) {
@@ -514,29 +773,39 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
        if (err)
                goto remove;
 
-       /*
-        * Switch to high-speed (if supported).
-        */
-       err = sdio_enable_hs(card);
-       if (err > 0)
-               mmc_sd_go_highspeed(card);
-       else if (err)
-               goto remove;
+       /* Initialization sequence for UHS-I cards */
+       /* Only if card supports 1.8v and UHS signaling */
+       if ((ocr & R4_18V_PRESENT) && card->sw_caps.sd3_bus_mode) {
+               err = mmc_sdio_init_uhs_card(card);
+               if (err)
+                       goto remove;
 
-       /*
-        * Change to the card's maximum speed.
-        */
-       mmc_set_clock(host, mmc_sdio_get_max_clock(card));
+               /* Card is an ultra-high-speed card */
+               mmc_card_set_uhs(card);
+       } else {
+               /*
+                * Switch to high-speed (if supported).
+                */
+               err = sdio_enable_hs(card);
+               if (err > 0)
+                       mmc_sd_go_highspeed(card);
+               else if (err)
+                       goto remove;
 
-       /*
-        * Switch to wider bus (if supported).
-        */
-       err = sdio_enable_4bit_bus(card);
-       if (err > 0)
-               mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
-       else if (err)
-               goto remove;
+               /*
+                * Change to the card's maximum speed.
+                */
+               mmc_set_clock(host, mmc_sdio_get_max_clock(card));
 
+               /*
+                * Switch to wider bus (if supported).
+                */
+               err = sdio_enable_4bit_bus(card);
+               if (err > 0)
+                       mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
+               else if (err)
+                       goto remove;
+       }
 finish:
        if (!oldcard)
                host->card = card;
@@ -819,8 +1088,17 @@ int mmc_attach_sdio(struct mmc_host *host)
         * Detect and init the card.
         */
        err = mmc_sdio_init_card(host, host->ocr, NULL, 0);
-       if (err)
-               goto err;
+       if (err) {
+               if (err == -EAGAIN) {
+                       /*
+                        * Retry initialization with S18R set to 0.
+                        */
+                       host->ocr &= ~R4_18V_PRESENT;
+                       err = mmc_sdio_init_card(host, host->ocr, NULL, 0);
+               }
+               if (err)
+                       goto err;
+       }
        card = host->card;
 
        /*
index b460fc2..c9f5d65 100644 (file)
@@ -312,9 +312,10 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
 #define mmc_card_highspeed(c)  ((c)->state & MMC_STATE_HIGHSPEED)
 #define mmc_card_blockaddr(c)  ((c)->state & MMC_STATE_BLOCKADDR)
 #define mmc_card_ddr_mode(c)   ((c)->state & MMC_STATE_HIGHSPEED_DDR)
-#define mmc_sd_card_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED)
+#define mmc_sd_card_uhs(c)     ((c)->state & MMC_STATE_ULTRAHIGHSPEED)
 #define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC)
-
+#define mmc_card_doing_bkops(c) ((c)->state & MMC_STATE_DOING_BKOPS)
+#define mmc_card_need_bkops(c) ((c)->state & MMC_STATE_NEED_BKOPS)
 #define mmc_card_set_present(c)        ((c)->state |= MMC_STATE_PRESENT)
 #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
 #define mmc_card_set_highspeed(c) ((c)->state |= MMC_STATE_HIGHSPEED)
index 2a2e990..aced403 100644 (file)
@@ -38,7 +38,8 @@
  *      [8:0] Byte/block count
  */
 
-#define R4_MEMORY_PRESENT (1 << 27)
+#define R4_18V_PRESENT BIT(24)
+#define R4_MEMORY_PRESENT BIT(27)
 
 /*
   SDIO status in R5
 #define  SDIO_SDIO_REV_1_10    1       /* SDIO Spec Version 1.10 */
 #define  SDIO_SDIO_REV_1_20    2       /* SDIO Spec Version 1.20 */
 #define  SDIO_SDIO_REV_2_00    3       /* SDIO Spec Version 2.00 */
+#define  SDIO_SD_REV_3_00      3       /* SD Physical Spev Version 3.00 */
 
 #define SDIO_CCCR_SD           0x01
 
 #define  SDIO_SD_REV_1_01      0       /* SD Physical Spec Version 1.01 */
 #define  SDIO_SD_REV_1_10      1       /* SD Physical Spec Version 1.10 */
 #define  SDIO_SD_REV_2_00      2       /* SD Physical Spec Version 2.00 */
+#define  SDIO_SD_REV_3_00      3       /* SD Physical Spev Version 3.00 */
 
 #define SDIO_CCCR_IOEx         0x02
 #define SDIO_CCCR_IORx         0x03
 #define SDIO_CCCR_SPEED                0x13
 
 #define  SDIO_SPEED_SHS                0x01    /* Supports High-Speed mode */
-#define  SDIO_SPEED_EHS                0x02    /* Enable High-Speed mode */
-
+#define  SDIO_SPEED_BSS_SHIFT  1
+#define  SDIO_SPEED_BSS_MASK   (7 << SDIO_SPEED_BSS_SHIFT)
+#define  SDIO_SPEED_SDR12      (0 << SDIO_SPEED_BSS_SHIFT)
+#define  SDIO_SPEED_SDR25      (1 << SDIO_SPEED_BSS_SHIFT)
+#define  SDIO_SPEED_SDR50      (2 << SDIO_SPEED_BSS_SHIFT)
+#define  SDIO_SPEED_SDR104     (3 << SDIO_SPEED_BSS_SHIFT)
+#define  SDIO_SPEED_DDR50      (4 << SDIO_SPEED_BSS_SHIFT)
+#define  SDIO_SPEED_EHS                SDIO_SPEED_SDR25        /* Enable High-Speed */
+
+#define SDIO_CCCR_UHS          0x14
+#define  SDIO_UHS_SDR50                0x01
+#define  SDIO_UHS_SDR104       0x02
+#define  SDIO_UHS_DDR50                0x04
+
+#define SDIO_CCCR_DRIVE_STRENGTH 0x15
+#define  SDIO_SDTx_MASK                0x07
+#define  SDIO_DRIVE_SDTA       (1 << 0)
+#define  SDIO_DRIVE_SDTC       (1 << 1)
+#define  SDIO_DRIVE_SDTD       (1 << 2)
+#define  SDIO_DRIVE_DTSx_MASK  0x03
+#define  SDIO_DRIVE_DTSx_SHIFT 4
+#define  SDIO_DTSx_SET_TYPE_B  (0 << SDIO_DRIVE_DTSx_SHIFT)
+#define  SDIO_DTSx_SET_TYPE_A  (1 << SDIO_DRIVE_DTSx_SHIFT)
+#define  SDIO_DTSx_SET_TYPE_C  (2 << SDIO_DRIVE_DTSx_SHIFT)
+#define  SDIO_DTSx_SET_TYPE_D  (3 << SDIO_DRIVE_DTSx_SHIFT)
 /*
  * Function Basic Registers (FBR)
  */