]>
Commit | Line | Data |
---|---|---|
7ef19503 NA |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * Meson AXG MIPI DPHY driver | |
4 | * | |
5 | * Copyright (C) 2018 Amlogic, Inc. All rights reserved | |
6 | * Copyright (C) 2020 BayLibre, SAS | |
7 | * Author: Neil Armstrong <narmstrong@baylibre.com> | |
8 | */ | |
9 | ||
10 | #include <common.h> | |
11 | #include <log.h> | |
12 | #include <malloc.h> | |
13 | #include <asm/io.h> | |
14 | #include <bitfield.h> | |
15 | #include <dm.h> | |
16 | #include <errno.h> | |
17 | #include <generic-phy.h> | |
18 | #include <regmap.h> | |
19 | #include <linux/delay.h> | |
20 | #include <power/regulator.h> | |
21 | #include <reset.h> | |
22 | #include <clk.h> | |
23 | #include <phy-mipi-dphy.h> | |
24 | ||
25 | #include <linux/bitops.h> | |
26 | #include <linux/compat.h> | |
27 | #include <linux/bitfield.h> | |
13248d66 | 28 | #include <linux/time.h> |
7ef19503 NA |
29 | |
30 | /* [31] soft reset for the phy. | |
31 | * 1: reset. 0: dessert the reset. | |
32 | * [30] clock lane soft reset. | |
33 | * [29] data byte lane 3 soft reset. | |
34 | * [28] data byte lane 2 soft reset. | |
35 | * [27] data byte lane 1 soft reset. | |
36 | * [26] data byte lane 0 soft reset. | |
37 | * [25] mipi dsi pll clock selection. | |
38 | * 1: clock from fixed 850Mhz clock source. 0: from VID2 PLL. | |
39 | * [12] mipi HSbyteclk enable. | |
40 | * [11] mipi divider clk selection. | |
41 | * 1: select the mipi DDRCLKHS from clock divider. | |
42 | * 0: from PLL clock. | |
43 | * [10] mipi clock divider control. | |
44 | * 1: /4. 0: /2. | |
45 | * [9] mipi divider output enable. | |
46 | * [8] mipi divider counter enable. | |
47 | * [7] PLL clock enable. | |
48 | * [5] LPDT data endian. | |
49 | * 1 = transfer the high bit first. 0 : transfer the low bit first. | |
50 | * [4] HS data endian. | |
51 | * [3] force data byte lane in stop mode. | |
52 | * [2] force data byte lane 0 in receiver mode. | |
53 | * [1] write 1 to sync the txclkesc input. the internal logic have to | |
54 | * use txclkesc to decide Txvalid and Txready. | |
55 | * [0] enalbe the MIPI DPHY TxDDRClk. | |
56 | */ | |
57 | #define MIPI_DSI_PHY_CTRL 0x0 | |
58 | ||
59 | /* [31] clk lane tx_hs_en control selection. | |
60 | * 1: from register. 0: use clk lane state machine. | |
61 | * [30] register bit for clock lane tx_hs_en. | |
62 | * [29] clk lane tx_lp_en contrl selection. | |
63 | * 1: from register. 0: from clk lane state machine. | |
64 | * [28] register bit for clock lane tx_lp_en. | |
65 | * [27] chan0 tx_hs_en control selection. | |
66 | * 1: from register. 0: from chan0 state machine. | |
67 | * [26] register bit for chan0 tx_hs_en. | |
68 | * [25] chan0 tx_lp_en control selection. | |
69 | * 1: from register. 0: from chan0 state machine. | |
70 | * [24] register bit from chan0 tx_lp_en. | |
71 | * [23] chan0 rx_lp_en control selection. | |
72 | * 1: from register. 0: from chan0 state machine. | |
73 | * [22] register bit from chan0 rx_lp_en. | |
74 | * [21] chan0 contention detection enable control selection. | |
75 | * 1: from register. 0: from chan0 state machine. | |
76 | * [20] register bit from chan0 contention dectection enable. | |
77 | * [19] chan1 tx_hs_en control selection. | |
78 | * 1: from register. 0: from chan0 state machine. | |
79 | * [18] register bit for chan1 tx_hs_en. | |
80 | * [17] chan1 tx_lp_en control selection. | |
81 | * 1: from register. 0: from chan0 state machine. | |
82 | * [16] register bit from chan1 tx_lp_en. | |
83 | * [15] chan2 tx_hs_en control selection. | |
84 | * 1: from register. 0: from chan0 state machine. | |
85 | * [14] register bit for chan2 tx_hs_en. | |
86 | * [13] chan2 tx_lp_en control selection. | |
87 | * 1: from register. 0: from chan0 state machine. | |
88 | * [12] register bit from chan2 tx_lp_en. | |
89 | * [11] chan3 tx_hs_en control selection. | |
90 | * 1: from register. 0: from chan0 state machine. | |
91 | * [10] register bit for chan3 tx_hs_en. | |
92 | * [9] chan3 tx_lp_en control selection. | |
93 | * 1: from register. 0: from chan0 state machine. | |
94 | * [8] register bit from chan3 tx_lp_en. | |
95 | * [4] clk chan power down. this bit is also used as the power down | |
96 | * of the whole MIPI_DSI_PHY. | |
97 | * [3] chan3 power down. | |
98 | * [2] chan2 power down. | |
99 | * [1] chan1 power down. | |
100 | * [0] chan0 power down. | |
101 | */ | |
102 | #define MIPI_DSI_CHAN_CTRL 0x4 | |
103 | ||
104 | /* [24] rx turn watch dog triggered. | |
105 | * [23] rx esc watchdog triggered. | |
106 | * [22] mbias ready. | |
107 | * [21] txclkesc synced and ready. | |
108 | * [20:17] clk lane state. {mbias_ready, tx_stop, tx_ulps, tx_hs_active} | |
109 | * [16:13] chan3 state{0, tx_stop, tx_ulps, tx_hs_active} | |
110 | * [12:9] chan2 state.{0, tx_stop, tx_ulps, tx_hs_active} | |
111 | * [8:5] chan1 state. {0, tx_stop, tx_ulps, tx_hs_active} | |
112 | * [4:0] chan0 state. {TX_STOP, tx_ULPS, hs_active, direction, rxulpsesc} | |
113 | */ | |
114 | #define MIPI_DSI_CHAN_STS 0x8 | |
115 | ||
116 | /* [31:24] TCLK_PREPARE. | |
117 | * [23:16] TCLK_ZERO. | |
118 | * [15:8] TCLK_POST. | |
119 | * [7:0] TCLK_TRAIL. | |
120 | */ | |
121 | #define MIPI_DSI_CLK_TIM 0xc | |
122 | ||
123 | /* [31:24] THS_PREPARE. | |
124 | * [23:16] THS_ZERO. | |
125 | * [15:8] THS_TRAIL. | |
126 | * [7:0] THS_EXIT. | |
127 | */ | |
128 | #define MIPI_DSI_HS_TIM 0x10 | |
129 | ||
130 | /* [31:24] tTA_GET. | |
131 | * [23:16] tTA_GO. | |
132 | * [15:8] tTA_SURE. | |
133 | * [7:0] tLPX. | |
134 | */ | |
135 | #define MIPI_DSI_LP_TIM 0x14 | |
136 | ||
137 | /* wait time to MIPI DIS analog ready. */ | |
138 | #define MIPI_DSI_ANA_UP_TIM 0x18 | |
139 | ||
140 | /* TINIT. */ | |
141 | #define MIPI_DSI_INIT_TIM 0x1c | |
142 | ||
143 | /* TWAKEUP. */ | |
144 | #define MIPI_DSI_WAKEUP_TIM 0x20 | |
145 | ||
146 | /* when in RxULPS check state, after the the logic enable the analog, | |
147 | * how long we should wait to check the lP state . | |
148 | */ | |
149 | #define MIPI_DSI_LPOK_TIM 0x24 | |
150 | ||
151 | /* Watchdog for RX low power state no finished. */ | |
152 | #define MIPI_DSI_LP_WCHDOG 0x28 | |
153 | ||
154 | /* tMBIAS, after send power up signals to analog, | |
155 | * how long we should wait for analog powered up. | |
156 | */ | |
157 | #define MIPI_DSI_ANA_CTRL 0x2c | |
158 | ||
159 | /* [31:8] reserved for future. | |
160 | * [7:0] tCLK_PRE. | |
161 | */ | |
162 | #define MIPI_DSI_CLK_TIM1 0x30 | |
163 | ||
164 | /* watchdog for turn around waiting time. */ | |
165 | #define MIPI_DSI_TURN_WCHDOG 0x34 | |
166 | ||
167 | /* When in RxULPS state, how frequency we should to check | |
168 | * if the TX side out of ULPS state. | |
169 | */ | |
170 | #define MIPI_DSI_ULPS_CHECK 0x38 | |
171 | #define MIPI_DSI_TEST_CTRL0 0x3c | |
172 | #define MIPI_DSI_TEST_CTRL1 0x40 | |
173 | ||
7ef19503 NA |
174 | struct phy_meson_axg_mipi_dphy_priv { |
175 | struct regmap *regmap; | |
176 | #if CONFIG_IS_ENABLED(CLK) | |
177 | struct clk clk; | |
178 | #endif | |
179 | struct reset_ctl reset; | |
180 | struct phy analog; | |
181 | struct phy_configure_opts_mipi_dphy config; | |
182 | }; | |
183 | ||
184 | static int phy_meson_axg_mipi_dphy_configure(struct phy *phy, void *params) | |
185 | { | |
186 | struct udevice *dev = phy->dev; | |
187 | struct phy_meson_axg_mipi_dphy_priv *priv = dev_get_priv(dev); | |
188 | struct phy_configure_opts_mipi_dphy *config = params; | |
189 | int ret; | |
190 | ||
191 | ret = phy_mipi_dphy_config_validate(config); | |
192 | if (ret) | |
193 | return ret; | |
194 | ||
195 | ret = generic_phy_configure(&priv->analog, config); | |
196 | if (ret) | |
197 | return ret; | |
198 | ||
199 | memcpy(&priv->config, config, sizeof(priv->config)); | |
200 | ||
201 | return 0; | |
202 | } | |
203 | ||
204 | static int phy_meson_axg_mipi_dphy_power_on(struct phy *phy) | |
205 | { | |
206 | struct udevice *dev = phy->dev; | |
207 | struct phy_meson_axg_mipi_dphy_priv *priv = dev_get_priv(dev); | |
208 | unsigned long temp; | |
209 | int ret; | |
210 | ||
211 | ret = generic_phy_power_on(&priv->analog); | |
212 | if (ret) | |
213 | return ret; | |
214 | ||
215 | /* enable phy clock */ | |
216 | regmap_write(priv->regmap, MIPI_DSI_PHY_CTRL, 0x1); | |
217 | regmap_write(priv->regmap, MIPI_DSI_PHY_CTRL, | |
218 | BIT(0) | /* enable the DSI PLL clock . */ | |
219 | BIT(7) | /* enable pll clock which connected to DDR clock path */ | |
220 | BIT(8)); /* enable the clock divider counter */ | |
221 | ||
222 | /* enable the divider clock out */ | |
223 | regmap_update_bits(priv->regmap, MIPI_DSI_PHY_CTRL, BIT(9), BIT(9)); | |
224 | ||
225 | /* enable the byte clock generation. */ | |
226 | regmap_update_bits(priv->regmap, MIPI_DSI_PHY_CTRL, BIT(12), BIT(12)); | |
227 | regmap_update_bits(priv->regmap, MIPI_DSI_PHY_CTRL, BIT(31), BIT(31)); | |
228 | regmap_update_bits(priv->regmap, MIPI_DSI_PHY_CTRL, BIT(31), 0); | |
229 | ||
230 | /* Calculate lanebyteclk period in ps */ | |
231 | temp = (1000000 * 100) / (priv->config.hs_clk_rate / 1000); | |
232 | temp = temp * 8 * 10; | |
233 | ||
234 | regmap_write(priv->regmap, MIPI_DSI_CLK_TIM, | |
235 | DIV_ROUND_UP(priv->config.clk_trail, temp) | | |
236 | (DIV_ROUND_UP(priv->config.clk_post + | |
237 | priv->config.hs_trail, temp) << 8) | | |
238 | (DIV_ROUND_UP(priv->config.clk_zero, temp) << 16) | | |
239 | (DIV_ROUND_UP(priv->config.clk_prepare, temp) << 24)); | |
240 | regmap_write(priv->regmap, MIPI_DSI_CLK_TIM1, | |
241 | DIV_ROUND_UP(priv->config.clk_pre, temp)); | |
242 | ||
243 | regmap_write(priv->regmap, MIPI_DSI_HS_TIM, | |
244 | DIV_ROUND_UP(priv->config.hs_exit, temp) | | |
245 | (DIV_ROUND_UP(priv->config.hs_trail, temp) << 8) | | |
246 | (DIV_ROUND_UP(priv->config.hs_zero, temp) << 16) | | |
247 | (DIV_ROUND_UP(priv->config.hs_prepare, temp) << 24)); | |
248 | ||
249 | regmap_write(priv->regmap, MIPI_DSI_LP_TIM, | |
250 | DIV_ROUND_UP(priv->config.lpx, temp) | | |
251 | (DIV_ROUND_UP(priv->config.ta_sure, temp) << 8) | | |
252 | (DIV_ROUND_UP(priv->config.ta_go, temp) << 16) | | |
253 | (DIV_ROUND_UP(priv->config.ta_get, temp) << 24)); | |
254 | ||
255 | regmap_write(priv->regmap, MIPI_DSI_ANA_UP_TIM, 0x0100); | |
256 | regmap_write(priv->regmap, MIPI_DSI_INIT_TIM, | |
257 | DIV_ROUND_UP(priv->config.init * NSEC_PER_MSEC, temp)); | |
258 | regmap_write(priv->regmap, MIPI_DSI_WAKEUP_TIM, | |
259 | DIV_ROUND_UP(priv->config.wakeup * NSEC_PER_MSEC, temp)); | |
260 | regmap_write(priv->regmap, MIPI_DSI_LPOK_TIM, 0x7C); | |
261 | regmap_write(priv->regmap, MIPI_DSI_ULPS_CHECK, 0x927C); | |
262 | regmap_write(priv->regmap, MIPI_DSI_LP_WCHDOG, 0x1000); | |
263 | regmap_write(priv->regmap, MIPI_DSI_TURN_WCHDOG, 0x1000); | |
264 | ||
265 | /* Powerup the analog circuit */ | |
266 | switch (priv->config.lanes) { | |
267 | case 1: | |
268 | regmap_write(priv->regmap, MIPI_DSI_CHAN_CTRL, 0xe); | |
269 | break; | |
270 | case 2: | |
271 | regmap_write(priv->regmap, MIPI_DSI_CHAN_CTRL, 0xc); | |
272 | break; | |
273 | case 3: | |
274 | regmap_write(priv->regmap, MIPI_DSI_CHAN_CTRL, 0x8); | |
275 | break; | |
276 | case 4: | |
277 | default: | |
278 | regmap_write(priv->regmap, MIPI_DSI_CHAN_CTRL, 0); | |
279 | break; | |
280 | } | |
281 | ||
282 | /* Trigger a sync active for esc_clk */ | |
283 | regmap_update_bits(priv->regmap, MIPI_DSI_PHY_CTRL, BIT(1), BIT(1)); | |
284 | ||
285 | return 0; | |
286 | } | |
287 | ||
288 | static int phy_meson_axg_mipi_dphy_power_off(struct phy *phy) | |
289 | { | |
290 | struct udevice *dev = phy->dev; | |
291 | struct phy_meson_axg_mipi_dphy_priv *priv = dev_get_priv(dev); | |
292 | ||
293 | regmap_write(priv->regmap, MIPI_DSI_CHAN_CTRL, 0xf); | |
294 | regmap_write(priv->regmap, MIPI_DSI_PHY_CTRL, BIT(31)); | |
295 | ||
296 | return generic_phy_power_off(&priv->analog); | |
297 | } | |
298 | ||
299 | static int phy_meson_axg_mipi_dphy_init(struct phy *phy) | |
300 | { | |
301 | struct udevice *dev = phy->dev; | |
302 | struct phy_meson_axg_mipi_dphy_priv *priv = dev_get_priv(dev); | |
303 | int ret; | |
304 | ||
305 | ret = generic_phy_init(&priv->analog); | |
306 | if (ret) | |
307 | return ret; | |
308 | ||
309 | ret = reset_assert(&priv->reset); | |
310 | udelay(1); | |
311 | ret |= reset_deassert(&priv->reset); | |
312 | if (ret) | |
313 | return ret; | |
314 | ||
315 | return 0; | |
316 | } | |
317 | ||
318 | static int phy_meson_axg_mipi_dphy_exit(struct phy *phy) | |
319 | { | |
320 | struct udevice *dev = phy->dev; | |
321 | struct phy_meson_axg_mipi_dphy_priv *priv = dev_get_priv(dev); | |
322 | int ret; | |
323 | ||
324 | ret = generic_phy_exit(&priv->analog); | |
325 | if (ret) | |
326 | return ret; | |
327 | ||
328 | return reset_assert(&priv->reset); | |
329 | } | |
330 | ||
331 | struct phy_ops meson_axg_mipi_dphy_ops = { | |
332 | .init = phy_meson_axg_mipi_dphy_init, | |
333 | .exit = phy_meson_axg_mipi_dphy_exit, | |
334 | .power_on = phy_meson_axg_mipi_dphy_power_on, | |
335 | .power_off = phy_meson_axg_mipi_dphy_power_off, | |
336 | .configure = phy_meson_axg_mipi_dphy_configure, | |
337 | }; | |
338 | ||
339 | int meson_axg_mipi_dphy_probe(struct udevice *dev) | |
340 | { | |
341 | struct phy_meson_axg_mipi_dphy_priv *priv = dev_get_priv(dev); | |
342 | int ret; | |
343 | ||
344 | ret = regmap_init_mem(dev_ofnode(dev), &priv->regmap); | |
345 | if (ret) | |
346 | return ret; | |
347 | ||
348 | ret = generic_phy_get_by_index(dev, 0, &priv->analog); | |
349 | if (ret) | |
350 | return ret; | |
351 | ||
352 | ret = reset_get_by_index(dev, 0, &priv->reset); | |
353 | if (ret == -ENOTSUPP) | |
354 | return 0; | |
355 | else if (ret) | |
356 | return ret; | |
357 | ||
358 | ret = reset_deassert(&priv->reset); | |
359 | if (ret) { | |
360 | reset_release_all(&priv->reset, 1); | |
361 | return ret; | |
362 | } | |
363 | ||
364 | #if CONFIG_IS_ENABLED(CLK) | |
365 | ret = clk_get_by_index(dev, 0, &priv->clk); | |
366 | if (ret < 0) | |
367 | return ret; | |
368 | ||
369 | ret = clk_enable(&priv->clk); | |
370 | if (ret && ret != -ENOSYS && ret != -ENOTSUPP) { | |
371 | pr_err("failed to enable PHY clock\n"); | |
7ef19503 NA |
372 | return ret; |
373 | } | |
374 | #endif | |
375 | ||
376 | return 0; | |
377 | } | |
378 | ||
379 | static const struct udevice_id meson_axg_mipi_dphy_ids[] = { | |
380 | { .compatible = "amlogic,axg-mipi-dphy" }, | |
381 | { } | |
382 | }; | |
383 | ||
384 | U_BOOT_DRIVER(meson_axg_mipi_dphy) = { | |
385 | .name = "meson_axg_mipi_dphy", | |
386 | .id = UCLASS_PHY, | |
387 | .of_match = meson_axg_mipi_dphy_ids, | |
388 | .probe = meson_axg_mipi_dphy_probe, | |
389 | .ops = &meson_axg_mipi_dphy_ops, | |
a41862d2 | 390 | .priv_auto = sizeof(struct phy_meson_axg_mipi_dphy_priv), |
7ef19503 | 391 | }; |