pcr.c revision 194679
1194679Snwhitehorn/*-
2194679Snwhitehorn * Copyright (c) 2009 Nathan Whitehorn
3194679Snwhitehorn * All rights reserved.
4194679Snwhitehorn *
5194679Snwhitehorn * Redistribution and use in source and binary forms, with or without
6194679Snwhitehorn * modification, are permitted provided that the following conditions
7194679Snwhitehorn * are met:
8194679Snwhitehorn * 1. Redistributions of source code must retain the above copyright
9194679Snwhitehorn *    notice, this list of conditions and the following disclaimer.
10194679Snwhitehorn * 2. Redistributions in binary form must reproduce the above copyright
11194679Snwhitehorn *    notice, this list of conditions and the following disclaimer in the
12194679Snwhitehorn *    documentation and/or other materials provided with the distribution.
13194679Snwhitehorn *
14194679Snwhitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15194679Snwhitehorn * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16194679Snwhitehorn * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17194679Snwhitehorn * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18194679Snwhitehorn * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19194679Snwhitehorn * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20194679Snwhitehorn * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
21194679Snwhitehorn * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22194679Snwhitehorn * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23194679Snwhitehorn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24194679Snwhitehorn * SUCH DAMAGE.
25194679Snwhitehorn */
26194679Snwhitehorn
27194679Snwhitehorn#include <sys/cdefs.h>
28194679Snwhitehorn__FBSDID("$FreeBSD: head/sys/powerpc/cpufreq/pcr.c 194679 2009-06-23 04:28:32Z nwhitehorn $");
29194679Snwhitehorn
30194679Snwhitehorn#include <sys/param.h>
31194679Snwhitehorn#include <sys/systm.h>
32194679Snwhitehorn#include <sys/bus.h>
33194679Snwhitehorn#include <sys/cpu.h>
34194679Snwhitehorn#include <sys/kernel.h>
35194679Snwhitehorn#include <sys/module.h>
36194679Snwhitehorn
37194679Snwhitehorn#include <dev/ofw/ofw_bus.h>
38194679Snwhitehorn
39194679Snwhitehorn#include "cpufreq_if.h"
40194679Snwhitehorn
41194679Snwhitehornstruct pcr_softc {
42194679Snwhitehorn	device_t dev;
43194679Snwhitehorn	uint32_t pcr_vals[3];
44194679Snwhitehorn	int nmodes;
45194679Snwhitehorn};
46194679Snwhitehorn
47194679Snwhitehornstatic void	pcr_identify(driver_t *driver, device_t parent);
48194679Snwhitehornstatic int	pcr_probe(device_t dev);
49194679Snwhitehornstatic int	pcr_attach(device_t dev);
50194679Snwhitehornstatic int	pcr_settings(device_t dev, struct cf_setting *sets, int *count);
51194679Snwhitehornstatic int	pcr_set(device_t dev, const struct cf_setting *set);
52194679Snwhitehornstatic int	pcr_get(device_t dev, struct cf_setting *set);
53194679Snwhitehornstatic int	pcr_type(device_t dev, int *type);
54194679Snwhitehorn
55194679Snwhitehornstatic device_method_t pcr_methods[] = {
56194679Snwhitehorn	/* Device interface */
57194679Snwhitehorn	DEVMETHOD(device_identify,	pcr_identify),
58194679Snwhitehorn	DEVMETHOD(device_probe,		pcr_probe),
59194679Snwhitehorn	DEVMETHOD(device_attach,	pcr_attach),
60194679Snwhitehorn
61194679Snwhitehorn	/* cpufreq interface */
62194679Snwhitehorn	DEVMETHOD(cpufreq_drv_set,	pcr_set),
63194679Snwhitehorn	DEVMETHOD(cpufreq_drv_get,	pcr_get),
64194679Snwhitehorn	DEVMETHOD(cpufreq_drv_type,	pcr_type),
65194679Snwhitehorn	DEVMETHOD(cpufreq_drv_settings,	pcr_settings),
66194679Snwhitehorn
67194679Snwhitehorn	{0, 0}
68194679Snwhitehorn};
69194679Snwhitehorn
70194679Snwhitehornstatic driver_t pcr_driver = {
71194679Snwhitehorn	"pcr",
72194679Snwhitehorn	pcr_methods,
73194679Snwhitehorn	sizeof(struct pcr_softc)
74194679Snwhitehorn};
75194679Snwhitehorn
76194679Snwhitehornstatic devclass_t pcr_devclass;
77194679SnwhitehornDRIVER_MODULE(pcr, cpu, pcr_driver, pcr_devclass, 0, 0);
78194679Snwhitehorn
79194679Snwhitehorn/*
80194679Snwhitehorn * States
81194679Snwhitehorn */
82194679Snwhitehorn
83194679Snwhitehorn#define PCR_TO_FREQ(a)	((a >> 17) & 3)
84194679Snwhitehorn
85194679Snwhitehorn#define	PCR_FULL	0
86194679Snwhitehorn#define PCR_HALF	1
87194679Snwhitehorn#define PCR_QUARTER	2		/* Only on 970MP */
88194679Snwhitehorn
89194679Snwhitehorn#define PSR_RECEIVED	(1ULL << 61)
90194679Snwhitehorn#define PSR_COMPLETED	(1ULL << 61)
91194679Snwhitehorn
92194679Snwhitehorn/*
93194679Snwhitehorn * SCOM addresses
94194679Snwhitehorn */
95194679Snwhitehorn
96194679Snwhitehorn#define	SCOM_PCR	0x0aa00100	/* Power Control Register */
97194679Snwhitehorn#define SCOM_PCR_BIT	0x80000000	/* Data bit for PCR */
98194679Snwhitehorn#define SCOM_PSR	0x40800100	/* Power Status Register */
99194679Snwhitehorn
100194679Snwhitehorn/*
101194679Snwhitehorn * SCOM Glue
102194679Snwhitehorn */
103194679Snwhitehorn
104194679Snwhitehorn#define SCOMC_READ	0x00008000
105194679Snwhitehorn#define SCOMC_WRITE	0x00000000
106194679Snwhitehorn
107194679Snwhitehornstatic void
108194679Snwhitehornwrite_scom(register_t address, uint64_t value)
109194679Snwhitehorn{
110194679Snwhitehorn	register_t msr;
111194679Snwhitehorn	register_t hi, lo, scratch;
112194679Snwhitehorn
113194679Snwhitehorn	hi = (value >> 32) & 0xffffffff;
114194679Snwhitehorn	lo = value & 0xffffffff;
115194679Snwhitehorn
116194679Snwhitehorn	msr = mfmsr();
117194679Snwhitehorn	mtmsr(msr & ~PSL_EE); isync();
118194679Snwhitehorn
119194679Snwhitehorn	mtspr64(SPR_SCOMD, hi, lo, scratch);
120194679Snwhitehorn	isync();
121194679Snwhitehorn	mtspr(SPR_SCOMC, address | SCOMC_WRITE);
122194679Snwhitehorn	isync();
123194679Snwhitehorn
124194679Snwhitehorn	mtmsr(msr); isync();
125194679Snwhitehorn}
126194679Snwhitehorn
127194679Snwhitehornstatic uint64_t
128194679Snwhitehornread_scom(register_t address)
129194679Snwhitehorn{
130194679Snwhitehorn	register_t msr;
131194679Snwhitehorn	uint64_t ret;
132194679Snwhitehorn
133194679Snwhitehorn	msr = mfmsr();
134194679Snwhitehorn	mtmsr(msr & ~PSL_EE); isync();
135194679Snwhitehorn
136194679Snwhitehorn	mtspr(SPR_SCOMC, address | SCOMC_READ);
137194679Snwhitehorn	isync();
138194679Snwhitehorn
139194679Snwhitehorn	__asm __volatile ("mfspr %0,%1;"
140194679Snwhitehorn            " mr %0+1, %0; srdi %0,%0,32" : "=r" (ret) : "K" (SPR_SCOMD));
141194679Snwhitehorn
142194679Snwhitehorn	(void)mfspr(SPR_SCOMC); /* Complete transcation */
143194679Snwhitehorn
144194679Snwhitehorn	mtmsr(msr); isync();
145194679Snwhitehorn
146194679Snwhitehorn	return (ret);
147194679Snwhitehorn}
148194679Snwhitehorn
149194679Snwhitehornstatic void
150194679Snwhitehornpcr_identify(driver_t *driver, device_t parent)
151194679Snwhitehorn{
152194679Snwhitehorn	uint16_t vers;
153194679Snwhitehorn	vers = mfpvr() >> 16;
154194679Snwhitehorn
155194679Snwhitehorn	/* Check for an IBM 970-class CPU */
156194679Snwhitehorn	switch (vers) {
157194679Snwhitehorn		case IBM970FX:
158194679Snwhitehorn		case IBM970GX:
159194679Snwhitehorn		case IBM970MP:
160194679Snwhitehorn			break;
161194679Snwhitehorn		default:
162194679Snwhitehorn			return;
163194679Snwhitehorn	}
164194679Snwhitehorn
165194679Snwhitehorn	/* Make sure we're not being doubly invoked. */
166194679Snwhitehorn	if (device_find_child(parent, "pcr", -1) != NULL)
167194679Snwhitehorn		return;
168194679Snwhitehorn
169194679Snwhitehorn	/*
170194679Snwhitehorn	 * We attach a child for every CPU since settings need to
171194679Snwhitehorn	 * be performed on every CPU in the SMP case.
172194679Snwhitehorn	 */
173194679Snwhitehorn	if (BUS_ADD_CHILD(parent, 10, "pcr", -1) == NULL)
174194679Snwhitehorn		device_printf(parent, "add pcr child failed\n");
175194679Snwhitehorn}
176194679Snwhitehorn
177194679Snwhitehornstatic int
178194679Snwhitehornpcr_probe(device_t dev)
179194679Snwhitehorn{
180194679Snwhitehorn	if (resource_disabled("pcr", 0))
181194679Snwhitehorn		return (ENXIO);
182194679Snwhitehorn
183194679Snwhitehorn	device_set_desc(dev, "PPC 970 Power Control Register");
184194679Snwhitehorn	return (0);
185194679Snwhitehorn}
186194679Snwhitehorn
187194679Snwhitehornstatic int
188194679Snwhitehornpcr_attach(device_t dev)
189194679Snwhitehorn{
190194679Snwhitehorn	struct pcr_softc *sc;
191194679Snwhitehorn	phandle_t cpu;
192194679Snwhitehorn	uint32_t modes[3];
193194679Snwhitehorn	int i;
194194679Snwhitehorn
195194679Snwhitehorn	sc = device_get_softc(dev);
196194679Snwhitehorn	sc->dev = dev;
197194679Snwhitehorn
198194679Snwhitehorn	cpu = ofw_bus_get_node(device_get_parent(dev));
199194679Snwhitehorn
200194679Snwhitehorn	if (cpu <= 0) {
201194679Snwhitehorn		device_printf(dev,"No CPU device tree node!\n");
202194679Snwhitehorn		return (ENXIO);
203194679Snwhitehorn	}
204194679Snwhitehorn
205194679Snwhitehorn	/*
206194679Snwhitehorn	 * Collect the PCR values for each mode from the device tree.
207194679Snwhitehorn	 * These include bus timing information, and so cannot be
208194679Snwhitehorn	 * directly computed.
209194679Snwhitehorn	 */
210194679Snwhitehorn	sc->nmodes = OF_getproplen(cpu, "power-mode-data");
211194679Snwhitehorn	if (sc->nmodes <= 0 || sc->nmodes > sizeof(sc->pcr_vals)) {
212194679Snwhitehorn		device_printf(dev,"No power mode data in device tree!\n");
213194679Snwhitehorn		return (ENXIO);
214194679Snwhitehorn	}
215194679Snwhitehorn	OF_getprop(cpu, "power-mode-data", modes, sc->nmodes);
216194679Snwhitehorn	sc->nmodes /= sizeof(modes[0]);
217194679Snwhitehorn
218194679Snwhitehorn	/* Sort the modes */
219194679Snwhitehorn	for (i = 0; i < sc->nmodes; i++)
220194679Snwhitehorn		sc->pcr_vals[PCR_TO_FREQ(modes[i])] = modes[i];
221194679Snwhitehorn
222194679Snwhitehorn	cpufreq_register(dev);
223194679Snwhitehorn	return (0);
224194679Snwhitehorn}
225194679Snwhitehorn
226194679Snwhitehornstatic int
227194679Snwhitehornpcr_settings(device_t dev, struct cf_setting *sets, int *count)
228194679Snwhitehorn{
229194679Snwhitehorn	struct pcr_softc *sc;
230194679Snwhitehorn
231194679Snwhitehorn	sc = device_get_softc(dev);
232194679Snwhitehorn	if (sets == NULL || count == NULL)
233194679Snwhitehorn		return (EINVAL);
234194679Snwhitehorn	if (*count < sc->nmodes)
235194679Snwhitehorn		return (E2BIG);
236194679Snwhitehorn
237194679Snwhitehorn	/* Return a list of valid settings for this driver. */
238194679Snwhitehorn	memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * sc->nmodes);
239194679Snwhitehorn
240194679Snwhitehorn	sets[0].freq = 10000; sets[0].dev = dev;
241194679Snwhitehorn	sets[1].freq = 5000; sets[1].dev = dev;
242194679Snwhitehorn	if (sc->nmodes > 2)
243194679Snwhitehorn		sets[2].freq = 2500; sets[2].dev = dev;
244194679Snwhitehorn	*count = sc->nmodes;
245194679Snwhitehorn
246194679Snwhitehorn	return (0);
247194679Snwhitehorn}
248194679Snwhitehorn
249194679Snwhitehornstatic int
250194679Snwhitehornpcr_set(device_t dev, const struct cf_setting *set)
251194679Snwhitehorn{
252194679Snwhitehorn	struct pcr_softc *sc;
253194679Snwhitehorn	register_t pcr, msr;
254194679Snwhitehorn	uint64_t psr;
255194679Snwhitehorn
256194679Snwhitehorn	if (set == NULL)
257194679Snwhitehorn		return (EINVAL);
258194679Snwhitehorn	sc = device_get_softc(dev);
259194679Snwhitehorn
260194679Snwhitehorn	/* Construct the new PCR */
261194679Snwhitehorn
262194679Snwhitehorn	pcr = SCOM_PCR_BIT;
263194679Snwhitehorn
264194679Snwhitehorn	if (set->freq == 10000)
265194679Snwhitehorn		pcr |= sc->pcr_vals[0];
266194679Snwhitehorn	else if (set->freq == 5000)
267194679Snwhitehorn		pcr |= sc->pcr_vals[1];
268194679Snwhitehorn	else if (set->freq == 2500)
269194679Snwhitehorn		pcr |= sc->pcr_vals[2];
270194679Snwhitehorn
271194679Snwhitehorn	msr = mfmsr();
272194679Snwhitehorn	mtmsr(msr & ~PSL_EE); isync();
273194679Snwhitehorn
274194679Snwhitehorn	/* 970MP requires PCR and PCRH to be cleared first */
275194679Snwhitehorn
276194679Snwhitehorn	write_scom(SCOM_PCR,0);			/* Clear PCRH */
277194679Snwhitehorn	write_scom(SCOM_PCR,SCOM_PCR_BIT);	/* Clear PCR */
278194679Snwhitehorn
279194679Snwhitehorn	/* Set PCR */
280194679Snwhitehorn
281194679Snwhitehorn	write_scom(SCOM_PCR, pcr);
282194679Snwhitehorn
283194679Snwhitehorn	/* Wait for completion */
284194679Snwhitehorn
285194679Snwhitehorn	do {
286194679Snwhitehorn		DELAY(100);
287194679Snwhitehorn		psr = read_scom(SCOM_PSR);
288194679Snwhitehorn	} while ((psr & PSR_RECEIVED) && !(psr & PSR_COMPLETED));
289194679Snwhitehorn
290194679Snwhitehorn	mtmsr(msr); isync();
291194679Snwhitehorn
292194679Snwhitehorn	return (0);
293194679Snwhitehorn}
294194679Snwhitehorn
295194679Snwhitehornstatic int
296194679Snwhitehornpcr_get(device_t dev, struct cf_setting *set)
297194679Snwhitehorn{
298194679Snwhitehorn	struct pcr_softc *sc;
299194679Snwhitehorn	uint64_t psr;
300194679Snwhitehorn
301194679Snwhitehorn	if (set == NULL)
302194679Snwhitehorn		return (EINVAL);
303194679Snwhitehorn	sc = device_get_softc(dev);
304194679Snwhitehorn
305194679Snwhitehorn	memset(set, CPUFREQ_VAL_UNKNOWN, sizeof(*set));
306194679Snwhitehorn
307194679Snwhitehorn	psr = read_scom(SCOM_PSR);
308194679Snwhitehorn
309194679Snwhitehorn	/* We want bits 6 and 7 */
310194679Snwhitehorn	psr = (psr >> 56) & 3;
311194679Snwhitehorn
312194679Snwhitehorn	set->freq = 10000;
313194679Snwhitehorn	if (psr == PCR_HALF)
314194679Snwhitehorn		set->freq = 5000;
315194679Snwhitehorn	else if (psr == PCR_QUARTER)
316194679Snwhitehorn		set->freq = 2500;
317194679Snwhitehorn
318194679Snwhitehorn	set->dev = dev;
319194679Snwhitehorn
320194679Snwhitehorn	return (0);
321194679Snwhitehorn}
322194679Snwhitehorn
323194679Snwhitehornstatic int
324194679Snwhitehornpcr_type(device_t dev, int *type)
325194679Snwhitehorn{
326194679Snwhitehorn
327194679Snwhitehorn	if (type == NULL)
328194679Snwhitehorn		return (EINVAL);
329194679Snwhitehorn
330194679Snwhitehorn	*type = CPUFREQ_TYPE_RELATIVE;
331194679Snwhitehorn	return (0);
332194679Snwhitehorn}
333194679Snwhitehorn
334