1 /* 2 * Copyright (C) 2010 Brian King IBM Corporation 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 */ 18 19 #include <linux/cpu.h> 20 #include <linux/delay.h> 21 #include <linux/suspend.h> 22 #include <linux/stat.h> 23 #include <asm/firmware.h> 24 #include <asm/hvcall.h> 25 #include <asm/machdep.h> 26 #include <asm/mmu.h> 27 #include <asm/rtas.h> 28 #include <asm/topology.h> 29 #include "../../kernel/cacheinfo.h" 30 31 static u64 stream_id; 32 static struct device suspend_dev; 33 static DECLARE_COMPLETION(suspend_work); 34 static struct rtas_suspend_me_data suspend_data; 35 static atomic_t suspending; 36 37 /** 38 * pseries_suspend_begin - First phase of hibernation 39 * 40 * Check to ensure we are in a valid state to hibernate 41 * 42 * Return value: 43 * 0 on success / other on failure 44 **/ 45 static int pseries_suspend_begin(suspend_state_t state) 46 { 47 long vasi_state, rc; 48 unsigned long retbuf[PLPAR_HCALL_BUFSIZE]; 49 50 /* Make sure the state is valid */ 51 rc = plpar_hcall(H_VASI_STATE, retbuf, stream_id); 52 53 vasi_state = retbuf[0]; 54 55 if (rc) { 56 pr_err("pseries_suspend_begin: vasi_state returned %ld\n",rc); 57 return rc; 58 } else if (vasi_state == H_VASI_ENABLED) { 59 return -EAGAIN; 60 } else if (vasi_state != H_VASI_SUSPENDING) { 61 pr_err("pseries_suspend_begin: vasi_state returned state %ld\n", 62 vasi_state); 63 return -EIO; 64 } 65 66 return 0; 67 } 68 69 /** 70 * pseries_suspend_cpu - Suspend a single CPU 71 * 72 * Makes the H_JOIN call to suspend the CPU 73 * 74 **/ 75 static int pseries_suspend_cpu(void) 76 { 77 if (atomic_read(&suspending)) 78 return rtas_suspend_cpu(&suspend_data); 79 return 0; 80 } 81 82 /** 83 * pseries_suspend_enable_irqs 84 * 85 * Post suspend configuration updates 86 * 87 **/ 88 static void pseries_suspend_enable_irqs(void) 89 { 90 /* 91 * Update configuration which can be modified based on device tree 92 * changes during resume. 93 */ 94 cacheinfo_cpu_offline(smp_processor_id()); 95 post_mobility_fixup(); 96 cacheinfo_cpu_online(smp_processor_id()); 97 } 98 99 /** 100 * pseries_suspend_enter - Final phase of hibernation 101 * 102 * Return value: 103 * 0 on success / other on failure 104 **/ 105 static int pseries_suspend_enter(suspend_state_t state) 106 { 107 int rc = rtas_suspend_last_cpu(&suspend_data); 108 109 atomic_set(&suspending, 0); 110 atomic_set(&suspend_data.done, 1); 111 return rc; 112 } 113 114 /** 115 * pseries_prepare_late - Prepare to suspend all other CPUs 116 * 117 * Return value: 118 * 0 on success / other on failure 119 **/ 120 static int pseries_prepare_late(void) 121 { 122 atomic_set(&suspending, 1); 123 atomic_set(&suspend_data.working, 0); 124 atomic_set(&suspend_data.done, 0); 125 atomic_set(&suspend_data.error, 0); 126 suspend_data.complete = &suspend_work; 127 reinit_completion(&suspend_work); 128 return 0; 129 } 130 131 /** 132 * store_hibernate - Initiate partition hibernation 133 * @dev: subsys root device 134 * @attr: device attribute struct 135 * @buf: buffer 136 * @count: buffer size 137 * 138 * Write the stream ID received from the HMC to this file 139 * to trigger hibernating the partition 140 * 141 * Return value: 142 * number of bytes printed to buffer / other on failure 143 **/ 144 static ssize_t store_hibernate(struct device *dev, 145 struct device_attribute *attr, 146 const char *buf, size_t count) 147 { 148 cpumask_var_t offline_mask; 149 int rc; 150 151 if (!capable(CAP_SYS_ADMIN)) 152 return -EPERM; 153 154 if (!alloc_cpumask_var(&offline_mask, GFP_KERNEL)) 155 return -ENOMEM; 156 157 stream_id = simple_strtoul(buf, NULL, 16); 158 159 do { 160 rc = pseries_suspend_begin(PM_SUSPEND_MEM); 161 if (rc == -EAGAIN) 162 ssleep(1); 163 } while (rc == -EAGAIN); 164 165 if (!rc) { 166 /* All present CPUs must be online */ 167 cpumask_andnot(offline_mask, cpu_present_mask, 168 cpu_online_mask); 169 rc = rtas_online_cpus_mask(offline_mask); 170 if (rc) { 171 pr_err("%s: Could not bring present CPUs online.\n", 172 __func__); 173 goto out; 174 } 175 176 stop_topology_update(); 177 rc = pm_suspend(PM_SUSPEND_MEM); 178 start_topology_update(); 179 180 /* Take down CPUs not online prior to suspend */ 181 if (!rtas_offline_cpus_mask(offline_mask)) 182 pr_warn("%s: Could not restore CPUs to offline " 183 "state.\n", __func__); 184 } 185 186 stream_id = 0; 187 188 if (!rc) 189 rc = count; 190 out: 191 free_cpumask_var(offline_mask); 192 return rc; 193 } 194 195 #define USER_DT_UPDATE 0 196 #define KERN_DT_UPDATE 1 197 198 /** 199 * show_hibernate - Report device tree update responsibilty 200 * @dev: subsys root device 201 * @attr: device attribute struct 202 * @buf: buffer 203 * 204 * Report whether a device tree update is performed by the kernel after a 205 * resume, or if drmgr must coordinate the update from user space. 206 * 207 * Return value: 208 * 0 if drmgr is to initiate update, and 1 otherwise 209 **/ 210 static ssize_t show_hibernate(struct device *dev, 211 struct device_attribute *attr, 212 char *buf) 213 { 214 return sprintf(buf, "%d\n", KERN_DT_UPDATE); 215 } 216 217 static DEVICE_ATTR(hibernate, 0644, show_hibernate, store_hibernate); 218 219 static struct bus_type suspend_subsys = { 220 .name = "power", 221 .dev_name = "power", 222 }; 223 224 static const struct platform_suspend_ops pseries_suspend_ops = { 225 .valid = suspend_valid_only_mem, 226 .begin = pseries_suspend_begin, 227 .prepare_late = pseries_prepare_late, 228 .enter = pseries_suspend_enter, 229 }; 230 231 /** 232 * pseries_suspend_sysfs_register - Register with sysfs 233 * 234 * Return value: 235 * 0 on success / other on failure 236 **/ 237 static int pseries_suspend_sysfs_register(struct device *dev) 238 { 239 int rc; 240 241 if ((rc = subsys_system_register(&suspend_subsys, NULL))) 242 return rc; 243 244 dev->id = 0; 245 dev->bus = &suspend_subsys; 246 247 if ((rc = device_create_file(suspend_subsys.dev_root, &dev_attr_hibernate))) 248 goto subsys_unregister; 249 250 return 0; 251 252 subsys_unregister: 253 bus_unregister(&suspend_subsys); 254 return rc; 255 } 256 257 /** 258 * pseries_suspend_init - initcall for pSeries suspend 259 * 260 * Return value: 261 * 0 on success / other on failure 262 **/ 263 static int __init pseries_suspend_init(void) 264 { 265 int rc; 266 267 if (!firmware_has_feature(FW_FEATURE_LPAR)) 268 return 0; 269 270 suspend_data.token = rtas_token("ibm,suspend-me"); 271 if (suspend_data.token == RTAS_UNKNOWN_SERVICE) 272 return 0; 273 274 if ((rc = pseries_suspend_sysfs_register(&suspend_dev))) 275 return rc; 276 277 ppc_md.suspend_disable_cpu = pseries_suspend_cpu; 278 ppc_md.suspend_enable_irqs = pseries_suspend_enable_irqs; 279 suspend_set_ops(&pseries_suspend_ops); 280 return 0; 281 } 282 machine_device_initcall(pseries, pseries_suspend_init); 283