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