root/drivers/phy/rockchip/phy-rockchip-usb.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. rockchip_usb_phy_power
  2. rockchip_usb_phy480m_recalc_rate
  3. rockchip_usb_phy480m_disable
  4. rockchip_usb_phy480m_enable
  5. rockchip_usb_phy480m_is_enabled
  6. rockchip_usb_phy_power_off
  7. rockchip_usb_phy_power_on
  8. rockchip_usb_phy_reset
  9. rockchip_usb_phy_action
  10. rockchip_usb_phy_init
  11. rockchip_init_usb_uart_common
  12. rk3188_init_usb_uart
  13. rk3288_init_usb_uart
  14. rockchip_usb_phy_probe
  15. rockchip_init_usb_uart
  16. rockchip_usb_uart

   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 
  26 static 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 
  48 struct rockchip_usb_phys {
  49         int reg;
  50         const char *pll_name;
  51 };
  52 
  53 struct rockchip_usb_phy_base;
  54 struct 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 
  61 struct rockchip_usb_phy_base {
  62         struct device *dev;
  63         struct regmap *reg_base;
  64         const struct rockchip_usb_phy_pdata *pdata;
  65 };
  66 
  67 struct 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 
  80 static 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 
  88 static unsigned long rockchip_usb_phy480m_recalc_rate(struct clk_hw *hw,
  89                                                 unsigned long parent_rate)
  90 {
  91         return 480000000;
  92 }
  93 
  94 static 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 
 107 static 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 
 117 static 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 
 132 static 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 
 139 static 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 
 151 static 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 
 169 static 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 
 182 static 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 
 189 static 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 
 202 static 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 
 307 err_clk_prov:
 308         if (!rk_phy->uart_enabled)
 309                 clk_unregister(rk_phy->clk480m);
 310 err_clk:
 311         if (rk_phy->clk)
 312                 clk_put(rk_phy->clk);
 313         return err;
 314 }
 315 
 316 static 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 
 324 static 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  */
 374 static 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 
 395 static 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  */
 424 static 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 
 445 static 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 
 456 static 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 
 502 static 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 
 509 MODULE_DEVICE_TABLE(of, rockchip_usb_phy_dt_ids);
 510 
 511 static 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 
 519 module_platform_driver(rockchip_usb_driver);
 520 
 521 #ifndef MODULE
 522 static 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 }
 569 early_initcall(rockchip_init_usb_uart);
 570 
 571 static int __init rockchip_usb_uart(char *buf)
 572 {
 573         enable_usb_uart = true;
 574         return 0;
 575 }
 576 early_param("rockchip.usb_uart", rockchip_usb_uart);
 577 #endif
 578 
 579 MODULE_AUTHOR("Yunzhi Li <lyz@rock-chips.com>");
 580 MODULE_DESCRIPTION("Rockchip USB 2.0 PHY driver");
 581 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */