linux/drivers/phy/socionext/phy-uniphier-usb3ss.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * phy-uniphier-usb3ss.c - SS-PHY driver for Socionext UniPhier USB3 controller
   4 * Copyright 2015-2018 Socionext Inc.
   5 * Author:
   6 *      Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
   7 * Contributors:
   8 *      Motoya Tanigawa <tanigawa.motoya@socionext.com>
   9 *      Masami Hiramatsu <masami.hiramatsu@linaro.org>
  10 */
  11
  12#include <linux/bitfield.h>
  13#include <linux/bitops.h>
  14#include <linux/clk.h>
  15#include <linux/io.h>
  16#include <linux/module.h>
  17#include <linux/of.h>
  18#include <linux/of_platform.h>
  19#include <linux/phy/phy.h>
  20#include <linux/platform_device.h>
  21#include <linux/regulator/consumer.h>
  22#include <linux/reset.h>
  23
  24#define SSPHY_TESTI             0x0
  25#define TESTI_DAT_MASK          GENMASK(13, 6)
  26#define TESTI_ADR_MASK          GENMASK(5, 1)
  27#define TESTI_WR_EN             BIT(0)
  28
  29#define SSPHY_TESTO             0x4
  30#define TESTO_DAT_MASK          GENMASK(7, 0)
  31
  32#define PHY_F(regno, msb, lsb) { (regno), (msb), (lsb) }
  33
  34#define CDR_CPD_TRIM    PHY_F(7, 3, 0)  /* RxPLL charge pump current */
  35#define CDR_CPF_TRIM    PHY_F(8, 3, 0)  /* RxPLL charge pump current 2 */
  36#define TX_PLL_TRIM     PHY_F(9, 3, 0)  /* TxPLL charge pump current */
  37#define BGAP_TRIM       PHY_F(11, 3, 0) /* Bandgap voltage */
  38#define CDR_TRIM        PHY_F(13, 6, 5) /* Clock Data Recovery setting */
  39#define VCO_CTRL        PHY_F(26, 7, 4) /* VCO control */
  40#define VCOPLL_CTRL     PHY_F(27, 2, 0) /* TxPLL VCO tuning */
  41#define VCOPLL_CM       PHY_F(28, 1, 0) /* TxPLL voltage */
  42
  43#define MAX_PHY_PARAMS  7
  44
  45struct uniphier_u3ssphy_param {
  46        struct {
  47                int reg_no;
  48                int msb;
  49                int lsb;
  50        } field;
  51        u8 value;
  52};
  53
  54struct uniphier_u3ssphy_priv {
  55        struct device *dev;
  56        void __iomem *base;
  57        struct clk *clk, *clk_ext, *clk_parent, *clk_parent_gio;
  58        struct reset_control *rst, *rst_parent, *rst_parent_gio;
  59        struct regulator *vbus;
  60        const struct uniphier_u3ssphy_soc_data *data;
  61};
  62
  63struct uniphier_u3ssphy_soc_data {
  64        bool is_legacy;
  65        int nparams;
  66        const struct uniphier_u3ssphy_param param[MAX_PHY_PARAMS];
  67};
  68
  69static void uniphier_u3ssphy_testio_write(struct uniphier_u3ssphy_priv *priv,
  70                                          u32 data)
  71{
  72        /* need to read TESTO twice after accessing TESTI */
  73        writel(data, priv->base + SSPHY_TESTI);
  74        readl(priv->base + SSPHY_TESTO);
  75        readl(priv->base + SSPHY_TESTO);
  76}
  77
  78static void uniphier_u3ssphy_set_param(struct uniphier_u3ssphy_priv *priv,
  79                                       const struct uniphier_u3ssphy_param *p)
  80{
  81        u32 val;
  82        u8 field_mask = GENMASK(p->field.msb, p->field.lsb);
  83        u8 data;
  84
  85        /* read previous data */
  86        val  = FIELD_PREP(TESTI_DAT_MASK, 1);
  87        val |= FIELD_PREP(TESTI_ADR_MASK, p->field.reg_no);
  88        uniphier_u3ssphy_testio_write(priv, val);
  89        val = readl(priv->base + SSPHY_TESTO) & TESTO_DAT_MASK;
  90
  91        /* update value */
  92        val &= ~field_mask;
  93        data = field_mask & (p->value << p->field.lsb);
  94        val  = FIELD_PREP(TESTI_DAT_MASK, data | val);
  95        val |= FIELD_PREP(TESTI_ADR_MASK, p->field.reg_no);
  96        uniphier_u3ssphy_testio_write(priv, val);
  97        uniphier_u3ssphy_testio_write(priv, val | TESTI_WR_EN);
  98        uniphier_u3ssphy_testio_write(priv, val);
  99
 100        /* read current data as dummy */
 101        val  = FIELD_PREP(TESTI_DAT_MASK, 1);
 102        val |= FIELD_PREP(TESTI_ADR_MASK, p->field.reg_no);
 103        uniphier_u3ssphy_testio_write(priv, val);
 104        readl(priv->base + SSPHY_TESTO);
 105}
 106
 107static int uniphier_u3ssphy_power_on(struct phy *phy)
 108{
 109        struct uniphier_u3ssphy_priv *priv = phy_get_drvdata(phy);
 110        int ret;
 111
 112        ret = clk_prepare_enable(priv->clk_ext);
 113        if (ret)
 114                return ret;
 115
 116        ret = clk_prepare_enable(priv->clk);
 117        if (ret)
 118                goto out_clk_ext_disable;
 119
 120        ret = reset_control_deassert(priv->rst);
 121        if (ret)
 122                goto out_clk_disable;
 123
 124        if (priv->vbus) {
 125                ret = regulator_enable(priv->vbus);
 126                if (ret)
 127                        goto out_rst_assert;
 128        }
 129
 130        return 0;
 131
 132out_rst_assert:
 133        reset_control_assert(priv->rst);
 134out_clk_disable:
 135        clk_disable_unprepare(priv->clk);
 136out_clk_ext_disable:
 137        clk_disable_unprepare(priv->clk_ext);
 138
 139        return ret;
 140}
 141
 142static int uniphier_u3ssphy_power_off(struct phy *phy)
 143{
 144        struct uniphier_u3ssphy_priv *priv = phy_get_drvdata(phy);
 145
 146        if (priv->vbus)
 147                regulator_disable(priv->vbus);
 148
 149        reset_control_assert(priv->rst);
 150        clk_disable_unprepare(priv->clk);
 151        clk_disable_unprepare(priv->clk_ext);
 152
 153        return 0;
 154}
 155
 156static int uniphier_u3ssphy_init(struct phy *phy)
 157{
 158        struct uniphier_u3ssphy_priv *priv = phy_get_drvdata(phy);
 159        int i, ret;
 160
 161        ret = clk_prepare_enable(priv->clk_parent);
 162        if (ret)
 163                return ret;
 164
 165        ret = clk_prepare_enable(priv->clk_parent_gio);
 166        if (ret)
 167                goto out_clk_disable;
 168
 169        ret = reset_control_deassert(priv->rst_parent);
 170        if (ret)
 171                goto out_clk_gio_disable;
 172
 173        ret = reset_control_deassert(priv->rst_parent_gio);
 174        if (ret)
 175                goto out_rst_assert;
 176
 177        if (priv->data->is_legacy)
 178                return 0;
 179
 180        for (i = 0; i < priv->data->nparams; i++)
 181                uniphier_u3ssphy_set_param(priv, &priv->data->param[i]);
 182
 183        return 0;
 184
 185out_rst_assert:
 186        reset_control_assert(priv->rst_parent);
 187out_clk_gio_disable:
 188        clk_disable_unprepare(priv->clk_parent_gio);
 189out_clk_disable:
 190        clk_disable_unprepare(priv->clk_parent);
 191
 192        return ret;
 193}
 194
 195static int uniphier_u3ssphy_exit(struct phy *phy)
 196{
 197        struct uniphier_u3ssphy_priv *priv = phy_get_drvdata(phy);
 198
 199        reset_control_assert(priv->rst_parent_gio);
 200        reset_control_assert(priv->rst_parent);
 201        clk_disable_unprepare(priv->clk_parent_gio);
 202        clk_disable_unprepare(priv->clk_parent);
 203
 204        return 0;
 205}
 206
 207static const struct phy_ops uniphier_u3ssphy_ops = {
 208        .init           = uniphier_u3ssphy_init,
 209        .exit           = uniphier_u3ssphy_exit,
 210        .power_on       = uniphier_u3ssphy_power_on,
 211        .power_off      = uniphier_u3ssphy_power_off,
 212        .owner          = THIS_MODULE,
 213};
 214
 215static int uniphier_u3ssphy_probe(struct platform_device *pdev)
 216{
 217        struct device *dev = &pdev->dev;
 218        struct uniphier_u3ssphy_priv *priv;
 219        struct phy_provider *phy_provider;
 220        struct phy *phy;
 221
 222        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 223        if (!priv)
 224                return -ENOMEM;
 225
 226        priv->dev = dev;
 227        priv->data = of_device_get_match_data(dev);
 228        if (WARN_ON(!priv->data ||
 229                    priv->data->nparams > MAX_PHY_PARAMS))
 230                return -EINVAL;
 231
 232        priv->base = devm_platform_ioremap_resource(pdev, 0);
 233        if (IS_ERR(priv->base))
 234                return PTR_ERR(priv->base);
 235
 236        if (!priv->data->is_legacy) {
 237                priv->clk = devm_clk_get(dev, "phy");
 238                if (IS_ERR(priv->clk))
 239                        return PTR_ERR(priv->clk);
 240
 241                priv->clk_ext = devm_clk_get_optional(dev, "phy-ext");
 242                if (IS_ERR(priv->clk_ext))
 243                        return PTR_ERR(priv->clk_ext);
 244
 245                priv->rst = devm_reset_control_get_shared(dev, "phy");
 246                if (IS_ERR(priv->rst))
 247                        return PTR_ERR(priv->rst);
 248        } else {
 249                priv->clk_parent_gio = devm_clk_get(dev, "gio");
 250                if (IS_ERR(priv->clk_parent_gio))
 251                        return PTR_ERR(priv->clk_parent_gio);
 252
 253                priv->rst_parent_gio =
 254                        devm_reset_control_get_shared(dev, "gio");
 255                if (IS_ERR(priv->rst_parent_gio))
 256                        return PTR_ERR(priv->rst_parent_gio);
 257        }
 258
 259        priv->clk_parent = devm_clk_get(dev, "link");
 260        if (IS_ERR(priv->clk_parent))
 261                return PTR_ERR(priv->clk_parent);
 262
 263        priv->rst_parent = devm_reset_control_get_shared(dev, "link");
 264        if (IS_ERR(priv->rst_parent))
 265                return PTR_ERR(priv->rst_parent);
 266
 267        priv->vbus = devm_regulator_get_optional(dev, "vbus");
 268        if (IS_ERR(priv->vbus)) {
 269                if (PTR_ERR(priv->vbus) == -EPROBE_DEFER)
 270                        return PTR_ERR(priv->vbus);
 271                priv->vbus = NULL;
 272        }
 273
 274        phy = devm_phy_create(dev, dev->of_node, &uniphier_u3ssphy_ops);
 275        if (IS_ERR(phy))
 276                return PTR_ERR(phy);
 277
 278        phy_set_drvdata(phy, priv);
 279        phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
 280
 281        return PTR_ERR_OR_ZERO(phy_provider);
 282}
 283
 284static const struct uniphier_u3ssphy_soc_data uniphier_pro4_data = {
 285        .is_legacy = true,
 286};
 287
 288static const struct uniphier_u3ssphy_soc_data uniphier_pxs2_data = {
 289        .is_legacy = false,
 290        .nparams = 7,
 291        .param = {
 292                { CDR_CPD_TRIM, 10 },
 293                { CDR_CPF_TRIM, 3 },
 294                { TX_PLL_TRIM, 5 },
 295                { BGAP_TRIM, 9 },
 296                { CDR_TRIM, 2 },
 297                { VCOPLL_CTRL, 7 },
 298                { VCOPLL_CM, 1 },
 299        },
 300};
 301
 302static const struct uniphier_u3ssphy_soc_data uniphier_ld20_data = {
 303        .is_legacy = false,
 304        .nparams = 3,
 305        .param = {
 306                { CDR_CPD_TRIM, 6 },
 307                { CDR_TRIM, 2 },
 308                { VCO_CTRL, 5 },
 309        },
 310};
 311
 312static const struct of_device_id uniphier_u3ssphy_match[] = {
 313        {
 314                .compatible = "socionext,uniphier-pro4-usb3-ssphy",
 315                .data = &uniphier_pro4_data,
 316        },
 317        {
 318                .compatible = "socionext,uniphier-pro5-usb3-ssphy",
 319                .data = &uniphier_pro4_data,
 320        },
 321        {
 322                .compatible = "socionext,uniphier-pxs2-usb3-ssphy",
 323                .data = &uniphier_pxs2_data,
 324        },
 325        {
 326                .compatible = "socionext,uniphier-ld20-usb3-ssphy",
 327                .data = &uniphier_ld20_data,
 328        },
 329        {
 330                .compatible = "socionext,uniphier-pxs3-usb3-ssphy",
 331                .data = &uniphier_ld20_data,
 332        },
 333        {
 334                .compatible = "socionext,uniphier-nx1-usb3-ssphy",
 335                .data = &uniphier_ld20_data,
 336        },
 337        { /* sentinel */ }
 338};
 339MODULE_DEVICE_TABLE(of, uniphier_u3ssphy_match);
 340
 341static struct platform_driver uniphier_u3ssphy_driver = {
 342        .probe = uniphier_u3ssphy_probe,
 343        .driver = {
 344                .name = "uniphier-usb3-ssphy",
 345                .of_match_table = uniphier_u3ssphy_match,
 346        },
 347};
 348
 349module_platform_driver(uniphier_u3ssphy_driver);
 350
 351MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>");
 352MODULE_DESCRIPTION("UniPhier SS-PHY driver for USB3 controller");
 353MODULE_LICENSE("GPL v2");
 354