linux/drivers/phy/amlogic/phy-meson-axg-mipi-pcie-analog.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Amlogic AXG MIPI + PCIE analog PHY driver
   4 *
   5 * Copyright (C) 2019 Remi Pommarel <repk@triplefau.lt>
   6 */
   7#include <linux/bitfield.h>
   8#include <linux/bitops.h>
   9#include <linux/module.h>
  10#include <linux/phy/phy.h>
  11#include <linux/regmap.h>
  12#include <linux/delay.h>
  13#include <linux/mfd/syscon.h>
  14#include <linux/platform_device.h>
  15#include <dt-bindings/phy/phy.h>
  16
  17#define HHI_MIPI_CNTL0 0x00
  18#define         HHI_MIPI_CNTL0_COMMON_BLOCK     GENMASK(31, 28)
  19#define         HHI_MIPI_CNTL0_ENABLE           BIT(29)
  20#define         HHI_MIPI_CNTL0_BANDGAP          BIT(26)
  21#define         HHI_MIPI_CNTL0_DIF_REF_CTL1     GENMASK(25, 16)
  22#define         HHI_MIPI_CNTL0_DIF_REF_CTL0     GENMASK(15, 0)
  23
  24#define HHI_MIPI_CNTL1 0x04
  25#define         HHI_MIPI_CNTL1_CH0_CML_PDR_EN   BIT(12)
  26#define         HHI_MIPI_CNTL1_LP_ABILITY       GENMASK(5, 4)
  27#define         HHI_MIPI_CNTL1_LP_RESISTER      BIT(3)
  28#define         HHI_MIPI_CNTL1_INPUT_SETTING    BIT(2)
  29#define         HHI_MIPI_CNTL1_INPUT_SEL        BIT(1)
  30#define         HHI_MIPI_CNTL1_PRBS7_EN         BIT(0)
  31
  32#define HHI_MIPI_CNTL2 0x08
  33#define         HHI_MIPI_CNTL2_CH_PU            GENMASK(31, 25)
  34#define         HHI_MIPI_CNTL2_CH_CTL           GENMASK(24, 19)
  35#define         HHI_MIPI_CNTL2_CH0_DIGDR_EN     BIT(18)
  36#define         HHI_MIPI_CNTL2_CH_DIGDR_EN      BIT(17)
  37#define         HHI_MIPI_CNTL2_LPULPS_EN        BIT(16)
  38#define         HHI_MIPI_CNTL2_CH_EN            GENMASK(15, 11)
  39#define         HHI_MIPI_CNTL2_CH0_LP_CTL       GENMASK(10, 1)
  40
  41#define DSI_LANE_0              BIT(4)
  42#define DSI_LANE_1              BIT(3)
  43#define DSI_LANE_CLK            BIT(2)
  44#define DSI_LANE_2              BIT(1)
  45#define DSI_LANE_3              BIT(0)
  46
  47struct phy_axg_mipi_pcie_analog_priv {
  48        struct phy *phy;
  49        struct regmap *regmap;
  50        bool dsi_configured;
  51        bool dsi_enabled;
  52        bool powered;
  53        struct phy_configure_opts_mipi_dphy config;
  54};
  55
  56static void phy_bandgap_enable(struct phy_axg_mipi_pcie_analog_priv *priv)
  57{
  58        regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0,
  59                        HHI_MIPI_CNTL0_BANDGAP, HHI_MIPI_CNTL0_BANDGAP);
  60
  61        regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0,
  62                        HHI_MIPI_CNTL0_ENABLE, HHI_MIPI_CNTL0_ENABLE);
  63}
  64
  65static void phy_bandgap_disable(struct phy_axg_mipi_pcie_analog_priv *priv)
  66{
  67        regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0,
  68                        HHI_MIPI_CNTL0_BANDGAP, 0);
  69        regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0,
  70                        HHI_MIPI_CNTL0_ENABLE, 0);
  71}
  72
  73static void phy_dsi_analog_enable(struct phy_axg_mipi_pcie_analog_priv *priv)
  74{
  75        u32 reg;
  76
  77        regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0,
  78                           HHI_MIPI_CNTL0_DIF_REF_CTL1,
  79                           FIELD_PREP(HHI_MIPI_CNTL0_DIF_REF_CTL1, 0x1b8));
  80        regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0,
  81                           BIT(31), BIT(31));
  82        regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0,
  83                           HHI_MIPI_CNTL0_DIF_REF_CTL0,
  84                           FIELD_PREP(HHI_MIPI_CNTL0_DIF_REF_CTL0, 0x8));
  85
  86        regmap_write(priv->regmap, HHI_MIPI_CNTL1, 0x001e);
  87
  88        regmap_write(priv->regmap, HHI_MIPI_CNTL2,
  89                     (0x26e0 << 16) | (0x459 << 0));
  90
  91        reg = DSI_LANE_CLK;
  92        switch (priv->config.lanes) {
  93        case 4:
  94                reg |= DSI_LANE_3;
  95                fallthrough;
  96        case 3:
  97                reg |= DSI_LANE_2;
  98                fallthrough;
  99        case 2:
 100                reg |= DSI_LANE_1;
 101                fallthrough;
 102        case 1:
 103                reg |= DSI_LANE_0;
 104                break;
 105        default:
 106                reg = 0;
 107        }
 108
 109        regmap_update_bits(priv->regmap, HHI_MIPI_CNTL2,
 110                           HHI_MIPI_CNTL2_CH_EN,
 111                           FIELD_PREP(HHI_MIPI_CNTL2_CH_EN, reg));
 112
 113        priv->dsi_enabled = true;
 114}
 115
 116static void phy_dsi_analog_disable(struct phy_axg_mipi_pcie_analog_priv *priv)
 117{
 118        regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0,
 119                        HHI_MIPI_CNTL0_DIF_REF_CTL1,
 120                        FIELD_PREP(HHI_MIPI_CNTL0_DIF_REF_CTL1, 0));
 121        regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0, BIT(31), 0);
 122        regmap_update_bits(priv->regmap, HHI_MIPI_CNTL0,
 123                        HHI_MIPI_CNTL0_DIF_REF_CTL1, 0);
 124
 125        regmap_write(priv->regmap, HHI_MIPI_CNTL1, 0x6);
 126
 127        regmap_write(priv->regmap, HHI_MIPI_CNTL2, 0x00200000);
 128
 129        priv->dsi_enabled = false;
 130}
 131
 132static int phy_axg_mipi_pcie_analog_configure(struct phy *phy,
 133                                              union phy_configure_opts *opts)
 134{
 135        struct phy_axg_mipi_pcie_analog_priv *priv = phy_get_drvdata(phy);
 136        int ret;
 137
 138        ret = phy_mipi_dphy_config_validate(&opts->mipi_dphy);
 139        if (ret)
 140                return ret;
 141
 142        memcpy(&priv->config, opts, sizeof(priv->config));
 143
 144        priv->dsi_configured = true;
 145
 146        /* If PHY was already powered on, setup the DSI analog part */
 147        if (priv->powered) {
 148                /* If reconfiguring, disable & reconfigure */
 149                if (priv->dsi_enabled)
 150                        phy_dsi_analog_disable(priv);
 151
 152                usleep_range(100, 200);
 153
 154                phy_dsi_analog_enable(priv);
 155        }
 156
 157        return 0;
 158}
 159
 160static int phy_axg_mipi_pcie_analog_power_on(struct phy *phy)
 161{
 162        struct phy_axg_mipi_pcie_analog_priv *priv = phy_get_drvdata(phy);
 163
 164        phy_bandgap_enable(priv);
 165
 166        if (priv->dsi_configured)
 167                phy_dsi_analog_enable(priv);
 168
 169        priv->powered = true;
 170
 171        return 0;
 172}
 173
 174static int phy_axg_mipi_pcie_analog_power_off(struct phy *phy)
 175{
 176        struct phy_axg_mipi_pcie_analog_priv *priv = phy_get_drvdata(phy);
 177
 178        phy_bandgap_disable(priv);
 179
 180        if (priv->dsi_enabled)
 181                phy_dsi_analog_disable(priv);
 182
 183        priv->powered = false;
 184
 185        return 0;
 186}
 187
 188static const struct phy_ops phy_axg_mipi_pcie_analog_ops = {
 189        .configure = phy_axg_mipi_pcie_analog_configure,
 190        .power_on = phy_axg_mipi_pcie_analog_power_on,
 191        .power_off = phy_axg_mipi_pcie_analog_power_off,
 192        .owner = THIS_MODULE,
 193};
 194
 195static int phy_axg_mipi_pcie_analog_probe(struct platform_device *pdev)
 196{
 197        struct phy_provider *phy;
 198        struct device *dev = &pdev->dev;
 199        struct phy_axg_mipi_pcie_analog_priv *priv;
 200        struct device_node *np = dev->of_node;
 201        struct regmap *map;
 202        int ret;
 203
 204        priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL);
 205        if (!priv)
 206                return -ENOMEM;
 207
 208        /* Get the hhi system controller node */
 209        map = syscon_node_to_regmap(of_get_parent(dev->of_node));
 210        if (IS_ERR(map)) {
 211                dev_err(dev,
 212                        "failed to get HHI regmap\n");
 213                return PTR_ERR(map);
 214        }
 215
 216        priv->regmap = map;
 217
 218        priv->phy = devm_phy_create(dev, np, &phy_axg_mipi_pcie_analog_ops);
 219        if (IS_ERR(priv->phy)) {
 220                ret = PTR_ERR(priv->phy);
 221                if (ret != -EPROBE_DEFER)
 222                        dev_err(dev, "failed to create PHY\n");
 223                return ret;
 224        }
 225
 226        phy_set_drvdata(priv->phy, priv);
 227        dev_set_drvdata(dev, priv);
 228
 229        phy = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
 230
 231        return PTR_ERR_OR_ZERO(phy);
 232}
 233
 234static const struct of_device_id phy_axg_mipi_pcie_analog_of_match[] = {
 235        {
 236                .compatible = "amlogic,axg-mipi-pcie-analog-phy",
 237        },
 238        { },
 239};
 240MODULE_DEVICE_TABLE(of, phy_axg_mipi_pcie_analog_of_match);
 241
 242static struct platform_driver phy_axg_mipi_pcie_analog_driver = {
 243        .probe = phy_axg_mipi_pcie_analog_probe,
 244        .driver = {
 245                .name = "phy-axg-mipi-pcie-analog",
 246                .of_match_table = phy_axg_mipi_pcie_analog_of_match,
 247        },
 248};
 249module_platform_driver(phy_axg_mipi_pcie_analog_driver);
 250
 251MODULE_AUTHOR("Remi Pommarel <repk@triplefau.lt>");
 252MODULE_DESCRIPTION("Amlogic AXG MIPI + PCIE analog PHY driver");
 253MODULE_LICENSE("GPL v2");
 254