162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2019 MediaTek Inc.
462306a36Sopenharmony_ci * Authors:
562306a36Sopenharmony_ci *	Stanley Chu <stanley.chu@mediatek.com>
662306a36Sopenharmony_ci *	Peter Wang <peter.wang@mediatek.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/arm-smccc.h>
1062306a36Sopenharmony_ci#include <linux/bitfield.h>
1162306a36Sopenharmony_ci#include <linux/clk.h>
1262306a36Sopenharmony_ci#include <linux/delay.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/of.h>
1562306a36Sopenharmony_ci#include <linux/of_address.h>
1662306a36Sopenharmony_ci#include <linux/of_device.h>
1762306a36Sopenharmony_ci#include <linux/of_platform.h>
1862306a36Sopenharmony_ci#include <linux/phy/phy.h>
1962306a36Sopenharmony_ci#include <linux/platform_device.h>
2062306a36Sopenharmony_ci#include <linux/pm_qos.h>
2162306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
2262306a36Sopenharmony_ci#include <linux/reset.h>
2362306a36Sopenharmony_ci#include <linux/soc/mediatek/mtk_sip_svc.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include <ufs/ufshcd.h>
2662306a36Sopenharmony_ci#include "ufshcd-pltfrm.h"
2762306a36Sopenharmony_ci#include <ufs/ufs_quirks.h>
2862306a36Sopenharmony_ci#include <ufs/unipro.h>
2962306a36Sopenharmony_ci#include "ufs-mediatek.h"
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic int  ufs_mtk_config_mcq(struct ufs_hba *hba, bool irq);
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define CREATE_TRACE_POINTS
3462306a36Sopenharmony_ci#include "ufs-mediatek-trace.h"
3562306a36Sopenharmony_ci#undef CREATE_TRACE_POINTS
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define MAX_SUPP_MAC 64
3862306a36Sopenharmony_ci#define MCQ_QUEUE_OFFSET(c) ((((c) >> 16) & 0xFF) * 0x200)
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic const struct ufs_dev_quirk ufs_mtk_dev_fixups[] = {
4162306a36Sopenharmony_ci	{ .wmanufacturerid = UFS_ANY_VENDOR,
4262306a36Sopenharmony_ci	  .model = UFS_ANY_MODEL,
4362306a36Sopenharmony_ci	  .quirk = UFS_DEVICE_QUIRK_DELAY_AFTER_LPM |
4462306a36Sopenharmony_ci		UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM },
4562306a36Sopenharmony_ci	{ .wmanufacturerid = UFS_VENDOR_SKHYNIX,
4662306a36Sopenharmony_ci	  .model = "H9HQ21AFAMZDAR",
4762306a36Sopenharmony_ci	  .quirk = UFS_DEVICE_QUIRK_SUPPORT_EXTENDED_FEATURES },
4862306a36Sopenharmony_ci	{}
4962306a36Sopenharmony_ci};
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic const struct of_device_id ufs_mtk_of_match[] = {
5262306a36Sopenharmony_ci	{ .compatible = "mediatek,mt8183-ufshci" },
5362306a36Sopenharmony_ci	{},
5462306a36Sopenharmony_ci};
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci/*
5762306a36Sopenharmony_ci * Details of UIC Errors
5862306a36Sopenharmony_ci */
5962306a36Sopenharmony_cistatic const char *const ufs_uic_err_str[] = {
6062306a36Sopenharmony_ci	"PHY Adapter Layer",
6162306a36Sopenharmony_ci	"Data Link Layer",
6262306a36Sopenharmony_ci	"Network Link Layer",
6362306a36Sopenharmony_ci	"Transport Link Layer",
6462306a36Sopenharmony_ci	"DME"
6562306a36Sopenharmony_ci};
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic const char *const ufs_uic_pa_err_str[] = {
6862306a36Sopenharmony_ci	"PHY error on Lane 0",
6962306a36Sopenharmony_ci	"PHY error on Lane 1",
7062306a36Sopenharmony_ci	"PHY error on Lane 2",
7162306a36Sopenharmony_ci	"PHY error on Lane 3",
7262306a36Sopenharmony_ci	"Generic PHY Adapter Error. This should be the LINERESET indication"
7362306a36Sopenharmony_ci};
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic const char *const ufs_uic_dl_err_str[] = {
7662306a36Sopenharmony_ci	"NAC_RECEIVED",
7762306a36Sopenharmony_ci	"TCx_REPLAY_TIMER_EXPIRED",
7862306a36Sopenharmony_ci	"AFCx_REQUEST_TIMER_EXPIRED",
7962306a36Sopenharmony_ci	"FCx_PROTECTION_TIMER_EXPIRED",
8062306a36Sopenharmony_ci	"CRC_ERROR",
8162306a36Sopenharmony_ci	"RX_BUFFER_OVERFLOW",
8262306a36Sopenharmony_ci	"MAX_FRAME_LENGTH_EXCEEDED",
8362306a36Sopenharmony_ci	"WRONG_SEQUENCE_NUMBER",
8462306a36Sopenharmony_ci	"AFC_FRAME_SYNTAX_ERROR",
8562306a36Sopenharmony_ci	"NAC_FRAME_SYNTAX_ERROR",
8662306a36Sopenharmony_ci	"EOF_SYNTAX_ERROR",
8762306a36Sopenharmony_ci	"FRAME_SYNTAX_ERROR",
8862306a36Sopenharmony_ci	"BAD_CTRL_SYMBOL_TYPE",
8962306a36Sopenharmony_ci	"PA_INIT_ERROR",
9062306a36Sopenharmony_ci	"PA_ERROR_IND_RECEIVED",
9162306a36Sopenharmony_ci	"PA_INIT"
9262306a36Sopenharmony_ci};
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic bool ufs_mtk_is_boost_crypt_enabled(struct ufs_hba *hba)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	struct ufs_mtk_host *host = ufshcd_get_variant(hba);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	return !!(host->caps & UFS_MTK_CAP_BOOST_CRYPT_ENGINE);
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic bool ufs_mtk_is_va09_supported(struct ufs_hba *hba)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	struct ufs_mtk_host *host = ufshcd_get_variant(hba);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	return !!(host->caps & UFS_MTK_CAP_VA09_PWR_CTRL);
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic bool ufs_mtk_is_broken_vcc(struct ufs_hba *hba)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	struct ufs_mtk_host *host = ufshcd_get_variant(hba);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	return !!(host->caps & UFS_MTK_CAP_BROKEN_VCC);
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic bool ufs_mtk_is_pmc_via_fastauto(struct ufs_hba *hba)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	struct ufs_mtk_host *host = ufshcd_get_variant(hba);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	return !!(host->caps & UFS_MTK_CAP_PMC_VIA_FASTAUTO);
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic void ufs_mtk_cfg_unipro_cg(struct ufs_hba *hba, bool enable)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	u32 tmp;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	if (enable) {
12762306a36Sopenharmony_ci		ufshcd_dme_get(hba,
12862306a36Sopenharmony_ci			       UIC_ARG_MIB(VS_SAVEPOWERCONTROL), &tmp);
12962306a36Sopenharmony_ci		tmp = tmp |
13062306a36Sopenharmony_ci		      (1 << RX_SYMBOL_CLK_GATE_EN) |
13162306a36Sopenharmony_ci		      (1 << SYS_CLK_GATE_EN) |
13262306a36Sopenharmony_ci		      (1 << TX_CLK_GATE_EN);
13362306a36Sopenharmony_ci		ufshcd_dme_set(hba,
13462306a36Sopenharmony_ci			       UIC_ARG_MIB(VS_SAVEPOWERCONTROL), tmp);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci		ufshcd_dme_get(hba,
13762306a36Sopenharmony_ci			       UIC_ARG_MIB(VS_DEBUGCLOCKENABLE), &tmp);
13862306a36Sopenharmony_ci		tmp = tmp & ~(1 << TX_SYMBOL_CLK_REQ_FORCE);
13962306a36Sopenharmony_ci		ufshcd_dme_set(hba,
14062306a36Sopenharmony_ci			       UIC_ARG_MIB(VS_DEBUGCLOCKENABLE), tmp);
14162306a36Sopenharmony_ci	} else {
14262306a36Sopenharmony_ci		ufshcd_dme_get(hba,
14362306a36Sopenharmony_ci			       UIC_ARG_MIB(VS_SAVEPOWERCONTROL), &tmp);
14462306a36Sopenharmony_ci		tmp = tmp & ~((1 << RX_SYMBOL_CLK_GATE_EN) |
14562306a36Sopenharmony_ci			      (1 << SYS_CLK_GATE_EN) |
14662306a36Sopenharmony_ci			      (1 << TX_CLK_GATE_EN));
14762306a36Sopenharmony_ci		ufshcd_dme_set(hba,
14862306a36Sopenharmony_ci			       UIC_ARG_MIB(VS_SAVEPOWERCONTROL), tmp);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci		ufshcd_dme_get(hba,
15162306a36Sopenharmony_ci			       UIC_ARG_MIB(VS_DEBUGCLOCKENABLE), &tmp);
15262306a36Sopenharmony_ci		tmp = tmp | (1 << TX_SYMBOL_CLK_REQ_FORCE);
15362306a36Sopenharmony_ci		ufshcd_dme_set(hba,
15462306a36Sopenharmony_ci			       UIC_ARG_MIB(VS_DEBUGCLOCKENABLE), tmp);
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic void ufs_mtk_crypto_enable(struct ufs_hba *hba)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	struct arm_smccc_res res;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	ufs_mtk_crypto_ctrl(res, 1);
16362306a36Sopenharmony_ci	if (res.a0) {
16462306a36Sopenharmony_ci		dev_info(hba->dev, "%s: crypto enable failed, err: %lu\n",
16562306a36Sopenharmony_ci			 __func__, res.a0);
16662306a36Sopenharmony_ci		hba->caps &= ~UFSHCD_CAP_CRYPTO;
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic void ufs_mtk_host_reset(struct ufs_hba *hba)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	struct ufs_mtk_host *host = ufshcd_get_variant(hba);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	reset_control_assert(host->hci_reset);
17562306a36Sopenharmony_ci	reset_control_assert(host->crypto_reset);
17662306a36Sopenharmony_ci	reset_control_assert(host->unipro_reset);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	usleep_range(100, 110);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	reset_control_deassert(host->unipro_reset);
18162306a36Sopenharmony_ci	reset_control_deassert(host->crypto_reset);
18262306a36Sopenharmony_ci	reset_control_deassert(host->hci_reset);
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic void ufs_mtk_init_reset_control(struct ufs_hba *hba,
18662306a36Sopenharmony_ci				       struct reset_control **rc,
18762306a36Sopenharmony_ci				       char *str)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	*rc = devm_reset_control_get(hba->dev, str);
19062306a36Sopenharmony_ci	if (IS_ERR(*rc)) {
19162306a36Sopenharmony_ci		dev_info(hba->dev, "Failed to get reset control %s: %ld\n",
19262306a36Sopenharmony_ci			 str, PTR_ERR(*rc));
19362306a36Sopenharmony_ci		*rc = NULL;
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic void ufs_mtk_init_reset(struct ufs_hba *hba)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	struct ufs_mtk_host *host = ufshcd_get_variant(hba);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	ufs_mtk_init_reset_control(hba, &host->hci_reset,
20262306a36Sopenharmony_ci				   "hci_rst");
20362306a36Sopenharmony_ci	ufs_mtk_init_reset_control(hba, &host->unipro_reset,
20462306a36Sopenharmony_ci				   "unipro_rst");
20562306a36Sopenharmony_ci	ufs_mtk_init_reset_control(hba, &host->crypto_reset,
20662306a36Sopenharmony_ci				   "crypto_rst");
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_cistatic int ufs_mtk_hce_enable_notify(struct ufs_hba *hba,
21062306a36Sopenharmony_ci				     enum ufs_notify_change_status status)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	struct ufs_mtk_host *host = ufshcd_get_variant(hba);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	if (status == PRE_CHANGE) {
21562306a36Sopenharmony_ci		if (host->unipro_lpm) {
21662306a36Sopenharmony_ci			hba->vps->hba_enable_delay_us = 0;
21762306a36Sopenharmony_ci		} else {
21862306a36Sopenharmony_ci			hba->vps->hba_enable_delay_us = 600;
21962306a36Sopenharmony_ci			ufs_mtk_host_reset(hba);
22062306a36Sopenharmony_ci		}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci		if (hba->caps & UFSHCD_CAP_CRYPTO)
22362306a36Sopenharmony_ci			ufs_mtk_crypto_enable(hba);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci		if (host->caps & UFS_MTK_CAP_DISABLE_AH8) {
22662306a36Sopenharmony_ci			ufshcd_writel(hba, 0,
22762306a36Sopenharmony_ci				      REG_AUTO_HIBERNATE_IDLE_TIMER);
22862306a36Sopenharmony_ci			hba->capabilities &= ~MASK_AUTO_HIBERN8_SUPPORT;
22962306a36Sopenharmony_ci			hba->ahit = 0;
23062306a36Sopenharmony_ci		}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci		/*
23362306a36Sopenharmony_ci		 * Turn on CLK_CG early to bypass abnormal ERR_CHK signal
23462306a36Sopenharmony_ci		 * to prevent host hang issue
23562306a36Sopenharmony_ci		 */
23662306a36Sopenharmony_ci		ufshcd_writel(hba,
23762306a36Sopenharmony_ci			      ufshcd_readl(hba, REG_UFS_XOUFS_CTRL) | 0x80,
23862306a36Sopenharmony_ci			      REG_UFS_XOUFS_CTRL);
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	return 0;
24262306a36Sopenharmony_ci}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cistatic int ufs_mtk_bind_mphy(struct ufs_hba *hba)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	struct ufs_mtk_host *host = ufshcd_get_variant(hba);
24762306a36Sopenharmony_ci	struct device *dev = hba->dev;
24862306a36Sopenharmony_ci	struct device_node *np = dev->of_node;
24962306a36Sopenharmony_ci	int err = 0;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	host->mphy = devm_of_phy_get_by_index(dev, np, 0);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	if (host->mphy == ERR_PTR(-EPROBE_DEFER)) {
25462306a36Sopenharmony_ci		/*
25562306a36Sopenharmony_ci		 * UFS driver might be probed before the phy driver does.
25662306a36Sopenharmony_ci		 * In that case we would like to return EPROBE_DEFER code.
25762306a36Sopenharmony_ci		 */
25862306a36Sopenharmony_ci		err = -EPROBE_DEFER;
25962306a36Sopenharmony_ci		dev_info(dev,
26062306a36Sopenharmony_ci			 "%s: required phy hasn't probed yet. err = %d\n",
26162306a36Sopenharmony_ci			__func__, err);
26262306a36Sopenharmony_ci	} else if (IS_ERR(host->mphy)) {
26362306a36Sopenharmony_ci		err = PTR_ERR(host->mphy);
26462306a36Sopenharmony_ci		if (err != -ENODEV) {
26562306a36Sopenharmony_ci			dev_info(dev, "%s: PHY get failed %d\n", __func__,
26662306a36Sopenharmony_ci				 err);
26762306a36Sopenharmony_ci		}
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	if (err)
27162306a36Sopenharmony_ci		host->mphy = NULL;
27262306a36Sopenharmony_ci	/*
27362306a36Sopenharmony_ci	 * Allow unbound mphy because not every platform needs specific
27462306a36Sopenharmony_ci	 * mphy control.
27562306a36Sopenharmony_ci	 */
27662306a36Sopenharmony_ci	if (err == -ENODEV)
27762306a36Sopenharmony_ci		err = 0;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	return err;
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic int ufs_mtk_setup_ref_clk(struct ufs_hba *hba, bool on)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	struct ufs_mtk_host *host = ufshcd_get_variant(hba);
28562306a36Sopenharmony_ci	struct arm_smccc_res res;
28662306a36Sopenharmony_ci	ktime_t timeout, time_checked;
28762306a36Sopenharmony_ci	u32 value;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	if (host->ref_clk_enabled == on)
29062306a36Sopenharmony_ci		return 0;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	ufs_mtk_ref_clk_notify(on, PRE_CHANGE, res);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	if (on) {
29562306a36Sopenharmony_ci		ufshcd_writel(hba, REFCLK_REQUEST, REG_UFS_REFCLK_CTRL);
29662306a36Sopenharmony_ci	} else {
29762306a36Sopenharmony_ci		ufshcd_delay_us(host->ref_clk_gating_wait_us, 10);
29862306a36Sopenharmony_ci		ufshcd_writel(hba, REFCLK_RELEASE, REG_UFS_REFCLK_CTRL);
29962306a36Sopenharmony_ci	}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	/* Wait for ack */
30262306a36Sopenharmony_ci	timeout = ktime_add_us(ktime_get(), REFCLK_REQ_TIMEOUT_US);
30362306a36Sopenharmony_ci	do {
30462306a36Sopenharmony_ci		time_checked = ktime_get();
30562306a36Sopenharmony_ci		value = ufshcd_readl(hba, REG_UFS_REFCLK_CTRL);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci		/* Wait until ack bit equals to req bit */
30862306a36Sopenharmony_ci		if (((value & REFCLK_ACK) >> 1) == (value & REFCLK_REQUEST))
30962306a36Sopenharmony_ci			goto out;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci		usleep_range(100, 200);
31262306a36Sopenharmony_ci	} while (ktime_before(time_checked, timeout));
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	dev_err(hba->dev, "missing ack of refclk req, reg: 0x%x\n", value);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	ufs_mtk_ref_clk_notify(host->ref_clk_enabled, POST_CHANGE, res);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	return -ETIMEDOUT;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ciout:
32162306a36Sopenharmony_ci	host->ref_clk_enabled = on;
32262306a36Sopenharmony_ci	if (on)
32362306a36Sopenharmony_ci		ufshcd_delay_us(host->ref_clk_ungating_wait_us, 10);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	ufs_mtk_ref_clk_notify(on, POST_CHANGE, res);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	return 0;
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistatic void ufs_mtk_setup_ref_clk_wait_us(struct ufs_hba *hba,
33162306a36Sopenharmony_ci					  u16 gating_us)
33262306a36Sopenharmony_ci{
33362306a36Sopenharmony_ci	struct ufs_mtk_host *host = ufshcd_get_variant(hba);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	if (hba->dev_info.clk_gating_wait_us) {
33662306a36Sopenharmony_ci		host->ref_clk_gating_wait_us =
33762306a36Sopenharmony_ci			hba->dev_info.clk_gating_wait_us;
33862306a36Sopenharmony_ci	} else {
33962306a36Sopenharmony_ci		host->ref_clk_gating_wait_us = gating_us;
34062306a36Sopenharmony_ci	}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	host->ref_clk_ungating_wait_us = REFCLK_DEFAULT_WAIT_US;
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_cistatic void ufs_mtk_dbg_sel(struct ufs_hba *hba)
34662306a36Sopenharmony_ci{
34762306a36Sopenharmony_ci	struct ufs_mtk_host *host = ufshcd_get_variant(hba);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	if (((host->ip_ver >> 16) & 0xFF) >= 0x36) {
35062306a36Sopenharmony_ci		ufshcd_writel(hba, 0x820820, REG_UFS_DEBUG_SEL);
35162306a36Sopenharmony_ci		ufshcd_writel(hba, 0x0, REG_UFS_DEBUG_SEL_B0);
35262306a36Sopenharmony_ci		ufshcd_writel(hba, 0x55555555, REG_UFS_DEBUG_SEL_B1);
35362306a36Sopenharmony_ci		ufshcd_writel(hba, 0xaaaaaaaa, REG_UFS_DEBUG_SEL_B2);
35462306a36Sopenharmony_ci		ufshcd_writel(hba, 0xffffffff, REG_UFS_DEBUG_SEL_B3);
35562306a36Sopenharmony_ci	} else {
35662306a36Sopenharmony_ci		ufshcd_writel(hba, 0x20, REG_UFS_DEBUG_SEL);
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_cistatic void ufs_mtk_wait_idle_state(struct ufs_hba *hba,
36162306a36Sopenharmony_ci			    unsigned long retry_ms)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	u64 timeout, time_checked;
36462306a36Sopenharmony_ci	u32 val, sm;
36562306a36Sopenharmony_ci	bool wait_idle;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	/* cannot use plain ktime_get() in suspend */
36862306a36Sopenharmony_ci	timeout = ktime_get_mono_fast_ns() + retry_ms * 1000000UL;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	/* wait a specific time after check base */
37162306a36Sopenharmony_ci	udelay(10);
37262306a36Sopenharmony_ci	wait_idle = false;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	do {
37562306a36Sopenharmony_ci		time_checked = ktime_get_mono_fast_ns();
37662306a36Sopenharmony_ci		ufs_mtk_dbg_sel(hba);
37762306a36Sopenharmony_ci		val = ufshcd_readl(hba, REG_UFS_PROBE);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci		sm = val & 0x1f;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci		/*
38262306a36Sopenharmony_ci		 * if state is in H8 enter and H8 enter confirm
38362306a36Sopenharmony_ci		 * wait until return to idle state.
38462306a36Sopenharmony_ci		 */
38562306a36Sopenharmony_ci		if ((sm >= VS_HIB_ENTER) && (sm <= VS_HIB_EXIT)) {
38662306a36Sopenharmony_ci			wait_idle = true;
38762306a36Sopenharmony_ci			udelay(50);
38862306a36Sopenharmony_ci			continue;
38962306a36Sopenharmony_ci		} else if (!wait_idle)
39062306a36Sopenharmony_ci			break;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci		if (wait_idle && (sm == VS_HCE_BASE))
39362306a36Sopenharmony_ci			break;
39462306a36Sopenharmony_ci	} while (time_checked < timeout);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	if (wait_idle && sm != VS_HCE_BASE)
39762306a36Sopenharmony_ci		dev_info(hba->dev, "wait idle tmo: 0x%x\n", val);
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_cistatic int ufs_mtk_wait_link_state(struct ufs_hba *hba, u32 state,
40162306a36Sopenharmony_ci				   unsigned long max_wait_ms)
40262306a36Sopenharmony_ci{
40362306a36Sopenharmony_ci	ktime_t timeout, time_checked;
40462306a36Sopenharmony_ci	u32 val;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	timeout = ktime_add_ms(ktime_get(), max_wait_ms);
40762306a36Sopenharmony_ci	do {
40862306a36Sopenharmony_ci		time_checked = ktime_get();
40962306a36Sopenharmony_ci		ufs_mtk_dbg_sel(hba);
41062306a36Sopenharmony_ci		val = ufshcd_readl(hba, REG_UFS_PROBE);
41162306a36Sopenharmony_ci		val = val >> 28;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci		if (val == state)
41462306a36Sopenharmony_ci			return 0;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci		/* Sleep for max. 200us */
41762306a36Sopenharmony_ci		usleep_range(100, 200);
41862306a36Sopenharmony_ci	} while (ktime_before(time_checked, timeout));
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	return -ETIMEDOUT;
42162306a36Sopenharmony_ci}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_cistatic int ufs_mtk_mphy_power_on(struct ufs_hba *hba, bool on)
42462306a36Sopenharmony_ci{
42562306a36Sopenharmony_ci	struct ufs_mtk_host *host = ufshcd_get_variant(hba);
42662306a36Sopenharmony_ci	struct phy *mphy = host->mphy;
42762306a36Sopenharmony_ci	struct arm_smccc_res res;
42862306a36Sopenharmony_ci	int ret = 0;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	if (!mphy || !(on ^ host->mphy_powered_on))
43162306a36Sopenharmony_ci		return 0;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	if (on) {
43462306a36Sopenharmony_ci		if (ufs_mtk_is_va09_supported(hba)) {
43562306a36Sopenharmony_ci			ret = regulator_enable(host->reg_va09);
43662306a36Sopenharmony_ci			if (ret < 0)
43762306a36Sopenharmony_ci				goto out;
43862306a36Sopenharmony_ci			/* wait 200 us to stablize VA09 */
43962306a36Sopenharmony_ci			usleep_range(200, 210);
44062306a36Sopenharmony_ci			ufs_mtk_va09_pwr_ctrl(res, 1);
44162306a36Sopenharmony_ci		}
44262306a36Sopenharmony_ci		phy_power_on(mphy);
44362306a36Sopenharmony_ci	} else {
44462306a36Sopenharmony_ci		phy_power_off(mphy);
44562306a36Sopenharmony_ci		if (ufs_mtk_is_va09_supported(hba)) {
44662306a36Sopenharmony_ci			ufs_mtk_va09_pwr_ctrl(res, 0);
44762306a36Sopenharmony_ci			ret = regulator_disable(host->reg_va09);
44862306a36Sopenharmony_ci		}
44962306a36Sopenharmony_ci	}
45062306a36Sopenharmony_ciout:
45162306a36Sopenharmony_ci	if (ret) {
45262306a36Sopenharmony_ci		dev_info(hba->dev,
45362306a36Sopenharmony_ci			 "failed to %s va09: %d\n",
45462306a36Sopenharmony_ci			 on ? "enable" : "disable",
45562306a36Sopenharmony_ci			 ret);
45662306a36Sopenharmony_ci	} else {
45762306a36Sopenharmony_ci		host->mphy_powered_on = on;
45862306a36Sopenharmony_ci	}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	return ret;
46162306a36Sopenharmony_ci}
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_cistatic int ufs_mtk_get_host_clk(struct device *dev, const char *name,
46462306a36Sopenharmony_ci				struct clk **clk_out)
46562306a36Sopenharmony_ci{
46662306a36Sopenharmony_ci	struct clk *clk;
46762306a36Sopenharmony_ci	int err = 0;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	clk = devm_clk_get(dev, name);
47062306a36Sopenharmony_ci	if (IS_ERR(clk))
47162306a36Sopenharmony_ci		err = PTR_ERR(clk);
47262306a36Sopenharmony_ci	else
47362306a36Sopenharmony_ci		*clk_out = clk;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	return err;
47662306a36Sopenharmony_ci}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_cistatic void ufs_mtk_boost_crypt(struct ufs_hba *hba, bool boost)
47962306a36Sopenharmony_ci{
48062306a36Sopenharmony_ci	struct ufs_mtk_host *host = ufshcd_get_variant(hba);
48162306a36Sopenharmony_ci	struct ufs_mtk_crypt_cfg *cfg;
48262306a36Sopenharmony_ci	struct regulator *reg;
48362306a36Sopenharmony_ci	int volt, ret;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	if (!ufs_mtk_is_boost_crypt_enabled(hba))
48662306a36Sopenharmony_ci		return;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	cfg = host->crypt;
48962306a36Sopenharmony_ci	volt = cfg->vcore_volt;
49062306a36Sopenharmony_ci	reg = cfg->reg_vcore;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	ret = clk_prepare_enable(cfg->clk_crypt_mux);
49362306a36Sopenharmony_ci	if (ret) {
49462306a36Sopenharmony_ci		dev_info(hba->dev, "clk_prepare_enable(): %d\n",
49562306a36Sopenharmony_ci			 ret);
49662306a36Sopenharmony_ci		return;
49762306a36Sopenharmony_ci	}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	if (boost) {
50062306a36Sopenharmony_ci		ret = regulator_set_voltage(reg, volt, INT_MAX);
50162306a36Sopenharmony_ci		if (ret) {
50262306a36Sopenharmony_ci			dev_info(hba->dev,
50362306a36Sopenharmony_ci				 "failed to set vcore to %d\n", volt);
50462306a36Sopenharmony_ci			goto out;
50562306a36Sopenharmony_ci		}
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci		ret = clk_set_parent(cfg->clk_crypt_mux,
50862306a36Sopenharmony_ci				     cfg->clk_crypt_perf);
50962306a36Sopenharmony_ci		if (ret) {
51062306a36Sopenharmony_ci			dev_info(hba->dev,
51162306a36Sopenharmony_ci				 "failed to set clk_crypt_perf\n");
51262306a36Sopenharmony_ci			regulator_set_voltage(reg, 0, INT_MAX);
51362306a36Sopenharmony_ci			goto out;
51462306a36Sopenharmony_ci		}
51562306a36Sopenharmony_ci	} else {
51662306a36Sopenharmony_ci		ret = clk_set_parent(cfg->clk_crypt_mux,
51762306a36Sopenharmony_ci				     cfg->clk_crypt_lp);
51862306a36Sopenharmony_ci		if (ret) {
51962306a36Sopenharmony_ci			dev_info(hba->dev,
52062306a36Sopenharmony_ci				 "failed to set clk_crypt_lp\n");
52162306a36Sopenharmony_ci			goto out;
52262306a36Sopenharmony_ci		}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci		ret = regulator_set_voltage(reg, 0, INT_MAX);
52562306a36Sopenharmony_ci		if (ret) {
52662306a36Sopenharmony_ci			dev_info(hba->dev,
52762306a36Sopenharmony_ci				 "failed to set vcore to MIN\n");
52862306a36Sopenharmony_ci		}
52962306a36Sopenharmony_ci	}
53062306a36Sopenharmony_ciout:
53162306a36Sopenharmony_ci	clk_disable_unprepare(cfg->clk_crypt_mux);
53262306a36Sopenharmony_ci}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_cistatic int ufs_mtk_init_host_clk(struct ufs_hba *hba, const char *name,
53562306a36Sopenharmony_ci				 struct clk **clk)
53662306a36Sopenharmony_ci{
53762306a36Sopenharmony_ci	int ret;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	ret = ufs_mtk_get_host_clk(hba->dev, name, clk);
54062306a36Sopenharmony_ci	if (ret) {
54162306a36Sopenharmony_ci		dev_info(hba->dev, "%s: failed to get %s: %d", __func__,
54262306a36Sopenharmony_ci			 name, ret);
54362306a36Sopenharmony_ci	}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	return ret;
54662306a36Sopenharmony_ci}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_cistatic void ufs_mtk_init_boost_crypt(struct ufs_hba *hba)
54962306a36Sopenharmony_ci{
55062306a36Sopenharmony_ci	struct ufs_mtk_host *host = ufshcd_get_variant(hba);
55162306a36Sopenharmony_ci	struct ufs_mtk_crypt_cfg *cfg;
55262306a36Sopenharmony_ci	struct device *dev = hba->dev;
55362306a36Sopenharmony_ci	struct regulator *reg;
55462306a36Sopenharmony_ci	u32 volt;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	host->crypt = devm_kzalloc(dev, sizeof(*(host->crypt)),
55762306a36Sopenharmony_ci				   GFP_KERNEL);
55862306a36Sopenharmony_ci	if (!host->crypt)
55962306a36Sopenharmony_ci		goto disable_caps;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	reg = devm_regulator_get_optional(dev, "dvfsrc-vcore");
56262306a36Sopenharmony_ci	if (IS_ERR(reg)) {
56362306a36Sopenharmony_ci		dev_info(dev, "failed to get dvfsrc-vcore: %ld",
56462306a36Sopenharmony_ci			 PTR_ERR(reg));
56562306a36Sopenharmony_ci		goto disable_caps;
56662306a36Sopenharmony_ci	}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	if (of_property_read_u32(dev->of_node, "boost-crypt-vcore-min",
56962306a36Sopenharmony_ci				 &volt)) {
57062306a36Sopenharmony_ci		dev_info(dev, "failed to get boost-crypt-vcore-min");
57162306a36Sopenharmony_ci		goto disable_caps;
57262306a36Sopenharmony_ci	}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	cfg = host->crypt;
57562306a36Sopenharmony_ci	if (ufs_mtk_init_host_clk(hba, "crypt_mux",
57662306a36Sopenharmony_ci				  &cfg->clk_crypt_mux))
57762306a36Sopenharmony_ci		goto disable_caps;
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	if (ufs_mtk_init_host_clk(hba, "crypt_lp",
58062306a36Sopenharmony_ci				  &cfg->clk_crypt_lp))
58162306a36Sopenharmony_ci		goto disable_caps;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	if (ufs_mtk_init_host_clk(hba, "crypt_perf",
58462306a36Sopenharmony_ci				  &cfg->clk_crypt_perf))
58562306a36Sopenharmony_ci		goto disable_caps;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	cfg->reg_vcore = reg;
58862306a36Sopenharmony_ci	cfg->vcore_volt = volt;
58962306a36Sopenharmony_ci	host->caps |= UFS_MTK_CAP_BOOST_CRYPT_ENGINE;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_cidisable_caps:
59262306a36Sopenharmony_ci	return;
59362306a36Sopenharmony_ci}
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_cistatic void ufs_mtk_init_va09_pwr_ctrl(struct ufs_hba *hba)
59662306a36Sopenharmony_ci{
59762306a36Sopenharmony_ci	struct ufs_mtk_host *host = ufshcd_get_variant(hba);
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	host->reg_va09 = regulator_get(hba->dev, "va09");
60062306a36Sopenharmony_ci	if (IS_ERR(host->reg_va09))
60162306a36Sopenharmony_ci		dev_info(hba->dev, "failed to get va09");
60262306a36Sopenharmony_ci	else
60362306a36Sopenharmony_ci		host->caps |= UFS_MTK_CAP_VA09_PWR_CTRL;
60462306a36Sopenharmony_ci}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_cistatic void ufs_mtk_init_host_caps(struct ufs_hba *hba)
60762306a36Sopenharmony_ci{
60862306a36Sopenharmony_ci	struct ufs_mtk_host *host = ufshcd_get_variant(hba);
60962306a36Sopenharmony_ci	struct device_node *np = hba->dev->of_node;
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	if (of_property_read_bool(np, "mediatek,ufs-boost-crypt"))
61262306a36Sopenharmony_ci		ufs_mtk_init_boost_crypt(hba);
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	if (of_property_read_bool(np, "mediatek,ufs-support-va09"))
61562306a36Sopenharmony_ci		ufs_mtk_init_va09_pwr_ctrl(hba);
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	if (of_property_read_bool(np, "mediatek,ufs-disable-ah8"))
61862306a36Sopenharmony_ci		host->caps |= UFS_MTK_CAP_DISABLE_AH8;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	if (of_property_read_bool(np, "mediatek,ufs-broken-vcc"))
62162306a36Sopenharmony_ci		host->caps |= UFS_MTK_CAP_BROKEN_VCC;
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	if (of_property_read_bool(np, "mediatek,ufs-pmc-via-fastauto"))
62462306a36Sopenharmony_ci		host->caps |= UFS_MTK_CAP_PMC_VIA_FASTAUTO;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	dev_info(hba->dev, "caps: 0x%x", host->caps);
62762306a36Sopenharmony_ci}
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_cistatic void ufs_mtk_boost_pm_qos(struct ufs_hba *hba, bool boost)
63062306a36Sopenharmony_ci{
63162306a36Sopenharmony_ci	struct ufs_mtk_host *host = ufshcd_get_variant(hba);
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	if (!host || !host->pm_qos_init)
63462306a36Sopenharmony_ci		return;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	cpu_latency_qos_update_request(&host->pm_qos_req,
63762306a36Sopenharmony_ci				       boost ? 0 : PM_QOS_DEFAULT_VALUE);
63862306a36Sopenharmony_ci}
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_cistatic void ufs_mtk_scale_perf(struct ufs_hba *hba, bool scale_up)
64162306a36Sopenharmony_ci{
64262306a36Sopenharmony_ci	ufs_mtk_boost_crypt(hba, scale_up);
64362306a36Sopenharmony_ci	ufs_mtk_boost_pm_qos(hba, scale_up);
64462306a36Sopenharmony_ci}
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_cistatic void ufs_mtk_pwr_ctrl(struct ufs_hba *hba, bool on)
64762306a36Sopenharmony_ci{
64862306a36Sopenharmony_ci	struct ufs_mtk_host *host = ufshcd_get_variant(hba);
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	if (on) {
65162306a36Sopenharmony_ci		phy_power_on(host->mphy);
65262306a36Sopenharmony_ci		ufs_mtk_setup_ref_clk(hba, on);
65362306a36Sopenharmony_ci		if (!ufshcd_is_clkscaling_supported(hba))
65462306a36Sopenharmony_ci			ufs_mtk_scale_perf(hba, on);
65562306a36Sopenharmony_ci	} else {
65662306a36Sopenharmony_ci		if (!ufshcd_is_clkscaling_supported(hba))
65762306a36Sopenharmony_ci			ufs_mtk_scale_perf(hba, on);
65862306a36Sopenharmony_ci		ufs_mtk_setup_ref_clk(hba, on);
65962306a36Sopenharmony_ci		phy_power_off(host->mphy);
66062306a36Sopenharmony_ci	}
66162306a36Sopenharmony_ci}
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci/**
66462306a36Sopenharmony_ci * ufs_mtk_setup_clocks - enables/disable clocks
66562306a36Sopenharmony_ci * @hba: host controller instance
66662306a36Sopenharmony_ci * @on: If true, enable clocks else disable them.
66762306a36Sopenharmony_ci * @status: PRE_CHANGE or POST_CHANGE notify
66862306a36Sopenharmony_ci *
66962306a36Sopenharmony_ci * Return: 0 on success, non-zero on failure.
67062306a36Sopenharmony_ci */
67162306a36Sopenharmony_cistatic int ufs_mtk_setup_clocks(struct ufs_hba *hba, bool on,
67262306a36Sopenharmony_ci				enum ufs_notify_change_status status)
67362306a36Sopenharmony_ci{
67462306a36Sopenharmony_ci	struct ufs_mtk_host *host = ufshcd_get_variant(hba);
67562306a36Sopenharmony_ci	bool clk_pwr_off = false;
67662306a36Sopenharmony_ci	int ret = 0;
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	/*
67962306a36Sopenharmony_ci	 * In case ufs_mtk_init() is not yet done, simply ignore.
68062306a36Sopenharmony_ci	 * This ufs_mtk_setup_clocks() shall be called from
68162306a36Sopenharmony_ci	 * ufs_mtk_init() after init is done.
68262306a36Sopenharmony_ci	 */
68362306a36Sopenharmony_ci	if (!host)
68462306a36Sopenharmony_ci		return 0;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	if (!on && status == PRE_CHANGE) {
68762306a36Sopenharmony_ci		if (ufshcd_is_link_off(hba)) {
68862306a36Sopenharmony_ci			clk_pwr_off = true;
68962306a36Sopenharmony_ci		} else if (ufshcd_is_link_hibern8(hba) ||
69062306a36Sopenharmony_ci			 (!ufshcd_can_hibern8_during_gating(hba) &&
69162306a36Sopenharmony_ci			 ufshcd_is_auto_hibern8_enabled(hba))) {
69262306a36Sopenharmony_ci			/*
69362306a36Sopenharmony_ci			 * Gate ref-clk and poweroff mphy if link state is in
69462306a36Sopenharmony_ci			 * OFF or Hibern8 by either Auto-Hibern8 or
69562306a36Sopenharmony_ci			 * ufshcd_link_state_transition().
69662306a36Sopenharmony_ci			 */
69762306a36Sopenharmony_ci			ret = ufs_mtk_wait_link_state(hba,
69862306a36Sopenharmony_ci						      VS_LINK_HIBERN8,
69962306a36Sopenharmony_ci						      15);
70062306a36Sopenharmony_ci			if (!ret)
70162306a36Sopenharmony_ci				clk_pwr_off = true;
70262306a36Sopenharmony_ci		}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci		if (clk_pwr_off)
70562306a36Sopenharmony_ci			ufs_mtk_pwr_ctrl(hba, false);
70662306a36Sopenharmony_ci	} else if (on && status == POST_CHANGE) {
70762306a36Sopenharmony_ci		ufs_mtk_pwr_ctrl(hba, true);
70862306a36Sopenharmony_ci	}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	return ret;
71162306a36Sopenharmony_ci}
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_cistatic void ufs_mtk_get_controller_version(struct ufs_hba *hba)
71462306a36Sopenharmony_ci{
71562306a36Sopenharmony_ci	struct ufs_mtk_host *host = ufshcd_get_variant(hba);
71662306a36Sopenharmony_ci	int ret, ver = 0;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	if (host->hw_ver.major)
71962306a36Sopenharmony_ci		return;
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	/* Set default (minimum) version anyway */
72262306a36Sopenharmony_ci	host->hw_ver.major = 2;
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	ret = ufshcd_dme_get(hba, UIC_ARG_MIB(PA_LOCALVERINFO), &ver);
72562306a36Sopenharmony_ci	if (!ret) {
72662306a36Sopenharmony_ci		if (ver >= UFS_UNIPRO_VER_1_8) {
72762306a36Sopenharmony_ci			host->hw_ver.major = 3;
72862306a36Sopenharmony_ci			/*
72962306a36Sopenharmony_ci			 * Fix HCI version for some platforms with
73062306a36Sopenharmony_ci			 * incorrect version
73162306a36Sopenharmony_ci			 */
73262306a36Sopenharmony_ci			if (hba->ufs_version < ufshci_version(3, 0))
73362306a36Sopenharmony_ci				hba->ufs_version = ufshci_version(3, 0);
73462306a36Sopenharmony_ci		}
73562306a36Sopenharmony_ci	}
73662306a36Sopenharmony_ci}
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_cistatic u32 ufs_mtk_get_ufs_hci_version(struct ufs_hba *hba)
73962306a36Sopenharmony_ci{
74062306a36Sopenharmony_ci	return hba->ufs_version;
74162306a36Sopenharmony_ci}
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci/**
74462306a36Sopenharmony_ci * ufs_mtk_init_clocks - Init mtk driver private clocks
74562306a36Sopenharmony_ci *
74662306a36Sopenharmony_ci * @hba: per adapter instance
74762306a36Sopenharmony_ci */
74862306a36Sopenharmony_cistatic void ufs_mtk_init_clocks(struct ufs_hba *hba)
74962306a36Sopenharmony_ci{
75062306a36Sopenharmony_ci	struct ufs_mtk_host *host = ufshcd_get_variant(hba);
75162306a36Sopenharmony_ci	struct list_head *head = &hba->clk_list_head;
75262306a36Sopenharmony_ci	struct ufs_mtk_clk *mclk = &host->mclk;
75362306a36Sopenharmony_ci	struct ufs_clk_info *clki, *clki_tmp;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	/*
75662306a36Sopenharmony_ci	 * Find private clocks and store them in struct ufs_mtk_clk.
75762306a36Sopenharmony_ci	 * Remove "ufs_sel_min_src" and "ufs_sel_min_src" from list to avoid
75862306a36Sopenharmony_ci	 * being switched on/off in clock gating.
75962306a36Sopenharmony_ci	 */
76062306a36Sopenharmony_ci	list_for_each_entry_safe(clki, clki_tmp, head, list) {
76162306a36Sopenharmony_ci		if (!strcmp(clki->name, "ufs_sel")) {
76262306a36Sopenharmony_ci			host->mclk.ufs_sel_clki = clki;
76362306a36Sopenharmony_ci		} else if (!strcmp(clki->name, "ufs_sel_max_src")) {
76462306a36Sopenharmony_ci			host->mclk.ufs_sel_max_clki = clki;
76562306a36Sopenharmony_ci			clk_disable_unprepare(clki->clk);
76662306a36Sopenharmony_ci			list_del(&clki->list);
76762306a36Sopenharmony_ci		} else if (!strcmp(clki->name, "ufs_sel_min_src")) {
76862306a36Sopenharmony_ci			host->mclk.ufs_sel_min_clki = clki;
76962306a36Sopenharmony_ci			clk_disable_unprepare(clki->clk);
77062306a36Sopenharmony_ci			list_del(&clki->list);
77162306a36Sopenharmony_ci		}
77262306a36Sopenharmony_ci	}
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	if (!mclk->ufs_sel_clki || !mclk->ufs_sel_max_clki ||
77562306a36Sopenharmony_ci	    !mclk->ufs_sel_min_clki) {
77662306a36Sopenharmony_ci		hba->caps &= ~UFSHCD_CAP_CLK_SCALING;
77762306a36Sopenharmony_ci		dev_info(hba->dev,
77862306a36Sopenharmony_ci			 "%s: Clk-scaling not ready. Feature disabled.",
77962306a36Sopenharmony_ci			 __func__);
78062306a36Sopenharmony_ci	}
78162306a36Sopenharmony_ci}
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci#define MAX_VCC_NAME 30
78462306a36Sopenharmony_cistatic int ufs_mtk_vreg_fix_vcc(struct ufs_hba *hba)
78562306a36Sopenharmony_ci{
78662306a36Sopenharmony_ci	struct ufs_vreg_info *info = &hba->vreg_info;
78762306a36Sopenharmony_ci	struct device_node *np = hba->dev->of_node;
78862306a36Sopenharmony_ci	struct device *dev = hba->dev;
78962306a36Sopenharmony_ci	char vcc_name[MAX_VCC_NAME];
79062306a36Sopenharmony_ci	struct arm_smccc_res res;
79162306a36Sopenharmony_ci	int err, ver;
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	if (hba->vreg_info.vcc)
79462306a36Sopenharmony_ci		return 0;
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	if (of_property_read_bool(np, "mediatek,ufs-vcc-by-num")) {
79762306a36Sopenharmony_ci		ufs_mtk_get_vcc_num(res);
79862306a36Sopenharmony_ci		if (res.a1 > UFS_VCC_NONE && res.a1 < UFS_VCC_MAX)
79962306a36Sopenharmony_ci			snprintf(vcc_name, MAX_VCC_NAME, "vcc-opt%lu", res.a1);
80062306a36Sopenharmony_ci		else
80162306a36Sopenharmony_ci			return -ENODEV;
80262306a36Sopenharmony_ci	} else if (of_property_read_bool(np, "mediatek,ufs-vcc-by-ver")) {
80362306a36Sopenharmony_ci		ver = (hba->dev_info.wspecversion & 0xF00) >> 8;
80462306a36Sopenharmony_ci		snprintf(vcc_name, MAX_VCC_NAME, "vcc-ufs%u", ver);
80562306a36Sopenharmony_ci	} else {
80662306a36Sopenharmony_ci		return 0;
80762306a36Sopenharmony_ci	}
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	err = ufshcd_populate_vreg(dev, vcc_name, &info->vcc);
81062306a36Sopenharmony_ci	if (err)
81162306a36Sopenharmony_ci		return err;
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	err = ufshcd_get_vreg(dev, info->vcc);
81462306a36Sopenharmony_ci	if (err)
81562306a36Sopenharmony_ci		return err;
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	err = regulator_enable(info->vcc->reg);
81862306a36Sopenharmony_ci	if (!err) {
81962306a36Sopenharmony_ci		info->vcc->enabled = true;
82062306a36Sopenharmony_ci		dev_info(dev, "%s: %s enabled\n", __func__, vcc_name);
82162306a36Sopenharmony_ci	}
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	return err;
82462306a36Sopenharmony_ci}
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_cistatic void ufs_mtk_vreg_fix_vccqx(struct ufs_hba *hba)
82762306a36Sopenharmony_ci{
82862306a36Sopenharmony_ci	struct ufs_vreg_info *info = &hba->vreg_info;
82962306a36Sopenharmony_ci	struct ufs_vreg **vreg_on, **vreg_off;
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	if (hba->dev_info.wspecversion >= 0x0300) {
83262306a36Sopenharmony_ci		vreg_on = &info->vccq;
83362306a36Sopenharmony_ci		vreg_off = &info->vccq2;
83462306a36Sopenharmony_ci	} else {
83562306a36Sopenharmony_ci		vreg_on = &info->vccq2;
83662306a36Sopenharmony_ci		vreg_off = &info->vccq;
83762306a36Sopenharmony_ci	}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	if (*vreg_on)
84062306a36Sopenharmony_ci		(*vreg_on)->always_on = true;
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	if (*vreg_off) {
84362306a36Sopenharmony_ci		regulator_disable((*vreg_off)->reg);
84462306a36Sopenharmony_ci		devm_kfree(hba->dev, (*vreg_off)->name);
84562306a36Sopenharmony_ci		devm_kfree(hba->dev, *vreg_off);
84662306a36Sopenharmony_ci		*vreg_off = NULL;
84762306a36Sopenharmony_ci	}
84862306a36Sopenharmony_ci}
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_cistatic void ufs_mtk_init_mcq_irq(struct ufs_hba *hba)
85162306a36Sopenharmony_ci{
85262306a36Sopenharmony_ci	struct ufs_mtk_host *host = ufshcd_get_variant(hba);
85362306a36Sopenharmony_ci	struct platform_device *pdev;
85462306a36Sopenharmony_ci	int i;
85562306a36Sopenharmony_ci	int irq;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	host->mcq_nr_intr = UFSHCD_MAX_Q_NR;
85862306a36Sopenharmony_ci	pdev = container_of(hba->dev, struct platform_device, dev);
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	for (i = 0; i < host->mcq_nr_intr; i++) {
86162306a36Sopenharmony_ci		/* irq index 0 is legacy irq, sq/cq irq start from index 1 */
86262306a36Sopenharmony_ci		irq = platform_get_irq(pdev, i + 1);
86362306a36Sopenharmony_ci		if (irq < 0) {
86462306a36Sopenharmony_ci			host->mcq_intr_info[i].irq = MTK_MCQ_INVALID_IRQ;
86562306a36Sopenharmony_ci			goto failed;
86662306a36Sopenharmony_ci		}
86762306a36Sopenharmony_ci		host->mcq_intr_info[i].hba = hba;
86862306a36Sopenharmony_ci		host->mcq_intr_info[i].irq = irq;
86962306a36Sopenharmony_ci		dev_info(hba->dev, "get platform mcq irq: %d, %d\n", i, irq);
87062306a36Sopenharmony_ci	}
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	return;
87362306a36Sopenharmony_cifailed:
87462306a36Sopenharmony_ci       /* invalidate irq info */
87562306a36Sopenharmony_ci	for (i = 0; i < host->mcq_nr_intr; i++)
87662306a36Sopenharmony_ci		host->mcq_intr_info[i].irq = MTK_MCQ_INVALID_IRQ;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	host->mcq_nr_intr = 0;
87962306a36Sopenharmony_ci}
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci/**
88262306a36Sopenharmony_ci * ufs_mtk_init - find other essential mmio bases
88362306a36Sopenharmony_ci * @hba: host controller instance
88462306a36Sopenharmony_ci *
88562306a36Sopenharmony_ci * Binds PHY with controller and powers up PHY enabling clocks
88662306a36Sopenharmony_ci * and regulators.
88762306a36Sopenharmony_ci *
88862306a36Sopenharmony_ci * Return: -EPROBE_DEFER if binding fails, returns negative error
88962306a36Sopenharmony_ci * on phy power up failure and returns zero on success.
89062306a36Sopenharmony_ci */
89162306a36Sopenharmony_cistatic int ufs_mtk_init(struct ufs_hba *hba)
89262306a36Sopenharmony_ci{
89362306a36Sopenharmony_ci	const struct of_device_id *id;
89462306a36Sopenharmony_ci	struct device *dev = hba->dev;
89562306a36Sopenharmony_ci	struct ufs_mtk_host *host;
89662306a36Sopenharmony_ci	int err = 0;
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
89962306a36Sopenharmony_ci	if (!host) {
90062306a36Sopenharmony_ci		err = -ENOMEM;
90162306a36Sopenharmony_ci		dev_info(dev, "%s: no memory for mtk ufs host\n", __func__);
90262306a36Sopenharmony_ci		goto out;
90362306a36Sopenharmony_ci	}
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	host->hba = hba;
90662306a36Sopenharmony_ci	ufshcd_set_variant(hba, host);
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	id = of_match_device(ufs_mtk_of_match, dev);
90962306a36Sopenharmony_ci	if (!id) {
91062306a36Sopenharmony_ci		err = -EINVAL;
91162306a36Sopenharmony_ci		goto out;
91262306a36Sopenharmony_ci	}
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	/* Initialize host capability */
91562306a36Sopenharmony_ci	ufs_mtk_init_host_caps(hba);
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	ufs_mtk_init_mcq_irq(hba);
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	err = ufs_mtk_bind_mphy(hba);
92062306a36Sopenharmony_ci	if (err)
92162306a36Sopenharmony_ci		goto out_variant_clear;
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	ufs_mtk_init_reset(hba);
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	/* Enable runtime autosuspend */
92662306a36Sopenharmony_ci	hba->caps |= UFSHCD_CAP_RPM_AUTOSUSPEND;
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	/* Enable clock-gating */
92962306a36Sopenharmony_ci	hba->caps |= UFSHCD_CAP_CLK_GATING;
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	/* Enable inline encryption */
93262306a36Sopenharmony_ci	hba->caps |= UFSHCD_CAP_CRYPTO;
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	/* Enable WriteBooster */
93562306a36Sopenharmony_ci	hba->caps |= UFSHCD_CAP_WB_EN;
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	/* Enable clk scaling*/
93862306a36Sopenharmony_ci	hba->caps |= UFSHCD_CAP_CLK_SCALING;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	hba->quirks |= UFSHCI_QUIRK_SKIP_MANUAL_WB_FLUSH_CTRL;
94162306a36Sopenharmony_ci	hba->quirks |= UFSHCD_QUIRK_MCQ_BROKEN_INTR;
94262306a36Sopenharmony_ci	hba->quirks |= UFSHCD_QUIRK_MCQ_BROKEN_RTC;
94362306a36Sopenharmony_ci	hba->vps->wb_flush_threshold = UFS_WB_BUF_REMAIN_PERCENT(80);
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	if (host->caps & UFS_MTK_CAP_DISABLE_AH8)
94662306a36Sopenharmony_ci		hba->caps |= UFSHCD_CAP_HIBERN8_WITH_CLK_GATING;
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	ufs_mtk_init_clocks(hba);
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	/*
95162306a36Sopenharmony_ci	 * ufshcd_vops_init() is invoked after
95262306a36Sopenharmony_ci	 * ufshcd_setup_clock(true) in ufshcd_hba_init() thus
95362306a36Sopenharmony_ci	 * phy clock setup is skipped.
95462306a36Sopenharmony_ci	 *
95562306a36Sopenharmony_ci	 * Enable phy clocks specifically here.
95662306a36Sopenharmony_ci	 */
95762306a36Sopenharmony_ci	ufs_mtk_mphy_power_on(hba, true);
95862306a36Sopenharmony_ci	ufs_mtk_setup_clocks(hba, true, POST_CHANGE);
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	host->ip_ver = ufshcd_readl(hba, REG_UFS_MTK_IP_VER);
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	/* Initialize pm-qos request */
96362306a36Sopenharmony_ci	cpu_latency_qos_add_request(&host->pm_qos_req, PM_QOS_DEFAULT_VALUE);
96462306a36Sopenharmony_ci	host->pm_qos_init = true;
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	goto out;
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ciout_variant_clear:
96962306a36Sopenharmony_ci	ufshcd_set_variant(hba, NULL);
97062306a36Sopenharmony_ciout:
97162306a36Sopenharmony_ci	return err;
97262306a36Sopenharmony_ci}
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_cistatic bool ufs_mtk_pmc_via_fastauto(struct ufs_hba *hba,
97562306a36Sopenharmony_ci				     struct ufs_pa_layer_attr *dev_req_params)
97662306a36Sopenharmony_ci{
97762306a36Sopenharmony_ci	if (!ufs_mtk_is_pmc_via_fastauto(hba))
97862306a36Sopenharmony_ci		return false;
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	if (dev_req_params->hs_rate == hba->pwr_info.hs_rate)
98162306a36Sopenharmony_ci		return false;
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	if (dev_req_params->pwr_tx != FAST_MODE &&
98462306a36Sopenharmony_ci	    dev_req_params->gear_tx < UFS_HS_G4)
98562306a36Sopenharmony_ci		return false;
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	if (dev_req_params->pwr_rx != FAST_MODE &&
98862306a36Sopenharmony_ci	    dev_req_params->gear_rx < UFS_HS_G4)
98962306a36Sopenharmony_ci		return false;
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	return true;
99262306a36Sopenharmony_ci}
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_cistatic int ufs_mtk_pre_pwr_change(struct ufs_hba *hba,
99562306a36Sopenharmony_ci				  struct ufs_pa_layer_attr *dev_max_params,
99662306a36Sopenharmony_ci				  struct ufs_pa_layer_attr *dev_req_params)
99762306a36Sopenharmony_ci{
99862306a36Sopenharmony_ci	struct ufs_mtk_host *host = ufshcd_get_variant(hba);
99962306a36Sopenharmony_ci	struct ufs_dev_params host_cap;
100062306a36Sopenharmony_ci	int ret;
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	ufshcd_init_pwr_dev_param(&host_cap);
100362306a36Sopenharmony_ci	host_cap.hs_rx_gear = UFS_HS_G5;
100462306a36Sopenharmony_ci	host_cap.hs_tx_gear = UFS_HS_G5;
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	ret = ufshcd_get_pwr_dev_param(&host_cap,
100762306a36Sopenharmony_ci				       dev_max_params,
100862306a36Sopenharmony_ci				       dev_req_params);
100962306a36Sopenharmony_ci	if (ret) {
101062306a36Sopenharmony_ci		pr_info("%s: failed to determine capabilities\n",
101162306a36Sopenharmony_ci			__func__);
101262306a36Sopenharmony_ci	}
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	if (ufs_mtk_pmc_via_fastauto(hba, dev_req_params)) {
101562306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXTERMINATION), true);
101662306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXGEAR), UFS_HS_G1);
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_RXTERMINATION), true);
101962306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_RXGEAR), UFS_HS_G1);
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_ACTIVETXDATALANES),
102262306a36Sopenharmony_ci			       dev_req_params->lane_tx);
102362306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_ACTIVERXDATALANES),
102462306a36Sopenharmony_ci			       dev_req_params->lane_rx);
102562306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_HSSERIES),
102662306a36Sopenharmony_ci			       dev_req_params->hs_rate);
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXHSADAPTTYPE),
102962306a36Sopenharmony_ci			       PA_NO_ADAPT);
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci		ret = ufshcd_uic_change_pwr_mode(hba,
103262306a36Sopenharmony_ci					FASTAUTO_MODE << 4 | FASTAUTO_MODE);
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci		if (ret) {
103562306a36Sopenharmony_ci			dev_err(hba->dev, "%s: HSG1B FASTAUTO failed ret=%d\n",
103662306a36Sopenharmony_ci				__func__, ret);
103762306a36Sopenharmony_ci		}
103862306a36Sopenharmony_ci	}
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	if (host->hw_ver.major >= 3) {
104162306a36Sopenharmony_ci		ret = ufshcd_dme_configure_adapt(hba,
104262306a36Sopenharmony_ci					   dev_req_params->gear_tx,
104362306a36Sopenharmony_ci					   PA_INITIAL_ADAPT);
104462306a36Sopenharmony_ci	}
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	return ret;
104762306a36Sopenharmony_ci}
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_cistatic int ufs_mtk_pwr_change_notify(struct ufs_hba *hba,
105062306a36Sopenharmony_ci				     enum ufs_notify_change_status stage,
105162306a36Sopenharmony_ci				     struct ufs_pa_layer_attr *dev_max_params,
105262306a36Sopenharmony_ci				     struct ufs_pa_layer_attr *dev_req_params)
105362306a36Sopenharmony_ci{
105462306a36Sopenharmony_ci	int ret = 0;
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	switch (stage) {
105762306a36Sopenharmony_ci	case PRE_CHANGE:
105862306a36Sopenharmony_ci		ret = ufs_mtk_pre_pwr_change(hba, dev_max_params,
105962306a36Sopenharmony_ci					     dev_req_params);
106062306a36Sopenharmony_ci		break;
106162306a36Sopenharmony_ci	case POST_CHANGE:
106262306a36Sopenharmony_ci		break;
106362306a36Sopenharmony_ci	default:
106462306a36Sopenharmony_ci		ret = -EINVAL;
106562306a36Sopenharmony_ci		break;
106662306a36Sopenharmony_ci	}
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	return ret;
106962306a36Sopenharmony_ci}
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_cistatic int ufs_mtk_unipro_set_lpm(struct ufs_hba *hba, bool lpm)
107262306a36Sopenharmony_ci{
107362306a36Sopenharmony_ci	int ret;
107462306a36Sopenharmony_ci	struct ufs_mtk_host *host = ufshcd_get_variant(hba);
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	ret = ufshcd_dme_set(hba,
107762306a36Sopenharmony_ci			     UIC_ARG_MIB_SEL(VS_UNIPROPOWERDOWNCONTROL, 0),
107862306a36Sopenharmony_ci			     lpm ? 1 : 0);
107962306a36Sopenharmony_ci	if (!ret || !lpm) {
108062306a36Sopenharmony_ci		/*
108162306a36Sopenharmony_ci		 * Forcibly set as non-LPM mode if UIC commands is failed
108262306a36Sopenharmony_ci		 * to use default hba_enable_delay_us value for re-enabling
108362306a36Sopenharmony_ci		 * the host.
108462306a36Sopenharmony_ci		 */
108562306a36Sopenharmony_ci		host->unipro_lpm = lpm;
108662306a36Sopenharmony_ci	}
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	return ret;
108962306a36Sopenharmony_ci}
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_cistatic int ufs_mtk_pre_link(struct ufs_hba *hba)
109262306a36Sopenharmony_ci{
109362306a36Sopenharmony_ci	int ret;
109462306a36Sopenharmony_ci	u32 tmp;
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	ufs_mtk_get_controller_version(hba);
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	ret = ufs_mtk_unipro_set_lpm(hba, false);
109962306a36Sopenharmony_ci	if (ret)
110062306a36Sopenharmony_ci		return ret;
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	/*
110362306a36Sopenharmony_ci	 * Setting PA_Local_TX_LCC_Enable to 0 before link startup
110462306a36Sopenharmony_ci	 * to make sure that both host and device TX LCC are disabled
110562306a36Sopenharmony_ci	 * once link startup is completed.
110662306a36Sopenharmony_ci	 */
110762306a36Sopenharmony_ci	ret = ufshcd_disable_host_tx_lcc(hba);
110862306a36Sopenharmony_ci	if (ret)
110962306a36Sopenharmony_ci		return ret;
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	/* disable deep stall */
111262306a36Sopenharmony_ci	ret = ufshcd_dme_get(hba, UIC_ARG_MIB(VS_SAVEPOWERCONTROL), &tmp);
111362306a36Sopenharmony_ci	if (ret)
111462306a36Sopenharmony_ci		return ret;
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	tmp &= ~(1 << 6);
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	ret = ufshcd_dme_set(hba, UIC_ARG_MIB(VS_SAVEPOWERCONTROL), tmp);
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	return ret;
112162306a36Sopenharmony_ci}
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_cistatic void ufs_mtk_setup_clk_gating(struct ufs_hba *hba)
112462306a36Sopenharmony_ci{
112562306a36Sopenharmony_ci	u32 ah_ms;
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	if (ufshcd_is_clkgating_allowed(hba)) {
112862306a36Sopenharmony_ci		if (ufshcd_is_auto_hibern8_supported(hba) && hba->ahit)
112962306a36Sopenharmony_ci			ah_ms = FIELD_GET(UFSHCI_AHIBERN8_TIMER_MASK,
113062306a36Sopenharmony_ci					  hba->ahit);
113162306a36Sopenharmony_ci		else
113262306a36Sopenharmony_ci			ah_ms = 10;
113362306a36Sopenharmony_ci		ufshcd_clkgate_delay_set(hba->dev, ah_ms + 5);
113462306a36Sopenharmony_ci	}
113562306a36Sopenharmony_ci}
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_cistatic void ufs_mtk_post_link(struct ufs_hba *hba)
113862306a36Sopenharmony_ci{
113962306a36Sopenharmony_ci	/* enable unipro clock gating feature */
114062306a36Sopenharmony_ci	ufs_mtk_cfg_unipro_cg(hba, true);
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	/* will be configured during probe hba */
114362306a36Sopenharmony_ci	if (ufshcd_is_auto_hibern8_supported(hba))
114462306a36Sopenharmony_ci		hba->ahit = FIELD_PREP(UFSHCI_AHIBERN8_TIMER_MASK, 10) |
114562306a36Sopenharmony_ci			FIELD_PREP(UFSHCI_AHIBERN8_SCALE_MASK, 3);
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	ufs_mtk_setup_clk_gating(hba);
114862306a36Sopenharmony_ci}
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_cistatic int ufs_mtk_link_startup_notify(struct ufs_hba *hba,
115162306a36Sopenharmony_ci				       enum ufs_notify_change_status stage)
115262306a36Sopenharmony_ci{
115362306a36Sopenharmony_ci	int ret = 0;
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	switch (stage) {
115662306a36Sopenharmony_ci	case PRE_CHANGE:
115762306a36Sopenharmony_ci		ret = ufs_mtk_pre_link(hba);
115862306a36Sopenharmony_ci		break;
115962306a36Sopenharmony_ci	case POST_CHANGE:
116062306a36Sopenharmony_ci		ufs_mtk_post_link(hba);
116162306a36Sopenharmony_ci		break;
116262306a36Sopenharmony_ci	default:
116362306a36Sopenharmony_ci		ret = -EINVAL;
116462306a36Sopenharmony_ci		break;
116562306a36Sopenharmony_ci	}
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci	return ret;
116862306a36Sopenharmony_ci}
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_cistatic int ufs_mtk_device_reset(struct ufs_hba *hba)
117162306a36Sopenharmony_ci{
117262306a36Sopenharmony_ci	struct arm_smccc_res res;
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	/* disable hba before device reset */
117562306a36Sopenharmony_ci	ufshcd_hba_stop(hba);
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	ufs_mtk_device_reset_ctrl(0, res);
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	/*
118062306a36Sopenharmony_ci	 * The reset signal is active low. UFS devices shall detect
118162306a36Sopenharmony_ci	 * more than or equal to 1us of positive or negative RST_n
118262306a36Sopenharmony_ci	 * pulse width.
118362306a36Sopenharmony_ci	 *
118462306a36Sopenharmony_ci	 * To be on safe side, keep the reset low for at least 10us.
118562306a36Sopenharmony_ci	 */
118662306a36Sopenharmony_ci	usleep_range(10, 15);
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	ufs_mtk_device_reset_ctrl(1, res);
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	/* Some devices may need time to respond to rst_n */
119162306a36Sopenharmony_ci	usleep_range(10000, 15000);
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	dev_info(hba->dev, "device reset done\n");
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci	return 0;
119662306a36Sopenharmony_ci}
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_cistatic int ufs_mtk_link_set_hpm(struct ufs_hba *hba)
119962306a36Sopenharmony_ci{
120062306a36Sopenharmony_ci	int err;
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	err = ufshcd_hba_enable(hba);
120362306a36Sopenharmony_ci	if (err)
120462306a36Sopenharmony_ci		return err;
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci	err = ufs_mtk_unipro_set_lpm(hba, false);
120762306a36Sopenharmony_ci	if (err)
120862306a36Sopenharmony_ci		return err;
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	err = ufshcd_uic_hibern8_exit(hba);
121162306a36Sopenharmony_ci	if (!err)
121262306a36Sopenharmony_ci		ufshcd_set_link_active(hba);
121362306a36Sopenharmony_ci	else
121462306a36Sopenharmony_ci		return err;
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci	if (!hba->mcq_enabled) {
121762306a36Sopenharmony_ci		err = ufshcd_make_hba_operational(hba);
121862306a36Sopenharmony_ci	} else {
121962306a36Sopenharmony_ci		ufs_mtk_config_mcq(hba, false);
122062306a36Sopenharmony_ci		ufshcd_mcq_make_queues_operational(hba);
122162306a36Sopenharmony_ci		ufshcd_mcq_config_mac(hba, hba->nutrs);
122262306a36Sopenharmony_ci		/* Enable MCQ mode */
122362306a36Sopenharmony_ci		ufshcd_writel(hba, ufshcd_readl(hba, REG_UFS_MEM_CFG) | 0x1,
122462306a36Sopenharmony_ci			      REG_UFS_MEM_CFG);
122562306a36Sopenharmony_ci	}
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	if (err)
122862306a36Sopenharmony_ci		return err;
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	return 0;
123162306a36Sopenharmony_ci}
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_cistatic int ufs_mtk_link_set_lpm(struct ufs_hba *hba)
123462306a36Sopenharmony_ci{
123562306a36Sopenharmony_ci	int err;
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	/* Disable reset confirm feature by UniPro */
123862306a36Sopenharmony_ci	ufshcd_writel(hba,
123962306a36Sopenharmony_ci		      (ufshcd_readl(hba, REG_UFS_XOUFS_CTRL) & ~0x100),
124062306a36Sopenharmony_ci		      REG_UFS_XOUFS_CTRL);
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	err = ufs_mtk_unipro_set_lpm(hba, true);
124362306a36Sopenharmony_ci	if (err) {
124462306a36Sopenharmony_ci		/* Resume UniPro state for following error recovery */
124562306a36Sopenharmony_ci		ufs_mtk_unipro_set_lpm(hba, false);
124662306a36Sopenharmony_ci		return err;
124762306a36Sopenharmony_ci	}
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	return 0;
125062306a36Sopenharmony_ci}
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_cistatic void ufs_mtk_vccqx_set_lpm(struct ufs_hba *hba, bool lpm)
125362306a36Sopenharmony_ci{
125462306a36Sopenharmony_ci	struct ufs_vreg *vccqx = NULL;
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	if (hba->vreg_info.vccq)
125762306a36Sopenharmony_ci		vccqx = hba->vreg_info.vccq;
125862306a36Sopenharmony_ci	else
125962306a36Sopenharmony_ci		vccqx = hba->vreg_info.vccq2;
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci	regulator_set_mode(vccqx->reg,
126262306a36Sopenharmony_ci			   lpm ? REGULATOR_MODE_IDLE : REGULATOR_MODE_NORMAL);
126362306a36Sopenharmony_ci}
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_cistatic void ufs_mtk_vsx_set_lpm(struct ufs_hba *hba, bool lpm)
126662306a36Sopenharmony_ci{
126762306a36Sopenharmony_ci	struct arm_smccc_res res;
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci	ufs_mtk_device_pwr_ctrl(!lpm,
127062306a36Sopenharmony_ci				(unsigned long)hba->dev_info.wspecversion,
127162306a36Sopenharmony_ci				res);
127262306a36Sopenharmony_ci}
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_cistatic void ufs_mtk_dev_vreg_set_lpm(struct ufs_hba *hba, bool lpm)
127562306a36Sopenharmony_ci{
127662306a36Sopenharmony_ci	if (!hba->vreg_info.vccq && !hba->vreg_info.vccq2)
127762306a36Sopenharmony_ci		return;
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci	/* Skip if VCC is assumed always-on */
128062306a36Sopenharmony_ci	if (!hba->vreg_info.vcc)
128162306a36Sopenharmony_ci		return;
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	/* Bypass LPM when device is still active */
128462306a36Sopenharmony_ci	if (lpm && ufshcd_is_ufs_dev_active(hba))
128562306a36Sopenharmony_ci		return;
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci	/* Bypass LPM if VCC is enabled */
128862306a36Sopenharmony_ci	if (lpm && hba->vreg_info.vcc->enabled)
128962306a36Sopenharmony_ci		return;
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	if (lpm) {
129262306a36Sopenharmony_ci		ufs_mtk_vccqx_set_lpm(hba, lpm);
129362306a36Sopenharmony_ci		ufs_mtk_vsx_set_lpm(hba, lpm);
129462306a36Sopenharmony_ci	} else {
129562306a36Sopenharmony_ci		ufs_mtk_vsx_set_lpm(hba, lpm);
129662306a36Sopenharmony_ci		ufs_mtk_vccqx_set_lpm(hba, lpm);
129762306a36Sopenharmony_ci	}
129862306a36Sopenharmony_ci}
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_cistatic void ufs_mtk_auto_hibern8_disable(struct ufs_hba *hba)
130162306a36Sopenharmony_ci{
130262306a36Sopenharmony_ci	int ret;
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci	/* disable auto-hibern8 */
130562306a36Sopenharmony_ci	ufshcd_writel(hba, 0, REG_AUTO_HIBERNATE_IDLE_TIMER);
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	/* wait host return to idle state when auto-hibern8 off */
130862306a36Sopenharmony_ci	ufs_mtk_wait_idle_state(hba, 5);
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci	ret = ufs_mtk_wait_link_state(hba, VS_LINK_UP, 100);
131162306a36Sopenharmony_ci	if (ret)
131262306a36Sopenharmony_ci		dev_warn(hba->dev, "exit h8 state fail, ret=%d\n", ret);
131362306a36Sopenharmony_ci}
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_cistatic int ufs_mtk_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op,
131662306a36Sopenharmony_ci	enum ufs_notify_change_status status)
131762306a36Sopenharmony_ci{
131862306a36Sopenharmony_ci	int err;
131962306a36Sopenharmony_ci	struct arm_smccc_res res;
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	if (status == PRE_CHANGE) {
132262306a36Sopenharmony_ci		if (ufshcd_is_auto_hibern8_supported(hba))
132362306a36Sopenharmony_ci			ufs_mtk_auto_hibern8_disable(hba);
132462306a36Sopenharmony_ci		return 0;
132562306a36Sopenharmony_ci	}
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci	if (ufshcd_is_link_hibern8(hba)) {
132862306a36Sopenharmony_ci		err = ufs_mtk_link_set_lpm(hba);
132962306a36Sopenharmony_ci		if (err)
133062306a36Sopenharmony_ci			goto fail;
133162306a36Sopenharmony_ci	}
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	if (!ufshcd_is_link_active(hba)) {
133462306a36Sopenharmony_ci		/*
133562306a36Sopenharmony_ci		 * Make sure no error will be returned to prevent
133662306a36Sopenharmony_ci		 * ufshcd_suspend() re-enabling regulators while vreg is still
133762306a36Sopenharmony_ci		 * in low-power mode.
133862306a36Sopenharmony_ci		 */
133962306a36Sopenharmony_ci		err = ufs_mtk_mphy_power_on(hba, false);
134062306a36Sopenharmony_ci		if (err)
134162306a36Sopenharmony_ci			goto fail;
134262306a36Sopenharmony_ci	}
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	if (ufshcd_is_link_off(hba))
134562306a36Sopenharmony_ci		ufs_mtk_device_reset_ctrl(0, res);
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_ci	ufs_mtk_host_pwr_ctrl(HOST_PWR_HCI, false, res);
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci	return 0;
135062306a36Sopenharmony_cifail:
135162306a36Sopenharmony_ci	/*
135262306a36Sopenharmony_ci	 * Set link as off state enforcedly to trigger
135362306a36Sopenharmony_ci	 * ufshcd_host_reset_and_restore() in ufshcd_suspend()
135462306a36Sopenharmony_ci	 * for completed host reset.
135562306a36Sopenharmony_ci	 */
135662306a36Sopenharmony_ci	ufshcd_set_link_off(hba);
135762306a36Sopenharmony_ci	return -EAGAIN;
135862306a36Sopenharmony_ci}
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_cistatic int ufs_mtk_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
136162306a36Sopenharmony_ci{
136262306a36Sopenharmony_ci	int err;
136362306a36Sopenharmony_ci	struct arm_smccc_res res;
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_ci	if (hba->ufshcd_state != UFSHCD_STATE_OPERATIONAL)
136662306a36Sopenharmony_ci		ufs_mtk_dev_vreg_set_lpm(hba, false);
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci	ufs_mtk_host_pwr_ctrl(HOST_PWR_HCI, true, res);
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ci	err = ufs_mtk_mphy_power_on(hba, true);
137162306a36Sopenharmony_ci	if (err)
137262306a36Sopenharmony_ci		goto fail;
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	if (ufshcd_is_link_hibern8(hba)) {
137562306a36Sopenharmony_ci		err = ufs_mtk_link_set_hpm(hba);
137662306a36Sopenharmony_ci		if (err)
137762306a36Sopenharmony_ci			goto fail;
137862306a36Sopenharmony_ci	}
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci	return 0;
138162306a36Sopenharmony_cifail:
138262306a36Sopenharmony_ci	return ufshcd_link_recovery(hba);
138362306a36Sopenharmony_ci}
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_cistatic void ufs_mtk_dbg_register_dump(struct ufs_hba *hba)
138662306a36Sopenharmony_ci{
138762306a36Sopenharmony_ci	/* Dump ufshci register 0x140 ~ 0x14C */
138862306a36Sopenharmony_ci	ufshcd_dump_regs(hba, REG_UFS_XOUFS_CTRL, 0x10,
138962306a36Sopenharmony_ci			 "XOUFS Ctrl (0x140): ");
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci	ufshcd_dump_regs(hba, REG_UFS_EXTREG, 0x4, "Ext Reg ");
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	/* Dump ufshci register 0x2200 ~ 0x22AC */
139462306a36Sopenharmony_ci	ufshcd_dump_regs(hba, REG_UFS_MPHYCTRL,
139562306a36Sopenharmony_ci			 REG_UFS_REJECT_MON - REG_UFS_MPHYCTRL + 4,
139662306a36Sopenharmony_ci			 "MPHY Ctrl (0x2200): ");
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci	/* Direct debugging information to REG_MTK_PROBE */
139962306a36Sopenharmony_ci	ufs_mtk_dbg_sel(hba);
140062306a36Sopenharmony_ci	ufshcd_dump_regs(hba, REG_UFS_PROBE, 0x4, "Debug Probe ");
140162306a36Sopenharmony_ci}
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_cistatic int ufs_mtk_apply_dev_quirks(struct ufs_hba *hba)
140462306a36Sopenharmony_ci{
140562306a36Sopenharmony_ci	struct ufs_dev_info *dev_info = &hba->dev_info;
140662306a36Sopenharmony_ci	u16 mid = dev_info->wmanufacturerid;
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci	if (mid == UFS_VENDOR_SAMSUNG) {
140962306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TACTIVATE), 6);
141062306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_HIBERN8TIME), 10);
141162306a36Sopenharmony_ci	}
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci	/*
141462306a36Sopenharmony_ci	 * Decide waiting time before gating reference clock and
141562306a36Sopenharmony_ci	 * after ungating reference clock according to vendors'
141662306a36Sopenharmony_ci	 * requirements.
141762306a36Sopenharmony_ci	 */
141862306a36Sopenharmony_ci	if (mid == UFS_VENDOR_SAMSUNG)
141962306a36Sopenharmony_ci		ufs_mtk_setup_ref_clk_wait_us(hba, 1);
142062306a36Sopenharmony_ci	else if (mid == UFS_VENDOR_SKHYNIX)
142162306a36Sopenharmony_ci		ufs_mtk_setup_ref_clk_wait_us(hba, 30);
142262306a36Sopenharmony_ci	else if (mid == UFS_VENDOR_TOSHIBA)
142362306a36Sopenharmony_ci		ufs_mtk_setup_ref_clk_wait_us(hba, 100);
142462306a36Sopenharmony_ci	else
142562306a36Sopenharmony_ci		ufs_mtk_setup_ref_clk_wait_us(hba,
142662306a36Sopenharmony_ci					      REFCLK_DEFAULT_WAIT_US);
142762306a36Sopenharmony_ci	return 0;
142862306a36Sopenharmony_ci}
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_cistatic void ufs_mtk_fixup_dev_quirks(struct ufs_hba *hba)
143162306a36Sopenharmony_ci{
143262306a36Sopenharmony_ci	ufshcd_fixup_dev_quirks(hba, ufs_mtk_dev_fixups);
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci	if (ufs_mtk_is_broken_vcc(hba) && hba->vreg_info.vcc &&
143562306a36Sopenharmony_ci	    (hba->dev_quirks & UFS_DEVICE_QUIRK_DELAY_AFTER_LPM)) {
143662306a36Sopenharmony_ci		hba->vreg_info.vcc->always_on = true;
143762306a36Sopenharmony_ci		/*
143862306a36Sopenharmony_ci		 * VCC will be kept always-on thus we don't
143962306a36Sopenharmony_ci		 * need any delay during regulator operations
144062306a36Sopenharmony_ci		 */
144162306a36Sopenharmony_ci		hba->dev_quirks &= ~(UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM |
144262306a36Sopenharmony_ci			UFS_DEVICE_QUIRK_DELAY_AFTER_LPM);
144362306a36Sopenharmony_ci	}
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_ci	ufs_mtk_vreg_fix_vcc(hba);
144662306a36Sopenharmony_ci	ufs_mtk_vreg_fix_vccqx(hba);
144762306a36Sopenharmony_ci}
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_cistatic void ufs_mtk_event_notify(struct ufs_hba *hba,
145062306a36Sopenharmony_ci				 enum ufs_event_type evt, void *data)
145162306a36Sopenharmony_ci{
145262306a36Sopenharmony_ci	unsigned int val = *(u32 *)data;
145362306a36Sopenharmony_ci	unsigned long reg;
145462306a36Sopenharmony_ci	u8 bit;
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci	trace_ufs_mtk_event(evt, val);
145762306a36Sopenharmony_ci
145862306a36Sopenharmony_ci	/* Print details of UIC Errors */
145962306a36Sopenharmony_ci	if (evt <= UFS_EVT_DME_ERR) {
146062306a36Sopenharmony_ci		dev_info(hba->dev,
146162306a36Sopenharmony_ci			 "Host UIC Error Code (%s): %08x\n",
146262306a36Sopenharmony_ci			 ufs_uic_err_str[evt], val);
146362306a36Sopenharmony_ci		reg = val;
146462306a36Sopenharmony_ci	}
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	if (evt == UFS_EVT_PA_ERR) {
146762306a36Sopenharmony_ci		for_each_set_bit(bit, &reg, ARRAY_SIZE(ufs_uic_pa_err_str))
146862306a36Sopenharmony_ci			dev_info(hba->dev, "%s\n", ufs_uic_pa_err_str[bit]);
146962306a36Sopenharmony_ci	}
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci	if (evt == UFS_EVT_DL_ERR) {
147262306a36Sopenharmony_ci		for_each_set_bit(bit, &reg, ARRAY_SIZE(ufs_uic_dl_err_str))
147362306a36Sopenharmony_ci			dev_info(hba->dev, "%s\n", ufs_uic_dl_err_str[bit]);
147462306a36Sopenharmony_ci	}
147562306a36Sopenharmony_ci}
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_cistatic void ufs_mtk_config_scaling_param(struct ufs_hba *hba,
147862306a36Sopenharmony_ci				struct devfreq_dev_profile *profile,
147962306a36Sopenharmony_ci				struct devfreq_simple_ondemand_data *data)
148062306a36Sopenharmony_ci{
148162306a36Sopenharmony_ci	/* Customize min gear in clk scaling */
148262306a36Sopenharmony_ci	hba->clk_scaling.min_gear = UFS_HS_G4;
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci	hba->vps->devfreq_profile.polling_ms = 200;
148562306a36Sopenharmony_ci	hba->vps->ondemand_data.upthreshold = 50;
148662306a36Sopenharmony_ci	hba->vps->ondemand_data.downdifferential = 20;
148762306a36Sopenharmony_ci}
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci/**
149062306a36Sopenharmony_ci * ufs_mtk_clk_scale - Internal clk scaling operation
149162306a36Sopenharmony_ci *
149262306a36Sopenharmony_ci * MTK platform supports clk scaling by switching parent of ufs_sel(mux).
149362306a36Sopenharmony_ci * The ufs_sel downstream to ufs_ck which feeds directly to UFS hardware.
149462306a36Sopenharmony_ci * Max and min clocks rate of ufs_sel defined in dts should match rate of
149562306a36Sopenharmony_ci * "ufs_sel_max_src" and "ufs_sel_min_src" respectively.
149662306a36Sopenharmony_ci * This prevent changing rate of pll clock that is shared between modules.
149762306a36Sopenharmony_ci *
149862306a36Sopenharmony_ci * @hba: per adapter instance
149962306a36Sopenharmony_ci * @scale_up: True for scaling up and false for scaling down
150062306a36Sopenharmony_ci */
150162306a36Sopenharmony_cistatic void ufs_mtk_clk_scale(struct ufs_hba *hba, bool scale_up)
150262306a36Sopenharmony_ci{
150362306a36Sopenharmony_ci	struct ufs_mtk_host *host = ufshcd_get_variant(hba);
150462306a36Sopenharmony_ci	struct ufs_mtk_clk *mclk = &host->mclk;
150562306a36Sopenharmony_ci	struct ufs_clk_info *clki = mclk->ufs_sel_clki;
150662306a36Sopenharmony_ci	int ret = 0;
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci	ret = clk_prepare_enable(clki->clk);
150962306a36Sopenharmony_ci	if (ret) {
151062306a36Sopenharmony_ci		dev_info(hba->dev,
151162306a36Sopenharmony_ci			 "clk_prepare_enable() fail, ret: %d\n", ret);
151262306a36Sopenharmony_ci		return;
151362306a36Sopenharmony_ci	}
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ci	if (scale_up) {
151662306a36Sopenharmony_ci		ret = clk_set_parent(clki->clk, mclk->ufs_sel_max_clki->clk);
151762306a36Sopenharmony_ci		clki->curr_freq = clki->max_freq;
151862306a36Sopenharmony_ci	} else {
151962306a36Sopenharmony_ci		ret = clk_set_parent(clki->clk, mclk->ufs_sel_min_clki->clk);
152062306a36Sopenharmony_ci		clki->curr_freq = clki->min_freq;
152162306a36Sopenharmony_ci	}
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci	if (ret) {
152462306a36Sopenharmony_ci		dev_info(hba->dev,
152562306a36Sopenharmony_ci			 "Failed to set ufs_sel_clki, ret: %d\n", ret);
152662306a36Sopenharmony_ci	}
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci	clk_disable_unprepare(clki->clk);
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci	trace_ufs_mtk_clk_scale(clki->name, scale_up, clk_get_rate(clki->clk));
153162306a36Sopenharmony_ci}
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_cistatic int ufs_mtk_clk_scale_notify(struct ufs_hba *hba, bool scale_up,
153462306a36Sopenharmony_ci				    enum ufs_notify_change_status status)
153562306a36Sopenharmony_ci{
153662306a36Sopenharmony_ci	if (!ufshcd_is_clkscaling_supported(hba))
153762306a36Sopenharmony_ci		return 0;
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci	if (status == PRE_CHANGE) {
154062306a36Sopenharmony_ci		/* Switch parent before clk_set_rate() */
154162306a36Sopenharmony_ci		ufs_mtk_clk_scale(hba, scale_up);
154262306a36Sopenharmony_ci	} else {
154362306a36Sopenharmony_ci		/* Request interrupt latency QoS accordingly */
154462306a36Sopenharmony_ci		ufs_mtk_scale_perf(hba, scale_up);
154562306a36Sopenharmony_ci	}
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_ci	return 0;
154862306a36Sopenharmony_ci}
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_cistatic int ufs_mtk_get_hba_mac(struct ufs_hba *hba)
155162306a36Sopenharmony_ci{
155262306a36Sopenharmony_ci	return MAX_SUPP_MAC;
155362306a36Sopenharmony_ci}
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_cistatic int ufs_mtk_op_runtime_config(struct ufs_hba *hba)
155662306a36Sopenharmony_ci{
155762306a36Sopenharmony_ci	struct ufshcd_mcq_opr_info_t *opr;
155862306a36Sopenharmony_ci	int i;
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_ci	hba->mcq_opr[OPR_SQD].offset = REG_UFS_MTK_SQD;
156162306a36Sopenharmony_ci	hba->mcq_opr[OPR_SQIS].offset = REG_UFS_MTK_SQIS;
156262306a36Sopenharmony_ci	hba->mcq_opr[OPR_CQD].offset = REG_UFS_MTK_CQD;
156362306a36Sopenharmony_ci	hba->mcq_opr[OPR_CQIS].offset = REG_UFS_MTK_CQIS;
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_ci	for (i = 0; i < OPR_MAX; i++) {
156662306a36Sopenharmony_ci		opr = &hba->mcq_opr[i];
156762306a36Sopenharmony_ci		opr->stride = REG_UFS_MCQ_STRIDE;
156862306a36Sopenharmony_ci		opr->base = hba->mmio_base + opr->offset;
156962306a36Sopenharmony_ci	}
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci	return 0;
157262306a36Sopenharmony_ci}
157362306a36Sopenharmony_ci
157462306a36Sopenharmony_cistatic int ufs_mtk_mcq_config_resource(struct ufs_hba *hba)
157562306a36Sopenharmony_ci{
157662306a36Sopenharmony_ci	struct ufs_mtk_host *host = ufshcd_get_variant(hba);
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci	/* fail mcq initialization if interrupt is not filled properly */
157962306a36Sopenharmony_ci	if (!host->mcq_nr_intr) {
158062306a36Sopenharmony_ci		dev_info(hba->dev, "IRQs not ready. MCQ disabled.");
158162306a36Sopenharmony_ci		return -EINVAL;
158262306a36Sopenharmony_ci	}
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_ci	hba->mcq_base = hba->mmio_base + MCQ_QUEUE_OFFSET(hba->mcq_capabilities);
158562306a36Sopenharmony_ci	return 0;
158662306a36Sopenharmony_ci}
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_cistatic irqreturn_t ufs_mtk_mcq_intr(int irq, void *__intr_info)
158962306a36Sopenharmony_ci{
159062306a36Sopenharmony_ci	struct ufs_mtk_mcq_intr_info *mcq_intr_info = __intr_info;
159162306a36Sopenharmony_ci	struct ufs_hba *hba = mcq_intr_info->hba;
159262306a36Sopenharmony_ci	struct ufs_hw_queue *hwq;
159362306a36Sopenharmony_ci	u32 events;
159462306a36Sopenharmony_ci	int qid = mcq_intr_info->qid;
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci	hwq = &hba->uhq[qid];
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_ci	events = ufshcd_mcq_read_cqis(hba, qid);
159962306a36Sopenharmony_ci	if (events)
160062306a36Sopenharmony_ci		ufshcd_mcq_write_cqis(hba, events, qid);
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci	if (events & UFSHCD_MCQ_CQIS_TAIL_ENT_PUSH_STS)
160362306a36Sopenharmony_ci		ufshcd_mcq_poll_cqe_lock(hba, hwq);
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_ci	return IRQ_HANDLED;
160662306a36Sopenharmony_ci}
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_cistatic int ufs_mtk_config_mcq_irq(struct ufs_hba *hba)
160962306a36Sopenharmony_ci{
161062306a36Sopenharmony_ci	struct ufs_mtk_host *host = ufshcd_get_variant(hba);
161162306a36Sopenharmony_ci	u32 irq, i;
161262306a36Sopenharmony_ci	int ret;
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci	for (i = 0; i < host->mcq_nr_intr; i++) {
161562306a36Sopenharmony_ci		irq = host->mcq_intr_info[i].irq;
161662306a36Sopenharmony_ci		if (irq == MTK_MCQ_INVALID_IRQ) {
161762306a36Sopenharmony_ci			dev_err(hba->dev, "invalid irq. %d\n", i);
161862306a36Sopenharmony_ci			return -ENOPARAM;
161962306a36Sopenharmony_ci		}
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci		host->mcq_intr_info[i].qid = i;
162262306a36Sopenharmony_ci		ret = devm_request_irq(hba->dev, irq, ufs_mtk_mcq_intr, 0, UFSHCD,
162362306a36Sopenharmony_ci				       &host->mcq_intr_info[i]);
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci		dev_dbg(hba->dev, "request irq %d intr %s\n", irq, ret ? "failed" : "");
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci		if (ret) {
162862306a36Sopenharmony_ci			dev_err(hba->dev, "Cannot request irq %d\n", ret);
162962306a36Sopenharmony_ci			return ret;
163062306a36Sopenharmony_ci		}
163162306a36Sopenharmony_ci	}
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci	return 0;
163462306a36Sopenharmony_ci}
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_cistatic int ufs_mtk_config_mcq(struct ufs_hba *hba, bool irq)
163762306a36Sopenharmony_ci{
163862306a36Sopenharmony_ci	struct ufs_mtk_host *host = ufshcd_get_variant(hba);
163962306a36Sopenharmony_ci	int ret = 0;
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ci	if (!host->mcq_set_intr) {
164262306a36Sopenharmony_ci		/* Disable irq option register */
164362306a36Sopenharmony_ci		ufshcd_rmwl(hba, MCQ_INTR_EN_MSK, 0, REG_UFS_MMIO_OPT_CTRL_0);
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ci		if (irq) {
164662306a36Sopenharmony_ci			ret = ufs_mtk_config_mcq_irq(hba);
164762306a36Sopenharmony_ci			if (ret)
164862306a36Sopenharmony_ci				return ret;
164962306a36Sopenharmony_ci		}
165062306a36Sopenharmony_ci
165162306a36Sopenharmony_ci		host->mcq_set_intr = true;
165262306a36Sopenharmony_ci	}
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_ci	ufshcd_rmwl(hba, MCQ_AH8, MCQ_AH8, REG_UFS_MMIO_OPT_CTRL_0);
165562306a36Sopenharmony_ci	ufshcd_rmwl(hba, MCQ_INTR_EN_MSK, MCQ_MULTI_INTR_EN, REG_UFS_MMIO_OPT_CTRL_0);
165662306a36Sopenharmony_ci
165762306a36Sopenharmony_ci	return 0;
165862306a36Sopenharmony_ci}
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_cistatic int ufs_mtk_config_esi(struct ufs_hba *hba)
166162306a36Sopenharmony_ci{
166262306a36Sopenharmony_ci	return ufs_mtk_config_mcq(hba, true);
166362306a36Sopenharmony_ci}
166462306a36Sopenharmony_ci
166562306a36Sopenharmony_ci/*
166662306a36Sopenharmony_ci * struct ufs_hba_mtk_vops - UFS MTK specific variant operations
166762306a36Sopenharmony_ci *
166862306a36Sopenharmony_ci * The variant operations configure the necessary controller and PHY
166962306a36Sopenharmony_ci * handshake during initialization.
167062306a36Sopenharmony_ci */
167162306a36Sopenharmony_cistatic const struct ufs_hba_variant_ops ufs_hba_mtk_vops = {
167262306a36Sopenharmony_ci	.name                = "mediatek.ufshci",
167362306a36Sopenharmony_ci	.init                = ufs_mtk_init,
167462306a36Sopenharmony_ci	.get_ufs_hci_version = ufs_mtk_get_ufs_hci_version,
167562306a36Sopenharmony_ci	.setup_clocks        = ufs_mtk_setup_clocks,
167662306a36Sopenharmony_ci	.hce_enable_notify   = ufs_mtk_hce_enable_notify,
167762306a36Sopenharmony_ci	.link_startup_notify = ufs_mtk_link_startup_notify,
167862306a36Sopenharmony_ci	.pwr_change_notify   = ufs_mtk_pwr_change_notify,
167962306a36Sopenharmony_ci	.apply_dev_quirks    = ufs_mtk_apply_dev_quirks,
168062306a36Sopenharmony_ci	.fixup_dev_quirks    = ufs_mtk_fixup_dev_quirks,
168162306a36Sopenharmony_ci	.suspend             = ufs_mtk_suspend,
168262306a36Sopenharmony_ci	.resume              = ufs_mtk_resume,
168362306a36Sopenharmony_ci	.dbg_register_dump   = ufs_mtk_dbg_register_dump,
168462306a36Sopenharmony_ci	.device_reset        = ufs_mtk_device_reset,
168562306a36Sopenharmony_ci	.event_notify        = ufs_mtk_event_notify,
168662306a36Sopenharmony_ci	.config_scaling_param = ufs_mtk_config_scaling_param,
168762306a36Sopenharmony_ci	.clk_scale_notify    = ufs_mtk_clk_scale_notify,
168862306a36Sopenharmony_ci	/* mcq vops */
168962306a36Sopenharmony_ci	.get_hba_mac         = ufs_mtk_get_hba_mac,
169062306a36Sopenharmony_ci	.op_runtime_config   = ufs_mtk_op_runtime_config,
169162306a36Sopenharmony_ci	.mcq_config_resource = ufs_mtk_mcq_config_resource,
169262306a36Sopenharmony_ci	.config_esi          = ufs_mtk_config_esi,
169362306a36Sopenharmony_ci};
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_ci/**
169662306a36Sopenharmony_ci * ufs_mtk_probe - probe routine of the driver
169762306a36Sopenharmony_ci * @pdev: pointer to Platform device handle
169862306a36Sopenharmony_ci *
169962306a36Sopenharmony_ci * Return: zero for success and non-zero for failure.
170062306a36Sopenharmony_ci */
170162306a36Sopenharmony_cistatic int ufs_mtk_probe(struct platform_device *pdev)
170262306a36Sopenharmony_ci{
170362306a36Sopenharmony_ci	int err;
170462306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
170562306a36Sopenharmony_ci	struct device_node *reset_node;
170662306a36Sopenharmony_ci	struct platform_device *reset_pdev;
170762306a36Sopenharmony_ci	struct device_link *link;
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_ci	reset_node = of_find_compatible_node(NULL, NULL,
171062306a36Sopenharmony_ci					     "ti,syscon-reset");
171162306a36Sopenharmony_ci	if (!reset_node) {
171262306a36Sopenharmony_ci		dev_notice(dev, "find ti,syscon-reset fail\n");
171362306a36Sopenharmony_ci		goto skip_reset;
171462306a36Sopenharmony_ci	}
171562306a36Sopenharmony_ci	reset_pdev = of_find_device_by_node(reset_node);
171662306a36Sopenharmony_ci	if (!reset_pdev) {
171762306a36Sopenharmony_ci		dev_notice(dev, "find reset_pdev fail\n");
171862306a36Sopenharmony_ci		goto skip_reset;
171962306a36Sopenharmony_ci	}
172062306a36Sopenharmony_ci	link = device_link_add(dev, &reset_pdev->dev,
172162306a36Sopenharmony_ci		DL_FLAG_AUTOPROBE_CONSUMER);
172262306a36Sopenharmony_ci	put_device(&reset_pdev->dev);
172362306a36Sopenharmony_ci	if (!link) {
172462306a36Sopenharmony_ci		dev_notice(dev, "add reset device_link fail\n");
172562306a36Sopenharmony_ci		goto skip_reset;
172662306a36Sopenharmony_ci	}
172762306a36Sopenharmony_ci	/* supplier is not probed */
172862306a36Sopenharmony_ci	if (link->status == DL_STATE_DORMANT) {
172962306a36Sopenharmony_ci		err = -EPROBE_DEFER;
173062306a36Sopenharmony_ci		goto out;
173162306a36Sopenharmony_ci	}
173262306a36Sopenharmony_ci
173362306a36Sopenharmony_ciskip_reset:
173462306a36Sopenharmony_ci	/* perform generic probe */
173562306a36Sopenharmony_ci	err = ufshcd_pltfrm_init(pdev, &ufs_hba_mtk_vops);
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_ciout:
173862306a36Sopenharmony_ci	if (err)
173962306a36Sopenharmony_ci		dev_err(dev, "probe failed %d\n", err);
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_ci	of_node_put(reset_node);
174262306a36Sopenharmony_ci	return err;
174362306a36Sopenharmony_ci}
174462306a36Sopenharmony_ci
174562306a36Sopenharmony_ci/**
174662306a36Sopenharmony_ci * ufs_mtk_remove - set driver_data of the device to NULL
174762306a36Sopenharmony_ci * @pdev: pointer to platform device handle
174862306a36Sopenharmony_ci *
174962306a36Sopenharmony_ci * Always return 0
175062306a36Sopenharmony_ci */
175162306a36Sopenharmony_cistatic int ufs_mtk_remove(struct platform_device *pdev)
175262306a36Sopenharmony_ci{
175362306a36Sopenharmony_ci	struct ufs_hba *hba =  platform_get_drvdata(pdev);
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_ci	pm_runtime_get_sync(&(pdev)->dev);
175662306a36Sopenharmony_ci	ufshcd_remove(hba);
175762306a36Sopenharmony_ci	return 0;
175862306a36Sopenharmony_ci}
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
176162306a36Sopenharmony_cistatic int ufs_mtk_system_suspend(struct device *dev)
176262306a36Sopenharmony_ci{
176362306a36Sopenharmony_ci	struct ufs_hba *hba = dev_get_drvdata(dev);
176462306a36Sopenharmony_ci	int ret;
176562306a36Sopenharmony_ci
176662306a36Sopenharmony_ci	ret = ufshcd_system_suspend(dev);
176762306a36Sopenharmony_ci	if (ret)
176862306a36Sopenharmony_ci		return ret;
176962306a36Sopenharmony_ci
177062306a36Sopenharmony_ci	ufs_mtk_dev_vreg_set_lpm(hba, true);
177162306a36Sopenharmony_ci
177262306a36Sopenharmony_ci	return 0;
177362306a36Sopenharmony_ci}
177462306a36Sopenharmony_ci
177562306a36Sopenharmony_cistatic int ufs_mtk_system_resume(struct device *dev)
177662306a36Sopenharmony_ci{
177762306a36Sopenharmony_ci	struct ufs_hba *hba = dev_get_drvdata(dev);
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_ci	ufs_mtk_dev_vreg_set_lpm(hba, false);
178062306a36Sopenharmony_ci
178162306a36Sopenharmony_ci	return ufshcd_system_resume(dev);
178262306a36Sopenharmony_ci}
178362306a36Sopenharmony_ci#endif
178462306a36Sopenharmony_ci
178562306a36Sopenharmony_ci#ifdef CONFIG_PM
178662306a36Sopenharmony_cistatic int ufs_mtk_runtime_suspend(struct device *dev)
178762306a36Sopenharmony_ci{
178862306a36Sopenharmony_ci	struct ufs_hba *hba = dev_get_drvdata(dev);
178962306a36Sopenharmony_ci	int ret = 0;
179062306a36Sopenharmony_ci
179162306a36Sopenharmony_ci	ret = ufshcd_runtime_suspend(dev);
179262306a36Sopenharmony_ci	if (ret)
179362306a36Sopenharmony_ci		return ret;
179462306a36Sopenharmony_ci
179562306a36Sopenharmony_ci	ufs_mtk_dev_vreg_set_lpm(hba, true);
179662306a36Sopenharmony_ci
179762306a36Sopenharmony_ci	return 0;
179862306a36Sopenharmony_ci}
179962306a36Sopenharmony_ci
180062306a36Sopenharmony_cistatic int ufs_mtk_runtime_resume(struct device *dev)
180162306a36Sopenharmony_ci{
180262306a36Sopenharmony_ci	struct ufs_hba *hba = dev_get_drvdata(dev);
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_ci	ufs_mtk_dev_vreg_set_lpm(hba, false);
180562306a36Sopenharmony_ci
180662306a36Sopenharmony_ci	return ufshcd_runtime_resume(dev);
180762306a36Sopenharmony_ci}
180862306a36Sopenharmony_ci#endif
180962306a36Sopenharmony_ci
181062306a36Sopenharmony_cistatic const struct dev_pm_ops ufs_mtk_pm_ops = {
181162306a36Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(ufs_mtk_system_suspend,
181262306a36Sopenharmony_ci				ufs_mtk_system_resume)
181362306a36Sopenharmony_ci	SET_RUNTIME_PM_OPS(ufs_mtk_runtime_suspend,
181462306a36Sopenharmony_ci			   ufs_mtk_runtime_resume, NULL)
181562306a36Sopenharmony_ci	.prepare	 = ufshcd_suspend_prepare,
181662306a36Sopenharmony_ci	.complete	 = ufshcd_resume_complete,
181762306a36Sopenharmony_ci};
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_cistatic struct platform_driver ufs_mtk_pltform = {
182062306a36Sopenharmony_ci	.probe      = ufs_mtk_probe,
182162306a36Sopenharmony_ci	.remove     = ufs_mtk_remove,
182262306a36Sopenharmony_ci	.driver = {
182362306a36Sopenharmony_ci		.name   = "ufshcd-mtk",
182462306a36Sopenharmony_ci		.pm     = &ufs_mtk_pm_ops,
182562306a36Sopenharmony_ci		.of_match_table = ufs_mtk_of_match,
182662306a36Sopenharmony_ci	},
182762306a36Sopenharmony_ci};
182862306a36Sopenharmony_ci
182962306a36Sopenharmony_ciMODULE_AUTHOR("Stanley Chu <stanley.chu@mediatek.com>");
183062306a36Sopenharmony_ciMODULE_AUTHOR("Peter Wang <peter.wang@mediatek.com>");
183162306a36Sopenharmony_ciMODULE_DESCRIPTION("MediaTek UFS Host Driver");
183262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_cimodule_platform_driver(ufs_mtk_pltform);
1835