Merge "mmc: sdhci-msm: Enable enhanced strobe support for host"

This commit is contained in:
Linux Build Service Account 2015-12-15 22:01:55 -08:00 committed by Gerrit - the friendly Code Review server
commit e960032081
11 changed files with 237 additions and 5 deletions

View file

@ -398,11 +398,12 @@ int mmc_add_card(struct mmc_card *card)
mmc_card_ddr_mode(card) ? "DDR " : "",
type);
} else {
pr_info("%s: new %s%s%s%s%s%s card at address %04x\n",
pr_info("%s: new %s%s%s%s%s%s%s card at address %04x\n",
mmc_hostname(card->host),
mmc_card_uhs(card) ? "ultra high speed " :
(mmc_card_highspeed(card) ? "high speed " : ""),
(mmc_card_hs400(card) ? "HS400 " : ""),
(mmc_card_hs400_strobe(card) ? "enhanced strobe " : ""),
(mmc_card_hs200(card) ? "HS200 " : ""),
mmc_card_ddr_mode(card) ? "DDR " : "",
uhs_bus_speed_mode, type, card->rca);

View file

@ -313,6 +313,7 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
}
}
card->ext_csd.raw_strobe_support = ext_csd[EXT_CSD_STROBE_SUPPORT];
/*
* The EXT_CSD format is meant to be forward compatible. As long
* as CSD_STRUCTURE does not change, all values for EXT_CSD_REV
@ -1117,6 +1118,98 @@ out:
return err;
}
static int mmc_select_hs400_strobe(struct mmc_card *card, u8 *ext_csd)
{
int err = 0, ret = 0;
struct mmc_host *host = card->host;
if (!(host->caps2 & MMC_CAP2_HS400) || !host->ops->enhanced_strobe ||
!(card->ext_csd.card_type & EXT_CSD_CARD_TYPE_HS400) ||
!mmc_card_strobe(card)) {
err = -EOPNOTSUPP;
goto err;
}
if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_HS400_1_2V)
&& (host->caps2 & MMC_CAP2_HS400_1_2V))
if (__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120))
err = __mmc_set_signal_voltage(host,
MMC_SIGNAL_VOLTAGE_180);
/* If fails try again during next card power cycle */
if (err) {
pr_err("%s: err in __mmc_set_signal_voltage failed\n",
mmc_hostname(host));
goto err;
}
/*
* For HS400 enhanced strobe mode following sequence is being followed:
* - switch to HS mode.
* - select DDR bus width along with enhanced strobe mode enabled.
* - switch to HS400 mode and set clock to max.
* - call for host specific ops to enable enhanced strobe at host side.
*/
err = mmc_select_hs(card, ext_csd);
if (err) {
pr_warn("%s: switch to high-speed failed, err:%d\n",
mmc_hostname(host), err);
goto err;
}
mmc_card_clr_highspeed(card);
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BUS_WIDTH,
0x86,
card->ext_csd.generic_cmd6_time);
if (err) {
pr_warn("%s: switch to DDR bus width with enhanced strobe for hs400 failed, err:%d\n",
mmc_hostname(host), err);
goto err;
}
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_HS_TIMING,
0x3,
card->ext_csd.generic_cmd6_time);
if (err) {
pr_warn("%s: switch to hs400 failed, err:%d\n",
mmc_hostname(host), err);
goto err;
}
mmc_set_timing(host, MMC_TIMING_MMC_HS400);
mmc_set_clock(host, MMC_HS400_MAX_DTR);
mmc_card_set_hs400(card);
mmc_host_clk_hold(host);
err = host->ops->enhanced_strobe(host);
mmc_host_clk_release(host);
/*
* Fall back to HS mode if hs400 enhanced
* strobe is unsuccessful.
* Lower the clock and adjust the timing
* to be able to switch to HighSpeed mode
*/
if (err) {
pr_debug("%s: strobe execution failed %d\n",
mmc_hostname(host), err);
mmc_card_clr_hs400(card);
mmc_set_timing(host, MMC_TIMING_LEGACY);
mmc_set_clock(host, MMC_HIGH_26_MAX_DTR);
ret = mmc_select_hs(card, ext_csd);
if (ret)
pr_warn("%s: select highspeed mode failed %d\n",
__func__, ret);
goto err;
}
mmc_card_set_hs400_strobe(card);
err:
return err;
}
static int mmc_select_hs400(struct mmc_card *card, u8 *ext_csd)
{
int err = 0;
@ -1236,7 +1329,11 @@ int mmc_set_clock_bus_speed(struct mmc_card *card, unsigned long freq)
mmc_set_timing(card->host, MMC_TIMING_LEGACY);
mmc_set_clock(card->host, MMC_HIGH_26_MAX_DTR);
}
err = mmc_select_hs400(card, card->cached_ext_csd);
if (mmc_card_hs400_strobe(card))
err = mmc_select_hs400_strobe(card,
card->cached_ext_csd);
else
err = mmc_select_hs400(card, card->cached_ext_csd);
}
return err;
@ -1335,7 +1432,8 @@ static int mmc_select_bus_speed(struct mmc_card *card, u8 *ext_csd)
int err = 0;
BUG_ON(!card);
if (!mmc_select_hs400_strobe(card, ext_csd))
goto out;
if (!mmc_select_hs400(card, ext_csd))
goto out;
if (!mmc_select_hs200(card, ext_csd))

