Author: kibab
Date: Tue Jun  5 11:03:24 2018
New Revision: 334651
URL: https://svnweb.freebsd.org/changeset/base/334651

Log:
  Enable high-speed on the card before increasing frequency on the controller
  
  Increasing operating frequency without telling card to switch
  to high-speed mode first upsets some cards and generates CRC errors.
  
  While here, deselect / reselect cards after CMD6 and SCR fetch, as in 
original code.
  
  Approved by:  imp (mentor)
  Differential Revision:        https://reviews.freebsd.org/D15568

Modified:
  head/sys/cam/mmc/mmc_da.c

Modified: head/sys/cam/mmc/mmc_da.c
==============================================================================
--- head/sys/cam/mmc/mmc_da.c   Tue Jun  5 09:52:38 2018        (r334650)
+++ head/sys/cam/mmc/mmc_da.c   Tue Jun  5 11:03:24 2018        (r334651)
@@ -171,7 +171,7 @@ static void sdda_start_init_task(void *context, int pe
 static void sdda_process_mmc_partitions(struct cam_periph *periph, union ccb 
*start_ccb);
 static uint32_t sdda_get_host_caps(struct cam_periph *periph, union ccb *ccb);
 static void sdda_init_switch_part(struct cam_periph *periph, union ccb 
*start_ccb, u_int part);
-
+static int mmc_select_card(struct cam_periph *periph, union ccb *ccb, uint32_t 
rca);
 static inline uint32_t mmc_get_sector_size(struct cam_periph *periph) {return 
MMC_SECTOR_SIZE;}
 
 /* TODO: actually issue GET_TRAN_SETTINGS to get R/O status */
@@ -901,6 +901,38 @@ mmc_switch_fill_mmcio(union ccb *ccb,
 }
 
 static int
+mmc_select_card(struct cam_periph *periph, union ccb *ccb, uint32_t rca)
+{
+       int flags;
+
+       flags = (rca ? MMC_RSP_R1B : MMC_RSP_NONE) | MMC_CMD_AC;
+       cam_fill_mmcio(&ccb->mmcio,
+                      /*retries*/ 0,
+                      /*cbfcnp*/ NULL,
+                      /*flags*/ CAM_DIR_IN,
+                      /*mmc_opcode*/ MMC_SELECT_CARD,
+                      /*mmc_arg*/ rca << 16,
+                      /*mmc_flags*/ flags,
+                      /*mmc_data*/ NULL,
+                      /*timeout*/ 0);
+
+       cam_periph_runccb(ccb, sddaerror, CAM_FLAG_NONE, /*sense_flags*/0, 
NULL);
+
+       if (((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)) {
+               if (ccb->mmcio.cmd.error != 0) {
+                       CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_PERIPH,
+                                 ("%s: MMC_SELECT command failed", __func__));
+                       return EIO;
+               }
+               return 0; /* Normal return */
+       } else {
+               CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_PERIPH,
+                         ("%s: CAM request failed\n", __func__));
+               return EIO;
+       }
+}
+
+static int
 mmc_switch(struct cam_periph *periph, union ccb *ccb,
     uint8_t set, uint8_t index, uint8_t value, u_int timeout)
 {
@@ -953,18 +985,24 @@ mmc_sd_switch(struct cam_periph *periph, union ccb *cc
              uint8_t *res) {
 
        struct mmc_data mmc_d;
+       uint32_t arg;
 
        memset(res, 0, 64);
        mmc_d.len = 64;
        mmc_d.data = res;
        mmc_d.flags = MMC_DATA_READ;
 
+       arg = mode << 31;                       /* 0 - check, 1 - set */
+       arg |= 0x00FFFFFF;
+       arg &= ~(0xF << (grp * 4));
+       arg |= value << (grp * 4);
+
        cam_fill_mmcio(&ccb->mmcio,
                       /*retries*/ 0,
                       /*cbfcnp*/ NULL,
                       /*flags*/ CAM_DIR_IN,
                       /*mmc_opcode*/ SD_SWITCH_FUNC,
-                      /*mmc_arg*/ mode << 31,
+                      /*mmc_arg*/ arg,
                       /*mmc_flags*/ MMC_RSP_R1 | MMC_CMD_ADTC,
                       /*mmc_data*/ &mmc_d,
                       /*timeout*/ 0);
@@ -1273,6 +1311,19 @@ sdda_start_init(void *context, union ccb *start_ccb)
                                        CAM_DEBUG(periph->path, 
CAM_DEBUG_PERIPH, ("Card supports HS\n"));
                                        softc->card_f_max = SD_HS_MAX;
                                }
+
+                               /*
+                                * We deselect then reselect the card here.  
Some cards
+                                * become unselected and timeout with the above 
two
+                                * commands, although the state tables / 
diagrams in the
+                                * standard suggest they go back to the 
transfer state.
+                                * Other cards don't become deselected, and if 
we
+                                * attempt to blindly re-select them, we get 
timeout
+                                * errors from some controllers.  So we 
deselect then
+                                * reselect to handle all situations.
+                                */
+                               mmc_select_card(periph, start_ccb, 0);
+                               mmc_select_card(periph, start_ccb, 
get_rca(periph));
                        } else {
                                CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, ("Not 
trying the switch\n"));
                                goto finish_hs_tests;
@@ -1293,6 +1344,15 @@ finish_hs_tests:
        f_max = min(host_f_max, softc->card_f_max);
        CAM_DEBUG(periph->path, CAM_DEBUG_PERIPH, ("Set SD freq to %d MHz (min 
out of host f=%d MHz and card f=%d MHz)\n", f_max  / 1000000, host_f_max / 
1000000, softc->card_f_max / 1000000));
 
+       /* Enable high-speed timing on the card */
+       if (f_max > 25000000) {
+               err = mmc_set_timing(periph, start_ccb, bus_timing_hs);
+               if (err != MMC_ERR_NONE) {
+                       CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("Cannot 
switch card to high-speed mode"));
+                       f_max = 25000000;
+               }
+       }
+       /* Set frequency on the controller */
        start_ccb->ccb_h.func_code = XPT_SET_TRAN_SETTINGS;
        start_ccb->ccb_h.flags = CAM_DIR_NONE;
        start_ccb->ccb_h.retry_count = 0;
@@ -1326,12 +1386,6 @@ finish_hs_tests:
                   bus_width_str(max_host_bus_width),
                   bus_width_str(max_card_bus_width)));
        sdda_set_bus_width(periph, start_ccb, desired_bus_width);
-
-       if (f_max > 25000000) {
-               err = mmc_set_timing(periph, start_ccb, bus_timing_hs);
-               if (err != MMC_ERR_NONE)
-                       CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("Cannot 
switch card to high-speed mode"));
-       }
 
        softc->state = SDDA_STATE_NORMAL;
 
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to