root/arch/powerpc/platforms/pseries/suspend.c

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

DEFINITIONS

This source file includes following definitions.
  1. pseries_suspend_begin
  2. pseries_suspend_cpu
  3. pseries_suspend_enable_irqs
  4. pseries_suspend_enter
  5. pseries_prepare_late
  6. store_hibernate
  7. show_hibernate
  8. pseries_suspend_sysfs_register
  9. pseries_suspend_init

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

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