|
13 | 13 | #include <linux/mm.h>
|
14 | 14 | #include <linux/slab.h>
|
15 | 15 | #include <linux/of.h>
|
| 16 | +#include <linux/device.h> |
| 17 | +#include <linux/cpu.h> |
16 | 18 |
|
17 | 19 | #include <asm/firmware.h>
|
18 | 20 | #include <asm/opal.h>
|
@@ -137,6 +139,96 @@ u32 pnv_get_supported_cpuidle_states(void)
|
137 | 139 | }
|
138 | 140 | EXPORT_SYMBOL_GPL(pnv_get_supported_cpuidle_states);
|
139 | 141 |
|
| 142 | + |
| 143 | +static void pnv_fastsleep_workaround_apply(void *info) |
| 144 | + |
| 145 | +{ |
| 146 | + int rc; |
| 147 | + int *err = info; |
| 148 | + |
| 149 | + rc = opal_config_cpu_idle_state(OPAL_CONFIG_IDLE_FASTSLEEP, |
| 150 | + OPAL_CONFIG_IDLE_APPLY); |
| 151 | + if (rc) |
| 152 | + *err = 1; |
| 153 | +} |
| 154 | + |
| 155 | +/* |
| 156 | + * Used to store fastsleep workaround state |
| 157 | + * 0 - Workaround applied/undone at fastsleep entry/exit path (Default) |
| 158 | + * 1 - Workaround applied once, never undone. |
| 159 | + */ |
| 160 | +static u8 fastsleep_workaround_applyonce; |
| 161 | + |
| 162 | +static ssize_t show_fastsleep_workaround_applyonce(struct device *dev, |
| 163 | + struct device_attribute *attr, char *buf) |
| 164 | +{ |
| 165 | + return sprintf(buf, "%u\n", fastsleep_workaround_applyonce); |
| 166 | +} |
| 167 | + |
| 168 | +static ssize_t store_fastsleep_workaround_applyonce(struct device *dev, |
| 169 | + struct device_attribute *attr, const char *buf, |
| 170 | + size_t count) |
| 171 | +{ |
| 172 | + cpumask_t primary_thread_mask; |
| 173 | + int err; |
| 174 | + u8 val; |
| 175 | + |
| 176 | + if (kstrtou8(buf, 0, &val) || val != 1) |
| 177 | + return -EINVAL; |
| 178 | + |
| 179 | + if (fastsleep_workaround_applyonce == 1) |
| 180 | + return count; |
| 181 | + |
| 182 | + /* |
| 183 | + * fastsleep_workaround_applyonce = 1 implies |
| 184 | + * fastsleep workaround needs to be left in 'applied' state on all |
| 185 | + * the cores. Do this by- |
| 186 | + * 1. Patching out the call to 'undo' workaround in fastsleep exit path |
| 187 | + * 2. Sending ipi to all the cores which have atleast one online thread |
| 188 | + * 3. Patching out the call to 'apply' workaround in fastsleep entry |
| 189 | + * path |
| 190 | + * There is no need to send ipi to cores which have all threads |
| 191 | + * offlined, as last thread of the core entering fastsleep or deeper |
| 192 | + * state would have applied workaround. |
| 193 | + */ |
| 194 | + err = patch_instruction( |
| 195 | + (unsigned int *)pnv_fastsleep_workaround_at_exit, |
| 196 | + PPC_INST_NOP); |
| 197 | + if (err) { |
| 198 | + pr_err("fastsleep_workaround_applyonce change failed while patching pnv_fastsleep_workaround_at_exit"); |
| 199 | + goto fail; |
| 200 | + } |
| 201 | + |
| 202 | + get_online_cpus(); |
| 203 | + primary_thread_mask = cpu_online_cores_map(); |
| 204 | + on_each_cpu_mask(&primary_thread_mask, |
| 205 | + pnv_fastsleep_workaround_apply, |
| 206 | + &err, 1); |
| 207 | + put_online_cpus(); |
| 208 | + if (err) { |
| 209 | + pr_err("fastsleep_workaround_applyonce change failed while running pnv_fastsleep_workaround_apply"); |
| 210 | + goto fail; |
| 211 | + } |
| 212 | + |
| 213 | + err = patch_instruction( |
| 214 | + (unsigned int *)pnv_fastsleep_workaround_at_entry, |
| 215 | + PPC_INST_NOP); |
| 216 | + if (err) { |
| 217 | + pr_err("fastsleep_workaround_applyonce change failed while patching pnv_fastsleep_workaround_at_entry"); |
| 218 | + goto fail; |
| 219 | + } |
| 220 | + |
| 221 | + fastsleep_workaround_applyonce = 1; |
| 222 | + |
| 223 | + return count; |
| 224 | +fail: |
| 225 | + return -EIO; |
| 226 | +} |
| 227 | + |
| 228 | +static DEVICE_ATTR(fastsleep_workaround_applyonce, 0600, |
| 229 | + show_fastsleep_workaround_applyonce, |
| 230 | + store_fastsleep_workaround_applyonce); |
| 231 | + |
140 | 232 | static int __init pnv_init_idle_states(void)
|
141 | 233 | {
|
142 | 234 | struct device_node *power_mgt;
|
@@ -181,7 +273,16 @@ static int __init pnv_init_idle_states(void)
|
181 | 273 | patch_instruction(
|
182 | 274 | (unsigned int *)pnv_fastsleep_workaround_at_exit,
|
183 | 275 | PPC_INST_NOP);
|
| 276 | + } else { |
| 277 | + /* |
| 278 | + * OPAL_PM_SLEEP_ENABLED_ER1 is set. It indicates that |
| 279 | + * workaround is needed to use fastsleep. Provide sysfs |
| 280 | + * control to choose how this workaround has to be applied. |
| 281 | + */ |
| 282 | + device_create_file(cpu_subsys.dev_root, |
| 283 | + &dev_attr_fastsleep_workaround_applyonce); |
184 | 284 | }
|
| 285 | + |
185 | 286 | pnv_alloc_idle_core_states();
|
186 | 287 | out_free:
|
187 | 288 | kfree(flags);
|
|
0 commit comments