linux/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (c) 2020, The Linux Foundation. All rights reserved.
   4 */
   5
   6#include <linux/clk.h>
   7#include <linux/delay.h>
   8#include <linux/err.h>
   9#include <linux/io.h>
  10#include <linux/kernel.h>
  11#include <linux/module.h>
  12#include <linux/of.h>
  13#include <linux/of_device.h>
  14#include <linux/phy/phy.h>
  15#include <linux/platform_device.h>
  16#include <linux/regmap.h>
  17#include <linux/regulator/consumer.h>
  18#include <linux/reset.h>
  19#include <linux/slab.h>
  20
  21#define USB2_PHY_USB_PHY_UTMI_CTRL0             (0x3c)
  22#define SLEEPM                                  BIT(0)
  23#define OPMODE_MASK                             GENMASK(4, 3)
  24#define OPMODE_NORMAL                           (0x00)
  25#define OPMODE_NONDRIVING                       BIT(3)
  26#define TERMSEL                                 BIT(5)
  27
  28#define USB2_PHY_USB_PHY_UTMI_CTRL1             (0x40)
  29#define XCVRSEL                                 BIT(0)
  30
  31#define USB2_PHY_USB_PHY_UTMI_CTRL5             (0x50)
  32#define POR                                     BIT(1)
  33
  34#define USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON0    (0x54)
  35#define RETENABLEN                              BIT(3)
  36#define FSEL_MASK                               GENMASK(6, 4)
  37#define FSEL_DEFAULT                            (0x3 << 4)
  38
  39#define USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON1    (0x58)
  40#define VBUSVLDEXTSEL0                          BIT(4)
  41#define PLLBTUNE                                BIT(5)
  42
  43#define USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON2    (0x5c)
  44#define VREGBYPASS                              BIT(0)
  45
  46#define USB2_PHY_USB_PHY_HS_PHY_CTRL1           (0x60)
  47#define VBUSVLDEXT0                             BIT(0)
  48
  49#define USB2_PHY_USB_PHY_HS_PHY_CTRL2           (0x64)
  50#define USB2_AUTO_RESUME                        BIT(0)
  51#define USB2_SUSPEND_N                          BIT(2)
  52#define USB2_SUSPEND_N_SEL                      BIT(3)
  53
  54#define USB2_PHY_USB_PHY_CFG0                   (0x94)
  55#define UTMI_PHY_DATAPATH_CTRL_OVERRIDE_EN      BIT(0)
  56#define UTMI_PHY_CMN_CTRL_OVERRIDE_EN           BIT(1)
  57
  58#define USB2_PHY_USB_PHY_REFCLK_CTRL            (0xa0)
  59#define REFCLK_SEL_MASK                         GENMASK(1, 0)
  60#define REFCLK_SEL_DEFAULT                      (0x2 << 0)
  61
  62static const char * const qcom_snps_hsphy_vreg_names[] = {
  63        "vdda-pll", "vdda33", "vdda18",
  64};
  65
  66#define SNPS_HS_NUM_VREGS               ARRAY_SIZE(qcom_snps_hsphy_vreg_names)
  67
  68/**
  69 * struct qcom_snps_hsphy - snps hs phy attributes
  70 *
  71 * @phy: generic phy
  72 * @base: iomapped memory space for snps hs phy
  73 *
  74 * @cfg_ahb_clk: AHB2PHY interface clock
  75 * @ref_clk: phy reference clock
  76 * @iface_clk: phy interface clock
  77 * @phy_reset: phy reset control
  78 * @vregs: regulator supplies bulk data
  79 * @phy_initialized: if PHY has been initialized correctly
  80 * @mode: contains the current mode the PHY is in
  81 */
  82struct qcom_snps_hsphy {
  83        struct phy *phy;
  84        void __iomem *base;
  85
  86        struct clk *cfg_ahb_clk;
  87        struct clk *ref_clk;
  88        struct reset_control *phy_reset;
  89        struct regulator_bulk_data vregs[SNPS_HS_NUM_VREGS];
  90
  91        bool phy_initialized;
  92        enum phy_mode mode;
  93};
  94
  95static inline void qcom_snps_hsphy_write_mask(void __iomem *base, u32 offset,
  96                                                u32 mask, u32 val)
  97{
  98        u32 reg;
  99
 100        reg = readl_relaxed(base + offset);
 101        reg &= ~mask;
 102        reg |= val & mask;
 103        writel_relaxed(reg, base + offset);
 104
 105        /* Ensure above write is completed */
 106        readl_relaxed(base + offset);
 107}
 108
 109static int qcom_snps_hsphy_suspend(struct qcom_snps_hsphy *hsphy)
 110{
 111        dev_dbg(&hsphy->phy->dev, "Suspend QCOM SNPS PHY\n");
 112
 113        if (hsphy->mode == PHY_MODE_USB_HOST) {
 114                /* Enable auto-resume to meet remote wakeup timing */
 115                qcom_snps_hsphy_write_mask(hsphy->base,
 116                                           USB2_PHY_USB_PHY_HS_PHY_CTRL2,
 117                                           USB2_AUTO_RESUME,
 118                                           USB2_AUTO_RESUME);
 119                usleep_range(500, 1000);
 120                qcom_snps_hsphy_write_mask(hsphy->base,
 121                                           USB2_PHY_USB_PHY_HS_PHY_CTRL2,
 122                                           0, USB2_AUTO_RESUME);
 123        }
 124
 125        clk_disable_unprepare(hsphy->cfg_ahb_clk);
 126        return 0;
 127}
 128
 129static int qcom_snps_hsphy_resume(struct qcom_snps_hsphy *hsphy)
 130{
 131        int ret;
 132
 133        dev_dbg(&hsphy->phy->dev, "Resume QCOM SNPS PHY, mode\n");
 134
 135        ret = clk_prepare_enable(hsphy->cfg_ahb_clk);
 136        if (ret) {
 137                dev_err(&hsphy->phy->dev, "failed to enable cfg ahb clock\n");
 138                return ret;
 139        }
 140
 141        return 0;
 142}
 143
 144static int __maybe_unused qcom_snps_hsphy_runtime_suspend(struct device *dev)
 145{
 146        struct qcom_snps_hsphy *hsphy = dev_get_drvdata(dev);
 147
 148        if (!hsphy->phy_initialized)
 149                return 0;
 150
 151        qcom_snps_hsphy_suspend(hsphy);
 152        return 0;
 153}
 154
 155static int __maybe_unused qcom_snps_hsphy_runtime_resume(struct device *dev)
 156{
 157        struct qcom_snps_hsphy *hsphy = dev_get_drvdata(dev);
 158
 159        if (!hsphy->phy_initialized)
 160                return 0;
 161
 162        qcom_snps_hsphy_resume(hsphy);
 163        return 0;
 164}
 165
 166static int qcom_snps_hsphy_set_mode(struct phy *phy, enum phy_mode mode,
 167                                    int submode)
 168{
 169        struct qcom_snps_hsphy *hsphy = phy_get_drvdata(phy);
 170
 171        hsphy->mode = mode;
 172        return 0;
 173}
 174
 175static int qcom_snps_hsphy_init(struct phy *phy)
 176{
 177        struct qcom_snps_hsphy *hsphy = phy_get_drvdata(phy);
 178        int ret;
 179
 180        dev_vdbg(&phy->dev, "%s(): Initializing SNPS HS phy\n", __func__);
 181
 182        ret = regulator_bulk_enable(ARRAY_SIZE(hsphy->vregs), hsphy->vregs);
 183        if (ret)
 184                return ret;
 185
 186        ret = clk_prepare_enable(hsphy->cfg_ahb_clk);
 187        if (ret) {
 188                dev_err(&phy->dev, "failed to enable cfg ahb clock, %d\n", ret);
 189                goto poweroff_phy;
 190        }
 191
 192        ret = reset_control_assert(hsphy->phy_reset);
 193        if (ret) {
 194                dev_err(&phy->dev, "failed to assert phy_reset, %d\n", ret);
 195                goto disable_ahb_clk;
 196        }
 197
 198        usleep_range(100, 150);
 199
 200        ret = reset_control_deassert(hsphy->phy_reset);
 201        if (ret) {
 202                dev_err(&phy->dev, "failed to de-assert phy_reset, %d\n", ret);
 203                goto disable_ahb_clk;
 204        }
 205
 206        qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_CFG0,
 207                                        UTMI_PHY_CMN_CTRL_OVERRIDE_EN,
 208                                        UTMI_PHY_CMN_CTRL_OVERRIDE_EN);
 209        qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_UTMI_CTRL5,
 210                                                        POR, POR);
 211        qcom_snps_hsphy_write_mask(hsphy->base,
 212                                        USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON0,
 213                                        FSEL_MASK, 0);
 214        qcom_snps_hsphy_write_mask(hsphy->base,
 215                                        USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON1,
 216                                        PLLBTUNE, PLLBTUNE);
 217        qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_REFCLK_CTRL,
 218                                        REFCLK_SEL_DEFAULT, REFCLK_SEL_MASK);
 219        qcom_snps_hsphy_write_mask(hsphy->base,
 220                                        USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON1,
 221                                        VBUSVLDEXTSEL0, VBUSVLDEXTSEL0);
 222        qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL1,
 223                                        VBUSVLDEXT0, VBUSVLDEXT0);
 224
 225        qcom_snps_hsphy_write_mask(hsphy->base,
 226                                        USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON2,
 227                                        VREGBYPASS, VREGBYPASS);
 228
 229        qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL2,
 230                                        USB2_SUSPEND_N_SEL | USB2_SUSPEND_N,
 231                                        USB2_SUSPEND_N_SEL | USB2_SUSPEND_N);
 232
 233        qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_UTMI_CTRL0,
 234                                        SLEEPM, SLEEPM);
 235
 236        qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_UTMI_CTRL5,
 237                                        POR, 0);
 238
 239        qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL2,
 240                                        USB2_SUSPEND_N_SEL, 0);
 241
 242        qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_CFG0,
 243                                        UTMI_PHY_CMN_CTRL_OVERRIDE_EN, 0);
 244
 245        hsphy->phy_initialized = true;
 246
 247        return 0;
 248
 249disable_ahb_clk:
 250        clk_disable_unprepare(hsphy->cfg_ahb_clk);
 251poweroff_phy:
 252        regulator_bulk_disable(ARRAY_SIZE(hsphy->vregs), hsphy->vregs);
 253
 254        return ret;
 255}
 256
 257static int qcom_snps_hsphy_exit(struct phy *phy)
 258{
 259        struct qcom_snps_hsphy *hsphy = phy_get_drvdata(phy);
 260
 261        reset_control_assert(hsphy->phy_reset);
 262        clk_disable_unprepare(hsphy->cfg_ahb_clk);
 263        regulator_bulk_disable(ARRAY_SIZE(hsphy->vregs), hsphy->vregs);
 264        hsphy->phy_initialized = false;
 265
 266        return 0;
 267}
 268
 269static const struct phy_ops qcom_snps_hsphy_gen_ops = {
 270        .init           = qcom_snps_hsphy_init,
 271        .exit           = qcom_snps_hsphy_exit,
 272        .set_mode       = qcom_snps_hsphy_set_mode,
 273        .owner          = THIS_MODULE,
 274};
 275
 276static const struct of_device_id qcom_snps_hsphy_of_match_table[] = {
 277        { .compatible   = "qcom,sm8150-usb-hs-phy", },
 278        { .compatible   = "qcom,usb-snps-hs-7nm-phy", },
 279        { .compatible   = "qcom,usb-snps-femto-v2-phy", },
 280        { }
 281};
 282MODULE_DEVICE_TABLE(of, qcom_snps_hsphy_of_match_table);
 283
 284static const struct dev_pm_ops qcom_snps_hsphy_pm_ops = {
 285        SET_RUNTIME_PM_OPS(qcom_snps_hsphy_runtime_suspend,
 286                           qcom_snps_hsphy_runtime_resume, NULL)
 287};
 288
 289static int qcom_snps_hsphy_probe(struct platform_device *pdev)
 290{
 291        struct device *dev = &pdev->dev;
 292        struct qcom_snps_hsphy *hsphy;
 293        struct phy_provider *phy_provider;
 294        struct phy *generic_phy;
 295        int ret, i;
 296        int num;
 297
 298        hsphy = devm_kzalloc(dev, sizeof(*hsphy), GFP_KERNEL);
 299        if (!hsphy)
 300                return -ENOMEM;
 301
 302        hsphy->base = devm_platform_ioremap_resource(pdev, 0);
 303        if (IS_ERR(hsphy->base))
 304                return PTR_ERR(hsphy->base);
 305
 306        hsphy->ref_clk = devm_clk_get(dev, "ref");
 307        if (IS_ERR(hsphy->ref_clk)) {
 308                ret = PTR_ERR(hsphy->ref_clk);
 309                if (ret != -EPROBE_DEFER)
 310                        dev_err(dev, "failed to get ref clk, %d\n", ret);
 311                return ret;
 312        }
 313
 314        hsphy->phy_reset = devm_reset_control_get_exclusive(&pdev->dev, NULL);
 315        if (IS_ERR(hsphy->phy_reset)) {
 316                dev_err(dev, "failed to get phy core reset\n");
 317                return PTR_ERR(hsphy->phy_reset);
 318        }
 319
 320        num = ARRAY_SIZE(hsphy->vregs);
 321        for (i = 0; i < num; i++)
 322                hsphy->vregs[i].supply = qcom_snps_hsphy_vreg_names[i];
 323
 324        ret = devm_regulator_bulk_get(dev, num, hsphy->vregs);
 325        if (ret) {
 326                if (ret != -EPROBE_DEFER)
 327                        dev_err(dev, "failed to get regulator supplies: %d\n",
 328                                ret);
 329                return ret;
 330        }
 331
 332        pm_runtime_set_active(dev);
 333        pm_runtime_enable(dev);
 334        /*
 335         * Prevent runtime pm from being ON by default. Users can enable
 336         * it using power/control in sysfs.
 337         */
 338        pm_runtime_forbid(dev);
 339
 340        generic_phy = devm_phy_create(dev, NULL, &qcom_snps_hsphy_gen_ops);
 341        if (IS_ERR(generic_phy)) {
 342                ret = PTR_ERR(generic_phy);
 343                dev_err(dev, "failed to create phy, %d\n", ret);
 344                return ret;
 345        }
 346        hsphy->phy = generic_phy;
 347
 348        dev_set_drvdata(dev, hsphy);
 349        phy_set_drvdata(generic_phy, hsphy);
 350
 351        phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
 352        if (!IS_ERR(phy_provider))
 353                dev_dbg(dev, "Registered Qcom-SNPS HS phy\n");
 354        else
 355                pm_runtime_disable(dev);
 356
 357        return PTR_ERR_OR_ZERO(phy_provider);
 358}
 359
 360static struct platform_driver qcom_snps_hsphy_driver = {
 361        .probe          = qcom_snps_hsphy_probe,
 362        .driver = {
 363                .name   = "qcom-snps-hs-femto-v2-phy",
 364                .pm = &qcom_snps_hsphy_pm_ops,
 365                .of_match_table = qcom_snps_hsphy_of_match_table,
 366        },
 367};
 368
 369module_platform_driver(qcom_snps_hsphy_driver);
 370
 371MODULE_DESCRIPTION("Qualcomm SNPS FEMTO USB HS PHY V2 driver");
 372MODULE_LICENSE("GPL v2");
 373