18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Universal Flash Storage Host controller driver Core
48c2ecf20Sopenharmony_ci * Copyright (C) 2011-2013 Samsung India Software Operations
58c2ecf20Sopenharmony_ci * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Authors:
88c2ecf20Sopenharmony_ci *	Santosh Yaraganavi <santosh.sy@samsung.com>
98c2ecf20Sopenharmony_ci *	Vinayak Holikatti <h.vinayak@samsung.com>
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/async.h>
138c2ecf20Sopenharmony_ci#include <linux/devfreq.h>
148c2ecf20Sopenharmony_ci#include <linux/nls.h>
158c2ecf20Sopenharmony_ci#include <linux/of.h>
168c2ecf20Sopenharmony_ci#include <linux/bitfield.h>
178c2ecf20Sopenharmony_ci#include <linux/blk-pm.h>
188c2ecf20Sopenharmony_ci#include <linux/blkdev.h>
198c2ecf20Sopenharmony_ci#include "ufshcd.h"
208c2ecf20Sopenharmony_ci#include "ufs_quirks.h"
218c2ecf20Sopenharmony_ci#include "unipro.h"
228c2ecf20Sopenharmony_ci#include "ufs-sysfs.h"
238c2ecf20Sopenharmony_ci#include "ufs_bsg.h"
248c2ecf20Sopenharmony_ci#include "ufshcd-crypto.h"
258c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
268c2ecf20Sopenharmony_ci#include <linux/blkdev.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#define CREATE_TRACE_POINTS
298c2ecf20Sopenharmony_ci#include <trace/events/ufs.h>
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#define UFSHCD_ENABLE_INTRS	(UTP_TRANSFER_REQ_COMPL |\
328c2ecf20Sopenharmony_ci				 UTP_TASK_REQ_COMPL |\
338c2ecf20Sopenharmony_ci				 UFSHCD_ERROR_MASK)
348c2ecf20Sopenharmony_ci/* UIC command timeout, unit: ms */
358c2ecf20Sopenharmony_ci#define UIC_CMD_TIMEOUT	500
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci/* NOP OUT retries waiting for NOP IN response */
388c2ecf20Sopenharmony_ci#define NOP_OUT_RETRIES    10
398c2ecf20Sopenharmony_ci/* Timeout after 50 msecs if NOP OUT hangs without response */
408c2ecf20Sopenharmony_ci#define NOP_OUT_TIMEOUT    50 /* msecs */
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/* Query request retries */
438c2ecf20Sopenharmony_ci#define QUERY_REQ_RETRIES 3
448c2ecf20Sopenharmony_ci/* Query request timeout */
458c2ecf20Sopenharmony_ci#define QUERY_REQ_TIMEOUT 1500 /* 1.5 seconds */
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/* Task management command timeout */
488c2ecf20Sopenharmony_ci#define TM_CMD_TIMEOUT	100 /* msecs */
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/* maximum number of retries for a general UIC command  */
518c2ecf20Sopenharmony_ci#define UFS_UIC_COMMAND_RETRIES 3
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci/* maximum number of link-startup retries */
548c2ecf20Sopenharmony_ci#define DME_LINKSTARTUP_RETRIES 3
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci/* Maximum retries for Hibern8 enter */
578c2ecf20Sopenharmony_ci#define UIC_HIBERN8_ENTER_RETRIES 3
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci/* maximum number of reset retries before giving up */
608c2ecf20Sopenharmony_ci#define MAX_HOST_RESET_RETRIES 5
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci/* Expose the flag value from utp_upiu_query.value */
638c2ecf20Sopenharmony_ci#define MASK_QUERY_UPIU_FLAG_LOC 0xFF
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci/* Interrupt aggregation default timeout, unit: 40us */
668c2ecf20Sopenharmony_ci#define INT_AGGR_DEF_TO	0x02
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci/* default delay of autosuspend: 2000 ms */
698c2ecf20Sopenharmony_ci#define RPM_AUTOSUSPEND_DELAY_MS 2000
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci/* Default delay of RPM device flush delayed work */
728c2ecf20Sopenharmony_ci#define RPM_DEV_FLUSH_RECHECK_WORK_DELAY_MS 5000
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci/* Default value of wait time before gating device ref clock */
758c2ecf20Sopenharmony_ci#define UFSHCD_REF_CLK_GATING_WAIT_US 0xFF /* microsecs */
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci/* Polling time to wait for fDeviceInit */
788c2ecf20Sopenharmony_ci#define FDEVICEINIT_COMPL_TIMEOUT 1500 /* millisecs */
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci#define ufshcd_toggle_vreg(_dev, _vreg, _on)				\
818c2ecf20Sopenharmony_ci	({                                                              \
828c2ecf20Sopenharmony_ci		int _ret;                                               \
838c2ecf20Sopenharmony_ci		if (_on)                                                \
848c2ecf20Sopenharmony_ci			_ret = ufshcd_enable_vreg(_dev, _vreg);         \
858c2ecf20Sopenharmony_ci		else                                                    \
868c2ecf20Sopenharmony_ci			_ret = ufshcd_disable_vreg(_dev, _vreg);        \
878c2ecf20Sopenharmony_ci		_ret;                                                   \
888c2ecf20Sopenharmony_ci	})
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci#define ufshcd_hex_dump(prefix_str, buf, len) do {                       \
918c2ecf20Sopenharmony_ci	size_t __len = (len);                                            \
928c2ecf20Sopenharmony_ci	print_hex_dump(KERN_ERR, prefix_str,                             \
938c2ecf20Sopenharmony_ci		       __len > 4 ? DUMP_PREFIX_OFFSET : DUMP_PREFIX_NONE,\
948c2ecf20Sopenharmony_ci		       16, 4, buf, __len, false);                        \
958c2ecf20Sopenharmony_ci} while (0)
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ciint ufshcd_dump_regs(struct ufs_hba *hba, size_t offset, size_t len,
988c2ecf20Sopenharmony_ci		     const char *prefix)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	u32 *regs;
1018c2ecf20Sopenharmony_ci	size_t pos;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	if (offset % 4 != 0 || len % 4 != 0) /* keep readl happy */
1048c2ecf20Sopenharmony_ci		return -EINVAL;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	regs = kzalloc(len, GFP_ATOMIC);
1078c2ecf20Sopenharmony_ci	if (!regs)
1088c2ecf20Sopenharmony_ci		return -ENOMEM;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	for (pos = 0; pos < len; pos += 4) {
1118c2ecf20Sopenharmony_ci		if (offset == 0 &&
1128c2ecf20Sopenharmony_ci		    pos >= REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER &&
1138c2ecf20Sopenharmony_ci		    pos <= REG_UIC_ERROR_CODE_DME)
1148c2ecf20Sopenharmony_ci			continue;
1158c2ecf20Sopenharmony_ci		regs[pos / 4] = ufshcd_readl(hba, offset + pos);
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	ufshcd_hex_dump(prefix, regs, len);
1198c2ecf20Sopenharmony_ci	kfree(regs);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	return 0;
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ufshcd_dump_regs);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cienum {
1268c2ecf20Sopenharmony_ci	UFSHCD_MAX_CHANNEL	= 0,
1278c2ecf20Sopenharmony_ci	UFSHCD_MAX_ID		= 1,
1288c2ecf20Sopenharmony_ci	UFSHCD_CMD_PER_LUN	= 32,
1298c2ecf20Sopenharmony_ci	UFSHCD_CAN_QUEUE	= 32,
1308c2ecf20Sopenharmony_ci};
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci/* UFSHCD states */
1338c2ecf20Sopenharmony_cienum {
1348c2ecf20Sopenharmony_ci	UFSHCD_STATE_RESET,
1358c2ecf20Sopenharmony_ci	UFSHCD_STATE_ERROR,
1368c2ecf20Sopenharmony_ci	UFSHCD_STATE_OPERATIONAL,
1378c2ecf20Sopenharmony_ci	UFSHCD_STATE_EH_SCHEDULED_FATAL,
1388c2ecf20Sopenharmony_ci	UFSHCD_STATE_EH_SCHEDULED_NON_FATAL,
1398c2ecf20Sopenharmony_ci};
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci/* UFSHCD error handling flags */
1428c2ecf20Sopenharmony_cienum {
1438c2ecf20Sopenharmony_ci	UFSHCD_EH_IN_PROGRESS = (1 << 0),
1448c2ecf20Sopenharmony_ci};
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci/* UFSHCD UIC layer error flags */
1478c2ecf20Sopenharmony_cienum {
1488c2ecf20Sopenharmony_ci	UFSHCD_UIC_DL_PA_INIT_ERROR = (1 << 0), /* Data link layer error */
1498c2ecf20Sopenharmony_ci	UFSHCD_UIC_DL_NAC_RECEIVED_ERROR = (1 << 1), /* Data link layer error */
1508c2ecf20Sopenharmony_ci	UFSHCD_UIC_DL_TCx_REPLAY_ERROR = (1 << 2), /* Data link layer error */
1518c2ecf20Sopenharmony_ci	UFSHCD_UIC_NL_ERROR = (1 << 3), /* Network layer error */
1528c2ecf20Sopenharmony_ci	UFSHCD_UIC_TL_ERROR = (1 << 4), /* Transport Layer error */
1538c2ecf20Sopenharmony_ci	UFSHCD_UIC_DME_ERROR = (1 << 5), /* DME error */
1548c2ecf20Sopenharmony_ci	UFSHCD_UIC_PA_GENERIC_ERROR = (1 << 6), /* Generic PA error */
1558c2ecf20Sopenharmony_ci};
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci#define ufshcd_set_eh_in_progress(h) \
1588c2ecf20Sopenharmony_ci	((h)->eh_flags |= UFSHCD_EH_IN_PROGRESS)
1598c2ecf20Sopenharmony_ci#define ufshcd_eh_in_progress(h) \
1608c2ecf20Sopenharmony_ci	((h)->eh_flags & UFSHCD_EH_IN_PROGRESS)
1618c2ecf20Sopenharmony_ci#define ufshcd_clear_eh_in_progress(h) \
1628c2ecf20Sopenharmony_ci	((h)->eh_flags &= ~UFSHCD_EH_IN_PROGRESS)
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_cistruct ufs_pm_lvl_states ufs_pm_lvl_states[] = {
1658c2ecf20Sopenharmony_ci	{UFS_ACTIVE_PWR_MODE, UIC_LINK_ACTIVE_STATE},
1668c2ecf20Sopenharmony_ci	{UFS_ACTIVE_PWR_MODE, UIC_LINK_HIBERN8_STATE},
1678c2ecf20Sopenharmony_ci	{UFS_SLEEP_PWR_MODE, UIC_LINK_ACTIVE_STATE},
1688c2ecf20Sopenharmony_ci	{UFS_SLEEP_PWR_MODE, UIC_LINK_HIBERN8_STATE},
1698c2ecf20Sopenharmony_ci	{UFS_POWERDOWN_PWR_MODE, UIC_LINK_HIBERN8_STATE},
1708c2ecf20Sopenharmony_ci	{UFS_POWERDOWN_PWR_MODE, UIC_LINK_OFF_STATE},
1718c2ecf20Sopenharmony_ci};
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic inline enum ufs_dev_pwr_mode
1748c2ecf20Sopenharmony_ciufs_get_pm_lvl_to_dev_pwr_mode(enum ufs_pm_level lvl)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	return ufs_pm_lvl_states[lvl].dev_state;
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic inline enum uic_link_state
1808c2ecf20Sopenharmony_ciufs_get_pm_lvl_to_link_pwr_state(enum ufs_pm_level lvl)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	return ufs_pm_lvl_states[lvl].link_state;
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic inline enum ufs_pm_level
1868c2ecf20Sopenharmony_ciufs_get_desired_pm_lvl_for_dev_link_state(enum ufs_dev_pwr_mode dev_state,
1878c2ecf20Sopenharmony_ci					enum uic_link_state link_state)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	enum ufs_pm_level lvl;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	for (lvl = UFS_PM_LVL_0; lvl < UFS_PM_LVL_MAX; lvl++) {
1928c2ecf20Sopenharmony_ci		if ((ufs_pm_lvl_states[lvl].dev_state == dev_state) &&
1938c2ecf20Sopenharmony_ci			(ufs_pm_lvl_states[lvl].link_state == link_state))
1948c2ecf20Sopenharmony_ci			return lvl;
1958c2ecf20Sopenharmony_ci	}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	/* if no match found, return the level 0 */
1988c2ecf20Sopenharmony_ci	return UFS_PM_LVL_0;
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_cistatic struct ufs_dev_fix ufs_fixups[] = {
2028c2ecf20Sopenharmony_ci	/* UFS cards deviations table */
2038c2ecf20Sopenharmony_ci	UFS_FIX(UFS_VENDOR_MICRON, UFS_ANY_MODEL,
2048c2ecf20Sopenharmony_ci		UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM),
2058c2ecf20Sopenharmony_ci	UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
2068c2ecf20Sopenharmony_ci		UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM |
2078c2ecf20Sopenharmony_ci		UFS_DEVICE_QUIRK_HOST_PA_TACTIVATE |
2088c2ecf20Sopenharmony_ci		UFS_DEVICE_QUIRK_RECOVERY_FROM_DL_NAC_ERRORS),
2098c2ecf20Sopenharmony_ci	UFS_FIX(UFS_VENDOR_SKHYNIX, UFS_ANY_MODEL,
2108c2ecf20Sopenharmony_ci		UFS_DEVICE_QUIRK_HOST_PA_SAVECONFIGTIME),
2118c2ecf20Sopenharmony_ci	UFS_FIX(UFS_VENDOR_SKHYNIX, "hB8aL1" /*H28U62301AMR*/,
2128c2ecf20Sopenharmony_ci		UFS_DEVICE_QUIRK_HOST_VS_DEBUGSAVECONFIGTIME),
2138c2ecf20Sopenharmony_ci	UFS_FIX(UFS_VENDOR_TOSHIBA, UFS_ANY_MODEL,
2148c2ecf20Sopenharmony_ci		UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM),
2158c2ecf20Sopenharmony_ci	UFS_FIX(UFS_VENDOR_TOSHIBA, "THGLF2G9C8KBADG",
2168c2ecf20Sopenharmony_ci		UFS_DEVICE_QUIRK_PA_TACTIVATE),
2178c2ecf20Sopenharmony_ci	UFS_FIX(UFS_VENDOR_TOSHIBA, "THGLF2G9D8KBADG",
2188c2ecf20Sopenharmony_ci		UFS_DEVICE_QUIRK_PA_TACTIVATE),
2198c2ecf20Sopenharmony_ci	END_FIX
2208c2ecf20Sopenharmony_ci};
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cistatic irqreturn_t ufshcd_tmc_handler(struct ufs_hba *hba);
2238c2ecf20Sopenharmony_cistatic void ufshcd_async_scan(void *data, async_cookie_t cookie);
2248c2ecf20Sopenharmony_cistatic int ufshcd_reset_and_restore(struct ufs_hba *hba);
2258c2ecf20Sopenharmony_cistatic int ufshcd_eh_host_reset_handler(struct scsi_cmnd *cmd);
2268c2ecf20Sopenharmony_cistatic int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int tag);
2278c2ecf20Sopenharmony_cistatic void ufshcd_hba_exit(struct ufs_hba *hba);
2288c2ecf20Sopenharmony_cistatic int ufshcd_probe_hba(struct ufs_hba *hba, bool async);
2298c2ecf20Sopenharmony_cistatic int ufshcd_setup_clocks(struct ufs_hba *hba, bool on);
2308c2ecf20Sopenharmony_cistatic int ufshcd_uic_hibern8_enter(struct ufs_hba *hba);
2318c2ecf20Sopenharmony_cistatic inline void ufshcd_add_delay_before_dme_cmd(struct ufs_hba *hba);
2328c2ecf20Sopenharmony_cistatic int ufshcd_host_reset_and_restore(struct ufs_hba *hba);
2338c2ecf20Sopenharmony_cistatic void ufshcd_resume_clkscaling(struct ufs_hba *hba);
2348c2ecf20Sopenharmony_cistatic void ufshcd_suspend_clkscaling(struct ufs_hba *hba);
2358c2ecf20Sopenharmony_cistatic void __ufshcd_suspend_clkscaling(struct ufs_hba *hba);
2368c2ecf20Sopenharmony_cistatic int ufshcd_scale_clks(struct ufs_hba *hba, bool scale_up);
2378c2ecf20Sopenharmony_cistatic irqreturn_t ufshcd_intr(int irq, void *__hba);
2388c2ecf20Sopenharmony_cistatic int ufshcd_change_power_mode(struct ufs_hba *hba,
2398c2ecf20Sopenharmony_ci			     struct ufs_pa_layer_attr *pwr_mode);
2408c2ecf20Sopenharmony_cistatic void ufshcd_schedule_eh_work(struct ufs_hba *hba);
2418c2ecf20Sopenharmony_cistatic int ufshcd_setup_hba_vreg(struct ufs_hba *hba, bool on);
2428c2ecf20Sopenharmony_cistatic int ufshcd_setup_vreg(struct ufs_hba *hba, bool on);
2438c2ecf20Sopenharmony_cistatic inline int ufshcd_config_vreg_hpm(struct ufs_hba *hba,
2448c2ecf20Sopenharmony_ci					 struct ufs_vreg *vreg);
2458c2ecf20Sopenharmony_cistatic int ufshcd_try_to_abort_task(struct ufs_hba *hba, int tag);
2468c2ecf20Sopenharmony_cistatic int ufshcd_wb_buf_flush_enable(struct ufs_hba *hba);
2478c2ecf20Sopenharmony_cistatic int ufshcd_wb_buf_flush_disable(struct ufs_hba *hba);
2488c2ecf20Sopenharmony_cistatic int ufshcd_wb_ctrl(struct ufs_hba *hba, bool enable);
2498c2ecf20Sopenharmony_cistatic int ufshcd_wb_toggle_flush_during_h8(struct ufs_hba *hba, bool set);
2508c2ecf20Sopenharmony_cistatic inline void ufshcd_wb_toggle_flush(struct ufs_hba *hba, bool enable);
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_cistatic inline bool ufshcd_valid_tag(struct ufs_hba *hba, int tag)
2538c2ecf20Sopenharmony_ci{
2548c2ecf20Sopenharmony_ci	return tag >= 0 && tag < hba->nutrs;
2558c2ecf20Sopenharmony_ci}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cistatic inline void ufshcd_enable_irq(struct ufs_hba *hba)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	if (!hba->is_irq_enabled) {
2608c2ecf20Sopenharmony_ci		enable_irq(hba->irq);
2618c2ecf20Sopenharmony_ci		hba->is_irq_enabled = true;
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cistatic inline void ufshcd_disable_irq(struct ufs_hba *hba)
2668c2ecf20Sopenharmony_ci{
2678c2ecf20Sopenharmony_ci	if (hba->is_irq_enabled) {
2688c2ecf20Sopenharmony_ci		disable_irq(hba->irq);
2698c2ecf20Sopenharmony_ci		hba->is_irq_enabled = false;
2708c2ecf20Sopenharmony_ci	}
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_cistatic inline void ufshcd_wb_config(struct ufs_hba *hba)
2748c2ecf20Sopenharmony_ci{
2758c2ecf20Sopenharmony_ci	int ret;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	if (!ufshcd_is_wb_allowed(hba))
2788c2ecf20Sopenharmony_ci		return;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	ret = ufshcd_wb_ctrl(hba, true);
2818c2ecf20Sopenharmony_ci	if (ret)
2828c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: Enable WB failed: %d\n", __func__, ret);
2838c2ecf20Sopenharmony_ci	else
2848c2ecf20Sopenharmony_ci		dev_info(hba->dev, "%s: Write Booster Configured\n", __func__);
2858c2ecf20Sopenharmony_ci	ret = ufshcd_wb_toggle_flush_during_h8(hba, true);
2868c2ecf20Sopenharmony_ci	if (ret)
2878c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: En WB flush during H8: failed: %d\n",
2888c2ecf20Sopenharmony_ci			__func__, ret);
2898c2ecf20Sopenharmony_ci	if (!(hba->quirks & UFSHCI_QUIRK_SKIP_MANUAL_WB_FLUSH_CTRL))
2908c2ecf20Sopenharmony_ci		ufshcd_wb_toggle_flush(hba, true);
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic void ufshcd_scsi_unblock_requests(struct ufs_hba *hba)
2948c2ecf20Sopenharmony_ci{
2958c2ecf20Sopenharmony_ci	if (atomic_dec_and_test(&hba->scsi_block_reqs_cnt))
2968c2ecf20Sopenharmony_ci		scsi_unblock_requests(hba->host);
2978c2ecf20Sopenharmony_ci}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_cistatic void ufshcd_scsi_block_requests(struct ufs_hba *hba)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	if (atomic_inc_return(&hba->scsi_block_reqs_cnt) == 1)
3028c2ecf20Sopenharmony_ci		scsi_block_requests(hba->host);
3038c2ecf20Sopenharmony_ci}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_cistatic void ufshcd_add_cmd_upiu_trace(struct ufs_hba *hba, unsigned int tag,
3068c2ecf20Sopenharmony_ci		const char *str)
3078c2ecf20Sopenharmony_ci{
3088c2ecf20Sopenharmony_ci	struct utp_upiu_req *rq = hba->lrb[tag].ucd_req_ptr;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	trace_ufshcd_upiu(dev_name(hba->dev), str, &rq->header, &rq->sc.cdb);
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_cistatic void ufshcd_add_query_upiu_trace(struct ufs_hba *hba, unsigned int tag,
3148c2ecf20Sopenharmony_ci		const char *str)
3158c2ecf20Sopenharmony_ci{
3168c2ecf20Sopenharmony_ci	struct utp_upiu_req *rq = hba->lrb[tag].ucd_req_ptr;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	trace_ufshcd_upiu(dev_name(hba->dev), str, &rq->header, &rq->qr);
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic void ufshcd_add_tm_upiu_trace(struct ufs_hba *hba, unsigned int tag,
3228c2ecf20Sopenharmony_ci		const char *str)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	struct utp_task_req_desc *descp = &hba->utmrdl_base_addr[tag];
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	trace_ufshcd_upiu(dev_name(hba->dev), str, &descp->req_header,
3278c2ecf20Sopenharmony_ci			&descp->input_param1);
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cistatic void ufshcd_add_uic_command_trace(struct ufs_hba *hba,
3318c2ecf20Sopenharmony_ci					 struct uic_command *ucmd,
3328c2ecf20Sopenharmony_ci					 const char *str)
3338c2ecf20Sopenharmony_ci{
3348c2ecf20Sopenharmony_ci	u32 cmd;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	if (!trace_ufshcd_uic_command_enabled())
3378c2ecf20Sopenharmony_ci		return;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	if (!strcmp(str, "send"))
3408c2ecf20Sopenharmony_ci		cmd = ucmd->command;
3418c2ecf20Sopenharmony_ci	else
3428c2ecf20Sopenharmony_ci		cmd = ufshcd_readl(hba, REG_UIC_COMMAND);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	trace_ufshcd_uic_command(dev_name(hba->dev), str, cmd,
3458c2ecf20Sopenharmony_ci				 ufshcd_readl(hba, REG_UIC_COMMAND_ARG_1),
3468c2ecf20Sopenharmony_ci				 ufshcd_readl(hba, REG_UIC_COMMAND_ARG_2),
3478c2ecf20Sopenharmony_ci				 ufshcd_readl(hba, REG_UIC_COMMAND_ARG_3));
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_cistatic void ufshcd_add_command_trace(struct ufs_hba *hba,
3518c2ecf20Sopenharmony_ci		unsigned int tag, const char *str)
3528c2ecf20Sopenharmony_ci{
3538c2ecf20Sopenharmony_ci	sector_t lba = -1;
3548c2ecf20Sopenharmony_ci	u8 opcode = 0;
3558c2ecf20Sopenharmony_ci	u32 intr, doorbell;
3568c2ecf20Sopenharmony_ci	struct ufshcd_lrb *lrbp = &hba->lrb[tag];
3578c2ecf20Sopenharmony_ci	struct scsi_cmnd *cmd = lrbp->cmd;
3588c2ecf20Sopenharmony_ci	int transfer_len = -1;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	if (!trace_ufshcd_command_enabled()) {
3618c2ecf20Sopenharmony_ci		/* trace UPIU W/O tracing command */
3628c2ecf20Sopenharmony_ci		if (cmd)
3638c2ecf20Sopenharmony_ci			ufshcd_add_cmd_upiu_trace(hba, tag, str);
3648c2ecf20Sopenharmony_ci		return;
3658c2ecf20Sopenharmony_ci	}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	if (cmd) { /* data phase exists */
3688c2ecf20Sopenharmony_ci		/* trace UPIU also */
3698c2ecf20Sopenharmony_ci		ufshcd_add_cmd_upiu_trace(hba, tag, str);
3708c2ecf20Sopenharmony_ci		opcode = cmd->cmnd[0];
3718c2ecf20Sopenharmony_ci		if ((opcode == READ_10) || (opcode == WRITE_10)) {
3728c2ecf20Sopenharmony_ci			/*
3738c2ecf20Sopenharmony_ci			 * Currently we only fully trace read(10) and write(10)
3748c2ecf20Sopenharmony_ci			 * commands
3758c2ecf20Sopenharmony_ci			 */
3768c2ecf20Sopenharmony_ci			if (cmd->request && cmd->request->bio)
3778c2ecf20Sopenharmony_ci				lba = cmd->request->bio->bi_iter.bi_sector;
3788c2ecf20Sopenharmony_ci			transfer_len = be32_to_cpu(
3798c2ecf20Sopenharmony_ci				lrbp->ucd_req_ptr->sc.exp_data_transfer_len);
3808c2ecf20Sopenharmony_ci		}
3818c2ecf20Sopenharmony_ci	}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	intr = ufshcd_readl(hba, REG_INTERRUPT_STATUS);
3848c2ecf20Sopenharmony_ci	doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
3858c2ecf20Sopenharmony_ci	trace_ufshcd_command(dev_name(hba->dev), str, tag,
3868c2ecf20Sopenharmony_ci				doorbell, transfer_len, intr, lba, opcode);
3878c2ecf20Sopenharmony_ci}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_cistatic void ufshcd_print_clk_freqs(struct ufs_hba *hba)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	struct ufs_clk_info *clki;
3928c2ecf20Sopenharmony_ci	struct list_head *head = &hba->clk_list_head;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	if (list_empty(head))
3958c2ecf20Sopenharmony_ci		return;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	list_for_each_entry(clki, head, list) {
3988c2ecf20Sopenharmony_ci		if (!IS_ERR_OR_NULL(clki->clk) && clki->min_freq &&
3998c2ecf20Sopenharmony_ci				clki->max_freq)
4008c2ecf20Sopenharmony_ci			dev_err(hba->dev, "clk: %s, rate: %u\n",
4018c2ecf20Sopenharmony_ci					clki->name, clki->curr_freq);
4028c2ecf20Sopenharmony_ci	}
4038c2ecf20Sopenharmony_ci}
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_cistatic void ufshcd_print_err_hist(struct ufs_hba *hba,
4068c2ecf20Sopenharmony_ci				  struct ufs_err_reg_hist *err_hist,
4078c2ecf20Sopenharmony_ci				  char *err_name)
4088c2ecf20Sopenharmony_ci{
4098c2ecf20Sopenharmony_ci	int i;
4108c2ecf20Sopenharmony_ci	bool found = false;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	for (i = 0; i < UFS_ERR_REG_HIST_LENGTH; i++) {
4138c2ecf20Sopenharmony_ci		int p = (i + err_hist->pos) % UFS_ERR_REG_HIST_LENGTH;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci		if (err_hist->tstamp[p] == 0)
4168c2ecf20Sopenharmony_ci			continue;
4178c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s[%d] = 0x%x at %lld us\n", err_name, p,
4188c2ecf20Sopenharmony_ci			err_hist->reg[p], ktime_to_us(err_hist->tstamp[p]));
4198c2ecf20Sopenharmony_ci		found = true;
4208c2ecf20Sopenharmony_ci	}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	if (!found)
4238c2ecf20Sopenharmony_ci		dev_err(hba->dev, "No record of %s\n", err_name);
4248c2ecf20Sopenharmony_ci}
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_cistatic void ufshcd_print_host_regs(struct ufs_hba *hba)
4278c2ecf20Sopenharmony_ci{
4288c2ecf20Sopenharmony_ci	ufshcd_dump_regs(hba, 0, UFSHCI_REG_SPACE_SIZE, "host_regs: ");
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	ufshcd_print_err_hist(hba, &hba->ufs_stats.pa_err, "pa_err");
4318c2ecf20Sopenharmony_ci	ufshcd_print_err_hist(hba, &hba->ufs_stats.dl_err, "dl_err");
4328c2ecf20Sopenharmony_ci	ufshcd_print_err_hist(hba, &hba->ufs_stats.nl_err, "nl_err");
4338c2ecf20Sopenharmony_ci	ufshcd_print_err_hist(hba, &hba->ufs_stats.tl_err, "tl_err");
4348c2ecf20Sopenharmony_ci	ufshcd_print_err_hist(hba, &hba->ufs_stats.dme_err, "dme_err");
4358c2ecf20Sopenharmony_ci	ufshcd_print_err_hist(hba, &hba->ufs_stats.auto_hibern8_err,
4368c2ecf20Sopenharmony_ci			      "auto_hibern8_err");
4378c2ecf20Sopenharmony_ci	ufshcd_print_err_hist(hba, &hba->ufs_stats.fatal_err, "fatal_err");
4388c2ecf20Sopenharmony_ci	ufshcd_print_err_hist(hba, &hba->ufs_stats.link_startup_err,
4398c2ecf20Sopenharmony_ci			      "link_startup_fail");
4408c2ecf20Sopenharmony_ci	ufshcd_print_err_hist(hba, &hba->ufs_stats.resume_err, "resume_fail");
4418c2ecf20Sopenharmony_ci	ufshcd_print_err_hist(hba, &hba->ufs_stats.suspend_err,
4428c2ecf20Sopenharmony_ci			      "suspend_fail");
4438c2ecf20Sopenharmony_ci	ufshcd_print_err_hist(hba, &hba->ufs_stats.dev_reset, "dev_reset");
4448c2ecf20Sopenharmony_ci	ufshcd_print_err_hist(hba, &hba->ufs_stats.host_reset, "host_reset");
4458c2ecf20Sopenharmony_ci	ufshcd_print_err_hist(hba, &hba->ufs_stats.task_abort, "task_abort");
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	ufshcd_vops_dbg_register_dump(hba);
4488c2ecf20Sopenharmony_ci}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_cistatic
4518c2ecf20Sopenharmony_civoid ufshcd_print_trs(struct ufs_hba *hba, unsigned long bitmap, bool pr_prdt)
4528c2ecf20Sopenharmony_ci{
4538c2ecf20Sopenharmony_ci	struct ufshcd_lrb *lrbp;
4548c2ecf20Sopenharmony_ci	int prdt_length;
4558c2ecf20Sopenharmony_ci	int tag;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	for_each_set_bit(tag, &bitmap, hba->nutrs) {
4588c2ecf20Sopenharmony_ci		lrbp = &hba->lrb[tag];
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci		dev_err(hba->dev, "UPIU[%d] - issue time %lld us\n",
4618c2ecf20Sopenharmony_ci				tag, ktime_to_us(lrbp->issue_time_stamp));
4628c2ecf20Sopenharmony_ci		dev_err(hba->dev, "UPIU[%d] - complete time %lld us\n",
4638c2ecf20Sopenharmony_ci				tag, ktime_to_us(lrbp->compl_time_stamp));
4648c2ecf20Sopenharmony_ci		dev_err(hba->dev,
4658c2ecf20Sopenharmony_ci			"UPIU[%d] - Transfer Request Descriptor phys@0x%llx\n",
4668c2ecf20Sopenharmony_ci			tag, (u64)lrbp->utrd_dma_addr);
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci		ufshcd_hex_dump("UPIU TRD: ", lrbp->utr_descriptor_ptr,
4698c2ecf20Sopenharmony_ci				sizeof(struct utp_transfer_req_desc));
4708c2ecf20Sopenharmony_ci		dev_err(hba->dev, "UPIU[%d] - Request UPIU phys@0x%llx\n", tag,
4718c2ecf20Sopenharmony_ci			(u64)lrbp->ucd_req_dma_addr);
4728c2ecf20Sopenharmony_ci		ufshcd_hex_dump("UPIU REQ: ", lrbp->ucd_req_ptr,
4738c2ecf20Sopenharmony_ci				sizeof(struct utp_upiu_req));
4748c2ecf20Sopenharmony_ci		dev_err(hba->dev, "UPIU[%d] - Response UPIU phys@0x%llx\n", tag,
4758c2ecf20Sopenharmony_ci			(u64)lrbp->ucd_rsp_dma_addr);
4768c2ecf20Sopenharmony_ci		ufshcd_hex_dump("UPIU RSP: ", lrbp->ucd_rsp_ptr,
4778c2ecf20Sopenharmony_ci				sizeof(struct utp_upiu_rsp));
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci		prdt_length = le16_to_cpu(
4808c2ecf20Sopenharmony_ci			lrbp->utr_descriptor_ptr->prd_table_length);
4818c2ecf20Sopenharmony_ci		if (hba->quirks & UFSHCD_QUIRK_PRDT_BYTE_GRAN)
4828c2ecf20Sopenharmony_ci			prdt_length /= sizeof(struct ufshcd_sg_entry);
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci		dev_err(hba->dev,
4858c2ecf20Sopenharmony_ci			"UPIU[%d] - PRDT - %d entries  phys@0x%llx\n",
4868c2ecf20Sopenharmony_ci			tag, prdt_length,
4878c2ecf20Sopenharmony_ci			(u64)lrbp->ucd_prdt_dma_addr);
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci		if (pr_prdt)
4908c2ecf20Sopenharmony_ci			ufshcd_hex_dump("UPIU PRDT: ", lrbp->ucd_prdt_ptr,
4918c2ecf20Sopenharmony_ci				sizeof(struct ufshcd_sg_entry) * prdt_length);
4928c2ecf20Sopenharmony_ci	}
4938c2ecf20Sopenharmony_ci}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_cistatic void ufshcd_print_tmrs(struct ufs_hba *hba, unsigned long bitmap)
4968c2ecf20Sopenharmony_ci{
4978c2ecf20Sopenharmony_ci	int tag;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	for_each_set_bit(tag, &bitmap, hba->nutmrs) {
5008c2ecf20Sopenharmony_ci		struct utp_task_req_desc *tmrdp = &hba->utmrdl_base_addr[tag];
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci		dev_err(hba->dev, "TM[%d] - Task Management Header\n", tag);
5038c2ecf20Sopenharmony_ci		ufshcd_hex_dump("", tmrdp, sizeof(*tmrdp));
5048c2ecf20Sopenharmony_ci	}
5058c2ecf20Sopenharmony_ci}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_cistatic void ufshcd_print_host_state(struct ufs_hba *hba)
5088c2ecf20Sopenharmony_ci{
5098c2ecf20Sopenharmony_ci	struct scsi_device *sdev_ufs = hba->sdev_ufs_device;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	dev_err(hba->dev, "UFS Host state=%d\n", hba->ufshcd_state);
5128c2ecf20Sopenharmony_ci	dev_err(hba->dev, "outstanding reqs=0x%lx tasks=0x%lx\n",
5138c2ecf20Sopenharmony_ci		hba->outstanding_reqs, hba->outstanding_tasks);
5148c2ecf20Sopenharmony_ci	dev_err(hba->dev, "saved_err=0x%x, saved_uic_err=0x%x\n",
5158c2ecf20Sopenharmony_ci		hba->saved_err, hba->saved_uic_err);
5168c2ecf20Sopenharmony_ci	dev_err(hba->dev, "Device power mode=%d, UIC link state=%d\n",
5178c2ecf20Sopenharmony_ci		hba->curr_dev_pwr_mode, hba->uic_link_state);
5188c2ecf20Sopenharmony_ci	dev_err(hba->dev, "PM in progress=%d, sys. suspended=%d\n",
5198c2ecf20Sopenharmony_ci		hba->pm_op_in_progress, hba->is_sys_suspended);
5208c2ecf20Sopenharmony_ci	dev_err(hba->dev, "Auto BKOPS=%d, Host self-block=%d\n",
5218c2ecf20Sopenharmony_ci		hba->auto_bkops_enabled, hba->host->host_self_blocked);
5228c2ecf20Sopenharmony_ci	dev_err(hba->dev, "Clk gate=%d\n", hba->clk_gating.state);
5238c2ecf20Sopenharmony_ci	dev_err(hba->dev,
5248c2ecf20Sopenharmony_ci		"last_hibern8_exit_tstamp at %lld us, hibern8_exit_cnt=%d\n",
5258c2ecf20Sopenharmony_ci		ktime_to_us(hba->ufs_stats.last_hibern8_exit_tstamp),
5268c2ecf20Sopenharmony_ci		hba->ufs_stats.hibern8_exit_cnt);
5278c2ecf20Sopenharmony_ci	dev_err(hba->dev, "last intr at %lld us, last intr status=0x%x\n",
5288c2ecf20Sopenharmony_ci		ktime_to_us(hba->ufs_stats.last_intr_ts),
5298c2ecf20Sopenharmony_ci		hba->ufs_stats.last_intr_status);
5308c2ecf20Sopenharmony_ci	dev_err(hba->dev, "error handling flags=0x%x, req. abort count=%d\n",
5318c2ecf20Sopenharmony_ci		hba->eh_flags, hba->req_abort_count);
5328c2ecf20Sopenharmony_ci	dev_err(hba->dev, "hba->ufs_version=0x%x, Host capabilities=0x%x, caps=0x%x\n",
5338c2ecf20Sopenharmony_ci		hba->ufs_version, hba->capabilities, hba->caps);
5348c2ecf20Sopenharmony_ci	dev_err(hba->dev, "quirks=0x%x, dev. quirks=0x%x\n", hba->quirks,
5358c2ecf20Sopenharmony_ci		hba->dev_quirks);
5368c2ecf20Sopenharmony_ci	if (sdev_ufs)
5378c2ecf20Sopenharmony_ci		dev_err(hba->dev, "UFS dev info: %.8s %.16s rev %.4s\n",
5388c2ecf20Sopenharmony_ci			sdev_ufs->vendor, sdev_ufs->model, sdev_ufs->rev);
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	ufshcd_print_clk_freqs(hba);
5418c2ecf20Sopenharmony_ci}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci/**
5448c2ecf20Sopenharmony_ci * ufshcd_print_pwr_info - print power params as saved in hba
5458c2ecf20Sopenharmony_ci * power info
5468c2ecf20Sopenharmony_ci * @hba: per-adapter instance
5478c2ecf20Sopenharmony_ci */
5488c2ecf20Sopenharmony_cistatic void ufshcd_print_pwr_info(struct ufs_hba *hba)
5498c2ecf20Sopenharmony_ci{
5508c2ecf20Sopenharmony_ci	static const char * const names[] = {
5518c2ecf20Sopenharmony_ci		"INVALID MODE",
5528c2ecf20Sopenharmony_ci		"FAST MODE",
5538c2ecf20Sopenharmony_ci		"SLOW_MODE",
5548c2ecf20Sopenharmony_ci		"INVALID MODE",
5558c2ecf20Sopenharmony_ci		"FASTAUTO_MODE",
5568c2ecf20Sopenharmony_ci		"SLOWAUTO_MODE",
5578c2ecf20Sopenharmony_ci		"INVALID MODE",
5588c2ecf20Sopenharmony_ci	};
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	dev_err(hba->dev, "%s:[RX, TX]: gear=[%d, %d], lane[%d, %d], pwr[%s, %s], rate = %d\n",
5618c2ecf20Sopenharmony_ci		 __func__,
5628c2ecf20Sopenharmony_ci		 hba->pwr_info.gear_rx, hba->pwr_info.gear_tx,
5638c2ecf20Sopenharmony_ci		 hba->pwr_info.lane_rx, hba->pwr_info.lane_tx,
5648c2ecf20Sopenharmony_ci		 names[hba->pwr_info.pwr_rx],
5658c2ecf20Sopenharmony_ci		 names[hba->pwr_info.pwr_tx],
5668c2ecf20Sopenharmony_ci		 hba->pwr_info.hs_rate);
5678c2ecf20Sopenharmony_ci}
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_civoid ufshcd_delay_us(unsigned long us, unsigned long tolerance)
5708c2ecf20Sopenharmony_ci{
5718c2ecf20Sopenharmony_ci	if (!us)
5728c2ecf20Sopenharmony_ci		return;
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	if (us < 10)
5758c2ecf20Sopenharmony_ci		udelay(us);
5768c2ecf20Sopenharmony_ci	else
5778c2ecf20Sopenharmony_ci		usleep_range(us, us + tolerance);
5788c2ecf20Sopenharmony_ci}
5798c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ufshcd_delay_us);
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci/**
5828c2ecf20Sopenharmony_ci * ufshcd_wait_for_register - wait for register value to change
5838c2ecf20Sopenharmony_ci * @hba: per-adapter interface
5848c2ecf20Sopenharmony_ci * @reg: mmio register offset
5858c2ecf20Sopenharmony_ci * @mask: mask to apply to the read register value
5868c2ecf20Sopenharmony_ci * @val: value to wait for
5878c2ecf20Sopenharmony_ci * @interval_us: polling interval in microseconds
5888c2ecf20Sopenharmony_ci * @timeout_ms: timeout in milliseconds
5898c2ecf20Sopenharmony_ci *
5908c2ecf20Sopenharmony_ci * Return:
5918c2ecf20Sopenharmony_ci * -ETIMEDOUT on error, zero on success.
5928c2ecf20Sopenharmony_ci */
5938c2ecf20Sopenharmony_ciint ufshcd_wait_for_register(struct ufs_hba *hba, u32 reg, u32 mask,
5948c2ecf20Sopenharmony_ci				u32 val, unsigned long interval_us,
5958c2ecf20Sopenharmony_ci				unsigned long timeout_ms)
5968c2ecf20Sopenharmony_ci{
5978c2ecf20Sopenharmony_ci	int err = 0;
5988c2ecf20Sopenharmony_ci	unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms);
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	/* ignore bits that we don't intend to wait on */
6018c2ecf20Sopenharmony_ci	val = val & mask;
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	while ((ufshcd_readl(hba, reg) & mask) != val) {
6048c2ecf20Sopenharmony_ci		usleep_range(interval_us, interval_us + 50);
6058c2ecf20Sopenharmony_ci		if (time_after(jiffies, timeout)) {
6068c2ecf20Sopenharmony_ci			if ((ufshcd_readl(hba, reg) & mask) != val)
6078c2ecf20Sopenharmony_ci				err = -ETIMEDOUT;
6088c2ecf20Sopenharmony_ci			break;
6098c2ecf20Sopenharmony_ci		}
6108c2ecf20Sopenharmony_ci	}
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	return err;
6138c2ecf20Sopenharmony_ci}
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci/**
6168c2ecf20Sopenharmony_ci * ufshcd_get_intr_mask - Get the interrupt bit mask
6178c2ecf20Sopenharmony_ci * @hba: Pointer to adapter instance
6188c2ecf20Sopenharmony_ci *
6198c2ecf20Sopenharmony_ci * Returns interrupt bit mask per version
6208c2ecf20Sopenharmony_ci */
6218c2ecf20Sopenharmony_cistatic inline u32 ufshcd_get_intr_mask(struct ufs_hba *hba)
6228c2ecf20Sopenharmony_ci{
6238c2ecf20Sopenharmony_ci	u32 intr_mask = 0;
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	switch (hba->ufs_version) {
6268c2ecf20Sopenharmony_ci	case UFSHCI_VERSION_10:
6278c2ecf20Sopenharmony_ci		intr_mask = INTERRUPT_MASK_ALL_VER_10;
6288c2ecf20Sopenharmony_ci		break;
6298c2ecf20Sopenharmony_ci	case UFSHCI_VERSION_11:
6308c2ecf20Sopenharmony_ci	case UFSHCI_VERSION_20:
6318c2ecf20Sopenharmony_ci		intr_mask = INTERRUPT_MASK_ALL_VER_11;
6328c2ecf20Sopenharmony_ci		break;
6338c2ecf20Sopenharmony_ci	case UFSHCI_VERSION_21:
6348c2ecf20Sopenharmony_ci	default:
6358c2ecf20Sopenharmony_ci		intr_mask = INTERRUPT_MASK_ALL_VER_21;
6368c2ecf20Sopenharmony_ci		break;
6378c2ecf20Sopenharmony_ci	}
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	return intr_mask;
6408c2ecf20Sopenharmony_ci}
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci/**
6438c2ecf20Sopenharmony_ci * ufshcd_get_ufs_version - Get the UFS version supported by the HBA
6448c2ecf20Sopenharmony_ci * @hba: Pointer to adapter instance
6458c2ecf20Sopenharmony_ci *
6468c2ecf20Sopenharmony_ci * Returns UFSHCI version supported by the controller
6478c2ecf20Sopenharmony_ci */
6488c2ecf20Sopenharmony_cistatic inline u32 ufshcd_get_ufs_version(struct ufs_hba *hba)
6498c2ecf20Sopenharmony_ci{
6508c2ecf20Sopenharmony_ci	if (hba->quirks & UFSHCD_QUIRK_BROKEN_UFS_HCI_VERSION)
6518c2ecf20Sopenharmony_ci		return ufshcd_vops_get_ufs_hci_version(hba);
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	return ufshcd_readl(hba, REG_UFS_VERSION);
6548c2ecf20Sopenharmony_ci}
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci/**
6578c2ecf20Sopenharmony_ci * ufshcd_is_device_present - Check if any device connected to
6588c2ecf20Sopenharmony_ci *			      the host controller
6598c2ecf20Sopenharmony_ci * @hba: pointer to adapter instance
6608c2ecf20Sopenharmony_ci *
6618c2ecf20Sopenharmony_ci * Returns true if device present, false if no device detected
6628c2ecf20Sopenharmony_ci */
6638c2ecf20Sopenharmony_cistatic inline bool ufshcd_is_device_present(struct ufs_hba *hba)
6648c2ecf20Sopenharmony_ci{
6658c2ecf20Sopenharmony_ci	return (ufshcd_readl(hba, REG_CONTROLLER_STATUS) &
6668c2ecf20Sopenharmony_ci						DEVICE_PRESENT) ? true : false;
6678c2ecf20Sopenharmony_ci}
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci/**
6708c2ecf20Sopenharmony_ci * ufshcd_get_tr_ocs - Get the UTRD Overall Command Status
6718c2ecf20Sopenharmony_ci * @lrbp: pointer to local command reference block
6728c2ecf20Sopenharmony_ci *
6738c2ecf20Sopenharmony_ci * This function is used to get the OCS field from UTRD
6748c2ecf20Sopenharmony_ci * Returns the OCS field in the UTRD
6758c2ecf20Sopenharmony_ci */
6768c2ecf20Sopenharmony_cistatic inline int ufshcd_get_tr_ocs(struct ufshcd_lrb *lrbp)
6778c2ecf20Sopenharmony_ci{
6788c2ecf20Sopenharmony_ci	return le32_to_cpu(lrbp->utr_descriptor_ptr->header.dword_2) & MASK_OCS;
6798c2ecf20Sopenharmony_ci}
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci/**
6828c2ecf20Sopenharmony_ci * ufshcd_utrl_clear - Clear a bit in UTRLCLR register
6838c2ecf20Sopenharmony_ci * @hba: per adapter instance
6848c2ecf20Sopenharmony_ci * @pos: position of the bit to be cleared
6858c2ecf20Sopenharmony_ci */
6868c2ecf20Sopenharmony_cistatic inline void ufshcd_utrl_clear(struct ufs_hba *hba, u32 pos)
6878c2ecf20Sopenharmony_ci{
6888c2ecf20Sopenharmony_ci	if (hba->quirks & UFSHCI_QUIRK_BROKEN_REQ_LIST_CLR)
6898c2ecf20Sopenharmony_ci		ufshcd_writel(hba, (1 << pos), REG_UTP_TRANSFER_REQ_LIST_CLEAR);
6908c2ecf20Sopenharmony_ci	else
6918c2ecf20Sopenharmony_ci		ufshcd_writel(hba, ~(1 << pos),
6928c2ecf20Sopenharmony_ci				REG_UTP_TRANSFER_REQ_LIST_CLEAR);
6938c2ecf20Sopenharmony_ci}
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci/**
6968c2ecf20Sopenharmony_ci * ufshcd_utmrl_clear - Clear a bit in UTRMLCLR register
6978c2ecf20Sopenharmony_ci * @hba: per adapter instance
6988c2ecf20Sopenharmony_ci * @pos: position of the bit to be cleared
6998c2ecf20Sopenharmony_ci */
7008c2ecf20Sopenharmony_cistatic inline void ufshcd_utmrl_clear(struct ufs_hba *hba, u32 pos)
7018c2ecf20Sopenharmony_ci{
7028c2ecf20Sopenharmony_ci	if (hba->quirks & UFSHCI_QUIRK_BROKEN_REQ_LIST_CLR)
7038c2ecf20Sopenharmony_ci		ufshcd_writel(hba, (1 << pos), REG_UTP_TASK_REQ_LIST_CLEAR);
7048c2ecf20Sopenharmony_ci	else
7058c2ecf20Sopenharmony_ci		ufshcd_writel(hba, ~(1 << pos), REG_UTP_TASK_REQ_LIST_CLEAR);
7068c2ecf20Sopenharmony_ci}
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci/**
7098c2ecf20Sopenharmony_ci * ufshcd_outstanding_req_clear - Clear a bit in outstanding request field
7108c2ecf20Sopenharmony_ci * @hba: per adapter instance
7118c2ecf20Sopenharmony_ci * @tag: position of the bit to be cleared
7128c2ecf20Sopenharmony_ci */
7138c2ecf20Sopenharmony_cistatic inline void ufshcd_outstanding_req_clear(struct ufs_hba *hba, int tag)
7148c2ecf20Sopenharmony_ci{
7158c2ecf20Sopenharmony_ci	__clear_bit(tag, &hba->outstanding_reqs);
7168c2ecf20Sopenharmony_ci}
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci/**
7198c2ecf20Sopenharmony_ci * ufshcd_get_lists_status - Check UCRDY, UTRLRDY and UTMRLRDY
7208c2ecf20Sopenharmony_ci * @reg: Register value of host controller status
7218c2ecf20Sopenharmony_ci *
7228c2ecf20Sopenharmony_ci * Returns integer, 0 on Success and positive value if failed
7238c2ecf20Sopenharmony_ci */
7248c2ecf20Sopenharmony_cistatic inline int ufshcd_get_lists_status(u32 reg)
7258c2ecf20Sopenharmony_ci{
7268c2ecf20Sopenharmony_ci	return !((reg & UFSHCD_STATUS_READY) == UFSHCD_STATUS_READY);
7278c2ecf20Sopenharmony_ci}
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci/**
7308c2ecf20Sopenharmony_ci * ufshcd_get_uic_cmd_result - Get the UIC command result
7318c2ecf20Sopenharmony_ci * @hba: Pointer to adapter instance
7328c2ecf20Sopenharmony_ci *
7338c2ecf20Sopenharmony_ci * This function gets the result of UIC command completion
7348c2ecf20Sopenharmony_ci * Returns 0 on success, non zero value on error
7358c2ecf20Sopenharmony_ci */
7368c2ecf20Sopenharmony_cistatic inline int ufshcd_get_uic_cmd_result(struct ufs_hba *hba)
7378c2ecf20Sopenharmony_ci{
7388c2ecf20Sopenharmony_ci	return ufshcd_readl(hba, REG_UIC_COMMAND_ARG_2) &
7398c2ecf20Sopenharmony_ci	       MASK_UIC_COMMAND_RESULT;
7408c2ecf20Sopenharmony_ci}
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci/**
7438c2ecf20Sopenharmony_ci * ufshcd_get_dme_attr_val - Get the value of attribute returned by UIC command
7448c2ecf20Sopenharmony_ci * @hba: Pointer to adapter instance
7458c2ecf20Sopenharmony_ci *
7468c2ecf20Sopenharmony_ci * This function gets UIC command argument3
7478c2ecf20Sopenharmony_ci * Returns 0 on success, non zero value on error
7488c2ecf20Sopenharmony_ci */
7498c2ecf20Sopenharmony_cistatic inline u32 ufshcd_get_dme_attr_val(struct ufs_hba *hba)
7508c2ecf20Sopenharmony_ci{
7518c2ecf20Sopenharmony_ci	return ufshcd_readl(hba, REG_UIC_COMMAND_ARG_3);
7528c2ecf20Sopenharmony_ci}
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci/**
7558c2ecf20Sopenharmony_ci * ufshcd_get_req_rsp - returns the TR response transaction type
7568c2ecf20Sopenharmony_ci * @ucd_rsp_ptr: pointer to response UPIU
7578c2ecf20Sopenharmony_ci */
7588c2ecf20Sopenharmony_cistatic inline int
7598c2ecf20Sopenharmony_ciufshcd_get_req_rsp(struct utp_upiu_rsp *ucd_rsp_ptr)
7608c2ecf20Sopenharmony_ci{
7618c2ecf20Sopenharmony_ci	return be32_to_cpu(ucd_rsp_ptr->header.dword_0) >> 24;
7628c2ecf20Sopenharmony_ci}
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci/**
7658c2ecf20Sopenharmony_ci * ufshcd_get_rsp_upiu_result - Get the result from response UPIU
7668c2ecf20Sopenharmony_ci * @ucd_rsp_ptr: pointer to response UPIU
7678c2ecf20Sopenharmony_ci *
7688c2ecf20Sopenharmony_ci * This function gets the response status and scsi_status from response UPIU
7698c2ecf20Sopenharmony_ci * Returns the response result code.
7708c2ecf20Sopenharmony_ci */
7718c2ecf20Sopenharmony_cistatic inline int
7728c2ecf20Sopenharmony_ciufshcd_get_rsp_upiu_result(struct utp_upiu_rsp *ucd_rsp_ptr)
7738c2ecf20Sopenharmony_ci{
7748c2ecf20Sopenharmony_ci	return be32_to_cpu(ucd_rsp_ptr->header.dword_1) & MASK_RSP_UPIU_RESULT;
7758c2ecf20Sopenharmony_ci}
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci/*
7788c2ecf20Sopenharmony_ci * ufshcd_get_rsp_upiu_data_seg_len - Get the data segment length
7798c2ecf20Sopenharmony_ci *				from response UPIU
7808c2ecf20Sopenharmony_ci * @ucd_rsp_ptr: pointer to response UPIU
7818c2ecf20Sopenharmony_ci *
7828c2ecf20Sopenharmony_ci * Return the data segment length.
7838c2ecf20Sopenharmony_ci */
7848c2ecf20Sopenharmony_cistatic inline unsigned int
7858c2ecf20Sopenharmony_ciufshcd_get_rsp_upiu_data_seg_len(struct utp_upiu_rsp *ucd_rsp_ptr)
7868c2ecf20Sopenharmony_ci{
7878c2ecf20Sopenharmony_ci	return be32_to_cpu(ucd_rsp_ptr->header.dword_2) &
7888c2ecf20Sopenharmony_ci		MASK_RSP_UPIU_DATA_SEG_LEN;
7898c2ecf20Sopenharmony_ci}
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci/**
7928c2ecf20Sopenharmony_ci * ufshcd_is_exception_event - Check if the device raised an exception event
7938c2ecf20Sopenharmony_ci * @ucd_rsp_ptr: pointer to response UPIU
7948c2ecf20Sopenharmony_ci *
7958c2ecf20Sopenharmony_ci * The function checks if the device raised an exception event indicated in
7968c2ecf20Sopenharmony_ci * the Device Information field of response UPIU.
7978c2ecf20Sopenharmony_ci *
7988c2ecf20Sopenharmony_ci * Returns true if exception is raised, false otherwise.
7998c2ecf20Sopenharmony_ci */
8008c2ecf20Sopenharmony_cistatic inline bool ufshcd_is_exception_event(struct utp_upiu_rsp *ucd_rsp_ptr)
8018c2ecf20Sopenharmony_ci{
8028c2ecf20Sopenharmony_ci	return be32_to_cpu(ucd_rsp_ptr->header.dword_2) &
8038c2ecf20Sopenharmony_ci			MASK_RSP_EXCEPTION_EVENT ? true : false;
8048c2ecf20Sopenharmony_ci}
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci/**
8078c2ecf20Sopenharmony_ci * ufshcd_reset_intr_aggr - Reset interrupt aggregation values.
8088c2ecf20Sopenharmony_ci * @hba: per adapter instance
8098c2ecf20Sopenharmony_ci */
8108c2ecf20Sopenharmony_cistatic inline void
8118c2ecf20Sopenharmony_ciufshcd_reset_intr_aggr(struct ufs_hba *hba)
8128c2ecf20Sopenharmony_ci{
8138c2ecf20Sopenharmony_ci	ufshcd_writel(hba, INT_AGGR_ENABLE |
8148c2ecf20Sopenharmony_ci		      INT_AGGR_COUNTER_AND_TIMER_RESET,
8158c2ecf20Sopenharmony_ci		      REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
8168c2ecf20Sopenharmony_ci}
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci/**
8198c2ecf20Sopenharmony_ci * ufshcd_config_intr_aggr - Configure interrupt aggregation values.
8208c2ecf20Sopenharmony_ci * @hba: per adapter instance
8218c2ecf20Sopenharmony_ci * @cnt: Interrupt aggregation counter threshold
8228c2ecf20Sopenharmony_ci * @tmout: Interrupt aggregation timeout value
8238c2ecf20Sopenharmony_ci */
8248c2ecf20Sopenharmony_cistatic inline void
8258c2ecf20Sopenharmony_ciufshcd_config_intr_aggr(struct ufs_hba *hba, u8 cnt, u8 tmout)
8268c2ecf20Sopenharmony_ci{
8278c2ecf20Sopenharmony_ci	ufshcd_writel(hba, INT_AGGR_ENABLE | INT_AGGR_PARAM_WRITE |
8288c2ecf20Sopenharmony_ci		      INT_AGGR_COUNTER_THLD_VAL(cnt) |
8298c2ecf20Sopenharmony_ci		      INT_AGGR_TIMEOUT_VAL(tmout),
8308c2ecf20Sopenharmony_ci		      REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
8318c2ecf20Sopenharmony_ci}
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci/**
8348c2ecf20Sopenharmony_ci * ufshcd_disable_intr_aggr - Disables interrupt aggregation.
8358c2ecf20Sopenharmony_ci * @hba: per adapter instance
8368c2ecf20Sopenharmony_ci */
8378c2ecf20Sopenharmony_cistatic inline void ufshcd_disable_intr_aggr(struct ufs_hba *hba)
8388c2ecf20Sopenharmony_ci{
8398c2ecf20Sopenharmony_ci	ufshcd_writel(hba, 0, REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
8408c2ecf20Sopenharmony_ci}
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci/**
8438c2ecf20Sopenharmony_ci * ufshcd_enable_run_stop_reg - Enable run-stop registers,
8448c2ecf20Sopenharmony_ci *			When run-stop registers are set to 1, it indicates the
8458c2ecf20Sopenharmony_ci *			host controller that it can process the requests
8468c2ecf20Sopenharmony_ci * @hba: per adapter instance
8478c2ecf20Sopenharmony_ci */
8488c2ecf20Sopenharmony_cistatic void ufshcd_enable_run_stop_reg(struct ufs_hba *hba)
8498c2ecf20Sopenharmony_ci{
8508c2ecf20Sopenharmony_ci	ufshcd_writel(hba, UTP_TASK_REQ_LIST_RUN_STOP_BIT,
8518c2ecf20Sopenharmony_ci		      REG_UTP_TASK_REQ_LIST_RUN_STOP);
8528c2ecf20Sopenharmony_ci	ufshcd_writel(hba, UTP_TRANSFER_REQ_LIST_RUN_STOP_BIT,
8538c2ecf20Sopenharmony_ci		      REG_UTP_TRANSFER_REQ_LIST_RUN_STOP);
8548c2ecf20Sopenharmony_ci}
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci/**
8578c2ecf20Sopenharmony_ci * ufshcd_hba_start - Start controller initialization sequence
8588c2ecf20Sopenharmony_ci * @hba: per adapter instance
8598c2ecf20Sopenharmony_ci */
8608c2ecf20Sopenharmony_cistatic inline void ufshcd_hba_start(struct ufs_hba *hba)
8618c2ecf20Sopenharmony_ci{
8628c2ecf20Sopenharmony_ci	u32 val = CONTROLLER_ENABLE;
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci	if (ufshcd_crypto_enable(hba))
8658c2ecf20Sopenharmony_ci		val |= CRYPTO_GENERAL_ENABLE;
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	ufshcd_writel(hba, val, REG_CONTROLLER_ENABLE);
8688c2ecf20Sopenharmony_ci}
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci/**
8718c2ecf20Sopenharmony_ci * ufshcd_is_hba_active - Get controller state
8728c2ecf20Sopenharmony_ci * @hba: per adapter instance
8738c2ecf20Sopenharmony_ci *
8748c2ecf20Sopenharmony_ci * Returns false if controller is active, true otherwise
8758c2ecf20Sopenharmony_ci */
8768c2ecf20Sopenharmony_cistatic inline bool ufshcd_is_hba_active(struct ufs_hba *hba)
8778c2ecf20Sopenharmony_ci{
8788c2ecf20Sopenharmony_ci	return (ufshcd_readl(hba, REG_CONTROLLER_ENABLE) & CONTROLLER_ENABLE)
8798c2ecf20Sopenharmony_ci		? false : true;
8808c2ecf20Sopenharmony_ci}
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ciu32 ufshcd_get_local_unipro_ver(struct ufs_hba *hba)
8838c2ecf20Sopenharmony_ci{
8848c2ecf20Sopenharmony_ci	/* HCI version 1.0 and 1.1 supports UniPro 1.41 */
8858c2ecf20Sopenharmony_ci	if ((hba->ufs_version == UFSHCI_VERSION_10) ||
8868c2ecf20Sopenharmony_ci	    (hba->ufs_version == UFSHCI_VERSION_11))
8878c2ecf20Sopenharmony_ci		return UFS_UNIPRO_VER_1_41;
8888c2ecf20Sopenharmony_ci	else
8898c2ecf20Sopenharmony_ci		return UFS_UNIPRO_VER_1_6;
8908c2ecf20Sopenharmony_ci}
8918c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ufshcd_get_local_unipro_ver);
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_cistatic bool ufshcd_is_unipro_pa_params_tuning_req(struct ufs_hba *hba)
8948c2ecf20Sopenharmony_ci{
8958c2ecf20Sopenharmony_ci	/*
8968c2ecf20Sopenharmony_ci	 * If both host and device support UniPro ver1.6 or later, PA layer
8978c2ecf20Sopenharmony_ci	 * parameters tuning happens during link startup itself.
8988c2ecf20Sopenharmony_ci	 *
8998c2ecf20Sopenharmony_ci	 * We can manually tune PA layer parameters if either host or device
9008c2ecf20Sopenharmony_ci	 * doesn't support UniPro ver 1.6 or later. But to keep manual tuning
9018c2ecf20Sopenharmony_ci	 * logic simple, we will only do manual tuning if local unipro version
9028c2ecf20Sopenharmony_ci	 * doesn't support ver1.6 or later.
9038c2ecf20Sopenharmony_ci	 */
9048c2ecf20Sopenharmony_ci	if (ufshcd_get_local_unipro_ver(hba) < UFS_UNIPRO_VER_1_6)
9058c2ecf20Sopenharmony_ci		return true;
9068c2ecf20Sopenharmony_ci	else
9078c2ecf20Sopenharmony_ci		return false;
9088c2ecf20Sopenharmony_ci}
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci/**
9118c2ecf20Sopenharmony_ci * ufshcd_set_clk_freq - set UFS controller clock frequencies
9128c2ecf20Sopenharmony_ci * @hba: per adapter instance
9138c2ecf20Sopenharmony_ci * @scale_up: If True, set max possible frequency othewise set low frequency
9148c2ecf20Sopenharmony_ci *
9158c2ecf20Sopenharmony_ci * Returns 0 if successful
9168c2ecf20Sopenharmony_ci * Returns < 0 for any other errors
9178c2ecf20Sopenharmony_ci */
9188c2ecf20Sopenharmony_cistatic int ufshcd_set_clk_freq(struct ufs_hba *hba, bool scale_up)
9198c2ecf20Sopenharmony_ci{
9208c2ecf20Sopenharmony_ci	int ret = 0;
9218c2ecf20Sopenharmony_ci	struct ufs_clk_info *clki;
9228c2ecf20Sopenharmony_ci	struct list_head *head = &hba->clk_list_head;
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	if (list_empty(head))
9258c2ecf20Sopenharmony_ci		goto out;
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci	list_for_each_entry(clki, head, list) {
9288c2ecf20Sopenharmony_ci		if (!IS_ERR_OR_NULL(clki->clk)) {
9298c2ecf20Sopenharmony_ci			if (scale_up && clki->max_freq) {
9308c2ecf20Sopenharmony_ci				if (clki->curr_freq == clki->max_freq)
9318c2ecf20Sopenharmony_ci					continue;
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci				ret = clk_set_rate(clki->clk, clki->max_freq);
9348c2ecf20Sopenharmony_ci				if (ret) {
9358c2ecf20Sopenharmony_ci					dev_err(hba->dev, "%s: %s clk set rate(%dHz) failed, %d\n",
9368c2ecf20Sopenharmony_ci						__func__, clki->name,
9378c2ecf20Sopenharmony_ci						clki->max_freq, ret);
9388c2ecf20Sopenharmony_ci					break;
9398c2ecf20Sopenharmony_ci				}
9408c2ecf20Sopenharmony_ci				trace_ufshcd_clk_scaling(dev_name(hba->dev),
9418c2ecf20Sopenharmony_ci						"scaled up", clki->name,
9428c2ecf20Sopenharmony_ci						clki->curr_freq,
9438c2ecf20Sopenharmony_ci						clki->max_freq);
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci				clki->curr_freq = clki->max_freq;
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci			} else if (!scale_up && clki->min_freq) {
9488c2ecf20Sopenharmony_ci				if (clki->curr_freq == clki->min_freq)
9498c2ecf20Sopenharmony_ci					continue;
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci				ret = clk_set_rate(clki->clk, clki->min_freq);
9528c2ecf20Sopenharmony_ci				if (ret) {
9538c2ecf20Sopenharmony_ci					dev_err(hba->dev, "%s: %s clk set rate(%dHz) failed, %d\n",
9548c2ecf20Sopenharmony_ci						__func__, clki->name,
9558c2ecf20Sopenharmony_ci						clki->min_freq, ret);
9568c2ecf20Sopenharmony_ci					break;
9578c2ecf20Sopenharmony_ci				}
9588c2ecf20Sopenharmony_ci				trace_ufshcd_clk_scaling(dev_name(hba->dev),
9598c2ecf20Sopenharmony_ci						"scaled down", clki->name,
9608c2ecf20Sopenharmony_ci						clki->curr_freq,
9618c2ecf20Sopenharmony_ci						clki->min_freq);
9628c2ecf20Sopenharmony_ci				clki->curr_freq = clki->min_freq;
9638c2ecf20Sopenharmony_ci			}
9648c2ecf20Sopenharmony_ci		}
9658c2ecf20Sopenharmony_ci		dev_dbg(hba->dev, "%s: clk: %s, rate: %lu\n", __func__,
9668c2ecf20Sopenharmony_ci				clki->name, clk_get_rate(clki->clk));
9678c2ecf20Sopenharmony_ci	}
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ciout:
9708c2ecf20Sopenharmony_ci	return ret;
9718c2ecf20Sopenharmony_ci}
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci/**
9748c2ecf20Sopenharmony_ci * ufshcd_scale_clks - scale up or scale down UFS controller clocks
9758c2ecf20Sopenharmony_ci * @hba: per adapter instance
9768c2ecf20Sopenharmony_ci * @scale_up: True if scaling up and false if scaling down
9778c2ecf20Sopenharmony_ci *
9788c2ecf20Sopenharmony_ci * Returns 0 if successful
9798c2ecf20Sopenharmony_ci * Returns < 0 for any other errors
9808c2ecf20Sopenharmony_ci */
9818c2ecf20Sopenharmony_cistatic int ufshcd_scale_clks(struct ufs_hba *hba, bool scale_up)
9828c2ecf20Sopenharmony_ci{
9838c2ecf20Sopenharmony_ci	int ret = 0;
9848c2ecf20Sopenharmony_ci	ktime_t start = ktime_get();
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	ret = ufshcd_vops_clk_scale_notify(hba, scale_up, PRE_CHANGE);
9878c2ecf20Sopenharmony_ci	if (ret)
9888c2ecf20Sopenharmony_ci		goto out;
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci	ret = ufshcd_set_clk_freq(hba, scale_up);
9918c2ecf20Sopenharmony_ci	if (ret)
9928c2ecf20Sopenharmony_ci		goto out;
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci	ret = ufshcd_vops_clk_scale_notify(hba, scale_up, POST_CHANGE);
9958c2ecf20Sopenharmony_ci	if (ret)
9968c2ecf20Sopenharmony_ci		ufshcd_set_clk_freq(hba, !scale_up);
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ciout:
9998c2ecf20Sopenharmony_ci	trace_ufshcd_profile_clk_scaling(dev_name(hba->dev),
10008c2ecf20Sopenharmony_ci			(scale_up ? "up" : "down"),
10018c2ecf20Sopenharmony_ci			ktime_to_us(ktime_sub(ktime_get(), start)), ret);
10028c2ecf20Sopenharmony_ci	return ret;
10038c2ecf20Sopenharmony_ci}
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci/**
10068c2ecf20Sopenharmony_ci * ufshcd_is_devfreq_scaling_required - check if scaling is required or not
10078c2ecf20Sopenharmony_ci * @hba: per adapter instance
10088c2ecf20Sopenharmony_ci * @scale_up: True if scaling up and false if scaling down
10098c2ecf20Sopenharmony_ci *
10108c2ecf20Sopenharmony_ci * Returns true if scaling is required, false otherwise.
10118c2ecf20Sopenharmony_ci */
10128c2ecf20Sopenharmony_cistatic bool ufshcd_is_devfreq_scaling_required(struct ufs_hba *hba,
10138c2ecf20Sopenharmony_ci					       bool scale_up)
10148c2ecf20Sopenharmony_ci{
10158c2ecf20Sopenharmony_ci	struct ufs_clk_info *clki;
10168c2ecf20Sopenharmony_ci	struct list_head *head = &hba->clk_list_head;
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci	if (list_empty(head))
10198c2ecf20Sopenharmony_ci		return false;
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci	list_for_each_entry(clki, head, list) {
10228c2ecf20Sopenharmony_ci		if (!IS_ERR_OR_NULL(clki->clk)) {
10238c2ecf20Sopenharmony_ci			if (scale_up && clki->max_freq) {
10248c2ecf20Sopenharmony_ci				if (clki->curr_freq == clki->max_freq)
10258c2ecf20Sopenharmony_ci					continue;
10268c2ecf20Sopenharmony_ci				return true;
10278c2ecf20Sopenharmony_ci			} else if (!scale_up && clki->min_freq) {
10288c2ecf20Sopenharmony_ci				if (clki->curr_freq == clki->min_freq)
10298c2ecf20Sopenharmony_ci					continue;
10308c2ecf20Sopenharmony_ci				return true;
10318c2ecf20Sopenharmony_ci			}
10328c2ecf20Sopenharmony_ci		}
10338c2ecf20Sopenharmony_ci	}
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci	return false;
10368c2ecf20Sopenharmony_ci}
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_cistatic int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba,
10398c2ecf20Sopenharmony_ci					u64 wait_timeout_us)
10408c2ecf20Sopenharmony_ci{
10418c2ecf20Sopenharmony_ci	unsigned long flags;
10428c2ecf20Sopenharmony_ci	int ret = 0;
10438c2ecf20Sopenharmony_ci	u32 tm_doorbell;
10448c2ecf20Sopenharmony_ci	u32 tr_doorbell;
10458c2ecf20Sopenharmony_ci	bool timeout = false, do_last_check = false;
10468c2ecf20Sopenharmony_ci	ktime_t start;
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci	ufshcd_hold(hba, false);
10498c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
10508c2ecf20Sopenharmony_ci	/*
10518c2ecf20Sopenharmony_ci	 * Wait for all the outstanding tasks/transfer requests.
10528c2ecf20Sopenharmony_ci	 * Verify by checking the doorbell registers are clear.
10538c2ecf20Sopenharmony_ci	 */
10548c2ecf20Sopenharmony_ci	start = ktime_get();
10558c2ecf20Sopenharmony_ci	do {
10568c2ecf20Sopenharmony_ci		if (hba->ufshcd_state != UFSHCD_STATE_OPERATIONAL) {
10578c2ecf20Sopenharmony_ci			ret = -EBUSY;
10588c2ecf20Sopenharmony_ci			goto out;
10598c2ecf20Sopenharmony_ci		}
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci		tm_doorbell = ufshcd_readl(hba, REG_UTP_TASK_REQ_DOOR_BELL);
10628c2ecf20Sopenharmony_ci		tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
10638c2ecf20Sopenharmony_ci		if (!tm_doorbell && !tr_doorbell) {
10648c2ecf20Sopenharmony_ci			timeout = false;
10658c2ecf20Sopenharmony_ci			break;
10668c2ecf20Sopenharmony_ci		} else if (do_last_check) {
10678c2ecf20Sopenharmony_ci			break;
10688c2ecf20Sopenharmony_ci		}
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(hba->host->host_lock, flags);
10718c2ecf20Sopenharmony_ci		schedule();
10728c2ecf20Sopenharmony_ci		if (ktime_to_us(ktime_sub(ktime_get(), start)) >
10738c2ecf20Sopenharmony_ci		    wait_timeout_us) {
10748c2ecf20Sopenharmony_ci			timeout = true;
10758c2ecf20Sopenharmony_ci			/*
10768c2ecf20Sopenharmony_ci			 * We might have scheduled out for long time so make
10778c2ecf20Sopenharmony_ci			 * sure to check if doorbells are cleared by this time
10788c2ecf20Sopenharmony_ci			 * or not.
10798c2ecf20Sopenharmony_ci			 */
10808c2ecf20Sopenharmony_ci			do_last_check = true;
10818c2ecf20Sopenharmony_ci		}
10828c2ecf20Sopenharmony_ci		spin_lock_irqsave(hba->host->host_lock, flags);
10838c2ecf20Sopenharmony_ci	} while (tm_doorbell || tr_doorbell);
10848c2ecf20Sopenharmony_ci
10858c2ecf20Sopenharmony_ci	if (timeout) {
10868c2ecf20Sopenharmony_ci		dev_err(hba->dev,
10878c2ecf20Sopenharmony_ci			"%s: timedout waiting for doorbell to clear (tm=0x%x, tr=0x%x)\n",
10888c2ecf20Sopenharmony_ci			__func__, tm_doorbell, tr_doorbell);
10898c2ecf20Sopenharmony_ci		ret = -EBUSY;
10908c2ecf20Sopenharmony_ci	}
10918c2ecf20Sopenharmony_ciout:
10928c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
10938c2ecf20Sopenharmony_ci	ufshcd_release(hba);
10948c2ecf20Sopenharmony_ci	return ret;
10958c2ecf20Sopenharmony_ci}
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci/**
10988c2ecf20Sopenharmony_ci * ufshcd_scale_gear - scale up/down UFS gear
10998c2ecf20Sopenharmony_ci * @hba: per adapter instance
11008c2ecf20Sopenharmony_ci * @scale_up: True for scaling up gear and false for scaling down
11018c2ecf20Sopenharmony_ci *
11028c2ecf20Sopenharmony_ci * Returns 0 for success,
11038c2ecf20Sopenharmony_ci * Returns -EBUSY if scaling can't happen at this time
11048c2ecf20Sopenharmony_ci * Returns non-zero for any other errors
11058c2ecf20Sopenharmony_ci */
11068c2ecf20Sopenharmony_cistatic int ufshcd_scale_gear(struct ufs_hba *hba, bool scale_up)
11078c2ecf20Sopenharmony_ci{
11088c2ecf20Sopenharmony_ci	#define UFS_MIN_GEAR_TO_SCALE_DOWN	UFS_HS_G1
11098c2ecf20Sopenharmony_ci	int ret = 0;
11108c2ecf20Sopenharmony_ci	struct ufs_pa_layer_attr new_pwr_info;
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_ci	if (scale_up) {
11138c2ecf20Sopenharmony_ci		memcpy(&new_pwr_info, &hba->clk_scaling.saved_pwr_info.info,
11148c2ecf20Sopenharmony_ci		       sizeof(struct ufs_pa_layer_attr));
11158c2ecf20Sopenharmony_ci	} else {
11168c2ecf20Sopenharmony_ci		memcpy(&new_pwr_info, &hba->pwr_info,
11178c2ecf20Sopenharmony_ci		       sizeof(struct ufs_pa_layer_attr));
11188c2ecf20Sopenharmony_ci
11198c2ecf20Sopenharmony_ci		if (hba->pwr_info.gear_tx > UFS_MIN_GEAR_TO_SCALE_DOWN
11208c2ecf20Sopenharmony_ci		    || hba->pwr_info.gear_rx > UFS_MIN_GEAR_TO_SCALE_DOWN) {
11218c2ecf20Sopenharmony_ci			/* save the current power mode */
11228c2ecf20Sopenharmony_ci			memcpy(&hba->clk_scaling.saved_pwr_info.info,
11238c2ecf20Sopenharmony_ci				&hba->pwr_info,
11248c2ecf20Sopenharmony_ci				sizeof(struct ufs_pa_layer_attr));
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ci			/* scale down gear */
11278c2ecf20Sopenharmony_ci			new_pwr_info.gear_tx = UFS_MIN_GEAR_TO_SCALE_DOWN;
11288c2ecf20Sopenharmony_ci			new_pwr_info.gear_rx = UFS_MIN_GEAR_TO_SCALE_DOWN;
11298c2ecf20Sopenharmony_ci		}
11308c2ecf20Sopenharmony_ci	}
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_ci	/* check if the power mode needs to be changed or not? */
11338c2ecf20Sopenharmony_ci	ret = ufshcd_config_pwr_mode(hba, &new_pwr_info);
11348c2ecf20Sopenharmony_ci	if (ret)
11358c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: failed err %d, old gear: (tx %d rx %d), new gear: (tx %d rx %d)",
11368c2ecf20Sopenharmony_ci			__func__, ret,
11378c2ecf20Sopenharmony_ci			hba->pwr_info.gear_tx, hba->pwr_info.gear_rx,
11388c2ecf20Sopenharmony_ci			new_pwr_info.gear_tx, new_pwr_info.gear_rx);
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci	return ret;
11418c2ecf20Sopenharmony_ci}
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_cistatic int ufshcd_clock_scaling_prepare(struct ufs_hba *hba)
11448c2ecf20Sopenharmony_ci{
11458c2ecf20Sopenharmony_ci	#define DOORBELL_CLR_TOUT_US		(1000 * 1000) /* 1 sec */
11468c2ecf20Sopenharmony_ci	int ret = 0;
11478c2ecf20Sopenharmony_ci	/*
11488c2ecf20Sopenharmony_ci	 * make sure that there are no outstanding requests when
11498c2ecf20Sopenharmony_ci	 * clock scaling is in progress
11508c2ecf20Sopenharmony_ci	 */
11518c2ecf20Sopenharmony_ci	ufshcd_scsi_block_requests(hba);
11528c2ecf20Sopenharmony_ci	down_write(&hba->clk_scaling_lock);
11538c2ecf20Sopenharmony_ci	if (ufshcd_wait_for_doorbell_clr(hba, DOORBELL_CLR_TOUT_US)) {
11548c2ecf20Sopenharmony_ci		ret = -EBUSY;
11558c2ecf20Sopenharmony_ci		up_write(&hba->clk_scaling_lock);
11568c2ecf20Sopenharmony_ci		ufshcd_scsi_unblock_requests(hba);
11578c2ecf20Sopenharmony_ci	}
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci	return ret;
11608c2ecf20Sopenharmony_ci}
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_cistatic void ufshcd_clock_scaling_unprepare(struct ufs_hba *hba)
11638c2ecf20Sopenharmony_ci{
11648c2ecf20Sopenharmony_ci	up_write(&hba->clk_scaling_lock);
11658c2ecf20Sopenharmony_ci	ufshcd_scsi_unblock_requests(hba);
11668c2ecf20Sopenharmony_ci}
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci/**
11698c2ecf20Sopenharmony_ci * ufshcd_devfreq_scale - scale up/down UFS clocks and gear
11708c2ecf20Sopenharmony_ci * @hba: per adapter instance
11718c2ecf20Sopenharmony_ci * @scale_up: True for scaling up and false for scalin down
11728c2ecf20Sopenharmony_ci *
11738c2ecf20Sopenharmony_ci * Returns 0 for success,
11748c2ecf20Sopenharmony_ci * Returns -EBUSY if scaling can't happen at this time
11758c2ecf20Sopenharmony_ci * Returns non-zero for any other errors
11768c2ecf20Sopenharmony_ci */
11778c2ecf20Sopenharmony_cistatic int ufshcd_devfreq_scale(struct ufs_hba *hba, bool scale_up)
11788c2ecf20Sopenharmony_ci{
11798c2ecf20Sopenharmony_ci	int ret = 0;
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci	/* let's not get into low power until clock scaling is completed */
11828c2ecf20Sopenharmony_ci	ufshcd_hold(hba, false);
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci	ret = ufshcd_clock_scaling_prepare(hba);
11858c2ecf20Sopenharmony_ci	if (ret)
11868c2ecf20Sopenharmony_ci		goto out;
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_ci	/* scale down the gear before scaling down clocks */
11898c2ecf20Sopenharmony_ci	if (!scale_up) {
11908c2ecf20Sopenharmony_ci		ret = ufshcd_scale_gear(hba, false);
11918c2ecf20Sopenharmony_ci		if (ret)
11928c2ecf20Sopenharmony_ci			goto out_unprepare;
11938c2ecf20Sopenharmony_ci	}
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_ci	ret = ufshcd_scale_clks(hba, scale_up);
11968c2ecf20Sopenharmony_ci	if (ret) {
11978c2ecf20Sopenharmony_ci		if (!scale_up)
11988c2ecf20Sopenharmony_ci			ufshcd_scale_gear(hba, true);
11998c2ecf20Sopenharmony_ci		goto out_unprepare;
12008c2ecf20Sopenharmony_ci	}
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_ci	/* scale up the gear after scaling up clocks */
12038c2ecf20Sopenharmony_ci	if (scale_up) {
12048c2ecf20Sopenharmony_ci		ret = ufshcd_scale_gear(hba, true);
12058c2ecf20Sopenharmony_ci		if (ret) {
12068c2ecf20Sopenharmony_ci			ufshcd_scale_clks(hba, false);
12078c2ecf20Sopenharmony_ci			goto out_unprepare;
12088c2ecf20Sopenharmony_ci		}
12098c2ecf20Sopenharmony_ci	}
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	/* Enable Write Booster if we have scaled up else disable it */
12128c2ecf20Sopenharmony_ci	up_write(&hba->clk_scaling_lock);
12138c2ecf20Sopenharmony_ci	ufshcd_wb_ctrl(hba, scale_up);
12148c2ecf20Sopenharmony_ci	down_write(&hba->clk_scaling_lock);
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ciout_unprepare:
12178c2ecf20Sopenharmony_ci	ufshcd_clock_scaling_unprepare(hba);
12188c2ecf20Sopenharmony_ciout:
12198c2ecf20Sopenharmony_ci	ufshcd_release(hba);
12208c2ecf20Sopenharmony_ci	return ret;
12218c2ecf20Sopenharmony_ci}
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_cistatic void ufshcd_clk_scaling_suspend_work(struct work_struct *work)
12248c2ecf20Sopenharmony_ci{
12258c2ecf20Sopenharmony_ci	struct ufs_hba *hba = container_of(work, struct ufs_hba,
12268c2ecf20Sopenharmony_ci					   clk_scaling.suspend_work);
12278c2ecf20Sopenharmony_ci	unsigned long irq_flags;
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, irq_flags);
12308c2ecf20Sopenharmony_ci	if (hba->clk_scaling.active_reqs || hba->clk_scaling.is_suspended) {
12318c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(hba->host->host_lock, irq_flags);
12328c2ecf20Sopenharmony_ci		return;
12338c2ecf20Sopenharmony_ci	}
12348c2ecf20Sopenharmony_ci	hba->clk_scaling.is_suspended = true;
12358c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, irq_flags);
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_ci	__ufshcd_suspend_clkscaling(hba);
12388c2ecf20Sopenharmony_ci}
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_cistatic void ufshcd_clk_scaling_resume_work(struct work_struct *work)
12418c2ecf20Sopenharmony_ci{
12428c2ecf20Sopenharmony_ci	struct ufs_hba *hba = container_of(work, struct ufs_hba,
12438c2ecf20Sopenharmony_ci					   clk_scaling.resume_work);
12448c2ecf20Sopenharmony_ci	unsigned long irq_flags;
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, irq_flags);
12478c2ecf20Sopenharmony_ci	if (!hba->clk_scaling.is_suspended) {
12488c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(hba->host->host_lock, irq_flags);
12498c2ecf20Sopenharmony_ci		return;
12508c2ecf20Sopenharmony_ci	}
12518c2ecf20Sopenharmony_ci	hba->clk_scaling.is_suspended = false;
12528c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, irq_flags);
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_ci	devfreq_resume_device(hba->devfreq);
12558c2ecf20Sopenharmony_ci}
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_cistatic int ufshcd_devfreq_target(struct device *dev,
12588c2ecf20Sopenharmony_ci				unsigned long *freq, u32 flags)
12598c2ecf20Sopenharmony_ci{
12608c2ecf20Sopenharmony_ci	int ret = 0;
12618c2ecf20Sopenharmony_ci	struct ufs_hba *hba = dev_get_drvdata(dev);
12628c2ecf20Sopenharmony_ci	ktime_t start;
12638c2ecf20Sopenharmony_ci	bool scale_up, sched_clk_scaling_suspend_work = false;
12648c2ecf20Sopenharmony_ci	struct list_head *clk_list = &hba->clk_list_head;
12658c2ecf20Sopenharmony_ci	struct ufs_clk_info *clki;
12668c2ecf20Sopenharmony_ci	unsigned long irq_flags;
12678c2ecf20Sopenharmony_ci
12688c2ecf20Sopenharmony_ci	if (!ufshcd_is_clkscaling_supported(hba))
12698c2ecf20Sopenharmony_ci		return -EINVAL;
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_ci	clki = list_first_entry(&hba->clk_list_head, struct ufs_clk_info, list);
12728c2ecf20Sopenharmony_ci	/* Override with the closest supported frequency */
12738c2ecf20Sopenharmony_ci	*freq = (unsigned long) clk_round_rate(clki->clk, *freq);
12748c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, irq_flags);
12758c2ecf20Sopenharmony_ci	if (ufshcd_eh_in_progress(hba)) {
12768c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(hba->host->host_lock, irq_flags);
12778c2ecf20Sopenharmony_ci		return 0;
12788c2ecf20Sopenharmony_ci	}
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ci	if (!hba->clk_scaling.active_reqs)
12818c2ecf20Sopenharmony_ci		sched_clk_scaling_suspend_work = true;
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci	if (list_empty(clk_list)) {
12848c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(hba->host->host_lock, irq_flags);
12858c2ecf20Sopenharmony_ci		goto out;
12868c2ecf20Sopenharmony_ci	}
12878c2ecf20Sopenharmony_ci
12888c2ecf20Sopenharmony_ci	/* Decide based on the rounded-off frequency and update */
12898c2ecf20Sopenharmony_ci	scale_up = (*freq == clki->max_freq) ? true : false;
12908c2ecf20Sopenharmony_ci	if (!scale_up)
12918c2ecf20Sopenharmony_ci		*freq = clki->min_freq;
12928c2ecf20Sopenharmony_ci	/* Update the frequency */
12938c2ecf20Sopenharmony_ci	if (!ufshcd_is_devfreq_scaling_required(hba, scale_up)) {
12948c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(hba->host->host_lock, irq_flags);
12958c2ecf20Sopenharmony_ci		ret = 0;
12968c2ecf20Sopenharmony_ci		goto out; /* no state change required */
12978c2ecf20Sopenharmony_ci	}
12988c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, irq_flags);
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_ci	pm_runtime_get_noresume(hba->dev);
13018c2ecf20Sopenharmony_ci	if (!pm_runtime_active(hba->dev)) {
13028c2ecf20Sopenharmony_ci		pm_runtime_put_noidle(hba->dev);
13038c2ecf20Sopenharmony_ci		ret = -EAGAIN;
13048c2ecf20Sopenharmony_ci		goto out;
13058c2ecf20Sopenharmony_ci	}
13068c2ecf20Sopenharmony_ci	start = ktime_get();
13078c2ecf20Sopenharmony_ci	ret = ufshcd_devfreq_scale(hba, scale_up);
13088c2ecf20Sopenharmony_ci	pm_runtime_put(hba->dev);
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_ci	trace_ufshcd_profile_clk_scaling(dev_name(hba->dev),
13118c2ecf20Sopenharmony_ci		(scale_up ? "up" : "down"),
13128c2ecf20Sopenharmony_ci		ktime_to_us(ktime_sub(ktime_get(), start)), ret);
13138c2ecf20Sopenharmony_ci
13148c2ecf20Sopenharmony_ciout:
13158c2ecf20Sopenharmony_ci	if (sched_clk_scaling_suspend_work)
13168c2ecf20Sopenharmony_ci		queue_work(hba->clk_scaling.workq,
13178c2ecf20Sopenharmony_ci			   &hba->clk_scaling.suspend_work);
13188c2ecf20Sopenharmony_ci
13198c2ecf20Sopenharmony_ci	return ret;
13208c2ecf20Sopenharmony_ci}
13218c2ecf20Sopenharmony_ci
13228c2ecf20Sopenharmony_cistatic bool ufshcd_is_busy(struct request *req, void *priv, bool reserved)
13238c2ecf20Sopenharmony_ci{
13248c2ecf20Sopenharmony_ci	int *busy = priv;
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_ci	WARN_ON_ONCE(reserved);
13278c2ecf20Sopenharmony_ci	(*busy)++;
13288c2ecf20Sopenharmony_ci	return false;
13298c2ecf20Sopenharmony_ci}
13308c2ecf20Sopenharmony_ci
13318c2ecf20Sopenharmony_ci/* Whether or not any tag is in use by a request that is in progress. */
13328c2ecf20Sopenharmony_cistatic bool ufshcd_any_tag_in_use(struct ufs_hba *hba)
13338c2ecf20Sopenharmony_ci{
13348c2ecf20Sopenharmony_ci	struct request_queue *q = hba->cmd_queue;
13358c2ecf20Sopenharmony_ci	int busy = 0;
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ci	blk_mq_tagset_busy_iter(q->tag_set, ufshcd_is_busy, &busy);
13388c2ecf20Sopenharmony_ci	return busy;
13398c2ecf20Sopenharmony_ci}
13408c2ecf20Sopenharmony_ci
13418c2ecf20Sopenharmony_cistatic int ufshcd_devfreq_get_dev_status(struct device *dev,
13428c2ecf20Sopenharmony_ci		struct devfreq_dev_status *stat)
13438c2ecf20Sopenharmony_ci{
13448c2ecf20Sopenharmony_ci	struct ufs_hba *hba = dev_get_drvdata(dev);
13458c2ecf20Sopenharmony_ci	struct ufs_clk_scaling *scaling = &hba->clk_scaling;
13468c2ecf20Sopenharmony_ci	unsigned long flags;
13478c2ecf20Sopenharmony_ci	struct list_head *clk_list = &hba->clk_list_head;
13488c2ecf20Sopenharmony_ci	struct ufs_clk_info *clki;
13498c2ecf20Sopenharmony_ci	ktime_t curr_t;
13508c2ecf20Sopenharmony_ci
13518c2ecf20Sopenharmony_ci	if (!ufshcd_is_clkscaling_supported(hba))
13528c2ecf20Sopenharmony_ci		return -EINVAL;
13538c2ecf20Sopenharmony_ci
13548c2ecf20Sopenharmony_ci	memset(stat, 0, sizeof(*stat));
13558c2ecf20Sopenharmony_ci
13568c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
13578c2ecf20Sopenharmony_ci	curr_t = ktime_get();
13588c2ecf20Sopenharmony_ci	if (!scaling->window_start_t)
13598c2ecf20Sopenharmony_ci		goto start_window;
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_ci	clki = list_first_entry(clk_list, struct ufs_clk_info, list);
13628c2ecf20Sopenharmony_ci	/*
13638c2ecf20Sopenharmony_ci	 * If current frequency is 0, then the ondemand governor considers
13648c2ecf20Sopenharmony_ci	 * there's no initial frequency set. And it always requests to set
13658c2ecf20Sopenharmony_ci	 * to max. frequency.
13668c2ecf20Sopenharmony_ci	 */
13678c2ecf20Sopenharmony_ci	stat->current_frequency = clki->curr_freq;
13688c2ecf20Sopenharmony_ci	if (scaling->is_busy_started)
13698c2ecf20Sopenharmony_ci		scaling->tot_busy_t += ktime_us_delta(curr_t,
13708c2ecf20Sopenharmony_ci				scaling->busy_start_t);
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_ci	stat->total_time = ktime_us_delta(curr_t, scaling->window_start_t);
13738c2ecf20Sopenharmony_ci	stat->busy_time = scaling->tot_busy_t;
13748c2ecf20Sopenharmony_cistart_window:
13758c2ecf20Sopenharmony_ci	scaling->window_start_t = curr_t;
13768c2ecf20Sopenharmony_ci	scaling->tot_busy_t = 0;
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_ci	if (hba->outstanding_reqs) {
13798c2ecf20Sopenharmony_ci		scaling->busy_start_t = curr_t;
13808c2ecf20Sopenharmony_ci		scaling->is_busy_started = true;
13818c2ecf20Sopenharmony_ci	} else {
13828c2ecf20Sopenharmony_ci		scaling->busy_start_t = 0;
13838c2ecf20Sopenharmony_ci		scaling->is_busy_started = false;
13848c2ecf20Sopenharmony_ci	}
13858c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
13868c2ecf20Sopenharmony_ci	return 0;
13878c2ecf20Sopenharmony_ci}
13888c2ecf20Sopenharmony_ci
13898c2ecf20Sopenharmony_cistatic int ufshcd_devfreq_init(struct ufs_hba *hba)
13908c2ecf20Sopenharmony_ci{
13918c2ecf20Sopenharmony_ci	struct list_head *clk_list = &hba->clk_list_head;
13928c2ecf20Sopenharmony_ci	struct ufs_clk_info *clki;
13938c2ecf20Sopenharmony_ci	struct devfreq *devfreq;
13948c2ecf20Sopenharmony_ci	int ret;
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci	/* Skip devfreq if we don't have any clocks in the list */
13978c2ecf20Sopenharmony_ci	if (list_empty(clk_list))
13988c2ecf20Sopenharmony_ci		return 0;
13998c2ecf20Sopenharmony_ci
14008c2ecf20Sopenharmony_ci	clki = list_first_entry(clk_list, struct ufs_clk_info, list);
14018c2ecf20Sopenharmony_ci	dev_pm_opp_add(hba->dev, clki->min_freq, 0);
14028c2ecf20Sopenharmony_ci	dev_pm_opp_add(hba->dev, clki->max_freq, 0);
14038c2ecf20Sopenharmony_ci
14048c2ecf20Sopenharmony_ci	ufshcd_vops_config_scaling_param(hba, &hba->vps->devfreq_profile,
14058c2ecf20Sopenharmony_ci					 &hba->vps->ondemand_data);
14068c2ecf20Sopenharmony_ci	devfreq = devfreq_add_device(hba->dev,
14078c2ecf20Sopenharmony_ci			&hba->vps->devfreq_profile,
14088c2ecf20Sopenharmony_ci			DEVFREQ_GOV_SIMPLE_ONDEMAND,
14098c2ecf20Sopenharmony_ci			&hba->vps->ondemand_data);
14108c2ecf20Sopenharmony_ci	if (IS_ERR(devfreq)) {
14118c2ecf20Sopenharmony_ci		ret = PTR_ERR(devfreq);
14128c2ecf20Sopenharmony_ci		dev_err(hba->dev, "Unable to register with devfreq %d\n", ret);
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_ci		dev_pm_opp_remove(hba->dev, clki->min_freq);
14158c2ecf20Sopenharmony_ci		dev_pm_opp_remove(hba->dev, clki->max_freq);
14168c2ecf20Sopenharmony_ci		return ret;
14178c2ecf20Sopenharmony_ci	}
14188c2ecf20Sopenharmony_ci
14198c2ecf20Sopenharmony_ci	hba->devfreq = devfreq;
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_ci	return 0;
14228c2ecf20Sopenharmony_ci}
14238c2ecf20Sopenharmony_ci
14248c2ecf20Sopenharmony_cistatic void ufshcd_devfreq_remove(struct ufs_hba *hba)
14258c2ecf20Sopenharmony_ci{
14268c2ecf20Sopenharmony_ci	struct list_head *clk_list = &hba->clk_list_head;
14278c2ecf20Sopenharmony_ci	struct ufs_clk_info *clki;
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_ci	if (!hba->devfreq)
14308c2ecf20Sopenharmony_ci		return;
14318c2ecf20Sopenharmony_ci
14328c2ecf20Sopenharmony_ci	devfreq_remove_device(hba->devfreq);
14338c2ecf20Sopenharmony_ci	hba->devfreq = NULL;
14348c2ecf20Sopenharmony_ci
14358c2ecf20Sopenharmony_ci	clki = list_first_entry(clk_list, struct ufs_clk_info, list);
14368c2ecf20Sopenharmony_ci	dev_pm_opp_remove(hba->dev, clki->min_freq);
14378c2ecf20Sopenharmony_ci	dev_pm_opp_remove(hba->dev, clki->max_freq);
14388c2ecf20Sopenharmony_ci}
14398c2ecf20Sopenharmony_ci
14408c2ecf20Sopenharmony_cistatic void __ufshcd_suspend_clkscaling(struct ufs_hba *hba)
14418c2ecf20Sopenharmony_ci{
14428c2ecf20Sopenharmony_ci	unsigned long flags;
14438c2ecf20Sopenharmony_ci
14448c2ecf20Sopenharmony_ci	devfreq_suspend_device(hba->devfreq);
14458c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
14468c2ecf20Sopenharmony_ci	hba->clk_scaling.window_start_t = 0;
14478c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
14488c2ecf20Sopenharmony_ci}
14498c2ecf20Sopenharmony_ci
14508c2ecf20Sopenharmony_cistatic void ufshcd_suspend_clkscaling(struct ufs_hba *hba)
14518c2ecf20Sopenharmony_ci{
14528c2ecf20Sopenharmony_ci	unsigned long flags;
14538c2ecf20Sopenharmony_ci	bool suspend = false;
14548c2ecf20Sopenharmony_ci
14558c2ecf20Sopenharmony_ci	if (!ufshcd_is_clkscaling_supported(hba))
14568c2ecf20Sopenharmony_ci		return;
14578c2ecf20Sopenharmony_ci
14588c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
14598c2ecf20Sopenharmony_ci	if (!hba->clk_scaling.is_suspended) {
14608c2ecf20Sopenharmony_ci		suspend = true;
14618c2ecf20Sopenharmony_ci		hba->clk_scaling.is_suspended = true;
14628c2ecf20Sopenharmony_ci	}
14638c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
14648c2ecf20Sopenharmony_ci
14658c2ecf20Sopenharmony_ci	if (suspend)
14668c2ecf20Sopenharmony_ci		__ufshcd_suspend_clkscaling(hba);
14678c2ecf20Sopenharmony_ci}
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_cistatic void ufshcd_resume_clkscaling(struct ufs_hba *hba)
14708c2ecf20Sopenharmony_ci{
14718c2ecf20Sopenharmony_ci	unsigned long flags;
14728c2ecf20Sopenharmony_ci	bool resume = false;
14738c2ecf20Sopenharmony_ci
14748c2ecf20Sopenharmony_ci	if (!ufshcd_is_clkscaling_supported(hba))
14758c2ecf20Sopenharmony_ci		return;
14768c2ecf20Sopenharmony_ci
14778c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
14788c2ecf20Sopenharmony_ci	if (hba->clk_scaling.is_suspended) {
14798c2ecf20Sopenharmony_ci		resume = true;
14808c2ecf20Sopenharmony_ci		hba->clk_scaling.is_suspended = false;
14818c2ecf20Sopenharmony_ci	}
14828c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
14838c2ecf20Sopenharmony_ci
14848c2ecf20Sopenharmony_ci	if (resume)
14858c2ecf20Sopenharmony_ci		devfreq_resume_device(hba->devfreq);
14868c2ecf20Sopenharmony_ci}
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_cistatic ssize_t ufshcd_clkscale_enable_show(struct device *dev,
14898c2ecf20Sopenharmony_ci		struct device_attribute *attr, char *buf)
14908c2ecf20Sopenharmony_ci{
14918c2ecf20Sopenharmony_ci	struct ufs_hba *hba = dev_get_drvdata(dev);
14928c2ecf20Sopenharmony_ci
14938c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%d\n", hba->clk_scaling.is_allowed);
14948c2ecf20Sopenharmony_ci}
14958c2ecf20Sopenharmony_ci
14968c2ecf20Sopenharmony_cistatic ssize_t ufshcd_clkscale_enable_store(struct device *dev,
14978c2ecf20Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t count)
14988c2ecf20Sopenharmony_ci{
14998c2ecf20Sopenharmony_ci	struct ufs_hba *hba = dev_get_drvdata(dev);
15008c2ecf20Sopenharmony_ci	u32 value;
15018c2ecf20Sopenharmony_ci	int err;
15028c2ecf20Sopenharmony_ci
15038c2ecf20Sopenharmony_ci	if (kstrtou32(buf, 0, &value))
15048c2ecf20Sopenharmony_ci		return -EINVAL;
15058c2ecf20Sopenharmony_ci
15068c2ecf20Sopenharmony_ci	value = !!value;
15078c2ecf20Sopenharmony_ci	if (value == hba->clk_scaling.is_allowed)
15088c2ecf20Sopenharmony_ci		goto out;
15098c2ecf20Sopenharmony_ci
15108c2ecf20Sopenharmony_ci	pm_runtime_get_sync(hba->dev);
15118c2ecf20Sopenharmony_ci	ufshcd_hold(hba, false);
15128c2ecf20Sopenharmony_ci
15138c2ecf20Sopenharmony_ci	cancel_work_sync(&hba->clk_scaling.suspend_work);
15148c2ecf20Sopenharmony_ci	cancel_work_sync(&hba->clk_scaling.resume_work);
15158c2ecf20Sopenharmony_ci
15168c2ecf20Sopenharmony_ci	hba->clk_scaling.is_allowed = value;
15178c2ecf20Sopenharmony_ci
15188c2ecf20Sopenharmony_ci	if (value) {
15198c2ecf20Sopenharmony_ci		ufshcd_resume_clkscaling(hba);
15208c2ecf20Sopenharmony_ci	} else {
15218c2ecf20Sopenharmony_ci		ufshcd_suspend_clkscaling(hba);
15228c2ecf20Sopenharmony_ci		err = ufshcd_devfreq_scale(hba, true);
15238c2ecf20Sopenharmony_ci		if (err)
15248c2ecf20Sopenharmony_ci			dev_err(hba->dev, "%s: failed to scale clocks up %d\n",
15258c2ecf20Sopenharmony_ci					__func__, err);
15268c2ecf20Sopenharmony_ci	}
15278c2ecf20Sopenharmony_ci
15288c2ecf20Sopenharmony_ci	ufshcd_release(hba);
15298c2ecf20Sopenharmony_ci	pm_runtime_put_sync(hba->dev);
15308c2ecf20Sopenharmony_ciout:
15318c2ecf20Sopenharmony_ci	return count;
15328c2ecf20Sopenharmony_ci}
15338c2ecf20Sopenharmony_ci
15348c2ecf20Sopenharmony_cistatic void ufshcd_clkscaling_init_sysfs(struct ufs_hba *hba)
15358c2ecf20Sopenharmony_ci{
15368c2ecf20Sopenharmony_ci	hba->clk_scaling.enable_attr.show = ufshcd_clkscale_enable_show;
15378c2ecf20Sopenharmony_ci	hba->clk_scaling.enable_attr.store = ufshcd_clkscale_enable_store;
15388c2ecf20Sopenharmony_ci	sysfs_attr_init(&hba->clk_scaling.enable_attr.attr);
15398c2ecf20Sopenharmony_ci	hba->clk_scaling.enable_attr.attr.name = "clkscale_enable";
15408c2ecf20Sopenharmony_ci	hba->clk_scaling.enable_attr.attr.mode = 0644;
15418c2ecf20Sopenharmony_ci	if (device_create_file(hba->dev, &hba->clk_scaling.enable_attr))
15428c2ecf20Sopenharmony_ci		dev_err(hba->dev, "Failed to create sysfs for clkscale_enable\n");
15438c2ecf20Sopenharmony_ci}
15448c2ecf20Sopenharmony_ci
15458c2ecf20Sopenharmony_cistatic void ufshcd_ungate_work(struct work_struct *work)
15468c2ecf20Sopenharmony_ci{
15478c2ecf20Sopenharmony_ci	int ret;
15488c2ecf20Sopenharmony_ci	unsigned long flags;
15498c2ecf20Sopenharmony_ci	struct ufs_hba *hba = container_of(work, struct ufs_hba,
15508c2ecf20Sopenharmony_ci			clk_gating.ungate_work);
15518c2ecf20Sopenharmony_ci
15528c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&hba->clk_gating.gate_work);
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
15558c2ecf20Sopenharmony_ci	if (hba->clk_gating.state == CLKS_ON) {
15568c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(hba->host->host_lock, flags);
15578c2ecf20Sopenharmony_ci		goto unblock_reqs;
15588c2ecf20Sopenharmony_ci	}
15598c2ecf20Sopenharmony_ci
15608c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
15618c2ecf20Sopenharmony_ci	ufshcd_setup_clocks(hba, true);
15628c2ecf20Sopenharmony_ci
15638c2ecf20Sopenharmony_ci	ufshcd_enable_irq(hba);
15648c2ecf20Sopenharmony_ci
15658c2ecf20Sopenharmony_ci	/* Exit from hibern8 */
15668c2ecf20Sopenharmony_ci	if (ufshcd_can_hibern8_during_gating(hba)) {
15678c2ecf20Sopenharmony_ci		/* Prevent gating in this path */
15688c2ecf20Sopenharmony_ci		hba->clk_gating.is_suspended = true;
15698c2ecf20Sopenharmony_ci		if (ufshcd_is_link_hibern8(hba)) {
15708c2ecf20Sopenharmony_ci			ret = ufshcd_uic_hibern8_exit(hba);
15718c2ecf20Sopenharmony_ci			if (ret)
15728c2ecf20Sopenharmony_ci				dev_err(hba->dev, "%s: hibern8 exit failed %d\n",
15738c2ecf20Sopenharmony_ci					__func__, ret);
15748c2ecf20Sopenharmony_ci			else
15758c2ecf20Sopenharmony_ci				ufshcd_set_link_active(hba);
15768c2ecf20Sopenharmony_ci		}
15778c2ecf20Sopenharmony_ci		hba->clk_gating.is_suspended = false;
15788c2ecf20Sopenharmony_ci	}
15798c2ecf20Sopenharmony_ciunblock_reqs:
15808c2ecf20Sopenharmony_ci	ufshcd_scsi_unblock_requests(hba);
15818c2ecf20Sopenharmony_ci}
15828c2ecf20Sopenharmony_ci
15838c2ecf20Sopenharmony_ci/**
15848c2ecf20Sopenharmony_ci * ufshcd_hold - Enable clocks that were gated earlier due to ufshcd_release.
15858c2ecf20Sopenharmony_ci * Also, exit from hibern8 mode and set the link as active.
15868c2ecf20Sopenharmony_ci * @hba: per adapter instance
15878c2ecf20Sopenharmony_ci * @async: This indicates whether caller should ungate clocks asynchronously.
15888c2ecf20Sopenharmony_ci */
15898c2ecf20Sopenharmony_ciint ufshcd_hold(struct ufs_hba *hba, bool async)
15908c2ecf20Sopenharmony_ci{
15918c2ecf20Sopenharmony_ci	int rc = 0;
15928c2ecf20Sopenharmony_ci	bool flush_result;
15938c2ecf20Sopenharmony_ci	unsigned long flags;
15948c2ecf20Sopenharmony_ci
15958c2ecf20Sopenharmony_ci	if (!ufshcd_is_clkgating_allowed(hba))
15968c2ecf20Sopenharmony_ci		goto out;
15978c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
15988c2ecf20Sopenharmony_ci	hba->clk_gating.active_reqs++;
15998c2ecf20Sopenharmony_ci
16008c2ecf20Sopenharmony_cistart:
16018c2ecf20Sopenharmony_ci	switch (hba->clk_gating.state) {
16028c2ecf20Sopenharmony_ci	case CLKS_ON:
16038c2ecf20Sopenharmony_ci		/*
16048c2ecf20Sopenharmony_ci		 * Wait for the ungate work to complete if in progress.
16058c2ecf20Sopenharmony_ci		 * Though the clocks may be in ON state, the link could
16068c2ecf20Sopenharmony_ci		 * still be in hibner8 state if hibern8 is allowed
16078c2ecf20Sopenharmony_ci		 * during clock gating.
16088c2ecf20Sopenharmony_ci		 * Make sure we exit hibern8 state also in addition to
16098c2ecf20Sopenharmony_ci		 * clocks being ON.
16108c2ecf20Sopenharmony_ci		 */
16118c2ecf20Sopenharmony_ci		if (ufshcd_can_hibern8_during_gating(hba) &&
16128c2ecf20Sopenharmony_ci		    ufshcd_is_link_hibern8(hba)) {
16138c2ecf20Sopenharmony_ci			if (async) {
16148c2ecf20Sopenharmony_ci				rc = -EAGAIN;
16158c2ecf20Sopenharmony_ci				hba->clk_gating.active_reqs--;
16168c2ecf20Sopenharmony_ci				break;
16178c2ecf20Sopenharmony_ci			}
16188c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(hba->host->host_lock, flags);
16198c2ecf20Sopenharmony_ci			flush_result = flush_work(&hba->clk_gating.ungate_work);
16208c2ecf20Sopenharmony_ci			if (hba->clk_gating.is_suspended && !flush_result)
16218c2ecf20Sopenharmony_ci				goto out;
16228c2ecf20Sopenharmony_ci			spin_lock_irqsave(hba->host->host_lock, flags);
16238c2ecf20Sopenharmony_ci			goto start;
16248c2ecf20Sopenharmony_ci		}
16258c2ecf20Sopenharmony_ci		break;
16268c2ecf20Sopenharmony_ci	case REQ_CLKS_OFF:
16278c2ecf20Sopenharmony_ci		if (cancel_delayed_work(&hba->clk_gating.gate_work)) {
16288c2ecf20Sopenharmony_ci			hba->clk_gating.state = CLKS_ON;
16298c2ecf20Sopenharmony_ci			trace_ufshcd_clk_gating(dev_name(hba->dev),
16308c2ecf20Sopenharmony_ci						hba->clk_gating.state);
16318c2ecf20Sopenharmony_ci			break;
16328c2ecf20Sopenharmony_ci		}
16338c2ecf20Sopenharmony_ci		/*
16348c2ecf20Sopenharmony_ci		 * If we are here, it means gating work is either done or
16358c2ecf20Sopenharmony_ci		 * currently running. Hence, fall through to cancel gating
16368c2ecf20Sopenharmony_ci		 * work and to enable clocks.
16378c2ecf20Sopenharmony_ci		 */
16388c2ecf20Sopenharmony_ci		fallthrough;
16398c2ecf20Sopenharmony_ci	case CLKS_OFF:
16408c2ecf20Sopenharmony_ci		hba->clk_gating.state = REQ_CLKS_ON;
16418c2ecf20Sopenharmony_ci		trace_ufshcd_clk_gating(dev_name(hba->dev),
16428c2ecf20Sopenharmony_ci					hba->clk_gating.state);
16438c2ecf20Sopenharmony_ci		if (queue_work(hba->clk_gating.clk_gating_workq,
16448c2ecf20Sopenharmony_ci			       &hba->clk_gating.ungate_work))
16458c2ecf20Sopenharmony_ci			ufshcd_scsi_block_requests(hba);
16468c2ecf20Sopenharmony_ci		/*
16478c2ecf20Sopenharmony_ci		 * fall through to check if we should wait for this
16488c2ecf20Sopenharmony_ci		 * work to be done or not.
16498c2ecf20Sopenharmony_ci		 */
16508c2ecf20Sopenharmony_ci		fallthrough;
16518c2ecf20Sopenharmony_ci	case REQ_CLKS_ON:
16528c2ecf20Sopenharmony_ci		if (async) {
16538c2ecf20Sopenharmony_ci			rc = -EAGAIN;
16548c2ecf20Sopenharmony_ci			hba->clk_gating.active_reqs--;
16558c2ecf20Sopenharmony_ci			break;
16568c2ecf20Sopenharmony_ci		}
16578c2ecf20Sopenharmony_ci
16588c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(hba->host->host_lock, flags);
16598c2ecf20Sopenharmony_ci		flush_work(&hba->clk_gating.ungate_work);
16608c2ecf20Sopenharmony_ci		/* Make sure state is CLKS_ON before returning */
16618c2ecf20Sopenharmony_ci		spin_lock_irqsave(hba->host->host_lock, flags);
16628c2ecf20Sopenharmony_ci		goto start;
16638c2ecf20Sopenharmony_ci	default:
16648c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: clk gating is in invalid state %d\n",
16658c2ecf20Sopenharmony_ci				__func__, hba->clk_gating.state);
16668c2ecf20Sopenharmony_ci		break;
16678c2ecf20Sopenharmony_ci	}
16688c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
16698c2ecf20Sopenharmony_ciout:
16708c2ecf20Sopenharmony_ci	return rc;
16718c2ecf20Sopenharmony_ci}
16728c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ufshcd_hold);
16738c2ecf20Sopenharmony_ci
16748c2ecf20Sopenharmony_cistatic void ufshcd_gate_work(struct work_struct *work)
16758c2ecf20Sopenharmony_ci{
16768c2ecf20Sopenharmony_ci	struct ufs_hba *hba = container_of(work, struct ufs_hba,
16778c2ecf20Sopenharmony_ci			clk_gating.gate_work.work);
16788c2ecf20Sopenharmony_ci	unsigned long flags;
16798c2ecf20Sopenharmony_ci	int ret;
16808c2ecf20Sopenharmony_ci
16818c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
16828c2ecf20Sopenharmony_ci	/*
16838c2ecf20Sopenharmony_ci	 * In case you are here to cancel this work the gating state
16848c2ecf20Sopenharmony_ci	 * would be marked as REQ_CLKS_ON. In this case save time by
16858c2ecf20Sopenharmony_ci	 * skipping the gating work and exit after changing the clock
16868c2ecf20Sopenharmony_ci	 * state to CLKS_ON.
16878c2ecf20Sopenharmony_ci	 */
16888c2ecf20Sopenharmony_ci	if (hba->clk_gating.is_suspended ||
16898c2ecf20Sopenharmony_ci		(hba->clk_gating.state != REQ_CLKS_OFF)) {
16908c2ecf20Sopenharmony_ci		hba->clk_gating.state = CLKS_ON;
16918c2ecf20Sopenharmony_ci		trace_ufshcd_clk_gating(dev_name(hba->dev),
16928c2ecf20Sopenharmony_ci					hba->clk_gating.state);
16938c2ecf20Sopenharmony_ci		goto rel_lock;
16948c2ecf20Sopenharmony_ci	}
16958c2ecf20Sopenharmony_ci
16968c2ecf20Sopenharmony_ci	if (hba->clk_gating.active_reqs
16978c2ecf20Sopenharmony_ci		|| hba->ufshcd_state != UFSHCD_STATE_OPERATIONAL
16988c2ecf20Sopenharmony_ci		|| ufshcd_any_tag_in_use(hba) || hba->outstanding_tasks
16998c2ecf20Sopenharmony_ci		|| hba->active_uic_cmd || hba->uic_async_done)
17008c2ecf20Sopenharmony_ci		goto rel_lock;
17018c2ecf20Sopenharmony_ci
17028c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
17038c2ecf20Sopenharmony_ci
17048c2ecf20Sopenharmony_ci	/* put the link into hibern8 mode before turning off clocks */
17058c2ecf20Sopenharmony_ci	if (ufshcd_can_hibern8_during_gating(hba)) {
17068c2ecf20Sopenharmony_ci		ret = ufshcd_uic_hibern8_enter(hba);
17078c2ecf20Sopenharmony_ci		if (ret) {
17088c2ecf20Sopenharmony_ci			hba->clk_gating.state = CLKS_ON;
17098c2ecf20Sopenharmony_ci			dev_err(hba->dev, "%s: hibern8 enter failed %d\n",
17108c2ecf20Sopenharmony_ci					__func__, ret);
17118c2ecf20Sopenharmony_ci			trace_ufshcd_clk_gating(dev_name(hba->dev),
17128c2ecf20Sopenharmony_ci						hba->clk_gating.state);
17138c2ecf20Sopenharmony_ci			goto out;
17148c2ecf20Sopenharmony_ci		}
17158c2ecf20Sopenharmony_ci		ufshcd_set_link_hibern8(hba);
17168c2ecf20Sopenharmony_ci	}
17178c2ecf20Sopenharmony_ci
17188c2ecf20Sopenharmony_ci	ufshcd_disable_irq(hba);
17198c2ecf20Sopenharmony_ci
17208c2ecf20Sopenharmony_ci	ufshcd_setup_clocks(hba, false);
17218c2ecf20Sopenharmony_ci
17228c2ecf20Sopenharmony_ci	/*
17238c2ecf20Sopenharmony_ci	 * In case you are here to cancel this work the gating state
17248c2ecf20Sopenharmony_ci	 * would be marked as REQ_CLKS_ON. In this case keep the state
17258c2ecf20Sopenharmony_ci	 * as REQ_CLKS_ON which would anyway imply that clocks are off
17268c2ecf20Sopenharmony_ci	 * and a request to turn them on is pending. By doing this way,
17278c2ecf20Sopenharmony_ci	 * we keep the state machine in tact and this would ultimately
17288c2ecf20Sopenharmony_ci	 * prevent from doing cancel work multiple times when there are
17298c2ecf20Sopenharmony_ci	 * new requests arriving before the current cancel work is done.
17308c2ecf20Sopenharmony_ci	 */
17318c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
17328c2ecf20Sopenharmony_ci	if (hba->clk_gating.state == REQ_CLKS_OFF) {
17338c2ecf20Sopenharmony_ci		hba->clk_gating.state = CLKS_OFF;
17348c2ecf20Sopenharmony_ci		trace_ufshcd_clk_gating(dev_name(hba->dev),
17358c2ecf20Sopenharmony_ci					hba->clk_gating.state);
17368c2ecf20Sopenharmony_ci	}
17378c2ecf20Sopenharmony_cirel_lock:
17388c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
17398c2ecf20Sopenharmony_ciout:
17408c2ecf20Sopenharmony_ci	return;
17418c2ecf20Sopenharmony_ci}
17428c2ecf20Sopenharmony_ci
17438c2ecf20Sopenharmony_ci/* host lock must be held before calling this variant */
17448c2ecf20Sopenharmony_cistatic void __ufshcd_release(struct ufs_hba *hba)
17458c2ecf20Sopenharmony_ci{
17468c2ecf20Sopenharmony_ci	if (!ufshcd_is_clkgating_allowed(hba))
17478c2ecf20Sopenharmony_ci		return;
17488c2ecf20Sopenharmony_ci
17498c2ecf20Sopenharmony_ci	hba->clk_gating.active_reqs--;
17508c2ecf20Sopenharmony_ci
17518c2ecf20Sopenharmony_ci	if (hba->clk_gating.active_reqs || hba->clk_gating.is_suspended ||
17528c2ecf20Sopenharmony_ci	    hba->ufshcd_state != UFSHCD_STATE_OPERATIONAL ||
17538c2ecf20Sopenharmony_ci	    hba->outstanding_tasks ||
17548c2ecf20Sopenharmony_ci	    hba->active_uic_cmd || hba->uic_async_done ||
17558c2ecf20Sopenharmony_ci	    hba->clk_gating.state == CLKS_OFF)
17568c2ecf20Sopenharmony_ci		return;
17578c2ecf20Sopenharmony_ci
17588c2ecf20Sopenharmony_ci	hba->clk_gating.state = REQ_CLKS_OFF;
17598c2ecf20Sopenharmony_ci	trace_ufshcd_clk_gating(dev_name(hba->dev), hba->clk_gating.state);
17608c2ecf20Sopenharmony_ci	queue_delayed_work(hba->clk_gating.clk_gating_workq,
17618c2ecf20Sopenharmony_ci			   &hba->clk_gating.gate_work,
17628c2ecf20Sopenharmony_ci			   msecs_to_jiffies(hba->clk_gating.delay_ms));
17638c2ecf20Sopenharmony_ci}
17648c2ecf20Sopenharmony_ci
17658c2ecf20Sopenharmony_civoid ufshcd_release(struct ufs_hba *hba)
17668c2ecf20Sopenharmony_ci{
17678c2ecf20Sopenharmony_ci	unsigned long flags;
17688c2ecf20Sopenharmony_ci
17698c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
17708c2ecf20Sopenharmony_ci	__ufshcd_release(hba);
17718c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
17728c2ecf20Sopenharmony_ci}
17738c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ufshcd_release);
17748c2ecf20Sopenharmony_ci
17758c2ecf20Sopenharmony_cistatic ssize_t ufshcd_clkgate_delay_show(struct device *dev,
17768c2ecf20Sopenharmony_ci		struct device_attribute *attr, char *buf)
17778c2ecf20Sopenharmony_ci{
17788c2ecf20Sopenharmony_ci	struct ufs_hba *hba = dev_get_drvdata(dev);
17798c2ecf20Sopenharmony_ci
17808c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%lu\n", hba->clk_gating.delay_ms);
17818c2ecf20Sopenharmony_ci}
17828c2ecf20Sopenharmony_ci
17838c2ecf20Sopenharmony_cistatic ssize_t ufshcd_clkgate_delay_store(struct device *dev,
17848c2ecf20Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t count)
17858c2ecf20Sopenharmony_ci{
17868c2ecf20Sopenharmony_ci	struct ufs_hba *hba = dev_get_drvdata(dev);
17878c2ecf20Sopenharmony_ci	unsigned long flags, value;
17888c2ecf20Sopenharmony_ci
17898c2ecf20Sopenharmony_ci	if (kstrtoul(buf, 0, &value))
17908c2ecf20Sopenharmony_ci		return -EINVAL;
17918c2ecf20Sopenharmony_ci
17928c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
17938c2ecf20Sopenharmony_ci	hba->clk_gating.delay_ms = value;
17948c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
17958c2ecf20Sopenharmony_ci	return count;
17968c2ecf20Sopenharmony_ci}
17978c2ecf20Sopenharmony_ci
17988c2ecf20Sopenharmony_cistatic ssize_t ufshcd_clkgate_enable_show(struct device *dev,
17998c2ecf20Sopenharmony_ci		struct device_attribute *attr, char *buf)
18008c2ecf20Sopenharmony_ci{
18018c2ecf20Sopenharmony_ci	struct ufs_hba *hba = dev_get_drvdata(dev);
18028c2ecf20Sopenharmony_ci
18038c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%d\n", hba->clk_gating.is_enabled);
18048c2ecf20Sopenharmony_ci}
18058c2ecf20Sopenharmony_ci
18068c2ecf20Sopenharmony_cistatic ssize_t ufshcd_clkgate_enable_store(struct device *dev,
18078c2ecf20Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t count)
18088c2ecf20Sopenharmony_ci{
18098c2ecf20Sopenharmony_ci	struct ufs_hba *hba = dev_get_drvdata(dev);
18108c2ecf20Sopenharmony_ci	unsigned long flags;
18118c2ecf20Sopenharmony_ci	u32 value;
18128c2ecf20Sopenharmony_ci
18138c2ecf20Sopenharmony_ci	if (kstrtou32(buf, 0, &value))
18148c2ecf20Sopenharmony_ci		return -EINVAL;
18158c2ecf20Sopenharmony_ci
18168c2ecf20Sopenharmony_ci	value = !!value;
18178c2ecf20Sopenharmony_ci	if (value == hba->clk_gating.is_enabled)
18188c2ecf20Sopenharmony_ci		goto out;
18198c2ecf20Sopenharmony_ci
18208c2ecf20Sopenharmony_ci	if (value) {
18218c2ecf20Sopenharmony_ci		ufshcd_release(hba);
18228c2ecf20Sopenharmony_ci	} else {
18238c2ecf20Sopenharmony_ci		spin_lock_irqsave(hba->host->host_lock, flags);
18248c2ecf20Sopenharmony_ci		hba->clk_gating.active_reqs++;
18258c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(hba->host->host_lock, flags);
18268c2ecf20Sopenharmony_ci	}
18278c2ecf20Sopenharmony_ci
18288c2ecf20Sopenharmony_ci	hba->clk_gating.is_enabled = value;
18298c2ecf20Sopenharmony_ciout:
18308c2ecf20Sopenharmony_ci	return count;
18318c2ecf20Sopenharmony_ci}
18328c2ecf20Sopenharmony_ci
18338c2ecf20Sopenharmony_cistatic void ufshcd_init_clk_scaling(struct ufs_hba *hba)
18348c2ecf20Sopenharmony_ci{
18358c2ecf20Sopenharmony_ci	char wq_name[sizeof("ufs_clkscaling_00")];
18368c2ecf20Sopenharmony_ci
18378c2ecf20Sopenharmony_ci	if (!ufshcd_is_clkscaling_supported(hba))
18388c2ecf20Sopenharmony_ci		return;
18398c2ecf20Sopenharmony_ci
18408c2ecf20Sopenharmony_ci	INIT_WORK(&hba->clk_scaling.suspend_work,
18418c2ecf20Sopenharmony_ci		  ufshcd_clk_scaling_suspend_work);
18428c2ecf20Sopenharmony_ci	INIT_WORK(&hba->clk_scaling.resume_work,
18438c2ecf20Sopenharmony_ci		  ufshcd_clk_scaling_resume_work);
18448c2ecf20Sopenharmony_ci
18458c2ecf20Sopenharmony_ci	snprintf(wq_name, sizeof(wq_name), "ufs_clkscaling_%d",
18468c2ecf20Sopenharmony_ci		 hba->host->host_no);
18478c2ecf20Sopenharmony_ci	hba->clk_scaling.workq = create_singlethread_workqueue(wq_name);
18488c2ecf20Sopenharmony_ci
18498c2ecf20Sopenharmony_ci	ufshcd_clkscaling_init_sysfs(hba);
18508c2ecf20Sopenharmony_ci}
18518c2ecf20Sopenharmony_ci
18528c2ecf20Sopenharmony_cistatic void ufshcd_exit_clk_scaling(struct ufs_hba *hba)
18538c2ecf20Sopenharmony_ci{
18548c2ecf20Sopenharmony_ci	if (!ufshcd_is_clkscaling_supported(hba))
18558c2ecf20Sopenharmony_ci		return;
18568c2ecf20Sopenharmony_ci
18578c2ecf20Sopenharmony_ci	destroy_workqueue(hba->clk_scaling.workq);
18588c2ecf20Sopenharmony_ci	ufshcd_devfreq_remove(hba);
18598c2ecf20Sopenharmony_ci}
18608c2ecf20Sopenharmony_ci
18618c2ecf20Sopenharmony_cistatic void ufshcd_init_clk_gating(struct ufs_hba *hba)
18628c2ecf20Sopenharmony_ci{
18638c2ecf20Sopenharmony_ci	char wq_name[sizeof("ufs_clk_gating_00")];
18648c2ecf20Sopenharmony_ci
18658c2ecf20Sopenharmony_ci	if (!ufshcd_is_clkgating_allowed(hba))
18668c2ecf20Sopenharmony_ci		return;
18678c2ecf20Sopenharmony_ci
18688c2ecf20Sopenharmony_ci	hba->clk_gating.state = CLKS_ON;
18698c2ecf20Sopenharmony_ci
18708c2ecf20Sopenharmony_ci	hba->clk_gating.delay_ms = 150;
18718c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&hba->clk_gating.gate_work, ufshcd_gate_work);
18728c2ecf20Sopenharmony_ci	INIT_WORK(&hba->clk_gating.ungate_work, ufshcd_ungate_work);
18738c2ecf20Sopenharmony_ci
18748c2ecf20Sopenharmony_ci	snprintf(wq_name, ARRAY_SIZE(wq_name), "ufs_clk_gating_%d",
18758c2ecf20Sopenharmony_ci		 hba->host->host_no);
18768c2ecf20Sopenharmony_ci	hba->clk_gating.clk_gating_workq = alloc_ordered_workqueue(wq_name,
18778c2ecf20Sopenharmony_ci							   WQ_MEM_RECLAIM);
18788c2ecf20Sopenharmony_ci
18798c2ecf20Sopenharmony_ci	hba->clk_gating.is_enabled = true;
18808c2ecf20Sopenharmony_ci
18818c2ecf20Sopenharmony_ci	hba->clk_gating.delay_attr.show = ufshcd_clkgate_delay_show;
18828c2ecf20Sopenharmony_ci	hba->clk_gating.delay_attr.store = ufshcd_clkgate_delay_store;
18838c2ecf20Sopenharmony_ci	sysfs_attr_init(&hba->clk_gating.delay_attr.attr);
18848c2ecf20Sopenharmony_ci	hba->clk_gating.delay_attr.attr.name = "clkgate_delay_ms";
18858c2ecf20Sopenharmony_ci	hba->clk_gating.delay_attr.attr.mode = 0644;
18868c2ecf20Sopenharmony_ci	if (device_create_file(hba->dev, &hba->clk_gating.delay_attr))
18878c2ecf20Sopenharmony_ci		dev_err(hba->dev, "Failed to create sysfs for clkgate_delay\n");
18888c2ecf20Sopenharmony_ci
18898c2ecf20Sopenharmony_ci	hba->clk_gating.enable_attr.show = ufshcd_clkgate_enable_show;
18908c2ecf20Sopenharmony_ci	hba->clk_gating.enable_attr.store = ufshcd_clkgate_enable_store;
18918c2ecf20Sopenharmony_ci	sysfs_attr_init(&hba->clk_gating.enable_attr.attr);
18928c2ecf20Sopenharmony_ci	hba->clk_gating.enable_attr.attr.name = "clkgate_enable";
18938c2ecf20Sopenharmony_ci	hba->clk_gating.enable_attr.attr.mode = 0644;
18948c2ecf20Sopenharmony_ci	if (device_create_file(hba->dev, &hba->clk_gating.enable_attr))
18958c2ecf20Sopenharmony_ci		dev_err(hba->dev, "Failed to create sysfs for clkgate_enable\n");
18968c2ecf20Sopenharmony_ci}
18978c2ecf20Sopenharmony_ci
18988c2ecf20Sopenharmony_cistatic void ufshcd_exit_clk_gating(struct ufs_hba *hba)
18998c2ecf20Sopenharmony_ci{
19008c2ecf20Sopenharmony_ci	if (!ufshcd_is_clkgating_allowed(hba))
19018c2ecf20Sopenharmony_ci		return;
19028c2ecf20Sopenharmony_ci	device_remove_file(hba->dev, &hba->clk_gating.delay_attr);
19038c2ecf20Sopenharmony_ci	device_remove_file(hba->dev, &hba->clk_gating.enable_attr);
19048c2ecf20Sopenharmony_ci	cancel_work_sync(&hba->clk_gating.ungate_work);
19058c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&hba->clk_gating.gate_work);
19068c2ecf20Sopenharmony_ci	destroy_workqueue(hba->clk_gating.clk_gating_workq);
19078c2ecf20Sopenharmony_ci}
19088c2ecf20Sopenharmony_ci
19098c2ecf20Sopenharmony_ci/* Must be called with host lock acquired */
19108c2ecf20Sopenharmony_cistatic void ufshcd_clk_scaling_start_busy(struct ufs_hba *hba)
19118c2ecf20Sopenharmony_ci{
19128c2ecf20Sopenharmony_ci	bool queue_resume_work = false;
19138c2ecf20Sopenharmony_ci	ktime_t curr_t = ktime_get();
19148c2ecf20Sopenharmony_ci
19158c2ecf20Sopenharmony_ci	if (!ufshcd_is_clkscaling_supported(hba))
19168c2ecf20Sopenharmony_ci		return;
19178c2ecf20Sopenharmony_ci
19188c2ecf20Sopenharmony_ci	if (!hba->clk_scaling.active_reqs++)
19198c2ecf20Sopenharmony_ci		queue_resume_work = true;
19208c2ecf20Sopenharmony_ci
19218c2ecf20Sopenharmony_ci	if (!hba->clk_scaling.is_allowed || hba->pm_op_in_progress)
19228c2ecf20Sopenharmony_ci		return;
19238c2ecf20Sopenharmony_ci
19248c2ecf20Sopenharmony_ci	if (queue_resume_work)
19258c2ecf20Sopenharmony_ci		queue_work(hba->clk_scaling.workq,
19268c2ecf20Sopenharmony_ci			   &hba->clk_scaling.resume_work);
19278c2ecf20Sopenharmony_ci
19288c2ecf20Sopenharmony_ci	if (!hba->clk_scaling.window_start_t) {
19298c2ecf20Sopenharmony_ci		hba->clk_scaling.window_start_t = curr_t;
19308c2ecf20Sopenharmony_ci		hba->clk_scaling.tot_busy_t = 0;
19318c2ecf20Sopenharmony_ci		hba->clk_scaling.is_busy_started = false;
19328c2ecf20Sopenharmony_ci	}
19338c2ecf20Sopenharmony_ci
19348c2ecf20Sopenharmony_ci	if (!hba->clk_scaling.is_busy_started) {
19358c2ecf20Sopenharmony_ci		hba->clk_scaling.busy_start_t = curr_t;
19368c2ecf20Sopenharmony_ci		hba->clk_scaling.is_busy_started = true;
19378c2ecf20Sopenharmony_ci	}
19388c2ecf20Sopenharmony_ci}
19398c2ecf20Sopenharmony_ci
19408c2ecf20Sopenharmony_cistatic void ufshcd_clk_scaling_update_busy(struct ufs_hba *hba)
19418c2ecf20Sopenharmony_ci{
19428c2ecf20Sopenharmony_ci	struct ufs_clk_scaling *scaling = &hba->clk_scaling;
19438c2ecf20Sopenharmony_ci
19448c2ecf20Sopenharmony_ci	if (!ufshcd_is_clkscaling_supported(hba))
19458c2ecf20Sopenharmony_ci		return;
19468c2ecf20Sopenharmony_ci
19478c2ecf20Sopenharmony_ci	if (!hba->outstanding_reqs && scaling->is_busy_started) {
19488c2ecf20Sopenharmony_ci		scaling->tot_busy_t += ktime_to_us(ktime_sub(ktime_get(),
19498c2ecf20Sopenharmony_ci					scaling->busy_start_t));
19508c2ecf20Sopenharmony_ci		scaling->busy_start_t = 0;
19518c2ecf20Sopenharmony_ci		scaling->is_busy_started = false;
19528c2ecf20Sopenharmony_ci	}
19538c2ecf20Sopenharmony_ci}
19548c2ecf20Sopenharmony_ci/**
19558c2ecf20Sopenharmony_ci * ufshcd_send_command - Send SCSI or device management commands
19568c2ecf20Sopenharmony_ci * @hba: per adapter instance
19578c2ecf20Sopenharmony_ci * @task_tag: Task tag of the command
19588c2ecf20Sopenharmony_ci */
19598c2ecf20Sopenharmony_cistatic inline
19608c2ecf20Sopenharmony_civoid ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag)
19618c2ecf20Sopenharmony_ci{
19628c2ecf20Sopenharmony_ci	struct ufshcd_lrb *lrbp = &hba->lrb[task_tag];
19638c2ecf20Sopenharmony_ci
19648c2ecf20Sopenharmony_ci	lrbp->issue_time_stamp = ktime_get();
19658c2ecf20Sopenharmony_ci	lrbp->compl_time_stamp = ktime_set(0, 0);
19668c2ecf20Sopenharmony_ci	ufshcd_vops_setup_xfer_req(hba, task_tag, (lrbp->cmd ? true : false));
19678c2ecf20Sopenharmony_ci	ufshcd_add_command_trace(hba, task_tag, "send");
19688c2ecf20Sopenharmony_ci	ufshcd_clk_scaling_start_busy(hba);
19698c2ecf20Sopenharmony_ci	__set_bit(task_tag, &hba->outstanding_reqs);
19708c2ecf20Sopenharmony_ci	ufshcd_writel(hba, 1 << task_tag, REG_UTP_TRANSFER_REQ_DOOR_BELL);
19718c2ecf20Sopenharmony_ci	/* Make sure that doorbell is committed immediately */
19728c2ecf20Sopenharmony_ci	wmb();
19738c2ecf20Sopenharmony_ci}
19748c2ecf20Sopenharmony_ci
19758c2ecf20Sopenharmony_ci/**
19768c2ecf20Sopenharmony_ci * ufshcd_copy_sense_data - Copy sense data in case of check condition
19778c2ecf20Sopenharmony_ci * @lrbp: pointer to local reference block
19788c2ecf20Sopenharmony_ci */
19798c2ecf20Sopenharmony_cistatic inline void ufshcd_copy_sense_data(struct ufshcd_lrb *lrbp)
19808c2ecf20Sopenharmony_ci{
19818c2ecf20Sopenharmony_ci	int len;
19828c2ecf20Sopenharmony_ci	if (lrbp->sense_buffer &&
19838c2ecf20Sopenharmony_ci	    ufshcd_get_rsp_upiu_data_seg_len(lrbp->ucd_rsp_ptr)) {
19848c2ecf20Sopenharmony_ci		int len_to_copy;
19858c2ecf20Sopenharmony_ci
19868c2ecf20Sopenharmony_ci		len = be16_to_cpu(lrbp->ucd_rsp_ptr->sr.sense_data_len);
19878c2ecf20Sopenharmony_ci		len_to_copy = min_t(int, UFS_SENSE_SIZE, len);
19888c2ecf20Sopenharmony_ci
19898c2ecf20Sopenharmony_ci		memcpy(lrbp->sense_buffer, lrbp->ucd_rsp_ptr->sr.sense_data,
19908c2ecf20Sopenharmony_ci		       len_to_copy);
19918c2ecf20Sopenharmony_ci	}
19928c2ecf20Sopenharmony_ci}
19938c2ecf20Sopenharmony_ci
19948c2ecf20Sopenharmony_ci/**
19958c2ecf20Sopenharmony_ci * ufshcd_copy_query_response() - Copy the Query Response and the data
19968c2ecf20Sopenharmony_ci * descriptor
19978c2ecf20Sopenharmony_ci * @hba: per adapter instance
19988c2ecf20Sopenharmony_ci * @lrbp: pointer to local reference block
19998c2ecf20Sopenharmony_ci */
20008c2ecf20Sopenharmony_cistatic
20018c2ecf20Sopenharmony_ciint ufshcd_copy_query_response(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
20028c2ecf20Sopenharmony_ci{
20038c2ecf20Sopenharmony_ci	struct ufs_query_res *query_res = &hba->dev_cmd.query.response;
20048c2ecf20Sopenharmony_ci
20058c2ecf20Sopenharmony_ci	memcpy(&query_res->upiu_res, &lrbp->ucd_rsp_ptr->qr, QUERY_OSF_SIZE);
20068c2ecf20Sopenharmony_ci
20078c2ecf20Sopenharmony_ci	/* Get the descriptor */
20088c2ecf20Sopenharmony_ci	if (hba->dev_cmd.query.descriptor &&
20098c2ecf20Sopenharmony_ci	    lrbp->ucd_rsp_ptr->qr.opcode == UPIU_QUERY_OPCODE_READ_DESC) {
20108c2ecf20Sopenharmony_ci		u8 *descp = (u8 *)lrbp->ucd_rsp_ptr +
20118c2ecf20Sopenharmony_ci				GENERAL_UPIU_REQUEST_SIZE;
20128c2ecf20Sopenharmony_ci		u16 resp_len;
20138c2ecf20Sopenharmony_ci		u16 buf_len;
20148c2ecf20Sopenharmony_ci
20158c2ecf20Sopenharmony_ci		/* data segment length */
20168c2ecf20Sopenharmony_ci		resp_len = be32_to_cpu(lrbp->ucd_rsp_ptr->header.dword_2) &
20178c2ecf20Sopenharmony_ci						MASK_QUERY_DATA_SEG_LEN;
20188c2ecf20Sopenharmony_ci		buf_len = be16_to_cpu(
20198c2ecf20Sopenharmony_ci				hba->dev_cmd.query.request.upiu_req.length);
20208c2ecf20Sopenharmony_ci		if (likely(buf_len >= resp_len)) {
20218c2ecf20Sopenharmony_ci			memcpy(hba->dev_cmd.query.descriptor, descp, resp_len);
20228c2ecf20Sopenharmony_ci		} else {
20238c2ecf20Sopenharmony_ci			dev_warn(hba->dev,
20248c2ecf20Sopenharmony_ci				 "%s: rsp size %d is bigger than buffer size %d",
20258c2ecf20Sopenharmony_ci				 __func__, resp_len, buf_len);
20268c2ecf20Sopenharmony_ci			return -EINVAL;
20278c2ecf20Sopenharmony_ci		}
20288c2ecf20Sopenharmony_ci	}
20298c2ecf20Sopenharmony_ci
20308c2ecf20Sopenharmony_ci	return 0;
20318c2ecf20Sopenharmony_ci}
20328c2ecf20Sopenharmony_ci
20338c2ecf20Sopenharmony_ci/**
20348c2ecf20Sopenharmony_ci * ufshcd_hba_capabilities - Read controller capabilities
20358c2ecf20Sopenharmony_ci * @hba: per adapter instance
20368c2ecf20Sopenharmony_ci *
20378c2ecf20Sopenharmony_ci * Return: 0 on success, negative on error.
20388c2ecf20Sopenharmony_ci */
20398c2ecf20Sopenharmony_cistatic inline int ufshcd_hba_capabilities(struct ufs_hba *hba)
20408c2ecf20Sopenharmony_ci{
20418c2ecf20Sopenharmony_ci	int err;
20428c2ecf20Sopenharmony_ci
20438c2ecf20Sopenharmony_ci	hba->capabilities = ufshcd_readl(hba, REG_CONTROLLER_CAPABILITIES);
20448c2ecf20Sopenharmony_ci
20458c2ecf20Sopenharmony_ci	/* nutrs and nutmrs are 0 based values */
20468c2ecf20Sopenharmony_ci	hba->nutrs = (hba->capabilities & MASK_TRANSFER_REQUESTS_SLOTS) + 1;
20478c2ecf20Sopenharmony_ci	hba->nutmrs =
20488c2ecf20Sopenharmony_ci	((hba->capabilities & MASK_TASK_MANAGEMENT_REQUEST_SLOTS) >> 16) + 1;
20498c2ecf20Sopenharmony_ci
20508c2ecf20Sopenharmony_ci	/* Read crypto capabilities */
20518c2ecf20Sopenharmony_ci	err = ufshcd_hba_init_crypto_capabilities(hba);
20528c2ecf20Sopenharmony_ci	if (err)
20538c2ecf20Sopenharmony_ci		dev_err(hba->dev, "crypto setup failed\n");
20548c2ecf20Sopenharmony_ci
20558c2ecf20Sopenharmony_ci	return err;
20568c2ecf20Sopenharmony_ci}
20578c2ecf20Sopenharmony_ci
20588c2ecf20Sopenharmony_ci/**
20598c2ecf20Sopenharmony_ci * ufshcd_ready_for_uic_cmd - Check if controller is ready
20608c2ecf20Sopenharmony_ci *                            to accept UIC commands
20618c2ecf20Sopenharmony_ci * @hba: per adapter instance
20628c2ecf20Sopenharmony_ci * Return true on success, else false
20638c2ecf20Sopenharmony_ci */
20648c2ecf20Sopenharmony_cistatic inline bool ufshcd_ready_for_uic_cmd(struct ufs_hba *hba)
20658c2ecf20Sopenharmony_ci{
20668c2ecf20Sopenharmony_ci	if (ufshcd_readl(hba, REG_CONTROLLER_STATUS) & UIC_COMMAND_READY)
20678c2ecf20Sopenharmony_ci		return true;
20688c2ecf20Sopenharmony_ci	else
20698c2ecf20Sopenharmony_ci		return false;
20708c2ecf20Sopenharmony_ci}
20718c2ecf20Sopenharmony_ci
20728c2ecf20Sopenharmony_ci/**
20738c2ecf20Sopenharmony_ci * ufshcd_get_upmcrs - Get the power mode change request status
20748c2ecf20Sopenharmony_ci * @hba: Pointer to adapter instance
20758c2ecf20Sopenharmony_ci *
20768c2ecf20Sopenharmony_ci * This function gets the UPMCRS field of HCS register
20778c2ecf20Sopenharmony_ci * Returns value of UPMCRS field
20788c2ecf20Sopenharmony_ci */
20798c2ecf20Sopenharmony_cistatic inline u8 ufshcd_get_upmcrs(struct ufs_hba *hba)
20808c2ecf20Sopenharmony_ci{
20818c2ecf20Sopenharmony_ci	return (ufshcd_readl(hba, REG_CONTROLLER_STATUS) >> 8) & 0x7;
20828c2ecf20Sopenharmony_ci}
20838c2ecf20Sopenharmony_ci
20848c2ecf20Sopenharmony_ci/**
20858c2ecf20Sopenharmony_ci * ufshcd_dispatch_uic_cmd - Dispatch UIC commands to unipro layers
20868c2ecf20Sopenharmony_ci * @hba: per adapter instance
20878c2ecf20Sopenharmony_ci * @uic_cmd: UIC command
20888c2ecf20Sopenharmony_ci *
20898c2ecf20Sopenharmony_ci * Mutex must be held.
20908c2ecf20Sopenharmony_ci */
20918c2ecf20Sopenharmony_cistatic inline void
20928c2ecf20Sopenharmony_ciufshcd_dispatch_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd)
20938c2ecf20Sopenharmony_ci{
20948c2ecf20Sopenharmony_ci	WARN_ON(hba->active_uic_cmd);
20958c2ecf20Sopenharmony_ci
20968c2ecf20Sopenharmony_ci	hba->active_uic_cmd = uic_cmd;
20978c2ecf20Sopenharmony_ci
20988c2ecf20Sopenharmony_ci	/* Write Args */
20998c2ecf20Sopenharmony_ci	ufshcd_writel(hba, uic_cmd->argument1, REG_UIC_COMMAND_ARG_1);
21008c2ecf20Sopenharmony_ci	ufshcd_writel(hba, uic_cmd->argument2, REG_UIC_COMMAND_ARG_2);
21018c2ecf20Sopenharmony_ci	ufshcd_writel(hba, uic_cmd->argument3, REG_UIC_COMMAND_ARG_3);
21028c2ecf20Sopenharmony_ci
21038c2ecf20Sopenharmony_ci	ufshcd_add_uic_command_trace(hba, uic_cmd, "send");
21048c2ecf20Sopenharmony_ci
21058c2ecf20Sopenharmony_ci	/* Write UIC Cmd */
21068c2ecf20Sopenharmony_ci	ufshcd_writel(hba, uic_cmd->command & COMMAND_OPCODE_MASK,
21078c2ecf20Sopenharmony_ci		      REG_UIC_COMMAND);
21088c2ecf20Sopenharmony_ci}
21098c2ecf20Sopenharmony_ci
21108c2ecf20Sopenharmony_ci/**
21118c2ecf20Sopenharmony_ci * ufshcd_wait_for_uic_cmd - Wait complectioin of UIC command
21128c2ecf20Sopenharmony_ci * @hba: per adapter instance
21138c2ecf20Sopenharmony_ci * @uic_cmd: UIC command
21148c2ecf20Sopenharmony_ci *
21158c2ecf20Sopenharmony_ci * Must be called with mutex held.
21168c2ecf20Sopenharmony_ci * Returns 0 only if success.
21178c2ecf20Sopenharmony_ci */
21188c2ecf20Sopenharmony_cistatic int
21198c2ecf20Sopenharmony_ciufshcd_wait_for_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd)
21208c2ecf20Sopenharmony_ci{
21218c2ecf20Sopenharmony_ci	int ret;
21228c2ecf20Sopenharmony_ci	unsigned long flags;
21238c2ecf20Sopenharmony_ci
21248c2ecf20Sopenharmony_ci	if (wait_for_completion_timeout(&uic_cmd->done,
21258c2ecf20Sopenharmony_ci					msecs_to_jiffies(UIC_CMD_TIMEOUT))) {
21268c2ecf20Sopenharmony_ci		ret = uic_cmd->argument2 & MASK_UIC_COMMAND_RESULT;
21278c2ecf20Sopenharmony_ci	} else {
21288c2ecf20Sopenharmony_ci		ret = -ETIMEDOUT;
21298c2ecf20Sopenharmony_ci		dev_err(hba->dev,
21308c2ecf20Sopenharmony_ci			"uic cmd 0x%x with arg3 0x%x completion timeout\n",
21318c2ecf20Sopenharmony_ci			uic_cmd->command, uic_cmd->argument3);
21328c2ecf20Sopenharmony_ci
21338c2ecf20Sopenharmony_ci		if (!uic_cmd->cmd_active) {
21348c2ecf20Sopenharmony_ci			dev_err(hba->dev, "%s: UIC cmd has been completed, return the result\n",
21358c2ecf20Sopenharmony_ci				__func__);
21368c2ecf20Sopenharmony_ci			ret = uic_cmd->argument2 & MASK_UIC_COMMAND_RESULT;
21378c2ecf20Sopenharmony_ci		}
21388c2ecf20Sopenharmony_ci	}
21398c2ecf20Sopenharmony_ci
21408c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
21418c2ecf20Sopenharmony_ci	hba->active_uic_cmd = NULL;
21428c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
21438c2ecf20Sopenharmony_ci
21448c2ecf20Sopenharmony_ci	return ret;
21458c2ecf20Sopenharmony_ci}
21468c2ecf20Sopenharmony_ci
21478c2ecf20Sopenharmony_ci/**
21488c2ecf20Sopenharmony_ci * __ufshcd_send_uic_cmd - Send UIC commands and retrieve the result
21498c2ecf20Sopenharmony_ci * @hba: per adapter instance
21508c2ecf20Sopenharmony_ci * @uic_cmd: UIC command
21518c2ecf20Sopenharmony_ci * @completion: initialize the completion only if this is set to true
21528c2ecf20Sopenharmony_ci *
21538c2ecf20Sopenharmony_ci * Identical to ufshcd_send_uic_cmd() expect mutex. Must be called
21548c2ecf20Sopenharmony_ci * with mutex held and host_lock locked.
21558c2ecf20Sopenharmony_ci * Returns 0 only if success.
21568c2ecf20Sopenharmony_ci */
21578c2ecf20Sopenharmony_cistatic int
21588c2ecf20Sopenharmony_ci__ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd,
21598c2ecf20Sopenharmony_ci		      bool completion)
21608c2ecf20Sopenharmony_ci{
21618c2ecf20Sopenharmony_ci	if (!ufshcd_ready_for_uic_cmd(hba)) {
21628c2ecf20Sopenharmony_ci		dev_err(hba->dev,
21638c2ecf20Sopenharmony_ci			"Controller not ready to accept UIC commands\n");
21648c2ecf20Sopenharmony_ci		return -EIO;
21658c2ecf20Sopenharmony_ci	}
21668c2ecf20Sopenharmony_ci
21678c2ecf20Sopenharmony_ci	if (completion)
21688c2ecf20Sopenharmony_ci		init_completion(&uic_cmd->done);
21698c2ecf20Sopenharmony_ci
21708c2ecf20Sopenharmony_ci	uic_cmd->cmd_active = 1;
21718c2ecf20Sopenharmony_ci	ufshcd_dispatch_uic_cmd(hba, uic_cmd);
21728c2ecf20Sopenharmony_ci
21738c2ecf20Sopenharmony_ci	return 0;
21748c2ecf20Sopenharmony_ci}
21758c2ecf20Sopenharmony_ci
21768c2ecf20Sopenharmony_ci/**
21778c2ecf20Sopenharmony_ci * ufshcd_send_uic_cmd - Send UIC commands and retrieve the result
21788c2ecf20Sopenharmony_ci * @hba: per adapter instance
21798c2ecf20Sopenharmony_ci * @uic_cmd: UIC command
21808c2ecf20Sopenharmony_ci *
21818c2ecf20Sopenharmony_ci * Returns 0 only if success.
21828c2ecf20Sopenharmony_ci */
21838c2ecf20Sopenharmony_ciint ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd)
21848c2ecf20Sopenharmony_ci{
21858c2ecf20Sopenharmony_ci	int ret;
21868c2ecf20Sopenharmony_ci	unsigned long flags;
21878c2ecf20Sopenharmony_ci
21888c2ecf20Sopenharmony_ci	ufshcd_hold(hba, false);
21898c2ecf20Sopenharmony_ci	mutex_lock(&hba->uic_cmd_mutex);
21908c2ecf20Sopenharmony_ci	ufshcd_add_delay_before_dme_cmd(hba);
21918c2ecf20Sopenharmony_ci
21928c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
21938c2ecf20Sopenharmony_ci	ret = __ufshcd_send_uic_cmd(hba, uic_cmd, true);
21948c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
21958c2ecf20Sopenharmony_ci	if (!ret)
21968c2ecf20Sopenharmony_ci		ret = ufshcd_wait_for_uic_cmd(hba, uic_cmd);
21978c2ecf20Sopenharmony_ci
21988c2ecf20Sopenharmony_ci	mutex_unlock(&hba->uic_cmd_mutex);
21998c2ecf20Sopenharmony_ci
22008c2ecf20Sopenharmony_ci	ufshcd_release(hba);
22018c2ecf20Sopenharmony_ci	return ret;
22028c2ecf20Sopenharmony_ci}
22038c2ecf20Sopenharmony_ci
22048c2ecf20Sopenharmony_ci/**
22058c2ecf20Sopenharmony_ci * ufshcd_map_sg - Map scatter-gather list to prdt
22068c2ecf20Sopenharmony_ci * @hba: per adapter instance
22078c2ecf20Sopenharmony_ci * @lrbp: pointer to local reference block
22088c2ecf20Sopenharmony_ci *
22098c2ecf20Sopenharmony_ci * Returns 0 in case of success, non-zero value in case of failure
22108c2ecf20Sopenharmony_ci */
22118c2ecf20Sopenharmony_cistatic int ufshcd_map_sg(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
22128c2ecf20Sopenharmony_ci{
22138c2ecf20Sopenharmony_ci	struct ufshcd_sg_entry *prd_table;
22148c2ecf20Sopenharmony_ci	struct scatterlist *sg;
22158c2ecf20Sopenharmony_ci	struct scsi_cmnd *cmd;
22168c2ecf20Sopenharmony_ci	int sg_segments;
22178c2ecf20Sopenharmony_ci	int i;
22188c2ecf20Sopenharmony_ci
22198c2ecf20Sopenharmony_ci	cmd = lrbp->cmd;
22208c2ecf20Sopenharmony_ci	sg_segments = scsi_dma_map(cmd);
22218c2ecf20Sopenharmony_ci	if (sg_segments < 0)
22228c2ecf20Sopenharmony_ci		return sg_segments;
22238c2ecf20Sopenharmony_ci
22248c2ecf20Sopenharmony_ci	if (sg_segments) {
22258c2ecf20Sopenharmony_ci
22268c2ecf20Sopenharmony_ci		if (hba->quirks & UFSHCD_QUIRK_PRDT_BYTE_GRAN)
22278c2ecf20Sopenharmony_ci			lrbp->utr_descriptor_ptr->prd_table_length =
22288c2ecf20Sopenharmony_ci				cpu_to_le16((sg_segments *
22298c2ecf20Sopenharmony_ci					sizeof(struct ufshcd_sg_entry)));
22308c2ecf20Sopenharmony_ci		else
22318c2ecf20Sopenharmony_ci			lrbp->utr_descriptor_ptr->prd_table_length =
22328c2ecf20Sopenharmony_ci				cpu_to_le16((u16) (sg_segments));
22338c2ecf20Sopenharmony_ci
22348c2ecf20Sopenharmony_ci		prd_table = (struct ufshcd_sg_entry *)lrbp->ucd_prdt_ptr;
22358c2ecf20Sopenharmony_ci
22368c2ecf20Sopenharmony_ci		scsi_for_each_sg(cmd, sg, sg_segments, i) {
22378c2ecf20Sopenharmony_ci			prd_table[i].size  =
22388c2ecf20Sopenharmony_ci				cpu_to_le32(((u32) sg_dma_len(sg))-1);
22398c2ecf20Sopenharmony_ci			prd_table[i].base_addr =
22408c2ecf20Sopenharmony_ci				cpu_to_le32(lower_32_bits(sg->dma_address));
22418c2ecf20Sopenharmony_ci			prd_table[i].upper_addr =
22428c2ecf20Sopenharmony_ci				cpu_to_le32(upper_32_bits(sg->dma_address));
22438c2ecf20Sopenharmony_ci			prd_table[i].reserved = 0;
22448c2ecf20Sopenharmony_ci		}
22458c2ecf20Sopenharmony_ci	} else {
22468c2ecf20Sopenharmony_ci		lrbp->utr_descriptor_ptr->prd_table_length = 0;
22478c2ecf20Sopenharmony_ci	}
22488c2ecf20Sopenharmony_ci
22498c2ecf20Sopenharmony_ci	return 0;
22508c2ecf20Sopenharmony_ci}
22518c2ecf20Sopenharmony_ci
22528c2ecf20Sopenharmony_ci/**
22538c2ecf20Sopenharmony_ci * ufshcd_enable_intr - enable interrupts
22548c2ecf20Sopenharmony_ci * @hba: per adapter instance
22558c2ecf20Sopenharmony_ci * @intrs: interrupt bits
22568c2ecf20Sopenharmony_ci */
22578c2ecf20Sopenharmony_cistatic void ufshcd_enable_intr(struct ufs_hba *hba, u32 intrs)
22588c2ecf20Sopenharmony_ci{
22598c2ecf20Sopenharmony_ci	u32 set = ufshcd_readl(hba, REG_INTERRUPT_ENABLE);
22608c2ecf20Sopenharmony_ci
22618c2ecf20Sopenharmony_ci	if (hba->ufs_version == UFSHCI_VERSION_10) {
22628c2ecf20Sopenharmony_ci		u32 rw;
22638c2ecf20Sopenharmony_ci		rw = set & INTERRUPT_MASK_RW_VER_10;
22648c2ecf20Sopenharmony_ci		set = rw | ((set ^ intrs) & intrs);
22658c2ecf20Sopenharmony_ci	} else {
22668c2ecf20Sopenharmony_ci		set |= intrs;
22678c2ecf20Sopenharmony_ci	}
22688c2ecf20Sopenharmony_ci
22698c2ecf20Sopenharmony_ci	ufshcd_writel(hba, set, REG_INTERRUPT_ENABLE);
22708c2ecf20Sopenharmony_ci}
22718c2ecf20Sopenharmony_ci
22728c2ecf20Sopenharmony_ci/**
22738c2ecf20Sopenharmony_ci * ufshcd_disable_intr - disable interrupts
22748c2ecf20Sopenharmony_ci * @hba: per adapter instance
22758c2ecf20Sopenharmony_ci * @intrs: interrupt bits
22768c2ecf20Sopenharmony_ci */
22778c2ecf20Sopenharmony_cistatic void ufshcd_disable_intr(struct ufs_hba *hba, u32 intrs)
22788c2ecf20Sopenharmony_ci{
22798c2ecf20Sopenharmony_ci	u32 set = ufshcd_readl(hba, REG_INTERRUPT_ENABLE);
22808c2ecf20Sopenharmony_ci
22818c2ecf20Sopenharmony_ci	if (hba->ufs_version == UFSHCI_VERSION_10) {
22828c2ecf20Sopenharmony_ci		u32 rw;
22838c2ecf20Sopenharmony_ci		rw = (set & INTERRUPT_MASK_RW_VER_10) &
22848c2ecf20Sopenharmony_ci			~(intrs & INTERRUPT_MASK_RW_VER_10);
22858c2ecf20Sopenharmony_ci		set = rw | ((set & intrs) & ~INTERRUPT_MASK_RW_VER_10);
22868c2ecf20Sopenharmony_ci
22878c2ecf20Sopenharmony_ci	} else {
22888c2ecf20Sopenharmony_ci		set &= ~intrs;
22898c2ecf20Sopenharmony_ci	}
22908c2ecf20Sopenharmony_ci
22918c2ecf20Sopenharmony_ci	ufshcd_writel(hba, set, REG_INTERRUPT_ENABLE);
22928c2ecf20Sopenharmony_ci}
22938c2ecf20Sopenharmony_ci
22948c2ecf20Sopenharmony_ci/**
22958c2ecf20Sopenharmony_ci * ufshcd_prepare_req_desc_hdr() - Fills the requests header
22968c2ecf20Sopenharmony_ci * descriptor according to request
22978c2ecf20Sopenharmony_ci * @lrbp: pointer to local reference block
22988c2ecf20Sopenharmony_ci * @upiu_flags: flags required in the header
22998c2ecf20Sopenharmony_ci * @cmd_dir: requests data direction
23008c2ecf20Sopenharmony_ci */
23018c2ecf20Sopenharmony_cistatic void ufshcd_prepare_req_desc_hdr(struct ufshcd_lrb *lrbp,
23028c2ecf20Sopenharmony_ci			u8 *upiu_flags, enum dma_data_direction cmd_dir)
23038c2ecf20Sopenharmony_ci{
23048c2ecf20Sopenharmony_ci	struct utp_transfer_req_desc *req_desc = lrbp->utr_descriptor_ptr;
23058c2ecf20Sopenharmony_ci	u32 data_direction;
23068c2ecf20Sopenharmony_ci	u32 dword_0;
23078c2ecf20Sopenharmony_ci	u32 dword_1 = 0;
23088c2ecf20Sopenharmony_ci	u32 dword_3 = 0;
23098c2ecf20Sopenharmony_ci
23108c2ecf20Sopenharmony_ci	if (cmd_dir == DMA_FROM_DEVICE) {
23118c2ecf20Sopenharmony_ci		data_direction = UTP_DEVICE_TO_HOST;
23128c2ecf20Sopenharmony_ci		*upiu_flags = UPIU_CMD_FLAGS_READ;
23138c2ecf20Sopenharmony_ci	} else if (cmd_dir == DMA_TO_DEVICE) {
23148c2ecf20Sopenharmony_ci		data_direction = UTP_HOST_TO_DEVICE;
23158c2ecf20Sopenharmony_ci		*upiu_flags = UPIU_CMD_FLAGS_WRITE;
23168c2ecf20Sopenharmony_ci	} else {
23178c2ecf20Sopenharmony_ci		data_direction = UTP_NO_DATA_TRANSFER;
23188c2ecf20Sopenharmony_ci		*upiu_flags = UPIU_CMD_FLAGS_NONE;
23198c2ecf20Sopenharmony_ci	}
23208c2ecf20Sopenharmony_ci
23218c2ecf20Sopenharmony_ci	dword_0 = data_direction | (lrbp->command_type
23228c2ecf20Sopenharmony_ci				<< UPIU_COMMAND_TYPE_OFFSET);
23238c2ecf20Sopenharmony_ci	if (lrbp->intr_cmd)
23248c2ecf20Sopenharmony_ci		dword_0 |= UTP_REQ_DESC_INT_CMD;
23258c2ecf20Sopenharmony_ci
23268c2ecf20Sopenharmony_ci	/* Prepare crypto related dwords */
23278c2ecf20Sopenharmony_ci	ufshcd_prepare_req_desc_hdr_crypto(lrbp, &dword_0, &dword_1, &dword_3);
23288c2ecf20Sopenharmony_ci
23298c2ecf20Sopenharmony_ci	/* Transfer request descriptor header fields */
23308c2ecf20Sopenharmony_ci	req_desc->header.dword_0 = cpu_to_le32(dword_0);
23318c2ecf20Sopenharmony_ci	req_desc->header.dword_1 = cpu_to_le32(dword_1);
23328c2ecf20Sopenharmony_ci	/*
23338c2ecf20Sopenharmony_ci	 * assigning invalid value for command status. Controller
23348c2ecf20Sopenharmony_ci	 * updates OCS on command completion, with the command
23358c2ecf20Sopenharmony_ci	 * status
23368c2ecf20Sopenharmony_ci	 */
23378c2ecf20Sopenharmony_ci	req_desc->header.dword_2 =
23388c2ecf20Sopenharmony_ci		cpu_to_le32(OCS_INVALID_COMMAND_STATUS);
23398c2ecf20Sopenharmony_ci	req_desc->header.dword_3 = cpu_to_le32(dword_3);
23408c2ecf20Sopenharmony_ci
23418c2ecf20Sopenharmony_ci	req_desc->prd_table_length = 0;
23428c2ecf20Sopenharmony_ci}
23438c2ecf20Sopenharmony_ci
23448c2ecf20Sopenharmony_ci/**
23458c2ecf20Sopenharmony_ci * ufshcd_prepare_utp_scsi_cmd_upiu() - fills the utp_transfer_req_desc,
23468c2ecf20Sopenharmony_ci * for scsi commands
23478c2ecf20Sopenharmony_ci * @lrbp: local reference block pointer
23488c2ecf20Sopenharmony_ci * @upiu_flags: flags
23498c2ecf20Sopenharmony_ci */
23508c2ecf20Sopenharmony_cistatic
23518c2ecf20Sopenharmony_civoid ufshcd_prepare_utp_scsi_cmd_upiu(struct ufshcd_lrb *lrbp, u8 upiu_flags)
23528c2ecf20Sopenharmony_ci{
23538c2ecf20Sopenharmony_ci	struct scsi_cmnd *cmd = lrbp->cmd;
23548c2ecf20Sopenharmony_ci	struct utp_upiu_req *ucd_req_ptr = lrbp->ucd_req_ptr;
23558c2ecf20Sopenharmony_ci	unsigned short cdb_len;
23568c2ecf20Sopenharmony_ci
23578c2ecf20Sopenharmony_ci	/* command descriptor fields */
23588c2ecf20Sopenharmony_ci	ucd_req_ptr->header.dword_0 = UPIU_HEADER_DWORD(
23598c2ecf20Sopenharmony_ci				UPIU_TRANSACTION_COMMAND, upiu_flags,
23608c2ecf20Sopenharmony_ci				lrbp->lun, lrbp->task_tag);
23618c2ecf20Sopenharmony_ci	ucd_req_ptr->header.dword_1 = UPIU_HEADER_DWORD(
23628c2ecf20Sopenharmony_ci				UPIU_COMMAND_SET_TYPE_SCSI, 0, 0, 0);
23638c2ecf20Sopenharmony_ci
23648c2ecf20Sopenharmony_ci	/* Total EHS length and Data segment length will be zero */
23658c2ecf20Sopenharmony_ci	ucd_req_ptr->header.dword_2 = 0;
23668c2ecf20Sopenharmony_ci
23678c2ecf20Sopenharmony_ci	ucd_req_ptr->sc.exp_data_transfer_len = cpu_to_be32(cmd->sdb.length);
23688c2ecf20Sopenharmony_ci
23698c2ecf20Sopenharmony_ci	cdb_len = min_t(unsigned short, cmd->cmd_len, UFS_CDB_SIZE);
23708c2ecf20Sopenharmony_ci	memset(ucd_req_ptr->sc.cdb, 0, UFS_CDB_SIZE);
23718c2ecf20Sopenharmony_ci	memcpy(ucd_req_ptr->sc.cdb, cmd->cmnd, cdb_len);
23728c2ecf20Sopenharmony_ci
23738c2ecf20Sopenharmony_ci	memset(lrbp->ucd_rsp_ptr, 0, sizeof(struct utp_upiu_rsp));
23748c2ecf20Sopenharmony_ci}
23758c2ecf20Sopenharmony_ci
23768c2ecf20Sopenharmony_ci/**
23778c2ecf20Sopenharmony_ci * ufshcd_prepare_utp_query_req_upiu() - fills the utp_transfer_req_desc,
23788c2ecf20Sopenharmony_ci * for query requsts
23798c2ecf20Sopenharmony_ci * @hba: UFS hba
23808c2ecf20Sopenharmony_ci * @lrbp: local reference block pointer
23818c2ecf20Sopenharmony_ci * @upiu_flags: flags
23828c2ecf20Sopenharmony_ci */
23838c2ecf20Sopenharmony_cistatic void ufshcd_prepare_utp_query_req_upiu(struct ufs_hba *hba,
23848c2ecf20Sopenharmony_ci				struct ufshcd_lrb *lrbp, u8 upiu_flags)
23858c2ecf20Sopenharmony_ci{
23868c2ecf20Sopenharmony_ci	struct utp_upiu_req *ucd_req_ptr = lrbp->ucd_req_ptr;
23878c2ecf20Sopenharmony_ci	struct ufs_query *query = &hba->dev_cmd.query;
23888c2ecf20Sopenharmony_ci	u16 len = be16_to_cpu(query->request.upiu_req.length);
23898c2ecf20Sopenharmony_ci
23908c2ecf20Sopenharmony_ci	/* Query request header */
23918c2ecf20Sopenharmony_ci	ucd_req_ptr->header.dword_0 = UPIU_HEADER_DWORD(
23928c2ecf20Sopenharmony_ci			UPIU_TRANSACTION_QUERY_REQ, upiu_flags,
23938c2ecf20Sopenharmony_ci			lrbp->lun, lrbp->task_tag);
23948c2ecf20Sopenharmony_ci	ucd_req_ptr->header.dword_1 = UPIU_HEADER_DWORD(
23958c2ecf20Sopenharmony_ci			0, query->request.query_func, 0, 0);
23968c2ecf20Sopenharmony_ci
23978c2ecf20Sopenharmony_ci	/* Data segment length only need for WRITE_DESC */
23988c2ecf20Sopenharmony_ci	if (query->request.upiu_req.opcode == UPIU_QUERY_OPCODE_WRITE_DESC)
23998c2ecf20Sopenharmony_ci		ucd_req_ptr->header.dword_2 =
24008c2ecf20Sopenharmony_ci			UPIU_HEADER_DWORD(0, 0, (len >> 8), (u8)len);
24018c2ecf20Sopenharmony_ci	else
24028c2ecf20Sopenharmony_ci		ucd_req_ptr->header.dword_2 = 0;
24038c2ecf20Sopenharmony_ci
24048c2ecf20Sopenharmony_ci	/* Copy the Query Request buffer as is */
24058c2ecf20Sopenharmony_ci	memcpy(&ucd_req_ptr->qr, &query->request.upiu_req,
24068c2ecf20Sopenharmony_ci			QUERY_OSF_SIZE);
24078c2ecf20Sopenharmony_ci
24088c2ecf20Sopenharmony_ci	/* Copy the Descriptor */
24098c2ecf20Sopenharmony_ci	if (query->request.upiu_req.opcode == UPIU_QUERY_OPCODE_WRITE_DESC)
24108c2ecf20Sopenharmony_ci		memcpy(ucd_req_ptr + 1, query->descriptor, len);
24118c2ecf20Sopenharmony_ci
24128c2ecf20Sopenharmony_ci	memset(lrbp->ucd_rsp_ptr, 0, sizeof(struct utp_upiu_rsp));
24138c2ecf20Sopenharmony_ci}
24148c2ecf20Sopenharmony_ci
24158c2ecf20Sopenharmony_cistatic inline void ufshcd_prepare_utp_nop_upiu(struct ufshcd_lrb *lrbp)
24168c2ecf20Sopenharmony_ci{
24178c2ecf20Sopenharmony_ci	struct utp_upiu_req *ucd_req_ptr = lrbp->ucd_req_ptr;
24188c2ecf20Sopenharmony_ci
24198c2ecf20Sopenharmony_ci	memset(ucd_req_ptr, 0, sizeof(struct utp_upiu_req));
24208c2ecf20Sopenharmony_ci
24218c2ecf20Sopenharmony_ci	/* command descriptor fields */
24228c2ecf20Sopenharmony_ci	ucd_req_ptr->header.dword_0 =
24238c2ecf20Sopenharmony_ci		UPIU_HEADER_DWORD(
24248c2ecf20Sopenharmony_ci			UPIU_TRANSACTION_NOP_OUT, 0, 0, lrbp->task_tag);
24258c2ecf20Sopenharmony_ci	/* clear rest of the fields of basic header */
24268c2ecf20Sopenharmony_ci	ucd_req_ptr->header.dword_1 = 0;
24278c2ecf20Sopenharmony_ci	ucd_req_ptr->header.dword_2 = 0;
24288c2ecf20Sopenharmony_ci
24298c2ecf20Sopenharmony_ci	memset(lrbp->ucd_rsp_ptr, 0, sizeof(struct utp_upiu_rsp));
24308c2ecf20Sopenharmony_ci}
24318c2ecf20Sopenharmony_ci
24328c2ecf20Sopenharmony_ci/**
24338c2ecf20Sopenharmony_ci * ufshcd_compose_devman_upiu - UFS Protocol Information Unit(UPIU)
24348c2ecf20Sopenharmony_ci *			     for Device Management Purposes
24358c2ecf20Sopenharmony_ci * @hba: per adapter instance
24368c2ecf20Sopenharmony_ci * @lrbp: pointer to local reference block
24378c2ecf20Sopenharmony_ci */
24388c2ecf20Sopenharmony_cistatic int ufshcd_compose_devman_upiu(struct ufs_hba *hba,
24398c2ecf20Sopenharmony_ci				      struct ufshcd_lrb *lrbp)
24408c2ecf20Sopenharmony_ci{
24418c2ecf20Sopenharmony_ci	u8 upiu_flags;
24428c2ecf20Sopenharmony_ci	int ret = 0;
24438c2ecf20Sopenharmony_ci
24448c2ecf20Sopenharmony_ci	if ((hba->ufs_version == UFSHCI_VERSION_10) ||
24458c2ecf20Sopenharmony_ci	    (hba->ufs_version == UFSHCI_VERSION_11))
24468c2ecf20Sopenharmony_ci		lrbp->command_type = UTP_CMD_TYPE_DEV_MANAGE;
24478c2ecf20Sopenharmony_ci	else
24488c2ecf20Sopenharmony_ci		lrbp->command_type = UTP_CMD_TYPE_UFS_STORAGE;
24498c2ecf20Sopenharmony_ci
24508c2ecf20Sopenharmony_ci	ufshcd_prepare_req_desc_hdr(lrbp, &upiu_flags, DMA_NONE);
24518c2ecf20Sopenharmony_ci	if (hba->dev_cmd.type == DEV_CMD_TYPE_QUERY)
24528c2ecf20Sopenharmony_ci		ufshcd_prepare_utp_query_req_upiu(hba, lrbp, upiu_flags);
24538c2ecf20Sopenharmony_ci	else if (hba->dev_cmd.type == DEV_CMD_TYPE_NOP)
24548c2ecf20Sopenharmony_ci		ufshcd_prepare_utp_nop_upiu(lrbp);
24558c2ecf20Sopenharmony_ci	else
24568c2ecf20Sopenharmony_ci		ret = -EINVAL;
24578c2ecf20Sopenharmony_ci
24588c2ecf20Sopenharmony_ci	return ret;
24598c2ecf20Sopenharmony_ci}
24608c2ecf20Sopenharmony_ci
24618c2ecf20Sopenharmony_ci/**
24628c2ecf20Sopenharmony_ci * ufshcd_comp_scsi_upiu - UFS Protocol Information Unit(UPIU)
24638c2ecf20Sopenharmony_ci *			   for SCSI Purposes
24648c2ecf20Sopenharmony_ci * @hba: per adapter instance
24658c2ecf20Sopenharmony_ci * @lrbp: pointer to local reference block
24668c2ecf20Sopenharmony_ci */
24678c2ecf20Sopenharmony_cistatic int ufshcd_comp_scsi_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
24688c2ecf20Sopenharmony_ci{
24698c2ecf20Sopenharmony_ci	u8 upiu_flags;
24708c2ecf20Sopenharmony_ci	int ret = 0;
24718c2ecf20Sopenharmony_ci
24728c2ecf20Sopenharmony_ci	if ((hba->ufs_version == UFSHCI_VERSION_10) ||
24738c2ecf20Sopenharmony_ci	    (hba->ufs_version == UFSHCI_VERSION_11))
24748c2ecf20Sopenharmony_ci		lrbp->command_type = UTP_CMD_TYPE_SCSI;
24758c2ecf20Sopenharmony_ci	else
24768c2ecf20Sopenharmony_ci		lrbp->command_type = UTP_CMD_TYPE_UFS_STORAGE;
24778c2ecf20Sopenharmony_ci
24788c2ecf20Sopenharmony_ci	if (likely(lrbp->cmd)) {
24798c2ecf20Sopenharmony_ci		ufshcd_prepare_req_desc_hdr(lrbp, &upiu_flags,
24808c2ecf20Sopenharmony_ci						lrbp->cmd->sc_data_direction);
24818c2ecf20Sopenharmony_ci		ufshcd_prepare_utp_scsi_cmd_upiu(lrbp, upiu_flags);
24828c2ecf20Sopenharmony_ci	} else {
24838c2ecf20Sopenharmony_ci		ret = -EINVAL;
24848c2ecf20Sopenharmony_ci	}
24858c2ecf20Sopenharmony_ci
24868c2ecf20Sopenharmony_ci	return ret;
24878c2ecf20Sopenharmony_ci}
24888c2ecf20Sopenharmony_ci
24898c2ecf20Sopenharmony_ci/**
24908c2ecf20Sopenharmony_ci * ufshcd_upiu_wlun_to_scsi_wlun - maps UPIU W-LUN id to SCSI W-LUN ID
24918c2ecf20Sopenharmony_ci * @upiu_wlun_id: UPIU W-LUN id
24928c2ecf20Sopenharmony_ci *
24938c2ecf20Sopenharmony_ci * Returns SCSI W-LUN id
24948c2ecf20Sopenharmony_ci */
24958c2ecf20Sopenharmony_cistatic inline u16 ufshcd_upiu_wlun_to_scsi_wlun(u8 upiu_wlun_id)
24968c2ecf20Sopenharmony_ci{
24978c2ecf20Sopenharmony_ci	return (upiu_wlun_id & ~UFS_UPIU_WLUN_ID) | SCSI_W_LUN_BASE;
24988c2ecf20Sopenharmony_ci}
24998c2ecf20Sopenharmony_ci
25008c2ecf20Sopenharmony_cistatic void ufshcd_init_lrb(struct ufs_hba *hba, struct ufshcd_lrb *lrb, int i)
25018c2ecf20Sopenharmony_ci{
25028c2ecf20Sopenharmony_ci	struct utp_transfer_cmd_desc *cmd_descp = hba->ucdl_base_addr;
25038c2ecf20Sopenharmony_ci	struct utp_transfer_req_desc *utrdlp = hba->utrdl_base_addr;
25048c2ecf20Sopenharmony_ci	dma_addr_t cmd_desc_element_addr = hba->ucdl_dma_addr +
25058c2ecf20Sopenharmony_ci		i * sizeof(struct utp_transfer_cmd_desc);
25068c2ecf20Sopenharmony_ci	u16 response_offset = offsetof(struct utp_transfer_cmd_desc,
25078c2ecf20Sopenharmony_ci				       response_upiu);
25088c2ecf20Sopenharmony_ci	u16 prdt_offset = offsetof(struct utp_transfer_cmd_desc, prd_table);
25098c2ecf20Sopenharmony_ci
25108c2ecf20Sopenharmony_ci	lrb->utr_descriptor_ptr = utrdlp + i;
25118c2ecf20Sopenharmony_ci	lrb->utrd_dma_addr = hba->utrdl_dma_addr +
25128c2ecf20Sopenharmony_ci		i * sizeof(struct utp_transfer_req_desc);
25138c2ecf20Sopenharmony_ci	lrb->ucd_req_ptr = (struct utp_upiu_req *)(cmd_descp + i);
25148c2ecf20Sopenharmony_ci	lrb->ucd_req_dma_addr = cmd_desc_element_addr;
25158c2ecf20Sopenharmony_ci	lrb->ucd_rsp_ptr = (struct utp_upiu_rsp *)cmd_descp[i].response_upiu;
25168c2ecf20Sopenharmony_ci	lrb->ucd_rsp_dma_addr = cmd_desc_element_addr + response_offset;
25178c2ecf20Sopenharmony_ci	lrb->ucd_prdt_ptr = (struct ufshcd_sg_entry *)cmd_descp[i].prd_table;
25188c2ecf20Sopenharmony_ci	lrb->ucd_prdt_dma_addr = cmd_desc_element_addr + prdt_offset;
25198c2ecf20Sopenharmony_ci}
25208c2ecf20Sopenharmony_ci
25218c2ecf20Sopenharmony_ci/**
25228c2ecf20Sopenharmony_ci * ufshcd_queuecommand - main entry point for SCSI requests
25238c2ecf20Sopenharmony_ci * @host: SCSI host pointer
25248c2ecf20Sopenharmony_ci * @cmd: command from SCSI Midlayer
25258c2ecf20Sopenharmony_ci *
25268c2ecf20Sopenharmony_ci * Returns 0 for success, non-zero in case of failure
25278c2ecf20Sopenharmony_ci */
25288c2ecf20Sopenharmony_cistatic int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
25298c2ecf20Sopenharmony_ci{
25308c2ecf20Sopenharmony_ci	struct ufshcd_lrb *lrbp;
25318c2ecf20Sopenharmony_ci	struct ufs_hba *hba;
25328c2ecf20Sopenharmony_ci	unsigned long flags;
25338c2ecf20Sopenharmony_ci	int tag;
25348c2ecf20Sopenharmony_ci	int err = 0;
25358c2ecf20Sopenharmony_ci
25368c2ecf20Sopenharmony_ci	hba = shost_priv(host);
25378c2ecf20Sopenharmony_ci
25388c2ecf20Sopenharmony_ci	tag = cmd->request->tag;
25398c2ecf20Sopenharmony_ci	if (!ufshcd_valid_tag(hba, tag)) {
25408c2ecf20Sopenharmony_ci		dev_err(hba->dev,
25418c2ecf20Sopenharmony_ci			"%s: invalid command tag %d: cmd=0x%p, cmd->request=0x%p",
25428c2ecf20Sopenharmony_ci			__func__, tag, cmd, cmd->request);
25438c2ecf20Sopenharmony_ci		BUG();
25448c2ecf20Sopenharmony_ci	}
25458c2ecf20Sopenharmony_ci
25468c2ecf20Sopenharmony_ci	if (!down_read_trylock(&hba->clk_scaling_lock))
25478c2ecf20Sopenharmony_ci		return SCSI_MLQUEUE_HOST_BUSY;
25488c2ecf20Sopenharmony_ci
25498c2ecf20Sopenharmony_ci	hba->req_abort_count = 0;
25508c2ecf20Sopenharmony_ci
25518c2ecf20Sopenharmony_ci	err = ufshcd_hold(hba, true);
25528c2ecf20Sopenharmony_ci	if (err) {
25538c2ecf20Sopenharmony_ci		err = SCSI_MLQUEUE_HOST_BUSY;
25548c2ecf20Sopenharmony_ci		goto out;
25558c2ecf20Sopenharmony_ci	}
25568c2ecf20Sopenharmony_ci	WARN_ON(ufshcd_is_clkgating_allowed(hba) &&
25578c2ecf20Sopenharmony_ci		(hba->clk_gating.state != CLKS_ON));
25588c2ecf20Sopenharmony_ci
25598c2ecf20Sopenharmony_ci	lrbp = &hba->lrb[tag];
25608c2ecf20Sopenharmony_ci
25618c2ecf20Sopenharmony_ci	WARN_ON(lrbp->cmd);
25628c2ecf20Sopenharmony_ci	lrbp->cmd = cmd;
25638c2ecf20Sopenharmony_ci	lrbp->sense_bufflen = UFS_SENSE_SIZE;
25648c2ecf20Sopenharmony_ci	lrbp->sense_buffer = cmd->sense_buffer;
25658c2ecf20Sopenharmony_ci	lrbp->task_tag = tag;
25668c2ecf20Sopenharmony_ci	lrbp->lun = ufshcd_scsi_to_upiu_lun(cmd->device->lun);
25678c2ecf20Sopenharmony_ci	lrbp->intr_cmd = !ufshcd_is_intr_aggr_allowed(hba) ? true : false;
25688c2ecf20Sopenharmony_ci
25698c2ecf20Sopenharmony_ci	ufshcd_prepare_lrbp_crypto(cmd->request, lrbp);
25708c2ecf20Sopenharmony_ci
25718c2ecf20Sopenharmony_ci	lrbp->req_abort_skip = false;
25728c2ecf20Sopenharmony_ci
25738c2ecf20Sopenharmony_ci	ufshcd_comp_scsi_upiu(hba, lrbp);
25748c2ecf20Sopenharmony_ci
25758c2ecf20Sopenharmony_ci	err = ufshcd_map_sg(hba, lrbp);
25768c2ecf20Sopenharmony_ci	if (err) {
25778c2ecf20Sopenharmony_ci		lrbp->cmd = NULL;
25788c2ecf20Sopenharmony_ci		ufshcd_release(hba);
25798c2ecf20Sopenharmony_ci		goto out;
25808c2ecf20Sopenharmony_ci	}
25818c2ecf20Sopenharmony_ci	/* Make sure descriptors are ready before ringing the doorbell */
25828c2ecf20Sopenharmony_ci	wmb();
25838c2ecf20Sopenharmony_ci
25848c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
25858c2ecf20Sopenharmony_ci	switch (hba->ufshcd_state) {
25868c2ecf20Sopenharmony_ci	case UFSHCD_STATE_OPERATIONAL:
25878c2ecf20Sopenharmony_ci	case UFSHCD_STATE_EH_SCHEDULED_NON_FATAL:
25888c2ecf20Sopenharmony_ci		break;
25898c2ecf20Sopenharmony_ci	case UFSHCD_STATE_EH_SCHEDULED_FATAL:
25908c2ecf20Sopenharmony_ci		/*
25918c2ecf20Sopenharmony_ci		 * pm_runtime_get_sync() is used at error handling preparation
25928c2ecf20Sopenharmony_ci		 * stage. If a scsi cmd, e.g. the SSU cmd, is sent from hba's
25938c2ecf20Sopenharmony_ci		 * PM ops, it can never be finished if we let SCSI layer keep
25948c2ecf20Sopenharmony_ci		 * retrying it, which gets err handler stuck forever. Neither
25958c2ecf20Sopenharmony_ci		 * can we let the scsi cmd pass through, because UFS is in bad
25968c2ecf20Sopenharmony_ci		 * state, the scsi cmd may eventually time out, which will get
25978c2ecf20Sopenharmony_ci		 * err handler blocked for too long. So, just fail the scsi cmd
25988c2ecf20Sopenharmony_ci		 * sent from PM ops, err handler can recover PM error anyways.
25998c2ecf20Sopenharmony_ci		 */
26008c2ecf20Sopenharmony_ci		if (hba->pm_op_in_progress) {
26018c2ecf20Sopenharmony_ci			hba->force_reset = true;
26028c2ecf20Sopenharmony_ci			set_host_byte(cmd, DID_BAD_TARGET);
26038c2ecf20Sopenharmony_ci			goto out_compl_cmd;
26048c2ecf20Sopenharmony_ci		}
26058c2ecf20Sopenharmony_ci		fallthrough;
26068c2ecf20Sopenharmony_ci	case UFSHCD_STATE_RESET:
26078c2ecf20Sopenharmony_ci		err = SCSI_MLQUEUE_HOST_BUSY;
26088c2ecf20Sopenharmony_ci		goto out_compl_cmd;
26098c2ecf20Sopenharmony_ci	case UFSHCD_STATE_ERROR:
26108c2ecf20Sopenharmony_ci		set_host_byte(cmd, DID_ERROR);
26118c2ecf20Sopenharmony_ci		goto out_compl_cmd;
26128c2ecf20Sopenharmony_ci	default:
26138c2ecf20Sopenharmony_ci		dev_WARN_ONCE(hba->dev, 1, "%s: invalid state %d\n",
26148c2ecf20Sopenharmony_ci				__func__, hba->ufshcd_state);
26158c2ecf20Sopenharmony_ci		set_host_byte(cmd, DID_BAD_TARGET);
26168c2ecf20Sopenharmony_ci		goto out_compl_cmd;
26178c2ecf20Sopenharmony_ci	}
26188c2ecf20Sopenharmony_ci	ufshcd_send_command(hba, tag);
26198c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
26208c2ecf20Sopenharmony_ci	goto out;
26218c2ecf20Sopenharmony_ci
26228c2ecf20Sopenharmony_ciout_compl_cmd:
26238c2ecf20Sopenharmony_ci	scsi_dma_unmap(lrbp->cmd);
26248c2ecf20Sopenharmony_ci	lrbp->cmd = NULL;
26258c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
26268c2ecf20Sopenharmony_ci	ufshcd_release(hba);
26278c2ecf20Sopenharmony_ci	if (!err)
26288c2ecf20Sopenharmony_ci		cmd->scsi_done(cmd);
26298c2ecf20Sopenharmony_ciout:
26308c2ecf20Sopenharmony_ci	up_read(&hba->clk_scaling_lock);
26318c2ecf20Sopenharmony_ci	return err;
26328c2ecf20Sopenharmony_ci}
26338c2ecf20Sopenharmony_ci
26348c2ecf20Sopenharmony_cistatic int ufshcd_compose_dev_cmd(struct ufs_hba *hba,
26358c2ecf20Sopenharmony_ci		struct ufshcd_lrb *lrbp, enum dev_cmd_type cmd_type, int tag)
26368c2ecf20Sopenharmony_ci{
26378c2ecf20Sopenharmony_ci	lrbp->cmd = NULL;
26388c2ecf20Sopenharmony_ci	lrbp->sense_bufflen = 0;
26398c2ecf20Sopenharmony_ci	lrbp->sense_buffer = NULL;
26408c2ecf20Sopenharmony_ci	lrbp->task_tag = tag;
26418c2ecf20Sopenharmony_ci	lrbp->lun = 0; /* device management cmd is not specific to any LUN */
26428c2ecf20Sopenharmony_ci	lrbp->intr_cmd = true; /* No interrupt aggregation */
26438c2ecf20Sopenharmony_ci	ufshcd_prepare_lrbp_crypto(NULL, lrbp);
26448c2ecf20Sopenharmony_ci	hba->dev_cmd.type = cmd_type;
26458c2ecf20Sopenharmony_ci
26468c2ecf20Sopenharmony_ci	return ufshcd_compose_devman_upiu(hba, lrbp);
26478c2ecf20Sopenharmony_ci}
26488c2ecf20Sopenharmony_ci
26498c2ecf20Sopenharmony_cistatic int
26508c2ecf20Sopenharmony_ciufshcd_clear_cmd(struct ufs_hba *hba, int tag)
26518c2ecf20Sopenharmony_ci{
26528c2ecf20Sopenharmony_ci	int err = 0;
26538c2ecf20Sopenharmony_ci	unsigned long flags;
26548c2ecf20Sopenharmony_ci	u32 mask = 1 << tag;
26558c2ecf20Sopenharmony_ci
26568c2ecf20Sopenharmony_ci	/* clear outstanding transaction before retry */
26578c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
26588c2ecf20Sopenharmony_ci	ufshcd_utrl_clear(hba, tag);
26598c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
26608c2ecf20Sopenharmony_ci
26618c2ecf20Sopenharmony_ci	/*
26628c2ecf20Sopenharmony_ci	 * wait for for h/w to clear corresponding bit in door-bell.
26638c2ecf20Sopenharmony_ci	 * max. wait is 1 sec.
26648c2ecf20Sopenharmony_ci	 */
26658c2ecf20Sopenharmony_ci	err = ufshcd_wait_for_register(hba,
26668c2ecf20Sopenharmony_ci			REG_UTP_TRANSFER_REQ_DOOR_BELL,
26678c2ecf20Sopenharmony_ci			mask, ~mask, 1000, 1000);
26688c2ecf20Sopenharmony_ci
26698c2ecf20Sopenharmony_ci	return err;
26708c2ecf20Sopenharmony_ci}
26718c2ecf20Sopenharmony_ci
26728c2ecf20Sopenharmony_cistatic int
26738c2ecf20Sopenharmony_ciufshcd_check_query_response(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
26748c2ecf20Sopenharmony_ci{
26758c2ecf20Sopenharmony_ci	struct ufs_query_res *query_res = &hba->dev_cmd.query.response;
26768c2ecf20Sopenharmony_ci
26778c2ecf20Sopenharmony_ci	/* Get the UPIU response */
26788c2ecf20Sopenharmony_ci	query_res->response = ufshcd_get_rsp_upiu_result(lrbp->ucd_rsp_ptr) >>
26798c2ecf20Sopenharmony_ci				UPIU_RSP_CODE_OFFSET;
26808c2ecf20Sopenharmony_ci	return query_res->response;
26818c2ecf20Sopenharmony_ci}
26828c2ecf20Sopenharmony_ci
26838c2ecf20Sopenharmony_ci/**
26848c2ecf20Sopenharmony_ci * ufshcd_dev_cmd_completion() - handles device management command responses
26858c2ecf20Sopenharmony_ci * @hba: per adapter instance
26868c2ecf20Sopenharmony_ci * @lrbp: pointer to local reference block
26878c2ecf20Sopenharmony_ci */
26888c2ecf20Sopenharmony_cistatic int
26898c2ecf20Sopenharmony_ciufshcd_dev_cmd_completion(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
26908c2ecf20Sopenharmony_ci{
26918c2ecf20Sopenharmony_ci	int resp;
26928c2ecf20Sopenharmony_ci	int err = 0;
26938c2ecf20Sopenharmony_ci
26948c2ecf20Sopenharmony_ci	hba->ufs_stats.last_hibern8_exit_tstamp = ktime_set(0, 0);
26958c2ecf20Sopenharmony_ci	resp = ufshcd_get_req_rsp(lrbp->ucd_rsp_ptr);
26968c2ecf20Sopenharmony_ci
26978c2ecf20Sopenharmony_ci	switch (resp) {
26988c2ecf20Sopenharmony_ci	case UPIU_TRANSACTION_NOP_IN:
26998c2ecf20Sopenharmony_ci		if (hba->dev_cmd.type != DEV_CMD_TYPE_NOP) {
27008c2ecf20Sopenharmony_ci			err = -EINVAL;
27018c2ecf20Sopenharmony_ci			dev_err(hba->dev, "%s: unexpected response %x\n",
27028c2ecf20Sopenharmony_ci					__func__, resp);
27038c2ecf20Sopenharmony_ci		}
27048c2ecf20Sopenharmony_ci		break;
27058c2ecf20Sopenharmony_ci	case UPIU_TRANSACTION_QUERY_RSP:
27068c2ecf20Sopenharmony_ci		err = ufshcd_check_query_response(hba, lrbp);
27078c2ecf20Sopenharmony_ci		if (!err)
27088c2ecf20Sopenharmony_ci			err = ufshcd_copy_query_response(hba, lrbp);
27098c2ecf20Sopenharmony_ci		break;
27108c2ecf20Sopenharmony_ci	case UPIU_TRANSACTION_REJECT_UPIU:
27118c2ecf20Sopenharmony_ci		/* TODO: handle Reject UPIU Response */
27128c2ecf20Sopenharmony_ci		err = -EPERM;
27138c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: Reject UPIU not fully implemented\n",
27148c2ecf20Sopenharmony_ci				__func__);
27158c2ecf20Sopenharmony_ci		break;
27168c2ecf20Sopenharmony_ci	default:
27178c2ecf20Sopenharmony_ci		err = -EINVAL;
27188c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: Invalid device management cmd response: %x\n",
27198c2ecf20Sopenharmony_ci				__func__, resp);
27208c2ecf20Sopenharmony_ci		break;
27218c2ecf20Sopenharmony_ci	}
27228c2ecf20Sopenharmony_ci
27238c2ecf20Sopenharmony_ci	return err;
27248c2ecf20Sopenharmony_ci}
27258c2ecf20Sopenharmony_ci
27268c2ecf20Sopenharmony_cistatic int ufshcd_wait_for_dev_cmd(struct ufs_hba *hba,
27278c2ecf20Sopenharmony_ci		struct ufshcd_lrb *lrbp, int max_timeout)
27288c2ecf20Sopenharmony_ci{
27298c2ecf20Sopenharmony_ci	int err = 0;
27308c2ecf20Sopenharmony_ci	unsigned long time_left;
27318c2ecf20Sopenharmony_ci	unsigned long flags;
27328c2ecf20Sopenharmony_ci
27338c2ecf20Sopenharmony_ci	time_left = wait_for_completion_timeout(hba->dev_cmd.complete,
27348c2ecf20Sopenharmony_ci			msecs_to_jiffies(max_timeout));
27358c2ecf20Sopenharmony_ci
27368c2ecf20Sopenharmony_ci	/* Make sure descriptors are ready before ringing the doorbell */
27378c2ecf20Sopenharmony_ci	wmb();
27388c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
27398c2ecf20Sopenharmony_ci	hba->dev_cmd.complete = NULL;
27408c2ecf20Sopenharmony_ci	if (likely(time_left)) {
27418c2ecf20Sopenharmony_ci		err = ufshcd_get_tr_ocs(lrbp);
27428c2ecf20Sopenharmony_ci		if (!err)
27438c2ecf20Sopenharmony_ci			err = ufshcd_dev_cmd_completion(hba, lrbp);
27448c2ecf20Sopenharmony_ci	}
27458c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
27468c2ecf20Sopenharmony_ci
27478c2ecf20Sopenharmony_ci	if (!time_left) {
27488c2ecf20Sopenharmony_ci		err = -ETIMEDOUT;
27498c2ecf20Sopenharmony_ci		dev_dbg(hba->dev, "%s: dev_cmd request timedout, tag %d\n",
27508c2ecf20Sopenharmony_ci			__func__, lrbp->task_tag);
27518c2ecf20Sopenharmony_ci		if (!ufshcd_clear_cmd(hba, lrbp->task_tag))
27528c2ecf20Sopenharmony_ci			/* successfully cleared the command, retry if needed */
27538c2ecf20Sopenharmony_ci			err = -EAGAIN;
27548c2ecf20Sopenharmony_ci		/*
27558c2ecf20Sopenharmony_ci		 * in case of an error, after clearing the doorbell,
27568c2ecf20Sopenharmony_ci		 * we also need to clear the outstanding_request
27578c2ecf20Sopenharmony_ci		 * field in hba
27588c2ecf20Sopenharmony_ci		 */
27598c2ecf20Sopenharmony_ci		ufshcd_outstanding_req_clear(hba, lrbp->task_tag);
27608c2ecf20Sopenharmony_ci	}
27618c2ecf20Sopenharmony_ci
27628c2ecf20Sopenharmony_ci	return err;
27638c2ecf20Sopenharmony_ci}
27648c2ecf20Sopenharmony_ci
27658c2ecf20Sopenharmony_ci/**
27668c2ecf20Sopenharmony_ci * ufshcd_exec_dev_cmd - API for sending device management requests
27678c2ecf20Sopenharmony_ci * @hba: UFS hba
27688c2ecf20Sopenharmony_ci * @cmd_type: specifies the type (NOP, Query...)
27698c2ecf20Sopenharmony_ci * @timeout: timeout in milliseconds
27708c2ecf20Sopenharmony_ci *
27718c2ecf20Sopenharmony_ci * NOTE: Since there is only one available tag for device management commands,
27728c2ecf20Sopenharmony_ci * it is expected you hold the hba->dev_cmd.lock mutex.
27738c2ecf20Sopenharmony_ci */
27748c2ecf20Sopenharmony_cistatic int ufshcd_exec_dev_cmd(struct ufs_hba *hba,
27758c2ecf20Sopenharmony_ci		enum dev_cmd_type cmd_type, int timeout)
27768c2ecf20Sopenharmony_ci{
27778c2ecf20Sopenharmony_ci	struct request_queue *q = hba->cmd_queue;
27788c2ecf20Sopenharmony_ci	struct request *req;
27798c2ecf20Sopenharmony_ci	struct ufshcd_lrb *lrbp;
27808c2ecf20Sopenharmony_ci	int err;
27818c2ecf20Sopenharmony_ci	int tag;
27828c2ecf20Sopenharmony_ci	struct completion wait;
27838c2ecf20Sopenharmony_ci	unsigned long flags;
27848c2ecf20Sopenharmony_ci
27858c2ecf20Sopenharmony_ci	down_read(&hba->clk_scaling_lock);
27868c2ecf20Sopenharmony_ci
27878c2ecf20Sopenharmony_ci	/*
27888c2ecf20Sopenharmony_ci	 * Get free slot, sleep if slots are unavailable.
27898c2ecf20Sopenharmony_ci	 * Even though we use wait_event() which sleeps indefinitely,
27908c2ecf20Sopenharmony_ci	 * the maximum wait time is bounded by SCSI request timeout.
27918c2ecf20Sopenharmony_ci	 */
27928c2ecf20Sopenharmony_ci	req = blk_get_request(q, REQ_OP_DRV_OUT, 0);
27938c2ecf20Sopenharmony_ci	if (IS_ERR(req)) {
27948c2ecf20Sopenharmony_ci		err = PTR_ERR(req);
27958c2ecf20Sopenharmony_ci		goto out_unlock;
27968c2ecf20Sopenharmony_ci	}
27978c2ecf20Sopenharmony_ci	tag = req->tag;
27988c2ecf20Sopenharmony_ci	WARN_ON_ONCE(!ufshcd_valid_tag(hba, tag));
27998c2ecf20Sopenharmony_ci	/* Set the timeout such that the SCSI error handler is not activated. */
28008c2ecf20Sopenharmony_ci	req->timeout = msecs_to_jiffies(2 * timeout);
28018c2ecf20Sopenharmony_ci	blk_mq_start_request(req);
28028c2ecf20Sopenharmony_ci
28038c2ecf20Sopenharmony_ci	init_completion(&wait);
28048c2ecf20Sopenharmony_ci	lrbp = &hba->lrb[tag];
28058c2ecf20Sopenharmony_ci	WARN_ON(lrbp->cmd);
28068c2ecf20Sopenharmony_ci	err = ufshcd_compose_dev_cmd(hba, lrbp, cmd_type, tag);
28078c2ecf20Sopenharmony_ci	if (unlikely(err))
28088c2ecf20Sopenharmony_ci		goto out_put_tag;
28098c2ecf20Sopenharmony_ci
28108c2ecf20Sopenharmony_ci	hba->dev_cmd.complete = &wait;
28118c2ecf20Sopenharmony_ci
28128c2ecf20Sopenharmony_ci	ufshcd_add_query_upiu_trace(hba, tag, "query_send");
28138c2ecf20Sopenharmony_ci	/* Make sure descriptors are ready before ringing the doorbell */
28148c2ecf20Sopenharmony_ci	wmb();
28158c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
28168c2ecf20Sopenharmony_ci	ufshcd_send_command(hba, tag);
28178c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
28188c2ecf20Sopenharmony_ci
28198c2ecf20Sopenharmony_ci	err = ufshcd_wait_for_dev_cmd(hba, lrbp, timeout);
28208c2ecf20Sopenharmony_ci
28218c2ecf20Sopenharmony_ci	ufshcd_add_query_upiu_trace(hba, tag,
28228c2ecf20Sopenharmony_ci			err ? "query_complete_err" : "query_complete");
28238c2ecf20Sopenharmony_ci
28248c2ecf20Sopenharmony_ciout_put_tag:
28258c2ecf20Sopenharmony_ci	blk_put_request(req);
28268c2ecf20Sopenharmony_ciout_unlock:
28278c2ecf20Sopenharmony_ci	up_read(&hba->clk_scaling_lock);
28288c2ecf20Sopenharmony_ci	return err;
28298c2ecf20Sopenharmony_ci}
28308c2ecf20Sopenharmony_ci
28318c2ecf20Sopenharmony_ci/**
28328c2ecf20Sopenharmony_ci * ufshcd_init_query() - init the query response and request parameters
28338c2ecf20Sopenharmony_ci * @hba: per-adapter instance
28348c2ecf20Sopenharmony_ci * @request: address of the request pointer to be initialized
28358c2ecf20Sopenharmony_ci * @response: address of the response pointer to be initialized
28368c2ecf20Sopenharmony_ci * @opcode: operation to perform
28378c2ecf20Sopenharmony_ci * @idn: flag idn to access
28388c2ecf20Sopenharmony_ci * @index: LU number to access
28398c2ecf20Sopenharmony_ci * @selector: query/flag/descriptor further identification
28408c2ecf20Sopenharmony_ci */
28418c2ecf20Sopenharmony_cistatic inline void ufshcd_init_query(struct ufs_hba *hba,
28428c2ecf20Sopenharmony_ci		struct ufs_query_req **request, struct ufs_query_res **response,
28438c2ecf20Sopenharmony_ci		enum query_opcode opcode, u8 idn, u8 index, u8 selector)
28448c2ecf20Sopenharmony_ci{
28458c2ecf20Sopenharmony_ci	*request = &hba->dev_cmd.query.request;
28468c2ecf20Sopenharmony_ci	*response = &hba->dev_cmd.query.response;
28478c2ecf20Sopenharmony_ci	memset(*request, 0, sizeof(struct ufs_query_req));
28488c2ecf20Sopenharmony_ci	memset(*response, 0, sizeof(struct ufs_query_res));
28498c2ecf20Sopenharmony_ci	(*request)->upiu_req.opcode = opcode;
28508c2ecf20Sopenharmony_ci	(*request)->upiu_req.idn = idn;
28518c2ecf20Sopenharmony_ci	(*request)->upiu_req.index = index;
28528c2ecf20Sopenharmony_ci	(*request)->upiu_req.selector = selector;
28538c2ecf20Sopenharmony_ci}
28548c2ecf20Sopenharmony_ci
28558c2ecf20Sopenharmony_cistatic int ufshcd_query_flag_retry(struct ufs_hba *hba,
28568c2ecf20Sopenharmony_ci	enum query_opcode opcode, enum flag_idn idn, u8 index, bool *flag_res)
28578c2ecf20Sopenharmony_ci{
28588c2ecf20Sopenharmony_ci	int ret;
28598c2ecf20Sopenharmony_ci	int retries;
28608c2ecf20Sopenharmony_ci
28618c2ecf20Sopenharmony_ci	for (retries = 0; retries < QUERY_REQ_RETRIES; retries++) {
28628c2ecf20Sopenharmony_ci		ret = ufshcd_query_flag(hba, opcode, idn, index, flag_res);
28638c2ecf20Sopenharmony_ci		if (ret)
28648c2ecf20Sopenharmony_ci			dev_dbg(hba->dev,
28658c2ecf20Sopenharmony_ci				"%s: failed with error %d, retries %d\n",
28668c2ecf20Sopenharmony_ci				__func__, ret, retries);
28678c2ecf20Sopenharmony_ci		else
28688c2ecf20Sopenharmony_ci			break;
28698c2ecf20Sopenharmony_ci	}
28708c2ecf20Sopenharmony_ci
28718c2ecf20Sopenharmony_ci	if (ret)
28728c2ecf20Sopenharmony_ci		dev_err(hba->dev,
28738c2ecf20Sopenharmony_ci			"%s: query attribute, opcode %d, idn %d, failed with error %d after %d retires\n",
28748c2ecf20Sopenharmony_ci			__func__, opcode, idn, ret, retries);
28758c2ecf20Sopenharmony_ci	return ret;
28768c2ecf20Sopenharmony_ci}
28778c2ecf20Sopenharmony_ci
28788c2ecf20Sopenharmony_ci/**
28798c2ecf20Sopenharmony_ci * ufshcd_query_flag() - API function for sending flag query requests
28808c2ecf20Sopenharmony_ci * @hba: per-adapter instance
28818c2ecf20Sopenharmony_ci * @opcode: flag query to perform
28828c2ecf20Sopenharmony_ci * @idn: flag idn to access
28838c2ecf20Sopenharmony_ci * @index: flag index to access
28848c2ecf20Sopenharmony_ci * @flag_res: the flag value after the query request completes
28858c2ecf20Sopenharmony_ci *
28868c2ecf20Sopenharmony_ci * Returns 0 for success, non-zero in case of failure
28878c2ecf20Sopenharmony_ci */
28888c2ecf20Sopenharmony_ciint ufshcd_query_flag(struct ufs_hba *hba, enum query_opcode opcode,
28898c2ecf20Sopenharmony_ci			enum flag_idn idn, u8 index, bool *flag_res)
28908c2ecf20Sopenharmony_ci{
28918c2ecf20Sopenharmony_ci	struct ufs_query_req *request = NULL;
28928c2ecf20Sopenharmony_ci	struct ufs_query_res *response = NULL;
28938c2ecf20Sopenharmony_ci	int err, selector = 0;
28948c2ecf20Sopenharmony_ci	int timeout = QUERY_REQ_TIMEOUT;
28958c2ecf20Sopenharmony_ci
28968c2ecf20Sopenharmony_ci	BUG_ON(!hba);
28978c2ecf20Sopenharmony_ci
28988c2ecf20Sopenharmony_ci	ufshcd_hold(hba, false);
28998c2ecf20Sopenharmony_ci	mutex_lock(&hba->dev_cmd.lock);
29008c2ecf20Sopenharmony_ci	ufshcd_init_query(hba, &request, &response, opcode, idn, index,
29018c2ecf20Sopenharmony_ci			selector);
29028c2ecf20Sopenharmony_ci
29038c2ecf20Sopenharmony_ci	switch (opcode) {
29048c2ecf20Sopenharmony_ci	case UPIU_QUERY_OPCODE_SET_FLAG:
29058c2ecf20Sopenharmony_ci	case UPIU_QUERY_OPCODE_CLEAR_FLAG:
29068c2ecf20Sopenharmony_ci	case UPIU_QUERY_OPCODE_TOGGLE_FLAG:
29078c2ecf20Sopenharmony_ci		request->query_func = UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST;
29088c2ecf20Sopenharmony_ci		break;
29098c2ecf20Sopenharmony_ci	case UPIU_QUERY_OPCODE_READ_FLAG:
29108c2ecf20Sopenharmony_ci		request->query_func = UPIU_QUERY_FUNC_STANDARD_READ_REQUEST;
29118c2ecf20Sopenharmony_ci		if (!flag_res) {
29128c2ecf20Sopenharmony_ci			/* No dummy reads */
29138c2ecf20Sopenharmony_ci			dev_err(hba->dev, "%s: Invalid argument for read request\n",
29148c2ecf20Sopenharmony_ci					__func__);
29158c2ecf20Sopenharmony_ci			err = -EINVAL;
29168c2ecf20Sopenharmony_ci			goto out_unlock;
29178c2ecf20Sopenharmony_ci		}
29188c2ecf20Sopenharmony_ci		break;
29198c2ecf20Sopenharmony_ci	default:
29208c2ecf20Sopenharmony_ci		dev_err(hba->dev,
29218c2ecf20Sopenharmony_ci			"%s: Expected query flag opcode but got = %d\n",
29228c2ecf20Sopenharmony_ci			__func__, opcode);
29238c2ecf20Sopenharmony_ci		err = -EINVAL;
29248c2ecf20Sopenharmony_ci		goto out_unlock;
29258c2ecf20Sopenharmony_ci	}
29268c2ecf20Sopenharmony_ci
29278c2ecf20Sopenharmony_ci	err = ufshcd_exec_dev_cmd(hba, DEV_CMD_TYPE_QUERY, timeout);
29288c2ecf20Sopenharmony_ci
29298c2ecf20Sopenharmony_ci	if (err) {
29308c2ecf20Sopenharmony_ci		dev_err(hba->dev,
29318c2ecf20Sopenharmony_ci			"%s: Sending flag query for idn %d failed, err = %d\n",
29328c2ecf20Sopenharmony_ci			__func__, idn, err);
29338c2ecf20Sopenharmony_ci		goto out_unlock;
29348c2ecf20Sopenharmony_ci	}
29358c2ecf20Sopenharmony_ci
29368c2ecf20Sopenharmony_ci	if (flag_res)
29378c2ecf20Sopenharmony_ci		*flag_res = (be32_to_cpu(response->upiu_res.value) &
29388c2ecf20Sopenharmony_ci				MASK_QUERY_UPIU_FLAG_LOC) & 0x1;
29398c2ecf20Sopenharmony_ci
29408c2ecf20Sopenharmony_ciout_unlock:
29418c2ecf20Sopenharmony_ci	mutex_unlock(&hba->dev_cmd.lock);
29428c2ecf20Sopenharmony_ci	ufshcd_release(hba);
29438c2ecf20Sopenharmony_ci	return err;
29448c2ecf20Sopenharmony_ci}
29458c2ecf20Sopenharmony_ci
29468c2ecf20Sopenharmony_ci/**
29478c2ecf20Sopenharmony_ci * ufshcd_query_attr - API function for sending attribute requests
29488c2ecf20Sopenharmony_ci * @hba: per-adapter instance
29498c2ecf20Sopenharmony_ci * @opcode: attribute opcode
29508c2ecf20Sopenharmony_ci * @idn: attribute idn to access
29518c2ecf20Sopenharmony_ci * @index: index field
29528c2ecf20Sopenharmony_ci * @selector: selector field
29538c2ecf20Sopenharmony_ci * @attr_val: the attribute value after the query request completes
29548c2ecf20Sopenharmony_ci *
29558c2ecf20Sopenharmony_ci * Returns 0 for success, non-zero in case of failure
29568c2ecf20Sopenharmony_ci*/
29578c2ecf20Sopenharmony_ciint ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode,
29588c2ecf20Sopenharmony_ci		      enum attr_idn idn, u8 index, u8 selector, u32 *attr_val)
29598c2ecf20Sopenharmony_ci{
29608c2ecf20Sopenharmony_ci	struct ufs_query_req *request = NULL;
29618c2ecf20Sopenharmony_ci	struct ufs_query_res *response = NULL;
29628c2ecf20Sopenharmony_ci	int err;
29638c2ecf20Sopenharmony_ci
29648c2ecf20Sopenharmony_ci	BUG_ON(!hba);
29658c2ecf20Sopenharmony_ci
29668c2ecf20Sopenharmony_ci	ufshcd_hold(hba, false);
29678c2ecf20Sopenharmony_ci	if (!attr_val) {
29688c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: attribute value required for opcode 0x%x\n",
29698c2ecf20Sopenharmony_ci				__func__, opcode);
29708c2ecf20Sopenharmony_ci		err = -EINVAL;
29718c2ecf20Sopenharmony_ci		goto out;
29728c2ecf20Sopenharmony_ci	}
29738c2ecf20Sopenharmony_ci
29748c2ecf20Sopenharmony_ci	mutex_lock(&hba->dev_cmd.lock);
29758c2ecf20Sopenharmony_ci	ufshcd_init_query(hba, &request, &response, opcode, idn, index,
29768c2ecf20Sopenharmony_ci			selector);
29778c2ecf20Sopenharmony_ci
29788c2ecf20Sopenharmony_ci	switch (opcode) {
29798c2ecf20Sopenharmony_ci	case UPIU_QUERY_OPCODE_WRITE_ATTR:
29808c2ecf20Sopenharmony_ci		request->query_func = UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST;
29818c2ecf20Sopenharmony_ci		request->upiu_req.value = cpu_to_be32(*attr_val);
29828c2ecf20Sopenharmony_ci		break;
29838c2ecf20Sopenharmony_ci	case UPIU_QUERY_OPCODE_READ_ATTR:
29848c2ecf20Sopenharmony_ci		request->query_func = UPIU_QUERY_FUNC_STANDARD_READ_REQUEST;
29858c2ecf20Sopenharmony_ci		break;
29868c2ecf20Sopenharmony_ci	default:
29878c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: Expected query attr opcode but got = 0x%.2x\n",
29888c2ecf20Sopenharmony_ci				__func__, opcode);
29898c2ecf20Sopenharmony_ci		err = -EINVAL;
29908c2ecf20Sopenharmony_ci		goto out_unlock;
29918c2ecf20Sopenharmony_ci	}
29928c2ecf20Sopenharmony_ci
29938c2ecf20Sopenharmony_ci	err = ufshcd_exec_dev_cmd(hba, DEV_CMD_TYPE_QUERY, QUERY_REQ_TIMEOUT);
29948c2ecf20Sopenharmony_ci
29958c2ecf20Sopenharmony_ci	if (err) {
29968c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: opcode 0x%.2x for idn %d failed, index %d, err = %d\n",
29978c2ecf20Sopenharmony_ci				__func__, opcode, idn, index, err);
29988c2ecf20Sopenharmony_ci		goto out_unlock;
29998c2ecf20Sopenharmony_ci	}
30008c2ecf20Sopenharmony_ci
30018c2ecf20Sopenharmony_ci	*attr_val = be32_to_cpu(response->upiu_res.value);
30028c2ecf20Sopenharmony_ci
30038c2ecf20Sopenharmony_ciout_unlock:
30048c2ecf20Sopenharmony_ci	mutex_unlock(&hba->dev_cmd.lock);
30058c2ecf20Sopenharmony_ciout:
30068c2ecf20Sopenharmony_ci	ufshcd_release(hba);
30078c2ecf20Sopenharmony_ci	return err;
30088c2ecf20Sopenharmony_ci}
30098c2ecf20Sopenharmony_ci
30108c2ecf20Sopenharmony_ci/**
30118c2ecf20Sopenharmony_ci * ufshcd_query_attr_retry() - API function for sending query
30128c2ecf20Sopenharmony_ci * attribute with retries
30138c2ecf20Sopenharmony_ci * @hba: per-adapter instance
30148c2ecf20Sopenharmony_ci * @opcode: attribute opcode
30158c2ecf20Sopenharmony_ci * @idn: attribute idn to access
30168c2ecf20Sopenharmony_ci * @index: index field
30178c2ecf20Sopenharmony_ci * @selector: selector field
30188c2ecf20Sopenharmony_ci * @attr_val: the attribute value after the query request
30198c2ecf20Sopenharmony_ci * completes
30208c2ecf20Sopenharmony_ci *
30218c2ecf20Sopenharmony_ci * Returns 0 for success, non-zero in case of failure
30228c2ecf20Sopenharmony_ci*/
30238c2ecf20Sopenharmony_cistatic int ufshcd_query_attr_retry(struct ufs_hba *hba,
30248c2ecf20Sopenharmony_ci	enum query_opcode opcode, enum attr_idn idn, u8 index, u8 selector,
30258c2ecf20Sopenharmony_ci	u32 *attr_val)
30268c2ecf20Sopenharmony_ci{
30278c2ecf20Sopenharmony_ci	int ret = 0;
30288c2ecf20Sopenharmony_ci	u32 retries;
30298c2ecf20Sopenharmony_ci
30308c2ecf20Sopenharmony_ci	for (retries = QUERY_REQ_RETRIES; retries > 0; retries--) {
30318c2ecf20Sopenharmony_ci		ret = ufshcd_query_attr(hba, opcode, idn, index,
30328c2ecf20Sopenharmony_ci						selector, attr_val);
30338c2ecf20Sopenharmony_ci		if (ret)
30348c2ecf20Sopenharmony_ci			dev_dbg(hba->dev, "%s: failed with error %d, retries %d\n",
30358c2ecf20Sopenharmony_ci				__func__, ret, retries);
30368c2ecf20Sopenharmony_ci		else
30378c2ecf20Sopenharmony_ci			break;
30388c2ecf20Sopenharmony_ci	}
30398c2ecf20Sopenharmony_ci
30408c2ecf20Sopenharmony_ci	if (ret)
30418c2ecf20Sopenharmony_ci		dev_err(hba->dev,
30428c2ecf20Sopenharmony_ci			"%s: query attribute, idn %d, failed with error %d after %d retires\n",
30438c2ecf20Sopenharmony_ci			__func__, idn, ret, QUERY_REQ_RETRIES);
30448c2ecf20Sopenharmony_ci	return ret;
30458c2ecf20Sopenharmony_ci}
30468c2ecf20Sopenharmony_ci
30478c2ecf20Sopenharmony_cistatic int __ufshcd_query_descriptor(struct ufs_hba *hba,
30488c2ecf20Sopenharmony_ci			enum query_opcode opcode, enum desc_idn idn, u8 index,
30498c2ecf20Sopenharmony_ci			u8 selector, u8 *desc_buf, int *buf_len)
30508c2ecf20Sopenharmony_ci{
30518c2ecf20Sopenharmony_ci	struct ufs_query_req *request = NULL;
30528c2ecf20Sopenharmony_ci	struct ufs_query_res *response = NULL;
30538c2ecf20Sopenharmony_ci	int err;
30548c2ecf20Sopenharmony_ci
30558c2ecf20Sopenharmony_ci	BUG_ON(!hba);
30568c2ecf20Sopenharmony_ci
30578c2ecf20Sopenharmony_ci	ufshcd_hold(hba, false);
30588c2ecf20Sopenharmony_ci	if (!desc_buf) {
30598c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: descriptor buffer required for opcode 0x%x\n",
30608c2ecf20Sopenharmony_ci				__func__, opcode);
30618c2ecf20Sopenharmony_ci		err = -EINVAL;
30628c2ecf20Sopenharmony_ci		goto out;
30638c2ecf20Sopenharmony_ci	}
30648c2ecf20Sopenharmony_ci
30658c2ecf20Sopenharmony_ci	if (*buf_len < QUERY_DESC_MIN_SIZE || *buf_len > QUERY_DESC_MAX_SIZE) {
30668c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: descriptor buffer size (%d) is out of range\n",
30678c2ecf20Sopenharmony_ci				__func__, *buf_len);
30688c2ecf20Sopenharmony_ci		err = -EINVAL;
30698c2ecf20Sopenharmony_ci		goto out;
30708c2ecf20Sopenharmony_ci	}
30718c2ecf20Sopenharmony_ci
30728c2ecf20Sopenharmony_ci	mutex_lock(&hba->dev_cmd.lock);
30738c2ecf20Sopenharmony_ci	ufshcd_init_query(hba, &request, &response, opcode, idn, index,
30748c2ecf20Sopenharmony_ci			selector);
30758c2ecf20Sopenharmony_ci	hba->dev_cmd.query.descriptor = desc_buf;
30768c2ecf20Sopenharmony_ci	request->upiu_req.length = cpu_to_be16(*buf_len);
30778c2ecf20Sopenharmony_ci
30788c2ecf20Sopenharmony_ci	switch (opcode) {
30798c2ecf20Sopenharmony_ci	case UPIU_QUERY_OPCODE_WRITE_DESC:
30808c2ecf20Sopenharmony_ci		request->query_func = UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST;
30818c2ecf20Sopenharmony_ci		break;
30828c2ecf20Sopenharmony_ci	case UPIU_QUERY_OPCODE_READ_DESC:
30838c2ecf20Sopenharmony_ci		request->query_func = UPIU_QUERY_FUNC_STANDARD_READ_REQUEST;
30848c2ecf20Sopenharmony_ci		break;
30858c2ecf20Sopenharmony_ci	default:
30868c2ecf20Sopenharmony_ci		dev_err(hba->dev,
30878c2ecf20Sopenharmony_ci				"%s: Expected query descriptor opcode but got = 0x%.2x\n",
30888c2ecf20Sopenharmony_ci				__func__, opcode);
30898c2ecf20Sopenharmony_ci		err = -EINVAL;
30908c2ecf20Sopenharmony_ci		goto out_unlock;
30918c2ecf20Sopenharmony_ci	}
30928c2ecf20Sopenharmony_ci
30938c2ecf20Sopenharmony_ci	err = ufshcd_exec_dev_cmd(hba, DEV_CMD_TYPE_QUERY, QUERY_REQ_TIMEOUT);
30948c2ecf20Sopenharmony_ci
30958c2ecf20Sopenharmony_ci	if (err) {
30968c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: opcode 0x%.2x for idn %d failed, index %d, err = %d\n",
30978c2ecf20Sopenharmony_ci				__func__, opcode, idn, index, err);
30988c2ecf20Sopenharmony_ci		goto out_unlock;
30998c2ecf20Sopenharmony_ci	}
31008c2ecf20Sopenharmony_ci
31018c2ecf20Sopenharmony_ci	*buf_len = be16_to_cpu(response->upiu_res.length);
31028c2ecf20Sopenharmony_ci
31038c2ecf20Sopenharmony_ciout_unlock:
31048c2ecf20Sopenharmony_ci	hba->dev_cmd.query.descriptor = NULL;
31058c2ecf20Sopenharmony_ci	mutex_unlock(&hba->dev_cmd.lock);
31068c2ecf20Sopenharmony_ciout:
31078c2ecf20Sopenharmony_ci	ufshcd_release(hba);
31088c2ecf20Sopenharmony_ci	return err;
31098c2ecf20Sopenharmony_ci}
31108c2ecf20Sopenharmony_ci
31118c2ecf20Sopenharmony_ci/**
31128c2ecf20Sopenharmony_ci * ufshcd_query_descriptor_retry - API function for sending descriptor requests
31138c2ecf20Sopenharmony_ci * @hba: per-adapter instance
31148c2ecf20Sopenharmony_ci * @opcode: attribute opcode
31158c2ecf20Sopenharmony_ci * @idn: attribute idn to access
31168c2ecf20Sopenharmony_ci * @index: index field
31178c2ecf20Sopenharmony_ci * @selector: selector field
31188c2ecf20Sopenharmony_ci * @desc_buf: the buffer that contains the descriptor
31198c2ecf20Sopenharmony_ci * @buf_len: length parameter passed to the device
31208c2ecf20Sopenharmony_ci *
31218c2ecf20Sopenharmony_ci * Returns 0 for success, non-zero in case of failure.
31228c2ecf20Sopenharmony_ci * The buf_len parameter will contain, on return, the length parameter
31238c2ecf20Sopenharmony_ci * received on the response.
31248c2ecf20Sopenharmony_ci */
31258c2ecf20Sopenharmony_ciint ufshcd_query_descriptor_retry(struct ufs_hba *hba,
31268c2ecf20Sopenharmony_ci				  enum query_opcode opcode,
31278c2ecf20Sopenharmony_ci				  enum desc_idn idn, u8 index,
31288c2ecf20Sopenharmony_ci				  u8 selector,
31298c2ecf20Sopenharmony_ci				  u8 *desc_buf, int *buf_len)
31308c2ecf20Sopenharmony_ci{
31318c2ecf20Sopenharmony_ci	int err;
31328c2ecf20Sopenharmony_ci	int retries;
31338c2ecf20Sopenharmony_ci
31348c2ecf20Sopenharmony_ci	for (retries = QUERY_REQ_RETRIES; retries > 0; retries--) {
31358c2ecf20Sopenharmony_ci		err = __ufshcd_query_descriptor(hba, opcode, idn, index,
31368c2ecf20Sopenharmony_ci						selector, desc_buf, buf_len);
31378c2ecf20Sopenharmony_ci		if (!err || err == -EINVAL)
31388c2ecf20Sopenharmony_ci			break;
31398c2ecf20Sopenharmony_ci	}
31408c2ecf20Sopenharmony_ci
31418c2ecf20Sopenharmony_ci	return err;
31428c2ecf20Sopenharmony_ci}
31438c2ecf20Sopenharmony_ci
31448c2ecf20Sopenharmony_ci/**
31458c2ecf20Sopenharmony_ci * ufshcd_map_desc_id_to_length - map descriptor IDN to its length
31468c2ecf20Sopenharmony_ci * @hba: Pointer to adapter instance
31478c2ecf20Sopenharmony_ci * @desc_id: descriptor idn value
31488c2ecf20Sopenharmony_ci * @desc_len: mapped desc length (out)
31498c2ecf20Sopenharmony_ci */
31508c2ecf20Sopenharmony_civoid ufshcd_map_desc_id_to_length(struct ufs_hba *hba, enum desc_idn desc_id,
31518c2ecf20Sopenharmony_ci				  int *desc_len)
31528c2ecf20Sopenharmony_ci{
31538c2ecf20Sopenharmony_ci	if (desc_id >= QUERY_DESC_IDN_MAX || desc_id == QUERY_DESC_IDN_RFU_0 ||
31548c2ecf20Sopenharmony_ci	    desc_id == QUERY_DESC_IDN_RFU_1)
31558c2ecf20Sopenharmony_ci		*desc_len = 0;
31568c2ecf20Sopenharmony_ci	else
31578c2ecf20Sopenharmony_ci		*desc_len = hba->desc_size[desc_id];
31588c2ecf20Sopenharmony_ci}
31598c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ufshcd_map_desc_id_to_length);
31608c2ecf20Sopenharmony_ci
31618c2ecf20Sopenharmony_cistatic void ufshcd_update_desc_length(struct ufs_hba *hba,
31628c2ecf20Sopenharmony_ci				      enum desc_idn desc_id, int desc_index,
31638c2ecf20Sopenharmony_ci				      unsigned char desc_len)
31648c2ecf20Sopenharmony_ci{
31658c2ecf20Sopenharmony_ci	if (hba->desc_size[desc_id] == QUERY_DESC_MAX_SIZE &&
31668c2ecf20Sopenharmony_ci	    desc_id != QUERY_DESC_IDN_STRING && desc_index != UFS_RPMB_UNIT)
31678c2ecf20Sopenharmony_ci		/* For UFS 3.1, the normal unit descriptor is 10 bytes larger
31688c2ecf20Sopenharmony_ci		 * than the RPMB unit, however, both descriptors share the same
31698c2ecf20Sopenharmony_ci		 * desc_idn, to cover both unit descriptors with one length, we
31708c2ecf20Sopenharmony_ci		 * choose the normal unit descriptor length by desc_index.
31718c2ecf20Sopenharmony_ci		 */
31728c2ecf20Sopenharmony_ci		hba->desc_size[desc_id] = desc_len;
31738c2ecf20Sopenharmony_ci}
31748c2ecf20Sopenharmony_ci
31758c2ecf20Sopenharmony_ci/**
31768c2ecf20Sopenharmony_ci * ufshcd_read_desc_param - read the specified descriptor parameter
31778c2ecf20Sopenharmony_ci * @hba: Pointer to adapter instance
31788c2ecf20Sopenharmony_ci * @desc_id: descriptor idn value
31798c2ecf20Sopenharmony_ci * @desc_index: descriptor index
31808c2ecf20Sopenharmony_ci * @param_offset: offset of the parameter to read
31818c2ecf20Sopenharmony_ci * @param_read_buf: pointer to buffer where parameter would be read
31828c2ecf20Sopenharmony_ci * @param_size: sizeof(param_read_buf)
31838c2ecf20Sopenharmony_ci *
31848c2ecf20Sopenharmony_ci * Return 0 in case of success, non-zero otherwise
31858c2ecf20Sopenharmony_ci */
31868c2ecf20Sopenharmony_ciint ufshcd_read_desc_param(struct ufs_hba *hba,
31878c2ecf20Sopenharmony_ci			   enum desc_idn desc_id,
31888c2ecf20Sopenharmony_ci			   int desc_index,
31898c2ecf20Sopenharmony_ci			   u8 param_offset,
31908c2ecf20Sopenharmony_ci			   u8 *param_read_buf,
31918c2ecf20Sopenharmony_ci			   u8 param_size)
31928c2ecf20Sopenharmony_ci{
31938c2ecf20Sopenharmony_ci	int ret;
31948c2ecf20Sopenharmony_ci	u8 *desc_buf;
31958c2ecf20Sopenharmony_ci	int buff_len;
31968c2ecf20Sopenharmony_ci	bool is_kmalloc = true;
31978c2ecf20Sopenharmony_ci
31988c2ecf20Sopenharmony_ci	/* Safety check */
31998c2ecf20Sopenharmony_ci	if (desc_id >= QUERY_DESC_IDN_MAX || !param_size)
32008c2ecf20Sopenharmony_ci		return -EINVAL;
32018c2ecf20Sopenharmony_ci
32028c2ecf20Sopenharmony_ci	/* Get the length of descriptor */
32038c2ecf20Sopenharmony_ci	ufshcd_map_desc_id_to_length(hba, desc_id, &buff_len);
32048c2ecf20Sopenharmony_ci	if (!buff_len) {
32058c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: Failed to get desc length\n", __func__);
32068c2ecf20Sopenharmony_ci		return -EINVAL;
32078c2ecf20Sopenharmony_ci	}
32088c2ecf20Sopenharmony_ci
32098c2ecf20Sopenharmony_ci	if (param_offset >= buff_len) {
32108c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: Invalid offset 0x%x in descriptor IDN 0x%x, length 0x%x\n",
32118c2ecf20Sopenharmony_ci			__func__, param_offset, desc_id, buff_len);
32128c2ecf20Sopenharmony_ci		return -EINVAL;
32138c2ecf20Sopenharmony_ci	}
32148c2ecf20Sopenharmony_ci
32158c2ecf20Sopenharmony_ci	/* Check whether we need temp memory */
32168c2ecf20Sopenharmony_ci	if (param_offset != 0 || param_size < buff_len) {
32178c2ecf20Sopenharmony_ci		desc_buf = kzalloc(buff_len, GFP_KERNEL);
32188c2ecf20Sopenharmony_ci		if (!desc_buf)
32198c2ecf20Sopenharmony_ci			return -ENOMEM;
32208c2ecf20Sopenharmony_ci	} else {
32218c2ecf20Sopenharmony_ci		desc_buf = param_read_buf;
32228c2ecf20Sopenharmony_ci		is_kmalloc = false;
32238c2ecf20Sopenharmony_ci	}
32248c2ecf20Sopenharmony_ci
32258c2ecf20Sopenharmony_ci	/* Request for full descriptor */
32268c2ecf20Sopenharmony_ci	ret = ufshcd_query_descriptor_retry(hba, UPIU_QUERY_OPCODE_READ_DESC,
32278c2ecf20Sopenharmony_ci					desc_id, desc_index, 0,
32288c2ecf20Sopenharmony_ci					desc_buf, &buff_len);
32298c2ecf20Sopenharmony_ci
32308c2ecf20Sopenharmony_ci	if (ret) {
32318c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: Failed reading descriptor. desc_id %d, desc_index %d, param_offset %d, ret %d\n",
32328c2ecf20Sopenharmony_ci			__func__, desc_id, desc_index, param_offset, ret);
32338c2ecf20Sopenharmony_ci		goto out;
32348c2ecf20Sopenharmony_ci	}
32358c2ecf20Sopenharmony_ci
32368c2ecf20Sopenharmony_ci	/* Sanity check */
32378c2ecf20Sopenharmony_ci	if (desc_buf[QUERY_DESC_DESC_TYPE_OFFSET] != desc_id) {
32388c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: invalid desc_id %d in descriptor header\n",
32398c2ecf20Sopenharmony_ci			__func__, desc_buf[QUERY_DESC_DESC_TYPE_OFFSET]);
32408c2ecf20Sopenharmony_ci		ret = -EINVAL;
32418c2ecf20Sopenharmony_ci		goto out;
32428c2ecf20Sopenharmony_ci	}
32438c2ecf20Sopenharmony_ci
32448c2ecf20Sopenharmony_ci	/* Update descriptor length */
32458c2ecf20Sopenharmony_ci	buff_len = desc_buf[QUERY_DESC_LENGTH_OFFSET];
32468c2ecf20Sopenharmony_ci	ufshcd_update_desc_length(hba, desc_id, desc_index, buff_len);
32478c2ecf20Sopenharmony_ci
32488c2ecf20Sopenharmony_ci	if (is_kmalloc) {
32498c2ecf20Sopenharmony_ci		/* Make sure we don't copy more data than available */
32508c2ecf20Sopenharmony_ci		if (param_offset >= buff_len)
32518c2ecf20Sopenharmony_ci			ret = -EINVAL;
32528c2ecf20Sopenharmony_ci		else
32538c2ecf20Sopenharmony_ci			memcpy(param_read_buf, &desc_buf[param_offset],
32548c2ecf20Sopenharmony_ci			       min_t(u32, param_size, buff_len - param_offset));
32558c2ecf20Sopenharmony_ci	}
32568c2ecf20Sopenharmony_ciout:
32578c2ecf20Sopenharmony_ci	if (is_kmalloc)
32588c2ecf20Sopenharmony_ci		kfree(desc_buf);
32598c2ecf20Sopenharmony_ci	return ret;
32608c2ecf20Sopenharmony_ci}
32618c2ecf20Sopenharmony_ci
32628c2ecf20Sopenharmony_ci/**
32638c2ecf20Sopenharmony_ci * struct uc_string_id - unicode string
32648c2ecf20Sopenharmony_ci *
32658c2ecf20Sopenharmony_ci * @len: size of this descriptor inclusive
32668c2ecf20Sopenharmony_ci * @type: descriptor type
32678c2ecf20Sopenharmony_ci * @uc: unicode string character
32688c2ecf20Sopenharmony_ci */
32698c2ecf20Sopenharmony_cistruct uc_string_id {
32708c2ecf20Sopenharmony_ci	u8 len;
32718c2ecf20Sopenharmony_ci	u8 type;
32728c2ecf20Sopenharmony_ci	wchar_t uc[];
32738c2ecf20Sopenharmony_ci} __packed;
32748c2ecf20Sopenharmony_ci
32758c2ecf20Sopenharmony_ci/* replace non-printable or non-ASCII characters with spaces */
32768c2ecf20Sopenharmony_cistatic inline char ufshcd_remove_non_printable(u8 ch)
32778c2ecf20Sopenharmony_ci{
32788c2ecf20Sopenharmony_ci	return (ch >= 0x20 && ch <= 0x7e) ? ch : ' ';
32798c2ecf20Sopenharmony_ci}
32808c2ecf20Sopenharmony_ci
32818c2ecf20Sopenharmony_ci/**
32828c2ecf20Sopenharmony_ci * ufshcd_read_string_desc - read string descriptor
32838c2ecf20Sopenharmony_ci * @hba: pointer to adapter instance
32848c2ecf20Sopenharmony_ci * @desc_index: descriptor index
32858c2ecf20Sopenharmony_ci * @buf: pointer to buffer where descriptor would be read,
32868c2ecf20Sopenharmony_ci *       the caller should free the memory.
32878c2ecf20Sopenharmony_ci * @ascii: if true convert from unicode to ascii characters
32888c2ecf20Sopenharmony_ci *         null terminated string.
32898c2ecf20Sopenharmony_ci *
32908c2ecf20Sopenharmony_ci * Return:
32918c2ecf20Sopenharmony_ci * *      string size on success.
32928c2ecf20Sopenharmony_ci * *      -ENOMEM: on allocation failure
32938c2ecf20Sopenharmony_ci * *      -EINVAL: on a wrong parameter
32948c2ecf20Sopenharmony_ci */
32958c2ecf20Sopenharmony_ciint ufshcd_read_string_desc(struct ufs_hba *hba, u8 desc_index,
32968c2ecf20Sopenharmony_ci			    u8 **buf, bool ascii)
32978c2ecf20Sopenharmony_ci{
32988c2ecf20Sopenharmony_ci	struct uc_string_id *uc_str;
32998c2ecf20Sopenharmony_ci	u8 *str;
33008c2ecf20Sopenharmony_ci	int ret;
33018c2ecf20Sopenharmony_ci
33028c2ecf20Sopenharmony_ci	if (!buf)
33038c2ecf20Sopenharmony_ci		return -EINVAL;
33048c2ecf20Sopenharmony_ci
33058c2ecf20Sopenharmony_ci	uc_str = kzalloc(QUERY_DESC_MAX_SIZE, GFP_KERNEL);
33068c2ecf20Sopenharmony_ci	if (!uc_str)
33078c2ecf20Sopenharmony_ci		return -ENOMEM;
33088c2ecf20Sopenharmony_ci
33098c2ecf20Sopenharmony_ci	ret = ufshcd_read_desc_param(hba, QUERY_DESC_IDN_STRING, desc_index, 0,
33108c2ecf20Sopenharmony_ci				     (u8 *)uc_str, QUERY_DESC_MAX_SIZE);
33118c2ecf20Sopenharmony_ci	if (ret < 0) {
33128c2ecf20Sopenharmony_ci		dev_err(hba->dev, "Reading String Desc failed after %d retries. err = %d\n",
33138c2ecf20Sopenharmony_ci			QUERY_REQ_RETRIES, ret);
33148c2ecf20Sopenharmony_ci		str = NULL;
33158c2ecf20Sopenharmony_ci		goto out;
33168c2ecf20Sopenharmony_ci	}
33178c2ecf20Sopenharmony_ci
33188c2ecf20Sopenharmony_ci	if (uc_str->len <= QUERY_DESC_HDR_SIZE) {
33198c2ecf20Sopenharmony_ci		dev_dbg(hba->dev, "String Desc is of zero length\n");
33208c2ecf20Sopenharmony_ci		str = NULL;
33218c2ecf20Sopenharmony_ci		ret = 0;
33228c2ecf20Sopenharmony_ci		goto out;
33238c2ecf20Sopenharmony_ci	}
33248c2ecf20Sopenharmony_ci
33258c2ecf20Sopenharmony_ci	if (ascii) {
33268c2ecf20Sopenharmony_ci		ssize_t ascii_len;
33278c2ecf20Sopenharmony_ci		int i;
33288c2ecf20Sopenharmony_ci		/* remove header and divide by 2 to move from UTF16 to UTF8 */
33298c2ecf20Sopenharmony_ci		ascii_len = (uc_str->len - QUERY_DESC_HDR_SIZE) / 2 + 1;
33308c2ecf20Sopenharmony_ci		str = kzalloc(ascii_len, GFP_KERNEL);
33318c2ecf20Sopenharmony_ci		if (!str) {
33328c2ecf20Sopenharmony_ci			ret = -ENOMEM;
33338c2ecf20Sopenharmony_ci			goto out;
33348c2ecf20Sopenharmony_ci		}
33358c2ecf20Sopenharmony_ci
33368c2ecf20Sopenharmony_ci		/*
33378c2ecf20Sopenharmony_ci		 * the descriptor contains string in UTF16 format
33388c2ecf20Sopenharmony_ci		 * we need to convert to utf-8 so it can be displayed
33398c2ecf20Sopenharmony_ci		 */
33408c2ecf20Sopenharmony_ci		ret = utf16s_to_utf8s(uc_str->uc,
33418c2ecf20Sopenharmony_ci				      uc_str->len - QUERY_DESC_HDR_SIZE,
33428c2ecf20Sopenharmony_ci				      UTF16_BIG_ENDIAN, str, ascii_len - 1);
33438c2ecf20Sopenharmony_ci
33448c2ecf20Sopenharmony_ci		/* replace non-printable or non-ASCII characters with spaces */
33458c2ecf20Sopenharmony_ci		for (i = 0; i < ret; i++)
33468c2ecf20Sopenharmony_ci			str[i] = ufshcd_remove_non_printable(str[i]);
33478c2ecf20Sopenharmony_ci
33488c2ecf20Sopenharmony_ci		str[ret++] = '\0';
33498c2ecf20Sopenharmony_ci
33508c2ecf20Sopenharmony_ci	} else {
33518c2ecf20Sopenharmony_ci		str = kmemdup(uc_str, uc_str->len, GFP_KERNEL);
33528c2ecf20Sopenharmony_ci		if (!str) {
33538c2ecf20Sopenharmony_ci			ret = -ENOMEM;
33548c2ecf20Sopenharmony_ci			goto out;
33558c2ecf20Sopenharmony_ci		}
33568c2ecf20Sopenharmony_ci		ret = uc_str->len;
33578c2ecf20Sopenharmony_ci	}
33588c2ecf20Sopenharmony_ciout:
33598c2ecf20Sopenharmony_ci	*buf = str;
33608c2ecf20Sopenharmony_ci	kfree(uc_str);
33618c2ecf20Sopenharmony_ci	return ret;
33628c2ecf20Sopenharmony_ci}
33638c2ecf20Sopenharmony_ci
33648c2ecf20Sopenharmony_ci/**
33658c2ecf20Sopenharmony_ci * ufshcd_read_unit_desc_param - read the specified unit descriptor parameter
33668c2ecf20Sopenharmony_ci * @hba: Pointer to adapter instance
33678c2ecf20Sopenharmony_ci * @lun: lun id
33688c2ecf20Sopenharmony_ci * @param_offset: offset of the parameter to read
33698c2ecf20Sopenharmony_ci * @param_read_buf: pointer to buffer where parameter would be read
33708c2ecf20Sopenharmony_ci * @param_size: sizeof(param_read_buf)
33718c2ecf20Sopenharmony_ci *
33728c2ecf20Sopenharmony_ci * Return 0 in case of success, non-zero otherwise
33738c2ecf20Sopenharmony_ci */
33748c2ecf20Sopenharmony_cistatic inline int ufshcd_read_unit_desc_param(struct ufs_hba *hba,
33758c2ecf20Sopenharmony_ci					      int lun,
33768c2ecf20Sopenharmony_ci					      enum unit_desc_param param_offset,
33778c2ecf20Sopenharmony_ci					      u8 *param_read_buf,
33788c2ecf20Sopenharmony_ci					      u32 param_size)
33798c2ecf20Sopenharmony_ci{
33808c2ecf20Sopenharmony_ci	/*
33818c2ecf20Sopenharmony_ci	 * Unit descriptors are only available for general purpose LUs (LUN id
33828c2ecf20Sopenharmony_ci	 * from 0 to 7) and RPMB Well known LU.
33838c2ecf20Sopenharmony_ci	 */
33848c2ecf20Sopenharmony_ci	if (!ufs_is_valid_unit_desc_lun(&hba->dev_info, lun, param_offset))
33858c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
33868c2ecf20Sopenharmony_ci
33878c2ecf20Sopenharmony_ci	return ufshcd_read_desc_param(hba, QUERY_DESC_IDN_UNIT, lun,
33888c2ecf20Sopenharmony_ci				      param_offset, param_read_buf, param_size);
33898c2ecf20Sopenharmony_ci}
33908c2ecf20Sopenharmony_ci
33918c2ecf20Sopenharmony_cistatic int ufshcd_get_ref_clk_gating_wait(struct ufs_hba *hba)
33928c2ecf20Sopenharmony_ci{
33938c2ecf20Sopenharmony_ci	int err = 0;
33948c2ecf20Sopenharmony_ci	u32 gating_wait = UFSHCD_REF_CLK_GATING_WAIT_US;
33958c2ecf20Sopenharmony_ci
33968c2ecf20Sopenharmony_ci	if (hba->dev_info.wspecversion >= 0x300) {
33978c2ecf20Sopenharmony_ci		err = ufshcd_query_attr_retry(hba, UPIU_QUERY_OPCODE_READ_ATTR,
33988c2ecf20Sopenharmony_ci				QUERY_ATTR_IDN_REF_CLK_GATING_WAIT_TIME, 0, 0,
33998c2ecf20Sopenharmony_ci				&gating_wait);
34008c2ecf20Sopenharmony_ci		if (err)
34018c2ecf20Sopenharmony_ci			dev_err(hba->dev, "Failed reading bRefClkGatingWait. err = %d, use default %uus\n",
34028c2ecf20Sopenharmony_ci					 err, gating_wait);
34038c2ecf20Sopenharmony_ci
34048c2ecf20Sopenharmony_ci		if (gating_wait == 0) {
34058c2ecf20Sopenharmony_ci			gating_wait = UFSHCD_REF_CLK_GATING_WAIT_US;
34068c2ecf20Sopenharmony_ci			dev_err(hba->dev, "Undefined ref clk gating wait time, use default %uus\n",
34078c2ecf20Sopenharmony_ci					 gating_wait);
34088c2ecf20Sopenharmony_ci		}
34098c2ecf20Sopenharmony_ci
34108c2ecf20Sopenharmony_ci		hba->dev_info.clk_gating_wait_us = gating_wait;
34118c2ecf20Sopenharmony_ci	}
34128c2ecf20Sopenharmony_ci
34138c2ecf20Sopenharmony_ci	return err;
34148c2ecf20Sopenharmony_ci}
34158c2ecf20Sopenharmony_ci
34168c2ecf20Sopenharmony_ci/**
34178c2ecf20Sopenharmony_ci * ufshcd_memory_alloc - allocate memory for host memory space data structures
34188c2ecf20Sopenharmony_ci * @hba: per adapter instance
34198c2ecf20Sopenharmony_ci *
34208c2ecf20Sopenharmony_ci * 1. Allocate DMA memory for Command Descriptor array
34218c2ecf20Sopenharmony_ci *	Each command descriptor consist of Command UPIU, Response UPIU and PRDT
34228c2ecf20Sopenharmony_ci * 2. Allocate DMA memory for UTP Transfer Request Descriptor List (UTRDL).
34238c2ecf20Sopenharmony_ci * 3. Allocate DMA memory for UTP Task Management Request Descriptor List
34248c2ecf20Sopenharmony_ci *	(UTMRDL)
34258c2ecf20Sopenharmony_ci * 4. Allocate memory for local reference block(lrb).
34268c2ecf20Sopenharmony_ci *
34278c2ecf20Sopenharmony_ci * Returns 0 for success, non-zero in case of failure
34288c2ecf20Sopenharmony_ci */
34298c2ecf20Sopenharmony_cistatic int ufshcd_memory_alloc(struct ufs_hba *hba)
34308c2ecf20Sopenharmony_ci{
34318c2ecf20Sopenharmony_ci	size_t utmrdl_size, utrdl_size, ucdl_size;
34328c2ecf20Sopenharmony_ci
34338c2ecf20Sopenharmony_ci	/* Allocate memory for UTP command descriptors */
34348c2ecf20Sopenharmony_ci	ucdl_size = (sizeof(struct utp_transfer_cmd_desc) * hba->nutrs);
34358c2ecf20Sopenharmony_ci	hba->ucdl_base_addr = dmam_alloc_coherent(hba->dev,
34368c2ecf20Sopenharmony_ci						  ucdl_size,
34378c2ecf20Sopenharmony_ci						  &hba->ucdl_dma_addr,
34388c2ecf20Sopenharmony_ci						  GFP_KERNEL);
34398c2ecf20Sopenharmony_ci
34408c2ecf20Sopenharmony_ci	/*
34418c2ecf20Sopenharmony_ci	 * UFSHCI requires UTP command descriptor to be 128 byte aligned.
34428c2ecf20Sopenharmony_ci	 * make sure hba->ucdl_dma_addr is aligned to PAGE_SIZE
34438c2ecf20Sopenharmony_ci	 * if hba->ucdl_dma_addr is aligned to PAGE_SIZE, then it will
34448c2ecf20Sopenharmony_ci	 * be aligned to 128 bytes as well
34458c2ecf20Sopenharmony_ci	 */
34468c2ecf20Sopenharmony_ci	if (!hba->ucdl_base_addr ||
34478c2ecf20Sopenharmony_ci	    WARN_ON(hba->ucdl_dma_addr & (PAGE_SIZE - 1))) {
34488c2ecf20Sopenharmony_ci		dev_err(hba->dev,
34498c2ecf20Sopenharmony_ci			"Command Descriptor Memory allocation failed\n");
34508c2ecf20Sopenharmony_ci		goto out;
34518c2ecf20Sopenharmony_ci	}
34528c2ecf20Sopenharmony_ci
34538c2ecf20Sopenharmony_ci	/*
34548c2ecf20Sopenharmony_ci	 * Allocate memory for UTP Transfer descriptors
34558c2ecf20Sopenharmony_ci	 * UFSHCI requires 1024 byte alignment of UTRD
34568c2ecf20Sopenharmony_ci	 */
34578c2ecf20Sopenharmony_ci	utrdl_size = (sizeof(struct utp_transfer_req_desc) * hba->nutrs);
34588c2ecf20Sopenharmony_ci	hba->utrdl_base_addr = dmam_alloc_coherent(hba->dev,
34598c2ecf20Sopenharmony_ci						   utrdl_size,
34608c2ecf20Sopenharmony_ci						   &hba->utrdl_dma_addr,
34618c2ecf20Sopenharmony_ci						   GFP_KERNEL);
34628c2ecf20Sopenharmony_ci	if (!hba->utrdl_base_addr ||
34638c2ecf20Sopenharmony_ci	    WARN_ON(hba->utrdl_dma_addr & (PAGE_SIZE - 1))) {
34648c2ecf20Sopenharmony_ci		dev_err(hba->dev,
34658c2ecf20Sopenharmony_ci			"Transfer Descriptor Memory allocation failed\n");
34668c2ecf20Sopenharmony_ci		goto out;
34678c2ecf20Sopenharmony_ci	}
34688c2ecf20Sopenharmony_ci
34698c2ecf20Sopenharmony_ci	/*
34708c2ecf20Sopenharmony_ci	 * Allocate memory for UTP Task Management descriptors
34718c2ecf20Sopenharmony_ci	 * UFSHCI requires 1024 byte alignment of UTMRD
34728c2ecf20Sopenharmony_ci	 */
34738c2ecf20Sopenharmony_ci	utmrdl_size = sizeof(struct utp_task_req_desc) * hba->nutmrs;
34748c2ecf20Sopenharmony_ci	hba->utmrdl_base_addr = dmam_alloc_coherent(hba->dev,
34758c2ecf20Sopenharmony_ci						    utmrdl_size,
34768c2ecf20Sopenharmony_ci						    &hba->utmrdl_dma_addr,
34778c2ecf20Sopenharmony_ci						    GFP_KERNEL);
34788c2ecf20Sopenharmony_ci	if (!hba->utmrdl_base_addr ||
34798c2ecf20Sopenharmony_ci	    WARN_ON(hba->utmrdl_dma_addr & (PAGE_SIZE - 1))) {
34808c2ecf20Sopenharmony_ci		dev_err(hba->dev,
34818c2ecf20Sopenharmony_ci		"Task Management Descriptor Memory allocation failed\n");
34828c2ecf20Sopenharmony_ci		goto out;
34838c2ecf20Sopenharmony_ci	}
34848c2ecf20Sopenharmony_ci
34858c2ecf20Sopenharmony_ci	/* Allocate memory for local reference block */
34868c2ecf20Sopenharmony_ci	hba->lrb = devm_kcalloc(hba->dev,
34878c2ecf20Sopenharmony_ci				hba->nutrs, sizeof(struct ufshcd_lrb),
34888c2ecf20Sopenharmony_ci				GFP_KERNEL);
34898c2ecf20Sopenharmony_ci	if (!hba->lrb) {
34908c2ecf20Sopenharmony_ci		dev_err(hba->dev, "LRB Memory allocation failed\n");
34918c2ecf20Sopenharmony_ci		goto out;
34928c2ecf20Sopenharmony_ci	}
34938c2ecf20Sopenharmony_ci	return 0;
34948c2ecf20Sopenharmony_ciout:
34958c2ecf20Sopenharmony_ci	return -ENOMEM;
34968c2ecf20Sopenharmony_ci}
34978c2ecf20Sopenharmony_ci
34988c2ecf20Sopenharmony_ci/**
34998c2ecf20Sopenharmony_ci * ufshcd_host_memory_configure - configure local reference block with
35008c2ecf20Sopenharmony_ci *				memory offsets
35018c2ecf20Sopenharmony_ci * @hba: per adapter instance
35028c2ecf20Sopenharmony_ci *
35038c2ecf20Sopenharmony_ci * Configure Host memory space
35048c2ecf20Sopenharmony_ci * 1. Update Corresponding UTRD.UCDBA and UTRD.UCDBAU with UCD DMA
35058c2ecf20Sopenharmony_ci * address.
35068c2ecf20Sopenharmony_ci * 2. Update each UTRD with Response UPIU offset, Response UPIU length
35078c2ecf20Sopenharmony_ci * and PRDT offset.
35088c2ecf20Sopenharmony_ci * 3. Save the corresponding addresses of UTRD, UCD.CMD, UCD.RSP and UCD.PRDT
35098c2ecf20Sopenharmony_ci * into local reference block.
35108c2ecf20Sopenharmony_ci */
35118c2ecf20Sopenharmony_cistatic void ufshcd_host_memory_configure(struct ufs_hba *hba)
35128c2ecf20Sopenharmony_ci{
35138c2ecf20Sopenharmony_ci	struct utp_transfer_req_desc *utrdlp;
35148c2ecf20Sopenharmony_ci	dma_addr_t cmd_desc_dma_addr;
35158c2ecf20Sopenharmony_ci	dma_addr_t cmd_desc_element_addr;
35168c2ecf20Sopenharmony_ci	u16 response_offset;
35178c2ecf20Sopenharmony_ci	u16 prdt_offset;
35188c2ecf20Sopenharmony_ci	int cmd_desc_size;
35198c2ecf20Sopenharmony_ci	int i;
35208c2ecf20Sopenharmony_ci
35218c2ecf20Sopenharmony_ci	utrdlp = hba->utrdl_base_addr;
35228c2ecf20Sopenharmony_ci
35238c2ecf20Sopenharmony_ci	response_offset =
35248c2ecf20Sopenharmony_ci		offsetof(struct utp_transfer_cmd_desc, response_upiu);
35258c2ecf20Sopenharmony_ci	prdt_offset =
35268c2ecf20Sopenharmony_ci		offsetof(struct utp_transfer_cmd_desc, prd_table);
35278c2ecf20Sopenharmony_ci
35288c2ecf20Sopenharmony_ci	cmd_desc_size = sizeof(struct utp_transfer_cmd_desc);
35298c2ecf20Sopenharmony_ci	cmd_desc_dma_addr = hba->ucdl_dma_addr;
35308c2ecf20Sopenharmony_ci
35318c2ecf20Sopenharmony_ci	for (i = 0; i < hba->nutrs; i++) {
35328c2ecf20Sopenharmony_ci		/* Configure UTRD with command descriptor base address */
35338c2ecf20Sopenharmony_ci		cmd_desc_element_addr =
35348c2ecf20Sopenharmony_ci				(cmd_desc_dma_addr + (cmd_desc_size * i));
35358c2ecf20Sopenharmony_ci		utrdlp[i].command_desc_base_addr_lo =
35368c2ecf20Sopenharmony_ci				cpu_to_le32(lower_32_bits(cmd_desc_element_addr));
35378c2ecf20Sopenharmony_ci		utrdlp[i].command_desc_base_addr_hi =
35388c2ecf20Sopenharmony_ci				cpu_to_le32(upper_32_bits(cmd_desc_element_addr));
35398c2ecf20Sopenharmony_ci
35408c2ecf20Sopenharmony_ci		/* Response upiu and prdt offset should be in double words */
35418c2ecf20Sopenharmony_ci		if (hba->quirks & UFSHCD_QUIRK_PRDT_BYTE_GRAN) {
35428c2ecf20Sopenharmony_ci			utrdlp[i].response_upiu_offset =
35438c2ecf20Sopenharmony_ci				cpu_to_le16(response_offset);
35448c2ecf20Sopenharmony_ci			utrdlp[i].prd_table_offset =
35458c2ecf20Sopenharmony_ci				cpu_to_le16(prdt_offset);
35468c2ecf20Sopenharmony_ci			utrdlp[i].response_upiu_length =
35478c2ecf20Sopenharmony_ci				cpu_to_le16(ALIGNED_UPIU_SIZE);
35488c2ecf20Sopenharmony_ci		} else {
35498c2ecf20Sopenharmony_ci			utrdlp[i].response_upiu_offset =
35508c2ecf20Sopenharmony_ci				cpu_to_le16(response_offset >> 2);
35518c2ecf20Sopenharmony_ci			utrdlp[i].prd_table_offset =
35528c2ecf20Sopenharmony_ci				cpu_to_le16(prdt_offset >> 2);
35538c2ecf20Sopenharmony_ci			utrdlp[i].response_upiu_length =
35548c2ecf20Sopenharmony_ci				cpu_to_le16(ALIGNED_UPIU_SIZE >> 2);
35558c2ecf20Sopenharmony_ci		}
35568c2ecf20Sopenharmony_ci
35578c2ecf20Sopenharmony_ci		ufshcd_init_lrb(hba, &hba->lrb[i], i);
35588c2ecf20Sopenharmony_ci	}
35598c2ecf20Sopenharmony_ci}
35608c2ecf20Sopenharmony_ci
35618c2ecf20Sopenharmony_ci/**
35628c2ecf20Sopenharmony_ci * ufshcd_dme_link_startup - Notify Unipro to perform link startup
35638c2ecf20Sopenharmony_ci * @hba: per adapter instance
35648c2ecf20Sopenharmony_ci *
35658c2ecf20Sopenharmony_ci * UIC_CMD_DME_LINK_STARTUP command must be issued to Unipro layer,
35668c2ecf20Sopenharmony_ci * in order to initialize the Unipro link startup procedure.
35678c2ecf20Sopenharmony_ci * Once the Unipro links are up, the device connected to the controller
35688c2ecf20Sopenharmony_ci * is detected.
35698c2ecf20Sopenharmony_ci *
35708c2ecf20Sopenharmony_ci * Returns 0 on success, non-zero value on failure
35718c2ecf20Sopenharmony_ci */
35728c2ecf20Sopenharmony_cistatic int ufshcd_dme_link_startup(struct ufs_hba *hba)
35738c2ecf20Sopenharmony_ci{
35748c2ecf20Sopenharmony_ci	struct uic_command uic_cmd = {0};
35758c2ecf20Sopenharmony_ci	int ret;
35768c2ecf20Sopenharmony_ci
35778c2ecf20Sopenharmony_ci	uic_cmd.command = UIC_CMD_DME_LINK_STARTUP;
35788c2ecf20Sopenharmony_ci
35798c2ecf20Sopenharmony_ci	ret = ufshcd_send_uic_cmd(hba, &uic_cmd);
35808c2ecf20Sopenharmony_ci	if (ret)
35818c2ecf20Sopenharmony_ci		dev_dbg(hba->dev,
35828c2ecf20Sopenharmony_ci			"dme-link-startup: error code %d\n", ret);
35838c2ecf20Sopenharmony_ci	return ret;
35848c2ecf20Sopenharmony_ci}
35858c2ecf20Sopenharmony_ci/**
35868c2ecf20Sopenharmony_ci * ufshcd_dme_reset - UIC command for DME_RESET
35878c2ecf20Sopenharmony_ci * @hba: per adapter instance
35888c2ecf20Sopenharmony_ci *
35898c2ecf20Sopenharmony_ci * DME_RESET command is issued in order to reset UniPro stack.
35908c2ecf20Sopenharmony_ci * This function now deals with cold reset.
35918c2ecf20Sopenharmony_ci *
35928c2ecf20Sopenharmony_ci * Returns 0 on success, non-zero value on failure
35938c2ecf20Sopenharmony_ci */
35948c2ecf20Sopenharmony_cistatic int ufshcd_dme_reset(struct ufs_hba *hba)
35958c2ecf20Sopenharmony_ci{
35968c2ecf20Sopenharmony_ci	struct uic_command uic_cmd = {0};
35978c2ecf20Sopenharmony_ci	int ret;
35988c2ecf20Sopenharmony_ci
35998c2ecf20Sopenharmony_ci	uic_cmd.command = UIC_CMD_DME_RESET;
36008c2ecf20Sopenharmony_ci
36018c2ecf20Sopenharmony_ci	ret = ufshcd_send_uic_cmd(hba, &uic_cmd);
36028c2ecf20Sopenharmony_ci	if (ret)
36038c2ecf20Sopenharmony_ci		dev_err(hba->dev,
36048c2ecf20Sopenharmony_ci			"dme-reset: error code %d\n", ret);
36058c2ecf20Sopenharmony_ci
36068c2ecf20Sopenharmony_ci	return ret;
36078c2ecf20Sopenharmony_ci}
36088c2ecf20Sopenharmony_ci
36098c2ecf20Sopenharmony_ci/**
36108c2ecf20Sopenharmony_ci * ufshcd_dme_enable - UIC command for DME_ENABLE
36118c2ecf20Sopenharmony_ci * @hba: per adapter instance
36128c2ecf20Sopenharmony_ci *
36138c2ecf20Sopenharmony_ci * DME_ENABLE command is issued in order to enable UniPro stack.
36148c2ecf20Sopenharmony_ci *
36158c2ecf20Sopenharmony_ci * Returns 0 on success, non-zero value on failure
36168c2ecf20Sopenharmony_ci */
36178c2ecf20Sopenharmony_cistatic int ufshcd_dme_enable(struct ufs_hba *hba)
36188c2ecf20Sopenharmony_ci{
36198c2ecf20Sopenharmony_ci	struct uic_command uic_cmd = {0};
36208c2ecf20Sopenharmony_ci	int ret;
36218c2ecf20Sopenharmony_ci
36228c2ecf20Sopenharmony_ci	uic_cmd.command = UIC_CMD_DME_ENABLE;
36238c2ecf20Sopenharmony_ci
36248c2ecf20Sopenharmony_ci	ret = ufshcd_send_uic_cmd(hba, &uic_cmd);
36258c2ecf20Sopenharmony_ci	if (ret)
36268c2ecf20Sopenharmony_ci		dev_err(hba->dev,
36278c2ecf20Sopenharmony_ci			"dme-enable: error code %d\n", ret);
36288c2ecf20Sopenharmony_ci
36298c2ecf20Sopenharmony_ci	return ret;
36308c2ecf20Sopenharmony_ci}
36318c2ecf20Sopenharmony_ci
36328c2ecf20Sopenharmony_cistatic inline void ufshcd_add_delay_before_dme_cmd(struct ufs_hba *hba)
36338c2ecf20Sopenharmony_ci{
36348c2ecf20Sopenharmony_ci	#define MIN_DELAY_BEFORE_DME_CMDS_US	1000
36358c2ecf20Sopenharmony_ci	unsigned long min_sleep_time_us;
36368c2ecf20Sopenharmony_ci
36378c2ecf20Sopenharmony_ci	if (!(hba->quirks & UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS))
36388c2ecf20Sopenharmony_ci		return;
36398c2ecf20Sopenharmony_ci
36408c2ecf20Sopenharmony_ci	/*
36418c2ecf20Sopenharmony_ci	 * last_dme_cmd_tstamp will be 0 only for 1st call to
36428c2ecf20Sopenharmony_ci	 * this function
36438c2ecf20Sopenharmony_ci	 */
36448c2ecf20Sopenharmony_ci	if (unlikely(!ktime_to_us(hba->last_dme_cmd_tstamp))) {
36458c2ecf20Sopenharmony_ci		min_sleep_time_us = MIN_DELAY_BEFORE_DME_CMDS_US;
36468c2ecf20Sopenharmony_ci	} else {
36478c2ecf20Sopenharmony_ci		unsigned long delta =
36488c2ecf20Sopenharmony_ci			(unsigned long) ktime_to_us(
36498c2ecf20Sopenharmony_ci				ktime_sub(ktime_get(),
36508c2ecf20Sopenharmony_ci				hba->last_dme_cmd_tstamp));
36518c2ecf20Sopenharmony_ci
36528c2ecf20Sopenharmony_ci		if (delta < MIN_DELAY_BEFORE_DME_CMDS_US)
36538c2ecf20Sopenharmony_ci			min_sleep_time_us =
36548c2ecf20Sopenharmony_ci				MIN_DELAY_BEFORE_DME_CMDS_US - delta;
36558c2ecf20Sopenharmony_ci		else
36568c2ecf20Sopenharmony_ci			return; /* no more delay required */
36578c2ecf20Sopenharmony_ci	}
36588c2ecf20Sopenharmony_ci
36598c2ecf20Sopenharmony_ci	/* allow sleep for extra 50us if needed */
36608c2ecf20Sopenharmony_ci	usleep_range(min_sleep_time_us, min_sleep_time_us + 50);
36618c2ecf20Sopenharmony_ci}
36628c2ecf20Sopenharmony_ci
36638c2ecf20Sopenharmony_ci/**
36648c2ecf20Sopenharmony_ci * ufshcd_dme_set_attr - UIC command for DME_SET, DME_PEER_SET
36658c2ecf20Sopenharmony_ci * @hba: per adapter instance
36668c2ecf20Sopenharmony_ci * @attr_sel: uic command argument1
36678c2ecf20Sopenharmony_ci * @attr_set: attribute set type as uic command argument2
36688c2ecf20Sopenharmony_ci * @mib_val: setting value as uic command argument3
36698c2ecf20Sopenharmony_ci * @peer: indicate whether peer or local
36708c2ecf20Sopenharmony_ci *
36718c2ecf20Sopenharmony_ci * Returns 0 on success, non-zero value on failure
36728c2ecf20Sopenharmony_ci */
36738c2ecf20Sopenharmony_ciint ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel,
36748c2ecf20Sopenharmony_ci			u8 attr_set, u32 mib_val, u8 peer)
36758c2ecf20Sopenharmony_ci{
36768c2ecf20Sopenharmony_ci	struct uic_command uic_cmd = {0};
36778c2ecf20Sopenharmony_ci	static const char *const action[] = {
36788c2ecf20Sopenharmony_ci		"dme-set",
36798c2ecf20Sopenharmony_ci		"dme-peer-set"
36808c2ecf20Sopenharmony_ci	};
36818c2ecf20Sopenharmony_ci	const char *set = action[!!peer];
36828c2ecf20Sopenharmony_ci	int ret;
36838c2ecf20Sopenharmony_ci	int retries = UFS_UIC_COMMAND_RETRIES;
36848c2ecf20Sopenharmony_ci
36858c2ecf20Sopenharmony_ci	uic_cmd.command = peer ?
36868c2ecf20Sopenharmony_ci		UIC_CMD_DME_PEER_SET : UIC_CMD_DME_SET;
36878c2ecf20Sopenharmony_ci	uic_cmd.argument1 = attr_sel;
36888c2ecf20Sopenharmony_ci	uic_cmd.argument2 = UIC_ARG_ATTR_TYPE(attr_set);
36898c2ecf20Sopenharmony_ci	uic_cmd.argument3 = mib_val;
36908c2ecf20Sopenharmony_ci
36918c2ecf20Sopenharmony_ci	do {
36928c2ecf20Sopenharmony_ci		/* for peer attributes we retry upon failure */
36938c2ecf20Sopenharmony_ci		ret = ufshcd_send_uic_cmd(hba, &uic_cmd);
36948c2ecf20Sopenharmony_ci		if (ret)
36958c2ecf20Sopenharmony_ci			dev_dbg(hba->dev, "%s: attr-id 0x%x val 0x%x error code %d\n",
36968c2ecf20Sopenharmony_ci				set, UIC_GET_ATTR_ID(attr_sel), mib_val, ret);
36978c2ecf20Sopenharmony_ci	} while (ret && peer && --retries);
36988c2ecf20Sopenharmony_ci
36998c2ecf20Sopenharmony_ci	if (ret)
37008c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: attr-id 0x%x val 0x%x failed %d retries\n",
37018c2ecf20Sopenharmony_ci			set, UIC_GET_ATTR_ID(attr_sel), mib_val,
37028c2ecf20Sopenharmony_ci			UFS_UIC_COMMAND_RETRIES - retries);
37038c2ecf20Sopenharmony_ci
37048c2ecf20Sopenharmony_ci	return ret;
37058c2ecf20Sopenharmony_ci}
37068c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ufshcd_dme_set_attr);
37078c2ecf20Sopenharmony_ci
37088c2ecf20Sopenharmony_ci/**
37098c2ecf20Sopenharmony_ci * ufshcd_dme_get_attr - UIC command for DME_GET, DME_PEER_GET
37108c2ecf20Sopenharmony_ci * @hba: per adapter instance
37118c2ecf20Sopenharmony_ci * @attr_sel: uic command argument1
37128c2ecf20Sopenharmony_ci * @mib_val: the value of the attribute as returned by the UIC command
37138c2ecf20Sopenharmony_ci * @peer: indicate whether peer or local
37148c2ecf20Sopenharmony_ci *
37158c2ecf20Sopenharmony_ci * Returns 0 on success, non-zero value on failure
37168c2ecf20Sopenharmony_ci */
37178c2ecf20Sopenharmony_ciint ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel,
37188c2ecf20Sopenharmony_ci			u32 *mib_val, u8 peer)
37198c2ecf20Sopenharmony_ci{
37208c2ecf20Sopenharmony_ci	struct uic_command uic_cmd = {0};
37218c2ecf20Sopenharmony_ci	static const char *const action[] = {
37228c2ecf20Sopenharmony_ci		"dme-get",
37238c2ecf20Sopenharmony_ci		"dme-peer-get"
37248c2ecf20Sopenharmony_ci	};
37258c2ecf20Sopenharmony_ci	const char *get = action[!!peer];
37268c2ecf20Sopenharmony_ci	int ret;
37278c2ecf20Sopenharmony_ci	int retries = UFS_UIC_COMMAND_RETRIES;
37288c2ecf20Sopenharmony_ci	struct ufs_pa_layer_attr orig_pwr_info;
37298c2ecf20Sopenharmony_ci	struct ufs_pa_layer_attr temp_pwr_info;
37308c2ecf20Sopenharmony_ci	bool pwr_mode_change = false;
37318c2ecf20Sopenharmony_ci
37328c2ecf20Sopenharmony_ci	if (peer && (hba->quirks & UFSHCD_QUIRK_DME_PEER_ACCESS_AUTO_MODE)) {
37338c2ecf20Sopenharmony_ci		orig_pwr_info = hba->pwr_info;
37348c2ecf20Sopenharmony_ci		temp_pwr_info = orig_pwr_info;
37358c2ecf20Sopenharmony_ci
37368c2ecf20Sopenharmony_ci		if (orig_pwr_info.pwr_tx == FAST_MODE ||
37378c2ecf20Sopenharmony_ci		    orig_pwr_info.pwr_rx == FAST_MODE) {
37388c2ecf20Sopenharmony_ci			temp_pwr_info.pwr_tx = FASTAUTO_MODE;
37398c2ecf20Sopenharmony_ci			temp_pwr_info.pwr_rx = FASTAUTO_MODE;
37408c2ecf20Sopenharmony_ci			pwr_mode_change = true;
37418c2ecf20Sopenharmony_ci		} else if (orig_pwr_info.pwr_tx == SLOW_MODE ||
37428c2ecf20Sopenharmony_ci		    orig_pwr_info.pwr_rx == SLOW_MODE) {
37438c2ecf20Sopenharmony_ci			temp_pwr_info.pwr_tx = SLOWAUTO_MODE;
37448c2ecf20Sopenharmony_ci			temp_pwr_info.pwr_rx = SLOWAUTO_MODE;
37458c2ecf20Sopenharmony_ci			pwr_mode_change = true;
37468c2ecf20Sopenharmony_ci		}
37478c2ecf20Sopenharmony_ci		if (pwr_mode_change) {
37488c2ecf20Sopenharmony_ci			ret = ufshcd_change_power_mode(hba, &temp_pwr_info);
37498c2ecf20Sopenharmony_ci			if (ret)
37508c2ecf20Sopenharmony_ci				goto out;
37518c2ecf20Sopenharmony_ci		}
37528c2ecf20Sopenharmony_ci	}
37538c2ecf20Sopenharmony_ci
37548c2ecf20Sopenharmony_ci	uic_cmd.command = peer ?
37558c2ecf20Sopenharmony_ci		UIC_CMD_DME_PEER_GET : UIC_CMD_DME_GET;
37568c2ecf20Sopenharmony_ci	uic_cmd.argument1 = attr_sel;
37578c2ecf20Sopenharmony_ci
37588c2ecf20Sopenharmony_ci	do {
37598c2ecf20Sopenharmony_ci		/* for peer attributes we retry upon failure */
37608c2ecf20Sopenharmony_ci		ret = ufshcd_send_uic_cmd(hba, &uic_cmd);
37618c2ecf20Sopenharmony_ci		if (ret)
37628c2ecf20Sopenharmony_ci			dev_dbg(hba->dev, "%s: attr-id 0x%x error code %d\n",
37638c2ecf20Sopenharmony_ci				get, UIC_GET_ATTR_ID(attr_sel), ret);
37648c2ecf20Sopenharmony_ci	} while (ret && peer && --retries);
37658c2ecf20Sopenharmony_ci
37668c2ecf20Sopenharmony_ci	if (ret)
37678c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: attr-id 0x%x failed %d retries\n",
37688c2ecf20Sopenharmony_ci			get, UIC_GET_ATTR_ID(attr_sel),
37698c2ecf20Sopenharmony_ci			UFS_UIC_COMMAND_RETRIES - retries);
37708c2ecf20Sopenharmony_ci
37718c2ecf20Sopenharmony_ci	if (mib_val && !ret)
37728c2ecf20Sopenharmony_ci		*mib_val = uic_cmd.argument3;
37738c2ecf20Sopenharmony_ci
37748c2ecf20Sopenharmony_ci	if (peer && (hba->quirks & UFSHCD_QUIRK_DME_PEER_ACCESS_AUTO_MODE)
37758c2ecf20Sopenharmony_ci	    && pwr_mode_change)
37768c2ecf20Sopenharmony_ci		ufshcd_change_power_mode(hba, &orig_pwr_info);
37778c2ecf20Sopenharmony_ciout:
37788c2ecf20Sopenharmony_ci	return ret;
37798c2ecf20Sopenharmony_ci}
37808c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ufshcd_dme_get_attr);
37818c2ecf20Sopenharmony_ci
37828c2ecf20Sopenharmony_ci/**
37838c2ecf20Sopenharmony_ci * ufshcd_uic_pwr_ctrl - executes UIC commands (which affects the link power
37848c2ecf20Sopenharmony_ci * state) and waits for it to take effect.
37858c2ecf20Sopenharmony_ci *
37868c2ecf20Sopenharmony_ci * @hba: per adapter instance
37878c2ecf20Sopenharmony_ci * @cmd: UIC command to execute
37888c2ecf20Sopenharmony_ci *
37898c2ecf20Sopenharmony_ci * DME operations like DME_SET(PA_PWRMODE), DME_HIBERNATE_ENTER &
37908c2ecf20Sopenharmony_ci * DME_HIBERNATE_EXIT commands take some time to take its effect on both host
37918c2ecf20Sopenharmony_ci * and device UniPro link and hence it's final completion would be indicated by
37928c2ecf20Sopenharmony_ci * dedicated status bits in Interrupt Status register (UPMS, UHES, UHXS) in
37938c2ecf20Sopenharmony_ci * addition to normal UIC command completion Status (UCCS). This function only
37948c2ecf20Sopenharmony_ci * returns after the relevant status bits indicate the completion.
37958c2ecf20Sopenharmony_ci *
37968c2ecf20Sopenharmony_ci * Returns 0 on success, non-zero value on failure
37978c2ecf20Sopenharmony_ci */
37988c2ecf20Sopenharmony_cistatic int ufshcd_uic_pwr_ctrl(struct ufs_hba *hba, struct uic_command *cmd)
37998c2ecf20Sopenharmony_ci{
38008c2ecf20Sopenharmony_ci	struct completion uic_async_done;
38018c2ecf20Sopenharmony_ci	unsigned long flags;
38028c2ecf20Sopenharmony_ci	u8 status;
38038c2ecf20Sopenharmony_ci	int ret;
38048c2ecf20Sopenharmony_ci	bool reenable_intr = false;
38058c2ecf20Sopenharmony_ci
38068c2ecf20Sopenharmony_ci	mutex_lock(&hba->uic_cmd_mutex);
38078c2ecf20Sopenharmony_ci	init_completion(&uic_async_done);
38088c2ecf20Sopenharmony_ci	ufshcd_add_delay_before_dme_cmd(hba);
38098c2ecf20Sopenharmony_ci
38108c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
38118c2ecf20Sopenharmony_ci	if (ufshcd_is_link_broken(hba)) {
38128c2ecf20Sopenharmony_ci		ret = -ENOLINK;
38138c2ecf20Sopenharmony_ci		goto out_unlock;
38148c2ecf20Sopenharmony_ci	}
38158c2ecf20Sopenharmony_ci	hba->uic_async_done = &uic_async_done;
38168c2ecf20Sopenharmony_ci	if (ufshcd_readl(hba, REG_INTERRUPT_ENABLE) & UIC_COMMAND_COMPL) {
38178c2ecf20Sopenharmony_ci		ufshcd_disable_intr(hba, UIC_COMMAND_COMPL);
38188c2ecf20Sopenharmony_ci		/*
38198c2ecf20Sopenharmony_ci		 * Make sure UIC command completion interrupt is disabled before
38208c2ecf20Sopenharmony_ci		 * issuing UIC command.
38218c2ecf20Sopenharmony_ci		 */
38228c2ecf20Sopenharmony_ci		wmb();
38238c2ecf20Sopenharmony_ci		reenable_intr = true;
38248c2ecf20Sopenharmony_ci	}
38258c2ecf20Sopenharmony_ci	ret = __ufshcd_send_uic_cmd(hba, cmd, false);
38268c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
38278c2ecf20Sopenharmony_ci	if (ret) {
38288c2ecf20Sopenharmony_ci		dev_err(hba->dev,
38298c2ecf20Sopenharmony_ci			"pwr ctrl cmd 0x%x with mode 0x%x uic error %d\n",
38308c2ecf20Sopenharmony_ci			cmd->command, cmd->argument3, ret);
38318c2ecf20Sopenharmony_ci		goto out;
38328c2ecf20Sopenharmony_ci	}
38338c2ecf20Sopenharmony_ci
38348c2ecf20Sopenharmony_ci	if (!wait_for_completion_timeout(hba->uic_async_done,
38358c2ecf20Sopenharmony_ci					 msecs_to_jiffies(UIC_CMD_TIMEOUT))) {
38368c2ecf20Sopenharmony_ci		dev_err(hba->dev,
38378c2ecf20Sopenharmony_ci			"pwr ctrl cmd 0x%x with mode 0x%x completion timeout\n",
38388c2ecf20Sopenharmony_ci			cmd->command, cmd->argument3);
38398c2ecf20Sopenharmony_ci
38408c2ecf20Sopenharmony_ci		if (!cmd->cmd_active) {
38418c2ecf20Sopenharmony_ci			dev_err(hba->dev, "%s: Power Mode Change operation has been completed, go check UPMCRS\n",
38428c2ecf20Sopenharmony_ci				__func__);
38438c2ecf20Sopenharmony_ci			goto check_upmcrs;
38448c2ecf20Sopenharmony_ci		}
38458c2ecf20Sopenharmony_ci
38468c2ecf20Sopenharmony_ci		ret = -ETIMEDOUT;
38478c2ecf20Sopenharmony_ci		goto out;
38488c2ecf20Sopenharmony_ci	}
38498c2ecf20Sopenharmony_ci
38508c2ecf20Sopenharmony_cicheck_upmcrs:
38518c2ecf20Sopenharmony_ci	status = ufshcd_get_upmcrs(hba);
38528c2ecf20Sopenharmony_ci	if (status != PWR_LOCAL) {
38538c2ecf20Sopenharmony_ci		dev_err(hba->dev,
38548c2ecf20Sopenharmony_ci			"pwr ctrl cmd 0x%x failed, host upmcrs:0x%x\n",
38558c2ecf20Sopenharmony_ci			cmd->command, status);
38568c2ecf20Sopenharmony_ci		ret = (status != PWR_OK) ? status : -1;
38578c2ecf20Sopenharmony_ci	}
38588c2ecf20Sopenharmony_ciout:
38598c2ecf20Sopenharmony_ci	if (ret) {
38608c2ecf20Sopenharmony_ci		ufshcd_print_host_state(hba);
38618c2ecf20Sopenharmony_ci		ufshcd_print_pwr_info(hba);
38628c2ecf20Sopenharmony_ci		ufshcd_print_host_regs(hba);
38638c2ecf20Sopenharmony_ci	}
38648c2ecf20Sopenharmony_ci
38658c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
38668c2ecf20Sopenharmony_ci	hba->active_uic_cmd = NULL;
38678c2ecf20Sopenharmony_ci	hba->uic_async_done = NULL;
38688c2ecf20Sopenharmony_ci	if (reenable_intr)
38698c2ecf20Sopenharmony_ci		ufshcd_enable_intr(hba, UIC_COMMAND_COMPL);
38708c2ecf20Sopenharmony_ci	if (ret) {
38718c2ecf20Sopenharmony_ci		ufshcd_set_link_broken(hba);
38728c2ecf20Sopenharmony_ci		ufshcd_schedule_eh_work(hba);
38738c2ecf20Sopenharmony_ci	}
38748c2ecf20Sopenharmony_ciout_unlock:
38758c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
38768c2ecf20Sopenharmony_ci	mutex_unlock(&hba->uic_cmd_mutex);
38778c2ecf20Sopenharmony_ci
38788c2ecf20Sopenharmony_ci	return ret;
38798c2ecf20Sopenharmony_ci}
38808c2ecf20Sopenharmony_ci
38818c2ecf20Sopenharmony_ci/**
38828c2ecf20Sopenharmony_ci * ufshcd_uic_change_pwr_mode - Perform the UIC power mode chage
38838c2ecf20Sopenharmony_ci *				using DME_SET primitives.
38848c2ecf20Sopenharmony_ci * @hba: per adapter instance
38858c2ecf20Sopenharmony_ci * @mode: powr mode value
38868c2ecf20Sopenharmony_ci *
38878c2ecf20Sopenharmony_ci * Returns 0 on success, non-zero value on failure
38888c2ecf20Sopenharmony_ci */
38898c2ecf20Sopenharmony_cistatic int ufshcd_uic_change_pwr_mode(struct ufs_hba *hba, u8 mode)
38908c2ecf20Sopenharmony_ci{
38918c2ecf20Sopenharmony_ci	struct uic_command uic_cmd = {0};
38928c2ecf20Sopenharmony_ci	int ret;
38938c2ecf20Sopenharmony_ci
38948c2ecf20Sopenharmony_ci	if (hba->quirks & UFSHCD_QUIRK_BROKEN_PA_RXHSUNTERMCAP) {
38958c2ecf20Sopenharmony_ci		ret = ufshcd_dme_set(hba,
38968c2ecf20Sopenharmony_ci				UIC_ARG_MIB_SEL(PA_RXHSUNTERMCAP, 0), 1);
38978c2ecf20Sopenharmony_ci		if (ret) {
38988c2ecf20Sopenharmony_ci			dev_err(hba->dev, "%s: failed to enable PA_RXHSUNTERMCAP ret %d\n",
38998c2ecf20Sopenharmony_ci						__func__, ret);
39008c2ecf20Sopenharmony_ci			goto out;
39018c2ecf20Sopenharmony_ci		}
39028c2ecf20Sopenharmony_ci	}
39038c2ecf20Sopenharmony_ci
39048c2ecf20Sopenharmony_ci	uic_cmd.command = UIC_CMD_DME_SET;
39058c2ecf20Sopenharmony_ci	uic_cmd.argument1 = UIC_ARG_MIB(PA_PWRMODE);
39068c2ecf20Sopenharmony_ci	uic_cmd.argument3 = mode;
39078c2ecf20Sopenharmony_ci	ufshcd_hold(hba, false);
39088c2ecf20Sopenharmony_ci	ret = ufshcd_uic_pwr_ctrl(hba, &uic_cmd);
39098c2ecf20Sopenharmony_ci	ufshcd_release(hba);
39108c2ecf20Sopenharmony_ci
39118c2ecf20Sopenharmony_ciout:
39128c2ecf20Sopenharmony_ci	return ret;
39138c2ecf20Sopenharmony_ci}
39148c2ecf20Sopenharmony_ci
39158c2ecf20Sopenharmony_ciint ufshcd_link_recovery(struct ufs_hba *hba)
39168c2ecf20Sopenharmony_ci{
39178c2ecf20Sopenharmony_ci	int ret;
39188c2ecf20Sopenharmony_ci	unsigned long flags;
39198c2ecf20Sopenharmony_ci
39208c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
39218c2ecf20Sopenharmony_ci	hba->ufshcd_state = UFSHCD_STATE_RESET;
39228c2ecf20Sopenharmony_ci	ufshcd_set_eh_in_progress(hba);
39238c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
39248c2ecf20Sopenharmony_ci
39258c2ecf20Sopenharmony_ci	/* Reset the attached device */
39268c2ecf20Sopenharmony_ci	ufshcd_vops_device_reset(hba);
39278c2ecf20Sopenharmony_ci
39288c2ecf20Sopenharmony_ci	ret = ufshcd_host_reset_and_restore(hba);
39298c2ecf20Sopenharmony_ci
39308c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
39318c2ecf20Sopenharmony_ci	if (ret)
39328c2ecf20Sopenharmony_ci		hba->ufshcd_state = UFSHCD_STATE_ERROR;
39338c2ecf20Sopenharmony_ci	ufshcd_clear_eh_in_progress(hba);
39348c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
39358c2ecf20Sopenharmony_ci
39368c2ecf20Sopenharmony_ci	if (ret)
39378c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: link recovery failed, err %d",
39388c2ecf20Sopenharmony_ci			__func__, ret);
39398c2ecf20Sopenharmony_ci
39408c2ecf20Sopenharmony_ci	return ret;
39418c2ecf20Sopenharmony_ci}
39428c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ufshcd_link_recovery);
39438c2ecf20Sopenharmony_ci
39448c2ecf20Sopenharmony_cistatic int ufshcd_uic_hibern8_enter(struct ufs_hba *hba)
39458c2ecf20Sopenharmony_ci{
39468c2ecf20Sopenharmony_ci	int ret;
39478c2ecf20Sopenharmony_ci	struct uic_command uic_cmd = {0};
39488c2ecf20Sopenharmony_ci	ktime_t start = ktime_get();
39498c2ecf20Sopenharmony_ci
39508c2ecf20Sopenharmony_ci	ufshcd_vops_hibern8_notify(hba, UIC_CMD_DME_HIBER_ENTER, PRE_CHANGE);
39518c2ecf20Sopenharmony_ci
39528c2ecf20Sopenharmony_ci	uic_cmd.command = UIC_CMD_DME_HIBER_ENTER;
39538c2ecf20Sopenharmony_ci	ret = ufshcd_uic_pwr_ctrl(hba, &uic_cmd);
39548c2ecf20Sopenharmony_ci	trace_ufshcd_profile_hibern8(dev_name(hba->dev), "enter",
39558c2ecf20Sopenharmony_ci			     ktime_to_us(ktime_sub(ktime_get(), start)), ret);
39568c2ecf20Sopenharmony_ci
39578c2ecf20Sopenharmony_ci	if (ret)
39588c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: hibern8 enter failed. ret = %d\n",
39598c2ecf20Sopenharmony_ci			__func__, ret);
39608c2ecf20Sopenharmony_ci	else
39618c2ecf20Sopenharmony_ci		ufshcd_vops_hibern8_notify(hba, UIC_CMD_DME_HIBER_ENTER,
39628c2ecf20Sopenharmony_ci								POST_CHANGE);
39638c2ecf20Sopenharmony_ci
39648c2ecf20Sopenharmony_ci	return ret;
39658c2ecf20Sopenharmony_ci}
39668c2ecf20Sopenharmony_ci
39678c2ecf20Sopenharmony_ciint ufshcd_uic_hibern8_exit(struct ufs_hba *hba)
39688c2ecf20Sopenharmony_ci{
39698c2ecf20Sopenharmony_ci	struct uic_command uic_cmd = {0};
39708c2ecf20Sopenharmony_ci	int ret;
39718c2ecf20Sopenharmony_ci	ktime_t start = ktime_get();
39728c2ecf20Sopenharmony_ci
39738c2ecf20Sopenharmony_ci	ufshcd_vops_hibern8_notify(hba, UIC_CMD_DME_HIBER_EXIT, PRE_CHANGE);
39748c2ecf20Sopenharmony_ci
39758c2ecf20Sopenharmony_ci	uic_cmd.command = UIC_CMD_DME_HIBER_EXIT;
39768c2ecf20Sopenharmony_ci	ret = ufshcd_uic_pwr_ctrl(hba, &uic_cmd);
39778c2ecf20Sopenharmony_ci	trace_ufshcd_profile_hibern8(dev_name(hba->dev), "exit",
39788c2ecf20Sopenharmony_ci			     ktime_to_us(ktime_sub(ktime_get(), start)), ret);
39798c2ecf20Sopenharmony_ci
39808c2ecf20Sopenharmony_ci	if (ret) {
39818c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: hibern8 exit failed. ret = %d\n",
39828c2ecf20Sopenharmony_ci			__func__, ret);
39838c2ecf20Sopenharmony_ci	} else {
39848c2ecf20Sopenharmony_ci		ufshcd_vops_hibern8_notify(hba, UIC_CMD_DME_HIBER_EXIT,
39858c2ecf20Sopenharmony_ci								POST_CHANGE);
39868c2ecf20Sopenharmony_ci		hba->ufs_stats.last_hibern8_exit_tstamp = ktime_get();
39878c2ecf20Sopenharmony_ci		hba->ufs_stats.hibern8_exit_cnt++;
39888c2ecf20Sopenharmony_ci	}
39898c2ecf20Sopenharmony_ci
39908c2ecf20Sopenharmony_ci	return ret;
39918c2ecf20Sopenharmony_ci}
39928c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ufshcd_uic_hibern8_exit);
39938c2ecf20Sopenharmony_ci
39948c2ecf20Sopenharmony_civoid ufshcd_auto_hibern8_update(struct ufs_hba *hba, u32 ahit)
39958c2ecf20Sopenharmony_ci{
39968c2ecf20Sopenharmony_ci	unsigned long flags;
39978c2ecf20Sopenharmony_ci	bool update = false;
39988c2ecf20Sopenharmony_ci
39998c2ecf20Sopenharmony_ci	if (!ufshcd_is_auto_hibern8_supported(hba))
40008c2ecf20Sopenharmony_ci		return;
40018c2ecf20Sopenharmony_ci
40028c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
40038c2ecf20Sopenharmony_ci	if (hba->ahit != ahit) {
40048c2ecf20Sopenharmony_ci		hba->ahit = ahit;
40058c2ecf20Sopenharmony_ci		update = true;
40068c2ecf20Sopenharmony_ci	}
40078c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
40088c2ecf20Sopenharmony_ci
40098c2ecf20Sopenharmony_ci	if (update && !pm_runtime_suspended(hba->dev)) {
40108c2ecf20Sopenharmony_ci		pm_runtime_get_sync(hba->dev);
40118c2ecf20Sopenharmony_ci		ufshcd_hold(hba, false);
40128c2ecf20Sopenharmony_ci		ufshcd_auto_hibern8_enable(hba);
40138c2ecf20Sopenharmony_ci		ufshcd_release(hba);
40148c2ecf20Sopenharmony_ci		pm_runtime_put(hba->dev);
40158c2ecf20Sopenharmony_ci	}
40168c2ecf20Sopenharmony_ci}
40178c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ufshcd_auto_hibern8_update);
40188c2ecf20Sopenharmony_ci
40198c2ecf20Sopenharmony_civoid ufshcd_auto_hibern8_enable(struct ufs_hba *hba)
40208c2ecf20Sopenharmony_ci{
40218c2ecf20Sopenharmony_ci	unsigned long flags;
40228c2ecf20Sopenharmony_ci
40238c2ecf20Sopenharmony_ci	if (!ufshcd_is_auto_hibern8_supported(hba))
40248c2ecf20Sopenharmony_ci		return;
40258c2ecf20Sopenharmony_ci
40268c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
40278c2ecf20Sopenharmony_ci	ufshcd_writel(hba, hba->ahit, REG_AUTO_HIBERNATE_IDLE_TIMER);
40288c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
40298c2ecf20Sopenharmony_ci}
40308c2ecf20Sopenharmony_ci
40318c2ecf20Sopenharmony_ci /**
40328c2ecf20Sopenharmony_ci * ufshcd_init_pwr_info - setting the POR (power on reset)
40338c2ecf20Sopenharmony_ci * values in hba power info
40348c2ecf20Sopenharmony_ci * @hba: per-adapter instance
40358c2ecf20Sopenharmony_ci */
40368c2ecf20Sopenharmony_cistatic void ufshcd_init_pwr_info(struct ufs_hba *hba)
40378c2ecf20Sopenharmony_ci{
40388c2ecf20Sopenharmony_ci	hba->pwr_info.gear_rx = UFS_PWM_G1;
40398c2ecf20Sopenharmony_ci	hba->pwr_info.gear_tx = UFS_PWM_G1;
40408c2ecf20Sopenharmony_ci	hba->pwr_info.lane_rx = 1;
40418c2ecf20Sopenharmony_ci	hba->pwr_info.lane_tx = 1;
40428c2ecf20Sopenharmony_ci	hba->pwr_info.pwr_rx = SLOWAUTO_MODE;
40438c2ecf20Sopenharmony_ci	hba->pwr_info.pwr_tx = SLOWAUTO_MODE;
40448c2ecf20Sopenharmony_ci	hba->pwr_info.hs_rate = 0;
40458c2ecf20Sopenharmony_ci}
40468c2ecf20Sopenharmony_ci
40478c2ecf20Sopenharmony_ci/**
40488c2ecf20Sopenharmony_ci * ufshcd_get_max_pwr_mode - reads the max power mode negotiated with device
40498c2ecf20Sopenharmony_ci * @hba: per-adapter instance
40508c2ecf20Sopenharmony_ci */
40518c2ecf20Sopenharmony_cistatic int ufshcd_get_max_pwr_mode(struct ufs_hba *hba)
40528c2ecf20Sopenharmony_ci{
40538c2ecf20Sopenharmony_ci	struct ufs_pa_layer_attr *pwr_info = &hba->max_pwr_info.info;
40548c2ecf20Sopenharmony_ci
40558c2ecf20Sopenharmony_ci	if (hba->max_pwr_info.is_valid)
40568c2ecf20Sopenharmony_ci		return 0;
40578c2ecf20Sopenharmony_ci
40588c2ecf20Sopenharmony_ci	pwr_info->pwr_tx = FAST_MODE;
40598c2ecf20Sopenharmony_ci	pwr_info->pwr_rx = FAST_MODE;
40608c2ecf20Sopenharmony_ci	pwr_info->hs_rate = PA_HS_MODE_B;
40618c2ecf20Sopenharmony_ci
40628c2ecf20Sopenharmony_ci	/* Get the connected lane count */
40638c2ecf20Sopenharmony_ci	ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDRXDATALANES),
40648c2ecf20Sopenharmony_ci			&pwr_info->lane_rx);
40658c2ecf20Sopenharmony_ci	ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDTXDATALANES),
40668c2ecf20Sopenharmony_ci			&pwr_info->lane_tx);
40678c2ecf20Sopenharmony_ci
40688c2ecf20Sopenharmony_ci	if (!pwr_info->lane_rx || !pwr_info->lane_tx) {
40698c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: invalid connected lanes value. rx=%d, tx=%d\n",
40708c2ecf20Sopenharmony_ci				__func__,
40718c2ecf20Sopenharmony_ci				pwr_info->lane_rx,
40728c2ecf20Sopenharmony_ci				pwr_info->lane_tx);
40738c2ecf20Sopenharmony_ci		return -EINVAL;
40748c2ecf20Sopenharmony_ci	}
40758c2ecf20Sopenharmony_ci
40768c2ecf20Sopenharmony_ci	/*
40778c2ecf20Sopenharmony_ci	 * First, get the maximum gears of HS speed.
40788c2ecf20Sopenharmony_ci	 * If a zero value, it means there is no HSGEAR capability.
40798c2ecf20Sopenharmony_ci	 * Then, get the maximum gears of PWM speed.
40808c2ecf20Sopenharmony_ci	 */
40818c2ecf20Sopenharmony_ci	ufshcd_dme_get(hba, UIC_ARG_MIB(PA_MAXRXHSGEAR), &pwr_info->gear_rx);
40828c2ecf20Sopenharmony_ci	if (!pwr_info->gear_rx) {
40838c2ecf20Sopenharmony_ci		ufshcd_dme_get(hba, UIC_ARG_MIB(PA_MAXRXPWMGEAR),
40848c2ecf20Sopenharmony_ci				&pwr_info->gear_rx);
40858c2ecf20Sopenharmony_ci		if (!pwr_info->gear_rx) {
40868c2ecf20Sopenharmony_ci			dev_err(hba->dev, "%s: invalid max pwm rx gear read = %d\n",
40878c2ecf20Sopenharmony_ci				__func__, pwr_info->gear_rx);
40888c2ecf20Sopenharmony_ci			return -EINVAL;
40898c2ecf20Sopenharmony_ci		}
40908c2ecf20Sopenharmony_ci		pwr_info->pwr_rx = SLOW_MODE;
40918c2ecf20Sopenharmony_ci	}
40928c2ecf20Sopenharmony_ci
40938c2ecf20Sopenharmony_ci	ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_MAXRXHSGEAR),
40948c2ecf20Sopenharmony_ci			&pwr_info->gear_tx);
40958c2ecf20Sopenharmony_ci	if (!pwr_info->gear_tx) {
40968c2ecf20Sopenharmony_ci		ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_MAXRXPWMGEAR),
40978c2ecf20Sopenharmony_ci				&pwr_info->gear_tx);
40988c2ecf20Sopenharmony_ci		if (!pwr_info->gear_tx) {
40998c2ecf20Sopenharmony_ci			dev_err(hba->dev, "%s: invalid max pwm tx gear read = %d\n",
41008c2ecf20Sopenharmony_ci				__func__, pwr_info->gear_tx);
41018c2ecf20Sopenharmony_ci			return -EINVAL;
41028c2ecf20Sopenharmony_ci		}
41038c2ecf20Sopenharmony_ci		pwr_info->pwr_tx = SLOW_MODE;
41048c2ecf20Sopenharmony_ci	}
41058c2ecf20Sopenharmony_ci
41068c2ecf20Sopenharmony_ci	hba->max_pwr_info.is_valid = true;
41078c2ecf20Sopenharmony_ci	return 0;
41088c2ecf20Sopenharmony_ci}
41098c2ecf20Sopenharmony_ci
41108c2ecf20Sopenharmony_cistatic int ufshcd_change_power_mode(struct ufs_hba *hba,
41118c2ecf20Sopenharmony_ci			     struct ufs_pa_layer_attr *pwr_mode)
41128c2ecf20Sopenharmony_ci{
41138c2ecf20Sopenharmony_ci	int ret;
41148c2ecf20Sopenharmony_ci
41158c2ecf20Sopenharmony_ci	/* if already configured to the requested pwr_mode */
41168c2ecf20Sopenharmony_ci	if (!hba->force_pmc &&
41178c2ecf20Sopenharmony_ci	    pwr_mode->gear_rx == hba->pwr_info.gear_rx &&
41188c2ecf20Sopenharmony_ci	    pwr_mode->gear_tx == hba->pwr_info.gear_tx &&
41198c2ecf20Sopenharmony_ci	    pwr_mode->lane_rx == hba->pwr_info.lane_rx &&
41208c2ecf20Sopenharmony_ci	    pwr_mode->lane_tx == hba->pwr_info.lane_tx &&
41218c2ecf20Sopenharmony_ci	    pwr_mode->pwr_rx == hba->pwr_info.pwr_rx &&
41228c2ecf20Sopenharmony_ci	    pwr_mode->pwr_tx == hba->pwr_info.pwr_tx &&
41238c2ecf20Sopenharmony_ci	    pwr_mode->hs_rate == hba->pwr_info.hs_rate) {
41248c2ecf20Sopenharmony_ci		dev_dbg(hba->dev, "%s: power already configured\n", __func__);
41258c2ecf20Sopenharmony_ci		return 0;
41268c2ecf20Sopenharmony_ci	}
41278c2ecf20Sopenharmony_ci
41288c2ecf20Sopenharmony_ci	/*
41298c2ecf20Sopenharmony_ci	 * Configure attributes for power mode change with below.
41308c2ecf20Sopenharmony_ci	 * - PA_RXGEAR, PA_ACTIVERXDATALANES, PA_RXTERMINATION,
41318c2ecf20Sopenharmony_ci	 * - PA_TXGEAR, PA_ACTIVETXDATALANES, PA_TXTERMINATION,
41328c2ecf20Sopenharmony_ci	 * - PA_HSSERIES
41338c2ecf20Sopenharmony_ci	 */
41348c2ecf20Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_RXGEAR), pwr_mode->gear_rx);
41358c2ecf20Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_ACTIVERXDATALANES),
41368c2ecf20Sopenharmony_ci			pwr_mode->lane_rx);
41378c2ecf20Sopenharmony_ci	if (pwr_mode->pwr_rx == FASTAUTO_MODE ||
41388c2ecf20Sopenharmony_ci			pwr_mode->pwr_rx == FAST_MODE)
41398c2ecf20Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_RXTERMINATION), TRUE);
41408c2ecf20Sopenharmony_ci	else
41418c2ecf20Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_RXTERMINATION), FALSE);
41428c2ecf20Sopenharmony_ci
41438c2ecf20Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXGEAR), pwr_mode->gear_tx);
41448c2ecf20Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_ACTIVETXDATALANES),
41458c2ecf20Sopenharmony_ci			pwr_mode->lane_tx);
41468c2ecf20Sopenharmony_ci	if (pwr_mode->pwr_tx == FASTAUTO_MODE ||
41478c2ecf20Sopenharmony_ci			pwr_mode->pwr_tx == FAST_MODE)
41488c2ecf20Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXTERMINATION), TRUE);
41498c2ecf20Sopenharmony_ci	else
41508c2ecf20Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXTERMINATION), FALSE);
41518c2ecf20Sopenharmony_ci
41528c2ecf20Sopenharmony_ci	if (pwr_mode->pwr_rx == FASTAUTO_MODE ||
41538c2ecf20Sopenharmony_ci	    pwr_mode->pwr_tx == FASTAUTO_MODE ||
41548c2ecf20Sopenharmony_ci	    pwr_mode->pwr_rx == FAST_MODE ||
41558c2ecf20Sopenharmony_ci	    pwr_mode->pwr_tx == FAST_MODE)
41568c2ecf20Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_HSSERIES),
41578c2ecf20Sopenharmony_ci						pwr_mode->hs_rate);
41588c2ecf20Sopenharmony_ci
41598c2ecf20Sopenharmony_ci	if (!(hba->quirks & UFSHCD_QUIRK_SKIP_DEF_UNIPRO_TIMEOUT_SETTING)) {
41608c2ecf20Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA0),
41618c2ecf20Sopenharmony_ci				DL_FC0ProtectionTimeOutVal_Default);
41628c2ecf20Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA1),
41638c2ecf20Sopenharmony_ci				DL_TC0ReplayTimeOutVal_Default);
41648c2ecf20Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA2),
41658c2ecf20Sopenharmony_ci				DL_AFC0ReqTimeOutVal_Default);
41668c2ecf20Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA3),
41678c2ecf20Sopenharmony_ci				DL_FC1ProtectionTimeOutVal_Default);
41688c2ecf20Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA4),
41698c2ecf20Sopenharmony_ci				DL_TC1ReplayTimeOutVal_Default);
41708c2ecf20Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA5),
41718c2ecf20Sopenharmony_ci				DL_AFC1ReqTimeOutVal_Default);
41728c2ecf20Sopenharmony_ci
41738c2ecf20Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB(DME_LocalFC0ProtectionTimeOutVal),
41748c2ecf20Sopenharmony_ci				DL_FC0ProtectionTimeOutVal_Default);
41758c2ecf20Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB(DME_LocalTC0ReplayTimeOutVal),
41768c2ecf20Sopenharmony_ci				DL_TC0ReplayTimeOutVal_Default);
41778c2ecf20Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB(DME_LocalAFC0ReqTimeOutVal),
41788c2ecf20Sopenharmony_ci				DL_AFC0ReqTimeOutVal_Default);
41798c2ecf20Sopenharmony_ci	}
41808c2ecf20Sopenharmony_ci
41818c2ecf20Sopenharmony_ci	ret = ufshcd_uic_change_pwr_mode(hba, pwr_mode->pwr_rx << 4
41828c2ecf20Sopenharmony_ci			| pwr_mode->pwr_tx);
41838c2ecf20Sopenharmony_ci
41848c2ecf20Sopenharmony_ci	if (ret) {
41858c2ecf20Sopenharmony_ci		dev_err(hba->dev,
41868c2ecf20Sopenharmony_ci			"%s: power mode change failed %d\n", __func__, ret);
41878c2ecf20Sopenharmony_ci	} else {
41888c2ecf20Sopenharmony_ci		ufshcd_vops_pwr_change_notify(hba, POST_CHANGE, NULL,
41898c2ecf20Sopenharmony_ci								pwr_mode);
41908c2ecf20Sopenharmony_ci
41918c2ecf20Sopenharmony_ci		memcpy(&hba->pwr_info, pwr_mode,
41928c2ecf20Sopenharmony_ci			sizeof(struct ufs_pa_layer_attr));
41938c2ecf20Sopenharmony_ci	}
41948c2ecf20Sopenharmony_ci
41958c2ecf20Sopenharmony_ci	return ret;
41968c2ecf20Sopenharmony_ci}
41978c2ecf20Sopenharmony_ci
41988c2ecf20Sopenharmony_ci/**
41998c2ecf20Sopenharmony_ci * ufshcd_config_pwr_mode - configure a new power mode
42008c2ecf20Sopenharmony_ci * @hba: per-adapter instance
42018c2ecf20Sopenharmony_ci * @desired_pwr_mode: desired power configuration
42028c2ecf20Sopenharmony_ci */
42038c2ecf20Sopenharmony_ciint ufshcd_config_pwr_mode(struct ufs_hba *hba,
42048c2ecf20Sopenharmony_ci		struct ufs_pa_layer_attr *desired_pwr_mode)
42058c2ecf20Sopenharmony_ci{
42068c2ecf20Sopenharmony_ci	struct ufs_pa_layer_attr final_params = { 0 };
42078c2ecf20Sopenharmony_ci	int ret;
42088c2ecf20Sopenharmony_ci
42098c2ecf20Sopenharmony_ci	ret = ufshcd_vops_pwr_change_notify(hba, PRE_CHANGE,
42108c2ecf20Sopenharmony_ci					desired_pwr_mode, &final_params);
42118c2ecf20Sopenharmony_ci
42128c2ecf20Sopenharmony_ci	if (ret)
42138c2ecf20Sopenharmony_ci		memcpy(&final_params, desired_pwr_mode, sizeof(final_params));
42148c2ecf20Sopenharmony_ci
42158c2ecf20Sopenharmony_ci	ret = ufshcd_change_power_mode(hba, &final_params);
42168c2ecf20Sopenharmony_ci
42178c2ecf20Sopenharmony_ci	return ret;
42188c2ecf20Sopenharmony_ci}
42198c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ufshcd_config_pwr_mode);
42208c2ecf20Sopenharmony_ci
42218c2ecf20Sopenharmony_ci/**
42228c2ecf20Sopenharmony_ci * ufshcd_complete_dev_init() - checks device readiness
42238c2ecf20Sopenharmony_ci * @hba: per-adapter instance
42248c2ecf20Sopenharmony_ci *
42258c2ecf20Sopenharmony_ci * Set fDeviceInit flag and poll until device toggles it.
42268c2ecf20Sopenharmony_ci */
42278c2ecf20Sopenharmony_cistatic int ufshcd_complete_dev_init(struct ufs_hba *hba)
42288c2ecf20Sopenharmony_ci{
42298c2ecf20Sopenharmony_ci	int err;
42308c2ecf20Sopenharmony_ci	bool flag_res = true;
42318c2ecf20Sopenharmony_ci	ktime_t timeout;
42328c2ecf20Sopenharmony_ci
42338c2ecf20Sopenharmony_ci	err = ufshcd_query_flag_retry(hba, UPIU_QUERY_OPCODE_SET_FLAG,
42348c2ecf20Sopenharmony_ci		QUERY_FLAG_IDN_FDEVICEINIT, 0, NULL);
42358c2ecf20Sopenharmony_ci	if (err) {
42368c2ecf20Sopenharmony_ci		dev_err(hba->dev,
42378c2ecf20Sopenharmony_ci			"%s setting fDeviceInit flag failed with error %d\n",
42388c2ecf20Sopenharmony_ci			__func__, err);
42398c2ecf20Sopenharmony_ci		goto out;
42408c2ecf20Sopenharmony_ci	}
42418c2ecf20Sopenharmony_ci
42428c2ecf20Sopenharmony_ci	/* Poll fDeviceInit flag to be cleared */
42438c2ecf20Sopenharmony_ci	timeout = ktime_add_ms(ktime_get(), FDEVICEINIT_COMPL_TIMEOUT);
42448c2ecf20Sopenharmony_ci	do {
42458c2ecf20Sopenharmony_ci		err = ufshcd_query_flag(hba, UPIU_QUERY_OPCODE_READ_FLAG,
42468c2ecf20Sopenharmony_ci					QUERY_FLAG_IDN_FDEVICEINIT, 0, &flag_res);
42478c2ecf20Sopenharmony_ci		if (!flag_res)
42488c2ecf20Sopenharmony_ci			break;
42498c2ecf20Sopenharmony_ci		usleep_range(5000, 10000);
42508c2ecf20Sopenharmony_ci	} while (ktime_before(ktime_get(), timeout));
42518c2ecf20Sopenharmony_ci
42528c2ecf20Sopenharmony_ci	if (err) {
42538c2ecf20Sopenharmony_ci		dev_err(hba->dev,
42548c2ecf20Sopenharmony_ci				"%s reading fDeviceInit flag failed with error %d\n",
42558c2ecf20Sopenharmony_ci				__func__, err);
42568c2ecf20Sopenharmony_ci	} else if (flag_res) {
42578c2ecf20Sopenharmony_ci		dev_err(hba->dev,
42588c2ecf20Sopenharmony_ci				"%s fDeviceInit was not cleared by the device\n",
42598c2ecf20Sopenharmony_ci				__func__);
42608c2ecf20Sopenharmony_ci		err = -EBUSY;
42618c2ecf20Sopenharmony_ci	}
42628c2ecf20Sopenharmony_ciout:
42638c2ecf20Sopenharmony_ci	return err;
42648c2ecf20Sopenharmony_ci}
42658c2ecf20Sopenharmony_ci
42668c2ecf20Sopenharmony_ci/**
42678c2ecf20Sopenharmony_ci * ufshcd_make_hba_operational - Make UFS controller operational
42688c2ecf20Sopenharmony_ci * @hba: per adapter instance
42698c2ecf20Sopenharmony_ci *
42708c2ecf20Sopenharmony_ci * To bring UFS host controller to operational state,
42718c2ecf20Sopenharmony_ci * 1. Enable required interrupts
42728c2ecf20Sopenharmony_ci * 2. Configure interrupt aggregation
42738c2ecf20Sopenharmony_ci * 3. Program UTRL and UTMRL base address
42748c2ecf20Sopenharmony_ci * 4. Configure run-stop-registers
42758c2ecf20Sopenharmony_ci *
42768c2ecf20Sopenharmony_ci * Returns 0 on success, non-zero value on failure
42778c2ecf20Sopenharmony_ci */
42788c2ecf20Sopenharmony_ciint ufshcd_make_hba_operational(struct ufs_hba *hba)
42798c2ecf20Sopenharmony_ci{
42808c2ecf20Sopenharmony_ci	int err = 0;
42818c2ecf20Sopenharmony_ci	u32 reg;
42828c2ecf20Sopenharmony_ci
42838c2ecf20Sopenharmony_ci	/* Enable required interrupts */
42848c2ecf20Sopenharmony_ci	ufshcd_enable_intr(hba, UFSHCD_ENABLE_INTRS);
42858c2ecf20Sopenharmony_ci
42868c2ecf20Sopenharmony_ci	/* Configure interrupt aggregation */
42878c2ecf20Sopenharmony_ci	if (ufshcd_is_intr_aggr_allowed(hba))
42888c2ecf20Sopenharmony_ci		ufshcd_config_intr_aggr(hba, hba->nutrs - 1, INT_AGGR_DEF_TO);
42898c2ecf20Sopenharmony_ci	else
42908c2ecf20Sopenharmony_ci		ufshcd_disable_intr_aggr(hba);
42918c2ecf20Sopenharmony_ci
42928c2ecf20Sopenharmony_ci	/* Configure UTRL and UTMRL base address registers */
42938c2ecf20Sopenharmony_ci	ufshcd_writel(hba, lower_32_bits(hba->utrdl_dma_addr),
42948c2ecf20Sopenharmony_ci			REG_UTP_TRANSFER_REQ_LIST_BASE_L);
42958c2ecf20Sopenharmony_ci	ufshcd_writel(hba, upper_32_bits(hba->utrdl_dma_addr),
42968c2ecf20Sopenharmony_ci			REG_UTP_TRANSFER_REQ_LIST_BASE_H);
42978c2ecf20Sopenharmony_ci	ufshcd_writel(hba, lower_32_bits(hba->utmrdl_dma_addr),
42988c2ecf20Sopenharmony_ci			REG_UTP_TASK_REQ_LIST_BASE_L);
42998c2ecf20Sopenharmony_ci	ufshcd_writel(hba, upper_32_bits(hba->utmrdl_dma_addr),
43008c2ecf20Sopenharmony_ci			REG_UTP_TASK_REQ_LIST_BASE_H);
43018c2ecf20Sopenharmony_ci
43028c2ecf20Sopenharmony_ci	/*
43038c2ecf20Sopenharmony_ci	 * Make sure base address and interrupt setup are updated before
43048c2ecf20Sopenharmony_ci	 * enabling the run/stop registers below.
43058c2ecf20Sopenharmony_ci	 */
43068c2ecf20Sopenharmony_ci	wmb();
43078c2ecf20Sopenharmony_ci
43088c2ecf20Sopenharmony_ci	/*
43098c2ecf20Sopenharmony_ci	 * UCRDY, UTMRLDY and UTRLRDY bits must be 1
43108c2ecf20Sopenharmony_ci	 */
43118c2ecf20Sopenharmony_ci	reg = ufshcd_readl(hba, REG_CONTROLLER_STATUS);
43128c2ecf20Sopenharmony_ci	if (!(ufshcd_get_lists_status(reg))) {
43138c2ecf20Sopenharmony_ci		ufshcd_enable_run_stop_reg(hba);
43148c2ecf20Sopenharmony_ci	} else {
43158c2ecf20Sopenharmony_ci		dev_err(hba->dev,
43168c2ecf20Sopenharmony_ci			"Host controller not ready to process requests");
43178c2ecf20Sopenharmony_ci		err = -EIO;
43188c2ecf20Sopenharmony_ci	}
43198c2ecf20Sopenharmony_ci
43208c2ecf20Sopenharmony_ci	return err;
43218c2ecf20Sopenharmony_ci}
43228c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ufshcd_make_hba_operational);
43238c2ecf20Sopenharmony_ci
43248c2ecf20Sopenharmony_ci/**
43258c2ecf20Sopenharmony_ci * ufshcd_hba_stop - Send controller to reset state
43268c2ecf20Sopenharmony_ci * @hba: per adapter instance
43278c2ecf20Sopenharmony_ci */
43288c2ecf20Sopenharmony_cistatic inline void ufshcd_hba_stop(struct ufs_hba *hba)
43298c2ecf20Sopenharmony_ci{
43308c2ecf20Sopenharmony_ci	unsigned long flags;
43318c2ecf20Sopenharmony_ci	int err;
43328c2ecf20Sopenharmony_ci
43338c2ecf20Sopenharmony_ci	/*
43348c2ecf20Sopenharmony_ci	 * Obtain the host lock to prevent that the controller is disabled
43358c2ecf20Sopenharmony_ci	 * while the UFS interrupt handler is active on another CPU.
43368c2ecf20Sopenharmony_ci	 */
43378c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
43388c2ecf20Sopenharmony_ci	ufshcd_writel(hba, CONTROLLER_DISABLE,  REG_CONTROLLER_ENABLE);
43398c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
43408c2ecf20Sopenharmony_ci
43418c2ecf20Sopenharmony_ci	err = ufshcd_wait_for_register(hba, REG_CONTROLLER_ENABLE,
43428c2ecf20Sopenharmony_ci					CONTROLLER_ENABLE, CONTROLLER_DISABLE,
43438c2ecf20Sopenharmony_ci					10, 1);
43448c2ecf20Sopenharmony_ci	if (err)
43458c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: Controller disable failed\n", __func__);
43468c2ecf20Sopenharmony_ci}
43478c2ecf20Sopenharmony_ci
43488c2ecf20Sopenharmony_ci/**
43498c2ecf20Sopenharmony_ci * ufshcd_hba_execute_hce - initialize the controller
43508c2ecf20Sopenharmony_ci * @hba: per adapter instance
43518c2ecf20Sopenharmony_ci *
43528c2ecf20Sopenharmony_ci * The controller resets itself and controller firmware initialization
43538c2ecf20Sopenharmony_ci * sequence kicks off. When controller is ready it will set
43548c2ecf20Sopenharmony_ci * the Host Controller Enable bit to 1.
43558c2ecf20Sopenharmony_ci *
43568c2ecf20Sopenharmony_ci * Returns 0 on success, non-zero value on failure
43578c2ecf20Sopenharmony_ci */
43588c2ecf20Sopenharmony_cistatic int ufshcd_hba_execute_hce(struct ufs_hba *hba)
43598c2ecf20Sopenharmony_ci{
43608c2ecf20Sopenharmony_ci	int retry;
43618c2ecf20Sopenharmony_ci
43628c2ecf20Sopenharmony_ci	if (!ufshcd_is_hba_active(hba))
43638c2ecf20Sopenharmony_ci		/* change controller state to "reset state" */
43648c2ecf20Sopenharmony_ci		ufshcd_hba_stop(hba);
43658c2ecf20Sopenharmony_ci
43668c2ecf20Sopenharmony_ci	/* UniPro link is disabled at this point */
43678c2ecf20Sopenharmony_ci	ufshcd_set_link_off(hba);
43688c2ecf20Sopenharmony_ci
43698c2ecf20Sopenharmony_ci	ufshcd_vops_hce_enable_notify(hba, PRE_CHANGE);
43708c2ecf20Sopenharmony_ci
43718c2ecf20Sopenharmony_ci	/* start controller initialization sequence */
43728c2ecf20Sopenharmony_ci	ufshcd_hba_start(hba);
43738c2ecf20Sopenharmony_ci
43748c2ecf20Sopenharmony_ci	/*
43758c2ecf20Sopenharmony_ci	 * To initialize a UFS host controller HCE bit must be set to 1.
43768c2ecf20Sopenharmony_ci	 * During initialization the HCE bit value changes from 1->0->1.
43778c2ecf20Sopenharmony_ci	 * When the host controller completes initialization sequence
43788c2ecf20Sopenharmony_ci	 * it sets the value of HCE bit to 1. The same HCE bit is read back
43798c2ecf20Sopenharmony_ci	 * to check if the controller has completed initialization sequence.
43808c2ecf20Sopenharmony_ci	 * So without this delay the value HCE = 1, set in the previous
43818c2ecf20Sopenharmony_ci	 * instruction might be read back.
43828c2ecf20Sopenharmony_ci	 * This delay can be changed based on the controller.
43838c2ecf20Sopenharmony_ci	 */
43848c2ecf20Sopenharmony_ci	ufshcd_delay_us(hba->vps->hba_enable_delay_us, 100);
43858c2ecf20Sopenharmony_ci
43868c2ecf20Sopenharmony_ci	/* wait for the host controller to complete initialization */
43878c2ecf20Sopenharmony_ci	retry = 50;
43888c2ecf20Sopenharmony_ci	while (ufshcd_is_hba_active(hba)) {
43898c2ecf20Sopenharmony_ci		if (retry) {
43908c2ecf20Sopenharmony_ci			retry--;
43918c2ecf20Sopenharmony_ci		} else {
43928c2ecf20Sopenharmony_ci			dev_err(hba->dev,
43938c2ecf20Sopenharmony_ci				"Controller enable failed\n");
43948c2ecf20Sopenharmony_ci			return -EIO;
43958c2ecf20Sopenharmony_ci		}
43968c2ecf20Sopenharmony_ci		usleep_range(1000, 1100);
43978c2ecf20Sopenharmony_ci	}
43988c2ecf20Sopenharmony_ci
43998c2ecf20Sopenharmony_ci	/* enable UIC related interrupts */
44008c2ecf20Sopenharmony_ci	ufshcd_enable_intr(hba, UFSHCD_UIC_MASK);
44018c2ecf20Sopenharmony_ci
44028c2ecf20Sopenharmony_ci	ufshcd_vops_hce_enable_notify(hba, POST_CHANGE);
44038c2ecf20Sopenharmony_ci
44048c2ecf20Sopenharmony_ci	return 0;
44058c2ecf20Sopenharmony_ci}
44068c2ecf20Sopenharmony_ci
44078c2ecf20Sopenharmony_ciint ufshcd_hba_enable(struct ufs_hba *hba)
44088c2ecf20Sopenharmony_ci{
44098c2ecf20Sopenharmony_ci	int ret;
44108c2ecf20Sopenharmony_ci
44118c2ecf20Sopenharmony_ci	if (hba->quirks & UFSHCI_QUIRK_BROKEN_HCE) {
44128c2ecf20Sopenharmony_ci		ufshcd_set_link_off(hba);
44138c2ecf20Sopenharmony_ci		ufshcd_vops_hce_enable_notify(hba, PRE_CHANGE);
44148c2ecf20Sopenharmony_ci
44158c2ecf20Sopenharmony_ci		/* enable UIC related interrupts */
44168c2ecf20Sopenharmony_ci		ufshcd_enable_intr(hba, UFSHCD_UIC_MASK);
44178c2ecf20Sopenharmony_ci		ret = ufshcd_dme_reset(hba);
44188c2ecf20Sopenharmony_ci		if (!ret) {
44198c2ecf20Sopenharmony_ci			ret = ufshcd_dme_enable(hba);
44208c2ecf20Sopenharmony_ci			if (!ret)
44218c2ecf20Sopenharmony_ci				ufshcd_vops_hce_enable_notify(hba, POST_CHANGE);
44228c2ecf20Sopenharmony_ci			if (ret)
44238c2ecf20Sopenharmony_ci				dev_err(hba->dev,
44248c2ecf20Sopenharmony_ci					"Host controller enable failed with non-hce\n");
44258c2ecf20Sopenharmony_ci		}
44268c2ecf20Sopenharmony_ci	} else {
44278c2ecf20Sopenharmony_ci		ret = ufshcd_hba_execute_hce(hba);
44288c2ecf20Sopenharmony_ci	}
44298c2ecf20Sopenharmony_ci
44308c2ecf20Sopenharmony_ci	return ret;
44318c2ecf20Sopenharmony_ci}
44328c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ufshcd_hba_enable);
44338c2ecf20Sopenharmony_ci
44348c2ecf20Sopenharmony_cistatic int ufshcd_disable_tx_lcc(struct ufs_hba *hba, bool peer)
44358c2ecf20Sopenharmony_ci{
44368c2ecf20Sopenharmony_ci	int tx_lanes = 0, i, err = 0;
44378c2ecf20Sopenharmony_ci
44388c2ecf20Sopenharmony_ci	if (!peer)
44398c2ecf20Sopenharmony_ci		ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDTXDATALANES),
44408c2ecf20Sopenharmony_ci			       &tx_lanes);
44418c2ecf20Sopenharmony_ci	else
44428c2ecf20Sopenharmony_ci		ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_CONNECTEDTXDATALANES),
44438c2ecf20Sopenharmony_ci				    &tx_lanes);
44448c2ecf20Sopenharmony_ci	for (i = 0; i < tx_lanes; i++) {
44458c2ecf20Sopenharmony_ci		if (!peer)
44468c2ecf20Sopenharmony_ci			err = ufshcd_dme_set(hba,
44478c2ecf20Sopenharmony_ci				UIC_ARG_MIB_SEL(TX_LCC_ENABLE,
44488c2ecf20Sopenharmony_ci					UIC_ARG_MPHY_TX_GEN_SEL_INDEX(i)),
44498c2ecf20Sopenharmony_ci					0);
44508c2ecf20Sopenharmony_ci		else
44518c2ecf20Sopenharmony_ci			err = ufshcd_dme_peer_set(hba,
44528c2ecf20Sopenharmony_ci				UIC_ARG_MIB_SEL(TX_LCC_ENABLE,
44538c2ecf20Sopenharmony_ci					UIC_ARG_MPHY_TX_GEN_SEL_INDEX(i)),
44548c2ecf20Sopenharmony_ci					0);
44558c2ecf20Sopenharmony_ci		if (err) {
44568c2ecf20Sopenharmony_ci			dev_err(hba->dev, "%s: TX LCC Disable failed, peer = %d, lane = %d, err = %d",
44578c2ecf20Sopenharmony_ci				__func__, peer, i, err);
44588c2ecf20Sopenharmony_ci			break;
44598c2ecf20Sopenharmony_ci		}
44608c2ecf20Sopenharmony_ci	}
44618c2ecf20Sopenharmony_ci
44628c2ecf20Sopenharmony_ci	return err;
44638c2ecf20Sopenharmony_ci}
44648c2ecf20Sopenharmony_ci
44658c2ecf20Sopenharmony_cistatic inline int ufshcd_disable_device_tx_lcc(struct ufs_hba *hba)
44668c2ecf20Sopenharmony_ci{
44678c2ecf20Sopenharmony_ci	return ufshcd_disable_tx_lcc(hba, true);
44688c2ecf20Sopenharmony_ci}
44698c2ecf20Sopenharmony_ci
44708c2ecf20Sopenharmony_civoid ufshcd_update_reg_hist(struct ufs_err_reg_hist *reg_hist,
44718c2ecf20Sopenharmony_ci			    u32 reg)
44728c2ecf20Sopenharmony_ci{
44738c2ecf20Sopenharmony_ci	reg_hist->reg[reg_hist->pos] = reg;
44748c2ecf20Sopenharmony_ci	reg_hist->tstamp[reg_hist->pos] = ktime_get();
44758c2ecf20Sopenharmony_ci	reg_hist->pos = (reg_hist->pos + 1) % UFS_ERR_REG_HIST_LENGTH;
44768c2ecf20Sopenharmony_ci}
44778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ufshcd_update_reg_hist);
44788c2ecf20Sopenharmony_ci
44798c2ecf20Sopenharmony_ci/**
44808c2ecf20Sopenharmony_ci * ufshcd_link_startup - Initialize unipro link startup
44818c2ecf20Sopenharmony_ci * @hba: per adapter instance
44828c2ecf20Sopenharmony_ci *
44838c2ecf20Sopenharmony_ci * Returns 0 for success, non-zero in case of failure
44848c2ecf20Sopenharmony_ci */
44858c2ecf20Sopenharmony_cistatic int ufshcd_link_startup(struct ufs_hba *hba)
44868c2ecf20Sopenharmony_ci{
44878c2ecf20Sopenharmony_ci	int ret;
44888c2ecf20Sopenharmony_ci	int retries = DME_LINKSTARTUP_RETRIES;
44898c2ecf20Sopenharmony_ci	bool link_startup_again = false;
44908c2ecf20Sopenharmony_ci
44918c2ecf20Sopenharmony_ci	/*
44928c2ecf20Sopenharmony_ci	 * If UFS device isn't active then we will have to issue link startup
44938c2ecf20Sopenharmony_ci	 * 2 times to make sure the device state move to active.
44948c2ecf20Sopenharmony_ci	 */
44958c2ecf20Sopenharmony_ci	if (!ufshcd_is_ufs_dev_active(hba))
44968c2ecf20Sopenharmony_ci		link_startup_again = true;
44978c2ecf20Sopenharmony_ci
44988c2ecf20Sopenharmony_cilink_startup:
44998c2ecf20Sopenharmony_ci	do {
45008c2ecf20Sopenharmony_ci		ufshcd_vops_link_startup_notify(hba, PRE_CHANGE);
45018c2ecf20Sopenharmony_ci
45028c2ecf20Sopenharmony_ci		ret = ufshcd_dme_link_startup(hba);
45038c2ecf20Sopenharmony_ci
45048c2ecf20Sopenharmony_ci		/* check if device is detected by inter-connect layer */
45058c2ecf20Sopenharmony_ci		if (!ret && !ufshcd_is_device_present(hba)) {
45068c2ecf20Sopenharmony_ci			ufshcd_update_reg_hist(&hba->ufs_stats.link_startup_err,
45078c2ecf20Sopenharmony_ci					       0);
45088c2ecf20Sopenharmony_ci			dev_err(hba->dev, "%s: Device not present\n", __func__);
45098c2ecf20Sopenharmony_ci			ret = -ENXIO;
45108c2ecf20Sopenharmony_ci			goto out;
45118c2ecf20Sopenharmony_ci		}
45128c2ecf20Sopenharmony_ci
45138c2ecf20Sopenharmony_ci		/*
45148c2ecf20Sopenharmony_ci		 * DME link lost indication is only received when link is up,
45158c2ecf20Sopenharmony_ci		 * but we can't be sure if the link is up until link startup
45168c2ecf20Sopenharmony_ci		 * succeeds. So reset the local Uni-Pro and try again.
45178c2ecf20Sopenharmony_ci		 */
45188c2ecf20Sopenharmony_ci		if (ret && ufshcd_hba_enable(hba)) {
45198c2ecf20Sopenharmony_ci			ufshcd_update_reg_hist(&hba->ufs_stats.link_startup_err,
45208c2ecf20Sopenharmony_ci					       (u32)ret);
45218c2ecf20Sopenharmony_ci			goto out;
45228c2ecf20Sopenharmony_ci		}
45238c2ecf20Sopenharmony_ci	} while (ret && retries--);
45248c2ecf20Sopenharmony_ci
45258c2ecf20Sopenharmony_ci	if (ret) {
45268c2ecf20Sopenharmony_ci		/* failed to get the link up... retire */
45278c2ecf20Sopenharmony_ci		ufshcd_update_reg_hist(&hba->ufs_stats.link_startup_err,
45288c2ecf20Sopenharmony_ci				       (u32)ret);
45298c2ecf20Sopenharmony_ci		goto out;
45308c2ecf20Sopenharmony_ci	}
45318c2ecf20Sopenharmony_ci
45328c2ecf20Sopenharmony_ci	if (link_startup_again) {
45338c2ecf20Sopenharmony_ci		link_startup_again = false;
45348c2ecf20Sopenharmony_ci		retries = DME_LINKSTARTUP_RETRIES;
45358c2ecf20Sopenharmony_ci		goto link_startup;
45368c2ecf20Sopenharmony_ci	}
45378c2ecf20Sopenharmony_ci
45388c2ecf20Sopenharmony_ci	/* Mark that link is up in PWM-G1, 1-lane, SLOW-AUTO mode */
45398c2ecf20Sopenharmony_ci	ufshcd_init_pwr_info(hba);
45408c2ecf20Sopenharmony_ci	ufshcd_print_pwr_info(hba);
45418c2ecf20Sopenharmony_ci
45428c2ecf20Sopenharmony_ci	if (hba->quirks & UFSHCD_QUIRK_BROKEN_LCC) {
45438c2ecf20Sopenharmony_ci		ret = ufshcd_disable_device_tx_lcc(hba);
45448c2ecf20Sopenharmony_ci		if (ret)
45458c2ecf20Sopenharmony_ci			goto out;
45468c2ecf20Sopenharmony_ci	}
45478c2ecf20Sopenharmony_ci
45488c2ecf20Sopenharmony_ci	/* Include any host controller configuration via UIC commands */
45498c2ecf20Sopenharmony_ci	ret = ufshcd_vops_link_startup_notify(hba, POST_CHANGE);
45508c2ecf20Sopenharmony_ci	if (ret)
45518c2ecf20Sopenharmony_ci		goto out;
45528c2ecf20Sopenharmony_ci
45538c2ecf20Sopenharmony_ci	/* Clear UECPA once due to LINERESET has happened during LINK_STARTUP */
45548c2ecf20Sopenharmony_ci	ufshcd_readl(hba, REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER);
45558c2ecf20Sopenharmony_ci	ret = ufshcd_make_hba_operational(hba);
45568c2ecf20Sopenharmony_ciout:
45578c2ecf20Sopenharmony_ci	if (ret) {
45588c2ecf20Sopenharmony_ci		dev_err(hba->dev, "link startup failed %d\n", ret);
45598c2ecf20Sopenharmony_ci		ufshcd_print_host_state(hba);
45608c2ecf20Sopenharmony_ci		ufshcd_print_pwr_info(hba);
45618c2ecf20Sopenharmony_ci		ufshcd_print_host_regs(hba);
45628c2ecf20Sopenharmony_ci	}
45638c2ecf20Sopenharmony_ci	return ret;
45648c2ecf20Sopenharmony_ci}
45658c2ecf20Sopenharmony_ci
45668c2ecf20Sopenharmony_ci/**
45678c2ecf20Sopenharmony_ci * ufshcd_verify_dev_init() - Verify device initialization
45688c2ecf20Sopenharmony_ci * @hba: per-adapter instance
45698c2ecf20Sopenharmony_ci *
45708c2ecf20Sopenharmony_ci * Send NOP OUT UPIU and wait for NOP IN response to check whether the
45718c2ecf20Sopenharmony_ci * device Transport Protocol (UTP) layer is ready after a reset.
45728c2ecf20Sopenharmony_ci * If the UTP layer at the device side is not initialized, it may
45738c2ecf20Sopenharmony_ci * not respond with NOP IN UPIU within timeout of %NOP_OUT_TIMEOUT
45748c2ecf20Sopenharmony_ci * and we retry sending NOP OUT for %NOP_OUT_RETRIES iterations.
45758c2ecf20Sopenharmony_ci */
45768c2ecf20Sopenharmony_cistatic int ufshcd_verify_dev_init(struct ufs_hba *hba)
45778c2ecf20Sopenharmony_ci{
45788c2ecf20Sopenharmony_ci	int err = 0;
45798c2ecf20Sopenharmony_ci	int retries;
45808c2ecf20Sopenharmony_ci
45818c2ecf20Sopenharmony_ci	ufshcd_hold(hba, false);
45828c2ecf20Sopenharmony_ci	mutex_lock(&hba->dev_cmd.lock);
45838c2ecf20Sopenharmony_ci	for (retries = NOP_OUT_RETRIES; retries > 0; retries--) {
45848c2ecf20Sopenharmony_ci		err = ufshcd_exec_dev_cmd(hba, DEV_CMD_TYPE_NOP,
45858c2ecf20Sopenharmony_ci					       NOP_OUT_TIMEOUT);
45868c2ecf20Sopenharmony_ci
45878c2ecf20Sopenharmony_ci		if (!err || err == -ETIMEDOUT)
45888c2ecf20Sopenharmony_ci			break;
45898c2ecf20Sopenharmony_ci
45908c2ecf20Sopenharmony_ci		dev_dbg(hba->dev, "%s: error %d retrying\n", __func__, err);
45918c2ecf20Sopenharmony_ci	}
45928c2ecf20Sopenharmony_ci	mutex_unlock(&hba->dev_cmd.lock);
45938c2ecf20Sopenharmony_ci	ufshcd_release(hba);
45948c2ecf20Sopenharmony_ci
45958c2ecf20Sopenharmony_ci	if (err)
45968c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: NOP OUT failed %d\n", __func__, err);
45978c2ecf20Sopenharmony_ci	return err;
45988c2ecf20Sopenharmony_ci}
45998c2ecf20Sopenharmony_ci
46008c2ecf20Sopenharmony_ci/**
46018c2ecf20Sopenharmony_ci * ufshcd_set_queue_depth - set lun queue depth
46028c2ecf20Sopenharmony_ci * @sdev: pointer to SCSI device
46038c2ecf20Sopenharmony_ci *
46048c2ecf20Sopenharmony_ci * Read bLUQueueDepth value and activate scsi tagged command
46058c2ecf20Sopenharmony_ci * queueing. For WLUN, queue depth is set to 1. For best-effort
46068c2ecf20Sopenharmony_ci * cases (bLUQueueDepth = 0) the queue depth is set to a maximum
46078c2ecf20Sopenharmony_ci * value that host can queue.
46088c2ecf20Sopenharmony_ci */
46098c2ecf20Sopenharmony_cistatic void ufshcd_set_queue_depth(struct scsi_device *sdev)
46108c2ecf20Sopenharmony_ci{
46118c2ecf20Sopenharmony_ci	int ret = 0;
46128c2ecf20Sopenharmony_ci	u8 lun_qdepth;
46138c2ecf20Sopenharmony_ci	struct ufs_hba *hba;
46148c2ecf20Sopenharmony_ci
46158c2ecf20Sopenharmony_ci	hba = shost_priv(sdev->host);
46168c2ecf20Sopenharmony_ci
46178c2ecf20Sopenharmony_ci	lun_qdepth = hba->nutrs;
46188c2ecf20Sopenharmony_ci	ret = ufshcd_read_unit_desc_param(hba,
46198c2ecf20Sopenharmony_ci					  ufshcd_scsi_to_upiu_lun(sdev->lun),
46208c2ecf20Sopenharmony_ci					  UNIT_DESC_PARAM_LU_Q_DEPTH,
46218c2ecf20Sopenharmony_ci					  &lun_qdepth,
46228c2ecf20Sopenharmony_ci					  sizeof(lun_qdepth));
46238c2ecf20Sopenharmony_ci
46248c2ecf20Sopenharmony_ci	/* Some WLUN doesn't support unit descriptor */
46258c2ecf20Sopenharmony_ci	if (ret == -EOPNOTSUPP)
46268c2ecf20Sopenharmony_ci		lun_qdepth = 1;
46278c2ecf20Sopenharmony_ci	else if (!lun_qdepth)
46288c2ecf20Sopenharmony_ci		/* eventually, we can figure out the real queue depth */
46298c2ecf20Sopenharmony_ci		lun_qdepth = hba->nutrs;
46308c2ecf20Sopenharmony_ci	else
46318c2ecf20Sopenharmony_ci		lun_qdepth = min_t(int, lun_qdepth, hba->nutrs);
46328c2ecf20Sopenharmony_ci
46338c2ecf20Sopenharmony_ci	dev_dbg(hba->dev, "%s: activate tcq with queue depth %d\n",
46348c2ecf20Sopenharmony_ci			__func__, lun_qdepth);
46358c2ecf20Sopenharmony_ci	scsi_change_queue_depth(sdev, lun_qdepth);
46368c2ecf20Sopenharmony_ci}
46378c2ecf20Sopenharmony_ci
46388c2ecf20Sopenharmony_ci/*
46398c2ecf20Sopenharmony_ci * ufshcd_get_lu_wp - returns the "b_lu_write_protect" from UNIT DESCRIPTOR
46408c2ecf20Sopenharmony_ci * @hba: per-adapter instance
46418c2ecf20Sopenharmony_ci * @lun: UFS device lun id
46428c2ecf20Sopenharmony_ci * @b_lu_write_protect: pointer to buffer to hold the LU's write protect info
46438c2ecf20Sopenharmony_ci *
46448c2ecf20Sopenharmony_ci * Returns 0 in case of success and b_lu_write_protect status would be returned
46458c2ecf20Sopenharmony_ci * @b_lu_write_protect parameter.
46468c2ecf20Sopenharmony_ci * Returns -ENOTSUPP if reading b_lu_write_protect is not supported.
46478c2ecf20Sopenharmony_ci * Returns -EINVAL in case of invalid parameters passed to this function.
46488c2ecf20Sopenharmony_ci */
46498c2ecf20Sopenharmony_cistatic int ufshcd_get_lu_wp(struct ufs_hba *hba,
46508c2ecf20Sopenharmony_ci			    u8 lun,
46518c2ecf20Sopenharmony_ci			    u8 *b_lu_write_protect)
46528c2ecf20Sopenharmony_ci{
46538c2ecf20Sopenharmony_ci	int ret;
46548c2ecf20Sopenharmony_ci
46558c2ecf20Sopenharmony_ci	if (!b_lu_write_protect)
46568c2ecf20Sopenharmony_ci		ret = -EINVAL;
46578c2ecf20Sopenharmony_ci	/*
46588c2ecf20Sopenharmony_ci	 * According to UFS device spec, RPMB LU can't be write
46598c2ecf20Sopenharmony_ci	 * protected so skip reading bLUWriteProtect parameter for
46608c2ecf20Sopenharmony_ci	 * it. For other W-LUs, UNIT DESCRIPTOR is not available.
46618c2ecf20Sopenharmony_ci	 */
46628c2ecf20Sopenharmony_ci	else if (lun >= hba->dev_info.max_lu_supported)
46638c2ecf20Sopenharmony_ci		ret = -ENOTSUPP;
46648c2ecf20Sopenharmony_ci	else
46658c2ecf20Sopenharmony_ci		ret = ufshcd_read_unit_desc_param(hba,
46668c2ecf20Sopenharmony_ci					  lun,
46678c2ecf20Sopenharmony_ci					  UNIT_DESC_PARAM_LU_WR_PROTECT,
46688c2ecf20Sopenharmony_ci					  b_lu_write_protect,
46698c2ecf20Sopenharmony_ci					  sizeof(*b_lu_write_protect));
46708c2ecf20Sopenharmony_ci	return ret;
46718c2ecf20Sopenharmony_ci}
46728c2ecf20Sopenharmony_ci
46738c2ecf20Sopenharmony_ci/**
46748c2ecf20Sopenharmony_ci * ufshcd_get_lu_power_on_wp_status - get LU's power on write protect
46758c2ecf20Sopenharmony_ci * status
46768c2ecf20Sopenharmony_ci * @hba: per-adapter instance
46778c2ecf20Sopenharmony_ci * @sdev: pointer to SCSI device
46788c2ecf20Sopenharmony_ci *
46798c2ecf20Sopenharmony_ci */
46808c2ecf20Sopenharmony_cistatic inline void ufshcd_get_lu_power_on_wp_status(struct ufs_hba *hba,
46818c2ecf20Sopenharmony_ci						    struct scsi_device *sdev)
46828c2ecf20Sopenharmony_ci{
46838c2ecf20Sopenharmony_ci	if (hba->dev_info.f_power_on_wp_en &&
46848c2ecf20Sopenharmony_ci	    !hba->dev_info.is_lu_power_on_wp) {
46858c2ecf20Sopenharmony_ci		u8 b_lu_write_protect;
46868c2ecf20Sopenharmony_ci
46878c2ecf20Sopenharmony_ci		if (!ufshcd_get_lu_wp(hba, ufshcd_scsi_to_upiu_lun(sdev->lun),
46888c2ecf20Sopenharmony_ci				      &b_lu_write_protect) &&
46898c2ecf20Sopenharmony_ci		    (b_lu_write_protect == UFS_LU_POWER_ON_WP))
46908c2ecf20Sopenharmony_ci			hba->dev_info.is_lu_power_on_wp = true;
46918c2ecf20Sopenharmony_ci	}
46928c2ecf20Sopenharmony_ci}
46938c2ecf20Sopenharmony_ci
46948c2ecf20Sopenharmony_ci/**
46958c2ecf20Sopenharmony_ci * ufshcd_slave_alloc - handle initial SCSI device configurations
46968c2ecf20Sopenharmony_ci * @sdev: pointer to SCSI device
46978c2ecf20Sopenharmony_ci *
46988c2ecf20Sopenharmony_ci * Returns success
46998c2ecf20Sopenharmony_ci */
47008c2ecf20Sopenharmony_cistatic int ufshcd_slave_alloc(struct scsi_device *sdev)
47018c2ecf20Sopenharmony_ci{
47028c2ecf20Sopenharmony_ci	struct ufs_hba *hba;
47038c2ecf20Sopenharmony_ci
47048c2ecf20Sopenharmony_ci	hba = shost_priv(sdev->host);
47058c2ecf20Sopenharmony_ci
47068c2ecf20Sopenharmony_ci	/* Mode sense(6) is not supported by UFS, so use Mode sense(10) */
47078c2ecf20Sopenharmony_ci	sdev->use_10_for_ms = 1;
47088c2ecf20Sopenharmony_ci
47098c2ecf20Sopenharmony_ci	/* DBD field should be set to 1 in mode sense(10) */
47108c2ecf20Sopenharmony_ci	sdev->set_dbd_for_ms = 1;
47118c2ecf20Sopenharmony_ci
47128c2ecf20Sopenharmony_ci	/* allow SCSI layer to restart the device in case of errors */
47138c2ecf20Sopenharmony_ci	sdev->allow_restart = 1;
47148c2ecf20Sopenharmony_ci
47158c2ecf20Sopenharmony_ci	/* REPORT SUPPORTED OPERATION CODES is not supported */
47168c2ecf20Sopenharmony_ci	sdev->no_report_opcodes = 1;
47178c2ecf20Sopenharmony_ci
47188c2ecf20Sopenharmony_ci	/* WRITE_SAME command is not supported */
47198c2ecf20Sopenharmony_ci	sdev->no_write_same = 1;
47208c2ecf20Sopenharmony_ci
47218c2ecf20Sopenharmony_ci	ufshcd_set_queue_depth(sdev);
47228c2ecf20Sopenharmony_ci
47238c2ecf20Sopenharmony_ci	ufshcd_get_lu_power_on_wp_status(hba, sdev);
47248c2ecf20Sopenharmony_ci
47258c2ecf20Sopenharmony_ci	return 0;
47268c2ecf20Sopenharmony_ci}
47278c2ecf20Sopenharmony_ci
47288c2ecf20Sopenharmony_ci/**
47298c2ecf20Sopenharmony_ci * ufshcd_change_queue_depth - change queue depth
47308c2ecf20Sopenharmony_ci * @sdev: pointer to SCSI device
47318c2ecf20Sopenharmony_ci * @depth: required depth to set
47328c2ecf20Sopenharmony_ci *
47338c2ecf20Sopenharmony_ci * Change queue depth and make sure the max. limits are not crossed.
47348c2ecf20Sopenharmony_ci */
47358c2ecf20Sopenharmony_cistatic int ufshcd_change_queue_depth(struct scsi_device *sdev, int depth)
47368c2ecf20Sopenharmony_ci{
47378c2ecf20Sopenharmony_ci	struct ufs_hba *hba = shost_priv(sdev->host);
47388c2ecf20Sopenharmony_ci
47398c2ecf20Sopenharmony_ci	if (depth > hba->nutrs)
47408c2ecf20Sopenharmony_ci		depth = hba->nutrs;
47418c2ecf20Sopenharmony_ci	return scsi_change_queue_depth(sdev, depth);
47428c2ecf20Sopenharmony_ci}
47438c2ecf20Sopenharmony_ci
47448c2ecf20Sopenharmony_ci/**
47458c2ecf20Sopenharmony_ci * ufshcd_slave_configure - adjust SCSI device configurations
47468c2ecf20Sopenharmony_ci * @sdev: pointer to SCSI device
47478c2ecf20Sopenharmony_ci */
47488c2ecf20Sopenharmony_cistatic int ufshcd_slave_configure(struct scsi_device *sdev)
47498c2ecf20Sopenharmony_ci{
47508c2ecf20Sopenharmony_ci	struct ufs_hba *hba = shost_priv(sdev->host);
47518c2ecf20Sopenharmony_ci	struct request_queue *q = sdev->request_queue;
47528c2ecf20Sopenharmony_ci
47538c2ecf20Sopenharmony_ci	blk_queue_update_dma_pad(q, PRDT_DATA_BYTE_COUNT_PAD - 1);
47548c2ecf20Sopenharmony_ci	if (hba->quirks & UFSHCD_QUIRK_ALIGN_SG_WITH_PAGE_SIZE)
47558c2ecf20Sopenharmony_ci		blk_queue_update_dma_alignment(q, PAGE_SIZE - 1);
47568c2ecf20Sopenharmony_ci
47578c2ecf20Sopenharmony_ci	if (ufshcd_is_rpm_autosuspend_allowed(hba))
47588c2ecf20Sopenharmony_ci		sdev->rpm_autosuspend = 1;
47598c2ecf20Sopenharmony_ci
47608c2ecf20Sopenharmony_ci	ufshcd_crypto_setup_rq_keyslot_manager(hba, q);
47618c2ecf20Sopenharmony_ci
47628c2ecf20Sopenharmony_ci	return 0;
47638c2ecf20Sopenharmony_ci}
47648c2ecf20Sopenharmony_ci
47658c2ecf20Sopenharmony_ci/**
47668c2ecf20Sopenharmony_ci * ufshcd_slave_destroy - remove SCSI device configurations
47678c2ecf20Sopenharmony_ci * @sdev: pointer to SCSI device
47688c2ecf20Sopenharmony_ci */
47698c2ecf20Sopenharmony_cistatic void ufshcd_slave_destroy(struct scsi_device *sdev)
47708c2ecf20Sopenharmony_ci{
47718c2ecf20Sopenharmony_ci	struct ufs_hba *hba;
47728c2ecf20Sopenharmony_ci
47738c2ecf20Sopenharmony_ci	hba = shost_priv(sdev->host);
47748c2ecf20Sopenharmony_ci	/* Drop the reference as it won't be needed anymore */
47758c2ecf20Sopenharmony_ci	if (ufshcd_scsi_to_upiu_lun(sdev->lun) == UFS_UPIU_UFS_DEVICE_WLUN) {
47768c2ecf20Sopenharmony_ci		unsigned long flags;
47778c2ecf20Sopenharmony_ci
47788c2ecf20Sopenharmony_ci		spin_lock_irqsave(hba->host->host_lock, flags);
47798c2ecf20Sopenharmony_ci		hba->sdev_ufs_device = NULL;
47808c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(hba->host->host_lock, flags);
47818c2ecf20Sopenharmony_ci	}
47828c2ecf20Sopenharmony_ci}
47838c2ecf20Sopenharmony_ci
47848c2ecf20Sopenharmony_ci/**
47858c2ecf20Sopenharmony_ci * ufshcd_scsi_cmd_status - Update SCSI command result based on SCSI status
47868c2ecf20Sopenharmony_ci * @lrbp: pointer to local reference block of completed command
47878c2ecf20Sopenharmony_ci * @scsi_status: SCSI command status
47888c2ecf20Sopenharmony_ci *
47898c2ecf20Sopenharmony_ci * Returns value base on SCSI command status
47908c2ecf20Sopenharmony_ci */
47918c2ecf20Sopenharmony_cistatic inline int
47928c2ecf20Sopenharmony_ciufshcd_scsi_cmd_status(struct ufshcd_lrb *lrbp, int scsi_status)
47938c2ecf20Sopenharmony_ci{
47948c2ecf20Sopenharmony_ci	int result = 0;
47958c2ecf20Sopenharmony_ci
47968c2ecf20Sopenharmony_ci	switch (scsi_status) {
47978c2ecf20Sopenharmony_ci	case SAM_STAT_CHECK_CONDITION:
47988c2ecf20Sopenharmony_ci		ufshcd_copy_sense_data(lrbp);
47998c2ecf20Sopenharmony_ci		fallthrough;
48008c2ecf20Sopenharmony_ci	case SAM_STAT_GOOD:
48018c2ecf20Sopenharmony_ci		result |= DID_OK << 16 |
48028c2ecf20Sopenharmony_ci			  COMMAND_COMPLETE << 8 |
48038c2ecf20Sopenharmony_ci			  scsi_status;
48048c2ecf20Sopenharmony_ci		break;
48058c2ecf20Sopenharmony_ci	case SAM_STAT_TASK_SET_FULL:
48068c2ecf20Sopenharmony_ci	case SAM_STAT_BUSY:
48078c2ecf20Sopenharmony_ci	case SAM_STAT_TASK_ABORTED:
48088c2ecf20Sopenharmony_ci		ufshcd_copy_sense_data(lrbp);
48098c2ecf20Sopenharmony_ci		result |= scsi_status;
48108c2ecf20Sopenharmony_ci		break;
48118c2ecf20Sopenharmony_ci	default:
48128c2ecf20Sopenharmony_ci		result |= DID_ERROR << 16;
48138c2ecf20Sopenharmony_ci		break;
48148c2ecf20Sopenharmony_ci	} /* end of switch */
48158c2ecf20Sopenharmony_ci
48168c2ecf20Sopenharmony_ci	return result;
48178c2ecf20Sopenharmony_ci}
48188c2ecf20Sopenharmony_ci
48198c2ecf20Sopenharmony_ci/**
48208c2ecf20Sopenharmony_ci * ufshcd_transfer_rsp_status - Get overall status of the response
48218c2ecf20Sopenharmony_ci * @hba: per adapter instance
48228c2ecf20Sopenharmony_ci * @lrbp: pointer to local reference block of completed command
48238c2ecf20Sopenharmony_ci *
48248c2ecf20Sopenharmony_ci * Returns result of the command to notify SCSI midlayer
48258c2ecf20Sopenharmony_ci */
48268c2ecf20Sopenharmony_cistatic inline int
48278c2ecf20Sopenharmony_ciufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
48288c2ecf20Sopenharmony_ci{
48298c2ecf20Sopenharmony_ci	int result = 0;
48308c2ecf20Sopenharmony_ci	int scsi_status;
48318c2ecf20Sopenharmony_ci	int ocs;
48328c2ecf20Sopenharmony_ci
48338c2ecf20Sopenharmony_ci	/* overall command status of utrd */
48348c2ecf20Sopenharmony_ci	ocs = ufshcd_get_tr_ocs(lrbp);
48358c2ecf20Sopenharmony_ci
48368c2ecf20Sopenharmony_ci	if (hba->quirks & UFSHCD_QUIRK_BROKEN_OCS_FATAL_ERROR) {
48378c2ecf20Sopenharmony_ci		if (be32_to_cpu(lrbp->ucd_rsp_ptr->header.dword_1) &
48388c2ecf20Sopenharmony_ci					MASK_RSP_UPIU_RESULT)
48398c2ecf20Sopenharmony_ci			ocs = OCS_SUCCESS;
48408c2ecf20Sopenharmony_ci	}
48418c2ecf20Sopenharmony_ci
48428c2ecf20Sopenharmony_ci	switch (ocs) {
48438c2ecf20Sopenharmony_ci	case OCS_SUCCESS:
48448c2ecf20Sopenharmony_ci		result = ufshcd_get_req_rsp(lrbp->ucd_rsp_ptr);
48458c2ecf20Sopenharmony_ci		hba->ufs_stats.last_hibern8_exit_tstamp = ktime_set(0, 0);
48468c2ecf20Sopenharmony_ci		switch (result) {
48478c2ecf20Sopenharmony_ci		case UPIU_TRANSACTION_RESPONSE:
48488c2ecf20Sopenharmony_ci			/*
48498c2ecf20Sopenharmony_ci			 * get the response UPIU result to extract
48508c2ecf20Sopenharmony_ci			 * the SCSI command status
48518c2ecf20Sopenharmony_ci			 */
48528c2ecf20Sopenharmony_ci			result = ufshcd_get_rsp_upiu_result(lrbp->ucd_rsp_ptr);
48538c2ecf20Sopenharmony_ci
48548c2ecf20Sopenharmony_ci			/*
48558c2ecf20Sopenharmony_ci			 * get the result based on SCSI status response
48568c2ecf20Sopenharmony_ci			 * to notify the SCSI midlayer of the command status
48578c2ecf20Sopenharmony_ci			 */
48588c2ecf20Sopenharmony_ci			scsi_status = result & MASK_SCSI_STATUS;
48598c2ecf20Sopenharmony_ci			result = ufshcd_scsi_cmd_status(lrbp, scsi_status);
48608c2ecf20Sopenharmony_ci
48618c2ecf20Sopenharmony_ci			/*
48628c2ecf20Sopenharmony_ci			 * Currently we are only supporting BKOPs exception
48638c2ecf20Sopenharmony_ci			 * events hence we can ignore BKOPs exception event
48648c2ecf20Sopenharmony_ci			 * during power management callbacks. BKOPs exception
48658c2ecf20Sopenharmony_ci			 * event is not expected to be raised in runtime suspend
48668c2ecf20Sopenharmony_ci			 * callback as it allows the urgent bkops.
48678c2ecf20Sopenharmony_ci			 * During system suspend, we are anyway forcefully
48688c2ecf20Sopenharmony_ci			 * disabling the bkops and if urgent bkops is needed
48698c2ecf20Sopenharmony_ci			 * it will be enabled on system resume. Long term
48708c2ecf20Sopenharmony_ci			 * solution could be to abort the system suspend if
48718c2ecf20Sopenharmony_ci			 * UFS device needs urgent BKOPs.
48728c2ecf20Sopenharmony_ci			 */
48738c2ecf20Sopenharmony_ci			if (!hba->pm_op_in_progress &&
48748c2ecf20Sopenharmony_ci			    ufshcd_is_exception_event(lrbp->ucd_rsp_ptr) &&
48758c2ecf20Sopenharmony_ci			    schedule_work(&hba->eeh_work)) {
48768c2ecf20Sopenharmony_ci				/*
48778c2ecf20Sopenharmony_ci				 * Prevent suspend once eeh_work is scheduled
48788c2ecf20Sopenharmony_ci				 * to avoid deadlock between ufshcd_suspend
48798c2ecf20Sopenharmony_ci				 * and exception event handler.
48808c2ecf20Sopenharmony_ci				 */
48818c2ecf20Sopenharmony_ci				pm_runtime_get_noresume(hba->dev);
48828c2ecf20Sopenharmony_ci			}
48838c2ecf20Sopenharmony_ci			break;
48848c2ecf20Sopenharmony_ci		case UPIU_TRANSACTION_REJECT_UPIU:
48858c2ecf20Sopenharmony_ci			/* TODO: handle Reject UPIU Response */
48868c2ecf20Sopenharmony_ci			result = DID_ERROR << 16;
48878c2ecf20Sopenharmony_ci			dev_err(hba->dev,
48888c2ecf20Sopenharmony_ci				"Reject UPIU not fully implemented\n");
48898c2ecf20Sopenharmony_ci			break;
48908c2ecf20Sopenharmony_ci		default:
48918c2ecf20Sopenharmony_ci			dev_err(hba->dev,
48928c2ecf20Sopenharmony_ci				"Unexpected request response code = %x\n",
48938c2ecf20Sopenharmony_ci				result);
48948c2ecf20Sopenharmony_ci			result = DID_ERROR << 16;
48958c2ecf20Sopenharmony_ci			break;
48968c2ecf20Sopenharmony_ci		}
48978c2ecf20Sopenharmony_ci		break;
48988c2ecf20Sopenharmony_ci	case OCS_ABORTED:
48998c2ecf20Sopenharmony_ci		result |= DID_ABORT << 16;
49008c2ecf20Sopenharmony_ci		break;
49018c2ecf20Sopenharmony_ci	case OCS_INVALID_COMMAND_STATUS:
49028c2ecf20Sopenharmony_ci		result |= DID_REQUEUE << 16;
49038c2ecf20Sopenharmony_ci		break;
49048c2ecf20Sopenharmony_ci	case OCS_INVALID_CMD_TABLE_ATTR:
49058c2ecf20Sopenharmony_ci	case OCS_INVALID_PRDT_ATTR:
49068c2ecf20Sopenharmony_ci	case OCS_MISMATCH_DATA_BUF_SIZE:
49078c2ecf20Sopenharmony_ci	case OCS_MISMATCH_RESP_UPIU_SIZE:
49088c2ecf20Sopenharmony_ci	case OCS_PEER_COMM_FAILURE:
49098c2ecf20Sopenharmony_ci	case OCS_FATAL_ERROR:
49108c2ecf20Sopenharmony_ci	case OCS_DEVICE_FATAL_ERROR:
49118c2ecf20Sopenharmony_ci	case OCS_INVALID_CRYPTO_CONFIG:
49128c2ecf20Sopenharmony_ci	case OCS_GENERAL_CRYPTO_ERROR:
49138c2ecf20Sopenharmony_ci	default:
49148c2ecf20Sopenharmony_ci		result |= DID_ERROR << 16;
49158c2ecf20Sopenharmony_ci		dev_err(hba->dev,
49168c2ecf20Sopenharmony_ci				"OCS error from controller = %x for tag %d\n",
49178c2ecf20Sopenharmony_ci				ocs, lrbp->task_tag);
49188c2ecf20Sopenharmony_ci		ufshcd_print_host_regs(hba);
49198c2ecf20Sopenharmony_ci		ufshcd_print_host_state(hba);
49208c2ecf20Sopenharmony_ci		break;
49218c2ecf20Sopenharmony_ci	} /* end of switch */
49228c2ecf20Sopenharmony_ci
49238c2ecf20Sopenharmony_ci	if ((host_byte(result) != DID_OK) &&
49248c2ecf20Sopenharmony_ci	    (host_byte(result) != DID_REQUEUE) && !hba->silence_err_logs)
49258c2ecf20Sopenharmony_ci		ufshcd_print_trs(hba, 1 << lrbp->task_tag, true);
49268c2ecf20Sopenharmony_ci	return result;
49278c2ecf20Sopenharmony_ci}
49288c2ecf20Sopenharmony_ci
49298c2ecf20Sopenharmony_ci/**
49308c2ecf20Sopenharmony_ci * ufshcd_uic_cmd_compl - handle completion of uic command
49318c2ecf20Sopenharmony_ci * @hba: per adapter instance
49328c2ecf20Sopenharmony_ci * @intr_status: interrupt status generated by the controller
49338c2ecf20Sopenharmony_ci *
49348c2ecf20Sopenharmony_ci * Returns
49358c2ecf20Sopenharmony_ci *  IRQ_HANDLED - If interrupt is valid
49368c2ecf20Sopenharmony_ci *  IRQ_NONE    - If invalid interrupt
49378c2ecf20Sopenharmony_ci */
49388c2ecf20Sopenharmony_cistatic irqreturn_t ufshcd_uic_cmd_compl(struct ufs_hba *hba, u32 intr_status)
49398c2ecf20Sopenharmony_ci{
49408c2ecf20Sopenharmony_ci	irqreturn_t retval = IRQ_NONE;
49418c2ecf20Sopenharmony_ci
49428c2ecf20Sopenharmony_ci	if ((intr_status & UIC_COMMAND_COMPL) && hba->active_uic_cmd) {
49438c2ecf20Sopenharmony_ci		hba->active_uic_cmd->argument2 |=
49448c2ecf20Sopenharmony_ci			ufshcd_get_uic_cmd_result(hba);
49458c2ecf20Sopenharmony_ci		hba->active_uic_cmd->argument3 =
49468c2ecf20Sopenharmony_ci			ufshcd_get_dme_attr_val(hba);
49478c2ecf20Sopenharmony_ci		if (!hba->uic_async_done)
49488c2ecf20Sopenharmony_ci			hba->active_uic_cmd->cmd_active = 0;
49498c2ecf20Sopenharmony_ci		complete(&hba->active_uic_cmd->done);
49508c2ecf20Sopenharmony_ci		retval = IRQ_HANDLED;
49518c2ecf20Sopenharmony_ci	}
49528c2ecf20Sopenharmony_ci
49538c2ecf20Sopenharmony_ci	if ((intr_status & UFSHCD_UIC_PWR_MASK) && hba->uic_async_done) {
49548c2ecf20Sopenharmony_ci		hba->active_uic_cmd->cmd_active = 0;
49558c2ecf20Sopenharmony_ci		complete(hba->uic_async_done);
49568c2ecf20Sopenharmony_ci		retval = IRQ_HANDLED;
49578c2ecf20Sopenharmony_ci	}
49588c2ecf20Sopenharmony_ci
49598c2ecf20Sopenharmony_ci	if (retval == IRQ_HANDLED)
49608c2ecf20Sopenharmony_ci		ufshcd_add_uic_command_trace(hba, hba->active_uic_cmd,
49618c2ecf20Sopenharmony_ci					     "complete");
49628c2ecf20Sopenharmony_ci	return retval;
49638c2ecf20Sopenharmony_ci}
49648c2ecf20Sopenharmony_ci
49658c2ecf20Sopenharmony_ci/**
49668c2ecf20Sopenharmony_ci * __ufshcd_transfer_req_compl - handle SCSI and query command completion
49678c2ecf20Sopenharmony_ci * @hba: per adapter instance
49688c2ecf20Sopenharmony_ci * @completed_reqs: requests to complete
49698c2ecf20Sopenharmony_ci */
49708c2ecf20Sopenharmony_cistatic void __ufshcd_transfer_req_compl(struct ufs_hba *hba,
49718c2ecf20Sopenharmony_ci					unsigned long completed_reqs)
49728c2ecf20Sopenharmony_ci{
49738c2ecf20Sopenharmony_ci	struct ufshcd_lrb *lrbp;
49748c2ecf20Sopenharmony_ci	struct scsi_cmnd *cmd;
49758c2ecf20Sopenharmony_ci	int result;
49768c2ecf20Sopenharmony_ci	int index;
49778c2ecf20Sopenharmony_ci
49788c2ecf20Sopenharmony_ci	for_each_set_bit(index, &completed_reqs, hba->nutrs) {
49798c2ecf20Sopenharmony_ci		lrbp = &hba->lrb[index];
49808c2ecf20Sopenharmony_ci		lrbp->compl_time_stamp = ktime_get();
49818c2ecf20Sopenharmony_ci		cmd = lrbp->cmd;
49828c2ecf20Sopenharmony_ci		if (cmd) {
49838c2ecf20Sopenharmony_ci			ufshcd_add_command_trace(hba, index, "complete");
49848c2ecf20Sopenharmony_ci			result = ufshcd_transfer_rsp_status(hba, lrbp);
49858c2ecf20Sopenharmony_ci			scsi_dma_unmap(cmd);
49868c2ecf20Sopenharmony_ci			cmd->result = result;
49878c2ecf20Sopenharmony_ci			/* Mark completed command as NULL in LRB */
49888c2ecf20Sopenharmony_ci			lrbp->cmd = NULL;
49898c2ecf20Sopenharmony_ci			/* Do not touch lrbp after scsi done */
49908c2ecf20Sopenharmony_ci			cmd->scsi_done(cmd);
49918c2ecf20Sopenharmony_ci			__ufshcd_release(hba);
49928c2ecf20Sopenharmony_ci		} else if (lrbp->command_type == UTP_CMD_TYPE_DEV_MANAGE ||
49938c2ecf20Sopenharmony_ci			lrbp->command_type == UTP_CMD_TYPE_UFS_STORAGE) {
49948c2ecf20Sopenharmony_ci			if (hba->dev_cmd.complete) {
49958c2ecf20Sopenharmony_ci				ufshcd_add_command_trace(hba, index,
49968c2ecf20Sopenharmony_ci						"dev_complete");
49978c2ecf20Sopenharmony_ci				complete(hba->dev_cmd.complete);
49988c2ecf20Sopenharmony_ci			}
49998c2ecf20Sopenharmony_ci		}
50008c2ecf20Sopenharmony_ci		if (ufshcd_is_clkscaling_supported(hba))
50018c2ecf20Sopenharmony_ci			hba->clk_scaling.active_reqs--;
50028c2ecf20Sopenharmony_ci	}
50038c2ecf20Sopenharmony_ci
50048c2ecf20Sopenharmony_ci	/* clear corresponding bits of completed commands */
50058c2ecf20Sopenharmony_ci	hba->outstanding_reqs ^= completed_reqs;
50068c2ecf20Sopenharmony_ci
50078c2ecf20Sopenharmony_ci	ufshcd_clk_scaling_update_busy(hba);
50088c2ecf20Sopenharmony_ci}
50098c2ecf20Sopenharmony_ci
50108c2ecf20Sopenharmony_ci/**
50118c2ecf20Sopenharmony_ci * ufshcd_transfer_req_compl - handle SCSI and query command completion
50128c2ecf20Sopenharmony_ci * @hba: per adapter instance
50138c2ecf20Sopenharmony_ci *
50148c2ecf20Sopenharmony_ci * Returns
50158c2ecf20Sopenharmony_ci *  IRQ_HANDLED - If interrupt is valid
50168c2ecf20Sopenharmony_ci *  IRQ_NONE    - If invalid interrupt
50178c2ecf20Sopenharmony_ci */
50188c2ecf20Sopenharmony_cistatic irqreturn_t ufshcd_transfer_req_compl(struct ufs_hba *hba)
50198c2ecf20Sopenharmony_ci{
50208c2ecf20Sopenharmony_ci	unsigned long completed_reqs;
50218c2ecf20Sopenharmony_ci	u32 tr_doorbell;
50228c2ecf20Sopenharmony_ci
50238c2ecf20Sopenharmony_ci	/* Resetting interrupt aggregation counters first and reading the
50248c2ecf20Sopenharmony_ci	 * DOOR_BELL afterward allows us to handle all the completed requests.
50258c2ecf20Sopenharmony_ci	 * In order to prevent other interrupts starvation the DB is read once
50268c2ecf20Sopenharmony_ci	 * after reset. The down side of this solution is the possibility of
50278c2ecf20Sopenharmony_ci	 * false interrupt if device completes another request after resetting
50288c2ecf20Sopenharmony_ci	 * aggregation and before reading the DB.
50298c2ecf20Sopenharmony_ci	 */
50308c2ecf20Sopenharmony_ci	if (ufshcd_is_intr_aggr_allowed(hba) &&
50318c2ecf20Sopenharmony_ci	    !(hba->quirks & UFSHCI_QUIRK_SKIP_RESET_INTR_AGGR))
50328c2ecf20Sopenharmony_ci		ufshcd_reset_intr_aggr(hba);
50338c2ecf20Sopenharmony_ci
50348c2ecf20Sopenharmony_ci	tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
50358c2ecf20Sopenharmony_ci	completed_reqs = tr_doorbell ^ hba->outstanding_reqs;
50368c2ecf20Sopenharmony_ci
50378c2ecf20Sopenharmony_ci	if (completed_reqs) {
50388c2ecf20Sopenharmony_ci		__ufshcd_transfer_req_compl(hba, completed_reqs);
50398c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
50408c2ecf20Sopenharmony_ci	} else {
50418c2ecf20Sopenharmony_ci		return IRQ_NONE;
50428c2ecf20Sopenharmony_ci	}
50438c2ecf20Sopenharmony_ci}
50448c2ecf20Sopenharmony_ci
50458c2ecf20Sopenharmony_ci/**
50468c2ecf20Sopenharmony_ci * ufshcd_disable_ee - disable exception event
50478c2ecf20Sopenharmony_ci * @hba: per-adapter instance
50488c2ecf20Sopenharmony_ci * @mask: exception event to disable
50498c2ecf20Sopenharmony_ci *
50508c2ecf20Sopenharmony_ci * Disables exception event in the device so that the EVENT_ALERT
50518c2ecf20Sopenharmony_ci * bit is not set.
50528c2ecf20Sopenharmony_ci *
50538c2ecf20Sopenharmony_ci * Returns zero on success, non-zero error value on failure.
50548c2ecf20Sopenharmony_ci */
50558c2ecf20Sopenharmony_cistatic int ufshcd_disable_ee(struct ufs_hba *hba, u16 mask)
50568c2ecf20Sopenharmony_ci{
50578c2ecf20Sopenharmony_ci	int err = 0;
50588c2ecf20Sopenharmony_ci	u32 val;
50598c2ecf20Sopenharmony_ci
50608c2ecf20Sopenharmony_ci	if (!(hba->ee_ctrl_mask & mask))
50618c2ecf20Sopenharmony_ci		goto out;
50628c2ecf20Sopenharmony_ci
50638c2ecf20Sopenharmony_ci	val = hba->ee_ctrl_mask & ~mask;
50648c2ecf20Sopenharmony_ci	val &= MASK_EE_STATUS;
50658c2ecf20Sopenharmony_ci	err = ufshcd_query_attr_retry(hba, UPIU_QUERY_OPCODE_WRITE_ATTR,
50668c2ecf20Sopenharmony_ci			QUERY_ATTR_IDN_EE_CONTROL, 0, 0, &val);
50678c2ecf20Sopenharmony_ci	if (!err)
50688c2ecf20Sopenharmony_ci		hba->ee_ctrl_mask &= ~mask;
50698c2ecf20Sopenharmony_ciout:
50708c2ecf20Sopenharmony_ci	return err;
50718c2ecf20Sopenharmony_ci}
50728c2ecf20Sopenharmony_ci
50738c2ecf20Sopenharmony_ci/**
50748c2ecf20Sopenharmony_ci * ufshcd_enable_ee - enable exception event
50758c2ecf20Sopenharmony_ci * @hba: per-adapter instance
50768c2ecf20Sopenharmony_ci * @mask: exception event to enable
50778c2ecf20Sopenharmony_ci *
50788c2ecf20Sopenharmony_ci * Enable corresponding exception event in the device to allow
50798c2ecf20Sopenharmony_ci * device to alert host in critical scenarios.
50808c2ecf20Sopenharmony_ci *
50818c2ecf20Sopenharmony_ci * Returns zero on success, non-zero error value on failure.
50828c2ecf20Sopenharmony_ci */
50838c2ecf20Sopenharmony_cistatic int ufshcd_enable_ee(struct ufs_hba *hba, u16 mask)
50848c2ecf20Sopenharmony_ci{
50858c2ecf20Sopenharmony_ci	int err = 0;
50868c2ecf20Sopenharmony_ci	u32 val;
50878c2ecf20Sopenharmony_ci
50888c2ecf20Sopenharmony_ci	if (hba->ee_ctrl_mask & mask)
50898c2ecf20Sopenharmony_ci		goto out;
50908c2ecf20Sopenharmony_ci
50918c2ecf20Sopenharmony_ci	val = hba->ee_ctrl_mask | mask;
50928c2ecf20Sopenharmony_ci	val &= MASK_EE_STATUS;
50938c2ecf20Sopenharmony_ci	err = ufshcd_query_attr_retry(hba, UPIU_QUERY_OPCODE_WRITE_ATTR,
50948c2ecf20Sopenharmony_ci			QUERY_ATTR_IDN_EE_CONTROL, 0, 0, &val);
50958c2ecf20Sopenharmony_ci	if (!err)
50968c2ecf20Sopenharmony_ci		hba->ee_ctrl_mask |= mask;
50978c2ecf20Sopenharmony_ciout:
50988c2ecf20Sopenharmony_ci	return err;
50998c2ecf20Sopenharmony_ci}
51008c2ecf20Sopenharmony_ci
51018c2ecf20Sopenharmony_ci/**
51028c2ecf20Sopenharmony_ci * ufshcd_enable_auto_bkops - Allow device managed BKOPS
51038c2ecf20Sopenharmony_ci * @hba: per-adapter instance
51048c2ecf20Sopenharmony_ci *
51058c2ecf20Sopenharmony_ci * Allow device to manage background operations on its own. Enabling
51068c2ecf20Sopenharmony_ci * this might lead to inconsistent latencies during normal data transfers
51078c2ecf20Sopenharmony_ci * as the device is allowed to manage its own way of handling background
51088c2ecf20Sopenharmony_ci * operations.
51098c2ecf20Sopenharmony_ci *
51108c2ecf20Sopenharmony_ci * Returns zero on success, non-zero on failure.
51118c2ecf20Sopenharmony_ci */
51128c2ecf20Sopenharmony_cistatic int ufshcd_enable_auto_bkops(struct ufs_hba *hba)
51138c2ecf20Sopenharmony_ci{
51148c2ecf20Sopenharmony_ci	int err = 0;
51158c2ecf20Sopenharmony_ci
51168c2ecf20Sopenharmony_ci	if (hba->auto_bkops_enabled)
51178c2ecf20Sopenharmony_ci		goto out;
51188c2ecf20Sopenharmony_ci
51198c2ecf20Sopenharmony_ci	err = ufshcd_query_flag_retry(hba, UPIU_QUERY_OPCODE_SET_FLAG,
51208c2ecf20Sopenharmony_ci			QUERY_FLAG_IDN_BKOPS_EN, 0, NULL);
51218c2ecf20Sopenharmony_ci	if (err) {
51228c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: failed to enable bkops %d\n",
51238c2ecf20Sopenharmony_ci				__func__, err);
51248c2ecf20Sopenharmony_ci		goto out;
51258c2ecf20Sopenharmony_ci	}
51268c2ecf20Sopenharmony_ci
51278c2ecf20Sopenharmony_ci	hba->auto_bkops_enabled = true;
51288c2ecf20Sopenharmony_ci	trace_ufshcd_auto_bkops_state(dev_name(hba->dev), "Enabled");
51298c2ecf20Sopenharmony_ci
51308c2ecf20Sopenharmony_ci	/* No need of URGENT_BKOPS exception from the device */
51318c2ecf20Sopenharmony_ci	err = ufshcd_disable_ee(hba, MASK_EE_URGENT_BKOPS);
51328c2ecf20Sopenharmony_ci	if (err)
51338c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: failed to disable exception event %d\n",
51348c2ecf20Sopenharmony_ci				__func__, err);
51358c2ecf20Sopenharmony_ciout:
51368c2ecf20Sopenharmony_ci	return err;
51378c2ecf20Sopenharmony_ci}
51388c2ecf20Sopenharmony_ci
51398c2ecf20Sopenharmony_ci/**
51408c2ecf20Sopenharmony_ci * ufshcd_disable_auto_bkops - block device in doing background operations
51418c2ecf20Sopenharmony_ci * @hba: per-adapter instance
51428c2ecf20Sopenharmony_ci *
51438c2ecf20Sopenharmony_ci * Disabling background operations improves command response latency but
51448c2ecf20Sopenharmony_ci * has drawback of device moving into critical state where the device is
51458c2ecf20Sopenharmony_ci * not-operable. Make sure to call ufshcd_enable_auto_bkops() whenever the
51468c2ecf20Sopenharmony_ci * host is idle so that BKOPS are managed effectively without any negative
51478c2ecf20Sopenharmony_ci * impacts.
51488c2ecf20Sopenharmony_ci *
51498c2ecf20Sopenharmony_ci * Returns zero on success, non-zero on failure.
51508c2ecf20Sopenharmony_ci */
51518c2ecf20Sopenharmony_cistatic int ufshcd_disable_auto_bkops(struct ufs_hba *hba)
51528c2ecf20Sopenharmony_ci{
51538c2ecf20Sopenharmony_ci	int err = 0;
51548c2ecf20Sopenharmony_ci
51558c2ecf20Sopenharmony_ci	if (!hba->auto_bkops_enabled)
51568c2ecf20Sopenharmony_ci		goto out;
51578c2ecf20Sopenharmony_ci
51588c2ecf20Sopenharmony_ci	/*
51598c2ecf20Sopenharmony_ci	 * If host assisted BKOPs is to be enabled, make sure
51608c2ecf20Sopenharmony_ci	 * urgent bkops exception is allowed.
51618c2ecf20Sopenharmony_ci	 */
51628c2ecf20Sopenharmony_ci	err = ufshcd_enable_ee(hba, MASK_EE_URGENT_BKOPS);
51638c2ecf20Sopenharmony_ci	if (err) {
51648c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: failed to enable exception event %d\n",
51658c2ecf20Sopenharmony_ci				__func__, err);
51668c2ecf20Sopenharmony_ci		goto out;
51678c2ecf20Sopenharmony_ci	}
51688c2ecf20Sopenharmony_ci
51698c2ecf20Sopenharmony_ci	err = ufshcd_query_flag_retry(hba, UPIU_QUERY_OPCODE_CLEAR_FLAG,
51708c2ecf20Sopenharmony_ci			QUERY_FLAG_IDN_BKOPS_EN, 0, NULL);
51718c2ecf20Sopenharmony_ci	if (err) {
51728c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: failed to disable bkops %d\n",
51738c2ecf20Sopenharmony_ci				__func__, err);
51748c2ecf20Sopenharmony_ci		ufshcd_disable_ee(hba, MASK_EE_URGENT_BKOPS);
51758c2ecf20Sopenharmony_ci		goto out;
51768c2ecf20Sopenharmony_ci	}
51778c2ecf20Sopenharmony_ci
51788c2ecf20Sopenharmony_ci	hba->auto_bkops_enabled = false;
51798c2ecf20Sopenharmony_ci	trace_ufshcd_auto_bkops_state(dev_name(hba->dev), "Disabled");
51808c2ecf20Sopenharmony_ci	hba->is_urgent_bkops_lvl_checked = false;
51818c2ecf20Sopenharmony_ciout:
51828c2ecf20Sopenharmony_ci	return err;
51838c2ecf20Sopenharmony_ci}
51848c2ecf20Sopenharmony_ci
51858c2ecf20Sopenharmony_ci/**
51868c2ecf20Sopenharmony_ci * ufshcd_force_reset_auto_bkops - force reset auto bkops state
51878c2ecf20Sopenharmony_ci * @hba: per adapter instance
51888c2ecf20Sopenharmony_ci *
51898c2ecf20Sopenharmony_ci * After a device reset the device may toggle the BKOPS_EN flag
51908c2ecf20Sopenharmony_ci * to default value. The s/w tracking variables should be updated
51918c2ecf20Sopenharmony_ci * as well. This function would change the auto-bkops state based on
51928c2ecf20Sopenharmony_ci * UFSHCD_CAP_KEEP_AUTO_BKOPS_ENABLED_EXCEPT_SUSPEND.
51938c2ecf20Sopenharmony_ci */
51948c2ecf20Sopenharmony_cistatic void ufshcd_force_reset_auto_bkops(struct ufs_hba *hba)
51958c2ecf20Sopenharmony_ci{
51968c2ecf20Sopenharmony_ci	if (ufshcd_keep_autobkops_enabled_except_suspend(hba)) {
51978c2ecf20Sopenharmony_ci		hba->auto_bkops_enabled = false;
51988c2ecf20Sopenharmony_ci		hba->ee_ctrl_mask |= MASK_EE_URGENT_BKOPS;
51998c2ecf20Sopenharmony_ci		ufshcd_enable_auto_bkops(hba);
52008c2ecf20Sopenharmony_ci	} else {
52018c2ecf20Sopenharmony_ci		hba->auto_bkops_enabled = true;
52028c2ecf20Sopenharmony_ci		hba->ee_ctrl_mask &= ~MASK_EE_URGENT_BKOPS;
52038c2ecf20Sopenharmony_ci		ufshcd_disable_auto_bkops(hba);
52048c2ecf20Sopenharmony_ci	}
52058c2ecf20Sopenharmony_ci	hba->urgent_bkops_lvl = BKOPS_STATUS_PERF_IMPACT;
52068c2ecf20Sopenharmony_ci	hba->is_urgent_bkops_lvl_checked = false;
52078c2ecf20Sopenharmony_ci}
52088c2ecf20Sopenharmony_ci
52098c2ecf20Sopenharmony_cistatic inline int ufshcd_get_bkops_status(struct ufs_hba *hba, u32 *status)
52108c2ecf20Sopenharmony_ci{
52118c2ecf20Sopenharmony_ci	return ufshcd_query_attr_retry(hba, UPIU_QUERY_OPCODE_READ_ATTR,
52128c2ecf20Sopenharmony_ci			QUERY_ATTR_IDN_BKOPS_STATUS, 0, 0, status);
52138c2ecf20Sopenharmony_ci}
52148c2ecf20Sopenharmony_ci
52158c2ecf20Sopenharmony_ci/**
52168c2ecf20Sopenharmony_ci * ufshcd_bkops_ctrl - control the auto bkops based on current bkops status
52178c2ecf20Sopenharmony_ci * @hba: per-adapter instance
52188c2ecf20Sopenharmony_ci * @status: bkops_status value
52198c2ecf20Sopenharmony_ci *
52208c2ecf20Sopenharmony_ci * Read the bkops_status from the UFS device and Enable fBackgroundOpsEn
52218c2ecf20Sopenharmony_ci * flag in the device to permit background operations if the device
52228c2ecf20Sopenharmony_ci * bkops_status is greater than or equal to "status" argument passed to
52238c2ecf20Sopenharmony_ci * this function, disable otherwise.
52248c2ecf20Sopenharmony_ci *
52258c2ecf20Sopenharmony_ci * Returns 0 for success, non-zero in case of failure.
52268c2ecf20Sopenharmony_ci *
52278c2ecf20Sopenharmony_ci * NOTE: Caller of this function can check the "hba->auto_bkops_enabled" flag
52288c2ecf20Sopenharmony_ci * to know whether auto bkops is enabled or disabled after this function
52298c2ecf20Sopenharmony_ci * returns control to it.
52308c2ecf20Sopenharmony_ci */
52318c2ecf20Sopenharmony_cistatic int ufshcd_bkops_ctrl(struct ufs_hba *hba,
52328c2ecf20Sopenharmony_ci			     enum bkops_status status)
52338c2ecf20Sopenharmony_ci{
52348c2ecf20Sopenharmony_ci	int err;
52358c2ecf20Sopenharmony_ci	u32 curr_status = 0;
52368c2ecf20Sopenharmony_ci
52378c2ecf20Sopenharmony_ci	err = ufshcd_get_bkops_status(hba, &curr_status);
52388c2ecf20Sopenharmony_ci	if (err) {
52398c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: failed to get BKOPS status %d\n",
52408c2ecf20Sopenharmony_ci				__func__, err);
52418c2ecf20Sopenharmony_ci		goto out;
52428c2ecf20Sopenharmony_ci	} else if (curr_status > BKOPS_STATUS_MAX) {
52438c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: invalid BKOPS status %d\n",
52448c2ecf20Sopenharmony_ci				__func__, curr_status);
52458c2ecf20Sopenharmony_ci		err = -EINVAL;
52468c2ecf20Sopenharmony_ci		goto out;
52478c2ecf20Sopenharmony_ci	}
52488c2ecf20Sopenharmony_ci
52498c2ecf20Sopenharmony_ci	if (curr_status >= status)
52508c2ecf20Sopenharmony_ci		err = ufshcd_enable_auto_bkops(hba);
52518c2ecf20Sopenharmony_ci	else
52528c2ecf20Sopenharmony_ci		err = ufshcd_disable_auto_bkops(hba);
52538c2ecf20Sopenharmony_ciout:
52548c2ecf20Sopenharmony_ci	return err;
52558c2ecf20Sopenharmony_ci}
52568c2ecf20Sopenharmony_ci
52578c2ecf20Sopenharmony_ci/**
52588c2ecf20Sopenharmony_ci * ufshcd_urgent_bkops - handle urgent bkops exception event
52598c2ecf20Sopenharmony_ci * @hba: per-adapter instance
52608c2ecf20Sopenharmony_ci *
52618c2ecf20Sopenharmony_ci * Enable fBackgroundOpsEn flag in the device to permit background
52628c2ecf20Sopenharmony_ci * operations.
52638c2ecf20Sopenharmony_ci *
52648c2ecf20Sopenharmony_ci * If BKOPs is enabled, this function returns 0, 1 if the bkops in not enabled
52658c2ecf20Sopenharmony_ci * and negative error value for any other failure.
52668c2ecf20Sopenharmony_ci */
52678c2ecf20Sopenharmony_cistatic int ufshcd_urgent_bkops(struct ufs_hba *hba)
52688c2ecf20Sopenharmony_ci{
52698c2ecf20Sopenharmony_ci	return ufshcd_bkops_ctrl(hba, hba->urgent_bkops_lvl);
52708c2ecf20Sopenharmony_ci}
52718c2ecf20Sopenharmony_ci
52728c2ecf20Sopenharmony_cistatic inline int ufshcd_get_ee_status(struct ufs_hba *hba, u32 *status)
52738c2ecf20Sopenharmony_ci{
52748c2ecf20Sopenharmony_ci	return ufshcd_query_attr_retry(hba, UPIU_QUERY_OPCODE_READ_ATTR,
52758c2ecf20Sopenharmony_ci			QUERY_ATTR_IDN_EE_STATUS, 0, 0, status);
52768c2ecf20Sopenharmony_ci}
52778c2ecf20Sopenharmony_ci
52788c2ecf20Sopenharmony_cistatic void ufshcd_bkops_exception_event_handler(struct ufs_hba *hba)
52798c2ecf20Sopenharmony_ci{
52808c2ecf20Sopenharmony_ci	int err;
52818c2ecf20Sopenharmony_ci	u32 curr_status = 0;
52828c2ecf20Sopenharmony_ci
52838c2ecf20Sopenharmony_ci	if (hba->is_urgent_bkops_lvl_checked)
52848c2ecf20Sopenharmony_ci		goto enable_auto_bkops;
52858c2ecf20Sopenharmony_ci
52868c2ecf20Sopenharmony_ci	err = ufshcd_get_bkops_status(hba, &curr_status);
52878c2ecf20Sopenharmony_ci	if (err) {
52888c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: failed to get BKOPS status %d\n",
52898c2ecf20Sopenharmony_ci				__func__, err);
52908c2ecf20Sopenharmony_ci		goto out;
52918c2ecf20Sopenharmony_ci	}
52928c2ecf20Sopenharmony_ci
52938c2ecf20Sopenharmony_ci	/*
52948c2ecf20Sopenharmony_ci	 * We are seeing that some devices are raising the urgent bkops
52958c2ecf20Sopenharmony_ci	 * exception events even when BKOPS status doesn't indicate performace
52968c2ecf20Sopenharmony_ci	 * impacted or critical. Handle these device by determining their urgent
52978c2ecf20Sopenharmony_ci	 * bkops status at runtime.
52988c2ecf20Sopenharmony_ci	 */
52998c2ecf20Sopenharmony_ci	if (curr_status < BKOPS_STATUS_PERF_IMPACT) {
53008c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: device raised urgent BKOPS exception for bkops status %d\n",
53018c2ecf20Sopenharmony_ci				__func__, curr_status);
53028c2ecf20Sopenharmony_ci		/* update the current status as the urgent bkops level */
53038c2ecf20Sopenharmony_ci		hba->urgent_bkops_lvl = curr_status;
53048c2ecf20Sopenharmony_ci		hba->is_urgent_bkops_lvl_checked = true;
53058c2ecf20Sopenharmony_ci	}
53068c2ecf20Sopenharmony_ci
53078c2ecf20Sopenharmony_cienable_auto_bkops:
53088c2ecf20Sopenharmony_ci	err = ufshcd_enable_auto_bkops(hba);
53098c2ecf20Sopenharmony_ciout:
53108c2ecf20Sopenharmony_ci	if (err < 0)
53118c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: failed to handle urgent bkops %d\n",
53128c2ecf20Sopenharmony_ci				__func__, err);
53138c2ecf20Sopenharmony_ci}
53148c2ecf20Sopenharmony_ci
53158c2ecf20Sopenharmony_cistatic int ufshcd_wb_ctrl(struct ufs_hba *hba, bool enable)
53168c2ecf20Sopenharmony_ci{
53178c2ecf20Sopenharmony_ci	int ret;
53188c2ecf20Sopenharmony_ci	u8 index;
53198c2ecf20Sopenharmony_ci	enum query_opcode opcode;
53208c2ecf20Sopenharmony_ci
53218c2ecf20Sopenharmony_ci	if (!ufshcd_is_wb_allowed(hba))
53228c2ecf20Sopenharmony_ci		return 0;
53238c2ecf20Sopenharmony_ci
53248c2ecf20Sopenharmony_ci	if (!(enable ^ hba->wb_enabled))
53258c2ecf20Sopenharmony_ci		return 0;
53268c2ecf20Sopenharmony_ci	if (enable)
53278c2ecf20Sopenharmony_ci		opcode = UPIU_QUERY_OPCODE_SET_FLAG;
53288c2ecf20Sopenharmony_ci	else
53298c2ecf20Sopenharmony_ci		opcode = UPIU_QUERY_OPCODE_CLEAR_FLAG;
53308c2ecf20Sopenharmony_ci
53318c2ecf20Sopenharmony_ci	index = ufshcd_wb_get_query_index(hba);
53328c2ecf20Sopenharmony_ci	ret = ufshcd_query_flag_retry(hba, opcode,
53338c2ecf20Sopenharmony_ci				      QUERY_FLAG_IDN_WB_EN, index, NULL);
53348c2ecf20Sopenharmony_ci	if (ret) {
53358c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s write booster %s failed %d\n",
53368c2ecf20Sopenharmony_ci			__func__, enable ? "enable" : "disable", ret);
53378c2ecf20Sopenharmony_ci		return ret;
53388c2ecf20Sopenharmony_ci	}
53398c2ecf20Sopenharmony_ci
53408c2ecf20Sopenharmony_ci	hba->wb_enabled = enable;
53418c2ecf20Sopenharmony_ci	dev_dbg(hba->dev, "%s write booster %s %d\n",
53428c2ecf20Sopenharmony_ci			__func__, enable ? "enable" : "disable", ret);
53438c2ecf20Sopenharmony_ci
53448c2ecf20Sopenharmony_ci	return ret;
53458c2ecf20Sopenharmony_ci}
53468c2ecf20Sopenharmony_ci
53478c2ecf20Sopenharmony_cistatic int ufshcd_wb_toggle_flush_during_h8(struct ufs_hba *hba, bool set)
53488c2ecf20Sopenharmony_ci{
53498c2ecf20Sopenharmony_ci	int val;
53508c2ecf20Sopenharmony_ci	u8 index;
53518c2ecf20Sopenharmony_ci
53528c2ecf20Sopenharmony_ci	if (set)
53538c2ecf20Sopenharmony_ci		val =  UPIU_QUERY_OPCODE_SET_FLAG;
53548c2ecf20Sopenharmony_ci	else
53558c2ecf20Sopenharmony_ci		val = UPIU_QUERY_OPCODE_CLEAR_FLAG;
53568c2ecf20Sopenharmony_ci
53578c2ecf20Sopenharmony_ci	index = ufshcd_wb_get_query_index(hba);
53588c2ecf20Sopenharmony_ci	return ufshcd_query_flag_retry(hba, val,
53598c2ecf20Sopenharmony_ci				QUERY_FLAG_IDN_WB_BUFF_FLUSH_DURING_HIBERN8,
53608c2ecf20Sopenharmony_ci				index, NULL);
53618c2ecf20Sopenharmony_ci}
53628c2ecf20Sopenharmony_ci
53638c2ecf20Sopenharmony_cistatic inline void ufshcd_wb_toggle_flush(struct ufs_hba *hba, bool enable)
53648c2ecf20Sopenharmony_ci{
53658c2ecf20Sopenharmony_ci	if (enable)
53668c2ecf20Sopenharmony_ci		ufshcd_wb_buf_flush_enable(hba);
53678c2ecf20Sopenharmony_ci	else
53688c2ecf20Sopenharmony_ci		ufshcd_wb_buf_flush_disable(hba);
53698c2ecf20Sopenharmony_ci
53708c2ecf20Sopenharmony_ci}
53718c2ecf20Sopenharmony_ci
53728c2ecf20Sopenharmony_cistatic int ufshcd_wb_buf_flush_enable(struct ufs_hba *hba)
53738c2ecf20Sopenharmony_ci{
53748c2ecf20Sopenharmony_ci	int ret;
53758c2ecf20Sopenharmony_ci	u8 index;
53768c2ecf20Sopenharmony_ci
53778c2ecf20Sopenharmony_ci	if (!ufshcd_is_wb_allowed(hba) || hba->wb_buf_flush_enabled)
53788c2ecf20Sopenharmony_ci		return 0;
53798c2ecf20Sopenharmony_ci
53808c2ecf20Sopenharmony_ci	index = ufshcd_wb_get_query_index(hba);
53818c2ecf20Sopenharmony_ci	ret = ufshcd_query_flag_retry(hba, UPIU_QUERY_OPCODE_SET_FLAG,
53828c2ecf20Sopenharmony_ci				      QUERY_FLAG_IDN_WB_BUFF_FLUSH_EN,
53838c2ecf20Sopenharmony_ci				      index, NULL);
53848c2ecf20Sopenharmony_ci	if (ret)
53858c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s WB - buf flush enable failed %d\n",
53868c2ecf20Sopenharmony_ci			__func__, ret);
53878c2ecf20Sopenharmony_ci	else
53888c2ecf20Sopenharmony_ci		hba->wb_buf_flush_enabled = true;
53898c2ecf20Sopenharmony_ci
53908c2ecf20Sopenharmony_ci	dev_dbg(hba->dev, "WB - Flush enabled: %d\n", ret);
53918c2ecf20Sopenharmony_ci	return ret;
53928c2ecf20Sopenharmony_ci}
53938c2ecf20Sopenharmony_ci
53948c2ecf20Sopenharmony_cistatic int ufshcd_wb_buf_flush_disable(struct ufs_hba *hba)
53958c2ecf20Sopenharmony_ci{
53968c2ecf20Sopenharmony_ci	int ret;
53978c2ecf20Sopenharmony_ci	u8 index;
53988c2ecf20Sopenharmony_ci
53998c2ecf20Sopenharmony_ci	if (!ufshcd_is_wb_allowed(hba) || !hba->wb_buf_flush_enabled)
54008c2ecf20Sopenharmony_ci		return 0;
54018c2ecf20Sopenharmony_ci
54028c2ecf20Sopenharmony_ci	index = ufshcd_wb_get_query_index(hba);
54038c2ecf20Sopenharmony_ci	ret = ufshcd_query_flag_retry(hba, UPIU_QUERY_OPCODE_CLEAR_FLAG,
54048c2ecf20Sopenharmony_ci				      QUERY_FLAG_IDN_WB_BUFF_FLUSH_EN,
54058c2ecf20Sopenharmony_ci				      index, NULL);
54068c2ecf20Sopenharmony_ci	if (ret) {
54078c2ecf20Sopenharmony_ci		dev_warn(hba->dev, "%s: WB - buf flush disable failed %d\n",
54088c2ecf20Sopenharmony_ci			 __func__, ret);
54098c2ecf20Sopenharmony_ci	} else {
54108c2ecf20Sopenharmony_ci		hba->wb_buf_flush_enabled = false;
54118c2ecf20Sopenharmony_ci		dev_dbg(hba->dev, "WB - Flush disabled: %d\n", ret);
54128c2ecf20Sopenharmony_ci	}
54138c2ecf20Sopenharmony_ci
54148c2ecf20Sopenharmony_ci	return ret;
54158c2ecf20Sopenharmony_ci}
54168c2ecf20Sopenharmony_ci
54178c2ecf20Sopenharmony_cistatic bool ufshcd_wb_presrv_usrspc_keep_vcc_on(struct ufs_hba *hba,
54188c2ecf20Sopenharmony_ci						u32 avail_buf)
54198c2ecf20Sopenharmony_ci{
54208c2ecf20Sopenharmony_ci	u32 cur_buf;
54218c2ecf20Sopenharmony_ci	int ret;
54228c2ecf20Sopenharmony_ci	u8 index;
54238c2ecf20Sopenharmony_ci
54248c2ecf20Sopenharmony_ci	index = ufshcd_wb_get_query_index(hba);
54258c2ecf20Sopenharmony_ci	ret = ufshcd_query_attr_retry(hba, UPIU_QUERY_OPCODE_READ_ATTR,
54268c2ecf20Sopenharmony_ci					      QUERY_ATTR_IDN_CURR_WB_BUFF_SIZE,
54278c2ecf20Sopenharmony_ci					      index, 0, &cur_buf);
54288c2ecf20Sopenharmony_ci	if (ret) {
54298c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s dCurWriteBoosterBufferSize read failed %d\n",
54308c2ecf20Sopenharmony_ci			__func__, ret);
54318c2ecf20Sopenharmony_ci		return false;
54328c2ecf20Sopenharmony_ci	}
54338c2ecf20Sopenharmony_ci
54348c2ecf20Sopenharmony_ci	if (!cur_buf) {
54358c2ecf20Sopenharmony_ci		dev_info(hba->dev, "dCurWBBuf: %d WB disabled until free-space is available\n",
54368c2ecf20Sopenharmony_ci			 cur_buf);
54378c2ecf20Sopenharmony_ci		return false;
54388c2ecf20Sopenharmony_ci	}
54398c2ecf20Sopenharmony_ci	/* Let it continue to flush when available buffer exceeds threshold */
54408c2ecf20Sopenharmony_ci	if (avail_buf < hba->vps->wb_flush_threshold)
54418c2ecf20Sopenharmony_ci		return true;
54428c2ecf20Sopenharmony_ci
54438c2ecf20Sopenharmony_ci	return false;
54448c2ecf20Sopenharmony_ci}
54458c2ecf20Sopenharmony_ci
54468c2ecf20Sopenharmony_cistatic bool ufshcd_wb_need_flush(struct ufs_hba *hba)
54478c2ecf20Sopenharmony_ci{
54488c2ecf20Sopenharmony_ci	int ret;
54498c2ecf20Sopenharmony_ci	u32 avail_buf;
54508c2ecf20Sopenharmony_ci	u8 index;
54518c2ecf20Sopenharmony_ci
54528c2ecf20Sopenharmony_ci	if (!ufshcd_is_wb_allowed(hba))
54538c2ecf20Sopenharmony_ci		return false;
54548c2ecf20Sopenharmony_ci	/*
54558c2ecf20Sopenharmony_ci	 * The ufs device needs the vcc to be ON to flush.
54568c2ecf20Sopenharmony_ci	 * With user-space reduction enabled, it's enough to enable flush
54578c2ecf20Sopenharmony_ci	 * by checking only the available buffer. The threshold
54588c2ecf20Sopenharmony_ci	 * defined here is > 90% full.
54598c2ecf20Sopenharmony_ci	 * With user-space preserved enabled, the current-buffer
54608c2ecf20Sopenharmony_ci	 * should be checked too because the wb buffer size can reduce
54618c2ecf20Sopenharmony_ci	 * when disk tends to be full. This info is provided by current
54628c2ecf20Sopenharmony_ci	 * buffer (dCurrentWriteBoosterBufferSize). There's no point in
54638c2ecf20Sopenharmony_ci	 * keeping vcc on when current buffer is empty.
54648c2ecf20Sopenharmony_ci	 */
54658c2ecf20Sopenharmony_ci	index = ufshcd_wb_get_query_index(hba);
54668c2ecf20Sopenharmony_ci	ret = ufshcd_query_attr_retry(hba, UPIU_QUERY_OPCODE_READ_ATTR,
54678c2ecf20Sopenharmony_ci				      QUERY_ATTR_IDN_AVAIL_WB_BUFF_SIZE,
54688c2ecf20Sopenharmony_ci				      index, 0, &avail_buf);
54698c2ecf20Sopenharmony_ci	if (ret) {
54708c2ecf20Sopenharmony_ci		dev_warn(hba->dev, "%s dAvailableWriteBoosterBufferSize read failed %d\n",
54718c2ecf20Sopenharmony_ci			 __func__, ret);
54728c2ecf20Sopenharmony_ci		return false;
54738c2ecf20Sopenharmony_ci	}
54748c2ecf20Sopenharmony_ci
54758c2ecf20Sopenharmony_ci	if (!hba->dev_info.b_presrv_uspc_en) {
54768c2ecf20Sopenharmony_ci		if (avail_buf <= UFS_WB_BUF_REMAIN_PERCENT(10))
54778c2ecf20Sopenharmony_ci			return true;
54788c2ecf20Sopenharmony_ci		return false;
54798c2ecf20Sopenharmony_ci	}
54808c2ecf20Sopenharmony_ci
54818c2ecf20Sopenharmony_ci	return ufshcd_wb_presrv_usrspc_keep_vcc_on(hba, avail_buf);
54828c2ecf20Sopenharmony_ci}
54838c2ecf20Sopenharmony_ci
54848c2ecf20Sopenharmony_cistatic void ufshcd_rpm_dev_flush_recheck_work(struct work_struct *work)
54858c2ecf20Sopenharmony_ci{
54868c2ecf20Sopenharmony_ci	struct ufs_hba *hba = container_of(to_delayed_work(work),
54878c2ecf20Sopenharmony_ci					   struct ufs_hba,
54888c2ecf20Sopenharmony_ci					   rpm_dev_flush_recheck_work);
54898c2ecf20Sopenharmony_ci	/*
54908c2ecf20Sopenharmony_ci	 * To prevent unnecessary VCC power drain after device finishes
54918c2ecf20Sopenharmony_ci	 * WriteBooster buffer flush or Auto BKOPs, force runtime resume
54928c2ecf20Sopenharmony_ci	 * after a certain delay to recheck the threshold by next runtime
54938c2ecf20Sopenharmony_ci	 * suspend.
54948c2ecf20Sopenharmony_ci	 */
54958c2ecf20Sopenharmony_ci	pm_runtime_get_sync(hba->dev);
54968c2ecf20Sopenharmony_ci	pm_runtime_put_sync(hba->dev);
54978c2ecf20Sopenharmony_ci}
54988c2ecf20Sopenharmony_ci
54998c2ecf20Sopenharmony_ci/**
55008c2ecf20Sopenharmony_ci * ufshcd_exception_event_handler - handle exceptions raised by device
55018c2ecf20Sopenharmony_ci * @work: pointer to work data
55028c2ecf20Sopenharmony_ci *
55038c2ecf20Sopenharmony_ci * Read bExceptionEventStatus attribute from the device and handle the
55048c2ecf20Sopenharmony_ci * exception event accordingly.
55058c2ecf20Sopenharmony_ci */
55068c2ecf20Sopenharmony_cistatic void ufshcd_exception_event_handler(struct work_struct *work)
55078c2ecf20Sopenharmony_ci{
55088c2ecf20Sopenharmony_ci	struct ufs_hba *hba;
55098c2ecf20Sopenharmony_ci	int err;
55108c2ecf20Sopenharmony_ci	u32 status = 0;
55118c2ecf20Sopenharmony_ci	hba = container_of(work, struct ufs_hba, eeh_work);
55128c2ecf20Sopenharmony_ci
55138c2ecf20Sopenharmony_ci	pm_runtime_get_sync(hba->dev);
55148c2ecf20Sopenharmony_ci	ufshcd_scsi_block_requests(hba);
55158c2ecf20Sopenharmony_ci	err = ufshcd_get_ee_status(hba, &status);
55168c2ecf20Sopenharmony_ci	if (err) {
55178c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: failed to get exception status %d\n",
55188c2ecf20Sopenharmony_ci				__func__, err);
55198c2ecf20Sopenharmony_ci		goto out;
55208c2ecf20Sopenharmony_ci	}
55218c2ecf20Sopenharmony_ci
55228c2ecf20Sopenharmony_ci	status &= hba->ee_ctrl_mask;
55238c2ecf20Sopenharmony_ci
55248c2ecf20Sopenharmony_ci	if (status & MASK_EE_URGENT_BKOPS)
55258c2ecf20Sopenharmony_ci		ufshcd_bkops_exception_event_handler(hba);
55268c2ecf20Sopenharmony_ci
55278c2ecf20Sopenharmony_ciout:
55288c2ecf20Sopenharmony_ci	ufshcd_scsi_unblock_requests(hba);
55298c2ecf20Sopenharmony_ci	/*
55308c2ecf20Sopenharmony_ci	 * pm_runtime_get_noresume is called while scheduling
55318c2ecf20Sopenharmony_ci	 * eeh_work to avoid suspend racing with exception work.
55328c2ecf20Sopenharmony_ci	 * Hence decrement usage counter using pm_runtime_put_noidle
55338c2ecf20Sopenharmony_ci	 * to allow suspend on completion of exception event handler.
55348c2ecf20Sopenharmony_ci	 */
55358c2ecf20Sopenharmony_ci	pm_runtime_put_noidle(hba->dev);
55368c2ecf20Sopenharmony_ci	pm_runtime_put(hba->dev);
55378c2ecf20Sopenharmony_ci	return;
55388c2ecf20Sopenharmony_ci}
55398c2ecf20Sopenharmony_ci
55408c2ecf20Sopenharmony_ci/* Complete requests that have door-bell cleared */
55418c2ecf20Sopenharmony_cistatic void ufshcd_complete_requests(struct ufs_hba *hba)
55428c2ecf20Sopenharmony_ci{
55438c2ecf20Sopenharmony_ci	ufshcd_transfer_req_compl(hba);
55448c2ecf20Sopenharmony_ci	ufshcd_tmc_handler(hba);
55458c2ecf20Sopenharmony_ci}
55468c2ecf20Sopenharmony_ci
55478c2ecf20Sopenharmony_ci/**
55488c2ecf20Sopenharmony_ci * ufshcd_quirk_dl_nac_errors - This function checks if error handling is
55498c2ecf20Sopenharmony_ci *				to recover from the DL NAC errors or not.
55508c2ecf20Sopenharmony_ci * @hba: per-adapter instance
55518c2ecf20Sopenharmony_ci *
55528c2ecf20Sopenharmony_ci * Returns true if error handling is required, false otherwise
55538c2ecf20Sopenharmony_ci */
55548c2ecf20Sopenharmony_cistatic bool ufshcd_quirk_dl_nac_errors(struct ufs_hba *hba)
55558c2ecf20Sopenharmony_ci{
55568c2ecf20Sopenharmony_ci	unsigned long flags;
55578c2ecf20Sopenharmony_ci	bool err_handling = true;
55588c2ecf20Sopenharmony_ci
55598c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
55608c2ecf20Sopenharmony_ci	/*
55618c2ecf20Sopenharmony_ci	 * UFS_DEVICE_QUIRK_RECOVERY_FROM_DL_NAC_ERRORS only workaround the
55628c2ecf20Sopenharmony_ci	 * device fatal error and/or DL NAC & REPLAY timeout errors.
55638c2ecf20Sopenharmony_ci	 */
55648c2ecf20Sopenharmony_ci	if (hba->saved_err & (CONTROLLER_FATAL_ERROR | SYSTEM_BUS_FATAL_ERROR))
55658c2ecf20Sopenharmony_ci		goto out;
55668c2ecf20Sopenharmony_ci
55678c2ecf20Sopenharmony_ci	if ((hba->saved_err & DEVICE_FATAL_ERROR) ||
55688c2ecf20Sopenharmony_ci	    ((hba->saved_err & UIC_ERROR) &&
55698c2ecf20Sopenharmony_ci	     (hba->saved_uic_err & UFSHCD_UIC_DL_TCx_REPLAY_ERROR)))
55708c2ecf20Sopenharmony_ci		goto out;
55718c2ecf20Sopenharmony_ci
55728c2ecf20Sopenharmony_ci	if ((hba->saved_err & UIC_ERROR) &&
55738c2ecf20Sopenharmony_ci	    (hba->saved_uic_err & UFSHCD_UIC_DL_NAC_RECEIVED_ERROR)) {
55748c2ecf20Sopenharmony_ci		int err;
55758c2ecf20Sopenharmony_ci		/*
55768c2ecf20Sopenharmony_ci		 * wait for 50ms to see if we can get any other errors or not.
55778c2ecf20Sopenharmony_ci		 */
55788c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(hba->host->host_lock, flags);
55798c2ecf20Sopenharmony_ci		msleep(50);
55808c2ecf20Sopenharmony_ci		spin_lock_irqsave(hba->host->host_lock, flags);
55818c2ecf20Sopenharmony_ci
55828c2ecf20Sopenharmony_ci		/*
55838c2ecf20Sopenharmony_ci		 * now check if we have got any other severe errors other than
55848c2ecf20Sopenharmony_ci		 * DL NAC error?
55858c2ecf20Sopenharmony_ci		 */
55868c2ecf20Sopenharmony_ci		if ((hba->saved_err & INT_FATAL_ERRORS) ||
55878c2ecf20Sopenharmony_ci		    ((hba->saved_err & UIC_ERROR) &&
55888c2ecf20Sopenharmony_ci		    (hba->saved_uic_err & ~UFSHCD_UIC_DL_NAC_RECEIVED_ERROR)))
55898c2ecf20Sopenharmony_ci			goto out;
55908c2ecf20Sopenharmony_ci
55918c2ecf20Sopenharmony_ci		/*
55928c2ecf20Sopenharmony_ci		 * As DL NAC is the only error received so far, send out NOP
55938c2ecf20Sopenharmony_ci		 * command to confirm if link is still active or not.
55948c2ecf20Sopenharmony_ci		 *   - If we don't get any response then do error recovery.
55958c2ecf20Sopenharmony_ci		 *   - If we get response then clear the DL NAC error bit.
55968c2ecf20Sopenharmony_ci		 */
55978c2ecf20Sopenharmony_ci
55988c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(hba->host->host_lock, flags);
55998c2ecf20Sopenharmony_ci		err = ufshcd_verify_dev_init(hba);
56008c2ecf20Sopenharmony_ci		spin_lock_irqsave(hba->host->host_lock, flags);
56018c2ecf20Sopenharmony_ci
56028c2ecf20Sopenharmony_ci		if (err)
56038c2ecf20Sopenharmony_ci			goto out;
56048c2ecf20Sopenharmony_ci
56058c2ecf20Sopenharmony_ci		/* Link seems to be alive hence ignore the DL NAC errors */
56068c2ecf20Sopenharmony_ci		if (hba->saved_uic_err == UFSHCD_UIC_DL_NAC_RECEIVED_ERROR)
56078c2ecf20Sopenharmony_ci			hba->saved_err &= ~UIC_ERROR;
56088c2ecf20Sopenharmony_ci		/* clear NAC error */
56098c2ecf20Sopenharmony_ci		hba->saved_uic_err &= ~UFSHCD_UIC_DL_NAC_RECEIVED_ERROR;
56108c2ecf20Sopenharmony_ci		if (!hba->saved_uic_err)
56118c2ecf20Sopenharmony_ci			err_handling = false;
56128c2ecf20Sopenharmony_ci	}
56138c2ecf20Sopenharmony_ciout:
56148c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
56158c2ecf20Sopenharmony_ci	return err_handling;
56168c2ecf20Sopenharmony_ci}
56178c2ecf20Sopenharmony_ci
56188c2ecf20Sopenharmony_ci/* host lock must be held before calling this func */
56198c2ecf20Sopenharmony_cistatic inline bool ufshcd_is_saved_err_fatal(struct ufs_hba *hba)
56208c2ecf20Sopenharmony_ci{
56218c2ecf20Sopenharmony_ci	return (hba->saved_uic_err & UFSHCD_UIC_DL_PA_INIT_ERROR) ||
56228c2ecf20Sopenharmony_ci	       (hba->saved_err & (INT_FATAL_ERRORS | UFSHCD_UIC_HIBERN8_MASK));
56238c2ecf20Sopenharmony_ci}
56248c2ecf20Sopenharmony_ci
56258c2ecf20Sopenharmony_ci/* host lock must be held before calling this func */
56268c2ecf20Sopenharmony_cistatic inline void ufshcd_schedule_eh_work(struct ufs_hba *hba)
56278c2ecf20Sopenharmony_ci{
56288c2ecf20Sopenharmony_ci	/* handle fatal errors only when link is not in error state */
56298c2ecf20Sopenharmony_ci	if (hba->ufshcd_state != UFSHCD_STATE_ERROR) {
56308c2ecf20Sopenharmony_ci		if (hba->force_reset || ufshcd_is_link_broken(hba) ||
56318c2ecf20Sopenharmony_ci		    ufshcd_is_saved_err_fatal(hba))
56328c2ecf20Sopenharmony_ci			hba->ufshcd_state = UFSHCD_STATE_EH_SCHEDULED_FATAL;
56338c2ecf20Sopenharmony_ci		else
56348c2ecf20Sopenharmony_ci			hba->ufshcd_state = UFSHCD_STATE_EH_SCHEDULED_NON_FATAL;
56358c2ecf20Sopenharmony_ci		queue_work(hba->eh_wq, &hba->eh_work);
56368c2ecf20Sopenharmony_ci	}
56378c2ecf20Sopenharmony_ci}
56388c2ecf20Sopenharmony_ci
56398c2ecf20Sopenharmony_cistatic void ufshcd_err_handling_prepare(struct ufs_hba *hba)
56408c2ecf20Sopenharmony_ci{
56418c2ecf20Sopenharmony_ci	pm_runtime_get_sync(hba->dev);
56428c2ecf20Sopenharmony_ci	if (pm_runtime_suspended(hba->dev)) {
56438c2ecf20Sopenharmony_ci		/*
56448c2ecf20Sopenharmony_ci		 * Don't assume anything of pm_runtime_get_sync(), if
56458c2ecf20Sopenharmony_ci		 * resume fails, irq and clocks can be OFF, and powers
56468c2ecf20Sopenharmony_ci		 * can be OFF or in LPM.
56478c2ecf20Sopenharmony_ci		 */
56488c2ecf20Sopenharmony_ci		ufshcd_setup_hba_vreg(hba, true);
56498c2ecf20Sopenharmony_ci		ufshcd_enable_irq(hba);
56508c2ecf20Sopenharmony_ci		ufshcd_setup_vreg(hba, true);
56518c2ecf20Sopenharmony_ci		ufshcd_config_vreg_hpm(hba, hba->vreg_info.vccq);
56528c2ecf20Sopenharmony_ci		ufshcd_config_vreg_hpm(hba, hba->vreg_info.vccq2);
56538c2ecf20Sopenharmony_ci		ufshcd_hold(hba, false);
56548c2ecf20Sopenharmony_ci		if (!ufshcd_is_clkgating_allowed(hba))
56558c2ecf20Sopenharmony_ci			ufshcd_setup_clocks(hba, true);
56568c2ecf20Sopenharmony_ci		ufshcd_release(hba);
56578c2ecf20Sopenharmony_ci		ufshcd_vops_resume(hba, UFS_RUNTIME_PM);
56588c2ecf20Sopenharmony_ci	} else {
56598c2ecf20Sopenharmony_ci		ufshcd_hold(hba, false);
56608c2ecf20Sopenharmony_ci		if (hba->clk_scaling.is_allowed) {
56618c2ecf20Sopenharmony_ci			cancel_work_sync(&hba->clk_scaling.suspend_work);
56628c2ecf20Sopenharmony_ci			cancel_work_sync(&hba->clk_scaling.resume_work);
56638c2ecf20Sopenharmony_ci			ufshcd_suspend_clkscaling(hba);
56648c2ecf20Sopenharmony_ci		}
56658c2ecf20Sopenharmony_ci	}
56668c2ecf20Sopenharmony_ci}
56678c2ecf20Sopenharmony_ci
56688c2ecf20Sopenharmony_cistatic void ufshcd_err_handling_unprepare(struct ufs_hba *hba)
56698c2ecf20Sopenharmony_ci{
56708c2ecf20Sopenharmony_ci	ufshcd_release(hba);
56718c2ecf20Sopenharmony_ci	if (hba->clk_scaling.is_allowed)
56728c2ecf20Sopenharmony_ci		ufshcd_resume_clkscaling(hba);
56738c2ecf20Sopenharmony_ci	pm_runtime_put(hba->dev);
56748c2ecf20Sopenharmony_ci}
56758c2ecf20Sopenharmony_ci
56768c2ecf20Sopenharmony_cistatic inline bool ufshcd_err_handling_should_stop(struct ufs_hba *hba)
56778c2ecf20Sopenharmony_ci{
56788c2ecf20Sopenharmony_ci	return (hba->ufshcd_state == UFSHCD_STATE_ERROR ||
56798c2ecf20Sopenharmony_ci		(!(hba->saved_err || hba->saved_uic_err || hba->force_reset ||
56808c2ecf20Sopenharmony_ci			ufshcd_is_link_broken(hba))));
56818c2ecf20Sopenharmony_ci}
56828c2ecf20Sopenharmony_ci
56838c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
56848c2ecf20Sopenharmony_cistatic void ufshcd_recover_pm_error(struct ufs_hba *hba)
56858c2ecf20Sopenharmony_ci{
56868c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = hba->host;
56878c2ecf20Sopenharmony_ci	struct scsi_device *sdev;
56888c2ecf20Sopenharmony_ci	struct request_queue *q;
56898c2ecf20Sopenharmony_ci	int ret;
56908c2ecf20Sopenharmony_ci
56918c2ecf20Sopenharmony_ci	/*
56928c2ecf20Sopenharmony_ci	 * Set RPM status of hba device to RPM_ACTIVE,
56938c2ecf20Sopenharmony_ci	 * this also clears its runtime error.
56948c2ecf20Sopenharmony_ci	 */
56958c2ecf20Sopenharmony_ci	ret = pm_runtime_set_active(hba->dev);
56968c2ecf20Sopenharmony_ci	/*
56978c2ecf20Sopenharmony_ci	 * If hba device had runtime error, we also need to resume those
56988c2ecf20Sopenharmony_ci	 * scsi devices under hba in case any of them has failed to be
56998c2ecf20Sopenharmony_ci	 * resumed due to hba runtime resume failure. This is to unblock
57008c2ecf20Sopenharmony_ci	 * blk_queue_enter in case there are bios waiting inside it.
57018c2ecf20Sopenharmony_ci	 */
57028c2ecf20Sopenharmony_ci	if (!ret) {
57038c2ecf20Sopenharmony_ci		shost_for_each_device(sdev, shost) {
57048c2ecf20Sopenharmony_ci			q = sdev->request_queue;
57058c2ecf20Sopenharmony_ci			if (q->dev && (q->rpm_status == RPM_SUSPENDED ||
57068c2ecf20Sopenharmony_ci				       q->rpm_status == RPM_SUSPENDING))
57078c2ecf20Sopenharmony_ci				pm_request_resume(q->dev);
57088c2ecf20Sopenharmony_ci		}
57098c2ecf20Sopenharmony_ci	}
57108c2ecf20Sopenharmony_ci}
57118c2ecf20Sopenharmony_ci#else
57128c2ecf20Sopenharmony_cistatic inline void ufshcd_recover_pm_error(struct ufs_hba *hba)
57138c2ecf20Sopenharmony_ci{
57148c2ecf20Sopenharmony_ci}
57158c2ecf20Sopenharmony_ci#endif
57168c2ecf20Sopenharmony_ci
57178c2ecf20Sopenharmony_cistatic bool ufshcd_is_pwr_mode_restore_needed(struct ufs_hba *hba)
57188c2ecf20Sopenharmony_ci{
57198c2ecf20Sopenharmony_ci	struct ufs_pa_layer_attr *pwr_info = &hba->pwr_info;
57208c2ecf20Sopenharmony_ci	u32 mode;
57218c2ecf20Sopenharmony_ci
57228c2ecf20Sopenharmony_ci	ufshcd_dme_get(hba, UIC_ARG_MIB(PA_PWRMODE), &mode);
57238c2ecf20Sopenharmony_ci
57248c2ecf20Sopenharmony_ci	if (pwr_info->pwr_rx != ((mode >> PWRMODE_RX_OFFSET) & PWRMODE_MASK))
57258c2ecf20Sopenharmony_ci		return true;
57268c2ecf20Sopenharmony_ci
57278c2ecf20Sopenharmony_ci	if (pwr_info->pwr_tx != (mode & PWRMODE_MASK))
57288c2ecf20Sopenharmony_ci		return true;
57298c2ecf20Sopenharmony_ci
57308c2ecf20Sopenharmony_ci	return false;
57318c2ecf20Sopenharmony_ci}
57328c2ecf20Sopenharmony_ci
57338c2ecf20Sopenharmony_ci/**
57348c2ecf20Sopenharmony_ci * ufshcd_err_handler - handle UFS errors that require s/w attention
57358c2ecf20Sopenharmony_ci * @work: pointer to work structure
57368c2ecf20Sopenharmony_ci */
57378c2ecf20Sopenharmony_cistatic void ufshcd_err_handler(struct work_struct *work)
57388c2ecf20Sopenharmony_ci{
57398c2ecf20Sopenharmony_ci	struct ufs_hba *hba;
57408c2ecf20Sopenharmony_ci	unsigned long flags;
57418c2ecf20Sopenharmony_ci	bool err_xfer = false;
57428c2ecf20Sopenharmony_ci	bool err_tm = false;
57438c2ecf20Sopenharmony_ci	int err = 0, pmc_err;
57448c2ecf20Sopenharmony_ci	int tag;
57458c2ecf20Sopenharmony_ci	bool needs_reset = false, needs_restore = false;
57468c2ecf20Sopenharmony_ci
57478c2ecf20Sopenharmony_ci	hba = container_of(work, struct ufs_hba, eh_work);
57488c2ecf20Sopenharmony_ci
57498c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
57508c2ecf20Sopenharmony_ci	if (ufshcd_err_handling_should_stop(hba)) {
57518c2ecf20Sopenharmony_ci		if (hba->ufshcd_state != UFSHCD_STATE_ERROR)
57528c2ecf20Sopenharmony_ci			hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL;
57538c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(hba->host->host_lock, flags);
57548c2ecf20Sopenharmony_ci		return;
57558c2ecf20Sopenharmony_ci	}
57568c2ecf20Sopenharmony_ci	ufshcd_set_eh_in_progress(hba);
57578c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
57588c2ecf20Sopenharmony_ci	ufshcd_err_handling_prepare(hba);
57598c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
57608c2ecf20Sopenharmony_ci	ufshcd_scsi_block_requests(hba);
57618c2ecf20Sopenharmony_ci	/*
57628c2ecf20Sopenharmony_ci	 * A full reset and restore might have happened after preparation
57638c2ecf20Sopenharmony_ci	 * is finished, double check whether we should stop.
57648c2ecf20Sopenharmony_ci	 */
57658c2ecf20Sopenharmony_ci	if (ufshcd_err_handling_should_stop(hba)) {
57668c2ecf20Sopenharmony_ci		if (hba->ufshcd_state != UFSHCD_STATE_ERROR)
57678c2ecf20Sopenharmony_ci			hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL;
57688c2ecf20Sopenharmony_ci		goto out;
57698c2ecf20Sopenharmony_ci	}
57708c2ecf20Sopenharmony_ci	hba->ufshcd_state = UFSHCD_STATE_RESET;
57718c2ecf20Sopenharmony_ci
57728c2ecf20Sopenharmony_ci	/* Complete requests that have door-bell cleared by h/w */
57738c2ecf20Sopenharmony_ci	ufshcd_complete_requests(hba);
57748c2ecf20Sopenharmony_ci
57758c2ecf20Sopenharmony_ci	if (hba->dev_quirks & UFS_DEVICE_QUIRK_RECOVERY_FROM_DL_NAC_ERRORS) {
57768c2ecf20Sopenharmony_ci		bool ret;
57778c2ecf20Sopenharmony_ci
57788c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(hba->host->host_lock, flags);
57798c2ecf20Sopenharmony_ci		/* release the lock as ufshcd_quirk_dl_nac_errors() may sleep */
57808c2ecf20Sopenharmony_ci		ret = ufshcd_quirk_dl_nac_errors(hba);
57818c2ecf20Sopenharmony_ci		spin_lock_irqsave(hba->host->host_lock, flags);
57828c2ecf20Sopenharmony_ci		if (!ret && !hba->force_reset && ufshcd_is_link_active(hba))
57838c2ecf20Sopenharmony_ci			goto skip_err_handling;
57848c2ecf20Sopenharmony_ci	}
57858c2ecf20Sopenharmony_ci
57868c2ecf20Sopenharmony_ci	if (hba->force_reset || ufshcd_is_link_broken(hba) ||
57878c2ecf20Sopenharmony_ci	    ufshcd_is_saved_err_fatal(hba) ||
57888c2ecf20Sopenharmony_ci	    ((hba->saved_err & UIC_ERROR) &&
57898c2ecf20Sopenharmony_ci	     (hba->saved_uic_err & (UFSHCD_UIC_DL_NAC_RECEIVED_ERROR |
57908c2ecf20Sopenharmony_ci				    UFSHCD_UIC_DL_TCx_REPLAY_ERROR))))
57918c2ecf20Sopenharmony_ci		needs_reset = true;
57928c2ecf20Sopenharmony_ci
57938c2ecf20Sopenharmony_ci	if ((hba->saved_err & (INT_FATAL_ERRORS | UFSHCD_UIC_HIBERN8_MASK)) ||
57948c2ecf20Sopenharmony_ci	    (hba->saved_uic_err &&
57958c2ecf20Sopenharmony_ci	     (hba->saved_uic_err != UFSHCD_UIC_PA_GENERIC_ERROR))) {
57968c2ecf20Sopenharmony_ci		bool pr_prdt = !!(hba->saved_err & SYSTEM_BUS_FATAL_ERROR);
57978c2ecf20Sopenharmony_ci
57988c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(hba->host->host_lock, flags);
57998c2ecf20Sopenharmony_ci		ufshcd_print_host_state(hba);
58008c2ecf20Sopenharmony_ci		ufshcd_print_pwr_info(hba);
58018c2ecf20Sopenharmony_ci		ufshcd_print_host_regs(hba);
58028c2ecf20Sopenharmony_ci		ufshcd_print_tmrs(hba, hba->outstanding_tasks);
58038c2ecf20Sopenharmony_ci		ufshcd_print_trs(hba, hba->outstanding_reqs, pr_prdt);
58048c2ecf20Sopenharmony_ci		spin_lock_irqsave(hba->host->host_lock, flags);
58058c2ecf20Sopenharmony_ci	}
58068c2ecf20Sopenharmony_ci
58078c2ecf20Sopenharmony_ci	/*
58088c2ecf20Sopenharmony_ci	 * if host reset is required then skip clearing the pending
58098c2ecf20Sopenharmony_ci	 * transfers forcefully because they will get cleared during
58108c2ecf20Sopenharmony_ci	 * host reset and restore
58118c2ecf20Sopenharmony_ci	 */
58128c2ecf20Sopenharmony_ci	if (needs_reset)
58138c2ecf20Sopenharmony_ci		goto do_reset;
58148c2ecf20Sopenharmony_ci
58158c2ecf20Sopenharmony_ci	/*
58168c2ecf20Sopenharmony_ci	 * If LINERESET was caught, UFS might have been put to PWM mode,
58178c2ecf20Sopenharmony_ci	 * check if power mode restore is needed.
58188c2ecf20Sopenharmony_ci	 */
58198c2ecf20Sopenharmony_ci	if (hba->saved_uic_err & UFSHCD_UIC_PA_GENERIC_ERROR) {
58208c2ecf20Sopenharmony_ci		hba->saved_uic_err &= ~UFSHCD_UIC_PA_GENERIC_ERROR;
58218c2ecf20Sopenharmony_ci		if (!hba->saved_uic_err)
58228c2ecf20Sopenharmony_ci			hba->saved_err &= ~UIC_ERROR;
58238c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(hba->host->host_lock, flags);
58248c2ecf20Sopenharmony_ci		if (ufshcd_is_pwr_mode_restore_needed(hba))
58258c2ecf20Sopenharmony_ci			needs_restore = true;
58268c2ecf20Sopenharmony_ci		spin_lock_irqsave(hba->host->host_lock, flags);
58278c2ecf20Sopenharmony_ci		if (!hba->saved_err && !needs_restore)
58288c2ecf20Sopenharmony_ci			goto skip_err_handling;
58298c2ecf20Sopenharmony_ci	}
58308c2ecf20Sopenharmony_ci
58318c2ecf20Sopenharmony_ci	hba->silence_err_logs = true;
58328c2ecf20Sopenharmony_ci	/* release lock as clear command might sleep */
58338c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
58348c2ecf20Sopenharmony_ci	/* Clear pending transfer requests */
58358c2ecf20Sopenharmony_ci	for_each_set_bit(tag, &hba->outstanding_reqs, hba->nutrs) {
58368c2ecf20Sopenharmony_ci		if (ufshcd_try_to_abort_task(hba, tag)) {
58378c2ecf20Sopenharmony_ci			err_xfer = true;
58388c2ecf20Sopenharmony_ci			goto lock_skip_pending_xfer_clear;
58398c2ecf20Sopenharmony_ci		}
58408c2ecf20Sopenharmony_ci	}
58418c2ecf20Sopenharmony_ci
58428c2ecf20Sopenharmony_ci	/* Clear pending task management requests */
58438c2ecf20Sopenharmony_ci	for_each_set_bit(tag, &hba->outstanding_tasks, hba->nutmrs) {
58448c2ecf20Sopenharmony_ci		if (ufshcd_clear_tm_cmd(hba, tag)) {
58458c2ecf20Sopenharmony_ci			err_tm = true;
58468c2ecf20Sopenharmony_ci			goto lock_skip_pending_xfer_clear;
58478c2ecf20Sopenharmony_ci		}
58488c2ecf20Sopenharmony_ci	}
58498c2ecf20Sopenharmony_ci
58508c2ecf20Sopenharmony_cilock_skip_pending_xfer_clear:
58518c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
58528c2ecf20Sopenharmony_ci
58538c2ecf20Sopenharmony_ci	/* Complete the requests that are cleared by s/w */
58548c2ecf20Sopenharmony_ci	ufshcd_complete_requests(hba);
58558c2ecf20Sopenharmony_ci	hba->silence_err_logs = false;
58568c2ecf20Sopenharmony_ci
58578c2ecf20Sopenharmony_ci	if (err_xfer || err_tm) {
58588c2ecf20Sopenharmony_ci		needs_reset = true;
58598c2ecf20Sopenharmony_ci		goto do_reset;
58608c2ecf20Sopenharmony_ci	}
58618c2ecf20Sopenharmony_ci
58628c2ecf20Sopenharmony_ci	/*
58638c2ecf20Sopenharmony_ci	 * After all reqs and tasks are cleared from doorbell,
58648c2ecf20Sopenharmony_ci	 * now it is safe to retore power mode.
58658c2ecf20Sopenharmony_ci	 */
58668c2ecf20Sopenharmony_ci	if (needs_restore) {
58678c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(hba->host->host_lock, flags);
58688c2ecf20Sopenharmony_ci		/*
58698c2ecf20Sopenharmony_ci		 * Hold the scaling lock just in case dev cmds
58708c2ecf20Sopenharmony_ci		 * are sent via bsg and/or sysfs.
58718c2ecf20Sopenharmony_ci		 */
58728c2ecf20Sopenharmony_ci		down_write(&hba->clk_scaling_lock);
58738c2ecf20Sopenharmony_ci		hba->force_pmc = true;
58748c2ecf20Sopenharmony_ci		pmc_err = ufshcd_config_pwr_mode(hba, &(hba->pwr_info));
58758c2ecf20Sopenharmony_ci		if (pmc_err) {
58768c2ecf20Sopenharmony_ci			needs_reset = true;
58778c2ecf20Sopenharmony_ci			dev_err(hba->dev, "%s: Failed to restore power mode, err = %d\n",
58788c2ecf20Sopenharmony_ci					__func__, pmc_err);
58798c2ecf20Sopenharmony_ci		}
58808c2ecf20Sopenharmony_ci		hba->force_pmc = false;
58818c2ecf20Sopenharmony_ci		ufshcd_print_pwr_info(hba);
58828c2ecf20Sopenharmony_ci		up_write(&hba->clk_scaling_lock);
58838c2ecf20Sopenharmony_ci		spin_lock_irqsave(hba->host->host_lock, flags);
58848c2ecf20Sopenharmony_ci	}
58858c2ecf20Sopenharmony_ci
58868c2ecf20Sopenharmony_cido_reset:
58878c2ecf20Sopenharmony_ci	/* Fatal errors need reset */
58888c2ecf20Sopenharmony_ci	if (needs_reset) {
58898c2ecf20Sopenharmony_ci		unsigned long max_doorbells = (1UL << hba->nutrs) - 1;
58908c2ecf20Sopenharmony_ci
58918c2ecf20Sopenharmony_ci		/*
58928c2ecf20Sopenharmony_ci		 * ufshcd_reset_and_restore() does the link reinitialization
58938c2ecf20Sopenharmony_ci		 * which will need atleast one empty doorbell slot to send the
58948c2ecf20Sopenharmony_ci		 * device management commands (NOP and query commands).
58958c2ecf20Sopenharmony_ci		 * If there is no slot empty at this moment then free up last
58968c2ecf20Sopenharmony_ci		 * slot forcefully.
58978c2ecf20Sopenharmony_ci		 */
58988c2ecf20Sopenharmony_ci		if (hba->outstanding_reqs == max_doorbells)
58998c2ecf20Sopenharmony_ci			__ufshcd_transfer_req_compl(hba,
59008c2ecf20Sopenharmony_ci						    (1UL << (hba->nutrs - 1)));
59018c2ecf20Sopenharmony_ci
59028c2ecf20Sopenharmony_ci		hba->force_reset = false;
59038c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(hba->host->host_lock, flags);
59048c2ecf20Sopenharmony_ci		err = ufshcd_reset_and_restore(hba);
59058c2ecf20Sopenharmony_ci		if (err)
59068c2ecf20Sopenharmony_ci			dev_err(hba->dev, "%s: reset and restore failed with err %d\n",
59078c2ecf20Sopenharmony_ci					__func__, err);
59088c2ecf20Sopenharmony_ci		else
59098c2ecf20Sopenharmony_ci			ufshcd_recover_pm_error(hba);
59108c2ecf20Sopenharmony_ci		spin_lock_irqsave(hba->host->host_lock, flags);
59118c2ecf20Sopenharmony_ci	}
59128c2ecf20Sopenharmony_ci
59138c2ecf20Sopenharmony_ciskip_err_handling:
59148c2ecf20Sopenharmony_ci	if (!needs_reset) {
59158c2ecf20Sopenharmony_ci		if (hba->ufshcd_state == UFSHCD_STATE_RESET)
59168c2ecf20Sopenharmony_ci			hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL;
59178c2ecf20Sopenharmony_ci		if (hba->saved_err || hba->saved_uic_err)
59188c2ecf20Sopenharmony_ci			dev_err_ratelimited(hba->dev, "%s: exit: saved_err 0x%x saved_uic_err 0x%x",
59198c2ecf20Sopenharmony_ci			    __func__, hba->saved_err, hba->saved_uic_err);
59208c2ecf20Sopenharmony_ci	}
59218c2ecf20Sopenharmony_ci
59228c2ecf20Sopenharmony_ciout:
59238c2ecf20Sopenharmony_ci	ufshcd_clear_eh_in_progress(hba);
59248c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
59258c2ecf20Sopenharmony_ci	ufshcd_scsi_unblock_requests(hba);
59268c2ecf20Sopenharmony_ci	ufshcd_err_handling_unprepare(hba);
59278c2ecf20Sopenharmony_ci}
59288c2ecf20Sopenharmony_ci
59298c2ecf20Sopenharmony_ci/**
59308c2ecf20Sopenharmony_ci * ufshcd_update_uic_error - check and set fatal UIC error flags.
59318c2ecf20Sopenharmony_ci * @hba: per-adapter instance
59328c2ecf20Sopenharmony_ci *
59338c2ecf20Sopenharmony_ci * Returns
59348c2ecf20Sopenharmony_ci *  IRQ_HANDLED - If interrupt is valid
59358c2ecf20Sopenharmony_ci *  IRQ_NONE    - If invalid interrupt
59368c2ecf20Sopenharmony_ci */
59378c2ecf20Sopenharmony_cistatic irqreturn_t ufshcd_update_uic_error(struct ufs_hba *hba)
59388c2ecf20Sopenharmony_ci{
59398c2ecf20Sopenharmony_ci	u32 reg;
59408c2ecf20Sopenharmony_ci	irqreturn_t retval = IRQ_NONE;
59418c2ecf20Sopenharmony_ci
59428c2ecf20Sopenharmony_ci	/* PHY layer error */
59438c2ecf20Sopenharmony_ci	reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER);
59448c2ecf20Sopenharmony_ci	if ((reg & UIC_PHY_ADAPTER_LAYER_ERROR) &&
59458c2ecf20Sopenharmony_ci	    (reg & UIC_PHY_ADAPTER_LAYER_ERROR_CODE_MASK)) {
59468c2ecf20Sopenharmony_ci		ufshcd_update_reg_hist(&hba->ufs_stats.pa_err, reg);
59478c2ecf20Sopenharmony_ci		/*
59488c2ecf20Sopenharmony_ci		 * To know whether this error is fatal or not, DB timeout
59498c2ecf20Sopenharmony_ci		 * must be checked but this error is handled separately.
59508c2ecf20Sopenharmony_ci		 */
59518c2ecf20Sopenharmony_ci		if (reg & UIC_PHY_ADAPTER_LAYER_LANE_ERR_MASK)
59528c2ecf20Sopenharmony_ci			dev_dbg(hba->dev, "%s: UIC Lane error reported\n",
59538c2ecf20Sopenharmony_ci					__func__);
59548c2ecf20Sopenharmony_ci
59558c2ecf20Sopenharmony_ci		/* Got a LINERESET indication. */
59568c2ecf20Sopenharmony_ci		if (reg & UIC_PHY_ADAPTER_LAYER_GENERIC_ERROR) {
59578c2ecf20Sopenharmony_ci			struct uic_command *cmd = NULL;
59588c2ecf20Sopenharmony_ci
59598c2ecf20Sopenharmony_ci			hba->uic_error |= UFSHCD_UIC_PA_GENERIC_ERROR;
59608c2ecf20Sopenharmony_ci			if (hba->uic_async_done && hba->active_uic_cmd)
59618c2ecf20Sopenharmony_ci				cmd = hba->active_uic_cmd;
59628c2ecf20Sopenharmony_ci			/*
59638c2ecf20Sopenharmony_ci			 * Ignore the LINERESET during power mode change
59648c2ecf20Sopenharmony_ci			 * operation via DME_SET command.
59658c2ecf20Sopenharmony_ci			 */
59668c2ecf20Sopenharmony_ci			if (cmd && (cmd->command == UIC_CMD_DME_SET))
59678c2ecf20Sopenharmony_ci				hba->uic_error &= ~UFSHCD_UIC_PA_GENERIC_ERROR;
59688c2ecf20Sopenharmony_ci		}
59698c2ecf20Sopenharmony_ci		retval |= IRQ_HANDLED;
59708c2ecf20Sopenharmony_ci	}
59718c2ecf20Sopenharmony_ci
59728c2ecf20Sopenharmony_ci	/* PA_INIT_ERROR is fatal and needs UIC reset */
59738c2ecf20Sopenharmony_ci	reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_DATA_LINK_LAYER);
59748c2ecf20Sopenharmony_ci	if ((reg & UIC_DATA_LINK_LAYER_ERROR) &&
59758c2ecf20Sopenharmony_ci	    (reg & UIC_DATA_LINK_LAYER_ERROR_CODE_MASK)) {
59768c2ecf20Sopenharmony_ci		ufshcd_update_reg_hist(&hba->ufs_stats.dl_err, reg);
59778c2ecf20Sopenharmony_ci
59788c2ecf20Sopenharmony_ci		if (reg & UIC_DATA_LINK_LAYER_ERROR_PA_INIT)
59798c2ecf20Sopenharmony_ci			hba->uic_error |= UFSHCD_UIC_DL_PA_INIT_ERROR;
59808c2ecf20Sopenharmony_ci		else if (hba->dev_quirks &
59818c2ecf20Sopenharmony_ci				UFS_DEVICE_QUIRK_RECOVERY_FROM_DL_NAC_ERRORS) {
59828c2ecf20Sopenharmony_ci			if (reg & UIC_DATA_LINK_LAYER_ERROR_NAC_RECEIVED)
59838c2ecf20Sopenharmony_ci				hba->uic_error |=
59848c2ecf20Sopenharmony_ci					UFSHCD_UIC_DL_NAC_RECEIVED_ERROR;
59858c2ecf20Sopenharmony_ci			else if (reg & UIC_DATA_LINK_LAYER_ERROR_TCx_REPLAY_TIMEOUT)
59868c2ecf20Sopenharmony_ci				hba->uic_error |= UFSHCD_UIC_DL_TCx_REPLAY_ERROR;
59878c2ecf20Sopenharmony_ci		}
59888c2ecf20Sopenharmony_ci		retval |= IRQ_HANDLED;
59898c2ecf20Sopenharmony_ci	}
59908c2ecf20Sopenharmony_ci
59918c2ecf20Sopenharmony_ci	/* UIC NL/TL/DME errors needs software retry */
59928c2ecf20Sopenharmony_ci	reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_NETWORK_LAYER);
59938c2ecf20Sopenharmony_ci	if ((reg & UIC_NETWORK_LAYER_ERROR) &&
59948c2ecf20Sopenharmony_ci	    (reg & UIC_NETWORK_LAYER_ERROR_CODE_MASK)) {
59958c2ecf20Sopenharmony_ci		ufshcd_update_reg_hist(&hba->ufs_stats.nl_err, reg);
59968c2ecf20Sopenharmony_ci		hba->uic_error |= UFSHCD_UIC_NL_ERROR;
59978c2ecf20Sopenharmony_ci		retval |= IRQ_HANDLED;
59988c2ecf20Sopenharmony_ci	}
59998c2ecf20Sopenharmony_ci
60008c2ecf20Sopenharmony_ci	reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_TRANSPORT_LAYER);
60018c2ecf20Sopenharmony_ci	if ((reg & UIC_TRANSPORT_LAYER_ERROR) &&
60028c2ecf20Sopenharmony_ci	    (reg & UIC_TRANSPORT_LAYER_ERROR_CODE_MASK)) {
60038c2ecf20Sopenharmony_ci		ufshcd_update_reg_hist(&hba->ufs_stats.tl_err, reg);
60048c2ecf20Sopenharmony_ci		hba->uic_error |= UFSHCD_UIC_TL_ERROR;
60058c2ecf20Sopenharmony_ci		retval |= IRQ_HANDLED;
60068c2ecf20Sopenharmony_ci	}
60078c2ecf20Sopenharmony_ci
60088c2ecf20Sopenharmony_ci	reg = ufshcd_readl(hba, REG_UIC_ERROR_CODE_DME);
60098c2ecf20Sopenharmony_ci	if ((reg & UIC_DME_ERROR) &&
60108c2ecf20Sopenharmony_ci	    (reg & UIC_DME_ERROR_CODE_MASK)) {
60118c2ecf20Sopenharmony_ci		ufshcd_update_reg_hist(&hba->ufs_stats.dme_err, reg);
60128c2ecf20Sopenharmony_ci		hba->uic_error |= UFSHCD_UIC_DME_ERROR;
60138c2ecf20Sopenharmony_ci		retval |= IRQ_HANDLED;
60148c2ecf20Sopenharmony_ci	}
60158c2ecf20Sopenharmony_ci
60168c2ecf20Sopenharmony_ci	dev_dbg(hba->dev, "%s: UIC error flags = 0x%08x\n",
60178c2ecf20Sopenharmony_ci			__func__, hba->uic_error);
60188c2ecf20Sopenharmony_ci	return retval;
60198c2ecf20Sopenharmony_ci}
60208c2ecf20Sopenharmony_ci
60218c2ecf20Sopenharmony_cistatic bool ufshcd_is_auto_hibern8_error(struct ufs_hba *hba,
60228c2ecf20Sopenharmony_ci					 u32 intr_mask)
60238c2ecf20Sopenharmony_ci{
60248c2ecf20Sopenharmony_ci	if (!ufshcd_is_auto_hibern8_supported(hba) ||
60258c2ecf20Sopenharmony_ci	    !ufshcd_is_auto_hibern8_enabled(hba))
60268c2ecf20Sopenharmony_ci		return false;
60278c2ecf20Sopenharmony_ci
60288c2ecf20Sopenharmony_ci	if (!(intr_mask & UFSHCD_UIC_HIBERN8_MASK))
60298c2ecf20Sopenharmony_ci		return false;
60308c2ecf20Sopenharmony_ci
60318c2ecf20Sopenharmony_ci	if (hba->active_uic_cmd &&
60328c2ecf20Sopenharmony_ci	    (hba->active_uic_cmd->command == UIC_CMD_DME_HIBER_ENTER ||
60338c2ecf20Sopenharmony_ci	    hba->active_uic_cmd->command == UIC_CMD_DME_HIBER_EXIT))
60348c2ecf20Sopenharmony_ci		return false;
60358c2ecf20Sopenharmony_ci
60368c2ecf20Sopenharmony_ci	return true;
60378c2ecf20Sopenharmony_ci}
60388c2ecf20Sopenharmony_ci
60398c2ecf20Sopenharmony_ci/**
60408c2ecf20Sopenharmony_ci * ufshcd_check_errors - Check for errors that need s/w attention
60418c2ecf20Sopenharmony_ci * @hba: per-adapter instance
60428c2ecf20Sopenharmony_ci *
60438c2ecf20Sopenharmony_ci * Returns
60448c2ecf20Sopenharmony_ci *  IRQ_HANDLED - If interrupt is valid
60458c2ecf20Sopenharmony_ci *  IRQ_NONE    - If invalid interrupt
60468c2ecf20Sopenharmony_ci */
60478c2ecf20Sopenharmony_cistatic irqreturn_t ufshcd_check_errors(struct ufs_hba *hba)
60488c2ecf20Sopenharmony_ci{
60498c2ecf20Sopenharmony_ci	bool queue_eh_work = false;
60508c2ecf20Sopenharmony_ci	irqreturn_t retval = IRQ_NONE;
60518c2ecf20Sopenharmony_ci
60528c2ecf20Sopenharmony_ci	if (hba->errors & INT_FATAL_ERRORS) {
60538c2ecf20Sopenharmony_ci		ufshcd_update_reg_hist(&hba->ufs_stats.fatal_err, hba->errors);
60548c2ecf20Sopenharmony_ci		queue_eh_work = true;
60558c2ecf20Sopenharmony_ci	}
60568c2ecf20Sopenharmony_ci
60578c2ecf20Sopenharmony_ci	if (hba->errors & UIC_ERROR) {
60588c2ecf20Sopenharmony_ci		hba->uic_error = 0;
60598c2ecf20Sopenharmony_ci		retval = ufshcd_update_uic_error(hba);
60608c2ecf20Sopenharmony_ci		if (hba->uic_error)
60618c2ecf20Sopenharmony_ci			queue_eh_work = true;
60628c2ecf20Sopenharmony_ci	}
60638c2ecf20Sopenharmony_ci
60648c2ecf20Sopenharmony_ci	if (hba->errors & UFSHCD_UIC_HIBERN8_MASK) {
60658c2ecf20Sopenharmony_ci		dev_err(hba->dev,
60668c2ecf20Sopenharmony_ci			"%s: Auto Hibern8 %s failed - status: 0x%08x, upmcrs: 0x%08x\n",
60678c2ecf20Sopenharmony_ci			__func__, (hba->errors & UIC_HIBERNATE_ENTER) ?
60688c2ecf20Sopenharmony_ci			"Enter" : "Exit",
60698c2ecf20Sopenharmony_ci			hba->errors, ufshcd_get_upmcrs(hba));
60708c2ecf20Sopenharmony_ci		ufshcd_update_reg_hist(&hba->ufs_stats.auto_hibern8_err,
60718c2ecf20Sopenharmony_ci				       hba->errors);
60728c2ecf20Sopenharmony_ci		ufshcd_set_link_broken(hba);
60738c2ecf20Sopenharmony_ci		queue_eh_work = true;
60748c2ecf20Sopenharmony_ci	}
60758c2ecf20Sopenharmony_ci
60768c2ecf20Sopenharmony_ci	if (queue_eh_work) {
60778c2ecf20Sopenharmony_ci		/*
60788c2ecf20Sopenharmony_ci		 * update the transfer error masks to sticky bits, let's do this
60798c2ecf20Sopenharmony_ci		 * irrespective of current ufshcd_state.
60808c2ecf20Sopenharmony_ci		 */
60818c2ecf20Sopenharmony_ci		hba->saved_err |= hba->errors;
60828c2ecf20Sopenharmony_ci		hba->saved_uic_err |= hba->uic_error;
60838c2ecf20Sopenharmony_ci
60848c2ecf20Sopenharmony_ci		/* dump controller state before resetting */
60858c2ecf20Sopenharmony_ci		if ((hba->saved_err & (INT_FATAL_ERRORS)) ||
60868c2ecf20Sopenharmony_ci		    (hba->saved_uic_err &&
60878c2ecf20Sopenharmony_ci		     (hba->saved_uic_err != UFSHCD_UIC_PA_GENERIC_ERROR))) {
60888c2ecf20Sopenharmony_ci			dev_err(hba->dev, "%s: saved_err 0x%x saved_uic_err 0x%x\n",
60898c2ecf20Sopenharmony_ci					__func__, hba->saved_err,
60908c2ecf20Sopenharmony_ci					hba->saved_uic_err);
60918c2ecf20Sopenharmony_ci			ufshcd_dump_regs(hba, 0, UFSHCI_REG_SPACE_SIZE,
60928c2ecf20Sopenharmony_ci					 "host_regs: ");
60938c2ecf20Sopenharmony_ci			ufshcd_print_pwr_info(hba);
60948c2ecf20Sopenharmony_ci		}
60958c2ecf20Sopenharmony_ci		ufshcd_schedule_eh_work(hba);
60968c2ecf20Sopenharmony_ci		retval |= IRQ_HANDLED;
60978c2ecf20Sopenharmony_ci	}
60988c2ecf20Sopenharmony_ci	/*
60998c2ecf20Sopenharmony_ci	 * if (!queue_eh_work) -
61008c2ecf20Sopenharmony_ci	 * Other errors are either non-fatal where host recovers
61018c2ecf20Sopenharmony_ci	 * itself without s/w intervention or errors that will be
61028c2ecf20Sopenharmony_ci	 * handled by the SCSI core layer.
61038c2ecf20Sopenharmony_ci	 */
61048c2ecf20Sopenharmony_ci	return retval;
61058c2ecf20Sopenharmony_ci}
61068c2ecf20Sopenharmony_ci
61078c2ecf20Sopenharmony_ci/**
61088c2ecf20Sopenharmony_ci * ufshcd_tmc_handler - handle task management function completion
61098c2ecf20Sopenharmony_ci * @hba: per adapter instance
61108c2ecf20Sopenharmony_ci *
61118c2ecf20Sopenharmony_ci * Returns
61128c2ecf20Sopenharmony_ci *  IRQ_HANDLED - If interrupt is valid
61138c2ecf20Sopenharmony_ci *  IRQ_NONE    - If invalid interrupt
61148c2ecf20Sopenharmony_ci */
61158c2ecf20Sopenharmony_cistatic irqreturn_t ufshcd_tmc_handler(struct ufs_hba *hba)
61168c2ecf20Sopenharmony_ci{
61178c2ecf20Sopenharmony_ci	unsigned long pending, issued;
61188c2ecf20Sopenharmony_ci	irqreturn_t ret = IRQ_NONE;
61198c2ecf20Sopenharmony_ci	int tag;
61208c2ecf20Sopenharmony_ci
61218c2ecf20Sopenharmony_ci	pending = ufshcd_readl(hba, REG_UTP_TASK_REQ_DOOR_BELL);
61228c2ecf20Sopenharmony_ci
61238c2ecf20Sopenharmony_ci	issued = hba->outstanding_tasks & ~pending;
61248c2ecf20Sopenharmony_ci	for_each_set_bit(tag, &issued, hba->nutmrs) {
61258c2ecf20Sopenharmony_ci		struct request *req = hba->tmf_rqs[tag];
61268c2ecf20Sopenharmony_ci		struct completion *c = req->end_io_data;
61278c2ecf20Sopenharmony_ci
61288c2ecf20Sopenharmony_ci		complete(c);
61298c2ecf20Sopenharmony_ci		ret = IRQ_HANDLED;
61308c2ecf20Sopenharmony_ci	}
61318c2ecf20Sopenharmony_ci
61328c2ecf20Sopenharmony_ci	return ret;
61338c2ecf20Sopenharmony_ci}
61348c2ecf20Sopenharmony_ci
61358c2ecf20Sopenharmony_ci/**
61368c2ecf20Sopenharmony_ci * ufshcd_sl_intr - Interrupt service routine
61378c2ecf20Sopenharmony_ci * @hba: per adapter instance
61388c2ecf20Sopenharmony_ci * @intr_status: contains interrupts generated by the controller
61398c2ecf20Sopenharmony_ci *
61408c2ecf20Sopenharmony_ci * Returns
61418c2ecf20Sopenharmony_ci *  IRQ_HANDLED - If interrupt is valid
61428c2ecf20Sopenharmony_ci *  IRQ_NONE    - If invalid interrupt
61438c2ecf20Sopenharmony_ci */
61448c2ecf20Sopenharmony_cistatic irqreturn_t ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
61458c2ecf20Sopenharmony_ci{
61468c2ecf20Sopenharmony_ci	irqreturn_t retval = IRQ_NONE;
61478c2ecf20Sopenharmony_ci
61488c2ecf20Sopenharmony_ci	hba->errors = UFSHCD_ERROR_MASK & intr_status;
61498c2ecf20Sopenharmony_ci
61508c2ecf20Sopenharmony_ci	if (ufshcd_is_auto_hibern8_error(hba, intr_status))
61518c2ecf20Sopenharmony_ci		hba->errors |= (UFSHCD_UIC_HIBERN8_MASK & intr_status);
61528c2ecf20Sopenharmony_ci
61538c2ecf20Sopenharmony_ci	if (hba->errors)
61548c2ecf20Sopenharmony_ci		retval |= ufshcd_check_errors(hba);
61558c2ecf20Sopenharmony_ci
61568c2ecf20Sopenharmony_ci	if (intr_status & UFSHCD_UIC_MASK)
61578c2ecf20Sopenharmony_ci		retval |= ufshcd_uic_cmd_compl(hba, intr_status);
61588c2ecf20Sopenharmony_ci
61598c2ecf20Sopenharmony_ci	if (intr_status & UTP_TASK_REQ_COMPL)
61608c2ecf20Sopenharmony_ci		retval |= ufshcd_tmc_handler(hba);
61618c2ecf20Sopenharmony_ci
61628c2ecf20Sopenharmony_ci	if (intr_status & UTP_TRANSFER_REQ_COMPL)
61638c2ecf20Sopenharmony_ci		retval |= ufshcd_transfer_req_compl(hba);
61648c2ecf20Sopenharmony_ci
61658c2ecf20Sopenharmony_ci	return retval;
61668c2ecf20Sopenharmony_ci}
61678c2ecf20Sopenharmony_ci
61688c2ecf20Sopenharmony_ci/**
61698c2ecf20Sopenharmony_ci * ufshcd_intr - Main interrupt service routine
61708c2ecf20Sopenharmony_ci * @irq: irq number
61718c2ecf20Sopenharmony_ci * @__hba: pointer to adapter instance
61728c2ecf20Sopenharmony_ci *
61738c2ecf20Sopenharmony_ci * Returns
61748c2ecf20Sopenharmony_ci *  IRQ_HANDLED - If interrupt is valid
61758c2ecf20Sopenharmony_ci *  IRQ_NONE    - If invalid interrupt
61768c2ecf20Sopenharmony_ci */
61778c2ecf20Sopenharmony_cistatic irqreturn_t ufshcd_intr(int irq, void *__hba)
61788c2ecf20Sopenharmony_ci{
61798c2ecf20Sopenharmony_ci	u32 intr_status, enabled_intr_status = 0;
61808c2ecf20Sopenharmony_ci	irqreturn_t retval = IRQ_NONE;
61818c2ecf20Sopenharmony_ci	struct ufs_hba *hba = __hba;
61828c2ecf20Sopenharmony_ci	int retries = hba->nutrs;
61838c2ecf20Sopenharmony_ci
61848c2ecf20Sopenharmony_ci	spin_lock(hba->host->host_lock);
61858c2ecf20Sopenharmony_ci	intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS);
61868c2ecf20Sopenharmony_ci	hba->ufs_stats.last_intr_status = intr_status;
61878c2ecf20Sopenharmony_ci	hba->ufs_stats.last_intr_ts = ktime_get();
61888c2ecf20Sopenharmony_ci
61898c2ecf20Sopenharmony_ci	/*
61908c2ecf20Sopenharmony_ci	 * There could be max of hba->nutrs reqs in flight and in worst case
61918c2ecf20Sopenharmony_ci	 * if the reqs get finished 1 by 1 after the interrupt status is
61928c2ecf20Sopenharmony_ci	 * read, make sure we handle them by checking the interrupt status
61938c2ecf20Sopenharmony_ci	 * again in a loop until we process all of the reqs before returning.
61948c2ecf20Sopenharmony_ci	 */
61958c2ecf20Sopenharmony_ci	while (intr_status && retries--) {
61968c2ecf20Sopenharmony_ci		enabled_intr_status =
61978c2ecf20Sopenharmony_ci			intr_status & ufshcd_readl(hba, REG_INTERRUPT_ENABLE);
61988c2ecf20Sopenharmony_ci		if (intr_status)
61998c2ecf20Sopenharmony_ci			ufshcd_writel(hba, intr_status, REG_INTERRUPT_STATUS);
62008c2ecf20Sopenharmony_ci		if (enabled_intr_status)
62018c2ecf20Sopenharmony_ci			retval |= ufshcd_sl_intr(hba, enabled_intr_status);
62028c2ecf20Sopenharmony_ci
62038c2ecf20Sopenharmony_ci		intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS);
62048c2ecf20Sopenharmony_ci	}
62058c2ecf20Sopenharmony_ci
62068c2ecf20Sopenharmony_ci	if (enabled_intr_status && retval == IRQ_NONE &&
62078c2ecf20Sopenharmony_ci				!ufshcd_eh_in_progress(hba)) {
62088c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: Unhandled interrupt 0x%08x (0x%08x, 0x%08x)\n",
62098c2ecf20Sopenharmony_ci					__func__,
62108c2ecf20Sopenharmony_ci					intr_status,
62118c2ecf20Sopenharmony_ci					hba->ufs_stats.last_intr_status,
62128c2ecf20Sopenharmony_ci					enabled_intr_status);
62138c2ecf20Sopenharmony_ci		ufshcd_dump_regs(hba, 0, UFSHCI_REG_SPACE_SIZE, "host_regs: ");
62148c2ecf20Sopenharmony_ci	}
62158c2ecf20Sopenharmony_ci
62168c2ecf20Sopenharmony_ci	spin_unlock(hba->host->host_lock);
62178c2ecf20Sopenharmony_ci	return retval;
62188c2ecf20Sopenharmony_ci}
62198c2ecf20Sopenharmony_ci
62208c2ecf20Sopenharmony_cistatic int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int tag)
62218c2ecf20Sopenharmony_ci{
62228c2ecf20Sopenharmony_ci	int err = 0;
62238c2ecf20Sopenharmony_ci	u32 mask = 1 << tag;
62248c2ecf20Sopenharmony_ci	unsigned long flags;
62258c2ecf20Sopenharmony_ci
62268c2ecf20Sopenharmony_ci	if (!test_bit(tag, &hba->outstanding_tasks))
62278c2ecf20Sopenharmony_ci		goto out;
62288c2ecf20Sopenharmony_ci
62298c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
62308c2ecf20Sopenharmony_ci	ufshcd_utmrl_clear(hba, tag);
62318c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
62328c2ecf20Sopenharmony_ci
62338c2ecf20Sopenharmony_ci	/* poll for max. 1 sec to clear door bell register by h/w */
62348c2ecf20Sopenharmony_ci	err = ufshcd_wait_for_register(hba,
62358c2ecf20Sopenharmony_ci			REG_UTP_TASK_REQ_DOOR_BELL,
62368c2ecf20Sopenharmony_ci			mask, 0, 1000, 1000);
62378c2ecf20Sopenharmony_ciout:
62388c2ecf20Sopenharmony_ci	return err;
62398c2ecf20Sopenharmony_ci}
62408c2ecf20Sopenharmony_ci
62418c2ecf20Sopenharmony_cistatic int __ufshcd_issue_tm_cmd(struct ufs_hba *hba,
62428c2ecf20Sopenharmony_ci		struct utp_task_req_desc *treq, u8 tm_function)
62438c2ecf20Sopenharmony_ci{
62448c2ecf20Sopenharmony_ci	struct request_queue *q = hba->tmf_queue;
62458c2ecf20Sopenharmony_ci	struct Scsi_Host *host = hba->host;
62468c2ecf20Sopenharmony_ci	DECLARE_COMPLETION_ONSTACK(wait);
62478c2ecf20Sopenharmony_ci	struct request *req;
62488c2ecf20Sopenharmony_ci	unsigned long flags;
62498c2ecf20Sopenharmony_ci	int task_tag, err;
62508c2ecf20Sopenharmony_ci
62518c2ecf20Sopenharmony_ci	/*
62528c2ecf20Sopenharmony_ci	 * blk_get_request() is used here only to get a free tag.
62538c2ecf20Sopenharmony_ci	 */
62548c2ecf20Sopenharmony_ci	req = blk_get_request(q, REQ_OP_DRV_OUT, 0);
62558c2ecf20Sopenharmony_ci	if (IS_ERR(req))
62568c2ecf20Sopenharmony_ci		return PTR_ERR(req);
62578c2ecf20Sopenharmony_ci
62588c2ecf20Sopenharmony_ci	req->end_io_data = &wait;
62598c2ecf20Sopenharmony_ci	ufshcd_hold(hba, false);
62608c2ecf20Sopenharmony_ci
62618c2ecf20Sopenharmony_ci	spin_lock_irqsave(host->host_lock, flags);
62628c2ecf20Sopenharmony_ci
62638c2ecf20Sopenharmony_ci	task_tag = req->tag;
62648c2ecf20Sopenharmony_ci	hba->tmf_rqs[req->tag] = req;
62658c2ecf20Sopenharmony_ci	treq->req_header.dword_0 |= cpu_to_be32(task_tag);
62668c2ecf20Sopenharmony_ci
62678c2ecf20Sopenharmony_ci	memcpy(hba->utmrdl_base_addr + task_tag, treq, sizeof(*treq));
62688c2ecf20Sopenharmony_ci	ufshcd_vops_setup_task_mgmt(hba, task_tag, tm_function);
62698c2ecf20Sopenharmony_ci
62708c2ecf20Sopenharmony_ci	/* send command to the controller */
62718c2ecf20Sopenharmony_ci	__set_bit(task_tag, &hba->outstanding_tasks);
62728c2ecf20Sopenharmony_ci
62738c2ecf20Sopenharmony_ci	/* Make sure descriptors are ready before ringing the task doorbell */
62748c2ecf20Sopenharmony_ci	wmb();
62758c2ecf20Sopenharmony_ci
62768c2ecf20Sopenharmony_ci	ufshcd_writel(hba, 1 << task_tag, REG_UTP_TASK_REQ_DOOR_BELL);
62778c2ecf20Sopenharmony_ci	/* Make sure that doorbell is committed immediately */
62788c2ecf20Sopenharmony_ci	wmb();
62798c2ecf20Sopenharmony_ci
62808c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(host->host_lock, flags);
62818c2ecf20Sopenharmony_ci
62828c2ecf20Sopenharmony_ci	ufshcd_add_tm_upiu_trace(hba, task_tag, "tm_send");
62838c2ecf20Sopenharmony_ci
62848c2ecf20Sopenharmony_ci	/* wait until the task management command is completed */
62858c2ecf20Sopenharmony_ci	err = wait_for_completion_io_timeout(&wait,
62868c2ecf20Sopenharmony_ci			msecs_to_jiffies(TM_CMD_TIMEOUT));
62878c2ecf20Sopenharmony_ci	if (!err) {
62888c2ecf20Sopenharmony_ci		ufshcd_add_tm_upiu_trace(hba, task_tag, "tm_complete_err");
62898c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: task management cmd 0x%.2x timed-out\n",
62908c2ecf20Sopenharmony_ci				__func__, tm_function);
62918c2ecf20Sopenharmony_ci		if (ufshcd_clear_tm_cmd(hba, task_tag))
62928c2ecf20Sopenharmony_ci			dev_WARN(hba->dev, "%s: unable to clear tm cmd (slot %d) after timeout\n",
62938c2ecf20Sopenharmony_ci					__func__, task_tag);
62948c2ecf20Sopenharmony_ci		err = -ETIMEDOUT;
62958c2ecf20Sopenharmony_ci	} else {
62968c2ecf20Sopenharmony_ci		err = 0;
62978c2ecf20Sopenharmony_ci		memcpy(treq, hba->utmrdl_base_addr + task_tag, sizeof(*treq));
62988c2ecf20Sopenharmony_ci
62998c2ecf20Sopenharmony_ci		ufshcd_add_tm_upiu_trace(hba, task_tag, "tm_complete");
63008c2ecf20Sopenharmony_ci	}
63018c2ecf20Sopenharmony_ci
63028c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
63038c2ecf20Sopenharmony_ci	hba->tmf_rqs[req->tag] = NULL;
63048c2ecf20Sopenharmony_ci	__clear_bit(task_tag, &hba->outstanding_tasks);
63058c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
63068c2ecf20Sopenharmony_ci
63078c2ecf20Sopenharmony_ci	ufshcd_release(hba);
63088c2ecf20Sopenharmony_ci	blk_put_request(req);
63098c2ecf20Sopenharmony_ci
63108c2ecf20Sopenharmony_ci	return err;
63118c2ecf20Sopenharmony_ci}
63128c2ecf20Sopenharmony_ci
63138c2ecf20Sopenharmony_ci/**
63148c2ecf20Sopenharmony_ci * ufshcd_issue_tm_cmd - issues task management commands to controller
63158c2ecf20Sopenharmony_ci * @hba: per adapter instance
63168c2ecf20Sopenharmony_ci * @lun_id: LUN ID to which TM command is sent
63178c2ecf20Sopenharmony_ci * @task_id: task ID to which the TM command is applicable
63188c2ecf20Sopenharmony_ci * @tm_function: task management function opcode
63198c2ecf20Sopenharmony_ci * @tm_response: task management service response return value
63208c2ecf20Sopenharmony_ci *
63218c2ecf20Sopenharmony_ci * Returns non-zero value on error, zero on success.
63228c2ecf20Sopenharmony_ci */
63238c2ecf20Sopenharmony_cistatic int ufshcd_issue_tm_cmd(struct ufs_hba *hba, int lun_id, int task_id,
63248c2ecf20Sopenharmony_ci		u8 tm_function, u8 *tm_response)
63258c2ecf20Sopenharmony_ci{
63268c2ecf20Sopenharmony_ci	struct utp_task_req_desc treq = { { 0 }, };
63278c2ecf20Sopenharmony_ci	int ocs_value, err;
63288c2ecf20Sopenharmony_ci
63298c2ecf20Sopenharmony_ci	/* Configure task request descriptor */
63308c2ecf20Sopenharmony_ci	treq.header.dword_0 = cpu_to_le32(UTP_REQ_DESC_INT_CMD);
63318c2ecf20Sopenharmony_ci	treq.header.dword_2 = cpu_to_le32(OCS_INVALID_COMMAND_STATUS);
63328c2ecf20Sopenharmony_ci
63338c2ecf20Sopenharmony_ci	/* Configure task request UPIU */
63348c2ecf20Sopenharmony_ci	treq.req_header.dword_0 = cpu_to_be32(lun_id << 8) |
63358c2ecf20Sopenharmony_ci				  cpu_to_be32(UPIU_TRANSACTION_TASK_REQ << 24);
63368c2ecf20Sopenharmony_ci	treq.req_header.dword_1 = cpu_to_be32(tm_function << 16);
63378c2ecf20Sopenharmony_ci
63388c2ecf20Sopenharmony_ci	/*
63398c2ecf20Sopenharmony_ci	 * The host shall provide the same value for LUN field in the basic
63408c2ecf20Sopenharmony_ci	 * header and for Input Parameter.
63418c2ecf20Sopenharmony_ci	 */
63428c2ecf20Sopenharmony_ci	treq.input_param1 = cpu_to_be32(lun_id);
63438c2ecf20Sopenharmony_ci	treq.input_param2 = cpu_to_be32(task_id);
63448c2ecf20Sopenharmony_ci
63458c2ecf20Sopenharmony_ci	err = __ufshcd_issue_tm_cmd(hba, &treq, tm_function);
63468c2ecf20Sopenharmony_ci	if (err == -ETIMEDOUT)
63478c2ecf20Sopenharmony_ci		return err;
63488c2ecf20Sopenharmony_ci
63498c2ecf20Sopenharmony_ci	ocs_value = le32_to_cpu(treq.header.dword_2) & MASK_OCS;
63508c2ecf20Sopenharmony_ci	if (ocs_value != OCS_SUCCESS)
63518c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: failed, ocs = 0x%x\n",
63528c2ecf20Sopenharmony_ci				__func__, ocs_value);
63538c2ecf20Sopenharmony_ci	else if (tm_response)
63548c2ecf20Sopenharmony_ci		*tm_response = be32_to_cpu(treq.output_param1) &
63558c2ecf20Sopenharmony_ci				MASK_TM_SERVICE_RESP;
63568c2ecf20Sopenharmony_ci	return err;
63578c2ecf20Sopenharmony_ci}
63588c2ecf20Sopenharmony_ci
63598c2ecf20Sopenharmony_ci/**
63608c2ecf20Sopenharmony_ci * ufshcd_issue_devman_upiu_cmd - API for sending "utrd" type requests
63618c2ecf20Sopenharmony_ci * @hba:	per-adapter instance
63628c2ecf20Sopenharmony_ci * @req_upiu:	upiu request
63638c2ecf20Sopenharmony_ci * @rsp_upiu:	upiu reply
63648c2ecf20Sopenharmony_ci * @desc_buff:	pointer to descriptor buffer, NULL if NA
63658c2ecf20Sopenharmony_ci * @buff_len:	descriptor size, 0 if NA
63668c2ecf20Sopenharmony_ci * @cmd_type:	specifies the type (NOP, Query...)
63678c2ecf20Sopenharmony_ci * @desc_op:	descriptor operation
63688c2ecf20Sopenharmony_ci *
63698c2ecf20Sopenharmony_ci * Those type of requests uses UTP Transfer Request Descriptor - utrd.
63708c2ecf20Sopenharmony_ci * Therefore, it "rides" the device management infrastructure: uses its tag and
63718c2ecf20Sopenharmony_ci * tasks work queues.
63728c2ecf20Sopenharmony_ci *
63738c2ecf20Sopenharmony_ci * Since there is only one available tag for device management commands,
63748c2ecf20Sopenharmony_ci * the caller is expected to hold the hba->dev_cmd.lock mutex.
63758c2ecf20Sopenharmony_ci */
63768c2ecf20Sopenharmony_cistatic int ufshcd_issue_devman_upiu_cmd(struct ufs_hba *hba,
63778c2ecf20Sopenharmony_ci					struct utp_upiu_req *req_upiu,
63788c2ecf20Sopenharmony_ci					struct utp_upiu_req *rsp_upiu,
63798c2ecf20Sopenharmony_ci					u8 *desc_buff, int *buff_len,
63808c2ecf20Sopenharmony_ci					enum dev_cmd_type cmd_type,
63818c2ecf20Sopenharmony_ci					enum query_opcode desc_op)
63828c2ecf20Sopenharmony_ci{
63838c2ecf20Sopenharmony_ci	struct request_queue *q = hba->cmd_queue;
63848c2ecf20Sopenharmony_ci	struct request *req;
63858c2ecf20Sopenharmony_ci	struct ufshcd_lrb *lrbp;
63868c2ecf20Sopenharmony_ci	int err = 0;
63878c2ecf20Sopenharmony_ci	int tag;
63888c2ecf20Sopenharmony_ci	struct completion wait;
63898c2ecf20Sopenharmony_ci	unsigned long flags;
63908c2ecf20Sopenharmony_ci	u8 upiu_flags;
63918c2ecf20Sopenharmony_ci
63928c2ecf20Sopenharmony_ci	down_read(&hba->clk_scaling_lock);
63938c2ecf20Sopenharmony_ci
63948c2ecf20Sopenharmony_ci	req = blk_get_request(q, REQ_OP_DRV_OUT, 0);
63958c2ecf20Sopenharmony_ci	if (IS_ERR(req)) {
63968c2ecf20Sopenharmony_ci		err = PTR_ERR(req);
63978c2ecf20Sopenharmony_ci		goto out_unlock;
63988c2ecf20Sopenharmony_ci	}
63998c2ecf20Sopenharmony_ci	tag = req->tag;
64008c2ecf20Sopenharmony_ci	WARN_ON_ONCE(!ufshcd_valid_tag(hba, tag));
64018c2ecf20Sopenharmony_ci
64028c2ecf20Sopenharmony_ci	init_completion(&wait);
64038c2ecf20Sopenharmony_ci	lrbp = &hba->lrb[tag];
64048c2ecf20Sopenharmony_ci	WARN_ON(lrbp->cmd);
64058c2ecf20Sopenharmony_ci
64068c2ecf20Sopenharmony_ci	lrbp->cmd = NULL;
64078c2ecf20Sopenharmony_ci	lrbp->sense_bufflen = 0;
64088c2ecf20Sopenharmony_ci	lrbp->sense_buffer = NULL;
64098c2ecf20Sopenharmony_ci	lrbp->task_tag = tag;
64108c2ecf20Sopenharmony_ci	lrbp->lun = 0;
64118c2ecf20Sopenharmony_ci	lrbp->intr_cmd = true;
64128c2ecf20Sopenharmony_ci	ufshcd_prepare_lrbp_crypto(NULL, lrbp);
64138c2ecf20Sopenharmony_ci	hba->dev_cmd.type = cmd_type;
64148c2ecf20Sopenharmony_ci
64158c2ecf20Sopenharmony_ci	switch (hba->ufs_version) {
64168c2ecf20Sopenharmony_ci	case UFSHCI_VERSION_10:
64178c2ecf20Sopenharmony_ci	case UFSHCI_VERSION_11:
64188c2ecf20Sopenharmony_ci		lrbp->command_type = UTP_CMD_TYPE_DEV_MANAGE;
64198c2ecf20Sopenharmony_ci		break;
64208c2ecf20Sopenharmony_ci	default:
64218c2ecf20Sopenharmony_ci		lrbp->command_type = UTP_CMD_TYPE_UFS_STORAGE;
64228c2ecf20Sopenharmony_ci		break;
64238c2ecf20Sopenharmony_ci	}
64248c2ecf20Sopenharmony_ci
64258c2ecf20Sopenharmony_ci	/* update the task tag in the request upiu */
64268c2ecf20Sopenharmony_ci	req_upiu->header.dword_0 |= cpu_to_be32(tag);
64278c2ecf20Sopenharmony_ci
64288c2ecf20Sopenharmony_ci	ufshcd_prepare_req_desc_hdr(lrbp, &upiu_flags, DMA_NONE);
64298c2ecf20Sopenharmony_ci
64308c2ecf20Sopenharmony_ci	/* just copy the upiu request as it is */
64318c2ecf20Sopenharmony_ci	memcpy(lrbp->ucd_req_ptr, req_upiu, sizeof(*lrbp->ucd_req_ptr));
64328c2ecf20Sopenharmony_ci	if (desc_buff && desc_op == UPIU_QUERY_OPCODE_WRITE_DESC) {
64338c2ecf20Sopenharmony_ci		/* The Data Segment Area is optional depending upon the query
64348c2ecf20Sopenharmony_ci		 * function value. for WRITE DESCRIPTOR, the data segment
64358c2ecf20Sopenharmony_ci		 * follows right after the tsf.
64368c2ecf20Sopenharmony_ci		 */
64378c2ecf20Sopenharmony_ci		memcpy(lrbp->ucd_req_ptr + 1, desc_buff, *buff_len);
64388c2ecf20Sopenharmony_ci		*buff_len = 0;
64398c2ecf20Sopenharmony_ci	}
64408c2ecf20Sopenharmony_ci
64418c2ecf20Sopenharmony_ci	memset(lrbp->ucd_rsp_ptr, 0, sizeof(struct utp_upiu_rsp));
64428c2ecf20Sopenharmony_ci
64438c2ecf20Sopenharmony_ci	hba->dev_cmd.complete = &wait;
64448c2ecf20Sopenharmony_ci
64458c2ecf20Sopenharmony_ci	/* Make sure descriptors are ready before ringing the doorbell */
64468c2ecf20Sopenharmony_ci	wmb();
64478c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
64488c2ecf20Sopenharmony_ci	ufshcd_send_command(hba, tag);
64498c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
64508c2ecf20Sopenharmony_ci
64518c2ecf20Sopenharmony_ci	/*
64528c2ecf20Sopenharmony_ci	 * ignore the returning value here - ufshcd_check_query_response is
64538c2ecf20Sopenharmony_ci	 * bound to fail since dev_cmd.query and dev_cmd.type were left empty.
64548c2ecf20Sopenharmony_ci	 * read the response directly ignoring all errors.
64558c2ecf20Sopenharmony_ci	 */
64568c2ecf20Sopenharmony_ci	ufshcd_wait_for_dev_cmd(hba, lrbp, QUERY_REQ_TIMEOUT);
64578c2ecf20Sopenharmony_ci
64588c2ecf20Sopenharmony_ci	/* just copy the upiu response as it is */
64598c2ecf20Sopenharmony_ci	memcpy(rsp_upiu, lrbp->ucd_rsp_ptr, sizeof(*rsp_upiu));
64608c2ecf20Sopenharmony_ci	if (desc_buff && desc_op == UPIU_QUERY_OPCODE_READ_DESC) {
64618c2ecf20Sopenharmony_ci		u8 *descp = (u8 *)lrbp->ucd_rsp_ptr + sizeof(*rsp_upiu);
64628c2ecf20Sopenharmony_ci		u16 resp_len = be32_to_cpu(lrbp->ucd_rsp_ptr->header.dword_2) &
64638c2ecf20Sopenharmony_ci			       MASK_QUERY_DATA_SEG_LEN;
64648c2ecf20Sopenharmony_ci
64658c2ecf20Sopenharmony_ci		if (*buff_len >= resp_len) {
64668c2ecf20Sopenharmony_ci			memcpy(desc_buff, descp, resp_len);
64678c2ecf20Sopenharmony_ci			*buff_len = resp_len;
64688c2ecf20Sopenharmony_ci		} else {
64698c2ecf20Sopenharmony_ci			dev_warn(hba->dev,
64708c2ecf20Sopenharmony_ci				 "%s: rsp size %d is bigger than buffer size %d",
64718c2ecf20Sopenharmony_ci				 __func__, resp_len, *buff_len);
64728c2ecf20Sopenharmony_ci			*buff_len = 0;
64738c2ecf20Sopenharmony_ci			err = -EINVAL;
64748c2ecf20Sopenharmony_ci		}
64758c2ecf20Sopenharmony_ci	}
64768c2ecf20Sopenharmony_ci
64778c2ecf20Sopenharmony_ci	blk_put_request(req);
64788c2ecf20Sopenharmony_ciout_unlock:
64798c2ecf20Sopenharmony_ci	up_read(&hba->clk_scaling_lock);
64808c2ecf20Sopenharmony_ci	return err;
64818c2ecf20Sopenharmony_ci}
64828c2ecf20Sopenharmony_ci
64838c2ecf20Sopenharmony_ci/**
64848c2ecf20Sopenharmony_ci * ufshcd_exec_raw_upiu_cmd - API function for sending raw upiu commands
64858c2ecf20Sopenharmony_ci * @hba:	per-adapter instance
64868c2ecf20Sopenharmony_ci * @req_upiu:	upiu request
64878c2ecf20Sopenharmony_ci * @rsp_upiu:	upiu reply - only 8 DW as we do not support scsi commands
64888c2ecf20Sopenharmony_ci * @msgcode:	message code, one of UPIU Transaction Codes Initiator to Target
64898c2ecf20Sopenharmony_ci * @desc_buff:	pointer to descriptor buffer, NULL if NA
64908c2ecf20Sopenharmony_ci * @buff_len:	descriptor size, 0 if NA
64918c2ecf20Sopenharmony_ci * @desc_op:	descriptor operation
64928c2ecf20Sopenharmony_ci *
64938c2ecf20Sopenharmony_ci * Supports UTP Transfer requests (nop and query), and UTP Task
64948c2ecf20Sopenharmony_ci * Management requests.
64958c2ecf20Sopenharmony_ci * It is up to the caller to fill the upiu conent properly, as it will
64968c2ecf20Sopenharmony_ci * be copied without any further input validations.
64978c2ecf20Sopenharmony_ci */
64988c2ecf20Sopenharmony_ciint ufshcd_exec_raw_upiu_cmd(struct ufs_hba *hba,
64998c2ecf20Sopenharmony_ci			     struct utp_upiu_req *req_upiu,
65008c2ecf20Sopenharmony_ci			     struct utp_upiu_req *rsp_upiu,
65018c2ecf20Sopenharmony_ci			     int msgcode,
65028c2ecf20Sopenharmony_ci			     u8 *desc_buff, int *buff_len,
65038c2ecf20Sopenharmony_ci			     enum query_opcode desc_op)
65048c2ecf20Sopenharmony_ci{
65058c2ecf20Sopenharmony_ci	int err;
65068c2ecf20Sopenharmony_ci	enum dev_cmd_type cmd_type = DEV_CMD_TYPE_QUERY;
65078c2ecf20Sopenharmony_ci	struct utp_task_req_desc treq = { { 0 }, };
65088c2ecf20Sopenharmony_ci	int ocs_value;
65098c2ecf20Sopenharmony_ci	u8 tm_f = be32_to_cpu(req_upiu->header.dword_1) >> 16 & MASK_TM_FUNC;
65108c2ecf20Sopenharmony_ci
65118c2ecf20Sopenharmony_ci	switch (msgcode) {
65128c2ecf20Sopenharmony_ci	case UPIU_TRANSACTION_NOP_OUT:
65138c2ecf20Sopenharmony_ci		cmd_type = DEV_CMD_TYPE_NOP;
65148c2ecf20Sopenharmony_ci		fallthrough;
65158c2ecf20Sopenharmony_ci	case UPIU_TRANSACTION_QUERY_REQ:
65168c2ecf20Sopenharmony_ci		ufshcd_hold(hba, false);
65178c2ecf20Sopenharmony_ci		mutex_lock(&hba->dev_cmd.lock);
65188c2ecf20Sopenharmony_ci		err = ufshcd_issue_devman_upiu_cmd(hba, req_upiu, rsp_upiu,
65198c2ecf20Sopenharmony_ci						   desc_buff, buff_len,
65208c2ecf20Sopenharmony_ci						   cmd_type, desc_op);
65218c2ecf20Sopenharmony_ci		mutex_unlock(&hba->dev_cmd.lock);
65228c2ecf20Sopenharmony_ci		ufshcd_release(hba);
65238c2ecf20Sopenharmony_ci
65248c2ecf20Sopenharmony_ci		break;
65258c2ecf20Sopenharmony_ci	case UPIU_TRANSACTION_TASK_REQ:
65268c2ecf20Sopenharmony_ci		treq.header.dword_0 = cpu_to_le32(UTP_REQ_DESC_INT_CMD);
65278c2ecf20Sopenharmony_ci		treq.header.dword_2 = cpu_to_le32(OCS_INVALID_COMMAND_STATUS);
65288c2ecf20Sopenharmony_ci
65298c2ecf20Sopenharmony_ci		memcpy(&treq.req_header, req_upiu, sizeof(*req_upiu));
65308c2ecf20Sopenharmony_ci
65318c2ecf20Sopenharmony_ci		err = __ufshcd_issue_tm_cmd(hba, &treq, tm_f);
65328c2ecf20Sopenharmony_ci		if (err == -ETIMEDOUT)
65338c2ecf20Sopenharmony_ci			break;
65348c2ecf20Sopenharmony_ci
65358c2ecf20Sopenharmony_ci		ocs_value = le32_to_cpu(treq.header.dword_2) & MASK_OCS;
65368c2ecf20Sopenharmony_ci		if (ocs_value != OCS_SUCCESS) {
65378c2ecf20Sopenharmony_ci			dev_err(hba->dev, "%s: failed, ocs = 0x%x\n", __func__,
65388c2ecf20Sopenharmony_ci				ocs_value);
65398c2ecf20Sopenharmony_ci			break;
65408c2ecf20Sopenharmony_ci		}
65418c2ecf20Sopenharmony_ci
65428c2ecf20Sopenharmony_ci		memcpy(rsp_upiu, &treq.rsp_header, sizeof(*rsp_upiu));
65438c2ecf20Sopenharmony_ci
65448c2ecf20Sopenharmony_ci		break;
65458c2ecf20Sopenharmony_ci	default:
65468c2ecf20Sopenharmony_ci		err = -EINVAL;
65478c2ecf20Sopenharmony_ci
65488c2ecf20Sopenharmony_ci		break;
65498c2ecf20Sopenharmony_ci	}
65508c2ecf20Sopenharmony_ci
65518c2ecf20Sopenharmony_ci	return err;
65528c2ecf20Sopenharmony_ci}
65538c2ecf20Sopenharmony_ci
65548c2ecf20Sopenharmony_ci/**
65558c2ecf20Sopenharmony_ci * ufshcd_eh_device_reset_handler - device reset handler registered to
65568c2ecf20Sopenharmony_ci *                                    scsi layer.
65578c2ecf20Sopenharmony_ci * @cmd: SCSI command pointer
65588c2ecf20Sopenharmony_ci *
65598c2ecf20Sopenharmony_ci * Returns SUCCESS/FAILED
65608c2ecf20Sopenharmony_ci */
65618c2ecf20Sopenharmony_cistatic int ufshcd_eh_device_reset_handler(struct scsi_cmnd *cmd)
65628c2ecf20Sopenharmony_ci{
65638c2ecf20Sopenharmony_ci	struct Scsi_Host *host;
65648c2ecf20Sopenharmony_ci	struct ufs_hba *hba;
65658c2ecf20Sopenharmony_ci	u32 pos;
65668c2ecf20Sopenharmony_ci	int err;
65678c2ecf20Sopenharmony_ci	u8 resp = 0xF, lun;
65688c2ecf20Sopenharmony_ci	unsigned long flags;
65698c2ecf20Sopenharmony_ci
65708c2ecf20Sopenharmony_ci	host = cmd->device->host;
65718c2ecf20Sopenharmony_ci	hba = shost_priv(host);
65728c2ecf20Sopenharmony_ci
65738c2ecf20Sopenharmony_ci	lun = ufshcd_scsi_to_upiu_lun(cmd->device->lun);
65748c2ecf20Sopenharmony_ci	err = ufshcd_issue_tm_cmd(hba, lun, 0, UFS_LOGICAL_RESET, &resp);
65758c2ecf20Sopenharmony_ci	if (err || resp != UPIU_TASK_MANAGEMENT_FUNC_COMPL) {
65768c2ecf20Sopenharmony_ci		if (!err)
65778c2ecf20Sopenharmony_ci			err = resp;
65788c2ecf20Sopenharmony_ci		goto out;
65798c2ecf20Sopenharmony_ci	}
65808c2ecf20Sopenharmony_ci
65818c2ecf20Sopenharmony_ci	/* clear the commands that were pending for corresponding LUN */
65828c2ecf20Sopenharmony_ci	for_each_set_bit(pos, &hba->outstanding_reqs, hba->nutrs) {
65838c2ecf20Sopenharmony_ci		if (hba->lrb[pos].lun == lun) {
65848c2ecf20Sopenharmony_ci			err = ufshcd_clear_cmd(hba, pos);
65858c2ecf20Sopenharmony_ci			if (err)
65868c2ecf20Sopenharmony_ci				break;
65878c2ecf20Sopenharmony_ci		}
65888c2ecf20Sopenharmony_ci	}
65898c2ecf20Sopenharmony_ci	spin_lock_irqsave(host->host_lock, flags);
65908c2ecf20Sopenharmony_ci	ufshcd_transfer_req_compl(hba);
65918c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(host->host_lock, flags);
65928c2ecf20Sopenharmony_ci
65938c2ecf20Sopenharmony_ciout:
65948c2ecf20Sopenharmony_ci	hba->req_abort_count = 0;
65958c2ecf20Sopenharmony_ci	ufshcd_update_reg_hist(&hba->ufs_stats.dev_reset, (u32)err);
65968c2ecf20Sopenharmony_ci	if (!err) {
65978c2ecf20Sopenharmony_ci		err = SUCCESS;
65988c2ecf20Sopenharmony_ci	} else {
65998c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: failed with err %d\n", __func__, err);
66008c2ecf20Sopenharmony_ci		err = FAILED;
66018c2ecf20Sopenharmony_ci	}
66028c2ecf20Sopenharmony_ci	return err;
66038c2ecf20Sopenharmony_ci}
66048c2ecf20Sopenharmony_ci
66058c2ecf20Sopenharmony_cistatic void ufshcd_set_req_abort_skip(struct ufs_hba *hba, unsigned long bitmap)
66068c2ecf20Sopenharmony_ci{
66078c2ecf20Sopenharmony_ci	struct ufshcd_lrb *lrbp;
66088c2ecf20Sopenharmony_ci	int tag;
66098c2ecf20Sopenharmony_ci
66108c2ecf20Sopenharmony_ci	for_each_set_bit(tag, &bitmap, hba->nutrs) {
66118c2ecf20Sopenharmony_ci		lrbp = &hba->lrb[tag];
66128c2ecf20Sopenharmony_ci		lrbp->req_abort_skip = true;
66138c2ecf20Sopenharmony_ci	}
66148c2ecf20Sopenharmony_ci}
66158c2ecf20Sopenharmony_ci
66168c2ecf20Sopenharmony_ci/**
66178c2ecf20Sopenharmony_ci * ufshcd_try_to_abort_task - abort a specific task
66188c2ecf20Sopenharmony_ci * @cmd: SCSI command pointer
66198c2ecf20Sopenharmony_ci *
66208c2ecf20Sopenharmony_ci * Abort the pending command in device by sending UFS_ABORT_TASK task management
66218c2ecf20Sopenharmony_ci * command, and in host controller by clearing the door-bell register. There can
66228c2ecf20Sopenharmony_ci * be race between controller sending the command to the device while abort is
66238c2ecf20Sopenharmony_ci * issued. To avoid that, first issue UFS_QUERY_TASK to check if the command is
66248c2ecf20Sopenharmony_ci * really issued and then try to abort it.
66258c2ecf20Sopenharmony_ci *
66268c2ecf20Sopenharmony_ci * Returns zero on success, non-zero on failure
66278c2ecf20Sopenharmony_ci */
66288c2ecf20Sopenharmony_cistatic int ufshcd_try_to_abort_task(struct ufs_hba *hba, int tag)
66298c2ecf20Sopenharmony_ci{
66308c2ecf20Sopenharmony_ci	struct ufshcd_lrb *lrbp = &hba->lrb[tag];
66318c2ecf20Sopenharmony_ci	int err = 0;
66328c2ecf20Sopenharmony_ci	int poll_cnt;
66338c2ecf20Sopenharmony_ci	u8 resp = 0xF;
66348c2ecf20Sopenharmony_ci	u32 reg;
66358c2ecf20Sopenharmony_ci
66368c2ecf20Sopenharmony_ci	for (poll_cnt = 100; poll_cnt; poll_cnt--) {
66378c2ecf20Sopenharmony_ci		err = ufshcd_issue_tm_cmd(hba, lrbp->lun, lrbp->task_tag,
66388c2ecf20Sopenharmony_ci				UFS_QUERY_TASK, &resp);
66398c2ecf20Sopenharmony_ci		if (!err && resp == UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED) {
66408c2ecf20Sopenharmony_ci			/* cmd pending in the device */
66418c2ecf20Sopenharmony_ci			dev_err(hba->dev, "%s: cmd pending in the device. tag = %d\n",
66428c2ecf20Sopenharmony_ci				__func__, tag);
66438c2ecf20Sopenharmony_ci			break;
66448c2ecf20Sopenharmony_ci		} else if (!err && resp == UPIU_TASK_MANAGEMENT_FUNC_COMPL) {
66458c2ecf20Sopenharmony_ci			/*
66468c2ecf20Sopenharmony_ci			 * cmd not pending in the device, check if it is
66478c2ecf20Sopenharmony_ci			 * in transition.
66488c2ecf20Sopenharmony_ci			 */
66498c2ecf20Sopenharmony_ci			dev_err(hba->dev, "%s: cmd at tag %d not pending in the device.\n",
66508c2ecf20Sopenharmony_ci				__func__, tag);
66518c2ecf20Sopenharmony_ci			reg = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
66528c2ecf20Sopenharmony_ci			if (reg & (1 << tag)) {
66538c2ecf20Sopenharmony_ci				/* sleep for max. 200us to stabilize */
66548c2ecf20Sopenharmony_ci				usleep_range(100, 200);
66558c2ecf20Sopenharmony_ci				continue;
66568c2ecf20Sopenharmony_ci			}
66578c2ecf20Sopenharmony_ci			/* command completed already */
66588c2ecf20Sopenharmony_ci			dev_err(hba->dev, "%s: cmd at tag %d successfully cleared from DB.\n",
66598c2ecf20Sopenharmony_ci				__func__, tag);
66608c2ecf20Sopenharmony_ci			goto out;
66618c2ecf20Sopenharmony_ci		} else {
66628c2ecf20Sopenharmony_ci			dev_err(hba->dev,
66638c2ecf20Sopenharmony_ci				"%s: no response from device. tag = %d, err %d\n",
66648c2ecf20Sopenharmony_ci				__func__, tag, err);
66658c2ecf20Sopenharmony_ci			if (!err)
66668c2ecf20Sopenharmony_ci				err = resp; /* service response error */
66678c2ecf20Sopenharmony_ci			goto out;
66688c2ecf20Sopenharmony_ci		}
66698c2ecf20Sopenharmony_ci	}
66708c2ecf20Sopenharmony_ci
66718c2ecf20Sopenharmony_ci	if (!poll_cnt) {
66728c2ecf20Sopenharmony_ci		err = -EBUSY;
66738c2ecf20Sopenharmony_ci		goto out;
66748c2ecf20Sopenharmony_ci	}
66758c2ecf20Sopenharmony_ci
66768c2ecf20Sopenharmony_ci	err = ufshcd_issue_tm_cmd(hba, lrbp->lun, lrbp->task_tag,
66778c2ecf20Sopenharmony_ci			UFS_ABORT_TASK, &resp);
66788c2ecf20Sopenharmony_ci	if (err || resp != UPIU_TASK_MANAGEMENT_FUNC_COMPL) {
66798c2ecf20Sopenharmony_ci		if (!err) {
66808c2ecf20Sopenharmony_ci			err = resp; /* service response error */
66818c2ecf20Sopenharmony_ci			dev_err(hba->dev, "%s: issued. tag = %d, err %d\n",
66828c2ecf20Sopenharmony_ci				__func__, tag, err);
66838c2ecf20Sopenharmony_ci		}
66848c2ecf20Sopenharmony_ci		goto out;
66858c2ecf20Sopenharmony_ci	}
66868c2ecf20Sopenharmony_ci
66878c2ecf20Sopenharmony_ci	err = ufshcd_clear_cmd(hba, tag);
66888c2ecf20Sopenharmony_ci	if (err)
66898c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: Failed clearing cmd at tag %d, err %d\n",
66908c2ecf20Sopenharmony_ci			__func__, tag, err);
66918c2ecf20Sopenharmony_ci
66928c2ecf20Sopenharmony_ciout:
66938c2ecf20Sopenharmony_ci	return err;
66948c2ecf20Sopenharmony_ci}
66958c2ecf20Sopenharmony_ci
66968c2ecf20Sopenharmony_ci/**
66978c2ecf20Sopenharmony_ci * ufshcd_abort - scsi host template eh_abort_handler callback
66988c2ecf20Sopenharmony_ci * @cmd: SCSI command pointer
66998c2ecf20Sopenharmony_ci *
67008c2ecf20Sopenharmony_ci * Returns SUCCESS/FAILED
67018c2ecf20Sopenharmony_ci */
67028c2ecf20Sopenharmony_cistatic int ufshcd_abort(struct scsi_cmnd *cmd)
67038c2ecf20Sopenharmony_ci{
67048c2ecf20Sopenharmony_ci	struct Scsi_Host *host;
67058c2ecf20Sopenharmony_ci	struct ufs_hba *hba;
67068c2ecf20Sopenharmony_ci	unsigned long flags;
67078c2ecf20Sopenharmony_ci	unsigned int tag;
67088c2ecf20Sopenharmony_ci	int err = 0;
67098c2ecf20Sopenharmony_ci	struct ufshcd_lrb *lrbp;
67108c2ecf20Sopenharmony_ci	u32 reg;
67118c2ecf20Sopenharmony_ci
67128c2ecf20Sopenharmony_ci	host = cmd->device->host;
67138c2ecf20Sopenharmony_ci	hba = shost_priv(host);
67148c2ecf20Sopenharmony_ci	tag = cmd->request->tag;
67158c2ecf20Sopenharmony_ci	lrbp = &hba->lrb[tag];
67168c2ecf20Sopenharmony_ci	if (!ufshcd_valid_tag(hba, tag)) {
67178c2ecf20Sopenharmony_ci		dev_err(hba->dev,
67188c2ecf20Sopenharmony_ci			"%s: invalid command tag %d: cmd=0x%p, cmd->request=0x%p",
67198c2ecf20Sopenharmony_ci			__func__, tag, cmd, cmd->request);
67208c2ecf20Sopenharmony_ci		BUG();
67218c2ecf20Sopenharmony_ci	}
67228c2ecf20Sopenharmony_ci
67238c2ecf20Sopenharmony_ci	/*
67248c2ecf20Sopenharmony_ci	 * Task abort to the device W-LUN is illegal. When this command
67258c2ecf20Sopenharmony_ci	 * will fail, due to spec violation, scsi err handling next step
67268c2ecf20Sopenharmony_ci	 * will be to send LU reset which, again, is a spec violation.
67278c2ecf20Sopenharmony_ci	 * To avoid these unnecessary/illegal step we skip to the last error
67288c2ecf20Sopenharmony_ci	 * handling stage: reset and restore.
67298c2ecf20Sopenharmony_ci	 */
67308c2ecf20Sopenharmony_ci	if (lrbp->lun == UFS_UPIU_UFS_DEVICE_WLUN)
67318c2ecf20Sopenharmony_ci		return ufshcd_eh_host_reset_handler(cmd);
67328c2ecf20Sopenharmony_ci
67338c2ecf20Sopenharmony_ci	ufshcd_hold(hba, false);
67348c2ecf20Sopenharmony_ci	reg = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
67358c2ecf20Sopenharmony_ci	/* If command is already aborted/completed, return SUCCESS */
67368c2ecf20Sopenharmony_ci	if (!(test_bit(tag, &hba->outstanding_reqs))) {
67378c2ecf20Sopenharmony_ci		dev_err(hba->dev,
67388c2ecf20Sopenharmony_ci			"%s: cmd at tag %d already completed, outstanding=0x%lx, doorbell=0x%x\n",
67398c2ecf20Sopenharmony_ci			__func__, tag, hba->outstanding_reqs, reg);
67408c2ecf20Sopenharmony_ci		goto out;
67418c2ecf20Sopenharmony_ci	}
67428c2ecf20Sopenharmony_ci
67438c2ecf20Sopenharmony_ci	/* Print Transfer Request of aborted task */
67448c2ecf20Sopenharmony_ci	dev_info(hba->dev, "%s: Device abort task at tag %d\n", __func__, tag);
67458c2ecf20Sopenharmony_ci
67468c2ecf20Sopenharmony_ci	/*
67478c2ecf20Sopenharmony_ci	 * Print detailed info about aborted request.
67488c2ecf20Sopenharmony_ci	 * As more than one request might get aborted at the same time,
67498c2ecf20Sopenharmony_ci	 * print full information only for the first aborted request in order
67508c2ecf20Sopenharmony_ci	 * to reduce repeated printouts. For other aborted requests only print
67518c2ecf20Sopenharmony_ci	 * basic details.
67528c2ecf20Sopenharmony_ci	 */
67538c2ecf20Sopenharmony_ci	scsi_print_command(hba->lrb[tag].cmd);
67548c2ecf20Sopenharmony_ci	if (!hba->req_abort_count) {
67558c2ecf20Sopenharmony_ci		ufshcd_update_reg_hist(&hba->ufs_stats.task_abort, 0);
67568c2ecf20Sopenharmony_ci		ufshcd_print_host_regs(hba);
67578c2ecf20Sopenharmony_ci		ufshcd_print_host_state(hba);
67588c2ecf20Sopenharmony_ci		ufshcd_print_pwr_info(hba);
67598c2ecf20Sopenharmony_ci		ufshcd_print_trs(hba, 1 << tag, true);
67608c2ecf20Sopenharmony_ci	} else {
67618c2ecf20Sopenharmony_ci		ufshcd_print_trs(hba, 1 << tag, false);
67628c2ecf20Sopenharmony_ci	}
67638c2ecf20Sopenharmony_ci	hba->req_abort_count++;
67648c2ecf20Sopenharmony_ci
67658c2ecf20Sopenharmony_ci	if (!(reg & (1 << tag))) {
67668c2ecf20Sopenharmony_ci		dev_err(hba->dev,
67678c2ecf20Sopenharmony_ci		"%s: cmd was completed, but without a notifying intr, tag = %d",
67688c2ecf20Sopenharmony_ci		__func__, tag);
67698c2ecf20Sopenharmony_ci		goto cleanup;
67708c2ecf20Sopenharmony_ci	}
67718c2ecf20Sopenharmony_ci
67728c2ecf20Sopenharmony_ci	/* Skip task abort in case previous aborts failed and report failure */
67738c2ecf20Sopenharmony_ci	if (lrbp->req_abort_skip)
67748c2ecf20Sopenharmony_ci		err = -EIO;
67758c2ecf20Sopenharmony_ci	else
67768c2ecf20Sopenharmony_ci		err = ufshcd_try_to_abort_task(hba, tag);
67778c2ecf20Sopenharmony_ci
67788c2ecf20Sopenharmony_ci	if (!err) {
67798c2ecf20Sopenharmony_cicleanup:
67808c2ecf20Sopenharmony_ci		spin_lock_irqsave(host->host_lock, flags);
67818c2ecf20Sopenharmony_ci		__ufshcd_transfer_req_compl(hba, (1UL << tag));
67828c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(host->host_lock, flags);
67838c2ecf20Sopenharmony_ciout:
67848c2ecf20Sopenharmony_ci		err = SUCCESS;
67858c2ecf20Sopenharmony_ci	} else {
67868c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: failed with err %d\n", __func__, err);
67878c2ecf20Sopenharmony_ci		ufshcd_set_req_abort_skip(hba, hba->outstanding_reqs);
67888c2ecf20Sopenharmony_ci		err = FAILED;
67898c2ecf20Sopenharmony_ci	}
67908c2ecf20Sopenharmony_ci
67918c2ecf20Sopenharmony_ci	/*
67928c2ecf20Sopenharmony_ci	 * This ufshcd_release() corresponds to the original scsi cmd that got
67938c2ecf20Sopenharmony_ci	 * aborted here (as we won't get any IRQ for it).
67948c2ecf20Sopenharmony_ci	 */
67958c2ecf20Sopenharmony_ci	ufshcd_release(hba);
67968c2ecf20Sopenharmony_ci	return err;
67978c2ecf20Sopenharmony_ci}
67988c2ecf20Sopenharmony_ci
67998c2ecf20Sopenharmony_ci/**
68008c2ecf20Sopenharmony_ci * ufshcd_host_reset_and_restore - reset and restore host controller
68018c2ecf20Sopenharmony_ci * @hba: per-adapter instance
68028c2ecf20Sopenharmony_ci *
68038c2ecf20Sopenharmony_ci * Note that host controller reset may issue DME_RESET to
68048c2ecf20Sopenharmony_ci * local and remote (device) Uni-Pro stack and the attributes
68058c2ecf20Sopenharmony_ci * are reset to default state.
68068c2ecf20Sopenharmony_ci *
68078c2ecf20Sopenharmony_ci * Returns zero on success, non-zero on failure
68088c2ecf20Sopenharmony_ci */
68098c2ecf20Sopenharmony_cistatic int ufshcd_host_reset_and_restore(struct ufs_hba *hba)
68108c2ecf20Sopenharmony_ci{
68118c2ecf20Sopenharmony_ci	int err;
68128c2ecf20Sopenharmony_ci	unsigned long flags;
68138c2ecf20Sopenharmony_ci
68148c2ecf20Sopenharmony_ci	/*
68158c2ecf20Sopenharmony_ci	 * Stop the host controller and complete the requests
68168c2ecf20Sopenharmony_ci	 * cleared by h/w
68178c2ecf20Sopenharmony_ci	 */
68188c2ecf20Sopenharmony_ci	ufshcd_hba_stop(hba);
68198c2ecf20Sopenharmony_ci
68208c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
68218c2ecf20Sopenharmony_ci	hba->silence_err_logs = true;
68228c2ecf20Sopenharmony_ci	ufshcd_complete_requests(hba);
68238c2ecf20Sopenharmony_ci	hba->silence_err_logs = false;
68248c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
68258c2ecf20Sopenharmony_ci
68268c2ecf20Sopenharmony_ci	/* scale up clocks to max frequency before full reinitialization */
68278c2ecf20Sopenharmony_ci	ufshcd_set_clk_freq(hba, true);
68288c2ecf20Sopenharmony_ci
68298c2ecf20Sopenharmony_ci	err = ufshcd_hba_enable(hba);
68308c2ecf20Sopenharmony_ci	if (err)
68318c2ecf20Sopenharmony_ci		goto out;
68328c2ecf20Sopenharmony_ci
68338c2ecf20Sopenharmony_ci	/* Establish the link again and restore the device */
68348c2ecf20Sopenharmony_ci	err = ufshcd_probe_hba(hba, false);
68358c2ecf20Sopenharmony_ci
68368c2ecf20Sopenharmony_ciout:
68378c2ecf20Sopenharmony_ci	if (err)
68388c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: Host init failed %d\n", __func__, err);
68398c2ecf20Sopenharmony_ci	ufshcd_update_reg_hist(&hba->ufs_stats.host_reset, (u32)err);
68408c2ecf20Sopenharmony_ci	return err;
68418c2ecf20Sopenharmony_ci}
68428c2ecf20Sopenharmony_ci
68438c2ecf20Sopenharmony_ci/**
68448c2ecf20Sopenharmony_ci * ufshcd_reset_and_restore - reset and re-initialize host/device
68458c2ecf20Sopenharmony_ci * @hba: per-adapter instance
68468c2ecf20Sopenharmony_ci *
68478c2ecf20Sopenharmony_ci * Reset and recover device, host and re-establish link. This
68488c2ecf20Sopenharmony_ci * is helpful to recover the communication in fatal error conditions.
68498c2ecf20Sopenharmony_ci *
68508c2ecf20Sopenharmony_ci * Returns zero on success, non-zero on failure
68518c2ecf20Sopenharmony_ci */
68528c2ecf20Sopenharmony_cistatic int ufshcd_reset_and_restore(struct ufs_hba *hba)
68538c2ecf20Sopenharmony_ci{
68548c2ecf20Sopenharmony_ci	u32 saved_err;
68558c2ecf20Sopenharmony_ci	u32 saved_uic_err;
68568c2ecf20Sopenharmony_ci	int err = 0;
68578c2ecf20Sopenharmony_ci	unsigned long flags;
68588c2ecf20Sopenharmony_ci	int retries = MAX_HOST_RESET_RETRIES;
68598c2ecf20Sopenharmony_ci
68608c2ecf20Sopenharmony_ci	/*
68618c2ecf20Sopenharmony_ci	 * This is a fresh start, cache and clear saved error first,
68628c2ecf20Sopenharmony_ci	 * in case new error generated during reset and restore.
68638c2ecf20Sopenharmony_ci	 */
68648c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
68658c2ecf20Sopenharmony_ci	saved_err = hba->saved_err;
68668c2ecf20Sopenharmony_ci	saved_uic_err = hba->saved_uic_err;
68678c2ecf20Sopenharmony_ci	hba->saved_err = 0;
68688c2ecf20Sopenharmony_ci	hba->saved_uic_err = 0;
68698c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
68708c2ecf20Sopenharmony_ci
68718c2ecf20Sopenharmony_ci	do {
68728c2ecf20Sopenharmony_ci		/* Reset the attached device */
68738c2ecf20Sopenharmony_ci		ufshcd_vops_device_reset(hba);
68748c2ecf20Sopenharmony_ci
68758c2ecf20Sopenharmony_ci		err = ufshcd_host_reset_and_restore(hba);
68768c2ecf20Sopenharmony_ci	} while (err && --retries);
68778c2ecf20Sopenharmony_ci
68788c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
68798c2ecf20Sopenharmony_ci	/*
68808c2ecf20Sopenharmony_ci	 * Inform scsi mid-layer that we did reset and allow to handle
68818c2ecf20Sopenharmony_ci	 * Unit Attention properly.
68828c2ecf20Sopenharmony_ci	 */
68838c2ecf20Sopenharmony_ci	scsi_report_bus_reset(hba->host, 0);
68848c2ecf20Sopenharmony_ci	if (err) {
68858c2ecf20Sopenharmony_ci		hba->saved_err |= saved_err;
68868c2ecf20Sopenharmony_ci		hba->saved_uic_err |= saved_uic_err;
68878c2ecf20Sopenharmony_ci	}
68888c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
68898c2ecf20Sopenharmony_ci
68908c2ecf20Sopenharmony_ci	return err;
68918c2ecf20Sopenharmony_ci}
68928c2ecf20Sopenharmony_ci
68938c2ecf20Sopenharmony_ci/**
68948c2ecf20Sopenharmony_ci * ufshcd_eh_host_reset_handler - host reset handler registered to scsi layer
68958c2ecf20Sopenharmony_ci * @cmd: SCSI command pointer
68968c2ecf20Sopenharmony_ci *
68978c2ecf20Sopenharmony_ci * Returns SUCCESS/FAILED
68988c2ecf20Sopenharmony_ci */
68998c2ecf20Sopenharmony_cistatic int ufshcd_eh_host_reset_handler(struct scsi_cmnd *cmd)
69008c2ecf20Sopenharmony_ci{
69018c2ecf20Sopenharmony_ci	int err = SUCCESS;
69028c2ecf20Sopenharmony_ci	unsigned long flags;
69038c2ecf20Sopenharmony_ci	struct ufs_hba *hba;
69048c2ecf20Sopenharmony_ci
69058c2ecf20Sopenharmony_ci	hba = shost_priv(cmd->device->host);
69068c2ecf20Sopenharmony_ci
69078c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
69088c2ecf20Sopenharmony_ci	hba->force_reset = true;
69098c2ecf20Sopenharmony_ci	ufshcd_schedule_eh_work(hba);
69108c2ecf20Sopenharmony_ci	dev_err(hba->dev, "%s: reset in progress - 1\n", __func__);
69118c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
69128c2ecf20Sopenharmony_ci
69138c2ecf20Sopenharmony_ci	flush_work(&hba->eh_work);
69148c2ecf20Sopenharmony_ci
69158c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
69168c2ecf20Sopenharmony_ci	if (hba->ufshcd_state == UFSHCD_STATE_ERROR)
69178c2ecf20Sopenharmony_ci		err = FAILED;
69188c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
69198c2ecf20Sopenharmony_ci
69208c2ecf20Sopenharmony_ci	return err;
69218c2ecf20Sopenharmony_ci}
69228c2ecf20Sopenharmony_ci
69238c2ecf20Sopenharmony_ci/**
69248c2ecf20Sopenharmony_ci * ufshcd_get_max_icc_level - calculate the ICC level
69258c2ecf20Sopenharmony_ci * @sup_curr_uA: max. current supported by the regulator
69268c2ecf20Sopenharmony_ci * @start_scan: row at the desc table to start scan from
69278c2ecf20Sopenharmony_ci * @buff: power descriptor buffer
69288c2ecf20Sopenharmony_ci *
69298c2ecf20Sopenharmony_ci * Returns calculated max ICC level for specific regulator
69308c2ecf20Sopenharmony_ci */
69318c2ecf20Sopenharmony_cistatic u32 ufshcd_get_max_icc_level(int sup_curr_uA, u32 start_scan, char *buff)
69328c2ecf20Sopenharmony_ci{
69338c2ecf20Sopenharmony_ci	int i;
69348c2ecf20Sopenharmony_ci	int curr_uA;
69358c2ecf20Sopenharmony_ci	u16 data;
69368c2ecf20Sopenharmony_ci	u16 unit;
69378c2ecf20Sopenharmony_ci
69388c2ecf20Sopenharmony_ci	for (i = start_scan; i >= 0; i--) {
69398c2ecf20Sopenharmony_ci		data = be16_to_cpup((__be16 *)&buff[2 * i]);
69408c2ecf20Sopenharmony_ci		unit = (data & ATTR_ICC_LVL_UNIT_MASK) >>
69418c2ecf20Sopenharmony_ci						ATTR_ICC_LVL_UNIT_OFFSET;
69428c2ecf20Sopenharmony_ci		curr_uA = data & ATTR_ICC_LVL_VALUE_MASK;
69438c2ecf20Sopenharmony_ci		switch (unit) {
69448c2ecf20Sopenharmony_ci		case UFSHCD_NANO_AMP:
69458c2ecf20Sopenharmony_ci			curr_uA = curr_uA / 1000;
69468c2ecf20Sopenharmony_ci			break;
69478c2ecf20Sopenharmony_ci		case UFSHCD_MILI_AMP:
69488c2ecf20Sopenharmony_ci			curr_uA = curr_uA * 1000;
69498c2ecf20Sopenharmony_ci			break;
69508c2ecf20Sopenharmony_ci		case UFSHCD_AMP:
69518c2ecf20Sopenharmony_ci			curr_uA = curr_uA * 1000 * 1000;
69528c2ecf20Sopenharmony_ci			break;
69538c2ecf20Sopenharmony_ci		case UFSHCD_MICRO_AMP:
69548c2ecf20Sopenharmony_ci		default:
69558c2ecf20Sopenharmony_ci			break;
69568c2ecf20Sopenharmony_ci		}
69578c2ecf20Sopenharmony_ci		if (sup_curr_uA >= curr_uA)
69588c2ecf20Sopenharmony_ci			break;
69598c2ecf20Sopenharmony_ci	}
69608c2ecf20Sopenharmony_ci	if (i < 0) {
69618c2ecf20Sopenharmony_ci		i = 0;
69628c2ecf20Sopenharmony_ci		pr_err("%s: Couldn't find valid icc_level = %d", __func__, i);
69638c2ecf20Sopenharmony_ci	}
69648c2ecf20Sopenharmony_ci
69658c2ecf20Sopenharmony_ci	return (u32)i;
69668c2ecf20Sopenharmony_ci}
69678c2ecf20Sopenharmony_ci
69688c2ecf20Sopenharmony_ci/**
69698c2ecf20Sopenharmony_ci * ufshcd_calc_icc_level - calculate the max ICC level
69708c2ecf20Sopenharmony_ci * In case regulators are not initialized we'll return 0
69718c2ecf20Sopenharmony_ci * @hba: per-adapter instance
69728c2ecf20Sopenharmony_ci * @desc_buf: power descriptor buffer to extract ICC levels from.
69738c2ecf20Sopenharmony_ci * @len: length of desc_buff
69748c2ecf20Sopenharmony_ci *
69758c2ecf20Sopenharmony_ci * Returns calculated ICC level
69768c2ecf20Sopenharmony_ci */
69778c2ecf20Sopenharmony_cistatic u32 ufshcd_find_max_sup_active_icc_level(struct ufs_hba *hba,
69788c2ecf20Sopenharmony_ci							u8 *desc_buf, int len)
69798c2ecf20Sopenharmony_ci{
69808c2ecf20Sopenharmony_ci	u32 icc_level = 0;
69818c2ecf20Sopenharmony_ci
69828c2ecf20Sopenharmony_ci	if (!hba->vreg_info.vcc || !hba->vreg_info.vccq ||
69838c2ecf20Sopenharmony_ci						!hba->vreg_info.vccq2) {
69848c2ecf20Sopenharmony_ci		dev_err(hba->dev,
69858c2ecf20Sopenharmony_ci			"%s: Regulator capability was not set, actvIccLevel=%d",
69868c2ecf20Sopenharmony_ci							__func__, icc_level);
69878c2ecf20Sopenharmony_ci		goto out;
69888c2ecf20Sopenharmony_ci	}
69898c2ecf20Sopenharmony_ci
69908c2ecf20Sopenharmony_ci	if (hba->vreg_info.vcc && hba->vreg_info.vcc->max_uA)
69918c2ecf20Sopenharmony_ci		icc_level = ufshcd_get_max_icc_level(
69928c2ecf20Sopenharmony_ci				hba->vreg_info.vcc->max_uA,
69938c2ecf20Sopenharmony_ci				POWER_DESC_MAX_ACTV_ICC_LVLS - 1,
69948c2ecf20Sopenharmony_ci				&desc_buf[PWR_DESC_ACTIVE_LVLS_VCC_0]);
69958c2ecf20Sopenharmony_ci
69968c2ecf20Sopenharmony_ci	if (hba->vreg_info.vccq && hba->vreg_info.vccq->max_uA)
69978c2ecf20Sopenharmony_ci		icc_level = ufshcd_get_max_icc_level(
69988c2ecf20Sopenharmony_ci				hba->vreg_info.vccq->max_uA,
69998c2ecf20Sopenharmony_ci				icc_level,
70008c2ecf20Sopenharmony_ci				&desc_buf[PWR_DESC_ACTIVE_LVLS_VCCQ_0]);
70018c2ecf20Sopenharmony_ci
70028c2ecf20Sopenharmony_ci	if (hba->vreg_info.vccq2 && hba->vreg_info.vccq2->max_uA)
70038c2ecf20Sopenharmony_ci		icc_level = ufshcd_get_max_icc_level(
70048c2ecf20Sopenharmony_ci				hba->vreg_info.vccq2->max_uA,
70058c2ecf20Sopenharmony_ci				icc_level,
70068c2ecf20Sopenharmony_ci				&desc_buf[PWR_DESC_ACTIVE_LVLS_VCCQ2_0]);
70078c2ecf20Sopenharmony_ciout:
70088c2ecf20Sopenharmony_ci	return icc_level;
70098c2ecf20Sopenharmony_ci}
70108c2ecf20Sopenharmony_ci
70118c2ecf20Sopenharmony_cistatic void ufshcd_set_active_icc_lvl(struct ufs_hba *hba)
70128c2ecf20Sopenharmony_ci{
70138c2ecf20Sopenharmony_ci	int ret;
70148c2ecf20Sopenharmony_ci	int buff_len = hba->desc_size[QUERY_DESC_IDN_POWER];
70158c2ecf20Sopenharmony_ci	u8 *desc_buf;
70168c2ecf20Sopenharmony_ci	u32 icc_level;
70178c2ecf20Sopenharmony_ci
70188c2ecf20Sopenharmony_ci	desc_buf = kmalloc(buff_len, GFP_KERNEL);
70198c2ecf20Sopenharmony_ci	if (!desc_buf)
70208c2ecf20Sopenharmony_ci		return;
70218c2ecf20Sopenharmony_ci
70228c2ecf20Sopenharmony_ci	ret = ufshcd_read_desc_param(hba, QUERY_DESC_IDN_POWER, 0, 0,
70238c2ecf20Sopenharmony_ci				     desc_buf, buff_len);
70248c2ecf20Sopenharmony_ci	if (ret) {
70258c2ecf20Sopenharmony_ci		dev_err(hba->dev,
70268c2ecf20Sopenharmony_ci			"%s: Failed reading power descriptor.len = %d ret = %d",
70278c2ecf20Sopenharmony_ci			__func__, buff_len, ret);
70288c2ecf20Sopenharmony_ci		goto out;
70298c2ecf20Sopenharmony_ci	}
70308c2ecf20Sopenharmony_ci
70318c2ecf20Sopenharmony_ci	icc_level = ufshcd_find_max_sup_active_icc_level(hba, desc_buf,
70328c2ecf20Sopenharmony_ci							 buff_len);
70338c2ecf20Sopenharmony_ci	dev_dbg(hba->dev, "%s: setting icc_level 0x%x", __func__, icc_level);
70348c2ecf20Sopenharmony_ci
70358c2ecf20Sopenharmony_ci	ret = ufshcd_query_attr_retry(hba, UPIU_QUERY_OPCODE_WRITE_ATTR,
70368c2ecf20Sopenharmony_ci		QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, &icc_level);
70378c2ecf20Sopenharmony_ci
70388c2ecf20Sopenharmony_ci	if (ret)
70398c2ecf20Sopenharmony_ci		dev_err(hba->dev,
70408c2ecf20Sopenharmony_ci			"%s: Failed configuring bActiveICCLevel = %d ret = %d",
70418c2ecf20Sopenharmony_ci			__func__, icc_level, ret);
70428c2ecf20Sopenharmony_ci
70438c2ecf20Sopenharmony_ciout:
70448c2ecf20Sopenharmony_ci	kfree(desc_buf);
70458c2ecf20Sopenharmony_ci}
70468c2ecf20Sopenharmony_ci
70478c2ecf20Sopenharmony_cistatic inline void ufshcd_blk_pm_runtime_init(struct scsi_device *sdev)
70488c2ecf20Sopenharmony_ci{
70498c2ecf20Sopenharmony_ci	scsi_autopm_get_device(sdev);
70508c2ecf20Sopenharmony_ci	blk_pm_runtime_init(sdev->request_queue, &sdev->sdev_gendev);
70518c2ecf20Sopenharmony_ci	if (sdev->rpm_autosuspend)
70528c2ecf20Sopenharmony_ci		pm_runtime_set_autosuspend_delay(&sdev->sdev_gendev,
70538c2ecf20Sopenharmony_ci						 RPM_AUTOSUSPEND_DELAY_MS);
70548c2ecf20Sopenharmony_ci	scsi_autopm_put_device(sdev);
70558c2ecf20Sopenharmony_ci}
70568c2ecf20Sopenharmony_ci
70578c2ecf20Sopenharmony_ci/**
70588c2ecf20Sopenharmony_ci * ufshcd_scsi_add_wlus - Adds required W-LUs
70598c2ecf20Sopenharmony_ci * @hba: per-adapter instance
70608c2ecf20Sopenharmony_ci *
70618c2ecf20Sopenharmony_ci * UFS device specification requires the UFS devices to support 4 well known
70628c2ecf20Sopenharmony_ci * logical units:
70638c2ecf20Sopenharmony_ci *	"REPORT_LUNS" (address: 01h)
70648c2ecf20Sopenharmony_ci *	"UFS Device" (address: 50h)
70658c2ecf20Sopenharmony_ci *	"RPMB" (address: 44h)
70668c2ecf20Sopenharmony_ci *	"BOOT" (address: 30h)
70678c2ecf20Sopenharmony_ci * UFS device's power management needs to be controlled by "POWER CONDITION"
70688c2ecf20Sopenharmony_ci * field of SSU (START STOP UNIT) command. But this "power condition" field
70698c2ecf20Sopenharmony_ci * will take effect only when its sent to "UFS device" well known logical unit
70708c2ecf20Sopenharmony_ci * hence we require the scsi_device instance to represent this logical unit in
70718c2ecf20Sopenharmony_ci * order for the UFS host driver to send the SSU command for power management.
70728c2ecf20Sopenharmony_ci *
70738c2ecf20Sopenharmony_ci * We also require the scsi_device instance for "RPMB" (Replay Protected Memory
70748c2ecf20Sopenharmony_ci * Block) LU so user space process can control this LU. User space may also
70758c2ecf20Sopenharmony_ci * want to have access to BOOT LU.
70768c2ecf20Sopenharmony_ci *
70778c2ecf20Sopenharmony_ci * This function adds scsi device instances for each of all well known LUs
70788c2ecf20Sopenharmony_ci * (except "REPORT LUNS" LU).
70798c2ecf20Sopenharmony_ci *
70808c2ecf20Sopenharmony_ci * Returns zero on success (all required W-LUs are added successfully),
70818c2ecf20Sopenharmony_ci * non-zero error value on failure (if failed to add any of the required W-LU).
70828c2ecf20Sopenharmony_ci */
70838c2ecf20Sopenharmony_cistatic int ufshcd_scsi_add_wlus(struct ufs_hba *hba)
70848c2ecf20Sopenharmony_ci{
70858c2ecf20Sopenharmony_ci	int ret = 0;
70868c2ecf20Sopenharmony_ci	struct scsi_device *sdev_boot;
70878c2ecf20Sopenharmony_ci
70888c2ecf20Sopenharmony_ci	hba->sdev_ufs_device = __scsi_add_device(hba->host, 0, 0,
70898c2ecf20Sopenharmony_ci		ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_UFS_DEVICE_WLUN), NULL);
70908c2ecf20Sopenharmony_ci	if (IS_ERR(hba->sdev_ufs_device)) {
70918c2ecf20Sopenharmony_ci		ret = PTR_ERR(hba->sdev_ufs_device);
70928c2ecf20Sopenharmony_ci		hba->sdev_ufs_device = NULL;
70938c2ecf20Sopenharmony_ci		goto out;
70948c2ecf20Sopenharmony_ci	}
70958c2ecf20Sopenharmony_ci	ufshcd_blk_pm_runtime_init(hba->sdev_ufs_device);
70968c2ecf20Sopenharmony_ci	scsi_device_put(hba->sdev_ufs_device);
70978c2ecf20Sopenharmony_ci
70988c2ecf20Sopenharmony_ci	hba->sdev_rpmb = __scsi_add_device(hba->host, 0, 0,
70998c2ecf20Sopenharmony_ci		ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_RPMB_WLUN), NULL);
71008c2ecf20Sopenharmony_ci	if (IS_ERR(hba->sdev_rpmb)) {
71018c2ecf20Sopenharmony_ci		ret = PTR_ERR(hba->sdev_rpmb);
71028c2ecf20Sopenharmony_ci		goto remove_sdev_ufs_device;
71038c2ecf20Sopenharmony_ci	}
71048c2ecf20Sopenharmony_ci	ufshcd_blk_pm_runtime_init(hba->sdev_rpmb);
71058c2ecf20Sopenharmony_ci	scsi_device_put(hba->sdev_rpmb);
71068c2ecf20Sopenharmony_ci
71078c2ecf20Sopenharmony_ci	sdev_boot = __scsi_add_device(hba->host, 0, 0,
71088c2ecf20Sopenharmony_ci		ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_BOOT_WLUN), NULL);
71098c2ecf20Sopenharmony_ci	if (IS_ERR(sdev_boot)) {
71108c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: BOOT WLUN not found\n", __func__);
71118c2ecf20Sopenharmony_ci	} else {
71128c2ecf20Sopenharmony_ci		ufshcd_blk_pm_runtime_init(sdev_boot);
71138c2ecf20Sopenharmony_ci		scsi_device_put(sdev_boot);
71148c2ecf20Sopenharmony_ci	}
71158c2ecf20Sopenharmony_ci	goto out;
71168c2ecf20Sopenharmony_ci
71178c2ecf20Sopenharmony_ciremove_sdev_ufs_device:
71188c2ecf20Sopenharmony_ci	scsi_remove_device(hba->sdev_ufs_device);
71198c2ecf20Sopenharmony_ciout:
71208c2ecf20Sopenharmony_ci	return ret;
71218c2ecf20Sopenharmony_ci}
71228c2ecf20Sopenharmony_ci
71238c2ecf20Sopenharmony_cistatic void ufshcd_wb_probe(struct ufs_hba *hba, u8 *desc_buf)
71248c2ecf20Sopenharmony_ci{
71258c2ecf20Sopenharmony_ci	struct ufs_dev_info *dev_info = &hba->dev_info;
71268c2ecf20Sopenharmony_ci	u8 lun;
71278c2ecf20Sopenharmony_ci	u32 d_lu_wb_buf_alloc;
71288c2ecf20Sopenharmony_ci
71298c2ecf20Sopenharmony_ci	if (!ufshcd_is_wb_allowed(hba))
71308c2ecf20Sopenharmony_ci		return;
71318c2ecf20Sopenharmony_ci	/*
71328c2ecf20Sopenharmony_ci	 * Probe WB only for UFS-2.2 and UFS-3.1 (and later) devices or
71338c2ecf20Sopenharmony_ci	 * UFS devices with quirk UFS_DEVICE_QUIRK_SUPPORT_EXTENDED_FEATURES
71348c2ecf20Sopenharmony_ci	 * enabled
71358c2ecf20Sopenharmony_ci	 */
71368c2ecf20Sopenharmony_ci	if (!(dev_info->wspecversion >= 0x310 ||
71378c2ecf20Sopenharmony_ci	      dev_info->wspecversion == 0x220 ||
71388c2ecf20Sopenharmony_ci	     (hba->dev_quirks & UFS_DEVICE_QUIRK_SUPPORT_EXTENDED_FEATURES)))
71398c2ecf20Sopenharmony_ci		goto wb_disabled;
71408c2ecf20Sopenharmony_ci
71418c2ecf20Sopenharmony_ci	if (hba->desc_size[QUERY_DESC_IDN_DEVICE] <
71428c2ecf20Sopenharmony_ci	    DEVICE_DESC_PARAM_EXT_UFS_FEATURE_SUP + 4)
71438c2ecf20Sopenharmony_ci		goto wb_disabled;
71448c2ecf20Sopenharmony_ci
71458c2ecf20Sopenharmony_ci	dev_info->d_ext_ufs_feature_sup =
71468c2ecf20Sopenharmony_ci		get_unaligned_be32(desc_buf +
71478c2ecf20Sopenharmony_ci				   DEVICE_DESC_PARAM_EXT_UFS_FEATURE_SUP);
71488c2ecf20Sopenharmony_ci
71498c2ecf20Sopenharmony_ci	if (!(dev_info->d_ext_ufs_feature_sup & UFS_DEV_WRITE_BOOSTER_SUP))
71508c2ecf20Sopenharmony_ci		goto wb_disabled;
71518c2ecf20Sopenharmony_ci
71528c2ecf20Sopenharmony_ci	/*
71538c2ecf20Sopenharmony_ci	 * WB may be supported but not configured while provisioning.
71548c2ecf20Sopenharmony_ci	 * The spec says, in dedicated wb buffer mode,
71558c2ecf20Sopenharmony_ci	 * a max of 1 lun would have wb buffer configured.
71568c2ecf20Sopenharmony_ci	 * Now only shared buffer mode is supported.
71578c2ecf20Sopenharmony_ci	 */
71588c2ecf20Sopenharmony_ci	dev_info->b_wb_buffer_type =
71598c2ecf20Sopenharmony_ci		desc_buf[DEVICE_DESC_PARAM_WB_TYPE];
71608c2ecf20Sopenharmony_ci
71618c2ecf20Sopenharmony_ci	dev_info->b_presrv_uspc_en =
71628c2ecf20Sopenharmony_ci		desc_buf[DEVICE_DESC_PARAM_WB_PRESRV_USRSPC_EN];
71638c2ecf20Sopenharmony_ci
71648c2ecf20Sopenharmony_ci	if (dev_info->b_wb_buffer_type == WB_BUF_MODE_SHARED) {
71658c2ecf20Sopenharmony_ci		dev_info->d_wb_alloc_units =
71668c2ecf20Sopenharmony_ci		get_unaligned_be32(desc_buf +
71678c2ecf20Sopenharmony_ci				   DEVICE_DESC_PARAM_WB_SHARED_ALLOC_UNITS);
71688c2ecf20Sopenharmony_ci		if (!dev_info->d_wb_alloc_units)
71698c2ecf20Sopenharmony_ci			goto wb_disabled;
71708c2ecf20Sopenharmony_ci	} else {
71718c2ecf20Sopenharmony_ci		for (lun = 0; lun < UFS_UPIU_MAX_WB_LUN_ID; lun++) {
71728c2ecf20Sopenharmony_ci			d_lu_wb_buf_alloc = 0;
71738c2ecf20Sopenharmony_ci			ufshcd_read_unit_desc_param(hba,
71748c2ecf20Sopenharmony_ci					lun,
71758c2ecf20Sopenharmony_ci					UNIT_DESC_PARAM_WB_BUF_ALLOC_UNITS,
71768c2ecf20Sopenharmony_ci					(u8 *)&d_lu_wb_buf_alloc,
71778c2ecf20Sopenharmony_ci					sizeof(d_lu_wb_buf_alloc));
71788c2ecf20Sopenharmony_ci			if (d_lu_wb_buf_alloc) {
71798c2ecf20Sopenharmony_ci				dev_info->wb_dedicated_lu = lun;
71808c2ecf20Sopenharmony_ci				break;
71818c2ecf20Sopenharmony_ci			}
71828c2ecf20Sopenharmony_ci		}
71838c2ecf20Sopenharmony_ci
71848c2ecf20Sopenharmony_ci		if (!d_lu_wb_buf_alloc)
71858c2ecf20Sopenharmony_ci			goto wb_disabled;
71868c2ecf20Sopenharmony_ci	}
71878c2ecf20Sopenharmony_ci	return;
71888c2ecf20Sopenharmony_ci
71898c2ecf20Sopenharmony_ciwb_disabled:
71908c2ecf20Sopenharmony_ci	hba->caps &= ~UFSHCD_CAP_WB_EN;
71918c2ecf20Sopenharmony_ci}
71928c2ecf20Sopenharmony_ci
71938c2ecf20Sopenharmony_civoid ufshcd_fixup_dev_quirks(struct ufs_hba *hba, struct ufs_dev_fix *fixups)
71948c2ecf20Sopenharmony_ci{
71958c2ecf20Sopenharmony_ci	struct ufs_dev_fix *f;
71968c2ecf20Sopenharmony_ci	struct ufs_dev_info *dev_info = &hba->dev_info;
71978c2ecf20Sopenharmony_ci
71988c2ecf20Sopenharmony_ci	if (!fixups)
71998c2ecf20Sopenharmony_ci		return;
72008c2ecf20Sopenharmony_ci
72018c2ecf20Sopenharmony_ci	for (f = fixups; f->quirk; f++) {
72028c2ecf20Sopenharmony_ci		if ((f->wmanufacturerid == dev_info->wmanufacturerid ||
72038c2ecf20Sopenharmony_ci		     f->wmanufacturerid == UFS_ANY_VENDOR) &&
72048c2ecf20Sopenharmony_ci		     ((dev_info->model &&
72058c2ecf20Sopenharmony_ci		       STR_PRFX_EQUAL(f->model, dev_info->model)) ||
72068c2ecf20Sopenharmony_ci		      !strcmp(f->model, UFS_ANY_MODEL)))
72078c2ecf20Sopenharmony_ci			hba->dev_quirks |= f->quirk;
72088c2ecf20Sopenharmony_ci	}
72098c2ecf20Sopenharmony_ci}
72108c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ufshcd_fixup_dev_quirks);
72118c2ecf20Sopenharmony_ci
72128c2ecf20Sopenharmony_cistatic void ufs_fixup_device_setup(struct ufs_hba *hba)
72138c2ecf20Sopenharmony_ci{
72148c2ecf20Sopenharmony_ci	/* fix by general quirk table */
72158c2ecf20Sopenharmony_ci	ufshcd_fixup_dev_quirks(hba, ufs_fixups);
72168c2ecf20Sopenharmony_ci
72178c2ecf20Sopenharmony_ci	/* allow vendors to fix quirks */
72188c2ecf20Sopenharmony_ci	ufshcd_vops_fixup_dev_quirks(hba);
72198c2ecf20Sopenharmony_ci}
72208c2ecf20Sopenharmony_ci
72218c2ecf20Sopenharmony_cistatic int ufs_get_device_desc(struct ufs_hba *hba)
72228c2ecf20Sopenharmony_ci{
72238c2ecf20Sopenharmony_ci	int err;
72248c2ecf20Sopenharmony_ci	u8 model_index;
72258c2ecf20Sopenharmony_ci	u8 *desc_buf;
72268c2ecf20Sopenharmony_ci	struct ufs_dev_info *dev_info = &hba->dev_info;
72278c2ecf20Sopenharmony_ci
72288c2ecf20Sopenharmony_ci	desc_buf = kmalloc(QUERY_DESC_MAX_SIZE, GFP_KERNEL);
72298c2ecf20Sopenharmony_ci	if (!desc_buf) {
72308c2ecf20Sopenharmony_ci		err = -ENOMEM;
72318c2ecf20Sopenharmony_ci		goto out;
72328c2ecf20Sopenharmony_ci	}
72338c2ecf20Sopenharmony_ci
72348c2ecf20Sopenharmony_ci	err = ufshcd_read_desc_param(hba, QUERY_DESC_IDN_DEVICE, 0, 0, desc_buf,
72358c2ecf20Sopenharmony_ci				     hba->desc_size[QUERY_DESC_IDN_DEVICE]);
72368c2ecf20Sopenharmony_ci	if (err) {
72378c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: Failed reading Device Desc. err = %d\n",
72388c2ecf20Sopenharmony_ci			__func__, err);
72398c2ecf20Sopenharmony_ci		goto out;
72408c2ecf20Sopenharmony_ci	}
72418c2ecf20Sopenharmony_ci
72428c2ecf20Sopenharmony_ci	/*
72438c2ecf20Sopenharmony_ci	 * getting vendor (manufacturerID) and Bank Index in big endian
72448c2ecf20Sopenharmony_ci	 * format
72458c2ecf20Sopenharmony_ci	 */
72468c2ecf20Sopenharmony_ci	dev_info->wmanufacturerid = desc_buf[DEVICE_DESC_PARAM_MANF_ID] << 8 |
72478c2ecf20Sopenharmony_ci				     desc_buf[DEVICE_DESC_PARAM_MANF_ID + 1];
72488c2ecf20Sopenharmony_ci
72498c2ecf20Sopenharmony_ci	/* getting Specification Version in big endian format */
72508c2ecf20Sopenharmony_ci	dev_info->wspecversion = desc_buf[DEVICE_DESC_PARAM_SPEC_VER] << 8 |
72518c2ecf20Sopenharmony_ci				      desc_buf[DEVICE_DESC_PARAM_SPEC_VER + 1];
72528c2ecf20Sopenharmony_ci
72538c2ecf20Sopenharmony_ci	model_index = desc_buf[DEVICE_DESC_PARAM_PRDCT_NAME];
72548c2ecf20Sopenharmony_ci
72558c2ecf20Sopenharmony_ci	err = ufshcd_read_string_desc(hba, model_index,
72568c2ecf20Sopenharmony_ci				      &dev_info->model, SD_ASCII_STD);
72578c2ecf20Sopenharmony_ci	if (err < 0) {
72588c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: Failed reading Product Name. err = %d\n",
72598c2ecf20Sopenharmony_ci			__func__, err);
72608c2ecf20Sopenharmony_ci		goto out;
72618c2ecf20Sopenharmony_ci	}
72628c2ecf20Sopenharmony_ci
72638c2ecf20Sopenharmony_ci	ufs_fixup_device_setup(hba);
72648c2ecf20Sopenharmony_ci
72658c2ecf20Sopenharmony_ci	ufshcd_wb_probe(hba, desc_buf);
72668c2ecf20Sopenharmony_ci
72678c2ecf20Sopenharmony_ci	/*
72688c2ecf20Sopenharmony_ci	 * ufshcd_read_string_desc returns size of the string
72698c2ecf20Sopenharmony_ci	 * reset the error value
72708c2ecf20Sopenharmony_ci	 */
72718c2ecf20Sopenharmony_ci	err = 0;
72728c2ecf20Sopenharmony_ci
72738c2ecf20Sopenharmony_ciout:
72748c2ecf20Sopenharmony_ci	kfree(desc_buf);
72758c2ecf20Sopenharmony_ci	return err;
72768c2ecf20Sopenharmony_ci}
72778c2ecf20Sopenharmony_ci
72788c2ecf20Sopenharmony_cistatic void ufs_put_device_desc(struct ufs_hba *hba)
72798c2ecf20Sopenharmony_ci{
72808c2ecf20Sopenharmony_ci	struct ufs_dev_info *dev_info = &hba->dev_info;
72818c2ecf20Sopenharmony_ci
72828c2ecf20Sopenharmony_ci	kfree(dev_info->model);
72838c2ecf20Sopenharmony_ci	dev_info->model = NULL;
72848c2ecf20Sopenharmony_ci}
72858c2ecf20Sopenharmony_ci
72868c2ecf20Sopenharmony_ci/**
72878c2ecf20Sopenharmony_ci * ufshcd_tune_pa_tactivate - Tunes PA_TActivate of local UniPro
72888c2ecf20Sopenharmony_ci * @hba: per-adapter instance
72898c2ecf20Sopenharmony_ci *
72908c2ecf20Sopenharmony_ci * PA_TActivate parameter can be tuned manually if UniPro version is less than
72918c2ecf20Sopenharmony_ci * 1.61. PA_TActivate needs to be greater than or equal to peerM-PHY's
72928c2ecf20Sopenharmony_ci * RX_MIN_ACTIVATETIME_CAPABILITY attribute. This optimal value can help reduce
72938c2ecf20Sopenharmony_ci * the hibern8 exit latency.
72948c2ecf20Sopenharmony_ci *
72958c2ecf20Sopenharmony_ci * Returns zero on success, non-zero error value on failure.
72968c2ecf20Sopenharmony_ci */
72978c2ecf20Sopenharmony_cistatic int ufshcd_tune_pa_tactivate(struct ufs_hba *hba)
72988c2ecf20Sopenharmony_ci{
72998c2ecf20Sopenharmony_ci	int ret = 0;
73008c2ecf20Sopenharmony_ci	u32 peer_rx_min_activatetime = 0, tuned_pa_tactivate;
73018c2ecf20Sopenharmony_ci
73028c2ecf20Sopenharmony_ci	ret = ufshcd_dme_peer_get(hba,
73038c2ecf20Sopenharmony_ci				  UIC_ARG_MIB_SEL(
73048c2ecf20Sopenharmony_ci					RX_MIN_ACTIVATETIME_CAPABILITY,
73058c2ecf20Sopenharmony_ci					UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)),
73068c2ecf20Sopenharmony_ci				  &peer_rx_min_activatetime);
73078c2ecf20Sopenharmony_ci	if (ret)
73088c2ecf20Sopenharmony_ci		goto out;
73098c2ecf20Sopenharmony_ci
73108c2ecf20Sopenharmony_ci	/* make sure proper unit conversion is applied */
73118c2ecf20Sopenharmony_ci	tuned_pa_tactivate =
73128c2ecf20Sopenharmony_ci		((peer_rx_min_activatetime * RX_MIN_ACTIVATETIME_UNIT_US)
73138c2ecf20Sopenharmony_ci		 / PA_TACTIVATE_TIME_UNIT_US);
73148c2ecf20Sopenharmony_ci	ret = ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TACTIVATE),
73158c2ecf20Sopenharmony_ci			     tuned_pa_tactivate);
73168c2ecf20Sopenharmony_ci
73178c2ecf20Sopenharmony_ciout:
73188c2ecf20Sopenharmony_ci	return ret;
73198c2ecf20Sopenharmony_ci}
73208c2ecf20Sopenharmony_ci
73218c2ecf20Sopenharmony_ci/**
73228c2ecf20Sopenharmony_ci * ufshcd_tune_pa_hibern8time - Tunes PA_Hibern8Time of local UniPro
73238c2ecf20Sopenharmony_ci * @hba: per-adapter instance
73248c2ecf20Sopenharmony_ci *
73258c2ecf20Sopenharmony_ci * PA_Hibern8Time parameter can be tuned manually if UniPro version is less than
73268c2ecf20Sopenharmony_ci * 1.61. PA_Hibern8Time needs to be maximum of local M-PHY's
73278c2ecf20Sopenharmony_ci * TX_HIBERN8TIME_CAPABILITY & peer M-PHY's RX_HIBERN8TIME_CAPABILITY.
73288c2ecf20Sopenharmony_ci * This optimal value can help reduce the hibern8 exit latency.
73298c2ecf20Sopenharmony_ci *
73308c2ecf20Sopenharmony_ci * Returns zero on success, non-zero error value on failure.
73318c2ecf20Sopenharmony_ci */
73328c2ecf20Sopenharmony_cistatic int ufshcd_tune_pa_hibern8time(struct ufs_hba *hba)
73338c2ecf20Sopenharmony_ci{
73348c2ecf20Sopenharmony_ci	int ret = 0;
73358c2ecf20Sopenharmony_ci	u32 local_tx_hibern8_time_cap = 0, peer_rx_hibern8_time_cap = 0;
73368c2ecf20Sopenharmony_ci	u32 max_hibern8_time, tuned_pa_hibern8time;
73378c2ecf20Sopenharmony_ci
73388c2ecf20Sopenharmony_ci	ret = ufshcd_dme_get(hba,
73398c2ecf20Sopenharmony_ci			     UIC_ARG_MIB_SEL(TX_HIBERN8TIME_CAPABILITY,
73408c2ecf20Sopenharmony_ci					UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)),
73418c2ecf20Sopenharmony_ci				  &local_tx_hibern8_time_cap);
73428c2ecf20Sopenharmony_ci	if (ret)
73438c2ecf20Sopenharmony_ci		goto out;
73448c2ecf20Sopenharmony_ci
73458c2ecf20Sopenharmony_ci	ret = ufshcd_dme_peer_get(hba,
73468c2ecf20Sopenharmony_ci				  UIC_ARG_MIB_SEL(RX_HIBERN8TIME_CAPABILITY,
73478c2ecf20Sopenharmony_ci					UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)),
73488c2ecf20Sopenharmony_ci				  &peer_rx_hibern8_time_cap);
73498c2ecf20Sopenharmony_ci	if (ret)
73508c2ecf20Sopenharmony_ci		goto out;
73518c2ecf20Sopenharmony_ci
73528c2ecf20Sopenharmony_ci	max_hibern8_time = max(local_tx_hibern8_time_cap,
73538c2ecf20Sopenharmony_ci			       peer_rx_hibern8_time_cap);
73548c2ecf20Sopenharmony_ci	/* make sure proper unit conversion is applied */
73558c2ecf20Sopenharmony_ci	tuned_pa_hibern8time = ((max_hibern8_time * HIBERN8TIME_UNIT_US)
73568c2ecf20Sopenharmony_ci				/ PA_HIBERN8_TIME_UNIT_US);
73578c2ecf20Sopenharmony_ci	ret = ufshcd_dme_set(hba, UIC_ARG_MIB(PA_HIBERN8TIME),
73588c2ecf20Sopenharmony_ci			     tuned_pa_hibern8time);
73598c2ecf20Sopenharmony_ciout:
73608c2ecf20Sopenharmony_ci	return ret;
73618c2ecf20Sopenharmony_ci}
73628c2ecf20Sopenharmony_ci
73638c2ecf20Sopenharmony_ci/**
73648c2ecf20Sopenharmony_ci * ufshcd_quirk_tune_host_pa_tactivate - Ensures that host PA_TACTIVATE is
73658c2ecf20Sopenharmony_ci * less than device PA_TACTIVATE time.
73668c2ecf20Sopenharmony_ci * @hba: per-adapter instance
73678c2ecf20Sopenharmony_ci *
73688c2ecf20Sopenharmony_ci * Some UFS devices require host PA_TACTIVATE to be lower than device
73698c2ecf20Sopenharmony_ci * PA_TACTIVATE, we need to enable UFS_DEVICE_QUIRK_HOST_PA_TACTIVATE quirk
73708c2ecf20Sopenharmony_ci * for such devices.
73718c2ecf20Sopenharmony_ci *
73728c2ecf20Sopenharmony_ci * Returns zero on success, non-zero error value on failure.
73738c2ecf20Sopenharmony_ci */
73748c2ecf20Sopenharmony_cistatic int ufshcd_quirk_tune_host_pa_tactivate(struct ufs_hba *hba)
73758c2ecf20Sopenharmony_ci{
73768c2ecf20Sopenharmony_ci	int ret = 0;
73778c2ecf20Sopenharmony_ci	u32 granularity, peer_granularity;
73788c2ecf20Sopenharmony_ci	u32 pa_tactivate, peer_pa_tactivate;
73798c2ecf20Sopenharmony_ci	u32 pa_tactivate_us, peer_pa_tactivate_us;
73808c2ecf20Sopenharmony_ci	u8 gran_to_us_table[] = {1, 4, 8, 16, 32, 100};
73818c2ecf20Sopenharmony_ci
73828c2ecf20Sopenharmony_ci	ret = ufshcd_dme_get(hba, UIC_ARG_MIB(PA_GRANULARITY),
73838c2ecf20Sopenharmony_ci				  &granularity);
73848c2ecf20Sopenharmony_ci	if (ret)
73858c2ecf20Sopenharmony_ci		goto out;
73868c2ecf20Sopenharmony_ci
73878c2ecf20Sopenharmony_ci	ret = ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_GRANULARITY),
73888c2ecf20Sopenharmony_ci				  &peer_granularity);
73898c2ecf20Sopenharmony_ci	if (ret)
73908c2ecf20Sopenharmony_ci		goto out;
73918c2ecf20Sopenharmony_ci
73928c2ecf20Sopenharmony_ci	if ((granularity < PA_GRANULARITY_MIN_VAL) ||
73938c2ecf20Sopenharmony_ci	    (granularity > PA_GRANULARITY_MAX_VAL)) {
73948c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: invalid host PA_GRANULARITY %d",
73958c2ecf20Sopenharmony_ci			__func__, granularity);
73968c2ecf20Sopenharmony_ci		return -EINVAL;
73978c2ecf20Sopenharmony_ci	}
73988c2ecf20Sopenharmony_ci
73998c2ecf20Sopenharmony_ci	if ((peer_granularity < PA_GRANULARITY_MIN_VAL) ||
74008c2ecf20Sopenharmony_ci	    (peer_granularity > PA_GRANULARITY_MAX_VAL)) {
74018c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: invalid device PA_GRANULARITY %d",
74028c2ecf20Sopenharmony_ci			__func__, peer_granularity);
74038c2ecf20Sopenharmony_ci		return -EINVAL;
74048c2ecf20Sopenharmony_ci	}
74058c2ecf20Sopenharmony_ci
74068c2ecf20Sopenharmony_ci	ret = ufshcd_dme_get(hba, UIC_ARG_MIB(PA_TACTIVATE), &pa_tactivate);
74078c2ecf20Sopenharmony_ci	if (ret)
74088c2ecf20Sopenharmony_ci		goto out;
74098c2ecf20Sopenharmony_ci
74108c2ecf20Sopenharmony_ci	ret = ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_TACTIVATE),
74118c2ecf20Sopenharmony_ci				  &peer_pa_tactivate);
74128c2ecf20Sopenharmony_ci	if (ret)
74138c2ecf20Sopenharmony_ci		goto out;
74148c2ecf20Sopenharmony_ci
74158c2ecf20Sopenharmony_ci	pa_tactivate_us = pa_tactivate * gran_to_us_table[granularity - 1];
74168c2ecf20Sopenharmony_ci	peer_pa_tactivate_us = peer_pa_tactivate *
74178c2ecf20Sopenharmony_ci			     gran_to_us_table[peer_granularity - 1];
74188c2ecf20Sopenharmony_ci
74198c2ecf20Sopenharmony_ci	if (pa_tactivate_us > peer_pa_tactivate_us) {
74208c2ecf20Sopenharmony_ci		u32 new_peer_pa_tactivate;
74218c2ecf20Sopenharmony_ci
74228c2ecf20Sopenharmony_ci		new_peer_pa_tactivate = pa_tactivate_us /
74238c2ecf20Sopenharmony_ci				      gran_to_us_table[peer_granularity - 1];
74248c2ecf20Sopenharmony_ci		new_peer_pa_tactivate++;
74258c2ecf20Sopenharmony_ci		ret = ufshcd_dme_peer_set(hba, UIC_ARG_MIB(PA_TACTIVATE),
74268c2ecf20Sopenharmony_ci					  new_peer_pa_tactivate);
74278c2ecf20Sopenharmony_ci	}
74288c2ecf20Sopenharmony_ci
74298c2ecf20Sopenharmony_ciout:
74308c2ecf20Sopenharmony_ci	return ret;
74318c2ecf20Sopenharmony_ci}
74328c2ecf20Sopenharmony_ci
74338c2ecf20Sopenharmony_cistatic void ufshcd_tune_unipro_params(struct ufs_hba *hba)
74348c2ecf20Sopenharmony_ci{
74358c2ecf20Sopenharmony_ci	if (ufshcd_is_unipro_pa_params_tuning_req(hba)) {
74368c2ecf20Sopenharmony_ci		ufshcd_tune_pa_tactivate(hba);
74378c2ecf20Sopenharmony_ci		ufshcd_tune_pa_hibern8time(hba);
74388c2ecf20Sopenharmony_ci	}
74398c2ecf20Sopenharmony_ci
74408c2ecf20Sopenharmony_ci	ufshcd_vops_apply_dev_quirks(hba);
74418c2ecf20Sopenharmony_ci
74428c2ecf20Sopenharmony_ci	if (hba->dev_quirks & UFS_DEVICE_QUIRK_PA_TACTIVATE)
74438c2ecf20Sopenharmony_ci		/* set 1ms timeout for PA_TACTIVATE */
74448c2ecf20Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TACTIVATE), 10);
74458c2ecf20Sopenharmony_ci
74468c2ecf20Sopenharmony_ci	if (hba->dev_quirks & UFS_DEVICE_QUIRK_HOST_PA_TACTIVATE)
74478c2ecf20Sopenharmony_ci		ufshcd_quirk_tune_host_pa_tactivate(hba);
74488c2ecf20Sopenharmony_ci}
74498c2ecf20Sopenharmony_ci
74508c2ecf20Sopenharmony_cistatic void ufshcd_clear_dbg_ufs_stats(struct ufs_hba *hba)
74518c2ecf20Sopenharmony_ci{
74528c2ecf20Sopenharmony_ci	hba->ufs_stats.hibern8_exit_cnt = 0;
74538c2ecf20Sopenharmony_ci	hba->ufs_stats.last_hibern8_exit_tstamp = ktime_set(0, 0);
74548c2ecf20Sopenharmony_ci	hba->req_abort_count = 0;
74558c2ecf20Sopenharmony_ci}
74568c2ecf20Sopenharmony_ci
74578c2ecf20Sopenharmony_cistatic int ufshcd_device_geo_params_init(struct ufs_hba *hba)
74588c2ecf20Sopenharmony_ci{
74598c2ecf20Sopenharmony_ci	int err;
74608c2ecf20Sopenharmony_ci	size_t buff_len;
74618c2ecf20Sopenharmony_ci	u8 *desc_buf;
74628c2ecf20Sopenharmony_ci
74638c2ecf20Sopenharmony_ci	buff_len = hba->desc_size[QUERY_DESC_IDN_GEOMETRY];
74648c2ecf20Sopenharmony_ci	desc_buf = kmalloc(buff_len, GFP_KERNEL);
74658c2ecf20Sopenharmony_ci	if (!desc_buf) {
74668c2ecf20Sopenharmony_ci		err = -ENOMEM;
74678c2ecf20Sopenharmony_ci		goto out;
74688c2ecf20Sopenharmony_ci	}
74698c2ecf20Sopenharmony_ci
74708c2ecf20Sopenharmony_ci	err = ufshcd_read_desc_param(hba, QUERY_DESC_IDN_GEOMETRY, 0, 0,
74718c2ecf20Sopenharmony_ci				     desc_buf, buff_len);
74728c2ecf20Sopenharmony_ci	if (err) {
74738c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: Failed reading Geometry Desc. err = %d\n",
74748c2ecf20Sopenharmony_ci				__func__, err);
74758c2ecf20Sopenharmony_ci		goto out;
74768c2ecf20Sopenharmony_ci	}
74778c2ecf20Sopenharmony_ci
74788c2ecf20Sopenharmony_ci	if (desc_buf[GEOMETRY_DESC_PARAM_MAX_NUM_LUN] == 1)
74798c2ecf20Sopenharmony_ci		hba->dev_info.max_lu_supported = 32;
74808c2ecf20Sopenharmony_ci	else if (desc_buf[GEOMETRY_DESC_PARAM_MAX_NUM_LUN] == 0)
74818c2ecf20Sopenharmony_ci		hba->dev_info.max_lu_supported = 8;
74828c2ecf20Sopenharmony_ci
74838c2ecf20Sopenharmony_ciout:
74848c2ecf20Sopenharmony_ci	kfree(desc_buf);
74858c2ecf20Sopenharmony_ci	return err;
74868c2ecf20Sopenharmony_ci}
74878c2ecf20Sopenharmony_ci
74888c2ecf20Sopenharmony_cistatic struct ufs_ref_clk ufs_ref_clk_freqs[] = {
74898c2ecf20Sopenharmony_ci	{19200000, REF_CLK_FREQ_19_2_MHZ},
74908c2ecf20Sopenharmony_ci	{26000000, REF_CLK_FREQ_26_MHZ},
74918c2ecf20Sopenharmony_ci	{38400000, REF_CLK_FREQ_38_4_MHZ},
74928c2ecf20Sopenharmony_ci	{52000000, REF_CLK_FREQ_52_MHZ},
74938c2ecf20Sopenharmony_ci	{0, REF_CLK_FREQ_INVAL},
74948c2ecf20Sopenharmony_ci};
74958c2ecf20Sopenharmony_ci
74968c2ecf20Sopenharmony_cistatic enum ufs_ref_clk_freq
74978c2ecf20Sopenharmony_ciufs_get_bref_clk_from_hz(unsigned long freq)
74988c2ecf20Sopenharmony_ci{
74998c2ecf20Sopenharmony_ci	int i;
75008c2ecf20Sopenharmony_ci
75018c2ecf20Sopenharmony_ci	for (i = 0; ufs_ref_clk_freqs[i].freq_hz; i++)
75028c2ecf20Sopenharmony_ci		if (ufs_ref_clk_freqs[i].freq_hz == freq)
75038c2ecf20Sopenharmony_ci			return ufs_ref_clk_freqs[i].val;
75048c2ecf20Sopenharmony_ci
75058c2ecf20Sopenharmony_ci	return REF_CLK_FREQ_INVAL;
75068c2ecf20Sopenharmony_ci}
75078c2ecf20Sopenharmony_ci
75088c2ecf20Sopenharmony_civoid ufshcd_parse_dev_ref_clk_freq(struct ufs_hba *hba, struct clk *refclk)
75098c2ecf20Sopenharmony_ci{
75108c2ecf20Sopenharmony_ci	unsigned long freq;
75118c2ecf20Sopenharmony_ci
75128c2ecf20Sopenharmony_ci	freq = clk_get_rate(refclk);
75138c2ecf20Sopenharmony_ci
75148c2ecf20Sopenharmony_ci	hba->dev_ref_clk_freq =
75158c2ecf20Sopenharmony_ci		ufs_get_bref_clk_from_hz(freq);
75168c2ecf20Sopenharmony_ci
75178c2ecf20Sopenharmony_ci	if (hba->dev_ref_clk_freq == REF_CLK_FREQ_INVAL)
75188c2ecf20Sopenharmony_ci		dev_err(hba->dev,
75198c2ecf20Sopenharmony_ci		"invalid ref_clk setting = %ld\n", freq);
75208c2ecf20Sopenharmony_ci}
75218c2ecf20Sopenharmony_ci
75228c2ecf20Sopenharmony_cistatic int ufshcd_set_dev_ref_clk(struct ufs_hba *hba)
75238c2ecf20Sopenharmony_ci{
75248c2ecf20Sopenharmony_ci	int err;
75258c2ecf20Sopenharmony_ci	u32 ref_clk;
75268c2ecf20Sopenharmony_ci	u32 freq = hba->dev_ref_clk_freq;
75278c2ecf20Sopenharmony_ci
75288c2ecf20Sopenharmony_ci	err = ufshcd_query_attr_retry(hba, UPIU_QUERY_OPCODE_READ_ATTR,
75298c2ecf20Sopenharmony_ci			QUERY_ATTR_IDN_REF_CLK_FREQ, 0, 0, &ref_clk);
75308c2ecf20Sopenharmony_ci
75318c2ecf20Sopenharmony_ci	if (err) {
75328c2ecf20Sopenharmony_ci		dev_err(hba->dev, "failed reading bRefClkFreq. err = %d\n",
75338c2ecf20Sopenharmony_ci			err);
75348c2ecf20Sopenharmony_ci		goto out;
75358c2ecf20Sopenharmony_ci	}
75368c2ecf20Sopenharmony_ci
75378c2ecf20Sopenharmony_ci	if (ref_clk == freq)
75388c2ecf20Sopenharmony_ci		goto out; /* nothing to update */
75398c2ecf20Sopenharmony_ci
75408c2ecf20Sopenharmony_ci	err = ufshcd_query_attr_retry(hba, UPIU_QUERY_OPCODE_WRITE_ATTR,
75418c2ecf20Sopenharmony_ci			QUERY_ATTR_IDN_REF_CLK_FREQ, 0, 0, &freq);
75428c2ecf20Sopenharmony_ci
75438c2ecf20Sopenharmony_ci	if (err) {
75448c2ecf20Sopenharmony_ci		dev_err(hba->dev, "bRefClkFreq setting to %lu Hz failed\n",
75458c2ecf20Sopenharmony_ci			ufs_ref_clk_freqs[freq].freq_hz);
75468c2ecf20Sopenharmony_ci		goto out;
75478c2ecf20Sopenharmony_ci	}
75488c2ecf20Sopenharmony_ci
75498c2ecf20Sopenharmony_ci	dev_dbg(hba->dev, "bRefClkFreq setting to %lu Hz succeeded\n",
75508c2ecf20Sopenharmony_ci			ufs_ref_clk_freqs[freq].freq_hz);
75518c2ecf20Sopenharmony_ci
75528c2ecf20Sopenharmony_ciout:
75538c2ecf20Sopenharmony_ci	return err;
75548c2ecf20Sopenharmony_ci}
75558c2ecf20Sopenharmony_ci
75568c2ecf20Sopenharmony_cistatic int ufshcd_device_params_init(struct ufs_hba *hba)
75578c2ecf20Sopenharmony_ci{
75588c2ecf20Sopenharmony_ci	bool flag;
75598c2ecf20Sopenharmony_ci	int ret, i;
75608c2ecf20Sopenharmony_ci
75618c2ecf20Sopenharmony_ci	 /* Init device descriptor sizes */
75628c2ecf20Sopenharmony_ci	for (i = 0; i < QUERY_DESC_IDN_MAX; i++)
75638c2ecf20Sopenharmony_ci		hba->desc_size[i] = QUERY_DESC_MAX_SIZE;
75648c2ecf20Sopenharmony_ci
75658c2ecf20Sopenharmony_ci	/* Init UFS geometry descriptor related parameters */
75668c2ecf20Sopenharmony_ci	ret = ufshcd_device_geo_params_init(hba);
75678c2ecf20Sopenharmony_ci	if (ret)
75688c2ecf20Sopenharmony_ci		goto out;
75698c2ecf20Sopenharmony_ci
75708c2ecf20Sopenharmony_ci	/* Check and apply UFS device quirks */
75718c2ecf20Sopenharmony_ci	ret = ufs_get_device_desc(hba);
75728c2ecf20Sopenharmony_ci	if (ret) {
75738c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: Failed getting device info. err = %d\n",
75748c2ecf20Sopenharmony_ci			__func__, ret);
75758c2ecf20Sopenharmony_ci		goto out;
75768c2ecf20Sopenharmony_ci	}
75778c2ecf20Sopenharmony_ci
75788c2ecf20Sopenharmony_ci	ufshcd_get_ref_clk_gating_wait(hba);
75798c2ecf20Sopenharmony_ci
75808c2ecf20Sopenharmony_ci	if (!ufshcd_query_flag_retry(hba, UPIU_QUERY_OPCODE_READ_FLAG,
75818c2ecf20Sopenharmony_ci			QUERY_FLAG_IDN_PWR_ON_WPE, 0, &flag))
75828c2ecf20Sopenharmony_ci		hba->dev_info.f_power_on_wp_en = flag;
75838c2ecf20Sopenharmony_ci
75848c2ecf20Sopenharmony_ci	/* Probe maximum power mode co-supported by both UFS host and device */
75858c2ecf20Sopenharmony_ci	if (ufshcd_get_max_pwr_mode(hba))
75868c2ecf20Sopenharmony_ci		dev_err(hba->dev,
75878c2ecf20Sopenharmony_ci			"%s: Failed getting max supported power mode\n",
75888c2ecf20Sopenharmony_ci			__func__);
75898c2ecf20Sopenharmony_ciout:
75908c2ecf20Sopenharmony_ci	return ret;
75918c2ecf20Sopenharmony_ci}
75928c2ecf20Sopenharmony_ci
75938c2ecf20Sopenharmony_ci/**
75948c2ecf20Sopenharmony_ci * ufshcd_add_lus - probe and add UFS logical units
75958c2ecf20Sopenharmony_ci * @hba: per-adapter instance
75968c2ecf20Sopenharmony_ci */
75978c2ecf20Sopenharmony_cistatic int ufshcd_add_lus(struct ufs_hba *hba)
75988c2ecf20Sopenharmony_ci{
75998c2ecf20Sopenharmony_ci	int ret;
76008c2ecf20Sopenharmony_ci
76018c2ecf20Sopenharmony_ci	/* Add required well known logical units to scsi mid layer */
76028c2ecf20Sopenharmony_ci	ret = ufshcd_scsi_add_wlus(hba);
76038c2ecf20Sopenharmony_ci	if (ret)
76048c2ecf20Sopenharmony_ci		goto out;
76058c2ecf20Sopenharmony_ci
76068c2ecf20Sopenharmony_ci	/* Initialize devfreq after UFS device is detected */
76078c2ecf20Sopenharmony_ci	if (ufshcd_is_clkscaling_supported(hba)) {
76088c2ecf20Sopenharmony_ci		memcpy(&hba->clk_scaling.saved_pwr_info.info,
76098c2ecf20Sopenharmony_ci			&hba->pwr_info,
76108c2ecf20Sopenharmony_ci			sizeof(struct ufs_pa_layer_attr));
76118c2ecf20Sopenharmony_ci		hba->clk_scaling.saved_pwr_info.is_valid = true;
76128c2ecf20Sopenharmony_ci		if (!hba->devfreq) {
76138c2ecf20Sopenharmony_ci			ret = ufshcd_devfreq_init(hba);
76148c2ecf20Sopenharmony_ci			if (ret)
76158c2ecf20Sopenharmony_ci				goto out;
76168c2ecf20Sopenharmony_ci		}
76178c2ecf20Sopenharmony_ci
76188c2ecf20Sopenharmony_ci		hba->clk_scaling.is_allowed = true;
76198c2ecf20Sopenharmony_ci	}
76208c2ecf20Sopenharmony_ci
76218c2ecf20Sopenharmony_ci	ufs_bsg_probe(hba);
76228c2ecf20Sopenharmony_ci	scsi_scan_host(hba->host);
76238c2ecf20Sopenharmony_ci	pm_runtime_put_sync(hba->dev);
76248c2ecf20Sopenharmony_ci
76258c2ecf20Sopenharmony_ciout:
76268c2ecf20Sopenharmony_ci	return ret;
76278c2ecf20Sopenharmony_ci}
76288c2ecf20Sopenharmony_ci
76298c2ecf20Sopenharmony_cistatic int
76308c2ecf20Sopenharmony_ciufshcd_send_request_sense(struct ufs_hba *hba, struct scsi_device *sdp);
76318c2ecf20Sopenharmony_ci
76328c2ecf20Sopenharmony_cistatic int ufshcd_clear_ua_wlun(struct ufs_hba *hba, u8 wlun)
76338c2ecf20Sopenharmony_ci{
76348c2ecf20Sopenharmony_ci	struct scsi_device *sdp;
76358c2ecf20Sopenharmony_ci	unsigned long flags;
76368c2ecf20Sopenharmony_ci	int ret = 0;
76378c2ecf20Sopenharmony_ci
76388c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
76398c2ecf20Sopenharmony_ci	if (wlun == UFS_UPIU_UFS_DEVICE_WLUN)
76408c2ecf20Sopenharmony_ci		sdp = hba->sdev_ufs_device;
76418c2ecf20Sopenharmony_ci	else if (wlun == UFS_UPIU_RPMB_WLUN)
76428c2ecf20Sopenharmony_ci		sdp = hba->sdev_rpmb;
76438c2ecf20Sopenharmony_ci	else
76448c2ecf20Sopenharmony_ci		BUG();
76458c2ecf20Sopenharmony_ci	if (sdp) {
76468c2ecf20Sopenharmony_ci		ret = scsi_device_get(sdp);
76478c2ecf20Sopenharmony_ci		if (!ret && !scsi_device_online(sdp)) {
76488c2ecf20Sopenharmony_ci			ret = -ENODEV;
76498c2ecf20Sopenharmony_ci			scsi_device_put(sdp);
76508c2ecf20Sopenharmony_ci		}
76518c2ecf20Sopenharmony_ci	} else {
76528c2ecf20Sopenharmony_ci		ret = -ENODEV;
76538c2ecf20Sopenharmony_ci	}
76548c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
76558c2ecf20Sopenharmony_ci	if (ret)
76568c2ecf20Sopenharmony_ci		goto out_err;
76578c2ecf20Sopenharmony_ci
76588c2ecf20Sopenharmony_ci	ret = ufshcd_send_request_sense(hba, sdp);
76598c2ecf20Sopenharmony_ci	scsi_device_put(sdp);
76608c2ecf20Sopenharmony_ciout_err:
76618c2ecf20Sopenharmony_ci	if (ret)
76628c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: UAC clear LU=%x ret = %d\n",
76638c2ecf20Sopenharmony_ci				__func__, wlun, ret);
76648c2ecf20Sopenharmony_ci	return ret;
76658c2ecf20Sopenharmony_ci}
76668c2ecf20Sopenharmony_ci
76678c2ecf20Sopenharmony_cistatic int ufshcd_clear_ua_wluns(struct ufs_hba *hba)
76688c2ecf20Sopenharmony_ci{
76698c2ecf20Sopenharmony_ci	int ret = 0;
76708c2ecf20Sopenharmony_ci
76718c2ecf20Sopenharmony_ci	if (!hba->wlun_dev_clr_ua)
76728c2ecf20Sopenharmony_ci		goto out;
76738c2ecf20Sopenharmony_ci
76748c2ecf20Sopenharmony_ci	ret = ufshcd_clear_ua_wlun(hba, UFS_UPIU_UFS_DEVICE_WLUN);
76758c2ecf20Sopenharmony_ci	if (!ret)
76768c2ecf20Sopenharmony_ci		ret = ufshcd_clear_ua_wlun(hba, UFS_UPIU_RPMB_WLUN);
76778c2ecf20Sopenharmony_ci	if (!ret)
76788c2ecf20Sopenharmony_ci		hba->wlun_dev_clr_ua = false;
76798c2ecf20Sopenharmony_ciout:
76808c2ecf20Sopenharmony_ci	if (ret)
76818c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: Failed to clear UAC WLUNS ret = %d\n",
76828c2ecf20Sopenharmony_ci				__func__, ret);
76838c2ecf20Sopenharmony_ci	return ret;
76848c2ecf20Sopenharmony_ci}
76858c2ecf20Sopenharmony_ci
76868c2ecf20Sopenharmony_ci/**
76878c2ecf20Sopenharmony_ci * ufshcd_probe_hba - probe hba to detect device and initialize
76888c2ecf20Sopenharmony_ci * @hba: per-adapter instance
76898c2ecf20Sopenharmony_ci * @async: asynchronous execution or not
76908c2ecf20Sopenharmony_ci *
76918c2ecf20Sopenharmony_ci * Execute link-startup and verify device initialization
76928c2ecf20Sopenharmony_ci */
76938c2ecf20Sopenharmony_cistatic int ufshcd_probe_hba(struct ufs_hba *hba, bool async)
76948c2ecf20Sopenharmony_ci{
76958c2ecf20Sopenharmony_ci	int ret;
76968c2ecf20Sopenharmony_ci	unsigned long flags;
76978c2ecf20Sopenharmony_ci	ktime_t start = ktime_get();
76988c2ecf20Sopenharmony_ci
76998c2ecf20Sopenharmony_ci	ret = ufshcd_link_startup(hba);
77008c2ecf20Sopenharmony_ci	if (ret)
77018c2ecf20Sopenharmony_ci		goto out;
77028c2ecf20Sopenharmony_ci
77038c2ecf20Sopenharmony_ci	/* Debug counters initialization */
77048c2ecf20Sopenharmony_ci	ufshcd_clear_dbg_ufs_stats(hba);
77058c2ecf20Sopenharmony_ci
77068c2ecf20Sopenharmony_ci	/* UniPro link is active now */
77078c2ecf20Sopenharmony_ci	ufshcd_set_link_active(hba);
77088c2ecf20Sopenharmony_ci
77098c2ecf20Sopenharmony_ci	/* Verify device initialization by sending NOP OUT UPIU */
77108c2ecf20Sopenharmony_ci	ret = ufshcd_verify_dev_init(hba);
77118c2ecf20Sopenharmony_ci	if (ret)
77128c2ecf20Sopenharmony_ci		goto out;
77138c2ecf20Sopenharmony_ci
77148c2ecf20Sopenharmony_ci	/* Initiate UFS initialization, and waiting until completion */
77158c2ecf20Sopenharmony_ci	ret = ufshcd_complete_dev_init(hba);
77168c2ecf20Sopenharmony_ci	if (ret)
77178c2ecf20Sopenharmony_ci		goto out;
77188c2ecf20Sopenharmony_ci
77198c2ecf20Sopenharmony_ci	/*
77208c2ecf20Sopenharmony_ci	 * Initialize UFS device parameters used by driver, these
77218c2ecf20Sopenharmony_ci	 * parameters are associated with UFS descriptors.
77228c2ecf20Sopenharmony_ci	 */
77238c2ecf20Sopenharmony_ci	if (async) {
77248c2ecf20Sopenharmony_ci		ret = ufshcd_device_params_init(hba);
77258c2ecf20Sopenharmony_ci		if (ret)
77268c2ecf20Sopenharmony_ci			goto out;
77278c2ecf20Sopenharmony_ci	}
77288c2ecf20Sopenharmony_ci
77298c2ecf20Sopenharmony_ci	ufshcd_tune_unipro_params(hba);
77308c2ecf20Sopenharmony_ci
77318c2ecf20Sopenharmony_ci	/* UFS device is also active now */
77328c2ecf20Sopenharmony_ci	ufshcd_set_ufs_dev_active(hba);
77338c2ecf20Sopenharmony_ci	ufshcd_force_reset_auto_bkops(hba);
77348c2ecf20Sopenharmony_ci	hba->wlun_dev_clr_ua = true;
77358c2ecf20Sopenharmony_ci
77368c2ecf20Sopenharmony_ci	/* Gear up to HS gear if supported */
77378c2ecf20Sopenharmony_ci	if (hba->max_pwr_info.is_valid) {
77388c2ecf20Sopenharmony_ci		/*
77398c2ecf20Sopenharmony_ci		 * Set the right value to bRefClkFreq before attempting to
77408c2ecf20Sopenharmony_ci		 * switch to HS gears.
77418c2ecf20Sopenharmony_ci		 */
77428c2ecf20Sopenharmony_ci		if (hba->dev_ref_clk_freq != REF_CLK_FREQ_INVAL)
77438c2ecf20Sopenharmony_ci			ufshcd_set_dev_ref_clk(hba);
77448c2ecf20Sopenharmony_ci		ret = ufshcd_config_pwr_mode(hba, &hba->max_pwr_info.info);
77458c2ecf20Sopenharmony_ci		if (ret) {
77468c2ecf20Sopenharmony_ci			dev_err(hba->dev, "%s: Failed setting power mode, err = %d\n",
77478c2ecf20Sopenharmony_ci					__func__, ret);
77488c2ecf20Sopenharmony_ci			goto out;
77498c2ecf20Sopenharmony_ci		}
77508c2ecf20Sopenharmony_ci		ufshcd_print_pwr_info(hba);
77518c2ecf20Sopenharmony_ci	}
77528c2ecf20Sopenharmony_ci
77538c2ecf20Sopenharmony_ci	/*
77548c2ecf20Sopenharmony_ci	 * bActiveICCLevel is volatile for UFS device (as per latest v2.1 spec)
77558c2ecf20Sopenharmony_ci	 * and for removable UFS card as well, hence always set the parameter.
77568c2ecf20Sopenharmony_ci	 * Note: Error handler may issue the device reset hence resetting
77578c2ecf20Sopenharmony_ci	 * bActiveICCLevel as well so it is always safe to set this here.
77588c2ecf20Sopenharmony_ci	 */
77598c2ecf20Sopenharmony_ci	ufshcd_set_active_icc_lvl(hba);
77608c2ecf20Sopenharmony_ci
77618c2ecf20Sopenharmony_ci	ufshcd_wb_config(hba);
77628c2ecf20Sopenharmony_ci	/* Enable Auto-Hibernate if configured */
77638c2ecf20Sopenharmony_ci	ufshcd_auto_hibern8_enable(hba);
77648c2ecf20Sopenharmony_ci
77658c2ecf20Sopenharmony_ciout:
77668c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
77678c2ecf20Sopenharmony_ci	if (ret)
77688c2ecf20Sopenharmony_ci		hba->ufshcd_state = UFSHCD_STATE_ERROR;
77698c2ecf20Sopenharmony_ci	else if (hba->ufshcd_state == UFSHCD_STATE_RESET)
77708c2ecf20Sopenharmony_ci		hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL;
77718c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
77728c2ecf20Sopenharmony_ci
77738c2ecf20Sopenharmony_ci	trace_ufshcd_init(dev_name(hba->dev), ret,
77748c2ecf20Sopenharmony_ci		ktime_to_us(ktime_sub(ktime_get(), start)),
77758c2ecf20Sopenharmony_ci		hba->curr_dev_pwr_mode, hba->uic_link_state);
77768c2ecf20Sopenharmony_ci	return ret;
77778c2ecf20Sopenharmony_ci}
77788c2ecf20Sopenharmony_ci
77798c2ecf20Sopenharmony_ci/**
77808c2ecf20Sopenharmony_ci * ufshcd_async_scan - asynchronous execution for probing hba
77818c2ecf20Sopenharmony_ci * @data: data pointer to pass to this function
77828c2ecf20Sopenharmony_ci * @cookie: cookie data
77838c2ecf20Sopenharmony_ci */
77848c2ecf20Sopenharmony_cistatic void ufshcd_async_scan(void *data, async_cookie_t cookie)
77858c2ecf20Sopenharmony_ci{
77868c2ecf20Sopenharmony_ci	struct ufs_hba *hba = (struct ufs_hba *)data;
77878c2ecf20Sopenharmony_ci	int ret;
77888c2ecf20Sopenharmony_ci
77898c2ecf20Sopenharmony_ci	/* Initialize hba, detect and initialize UFS device */
77908c2ecf20Sopenharmony_ci	ret = ufshcd_probe_hba(hba, true);
77918c2ecf20Sopenharmony_ci	if (ret)
77928c2ecf20Sopenharmony_ci		goto out;
77938c2ecf20Sopenharmony_ci
77948c2ecf20Sopenharmony_ci	/* Probe and add UFS logical units  */
77958c2ecf20Sopenharmony_ci	ret = ufshcd_add_lus(hba);
77968c2ecf20Sopenharmony_ciout:
77978c2ecf20Sopenharmony_ci	/*
77988c2ecf20Sopenharmony_ci	 * If we failed to initialize the device or the device is not
77998c2ecf20Sopenharmony_ci	 * present, turn off the power/clocks etc.
78008c2ecf20Sopenharmony_ci	 */
78018c2ecf20Sopenharmony_ci	if (ret) {
78028c2ecf20Sopenharmony_ci		pm_runtime_put_sync(hba->dev);
78038c2ecf20Sopenharmony_ci		ufshcd_exit_clk_scaling(hba);
78048c2ecf20Sopenharmony_ci		ufshcd_hba_exit(hba);
78058c2ecf20Sopenharmony_ci	} else {
78068c2ecf20Sopenharmony_ci		ufshcd_clear_ua_wluns(hba);
78078c2ecf20Sopenharmony_ci	}
78088c2ecf20Sopenharmony_ci}
78098c2ecf20Sopenharmony_ci
78108c2ecf20Sopenharmony_cistatic const struct attribute_group *ufshcd_driver_groups[] = {
78118c2ecf20Sopenharmony_ci	&ufs_sysfs_unit_descriptor_group,
78128c2ecf20Sopenharmony_ci	&ufs_sysfs_lun_attributes_group,
78138c2ecf20Sopenharmony_ci	NULL,
78148c2ecf20Sopenharmony_ci};
78158c2ecf20Sopenharmony_ci
78168c2ecf20Sopenharmony_cistatic struct ufs_hba_variant_params ufs_hba_vps = {
78178c2ecf20Sopenharmony_ci	.hba_enable_delay_us		= 1000,
78188c2ecf20Sopenharmony_ci	.wb_flush_threshold		= UFS_WB_BUF_REMAIN_PERCENT(40),
78198c2ecf20Sopenharmony_ci	.devfreq_profile.polling_ms	= 100,
78208c2ecf20Sopenharmony_ci	.devfreq_profile.target		= ufshcd_devfreq_target,
78218c2ecf20Sopenharmony_ci	.devfreq_profile.get_dev_status	= ufshcd_devfreq_get_dev_status,
78228c2ecf20Sopenharmony_ci	.ondemand_data.upthreshold	= 70,
78238c2ecf20Sopenharmony_ci	.ondemand_data.downdifferential	= 5,
78248c2ecf20Sopenharmony_ci};
78258c2ecf20Sopenharmony_ci
78268c2ecf20Sopenharmony_cistatic struct scsi_host_template ufshcd_driver_template = {
78278c2ecf20Sopenharmony_ci	.module			= THIS_MODULE,
78288c2ecf20Sopenharmony_ci	.name			= UFSHCD,
78298c2ecf20Sopenharmony_ci	.proc_name		= UFSHCD,
78308c2ecf20Sopenharmony_ci	.queuecommand		= ufshcd_queuecommand,
78318c2ecf20Sopenharmony_ci	.slave_alloc		= ufshcd_slave_alloc,
78328c2ecf20Sopenharmony_ci	.slave_configure	= ufshcd_slave_configure,
78338c2ecf20Sopenharmony_ci	.slave_destroy		= ufshcd_slave_destroy,
78348c2ecf20Sopenharmony_ci	.change_queue_depth	= ufshcd_change_queue_depth,
78358c2ecf20Sopenharmony_ci	.eh_abort_handler	= ufshcd_abort,
78368c2ecf20Sopenharmony_ci	.eh_device_reset_handler = ufshcd_eh_device_reset_handler,
78378c2ecf20Sopenharmony_ci	.eh_host_reset_handler   = ufshcd_eh_host_reset_handler,
78388c2ecf20Sopenharmony_ci	.this_id		= -1,
78398c2ecf20Sopenharmony_ci	.sg_tablesize		= SG_ALL,
78408c2ecf20Sopenharmony_ci	.cmd_per_lun		= UFSHCD_CMD_PER_LUN,
78418c2ecf20Sopenharmony_ci	.can_queue		= UFSHCD_CAN_QUEUE,
78428c2ecf20Sopenharmony_ci	.max_segment_size	= PRDT_DATA_BYTE_COUNT_MAX,
78438c2ecf20Sopenharmony_ci	.max_host_blocked	= 1,
78448c2ecf20Sopenharmony_ci	.track_queue_depth	= 1,
78458c2ecf20Sopenharmony_ci	.sdev_groups		= ufshcd_driver_groups,
78468c2ecf20Sopenharmony_ci	.dma_boundary		= PAGE_SIZE - 1,
78478c2ecf20Sopenharmony_ci	.rpm_autosuspend_delay	= RPM_AUTOSUSPEND_DELAY_MS,
78488c2ecf20Sopenharmony_ci};
78498c2ecf20Sopenharmony_ci
78508c2ecf20Sopenharmony_cistatic int ufshcd_config_vreg_load(struct device *dev, struct ufs_vreg *vreg,
78518c2ecf20Sopenharmony_ci				   int ua)
78528c2ecf20Sopenharmony_ci{
78538c2ecf20Sopenharmony_ci	int ret;
78548c2ecf20Sopenharmony_ci
78558c2ecf20Sopenharmony_ci	if (!vreg)
78568c2ecf20Sopenharmony_ci		return 0;
78578c2ecf20Sopenharmony_ci
78588c2ecf20Sopenharmony_ci	/*
78598c2ecf20Sopenharmony_ci	 * "set_load" operation shall be required on those regulators
78608c2ecf20Sopenharmony_ci	 * which specifically configured current limitation. Otherwise
78618c2ecf20Sopenharmony_ci	 * zero max_uA may cause unexpected behavior when regulator is
78628c2ecf20Sopenharmony_ci	 * enabled or set as high power mode.
78638c2ecf20Sopenharmony_ci	 */
78648c2ecf20Sopenharmony_ci	if (!vreg->max_uA)
78658c2ecf20Sopenharmony_ci		return 0;
78668c2ecf20Sopenharmony_ci
78678c2ecf20Sopenharmony_ci	ret = regulator_set_load(vreg->reg, ua);
78688c2ecf20Sopenharmony_ci	if (ret < 0) {
78698c2ecf20Sopenharmony_ci		dev_err(dev, "%s: %s set load (ua=%d) failed, err=%d\n",
78708c2ecf20Sopenharmony_ci				__func__, vreg->name, ua, ret);
78718c2ecf20Sopenharmony_ci	}
78728c2ecf20Sopenharmony_ci
78738c2ecf20Sopenharmony_ci	return ret;
78748c2ecf20Sopenharmony_ci}
78758c2ecf20Sopenharmony_ci
78768c2ecf20Sopenharmony_cistatic inline int ufshcd_config_vreg_lpm(struct ufs_hba *hba,
78778c2ecf20Sopenharmony_ci					 struct ufs_vreg *vreg)
78788c2ecf20Sopenharmony_ci{
78798c2ecf20Sopenharmony_ci	return ufshcd_config_vreg_load(hba->dev, vreg, UFS_VREG_LPM_LOAD_UA);
78808c2ecf20Sopenharmony_ci}
78818c2ecf20Sopenharmony_ci
78828c2ecf20Sopenharmony_cistatic inline int ufshcd_config_vreg_hpm(struct ufs_hba *hba,
78838c2ecf20Sopenharmony_ci					 struct ufs_vreg *vreg)
78848c2ecf20Sopenharmony_ci{
78858c2ecf20Sopenharmony_ci	if (!vreg)
78868c2ecf20Sopenharmony_ci		return 0;
78878c2ecf20Sopenharmony_ci
78888c2ecf20Sopenharmony_ci	return ufshcd_config_vreg_load(hba->dev, vreg, vreg->max_uA);
78898c2ecf20Sopenharmony_ci}
78908c2ecf20Sopenharmony_ci
78918c2ecf20Sopenharmony_cistatic int ufshcd_config_vreg(struct device *dev,
78928c2ecf20Sopenharmony_ci		struct ufs_vreg *vreg, bool on)
78938c2ecf20Sopenharmony_ci{
78948c2ecf20Sopenharmony_ci	int ret = 0;
78958c2ecf20Sopenharmony_ci	struct regulator *reg;
78968c2ecf20Sopenharmony_ci	const char *name;
78978c2ecf20Sopenharmony_ci	int min_uV, uA_load;
78988c2ecf20Sopenharmony_ci
78998c2ecf20Sopenharmony_ci	BUG_ON(!vreg);
79008c2ecf20Sopenharmony_ci
79018c2ecf20Sopenharmony_ci	reg = vreg->reg;
79028c2ecf20Sopenharmony_ci	name = vreg->name;
79038c2ecf20Sopenharmony_ci
79048c2ecf20Sopenharmony_ci	if (regulator_count_voltages(reg) > 0) {
79058c2ecf20Sopenharmony_ci		uA_load = on ? vreg->max_uA : 0;
79068c2ecf20Sopenharmony_ci		ret = ufshcd_config_vreg_load(dev, vreg, uA_load);
79078c2ecf20Sopenharmony_ci		if (ret)
79088c2ecf20Sopenharmony_ci			goto out;
79098c2ecf20Sopenharmony_ci
79108c2ecf20Sopenharmony_ci		if (vreg->min_uV && vreg->max_uV) {
79118c2ecf20Sopenharmony_ci			min_uV = on ? vreg->min_uV : 0;
79128c2ecf20Sopenharmony_ci			ret = regulator_set_voltage(reg, min_uV, vreg->max_uV);
79138c2ecf20Sopenharmony_ci			if (ret)
79148c2ecf20Sopenharmony_ci				dev_err(dev,
79158c2ecf20Sopenharmony_ci					"%s: %s set voltage failed, err=%d\n",
79168c2ecf20Sopenharmony_ci					__func__, name, ret);
79178c2ecf20Sopenharmony_ci		}
79188c2ecf20Sopenharmony_ci	}
79198c2ecf20Sopenharmony_ciout:
79208c2ecf20Sopenharmony_ci	return ret;
79218c2ecf20Sopenharmony_ci}
79228c2ecf20Sopenharmony_ci
79238c2ecf20Sopenharmony_cistatic int ufshcd_enable_vreg(struct device *dev, struct ufs_vreg *vreg)
79248c2ecf20Sopenharmony_ci{
79258c2ecf20Sopenharmony_ci	int ret = 0;
79268c2ecf20Sopenharmony_ci
79278c2ecf20Sopenharmony_ci	if (!vreg || vreg->enabled)
79288c2ecf20Sopenharmony_ci		goto out;
79298c2ecf20Sopenharmony_ci
79308c2ecf20Sopenharmony_ci	ret = ufshcd_config_vreg(dev, vreg, true);
79318c2ecf20Sopenharmony_ci	if (!ret)
79328c2ecf20Sopenharmony_ci		ret = regulator_enable(vreg->reg);
79338c2ecf20Sopenharmony_ci
79348c2ecf20Sopenharmony_ci	if (!ret)
79358c2ecf20Sopenharmony_ci		vreg->enabled = true;
79368c2ecf20Sopenharmony_ci	else
79378c2ecf20Sopenharmony_ci		dev_err(dev, "%s: %s enable failed, err=%d\n",
79388c2ecf20Sopenharmony_ci				__func__, vreg->name, ret);
79398c2ecf20Sopenharmony_ciout:
79408c2ecf20Sopenharmony_ci	return ret;
79418c2ecf20Sopenharmony_ci}
79428c2ecf20Sopenharmony_ci
79438c2ecf20Sopenharmony_cistatic int ufshcd_disable_vreg(struct device *dev, struct ufs_vreg *vreg)
79448c2ecf20Sopenharmony_ci{
79458c2ecf20Sopenharmony_ci	int ret = 0;
79468c2ecf20Sopenharmony_ci
79478c2ecf20Sopenharmony_ci	if (!vreg || !vreg->enabled)
79488c2ecf20Sopenharmony_ci		goto out;
79498c2ecf20Sopenharmony_ci
79508c2ecf20Sopenharmony_ci	ret = regulator_disable(vreg->reg);
79518c2ecf20Sopenharmony_ci
79528c2ecf20Sopenharmony_ci	if (!ret) {
79538c2ecf20Sopenharmony_ci		/* ignore errors on applying disable config */
79548c2ecf20Sopenharmony_ci		ufshcd_config_vreg(dev, vreg, false);
79558c2ecf20Sopenharmony_ci		vreg->enabled = false;
79568c2ecf20Sopenharmony_ci	} else {
79578c2ecf20Sopenharmony_ci		dev_err(dev, "%s: %s disable failed, err=%d\n",
79588c2ecf20Sopenharmony_ci				__func__, vreg->name, ret);
79598c2ecf20Sopenharmony_ci	}
79608c2ecf20Sopenharmony_ciout:
79618c2ecf20Sopenharmony_ci	return ret;
79628c2ecf20Sopenharmony_ci}
79638c2ecf20Sopenharmony_ci
79648c2ecf20Sopenharmony_cistatic int ufshcd_setup_vreg(struct ufs_hba *hba, bool on)
79658c2ecf20Sopenharmony_ci{
79668c2ecf20Sopenharmony_ci	int ret = 0;
79678c2ecf20Sopenharmony_ci	struct device *dev = hba->dev;
79688c2ecf20Sopenharmony_ci	struct ufs_vreg_info *info = &hba->vreg_info;
79698c2ecf20Sopenharmony_ci
79708c2ecf20Sopenharmony_ci	ret = ufshcd_toggle_vreg(dev, info->vcc, on);
79718c2ecf20Sopenharmony_ci	if (ret)
79728c2ecf20Sopenharmony_ci		goto out;
79738c2ecf20Sopenharmony_ci
79748c2ecf20Sopenharmony_ci	ret = ufshcd_toggle_vreg(dev, info->vccq, on);
79758c2ecf20Sopenharmony_ci	if (ret)
79768c2ecf20Sopenharmony_ci		goto out;
79778c2ecf20Sopenharmony_ci
79788c2ecf20Sopenharmony_ci	ret = ufshcd_toggle_vreg(dev, info->vccq2, on);
79798c2ecf20Sopenharmony_ci
79808c2ecf20Sopenharmony_ciout:
79818c2ecf20Sopenharmony_ci	if (ret) {
79828c2ecf20Sopenharmony_ci		ufshcd_toggle_vreg(dev, info->vccq2, false);
79838c2ecf20Sopenharmony_ci		ufshcd_toggle_vreg(dev, info->vccq, false);
79848c2ecf20Sopenharmony_ci		ufshcd_toggle_vreg(dev, info->vcc, false);
79858c2ecf20Sopenharmony_ci	}
79868c2ecf20Sopenharmony_ci	return ret;
79878c2ecf20Sopenharmony_ci}
79888c2ecf20Sopenharmony_ci
79898c2ecf20Sopenharmony_cistatic int ufshcd_setup_hba_vreg(struct ufs_hba *hba, bool on)
79908c2ecf20Sopenharmony_ci{
79918c2ecf20Sopenharmony_ci	struct ufs_vreg_info *info = &hba->vreg_info;
79928c2ecf20Sopenharmony_ci
79938c2ecf20Sopenharmony_ci	return ufshcd_toggle_vreg(hba->dev, info->vdd_hba, on);
79948c2ecf20Sopenharmony_ci}
79958c2ecf20Sopenharmony_ci
79968c2ecf20Sopenharmony_cistatic int ufshcd_get_vreg(struct device *dev, struct ufs_vreg *vreg)
79978c2ecf20Sopenharmony_ci{
79988c2ecf20Sopenharmony_ci	int ret = 0;
79998c2ecf20Sopenharmony_ci
80008c2ecf20Sopenharmony_ci	if (!vreg)
80018c2ecf20Sopenharmony_ci		goto out;
80028c2ecf20Sopenharmony_ci
80038c2ecf20Sopenharmony_ci	vreg->reg = devm_regulator_get(dev, vreg->name);
80048c2ecf20Sopenharmony_ci	if (IS_ERR(vreg->reg)) {
80058c2ecf20Sopenharmony_ci		ret = PTR_ERR(vreg->reg);
80068c2ecf20Sopenharmony_ci		dev_err(dev, "%s: %s get failed, err=%d\n",
80078c2ecf20Sopenharmony_ci				__func__, vreg->name, ret);
80088c2ecf20Sopenharmony_ci	}
80098c2ecf20Sopenharmony_ciout:
80108c2ecf20Sopenharmony_ci	return ret;
80118c2ecf20Sopenharmony_ci}
80128c2ecf20Sopenharmony_ci
80138c2ecf20Sopenharmony_cistatic int ufshcd_init_vreg(struct ufs_hba *hba)
80148c2ecf20Sopenharmony_ci{
80158c2ecf20Sopenharmony_ci	int ret = 0;
80168c2ecf20Sopenharmony_ci	struct device *dev = hba->dev;
80178c2ecf20Sopenharmony_ci	struct ufs_vreg_info *info = &hba->vreg_info;
80188c2ecf20Sopenharmony_ci
80198c2ecf20Sopenharmony_ci	ret = ufshcd_get_vreg(dev, info->vcc);
80208c2ecf20Sopenharmony_ci	if (ret)
80218c2ecf20Sopenharmony_ci		goto out;
80228c2ecf20Sopenharmony_ci
80238c2ecf20Sopenharmony_ci	ret = ufshcd_get_vreg(dev, info->vccq);
80248c2ecf20Sopenharmony_ci	if (!ret)
80258c2ecf20Sopenharmony_ci		ret = ufshcd_get_vreg(dev, info->vccq2);
80268c2ecf20Sopenharmony_ciout:
80278c2ecf20Sopenharmony_ci	return ret;
80288c2ecf20Sopenharmony_ci}
80298c2ecf20Sopenharmony_ci
80308c2ecf20Sopenharmony_cistatic int ufshcd_init_hba_vreg(struct ufs_hba *hba)
80318c2ecf20Sopenharmony_ci{
80328c2ecf20Sopenharmony_ci	struct ufs_vreg_info *info = &hba->vreg_info;
80338c2ecf20Sopenharmony_ci
80348c2ecf20Sopenharmony_ci	if (info)
80358c2ecf20Sopenharmony_ci		return ufshcd_get_vreg(hba->dev, info->vdd_hba);
80368c2ecf20Sopenharmony_ci
80378c2ecf20Sopenharmony_ci	return 0;
80388c2ecf20Sopenharmony_ci}
80398c2ecf20Sopenharmony_ci
80408c2ecf20Sopenharmony_cistatic int ufshcd_setup_clocks(struct ufs_hba *hba, bool on)
80418c2ecf20Sopenharmony_ci{
80428c2ecf20Sopenharmony_ci	int ret = 0;
80438c2ecf20Sopenharmony_ci	struct ufs_clk_info *clki;
80448c2ecf20Sopenharmony_ci	struct list_head *head = &hba->clk_list_head;
80458c2ecf20Sopenharmony_ci	unsigned long flags;
80468c2ecf20Sopenharmony_ci	ktime_t start = ktime_get();
80478c2ecf20Sopenharmony_ci	bool clk_state_changed = false;
80488c2ecf20Sopenharmony_ci
80498c2ecf20Sopenharmony_ci	if (list_empty(head))
80508c2ecf20Sopenharmony_ci		goto out;
80518c2ecf20Sopenharmony_ci
80528c2ecf20Sopenharmony_ci	ret = ufshcd_vops_setup_clocks(hba, on, PRE_CHANGE);
80538c2ecf20Sopenharmony_ci	if (ret)
80548c2ecf20Sopenharmony_ci		return ret;
80558c2ecf20Sopenharmony_ci
80568c2ecf20Sopenharmony_ci	list_for_each_entry(clki, head, list) {
80578c2ecf20Sopenharmony_ci		if (!IS_ERR_OR_NULL(clki->clk)) {
80588c2ecf20Sopenharmony_ci			/*
80598c2ecf20Sopenharmony_ci			 * Don't disable clocks which are needed
80608c2ecf20Sopenharmony_ci			 * to keep the link active.
80618c2ecf20Sopenharmony_ci			 */
80628c2ecf20Sopenharmony_ci			if (ufshcd_is_link_active(hba) &&
80638c2ecf20Sopenharmony_ci			    clki->keep_link_active)
80648c2ecf20Sopenharmony_ci				continue;
80658c2ecf20Sopenharmony_ci
80668c2ecf20Sopenharmony_ci			clk_state_changed = on ^ clki->enabled;
80678c2ecf20Sopenharmony_ci			if (on && !clki->enabled) {
80688c2ecf20Sopenharmony_ci				ret = clk_prepare_enable(clki->clk);
80698c2ecf20Sopenharmony_ci				if (ret) {
80708c2ecf20Sopenharmony_ci					dev_err(hba->dev, "%s: %s prepare enable failed, %d\n",
80718c2ecf20Sopenharmony_ci						__func__, clki->name, ret);
80728c2ecf20Sopenharmony_ci					goto out;
80738c2ecf20Sopenharmony_ci				}
80748c2ecf20Sopenharmony_ci			} else if (!on && clki->enabled) {
80758c2ecf20Sopenharmony_ci				clk_disable_unprepare(clki->clk);
80768c2ecf20Sopenharmony_ci			}
80778c2ecf20Sopenharmony_ci			clki->enabled = on;
80788c2ecf20Sopenharmony_ci			dev_dbg(hba->dev, "%s: clk: %s %sabled\n", __func__,
80798c2ecf20Sopenharmony_ci					clki->name, on ? "en" : "dis");
80808c2ecf20Sopenharmony_ci		}
80818c2ecf20Sopenharmony_ci	}
80828c2ecf20Sopenharmony_ci
80838c2ecf20Sopenharmony_ci	ret = ufshcd_vops_setup_clocks(hba, on, POST_CHANGE);
80848c2ecf20Sopenharmony_ci	if (ret)
80858c2ecf20Sopenharmony_ci		return ret;
80868c2ecf20Sopenharmony_ci
80878c2ecf20Sopenharmony_ciout:
80888c2ecf20Sopenharmony_ci	if (ret) {
80898c2ecf20Sopenharmony_ci		list_for_each_entry(clki, head, list) {
80908c2ecf20Sopenharmony_ci			if (!IS_ERR_OR_NULL(clki->clk) && clki->enabled)
80918c2ecf20Sopenharmony_ci				clk_disable_unprepare(clki->clk);
80928c2ecf20Sopenharmony_ci		}
80938c2ecf20Sopenharmony_ci	} else if (!ret && on) {
80948c2ecf20Sopenharmony_ci		spin_lock_irqsave(hba->host->host_lock, flags);
80958c2ecf20Sopenharmony_ci		hba->clk_gating.state = CLKS_ON;
80968c2ecf20Sopenharmony_ci		trace_ufshcd_clk_gating(dev_name(hba->dev),
80978c2ecf20Sopenharmony_ci					hba->clk_gating.state);
80988c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(hba->host->host_lock, flags);
80998c2ecf20Sopenharmony_ci	}
81008c2ecf20Sopenharmony_ci
81018c2ecf20Sopenharmony_ci	if (clk_state_changed)
81028c2ecf20Sopenharmony_ci		trace_ufshcd_profile_clk_gating(dev_name(hba->dev),
81038c2ecf20Sopenharmony_ci			(on ? "on" : "off"),
81048c2ecf20Sopenharmony_ci			ktime_to_us(ktime_sub(ktime_get(), start)), ret);
81058c2ecf20Sopenharmony_ci	return ret;
81068c2ecf20Sopenharmony_ci}
81078c2ecf20Sopenharmony_ci
81088c2ecf20Sopenharmony_cistatic int ufshcd_init_clocks(struct ufs_hba *hba)
81098c2ecf20Sopenharmony_ci{
81108c2ecf20Sopenharmony_ci	int ret = 0;
81118c2ecf20Sopenharmony_ci	struct ufs_clk_info *clki;
81128c2ecf20Sopenharmony_ci	struct device *dev = hba->dev;
81138c2ecf20Sopenharmony_ci	struct list_head *head = &hba->clk_list_head;
81148c2ecf20Sopenharmony_ci
81158c2ecf20Sopenharmony_ci	if (list_empty(head))
81168c2ecf20Sopenharmony_ci		goto out;
81178c2ecf20Sopenharmony_ci
81188c2ecf20Sopenharmony_ci	list_for_each_entry(clki, head, list) {
81198c2ecf20Sopenharmony_ci		if (!clki->name)
81208c2ecf20Sopenharmony_ci			continue;
81218c2ecf20Sopenharmony_ci
81228c2ecf20Sopenharmony_ci		clki->clk = devm_clk_get(dev, clki->name);
81238c2ecf20Sopenharmony_ci		if (IS_ERR(clki->clk)) {
81248c2ecf20Sopenharmony_ci			ret = PTR_ERR(clki->clk);
81258c2ecf20Sopenharmony_ci			dev_err(dev, "%s: %s clk get failed, %d\n",
81268c2ecf20Sopenharmony_ci					__func__, clki->name, ret);
81278c2ecf20Sopenharmony_ci			goto out;
81288c2ecf20Sopenharmony_ci		}
81298c2ecf20Sopenharmony_ci
81308c2ecf20Sopenharmony_ci		/*
81318c2ecf20Sopenharmony_ci		 * Parse device ref clk freq as per device tree "ref_clk".
81328c2ecf20Sopenharmony_ci		 * Default dev_ref_clk_freq is set to REF_CLK_FREQ_INVAL
81338c2ecf20Sopenharmony_ci		 * in ufshcd_alloc_host().
81348c2ecf20Sopenharmony_ci		 */
81358c2ecf20Sopenharmony_ci		if (!strcmp(clki->name, "ref_clk"))
81368c2ecf20Sopenharmony_ci			ufshcd_parse_dev_ref_clk_freq(hba, clki->clk);
81378c2ecf20Sopenharmony_ci
81388c2ecf20Sopenharmony_ci		if (clki->max_freq) {
81398c2ecf20Sopenharmony_ci			ret = clk_set_rate(clki->clk, clki->max_freq);
81408c2ecf20Sopenharmony_ci			if (ret) {
81418c2ecf20Sopenharmony_ci				dev_err(hba->dev, "%s: %s clk set rate(%dHz) failed, %d\n",
81428c2ecf20Sopenharmony_ci					__func__, clki->name,
81438c2ecf20Sopenharmony_ci					clki->max_freq, ret);
81448c2ecf20Sopenharmony_ci				goto out;
81458c2ecf20Sopenharmony_ci			}
81468c2ecf20Sopenharmony_ci			clki->curr_freq = clki->max_freq;
81478c2ecf20Sopenharmony_ci		}
81488c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s: clk: %s, rate: %lu\n", __func__,
81498c2ecf20Sopenharmony_ci				clki->name, clk_get_rate(clki->clk));
81508c2ecf20Sopenharmony_ci	}
81518c2ecf20Sopenharmony_ciout:
81528c2ecf20Sopenharmony_ci	return ret;
81538c2ecf20Sopenharmony_ci}
81548c2ecf20Sopenharmony_ci
81558c2ecf20Sopenharmony_cistatic int ufshcd_variant_hba_init(struct ufs_hba *hba)
81568c2ecf20Sopenharmony_ci{
81578c2ecf20Sopenharmony_ci	int err = 0;
81588c2ecf20Sopenharmony_ci
81598c2ecf20Sopenharmony_ci	if (!hba->vops)
81608c2ecf20Sopenharmony_ci		goto out;
81618c2ecf20Sopenharmony_ci
81628c2ecf20Sopenharmony_ci	err = ufshcd_vops_init(hba);
81638c2ecf20Sopenharmony_ci	if (err)
81648c2ecf20Sopenharmony_ci		goto out;
81658c2ecf20Sopenharmony_ci
81668c2ecf20Sopenharmony_ci	err = ufshcd_vops_setup_regulators(hba, true);
81678c2ecf20Sopenharmony_ci	if (err)
81688c2ecf20Sopenharmony_ci		ufshcd_vops_exit(hba);
81698c2ecf20Sopenharmony_ciout:
81708c2ecf20Sopenharmony_ci	if (err)
81718c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: variant %s init failed err %d\n",
81728c2ecf20Sopenharmony_ci			__func__, ufshcd_get_var_name(hba), err);
81738c2ecf20Sopenharmony_ci	return err;
81748c2ecf20Sopenharmony_ci}
81758c2ecf20Sopenharmony_ci
81768c2ecf20Sopenharmony_cistatic void ufshcd_variant_hba_exit(struct ufs_hba *hba)
81778c2ecf20Sopenharmony_ci{
81788c2ecf20Sopenharmony_ci	if (!hba->vops)
81798c2ecf20Sopenharmony_ci		return;
81808c2ecf20Sopenharmony_ci
81818c2ecf20Sopenharmony_ci	ufshcd_vops_setup_regulators(hba, false);
81828c2ecf20Sopenharmony_ci
81838c2ecf20Sopenharmony_ci	ufshcd_vops_exit(hba);
81848c2ecf20Sopenharmony_ci}
81858c2ecf20Sopenharmony_ci
81868c2ecf20Sopenharmony_cistatic int ufshcd_hba_init(struct ufs_hba *hba)
81878c2ecf20Sopenharmony_ci{
81888c2ecf20Sopenharmony_ci	int err;
81898c2ecf20Sopenharmony_ci
81908c2ecf20Sopenharmony_ci	/*
81918c2ecf20Sopenharmony_ci	 * Handle host controller power separately from the UFS device power
81928c2ecf20Sopenharmony_ci	 * rails as it will help controlling the UFS host controller power
81938c2ecf20Sopenharmony_ci	 * collapse easily which is different than UFS device power collapse.
81948c2ecf20Sopenharmony_ci	 * Also, enable the host controller power before we go ahead with rest
81958c2ecf20Sopenharmony_ci	 * of the initialization here.
81968c2ecf20Sopenharmony_ci	 */
81978c2ecf20Sopenharmony_ci	err = ufshcd_init_hba_vreg(hba);
81988c2ecf20Sopenharmony_ci	if (err)
81998c2ecf20Sopenharmony_ci		goto out;
82008c2ecf20Sopenharmony_ci
82018c2ecf20Sopenharmony_ci	err = ufshcd_setup_hba_vreg(hba, true);
82028c2ecf20Sopenharmony_ci	if (err)
82038c2ecf20Sopenharmony_ci		goto out;
82048c2ecf20Sopenharmony_ci
82058c2ecf20Sopenharmony_ci	err = ufshcd_init_clocks(hba);
82068c2ecf20Sopenharmony_ci	if (err)
82078c2ecf20Sopenharmony_ci		goto out_disable_hba_vreg;
82088c2ecf20Sopenharmony_ci
82098c2ecf20Sopenharmony_ci	err = ufshcd_setup_clocks(hba, true);
82108c2ecf20Sopenharmony_ci	if (err)
82118c2ecf20Sopenharmony_ci		goto out_disable_hba_vreg;
82128c2ecf20Sopenharmony_ci
82138c2ecf20Sopenharmony_ci	err = ufshcd_init_vreg(hba);
82148c2ecf20Sopenharmony_ci	if (err)
82158c2ecf20Sopenharmony_ci		goto out_disable_clks;
82168c2ecf20Sopenharmony_ci
82178c2ecf20Sopenharmony_ci	err = ufshcd_setup_vreg(hba, true);
82188c2ecf20Sopenharmony_ci	if (err)
82198c2ecf20Sopenharmony_ci		goto out_disable_clks;
82208c2ecf20Sopenharmony_ci
82218c2ecf20Sopenharmony_ci	err = ufshcd_variant_hba_init(hba);
82228c2ecf20Sopenharmony_ci	if (err)
82238c2ecf20Sopenharmony_ci		goto out_disable_vreg;
82248c2ecf20Sopenharmony_ci
82258c2ecf20Sopenharmony_ci	hba->is_powered = true;
82268c2ecf20Sopenharmony_ci	goto out;
82278c2ecf20Sopenharmony_ci
82288c2ecf20Sopenharmony_ciout_disable_vreg:
82298c2ecf20Sopenharmony_ci	ufshcd_setup_vreg(hba, false);
82308c2ecf20Sopenharmony_ciout_disable_clks:
82318c2ecf20Sopenharmony_ci	ufshcd_setup_clocks(hba, false);
82328c2ecf20Sopenharmony_ciout_disable_hba_vreg:
82338c2ecf20Sopenharmony_ci	ufshcd_setup_hba_vreg(hba, false);
82348c2ecf20Sopenharmony_ciout:
82358c2ecf20Sopenharmony_ci	return err;
82368c2ecf20Sopenharmony_ci}
82378c2ecf20Sopenharmony_ci
82388c2ecf20Sopenharmony_cistatic void ufshcd_hba_exit(struct ufs_hba *hba)
82398c2ecf20Sopenharmony_ci{
82408c2ecf20Sopenharmony_ci	if (hba->is_powered) {
82418c2ecf20Sopenharmony_ci		ufshcd_variant_hba_exit(hba);
82428c2ecf20Sopenharmony_ci		ufshcd_setup_vreg(hba, false);
82438c2ecf20Sopenharmony_ci		ufshcd_suspend_clkscaling(hba);
82448c2ecf20Sopenharmony_ci		if (ufshcd_is_clkscaling_supported(hba))
82458c2ecf20Sopenharmony_ci			if (hba->devfreq)
82468c2ecf20Sopenharmony_ci				ufshcd_suspend_clkscaling(hba);
82478c2ecf20Sopenharmony_ci		ufshcd_setup_clocks(hba, false);
82488c2ecf20Sopenharmony_ci		ufshcd_setup_hba_vreg(hba, false);
82498c2ecf20Sopenharmony_ci		hba->is_powered = false;
82508c2ecf20Sopenharmony_ci		ufs_put_device_desc(hba);
82518c2ecf20Sopenharmony_ci	}
82528c2ecf20Sopenharmony_ci}
82538c2ecf20Sopenharmony_ci
82548c2ecf20Sopenharmony_cistatic int
82558c2ecf20Sopenharmony_ciufshcd_send_request_sense(struct ufs_hba *hba, struct scsi_device *sdp)
82568c2ecf20Sopenharmony_ci{
82578c2ecf20Sopenharmony_ci	unsigned char cmd[6] = {REQUEST_SENSE,
82588c2ecf20Sopenharmony_ci				0,
82598c2ecf20Sopenharmony_ci				0,
82608c2ecf20Sopenharmony_ci				0,
82618c2ecf20Sopenharmony_ci				UFS_SENSE_SIZE,
82628c2ecf20Sopenharmony_ci				0};
82638c2ecf20Sopenharmony_ci	char *buffer;
82648c2ecf20Sopenharmony_ci	int ret;
82658c2ecf20Sopenharmony_ci
82668c2ecf20Sopenharmony_ci	buffer = kzalloc(UFS_SENSE_SIZE, GFP_KERNEL);
82678c2ecf20Sopenharmony_ci	if (!buffer) {
82688c2ecf20Sopenharmony_ci		ret = -ENOMEM;
82698c2ecf20Sopenharmony_ci		goto out;
82708c2ecf20Sopenharmony_ci	}
82718c2ecf20Sopenharmony_ci
82728c2ecf20Sopenharmony_ci	ret = scsi_execute(sdp, cmd, DMA_FROM_DEVICE, buffer,
82738c2ecf20Sopenharmony_ci			UFS_SENSE_SIZE, NULL, NULL,
82748c2ecf20Sopenharmony_ci			msecs_to_jiffies(1000), 3, 0, RQF_PM, NULL);
82758c2ecf20Sopenharmony_ci	if (ret)
82768c2ecf20Sopenharmony_ci		pr_err("%s: failed with err %d\n", __func__, ret);
82778c2ecf20Sopenharmony_ci
82788c2ecf20Sopenharmony_ci	kfree(buffer);
82798c2ecf20Sopenharmony_ciout:
82808c2ecf20Sopenharmony_ci	return ret;
82818c2ecf20Sopenharmony_ci}
82828c2ecf20Sopenharmony_ci
82838c2ecf20Sopenharmony_ci/**
82848c2ecf20Sopenharmony_ci * ufshcd_set_dev_pwr_mode - sends START STOP UNIT command to set device
82858c2ecf20Sopenharmony_ci *			     power mode
82868c2ecf20Sopenharmony_ci * @hba: per adapter instance
82878c2ecf20Sopenharmony_ci * @pwr_mode: device power mode to set
82888c2ecf20Sopenharmony_ci *
82898c2ecf20Sopenharmony_ci * Returns 0 if requested power mode is set successfully
82908c2ecf20Sopenharmony_ci * Returns non-zero if failed to set the requested power mode
82918c2ecf20Sopenharmony_ci */
82928c2ecf20Sopenharmony_cistatic int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba,
82938c2ecf20Sopenharmony_ci				     enum ufs_dev_pwr_mode pwr_mode)
82948c2ecf20Sopenharmony_ci{
82958c2ecf20Sopenharmony_ci	unsigned char cmd[6] = { START_STOP };
82968c2ecf20Sopenharmony_ci	struct scsi_sense_hdr sshdr;
82978c2ecf20Sopenharmony_ci	struct scsi_device *sdp;
82988c2ecf20Sopenharmony_ci	unsigned long flags;
82998c2ecf20Sopenharmony_ci	int ret;
83008c2ecf20Sopenharmony_ci
83018c2ecf20Sopenharmony_ci	spin_lock_irqsave(hba->host->host_lock, flags);
83028c2ecf20Sopenharmony_ci	sdp = hba->sdev_ufs_device;
83038c2ecf20Sopenharmony_ci	if (sdp) {
83048c2ecf20Sopenharmony_ci		ret = scsi_device_get(sdp);
83058c2ecf20Sopenharmony_ci		if (!ret && !scsi_device_online(sdp)) {
83068c2ecf20Sopenharmony_ci			ret = -ENODEV;
83078c2ecf20Sopenharmony_ci			scsi_device_put(sdp);
83088c2ecf20Sopenharmony_ci		}
83098c2ecf20Sopenharmony_ci	} else {
83108c2ecf20Sopenharmony_ci		ret = -ENODEV;
83118c2ecf20Sopenharmony_ci	}
83128c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(hba->host->host_lock, flags);
83138c2ecf20Sopenharmony_ci
83148c2ecf20Sopenharmony_ci	if (ret)
83158c2ecf20Sopenharmony_ci		return ret;
83168c2ecf20Sopenharmony_ci
83178c2ecf20Sopenharmony_ci	/*
83188c2ecf20Sopenharmony_ci	 * If scsi commands fail, the scsi mid-layer schedules scsi error-
83198c2ecf20Sopenharmony_ci	 * handling, which would wait for host to be resumed. Since we know
83208c2ecf20Sopenharmony_ci	 * we are functional while we are here, skip host resume in error
83218c2ecf20Sopenharmony_ci	 * handling context.
83228c2ecf20Sopenharmony_ci	 */
83238c2ecf20Sopenharmony_ci	hba->host->eh_noresume = 1;
83248c2ecf20Sopenharmony_ci	if (hba->wlun_dev_clr_ua) {
83258c2ecf20Sopenharmony_ci		ret = ufshcd_send_request_sense(hba, sdp);
83268c2ecf20Sopenharmony_ci		if (ret)
83278c2ecf20Sopenharmony_ci			goto out;
83288c2ecf20Sopenharmony_ci		/* Unit attention condition is cleared now */
83298c2ecf20Sopenharmony_ci		hba->wlun_dev_clr_ua = false;
83308c2ecf20Sopenharmony_ci	}
83318c2ecf20Sopenharmony_ci
83328c2ecf20Sopenharmony_ci	cmd[4] = pwr_mode << 4;
83338c2ecf20Sopenharmony_ci
83348c2ecf20Sopenharmony_ci	/*
83358c2ecf20Sopenharmony_ci	 * Current function would be generally called from the power management
83368c2ecf20Sopenharmony_ci	 * callbacks hence set the RQF_PM flag so that it doesn't resume the
83378c2ecf20Sopenharmony_ci	 * already suspended childs.
83388c2ecf20Sopenharmony_ci	 */
83398c2ecf20Sopenharmony_ci	ret = scsi_execute(sdp, cmd, DMA_NONE, NULL, 0, NULL, &sshdr,
83408c2ecf20Sopenharmony_ci			START_STOP_TIMEOUT, 0, 0, RQF_PM, NULL);
83418c2ecf20Sopenharmony_ci	if (ret) {
83428c2ecf20Sopenharmony_ci		sdev_printk(KERN_WARNING, sdp,
83438c2ecf20Sopenharmony_ci			    "START_STOP failed for power mode: %d, result %x\n",
83448c2ecf20Sopenharmony_ci			    pwr_mode, ret);
83458c2ecf20Sopenharmony_ci		if (driver_byte(ret) == DRIVER_SENSE)
83468c2ecf20Sopenharmony_ci			scsi_print_sense_hdr(sdp, NULL, &sshdr);
83478c2ecf20Sopenharmony_ci	}
83488c2ecf20Sopenharmony_ci
83498c2ecf20Sopenharmony_ci	if (!ret)
83508c2ecf20Sopenharmony_ci		hba->curr_dev_pwr_mode = pwr_mode;
83518c2ecf20Sopenharmony_ciout:
83528c2ecf20Sopenharmony_ci	scsi_device_put(sdp);
83538c2ecf20Sopenharmony_ci	hba->host->eh_noresume = 0;
83548c2ecf20Sopenharmony_ci	return ret;
83558c2ecf20Sopenharmony_ci}
83568c2ecf20Sopenharmony_ci
83578c2ecf20Sopenharmony_cistatic int ufshcd_link_state_transition(struct ufs_hba *hba,
83588c2ecf20Sopenharmony_ci					enum uic_link_state req_link_state,
83598c2ecf20Sopenharmony_ci					int check_for_bkops)
83608c2ecf20Sopenharmony_ci{
83618c2ecf20Sopenharmony_ci	int ret = 0;
83628c2ecf20Sopenharmony_ci
83638c2ecf20Sopenharmony_ci	if (req_link_state == hba->uic_link_state)
83648c2ecf20Sopenharmony_ci		return 0;
83658c2ecf20Sopenharmony_ci
83668c2ecf20Sopenharmony_ci	if (req_link_state == UIC_LINK_HIBERN8_STATE) {
83678c2ecf20Sopenharmony_ci		ret = ufshcd_uic_hibern8_enter(hba);
83688c2ecf20Sopenharmony_ci		if (!ret) {
83698c2ecf20Sopenharmony_ci			ufshcd_set_link_hibern8(hba);
83708c2ecf20Sopenharmony_ci		} else {
83718c2ecf20Sopenharmony_ci			dev_err(hba->dev, "%s: hibern8 enter failed %d\n",
83728c2ecf20Sopenharmony_ci					__func__, ret);
83738c2ecf20Sopenharmony_ci			goto out;
83748c2ecf20Sopenharmony_ci		}
83758c2ecf20Sopenharmony_ci	}
83768c2ecf20Sopenharmony_ci	/*
83778c2ecf20Sopenharmony_ci	 * If autobkops is enabled, link can't be turned off because
83788c2ecf20Sopenharmony_ci	 * turning off the link would also turn off the device.
83798c2ecf20Sopenharmony_ci	 */
83808c2ecf20Sopenharmony_ci	else if ((req_link_state == UIC_LINK_OFF_STATE) &&
83818c2ecf20Sopenharmony_ci		 (!check_for_bkops || !hba->auto_bkops_enabled)) {
83828c2ecf20Sopenharmony_ci		/*
83838c2ecf20Sopenharmony_ci		 * Let's make sure that link is in low power mode, we are doing
83848c2ecf20Sopenharmony_ci		 * this currently by putting the link in Hibern8. Otherway to
83858c2ecf20Sopenharmony_ci		 * put the link in low power mode is to send the DME end point
83868c2ecf20Sopenharmony_ci		 * to device and then send the DME reset command to local
83878c2ecf20Sopenharmony_ci		 * unipro. But putting the link in hibern8 is much faster.
83888c2ecf20Sopenharmony_ci		 */
83898c2ecf20Sopenharmony_ci		ret = ufshcd_uic_hibern8_enter(hba);
83908c2ecf20Sopenharmony_ci		if (ret) {
83918c2ecf20Sopenharmony_ci			dev_err(hba->dev, "%s: hibern8 enter failed %d\n",
83928c2ecf20Sopenharmony_ci					__func__, ret);
83938c2ecf20Sopenharmony_ci			goto out;
83948c2ecf20Sopenharmony_ci		}
83958c2ecf20Sopenharmony_ci		/*
83968c2ecf20Sopenharmony_ci		 * Change controller state to "reset state" which
83978c2ecf20Sopenharmony_ci		 * should also put the link in off/reset state
83988c2ecf20Sopenharmony_ci		 */
83998c2ecf20Sopenharmony_ci		ufshcd_hba_stop(hba);
84008c2ecf20Sopenharmony_ci		/*
84018c2ecf20Sopenharmony_ci		 * TODO: Check if we need any delay to make sure that
84028c2ecf20Sopenharmony_ci		 * controller is reset
84038c2ecf20Sopenharmony_ci		 */
84048c2ecf20Sopenharmony_ci		ufshcd_set_link_off(hba);
84058c2ecf20Sopenharmony_ci	}
84068c2ecf20Sopenharmony_ci
84078c2ecf20Sopenharmony_ciout:
84088c2ecf20Sopenharmony_ci	return ret;
84098c2ecf20Sopenharmony_ci}
84108c2ecf20Sopenharmony_ci
84118c2ecf20Sopenharmony_cistatic void ufshcd_vreg_set_lpm(struct ufs_hba *hba)
84128c2ecf20Sopenharmony_ci{
84138c2ecf20Sopenharmony_ci	bool vcc_off = false;
84148c2ecf20Sopenharmony_ci
84158c2ecf20Sopenharmony_ci	/*
84168c2ecf20Sopenharmony_ci	 * It seems some UFS devices may keep drawing more than sleep current
84178c2ecf20Sopenharmony_ci	 * (atleast for 500us) from UFS rails (especially from VCCQ rail).
84188c2ecf20Sopenharmony_ci	 * To avoid this situation, add 2ms delay before putting these UFS
84198c2ecf20Sopenharmony_ci	 * rails in LPM mode.
84208c2ecf20Sopenharmony_ci	 */
84218c2ecf20Sopenharmony_ci	if (!ufshcd_is_link_active(hba) &&
84228c2ecf20Sopenharmony_ci	    hba->dev_quirks & UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM)
84238c2ecf20Sopenharmony_ci		usleep_range(2000, 2100);
84248c2ecf20Sopenharmony_ci
84258c2ecf20Sopenharmony_ci	/*
84268c2ecf20Sopenharmony_ci	 * If UFS device is either in UFS_Sleep turn off VCC rail to save some
84278c2ecf20Sopenharmony_ci	 * power.
84288c2ecf20Sopenharmony_ci	 *
84298c2ecf20Sopenharmony_ci	 * If UFS device and link is in OFF state, all power supplies (VCC,
84308c2ecf20Sopenharmony_ci	 * VCCQ, VCCQ2) can be turned off if power on write protect is not
84318c2ecf20Sopenharmony_ci	 * required. If UFS link is inactive (Hibern8 or OFF state) and device
84328c2ecf20Sopenharmony_ci	 * is in sleep state, put VCCQ & VCCQ2 rails in LPM mode.
84338c2ecf20Sopenharmony_ci	 *
84348c2ecf20Sopenharmony_ci	 * Ignore the error returned by ufshcd_toggle_vreg() as device is anyway
84358c2ecf20Sopenharmony_ci	 * in low power state which would save some power.
84368c2ecf20Sopenharmony_ci	 *
84378c2ecf20Sopenharmony_ci	 * If Write Booster is enabled and the device needs to flush the WB
84388c2ecf20Sopenharmony_ci	 * buffer OR if bkops status is urgent for WB, keep Vcc on.
84398c2ecf20Sopenharmony_ci	 */
84408c2ecf20Sopenharmony_ci	if (ufshcd_is_ufs_dev_poweroff(hba) && ufshcd_is_link_off(hba) &&
84418c2ecf20Sopenharmony_ci	    !hba->dev_info.is_lu_power_on_wp) {
84428c2ecf20Sopenharmony_ci		ufshcd_setup_vreg(hba, false);
84438c2ecf20Sopenharmony_ci		vcc_off = true;
84448c2ecf20Sopenharmony_ci	} else if (!ufshcd_is_ufs_dev_active(hba)) {
84458c2ecf20Sopenharmony_ci		ufshcd_toggle_vreg(hba->dev, hba->vreg_info.vcc, false);
84468c2ecf20Sopenharmony_ci		vcc_off = true;
84478c2ecf20Sopenharmony_ci		if (ufshcd_is_link_hibern8(hba) || ufshcd_is_link_off(hba)) {
84488c2ecf20Sopenharmony_ci			ufshcd_config_vreg_lpm(hba, hba->vreg_info.vccq);
84498c2ecf20Sopenharmony_ci			ufshcd_config_vreg_lpm(hba, hba->vreg_info.vccq2);
84508c2ecf20Sopenharmony_ci		}
84518c2ecf20Sopenharmony_ci	}
84528c2ecf20Sopenharmony_ci
84538c2ecf20Sopenharmony_ci	/*
84548c2ecf20Sopenharmony_ci	 * Some UFS devices require delay after VCC power rail is turned-off.
84558c2ecf20Sopenharmony_ci	 */
84568c2ecf20Sopenharmony_ci	if (vcc_off && hba->vreg_info.vcc &&
84578c2ecf20Sopenharmony_ci		hba->dev_quirks & UFS_DEVICE_QUIRK_DELAY_AFTER_LPM)
84588c2ecf20Sopenharmony_ci		usleep_range(5000, 5100);
84598c2ecf20Sopenharmony_ci}
84608c2ecf20Sopenharmony_ci
84618c2ecf20Sopenharmony_cistatic int ufshcd_vreg_set_hpm(struct ufs_hba *hba)
84628c2ecf20Sopenharmony_ci{
84638c2ecf20Sopenharmony_ci	int ret = 0;
84648c2ecf20Sopenharmony_ci
84658c2ecf20Sopenharmony_ci	if (ufshcd_is_ufs_dev_poweroff(hba) && ufshcd_is_link_off(hba) &&
84668c2ecf20Sopenharmony_ci	    !hba->dev_info.is_lu_power_on_wp) {
84678c2ecf20Sopenharmony_ci		ret = ufshcd_setup_vreg(hba, true);
84688c2ecf20Sopenharmony_ci	} else if (!ufshcd_is_ufs_dev_active(hba)) {
84698c2ecf20Sopenharmony_ci		if (!ufshcd_is_link_active(hba)) {
84708c2ecf20Sopenharmony_ci			ret = ufshcd_config_vreg_hpm(hba, hba->vreg_info.vccq);
84718c2ecf20Sopenharmony_ci			if (ret)
84728c2ecf20Sopenharmony_ci				goto vcc_disable;
84738c2ecf20Sopenharmony_ci			ret = ufshcd_config_vreg_hpm(hba, hba->vreg_info.vccq2);
84748c2ecf20Sopenharmony_ci			if (ret)
84758c2ecf20Sopenharmony_ci				goto vccq_lpm;
84768c2ecf20Sopenharmony_ci		}
84778c2ecf20Sopenharmony_ci		ret = ufshcd_toggle_vreg(hba->dev, hba->vreg_info.vcc, true);
84788c2ecf20Sopenharmony_ci	}
84798c2ecf20Sopenharmony_ci	goto out;
84808c2ecf20Sopenharmony_ci
84818c2ecf20Sopenharmony_civccq_lpm:
84828c2ecf20Sopenharmony_ci	ufshcd_config_vreg_lpm(hba, hba->vreg_info.vccq);
84838c2ecf20Sopenharmony_civcc_disable:
84848c2ecf20Sopenharmony_ci	ufshcd_toggle_vreg(hba->dev, hba->vreg_info.vcc, false);
84858c2ecf20Sopenharmony_ciout:
84868c2ecf20Sopenharmony_ci	return ret;
84878c2ecf20Sopenharmony_ci}
84888c2ecf20Sopenharmony_ci
84898c2ecf20Sopenharmony_cistatic void ufshcd_hba_vreg_set_lpm(struct ufs_hba *hba)
84908c2ecf20Sopenharmony_ci{
84918c2ecf20Sopenharmony_ci	if (ufshcd_is_link_off(hba))
84928c2ecf20Sopenharmony_ci		ufshcd_setup_hba_vreg(hba, false);
84938c2ecf20Sopenharmony_ci}
84948c2ecf20Sopenharmony_ci
84958c2ecf20Sopenharmony_cistatic void ufshcd_hba_vreg_set_hpm(struct ufs_hba *hba)
84968c2ecf20Sopenharmony_ci{
84978c2ecf20Sopenharmony_ci	if (ufshcd_is_link_off(hba))
84988c2ecf20Sopenharmony_ci		ufshcd_setup_hba_vreg(hba, true);
84998c2ecf20Sopenharmony_ci}
85008c2ecf20Sopenharmony_ci
85018c2ecf20Sopenharmony_ci/**
85028c2ecf20Sopenharmony_ci * ufshcd_suspend - helper function for suspend operations
85038c2ecf20Sopenharmony_ci * @hba: per adapter instance
85048c2ecf20Sopenharmony_ci * @pm_op: desired low power operation type
85058c2ecf20Sopenharmony_ci *
85068c2ecf20Sopenharmony_ci * This function will try to put the UFS device and link into low power
85078c2ecf20Sopenharmony_ci * mode based on the "rpm_lvl" (Runtime PM level) or "spm_lvl"
85088c2ecf20Sopenharmony_ci * (System PM level).
85098c2ecf20Sopenharmony_ci *
85108c2ecf20Sopenharmony_ci * If this function is called during shutdown, it will make sure that
85118c2ecf20Sopenharmony_ci * both UFS device and UFS link is powered off.
85128c2ecf20Sopenharmony_ci *
85138c2ecf20Sopenharmony_ci * NOTE: UFS device & link must be active before we enter in this function.
85148c2ecf20Sopenharmony_ci *
85158c2ecf20Sopenharmony_ci * Returns 0 for success and non-zero for failure
85168c2ecf20Sopenharmony_ci */
85178c2ecf20Sopenharmony_cistatic int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
85188c2ecf20Sopenharmony_ci{
85198c2ecf20Sopenharmony_ci	int ret = 0;
85208c2ecf20Sopenharmony_ci	enum ufs_pm_level pm_lvl;
85218c2ecf20Sopenharmony_ci	enum ufs_dev_pwr_mode req_dev_pwr_mode;
85228c2ecf20Sopenharmony_ci	enum uic_link_state req_link_state;
85238c2ecf20Sopenharmony_ci
85248c2ecf20Sopenharmony_ci	hba->pm_op_in_progress = 1;
85258c2ecf20Sopenharmony_ci	if (!ufshcd_is_shutdown_pm(pm_op)) {
85268c2ecf20Sopenharmony_ci		pm_lvl = ufshcd_is_runtime_pm(pm_op) ?
85278c2ecf20Sopenharmony_ci			 hba->rpm_lvl : hba->spm_lvl;
85288c2ecf20Sopenharmony_ci		req_dev_pwr_mode = ufs_get_pm_lvl_to_dev_pwr_mode(pm_lvl);
85298c2ecf20Sopenharmony_ci		req_link_state = ufs_get_pm_lvl_to_link_pwr_state(pm_lvl);
85308c2ecf20Sopenharmony_ci	} else {
85318c2ecf20Sopenharmony_ci		req_dev_pwr_mode = UFS_POWERDOWN_PWR_MODE;
85328c2ecf20Sopenharmony_ci		req_link_state = UIC_LINK_OFF_STATE;
85338c2ecf20Sopenharmony_ci	}
85348c2ecf20Sopenharmony_ci
85358c2ecf20Sopenharmony_ci	/*
85368c2ecf20Sopenharmony_ci	 * If we can't transition into any of the low power modes
85378c2ecf20Sopenharmony_ci	 * just gate the clocks.
85388c2ecf20Sopenharmony_ci	 */
85398c2ecf20Sopenharmony_ci	ufshcd_hold(hba, false);
85408c2ecf20Sopenharmony_ci	hba->clk_gating.is_suspended = true;
85418c2ecf20Sopenharmony_ci
85428c2ecf20Sopenharmony_ci	if (hba->clk_scaling.is_allowed) {
85438c2ecf20Sopenharmony_ci		cancel_work_sync(&hba->clk_scaling.suspend_work);
85448c2ecf20Sopenharmony_ci		cancel_work_sync(&hba->clk_scaling.resume_work);
85458c2ecf20Sopenharmony_ci		ufshcd_suspend_clkscaling(hba);
85468c2ecf20Sopenharmony_ci	}
85478c2ecf20Sopenharmony_ci
85488c2ecf20Sopenharmony_ci	if (req_dev_pwr_mode == UFS_ACTIVE_PWR_MODE &&
85498c2ecf20Sopenharmony_ci			req_link_state == UIC_LINK_ACTIVE_STATE) {
85508c2ecf20Sopenharmony_ci		goto disable_clks;
85518c2ecf20Sopenharmony_ci	}
85528c2ecf20Sopenharmony_ci
85538c2ecf20Sopenharmony_ci	if ((req_dev_pwr_mode == hba->curr_dev_pwr_mode) &&
85548c2ecf20Sopenharmony_ci	    (req_link_state == hba->uic_link_state))
85558c2ecf20Sopenharmony_ci		goto enable_gating;
85568c2ecf20Sopenharmony_ci
85578c2ecf20Sopenharmony_ci	/* UFS device & link must be active before we enter in this function */
85588c2ecf20Sopenharmony_ci	if (!ufshcd_is_ufs_dev_active(hba) || !ufshcd_is_link_active(hba)) {
85598c2ecf20Sopenharmony_ci		ret = -EINVAL;
85608c2ecf20Sopenharmony_ci		goto enable_gating;
85618c2ecf20Sopenharmony_ci	}
85628c2ecf20Sopenharmony_ci
85638c2ecf20Sopenharmony_ci	if (ufshcd_is_runtime_pm(pm_op)) {
85648c2ecf20Sopenharmony_ci		if (ufshcd_can_autobkops_during_suspend(hba)) {
85658c2ecf20Sopenharmony_ci			/*
85668c2ecf20Sopenharmony_ci			 * The device is idle with no requests in the queue,
85678c2ecf20Sopenharmony_ci			 * allow background operations if bkops status shows
85688c2ecf20Sopenharmony_ci			 * that performance might be impacted.
85698c2ecf20Sopenharmony_ci			 */
85708c2ecf20Sopenharmony_ci			ret = ufshcd_urgent_bkops(hba);
85718c2ecf20Sopenharmony_ci			if (ret)
85728c2ecf20Sopenharmony_ci				goto enable_gating;
85738c2ecf20Sopenharmony_ci		} else {
85748c2ecf20Sopenharmony_ci			/* make sure that auto bkops is disabled */
85758c2ecf20Sopenharmony_ci			ufshcd_disable_auto_bkops(hba);
85768c2ecf20Sopenharmony_ci		}
85778c2ecf20Sopenharmony_ci		/*
85788c2ecf20Sopenharmony_ci		 * If device needs to do BKOP or WB buffer flush during
85798c2ecf20Sopenharmony_ci		 * Hibern8, keep device power mode as "active power mode"
85808c2ecf20Sopenharmony_ci		 * and VCC supply.
85818c2ecf20Sopenharmony_ci		 */
85828c2ecf20Sopenharmony_ci		hba->dev_info.b_rpm_dev_flush_capable =
85838c2ecf20Sopenharmony_ci			hba->auto_bkops_enabled ||
85848c2ecf20Sopenharmony_ci			(((req_link_state == UIC_LINK_HIBERN8_STATE) ||
85858c2ecf20Sopenharmony_ci			((req_link_state == UIC_LINK_ACTIVE_STATE) &&
85868c2ecf20Sopenharmony_ci			ufshcd_is_auto_hibern8_enabled(hba))) &&
85878c2ecf20Sopenharmony_ci			ufshcd_wb_need_flush(hba));
85888c2ecf20Sopenharmony_ci	}
85898c2ecf20Sopenharmony_ci
85908c2ecf20Sopenharmony_ci	if (req_dev_pwr_mode != hba->curr_dev_pwr_mode) {
85918c2ecf20Sopenharmony_ci		if ((ufshcd_is_runtime_pm(pm_op) && !hba->auto_bkops_enabled) ||
85928c2ecf20Sopenharmony_ci		    !ufshcd_is_runtime_pm(pm_op)) {
85938c2ecf20Sopenharmony_ci			/* ensure that bkops is disabled */
85948c2ecf20Sopenharmony_ci			ufshcd_disable_auto_bkops(hba);
85958c2ecf20Sopenharmony_ci		}
85968c2ecf20Sopenharmony_ci
85978c2ecf20Sopenharmony_ci		if (!hba->dev_info.b_rpm_dev_flush_capable) {
85988c2ecf20Sopenharmony_ci			ret = ufshcd_set_dev_pwr_mode(hba, req_dev_pwr_mode);
85998c2ecf20Sopenharmony_ci			if (ret)
86008c2ecf20Sopenharmony_ci				goto enable_gating;
86018c2ecf20Sopenharmony_ci		}
86028c2ecf20Sopenharmony_ci	}
86038c2ecf20Sopenharmony_ci
86048c2ecf20Sopenharmony_ci	flush_work(&hba->eeh_work);
86058c2ecf20Sopenharmony_ci	ret = ufshcd_link_state_transition(hba, req_link_state, 1);
86068c2ecf20Sopenharmony_ci	if (ret)
86078c2ecf20Sopenharmony_ci		goto set_dev_active;
86088c2ecf20Sopenharmony_ci
86098c2ecf20Sopenharmony_ci	ufshcd_vreg_set_lpm(hba);
86108c2ecf20Sopenharmony_ci
86118c2ecf20Sopenharmony_cidisable_clks:
86128c2ecf20Sopenharmony_ci	/*
86138c2ecf20Sopenharmony_ci	 * Call vendor specific suspend callback. As these callbacks may access
86148c2ecf20Sopenharmony_ci	 * vendor specific host controller register space call them before the
86158c2ecf20Sopenharmony_ci	 * host clocks are ON.
86168c2ecf20Sopenharmony_ci	 */
86178c2ecf20Sopenharmony_ci	ret = ufshcd_vops_suspend(hba, pm_op);
86188c2ecf20Sopenharmony_ci	if (ret)
86198c2ecf20Sopenharmony_ci		goto set_link_active;
86208c2ecf20Sopenharmony_ci	/*
86218c2ecf20Sopenharmony_ci	 * Disable the host irq as host controller as there won't be any
86228c2ecf20Sopenharmony_ci	 * host controller transaction expected till resume.
86238c2ecf20Sopenharmony_ci	 */
86248c2ecf20Sopenharmony_ci	ufshcd_disable_irq(hba);
86258c2ecf20Sopenharmony_ci
86268c2ecf20Sopenharmony_ci	ufshcd_setup_clocks(hba, false);
86278c2ecf20Sopenharmony_ci
86288c2ecf20Sopenharmony_ci	if (ufshcd_is_clkgating_allowed(hba)) {
86298c2ecf20Sopenharmony_ci		hba->clk_gating.state = CLKS_OFF;
86308c2ecf20Sopenharmony_ci		trace_ufshcd_clk_gating(dev_name(hba->dev),
86318c2ecf20Sopenharmony_ci					hba->clk_gating.state);
86328c2ecf20Sopenharmony_ci	}
86338c2ecf20Sopenharmony_ci
86348c2ecf20Sopenharmony_ci	/* Put the host controller in low power mode if possible */
86358c2ecf20Sopenharmony_ci	ufshcd_hba_vreg_set_lpm(hba);
86368c2ecf20Sopenharmony_ci	goto out;
86378c2ecf20Sopenharmony_ci
86388c2ecf20Sopenharmony_ciset_link_active:
86398c2ecf20Sopenharmony_ci	if (hba->clk_scaling.is_allowed)
86408c2ecf20Sopenharmony_ci		ufshcd_resume_clkscaling(hba);
86418c2ecf20Sopenharmony_ci	ufshcd_vreg_set_hpm(hba);
86428c2ecf20Sopenharmony_ci	if (ufshcd_is_link_hibern8(hba) && !ufshcd_uic_hibern8_exit(hba))
86438c2ecf20Sopenharmony_ci		ufshcd_set_link_active(hba);
86448c2ecf20Sopenharmony_ci	else if (ufshcd_is_link_off(hba))
86458c2ecf20Sopenharmony_ci		ufshcd_host_reset_and_restore(hba);
86468c2ecf20Sopenharmony_ciset_dev_active:
86478c2ecf20Sopenharmony_ci	if (!ufshcd_set_dev_pwr_mode(hba, UFS_ACTIVE_PWR_MODE))
86488c2ecf20Sopenharmony_ci		ufshcd_disable_auto_bkops(hba);
86498c2ecf20Sopenharmony_cienable_gating:
86508c2ecf20Sopenharmony_ci	if (hba->clk_scaling.is_allowed)
86518c2ecf20Sopenharmony_ci		ufshcd_resume_clkscaling(hba);
86528c2ecf20Sopenharmony_ci	hba->clk_gating.is_suspended = false;
86538c2ecf20Sopenharmony_ci	hba->dev_info.b_rpm_dev_flush_capable = false;
86548c2ecf20Sopenharmony_ci	ufshcd_release(hba);
86558c2ecf20Sopenharmony_ciout:
86568c2ecf20Sopenharmony_ci	if (hba->dev_info.b_rpm_dev_flush_capable) {
86578c2ecf20Sopenharmony_ci		schedule_delayed_work(&hba->rpm_dev_flush_recheck_work,
86588c2ecf20Sopenharmony_ci			msecs_to_jiffies(RPM_DEV_FLUSH_RECHECK_WORK_DELAY_MS));
86598c2ecf20Sopenharmony_ci	}
86608c2ecf20Sopenharmony_ci
86618c2ecf20Sopenharmony_ci	hba->pm_op_in_progress = 0;
86628c2ecf20Sopenharmony_ci
86638c2ecf20Sopenharmony_ci	if (ret)
86648c2ecf20Sopenharmony_ci		ufshcd_update_reg_hist(&hba->ufs_stats.suspend_err, (u32)ret);
86658c2ecf20Sopenharmony_ci	return ret;
86668c2ecf20Sopenharmony_ci}
86678c2ecf20Sopenharmony_ci
86688c2ecf20Sopenharmony_ci/**
86698c2ecf20Sopenharmony_ci * ufshcd_resume - helper function for resume operations
86708c2ecf20Sopenharmony_ci * @hba: per adapter instance
86718c2ecf20Sopenharmony_ci * @pm_op: runtime PM or system PM
86728c2ecf20Sopenharmony_ci *
86738c2ecf20Sopenharmony_ci * This function basically brings the UFS device, UniPro link and controller
86748c2ecf20Sopenharmony_ci * to active state.
86758c2ecf20Sopenharmony_ci *
86768c2ecf20Sopenharmony_ci * Returns 0 for success and non-zero for failure
86778c2ecf20Sopenharmony_ci */
86788c2ecf20Sopenharmony_cistatic int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
86798c2ecf20Sopenharmony_ci{
86808c2ecf20Sopenharmony_ci	int ret;
86818c2ecf20Sopenharmony_ci	enum uic_link_state old_link_state;
86828c2ecf20Sopenharmony_ci
86838c2ecf20Sopenharmony_ci	hba->pm_op_in_progress = 1;
86848c2ecf20Sopenharmony_ci	old_link_state = hba->uic_link_state;
86858c2ecf20Sopenharmony_ci
86868c2ecf20Sopenharmony_ci	ufshcd_hba_vreg_set_hpm(hba);
86878c2ecf20Sopenharmony_ci	/* Make sure clocks are enabled before accessing controller */
86888c2ecf20Sopenharmony_ci	ret = ufshcd_setup_clocks(hba, true);
86898c2ecf20Sopenharmony_ci	if (ret)
86908c2ecf20Sopenharmony_ci		goto out;
86918c2ecf20Sopenharmony_ci
86928c2ecf20Sopenharmony_ci	/* enable the host irq as host controller would be active soon */
86938c2ecf20Sopenharmony_ci	ufshcd_enable_irq(hba);
86948c2ecf20Sopenharmony_ci
86958c2ecf20Sopenharmony_ci	ret = ufshcd_vreg_set_hpm(hba);
86968c2ecf20Sopenharmony_ci	if (ret)
86978c2ecf20Sopenharmony_ci		goto disable_irq_and_vops_clks;
86988c2ecf20Sopenharmony_ci
86998c2ecf20Sopenharmony_ci	/*
87008c2ecf20Sopenharmony_ci	 * Call vendor specific resume callback. As these callbacks may access
87018c2ecf20Sopenharmony_ci	 * vendor specific host controller register space call them when the
87028c2ecf20Sopenharmony_ci	 * host clocks are ON.
87038c2ecf20Sopenharmony_ci	 */
87048c2ecf20Sopenharmony_ci	ret = ufshcd_vops_resume(hba, pm_op);
87058c2ecf20Sopenharmony_ci	if (ret)
87068c2ecf20Sopenharmony_ci		goto disable_vreg;
87078c2ecf20Sopenharmony_ci
87088c2ecf20Sopenharmony_ci	if (ufshcd_is_link_hibern8(hba)) {
87098c2ecf20Sopenharmony_ci		ret = ufshcd_uic_hibern8_exit(hba);
87108c2ecf20Sopenharmony_ci		if (!ret) {
87118c2ecf20Sopenharmony_ci			ufshcd_set_link_active(hba);
87128c2ecf20Sopenharmony_ci		} else {
87138c2ecf20Sopenharmony_ci			dev_err(hba->dev, "%s: hibern8 exit failed %d\n",
87148c2ecf20Sopenharmony_ci					__func__, ret);
87158c2ecf20Sopenharmony_ci			goto vendor_suspend;
87168c2ecf20Sopenharmony_ci		}
87178c2ecf20Sopenharmony_ci	} else if (ufshcd_is_link_off(hba)) {
87188c2ecf20Sopenharmony_ci		/*
87198c2ecf20Sopenharmony_ci		 * A full initialization of the host and the device is
87208c2ecf20Sopenharmony_ci		 * required since the link was put to off during suspend.
87218c2ecf20Sopenharmony_ci		 */
87228c2ecf20Sopenharmony_ci		ret = ufshcd_reset_and_restore(hba);
87238c2ecf20Sopenharmony_ci		/*
87248c2ecf20Sopenharmony_ci		 * ufshcd_reset_and_restore() should have already
87258c2ecf20Sopenharmony_ci		 * set the link state as active
87268c2ecf20Sopenharmony_ci		 */
87278c2ecf20Sopenharmony_ci		if (ret || !ufshcd_is_link_active(hba))
87288c2ecf20Sopenharmony_ci			goto vendor_suspend;
87298c2ecf20Sopenharmony_ci	}
87308c2ecf20Sopenharmony_ci
87318c2ecf20Sopenharmony_ci	if (!ufshcd_is_ufs_dev_active(hba)) {
87328c2ecf20Sopenharmony_ci		ret = ufshcd_set_dev_pwr_mode(hba, UFS_ACTIVE_PWR_MODE);
87338c2ecf20Sopenharmony_ci		if (ret)
87348c2ecf20Sopenharmony_ci			goto set_old_link_state;
87358c2ecf20Sopenharmony_ci	}
87368c2ecf20Sopenharmony_ci
87378c2ecf20Sopenharmony_ci	if (ufshcd_keep_autobkops_enabled_except_suspend(hba))
87388c2ecf20Sopenharmony_ci		ufshcd_enable_auto_bkops(hba);
87398c2ecf20Sopenharmony_ci	else
87408c2ecf20Sopenharmony_ci		/*
87418c2ecf20Sopenharmony_ci		 * If BKOPs operations are urgently needed at this moment then
87428c2ecf20Sopenharmony_ci		 * keep auto-bkops enabled or else disable it.
87438c2ecf20Sopenharmony_ci		 */
87448c2ecf20Sopenharmony_ci		ufshcd_urgent_bkops(hba);
87458c2ecf20Sopenharmony_ci
87468c2ecf20Sopenharmony_ci	hba->clk_gating.is_suspended = false;
87478c2ecf20Sopenharmony_ci
87488c2ecf20Sopenharmony_ci	if (hba->clk_scaling.is_allowed)
87498c2ecf20Sopenharmony_ci		ufshcd_resume_clkscaling(hba);
87508c2ecf20Sopenharmony_ci
87518c2ecf20Sopenharmony_ci	/* Enable Auto-Hibernate if configured */
87528c2ecf20Sopenharmony_ci	ufshcd_auto_hibern8_enable(hba);
87538c2ecf20Sopenharmony_ci
87548c2ecf20Sopenharmony_ci	if (hba->dev_info.b_rpm_dev_flush_capable) {
87558c2ecf20Sopenharmony_ci		hba->dev_info.b_rpm_dev_flush_capable = false;
87568c2ecf20Sopenharmony_ci		cancel_delayed_work(&hba->rpm_dev_flush_recheck_work);
87578c2ecf20Sopenharmony_ci	}
87588c2ecf20Sopenharmony_ci
87598c2ecf20Sopenharmony_ci	/* Schedule clock gating in case of no access to UFS device yet */
87608c2ecf20Sopenharmony_ci	ufshcd_release(hba);
87618c2ecf20Sopenharmony_ci
87628c2ecf20Sopenharmony_ci	goto out;
87638c2ecf20Sopenharmony_ci
87648c2ecf20Sopenharmony_ciset_old_link_state:
87658c2ecf20Sopenharmony_ci	ufshcd_link_state_transition(hba, old_link_state, 0);
87668c2ecf20Sopenharmony_civendor_suspend:
87678c2ecf20Sopenharmony_ci	ufshcd_vops_suspend(hba, pm_op);
87688c2ecf20Sopenharmony_cidisable_vreg:
87698c2ecf20Sopenharmony_ci	ufshcd_vreg_set_lpm(hba);
87708c2ecf20Sopenharmony_cidisable_irq_and_vops_clks:
87718c2ecf20Sopenharmony_ci	ufshcd_disable_irq(hba);
87728c2ecf20Sopenharmony_ci	if (hba->clk_scaling.is_allowed)
87738c2ecf20Sopenharmony_ci		ufshcd_suspend_clkscaling(hba);
87748c2ecf20Sopenharmony_ci	ufshcd_setup_clocks(hba, false);
87758c2ecf20Sopenharmony_ci	if (ufshcd_is_clkgating_allowed(hba)) {
87768c2ecf20Sopenharmony_ci		hba->clk_gating.state = CLKS_OFF;
87778c2ecf20Sopenharmony_ci		trace_ufshcd_clk_gating(dev_name(hba->dev),
87788c2ecf20Sopenharmony_ci					hba->clk_gating.state);
87798c2ecf20Sopenharmony_ci	}
87808c2ecf20Sopenharmony_ciout:
87818c2ecf20Sopenharmony_ci	hba->pm_op_in_progress = 0;
87828c2ecf20Sopenharmony_ci	if (ret)
87838c2ecf20Sopenharmony_ci		ufshcd_update_reg_hist(&hba->ufs_stats.resume_err, (u32)ret);
87848c2ecf20Sopenharmony_ci	return ret;
87858c2ecf20Sopenharmony_ci}
87868c2ecf20Sopenharmony_ci
87878c2ecf20Sopenharmony_ci/**
87888c2ecf20Sopenharmony_ci * ufshcd_system_suspend - system suspend routine
87898c2ecf20Sopenharmony_ci * @hba: per adapter instance
87908c2ecf20Sopenharmony_ci *
87918c2ecf20Sopenharmony_ci * Check the description of ufshcd_suspend() function for more details.
87928c2ecf20Sopenharmony_ci *
87938c2ecf20Sopenharmony_ci * Returns 0 for success and non-zero for failure
87948c2ecf20Sopenharmony_ci */
87958c2ecf20Sopenharmony_ciint ufshcd_system_suspend(struct ufs_hba *hba)
87968c2ecf20Sopenharmony_ci{
87978c2ecf20Sopenharmony_ci	int ret = 0;
87988c2ecf20Sopenharmony_ci	ktime_t start = ktime_get();
87998c2ecf20Sopenharmony_ci
88008c2ecf20Sopenharmony_ci	if (!hba || !hba->is_powered)
88018c2ecf20Sopenharmony_ci		return 0;
88028c2ecf20Sopenharmony_ci
88038c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&hba->rpm_dev_flush_recheck_work);
88048c2ecf20Sopenharmony_ci
88058c2ecf20Sopenharmony_ci	if ((ufs_get_pm_lvl_to_dev_pwr_mode(hba->spm_lvl) ==
88068c2ecf20Sopenharmony_ci	     hba->curr_dev_pwr_mode) &&
88078c2ecf20Sopenharmony_ci	    (ufs_get_pm_lvl_to_link_pwr_state(hba->spm_lvl) ==
88088c2ecf20Sopenharmony_ci	     hba->uic_link_state) &&
88098c2ecf20Sopenharmony_ci	     pm_runtime_suspended(hba->dev) &&
88108c2ecf20Sopenharmony_ci	     !hba->dev_info.b_rpm_dev_flush_capable)
88118c2ecf20Sopenharmony_ci		goto out;
88128c2ecf20Sopenharmony_ci
88138c2ecf20Sopenharmony_ci	if (pm_runtime_suspended(hba->dev)) {
88148c2ecf20Sopenharmony_ci		/*
88158c2ecf20Sopenharmony_ci		 * UFS device and/or UFS link low power states during runtime
88168c2ecf20Sopenharmony_ci		 * suspend seems to be different than what is expected during
88178c2ecf20Sopenharmony_ci		 * system suspend. Hence runtime resume the devic & link and
88188c2ecf20Sopenharmony_ci		 * let the system suspend low power states to take effect.
88198c2ecf20Sopenharmony_ci		 * TODO: If resume takes longer time, we might have optimize
88208c2ecf20Sopenharmony_ci		 * it in future by not resuming everything if possible.
88218c2ecf20Sopenharmony_ci		 */
88228c2ecf20Sopenharmony_ci		ret = ufshcd_runtime_resume(hba);
88238c2ecf20Sopenharmony_ci		if (ret)
88248c2ecf20Sopenharmony_ci			goto out;
88258c2ecf20Sopenharmony_ci	}
88268c2ecf20Sopenharmony_ci
88278c2ecf20Sopenharmony_ci	ret = ufshcd_suspend(hba, UFS_SYSTEM_PM);
88288c2ecf20Sopenharmony_ciout:
88298c2ecf20Sopenharmony_ci	trace_ufshcd_system_suspend(dev_name(hba->dev), ret,
88308c2ecf20Sopenharmony_ci		ktime_to_us(ktime_sub(ktime_get(), start)),
88318c2ecf20Sopenharmony_ci		hba->curr_dev_pwr_mode, hba->uic_link_state);
88328c2ecf20Sopenharmony_ci	if (!ret)
88338c2ecf20Sopenharmony_ci		hba->is_sys_suspended = true;
88348c2ecf20Sopenharmony_ci	return ret;
88358c2ecf20Sopenharmony_ci}
88368c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ufshcd_system_suspend);
88378c2ecf20Sopenharmony_ci
88388c2ecf20Sopenharmony_ci/**
88398c2ecf20Sopenharmony_ci * ufshcd_system_resume - system resume routine
88408c2ecf20Sopenharmony_ci * @hba: per adapter instance
88418c2ecf20Sopenharmony_ci *
88428c2ecf20Sopenharmony_ci * Returns 0 for success and non-zero for failure
88438c2ecf20Sopenharmony_ci */
88448c2ecf20Sopenharmony_ci
88458c2ecf20Sopenharmony_ciint ufshcd_system_resume(struct ufs_hba *hba)
88468c2ecf20Sopenharmony_ci{
88478c2ecf20Sopenharmony_ci	int ret = 0;
88488c2ecf20Sopenharmony_ci	ktime_t start = ktime_get();
88498c2ecf20Sopenharmony_ci
88508c2ecf20Sopenharmony_ci	if (!hba)
88518c2ecf20Sopenharmony_ci		return -EINVAL;
88528c2ecf20Sopenharmony_ci
88538c2ecf20Sopenharmony_ci	if (!hba->is_powered || pm_runtime_suspended(hba->dev))
88548c2ecf20Sopenharmony_ci		/*
88558c2ecf20Sopenharmony_ci		 * Let the runtime resume take care of resuming
88568c2ecf20Sopenharmony_ci		 * if runtime suspended.
88578c2ecf20Sopenharmony_ci		 */
88588c2ecf20Sopenharmony_ci		goto out;
88598c2ecf20Sopenharmony_ci	else
88608c2ecf20Sopenharmony_ci		ret = ufshcd_resume(hba, UFS_SYSTEM_PM);
88618c2ecf20Sopenharmony_ciout:
88628c2ecf20Sopenharmony_ci	trace_ufshcd_system_resume(dev_name(hba->dev), ret,
88638c2ecf20Sopenharmony_ci		ktime_to_us(ktime_sub(ktime_get(), start)),
88648c2ecf20Sopenharmony_ci		hba->curr_dev_pwr_mode, hba->uic_link_state);
88658c2ecf20Sopenharmony_ci	if (!ret)
88668c2ecf20Sopenharmony_ci		hba->is_sys_suspended = false;
88678c2ecf20Sopenharmony_ci	return ret;
88688c2ecf20Sopenharmony_ci}
88698c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ufshcd_system_resume);
88708c2ecf20Sopenharmony_ci
88718c2ecf20Sopenharmony_ci/**
88728c2ecf20Sopenharmony_ci * ufshcd_runtime_suspend - runtime suspend routine
88738c2ecf20Sopenharmony_ci * @hba: per adapter instance
88748c2ecf20Sopenharmony_ci *
88758c2ecf20Sopenharmony_ci * Check the description of ufshcd_suspend() function for more details.
88768c2ecf20Sopenharmony_ci *
88778c2ecf20Sopenharmony_ci * Returns 0 for success and non-zero for failure
88788c2ecf20Sopenharmony_ci */
88798c2ecf20Sopenharmony_ciint ufshcd_runtime_suspend(struct ufs_hba *hba)
88808c2ecf20Sopenharmony_ci{
88818c2ecf20Sopenharmony_ci	int ret = 0;
88828c2ecf20Sopenharmony_ci	ktime_t start = ktime_get();
88838c2ecf20Sopenharmony_ci
88848c2ecf20Sopenharmony_ci	if (!hba)
88858c2ecf20Sopenharmony_ci		return -EINVAL;
88868c2ecf20Sopenharmony_ci
88878c2ecf20Sopenharmony_ci	if (!hba->is_powered)
88888c2ecf20Sopenharmony_ci		goto out;
88898c2ecf20Sopenharmony_ci	else
88908c2ecf20Sopenharmony_ci		ret = ufshcd_suspend(hba, UFS_RUNTIME_PM);
88918c2ecf20Sopenharmony_ciout:
88928c2ecf20Sopenharmony_ci	trace_ufshcd_runtime_suspend(dev_name(hba->dev), ret,
88938c2ecf20Sopenharmony_ci		ktime_to_us(ktime_sub(ktime_get(), start)),
88948c2ecf20Sopenharmony_ci		hba->curr_dev_pwr_mode, hba->uic_link_state);
88958c2ecf20Sopenharmony_ci	return ret;
88968c2ecf20Sopenharmony_ci}
88978c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ufshcd_runtime_suspend);
88988c2ecf20Sopenharmony_ci
88998c2ecf20Sopenharmony_ci/**
89008c2ecf20Sopenharmony_ci * ufshcd_runtime_resume - runtime resume routine
89018c2ecf20Sopenharmony_ci * @hba: per adapter instance
89028c2ecf20Sopenharmony_ci *
89038c2ecf20Sopenharmony_ci * This function basically brings the UFS device, UniPro link and controller
89048c2ecf20Sopenharmony_ci * to active state. Following operations are done in this function:
89058c2ecf20Sopenharmony_ci *
89068c2ecf20Sopenharmony_ci * 1. Turn on all the controller related clocks
89078c2ecf20Sopenharmony_ci * 2. Bring the UniPro link out of Hibernate state
89088c2ecf20Sopenharmony_ci * 3. If UFS device is in sleep state, turn ON VCC rail and bring the UFS device
89098c2ecf20Sopenharmony_ci *    to active state.
89108c2ecf20Sopenharmony_ci * 4. If auto-bkops is enabled on the device, disable it.
89118c2ecf20Sopenharmony_ci *
89128c2ecf20Sopenharmony_ci * So following would be the possible power state after this function return
89138c2ecf20Sopenharmony_ci * successfully:
89148c2ecf20Sopenharmony_ci *	S1: UFS device in Active state with VCC rail ON
89158c2ecf20Sopenharmony_ci *	    UniPro link in Active state
89168c2ecf20Sopenharmony_ci *	    All the UFS/UniPro controller clocks are ON
89178c2ecf20Sopenharmony_ci *
89188c2ecf20Sopenharmony_ci * Returns 0 for success and non-zero for failure
89198c2ecf20Sopenharmony_ci */
89208c2ecf20Sopenharmony_ciint ufshcd_runtime_resume(struct ufs_hba *hba)
89218c2ecf20Sopenharmony_ci{
89228c2ecf20Sopenharmony_ci	int ret = 0;
89238c2ecf20Sopenharmony_ci	ktime_t start = ktime_get();
89248c2ecf20Sopenharmony_ci
89258c2ecf20Sopenharmony_ci	if (!hba)
89268c2ecf20Sopenharmony_ci		return -EINVAL;
89278c2ecf20Sopenharmony_ci
89288c2ecf20Sopenharmony_ci	if (!hba->is_powered)
89298c2ecf20Sopenharmony_ci		goto out;
89308c2ecf20Sopenharmony_ci	else
89318c2ecf20Sopenharmony_ci		ret = ufshcd_resume(hba, UFS_RUNTIME_PM);
89328c2ecf20Sopenharmony_ciout:
89338c2ecf20Sopenharmony_ci	trace_ufshcd_runtime_resume(dev_name(hba->dev), ret,
89348c2ecf20Sopenharmony_ci		ktime_to_us(ktime_sub(ktime_get(), start)),
89358c2ecf20Sopenharmony_ci		hba->curr_dev_pwr_mode, hba->uic_link_state);
89368c2ecf20Sopenharmony_ci	return ret;
89378c2ecf20Sopenharmony_ci}
89388c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ufshcd_runtime_resume);
89398c2ecf20Sopenharmony_ci
89408c2ecf20Sopenharmony_ciint ufshcd_runtime_idle(struct ufs_hba *hba)
89418c2ecf20Sopenharmony_ci{
89428c2ecf20Sopenharmony_ci	return 0;
89438c2ecf20Sopenharmony_ci}
89448c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ufshcd_runtime_idle);
89458c2ecf20Sopenharmony_ci
89468c2ecf20Sopenharmony_ci/**
89478c2ecf20Sopenharmony_ci * ufshcd_shutdown - shutdown routine
89488c2ecf20Sopenharmony_ci * @hba: per adapter instance
89498c2ecf20Sopenharmony_ci *
89508c2ecf20Sopenharmony_ci * This function would power off both UFS device and UFS link.
89518c2ecf20Sopenharmony_ci *
89528c2ecf20Sopenharmony_ci * Returns 0 always to allow force shutdown even in case of errors.
89538c2ecf20Sopenharmony_ci */
89548c2ecf20Sopenharmony_ciint ufshcd_shutdown(struct ufs_hba *hba)
89558c2ecf20Sopenharmony_ci{
89568c2ecf20Sopenharmony_ci	int ret = 0;
89578c2ecf20Sopenharmony_ci
89588c2ecf20Sopenharmony_ci	if (!hba->is_powered)
89598c2ecf20Sopenharmony_ci		goto out;
89608c2ecf20Sopenharmony_ci
89618c2ecf20Sopenharmony_ci	if (ufshcd_is_ufs_dev_poweroff(hba) && ufshcd_is_link_off(hba))
89628c2ecf20Sopenharmony_ci		goto out;
89638c2ecf20Sopenharmony_ci
89648c2ecf20Sopenharmony_ci	pm_runtime_get_sync(hba->dev);
89658c2ecf20Sopenharmony_ci
89668c2ecf20Sopenharmony_ci	ret = ufshcd_suspend(hba, UFS_SHUTDOWN_PM);
89678c2ecf20Sopenharmony_ciout:
89688c2ecf20Sopenharmony_ci	if (ret)
89698c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s failed, err %d\n", __func__, ret);
89708c2ecf20Sopenharmony_ci	/* allow force shutdown even in case of errors */
89718c2ecf20Sopenharmony_ci	return 0;
89728c2ecf20Sopenharmony_ci}
89738c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ufshcd_shutdown);
89748c2ecf20Sopenharmony_ci
89758c2ecf20Sopenharmony_ci/**
89768c2ecf20Sopenharmony_ci * ufshcd_remove - de-allocate SCSI host and host memory space
89778c2ecf20Sopenharmony_ci *		data structure memory
89788c2ecf20Sopenharmony_ci * @hba: per adapter instance
89798c2ecf20Sopenharmony_ci */
89808c2ecf20Sopenharmony_civoid ufshcd_remove(struct ufs_hba *hba)
89818c2ecf20Sopenharmony_ci{
89828c2ecf20Sopenharmony_ci	ufs_bsg_remove(hba);
89838c2ecf20Sopenharmony_ci	ufs_sysfs_remove_nodes(hba->dev);
89848c2ecf20Sopenharmony_ci	blk_cleanup_queue(hba->tmf_queue);
89858c2ecf20Sopenharmony_ci	blk_mq_free_tag_set(&hba->tmf_tag_set);
89868c2ecf20Sopenharmony_ci	blk_cleanup_queue(hba->cmd_queue);
89878c2ecf20Sopenharmony_ci	scsi_remove_host(hba->host);
89888c2ecf20Sopenharmony_ci	destroy_workqueue(hba->eh_wq);
89898c2ecf20Sopenharmony_ci	/* disable interrupts */
89908c2ecf20Sopenharmony_ci	ufshcd_disable_intr(hba, hba->intr_mask);
89918c2ecf20Sopenharmony_ci	ufshcd_hba_stop(hba);
89928c2ecf20Sopenharmony_ci
89938c2ecf20Sopenharmony_ci	ufshcd_exit_clk_scaling(hba);
89948c2ecf20Sopenharmony_ci	ufshcd_exit_clk_gating(hba);
89958c2ecf20Sopenharmony_ci	if (ufshcd_is_clkscaling_supported(hba))
89968c2ecf20Sopenharmony_ci		device_remove_file(hba->dev, &hba->clk_scaling.enable_attr);
89978c2ecf20Sopenharmony_ci	ufshcd_hba_exit(hba);
89988c2ecf20Sopenharmony_ci}
89998c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ufshcd_remove);
90008c2ecf20Sopenharmony_ci
90018c2ecf20Sopenharmony_ci/**
90028c2ecf20Sopenharmony_ci * ufshcd_dealloc_host - deallocate Host Bus Adapter (HBA)
90038c2ecf20Sopenharmony_ci * @hba: pointer to Host Bus Adapter (HBA)
90048c2ecf20Sopenharmony_ci */
90058c2ecf20Sopenharmony_civoid ufshcd_dealloc_host(struct ufs_hba *hba)
90068c2ecf20Sopenharmony_ci{
90078c2ecf20Sopenharmony_ci	ufshcd_crypto_destroy_keyslot_manager(hba);
90088c2ecf20Sopenharmony_ci	scsi_host_put(hba->host);
90098c2ecf20Sopenharmony_ci}
90108c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ufshcd_dealloc_host);
90118c2ecf20Sopenharmony_ci
90128c2ecf20Sopenharmony_ci/**
90138c2ecf20Sopenharmony_ci * ufshcd_set_dma_mask - Set dma mask based on the controller
90148c2ecf20Sopenharmony_ci *			 addressing capability
90158c2ecf20Sopenharmony_ci * @hba: per adapter instance
90168c2ecf20Sopenharmony_ci *
90178c2ecf20Sopenharmony_ci * Returns 0 for success, non-zero for failure
90188c2ecf20Sopenharmony_ci */
90198c2ecf20Sopenharmony_cistatic int ufshcd_set_dma_mask(struct ufs_hba *hba)
90208c2ecf20Sopenharmony_ci{
90218c2ecf20Sopenharmony_ci	if (hba->capabilities & MASK_64_ADDRESSING_SUPPORT) {
90228c2ecf20Sopenharmony_ci		if (!dma_set_mask_and_coherent(hba->dev, DMA_BIT_MASK(64)))
90238c2ecf20Sopenharmony_ci			return 0;
90248c2ecf20Sopenharmony_ci	}
90258c2ecf20Sopenharmony_ci	return dma_set_mask_and_coherent(hba->dev, DMA_BIT_MASK(32));
90268c2ecf20Sopenharmony_ci}
90278c2ecf20Sopenharmony_ci
90288c2ecf20Sopenharmony_ci/**
90298c2ecf20Sopenharmony_ci * ufshcd_alloc_host - allocate Host Bus Adapter (HBA)
90308c2ecf20Sopenharmony_ci * @dev: pointer to device handle
90318c2ecf20Sopenharmony_ci * @hba_handle: driver private handle
90328c2ecf20Sopenharmony_ci * Returns 0 on success, non-zero value on failure
90338c2ecf20Sopenharmony_ci */
90348c2ecf20Sopenharmony_ciint ufshcd_alloc_host(struct device *dev, struct ufs_hba **hba_handle)
90358c2ecf20Sopenharmony_ci{
90368c2ecf20Sopenharmony_ci	struct Scsi_Host *host;
90378c2ecf20Sopenharmony_ci	struct ufs_hba *hba;
90388c2ecf20Sopenharmony_ci	int err = 0;
90398c2ecf20Sopenharmony_ci
90408c2ecf20Sopenharmony_ci	if (!dev) {
90418c2ecf20Sopenharmony_ci		dev_err(dev,
90428c2ecf20Sopenharmony_ci		"Invalid memory reference for dev is NULL\n");
90438c2ecf20Sopenharmony_ci		err = -ENODEV;
90448c2ecf20Sopenharmony_ci		goto out_error;
90458c2ecf20Sopenharmony_ci	}
90468c2ecf20Sopenharmony_ci
90478c2ecf20Sopenharmony_ci	host = scsi_host_alloc(&ufshcd_driver_template,
90488c2ecf20Sopenharmony_ci				sizeof(struct ufs_hba));
90498c2ecf20Sopenharmony_ci	if (!host) {
90508c2ecf20Sopenharmony_ci		dev_err(dev, "scsi_host_alloc failed\n");
90518c2ecf20Sopenharmony_ci		err = -ENOMEM;
90528c2ecf20Sopenharmony_ci		goto out_error;
90538c2ecf20Sopenharmony_ci	}
90548c2ecf20Sopenharmony_ci	hba = shost_priv(host);
90558c2ecf20Sopenharmony_ci	hba->host = host;
90568c2ecf20Sopenharmony_ci	hba->dev = dev;
90578c2ecf20Sopenharmony_ci	*hba_handle = hba;
90588c2ecf20Sopenharmony_ci	hba->dev_ref_clk_freq = REF_CLK_FREQ_INVAL;
90598c2ecf20Sopenharmony_ci
90608c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&hba->clk_list_head);
90618c2ecf20Sopenharmony_ci
90628c2ecf20Sopenharmony_ciout_error:
90638c2ecf20Sopenharmony_ci	return err;
90648c2ecf20Sopenharmony_ci}
90658c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ufshcd_alloc_host);
90668c2ecf20Sopenharmony_ci
90678c2ecf20Sopenharmony_ci/* This function exists because blk_mq_alloc_tag_set() requires this. */
90688c2ecf20Sopenharmony_cistatic blk_status_t ufshcd_queue_tmf(struct blk_mq_hw_ctx *hctx,
90698c2ecf20Sopenharmony_ci				     const struct blk_mq_queue_data *qd)
90708c2ecf20Sopenharmony_ci{
90718c2ecf20Sopenharmony_ci	WARN_ON_ONCE(true);
90728c2ecf20Sopenharmony_ci	return BLK_STS_NOTSUPP;
90738c2ecf20Sopenharmony_ci}
90748c2ecf20Sopenharmony_ci
90758c2ecf20Sopenharmony_cistatic const struct blk_mq_ops ufshcd_tmf_ops = {
90768c2ecf20Sopenharmony_ci	.queue_rq = ufshcd_queue_tmf,
90778c2ecf20Sopenharmony_ci};
90788c2ecf20Sopenharmony_ci
90798c2ecf20Sopenharmony_ci/**
90808c2ecf20Sopenharmony_ci * ufshcd_init - Driver initialization routine
90818c2ecf20Sopenharmony_ci * @hba: per-adapter instance
90828c2ecf20Sopenharmony_ci * @mmio_base: base register address
90838c2ecf20Sopenharmony_ci * @irq: Interrupt line of device
90848c2ecf20Sopenharmony_ci * Returns 0 on success, non-zero value on failure
90858c2ecf20Sopenharmony_ci */
90868c2ecf20Sopenharmony_ciint ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
90878c2ecf20Sopenharmony_ci{
90888c2ecf20Sopenharmony_ci	int err;
90898c2ecf20Sopenharmony_ci	struct Scsi_Host *host = hba->host;
90908c2ecf20Sopenharmony_ci	struct device *dev = hba->dev;
90918c2ecf20Sopenharmony_ci	char eh_wq_name[sizeof("ufs_eh_wq_00")];
90928c2ecf20Sopenharmony_ci
90938c2ecf20Sopenharmony_ci	/*
90948c2ecf20Sopenharmony_ci	 * dev_set_drvdata() must be called before any callbacks are registered
90958c2ecf20Sopenharmony_ci	 * that use dev_get_drvdata() (frequency scaling, clock scaling, hwmon,
90968c2ecf20Sopenharmony_ci	 * sysfs).
90978c2ecf20Sopenharmony_ci	 */
90988c2ecf20Sopenharmony_ci	dev_set_drvdata(dev, hba);
90998c2ecf20Sopenharmony_ci
91008c2ecf20Sopenharmony_ci	if (!mmio_base) {
91018c2ecf20Sopenharmony_ci		dev_err(hba->dev,
91028c2ecf20Sopenharmony_ci		"Invalid memory reference for mmio_base is NULL\n");
91038c2ecf20Sopenharmony_ci		err = -ENODEV;
91048c2ecf20Sopenharmony_ci		goto out_error;
91058c2ecf20Sopenharmony_ci	}
91068c2ecf20Sopenharmony_ci
91078c2ecf20Sopenharmony_ci	hba->mmio_base = mmio_base;
91088c2ecf20Sopenharmony_ci	hba->irq = irq;
91098c2ecf20Sopenharmony_ci	hba->vps = &ufs_hba_vps;
91108c2ecf20Sopenharmony_ci
91118c2ecf20Sopenharmony_ci	err = ufshcd_hba_init(hba);
91128c2ecf20Sopenharmony_ci	if (err)
91138c2ecf20Sopenharmony_ci		goto out_error;
91148c2ecf20Sopenharmony_ci
91158c2ecf20Sopenharmony_ci	/* Read capabilities registers */
91168c2ecf20Sopenharmony_ci	err = ufshcd_hba_capabilities(hba);
91178c2ecf20Sopenharmony_ci	if (err)
91188c2ecf20Sopenharmony_ci		goto out_disable;
91198c2ecf20Sopenharmony_ci
91208c2ecf20Sopenharmony_ci	/* Get UFS version supported by the controller */
91218c2ecf20Sopenharmony_ci	hba->ufs_version = ufshcd_get_ufs_version(hba);
91228c2ecf20Sopenharmony_ci
91238c2ecf20Sopenharmony_ci	if ((hba->ufs_version != UFSHCI_VERSION_10) &&
91248c2ecf20Sopenharmony_ci	    (hba->ufs_version != UFSHCI_VERSION_11) &&
91258c2ecf20Sopenharmony_ci	    (hba->ufs_version != UFSHCI_VERSION_20) &&
91268c2ecf20Sopenharmony_ci	    (hba->ufs_version != UFSHCI_VERSION_21))
91278c2ecf20Sopenharmony_ci		dev_err(hba->dev, "invalid UFS version 0x%x\n",
91288c2ecf20Sopenharmony_ci			hba->ufs_version);
91298c2ecf20Sopenharmony_ci
91308c2ecf20Sopenharmony_ci	/* Get Interrupt bit mask per version */
91318c2ecf20Sopenharmony_ci	hba->intr_mask = ufshcd_get_intr_mask(hba);
91328c2ecf20Sopenharmony_ci
91338c2ecf20Sopenharmony_ci	err = ufshcd_set_dma_mask(hba);
91348c2ecf20Sopenharmony_ci	if (err) {
91358c2ecf20Sopenharmony_ci		dev_err(hba->dev, "set dma mask failed\n");
91368c2ecf20Sopenharmony_ci		goto out_disable;
91378c2ecf20Sopenharmony_ci	}
91388c2ecf20Sopenharmony_ci
91398c2ecf20Sopenharmony_ci	/* Allocate memory for host memory space */
91408c2ecf20Sopenharmony_ci	err = ufshcd_memory_alloc(hba);
91418c2ecf20Sopenharmony_ci	if (err) {
91428c2ecf20Sopenharmony_ci		dev_err(hba->dev, "Memory allocation failed\n");
91438c2ecf20Sopenharmony_ci		goto out_disable;
91448c2ecf20Sopenharmony_ci	}
91458c2ecf20Sopenharmony_ci
91468c2ecf20Sopenharmony_ci	/* Configure LRB */
91478c2ecf20Sopenharmony_ci	ufshcd_host_memory_configure(hba);
91488c2ecf20Sopenharmony_ci
91498c2ecf20Sopenharmony_ci	host->can_queue = hba->nutrs;
91508c2ecf20Sopenharmony_ci	host->cmd_per_lun = hba->nutrs;
91518c2ecf20Sopenharmony_ci	host->max_id = UFSHCD_MAX_ID;
91528c2ecf20Sopenharmony_ci	host->max_lun = UFS_MAX_LUNS;
91538c2ecf20Sopenharmony_ci	host->max_channel = UFSHCD_MAX_CHANNEL;
91548c2ecf20Sopenharmony_ci	host->unique_id = host->host_no;
91558c2ecf20Sopenharmony_ci	host->max_cmd_len = UFS_CDB_SIZE;
91568c2ecf20Sopenharmony_ci
91578c2ecf20Sopenharmony_ci	hba->max_pwr_info.is_valid = false;
91588c2ecf20Sopenharmony_ci
91598c2ecf20Sopenharmony_ci	/* Initialize work queues */
91608c2ecf20Sopenharmony_ci	snprintf(eh_wq_name, sizeof(eh_wq_name), "ufs_eh_wq_%d",
91618c2ecf20Sopenharmony_ci		 hba->host->host_no);
91628c2ecf20Sopenharmony_ci	hba->eh_wq = create_singlethread_workqueue(eh_wq_name);
91638c2ecf20Sopenharmony_ci	if (!hba->eh_wq) {
91648c2ecf20Sopenharmony_ci		dev_err(hba->dev, "%s: failed to create eh workqueue\n",
91658c2ecf20Sopenharmony_ci				__func__);
91668c2ecf20Sopenharmony_ci		err = -ENOMEM;
91678c2ecf20Sopenharmony_ci		goto out_disable;
91688c2ecf20Sopenharmony_ci	}
91698c2ecf20Sopenharmony_ci	INIT_WORK(&hba->eh_work, ufshcd_err_handler);
91708c2ecf20Sopenharmony_ci	INIT_WORK(&hba->eeh_work, ufshcd_exception_event_handler);
91718c2ecf20Sopenharmony_ci
91728c2ecf20Sopenharmony_ci	/* Initialize UIC command mutex */
91738c2ecf20Sopenharmony_ci	mutex_init(&hba->uic_cmd_mutex);
91748c2ecf20Sopenharmony_ci
91758c2ecf20Sopenharmony_ci	/* Initialize mutex for device management commands */
91768c2ecf20Sopenharmony_ci	mutex_init(&hba->dev_cmd.lock);
91778c2ecf20Sopenharmony_ci
91788c2ecf20Sopenharmony_ci	init_rwsem(&hba->clk_scaling_lock);
91798c2ecf20Sopenharmony_ci
91808c2ecf20Sopenharmony_ci	ufshcd_init_clk_gating(hba);
91818c2ecf20Sopenharmony_ci
91828c2ecf20Sopenharmony_ci	ufshcd_init_clk_scaling(hba);
91838c2ecf20Sopenharmony_ci
91848c2ecf20Sopenharmony_ci	/*
91858c2ecf20Sopenharmony_ci	 * In order to avoid any spurious interrupt immediately after
91868c2ecf20Sopenharmony_ci	 * registering UFS controller interrupt handler, clear any pending UFS
91878c2ecf20Sopenharmony_ci	 * interrupt status and disable all the UFS interrupts.
91888c2ecf20Sopenharmony_ci	 */
91898c2ecf20Sopenharmony_ci	ufshcd_writel(hba, ufshcd_readl(hba, REG_INTERRUPT_STATUS),
91908c2ecf20Sopenharmony_ci		      REG_INTERRUPT_STATUS);
91918c2ecf20Sopenharmony_ci	ufshcd_writel(hba, 0, REG_INTERRUPT_ENABLE);
91928c2ecf20Sopenharmony_ci	/*
91938c2ecf20Sopenharmony_ci	 * Make sure that UFS interrupts are disabled and any pending interrupt
91948c2ecf20Sopenharmony_ci	 * status is cleared before registering UFS interrupt handler.
91958c2ecf20Sopenharmony_ci	 */
91968c2ecf20Sopenharmony_ci	mb();
91978c2ecf20Sopenharmony_ci
91988c2ecf20Sopenharmony_ci	/* IRQ registration */
91998c2ecf20Sopenharmony_ci	err = devm_request_irq(dev, irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba);
92008c2ecf20Sopenharmony_ci	if (err) {
92018c2ecf20Sopenharmony_ci		dev_err(hba->dev, "request irq failed\n");
92028c2ecf20Sopenharmony_ci		goto exit_gating;
92038c2ecf20Sopenharmony_ci	} else {
92048c2ecf20Sopenharmony_ci		hba->is_irq_enabled = true;
92058c2ecf20Sopenharmony_ci	}
92068c2ecf20Sopenharmony_ci
92078c2ecf20Sopenharmony_ci	err = scsi_add_host(host, hba->dev);
92088c2ecf20Sopenharmony_ci	if (err) {
92098c2ecf20Sopenharmony_ci		dev_err(hba->dev, "scsi_add_host failed\n");
92108c2ecf20Sopenharmony_ci		goto exit_gating;
92118c2ecf20Sopenharmony_ci	}
92128c2ecf20Sopenharmony_ci
92138c2ecf20Sopenharmony_ci	hba->cmd_queue = blk_mq_init_queue(&hba->host->tag_set);
92148c2ecf20Sopenharmony_ci	if (IS_ERR(hba->cmd_queue)) {
92158c2ecf20Sopenharmony_ci		err = PTR_ERR(hba->cmd_queue);
92168c2ecf20Sopenharmony_ci		goto out_remove_scsi_host;
92178c2ecf20Sopenharmony_ci	}
92188c2ecf20Sopenharmony_ci
92198c2ecf20Sopenharmony_ci	hba->tmf_tag_set = (struct blk_mq_tag_set) {
92208c2ecf20Sopenharmony_ci		.nr_hw_queues	= 1,
92218c2ecf20Sopenharmony_ci		.queue_depth	= hba->nutmrs,
92228c2ecf20Sopenharmony_ci		.ops		= &ufshcd_tmf_ops,
92238c2ecf20Sopenharmony_ci		.flags		= BLK_MQ_F_NO_SCHED,
92248c2ecf20Sopenharmony_ci	};
92258c2ecf20Sopenharmony_ci	err = blk_mq_alloc_tag_set(&hba->tmf_tag_set);
92268c2ecf20Sopenharmony_ci	if (err < 0)
92278c2ecf20Sopenharmony_ci		goto free_cmd_queue;
92288c2ecf20Sopenharmony_ci	hba->tmf_queue = blk_mq_init_queue(&hba->tmf_tag_set);
92298c2ecf20Sopenharmony_ci	if (IS_ERR(hba->tmf_queue)) {
92308c2ecf20Sopenharmony_ci		err = PTR_ERR(hba->tmf_queue);
92318c2ecf20Sopenharmony_ci		goto free_tmf_tag_set;
92328c2ecf20Sopenharmony_ci	}
92338c2ecf20Sopenharmony_ci	hba->tmf_rqs = devm_kcalloc(hba->dev, hba->nutmrs,
92348c2ecf20Sopenharmony_ci				    sizeof(*hba->tmf_rqs), GFP_KERNEL);
92358c2ecf20Sopenharmony_ci	if (!hba->tmf_rqs) {
92368c2ecf20Sopenharmony_ci		err = -ENOMEM;
92378c2ecf20Sopenharmony_ci		goto free_tmf_queue;
92388c2ecf20Sopenharmony_ci	}
92398c2ecf20Sopenharmony_ci
92408c2ecf20Sopenharmony_ci	/* Reset the attached device */
92418c2ecf20Sopenharmony_ci	ufshcd_vops_device_reset(hba);
92428c2ecf20Sopenharmony_ci
92438c2ecf20Sopenharmony_ci	ufshcd_init_crypto(hba);
92448c2ecf20Sopenharmony_ci
92458c2ecf20Sopenharmony_ci	/* Host controller enable */
92468c2ecf20Sopenharmony_ci	err = ufshcd_hba_enable(hba);
92478c2ecf20Sopenharmony_ci	if (err) {
92488c2ecf20Sopenharmony_ci		dev_err(hba->dev, "Host controller enable failed\n");
92498c2ecf20Sopenharmony_ci		ufshcd_print_host_regs(hba);
92508c2ecf20Sopenharmony_ci		ufshcd_print_host_state(hba);
92518c2ecf20Sopenharmony_ci		goto free_tmf_queue;
92528c2ecf20Sopenharmony_ci	}
92538c2ecf20Sopenharmony_ci
92548c2ecf20Sopenharmony_ci	/*
92558c2ecf20Sopenharmony_ci	 * Set the default power management level for runtime and system PM.
92568c2ecf20Sopenharmony_ci	 * Default power saving mode is to keep UFS link in Hibern8 state
92578c2ecf20Sopenharmony_ci	 * and UFS device in sleep state.
92588c2ecf20Sopenharmony_ci	 */
92598c2ecf20Sopenharmony_ci	hba->rpm_lvl = ufs_get_desired_pm_lvl_for_dev_link_state(
92608c2ecf20Sopenharmony_ci						UFS_SLEEP_PWR_MODE,
92618c2ecf20Sopenharmony_ci						UIC_LINK_HIBERN8_STATE);
92628c2ecf20Sopenharmony_ci	hba->spm_lvl = ufs_get_desired_pm_lvl_for_dev_link_state(
92638c2ecf20Sopenharmony_ci						UFS_SLEEP_PWR_MODE,
92648c2ecf20Sopenharmony_ci						UIC_LINK_HIBERN8_STATE);
92658c2ecf20Sopenharmony_ci
92668c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&hba->rpm_dev_flush_recheck_work,
92678c2ecf20Sopenharmony_ci			  ufshcd_rpm_dev_flush_recheck_work);
92688c2ecf20Sopenharmony_ci
92698c2ecf20Sopenharmony_ci	/* Set the default auto-hiberate idle timer value to 150 ms */
92708c2ecf20Sopenharmony_ci	if (ufshcd_is_auto_hibern8_supported(hba) && !hba->ahit) {
92718c2ecf20Sopenharmony_ci		hba->ahit = FIELD_PREP(UFSHCI_AHIBERN8_TIMER_MASK, 150) |
92728c2ecf20Sopenharmony_ci			    FIELD_PREP(UFSHCI_AHIBERN8_SCALE_MASK, 3);
92738c2ecf20Sopenharmony_ci	}
92748c2ecf20Sopenharmony_ci
92758c2ecf20Sopenharmony_ci	/* Hold auto suspend until async scan completes */
92768c2ecf20Sopenharmony_ci	pm_runtime_get_sync(dev);
92778c2ecf20Sopenharmony_ci	atomic_set(&hba->scsi_block_reqs_cnt, 0);
92788c2ecf20Sopenharmony_ci	/*
92798c2ecf20Sopenharmony_ci	 * We are assuming that device wasn't put in sleep/power-down
92808c2ecf20Sopenharmony_ci	 * state exclusively during the boot stage before kernel.
92818c2ecf20Sopenharmony_ci	 * This assumption helps avoid doing link startup twice during
92828c2ecf20Sopenharmony_ci	 * ufshcd_probe_hba().
92838c2ecf20Sopenharmony_ci	 */
92848c2ecf20Sopenharmony_ci	ufshcd_set_ufs_dev_active(hba);
92858c2ecf20Sopenharmony_ci
92868c2ecf20Sopenharmony_ci	async_schedule(ufshcd_async_scan, hba);
92878c2ecf20Sopenharmony_ci	ufs_sysfs_add_nodes(hba->dev);
92888c2ecf20Sopenharmony_ci
92898c2ecf20Sopenharmony_ci	return 0;
92908c2ecf20Sopenharmony_ci
92918c2ecf20Sopenharmony_cifree_tmf_queue:
92928c2ecf20Sopenharmony_ci	blk_cleanup_queue(hba->tmf_queue);
92938c2ecf20Sopenharmony_cifree_tmf_tag_set:
92948c2ecf20Sopenharmony_ci	blk_mq_free_tag_set(&hba->tmf_tag_set);
92958c2ecf20Sopenharmony_cifree_cmd_queue:
92968c2ecf20Sopenharmony_ci	blk_cleanup_queue(hba->cmd_queue);
92978c2ecf20Sopenharmony_ciout_remove_scsi_host:
92988c2ecf20Sopenharmony_ci	scsi_remove_host(hba->host);
92998c2ecf20Sopenharmony_ciexit_gating:
93008c2ecf20Sopenharmony_ci	ufshcd_exit_clk_scaling(hba);
93018c2ecf20Sopenharmony_ci	ufshcd_exit_clk_gating(hba);
93028c2ecf20Sopenharmony_ci	destroy_workqueue(hba->eh_wq);
93038c2ecf20Sopenharmony_ciout_disable:
93048c2ecf20Sopenharmony_ci	hba->is_irq_enabled = false;
93058c2ecf20Sopenharmony_ci	ufshcd_hba_exit(hba);
93068c2ecf20Sopenharmony_ciout_error:
93078c2ecf20Sopenharmony_ci	return err;
93088c2ecf20Sopenharmony_ci}
93098c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ufshcd_init);
93108c2ecf20Sopenharmony_ci
93118c2ecf20Sopenharmony_ciMODULE_AUTHOR("Santosh Yaragnavi <santosh.sy@samsung.com>");
93128c2ecf20Sopenharmony_ciMODULE_AUTHOR("Vinayak Holikatti <h.vinayak@samsung.com>");
93138c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Generic UFS host controller driver Core");
93148c2ecf20Sopenharmony_ciMODULE_SOFTDEP("pre: governor_simpleondemand");
93158c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
93168c2ecf20Sopenharmony_ciMODULE_VERSION(UFSHCD_DRIVER_VERSION);
9317