Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/powerpc/cpufreq/pcr.c
39536 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2009 Nathan Whitehorn
5
* All rights reserved.
6
*
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions
9
* are met:
10
* 1. Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
* 2. Redistributions in binary form must reproduce the above copyright
13
* notice, this list of conditions and the following disclaimer in the
14
* documentation and/or other materials provided with the distribution.
15
*
16
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
* SUCH DAMAGE.
27
*/
28
29
#include <sys/param.h>
30
#include <sys/systm.h>
31
#include <sys/bus.h>
32
#include <sys/cpu.h>
33
#include <sys/kernel.h>
34
#include <sys/module.h>
35
36
#include <dev/ofw/ofw_bus.h>
37
38
#include "cpufreq_if.h"
39
40
struct pcr_softc {
41
device_t dev;
42
uint32_t pcr_vals[3];
43
int nmodes;
44
};
45
46
static void pcr_identify(driver_t *driver, device_t parent);
47
static int pcr_probe(device_t dev);
48
static int pcr_attach(device_t dev);
49
static int pcr_settings(device_t dev, struct cf_setting *sets, int *count);
50
static int pcr_set(device_t dev, const struct cf_setting *set);
51
static int pcr_get(device_t dev, struct cf_setting *set);
52
static int pcr_type(device_t dev, int *type);
53
54
static device_method_t pcr_methods[] = {
55
/* Device interface */
56
DEVMETHOD(device_identify, pcr_identify),
57
DEVMETHOD(device_probe, pcr_probe),
58
DEVMETHOD(device_attach, pcr_attach),
59
60
/* cpufreq interface */
61
DEVMETHOD(cpufreq_drv_set, pcr_set),
62
DEVMETHOD(cpufreq_drv_get, pcr_get),
63
DEVMETHOD(cpufreq_drv_type, pcr_type),
64
DEVMETHOD(cpufreq_drv_settings, pcr_settings),
65
{0, 0}
66
};
67
68
static driver_t pcr_driver = {
69
"pcr",
70
pcr_methods,
71
sizeof(struct pcr_softc)
72
};
73
74
DRIVER_MODULE(pcr, cpu, pcr_driver, 0, 0);
75
76
/*
77
* States
78
*/
79
80
#define PCR_TO_FREQ(a) ((a >> 17) & 3)
81
82
#define PCR_FULL 0
83
#define PCR_HALF 1
84
#define PCR_QUARTER 2 /* Only on 970MP */
85
86
#define PSR_RECEIVED (1ULL << 61)
87
#define PSR_COMPLETED (1ULL << 61)
88
89
/*
90
* SCOM addresses
91
*/
92
93
#define SCOM_PCR 0x0aa00100 /* Power Control Register */
94
#define SCOM_PCR_BIT 0x80000000 /* Data bit for PCR */
95
#define SCOM_PSR 0x40800100 /* Power Status Register */
96
97
/*
98
* SCOM Glue
99
*/
100
101
#define SCOMC_READ 0x00008000
102
#define SCOMC_WRITE 0x00000000
103
104
static void
105
write_scom(register_t address, uint64_t value)
106
{
107
register_t msr;
108
#ifndef __powerpc64__
109
register_t hi, lo, scratch;
110
#endif
111
112
msr = mfmsr();
113
mtmsr(msr & ~PSL_EE); isync();
114
115
#ifdef __powerpc64__
116
mtspr(SPR_SCOMD, value);
117
#else
118
hi = (value >> 32) & 0xffffffff;
119
lo = value & 0xffffffff;
120
mtspr64(SPR_SCOMD, hi, lo, scratch);
121
#endif
122
isync();
123
mtspr(SPR_SCOMC, address | SCOMC_WRITE);
124
isync();
125
126
mtmsr(msr); isync();
127
}
128
129
static uint64_t
130
read_scom(register_t address)
131
{
132
register_t msr;
133
uint64_t ret;
134
135
msr = mfmsr();
136
mtmsr(msr & ~PSL_EE); isync();
137
138
mtspr(SPR_SCOMC, address | SCOMC_READ);
139
isync();
140
141
__asm __volatile ("mfspr %0,%1;"
142
" mr %0+1, %0; srdi %0,%0,32" : "=r" (ret) : "K" (SPR_SCOMD));
143
144
(void)mfspr(SPR_SCOMC); /* Complete transcation */
145
146
mtmsr(msr); isync();
147
148
return (ret);
149
}
150
151
static void
152
pcr_identify(driver_t *driver, device_t parent)
153
{
154
uint16_t vers;
155
vers = mfpvr() >> 16;
156
157
/* Check for an IBM 970-class CPU */
158
switch (vers) {
159
case IBM970FX:
160
case IBM970GX:
161
case IBM970MP:
162
break;
163
default:
164
return;
165
}
166
167
/* Make sure we're not being doubly invoked. */
168
if (device_find_child(parent, "pcr", DEVICE_UNIT_ANY) != NULL)
169
return;
170
171
/*
172
* We attach a child for every CPU since settings need to
173
* be performed on every CPU in the SMP case.
174
*/
175
if (BUS_ADD_CHILD(parent, 10, "pcr", DEVICE_UNIT_ANY) == NULL)
176
device_printf(parent, "add pcr child failed\n");
177
}
178
179
static int
180
pcr_probe(device_t dev)
181
{
182
if (resource_disabled("pcr", 0))
183
return (ENXIO);
184
185
device_set_desc(dev, "PPC 970 Power Control Register");
186
return (0);
187
}
188
189
static int
190
pcr_attach(device_t dev)
191
{
192
struct pcr_softc *sc;
193
phandle_t cpu;
194
uint32_t modes[3];
195
int i;
196
197
sc = device_get_softc(dev);
198
sc->dev = dev;
199
200
cpu = ofw_bus_get_node(device_get_parent(dev));
201
202
if (cpu <= 0) {
203
device_printf(dev,"No CPU device tree node!\n");
204
return (ENXIO);
205
}
206
207
if (OF_getproplen(cpu, "power-mode-data") <= 0) {
208
/* Use the first CPU's node */
209
cpu = OF_child(OF_parent(cpu));
210
}
211
212
/*
213
* Collect the PCR values for each mode from the device tree.
214
* These include bus timing information, and so cannot be
215
* directly computed.
216
*/
217
sc->nmodes = OF_getproplen(cpu, "power-mode-data");
218
if (sc->nmodes <= 0 || sc->nmodes > sizeof(sc->pcr_vals)) {
219
device_printf(dev,"No power mode data in device tree!\n");
220
return (ENXIO);
221
}
222
OF_getprop(cpu, "power-mode-data", modes, sc->nmodes);
223
sc->nmodes /= sizeof(modes[0]);
224
225
/* Sort the modes */
226
for (i = 0; i < sc->nmodes; i++)
227
sc->pcr_vals[PCR_TO_FREQ(modes[i])] = modes[i];
228
229
cpufreq_register(dev);
230
return (0);
231
}
232
233
static int
234
pcr_settings(device_t dev, struct cf_setting *sets, int *count)
235
{
236
struct pcr_softc *sc;
237
238
sc = device_get_softc(dev);
239
if (sets == NULL || count == NULL)
240
return (EINVAL);
241
if (*count < sc->nmodes)
242
return (E2BIG);
243
244
/* Return a list of valid settings for this driver. */
245
memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * sc->nmodes);
246
247
sets[0].freq = 10000; sets[0].dev = dev;
248
sets[1].freq = 5000; sets[1].dev = dev;
249
if (sc->nmodes > 2) {
250
sets[2].freq = 2500;
251
sets[2].dev = dev;
252
}
253
*count = sc->nmodes;
254
255
return (0);
256
}
257
258
static int
259
pcr_set(device_t dev, const struct cf_setting *set)
260
{
261
struct pcr_softc *sc;
262
register_t pcr, msr;
263
uint64_t psr;
264
265
if (set == NULL)
266
return (EINVAL);
267
sc = device_get_softc(dev);
268
269
/* Construct the new PCR */
270
271
pcr = SCOM_PCR_BIT;
272
273
if (set->freq == 10000)
274
pcr |= sc->pcr_vals[0];
275
else if (set->freq == 5000)
276
pcr |= sc->pcr_vals[1];
277
else if (set->freq == 2500)
278
pcr |= sc->pcr_vals[2];
279
280
msr = mfmsr();
281
mtmsr(msr & ~PSL_EE); isync();
282
283
/* 970MP requires PCR and PCRH to be cleared first */
284
285
write_scom(SCOM_PCR,0); /* Clear PCRH */
286
write_scom(SCOM_PCR,SCOM_PCR_BIT); /* Clear PCR */
287
288
/* Set PCR */
289
290
write_scom(SCOM_PCR, pcr);
291
292
/* Wait for completion */
293
294
do {
295
DELAY(100);
296
psr = read_scom(SCOM_PSR);
297
} while ((psr & PSR_RECEIVED) && !(psr & PSR_COMPLETED));
298
299
mtmsr(msr); isync();
300
301
return (0);
302
}
303
304
static int
305
pcr_get(device_t dev, struct cf_setting *set)
306
{
307
uint64_t psr;
308
309
if (set == NULL)
310
return (EINVAL);
311
312
memset(set, CPUFREQ_VAL_UNKNOWN, sizeof(*set));
313
314
psr = read_scom(SCOM_PSR);
315
316
/* We want bits 6 and 7 */
317
psr = (psr >> 56) & 3;
318
319
set->freq = 10000;
320
if (psr == PCR_HALF)
321
set->freq = 5000;
322
else if (psr == PCR_QUARTER)
323
set->freq = 2500;
324
325
set->dev = dev;
326
327
return (0);
328
}
329
330
static int
331
pcr_type(device_t dev, int *type)
332
{
333
334
if (type == NULL)
335
return (EINVAL);
336
337
*type = CPUFREQ_TYPE_RELATIVE;
338
return (0);
339
}
340
341