xref: /freebsd/sys/powerpc/cpufreq/pcr.c (revision 61e21613)
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", -1) != 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", -1) == 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