Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * PowerPC 4xx Clock and Power Management
0004  *
0005  * Copyright (C) 2010, Applied Micro Circuits Corporation
0006  * Victor Gallardo (vgallardo@apm.com)
0007  *
0008  * Based on arch/powerpc/platforms/44x/idle.c:
0009  * Jerone Young <jyoung5@us.ibm.com>
0010  * Copyright 2008 IBM Corp.
0011  *
0012  * Based on arch/powerpc/sysdev/fsl_pmc.c:
0013  * Anton Vorontsov <avorontsov@ru.mvista.com>
0014  * Copyright 2009  MontaVista Software, Inc.
0015  *
0016  * See file CREDITS for list of people who contributed to this
0017  * project.
0018  */
0019 
0020 #include <linux/kernel.h>
0021 #include <linux/of_platform.h>
0022 #include <linux/sysfs.h>
0023 #include <linux/cpu.h>
0024 #include <linux/suspend.h>
0025 #include <asm/dcr.h>
0026 #include <asm/dcr-native.h>
0027 #include <asm/machdep.h>
0028 
0029 #define CPM_ER  0
0030 #define CPM_FR  1
0031 #define CPM_SR  2
0032 
0033 #define CPM_IDLE_WAIT   0
0034 #define CPM_IDLE_DOZE   1
0035 
0036 struct cpm {
0037     dcr_host_t  dcr_host;
0038     unsigned int    dcr_offset[3];
0039     unsigned int    powersave_off;
0040     unsigned int    unused;
0041     unsigned int    idle_doze;
0042     unsigned int    standby;
0043     unsigned int    suspend;
0044 };
0045 
0046 static struct cpm cpm;
0047 
0048 struct cpm_idle_mode {
0049     unsigned int enabled;
0050     const char  *name;
0051 };
0052 
0053 static struct cpm_idle_mode idle_mode[] = {
0054     [CPM_IDLE_WAIT] = { 1, "wait" }, /* default */
0055     [CPM_IDLE_DOZE] = { 0, "doze" },
0056 };
0057 
0058 static unsigned int cpm_set(unsigned int cpm_reg, unsigned int mask)
0059 {
0060     unsigned int value;
0061 
0062     /* CPM controller supports 3 different types of sleep interface
0063      * known as class 1, 2 and 3. For class 1 units, they are
0064      * unconditionally put to sleep when the corresponding CPM bit is
0065      * set. For class 2 and 3 units this is not case; if they can be
0066      * put to sleep, they will. Here we do not verify, we just
0067      * set them and expect them to eventually go off when they can.
0068      */
0069     value = dcr_read(cpm.dcr_host, cpm.dcr_offset[cpm_reg]);
0070     dcr_write(cpm.dcr_host, cpm.dcr_offset[cpm_reg], value | mask);
0071 
0072     /* return old state, to restore later if needed */
0073     return value;
0074 }
0075 
0076 static void cpm_idle_wait(void)
0077 {
0078     unsigned long msr_save;
0079 
0080     /* save off initial state */
0081     msr_save = mfmsr();
0082     /* sync required when CPM0_ER[CPU] is set */
0083     mb();
0084     /* set wait state MSR */
0085     mtmsr(msr_save|MSR_WE|MSR_EE|MSR_CE|MSR_DE);
0086     isync();
0087     /* return to initial state */
0088     mtmsr(msr_save);
0089     isync();
0090 }
0091 
0092 static void cpm_idle_sleep(unsigned int mask)
0093 {
0094     unsigned int er_save;
0095 
0096     /* update CPM_ER state */
0097     er_save = cpm_set(CPM_ER, mask);
0098 
0099     /* go to wait state so that CPM0_ER[CPU] can take effect */
0100     cpm_idle_wait();
0101 
0102     /* restore CPM_ER state */
0103     dcr_write(cpm.dcr_host, cpm.dcr_offset[CPM_ER], er_save);
0104 }
0105 
0106 static void cpm_idle_doze(void)
0107 {
0108     cpm_idle_sleep(cpm.idle_doze);
0109 }
0110 
0111 static void cpm_idle_config(int mode)
0112 {
0113     int i;
0114 
0115     if (idle_mode[mode].enabled)
0116         return;
0117 
0118     for (i = 0; i < ARRAY_SIZE(idle_mode); i++)
0119         idle_mode[i].enabled = 0;
0120 
0121     idle_mode[mode].enabled = 1;
0122 }
0123 
0124 static ssize_t cpm_idle_show(struct kobject *kobj,
0125                  struct kobj_attribute *attr, char *buf)
0126 {
0127     char *s = buf;
0128     int i;
0129 
0130     for (i = 0; i < ARRAY_SIZE(idle_mode); i++) {
0131         if (idle_mode[i].enabled)
0132             s += sprintf(s, "[%s] ", idle_mode[i].name);
0133         else
0134             s += sprintf(s, "%s ", idle_mode[i].name);
0135     }
0136 
0137     *(s-1) = '\n'; /* convert the last space to a newline */
0138 
0139     return s - buf;
0140 }
0141 
0142 static ssize_t cpm_idle_store(struct kobject *kobj,
0143                   struct kobj_attribute *attr,
0144                   const char *buf, size_t n)
0145 {
0146     int i;
0147     char *p;
0148     int len;
0149 
0150     p = memchr(buf, '\n', n);
0151     len = p ? p - buf : n;
0152 
0153     for (i = 0; i < ARRAY_SIZE(idle_mode); i++) {
0154         if (strncmp(buf, idle_mode[i].name, len) == 0) {
0155             cpm_idle_config(i);
0156             return n;
0157         }
0158     }
0159 
0160     return -EINVAL;
0161 }
0162 
0163 static struct kobj_attribute cpm_idle_attr =
0164     __ATTR(idle, 0644, cpm_idle_show, cpm_idle_store);
0165 
0166 static void __init cpm_idle_config_sysfs(void)
0167 {
0168     struct device *dev;
0169     unsigned long ret;
0170 
0171     dev = get_cpu_device(0);
0172 
0173     ret = sysfs_create_file(&dev->kobj,
0174                 &cpm_idle_attr.attr);
0175     if (ret)
0176         printk(KERN_WARNING
0177                "cpm: failed to create idle sysfs entry\n");
0178 }
0179 
0180 static void cpm_idle(void)
0181 {
0182     if (idle_mode[CPM_IDLE_DOZE].enabled)
0183         cpm_idle_doze();
0184     else
0185         cpm_idle_wait();
0186 }
0187 
0188 static int cpm_suspend_valid(suspend_state_t state)
0189 {
0190     switch (state) {
0191     case PM_SUSPEND_STANDBY:
0192         return !!cpm.standby;
0193     case PM_SUSPEND_MEM:
0194         return !!cpm.suspend;
0195     default:
0196         return 0;
0197     }
0198 }
0199 
0200 static void cpm_suspend_standby(unsigned int mask)
0201 {
0202     unsigned long tcr_save;
0203 
0204     /* disable decrement interrupt */
0205     tcr_save = mfspr(SPRN_TCR);
0206     mtspr(SPRN_TCR, tcr_save & ~TCR_DIE);
0207 
0208     /* go to sleep state */
0209     cpm_idle_sleep(mask);
0210 
0211     /* restore decrement interrupt */
0212     mtspr(SPRN_TCR, tcr_save);
0213 }
0214 
0215 static int cpm_suspend_enter(suspend_state_t state)
0216 {
0217     switch (state) {
0218     case PM_SUSPEND_STANDBY:
0219         cpm_suspend_standby(cpm.standby);
0220         break;
0221     case PM_SUSPEND_MEM:
0222         cpm_suspend_standby(cpm.suspend);
0223         break;
0224     }
0225 
0226     return 0;
0227 }
0228 
0229 static const struct platform_suspend_ops cpm_suspend_ops = {
0230     .valid      = cpm_suspend_valid,
0231     .enter      = cpm_suspend_enter,
0232 };
0233 
0234 static int __init cpm_get_uint_property(struct device_node *np,
0235                  const char *name)
0236 {
0237     int len;
0238     const unsigned int *prop = of_get_property(np, name, &len);
0239 
0240     if (prop == NULL || len < sizeof(u32))
0241         return 0;
0242 
0243     return *prop;
0244 }
0245 
0246 static int __init cpm_init(void)
0247 {
0248     struct device_node *np;
0249     int dcr_base, dcr_len;
0250     int ret = 0;
0251 
0252     if (!cpm.powersave_off) {
0253         cpm_idle_config(CPM_IDLE_WAIT);
0254         ppc_md.power_save = &cpm_idle;
0255     }
0256 
0257     np = of_find_compatible_node(NULL, NULL, "ibm,cpm");
0258     if (!np) {
0259         ret = -EINVAL;
0260         goto out;
0261     }
0262 
0263     dcr_base = dcr_resource_start(np, 0);
0264     dcr_len = dcr_resource_len(np, 0);
0265 
0266     if (dcr_base == 0 || dcr_len == 0) {
0267         printk(KERN_ERR "cpm: could not parse dcr property for %pOF\n",
0268                np);
0269         ret = -EINVAL;
0270         goto node_put;
0271     }
0272 
0273     cpm.dcr_host = dcr_map(np, dcr_base, dcr_len);
0274 
0275     if (!DCR_MAP_OK(cpm.dcr_host)) {
0276         printk(KERN_ERR "cpm: failed to map dcr property for %pOF\n",
0277                np);
0278         ret = -EINVAL;
0279         goto node_put;
0280     }
0281 
0282     /* All 4xx SoCs with a CPM controller have one of two
0283      * different order for the CPM registers. Some have the
0284      * CPM registers in the following order (ER,FR,SR). The
0285      * others have them in the following order (SR,ER,FR).
0286      */
0287 
0288     if (cpm_get_uint_property(np, "er-offset") == 0) {
0289         cpm.dcr_offset[CPM_ER] = 0;
0290         cpm.dcr_offset[CPM_FR] = 1;
0291         cpm.dcr_offset[CPM_SR] = 2;
0292     } else {
0293         cpm.dcr_offset[CPM_ER] = 1;
0294         cpm.dcr_offset[CPM_FR] = 2;
0295         cpm.dcr_offset[CPM_SR] = 0;
0296     }
0297 
0298     /* Now let's see what IPs to turn off for the following modes */
0299 
0300     cpm.unused = cpm_get_uint_property(np, "unused-units");
0301     cpm.idle_doze = cpm_get_uint_property(np, "idle-doze");
0302     cpm.standby = cpm_get_uint_property(np, "standby");
0303     cpm.suspend = cpm_get_uint_property(np, "suspend");
0304 
0305     /* If some IPs are unused let's turn them off now */
0306 
0307     if (cpm.unused) {
0308         cpm_set(CPM_ER, cpm.unused);
0309         cpm_set(CPM_FR, cpm.unused);
0310     }
0311 
0312     /* Now let's export interfaces */
0313 
0314     if (!cpm.powersave_off && cpm.idle_doze)
0315         cpm_idle_config_sysfs();
0316 
0317     if (cpm.standby || cpm.suspend)
0318         suspend_set_ops(&cpm_suspend_ops);
0319 node_put:
0320     of_node_put(np);
0321 out:
0322     return ret;
0323 }
0324 
0325 late_initcall(cpm_init);
0326 
0327 static int __init cpm_powersave_off(char *arg)
0328 {
0329     cpm.powersave_off = 1;
0330     return 1;
0331 }
0332 __setup("powersave=off", cpm_powersave_off);