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