~ubuntu-branches/debian/wheezy/linux-2.6/wheezy

« back to all changes in this revision

Viewing changes to arch/powerpc/platforms/pseries/suspend.c

  • Committer: Bazaar Package Importer
  • Author(s): Ben Hutchings, Ben Hutchings, Aurelien Jarno, Martin Michlmayr
  • Date: 2011-04-06 13:53:30 UTC
  • mfrom: (43.1.5 sid)
  • Revision ID: james.westby@ubuntu.com-20110406135330-wjufxhd0tvn3zx4z
Tags: 2.6.38-3
[ Ben Hutchings ]
* [ppc64] Add to linux-tools package architectures (Closes: #620124)
* [amd64] Save cr4 to mmu_cr4_features at boot time (Closes: #620284)
* appletalk: Fix bugs introduced when removing use of BKL
* ALSA: Fix yet another race in disconnection
* cciss: Fix lost command issue
* ath9k: Fix kernel panic in AR2427
* ses: Avoid kernel panic when lun 0 is not mapped
* PCI/ACPI: Report ASPM support to BIOS if not disabled from command line

[ Aurelien Jarno ]
* rtlwifi: fix build when PCI is not enabled.

[ Martin Michlmayr ]
* rtlwifi: Eliminate udelay calls with too large values (Closes: #620204)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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/delay.h>
 
20
#include <linux/suspend.h>
 
21
#include <asm/firmware.h>
 
22
#include <asm/hvcall.h>
 
23
#include <asm/machdep.h>
 
24
#include <asm/mmu.h>
 
25
#include <asm/rtas.h>
 
26
 
 
27
static u64 stream_id;
 
28
static struct sys_device suspend_sysdev;
 
29
static DECLARE_COMPLETION(suspend_work);
 
30
static struct rtas_suspend_me_data suspend_data;
 
31
static atomic_t suspending;
 
32
 
 
33
/**
 
34
 * pseries_suspend_begin - First phase of hibernation
 
35
 *
 
36
 * Check to ensure we are in a valid state to hibernate
 
37
 *
 
38
 * Return value:
 
39
 *      0 on success / other on failure
 
40
 **/
 
41
static int pseries_suspend_begin(suspend_state_t state)
 
42
{
 
43
        long vasi_state, rc;
 
44
        unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
 
45
 
 
46
        /* Make sure the state is valid */
 
47
        rc = plpar_hcall(H_VASI_STATE, retbuf, stream_id);
 
48
 
 
49
        vasi_state = retbuf[0];
 
50
 
 
51
        if (rc) {
 
52
                pr_err("pseries_suspend_begin: vasi_state returned %ld\n",rc);
 
53
                return rc;
 
54
        } else if (vasi_state == H_VASI_ENABLED) {
 
55
                return -EAGAIN;
 
56
        } else if (vasi_state != H_VASI_SUSPENDING) {
 
57
                pr_err("pseries_suspend_begin: vasi_state returned state %ld\n",
 
58
                       vasi_state);
 
59
                return -EIO;
 
60
        }
 
61
 
 
62
        return 0;
 
63
}
 
64
 
 
65
/**
 
66
 * pseries_suspend_cpu - Suspend a single CPU
 
67
 *
 
68
 * Makes the H_JOIN call to suspend the CPU
 
69
 *
 
70
 **/
 
71
static int pseries_suspend_cpu(void)
 
72
{
 
73
        if (atomic_read(&suspending))
 
74
                return rtas_suspend_cpu(&suspend_data);
 
75
        return 0;
 
76
}
 
77
 
 
78
/**
 
79
 * pseries_suspend_enter - Final phase of hibernation
 
80
 *
 
81
 * Return value:
 
82
 *      0 on success / other on failure
 
83
 **/
 
84
static int pseries_suspend_enter(suspend_state_t state)
 
85
{
 
86
        int rc = rtas_suspend_last_cpu(&suspend_data);
 
87
 
 
88
        atomic_set(&suspending, 0);
 
89
        atomic_set(&suspend_data.done, 1);
 
90
        return rc;
 
91
}
 
92
 
 
93
/**
 
94
 * pseries_prepare_late - Prepare to suspend all other CPUs
 
95
 *
 
96
 * Return value:
 
97
 *      0 on success / other on failure
 
98
 **/
 
99
static int pseries_prepare_late(void)
 
100
{
 
101
        atomic_set(&suspending, 1);
 
102
        atomic_set(&suspend_data.working, 0);
 
103
        atomic_set(&suspend_data.done, 0);
 
104
        atomic_set(&suspend_data.error, 0);
 
105
        suspend_data.complete = &suspend_work;
 
106
        INIT_COMPLETION(suspend_work);
 
107
        return 0;
 
108
}
 
109
 
 
110
/**
 
111
 * store_hibernate - Initiate partition hibernation
 
112
 * @classdev:   sysdev class struct
 
113
 * @attr:               class device attribute struct
 
114
 * @buf:                buffer
 
115
 * @count:              buffer size
 
116
 *
 
117
 * Write the stream ID received from the HMC to this file
 
118
 * to trigger hibernating the partition
 
119
 *
 
120
 * Return value:
 
121
 *      number of bytes printed to buffer / other on failure
 
122
 **/
 
123
static ssize_t store_hibernate(struct sysdev_class *classdev,
 
124
                               struct sysdev_class_attribute *attr,
 
125
                               const char *buf, size_t count)
 
126
{
 
127
        int rc;
 
128
 
 
129
        if (!capable(CAP_SYS_ADMIN))
 
130
                return -EPERM;
 
131
 
 
132
        stream_id = simple_strtoul(buf, NULL, 16);
 
133
 
 
134
        do {
 
135
                rc = pseries_suspend_begin(PM_SUSPEND_MEM);
 
136
                if (rc == -EAGAIN)
 
137
                        ssleep(1);
 
138
        } while (rc == -EAGAIN);
 
139
 
 
140
        if (!rc)
 
141
                rc = pm_suspend(PM_SUSPEND_MEM);
 
142
 
 
143
        stream_id = 0;
 
144
 
 
145
        if (!rc)
 
146
                rc = count;
 
147
        return rc;
 
148
}
 
149
 
 
150
static SYSDEV_CLASS_ATTR(hibernate, S_IWUSR, NULL, store_hibernate);
 
151
 
 
152
static struct sysdev_class suspend_sysdev_class = {
 
153
        .name = "power",
 
154
};
 
155
 
 
156
static const struct platform_suspend_ops pseries_suspend_ops = {
 
157
        .valid          = suspend_valid_only_mem,
 
158
        .begin          = pseries_suspend_begin,
 
159
        .prepare_late   = pseries_prepare_late,
 
160
        .enter          = pseries_suspend_enter,
 
161
};
 
162
 
 
163
/**
 
164
 * pseries_suspend_sysfs_register - Register with sysfs
 
165
 *
 
166
 * Return value:
 
167
 *      0 on success / other on failure
 
168
 **/
 
169
static int pseries_suspend_sysfs_register(struct sys_device *sysdev)
 
170
{
 
171
        int rc;
 
172
 
 
173
        if ((rc = sysdev_class_register(&suspend_sysdev_class)))
 
174
                return rc;
 
175
 
 
176
        sysdev->id = 0;
 
177
        sysdev->cls = &suspend_sysdev_class;
 
178
 
 
179
        if ((rc = sysdev_class_create_file(&suspend_sysdev_class, &attr_hibernate)))
 
180
                goto class_unregister;
 
181
 
 
182
        return 0;
 
183
 
 
184
class_unregister:
 
185
        sysdev_class_unregister(&suspend_sysdev_class);
 
186
        return rc;
 
187
}
 
188
 
 
189
/**
 
190
 * pseries_suspend_init - initcall for pSeries suspend
 
191
 *
 
192
 * Return value:
 
193
 *      0 on success / other on failure
 
194
 **/
 
195
static int __init pseries_suspend_init(void)
 
196
{
 
197
        int rc;
 
198
 
 
199
        if (!machine_is(pseries) || !firmware_has_feature(FW_FEATURE_LPAR))
 
200
                return 0;
 
201
 
 
202
        suspend_data.token = rtas_token("ibm,suspend-me");
 
203
        if (suspend_data.token == RTAS_UNKNOWN_SERVICE)
 
204
                return 0;
 
205
 
 
206
        if ((rc = pseries_suspend_sysfs_register(&suspend_sysdev)))
 
207
                return rc;
 
208
 
 
209
        ppc_md.suspend_disable_cpu = pseries_suspend_cpu;
 
210
        suspend_set_ops(&pseries_suspend_ops);
 
211
        return 0;
 
212
}
 
213
 
 
214
__initcall(pseries_suspend_init);