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$");
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	#ifndef __powerpc64__
112	register_t hi, lo, scratch;
113	#endif
114
115	msr = mfmsr();
116	mtmsr(msr & ~PSL_EE); isync();
117
118	#ifdef __powerpc64__
119	mtspr(SPR_SCOMD, value);
120	#else
121	hi = (value >> 32) & 0xffffffff;
122	lo = value & 0xffffffff;
123	mtspr64(SPR_SCOMD, hi, lo, scratch);
124	#endif
125	isync();
126	mtspr(SPR_SCOMC, address | SCOMC_WRITE);
127	isync();
128
129	mtmsr(msr); isync();
130}
131
132static uint64_t
133read_scom(register_t address)
134{
135	register_t msr;
136	uint64_t ret;
137
138	msr = mfmsr();
139	mtmsr(msr & ~PSL_EE); isync();
140
141	mtspr(SPR_SCOMC, address | SCOMC_READ);
142	isync();
143
144	__asm __volatile ("mfspr %0,%1;"
145            " mr %0+1, %0; srdi %0,%0,32" : "=r" (ret) : "K" (SPR_SCOMD));
146
147	(void)mfspr(SPR_SCOMC); /* Complete transcation */
148
149	mtmsr(msr); isync();
150
151	return (ret);
152}
153
154static void
155pcr_identify(driver_t *driver, device_t parent)
156{
157	uint16_t vers;
158	vers = mfpvr() >> 16;
159
160	/* Check for an IBM 970-class CPU */
161	switch (vers) {
162		case IBM970FX:
163		case IBM970GX:
164		case IBM970MP:
165			break;
166		default:
167			return;
168	}
169
170	/* Make sure we're not being doubly invoked. */
171	if (device_find_child(parent, "pcr", -1) != NULL)
172		return;
173
174	/*
175	 * We attach a child for every CPU since settings need to
176	 * be performed on every CPU in the SMP case.
177	 */
178	if (BUS_ADD_CHILD(parent, 10, "pcr", -1) == NULL)
179		device_printf(parent, "add pcr child failed\n");
180}
181
182static int
183pcr_probe(device_t dev)
184{
185	if (resource_disabled("pcr", 0))
186		return (ENXIO);
187
188	device_set_desc(dev, "PPC 970 Power Control Register");
189	return (0);
190}
191
192static int
193pcr_attach(device_t dev)
194{
195	struct pcr_softc *sc;
196	phandle_t cpu;
197	uint32_t modes[3];
198	int i;
199
200	sc = device_get_softc(dev);
201	sc->dev = dev;
202
203	cpu = ofw_bus_get_node(device_get_parent(dev));
204
205	if (cpu <= 0) {
206		device_printf(dev,"No CPU device tree node!\n");
207		return (ENXIO);
208	}
209
210	if (OF_getproplen(cpu, "power-mode-data") <= 0) {
211		/* Use the first CPU's node */
212		cpu = OF_child(OF_parent(cpu));
213	}
214
215	/*
216	 * Collect the PCR values for each mode from the device tree.
217	 * These include bus timing information, and so cannot be
218	 * directly computed.
219	 */
220	sc->nmodes = OF_getproplen(cpu, "power-mode-data");
221	if (sc->nmodes <= 0 || sc->nmodes > sizeof(sc->pcr_vals)) {
222		device_printf(dev,"No power mode data in device tree!\n");
223		return (ENXIO);
224	}
225	OF_getprop(cpu, "power-mode-data", modes, sc->nmodes);
226	sc->nmodes /= sizeof(modes[0]);
227
228	/* Sort the modes */
229	for (i = 0; i < sc->nmodes; i++)
230		sc->pcr_vals[PCR_TO_FREQ(modes[i])] = modes[i];
231
232	cpufreq_register(dev);
233	return (0);
234}
235
236static int
237pcr_settings(device_t dev, struct cf_setting *sets, int *count)
238{
239	struct pcr_softc *sc;
240
241	sc = device_get_softc(dev);
242	if (sets == NULL || count == NULL)
243		return (EINVAL);
244	if (*count < sc->nmodes)
245		return (E2BIG);
246
247	/* Return a list of valid settings for this driver. */
248	memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * sc->nmodes);
249
250	sets[0].freq = 10000; sets[0].dev = dev;
251	sets[1].freq = 5000; sets[1].dev = dev;
252	if (sc->nmodes > 2)
253		sets[2].freq = 2500; sets[2].dev = dev;
254	*count = sc->nmodes;
255
256	return (0);
257}
258
259static int
260pcr_set(device_t dev, const struct cf_setting *set)
261{
262	struct pcr_softc *sc;
263	register_t pcr, msr;
264	uint64_t psr;
265
266	if (set == NULL)
267		return (EINVAL);
268	sc = device_get_softc(dev);
269
270	/* Construct the new PCR */
271
272	pcr = SCOM_PCR_BIT;
273
274	if (set->freq == 10000)
275		pcr |= sc->pcr_vals[0];
276	else if (set->freq == 5000)
277		pcr |= sc->pcr_vals[1];
278	else if (set->freq == 2500)
279		pcr |= sc->pcr_vals[2];
280
281	msr = mfmsr();
282	mtmsr(msr & ~PSL_EE); isync();
283
284	/* 970MP requires PCR and PCRH to be cleared first */
285
286	write_scom(SCOM_PCR,0);			/* Clear PCRH */
287	write_scom(SCOM_PCR,SCOM_PCR_BIT);	/* Clear PCR */
288
289	/* Set PCR */
290
291	write_scom(SCOM_PCR, pcr);
292
293	/* Wait for completion */
294
295	do {
296		DELAY(100);
297		psr = read_scom(SCOM_PSR);
298	} while ((psr & PSR_RECEIVED) && !(psr & PSR_COMPLETED));
299
300	mtmsr(msr); isync();
301
302	return (0);
303}
304
305static int
306pcr_get(device_t dev, struct cf_setting *set)
307{
308	uint64_t psr;
309
310	if (set == NULL)
311		return (EINVAL);
312
313	memset(set, CPUFREQ_VAL_UNKNOWN, sizeof(*set));
314
315	psr = read_scom(SCOM_PSR);
316
317	/* We want bits 6 and 7 */
318	psr = (psr >> 56) & 3;
319
320	set->freq = 10000;
321	if (psr == PCR_HALF)
322		set->freq = 5000;
323	else if (psr == PCR_QUARTER)
324		set->freq = 2500;
325
326	set->dev = dev;
327
328	return (0);
329}
330
331static int
332pcr_type(device_t dev, int *type)
333{
334
335	if (type == NULL)
336		return (EINVAL);
337
338	*type = CPUFREQ_TYPE_RELATIVE;
339	return (0);
340}
341
342