]> rtime.felk.cvut.cz Git - hercules2020/nv-tegra/linux-4.4.git/commitdiff
mmc: core: add debugfs node to switch speed mode
authorAnubhav <anubhavj@nvidia.com>
Thu, 24 Nov 2016 11:07:35 +0000 (16:37 +0530)
committermobile promotions <svcmobile_promotions@nvidia.com>
Wed, 30 Nov 2016 10:18:13 +0000 (02:18 -0800)
-Added the debugfs node "speed" and corresponding function
to support switch between various speed mode.

Bug 200251111

Change-Id: Ic5294aed9b50187dfc57dac6e940866fddcd3f77
Signed-off-by: Anubhav <anubhavj@nvidia.com>
Reviewed-on: http://git-master/r/1259531
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Pavan Kunapuli <pkunapuli@nvidia.com>
GVS: Gerrit_Virtual_Submit
Reviewed-by: Bitan Biswas <bbiswas@nvidia.com>
drivers/mmc/core/debugfs.c

index fb537c7d41c16da855b04a1ca97266d3b430a889..cf0c3f75af4a1132c7daf4fb5360b671ac467a3b 100644 (file)
@@ -233,8 +233,208 @@ static int mmc_clock_opt_set(void *data, u64 val)
        return 0;
 }
 
