phy-qcom-ipq4019-usb.c 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Copyright (C) 2018 John Crispin <john@phrozen.org>
  4. *
  5. * Based on code from
  6. * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
  7. *
  8. */
  9. #include <linux/delay.h>
  10. #include <linux/err.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/phy/phy.h>
  17. #include <linux/platform_device.h>
  18. #include <linux/reset.h>
  19. struct ipq4019_usb_phy {
  20. struct device *dev;
  21. struct phy *phy;
  22. void __iomem *base;
  23. struct reset_control *por_rst;
  24. struct reset_control *srif_rst;
  25. };
  26. static int ipq4019_ss_phy_power_off(struct phy *_phy)
  27. {
  28. struct ipq4019_usb_phy *phy = phy_get_drvdata(_phy);
  29. reset_control_assert(phy->por_rst);
  30. msleep(10);
  31. return 0;
  32. }
  33. static int ipq4019_ss_phy_power_on(struct phy *_phy)
  34. {
  35. struct ipq4019_usb_phy *phy = phy_get_drvdata(_phy);
  36. ipq4019_ss_phy_power_off(_phy);
  37. reset_control_deassert(phy->por_rst);
  38. return 0;
  39. }
  40. static const struct phy_ops ipq4019_usb_ss_phy_ops = {
  41. .power_on = ipq4019_ss_phy_power_on,
  42. .power_off = ipq4019_ss_phy_power_off,
  43. };
  44. static int ipq4019_hs_phy_power_off(struct phy *_phy)
  45. {
  46. struct ipq4019_usb_phy *phy = phy_get_drvdata(_phy);
  47. reset_control_assert(phy->por_rst);
  48. msleep(10);
  49. reset_control_assert(phy->srif_rst);
  50. msleep(10);
  51. return 0;
  52. }
  53. static int ipq4019_hs_phy_power_on(struct phy *_phy)
  54. {
  55. struct ipq4019_usb_phy *phy = phy_get_drvdata(_phy);
  56. ipq4019_hs_phy_power_off(_phy);
  57. reset_control_deassert(phy->srif_rst);
  58. msleep(10);
  59. reset_control_deassert(phy->por_rst);
  60. return 0;
  61. }
  62. static const struct phy_ops ipq4019_usb_hs_phy_ops = {
  63. .power_on = ipq4019_hs_phy_power_on,
  64. .power_off = ipq4019_hs_phy_power_off,
  65. };
  66. static const struct of_device_id ipq4019_usb_phy_of_match[] = {
  67. { .compatible = "qcom,usb-hs-ipq4019-phy", .data = &ipq4019_usb_hs_phy_ops},
  68. { .compatible = "qcom,usb-ss-ipq4019-phy", .data = &ipq4019_usb_ss_phy_ops},
  69. { },
  70. };
  71. MODULE_DEVICE_TABLE(of, ipq4019_usb_phy_of_match);
  72. static int ipq4019_usb_phy_probe(struct platform_device *pdev)
  73. {
  74. struct device *dev = &pdev->dev;
  75. struct phy_provider *phy_provider;
  76. struct ipq4019_usb_phy *phy;
  77. phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
  78. if (!phy)
  79. return -ENOMEM;
  80. phy->dev = &pdev->dev;
  81. phy->base = devm_platform_ioremap_resource(pdev, 0);
  82. if (IS_ERR(phy->base)) {
  83. dev_err(dev, "failed to remap register memory\n");
  84. return PTR_ERR(phy->base);
  85. }
  86. phy->por_rst = devm_reset_control_get(phy->dev, "por_rst");
  87. if (IS_ERR(phy->por_rst)) {
  88. if (PTR_ERR(phy->por_rst) != -EPROBE_DEFER)
  89. dev_err(dev, "POR reset is missing\n");
  90. return PTR_ERR(phy->por_rst);
  91. }
  92. phy->srif_rst = devm_reset_control_get_optional(phy->dev, "srif_rst");
  93. if (IS_ERR(phy->srif_rst))
  94. return PTR_ERR(phy->srif_rst);
  95. phy->phy = devm_phy_create(dev, NULL, of_device_get_match_data(dev));
  96. if (IS_ERR(phy->phy)) {
  97. dev_err(dev, "failed to create PHY\n");
  98. return PTR_ERR(phy->phy);
  99. }
  100. phy_set_drvdata(phy->phy, phy);
  101. phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
  102. return PTR_ERR_OR_ZERO(phy_provider);
  103. }
  104. static struct platform_driver ipq4019_usb_phy_driver = {
  105. .probe = ipq4019_usb_phy_probe,
  106. .driver = {
  107. .of_match_table = ipq4019_usb_phy_of_match,
  108. .name = "ipq4019-usb-phy",
  109. }
  110. };
  111. module_platform_driver(ipq4019_usb_phy_driver);
  112. MODULE_DESCRIPTION("QCOM/IPQ4019 USB phy driver");
  113. MODULE_AUTHOR("John Crispin <john@phrozen.org>");
  114. MODULE_LICENSE("GPL v2");