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