1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Copyright (c) 2016 Allwinnertech Co., Ltd. |
4 | * Copyright (C) 2017-2018 Bootlin |
5 | * |
6 | * Maxime Ripard <maxime.ripard@free-electrons.com> |
7 | */ |
8 | |
9 | #include <linux/bitops.h> |
10 | #include <linux/clk.h> |
11 | #include <linux/module.h> |
12 | #include <linux/of_address.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/regmap.h> |
15 | #include <linux/reset.h> |
16 | |
17 | #include <linux/phy/phy.h> |
18 | #include <linux/phy/phy-mipi-dphy.h> |
19 | |
20 | #define SUN6I_DPHY_GCTL_REG 0x00 |
21 | #define SUN6I_DPHY_GCTL_LANE_NUM(n) ((((n) - 1) & 3) << 4) |
22 | #define SUN6I_DPHY_GCTL_EN BIT(0) |
23 | |
24 | #define SUN6I_DPHY_TX_CTL_REG 0x04 |
25 | #define SUN6I_DPHY_TX_CTL_HS_TX_CLK_CONT BIT(28) |
26 | |
27 | #define SUN6I_DPHY_RX_CTL_REG 0x08 |
28 | #define SUN6I_DPHY_RX_CTL_EN_DBC BIT(31) |
29 | #define SUN6I_DPHY_RX_CTL_RX_CLK_FORCE BIT(24) |
30 | #define SUN6I_DPHY_RX_CTL_RX_D3_FORCE BIT(23) |
31 | #define SUN6I_DPHY_RX_CTL_RX_D2_FORCE BIT(22) |
32 | #define SUN6I_DPHY_RX_CTL_RX_D1_FORCE BIT(21) |
33 | #define SUN6I_DPHY_RX_CTL_RX_D0_FORCE BIT(20) |
34 | |
35 | #define SUN6I_DPHY_TX_TIME0_REG 0x10 |
36 | #define SUN6I_DPHY_TX_TIME0_HS_TRAIL(n) (((n) & 0xff) << 24) |
37 | #define SUN6I_DPHY_TX_TIME0_HS_PREPARE(n) (((n) & 0xff) << 16) |
38 | #define SUN6I_DPHY_TX_TIME0_LP_CLK_DIV(n) ((n) & 0xff) |
39 | |
40 | #define SUN6I_DPHY_TX_TIME1_REG 0x14 |
41 | #define SUN6I_DPHY_TX_TIME1_CLK_POST(n) (((n) & 0xff) << 24) |
42 | #define SUN6I_DPHY_TX_TIME1_CLK_PRE(n) (((n) & 0xff) << 16) |
43 | #define SUN6I_DPHY_TX_TIME1_CLK_ZERO(n) (((n) & 0xff) << 8) |
44 | #define SUN6I_DPHY_TX_TIME1_CLK_PREPARE(n) ((n) & 0xff) |
45 | |
46 | #define SUN6I_DPHY_TX_TIME2_REG 0x18 |
47 | #define SUN6I_DPHY_TX_TIME2_CLK_TRAIL(n) ((n) & 0xff) |
48 | |
49 | #define SUN6I_DPHY_TX_TIME3_REG 0x1c |
50 | |
51 | #define SUN6I_DPHY_TX_TIME4_REG 0x20 |
52 | #define SUN6I_DPHY_TX_TIME4_HS_TX_ANA1(n) (((n) & 0xff) << 8) |
53 | #define SUN6I_DPHY_TX_TIME4_HS_TX_ANA0(n) ((n) & 0xff) |
54 | |
55 | #define SUN6I_DPHY_RX_TIME0_REG 0x30 |
56 | #define SUN6I_DPHY_RX_TIME0_HS_RX_SYNC(n) (((n) & 0xff) << 24) |
57 | #define SUN6I_DPHY_RX_TIME0_HS_RX_CLK_MISS(n) (((n) & 0xff) << 16) |
58 | #define SUN6I_DPHY_RX_TIME0_LP_RX(n) (((n) & 0xff) << 8) |
59 | |
60 | #define SUN6I_DPHY_RX_TIME1_REG 0x34 |
61 | #define SUN6I_DPHY_RX_TIME1_RX_DLY(n) (((n) & 0xfff) << 20) |
62 | #define SUN6I_DPHY_RX_TIME1_LP_RX_ULPS_WP(n) ((n) & 0xfffff) |
63 | |
64 | #define SUN6I_DPHY_RX_TIME2_REG 0x38 |
65 | #define SUN6I_DPHY_RX_TIME2_HS_RX_ANA1(n) (((n) & 0xff) << 8) |
66 | #define SUN6I_DPHY_RX_TIME2_HS_RX_ANA0(n) ((n) & 0xff) |
67 | |
68 | #define SUN6I_DPHY_RX_TIME3_REG 0x40 |
69 | #define SUN6I_DPHY_RX_TIME3_LPRST_DLY(n) (((n) & 0xffff) << 16) |
70 | |
71 | #define SUN6I_DPHY_ANA0_REG 0x4c |
72 | #define SUN6I_DPHY_ANA0_REG_PWS BIT(31) |
73 | #define SUN6I_DPHY_ANA0_REG_PWEND BIT(30) |
74 | #define SUN6I_DPHY_ANA0_REG_PWENC BIT(29) |
75 | #define SUN6I_DPHY_ANA0_REG_DMPC BIT(28) |
76 | #define SUN6I_DPHY_ANA0_REG_DMPD(n) (((n) & 0xf) << 24) |
77 | #define SUN6I_DPHY_ANA0_REG_SRXDT(n) (((n) & 0xf) << 20) |
78 | #define SUN6I_DPHY_ANA0_REG_SRXCK(n) (((n) & 0xf) << 16) |
79 | #define SUN6I_DPHY_ANA0_REG_SDIV2 BIT(15) |
80 | #define SUN6I_DPHY_ANA0_REG_SLV(n) (((n) & 7) << 12) |
81 | #define SUN6I_DPHY_ANA0_REG_DEN(n) (((n) & 0xf) << 8) |
82 | #define SUN6I_DPHY_ANA0_REG_PLR(n) (((n) & 0xf) << 4) |
83 | #define SUN6I_DPHY_ANA0_REG_SFB(n) (((n) & 3) << 2) |
84 | #define SUN6I_DPHY_ANA0_REG_RSD BIT(1) |
85 | #define SUN6I_DPHY_ANA0_REG_SELSCK BIT(0) |
86 | |
87 | #define SUN6I_DPHY_ANA1_REG 0x50 |
88 | #define SUN6I_DPHY_ANA1_REG_VTTMODE BIT(31) |
89 | #define SUN6I_DPHY_ANA1_REG_CSMPS(n) (((n) & 3) << 28) |
90 | #define SUN6I_DPHY_ANA1_REG_SVTT(n) (((n) & 0xf) << 24) |
91 | |
92 | #define SUN6I_DPHY_ANA2_REG 0x54 |
93 | #define SUN6I_DPHY_ANA2_EN_P2S_CPU(n) (((n) & 0xf) << 24) |
94 | #define SUN6I_DPHY_ANA2_EN_P2S_CPU_MASK GENMASK(27, 24) |
95 | #define SUN6I_DPHY_ANA2_EN_CK_CPU BIT(4) |
96 | #define SUN6I_DPHY_ANA2_REG_ENIB BIT(1) |
97 | |
98 | #define SUN6I_DPHY_ANA3_REG 0x58 |
99 | #define SUN6I_DPHY_ANA3_EN_VTTD(n) (((n) & 0xf) << 28) |
100 | #define SUN6I_DPHY_ANA3_EN_VTTD_MASK GENMASK(31, 28) |
101 | #define SUN6I_DPHY_ANA3_EN_VTTC BIT(27) |
102 | #define SUN6I_DPHY_ANA3_EN_DIV BIT(26) |
103 | #define SUN6I_DPHY_ANA3_EN_LDOC BIT(25) |
104 | #define SUN6I_DPHY_ANA3_EN_LDOD BIT(24) |
105 | #define SUN6I_DPHY_ANA3_EN_LDOR BIT(18) |
106 | |
107 | #define SUN6I_DPHY_ANA4_REG 0x5c |
108 | #define SUN6I_DPHY_ANA4_REG_EN_MIPI BIT(31) |
109 | #define SUN6I_DPHY_ANA4_REG_EN_COMTEST BIT(30) |
110 | #define SUN6I_DPHY_ANA4_REG_COMTEST(n) (((n) & 3) << 28) |
111 | #define SUN6I_DPHY_ANA4_REG_IB(n) (((n) & 3) << 25) |
112 | #define SUN6I_DPHY_ANA4_REG_DMPLVC BIT(24) |
113 | #define SUN6I_DPHY_ANA4_REG_DMPLVD(n) (((n) & 0xf) << 20) |
114 | #define SUN6I_DPHY_ANA4_REG_VTT_SET(n) (((n) & 0x7) << 17) |
115 | #define SUN6I_DPHY_ANA4_REG_CKDV(n) (((n) & 0x1f) << 12) |
116 | #define SUN6I_DPHY_ANA4_REG_TMSC(n) (((n) & 3) << 10) |
117 | #define SUN6I_DPHY_ANA4_REG_TMSD(n) (((n) & 3) << 8) |
118 | #define SUN6I_DPHY_ANA4_REG_TXDNSC(n) (((n) & 3) << 6) |
119 | #define SUN6I_DPHY_ANA4_REG_TXDNSD(n) (((n) & 3) << 4) |
120 | #define SUN6I_DPHY_ANA4_REG_TXPUSC(n) (((n) & 3) << 2) |
121 | #define SUN6I_DPHY_ANA4_REG_TXPUSD(n) ((n) & 3) |
122 | |
123 | #define SUN6I_DPHY_DBG5_REG 0xf4 |
124 | |
125 | #define SUN50I_DPHY_TX_SLEW_REG0 0xf8 |
126 | #define SUN50I_DPHY_TX_SLEW_REG1 0xfc |
127 | #define SUN50I_DPHY_TX_SLEW_REG2 0x100 |
128 | |
129 | #define SUN50I_DPHY_PLL_REG0 0x104 |
130 | #define SUN50I_DPHY_PLL_REG0_CP36_EN BIT(23) |
131 | #define SUN50I_DPHY_PLL_REG0_LDO_EN BIT(22) |
132 | #define SUN50I_DPHY_PLL_REG0_EN_LVS BIT(21) |
133 | #define SUN50I_DPHY_PLL_REG0_PLL_EN BIT(20) |
134 | #define SUN50I_DPHY_PLL_REG0_P(n) (((n) & 0xf) << 16) |
135 | #define SUN50I_DPHY_PLL_REG0_N(n) (((n) & 0xff) << 8) |
136 | #define SUN50I_DPHY_PLL_REG0_NDET BIT(7) |
137 | #define SUN50I_DPHY_PLL_REG0_TDIV BIT(6) |
138 | #define SUN50I_DPHY_PLL_REG0_M0(n) (((n) & 3) << 4) |
139 | #define SUN50I_DPHY_PLL_REG0_M1(n) ((n) & 0xf) |
140 | |
141 | #define SUN50I_DPHY_PLL_REG1 0x108 |
142 | #define SUN50I_DPHY_PLL_REG1_UNLOCK_MDSEL(n) (((n) & 3) << 14) |
143 | #define SUN50I_DPHY_PLL_REG1_LOCKMDSEL BIT(13) |
144 | #define SUN50I_DPHY_PLL_REG1_LOCKDET_EN BIT(12) |
145 | #define SUN50I_DPHY_PLL_REG1_VSETA(n) (((n) & 0x7) << 9) |
146 | #define SUN50I_DPHY_PLL_REG1_VSETD(n) (((n) & 0x7) << 6) |
147 | #define SUN50I_DPHY_PLL_REG1_LPF_SW BIT(5) |
148 | #define SUN50I_DPHY_PLL_REG1_ICP_SEL(n) (((n) & 3) << 3) |
149 | #define SUN50I_DPHY_PLL_REG1_ATEST_SEL(n) (((n) & 3) << 1) |
150 | #define SUN50I_DPHY_PLL_REG1_TEST_EN BIT(0) |
151 | |
152 | #define SUN50I_DPHY_PLL_REG2 0x10c |
153 | #define SUN50I_DPHY_PLL_REG2_SDM_EN BIT(31) |
154 | #define SUN50I_DPHY_PLL_REG2_FF_EN BIT(30) |
155 | #define SUN50I_DPHY_PLL_REG2_SS_EN BIT(29) |
156 | #define SUN50I_DPHY_PLL_REG2_SS_FRAC(n) (((n) & 0x1ff) << 20) |
157 | #define SUN50I_DPHY_PLL_REG2_SS_INT(n) (((n) & 0xff) << 12) |
158 | #define SUN50I_DPHY_PLL_REG2_FRAC(n) ((n) & 0xfff) |
159 | |
160 | #define SUN50I_COMBO_PHY_REG0 0x110 |
161 | #define SUN50I_COMBO_PHY_REG0_EN_TEST_COMBOLDO BIT(5) |
162 | #define SUN50I_COMBO_PHY_REG0_EN_TEST_0P8 BIT(4) |
163 | #define SUN50I_COMBO_PHY_REG0_EN_MIPI BIT(3) |
164 | #define SUN50I_COMBO_PHY_REG0_EN_LVDS BIT(2) |
165 | #define SUN50I_COMBO_PHY_REG0_EN_COMBOLDO BIT(1) |
166 | #define SUN50I_COMBO_PHY_REG0_EN_CP BIT(0) |
167 | |
168 | #define SUN50I_COMBO_PHY_REG1 0x114 |
169 | #define SUN50I_COMBO_PHY_REG2_REG_VREF1P6(n) (((n) & 0x7) << 4) |
170 | #define SUN50I_COMBO_PHY_REG2_REG_VREF0P8(n) ((n) & 0x7) |
171 | |
172 | #define SUN50I_COMBO_PHY_REG2 0x118 |
173 | #define SUN50I_COMBO_PHY_REG2_HS_STOP_DLY(n) ((n) & 0xff) |
174 | |
175 | enum sun6i_dphy_direction { |
176 | SUN6I_DPHY_DIRECTION_TX, |
177 | SUN6I_DPHY_DIRECTION_RX, |
178 | }; |
179 | |
180 | struct sun6i_dphy; |
181 | |
182 | struct sun6i_dphy_variant { |
183 | void (*tx_power_on)(struct sun6i_dphy *dphy); |
184 | bool rx_supported; |
185 | }; |
186 | |
187 | struct sun6i_dphy { |
188 | struct clk *bus_clk; |
189 | struct clk *mod_clk; |
190 | struct regmap *regs; |
191 | struct reset_control *reset; |
192 | |
193 | struct phy *phy; |
194 | struct phy_configure_opts_mipi_dphy config; |
195 | |
196 | const struct sun6i_dphy_variant *variant; |
197 | enum sun6i_dphy_direction direction; |
198 | }; |
199 | |
200 | static int sun6i_dphy_init(struct phy *phy) |
201 | { |
202 | struct sun6i_dphy *dphy = phy_get_drvdata(phy); |
203 | |
204 | reset_control_deassert(rstc: dphy->reset); |
205 | clk_prepare_enable(clk: dphy->mod_clk); |
206 | clk_set_rate_exclusive(clk: dphy->mod_clk, rate: 150000000); |
207 | |
208 | return 0; |
209 | } |
210 | |
211 | static int sun6i_dphy_configure(struct phy *phy, union phy_configure_opts *opts) |
212 | { |
213 | struct sun6i_dphy *dphy = phy_get_drvdata(phy); |
214 | int ret; |
215 | |
216 | ret = phy_mipi_dphy_config_validate(cfg: &opts->mipi_dphy); |
217 | if (ret) |
218 | return ret; |
219 | |
220 | memcpy(&dphy->config, opts, sizeof(dphy->config)); |
221 | |
222 | return 0; |
223 | } |
224 | |
225 | static void sun6i_a31_mipi_dphy_tx_power_on(struct sun6i_dphy *dphy) |
226 | { |
227 | u8 lanes_mask = GENMASK(dphy->config.lanes - 1, 0); |
228 | |
229 | regmap_write(map: dphy->regs, SUN6I_DPHY_ANA0_REG, |
230 | SUN6I_DPHY_ANA0_REG_PWS | |
231 | SUN6I_DPHY_ANA0_REG_DMPC | |
232 | SUN6I_DPHY_ANA0_REG_SLV(7) | |
233 | SUN6I_DPHY_ANA0_REG_DMPD(lanes_mask) | |
234 | SUN6I_DPHY_ANA0_REG_DEN(lanes_mask)); |
235 | |
236 | regmap_write(map: dphy->regs, SUN6I_DPHY_ANA1_REG, |
237 | SUN6I_DPHY_ANA1_REG_CSMPS(1) | |
238 | SUN6I_DPHY_ANA1_REG_SVTT(7)); |
239 | |
240 | regmap_write(map: dphy->regs, SUN6I_DPHY_ANA4_REG, |
241 | SUN6I_DPHY_ANA4_REG_CKDV(1) | |
242 | SUN6I_DPHY_ANA4_REG_TMSC(1) | |
243 | SUN6I_DPHY_ANA4_REG_TMSD(1) | |
244 | SUN6I_DPHY_ANA4_REG_TXDNSC(1) | |
245 | SUN6I_DPHY_ANA4_REG_TXDNSD(1) | |
246 | SUN6I_DPHY_ANA4_REG_TXPUSC(1) | |
247 | SUN6I_DPHY_ANA4_REG_TXPUSD(1) | |
248 | SUN6I_DPHY_ANA4_REG_DMPLVC | |
249 | SUN6I_DPHY_ANA4_REG_DMPLVD(lanes_mask)); |
250 | |
251 | regmap_write(map: dphy->regs, SUN6I_DPHY_ANA2_REG, |
252 | SUN6I_DPHY_ANA2_REG_ENIB); |
253 | udelay(5); |
254 | |
255 | regmap_write(map: dphy->regs, SUN6I_DPHY_ANA3_REG, |
256 | SUN6I_DPHY_ANA3_EN_LDOR | |
257 | SUN6I_DPHY_ANA3_EN_LDOC | |
258 | SUN6I_DPHY_ANA3_EN_LDOD); |
259 | udelay(1); |
260 | } |
261 | |
262 | static void sun50i_a100_mipi_dphy_tx_power_on(struct sun6i_dphy *dphy) |
263 | { |
264 | unsigned long mipi_symbol_rate = dphy->config.hs_clk_rate; |
265 | unsigned int div, n; |
266 | |
267 | regmap_write(map: dphy->regs, SUN6I_DPHY_ANA4_REG, |
268 | SUN6I_DPHY_ANA4_REG_IB(2) | |
269 | SUN6I_DPHY_ANA4_REG_DMPLVD(4) | |
270 | SUN6I_DPHY_ANA4_REG_VTT_SET(3) | |
271 | SUN6I_DPHY_ANA4_REG_CKDV(3) | |
272 | SUN6I_DPHY_ANA4_REG_TMSD(1) | |
273 | SUN6I_DPHY_ANA4_REG_TMSC(1) | |
274 | SUN6I_DPHY_ANA4_REG_TXPUSD(2) | |
275 | SUN6I_DPHY_ANA4_REG_TXPUSC(3) | |
276 | SUN6I_DPHY_ANA4_REG_TXDNSD(2) | |
277 | SUN6I_DPHY_ANA4_REG_TXDNSC(3)); |
278 | |
279 | regmap_update_bits(map: dphy->regs, SUN6I_DPHY_ANA2_REG, |
280 | SUN6I_DPHY_ANA2_EN_CK_CPU, |
281 | SUN6I_DPHY_ANA2_EN_CK_CPU); |
282 | |
283 | regmap_update_bits(map: dphy->regs, SUN6I_DPHY_ANA2_REG, |
284 | SUN6I_DPHY_ANA2_REG_ENIB, |
285 | SUN6I_DPHY_ANA2_REG_ENIB); |
286 | |
287 | regmap_write(map: dphy->regs, SUN6I_DPHY_ANA3_REG, |
288 | SUN6I_DPHY_ANA3_EN_LDOR | |
289 | SUN6I_DPHY_ANA3_EN_LDOC | |
290 | SUN6I_DPHY_ANA3_EN_LDOD); |
291 | |
292 | regmap_write(map: dphy->regs, SUN6I_DPHY_ANA0_REG, |
293 | SUN6I_DPHY_ANA0_REG_PLR(4) | |
294 | SUN6I_DPHY_ANA0_REG_SFB(1)); |
295 | |
296 | regmap_write(map: dphy->regs, SUN50I_COMBO_PHY_REG0, |
297 | SUN50I_COMBO_PHY_REG0_EN_CP); |
298 | |
299 | /* Choose a divider to limit the VCO frequency to around 2 GHz. */ |
300 | div = 16 >> order_base_2(DIV_ROUND_UP(mipi_symbol_rate, 264000000)); |
301 | n = mipi_symbol_rate * div / 24000000; |
302 | |
303 | regmap_write(map: dphy->regs, SUN50I_DPHY_PLL_REG0, |
304 | SUN50I_DPHY_PLL_REG0_CP36_EN | |
305 | SUN50I_DPHY_PLL_REG0_LDO_EN | |
306 | SUN50I_DPHY_PLL_REG0_EN_LVS | |
307 | SUN50I_DPHY_PLL_REG0_PLL_EN | |
308 | SUN50I_DPHY_PLL_REG0_NDET | |
309 | SUN50I_DPHY_PLL_REG0_P((div - 1) % 8) | |
310 | SUN50I_DPHY_PLL_REG0_N(n) | |
311 | SUN50I_DPHY_PLL_REG0_M0((div - 1) / 8) | |
312 | SUN50I_DPHY_PLL_REG0_M1(2)); |
313 | |
314 | /* Disable sigma-delta modulation. */ |
315 | regmap_write(map: dphy->regs, SUN50I_DPHY_PLL_REG2, val: 0); |
316 | |
317 | regmap_update_bits(map: dphy->regs, SUN6I_DPHY_ANA4_REG, |
318 | SUN6I_DPHY_ANA4_REG_EN_MIPI, |
319 | SUN6I_DPHY_ANA4_REG_EN_MIPI); |
320 | |
321 | regmap_update_bits(map: dphy->regs, SUN50I_COMBO_PHY_REG0, |
322 | SUN50I_COMBO_PHY_REG0_EN_MIPI | |
323 | SUN50I_COMBO_PHY_REG0_EN_COMBOLDO, |
324 | SUN50I_COMBO_PHY_REG0_EN_MIPI | |
325 | SUN50I_COMBO_PHY_REG0_EN_COMBOLDO); |
326 | |
327 | regmap_write(map: dphy->regs, SUN50I_COMBO_PHY_REG2, |
328 | SUN50I_COMBO_PHY_REG2_HS_STOP_DLY(20)); |
329 | udelay(1); |
330 | } |
331 | |
332 | static int sun6i_dphy_tx_power_on(struct sun6i_dphy *dphy) |
333 | { |
334 | u8 lanes_mask = GENMASK(dphy->config.lanes - 1, 0); |
335 | |
336 | regmap_write(map: dphy->regs, SUN6I_DPHY_TX_CTL_REG, |
337 | SUN6I_DPHY_TX_CTL_HS_TX_CLK_CONT); |
338 | |
339 | regmap_write(map: dphy->regs, SUN6I_DPHY_TX_TIME0_REG, |
340 | SUN6I_DPHY_TX_TIME0_LP_CLK_DIV(14) | |
341 | SUN6I_DPHY_TX_TIME0_HS_PREPARE(6) | |
342 | SUN6I_DPHY_TX_TIME0_HS_TRAIL(10)); |
343 | |
344 | regmap_write(map: dphy->regs, SUN6I_DPHY_TX_TIME1_REG, |
345 | SUN6I_DPHY_TX_TIME1_CLK_PREPARE(7) | |
346 | SUN6I_DPHY_TX_TIME1_CLK_ZERO(50) | |
347 | SUN6I_DPHY_TX_TIME1_CLK_PRE(3) | |
348 | SUN6I_DPHY_TX_TIME1_CLK_POST(10)); |
349 | |
350 | regmap_write(map: dphy->regs, SUN6I_DPHY_TX_TIME2_REG, |
351 | SUN6I_DPHY_TX_TIME2_CLK_TRAIL(30)); |
352 | |
353 | regmap_write(map: dphy->regs, SUN6I_DPHY_TX_TIME3_REG, val: 0); |
354 | |
355 | regmap_write(map: dphy->regs, SUN6I_DPHY_TX_TIME4_REG, |
356 | SUN6I_DPHY_TX_TIME4_HS_TX_ANA0(3) | |
357 | SUN6I_DPHY_TX_TIME4_HS_TX_ANA1(3)); |
358 | |
359 | dphy->variant->tx_power_on(dphy); |
360 | |
361 | regmap_update_bits(map: dphy->regs, SUN6I_DPHY_ANA3_REG, |
362 | SUN6I_DPHY_ANA3_EN_VTTC | |
363 | SUN6I_DPHY_ANA3_EN_VTTD_MASK, |
364 | SUN6I_DPHY_ANA3_EN_VTTC | |
365 | SUN6I_DPHY_ANA3_EN_VTTD(lanes_mask)); |
366 | udelay(1); |
367 | |
368 | regmap_update_bits(map: dphy->regs, SUN6I_DPHY_ANA3_REG, |
369 | SUN6I_DPHY_ANA3_EN_DIV, |
370 | SUN6I_DPHY_ANA3_EN_DIV); |
371 | udelay(1); |
372 | |
373 | regmap_update_bits(map: dphy->regs, SUN6I_DPHY_ANA2_REG, |
374 | SUN6I_DPHY_ANA2_EN_CK_CPU, |
375 | SUN6I_DPHY_ANA2_EN_CK_CPU); |
376 | udelay(1); |
377 | |
378 | regmap_update_bits(map: dphy->regs, SUN6I_DPHY_ANA1_REG, |
379 | SUN6I_DPHY_ANA1_REG_VTTMODE, |
380 | SUN6I_DPHY_ANA1_REG_VTTMODE); |
381 | |
382 | regmap_update_bits(map: dphy->regs, SUN6I_DPHY_ANA2_REG, |
383 | SUN6I_DPHY_ANA2_EN_P2S_CPU_MASK, |
384 | SUN6I_DPHY_ANA2_EN_P2S_CPU(lanes_mask)); |
385 | |
386 | regmap_write(map: dphy->regs, SUN6I_DPHY_GCTL_REG, |
387 | SUN6I_DPHY_GCTL_LANE_NUM(dphy->config.lanes) | |
388 | SUN6I_DPHY_GCTL_EN); |
389 | |
390 | return 0; |
391 | } |
392 | |
393 | static int sun6i_dphy_rx_power_on(struct sun6i_dphy *dphy) |
394 | { |
395 | /* Physical clock rate is actually half of symbol rate with DDR. */ |
396 | unsigned long mipi_symbol_rate = dphy->config.hs_clk_rate; |
397 | unsigned long dphy_clk_rate; |
398 | unsigned int rx_dly; |
399 | unsigned int lprst_dly; |
400 | u32 value; |
401 | |
402 | dphy_clk_rate = clk_get_rate(clk: dphy->mod_clk); |
403 | if (!dphy_clk_rate) |
404 | return -EINVAL; |
405 | |
406 | /* Hardcoded timing parameters from the Allwinner BSP. */ |
407 | regmap_write(map: dphy->regs, SUN6I_DPHY_RX_TIME0_REG, |
408 | SUN6I_DPHY_RX_TIME0_HS_RX_SYNC(255) | |
409 | SUN6I_DPHY_RX_TIME0_HS_RX_CLK_MISS(255) | |
410 | SUN6I_DPHY_RX_TIME0_LP_RX(255)); |
411 | |
412 | /* |
413 | * Formula from the Allwinner BSP, with hardcoded coefficients |
414 | * (probably internal divider/multiplier). |
415 | */ |
416 | rx_dly = 8 * (unsigned int)(dphy_clk_rate / (mipi_symbol_rate / 8)); |
417 | |
418 | /* |
419 | * The Allwinner BSP has an alternative formula for LP_RX_ULPS_WP: |
420 | * lp_ulps_wp_cnt = lp_ulps_wp_ms * lp_clk / 1000 |
421 | * but does not use it and hardcodes 255 instead. |
422 | */ |
423 | regmap_write(map: dphy->regs, SUN6I_DPHY_RX_TIME1_REG, |
424 | SUN6I_DPHY_RX_TIME1_RX_DLY(rx_dly) | |
425 | SUN6I_DPHY_RX_TIME1_LP_RX_ULPS_WP(255)); |
426 | |
427 | /* HS_RX_ANA0 value is hardcoded in the Allwinner BSP. */ |
428 | regmap_write(map: dphy->regs, SUN6I_DPHY_RX_TIME2_REG, |
429 | SUN6I_DPHY_RX_TIME2_HS_RX_ANA0(4)); |
430 | |
431 | /* |
432 | * Formula from the Allwinner BSP, with hardcoded coefficients |
433 | * (probably internal divider/multiplier). |
434 | */ |
435 | lprst_dly = 4 * (unsigned int)(dphy_clk_rate / (mipi_symbol_rate / 2)); |
436 | |
437 | regmap_write(map: dphy->regs, SUN6I_DPHY_RX_TIME3_REG, |
438 | SUN6I_DPHY_RX_TIME3_LPRST_DLY(lprst_dly)); |
439 | |
440 | /* Analog parameters are hardcoded in the Allwinner BSP. */ |
441 | regmap_write(map: dphy->regs, SUN6I_DPHY_ANA0_REG, |
442 | SUN6I_DPHY_ANA0_REG_PWS | |
443 | SUN6I_DPHY_ANA0_REG_SLV(7) | |
444 | SUN6I_DPHY_ANA0_REG_SFB(2)); |
445 | |
446 | regmap_write(map: dphy->regs, SUN6I_DPHY_ANA1_REG, |
447 | SUN6I_DPHY_ANA1_REG_SVTT(4)); |
448 | |
449 | regmap_write(map: dphy->regs, SUN6I_DPHY_ANA4_REG, |
450 | SUN6I_DPHY_ANA4_REG_DMPLVC | |
451 | SUN6I_DPHY_ANA4_REG_DMPLVD(1)); |
452 | |
453 | regmap_write(map: dphy->regs, SUN6I_DPHY_ANA2_REG, |
454 | SUN6I_DPHY_ANA2_REG_ENIB); |
455 | |
456 | regmap_write(map: dphy->regs, SUN6I_DPHY_ANA3_REG, |
457 | SUN6I_DPHY_ANA3_EN_LDOR | |
458 | SUN6I_DPHY_ANA3_EN_LDOC | |
459 | SUN6I_DPHY_ANA3_EN_LDOD); |
460 | |
461 | /* |
462 | * Delay comes from the Allwinner BSP, likely for internal regulator |
463 | * ramp-up. |
464 | */ |
465 | udelay(3); |
466 | |
467 | value = SUN6I_DPHY_RX_CTL_EN_DBC | SUN6I_DPHY_RX_CTL_RX_CLK_FORCE; |
468 | |
469 | /* |
470 | * Rx data lane force-enable bits are used as regular RX enable by the |
471 | * Allwinner BSP. |
472 | */ |
473 | if (dphy->config.lanes >= 1) |
474 | value |= SUN6I_DPHY_RX_CTL_RX_D0_FORCE; |
475 | if (dphy->config.lanes >= 2) |
476 | value |= SUN6I_DPHY_RX_CTL_RX_D1_FORCE; |
477 | if (dphy->config.lanes >= 3) |
478 | value |= SUN6I_DPHY_RX_CTL_RX_D2_FORCE; |
479 | if (dphy->config.lanes == 4) |
480 | value |= SUN6I_DPHY_RX_CTL_RX_D3_FORCE; |
481 | |
482 | regmap_write(map: dphy->regs, SUN6I_DPHY_RX_CTL_REG, val: value); |
483 | |
484 | regmap_write(map: dphy->regs, SUN6I_DPHY_GCTL_REG, |
485 | SUN6I_DPHY_GCTL_LANE_NUM(dphy->config.lanes) | |
486 | SUN6I_DPHY_GCTL_EN); |
487 | |
488 | return 0; |
489 | } |
490 | |
491 | static int sun6i_dphy_power_on(struct phy *phy) |
492 | { |
493 | struct sun6i_dphy *dphy = phy_get_drvdata(phy); |
494 | |
495 | switch (dphy->direction) { |
496 | case SUN6I_DPHY_DIRECTION_TX: |
497 | return sun6i_dphy_tx_power_on(dphy); |
498 | case SUN6I_DPHY_DIRECTION_RX: |
499 | return sun6i_dphy_rx_power_on(dphy); |
500 | default: |
501 | return -EINVAL; |
502 | } |
503 | } |
504 | |
505 | static int sun6i_dphy_power_off(struct phy *phy) |
506 | { |
507 | struct sun6i_dphy *dphy = phy_get_drvdata(phy); |
508 | |
509 | regmap_write(map: dphy->regs, SUN6I_DPHY_GCTL_REG, val: 0); |
510 | |
511 | regmap_write(map: dphy->regs, SUN6I_DPHY_ANA0_REG, val: 0); |
512 | regmap_write(map: dphy->regs, SUN6I_DPHY_ANA1_REG, val: 0); |
513 | regmap_write(map: dphy->regs, SUN6I_DPHY_ANA2_REG, val: 0); |
514 | regmap_write(map: dphy->regs, SUN6I_DPHY_ANA3_REG, val: 0); |
515 | regmap_write(map: dphy->regs, SUN6I_DPHY_ANA4_REG, val: 0); |
516 | |
517 | return 0; |
518 | } |
519 | |
520 | static int sun6i_dphy_exit(struct phy *phy) |
521 | { |
522 | struct sun6i_dphy *dphy = phy_get_drvdata(phy); |
523 | |
524 | clk_rate_exclusive_put(clk: dphy->mod_clk); |
525 | clk_disable_unprepare(clk: dphy->mod_clk); |
526 | reset_control_assert(rstc: dphy->reset); |
527 | |
528 | return 0; |
529 | } |
530 | |
531 | |
532 | static const struct phy_ops sun6i_dphy_ops = { |
533 | .configure = sun6i_dphy_configure, |
534 | .power_on = sun6i_dphy_power_on, |
535 | .power_off = sun6i_dphy_power_off, |
536 | .init = sun6i_dphy_init, |
537 | .exit = sun6i_dphy_exit, |
538 | }; |
539 | |
540 | static const struct regmap_config sun6i_dphy_regmap_config = { |
541 | .reg_bits = 32, |
542 | .val_bits = 32, |
543 | .reg_stride = 4, |
544 | .max_register = SUN50I_COMBO_PHY_REG2, |
545 | .name = "mipi-dphy" , |
546 | }; |
547 | |
548 | static int sun6i_dphy_probe(struct platform_device *pdev) |
549 | { |
550 | struct phy_provider *phy_provider; |
551 | struct sun6i_dphy *dphy; |
552 | const char *direction; |
553 | void __iomem *regs; |
554 | int ret; |
555 | |
556 | dphy = devm_kzalloc(dev: &pdev->dev, size: sizeof(*dphy), GFP_KERNEL); |
557 | if (!dphy) |
558 | return -ENOMEM; |
559 | |
560 | dphy->variant = device_get_match_data(dev: &pdev->dev); |
561 | if (!dphy->variant) |
562 | return -EINVAL; |
563 | |
564 | regs = devm_platform_ioremap_resource(pdev, index: 0); |
565 | if (IS_ERR(ptr: regs)) { |
566 | dev_err(&pdev->dev, "Couldn't map the DPHY encoder registers\n" ); |
567 | return PTR_ERR(ptr: regs); |
568 | } |
569 | |
570 | dphy->regs = devm_regmap_init_mmio_clk(&pdev->dev, "bus" , |
571 | regs, &sun6i_dphy_regmap_config); |
572 | if (IS_ERR(ptr: dphy->regs)) { |
573 | dev_err(&pdev->dev, "Couldn't create the DPHY encoder regmap\n" ); |
574 | return PTR_ERR(ptr: dphy->regs); |
575 | } |
576 | |
577 | dphy->reset = devm_reset_control_get_shared(dev: &pdev->dev, NULL); |
578 | if (IS_ERR(ptr: dphy->reset)) { |
579 | dev_err(&pdev->dev, "Couldn't get our reset line\n" ); |
580 | return PTR_ERR(ptr: dphy->reset); |
581 | } |
582 | |
583 | dphy->mod_clk = devm_clk_get(dev: &pdev->dev, id: "mod" ); |
584 | if (IS_ERR(ptr: dphy->mod_clk)) { |
585 | dev_err(&pdev->dev, "Couldn't get the DPHY mod clock\n" ); |
586 | return PTR_ERR(ptr: dphy->mod_clk); |
587 | } |
588 | |
589 | dphy->phy = devm_phy_create(dev: &pdev->dev, NULL, ops: &sun6i_dphy_ops); |
590 | if (IS_ERR(ptr: dphy->phy)) { |
591 | dev_err(&pdev->dev, "failed to create PHY\n" ); |
592 | return PTR_ERR(ptr: dphy->phy); |
593 | } |
594 | |
595 | dphy->direction = SUN6I_DPHY_DIRECTION_TX; |
596 | |
597 | ret = of_property_read_string(np: pdev->dev.of_node, propname: "allwinner,direction" , |
598 | out_string: &direction); |
599 | |
600 | if (!ret && !strncmp(direction, "rx" , 2)) { |
601 | if (!dphy->variant->rx_supported) { |
602 | dev_err(&pdev->dev, "RX not supported on this variant\n" ); |
603 | return -EOPNOTSUPP; |
604 | } |
605 | |
606 | dphy->direction = SUN6I_DPHY_DIRECTION_RX; |
607 | } |
608 | |
609 | phy_set_drvdata(phy: dphy->phy, data: dphy); |
610 | phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); |
611 | |
612 | return PTR_ERR_OR_ZERO(ptr: phy_provider); |
613 | } |
614 | |
615 | static const struct sun6i_dphy_variant sun6i_a31_mipi_dphy_variant = { |
616 | .tx_power_on = sun6i_a31_mipi_dphy_tx_power_on, |
617 | .rx_supported = true, |
618 | }; |
619 | |
620 | static const struct sun6i_dphy_variant sun50i_a100_mipi_dphy_variant = { |
621 | .tx_power_on = sun50i_a100_mipi_dphy_tx_power_on, |
622 | }; |
623 | |
624 | static const struct of_device_id sun6i_dphy_of_table[] = { |
625 | { |
626 | .compatible = "allwinner,sun6i-a31-mipi-dphy" , |
627 | .data = &sun6i_a31_mipi_dphy_variant, |
628 | }, |
629 | { |
630 | .compatible = "allwinner,sun50i-a100-mipi-dphy" , |
631 | .data = &sun50i_a100_mipi_dphy_variant, |
632 | }, |
633 | { } |
634 | }; |
635 | MODULE_DEVICE_TABLE(of, sun6i_dphy_of_table); |
636 | |
637 | static struct platform_driver sun6i_dphy_platform_driver = { |
638 | .probe = sun6i_dphy_probe, |
639 | .driver = { |
640 | .name = "sun6i-mipi-dphy" , |
641 | .of_match_table = sun6i_dphy_of_table, |
642 | }, |
643 | }; |
644 | module_platform_driver(sun6i_dphy_platform_driver); |
645 | |
646 | MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin>" ); |
647 | MODULE_DESCRIPTION("Allwinner A31 MIPI D-PHY Driver" ); |
648 | MODULE_LICENSE("GPL" ); |
649 | |