Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

phy: freescale: Add i.MX8qm Mixel LVDS PHY support

Add Freescale i.MX8qm LVDS PHY support.
The PHY IP is from Mixel, Inc.

Signed-off-by: Liu Ying <victor.liu@nxp.com>
Link: https://lore.kernel.org/r/20220706034810.2352641-4-victor.liu@nxp.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>

authored by

Liu Ying and committed by
Vinod Koul
06ff622d 4a902a02

+460
+9
drivers/phy/freescale/Kconfig
··· 8 8 select GENERIC_PHY 9 9 default ARCH_MXC && ARM64 10 10 11 + config PHY_MIXEL_LVDS_PHY 12 + tristate "Mixel LVDS PHY support" 13 + depends on OF 14 + select GENERIC_PHY 15 + select REGMAP_MMIO 16 + help 17 + Enable this to add support for the Mixel LVDS PHY as found 18 + on NXP's i.MX8qm SoC. 19 + 11 20 config PHY_MIXEL_MIPI_DPHY 12 21 tristate "Mixel MIPI DSI PHY support" 13 22 depends on OF && HAS_IOMEM
+1
drivers/phy/freescale/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0-only 2 2 obj-$(CONFIG_PHY_FSL_IMX8MQ_USB) += phy-fsl-imx8mq-usb.o 3 + obj-$(CONFIG_PHY_MIXEL_LVDS_PHY) += phy-fsl-imx8qm-lvds-phy.o 3 4 obj-$(CONFIG_PHY_MIXEL_MIPI_DPHY) += phy-fsl-imx8-mipi-dphy.o 4 5 obj-$(CONFIG_PHY_FSL_IMX8M_PCIE) += phy-fsl-imx8m-pcie.o 5 6 obj-$(CONFIG_PHY_FSL_LYNX_28G) += phy-fsl-lynx-28g.o
+450
drivers/phy/freescale/phy-fsl-imx8qm-lvds-phy.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + /* 3 + * Copyright 2017-2020,2022 NXP 4 + */ 5 + 6 + #include <linux/bitfield.h> 7 + #include <linux/bits.h> 8 + #include <linux/clk.h> 9 + #include <linux/mfd/syscon.h> 10 + #include <linux/module.h> 11 + #include <linux/of.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> 17 + 18 + #define REG_SET 0x4 19 + #define REG_CLR 0x8 20 + 21 + #define PHY_CTRL 0x0 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)) 31 + #define NB BIT(2) 32 + #define RFB BIT(1) 33 + #define PD BIT(0) 34 + 35 + /* Power On Reset(POR) value */ 36 + #define CTRL_RESET_VAL (M(0x0) | CCM(0x4) | CA(0x4) | TST(0x25)) 37 + 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) 41 + 42 + #define PHY_STATUS 0x10 43 + #define LOCK BIT(0) 44 + 45 + #define PHY_NUM 2 46 + 47 + #define MIN_CLKIN_FREQ (25 * MEGA) 48 + #define MAX_CLKIN_FREQ (165 * MEGA) 49 + 50 + #define PLL_LOCK_SLEEP 10 51 + #define PLL_LOCK_TIMEOUT 1000 52 + 53 + struct mixel_lvds_phy { 54 + struct phy *phy; 55 + struct phy_configure_opts_lvds cfg; 56 + unsigned int id; 57 + }; 58 + 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]; 64 + }; 65 + 66 + static int mixel_lvds_phy_init(struct phy *phy) 67 + { 68 + struct mixel_lvds_phy_priv *priv = dev_get_drvdata(phy->dev.parent); 69 + 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); 74 + 75 + return 0; 76 + } 77 + 78 + static int mixel_lvds_phy_power_on(struct phy *phy) 79 + { 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; 84 + u32 val = 0; 85 + u32 locked; 86 + int ret; 87 + 88 + /* The master PHY would power on the slave PHY. */ 89 + if (cfg->is_slave) 90 + return 0; 91 + 92 + ret = clk_prepare_enable(priv->phy_ref_clk); 93 + if (ret < 0) { 94 + dev_err(&phy->dev, 95 + "failed to enable PHY reference clock: %d\n", ret); 96 + return ret; 97 + } 98 + 99 + mutex_lock(&priv->lock); 100 + if (cfg->bits_per_lane_and_dclk_cycle == 7) { 101 + if (cfg->differential_clk_rate < 44000000) 102 + val |= M(0x2); 103 + else if (cfg->differential_clk_rate < 90000000) 104 + val |= M(0x1); 105 + else 106 + val |= M(0x0); 107 + } else { 108 + val = NB; 109 + 110 + if (cfg->differential_clk_rate < 32000000) 111 + val |= M(0x2); 112 + else if (cfg->differential_clk_rate < 63000000) 113 + val |= M(0x1); 114 + else 115 + val |= M(0x0); 116 + } 117 + regmap_update_bits(priv->regmap, PHY_CTRL, M_MASK | NB, val); 118 + 119 + /* 120 + * Enable two channels synchronously, 121 + * if the companion PHY is a slave PHY. 122 + */ 123 + if (companion->cfg.is_slave) 124 + val = CH_EN(0) | CH_EN(1); 125 + else 126 + val = CH_EN(lvds_phy->id); 127 + regmap_write(priv->regmap, PHY_CTRL + REG_SET, val); 128 + 129 + ret = regmap_read_poll_timeout(priv->regmap, PHY_STATUS, locked, 130 + locked, PLL_LOCK_SLEEP, 131 + PLL_LOCK_TIMEOUT); 132 + if (ret < 0) { 133 + dev_err(&phy->dev, "failed to get PHY lock: %d\n", ret); 134 + clk_disable_unprepare(priv->phy_ref_clk); 135 + } 136 + mutex_unlock(&priv->lock); 137 + 138 + return ret; 139 + } 140 + 141 + static int mixel_lvds_phy_power_off(struct phy *phy) 142 + { 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; 147 + 148 + /* The master PHY would power off the slave PHY. */ 149 + if (cfg->is_slave) 150 + return 0; 151 + 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)); 156 + else 157 + regmap_write(priv->regmap, PHY_CTRL + REG_CLR, 158 + CH_EN(lvds_phy->id)); 159 + mutex_unlock(&priv->lock); 160 + 161 + clk_disable_unprepare(priv->phy_ref_clk); 162 + 163 + return 0; 164 + } 165 + 166 + static int mixel_lvds_phy_configure(struct phy *phy, 167 + union phy_configure_opts *opts) 168 + { 169 + struct mixel_lvds_phy_priv *priv = dev_get_drvdata(phy->dev.parent); 170 + struct phy_configure_opts_lvds *cfg = &opts->lvds; 171 + int ret; 172 + 173 + ret = clk_set_rate(priv->phy_ref_clk, cfg->differential_clk_rate); 174 + if (ret) 175 + dev_err(&phy->dev, "failed to set PHY reference clock rate(%lu): %d\n", 176 + cfg->differential_clk_rate, ret); 177 + 178 + return ret; 179 + } 180 + 181 + /* Assume the master PHY's configuration set is cached first. */ 182 + static int mixel_lvds_phy_check_slave(struct phy *slave_phy) 183 + { 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; 190 + 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); 196 + return -EINVAL; 197 + } 198 + 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); 204 + return -EINVAL; 205 + } 206 + 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); 210 + return -EINVAL; 211 + } 212 + 213 + if (mst_cfg->is_slave == slv_cfg->is_slave) { 214 + dev_err(dev, "master PHY is not found\n"); 215 + return -EINVAL; 216 + } 217 + 218 + return 0; 219 + } 220 + 221 + static int mixel_lvds_phy_validate(struct phy *phy, enum phy_mode mode, 222 + int submode, union phy_configure_opts *opts) 223 + { 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; 227 + int ret = 0; 228 + 229 + if (mode != PHY_MODE_LVDS) { 230 + dev_err(&phy->dev, "invalid PHY mode(%d)\n", mode); 231 + return -EINVAL; 232 + } 233 + 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); 238 + return -EINVAL; 239 + } 240 + 241 + if (cfg->lanes != 4 && cfg->lanes != 3) { 242 + dev_err(&phy->dev, "invalid data lanes(%u)\n", cfg->lanes); 243 + return -EINVAL; 244 + } 245 + 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); 250 + return -EINVAL; 251 + } 252 + 253 + mutex_lock(&priv->lock); 254 + /* cache configuration set of our own for check */ 255 + memcpy(&lvds_phy->cfg, cfg, sizeof(*cfg)); 256 + 257 + if (cfg->is_slave) { 258 + ret = mixel_lvds_phy_check_slave(phy); 259 + if (ret) 260 + dev_err(&phy->dev, "failed to check slave PHY: %d\n", ret); 261 + } 262 + mutex_unlock(&priv->lock); 263 + 264 + return ret; 265 + } 266 + 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, 274 + }; 275 + 276 + static int mixel_lvds_phy_reset(struct device *dev) 277 + { 278 + struct mixel_lvds_phy_priv *priv = dev_get_drvdata(dev); 279 + int ret; 280 + 281 + ret = pm_runtime_resume_and_get(dev); 282 + if (ret < 0) { 283 + dev_err(dev, "failed to get PM runtime: %d\n", ret); 284 + return ret; 285 + } 286 + 287 + regmap_write(priv->regmap, PHY_CTRL, CTRL_RESET_VAL); 288 + 289 + ret = pm_runtime_put(dev); 290 + if (ret < 0) 291 + dev_err(dev, "failed to put PM runtime: %d\n", ret); 292 + 293 + return ret; 294 + } 295 + 296 + static struct phy *mixel_lvds_phy_xlate(struct device *dev, 297 + struct of_phandle_args *args) 298 + { 299 + struct mixel_lvds_phy_priv *priv = dev_get_drvdata(dev); 300 + unsigned int phy_id; 301 + 302 + if (args->args_count != 1) { 303 + dev_err(dev, 304 + "invalid argument number(%d) for 'phys' property\n", 305 + args->args_count); 306 + return ERR_PTR(-EINVAL); 307 + } 308 + 309 + phy_id = args->args[0]; 310 + 311 + if (phy_id >= PHY_NUM) { 312 + dev_err(dev, "invalid PHY index(%d)\n", phy_id); 313 + return ERR_PTR(-ENODEV); 314 + } 315 + 316 + return priv->phys[phy_id]->phy; 317 + } 318 + 319 + static int mixel_lvds_phy_probe(struct platform_device *pdev) 320 + { 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; 325 + struct phy *phy; 326 + int i; 327 + int ret; 328 + 329 + if (!dev->of_node) 330 + return -ENODEV; 331 + 332 + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 333 + if (!priv) 334 + return -ENOMEM; 335 + 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"); 340 + 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"); 345 + 346 + mutex_init(&priv->lock); 347 + 348 + dev_set_drvdata(dev, priv); 349 + 350 + pm_runtime_enable(dev); 351 + 352 + ret = mixel_lvds_phy_reset(dev); 353 + if (ret) { 354 + dev_err(dev, "failed to do POR reset: %d\n", ret); 355 + return ret; 356 + } 357 + 358 + for (i = 0; i < PHY_NUM; i++) { 359 + lvds_phy = devm_kzalloc(dev, sizeof(*lvds_phy), GFP_KERNEL); 360 + if (!lvds_phy) { 361 + ret = -ENOMEM; 362 + goto err; 363 + } 364 + 365 + phy = devm_phy_create(dev, NULL, &mixel_lvds_phy_ops); 366 + if (IS_ERR(phy)) { 367 + ret = PTR_ERR(phy); 368 + dev_err(dev, "failed to create PHY for channel%d: %d\n", 369 + i, ret); 370 + goto err; 371 + } 372 + 373 + lvds_phy->phy = phy; 374 + lvds_phy->id = i; 375 + priv->phys[i] = lvds_phy; 376 + 377 + phy_set_drvdata(phy, lvds_phy); 378 + } 379 + 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); 384 + goto err; 385 + } 386 + 387 + return 0; 388 + err: 389 + pm_runtime_disable(dev); 390 + 391 + return ret; 392 + } 393 + 394 + static int mixel_lvds_phy_remove(struct platform_device *pdev) 395 + { 396 + pm_runtime_disable(&pdev->dev); 397 + 398 + return 0; 399 + } 400 + 401 + static int __maybe_unused mixel_lvds_phy_runtime_suspend(struct device *dev) 402 + { 403 + struct mixel_lvds_phy_priv *priv = dev_get_drvdata(dev); 404 + 405 + /* power down */ 406 + mutex_lock(&priv->lock); 407 + regmap_write(priv->regmap, PHY_CTRL + REG_SET, PD); 408 + mutex_unlock(&priv->lock); 409 + 410 + return 0; 411 + } 412 + 413 + static int __maybe_unused mixel_lvds_phy_runtime_resume(struct device *dev) 414 + { 415 + struct mixel_lvds_phy_priv *priv = dev_get_drvdata(dev); 416 + 417 + /* power up + control initialization */ 418 + mutex_lock(&priv->lock); 419 + regmap_update_bits(priv->regmap, PHY_CTRL, 420 + CTRL_INIT_MASK | PD, CTRL_INIT_VAL); 421 + mutex_unlock(&priv->lock); 422 + 423 + return 0; 424 + } 425 + 426 + static const struct dev_pm_ops mixel_lvds_phy_pm_ops = { 427 + SET_RUNTIME_PM_OPS(mixel_lvds_phy_runtime_suspend, 428 + mixel_lvds_phy_runtime_resume, NULL) 429 + }; 430 + 431 + static const struct of_device_id mixel_lvds_phy_of_match[] = { 432 + { .compatible = "fsl,imx8qm-lvds-phy" }, 433 + { /* sentinel */ } 434 + }; 435 + MODULE_DEVICE_TABLE(of, mixel_lvds_phy_of_match); 436 + 437 + static struct platform_driver mixel_lvds_phy_driver = { 438 + .probe = mixel_lvds_phy_probe, 439 + .remove = mixel_lvds_phy_remove, 440 + .driver = { 441 + .pm = &mixel_lvds_phy_pm_ops, 442 + .name = "mixel-lvds-phy", 443 + .of_match_table = mixel_lvds_phy_of_match, 444 + } 445 + }; 446 + module_platform_driver(mixel_lvds_phy_driver); 447 + 448 + MODULE_DESCRIPTION("Mixel LVDS PHY driver"); 449 + MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>"); 450 + MODULE_LICENSE("GPL");