1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright 2017,2018 NXP
4 * Copyright 2019 Purism SPC
8 #include <linux/clk-provider.h>
9 #include <linux/delay.h>
11 #include <linux/kernel.h>
12 #include <linux/module.h>
14 #include <linux/of_platform.h>
15 #include <linux/phy/phy.h>
16 #include <linux/platform_device.h>
17 #include <linux/regmap.h>
20 #define DPHY_PD_DPHY 0x00
21 #define DPHY_M_PRG_HS_PREPARE 0x04
22 #define DPHY_MC_PRG_HS_PREPARE 0x08
23 #define DPHY_M_PRG_HS_ZERO 0x0c
24 #define DPHY_MC_PRG_HS_ZERO 0x10
25 #define DPHY_M_PRG_HS_TRAIL 0x14
26 #define DPHY_MC_PRG_HS_TRAIL 0x18
27 #define DPHY_PD_PLL 0x1c
32 #define DPHY_LOCK 0x30
33 #define DPHY_LOCK_BYP 0x34
34 #define DPHY_REG_BYPASS_PLL 0x4C
36 #define MBPS(x) ((x) * 1000000)
38 #define DATA_RATE_MAX_SPEED MBPS(1500)
39 #define DATA_RATE_MIN_SPEED MBPS(80)
41 #define PLL_LOCK_SLEEP 10
42 #define PLL_LOCK_TIMEOUT 1000
44 #define CN_BUF 0xcb7a89c0
47 ((x) < 32) ? 0xe0 | ((x) - 16) : \
48 ((x) < 64) ? 0xc0 | ((x) - 32) : \
49 ((x) < 128) ? 0x80 | ((x) - 64) : \
51 #define CN(x) (((x) == 1) ? 0x1f : (((CN_BUF) >> ((x) - 1)) & 0x1f))
52 #define CO(x) ((CO_BUF) >> (8 - (x)) & 0x03)
54 /* PHY power on is active low */
58 enum mixel_dphy_devtype
{
62 struct mixel_dphy_devdata
{
70 static const struct mixel_dphy_devdata mixel_dphy_devdata
[] = {
73 .reg_auto_pd_en
= 0x3c,
76 .reg_rxhs_settle
= 0x48,
80 struct mixel_dphy_cfg
{
81 /* DPHY PLL parameters */
85 /* DPHY register values */
95 struct mixel_dphy_priv
{
96 struct mixel_dphy_cfg cfg
;
97 struct regmap
*regmap
;
98 struct clk
*phy_ref_clk
;
99 const struct mixel_dphy_devdata
*devdata
;
102 static const struct regmap_config mixel_dphy_regmap_config
= {
106 .max_register
= DPHY_REG_BYPASS_PLL
,
110 static int phy_write(struct phy
*phy
, u32 value
, unsigned int reg
)
112 struct mixel_dphy_priv
*priv
= phy_get_drvdata(phy
);
115 ret
= regmap_write(priv
->regmap
, reg
, value
);
117 dev_err(&phy
->dev
, "Failed to write DPHY reg %d: %d\n", reg
,
123 * Find a ratio close to the desired one using continued fraction
124 * approximation ending either at exact match or maximum allowed
125 * nominator, denominator.
127 static void get_best_ratio(u32
*pnum
, u32
*pdenom
, u32 max_n
, u32 max_d
)
140 n
[i
] += (n
[i
^ 1] * whole
);
141 d
[i
] += (d
[i
^ 1] * whole
);
142 if ((n
[i
] > max_n
) || (d
[i
] > max_d
)) {
154 static int mixel_dphy_config_from_opts(struct phy
*phy
,
155 struct phy_configure_opts_mipi_dphy
*dphy_opts
,
156 struct mixel_dphy_cfg
*cfg
)
158 struct mixel_dphy_priv
*priv
= dev_get_drvdata(phy
->dev
.parent
);
159 unsigned long ref_clk
= clk_get_rate(priv
->phy_ref_clk
);
160 u32 lp_t
, numerator
, denominator
;
161 unsigned long long tmp
;
165 if (dphy_opts
->hs_clk_rate
> DATA_RATE_MAX_SPEED
||
166 dphy_opts
->hs_clk_rate
< DATA_RATE_MIN_SPEED
)
169 numerator
= dphy_opts
->hs_clk_rate
;
170 denominator
= ref_clk
;
171 get_best_ratio(&numerator
, &denominator
, 255, 256);
172 if (!numerator
|| !denominator
) {
173 dev_err(&phy
->dev
, "Invalid %d/%d for %ld/%ld\n",
174 numerator
, denominator
,
175 dphy_opts
->hs_clk_rate
, ref_clk
);
179 while ((numerator
< 16) && (denominator
<= 128)) {
184 * CM ranges between 16 and 255
185 * CN ranges between 1 and 32
186 * CO is power of 2: 1, 2, 4, 8
188 i
= __ffs(denominator
);
191 cfg
->cn
= denominator
>> i
;
195 if (cfg
->cm
< 16 || cfg
->cm
> 255 ||
196 cfg
->cn
< 1 || cfg
->cn
> 32 ||
197 cfg
->co
< 1 || cfg
->co
> 8) {
198 dev_err(&phy
->dev
, "Invalid CM/CN/CO values: %u/%u/%u\n",
199 cfg
->cm
, cfg
->cn
, cfg
->co
);
200 dev_err(&phy
->dev
, "for hs_clk/ref_clk=%ld/%ld ~ %d/%d\n",
201 dphy_opts
->hs_clk_rate
, ref_clk
,
202 numerator
, denominator
);
206 dev_dbg(&phy
->dev
, "hs_clk/ref_clk=%ld/%ld ~ %d/%d\n",
207 dphy_opts
->hs_clk_rate
, ref_clk
, numerator
, denominator
);
209 /* LP clock period */
210 tmp
= 1000000000000LL;
211 do_div(tmp
, dphy_opts
->lp_clk_rate
); /* ps */
216 dev_dbg(&phy
->dev
, "LP clock %lu, period: %u ps\n",
217 dphy_opts
->lp_clk_rate
, lp_t
);
219 /* hs_prepare: in lp clock periods */
220 if (2 * dphy_opts
->hs_prepare
> 5 * lp_t
) {
222 "hs_prepare (%u) > 2.5 * lp clock period (%u)\n",
223 dphy_opts
->hs_prepare
, lp_t
);
226 /* 00: lp_t, 01: 1.5 * lp_t, 10: 2 * lp_t, 11: 2.5 * lp_t */
227 if (dphy_opts
->hs_prepare
< lp_t
) {
230 tmp
= 2 * (dphy_opts
->hs_prepare
- lp_t
);
234 cfg
->m_prg_hs_prepare
= n
;
236 /* clk_prepare: in lp clock periods */
237 if (2 * dphy_opts
->clk_prepare
> 3 * lp_t
) {
239 "clk_prepare (%u) > 1.5 * lp clock period (%u)\n",
240 dphy_opts
->clk_prepare
, lp_t
);
243 /* 00: lp_t, 01: 1.5 * lp_t */
244 cfg
->mc_prg_hs_prepare
= dphy_opts
->clk_prepare
> lp_t
? 1 : 0;
246 /* hs_zero: formula from NXP BSP */
247 n
= (144 * (dphy_opts
->hs_clk_rate
/ 1000000) - 47500) / 10000;
248 cfg
->m_prg_hs_zero
= n
< 1 ? 1 : n
;
250 /* clk_zero: formula from NXP BSP */
251 n
= (34 * (dphy_opts
->hs_clk_rate
/ 1000000) - 2500) / 1000;
252 cfg
->mc_prg_hs_zero
= n
< 1 ? 1 : n
;
254 /* clk_trail, hs_trail: formula from NXP BSP */
255 n
= (103 * (dphy_opts
->hs_clk_rate
/ 1000000) + 10000) / 10000;
260 cfg
->m_prg_hs_trail
= n
;
261 cfg
->mc_prg_hs_trail
= n
;
263 /* rxhs_settle: formula from NXP BSP */
264 if (dphy_opts
->hs_clk_rate
< MBPS(80))
265 cfg
->rxhs_settle
= 0x0d;
266 else if (dphy_opts
->hs_clk_rate
< MBPS(90))
267 cfg
->rxhs_settle
= 0x0c;
268 else if (dphy_opts
->hs_clk_rate
< MBPS(125))
269 cfg
->rxhs_settle
= 0x0b;
270 else if (dphy_opts
->hs_clk_rate
< MBPS(150))
271 cfg
->rxhs_settle
= 0x0a;
272 else if (dphy_opts
->hs_clk_rate
< MBPS(225))
273 cfg
->rxhs_settle
= 0x09;
274 else if (dphy_opts
->hs_clk_rate
< MBPS(500))
275 cfg
->rxhs_settle
= 0x08;
277 cfg
->rxhs_settle
= 0x07;
279 dev_dbg(&phy
->dev
, "phy_config: %u %u %u %u %u %u %u\n",
280 cfg
->m_prg_hs_prepare
, cfg
->mc_prg_hs_prepare
,
281 cfg
->m_prg_hs_zero
, cfg
->mc_prg_hs_zero
,
282 cfg
->m_prg_hs_trail
, cfg
->mc_prg_hs_trail
,
288 static void mixel_phy_set_hs_timings(struct phy
*phy
)
290 struct mixel_dphy_priv
*priv
= phy_get_drvdata(phy
);
292 phy_write(phy
, priv
->cfg
.m_prg_hs_prepare
, DPHY_M_PRG_HS_PREPARE
);
293 phy_write(phy
, priv
->cfg
.mc_prg_hs_prepare
, DPHY_MC_PRG_HS_PREPARE
);
294 phy_write(phy
, priv
->cfg
.m_prg_hs_zero
, DPHY_M_PRG_HS_ZERO
);
295 phy_write(phy
, priv
->cfg
.mc_prg_hs_zero
, DPHY_MC_PRG_HS_ZERO
);
296 phy_write(phy
, priv
->cfg
.m_prg_hs_trail
, DPHY_M_PRG_HS_TRAIL
);
297 phy_write(phy
, priv
->cfg
.mc_prg_hs_trail
, DPHY_MC_PRG_HS_TRAIL
);
298 phy_write(phy
, priv
->cfg
.rxhs_settle
, priv
->devdata
->reg_rxhs_settle
);
301 static int mixel_dphy_set_pll_params(struct phy
*phy
)
303 struct mixel_dphy_priv
*priv
= dev_get_drvdata(phy
->dev
.parent
);
305 if (priv
->cfg
.cm
< 16 || priv
->cfg
.cm
> 255 ||
306 priv
->cfg
.cn
< 1 || priv
->cfg
.cn
> 32 ||
307 priv
->cfg
.co
< 1 || priv
->cfg
.co
> 8) {
308 dev_err(&phy
->dev
, "Invalid CM/CN/CO values! (%u/%u/%u)\n",
309 priv
->cfg
.cm
, priv
->cfg
.cn
, priv
->cfg
.co
);
312 dev_dbg(&phy
->dev
, "Using CM:%u CN:%u CO:%u\n",
313 priv
->cfg
.cm
, priv
->cfg
.cn
, priv
->cfg
.co
);
314 phy_write(phy
, CM(priv
->cfg
.cm
), DPHY_CM
);
315 phy_write(phy
, CN(priv
->cfg
.cn
), DPHY_CN
);
316 phy_write(phy
, CO(priv
->cfg
.co
), DPHY_CO
);
320 static int mixel_dphy_configure(struct phy
*phy
, union phy_configure_opts
*opts
)
322 struct mixel_dphy_priv
*priv
= phy_get_drvdata(phy
);
323 struct mixel_dphy_cfg cfg
= { 0 };
326 ret
= mixel_dphy_config_from_opts(phy
, &opts
->mipi_dphy
, &cfg
);
330 /* Update the configuration */
331 memcpy(&priv
->cfg
, &cfg
, sizeof(struct mixel_dphy_cfg
));
333 phy_write(phy
, 0x00, DPHY_LOCK_BYP
);
334 phy_write(phy
, 0x01, priv
->devdata
->reg_tx_rcal
);
335 phy_write(phy
, 0x00, priv
->devdata
->reg_auto_pd_en
);
336 phy_write(phy
, 0x02, priv
->devdata
->reg_rxlprp
);
337 phy_write(phy
, 0x02, priv
->devdata
->reg_rxcdrp
);
338 phy_write(phy
, 0x25, DPHY_TST
);
340 mixel_phy_set_hs_timings(phy
);
341 ret
= mixel_dphy_set_pll_params(phy
);
348 static int mixel_dphy_validate(struct phy
*phy
, enum phy_mode mode
, int submode
,
349 union phy_configure_opts
*opts
)
351 struct mixel_dphy_cfg cfg
= { 0 };
353 if (mode
!= PHY_MODE_MIPI_DPHY
)
356 return mixel_dphy_config_from_opts(phy
, &opts
->mipi_dphy
, &cfg
);
359 static int mixel_dphy_init(struct phy
*phy
)
361 phy_write(phy
, PWR_OFF
, DPHY_PD_PLL
);
362 phy_write(phy
, PWR_OFF
, DPHY_PD_DPHY
);
367 static int mixel_dphy_exit(struct phy
*phy
)
369 phy_write(phy
, 0, DPHY_CM
);
370 phy_write(phy
, 0, DPHY_CN
);
371 phy_write(phy
, 0, DPHY_CO
);
376 static int mixel_dphy_power_on(struct phy
*phy
)
378 struct mixel_dphy_priv
*priv
= phy_get_drvdata(phy
);
382 ret
= clk_prepare_enable(priv
->phy_ref_clk
);
386 phy_write(phy
, PWR_ON
, DPHY_PD_PLL
);
387 ret
= regmap_read_poll_timeout(priv
->regmap
, DPHY_LOCK
, locked
,
388 locked
, PLL_LOCK_SLEEP
,
391 dev_err(&phy
->dev
, "Could not get DPHY lock (%d)!\n", ret
);
394 phy_write(phy
, PWR_ON
, DPHY_PD_DPHY
);
398 clk_disable_unprepare(priv
->phy_ref_clk
);
402 static int mixel_dphy_power_off(struct phy
*phy
)
404 struct mixel_dphy_priv
*priv
= phy_get_drvdata(phy
);
406 phy_write(phy
, PWR_OFF
, DPHY_PD_PLL
);
407 phy_write(phy
, PWR_OFF
, DPHY_PD_DPHY
);
409 clk_disable_unprepare(priv
->phy_ref_clk
);
414 static const struct phy_ops mixel_dphy_phy_ops
= {
415 .init
= mixel_dphy_init
,
416 .exit
= mixel_dphy_exit
,
417 .power_on
= mixel_dphy_power_on
,
418 .power_off
= mixel_dphy_power_off
,
419 .configure
= mixel_dphy_configure
,
420 .validate
= mixel_dphy_validate
,
421 .owner
= THIS_MODULE
,
424 static const struct of_device_id mixel_dphy_of_match
[] = {
425 { .compatible
= "fsl,imx8mq-mipi-dphy",
426 .data
= &mixel_dphy_devdata
[MIXEL_IMX8MQ
] },
429 MODULE_DEVICE_TABLE(of
, mixel_dphy_of_match
);
431 static int mixel_dphy_probe(struct platform_device
*pdev
)
433 struct device
*dev
= &pdev
->dev
;
434 struct device_node
*np
= dev
->of_node
;
435 struct phy_provider
*phy_provider
;
436 struct mixel_dphy_priv
*priv
;
437 struct resource
*res
;
444 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
448 priv
->devdata
= of_device_get_match_data(&pdev
->dev
);
452 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
453 base
= devm_ioremap_resource(dev
, res
);
455 return PTR_ERR(base
);
457 priv
->regmap
= devm_regmap_init_mmio(&pdev
->dev
, base
,
458 &mixel_dphy_regmap_config
);
459 if (IS_ERR(priv
->regmap
)) {
460 dev_err(dev
, "Couldn't create the DPHY regmap\n");
461 return PTR_ERR(priv
->regmap
);
464 priv
->phy_ref_clk
= devm_clk_get(&pdev
->dev
, "phy_ref");
465 if (IS_ERR(priv
->phy_ref_clk
)) {
466 dev_err(dev
, "No phy_ref clock found\n");
467 return PTR_ERR(priv
->phy_ref_clk
);
469 dev_dbg(dev
, "phy_ref clock rate: %lu\n",
470 clk_get_rate(priv
->phy_ref_clk
));
472 dev_set_drvdata(dev
, priv
);
474 phy
= devm_phy_create(dev
, np
, &mixel_dphy_phy_ops
);
476 dev_err(dev
, "Failed to create phy %ld\n", PTR_ERR(phy
));
479 phy_set_drvdata(phy
, priv
);
481 phy_provider
= devm_of_phy_provider_register(dev
, of_phy_simple_xlate
);
483 return PTR_ERR_OR_ZERO(phy_provider
);
486 static struct platform_driver mixel_dphy_driver
= {
487 .probe
= mixel_dphy_probe
,
489 .name
= "mixel-mipi-dphy",
490 .of_match_table
= mixel_dphy_of_match
,
493 module_platform_driver(mixel_dphy_driver
);
495 MODULE_AUTHOR("NXP Semiconductor");
496 MODULE_DESCRIPTION("Mixel MIPI-DSI PHY driver");
497 MODULE_LICENSE("GPL");