Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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