1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * RCPM(Run Control/Power Management) support 4 * 5 * Copyright 2012-2015 Freescale Semiconductor Inc. 6 * 7 * Author: Chenhui Zhao <chenhui.zhao@freescale.com> 8 */ 9 10 #define pr_fmt(fmt) "%s: " fmt, __func__ 11 12 #include <linux/types.h> 13 #include <linux/errno.h> 14 #include <linux/of_address.h> 15 #include <linux/export.h> 16 17 #include <asm/io.h> 18 #include <linux/fsl/guts.h> 19 #include <asm/cputhreads.h> 20 #include <asm/fsl_pm.h> 21 #include <asm/smp.h> 22 23 static struct ccsr_rcpm_v1 __iomem *rcpm_v1_regs; 24 static struct ccsr_rcpm_v2 __iomem *rcpm_v2_regs; 25 static unsigned int fsl_supported_pm_modes; 26 27 static void rcpm_v1_irq_mask(int cpu) 28 { 29 int hw_cpu = get_hard_smp_processor_id(cpu); 30 unsigned int mask = 1 << hw_cpu; 31 32 setbits32(&rcpm_v1_regs->cpmimr, mask); 33 setbits32(&rcpm_v1_regs->cpmcimr, mask); 34 setbits32(&rcpm_v1_regs->cpmmcmr, mask); 35 setbits32(&rcpm_v1_regs->cpmnmimr, mask); 36 } 37 38 static void rcpm_v2_irq_mask(int cpu) 39 { 40 int hw_cpu = get_hard_smp_processor_id(cpu); 41 unsigned int mask = 1 << hw_cpu; 42 43 setbits32(&rcpm_v2_regs->tpmimr0, mask); 44 setbits32(&rcpm_v2_regs->tpmcimr0, mask); 45 setbits32(&rcpm_v2_regs->tpmmcmr0, mask); 46 setbits32(&rcpm_v2_regs->tpmnmimr0, mask); 47 } 48 49 static void rcpm_v1_irq_unmask(int cpu) 50 { 51 int hw_cpu = get_hard_smp_processor_id(cpu); 52 unsigned int mask = 1 << hw_cpu; 53 54 clrbits32(&rcpm_v1_regs->cpmimr, mask); 55 clrbits32(&rcpm_v1_regs->cpmcimr, mask); 56 clrbits32(&rcpm_v1_regs->cpmmcmr, mask); 57 clrbits32(&rcpm_v1_regs->cpmnmimr, mask); 58 } 59 60 static void rcpm_v2_irq_unmask(int cpu) 61 { 62 int hw_cpu = get_hard_smp_processor_id(cpu); 63 unsigned int mask = 1 << hw_cpu; 64 65 clrbits32(&rcpm_v2_regs->tpmimr0, mask); 66 clrbits32(&rcpm_v2_regs->tpmcimr0, mask); 67 clrbits32(&rcpm_v2_regs->tpmmcmr0, mask); 68 clrbits32(&rcpm_v2_regs->tpmnmimr0, mask); 69 } 70 71 static void rcpm_v1_set_ip_power(bool enable, u32 mask) 72 { 73 if (enable) 74 setbits32(&rcpm_v1_regs->ippdexpcr, mask); 75 else 76 clrbits32(&rcpm_v1_regs->ippdexpcr, mask); 77 } 78 79 static void rcpm_v2_set_ip_power(bool enable, u32 mask) 80 { 81 if (enable) 82 setbits32(&rcpm_v2_regs->ippdexpcr[0], mask); 83 else 84 clrbits32(&rcpm_v2_regs->ippdexpcr[0], mask); 85 } 86 87 static void rcpm_v1_cpu_enter_state(int cpu, int state) 88 { 89 int hw_cpu = get_hard_smp_processor_id(cpu); 90 unsigned int mask = 1 << hw_cpu; 91 92 switch (state) { 93 case E500_PM_PH10: 94 setbits32(&rcpm_v1_regs->cdozcr, mask); 95 break; 96 case E500_PM_PH15: 97 setbits32(&rcpm_v1_regs->cnapcr, mask); 98 break; 99 default: 100 pr_warn("Unknown cpu PM state (%d)\n", state); 101 break; 102 } 103 } 104 105 static void rcpm_v2_cpu_enter_state(int cpu, int state) 106 { 107 int hw_cpu = get_hard_smp_processor_id(cpu); 108 u32 mask = 1 << cpu_core_index_of_thread(cpu); 109 110 switch (state) { 111 case E500_PM_PH10: 112 /* one bit corresponds to one thread for PH10 of 6500 */ 113 setbits32(&rcpm_v2_regs->tph10setr0, 1 << hw_cpu); 114 break; 115 case E500_PM_PH15: 116 setbits32(&rcpm_v2_regs->pcph15setr, mask); 117 break; 118 case E500_PM_PH20: 119 setbits32(&rcpm_v2_regs->pcph20setr, mask); 120 break; 121 case E500_PM_PH30: 122 setbits32(&rcpm_v2_regs->pcph30setr, mask); 123 break; 124 default: 125 pr_warn("Unknown cpu PM state (%d)\n", state); 126 } 127 } 128 129 static void rcpm_v1_cpu_die(int cpu) 130 { 131 rcpm_v1_cpu_enter_state(cpu, E500_PM_PH15); 132 } 133 134 #ifdef CONFIG_PPC64 135 static void qoriq_disable_thread(int cpu) 136 { 137 int thread = cpu_thread_in_core(cpu); 138 139 book3e_stop_thread(thread); 140 } 141 #endif 142 143 static void rcpm_v2_cpu_die(int cpu) 144 { 145 #ifdef CONFIG_PPC64 146 int primary; 147 148 if (threads_per_core == 2) { 149 primary = cpu_first_thread_sibling(cpu); 150 if (cpu_is_offline(primary) && cpu_is_offline(primary + 1)) { 151 /* if both threads are offline, put the cpu in PH20 */ 152 rcpm_v2_cpu_enter_state(cpu, E500_PM_PH20); 153 } else { 154 /* if only one thread is offline, disable the thread */ 155 qoriq_disable_thread(cpu); 156 } 157 } 158 #endif 159 160 if (threads_per_core == 1) 161 rcpm_v2_cpu_enter_state(cpu, E500_PM_PH20); 162 } 163 164 static void rcpm_v1_cpu_exit_state(int cpu, int state) 165 { 166 int hw_cpu = get_hard_smp_processor_id(cpu); 167 unsigned int mask = 1 << hw_cpu; 168 169 switch (state) { 170 case E500_PM_PH10: 171 clrbits32(&rcpm_v1_regs->cdozcr, mask); 172 break; 173 case E500_PM_PH15: 174 clrbits32(&rcpm_v1_regs->cnapcr, mask); 175 break; 176 default: 177 pr_warn("Unknown cpu PM state (%d)\n", state); 178 break; 179 } 180 } 181 182 static void rcpm_v1_cpu_up_prepare(int cpu) 183 { 184 rcpm_v1_cpu_exit_state(cpu, E500_PM_PH15); 185 rcpm_v1_irq_unmask(cpu); 186 } 187 188 static void rcpm_v2_cpu_exit_state(int cpu, int state) 189 { 190 int hw_cpu = get_hard_smp_processor_id(cpu); 191 u32 mask = 1 << cpu_core_index_of_thread(cpu); 192 193 switch (state) { 194 case E500_PM_PH10: 195 setbits32(&rcpm_v2_regs->tph10clrr0, 1 << hw_cpu); 196 break; 197 case E500_PM_PH15: 198 setbits32(&rcpm_v2_regs->pcph15clrr, mask); 199 break; 200 case E500_PM_PH20: 201 setbits32(&rcpm_v2_regs->pcph20clrr, mask); 202 break; 203 case E500_PM_PH30: 204 setbits32(&rcpm_v2_regs->pcph30clrr, mask); 205 break; 206 default: 207 pr_warn("Unknown cpu PM state (%d)\n", state); 208 } 209 } 210 211 static void rcpm_v2_cpu_up_prepare(int cpu) 212 { 213 rcpm_v2_cpu_exit_state(cpu, E500_PM_PH20); 214 rcpm_v2_irq_unmask(cpu); 215 } 216 217 static int rcpm_v1_plat_enter_state(int state) 218 { 219 u32 *pmcsr_reg = &rcpm_v1_regs->powmgtcsr; 220 int ret = 0; 221 int result; 222 223 switch (state) { 224 case PLAT_PM_SLEEP: 225 setbits32(pmcsr_reg, RCPM_POWMGTCSR_SLP); 226 227 /* Upon resume, wait for RCPM_POWMGTCSR_SLP bit to be clear. */ 228 result = spin_event_timeout( 229 !(in_be32(pmcsr_reg) & RCPM_POWMGTCSR_SLP), 10000, 10); 230 if (!result) { 231 pr_err("timeout waiting for SLP bit to be cleared\n"); 232 ret = -ETIMEDOUT; 233 } 234 break; 235 default: 236 pr_warn("Unknown platform PM state (%d)", state); 237 ret = -EINVAL; 238 } 239 240 return ret; 241 } 242 243 static int rcpm_v2_plat_enter_state(int state) 244 { 245 u32 *pmcsr_reg = &rcpm_v2_regs->powmgtcsr; 246 int ret = 0; 247 int result; 248 249 switch (state) { 250 case PLAT_PM_LPM20: 251 /* clear previous LPM20 status */ 252 setbits32(pmcsr_reg, RCPM_POWMGTCSR_P_LPM20_ST); 253 /* enter LPM20 status */ 254 setbits32(pmcsr_reg, RCPM_POWMGTCSR_LPM20_RQ); 255 256 /* At this point, the device is in LPM20 status. */ 257 258 /* resume ... */ 259 result = spin_event_timeout( 260 !(in_be32(pmcsr_reg) & RCPM_POWMGTCSR_LPM20_ST), 10000, 10); 261 if (!result) { 262 pr_err("timeout waiting for LPM20 bit to be cleared\n"); 263 ret = -ETIMEDOUT; 264 } 265 break; 266 default: 267 pr_warn("Unknown platform PM state (%d)\n", state); 268 ret = -EINVAL; 269 } 270 271 return ret; 272 } 273 274 static int rcpm_v1_plat_enter_sleep(void) 275 { 276 return rcpm_v1_plat_enter_state(PLAT_PM_SLEEP); 277 } 278 279 static int rcpm_v2_plat_enter_sleep(void) 280 { 281 return rcpm_v2_plat_enter_state(PLAT_PM_LPM20); 282 } 283 284 static void rcpm_common_freeze_time_base(u32 *tben_reg, int freeze) 285 { 286 static u32 mask; 287 288 if (freeze) { 289 mask = in_be32(tben_reg); 290 clrbits32(tben_reg, mask); 291 } else { 292 setbits32(tben_reg, mask); 293 } 294 295 /* read back to push the previous write */ 296 in_be32(tben_reg); 297 } 298 299 static void rcpm_v1_freeze_time_base(bool freeze) 300 { 301 rcpm_common_freeze_time_base(&rcpm_v1_regs->ctbenr, freeze); 302 } 303 304 static void rcpm_v2_freeze_time_base(bool freeze) 305 { 306 rcpm_common_freeze_time_base(&rcpm_v2_regs->pctbenr, freeze); 307 } 308 309 static unsigned int rcpm_get_pm_modes(void) 310 { 311 return fsl_supported_pm_modes; 312 } 313 314 static const struct fsl_pm_ops qoriq_rcpm_v1_ops = { 315 .irq_mask = rcpm_v1_irq_mask, 316 .irq_unmask = rcpm_v1_irq_unmask, 317 .cpu_enter_state = rcpm_v1_cpu_enter_state, 318 .cpu_exit_state = rcpm_v1_cpu_exit_state, 319 .cpu_up_prepare = rcpm_v1_cpu_up_prepare, 320 .cpu_die = rcpm_v1_cpu_die, 321 .plat_enter_sleep = rcpm_v1_plat_enter_sleep, 322 .set_ip_power = rcpm_v1_set_ip_power, 323 .freeze_time_base = rcpm_v1_freeze_time_base, 324 .get_pm_modes = rcpm_get_pm_modes, 325 }; 326 327 static const struct fsl_pm_ops qoriq_rcpm_v2_ops = { 328 .irq_mask = rcpm_v2_irq_mask, 329 .irq_unmask = rcpm_v2_irq_unmask, 330 .cpu_enter_state = rcpm_v2_cpu_enter_state, 331 .cpu_exit_state = rcpm_v2_cpu_exit_state, 332 .cpu_up_prepare = rcpm_v2_cpu_up_prepare, 333 .cpu_die = rcpm_v2_cpu_die, 334 .plat_enter_sleep = rcpm_v2_plat_enter_sleep, 335 .set_ip_power = rcpm_v2_set_ip_power, 336 .freeze_time_base = rcpm_v2_freeze_time_base, 337 .get_pm_modes = rcpm_get_pm_modes, 338 }; 339 340 static const struct of_device_id rcpm_matches[] = { 341 { 342 .compatible = "fsl,qoriq-rcpm-1.0", 343 .data = &qoriq_rcpm_v1_ops, 344 }, 345 { 346 .compatible = "fsl,qoriq-rcpm-2.0", 347 .data = &qoriq_rcpm_v2_ops, 348 }, 349 { 350 .compatible = "fsl,qoriq-rcpm-2.1", 351 .data = &qoriq_rcpm_v2_ops, 352 }, 353 {}, 354 }; 355 356 int __init fsl_rcpm_init(void) 357 { 358 struct device_node *np; 359 const struct of_device_id *match; 360 void __iomem *base; 361 362 np = of_find_matching_node_and_match(NULL, rcpm_matches, &match); 363 if (!np) 364 return 0; 365 366 base = of_iomap(np, 0); 367 of_node_put(np); 368 if (!base) { 369 pr_err("of_iomap() error.\n"); 370 return -ENOMEM; 371 } 372 373 rcpm_v1_regs = base; 374 rcpm_v2_regs = base; 375 376 /* support sleep by default */ 377 fsl_supported_pm_modes = FSL_PM_SLEEP; 378 379 qoriq_pm_ops = match->data; 380 381 return 0; 382 } 383
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.