linux/drivers/phy/socionext/phy-uniphier-usb3hs.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * phy-uniphier-usb3hs.c - HS-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/nvmem-consumer.h>
  18#include <linux/of.h>
  19#include <linux/of_platform.h>
  20#include <linux/phy/phy.h>
  21#include <linux/platform_device.h>
  22#include <linux/regulator/consumer.h>
  23#include <linux/reset.h>
  24#include <linux/slab.h>
  25
  26#define HSPHY_CFG0              0x0
  27#define HSPHY_CFG0_HS_I_MASK    GENMASK(31, 28)
  28#define HSPHY_CFG0_HSDISC_MASK  GENMASK(27, 26)
  29#define HSPHY_CFG0_SWING_MASK   GENMASK(17, 16)
  30#define HSPHY_CFG0_SEL_T_MASK   GENMASK(15, 12)
  31#define HSPHY_CFG0_RTERM_MASK   GENMASK(7, 6)
  32#define HSPHY_CFG0_TRIMMASK     (HSPHY_CFG0_HS_I_MASK \
  33                                 | HSPHY_CFG0_SEL_T_MASK \
  34                                 | HSPHY_CFG0_RTERM_MASK)
  35
  36#define HSPHY_CFG1              0x4
  37#define HSPHY_CFG1_DAT_EN       BIT(29)
  38#define HSPHY_CFG1_ADR_EN       BIT(28)
  39#define HSPHY_CFG1_ADR_MASK     GENMASK(27, 16)
  40#define HSPHY_CFG1_DAT_MASK     GENMASK(23, 16)
  41
  42#define PHY_F(regno, msb, lsb) { (regno), (msb), (lsb) }
  43
  44#define RX_CHK_SYNC     PHY_F(0, 5, 5)  /* RX sync mode */
  45#define RX_SYNC_SEL     PHY_F(1, 1, 0)  /* RX sync length */
  46#define LS_SLEW         PHY_F(10, 6, 6) /* LS mode slew rate */
  47#define FS_LS_DRV       PHY_F(10, 5, 5) /* FS/LS slew rate */
  48
  49#define MAX_PHY_PARAMS  4
  50
  51struct uniphier_u3hsphy_param {
  52        struct {
  53                int reg_no;
  54                int msb;
  55                int lsb;
  56        } field;
  57        u8 value;
  58};
  59
  60struct uniphier_u3hsphy_trim_param {
  61        unsigned int rterm;
  62        unsigned int sel_t;
  63        unsigned int hs_i;
  64};
  65
  66#define trim_param_is_valid(p)  ((p)->rterm || (p)->sel_t || (p)->hs_i)
  67
  68struct uniphier_u3hsphy_priv {
  69        struct device *dev;
  70        void __iomem *base;
  71        struct clk *clk, *clk_parent, *clk_ext, *clk_parent_gio;
  72        struct reset_control *rst, *rst_parent, *rst_parent_gio;
  73        struct regulator *vbus;
  74        const struct uniphier_u3hsphy_soc_data *data;
  75};
  76
  77struct uniphier_u3hsphy_soc_data {
  78        bool is_legacy;
  79        int nparams;
  80        const struct uniphier_u3hsphy_param param[MAX_PHY_PARAMS];
  81        u32 config0;
  82        u32 config1;
  83        void (*trim_func)(struct uniphier_u3hsphy_priv *priv, u32 *pconfig,
  84                          struct uniphier_u3hsphy_trim_param *pt);
  85};
  86
  87static void uniphier_u3hsphy_trim_ld20(struct uniphier_u3hsphy_priv *priv,
  88                                       u32 *pconfig,
  89                                       struct uniphier_u3hsphy_trim_param *pt)
  90{
  91        *pconfig &= ~HSPHY_CFG0_RTERM_MASK;
  92        *pconfig |= FIELD_PREP(HSPHY_CFG0_RTERM_MASK, pt->rterm);
  93
  94        *pconfig &= ~HSPHY_CFG0_SEL_T_MASK;
  95        *pconfig |= FIELD_PREP(HSPHY_CFG0_SEL_T_MASK, pt->sel_t);
  96
  97        *pconfig &= ~HSPHY_CFG0_HS_I_MASK;
  98        *pconfig |= FIELD_PREP(HSPHY_CFG0_HS_I_MASK,  pt->hs_i);
  99}
 100
 101static int uniphier_u3hsphy_get_nvparam(struct uniphier_u3hsphy_priv *priv,
 102                                        const char *name, unsigned int *val)
 103{
 104        struct nvmem_cell *cell;
 105        u8 *buf;
 106
 107        cell = devm_nvmem_cell_get(priv->dev, name);
 108        if (IS_ERR(cell))
 109                return PTR_ERR(cell);
 110
 111        buf = nvmem_cell_read(cell, NULL);
 112        if (IS_ERR(buf))
 113                return PTR_ERR(buf);
 114
 115        *val = *buf;
 116
 117        kfree(buf);
 118
 119        return 0;
 120}
 121
 122static int uniphier_u3hsphy_get_nvparams(struct uniphier_u3hsphy_priv *priv,
 123                                         struct uniphier_u3hsphy_trim_param *pt)
 124{
 125        int ret;
 126
 127        ret = uniphier_u3hsphy_get_nvparam(priv, "rterm", &pt->rterm);
 128        if (ret)
 129                return ret;
 130
 131        ret = uniphier_u3hsphy_get_nvparam(priv, "sel_t", &pt->sel_t);
 132        if (ret)
 133                return ret;
 134
 135        ret = uniphier_u3hsphy_get_nvparam(priv, "hs_i", &pt->hs_i);
 136        if (ret)
 137                return ret;
 138
 139        return 0;
 140}
 141
 142static int uniphier_u3hsphy_update_config(struct uniphier_u3hsphy_priv *priv,
 143                                          u32 *pconfig)
 144{
 145        struct uniphier_u3hsphy_trim_param trim;
 146        int ret, trimmed = 0;
 147
 148        if (priv->data->trim_func) {
 149                ret = uniphier_u3hsphy_get_nvparams(priv, &trim);
 150                if (ret == -EPROBE_DEFER)
 151                        return ret;
 152
 153                /*
 154                 * call trim_func only when trimming parameters that aren't
 155                 * all-zero can be acquired. All-zero parameters mean nothing
 156                 * has been written to nvmem.
 157                 */
 158                if (!ret && trim_param_is_valid(&trim)) {
 159                        priv->data->trim_func(priv, pconfig, &trim);
 160                        trimmed = 1;
 161                } else {
 162                        dev_dbg(priv->dev, "can't get parameter from nvmem\n");
 163                }
 164        }
 165
 166        /* use default parameters without trimming values */
 167        if (!trimmed) {
 168                *pconfig &= ~HSPHY_CFG0_HSDISC_MASK;
 169                *pconfig |= FIELD_PREP(HSPHY_CFG0_HSDISC_MASK, 3);
 170        }
 171
 172        return 0;
 173}
 174
 175static void uniphier_u3hsphy_set_param(struct uniphier_u3hsphy_priv *priv,
 176                                       const struct uniphier_u3hsphy_param *p)
 177{
 178        u32 val;
 179        u32 field_mask = GENMASK(p->field.msb, p->field.lsb);
 180        u8 data;
 181
 182        val = readl(priv->base + HSPHY_CFG1);
 183        val &= ~HSPHY_CFG1_ADR_MASK;
 184        val |= FIELD_PREP(HSPHY_CFG1_ADR_MASK, p->field.reg_no)
 185                | HSPHY_CFG1_ADR_EN;
 186        writel(val, priv->base + HSPHY_CFG1);
 187
 188        val = readl(priv->base + HSPHY_CFG1);
 189        val &= ~HSPHY_CFG1_ADR_EN;
 190        writel(val, priv->base + HSPHY_CFG1);
 191
 192        val = readl(priv->base + HSPHY_CFG1);
 193        val &= ~FIELD_PREP(HSPHY_CFG1_DAT_MASK, field_mask);
 194        data = field_mask & (p->value << p->field.lsb);
 195        val |=  FIELD_PREP(HSPHY_CFG1_DAT_MASK, data) | HSPHY_CFG1_DAT_EN;
 196        writel(val, priv->base + HSPHY_CFG1);
 197
 198        val = readl(priv->base + HSPHY_CFG1);
 199        val &= ~HSPHY_CFG1_DAT_EN;
 200        writel(val, priv->base + HSPHY_CFG1);
 201}
 202
 203static int uniphier_u3hsphy_power_on(struct phy *phy)
 204{
 205        struct uniphier_u3hsphy_priv *priv = phy_get_drvdata(phy);
 206        int ret;
 207
 208        ret = clk_prepare_enable(priv->clk_ext);
 209        if (ret)
 210                return ret;
 211
 212        ret = clk_prepare_enable(priv->clk);
 213        if (ret)
 214                goto out_clk_ext_disable;
 215
 216        ret = reset_control_deassert(priv->rst);
 217        if (ret)
 218                goto out_clk_disable;
 219
 220        if (priv->vbus) {
 221                ret = regulator_enable(priv->vbus);
 222                if (ret)
 223                        goto out_rst_assert;
 224        }
 225
 226        return 0;
 227
 228out_rst_assert:
 229        reset_control_assert(priv->rst);
 230out_clk_disable:
 231        clk_disable_unprepare(priv->clk);
 232out_clk_ext_disable:
 233        clk_disable_unprepare(priv->clk_ext);
 234
 235        return ret;
 236}
 237
 238static int uniphier_u3hsphy_power_off(struct phy *phy)
 239{
 240        struct uniphier_u3hsphy_priv *priv = phy_get_drvdata(phy);
 241
 242        if (priv->vbus)
 243                regulator_disable(priv->vbus);
 244
 245        reset_control_assert(priv->rst);
 246        clk_disable_unprepare(priv->clk);
 247        clk_disable_unprepare(priv->clk_ext);
 248
 249        return 0;
 250}
 251
 252static int uniphier_u3hsphy_init(struct phy *phy)
 253{
 254        struct uniphier_u3hsphy_priv *priv = phy_get_drvdata(phy);
 255        u32 config0, config1;
 256        int i, ret;
 257
 258        ret = clk_prepare_enable(priv->clk_parent);
 259        if (ret)
 260                return ret;
 261
 262        ret = clk_prepare_enable(priv->clk_parent_gio);
 263        if (ret)
 264                goto out_clk_disable;
 265
 266        ret = reset_control_deassert(priv->rst_parent);
 267        if (ret)
 268                goto out_clk_gio_disable;
 269
 270        ret = reset_control_deassert(priv->rst_parent_gio);
 271        if (ret)
 272                goto out_rst_assert;
 273
 274        if ((priv->data->is_legacy)
 275            || (!priv->data->config0 && !priv->data->config1))
 276                return 0;
 277
 278        config0 = priv->data->config0;
 279        config1 = priv->data->config1;
 280
 281        ret = uniphier_u3hsphy_update_config(priv, &config0);
 282        if (ret)
 283                goto out_rst_assert;
 284
 285        writel(config0, priv->base + HSPHY_CFG0);
 286        writel(config1, priv->base + HSPHY_CFG1);
 287
 288        for (i = 0; i < priv->data->nparams; i++)
 289                uniphier_u3hsphy_set_param(priv, &priv->data->param[i]);
 290
 291        return 0;
 292
 293out_rst_assert:
 294        reset_control_assert(priv->rst_parent);
 295out_clk_gio_disable:
 296        clk_disable_unprepare(priv->clk_parent_gio);
 297out_clk_disable:
 298        clk_disable_unprepare(priv->clk_parent);
 299
 300        return ret;
 301}
 302
 303static int uniphier_u3hsphy_exit(struct phy *phy)
 304{
 305        struct uniphier_u3hsphy_priv *priv = phy_get_drvdata(phy);
 306
 307        reset_control_assert(priv->rst_parent_gio);
 308        reset_control_assert(priv->rst_parent);
 309        clk_disable_unprepare(priv->clk_parent_gio);
 310        clk_disable_unprepare(priv->clk_parent);
 311
 312        return 0;
 313}
 314
 315static const struct phy_ops uniphier_u3hsphy_ops = {
 316        .init           = uniphier_u3hsphy_init,
 317        .exit           = uniphier_u3hsphy_exit,
 318        .power_on       = uniphier_u3hsphy_power_on,
 319        .power_off      = uniphier_u3hsphy_power_off,
 320        .owner          = THIS_MODULE,
 321};
 322
 323static int uniphier_u3hsphy_probe(struct platform_device *pdev)
 324{
 325        struct device *dev = &pdev->dev;
 326        struct uniphier_u3hsphy_priv *priv;
 327        struct phy_provider *phy_provider;
 328        struct phy *phy;
 329
 330        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 331        if (!priv)
 332                return -ENOMEM;
 333
 334        priv->dev = dev;
 335        priv->data = of_device_get_match_data(dev);
 336        if (WARN_ON(!priv->data ||
 337                    priv->data->nparams > MAX_PHY_PARAMS))
 338                return -EINVAL;
 339
 340        priv->base = devm_platform_ioremap_resource(pdev, 0);
 341        if (IS_ERR(priv->base))
 342                return PTR_ERR(priv->base);
 343
 344        if (!priv->data->is_legacy) {
 345                priv->clk = devm_clk_get(dev, "phy");
 346                if (IS_ERR(priv->clk))
 347                        return PTR_ERR(priv->clk);
 348
 349                priv->clk_ext = devm_clk_get_optional(dev, "phy-ext");
 350                if (IS_ERR(priv->clk_ext))
 351                        return PTR_ERR(priv->clk_ext);
 352
 353                priv->rst = devm_reset_control_get_shared(dev, "phy");
 354                if (IS_ERR(priv->rst))
 355                        return PTR_ERR(priv->rst);
 356
 357        } else {
 358                priv->clk_parent_gio = devm_clk_get(dev, "gio");
 359                if (IS_ERR(priv->clk_parent_gio))
 360                        return PTR_ERR(priv->clk_parent_gio);
 361
 362                priv->rst_parent_gio =
 363                        devm_reset_control_get_shared(dev, "gio");
 364                if (IS_ERR(priv->rst_parent_gio))
 365                        return PTR_ERR(priv->rst_parent_gio);
 366        }
 367
 368        priv->clk_parent = devm_clk_get(dev, "link");
 369        if (IS_ERR(priv->clk_parent))
 370                return PTR_ERR(priv->clk_parent);
 371
 372        priv->rst_parent = devm_reset_control_get_shared(dev, "link");
 373        if (IS_ERR(priv->rst_parent))
 374                return PTR_ERR(priv->rst_parent);
 375
 376        priv->vbus = devm_regulator_get_optional(dev, "vbus");
 377        if (IS_ERR(priv->vbus)) {
 378                if (PTR_ERR(priv->vbus) == -EPROBE_DEFER)
 379                        return PTR_ERR(priv->vbus);
 380                priv->vbus = NULL;
 381        }
 382
 383        phy = devm_phy_create(dev, dev->of_node, &uniphier_u3hsphy_ops);
 384        if (IS_ERR(phy))
 385                return PTR_ERR(phy);
 386
 387        phy_set_drvdata(phy, priv);
 388        phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
 389
 390        return PTR_ERR_OR_ZERO(phy_provider);
 391}
 392
 393static const struct uniphier_u3hsphy_soc_data uniphier_pro5_data = {
 394        .is_legacy = true,
 395        .nparams = 0,
 396};
 397
 398static const struct uniphier_u3hsphy_soc_data uniphier_pxs2_data = {
 399        .is_legacy = false,
 400        .nparams = 2,
 401        .param = {
 402                { RX_CHK_SYNC, 1 },
 403                { RX_SYNC_SEL, 1 },
 404        },
 405};
 406
 407static const struct uniphier_u3hsphy_soc_data uniphier_ld20_data = {
 408        .is_legacy = false,
 409        .nparams = 4,
 410        .param = {
 411                { RX_CHK_SYNC, 1 },
 412                { RX_SYNC_SEL, 1 },
 413                { LS_SLEW, 1 },
 414                { FS_LS_DRV, 1 },
 415        },
 416        .trim_func = uniphier_u3hsphy_trim_ld20,
 417        .config0 = 0x92316680,
 418        .config1 = 0x00000106,
 419};
 420
 421static const struct uniphier_u3hsphy_soc_data uniphier_pxs3_data = {
 422        .is_legacy = false,
 423        .nparams = 2,
 424        .param = {
 425                { RX_CHK_SYNC, 1 },
 426                { RX_SYNC_SEL, 1 },
 427        },
 428        .trim_func = uniphier_u3hsphy_trim_ld20,
 429        .config0 = 0x92316680,
 430        .config1 = 0x00000106,
 431};
 432
 433static const struct of_device_id uniphier_u3hsphy_match[] = {
 434        {
 435                .compatible = "socionext,uniphier-pro5-usb3-hsphy",
 436                .data = &uniphier_pro5_data,
 437        },
 438        {
 439                .compatible = "socionext,uniphier-pxs2-usb3-hsphy",
 440                .data = &uniphier_pxs2_data,
 441        },
 442        {
 443                .compatible = "socionext,uniphier-ld20-usb3-hsphy",
 444                .data = &uniphier_ld20_data,
 445        },
 446        {
 447                .compatible = "socionext,uniphier-pxs3-usb3-hsphy",
 448                .data = &uniphier_pxs3_data,
 449        },
 450        { /* sentinel */ }
 451};
 452MODULE_DEVICE_TABLE(of, uniphier_u3hsphy_match);
 453
 454static struct platform_driver uniphier_u3hsphy_driver = {
 455        .probe = uniphier_u3hsphy_probe,
 456        .driver = {
 457                .name = "uniphier-usb3-hsphy",
 458                .of_match_table = uniphier_u3hsphy_match,
 459        },
 460};
 461
 462module_platform_driver(uniphier_u3hsphy_driver);
 463
 464MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>");
 465MODULE_DESCRIPTION("UniPhier HS-PHY driver for USB3 controller");
 466MODULE_LICENSE("GPL v2");
 467