pcr.c revision 330897
1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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/cdefs.h>
30__FBSDID("$FreeBSD: stable/11/sys/powerpc/cpufreq/pcr.c 330897 2018-03-14 03:19:51Z eadler $");
31
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/bus.h>
35#include <sys/cpu.h>
36#include <sys/kernel.h>
37#include <sys/module.h>
38
39#include <dev/ofw/ofw_bus.h>
40
41#include "cpufreq_if.h"
42
43struct pcr_softc {
44	device_t dev;
45	uint32_t pcr_vals[3];
46	int nmodes;
47};
48
49static void	pcr_identify(driver_t *driver, device_t parent);
50static int	pcr_probe(device_t dev);
51static int	pcr_attach(device_t dev);
52static int	pcr_settings(device_t dev, struct cf_setting *sets, int *count);
53static int	pcr_set(device_t dev, const struct cf_setting *set);
54static int	pcr_get(device_t dev, struct cf_setting *set);
55static int	pcr_type(device_t dev, int *type);
56
57static device_method_t pcr_methods[] = {
58	/* Device interface */
59	DEVMETHOD(device_identify,	pcr_identify),
60	DEVMETHOD(device_probe,		pcr_probe),
61	DEVMETHOD(device_attach,	pcr_attach),
62
63	/* cpufreq interface */
64	DEVMETHOD(cpufreq_drv_set,	pcr_set),
65	DEVMETHOD(cpufreq_drv_get,	pcr_get),
66	DEVMETHOD(cpufreq_drv_type,	pcr_type),
67	DEVMETHOD(cpufreq_drv_settings,	pcr_settings),
68
69	{0, 0}
70};
71
72static driver_t pcr_driver = {
73	"pcr",
74	pcr_methods,
75	sizeof(struct pcr_softc)
76};
77
78static devclass_t pcr_devclass;
79DRIVER_MODULE(pcr, cpu, pcr_driver, pcr_devclass, 0, 0);
80
81/*
82 * States
83 */
84
85#define PCR_TO_FREQ(a)	((a >> 17) & 3)
86
87#define	PCR_FULL	0
88#define PCR_HALF	1
89#define PCR_QUARTER	2		/* Only on 970MP */
90
91#define PSR_RECEIVED	(1ULL << 61)
92#define PSR_COMPLETED	(1ULL << 61)
93
94/*
95 * SCOM addresses
96 */
97
98#define	SCOM_PCR	0x0aa00100	/* Power Control Register */
99#define SCOM_PCR_BIT	0x80000000	/* Data bit for PCR */
100#define SCOM_PSR	0x40800100	/* Power Status Register */
101
102/*
103 * SCOM Glue
104 */
105
106#define SCOMC_READ	0x00008000
107#define SCOMC_WRITE	0x00000000
108
109static void
110write_scom(register_t address, uint64_t value)
111{
112	register_t msr;
113	#ifndef __powerpc64__
114	register_t hi, lo, scratch;
115	#endif
116
117	msr = mfmsr();
118	mtmsr(msr & ~PSL_EE); isync();
119
120	#ifdef __powerpc64__
121	mtspr(SPR_SCOMD, value);
122	#else
123	hi = (value >> 32) & 0xffffffff;
124	lo = value & 0xffffffff;
125	mtspr64(SPR_SCOMD, hi, lo, scratch);
126	#endif
127	isync();
128	mtspr(SPR_SCOMC, address | SCOMC_WRITE);
129	isync();
130
131	mtmsr(msr); isync();
132}
133
134static uint64_t
135read_scom(register_t address)
136{
137	register_t msr;
138	uint64_t ret;
139
140	msr = mfmsr();
141	mtmsr(msr & ~PSL_EE); isync();
142
143	mtspr(SPR_SCOMC, address | SCOMC_READ);
144	isync();
145
146	__asm __volatile ("mfspr %0,%1;"
147            " mr %0+1, %0; srdi %0,%0,32" : "=r" (ret) : "K" (SPR_SCOMD));
148
149	(void)mfspr(SPR_SCOMC); /* Complete transcation */
150
151	mtmsr(msr); isync();
152
153	return (ret);
154}
155
156static void
157pcr_identify(driver_t *driver, device_t parent)
158{
159	uint16_t vers;
160	vers = mfpvr() >> 16;
161
162	/* Check for an IBM 970-class CPU */
163	switch (vers) {
164		case IBM970FX:
165		case IBM970GX:
166		case IBM970MP:
167			break;
168		default:
169			return;
170	}
171
172	/* Make sure we're not being doubly invoked. */
173	if (device_find_child(parent, "pcr", -1) != NULL)
174		return;
175
176	/*
177	 * We attach a child for every CPU since settings need to
178	 * be performed on every CPU in the SMP case.
179	 */
180	if (BUS_ADD_CHILD(parent, 10, "pcr", -1) == NULL)
181		device_printf(parent, "add pcr child failed\n");
182}
183
184static int
185pcr_probe(device_t dev)
186{
187	if (resource_disabled("pcr", 0))
188		return (ENXIO);
189
190	device_set_desc(dev, "PPC 970 Power Control Register");
191	return (0);
192}
193
194static int
195pcr_attach(device_t dev)
196{
197	struct pcr_softc *sc;
198	phandle_t cpu;
199	uint32_t modes[3];
200	int i;
201
202	sc = device_get_softc(dev);
203	sc->dev = dev;
204
205	cpu = ofw_bus_get_node(device_get_parent(dev));
206
207	if (cpu <= 0) {
208		device_printf(dev,"No CPU device tree node!\n");
209		return (ENXIO);
210	}
211
212	if (OF_getproplen(cpu, "power-mode-data") <= 0) {
213		/* Use the first CPU's node */
214		cpu = OF_child(OF_parent(cpu));
215	}
216
217	/*
218	 * Collect the PCR values for each mode from the device tree.
219	 * These include bus timing information, and so cannot be
220	 * directly computed.
221	 */
222	sc->nmodes = OF_getproplen(cpu, "power-mode-data");
223	if (sc->nmodes <= 0 || sc->nmodes > sizeof(sc->pcr_vals)) {
224		device_printf(dev,"No power mode data in device tree!\n");
225		return (ENXIO);
226	}
227	OF_getprop(cpu, "power-mode-data", modes, sc->nmodes);
228	sc->nmodes /= sizeof(modes[0]);
229
230	/* Sort the modes */
231	for (i = 0; i < sc->nmodes; i++)
232		sc->pcr_vals[PCR_TO_FREQ(modes[i])] = modes[i];
233
234	cpufreq_register(dev);
235	return (0);
236}
237
238static int
239pcr_settings(device_t dev, struct cf_setting *sets, int *count)
240{
241	struct pcr_softc *sc;
242
243	sc = device_get_softc(dev);
244	if (sets == NULL || count == NULL)
245		return (EINVAL);
246	if (*count < sc->nmodes)
247		return (E2BIG);
248
249	/* Return a list of valid settings for this driver. */
250	memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * sc->nmodes);
251
252	sets[0].freq = 10000; sets[0].dev = dev;
253	sets[1].freq = 5000; sets[1].dev = dev;
254	if (sc->nmodes > 2)
255		sets[2].freq = 2500; sets[2].dev = dev;
256	*count = sc->nmodes;
257
258	return (0);
259}
260
261static int
262pcr_set(device_t dev, const struct cf_setting *set)
263{
264	struct pcr_softc *sc;
265	register_t pcr, msr;
266	uint64_t psr;
267
268	if (set == NULL)
269		return (EINVAL);
270	sc = device_get_softc(dev);
271
272	/* Construct the new PCR */
273
274	pcr = SCOM_PCR_BIT;
275
276	if (set->freq == 10000)
277		pcr |= sc->pcr_vals[0];
278	else if (set->freq == 5000)
279		pcr |= sc->pcr_vals[1];
280	else if (set->freq == 2500)
281		pcr |= sc->pcr_vals[2];
282
283	msr = mfmsr();
284	mtmsr(msr & ~PSL_EE); isync();
285
286	/* 970MP requires PCR and PCRH to be cleared first */
287
288	write_scom(SCOM_PCR,0);			/* Clear PCRH */
289	write_scom(SCOM_PCR,SCOM_PCR_BIT);	/* Clear PCR */
290
291	/* Set PCR */
292
293	write_scom(SCOM_PCR, pcr);
294
295	/* Wait for completion */
296
297	do {
298		DELAY(100);
299		psr = read_scom(SCOM_PSR);
300	} while ((psr & PSR_RECEIVED) && !(psr & PSR_COMPLETED));
301
302	mtmsr(msr); isync();
303
304	return (0);
305}
306
307static int
308pcr_get(device_t dev, struct cf_setting *set)
309{
310	uint64_t psr;
311
312	if (set == NULL)
313		return (EINVAL);
314
315	memset(set, CPUFREQ_VAL_UNKNOWN, sizeof(*set));
316
317	psr = read_scom(SCOM_PSR);
318
319	/* We want bits 6 and 7 */
320	psr = (psr >> 56) & 3;
321
322	set->freq = 10000;
323	if (psr == PCR_HALF)
324		set->freq = 5000;
325	else if (psr == PCR_QUARTER)
326		set->freq = 2500;
327
328	set->dev = dev;
329
330	return (0);
331}
332
333static int
334pcr_type(device_t dev, int *type)
335{
336
337	if (type == NULL)
338		return (EINVAL);
339
340	*type = CPUFREQ_TYPE_RELATIVE;
341	return (0);
342}
343
344