提交 41236b73 编写于 作者: Z zhangguijiang 提交者: Yang Yingliang

EMMC: adaption for ascend customized emmc card

ascend inclusion
category: feature
feature: Ascend emmc adaption
bugzilla: https://gitee.com/openeuler/kernel/issues/I4F4LL
CVE: NA

-------------------

Modified mmc ops for Ascend HiSilicon emmc card because of the
difference bwteen our emmc card and others. we used customized
property in dts to judge if it is our chips. In this patch, we
modified emmc card init process, rate setting and ext_csd register
decode/encode.
Signed-off-by: Nzhangguijiang <zhangguijiang@huawei.com>
Reviewed-by: NDing Tianhong <dingtianhong@huawei.com>
Signed-off-by: NYang Yingliang <yangyingliang@huawei.com>
上级 76b1b2e0
......@@ -28,6 +28,7 @@
#include "quirks.h"
#include "sd_ops.h"
#include "pwrseq.h"
#include "hisi_core_mmc.h"
#define DEFAULT_CMD6_TIMEOUT_MS 500
#define MIN_CACHE_EN_TIMEOUT_MS 1600
......@@ -415,7 +416,9 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
card->ext_csd.strobe_support = ext_csd[EXT_CSD_STROBE_SUPPORT];
card->ext_csd.raw_card_type = ext_csd[EXT_CSD_CARD_TYPE];
mmc_select_card_type(card);
if (mmc_is_ascend_customized(card->host->parent))
card->ext_csd.raw_driver_strength =
ext_csd[EXT_CSD_DRIVER_STRENGTH];
card->ext_csd.raw_s_a_timeout = ext_csd[EXT_CSD_S_A_TIMEOUT];
card->ext_csd.raw_erase_timeout_mult =
ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT];
......@@ -424,7 +427,9 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
if (card->ext_csd.rev >= 3) {
u8 sa_shift = ext_csd[EXT_CSD_S_A_TIMEOUT];
card->ext_csd.part_config = ext_csd[EXT_CSD_PART_CONFIG];
if (mmc_is_ascend_customized(card->host->parent) &&
card->cid.manfid == CID_MANFID_HYNIX)
ext_csd[EXT_CSD_PART_SWITCH_TIME] = 0xA;
/* EXT_CSD value is in units of 10ms, but we store in ms */
card->ext_csd.part_time = 10 * ext_csd[EXT_CSD_PART_SWITCH_TIME];
/* Some eMMC set the value too low so set a minimum */
......@@ -513,6 +518,9 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
ext_csd[EXT_CSD_PWR_CL_200_195];
card->ext_csd.raw_pwr_cl_200_360 =
ext_csd[EXT_CSD_PWR_CL_200_360];
if (mmc_is_ascend_customized(card->host->parent))
card->ext_csd.raw_pwr_cl_ddr_200_360 =
ext_csd[EXT_CSD_PWR_CL_DDR_200_360];
card->ext_csd.raw_pwr_cl_ddr_52_195 =
ext_csd[EXT_CSD_PWR_CL_DDR_52_195];
card->ext_csd.raw_pwr_cl_ddr_52_360 =
......@@ -622,6 +630,9 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
/* eMMC v5 or later */
if (card->ext_csd.rev >= 7) {
if (mmc_is_ascend_customized(card->host->parent))
/*new feature for emmc v5.0*/
mmc_decode_ext_csd_emmc50(card, ext_csd);
memcpy(card->ext_csd.fwrev, &ext_csd[EXT_CSD_FIRMWARE_VERSION],
MMC_FIRMWARE_LEN);
card->ext_csd.ffu_capable =
......@@ -651,7 +662,21 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
mmc_hostname(card->host),
card->ext_csd.cmdq_depth);
}
} else {
if (mmc_is_ascend_customized(card->host->parent)) {
/* 0x00 means Not defined,
* write 0x00 in lower eMMC version
*/
card->ext_csd.pre_eol_info = 0x00;
card->ext_csd.device_life_time_est_typ_a = 0x00;
card->ext_csd.device_life_time_est_typ_b = 0x00;
card->ext_csd.cmdq_support = 0;
card->ext_csd.cmdq_depth = 0;
}
}
if (mmc_is_ascend_customized(card->host->parent))
/*new feature for emmc v5.1*/
mmc_decode_ext_csd_emmc51(card, ext_csd);
out:
return err;
}
......@@ -760,6 +785,12 @@ static int mmc_compare_ext_csds(struct mmc_card *card, unsigned bus_width)
(card->ext_csd.raw_pwr_cl_ddr_200_360 ==
bw_ext_csd[EXT_CSD_PWR_CL_DDR_200_360]));
if (!err && mmc_is_ascend_customized(card->host->parent))
err =
!((card->ext_csd.raw_driver_strength ==
bw_ext_csd[EXT_CSD_DRIVER_STRENGTH]) &&
(card->ext_csd.raw_pwr_cl_ddr_200_360 ==
bw_ext_csd[EXT_CSD_PWR_CL_DDR_200_360]));
if (err)
err = -EINVAL;
......@@ -794,6 +825,12 @@ MMC_DEV_ATTR(rel_sectors, "%#x\n", card->ext_csd.rel_sectors);
MMC_DEV_ATTR(ocr, "0x%08x\n", card->ocr);
MMC_DEV_ATTR(rca, "0x%04x\n", card->rca);
MMC_DEV_ATTR(cmdq_en, "%d\n", card->ext_csd.cmdq_en);
#ifdef CONFIG_ASCEND_HISI_MMC
MMC_DEV_ATTR(life_time_est_typ_a, "0x%02X\n",
card->ext_csd.device_life_time_est_typ_a);
MMC_DEV_ATTR(life_time_est_typ_b, "0x%02X\n",
card->ext_csd.device_life_time_est_typ_b);
#endif
static ssize_t mmc_fwrev_show(struct device *dev,
struct device_attribute *attr,
......@@ -852,6 +889,10 @@ static struct attribute *mmc_std_attrs[] = {
&dev_attr_rca.attr,
&dev_attr_dsr.attr,
&dev_attr_cmdq_en.attr,
#ifdef CONFIG_ASCEND_HISI_MMC
&dev_attr_life_time_est_typ_a.attr,
&dev_attr_life_time_est_typ_b.attr,
#endif
NULL,
};
ATTRIBUTE_GROUPS(mmc_std);
......@@ -1490,6 +1531,65 @@ static int mmc_select_hs200(struct mmc_card *card)
return err;
}
#ifdef CONFIG_ASCEND_HISI_MMC
static int hisi_mmc_select_hs200(struct mmc_card *card)
{
struct mmc_host *host = card->host;
unsigned int old_timing;
int err = -EINVAL;
u8 val;
mmc_select_driver_type(card);
/*
* Set the bus width(4 or 8) with host's support and
* switch to HS200 mode if bus width is set successfully.
*/
err = mmc_select_bus_width(card);
if (err > 0) {
val = EXT_CSD_TIMING_HS200 |
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, 0,
true, false, true);
if (err)
goto err;
old_timing = host->ios.timing;
mmc_set_timing(host, MMC_TIMING_MMC_HS200);
/*need to change voltage after set timing on hisi plat*/
if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200_1_2V)
err = mmc_set_signal_voltage(host,
MMC_SIGNAL_VOLTAGE_120);
if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200_1_8V)
err = mmc_set_signal_voltage(host,
MMC_SIGNAL_VOLTAGE_180);
/* If fails try again during next card power cycle */
if (err)
goto err;
}
err:
if (err)
pr_err("%s: %s failed, error %d\n", mmc_hostname(card->host),
__func__, err);
return err;
}
static inline int mmc_select_hs200_adapt(struct mmc_card *card)
{
if (mmc_is_ascend_customized(card->host->parent))
return hisi_mmc_select_hs200(card);
return mmc_select_hs200(card);
}
#else
static inline int mmc_select_hs200_adapt(struct mmc_card *card)
{
return mmc_select_hs200(card);
}
#endif /* CONFIG_ASCEND_HISI_MMC */
/*
* Activate High Speed, HS200 or HS400ES mode if supported.
*/
......@@ -1503,10 +1603,12 @@ static int mmc_select_timing(struct mmc_card *card)
if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400ES)
err = mmc_select_hs400es(card);
else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200)
err = mmc_select_hs200(card);
err = mmc_select_hs200_adapt(card);
else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS)
err = mmc_select_hs(card);
else if (mmc_is_ascend_customized(card->host->parent) &&
card->mmc_avail_type == 0)
mmc_select_new_sd(card);
if (err && err != -EBADMSG)
return err;
......@@ -1549,6 +1651,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
struct mmc_card *oldcard)
{
struct mmc_card *card;
int cache_size_is_valid;
int err;
u32 cid[4];
u32 rocr;
......@@ -1740,6 +1843,21 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
card->ext_csd.power_off_notification = EXT_CSD_POWER_ON;
}
#ifdef CONFIG_ASCEND_HISI_MMC
/*
* Enable Enhanced strobe
*/
if (mmc_is_ascend_customized(card->host->parent) &&
card->ext_csd.strobe_enhanced) {
if (host->caps2 & MMC_CAP2_ENHANCED_STROBE) {
card->ext_csd.strobe_enhanced_en = 1;
pr_err("%s: strobe_enhanced_en=%d\n", __func__,
card->ext_csd.strobe_enhanced_en);
} else {
card->ext_csd.strobe_enhanced_en = 0;
}
}
#endif
/*
* Select timing interface
*/
......@@ -1763,6 +1881,10 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
if (err)
goto free_card;
}
} else {
if (mmc_is_ascend_customized(card->host->parent))
/*to support mmc card*/
mmc_select_bus_width(card);
}
/*
......@@ -1796,7 +1918,15 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
* sudden power failure tests. Let's extend the timeout to a minimum of
* DEFAULT_CACHE_EN_TIMEOUT_MS and do it for all cards.
*/
if (card->ext_csd.cache_size > 0) {
#ifdef CONFIG_ASCEND_HISI_MMC
if (mmc_is_ascend_customized(host->parent))
cache_size_is_valid = (host->caps2 & MMC_CAP2_CACHE_CTRL) &&
(card->ext_csd.cache_size > 0);
else
#endif
cache_size_is_valid = (card->ext_csd.cache_size > 0);
if (cache_size_is_valid) {
unsigned int timeout_ms = MIN_CACHE_EN_TIMEOUT_MS;
timeout_ms = max(card->ext_csd.generic_cmd6_time, timeout_ms);
......@@ -1862,6 +1992,11 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
goto free_card;
}
if (mmc_is_ascend_customized(host->parent)) {
err = mmc_init_card_enable_feature(card);
if (err)
goto free_card;
}
if (!oldcard)
host->card = card;
......@@ -1879,6 +2014,58 @@ static int mmc_can_sleep(struct mmc_card *card)
return (card && card->ext_csd.rev >= 3);
}
#ifdef CONFIG_ASCEND_HISI_MMC
/*
* reinit card after reset in 2019.5.28
*/
static int mmc_reinit_card(struct mmc_host *host)
{
int err;
struct mmc_card *card = host->card;
pr_info("enter reinit card\n");
mmc_flush_cache(host->card);
err = mmc_init_card(host, card->ocr, card);
pr_info("exit reinit card ret %d\n", err);
return err;
}
static int mmc_can_sleep_notify(const struct mmc_card *card)
{
return card && mmc_card_mmc(card) && card->ext_csd.raw_sleep_noti_time;
}
static int hisi_mmc_sleep(struct mmc_host *host)
{
struct mmc_card *card = host->card;
int err = -ENOSYS;
if (card && card->ext_csd.rev >= 3) {
err = mmc_card_sleepawake(host, 1);
if (err < 0)
pr_debug("%s: Error %d while putting card into sleep",
mmc_hostname(host), err);
}
return err;
}
static int mmc_awake(struct mmc_host *host)
{
struct mmc_card *card = host->card;
int err = -ENOSYS;
if (card && card->ext_csd.rev >= 3) {
err = mmc_card_sleepawake(host, 0);
if (err < 0)
pr_debug("%s: Error %d while awaking sleeping card",
mmc_hostname(host), err);
}
return err;
}
#endif
static int mmc_sleep(struct mmc_host *host)
{
struct mmc_command cmd = {};
......@@ -1943,7 +2130,11 @@ static int mmc_poweroff_notify(struct mmc_card *card, unsigned int notify_type)
/* Use EXT_CSD_POWER_OFF_SHORT as default notification type. */
if (notify_type == EXT_CSD_POWER_OFF_LONG)
timeout = card->ext_csd.power_off_longtime;
#ifdef CONFIG_ASCEND_HISI_MMC
else if (mmc_is_ascend_customized(card->host->parent) &&
notify_type == EXT_CSD_SLEEP_NOTIFICATION)
timeout = card->ext_csd.sleep_notification_time;
#endif
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_POWER_OFF_NOTIFICATION,
notify_type, timeout, 0, true, false, false);
......@@ -2000,6 +2191,79 @@ static void mmc_detect(struct mmc_host *host)
}
}
#ifdef CONFIG_ASCEND_HISI_MMC
/*suspend operation on hisi platform, conclude disable cmdq
*swich to normal partion and so on
*/
extern int mmc_suspend_hisi_operation(struct mmc_host *host);
static int _hisi_mmc_suspend(struct mmc_host *host, bool is_suspend)
{
int err = 0;
unsigned int notify_type = is_suspend ? EXT_CSD_SLEEP_NOTIFICATION :
EXT_CSD_POWER_OFF_LONG;
WARN_ON(!host);
WARN_ON(!host->card);
mmc_claim_host(host);
if (mmc_card_suspended(host->card))
goto out;
err = mmc_suspend_hisi_operation(host);
if (err) {
pr_err("%s: mmc suspend hisi operation failed.\n", __func__);
goto out;
}
err = mmc_cache_ctrl(host, 0);
if (err) {
pr_err("%s: cache disab failed.\n", __func__);
goto out;
}
if (mmc_can_sleep(host->card)) {
if (mmc_can_poweroff_notify(host->card) &&
((host->caps2 & MMC_CAP2_FULL_PWR_CYCLE &&
mmc_can_sleep_notify(host->card)) || !is_suspend)) {
err = mmc_poweroff_notify(host->card, notify_type);
if (err)
goto out;
}
err = mmc_card_sleep(host);
/*if sleep(send cmd5 failed),we reinit the card
*we don't need to reinit when shut down
*/
if (err && is_suspend) {
pr_err("%s suspend failed err=%d\n", __func__, err);
mmc_power_off(host);
mdelay(200);
mmc_power_up(host, host->card->ocr);
(void)mmc_init_card(host, host->card->ocr, host->card);
}
} else if (!mmc_host_is_spi(host)) {
if (mmc_can_poweroff_notify(host->card))
err = mmc_poweroff_notify(host->card,
EXT_CSD_POWER_OFF_SHORT);
else
err = mmc_deselect_cards(host);
}
if (!err) {
if (!mmc_card_keep_power(host))
mmc_power_off_vcc(host);
mmc_card_set_suspended(host->card);
}
out:
mmc_release_host(host);
return err;
}
#else
static int _hisi_mmc_suspend(struct mmc_host *host, bool is_suspend)
{
return 0;
}
#endif
static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
{
int err = 0;
......@@ -2054,6 +2318,59 @@ static int mmc_suspend(struct mmc_host *host)
return err;
}
#ifdef CONFIG_ASCEND_HISI_MMC
static int hisi_mmc_suspend(struct mmc_host *host)
{
int err;
err = _hisi_mmc_suspend(host, true);
if (!err) {
pm_runtime_disable(&host->card->dev);
pm_runtime_set_suspended(&host->card->dev);
}
return err;
}
static int hisi_enable_power_off_notify(struct mmc_host *host)
{
int err = 0;
if (!mmc_card_keep_power(host))
mmc_power_up_vcc(host, host->card->ocr);
/* The native code enable power_off_notification
* in mmc_init_card,
* But we use mmc_card_awake instead of mmc_init_card
* so we enable power_off_notification here.
*/
if (host->caps2 & MMC_CAP2_FULL_PWR_CYCLE
&& mmc_can_sleep_notify(host->card))
host->card->ext_csd.power_off_notification =
EXT_CSD_POWER_ON;
err = mmc_card_awake(host);/*according to K3 modify*/
if (err) {
pr_err("%s awake failed err=%d\n", __func__, err);
mmc_power_off(host);
mdelay(200);
mmc_power_up(host, host->card->ocr);
err = mmc_init_card(host, host->card->ocr, host->card);
} else {
err = mmc_cache_ctrl(host, 1);
if (err)
pr_err("%s: error %d cache on\n",
mmc_hostname(host), err);
}
return err;
}
#else
static int hisi_enable_power_off_notify(struct mmc_host *host)
{
return 0;
}
#endif
/*
* This function tries to determine if the same card is still present
* and, if so, restore all state to it.
......@@ -2066,7 +2383,8 @@ static int _mmc_resume(struct mmc_host *host)
if (!mmc_card_suspended(host->card))
goto out;
if (mmc_is_ascend_customized(host->parent))
err = hisi_enable_power_off_notify(host);
mmc_power_up(host, host->card->ocr);
err = mmc_init_card(host, host->card->ocr, host->card);
mmc_card_clr_suspended(host->card);
......@@ -2091,8 +2409,12 @@ static int mmc_shutdown(struct mmc_host *host)
!(host->caps2 & MMC_CAP2_FULL_PWR_CYCLE))
err = _mmc_resume(host);
if (!err)
err = _mmc_suspend(host, false);
if (!err) {
if (mmc_is_ascend_customized(host->parent))
err = _hisi_mmc_suspend(host, false);
else
err = _mmc_suspend(host, false);
}
return err;
}
......@@ -2102,8 +2424,18 @@ static int mmc_shutdown(struct mmc_host *host)
*/
static int mmc_resume(struct mmc_host *host)
{
int err = 0;
#ifdef CONFIG_ASCEND_HISI_MMC
if (mmc_is_ascend_customized(host->parent)) {
if (!(host->caps & MMC_CAP_RUNTIME_RESUME)) {
err = _mmc_resume(host);
pm_runtime_set_active(&host->card->dev);
pm_runtime_mark_last_busy(&host->card->dev);
}
}
#endif
pm_runtime_enable(&host->card->dev);
return 0;
return err;
}
/*
......@@ -2116,7 +2448,11 @@ static int mmc_runtime_suspend(struct mmc_host *host)
if (!(host->caps & MMC_CAP_AGGRESSIVE_PM))
return 0;
err = _mmc_suspend(host, true);
if (mmc_is_ascend_customized(host->parent))
err = _hisi_mmc_suspend(host, true);
else
err = _mmc_suspend(host, true);
if (err)
pr_err("%s: error %d doing aggressive suspend\n",
mmc_hostname(host), err);
......@@ -2131,6 +2467,12 @@ static int mmc_runtime_resume(struct mmc_host *host)
{
int err;
#ifdef CONFIG_ASCEND_HISI_MMC
if (mmc_is_ascend_customized(host->parent))
if (!(host->caps &
(MMC_CAP_AGGRESSIVE_PM | MMC_CAP_RUNTIME_RESUME)))
return 0;
#endif
err = _mmc_resume(host);
if (err && err != -ENOMEDIUM)
pr_err("%s: error %d doing runtime resume\n",
......@@ -2186,6 +2528,23 @@ static const struct mmc_bus_ops mmc_ops = {
.hw_reset = _mmc_hw_reset,
};
#ifdef CONFIG_ASCEND_HISI_MMC
static const struct mmc_bus_ops hisi_mmc_ops = {
.awake = mmc_awake,
.sleep = hisi_mmc_sleep,
.remove = mmc_remove,
.detect = mmc_detect,
.suspend = hisi_mmc_suspend,
.resume = mmc_resume,
.runtime_suspend = mmc_runtime_suspend,
.runtime_resume = mmc_runtime_resume,
.alive = mmc_alive,
.shutdown = mmc_shutdown,
.hw_reset = hisi_mmc_reset,
.reinit_card = mmc_reinit_card,
};
#endif
/*
* Starting point for MMC card init.
*/
......@@ -2203,8 +2562,12 @@ int mmc_attach_mmc(struct mmc_host *host)
err = mmc_send_op_cond(host, 0, &ocr);
if (err)
return err;
mmc_attach_bus(host, &mmc_ops);
#ifdef CONFIG_ASCEND_HISI_MMC
if (mmc_is_ascend_customized(host->parent))
mmc_attach_bus(host, &hisi_mmc_ops);
else
#endif
mmc_attach_bus(host, &mmc_ops);
if (host->ocr_avail_mmc)
host->ocr_avail = host->ocr_avail_mmc;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册