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 31static u64 stream_id; 32static struct device suspend_dev; 33static DECLARE_COMPLETION(suspend_work); 34static struct rtas_suspend_me_data suspend_data; 35static 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 **/ 45static 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 **/ 75static 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 **/ 88static 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 **/ 105static 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 **/ 120static 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 **/ 144static 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_TEMPORARY)) 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; 190out: 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 **/ 210static 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 217static DEVICE_ATTR(hibernate, S_IWUSR | S_IRUGO, 218 show_hibernate, store_hibernate); 219 220static struct bus_type suspend_subsys = { 221 .name = "power", 222 .dev_name = "power", 223}; 224 225static const struct platform_suspend_ops pseries_suspend_ops = { 226 .valid = suspend_valid_only_mem, 227 .begin = pseries_suspend_begin, 228 .prepare_late = pseries_prepare_late, 229 .enter = pseries_suspend_enter, 230}; 231 232/** 233 * pseries_suspend_sysfs_register - Register with sysfs 234 * 235 * Return value: 236 * 0 on success / other on failure 237 **/ 238static int pseries_suspend_sysfs_register(struct device *dev) 239{ 240 int rc; 241 242 if ((rc = subsys_system_register(&suspend_subsys, NULL))) 243 return rc; 244 245 dev->id = 0; 246 dev->bus = &suspend_subsys; 247 248 if ((rc = device_create_file(suspend_subsys.dev_root, &dev_attr_hibernate))) 249 goto subsys_unregister; 250 251 return 0; 252 253subsys_unregister: 254 bus_unregister(&suspend_subsys); 255 return rc; 256} 257 258/** 259 * pseries_suspend_init - initcall for pSeries suspend 260 * 261 * Return value: 262 * 0 on success / other on failure 263 **/ 264static int __init pseries_suspend_init(void) 265{ 266 int rc; 267 268 if (!firmware_has_feature(FW_FEATURE_LPAR)) 269 return 0; 270 271 suspend_data.token = rtas_token("ibm,suspend-me"); 272 if (suspend_data.token == RTAS_UNKNOWN_SERVICE) 273 return 0; 274 275 if ((rc = pseries_suspend_sysfs_register(&suspend_dev))) 276 return rc; 277 278 ppc_md.suspend_disable_cpu = pseries_suspend_cpu; 279 ppc_md.suspend_enable_irqs = pseries_suspend_enable_irqs; 280 suspend_set_ops(&pseries_suspend_ops); 281 return 0; 282} 283machine_device_initcall(pseries, pseries_suspend_init); 284