aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/drivers/phy/samsung/phy-exynosautov920-ufs.c
blob: 21ef79c42f95d5b94be38e53a6c9629581199b71 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
// SPDX-License-Identifier: GPL-2.0-only
/*
 * UFS PHY driver data for Samsung ExynosAuto v920 SoC
 *
 * Copyright (C) 2024 Samsung Electronics Co., Ltd.
 */

#include "phy-samsung-ufs.h"

#define EXYNOSAUTOV920_EMBEDDED_COMBO_PHY_CTRL			0x708
#define EXYNOSAUTOV920_EMBEDDED_COMBO_PHY_CTRL_MASK		0x1
#define EXYNOSAUTOV920_EMBEDDED_COMBO_PHY_CTRL_EN		BIT(0)
#define EXYNOSAUTOV920_EMBEDDED_COMBO_PHY_CDR_LOCK_STATUS	0x5e

#define EXYNOSAUTOV920_CDR_LOCK_OFFSET				0xce4

#define PHY_EXYNOSAUTOV920_LANE_OFFSET				0x200
#define PHY_TRSV_REG_CFG_AUTOV920(o, v, d) \
	PHY_TRSV_REG_CFG_OFFSET(o, v, d, PHY_EXYNOSAUTOV920_LANE_OFFSET)

/* Calibration for phy initialization */
static const struct samsung_ufs_phy_cfg exynosautov920_pre_init_cfg[] = {
	PHY_COMN_REG_CFG(0x29, 0x22, PWR_MODE_ANY),
	PHY_COMN_REG_CFG(0x43, 0x10, PWR_MODE_ANY),
	PHY_COMN_REG_CFG(0x3c, 0x14, PWR_MODE_ANY),
	PHY_COMN_REG_CFG(0x46, 0x48, PWR_MODE_ANY),
	PHY_COMN_REG_CFG(0x04, 0x95, PWR_MODE_ANY),
	PHY_COMN_REG_CFG(0x06, 0x30, PWR_MODE_ANY),

	PHY_TRSV_REG_CFG_AUTOV920(0x200, 0x00, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x201, 0x06, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x202, 0x06, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x203, 0x0a, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x204, 0x00, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x205, 0x10, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x207, 0x0c, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x2e1, 0xc0, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x22d, 0xf8, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x234, 0x60, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x238, 0x13, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x239, 0x48, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x23a, 0x01, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x23b, 0x29, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x23c, 0x2a, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x23d, 0x01, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x23e, 0x14, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x23f, 0x13, PWR_MODE_ANY),

	PHY_TRSV_REG_CFG_AUTOV920(0x240, 0x4a, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x243, 0x40, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x244, 0x02, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x25d, 0x00, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x25e, 0x3f, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x25f, 0xff, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x26f, 0xf0, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x273, 0x33, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x274, 0x50, PWR_MODE_ANY),

	PHY_TRSV_REG_CFG_AUTOV920(0x284, 0x02, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x285, 0x02, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x2a2, 0x04, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x27d, 0x01, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x2fa, 0x01, PWR_MODE_ANY),

	PHY_TRSV_REG_CFG_AUTOV920(0x286, 0x03, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x287, 0x03, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x288, 0x03, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x289, 0x03, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x2b3, 0x04, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x2b6, 0x0b, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x2b7, 0x0b, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x2b8, 0x0b, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x2b9, 0x0b, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x2ba, 0x0b, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x2bb, 0x06, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x2bc, 0x06, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x2bd, 0x06, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x2be, 0x06, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x34b, 0x01, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x34c, 0x24, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x34d, 0x23, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x34e, 0x45, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x34f, 0x00, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x350, 0x31, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x351, 0x00, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x352, 0x02, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x353, 0x00, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x354, 0x01, PWR_MODE_ANY),

	PHY_COMN_REG_CFG(0x43, 0x18, PWR_MODE_ANY),
	PHY_COMN_REG_CFG(0x43, 0x00, PWR_MODE_ANY),

	END_UFS_PHY_CFG,
};

