linux/drivers/phy/freescale/phy-fsl-imx8-mipi-dphy.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright 2017,2018 NXP
   4 * Copyright 2019 Purism SPC
   5 */
   6
   7#include <linux/clk.h>
   8#include <linux/clk-provider.h>
   9#include <linux/delay.h>
  10#include <linux/io.h>
  11#include <linux/kernel.h>
  12#include <linux/module.h>
  13#include <linux/of.h>
  14#include <linux/of_platform.h>
  15#include <linux/phy/phy.h>
  16#include <linux/platform_device.h>
  17#include <linux/regmap.h>
  18
  19/* DPHY registers */
  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
  28#define DPHY_TST                        0x20
  29#define DPHY_CN                         0x24
  30#define DPHY_CM                         0x28
  31#define DPHY_CO                         0x2c
  32#define DPHY_LOCK                       0x30
  33#define DPHY_LOCK_BYP                   0x34
  34#define DPHY_REG_BYPASS_PLL             0x4C
  35
  36#define MBPS(x) ((x) * 1000000)
  37
  38#define DATA_RATE_MAX_SPEED MBPS(1500)
  39#define DATA_RATE_MIN_SPEED MBPS(80)
  40
  41#define PLL_LOCK_SLEEP 10
  42#define PLL_LOCK_TIMEOUT 1000
  43
  44#define CN_BUF  0xcb7a89c0
  45#define CO_BUF  0x63
  46#define CM(x)   (                                 \
  47                ((x) <  32) ? 0xe0 | ((x) - 16) : \
  48                ((x) <  64) ? 0xc0 | ((x) - 32) : \
  49                ((x) < 128) ? 0x80 | ((x) - 64) : \
  50                ((x) - 128))
  51#define CN(x)   (((x) == 1) ? 0x1f : (((CN_BUF) >> ((x) - 1)) & 0x1f))
  52#define CO(x)   ((CO_BUF) >> (8 - (x)) & 0x03)
  53
  54/* PHY power on is active low */
  55#define PWR_ON  0
  56#define PWR_OFF 1
  57
  58enum mixel_dphy_devtype {
  59        MIXEL_IMX8MQ,
  60};
  61
  62struct mixel_dphy_devdata {
  63        u8 reg_tx_rcal;
  64        u8 reg_auto_pd_en;
  65        u8 reg_rxlprp;
  66        u8 reg_rxcdrp;
  67        u8 reg_rxhs_settle;
  68};
  69
  70static const struct mixel_dphy_devdata mixel_dphy_devdata[] = {
  71        [MIXEL_IMX8MQ] = {
  72                .reg_tx_rcal = 0x38,
  73                .reg_auto_pd_en = 0x3c,
  74                .reg_rxlprp = 0x40,
  75                .reg_rxcdrp = 0x44,
  76                .reg_rxhs_settle = 0x48,
  77        },
  78};
  79
  80struct mixel_dphy_cfg {
  81        /* DPHY PLL parameters */
  82        u32 cm;
  83        u32 cn;
  84        u32 co;
  85        /* DPHY register values */
  86        u8 mc_prg_hs_prepare;
  87        u8 m_prg_hs_prepare;
  88        u8 mc_prg_hs_zero;
  89        u8 m_prg_hs_zero;
  90        u8 mc_prg_hs_trail;
  91        u8 m_prg_hs_trail;
  92        u8 rxhs_settle;
  93};
  94
  95struct 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;
 100};
 101
 102static const struct regmap_config mixel_dphy_regmap_config = {
 103        .reg_bits = 8,
 104        .val_bits = 32,
 105        .reg_stride = 4,
 106        .max_register = DPHY_REG_BYPASS_PLL,
 107        .name = "mipi-dphy",
 108};
 109
 110static int phy_write(struct phy *phy, u32 value, unsigned int reg)
 111{
 112        struct mixel_dphy_priv *priv = phy_get_drvdata(phy);
 113        int ret;
 114
 115        ret = regmap_write(priv->regmap, reg, value);
 116        if (ret < 0)
 117                dev_err(&phy->dev, "Failed to write DPHY reg %d: %d\n", reg,
 118                        ret);
 119        return ret;
 120}
 121
 122/*
 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.
 126 */
 127static void get_best_ratio(u32 *pnum, u32 *pdenom, u32 max_n, u32 max_d)
 128{
 129        u32 a = *pnum;
 130        u32 b = *pdenom;
 131        u32 c;
 132        u32 n[] = {0, 1};
 133        u32 d[] = {1, 0};
 134        u32 whole;
 135        unsigned int i = 1;
 136
 137        while (b) {
 138                i ^= 1;
 139                whole = a / b;
 140                n[i] += (n[i ^ 1] * whole);
 141                d[i] += (d[i ^ 1] * whole);
 142                if ((n[i] > max_n) || (d[i] > max_d)) {
 143                        i ^= 1;
 144                        break;
 145                }
 146                c = a - (b * whole);
 147                a = b;
 148                b = c;
 149        }
 150        *pnum = n[i];
 151        *pdenom = d[i];
 152}
 153
 154static int mixel_dphy_config_from_opts(struct phy *phy,
 155               struct phy_configure_opts_mipi_dphy *dphy_opts,
 156               struct mixel_dphy_cfg *cfg)
 157{
 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;
 162        u32 n;
 163        int i;
 164
 165        if (dphy_opts->hs_clk_rate > DATA_RATE_MAX_SPEED ||
 166            dphy_opts->hs_clk_rate < DATA_RATE_MIN_SPEED)
 167                return -EINVAL;
 168
 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);
 176                return -EINVAL;
 177        }
 178
 179        while ((numerator < 16) && (denominator <= 128)) {
 180                numerator <<= 1;
 181                denominator <<= 1;
 182        }
 183        /*
 184         * CM ranges between 16 and 255
 185         * CN ranges between 1 and 32
 186         * CO is power of 2: 1, 2, 4, 8
 187         */
 188        i = __ffs(denominator);
 189        if (i > 3)
 190                i = 3;
 191        cfg->cn = denominator >> i;
 192        cfg->co = 1 << i;
 193        cfg->cm = numerator;
 194
 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);
 203                return -EINVAL;
 204        }
 205
 206        dev_dbg(&phy->dev, "hs_clk/ref_clk=%ld/%ld ~ %d/%d\n",
 207                dphy_opts->hs_clk_rate, ref_clk, numerator, denominator);
 208
 209        /* LP clock period */
 210        tmp = 1000000000000LL;
 211        do_div(tmp, dphy_opts->lp_clk_rate); /* ps */
 212        if (tmp > ULONG_MAX)
 213                return -EINVAL;
 214
 215        lp_t = tmp;
 216        dev_dbg(&phy->dev, "LP clock %lu, period: %u ps\n",
 217                dphy_opts->lp_clk_rate, lp_t);
 218
 219        /* hs_prepare: in lp clock periods */
 220        if (2 * dphy_opts->hs_prepare > 5 * lp_t) {
 221                dev_err(&phy->dev,
 222                        "hs_prepare (%u) > 2.5 * lp clock period (%u)\n",
 223                        dphy_opts->hs_prepare, lp_t);
 224                return -EINVAL;
 225        }
 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) {
 228                n = 0;
 229        } else {
 230                tmp = 2 * (dphy_opts->hs_prepare - lp_t);
 231                do_div(tmp, lp_t);
 232                n = tmp;
 233        }
 234        cfg->m_prg_hs_prepare = n;
 235
 236        /* clk_prepare: in lp clock periods */
 237        if (2 * dphy_opts->clk_prepare > 3 * lp_t) {
 238                dev_err(&phy->dev,
 239                        "clk_prepare (%u) > 1.5 * lp clock period (%u)\n",
 240                        dphy_opts->clk_prepare, lp_t);
 241                return -EINVAL;
 242        }
 243        /* 00: lp_t, 01: 1.5 * lp_t */
 244        cfg->mc_prg_hs_prepare = dphy_opts->clk_prepare > lp_t ? 1 : 0;
 245
 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;
 249
 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;
 253
 254        /* clk_trail, hs_trail: formula from NXP BSP */
 255        n = (103 * (dphy_opts->hs_clk_rate / 1000000) + 10000) / 10000;
 256        if (n > 15)
 257                n = 15;
 258        if (n < 1)
 259                n = 1;
 260        cfg->m_prg_hs_trail = n;
 261        cfg->mc_prg_hs_trail = n;
 262
 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;
 276        else
 277                cfg->rxhs_settle = 0x07;
 278
 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,
 283                cfg->rxhs_settle);
 284
 285        return 0;
 286}
 287
 288static void mixel_phy_set_hs_timings(struct phy *phy)
 289{
 290        struct mixel_dphy_priv *priv = phy_get_drvdata(phy);
 291
 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);
 299}
 300
 301static int mixel_dphy_set_pll_params(struct phy *phy)
 302{
 303        struct mixel_dphy_priv *priv = dev_get_drvdata(phy->dev.parent);
 304
 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);
 310                return -EINVAL;
 311        }
 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);
 317        return 0;
 318}
 319
 320static int mixel_dphy_configure(struct phy *phy, union phy_configure_opts *opts)
 321{
 322        struct mixel_dphy_priv *priv = phy_get_drvdata(phy);
 323        struct mixel_dphy_cfg cfg = { 0 };
 324        int ret;
 325
 326        ret = mixel_dphy_config_from_opts(phy, &opts->mipi_dphy, &cfg);
 327        if (ret)
 328                return ret;
 329
 330        /* Update the configuration */
 331        memcpy(&priv->cfg, &cfg, sizeof(struct mixel_dphy_cfg));
 332
 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);
 339
 340        mixel_phy_set_hs_timings(phy);
 341        ret = mixel_dphy_set_pll_params(phy);
 342        if (ret < 0)
 343                return ret;
 344
 345        return 0;
 346}
 347
 348static int mixel_dphy_validate(struct phy *phy, enum phy_mode mode, int submode,
 349                               union phy_configure_opts *opts)
 350{
 351        struct mixel_dphy_cfg cfg = { 0 };
 352
 353        if (mode != PHY_MODE_MIPI_DPHY)
 354                return -EINVAL;
 355
 356        return mixel_dphy_config_from_opts(phy, &opts->mipi_dphy, &cfg);
 357}
 358
 359static int mixel_dphy_init(struct phy *phy)
 360{
 361        phy_write(phy, PWR_OFF, DPHY_PD_PLL);
 362        phy_write(phy, PWR_OFF, DPHY_PD_DPHY);
 363
 364        return 0;
 365}
 366
 367static int mixel_dphy_exit(struct phy *phy)
 368{
 369        phy_write(phy, 0, DPHY_CM);
 370        phy_write(phy, 0, DPHY_CN);
 371        phy_write(phy, 0, DPHY_CO);
 372
 373        return 0;
 374}
 375
 376static int mixel_dphy_power_on(struct phy *phy)
 377{
 378        struct mixel_dphy_priv *priv = phy_get_drvdata(phy);
 379        u32 locked;
 380        int ret;
 381
 382        ret = clk_prepare_enable(priv->phy_ref_clk);
 383        if (ret < 0)
 384                return ret;
 385
 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,
 389                                       PLL_LOCK_TIMEOUT);
 390        if (ret < 0) {
 391                dev_err(&phy->dev, "Could not get DPHY lock (%d)!\n", ret);
 392                goto clock_disable;
 393        }
 394        phy_write(phy, PWR_ON, DPHY_PD_DPHY);
 395
 396        return 0;
 397clock_disable:
 398        clk_disable_unprepare(priv->phy_ref_clk);
 399        return ret;
 400}
 401
 402static int mixel_dphy_power_off(struct phy *phy)
 403{
 404        struct mixel_dphy_priv *priv = phy_get_drvdata(phy);
 405
 406        phy_write(phy, PWR_OFF, DPHY_PD_PLL);
 407        phy_write(phy, PWR_OFF, DPHY_PD_DPHY);
 408
 409        clk_disable_unprepare(priv->phy_ref_clk);
 410
 411        return 0;
 412}
 413
 414static 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,
 422};
 423
 424static const struct of_device_id mixel_dphy_of_match[] = {
 425        { .compatible = "fsl,imx8mq-mipi-dphy",
 426          .data = &mixel_dphy_devdata[MIXEL_IMX8MQ] },
 427        { /* sentinel */ },
 428};
 429MODULE_DEVICE_TABLE(of, mixel_dphy_of_match);
 430
 431static int mixel_dphy_probe(struct platform_device *pdev)
 432{
 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 phy *phy;
 438        void __iomem *base;
 439
 440        if (!np)
 441                return -ENODEV;
 442
 443        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 444        if (!priv)
 445                return -ENOMEM;
 446
 447        priv->devdata = of_device_get_match_data(&pdev->dev);
 448        if (!priv->devdata)
 449                return -EINVAL;
 450
 451        base = devm_platform_ioremap_resource(pdev, 0);
 452        if (IS_ERR(base))
 453                return PTR_ERR(base);
 454
 455        priv->regmap = devm_regmap_init_mmio(&pdev->dev, base,
 456                                             &mixel_dphy_regmap_config);
 457        if (IS_ERR(priv->regmap)) {
 458                dev_err(dev, "Couldn't create the DPHY regmap\n");
 459                return PTR_ERR(priv->regmap);
 460        }
 461
 462        priv->phy_ref_clk = devm_clk_get(&pdev->dev, "phy_ref");
 463        if (IS_ERR(priv->phy_ref_clk)) {
 464                dev_err(dev, "No phy_ref clock found\n");
 465                return PTR_ERR(priv->phy_ref_clk);
 466        }
 467        dev_dbg(dev, "phy_ref clock rate: %lu\n",
 468                clk_get_rate(priv->phy_ref_clk));
 469
 470        dev_set_drvdata(dev, priv);
 471
 472        phy = devm_phy_create(dev, np, &mixel_dphy_phy_ops);
 473        if (IS_ERR(phy)) {
 474                dev_err(dev, "Failed to create phy %ld\n", PTR_ERR(phy));
 475                return PTR_ERR(phy);
 476        }
 477        phy_set_drvdata(phy, priv);
 478
 479        phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
 480
 481        return PTR_ERR_OR_ZERO(phy_provider);
 482}
 483
 484static struct platform_driver mixel_dphy_driver = {
 485        .probe  = mixel_dphy_probe,
 486        .driver = {
 487                .name = "mixel-mipi-dphy",
 488                .of_match_table = mixel_dphy_of_match,
 489        }
 490};
 491module_platform_driver(mixel_dphy_driver);
 492
 493MODULE_AUTHOR("NXP Semiconductor");
 494MODULE_DESCRIPTION("Mixel MIPI-DSI PHY driver");
 495MODULE_LICENSE("GPL");
 496
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.