From: zhangguijiang zhangguijiang@huawei.com
ascend inclusion category: feature feature: Ascend emmc adaption bugzilla: https://gitee.com/openeuler/kernel/issues/I4F4LL CVE: NA
--------------------
To support Ascend HiSilicon emmc chip, we add specific extensions in mmc core, include mmc card sleep/awake/reset, cache ctrl, power vcc adaptions and etc. Also, we enable some features for emmc5.0/5.1.
Signed-off-by: zhangguijiang zhangguijiang@huawei.com Reviewed-by: Ding Tianhong dingtianhong@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- drivers/mmc/core/Makefile | 1 + drivers/mmc/core/block.c | 3 + drivers/mmc/core/bus.c | 11 + drivers/mmc/core/core.c | 65 ++- drivers/mmc/core/core.h | 18 + drivers/mmc/core/hisi_core_mmc.c | 818 +++++++++++++++++++++++++++++++ drivers/mmc/core/hisi_core_mmc.h | 52 ++ include/linux/mmc/card.h | 105 ++++ include/linux/mmc/core.h | 4 +- include/linux/mmc/mmc.h | 46 ++ 10 files changed, 1106 insertions(+), 17 deletions(-) create mode 100644 drivers/mmc/core/hisi_core_mmc.c create mode 100644 drivers/mmc/core/hisi_core_mmc.h
diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile index abba078f7f490..5d76782ffb76d 100644 --- a/drivers/mmc/core/Makefile +++ b/drivers/mmc/core/Makefile @@ -18,3 +18,4 @@ obj-$(CONFIG_MMC_BLOCK) += mmc_block.o mmc_block-objs := block.o queue.o obj-$(CONFIG_MMC_TEST) += mmc_test.o obj-$(CONFIG_SDIO_UART) += sdio_uart.o +mmc_core-$(CONFIG_ASCEND_HISI_MMC) += hisi_core_mmc.o diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 60eac66dc9f0b..d55f3abfc7edf 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -2247,6 +2247,9 @@ enum mmc_issued mmc_blk_mq_issue_rq(struct mmc_queue *mq, struct request *req)
static inline int mmc_blk_readonly(struct mmc_card *card) { + if (mmc_is_ascend_customized(card->host->parent)) + return 0; + return mmc_card_readonly(card) || !(card->csd.cmdclass & CCC_BLOCK_WRITE); } diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index fc92c6c1c9a4b..a12d260181a2b 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -134,6 +134,17 @@ static void mmc_bus_shutdown(struct device *dev) struct mmc_host *host = card->host; int ret;
+ if (mmc_is_ascend_customized(host->parent)) { + int present = 1; + + host->rescan_disable = 1; + if (host->ops->get_cd) + present = host->ops->get_cd(host); + + if (present == 0) + return; + } + if (dev->driver && drv->shutdown) drv->shutdown(card);
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 0a74785e575ba..c5c021bdc9ad6 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -36,8 +36,12 @@ #include <linux/mmc/sd.h> #include <linux/mmc/slot-gpio.h>
+#include <linux/jiffies.h> + #define CREATE_TRACE_POINTS #include <trace/events/mmc.h> +#include <linux/version.h> +
#include "core.h" #include "card.h" @@ -924,7 +928,7 @@ EXPORT_SYMBOL(mmc_put_card); * Internal function that does the actual ios call to the host driver, * optionally printing some debug output. */ -static inline void mmc_set_ios(struct mmc_host *host) +void mmc_set_ios(struct mmc_host *host) { struct mmc_ios *ios = &host->ios;
@@ -1021,7 +1025,8 @@ void mmc_set_initial_state(struct mmc_host *host) host->ios.chip_select = MMC_CS_HIGH; else host->ios.chip_select = MMC_CS_DONTCARE; - host->ios.bus_mode = MMC_BUSMODE_PUSHPULL; + if (!mmc_is_ascend_customized(host->parent)) + host->ios.bus_mode = MMC_BUSMODE_PUSHPULL; host->ios.bus_width = MMC_BUS_WIDTH_1; host->ios.timing = MMC_TIMING_LEGACY; host->ios.drv_type = 0; @@ -1533,7 +1538,15 @@ int mmc_set_uhs_voltage(struct mmc_host *host, u32 ocr)
cmd.opcode = SD_SWITCH_VOLTAGE; cmd.arg = 0; - cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + if (mmc_is_ascend_customized(host->parent)) + /* + * Here for using Kingsdon SD produced after 03-2015, + * We set long resp and delete CRC flag for cmd11. + */ + cmd.flags = MMC_RSP_PRESENT | MMC_RSP_OPCODE | + MMC_CMD_AC | MMC_RSP_136; + else + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(host, &cmd, 0); if (err) @@ -1651,6 +1664,8 @@ void mmc_power_up(struct mmc_host *host, u32 ocr) mmc_pwrseq_pre_power_on(host);
host->ios.vdd = fls(ocr) - 1; + if (mmc_is_ascend_customized(host->parent)) + host->ios.bus_mode = MMC_BUSMODE_PUSHPULL; host->ios.power_mode = MMC_POWER_UP; /* Set initial state and call mmc_set_ios */ mmc_set_initial_state(host); @@ -1687,6 +1702,9 @@ void mmc_power_off(struct mmc_host *host) host->ios.clock = 0; host->ios.vdd = 0;
+ if (!mmc_host_is_spi(host) && mmc_is_ascend_customized(host->parent)) + host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; + host->ios.power_mode = MMC_POWER_OFF; /* Set initial state and call mmc_set_ios */ mmc_set_initial_state(host); @@ -1720,7 +1738,7 @@ static void __mmc_release_bus(struct mmc_host *host) /* * Increase reference count of bus operator */ -static inline void mmc_bus_get(struct mmc_host *host) +void mmc_bus_get(struct mmc_host *host) { unsigned long flags;
@@ -1733,7 +1751,7 @@ static inline void mmc_bus_get(struct mmc_host *host) * Decrease reference count of bus operator and free it if * it is the last reference. */ -static inline void mmc_bus_put(struct mmc_host *host) +void mmc_bus_put(struct mmc_host *host) { unsigned long flags;
@@ -2515,9 +2533,10 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq) mmc_send_if_cond(host, host->ocr_avail);
/* Order's important: probe SDIO, then SD, then MMC */ - if (!(host->caps2 & MMC_CAP2_NO_SDIO)) - if (!mmc_attach_sdio(host)) - return 0; + if (!mmc_is_ascend_customized(host->parent)) + if (!(host->caps2 & MMC_CAP2_NO_SDIO)) + if (!mmc_attach_sdio(host)) + return 0;
if (!(host->caps2 & MMC_CAP2_NO_SD)) if (!mmc_attach_sd(host)) @@ -2604,12 +2623,18 @@ void mmc_rescan(struct work_struct *work) container_of(work, struct mmc_host, detect.work); int i;
- if (host->rescan_disable) + if (host->rescan_disable) { + if (mmc_is_ascend_customized(host->parent)) + goto out; return; + }
/* If there is a non-removable card registered, only scan once */ - if (!mmc_card_is_removable(host) && host->rescan_entered) + if (!mmc_card_is_removable(host) && host->rescan_entered) { + if (mmc_is_ascend_customized(host->parent)) + goto out; return; + } host->rescan_entered = 1;
if (host->trigger_card_event && host->ops->card_event) { @@ -2660,6 +2685,13 @@ void mmc_rescan(struct work_struct *work) for (i = 0; i < ARRAY_SIZE(freqs); i++) { if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min))) break; + else if (mmc_is_ascend_customized(host->parent)) { + if (host->index == 0) { + pr_err("%s emmc init failed,\n", __func__); + pr_err("need to reinit\n"); + continue; + } + } if (freqs[i] <= host->f_min) break; } @@ -2681,17 +2713,18 @@ void mmc_start_host(struct mmc_host *host) mmc_power_up(host, host->ocr_avail); mmc_release_host(host); } - - mmc_gpiod_request_cd_irq(host); + if (!mmc_is_ascend_customized(host->parent)) + mmc_gpiod_request_cd_irq(host); _mmc_detect_change(host, 0, false); }
void mmc_stop_host(struct mmc_host *host) { - if (host->slot.cd_irq >= 0) { - mmc_gpio_set_cd_wake(host, false); - disable_irq(host->slot.cd_irq); - } + if (!mmc_is_ascend_customized(host->parent)) + if (host->slot.cd_irq >= 0) { + mmc_gpio_set_cd_wake(host, false); + disable_irq(host->slot.cd_irq); + }
host->rescan_disable = 1; cancel_delayed_work_sync(&host->detect); diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 087ba68b29209..3c3f0ca80e241 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -32,6 +32,13 @@ struct mmc_bus_ops { int (*shutdown)(struct mmc_host *); int (*hw_reset)(struct mmc_host *); int (*sw_reset)(struct mmc_host *); +#ifdef CONFIG_ASCEND_HISI_MMC + int (*awake)(struct mmc_host *host); + int (*sleep)(struct mmc_host *host); + int (*power_save)(struct mmc_host *host); + int (*power_restore)(struct mmc_host *host); + int (*reinit_card)(struct mmc_host *host); +#endif };
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops); @@ -59,6 +66,17 @@ void mmc_power_up(struct mmc_host *host, u32 ocr); void mmc_power_off(struct mmc_host *host); void mmc_power_cycle(struct mmc_host *host, u32 ocr); void mmc_set_initial_state(struct mmc_host *host); +#ifdef CONFIG_ASCEND_HISI_MMC +void mmc_power_up_vcc(struct mmc_host *host, u32 ocr); +void mmc_power_off_vcc(struct mmc_host *host); +#else +static inline void mmc_power_up_vcc(struct mmc_host *host, u32 ocr) {} +static inline void mmc_power_off_vcc(struct mmc_host *host) {} +#endif +void mmc_set_ios(struct mmc_host *host); +void mmc_bus_get(struct mmc_host *host); +void mmc_bus_put(struct mmc_host *host); +
static inline void mmc_delay(unsigned int ms) { diff --git a/drivers/mmc/core/hisi_core_mmc.c b/drivers/mmc/core/hisi_core_mmc.c new file mode 100644 index 0000000000000..49a94374bee07 --- /dev/null +++ b/drivers/mmc/core/hisi_core_mmc.c @@ -0,0 +1,818 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/pm_runtime.h> + + +#include <linux/mmc/card.h> +#include <linux/mmc/host.h> +#include <linux/version.h> +#include <linux/mmc/mmc.h> +#include "core.h" +#include "bus.h" +#include "host.h" +#include "sdio_bus.h" + +#include "mmc_ops.h" +#include "sd_ops.h" +#include "sdio_ops.h" +#include "hisi_core_mmc.h" + + +void mmc_power_up_vcc(struct mmc_host *host, u32 ocr) +{ + if (host->ios.power_mode == MMC_POWER_UP) + return; + + host->ios.vdd = fls(ocr) - 1; + + /* + * This delay should be sufficient to allow the power supply + * to reach the minimum voltage. + */ + mmc_delay(10); + + host->ios.clock = host->ios.clock_store; + host->ios.power_mode = MMC_POWER_UP; + mmc_set_ios(host); + + /* + * This delay must be at least 74 clock sizes, or 1 ms, or the + * time required to reach a stable voltage. + */ + mmc_delay(10); + +} + + +void mmc_power_off_vcc(struct mmc_host *host) +{ + if (host->ios.power_mode == MMC_POWER_OFF) + return; + + /*store the ios.clock which will be used in mmc_power_up_vcc*/ + host->ios.clock_store = host->ios.clock; + host->ios.clock = 0; + host->ios.vdd = 0; + + host->ios.power_mode = MMC_POWER_OFF; + + mmc_set_ios(host); + + /* + * Some configurations, such as the 802.11 SDIO card in the OLPC + * XO-1.5, require a short delay after poweroff before the card + * can be successfully turned on again. + */ + mmc_delay(1); +} + + +int mmc_card_awake(struct mmc_host *host) +{ + int err = -ENOSYS; + + mmc_bus_get(host); + + if (host->bus_ops && !host->bus_dead && host->bus_ops->awake) + err = host->bus_ops->awake(host); + + mmc_bus_put(host); + + return err; +} +EXPORT_SYMBOL(mmc_card_awake); + +int mmc_card_sleep(struct mmc_host *host) +{ + int err = -ENOSYS; + + mmc_bus_get(host); + + if (host->bus_ops && !host->bus_dead && host->bus_ops->sleep) + err = host->bus_ops->sleep(host); + + mmc_bus_put(host); + + return err; +} +EXPORT_SYMBOL(mmc_card_sleep); + +int mmc_card_can_sleep(struct mmc_host *host) +{ + struct mmc_card *card = host->card; + + if (card && mmc_card_mmc(card) && card->ext_csd.rev >= 3) + return 1; + return 0; +} +EXPORT_SYMBOL(mmc_card_can_sleep); + + + +/* + * Turn the cache ON/OFF. + * Turning the cache OFF shall trigger flushing of the data + * to the non-volatile storage. + * This function should be called with host claimed + */ +int mmc_cache_ctrl(struct mmc_host *host, u8 enable) +{ + struct mmc_card *card = host->card; + unsigned int timeout; + int err = 0; + + if (!(host->caps2 & MMC_CAP2_CACHE_CTRL) || + mmc_card_is_removable(host)) + return err; + + if (card && mmc_card_mmc(card) && + (card->ext_csd.cache_size > 0)) { + enable = !!enable; + + if (card->ext_csd.cache_ctrl ^ enable) { + timeout = enable ? card->ext_csd.generic_cmd6_time : 0; + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_CACHE_CTRL, enable, timeout); + if (err) + pr_err("%s: cache %s error %d\n", + mmc_hostname(card->host), + enable ? "on" : "off", + err); + else + card->ext_csd.cache_ctrl = enable; + } + } + + return err; +} +EXPORT_SYMBOL(mmc_cache_ctrl); + +int mmc_card_sleepawake(struct mmc_host *host, int sleep) +{ + struct mmc_command cmd = {0}; + struct mmc_card *card = host->card; + int err; + unsigned long timeout; + + if (sleep) + (void)mmc_deselect_cards(host); + + cmd.opcode = MMC_SLEEP_AWAKE; + cmd.arg = card->rca << 16; + if (sleep) + cmd.arg |= 1 << 15; + + cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; + err = mmc_wait_for_cmd(host, &cmd, 0); + if (err) + return err; + + /* we use the the check busy method to reduce sr time*/ + if (host->ops->card_busy) { + timeout = jiffies + + msecs_to_jiffies(DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000)); + while (host->ops->card_busy(host)) { + /* + *Timeout if the device + *never leaves the program state. + */ + if (time_after(jiffies, timeout)) { + pr_err("%s: wait card not busy time out! %s\n", + mmc_hostname(host), __func__); + break; + } + } + } else { + /* + * If the host does not wait while the card signals busy, + * then we will have to wait the sleep/awake timeout. + * Note, we cannot use the + * SEND_STATUS command to poll the status + * because that command (and most + * others) is invalid while the card sleeps. + */ + if (!(host->caps & MMC_CAP_WAIT_WHILE_BUSY)) + mmc_delay(DIV_ROUND_UP(card->ext_csd.sa_timeout, + 10000)); + } + + if (!sleep) + err = mmc_select_card(card); + + return err; +} + +void mmc_decode_ext_csd_emmc50(struct mmc_card *card, u8 *ext_csd) +{ + card->ext_csd.raw_sleep_noti_time = + ext_csd[EXT_CSD_SLEEP_NOTIFICATION_TIME]; + if (card->ext_csd.raw_sleep_noti_time > 0 && + card->ext_csd.raw_sleep_noti_time <= 0x17) { + card->ext_csd.sleep_notification_time = + /* ms, raw_sleep_noti_time Units: 10us */ + (1 << card->ext_csd.raw_sleep_noti_time) / 100; + pr_debug("%s: support SLEEP_NOTIFICATION.", + mmc_hostname(card->host)); + pr_debug("sleep_notification_time=%d ms\n", + card->ext_csd.sleep_notification_time); + } + +#ifdef CONFIG_MMC_CQ_HCI + card->ext_csd.cmdq_support = ext_csd[EXT_CSD_CMDQ_SUPPORT] & 0x1; + if (card->ext_csd.cmdq_support) { + card->ext_csd.cmdq_depth = + (ext_csd[EXT_CSD_CMDQ_DEPTH] & 0x1F) + 1; + pr_info("%s: %s: CMDQ supported: depth: %d\n", + mmc_hostname(card->host), __func__, + card->ext_csd.cmdq_depth); + } +#endif +} + +void mmc_decode_ext_csd_emmc51(struct mmc_card *card, u8 *ext_csd) +{ + if (card->ext_csd.rev < 8) + return; + /* eMMC5.1*/ + /* strobe */ + if (ext_csd[EXT_CSD_STROBE_SUPPORT]) { + card->ext_csd.strobe_enhanced = + ext_csd[EXT_CSD_STROBE_SUPPORT]; + if (card->ext_csd.strobe_enhanced) + pr_info("%s: support STROBE_ENHANCED.\n", + mmc_hostname(card->host)); + else + pr_warn("%s: EXT_CSD_STROBE_SUPPORT bit is not set\n", + mmc_hostname(card->host)); + } + /* cache flush barrier */ + if (ext_csd[EXT_CSD_BARRIER_SUPPORT]) { + card->ext_csd.cache_flush_barrier = + ext_csd[EXT_CSD_BARRIER_SUPPORT]; + if (card->ext_csd.cache_flush_barrier) + pr_info("%s: support BARRIER_SUPPORT.\n", + mmc_hostname(card->host)); + } + /* cache flush policy */ + if (ext_csd[EXT_CSD_CACHE_FLUSH_POLICY]) { + card->ext_csd.cache_flush_policy = + ext_csd[EXT_CSD_CACHE_FLUSH_POLICY] & + EXT_CSD_CACHE_FLUSH_POLICY_FIFO; + if (card->ext_csd.cache_flush_policy) + pr_info("%s: support CACHE_FLUSH_POLICY_FIFO.\n", + mmc_hostname(card->host)); + } + /* bkops auto */ + if (ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) { + card->ext_csd.bkops_auto_en = + ext_csd[EXT_CSD_BKOPS_EN] & EXT_CSD_BKOPS_AUTO_EN; + if (!card->ext_csd.bkops_auto_en) + pr_info("%s: BKOPS_AUTO_EN bit is not set.\n", + mmc_hostname(card->host)); + } + /* rpmb 8k */ + if (ext_csd[EXT_CSD_WR_REL_PARAM] & EXT_CSD_RPMB_REL_WR_EN) + pr_info("%s: support RPMB 8K Bytes read/write.\n", + mmc_hostname(card->host)); + + pr_info("%s: EXT_CSD revision 0x%02x pre_eol_info = 0x%02X\n", + mmc_hostname(card->host), card->ext_csd.rev, + card->ext_csd.pre_eol_info); + pr_info("device_life_time_est_typ_a = 0x%02X\n", + card->ext_csd.device_life_time_est_typ_a); + pr_info("device_life_time_est_typ_b = 0x%02X.\n", + card->ext_csd.device_life_time_est_typ_b); +} + + +/* enable feature for emmc5.0 or later contains cmdq,barrier and bkops */ +int mmc_init_card_enable_feature(struct mmc_card *card) +{ + int err = 0; + struct mmc_host *host = card->host; + + /* + * Enable command queue (if supported) + */ + if (card->ext_csd.cmdq_support) { + if (host->caps2 & MMC_CAP2_CMD_QUEUE) { + card->ext_csd.cmdq_mode_en = 1; + pr_info("%s: cmdq_mode_en=%d\n", + __func__, card->ext_csd.cmdq_mode_en); + } else { + card->ext_csd.cmdq_mode_en = 0; + } + } + + /* + * Enable Barrier feature (if supported) + */ + if (card->ext_csd.cache_flush_barrier && + (host->caps2 & MMC_CAP2_CACHE_FLUSH_BARRIER)) { + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BARRIER_CTRL, 1, + card->ext_csd.generic_cmd6_time); + if (err && err != -EBADMSG) + return err; + if (err) { + pr_warn("%s: Enabling cache flush barrier failed\n", + mmc_hostname(card->host)); + err = 0; + card->ext_csd.cache_flush_barrier_en = 0; + } else { + card->ext_csd.cache_flush_barrier_en = 1; + pr_info("%s: cache_flush_barrier_en=%d\n", + __func__, card->ext_csd.cache_flush_barrier_en); + } + } + + /* + * Enable BKOPS AUTO feature (if supported) + */ + if ((host->caps2 & MMC_CAP2_BKOPS_AUTO_CTRL) && + (host->pm_flags & MMC_PM_KEEP_POWER) && + card->ext_csd.bkops && (card->ext_csd.rev >= 8)) { +#ifdef CONFIG_HISI_MMC_MANUAL_BKOPS + if (card->cid.manfid == CID_MANFID_MICRON) { + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BKOPS_EN, EXT_CSD_BKOPS_MANUAL_EN | + EXT_CSD_BKOPS_AUTO_EN, + card->ext_csd.generic_cmd6_time); + } else { +#endif + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BKOPS_EN, EXT_CSD_BKOPS_AUTO_EN, + card->ext_csd.generic_cmd6_time); +#ifdef CONFIG_HISI_MMC_MANUAL_BKOPS + } +#endif + if (err && err != -EBADMSG) + return err; + if (err) { + pr_warn("%s: BKOPS_AUTO_EN failed\n", + mmc_hostname(card->host)); + err = 0; + card->ext_csd.bkops_auto_en = 0; +#ifdef CONFIG_HISI_MMC_MANUAL_BKOPS + card->ext_csd.man_bkops_en = 0; +#endif + } else { + card->ext_csd.bkops_auto_en = 1; +#ifdef CONFIG_HISI_MMC_MANUAL_BKOPS + if (card->cid.manfid == CID_MANFID_MICRON) + card->ext_csd.man_bkops_en = 1; +#endif + pr_info("%s: support BKOPS_AUTO_EN, bkops_auto_en=%d\n", + __func__, card->ext_csd.bkops_auto_en); + } + } + + return err; +} + +/* suspend operation on hisi platform, conclude disable cmdq + * swich to normal partion, stop bkops and so on + */ +int mmc_suspend_hisi_operation(struct mmc_host *host) +{ + unsigned long timeout = 8000; + u8 part_config; + int err = 0; + + if (mmc_card_cmdq(host->card)) { + /* wait for cmdq req handle done.*/ + while (host->cmdq_ctx.active_reqs) { + if (timeout == 0) { + pr_err("%s: wait reqs timeout !\n", __func__); + err = -ETIMEDOUT; + goto err_handle; + } + timeout--; + mdelay(1); + } + + if (host->cmdq_ops->disable) { + err = host->cmdq_ops->disable(host, true); + if (err) { + pr_err("%s: cmdq disable failed.\n", __func__); + goto err_handle; + } + } + + err = mmc_switch(host->card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_CMDQ_MODE, 0, + host->card->ext_csd.generic_cmd6_time); + + if (err) { + pr_err("%s: disable device cmdq failed.\n", __func__); + pr_err("active_reqs is %lu,cmdq status is %d.\n", + host->cmdq_ctx.active_reqs, + mmc_card_cmdq(host->card)); + /* we re-enable the device cmdq feature just in case; + * For example: + * 1.rpmb_access disable the device cmdq feature; + * 2.disable device cmdq feqture failed + * here in _mmc_suspend; + * 3.if we don't re-enable device cmdq feature, + * next normal req + * will fail; + */ + (void)mmc_switch(host->card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_CMDQ_MODE, 1, + host->card->ext_csd.generic_cmd6_time); + + if (host->cmdq_ops->enable) { + if (host->cmdq_ops->enable(host)) + pr_err("%s %d: cmdq enable failed.\n", + __func__, __LINE__); + } + goto err_handle; + } + + mmc_card_clr_cmdq(host->card); + } + + /* + * Ensure eMMC user default partition is enabled,because cmd5 will be + * a illegal cmd when device is in rpmb partition. + */ + if (host->card->ext_csd.part_config & EXT_CSD_PART_CONFIG_ACC_MASK) { + pr_info("%s need to swich to default partition, current part_config is %d\n", + __func__, host->card->ext_csd.part_config); + part_config = host->card->ext_csd.part_config; + part_config &= ~EXT_CSD_PART_CONFIG_ACC_MASK; + err = mmc_switch(host->card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_PART_CONFIG, + part_config, + host->card->ext_csd.part_time); + if (err) { + pr_err("%s swich to default partition failed, err is %d\n", + __func__, err); + goto err_handle; + } + host->card->ext_csd.part_config = part_config; + } + + if (mmc_card_doing_bkops(host->card) && mmc_stop_bkops(host->card)) + err = -1; +err_handle: + return err; +} +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-variable" +int hisi_mmc_reset(struct mmc_host *host) +{ + int ret; + struct mmc_card *card = host->card; + + pr_info("%s enter\n", __func__); + + if (!host->ops->hw_reset) + return -EOPNOTSUPP; + + host->ops->hw_reset(host); + + mmc_power_off(host); + mdelay(200); + mmc_power_up(host, card->ocr); + + if (host->bus_ops->power_restore == NULL) + /* adapt miniV2, lp API in future */ + ret = host->bus_ops->reinit_card(host); + else + ret = host->bus_ops->power_restore(host); + pr_info("%s exit,ret=%d\n", __func__, ret); + return ret; +} +#pragma GCC diagnostic pop + +/** + * __mmc_send_status_direct - send the cmd 13 to device and get the device + * status;it can be called when the irq system cannot work. + */ +static int __mmc_send_status_direct(struct mmc_card *card, u32 *status, + bool ignore_crc) +{ + int err; + struct mmc_command cmd = {0}; + struct mmc_request mrq = {NULL}; + struct mmc_host *host; + + WARN_ON(!card); + WARN_ON(!card->host); + + host = card->host; + cmd.opcode = MMC_SEND_STATUS; + if (!mmc_host_is_spi(card->host)) + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC; + if (ignore_crc) + cmd.flags &= ~MMC_RSP_CRC; + + cmd.data = NULL; + cmd.busy_timeout = MMC_TIMEOUT_INVALID; + mrq.cmd = &cmd; + memset(cmd.resp, 0, sizeof(cmd.resp)); + cmd.retries = 0; + mrq.cmd->error = 0; + mrq.cmd->mrq = &mrq; + + if (host->ops->send_cmd_direct) { + err = host->ops->send_cmd_direct(host, &mrq); + if (err) + return err; + } + + if (status) + *status = cmd.resp[0]; + + return 0; +} + +/** + * mmc_switch_irq_safe - mmc_switch func for hisi platform; + * it can be called when the irq system cannot work. + */ +int mmc_switch_irq_safe(struct mmc_card *card, u8 set, u8 index, u8 value) +{ + struct mmc_host *host; + int err; + struct mmc_command cmd = {0}; + struct mmc_request mrq = {NULL}; + u32 status = 0; + u32 cmd_retries = 10; + + WARN_ON(!card); + WARN_ON(!card->host); + host = card->host; + + cmd.opcode = MMC_SWITCH; + cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | + (index << 16) | + (value << 8) | + set; + cmd.flags = MMC_CMD_AC; + cmd.flags |= MMC_RSP_SPI_R1 | MMC_RSP_R1; + + if (index == EXT_CSD_SANITIZE_START) + cmd.sanitize_busy = true; + + cmd.data = NULL; + cmd.busy_timeout = MMC_TIMEOUT_INVALID; + mrq.cmd = &cmd; + memset(cmd.resp, 0, sizeof(cmd.resp)); + cmd.retries = 0; + mrq.cmd->error = 0; + mrq.cmd->mrq = &mrq; + + if (host->ops->send_cmd_direct) { + /* this func will check busy after data send */ + err = host->ops->send_cmd_direct(host, &mrq); + if (err) + return err; + } + do { + err = __mmc_send_status_direct(card, &status, (bool)false); + if (err) + return err; + + /* only check the response status here so we olny send + * cmd13 10 times + */ + if (--cmd_retries == 0) { + pr_err("%s: Card stuck in programming state! %s\n", + mmc_hostname(host), __func__); + return -ETIMEDOUT; + } + } while (R1_CURRENT_STATE(status) == R1_STATE_PRG); + + if (status & 0xFDFFA000) + pr_warn("%s: unexpected status %#x after switch\n", + mmc_hostname(host), status); + if (status & R1_SWITCH_ERROR) + return -EBADMSG; + + return 0; +} + +/** + * mmc_try_claim_host - try exclusively to claim a host + * @host: mmc host to claim + * + * Returns %1 if the host is claimed, %0 otherwise. + */ +int mmc_try_claim_host(struct mmc_host *host) +{ + int claimed_host = 0; + unsigned long flags; + bool pm = false; + + if (!spin_trylock_irqsave(&host->lock, flags)) + return 0; + + if (!host->claimed || host->claimer == (struct mmc_ctx *)current) { + host->claimed = 1; + host->claimer->task = current; + host->claim_cnt += 1; + claimed_host = 1; + if (host->claim_cnt == 1) + pm = true; + } + spin_unlock_irqrestore(&host->lock, flags); + + if (pm) + pm_runtime_get_sync(mmc_dev(host)); + + return claimed_host; +} +EXPORT_SYMBOL(mmc_try_claim_host); +/* + * This is a helper function, which fetches a runtime pm reference for the + * card device and also claims the host. + */ +int mmc_get_card_hisi(struct mmc_card *card, bool use_irq) +{ + int claimed = 0; + unsigned int try_count = 200000; + int ret = 0; + + do { + claimed = mmc_try_claim_host(card->host); + if (claimed) + break; + udelay(10); + } while (--try_count > 0); + if (!claimed) { + pr_err("%s try to claim host failed\n", __func__); + ret = -EIO; + goto out; + } + + /* mmc has suspended,no more operation */ + if (mmc_card_suspended(card)) { + pr_err("%s mmc has suspended;\n", __func__); + ret = -EHOSTDOWN; + goto release_host; + } + +#ifdef CONFIG_MMC_CQ_HCI + if (card->ext_csd.cmdq_mode_en && cmdq_is_reset(card->host)) { + pr_err("%s cmdq is in reset process\n", __func__); + ret = -EHOSTUNREACH; + goto release_host; + } + if (use_irq) + ret = mmc_blk_cmdq_hangup(card); + else + ret = mmc_blk_cmdq_halt(card); + + if (ret) { + pr_err("%s: cmdq hangup|halt err.\n", __func__); + ret = -EIO; + goto release_host; + } + + if (mmc_card_doing_bkops(card) && mmc_stop_bkops(card)) + pr_err("%s: mmc_stop_bkops failed!\n", __func__); +#endif /* CONFIG_MMC_CQ_HCI */ + return ret; +release_host: + mmc_release_host(card->host); +out: + return ret; +} + +void mmc_put_card_irq_safe(struct mmc_card *card) +{ +#ifdef CONFIG_MMC_CQ_HCI + mmc_blk_cmdq_dishalt(card); +#endif + mmc_release_host(card->host); +} + + +/* + * Flush the cache to the non-volatile storage. + */ +int mmc_flush_cache_direct(struct mmc_card *card) +{ + struct mmc_host *host = card->host; + int err = 0; + + if (!(host->caps2 & MMC_CAP2_CACHE_CTRL)) + return err; + + if (mmc_card_mmc(card) && + (card->ext_csd.cache_size > 0) && + (card->ext_csd.cache_ctrl & 1)) { + + err = mmc_switch_irq_safe(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_FLUSH_CACHE, 1); + if (err) + pr_err("%s: cache flush error %d\n", + mmc_hostname(card->host), err); + else + pr_info("%s success!\n", __func__); + } + + return err; +} + + +/*********************sd ops begin**********************/ +static int mmc_do_sd_reset(struct mmc_host *host) +{ + struct mmc_card *card = host->card; + + if (!host->bus_ops->power_restore) + return -EOPNOTSUPP; + + if (!card) + return -EINVAL; + + /* hw_reset for ip reset */ + if (host->ops->hw_reset) + host->ops->hw_reset(host); + + /* Only for K930/920 SD slow down clk*/ + if (host->ops->slowdown_clk) + host->ops->slowdown_clk(host, host->ios.timing); + + mmc_power_off(host); + mmc_set_clock(host, host->f_init); + /* Wait at least 200 ms */ + mmc_delay(200); + mmc_power_up(host, host->card->ocr); + (void)mmc_select_voltage(host, host->card->ocr); + + return host->bus_ops->power_restore(host); +} + +int mmc_sd_reset(struct mmc_host *host) +{ + return mmc_do_sd_reset(host); +} +EXPORT_SYMBOL(mmc_sd_reset); + +/* low speed card, set frequency to 25M */ +void mmc_select_new_sd(struct mmc_card *card) +{ + unsigned int max_dtr; + + mmc_set_timing(card->host, MMC_TIMING_NEW_SD); + max_dtr = (unsigned int)-1; + if (max_dtr > card->csd.max_dtr) + max_dtr = card->csd.max_dtr; + mmc_set_clock(card->host, max_dtr); +} + +/*********************wifi ops begin**********************/ +int mmc_power_save_host_for_wifi(struct mmc_host *host) +{ + int ret = 0; + +#ifdef CONFIG_MMC_DEBUG + pr_info("%s: %s: powering down\n", mmc_hostname(host), __func__); +#endif + + mmc_bus_get(host); + + if ((!host->ops || !host->ops->set_ios) || + (host->bus_dead)) { + mmc_bus_put(host); + return -EINVAL; + } + + mmc_bus_put(host); + + mmc_power_off(host); + + return ret; +} +EXPORT_SYMBOL(mmc_power_save_host_for_wifi); + +int mmc_power_restore_host_for_wifi(struct mmc_host *host) +{ + int ret = 0; + +#ifdef CONFIG_MMC_DEBUG + pr_info("%s: %s: powering up\n", mmc_hostname(host), __func__); +#endif + + mmc_bus_get(host); + + if ((!host->ops || !host->ops->set_ios) || + (host->bus_dead)) { + mmc_bus_put(host); + return -EINVAL; + } + + mmc_power_up(host, host->card->ocr); + + mmc_bus_put(host); + + return ret; +} +EXPORT_SYMBOL(mmc_power_restore_host_for_wifi); diff --git a/drivers/mmc/core/hisi_core_mmc.h b/drivers/mmc/core/hisi_core_mmc.h new file mode 100644 index 0000000000000..f662e534b9bc7 --- /dev/null +++ b/drivers/mmc/core/hisi_core_mmc.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _HISI_CORE_MMC_H +#define _HISI_CORE_MMC_H + +struct mmc_card; + +#ifdef CONFIG_ASCEND_HISI_MMC + +#ifdef CONFIG_MMC_CQ_HCI +extern void mmc_blk_cmdq_dishalt(struct mmc_card *card); +extern int cmdq_is_reset(struct mmc_host *host); +extern int mmc_blk_cmdq_halt(struct mmc_card *card); +#endif + +void mmc_decode_ext_csd_emmc50(struct mmc_card *card, u8 *ext_csd); +void mmc_decode_ext_csd_emmc51(struct mmc_card *card, u8 *ext_csd); + +void mmc_select_new_sd(struct mmc_card *card); +int mmc_init_card_enable_feature(struct mmc_card *card); +int mmc_suspend_hisi_operation(struct mmc_host *host); +int mmc_card_sleepawake(struct mmc_host *host, int sleep); +int mmc_sd_reset(struct mmc_host *host); + +int hisi_mmc_reset(struct mmc_host *host); +#else +static inline void mmc_decode_ext_csd_emmc51(struct mmc_card *card, u8 *ext_csd) +{ + +} +static inline void mmc_decode_ext_csd_emmc50(struct mmc_card *card, u8 *ext_csd) +{ + +} +static inline void mmc_select_new_sd(struct mmc_card *card) {} +static inline int mmc_init_card_enable_feature(struct mmc_card *card) +{ + return 0; +} +static inline int mmc_suspend_hisi_operation(struct mmc_host *host) +{ + return 0; +} +static inline int mmc_card_sleepawake(struct mmc_host *host, int sleep) +{ + return 0; +} +static inline int mmc_sd_reset(struct mmc_host *host) +{ + return 0; +} +#endif /* CONFIG_ASCEND_HISI_MMC */ +#endif /* _HISI_CORE_MMC_H */ diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 8ef330027b134..45899066f7ea2 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -128,6 +128,26 @@ struct mmc_ext_csd {
unsigned int feature_support; #define MMC_DISCARD_FEATURE BIT(0) /* CMD38 feature */ +#ifdef CONFIG_ASCEND_HISI_MMC + unsigned int sleep_notification_time; /* Units: ms */ + u8 raw_vendor_feature_support; /* 124 */ + u8 raw_wr_rel_param;/* 166 */ + u8 raw_rpmb_region1_size_mult; /* 180 */ + u8 raw_rpmb_region2_size_mult; /* 182 */ + u8 raw_rpmb_region3_size_mult; /* 186 */ + u8 raw_rpmb_region4_size_mult; /* 188 */ + u8 raw_sleep_noti_time; /* 216 */ + u8 cmdq_mode_en; /* 15 */ + /* 184 enhanced strobe support */ + u8 strobe_enhanced; + u8 strobe_enhanced_en; + /* 486 barrier support */ + u8 cache_flush_barrier; + u8 cache_flush_barrier_en; /* barrier enable */ + u8 cache_flush_policy; /* 240 */ + /* 163 bit(1) background op auto enable */ + u8 bkops_auto_en; +#endif };
struct sd_scr { @@ -145,6 +165,9 @@ struct sd_ssr { unsigned int au; /* In sectors */ unsigned int erase_timeout; /* In milliseconds */ unsigned int erase_offset; /* In milliseconds */ +#ifdef CONFIG_ASCEND_HISI_MMC + unsigned int speed_class; +#endif };
struct sd_switch_caps { @@ -252,6 +275,18 @@ struct mmc_card { #define MMC_TYPE_SDIO 2 /* SDIO card */ #define MMC_TYPE_SD_COMBO 3 /* SD combo (IO+mem) card */ unsigned int state; /* (our) card state */ +#ifdef CONFIG_ASCEND_HISI_MMC +#define MMC_STATE_PRESENT (1<<0) /* present in sysfs */ +#define MMC_STATE_READONLY (1<<1) /* card is read-only */ +#define MMC_STATE_BLOCKADDR (1<<2) /* card uses block-addressing */ +#define MMC_CARD_SDXC (1<<3) /* card is SDXC */ +#define MMC_CARD_REMOVED (1<<4) /* card has been removed */ +#define MMC_STATE_DOING_BKOPS (1<<5) /* card is doing BKOPS */ +/* card is in ultra high speed mode */ +#define MMC_STATE_ULTRAHIGHSPEED (1<<14) +#define MMC_STATE_SUSPENDED (1<<6) /* card is suspended */ +#define MMC_STATE_CMDQ (1<<7) /* card is in cmd queue mode */ +#endif unsigned int quirks; /* card quirks */ unsigned int quirk_max_rate; /* max rate set by quirks */ #define MMC_QUIRK_LENIENT_FN0 (1<<0) /* allow SDIO FN0 writes outside of the VS CCCR range */ @@ -259,6 +294,10 @@ struct mmc_card { /* for byte mode */ #define MMC_QUIRK_NONSTD_SDIO (1<<2) /* non-standard SDIO card attached */ /* (missing CIA registers) */ +#ifdef CONFIG_ASCEND_HISI_MMC +/* clock gating the sdio bus will make card fail */ +#define MMC_QUIRK_BROKEN_CLK_GATING (1<<3) +#endif #define MMC_QUIRK_NONSTD_FUNC_IF (1<<4) /* SDIO card has nonstd function interfaces */ #define MMC_QUIRK_DISABLE_CD (1<<5) /* disconnect CD/DAT[3] resistor */ #define MMC_QUIRK_INAND_CMD38 (1<<6) /* iNAND devices have broken CMD38 */ @@ -322,4 +361,70 @@ bool mmc_card_is_blockaddr(struct mmc_card *card); #define mmc_card_sd(c) ((c)->type == MMC_TYPE_SD) #define mmc_card_sdio(c) ((c)->type == MMC_TYPE_SDIO)
+#ifdef CONFIG_ASCEND_HISI_MMC +#define MMC_CARD_CMDQ_BLK_SIZE 512 +#define EXT_CSD_PRE_EOL_INFO_NORMAL 0x01 +#define EXT_CSD_PRE_EOL_INFO_WARNING 0x02 +#define EXT_CSD_PRE_EOL_INFO_URGENT 0x03 + +#define mmc_card_present(c) ((c)->state & MMC_STATE_PRESENT) +#define mmc_card_readonly(c) ((c)->state & MMC_STATE_READONLY) +#define mmc_card_blockaddr(c) ((c)->state & MMC_STATE_BLOCKADDR) +#define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC) +#define mmc_card_removed(c) ((c) && ((c)->state & MMC_CARD_REMOVED)) +#define mmc_card_doing_bkops(c) ((c)->state & MMC_STATE_DOING_BKOPS) + +#define mmc_sd_card_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED) + +#define mmc_card_suspended(c) ((c)->state & MMC_STATE_SUSPENDED) + +#define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT) +#define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY) +#define mmc_card_set_highspeed(c) ((c)->state |= MMC_STATE_HIGHSPEED) +#define mmc_card_set_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR) +#define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC) +#define mmc_card_set_removed(c) ((c)->state |= MMC_CARD_REMOVED) +#define mmc_card_set_doing_bkops(c) ((c)->state |= MMC_STATE_DOING_BKOPS) +#define mmc_card_clr_doing_bkops(c) ((c)->state &= ~MMC_STATE_DOING_BKOPS) +#define mmc_card_set_suspended(c) ((c)->state |= MMC_STATE_SUSPENDED) +#define mmc_card_clr_suspended(c) ((c)->state &= ~MMC_STATE_SUSPENDED) +#define mmc_card_cmdq(c) ((c)->state & MMC_STATE_CMDQ) +#define mmc_card_set_cmdq(c) ((c)->state |= MMC_STATE_CMDQ) +#define mmc_card_clr_cmdq(c) ((c)->state &= ~MMC_STATE_CMDQ) +#define mmc_sd_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED) + +#define mmc_card_name(c) ((c)->cid.prod_name) +#define mmc_card_id(c) (dev_name(&(c)->dev)) + +#define mmc_dev_to_card(d) container_of(d, struct mmc_card, dev) + +/* + * The world is not perfect and supplies us with broken mmc/sdio devices. + * For at least some of these bugs we need a work-around. + */ +#define CID_MANFID_SANDISK 0x2 +#define CID_MANFID_TOSHIBA 0x11 +#define CID_MANFID_MICRON 0x13 +#define CID_MANFID_SAMSUNG 0x15 +#define CID_MANFID_KINGSTON 0x70 +#define CID_MANFID_HYNIX 0x90 + +#define CID_MANFID_ANY (-1u) +#define CID_OEMID_ANY ((unsigned short) -1) +#define CID_NAME_ANY (NULL) + +#define END_FIXUP { NULL } + +#define cid_rev(hwrev, fwrev, year, month) \ + (((u64) hwrev) << 40 | \ + ((u64) fwrev) << 32 | \ + ((u64) year) << 16 | \ + ((u64) month)) + +#define cid_rev_card(card) \ + cid_rev(card->cid.hwrev, \ + card->cid.fwrev, \ + card->cid.year, \ + card->cid.month) +#endif /* CONFIG_ASCEND_HISI_MMC */ #endif /* LINUX_MMC_CARD_H */ diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index 134a6483347a1..c1283b9739c83 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -108,7 +108,9 @@ struct mmc_command { * ENOMEDIUM Host can determine that the slot is empty and is * actively failing requests */ - +#ifdef CONFIG_ASCEND_HISI_MMC +#define MMC_TIMEOUT_INVALID (0xFFFFFFFF) +#endif unsigned int busy_timeout; /* busy detect timeout in ms */ /* Set this flag only for blocking sanitize request */ bool sanitize_busy; diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index 897a87c4c8275..ab082f65a034e 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -387,6 +387,52 @@ static inline bool mmc_op_multi(u32 opcode)
#define EXT_CSD_PACKED_EVENT_EN BIT(3)
+#ifdef CONFIG_ASCEND_HISI_MMC +#define MMC_FLUSH_REQ_TIMEOUT_MS 90000 /* msec */ + +/* EXT_CSD field definitions for hisi mmc */ +#define EXT_CSD_CMDQ_MODE 15 /* R/W */ +#define EXT_CSD_FFU_STATUS 26 /* R */ +#define EXT_CSD_MODE_OPERATION_CODES 29 /* W */ +#define EXT_CSD_MODE_CONFIG 30 /* R/W */ +#define EXT_CSD_BARRIER_CTRL 31 /* W */ +#define EXT_CSD_VENDOR_FEATURE_SUPPORT 124 /* Reserved R */ +#define EXT_CSD_USER_WP 171 /* R/W */ +#define EXT_CSD_RPMB_REGION1_SIZE_MULT 180 /* Reserved R/W */ +#define EXT_CSD_RPMB_REGION2_SIZE_MULT 182 /* Reserved R/W */ +#define EXT_CSD_RPMB_REGION3_SIZE_MULT 186 /* Reserved R/W */ +#define EXT_CSD_RPMB_REGION4_SIZE_MULT 188 /* Reserved R/W */ +#define EXT_CSD_SLEEP_NOTIFICATION_TIME 216 /* Ro */ +#define EXT_CSD_CACHE_FLUSH_POLICY 240 /* RO */ +#define EXT_CSD_HEALTH_REPORT 270 /* RO 32 bits*/ +#define EXT_CSD_NUM_OF_FW_SEC_PROG 302 /* RO */ +#define EXT_CSD_BARRIER_SUPPORT 486 /* RO */ +#define EXT_CSD_FFU_ARG 487 /* RO, 4 bytes */ +#define EXT_CSD_OPERATION_CODE_TIMEOUT 491 /* RO */ +#define EXT_CSD_FFU_FEATURES 492 /* RO */ +#define EXT_CSD_RPMB_REL_WR_EN (1<<4) +#define EXT_CSD_SLEEP_NOTIFICATION 4 + +#define EXT_CSD_TIMING_BC 0 /* Backwards compatility */ +#define EXT_CSD_TIMING_HS 1 /* High speed */ +#define EXT_CSD_TIMING_HS200 2 /* HS200 */ +#define EXT_CSD_TIMING_HS400 3 /* HS400 */ + +#define EXT_CSD_DRVIER_STRENGTH_SHIFT 4 /*Hight 4bit of EXT_CSD_HS_TIMING*/ +#define EXT_CSD_DRVIER_STRENGTH_50 0x0 +#define EXT_CSD_DRVIER_STRENGTH_33 0x1 +#define EXT_CSD_DRVIER_STRENGTH_66 0x2 +#define EXT_CSD_DRVIER_STRENGTH_100 0x3 +#define EXT_CSD_DRVIER_STRENGTH_40 0x4 + +#define EXT_CSD_STROBE_ENHANCED BIT(7) +#define EXT_CSD_TRIGGER_CACHE_FLUSH BIT(0) +#define EXT_CSD_SET_CACHE_FLUSH_BARRIER BIT(1) +#define EXT_CSD_CACHE_FLUSH_POLICY_FIFO BIT(0) +#define EXT_CSD_BKOPS_MANUAL_EN BIT(0) +#define EXT_CSD_BKOPS_AUTO_EN BIT(1) +#endif /* CONFIG_ASCEND_HISI_MMC */ + /* * EXCEPTION_EVENT_STATUS field */