sti-cpufreq.c 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  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 %s [%d]\n",
  61. np->full_name, 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 %s [%d]\n",
  84. np->full_name, 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. reg_fields = sti_cpufreq_match();
  142. if (!reg_fields) {
  143. dev_err(dev, "This SoC doesn't support voltage scaling\n");
  144. return -ENODEV;
  145. }
  146. ret = of_property_read_u32_index(np, "st,syscfg-eng",
  147. HW_INFO_INDEX, &hw_info_offset);
  148. if (ret) {
  149. dev_warn(dev, "Failed to read HW info offset from DT\n");
  150. substrate = DEFAULT_VERSION;
  151. pcode = 0;
  152. goto use_defaults;
  153. }
  154. pcode = sti_cpufreq_fetch_regmap_field(reg_fields,
  155. hw_info_offset,
  156. PCODE);
  157. if (pcode < 0) {
  158. dev_warn(dev, "Failed to obtain process code\n");
  159. /* Use default pcode */
  160. pcode = 0;
  161. }
  162. substrate = sti_cpufreq_fetch_regmap_field(reg_fields,
  163. hw_info_offset,
  164. SUBSTRATE);
  165. if (substrate) {
  166. dev_warn(dev, "Failed to obtain substrate code\n");
  167. /* Use default substrate */
  168. substrate = DEFAULT_VERSION;
  169. }
  170. use_defaults:
  171. major = sti_cpufreq_fetch_major();
  172. if (major < 0) {
  173. dev_err(dev, "Failed to obtain major version\n");
  174. /* Use default major number */
  175. major = DEFAULT_VERSION;
  176. }
  177. minor = sti_cpufreq_fetch_minor();
  178. if (minor < 0) {
  179. dev_err(dev, "Failed to obtain minor version\n");
  180. /* Use default minor number */
  181. minor = DEFAULT_VERSION;
  182. }
  183. snprintf(name, MAX_PCODE_NAME_LEN, "pcode%d", pcode);
  184. ret = dev_pm_opp_set_prop_name(dev, name);
  185. if (ret) {
  186. dev_err(dev, "Failed to set prop name\n");
  187. return ret;
  188. }
  189. version[0] = BIT(major);
  190. version[1] = BIT(minor);
  191. version[2] = BIT(substrate);
  192. ret = dev_pm_opp_set_supported_hw(dev, version, VERSION_ELEMENTS);
  193. if (ret) {
  194. dev_err(dev, "Failed to set supported hardware\n");
  195. return ret;
  196. }
  197. dev_dbg(dev, "pcode: %d major: %d minor: %d substrate: %d\n",
  198. pcode, major, minor, substrate);
  199. dev_dbg(dev, "version[0]: %x version[1]: %x version[2]: %x\n",
  200. version[0], version[1], version[2]);
  201. return 0;
  202. }
  203. static int sti_cpufreq_fetch_syscon_regsiters(void)
  204. {
  205. struct device *dev = ddata.cpu;
  206. struct device_node *np = dev->of_node;
  207. ddata.syscfg = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
  208. if (IS_ERR(ddata.syscfg)) {
  209. dev_err(dev, "\"st,syscfg\" not supplied\n");
  210. return PTR_ERR(ddata.syscfg);
  211. }
  212. ddata.syscfg_eng = syscon_regmap_lookup_by_phandle(np, "st,syscfg-eng");
  213. if (IS_ERR(ddata.syscfg_eng)) {
  214. dev_err(dev, "\"st,syscfg-eng\" not supplied\n");
  215. return PTR_ERR(ddata.syscfg_eng);
  216. }
  217. return 0;
  218. }
  219. static int sti_cpufreq_init(void)
  220. {
  221. int ret;
  222. if ((!of_machine_is_compatible("st,stih407")) &&
  223. (!of_machine_is_compatible("st,stih410")))
  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_regsiters();
  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. MODULE_DESCRIPTION("STMicroelectronics CPUFreq/OPP driver");
  248. MODULE_AUTHOR("Ajitpal Singh <ajitpal.singh@st.com>");
  249. MODULE_AUTHOR("Lee Jones <lee.jones@linaro.org>");
  250. MODULE_LICENSE("GPL v2");