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