sti-cpufreq.c 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  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. return sti_stih407_dvfs_regfields;
  129. return NULL;
  130. }
  131. static int sti_cpufreq_set_opp_info(void)
  132. {
  133. struct device *dev = ddata.cpu;
  134. struct device_node *np = dev->of_node;
  135. const struct reg_field *reg_fields;
  136. unsigned int hw_info_offset;
  137. unsigned int version[VERSION_ELEMENTS];
  138. int pcode, substrate, major, minor;
  139. int ret;
  140. char name[MAX_PCODE_NAME_LEN];
  141. struct opp_table *opp_table;
  142. reg_fields = sti_cpufreq_match();
  143. if (!reg_fields) {
  144. dev_err(dev, "This SoC doesn't support voltage scaling\n");
  145. return -ENODEV;
  146. }
  147. ret = of_property_read_u32_index(np, "st,syscfg-eng",
  148. HW_INFO_INDEX, &hw_info_offset);
  149. if (ret) {
  150. dev_warn(dev, "Failed to read HW info offset from DT\n");
  151. substrate = DEFAULT_VERSION;
  152. pcode = 0;
  153. goto use_defaults;
  154. }
  155. pcode = sti_cpufreq_fetch_regmap_field(reg_fields,
  156. hw_info_offset,
  157. PCODE);
  158. if (pcode < 0) {
  159. dev_warn(dev, "Failed to obtain process code\n");
  160. /* Use default pcode */
  161. pcode = 0;
  162. }
  163. substrate = sti_cpufreq_fetch_regmap_field(reg_fields,
  164. hw_info_offset,
  165. SUBSTRATE);
  166. if (substrate) {
  167. dev_warn(dev, "Failed to obtain substrate code\n");
  168. /* Use default substrate */
  169. substrate = DEFAULT_VERSION;
  170. }
  171. use_defaults:
  172. major = sti_cpufreq_fetch_major();
  173. if (major < 0) {
  174. dev_err(dev, "Failed to obtain major version\n");
  175. /* Use default major number */
  176. major = DEFAULT_VERSION;
  177. }
  178. minor = sti_cpufreq_fetch_minor();
  179. if (minor < 0) {
  180. dev_err(dev, "Failed to obtain minor version\n");
  181. /* Use default minor number */
  182. minor = DEFAULT_VERSION;
  183. }
  184. snprintf(name, MAX_PCODE_NAME_LEN, "pcode%d", pcode);
  185. opp_table = dev_pm_opp_set_prop_name(dev, name);
  186. if (IS_ERR(opp_table)) {
  187. dev_err(dev, "Failed to set prop name\n");
  188. return PTR_ERR(opp_table);
  189. }
  190. version[0] = BIT(major);
  191. version[1] = BIT(minor);
  192. version[2] = BIT(substrate);
  193. opp_table = dev_pm_opp_set_supported_hw(dev, version, VERSION_ELEMENTS);
  194. if (IS_ERR(opp_table)) {
  195. dev_err(dev, "Failed to set supported hardware\n");
  196. return PTR_ERR(opp_table);
  197. }
  198. dev_dbg(dev, "pcode: %d major: %d minor: %d substrate: %d\n",
  199. pcode, major, minor, substrate);
  200. dev_dbg(dev, "version[0]: %x version[1]: %x version[2]: %x\n",
  201. version[0], version[1], version[2]);
  202. return 0;
  203. }
  204. static int sti_cpufreq_fetch_syscon_registers(void)
  205. {
  206. struct device *dev = ddata.cpu;
  207. struct device_node *np = dev->of_node;
  208. ddata.syscfg = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
  209. if (IS_ERR(ddata.syscfg)) {
  210. dev_err(dev, "\"st,syscfg\" not supplied\n");
  211. return PTR_ERR(ddata.syscfg);
  212. }
  213. ddata.syscfg_eng = syscon_regmap_lookup_by_phandle(np, "st,syscfg-eng");
  214. if (IS_ERR(ddata.syscfg_eng)) {
  215. dev_err(dev, "\"st,syscfg-eng\" not supplied\n");
  216. return PTR_ERR(ddata.syscfg_eng);
  217. }
  218. return 0;
  219. }
  220. static int sti_cpufreq_init(void)
  221. {
  222. int ret;
  223. if ((!of_machine_is_compatible("st,stih407")) &&
  224. (!of_machine_is_compatible("st,stih410")))
  225. return -ENODEV;
  226. ddata.cpu = get_cpu_device(0);
  227. if (!ddata.cpu) {
  228. dev_err(ddata.cpu, "Failed to get device for CPU0\n");
  229. goto skip_voltage_scaling;
  230. }
  231. if (!of_get_property(ddata.cpu->of_node, "operating-points-v2", NULL)) {
  232. dev_err(ddata.cpu, "OPP-v2 not supported\n");
  233. goto skip_voltage_scaling;
  234. }
  235. ret = sti_cpufreq_fetch_syscon_registers();
  236. if (ret)
  237. goto skip_voltage_scaling;
  238. ret = sti_cpufreq_set_opp_info();
  239. if (!ret)
  240. goto register_cpufreq_dt;
  241. skip_voltage_scaling:
  242. dev_err(ddata.cpu, "Not doing voltage scaling\n");
  243. register_cpufreq_dt:
  244. platform_device_register_simple("cpufreq-dt", -1, NULL, 0);
  245. return 0;
  246. }
  247. module_init(sti_cpufreq_init);
  248. MODULE_DESCRIPTION("STMicroelectronics CPUFreq/OPP driver");
  249. MODULE_AUTHOR("Ajitpal Singh <ajitpal.singh@st.com>");
  250. MODULE_AUTHOR("Lee Jones <lee.jones@linaro.org>");
  251. MODULE_LICENSE("GPL v2");