0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
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" },
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
0063
0064
0065
0066
0067
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
0073 return value;
0074 }
0075
0076 static void cpm_idle_wait(void)
0077 {
0078 unsigned long msr_save;
0079
0080
0081 msr_save = mfmsr();
0082
0083 mb();
0084
0085 mtmsr(msr_save|MSR_WE|MSR_EE|MSR_CE|MSR_DE);
0086 isync();
0087
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
0097 er_save = cpm_set(CPM_ER, mask);
0098
0099
0100 cpm_idle_wait();
0101
0102
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';
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
0205 tcr_save = mfspr(SPRN_TCR);
0206 mtspr(SPRN_TCR, tcr_save & ~TCR_DIE);
0207
0208
0209 cpm_idle_sleep(mask);
0210
0211
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
0283
0284
0285
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
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
0306
0307 if (cpm.unused) {
0308 cpm_set(CPM_ER, cpm.unused);
0309 cpm_set(CPM_FR, cpm.unused);
0310 }
0311
0312
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);