avail_type |= EXT_CSD_CARD_TYPE_HS400_1_2V;
}
- if ((caps2 & MMC_CAP2_HS400_ENHANCED_STROBE) &&
+ if ((caps2 & MMC_CAP2_HS400_ES) &&
card->ext_csd.strobe_support &&
- ((card_type & EXT_CSD_CARD_TYPE_HS400_1_2V) ||
- (card_type & EXT_CSD_CARD_TYPE_HS400_1_8V)))
+ (avail_type & EXT_CSD_CARD_TYPE_HS400))
avail_type |= EXT_CSD_CARD_TYPE_HS400ES;
card->ext_csd.hs_max_dtr = hs_max_dtr;
u8 val;
/*
- * HS400(ES) mode requires 8-bit bus width
+ * HS400 mode requires 8-bit bus width
*/
- if (!(((card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400) ||
- (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400ES)) &&
- host->ios.bus_width == MMC_BUS_WIDTH_8))
+ if (!(card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400 &&
+ host->ios.bus_width == MMC_BUS_WIDTH_8))
return 0;
if (host->caps & MMC_CAP_WAIT_WHILE_BUSY)
}
/* Switch card to DDR */
- val = EXT_CSD_DDR_BUS_WIDTH_8;
- if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400ES) {
- val |= EXT_CSD_BUS_WIDTH_STROBE;
- /*
- * Make sure we are in non-enhanced strobe mode before we
- * actually enable it in ext_csd.
- */
- if (host->ops->prepare_enhanced_strobe)
- err = host->ops->prepare_enhanced_strobe(host, false);
-
- if (err) {
- pr_err("%s: unprepare_enhanced strobe failed, err:%d\n",
- mmc_hostname(host), err);
- return err;
- }
- }
-
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BUS_WIDTH,
- val,
+ EXT_CSD_DDR_BUS_WIDTH_8,
card->ext_csd.generic_cmd6_time);
if (err) {
pr_err("%s: switch to bus width for hs400 failed, err:%d\n",
mmc_set_timing(host, MMC_TIMING_MMC_HS400);
mmc_set_bus_speed(card);
- if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400ES) {
- /* Controller enable enhanced strobe function */
- if (host->ops->prepare_enhanced_strobe)
- err = host->ops->prepare_enhanced_strobe(host, true);
-
- if (err) {
- pr_err("%s: prepare enhanced strobe failed, err:%d\n",
- mmc_hostname(host), err);
- return err;
- }
-
- /*
- * After switching to hs400 enhanced strobe mode, we expect to
- * verify whether it works or not. If controller can't handle
- * bus width test, compare ext_csd previously read in 1 bit mode
- * against ext_csd at new bus width
- */
- if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST))
- err = mmc_compare_ext_csds(card, MMC_BUS_WIDTH_8);
- else
- err = mmc_bus_test(card, MMC_BUS_WIDTH_8);
-
- if (err) {
- pr_warn("%s: switch to enhanced strobe failed\n",
- mmc_hostname(host));
- return err;
- }
- }
-
if (!send_status) {
err = mmc_switch_status(card);
if (err)
return err;
}
+static int mmc_select_hs400es(struct mmc_card *card)
+{
+ struct mmc_host *host = card->host;
+ int err = 0;
+ u8 val;
+
+ if (!(host->caps & MMC_CAP_8_BIT_DATA)) {
+ err = -ENOTSUPP;
+ goto out_err;
+ }
+
+ err = mmc_select_bus_width(card);
+ if (IS_ERR_VALUE(err))
+ goto out_err;
+
+ /* Switch card to HS mode */
+ err = mmc_select_hs(card);
+ if (err) {
+ pr_err("%s: switch to high-speed failed, err:%d\n",
+ mmc_hostname(host), err);
+ goto out_err;
+ }
+
+ err = mmc_switch_status(card);
+ if (err)
+ goto out_err;
+
+ /* Switch card to DDR with strobe bit */
+ val = EXT_CSD_DDR_BUS_WIDTH_8 | EXT_CSD_BUS_WIDTH_STROBE;
+
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_BUS_WIDTH,
+ val,
+ card->ext_csd.generic_cmd6_time);
+ if (err) {
+ pr_err("%s: switch to bus width for hs400es failed, err:%d\n",
+ mmc_hostname(host), err);
+ goto out_err;
+ }
+
+ /* Switch card to HS400 */
+ val = EXT_CSD_TIMING_HS400 |
+ card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
+ err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_HS_TIMING, val,
+ card->ext_csd.generic_cmd6_time,
+ true, false, true);
+ if (err) {
+ pr_err("%s: switch to hs400es failed, err:%d\n",
+ mmc_hostname(host), err);
+ goto out_err;
+ }
+
+ /* Set host controller to HS400 timing and frequency */
+ mmc_set_timing(host, MMC_TIMING_MMC_HS400);
+
+ /* Controller enable enhanced strobe function */
+ host->ios.enhanced_strobe = true;
+ if (host->ops->hs400_enhanced_strobe)
+ host->ops->hs400_enhanced_strobe(host, &host->ios);
+
+ err = mmc_switch_status(card);
+ if (err)
+ goto out_err;
+
+ return 0;
+
+out_err:
+ pr_err("%s: %s failed, error %d\n", mmc_hostname(card->host),
+ __func__, err);
+ return err;
+}
+
/*
- * Activate High Speed or HS200 mode if supported. For HS400
- * with enhanced strobe mode, we should activate High Speed.
+ * Activate High Speed or HS200 or HS400ES mode if supported.
*/
static int mmc_select_timing(struct mmc_card *card)
{
goto bus_speed;
if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400ES)
- err = mmc_select_hs(card);
+ err = mmc_select_hs400es(card);
else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200)
err = mmc_select_hs200(card);
else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS)
} else if (mmc_card_hs(card)) {
/* Select the desired bus width optionally */
err = mmc_select_bus_width(card);
- if (IS_ERR_VALUE(err))
- goto free_card;
-
- if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400ES) {
- /* Directly from HS to HS400-ES */
- err = mmc_select_hs400(card);
- if (err)
- goto free_card;
- } else {
+ if (!IS_ERR_VALUE(err)) {
err = mmc_select_hs_ddr(card);
if (err)
goto free_card;
#define MMC_SET_DRIVER_TYPE_A 1
#define MMC_SET_DRIVER_TYPE_C 2
#define MMC_SET_DRIVER_TYPE_D 3
+
+ bool enhanced_strobe; /* hs400 enhanced strobe selection */
};
struct mmc_host_ops {
/* Prepare HS400 target operating frequency depending host driver */
int (*prepare_hs400_tuning)(struct mmc_host *host, struct mmc_ios *ios);
/* Prepare enhanced strobe depending host driver */
- int (*prepare_enhanced_strobe)(struct mmc_host *host, bool enable);
+ void (*hs400_enhanced_strobe)(struct mmc_host *host, struct mmc_ios *ios);
int (*select_drive_strength)(struct mmc_card *card,
unsigned int max_dtr, int host_drv,
int card_drv, int *drv_type);
#define MMC_CAP2_HSX00_1_2V (MMC_CAP2_HS200_1_2V_SDR | MMC_CAP2_HS400_1_2V)
#define MMC_CAP2_SDIO_IRQ_NOTHREAD (1 << 17)
#define MMC_CAP2_NO_WRITE_PROTECT (1 << 18) /* No physical write protect pin, assume that card is always read-write */
-#define MMC_CAP2_HS400_ENHANCED_STROBE (1 << 20) /* Host supports enhanced strobe */
+#define MMC_CAP2_HS400_ES (1 << 20) /* Host supports enhanced strobe */
mmc_pm_flag_t pm_caps; /* supported pm features */
static inline int mmc_host_hs400_enhanced_strobe(struct mmc_host *host)
{
- return host->caps2 & MMC_CAP2_HS400_ENHANCED_STROBE;
+ return host->caps2 & MMC_CAP2_HS400_ES;
}
static inline int mmc_host_packed_wr(struct mmc_host *host)
static inline bool mmc_card_hs400es(struct mmc_card *card)
{
- if (mmc_card_hs400(card) &&
- (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400ES))
- return 1;
-
- return 0;
+ return card->host->ios.enhanced_strobe;
}
void mmc_retune_timer_stop(struct mmc_host *host);