1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
2 | /* |
3 | * UFS PHY driver for Samsung EXYNOS SoC |
4 | * |
5 | * Copyright (C) 2020 Samsung Electronics Co., Ltd. |
6 | * Author: Seungwon Jeon <essuuj@gmail.com> |
7 | * Author: Alim Akhtar <alim.akhtar@samsung.com> |
8 | * |
9 | */ |
10 | #ifndef _PHY_SAMSUNG_UFS_ |
11 | #define _PHY_SAMSUNG_UFS_ |
12 | |
13 | #include <linux/phy/phy.h> |
14 | #include <linux/regmap.h> |
15 | |
16 | #define PHY_COMN_BLK 1 |
17 | #define PHY_TRSV_BLK 2 |
18 | #define END_UFS_PHY_CFG { 0 } |
19 | #define PHY_TRSV_CH_OFFSET 0x30 |
20 | #define PHY_APB_ADDR(off) ((off) << 2) |
21 | |
22 | #define PHY_COMN_REG_CFG(o, v, d) { \ |
23 | .off_0 = PHY_APB_ADDR((o)), \ |
24 | .off_1 = 0, \ |
25 | .val = (v), \ |
26 | .desc = (d), \ |
27 | .id = PHY_COMN_BLK, \ |
28 | } |
29 | |
30 | #define PHY_TRSV_REG_CFG_OFFSET(o, v, d, c) { \ |
31 | .off_0 = PHY_APB_ADDR((o)), \ |
32 | .off_1 = PHY_APB_ADDR((o) + (c)), \ |
33 | .val = (v), \ |
34 | .desc = (d), \ |
35 | .id = PHY_TRSV_BLK, \ |
36 | } |
37 | |
38 | #define PHY_TRSV_REG_CFG(o, v, d) \ |
39 | PHY_TRSV_REG_CFG_OFFSET(o, v, d, PHY_TRSV_CH_OFFSET) |
40 | |
41 | /* UFS PHY registers */ |
42 | #define PHY_PLL_LOCK_STATUS 0x1e |
43 | |
44 | #define PHY_PLL_LOCK_BIT BIT(5) |
45 | #define PHY_CDR_LOCK_BIT BIT(4) |
46 | |
47 | /* description for PHY calibration */ |
48 | enum { |
49 | /* applicable to any */ |
50 | PWR_DESC_ANY = 0, |
51 | /* mode */ |
52 | PWR_DESC_PWM = 1, |
53 | PWR_DESC_HS = 2, |
54 | /* series */ |
55 | PWR_DESC_SER_A = 1, |
56 | PWR_DESC_SER_B = 2, |
57 | /* gear */ |
58 | PWR_DESC_G1 = 1, |
59 | PWR_DESC_G2 = 2, |
60 | PWR_DESC_G3 = 3, |
61 | /* field mask */ |
62 | MD_MASK = 0x3, |
63 | SR_MASK = 0x3, |
64 | GR_MASK = 0x7, |
65 | }; |
66 | |
67 | #define PWR_MODE_HS_G1_ANY PWR_MODE_HS(PWR_DESC_G1, PWR_DESC_ANY) |
68 | #define PWR_MODE_HS_G1_SER_A PWR_MODE_HS(PWR_DESC_G1, PWR_DESC_SER_A) |
69 | #define PWR_MODE_HS_G1_SER_B PWR_MODE_HS(PWR_DESC_G1, PWR_DESC_SER_B) |
70 | #define PWR_MODE_HS_G2_ANY PWR_MODE_HS(PWR_DESC_G2, PWR_DESC_ANY) |
71 | #define PWR_MODE_HS_G2_SER_A PWR_MODE_HS(PWR_DESC_G2, PWR_DESC_SER_A) |
72 | #define PWR_MODE_HS_G2_SER_B PWR_MODE_HS(PWR_DESC_G2, PWR_DESC_SER_B) |
73 | #define PWR_MODE_HS_G3_ANY PWR_MODE_HS(PWR_DESC_G3, PWR_DESC_ANY) |
74 | #define PWR_MODE_HS_G3_SER_A PWR_MODE_HS(PWR_DESC_G3, PWR_DESC_SER_A) |
75 | #define PWR_MODE_HS_G3_SER_B PWR_MODE_HS(PWR_DESC_G3, PWR_DESC_SER_B) |
76 | #define PWR_MODE(g, s, m) ((((g) & GR_MASK) << 4) |\ |
77 | (((s) & SR_MASK) << 2) | ((m) & MD_MASK)) |
78 | #define PWR_MODE_PWM_ANY PWR_MODE(PWR_DESC_ANY,\ |
79 | PWR_DESC_ANY, PWR_DESC_PWM) |
80 | #define PWR_MODE_HS(g, s) ((((g) & GR_MASK) << 4) |\ |
81 | (((s) & SR_MASK) << 2) | PWR_DESC_HS) |
82 | #define PWR_MODE_HS_ANY PWR_MODE(PWR_DESC_ANY,\ |
83 | PWR_DESC_ANY, PWR_DESC_HS) |
84 | #define PWR_MODE_ANY PWR_MODE(PWR_DESC_ANY,\ |
85 | PWR_DESC_ANY, PWR_DESC_ANY) |
86 | /* PHY calibration point/state */ |
87 | enum { |
88 | CFG_PRE_INIT, |
89 | CFG_POST_INIT, |
90 | CFG_PRE_PWR_HS, |
91 | CFG_POST_PWR_HS, |
92 | CFG_TAG_MAX, |
93 | }; |
94 | |
95 | struct samsung_ufs_phy_cfg { |
96 | u32 off_0; |
97 | u32 off_1; |
98 | u32 val; |
99 | u8 desc; |
100 | u8 id; |
101 | }; |
102 | |
103 | struct samsung_ufs_phy_pmu_isol { |
104 | u32 offset; |
105 | u32 mask; |
106 | u32 en; |
107 | }; |
108 | |
109 | struct samsung_ufs_phy_drvdata { |
110 | const struct samsung_ufs_phy_cfg **cfgs; |
111 | struct samsung_ufs_phy_pmu_isol isol; |
112 | const char * const *clk_list; |
113 | int num_clks; |
114 | u32 cdr_lock_status_offset; |
115 | }; |
116 | |
117 | struct samsung_ufs_phy { |
118 | struct device *dev; |
119 | void __iomem *reg_pma; |
120 | struct regmap *reg_pmu; |
121 | struct clk_bulk_data *clks; |
122 | const struct samsung_ufs_phy_drvdata *drvdata; |
123 | const struct samsung_ufs_phy_cfg * const *cfgs; |
124 | struct samsung_ufs_phy_pmu_isol isol; |
125 | u8 lane_cnt; |
126 | int ufs_phy_state; |
127 | enum phy_mode mode; |
128 | }; |
129 | |
130 | static inline struct samsung_ufs_phy *get_samsung_ufs_phy(struct phy *phy) |
131 | { |
132 | return (struct samsung_ufs_phy *)phy_get_drvdata(phy); |
133 | } |
134 | |
135 | static inline void samsung_ufs_phy_ctrl_isol( |
136 | struct samsung_ufs_phy *phy, u32 isol) |
137 | { |
138 | regmap_update_bits(map: phy->reg_pmu, reg: phy->isol.offset, |
139 | mask: phy->isol.mask, val: isol ? 0 : phy->isol.en); |
140 | } |
141 | |
142 | extern const struct samsung_ufs_phy_drvdata exynos7_ufs_phy; |
143 | extern const struct samsung_ufs_phy_drvdata exynosautov9_ufs_phy; |
144 | extern const struct samsung_ufs_phy_drvdata fsd_ufs_phy; |
145 | |
146 | #endif /* _PHY_SAMSUNG_UFS_ */ |
147 | |