From: zhangguijiang zhangguijiang@huawei.com
ascend inclusion category: feature feature: Ascend emmc adaption bugzilla: https://gitee.com/openeuler/kernel/issues/I4F4LL CVE: NA
--------------------
To identify Ascend HiSilicon emmc chip, we add a customized property to dts. In this patch we add an interface to read this property. At the same time, we provided a switch, which is CONFIG_ASCEND_HISI_MMC, for you to get rid of our modifications.
Signed-off-by: zhangguijiang zhangguijiang@huawei.com Reviewed-by: Ding Tianhong dingtianhong@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- drivers/mmc/Kconfig | 10 ++++ drivers/mmc/core/host.c | 43 ++++++++++++--- include/linux/mmc/host.h | 115 +++++++++++++++++++++++++++++++++++++++ include/linux/mmc/pm.h | 1 + 4 files changed, 162 insertions(+), 7 deletions(-)
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index ec21388311db2..8b29ecadd1862 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -12,6 +12,16 @@ menuconfig MMC If you want MMC/SD/SDIO support, you should say Y here and also to your specific host controller driver.
+config ASCEND_HISI_MMC + bool "Ascend HiSilicon MMC card support" + depends on MMC + default n + help + This selects for Hisilicon SoC specific extensions to the + Synopsys DesignWare Memory Card Interface driver. + You should select this option if you want mmc support on + Ascend platform. + if MMC
source "drivers/mmc/core/Kconfig" diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index dd1c14d8f6863..b29ee31e7865e 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -348,6 +348,11 @@ int mmc_of_parse(struct mmc_host *host)
EXPORT_SYMBOL(mmc_of_parse);
+static inline int mmc_is_ascend_hi_mci_1(struct device *dev) +{ + return !strncmp(dev_name(dev), "hi_mci.1", strlen("hi_mci.1")); +} + /** * mmc_alloc_host - initialise the per-host structure. * @extra: sizeof private data structure @@ -374,7 +379,10 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) }
host->index = err; - + if (mmc_is_ascend_customized(dev)) { + if (mmc_is_ascend_hi_mci_1(dev)) + host->index = 1; + } dev_set_name(&host->class_dev, "mmc%d", host->index);
host->parent = dev; @@ -383,10 +391,11 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) device_initialize(&host->class_dev); device_enable_async_suspend(&host->class_dev);
- if (mmc_gpio_alloc(host)) { - put_device(&host->class_dev); - return NULL; - } + if (!mmc_is_ascend_customized(host->parent)) + if (mmc_gpio_alloc(host)) { + put_device(&host->class_dev); + return NULL; + }
spin_lock_init(&host->lock); init_waitqueue_head(&host->wq); @@ -439,7 +448,9 @@ int mmc_add_host(struct mmc_host *host) #endif
mmc_start_host(host); - mmc_register_pm_notifier(host); + if (!mmc_is_ascend_customized(host->parent) || + !(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY)) + mmc_register_pm_notifier(host);
return 0; } @@ -456,7 +467,9 @@ EXPORT_SYMBOL(mmc_add_host); */ void mmc_remove_host(struct mmc_host *host) { - mmc_unregister_pm_notifier(host); + if (!mmc_is_ascend_customized(host->parent) || + !(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY)) + mmc_unregister_pm_notifier(host); mmc_stop_host(host);
#ifdef CONFIG_DEBUG_FS @@ -483,3 +496,19 @@ void mmc_free_host(struct mmc_host *host) }
EXPORT_SYMBOL(mmc_free_host); + + +int mmc_is_ascend_customized(struct device *dev) +{ +#ifdef CONFIG_ASCEND_HISI_MMC + static int is_ascend_customized = -1; + + if (is_ascend_customized == -1) + is_ascend_customized = ((dev == NULL) ? 0 : + of_find_property(dev->of_node, "customized", NULL) != NULL); + return is_ascend_customized; +#else + return 0; +#endif +} +EXPORT_SYMBOL(mmc_is_ascend_customized); diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 7e8e5b20e82b0..2cd5a73ab12a2 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -19,6 +19,9 @@ #include <linux/mmc/pm.h> #include <linux/dma-direction.h>
+#include <linux/jiffies.h> +#include <linux/version.h> + struct mmc_ios { unsigned int clock; /* clock rate */ unsigned short vdd; @@ -63,6 +66,7 @@ struct mmc_ios { #define MMC_TIMING_MMC_DDR52 8 #define MMC_TIMING_MMC_HS200 9 #define MMC_TIMING_MMC_HS400 10 +#define MMC_TIMING_NEW_SD MMC_TIMING_UHS_SDR12
unsigned char signal_voltage; /* signalling voltage (1.8V or 3.3V) */
@@ -78,7 +82,25 @@ struct mmc_ios { #define MMC_SET_DRIVER_TYPE_D 3
bool enhanced_strobe; /* hs400es selection */ +#ifdef CONFIG_ASCEND_HISI_MMC + unsigned int clock_store; /*store the clock before power off*/ +#endif +}; + +#ifdef CONFIG_ASCEND_HISI_MMC +struct mmc_cmdq_host_ops { + int (*enable)(struct mmc_host *mmc); + int (*disable)(struct mmc_host *mmc, bool soft); + int (*restore_irqs)(struct mmc_host *mmc); + int (*request)(struct mmc_host *mmc, struct mmc_request *mrq); + int (*halt)(struct mmc_host *mmc, bool halt); + void (*post_req)(struct mmc_host *mmc, struct mmc_request *mrq, + int err); + void (*disable_immediately)(struct mmc_host *mmc); + int (*clear_and_halt)(struct mmc_host *mmc); }; +#endif +
struct mmc_host;
@@ -168,6 +190,12 @@ struct mmc_host_ops { */ int (*multi_io_quirk)(struct mmc_card *card, unsigned int direction, int blk_size); +#ifdef CONFIG_ASCEND_HISI_MMC + /* Slow down clk for ascend chip SD cards */ + void (*slowdown_clk)(struct mmc_host *host, int timing); + int (*enable_enhanced_strobe)(struct mmc_host *host); + int (*send_cmd_direct)(struct mmc_host *host, struct mmc_request *mrq); +#endif };
struct mmc_cqe_ops { @@ -255,6 +283,30 @@ struct mmc_context_info { wait_queue_head_t wait; };
+#ifdef CONFIG_ASCEND_HISI_MMC +/** + * mmc_cmdq_context_info - describes the contexts of cmdq + * @active_reqs requests being processed + * @active_dcmd dcmd in progress, don't issue any + * more dcmd requests + * @rpmb_in_wait do not pull any more reqs till rpmb is handled + * @cmdq_state state of cmdq engine + * @req_starved completion should invoke the request_fn since + * no tags were available + * @cmdq_ctx_lock acquire this before accessing this structure + */ +struct mmc_cmdq_context_info { + unsigned long active_reqs; /* in-flight requests */ + bool active_dcmd; + bool rpmb_in_wait; + unsigned long curr_state; + + /* no free tag available */ + unsigned long req_starved; + spinlock_t cmdq_ctx_lock; +}; +#endif + struct regulator; struct mmc_pwrseq;
@@ -328,6 +380,9 @@ struct mmc_host { #define MMC_CAP_UHS_SDR50 (1 << 18) /* Host supports UHS SDR50 mode */ #define MMC_CAP_UHS_SDR104 (1 << 19) /* Host supports UHS SDR104 mode */ #define MMC_CAP_UHS_DDR50 (1 << 20) /* Host supports UHS DDR50 mode */ +#ifdef CONFIG_ASCEND_HISI_MMC +#define MMC_CAP_RUNTIME_RESUME (1 << 20) /* Resume at runtime_resume. */ +#endif #define MMC_CAP_UHS (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | \ MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | \ MMC_CAP_UHS_DDR50) @@ -368,6 +423,34 @@ struct mmc_host { #define MMC_CAP2_CQE (1 << 23) /* Has eMMC command queue engine */ #define MMC_CAP2_CQE_DCMD (1 << 24) /* CQE can issue a direct command */ #define MMC_CAP2_AVOID_3_3V (1 << 25) /* Host must negotiate down from 3.3V */ +#ifdef CONFIG_ASCEND_HISI_MMC +#define MMC_CAP2_CACHE_CTRL (1 << 1) /* Allow cache control */ +#define MMC_CAP2_NO_MULTI_READ (1 << 3) /* Multiblock read don't work */ +#define MMC_CAP2_NO_SLEEP_CMD (1 << 4) /* Don't allow sleep command */ +#define MMC_CAP2_BROKEN_VOLTAGE (1 << 7) /* Use the broken voltage */ +#define MMC_CAP2_DETECT_ON_ERR (1 << 8) /* I/O err check card removal */ +#define MMC_CAP2_HC_ERASE_SZ (1 << 9) /* High-capacity erase size */ +#define MMC_CAP2_PACKED_RD (1 << 12) /* Allow packed read */ +#define MMC_CAP2_PACKED_WR (1 << 13) /* Allow packed write */ +#define MMC_CAP2_PACKED_CMD (MMC_CAP2_PACKED_RD | \ + MMC_CAP2_PACKED_WR) +#define MMC_CAP2_CMD_QUEUE (1 << 18) /* support eMMC command queue */ +#define MMC_CAP2_ENHANCED_STROBE (1 << 19) +#define MMC_CAP2_CACHE_FLUSH_BARRIER (1 << 20) +/* Allow background operations auto enable control */ +#define MMC_CAP2_BKOPS_AUTO_CTRL (1 << 21) +/* Allow background operations manual enable control */ +#define MMC_CAP2_BKOPS_MANUAL_CTRL (1 << 22) + +/* host is connected by via modem through sdio */ +#define MMC_CAP2_SUPPORT_VIA_MODEM (1 << 26) +/* host is connected by wifi through sdio */ +#define MMC_CAP2_SUPPORT_WIFI (1 << 27) +/* host is connected to 1102 wifi */ +#define MMC_CAP2_SUPPORT_WIFI_CMD11 (1 << 28) +/* host do not support low power for wifi*/ +#define MMC_CAP2_WIFI_NO_LOWPWR (1 << 29) +#endif
int fixed_drv_type; /* fixed driver type for non-removable media */
@@ -461,6 +544,12 @@ struct mmc_host { bool cqe_on;
unsigned long private[0] ____cacheline_aligned; +#ifdef CONFIG_ASCEND_HISI_MMC + const struct mmc_cmdq_host_ops *cmdq_ops; + int sdio_present; + unsigned int cmdq_slots; + struct mmc_cmdq_context_info cmdq_ctx; +#endif };
struct device_node; @@ -588,4 +677,30 @@ static inline enum dma_data_direction mmc_get_dma_dir(struct mmc_data *data) int mmc_send_tuning(struct mmc_host *host, u32 opcode, int *cmd_error); int mmc_abort_tuning(struct mmc_host *host, u32 opcode);
+#ifdef CONFIG_ASCEND_HISI_MMC +int mmc_cache_ctrl(struct mmc_host *host, u8 enable); +int mmc_card_awake(struct mmc_host *host); +int mmc_card_sleep(struct mmc_host *host); +int mmc_card_can_sleep(struct mmc_host *host); +#else +static inline int mmc_cache_ctrl(struct mmc_host *host, u8 enable) +{ + return 0; +} +static inline int mmc_card_awake(struct mmc_host *host) +{ + return 0; +} +static inline int mmc_card_sleep(struct mmc_host *host) +{ + return 0; +} +static inline int mmc_card_can_sleep(struct mmc_host *host) +{ + return 0; +} +#endif + +int mmc_is_ascend_customized(struct device *dev); + #endif /* LINUX_MMC_HOST_H */ diff --git a/include/linux/mmc/pm.h b/include/linux/mmc/pm.h index 4a139204c20c0..6e2d6a135c7e0 100644 --- a/include/linux/mmc/pm.h +++ b/include/linux/mmc/pm.h @@ -26,5 +26,6 @@ typedef unsigned int mmc_pm_flag_t;
#define MMC_PM_KEEP_POWER (1 << 0) /* preserve card power during suspend */ #define MMC_PM_WAKE_SDIO_IRQ (1 << 1) /* wake up host system on SDIO IRQ assertion */ +#define MMC_PM_IGNORE_PM_NOTIFY (1 << 2) /* ignore mmc pm notify */
#endif /* LINUX_MMC_PM_H */
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 d1cc0fdbc51c8..782d35507b4b4 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -2271,6 +2271,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 941f39ca4eb70..877b2ab9a2a7d 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 e340791a8eab3..f61a723dce166 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;
@@ -1024,7 +1028,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; @@ -1536,7 +1541,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) @@ -1654,6 +1667,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); @@ -1690,6 +1705,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); @@ -1723,7 +1741,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;
@@ -1736,7 +1754,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;
@@ -2521,9 +2539,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)) @@ -2610,12 +2629,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) { @@ -2666,6 +2691,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; } @@ -2687,17 +2719,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 3071b644e94a6..50f0b820d217c 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -33,6 +33,13 @@ struct mmc_bus_ops { int (*hw_reset)(struct mmc_host *); int (*sw_reset)(struct mmc_host *); bool (*cache_enabled)(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); @@ -60,6 +67,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 3f8e84a80b4ad..b7c0934595c53 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 */
From: zhangguijiang zhangguijiang@huawei.com
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: zhangguijiang zhangguijiang@huawei.com Reviewed-by: Ding Tianhong dingtianhong@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- drivers/mmc/core/mmc.c | 389 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 376 insertions(+), 13 deletions(-)
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index d9202f2726d1a..cd66a269dfa77 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -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 @@ -414,7 +415,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]; @@ -423,7 +426,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];
@@ -508,6 +513,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 = @@ -628,6 +636,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 = @@ -657,7 +668,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; } @@ -766,6 +791,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;
@@ -800,6 +831,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, @@ -858,6 +895,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); @@ -1496,6 +1537,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. */ @@ -1509,10 +1609,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;
@@ -1555,6 +1657,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; @@ -1746,6 +1849,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 */ @@ -1769,6 +1887,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); }
/* @@ -1802,7 +1924,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); @@ -1868,6 +1998,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;
@@ -1885,6 +2020,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 = {}; @@ -1952,7 +2139,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); @@ -2009,6 +2200,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 bool _mmc_cache_enabled(struct mmc_host *host) { return host->card->ext_csd.cache_size > 0 && @@ -2069,6 +2333,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. @@ -2081,7 +2398,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); @@ -2106,8 +2424,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; } @@ -2117,8 +2439,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; }
/* @@ -2131,7 +2463,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); @@ -2146,6 +2482,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", @@ -2202,6 +2544,23 @@ static const struct mmc_bus_ops mmc_ops = { .cache_enabled = _mmc_cache_enabled, };
+#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. */ @@ -2219,8 +2578,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;
From: zhangguijiang zhangguijiang@huawei.com
ascend inclusion category: feature feature: Ascend emmc adaption bugzilla: https://gitee.com/openeuler/kernel/issues/I4F4LL CVE: NA
--------------------
Modified sd suspend, resume, reset and init process for ascend HiSilicon chip.
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/sd.c | 83 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 78 insertions(+), 5 deletions(-)
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index c179c4984e311..e439c0ad462b0 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -28,6 +28,7 @@ #include "mmc_ops.h" #include "sd.h" #include "sd_ops.h" +#include "hisi_core_mmc.h"
static const unsigned int tran_exp[] = { 10000, 100000, 1000000, 10000000, @@ -167,6 +168,19 @@ static int mmc_decode_csd(struct mmc_card *card) m = UNSTUFF_BITS(resp, 48, 22); csd->capacity = (1 + m) << 10;
+ /* + * Bit 12 - temporary write protection + * Bit 13 - permanent write protection + * while do sd card detection, check write protection attribute + */ + if (mmc_is_ascend_customized(card->host->parent)) + if (UNSTUFF_BITS(resp, 12, 1) || + UNSTUFF_BITS(resp, 13, 1)) { + pr_info("%s, SD is on write protection\n", + mmc_hostname(card->host)); + mmc_card_set_readonly(card); + } + csd->read_blkbits = 9; csd->read_partial = 0; csd->write_misalign = 0; @@ -261,7 +275,10 @@ static int mmc_read_ssr(struct mmc_card *card)
for (i = 0; i < 16; i++) card->raw_ssr[i] = be32_to_cpu(raw_ssr[i]); - +#ifdef CONFIG_ASCEND_HISI_MMC + if (mmc_is_ascend_customized(card->host->parent)) + card->ssr.speed_class = UNSTUFF_BITS(raw_ssr, 440 - 384, 8); +#endif kfree(raw_ssr);
/* @@ -1047,6 +1064,10 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, err = mmc_sd_init_uhs_card(card); if (err) goto free_card; +#ifdef CONFIG_ASCEND_HISI_MMC + if (mmc_is_ascend_customized(card->host->parent)) + mmc_sd_card_set_uhs(card); +#endif } else { /* * Attempt to change to high-speed (if supported) @@ -1056,7 +1077,8 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, mmc_set_timing(card->host, MMC_TIMING_SD_HS); else if (err) goto free_card; - + else if (mmc_is_ascend_customized(card->host->parent)) + mmc_set_timing(card->host, MMC_TIMING_NEW_SD); /* * Set bus speed. */ @@ -1149,7 +1171,10 @@ static int _mmc_sd_suspend(struct mmc_host *host) err = mmc_deselect_cards(host);
if (!err) { - mmc_power_off(host); + if (!(mmc_is_ascend_customized(host->parent))) + mmc_power_off(host); + else if (mmc_card_keep_power(host)) + mmc_power_off(host); mmc_card_set_suspended(host->card); }
@@ -1186,9 +1211,23 @@ static int _mmc_sd_resume(struct mmc_host *host)
if (!mmc_card_suspended(host->card)) goto out; + if (mmc_is_ascend_customized(host->parent)) { + if (!mmc_card_keep_power(host)) { + mmc_power_up(host, host->card->ocr); + err = mmc_sd_init_card( + host, host->card->ocr, host->card); + err = mmc_sd_init_card( + host, host->card->ocr, host->card); + } else { + err = mmc_select_card(host->card); + if (err) + err = mmc_sd_reset(host); + }
- mmc_power_up(host, host->card->ocr); - err = mmc_sd_init_card(host, host->card->ocr, host->card); + } else { + mmc_power_up(host, host->card->ocr); + err = mmc_sd_init_card(host, host->card->ocr, host->card); + } mmc_card_clr_suspended(host->card);
out: @@ -1238,8 +1277,42 @@ static int mmc_sd_runtime_resume(struct mmc_host *host) return 0; }
+#ifdef CONFIG_ASCEND_HISI_MMC +/*********************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); +} +#endif static int mmc_sd_hw_reset(struct mmc_host *host) { +#ifdef CONFIG_ASCEND_HISI_MMC + if (mmc_is_ascend_customized(host->parent)) + return mmc_do_sd_reset(host); +#endif mmc_power_cycle(host, host->card->ocr); return mmc_sd_init_card(host, host->card->ocr, host->card); }
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 host controller, we externs specific extensions. We provide an Ascend Hisilicon adapted dw mci operation set. Ascend Hisi mmc has different process such as Hisilicon adapted init slot, specials interrupt. We also add some properties to dts, so we need to parse these properties to customized our capability.
Signed-off-by: zhangguijiang zhangguijiang@huawei.com Reviewed-by: Ding Tianhong dingtianhong@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- drivers/mmc/host/dw_mmc-pltfm.c | 13 + drivers/mmc/host/dw_mmc-pltfm.h | 63 ++ drivers/mmc/host/dw_mmc.c | 1664 ++++++++++++++++++++++++++++-- drivers/mmc/host/dw_mmc.h | 155 +++ drivers/mmc/host/dw_mmc_extern.h | 76 ++ drivers/mmc/host/dw_mmc_hisi.h | 291 ++++++ drivers/mmc/host/hisi_mmc_pmic.h | 71 ++ 7 files changed, 2224 insertions(+), 109 deletions(-) create mode 100644 drivers/mmc/host/dw_mmc_extern.h create mode 100644 drivers/mmc/host/dw_mmc_hisi.h create mode 100644 drivers/mmc/host/hisi_mmc_pmic.h
diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c index 58c13e21bd5a7..9dd05cce4decd 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.c +++ b/drivers/mmc/host/dw_mmc-pltfm.c @@ -31,11 +31,17 @@ int dw_mci_pltfm_register(struct platform_device *pdev, { struct dw_mci *host; struct resource *regs; + int ret;
host = devm_kzalloc(&pdev->dev, sizeof(struct dw_mci), GFP_KERNEL); if (!host) return -ENOMEM;
+ if (mmc_is_ascend_customized(&pdev->dev)) { + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) + return -ENXIO; + } host->irq = platform_get_irq(pdev, 0); if (host->irq < 0) return host->irq; @@ -52,6 +58,13 @@ int dw_mci_pltfm_register(struct platform_device *pdev,
/* Get registers' physical base address */ host->phy_regs = regs->start; + if (mmc_is_ascend_customized(host->dev)) { + if (drv_data && drv_data->init) { + ret = drv_data->init(host); + if (ret) + return ret; + } + }
platform_set_drvdata(pdev, host); return dw_mci_probe(host); diff --git a/drivers/mmc/host/dw_mmc-pltfm.h b/drivers/mmc/host/dw_mmc-pltfm.h index 68e7fd2f6148b..6d2d61d435960 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.h +++ b/drivers/mmc/host/dw_mmc-pltfm.h @@ -12,6 +12,69 @@ #ifndef _DW_MMC_PLTFM_H_ #define _DW_MMC_PLTFM_H_
+#ifdef CONFIG_ASCEND_HISI_MMC + +#define INTMSK_ALL 0xFFFFFFFF +#define INTMSK_CDETECT (0x1 << 0) +#define INTMSK_RE (0x1 << 1) +#define INTMSK_CDONE (0x1 << 2) +#define INTMSK_DTO (0x1 << 3) +#define INTMSK_TXDR (0x1 << 4) +#define INTMSK_RXDR (0x1 << 5) +#define INTMSK_RCRC (0x1 << 6) +#define INTMSK_DCRC (0x1 << 7) +#define INTMSK_RTO (0x1 << 8) +#define INTMSK_DRTO (0x1 << 9) +#define INTMSK_HTO (0x1 << 10) +#define INTMSK_VOLT_SWITCH (0x1 << 10) +#define INTMSK_FRUN (0x1 << 11) +#define INTMSK_HLE (0x1 << 12) +#define INTMSK_SBE (0x1 << 13) +#define INTMSK_ACD (0x1 << 14) +#define INTMSK_EBE (0x1 << 15) +#define INTMSK_DMA (INTMSK_ACD | INTMSK_RXDR | INTMSK_TXDR) + +#define INT_SRC_IDMAC (0x0) +#define INT_SRC_MINT (0x1) + + +#define CMD_RESP_EXP_BIT (0x1 << 6) +#define CMD_RESP_LENGTH_BIT (0x1 << 7) +#define CMD_CHECK_CRC_BIT (0x1 << 8) +#define CMD_DATA_EXP_BIT (0x1 << 9) +#define CMD_RW_BIT (0x1 << 10) +#define CMD_TRANSMODE_BIT (0x1 << 11) +#define CMD_WAIT_PRV_DAT_BIT (0x1 << 13) +#define CMD_STOP_ABORT_CMD (0x1 << 14) +#define CMD_SEND_INITIALIZATION (0x1 << 15) +#define CMD_SEND_CLK_ONLY (0x1 << 21) +#define CMD_VOLT_SWITCH (0x1 << 28) +#define CMD_USE_HOLD_REG (0x1 << 29) +#define CMD_STRT_BIT (0x1 << 31) +#define CMD_ONLY_CLK (CMD_STRT_BIT | CMD_SEND_CLK_ONLY | \ + CMD_WAIT_PRV_DAT_BIT) + +#define CLK_ENABLE (0x1 << 0) +#define CLK_DISABLE (0x0 << 0) + +#define MMC_CCLK_MAX_24M (24000000) +#define MMC_CCLK_MAX_25M (25000000) +#define MMC_CCLK_MAX_48M (48000000) +#define MMC_CCLK_MAX_50M (50000000) +#define MMC_CCLK_MAX_80M (80000000) +#define MMC_CCLK_MAX_96M (96000000) +#define MMC_CCLK_MAX_100M (100000000) +#define MMC_CCLK_MAX_150M (150000000) +#define MMC_CCLK_MAX_180M (180000000) +#define MMC_CCLK_MAX_200M (200000000) + +#define MMC_EMMC (0x0) +#define MMC_SD (0x1) +#define MMC_SDIO (0x2) +#define MMC_SDIO_FOR_EMMC (0x3) + +#endif + extern int dw_mci_pltfm_register(struct platform_device *pdev, const struct dw_mci_drv_data *drv_data); extern int dw_mci_pltfm_remove(struct platform_device *pdev); diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index e3991df078efb..8b737847f59c7 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -41,6 +41,8 @@ #include <linux/mmc/slot-gpio.h>
#include "dw_mmc.h" +#include "hisi_mmc_pmic.h" +#include "dw_mmc_extern.h"
/* Common flag combinations */ #define DW_MCI_DATA_ERROR_FLAGS (SDMMC_INT_DRTO | SDMMC_INT_DCRC | \ @@ -271,10 +273,53 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) cmd, arg, cmd_status); }
+static int hisi_mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) +{ + struct dw_mci *host = slot->host; + unsigned long timeout = jiffies + msecs_to_jiffies(100); + unsigned int cmd_status = 0; + + mci_writel(host, CMDARG, arg); + /* Synchronous execution */ + wmb(); + + mci_writel(host, CMD, SDMMC_CMD_START | cmd); + while (time_before(jiffies, timeout)) { + cmd_status = mci_readl(host, CMD); + if (!(cmd_status & SDMMC_CMD_START)) + return 0; + } + + if (!dw_mci_wait_reset(host->dev, host, SDMMC_CTRL_RESET)) + return 1; + + timeout = jiffies + msecs_to_jiffies(100); + mci_writel(host, CMD, SDMMC_CMD_START | cmd); + while (time_before(jiffies, timeout)) { + cmd_status = mci_readl(host, CMD); + if (!(cmd_status & SDMMC_CMD_START)) + return 0; + } + + dev_warn(&slot->mmc->class_dev, + "Timeout sending command (cmd %#x arg %#x status %#x)\n", + cmd, arg, cmd_status); + return 1; +} + +static int dw_mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) +{ + if (mmc_is_ascend_customized(slot->host->dev)) + return hisi_mci_send_cmd(slot, cmd, arg); + mci_send_cmd(slot, cmd, arg); + return 0; +} + static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) { struct dw_mci_slot *slot = mmc_priv(mmc); struct dw_mci *host = slot->host; + u32 clk_en_a; u32 cmdr;
cmd->error = -EINPROGRESS; @@ -290,7 +335,12 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) cmdr |= SDMMC_CMD_PRV_DAT_WAIT;
if (cmd->opcode == SD_SWITCH_VOLTAGE) { - u32 clk_en_a; + if (mmc_is_ascend_customized(host->dev)) { + cmdr |= SDMMC_CMD_VOLT_SWITCH; + goto out; + } + +
/* Special bit makes CMD11 not die */ cmdr |= SDMMC_CMD_VOLT_SWITCH; @@ -313,10 +363,10 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) clk_en_a = mci_readl(host, CLKENA); clk_en_a &= ~(SDMMC_CLKEN_LOW_PWR << slot->id); mci_writel(host, CLKENA, clk_en_a); - mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | + dw_mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); } - +out: if (cmd->flags & MMC_RSP_PRESENT) { /* We expect a response, so set this bit */ cmdr |= SDMMC_CMD_RESP_EXP; @@ -416,6 +466,32 @@ static inline void dw_mci_set_cto(struct dw_mci *host) spin_unlock_irqrestore(&host->irq_lock, irqflags); }
+static void dw_mci_disable_clock_low_power(struct dw_mci *host, u32 cmd_flags) +{ +#ifdef CONFIG_ASCEND_HISI_MMC + int loop_count; + u32 temp; + + temp = mci_readl(host, INTMASK); + temp &= ~SDMMC_INT_RESP_ERR; + mci_writel(host, INTMASK, temp); + cmd_flags |= SDMMC_CMD_VOLT_SWITCH; + + /* disable clock low power */ + mci_writel(host, CLKENA, (0x1<<0)); + mci_writel(host, CMD, SDMMC_CMD_ONLY_CLK | SDMMC_CMD_VOLT_SWITCH); + loop_count = 0x100000; + do { + if (!(mci_readl(host, CMD) & SDMMC_CMD_START)) + break; + loop_count--; + } while (loop_count); + if (!loop_count) + dev_warn(host->dev, + "disable clk low power failed in volt_switch.\n"); +#endif +} + static void dw_mci_start_command(struct dw_mci *host, struct mmc_command *cmd, u32 cmd_flags) { @@ -424,6 +500,10 @@ static void dw_mci_start_command(struct dw_mci *host, "start command: ARGR=0x%08x CMDR=0x%08x\n", cmd->arg, cmd_flags);
+ if (mmc_is_ascend_customized(host->dev) && + cmd->opcode == SD_SWITCH_VOLTAGE) { + dw_mci_disable_clock_low_power(host, cmd_flags); + } mci_writel(host, CMDARG, cmd->arg); wmb(); /* drain writebuffer */ dw_mci_wait_while_busy(host, cmd_flags); @@ -442,8 +522,15 @@ static inline void send_stop_abort(struct dw_mci *host, struct mmc_data *data) dw_mci_start_command(host, stop, host->stop_cmdr); }
+#ifdef CONFIG_ASCEND_HISI_MMC +static void send_stop_cmd(struct dw_mci *host, struct mmc_data *data) +{ + dw_mci_start_command(host, data->stop, host->stop_cmdr); +} +#endif + /* DMA interface functions */ -static void dw_mci_stop_dma(struct dw_mci *host) +void dw_mci_stop_dma(struct dw_mci *host) { if (host->using_dma) { host->dma_ops->stop(host); @@ -453,6 +540,7 @@ static void dw_mci_stop_dma(struct dw_mci *host) /* Data transfer was stopped by the interrupt handler */ set_bit(EVENT_XFER_COMPLETE, &host->pending_events); } +EXPORT_SYMBOL(dw_mci_stop_dma);
static void dw_mci_dma_cleanup(struct dw_mci *host) { @@ -467,13 +555,14 @@ static void dw_mci_dma_cleanup(struct dw_mci *host) } }
-static void dw_mci_idmac_reset(struct dw_mci *host) +void dw_mci_idmac_reset(struct dw_mci *host) { u32 bmod = mci_readl(host, BMOD); /* Software reset of DMA */ bmod |= SDMMC_IDMAC_SWRESET; mci_writel(host, BMOD, bmod); } +EXPORT_SYMBOL(dw_mci_idmac_reset);
static void dw_mci_idmac_stop_dma(struct dw_mci *host) { @@ -597,6 +686,107 @@ static int dw_mci_idmac_init(struct dw_mci *host) return 0; }
+static void dw_mci_translate_sglist(struct dw_mci *host, struct mmc_data *data, + unsigned int sg_len) +{ + unsigned int desc_len; + + if (host->dma_64bit_address == SDMMC_32_BIT_DMA) { + unsigned int i; + struct idmac_desc *desc = host->sg_cpu; + + if (!sg_len) + return; + + for (i = 0; i < sg_len; i++, desc++) { + unsigned int length = sg_dma_len(&data->sg[i]); + u32 mem_addr = sg_dma_address(&data->sg[i]); + + /* + * Set the OWN bit and disable interrupts + * for this descriptor + */ + desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | + IDMAC_DES0_CH; + if (desc->des0 & IDMAC_DES0_CH) + desc->des1 = 0; + + if (desc->des0 & IDMAC_DES0_CH) + desc->des1 = 0; + + /* Buffer length */ + IDMAC_SET_BUFFER1_SIZE(desc, length); + + /* Physical address to DMA to/from */ + desc->des2 = mem_addr; + } + + /* Set first descriptor */ + desc = host->sg_cpu; + desc->des0 |= IDMAC_DES0_FD; + + /* Set last descriptor */ + desc = host->sg_cpu + (i - 1) * sizeof(struct idmac_desc); + desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC); + desc->des0 |= IDMAC_DES0_LD; + /* Synchronous execution */ + wmb(); + } else { + unsigned int i; + + struct idmac_desc_64addr *desc_first, *desc_last, *desc; + + desc_first = desc_last = desc = host->sg_cpu; + if (!sg_len) + return; + for (i = 0; i < sg_len; i++) { + unsigned int length = sg_dma_len(&data->sg[i]); + + u64 mem_addr = sg_dma_address(&data->sg[i]); + + for ( ; length ; desc++) { + desc_len = (length <= DW_MCI_DESC_DATA_LENGTH) ? + length : DW_MCI_DESC_DATA_LENGTH; + + length -= desc_len; + + /* + * Set the OWN bit and disable interrupts + * for this descriptor + */ + desc->des0 = IDMAC_DES0_OWN| + IDMAC_DES0_DIC | IDMAC_DES0_CH; + + if (desc->des0 & IDMAC_DES0_CH) + desc->des2 = 0; + + if (desc->des0 & IDMAC_DES0_CH) + desc->des2 = 0; + + /* Buffer length */ + IDMAC_64ADDR_SET_BUFFER1_SIZE(desc, desc_len); + + /* Physical address to DMA to/from */ + desc->des4 = mem_addr & 0xffffffff; + desc->des5 = mem_addr >> 32; + + /* Update physical address for the next desc */ + mem_addr += desc_len; + + /* Save pointer to the last descriptor */ + desc_last = desc; + } + } + + /* Set first descriptor */ + desc_first->des0 |= IDMAC_DES0_FD; + + /* Set last descriptor */ + desc_last->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC); + desc_last->des0 |= IDMAC_DES0_LD; + wmb(); /* drain writebuffer */ + } +} static inline int dw_mci_prepare_desc64(struct dw_mci *host, struct mmc_data *data, unsigned int sg_len) @@ -745,18 +935,22 @@ static inline int dw_mci_prepare_desc32(struct dw_mci *host, static int dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len) { u32 temp; - int ret; + int ret = 0;
- if (host->dma_64bit_address == 1) - ret = dw_mci_prepare_desc64(host, host->data, sg_len); - else - ret = dw_mci_prepare_desc32(host, host->data, sg_len); + if (mmc_is_ascend_customized(host->dev)) + dw_mci_translate_sglist(host, host->data, sg_len); + else { + if (host->dma_64bit_address == 1) + ret = dw_mci_prepare_desc64(host, host->data, sg_len); + else + ret = dw_mci_prepare_desc32(host, host->data, sg_len);
- if (ret) - goto out; + if (ret) + goto out;
- /* drain writebuffer */ - wmb(); + /* drain writebuffer */ + wmb(); + }
/* Make sure to reset DMA in case we did PIO before this */ dw_mci_ctrl_reset(host, SDMMC_CTRL_DMA_RESET); @@ -965,7 +1159,38 @@ static void dw_mci_post_req(struct mmc_host *mmc, data->host_cookie = COOKIE_UNMAPPED; }
-static int dw_mci_get_cd(struct mmc_host *mmc) +#ifdef CONFIG_ASCEND_HISI_MMC +int hisi_dw_mci_get_cd(struct mmc_host *mmc) +{ + int present; + struct dw_mci_slot *slot = mmc_priv(mmc); + struct dw_mci_board *brd = slot->host->pdata; + struct dw_mci *host = slot->host; + u32 id = (u32)(slot->id); + + pm_runtime_get_sync(mmc_dev(mmc)); + /* Use platform get_cd function, else try onboard card detect */ + if (brd->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION) + present = 1; + else if (brd->get_cd) + present = !brd->get_cd(slot->host, slot->id); + else if (host->hw_mmc_id == DWMMC_SDIO_ID) + present = mmc->sdio_present; + else + present = (mci_readl(slot->host, CDETECT) & (1 << id)) + == 0 ? 1 : 0; + if (!present) + dev_err(&mmc->class_dev, "card is not present\n"); + + pm_runtime_mark_last_busy(mmc_dev(mmc)); + pm_runtime_put_autosuspend(mmc_dev(mmc)); + + return present; +} +EXPORT_SYMBOL(hisi_dw_mci_get_cd); +#endif + +int dw_mci_get_cd(struct mmc_host *mmc) { int present; struct dw_mci_slot *slot = mmc_priv(mmc); @@ -1236,7 +1461,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
if (!clock) { mci_writel(host, CLKENA, 0); - mci_send_cmd(slot, sdmmc_cmd_bits, 0); + dw_mci_send_cmd(slot, sdmmc_cmd_bits, 0); } else if (clock != host->current_speed || force_clkinit) { div = host->bus_hz / clock; if (host->bus_hz % clock && host->bus_hz > clock) @@ -1273,13 +1498,13 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) mci_writel(host, CLKSRC, 0);
/* inform CIU */ - mci_send_cmd(slot, sdmmc_cmd_bits, 0); + dw_mci_send_cmd(slot, sdmmc_cmd_bits, 0);
/* set clock to desired speed */ mci_writel(host, CLKDIV, div);
/* inform CIU */ - mci_send_cmd(slot, sdmmc_cmd_bits, 0); + dw_mci_send_cmd(slot, sdmmc_cmd_bits, 0);
/* enable clock; only low power if no SDIO */ clk_en_a = SDMMC_CLKEN_ENABLE << slot->id; @@ -1288,7 +1513,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) mci_writel(host, CLKENA, clk_en_a);
/* inform CIU */ - mci_send_cmd(slot, sdmmc_cmd_bits, 0); + dw_mci_send_cmd(slot, sdmmc_cmd_bits, 0);
/* keep the last clock value that was requested from core */ slot->__clk_old = clock; @@ -1302,6 +1527,44 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) mci_writel(host, CTYPE, (slot->ctype << slot->id)); }
+static void hisi_mod_host_timer(struct dw_mci *host, struct dw_mci_slot *slot) +{ +#ifdef CONFIG_ASCEND_HISI_MMC + host->stop_snd = false; + if (host->pdata->select_slot) + host->pdata->select_slot(slot->id); + + /* Slot specific timing and width adjustment */ + dw_mci_setup_bus(slot, 0); + + if (host->flags & DWMMC_IN_TUNING) { + mod_timer(&host->timer, jiffies + msecs_to_jiffies(100)); + } else { + if (host->hw_mmc_id == DWMMC_EMMC_ID) { + /*add to 20s timeout for some write slow emmc*/ + mod_timer(&host->timer, + jiffies + msecs_to_jiffies(20000)); + + } else if ((host->hw_mmc_id == DWMMC_SD_ID) && + (slot->mmc->card)) { + if (mmc_card_suspended(slot->mmc->card)) + mod_timer(&host->timer, + jiffies + msecs_to_jiffies(2000)); + else + mod_timer(&host->timer, + jiffies + msecs_to_jiffies(10000)); + } else if (host->hw_mmc_id == DWMMC_SDIO_ID) { + mod_timer(&host->timer, + jiffies + msecs_to_jiffies(5000)); + } else + mod_timer(&host->timer, + jiffies + msecs_to_jiffies(10000)); + } + + host->cur_slot = slot; +#endif +} + static void __dw_mci_start_request(struct dw_mci *host, struct dw_mci_slot *slot, struct mmc_command *cmd) @@ -1311,7 +1574,8 @@ static void __dw_mci_start_request(struct dw_mci *host, u32 cmdflags;
mrq = slot->mrq; - + if (mmc_is_ascend_customized(host->dev)) + hisi_mod_host_timer(host, slot); host->mrq = mrq;
host->pending_events = 0; @@ -1360,6 +1624,15 @@ static void __dw_mci_start_request(struct dw_mci *host, spin_unlock_irqrestore(&host->irq_lock, irqflags); }
+ if (mmc_is_ascend_customized(host->dev)) { + if (mrq->stop) + host->stop_cmdr = + dw_mci_prepare_command(slot->mmc, mrq->stop); + else { + if (data) + host->stop_cmdr = dw_mci_prep_stop(host, cmd); + } + } host->stop_cmdr = dw_mci_prep_stop_abort(host, cmd); }
@@ -1413,12 +1686,21 @@ static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq) * atomic, otherwise the card could be removed in between and the * request wouldn't fail until another card was inserted. */ - - if (!dw_mci_get_cd(mmc)) { - mrq->cmd->error = -ENOMEDIUM; - mmc_request_done(mmc, mrq); - return; - } + if (mmc_is_ascend_customized(host->dev)) { + pm_runtime_get_sync(mmc_dev(mmc)); + if (!hisi_dw_mci_get_cd(mmc)) { + mrq->cmd->error = -ENOMEDIUM; + mmc_request_done(mmc, mrq); + pm_runtime_mark_last_busy(mmc_dev(mmc)); + pm_runtime_put_autosuspend(mmc_dev(mmc)); + return; + } + } else + if (!dw_mci_get_cd(mmc)) { + mrq->cmd->error = -ENOMEDIUM; + mmc_request_done(mmc, mrq); + return; + }
spin_lock_bh(&host->lock);
@@ -1426,6 +1708,54 @@ static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
spin_unlock_bh(&host->lock); } +#ifdef CONFIG_ASCEND_HISI_MMC +static inline void dw_mci_set_freq_specific(const struct dw_mci_hs_priv_data + *priv, unsigned int origin_freq, + unsigned int *freq) +{ + if ((priv->chip_type == CHIP_HIXX51) && + (priv->chip_platform == SDMMC_FPGA_PLATFORM)) { + *freq = origin_freq / 4; // FPGA work in 25MHz/4=6.25MHz + } +} +#else +static inline void dw_mci_set_freq_specific(const void *priv, + unsigned int origin_freq, + unsigned int *freq) +{ + +} +#endif + +static int dw_mci_ios_mmc_power_up(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct dw_mci_slot *slot = mmc_priv(mmc); + u32 regs; + int ret; + + if (!mmc_is_ascend_customized(mmc->parent)) + if (!IS_ERR(mmc->supply.vmmc)) { + ret = mmc_regulator_set_ocr(mmc, + mmc->supply.vmmc, ios->vdd); + if (ret) { + dev_err(slot->host->dev, + "failed to enable vmmc regulator\n"); + /*return, if failed turn on vmmc*/ + return ret; + } + } + set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags); + /* Power up slot */ +#ifdef CONFIG_ASCEND_HISI_MMC + if (mmc_is_ascend_customized(mmc->parent) && + slot->host->pdata->setpower) + slot->host->pdata->setpower(slot->id, mmc->ocr_avail); +#endif + regs = mci_readl(slot->host, PWREN); + regs |= (1 << slot->id); + mci_writel(slot->host, PWREN, regs); + return 0; +}
static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { @@ -1434,6 +1764,9 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) u32 regs; int ret;
+ if (mmc_is_ascend_customized(mmc->parent)) + pm_runtime_get_sync(mmc_dev(mmc)); + switch (ios->bus_width) { case MMC_BUS_WIDTH_4: slot->ctype = SDMMC_CTYPE_4BIT; @@ -1467,25 +1800,23 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (drv_data && drv_data->set_ios) drv_data->set_ios(slot->host, ios); - + if (mmc_is_ascend_customized(mmc->parent)) { + mmc->f_min = DIV_ROUND_UP(slot->host->bus_hz, 510); + mmc->f_max = slot->host->bus_hz; + dw_mci_set_freq_specific(slot->host->priv, slot->host->bus_hz, + &(mmc->f_max)); + /* Slot specific timing and width adjustment */ + dw_mci_setup_bus(slot, false); + } switch (ios->power_mode) { case MMC_POWER_UP: - if (!IS_ERR(mmc->supply.vmmc)) { - ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, - ios->vdd); - if (ret) { - dev_err(slot->host->dev, - "failed to enable vmmc regulator\n"); - /*return, if failed turn on vmmc*/ - return; - } - } - set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags); - regs = mci_readl(slot->host, PWREN); - regs |= (1 << slot->id); - mci_writel(slot->host, PWREN, regs); + ret = dw_mci_ios_mmc_power_up(mmc, ios); + if (ret) + return; break; case MMC_POWER_ON: + if (mmc_is_ascend_customized(mmc->parent)) + break; if (!slot->host->vqmmc_enabled) { if (!IS_ERR(mmc->supply.vqmmc)) { ret = regulator_enable(mmc->supply.vqmmc); @@ -1510,16 +1841,25 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
break; case MMC_POWER_OFF: - /* Turn clock off before power goes down */ - dw_mci_setup_bus(slot, false); - - if (!IS_ERR(mmc->supply.vmmc)) - mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); +#ifdef CONFIG_ASCEND_HISI_MMC + if (mmc_is_ascend_customized(mmc->parent) && + slot->host->pdata->setpower) + /* Power down slot */ + slot->host->pdata->setpower(slot->id, 0); + else +#endif + { + /* Turn clock off before power goes down */ + dw_mci_setup_bus(slot, false);
- if (!IS_ERR(mmc->supply.vqmmc) && slot->host->vqmmc_enabled) - regulator_disable(mmc->supply.vqmmc); - slot->host->vqmmc_enabled = false; + if (!IS_ERR(mmc->supply.vmmc)) + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
+ if (!IS_ERR(mmc->supply.vqmmc) && + slot->host->vqmmc_enabled) + regulator_disable(mmc->supply.vqmmc); + slot->host->vqmmc_enabled = false; + } regs = mci_readl(slot->host, PWREN); regs &= ~(1 << slot->id); mci_writel(slot->host, PWREN, regs); @@ -1530,6 +1870,10 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (slot->host->state == STATE_WAITING_CMD11_DONE && ios->clock != 0) slot->host->state = STATE_IDLE; + if (mmc_is_ascend_customized(mmc->parent)) { + pm_runtime_mark_last_busy(mmc_dev(mmc)); + pm_runtime_put_autosuspend(mmc_dev(mmc)); + } }
static int dw_mci_card_busy(struct mmc_host *mmc) @@ -1586,10 +1930,26 @@ static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
static int dw_mci_get_ro(struct mmc_host *mmc) { - int read_only; + int read_only = 0; struct dw_mci_slot *slot = mmc_priv(mmc); int gpio_ro = mmc_gpio_get_ro(mmc);
+ if (mmc_is_ascend_customized(mmc->parent)) { + /* + * Use platform get_ro function, + * else try on board write protect + */ +#ifdef CONFIG_ASCEND_HISI_MMC + if (slot->quirks & DW_MCI_SLOT_QUIRK_NO_WRITE_PROTECT || + (slot->host->quirks & DW_MCI_QUIRK_NO_WRITE_PROTECT)) + read_only = 0; + else + read_only = mci_readl(slot->host, WRTPRT) & + (1 << slot->id) ? 1 : 0; +#endif + goto out; + } + /* Use platform get_ro function, else try on board write protect */ if (gpio_ro >= 0) read_only = gpio_ro; @@ -1597,6 +1957,7 @@ static int dw_mci_get_ro(struct mmc_host *mmc) read_only = mci_readl(slot->host, WRTPRT) & (1 << slot->id) ? 1 : 0;
+out: dev_dbg(&mmc->class_dev, "card is %s\n", read_only ? "read-only" : "read-write");
@@ -1659,7 +2020,7 @@ static void dw_mci_init_card(struct mmc_host *mmc, struct mmc_card *card)
if (clk_en_a != clk_en_a_old) { mci_writel(host, CLKENA, clk_en_a); - mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | + dw_mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); } } @@ -1711,7 +2072,16 @@ static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode) struct dw_mci *host = slot->host; const struct dw_mci_drv_data *drv_data = host->drv_data; int err = -EINVAL; - +#ifdef CONFIG_ASCEND_HISI_MMC + struct dw_mci_tuning_data tuning_data; + + if (mmc_is_ascend_customized(mmc->parent)) { + if (drv_data && drv_data->execute_tuning_hisi) + err = drv_data->execute_tuning_hisi(slot, + opcode, &tuning_data); + return err; + } +#endif if (drv_data && drv_data->execute_tuning) err = drv_data->execute_tuning(slot, opcode); return err; @@ -1792,7 +2162,7 @@ static bool dw_mci_reset(struct dw_mci *host)
ciu_out: /* After a CTRL reset we need to have CIU set clock registers */ - mci_send_cmd(host->slot, SDMMC_CMD_UPD_CLK, 0); + dw_mci_send_cmd(host->slot, SDMMC_CMD_UPD_CLK, 0);
return ret; } @@ -1814,17 +2184,112 @@ static const struct mmc_host_ops dw_mci_ops = { .prepare_hs400_tuning = dw_mci_prepare_hs400_tuning, };
-static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) +#ifdef CONFIG_ASCEND_HISI_MMC +static const struct mmc_host_ops dw_hisi_mci_ops = { + .request = dw_mci_request, + .pre_req = dw_mci_pre_req, + .post_req = dw_mci_post_req, + .set_ios = dw_mci_set_ios, + .get_ro = dw_mci_get_ro, + .get_cd = hisi_dw_mci_get_cd, + .hw_reset = dw_mci_hw_reset, + .enable_sdio_irq = dw_mci_enable_sdio_irq, + .ack_sdio_irq = dw_mci_ack_sdio_irq, + .execute_tuning = dw_mci_execute_tuning, + .card_busy = dw_mci_card_busy, + .slowdown_clk = dw_mci_slowdown_clk, + .start_signal_voltage_switch = dw_mci_start_signal_voltage_switch, + .init_card = dw_mci_init_card, + .prepare_hs400_tuning = dw_mci_prepare_hs400_tuning, +}; +#endif + +static int hisi_dw_mci_del_timer(struct dw_mci *host) +{ +#ifdef CONFIG_ASCEND_HISI_MMC + if (del_timer(&host->timer) == 0) + dev_info(host->dev, "inactive timer\n"); + + if (host->cur_slot->mrq == NULL || host->mrq == NULL) + return 1; +#endif /* CONFIG_ASCEND_HISI_MMC */ + return 0; +} + +static void hisi_dw_mci_tuning_move(struct dw_mci *host, + struct mmc_request *mrq) +{ +#ifdef CONFIG_ASCEND_HISI_MMC + const struct dw_mci_drv_data *drv_data = host->drv_data; + struct mmc_host *prev_mmc = host->slot->mmc; + int timing = prev_mmc->ios.timing; + + if (!(drv_data->tuning_move)) + return; + + /*SOC1005*/ + if ((host->flags & DWMMC_TUNING_DONE) && mrq && mrq->cmd && + ((mrq->cmd->error) || + (mrq->cmd->data && ((mrq->cmd->data->error) || + (mrq->cmd->data->stop && + mrq->cmd->data->stop->error))))) { + /* if SD get hardware timeout, No tuning move! */ + if (host->sd_hw_timeout == 1) { + host->sd_hw_timeout = 0; + return; + } + + dev_dbg(host->dev, + "move tuning del_sel, start=%d, cmd=%d, arg=0x%x\n", + host->tuning_move_start, + mrq->cmd->opcode, + mrq->cmd->arg); + /* req error, need move del_sel */ + if (host->tuning_move_start != -1) { + if (drv_data->tuning_move(host, timing, + host->tuning_move_start)) { + host->tuning_move_start = 0; + mrq->cmd->retries++; + + if (mrq->cmd->data && mrq->cmd->data->error) + mrq->cmd->data->error = 0; + + if (mrq->cmd->data && + mrq->cmd->data->stop && + mrq->cmd->data->stop->error) + mrq->cmd->data->stop->error = 0; + + if (!mrq->cmd->error) + mrq->cmd->error = -EILSEQ; + } else { + host->tuning_move_start = -1; + } + } + } else { + host->tuning_move_start = 1; + } +#endif /* CONFIG_ASCEND_HISI_MMC */ +} + +void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) __releases(&host->lock) __acquires(&host->lock) { struct dw_mci_slot *slot; struct mmc_host *prev_mmc = host->slot->mmc;
+ if (mmc_is_ascend_customized(host->dev)) + if (hisi_dw_mci_del_timer(host)) + return; + WARN_ON(host->cmd || host->data);
host->slot->mrq = NULL; host->mrq = NULL; + + if (mmc_is_ascend_customized(host->dev)) + hisi_dw_mci_tuning_move(host, mrq); + if (!list_empty(&host->queue)) { slot = list_entry(host->queue.next, struct dw_mci_slot, queue_node); @@ -1845,6 +2310,24 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) spin_unlock(&host->lock); mmc_request_done(prev_mmc, mrq); spin_lock(&host->lock); + if (mmc_is_ascend_customized(host->dev)) { + pm_runtime_mark_last_busy(mmc_dev(prev_mmc)); + pm_runtime_put_autosuspend(mmc_dev(prev_mmc)); + } +} +EXPORT_SYMBOL(dw_mci_request_end); + +static inline void dw_mci_check_and_udelay_1000(struct dw_mci *host, + struct mmc_command *cmd) +{ +#ifdef CONFIG_ASCEND_HISI_MMC + if (!mmc_is_ascend_customized(host->dev)) + return; + if ((host->hw_mmc_id == DWMMC_SD_ID) && + (cmd->opcode == MMC_SEND_TUNING_BLOCK)) + udelay(1000); +#endif + return; }
static int dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd) @@ -1868,13 +2351,16 @@ static int dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd) } }
- if (status & SDMMC_INT_RTO) + if (status & SDMMC_INT_RTO) { + dw_mci_check_and_udelay_1000(host, cmd); cmd->error = -ETIMEDOUT; - else if ((cmd->flags & MMC_RSP_CRC) && (status & SDMMC_INT_RCRC)) + } else if ((cmd->flags & MMC_RSP_CRC) && (status & SDMMC_INT_RCRC)) { + dw_mci_check_and_udelay_1000(host, cmd); cmd->error = -EILSEQ; - else if (status & SDMMC_INT_RESP_ERR) + } else if (status & SDMMC_INT_RESP_ERR) { + dw_mci_check_and_udelay_1000(host, cmd); cmd->error = -EIO; - else + } else cmd->error = 0;
return cmd->error; @@ -1978,51 +2464,298 @@ static bool dw_mci_clear_pending_data_complete(struct dw_mci *host) return true; }
-static void dw_mci_tasklet_func(unsigned long priv) +#ifdef CONFIG_ASCEND_HISI_MMC +static void dw_hisi_mci_tasklet_func(unsigned long priv) { struct dw_mci *host = (struct dw_mci *)priv; struct mmc_data *data; struct mmc_command *cmd; - struct mmc_request *mrq; enum dw_mci_state state; enum dw_mci_state prev_state; - unsigned int err; + u32 status;
spin_lock(&host->lock);
state = host->state; data = host->data; - mrq = host->mrq; + + if (host->cmd_status & SDMMC_INT_HLE) { + clear_bit(EVENT_CMD_COMPLETE, &host->pending_events); + dev_err(host->dev, "hardware locked write error\n"); + dw_mci_reg_dump(host); + host->cmd_status &= ~SDMMC_INT_HLE; + goto unlock; + }
do { prev_state = state;
switch (state) { case STATE_IDLE: - case STATE_WAITING_CMD11_DONE: break;
- case STATE_SENDING_CMD11: case STATE_SENDING_CMD: - if (!dw_mci_clear_pending_cmd_complete(host)) + if (host->cmd == NULL) { + dev_err(host->dev, + "%s: The command currently being send to the card is NULL!\n", + __func__); + break; + } + if (!test_and_clear_bit(EVENT_CMD_COMPLETE, + &host->pending_events)) break; - cmd = host->cmd; host->cmd = NULL; set_bit(EVENT_CMD_COMPLETE, &host->completed_events); - err = dw_mci_command_complete(host, cmd); - if (cmd == mrq->sbc && !err) { - __dw_mci_start_request(host, host->slot, - mrq->cmd); + dw_mci_command_complete(host, cmd); + if (cmd == host->mrq->sbc && !cmd->error) { + prev_state = state = STATE_SENDING_CMD; + __dw_mci_start_request(host, host->cur_slot, + host->mrq->cmd); goto unlock; } + if (data && cmd->error && + cmd != data->stop) { + if (host->mrq->data->stop) + send_stop_cmd(host, host->mrq->data); + else { + dw_mci_start_command(host, + &host->stop, + host->stop_cmdr); + host->stop_snd = true; + }
- if (cmd->data && err) { - /* - * During UHS tuning sequence, sending the stop - * command after the response CRC error would - * throw the system into a confused state - * causing all future tuning phases to report + state = STATE_SENDING_STOP; + /* To avoid fifo full condition */ + dw_mci_fifo_reset(host->dev, host); + break; + } + + if (!host->mrq->data || cmd->error) { + dw_mci_request_end(host, host->mrq); + goto unlock; + } + + prev_state = state = STATE_SENDING_DATA; + /* fall through */ + + case STATE_SENDING_DATA: + if (test_and_clear_bit(EVENT_DATA_ERROR, + &host->pending_events)) { + set_bit(EVENT_XFER_COMPLETE, + &host->pending_events); + if (data) { + if (data->stop) + send_stop_cmd(host, data); + else { + dw_mci_start_command(host, + &host->stop, + host->stop_cmdr); + host->stop_snd = true; + } + } + /* To avoid fifo full condition */ + dw_mci_fifo_reset(host->dev, host); + state = STATE_DATA_ERROR; + break; + } + + if (!test_and_clear_bit(EVENT_XFER_COMPLETE, + &host->pending_events)) + break; + + set_bit(EVENT_XFER_COMPLETE, &host->completed_events); + prev_state = state = STATE_DATA_BUSY; + /* fall through */ + + case STATE_DATA_BUSY: + if (!test_and_clear_bit(EVENT_DATA_COMPLETE, + &host->pending_events)) + break; + + set_bit(EVENT_DATA_COMPLETE, &host->completed_events); + status = host->data_status; + if (!data) { + dev_err(host->dev, "data is null\n"); + break; + } + if (status & DW_MCI_DATA_ERROR_FLAGS) { + if (status & SDMMC_INT_DRTO) { + dev_warn(host->dev, + "data timeout error\n"); + data->error = -ETIMEDOUT; + } else if (status & SDMMC_INT_DCRC) { + if (!(host->flags & DWMMC_IN_TUNING)) { + dev_warn(host->dev, + "data CRC error\n"); + dw_mci_reg_dump(host); + } + data->error = -EILSEQ; + } else if (status & SDMMC_INT_EBE) { + if (host->dir_status == + DW_MCI_SEND_STATUS) { + /* + * No data CRC status + * was returned. + * The number of bytes + * transferred will + * be exaggerated in PIO mode. + */ + data->bytes_xfered = 0; + data->error = -ETIMEDOUT; + dev_warn(host->dev, + "Write no CRC\n"); + } else { + data->error = -EIO; + dev_warn(host->dev, + "End bit error\n"); + } + } else if (status & SDMMC_INT_SBE) { + dev_warn(host->dev, + "Start bit error (status=%08x)\n", + status); + data->error = -EIO; + } else { + dev_warn(host->dev, + "data FIFO erro (status=%08x)\n", + status); + data->error = -EIO; + } + /* + * After an error, there may be data lingering + * in the FIFO, so reset it - doing so + * generates a block interrupt, hence setting + * the scatter-gather pointer to NULL. + */ + sg_miter_stop(&host->sg_miter); + host->sg = NULL; + dw_mci_fifo_reset(host->dev, host); + } else { + data->bytes_xfered = data->blocks * data->blksz; + data->error = 0; + } + + host->data = NULL; + + if (!data->stop && !host->stop_snd) { + dw_mci_request_end(host, host->mrq); + goto unlock; + } + + if (host->mrq->sbc && !data->error) { + if (data->stop) + data->stop->error = 0; + dw_mci_request_end(host, host->mrq); + goto unlock; + } + + prev_state = state = STATE_SENDING_STOP; + if (!data->error) + send_stop_cmd(host, data); + + if (test_and_clear_bit(EVENT_DATA_ERROR, + &host->pending_events)) { + if (data->stop) + send_stop_cmd(host, data); + else { + dw_mci_start_command(host, + &host->stop, + host->stop_cmdr); + host->stop_snd = true; + } + } + /* fall through */ + + case STATE_SENDING_STOP: + if (!test_and_clear_bit(EVENT_CMD_COMPLETE, + &host->pending_events)) + break; + + if (host->mrq->cmd->error && + host->mrq->data) { + dw_mci_stop_dma(host); + sg_miter_stop(&host->sg_miter); + host->sg = NULL; + dw_mci_fifo_reset(host->dev, host); + dw_mci_ciu_reset(host->dev, host); + } + + host->cmd = NULL; + host->data = NULL; + + if (host->mrq->stop) + dw_mci_command_complete(host, host->mrq->stop); + else + host->cmd_status = 0; + dw_mci_request_end(host, host->mrq); + goto unlock; + + case STATE_DATA_ERROR: + if (!test_and_clear_bit(EVENT_XFER_COMPLETE, + &host->pending_events)) + break; + + dw_mci_stop_dma(host); + set_bit(EVENT_XFER_COMPLETE, &host->completed_events); + + state = STATE_DATA_BUSY; + break; + default: + break; + } + } while (state != prev_state); + + host->state = state; +unlock: + spin_unlock(&host->lock); +} +#endif /* CONFIG_ASCEND_HISI_MMC */ + +static void dw_mci_tasklet_func(unsigned long priv) +{ + struct dw_mci *host = (struct dw_mci *)priv; + struct mmc_data *data; + struct mmc_command *cmd; + struct mmc_request *mrq; + enum dw_mci_state state; + enum dw_mci_state prev_state; + unsigned int err; + + spin_lock(&host->lock); + + state = host->state; + data = host->data; + mrq = host->mrq; + + do { + prev_state = state; + + switch (state) { + case STATE_IDLE: + case STATE_WAITING_CMD11_DONE: + break; + + case STATE_SENDING_CMD11: + case STATE_SENDING_CMD: + if (!dw_mci_clear_pending_cmd_complete(host)) + break; + + cmd = host->cmd; + host->cmd = NULL; + set_bit(EVENT_CMD_COMPLETE, &host->completed_events); + err = dw_mci_command_complete(host, cmd); + if (cmd == mrq->sbc && !err) { + __dw_mci_start_request(host, host->slot, + mrq->cmd); + goto unlock; + } + + if (cmd->data && err) { + /* + * During UHS tuning sequence, sending the stop + * command after the response CRC error would + * throw the system into a confused state + * causing all future tuning phases to report * failure. * * In such case controller will move into a data @@ -2638,6 +3371,157 @@ static void dw_mci_handle_cd(struct dw_mci *host) msecs_to_jiffies(host->pdata->detect_delay_ms)); }
+#ifdef CONFIG_ASCEND_HISI_MMC +static irqreturn_t dw_hisi_mci_interrupt(int irq, void *dev_id) +{ + struct dw_mci *host = dev_id; + u32 pending; + int i; + u32 temp; + + pending = mci_readl(host, MINTSTS); /* read-only mask reg */ + + if (pending) { + + /* + * DTO fix - version 2.10a and below, and only if internal DMA + * is configured. + */ + if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) { + if (!pending && + ((mci_readl(host, STATUS) >> 17) & 0x1fff)) + pending |= SDMMC_INT_DATA_OVER; + } + + if (pending & SDMMC_INT_CMD_DONE) { + u32 cmd = mci_readl(host, CMD) & 0x3f; + + if (cmd == SD_SWITCH_VOLTAGE && + !(mci_readl(host, STATUS) & + SDMMC_STATUS_BUSY)) { + pending |= SDMMC_INT_RTO; + } + } + + if (pending & SDMMC_INT_HLE) { + mci_writel(host, RINTSTS, SDMMC_INT_HLE); + host->cmd_status = pending; + tasklet_schedule(&host->tasklet);/*dw_mci_tasklet_func*/ + } + + if (pending & DW_MCI_CMD_ERROR_FLAGS) { + mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS); + host->cmd_status = pending; + smp_wmb(); /* drain writebuffer */ + set_bit(EVENT_CMD_COMPLETE, &host->pending_events); + } + + if (pending & SDMMC_INT_VOLT_SWITCH) { + u32 cmd = mci_readl(host, CMD) & 0x3f; + + if (cmd == SD_SWITCH_VOLTAGE) { + mci_writel(host, + RINTSTS, SDMMC_INT_VOLT_SWITCH); + + dw_mci_cmd_interrupt(host, pending); + + mci_writel(host, RINTSTS, SDMMC_INT_RESP_ERR); + temp = mci_readl(host, INTMASK); + temp |= SDMMC_INT_RESP_ERR; + mci_writel(host, INTMASK, temp); + } + } + + if (pending & DW_MCI_DATA_ERROR_FLAGS) { + /* if there is an error report DATA_ERROR */ + mci_writel(host, RINTSTS, DW_MCI_DATA_ERROR_FLAGS); + host->data_status = pending; + smp_wmb(); /* drain writebuffer */ + set_bit(EVENT_DATA_ERROR, &host->pending_events); + tasklet_schedule(&host->tasklet);/*dw_mci_tasklet_func*/ + } + + if (pending & SDMMC_INT_DATA_OVER) { + mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER); + if (!host->data_status) + host->data_status = pending; + smp_wmb(); /* drain writebuffer */ + if (host->dir_status == DW_MCI_RECV_STATUS) { + if (host->sg != NULL) + dw_mci_read_data_pio(host, true); + } + set_bit(EVENT_DATA_COMPLETE, &host->pending_events); + tasklet_schedule(&host->tasklet);/*dw_mci_tasklet_func*/ + } + + if (pending & SDMMC_INT_RXDR) { + mci_writel(host, RINTSTS, SDMMC_INT_RXDR); + if (host->dir_status == DW_MCI_RECV_STATUS && host->sg) + dw_mci_read_data_pio(host, false); + } + + if (pending & SDMMC_INT_TXDR) { + mci_writel(host, RINTSTS, SDMMC_INT_TXDR); + if (host->dir_status == DW_MCI_SEND_STATUS && host->sg) + dw_mci_write_data_pio(host); + } + + if (pending & SDMMC_INT_CMD_DONE) { + mci_writel(host, RINTSTS, SDMMC_INT_CMD_DONE); + dw_mci_cmd_interrupt(host, pending); + } + + if (pending & SDMMC_INT_CD) { + mci_writel(host, RINTSTS, SDMMC_INT_CD); + /* dw_mci_work_routine_card */ + queue_work(host->card_workqueue, + &host->card_work); + } + + /* Handle SDIO Interrupts */ + for (i = 0; i < host->num_slots; i++) { + struct dw_mci_slot *slot = host->slot; + + if (pending & SDMMC_INT_SDIO(i)) { + mci_writel(host, RINTSTS, SDMMC_INT_SDIO(i)); + mmc_signal_sdio_irq(slot->mmc); + } + } + + } + + if (host->use_dma != TRANS_MODE_IDMAC) + return IRQ_HANDLED; + + +#ifdef CONFIG_MMC_DW_IDMAC + if (host->dma_64bit_address == SDMMC_32_BIT_DMA) { + /* Handle 32 bit DMA interrupts */ + pending = mci_readl(host, IDSTS); + if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) { + mci_writel(host, + IDSTS, + SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI); + mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI); + host->dma_ops->complete(host); + } + } else { + /* Handle 64 bit DMA interrupts */ + pending = mci_readl(host, IDSTS64); + if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) { + mci_writel(host, + IDSTS64, + SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI); + mci_writel(host, IDSTS64, SDMMC_IDMAC_INT_NI); + host->dma_ops->complete(host); + } + } +#endif + + return IRQ_HANDLED; +} +#endif /* CONFIG_ASCEND_HISI_MMC */ + static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) { struct dw_mci *host = dev_id; @@ -2817,6 +3701,172 @@ static int dw_mci_init_slot_caps(struct dw_mci_slot *slot) return 0; }
+#ifdef CONFIG_ASCEND_HISI_MMC +static int dw_hisi_mci_init_slot(struct dw_mci *host) +{ + struct mmc_host *mmc; + struct dw_mci_slot *slot; + const struct dw_mci_drv_data *drv_data = host->drv_data; + struct dw_mci_hs_priv_data *priv = host->priv; + int ctrl_id, ret; + u8 bus_width; + unsigned int id = 0; + + mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev); + if (!mmc) + return -ENOMEM; + + slot = mmc_priv(mmc); + slot->id = id; + slot->mmc = mmc; + slot->host = host; + host->slot = slot; + + mmc->ops = &dw_hisi_mci_ops; + mmc->f_min = DIV_ROUND_UP(host->bus_hz, 510); + mmc->f_max = host->bus_hz; + dw_mci_set_freq_specific(priv, slot->host->bus_hz, &(mmc->f_max)); + + mmc->sdio_present = 0; + + if (host->pdata->get_ocr) + mmc->ocr_avail = host->pdata->get_ocr(id); + else + mmc->ocr_avail = MMC_VDD_28_29; + + /* + * Start with slot power disabled, it will be enabled when a card + * is detected. + */ + if (host->pdata->setpower) + host->pdata->setpower(id, 0); + + + if (host->pdata->caps) + mmc->caps = host->pdata->caps; + + if (host->pdata->pm_caps) { + mmc->pm_caps = host->pdata->pm_caps; + mmc->pm_flags = mmc->pm_caps; + } + + if (host->dev->of_node) { + ctrl_id = of_alias_get_id(host->dev->of_node, "mshc"); + if (ctrl_id < 0) + ctrl_id = 0; + } else { + ctrl_id = to_platform_device(host->dev)->id; + } + + if (drv_data && drv_data->caps) + mmc->caps |= drv_data->caps[ctrl_id]; + + if (host->pdata->caps2) + mmc->caps2 = host->pdata->caps2; + + if (host->pdata->get_bus_wd) + bus_width = host->pdata->get_bus_wd(slot->id); + else + bus_width = 1; + + dev_info(host->dev, "%s %d mmc->caps: 0x%x, mmc->caps2: 0x%x\n", + __func__, __LINE__, mmc->caps, mmc->caps2); + + switch (bus_width) { + case 8: + mmc->caps |= MMC_CAP_8_BIT_DATA; + break; + case 4: + mmc->caps |= MMC_CAP_4_BIT_DATA; + break; + } + + if (host->pdata->quirks & DW_MCI_QUIRK_HIGHSPEED) + mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; + + if (host->pdata->blk_settings) { + mmc->max_segs = host->pdata->blk_settings->max_segs; + mmc->max_blk_size = host->pdata->blk_settings->max_blk_size; + mmc->max_blk_count = host->pdata->blk_settings->max_blk_count; + mmc->max_req_size = host->pdata->blk_settings->max_req_size; + mmc->max_seg_size = host->pdata->blk_settings->max_seg_size; + } else { + /* Useful defaults if platform data is unset. */ +#ifdef CONFIG_MMC_DW_IDMAC + mmc->max_segs = host->ring_size; + mmc->max_blk_size = 65536; + mmc->max_seg_size = 0x1000; + mmc->max_req_size = mmc->max_seg_size * host->ring_size; + mmc->max_blk_count = mmc->max_req_size / 512; +#else + mmc->max_segs = 64; + mmc->max_blk_size = 65536; /* BLKSIZ is 16 bits */ + mmc->max_blk_count = 512; + mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; + mmc->max_seg_size = mmc->max_req_size; +#endif /* CONFIG_MMC_DW_IDMAC */ + } + + if (priv->chip_platform == SDMMC_ASIC_PLATFORM) { + host->pinctrl = devm_pinctrl_get(mmc_dev(mmc)); + if (IS_ERR(host->pinctrl)) { + dev_warn(host->dev, "could not get pinctrl\n"); + host->pinctrl = NULL; + } + + host->pins_default = pinctrl_lookup_state(host->pinctrl, + PINCTRL_STATE_DEFAULT); + /* enable pins to be muxed in and configured */ + if (IS_ERR(host->pins_default)) { + dev_warn(host->dev, "could not get default pinstate\n"); + host->pins_default = NULL; + } + + host->pins_idle = pinctrl_lookup_state(host->pinctrl, + PINCTRL_STATE_IDLE); + if (IS_ERR(host->pins_idle)) { + dev_warn(host->dev, "could not get default pinstate\n"); + host->pins_idle = NULL; + } + } else { + host->pinctrl = NULL; + host->pins_default = NULL; + host->pins_idle = NULL; + } + + /*BEGIN vmmc and vqmmc force to NULL for ascend plat, 2018/04/26*/ + host->vmmc = NULL; + host->vqmmc = NULL; + /*BEGIN vmmc and vqmmc force to NULL for ascend plat, 2018/04/26*/ + + if (drv_data && drv_data->cd_detect_init) + drv_data->cd_detect_init(host); + + if (hisi_dw_mci_get_cd(mmc)) + set_bit(DW_MMC_CARD_PRESENT, &slot->flags); + else + clear_bit(DW_MMC_CARD_PRESENT, &slot->flags); + + + ret = mmc_add_host(mmc); + if (ret) + goto err_setup_bus; + +#if defined(CONFIG_DEBUG_FS) + dw_mci_init_debugfs(slot); +#endif + + /* Card initially undetected */ + slot->last_detect_state = 0; + + return 0; + +err_setup_bus: + mmc_free_host(mmc); + return -EINVAL; +} +#endif /* CONFIG_ASCEND_HISI_MMC */ + static int dw_mci_init_slot(struct dw_mci *host) { struct mmc_host *mmc; @@ -2895,12 +3945,39 @@ static int dw_mci_init_slot(struct dw_mci *host)
static void dw_mci_cleanup_slot(struct dw_mci_slot *slot) { +#ifdef CONFIG_ASCEND_HISI_MMC + unsigned int id = 0; + + if (mmc_is_ascend_customized(slot->host->dev)) { + /* Shutdown detect IRQ */ + if (slot->host->pdata->exit) + slot->host->pdata->exit(id); + } +#endif /* Debugfs stuff is cleaned up by mmc core */ mmc_remove_host(slot->mmc); slot->host->slot = NULL; mmc_free_host(slot->mmc); }
+static inline void hisi_set_disc_dz(struct dw_mci *host, int flag) +{ +#ifdef CONFIG_ASCEND_HISI_MMC + if (flag == 64) + host->desc_sz = DW_MCI_DESC_SZ_64BIT; + else if (flag == 32) + host->desc_sz = DW_MCI_DESC_SZ; +#endif +} + +static inline void hisi_set_dma_mask_32bit(struct dw_mci *host) +{ +#ifdef CONFIG_ASCEND_HISI_MMC + host->dma_mask = DMA_BIT_MASK(32); + host->dev->dma_mask = &host->dma_mask; +#endif +} + static void dw_mci_init_dma(struct dw_mci *host) { int addr_config; @@ -2937,6 +4014,8 @@ static void dw_mci_init_dma(struct dw_mci *host)
if (addr_config == 1) { /* host supports IDMAC in 64-bit address mode */ + if (mmc_is_ascend_customized(host->dev)) + hisi_set_disc_dz(host, 64); host->dma_64bit_address = 1; dev_info(host->dev, "IDMAC supports 64-bit address mode.\n"); @@ -2948,12 +4027,23 @@ static void dw_mci_init_dma(struct dw_mci *host) host->dma_64bit_address = 0; dev_info(host->dev, "IDMAC supports 32-bit address mode.\n"); + if (mmc_is_ascend_customized(host->dev)) { + hisi_set_disc_dz(host, 32); + hisi_set_dma_mask_32bit(host); + } }
/* Alloc memory for sg translation */ - host->sg_cpu = dmam_alloc_coherent(host->dev, - DESC_RING_BUF_SZ, - &host->sg_dma, GFP_KERNEL); + if (mmc_is_ascend_customized(host->dev)) + host->sg_cpu = + dmam_alloc_coherent(host->dev, + HISI_DISC_DZ(host) * DESC_RING_BUF_SZ, + &host->sg_dma, GFP_KERNEL); + else + host->sg_cpu = + dmam_alloc_coherent(host->dev, + DESC_RING_BUF_SZ, + &host->sg_dma, GFP_KERNEL); if (!host->sg_cpu) { dev_err(host->dev, "%s: could not alloc DMA memory\n", @@ -3114,6 +4204,140 @@ static void dw_mci_dto_timer(struct timer_list *t) }
#ifdef CONFIG_OF + +#ifdef CONFIG_ASCEND_HISI_MMC +static struct dw_mci_of_quirks { + char *quirk; + int id; +} of_quirks[] = { + { + .quirk = "supports-highspeed", + .id = DW_MCI_QUIRK_HIGHSPEED, + }, { + .quirk = "broken-cd", + .id = DW_MCI_QUIRK_BROKEN_CARD_DETECTION, + }, { + .quirk = "disable-wp", + .id = DW_MCI_QUIRK_NO_WRITE_PROTECT, + }, +}; + +static inline void hisi_get_num_slot_from_dt(struct dw_mci *host, + struct dw_mci_board *pdata) +{ + struct device *dev = host->dev; + + if (!mmc_is_ascend_customized(dev)) + return; + /* find out number of slots supported */ + if (device_property_read_u32(dev, "num-slots", &pdata->num_slots)) { + dev_info(dev, "num-slots property not found\n"); + dev_info(dev, "assuming 1 slot is available\n"); + pdata->num_slots = 1; + } +} +static inline void hisi_get_quirks_from_dt(struct dw_mci *host, + struct dw_mci_board *pdata) +{ + struct device *dev = host->dev; + struct device_node *np = host->dev->of_node; + int idx; + + if (!mmc_is_ascend_customized(dev)) + return; + /* get quirks */ + for (idx = 0; idx < ARRAY_SIZE(of_quirks); idx++) + if (of_get_property(np, of_quirks[idx].quirk, NULL)) { + pdata->quirks |= of_quirks[idx].id; + if (of_quirks[idx].id == + DW_MCI_QUIRK_NO_WRITE_PROTECT) { + pdata->caps2 |= MMC_CAP2_NO_WRITE_PROTECT; + } + } +} +static void hisi_parse_card_detection_from_dt(struct dw_mci *host, + struct dw_mci_board *pdata) +{ + const struct dw_mci_drv_data *drv_data = host->drv_data; + struct device *dev = host->dev; + struct device_node *np = dev->of_node; + + if (!mmc_is_ascend_customized(dev)) + return; + + if (of_find_property(np, "non-removable", NULL)) + pdata->caps |= MMC_CAP_NONREMOVABLE; + + if (of_find_property(np, "keep-power-in-suspend", NULL)) + pdata->pm_caps |= MMC_PM_KEEP_POWER; + + if (of_find_property(np, "keep-power-ignore-pm-notify", NULL)) + pdata->pm_caps |= MMC_PM_IGNORE_PM_NOTIFY; + + if (of_find_property(np, "enable-sdio-wakeup", NULL)) + pdata->pm_caps |= MMC_PM_WAKE_SDIO_IRQ; + + if (of_find_property(np, "caps2-mmc-packed-command", NULL)) + pdata->caps2 |= MMC_CAP2_PACKED_CMD; + + if (of_find_property(np, "caps2-mmc-hs200-1_8v", NULL)) + pdata->caps2 |= MMC_CAP2_HS200_1_8V_SDR; + + if (of_find_property(np, "caps2-mmc-hs200-1_2v", NULL)) + pdata->caps2 |= MMC_CAP2_HS200_1_2V_SDR; + + if (of_find_property(np, "caps2-mmc-cache-ctrl", NULL)) { + dev_info(dev, "caps2-mmc-cache-ctrl is set in dts.\n"); + dev_info(dev, "normal mode cache ctrl on\n"); + pdata->caps2 |= MMC_CAP2_CACHE_CTRL; + } + + if (of_find_property(np, "caps2-wifi-support-cmd11", NULL)) { + dev_info(dev, + "dw_mci:%d %s find wifi support cmd11 in dts\n", + host->hw_mmc_id, __func__); + pdata->caps2 |= MMC_CAP2_SUPPORT_WIFI_CMD11; + } + + if (of_find_property(np, "caps-wifi-no-lowpwr", NULL)) { + dev_info(dev, + "dw_mci:%d %s find wifi support no_lowpwr in dts\n", + host->hw_mmc_id, __func__); + pdata->caps2 |= MMC_CAP2_WIFI_NO_LOWPWR; + } + + if (of_find_property(np, "caps2-support-wifi", NULL)) { + pr_info("dw_mci:%d %s find caps2-support-wifi in dts\n", + host->hw_mmc_id, __func__); + pdata->caps2 |= MMC_CAP2_SUPPORT_WIFI; + } + + if (of_find_property(np, "caps2-support-via-modem", NULL)) { + pr_info("dw_mci:%d %s find caps2-support-via-modem in dts\n", + host->hw_mmc_id, __func__); + pdata->caps2 |= MMC_CAP2_SUPPORT_VIA_MODEM; + } + + if (of_find_property(np, "full-pwr-cycle", NULL)) + pdata->caps2 |= MMC_CAP2_FULL_PWR_CYCLE; + + if (of_find_property(np, "caps2-mmc-ddr50-notify", NULL)) { + if (drv_data && drv_data->caps) + drv_data->caps[0] |= + MMC_CAP_1_8V_DDR | MMC_CAP_UHS_DDR50; + } +} + +#else /* CONFIG_ASCEND_HISI_MMC */ +static inline void hisi_get_num_slot_from_dt(struct dw_mci *host, + struct dw_mci_board *pdata) {} +static inline void hisi_get_quirks_from_dt(struct dw_mci *host, + struct dw_mci_board *pdata) {} +static inline void hisi_parse_card_detection_from_dt(struct dw_mci *host, + struct dw_mci_board *pdata) {} +#endif /* CONFIG_ASCEND_HISI_MMC */ + + static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) { struct dw_mci_board *pdata; @@ -3133,6 +4357,8 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) return ERR_PTR(-EPROBE_DEFER); }
+ hisi_get_num_slot_from_dt(host, pdata); + hisi_get_quirks_from_dt(host, pdata); if (device_property_read_u32(dev, "fifo-depth", &pdata->fifo_depth)) dev_info(dev, "fifo-depth property not found, using value of FIFOTH register as default\n"); @@ -3153,7 +4379,7 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) if (ret) return ERR_PTR(ret); } - + hisi_parse_card_detection_from_dt(host, pdata); return pdata; }
@@ -3175,15 +4401,144 @@ static void dw_mci_enable_cd(struct dw_mci *host) */ if (host->slot->mmc->caps & MMC_CAP_NEEDS_POLL) return; - - if (mmc_gpio_get_cd(host->slot->mmc) < 0) { - spin_lock_irqsave(&host->irq_lock, irqflags); + if (mmc_is_ascend_customized(host->dev)) { temp = mci_readl(host, INTMASK); temp |= SDMMC_INT_CD; mci_writel(host, INTMASK, temp); - spin_unlock_irqrestore(&host->irq_lock, irqflags); + } else + if (mmc_gpio_get_cd(host->slot->mmc) < 0) { + spin_lock_irqsave(&host->irq_lock, irqflags); + temp = mci_readl(host, INTMASK); + temp |= SDMMC_INT_CD; + mci_writel(host, INTMASK, temp); + spin_unlock_irqrestore(&host->irq_lock, irqflags); + } +} + +#ifdef CONFIG_ASCEND_HISI_MMC +static inline int dw_mci_hisi_setup_clock(struct dw_mci *host) +{ + const struct dw_mci_drv_data *drv_data = host->drv_data; + int ret; + + if (!mmc_is_ascend_customized(host->dev)) + return 0; + if (drv_data && drv_data->setup_clock) + ret = drv_data->setup_clock(host); + return ret; +} +static int dw_mci_setup_slot(struct dw_mci *host) +{ + int ret; + + if (!mmc_is_ascend_customized(host->dev)) + return dw_mci_init_slot(host); + + ret = dw_hisi_mci_init_slot(host); + if (ret) { + destroy_workqueue(host->card_workqueue); + return ret; + } + + dev_info(host->dev, "slots initialized\n"); + if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) + dev_info(host->dev, + "Internal DMAC interrupt fix enabled.\n"); + return 0; +} + +static void hisi_dw_mci_clear_interrupt(struct dw_mci *host) +{ + mci_writel(host, RINTSTS, 0xFFFFFFFF); +#ifdef CONFIG_MMC_DW_IDMAC + if (host->dma_64bit_address == SDMMC_32_BIT_DMA) + /* 32 bit dma interrupt clear */ + mci_writel(host, IDSTS, IDMAC_INT_CLR); + else + /* 64 bit dma interrupt clear */ + mci_writel(host, IDSTS64, IDMAC_INT_CLR); +#endif /* CONFIG_MMC_DW_IDMAC */ +} +static int hisi_dw_mci_init_resource(struct dw_mci *host) +{ + int ret; + + tasklet_init(&host->tasklet, dw_hisi_mci_tasklet_func, + (unsigned long)host); + + host->card_workqueue = alloc_workqueue("dw-mci-card/%d", + WQ_MEM_RECLAIM, 1, host->hw_mmc_id); + if (!host->card_workqueue) + return -ENOBUFS; + + INIT_WORK(&host->card_work, dw_mci_work_routine_card); + timer_setup(&host->timer, dw_mci_timeout_timer, 0); + ret = devm_request_irq(host->dev, host->irq, + dw_hisi_mci_interrupt, + host->irq_flags, + "dw-mci", host); + if (ret) { + destroy_workqueue(host->card_workqueue); + return ret; + } + if (host->pdata->num_slots) + host->num_slots = host->pdata->num_slots; + else + host->num_slots = + ((mci_readl(host, HCON) >> 1) & 0x1F) + 1; + return 0; +} + +static inline void dw_mci_disable_vmmc_regulator(struct dw_mci *host) +{ + struct dw_mci_hs_priv_data *priv = host->priv; + + if (host->vmmc) { + if (regulator_disable(host->vmmc)) + dev_warn(host->dev, "regulator_disable vmmc failed\n"); + return; + } + if ((host->hw_mmc_id == DWMMC_SD_ID) && + (priv->chip_platform == SDMMC_ASIC_PLATFORM)) { + if (pmu_ldo16_disable()) + dev_warn(host->dev, "pmu_ldo16_disable failed\n"); + return; } } +static inline void dw_mci_disable_vqmmc_regulator(struct dw_mci *host) +{ + struct dw_mci_hs_priv_data *priv = host->priv; + + if (host->vqmmc) { + if (regulator_disable(host->vqmmc)) + dev_warn(host->dev, "regulator_disable vqmmc failed\n"); + return; + } + if ((host->hw_mmc_id == DWMMC_SD_ID) && + (priv->chip_platform == SDMMC_ASIC_PLATFORM)) { + if (pmu_ldo9_disable()) + dev_warn(host->dev, "pmu_ldo9_disable failed\n"); + return; + } +} + +#else /* CONFIG_ASCEND_HISI_MMC */ +static inline int dw_mci_hisi_setup_clock(struct dw_mci *host) +{ + return 0; +} +static inline int dw_mci_setup_slot(struct dw_mci *host) +{ + return dw_mci_init_slot(host); +} +static inline int hisi_dw_mci_init_resource(struct dw_mci *host) +{ + return 0; +} +static inline void hisi_dw_mci_clear_interrupt(struct dw_mci *host) {} +static inline void dw_mci_disable_vmmc_regulator(struct dw_mci *host) {} +static inline void dw_mci_disable_vqmmc_regulator(struct dw_mci *host) {} +#endif /* CONFIG_ASCEND_HISI_MMC */
int dw_mci_probe(struct dw_mci *host) { @@ -3233,6 +4588,13 @@ int dw_mci_probe(struct dw_mci *host) host->bus_hz = clk_get_rate(host->ciu_clk); }
+ ret = dw_mci_hisi_setup_clock(host); + if (ret) { + dev_err(host->dev, + "implementation specific clock setup failed\n"); + goto err_clk_ciu; + } + if (!host->bus_hz) { dev_err(host->dev, "Platform data must supply bus speed\n"); @@ -3240,21 +4602,28 @@ int dw_mci_probe(struct dw_mci *host) goto err_clk_ciu; }
- if (!IS_ERR(host->pdata->rstc)) { - reset_control_assert(host->pdata->rstc); - usleep_range(10, 50); - reset_control_deassert(host->pdata->rstc); - } + if (mmc_is_ascend_customized(host->dev)) { +#ifdef CONFIG_ASCEND_HISI_MMC + host->quirks = host->pdata->quirks; + host->sd_reinit = 0; + host->sd_hw_timeout = 0; +#endif + } else { + if (!IS_ERR(host->pdata->rstc)) { + reset_control_assert(host->pdata->rstc); + usleep_range(10, 50); + reset_control_deassert(host->pdata->rstc); + }
- if (drv_data && drv_data->init) { - ret = drv_data->init(host); - if (ret) { - dev_err(host->dev, - "implementation specific init failed\n"); - goto err_clk_ciu; + if (drv_data && drv_data->init) { + ret = drv_data->init(host); + if (ret) { + dev_err(host->dev, + "implementation specific init failed\n"); + goto err_clk_ciu; + } } } - timer_setup(&host->cmd11_timer, dw_mci_cmd11_timer, 0); timer_setup(&host->cto_timer, dw_mci_cto_timer, 0); timer_setup(&host->dto_timer, dw_mci_dto_timer, 0); @@ -3344,16 +4713,26 @@ int dw_mci_probe(struct dw_mci *host) else host->fifo_reg = host->regs + DATA_240A_OFFSET;
- tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host); - ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt, - host->irq_flags, "dw-mci", host); - if (ret) - goto err_dmaunmap; + if (mmc_is_ascend_customized(host->dev)) { + ret = hisi_dw_mci_init_resource(host); + if (ret) + goto err_dmaunmap; + } else { + tasklet_init(&host->tasklet, dw_mci_tasklet_func, + (unsigned long)host); + + ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt, + host->irq_flags, "dw-mci", host); + if (ret) + goto err_dmaunmap; + }
/* * Enable interrupts for command done, data over, data empty, * receive ready and error such as transmit, receive timeout, crc error */ + if (mmc_is_ascend_customized(host->dev)) + hisi_dw_mci_clear_interrupt(host); mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER | SDMMC_INT_TXDR | SDMMC_INT_RXDR | DW_MCI_ERROR_FLAGS); @@ -3365,12 +4744,11 @@ int dw_mci_probe(struct dw_mci *host) host->irq, width, fifo_size);
/* We need at least one slot to succeed */ - ret = dw_mci_init_slot(host); + ret = dw_mci_setup_slot(host); if (ret) { dev_dbg(host->dev, "slot %d init failed\n", i); goto err_dmaunmap; } - /* Now that slots are all setup, we can enable card detect */ dw_mci_enable_cd(host);
@@ -3380,7 +4758,9 @@ int dw_mci_probe(struct dw_mci *host) if (host->use_dma && host->dma_ops->exit) host->dma_ops->exit(host);
- if (!IS_ERR(host->pdata->rstc)) + if (mmc_is_ascend_customized(host->dev)) + dw_mci_disable_vmmc_regulator(host); + else if (!IS_ERR(host->pdata->rstc)) reset_control_assert(host->pdata->rstc);
err_clk_ciu: @@ -3402,14 +4782,26 @@ void dw_mci_remove(struct dw_mci *host) mci_writel(host, RINTSTS, 0xFFFFFFFF); mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */
+ if (mmc_is_ascend_customized(host->dev)) { +#ifdef CONFIG_ASCEND_HISI_MMC + host->flags &= ~DWMMC_IN_TUNING; + host->flags &= ~DWMMC_TUNING_DONE; + host->sd_reinit = 0; +#endif + } /* disable clock to CIU */ mci_writel(host, CLKENA, 0); mci_writel(host, CLKSRC, 0); - + if (mmc_is_ascend_customized(host->dev)) { + HISI_DEL_TIMER_SYNC(host); + HISI_DESTROY_CARD_WORKQUEUE(host); + } if (host->use_dma && host->dma_ops->exit) host->dma_ops->exit(host); - - if (!IS_ERR(host->pdata->rstc)) + if (mmc_is_ascend_customized(host->dev)) { + dw_mci_disable_vmmc_regulator(host); + dw_mci_disable_vqmmc_regulator(host); + } else if (!IS_ERR(host->pdata->rstc)) reset_control_assert(host->pdata->rstc);
clk_disable_unprepare(host->ciu_clk); @@ -3417,8 +4809,6 @@ void dw_mci_remove(struct dw_mci *host) } EXPORT_SYMBOL(dw_mci_remove);
- - #ifdef CONFIG_PM int dw_mci_runtime_suspend(struct device *dev) { @@ -3507,6 +4897,62 @@ int dw_mci_runtime_resume(struct device *dev) EXPORT_SYMBOL(dw_mci_runtime_resume); #endif /* CONFIG_PM */
+#ifdef CONFIG_ASCEND_HISI_MMC +#ifdef CONFIG_PM_SLEEP +/* + * we should probably disable the clock to the card in the suspend path. + */ +int dw_mci_suspend(struct dw_mci *host) +{ + return 0; +} +EXPORT_SYMBOL(dw_mci_suspend); + +int dw_mci_resume(struct dw_mci *host) +{ + int i, ret; + + if (!mci_wait_reset(host->dev, host)) { + ret = -ENODEV; + return ret; + } + + if (host->use_dma && host->dma_ops->init) + host->dma_ops->init(host); + + /* Restore the old value at FIFOTH register */ + mci_writel(host, FIFOTH, host->fifoth_val); + host->prev_blksz = 0; + + mci_writel(host, RINTSTS, 0xFFFFFFFF); + mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER | + SDMMC_INT_TXDR | SDMMC_INT_RXDR | + DW_MCI_ERROR_FLAGS); + mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); + + for (i = 0; i < host->num_slots; i++) { + struct dw_mci_slot *slot = host->slot; + + if (!slot) + continue; + if (slot->mmc->ios.power_mode == MMC_POWER_OFF) + continue; + + if (slot->mmc->pm_flags & MMC_PM_KEEP_POWER) { + dw_mci_set_ios(slot->mmc, &slot->mmc->ios); + dw_mci_setup_bus(slot, true); + } + } + + /* Now that slots are all setup, we can enable card detect */ + dw_mci_enable_cd(host); + + return 0; +} +EXPORT_SYMBOL(dw_mci_resume); +#endif /* CONFIG_PM_SLEEP */ +#endif /* CONFIG_ASCEND_HISI_MMC */ + static int __init dw_mci_init(void) { pr_info("Synopsys Designware Multimedia Card Interface Driver\n"); diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 46e9f8ec53980..0a0d2834444f9 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -20,6 +20,13 @@ #include <linux/reset.h> #include <linux/interrupt.h>
+#ifdef CONFIG_ASCEND_HISI_MMC +#include "dw_mmc_hisi.h" +#define MAX_MCI_SLOTS 2 +#define TUNING_INIT_CONFIG_NUM 7 +#define TUNING_INIT_TIMING_MODE 10 +#endif + enum dw_mci_state { STATE_IDLE = 0, STATE_SENDING_CMD, @@ -234,6 +241,58 @@ struct dw_mci { struct timer_list cmd11_timer; struct timer_list cto_timer; struct timer_list dto_timer; +#ifdef CONFIG_ASCEND_HISI_MMC + struct work_struct card_work; + u32 num_slots; + u16 data_offset; + struct clk *parent_clk; + int wifi_sdio_sdr104_160M; + int wifi_sdio_sdr104_177M; + struct dw_mci_slot *cur_slot; + struct mmc_command stop; + bool stop_snd; + int saved_tuning_phase; + int tuning_result_flag; + unsigned int desc_sz; + u64 dma_mask; /* custom DMA mask */ + + struct workqueue_struct *card_workqueue; + /* S/W reset timer */ + struct timer_list timer; + /* pinctrl handles */ + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_idle; + + struct regulator *vmmc; /* Power regulator */ + struct regulator *vqmmc; /* Signaling regulator (vccq) */ + unsigned int flags; /* Host attributes */ +#define DWMMC_IN_TUNING (1 << 5) /* Host is doing tuning */ +#define DWMMC_TUNING_DONE (1 << 6) /* Host initialization tuning done */ + /* Workaround flags */ + u32 quirks; + int current_div; /* record current div */ + int tuning_current_sample; /* record current sample */ + int tuning_init_sample; /* record the initial sample */ + int tuning_move_sample; /* record the move sample */ + int tuning_move_count; /* record the move count */ + unsigned int tuning_sample_flag; /* record the sample OK or NOT */ + int tuning_move_start; /* tuning move start flag */ + + +#define DWMMC_EMMC_ID 0 +#define DWMMC_SD_ID 1 +#define DWMMC_SDIO_ID 2 + int hw_mmc_id; /* Hardware mmc id */ + int sd_reinit; + int sd_hw_timeout; + + u32 clock; /* Current clock (MHz) */ + /* Saved clock for dynamic clock gating (MHz) */ + u32 clock_to_restore; + bool tuning_done; + bool tuning_needed; /* tuning move start flag */ +#endif };
/* DMA ops for Internal/External DMAC interface */ @@ -247,6 +306,40 @@ struct dw_mci_dma_ops { void (*exit)(struct dw_mci *host); };
+#ifdef CONFIG_ASCEND_HISI_MMC +/* IP Quirks/flags. */ +/* DTO fix for command transmission with IDMAC configured */ +#define DW_MCI_QUIRK_IDMAC_DTO BIT(0) +/* delay needed between retries on some 2.11a implementations */ +#define DW_MCI_QUIRK_RETRY_DELAY BIT(1) +/* High Speed Capable - Supports HS cards (up to 50MHz) */ +#define DW_MCI_QUIRK_HIGHSPEED BIT(2) +/* Unreliable card detection */ +#define DW_MCI_QUIRK_BROKEN_CARD_DETECTION BIT(3) +/* No write protect */ +#define DW_MCI_QUIRK_NO_WRITE_PROTECT BIT(4) + +/* Slot level quirks */ +/* This slot has no write protect */ +#define DW_MCI_SLOT_QUIRK_NO_WRITE_PROTECT BIT(0) + + +struct block_settings { + unsigned short max_segs; /* see blk_queue_max_segments */ + unsigned int max_blk_size; /* maximum size of one mmc block */ + unsigned int max_blk_count; /* maximum number of blocks in one req*/ + unsigned int max_req_size; /* maximum number of bytes in one req*/ + unsigned int max_seg_size; /* see blk_queue_max_segment_size */ +}; +#define HISI_DISC_DZ(x) x->desc_sz +#define HISI_DEL_TIMER_SYNC(x) del_timer_sync(&((x)->timer)) +#define HISI_DESTROY_CARD_WORKQUEUE(x) destroy_workqueue((x)->card_workqueue) +#else +#define HISI_DISC_DZ(x) 1 +#define HISI_DEL_TIMER_SYNC(x) +#define HISI_DESTROY_CARD_WORKQUEUE(x) +#endif /* CONFIG_ASCEND_HISI_MMC */ + struct dma_pdata;
/* Board platform data */ @@ -269,6 +362,23 @@ struct dw_mci_board { struct reset_control *rstc; struct dw_mci_dma_ops *dma_ops; struct dma_pdata *data; +#ifdef CONFIG_ASCEND_HISI_MMC + u32 num_slots; + u32 quirks; /* Workaround / Quirk flags */ + + struct block_settings *blk_settings; + int (*get_cd)(struct dw_mci *host, u32 slot_id); + int (*get_ocr)(u32 slot_id); + int (*get_bus_wd)(u32 slot_id); + /* + * Enable power to selected slot and set voltage to desired level. + * Voltage levels are specified using MMC_VDD_xxx defines defined + * in linux/mmc/host.h file. + */ + void (*setpower)(u32 slot_id, u32 volt); + void (*exit)(u32 slot_id); + void (*select_slot)(u32 slot_id); +#endif };
#define DW_MMC_240A 0x240a @@ -505,6 +615,30 @@ extern int dw_mci_runtime_suspend(struct device *device); extern int dw_mci_runtime_resume(struct device *device); #endif
+#define SDMMC_64_BIT_DMA 1 +#define SDMMC_32_BIT_DMA 0 +#ifdef CONFIG_ASCEND_HISI_MMC +#define SDMMC_CMD_ONLY_CLK (SDMMC_CMD_START | SDMMC_CMD_UPD_CLK | \ + SDMMC_CMD_PRV_DAT_WAIT) +#define SDMMC_SET_RD_THLD(v, x) (((v) & 0x1FFF) << 16 | (x)) + +extern void dw_mci_set_cd(struct dw_mci *host); +extern void dw_mci_stop_dma(struct dw_mci *host); +extern void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq); +void dw_mci_idmac_reset(struct dw_mci *host); + +#ifdef CONFIG_PM +extern int dw_mci_suspend(struct dw_mci *host); +extern int dw_mci_resume(struct dw_mci *host); +#endif /* CONFIG_PM */ +int hisi_dw_mci_get_cd(struct mmc_host *mmc); +#else +static inline int hisi_dw_mci_get_cd(struct mmc_host *mmc) +{ + return 0; +} +#endif /* CONFIG_ASCEND_HISI_MMC */ + /** * struct dw_mci_slot - MMC slot state * @mmc: The mmc_host representing this slot. @@ -541,6 +675,12 @@ struct dw_mci_slot { #define DW_MMC_CARD_NEEDS_POLL 4 int id; int sdio_id; +#ifdef CONFIG_ASCEND_HISI_MMC + unsigned int quirks; + int wp_gpio; + int last_detect_state; + int sdio_wakelog_switch; +#endif };
/** @@ -567,5 +707,20 @@ struct dw_mci_drv_data { struct mmc_ios *ios); int (*switch_voltage)(struct mmc_host *mmc, struct mmc_ios *ios); +#ifdef CONFIG_ASCEND_HISI_MMC + int (*setup_clock)(struct dw_mci *host); + void (*prepare_command)(struct dw_mci *host, u32 *cmdr); + int (*cd_detect_init)(struct dw_mci *host); + + int (*tuning_find_condition)(struct dw_mci *host, int timing); + void (*tuning_set_current_state)(struct dw_mci *host, int ok); + int (*tuning_move)(struct dw_mci *host, int timing, int start); + int (*slowdown_clk)(struct dw_mci *host, int timing); + int (*execute_tuning_hisi)(struct dw_mci_slot *slot, + u32 opcode, struct dw_mci_tuning_data *tuning_data); + int (*start_signal_voltage_switch)(struct mmc_host *mmc, + struct mmc_ios *ios); + void (*work_fail_reset)(struct dw_mci *host); +#endif }; #endif /* _DW_MMC_H_ */ diff --git a/drivers/mmc/host/dw_mmc_extern.h b/drivers/mmc/host/dw_mmc_extern.h new file mode 100644 index 0000000000000..04d8c23f39e9a --- /dev/null +++ b/drivers/mmc/host/dw_mmc_extern.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _DW_MMC_EXTERN_H +#define _DW_MMC_EXTERN_H + +#include <linux/device.h> +#include <linux/workqueue.h> +#include <linux/timer.h> + +#include "dw_mmc.h" + +#ifdef CONFIG_MMC_DW_HI3XXX_MODULE +extern void dw_mci_reg_dump(struct dw_mci *host); +extern void dw_mci_set_timeout(struct dw_mci *host); +extern bool dw_mci_stop_abort_cmd(struct mmc_command *cmd); +extern bool dw_mci_wait_reset(struct device *dev, struct dw_mci *host, + unsigned int reset_val); +extern void dw_mci_ciu_reset(struct device *dev, struct dw_mci *host); +extern bool dw_mci_fifo_reset(struct device *dev, struct dw_mci *host); +extern u32 dw_mci_prep_stop(struct dw_mci *host, struct mmc_command *cmd); +extern bool dw_mci_wait_data_busy(struct dw_mci *host, struct mmc_request *mrq); +extern int dw_mci_start_signal_voltage_switch(struct mmc_host *mmc, + struct mmc_ios *ios); +extern void dw_mci_slowdown_clk(struct mmc_host *mmc, int timing); +extern void dw_mci_timeout_timer(struct timer_list *t); +extern void dw_mci_work_routine_card(struct work_struct *work); +extern bool mci_wait_reset(struct device *dev, struct dw_mci *host); +/* only for SD voltage switch on hi3650 FPGA */ +extern int gpio_direction_output(unsigned int gpio, int value); + +#else + +static inline void dw_mci_reg_dump(struct dw_mci *host) {} +static inline void dw_mci_set_timeout(struct dw_mci *host) {} +static inline void dw_mci_timeout_timer(struct timer_list *t) {} +static inline void dw_mci_work_routine_card(struct work_struct *work) {} +static inline void dw_mci_slowdown_clk(struct mmc_host *mmc, int timing) {} +static inline void dw_mci_ciu_reset(struct device *dev, struct dw_mci *host) {} + +static inline bool dw_mci_stop_abort_cmd(struct mmc_command *cmd) +{ + return 0; +} +static inline bool dw_mci_wait_reset(struct device *dev, struct dw_mci *host, + unsigned int reset_val) +{ + return 0; +} +static inline bool dw_mci_fifo_reset(struct device *dev, struct dw_mci *host) +{ + return 0; +} +static inline u32 dw_mci_prep_stop(struct dw_mci *host, + struct mmc_command *cmd) +{ + return 0; +} +static inline bool dw_mci_wait_data_busy(struct dw_mci *host, + struct mmc_request *mrq) +{ + return 0; +} +static inline int dw_mci_start_signal_voltage_switch(struct mmc_host *mmc, + struct mmc_ios *ios) +{ + return 0; +} +static inline bool mci_wait_reset(struct device *dev, struct dw_mci *host) +{ + return 0; +} +int hisi_dw_mci_get_cd(struct mmc_host *mmc); +/* only for SD voltage switch on hi3650 FPGA */ +extern int gpio_direction_output(unsigned int gpio, int value); +#endif + +#endif diff --git a/drivers/mmc/host/dw_mmc_hisi.h b/drivers/mmc/host/dw_mmc_hisi.h new file mode 100644 index 0000000000000..394fd92dce7d1 --- /dev/null +++ b/drivers/mmc/host/dw_mmc_hisi.h @@ -0,0 +1,291 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _DW_MMC_HISI_ +#define _DW_MMC_HISI_ +#include <linux/pinctrl/consumer.h> +#include <linux/workqueue.h> +#include <linux/of_address.h> +#include <asm/cacheflush.h> +#include <linux/pm_runtime.h> + +#ifdef CONFIG_MMC_HISI_TRACE +#include <linux/hisi/mmc_trace.h> +#endif +#include <linux/version.h> + +#define SDMMC_UHS_REG_EXT 0x108 +#define SDMMC_ENABLE_SHIFT 0x110 + +#define SDMMC_64_BIT_DMA 1 +#define SDMMC_32_BIT_DMA 0 + +#define SDMMC_CMD_ONLY_CLK (SDMMC_CMD_START | SDMMC_CMD_UPD_CLK | \ + SDMMC_CMD_PRV_DAT_WAIT) + +#define CTRL_RESET (0x1 << 0) /* Reset DWC_mobile_storage controller */ +#define FIFO_RESET (0x1 << 1) /* Reset FIFO */ +#define DMA_RESET (0x1 << 2) /* Reset DMA interface */ +#define INT_ENABLE (0x1 << 4) /* Global interrupt enable/disable bit */ +#define DMA_ENABLE (0x1 << 5) /* DMA transfer mode enable/disable bit */ +#define ENABLE_IDMAC (0x1 << 25) + + +#define INTMSK_ALL 0xFFFFFFFF +#define INTMSK_CDETECT (0x1 << 0) +#define INTMSK_RE (0x1 << 1) +#define INTMSK_CDONE (0x1 << 2) +#define INTMSK_DTO (0x1 << 3) +#define INTMSK_TXDR (0x1 << 4) +#define INTMSK_RXDR (0x1 << 5) +#define INTMSK_RCRC (0x1 << 6) +#define INTMSK_DCRC (0x1 << 7) +#define INTMSK_RTO (0x1 << 8) +#define INTMSK_DRTO (0x1 << 9) +#define INTMSK_HTO (0x1 << 10) +#define INTMSK_VOLT_SWITCH (0x1 << 10) +#define INTMSK_FRUN (0x1 << 11) +#define INTMSK_HLE (0x1 << 12) +#define INTMSK_SBE (0x1 << 13) +#define INTMSK_ACD (0x1 << 14) +#define INTMSK_EBE (0x1 << 15) +#define INTMSK_DMA (INTMSK_ACD | INTMSK_RXDR | INTMSK_TXDR) + +#define INT_SRC_IDMAC (0x0) +#define INT_SRC_MINT (0x1) + + +#define CMD_RESP_EXP_BIT (0x1 << 6) +#define CMD_RESP_LENGTH_BIT (0x1 << 7) +#define CMD_CHECK_CRC_BIT (0x1 << 8) +#define CMD_DATA_EXP_BIT (0x1 << 9) +#define CMD_RW_BIT (0x1 << 10) +#define CMD_TRANSMODE_BIT (0x1 << 11) +#define CMD_WAIT_PRV_DAT_BIT (0x1 << 13) +#define CMD_STOP_ABORT_CMD (0x1 << 14) +#define CMD_SEND_INITIALIZATION (0x1 << 15) +#define CMD_SEND_CLK_ONLY (0x1 << 21) +#define CMD_VOLT_SWITCH (0x1 << 28) +#define CMD_USE_HOLD_REG (0x1 << 29) +#define CMD_STRT_BIT (0x1 << 31) +#define CMD_ONLY_CLK (CMD_STRT_BIT | CMD_SEND_CLK_ONLY | \ + CMD_WAIT_PRV_DAT_BIT) + +#define CLK_ENABLE (0x1 << 0) +#define CLK_DISABLE (0x0 << 0) + +#define BOARDTYPE_SFT (1) + +#define STATE_KEEP_PWR (1) +#define STATE_LEGACY (0) + +/* BEGIN PN: , Modified by , 2014/06/11 */ +#define SD_SLOT_VOL_OPEN 1 +#define SD_SLOT_VOL_CLOSE 0 +/* END PN: , Modified by , 2014/06/11 */ + +#define SDMMC_ASIC_PLATFORM (-1) +#define SDMMC_FPGA_PLATFORM (0x5a5a5a5a) + +#define PLL5_EMMC_DIV_MAX 16 +#define PLL5_CLK_FREQ_MAX 1600000000 +#define PLL5_CLK_DEFAULT_FREQ 3200000 + + +#define DW_MCI_DESC_SZ 1 +#define DW_MCI_DESC_SZ_64BIT 2 + +#define DRIVER_NAME "dwmmc_hs" + +#define DW_MCI_EMMC_ID (0x00) +#define DW_MCI_SD_ID (0x01) +#define DW_MCI_SDIO_ID (0x02) + +#define EMMC_BUS_WIDTH_8_BIT 0x08 +#define EMMC_BUS_WIDTH_4_BIT 0x04 +#define EMMC_BUS_WIDTH_1_BIT 0x01 + +#define HIXX51_EMMC_CLK_SEL_PLL_OFF 0xffffff0f +#define HIXX51_EMMC_CLK_SEL_PLL_VAL 0x00000010 +#define HIXX51_EMMC_CLK_SEL_PLL_L_S 0x4 +/* davinci mmc reset reg */ +#define HIXX51_PERI_SC_EMMC_RST_REQ (0xa30) //controller soft reset +#define HIXX51_PERI_SC_EMMC_RST_DREQ (0xa34) //controller soft disreset +#define HIXX51_PERI_SC_EMMC_ICG_EN (0x398) //clock enable +#define HIXX51_PERI_SC_EMMC_ICG_DIS (0x39c) //clock disable +#define HIXX51_PERI_SC_EMMC_CLK_SEL (0x104) // input clock select +#define HIXX51_SC_USER1_EMMC (0x2204) //eMMC user area configre +#define HIXX51_SC_AXI_EMMC (0x2210) //usb axi area configre +#define HIXX51_SC_BIAS_CTRL (0x3780) +#define HIXX51_SC_PLL_PROF_CFG0 (0x3688) + +#define HIXX10_PERI_SC_EMMC_RST_REQ (0xcb0) +#define HIXX10_PERI_SC_EMMC_RST_DREQ (0xcb4) +#define HIXX10_PERI_SC_EMMC_ICG_EN (0x5c0) +#define HIXX10_PERI_SC_EMMC_ICG_DIS (0x5c4) +#define HIXX10_PERI_SC_EMMC_CLK_SEL (0x3500) +#define HIXX10_SC_BIAS_CTRL (0x3780) +#define HIXX10_SC_PLL_PROF_CFG0 (0x3688) + +#define PERI_CRG_CLKDIV4 (0xb8) +#define PERI_CRG_CLKDIV6 (0xc0) + +enum { + PERI_SC_EMMC_RST_REQ = 0, + PERI_SC_EMMC_RST_DREQ, + PERI_SC_EMMC_ICG_EN, + PERI_SC_EMMC_ICG_DIS, + PERI_SC_EMMC_CLK_SEL, + SC_BIAS_CTRL, + SC_PLL_PROF_CFG0, + SC_USER1_EMMC, + SC_AXI_EMMC, + REG_MAX_NUM, +}; + +enum { + CHIP_HIXX10 = 0, + CHIP_HIXX51, + CHIP_TYPE_MAX_NUM, +}; + +/* mmc1 sys ctrl, start from kirin980 */ +#define MMC1_SYSCTRL_PEREN0 (0x300) +#define MMC1_SYSCTRL_PERDIS0 (0x304) +#define MMC1_SYSCTRL_PERCLKEN0 (0x308) +#define MMC1_SYSCTRL_PERSTAT0 (0x30C) +#define GT_HCLK_SDIO1_BIT (0x1) + +#define MMC1_SYSCTRL_PERRSTEN0 (0x310) +#define MMC1_SYSCTRL_PERRSTDIS0 (0x314) +#define MMC1_SYSCTRL_PERRSTSTAT0 (0x318) +#define BIT_HRST_SDIO_ATLANTA (0x1) +#define BIT_RST_SDIO_ATLANTA (0x1 << 1) +/* mmc1 sys ctrl end*/ + +#define HI3660_FPGA 1 +#define PERI_CRG_PERSTAT4 (0x04c) + +#define GTCLK_SD_EN (0x20000) + +#define BIT_VOLT_OFFSET (0x314) +#define BIT_VOLT_OFFSET_AUSTIN (0x214) +#define BIT_VOLT_VALUE_18 (0x4) + +#define BIT_RST_EMMC (1<<0) +#define BIT_RST_SD (1<<0) +#define BIT_RST_SDIO (1<<0) + +#define BIT_RST_SDIO_CHICAGO (1<<20) +#define BIT_RST_SDIO_BOSTON (1<<20) + +#define GPIO_CLK_DIV(x) (((x) & 0xf) << 8) +#define GPIO_USE_SAMPLE_DLY(x) (((x) & 0x1) << 13) + +#define GPIO_CLK_ENABLE (0x1 << 16) +#define UHS_REG_EXT_SAMPLE_PHASE(x) (((x) & 0x1f) << 16) +#define UHS_REG_EXT_SAMPLE_DLY(x) (((x) & 0x1f) << 26) +#define UHS_REG_EXT_SAMPLE_DRVPHASE(x) (((x) & 0x1f) << 21) +#define SDMMC_UHS_REG_EXT_VALUE(x, y, z) \ + (UHS_REG_EXT_SAMPLE_PHASE(x) | \ + UHS_REG_EXT_SAMPLE_DLY(y) | UHS_REG_EXT_SAMPLE_DRVPHASE(z)) +#define SDMMC_GPIO_VALUE(x, y) \ + (GPIO_CLK_DIV(x) | GPIO_USE_SAMPLE_DLY(y)) + +/*Reduce Max tuning loop,200 loops may case the watch dog timeout*/ +#define MAX_TUNING_LOOP 32 + +struct dw_mci_hs_priv_data { + int id; + int old_timing; + int gpio_cd; + int gpio_sw; + int sw_value; + int old_signal_voltage; + int old_power_mode; + unsigned int priv_bus_hz; + unsigned int cd_vol; +/* BEGIN PN: , Modified by , 2014/06/11 */ + unsigned int sd_slot_ldo10_status; +/* END PN: , Modified by , 2014/06/11 */ + int dw_mmc_bus_clk; + int dw_voltage_switch_gpio; + int chip_platform; + u32 chip_type; + int hi3660_sd_ioset_sd_sel; + int hi3660_sd_ioset_jtag_sd_sel; + int hi3660_fpga_sd_ioset; + int cs; + int in_resume; + void __iomem *ao_sysctrl; + void __iomem *peri_sysctrl; + void __iomem *ioc_off; +}; + +struct dw_mci_tuning_data { + const u8 *blk_pattern; + unsigned int blksz; +}; + +#ifdef CONFIG_MMC_DW_IDMAC +#define IDMAC_INT_CLR (SDMMC_IDMAC_INT_AI | SDMMC_IDMAC_INT_NI | \ + SDMMC_IDMAC_INT_CES | SDMMC_IDMAC_INT_DU | \ + SDMMC_IDMAC_INT_FBE | SDMMC_IDMAC_INT_RI | \ + SDMMC_IDMAC_INT_TI) +#endif +enum himntnEnum { + HIMNTN_NVE_VALID = 0, + HIMNTN_WDT_MIN, + HIMNTN_AP_WDT = HIMNTN_WDT_MIN, + HIMNTN_GLOBAL_WDT, + HIMNTN_MODEM_WDT, + HIMNTN_LPM3_WDT, + HIMNTN_IOM3_WDT, + HIMNTN_HIFI_WDT, + HIMNTN_SECOS_WDT, + HIMNTN_ISP_WDT, + HIMNTN_IVP_WDT, + HIMNTN_OCBC_WDT = 10, + HIMNTN_UCE_WDT, + HIMNTN_RESERVED_WDT3, + HIMNTN_WDT_MAX = HIMNTN_RESERVED_WDT3, + HIMNTN_FST_DUMP_MEM, + HIMNTN_MNTN_DUMP_MEM, + HIMNTN_SD2JTAG, + HIMNTN_PRESS_KEY_TO_FASTBOOT, + HIMNTN_PANIC_INTO_LOOP, + HIMNTN_GOBAL_RESETLOG, + HIMNTN_NOC_INT_HAPPEN, + HIMNTN_NOC_ERROR_REBOOT = 20, + HIMNTN_DFXPARTITION_TO_FILE, + HIMNTN_DDR_ERROR_REBOOT, + HIMNTN_HISEE, + HIMNTN_WATCHPOINT_EN, + HIMNTN_KMEMLEAK_SWITCH, + HIMNTN_FB_PANIC_REBOOT, + HIMNTN_MEM_TRACE = 27, /*Bit:27: Memory Trace hook switch.*/ + HIMNTN_FTRACE, + HIMNTN_EAGLE_EYE, + /*Hook switch is the same one of kdump.*/ + HIMNTN_KERNEL_DUMP_ENABLE = 30, + HIMNTN_SD2DJTAG, + HIMNTN_MMC_TRACE, + HIMNTN_LPM3_PANIC_INTO_LOOP, + HIMNTN_TRACE_CLK_REGULATOR, + HIMNTN_CORESIGHT, + HIMNTN_DMSSPT, + HIMNTN_HHEE, + HIMNTN_KASLR, + HIMNTN_SD2UART6, + /*Add above, and keep the same as*/ + /*definition in reboot_reason.h in fastboot !!!!*/ + HIMNTN_BOTTOM +}; + +enum { + MEM_EMMC_IOBASE, + MEM_PERI_SUBCTRL_IOBASE, + MEM_SYSCTRL_IOBASE, + MEM_GPIO_IOBASE, + MEM_IOBASE_MAX, +}; + +#endif /* _DW_MMC_HISI_ */ diff --git a/drivers/mmc/host/hisi_mmc_pmic.h b/drivers/mmc/host/hisi_mmc_pmic.h new file mode 100644 index 0000000000000..0993042046f36 --- /dev/null +++ b/drivers/mmc/host/hisi_mmc_pmic.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _HISI_MMC_PMIC_ +#define _HISI_MMC_PMIC_ + +#include "linux/types.h" + +#if defined(CONFIG_HISI_SPMI) +u32 hisi_pmic_read_reg(const s32 reg); +s32 hisi_pmic_write_reg(const s32 reg, const u32 val); +s32 pmu_ldo2_enable(void); +s32 pmu_ldo2_disable(void); +s32 pmu_ldo2_set_voltage(const u32 volt_value); +s32 pmu_ldo16_enable(void); +s32 pmu_ldo16_disable(void); +s32 pmu_ldo16_set_voltage(const u32 volt_value); +s32 pmu_ldo9_enable(void); +s32 pmu_ldo9_disable(void); +s32 pmu_ldo9_set_voltage(const u32 volt_value); +s32 hisi_adc_get_value(const u32 channel); +s32 ntc_read_temp(const s32 channel, s32 *temp); +u32 hisi_second_pmic_read_reg(const s32 sid, const s32 reg); +s32 get_second_pmu_buck_volt(u32 device_id, u32 channel, u32 *volt_mv); +s32 get_main_pmu_buck_volt(u32 channel, u32 *volt_mv); +s32 get_main_pmu_ldo_volt(u32 channel, u32 *volt_mv); +s32 get_main_pmu_die_id(u8 *die_id, u8 len); +s32 get_second_pmu_die_id(u32 device_id, u8 *die_id, u8 len); +#else +static inline s32 hisi_pmic_read_reg(const s32 reg) { return 0; } +static inline s32 hisi_pmic_write_reg(const s32 reg, const u32 val) +{ + return 0; +} +static inline s32 pmu_ldo2_enable(void) { return 0; } +static inline s32 pmu_ldo2_disable(void) { return 0; } +static inline s32 pmu_ldo2_set_voltage(const u32 volt_value) { return 0; } +static inline s32 pmu_ldo16_enable(void) { return 0; } +static inline s32 pmu_ldo16_disable(void) { return 0; } +static inline s32 pmu_ldo16_set_voltage(const u32 volt_value) { return 0; } +static inline s32 pmu_ldo9_enable(void) { return 0; } +static inline s32 pmu_ldo9_disable(void) { return 0; } +static inline s32 pmu_ldo9_set_voltage(const u32 volt_value) { return 0; } +static inline s32 hisi_adc_get_value(const u32 channel) { return 0; } +static inline s32 ntc_read_temp(const s32 channel, s32 *temp) { return 0; } +static inline u32 hisi_second_pmic_read_reg(const s32 sid, const s32 reg) +{ + return 0; +}; +static inline s32 get_second_pmu_buck_volt(u32 device_id, + u32 channel, u32 *volt_mv) +{ + return 0; +}; +static inline s32 get_main_pmu_buck_volt(u32 channel, u32 *volt_mv) +{ + return 0; +}; +static inline s32 get_main_pmu_ldo_volt(u32 channel, u32 *volt_mv) +{ + return 0; +}; +static inline s32 get_main_pmu_die_id(u8 *die_id, u8 len) +{ + return 0; +}; +static inline s32 get_second_pmu_die_id(u32 device_id, u8 *die_id, u8 len) +{ + return 0; +}; +#endif + +#endif /* _HISI_MMC_PMIC_ */
From: zhangguijiang zhangguijiang@huawei.com
ascend inclusion category: feature feature: Ascend emmc adaption bugzilla: https://gitee.com/openeuler/kernel/issues/I4F4LL CVE: NA
------------------- Hisilicon specific extensions for Synopsys DW Memory Card Storage Host Controller.
Signed-off-by: zhangguijiang zhangguijiang@huawei.com Reviewed-by: Ding Tianhong dingtianhong@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- drivers/mmc/host/Kconfig | 20 + drivers/mmc/host/Makefile | 1 + drivers/mmc/host/dw_mmc_hi3xxx.c | 3135 ++++++++++++++++++++++++++++++ 3 files changed, 3156 insertions(+) create mode 100644 drivers/mmc/host/dw_mmc_hi3xxx.c
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index b7f809aa40c2c..9a42e243dcc4a 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -682,6 +682,14 @@ config MMC_DW This selects support for the Synopsys DesignWare Mobile Storage IP block, this provides host support for SD and MMC interfaces, in both PIO, internal DMA mode and external DMA mode. +config MMC_DW_IDMAC + bool "Internal DMAC interface" + depends on MMC_DW + help + This selects support for the internal DMAC block within the Synopsys + Designware Mobile Storage IP block. This disables the external DMA + interface. + If you have a controller with this interface, say Y here.
config MMC_DW_PLTFM tristate "Synopsys Designware MCI Support as platform device" @@ -724,10 +732,22 @@ config MMC_DW_HI3798CV200 Synopsys DesignWare Memory Card Interface driver. Select this option for platforms based on HiSilicon Hi3798CV200 SoC.
+config MMC_DW_HI3XXX + tristate "Hisilicon specific extensions for Synopsys DW Memory Card Interface" + depends on MMC_DW && ASCEND_HISI_MMC + select MMC_DW_PLTFM + select MMC_DW_IDMAC + default n + help + This selects support for Hisilicon SoC specific extensions to the + Synopsys DesignWare Memory Card Interface driver. Select this option + for platforms based on Kirin 9 serial SoC's. + config MMC_DW_K3 tristate "K3 specific extensions for Synopsys DW Memory Card Interface" depends on MMC_DW select MMC_DW_PLTFM + select MMC_DW_IDMAC help This selects support for Hisilicon K3 SoC specific extensions to the Synopsys DesignWare Memory Card Interface driver. Select this option diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index ce8398e6f2c0e..e0a049608492a 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -52,6 +52,7 @@ obj-$(CONFIG_MMC_DW) += dw_mmc.o obj-$(CONFIG_MMC_DW_PLTFM) += dw_mmc-pltfm.o obj-$(CONFIG_MMC_DW_BLUEFIELD) += dw_mmc-bluefield.o obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.o +obj-$(CONFIG_MMC_DW_HI3XXX) += dw_mmc_hi3xxx.o obj-$(CONFIG_MMC_DW_HI3798CV200) += dw_mmc-hi3798cv200.o obj-$(CONFIG_MMC_DW_K3) += dw_mmc-k3.o obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o diff --git a/drivers/mmc/host/dw_mmc_hi3xxx.c b/drivers/mmc/host/dw_mmc_hi3xxx.c new file mode 100644 index 0000000000000..d41c1bf50b7c3 --- /dev/null +++ b/drivers/mmc/host/dw_mmc_hi3xxx.c @@ -0,0 +1,3135 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2013 Linaro Ltd. + * Copyright (c) 2013 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wswitch" +#include "dw_mmc_hisi.h" + +#ifdef CONFIG_HISI_BOOTDEVICE +#include <linux/bootdevice.h> +#endif +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/version.h> +#include <linux/mmc/mmc.h> +#include <linux/mmc/sd.h> +#include <linux/mmc/sdio.h> +#include <linux/mmc/host.h> + +#include <linux/slab.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/pinctrl/consumer.h> +#include <linux/regulator/consumer.h> +#include <linux/of_address.h> +#include <linux/pm_runtime.h> +#include <linux/clk-provider.h> +#include <linux/hwspinlock.h> + +#include "dw_mmc.h" +#include "dw_mmc-pltfm.h" +#include "hisi_mmc_pmic.h" +#include "dw_mmc_extern.h" + +/* Common flag combinations */ +#define DW_MCI_DATA_ERROR_FLAGS (SDMMC_INT_DRTO | SDMMC_INT_DCRC | \ + SDMMC_INT_HTO | SDMMC_INT_SBE | \ + SDMMC_INT_EBE) +#define DW_MCI_CMD_ERROR_FLAGS (SDMMC_INT_RTO | SDMMC_INT_RCRC | \ + SDMMC_INT_RESP_ERR) +#define DW_MCI_ERROR_FLAGS (DW_MCI_DATA_ERROR_FLAGS | \ + DW_MCI_CMD_ERROR_FLAGS | SDMMC_INT_HLE) + + +void __iomem *peri_subctrl_base; +void __iomem *sys_base; + +static unsigned long hs_dwmmc_reg[CHIP_TYPE_MAX_NUM][REG_MAX_NUM] = { + { + HIXX10_PERI_SC_EMMC_RST_REQ, + HIXX10_PERI_SC_EMMC_RST_DREQ, + HIXX10_PERI_SC_EMMC_ICG_EN, + HIXX10_PERI_SC_EMMC_ICG_DIS, + HIXX10_PERI_SC_EMMC_CLK_SEL, + HIXX10_SC_BIAS_CTRL, + HIXX10_SC_PLL_PROF_CFG0, + }, { + HIXX51_PERI_SC_EMMC_RST_REQ, + HIXX51_PERI_SC_EMMC_RST_DREQ, + HIXX51_PERI_SC_EMMC_ICG_EN, + HIXX51_PERI_SC_EMMC_ICG_DIS, + HIXX51_PERI_SC_EMMC_CLK_SEL, + HIXX51_SC_BIAS_CTRL, + HIXX51_SC_PLL_PROF_CFG0, + HIXX51_SC_USER1_EMMC, + HIXX51_SC_AXI_EMMC, + } +}; + +static unsigned long hs_dwmmc_caps[]; + +static const u8 tuning_blk_pattern_4bit[] = { + 0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc, + 0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef, + 0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb, + 0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef, + 0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c, + 0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee, + 0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff, + 0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde, +}; + +static const u8 tuning_blk_pattern_8bit[] = { + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc, + 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff, + 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff, + 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd, + 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, + 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff, + 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, + 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, + 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, + 0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, + 0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, + 0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, + 0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, +}; + +static int hs_timing_config[][TUNING_INIT_TIMING_MODE][TUNING_INIT_CONFIG_NUM] = { + /* bus_clk, div, drv_phase, sam_dly, */ + /* sam_phase_max, sam_phase_min, input_clk */ + { /*MMC*/ + {3200000, 7, 7, 0, 15, 15, 400000}, /* 0: LEGACY 400k */ + {400000000, 7, 6, 0, 15, 15, 50000000}, /* 1: MMC_HS */ + {200000000, 7, 6, 0, 15, 15, 25000000}, /* 2: SD_HS */ + {200000000, 7, 6, 0, 15, 15, 25000000}, /* 3: SDR12 */ + {400000000, 7, 6, 0, 15, 15, 50000000}, /* 4: SDR25 */ + {800000000, 7, 4, 0, 12, 0, 100000000}, /* 5: SDR50 */ + {1600000000, 7, 5, 4, 15, 0, 200000000},/* 6: SDR104 */ + {800000000, 7, 6, 0, 7, 0, 100000000}, /* 7: DDR50 */ + {800000000, 7, 6, 0, 7, 0, 100000000}, /* 8: DDR52 */ + {1600000000, 7, 5, 4, 15, 0, 200000000},/* 9: HS200 */ + }, + { /*SD*/ + {3200000, 7, 7, 0, 15, 15, 400000}, /* 0: LEGACY 400k */ + {0}, /* 1: MMC_HS */ + {400000000, 7, 6, 0, 1, 1, 50000000}, /* 2: SD_HS */ + {200000000, 7, 6, 0, 15, 15, 25000000}, /* 3: SDR12 */ + {400000000, 7, 6, 0, 1, 1, 50000000}, /* 4: SDR25 */ + {800000000, 7, 3, 0, 12, 0, 100000000}, /* 5: SDR50 */ + {1600000000, 7, 5, 4, 15, 0, 200000000},/* 6: SDR104 */ + {0}, /* 7: DDR50 */ + {0}, /* 8: DDR52 */ + {0}, /* 9: HS200 */ + }, + { /*SDIO*/ + {3200000, 7, 7, 0, 15, 15, 400000}, /* 0: LEGACY 400k */ + {0}, /* 1: MMC_HS */ + {400000000, 7, 6, 0, 15, 15, 50000000}, /* 2: SD_HS */ + {200000000, 7, 6, 0, 15, 15, 25000000}, /* 3: SDR12 */ + {400000000, 7, 6, 0, 15, 15, 50000000}, /* 4: SDR25 */ + {800000000, 7, 5, 0, 12, 0, 100000000}, /* 5: SDR50 */ + {1600000000, 7, 5, 4, 15, 0, 200000000},/* 6: SDR104 */ + {0}, /* 7: DDR50 */ + {0}, /* 8: DDR52 */ + {0}, /* 9: HS200 */ + } +}; + +static int hs_timing_config_kirin970[][TUNING_INIT_TIMING_MODE][TUNING_INIT_CONFIG_NUM] = { + /* bus_clk, div, drv_phase, sam_dly, */ + /* sam_phase_max, sam_phase_min, input_clk */ + { /*MMC*/ + {3200000, 7, 7, 0, 15, 15, 400000}, /* 0: LEGACY 400k */ + /* 1: MMC_HS */ /* ES 400M, 8div 50M */ + {480000000, 9, 6, 0, 1, 1, 48000000}, + {200000000, 7, 6, 0, 15, 15, 25000000}, /* 2: SD_HS */ + {200000000, 7, 6, 0, 15, 15, 25000000}, /* 3: SDR12 */ + {400000000, 7, 6, 0, 15, 15, 50000000}, /* 4: SDR25 */ + {800000000, 7, 4, 0, 12, 0, 100000000}, /* 5: SDR50 */ + {1600000000, 7, 5, 4, 15, 0, 200000000},/* 6: SDR104 */ + {800000000, 7, 6, 0, 7, 0, 100000000}, /* 7: DDR50 */ + {800000000, 7, 6, 0, 7, 0, 100000000}, /* 8: DDR52 */ + {800000000, 7, 5, 0, 15, 0, 100000000}, /* 9: HS200 */ + + }, + { /*SD*/ + {3200000, 7, 7, 0, 15, 15, 400000}, /* 0: LEGACY 400k */ + {200000000, 7, 6, 0, 1, 1, 25000000}, /* 1: MMC_HS */ + {400000000, 7, 6, 0, 4, 4, 50000000}, /* 2: SD_HS */ + {200000000, 7, 6, 0, 15, 15, 25000000}, /* 3: SDR12 */ + {400000000, 7, 6, 0, 2, 2, 50000000}, /* 4: SDR25 */ + {800000000, 7, 5, 0, 12, 0, 100000000}, /* 5: SDR50 */ + {1600000000, 7, 5, 4, 15, 0, 200000000},/* 6: SDR104 */ + {0}, /* 7: DDR50 */ + {0}, /* 8: DDR52 */ + {0}, /* 9: HS200 */ + }, + { /*SDIO*/ + {3200000, 7, 7, 0, 15, 15, 400000}, /* 0: LEGACY 400k */ + {0}, /* 1: MMC_HS */ + {400000000, 7, 6, 0, 15, 15, 50000000}, /* 2: SD_HS */ + {200000000, 7, 6, 0, 15, 15, 25000000}, /* 3: SDR12 */ + {400000000, 7, 6, 0, 1, 1, 50000000}, /* 4: SDR25 */ + {800000000, 7, 2, 0, 12, 0, 100000000}, /* 5: SDR50 */ + {1600000000, 7, 5, 4, 15, 0, 200000000},/* 6: SDR104 */ + {0}, /* 7: DDR50 */ + {0}, /* 8: DDR52 */ + {0}, /* 9: HS200 */ + } +}; + +static int hs_timing_config_kirin970_cs[][TUNING_INIT_TIMING_MODE][TUNING_INIT_CONFIG_NUM] = { + /* bus_clk, div, drv_phase, sam_dly, */ + /* sam_phase_max, sam_phase_min, input_clk */ + { /*MMC*/ + {3200000, 7, 7, 0, 15, 15, 400000}, /* 0: LEGACY 400k */ + /* 1: MMC_HS */ /* ES 400M, 8div 50M */ + {480000000, 9, 6, 0, 1, 1, 48000000}, + {200000000, 7, 6, 0, 15, 15, 25000000}, /* 2: SD_HS */ + {200000000, 7, 6, 0, 15, 15, 25000000}, /* 3: SDR12 */ + {400000000, 7, 6, 0, 15, 15, 50000000}, /* 4: SDR25 */ + {800000000, 7, 4, 0, 12, 0, 100000000}, /* 5: SDR50 */ + {1600000000, 7, 5, 4, 15, 0, 200000000},/* 6: SDR104 */ + {800000000, 7, 6, 0, 7, 0, 100000000}, /* 7: DDR50 */ + {800000000, 7, 6, 0, 7, 0, 100000000}, /* 8: DDR52 */ + /* 9: HS200 */ /* ES 960M, 8div 120M */ + {1920000000, 9, 5, 4, 15, 0, 192000000}, + }, + { /*SD*/ + {3200000, 7, 7, 0, 19, 19, 400000}, /* 0: LEGACY 400k */ + {240000000, 9, 8, 0, 3, 3, 24000000}, /* 1: MMC_HS */ + {480000000, 9, 7, 0, 4, 4, 48000000}, /* 2: SD_HS */ + {240000000, 9, 8, 0, 19, 19, 24000000}, /* 3: SDR12 */ + {480000000, 9, 7, 0, 3, 3, 48000000}, /* 4: SDR25 */ + {960000000, 9, 4, 0, 16, 0, 96000000}, /* 5: SDR50 */ + {1920000000, 9, 6, 4, 19, 0, 192000000},/* 6: SDR104 */ + {0}, /* 7: DDR50 */ + {0}, /* 8: DDR52 */ + {0}, /* 9: HS200 */ + }, + { /*SDIO*/ + {3200000, 7, 7, 0, 19, 19, 400000}, /* 0: LEGACY 400k */ + {0}, /* 1: MMC_HS */ + {480000000, 9, 7, 0, 19, 19, 48000000}, /* 2: SD_HS */ + {240000000, 9, 8, 0, 19, 19, 24000000}, /* 3: SDR12 */ + {480000000, 9, 7, 0, 19, 19, 48000000}, /* 4: SDR25 */ + {960000000, 9, 4, 0, 16, 0, 96000000}, /* 5: SDR50 */ + {1920000000, 9, 5, 4, 19, 0, 192000000},/* 6: SDR104 */ + {0}, /* 7: DDR50 */ + {0}, /* 8: DDR52 */ + {0}, /* 9: HS200 */ + } +}; + +static int check_himntn(int feature) +{ + return 0; +} + + +static void dw_mci_hs_set_timing(struct dw_mci *host, + int id, int timing, int sam_phase, int clk_div) +{ + int cclk_div; + int drv_phase; + int sam_dly; + int sam_phase_max, sam_phase_min; + int sam_phase_val; + int reg_value; + int enable_shift = 0; + int use_sam_dly = 0; + int d_value = 0; + struct dw_mci_slot *slot = host->cur_slot; + + if ((host->hw_mmc_id == DWMMC_SD_ID) && (timing == MMC_TIMING_LEGACY)) + cclk_div = hs_timing_config[id][timing][1]; + else + cclk_div = clk_div; + if (host->hw_mmc_id == DWMMC_SD_ID) + d_value = cclk_div - hs_timing_config[id][timing][1]; + drv_phase = hs_timing_config[id][timing][2]; + sam_dly = hs_timing_config[id][timing][3] + d_value; + sam_phase_max = hs_timing_config[id][timing][4] + 2 * d_value; + sam_phase_min = hs_timing_config[id][timing][5]; + + if (sam_phase == -1) + sam_phase_val = (sam_phase_max + sam_phase_min) / 2; + else + sam_phase_val = sam_phase; + + /* enable_shift and use_sam_dly setting code */ + /* warning! different with K3V3 */ + switch (id) { + case DW_MCI_EMMC_ID: + switch (timing) { + case MMC_TIMING_UHS_DDR50: + if (sam_phase_val >= 4 && sam_phase_val <= 12) + enable_shift = 1; + break; + case MMC_TIMING_MMC_HS200: + if (sam_phase_val >= 4 && sam_phase_val <= 12) + enable_shift = 1; + if (sam_phase_val >= 11 && sam_phase_val <= 14) + use_sam_dly = 1; + break; + } + break; + case DW_MCI_SD_ID: + switch (timing) { + case MMC_TIMING_UHS_SDR50: + if (4 + d_value <= sam_phase_val && + sam_phase_val <= 12 + d_value) + enable_shift = 1; + break; + case MMC_TIMING_UHS_SDR104: + if (11 + 2 * d_value <= sam_phase_val && + sam_phase_val <= 14 + 2 * d_value) + use_sam_dly = 1; + if (4 + d_value <= sam_phase_val && + sam_phase_val <= 12 + d_value) + enable_shift = 1; + break; + } + break; + case DW_MCI_SDIO_ID: + switch (timing) { + case MMC_TIMING_UHS_SDR12: + break; + case MMC_TIMING_UHS_DDR50: + if (sam_phase_val >= 4 && sam_phase_val <= 12) + enable_shift = 1; + break; + case MMC_TIMING_UHS_SDR50: + if (sam_phase_val >= 4 && sam_phase_val <= 12) + enable_shift = 1; + break; + case MMC_TIMING_UHS_SDR104: + if (host->wifi_sdio_sdr104_160M == 0xaaaa) { + if (sam_phase_val >= 15 && sam_phase_val <= 18) + use_sam_dly = 1; + } else if (host->wifi_sdio_sdr104_177M == 0xaaaa) { + if (sam_phase_val >= 13 && sam_phase_val <= 16) + use_sam_dly = 1; + } else { + if (sam_phase_val >= 11 && sam_phase_val <= 14) + use_sam_dly = 1; + } + if (sam_phase_val >= 4 && sam_phase_val <= 12) + enable_shift = 1; + break; + } + break; + } + + /* first disabl clk */ + mci_writel(host, GPIO, 0x0); + udelay(5); + + reg_value = SDMMC_UHS_REG_EXT_VALUE(sam_phase_val, sam_dly, drv_phase); + mci_writel(host, UHS_REG_EXT, reg_value); + + mci_writel(host, ENABLE_SHIFT, enable_shift); + + reg_value = SDMMC_GPIO_VALUE(cclk_div, use_sam_dly); + mci_writel(host, GPIO, (unsigned int)reg_value | GPIO_CLK_ENABLE); + + if (!(slot && slot->sdio_wakelog_switch)) + dev_info(host->dev, "id=%d,timing=%d,\n", id, timing); + dev_info(host->dev, + "UHS_REG_EXT=0x%x, ENABLE_SHIFT=0x%x,GPIO=0x%x\n", + mci_readl(host, UHS_REG_EXT), + mci_readl(host, ENABLE_SHIFT), + mci_readl(host, GPIO)); +} + +#define SD_HWLOCK_ID 11 +#define SD_LOCK_TIMEOUT 1000 +static struct hwspinlock *sd_hwlock; +static int dw_mci_set_sel18(int chip_type, bool set) +{ + u32 reg; + unsigned long flag = 0; + + /* hixx51 not support voltage selection */ + if (chip_type == CHIP_HIXX51) + return 0; + + if ((chip_type >= CHIP_TYPE_MAX_NUM) || (chip_type < 0)) { + pr_err("chip type error %d!\n", chip_type); + return -1; + } + + if (sd_hwlock == NULL) + return 0; + + if (sys_base == NULL) { + pr_err("sys_base is null, can't switch 1.8V or 3.0V !\n"); + return -1; + } + + /* + * 1s timeout, if we can't get sd_hwlock, + * sd card module will init failed + */ + if (hwspin_lock_timeout_irqsave(sd_hwlock, SD_LOCK_TIMEOUT, &flag)) { + pr_warn("%s: hwspinlock timeout!\n", __func__); + return 0; + } + + /*sysctrl offset: 0x3780 bit[1:0], 0x3--1.8v, 0x0--3.3v*/ + reg = readl(sys_base + hs_dwmmc_reg[chip_type][SC_BIAS_CTRL]); + if (set) + reg |= 0x3; + else + reg &= (~0x3); + + writel(reg, sys_base + hs_dwmmc_reg[chip_type][SC_BIAS_CTRL]); + + hwspin_unlock_irqrestore(sd_hwlock, &flag); + pr_info(" reg = 0x%x\n", reg); + return 0; +} + +int dw_mci_clk_set_rate(struct dw_mci *host, u32 clk) +{ + u32 reg = 0; + u32 freq_div_flag = 0; + u32 pll5_div = 0; + u32 chip_type; + struct dw_mci_hs_priv_data *priv = NULL; + + if ((peri_subctrl_base == NULL) || + (sys_base == NULL) || + (host == NULL)) { + dev_err(host->dev, + "peri_subctrl_base or sys_base is null, can't rst!\n" + ); + return -1; + } + priv = host->priv; + chip_type = priv->chip_type; + + /* peri_subctrl offset: 0x3500 bit[0], 0x1--PLL5(1.6G) 0x0--3.2M */ + reg = readl(peri_subctrl_base + + hs_dwmmc_reg[chip_type][PERI_SC_EMMC_CLK_SEL]); + if (clk > PLL5_CLK_DEFAULT_FREQ) { + reg |= 0x1; + freq_div_flag = 1; + } else { + reg &= ~0x1; + freq_div_flag = 0; + } + writel(reg, peri_subctrl_base + + hs_dwmmc_reg[chip_type][PERI_SC_EMMC_CLK_SEL]); + + if (freq_div_flag) { + /* set eMMC level-1 freq divider to 1.6G/div=xxxM, */ + /* sysctrl offset: 0x3688 bit[27:24] = div */ + pll5_div = PLL5_CLK_FREQ_MAX/clk; + if (pll5_div > PLL5_EMMC_DIV_MAX) { + dev_warn(host->dev, + "pll5_div(%d) invalid, max(16)\n", pll5_div); + pll5_div = PLL5_EMMC_DIV_MAX; + } + + if (pll5_div > 0) + pll5_div--; + + /* Hixx51 FPGA B500 not support pll configure */ + if (priv->chip_type == CHIP_HIXX10) { + reg = readl(sys_base + + hs_dwmmc_reg[chip_type][SC_PLL_PROF_CFG0]); + reg &= 0xf0ffffff; + reg |= (pll5_div << 24); + writel(reg, sys_base + + hs_dwmmc_reg[chip_type][SC_PLL_PROF_CFG0]); + } else if ((priv->chip_type == CHIP_HIXX51) && + (priv->chip_platform == SDMMC_ASIC_PLATFORM)) { + reg = readl(peri_subctrl_base + + hs_dwmmc_reg[chip_type][PERI_SC_EMMC_CLK_SEL]); + reg &= HIXX51_EMMC_CLK_SEL_PLL_OFF; + reg |= (pll5_div << HIXX51_EMMC_CLK_SEL_PLL_L_S); + writel(reg, peri_subctrl_base + + hs_dwmmc_reg[chip_type][PERI_SC_EMMC_CLK_SEL]); + } + } + + return 0; +} + + +static void dw_mci_hs_set_ios_power_off(struct dw_mci *host) +{ + struct dw_mci_hs_priv_data *priv = host->priv; + int ret; + + dev_info(host->dev, "set io to lowpower\n"); + /* set pin to idle, skip emmc for vccq keeping power always on */ + if ((host->hw_mmc_id == DWMMC_SD_ID) && + !(check_himntn(HIMNTN_SD2JTAG) || + check_himntn(HIMNTN_SD2DJTAG))) { + if ((host->pinctrl) && (host->pins_idle)) { + ret = pinctrl_select_state(host->pinctrl, + host->pins_idle); + if (ret) + dev_warn(host->dev, + "could not set idle pins\n"); + } + } else if ((host->hw_mmc_id != DWMMC_EMMC_ID) && + (host->hw_mmc_id != DWMMC_SD_ID)) { + if ((host->pinctrl) && (host->pins_idle)) { + ret = pinctrl_select_state(host->pinctrl, + host->pins_idle); + if (ret) + dev_warn(host->dev, + "could not set idle pins\n"); + } + } + + if (host->vqmmc) { + ret = regulator_disable(host->vqmmc); + if (ret) + dev_warn(host->dev, + "regulator_disable vqmmc failed\n"); + } else { + if ((host->hw_mmc_id == DWMMC_SD_ID) && + (priv->chip_platform == SDMMC_ASIC_PLATFORM)) { + ret = pmu_ldo9_disable(); + if (ret) + dev_warn(host->dev, + "pmu_ldo9_disable failed\n"); + } + } + + if (host->vmmc) { + ret = regulator_disable(host->vmmc); + if (ret) + dev_warn(host->dev, "regulator_disable vmmc failed\n"); + } else { + if ((host->hw_mmc_id == DWMMC_SD_ID) && + (priv->chip_platform == SDMMC_ASIC_PLATFORM)) { + ret = pmu_ldo16_disable(); + if (ret) + dev_warn(host->dev, + "pmu_ldo16_disable vmmc failed\n"); + } + } + +} + +static void dw_mci_hs_set_ios_power_up(struct dw_mci *host) +{ + struct dw_mci_hs_priv_data *priv = host->priv; + int ret; + + dev_info(host->dev, "set io to normal\n"); + if (priv->hi3660_fpga_sd_ioset == HI3660_FPGA) { + /* set GPIO15[0] and GPIO15[1] to outpot */ + /* set GPIO15[0] to High */ + /* set GPIO15[1] to Low */ + (void)gpio_request( + priv->hi3660_sd_ioset_jtag_sd_sel, "jtag_sd_sel"); + (void)gpio_request(priv->hi3660_sd_ioset_sd_sel, "sd_sel"); + gpio_direction_output(priv->hi3660_sd_ioset_jtag_sd_sel, 0); + gpio_direction_output(priv->hi3660_sd_ioset_sd_sel, 1); + dev_info(host->dev, "set Hi3660 FPGA sd io\n"); + gpio_free(priv->hi3660_sd_ioset_jtag_sd_sel); + gpio_free(priv->hi3660_sd_ioset_sd_sel); + } + if (host->hw_mmc_id == DWMMC_SD_ID) { + ret = dw_mci_set_sel18(priv->chip_type, 0); + if (ret) + dev_err(host->dev, " ios dw_mci_set_sel18 error!\n"); + /* Wait for 5ms */ + usleep_range(5000, 5500); + if (host->vqmmc) + host->vqmmc = NULL; + if (host->vmmc) + host->vmmc = NULL; + if (host->vqmmc) { + ret = regulator_set_voltage( + host->vqmmc, 2950000, 2950000); + if (ret) + dev_err(host->dev, + "regulator_set_voltage failed !\n"); + ret = regulator_enable(host->vqmmc); + if (ret) + dev_err(host->dev, + "regulator_enable failed !\n"); + usleep_range(1000, 1500); + } else { + if ((host->hw_mmc_id == DWMMC_SD_ID) && + (priv->chip_platform == SDMMC_ASIC_PLATFORM)) { + ret = pmu_ldo9_set_voltage(2950); + if (ret) + dev_err(host->dev, + "pmu_ldo9_set_voltage failed !\n"); + + ret = pmu_ldo9_enable(); + if (ret) + dev_err(host->dev, + "pmu_ldo9_enable failed !\n"); + usleep_range(1000, 1500); + } + } + if (host->vmmc) { + ret = regulator_set_voltage( + host->vmmc, 2950000, 2950000); + if (ret) + dev_err(host->dev, + "regulator_set_voltage failed !\n"); + ret = regulator_enable(host->vmmc); + if (ret) + dev_err(host->dev, + "regulator_enable failed !\n"); + usleep_range(1000, 1500); + } else { + if ((host->hw_mmc_id == DWMMC_SD_ID) && + (priv->chip_platform == SDMMC_ASIC_PLATFORM)) { + ret = pmu_ldo16_set_voltage(2950); + if (ret) + dev_err(host->dev, + "pmu_ldo16_set_voltage failed !\n"); + ret = pmu_ldo16_enable(); + if (ret) + dev_err(host->dev, + "pmu_ldo16_enable failed !\n"); + usleep_range(1000, 1500); + } + } + + if (!(check_himntn(HIMNTN_SD2JTAG) || + check_himntn(HIMNTN_SD2DJTAG))) { + if ((host->pinctrl) && (host->pins_default)) { + ret = pinctrl_select_state(host->pinctrl, + host->pins_default); + if (ret) + dev_warn(host->dev, + "could not set default pins\n"); + } + } + return; + } + if ((host->pinctrl) && (host->pins_default)) { + ret = pinctrl_select_state(host->pinctrl, host->pins_default); + if (ret) + dev_warn(host->dev, "could not set default pins\n"); + } + if (host->vmmc) { + ret = regulator_set_voltage(host->vmmc, 2950000, 2950000); + if (ret) + dev_err(host->dev, "regulator_set_voltage failed !\n"); + + ret = regulator_enable(host->vmmc); + if (ret) + dev_err(host->dev, "regulator_enable failed !\n"); + } else { + if ((host->hw_mmc_id == DWMMC_SD_ID) && + (priv->chip_platform == SDMMC_ASIC_PLATFORM)) { + ret = pmu_ldo16_set_voltage(2950); + if (ret) + dev_err(host->dev, + "pmu_ldo16_set_voltage failed !\n"); + + ret = pmu_ldo16_enable(); + if (ret) + dev_err(host->dev, + "pmu_ldo16_enable failed !\n"); + } + } + + if (host->vqmmc) { + ret = regulator_set_voltage(host->vqmmc, 2950000, 2950000); + if (ret) + dev_err(host->dev, "regulator_set_voltage failed !\n"); + + ret = regulator_enable(host->vqmmc); + if (ret) + dev_err(host->dev, "regulator_enable failed !\n"); + } else { + if ((host->hw_mmc_id == DWMMC_SD_ID) && + (priv->chip_platform == SDMMC_ASIC_PLATFORM)) { + ret = pmu_ldo9_set_voltage(2950); + if (ret) + dev_err(host->dev, + "pmu_ldo9_set_voltage failed !\n"); + + ret = pmu_ldo9_enable(); + if (ret) + dev_err(host->dev, + "pmu_ldo9_enable failed !\n"); + } + } + +} + +static void dw_mci_hs_set_ios(struct dw_mci *host, struct mmc_ios *ios) +{ + struct dw_mci_hs_priv_data *priv = host->priv; + int id = priv->id; + int ret; + + + if (priv->old_power_mode != ios->power_mode) { + switch (ios->power_mode) { + case MMC_POWER_OFF: + dw_mci_hs_set_ios_power_off(host); + break; + case MMC_POWER_UP: + dw_mci_hs_set_ios_power_up(host); + break; + case MMC_POWER_ON: + break; + default: + dev_info(host->dev, "unknown power supply mode\n"); + break; + } + priv->old_power_mode = ios->power_mode; + } + + if (priv->old_timing != ios->timing) { + if (peri_subctrl_base == NULL) { + dev_err(host->dev, "peri_subctrl_base is null,\n"); + dev_err(host->dev, + "can't disable and enable clock!\n"); + return; + } + + /* disable clock for fpga */ + writel(0x1, peri_subctrl_base + + hs_dwmmc_reg[priv->chip_type][PERI_SC_EMMC_ICG_DIS]); + + ret = dw_mci_clk_set_rate(host, + hs_timing_config[id][ios->timing][0]); + if (ret) + dev_err(host->dev, "dw_mci_clk_set_rate failed\n"); + + /* enable clock for fpga */ + writel(0x1, peri_subctrl_base + + hs_dwmmc_reg[priv->chip_type][PERI_SC_EMMC_ICG_EN]); + + if (priv->in_resume != STATE_KEEP_PWR) + host->tuning_init_sample = + (hs_timing_config[id][ios->timing][4] + + hs_timing_config[id][ios->timing][5]) / 2; + + if (host->sd_reinit == 0) + host->current_div = + hs_timing_config[id][ios->timing][1]; + + dw_mci_hs_set_timing(host, id, ios->timing, + host->tuning_init_sample, host->current_div); + + if (priv->priv_bus_hz == 0) + host->bus_hz = hs_timing_config[id][ios->timing][6]; + else + host->bus_hz = + 2 * hs_timing_config[id][ios->timing][6]; + + if (priv->dw_mmc_bus_clk) { + /*if FPGA, the clk for SD should be 20M */ + host->bus_hz = priv->dw_mmc_bus_clk; + } + + priv->old_timing = ios->timing; + } +} + +static void dw_mci_hs_prepare_command(struct dw_mci *host, u32 *cmdr) +{ + *cmdr |= SDMMC_CMD_USE_HOLD_REG; +} + +int dw_mci_hs_get_dt_pltfm_resource(struct device_node *of_node) +{ + if (of_device_is_compatible(of_node, "hisilicon,davinci-dw-mshc")) { + if (of_find_property(of_node, + "cs_sd_timing_config", (int *)NULL)) { + memcpy(hs_timing_config, hs_timing_config_kirin970_cs, + sizeof(hs_timing_config)); + pr_info("%s: boston_cs_sd_timing_config_cs\n", + __func__); + pr_info("%s: is used for timing_config!\n", __func__); + } else { + memcpy(hs_timing_config, hs_timing_config_kirin970, + sizeof(hs_timing_config)); + pr_info("%s: boston_cs_sd_timing_config\n", __func__); + pr_info("%s: is used for timing_config!\n", __func__); + } + } else { + pr_err("%s: no compatible platform resource found!\n", + __func__); + return -1; + } + + return 0; +} + +int dw_mci_hs_get_resource(void) +{ + struct device_node *np = NULL; + + np = of_find_compatible_node(NULL, NULL, "hisilicon,davinci-dw-mshc"); + if (np == NULL) { + pr_err("can't find davinci-dw-mshc!\n"); + return -EFAULT; + } + + if (!peri_subctrl_base) { + peri_subctrl_base = of_iomap(np, MEM_PERI_SUBCTRL_IOBASE); + if (!peri_subctrl_base) { + pr_err("peri_subctrl_base iomap error!\n"); + return -ENOMEM; + } + } + + if (!sys_base) { + sys_base = of_iomap(np, MEM_SYSCTRL_IOBASE); + if (!sys_base) { + pr_err("sysctrl iomap error!\n"); + iounmap(peri_subctrl_base); + return -ENOMEM; + } + } + + return 0; +} + + +/* + * Do private setting specified for controller. + * dw_mci_hs_priv_init execute before controller unreset, + * this will cause NOC error. + * put this function after unreset and clock set. + */ +static void dw_mci_hs_priv_setting(struct dw_mci *host) +{ + /* set threshold to 512 bytes */ + mci_writel(host, CDTHRCTL, 0x02000001); +} + +void dw_mci_hs_set_rst_m(struct dw_mci *host, bool set) +{ + struct dw_mci_hs_priv_data *priv = host->priv; + u32 chip_type = priv->chip_type; + int id = priv->id; + + if (peri_subctrl_base == NULL) { + dev_err(host->dev, "peri_subctrl_base is null, can't rst!\n"); + return; + } + + if (set) { + if (id == DW_MCI_EMMC_ID) { + writel(BIT_RST_EMMC, peri_subctrl_base + + hs_dwmmc_reg[chip_type][PERI_SC_EMMC_RST_REQ]); + dev_info(host->dev, "reset_m for emmc\n"); + } else if (id == DW_MCI_SD_ID) { + writel(BIT_RST_SD, peri_subctrl_base + + hs_dwmmc_reg[chip_type][PERI_SC_EMMC_RST_REQ]); + dev_info(host->dev, "reset_m for sd\n"); + } else { + dev_info(host->dev, + "other reset_m need to add, id : %d\n", id); + } + } else { + if (id == DW_MCI_EMMC_ID) { + writel(BIT_RST_EMMC, peri_subctrl_base + + hs_dwmmc_reg[chip_type][PERI_SC_EMMC_RST_DREQ] + ); + dev_info(host->dev, "unreset_m for emmc\n"); + } else if (id == DW_MCI_SD_ID) { + writel(BIT_RST_SD, peri_subctrl_base + + hs_dwmmc_reg[chip_type][PERI_SC_EMMC_RST_DREQ] + ); + dev_info(host->dev, "unreset_m for sd\n"); + } else { + dev_info(host->dev, + "other unreset_m need to add, id : %d\n", id); + } + } +} + +int dw_mci_hs_set_controller(struct dw_mci *host, bool set) +{ + struct dw_mci_hs_priv_data *priv = host->priv; + u32 chip_type = priv->chip_type; + int id = priv->id; + + if (peri_subctrl_base == NULL) { + dev_err(host->dev, + "peri_subctrl_base is null, can't reset mmc!\n"); + return -1; + } + + if (set) { + /* disable clock for fpga */ + writel(0x1, peri_subctrl_base + + hs_dwmmc_reg[chip_type][PERI_SC_EMMC_ICG_DIS]); + + if (id == DW_MCI_EMMC_ID) { + writel(BIT_RST_EMMC, peri_subctrl_base + + hs_dwmmc_reg[chip_type][PERI_SC_EMMC_RST_REQ]); + goto out; + } else if (id == DW_MCI_SD_ID) { + writel(BIT_RST_SD, peri_subctrl_base + + hs_dwmmc_reg[chip_type][PERI_SC_EMMC_RST_REQ]); + goto out; + } else { + goto out; + } + } else { + /* enable clock for fpga */ + writel(0x1, peri_subctrl_base + + hs_dwmmc_reg[chip_type][PERI_SC_EMMC_ICG_EN]); + dev_info(host->dev, "eMMC/SD clock gate enable\n"); + + if (id == DW_MCI_EMMC_ID) { + writel(BIT_RST_EMMC, peri_subctrl_base + + hs_dwmmc_reg[chip_type][PERI_SC_EMMC_RST_DREQ] + ); + goto out; + } else if (id == DW_MCI_SD_ID) { + writel(BIT_RST_SD, peri_subctrl_base + + hs_dwmmc_reg[chip_type][PERI_SC_EMMC_RST_DREQ] + ); + goto out; + } else { + goto out; + } + + } +out: + return 0; +} + +struct dw_mci *sdio_host; + +void dw_mci_sdio_card_detect(struct dw_mci *host) +{ + if (host == NULL) { + dev_info(host->dev, "sdio detect, host is null,\n"); + dev_info(host->dev, "can not used to detect sdio\n"); + return; + } + + dw_mci_set_cd(host); + + queue_work(host->card_workqueue, &host->card_work); + return; +}; + +void dw_mci_sdio_card_detect_change(void) +{ + dw_mci_sdio_card_detect(sdio_host); +} +EXPORT_SYMBOL(dw_mci_sdio_card_detect_change); + +static int dw_mci_hs_priv_device_info(struct dw_mci *host) +{ + struct dw_mci_hs_priv_data *priv = host->priv; + + priv->chip_platform = SDMMC_ASIC_PLATFORM; + + if (of_property_read_u32(host->dev->of_node, "device-type", + &priv->chip_type)) { + dev_info(host->dev, "can't find device_type!\n"); + priv->chip_type = CHIP_HIXX10; + } else { + if (priv->chip_type >= CHIP_TYPE_MAX_NUM) { + dev_err(host->dev, "device type %d error!\n", + priv->chip_type); + return -EFAULT; + } + } + + if (of_find_property(host->dev->of_node, "board_fpga", NULL)) + priv->chip_platform = SDMMC_FPGA_PLATFORM; + + dev_info(host->dev, "chip type %d chip platform %d!\n", priv->chip_type, + priv->chip_platform); + + return 0; +} + +static int dw_mci_hs_init_specific(struct dw_mci *host) +{ + struct platform_device *pdev = NULL; +#ifdef CONFIG_MMC_DW_EMMC_USED_AS_MODEM + static const char *const hi_mci0 = "hi_mci.3"; +#else + static const char *const hi_mci0 = "hi_mci.0"; +#endif + static const char *const hi_mci1 = "hi_mci.1"; + static const char *const hi_mci2 = "hi_mci.2"; + int error; + int ret; + + struct dw_mci_hs_priv_data *priv = host->priv; + + /* BUG: device rename krees old name, which would be realloced for */ + /* other device, pdev->name points to freed space, */ + /* driver match may cause a panic for wrong device */ + pdev = container_of(host->dev, struct platform_device, dev); + + switch (priv->id) { + case MMC_EMMC: + pdev->name = hi_mci0; + error = device_rename(host->dev, hi_mci0); + if (error < 0) { + dev_err(host->dev, "dev set name %s fail\n", hi_mci0); + goto fail; + } + + /* Sd hardware lock,avoid to access the SCPERCTRL5 */ + /* register in USIM card module in the same time */ + sd_hwlock = hwspin_lock_request_specific(SD_HWLOCK_ID); + if (sd_hwlock == NULL) { + dev_err(host->dev, "Request hwspin lock failed !\n"); + goto fail; + } + + ret = dw_mci_set_sel18(priv->chip_type, 1); + if (ret) + dev_err(host->dev, " ios dw_mci_set_sel18 error!\n"); + +#ifndef CONFIG_MMC_DW_EMMC_USED_AS_MODEM +#ifdef CONFIG_HISI_BOOTDEVICE + if (get_bootdevice_type() == BOOT_DEVICE_EMMC) + set_bootdevice_name(&pdev->dev); +#endif +#endif + break; + case MMC_SD: + pdev->name = hi_mci1; + error = device_rename(host->dev, hi_mci1); + if (error < 0) { + dev_err(host->dev, "dev set name hi_mci.1 fail\n"); + goto fail; + } + + /* Sd hardware lock,avoid to access the SCPERCTRL5 */ + /* register in USIM card module in the same time */ + sd_hwlock = hwspin_lock_request_specific(SD_HWLOCK_ID); + if (sd_hwlock == NULL) { + dev_err(host->dev, "Request hwspin lock failed !\n"); + goto fail; + } + + ret = dw_mci_set_sel18(priv->chip_type, 0); + if (ret) + dev_err(host->dev, " ios dw_mci_set_sel18 error!\n"); + break; + case MMC_SDIO: + pdev->name = hi_mci2; + error = device_rename(host->dev, hi_mci2); + if (error < 0) { + dev_err(host->dev, "dev set name hi_mci.2 fail\n"); + goto fail; + } + + break; + + default: + dev_err(host->dev, "mpriv->id is out of range!!!\n"); + goto fail; + } + /* still keep pdev->name same with dev->kobj.name */ + pdev->name = host->dev->kobj.name; + return 0; + +fail: + /* if rename failed, restore old value, keep pdev->name same to */ + /* dev->kobj.name */ + pdev->name = host->dev->kobj.name; + return -1; +} + + +static int dw_mci_hs_priv_init(struct dw_mci *host) +{ + struct dw_mci_hs_priv_data *priv = NULL; + u32 reg; + int ret; + + priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + priv->id = of_alias_get_id(host->dev->of_node, "mshc"); + /* BEGIN masked for davinci, 2018/01/08 */ + + /* BEGIN masked for davinci, 2018/01/08 */ + priv->old_timing = -1; + priv->in_resume = STATE_LEGACY; + host->priv = priv; + host->hw_mmc_id = priv->id; + host->flags &= ~DWMMC_IN_TUNING; + host->flags &= ~DWMMC_TUNING_DONE; + + /* + * Here for SD, the default value of voltage-switch gpio, + * which is only used in hi3650 FPGA, is set to (-1) for ASIC + */ + priv->dw_voltage_switch_gpio = SDMMC_ASIC_PLATFORM; + + ret = dw_mci_hs_priv_device_info(host); + if (ret) { + dev_err(host->dev, "device info error!\n"); + return -1; + } + + /* Hixx51 FPGA B500 not support sysctrl pll configure */ + if ((sys_base != NULL) && (priv->chip_type == CHIP_HIXX10)) { + /* Set the level-1 frequency divider of the eMMC + * to divide by 2. 1.6G/2=800M, + * sysctrl offset: 0x3688 bit[27:24] = 1 + */ + reg = readl(sys_base + + hs_dwmmc_reg[priv->chip_type][SC_PLL_PROF_CFG0]); + reg &= 0xf0ffffff; + reg |= 0x01000000; + writel(reg, sys_base + + hs_dwmmc_reg[priv->chip_type][SC_PLL_PROF_CFG0]); + } else if ((peri_subctrl_base != NULL) && + (priv->chip_type == CHIP_HIXX51) && + (priv->chip_platform == SDMMC_ASIC_PLATFORM)) { + /* Set the level-1 frequency divider of the eMMC + * to divide by 2. 1.6G/2=800M, + * peri_subctrl_base offset: 0x104 bit[7:4] = 1 + */ + reg = readl(peri_subctrl_base + + hs_dwmmc_reg[priv->chip_type][PERI_SC_EMMC_CLK_SEL]); + reg &= HIXX51_EMMC_CLK_SEL_PLL_OFF; + reg |= HIXX51_EMMC_CLK_SEL_PLL_VAL; + writel(reg, peri_subctrl_base + + hs_dwmmc_reg[priv->chip_type][PERI_SC_EMMC_CLK_SEL]); + } + + if (priv->id == DW_MCI_SDIO_ID) + sdio_host = host; + + ret = dw_mci_hs_init_specific(host); + if (ret) { + devm_kfree(host->dev, priv); + dev_err(host->dev, "device init specific error!\n"); + return -1; + } + + return 0; +} + +int dw_mci_hs_setup_clock(struct dw_mci *host) +{ + struct dw_mci_hs_priv_data *priv = host->priv; + int timing = MMC_TIMING_LEGACY; + int id = priv->id; + int ret; + + ret = dw_mci_clk_set_rate(host, hs_timing_config[id][timing][0]); + if (ret) + dev_err(host->dev, "dw_mci_clk_set_rate failed\n"); + + dw_mci_hs_set_controller(host, 0); + dw_mci_hs_priv_setting(host); + + host->tuning_current_sample = -1; + host->current_div = hs_timing_config[id][timing][1]; + + host->tuning_init_sample = + (hs_timing_config[id][timing][4] + + hs_timing_config[id][timing][5]) / 2; + + dw_mci_hs_set_timing(host, id, timing, host->tuning_init_sample, + host->current_div); + + if (priv->priv_bus_hz == 0) + host->bus_hz = hs_timing_config[id][timing][6]; + else + host->bus_hz = priv->priv_bus_hz; + + if (priv->dw_mmc_bus_clk) { + /* if FPGA, the clk for SD should be 20M */ + host->bus_hz = priv->dw_mmc_bus_clk; + } + + priv->old_timing = timing; + + return 0; +} + +static int dw_mci_dt_get_bus_width(struct dw_mci *host) +{ + struct device_node *cnp = NULL; + struct device_node *np = NULL; + u32 bus_width = 0; + + np = host->dev->of_node; + for_each_child_of_node(np, cnp) { + if (!of_property_read_u32(cnp, "bus-width", &bus_width)) + break; + + dev_info(host->dev, ""bus-width" property is missing,\n"); + dev_info(host->dev, " assuming 1 bit.\n"); + bus_width = EMMC_BUS_WIDTH_1_BIT; + } + dev_info(host->dev, ""bus-width" value %u\n", bus_width); + switch (bus_width) { + case EMMC_BUS_WIDTH_8_BIT: + hs_dwmmc_caps[DW_MCI_EMMC_ID] |= MMC_CAP_8_BIT_DATA; + break; + case EMMC_BUS_WIDTH_4_BIT: + hs_dwmmc_caps[DW_MCI_EMMC_ID] |= MMC_CAP_4_BIT_DATA; + break; + case EMMC_BUS_WIDTH_1_BIT: + break; + default: + dev_err(host->dev, + "Invalid "bus-width" value %u!\n", bus_width); + return -EINVAL; + } + return 0; +} + +static int dw_mci_hs_parse_dt(struct dw_mci *host) +{ + struct dw_mci_hs_priv_data *priv = host->priv; + struct device_node *np = host->dev->of_node; + u32 value = 0; + int error = 0; + + error = dw_mci_hs_get_dt_pltfm_resource(np); + if (error) + return error; + + if (of_find_property(np, "hi3660_fpga_sd_ioset", NULL)) { + priv->hi3660_fpga_sd_ioset = HI3660_FPGA; + dev_info(host->dev, "fpga_sd_ioset is %d", + priv->hi3660_fpga_sd_ioset); + } + + priv->hi3660_sd_ioset_sd_sel = + of_get_named_gpio(np, "hi3660_sd_ioset_sd_sel", 0); + if (!gpio_is_valid(priv->hi3660_sd_ioset_sd_sel)) { + dev_info(host->dev, "sd_ioset_sd_sel not available\n"); + priv->hi3660_sd_ioset_sd_sel = -1; + } + + priv->hi3660_sd_ioset_jtag_sd_sel = + of_get_named_gpio(np, "hi3660_sd_ioset_jtag_sd_sel", 0); + if (!gpio_is_valid(priv->hi3660_sd_ioset_jtag_sd_sel)) { + dev_info(host->dev, "sd_ioset_jtag_sd_sel not available\n"); + priv->hi3660_sd_ioset_jtag_sd_sel = -1; + } + + if (of_find_property(np, "hi6250-timing-65M", NULL)) { + hs_dwmmc_caps[DW_MCI_SDIO_ID] |= + (MMC_CAP_UHS_SDR12 | + MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50); + hs_timing_config[2][5][0] = 535000000; + dev_info(host->dev, "exit setup timing clock 65M.\n"); + } + + if (of_find_property(np, "wifi_sdio_sdr104_156M", (int *)NULL)) { + hs_dwmmc_caps[DW_MCI_SDIO_ID] |= + (MMC_CAP_UHS_SDR12 | + MMC_CAP_UHS_SDR25 | + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104); + hs_timing_config[2][6][1] = 9; + hs_timing_config[2][6][4] = 19; + hs_timing_config[2][6][6] = 160000000; + host->wifi_sdio_sdr104_160M = 0xaaaa; + dev_info(host->dev, "set berlin sdio sdr104 156M.\n"); + } + + if (of_find_property(np, "wifi_sdio_sdr104_177M", (int *)NULL)) { + hs_dwmmc_caps[DW_MCI_SDIO_ID] |= + (MMC_CAP_UHS_SDR12 | + MMC_CAP_UHS_SDR25 | + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104); + hs_timing_config[2][6][1] = 8; + hs_timing_config[2][6][4] = 17; + hs_timing_config[2][6][6] = 177777777; + host->wifi_sdio_sdr104_177M = 0xaaaa; + dev_info(host->dev, "set berlin sdio sdr104 177M.\n"); + } + + if (of_property_read_u32(np, "hisi,bus_hz", &value)) { + dev_info(host->dev, "bus_hz property not found, using\n"); + dev_info(host->dev, "value of 0 as default\n"); + value = 0; + } + priv->priv_bus_hz = value; + dev_info(host->dev, "dts bus_hz = %d\n", priv->priv_bus_hz); + + value = 0; + if (of_property_read_u32(np, "cd-vol", &value)) { + dev_info(host->dev, "cd-vol property not found, using\n"); + dev_info(host->dev, "value of 0 as default\n"); + value = 0; + } + priv->cd_vol = value; + dev_info(host->dev, "dts cd-vol = %d\n", priv->cd_vol); + + if (of_find_property(np, "sdio_support_uhs", NULL)) + hs_dwmmc_caps[DW_MCI_SDIO_ID] |= + (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104); + + if (of_find_property(np, "sd_support_uhs", (int *)NULL)) { + hs_dwmmc_caps[DW_MCI_SD_ID] |= + (MMC_CAP_UHS_SDR12 | + MMC_CAP_UHS_SDR25 | + MMC_CAP_UHS_SDR50); + dev_info(host->dev, "set sd_support_uhs.\n"); + } + + /* find out mmc_bus_clk supported for hi3650 FPGA */ + if (of_property_read_u32(np, "board-mmc-bus-clk", + &(priv->dw_mmc_bus_clk))) { + dev_info(host->dev, "board mmc_bus_clk property not found,\n"); + dev_info(host->dev, "assuming asic board is available\n"); + + priv->dw_mmc_bus_clk = 0; + } + dev_info(host->dev, "######board-mmc-bus-clk is %x\n", + priv->dw_mmc_bus_clk); + + /* find out voltage switch supported by gpio for hi3650 FPGA */ + priv->dw_voltage_switch_gpio = of_get_named_gpio(np, + "board-sd-voltage-switch-gpio", 0); + if (!gpio_is_valid(priv->dw_voltage_switch_gpio)) { + dev_info(host->dev, + "board-sd-voltage-switch-gpio not available\n"); + priv->dw_voltage_switch_gpio = SDMMC_ASIC_PLATFORM; + } + dev_info(host->dev, "######dw_voltage_switch_gpio is %d\n", + priv->dw_voltage_switch_gpio); + + if (dw_mci_dt_get_bus_width(host)) + dev_info(host->dev, "Invalid emmc "bus-width" value !\n"); + + return 0; +} + +static irqreturn_t dw_mci_hs_card_detect(int irq, void *data) +{ + struct dw_mci *host = (struct dw_mci *)data; + + host->sd_reinit = 0; + host->sd_hw_timeout = 0; + host->flags &= ~DWMMC_IN_TUNING; + host->flags &= ~DWMMC_TUNING_DONE; + + queue_work(host->card_workqueue, &host->card_work); + return IRQ_HANDLED; +}; + +static int dw_mci_hs_get_cd(struct dw_mci *host, u32 slot_id) +{ + unsigned int status; + struct dw_mci_hs_priv_data *priv = host->priv; + + /* cd_vol = 1 means sdcard gpio detect pin active-high */ + if (priv->cd_vol) + status = !gpio_get_value(priv->gpio_cd); + else /* cd_vol = 0 means sdcard gpio detect pin active-low */ + status = gpio_get_value(priv->gpio_cd); + + /* If sd to jtag func enabled, make the SD always not present */ + if ((host->hw_mmc_id == DWMMC_SD_ID) && + (check_himntn(HIMNTN_SD2JTAG) + || check_himntn(HIMNTN_SD2DJTAG))) + status = 1; + + + dev_info(host->dev, " sd status = %d\n", status); + + return status; +} + +static int dw_mci_hs_cd_detect_init(struct dw_mci *host) +{ + struct dw_mci_hs_priv_data *priv = host->priv; + struct device_node *np = host->dev->of_node; + u32 shared_irq = 0; + int gpio; + int err; + + if (host->pdata->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION) + return 0; + + gpio = of_get_named_gpio(np, "cd-gpio", 0); + if (gpio_is_valid(gpio)) { + if (devm_gpio_request_one( + host->dev, gpio, GPIOF_IN, "dw-mci-cd")) { + dev_warn(host->dev, + "gpio [%d] request failed\n", gpio); + } else { + dev_info(host->dev, "gpio [%d] request\n", gpio); + + priv->gpio_cd = gpio; + host->pdata->get_cd = dw_mci_hs_get_cd; + + if (of_property_read_u32( + np, "shared-irq", &shared_irq)) { + dev_info(host->dev, + "shared-irq property not found,\n"); + dev_info(host->dev, + "using shared_irq of 0 as default\n"); + shared_irq = 0; + } + + if (shared_irq) { + err = devm_request_irq(host->dev, + gpio_to_irq(gpio), + dw_mci_hs_card_detect, + IRQF_TRIGGER_FALLING | + IRQF_TRIGGER_RISING | + IRQF_NO_SUSPEND + | IRQF_SHARED, + DRIVER_NAME, host); + } else { + err = devm_request_irq(host->dev, + gpio_to_irq(gpio), + dw_mci_hs_card_detect, + IRQF_TRIGGER_FALLING | + IRQF_TRIGGER_RISING, + DRIVER_NAME, host); + } + + if (err) + dev_warn(mmc_dev(host->dev), + "request gpio irq error\n"); + } + + } else { + dev_info(host->dev, "cd gpio not available"); + } + return 0; +} + +static int hs_dwmmc_card_busy(struct dw_mci *host) +{ + if ((mci_readl(host, STATUS) & SDMMC_STATUS_BUSY) || host->cmd + || host->data || host->mrq || (host->state != STATE_IDLE)) { + dev_vdbg(host->dev, " card is busy!"); + return 1; + } + + return 0; +} + +static int dw_mci_3_3v_signal_voltage_switch(struct dw_mci_slot *slot) +{ + struct dw_mci *host = slot->host; + struct dw_mci_hs_priv_data *priv = host->priv; + u32 reg; + int ret = 0; + + ret = dw_mci_set_sel18(priv->chip_type, 0); + if (ret) { + dev_err(host->dev, " dw_mci_set_sel18 error!\n"); + return ret; + } + + /* hixx51 not support voltage 3.3v */ + if (priv->chip_type == CHIP_HIXX51) { + dev_info(host->dev, "hixx51 not support voltage 3.3v\n"); + return -EPERM; + } + + /* Wait for 5ms */ + usleep_range(5000, 5500); + + /* only for SD voltage switch on hi3650 FPGA */ + if (priv->dw_voltage_switch_gpio != SDMMC_ASIC_PLATFORM) { + (void)gpio_request(priv->dw_voltage_switch_gpio, + "board-sd-voltage-switch-gpio"); + /* set the voltage to 3V for SD IO */ + gpio_direction_output(priv->dw_voltage_switch_gpio, 1); + gpio_free(priv->dw_voltage_switch_gpio); + } else { + if (host->vqmmc) { + ret = regulator_set_voltage(host->vqmmc, + 2950000, 2950000); + if (ret) { + dev_warn(host->dev, + "Switching to 3.3V signalling\n"); + dev_warn(host->dev, "voltage failed\n"); + return -EIO; + } + } else if ((host->hw_mmc_id == DWMMC_SD_ID) && + (priv->chip_platform == SDMMC_ASIC_PLATFORM)) { + ret = pmu_ldo9_set_voltage(2950); + if (ret) { + dev_warn(host->dev, + "Switching to 3.3V signalling\n"); + dev_warn(host->dev, "voltage failed\n"); + return -EIO; + } + } else { + reg = mci_readl(slot->host, UHS_REG); + reg &= ~(0x1 << slot->id); + mci_writel(slot->host, UHS_REG, reg); + } + } + + /* Wait for 5ms */ + usleep_range(5000, 5500); + + return ret; +} + +static int dw_mci_1_8v_signal_voltage_switch(struct dw_mci_slot *slot) +{ + unsigned long loop_count = 0x100000; + struct dw_mci *host = slot->host; + struct dw_mci_hs_priv_data *priv = host->priv; + int ret; + int intrs; + + /* disable interrupt upon voltage switch. handle interrupt here */ + /* and DO NOT triggle irq */ + mci_writel(host, CTRL, + (mci_readl(host, CTRL) & ~SDMMC_CTRL_INT_ENABLE)); + + /* stop clock */ + mci_writel(host, CLKENA, (0x0 << 0)); + mci_writel(host, CMD, SDMMC_CMD_ONLY_CLK | SDMMC_CMD_VOLT_SWITCH); + do { + if (!(mci_readl(host, CMD) & SDMMC_CMD_START)) + break; + loop_count--; + } while (loop_count); + + if (!loop_count) + dev_warn(host->dev, + " disable clock failed in voltage_switch\n"); + + mmiowb(); + + if (priv->dw_voltage_switch_gpio != SDMMC_ASIC_PLATFORM) { + (void)gpio_request(priv->dw_voltage_switch_gpio, + "board-sd-voltage-switch-gpio"); + /* set the voltage to 3V for SD IO */ + (void)gpio_direction_output(priv->dw_voltage_switch_gpio, 0); + gpio_free(priv->dw_voltage_switch_gpio); + } else { + if (host->vqmmc) { + ret = regulator_set_voltage(host->vqmmc, + 1800000, 1800000); + if (ret) { + dev_warn(host->dev, + "Switching to 1.8V signalling\n"); + dev_warn(host->dev, "voltage failed\n"); + return -EIO; + } + } else { + if ((host->hw_mmc_id == DWMMC_SD_ID) && + (priv->chip_platform == SDMMC_ASIC_PLATFORM)) { + ret = pmu_ldo9_set_voltage(1800); + if (ret) { + dev_warn(host->dev, + "Switching to 1.8V\n"); + dev_warn(host->dev, + "signalling voltage failed\n"); + return -EIO; + } + } + } + } + + /* Wait 55ms for pmu voltage drop*/ + usleep_range(55000, 55500); + + ret = dw_mci_set_sel18(priv->chip_type, 1); + if (ret) { + dev_err(host->dev, " dw_mci_set_sel18 error!\n"); + return ret; + } + + /* Wait for 5ms */ + usleep_range(5000, 5500); + + /* start clock */ + mci_writel(host, CLKENA, (0x1 << 0)); + mci_writel(host, CMD, SDMMC_CMD_ONLY_CLK | SDMMC_CMD_VOLT_SWITCH); + loop_count = 0x100000; + do { + if (!(mci_readl(host, CMD) & SDMMC_CMD_START)) + break; + loop_count--; + } while (loop_count); + + if (!loop_count) + dev_warn(host->dev, " enable clock failed in voltage_switch\n"); + + /* poll cd interrupt */ + loop_count = 0x100000; + do { + intrs = mci_readl(host, RINTSTS); + if (intrs & SDMMC_INT_CMD_DONE) { + dev_info(host->dev, " cd 0x%x in voltage_switch\n", + intrs); + mci_writel(host, RINTSTS, intrs); + break; + } + loop_count--; + } while (loop_count); + + if (!loop_count) + dev_warn(host->dev, " poll cd failed in voltage_switch\n"); + + /* enable interrupt */ + mci_writel(host, CTRL, (mci_readl(host, CTRL) | SDMMC_CTRL_INT_ENABLE)); + + mmiowb(); + + return ret; +} + +static int dw_mci_priv_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct dw_mci_slot *slot = mmc_priv(mmc); + int ret = 0; +/* BEGIN FPGA not support voltage switch, 2018/02/08 */ + /* only sd need to switch voltage */ + if (slot->host->hw_mmc_id != DWMMC_SD_ID) + return ret; + + pm_runtime_get_sync(mmc_dev(mmc)); + + if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) + ret = dw_mci_3_3v_signal_voltage_switch(slot); + else if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) + ret = dw_mci_1_8v_signal_voltage_switch(slot); + + pm_runtime_mark_last_busy(mmc_dev(mmc)); + pm_runtime_put_autosuspend(mmc_dev(mmc)); +/* END FPGA not support voltage switch, 2018/02/08 */ + return ret; +} + +void dw_mci_set_timeout(struct dw_mci *host) +{ + /* timeout (maximum) */ + mci_writel(host, TMOUT, 0xffffffff); +} + +static void dw_mci_hs_tuning_clear_flags(struct dw_mci *host) +{ + host->tuning_sample_flag = 0; +} + +static bool dw_mci_hi3xxx_wait_reset(struct device *dev, + struct dw_mci *host, unsigned int reset_val) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(50); + unsigned int ctrl; + + ctrl = mci_readl(host, CTRL); + ctrl |= reset_val; + mci_writel(host, CTRL, ctrl); + + /* wait till resets clear */ + do { + if (!(mci_readl(host, CTRL) & reset_val)) + return true; + } while (time_before(jiffies, timeout)); + + dev_warn(dev, "Timeout resetting block (ctrl %#x)\n", ctrl); + + return false; +} + +static bool mci_hi3xxx_wait_reset(struct dw_mci *host) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(50); + unsigned int ctrl; + + mci_writel(host, CTRL, (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | + SDMMC_CTRL_DMA_RESET)); + + /* wait till resets clear */ + do { + ctrl = mci_readl(host, CTRL); + if (!(ctrl & (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | + SDMMC_CTRL_DMA_RESET))) + return true; + } while (time_before(jiffies, timeout)); + + dev_warn(host->dev, "Timeout resetting block (ctrl %#x)\n", ctrl); + + return false; +} + +static void dw_mci_hi3xxx_mci_send_cmd(struct dw_mci *host, u32 cmd, u32 arg) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(100); + unsigned int cmd_status = 0; + int try = 3; + + mci_writel(host, CMDARG, arg); + /* Synchronous execution */ + wmb(); + mci_writel(host, CMD, SDMMC_CMD_START | cmd); + + do { + while (time_before(jiffies, timeout)) { + cmd_status = mci_readl(host, CMD); + if (!(cmd_status & SDMMC_CMD_START)) + return; + } + + dw_mci_hi3xxx_wait_reset(host->dev, host, SDMMC_CTRL_RESET); + mci_writel(host, CMD, SDMMC_CMD_START | cmd); + timeout = jiffies + msecs_to_jiffies(100); + } while (--try); + + dev_warn(host->dev, "hi3xxx_dw_mmc\n"); + dev_warn(host->dev, + "Timeout sending command (cmd %#x arg %#x status %#x)\n", + cmd, arg, cmd_status); +} + +static void dw_mci_hi3xxx_work_fail_reset(struct dw_mci *host) +{ + struct dw_mci_hs_priv_data *priv = host->priv; + + unsigned int retval = 0; + unsigned int ctype; + unsigned int clkena; + unsigned int clkdiv; + unsigned int uhs_reg; + unsigned int uhs_reg_ext; + unsigned int enable_shift; + unsigned int gpio; + unsigned int fifoth; + unsigned int timeout; + unsigned int cardthrctrl; + unsigned int _rintsts; + unsigned int _tcbcnt; + unsigned int _tbbcnt; + unsigned int _fifoth; + + if ((priv->id != DW_MCI_SD_ID) && (priv->id != DW_MCI_SDIO_ID)) { + dev_err(host->dev, "Not support now, return\n"); + return; + } + + dev_warn(host->dev, "Start to reset SDIO IP\n"); + mci_writel(host, CTRL, (mci_readl(host, CTRL) & (~INT_ENABLE))); + mci_writel(host, INTMASK, 0); + + mci_writel(host, RINTSTS, INTMSK_ALL); + +#ifdef CONFIG_MMC_DW_IDMAC + if (host->dma_64bit_address == SDMMC_32_BIT_DMA) + mci_writel(host, IDSTS, IDMAC_INT_CLR); + else + mci_writel(host, IDSTS64, IDMAC_INT_CLR); +#endif + + ctype = mci_readl(host, CTYPE); + clkena = mci_readl(host, CLKENA); + clkdiv = mci_readl(host, CLKDIV); + fifoth = mci_readl(host, FIFOTH); + timeout = mci_readl(host, TMOUT); + cardthrctrl = mci_readl(host, CDTHRCTL); + uhs_reg = mci_readl(host, UHS_REG); + uhs_reg_ext = mci_readl(host, UHS_REG_EXT); + enable_shift = mci_readl(host, ENABLE_SHIFT); + gpio = mci_readl(host, GPIO); + + _rintsts = mci_readl(host, RINTSTS); + _tcbcnt = mci_readl(host, TCBCNT); + _tbbcnt = mci_readl(host, TBBCNT); + retval = mci_readl(host, CTRL); + + dev_info(host->dev, + "before ip reset: CTRL=%x, UHS_REG_EXT=%x, ENABLE_SHIFT=%x,\n", + retval, uhs_reg_ext, enable_shift); + dev_info(host->dev, + " GPIO=%x, CLKEN=%d, CLKDIV=%d, TMOUT=%x, RINTSTS=%x,\n", + gpio, clkena, clkdiv, timeout, _rintsts); + dev_info(host->dev, " TCBCNT=%x, TBBCNT=%x,FIFOTH=%x\n", + _tcbcnt, _tbbcnt, fifoth); + + udelay(20); + + dw_mci_hs_set_rst_m(host, 1); + dw_mci_hs_set_controller(host, 1); + + if (!IS_ERR(host->ciu_clk)) + clk_disable_unprepare(host->ciu_clk); + + dw_mci_hs_set_rst_m(host, 0); + + if (!IS_ERR(host->ciu_clk)) { + if (clk_prepare_enable(host->ciu_clk)) + dev_err(host->dev, "ciu_clk clk_prepare_enable failed\n"); + } + + dw_mci_hs_set_controller(host, 0); + + udelay(20); + mci_hi3xxx_wait_reset(host); + + mci_writel(host, CTYPE, ctype); + mci_writel(host, FIFOTH, fifoth); + mci_writel(host, TMOUT, timeout); + mci_writel(host, CDTHRCTL, cardthrctrl); + mci_writel(host, UHS_REG, uhs_reg); + mci_writel(host, GPIO, 0x0); + udelay(10); + mci_writel(host, UHS_REG_EXT, uhs_reg_ext); + mci_writel(host, ENABLE_SHIFT, enable_shift); + mci_writel(host, GPIO, gpio | GPIO_CLK_ENABLE); + + mci_writel(host, BMOD, SDMMC_IDMAC_SWRESET); +#ifdef CONFIG_MMC_DW_IDMAC + if (host->dma_64bit_address == SDMMC_32_BIT_DMA) { + mci_writel(host, IDSTS, IDMAC_INT_CLR); + mci_writel(host, IDINTEN, SDMMC_IDMAC_INT_NI | + SDMMC_IDMAC_INT_RI | SDMMC_IDMAC_INT_TI); + mci_writel(host, DBADDR, host->sg_dma); + } else { + mci_writel(host, IDSTS64, IDMAC_INT_CLR); + mci_writel(host, IDINTEN64, SDMMC_IDMAC_INT_NI | + SDMMC_IDMAC_INT_RI | SDMMC_IDMAC_INT_TI); + mci_writel(host, DBADDRL, host->sg_dma & 0xffffffff); + mci_writel(host, DBADDRU, (u64)host->sg_dma >> 32); + } +#endif + + + mci_writel(host, RINTSTS, INTMSK_ALL); + mci_writel(host, INTMASK, 0); + mci_writel(host, RINTSTS, INTMSK_ALL); +#ifdef CONFIG_MMC_DW_IDMAC + if (host->dma_64bit_address == SDMMC_32_BIT_DMA) + mci_writel(host, IDSTS, IDMAC_INT_CLR); + else + mci_writel(host, IDSTS64, IDMAC_INT_CLR); +#endif + mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER | + SDMMC_INT_TXDR | SDMMC_INT_RXDR | DW_MCI_ERROR_FLAGS | + SDMMC_INT_CD); + /* Enable mci interrupt */ + mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); + + /* disable clock */ + mci_writel(host, CLKENA, 0); + mci_writel(host, CLKSRC, 0); + + /* inform CIU */ + dw_mci_hi3xxx_mci_send_cmd(host, + SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); + + /* set clock to desired speed */ + mci_writel(host, CLKDIV, clkdiv); + + /* inform CIU */ + dw_mci_hi3xxx_mci_send_cmd(host, + SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); + + mci_writel(host, CLKENA, clkena); + + /* inform CIU */ + dw_mci_hi3xxx_mci_send_cmd(host, + SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); + + retval = mci_readl(host, CTRL); + _rintsts = mci_readl(host, RINTSTS); + _tcbcnt = mci_readl(host, TCBCNT); + _tbbcnt = mci_readl(host, TBBCNT); + _fifoth = mci_readl(host, FIFOTH); + uhs_reg_ext = mci_readl(host, UHS_REG_EXT); + enable_shift = mci_readl(host, ENABLE_SHIFT); + gpio = mci_readl(host, GPIO); + + dev_info(host->dev, "after ip reset: CTRL=%x, UHS_REG_EXT=%x,\n", + retval, uhs_reg_ext); + dev_info(host->dev, + "ENABLE_SHIFT=%x, GPIO=%x, CLKEN=%d, CLKDIV=%d, TMOUT=%x,\n", + enable_shift, gpio, clkena, clkdiv, timeout); + dev_info(host->dev, "RINTSTS=%x, TCBCNT=%x, TBBCNT=%x,FIFOTH=%x\n", + _rintsts, _tcbcnt, _tbbcnt, _fifoth); +} + +static void dw_mci_hs_tuning_set_flags(struct dw_mci *host, int sample, int ok) +{ + if (ok) + host->tuning_sample_flag |= (1 << sample); + else + host->tuning_sample_flag &= ~(1 << sample); +} + +/* By tuning, find the best timing condition + * 1 -- tuning is not finished. And this function should be called again + * 0 -- Tuning successfully. + * If this function be called again, another round of tuning would be start + * -1 -- Tuning failed. Maybe slow down the clock and call this function again + */ +static int dw_mci_hs_tuning_find_condition(struct dw_mci *host, int timing) +{ + struct dw_mci_hs_priv_data *priv = host->priv; + const struct dw_mci_drv_data *drv_data = host->drv_data; + int id = priv->id; + int sample_min, sample_max; + int i, j; + int ret = 0; + int mask, mask_length; + int d_value = 0; + + if (host->hw_mmc_id == DWMMC_SD_ID) { + d_value = host->current_div - hs_timing_config[id][timing][1]; + if (timing == MMC_TIMING_SD_HS) { + sample_max = hs_timing_config[id][timing][4] + d_value; + sample_min = hs_timing_config[id][timing][5] + d_value; + } else if ((timing == MMC_TIMING_UHS_SDR50) || + (timing == MMC_TIMING_UHS_SDR104)) { + sample_max = + hs_timing_config[id][timing][4] + 2 * d_value; + sample_min = hs_timing_config[id][timing][5]; + } else { + sample_max = hs_timing_config[id][timing][4]; + sample_min = hs_timing_config[id][timing][5]; + } + } else { + sample_max = hs_timing_config[id][timing][4]; + sample_min = hs_timing_config[id][timing][5]; + } + + if (sample_max == sample_min) { + host->tuning_init_sample = (sample_max + sample_min) / 2; + dw_mci_hs_set_timing(host, id, + timing, host->tuning_init_sample, host->current_div); + dev_info(host->dev, + "no need tuning: timing is %d, tuning sample = %d", + timing, host->tuning_init_sample); + return 0; + } + + if (-1 == host->tuning_current_sample) { + + dw_mci_hs_tuning_clear_flags(host); + + /* set the first sam del as the min_sam_del */ + host->tuning_current_sample = sample_min; + /* a trick for next "++" */ + host->tuning_current_sample--; + } + + if (host->tuning_current_sample >= sample_max) { + /* tuning finish, select the best sam_del */ + + /* set sam del to -1, for next tuning */ + host->tuning_current_sample = -1; + + host->tuning_init_sample = -1; + for (mask_length = + (((sample_max - sample_min) >> 1) << 1) + 1; + mask_length >= 1; mask_length -= 2) { + + mask = (1 << mask_length) - 1; + for (i = (sample_min + + sample_max - mask_length + 1) / 2, j = 1; + (i <= sample_max - mask_length + 1) && + (i >= sample_min); + i = ((sample_min + + sample_max - mask_length + 1) / 2) + + ((j % 2) ? -1 : 1) * (j / 2)) { + if ((host->tuning_sample_flag & + ((unsigned int)mask << (unsigned int)i) + ) == ( + (unsigned int)mask << (unsigned int)i) + ) { + host->tuning_init_sample = + i + mask_length / 2; + break; + } + + j++; + } + + if (host->tuning_init_sample != -1) { + if ((host->hw_mmc_id == DWMMC_SD_ID) + && (mask_length < 3) && + (drv_data->slowdown_clk)) { + dev_info(host->dev, + "sd card tuning need slow\n"); + dev_info(host->dev, + "down clk, timing is %d,\n", + timing); + dev_info(host->dev, + "tuning_flag = 0x%x\n", + host->tuning_sample_flag); + return -1; + } + dev_info(host->dev, + "tuning OK: timing is %d,\n", + timing); + dev_info(host->dev, "tuning sample = %d,\n", + host->tuning_init_sample); + dev_info(host->dev, "tuning_flag = 0x%x\n", + host->tuning_sample_flag); + ret = 0; + break; + } + } + + if (-1 == host->tuning_init_sample) { + host->tuning_init_sample = + (sample_min + sample_max) / 2; + dev_info(host->dev, + "tuning err: no good sam_del,\n"); + dev_info(host->dev, + " timing is %d, tuning_flag = 0x%x\n", + timing, host->tuning_sample_flag); + ret = -1; + } + + dw_mci_hs_set_timing(host, id, timing, + host->tuning_init_sample, host->current_div); + return ret; + } + host->tuning_current_sample++; + dw_mci_hs_set_timing(host, id, timing, + host->tuning_current_sample, host->current_div); + return 1; + + +} + +static void dw_mci_hs_tuning_set_current_state(struct dw_mci *host, int ok) +{ + dw_mci_hs_tuning_set_flags(host, host->tuning_current_sample, ok); +} + +#ifdef CONFIG_MMC_DW_SD_CLK_SLOWDOWN +static int dw_mci_hs_slowdown_clk(struct dw_mci *host, int timing) +{ + struct dw_mci_hs_priv_data *priv = host->priv; + int id = priv->id; + + host->current_div += 2; + + /* slow down up to half of original freq */ + if (host->current_div > 2 * hs_timing_config[id][timing][1]) { + host->current_div = 2 * hs_timing_config[id][timing][1]; + return -1; + } + dev_info(host->dev, + "begin slowdown clk, current_div=%d\n", + host->current_div); + + dw_mci_hs_set_timing(host, id, timing, + host->tuning_init_sample, host->current_div); + + return 0; +} +#endif + +int dw_mci_sdio_wakelog_switch(struct mmc_host *mmc, bool enable) +{ + struct dw_mci_slot *slot = NULL; + + if (!mmc) + return -1; + + slot = mmc_priv(mmc); + if (!slot) + return -1; + + if (enable) + slot->sdio_wakelog_switch = 1; + else + slot->sdio_wakelog_switch = 0; + + slot->sdio_wakelog_switch = slot->sdio_wakelog_switch && + (MMC_CAP2_SUPPORT_WIFI & (mmc->caps2)); + return slot->sdio_wakelog_switch; +} +EXPORT_SYMBOL(dw_mci_sdio_wakelog_switch); + +static int dw_mci_hs_tuning_move(struct dw_mci *host, int timing, int start) +{ + struct dw_mci_hs_priv_data *priv = host->priv; + int id = priv->id; + int sample_min, sample_max; + int loop; + struct dw_mci_slot *slot = host->cur_slot; + + sample_max = hs_timing_config[id][timing][4]; + sample_min = hs_timing_config[id][timing][5]; + + if (sample_max == sample_min) { + dev_info(host->dev, "id = %d, tuning move return\n", id); + return 0; + } + + if (start) + host->tuning_move_count = 0; + + for (loop = 0; loop < 2; loop++) { + host->tuning_move_count++; + host->tuning_move_sample = + host->tuning_init_sample + + ((host->tuning_move_count % 2) ? 1 : -1) * + (host->tuning_move_count / 2); + + if ((host->tuning_move_sample > sample_max) || + (host->tuning_move_sample < sample_min)) { + continue; + } else { + break; + } + } + + if ((host->tuning_move_sample > sample_max) || + (host->tuning_move_sample < sample_min)) { + dw_mci_hs_set_timing(host, id, timing, + host->tuning_init_sample, host->current_div); + dev_info(host->dev, + "id = %d, tuning move end to init del_sel %d\n", + id, host->tuning_init_sample); + return 0; + } + dw_mci_hs_set_timing(host, id, timing, + host->tuning_move_sample, host->current_div); + + if (!(slot && slot->sdio_wakelog_switch)) + dev_info(host->dev, + "id = %d, tuning move to current del_sel %d\n", + id, host->tuning_move_sample); + return 1; + +} + +#define EMMC_PATTERN_ADDRESS (384*2) +int dw_mci_priv_execute_tuning(struct dw_mci_slot *slot, + u32 opcode, struct dw_mci_tuning_data *tuning_data) +{ + struct mmc_host *mmc = slot->mmc; + struct dw_mci *host = slot->host; + const struct dw_mci_drv_data *drv_data = host->drv_data; + unsigned int tuning_loop = MAX_TUNING_LOOP; + const u8 *tuning_blk_pattern; + int ret = 0; + u8 *tuning_blk; + int blksz; + + int id = host->hw_mmc_id; + u32 arg = 0; + unsigned int flags = MMC_RSP_R1 | MMC_CMD_ADTC; + + if (opcode == MMC_SEND_TUNING_BLOCK_HS200) { + if (mmc->ios.bus_width == MMC_BUS_WIDTH_8) { + tuning_blk_pattern = tuning_blk_pattern_8bit; + blksz = 128; + } else if (mmc->ios.bus_width == MMC_BUS_WIDTH_4) { + tuning_blk_pattern = tuning_blk_pattern_4bit; + blksz = 64; + } else + return -EINVAL; + } else if (opcode == MMC_SEND_TUNING_BLOCK) { + tuning_blk_pattern = tuning_blk_pattern_4bit; + blksz = 64; + } else if (opcode == MMC_READ_SINGLE_BLOCK) { + if (id == 0) /* emmc ddr50 */ + arg = EMMC_PATTERN_ADDRESS; + + blksz = 512; + } else if (opcode == SD_IO_RW_EXTENDED) { + arg = 0x200004; + flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; + + blksz = 4; + } else { + dev_err(&mmc->class_dev, + "Undefined command(%d) for tuning\n", opcode); + return -EINVAL; + } + + tuning_blk = kmalloc(blksz, GFP_KERNEL); + if (!tuning_blk) + return -ENOMEM; + + if ((!drv_data->tuning_find_condition) || + (!drv_data->tuning_set_current_state)) { + dev_err(&mmc->class_dev, "no tuning find condition method\n"); + goto out; + } + + pm_runtime_get_sync(mmc_dev(mmc)); + + host->flags |= DWMMC_IN_TUNING; + host->flags &= ~DWMMC_TUNING_DONE; + + do { + struct mmc_request mrq = { NULL }; + struct mmc_command cmd = { 0 }; + struct mmc_data data = { 0 }; + struct scatterlist sg; + + cmd.opcode = opcode; + cmd.arg = arg; + cmd.flags = flags; + + data.blksz = blksz; + data.blocks = 1; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; + + sg_init_one(&sg, tuning_blk, blksz); + dw_mci_set_timeout(host); + + mrq.cmd = &cmd; + mrq.stop = NULL; + mrq.data = &data; + + ret = drv_data->tuning_find_condition(host, mmc->ios.timing); + if (ret == -1) { + if ((host->hw_mmc_id == DWMMC_SD_ID) && + (drv_data->slowdown_clk)) { + ret = drv_data->slowdown_clk(host, + mmc->ios.timing); + if (ret) + break; + } else { + break; + } + } else if (ret == 0) + break; + + mmc_wait_for_req(mmc, &mrq); + + if (!cmd.error && !data.error) { + drv_data->tuning_set_current_state(host, 1); + } else { + drv_data->tuning_set_current_state(host, 0); + dev_dbg(&mmc->class_dev, + "Tuning error: cmd.error:%d, data.error:%d\n", + cmd.error, data.error); + } + + } while (tuning_loop--); + + host->flags &= ~DWMMC_IN_TUNING; + if (!ret) + host->flags |= DWMMC_TUNING_DONE; + + host->tuning_move_start = 1; +out: + kfree(tuning_blk); + + pm_runtime_mark_last_busy(mmc_dev(mmc)); + pm_runtime_put_autosuspend(mmc_dev(mmc)); + + return ret; +} + +/* Common capabilities of hi3650 SoC */ +static unsigned long hs_dwmmc_caps[3] = { +#ifdef CONFIG_MMC_DW_EMMC_USED_AS_MODEM + /* sdio1 - via modem */ + MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ, +#else + MMC_CAP_CMD23, +#endif + /* sd */ + MMC_CAP_DRIVER_TYPE_A | MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED + | MMC_CAP_MMC_HIGHSPEED, + /* sdio */ + MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED | MMC_CAP_NONREMOVABLE, +}; + +static const struct dw_mci_drv_data hs_drv_data = { + .caps = hs_dwmmc_caps, + .init = dw_mci_hs_priv_init, + .set_ios = dw_mci_hs_set_ios, + .setup_clock = dw_mci_hs_setup_clock, + .prepare_command = dw_mci_hs_prepare_command, + .parse_dt = dw_mci_hs_parse_dt, + .cd_detect_init = dw_mci_hs_cd_detect_init, + .tuning_find_condition = dw_mci_hs_tuning_find_condition, + .tuning_set_current_state = dw_mci_hs_tuning_set_current_state, + .tuning_move = dw_mci_hs_tuning_move, +#ifdef CONFIG_MMC_DW_SD_CLK_SLOWDOWN + .slowdown_clk = dw_mci_hs_slowdown_clk, +#endif + .execute_tuning_hisi = dw_mci_priv_execute_tuning, + .start_signal_voltage_switch = dw_mci_priv_voltage_switch, + .work_fail_reset = dw_mci_hi3xxx_work_fail_reset, +}; + +static const struct of_device_id dw_mci_hs_match[] = { + { + .compatible = "hisilicon,davinci-dw-mshc", + .data = &hs_drv_data, + }, + {}, +}; + +MODULE_DEVICE_TABLE(of, dw_mci_hs_match); + +int dw_mci_hs_probe(struct platform_device *pdev) +{ + const struct dw_mci_drv_data *drv_data = NULL; + const struct of_device_id *match = NULL; + int err; + + match = of_match_node(dw_mci_hs_match, pdev->dev.of_node); + if (!match) + return -1; + drv_data = match->data; + + err = dw_mci_hs_get_resource(); + if (err) + return err; + + err = dw_mci_pltfm_register(pdev, drv_data); + if (err) + return err; + + /* when sdio1 used for via modem, disable pm runtime */ + if (!of_property_read_bool(pdev->dev.of_node, "modem_sdio_enable")) { + + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + pm_runtime_set_autosuspend_delay(&pdev->dev, 50); + pm_runtime_use_autosuspend(&pdev->dev); + pm_suspend_ignore_children(&pdev->dev, 1); + } else { + pr_info("%s mmc/sdio device support via modem,\n", __func__); + pr_info(" disable pm_runtime on this device\n"); + } + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int dw_mci_hs_suspend(struct device *dev) +{ + int ret; + struct dw_mci *host = dev_get_drvdata(dev); + struct dw_mci_hs_priv_data *priv = host->priv; + + dev_info(host->dev, " %s ++\n", __func__); + pm_runtime_get_sync(dev); + + if (priv->gpio_cd) { + disable_irq(gpio_to_irq(priv->gpio_cd)); + cancel_work_sync(&host->card_work); + dev_info(host->dev, " disable gpio detect\n"); + } + + ret = dw_mci_suspend(host); + if (ret) + return ret; + + priv->old_timing = -1; + priv->old_power_mode = MMC_POWER_OFF; + if (!IS_ERR(host->biu_clk)) + clk_disable_unprepare(host->biu_clk); + + if (!IS_ERR(host->ciu_clk)) + clk_disable_unprepare(host->ciu_clk); + + dw_mci_hs_set_controller(host, 1); + + host->current_speed = 0; + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + dev_info(host->dev, " %s --\n", __func__); + return 0; +} + +static int dw_mci_hs_resume(struct device *dev) +{ + int ret, i; + struct dw_mci *host = dev_get_drvdata(dev); + struct dw_mci_hs_priv_data *priv = host->priv; + + pm_runtime_get_sync(dev); + + if (!IS_ERR(host->biu_clk)) { + if (clk_prepare_enable(host->biu_clk)) + dev_err(host->dev, "biu_clk clk_prepare_enable failed\n"); + } + + if (!IS_ERR(host->ciu_clk)) { + if (clk_prepare_enable(host->ciu_clk)) + dev_err(host->dev, "ciu_clk clk_prepare_enable failed\n"); + } + + dw_mci_hs_set_controller(host, 0); + + for (i = 0; i < host->num_slots; i++) { + struct dw_mci_slot *slot = host->slot; + + if (!slot) + continue; + + if (slot->mmc->pm_flags & MMC_PM_KEEP_POWER) { + priv->in_resume = STATE_KEEP_PWR; + } else { + host->flags &= ~DWMMC_IN_TUNING; + host->flags &= ~DWMMC_TUNING_DONE; + } + } + + /* restore controller specified setting */ + dw_mci_hs_priv_setting(host); + ret = dw_mci_resume(host); + if (ret) + return ret; + + priv->in_resume = STATE_LEGACY; + + if (priv->gpio_cd) + enable_irq(gpio_to_irq(priv->gpio_cd)); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return 0; +} +#endif + +#ifdef CONFIG_PM +static int dw_mci_hs_runtime_suspend(struct device *dev) +{ + struct dw_mci *host = dev_get_drvdata(dev); + + dev_vdbg(host->dev, " %s ++\n", __func__); + if (hs_dwmmc_card_busy(host)) { + dev_warn(host->dev, " %s: card is busy\n", __func__); + return -EBUSY; + } + + if (!IS_ERR(host->biu_clk)) + clk_disable_unprepare(host->biu_clk); + + if (!IS_ERR(host->ciu_clk)) + clk_disable_unprepare(host->ciu_clk); + dev_vdbg(host->dev, " %s --\n", __func__); + + return 0; +} + +static int dw_mci_hs_runtime_resume(struct device *dev) +{ + struct dw_mci *host = dev_get_drvdata(dev); + + dev_vdbg(host->dev, " %s ++\n", __func__); + if (!IS_ERR(host->biu_clk)) { + if (clk_prepare_enable(host->biu_clk)) + dev_err(host->dev, "biu_clk clk_prepare_enable failed\n"); + } + + if (!IS_ERR(host->ciu_clk)) { + if (clk_prepare_enable(host->ciu_clk)) + dev_err(host->dev, "ciu_clk clk_prepare_enable failed\n"); + } + + dev_vdbg(host->dev, " %s --\n", __func__); + return 0; +} +#endif + +void dw_mci_reg_dump(struct dw_mci *host) +{ + u32 status, mintsts; + + dev_info(host->dev, ": ============== REGISTER DUMP ==============\n"); + dev_info(host->dev, ": CTRL: 0x%08x\n", mci_readl(host, CTRL)); + dev_info(host->dev, ": PWREN: 0x%08x\n", mci_readl(host, PWREN)); + dev_info(host->dev, ": CLKDIV: 0x%08x\n", mci_readl(host, CLKDIV)); + dev_info(host->dev, ": CLKSRC: 0x%08x\n", mci_readl(host, CLKSRC)); + dev_info(host->dev, ": CLKENA: 0x%08x\n", mci_readl(host, CLKENA)); + dev_info(host->dev, ": TMOUT: 0x%08x\n", mci_readl(host, TMOUT)); + dev_info(host->dev, ": CTYPE: 0x%08x\n", mci_readl(host, CTYPE)); + dev_info(host->dev, ": BLKSIZ: 0x%08x\n", mci_readl(host, BLKSIZ)); + dev_info(host->dev, ": BYTCNT: 0x%08x\n", mci_readl(host, BYTCNT)); + dev_info(host->dev, ": INTMSK: 0x%08x\n", mci_readl(host, INTMASK)); + dev_info(host->dev, ": CMDARG: 0x%08x\n", mci_readl(host, CMDARG)); + dev_info(host->dev, ": CMD: 0x%08x\n", mci_readl(host, CMD)); + dev_info(host->dev, ": MINTSTS: 0x%08x\n", mci_readl(host, MINTSTS)); + dev_info(host->dev, ": RINTSTS: 0x%08x\n", mci_readl(host, RINTSTS)); + dev_info(host->dev, ": STATUS: 0x%08x\n", mci_readl(host, STATUS)); + dev_info(host->dev, ": FIFOTH: 0x%08x\n", mci_readl(host, FIFOTH)); + dev_info(host->dev, ": CDETECT: 0x%08x\n", mci_readl(host, CDETECT)); + dev_info(host->dev, ": WRTPRT: 0x%08x\n", mci_readl(host, WRTPRT)); + dev_info(host->dev, ": GPIO: 0x%08x\n", mci_readl(host, GPIO)); + dev_info(host->dev, ": TCBCNT: 0x%08x\n", mci_readl(host, TCBCNT)); + dev_info(host->dev, ": TBBCNT: 0x%08x\n", mci_readl(host, TBBCNT)); + dev_info(host->dev, ": DEBNCE: 0x%08x\n", mci_readl(host, DEBNCE)); + dev_info(host->dev, ": USRID: 0x%08x\n", mci_readl(host, USRID)); + dev_info(host->dev, ": VERID: 0x%08x\n", mci_readl(host, VERID)); + dev_info(host->dev, ": HCON: 0x%08x\n", mci_readl(host, HCON)); + dev_info(host->dev, ": UHS_REG: 0x%08x\n", mci_readl(host, UHS_REG)); + dev_info(host->dev, ": BMOD: 0x%08x\n", mci_readl(host, BMOD)); + dev_info(host->dev, ": PLDMND: 0x%08x\n", mci_readl(host, PLDMND)); + if (host->dma_64bit_address == SDMMC_32_BIT_DMA) { + dev_info(host->dev, ": DBADDR: 0x%08x\n", + mci_readl(host, DBADDR)); + dev_info(host->dev, ": IDSTS: 0x%08x\n", + mci_readl(host, IDSTS)); + dev_info(host->dev, ": IDINTEN: 0x%08x\n", + mci_readl(host, IDINTEN)); + dev_info(host->dev, ": DSCADDR: 0x%08x\n", + mci_readl(host, DSCADDR)); + dev_info(host->dev, ": BUFADDR: 0x%08x\n", + mci_readl(host, BUFADDR)); + } else { + dev_info(host->dev, ": DBADDRL: 0x%08x\n", + mci_readl(host, DBADDRL)); + dev_info(host->dev, ": DBADDRU: 0x%08x\n", + mci_readl(host, DBADDRU)); + dev_info(host->dev, ": IDSTS: 0x%08x\n", + mci_readl(host, IDSTS64)); + dev_info(host->dev, ": IDINTEN: 0x%08x\n", + mci_readl(host, IDINTEN64)); + } + dev_info(host->dev, ": CDTHRCTL: 0x%08x\n", + mci_readl(host, CDTHRCTL)); + dev_info(host->dev, ": UHS_REG_EXT: 0x%08x\n", + mci_readl(host, UHS_REG_EXT)); + dev_info(host->dev, ": ============== STATUS DUMP ================\n"); + dev_info(host->dev, ": cmd_status: 0x%08x\n", host->cmd_status); + dev_info(host->dev, ": data_status: 0x%08x\n", host->data_status); + dev_info(host->dev, ": pending_events: 0x%08lx\n", + host->pending_events); + dev_info(host->dev, ": completed_events:0x%08lx\n", + host->completed_events); + dev_info(host->dev, ": state: %d\n", host->state); + dev_info(host->dev, ": ===========================================\n"); + + /* summary */ + mintsts = mci_readl(host, MINTSTS); + status = mci_readl(host, STATUS); + dev_info(host->dev, "CMD%d, ARG=0x%08x, intsts : %s, status : %s.\n", + mci_readl(host, CMD) & 0x3F, + mci_readl(host, CMDARG), + mintsts & 0x8 ? "Data transfer done" : + mintsts & 0x4 ? "Command Done" : "refer to dump", + status & (0x1 << 9) ? "dat0 busy" : "refer to dump"); + dev_info(host->dev, ": RESP0: 0x%08x\n", mci_readl(host, RESP0)); + dev_info(host->dev, ": RESP1: 0x%08x\n", mci_readl(host, RESP1)); + dev_info(host->dev, ": RESP2: 0x%08x\n", mci_readl(host, RESP2)); + dev_info(host->dev, ": RESP3: 0x%08x\n", mci_readl(host, RESP3)); + dev_info(host->dev, ": host : cmd_status=0x%08x, data_status=0x%08x.\n", + host->cmd_status, host->data_status); + dev_info(host->dev, + ": host : pending_events=0x%08lx,\n", host->pending_events); + dev_info(host->dev, + "completed_events=0x%08lx.\n", host->completed_events); + dev_info(host->dev, ": ===========================================\n"); +} +EXPORT_SYMBOL(dw_mci_reg_dump); + +#ifdef CONFIG_MMC_HISI_TRACE +void dw_mci_reg_dump_fortrace(struct mmc_host *mmc) +{ + u32 status, mintsts; + struct dw_mci_slot *slot = mmc_priv(mmc); + struct dw_mci *host = slot->host; + + pm_runtime_get_sync(mmc_dev(mmc)); + + mmc_trace_comm_record(mmc, + ": =========== REGISTER DUMP (%s)===========\n", + mmc_hostname(mmc)); + + mmc_trace_comm_record(mmc, + ": ============== REGISTER DUMP ==============\n"); + mmc_trace_comm_record(mmc, ": CTRL: 0x%x\n", + mci_readl(host, CTRL)); + mmc_trace_comm_record(mmc, ": PWREN: 0x%x\n", + mci_readl(host, PWREN)); + mmc_trace_comm_record(mmc, ": CLKDIV: 0x%x\n", + mci_readl(host, CLKDIV)); + mmc_trace_comm_record(mmc, ": CLKSRC: 0x%x\n", + mci_readl(host, CLKSRC)); + mmc_trace_comm_record(mmc, ": CLKENA: 0x%x\n", + mci_readl(host, CLKENA)); + mmc_trace_comm_record(mmc, ": TMOUT: 0x%x\n", + mci_readl(host, TMOUT)); + mmc_trace_comm_record(mmc, ": CTYPE: 0x%x\n", + mci_readl(host, CTYPE)); + mmc_trace_comm_record(mmc, ": BLKSIZ: 0x%x\n", + mci_readl(host, BLKSIZ)); + mmc_trace_comm_record(mmc, ": BYTCNT: 0x%x\n", + mci_readl(host, BYTCNT)); + mmc_trace_comm_record(mmc, ": INTMSK: 0x%x\n", + mci_readl(host, INTMASK)); + mmc_trace_comm_record(mmc, ": CMDARG: 0x%x\n", + mci_readl(host, CMDARG)); + mmc_trace_comm_record(mmc, ": CMD: 0x%x\n", + mci_readl(host, CMD)); + mmc_trace_comm_record(mmc, ": MINTSTS: 0x%x\n", + mci_readl(host, MINTSTS)); + mmc_trace_comm_record(mmc, ": RINTSTS: 0x%x\n", + mci_readl(host, RINTSTS)); + mmc_trace_comm_record(mmc, ": STATUS: 0x%x\n", + mci_readl(host, STATUS)); + mmc_trace_comm_record(mmc, ": FIFOTH: 0x%x\n", + mci_readl(host, FIFOTH)); + mmc_trace_comm_record(mmc, ": CDETECT: 0x%x\n", + mci_readl(host, CDETECT)); + mmc_trace_comm_record(mmc, ": WRTPRT: 0x%x\n", + mci_readl(host, WRTPRT)); + mmc_trace_comm_record(mmc, ": GPIO: 0x%x\n", + mci_readl(host, GPIO)); + mmc_trace_comm_record(mmc, ": TCBCNT: 0x%x\n", + mci_readl(host, TCBCNT)); + mmc_trace_comm_record(mmc, ": TBBCNT: 0x%x\n", + mci_readl(host, TBBCNT)); + mmc_trace_comm_record(mmc, ": DEBNCE: 0x%x\n", + mci_readl(host, DEBNCE)); + mmc_trace_comm_record(mmc, ": USRID: 0x%x\n", + mci_readl(host, USRID)); + mmc_trace_comm_record(mmc, ": VERID: 0x%x\n", + mci_readl(host, VERID)); + mmc_trace_comm_record(mmc, ": HCON: 0x%x\n", + mci_readl(host, HCON)); + mmc_trace_comm_record(mmc, ": UHS_REG: 0x%x\n", + mci_readl(host, UHS_REG)); + mmc_trace_comm_record(mmc, ": BMOD: 0x%08x\n", + mci_readl(host, BMOD)); + mmc_trace_comm_record(mmc, ": PLDMND: 0x%x\n", + mci_readl(host, PLDMND)); + mmc_trace_comm_record(mmc, ": DBADDR: 0x%x\n", + mci_readl(host, DBADDR)); + mmc_trace_comm_record(mmc, ": IDSTS: 0x%x\n", + mci_readl(host, IDSTS)); + mmc_trace_comm_record(mmc, ": IDINTEN: 0x%x\n", + mci_readl(host, IDINTEN)); + mmc_trace_comm_record(mmc, ": DSCADDR: 0x%x\n", + mci_readl(host, DSCADDR)); + mmc_trace_comm_record(mmc, ": BUFADDR: 0x%x\n", + mci_readl(host, BUFADDR)); + mmc_trace_comm_record(mmc, ": CDTHRCTL: 0x%x\n", + mci_readl(host, CDTHRCTL)); + mmc_trace_comm_record(mmc, ": UHS_REG_EXT: 0x%x\n", + mci_readl(host, UHS_REG_EXT)); + mmc_trace_comm_record(mmc, + ": ============== STATUS DUMP ================\n"); + mmc_trace_comm_record(mmc, ": cmd_status: 0x%x\n", + host->cmd_status); + mmc_trace_comm_record(mmc, ": data_status: 0x%x\n", + host->data_status); + mmc_trace_comm_record(mmc, ": pending_events: 0x%x\n", + host->pending_events); + mmc_trace_comm_record(mmc, ": completed_events:0x%x\n", + host->completed_events); + mmc_trace_comm_record(mmc, ": state: %d\n", + host->state); + mmc_trace_comm_record(mmc, + ": ===========================================\n"); + + /* summary */ + mintsts = mci_readl(host, MINTSTS); + status = mci_readl(host, STATUS); + mmc_trace_comm_record(mmc, + "CMD%d, ARG=0x%x, intsts : %s, status : %s.\n", + mci_readl(host, CMD) & 0x3F, + mci_readl(host, CMDARG), + mintsts & 0x8 ? "Data transfer done" : + mintsts & 0x4 ? "Command Done" : "refer to dump", + status & (0x1 << 9) ? "dat0 busy" : "refer to dump"); + mmc_trace_comm_record(mmc, + ": RESP0: 0x%x\n", mci_readl(host, RESP0)); + mmc_trace_comm_record(mmc, + ": RESP1: 0x%x\n", mci_readl(host, RESP1)); + mmc_trace_comm_record(mmc, + ": RESP2: 0x%x\n", mci_readl(host, RESP2)); + mmc_trace_comm_record(mmc, + ": RESP3: 0x%x\n", mci_readl(host, RESP3)); + mmc_trace_comm_record(mmc, + ": host : cmd_status=0x%x, data_status=0x%x.\n", + host->cmd_status, host->data_status); + mmc_trace_comm_record(mmc, + ": host : pending_events=0x%x,completed_events=0x%x.\n", + host->pending_events, host->completed_events); + + pm_runtime_mark_last_busy(mmc_dev(mmc)); + pm_runtime_put_autosuspend(mmc_dev(mmc)); + + mmc_trace_comm_record(mmc, + ": ===========================================\n"); +} +#endif + +bool dw_mci_stop_abort_cmd(struct mmc_command *cmd) +{ + u32 op = cmd->opcode; + + if ((op == MMC_STOP_TRANSMISSION) || + (op == MMC_GO_IDLE_STATE) || + (op == MMC_GO_INACTIVE_STATE) || + ((op == SD_IO_RW_DIRECT) && (cmd->arg & 0x80000000) && + ((cmd->arg >> 9) & 0x1FFFF) == SDIO_CCCR_ABORT)) + return true; + return false; +} + +bool dw_mci_wait_reset(struct device *dev, struct dw_mci *host, + unsigned int reset_val) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(50); + unsigned int ctrl; + + ctrl = mci_readl(host, CTRL); + ctrl |= reset_val; + mci_writel(host, CTRL, ctrl); + + /* wait till resets clear */ + do { + if (!(mci_readl(host, CTRL) & reset_val)) + return true; + } while (time_before(jiffies, timeout)); + + dev_warn(dev, "Timeout resetting block (ctrl %#x)\n", ctrl); + + return false; +} +EXPORT_SYMBOL(dw_mci_wait_reset); + +static int mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) +{ + struct dw_mci *host = slot->host; + unsigned long timeout = jiffies + msecs_to_jiffies(100); + unsigned int cmd_status = 0; + + mci_writel(host, CMDARG, arg); + /* synchronous execution */ + wmb(); + mci_writel(host, CMD, SDMMC_CMD_START | cmd); + while (time_before(jiffies, timeout)) { + cmd_status = mci_readl(host, CMD); + if (!(cmd_status & SDMMC_CMD_START)) + return 0; + } + + if (!dw_mci_wait_reset(host->dev, host, SDMMC_CTRL_RESET)) + return 1; + + timeout = jiffies + msecs_to_jiffies(100); + mci_writel(host, CMD, SDMMC_CMD_START | cmd); + while (time_before(jiffies, timeout)) { + cmd_status = mci_readl(host, CMD); + if (!(cmd_status & SDMMC_CMD_START)) + return 0; + } + + dev_info(&slot->mmc->class_dev, + "Timeout sending command (cmd %#x arg %#x status %#x)\n", + cmd, arg, cmd_status); + return 1; +} + +void dw_mci_ciu_reset(struct device *dev, struct dw_mci *host) +{ + struct dw_mci_slot *slot = host->cur_slot; + + if (slot) { + if (!dw_mci_wait_reset(dev, host, SDMMC_CTRL_RESET)) + dev_info(dev, "dw_mci_wait_reset failed\n"); + + mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | + SDMMC_CMD_PRV_DAT_WAIT, 0); + } +} +EXPORT_SYMBOL(dw_mci_ciu_reset); + +bool dw_mci_fifo_reset(struct device *dev, struct dw_mci *host) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(1000); + unsigned int ctrl; + bool result; + + do { + result = dw_mci_wait_reset(host->dev, + host, SDMMC_CTRL_FIFO_RESET); + + if (!result) + break; + + ctrl = mci_readl(host, STATUS); + if (!(ctrl & SDMMC_STATUS_DMA_REQ)) { + result = dw_mci_wait_reset(host->dev, host, + SDMMC_CTRL_FIFO_RESET); + if (result) { + /* clear exception raw interrupts */ + /* can not be handled */ + /* ex fifo full => RXDR interrupt rising */ + ctrl = mci_readl(host, RINTSTS); + ctrl = ctrl & ~(mci_readl(host, MINTSTS)); + if (ctrl) + mci_writel(host, RINTSTS, ctrl); + + return true; + } + } + } while (time_before(jiffies, timeout)); + + dev_warn(dev, "%s: Timeout while resetting host controller after err\n", + __func__); + + return false; +} +EXPORT_SYMBOL(dw_mci_fifo_reset); + +u32 dw_mci_prep_stop(struct dw_mci *host, struct mmc_command *cmd) +{ + struct mmc_command *stop = &host->stop; + const struct dw_mci_drv_data *drv_data = host->drv_data; + u32 cmdr = cmd->opcode; + + memset(stop, 0, sizeof(struct mmc_command)); + + if (cmdr == SD_IO_RW_EXTENDED) { + stop->opcode = SD_IO_RW_DIRECT; + stop->arg = 0x80000000; + stop->arg |= (cmd->arg >> 28) & 0x7; + stop->arg |= SDIO_CCCR_ABORT << 9; + stop->flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_AC; + } else { + stop->opcode = MMC_STOP_TRANSMISSION; + stop->arg = 0; + stop->flags = MMC_RSP_R1B | MMC_CMD_AC; + } + + cmdr = stop->opcode | SDMMC_CMD_STOP | + SDMMC_CMD_RESP_CRC | SDMMC_CMD_RESP_EXP; + + /* Use hold bit register */ + if (drv_data && drv_data->prepare_command) + drv_data->prepare_command(host, &cmdr); + + return cmdr; +} +EXPORT_SYMBOL(dw_mci_prep_stop); + +bool dw_mci_wait_data_busy(struct dw_mci *host, struct mmc_request *mrq) +{ + u32 status; + unsigned long timeout = jiffies + msecs_to_jiffies(500); + + do { + status = mci_readl(host, STATUS); + if (!(status & SDMMC_STATUS_BUSY)) + return true; + + usleep_range(10, 20); + } while (time_before(jiffies, timeout)); + + /* card is checked every 1s by CMD13 at least */ + if (mrq->cmd->opcode == MMC_SEND_STATUS) + return true; + + dev_info(host->dev, "status is busy, reset ctrl\n"); + + if (!dw_mci_wait_reset(host->dev, host, SDMMC_CTRL_RESET)) + return false; + + /* After CTRL Reset, Should be needed clk val to CIU */ + if (host->cur_slot) + mci_send_cmd(host->cur_slot, + SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); + + timeout = jiffies + msecs_to_jiffies(500); + do { + status = mci_readl(host, STATUS); + if (!(status & SDMMC_STATUS_BUSY)) + return true; + + usleep_range(10, 20); + } while (time_before(jiffies, timeout)); + + + dev_warn(host->dev, "Data[0]: data is busy\n"); + + return false; +} + +void dw_mci_set_cd(struct dw_mci *host) +{ + + if (host == NULL) + return; + if (host->slot && host->slot->mmc) { + dev_dbg(&host->slot->mmc->class_dev, "sdio_present = %d\n", + host->slot->mmc->sdio_present); + host->slot->mmc->sdio_present = 1; + } +} + +int dw_mci_start_signal_voltage_switch(struct mmc_host *mmc, + struct mmc_ios *ios) +{ + struct dw_mci_slot *slot = mmc_priv(mmc); + struct dw_mci *host = slot->host; + const struct dw_mci_drv_data *drv_data = host->drv_data; + int err = -ENOSYS; + + if (drv_data && drv_data->start_signal_voltage_switch) + err = drv_data->start_signal_voltage_switch(mmc, ios); + + return err; +} +EXPORT_SYMBOL(dw_mci_start_signal_voltage_switch); + +void dw_mci_slowdown_clk(struct mmc_host *mmc, int timing) +{ + struct dw_mci_slot *slot = mmc_priv(mmc); + struct dw_mci *host = slot->host; + const struct dw_mci_drv_data *drv_data = host->drv_data; + + if (host->flags & DWMMC_TUNING_DONE) + host->flags &= ~DWMMC_TUNING_DONE; + + if (drv_data->slowdown_clk) { + if (host->sd_reinit) + return; + + host->sd_reinit = 1; + pm_runtime_get_sync(mmc_dev(mmc)); + drv_data->slowdown_clk(host, timing); + pm_runtime_mark_last_busy(mmc_dev(mmc)); + pm_runtime_put_autosuspend(mmc_dev(mmc)); + + } + +} +EXPORT_SYMBOL(dw_mci_slowdown_clk); + +void dw_mci_timeout_timer(struct timer_list *t) +{ + struct dw_mci *host = from_timer(host, t, timer); + struct mmc_request *mrq; + + + if (host) { + spin_lock(&host->lock); + if (host->mrq) { + mrq = host->mrq; + dev_vdbg(host->dev, "time out host->mrq = %pK\n", + host->mrq); + + dev_warn(host->dev, + "Timeout waiting for hardware interrupt."); + dev_warn(host->dev, " state = %d\n", host->state); + dw_mci_reg_dump(host); + + host->sg = NULL; + host->data = NULL; + host->cmd = NULL; + + switch (host->state) { + case STATE_IDLE: + break; + case STATE_SENDING_CMD: + mrq->cmd->error = -ENOMEDIUM; + if (!mrq->data) + break; + /* fall through */ + case STATE_SENDING_DATA: + mrq->data->error = -ENOMEDIUM; + dw_mci_stop_dma(host); + break; + case STATE_DATA_BUSY: + case STATE_DATA_ERROR: + if (mrq->data->error == -EINPROGRESS) + mrq->data->error = -ENOMEDIUM; + /* fall through */ + case STATE_SENDING_STOP: + if (mrq->stop) + mrq->stop->error = -ENOMEDIUM; + break; + } + + host->sd_hw_timeout = 1; + + dw_mci_fifo_reset(host->dev, host); + dw_mci_ciu_reset(host->dev, host); + + dw_mci_request_end(host, mrq); + } + spin_unlock(&host->lock); + } +} +EXPORT_SYMBOL(dw_mci_timeout_timer); + +void dw_mci_work_routine_queue_clean(struct dw_mci *host) +{ + struct dw_mci_slot *slot = host->slot; + struct mmc_request *mrq; + + mrq = slot->mrq; + if (mrq) { + if (mrq == host->mrq) { + host->data = NULL; + host->cmd = NULL; + + switch (host->state) { + case STATE_IDLE: + break; + case STATE_SENDING_CMD: + mrq->cmd->error = -ENOMEDIUM; + if (!mrq->data) + break; + /* fall through */ + case STATE_SENDING_DATA: + mrq->data->error = -ENOMEDIUM; + dw_mci_stop_dma(host); + break; + case STATE_DATA_BUSY: + case STATE_DATA_ERROR: + if (mrq->data->error == -EINPROGRESS) + mrq->data->error = -ENOMEDIUM; + if (!mrq->stop) + break; + /* fall through */ + case STATE_SENDING_STOP: + if (mrq->stop) + mrq->stop->error = -ENOMEDIUM; + break; + } + + dw_mci_request_end(host, mrq); + } else { + list_del(&slot->queue_node); + mrq->cmd->error = -ENOMEDIUM; + if (mrq->data) + mrq->data->error = -ENOMEDIUM; + if (mrq->stop) + mrq->stop->error = -ENOMEDIUM; + + if (del_timer(&host->timer) != 0) + dev_info(host->dev, "del_timer failed\n"); + spin_unlock(&host->lock); + mmc_request_done(slot->mmc, mrq); + spin_lock(&host->lock); + } + } + +} + +void dw_mci_work_routine_card(struct work_struct *work) +{ + struct dw_mci *host = container_of(work, struct dw_mci, card_work); + int i; + + for (i = 0; i < host->num_slots; i++) { + struct dw_mci_slot *slot = host->slot; + struct mmc_host *mmc = slot->mmc; + int present; + + present = hisi_dw_mci_get_cd(mmc); + while (present != slot->last_detect_state) { + dev_dbg(&slot->mmc->class_dev, "card %s\n", + present ? "inserted" : "removed"); + + spin_lock_bh(&host->lock); + + /* Card change detected */ + slot->last_detect_state = present; + + /* Mark card as present if applicable */ + if (present != 0) + set_bit(DW_MMC_CARD_PRESENT, &slot->flags); + + /* Clean up queue if present */ + dw_mci_work_routine_queue_clean(host); + /* Power down slot */ + if (present == 0) { + clear_bit(DW_MMC_CARD_PRESENT, &slot->flags); + + /* + * Clear down the FIFO - doing so generates a + * block interrupt, hence setting the + * scatter-gather pointer to NULL. + */ + sg_miter_stop(&host->sg_miter); + host->sg = NULL; + + dw_mci_fifo_reset(host->dev, host); + dw_mci_ciu_reset(host->dev, host); +#ifdef CONFIG_MMC_DW_IDMAC + dw_mci_idmac_reset(host); +#endif + } + + spin_unlock_bh(&host->lock); + + present = hisi_dw_mci_get_cd(mmc); + } + + mmc_detect_change(slot->mmc, + msecs_to_jiffies(host->pdata->detect_delay_ms)); + } +} +EXPORT_SYMBOL(dw_mci_work_routine_card); + +bool mci_wait_reset(struct device *dev, struct dw_mci *host) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(50); + unsigned int ctrl; + + mci_writel(host, CTRL, (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | + SDMMC_CTRL_DMA_RESET)); + + /* wait till resets clear */ + do { + ctrl = mci_readl(host, CTRL); + if (!(ctrl & (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | + SDMMC_CTRL_DMA_RESET))) + return true; + } while (time_before(jiffies, timeout)); + + dev_warn(dev, "Timeout resetting block (ctrl %#x)\n", ctrl); + + return false; +} +EXPORT_SYMBOL(mci_wait_reset); + +static const struct dev_pm_ops dw_mci_hs_pmops = { + SET_SYSTEM_SLEEP_PM_OPS(dw_mci_hs_suspend, dw_mci_hs_resume) + SET_RUNTIME_PM_OPS(dw_mci_hs_runtime_suspend, + dw_mci_hs_runtime_resume, NULL) +}; + +static struct platform_driver dw_mci_hs_pltfm_driver = { + .probe = dw_mci_hs_probe, + .remove = dw_mci_pltfm_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = of_match_ptr(dw_mci_hs_match), + .pm = &dw_mci_hs_pmops, + }, +}; + +module_platform_driver(dw_mci_hs_pltfm_driver); + +MODULE_DESCRIPTION("Hisilicon Specific DW-MSHC Driver Extension"); +MODULE_LICENSE("GPL v2"); +#pragma GCC diagnostic pop
From: zhangguijiang zhangguijiang@huawei.com
ascend inclusion category: feature feature: Ascend emmc adaption bugzilla: https://gitee.com/openeuler/kernel/issues/I4F4LL CVE: NA
--------------------
This file documents differences between the core Synopsys dw mshc hi3xxx controller properties described by hi3xxx-dw-mshc.txt and the properties used by the Hisilicon hi3xxx specific extensions to the Synopsys Designware Storage Host Controller.
Signed-off-by: zhangguijiang zhangguijiang@huawei.com Reviewed-by: Ding Tianhong dingtianhong@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- .../bindings/mmc/hi3xxx-dw-mshc.txt | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 Documentation/devicetree/bindings/mmc/hi3xxx-dw-mshc.txt
diff --git a/Documentation/devicetree/bindings/mmc/hi3xxx-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/hi3xxx-dw-mshc.txt new file mode 100644 index 0000000000000..7309b647c6dbd --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/hi3xxx-dw-mshc.txt @@ -0,0 +1,36 @@ +* Hisilicon specific extensions for Synopsys DW Memory Card + Storage Host Controller + +Read hi3xxx-dw-mshc.txt for more details + +The Synopsys designware storage host controller is used to interface +a SoC with storage medium such as eMMC or SD/MMC cards. This file documents +differences between the core Synopsys dw mshc hi3xxx controller properties described +by hi3xxx-dw-mshc.txt and the properties used by the Hisilicon hi3xxx +specific extensions to the Synopsys Designware Storage Host Controller. + +Required Properties: +- compatible: Should contain "hisilicon,davinci-dw-mshc". +- customized: Should have this property. + +Example: + + dwmmc0: dwmmc0@10CF90000 { + compatible = "hisilicon,davinci-dw-mshc"; + reg = <0x1 0x0CF90000 0x0 0x1000>, + <0x1 0x0c000000 0x0 0x10000>, + <0x1 0x100c0000 0x0 0x10000>; + interrupts = <0 39 4>; + customized; + num-slots = <1>; + fifo-depth = <0x100>; + card-detect-delay = <200>; + broken-cd; + non-removable; + supports-highspeed; + caps2-mmc-hs200-1_8v; + caps2-mmc-cache-ctrl; + keep-power-in-suspend; + caps2-mmc-bkops_auto-ctrl; + pinctrl-names = "default", "idle"; + }
From: zhangguijiang zhangguijiang@huawei.com
ascend inclusion category: feature feature: Ascend emmc adaption bugzilla: https://gitee.com/openeuler/kernel/issues/I4F4LL CVE: NA
--------------------
open CONFIG_ASCEND_HISI_MMC
Signed-off-by: zhangguijiang zhangguijiang@huawei.com Reviewed-by: Ding Tianhong dingtianhong@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- arch/arm64/configs/hulk_defconfig | 1 + 1 file changed, 1 insertion(+)
diff --git a/arch/arm64/configs/hulk_defconfig b/arch/arm64/configs/hulk_defconfig index 45ca392bd0585..e8c368d683797 100644 --- a/arch/arm64/configs/hulk_defconfig +++ b/arch/arm64/configs/hulk_defconfig @@ -4201,6 +4201,7 @@ CONFIG_UWB_HWA=m CONFIG_UWB_WHCI=m CONFIG_UWB_I1480U=m CONFIG_MMC=m +CONFIG_ASCEND_HISI_MMC=y CONFIG_PWRSEQ_EMMC=m CONFIG_PWRSEQ_SIMPLE=m CONFIG_MMC_BLOCK=m