1/*-
2 * SPDX-License-Identifier: BSD-4-Clause
3 *
4 * Copyright (c) 2001 Tamotsu Hattori.
5 * Copyright (c) 2001 Mitsuru IWASAKI.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 */
37
38#include <sys/cdefs.h>
39#include "opt_cpu.h"
40
41#include <sys/param.h>
42#include <sys/systm.h>
43#include <sys/kernel.h>
44#include <sys/conf.h>
45#include <sys/eventhandler.h>
46#include <sys/power.h>
47#include <sys/sysctl.h>
48#include <sys/types.h>
49
50#include <machine/cputypes.h>
51#include <machine/md_var.h>
52#include <machine/specialreg.h>
53
54/*
55 * Transmeta Crusoe LongRun Support by Tamotsu Hattori.
56 */
57
58#define MSR_TMx86_LONGRUN		0x80868010
59#define MSR_TMx86_LONGRUN_FLAGS		0x80868011
60
61#define LONGRUN_MODE_MASK(x)		((x) & 0x000000007f)
62#define LONGRUN_MODE_RESERVED(x)	((x) & 0xffffff80)
63#define LONGRUN_MODE_WRITE(x, y)	(LONGRUN_MODE_RESERVED(x) | LONGRUN_MODE_MASK(y))
64
65#define LONGRUN_MODE_MINFREQUENCY	0x00
66#define LONGRUN_MODE_ECONOMY		0x01
67#define LONGRUN_MODE_PERFORMANCE	0x02
68#define LONGRUN_MODE_MAXFREQUENCY	0x03
69#define LONGRUN_MODE_UNKNOWN		0x04
70#define LONGRUN_MODE_MAX		0x04
71
72union msrinfo {
73	u_int64_t	msr;
74	u_int32_t	regs[2];
75};
76
77static u_int32_t longrun_modes[LONGRUN_MODE_MAX][3] = {
78	/*  MSR low, MSR high, flags bit0 */
79	{	  0,	  0,		0},	/* LONGRUN_MODE_MINFREQUENCY */
80	{	  0,	100,		0},	/* LONGRUN_MODE_ECONOMY */
81	{	  0,	100,		1},	/* LONGRUN_MODE_PERFORMANCE */
82	{	100,	100,		1},	/* LONGRUN_MODE_MAXFREQUENCY */
83};
84
85static u_int
86tmx86_get_longrun_mode(void)
87{
88	register_t	saveintr;
89	union msrinfo	msrinfo;
90	u_int		low, high, flags, mode;
91
92	saveintr = intr_disable();
93
94	msrinfo.msr = rdmsr(MSR_TMx86_LONGRUN);
95	low = LONGRUN_MODE_MASK(msrinfo.regs[0]);
96	high = LONGRUN_MODE_MASK(msrinfo.regs[1]);
97	flags = rdmsr(MSR_TMx86_LONGRUN_FLAGS) & 0x01;
98
99	for (mode = 0; mode < LONGRUN_MODE_MAX; mode++) {
100		if (low   == longrun_modes[mode][0] &&
101		    high  == longrun_modes[mode][1] &&
102		    flags == longrun_modes[mode][2]) {
103			goto out;
104		}
105	}
106	mode = LONGRUN_MODE_UNKNOWN;
107out:
108	intr_restore(saveintr);
109	return (mode);
110}
111
112static u_int
113tmx86_get_longrun_status(u_int * frequency, u_int * voltage, u_int * percentage)
114{
115	register_t	saveintr;
116	u_int		regs[4];
117
118	saveintr = intr_disable();
119
120	do_cpuid(0x80860007, regs);
121	*frequency = regs[0];
122	*voltage = regs[1];
123	*percentage = regs[2];
124
125	intr_restore(saveintr);
126	return (1);
127}
128
129static u_int
130tmx86_set_longrun_mode(u_int mode)
131{
132	register_t	saveintr;
133	union msrinfo	msrinfo;
134
135	if (mode >= LONGRUN_MODE_UNKNOWN) {
136		return (0);
137	}
138
139	saveintr = intr_disable();
140
141	/* Write LongRun mode values to Model Specific Register. */
142	msrinfo.msr = rdmsr(MSR_TMx86_LONGRUN);
143	msrinfo.regs[0] = LONGRUN_MODE_WRITE(msrinfo.regs[0],
144					     longrun_modes[mode][0]);
145	msrinfo.regs[1] = LONGRUN_MODE_WRITE(msrinfo.regs[1],
146					     longrun_modes[mode][1]);
147	wrmsr(MSR_TMx86_LONGRUN, msrinfo.msr);
148
149	/* Write LongRun mode flags to Model Specific Register. */
150	msrinfo.msr = rdmsr(MSR_TMx86_LONGRUN_FLAGS);
151	msrinfo.regs[0] = (msrinfo.regs[0] & ~0x01) | longrun_modes[mode][2];
152	wrmsr(MSR_TMx86_LONGRUN_FLAGS, msrinfo.msr);
153
154	intr_restore(saveintr);
155	return (1);
156}
157
158static u_int			 crusoe_longrun;
159static u_int			 crusoe_frequency;
160static u_int	 		 crusoe_voltage;
161static u_int	 		 crusoe_percentage;
162static u_int	 		 crusoe_performance_longrun = LONGRUN_MODE_PERFORMANCE;
163static u_int	 		 crusoe_economy_longrun = LONGRUN_MODE_ECONOMY;
164static struct sysctl_ctx_list	 crusoe_sysctl_ctx;
165static struct sysctl_oid	*crusoe_sysctl_tree;
166
167static void
168tmx86_longrun_power_profile(void *arg)
169{
170	int	state;
171	u_int	new;
172
173	state = power_profile_get_state();
174	if (state != POWER_PROFILE_PERFORMANCE &&
175	    state != POWER_PROFILE_ECONOMY) {
176		return;
177	}
178
179	switch (state) {
180	case POWER_PROFILE_PERFORMANCE:
181		new =crusoe_performance_longrun;
182		break;
183	case POWER_PROFILE_ECONOMY:
184		new = crusoe_economy_longrun;
185		break;
186	default:
187		new = tmx86_get_longrun_mode();
188		break;
189	}
190
191	if (tmx86_get_longrun_mode() != new) {
192		tmx86_set_longrun_mode(new);
193	}
194}
195
196static int
197tmx86_longrun_sysctl(SYSCTL_HANDLER_ARGS)
198{
199	u_int	mode;
200	int	error;
201
202	crusoe_longrun = tmx86_get_longrun_mode();
203	mode = crusoe_longrun;
204	error = sysctl_handle_int(oidp, &mode, 0, req);
205	if (error || !req->newptr) {
206		return (error);
207	}
208	if (mode >= LONGRUN_MODE_UNKNOWN) {
209		error = EINVAL;
210		return (error);
211	}
212	if (crusoe_longrun != mode) {
213		crusoe_longrun = mode;
214		tmx86_set_longrun_mode(crusoe_longrun);
215	}
216
217	return (error);
218}
219
220static int
221tmx86_status_sysctl(SYSCTL_HANDLER_ARGS)
222{
223	u_int	val;
224	int	error;
225
226	tmx86_get_longrun_status(&crusoe_frequency,
227				 &crusoe_voltage, &crusoe_percentage);
228	val = *(u_int *)oidp->oid_arg1;
229	error = sysctl_handle_int(oidp, &val, 0, req);
230	return (error);
231}
232
233static int
234tmx86_longrun_profile_sysctl(SYSCTL_HANDLER_ARGS)
235{
236	u_int32_t *argp;
237	u_int32_t arg;
238	int	error;
239
240	argp = (u_int32_t *)oidp->oid_arg1;
241	arg = *argp;
242	error = sysctl_handle_int(oidp, &arg, 0, req);
243
244	/* error or no new value */
245	if ((error != 0) || (req->newptr == NULL))
246		return (error);
247
248	/* range check */
249	if (arg >= LONGRUN_MODE_UNKNOWN)
250		return (EINVAL);
251
252	/* set new value and possibly switch */
253	*argp = arg;
254
255	tmx86_longrun_power_profile(NULL);
256
257	return (0);
258
259}
260
261static void
262setup_tmx86_longrun(void *dummy __unused)
263{
264
265	if (cpu_vendor_id != CPU_VENDOR_TRANSMETA)
266		return;
267
268	crusoe_longrun = tmx86_get_longrun_mode();
269	tmx86_get_longrun_status(&crusoe_frequency,
270				 &crusoe_voltage, &crusoe_percentage);
271	printf("Crusoe LongRun support enabled, current mode: %d "
272	       "<%dMHz %dmV %d%%>\n", crusoe_longrun, crusoe_frequency,
273	       crusoe_voltage, crusoe_percentage);
274
275	sysctl_ctx_init(&crusoe_sysctl_ctx);
276	crusoe_sysctl_tree = SYSCTL_ADD_NODE(&crusoe_sysctl_ctx,
277	    SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, "crusoe",
278	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
279	    "Transmeta Crusoe LongRun support");
280	SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree),
281	    OID_AUTO, "longrun", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
282	    &crusoe_longrun, 0, tmx86_longrun_sysctl, "I",
283	    "LongRun mode [0-3]");
284	SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree),
285	    OID_AUTO, "frequency", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
286	    &crusoe_frequency, 0, tmx86_status_sysctl, "I",
287	    "Current frequency (MHz)");
288	SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree),
289	    OID_AUTO, "voltage", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT,
290	    &crusoe_voltage, 0, tmx86_status_sysctl, "I",
291	    "Current voltage (mV)");
292	SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree),
293	    OID_AUTO, "percentage",
294	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, &crusoe_percentage,
295	    0, tmx86_status_sysctl, "I", "Processing performance (%)");
296	SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree),
297	    OID_AUTO, "performance_longrun",
298	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
299	    &crusoe_performance_longrun, 0, tmx86_longrun_profile_sysctl, "I",
300	    "");
301	SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree),
302	    OID_AUTO, "economy_longrun",
303	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
304	    &crusoe_economy_longrun, 0, tmx86_longrun_profile_sysctl, "I", "");
305
306	/* register performance profile change handler */
307	EVENTHANDLER_REGISTER(power_profile_change, tmx86_longrun_power_profile, NULL, 0);
308}
309SYSINIT(setup_tmx86_longrun, SI_SUB_CPU, SI_ORDER_ANY, setup_tmx86_longrun,
310    NULL);
311