clk-opp.c 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /* Copyright (c) 2020, The Linux Foundation. All rights reserved. */
  3. #include <linux/clk-provider.h>
  4. #include <linux/kernel.h>
  5. #include <linux/device.h>
  6. #include <linux/of_platform.h>
  7. #include <linux/pm_opp.h>
  8. #include <linux/slab.h>
  9. #include "clk-opp.h"
  10. #include "clk-regmap.h"
  11. static int derive_device_list(struct device **device_list,
  12. struct clk_hw *hw,
  13. struct device_node *np,
  14. char *clk_handle_name, int count)
  15. {
  16. struct platform_device *pdev;
  17. struct device_node *dev_node;
  18. int j;
  19. for (j = 0; j < count; j++) {
  20. device_list[j] = NULL;
  21. dev_node = of_parse_phandle(np, clk_handle_name, j);
  22. if (!dev_node) {
  23. pr_err("Unable to get device_node pointer for %s opp-handle (%s)\n",
  24. clk_hw_get_name(hw), clk_handle_name);
  25. return -ENODEV;
  26. }
  27. pdev = of_find_device_by_node(dev_node);
  28. if (!pdev) {
  29. pr_err("Unable to find platform_device node for %s opp-handle\n",
  30. clk_hw_get_name(hw));
  31. return -ENODEV;
  32. }
  33. device_list[j] = &pdev->dev;
  34. }
  35. return 0;
  36. }
  37. static int clk_get_voltage(struct clk_hw *hw, unsigned long rate, int n)
  38. {
  39. struct clk_regmap *rclk = to_clk_regmap(hw);
  40. struct clk_vdd_class_data *vdd_data = &rclk->vdd_data;
  41. struct clk_vdd_class *vdd;
  42. int level, corner;
  43. /* Use the first regulator in the vdd class for the OPP table. */
  44. vdd = rclk->vdd_data.vdd_class;
  45. if (vdd->num_regulators > 1) {
  46. corner = vdd->vdd_uv[vdd->num_regulators * n];
  47. } else {
  48. level = clk_find_vdd_level(hw, vdd_data, rate);
  49. if (level < 0) {
  50. pr_err("Could not find vdd level\n");
  51. return -EINVAL;
  52. }
  53. corner = vdd->vdd_uv[level];
  54. }
  55. if (!corner) {
  56. pr_err("%s: Unable to find vdd level for rate %lu\n",
  57. clk_hw_get_name(hw), rate);
  58. return -EINVAL;
  59. }
  60. return corner;
  61. }
  62. static int clk_add_and_print_opp(struct clk_hw *hw,
  63. struct device **device_list, int count,
  64. unsigned long rate, int uv, int n)
  65. {
  66. int j, ret;
  67. for (j = 0; j < count; j++) {
  68. ret = dev_pm_opp_add(device_list[j], rate, uv);
  69. if (ret) {
  70. pr_err("%s: couldn't add OPP for %lu - err: %d\n",
  71. clk_hw_get_name(hw), rate, ret);
  72. return ret;
  73. }
  74. pr_info("%s: set OPP pair(%lu Hz: %u uV) on %s\n",
  75. clk_hw_get_name(hw), rate, uv,
  76. dev_name(device_list[j]));
  77. }
  78. return 0;
  79. }
  80. void clk_hw_populate_clock_opp_table(struct device_node *np, struct clk_hw *hw)
  81. {
  82. struct device **device_list;
  83. struct clk_regmap *rclk = to_clk_regmap(hw);
  84. struct clk_vdd_class_data *vdd_data;
  85. char clk_handle_name[MAX_LEN_OPP_HANDLE];
  86. int n, len, count, uv, ret;
  87. unsigned long rate = 0, rrate;
  88. if (!rclk->vdd_data.vdd_class || !rclk->vdd_data.num_rate_max)
  89. return;
  90. if (strlen(clk_hw_get_name(hw)) + LEN_OPP_HANDLE < MAX_LEN_OPP_HANDLE) {
  91. ret = scnprintf(clk_handle_name, ARRAY_SIZE(clk_handle_name),
  92. "qcom,%s-opp-handle", clk_hw_get_name(hw));
  93. if (ret < strlen(clk_hw_get_name(hw)) + LEN_OPP_HANDLE) {
  94. pr_err("%s: Failed to hold clk_handle_name\n",
  95. clk_hw_get_name(hw));
  96. return;
  97. }
  98. } else {
  99. pr_err("clk name (%s) too large to fit in clk_handle_name\n",
  100. clk_hw_get_name(hw));
  101. return;
  102. }
  103. if (of_find_property(np, clk_handle_name, &len)) {
  104. count = len/sizeof(u32);
  105. device_list = kmalloc_array(count, sizeof(struct device *),
  106. GFP_KERNEL);
  107. if (!device_list)
  108. return;
  109. ret = derive_device_list(device_list, hw, np,
  110. clk_handle_name, count);
  111. if (ret < 0) {
  112. pr_err("Failed to fill device_list for %s\n",
  113. clk_handle_name);
  114. goto err_derive_device_list;
  115. }
  116. } else {
  117. pr_debug("Unable to find %s\n", clk_handle_name);
  118. return;
  119. }
  120. vdd_data = &rclk->vdd_data;
  121. for (n = 0; n < vdd_data->num_rate_max; n++) {
  122. rrate = clk_hw_round_rate(hw, rate + 1);
  123. if (!rrate) {
  124. /*
  125. * If the parent is not ready before this clock,
  126. * most likely the round rate would fail.
  127. */
  128. pr_err("clk_hw_round_rate failed for %s\n",
  129. clk_hw_get_name(hw));
  130. goto err_derive_device_list;
  131. }
  132. pr_debug("Rate %lu , uv %d, rrate %lu\n", rate + 1, uv,
  133. clk_hw_round_rate(hw, rate + 1));
  134. /*
  135. * If clk_hw_round_rate gives the same value on consecutive
  136. * iterations, exit the loop since we're at the maximum clock
  137. * frequency.
  138. */
  139. if (rate == rrate)
  140. break;
  141. rate = rrate;
  142. uv = clk_get_voltage(hw, rate, n);
  143. if (uv < 0)
  144. goto err_derive_device_list;
  145. ret = clk_add_and_print_opp(hw, device_list, count,
  146. rate, uv, n);
  147. if (ret)
  148. pr_err("Failed to add OPP table\n");
  149. }
  150. err_derive_device_list:
  151. kfree(device_list);
  152. }
  153. EXPORT_SYMBOL(clk_hw_populate_clock_opp_table);