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

usb: chipidea: tegra: Add runtime PM and OPP support

The Tegra USB controller belongs to the core power domain and we're going
to enable GENPD support for the core domain. Now USB controller must be
resumed using runtime PM API in order to initialize the USB power state.
We already support runtime PM for the CI device, but CI's PM is separated
from the RPM managed by tegra-usb driver. Add runtime PM and OPP support
to the driver.

Acked-by: Peter Chen <peter.chen@kernel.org>
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>

authored by

Dmitry Osipenko and committed by
Thierry Reding
8b85e11c c132bc88

+46 -7
+46 -7
drivers/usb/chipidea/ci_hdrc_tegra.c
··· 7 7 #include <linux/io.h> 8 8 #include <linux/module.h> 9 9 #include <linux/of_device.h> 10 + #include <linux/pm_runtime.h> 10 11 #include <linux/reset.h> 11 12 12 13 #include <linux/usb.h> ··· 15 14 #include <linux/usb/hcd.h> 16 15 #include <linux/usb/of.h> 17 16 #include <linux/usb/phy.h> 17 + 18 + #include <soc/tegra/common.h> 18 19 19 20 #include "../host/ehci.h" 20 21 ··· 281 278 if (!usb) 282 279 return -ENOMEM; 283 280 281 + platform_set_drvdata(pdev, usb); 282 + 284 283 soc = of_device_get_match_data(&pdev->dev); 285 284 if (!soc) { 286 285 dev_err(&pdev->dev, "failed to match OF data\n"); ··· 301 296 return err; 302 297 } 303 298 304 - err = clk_prepare_enable(usb->clk); 305 - if (err < 0) { 306 - dev_err(&pdev->dev, "failed to enable clock: %d\n", err); 299 + err = devm_tegra_core_dev_init_opp_table_common(&pdev->dev); 300 + if (err) 307 301 return err; 308 - } 302 + 303 + pm_runtime_enable(&pdev->dev); 304 + err = pm_runtime_resume_and_get(&pdev->dev); 305 + if (err) 306 + return err; 309 307 310 308 if (device_property_present(&pdev->dev, "nvidia,needs-double-reset")) 311 309 usb->needs_double_reset = true; ··· 327 319 err = usb_phy_init(usb->phy); 328 320 if (err) 329 321 goto fail_power_off; 330 - 331 - platform_set_drvdata(pdev, usb); 332 322 333 323 /* setup and register ChipIdea HDRC device */ 334 324 usb->soc = soc; ··· 356 350 phy_shutdown: 357 351 usb_phy_shutdown(usb->phy); 358 352 fail_power_off: 359 - clk_disable_unprepare(usb->clk); 353 + pm_runtime_put_sync_suspend(&pdev->dev); 354 + pm_runtime_force_suspend(&pdev->dev); 355 + 360 356 return err; 361 357 } 362 358 ··· 368 360 369 361 ci_hdrc_remove_device(usb->dev); 370 362 usb_phy_shutdown(usb->phy); 363 + 364 + pm_runtime_put_sync_suspend(&pdev->dev); 365 + pm_runtime_force_suspend(&pdev->dev); 366 + 367 + return 0; 368 + } 369 + 370 + static int __maybe_unused tegra_usb_runtime_resume(struct device *dev) 371 + { 372 + struct tegra_usb *usb = dev_get_drvdata(dev); 373 + int err; 374 + 375 + err = clk_prepare_enable(usb->clk); 376 + if (err < 0) { 377 + dev_err(dev, "failed to enable clock: %d\n", err); 378 + return err; 379 + } 380 + 381 + return 0; 382 + } 383 + 384 + static int __maybe_unused tegra_usb_runtime_suspend(struct device *dev) 385 + { 386 + struct tegra_usb *usb = dev_get_drvdata(dev); 387 + 371 388 clk_disable_unprepare(usb->clk); 372 389 373 390 return 0; 374 391 } 375 392 393 + static const struct dev_pm_ops tegra_usb_pm = { 394 + SET_RUNTIME_PM_OPS(tegra_usb_runtime_suspend, tegra_usb_runtime_resume, 395 + NULL) 396 + }; 397 + 376 398 static struct platform_driver tegra_usb_driver = { 377 399 .driver = { 378 400 .name = "tegra-usb", 379 401 .of_match_table = tegra_usb_of_match, 402 + .pm = &tegra_usb_pm, 380 403 }, 381 404 .probe = tegra_usb_probe, 382 405 .remove = tegra_usb_remove,