linux/drivers/phy/rockchip/phy-rockchip-usb.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Rockchip usb PHY driver
   4 *
   5 * Copyright (C) 2014 Yunzhi Li <lyz@rock-chips.com>
   6 * Copyright (C) 2014 ROCKCHIP, Inc.
   7 */
   8
   9#include <linux/clk.h>
  10#include <linux/clk-provider.h>
  11#include <linux/io.h>
  12#include <linux/kernel.h>
  13#include <linux/module.h>
  14#include <linux/mutex.h>
  15#include <linux/of.h>
  16#include <linux/of_address.h>
  17#include <linux/of_platform.h>
  18#include <linux/phy/phy.h>
  19#include <linux/platform_device.h>
  20#include <linux/regulator/consumer.h>
  21#include <linux/reset.h>
  22#include <linux/regmap.h>
  23#include <linux/mfd/syscon.h>
  24#include <linux/delay.h>
  25
  26static int enable_usb_uart;
  27
  28#define HIWORD_UPDATE(val, mask) \
  29                ((val) | (mask) << 16)
  30
  31#define UOC_CON0                                        0x00
  32#define UOC_CON0_SIDDQ                                  BIT(13)
  33#define UOC_CON0_DISABLE                                BIT(4)
  34#define UOC_CON0_COMMON_ON_N                            BIT(0)
  35
  36#define UOC_CON2                                        0x08
  37#define UOC_CON2_SOFT_CON_SEL                           BIT(2)
  38
  39#define UOC_CON3                                        0x0c
  40/* bits present on rk3188 and rk3288 phys */
  41#define UOC_CON3_UTMI_TERMSEL_FULLSPEED                 BIT(5)
  42#define UOC_CON3_UTMI_XCVRSEELCT_FSTRANSC               (1 << 3)
  43#define UOC_CON3_UTMI_XCVRSEELCT_MASK                   (3 << 3)
  44#define UOC_CON3_UTMI_OPMODE_NODRIVING                  (1 << 1)
  45#define UOC_CON3_UTMI_OPMODE_MASK                       (3 << 1)
  46#define UOC_CON3_UTMI_SUSPENDN                          BIT(0)
  47
  48struct rockchip_usb_phys {
  49        int reg;
  50        const char *pll_name;
  51};
  52
  53struct rockchip_usb_phy_base;
  54struct rockchip_usb_phy_pdata {
  55        struct rockchip_usb_phys *phys;
  56        int (*init_usb_uart)(struct regmap *grf,
  57                             const struct rockchip_usb_phy_pdata *pdata);
  58        int usb_uart_phy;
  59};
  60
  61struct rockchip_usb_phy_base {
  62        struct device *dev;
  63        struct regmap *reg_base;
  64        const struct rockchip_usb_phy_pdata *pdata;
  65};
  66
  67struct rockchip_usb_phy {
  68        struct rockchip_usb_phy_base *base;
  69        struct device_node *np;
  70        unsigned int    reg_offset;
  71        struct clk      *clk;
  72        struct clk      *clk480m;
  73        struct clk_hw   clk480m_hw;
  74        struct phy      *phy;
  75        bool            uart_enabled;
  76        struct reset_control *reset;
  77        struct regulator *vbus;
  78};
  79
  80static int rockchip_usb_phy_power(struct rockchip_usb_phy *phy,
  81                                           bool siddq)
  82{
  83        u32 val = HIWORD_UPDATE(siddq ? UOC_CON0_SIDDQ : 0, UOC_CON0_SIDDQ);
  84
  85        return regmap_write(phy->base->reg_base, phy->reg_offset, val);
  86}
  87
  88static unsigned long rockchip_usb_phy480m_recalc_rate(struct clk_hw *hw,
  89                                                unsigned long parent_rate)
  90{
  91        return 480000000;
  92}
  93
  94static void rockchip_usb_phy480m_disable(struct clk_hw *hw)
  95{
  96        struct rockchip_usb_phy *phy = container_of(hw,
  97                                                    struct rockchip_usb_phy,
  98                                                    clk480m_hw);
  99
 100        if (phy->vbus)
 101                regulator_disable(phy->vbus);
 102
 103        /* Power down usb phy analog blocks by set siddq 1 */
 104        rockchip_usb_phy_power(phy, 1);
 105}
 106
 107static int rockchip_usb_phy480m_enable(struct clk_hw *hw)
 108{
 109        struct rockchip_usb_phy *phy = container_of(hw,
 110                                                    struct rockchip_usb_phy,
 111                                                    clk480m_hw);
 112
 113        /* Power up usb phy analog blocks by set siddq 0 */
 114        return rockchip_usb_phy_power(phy, 0);
 115}
 116
 117static int rockchip_usb_phy480m_is_enabled(struct clk_hw *hw)
 118{
 119        struct rockchip_usb_phy *phy = container_of(hw,
 120                                                    struct rockchip_usb_phy,
 121                                                    clk480m_hw);
 122        int ret;
 123        u32 val;
 124
 125        ret = regmap_read(phy->base->reg_base, phy->reg_offset, &val);
 126        if (ret < 0)
 127                return ret;
 128
 129        return (val & UOC_CON0_SIDDQ) ? 0 : 1;
 130}
 131
 132static const struct clk_ops rockchip_usb_phy480m_ops = {
 133        .enable = rockchip_usb_phy480m_enable,
 134        .disable = rockchip_usb_phy480m_disable,
 135        .is_enabled = rockchip_usb_phy480m_is_enabled,
 136        .recalc_rate = rockchip_usb_phy480m_recalc_rate,
 137};
 138
 139static int rockchip_usb_phy_power_off(struct phy *_phy)
 140{
 141        struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
 142
 143        if (phy->uart_enabled)
 144                return -EBUSY;
 145
 146        clk_disable_unprepare(phy->clk480m);
 147
 148        return 0;
 149}
 150
 151static int rockchip_usb_phy_power_on(struct phy *_phy)
 152{
 153        struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
 154
 155        if (phy->uart_enabled)
 156                return -EBUSY;
 157
 158        if (phy->vbus) {
 159                int ret;
 160
 161                ret = regulator_enable(phy->vbus);
 162                if (ret)
 163                        return ret;
 164        }
 165
 166        return clk_prepare_enable(phy->clk480m);
 167}
 168
 169static int rockchip_usb_phy_reset(struct phy *_phy)
 170{
 171        struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
 172
 173        if (phy->reset) {
 174                reset_control_assert(phy->reset);
 175                udelay(10);
 176                reset_control_deassert(phy->reset);
 177        }
 178
 179        return 0;
 180}
 181
 182static const struct phy_ops ops = {
 183        .power_on       = rockchip_usb_phy_power_on,
 184        .power_off      = rockchip_usb_phy_power_off,
 185        .reset          = rockchip_usb_phy_reset,
 186        .owner          = THIS_MODULE,
 187};
 188
 189static void rockchip_usb_phy_action(void *data)
 190{
 191        struct rockchip_usb_phy *rk_phy = data;
 192
 193        if (!rk_phy->uart_enabled) {
 194                of_clk_del_provider(rk_phy->np);
 195                clk_unregister(rk_phy->clk480m);
 196        }
 197
 198        if (rk_phy->clk)
 199                clk_put(rk_phy->clk);
 200}
 201
 202static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base,
 203                                 struct device_node *child)
 204{
 205        struct rockchip_usb_phy *rk_phy;
 206        unsigned int reg_offset;
 207        const char *clk_name;
 208        struct clk_init_data init;
 209        int err, i;
 210
 211        rk_phy = devm_kzalloc(base->dev, sizeof(*rk_phy), GFP_KERNEL);
 212        if (!rk_phy)
 213                return -ENOMEM;
 214
 215        rk_phy->base = base;
 216        rk_phy->np = child;
 217
 218        if (of_property_read_u32(child, "reg", &reg_offset)) {
 219                dev_err(base->dev, "missing reg property in node %pOFn\n",
 220                        child);
 221                return -EINVAL;
 222        }
 223
 224        rk_phy->reset = of_reset_control_get(child, "phy-reset");
 225        if (IS_ERR(rk_phy->reset))
 226                rk_phy->reset = NULL;
 227
 228        rk_phy->reg_offset = reg_offset;
 229
 230        rk_phy->clk = of_clk_get_by_name(child, "phyclk");
 231        if (IS_ERR(rk_phy->clk))
 232                rk_phy->clk = NULL;
 233
 234        i = 0;
 235        init.name = NULL;
 236        while (base->pdata->phys[i].reg) {
 237                if (base->pdata->phys[i].reg == reg_offset) {
 238                        init.name = base->pdata->phys[i].pll_name;
 239                        break;
 240                }
 241                i++;
 242        }
 243
 244        if (!init.name) {
 245                dev_err(base->dev, "phy data not found\n");
 246                return -EINVAL;
 247        }
 248
 249        if (enable_usb_uart && base->pdata->usb_uart_phy == i) {
 250                dev_dbg(base->dev, "phy%d used as uart output\n", i);
 251                rk_phy->uart_enabled = true;
 252        } else {
 253                if (rk_phy->clk) {
 254                        clk_name = __clk_get_name(rk_phy->clk);
 255                        init.flags = 0;
 256                        init.parent_names = &clk_name;
 257                        init.num_parents = 1;
 258                } else {
 259                        init.flags = 0;
 260                        init.parent_names = NULL;
 261                        init.num_parents = 0;
 262                }
 263
 264                init.ops = &rockchip_usb_phy480m_ops;
 265                rk_phy->clk480m_hw.init = &init;
 266
 267                rk_phy->clk480m = clk_register(base->dev, &rk_phy->clk480m_hw);
 268                if (IS_ERR(rk_phy->clk480m)) {
 269                        err = PTR_ERR(rk_phy->clk480m);
 270                        goto err_clk;
 271                }
 272
 273                err = of_clk_add_provider(child, of_clk_src_simple_get,
 274                                        rk_phy->clk480m);
 275                if (err < 0)
 276                        goto err_clk_prov;
 277        }
 278
 279        err = devm_add_action_or_reset(base->dev, rockchip_usb_phy_action,
 280                                       rk_phy);
 281        if (err)
 282                return err;
 283
 284        rk_phy->phy = devm_phy_create(base->dev, child, &ops);
 285        if (IS_ERR(rk_phy->phy)) {
 286                dev_err(base->dev, "failed to create PHY\n");
 287                return PTR_ERR(rk_phy->phy);
 288        }
 289        phy_set_drvdata(rk_phy->phy, rk_phy);
 290
 291        rk_phy->vbus = devm_regulator_get_optional(&rk_phy->phy->dev, "vbus");
 292        if (IS_ERR(rk_phy->vbus)) {
 293                if (PTR_ERR(rk_phy->vbus) == -EPROBE_DEFER)
 294                        return PTR_ERR(rk_phy->vbus);
 295                rk_phy->vbus = NULL;
 296        }
 297
 298        /*
 299         * When acting as uart-pipe, just keep clock on otherwise
 300         * only power up usb phy when it use, so disable it when init
 301         */
 302        if (rk_phy->uart_enabled)
 303                return clk_prepare_enable(rk_phy->clk);
 304        else
 305                return rockchip_usb_phy_power(rk_phy, 1);
 306
 307err_clk_prov:
 308        if (!rk_phy->uart_enabled)
 309                clk_unregister(rk_phy->clk480m);
 310err_clk:
 311        if (rk_phy->clk)
 312                clk_put(rk_phy->clk);
 313        return err;
 314}
 315
 316static const struct rockchip_usb_phy_pdata rk3066a_pdata = {
 317        .phys = (struct rockchip_usb_phys[]){
 318                { .reg = 0x17c, .pll_name = "sclk_otgphy0_480m" },
 319                { .reg = 0x188, .pll_name = "sclk_otgphy1_480m" },
 320                { /* sentinel */ }
 321        },
 322};
 323
 324static int __init rockchip_init_usb_uart_common(struct regmap *grf,
 325                                const struct rockchip_usb_phy_pdata *pdata)
 326{
 327        int regoffs = pdata->phys[pdata->usb_uart_phy].reg;
 328        int ret;
 329        u32 val;
 330
 331        /*
 332         * COMMON_ON and DISABLE settings are described in the TRM,
 333         * but were not present in the original code.
 334         * Also disable the analog phy components to save power.
 335         */
 336        val = HIWORD_UPDATE(UOC_CON0_COMMON_ON_N
 337                                | UOC_CON0_DISABLE
 338                                | UOC_CON0_SIDDQ,
 339                            UOC_CON0_COMMON_ON_N
 340                                | UOC_CON0_DISABLE
 341                                | UOC_CON0_SIDDQ);
 342        ret = regmap_write(grf, regoffs + UOC_CON0, val);
 343        if (ret)
 344                return ret;
 345
 346        val = HIWORD_UPDATE(UOC_CON2_SOFT_CON_SEL,
 347                            UOC_CON2_SOFT_CON_SEL);
 348        ret = regmap_write(grf, regoffs + UOC_CON2, val);
 349        if (ret)
 350                return ret;
 351
 352        val = HIWORD_UPDATE(UOC_CON3_UTMI_OPMODE_NODRIVING
 353                                | UOC_CON3_UTMI_XCVRSEELCT_FSTRANSC
 354                                | UOC_CON3_UTMI_TERMSEL_FULLSPEED,
 355                            UOC_CON3_UTMI_SUSPENDN
 356                                | UOC_CON3_UTMI_OPMODE_MASK
 357                                | UOC_CON3_UTMI_XCVRSEELCT_MASK
 358                                | UOC_CON3_UTMI_TERMSEL_FULLSPEED);
 359        ret = regmap_write(grf, UOC_CON3, val);
 360        if (ret)
 361                return ret;
 362
 363        return 0;
 364}
 365
 366#define RK3188_UOC0_CON0                                0x10c
 367#define RK3188_UOC0_CON0_BYPASSSEL                      BIT(9)
 368#define RK3188_UOC0_CON0_BYPASSDMEN                     BIT(8)
 369
 370/*
 371 * Enable the bypass of uart2 data through the otg usb phy.
 372 * See description of rk3288-variant for details.
 373 */
 374static int __init rk3188_init_usb_uart(struct regmap *grf,
 375                                const struct rockchip_usb_phy_pdata *pdata)
 376{
 377        u32 val;
 378        int ret;
 379
 380        ret = rockchip_init_usb_uart_common(grf, pdata);
 381        if (ret)
 382                return ret;
 383
 384        val = HIWORD_UPDATE(RK3188_UOC0_CON0_BYPASSSEL
 385                                | RK3188_UOC0_CON0_BYPASSDMEN,
 386                            RK3188_UOC0_CON0_BYPASSSEL
 387                                | RK3188_UOC0_CON0_BYPASSDMEN);
 388        ret = regmap_write(grf, RK3188_UOC0_CON0, val);
 389        if (ret)
 390                return ret;
 391
 392        return 0;
 393}
 394
 395static const struct rockchip_usb_phy_pdata rk3188_pdata = {
 396        .phys = (struct rockchip_usb_phys[]){
 397                { .reg = 0x10c, .pll_name = "sclk_otgphy0_480m" },
 398                { .reg = 0x11c, .pll_name = "sclk_otgphy1_480m" },
 399                { /* sentinel */ }
 400        },
 401        .init_usb_uart = rk3188_init_usb_uart,
 402        .usb_uart_phy = 0,
 403};
 404
 405#define RK3288_UOC0_CON3                                0x32c
 406#define RK3288_UOC0_CON3_BYPASSDMEN                     BIT(6)
 407#define RK3288_UOC0_CON3_BYPASSSEL                      BIT(7)
 408
 409/*
 410 * Enable the bypass of uart2 data through the otg usb phy.
 411 * Original description in the TRM.
 412 * 1. Disable the OTG block by setting OTGDISABLE0 to 1’b1.
 413 * 2. Disable the pull-up resistance on the D+ line by setting
 414 *    OPMODE0[1:0] to 2’b01.
 415 * 3. To ensure that the XO, Bias, and PLL blocks are powered down in Suspend
 416 *    mode, set COMMONONN to 1’b1.
 417 * 4. Place the USB PHY in Suspend mode by setting SUSPENDM0 to 1’b0.
 418 * 5. Set BYPASSSEL0 to 1’b1.
 419 * 6. To transmit data, controls BYPASSDMEN0, and BYPASSDMDATA0.
 420 * To receive data, monitor FSVPLUS0.
 421 *
 422 * The actual code in the vendor kernel does some things differently.
 423 */
 424static int __init rk3288_init_usb_uart(struct regmap *grf,
 425                                const struct rockchip_usb_phy_pdata *pdata)
 426{
 427        u32 val;
 428        int ret;
 429
 430        ret = rockchip_init_usb_uart_common(grf, pdata);
 431        if (ret)
 432                return ret;
 433
 434        val = HIWORD_UPDATE(RK3288_UOC0_CON3_BYPASSSEL
 435                                | RK3288_UOC0_CON3_BYPASSDMEN,
 436                            RK3288_UOC0_CON3_BYPASSSEL
 437                                | RK3288_UOC0_CON3_BYPASSDMEN);
 438        ret = regmap_write(grf, RK3288_UOC0_CON3, val);
 439        if (ret)
 440                return ret;
 441
 442        return 0;
 443}
 444
 445static const struct rockchip_usb_phy_pdata rk3288_pdata = {
 446        .phys = (struct rockchip_usb_phys[]){
 447                { .reg = 0x320, .pll_name = "sclk_otgphy0_480m" },
 448                { .reg = 0x334, .pll_name = "sclk_otgphy1_480m" },
 449                { .reg = 0x348, .pll_name = "sclk_otgphy2_480m" },
 450                { /* sentinel */ }
 451        },
 452        .init_usb_uart = rk3288_init_usb_uart,
 453        .usb_uart_phy = 0,
 454};
 455
 456static int rockchip_usb_phy_probe(struct platform_device *pdev)
 457{
 458        struct device *dev = &pdev->dev;
 459        struct rockchip_usb_phy_base *phy_base;
 460        struct phy_provider *phy_provider;
 461        const struct of_device_id *match;
 462        struct device_node *child;
 463        int err;
 464
 465        phy_base = devm_kzalloc(dev, sizeof(*phy_base), GFP_KERNEL);
 466        if (!phy_base)
 467                return -ENOMEM;
 468
 469        match = of_match_device(dev->driver->of_match_table, dev);
 470        if (!match || !match->data) {
 471                dev_err(dev, "missing phy data\n");
 472                return -EINVAL;
 473        }
 474
 475        phy_base->pdata = match->data;
 476
 477        phy_base->dev = dev;
 478        phy_base->reg_base = ERR_PTR(-ENODEV);
 479        if (dev->parent && dev->parent->of_node)
 480                phy_base->reg_base = syscon_node_to_regmap(
 481                                                dev->parent->of_node);
 482        if (IS_ERR(phy_base->reg_base))
 483                phy_base->reg_base = syscon_regmap_lookup_by_phandle(
 484                                                dev->of_node, "rockchip,grf");
 485        if (IS_ERR(phy_base->reg_base)) {
 486                dev_err(&pdev->dev, "Missing rockchip,grf property\n");
 487                return PTR_ERR(phy_base->reg_base);
 488        }
 489
 490        for_each_available_child_of_node(dev->of_node, child) {
 491                err = rockchip_usb_phy_init(phy_base, child);
 492                if (err) {
 493                        of_node_put(child);
 494                        return err;
 495                }
 496        }
 497
 498        phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
 499        return PTR_ERR_OR_ZERO(phy_provider);
 500}
 501
 502static const struct of_device_id rockchip_usb_phy_dt_ids[] = {
 503        { .compatible = "rockchip,rk3066a-usb-phy", .data = &rk3066a_pdata },
 504        { .compatible = "rockchip,rk3188-usb-phy", .data = &rk3188_pdata },
 505        { .compatible = "rockchip,rk3288-usb-phy", .data = &rk3288_pdata },
 506        {}
 507};
 508
 509MODULE_DEVICE_TABLE(of, rockchip_usb_phy_dt_ids);
 510
 511static struct platform_driver rockchip_usb_driver = {
 512        .probe          = rockchip_usb_phy_probe,
 513        .driver         = {
 514                .name   = "rockchip-usb-phy",
 515                .of_match_table = rockchip_usb_phy_dt_ids,
 516        },
 517};
 518
 519module_platform_driver(rockchip_usb_driver);
 520
 521#ifndef MODULE
 522static int __init rockchip_init_usb_uart(void)
 523{
 524        const struct of_device_id *match;
 525        const struct rockchip_usb_phy_pdata *data;
 526        struct device_node *np;
 527        struct regmap *grf;
 528        int ret;
 529
 530        if (!enable_usb_uart)
 531                return 0;
 532
 533        np = of_find_matching_node_and_match(NULL, rockchip_usb_phy_dt_ids,
 534                                             &match);
 535        if (!np) {
 536                pr_err("%s: failed to find usbphy node\n", __func__);
 537                return -ENOTSUPP;
 538        }
 539
 540        pr_debug("%s: using settings for %s\n", __func__, match->compatible);
 541        data = match->data;
 542
 543        if (!data->init_usb_uart) {
 544                pr_err("%s: usb-uart not available on %s\n",
 545                       __func__, match->compatible);
 546                return -ENOTSUPP;
 547        }
 548
 549        grf = ERR_PTR(-ENODEV);
 550        if (np->parent)
 551                grf = syscon_node_to_regmap(np->parent);
 552        if (IS_ERR(grf))
 553                grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
 554        if (IS_ERR(grf)) {
 555                pr_err("%s: Missing rockchip,grf property, %lu\n",
 556                       __func__, PTR_ERR(grf));
 557                return PTR_ERR(grf);
 558        }
 559
 560        ret = data->init_usb_uart(grf, data);
 561        if (ret) {
 562                pr_err("%s: could not init usb_uart, %d\n", __func__, ret);
 563                enable_usb_uart = 0;
 564                return ret;
 565        }
 566
 567        return 0;
 568}
 569early_initcall(rockchip_init_usb_uart);
 570
 571static int __init rockchip_usb_uart(char *buf)
 572{
 573        enable_usb_uart = true;
 574        return 0;
 575}
 576early_param("rockchip.usb_uart", rockchip_usb_uart);
 577#endif
 578
 579MODULE_AUTHOR("Yunzhi Li <lyz@rock-chips.com>");
 580MODULE_DESCRIPTION("Rockchip USB 2.0 PHY driver");
 581MODULE_LICENSE("GPL v2");
 582
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.