pwrseq-pcie-m2.c 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
  4. * Author: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
  5. */
  6. #include <linux/device.h>
  7. #include <linux/mod_devicetable.h>
  8. #include <linux/module.h>
  9. #include <linux/of.h>
  10. #include <linux/of_graph.h>
  11. #include <linux/platform_device.h>
  12. #include <linux/pwrseq/provider.h>
  13. #include <linux/regulator/consumer.h>
  14. #include <linux/slab.h>
  15. struct pwrseq_pcie_m2_pdata {
  16. const struct pwrseq_target_data **targets;
  17. };
  18. struct pwrseq_pcie_m2_ctx {
  19. struct pwrseq_device *pwrseq;
  20. struct device_node *of_node;
  21. const struct pwrseq_pcie_m2_pdata *pdata;
  22. struct regulator_bulk_data *regs;
  23. size_t num_vregs;
  24. struct notifier_block nb;
  25. };
  26. static int pwrseq_pcie_m2_m_vregs_enable(struct pwrseq_device *pwrseq)
  27. {
  28. struct pwrseq_pcie_m2_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
  29. return regulator_bulk_enable(ctx->num_vregs, ctx->regs);
  30. }
  31. static int pwrseq_pcie_m2_m_vregs_disable(struct pwrseq_device *pwrseq)
  32. {
  33. struct pwrseq_pcie_m2_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
  34. return regulator_bulk_disable(ctx->num_vregs, ctx->regs);
  35. }
  36. static const struct pwrseq_unit_data pwrseq_pcie_m2_vregs_unit_data = {
  37. .name = "regulators-enable",
  38. .enable = pwrseq_pcie_m2_m_vregs_enable,
  39. .disable = pwrseq_pcie_m2_m_vregs_disable,
  40. };
  41. static const struct pwrseq_unit_data *pwrseq_pcie_m2_m_unit_deps[] = {
  42. &pwrseq_pcie_m2_vregs_unit_data,
  43. NULL
  44. };
  45. static const struct pwrseq_unit_data pwrseq_pcie_m2_m_pcie_unit_data = {
  46. .name = "pcie-enable",
  47. .deps = pwrseq_pcie_m2_m_unit_deps,
  48. };
  49. static const struct pwrseq_target_data pwrseq_pcie_m2_m_pcie_target_data = {
  50. .name = "pcie",
  51. .unit = &pwrseq_pcie_m2_m_pcie_unit_data,
  52. };
  53. static const struct pwrseq_target_data *pwrseq_pcie_m2_m_targets[] = {
  54. &pwrseq_pcie_m2_m_pcie_target_data,
  55. NULL
  56. };
  57. static const struct pwrseq_pcie_m2_pdata pwrseq_pcie_m2_m_of_data = {
  58. .targets = pwrseq_pcie_m2_m_targets,
  59. };
  60. static int pwrseq_pcie_m2_match(struct pwrseq_device *pwrseq,
  61. struct device *dev)
  62. {
  63. struct pwrseq_pcie_m2_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
  64. struct device_node *endpoint __free(device_node) = NULL;
  65. /*
  66. * Traverse the 'remote-endpoint' nodes and check if the remote node's
  67. * parent matches the OF node of 'dev'.
  68. */
  69. for_each_endpoint_of_node(ctx->of_node, endpoint) {
  70. struct device_node *remote __free(device_node) =
  71. of_graph_get_remote_port_parent(endpoint);
  72. if (remote && (remote == dev_of_node(dev)))
  73. return PWRSEQ_MATCH_OK;
  74. }
  75. return PWRSEQ_NO_MATCH;
  76. }
  77. static void pwrseq_pcie_m2_free_regulators(void *data)
  78. {
  79. struct pwrseq_pcie_m2_ctx *ctx = data;
  80. regulator_bulk_free(ctx->num_vregs, ctx->regs);
  81. }
  82. static int pwrseq_pcie_m2_probe(struct platform_device *pdev)
  83. {
  84. struct device *dev = &pdev->dev;
  85. struct pwrseq_pcie_m2_ctx *ctx;
  86. struct pwrseq_config config = {};
  87. int ret;
  88. ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
  89. if (!ctx)
  90. return -ENOMEM;
  91. ctx->of_node = dev_of_node(dev);
  92. ctx->pdata = device_get_match_data(dev);
  93. if (!ctx->pdata)
  94. return dev_err_probe(dev, -ENODEV,
  95. "Failed to obtain platform data\n");
  96. /*
  97. * Currently, of_regulator_bulk_get_all() is the only regulator API that
  98. * allows to get all supplies in the devicetree node without manually
  99. * specifying them.
  100. */
  101. ret = of_regulator_bulk_get_all(dev, dev_of_node(dev), &ctx->regs);
  102. if (ret < 0)
  103. return dev_err_probe(dev, ret,
  104. "Failed to get all regulators\n");
  105. ctx->num_vregs = ret;
  106. ret = devm_add_action_or_reset(dev, pwrseq_pcie_m2_free_regulators, ctx);
  107. if (ret)
  108. return ret;
  109. config.parent = dev;
  110. config.owner = THIS_MODULE;
  111. config.drvdata = ctx;
  112. config.match = pwrseq_pcie_m2_match;
  113. config.targets = ctx->pdata->targets;
  114. ctx->pwrseq = devm_pwrseq_device_register(dev, &config);
  115. if (IS_ERR(ctx->pwrseq))
  116. return dev_err_probe(dev, PTR_ERR(ctx->pwrseq),
  117. "Failed to register the power sequencer\n");
  118. return 0;
  119. }
  120. static const struct of_device_id pwrseq_pcie_m2_of_match[] = {
  121. {
  122. .compatible = "pcie-m2-m-connector",
  123. .data = &pwrseq_pcie_m2_m_of_data,
  124. },
  125. { }
  126. };
  127. MODULE_DEVICE_TABLE(of, pwrseq_pcie_m2_of_match);
  128. static struct platform_driver pwrseq_pcie_m2_driver = {
  129. .driver = {
  130. .name = "pwrseq-pcie-m2",
  131. .of_match_table = pwrseq_pcie_m2_of_match,
  132. },
  133. .probe = pwrseq_pcie_m2_probe,
  134. };
  135. module_platform_driver(pwrseq_pcie_m2_driver);
  136. MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>");
  137. MODULE_DESCRIPTION("Power Sequencing driver for PCIe M.2 connector");
  138. MODULE_LICENSE("GPL");