+static int mmc_speed_opt_get(void *data, u64 *val)
+{
+       struct mmc_host *host = data;
+       static const char *const uhs_speeds[] = {
+               [UHS_SDR12_BUS_SPEED] = "SDR12 ",
+               [UHS_SDR25_BUS_SPEED] = "SDR25 ",
+               [UHS_SDR50_BUS_SPEED] = "SDR50 ",
+               [UHS_SDR104_BUS_SPEED] = "SDR104 ",
+               [UHS_DDR50_BUS_SPEED] = "DDR50 ",
+       };
+       const char *str = "";
+       *val = 0;
+
+       if (!host || !host->card)
+               return 0;
+       if (mmc_sd_card_uhs(host->card) &&
+               (host->card->sd_bus_speed < ARRAY_SIZE(uhs_speeds))) {
+               str = uhs_speeds[host->card->sd_bus_speed];
+               *val = host->card->sd_bus_speed;
+       } else if ((host->ios.timing == MMC_TIMING_MMC_HS)
+                       || (host->ios.timing == MMC_TIMING_SD_HS)) {
+               str = "high speed";
+               *val = HIGH_SPEED_BUS_SPEED;
+       } else if (host->ios.timing == MMC_TIMING_MMC_HS200) {
+               str = "HS200";
+               *val = UHS_SDR104_BUS_SPEED;
+       } else if (host->ios.timing == MMC_TIMING_MMC_DDR52) {
+               str = "DDR50";
+               *val = UHS_DDR50_BUS_SPEED;
+       } else if (host->ios.timing == MMC_TIMING_MMC_HS400) {
+               str = "HS400";
+               *val = UHS_HS400_BUS_SPEED;
+       }
+
+       pr_info("%s: current speed %s\n",
+                       mmc_hostname(host->card->host),
+                       str);
+       return 0;
+}
+
+static int mmc_check_speed_mode_capability(struct mmc_card *card, u32 speed)
+{
+       u8 card_type = card->ext_csd.raw_card_type & EXT_CSD_CARD_TYPE_MASK;
+       u32 caps = card->host->caps, caps2 = card->host->caps2;
+
+       switch (speed) {
+       case UHS_DDR50_BUS_SPEED:
+       /* check card and host capability for DDR50 to proceed */
+               if (!(((caps & MMC_CAP_1_8V_DDR) &&
+                       (card_type & EXT_CSD_CARD_TYPE_DDR_1_8V)) ||
+                       ((caps & MMC_CAP_1_2V_DDR) &&
+                       (card_type & EXT_CSD_CARD_TYPE_DDR_1_2V)))) {
+                       pr_info("%s: DDR mode is not supported\n",
+                               mmc_hostname(card->host));
+                       return  -EINVAL;
+               }
+       break;
+       case UHS_SDR104_BUS_SPEED:
+       /* check card and host capability for HS200 to proceed */
+               if (!(((caps2 & MMC_CAP2_HS200_1_8V_SDR) &&
+                       (card_type & EXT_CSD_CARD_TYPE_HS200_1_8V)) ||
+                       ((caps2 & MMC_CAP2_HS200_1_2V_SDR) &&
+                       (card_type & EXT_CSD_CARD_TYPE_HS200_1_2V)))) {
+                       pr_info("%s: HS200 mode is not supported\n",
+                               mmc_hostname(card->host));
+                       return  -EINVAL;
+               }
+       break;
+       case UHS_HS400_BUS_SPEED:
+       /* check card and host capability for HS400 to proceed */
+               if (!(((caps2 & MMC_CAP2_HS400_1_8V) &&
+                       (card_type & EXT_CSD_CARD_TYPE_HS400_1_8V)) ||
+                       ((caps2 & MMC_CAP2_HS400_1_2V) &&
+                       (card_type & EXT_CSD_CARD_TYPE_HS400_1_2V)))) {
+                       pr_info("%s: HS400 mode is not supported\n",
+                               mmc_hostname(card->host));
+                       return -EINVAL;
+               }
+       break;
+       }
+       return 0;
+}
+
+static int mmc_print_speed_mode_error(struct mmc_host *host,
+       const char *mode, int err)
+{
+       if (err)
+               pr_err("%s: switch to %s failed with error %d\n",
+                       mmc_hostname(host), mode, err);
+       else
+               pr_err("%s: switch to %s mode successful\n",
+                       mmc_hostname(host), mode);
+       return err;
+}
+
+static int mmc_set_bus_speed_mode(struct mmc_host *host, u32 speed)
+{
+       int err = 0;
+       u64 current_mode;
+       const char *str = "";
+
+       err = mmc_check_speed_mode_capability(host->card, speed);
+       if (err)
+               return err;
+
+       mmc_speed_opt_get(host, &current_mode);
+       switch (current_mode) {
+       case UHS_HS400_BUS_SPEED:
+               switch (speed) {
+               case UHS_SDR104_BUS_SPEED: /*HS400 to HS200 mode switch */
+                       str = "HS200";
+                       err = mmc_hs400_to_hs200(host->card);
+                       return mmc_print_speed_mode_error(host, str, err);
+               case UHS_DDR50_BUS_SPEED:  /*Hs400 to DDR50 mode switch */
+                       str = "DDR";
+                       err = mmc_hs400_to_ddr(host->card);
+                       return mmc_print_speed_mode_error(host, str, err);
+               }
+               break;
+       case UHS_SDR104_BUS_SPEED:
+               switch (speed) {
+               case UHS_HS400_BUS_SPEED: /*HS200 to HS400 mode switch */
+                       str = "HS400";
+                       err = mmc_hs200_to_hs400(host->card);
+                       return mmc_print_speed_mode_error(host, str, err);
+               case UHS_DDR50_BUS_SPEED: /*HS200 to DDR50 mode switch */
+                       str = "DDR";
+                       err = mmc_hs200_to_ddr(host->card);
+                       return mmc_print_speed_mode_error(host, str, err);
+               }
+               break;
+       case UHS_DDR50_BUS_SPEED:
+               switch (speed) {
+               case UHS_HS400_BUS_SPEED: /*DDR50 to HS400 mode switch*/
+                       str = "HS400";
+                       err = mmc_ddr_to_hs400(host->card);
+                       return mmc_print_speed_mode_error(host, str, err);
+               case UHS_SDR104_BUS_SPEED: /*DDR50 to HS200 mode switch */
+                       str = "HS200";
+                       err = mmc_ddr_to_hs200(host->card);
+                       return mmc_print_speed_mode_error(host, str, err);
+               }
+               break;
+       }
+       return err;
+}
+
+static int mmc_speed_opt_set(void *data, u64 val)
+{
+       int err = 0;
+       struct mmc_host *host = data;
+       u32 prev_timing, prev_maxdtr;
+       u64 current_mode;
+
+       mmc_speed_opt_get(host, &current_mode);
+
+       if (host->card->type != MMC_TYPE_MMC) {
+               pr_warn("%s: usage error, only MMC device is supported\n",
+                       mmc_hostname(host));
+               return 0;
+       }
+
+       if ((val != UHS_DDR50_BUS_SPEED) && (val != UHS_HS400_BUS_SPEED)
+               && (val != UHS_SDR104_BUS_SPEED)) {
+               pr_info("%s: Usage info, Below are the list of possible switch\n",
+                               mmc_hostname(host));
+               pr_info("DDR50 to HS200 ( echo 3 > /d/mmc0/speed)\n");
+               pr_info("HS200 to DDR50 ( echo 4 > /d/mmc0/speed)\n");
+               pr_info("DDR50 to HS400 ( echo 5 > /d/mmc0/speed)\n");
+               pr_info("HS400 to DDR50 ( echo 4 > /d/mmc0/speed)\n");
+               pr_info("HS200 to HS400 ( echo 5 > /d/mmc0/speed)\n");
+               pr_info("HS400 to HS200 ( echo 3 > /d/mmc0/speed)\n");
+               return -EINVAL;
+       }
+
+       if (val == current_mode) {
+               pr_warn("%s: usage info: Current eMMC mode is same as Requested Mode\n",
+                       mmc_hostname(host));
+               return 0;
+       }
+
+       prev_timing = host->ios.timing;
+       prev_maxdtr = host->card->sw_caps.uhs_max_dtr;
+
+       /* Set bus speed mode of the card */
+       mmc_claim_host(host);
+       err = mmc_set_bus_speed_mode(host, val);
+       if (err) {
+               /* Restore to previous values in case of err*/
+               host->ios.timing = prev_timing;
+               host->card->sw_caps.uhs_max_dtr = prev_maxdtr;
+               pr_err("%s: could not set bus speed error = %d\n",
+                       mmc_hostname(host), err);
+       }
+       mmc_release_host(host);
+       return err;
+}
+
 DEFINE_SIMPLE_ATTRIBUTE(mmc_clock_fops, mmc_clock_opt_get, mmc_clock_opt_set,
        "%llu\n");
+DEFINE_SIMPLE_ATTRIBUTE(mmc_speed_fops, mmc_speed_opt_get, mmc_speed_opt_set,
+       "%llu\n");
 
 void mmc_add_host_debugfs(struct mmc_host *host)
 {
@@ -258,6 +458,10 @@ void mmc_add_host_debugfs(struct mmc_host *host)
                        &mmc_clock_fops))
                goto err_node;
 
+       if (!debugfs_create_file("speed", S_IRUSR | S_IWUSR, root, host,
+                       &mmc_speed_fops))
+               goto err_node;
+
 #ifdef CONFIG_FAIL_MMC_REQUEST
        if (fail_request)
                setup_fault_attr(&fail_default_attr, fail_request);