diff options
Diffstat (limited to 'arch/powerpc/platforms/pseries')
-rw-r--r-- | arch/powerpc/platforms/pseries/Makefile | 4 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/dlpar.c | 1 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/eeh_cache.c | 3 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/event_sources.c | 23 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/hotplug-cpu.c | 7 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/ras.c | 5 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/reconfig.c | 4 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/suspend.c | 214 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/xics.c | 2 |
9 files changed, 243 insertions, 20 deletions
diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile index 3dbef309bc8..046ace9c438 100644 --- a/arch/powerpc/platforms/pseries/Makefile +++ b/arch/powerpc/platforms/pseries/Makefile | |||
@@ -26,3 +26,7 @@ obj-$(CONFIG_HCALL_STATS) += hvCall_inst.o | |||
26 | obj-$(CONFIG_PHYP_DUMP) += phyp_dump.o | 26 | obj-$(CONFIG_PHYP_DUMP) += phyp_dump.o |
27 | obj-$(CONFIG_CMM) += cmm.o | 27 | obj-$(CONFIG_CMM) += cmm.o |
28 | obj-$(CONFIG_DTL) += dtl.o | 28 | obj-$(CONFIG_DTL) += dtl.o |
29 | |||
30 | ifeq ($(CONFIG_PPC_PSERIES),y) | ||
31 | obj-$(CONFIG_SUSPEND) += suspend.o | ||
32 | endif | ||
diff --git a/arch/powerpc/platforms/pseries/dlpar.c b/arch/powerpc/platforms/pseries/dlpar.c index d71e5858408..227c1c3d585 100644 --- a/arch/powerpc/platforms/pseries/dlpar.c +++ b/arch/powerpc/platforms/pseries/dlpar.c | |||
@@ -463,6 +463,7 @@ static int dlpar_offline_cpu(struct device_node *dn) | |||
463 | break; | 463 | break; |
464 | 464 | ||
465 | if (get_cpu_current_state(cpu) == CPU_STATE_ONLINE) { | 465 | if (get_cpu_current_state(cpu) == CPU_STATE_ONLINE) { |
466 | set_preferred_offline_state(cpu, CPU_STATE_OFFLINE); | ||
466 | cpu_maps_update_done(); | 467 | cpu_maps_update_done(); |
467 | rc = cpu_down(cpu); | 468 | rc = cpu_down(cpu); |
468 | if (rc) | 469 | if (rc) |
diff --git a/arch/powerpc/platforms/pseries/eeh_cache.c b/arch/powerpc/platforms/pseries/eeh_cache.c index 30b987b73c2..8ed0d2d0e1b 100644 --- a/arch/powerpc/platforms/pseries/eeh_cache.c +++ b/arch/powerpc/platforms/pseries/eeh_cache.c | |||
@@ -288,8 +288,7 @@ void __init pci_addr_cache_build(void) | |||
288 | 288 | ||
289 | spin_lock_init(&pci_io_addr_cache_root.piar_lock); | 289 | spin_lock_init(&pci_io_addr_cache_root.piar_lock); |
290 | 290 | ||
291 | while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { | 291 | for_each_pci_dev(dev) { |
292 | |||
293 | pci_addr_cache_insert_device(dev); | 292 | pci_addr_cache_insert_device(dev); |
294 | 293 | ||
295 | dn = pci_device_to_OF_node(dev); | 294 | dn = pci_device_to_OF_node(dev); |
diff --git a/arch/powerpc/platforms/pseries/event_sources.c b/arch/powerpc/platforms/pseries/event_sources.c index e889c9d9586..2605c310166 100644 --- a/arch/powerpc/platforms/pseries/event_sources.c +++ b/arch/powerpc/platforms/pseries/event_sources.c | |||
@@ -41,9 +41,12 @@ void request_event_sources_irqs(struct device_node *np, | |||
41 | if (count > 15) | 41 | if (count > 15) |
42 | break; | 42 | break; |
43 | virqs[count] = irq_create_mapping(NULL, *(opicprop++)); | 43 | virqs[count] = irq_create_mapping(NULL, *(opicprop++)); |
44 | if (virqs[count] == NO_IRQ) | 44 | if (virqs[count] == NO_IRQ) { |
45 | printk(KERN_ERR "Unable to allocate interrupt " | 45 | pr_err("event-sources: Unable to allocate " |
46 | "number for %s\n", np->full_name); | 46 | "interrupt number for %s\n", |
47 | np->full_name); | ||
48 | WARN_ON(1); | ||
49 | } | ||
47 | else | 50 | else |
48 | count++; | 51 | count++; |
49 | 52 | ||
@@ -59,9 +62,12 @@ void request_event_sources_irqs(struct device_node *np, | |||
59 | virqs[count] = irq_create_of_mapping(oirq.controller, | 62 | virqs[count] = irq_create_of_mapping(oirq.controller, |
60 | oirq.specifier, | 63 | oirq.specifier, |
61 | oirq.size); | 64 | oirq.size); |
62 | if (virqs[count] == NO_IRQ) | 65 | if (virqs[count] == NO_IRQ) { |
63 | printk(KERN_ERR "Unable to allocate interrupt " | 66 | pr_err("event-sources: Unable to allocate " |
64 | "number for %s\n", np->full_name); | 67 | "interrupt number for %s\n", |
68 | np->full_name); | ||
69 | WARN_ON(1); | ||
70 | } | ||
65 | else | 71 | else |
66 | count++; | 72 | count++; |
67 | } | 73 | } |
@@ -70,8 +76,9 @@ void request_event_sources_irqs(struct device_node *np, | |||
70 | /* Now request them */ | 76 | /* Now request them */ |
71 | for (i = 0; i < count; i++) { | 77 | for (i = 0; i < count; i++) { |
72 | if (request_irq(virqs[i], handler, 0, name, NULL)) { | 78 | if (request_irq(virqs[i], handler, 0, name, NULL)) { |
73 | printk(KERN_ERR "Unable to request interrupt %d for " | 79 | pr_err("event-sources: Unable to request interrupt " |
74 | "%s\n", virqs[i], np->full_name); | 80 | "%d for %s\n", virqs[i], np->full_name); |
81 | WARN_ON(1); | ||
75 | return; | 82 | return; |
76 | } | 83 | } |
77 | } | 84 | } |
diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c index 8f85f399ab9..fd50ccd4bac 100644 --- a/arch/powerpc/platforms/pseries/hotplug-cpu.c +++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c | |||
@@ -116,6 +116,9 @@ static void pseries_mach_cpu_die(void) | |||
116 | 116 | ||
117 | if (get_preferred_offline_state(cpu) == CPU_STATE_INACTIVE) { | 117 | if (get_preferred_offline_state(cpu) == CPU_STATE_INACTIVE) { |
118 | set_cpu_current_state(cpu, CPU_STATE_INACTIVE); | 118 | set_cpu_current_state(cpu, CPU_STATE_INACTIVE); |
119 | if (ppc_md.suspend_disable_cpu) | ||
120 | ppc_md.suspend_disable_cpu(); | ||
121 | |||
119 | cede_latency_hint = 2; | 122 | cede_latency_hint = 2; |
120 | 123 | ||
121 | get_lppaca()->idle = 1; | 124 | get_lppaca()->idle = 1; |
@@ -190,12 +193,12 @@ static void pseries_cpu_die(unsigned int cpu) | |||
190 | 193 | ||
191 | if (get_preferred_offline_state(cpu) == CPU_STATE_INACTIVE) { | 194 | if (get_preferred_offline_state(cpu) == CPU_STATE_INACTIVE) { |
192 | cpu_status = 1; | 195 | cpu_status = 1; |
193 | for (tries = 0; tries < 1000; tries++) { | 196 | for (tries = 0; tries < 5000; tries++) { |
194 | if (get_cpu_current_state(cpu) == CPU_STATE_INACTIVE) { | 197 | if (get_cpu_current_state(cpu) == CPU_STATE_INACTIVE) { |
195 | cpu_status = 0; | 198 | cpu_status = 0; |
196 | break; | 199 | break; |
197 | } | 200 | } |
198 | cpu_relax(); | 201 | msleep(1); |
199 | } | 202 | } |
200 | } else if (get_preferred_offline_state(cpu) == CPU_STATE_OFFLINE) { | 203 | } else if (get_preferred_offline_state(cpu) == CPU_STATE_OFFLINE) { |
201 | 204 | ||
diff --git a/arch/powerpc/platforms/pseries/ras.c b/arch/powerpc/platforms/pseries/ras.c index 41a3e9a039e..a4fc6da87c2 100644 --- a/arch/powerpc/platforms/pseries/ras.c +++ b/arch/powerpc/platforms/pseries/ras.c | |||
@@ -61,7 +61,6 @@ static int ras_check_exception_token; | |||
61 | 61 | ||
62 | #define EPOW_SENSOR_TOKEN 9 | 62 | #define EPOW_SENSOR_TOKEN 9 |
63 | #define EPOW_SENSOR_INDEX 0 | 63 | #define EPOW_SENSOR_INDEX 0 |
64 | #define RAS_VECTOR_OFFSET 0x500 | ||
65 | 64 | ||
66 | static irqreturn_t ras_epow_interrupt(int irq, void *dev_id); | 65 | static irqreturn_t ras_epow_interrupt(int irq, void *dev_id); |
67 | static irqreturn_t ras_error_interrupt(int irq, void *dev_id); | 66 | static irqreturn_t ras_error_interrupt(int irq, void *dev_id); |
@@ -121,7 +120,7 @@ static irqreturn_t ras_epow_interrupt(int irq, void *dev_id) | |||
121 | spin_lock(&ras_log_buf_lock); | 120 | spin_lock(&ras_log_buf_lock); |
122 | 121 | ||
123 | status = rtas_call(ras_check_exception_token, 6, 1, NULL, | 122 | status = rtas_call(ras_check_exception_token, 6, 1, NULL, |
124 | RAS_VECTOR_OFFSET, | 123 | RTAS_VECTOR_EXTERNAL_INTERRUPT, |
125 | irq_map[irq].hwirq, | 124 | irq_map[irq].hwirq, |
126 | RTAS_EPOW_WARNING | RTAS_POWERMGM_EVENTS, | 125 | RTAS_EPOW_WARNING | RTAS_POWERMGM_EVENTS, |
127 | critical, __pa(&ras_log_buf), | 126 | critical, __pa(&ras_log_buf), |
@@ -156,7 +155,7 @@ static irqreturn_t ras_error_interrupt(int irq, void *dev_id) | |||
156 | spin_lock(&ras_log_buf_lock); | 155 | spin_lock(&ras_log_buf_lock); |
157 | 156 | ||
158 | status = rtas_call(ras_check_exception_token, 6, 1, NULL, | 157 | status = rtas_call(ras_check_exception_token, 6, 1, NULL, |
159 | RAS_VECTOR_OFFSET, | 158 | RTAS_VECTOR_EXTERNAL_INTERRUPT, |
160 | irq_map[irq].hwirq, | 159 | irq_map[irq].hwirq, |
161 | RTAS_INTERNAL_ERROR, 1 /*Time Critical */, | 160 | RTAS_INTERNAL_ERROR, 1 /*Time Critical */, |
162 | __pa(&ras_log_buf), | 161 | __pa(&ras_log_buf), |
diff --git a/arch/powerpc/platforms/pseries/reconfig.c b/arch/powerpc/platforms/pseries/reconfig.c index 1a58637bcea..57ddbb43b33 100644 --- a/arch/powerpc/platforms/pseries/reconfig.c +++ b/arch/powerpc/platforms/pseries/reconfig.c | |||
@@ -118,12 +118,10 @@ static int pSeries_reconfig_add_node(const char *path, struct property *proplist | |||
118 | if (!np) | 118 | if (!np) |
119 | goto out_err; | 119 | goto out_err; |
120 | 120 | ||
121 | np->full_name = kmalloc(strlen(path) + 1, GFP_KERNEL); | 121 | np->full_name = kstrdup(path, GFP_KERNEL); |
122 | if (!np->full_name) | 122 | if (!np->full_name) |
123 | goto out_err; | 123 | goto out_err; |
124 | 124 | ||
125 | strcpy(np->full_name, path); | ||
126 | |||
127 | np->properties = proplist; | 125 | np->properties = proplist; |
128 | of_node_set_flag(np, OF_DYNAMIC); | 126 | of_node_set_flag(np, OF_DYNAMIC); |
129 | kref_init(&np->kref); | 127 | kref_init(&np->kref); |
diff --git a/arch/powerpc/platforms/pseries/suspend.c b/arch/powerpc/platforms/pseries/suspend.c new file mode 100644 index 00000000000..ed72098bb4e --- /dev/null +++ b/arch/powerpc/platforms/pseries/suspend.c | |||
@@ -0,0 +1,214 @@ | |||
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 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); | ||
diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c index f19d1946839..5b22b07c8f6 100644 --- a/arch/powerpc/platforms/pseries/xics.c +++ b/arch/powerpc/platforms/pseries/xics.c | |||
@@ -549,8 +549,6 @@ static irqreturn_t xics_ipi_dispatch(int cpu) | |||
549 | { | 549 | { |
550 | unsigned long *tgt = &per_cpu(xics_ipi_message, cpu); | 550 | unsigned long *tgt = &per_cpu(xics_ipi_message, cpu); |
551 | 551 | ||
552 | WARN_ON(cpu_is_offline(cpu)); | ||
553 | |||
554 | mb(); /* order mmio clearing qirr */ | 552 | mb(); /* order mmio clearing qirr */ |
555 | while (*tgt) { | 553 | while (*tgt) { |
556 | if (test_and_clear_bit(PPC_MSG_CALL_FUNCTION, tgt)) { | 554 | if (test_and_clear_bit(PPC_MSG_CALL_FUNCTION, tgt)) { |