linux/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (c) 2016 Allwinnertech Co., Ltd.
   4 * Copyright (C) 2017-2018 Bootlin
   5 *
   6 * Maxime Ripard <maxime.ripard@free-electrons.com>
   7 */
   8
   9#include <linux/bitops.h>
  10#include <linux/clk.h>
  11#include <linux/module.h>
  12#include <linux/of_address.h>
  13#include <linux/platform_device.h>
  14#include <linux/regmap.h>
  15#include <linux/reset.h>
  16
  17#include <linux/phy/phy.h>
  18#include <linux/phy/phy-mipi-dphy.h>
  19
  20#define SUN6I_DPHY_GCTL_REG             0x00
  21#define SUN6I_DPHY_GCTL_LANE_NUM(n)             ((((n) - 1) & 3) << 4)
  22#define SUN6I_DPHY_GCTL_EN                      BIT(0)
  23
  24#define SUN6I_DPHY_TX_CTL_REG           0x04
  25#define SUN6I_DPHY_TX_CTL_HS_TX_CLK_CONT        BIT(28)
  26
  27#define SUN6I_DPHY_TX_TIME0_REG         0x10
  28#define SUN6I_DPHY_TX_TIME0_HS_TRAIL(n)         (((n) & 0xff) << 24)
  29#define SUN6I_DPHY_TX_TIME0_HS_PREPARE(n)       (((n) & 0xff) << 16)
  30#define SUN6I_DPHY_TX_TIME0_LP_CLK_DIV(n)       ((n) & 0xff)
  31
  32#define SUN6I_DPHY_TX_TIME1_REG         0x14
  33#define SUN6I_DPHY_TX_TIME1_CLK_POST(n)         (((n) & 0xff) << 24)
  34#define SUN6I_DPHY_TX_TIME1_CLK_PRE(n)          (((n) & 0xff) << 16)
  35#define SUN6I_DPHY_TX_TIME1_CLK_ZERO(n)         (((n) & 0xff) << 8)
  36#define SUN6I_DPHY_TX_TIME1_CLK_PREPARE(n)      ((n) & 0xff)
  37
  38#define SUN6I_DPHY_TX_TIME2_REG         0x18
  39#define SUN6I_DPHY_TX_TIME2_CLK_TRAIL(n)        ((n) & 0xff)
  40
  41#define SUN6I_DPHY_TX_TIME3_REG         0x1c
  42
  43#define SUN6I_DPHY_TX_TIME4_REG         0x20
  44#define SUN6I_DPHY_TX_TIME4_HS_TX_ANA1(n)       (((n) & 0xff) << 8)
  45#define SUN6I_DPHY_TX_TIME4_HS_TX_ANA0(n)       ((n) & 0xff)
  46
  47#define SUN6I_DPHY_ANA0_REG             0x4c
  48#define SUN6I_DPHY_ANA0_REG_PWS                 BIT(31)
  49#define SUN6I_DPHY_ANA0_REG_DMPC                BIT(28)
  50#define SUN6I_DPHY_ANA0_REG_DMPD(n)             (((n) & 0xf) << 24)
  51#define SUN6I_DPHY_ANA0_REG_SLV(n)              (((n) & 7) << 12)
  52#define SUN6I_DPHY_ANA0_REG_DEN(n)              (((n) & 0xf) << 8)
  53
  54#define SUN6I_DPHY_ANA1_REG             0x50
  55#define SUN6I_DPHY_ANA1_REG_VTTMODE             BIT(31)
  56#define SUN6I_DPHY_ANA1_REG_CSMPS(n)            (((n) & 3) << 28)
  57#define SUN6I_DPHY_ANA1_REG_SVTT(n)             (((n) & 0xf) << 24)
  58
  59#define SUN6I_DPHY_ANA2_REG             0x54
  60#define SUN6I_DPHY_ANA2_EN_P2S_CPU(n)           (((n) & 0xf) << 24)
  61#define SUN6I_DPHY_ANA2_EN_P2S_CPU_MASK         GENMASK(27, 24)
  62#define SUN6I_DPHY_ANA2_EN_CK_CPU               BIT(4)
  63#define SUN6I_DPHY_ANA2_REG_ENIB                BIT(1)
  64
  65#define SUN6I_DPHY_ANA3_REG             0x58
  66#define SUN6I_DPHY_ANA3_EN_VTTD(n)              (((n) & 0xf) << 28)
  67#define SUN6I_DPHY_ANA3_EN_VTTD_MASK            GENMASK(31, 28)
  68#define SUN6I_DPHY_ANA3_EN_VTTC                 BIT(27)
  69#define SUN6I_DPHY_ANA3_EN_DIV                  BIT(26)
  70#define SUN6I_DPHY_ANA3_EN_LDOC                 BIT(25)
  71#define SUN6I_DPHY_ANA3_EN_LDOD                 BIT(24)
  72#define SUN6I_DPHY_ANA3_EN_LDOR                 BIT(18)
  73
  74#define SUN6I_DPHY_ANA4_REG             0x5c
  75#define SUN6I_DPHY_ANA4_REG_DMPLVC              BIT(24)
  76#define SUN6I_DPHY_ANA4_REG_DMPLVD(n)           (((n) & 0xf) << 20)
  77#define SUN6I_DPHY_ANA4_REG_CKDV(n)             (((n) & 0x1f) << 12)
  78#define SUN6I_DPHY_ANA4_REG_TMSC(n)             (((n) & 3) << 10)
  79#define SUN6I_DPHY_ANA4_REG_TMSD(n)             (((n) & 3) << 8)
  80#define SUN6I_DPHY_ANA4_REG_TXDNSC(n)           (((n) & 3) << 6)
  81#define SUN6I_DPHY_ANA4_REG_TXDNSD(n)           (((n) & 3) << 4)
  82#define SUN6I_DPHY_ANA4_REG_TXPUSC(n)           (((n) & 3) << 2)
  83#define SUN6I_DPHY_ANA4_REG_TXPUSD(n)           ((n) & 3)
  84
  85#define SUN6I_DPHY_DBG5_REG             0xf4
  86
  87struct sun6i_dphy {
  88        struct clk                              *bus_clk;
  89        struct clk                              *mod_clk;
  90        struct regmap                           *regs;
  91        struct reset_control                    *reset;
  92
  93        struct phy                              *phy;
  94        struct phy_configure_opts_mipi_dphy     config;
  95};
  96
  97static int sun6i_dphy_init(struct phy *phy)
  98{
  99        struct sun6i_dphy *dphy = phy_get_drvdata(phy);
 100
 101        reset_control_deassert(dphy->reset);
 102        clk_prepare_enable(dphy->mod_clk);
 103        clk_set_rate_exclusive(dphy->mod_clk, 150000000);
 104
 105        return 0;
 106}
 107
 108static int sun6i_dphy_configure(struct phy *phy, union phy_configure_opts *opts)
 109{
 110        struct sun6i_dphy *dphy = phy_get_drvdata(phy);
 111        int ret;
 112
 113        ret = phy_mipi_dphy_config_validate(&opts->mipi_dphy);
 114        if (ret)
 115                return ret;
 116
 117        memcpy(&dphy->config, opts, sizeof(dphy->config));
 118
 119        return 0;
 120}
 121
 122static int sun6i_dphy_power_on(struct phy *phy)
 123{
 124        struct sun6i_dphy *dphy = phy_get_drvdata(phy);
 125        u8 lanes_mask = GENMASK(dphy->config.lanes - 1, 0);
 126
 127        regmap_write(dphy->regs, SUN6I_DPHY_TX_CTL_REG,
 128                     SUN6I_DPHY_TX_CTL_HS_TX_CLK_CONT);
 129
 130        regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME0_REG,
 131                     SUN6I_DPHY_TX_TIME0_LP_CLK_DIV(14) |
 132                     SUN6I_DPHY_TX_TIME0_HS_PREPARE(6) |
 133                     SUN6I_DPHY_TX_TIME0_HS_TRAIL(10));
 134
 135        regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME1_REG,
 136                     SUN6I_DPHY_TX_TIME1_CLK_PREPARE(7) |
 137                     SUN6I_DPHY_TX_TIME1_CLK_ZERO(50) |
 138                     SUN6I_DPHY_TX_TIME1_CLK_PRE(3) |
 139                     SUN6I_DPHY_TX_TIME1_CLK_POST(10));
 140
 141        regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME2_REG,
 142                     SUN6I_DPHY_TX_TIME2_CLK_TRAIL(30));
 143
 144        regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME3_REG, 0);
 145
 146        regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME4_REG,
 147                     SUN6I_DPHY_TX_TIME4_HS_TX_ANA0(3) |
 148                     SUN6I_DPHY_TX_TIME4_HS_TX_ANA1(3));
 149
 150        regmap_write(dphy->regs, SUN6I_DPHY_GCTL_REG,
 151                     SUN6I_DPHY_GCTL_LANE_NUM(dphy->config.lanes) |
 152                     SUN6I_DPHY_GCTL_EN);
 153
 154        regmap_write(dphy->regs, SUN6I_DPHY_ANA0_REG,
 155                     SUN6I_DPHY_ANA0_REG_PWS |
 156                     SUN6I_DPHY_ANA0_REG_DMPC |
 157                     SUN6I_DPHY_ANA0_REG_SLV(7) |
 158                     SUN6I_DPHY_ANA0_REG_DMPD(lanes_mask) |
 159                     SUN6I_DPHY_ANA0_REG_DEN(lanes_mask));
 160
 161        regmap_write(dphy->regs, SUN6I_DPHY_ANA1_REG,
 162                     SUN6I_DPHY_ANA1_REG_CSMPS(1) |
 163                     SUN6I_DPHY_ANA1_REG_SVTT(7));
 164
 165        regmap_write(dphy->regs, SUN6I_DPHY_ANA4_REG,
 166                     SUN6I_DPHY_ANA4_REG_CKDV(1) |
 167                     SUN6I_DPHY_ANA4_REG_TMSC(1) |
 168                     SUN6I_DPHY_ANA4_REG_TMSD(1) |
 169                     SUN6I_DPHY_ANA4_REG_TXDNSC(1) |
 170                     SUN6I_DPHY_ANA4_REG_TXDNSD(1) |
 171                     SUN6I_DPHY_ANA4_REG_TXPUSC(1) |
 172                     SUN6I_DPHY_ANA4_REG_TXPUSD(1) |
 173                     SUN6I_DPHY_ANA4_REG_DMPLVC |
 174                     SUN6I_DPHY_ANA4_REG_DMPLVD(lanes_mask));
 175
 176        regmap_write(dphy->regs, SUN6I_DPHY_ANA2_REG,
 177                     SUN6I_DPHY_ANA2_REG_ENIB);
 178        udelay(5);
 179
 180        regmap_write(dphy->regs, SUN6I_DPHY_ANA3_REG,
 181                     SUN6I_DPHY_ANA3_EN_LDOR |
 182                     SUN6I_DPHY_ANA3_EN_LDOC |
 183                     SUN6I_DPHY_ANA3_EN_LDOD);
 184        udelay(1);
 185
 186        regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA3_REG,
 187                           SUN6I_DPHY_ANA3_EN_VTTC |
 188                           SUN6I_DPHY_ANA3_EN_VTTD_MASK,
 189                           SUN6I_DPHY_ANA3_EN_VTTC |
 190                           SUN6I_DPHY_ANA3_EN_VTTD(lanes_mask));
 191        udelay(1);
 192
 193        regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA3_REG,
 194                           SUN6I_DPHY_ANA3_EN_DIV,
 195                           SUN6I_DPHY_ANA3_EN_DIV);
 196        udelay(1);
 197
 198        regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA2_REG,
 199                           SUN6I_DPHY_ANA2_EN_CK_CPU,
 200                           SUN6I_DPHY_ANA2_EN_CK_CPU);
 201        udelay(1);
 202
 203        regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA1_REG,
 204                           SUN6I_DPHY_ANA1_REG_VTTMODE,
 205                           SUN6I_DPHY_ANA1_REG_VTTMODE);
 206
 207        regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA2_REG,
 208                           SUN6I_DPHY_ANA2_EN_P2S_CPU_MASK,
 209                           SUN6I_DPHY_ANA2_EN_P2S_CPU(lanes_mask));
 210
 211        return 0;
 212}
 213
 214static int sun6i_dphy_power_off(struct phy *phy)
 215{
 216        struct sun6i_dphy *dphy = phy_get_drvdata(phy);
 217
 218        regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA1_REG,
 219                           SUN6I_DPHY_ANA1_REG_VTTMODE, 0);
 220
 221        return 0;
 222}
 223
 224static int sun6i_dphy_exit(struct phy *phy)
 225{
 226        struct sun6i_dphy *dphy = phy_get_drvdata(phy);
 227
 228        clk_rate_exclusive_put(dphy->mod_clk);
 229        clk_disable_unprepare(dphy->mod_clk);
 230        reset_control_assert(dphy->reset);
 231
 232        return 0;
 233}
 234
 235
 236static const struct phy_ops sun6i_dphy_ops = {
 237        .configure      = sun6i_dphy_configure,
 238        .power_on       = sun6i_dphy_power_on,
 239        .power_off      = sun6i_dphy_power_off,
 240        .init           = sun6i_dphy_init,
 241        .exit           = sun6i_dphy_exit,
 242};
 243
 244static const struct regmap_config sun6i_dphy_regmap_config = {
 245        .reg_bits       = 32,
 246        .val_bits       = 32,
 247        .reg_stride     = 4,
 248        .max_register   = SUN6I_DPHY_DBG5_REG,
 249        .name           = "mipi-dphy",
 250};
 251
 252static int sun6i_dphy_probe(struct platform_device *pdev)
 253{
 254        struct phy_provider *phy_provider;
 255        struct sun6i_dphy *dphy;
 256        struct resource *res;
 257        void __iomem *regs;
 258
 259        dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
 260        if (!dphy)
 261                return -ENOMEM;
 262
 263        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 264        regs = devm_ioremap_resource(&pdev->dev, res);
 265        if (IS_ERR(regs)) {
 266                dev_err(&pdev->dev, "Couldn't map the DPHY encoder registers\n");
 267                return PTR_ERR(regs);
 268        }
 269
 270        dphy->regs = devm_regmap_init_mmio_clk(&pdev->dev, "bus",
 271                                               regs, &sun6i_dphy_regmap_config);
 272        if (IS_ERR(dphy->regs)) {
 273                dev_err(&pdev->dev, "Couldn't create the DPHY encoder regmap\n");
 274                return PTR_ERR(dphy->regs);
 275        }
 276
 277        dphy->reset = devm_reset_control_get_shared(&pdev->dev, NULL);
 278        if (IS_ERR(dphy->reset)) {
 279                dev_err(&pdev->dev, "Couldn't get our reset line\n");
 280                return PTR_ERR(dphy->reset);
 281        }
 282
 283        dphy->mod_clk = devm_clk_get(&pdev->dev, "mod");
 284        if (IS_ERR(dphy->mod_clk)) {
 285                dev_err(&pdev->dev, "Couldn't get the DPHY mod clock\n");
 286                return PTR_ERR(dphy->mod_clk);
 287        }
 288
 289        dphy->phy = devm_phy_create(&pdev->dev, NULL, &sun6i_dphy_ops);
 290        if (IS_ERR(dphy->phy)) {
 291                dev_err(&pdev->dev, "failed to create PHY\n");
 292                return PTR_ERR(dphy->phy);
 293        }
 294
 295        phy_set_drvdata(dphy->phy, dphy);
 296        phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate);
 297
 298        return PTR_ERR_OR_ZERO(phy_provider);
 299}
 300
 301static const struct of_device_id sun6i_dphy_of_table[] = {
 302        { .compatible = "allwinner,sun6i-a31-mipi-dphy" },
 303        { }
 304};
 305MODULE_DEVICE_TABLE(of, sun6i_dphy_of_table);
 306
 307static struct platform_driver sun6i_dphy_platform_driver = {
 308        .probe          = sun6i_dphy_probe,
 309        .driver         = {
 310                .name           = "sun6i-mipi-dphy",
 311                .of_match_table = sun6i_dphy_of_table,
 312        },
 313};
 314module_platform_driver(sun6i_dphy_platform_driver);
 315
 316MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin>");
 317MODULE_DESCRIPTION("Allwinner A31 MIPI D-PHY Driver");
 318MODULE_LICENSE("GPL");
 319