1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright 2017-2020,2022 NXP
6 #include <linux/bitfield.h>
7 #include <linux/bits.h>
9 #include <linux/mfd/syscon.h>
10 #include <linux/module.h>
12 #include <linux/phy/phy.h>
13 #include <linux/platform_device.h>
14 #include <linux/pm_runtime.h>
15 #include <linux/regmap.h>
16 #include <linux/units.h>
22 #define M_MASK GENMASK(18, 17)
23 #define M(n) FIELD_PREP(M_MASK, (n))
24 #define CCM_MASK GENMASK(16, 14)
25 #define CCM(n) FIELD_PREP(CCM_MASK, (n))
26 #define CA_MASK GENMASK(13, 11)
27 #define CA(n) FIELD_PREP(CA_MASK, (n))
28 #define TST_MASK GENMASK(10, 5)
29 #define TST(n) FIELD_PREP(TST_MASK, (n))
30 #define CH_EN(id) BIT(3 + (id))
35 /* Power On Reset(POR) value */
36 #define CTRL_RESET_VAL (M(0x0) | CCM(0x4) | CA(0x4) | TST(0x25))
38 /* PHY initialization value and mask */
39 #define CTRL_INIT_MASK (M_MASK | CCM_MASK | CA_MASK | TST_MASK | NB | RFB)
40 #define CTRL_INIT_VAL (M(0x0) | CCM(0x5) | CA(0x4) | TST(0x25) | RFB)
42 #define PHY_STATUS 0x10
47 #define MIN_CLKIN_FREQ (25 * MEGA)
48 #define MAX_CLKIN_FREQ (165 * MEGA)
50 #define PLL_LOCK_SLEEP 10
51 #define PLL_LOCK_TIMEOUT 1000
53 struct mixel_lvds_phy
{
55 struct phy_configure_opts_lvds cfg
;
59 struct mixel_lvds_phy_priv
{
60 struct regmap
*regmap
;
61 struct mutex lock
; /* protect remap access and cfg of our own */
62 struct clk
*phy_ref_clk
;
63 struct mixel_lvds_phy
*phys
[PHY_NUM
];
66 static int mixel_lvds_phy_init(struct phy
*phy
)
68 struct mixel_lvds_phy_priv
*priv
= dev_get_drvdata(phy
->dev
.parent
);
70 mutex_lock(&priv
->lock
);
71 regmap_update_bits(priv
->regmap
,
72 PHY_CTRL
, CTRL_INIT_MASK
, CTRL_INIT_VAL
);
73 mutex_unlock(&priv
->lock
);
78 static int mixel_lvds_phy_power_on(struct phy
*phy
)
80 struct mixel_lvds_phy_priv
*priv
= dev_get_drvdata(phy
->dev
.parent
);
81 struct mixel_lvds_phy
*lvds_phy
= phy_get_drvdata(phy
);
82 struct mixel_lvds_phy
*companion
= priv
->phys
[lvds_phy
->id
^ 1];
83 struct phy_configure_opts_lvds
*cfg
= &lvds_phy
->cfg
;
88 /* The master PHY would power on the slave PHY. */
92 ret
= clk_prepare_enable(priv
->phy_ref_clk
);
95 "failed to enable PHY reference clock: %d\n", ret
);
99 mutex_lock(&priv
->lock
);
100 if (cfg
->bits_per_lane_and_dclk_cycle
== 7) {
101 if (cfg
->differential_clk_rate
< 44000000)
103 else if (cfg
->differential_clk_rate
< 90000000)
110 if (cfg
->differential_clk_rate
< 32000000)
112 else if (cfg
->differential_clk_rate
< 63000000)
117 regmap_update_bits(priv
->regmap
, PHY_CTRL
, M_MASK
| NB
, val
);
120 * Enable two channels synchronously,
121 * if the companion PHY is a slave PHY.
123 if (companion
->cfg
.is_slave
)
124 val
= CH_EN(0) | CH_EN(1);
126 val
= CH_EN(lvds_phy
->id
);
127 regmap_write(priv
->regmap
, PHY_CTRL
+ REG_SET
, val
);
129 ret
= regmap_read_poll_timeout(priv
->regmap
, PHY_STATUS
, locked
,
130 locked
, PLL_LOCK_SLEEP
,
133 dev_err(&phy
->dev
, "failed to get PHY lock: %d\n", ret
);
134 clk_disable_unprepare(priv
->phy_ref_clk
);
136 mutex_unlock(&priv
->lock
);
141 static int mixel_lvds_phy_power_off(struct phy
*phy
)
143 struct mixel_lvds_phy_priv
*priv
= dev_get_drvdata(phy
->dev
.parent
);
144 struct mixel_lvds_phy
*lvds_phy
= phy_get_drvdata(phy
);
145 struct mixel_lvds_phy
*companion
= priv
->phys
[lvds_phy
->id
^ 1];
146 struct phy_configure_opts_lvds
*cfg
= &lvds_phy
->cfg
;
148 /* The master PHY would power off the slave PHY. */
152 mutex_lock(&priv
->lock
);
153 if (companion
->cfg
.is_slave
)
154 regmap_write(priv
->regmap
, PHY_CTRL
+ REG_CLR
,
155 CH_EN(0) | CH_EN(1));
157 regmap_write(priv
->regmap
, PHY_CTRL
+ REG_CLR
,
158 CH_EN(lvds_phy
->id
));
159 mutex_unlock(&priv
->lock
);
161 clk_disable_unprepare(priv
->phy_ref_clk
);
166 static int mixel_lvds_phy_configure(struct phy
*phy
,
167 union phy_configure_opts
*opts
)
169 struct mixel_lvds_phy_priv
*priv
= dev_get_drvdata(phy
->dev
.parent
);
170 struct phy_configure_opts_lvds
*cfg
= &opts
->lvds
;
173 ret
= clk_set_rate(priv
->phy_ref_clk
, cfg
->differential_clk_rate
);
175 dev_err(&phy
->dev
, "failed to set PHY reference clock rate(%lu): %d\n",
176 cfg
->differential_clk_rate
, ret
);
181 /* Assume the master PHY's configuration set is cached first. */
182 static int mixel_lvds_phy_check_slave(struct phy
*slave_phy
)
184 struct device
*dev
= &slave_phy
->dev
;
185 struct mixel_lvds_phy_priv
*priv
= dev_get_drvdata(dev
->parent
);
186 struct mixel_lvds_phy
*slv
= phy_get_drvdata(slave_phy
);
187 struct mixel_lvds_phy
*mst
= priv
->phys
[slv
->id
^ 1];
188 struct phy_configure_opts_lvds
*mst_cfg
= &mst
->cfg
;
189 struct phy_configure_opts_lvds
*slv_cfg
= &slv
->cfg
;
191 if (mst_cfg
->bits_per_lane_and_dclk_cycle
!=
192 slv_cfg
->bits_per_lane_and_dclk_cycle
) {
193 dev_err(dev
, "number bits mismatch(mst: %u vs slv: %u)\n",
194 mst_cfg
->bits_per_lane_and_dclk_cycle
,
195 slv_cfg
->bits_per_lane_and_dclk_cycle
);
199 if (mst_cfg
->differential_clk_rate
!=
200 slv_cfg
->differential_clk_rate
) {
201 dev_err(dev
, "dclk rate mismatch(mst: %lu vs slv: %lu)\n",
202 mst_cfg
->differential_clk_rate
,
203 slv_cfg
->differential_clk_rate
);
207 if (mst_cfg
->lanes
!= slv_cfg
->lanes
) {
208 dev_err(dev
, "lanes mismatch(mst: %u vs slv: %u)\n",
209 mst_cfg
->lanes
, slv_cfg
->lanes
);
213 if (mst_cfg
->is_slave
== slv_cfg
->is_slave
) {
214 dev_err(dev
, "master PHY is not found\n");
221 static int mixel_lvds_phy_validate(struct phy
*phy
, enum phy_mode mode
,
222 int submode
, union phy_configure_opts
*opts
)
224 struct mixel_lvds_phy_priv
*priv
= dev_get_drvdata(phy
->dev
.parent
);
225 struct mixel_lvds_phy
*lvds_phy
= phy_get_drvdata(phy
);
226 struct phy_configure_opts_lvds
*cfg
= &opts
->lvds
;
229 if (mode
!= PHY_MODE_LVDS
) {
230 dev_err(&phy
->dev
, "invalid PHY mode(%d)\n", mode
);
234 if (cfg
->bits_per_lane_and_dclk_cycle
!= 7 &&
235 cfg
->bits_per_lane_and_dclk_cycle
!= 10) {
236 dev_err(&phy
->dev
, "invalid bits per data lane(%u)\n",
237 cfg
->bits_per_lane_and_dclk_cycle
);
241 if (cfg
->lanes
!= 4 && cfg
->lanes
!= 3) {
242 dev_err(&phy
->dev
, "invalid data lanes(%u)\n", cfg
->lanes
);
246 if (cfg
->differential_clk_rate
< MIN_CLKIN_FREQ
||
247 cfg
->differential_clk_rate
> MAX_CLKIN_FREQ
) {
248 dev_err(&phy
->dev
, "invalid differential clock rate(%lu)\n",
249 cfg
->differential_clk_rate
);
253 mutex_lock(&priv
->lock
);
254 /* cache configuration set of our own for check */
255 memcpy(&lvds_phy
->cfg
, cfg
, sizeof(*cfg
));
258 ret
= mixel_lvds_phy_check_slave(phy
);
260 dev_err(&phy
->dev
, "failed to check slave PHY: %d\n", ret
);
262 mutex_unlock(&priv
->lock
);
267 static const struct phy_ops mixel_lvds_phy_ops
= {
268 .init
= mixel_lvds_phy_init
,
269 .power_on
= mixel_lvds_phy_power_on
,
270 .power_off
= mixel_lvds_phy_power_off
,
271 .configure
= mixel_lvds_phy_configure
,
272 .validate
= mixel_lvds_phy_validate
,
273 .owner
= THIS_MODULE
,
276 static int mixel_lvds_phy_reset(struct device
*dev
)
278 struct mixel_lvds_phy_priv
*priv
= dev_get_drvdata(dev
);
281 ret
= pm_runtime_resume_and_get(dev
);
283 dev_err(dev
, "failed to get PM runtime: %d\n", ret
);
287 regmap_write(priv
->regmap
, PHY_CTRL
, CTRL_RESET_VAL
);
289 ret
= pm_runtime_put(dev
);
291 dev_err(dev
, "failed to put PM runtime: %d\n", ret
);
296 static struct phy
*mixel_lvds_phy_xlate(struct device
*dev
,
297 const struct of_phandle_args
*args
)
299 struct mixel_lvds_phy_priv
*priv
= dev_get_drvdata(dev
);
302 if (args
->args_count
!= 1) {
304 "invalid argument number(%d) for 'phys' property\n",
306 return ERR_PTR(-EINVAL
);
309 phy_id
= args
->args
[0];
311 if (phy_id
>= PHY_NUM
) {
312 dev_err(dev
, "invalid PHY index(%d)\n", phy_id
);
313 return ERR_PTR(-ENODEV
);
316 return priv
->phys
[phy_id
]->phy
;
319 static int mixel_lvds_phy_probe(struct platform_device
*pdev
)
321 struct device
*dev
= &pdev
->dev
;
322 struct phy_provider
*phy_provider
;
323 struct mixel_lvds_phy_priv
*priv
;
324 struct mixel_lvds_phy
*lvds_phy
;
332 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
336 priv
->regmap
= syscon_node_to_regmap(dev
->of_node
->parent
);
337 if (IS_ERR(priv
->regmap
))
338 return dev_err_probe(dev
, PTR_ERR(priv
->regmap
),
339 "failed to get regmap\n");
341 priv
->phy_ref_clk
= devm_clk_get(dev
, NULL
);
342 if (IS_ERR(priv
->phy_ref_clk
))
343 return dev_err_probe(dev
, PTR_ERR(priv
->phy_ref_clk
),
344 "failed to get PHY reference clock\n");
346 mutex_init(&priv
->lock
);
348 dev_set_drvdata(dev
, priv
);
350 pm_runtime_enable(dev
);
352 ret
= mixel_lvds_phy_reset(dev
);
354 dev_err(dev
, "failed to do POR reset: %d\n", ret
);
358 for (i
= 0; i
< PHY_NUM
; i
++) {
359 lvds_phy
= devm_kzalloc(dev
, sizeof(*lvds_phy
), GFP_KERNEL
);
365 phy
= devm_phy_create(dev
, NULL
, &mixel_lvds_phy_ops
);
368 dev_err(dev
, "failed to create PHY for channel%d: %d\n",
375 priv
->phys
[i
] = lvds_phy
;
377 phy_set_drvdata(phy
, lvds_phy
);
380 phy_provider
= devm_of_phy_provider_register(dev
, mixel_lvds_phy_xlate
);
381 if (IS_ERR(phy_provider
)) {
382 ret
= PTR_ERR(phy_provider
);
383 dev_err(dev
, "failed to register PHY provider: %d\n", ret
);
389 pm_runtime_disable(dev
);
394 static void mixel_lvds_phy_remove(struct platform_device
*pdev
)
396 pm_runtime_disable(&pdev
->dev
);
399 static int __maybe_unused
mixel_lvds_phy_runtime_suspend(struct device
*dev
)
401 struct mixel_lvds_phy_priv
*priv
= dev_get_drvdata(dev
);
404 mutex_lock(&priv
->lock
);
405 regmap_write(priv
->regmap
, PHY_CTRL
+ REG_SET
, PD
);
406 mutex_unlock(&priv
->lock
);
411 static int __maybe_unused
mixel_lvds_phy_runtime_resume(struct device
*dev
)
413 struct mixel_lvds_phy_priv
*priv
= dev_get_drvdata(dev
);
415 /* power up + control initialization */
416 mutex_lock(&priv
->lock
);
417 regmap_update_bits(priv
->regmap
, PHY_CTRL
,
418 CTRL_INIT_MASK
| PD
, CTRL_INIT_VAL
);
419 mutex_unlock(&priv
->lock
);
424 static const struct dev_pm_ops mixel_lvds_phy_pm_ops
= {
425 SET_RUNTIME_PM_OPS(mixel_lvds_phy_runtime_suspend
,
426 mixel_lvds_phy_runtime_resume
, NULL
)
429 static const struct of_device_id mixel_lvds_phy_of_match
[] = {
430 { .compatible
= "fsl,imx8qm-lvds-phy" },
433 MODULE_DEVICE_TABLE(of
, mixel_lvds_phy_of_match
);
435 static struct platform_driver mixel_lvds_phy_driver
= {
436 .probe
= mixel_lvds_phy_probe
,
437 .remove
= mixel_lvds_phy_remove
,
439 .pm
= &mixel_lvds_phy_pm_ops
,
440 .name
= "mixel-lvds-phy",
441 .of_match_table
= mixel_lvds_phy_of_match
,
444 module_platform_driver(mixel_lvds_phy_driver
);
446 MODULE_DESCRIPTION("Mixel LVDS PHY driver");
447 MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>");
448 MODULE_LICENSE("GPL");