cpm.c 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * PowerPC 4xx Clock and Power Management
  4. *
  5. * Copyright (C) 2010, Applied Micro Circuits Corporation
  6. * Victor Gallardo ([email protected])
  7. *
  8. * Based on arch/powerpc/platforms/44x/idle.c:
  9. * Jerone Young <[email protected]>
  10. * Copyright 2008 IBM Corp.
  11. *
  12. * Based on arch/powerpc/sysdev/fsl_pmc.c:
  13. * Anton Vorontsov <[email protected]>
  14. * Copyright 2009 MontaVista Software, Inc.
  15. *
  16. * See file CREDITS for list of people who contributed to this
  17. * project.
  18. */
  19. #include <linux/kernel.h>
  20. #include <linux/of_platform.h>
  21. #include <linux/sysfs.h>
  22. #include <linux/cpu.h>
  23. #include <linux/suspend.h>
  24. #include <asm/dcr.h>
  25. #include <asm/dcr-native.h>
  26. #include <asm/machdep.h>
  27. #define CPM_ER 0
  28. #define CPM_FR 1
  29. #define CPM_SR 2
  30. #define CPM_IDLE_WAIT 0
  31. #define CPM_IDLE_DOZE 1
  32. struct cpm {
  33. dcr_host_t dcr_host;
  34. unsigned int dcr_offset[3];
  35. unsigned int powersave_off;
  36. unsigned int unused;
  37. unsigned int idle_doze;
  38. unsigned int standby;
  39. unsigned int suspend;
  40. };
  41. static struct cpm cpm;
  42. struct cpm_idle_mode {
  43. unsigned int enabled;
  44. const char *name;
  45. };
  46. static struct cpm_idle_mode idle_mode[] = {
  47. [CPM_IDLE_WAIT] = { 1, "wait" }, /* default */
  48. [CPM_IDLE_DOZE] = { 0, "doze" },
  49. };
  50. static unsigned int cpm_set(unsigned int cpm_reg, unsigned int mask)
  51. {
  52. unsigned int value;
  53. /* CPM controller supports 3 different types of sleep interface
  54. * known as class 1, 2 and 3. For class 1 units, they are
  55. * unconditionally put to sleep when the corresponding CPM bit is
  56. * set. For class 2 and 3 units this is not case; if they can be
  57. * put to sleep, they will. Here we do not verify, we just
  58. * set them and expect them to eventually go off when they can.
  59. */
  60. value = dcr_read(cpm.dcr_host, cpm.dcr_offset[cpm_reg]);
  61. dcr_write(cpm.dcr_host, cpm.dcr_offset[cpm_reg], value | mask);
  62. /* return old state, to restore later if needed */
  63. return value;
  64. }
  65. static void cpm_idle_wait(void)
  66. {
  67. unsigned long msr_save;
  68. /* save off initial state */
  69. msr_save = mfmsr();
  70. /* sync required when CPM0_ER[CPU] is set */
  71. mb();
  72. /* set wait state MSR */
  73. mtmsr(msr_save|MSR_WE|MSR_EE|MSR_CE|MSR_DE);
  74. isync();
  75. /* return to initial state */
  76. mtmsr(msr_save);
  77. isync();
  78. }
  79. static void cpm_idle_sleep(unsigned int mask)
  80. {
  81. unsigned int er_save;
  82. /* update CPM_ER state */
  83. er_save = cpm_set(CPM_ER, mask);
  84. /* go to wait state so that CPM0_ER[CPU] can take effect */
  85. cpm_idle_wait();
  86. /* restore CPM_ER state */
  87. dcr_write(cpm.dcr_host, cpm.dcr_offset[CPM_ER], er_save);
  88. }
  89. static void cpm_idle_doze(void)
  90. {
  91. cpm_idle_sleep(cpm.idle_doze);
  92. }
  93. static void cpm_idle_config(int mode)
  94. {
  95. int i;
  96. if (idle_mode[mode].enabled)
  97. return;
  98. for (i = 0; i < ARRAY_SIZE(idle_mode); i++)
  99. idle_mode[i].enabled = 0;
  100. idle_mode[mode].enabled = 1;
  101. }
  102. static ssize_t cpm_idle_show(struct kobject *kobj,
  103. struct kobj_attribute *attr, char *buf)
  104. {
  105. char *s = buf;
  106. int i;
  107. for (i = 0; i < ARRAY_SIZE(idle_mode); i++) {
  108. if (idle_mode[i].enabled)
  109. s += sprintf(s, "[%s] ", idle_mode[i].name);
  110. else
  111. s += sprintf(s, "%s ", idle_mode[i].name);
  112. }
  113. *(s-1) = '\n'; /* convert the last space to a newline */
  114. return s - buf;
  115. }
  116. static ssize_t cpm_idle_store(struct kobject *kobj,
  117. struct kobj_attribute *attr,
  118. const char *buf, size_t n)
  119. {
  120. int i;
  121. char *p;
  122. int len;
  123. p = memchr(buf, '\n', n);
  124. len = p ? p - buf : n;
  125. for (i = 0; i < ARRAY_SIZE(idle_mode); i++) {
  126. if (strncmp(buf, idle_mode[i].name, len) == 0) {
  127. cpm_idle_config(i);
  128. return n;
  129. }
  130. }
  131. return -EINVAL;
  132. }
  133. static struct kobj_attribute cpm_idle_attr =
  134. __ATTR(idle, 0644, cpm_idle_show, cpm_idle_store);
  135. static void __init cpm_idle_config_sysfs(void)
  136. {
  137. struct device *dev;
  138. unsigned long ret;
  139. dev = get_cpu_device(0);
  140. ret = sysfs_create_file(&dev->kobj,
  141. &cpm_idle_attr.attr);
  142. if (ret)
  143. printk(KERN_WARNING
  144. "cpm: failed to create idle sysfs entry\n");
  145. }
  146. static void cpm_idle(void)
  147. {
  148. if (idle_mode[CPM_IDLE_DOZE].enabled)
  149. cpm_idle_doze();
  150. else
  151. cpm_idle_wait();
  152. }
  153. static int cpm_suspend_valid(suspend_state_t state)
  154. {
  155. switch (state) {
  156. case PM_SUSPEND_STANDBY:
  157. return !!cpm.standby;
  158. case PM_SUSPEND_MEM:
  159. return !!cpm.suspend;
  160. default:
  161. return 0;
  162. }
  163. }
  164. static void cpm_suspend_standby(unsigned int mask)
  165. {
  166. unsigned long tcr_save;
  167. /* disable decrement interrupt */
  168. tcr_save = mfspr(SPRN_TCR);
  169. mtspr(SPRN_TCR, tcr_save & ~TCR_DIE);
  170. /* go to sleep state */
  171. cpm_idle_sleep(mask);
  172. /* restore decrement interrupt */
  173. mtspr(SPRN_TCR, tcr_save);
  174. }
  175. static int cpm_suspend_enter(suspend_state_t state)
  176. {
  177. switch (state) {
  178. case PM_SUSPEND_STANDBY:
  179. cpm_suspend_standby(cpm.standby);
  180. break;
  181. case PM_SUSPEND_MEM:
  182. cpm_suspend_standby(cpm.suspend);
  183. break;
  184. }
  185. return 0;
  186. }
  187. static const struct platform_suspend_ops cpm_suspend_ops = {
  188. .valid = cpm_suspend_valid,
  189. .enter = cpm_suspend_enter,
  190. };
  191. static int __init cpm_get_uint_property(struct device_node *np,
  192. const char *name)
  193. {
  194. int len;
  195. const unsigned int *prop = of_get_property(np, name, &len);
  196. if (prop == NULL || len < sizeof(u32))
  197. return 0;
  198. return *prop;
  199. }
  200. static int __init cpm_init(void)
  201. {
  202. struct device_node *np;
  203. int dcr_base, dcr_len;
  204. int ret = 0;
  205. if (!cpm.powersave_off) {
  206. cpm_idle_config(CPM_IDLE_WAIT);
  207. ppc_md.power_save = &cpm_idle;
  208. }
  209. np = of_find_compatible_node(NULL, NULL, "ibm,cpm");
  210. if (!np) {
  211. ret = -EINVAL;
  212. goto out;
  213. }
  214. dcr_base = dcr_resource_start(np, 0);
  215. dcr_len = dcr_resource_len(np, 0);
  216. if (dcr_base == 0 || dcr_len == 0) {
  217. printk(KERN_ERR "cpm: could not parse dcr property for %pOF\n",
  218. np);
  219. ret = -EINVAL;
  220. goto node_put;
  221. }
  222. cpm.dcr_host = dcr_map(np, dcr_base, dcr_len);
  223. if (!DCR_MAP_OK(cpm.dcr_host)) {
  224. printk(KERN_ERR "cpm: failed to map dcr property for %pOF\n",
  225. np);
  226. ret = -EINVAL;
  227. goto node_put;
  228. }
  229. /* All 4xx SoCs with a CPM controller have one of two
  230. * different order for the CPM registers. Some have the
  231. * CPM registers in the following order (ER,FR,SR). The
  232. * others have them in the following order (SR,ER,FR).
  233. */
  234. if (cpm_get_uint_property(np, "er-offset") == 0) {
  235. cpm.dcr_offset[CPM_ER] = 0;
  236. cpm.dcr_offset[CPM_FR] = 1;
  237. cpm.dcr_offset[CPM_SR] = 2;
  238. } else {
  239. cpm.dcr_offset[CPM_ER] = 1;
  240. cpm.dcr_offset[CPM_FR] = 2;
  241. cpm.dcr_offset[CPM_SR] = 0;
  242. }
  243. /* Now let's see what IPs to turn off for the following modes */
  244. cpm.unused = cpm_get_uint_property(np, "unused-units");
  245. cpm.idle_doze = cpm_get_uint_property(np, "idle-doze");
  246. cpm.standby = cpm_get_uint_property(np, "standby");
  247. cpm.suspend = cpm_get_uint_property(np, "suspend");
  248. /* If some IPs are unused let's turn them off now */
  249. if (cpm.unused) {
  250. cpm_set(CPM_ER, cpm.unused);
  251. cpm_set(CPM_FR, cpm.unused);
  252. }
  253. /* Now let's export interfaces */
  254. if (!cpm.powersave_off && cpm.idle_doze)
  255. cpm_idle_config_sysfs();
  256. if (cpm.standby || cpm.suspend)
  257. suspend_set_ops(&cpm_suspend_ops);
  258. node_put:
  259. of_node_put(np);
  260. out:
  261. return ret;
  262. }
  263. late_initcall(cpm_init);
  264. static int __init cpm_powersave_off(char *arg)
  265. {
  266. cpm.powersave_off = 1;
  267. return 1;
  268. }
  269. __setup("powersave=off", cpm_powersave_off);