162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * UFS Host Controller driver for Exynos specific extensions
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2014-2015 Samsung Electronics Co., Ltd.
662306a36Sopenharmony_ci * Author: Seungwon Jeon  <essuuj@gmail.com>
762306a36Sopenharmony_ci * Author: Alim Akhtar <alim.akhtar@samsung.com>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
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/mfd/syscon.h>
1762306a36Sopenharmony_ci#include <linux/phy/phy.h>
1862306a36Sopenharmony_ci#include <linux/platform_device.h>
1962306a36Sopenharmony_ci#include <linux/regmap.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include <ufs/ufshcd.h>
2262306a36Sopenharmony_ci#include "ufshcd-pltfrm.h"
2362306a36Sopenharmony_ci#include <ufs/ufshci.h>
2462306a36Sopenharmony_ci#include <ufs/unipro.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include "ufs-exynos.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/*
2962306a36Sopenharmony_ci * Exynos's Vendor specific registers for UFSHCI
3062306a36Sopenharmony_ci */
3162306a36Sopenharmony_ci#define HCI_TXPRDT_ENTRY_SIZE	0x00
3262306a36Sopenharmony_ci#define PRDT_PREFECT_EN		BIT(31)
3362306a36Sopenharmony_ci#define PRDT_SET_SIZE(x)	((x) & 0x1F)
3462306a36Sopenharmony_ci#define HCI_RXPRDT_ENTRY_SIZE	0x04
3562306a36Sopenharmony_ci#define HCI_1US_TO_CNT_VAL	0x0C
3662306a36Sopenharmony_ci#define CNT_VAL_1US_MASK	0x3FF
3762306a36Sopenharmony_ci#define HCI_UTRL_NEXUS_TYPE	0x40
3862306a36Sopenharmony_ci#define HCI_UTMRL_NEXUS_TYPE	0x44
3962306a36Sopenharmony_ci#define HCI_SW_RST		0x50
4062306a36Sopenharmony_ci#define UFS_LINK_SW_RST		BIT(0)
4162306a36Sopenharmony_ci#define UFS_UNIPRO_SW_RST	BIT(1)
4262306a36Sopenharmony_ci#define UFS_SW_RST_MASK		(UFS_UNIPRO_SW_RST | UFS_LINK_SW_RST)
4362306a36Sopenharmony_ci#define HCI_DATA_REORDER	0x60
4462306a36Sopenharmony_ci#define HCI_UNIPRO_APB_CLK_CTRL	0x68
4562306a36Sopenharmony_ci#define UNIPRO_APB_CLK(v, x)	(((v) & ~0xF) | ((x) & 0xF))
4662306a36Sopenharmony_ci#define HCI_AXIDMA_RWDATA_BURST_LEN	0x6C
4762306a36Sopenharmony_ci#define HCI_GPIO_OUT		0x70
4862306a36Sopenharmony_ci#define HCI_ERR_EN_PA_LAYER	0x78
4962306a36Sopenharmony_ci#define HCI_ERR_EN_DL_LAYER	0x7C
5062306a36Sopenharmony_ci#define HCI_ERR_EN_N_LAYER	0x80
5162306a36Sopenharmony_ci#define HCI_ERR_EN_T_LAYER	0x84
5262306a36Sopenharmony_ci#define HCI_ERR_EN_DME_LAYER	0x88
5362306a36Sopenharmony_ci#define HCI_CLKSTOP_CTRL	0xB0
5462306a36Sopenharmony_ci#define REFCLKOUT_STOP		BIT(4)
5562306a36Sopenharmony_ci#define MPHY_APBCLK_STOP	BIT(3)
5662306a36Sopenharmony_ci#define REFCLK_STOP		BIT(2)
5762306a36Sopenharmony_ci#define UNIPRO_MCLK_STOP	BIT(1)
5862306a36Sopenharmony_ci#define UNIPRO_PCLK_STOP	BIT(0)
5962306a36Sopenharmony_ci#define CLK_STOP_MASK		(REFCLKOUT_STOP | REFCLK_STOP |\
6062306a36Sopenharmony_ci				 UNIPRO_MCLK_STOP | MPHY_APBCLK_STOP|\
6162306a36Sopenharmony_ci				 UNIPRO_PCLK_STOP)
6262306a36Sopenharmony_ci#define HCI_MISC		0xB4
6362306a36Sopenharmony_ci#define REFCLK_CTRL_EN		BIT(7)
6462306a36Sopenharmony_ci#define UNIPRO_PCLK_CTRL_EN	BIT(6)
6562306a36Sopenharmony_ci#define UNIPRO_MCLK_CTRL_EN	BIT(5)
6662306a36Sopenharmony_ci#define HCI_CORECLK_CTRL_EN	BIT(4)
6762306a36Sopenharmony_ci#define CLK_CTRL_EN_MASK	(REFCLK_CTRL_EN |\
6862306a36Sopenharmony_ci				 UNIPRO_PCLK_CTRL_EN |\
6962306a36Sopenharmony_ci				 UNIPRO_MCLK_CTRL_EN)
7062306a36Sopenharmony_ci/* Device fatal error */
7162306a36Sopenharmony_ci#define DFES_ERR_EN		BIT(31)
7262306a36Sopenharmony_ci#define DFES_DEF_L2_ERRS	(UIC_DATA_LINK_LAYER_ERROR_RX_BUF_OF |\
7362306a36Sopenharmony_ci				 UIC_DATA_LINK_LAYER_ERROR_PA_INIT)
7462306a36Sopenharmony_ci#define DFES_DEF_L3_ERRS	(UIC_NETWORK_UNSUPPORTED_HEADER_TYPE |\
7562306a36Sopenharmony_ci				 UIC_NETWORK_BAD_DEVICEID_ENC |\
7662306a36Sopenharmony_ci				 UIC_NETWORK_LHDR_TRAP_PACKET_DROPPING)
7762306a36Sopenharmony_ci#define DFES_DEF_L4_ERRS	(UIC_TRANSPORT_UNSUPPORTED_HEADER_TYPE |\
7862306a36Sopenharmony_ci				 UIC_TRANSPORT_UNKNOWN_CPORTID |\
7962306a36Sopenharmony_ci				 UIC_TRANSPORT_NO_CONNECTION_RX |\
8062306a36Sopenharmony_ci				 UIC_TRANSPORT_BAD_TC)
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci/* FSYS UFS Shareability */
8362306a36Sopenharmony_ci#define UFS_WR_SHARABLE		BIT(2)
8462306a36Sopenharmony_ci#define UFS_RD_SHARABLE		BIT(1)
8562306a36Sopenharmony_ci#define UFS_SHARABLE		(UFS_WR_SHARABLE | UFS_RD_SHARABLE)
8662306a36Sopenharmony_ci#define UFS_SHAREABILITY_OFFSET	0x710
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci/* Multi-host registers */
8962306a36Sopenharmony_ci#define MHCTRL			0xC4
9062306a36Sopenharmony_ci#define MHCTRL_EN_VH_MASK	(0xE)
9162306a36Sopenharmony_ci#define MHCTRL_EN_VH(vh)	(vh << 1)
9262306a36Sopenharmony_ci#define PH2VH_MBOX		0xD8
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci#define MH_MSG_MASK		(0xFF)
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci#define MH_MSG(id, msg)		((id << 8) | (msg & 0xFF))
9762306a36Sopenharmony_ci#define MH_MSG_PH_READY		0x1
9862306a36Sopenharmony_ci#define MH_MSG_VH_READY		0x2
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci#define ALLOW_INQUIRY		BIT(25)
10162306a36Sopenharmony_ci#define ALLOW_MODE_SELECT	BIT(24)
10262306a36Sopenharmony_ci#define ALLOW_MODE_SENSE	BIT(23)
10362306a36Sopenharmony_ci#define ALLOW_PRE_FETCH		GENMASK(22, 21)
10462306a36Sopenharmony_ci#define ALLOW_READ_CMD_ALL	GENMASK(20, 18)	/* read_6/10/16 */
10562306a36Sopenharmony_ci#define ALLOW_READ_BUFFER	BIT(17)
10662306a36Sopenharmony_ci#define ALLOW_READ_CAPACITY	GENMASK(16, 15)
10762306a36Sopenharmony_ci#define ALLOW_REPORT_LUNS	BIT(14)
10862306a36Sopenharmony_ci#define ALLOW_REQUEST_SENSE	BIT(13)
10962306a36Sopenharmony_ci#define ALLOW_SYNCHRONIZE_CACHE	GENMASK(8, 7)
11062306a36Sopenharmony_ci#define ALLOW_TEST_UNIT_READY	BIT(6)
11162306a36Sopenharmony_ci#define ALLOW_UNMAP		BIT(5)
11262306a36Sopenharmony_ci#define ALLOW_VERIFY		BIT(4)
11362306a36Sopenharmony_ci#define ALLOW_WRITE_CMD_ALL	GENMASK(3, 1)	/* write_6/10/16 */
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci#define ALLOW_TRANS_VH_DEFAULT	(ALLOW_INQUIRY | ALLOW_MODE_SELECT | \
11662306a36Sopenharmony_ci				 ALLOW_MODE_SENSE | ALLOW_PRE_FETCH | \
11762306a36Sopenharmony_ci				 ALLOW_READ_CMD_ALL | ALLOW_READ_BUFFER | \
11862306a36Sopenharmony_ci				 ALLOW_READ_CAPACITY | ALLOW_REPORT_LUNS | \
11962306a36Sopenharmony_ci				 ALLOW_REQUEST_SENSE | ALLOW_SYNCHRONIZE_CACHE | \
12062306a36Sopenharmony_ci				 ALLOW_TEST_UNIT_READY | ALLOW_UNMAP | \
12162306a36Sopenharmony_ci				 ALLOW_VERIFY | ALLOW_WRITE_CMD_ALL)
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci#define HCI_MH_ALLOWABLE_TRAN_OF_VH		0x30C
12462306a36Sopenharmony_ci#define HCI_MH_IID_IN_TASK_TAG			0X308
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci#define PH_READY_TIMEOUT_MS			(5 * MSEC_PER_SEC)
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cienum {
12962306a36Sopenharmony_ci	UNIPRO_L1_5 = 0,/* PHY Adapter */
13062306a36Sopenharmony_ci	UNIPRO_L2,	/* Data Link */
13162306a36Sopenharmony_ci	UNIPRO_L3,	/* Network */
13262306a36Sopenharmony_ci	UNIPRO_L4,	/* Transport */
13362306a36Sopenharmony_ci	UNIPRO_DME,	/* DME */
13462306a36Sopenharmony_ci};
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci/*
13762306a36Sopenharmony_ci * UNIPRO registers
13862306a36Sopenharmony_ci */
13962306a36Sopenharmony_ci#define UNIPRO_DME_POWERMODE_REQ_REMOTEL2TIMER0	0x78B8
14062306a36Sopenharmony_ci#define UNIPRO_DME_POWERMODE_REQ_REMOTEL2TIMER1	0x78BC
14162306a36Sopenharmony_ci#define UNIPRO_DME_POWERMODE_REQ_REMOTEL2TIMER2	0x78C0
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci/*
14462306a36Sopenharmony_ci * UFS Protector registers
14562306a36Sopenharmony_ci */
14662306a36Sopenharmony_ci#define UFSPRSECURITY	0x010
14762306a36Sopenharmony_ci#define NSSMU		BIT(14)
14862306a36Sopenharmony_ci#define UFSPSBEGIN0	0x200
14962306a36Sopenharmony_ci#define UFSPSEND0	0x204
15062306a36Sopenharmony_ci#define UFSPSLUN0	0x208
15162306a36Sopenharmony_ci#define UFSPSCTRL0	0x20C
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci#define CNTR_DIV_VAL 40
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic void exynos_ufs_auto_ctrl_hcc(struct exynos_ufs *ufs, bool en);
15662306a36Sopenharmony_cistatic void exynos_ufs_ctrl_clkstop(struct exynos_ufs *ufs, bool en);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic inline void exynos_ufs_enable_auto_ctrl_hcc(struct exynos_ufs *ufs)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	exynos_ufs_auto_ctrl_hcc(ufs, true);
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic inline void exynos_ufs_disable_auto_ctrl_hcc(struct exynos_ufs *ufs)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	exynos_ufs_auto_ctrl_hcc(ufs, false);
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic inline void exynos_ufs_disable_auto_ctrl_hcc_save(
16962306a36Sopenharmony_ci					struct exynos_ufs *ufs, u32 *val)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	*val = hci_readl(ufs, HCI_MISC);
17262306a36Sopenharmony_ci	exynos_ufs_auto_ctrl_hcc(ufs, false);
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic inline void exynos_ufs_auto_ctrl_hcc_restore(
17662306a36Sopenharmony_ci					struct exynos_ufs *ufs, u32 *val)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	hci_writel(ufs, *val, HCI_MISC);
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic inline void exynos_ufs_gate_clks(struct exynos_ufs *ufs)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	exynos_ufs_ctrl_clkstop(ufs, true);
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic inline void exynos_ufs_ungate_clks(struct exynos_ufs *ufs)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	exynos_ufs_ctrl_clkstop(ufs, false);
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic int exynos7_ufs_drv_init(struct device *dev, struct exynos_ufs *ufs)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	return 0;
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic int exynosauto_ufs_drv_init(struct device *dev, struct exynos_ufs *ufs)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	struct exynos_ufs_uic_attr *attr = ufs->drv_data->uic_attr;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	/* IO Coherency setting */
20162306a36Sopenharmony_ci	if (ufs->sysreg) {
20262306a36Sopenharmony_ci		return regmap_update_bits(ufs->sysreg,
20362306a36Sopenharmony_ci					  ufs->shareability_reg_offset,
20462306a36Sopenharmony_ci					  UFS_SHARABLE, UFS_SHARABLE);
20562306a36Sopenharmony_ci	}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	attr->tx_dif_p_nsec = 3200000;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	return 0;
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic int exynosauto_ufs_post_hce_enable(struct exynos_ufs *ufs)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	struct ufs_hba *hba = ufs->hba;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	/* Enable Virtual Host #1 */
21762306a36Sopenharmony_ci	ufshcd_rmwl(hba, MHCTRL_EN_VH_MASK, MHCTRL_EN_VH(1), MHCTRL);
21862306a36Sopenharmony_ci	/* Default VH Transfer permissions */
21962306a36Sopenharmony_ci	hci_writel(ufs, ALLOW_TRANS_VH_DEFAULT, HCI_MH_ALLOWABLE_TRAN_OF_VH);
22062306a36Sopenharmony_ci	/* IID information is replaced in TASKTAG[7:5] instead of IID in UCD */
22162306a36Sopenharmony_ci	hci_writel(ufs, 0x1, HCI_MH_IID_IN_TASK_TAG);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	return 0;
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic int exynosauto_ufs_pre_link(struct exynos_ufs *ufs)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	struct ufs_hba *hba = ufs->hba;
22962306a36Sopenharmony_ci	int i;
23062306a36Sopenharmony_ci	u32 tx_line_reset_period, rx_line_reset_period;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	rx_line_reset_period = (RX_LINE_RESET_TIME * ufs->mclk_rate) / NSEC_PER_MSEC;
23362306a36Sopenharmony_ci	tx_line_reset_period = (TX_LINE_RESET_TIME * ufs->mclk_rate) / NSEC_PER_MSEC;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(0x200), 0x40);
23662306a36Sopenharmony_ci	for_each_ufs_rx_lane(ufs, i) {
23762306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_CLK_PRD, i),
23862306a36Sopenharmony_ci			       DIV_ROUND_UP(NSEC_PER_SEC, ufs->mclk_rate));
23962306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_CLK_PRD_EN, i), 0x0);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_LINERESET_VALUE2, i),
24262306a36Sopenharmony_ci			       (rx_line_reset_period >> 16) & 0xFF);
24362306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_LINERESET_VALUE1, i),
24462306a36Sopenharmony_ci			       (rx_line_reset_period >> 8) & 0xFF);
24562306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_LINERESET_VALUE0, i),
24662306a36Sopenharmony_ci			       (rx_line_reset_period) & 0xFF);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x2f, i), 0x79);
24962306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x84, i), 0x1);
25062306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x25, i), 0xf6);
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	for_each_ufs_tx_lane(ufs, i) {
25462306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_CLK_PRD, i),
25562306a36Sopenharmony_ci			       DIV_ROUND_UP(NSEC_PER_SEC, ufs->mclk_rate));
25662306a36Sopenharmony_ci		/* Not to affect VND_TX_LINERESET_PVALUE to VND_TX_CLK_PRD */
25762306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_CLK_PRD_EN, i),
25862306a36Sopenharmony_ci			       0x02);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_LINERESET_PVALUE2, i),
26162306a36Sopenharmony_ci			       (tx_line_reset_period >> 16) & 0xFF);
26262306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_LINERESET_PVALUE1, i),
26362306a36Sopenharmony_ci			       (tx_line_reset_period >> 8) & 0xFF);
26462306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_LINERESET_PVALUE0, i),
26562306a36Sopenharmony_ci			       (tx_line_reset_period) & 0xFF);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci		/* TX PWM Gear Capability / PWM_G1_ONLY */
26862306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x04, i), 0x1);
26962306a36Sopenharmony_ci	}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(0x200), 0x0);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_LOCAL_TX_LCC_ENABLE), 0x0);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(0xa011), 0x8000);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	return 0;
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cistatic int exynosauto_ufs_pre_pwr_change(struct exynos_ufs *ufs,
28162306a36Sopenharmony_ci					 struct ufs_pa_layer_attr *pwr)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	struct ufs_hba *hba = ufs->hba;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	/* PACP_PWR_req and delivered to the remote DME */
28662306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA0), 12000);
28762306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA1), 32000);
28862306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA2), 16000);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	return 0;
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cistatic int exynosauto_ufs_post_pwr_change(struct exynos_ufs *ufs,
29462306a36Sopenharmony_ci					  struct ufs_pa_layer_attr *pwr)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci	struct ufs_hba *hba = ufs->hba;
29762306a36Sopenharmony_ci	u32 enabled_vh;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	enabled_vh = ufshcd_readl(hba, MHCTRL) & MHCTRL_EN_VH_MASK;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	/* Send physical host ready message to virtual hosts */
30262306a36Sopenharmony_ci	ufshcd_writel(hba, MH_MSG(enabled_vh, MH_MSG_PH_READY), PH2VH_MBOX);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	return 0;
30562306a36Sopenharmony_ci}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_cistatic int exynos7_ufs_pre_link(struct exynos_ufs *ufs)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	struct ufs_hba *hba = ufs->hba;
31062306a36Sopenharmony_ci	u32 val = ufs->drv_data->uic_attr->pa_dbg_option_suite;
31162306a36Sopenharmony_ci	int i;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	exynos_ufs_enable_ov_tm(hba);
31462306a36Sopenharmony_ci	for_each_ufs_tx_lane(ufs, i)
31562306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x297, i), 0x17);
31662306a36Sopenharmony_ci	for_each_ufs_rx_lane(ufs, i) {
31762306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x362, i), 0xff);
31862306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x363, i), 0x00);
31962306a36Sopenharmony_ci	}
32062306a36Sopenharmony_ci	exynos_ufs_disable_ov_tm(hba);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	for_each_ufs_tx_lane(ufs, i)
32362306a36Sopenharmony_ci		ufshcd_dme_set(hba,
32462306a36Sopenharmony_ci			UIC_ARG_MIB_SEL(TX_HIBERN8_CONTROL, i), 0x0);
32562306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_DBG_TXPHY_CFGUPDT), 0x1);
32662306a36Sopenharmony_ci	udelay(1);
32762306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_DBG_OPTION_SUITE), val | (1 << 12));
32862306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_DBG_SKIP_RESET_PHY), 0x1);
32962306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_DBG_SKIP_LINE_RESET), 0x1);
33062306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_DBG_LINE_RESET_REQ), 0x1);
33162306a36Sopenharmony_ci	udelay(1600);
33262306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_DBG_OPTION_SUITE), val);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	return 0;
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic int exynos7_ufs_post_link(struct exynos_ufs *ufs)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	struct ufs_hba *hba = ufs->hba;
34062306a36Sopenharmony_ci	int i;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	exynos_ufs_enable_ov_tm(hba);
34362306a36Sopenharmony_ci	for_each_ufs_tx_lane(ufs, i) {
34462306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x28b, i), 0x83);
34562306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x29a, i), 0x07);
34662306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x277, i),
34762306a36Sopenharmony_ci			TX_LINERESET_N(exynos_ufs_calc_time_cntr(ufs, 200000)));
34862306a36Sopenharmony_ci	}
34962306a36Sopenharmony_ci	exynos_ufs_disable_ov_tm(hba);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	exynos_ufs_enable_dbg_mode(hba);
35262306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_SAVECONFIGTIME), 0xbb8);
35362306a36Sopenharmony_ci	exynos_ufs_disable_dbg_mode(hba);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	return 0;
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_cistatic int exynos7_ufs_pre_pwr_change(struct exynos_ufs *ufs,
35962306a36Sopenharmony_ci						struct ufs_pa_layer_attr *pwr)
36062306a36Sopenharmony_ci{
36162306a36Sopenharmony_ci	unipro_writel(ufs, 0x22, UNIPRO_DBG_FORCE_DME_CTRL_STATE);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	return 0;
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_cistatic int exynos7_ufs_post_pwr_change(struct exynos_ufs *ufs,
36762306a36Sopenharmony_ci						struct ufs_pa_layer_attr *pwr)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	struct ufs_hba *hba = ufs->hba;
37062306a36Sopenharmony_ci	int lanes = max_t(u32, pwr->lane_rx, pwr->lane_tx);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_DBG_RXPHY_CFGUPDT), 0x1);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	if (lanes == 1) {
37562306a36Sopenharmony_ci		exynos_ufs_enable_dbg_mode(hba);
37662306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_CONNECTEDTXDATALANES), 0x1);
37762306a36Sopenharmony_ci		exynos_ufs_disable_dbg_mode(hba);
37862306a36Sopenharmony_ci	}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	return 0;
38162306a36Sopenharmony_ci}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci/*
38462306a36Sopenharmony_ci * exynos_ufs_auto_ctrl_hcc - HCI core clock control by h/w
38562306a36Sopenharmony_ci * Control should be disabled in the below cases
38662306a36Sopenharmony_ci * - Before host controller S/W reset
38762306a36Sopenharmony_ci * - Access to UFS protector's register
38862306a36Sopenharmony_ci */
38962306a36Sopenharmony_cistatic void exynos_ufs_auto_ctrl_hcc(struct exynos_ufs *ufs, bool en)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci	u32 misc = hci_readl(ufs, HCI_MISC);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	if (en)
39462306a36Sopenharmony_ci		hci_writel(ufs, misc | HCI_CORECLK_CTRL_EN, HCI_MISC);
39562306a36Sopenharmony_ci	else
39662306a36Sopenharmony_ci		hci_writel(ufs, misc & ~HCI_CORECLK_CTRL_EN, HCI_MISC);
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_cistatic void exynos_ufs_ctrl_clkstop(struct exynos_ufs *ufs, bool en)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	u32 ctrl = hci_readl(ufs, HCI_CLKSTOP_CTRL);
40262306a36Sopenharmony_ci	u32 misc = hci_readl(ufs, HCI_MISC);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	if (en) {
40562306a36Sopenharmony_ci		hci_writel(ufs, misc | CLK_CTRL_EN_MASK, HCI_MISC);
40662306a36Sopenharmony_ci		hci_writel(ufs, ctrl | CLK_STOP_MASK, HCI_CLKSTOP_CTRL);
40762306a36Sopenharmony_ci	} else {
40862306a36Sopenharmony_ci		hci_writel(ufs, ctrl & ~CLK_STOP_MASK, HCI_CLKSTOP_CTRL);
40962306a36Sopenharmony_ci		hci_writel(ufs, misc & ~CLK_CTRL_EN_MASK, HCI_MISC);
41062306a36Sopenharmony_ci	}
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_cistatic int exynos_ufs_get_clk_info(struct exynos_ufs *ufs)
41462306a36Sopenharmony_ci{
41562306a36Sopenharmony_ci	struct ufs_hba *hba = ufs->hba;
41662306a36Sopenharmony_ci	struct list_head *head = &hba->clk_list_head;
41762306a36Sopenharmony_ci	struct ufs_clk_info *clki;
41862306a36Sopenharmony_ci	unsigned long pclk_rate;
41962306a36Sopenharmony_ci	u32 f_min, f_max;
42062306a36Sopenharmony_ci	u8 div = 0;
42162306a36Sopenharmony_ci	int ret = 0;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	if (list_empty(head))
42462306a36Sopenharmony_ci		goto out;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	list_for_each_entry(clki, head, list) {
42762306a36Sopenharmony_ci		if (!IS_ERR(clki->clk)) {
42862306a36Sopenharmony_ci			if (!strcmp(clki->name, "core_clk"))
42962306a36Sopenharmony_ci				ufs->clk_hci_core = clki->clk;
43062306a36Sopenharmony_ci			else if (!strcmp(clki->name, "sclk_unipro_main"))
43162306a36Sopenharmony_ci				ufs->clk_unipro_main = clki->clk;
43262306a36Sopenharmony_ci		}
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	if (!ufs->clk_hci_core || !ufs->clk_unipro_main) {
43662306a36Sopenharmony_ci		dev_err(hba->dev, "failed to get clk info\n");
43762306a36Sopenharmony_ci		ret = -EINVAL;
43862306a36Sopenharmony_ci		goto out;
43962306a36Sopenharmony_ci	}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	ufs->mclk_rate = clk_get_rate(ufs->clk_unipro_main);
44262306a36Sopenharmony_ci	pclk_rate = clk_get_rate(ufs->clk_hci_core);
44362306a36Sopenharmony_ci	f_min = ufs->pclk_avail_min;
44462306a36Sopenharmony_ci	f_max = ufs->pclk_avail_max;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	if (ufs->opts & EXYNOS_UFS_OPT_HAS_APB_CLK_CTRL) {
44762306a36Sopenharmony_ci		do {
44862306a36Sopenharmony_ci			pclk_rate /= (div + 1);
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci			if (pclk_rate <= f_max)
45162306a36Sopenharmony_ci				break;
45262306a36Sopenharmony_ci			div++;
45362306a36Sopenharmony_ci		} while (pclk_rate >= f_min);
45462306a36Sopenharmony_ci	}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	if (unlikely(pclk_rate < f_min || pclk_rate > f_max)) {
45762306a36Sopenharmony_ci		dev_err(hba->dev, "not available pclk range %lu\n", pclk_rate);
45862306a36Sopenharmony_ci		ret = -EINVAL;
45962306a36Sopenharmony_ci		goto out;
46062306a36Sopenharmony_ci	}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	ufs->pclk_rate = pclk_rate;
46362306a36Sopenharmony_ci	ufs->pclk_div = div;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ciout:
46662306a36Sopenharmony_ci	return ret;
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_cistatic void exynos_ufs_set_unipro_pclk_div(struct exynos_ufs *ufs)
47062306a36Sopenharmony_ci{
47162306a36Sopenharmony_ci	if (ufs->opts & EXYNOS_UFS_OPT_HAS_APB_CLK_CTRL) {
47262306a36Sopenharmony_ci		u32 val;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci		val = hci_readl(ufs, HCI_UNIPRO_APB_CLK_CTRL);
47562306a36Sopenharmony_ci		hci_writel(ufs, UNIPRO_APB_CLK(val, ufs->pclk_div),
47662306a36Sopenharmony_ci			   HCI_UNIPRO_APB_CLK_CTRL);
47762306a36Sopenharmony_ci	}
47862306a36Sopenharmony_ci}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_cistatic void exynos_ufs_set_pwm_clk_div(struct exynos_ufs *ufs)
48162306a36Sopenharmony_ci{
48262306a36Sopenharmony_ci	struct ufs_hba *hba = ufs->hba;
48362306a36Sopenharmony_ci	struct exynos_ufs_uic_attr *attr = ufs->drv_data->uic_attr;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	ufshcd_dme_set(hba,
48662306a36Sopenharmony_ci		UIC_ARG_MIB(CMN_PWM_CLK_CTRL), attr->cmn_pwm_clk_ctrl);
48762306a36Sopenharmony_ci}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_cistatic void exynos_ufs_calc_pwm_clk_div(struct exynos_ufs *ufs)
49062306a36Sopenharmony_ci{
49162306a36Sopenharmony_ci	struct ufs_hba *hba = ufs->hba;
49262306a36Sopenharmony_ci	struct exynos_ufs_uic_attr *attr = ufs->drv_data->uic_attr;
49362306a36Sopenharmony_ci	const unsigned int div = 30, mult = 20;
49462306a36Sopenharmony_ci	const unsigned long pwm_min = 3 * 1000 * 1000;
49562306a36Sopenharmony_ci	const unsigned long pwm_max = 9 * 1000 * 1000;
49662306a36Sopenharmony_ci	const int divs[] = {32, 16, 8, 4};
49762306a36Sopenharmony_ci	unsigned long clk = 0, _clk, clk_period;
49862306a36Sopenharmony_ci	int i = 0, clk_idx = -1;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	clk_period = UNIPRO_PCLK_PERIOD(ufs);
50162306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(divs); i++) {
50262306a36Sopenharmony_ci		_clk = NSEC_PER_SEC * mult / (clk_period * divs[i] * div);
50362306a36Sopenharmony_ci		if (_clk >= pwm_min && _clk <= pwm_max) {
50462306a36Sopenharmony_ci			if (_clk > clk) {
50562306a36Sopenharmony_ci				clk_idx = i;
50662306a36Sopenharmony_ci				clk = _clk;
50762306a36Sopenharmony_ci			}
50862306a36Sopenharmony_ci		}
50962306a36Sopenharmony_ci	}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	if (clk_idx == -1) {
51262306a36Sopenharmony_ci		ufshcd_dme_get(hba, UIC_ARG_MIB(CMN_PWM_CLK_CTRL), &clk_idx);
51362306a36Sopenharmony_ci		dev_err(hba->dev,
51462306a36Sopenharmony_ci			"failed to decide pwm clock divider, will not change\n");
51562306a36Sopenharmony_ci	}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	attr->cmn_pwm_clk_ctrl = clk_idx & PWM_CLK_CTRL_MASK;
51862306a36Sopenharmony_ci}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_cilong exynos_ufs_calc_time_cntr(struct exynos_ufs *ufs, long period)
52162306a36Sopenharmony_ci{
52262306a36Sopenharmony_ci	const int precise = 10;
52362306a36Sopenharmony_ci	long pclk_rate = ufs->pclk_rate;
52462306a36Sopenharmony_ci	long clk_period, fraction;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	clk_period = UNIPRO_PCLK_PERIOD(ufs);
52762306a36Sopenharmony_ci	fraction = ((NSEC_PER_SEC % pclk_rate) * precise) / pclk_rate;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	return (period * precise) / ((clk_period * precise) + fraction);
53062306a36Sopenharmony_ci}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_cistatic void exynos_ufs_specify_phy_time_attr(struct exynos_ufs *ufs)
53362306a36Sopenharmony_ci{
53462306a36Sopenharmony_ci	struct exynos_ufs_uic_attr *attr = ufs->drv_data->uic_attr;
53562306a36Sopenharmony_ci	struct ufs_phy_time_cfg *t_cfg = &ufs->t_cfg;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	t_cfg->tx_linereset_p =
53862306a36Sopenharmony_ci		exynos_ufs_calc_time_cntr(ufs, attr->tx_dif_p_nsec);
53962306a36Sopenharmony_ci	t_cfg->tx_linereset_n =
54062306a36Sopenharmony_ci		exynos_ufs_calc_time_cntr(ufs, attr->tx_dif_n_nsec);
54162306a36Sopenharmony_ci	t_cfg->tx_high_z_cnt =
54262306a36Sopenharmony_ci		exynos_ufs_calc_time_cntr(ufs, attr->tx_high_z_cnt_nsec);
54362306a36Sopenharmony_ci	t_cfg->tx_base_n_val =
54462306a36Sopenharmony_ci		exynos_ufs_calc_time_cntr(ufs, attr->tx_base_unit_nsec);
54562306a36Sopenharmony_ci	t_cfg->tx_gran_n_val =
54662306a36Sopenharmony_ci		exynos_ufs_calc_time_cntr(ufs, attr->tx_gran_unit_nsec);
54762306a36Sopenharmony_ci	t_cfg->tx_sleep_cnt =
54862306a36Sopenharmony_ci		exynos_ufs_calc_time_cntr(ufs, attr->tx_sleep_cnt);
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	t_cfg->rx_linereset =
55162306a36Sopenharmony_ci		exynos_ufs_calc_time_cntr(ufs, attr->rx_dif_p_nsec);
55262306a36Sopenharmony_ci	t_cfg->rx_hibern8_wait =
55362306a36Sopenharmony_ci		exynos_ufs_calc_time_cntr(ufs, attr->rx_hibern8_wait_nsec);
55462306a36Sopenharmony_ci	t_cfg->rx_base_n_val =
55562306a36Sopenharmony_ci		exynos_ufs_calc_time_cntr(ufs, attr->rx_base_unit_nsec);
55662306a36Sopenharmony_ci	t_cfg->rx_gran_n_val =
55762306a36Sopenharmony_ci		exynos_ufs_calc_time_cntr(ufs, attr->rx_gran_unit_nsec);
55862306a36Sopenharmony_ci	t_cfg->rx_sleep_cnt =
55962306a36Sopenharmony_ci		exynos_ufs_calc_time_cntr(ufs, attr->rx_sleep_cnt);
56062306a36Sopenharmony_ci	t_cfg->rx_stall_cnt =
56162306a36Sopenharmony_ci		exynos_ufs_calc_time_cntr(ufs, attr->rx_stall_cnt);
56262306a36Sopenharmony_ci}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_cistatic void exynos_ufs_config_phy_time_attr(struct exynos_ufs *ufs)
56562306a36Sopenharmony_ci{
56662306a36Sopenharmony_ci	struct ufs_hba *hba = ufs->hba;
56762306a36Sopenharmony_ci	struct ufs_phy_time_cfg *t_cfg = &ufs->t_cfg;
56862306a36Sopenharmony_ci	int i;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	exynos_ufs_set_pwm_clk_div(ufs);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	exynos_ufs_enable_ov_tm(hba);
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	for_each_ufs_rx_lane(ufs, i) {
57562306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(RX_FILLER_ENABLE, i),
57662306a36Sopenharmony_ci				ufs->drv_data->uic_attr->rx_filler_enable);
57762306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(RX_LINERESET_VAL, i),
57862306a36Sopenharmony_ci				RX_LINERESET(t_cfg->rx_linereset));
57962306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(RX_BASE_NVAL_07_00, i),
58062306a36Sopenharmony_ci				RX_BASE_NVAL_L(t_cfg->rx_base_n_val));
58162306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(RX_BASE_NVAL_15_08, i),
58262306a36Sopenharmony_ci				RX_BASE_NVAL_H(t_cfg->rx_base_n_val));
58362306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(RX_GRAN_NVAL_07_00, i),
58462306a36Sopenharmony_ci				RX_GRAN_NVAL_L(t_cfg->rx_gran_n_val));
58562306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(RX_GRAN_NVAL_10_08, i),
58662306a36Sopenharmony_ci				RX_GRAN_NVAL_H(t_cfg->rx_gran_n_val));
58762306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(RX_OV_SLEEP_CNT_TIMER, i),
58862306a36Sopenharmony_ci				RX_OV_SLEEP_CNT(t_cfg->rx_sleep_cnt));
58962306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(RX_OV_STALL_CNT_TIMER, i),
59062306a36Sopenharmony_ci				RX_OV_STALL_CNT(t_cfg->rx_stall_cnt));
59162306a36Sopenharmony_ci	}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	for_each_ufs_tx_lane(ufs, i) {
59462306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(TX_LINERESET_P_VAL, i),
59562306a36Sopenharmony_ci				TX_LINERESET_P(t_cfg->tx_linereset_p));
59662306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(TX_HIGH_Z_CNT_07_00, i),
59762306a36Sopenharmony_ci				TX_HIGH_Z_CNT_L(t_cfg->tx_high_z_cnt));
59862306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(TX_HIGH_Z_CNT_11_08, i),
59962306a36Sopenharmony_ci				TX_HIGH_Z_CNT_H(t_cfg->tx_high_z_cnt));
60062306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(TX_BASE_NVAL_07_00, i),
60162306a36Sopenharmony_ci				TX_BASE_NVAL_L(t_cfg->tx_base_n_val));
60262306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(TX_BASE_NVAL_15_08, i),
60362306a36Sopenharmony_ci				TX_BASE_NVAL_H(t_cfg->tx_base_n_val));
60462306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(TX_GRAN_NVAL_07_00, i),
60562306a36Sopenharmony_ci				TX_GRAN_NVAL_L(t_cfg->tx_gran_n_val));
60662306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(TX_GRAN_NVAL_10_08, i),
60762306a36Sopenharmony_ci				TX_GRAN_NVAL_H(t_cfg->tx_gran_n_val));
60862306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(TX_OV_SLEEP_CNT_TIMER, i),
60962306a36Sopenharmony_ci				TX_OV_H8_ENTER_EN |
61062306a36Sopenharmony_ci				TX_OV_SLEEP_CNT(t_cfg->tx_sleep_cnt));
61162306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(TX_MIN_ACTIVATETIME, i),
61262306a36Sopenharmony_ci				ufs->drv_data->uic_attr->tx_min_activatetime);
61362306a36Sopenharmony_ci	}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	exynos_ufs_disable_ov_tm(hba);
61662306a36Sopenharmony_ci}
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_cistatic void exynos_ufs_config_phy_cap_attr(struct exynos_ufs *ufs)
61962306a36Sopenharmony_ci{
62062306a36Sopenharmony_ci	struct ufs_hba *hba = ufs->hba;
62162306a36Sopenharmony_ci	struct exynos_ufs_uic_attr *attr = ufs->drv_data->uic_attr;
62262306a36Sopenharmony_ci	int i;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	exynos_ufs_enable_ov_tm(hba);
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	for_each_ufs_rx_lane(ufs, i) {
62762306a36Sopenharmony_ci		ufshcd_dme_set(hba,
62862306a36Sopenharmony_ci				UIC_ARG_MIB_SEL(RX_HS_G1_SYNC_LENGTH_CAP, i),
62962306a36Sopenharmony_ci				attr->rx_hs_g1_sync_len_cap);
63062306a36Sopenharmony_ci		ufshcd_dme_set(hba,
63162306a36Sopenharmony_ci				UIC_ARG_MIB_SEL(RX_HS_G2_SYNC_LENGTH_CAP, i),
63262306a36Sopenharmony_ci				attr->rx_hs_g2_sync_len_cap);
63362306a36Sopenharmony_ci		ufshcd_dme_set(hba,
63462306a36Sopenharmony_ci				UIC_ARG_MIB_SEL(RX_HS_G3_SYNC_LENGTH_CAP, i),
63562306a36Sopenharmony_ci				attr->rx_hs_g3_sync_len_cap);
63662306a36Sopenharmony_ci		ufshcd_dme_set(hba,
63762306a36Sopenharmony_ci				UIC_ARG_MIB_SEL(RX_HS_G1_PREP_LENGTH_CAP, i),
63862306a36Sopenharmony_ci				attr->rx_hs_g1_prep_sync_len_cap);
63962306a36Sopenharmony_ci		ufshcd_dme_set(hba,
64062306a36Sopenharmony_ci				UIC_ARG_MIB_SEL(RX_HS_G2_PREP_LENGTH_CAP, i),
64162306a36Sopenharmony_ci				attr->rx_hs_g2_prep_sync_len_cap);
64262306a36Sopenharmony_ci		ufshcd_dme_set(hba,
64362306a36Sopenharmony_ci				UIC_ARG_MIB_SEL(RX_HS_G3_PREP_LENGTH_CAP, i),
64462306a36Sopenharmony_ci				attr->rx_hs_g3_prep_sync_len_cap);
64562306a36Sopenharmony_ci	}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	if (attr->rx_adv_fine_gran_sup_en == 0) {
64862306a36Sopenharmony_ci		for_each_ufs_rx_lane(ufs, i) {
64962306a36Sopenharmony_ci			ufshcd_dme_set(hba,
65062306a36Sopenharmony_ci				UIC_ARG_MIB_SEL(RX_ADV_GRANULARITY_CAP, i), 0);
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci			if (attr->rx_min_actv_time_cap)
65362306a36Sopenharmony_ci				ufshcd_dme_set(hba,
65462306a36Sopenharmony_ci					UIC_ARG_MIB_SEL(
65562306a36Sopenharmony_ci					RX_MIN_ACTIVATETIME_CAPABILITY, i),
65662306a36Sopenharmony_ci					attr->rx_min_actv_time_cap);
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci			if (attr->rx_hibern8_time_cap)
65962306a36Sopenharmony_ci				ufshcd_dme_set(hba,
66062306a36Sopenharmony_ci					UIC_ARG_MIB_SEL(RX_HIBERN8TIME_CAP, i),
66162306a36Sopenharmony_ci						attr->rx_hibern8_time_cap);
66262306a36Sopenharmony_ci		}
66362306a36Sopenharmony_ci	} else if (attr->rx_adv_fine_gran_sup_en == 1) {
66462306a36Sopenharmony_ci		for_each_ufs_rx_lane(ufs, i) {
66562306a36Sopenharmony_ci			if (attr->rx_adv_fine_gran_step)
66662306a36Sopenharmony_ci				ufshcd_dme_set(hba,
66762306a36Sopenharmony_ci					UIC_ARG_MIB_SEL(RX_ADV_GRANULARITY_CAP,
66862306a36Sopenharmony_ci						i), RX_ADV_FINE_GRAN_STEP(
66962306a36Sopenharmony_ci						attr->rx_adv_fine_gran_step));
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci			if (attr->rx_adv_min_actv_time_cap)
67262306a36Sopenharmony_ci				ufshcd_dme_set(hba,
67362306a36Sopenharmony_ci					UIC_ARG_MIB_SEL(
67462306a36Sopenharmony_ci						RX_ADV_MIN_ACTIVATETIME_CAP, i),
67562306a36Sopenharmony_ci						attr->rx_adv_min_actv_time_cap);
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci			if (attr->rx_adv_hibern8_time_cap)
67862306a36Sopenharmony_ci				ufshcd_dme_set(hba,
67962306a36Sopenharmony_ci					UIC_ARG_MIB_SEL(RX_ADV_HIBERN8TIME_CAP,
68062306a36Sopenharmony_ci						i),
68162306a36Sopenharmony_ci						attr->rx_adv_hibern8_time_cap);
68262306a36Sopenharmony_ci		}
68362306a36Sopenharmony_ci	}
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	exynos_ufs_disable_ov_tm(hba);
68662306a36Sopenharmony_ci}
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_cistatic void exynos_ufs_establish_connt(struct exynos_ufs *ufs)
68962306a36Sopenharmony_ci{
69062306a36Sopenharmony_ci	struct ufs_hba *hba = ufs->hba;
69162306a36Sopenharmony_ci	enum {
69262306a36Sopenharmony_ci		DEV_ID		= 0x00,
69362306a36Sopenharmony_ci		PEER_DEV_ID	= 0x01,
69462306a36Sopenharmony_ci		PEER_CPORT_ID	= 0x00,
69562306a36Sopenharmony_ci		TRAFFIC_CLASS	= 0x00,
69662306a36Sopenharmony_ci	};
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	/* allow cport attributes to be set */
69962306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(T_CONNECTIONSTATE), CPORT_IDLE);
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	/* local unipro attributes */
70262306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(N_DEVICEID), DEV_ID);
70362306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(N_DEVICEID_VALID), true);
70462306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(T_PEERDEVICEID), PEER_DEV_ID);
70562306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(T_PEERCPORTID), PEER_CPORT_ID);
70662306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(T_CPORTFLAGS), CPORT_DEF_FLAGS);
70762306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(T_TRAFFICCLASS), TRAFFIC_CLASS);
70862306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(T_CONNECTIONSTATE), CPORT_CONNECTED);
70962306a36Sopenharmony_ci}
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_cistatic void exynos_ufs_config_smu(struct exynos_ufs *ufs)
71262306a36Sopenharmony_ci{
71362306a36Sopenharmony_ci	u32 reg, val;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	exynos_ufs_disable_auto_ctrl_hcc_save(ufs, &val);
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	/* make encryption disabled by default */
71862306a36Sopenharmony_ci	reg = ufsp_readl(ufs, UFSPRSECURITY);
71962306a36Sopenharmony_ci	ufsp_writel(ufs, reg | NSSMU, UFSPRSECURITY);
72062306a36Sopenharmony_ci	ufsp_writel(ufs, 0x0, UFSPSBEGIN0);
72162306a36Sopenharmony_ci	ufsp_writel(ufs, 0xffffffff, UFSPSEND0);
72262306a36Sopenharmony_ci	ufsp_writel(ufs, 0xff, UFSPSLUN0);
72362306a36Sopenharmony_ci	ufsp_writel(ufs, 0xf1, UFSPSCTRL0);
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	exynos_ufs_auto_ctrl_hcc_restore(ufs, &val);
72662306a36Sopenharmony_ci}
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_cistatic void exynos_ufs_config_sync_pattern_mask(struct exynos_ufs *ufs,
72962306a36Sopenharmony_ci					struct ufs_pa_layer_attr *pwr)
73062306a36Sopenharmony_ci{
73162306a36Sopenharmony_ci	struct ufs_hba *hba = ufs->hba;
73262306a36Sopenharmony_ci	u8 g = max_t(u32, pwr->gear_rx, pwr->gear_tx);
73362306a36Sopenharmony_ci	u32 mask, sync_len;
73462306a36Sopenharmony_ci	enum {
73562306a36Sopenharmony_ci		SYNC_LEN_G1 = 80 * 1000, /* 80us */
73662306a36Sopenharmony_ci		SYNC_LEN_G2 = 40 * 1000, /* 44us */
73762306a36Sopenharmony_ci		SYNC_LEN_G3 = 20 * 1000, /* 20us */
73862306a36Sopenharmony_ci	};
73962306a36Sopenharmony_ci	int i;
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	if (g == 1)
74262306a36Sopenharmony_ci		sync_len = SYNC_LEN_G1;
74362306a36Sopenharmony_ci	else if (g == 2)
74462306a36Sopenharmony_ci		sync_len = SYNC_LEN_G2;
74562306a36Sopenharmony_ci	else if (g == 3)
74662306a36Sopenharmony_ci		sync_len = SYNC_LEN_G3;
74762306a36Sopenharmony_ci	else
74862306a36Sopenharmony_ci		return;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	mask = exynos_ufs_calc_time_cntr(ufs, sync_len);
75162306a36Sopenharmony_ci	mask = (mask >> 8) & 0xff;
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	exynos_ufs_enable_ov_tm(hba);
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	for_each_ufs_rx_lane(ufs, i)
75662306a36Sopenharmony_ci		ufshcd_dme_set(hba,
75762306a36Sopenharmony_ci			UIC_ARG_MIB_SEL(RX_SYNC_MASK_LENGTH, i), mask);
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	exynos_ufs_disable_ov_tm(hba);
76062306a36Sopenharmony_ci}
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_cistatic int exynos_ufs_pre_pwr_mode(struct ufs_hba *hba,
76362306a36Sopenharmony_ci				struct ufs_pa_layer_attr *dev_max_params,
76462306a36Sopenharmony_ci				struct ufs_pa_layer_attr *dev_req_params)
76562306a36Sopenharmony_ci{
76662306a36Sopenharmony_ci	struct exynos_ufs *ufs = ufshcd_get_variant(hba);
76762306a36Sopenharmony_ci	struct phy *generic_phy = ufs->phy;
76862306a36Sopenharmony_ci	struct ufs_dev_params ufs_exynos_cap;
76962306a36Sopenharmony_ci	int ret;
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	if (!dev_req_params) {
77262306a36Sopenharmony_ci		pr_err("%s: incoming dev_req_params is NULL\n", __func__);
77362306a36Sopenharmony_ci		ret = -EINVAL;
77462306a36Sopenharmony_ci		goto out;
77562306a36Sopenharmony_ci	}
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	ufshcd_init_pwr_dev_param(&ufs_exynos_cap);
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	ret = ufshcd_get_pwr_dev_param(&ufs_exynos_cap,
78062306a36Sopenharmony_ci				       dev_max_params, dev_req_params);
78162306a36Sopenharmony_ci	if (ret) {
78262306a36Sopenharmony_ci		pr_err("%s: failed to determine capabilities\n", __func__);
78362306a36Sopenharmony_ci		goto out;
78462306a36Sopenharmony_ci	}
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	if (ufs->drv_data->pre_pwr_change)
78762306a36Sopenharmony_ci		ufs->drv_data->pre_pwr_change(ufs, dev_req_params);
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	if (ufshcd_is_hs_mode(dev_req_params)) {
79062306a36Sopenharmony_ci		exynos_ufs_config_sync_pattern_mask(ufs, dev_req_params);
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci		switch (dev_req_params->hs_rate) {
79362306a36Sopenharmony_ci		case PA_HS_MODE_A:
79462306a36Sopenharmony_ci		case PA_HS_MODE_B:
79562306a36Sopenharmony_ci			phy_calibrate(generic_phy);
79662306a36Sopenharmony_ci			break;
79762306a36Sopenharmony_ci		}
79862306a36Sopenharmony_ci	}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	/* setting for three timeout values for traffic class #0 */
80162306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(DL_FC0PROTTIMEOUTVAL), 8064);
80262306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(DL_TC0REPLAYTIMEOUTVAL), 28224);
80362306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(DL_AFC0REQTIMEOUTVAL), 20160);
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	return 0;
80662306a36Sopenharmony_ciout:
80762306a36Sopenharmony_ci	return ret;
80862306a36Sopenharmony_ci}
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci#define PWR_MODE_STR_LEN	64
81162306a36Sopenharmony_cistatic int exynos_ufs_post_pwr_mode(struct ufs_hba *hba,
81262306a36Sopenharmony_ci				struct ufs_pa_layer_attr *pwr_req)
81362306a36Sopenharmony_ci{
81462306a36Sopenharmony_ci	struct exynos_ufs *ufs = ufshcd_get_variant(hba);
81562306a36Sopenharmony_ci	struct phy *generic_phy = ufs->phy;
81662306a36Sopenharmony_ci	int gear = max_t(u32, pwr_req->gear_rx, pwr_req->gear_tx);
81762306a36Sopenharmony_ci	int lanes = max_t(u32, pwr_req->lane_rx, pwr_req->lane_tx);
81862306a36Sopenharmony_ci	char pwr_str[PWR_MODE_STR_LEN] = "";
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	/* let default be PWM Gear 1, Lane 1 */
82162306a36Sopenharmony_ci	if (!gear)
82262306a36Sopenharmony_ci		gear = 1;
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	if (!lanes)
82562306a36Sopenharmony_ci		lanes = 1;
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	if (ufs->drv_data->post_pwr_change)
82862306a36Sopenharmony_ci		ufs->drv_data->post_pwr_change(ufs, pwr_req);
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	if ((ufshcd_is_hs_mode(pwr_req))) {
83162306a36Sopenharmony_ci		switch (pwr_req->hs_rate) {
83262306a36Sopenharmony_ci		case PA_HS_MODE_A:
83362306a36Sopenharmony_ci		case PA_HS_MODE_B:
83462306a36Sopenharmony_ci			phy_calibrate(generic_phy);
83562306a36Sopenharmony_ci			break;
83662306a36Sopenharmony_ci		}
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci		snprintf(pwr_str, PWR_MODE_STR_LEN, "%s series_%s G_%d L_%d",
83962306a36Sopenharmony_ci			"FAST",	pwr_req->hs_rate == PA_HS_MODE_A ? "A" : "B",
84062306a36Sopenharmony_ci			gear, lanes);
84162306a36Sopenharmony_ci	} else {
84262306a36Sopenharmony_ci		snprintf(pwr_str, PWR_MODE_STR_LEN, "%s G_%d L_%d",
84362306a36Sopenharmony_ci			"SLOW", gear, lanes);
84462306a36Sopenharmony_ci	}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	dev_info(hba->dev, "Power mode changed to : %s\n", pwr_str);
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	return 0;
84962306a36Sopenharmony_ci}
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_cistatic void exynos_ufs_specify_nexus_t_xfer_req(struct ufs_hba *hba,
85262306a36Sopenharmony_ci						int tag, bool is_scsi_cmd)
85362306a36Sopenharmony_ci{
85462306a36Sopenharmony_ci	struct exynos_ufs *ufs = ufshcd_get_variant(hba);
85562306a36Sopenharmony_ci	u32 type;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	type =  hci_readl(ufs, HCI_UTRL_NEXUS_TYPE);
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	if (is_scsi_cmd)
86062306a36Sopenharmony_ci		hci_writel(ufs, type | (1 << tag), HCI_UTRL_NEXUS_TYPE);
86162306a36Sopenharmony_ci	else
86262306a36Sopenharmony_ci		hci_writel(ufs, type & ~(1 << tag), HCI_UTRL_NEXUS_TYPE);
86362306a36Sopenharmony_ci}
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_cistatic void exynos_ufs_specify_nexus_t_tm_req(struct ufs_hba *hba,
86662306a36Sopenharmony_ci						int tag, u8 func)
86762306a36Sopenharmony_ci{
86862306a36Sopenharmony_ci	struct exynos_ufs *ufs = ufshcd_get_variant(hba);
86962306a36Sopenharmony_ci	u32 type;
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	type =  hci_readl(ufs, HCI_UTMRL_NEXUS_TYPE);
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	switch (func) {
87462306a36Sopenharmony_ci	case UFS_ABORT_TASK:
87562306a36Sopenharmony_ci	case UFS_QUERY_TASK:
87662306a36Sopenharmony_ci		hci_writel(ufs, type | (1 << tag), HCI_UTMRL_NEXUS_TYPE);
87762306a36Sopenharmony_ci		break;
87862306a36Sopenharmony_ci	case UFS_ABORT_TASK_SET:
87962306a36Sopenharmony_ci	case UFS_CLEAR_TASK_SET:
88062306a36Sopenharmony_ci	case UFS_LOGICAL_RESET:
88162306a36Sopenharmony_ci	case UFS_QUERY_TASK_SET:
88262306a36Sopenharmony_ci		hci_writel(ufs, type & ~(1 << tag), HCI_UTMRL_NEXUS_TYPE);
88362306a36Sopenharmony_ci		break;
88462306a36Sopenharmony_ci	}
88562306a36Sopenharmony_ci}
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_cistatic int exynos_ufs_phy_init(struct exynos_ufs *ufs)
88862306a36Sopenharmony_ci{
88962306a36Sopenharmony_ci	struct ufs_hba *hba = ufs->hba;
89062306a36Sopenharmony_ci	struct phy *generic_phy = ufs->phy;
89162306a36Sopenharmony_ci	int ret = 0;
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	if (ufs->avail_ln_rx == 0 || ufs->avail_ln_tx == 0) {
89462306a36Sopenharmony_ci		ufshcd_dme_get(hba, UIC_ARG_MIB(PA_AVAILRXDATALANES),
89562306a36Sopenharmony_ci			&ufs->avail_ln_rx);
89662306a36Sopenharmony_ci		ufshcd_dme_get(hba, UIC_ARG_MIB(PA_AVAILTXDATALANES),
89762306a36Sopenharmony_ci			&ufs->avail_ln_tx);
89862306a36Sopenharmony_ci		WARN(ufs->avail_ln_rx != ufs->avail_ln_tx,
89962306a36Sopenharmony_ci			"available data lane is not equal(rx:%d, tx:%d)\n",
90062306a36Sopenharmony_ci			ufs->avail_ln_rx, ufs->avail_ln_tx);
90162306a36Sopenharmony_ci	}
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	phy_set_bus_width(generic_phy, ufs->avail_ln_rx);
90462306a36Sopenharmony_ci	ret = phy_init(generic_phy);
90562306a36Sopenharmony_ci	if (ret) {
90662306a36Sopenharmony_ci		dev_err(hba->dev, "%s: phy init failed, ret = %d\n",
90762306a36Sopenharmony_ci			__func__, ret);
90862306a36Sopenharmony_ci		return ret;
90962306a36Sopenharmony_ci	}
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	ret = phy_power_on(generic_phy);
91262306a36Sopenharmony_ci	if (ret)
91362306a36Sopenharmony_ci		goto out_exit_phy;
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	return 0;
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ciout_exit_phy:
91862306a36Sopenharmony_ci	phy_exit(generic_phy);
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	return ret;
92162306a36Sopenharmony_ci}
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_cistatic void exynos_ufs_config_unipro(struct exynos_ufs *ufs)
92462306a36Sopenharmony_ci{
92562306a36Sopenharmony_ci	struct ufs_hba *hba = ufs->hba;
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_DBG_CLK_PERIOD),
92862306a36Sopenharmony_ci		DIV_ROUND_UP(NSEC_PER_SEC, ufs->mclk_rate));
92962306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXTRAILINGCLOCKS),
93062306a36Sopenharmony_ci			ufs->drv_data->uic_attr->tx_trailingclks);
93162306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_DBG_OPTION_SUITE),
93262306a36Sopenharmony_ci			ufs->drv_data->uic_attr->pa_dbg_option_suite);
93362306a36Sopenharmony_ci}
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_cistatic void exynos_ufs_config_intr(struct exynos_ufs *ufs, u32 errs, u8 index)
93662306a36Sopenharmony_ci{
93762306a36Sopenharmony_ci	switch (index) {
93862306a36Sopenharmony_ci	case UNIPRO_L1_5:
93962306a36Sopenharmony_ci		hci_writel(ufs, DFES_ERR_EN | errs, HCI_ERR_EN_PA_LAYER);
94062306a36Sopenharmony_ci		break;
94162306a36Sopenharmony_ci	case UNIPRO_L2:
94262306a36Sopenharmony_ci		hci_writel(ufs, DFES_ERR_EN | errs, HCI_ERR_EN_DL_LAYER);
94362306a36Sopenharmony_ci		break;
94462306a36Sopenharmony_ci	case UNIPRO_L3:
94562306a36Sopenharmony_ci		hci_writel(ufs, DFES_ERR_EN | errs, HCI_ERR_EN_N_LAYER);
94662306a36Sopenharmony_ci		break;
94762306a36Sopenharmony_ci	case UNIPRO_L4:
94862306a36Sopenharmony_ci		hci_writel(ufs, DFES_ERR_EN | errs, HCI_ERR_EN_T_LAYER);
94962306a36Sopenharmony_ci		break;
95062306a36Sopenharmony_ci	case UNIPRO_DME:
95162306a36Sopenharmony_ci		hci_writel(ufs, DFES_ERR_EN | errs, HCI_ERR_EN_DME_LAYER);
95262306a36Sopenharmony_ci		break;
95362306a36Sopenharmony_ci	}
95462306a36Sopenharmony_ci}
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_cistatic int exynos_ufs_setup_clocks(struct ufs_hba *hba, bool on,
95762306a36Sopenharmony_ci				   enum ufs_notify_change_status status)
95862306a36Sopenharmony_ci{
95962306a36Sopenharmony_ci	struct exynos_ufs *ufs = ufshcd_get_variant(hba);
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	if (!ufs)
96262306a36Sopenharmony_ci		return 0;
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	if (on && status == PRE_CHANGE) {
96562306a36Sopenharmony_ci		if (ufs->opts & EXYNOS_UFS_OPT_BROKEN_AUTO_CLK_CTRL)
96662306a36Sopenharmony_ci			exynos_ufs_disable_auto_ctrl_hcc(ufs);
96762306a36Sopenharmony_ci		exynos_ufs_ungate_clks(ufs);
96862306a36Sopenharmony_ci	} else if (!on && status == POST_CHANGE) {
96962306a36Sopenharmony_ci		exynos_ufs_gate_clks(ufs);
97062306a36Sopenharmony_ci		if (ufs->opts & EXYNOS_UFS_OPT_BROKEN_AUTO_CLK_CTRL)
97162306a36Sopenharmony_ci			exynos_ufs_enable_auto_ctrl_hcc(ufs);
97262306a36Sopenharmony_ci	}
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	return 0;
97562306a36Sopenharmony_ci}
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_cistatic int exynos_ufs_pre_link(struct ufs_hba *hba)
97862306a36Sopenharmony_ci{
97962306a36Sopenharmony_ci	struct exynos_ufs *ufs = ufshcd_get_variant(hba);
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	/* hci */
98262306a36Sopenharmony_ci	exynos_ufs_config_intr(ufs, DFES_DEF_L2_ERRS, UNIPRO_L2);
98362306a36Sopenharmony_ci	exynos_ufs_config_intr(ufs, DFES_DEF_L3_ERRS, UNIPRO_L3);
98462306a36Sopenharmony_ci	exynos_ufs_config_intr(ufs, DFES_DEF_L4_ERRS, UNIPRO_L4);
98562306a36Sopenharmony_ci	exynos_ufs_set_unipro_pclk_div(ufs);
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	/* unipro */
98862306a36Sopenharmony_ci	exynos_ufs_config_unipro(ufs);
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	/* m-phy */
99162306a36Sopenharmony_ci	exynos_ufs_phy_init(ufs);
99262306a36Sopenharmony_ci	if (!(ufs->opts & EXYNOS_UFS_OPT_SKIP_CONFIG_PHY_ATTR)) {
99362306a36Sopenharmony_ci		exynos_ufs_config_phy_time_attr(ufs);
99462306a36Sopenharmony_ci		exynos_ufs_config_phy_cap_attr(ufs);
99562306a36Sopenharmony_ci	}
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	exynos_ufs_setup_clocks(hba, true, PRE_CHANGE);
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	if (ufs->drv_data->pre_link)
100062306a36Sopenharmony_ci		ufs->drv_data->pre_link(ufs);
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	return 0;
100362306a36Sopenharmony_ci}
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_cistatic void exynos_ufs_fit_aggr_timeout(struct exynos_ufs *ufs)
100662306a36Sopenharmony_ci{
100762306a36Sopenharmony_ci	u32 val;
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	val = exynos_ufs_calc_time_cntr(ufs, IATOVAL_NSEC / CNTR_DIV_VAL);
101062306a36Sopenharmony_ci	hci_writel(ufs, val & CNT_VAL_1US_MASK, HCI_1US_TO_CNT_VAL);
101162306a36Sopenharmony_ci}
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_cistatic int exynos_ufs_post_link(struct ufs_hba *hba)
101462306a36Sopenharmony_ci{
101562306a36Sopenharmony_ci	struct exynos_ufs *ufs = ufshcd_get_variant(hba);
101662306a36Sopenharmony_ci	struct phy *generic_phy = ufs->phy;
101762306a36Sopenharmony_ci	struct exynos_ufs_uic_attr *attr = ufs->drv_data->uic_attr;
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	exynos_ufs_establish_connt(ufs);
102062306a36Sopenharmony_ci	exynos_ufs_fit_aggr_timeout(ufs);
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	hci_writel(ufs, 0xa, HCI_DATA_REORDER);
102362306a36Sopenharmony_ci	hci_writel(ufs, PRDT_SET_SIZE(12), HCI_TXPRDT_ENTRY_SIZE);
102462306a36Sopenharmony_ci	hci_writel(ufs, PRDT_SET_SIZE(12), HCI_RXPRDT_ENTRY_SIZE);
102562306a36Sopenharmony_ci	hci_writel(ufs, (1 << hba->nutrs) - 1, HCI_UTRL_NEXUS_TYPE);
102662306a36Sopenharmony_ci	hci_writel(ufs, (1 << hba->nutmrs) - 1, HCI_UTMRL_NEXUS_TYPE);
102762306a36Sopenharmony_ci	hci_writel(ufs, 0xf, HCI_AXIDMA_RWDATA_BURST_LEN);
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	if (ufs->opts & EXYNOS_UFS_OPT_SKIP_CONNECTION_ESTAB)
103062306a36Sopenharmony_ci		ufshcd_dme_set(hba,
103162306a36Sopenharmony_ci			UIC_ARG_MIB(T_DBG_SKIP_INIT_HIBERN8_EXIT), true);
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	if (attr->pa_granularity) {
103462306a36Sopenharmony_ci		exynos_ufs_enable_dbg_mode(hba);
103562306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_GRANULARITY),
103662306a36Sopenharmony_ci				attr->pa_granularity);
103762306a36Sopenharmony_ci		exynos_ufs_disable_dbg_mode(hba);
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci		if (attr->pa_tactivate)
104062306a36Sopenharmony_ci			ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TACTIVATE),
104162306a36Sopenharmony_ci					attr->pa_tactivate);
104262306a36Sopenharmony_ci		if (attr->pa_hibern8time &&
104362306a36Sopenharmony_ci		    !(ufs->opts & EXYNOS_UFS_OPT_USE_SW_HIBERN8_TIMER))
104462306a36Sopenharmony_ci			ufshcd_dme_set(hba, UIC_ARG_MIB(PA_HIBERN8TIME),
104562306a36Sopenharmony_ci					attr->pa_hibern8time);
104662306a36Sopenharmony_ci	}
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	if (ufs->opts & EXYNOS_UFS_OPT_USE_SW_HIBERN8_TIMER) {
104962306a36Sopenharmony_ci		if (!attr->pa_granularity)
105062306a36Sopenharmony_ci			ufshcd_dme_get(hba, UIC_ARG_MIB(PA_GRANULARITY),
105162306a36Sopenharmony_ci					&attr->pa_granularity);
105262306a36Sopenharmony_ci		if (!attr->pa_hibern8time)
105362306a36Sopenharmony_ci			ufshcd_dme_get(hba, UIC_ARG_MIB(PA_HIBERN8TIME),
105462306a36Sopenharmony_ci					&attr->pa_hibern8time);
105562306a36Sopenharmony_ci		/*
105662306a36Sopenharmony_ci		 * not wait for HIBERN8 time to exit hibernation
105762306a36Sopenharmony_ci		 */
105862306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB(PA_HIBERN8TIME), 0);
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci		if (attr->pa_granularity < 1 || attr->pa_granularity > 6) {
106162306a36Sopenharmony_ci			/* Valid range for granularity: 1 ~ 6 */
106262306a36Sopenharmony_ci			dev_warn(hba->dev,
106362306a36Sopenharmony_ci				"%s: pa_granularity %d is invalid, assuming backwards compatibility\n",
106462306a36Sopenharmony_ci				__func__,
106562306a36Sopenharmony_ci				attr->pa_granularity);
106662306a36Sopenharmony_ci			attr->pa_granularity = 6;
106762306a36Sopenharmony_ci		}
106862306a36Sopenharmony_ci	}
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	phy_calibrate(generic_phy);
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci	if (ufs->drv_data->post_link)
107362306a36Sopenharmony_ci		ufs->drv_data->post_link(ufs);
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	return 0;
107662306a36Sopenharmony_ci}
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_cistatic int exynos_ufs_parse_dt(struct device *dev, struct exynos_ufs *ufs)
107962306a36Sopenharmony_ci{
108062306a36Sopenharmony_ci	struct device_node *np = dev->of_node;
108162306a36Sopenharmony_ci	struct exynos_ufs_uic_attr *attr;
108262306a36Sopenharmony_ci	int ret = 0;
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	ufs->drv_data = device_get_match_data(dev);
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	if (ufs->drv_data && ufs->drv_data->uic_attr) {
108762306a36Sopenharmony_ci		attr = ufs->drv_data->uic_attr;
108862306a36Sopenharmony_ci	} else {
108962306a36Sopenharmony_ci		dev_err(dev, "failed to get uic attributes\n");
109062306a36Sopenharmony_ci		ret = -EINVAL;
109162306a36Sopenharmony_ci		goto out;
109262306a36Sopenharmony_ci	}
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	ufs->sysreg = syscon_regmap_lookup_by_phandle(np, "samsung,sysreg");
109562306a36Sopenharmony_ci	if (IS_ERR(ufs->sysreg))
109662306a36Sopenharmony_ci		ufs->sysreg = NULL;
109762306a36Sopenharmony_ci	else {
109862306a36Sopenharmony_ci		if (of_property_read_u32_index(np, "samsung,sysreg", 1,
109962306a36Sopenharmony_ci					       &ufs->shareability_reg_offset)) {
110062306a36Sopenharmony_ci			dev_warn(dev, "can't get an offset from sysreg. Set to default value\n");
110162306a36Sopenharmony_ci			ufs->shareability_reg_offset = UFS_SHAREABILITY_OFFSET;
110262306a36Sopenharmony_ci		}
110362306a36Sopenharmony_ci	}
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	ufs->pclk_avail_min = PCLK_AVAIL_MIN;
110662306a36Sopenharmony_ci	ufs->pclk_avail_max = PCLK_AVAIL_MAX;
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	attr->rx_adv_fine_gran_sup_en = RX_ADV_FINE_GRAN_SUP_EN;
110962306a36Sopenharmony_ci	attr->rx_adv_fine_gran_step = RX_ADV_FINE_GRAN_STEP_VAL;
111062306a36Sopenharmony_ci	attr->rx_adv_min_actv_time_cap = RX_ADV_MIN_ACTV_TIME_CAP;
111162306a36Sopenharmony_ci	attr->pa_granularity = PA_GRANULARITY_VAL;
111262306a36Sopenharmony_ci	attr->pa_tactivate = PA_TACTIVATE_VAL;
111362306a36Sopenharmony_ci	attr->pa_hibern8time = PA_HIBERN8TIME_VAL;
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ciout:
111662306a36Sopenharmony_ci	return ret;
111762306a36Sopenharmony_ci}
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_cistatic inline void exynos_ufs_priv_init(struct ufs_hba *hba,
112062306a36Sopenharmony_ci					struct exynos_ufs *ufs)
112162306a36Sopenharmony_ci{
112262306a36Sopenharmony_ci	ufs->hba = hba;
112362306a36Sopenharmony_ci	ufs->opts = ufs->drv_data->opts;
112462306a36Sopenharmony_ci	ufs->rx_sel_idx = PA_MAXDATALANES;
112562306a36Sopenharmony_ci	if (ufs->opts & EXYNOS_UFS_OPT_BROKEN_RX_SEL_IDX)
112662306a36Sopenharmony_ci		ufs->rx_sel_idx = 0;
112762306a36Sopenharmony_ci	hba->priv = (void *)ufs;
112862306a36Sopenharmony_ci	hba->quirks = ufs->drv_data->quirks;
112962306a36Sopenharmony_ci}
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_cistatic int exynos_ufs_init(struct ufs_hba *hba)
113262306a36Sopenharmony_ci{
113362306a36Sopenharmony_ci	struct device *dev = hba->dev;
113462306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
113562306a36Sopenharmony_ci	struct exynos_ufs *ufs;
113662306a36Sopenharmony_ci	int ret;
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	ufs = devm_kzalloc(dev, sizeof(*ufs), GFP_KERNEL);
113962306a36Sopenharmony_ci	if (!ufs)
114062306a36Sopenharmony_ci		return -ENOMEM;
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	/* exynos-specific hci */
114362306a36Sopenharmony_ci	ufs->reg_hci = devm_platform_ioremap_resource_byname(pdev, "vs_hci");
114462306a36Sopenharmony_ci	if (IS_ERR(ufs->reg_hci)) {
114562306a36Sopenharmony_ci		dev_err(dev, "cannot ioremap for hci vendor register\n");
114662306a36Sopenharmony_ci		return PTR_ERR(ufs->reg_hci);
114762306a36Sopenharmony_ci	}
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	/* unipro */
115062306a36Sopenharmony_ci	ufs->reg_unipro = devm_platform_ioremap_resource_byname(pdev, "unipro");
115162306a36Sopenharmony_ci	if (IS_ERR(ufs->reg_unipro)) {
115262306a36Sopenharmony_ci		dev_err(dev, "cannot ioremap for unipro register\n");
115362306a36Sopenharmony_ci		return PTR_ERR(ufs->reg_unipro);
115462306a36Sopenharmony_ci	}
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	/* ufs protector */
115762306a36Sopenharmony_ci	ufs->reg_ufsp = devm_platform_ioremap_resource_byname(pdev, "ufsp");
115862306a36Sopenharmony_ci	if (IS_ERR(ufs->reg_ufsp)) {
115962306a36Sopenharmony_ci		dev_err(dev, "cannot ioremap for ufs protector register\n");
116062306a36Sopenharmony_ci		return PTR_ERR(ufs->reg_ufsp);
116162306a36Sopenharmony_ci	}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	ret = exynos_ufs_parse_dt(dev, ufs);
116462306a36Sopenharmony_ci	if (ret) {
116562306a36Sopenharmony_ci		dev_err(dev, "failed to get dt info.\n");
116662306a36Sopenharmony_ci		goto out;
116762306a36Sopenharmony_ci	}
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	ufs->phy = devm_phy_get(dev, "ufs-phy");
117062306a36Sopenharmony_ci	if (IS_ERR(ufs->phy)) {
117162306a36Sopenharmony_ci		ret = PTR_ERR(ufs->phy);
117262306a36Sopenharmony_ci		dev_err(dev, "failed to get ufs-phy\n");
117362306a36Sopenharmony_ci		goto out;
117462306a36Sopenharmony_ci	}
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci	exynos_ufs_priv_init(hba, ufs);
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	if (ufs->drv_data->drv_init) {
117962306a36Sopenharmony_ci		ret = ufs->drv_data->drv_init(dev, ufs);
118062306a36Sopenharmony_ci		if (ret) {
118162306a36Sopenharmony_ci			dev_err(dev, "failed to init drv-data\n");
118262306a36Sopenharmony_ci			goto out;
118362306a36Sopenharmony_ci		}
118462306a36Sopenharmony_ci	}
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	ret = exynos_ufs_get_clk_info(ufs);
118762306a36Sopenharmony_ci	if (ret)
118862306a36Sopenharmony_ci		goto out;
118962306a36Sopenharmony_ci	exynos_ufs_specify_phy_time_attr(ufs);
119062306a36Sopenharmony_ci	exynos_ufs_config_smu(ufs);
119162306a36Sopenharmony_ci	return 0;
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ciout:
119462306a36Sopenharmony_ci	hba->priv = NULL;
119562306a36Sopenharmony_ci	return ret;
119662306a36Sopenharmony_ci}
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_cistatic int exynos_ufs_host_reset(struct ufs_hba *hba)
119962306a36Sopenharmony_ci{
120062306a36Sopenharmony_ci	struct exynos_ufs *ufs = ufshcd_get_variant(hba);
120162306a36Sopenharmony_ci	unsigned long timeout = jiffies + msecs_to_jiffies(1);
120262306a36Sopenharmony_ci	u32 val;
120362306a36Sopenharmony_ci	int ret = 0;
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	exynos_ufs_disable_auto_ctrl_hcc_save(ufs, &val);
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci	hci_writel(ufs, UFS_SW_RST_MASK, HCI_SW_RST);
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	do {
121062306a36Sopenharmony_ci		if (!(hci_readl(ufs, HCI_SW_RST) & UFS_SW_RST_MASK))
121162306a36Sopenharmony_ci			goto out;
121262306a36Sopenharmony_ci	} while (time_before(jiffies, timeout));
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	dev_err(hba->dev, "timeout host sw-reset\n");
121562306a36Sopenharmony_ci	ret = -ETIMEDOUT;
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ciout:
121862306a36Sopenharmony_ci	exynos_ufs_auto_ctrl_hcc_restore(ufs, &val);
121962306a36Sopenharmony_ci	return ret;
122062306a36Sopenharmony_ci}
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_cistatic void exynos_ufs_dev_hw_reset(struct ufs_hba *hba)
122362306a36Sopenharmony_ci{
122462306a36Sopenharmony_ci	struct exynos_ufs *ufs = ufshcd_get_variant(hba);
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci	hci_writel(ufs, 0 << 0, HCI_GPIO_OUT);
122762306a36Sopenharmony_ci	udelay(5);
122862306a36Sopenharmony_ci	hci_writel(ufs, 1 << 0, HCI_GPIO_OUT);
122962306a36Sopenharmony_ci}
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_cistatic void exynos_ufs_pre_hibern8(struct ufs_hba *hba, u8 enter)
123262306a36Sopenharmony_ci{
123362306a36Sopenharmony_ci	struct exynos_ufs *ufs = ufshcd_get_variant(hba);
123462306a36Sopenharmony_ci	struct exynos_ufs_uic_attr *attr = ufs->drv_data->uic_attr;
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	if (!enter) {
123762306a36Sopenharmony_ci		if (ufs->opts & EXYNOS_UFS_OPT_BROKEN_AUTO_CLK_CTRL)
123862306a36Sopenharmony_ci			exynos_ufs_disable_auto_ctrl_hcc(ufs);
123962306a36Sopenharmony_ci		exynos_ufs_ungate_clks(ufs);
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci		if (ufs->opts & EXYNOS_UFS_OPT_USE_SW_HIBERN8_TIMER) {
124262306a36Sopenharmony_ci			static const unsigned int granularity_tbl[] = {
124362306a36Sopenharmony_ci				1, 4, 8, 16, 32, 100
124462306a36Sopenharmony_ci			};
124562306a36Sopenharmony_ci			int h8_time = attr->pa_hibern8time *
124662306a36Sopenharmony_ci				granularity_tbl[attr->pa_granularity - 1];
124762306a36Sopenharmony_ci			unsigned long us;
124862306a36Sopenharmony_ci			s64 delta;
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci			do {
125162306a36Sopenharmony_ci				delta = h8_time - ktime_us_delta(ktime_get(),
125262306a36Sopenharmony_ci							ufs->entry_hibern8_t);
125362306a36Sopenharmony_ci				if (delta <= 0)
125462306a36Sopenharmony_ci					break;
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci				us = min_t(s64, delta, USEC_PER_MSEC);
125762306a36Sopenharmony_ci				if (us >= 10)
125862306a36Sopenharmony_ci					usleep_range(us, us + 10);
125962306a36Sopenharmony_ci			} while (1);
126062306a36Sopenharmony_ci		}
126162306a36Sopenharmony_ci	}
126262306a36Sopenharmony_ci}
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_cistatic void exynos_ufs_post_hibern8(struct ufs_hba *hba, u8 enter)
126562306a36Sopenharmony_ci{
126662306a36Sopenharmony_ci	struct exynos_ufs *ufs = ufshcd_get_variant(hba);
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	if (!enter) {
126962306a36Sopenharmony_ci		u32 cur_mode = 0;
127062306a36Sopenharmony_ci		u32 pwrmode;
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci		if (ufshcd_is_hs_mode(&ufs->dev_req_params))
127362306a36Sopenharmony_ci			pwrmode = FAST_MODE;
127462306a36Sopenharmony_ci		else
127562306a36Sopenharmony_ci			pwrmode = SLOW_MODE;
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci		ufshcd_dme_get(hba, UIC_ARG_MIB(PA_PWRMODE), &cur_mode);
127862306a36Sopenharmony_ci		if (cur_mode != (pwrmode << 4 | pwrmode)) {
127962306a36Sopenharmony_ci			dev_warn(hba->dev, "%s: power mode change\n", __func__);
128062306a36Sopenharmony_ci			hba->pwr_info.pwr_rx = (cur_mode >> 4) & 0xf;
128162306a36Sopenharmony_ci			hba->pwr_info.pwr_tx = cur_mode & 0xf;
128262306a36Sopenharmony_ci			ufshcd_config_pwr_mode(hba, &hba->max_pwr_info.info);
128362306a36Sopenharmony_ci		}
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci		if (!(ufs->opts & EXYNOS_UFS_OPT_SKIP_CONNECTION_ESTAB))
128662306a36Sopenharmony_ci			exynos_ufs_establish_connt(ufs);
128762306a36Sopenharmony_ci	} else {
128862306a36Sopenharmony_ci		ufs->entry_hibern8_t = ktime_get();
128962306a36Sopenharmony_ci		exynos_ufs_gate_clks(ufs);
129062306a36Sopenharmony_ci		if (ufs->opts & EXYNOS_UFS_OPT_BROKEN_AUTO_CLK_CTRL)
129162306a36Sopenharmony_ci			exynos_ufs_enable_auto_ctrl_hcc(ufs);
129262306a36Sopenharmony_ci	}
129362306a36Sopenharmony_ci}
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_cistatic int exynos_ufs_hce_enable_notify(struct ufs_hba *hba,
129662306a36Sopenharmony_ci					enum ufs_notify_change_status status)
129762306a36Sopenharmony_ci{
129862306a36Sopenharmony_ci	struct exynos_ufs *ufs = ufshcd_get_variant(hba);
129962306a36Sopenharmony_ci	int ret = 0;
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	switch (status) {
130262306a36Sopenharmony_ci	case PRE_CHANGE:
130362306a36Sopenharmony_ci		/*
130462306a36Sopenharmony_ci		 * The maximum segment size must be set after scsi_host_alloc()
130562306a36Sopenharmony_ci		 * has been called and before LUN scanning starts
130662306a36Sopenharmony_ci		 * (ufshcd_async_scan()). Note: this callback may also be called
130762306a36Sopenharmony_ci		 * from other functions than ufshcd_init().
130862306a36Sopenharmony_ci		 */
130962306a36Sopenharmony_ci		hba->host->max_segment_size = SZ_4K;
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci		if (ufs->drv_data->pre_hce_enable) {
131262306a36Sopenharmony_ci			ret = ufs->drv_data->pre_hce_enable(ufs);
131362306a36Sopenharmony_ci			if (ret)
131462306a36Sopenharmony_ci				return ret;
131562306a36Sopenharmony_ci		}
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci		ret = exynos_ufs_host_reset(hba);
131862306a36Sopenharmony_ci		if (ret)
131962306a36Sopenharmony_ci			return ret;
132062306a36Sopenharmony_ci		exynos_ufs_dev_hw_reset(hba);
132162306a36Sopenharmony_ci		break;
132262306a36Sopenharmony_ci	case POST_CHANGE:
132362306a36Sopenharmony_ci		exynos_ufs_calc_pwm_clk_div(ufs);
132462306a36Sopenharmony_ci		if (!(ufs->opts & EXYNOS_UFS_OPT_BROKEN_AUTO_CLK_CTRL))
132562306a36Sopenharmony_ci			exynos_ufs_enable_auto_ctrl_hcc(ufs);
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci		if (ufs->drv_data->post_hce_enable)
132862306a36Sopenharmony_ci			ret = ufs->drv_data->post_hce_enable(ufs);
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci		break;
133162306a36Sopenharmony_ci	}
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	return ret;
133462306a36Sopenharmony_ci}
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_cistatic int exynos_ufs_link_startup_notify(struct ufs_hba *hba,
133762306a36Sopenharmony_ci					  enum ufs_notify_change_status status)
133862306a36Sopenharmony_ci{
133962306a36Sopenharmony_ci	int ret = 0;
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci	switch (status) {
134262306a36Sopenharmony_ci	case PRE_CHANGE:
134362306a36Sopenharmony_ci		ret = exynos_ufs_pre_link(hba);
134462306a36Sopenharmony_ci		break;
134562306a36Sopenharmony_ci	case POST_CHANGE:
134662306a36Sopenharmony_ci		ret = exynos_ufs_post_link(hba);
134762306a36Sopenharmony_ci		break;
134862306a36Sopenharmony_ci	}
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci	return ret;
135162306a36Sopenharmony_ci}
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_cistatic int exynos_ufs_pwr_change_notify(struct ufs_hba *hba,
135462306a36Sopenharmony_ci				enum ufs_notify_change_status status,
135562306a36Sopenharmony_ci				struct ufs_pa_layer_attr *dev_max_params,
135662306a36Sopenharmony_ci				struct ufs_pa_layer_attr *dev_req_params)
135762306a36Sopenharmony_ci{
135862306a36Sopenharmony_ci	int ret = 0;
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci	switch (status) {
136162306a36Sopenharmony_ci	case PRE_CHANGE:
136262306a36Sopenharmony_ci		ret = exynos_ufs_pre_pwr_mode(hba, dev_max_params,
136362306a36Sopenharmony_ci					      dev_req_params);
136462306a36Sopenharmony_ci		break;
136562306a36Sopenharmony_ci	case POST_CHANGE:
136662306a36Sopenharmony_ci		ret = exynos_ufs_post_pwr_mode(hba, dev_req_params);
136762306a36Sopenharmony_ci		break;
136862306a36Sopenharmony_ci	}
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ci	return ret;
137162306a36Sopenharmony_ci}
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_cistatic void exynos_ufs_hibern8_notify(struct ufs_hba *hba,
137462306a36Sopenharmony_ci				     enum uic_cmd_dme enter,
137562306a36Sopenharmony_ci				     enum ufs_notify_change_status notify)
137662306a36Sopenharmony_ci{
137762306a36Sopenharmony_ci	switch ((u8)notify) {
137862306a36Sopenharmony_ci	case PRE_CHANGE:
137962306a36Sopenharmony_ci		exynos_ufs_pre_hibern8(hba, enter);
138062306a36Sopenharmony_ci		break;
138162306a36Sopenharmony_ci	case POST_CHANGE:
138262306a36Sopenharmony_ci		exynos_ufs_post_hibern8(hba, enter);
138362306a36Sopenharmony_ci		break;
138462306a36Sopenharmony_ci	}
138562306a36Sopenharmony_ci}
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_cistatic int exynos_ufs_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op,
138862306a36Sopenharmony_ci	enum ufs_notify_change_status status)
138962306a36Sopenharmony_ci{
139062306a36Sopenharmony_ci	struct exynos_ufs *ufs = ufshcd_get_variant(hba);
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci	if (status == PRE_CHANGE)
139362306a36Sopenharmony_ci		return 0;
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	if (!ufshcd_is_link_active(hba))
139662306a36Sopenharmony_ci		phy_power_off(ufs->phy);
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci	return 0;
139962306a36Sopenharmony_ci}
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_cistatic int exynos_ufs_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
140262306a36Sopenharmony_ci{
140362306a36Sopenharmony_ci	struct exynos_ufs *ufs = ufshcd_get_variant(hba);
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_ci	if (!ufshcd_is_link_active(hba))
140662306a36Sopenharmony_ci		phy_power_on(ufs->phy);
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci	exynos_ufs_config_smu(ufs);
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci	return 0;
141162306a36Sopenharmony_ci}
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_cistatic int exynosauto_ufs_vh_link_startup_notify(struct ufs_hba *hba,
141462306a36Sopenharmony_ci						 enum ufs_notify_change_status status)
141562306a36Sopenharmony_ci{
141662306a36Sopenharmony_ci	if (status == POST_CHANGE) {
141762306a36Sopenharmony_ci		ufshcd_set_link_active(hba);
141862306a36Sopenharmony_ci		ufshcd_set_ufs_dev_active(hba);
141962306a36Sopenharmony_ci	}
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci	return 0;
142262306a36Sopenharmony_ci}
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_cistatic int exynosauto_ufs_vh_wait_ph_ready(struct ufs_hba *hba)
142562306a36Sopenharmony_ci{
142662306a36Sopenharmony_ci	u32 mbox;
142762306a36Sopenharmony_ci	ktime_t start, stop;
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci	start = ktime_get();
143062306a36Sopenharmony_ci	stop = ktime_add(start, ms_to_ktime(PH_READY_TIMEOUT_MS));
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	do {
143362306a36Sopenharmony_ci		mbox = ufshcd_readl(hba, PH2VH_MBOX);
143462306a36Sopenharmony_ci		/* TODO: Mailbox message protocols between the PH and VHs are
143562306a36Sopenharmony_ci		 * not implemented yet. This will be supported later
143662306a36Sopenharmony_ci		 */
143762306a36Sopenharmony_ci		if ((mbox & MH_MSG_MASK) == MH_MSG_PH_READY)
143862306a36Sopenharmony_ci			return 0;
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci		usleep_range(40, 50);
144162306a36Sopenharmony_ci	} while (ktime_before(ktime_get(), stop));
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci	return -ETIME;
144462306a36Sopenharmony_ci}
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_cistatic int exynosauto_ufs_vh_init(struct ufs_hba *hba)
144762306a36Sopenharmony_ci{
144862306a36Sopenharmony_ci	struct device *dev = hba->dev;
144962306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
145062306a36Sopenharmony_ci	struct exynos_ufs *ufs;
145162306a36Sopenharmony_ci	int ret;
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci	ufs = devm_kzalloc(dev, sizeof(*ufs), GFP_KERNEL);
145462306a36Sopenharmony_ci	if (!ufs)
145562306a36Sopenharmony_ci		return -ENOMEM;
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	/* exynos-specific hci */
145862306a36Sopenharmony_ci	ufs->reg_hci = devm_platform_ioremap_resource_byname(pdev, "vs_hci");
145962306a36Sopenharmony_ci	if (IS_ERR(ufs->reg_hci)) {
146062306a36Sopenharmony_ci		dev_err(dev, "cannot ioremap for hci vendor register\n");
146162306a36Sopenharmony_ci		return PTR_ERR(ufs->reg_hci);
146262306a36Sopenharmony_ci	}
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci	ret = exynosauto_ufs_vh_wait_ph_ready(hba);
146562306a36Sopenharmony_ci	if (ret)
146662306a36Sopenharmony_ci		return ret;
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci	ufs->drv_data = device_get_match_data(dev);
146962306a36Sopenharmony_ci	if (!ufs->drv_data)
147062306a36Sopenharmony_ci		return -ENODEV;
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci	exynos_ufs_priv_init(hba, ufs);
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_ci	return 0;
147562306a36Sopenharmony_ci}
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_cistatic int fsd_ufs_pre_link(struct exynos_ufs *ufs)
147862306a36Sopenharmony_ci{
147962306a36Sopenharmony_ci	int i;
148062306a36Sopenharmony_ci	struct ufs_hba *hba = ufs->hba;
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_DBG_CLK_PERIOD),
148362306a36Sopenharmony_ci		       DIV_ROUND_UP(NSEC_PER_SEC,  ufs->mclk_rate));
148462306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(0x201), 0x12);
148562306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(0x200), 0x40);
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci	for_each_ufs_tx_lane(ufs, i) {
148862306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0xAA, i),
148962306a36Sopenharmony_ci			       DIV_ROUND_UP(NSEC_PER_SEC, ufs->mclk_rate));
149062306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x8F, i), 0x3F);
149162306a36Sopenharmony_ci	}
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci	for_each_ufs_rx_lane(ufs, i) {
149462306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x12, i),
149562306a36Sopenharmony_ci			       DIV_ROUND_UP(NSEC_PER_SEC, ufs->mclk_rate));
149662306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x5C, i), 0x38);
149762306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x0F, i), 0x0);
149862306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x65, i), 0x1);
149962306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x69, i), 0x1);
150062306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x21, i), 0x0);
150162306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x22, i), 0x0);
150262306a36Sopenharmony_ci	}
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(0x200), 0x0);
150562306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_DBG_AUTOMODE_THLD), 0x4E20);
150662306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_DBG_OPTION_SUITE), 0x2e820183);
150762306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_LOCAL_TX_LCC_ENABLE), 0x0);
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci	exynos_ufs_establish_connt(ufs);
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_ci	return 0;
151262306a36Sopenharmony_ci}
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_cistatic int fsd_ufs_post_link(struct exynos_ufs *ufs)
151562306a36Sopenharmony_ci{
151662306a36Sopenharmony_ci	int i;
151762306a36Sopenharmony_ci	struct ufs_hba *hba = ufs->hba;
151862306a36Sopenharmony_ci	u32 hw_cap_min_tactivate;
151962306a36Sopenharmony_ci	u32 peer_rx_min_actv_time_cap;
152062306a36Sopenharmony_ci	u32 max_rx_hibern8_time_cap;
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci	ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(0x8F, 4),
152362306a36Sopenharmony_ci			&hw_cap_min_tactivate); /* HW Capability of MIN_TACTIVATE */
152462306a36Sopenharmony_ci	ufshcd_dme_get(hba, UIC_ARG_MIB(PA_TACTIVATE),
152562306a36Sopenharmony_ci			&peer_rx_min_actv_time_cap);    /* PA_TActivate */
152662306a36Sopenharmony_ci	ufshcd_dme_get(hba, UIC_ARG_MIB(PA_HIBERN8TIME),
152762306a36Sopenharmony_ci			&max_rx_hibern8_time_cap);      /* PA_Hibern8Time */
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	if (peer_rx_min_actv_time_cap >= hw_cap_min_tactivate)
153062306a36Sopenharmony_ci		ufshcd_dme_peer_set(hba, UIC_ARG_MIB(PA_TACTIVATE),
153162306a36Sopenharmony_ci					peer_rx_min_actv_time_cap + 1);
153262306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_HIBERN8TIME), max_rx_hibern8_time_cap + 1);
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_DBG_MODE), 0x01);
153562306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_SAVECONFIGTIME), 0xFA);
153662306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_DBG_MODE), 0x00);
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(0x200), 0x40);
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci	for_each_ufs_rx_lane(ufs, i) {
154162306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x35, i), 0x05);
154262306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x73, i), 0x01);
154362306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x41, i), 0x02);
154462306a36Sopenharmony_ci		ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x42, i), 0xAC);
154562306a36Sopenharmony_ci	}
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(0x200), 0x0);
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci	return 0;
155062306a36Sopenharmony_ci}
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_cistatic int fsd_ufs_pre_pwr_change(struct exynos_ufs *ufs,
155362306a36Sopenharmony_ci					struct ufs_pa_layer_attr *pwr)
155462306a36Sopenharmony_ci{
155562306a36Sopenharmony_ci	struct ufs_hba *hba = ufs->hba;
155662306a36Sopenharmony_ci
155762306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXTERMINATION), 0x1);
155862306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_RXTERMINATION), 0x1);
155962306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA0), 12000);
156062306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA1), 32000);
156162306a36Sopenharmony_ci	ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA2), 16000);
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_ci	unipro_writel(ufs, 12000, UNIPRO_DME_POWERMODE_REQ_REMOTEL2TIMER0);
156462306a36Sopenharmony_ci	unipro_writel(ufs, 32000, UNIPRO_DME_POWERMODE_REQ_REMOTEL2TIMER1);
156562306a36Sopenharmony_ci	unipro_writel(ufs, 16000, UNIPRO_DME_POWERMODE_REQ_REMOTEL2TIMER2);
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci	return 0;
156862306a36Sopenharmony_ci}
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_cistatic const struct ufs_hba_variant_ops ufs_hba_exynos_ops = {
157162306a36Sopenharmony_ci	.name				= "exynos_ufs",
157262306a36Sopenharmony_ci	.init				= exynos_ufs_init,
157362306a36Sopenharmony_ci	.hce_enable_notify		= exynos_ufs_hce_enable_notify,
157462306a36Sopenharmony_ci	.link_startup_notify		= exynos_ufs_link_startup_notify,
157562306a36Sopenharmony_ci	.pwr_change_notify		= exynos_ufs_pwr_change_notify,
157662306a36Sopenharmony_ci	.setup_clocks			= exynos_ufs_setup_clocks,
157762306a36Sopenharmony_ci	.setup_xfer_req			= exynos_ufs_specify_nexus_t_xfer_req,
157862306a36Sopenharmony_ci	.setup_task_mgmt		= exynos_ufs_specify_nexus_t_tm_req,
157962306a36Sopenharmony_ci	.hibern8_notify			= exynos_ufs_hibern8_notify,
158062306a36Sopenharmony_ci	.suspend			= exynos_ufs_suspend,
158162306a36Sopenharmony_ci	.resume				= exynos_ufs_resume,
158262306a36Sopenharmony_ci};
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_cistatic struct ufs_hba_variant_ops ufs_hba_exynosauto_vh_ops = {
158562306a36Sopenharmony_ci	.name				= "exynosauto_ufs_vh",
158662306a36Sopenharmony_ci	.init				= exynosauto_ufs_vh_init,
158762306a36Sopenharmony_ci	.link_startup_notify		= exynosauto_ufs_vh_link_startup_notify,
158862306a36Sopenharmony_ci};
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_cistatic int exynos_ufs_probe(struct platform_device *pdev)
159162306a36Sopenharmony_ci{
159262306a36Sopenharmony_ci	int err;
159362306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
159462306a36Sopenharmony_ci	const struct ufs_hba_variant_ops *vops = &ufs_hba_exynos_ops;
159562306a36Sopenharmony_ci	const struct exynos_ufs_drv_data *drv_data =
159662306a36Sopenharmony_ci		device_get_match_data(dev);
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_ci	if (drv_data && drv_data->vops)
159962306a36Sopenharmony_ci		vops = drv_data->vops;
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci	err = ufshcd_pltfrm_init(pdev, vops);
160262306a36Sopenharmony_ci	if (err)
160362306a36Sopenharmony_ci		dev_err(dev, "ufshcd_pltfrm_init() failed %d\n", err);
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_ci	return err;
160662306a36Sopenharmony_ci}
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_cistatic int exynos_ufs_remove(struct platform_device *pdev)
160962306a36Sopenharmony_ci{
161062306a36Sopenharmony_ci	struct ufs_hba *hba =  platform_get_drvdata(pdev);
161162306a36Sopenharmony_ci	struct exynos_ufs *ufs = ufshcd_get_variant(hba);
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci	pm_runtime_get_sync(&(pdev)->dev);
161462306a36Sopenharmony_ci	ufshcd_remove(hba);
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_ci	phy_power_off(ufs->phy);
161762306a36Sopenharmony_ci	phy_exit(ufs->phy);
161862306a36Sopenharmony_ci
161962306a36Sopenharmony_ci	return 0;
162062306a36Sopenharmony_ci}
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_cistatic struct exynos_ufs_uic_attr exynos7_uic_attr = {
162362306a36Sopenharmony_ci	.tx_trailingclks		= 0x10,
162462306a36Sopenharmony_ci	.tx_dif_p_nsec			= 3000000,	/* unit: ns */
162562306a36Sopenharmony_ci	.tx_dif_n_nsec			= 1000000,	/* unit: ns */
162662306a36Sopenharmony_ci	.tx_high_z_cnt_nsec		= 20000,	/* unit: ns */
162762306a36Sopenharmony_ci	.tx_base_unit_nsec		= 100000,	/* unit: ns */
162862306a36Sopenharmony_ci	.tx_gran_unit_nsec		= 4000,		/* unit: ns */
162962306a36Sopenharmony_ci	.tx_sleep_cnt			= 1000,		/* unit: ns */
163062306a36Sopenharmony_ci	.tx_min_activatetime		= 0xa,
163162306a36Sopenharmony_ci	.rx_filler_enable		= 0x2,
163262306a36Sopenharmony_ci	.rx_dif_p_nsec			= 1000000,	/* unit: ns */
163362306a36Sopenharmony_ci	.rx_hibern8_wait_nsec		= 4000000,	/* unit: ns */
163462306a36Sopenharmony_ci	.rx_base_unit_nsec		= 100000,	/* unit: ns */
163562306a36Sopenharmony_ci	.rx_gran_unit_nsec		= 4000,		/* unit: ns */
163662306a36Sopenharmony_ci	.rx_sleep_cnt			= 1280,		/* unit: ns */
163762306a36Sopenharmony_ci	.rx_stall_cnt			= 320,		/* unit: ns */
163862306a36Sopenharmony_ci	.rx_hs_g1_sync_len_cap		= SYNC_LEN_COARSE(0xf),
163962306a36Sopenharmony_ci	.rx_hs_g2_sync_len_cap		= SYNC_LEN_COARSE(0xf),
164062306a36Sopenharmony_ci	.rx_hs_g3_sync_len_cap		= SYNC_LEN_COARSE(0xf),
164162306a36Sopenharmony_ci	.rx_hs_g1_prep_sync_len_cap	= PREP_LEN(0xf),
164262306a36Sopenharmony_ci	.rx_hs_g2_prep_sync_len_cap	= PREP_LEN(0xf),
164362306a36Sopenharmony_ci	.rx_hs_g3_prep_sync_len_cap	= PREP_LEN(0xf),
164462306a36Sopenharmony_ci	.pa_dbg_option_suite		= 0x30103,
164562306a36Sopenharmony_ci};
164662306a36Sopenharmony_ci
164762306a36Sopenharmony_cistatic const struct exynos_ufs_drv_data exynosauto_ufs_drvs = {
164862306a36Sopenharmony_ci	.uic_attr		= &exynos7_uic_attr,
164962306a36Sopenharmony_ci	.quirks			= UFSHCD_QUIRK_PRDT_BYTE_GRAN |
165062306a36Sopenharmony_ci				  UFSHCI_QUIRK_SKIP_RESET_INTR_AGGR |
165162306a36Sopenharmony_ci				  UFSHCD_QUIRK_BROKEN_OCS_FATAL_ERROR |
165262306a36Sopenharmony_ci				  UFSHCD_QUIRK_SKIP_DEF_UNIPRO_TIMEOUT_SETTING,
165362306a36Sopenharmony_ci	.opts			= EXYNOS_UFS_OPT_BROKEN_AUTO_CLK_CTRL |
165462306a36Sopenharmony_ci				  EXYNOS_UFS_OPT_SKIP_CONFIG_PHY_ATTR |
165562306a36Sopenharmony_ci				  EXYNOS_UFS_OPT_BROKEN_RX_SEL_IDX,
165662306a36Sopenharmony_ci	.drv_init		= exynosauto_ufs_drv_init,
165762306a36Sopenharmony_ci	.post_hce_enable	= exynosauto_ufs_post_hce_enable,
165862306a36Sopenharmony_ci	.pre_link		= exynosauto_ufs_pre_link,
165962306a36Sopenharmony_ci	.pre_pwr_change		= exynosauto_ufs_pre_pwr_change,
166062306a36Sopenharmony_ci	.post_pwr_change	= exynosauto_ufs_post_pwr_change,
166162306a36Sopenharmony_ci};
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_cistatic const struct exynos_ufs_drv_data exynosauto_ufs_vh_drvs = {
166462306a36Sopenharmony_ci	.vops			= &ufs_hba_exynosauto_vh_ops,
166562306a36Sopenharmony_ci	.quirks			= UFSHCD_QUIRK_PRDT_BYTE_GRAN |
166662306a36Sopenharmony_ci				  UFSHCI_QUIRK_SKIP_RESET_INTR_AGGR |
166762306a36Sopenharmony_ci				  UFSHCD_QUIRK_BROKEN_OCS_FATAL_ERROR |
166862306a36Sopenharmony_ci				  UFSHCI_QUIRK_BROKEN_HCE |
166962306a36Sopenharmony_ci				  UFSHCD_QUIRK_BROKEN_UIC_CMD |
167062306a36Sopenharmony_ci				  UFSHCD_QUIRK_SKIP_PH_CONFIGURATION |
167162306a36Sopenharmony_ci				  UFSHCD_QUIRK_SKIP_DEF_UNIPRO_TIMEOUT_SETTING,
167262306a36Sopenharmony_ci	.opts			= EXYNOS_UFS_OPT_BROKEN_RX_SEL_IDX,
167362306a36Sopenharmony_ci};
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_cistatic const struct exynos_ufs_drv_data exynos_ufs_drvs = {
167662306a36Sopenharmony_ci	.uic_attr		= &exynos7_uic_attr,
167762306a36Sopenharmony_ci	.quirks			= UFSHCD_QUIRK_PRDT_BYTE_GRAN |
167862306a36Sopenharmony_ci				  UFSHCI_QUIRK_BROKEN_REQ_LIST_CLR |
167962306a36Sopenharmony_ci				  UFSHCI_QUIRK_BROKEN_HCE |
168062306a36Sopenharmony_ci				  UFSHCI_QUIRK_SKIP_RESET_INTR_AGGR |
168162306a36Sopenharmony_ci				  UFSHCD_QUIRK_BROKEN_OCS_FATAL_ERROR |
168262306a36Sopenharmony_ci				  UFSHCI_QUIRK_SKIP_MANUAL_WB_FLUSH_CTRL |
168362306a36Sopenharmony_ci				  UFSHCD_QUIRK_SKIP_DEF_UNIPRO_TIMEOUT_SETTING |
168462306a36Sopenharmony_ci				  UFSHCD_QUIRK_4KB_DMA_ALIGNMENT,
168562306a36Sopenharmony_ci	.opts			= EXYNOS_UFS_OPT_HAS_APB_CLK_CTRL |
168662306a36Sopenharmony_ci				  EXYNOS_UFS_OPT_BROKEN_AUTO_CLK_CTRL |
168762306a36Sopenharmony_ci				  EXYNOS_UFS_OPT_BROKEN_RX_SEL_IDX |
168862306a36Sopenharmony_ci				  EXYNOS_UFS_OPT_SKIP_CONNECTION_ESTAB |
168962306a36Sopenharmony_ci				  EXYNOS_UFS_OPT_USE_SW_HIBERN8_TIMER,
169062306a36Sopenharmony_ci	.drv_init		= exynos7_ufs_drv_init,
169162306a36Sopenharmony_ci	.pre_link		= exynos7_ufs_pre_link,
169262306a36Sopenharmony_ci	.post_link		= exynos7_ufs_post_link,
169362306a36Sopenharmony_ci	.pre_pwr_change		= exynos7_ufs_pre_pwr_change,
169462306a36Sopenharmony_ci	.post_pwr_change	= exynos7_ufs_post_pwr_change,
169562306a36Sopenharmony_ci};
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_cistatic struct exynos_ufs_uic_attr fsd_uic_attr = {
169862306a36Sopenharmony_ci	.tx_trailingclks		= 0x10,
169962306a36Sopenharmony_ci	.tx_dif_p_nsec			= 3000000,	/* unit: ns */
170062306a36Sopenharmony_ci	.tx_dif_n_nsec			= 1000000,	/* unit: ns */
170162306a36Sopenharmony_ci	.tx_high_z_cnt_nsec		= 20000,	/* unit: ns */
170262306a36Sopenharmony_ci	.tx_base_unit_nsec		= 100000,	/* unit: ns */
170362306a36Sopenharmony_ci	.tx_gran_unit_nsec		= 4000,		/* unit: ns */
170462306a36Sopenharmony_ci	.tx_sleep_cnt			= 1000,		/* unit: ns */
170562306a36Sopenharmony_ci	.tx_min_activatetime		= 0xa,
170662306a36Sopenharmony_ci	.rx_filler_enable		= 0x2,
170762306a36Sopenharmony_ci	.rx_dif_p_nsec			= 1000000,	/* unit: ns */
170862306a36Sopenharmony_ci	.rx_hibern8_wait_nsec		= 4000000,	/* unit: ns */
170962306a36Sopenharmony_ci	.rx_base_unit_nsec		= 100000,	/* unit: ns */
171062306a36Sopenharmony_ci	.rx_gran_unit_nsec		= 4000,		/* unit: ns */
171162306a36Sopenharmony_ci	.rx_sleep_cnt			= 1280,		/* unit: ns */
171262306a36Sopenharmony_ci	.rx_stall_cnt			= 320,		/* unit: ns */
171362306a36Sopenharmony_ci	.rx_hs_g1_sync_len_cap		= SYNC_LEN_COARSE(0xf),
171462306a36Sopenharmony_ci	.rx_hs_g2_sync_len_cap		= SYNC_LEN_COARSE(0xf),
171562306a36Sopenharmony_ci	.rx_hs_g3_sync_len_cap		= SYNC_LEN_COARSE(0xf),
171662306a36Sopenharmony_ci	.rx_hs_g1_prep_sync_len_cap	= PREP_LEN(0xf),
171762306a36Sopenharmony_ci	.rx_hs_g2_prep_sync_len_cap	= PREP_LEN(0xf),
171862306a36Sopenharmony_ci	.rx_hs_g3_prep_sync_len_cap	= PREP_LEN(0xf),
171962306a36Sopenharmony_ci	.pa_dbg_option_suite		= 0x2E820183,
172062306a36Sopenharmony_ci};
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_cistatic const struct exynos_ufs_drv_data fsd_ufs_drvs = {
172362306a36Sopenharmony_ci	.uic_attr               = &fsd_uic_attr,
172462306a36Sopenharmony_ci	.quirks                 = UFSHCD_QUIRK_PRDT_BYTE_GRAN |
172562306a36Sopenharmony_ci				  UFSHCI_QUIRK_BROKEN_REQ_LIST_CLR |
172662306a36Sopenharmony_ci				  UFSHCD_QUIRK_BROKEN_OCS_FATAL_ERROR |
172762306a36Sopenharmony_ci				  UFSHCD_QUIRK_SKIP_DEF_UNIPRO_TIMEOUT_SETTING |
172862306a36Sopenharmony_ci				  UFSHCI_QUIRK_SKIP_RESET_INTR_AGGR,
172962306a36Sopenharmony_ci	.opts                   = EXYNOS_UFS_OPT_HAS_APB_CLK_CTRL |
173062306a36Sopenharmony_ci				  EXYNOS_UFS_OPT_BROKEN_AUTO_CLK_CTRL |
173162306a36Sopenharmony_ci				  EXYNOS_UFS_OPT_SKIP_CONFIG_PHY_ATTR |
173262306a36Sopenharmony_ci				  EXYNOS_UFS_OPT_BROKEN_RX_SEL_IDX,
173362306a36Sopenharmony_ci	.pre_link               = fsd_ufs_pre_link,
173462306a36Sopenharmony_ci	.post_link              = fsd_ufs_post_link,
173562306a36Sopenharmony_ci	.pre_pwr_change         = fsd_ufs_pre_pwr_change,
173662306a36Sopenharmony_ci};
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_cistatic const struct of_device_id exynos_ufs_of_match[] = {
173962306a36Sopenharmony_ci	{ .compatible = "samsung,exynos7-ufs",
174062306a36Sopenharmony_ci	  .data	      = &exynos_ufs_drvs },
174162306a36Sopenharmony_ci	{ .compatible = "samsung,exynosautov9-ufs",
174262306a36Sopenharmony_ci	  .data	      = &exynosauto_ufs_drvs },
174362306a36Sopenharmony_ci	{ .compatible = "samsung,exynosautov9-ufs-vh",
174462306a36Sopenharmony_ci	  .data	      = &exynosauto_ufs_vh_drvs },
174562306a36Sopenharmony_ci	{ .compatible = "tesla,fsd-ufs",
174662306a36Sopenharmony_ci	  .data       = &fsd_ufs_drvs },
174762306a36Sopenharmony_ci	{},
174862306a36Sopenharmony_ci};
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_cistatic const struct dev_pm_ops exynos_ufs_pm_ops = {
175162306a36Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(ufshcd_system_suspend, ufshcd_system_resume)
175262306a36Sopenharmony_ci	SET_RUNTIME_PM_OPS(ufshcd_runtime_suspend, ufshcd_runtime_resume, NULL)
175362306a36Sopenharmony_ci	.prepare	 = ufshcd_suspend_prepare,
175462306a36Sopenharmony_ci	.complete	 = ufshcd_resume_complete,
175562306a36Sopenharmony_ci};
175662306a36Sopenharmony_ci
175762306a36Sopenharmony_cistatic struct platform_driver exynos_ufs_pltform = {
175862306a36Sopenharmony_ci	.probe	= exynos_ufs_probe,
175962306a36Sopenharmony_ci	.remove	= exynos_ufs_remove,
176062306a36Sopenharmony_ci	.driver	= {
176162306a36Sopenharmony_ci		.name	= "exynos-ufshc",
176262306a36Sopenharmony_ci		.pm	= &exynos_ufs_pm_ops,
176362306a36Sopenharmony_ci		.of_match_table = exynos_ufs_of_match,
176462306a36Sopenharmony_ci	},
176562306a36Sopenharmony_ci};
176662306a36Sopenharmony_cimodule_platform_driver(exynos_ufs_pltform);
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_ciMODULE_AUTHOR("Alim Akhtar <alim.akhtar@samsung.com>");
176962306a36Sopenharmony_ciMODULE_AUTHOR("Seungwon Jeon  <essuuj@gmail.com>");
177062306a36Sopenharmony_ciMODULE_DESCRIPTION("Exynos UFS HCI Driver");
177162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
1772