/* Calibration for HS mode series A/B */
static const struct samsung_ufs_phy_cfg exynosautov920_pre_pwr_hs_cfg[] = {
	PHY_TRSV_REG_CFG_AUTOV920(0x369, 0x11, PWR_MODE_ANY),
	PHY_TRSV_REG_CFG_AUTOV920(0x246, 0x03, PWR_MODE_ANY),

	END_UFS_PHY_CFG,
};

static const struct samsung_ufs_phy_cfg exynosautov920_post_pwr_hs_cfg[] = {
	END_UFS_PHY_CFG,
};

#define DELAY_IN_US	40
#define RETRY_CNT	100
#define EXYNOSAUTOV920_CDR_LOCK_MASK	0x8

int exynosautov920_ufs_phy_wait_cdr_lock(struct phy *phy, u8 lane)
{
	struct samsung_ufs_phy *ufs_phy = get_samsung_ufs_phy(phy);
	u32 reg, i;

	struct samsung_ufs_phy_cfg cfg[4] = {
		PHY_TRSV_REG_CFG_AUTOV920(0x222, 0x10, PWR_MODE_ANY),
		PHY_TRSV_REG_CFG_AUTOV920(0x222, 0x18, PWR_MODE_ANY),
		PHY_TRSV_REG_CFG_AUTOV920(0x246, 0x01, PWR_MODE_ANY),
		END_UFS_PHY_CFG,
	};

	for (i = 0; i < RETRY_CNT; i++) {
		udelay(DELAY_IN_US);

		reg = readl(ufs_phy->reg_pma + EXYNOSAUTOV920_CDR_LOCK_OFFSET +
			(PHY_APB_ADDR(PHY_EXYNOSAUTOV920_LANE_OFFSET) * lane));

		if ((reg & EXYNOSAUTOV920_CDR_LOCK_MASK)
					== EXYNOSAUTOV920_CDR_LOCK_MASK) {
			samsung_ufs_phy_config(ufs_phy, &cfg[2], lane);
			return 0;
		}

		udelay(DELAY_IN_US);

		/* Disable and enable CDR */
		samsung_ufs_phy_config(ufs_phy, &cfg[0], lane);
		samsung_ufs_phy_config(ufs_phy, &cfg[1], lane);
	}

	dev_err(ufs_phy->dev, "failed to get phy cdr lock\n");
	return -ETIMEDOUT;
}

static const struct samsung_ufs_phy_cfg *exynosautov920_ufs_phy_cfgs[CFG_TAG_MAX] = {
	[CFG_PRE_INIT]          = exynosautov920_pre_init_cfg,
	[CFG_PRE_PWR_HS]        = exynosautov920_pre_pwr_hs_cfg,
	[CFG_POST_PWR_HS]       = exynosautov920_post_pwr_hs_cfg,
};

static const char * const exynosautov920_ufs_phy_clks[] = {
	"ref_clk",
};

const struct samsung_ufs_phy_drvdata exynosautov920_ufs_phy = {
	.cfgs = exynosautov920_ufs_phy_cfgs,
	.isol = {
		.offset = EXYNOSAUTOV920_EMBEDDED_COMBO_PHY_CTRL,
		.mask = EXYNOSAUTOV920_EMBEDDED_COMBO_PHY_CTRL_MASK,
		.en = EXYNOSAUTOV920_EMBEDDED_COMBO_PHY_CTRL_EN,
	},
	.clk_list = exynosautov920_ufs_phy_clks,
	.num_clks = ARRAY_SIZE(exynosautov920_ufs_phy_clks),
	.cdr_lock_status_offset = EXYNOSAUTOV920_EMBEDDED_COMBO_PHY_CDR_LOCK_STATUS,
	.wait_for_cdr = exynosautov920_ufs_phy_wait_cdr_lock,
};