root/arch/powerpc/platforms/4xx/cpm.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. cpm_set
  2. cpm_idle_wait
  3. cpm_idle_sleep
  4. cpm_idle_doze
  5. cpm_idle_config
  6. cpm_idle_show
  7. cpm_idle_store
  8. cpm_idle_config_sysfs
  9. cpm_idle
  10. cpm_suspend_valid
  11. cpm_suspend_standby
  12. cpm_suspend_enter
  13. cpm_get_uint_property
  14. cpm_init
  15. cpm_powersave_off

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

/* [<][>][^][v][top][bottom][index][help] */