162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2017-2020,2022 NXP 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/bitfield.h> 762306a36Sopenharmony_ci#include <linux/bits.h> 862306a36Sopenharmony_ci#include <linux/clk.h> 962306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/of.h> 1262306a36Sopenharmony_ci#include <linux/phy/phy.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1562306a36Sopenharmony_ci#include <linux/regmap.h> 1662306a36Sopenharmony_ci#include <linux/units.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define REG_SET 0x4 1962306a36Sopenharmony_ci#define REG_CLR 0x8 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define PHY_CTRL 0x0 2262306a36Sopenharmony_ci#define M_MASK GENMASK(18, 17) 2362306a36Sopenharmony_ci#define M(n) FIELD_PREP(M_MASK, (n)) 2462306a36Sopenharmony_ci#define CCM_MASK GENMASK(16, 14) 2562306a36Sopenharmony_ci#define CCM(n) FIELD_PREP(CCM_MASK, (n)) 2662306a36Sopenharmony_ci#define CA_MASK GENMASK(13, 11) 2762306a36Sopenharmony_ci#define CA(n) FIELD_PREP(CA_MASK, (n)) 2862306a36Sopenharmony_ci#define TST_MASK GENMASK(10, 5) 2962306a36Sopenharmony_ci#define TST(n) FIELD_PREP(TST_MASK, (n)) 3062306a36Sopenharmony_ci#define CH_EN(id) BIT(3 + (id)) 3162306a36Sopenharmony_ci#define NB BIT(2) 3262306a36Sopenharmony_ci#define RFB BIT(1) 3362306a36Sopenharmony_ci#define PD BIT(0) 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* Power On Reset(POR) value */ 3662306a36Sopenharmony_ci#define CTRL_RESET_VAL (M(0x0) | CCM(0x4) | CA(0x4) | TST(0x25)) 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* PHY initialization value and mask */ 3962306a36Sopenharmony_ci#define CTRL_INIT_MASK (M_MASK | CCM_MASK | CA_MASK | TST_MASK | NB | RFB) 4062306a36Sopenharmony_ci#define CTRL_INIT_VAL (M(0x0) | CCM(0x5) | CA(0x4) | TST(0x25) | RFB) 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define PHY_STATUS 0x10 4362306a36Sopenharmony_ci#define LOCK BIT(0) 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define PHY_NUM 2 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define MIN_CLKIN_FREQ (25 * MEGA) 4862306a36Sopenharmony_ci#define MAX_CLKIN_FREQ (165 * MEGA) 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#define PLL_LOCK_SLEEP 10 5162306a36Sopenharmony_ci#define PLL_LOCK_TIMEOUT 1000 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistruct mixel_lvds_phy { 5462306a36Sopenharmony_ci struct phy *phy; 5562306a36Sopenharmony_ci struct phy_configure_opts_lvds cfg; 5662306a36Sopenharmony_ci unsigned int id; 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistruct mixel_lvds_phy_priv { 6062306a36Sopenharmony_ci struct regmap *regmap; 6162306a36Sopenharmony_ci struct mutex lock; /* protect remap access and cfg of our own */ 6262306a36Sopenharmony_ci struct clk *phy_ref_clk; 6362306a36Sopenharmony_ci struct mixel_lvds_phy *phys[PHY_NUM]; 6462306a36Sopenharmony_ci}; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic int mixel_lvds_phy_init(struct phy *phy) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci struct mixel_lvds_phy_priv *priv = dev_get_drvdata(phy->dev.parent); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci mutex_lock(&priv->lock); 7162306a36Sopenharmony_ci regmap_update_bits(priv->regmap, 7262306a36Sopenharmony_ci PHY_CTRL, CTRL_INIT_MASK, CTRL_INIT_VAL); 7362306a36Sopenharmony_ci mutex_unlock(&priv->lock); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci return 0; 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic int mixel_lvds_phy_power_on(struct phy *phy) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci struct mixel_lvds_phy_priv *priv = dev_get_drvdata(phy->dev.parent); 8162306a36Sopenharmony_ci struct mixel_lvds_phy *lvds_phy = phy_get_drvdata(phy); 8262306a36Sopenharmony_ci struct mixel_lvds_phy *companion = priv->phys[lvds_phy->id ^ 1]; 8362306a36Sopenharmony_ci struct phy_configure_opts_lvds *cfg = &lvds_phy->cfg; 8462306a36Sopenharmony_ci u32 val = 0; 8562306a36Sopenharmony_ci u32 locked; 8662306a36Sopenharmony_ci int ret; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci /* The master PHY would power on the slave PHY. */ 8962306a36Sopenharmony_ci if (cfg->is_slave) 9062306a36Sopenharmony_ci return 0; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci ret = clk_prepare_enable(priv->phy_ref_clk); 9362306a36Sopenharmony_ci if (ret < 0) { 9462306a36Sopenharmony_ci dev_err(&phy->dev, 9562306a36Sopenharmony_ci "failed to enable PHY reference clock: %d\n", ret); 9662306a36Sopenharmony_ci return ret; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci mutex_lock(&priv->lock); 10062306a36Sopenharmony_ci if (cfg->bits_per_lane_and_dclk_cycle == 7) { 10162306a36Sopenharmony_ci if (cfg->differential_clk_rate < 44000000) 10262306a36Sopenharmony_ci val |= M(0x2); 10362306a36Sopenharmony_ci else if (cfg->differential_clk_rate < 90000000) 10462306a36Sopenharmony_ci val |= M(0x1); 10562306a36Sopenharmony_ci else 10662306a36Sopenharmony_ci val |= M(0x0); 10762306a36Sopenharmony_ci } else { 10862306a36Sopenharmony_ci val = NB; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (cfg->differential_clk_rate < 32000000) 11162306a36Sopenharmony_ci val |= M(0x2); 11262306a36Sopenharmony_ci else if (cfg->differential_clk_rate < 63000000) 11362306a36Sopenharmony_ci val |= M(0x1); 11462306a36Sopenharmony_ci else 11562306a36Sopenharmony_ci val |= M(0x0); 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci regmap_update_bits(priv->regmap, PHY_CTRL, M_MASK | NB, val); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci /* 12062306a36Sopenharmony_ci * Enable two channels synchronously, 12162306a36Sopenharmony_ci * if the companion PHY is a slave PHY. 12262306a36Sopenharmony_ci */ 12362306a36Sopenharmony_ci if (companion->cfg.is_slave) 12462306a36Sopenharmony_ci val = CH_EN(0) | CH_EN(1); 12562306a36Sopenharmony_ci else 12662306a36Sopenharmony_ci val = CH_EN(lvds_phy->id); 12762306a36Sopenharmony_ci regmap_write(priv->regmap, PHY_CTRL + REG_SET, val); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci ret = regmap_read_poll_timeout(priv->regmap, PHY_STATUS, locked, 13062306a36Sopenharmony_ci locked, PLL_LOCK_SLEEP, 13162306a36Sopenharmony_ci PLL_LOCK_TIMEOUT); 13262306a36Sopenharmony_ci if (ret < 0) { 13362306a36Sopenharmony_ci dev_err(&phy->dev, "failed to get PHY lock: %d\n", ret); 13462306a36Sopenharmony_ci clk_disable_unprepare(priv->phy_ref_clk); 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci mutex_unlock(&priv->lock); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci return ret; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic int mixel_lvds_phy_power_off(struct phy *phy) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci struct mixel_lvds_phy_priv *priv = dev_get_drvdata(phy->dev.parent); 14462306a36Sopenharmony_ci struct mixel_lvds_phy *lvds_phy = phy_get_drvdata(phy); 14562306a36Sopenharmony_ci struct mixel_lvds_phy *companion = priv->phys[lvds_phy->id ^ 1]; 14662306a36Sopenharmony_ci struct phy_configure_opts_lvds *cfg = &lvds_phy->cfg; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* The master PHY would power off the slave PHY. */ 14962306a36Sopenharmony_ci if (cfg->is_slave) 15062306a36Sopenharmony_ci return 0; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci mutex_lock(&priv->lock); 15362306a36Sopenharmony_ci if (companion->cfg.is_slave) 15462306a36Sopenharmony_ci regmap_write(priv->regmap, PHY_CTRL + REG_CLR, 15562306a36Sopenharmony_ci CH_EN(0) | CH_EN(1)); 15662306a36Sopenharmony_ci else 15762306a36Sopenharmony_ci regmap_write(priv->regmap, PHY_CTRL + REG_CLR, 15862306a36Sopenharmony_ci CH_EN(lvds_phy->id)); 15962306a36Sopenharmony_ci mutex_unlock(&priv->lock); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci clk_disable_unprepare(priv->phy_ref_clk); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci return 0; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic int mixel_lvds_phy_configure(struct phy *phy, 16762306a36Sopenharmony_ci union phy_configure_opts *opts) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci struct mixel_lvds_phy_priv *priv = dev_get_drvdata(phy->dev.parent); 17062306a36Sopenharmony_ci struct phy_configure_opts_lvds *cfg = &opts->lvds; 17162306a36Sopenharmony_ci int ret; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci ret = clk_set_rate(priv->phy_ref_clk, cfg->differential_clk_rate); 17462306a36Sopenharmony_ci if (ret) 17562306a36Sopenharmony_ci dev_err(&phy->dev, "failed to set PHY reference clock rate(%lu): %d\n", 17662306a36Sopenharmony_ci cfg->differential_clk_rate, ret); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci return ret; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci/* Assume the master PHY's configuration set is cached first. */ 18262306a36Sopenharmony_cistatic int mixel_lvds_phy_check_slave(struct phy *slave_phy) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct device *dev = &slave_phy->dev; 18562306a36Sopenharmony_ci struct mixel_lvds_phy_priv *priv = dev_get_drvdata(dev->parent); 18662306a36Sopenharmony_ci struct mixel_lvds_phy *slv = phy_get_drvdata(slave_phy); 18762306a36Sopenharmony_ci struct mixel_lvds_phy *mst = priv->phys[slv->id ^ 1]; 18862306a36Sopenharmony_ci struct phy_configure_opts_lvds *mst_cfg = &mst->cfg; 18962306a36Sopenharmony_ci struct phy_configure_opts_lvds *slv_cfg = &slv->cfg; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (mst_cfg->bits_per_lane_and_dclk_cycle != 19262306a36Sopenharmony_ci slv_cfg->bits_per_lane_and_dclk_cycle) { 19362306a36Sopenharmony_ci dev_err(dev, "number bits mismatch(mst: %u vs slv: %u)\n", 19462306a36Sopenharmony_ci mst_cfg->bits_per_lane_and_dclk_cycle, 19562306a36Sopenharmony_ci slv_cfg->bits_per_lane_and_dclk_cycle); 19662306a36Sopenharmony_ci return -EINVAL; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (mst_cfg->differential_clk_rate != 20062306a36Sopenharmony_ci slv_cfg->differential_clk_rate) { 20162306a36Sopenharmony_ci dev_err(dev, "dclk rate mismatch(mst: %lu vs slv: %lu)\n", 20262306a36Sopenharmony_ci mst_cfg->differential_clk_rate, 20362306a36Sopenharmony_ci slv_cfg->differential_clk_rate); 20462306a36Sopenharmony_ci return -EINVAL; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (mst_cfg->lanes != slv_cfg->lanes) { 20862306a36Sopenharmony_ci dev_err(dev, "lanes mismatch(mst: %u vs slv: %u)\n", 20962306a36Sopenharmony_ci mst_cfg->lanes, slv_cfg->lanes); 21062306a36Sopenharmony_ci return -EINVAL; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (mst_cfg->is_slave == slv_cfg->is_slave) { 21462306a36Sopenharmony_ci dev_err(dev, "master PHY is not found\n"); 21562306a36Sopenharmony_ci return -EINVAL; 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci return 0; 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic int mixel_lvds_phy_validate(struct phy *phy, enum phy_mode mode, 22262306a36Sopenharmony_ci int submode, union phy_configure_opts *opts) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci struct mixel_lvds_phy_priv *priv = dev_get_drvdata(phy->dev.parent); 22562306a36Sopenharmony_ci struct mixel_lvds_phy *lvds_phy = phy_get_drvdata(phy); 22662306a36Sopenharmony_ci struct phy_configure_opts_lvds *cfg = &opts->lvds; 22762306a36Sopenharmony_ci int ret = 0; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (mode != PHY_MODE_LVDS) { 23062306a36Sopenharmony_ci dev_err(&phy->dev, "invalid PHY mode(%d)\n", mode); 23162306a36Sopenharmony_ci return -EINVAL; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if (cfg->bits_per_lane_and_dclk_cycle != 7 && 23562306a36Sopenharmony_ci cfg->bits_per_lane_and_dclk_cycle != 10) { 23662306a36Sopenharmony_ci dev_err(&phy->dev, "invalid bits per data lane(%u)\n", 23762306a36Sopenharmony_ci cfg->bits_per_lane_and_dclk_cycle); 23862306a36Sopenharmony_ci return -EINVAL; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (cfg->lanes != 4 && cfg->lanes != 3) { 24262306a36Sopenharmony_ci dev_err(&phy->dev, "invalid data lanes(%u)\n", cfg->lanes); 24362306a36Sopenharmony_ci return -EINVAL; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (cfg->differential_clk_rate < MIN_CLKIN_FREQ || 24762306a36Sopenharmony_ci cfg->differential_clk_rate > MAX_CLKIN_FREQ) { 24862306a36Sopenharmony_ci dev_err(&phy->dev, "invalid differential clock rate(%lu)\n", 24962306a36Sopenharmony_ci cfg->differential_clk_rate); 25062306a36Sopenharmony_ci return -EINVAL; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci mutex_lock(&priv->lock); 25462306a36Sopenharmony_ci /* cache configuration set of our own for check */ 25562306a36Sopenharmony_ci memcpy(&lvds_phy->cfg, cfg, sizeof(*cfg)); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (cfg->is_slave) { 25862306a36Sopenharmony_ci ret = mixel_lvds_phy_check_slave(phy); 25962306a36Sopenharmony_ci if (ret) 26062306a36Sopenharmony_ci dev_err(&phy->dev, "failed to check slave PHY: %d\n", ret); 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci mutex_unlock(&priv->lock); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci return ret; 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic const struct phy_ops mixel_lvds_phy_ops = { 26862306a36Sopenharmony_ci .init = mixel_lvds_phy_init, 26962306a36Sopenharmony_ci .power_on = mixel_lvds_phy_power_on, 27062306a36Sopenharmony_ci .power_off = mixel_lvds_phy_power_off, 27162306a36Sopenharmony_ci .configure = mixel_lvds_phy_configure, 27262306a36Sopenharmony_ci .validate = mixel_lvds_phy_validate, 27362306a36Sopenharmony_ci .owner = THIS_MODULE, 27462306a36Sopenharmony_ci}; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cistatic int mixel_lvds_phy_reset(struct device *dev) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci struct mixel_lvds_phy_priv *priv = dev_get_drvdata(dev); 27962306a36Sopenharmony_ci int ret; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(dev); 28262306a36Sopenharmony_ci if (ret < 0) { 28362306a36Sopenharmony_ci dev_err(dev, "failed to get PM runtime: %d\n", ret); 28462306a36Sopenharmony_ci return ret; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci regmap_write(priv->regmap, PHY_CTRL, CTRL_RESET_VAL); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci ret = pm_runtime_put(dev); 29062306a36Sopenharmony_ci if (ret < 0) 29162306a36Sopenharmony_ci dev_err(dev, "failed to put PM runtime: %d\n", ret); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci return ret; 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic struct phy *mixel_lvds_phy_xlate(struct device *dev, 29762306a36Sopenharmony_ci struct of_phandle_args *args) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci struct mixel_lvds_phy_priv *priv = dev_get_drvdata(dev); 30062306a36Sopenharmony_ci unsigned int phy_id; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (args->args_count != 1) { 30362306a36Sopenharmony_ci dev_err(dev, 30462306a36Sopenharmony_ci "invalid argument number(%d) for 'phys' property\n", 30562306a36Sopenharmony_ci args->args_count); 30662306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci phy_id = args->args[0]; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (phy_id >= PHY_NUM) { 31262306a36Sopenharmony_ci dev_err(dev, "invalid PHY index(%d)\n", phy_id); 31362306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci return priv->phys[phy_id]->phy; 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cistatic int mixel_lvds_phy_probe(struct platform_device *pdev) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci struct device *dev = &pdev->dev; 32262306a36Sopenharmony_ci struct phy_provider *phy_provider; 32362306a36Sopenharmony_ci struct mixel_lvds_phy_priv *priv; 32462306a36Sopenharmony_ci struct mixel_lvds_phy *lvds_phy; 32562306a36Sopenharmony_ci struct phy *phy; 32662306a36Sopenharmony_ci int i; 32762306a36Sopenharmony_ci int ret; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci if (!dev->of_node) 33062306a36Sopenharmony_ci return -ENODEV; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 33362306a36Sopenharmony_ci if (!priv) 33462306a36Sopenharmony_ci return -ENOMEM; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci priv->regmap = syscon_node_to_regmap(dev->of_node->parent); 33762306a36Sopenharmony_ci if (IS_ERR(priv->regmap)) 33862306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(priv->regmap), 33962306a36Sopenharmony_ci "failed to get regmap\n"); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci priv->phy_ref_clk = devm_clk_get(dev, NULL); 34262306a36Sopenharmony_ci if (IS_ERR(priv->phy_ref_clk)) 34362306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(priv->phy_ref_clk), 34462306a36Sopenharmony_ci "failed to get PHY reference clock\n"); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci mutex_init(&priv->lock); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci dev_set_drvdata(dev, priv); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci pm_runtime_enable(dev); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci ret = mixel_lvds_phy_reset(dev); 35362306a36Sopenharmony_ci if (ret) { 35462306a36Sopenharmony_ci dev_err(dev, "failed to do POR reset: %d\n", ret); 35562306a36Sopenharmony_ci return ret; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci for (i = 0; i < PHY_NUM; i++) { 35962306a36Sopenharmony_ci lvds_phy = devm_kzalloc(dev, sizeof(*lvds_phy), GFP_KERNEL); 36062306a36Sopenharmony_ci if (!lvds_phy) { 36162306a36Sopenharmony_ci ret = -ENOMEM; 36262306a36Sopenharmony_ci goto err; 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci phy = devm_phy_create(dev, NULL, &mixel_lvds_phy_ops); 36662306a36Sopenharmony_ci if (IS_ERR(phy)) { 36762306a36Sopenharmony_ci ret = PTR_ERR(phy); 36862306a36Sopenharmony_ci dev_err(dev, "failed to create PHY for channel%d: %d\n", 36962306a36Sopenharmony_ci i, ret); 37062306a36Sopenharmony_ci goto err; 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci lvds_phy->phy = phy; 37462306a36Sopenharmony_ci lvds_phy->id = i; 37562306a36Sopenharmony_ci priv->phys[i] = lvds_phy; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci phy_set_drvdata(phy, lvds_phy); 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci phy_provider = devm_of_phy_provider_register(dev, mixel_lvds_phy_xlate); 38162306a36Sopenharmony_ci if (IS_ERR(phy_provider)) { 38262306a36Sopenharmony_ci ret = PTR_ERR(phy_provider); 38362306a36Sopenharmony_ci dev_err(dev, "failed to register PHY provider: %d\n", ret); 38462306a36Sopenharmony_ci goto err; 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci return 0; 38862306a36Sopenharmony_cierr: 38962306a36Sopenharmony_ci pm_runtime_disable(dev); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci return ret; 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic void mixel_lvds_phy_remove(struct platform_device *pdev) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic int __maybe_unused mixel_lvds_phy_runtime_suspend(struct device *dev) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci struct mixel_lvds_phy_priv *priv = dev_get_drvdata(dev); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci /* power down */ 40462306a36Sopenharmony_ci mutex_lock(&priv->lock); 40562306a36Sopenharmony_ci regmap_write(priv->regmap, PHY_CTRL + REG_SET, PD); 40662306a36Sopenharmony_ci mutex_unlock(&priv->lock); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci return 0; 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cistatic int __maybe_unused mixel_lvds_phy_runtime_resume(struct device *dev) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci struct mixel_lvds_phy_priv *priv = dev_get_drvdata(dev); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci /* power up + control initialization */ 41662306a36Sopenharmony_ci mutex_lock(&priv->lock); 41762306a36Sopenharmony_ci regmap_update_bits(priv->regmap, PHY_CTRL, 41862306a36Sopenharmony_ci CTRL_INIT_MASK | PD, CTRL_INIT_VAL); 41962306a36Sopenharmony_ci mutex_unlock(&priv->lock); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci return 0; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic const struct dev_pm_ops mixel_lvds_phy_pm_ops = { 42562306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(mixel_lvds_phy_runtime_suspend, 42662306a36Sopenharmony_ci mixel_lvds_phy_runtime_resume, NULL) 42762306a36Sopenharmony_ci}; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cistatic const struct of_device_id mixel_lvds_phy_of_match[] = { 43062306a36Sopenharmony_ci { .compatible = "fsl,imx8qm-lvds-phy" }, 43162306a36Sopenharmony_ci { /* sentinel */ } 43262306a36Sopenharmony_ci}; 43362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mixel_lvds_phy_of_match); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic struct platform_driver mixel_lvds_phy_driver = { 43662306a36Sopenharmony_ci .probe = mixel_lvds_phy_probe, 43762306a36Sopenharmony_ci .remove_new = mixel_lvds_phy_remove, 43862306a36Sopenharmony_ci .driver = { 43962306a36Sopenharmony_ci .pm = &mixel_lvds_phy_pm_ops, 44062306a36Sopenharmony_ci .name = "mixel-lvds-phy", 44162306a36Sopenharmony_ci .of_match_table = mixel_lvds_phy_of_match, 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci}; 44462306a36Sopenharmony_cimodule_platform_driver(mixel_lvds_phy_driver); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ciMODULE_DESCRIPTION("Mixel LVDS PHY driver"); 44762306a36Sopenharmony_ciMODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>"); 44862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 449