18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2013-2016, Linux Foundation. All rights reserved. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/acpi.h> 78c2ecf20Sopenharmony_ci#include <linux/time.h> 88c2ecf20Sopenharmony_ci#include <linux/of.h> 98c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 108c2ecf20Sopenharmony_ci#include <linux/phy/phy.h> 118c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 128c2ecf20Sopenharmony_ci#include <linux/reset-controller.h> 138c2ecf20Sopenharmony_ci#include <linux/devfreq.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "ufshcd.h" 168c2ecf20Sopenharmony_ci#include "ufshcd-pltfrm.h" 178c2ecf20Sopenharmony_ci#include "unipro.h" 188c2ecf20Sopenharmony_ci#include "ufs-qcom.h" 198c2ecf20Sopenharmony_ci#include "ufshci.h" 208c2ecf20Sopenharmony_ci#include "ufs_quirks.h" 218c2ecf20Sopenharmony_ci#define UFS_QCOM_DEFAULT_DBG_PRINT_EN \ 228c2ecf20Sopenharmony_ci (UFS_QCOM_DBG_PRINT_REGS_EN | UFS_QCOM_DBG_PRINT_TEST_BUS_EN) 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cienum { 258c2ecf20Sopenharmony_ci TSTBUS_UAWM, 268c2ecf20Sopenharmony_ci TSTBUS_UARM, 278c2ecf20Sopenharmony_ci TSTBUS_TXUC, 288c2ecf20Sopenharmony_ci TSTBUS_RXUC, 298c2ecf20Sopenharmony_ci TSTBUS_DFC, 308c2ecf20Sopenharmony_ci TSTBUS_TRLUT, 318c2ecf20Sopenharmony_ci TSTBUS_TMRLUT, 328c2ecf20Sopenharmony_ci TSTBUS_OCSC, 338c2ecf20Sopenharmony_ci TSTBUS_UTP_HCI, 348c2ecf20Sopenharmony_ci TSTBUS_COMBINED, 358c2ecf20Sopenharmony_ci TSTBUS_WRAPPER, 368c2ecf20Sopenharmony_ci TSTBUS_UNIPRO, 378c2ecf20Sopenharmony_ci TSTBUS_MAX, 388c2ecf20Sopenharmony_ci}; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic struct ufs_qcom_host *ufs_qcom_hosts[MAX_UFS_QCOM_HOSTS]; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host); 438c2ecf20Sopenharmony_cistatic int ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(struct ufs_hba *hba, 448c2ecf20Sopenharmony_ci u32 clk_cycles); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic struct ufs_qcom_host *rcdev_to_ufs_host(struct reset_controller_dev *rcd) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci return container_of(rcd, struct ufs_qcom_host, rcdev); 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic void ufs_qcom_dump_regs_wrapper(struct ufs_hba *hba, int offset, int len, 528c2ecf20Sopenharmony_ci const char *prefix, void *priv) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci ufshcd_dump_regs(hba, offset, len * 4, prefix); 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic int ufs_qcom_get_connected_tx_lanes(struct ufs_hba *hba, u32 *tx_lanes) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci int err = 0; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci err = ufshcd_dme_get(hba, 628c2ecf20Sopenharmony_ci UIC_ARG_MIB(PA_CONNECTEDTXDATALANES), tx_lanes); 638c2ecf20Sopenharmony_ci if (err) 648c2ecf20Sopenharmony_ci dev_err(hba->dev, "%s: couldn't read PA_CONNECTEDTXDATALANES %d\n", 658c2ecf20Sopenharmony_ci __func__, err); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci return err; 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic int ufs_qcom_host_clk_get(struct device *dev, 718c2ecf20Sopenharmony_ci const char *name, struct clk **clk_out, bool optional) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci struct clk *clk; 748c2ecf20Sopenharmony_ci int err = 0; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci clk = devm_clk_get(dev, name); 778c2ecf20Sopenharmony_ci if (!IS_ERR(clk)) { 788c2ecf20Sopenharmony_ci *clk_out = clk; 798c2ecf20Sopenharmony_ci return 0; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci err = PTR_ERR(clk); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci if (optional && err == -ENOENT) { 858c2ecf20Sopenharmony_ci *clk_out = NULL; 868c2ecf20Sopenharmony_ci return 0; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (err != -EPROBE_DEFER) 908c2ecf20Sopenharmony_ci dev_err(dev, "failed to get %s err %d\n", name, err); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci return err; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic int ufs_qcom_host_clk_enable(struct device *dev, 968c2ecf20Sopenharmony_ci const char *name, struct clk *clk) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci int err = 0; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci err = clk_prepare_enable(clk); 1018c2ecf20Sopenharmony_ci if (err) 1028c2ecf20Sopenharmony_ci dev_err(dev, "%s: %s enable failed %d\n", __func__, name, err); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci return err; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic void ufs_qcom_disable_lane_clks(struct ufs_qcom_host *host) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci if (!host->is_lane_clks_enabled) 1108c2ecf20Sopenharmony_ci return; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci clk_disable_unprepare(host->tx_l1_sync_clk); 1138c2ecf20Sopenharmony_ci clk_disable_unprepare(host->tx_l0_sync_clk); 1148c2ecf20Sopenharmony_ci clk_disable_unprepare(host->rx_l1_sync_clk); 1158c2ecf20Sopenharmony_ci clk_disable_unprepare(host->rx_l0_sync_clk); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci host->is_lane_clks_enabled = false; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic int ufs_qcom_enable_lane_clks(struct ufs_qcom_host *host) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci int err = 0; 1238c2ecf20Sopenharmony_ci struct device *dev = host->hba->dev; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (host->is_lane_clks_enabled) 1268c2ecf20Sopenharmony_ci return 0; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci err = ufs_qcom_host_clk_enable(dev, "rx_lane0_sync_clk", 1298c2ecf20Sopenharmony_ci host->rx_l0_sync_clk); 1308c2ecf20Sopenharmony_ci if (err) 1318c2ecf20Sopenharmony_ci goto out; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci err = ufs_qcom_host_clk_enable(dev, "tx_lane0_sync_clk", 1348c2ecf20Sopenharmony_ci host->tx_l0_sync_clk); 1358c2ecf20Sopenharmony_ci if (err) 1368c2ecf20Sopenharmony_ci goto disable_rx_l0; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci err = ufs_qcom_host_clk_enable(dev, "rx_lane1_sync_clk", 1398c2ecf20Sopenharmony_ci host->rx_l1_sync_clk); 1408c2ecf20Sopenharmony_ci if (err) 1418c2ecf20Sopenharmony_ci goto disable_tx_l0; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci err = ufs_qcom_host_clk_enable(dev, "tx_lane1_sync_clk", 1448c2ecf20Sopenharmony_ci host->tx_l1_sync_clk); 1458c2ecf20Sopenharmony_ci if (err) 1468c2ecf20Sopenharmony_ci goto disable_rx_l1; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci host->is_lane_clks_enabled = true; 1498c2ecf20Sopenharmony_ci goto out; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cidisable_rx_l1: 1528c2ecf20Sopenharmony_ci clk_disable_unprepare(host->rx_l1_sync_clk); 1538c2ecf20Sopenharmony_cidisable_tx_l0: 1548c2ecf20Sopenharmony_ci clk_disable_unprepare(host->tx_l0_sync_clk); 1558c2ecf20Sopenharmony_cidisable_rx_l0: 1568c2ecf20Sopenharmony_ci clk_disable_unprepare(host->rx_l0_sync_clk); 1578c2ecf20Sopenharmony_ciout: 1588c2ecf20Sopenharmony_ci return err; 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic int ufs_qcom_init_lane_clks(struct ufs_qcom_host *host) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci int err = 0; 1648c2ecf20Sopenharmony_ci struct device *dev = host->hba->dev; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (has_acpi_companion(dev)) 1678c2ecf20Sopenharmony_ci return 0; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci err = ufs_qcom_host_clk_get(dev, "rx_lane0_sync_clk", 1708c2ecf20Sopenharmony_ci &host->rx_l0_sync_clk, false); 1718c2ecf20Sopenharmony_ci if (err) 1728c2ecf20Sopenharmony_ci goto out; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci err = ufs_qcom_host_clk_get(dev, "tx_lane0_sync_clk", 1758c2ecf20Sopenharmony_ci &host->tx_l0_sync_clk, false); 1768c2ecf20Sopenharmony_ci if (err) 1778c2ecf20Sopenharmony_ci goto out; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci /* In case of single lane per direction, don't read lane1 clocks */ 1808c2ecf20Sopenharmony_ci if (host->hba->lanes_per_direction > 1) { 1818c2ecf20Sopenharmony_ci err = ufs_qcom_host_clk_get(dev, "rx_lane1_sync_clk", 1828c2ecf20Sopenharmony_ci &host->rx_l1_sync_clk, false); 1838c2ecf20Sopenharmony_ci if (err) 1848c2ecf20Sopenharmony_ci goto out; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci err = ufs_qcom_host_clk_get(dev, "tx_lane1_sync_clk", 1878c2ecf20Sopenharmony_ci &host->tx_l1_sync_clk, true); 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ciout: 1908c2ecf20Sopenharmony_ci return err; 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistatic int ufs_qcom_link_startup_post_change(struct ufs_hba *hba) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci u32 tx_lanes; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci return ufs_qcom_get_connected_tx_lanes(hba, &tx_lanes); 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic int ufs_qcom_check_hibern8(struct ufs_hba *hba) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci int err; 2038c2ecf20Sopenharmony_ci u32 tx_fsm_val = 0; 2048c2ecf20Sopenharmony_ci unsigned long timeout = jiffies + msecs_to_jiffies(HBRN8_POLL_TOUT_MS); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci do { 2078c2ecf20Sopenharmony_ci err = ufshcd_dme_get(hba, 2088c2ecf20Sopenharmony_ci UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE, 2098c2ecf20Sopenharmony_ci UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)), 2108c2ecf20Sopenharmony_ci &tx_fsm_val); 2118c2ecf20Sopenharmony_ci if (err || tx_fsm_val == TX_FSM_HIBERN8) 2128c2ecf20Sopenharmony_ci break; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* sleep for max. 200us */ 2158c2ecf20Sopenharmony_ci usleep_range(100, 200); 2168c2ecf20Sopenharmony_ci } while (time_before(jiffies, timeout)); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci /* 2198c2ecf20Sopenharmony_ci * we might have scheduled out for long during polling so 2208c2ecf20Sopenharmony_ci * check the state again. 2218c2ecf20Sopenharmony_ci */ 2228c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) 2238c2ecf20Sopenharmony_ci err = ufshcd_dme_get(hba, 2248c2ecf20Sopenharmony_ci UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE, 2258c2ecf20Sopenharmony_ci UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)), 2268c2ecf20Sopenharmony_ci &tx_fsm_val); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (err) { 2298c2ecf20Sopenharmony_ci dev_err(hba->dev, "%s: unable to get TX_FSM_STATE, err %d\n", 2308c2ecf20Sopenharmony_ci __func__, err); 2318c2ecf20Sopenharmony_ci } else if (tx_fsm_val != TX_FSM_HIBERN8) { 2328c2ecf20Sopenharmony_ci err = tx_fsm_val; 2338c2ecf20Sopenharmony_ci dev_err(hba->dev, "%s: invalid TX_FSM_STATE = %d\n", 2348c2ecf20Sopenharmony_ci __func__, err); 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci return err; 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic void ufs_qcom_select_unipro_mode(struct ufs_qcom_host *host) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci ufshcd_rmwl(host->hba, QUNIPRO_SEL, 2438c2ecf20Sopenharmony_ci ufs_qcom_cap_qunipro(host) ? QUNIPRO_SEL : 0, 2448c2ecf20Sopenharmony_ci REG_UFS_CFG1); 2458c2ecf20Sopenharmony_ci /* make sure above configuration is applied before we return */ 2468c2ecf20Sopenharmony_ci mb(); 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci/* 2508c2ecf20Sopenharmony_ci * ufs_qcom_host_reset - reset host controller and PHY 2518c2ecf20Sopenharmony_ci */ 2528c2ecf20Sopenharmony_cistatic int ufs_qcom_host_reset(struct ufs_hba *hba) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci int ret = 0; 2558c2ecf20Sopenharmony_ci struct ufs_qcom_host *host = ufshcd_get_variant(hba); 2568c2ecf20Sopenharmony_ci bool reenable_intr = false; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci if (!host->core_reset) { 2598c2ecf20Sopenharmony_ci dev_warn(hba->dev, "%s: reset control not set\n", __func__); 2608c2ecf20Sopenharmony_ci goto out; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci reenable_intr = hba->is_irq_enabled; 2648c2ecf20Sopenharmony_ci disable_irq(hba->irq); 2658c2ecf20Sopenharmony_ci hba->is_irq_enabled = false; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci ret = reset_control_assert(host->core_reset); 2688c2ecf20Sopenharmony_ci if (ret) { 2698c2ecf20Sopenharmony_ci dev_err(hba->dev, "%s: core_reset assert failed, err = %d\n", 2708c2ecf20Sopenharmony_ci __func__, ret); 2718c2ecf20Sopenharmony_ci goto out; 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* 2758c2ecf20Sopenharmony_ci * The hardware requirement for delay between assert/deassert 2768c2ecf20Sopenharmony_ci * is at least 3-4 sleep clock (32.7KHz) cycles, which comes to 2778c2ecf20Sopenharmony_ci * ~125us (4/32768). To be on the safe side add 200us delay. 2788c2ecf20Sopenharmony_ci */ 2798c2ecf20Sopenharmony_ci usleep_range(200, 210); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci ret = reset_control_deassert(host->core_reset); 2828c2ecf20Sopenharmony_ci if (ret) 2838c2ecf20Sopenharmony_ci dev_err(hba->dev, "%s: core_reset deassert failed, err = %d\n", 2848c2ecf20Sopenharmony_ci __func__, ret); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci usleep_range(1000, 1100); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (reenable_intr) { 2898c2ecf20Sopenharmony_ci enable_irq(hba->irq); 2908c2ecf20Sopenharmony_ci hba->is_irq_enabled = true; 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ciout: 2948c2ecf20Sopenharmony_ci return ret; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic int ufs_qcom_power_up_sequence(struct ufs_hba *hba) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci struct ufs_qcom_host *host = ufshcd_get_variant(hba); 3008c2ecf20Sopenharmony_ci struct phy *phy = host->generic_phy; 3018c2ecf20Sopenharmony_ci int ret = 0; 3028c2ecf20Sopenharmony_ci bool is_rate_B = (UFS_QCOM_LIMIT_HS_RATE == PA_HS_MODE_B) 3038c2ecf20Sopenharmony_ci ? true : false; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci /* Reset UFS Host Controller and PHY */ 3068c2ecf20Sopenharmony_ci ret = ufs_qcom_host_reset(hba); 3078c2ecf20Sopenharmony_ci if (ret) 3088c2ecf20Sopenharmony_ci dev_warn(hba->dev, "%s: host reset returned %d\n", 3098c2ecf20Sopenharmony_ci __func__, ret); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci if (is_rate_B) 3128c2ecf20Sopenharmony_ci phy_set_mode(phy, PHY_MODE_UFS_HS_B); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci /* phy initialization - calibrate the phy */ 3158c2ecf20Sopenharmony_ci ret = phy_init(phy); 3168c2ecf20Sopenharmony_ci if (ret) { 3178c2ecf20Sopenharmony_ci dev_err(hba->dev, "%s: phy init failed, ret = %d\n", 3188c2ecf20Sopenharmony_ci __func__, ret); 3198c2ecf20Sopenharmony_ci goto out; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci /* power on phy - start serdes and phy's power and clocks */ 3238c2ecf20Sopenharmony_ci ret = phy_power_on(phy); 3248c2ecf20Sopenharmony_ci if (ret) { 3258c2ecf20Sopenharmony_ci dev_err(hba->dev, "%s: phy power on failed, ret = %d\n", 3268c2ecf20Sopenharmony_ci __func__, ret); 3278c2ecf20Sopenharmony_ci goto out_disable_phy; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci ufs_qcom_select_unipro_mode(host); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci return 0; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ciout_disable_phy: 3358c2ecf20Sopenharmony_ci phy_exit(phy); 3368c2ecf20Sopenharmony_ciout: 3378c2ecf20Sopenharmony_ci return ret; 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci/* 3418c2ecf20Sopenharmony_ci * The UTP controller has a number of internal clock gating cells (CGCs). 3428c2ecf20Sopenharmony_ci * Internal hardware sub-modules within the UTP controller control the CGCs. 3438c2ecf20Sopenharmony_ci * Hardware CGCs disable the clock to inactivate UTP sub-modules not involved 3448c2ecf20Sopenharmony_ci * in a specific operation, UTP controller CGCs are by default disabled and 3458c2ecf20Sopenharmony_ci * this function enables them (after every UFS link startup) to save some power 3468c2ecf20Sopenharmony_ci * leakage. 3478c2ecf20Sopenharmony_ci */ 3488c2ecf20Sopenharmony_cistatic void ufs_qcom_enable_hw_clk_gating(struct ufs_hba *hba) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci ufshcd_writel(hba, 3518c2ecf20Sopenharmony_ci ufshcd_readl(hba, REG_UFS_CFG2) | REG_UFS_CFG2_CGC_EN_ALL, 3528c2ecf20Sopenharmony_ci REG_UFS_CFG2); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci /* Ensure that HW clock gating is enabled before next operations */ 3558c2ecf20Sopenharmony_ci mb(); 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cistatic int ufs_qcom_hce_enable_notify(struct ufs_hba *hba, 3598c2ecf20Sopenharmony_ci enum ufs_notify_change_status status) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci struct ufs_qcom_host *host = ufshcd_get_variant(hba); 3628c2ecf20Sopenharmony_ci int err = 0; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci switch (status) { 3658c2ecf20Sopenharmony_ci case PRE_CHANGE: 3668c2ecf20Sopenharmony_ci ufs_qcom_power_up_sequence(hba); 3678c2ecf20Sopenharmony_ci /* 3688c2ecf20Sopenharmony_ci * The PHY PLL output is the source of tx/rx lane symbol 3698c2ecf20Sopenharmony_ci * clocks, hence, enable the lane clocks only after PHY 3708c2ecf20Sopenharmony_ci * is initialized. 3718c2ecf20Sopenharmony_ci */ 3728c2ecf20Sopenharmony_ci err = ufs_qcom_enable_lane_clks(host); 3738c2ecf20Sopenharmony_ci break; 3748c2ecf20Sopenharmony_ci case POST_CHANGE: 3758c2ecf20Sopenharmony_ci /* check if UFS PHY moved from DISABLED to HIBERN8 */ 3768c2ecf20Sopenharmony_ci err = ufs_qcom_check_hibern8(hba); 3778c2ecf20Sopenharmony_ci ufs_qcom_enable_hw_clk_gating(hba); 3788c2ecf20Sopenharmony_ci ufs_qcom_ice_enable(host); 3798c2ecf20Sopenharmony_ci break; 3808c2ecf20Sopenharmony_ci default: 3818c2ecf20Sopenharmony_ci dev_err(hba->dev, "%s: invalid status %d\n", __func__, status); 3828c2ecf20Sopenharmony_ci err = -EINVAL; 3838c2ecf20Sopenharmony_ci break; 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci return err; 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci/* 3898c2ecf20Sopenharmony_ci * Returns zero for success and non-zero in case of a failure 3908c2ecf20Sopenharmony_ci */ 3918c2ecf20Sopenharmony_cistatic int ufs_qcom_cfg_timers(struct ufs_hba *hba, u32 gear, 3928c2ecf20Sopenharmony_ci u32 hs, u32 rate, bool update_link_startup_timer) 3938c2ecf20Sopenharmony_ci{ 3948c2ecf20Sopenharmony_ci int ret = 0; 3958c2ecf20Sopenharmony_ci struct ufs_qcom_host *host = ufshcd_get_variant(hba); 3968c2ecf20Sopenharmony_ci struct ufs_clk_info *clki; 3978c2ecf20Sopenharmony_ci u32 core_clk_period_in_ns; 3988c2ecf20Sopenharmony_ci u32 tx_clk_cycles_per_us = 0; 3998c2ecf20Sopenharmony_ci unsigned long core_clk_rate = 0; 4008c2ecf20Sopenharmony_ci u32 core_clk_cycles_per_us = 0; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci static u32 pwm_fr_table[][2] = { 4038c2ecf20Sopenharmony_ci {UFS_PWM_G1, 0x1}, 4048c2ecf20Sopenharmony_ci {UFS_PWM_G2, 0x1}, 4058c2ecf20Sopenharmony_ci {UFS_PWM_G3, 0x1}, 4068c2ecf20Sopenharmony_ci {UFS_PWM_G4, 0x1}, 4078c2ecf20Sopenharmony_ci }; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci static u32 hs_fr_table_rA[][2] = { 4108c2ecf20Sopenharmony_ci {UFS_HS_G1, 0x1F}, 4118c2ecf20Sopenharmony_ci {UFS_HS_G2, 0x3e}, 4128c2ecf20Sopenharmony_ci {UFS_HS_G3, 0x7D}, 4138c2ecf20Sopenharmony_ci }; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci static u32 hs_fr_table_rB[][2] = { 4168c2ecf20Sopenharmony_ci {UFS_HS_G1, 0x24}, 4178c2ecf20Sopenharmony_ci {UFS_HS_G2, 0x49}, 4188c2ecf20Sopenharmony_ci {UFS_HS_G3, 0x92}, 4198c2ecf20Sopenharmony_ci }; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci /* 4228c2ecf20Sopenharmony_ci * The Qunipro controller does not use following registers: 4238c2ecf20Sopenharmony_ci * SYS1CLK_1US_REG, TX_SYMBOL_CLK_1US_REG, CLK_NS_REG & 4248c2ecf20Sopenharmony_ci * UFS_REG_PA_LINK_STARTUP_TIMER 4258c2ecf20Sopenharmony_ci * But UTP controller uses SYS1CLK_1US_REG register for Interrupt 4268c2ecf20Sopenharmony_ci * Aggregation logic. 4278c2ecf20Sopenharmony_ci */ 4288c2ecf20Sopenharmony_ci if (ufs_qcom_cap_qunipro(host) && !ufshcd_is_intr_aggr_allowed(hba)) 4298c2ecf20Sopenharmony_ci goto out; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if (gear == 0) { 4328c2ecf20Sopenharmony_ci dev_err(hba->dev, "%s: invalid gear = %d\n", __func__, gear); 4338c2ecf20Sopenharmony_ci goto out_error; 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci list_for_each_entry(clki, &hba->clk_list_head, list) { 4378c2ecf20Sopenharmony_ci if (!strcmp(clki->name, "core_clk")) 4388c2ecf20Sopenharmony_ci core_clk_rate = clk_get_rate(clki->clk); 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci /* If frequency is smaller than 1MHz, set to 1MHz */ 4428c2ecf20Sopenharmony_ci if (core_clk_rate < DEFAULT_CLK_RATE_HZ) 4438c2ecf20Sopenharmony_ci core_clk_rate = DEFAULT_CLK_RATE_HZ; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci core_clk_cycles_per_us = core_clk_rate / USEC_PER_SEC; 4468c2ecf20Sopenharmony_ci if (ufshcd_readl(hba, REG_UFS_SYS1CLK_1US) != core_clk_cycles_per_us) { 4478c2ecf20Sopenharmony_ci ufshcd_writel(hba, core_clk_cycles_per_us, REG_UFS_SYS1CLK_1US); 4488c2ecf20Sopenharmony_ci /* 4498c2ecf20Sopenharmony_ci * make sure above write gets applied before we return from 4508c2ecf20Sopenharmony_ci * this function. 4518c2ecf20Sopenharmony_ci */ 4528c2ecf20Sopenharmony_ci mb(); 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci if (ufs_qcom_cap_qunipro(host)) 4568c2ecf20Sopenharmony_ci goto out; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci core_clk_period_in_ns = NSEC_PER_SEC / core_clk_rate; 4598c2ecf20Sopenharmony_ci core_clk_period_in_ns <<= OFFSET_CLK_NS_REG; 4608c2ecf20Sopenharmony_ci core_clk_period_in_ns &= MASK_CLK_NS_REG; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci switch (hs) { 4638c2ecf20Sopenharmony_ci case FASTAUTO_MODE: 4648c2ecf20Sopenharmony_ci case FAST_MODE: 4658c2ecf20Sopenharmony_ci if (rate == PA_HS_MODE_A) { 4668c2ecf20Sopenharmony_ci if (gear > ARRAY_SIZE(hs_fr_table_rA)) { 4678c2ecf20Sopenharmony_ci dev_err(hba->dev, 4688c2ecf20Sopenharmony_ci "%s: index %d exceeds table size %zu\n", 4698c2ecf20Sopenharmony_ci __func__, gear, 4708c2ecf20Sopenharmony_ci ARRAY_SIZE(hs_fr_table_rA)); 4718c2ecf20Sopenharmony_ci goto out_error; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci tx_clk_cycles_per_us = hs_fr_table_rA[gear-1][1]; 4748c2ecf20Sopenharmony_ci } else if (rate == PA_HS_MODE_B) { 4758c2ecf20Sopenharmony_ci if (gear > ARRAY_SIZE(hs_fr_table_rB)) { 4768c2ecf20Sopenharmony_ci dev_err(hba->dev, 4778c2ecf20Sopenharmony_ci "%s: index %d exceeds table size %zu\n", 4788c2ecf20Sopenharmony_ci __func__, gear, 4798c2ecf20Sopenharmony_ci ARRAY_SIZE(hs_fr_table_rB)); 4808c2ecf20Sopenharmony_ci goto out_error; 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci tx_clk_cycles_per_us = hs_fr_table_rB[gear-1][1]; 4838c2ecf20Sopenharmony_ci } else { 4848c2ecf20Sopenharmony_ci dev_err(hba->dev, "%s: invalid rate = %d\n", 4858c2ecf20Sopenharmony_ci __func__, rate); 4868c2ecf20Sopenharmony_ci goto out_error; 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci break; 4898c2ecf20Sopenharmony_ci case SLOWAUTO_MODE: 4908c2ecf20Sopenharmony_ci case SLOW_MODE: 4918c2ecf20Sopenharmony_ci if (gear > ARRAY_SIZE(pwm_fr_table)) { 4928c2ecf20Sopenharmony_ci dev_err(hba->dev, 4938c2ecf20Sopenharmony_ci "%s: index %d exceeds table size %zu\n", 4948c2ecf20Sopenharmony_ci __func__, gear, 4958c2ecf20Sopenharmony_ci ARRAY_SIZE(pwm_fr_table)); 4968c2ecf20Sopenharmony_ci goto out_error; 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci tx_clk_cycles_per_us = pwm_fr_table[gear-1][1]; 4998c2ecf20Sopenharmony_ci break; 5008c2ecf20Sopenharmony_ci case UNCHANGED: 5018c2ecf20Sopenharmony_ci default: 5028c2ecf20Sopenharmony_ci dev_err(hba->dev, "%s: invalid mode = %d\n", __func__, hs); 5038c2ecf20Sopenharmony_ci goto out_error; 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci if (ufshcd_readl(hba, REG_UFS_TX_SYMBOL_CLK_NS_US) != 5078c2ecf20Sopenharmony_ci (core_clk_period_in_ns | tx_clk_cycles_per_us)) { 5088c2ecf20Sopenharmony_ci /* this register 2 fields shall be written at once */ 5098c2ecf20Sopenharmony_ci ufshcd_writel(hba, core_clk_period_in_ns | tx_clk_cycles_per_us, 5108c2ecf20Sopenharmony_ci REG_UFS_TX_SYMBOL_CLK_NS_US); 5118c2ecf20Sopenharmony_ci /* 5128c2ecf20Sopenharmony_ci * make sure above write gets applied before we return from 5138c2ecf20Sopenharmony_ci * this function. 5148c2ecf20Sopenharmony_ci */ 5158c2ecf20Sopenharmony_ci mb(); 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci if (update_link_startup_timer) { 5198c2ecf20Sopenharmony_ci ufshcd_writel(hba, ((core_clk_rate / MSEC_PER_SEC) * 100), 5208c2ecf20Sopenharmony_ci REG_UFS_PA_LINK_STARTUP_TIMER); 5218c2ecf20Sopenharmony_ci /* 5228c2ecf20Sopenharmony_ci * make sure that this configuration is applied before 5238c2ecf20Sopenharmony_ci * we return 5248c2ecf20Sopenharmony_ci */ 5258c2ecf20Sopenharmony_ci mb(); 5268c2ecf20Sopenharmony_ci } 5278c2ecf20Sopenharmony_ci goto out; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ciout_error: 5308c2ecf20Sopenharmony_ci ret = -EINVAL; 5318c2ecf20Sopenharmony_ciout: 5328c2ecf20Sopenharmony_ci return ret; 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cistatic int ufs_qcom_link_startup_notify(struct ufs_hba *hba, 5368c2ecf20Sopenharmony_ci enum ufs_notify_change_status status) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci int err = 0; 5398c2ecf20Sopenharmony_ci struct ufs_qcom_host *host = ufshcd_get_variant(hba); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci switch (status) { 5428c2ecf20Sopenharmony_ci case PRE_CHANGE: 5438c2ecf20Sopenharmony_ci if (ufs_qcom_cfg_timers(hba, UFS_PWM_G1, SLOWAUTO_MODE, 5448c2ecf20Sopenharmony_ci 0, true)) { 5458c2ecf20Sopenharmony_ci dev_err(hba->dev, "%s: ufs_qcom_cfg_timers() failed\n", 5468c2ecf20Sopenharmony_ci __func__); 5478c2ecf20Sopenharmony_ci err = -EINVAL; 5488c2ecf20Sopenharmony_ci goto out; 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci if (ufs_qcom_cap_qunipro(host)) 5528c2ecf20Sopenharmony_ci /* 5538c2ecf20Sopenharmony_ci * set unipro core clock cycles to 150 & clear clock 5548c2ecf20Sopenharmony_ci * divider 5558c2ecf20Sopenharmony_ci */ 5568c2ecf20Sopenharmony_ci err = ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(hba, 5578c2ecf20Sopenharmony_ci 150); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci /* 5608c2ecf20Sopenharmony_ci * Some UFS devices (and may be host) have issues if LCC is 5618c2ecf20Sopenharmony_ci * enabled. So we are setting PA_Local_TX_LCC_Enable to 0 5628c2ecf20Sopenharmony_ci * before link startup which will make sure that both host 5638c2ecf20Sopenharmony_ci * and device TX LCC are disabled once link startup is 5648c2ecf20Sopenharmony_ci * completed. 5658c2ecf20Sopenharmony_ci */ 5668c2ecf20Sopenharmony_ci if (ufshcd_get_local_unipro_ver(hba) != UFS_UNIPRO_VER_1_41) 5678c2ecf20Sopenharmony_ci err = ufshcd_disable_host_tx_lcc(hba); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci break; 5708c2ecf20Sopenharmony_ci case POST_CHANGE: 5718c2ecf20Sopenharmony_ci ufs_qcom_link_startup_post_change(hba); 5728c2ecf20Sopenharmony_ci break; 5738c2ecf20Sopenharmony_ci default: 5748c2ecf20Sopenharmony_ci break; 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ciout: 5788c2ecf20Sopenharmony_ci return err; 5798c2ecf20Sopenharmony_ci} 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_cistatic int ufs_qcom_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) 5828c2ecf20Sopenharmony_ci{ 5838c2ecf20Sopenharmony_ci struct ufs_qcom_host *host = ufshcd_get_variant(hba); 5848c2ecf20Sopenharmony_ci struct phy *phy = host->generic_phy; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci if (ufs_qcom_is_link_off(hba)) { 5878c2ecf20Sopenharmony_ci /* 5888c2ecf20Sopenharmony_ci * Disable the tx/rx lane symbol clocks before PHY is 5898c2ecf20Sopenharmony_ci * powered down as the PLL source should be disabled 5908c2ecf20Sopenharmony_ci * after downstream clocks are disabled. 5918c2ecf20Sopenharmony_ci */ 5928c2ecf20Sopenharmony_ci ufs_qcom_disable_lane_clks(host); 5938c2ecf20Sopenharmony_ci phy_power_off(phy); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci } else if (!ufs_qcom_is_link_active(hba)) { 5968c2ecf20Sopenharmony_ci ufs_qcom_disable_lane_clks(host); 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci return 0; 6008c2ecf20Sopenharmony_ci} 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_cistatic int ufs_qcom_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) 6038c2ecf20Sopenharmony_ci{ 6048c2ecf20Sopenharmony_ci struct ufs_qcom_host *host = ufshcd_get_variant(hba); 6058c2ecf20Sopenharmony_ci struct phy *phy = host->generic_phy; 6068c2ecf20Sopenharmony_ci int err; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci if (ufs_qcom_is_link_off(hba)) { 6098c2ecf20Sopenharmony_ci err = phy_power_on(phy); 6108c2ecf20Sopenharmony_ci if (err) { 6118c2ecf20Sopenharmony_ci dev_err(hba->dev, "%s: failed PHY power on: %d\n", 6128c2ecf20Sopenharmony_ci __func__, err); 6138c2ecf20Sopenharmony_ci return err; 6148c2ecf20Sopenharmony_ci } 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci err = ufs_qcom_enable_lane_clks(host); 6178c2ecf20Sopenharmony_ci if (err) 6188c2ecf20Sopenharmony_ci return err; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci } else if (!ufs_qcom_is_link_active(hba)) { 6218c2ecf20Sopenharmony_ci err = ufs_qcom_enable_lane_clks(host); 6228c2ecf20Sopenharmony_ci if (err) 6238c2ecf20Sopenharmony_ci return err; 6248c2ecf20Sopenharmony_ci } 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci return ufs_qcom_ice_resume(host); 6278c2ecf20Sopenharmony_ci} 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_cistatic void ufs_qcom_dev_ref_clk_ctrl(struct ufs_qcom_host *host, bool enable) 6308c2ecf20Sopenharmony_ci{ 6318c2ecf20Sopenharmony_ci if (host->dev_ref_clk_ctrl_mmio && 6328c2ecf20Sopenharmony_ci (enable ^ host->is_dev_ref_clk_enabled)) { 6338c2ecf20Sopenharmony_ci u32 temp = readl_relaxed(host->dev_ref_clk_ctrl_mmio); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci if (enable) 6368c2ecf20Sopenharmony_ci temp |= host->dev_ref_clk_en_mask; 6378c2ecf20Sopenharmony_ci else 6388c2ecf20Sopenharmony_ci temp &= ~host->dev_ref_clk_en_mask; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci /* 6418c2ecf20Sopenharmony_ci * If we are here to disable this clock it might be immediately 6428c2ecf20Sopenharmony_ci * after entering into hibern8 in which case we need to make 6438c2ecf20Sopenharmony_ci * sure that device ref_clk is active for specific time after 6448c2ecf20Sopenharmony_ci * hibern8 enter. 6458c2ecf20Sopenharmony_ci */ 6468c2ecf20Sopenharmony_ci if (!enable) { 6478c2ecf20Sopenharmony_ci unsigned long gating_wait; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci gating_wait = host->hba->dev_info.clk_gating_wait_us; 6508c2ecf20Sopenharmony_ci if (!gating_wait) { 6518c2ecf20Sopenharmony_ci udelay(1); 6528c2ecf20Sopenharmony_ci } else { 6538c2ecf20Sopenharmony_ci /* 6548c2ecf20Sopenharmony_ci * bRefClkGatingWaitTime defines the minimum 6558c2ecf20Sopenharmony_ci * time for which the reference clock is 6568c2ecf20Sopenharmony_ci * required by device during transition from 6578c2ecf20Sopenharmony_ci * HS-MODE to LS-MODE or HIBERN8 state. Give it 6588c2ecf20Sopenharmony_ci * more delay to be on the safe side. 6598c2ecf20Sopenharmony_ci */ 6608c2ecf20Sopenharmony_ci gating_wait += 10; 6618c2ecf20Sopenharmony_ci usleep_range(gating_wait, gating_wait + 10); 6628c2ecf20Sopenharmony_ci } 6638c2ecf20Sopenharmony_ci } 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci writel_relaxed(temp, host->dev_ref_clk_ctrl_mmio); 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci /* 6688c2ecf20Sopenharmony_ci * Make sure the write to ref_clk reaches the destination and 6698c2ecf20Sopenharmony_ci * not stored in a Write Buffer (WB). 6708c2ecf20Sopenharmony_ci */ 6718c2ecf20Sopenharmony_ci readl(host->dev_ref_clk_ctrl_mmio); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci /* 6748c2ecf20Sopenharmony_ci * If we call hibern8 exit after this, we need to make sure that 6758c2ecf20Sopenharmony_ci * device ref_clk is stable for at least 1us before the hibern8 6768c2ecf20Sopenharmony_ci * exit command. 6778c2ecf20Sopenharmony_ci */ 6788c2ecf20Sopenharmony_ci if (enable) 6798c2ecf20Sopenharmony_ci udelay(1); 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci host->is_dev_ref_clk_enabled = enable; 6828c2ecf20Sopenharmony_ci } 6838c2ecf20Sopenharmony_ci} 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_cistatic int ufs_qcom_pwr_change_notify(struct ufs_hba *hba, 6868c2ecf20Sopenharmony_ci enum ufs_notify_change_status status, 6878c2ecf20Sopenharmony_ci struct ufs_pa_layer_attr *dev_max_params, 6888c2ecf20Sopenharmony_ci struct ufs_pa_layer_attr *dev_req_params) 6898c2ecf20Sopenharmony_ci{ 6908c2ecf20Sopenharmony_ci struct ufs_qcom_host *host = ufshcd_get_variant(hba); 6918c2ecf20Sopenharmony_ci struct ufs_dev_params ufs_qcom_cap; 6928c2ecf20Sopenharmony_ci int ret = 0; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci if (!dev_req_params) { 6958c2ecf20Sopenharmony_ci pr_err("%s: incoming dev_req_params is NULL\n", __func__); 6968c2ecf20Sopenharmony_ci ret = -EINVAL; 6978c2ecf20Sopenharmony_ci goto out; 6988c2ecf20Sopenharmony_ci } 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci switch (status) { 7018c2ecf20Sopenharmony_ci case PRE_CHANGE: 7028c2ecf20Sopenharmony_ci ufs_qcom_cap.tx_lanes = UFS_QCOM_LIMIT_NUM_LANES_TX; 7038c2ecf20Sopenharmony_ci ufs_qcom_cap.rx_lanes = UFS_QCOM_LIMIT_NUM_LANES_RX; 7048c2ecf20Sopenharmony_ci ufs_qcom_cap.hs_rx_gear = UFS_QCOM_LIMIT_HSGEAR_RX; 7058c2ecf20Sopenharmony_ci ufs_qcom_cap.hs_tx_gear = UFS_QCOM_LIMIT_HSGEAR_TX; 7068c2ecf20Sopenharmony_ci ufs_qcom_cap.pwm_rx_gear = UFS_QCOM_LIMIT_PWMGEAR_RX; 7078c2ecf20Sopenharmony_ci ufs_qcom_cap.pwm_tx_gear = UFS_QCOM_LIMIT_PWMGEAR_TX; 7088c2ecf20Sopenharmony_ci ufs_qcom_cap.rx_pwr_pwm = UFS_QCOM_LIMIT_RX_PWR_PWM; 7098c2ecf20Sopenharmony_ci ufs_qcom_cap.tx_pwr_pwm = UFS_QCOM_LIMIT_TX_PWR_PWM; 7108c2ecf20Sopenharmony_ci ufs_qcom_cap.rx_pwr_hs = UFS_QCOM_LIMIT_RX_PWR_HS; 7118c2ecf20Sopenharmony_ci ufs_qcom_cap.tx_pwr_hs = UFS_QCOM_LIMIT_TX_PWR_HS; 7128c2ecf20Sopenharmony_ci ufs_qcom_cap.hs_rate = UFS_QCOM_LIMIT_HS_RATE; 7138c2ecf20Sopenharmony_ci ufs_qcom_cap.desired_working_mode = 7148c2ecf20Sopenharmony_ci UFS_QCOM_LIMIT_DESIRED_MODE; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci if (host->hw_ver.major == 0x1) { 7178c2ecf20Sopenharmony_ci /* 7188c2ecf20Sopenharmony_ci * HS-G3 operations may not reliably work on legacy QCOM 7198c2ecf20Sopenharmony_ci * UFS host controller hardware even though capability 7208c2ecf20Sopenharmony_ci * exchange during link startup phase may end up 7218c2ecf20Sopenharmony_ci * negotiating maximum supported gear as G3. 7228c2ecf20Sopenharmony_ci * Hence downgrade the maximum supported gear to HS-G2. 7238c2ecf20Sopenharmony_ci */ 7248c2ecf20Sopenharmony_ci if (ufs_qcom_cap.hs_tx_gear > UFS_HS_G2) 7258c2ecf20Sopenharmony_ci ufs_qcom_cap.hs_tx_gear = UFS_HS_G2; 7268c2ecf20Sopenharmony_ci if (ufs_qcom_cap.hs_rx_gear > UFS_HS_G2) 7278c2ecf20Sopenharmony_ci ufs_qcom_cap.hs_rx_gear = UFS_HS_G2; 7288c2ecf20Sopenharmony_ci } 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci ret = ufshcd_get_pwr_dev_param(&ufs_qcom_cap, 7318c2ecf20Sopenharmony_ci dev_max_params, 7328c2ecf20Sopenharmony_ci dev_req_params); 7338c2ecf20Sopenharmony_ci if (ret) { 7348c2ecf20Sopenharmony_ci pr_err("%s: failed to determine capabilities\n", 7358c2ecf20Sopenharmony_ci __func__); 7368c2ecf20Sopenharmony_ci goto out; 7378c2ecf20Sopenharmony_ci } 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci /* enable the device ref clock before changing to HS mode */ 7408c2ecf20Sopenharmony_ci if (!ufshcd_is_hs_mode(&hba->pwr_info) && 7418c2ecf20Sopenharmony_ci ufshcd_is_hs_mode(dev_req_params)) 7428c2ecf20Sopenharmony_ci ufs_qcom_dev_ref_clk_ctrl(host, true); 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci if (host->hw_ver.major >= 0x4) { 7458c2ecf20Sopenharmony_ci if (dev_req_params->gear_tx == UFS_HS_G4) { 7468c2ecf20Sopenharmony_ci /* INITIAL ADAPT */ 7478c2ecf20Sopenharmony_ci ufshcd_dme_set(hba, 7488c2ecf20Sopenharmony_ci UIC_ARG_MIB(PA_TXHSADAPTTYPE), 7498c2ecf20Sopenharmony_ci PA_INITIAL_ADAPT); 7508c2ecf20Sopenharmony_ci } else { 7518c2ecf20Sopenharmony_ci /* NO ADAPT */ 7528c2ecf20Sopenharmony_ci ufshcd_dme_set(hba, 7538c2ecf20Sopenharmony_ci UIC_ARG_MIB(PA_TXHSADAPTTYPE), 7548c2ecf20Sopenharmony_ci PA_NO_ADAPT); 7558c2ecf20Sopenharmony_ci } 7568c2ecf20Sopenharmony_ci } 7578c2ecf20Sopenharmony_ci break; 7588c2ecf20Sopenharmony_ci case POST_CHANGE: 7598c2ecf20Sopenharmony_ci if (ufs_qcom_cfg_timers(hba, dev_req_params->gear_rx, 7608c2ecf20Sopenharmony_ci dev_req_params->pwr_rx, 7618c2ecf20Sopenharmony_ci dev_req_params->hs_rate, false)) { 7628c2ecf20Sopenharmony_ci dev_err(hba->dev, "%s: ufs_qcom_cfg_timers() failed\n", 7638c2ecf20Sopenharmony_ci __func__); 7648c2ecf20Sopenharmony_ci /* 7658c2ecf20Sopenharmony_ci * we return error code at the end of the routine, 7668c2ecf20Sopenharmony_ci * but continue to configure UFS_PHY_TX_LANE_ENABLE 7678c2ecf20Sopenharmony_ci * and bus voting as usual 7688c2ecf20Sopenharmony_ci */ 7698c2ecf20Sopenharmony_ci ret = -EINVAL; 7708c2ecf20Sopenharmony_ci } 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci /* cache the power mode parameters to use internally */ 7738c2ecf20Sopenharmony_ci memcpy(&host->dev_req_params, 7748c2ecf20Sopenharmony_ci dev_req_params, sizeof(*dev_req_params)); 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci /* disable the device ref clock if entered PWM mode */ 7778c2ecf20Sopenharmony_ci if (ufshcd_is_hs_mode(&hba->pwr_info) && 7788c2ecf20Sopenharmony_ci !ufshcd_is_hs_mode(dev_req_params)) 7798c2ecf20Sopenharmony_ci ufs_qcom_dev_ref_clk_ctrl(host, false); 7808c2ecf20Sopenharmony_ci break; 7818c2ecf20Sopenharmony_ci default: 7828c2ecf20Sopenharmony_ci ret = -EINVAL; 7838c2ecf20Sopenharmony_ci break; 7848c2ecf20Sopenharmony_ci } 7858c2ecf20Sopenharmony_ciout: 7868c2ecf20Sopenharmony_ci return ret; 7878c2ecf20Sopenharmony_ci} 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_cistatic int ufs_qcom_quirk_host_pa_saveconfigtime(struct ufs_hba *hba) 7908c2ecf20Sopenharmony_ci{ 7918c2ecf20Sopenharmony_ci int err; 7928c2ecf20Sopenharmony_ci u32 pa_vs_config_reg1; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci err = ufshcd_dme_get(hba, UIC_ARG_MIB(PA_VS_CONFIG_REG1), 7958c2ecf20Sopenharmony_ci &pa_vs_config_reg1); 7968c2ecf20Sopenharmony_ci if (err) 7978c2ecf20Sopenharmony_ci goto out; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci /* Allow extension of MSB bits of PA_SaveConfigTime attribute */ 8008c2ecf20Sopenharmony_ci err = ufshcd_dme_set(hba, UIC_ARG_MIB(PA_VS_CONFIG_REG1), 8018c2ecf20Sopenharmony_ci (pa_vs_config_reg1 | (1 << 12))); 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ciout: 8048c2ecf20Sopenharmony_ci return err; 8058c2ecf20Sopenharmony_ci} 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_cistatic int ufs_qcom_apply_dev_quirks(struct ufs_hba *hba) 8088c2ecf20Sopenharmony_ci{ 8098c2ecf20Sopenharmony_ci int err = 0; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci if (hba->dev_quirks & UFS_DEVICE_QUIRK_HOST_PA_SAVECONFIGTIME) 8128c2ecf20Sopenharmony_ci err = ufs_qcom_quirk_host_pa_saveconfigtime(hba); 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci if (hba->dev_info.wmanufacturerid == UFS_VENDOR_WDC) 8158c2ecf20Sopenharmony_ci hba->dev_quirks |= UFS_DEVICE_QUIRK_HOST_PA_TACTIVATE; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci return err; 8188c2ecf20Sopenharmony_ci} 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_cistatic u32 ufs_qcom_get_ufs_hci_version(struct ufs_hba *hba) 8218c2ecf20Sopenharmony_ci{ 8228c2ecf20Sopenharmony_ci struct ufs_qcom_host *host = ufshcd_get_variant(hba); 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci if (host->hw_ver.major == 0x1) 8258c2ecf20Sopenharmony_ci return UFSHCI_VERSION_11; 8268c2ecf20Sopenharmony_ci else 8278c2ecf20Sopenharmony_ci return UFSHCI_VERSION_20; 8288c2ecf20Sopenharmony_ci} 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci/** 8318c2ecf20Sopenharmony_ci * ufs_qcom_advertise_quirks - advertise the known QCOM UFS controller quirks 8328c2ecf20Sopenharmony_ci * @hba: host controller instance 8338c2ecf20Sopenharmony_ci * 8348c2ecf20Sopenharmony_ci * QCOM UFS host controller might have some non standard behaviours (quirks) 8358c2ecf20Sopenharmony_ci * than what is specified by UFSHCI specification. Advertise all such 8368c2ecf20Sopenharmony_ci * quirks to standard UFS host controller driver so standard takes them into 8378c2ecf20Sopenharmony_ci * account. 8388c2ecf20Sopenharmony_ci */ 8398c2ecf20Sopenharmony_cistatic void ufs_qcom_advertise_quirks(struct ufs_hba *hba) 8408c2ecf20Sopenharmony_ci{ 8418c2ecf20Sopenharmony_ci struct ufs_qcom_host *host = ufshcd_get_variant(hba); 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci if (host->hw_ver.major == 0x01) { 8448c2ecf20Sopenharmony_ci hba->quirks |= UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS 8458c2ecf20Sopenharmony_ci | UFSHCD_QUIRK_BROKEN_PA_RXHSUNTERMCAP 8468c2ecf20Sopenharmony_ci | UFSHCD_QUIRK_DME_PEER_ACCESS_AUTO_MODE; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci if (host->hw_ver.minor == 0x0001 && host->hw_ver.step == 0x0001) 8498c2ecf20Sopenharmony_ci hba->quirks |= UFSHCD_QUIRK_BROKEN_INTR_AGGR; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci hba->quirks |= UFSHCD_QUIRK_BROKEN_LCC; 8528c2ecf20Sopenharmony_ci } 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci if (host->hw_ver.major == 0x2) { 8558c2ecf20Sopenharmony_ci hba->quirks |= UFSHCD_QUIRK_BROKEN_UFS_HCI_VERSION; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci if (!ufs_qcom_cap_qunipro(host)) 8588c2ecf20Sopenharmony_ci /* Legacy UniPro mode still need following quirks */ 8598c2ecf20Sopenharmony_ci hba->quirks |= (UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS 8608c2ecf20Sopenharmony_ci | UFSHCD_QUIRK_DME_PEER_ACCESS_AUTO_MODE 8618c2ecf20Sopenharmony_ci | UFSHCD_QUIRK_BROKEN_PA_RXHSUNTERMCAP); 8628c2ecf20Sopenharmony_ci } 8638c2ecf20Sopenharmony_ci} 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_cistatic void ufs_qcom_set_caps(struct ufs_hba *hba) 8668c2ecf20Sopenharmony_ci{ 8678c2ecf20Sopenharmony_ci struct ufs_qcom_host *host = ufshcd_get_variant(hba); 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci hba->caps |= UFSHCD_CAP_CLK_GATING | UFSHCD_CAP_HIBERN8_WITH_CLK_GATING; 8708c2ecf20Sopenharmony_ci hba->caps |= UFSHCD_CAP_CLK_SCALING; 8718c2ecf20Sopenharmony_ci hba->caps |= UFSHCD_CAP_AUTO_BKOPS_SUSPEND; 8728c2ecf20Sopenharmony_ci hba->caps |= UFSHCD_CAP_WB_EN; 8738c2ecf20Sopenharmony_ci hba->caps |= UFSHCD_CAP_CRYPTO; 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci if (host->hw_ver.major >= 0x2) { 8768c2ecf20Sopenharmony_ci host->caps = UFS_QCOM_CAP_QUNIPRO | 8778c2ecf20Sopenharmony_ci UFS_QCOM_CAP_RETAIN_SEC_CFG_AFTER_PWR_COLLAPSE; 8788c2ecf20Sopenharmony_ci } 8798c2ecf20Sopenharmony_ci} 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci/** 8828c2ecf20Sopenharmony_ci * ufs_qcom_setup_clocks - enables/disable clocks 8838c2ecf20Sopenharmony_ci * @hba: host controller instance 8848c2ecf20Sopenharmony_ci * @on: If true, enable clocks else disable them. 8858c2ecf20Sopenharmony_ci * @status: PRE_CHANGE or POST_CHANGE notify 8868c2ecf20Sopenharmony_ci * 8878c2ecf20Sopenharmony_ci * Returns 0 on success, non-zero on failure. 8888c2ecf20Sopenharmony_ci */ 8898c2ecf20Sopenharmony_cistatic int ufs_qcom_setup_clocks(struct ufs_hba *hba, bool on, 8908c2ecf20Sopenharmony_ci enum ufs_notify_change_status status) 8918c2ecf20Sopenharmony_ci{ 8928c2ecf20Sopenharmony_ci struct ufs_qcom_host *host = ufshcd_get_variant(hba); 8938c2ecf20Sopenharmony_ci int err = 0; 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci /* 8968c2ecf20Sopenharmony_ci * In case ufs_qcom_init() is not yet done, simply ignore. 8978c2ecf20Sopenharmony_ci * This ufs_qcom_setup_clocks() shall be called from 8988c2ecf20Sopenharmony_ci * ufs_qcom_init() after init is done. 8998c2ecf20Sopenharmony_ci */ 9008c2ecf20Sopenharmony_ci if (!host) 9018c2ecf20Sopenharmony_ci return 0; 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci switch (status) { 9048c2ecf20Sopenharmony_ci case PRE_CHANGE: 9058c2ecf20Sopenharmony_ci if (!on) { 9068c2ecf20Sopenharmony_ci if (!ufs_qcom_is_link_active(hba)) { 9078c2ecf20Sopenharmony_ci /* disable device ref_clk */ 9088c2ecf20Sopenharmony_ci ufs_qcom_dev_ref_clk_ctrl(host, false); 9098c2ecf20Sopenharmony_ci } 9108c2ecf20Sopenharmony_ci } 9118c2ecf20Sopenharmony_ci break; 9128c2ecf20Sopenharmony_ci case POST_CHANGE: 9138c2ecf20Sopenharmony_ci if (on) { 9148c2ecf20Sopenharmony_ci /* enable the device ref clock for HS mode*/ 9158c2ecf20Sopenharmony_ci if (ufshcd_is_hs_mode(&hba->pwr_info)) 9168c2ecf20Sopenharmony_ci ufs_qcom_dev_ref_clk_ctrl(host, true); 9178c2ecf20Sopenharmony_ci } 9188c2ecf20Sopenharmony_ci break; 9198c2ecf20Sopenharmony_ci } 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci return err; 9228c2ecf20Sopenharmony_ci} 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_cistatic int 9258c2ecf20Sopenharmony_ciufs_qcom_reset_assert(struct reset_controller_dev *rcdev, unsigned long id) 9268c2ecf20Sopenharmony_ci{ 9278c2ecf20Sopenharmony_ci struct ufs_qcom_host *host = rcdev_to_ufs_host(rcdev); 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci /* Currently this code only knows about a single reset. */ 9308c2ecf20Sopenharmony_ci WARN_ON(id); 9318c2ecf20Sopenharmony_ci ufs_qcom_assert_reset(host->hba); 9328c2ecf20Sopenharmony_ci /* provide 1ms delay to let the reset pulse propagate. */ 9338c2ecf20Sopenharmony_ci usleep_range(1000, 1100); 9348c2ecf20Sopenharmony_ci return 0; 9358c2ecf20Sopenharmony_ci} 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_cistatic int 9388c2ecf20Sopenharmony_ciufs_qcom_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id) 9398c2ecf20Sopenharmony_ci{ 9408c2ecf20Sopenharmony_ci struct ufs_qcom_host *host = rcdev_to_ufs_host(rcdev); 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci /* Currently this code only knows about a single reset. */ 9438c2ecf20Sopenharmony_ci WARN_ON(id); 9448c2ecf20Sopenharmony_ci ufs_qcom_deassert_reset(host->hba); 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci /* 9478c2ecf20Sopenharmony_ci * after reset deassertion, phy will need all ref clocks, 9488c2ecf20Sopenharmony_ci * voltage, current to settle down before starting serdes. 9498c2ecf20Sopenharmony_ci */ 9508c2ecf20Sopenharmony_ci usleep_range(1000, 1100); 9518c2ecf20Sopenharmony_ci return 0; 9528c2ecf20Sopenharmony_ci} 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_cistatic const struct reset_control_ops ufs_qcom_reset_ops = { 9558c2ecf20Sopenharmony_ci .assert = ufs_qcom_reset_assert, 9568c2ecf20Sopenharmony_ci .deassert = ufs_qcom_reset_deassert, 9578c2ecf20Sopenharmony_ci}; 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci#define ANDROID_BOOT_DEV_MAX 30 9608c2ecf20Sopenharmony_cistatic char android_boot_dev[ANDROID_BOOT_DEV_MAX]; 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci#ifndef MODULE 9638c2ecf20Sopenharmony_cistatic int __init get_android_boot_dev(char *str) 9648c2ecf20Sopenharmony_ci{ 9658c2ecf20Sopenharmony_ci strlcpy(android_boot_dev, str, ANDROID_BOOT_DEV_MAX); 9668c2ecf20Sopenharmony_ci return 1; 9678c2ecf20Sopenharmony_ci} 9688c2ecf20Sopenharmony_ci__setup("androidboot.bootdevice=", get_android_boot_dev); 9698c2ecf20Sopenharmony_ci#endif 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci/** 9728c2ecf20Sopenharmony_ci * ufs_qcom_init - bind phy with controller 9738c2ecf20Sopenharmony_ci * @hba: host controller instance 9748c2ecf20Sopenharmony_ci * 9758c2ecf20Sopenharmony_ci * Binds PHY with controller and powers up PHY enabling clocks 9768c2ecf20Sopenharmony_ci * and regulators. 9778c2ecf20Sopenharmony_ci * 9788c2ecf20Sopenharmony_ci * Returns -EPROBE_DEFER if binding fails, returns negative error 9798c2ecf20Sopenharmony_ci * on phy power up failure and returns zero on success. 9808c2ecf20Sopenharmony_ci */ 9818c2ecf20Sopenharmony_cistatic int ufs_qcom_init(struct ufs_hba *hba) 9828c2ecf20Sopenharmony_ci{ 9838c2ecf20Sopenharmony_ci int err; 9848c2ecf20Sopenharmony_ci struct device *dev = hba->dev; 9858c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 9868c2ecf20Sopenharmony_ci struct ufs_qcom_host *host; 9878c2ecf20Sopenharmony_ci struct resource *res; 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci if (strlen(android_boot_dev) && strcmp(android_boot_dev, dev_name(dev))) 9908c2ecf20Sopenharmony_ci return -ENODEV; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); 9938c2ecf20Sopenharmony_ci if (!host) { 9948c2ecf20Sopenharmony_ci err = -ENOMEM; 9958c2ecf20Sopenharmony_ci dev_err(dev, "%s: no memory for qcom ufs host\n", __func__); 9968c2ecf20Sopenharmony_ci goto out; 9978c2ecf20Sopenharmony_ci } 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci /* Make a two way bind between the qcom host and the hba */ 10008c2ecf20Sopenharmony_ci host->hba = hba; 10018c2ecf20Sopenharmony_ci ufshcd_set_variant(hba, host); 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci /* Setup the reset control of HCI */ 10048c2ecf20Sopenharmony_ci host->core_reset = devm_reset_control_get(hba->dev, "rst"); 10058c2ecf20Sopenharmony_ci if (IS_ERR(host->core_reset)) { 10068c2ecf20Sopenharmony_ci err = PTR_ERR(host->core_reset); 10078c2ecf20Sopenharmony_ci dev_warn(dev, "Failed to get reset control %d\n", err); 10088c2ecf20Sopenharmony_ci host->core_reset = NULL; 10098c2ecf20Sopenharmony_ci err = 0; 10108c2ecf20Sopenharmony_ci } 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci /* Fire up the reset controller. Failure here is non-fatal. */ 10138c2ecf20Sopenharmony_ci host->rcdev.of_node = dev->of_node; 10148c2ecf20Sopenharmony_ci host->rcdev.ops = &ufs_qcom_reset_ops; 10158c2ecf20Sopenharmony_ci host->rcdev.owner = dev->driver->owner; 10168c2ecf20Sopenharmony_ci host->rcdev.nr_resets = 1; 10178c2ecf20Sopenharmony_ci err = devm_reset_controller_register(dev, &host->rcdev); 10188c2ecf20Sopenharmony_ci if (err) { 10198c2ecf20Sopenharmony_ci dev_warn(dev, "Failed to register reset controller\n"); 10208c2ecf20Sopenharmony_ci err = 0; 10218c2ecf20Sopenharmony_ci } 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci /* 10248c2ecf20Sopenharmony_ci * voting/devoting device ref_clk source is time consuming hence 10258c2ecf20Sopenharmony_ci * skip devoting it during aggressive clock gating. This clock 10268c2ecf20Sopenharmony_ci * will still be gated off during runtime suspend. 10278c2ecf20Sopenharmony_ci */ 10288c2ecf20Sopenharmony_ci host->generic_phy = devm_phy_get(dev, "ufsphy"); 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci if (host->generic_phy == ERR_PTR(-EPROBE_DEFER)) { 10318c2ecf20Sopenharmony_ci /* 10328c2ecf20Sopenharmony_ci * UFS driver might be probed before the phy driver does. 10338c2ecf20Sopenharmony_ci * In that case we would like to return EPROBE_DEFER code. 10348c2ecf20Sopenharmony_ci */ 10358c2ecf20Sopenharmony_ci err = -EPROBE_DEFER; 10368c2ecf20Sopenharmony_ci dev_warn(dev, "%s: required phy device. hasn't probed yet. err = %d\n", 10378c2ecf20Sopenharmony_ci __func__, err); 10388c2ecf20Sopenharmony_ci goto out_variant_clear; 10398c2ecf20Sopenharmony_ci } else if (IS_ERR(host->generic_phy)) { 10408c2ecf20Sopenharmony_ci if (has_acpi_companion(dev)) { 10418c2ecf20Sopenharmony_ci host->generic_phy = NULL; 10428c2ecf20Sopenharmony_ci } else { 10438c2ecf20Sopenharmony_ci err = PTR_ERR(host->generic_phy); 10448c2ecf20Sopenharmony_ci dev_err(dev, "%s: PHY get failed %d\n", __func__, err); 10458c2ecf20Sopenharmony_ci goto out_variant_clear; 10468c2ecf20Sopenharmony_ci } 10478c2ecf20Sopenharmony_ci } 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci host->device_reset = devm_gpiod_get_optional(dev, "reset", 10508c2ecf20Sopenharmony_ci GPIOD_OUT_HIGH); 10518c2ecf20Sopenharmony_ci if (IS_ERR(host->device_reset)) { 10528c2ecf20Sopenharmony_ci err = PTR_ERR(host->device_reset); 10538c2ecf20Sopenharmony_ci if (err != -EPROBE_DEFER) 10548c2ecf20Sopenharmony_ci dev_err(dev, "failed to acquire reset gpio: %d\n", err); 10558c2ecf20Sopenharmony_ci goto out_variant_clear; 10568c2ecf20Sopenharmony_ci } 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci ufs_qcom_get_controller_revision(hba, &host->hw_ver.major, 10598c2ecf20Sopenharmony_ci &host->hw_ver.minor, &host->hw_ver.step); 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci /* 10628c2ecf20Sopenharmony_ci * for newer controllers, device reference clock control bit has 10638c2ecf20Sopenharmony_ci * moved inside UFS controller register address space itself. 10648c2ecf20Sopenharmony_ci */ 10658c2ecf20Sopenharmony_ci if (host->hw_ver.major >= 0x02) { 10668c2ecf20Sopenharmony_ci host->dev_ref_clk_ctrl_mmio = hba->mmio_base + REG_UFS_CFG1; 10678c2ecf20Sopenharmony_ci host->dev_ref_clk_en_mask = BIT(26); 10688c2ecf20Sopenharmony_ci } else { 10698c2ecf20Sopenharmony_ci /* "dev_ref_clk_ctrl_mem" is optional resource */ 10708c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 10718c2ecf20Sopenharmony_ci "dev_ref_clk_ctrl_mem"); 10728c2ecf20Sopenharmony_ci if (res) { 10738c2ecf20Sopenharmony_ci host->dev_ref_clk_ctrl_mmio = 10748c2ecf20Sopenharmony_ci devm_ioremap_resource(dev, res); 10758c2ecf20Sopenharmony_ci if (IS_ERR(host->dev_ref_clk_ctrl_mmio)) { 10768c2ecf20Sopenharmony_ci dev_warn(dev, 10778c2ecf20Sopenharmony_ci "%s: could not map dev_ref_clk_ctrl_mmio, err %ld\n", 10788c2ecf20Sopenharmony_ci __func__, 10798c2ecf20Sopenharmony_ci PTR_ERR(host->dev_ref_clk_ctrl_mmio)); 10808c2ecf20Sopenharmony_ci host->dev_ref_clk_ctrl_mmio = NULL; 10818c2ecf20Sopenharmony_ci } 10828c2ecf20Sopenharmony_ci host->dev_ref_clk_en_mask = BIT(5); 10838c2ecf20Sopenharmony_ci } 10848c2ecf20Sopenharmony_ci } 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci err = ufs_qcom_init_lane_clks(host); 10878c2ecf20Sopenharmony_ci if (err) 10888c2ecf20Sopenharmony_ci goto out_variant_clear; 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci ufs_qcom_set_caps(hba); 10918c2ecf20Sopenharmony_ci ufs_qcom_advertise_quirks(hba); 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci err = ufs_qcom_ice_init(host); 10948c2ecf20Sopenharmony_ci if (err) 10958c2ecf20Sopenharmony_ci goto out_variant_clear; 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci ufs_qcom_setup_clocks(hba, true, POST_CHANGE); 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci if (hba->dev->id < MAX_UFS_QCOM_HOSTS) 11008c2ecf20Sopenharmony_ci ufs_qcom_hosts[hba->dev->id] = host; 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci host->dbg_print_en |= UFS_QCOM_DEFAULT_DBG_PRINT_EN; 11038c2ecf20Sopenharmony_ci ufs_qcom_get_default_testbus_cfg(host); 11048c2ecf20Sopenharmony_ci err = ufs_qcom_testbus_config(host); 11058c2ecf20Sopenharmony_ci if (err) { 11068c2ecf20Sopenharmony_ci dev_warn(dev, "%s: failed to configure the testbus %d\n", 11078c2ecf20Sopenharmony_ci __func__, err); 11088c2ecf20Sopenharmony_ci err = 0; 11098c2ecf20Sopenharmony_ci } 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci goto out; 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ciout_variant_clear: 11148c2ecf20Sopenharmony_ci ufshcd_set_variant(hba, NULL); 11158c2ecf20Sopenharmony_ciout: 11168c2ecf20Sopenharmony_ci return err; 11178c2ecf20Sopenharmony_ci} 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_cistatic void ufs_qcom_exit(struct ufs_hba *hba) 11208c2ecf20Sopenharmony_ci{ 11218c2ecf20Sopenharmony_ci struct ufs_qcom_host *host = ufshcd_get_variant(hba); 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci ufs_qcom_disable_lane_clks(host); 11248c2ecf20Sopenharmony_ci phy_power_off(host->generic_phy); 11258c2ecf20Sopenharmony_ci phy_exit(host->generic_phy); 11268c2ecf20Sopenharmony_ci} 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_cistatic int ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(struct ufs_hba *hba, 11298c2ecf20Sopenharmony_ci u32 clk_cycles) 11308c2ecf20Sopenharmony_ci{ 11318c2ecf20Sopenharmony_ci int err; 11328c2ecf20Sopenharmony_ci u32 core_clk_ctrl_reg; 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci if (clk_cycles > DME_VS_CORE_CLK_CTRL_MAX_CORE_CLK_1US_CYCLES_MASK) 11358c2ecf20Sopenharmony_ci return -EINVAL; 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci err = ufshcd_dme_get(hba, 11388c2ecf20Sopenharmony_ci UIC_ARG_MIB(DME_VS_CORE_CLK_CTRL), 11398c2ecf20Sopenharmony_ci &core_clk_ctrl_reg); 11408c2ecf20Sopenharmony_ci if (err) 11418c2ecf20Sopenharmony_ci goto out; 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci core_clk_ctrl_reg &= ~DME_VS_CORE_CLK_CTRL_MAX_CORE_CLK_1US_CYCLES_MASK; 11448c2ecf20Sopenharmony_ci core_clk_ctrl_reg |= clk_cycles; 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci /* Clear CORE_CLK_DIV_EN */ 11478c2ecf20Sopenharmony_ci core_clk_ctrl_reg &= ~DME_VS_CORE_CLK_CTRL_CORE_CLK_DIV_EN_BIT; 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci err = ufshcd_dme_set(hba, 11508c2ecf20Sopenharmony_ci UIC_ARG_MIB(DME_VS_CORE_CLK_CTRL), 11518c2ecf20Sopenharmony_ci core_clk_ctrl_reg); 11528c2ecf20Sopenharmony_ciout: 11538c2ecf20Sopenharmony_ci return err; 11548c2ecf20Sopenharmony_ci} 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_cistatic int ufs_qcom_clk_scale_up_pre_change(struct ufs_hba *hba) 11578c2ecf20Sopenharmony_ci{ 11588c2ecf20Sopenharmony_ci /* nothing to do as of now */ 11598c2ecf20Sopenharmony_ci return 0; 11608c2ecf20Sopenharmony_ci} 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_cistatic int ufs_qcom_clk_scale_up_post_change(struct ufs_hba *hba) 11638c2ecf20Sopenharmony_ci{ 11648c2ecf20Sopenharmony_ci struct ufs_qcom_host *host = ufshcd_get_variant(hba); 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci if (!ufs_qcom_cap_qunipro(host)) 11678c2ecf20Sopenharmony_ci return 0; 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci /* set unipro core clock cycles to 150 and clear clock divider */ 11708c2ecf20Sopenharmony_ci return ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(hba, 150); 11718c2ecf20Sopenharmony_ci} 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_cistatic int ufs_qcom_clk_scale_down_pre_change(struct ufs_hba *hba) 11748c2ecf20Sopenharmony_ci{ 11758c2ecf20Sopenharmony_ci struct ufs_qcom_host *host = ufshcd_get_variant(hba); 11768c2ecf20Sopenharmony_ci int err; 11778c2ecf20Sopenharmony_ci u32 core_clk_ctrl_reg; 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci if (!ufs_qcom_cap_qunipro(host)) 11808c2ecf20Sopenharmony_ci return 0; 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci err = ufshcd_dme_get(hba, 11838c2ecf20Sopenharmony_ci UIC_ARG_MIB(DME_VS_CORE_CLK_CTRL), 11848c2ecf20Sopenharmony_ci &core_clk_ctrl_reg); 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci /* make sure CORE_CLK_DIV_EN is cleared */ 11878c2ecf20Sopenharmony_ci if (!err && 11888c2ecf20Sopenharmony_ci (core_clk_ctrl_reg & DME_VS_CORE_CLK_CTRL_CORE_CLK_DIV_EN_BIT)) { 11898c2ecf20Sopenharmony_ci core_clk_ctrl_reg &= ~DME_VS_CORE_CLK_CTRL_CORE_CLK_DIV_EN_BIT; 11908c2ecf20Sopenharmony_ci err = ufshcd_dme_set(hba, 11918c2ecf20Sopenharmony_ci UIC_ARG_MIB(DME_VS_CORE_CLK_CTRL), 11928c2ecf20Sopenharmony_ci core_clk_ctrl_reg); 11938c2ecf20Sopenharmony_ci } 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci return err; 11968c2ecf20Sopenharmony_ci} 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_cistatic int ufs_qcom_clk_scale_down_post_change(struct ufs_hba *hba) 11998c2ecf20Sopenharmony_ci{ 12008c2ecf20Sopenharmony_ci struct ufs_qcom_host *host = ufshcd_get_variant(hba); 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci if (!ufs_qcom_cap_qunipro(host)) 12038c2ecf20Sopenharmony_ci return 0; 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci /* set unipro core clock cycles to 75 and clear clock divider */ 12068c2ecf20Sopenharmony_ci return ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(hba, 75); 12078c2ecf20Sopenharmony_ci} 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_cistatic int ufs_qcom_clk_scale_notify(struct ufs_hba *hba, 12108c2ecf20Sopenharmony_ci bool scale_up, enum ufs_notify_change_status status) 12118c2ecf20Sopenharmony_ci{ 12128c2ecf20Sopenharmony_ci struct ufs_qcom_host *host = ufshcd_get_variant(hba); 12138c2ecf20Sopenharmony_ci struct ufs_pa_layer_attr *dev_req_params = &host->dev_req_params; 12148c2ecf20Sopenharmony_ci int err = 0; 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci if (status == PRE_CHANGE) { 12178c2ecf20Sopenharmony_ci if (scale_up) 12188c2ecf20Sopenharmony_ci err = ufs_qcom_clk_scale_up_pre_change(hba); 12198c2ecf20Sopenharmony_ci else 12208c2ecf20Sopenharmony_ci err = ufs_qcom_clk_scale_down_pre_change(hba); 12218c2ecf20Sopenharmony_ci } else { 12228c2ecf20Sopenharmony_ci if (scale_up) 12238c2ecf20Sopenharmony_ci err = ufs_qcom_clk_scale_up_post_change(hba); 12248c2ecf20Sopenharmony_ci else 12258c2ecf20Sopenharmony_ci err = ufs_qcom_clk_scale_down_post_change(hba); 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci if (err || !dev_req_params) 12288c2ecf20Sopenharmony_ci goto out; 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci ufs_qcom_cfg_timers(hba, 12318c2ecf20Sopenharmony_ci dev_req_params->gear_rx, 12328c2ecf20Sopenharmony_ci dev_req_params->pwr_rx, 12338c2ecf20Sopenharmony_ci dev_req_params->hs_rate, 12348c2ecf20Sopenharmony_ci false); 12358c2ecf20Sopenharmony_ci } 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ciout: 12388c2ecf20Sopenharmony_ci return err; 12398c2ecf20Sopenharmony_ci} 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_cistatic void ufs_qcom_print_hw_debug_reg_all(struct ufs_hba *hba, 12428c2ecf20Sopenharmony_ci void *priv, void (*print_fn)(struct ufs_hba *hba, 12438c2ecf20Sopenharmony_ci int offset, int num_regs, const char *str, void *priv)) 12448c2ecf20Sopenharmony_ci{ 12458c2ecf20Sopenharmony_ci u32 reg; 12468c2ecf20Sopenharmony_ci struct ufs_qcom_host *host; 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci if (unlikely(!hba)) { 12498c2ecf20Sopenharmony_ci pr_err("%s: hba is NULL\n", __func__); 12508c2ecf20Sopenharmony_ci return; 12518c2ecf20Sopenharmony_ci } 12528c2ecf20Sopenharmony_ci if (unlikely(!print_fn)) { 12538c2ecf20Sopenharmony_ci dev_err(hba->dev, "%s: print_fn is NULL\n", __func__); 12548c2ecf20Sopenharmony_ci return; 12558c2ecf20Sopenharmony_ci } 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci host = ufshcd_get_variant(hba); 12588c2ecf20Sopenharmony_ci if (!(host->dbg_print_en & UFS_QCOM_DBG_PRINT_REGS_EN)) 12598c2ecf20Sopenharmony_ci return; 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci reg = ufs_qcom_get_debug_reg_offset(host, UFS_UFS_DBG_RD_REG_OCSC); 12628c2ecf20Sopenharmony_ci print_fn(hba, reg, 44, "UFS_UFS_DBG_RD_REG_OCSC ", priv); 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci reg = ufshcd_readl(hba, REG_UFS_CFG1); 12658c2ecf20Sopenharmony_ci reg |= UTP_DBG_RAMS_EN; 12668c2ecf20Sopenharmony_ci ufshcd_writel(hba, reg, REG_UFS_CFG1); 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci reg = ufs_qcom_get_debug_reg_offset(host, UFS_UFS_DBG_RD_EDTL_RAM); 12698c2ecf20Sopenharmony_ci print_fn(hba, reg, 32, "UFS_UFS_DBG_RD_EDTL_RAM ", priv); 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci reg = ufs_qcom_get_debug_reg_offset(host, UFS_UFS_DBG_RD_DESC_RAM); 12728c2ecf20Sopenharmony_ci print_fn(hba, reg, 128, "UFS_UFS_DBG_RD_DESC_RAM ", priv); 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci reg = ufs_qcom_get_debug_reg_offset(host, UFS_UFS_DBG_RD_PRDT_RAM); 12758c2ecf20Sopenharmony_ci print_fn(hba, reg, 64, "UFS_UFS_DBG_RD_PRDT_RAM ", priv); 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci /* clear bit 17 - UTP_DBG_RAMS_EN */ 12788c2ecf20Sopenharmony_ci ufshcd_rmwl(hba, UTP_DBG_RAMS_EN, 0, REG_UFS_CFG1); 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_UAWM); 12818c2ecf20Sopenharmony_ci print_fn(hba, reg, 4, "UFS_DBG_RD_REG_UAWM ", priv); 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_UARM); 12848c2ecf20Sopenharmony_ci print_fn(hba, reg, 4, "UFS_DBG_RD_REG_UARM ", priv); 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_TXUC); 12878c2ecf20Sopenharmony_ci print_fn(hba, reg, 48, "UFS_DBG_RD_REG_TXUC ", priv); 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_RXUC); 12908c2ecf20Sopenharmony_ci print_fn(hba, reg, 27, "UFS_DBG_RD_REG_RXUC ", priv); 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_DFC); 12938c2ecf20Sopenharmony_ci print_fn(hba, reg, 19, "UFS_DBG_RD_REG_DFC ", priv); 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_TRLUT); 12968c2ecf20Sopenharmony_ci print_fn(hba, reg, 34, "UFS_DBG_RD_REG_TRLUT ", priv); 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci reg = ufs_qcom_get_debug_reg_offset(host, UFS_DBG_RD_REG_TMRLUT); 12998c2ecf20Sopenharmony_ci print_fn(hba, reg, 9, "UFS_DBG_RD_REG_TMRLUT ", priv); 13008c2ecf20Sopenharmony_ci} 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_cistatic void ufs_qcom_enable_test_bus(struct ufs_qcom_host *host) 13038c2ecf20Sopenharmony_ci{ 13048c2ecf20Sopenharmony_ci if (host->dbg_print_en & UFS_QCOM_DBG_PRINT_TEST_BUS_EN) { 13058c2ecf20Sopenharmony_ci ufshcd_rmwl(host->hba, UFS_REG_TEST_BUS_EN, 13068c2ecf20Sopenharmony_ci UFS_REG_TEST_BUS_EN, REG_UFS_CFG1); 13078c2ecf20Sopenharmony_ci ufshcd_rmwl(host->hba, TEST_BUS_EN, TEST_BUS_EN, REG_UFS_CFG1); 13088c2ecf20Sopenharmony_ci } else { 13098c2ecf20Sopenharmony_ci ufshcd_rmwl(host->hba, UFS_REG_TEST_BUS_EN, 0, REG_UFS_CFG1); 13108c2ecf20Sopenharmony_ci ufshcd_rmwl(host->hba, TEST_BUS_EN, 0, REG_UFS_CFG1); 13118c2ecf20Sopenharmony_ci } 13128c2ecf20Sopenharmony_ci} 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_cistatic void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host) 13158c2ecf20Sopenharmony_ci{ 13168c2ecf20Sopenharmony_ci /* provide a legal default configuration */ 13178c2ecf20Sopenharmony_ci host->testbus.select_major = TSTBUS_UNIPRO; 13188c2ecf20Sopenharmony_ci host->testbus.select_minor = 37; 13198c2ecf20Sopenharmony_ci} 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_cistatic bool ufs_qcom_testbus_cfg_is_ok(struct ufs_qcom_host *host) 13228c2ecf20Sopenharmony_ci{ 13238c2ecf20Sopenharmony_ci if (host->testbus.select_major >= TSTBUS_MAX) { 13248c2ecf20Sopenharmony_ci dev_err(host->hba->dev, 13258c2ecf20Sopenharmony_ci "%s: UFS_CFG1[TEST_BUS_SEL} may not equal 0x%05X\n", 13268c2ecf20Sopenharmony_ci __func__, host->testbus.select_major); 13278c2ecf20Sopenharmony_ci return false; 13288c2ecf20Sopenharmony_ci } 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci return true; 13318c2ecf20Sopenharmony_ci} 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ciint ufs_qcom_testbus_config(struct ufs_qcom_host *host) 13348c2ecf20Sopenharmony_ci{ 13358c2ecf20Sopenharmony_ci int reg; 13368c2ecf20Sopenharmony_ci int offset; 13378c2ecf20Sopenharmony_ci u32 mask = TEST_BUS_SUB_SEL_MASK; 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci if (!host) 13408c2ecf20Sopenharmony_ci return -EINVAL; 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci if (!ufs_qcom_testbus_cfg_is_ok(host)) 13438c2ecf20Sopenharmony_ci return -EPERM; 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci switch (host->testbus.select_major) { 13468c2ecf20Sopenharmony_ci case TSTBUS_UAWM: 13478c2ecf20Sopenharmony_ci reg = UFS_TEST_BUS_CTRL_0; 13488c2ecf20Sopenharmony_ci offset = 24; 13498c2ecf20Sopenharmony_ci break; 13508c2ecf20Sopenharmony_ci case TSTBUS_UARM: 13518c2ecf20Sopenharmony_ci reg = UFS_TEST_BUS_CTRL_0; 13528c2ecf20Sopenharmony_ci offset = 16; 13538c2ecf20Sopenharmony_ci break; 13548c2ecf20Sopenharmony_ci case TSTBUS_TXUC: 13558c2ecf20Sopenharmony_ci reg = UFS_TEST_BUS_CTRL_0; 13568c2ecf20Sopenharmony_ci offset = 8; 13578c2ecf20Sopenharmony_ci break; 13588c2ecf20Sopenharmony_ci case TSTBUS_RXUC: 13598c2ecf20Sopenharmony_ci reg = UFS_TEST_BUS_CTRL_0; 13608c2ecf20Sopenharmony_ci offset = 0; 13618c2ecf20Sopenharmony_ci break; 13628c2ecf20Sopenharmony_ci case TSTBUS_DFC: 13638c2ecf20Sopenharmony_ci reg = UFS_TEST_BUS_CTRL_1; 13648c2ecf20Sopenharmony_ci offset = 24; 13658c2ecf20Sopenharmony_ci break; 13668c2ecf20Sopenharmony_ci case TSTBUS_TRLUT: 13678c2ecf20Sopenharmony_ci reg = UFS_TEST_BUS_CTRL_1; 13688c2ecf20Sopenharmony_ci offset = 16; 13698c2ecf20Sopenharmony_ci break; 13708c2ecf20Sopenharmony_ci case TSTBUS_TMRLUT: 13718c2ecf20Sopenharmony_ci reg = UFS_TEST_BUS_CTRL_1; 13728c2ecf20Sopenharmony_ci offset = 8; 13738c2ecf20Sopenharmony_ci break; 13748c2ecf20Sopenharmony_ci case TSTBUS_OCSC: 13758c2ecf20Sopenharmony_ci reg = UFS_TEST_BUS_CTRL_1; 13768c2ecf20Sopenharmony_ci offset = 0; 13778c2ecf20Sopenharmony_ci break; 13788c2ecf20Sopenharmony_ci case TSTBUS_WRAPPER: 13798c2ecf20Sopenharmony_ci reg = UFS_TEST_BUS_CTRL_2; 13808c2ecf20Sopenharmony_ci offset = 16; 13818c2ecf20Sopenharmony_ci break; 13828c2ecf20Sopenharmony_ci case TSTBUS_COMBINED: 13838c2ecf20Sopenharmony_ci reg = UFS_TEST_BUS_CTRL_2; 13848c2ecf20Sopenharmony_ci offset = 8; 13858c2ecf20Sopenharmony_ci break; 13868c2ecf20Sopenharmony_ci case TSTBUS_UTP_HCI: 13878c2ecf20Sopenharmony_ci reg = UFS_TEST_BUS_CTRL_2; 13888c2ecf20Sopenharmony_ci offset = 0; 13898c2ecf20Sopenharmony_ci break; 13908c2ecf20Sopenharmony_ci case TSTBUS_UNIPRO: 13918c2ecf20Sopenharmony_ci reg = UFS_UNIPRO_CFG; 13928c2ecf20Sopenharmony_ci offset = 20; 13938c2ecf20Sopenharmony_ci mask = 0xFFF; 13948c2ecf20Sopenharmony_ci break; 13958c2ecf20Sopenharmony_ci /* 13968c2ecf20Sopenharmony_ci * No need for a default case, since 13978c2ecf20Sopenharmony_ci * ufs_qcom_testbus_cfg_is_ok() checks that the configuration 13988c2ecf20Sopenharmony_ci * is legal 13998c2ecf20Sopenharmony_ci */ 14008c2ecf20Sopenharmony_ci } 14018c2ecf20Sopenharmony_ci mask <<= offset; 14028c2ecf20Sopenharmony_ci ufshcd_rmwl(host->hba, TEST_BUS_SEL, 14038c2ecf20Sopenharmony_ci (u32)host->testbus.select_major << 19, 14048c2ecf20Sopenharmony_ci REG_UFS_CFG1); 14058c2ecf20Sopenharmony_ci ufshcd_rmwl(host->hba, mask, 14068c2ecf20Sopenharmony_ci (u32)host->testbus.select_minor << offset, 14078c2ecf20Sopenharmony_ci reg); 14088c2ecf20Sopenharmony_ci ufs_qcom_enable_test_bus(host); 14098c2ecf20Sopenharmony_ci /* 14108c2ecf20Sopenharmony_ci * Make sure the test bus configuration is 14118c2ecf20Sopenharmony_ci * committed before returning. 14128c2ecf20Sopenharmony_ci */ 14138c2ecf20Sopenharmony_ci mb(); 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci return 0; 14168c2ecf20Sopenharmony_ci} 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_cistatic void ufs_qcom_dump_dbg_regs(struct ufs_hba *hba) 14198c2ecf20Sopenharmony_ci{ 14208c2ecf20Sopenharmony_ci ufshcd_dump_regs(hba, REG_UFS_SYS1CLK_1US, 16 * 4, 14218c2ecf20Sopenharmony_ci "HCI Vendor Specific Registers "); 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci ufs_qcom_print_hw_debug_reg_all(hba, NULL, ufs_qcom_dump_regs_wrapper); 14248c2ecf20Sopenharmony_ci} 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci/** 14278c2ecf20Sopenharmony_ci * ufs_qcom_device_reset() - toggle the (optional) device reset line 14288c2ecf20Sopenharmony_ci * @hba: per-adapter instance 14298c2ecf20Sopenharmony_ci * 14308c2ecf20Sopenharmony_ci * Toggles the (optional) reset line to reset the attached device. 14318c2ecf20Sopenharmony_ci */ 14328c2ecf20Sopenharmony_cistatic int ufs_qcom_device_reset(struct ufs_hba *hba) 14338c2ecf20Sopenharmony_ci{ 14348c2ecf20Sopenharmony_ci struct ufs_qcom_host *host = ufshcd_get_variant(hba); 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci /* reset gpio is optional */ 14378c2ecf20Sopenharmony_ci if (!host->device_reset) 14388c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci /* 14418c2ecf20Sopenharmony_ci * The UFS device shall detect reset pulses of 1us, sleep for 10us to 14428c2ecf20Sopenharmony_ci * be on the safe side. 14438c2ecf20Sopenharmony_ci */ 14448c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(host->device_reset, 1); 14458c2ecf20Sopenharmony_ci usleep_range(10, 15); 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(host->device_reset, 0); 14488c2ecf20Sopenharmony_ci usleep_range(10, 15); 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci return 0; 14518c2ecf20Sopenharmony_ci} 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND) 14548c2ecf20Sopenharmony_cistatic void ufs_qcom_config_scaling_param(struct ufs_hba *hba, 14558c2ecf20Sopenharmony_ci struct devfreq_dev_profile *p, 14568c2ecf20Sopenharmony_ci void *data) 14578c2ecf20Sopenharmony_ci{ 14588c2ecf20Sopenharmony_ci static struct devfreq_simple_ondemand_data *d; 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci if (!data) 14618c2ecf20Sopenharmony_ci return; 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci d = (struct devfreq_simple_ondemand_data *)data; 14648c2ecf20Sopenharmony_ci p->polling_ms = 60; 14658c2ecf20Sopenharmony_ci d->upthreshold = 70; 14668c2ecf20Sopenharmony_ci d->downdifferential = 5; 14678c2ecf20Sopenharmony_ci} 14688c2ecf20Sopenharmony_ci#else 14698c2ecf20Sopenharmony_cistatic void ufs_qcom_config_scaling_param(struct ufs_hba *hba, 14708c2ecf20Sopenharmony_ci struct devfreq_dev_profile *p, 14718c2ecf20Sopenharmony_ci void *data) 14728c2ecf20Sopenharmony_ci{ 14738c2ecf20Sopenharmony_ci} 14748c2ecf20Sopenharmony_ci#endif 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_ci/* 14778c2ecf20Sopenharmony_ci * struct ufs_hba_qcom_vops - UFS QCOM specific variant operations 14788c2ecf20Sopenharmony_ci * 14798c2ecf20Sopenharmony_ci * The variant operations configure the necessary controller and PHY 14808c2ecf20Sopenharmony_ci * handshake during initialization. 14818c2ecf20Sopenharmony_ci */ 14828c2ecf20Sopenharmony_cistatic const struct ufs_hba_variant_ops ufs_hba_qcom_vops = { 14838c2ecf20Sopenharmony_ci .name = "qcom", 14848c2ecf20Sopenharmony_ci .init = ufs_qcom_init, 14858c2ecf20Sopenharmony_ci .exit = ufs_qcom_exit, 14868c2ecf20Sopenharmony_ci .get_ufs_hci_version = ufs_qcom_get_ufs_hci_version, 14878c2ecf20Sopenharmony_ci .clk_scale_notify = ufs_qcom_clk_scale_notify, 14888c2ecf20Sopenharmony_ci .setup_clocks = ufs_qcom_setup_clocks, 14898c2ecf20Sopenharmony_ci .hce_enable_notify = ufs_qcom_hce_enable_notify, 14908c2ecf20Sopenharmony_ci .link_startup_notify = ufs_qcom_link_startup_notify, 14918c2ecf20Sopenharmony_ci .pwr_change_notify = ufs_qcom_pwr_change_notify, 14928c2ecf20Sopenharmony_ci .apply_dev_quirks = ufs_qcom_apply_dev_quirks, 14938c2ecf20Sopenharmony_ci .suspend = ufs_qcom_suspend, 14948c2ecf20Sopenharmony_ci .resume = ufs_qcom_resume, 14958c2ecf20Sopenharmony_ci .dbg_register_dump = ufs_qcom_dump_dbg_regs, 14968c2ecf20Sopenharmony_ci .device_reset = ufs_qcom_device_reset, 14978c2ecf20Sopenharmony_ci .config_scaling_param = ufs_qcom_config_scaling_param, 14988c2ecf20Sopenharmony_ci .program_key = ufs_qcom_ice_program_key, 14998c2ecf20Sopenharmony_ci}; 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci/** 15028c2ecf20Sopenharmony_ci * ufs_qcom_probe - probe routine of the driver 15038c2ecf20Sopenharmony_ci * @pdev: pointer to Platform device handle 15048c2ecf20Sopenharmony_ci * 15058c2ecf20Sopenharmony_ci * Return zero for success and non-zero for failure 15068c2ecf20Sopenharmony_ci */ 15078c2ecf20Sopenharmony_cistatic int ufs_qcom_probe(struct platform_device *pdev) 15088c2ecf20Sopenharmony_ci{ 15098c2ecf20Sopenharmony_ci int err; 15108c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci /* Perform generic probe */ 15138c2ecf20Sopenharmony_ci err = ufshcd_pltfrm_init(pdev, &ufs_hba_qcom_vops); 15148c2ecf20Sopenharmony_ci if (err) 15158c2ecf20Sopenharmony_ci dev_err(dev, "ufshcd_pltfrm_init() failed %d\n", err); 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_ci return err; 15188c2ecf20Sopenharmony_ci} 15198c2ecf20Sopenharmony_ci 15208c2ecf20Sopenharmony_ci/** 15218c2ecf20Sopenharmony_ci * ufs_qcom_remove - set driver_data of the device to NULL 15228c2ecf20Sopenharmony_ci * @pdev: pointer to platform device handle 15238c2ecf20Sopenharmony_ci * 15248c2ecf20Sopenharmony_ci * Always returns 0 15258c2ecf20Sopenharmony_ci */ 15268c2ecf20Sopenharmony_cistatic int ufs_qcom_remove(struct platform_device *pdev) 15278c2ecf20Sopenharmony_ci{ 15288c2ecf20Sopenharmony_ci struct ufs_hba *hba = platform_get_drvdata(pdev); 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci pm_runtime_get_sync(&(pdev)->dev); 15318c2ecf20Sopenharmony_ci ufshcd_remove(hba); 15328c2ecf20Sopenharmony_ci return 0; 15338c2ecf20Sopenharmony_ci} 15348c2ecf20Sopenharmony_ci 15358c2ecf20Sopenharmony_cistatic const struct of_device_id ufs_qcom_of_match[] = { 15368c2ecf20Sopenharmony_ci { .compatible = "qcom,ufshc"}, 15378c2ecf20Sopenharmony_ci {}, 15388c2ecf20Sopenharmony_ci}; 15398c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ufs_qcom_of_match); 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI 15428c2ecf20Sopenharmony_cistatic const struct acpi_device_id ufs_qcom_acpi_match[] = { 15438c2ecf20Sopenharmony_ci { "QCOM24A5" }, 15448c2ecf20Sopenharmony_ci { }, 15458c2ecf20Sopenharmony_ci}; 15468c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, ufs_qcom_acpi_match); 15478c2ecf20Sopenharmony_ci#endif 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_cistatic const struct dev_pm_ops ufs_qcom_pm_ops = { 15508c2ecf20Sopenharmony_ci .suspend = ufshcd_pltfrm_suspend, 15518c2ecf20Sopenharmony_ci .resume = ufshcd_pltfrm_resume, 15528c2ecf20Sopenharmony_ci .runtime_suspend = ufshcd_pltfrm_runtime_suspend, 15538c2ecf20Sopenharmony_ci .runtime_resume = ufshcd_pltfrm_runtime_resume, 15548c2ecf20Sopenharmony_ci .runtime_idle = ufshcd_pltfrm_runtime_idle, 15558c2ecf20Sopenharmony_ci}; 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_cistatic struct platform_driver ufs_qcom_pltform = { 15588c2ecf20Sopenharmony_ci .probe = ufs_qcom_probe, 15598c2ecf20Sopenharmony_ci .remove = ufs_qcom_remove, 15608c2ecf20Sopenharmony_ci .shutdown = ufshcd_pltfrm_shutdown, 15618c2ecf20Sopenharmony_ci .driver = { 15628c2ecf20Sopenharmony_ci .name = "ufshcd-qcom", 15638c2ecf20Sopenharmony_ci .pm = &ufs_qcom_pm_ops, 15648c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(ufs_qcom_of_match), 15658c2ecf20Sopenharmony_ci .acpi_match_table = ACPI_PTR(ufs_qcom_acpi_match), 15668c2ecf20Sopenharmony_ci }, 15678c2ecf20Sopenharmony_ci}; 15688c2ecf20Sopenharmony_cimodule_platform_driver(ufs_qcom_pltform); 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1571