sti-cpufreq.c 7.4 KB

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