exynos-asv.c 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright (c) 2019 Samsung Electronics Co., Ltd.
  4. * http://www.samsung.com/
  5. * Copyright (c) 2020 Krzysztof Kozlowski <[email protected]>
  6. * Author: Sylwester Nawrocki <[email protected]>
  7. * Author: Krzysztof Kozlowski <[email protected]>
  8. *
  9. * Samsung Exynos SoC Adaptive Supply Voltage support
  10. */
  11. #include <linux/cpu.h>
  12. #include <linux/device.h>
  13. #include <linux/errno.h>
  14. #include <linux/of.h>
  15. #include <linux/pm_opp.h>
  16. #include <linux/regmap.h>
  17. #include <linux/soc/samsung/exynos-chipid.h>
  18. #include "exynos-asv.h"
  19. #include "exynos5422-asv.h"
  20. #define MHZ 1000000U
  21. static int exynos_asv_update_cpu_opps(struct exynos_asv *asv,
  22. struct device *cpu)
  23. {
  24. struct exynos_asv_subsys *subsys = NULL;
  25. struct dev_pm_opp *opp;
  26. unsigned int opp_freq;
  27. int i;
  28. for (i = 0; i < ARRAY_SIZE(asv->subsys); i++) {
  29. if (of_device_is_compatible(cpu->of_node,
  30. asv->subsys[i].cpu_dt_compat)) {
  31. subsys = &asv->subsys[i];
  32. break;
  33. }
  34. }
  35. if (!subsys)
  36. return -EINVAL;
  37. for (i = 0; i < subsys->table.num_rows; i++) {
  38. unsigned int new_volt, volt;
  39. int ret;
  40. opp_freq = exynos_asv_opp_get_frequency(subsys, i);
  41. opp = dev_pm_opp_find_freq_exact(cpu, opp_freq * MHZ, true);
  42. if (IS_ERR(opp)) {
  43. dev_info(asv->dev, "cpu%d opp%d, freq: %u missing\n",
  44. cpu->id, i, opp_freq);
  45. continue;
  46. }
  47. volt = dev_pm_opp_get_voltage(opp);
  48. new_volt = asv->opp_get_voltage(subsys, i, volt);
  49. dev_pm_opp_put(opp);
  50. if (new_volt == volt)
  51. continue;
  52. ret = dev_pm_opp_adjust_voltage(cpu, opp_freq * MHZ,
  53. new_volt, new_volt, new_volt);
  54. if (ret < 0)
  55. dev_err(asv->dev,
  56. "Failed to adjust OPP %u Hz/%u uV for cpu%d\n",
  57. opp_freq, new_volt, cpu->id);
  58. else
  59. dev_dbg(asv->dev,
  60. "Adjusted OPP %u Hz/%u -> %u uV, cpu%d\n",
  61. opp_freq, volt, new_volt, cpu->id);
  62. }
  63. return 0;
  64. }
  65. static int exynos_asv_update_opps(struct exynos_asv *asv)
  66. {
  67. struct opp_table *last_opp_table = NULL;
  68. struct device *cpu;
  69. int ret, cpuid;
  70. for_each_possible_cpu(cpuid) {
  71. struct opp_table *opp_table;
  72. cpu = get_cpu_device(cpuid);
  73. if (!cpu)
  74. continue;
  75. opp_table = dev_pm_opp_get_opp_table(cpu);
  76. if (IS_ERR(opp_table))
  77. continue;
  78. if (!last_opp_table || opp_table != last_opp_table) {
  79. last_opp_table = opp_table;
  80. ret = exynos_asv_update_cpu_opps(asv, cpu);
  81. if (ret < 0)
  82. dev_err(asv->dev, "Couldn't udate OPPs for cpu%d\n",
  83. cpuid);
  84. }
  85. dev_pm_opp_put_opp_table(opp_table);
  86. }
  87. return 0;
  88. }
  89. int exynos_asv_init(struct device *dev, struct regmap *regmap)
  90. {
  91. int (*probe_func)(struct exynos_asv *asv);
  92. struct exynos_asv *asv;
  93. struct device *cpu_dev;
  94. u32 product_id = 0;
  95. int ret, i;
  96. asv = devm_kzalloc(dev, sizeof(*asv), GFP_KERNEL);
  97. if (!asv)
  98. return -ENOMEM;
  99. asv->chipid_regmap = regmap;
  100. asv->dev = dev;
  101. ret = regmap_read(asv->chipid_regmap, EXYNOS_CHIPID_REG_PRO_ID,
  102. &product_id);
  103. if (ret < 0) {
  104. dev_err(dev, "Cannot read revision from ChipID: %d\n", ret);
  105. return -ENODEV;
  106. }
  107. switch (product_id & EXYNOS_MASK) {
  108. case 0xE5422000:
  109. probe_func = exynos5422_asv_init;
  110. break;
  111. default:
  112. dev_dbg(dev, "No ASV support for this SoC\n");
  113. devm_kfree(dev, asv);
  114. return 0;
  115. }
  116. cpu_dev = get_cpu_device(0);
  117. ret = dev_pm_opp_get_opp_count(cpu_dev);
  118. if (ret < 0)
  119. return -EPROBE_DEFER;
  120. ret = of_property_read_u32(dev->of_node, "samsung,asv-bin",
  121. &asv->of_bin);
  122. if (ret < 0)
  123. asv->of_bin = -EINVAL;
  124. for (i = 0; i < ARRAY_SIZE(asv->subsys); i++)
  125. asv->subsys[i].asv = asv;
  126. ret = probe_func(asv);
  127. if (ret < 0)
  128. return ret;
  129. return exynos_asv_update_opps(asv);
  130. }