phy-lpc18xx-usb-otg.c 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * PHY driver for NXP LPC18xx/43xx internal USB OTG PHY
  4. *
  5. * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com>
  6. */
  7. #include <linux/clk.h>
  8. #include <linux/err.h>
  9. #include <linux/mfd/syscon.h>
  10. #include <linux/module.h>
  11. #include <linux/of.h>
  12. #include <linux/phy/phy.h>
  13. #include <linux/platform_device.h>
  14. #include <linux/regmap.h>
  15. /* USB OTG PHY register offset and bit in CREG */
  16. #define LPC18XX_CREG_CREG0 0x004
  17. #define LPC18XX_CREG_CREG0_USB0PHY BIT(5)
  18. struct lpc18xx_usb_otg_phy {
  19. struct phy *phy;
  20. struct clk *clk;
  21. struct regmap *reg;
  22. };
  23. static int lpc18xx_usb_otg_phy_init(struct phy *phy)
  24. {
  25. struct lpc18xx_usb_otg_phy *lpc = phy_get_drvdata(phy);
  26. int ret;
  27. /* The PHY must be clocked at 480 MHz */
  28. ret = clk_set_rate(lpc->clk, 480000000);
  29. if (ret)
  30. return ret;
  31. return clk_prepare(lpc->clk);
  32. }
  33. static int lpc18xx_usb_otg_phy_exit(struct phy *phy)
  34. {
  35. struct lpc18xx_usb_otg_phy *lpc = phy_get_drvdata(phy);
  36. clk_unprepare(lpc->clk);
  37. return 0;
  38. }
  39. static int lpc18xx_usb_otg_phy_power_on(struct phy *phy)
  40. {
  41. struct lpc18xx_usb_otg_phy *lpc = phy_get_drvdata(phy);
  42. int ret;
  43. ret = clk_enable(lpc->clk);
  44. if (ret)
  45. return ret;
  46. /* The bit in CREG is cleared to enable the PHY */
  47. ret = regmap_update_bits(lpc->reg, LPC18XX_CREG_CREG0,
  48. LPC18XX_CREG_CREG0_USB0PHY, 0);
  49. if (ret) {
  50. clk_disable(lpc->clk);
  51. return ret;
  52. }
  53. return 0;
  54. }
  55. static int lpc18xx_usb_otg_phy_power_off(struct phy *phy)
  56. {
  57. struct lpc18xx_usb_otg_phy *lpc = phy_get_drvdata(phy);
  58. int ret;
  59. ret = regmap_update_bits(lpc->reg, LPC18XX_CREG_CREG0,
  60. LPC18XX_CREG_CREG0_USB0PHY,
  61. LPC18XX_CREG_CREG0_USB0PHY);
  62. if (ret)
  63. return ret;
  64. clk_disable(lpc->clk);
  65. return 0;
  66. }
  67. static const struct phy_ops lpc18xx_usb_otg_phy_ops = {
  68. .init = lpc18xx_usb_otg_phy_init,
  69. .exit = lpc18xx_usb_otg_phy_exit,
  70. .power_on = lpc18xx_usb_otg_phy_power_on,
  71. .power_off = lpc18xx_usb_otg_phy_power_off,
  72. .owner = THIS_MODULE,
  73. };
  74. static int lpc18xx_usb_otg_phy_probe(struct platform_device *pdev)
  75. {
  76. struct phy_provider *phy_provider;
  77. struct lpc18xx_usb_otg_phy *lpc;
  78. lpc = devm_kzalloc(&pdev->dev, sizeof(*lpc), GFP_KERNEL);
  79. if (!lpc)
  80. return -ENOMEM;
  81. lpc->reg = syscon_node_to_regmap(pdev->dev.of_node->parent);
  82. if (IS_ERR(lpc->reg)) {
  83. dev_err(&pdev->dev, "failed to get syscon\n");
  84. return PTR_ERR(lpc->reg);
  85. }
  86. lpc->clk = devm_clk_get(&pdev->dev, NULL);
  87. if (IS_ERR(lpc->clk)) {
  88. dev_err(&pdev->dev, "failed to get clock\n");
  89. return PTR_ERR(lpc->clk);
  90. }
  91. lpc->phy = devm_phy_create(&pdev->dev, NULL, &lpc18xx_usb_otg_phy_ops);
  92. if (IS_ERR(lpc->phy)) {
  93. dev_err(&pdev->dev, "failed to create PHY\n");
  94. return PTR_ERR(lpc->phy);
  95. }
  96. phy_set_drvdata(lpc->phy, lpc);
  97. phy_provider = devm_of_phy_provider_register(&pdev->dev,
  98. of_phy_simple_xlate);
  99. return PTR_ERR_OR_ZERO(phy_provider);
  100. }
  101. static const struct of_device_id lpc18xx_usb_otg_phy_match[] = {
  102. { .compatible = "nxp,lpc1850-usb-otg-phy" },
  103. { }
  104. };
  105. MODULE_DEVICE_TABLE(of, lpc18xx_usb_otg_phy_match);
  106. static struct platform_driver lpc18xx_usb_otg_phy_driver = {
  107. .probe = lpc18xx_usb_otg_phy_probe,
  108. .driver = {
  109. .name = "lpc18xx-usb-otg-phy",
  110. .of_match_table = lpc18xx_usb_otg_phy_match,
  111. },
  112. };
  113. module_platform_driver(lpc18xx_usb_otg_phy_driver);
  114. MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");
  115. MODULE_DESCRIPTION("NXP LPC18xx/43xx USB OTG PHY driver");
  116. MODULE_LICENSE("GPL v2");