View file

@ -374,6 +374,8 @@ static int cmdq_enable(struct mmc_host *mmc)
if (cq_host->ops->clear_set_dumpregs)
cq_host->ops->clear_set_dumpregs(mmc, 1);
if (cq_host->ops->enhanced_strobe_mask)
cq_host->ops->enhanced_strobe_mask(mmc, true);
out:
return err;
}
@ -389,6 +391,9 @@ static void cmdq_disable(struct mmc_host *mmc, bool soft)
}
mmc_host_set_cq_disable(mmc);
if (cq_host->ops->enhanced_strobe_mask)
cq_host->ops->enhanced_strobe_mask(mmc, false);
cq_host->enabled = false;
}

View file

@ -202,6 +202,7 @@ struct cmdq_host_ops {
void (*write_l)(struct cmdq_host *host, u32 val, int reg);
u32 (*read_l)(struct cmdq_host *host, int reg);
void (*clear_set_dumpregs)(struct mmc_host *mmc, bool set);
void (*enhanced_strobe_mask)(struct mmc_host *mmc, bool set);
int (*reset)(struct mmc_host *mmc);
int (*crypto_cfg)(struct mmc_host *mmc, struct mmc_request *mrq,
u32 slot);

View file

@ -146,10 +146,12 @@ enum sdc_mpm_pin_state {
#define CORE_DDR_200_CFG 0x184
#define CORE_CDC_T4_DLY_SEL (1 << 0)
#define CORE_CMDIN_RCLK_EN (1 << 1)
#define CORE_START_CDC_TRAFFIC (1 << 6)
#define CORE_VENDOR_SPEC3 0x1B0
#define CORE_PWRSAVE_DLL (1 << 3)
#define CORE_CMDEN_HS400_INPUT_MASK_CNT (1 << 13)
#define CORE_DLL_CONFIG_2 0x1B4
#define CORE_DDR_CAL_EN (1 << 0)
@ -797,6 +799,8 @@ static int sdhci_msm_cm_dll_sdc4_calibration(struct sdhci_host *host)
{
u32 dll_status, ddr_config;
int ret = 0;
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = pltfm_host->priv;
pr_debug("%s: Enter %s\n", mmc_hostname(host->mmc), __func__);
@ -808,6 +812,11 @@ static int sdhci_msm_cm_dll_sdc4_calibration(struct sdhci_host *host)
ddr_config |= DDR_CONFIG_PRG_RCLK_DLY;
writel_relaxed(ddr_config, host->ioaddr + CORE_DDR_CONFIG);
if (msm_host->enhanced_strobe && mmc_card_strobe(msm_host->mmc->card))
writel_relaxed((readl_relaxed(host->ioaddr + CORE_DDR_200_CFG)
| CORE_CMDIN_RCLK_EN),
host->ioaddr + CORE_DDR_200_CFG);
/* Write 1 to DDR_CAL_EN field in CORE_DLL_CONFIG_2 */
writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG_2)
| CORE_DDR_CAL_EN),
@ -834,6 +843,42 @@ out:
return ret;
}
static int sdhci_msm_enhanced_strobe(struct sdhci_host *host)
{
int ret = 0;
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = pltfm_host->priv;
struct mmc_host *mmc = host->mmc;
pr_debug("%s: Enter %s\n", mmc_hostname(host->mmc), __func__);
if (!msm_host->enhanced_strobe || !mmc_card_strobe(mmc->card)) {
pr_debug("%s: host/card does not support hs400 enhanced strobe\n",
mmc_hostname(mmc));
return -EINVAL;
}
if (msm_host->calibration_done ||
!(mmc->ios.timing == MMC_TIMING_MMC_HS400)) {
return 0;
}
/*
* Reset the tuning block.
*/
ret = msm_init_cm_dll(host);
if (ret)
goto out;
ret = sdhci_msm_cm_dll_sdc4_calibration(host);
out:
if (!ret)
msm_host->calibration_done = true;
pr_debug("%s: Exit %s, ret:%d\n", mmc_hostname(host->mmc),
__func__, ret);
return ret;
}
static int sdhci_msm_hs400_dll_calibration(struct sdhci_host *host)
{
int ret = 0;
@ -2849,7 +2894,10 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
* Select HS400 mode using the HC_SELECT_IN from VENDOR SPEC
* register
*/
if (msm_host->tuning_done && !msm_host->calibration_done) {
if ((msm_host->tuning_done ||
(mmc_card_strobe(msm_host->mmc->card) &&
msm_host->enhanced_strobe)) &&
!msm_host->calibration_done) {
/*
* Write 0x6 to HC_SELECT_IN and 1 to HC_SELECT_IN_EN
* field in VENDOR_SPEC_FUNC
@ -3189,6 +3237,36 @@ static void sdhci_msm_notify_pm_status(struct sdhci_host *host,
msm_host->mmc_dev_state = state;
}
/*
* sdhci_msm_enhanced_strobe_mask :-
* Before running CMDQ transfers in HS400 Enhanced Strobe mode,
* SW should write 3 to
* HC_VENDOR_SPECIFIC_FUNC3.CMDEN_HS400_INPUT_MASK_CNT register.
* The default reset value of this register is 2.
*/
static void sdhci_msm_enhanced_strobe_mask(struct sdhci_host *host, bool set)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = pltfm_host->priv;
if (!msm_host->enhanced_strobe ||
!mmc_card_strobe(msm_host->mmc->card)) {
pr_debug("%s: host/card does not support hs400 enhanced strobe\n",
mmc_hostname(host->mmc));
return;
}
if (set) {
writel_relaxed((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC3)
| CORE_CMDEN_HS400_INPUT_MASK_CNT),
host->ioaddr + CORE_VENDOR_SPEC3);
} else {
writel_relaxed((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC3)
& ~CORE_CMDEN_HS400_INPUT_MASK_CNT),
host->ioaddr + CORE_VENDOR_SPEC3);
}
}
static void sdhci_msm_clear_set_dumpregs(struct sdhci_host *host, bool set)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@ -3239,6 +3317,7 @@ static struct sdhci_ops sdhci_msm_ops = {
.set_uhs_signaling = sdhci_msm_set_uhs_signaling,
.check_power_status = sdhci_msm_check_power_status,
.execute_tuning = sdhci_msm_execute_tuning,
.enhanced_strobe = sdhci_msm_enhanced_strobe,
.toggle_cdr = sdhci_msm_toggle_cdr,
.get_max_segments = sdhci_msm_max_segs,
.set_clock = sdhci_msm_set_clock,
@ -3252,6 +3331,7 @@ static struct sdhci_ops sdhci_msm_ops = {
.clear_set_dumpregs = sdhci_msm_clear_set_dumpregs,
.notify_load = sdhci_msm_notify_load,
.notify_pm_status = sdhci_msm_notify_pm_status,
.enhanced_strobe_mask = sdhci_msm_enhanced_strobe_mask,
};
static int sdhci_msm_cfg_mpm_pin_wakeup(struct sdhci_host *host, unsigned mode)
@ -3343,9 +3423,12 @@ static void sdhci_set_default_hw_caps(struct sdhci_msm_host *msm_host,
/*
* SDCC 5 controller with major version 1, minor version 0x42 and later
* will require additional steps when resetting DLL.
* It also supports HS400 enhanced strobe mode.
*/
if ((major == 1) && (minor >= 0x42))
if ((major == 1) && (minor >= 0x42)) {
msm_host->use_updated_dll_reset = true;
msm_host->enhanced_strobe = true;
}
/*
* Mask 64-bit support for controller with 32-bit address bus so that

View file

@ -165,5 +165,6 @@ struct sdhci_msm_host {
enum dev_state mmc_dev_state;
struct sdhci_msm_ice_data ice;
u32 ice_clk_rate;
bool enhanced_strobe;
};
#endif /* __SDHCI_MSM_H__ */

View file

@ -62,6 +62,7 @@ static void sdhci_finish_data(struct sdhci_host *);
static void sdhci_send_command(struct sdhci_host *, struct mmc_command *);
static void sdhci_finish_command(struct sdhci_host *);
static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
static int sdhci_enhanced_strobe(struct mmc_host *mmc);
static void sdhci_tuning_timer(unsigned long data);
static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
static bool sdhci_check_state(struct sdhci_host *);
@ -2551,6 +2552,19 @@ static int sdhci_card_busy(struct mmc_host *mmc)
return !(present_state & SDHCI_DATA_LVL_MASK);
}
static int sdhci_enhanced_strobe(struct mmc_host *mmc)
{
struct sdhci_host *host = mmc_priv(mmc);
int err = -EINVAL;
sdhci_runtime_pm_get(host);
if (host->ops->enhanced_strobe)
err = host->ops->enhanced_strobe(host);
sdhci_runtime_pm_put(host);
return err;
}
static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
{
struct sdhci_host *host;
@ -2872,6 +2886,7 @@ static const struct mmc_host_ops sdhci_ops = {
.enable_sdio_irq = sdhci_enable_sdio_irq,
.start_signal_voltage_switch = sdhci_start_signal_voltage_switch,
.execute_tuning = sdhci_execute_tuning,
.enhanced_strobe = sdhci_enhanced_strobe,
.card_event = sdhci_card_event,
.card_busy = sdhci_card_busy,
.enable = sdhci_enable,
@ -3779,6 +3794,14 @@ static void sdhci_cmdq_set_block_size(struct mmc_host *mmc)
sdhci_set_blk_size_reg(host, 512, 0);
}
static void sdhci_enhanced_strobe_mask(struct mmc_host *mmc, bool set)
{
struct sdhci_host *host = mmc_priv(mmc);
if (host->ops->enhanced_strobe_mask)
host->ops->enhanced_strobe_mask(host, set);
}
static void sdhci_cmdq_clear_set_dumpregs(struct mmc_host *mmc, bool set)
{
struct sdhci_host *host = mmc_priv(mmc);
@ -3851,6 +3874,11 @@ static void sdhci_cmdq_set_block_size(struct mmc_host *mmc)
}
static void sdhci_enhanced_strobe_mask(struct mmc_host *mmc, bool set)
{
}
static void sdhci_cmdq_clear_set_dumpregs(struct mmc_host *mmc, bool set)
{
@ -3880,6 +3908,7 @@ static const struct cmdq_host_ops sdhci_cmdq_ops = {
.dump_vendor_regs = sdhci_cmdq_dump_vendor_regs,
.set_block_size = sdhci_cmdq_set_block_size,
.clear_set_dumpregs = sdhci_cmdq_clear_set_dumpregs,
.enhanced_strobe_mask = sdhci_enhanced_strobe_mask,
.crypto_cfg = sdhci_cmdq_crypto_cfg,
.crypto_cfg_reset = sdhci_cmdq_crypto_cfg_reset,
.post_cqe_halt = sdhci_cmdq_post_cqe_halt,

View file

@ -320,6 +320,7 @@ struct sdhci_ops {
int (*execute_tuning)(struct sdhci_host *host, u32 opcode);
void (*toggle_cdr)(struct sdhci_host *host, bool enable);
unsigned int (*get_max_segments)(void);
int (*enhanced_strobe)(struct sdhci_host *host);
void (*platform_bus_voting)(struct sdhci_host *host, u32 enable);
void (*disable_data_xfer)(struct sdhci_host *host);
void (*dump_vendor_regs)(struct sdhci_host *host);
@ -332,6 +333,7 @@ struct sdhci_ops {
int (*notify_load)(struct sdhci_host *host, enum mmc_load state);
void (*notify_pm_status)(struct sdhci_host *host,
enum dev_state state);
void (*enhanced_strobe_mask)(struct sdhci_host *host, bool set);
};
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS

View file

@ -95,6 +95,8 @@ struct mmc_ext_csd {
u8 raw_partition_support; /* 160 */
u8 raw_rpmb_size_mult; /* 168 */
u8 raw_erased_mem_count; /* 181 */
u8 raw_strobe_support; /* 184 */
#define MMC_STROBE_SUPPORT (1 << 0)
u8 raw_ext_csd_structure; /* 194 */
u8 raw_card_type; /* 196 */
u8 raw_drive_strength; /* 197 */
@ -351,6 +353,7 @@ struct mmc_card {
#define MMC_STATE_NEED_BKOPS (1<<11) /* card needs to do BKOPS */
#define MMC_STATE_CMDQ (1<<12) /* card is in cmd queue mode */
#define MMC_STATE_SUSPENDED (1<<13) /* card is suspended */
#define MMC_STATE_HS400_STROBE (1<<14) /* card is in strobe mode */
unsigned int quirks; /* card quirks */
#define MMC_QUIRK_LENIENT_FN0 (1<<0) /* allow SDIO FN0 writes outside of the VS CCCR range */
#define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1) /* use func->cur_blksize */
@ -546,6 +549,9 @@ struct mmc_fixup {
card->cid.year, \
card->cid.month)
#define mmc_card_strobe(c) (((c)->ext_csd).raw_strobe_support & \
MMC_STROBE_SUPPORT)
/*
* Unconditionally quirk add/remove.
*/
@ -569,6 +575,7 @@ 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_hs200(c) ((c)->state & MMC_STATE_HIGHSPEED_200)
#define mmc_card_hs400(c) ((c)->state & MMC_STATE_HIGHSPEED_400)
#define mmc_card_hs400_strobe(c) ((c)->state & MMC_STATE_HS400_STROBE)
#define mmc_card_blockaddr(c) ((c)->state & MMC_STATE_BLOCKADDR)
#define mmc_card_ddr_mode(c) ((c)->state & MMC_STATE_HIGHSPEED_DDR)
#define mmc_card_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED)
@ -588,6 +595,8 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
#define mmc_card_clr_hs200(c) ((c)->state &= ~MMC_STATE_HIGHSPEED_200)
#define mmc_card_set_hs400(c) ((c)->state |= MMC_STATE_HIGHSPEED_400)
#define mmc_card_clr_hs400(c) ((c)->state &= ~MMC_STATE_HIGHSPEED_400)
#define mmc_card_set_hs400_strobe(c) ((c)->state |= MMC_STATE_HS400_STROBE)
#define mmc_card_clr_hs400_strobe(c) ((c)->state &= ~MMC_STATE_HS400_STROBE)
#define mmc_card_set_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR)
#define mmc_card_set_ddr_mode(c) ((c)->state |= MMC_STATE_HIGHSPEED_DDR)
#define mmc_card_clr_ddr_mode(c) ((c)->state &= ~MMC_STATE_HIGHSPEED_DDR)

View file

@ -165,6 +165,7 @@ struct mmc_host_ops {
/* The tuning command opcode value is different for SD and eMMC cards */
int (*execute_tuning)(struct mmc_host *host, u32 opcode);
int (*enhanced_strobe)(struct mmc_host *host);
int (*select_drive_strength)(unsigned int max_dtr, int host_drv, int card_drv);
void (*hw_reset)(struct mmc_host *host);
void (*card_event)(struct mmc_host *host);

View file

@ -244,6 +244,7 @@ struct _mmc_csd {
#define EXT_CSD_PART_CONFIG 179 /* R/W */
#define EXT_CSD_ERASED_MEM_CONT 181 /* RO */
#define EXT_CSD_BUS_WIDTH 183 /* R/W */
#define EXT_CSD_STROBE_SUPPORT 184 /* RO */
#define EXT_CSD_HS_TIMING 185 /* R/W */
#define EXT_CSD_POWER_CLASS 187 /* R/W */
#define EXT_CSD_REV 192 /* RO */
@ -334,6 +335,7 @@ struct _mmc_csd {
#define EXT_CSD_CARD_TYPE_HS400 (EXT_CSD_CARD_TYPE_HS400_1_8V \
| EXT_CSD_CARD_TYPE_HS400_1_2V)
#define EXT_CSD_ENHANCED_STROBE (1 << 0)
#define EXT_CSD_BUS_WIDTH_1 0 /* Card is in 1 bit mode */
#define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */
#define EXT_CSD_BUS_WIDTH_8 2 /* Card is in 8 bit mode */