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$");
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;
111209975Snwhitehorn	#ifndef __powerpc64__
112194679Snwhitehorn	register_t hi, lo, scratch;
113209975Snwhitehorn	#endif
114194679Snwhitehorn
115194679Snwhitehorn	msr = mfmsr();
116194679Snwhitehorn	mtmsr(msr & ~PSL_EE); isync();
117194679Snwhitehorn
118209975Snwhitehorn	#ifdef __powerpc64__
119209975Snwhitehorn	mtspr(SPR_SCOMD, value);
120209975Snwhitehorn	#else
121209975Snwhitehorn	hi = (value >> 32) & 0xffffffff;
122209975Snwhitehorn	lo = value & 0xffffffff;
123194679Snwhitehorn	mtspr64(SPR_SCOMD, hi, lo, scratch);
124209975Snwhitehorn	#endif
125194679Snwhitehorn	isync();
126194679Snwhitehorn	mtspr(SPR_SCOMC, address | SCOMC_WRITE);
127194679Snwhitehorn	isync();
128194679Snwhitehorn
129194679Snwhitehorn	mtmsr(msr); isync();
130194679Snwhitehorn}
131194679Snwhitehorn
132194679Snwhitehornstatic uint64_t
133194679Snwhitehornread_scom(register_t address)
134194679Snwhitehorn{
135194679Snwhitehorn	register_t msr;
136194679Snwhitehorn	uint64_t ret;
137194679Snwhitehorn
138194679Snwhitehorn	msr = mfmsr();
139194679Snwhitehorn	mtmsr(msr & ~PSL_EE); isync();
140194679Snwhitehorn
141194679Snwhitehorn	mtspr(SPR_SCOMC, address | SCOMC_READ);
142194679Snwhitehorn	isync();
143194679Snwhitehorn
144194679Snwhitehorn	__asm __volatile ("mfspr %0,%1;"
145194679Snwhitehorn            " mr %0+1, %0; srdi %0,%0,32" : "=r" (ret) : "K" (SPR_SCOMD));
146194679Snwhitehorn
147194679Snwhitehorn	(void)mfspr(SPR_SCOMC); /* Complete transcation */
148194679Snwhitehorn
149194679Snwhitehorn	mtmsr(msr); isync();
150194679Snwhitehorn
151194679Snwhitehorn	return (ret);
152194679Snwhitehorn}
153194679Snwhitehorn
154194679Snwhitehornstatic void
155194679Snwhitehornpcr_identify(driver_t *driver, device_t parent)
156194679Snwhitehorn{
157194679Snwhitehorn	uint16_t vers;
158194679Snwhitehorn	vers = mfpvr() >> 16;
159194679Snwhitehorn
160194679Snwhitehorn	/* Check for an IBM 970-class CPU */
161194679Snwhitehorn	switch (vers) {
162194679Snwhitehorn		case IBM970FX:
163194679Snwhitehorn		case IBM970GX:
164194679Snwhitehorn		case IBM970MP:
165194679Snwhitehorn			break;
166194679Snwhitehorn		default:
167194679Snwhitehorn			return;
168194679Snwhitehorn	}
169194679Snwhitehorn
170194679Snwhitehorn	/* Make sure we're not being doubly invoked. */
171194679Snwhitehorn	if (device_find_child(parent, "pcr", -1) != NULL)
172194679Snwhitehorn		return;
173194679Snwhitehorn
174194679Snwhitehorn	/*
175194679Snwhitehorn	 * We attach a child for every CPU since settings need to
176194679Snwhitehorn	 * be performed on every CPU in the SMP case.
177194679Snwhitehorn	 */
178194679Snwhitehorn	if (BUS_ADD_CHILD(parent, 10, "pcr", -1) == NULL)
179194679Snwhitehorn		device_printf(parent, "add pcr child failed\n");
180194679Snwhitehorn}
181194679Snwhitehorn
182194679Snwhitehornstatic int
183194679Snwhitehornpcr_probe(device_t dev)
184194679Snwhitehorn{
185194679Snwhitehorn	if (resource_disabled("pcr", 0))
186194679Snwhitehorn		return (ENXIO);
187194679Snwhitehorn
188194679Snwhitehorn	device_set_desc(dev, "PPC 970 Power Control Register");
189194679Snwhitehorn	return (0);
190194679Snwhitehorn}
191194679Snwhitehorn
192194679Snwhitehornstatic int
193194679Snwhitehornpcr_attach(device_t dev)
194194679Snwhitehorn{
195194679Snwhitehorn	struct pcr_softc *sc;
196194679Snwhitehorn	phandle_t cpu;
197194679Snwhitehorn	uint32_t modes[3];
198194679Snwhitehorn	int i;
199194679Snwhitehorn
200194679Snwhitehorn	sc = device_get_softc(dev);
201194679Snwhitehorn	sc->dev = dev;
202194679Snwhitehorn
203194679Snwhitehorn	cpu = ofw_bus_get_node(device_get_parent(dev));
204194679Snwhitehorn
205194679Snwhitehorn	if (cpu <= 0) {
206194679Snwhitehorn		device_printf(dev,"No CPU device tree node!\n");
207194679Snwhitehorn		return (ENXIO);
208194679Snwhitehorn	}
209194679Snwhitehorn
210208150Snwhitehorn	if (OF_getproplen(cpu, "power-mode-data") <= 0) {
211208150Snwhitehorn		/* Use the first CPU's node */
212208150Snwhitehorn		cpu = OF_child(OF_parent(cpu));
213208150Snwhitehorn	}
214208150Snwhitehorn
215194679Snwhitehorn	/*
216194679Snwhitehorn	 * Collect the PCR values for each mode from the device tree.
217194679Snwhitehorn	 * These include bus timing information, and so cannot be
218194679Snwhitehorn	 * directly computed.
219194679Snwhitehorn	 */
220194679Snwhitehorn	sc->nmodes = OF_getproplen(cpu, "power-mode-data");
221194679Snwhitehorn	if (sc->nmodes <= 0 || sc->nmodes > sizeof(sc->pcr_vals)) {
222194679Snwhitehorn		device_printf(dev,"No power mode data in device tree!\n");
223194679Snwhitehorn		return (ENXIO);
224194679Snwhitehorn	}
225194679Snwhitehorn	OF_getprop(cpu, "power-mode-data", modes, sc->nmodes);
226194679Snwhitehorn	sc->nmodes /= sizeof(modes[0]);
227194679Snwhitehorn
228194679Snwhitehorn	/* Sort the modes */
229194679Snwhitehorn	for (i = 0; i < sc->nmodes; i++)
230194679Snwhitehorn		sc->pcr_vals[PCR_TO_FREQ(modes[i])] = modes[i];
231194679Snwhitehorn
232194679Snwhitehorn	cpufreq_register(dev);
233194679Snwhitehorn	return (0);
234194679Snwhitehorn}
235194679Snwhitehorn
236194679Snwhitehornstatic int
237194679Snwhitehornpcr_settings(device_t dev, struct cf_setting *sets, int *count)
238194679Snwhitehorn{
239194679Snwhitehorn	struct pcr_softc *sc;
240194679Snwhitehorn
241194679Snwhitehorn	sc = device_get_softc(dev);
242194679Snwhitehorn	if (sets == NULL || count == NULL)
243194679Snwhitehorn		return (EINVAL);
244194679Snwhitehorn	if (*count < sc->nmodes)
245194679Snwhitehorn		return (E2BIG);
246194679Snwhitehorn
247194679Snwhitehorn	/* Return a list of valid settings for this driver. */
248194679Snwhitehorn	memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * sc->nmodes);
249194679Snwhitehorn
250194679Snwhitehorn	sets[0].freq = 10000; sets[0].dev = dev;
251194679Snwhitehorn	sets[1].freq = 5000; sets[1].dev = dev;
252194679Snwhitehorn	if (sc->nmodes > 2)
253194679Snwhitehorn		sets[2].freq = 2500; sets[2].dev = dev;
254194679Snwhitehorn	*count = sc->nmodes;
255194679Snwhitehorn
256194679Snwhitehorn	return (0);
257194679Snwhitehorn}
258194679Snwhitehorn
259194679Snwhitehornstatic int
260194679Snwhitehornpcr_set(device_t dev, const struct cf_setting *set)
261194679Snwhitehorn{
262194679Snwhitehorn	struct pcr_softc *sc;
263194679Snwhitehorn	register_t pcr, msr;
264194679Snwhitehorn	uint64_t psr;
265194679Snwhitehorn
266194679Snwhitehorn	if (set == NULL)
267194679Snwhitehorn		return (EINVAL);
268194679Snwhitehorn	sc = device_get_softc(dev);
269194679Snwhitehorn
270194679Snwhitehorn	/* Construct the new PCR */
271194679Snwhitehorn
272194679Snwhitehorn	pcr = SCOM_PCR_BIT;
273194679Snwhitehorn
274194679Snwhitehorn	if (set->freq == 10000)
275194679Snwhitehorn		pcr |= sc->pcr_vals[0];
276194679Snwhitehorn	else if (set->freq == 5000)
277194679Snwhitehorn		pcr |= sc->pcr_vals[1];
278194679Snwhitehorn	else if (set->freq == 2500)
279194679Snwhitehorn		pcr |= sc->pcr_vals[2];
280194679Snwhitehorn
281194679Snwhitehorn	msr = mfmsr();
282194679Snwhitehorn	mtmsr(msr & ~PSL_EE); isync();
283194679Snwhitehorn
284194679Snwhitehorn	/* 970MP requires PCR and PCRH to be cleared first */
285194679Snwhitehorn
286194679Snwhitehorn	write_scom(SCOM_PCR,0);			/* Clear PCRH */
287194679Snwhitehorn	write_scom(SCOM_PCR,SCOM_PCR_BIT);	/* Clear PCR */
288194679Snwhitehorn
289194679Snwhitehorn	/* Set PCR */
290194679Snwhitehorn
291194679Snwhitehorn	write_scom(SCOM_PCR, pcr);
292194679Snwhitehorn
293194679Snwhitehorn	/* Wait for completion */
294194679Snwhitehorn
295194679Snwhitehorn	do {
296194679Snwhitehorn		DELAY(100);
297194679Snwhitehorn		psr = read_scom(SCOM_PSR);
298194679Snwhitehorn	} while ((psr & PSR_RECEIVED) && !(psr & PSR_COMPLETED));
299194679Snwhitehorn
300194679Snwhitehorn	mtmsr(msr); isync();
301194679Snwhitehorn
302194679Snwhitehorn	return (0);
303194679Snwhitehorn}
304194679Snwhitehorn
305194679Snwhitehornstatic int
306194679Snwhitehornpcr_get(device_t dev, struct cf_setting *set)
307194679Snwhitehorn{
308194679Snwhitehorn	uint64_t psr;
309194679Snwhitehorn
310194679Snwhitehorn	if (set == NULL)
311194679Snwhitehorn		return (EINVAL);
312194679Snwhitehorn
313194679Snwhitehorn	memset(set, CPUFREQ_VAL_UNKNOWN, sizeof(*set));
314194679Snwhitehorn
315194679Snwhitehorn	psr = read_scom(SCOM_PSR);
316194679Snwhitehorn
317194679Snwhitehorn	/* We want bits 6 and 7 */
318194679Snwhitehorn	psr = (psr >> 56) & 3;
319194679Snwhitehorn
320194679Snwhitehorn	set->freq = 10000;
321194679Snwhitehorn	if (psr == PCR_HALF)
322194679Snwhitehorn		set->freq = 5000;
323194679Snwhitehorn	else if (psr == PCR_QUARTER)
324194679Snwhitehorn		set->freq = 2500;
325194679Snwhitehorn
326194679Snwhitehorn	set->dev = dev;
327194679Snwhitehorn
328194679Snwhitehorn	return (0);
329194679Snwhitehorn}
330194679Snwhitehorn
331194679Snwhitehornstatic int
332194679Snwhitehornpcr_type(device_t dev, int *type)
333194679Snwhitehorn{
334194679Snwhitehorn
335194679Snwhitehorn	if (type == NULL)
336194679Snwhitehorn		return (EINVAL);
337194679Snwhitehorn
338194679Snwhitehorn	*type = CPUFREQ_TYPE_RELATIVE;
339194679Snwhitehorn	return (0);
340194679Snwhitehorn}
341194679Snwhitehorn
342