root/drivers/phy/cadence/cdns-dphy.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. cdns_dsi_get_dphy_pll_cfg
  2. cdns_dphy_setup_psm
  3. cdns_dphy_set_clk_lane_cfg
  4. cdns_dphy_set_pll_cfg
  5. cdns_dphy_get_wakeup_time_ns
  6. cdns_dphy_ref_get_wakeup_time_ns
  7. cdns_dphy_ref_set_pll_cfg
  8. cdns_dphy_ref_set_psm_div
  9. cdns_dphy_config_from_opts
  10. cdns_dphy_validate
  11. cdns_dphy_configure
  12. cdns_dphy_power_on
  13. cdns_dphy_power_off
  14. cdns_dphy_probe
  15. cdns_dphy_remove

   1 // SPDX-License-Identifier: GPL-2.0+
   2 /*
   3  * Copyright: 2017-2018 Cadence Design Systems, Inc.
   4  */
   5 
   6 #include <linux/bitops.h>
   7 #include <linux/clk.h>
   8 #include <linux/io.h>
   9 #include <linux/module.h>
  10 #include <linux/of_address.h>
  11 #include <linux/of_device.h>
  12 #include <linux/platform_device.h>
  13 #include <linux/reset.h>
  14 
  15 #include <linux/phy/phy.h>
  16 #include <linux/phy/phy-mipi-dphy.h>
  17 
  18 #define REG_WAKEUP_TIME_NS              800
  19 #define DPHY_PLL_RATE_HZ                108000000
  20 
  21 /* DPHY registers */
  22 #define DPHY_PMA_CMN(reg)               (reg)
  23 #define DPHY_PMA_LCLK(reg)              (0x100 + (reg))
  24 #define DPHY_PMA_LDATA(lane, reg)       (0x200 + ((lane) * 0x100) + (reg))
  25 #define DPHY_PMA_RCLK(reg)              (0x600 + (reg))
  26 #define DPHY_PMA_RDATA(lane, reg)       (0x700 + ((lane) * 0x100) + (reg))
  27 #define DPHY_PCS(reg)                   (0xb00 + (reg))
  28 
  29 #define DPHY_CMN_SSM                    DPHY_PMA_CMN(0x20)
  30 #define DPHY_CMN_SSM_EN                 BIT(0)
  31 #define DPHY_CMN_TX_MODE_EN             BIT(9)
  32 
  33 #define DPHY_CMN_PWM                    DPHY_PMA_CMN(0x40)
  34 #define DPHY_CMN_PWM_DIV(x)             ((x) << 20)
  35 #define DPHY_CMN_PWM_LOW(x)             ((x) << 10)
  36 #define DPHY_CMN_PWM_HIGH(x)            (x)
  37 
  38 #define DPHY_CMN_FBDIV                  DPHY_PMA_CMN(0x4c)
  39 #define DPHY_CMN_FBDIV_VAL(low, high)   (((high) << 11) | ((low) << 22))
  40 #define DPHY_CMN_FBDIV_FROM_REG         (BIT(10) | BIT(21))
  41 
  42 #define DPHY_CMN_OPIPDIV                DPHY_PMA_CMN(0x50)
  43 #define DPHY_CMN_IPDIV_FROM_REG         BIT(0)
  44 #define DPHY_CMN_IPDIV(x)               ((x) << 1)
  45 #define DPHY_CMN_OPDIV_FROM_REG         BIT(6)
  46 #define DPHY_CMN_OPDIV(x)               ((x) << 7)
  47 
  48 #define DPHY_PSM_CFG                    DPHY_PCS(0x4)
  49 #define DPHY_PSM_CFG_FROM_REG           BIT(0)
  50 #define DPHY_PSM_CLK_DIV(x)             ((x) << 1)
  51 
  52 #define DSI_HBP_FRAME_OVERHEAD          12
  53 #define DSI_HSA_FRAME_OVERHEAD          14
  54 #define DSI_HFP_FRAME_OVERHEAD          6
  55 #define DSI_HSS_VSS_VSE_FRAME_OVERHEAD  4
  56 #define DSI_BLANKING_FRAME_OVERHEAD     6
  57 #define DSI_NULL_FRAME_OVERHEAD         6
  58 #define DSI_EOT_PKT_SIZE                4
  59 
  60 struct cdns_dphy_cfg {
  61         u8 pll_ipdiv;
  62         u8 pll_opdiv;
  63         u16 pll_fbdiv;
  64         unsigned int nlanes;
  65 };
  66 
  67 enum cdns_dphy_clk_lane_cfg {
  68         DPHY_CLK_CFG_LEFT_DRIVES_ALL = 0,
  69         DPHY_CLK_CFG_LEFT_DRIVES_RIGHT = 1,
  70         DPHY_CLK_CFG_LEFT_DRIVES_LEFT = 2,
  71         DPHY_CLK_CFG_RIGHT_DRIVES_ALL = 3,
  72 };
  73 
  74 struct cdns_dphy;
  75 struct cdns_dphy_ops {
  76         int (*probe)(struct cdns_dphy *dphy);
  77         void (*remove)(struct cdns_dphy *dphy);
  78         void (*set_psm_div)(struct cdns_dphy *dphy, u8 div);
  79         void (*set_clk_lane_cfg)(struct cdns_dphy *dphy,
  80                                  enum cdns_dphy_clk_lane_cfg cfg);
  81         void (*set_pll_cfg)(struct cdns_dphy *dphy,
  82                             const struct cdns_dphy_cfg *cfg);
  83         unsigned long (*get_wakeup_time_ns)(struct cdns_dphy *dphy);
  84 };
  85 
  86 struct cdns_dphy {
  87         struct cdns_dphy_cfg cfg;
  88         void __iomem *regs;
  89         struct clk *psm_clk;
  90         struct clk *pll_ref_clk;
  91         const struct cdns_dphy_ops *ops;
  92         struct phy *phy;
  93 };
  94 
  95 static int cdns_dsi_get_dphy_pll_cfg(struct cdns_dphy *dphy,
  96                                      struct cdns_dphy_cfg *cfg,
  97                                      struct phy_configure_opts_mipi_dphy *opts,
  98                                      unsigned int *dsi_hfp_ext)
  99 {
 100         unsigned long pll_ref_hz = clk_get_rate(dphy->pll_ref_clk);
 101         u64 dlane_bps;
 102 
 103         memset(cfg, 0, sizeof(*cfg));
 104 
 105         if (pll_ref_hz < 9600000 || pll_ref_hz >= 150000000)
 106                 return -EINVAL;
 107         else if (pll_ref_hz < 19200000)
 108                 cfg->pll_ipdiv = 1;
 109         else if (pll_ref_hz < 38400000)
 110                 cfg->pll_ipdiv = 2;
 111         else if (pll_ref_hz < 76800000)
 112                 cfg->pll_ipdiv = 4;
 113         else
 114                 cfg->pll_ipdiv = 8;
 115 
 116         dlane_bps = opts->hs_clk_rate;
 117 
 118         if (dlane_bps > 2500000000UL || dlane_bps < 160000000UL)
 119                 return -EINVAL;
 120         else if (dlane_bps >= 1250000000)
 121                 cfg->pll_opdiv = 1;
 122         else if (dlane_bps >= 630000000)
 123                 cfg->pll_opdiv = 2;
 124         else if (dlane_bps >= 320000000)
 125                 cfg->pll_opdiv = 4;
 126         else if (dlane_bps >= 160000000)
 127                 cfg->pll_opdiv = 8;
 128 
 129         cfg->pll_fbdiv = DIV_ROUND_UP_ULL(dlane_bps * 2 * cfg->pll_opdiv *
 130                                           cfg->pll_ipdiv,
 131                                           pll_ref_hz);
 132 
 133         return 0;
 134 }
 135 
 136 static int cdns_dphy_setup_psm(struct cdns_dphy *dphy)
 137 {
 138         unsigned long psm_clk_hz = clk_get_rate(dphy->psm_clk);
 139         unsigned long psm_div;
 140 
 141         if (!psm_clk_hz || psm_clk_hz > 100000000)
 142                 return -EINVAL;
 143 
 144         psm_div = DIV_ROUND_CLOSEST(psm_clk_hz, 1000000);
 145         if (dphy->ops->set_psm_div)
 146                 dphy->ops->set_psm_div(dphy, psm_div);
 147 
 148         return 0;
 149 }
 150 
 151 static void cdns_dphy_set_clk_lane_cfg(struct cdns_dphy *dphy,
 152                                        enum cdns_dphy_clk_lane_cfg cfg)
 153 {
 154         if (dphy->ops->set_clk_lane_cfg)
 155                 dphy->ops->set_clk_lane_cfg(dphy, cfg);
 156 }
 157 
 158 static void cdns_dphy_set_pll_cfg(struct cdns_dphy *dphy,
 159                                   const struct cdns_dphy_cfg *cfg)
 160 {
 161         if (dphy->ops->set_pll_cfg)
 162                 dphy->ops->set_pll_cfg(dphy, cfg);
 163 }
 164 
 165 static unsigned long cdns_dphy_get_wakeup_time_ns(struct cdns_dphy *dphy)
 166 {
 167         return dphy->ops->get_wakeup_time_ns(dphy);
 168 }
 169 
 170 static unsigned long cdns_dphy_ref_get_wakeup_time_ns(struct cdns_dphy *dphy)
 171 {
 172         /* Default wakeup time is 800 ns (in a simulated environment). */
 173         return 800;
 174 }
 175 
 176 static void cdns_dphy_ref_set_pll_cfg(struct cdns_dphy *dphy,
 177                                       const struct cdns_dphy_cfg *cfg)
 178 {
 179         u32 fbdiv_low, fbdiv_high;
 180 
 181         fbdiv_low = (cfg->pll_fbdiv / 4) - 2;
 182         fbdiv_high = cfg->pll_fbdiv - fbdiv_low - 2;
 183 
 184         writel(DPHY_CMN_IPDIV_FROM_REG | DPHY_CMN_OPDIV_FROM_REG |
 185                DPHY_CMN_IPDIV(cfg->pll_ipdiv) |
 186                DPHY_CMN_OPDIV(cfg->pll_opdiv),
 187                dphy->regs + DPHY_CMN_OPIPDIV);
 188         writel(DPHY_CMN_FBDIV_FROM_REG |
 189                DPHY_CMN_FBDIV_VAL(fbdiv_low, fbdiv_high),
 190                dphy->regs + DPHY_CMN_FBDIV);
 191         writel(DPHY_CMN_PWM_HIGH(6) | DPHY_CMN_PWM_LOW(0x101) |
 192                DPHY_CMN_PWM_DIV(0x8),
 193                dphy->regs + DPHY_CMN_PWM);
 194 }
 195 
 196 static void cdns_dphy_ref_set_psm_div(struct cdns_dphy *dphy, u8 div)
 197 {
 198         writel(DPHY_PSM_CFG_FROM_REG | DPHY_PSM_CLK_DIV(div),
 199                dphy->regs + DPHY_PSM_CFG);
 200 }
 201 
 202 /*
 203  * This is the reference implementation of DPHY hooks. Specific integration of
 204  * this IP may have to re-implement some of them depending on how they decided
 205  * to wire things in the SoC.
 206  */
 207 static const struct cdns_dphy_ops ref_dphy_ops = {
 208         .get_wakeup_time_ns = cdns_dphy_ref_get_wakeup_time_ns,
 209         .set_pll_cfg = cdns_dphy_ref_set_pll_cfg,
 210         .set_psm_div = cdns_dphy_ref_set_psm_div,
 211 };
 212 
 213 static int cdns_dphy_config_from_opts(struct phy *phy,
 214                                       struct phy_configure_opts_mipi_dphy *opts,
 215                                       struct cdns_dphy_cfg *cfg)
 216 {
 217         struct cdns_dphy *dphy = phy_get_drvdata(phy);
 218         unsigned int dsi_hfp_ext = 0;
 219         int ret;
 220 
 221         ret = phy_mipi_dphy_config_validate(opts);
 222         if (ret)
 223                 return ret;
 224 
 225         ret = cdns_dsi_get_dphy_pll_cfg(dphy, cfg,
 226                                         opts, &dsi_hfp_ext);
 227         if (ret)
 228                 return ret;
 229 
 230         opts->wakeup = cdns_dphy_get_wakeup_time_ns(dphy) / 1000;
 231 
 232         return 0;
 233 }
 234 
 235 static int cdns_dphy_validate(struct phy *phy, enum phy_mode mode, int submode,
 236                               union phy_configure_opts *opts)
 237 {
 238         struct cdns_dphy_cfg cfg = { 0 };
 239 
 240         if (mode != PHY_MODE_MIPI_DPHY)
 241                 return -EINVAL;
 242 
 243         return cdns_dphy_config_from_opts(phy, &opts->mipi_dphy, &cfg);
 244 }
 245 
 246 static int cdns_dphy_configure(struct phy *phy, union phy_configure_opts *opts)
 247 {
 248         struct cdns_dphy *dphy = phy_get_drvdata(phy);
 249         struct cdns_dphy_cfg cfg = { 0 };
 250         int ret;
 251 
 252         ret = cdns_dphy_config_from_opts(phy, &opts->mipi_dphy, &cfg);
 253         if (ret)
 254                 return ret;
 255 
 256         /*
 257          * Configure the internal PSM clk divider so that the DPHY has a
 258          * 1MHz clk (or something close).
 259          */
 260         ret = cdns_dphy_setup_psm(dphy);
 261         if (ret)
 262                 return ret;
 263 
 264         /*
 265          * Configure attach clk lanes to data lanes: the DPHY has 2 clk lanes
 266          * and 8 data lanes, each clk lane can be attache different set of
 267          * data lanes. The 2 groups are named 'left' and 'right', so here we
 268          * just say that we want the 'left' clk lane to drive the 'left' data
 269          * lanes.
 270          */
 271         cdns_dphy_set_clk_lane_cfg(dphy, DPHY_CLK_CFG_LEFT_DRIVES_LEFT);
 272 
 273         /*
 274          * Configure the DPHY PLL that will be used to generate the TX byte
 275          * clk.
 276          */
 277         cdns_dphy_set_pll_cfg(dphy, &cfg);
 278 
 279         return 0;
 280 }
 281 
 282 static int cdns_dphy_power_on(struct phy *phy)
 283 {
 284         struct cdns_dphy *dphy = phy_get_drvdata(phy);
 285 
 286         clk_prepare_enable(dphy->psm_clk);
 287         clk_prepare_enable(dphy->pll_ref_clk);
 288 
 289         /* Start TX state machine. */
 290         writel(DPHY_CMN_SSM_EN | DPHY_CMN_TX_MODE_EN,
 291                dphy->regs + DPHY_CMN_SSM);
 292 
 293         return 0;
 294 }
 295 
 296 static int cdns_dphy_power_off(struct phy *phy)
 297 {
 298         struct cdns_dphy *dphy = phy_get_drvdata(phy);
 299 
 300         clk_disable_unprepare(dphy->pll_ref_clk);
 301         clk_disable_unprepare(dphy->psm_clk);
 302 
 303         return 0;
 304 }
 305 
 306 static const struct phy_ops cdns_dphy_ops = {
 307         .configure      = cdns_dphy_configure,
 308         .validate       = cdns_dphy_validate,
 309         .power_on       = cdns_dphy_power_on,
 310         .power_off      = cdns_dphy_power_off,
 311 };
 312 
 313 static int cdns_dphy_probe(struct platform_device *pdev)
 314 {
 315         struct phy_provider *phy_provider;
 316         struct cdns_dphy *dphy;
 317         struct resource *res;
 318         int ret;
 319 
 320         dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
 321         if (!dphy)
 322                 return -ENOMEM;
 323         dev_set_drvdata(&pdev->dev, dphy);
 324 
 325         dphy->ops = of_device_get_match_data(&pdev->dev);
 326         if (!dphy->ops)
 327                 return -EINVAL;
 328 
 329         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 330         dphy->regs = devm_ioremap_resource(&pdev->dev, res);
 331         if (IS_ERR(dphy->regs))
 332                 return PTR_ERR(dphy->regs);
 333 
 334         dphy->psm_clk = devm_clk_get(&pdev->dev, "psm");
 335         if (IS_ERR(dphy->psm_clk))
 336                 return PTR_ERR(dphy->psm_clk);
 337 
 338         dphy->pll_ref_clk = devm_clk_get(&pdev->dev, "pll_ref");
 339         if (IS_ERR(dphy->pll_ref_clk))
 340                 return PTR_ERR(dphy->pll_ref_clk);
 341 
 342         if (dphy->ops->probe) {
 343                 ret = dphy->ops->probe(dphy);
 344                 if (ret)
 345                         return ret;
 346         }
 347 
 348         dphy->phy = devm_phy_create(&pdev->dev, NULL, &cdns_dphy_ops);
 349         if (IS_ERR(dphy->phy)) {
 350                 dev_err(&pdev->dev, "failed to create PHY\n");
 351                 if (dphy->ops->remove)
 352                         dphy->ops->remove(dphy);
 353                 return PTR_ERR(dphy->phy);
 354         }
 355 
 356         phy_set_drvdata(dphy->phy, dphy);
 357         phy_provider = devm_of_phy_provider_register(&pdev->dev,
 358                                                      of_phy_simple_xlate);
 359 
 360         return PTR_ERR_OR_ZERO(phy_provider);
 361 }
 362 
 363 static int cdns_dphy_remove(struct platform_device *pdev)
 364 {
 365         struct cdns_dphy *dphy = dev_get_drvdata(&pdev->dev);
 366 
 367         if (dphy->ops->remove)
 368                 dphy->ops->remove(dphy);
 369 
 370         return 0;
 371 }
 372 
 373 static const struct of_device_id cdns_dphy_of_match[] = {
 374         { .compatible = "cdns,dphy", .data = &ref_dphy_ops },
 375         { /* sentinel */ },
 376 };
 377 MODULE_DEVICE_TABLE(of, cdns_dphy_of_match);
 378 
 379 static struct platform_driver cdns_dphy_platform_driver = {
 380         .probe          = cdns_dphy_probe,
 381         .remove         = cdns_dphy_remove,
 382         .driver         = {
 383                 .name           = "cdns-mipi-dphy",
 384                 .of_match_table = cdns_dphy_of_match,
 385         },
 386 };
 387 module_platform_driver(cdns_dphy_platform_driver);
 388 
 389 MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin.com>");
 390 MODULE_DESCRIPTION("Cadence MIPI D-PHY Driver");
 391 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */