sti-cpufreq.c 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Match running platform with pre-defined OPP values for CPUFreq
  4. *
  5. * Author: Ajit Pal Singh <ajitpal.singh@st.com>
  6. * Lee Jones <lee.jones@linaro.org>
  7. *
  8. * Copyright (C) 2015 STMicroelectronics (R&D) Limited
  9. */
  10. #include <linux/cpu.h>
  11. #include <linux/io.h>
  12. #include <linux/mfd/syscon.h>
  13. #include <linux/module.h>
  14. #include <linux/of.h>
  15. #include <linux/of_platform.h>
  16. #include <linux/pm_opp.h>
  17. #include <linux/regmap.h>
  18. #define VERSION_ELEMENTS 3
  19. #define MAX_PCODE_NAME_LEN 7
  20. #define VERSION_SHIFT 28
  21. #define HW_INFO_INDEX 1
  22. #define MAJOR_ID_INDEX 1
  23. #define MINOR_ID_INDEX 2
  24. /*
  25. * Only match on "suitable for ALL versions" entries
  26. *
  27. * This will be used with the BIT() macro. It sets the
  28. * top bit of a 32bit value and is equal to 0x80000000.
  29. */
  30. #define DEFAULT_VERSION 31
  31. enum {
  32. PCODE = 0,
  33. SUBSTRATE,
  34. DVFS_MAX_REGFIELDS,
  35. };
  36. /**
  37. * struct sti_cpufreq_ddata - ST CPUFreq Driver Data
  38. *
  39. * @cpu: CPU's OF node
  40. * @syscfg_eng: Engineering Syscon register map
  41. * @syscfg: Syscon register map
  42. */
  43. static struct sti_cpufreq_ddata {
  44. struct device *cpu;
  45. struct regmap *syscfg_eng;
  46. struct regmap *syscfg;
  47. } ddata;
  48. static int sti_cpufreq_fetch_major(void) {
  49. struct device_node *np = ddata.cpu->of_node;
  50. struct device *dev = ddata.cpu;
  51. unsigned int major_offset;
  52. unsigned int socid;
  53. int ret;
  54. ret = of_property_read_u32_index(np, "st,syscfg",
  55. MAJOR_ID_INDEX, &major_offset);
  56. if (ret) {
  57. dev_err(dev, "No major number offset provided in %pOF [%d]\n",
  58. np, ret);
  59. return ret;
  60. }
  61. ret = regmap_read(ddata.syscfg, major_offset, &socid);
  62. if (ret) {
  63. dev_err(dev, "Failed to read major number from syscon [%d]\n",
  64. ret);
  65. return ret;
  66. }
  67. return ((socid >> VERSION_SHIFT) & 0xf) + 1;
  68. }
  69. static int sti_cpufreq_fetch_minor(void)
  70. {
  71. struct device *dev = ddata.cpu;
  72. struct device_node *np = dev->of_node;
  73. unsigned int minor_offset;
  74. unsigned int minid;
  75. int ret;
  76. ret = of_property_read_u32_index(np, "st,syscfg-eng",
  77. MINOR_ID_INDEX, &minor_offset);
  78. if (ret) {
  79. dev_err(dev,
  80. "No minor number offset provided %pOF [%d]\n",
  81. np, ret);
  82. return ret;
  83. }
  84. ret = regmap_read(ddata.syscfg_eng, minor_offset, &minid);
  85. if (ret) {
  86. dev_err(dev,
  87. "Failed to read the minor number from syscon [%d]\n",
  88. ret);
  89. return ret;
  90. }
  91. return minid & 0xf;
  92. }
  93. static int sti_cpufreq_fetch_regmap_field(const struct reg_field *reg_fields,
  94. int hw_info_offset, int field)
  95. {
  96. struct regmap_field *regmap_field;
  97. struct reg_field reg_field = reg_fields[field];
  98. struct device *dev = ddata.cpu;
  99. unsigned int value;
  100. int ret;
  101. reg_field.reg = hw_info_offset;
  102. regmap_field = devm_regmap_field_alloc(dev,
  103. ddata.syscfg_eng,
  104. reg_field);
  105. if (IS_ERR(regmap_field)) {
  106. dev_err(dev, "Failed to allocate reg field\n");
  107. return PTR_ERR(regmap_field);
  108. }
  109. ret = regmap_field_read(regmap_field, &value);
  110. if (ret) {
  111. dev_err(dev, "Failed to read %s code\n",
  112. field ? "SUBSTRATE" : "PCODE");
  113. return ret;
  114. }
  115. return value;
  116. }
  117. static const struct reg_field sti_stih407_dvfs_regfields[DVFS_MAX_REGFIELDS] = {
  118. [PCODE] = REG_FIELD(0, 16, 19),
  119. [SUBSTRATE] = REG_FIELD(0, 0, 2),
  120. };
  121. static const struct reg_field *sti_cpufreq_match(void)
  122. {
  123. if (of_machine_is_compatible("st,stih407") ||
  124. of_machine_is_compatible("st,stih410") ||
  125. of_machine_is_compatible("st,stih418"))
  126. return sti_stih407_dvfs_regfields;
  127. return NULL;
  128. }
  129. static int sti_cpufreq_set_opp_info(void)
  130. {
  131. struct device *dev = ddata.cpu;
  132. struct device_node *np = dev->of_node;
  133. const struct reg_field *reg_fields;
  134. unsigned int hw_info_offset;
  135. unsigned int version[VERSION_ELEMENTS];
  136. int pcode, substrate, major, minor;
  137. int ret;
  138. char name[MAX_PCODE_NAME_LEN];
  139. struct opp_table *opp_table;
  140. reg_fields = sti_cpufreq_match();
  141. if (!reg_fields) {
  142. dev_err(dev, "This SoC doesn't support voltage scaling\n");
  143. return -ENODEV;
  144. }
  145. ret = of_property_read_u32_index(np, "st,syscfg-eng",
  146. HW_INFO_INDEX, &hw_info_offset);
  147. if (ret) {
  148. dev_warn(dev, "Failed to read HW info offset from DT\n");
  149. substrate = DEFAULT_VERSION;
  150. pcode = 0;
  151. goto use_defaults;
  152. }
  153. pcode = sti_cpufreq_fetch_regmap_field(reg_fields,
  154. hw_info_offset,
  155. PCODE);
  156. if (pcode < 0) {
  157. dev_warn(dev, "Failed to obtain process code\n");
  158. /* Use default pcode */
  159. pcode = 0;
  160. }
  161. substrate = sti_cpufreq_fetch_regmap_field(reg_fields,
  162. hw_info_offset,
  163. SUBSTRATE);
  164. if (substrate) {
  165. dev_warn(dev, "Failed to obtain substrate code\n");
  166. /* Use default substrate */
  167. substrate = DEFAULT_VERSION;
  168. }
  169. use_defaults:
  170. major = sti_cpufreq_fetch_major();
  171. if (major < 0) {
  172. dev_err(dev, "Failed to obtain major version\n");
  173. /* Use default major number */
  174. major = DEFAULT_VERSION;
  175. }
  176. minor = sti_cpufreq_fetch_minor();
  177. if (minor < 0) {
  178. dev_err(dev, "Failed to obtain minor version\n");
  179. /* Use default minor number */
  180. minor = DEFAULT_VERSION;
  181. }
  182. snprintf(name, MAX_PCODE_NAME_LEN, "pcode%d", pcode);
  183. opp_table = dev_pm_opp_set_prop_name(dev, name);
  184. if (IS_ERR(opp_table)) {
  185. dev_err(dev, "Failed to set prop name\n");
  186. return PTR_ERR(opp_table);
  187. }
  188. version[0] = BIT(major);
  189. version[1] = BIT(minor);
  190. version[2] = BIT(substrate);
  191. opp_table = dev_pm_opp_set_supported_hw(dev, version, VERSION_ELEMENTS);
  192. if (IS_ERR(opp_table)) {
  193. dev_err(dev, "Failed to set supported hardware\n");
  194. return PTR_ERR(opp_table);
  195. }
  196. dev_dbg(dev, "pcode: %d major: %d minor: %d substrate: %d\n",
  197. pcode, major, minor, substrate);
  198. dev_dbg(dev, "version[0]: %x version[1]: %x version[2]: %x\n",
  199. version[0], version[1], version[2]);
  200. return 0;
  201. }
  202. static int sti_cpufreq_fetch_syscon_registers(void)
  203. {
  204. struct device *dev = ddata.cpu;
  205. struct device_node *np = dev->of_node;
  206. ddata.syscfg = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
  207. if (IS_ERR(ddata.syscfg)) {
  208. dev_err(dev, "\"st,syscfg\" not supplied\n");
  209. return PTR_ERR(ddata.syscfg);
  210. }
  211. ddata.syscfg_eng = syscon_regmap_lookup_by_phandle(np, "st,syscfg-eng");
  212. if (IS_ERR(ddata.syscfg_eng)) {
  213. dev_err(dev, "\"st,syscfg-eng\" not supplied\n");
  214. return PTR_ERR(ddata.syscfg_eng);
  215. }
  216. return 0;
  217. }
  218. static int sti_cpufreq_init(void)
  219. {
  220. int ret;
  221. if ((!of_machine_is_compatible("st,stih407")) &&
  222. (!of_machine_is_compatible("st,stih410")) &&
  223. (!of_machine_is_compatible("st,stih418")))
  224. return -ENODEV;
  225. ddata.cpu = get_cpu_device(0);
  226. if (!ddata.cpu) {
  227. dev_err(ddata.cpu, "Failed to get device for CPU0\n");
  228. goto skip_voltage_scaling;
  229. }
  230. if (!of_get_property(ddata.cpu->of_node, "operating-points-v2", NULL)) {
  231. dev_err(ddata.cpu, "OPP-v2 not supported\n");
  232. goto skip_voltage_scaling;
  233. }
  234. ret = sti_cpufreq_fetch_syscon_registers();
  235. if (ret)
  236. goto skip_voltage_scaling;
  237. ret = sti_cpufreq_set_opp_info();
  238. if (!ret)
  239. goto register_cpufreq_dt;
  240. skip_voltage_scaling:
  241. dev_err(ddata.cpu, "Not doing voltage scaling\n");
  242. register_cpufreq_dt:
  243. platform_device_register_simple("cpufreq-dt", -1, NULL, 0);
  244. return 0;
  245. }
  246. module_init(sti_cpufreq_init);
  247. static const struct of_device_id __maybe_unused sti_cpufreq_of_match[] = {
  248. { .compatible = "st,stih407" },
  249. { .compatible = "st,stih410" },
  250. { },
  251. };
  252. MODULE_DEVICE_TABLE(of, sti_cpufreq_of_match);
  253. MODULE_DESCRIPTION("STMicroelectronics CPUFreq/OPP driver");
  254. MODULE_AUTHOR("Ajitpal Singh <ajitpal.singh@st.com>");
  255. MODULE_AUTHOR("Lee Jones <lee.jones@linaro.org>");
  256. MODULE_LICENSE("GPL v2");