Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/powerpc/sysdev/fsl_rcpm.c
26424 views
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 <[email protected]>
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
384