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