xref: /freebsd/sys/dev/acpica/acpi_cpu.c (revision be2b179704218e240b66f023545f8e97d4c29a08)
1fec754d4SMike Smith /*-
2fec754d4SMike Smith  * Copyright (c) 2001 Michael Smith
3fec754d4SMike Smith  * All rights reserved.
4fec754d4SMike Smith  *
5fec754d4SMike Smith  * Redistribution and use in source and binary forms, with or without
6fec754d4SMike Smith  * modification, are permitted provided that the following conditions
7fec754d4SMike Smith  * are met:
8fec754d4SMike Smith  * 1. Redistributions of source code must retain the above copyright
9fec754d4SMike Smith  *    notice, this list of conditions and the following disclaimer.
10fec754d4SMike Smith  * 2. Redistributions in binary form must reproduce the above copyright
11fec754d4SMike Smith  *    notice, this list of conditions and the following disclaimer in the
12fec754d4SMike Smith  *    documentation and/or other materials provided with the distribution.
13fec754d4SMike Smith  *
14fec754d4SMike Smith  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15fec754d4SMike Smith  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16fec754d4SMike Smith  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17fec754d4SMike Smith  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18fec754d4SMike Smith  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19fec754d4SMike Smith  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20fec754d4SMike Smith  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21fec754d4SMike Smith  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22fec754d4SMike Smith  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23fec754d4SMike Smith  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24fec754d4SMike Smith  * SUCH DAMAGE.
25fec754d4SMike Smith  */
26fec754d4SMike Smith 
27aad970f1SDavid E. O'Brien #include <sys/cdefs.h>
28aad970f1SDavid E. O'Brien __FBSDID("$FreeBSD$");
29aad970f1SDavid E. O'Brien 
30fec754d4SMike Smith #include "opt_acpi.h"
31fec754d4SMike Smith #include <sys/param.h>
32fec754d4SMike Smith #include <sys/kernel.h>
33fec754d4SMike Smith #include <sys/bus.h>
34899ccf54SMitsuru IWASAKI #include <sys/power.h>
35fec754d4SMike Smith 
36fec754d4SMike Smith #include <machine/bus_pio.h>
37fec754d4SMike Smith #include <machine/bus.h>
38fec754d4SMike Smith #include <machine/resource.h>
39fec754d4SMike Smith #include <sys/rman.h>
40fec754d4SMike Smith 
41fec754d4SMike Smith #include "acpi.h"
42fec754d4SMike Smith #include <dev/acpica/acpivar.h>
43fec754d4SMike Smith 
44fec754d4SMike Smith /*
45fec754d4SMike Smith  * Support for ACPI Processor devices.
46fec754d4SMike Smith  *
47fec754d4SMike Smith  * Note that this only provides ACPI 1.0 support (with the exception of the
48fec754d4SMike Smith  * PSTATE_CNT field).  2.0 support will involve implementing _PTC, _PCT,
49fec754d4SMike Smith  * _PSS and _PPC.
50fec754d4SMike Smith  */
51fec754d4SMike Smith 
52be2b1797SNate Lawson /* Hooks for the ACPI CA debugging infrastructure */
53fec754d4SMike Smith #define _COMPONENT	ACPI_PROCESSOR
549127281cSMike Smith ACPI_MODULE_NAME("PROCESSOR")
55fec754d4SMike Smith 
56fec754d4SMike Smith struct acpi_cpu_softc {
57fec754d4SMike Smith     device_t		cpu_dev;
58fec754d4SMike Smith     ACPI_HANDLE		cpu_handle;
59fec754d4SMike Smith 
60fec754d4SMike Smith     u_int32_t		cpu_id;
61fec754d4SMike Smith 
62fec754d4SMike Smith     /* CPU throttling control register */
63fec754d4SMike Smith     struct resource	*cpu_p_blk;
64fec754d4SMike Smith #define CPU_P_CNT_THT_EN (1<<4)
65fec754d4SMike Smith };
66fec754d4SMike Smith 
67be2b1797SNate Lawson #define CPU_GET_P_CNT(sc) 					\
68be2b1797SNate Lawson     (bus_space_read_4(rman_get_bustag((sc)->cpu_p_blk), 	\
69be2b1797SNate Lawson 		      rman_get_bushandle((sc)->cpu_p_blk), 0))
70be2b1797SNate Lawson #define CPU_SET_P_CNT(sc, val)					\
71be2b1797SNate Lawson     (bus_space_write_4(rman_get_bustag((sc)->cpu_p_blk), 	\
72be2b1797SNate Lawson 		       rman_get_bushandle((sc)->cpu_p_blk), 0, (val)))
73be2b1797SNate Lawson 
74fec754d4SMike Smith /*
75fec754d4SMike Smith  * Speeds are stored in counts, from 1 - CPU_MAX_SPEED, and
76fec754d4SMike Smith  * reported to the user in tenths of a percent.
77fec754d4SMike Smith  */
78fec754d4SMike Smith static u_int32_t	cpu_duty_offset;
79fec754d4SMike Smith static u_int32_t	cpu_duty_width;
80fec754d4SMike Smith #define CPU_MAX_SPEED		(1 << cpu_duty_width)
81fec754d4SMike Smith #define CPU_SPEED_PERCENT(x)	((1000 * (x)) / CPU_MAX_SPEED)
82fec754d4SMike Smith #define CPU_SPEED_PRINTABLE(x)	(CPU_SPEED_PERCENT(x) / 10),(CPU_SPEED_PERCENT(x) % 10)
83fec754d4SMike Smith 
84be2b1797SNate Lawson /* XXX Should be a generic way to do this */
85be2b1797SNate Lawson static u_int32_t	cpu_smi_cmd;
86fec754d4SMike Smith static u_int8_t		cpu_pstate_cnt;
87fec754d4SMike Smith 
88fec754d4SMike Smith static u_int32_t	cpu_current_state;
89fec754d4SMike Smith static u_int32_t	cpu_performance_state;
90fec754d4SMike Smith static u_int32_t	cpu_economy_state;
91fec754d4SMike Smith static u_int32_t	cpu_max_state;
92fec754d4SMike Smith 
93fec754d4SMike Smith static device_t		*cpu_devices;
94fec754d4SMike Smith static int		cpu_ndevices;
95fec754d4SMike Smith 
96fec754d4SMike Smith static struct sysctl_ctx_list	acpi_cpu_sysctl_ctx;
97fec754d4SMike Smith static struct sysctl_oid	*acpi_cpu_sysctl_tree;
98fec754d4SMike Smith 
99fec754d4SMike Smith static int	acpi_cpu_probe(device_t dev);
100fec754d4SMike Smith static int	acpi_cpu_attach(device_t dev);
101fec754d4SMike Smith static void	acpi_cpu_init_throttling(void *arg);
102fec754d4SMike Smith static void	acpi_cpu_set_speed(u_int32_t speed);
103899ccf54SMitsuru IWASAKI static void	acpi_cpu_power_profile(void *arg);
104fec754d4SMike Smith static int	acpi_cpu_speed_sysctl(SYSCTL_HANDLER_ARGS);
105fec754d4SMike Smith 
106fec754d4SMike Smith static device_method_t acpi_cpu_methods[] = {
107fec754d4SMike Smith     /* Device interface */
108fec754d4SMike Smith     DEVMETHOD(device_probe,	acpi_cpu_probe),
109fec754d4SMike Smith     DEVMETHOD(device_attach,	acpi_cpu_attach),
110fec754d4SMike Smith 
111fec754d4SMike Smith     {0, 0}
112fec754d4SMike Smith };
113fec754d4SMike Smith 
114fec754d4SMike Smith static driver_t acpi_cpu_driver = {
115fec754d4SMike Smith     "acpi_cpu",
116fec754d4SMike Smith     acpi_cpu_methods,
117fec754d4SMike Smith     sizeof(struct acpi_cpu_softc),
118fec754d4SMike Smith };
119fec754d4SMike Smith 
1203273b005SMike Smith static devclass_t acpi_cpu_devclass;
121fec754d4SMike Smith DRIVER_MODULE(acpi_cpu, acpi, acpi_cpu_driver, acpi_cpu_devclass, 0, 0);
122fec754d4SMike Smith 
123fec754d4SMike Smith static int
124fec754d4SMike Smith acpi_cpu_probe(device_t dev)
125fec754d4SMike Smith {
126be2b1797SNate Lawson     if (!acpi_disabled("cpu") && acpi_get_type(dev) == ACPI_TYPE_PROCESSOR) {
127be2b1797SNate Lawson 	/* XXX get more verbose description? */
128be2b1797SNate Lawson 	device_set_desc(dev, "CPU");
129fec754d4SMike Smith 	return (0);
130fec754d4SMike Smith     }
131be2b1797SNate Lawson 
132fec754d4SMike Smith     return (ENXIO);
133fec754d4SMike Smith }
134fec754d4SMike Smith 
135fec754d4SMike Smith static int
136fec754d4SMike Smith acpi_cpu_attach(device_t dev)
137fec754d4SMike Smith {
138fec754d4SMike Smith     struct acpi_cpu_softc	*sc;
139fec754d4SMike Smith     struct acpi_softc		*acpi_sc;
140fec754d4SMike Smith     ACPI_OBJECT			processor;
141fec754d4SMike Smith     ACPI_BUFFER			buf;
142fec754d4SMike Smith     ACPI_STATUS			status;
143fec754d4SMike Smith     u_int32_t			p_blk;
144fec754d4SMike Smith     u_int32_t			p_blk_length;
145fec754d4SMike Smith     u_int32_t			duty_end;
146fec754d4SMike Smith     int				rid;
147fec754d4SMike Smith 
148b4a05238SPeter Wemm     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
149fec754d4SMike Smith 
150fec754d4SMike Smith     ACPI_ASSERTLOCK;
151fec754d4SMike Smith 
152fec754d4SMike Smith     sc = device_get_softc(dev);
153fec754d4SMike Smith     sc->cpu_dev = dev;
154fec754d4SMike Smith     sc->cpu_handle = acpi_get_handle(dev);
155fec754d4SMike Smith 
156be2b1797SNate Lawson     /* Get global parameters from the FADT. */
157fec754d4SMike Smith     if (device_get_unit(sc->cpu_dev) == 0) {
158ad5dc75bSMike Smith 	cpu_duty_offset = AcpiGbl_FADT->DutyOffset;
159ad5dc75bSMike Smith 	cpu_duty_width = AcpiGbl_FADT->DutyWidth;
160ad5dc75bSMike Smith 	cpu_smi_cmd = AcpiGbl_FADT->SmiCmd;
161ad5dc75bSMike Smith 	cpu_pstate_cnt = AcpiGbl_FADT->PstateCnt;
162fec754d4SMike Smith 
163be2b1797SNate Lawson 	/* Validate the offset/width */
1643e759f36SMike Smith 	if (cpu_duty_width > 0) {
165fec754d4SMike Smith 	    duty_end = cpu_duty_offset + cpu_duty_width - 1;
166be2b1797SNate Lawson 
167be2b1797SNate Lawson 	    /* Check that it fits */
168fec754d4SMike Smith 	    if (duty_end > 31) {
169fec754d4SMike Smith 		printf("acpi_cpu: CLK_VAL field overflows P_CNT register\n");
170fec754d4SMike Smith 		cpu_duty_width = 0;
171fec754d4SMike Smith 	    }
172be2b1797SNate Lawson 
173be2b1797SNate Lawson 	    /* Check for overlap with the THT_EN bit */
174be2b1797SNate Lawson 	    if (cpu_duty_offset <= 4 && duty_end >= 4) {
175fec754d4SMike Smith 		printf("acpi_cpu: CLK_VAL field overlaps THT_EN bit\n");
176fec754d4SMike Smith 		cpu_duty_width = 0;
177fec754d4SMike Smith 	    }
1783e759f36SMike Smith 	}
179fec754d4SMike Smith 
180fec754d4SMike Smith 	/*
181be2b1797SNate Lawson 	 * Start the throttling process once the probe phase completes, if we
182be2b1797SNate Lawson 	 * think that it's going to be useful.  If the duty width value is
183be2b1797SNate Lawson 	 * zero, there are no significant bits in the register and thus no
184be2b1797SNate Lawson 	 * throttled states.
185fec754d4SMike Smith 	 */
186fec754d4SMike Smith 	if (cpu_duty_width > 0) {
187be2b1797SNate Lawson 	    AcpiOsQueueForExecution(OSD_PRIORITY_LO, acpi_cpu_init_throttling,
188be2b1797SNate Lawson 				    NULL);
189fec754d4SMike Smith 	    acpi_sc = acpi_device_get_parent_softc(dev);
190fec754d4SMike Smith 	    sysctl_ctx_init(&acpi_cpu_sysctl_ctx);
191fec754d4SMike Smith 	    acpi_cpu_sysctl_tree = SYSCTL_ADD_NODE(&acpi_cpu_sysctl_ctx,
192fec754d4SMike Smith 				   SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),
193fec754d4SMike Smith 				   OID_AUTO, "cpu", CTLFLAG_RD, 0, "");
194fec754d4SMike Smith 
195be2b1797SNate Lawson 	    SYSCTL_ADD_INT(&acpi_cpu_sysctl_ctx,
196be2b1797SNate Lawson 			   SYSCTL_CHILDREN(acpi_cpu_sysctl_tree),
197fec754d4SMike Smith 			   OID_AUTO, "max_speed", CTLFLAG_RD,
198fec754d4SMike Smith 			   &cpu_max_state, 0, "maximum CPU speed");
199be2b1797SNate Lawson 	    SYSCTL_ADD_INT(&acpi_cpu_sysctl_ctx,
200be2b1797SNate Lawson 			   SYSCTL_CHILDREN(acpi_cpu_sysctl_tree),
201fec754d4SMike Smith 			   OID_AUTO, "current_speed", CTLFLAG_RD,
202fec754d4SMike Smith 			   &cpu_current_state, 0, "current CPU speed");
203be2b1797SNate Lawson 	    SYSCTL_ADD_PROC(&acpi_cpu_sysctl_ctx,
204be2b1797SNate Lawson 			    SYSCTL_CHILDREN(acpi_cpu_sysctl_tree),
205be2b1797SNate Lawson 			    OID_AUTO, "performance_speed",
206be2b1797SNate Lawson 			    CTLTYPE_INT | CTLFLAG_RW, &cpu_performance_state,
207be2b1797SNate Lawson 			    0, acpi_cpu_speed_sysctl, "I", "");
208be2b1797SNate Lawson 	    SYSCTL_ADD_PROC(&acpi_cpu_sysctl_ctx,
209be2b1797SNate Lawson 			    SYSCTL_CHILDREN(acpi_cpu_sysctl_tree),
210be2b1797SNate Lawson 			    OID_AUTO, "economy_speed",
211be2b1797SNate Lawson 			    CTLTYPE_INT | CTLFLAG_RW, &cpu_economy_state,
212be2b1797SNate Lawson 			    0, acpi_cpu_speed_sysctl, "I", "");
213fec754d4SMike Smith 	}
214fec754d4SMike Smith     }
215fec754d4SMike Smith 
216be2b1797SNate Lawson     /* Get the processor object. */
217fec754d4SMike Smith     buf.Pointer = &processor;
218fec754d4SMike Smith     buf.Length = sizeof(processor);
219be2b1797SNate Lawson     status = AcpiEvaluateObject(sc->cpu_handle, NULL, NULL, &buf);
220be2b1797SNate Lawson     if (ACPI_FAILURE(status)) {
221be2b1797SNate Lawson 	device_printf(sc->cpu_dev, "couldn't get Processor object - %s\n",
222be2b1797SNate Lawson 		      AcpiFormatException(status));
223fec754d4SMike Smith 	return_VALUE (ENXIO);
224fec754d4SMike Smith     }
225fec754d4SMike Smith     if (processor.Type != ACPI_TYPE_PROCESSOR) {
226be2b1797SNate Lawson 	device_printf(sc->cpu_dev, "Processor object has bad type %d\n",
227be2b1797SNate Lawson 		      processor.Type);
228fec754d4SMike Smith 	return_VALUE (ENXIO);
229fec754d4SMike Smith     }
230fec754d4SMike Smith     sc->cpu_id = processor.Processor.ProcId;
231fec754d4SMike Smith 
232fec754d4SMike Smith     /*
233fec754d4SMike Smith      * If it looks like we support throttling, find this CPU's P_BLK.
234fec754d4SMike Smith      *
235fec754d4SMike Smith      * Note that some systems seem to duplicate the P_BLK pointer across
236fec754d4SMike Smith      * multiple CPUs, so not getting the resource is not fatal.
237fec754d4SMike Smith      *
238fec754d4SMike Smith      * XXX should support _PTC here as well, once we work out how to parse it.
239fec754d4SMike Smith      *
240fec754d4SMike Smith      * XXX is it valid to assume that the P_BLK must be 6 bytes long?
241fec754d4SMike Smith      */
242fec754d4SMike Smith     if (cpu_duty_width > 0) {
243fec754d4SMike Smith 	p_blk = processor.Processor.PblkAddress;
244fec754d4SMike Smith 	p_blk_length = processor.Processor.PblkLength;
245fec754d4SMike Smith 
246fec754d4SMike Smith 	/* allocate bus space if possible */
247be2b1797SNate Lawson 	if (p_blk > 0 && p_blk_length == 6) {
248fec754d4SMike Smith 	    rid = 0;
249be2b1797SNate Lawson 	    bus_set_resource(sc->cpu_dev, SYS_RES_IOPORT, rid, p_blk,
250be2b1797SNate Lawson 			     p_blk_length);
251be2b1797SNate Lawson 	    sc->cpu_p_blk = bus_alloc_resource(sc->cpu_dev, SYS_RES_IOPORT,
252be2b1797SNate Lawson 					       &rid, 0, ~0, 1, RF_ACTIVE);
253fec754d4SMike Smith 
254be2b1797SNate Lawson 	    ACPI_DEBUG_PRINT((ACPI_DB_IO, "acpi_cpu%d: throttling with P_BLK "
255be2b1797SNate Lawson 			     "at 0x%x/%d%s\n", device_get_unit(sc->cpu_dev),
256be2b1797SNate Lawson 			     p_blk, p_blk_length,
257f0987736SMitsuru IWASAKI 			     sc->cpu_p_blk ? "" : " (shadowed)"));
258fec754d4SMike Smith 	}
259fec754d4SMike Smith     }
260be2b1797SNate Lawson 
261fec754d4SMike Smith     return_VALUE (0);
262fec754d4SMike Smith }
263fec754d4SMike Smith 
264fec754d4SMike Smith /*
265fec754d4SMike Smith  * Call this *after* all CPUs have been attached.
266fec754d4SMike Smith  *
267fec754d4SMike Smith  * Takes the ACPI lock to avoid fighting anyone over the SMI command
268fec754d4SMike Smith  * port.  Could probably lock less code.
269fec754d4SMike Smith  */
270fec754d4SMike Smith static void
271fec754d4SMike Smith acpi_cpu_init_throttling(void *arg)
272fec754d4SMike Smith {
273e5e5b51fSJohn Baldwin     int cpu_temp_speed;
274fc0ea94aSJohn Baldwin     ACPI_LOCK_DECL;
275fec754d4SMike Smith 
276fec754d4SMike Smith     ACPI_LOCK;
277fec754d4SMike Smith 
278be2b1797SNate Lawson     /* Get set of CPU devices */
279fec754d4SMike Smith     devclass_get_devices(acpi_cpu_devclass, &cpu_devices, &cpu_ndevices);
280fec754d4SMike Smith 
281be2b1797SNate Lawson     /* Initialise throttling states */
282fec754d4SMike Smith     cpu_max_state = CPU_MAX_SPEED;
283fec754d4SMike Smith     cpu_performance_state = cpu_max_state;
284fec754d4SMike Smith     cpu_economy_state = cpu_performance_state / 2;
285be2b1797SNate Lawson 
286be2b1797SNate Lawson     /* 0 is 'reserved' */
287be2b1797SNate Lawson     if (cpu_economy_state == 0)
288fec754d4SMike Smith 	cpu_economy_state++;
289be2b1797SNate Lawson     if (TUNABLE_INT_FETCH("hw.acpi.cpu.performance_speed", &cpu_temp_speed) &&
290be2b1797SNate Lawson 	cpu_temp_speed > 0 && cpu_temp_speed <= cpu_max_state) {
291be2b1797SNate Lawson 
292e5e5b51fSJohn Baldwin 	cpu_performance_state = cpu_temp_speed;
293fec754d4SMike Smith     }
294be2b1797SNate Lawson     if (TUNABLE_INT_FETCH("hw.acpi.cpu.economy_speed", &cpu_temp_speed) &&
295be2b1797SNate Lawson 	cpu_temp_speed > 0 && cpu_temp_speed <= cpu_max_state) {
296be2b1797SNate Lawson 
297be2b1797SNate Lawson 	cpu_economy_state = cpu_temp_speed;
298be2b1797SNate Lawson     }
299be2b1797SNate Lawson 
300be2b1797SNate Lawson     /* Register performance profile change handler */
301be2b1797SNate Lawson     EVENTHANDLER_REGISTER(power_profile_change, acpi_cpu_power_profile, NULL,
302be2b1797SNate Lawson 			  0);
303be2b1797SNate Lawson 
304be2b1797SNate Lawson     /*
305be2b1797SNate Lawson      * If ACPI 2.0+, signal platform that we are taking over throttling
306be2b1797SNate Lawson      * XXX should be a generic interface for this
307be2b1797SNate Lawson      */
308be2b1797SNate Lawson     if (cpu_pstate_cnt != 0)
309be2b1797SNate Lawson 	AcpiOsWritePort(cpu_smi_cmd, cpu_pstate_cnt, 8);
310fec754d4SMike Smith 
311fec754d4SMike Smith     ACPI_UNLOCK;
312fec754d4SMike Smith 
313be2b1797SNate Lawson     /* Set initial speed */
314899ccf54SMitsuru IWASAKI     acpi_cpu_power_profile(NULL);
315fec754d4SMike Smith 
316a40f20c7SNate Lawson     printf("acpi_cpu: throttling enabled, %d steps (100%% to %d.%d%%), "
317a40f20c7SNate Lawson 	   "currently %d.%d%%\n", CPU_MAX_SPEED, CPU_SPEED_PRINTABLE(1),
318a40f20c7SNate Lawson 	   CPU_SPEED_PRINTABLE(cpu_current_state));
319fec754d4SMike Smith }
320fec754d4SMike Smith 
321fec754d4SMike Smith /*
322fec754d4SMike Smith  * Set CPUs to the new state.
323fec754d4SMike Smith  *
324fec754d4SMike Smith  * Must be called with the ACPI lock held.
325fec754d4SMike Smith  */
326fec754d4SMike Smith static void
327fec754d4SMike Smith acpi_cpu_set_speed(u_int32_t speed)
328fec754d4SMike Smith {
329fec754d4SMike Smith     struct acpi_cpu_softc	*sc;
330fec754d4SMike Smith     int				i;
331fec754d4SMike Smith     u_int32_t			p_cnt, clk_val;
332fec754d4SMike Smith 
333fec754d4SMike Smith     ACPI_ASSERTLOCK;
334fec754d4SMike Smith 
335be2b1797SNate Lawson     /* Iterate over processors */
336fec754d4SMike Smith     for (i = 0; i < cpu_ndevices; i++) {
337fec754d4SMike Smith 	sc = device_get_softc(cpu_devices[i]);
338fec754d4SMike Smith 	if (sc->cpu_p_blk == NULL)
339fec754d4SMike Smith 	    continue;
340fec754d4SMike Smith 
341be2b1797SNate Lawson 	/* Get the current P_CNT value and disable throttling */
342fec754d4SMike Smith 	p_cnt = CPU_GET_P_CNT(sc);
343fec754d4SMike Smith 	p_cnt &= ~CPU_P_CNT_THT_EN;
344fec754d4SMike Smith 	CPU_SET_P_CNT(sc, p_cnt);
345fec754d4SMike Smith 
346be2b1797SNate Lawson 	/* If we're at maximum speed, that's all */
347fec754d4SMike Smith 	if (speed < CPU_MAX_SPEED) {
348be2b1797SNate Lawson 	    /* Mask the old CLK_VAL off and or-in the new value */
349fec754d4SMike Smith 	    clk_val = CPU_MAX_SPEED << cpu_duty_offset;
350fec754d4SMike Smith 	    p_cnt &= ~clk_val;
351fec754d4SMike Smith 	    p_cnt |= (speed << cpu_duty_offset);
352fec754d4SMike Smith 
353be2b1797SNate Lawson 	    /* Write the new P_CNT value and then enable throttling */
354fec754d4SMike Smith 	    CPU_SET_P_CNT(sc, p_cnt);
355fec754d4SMike Smith 	    p_cnt |= CPU_P_CNT_THT_EN;
356fec754d4SMike Smith 	    CPU_SET_P_CNT(sc, p_cnt);
357fec754d4SMike Smith 	}
3586971b3c7SMitsuru IWASAKI 	ACPI_VPRINT(sc->cpu_dev, acpi_device_get_parent_softc(sc->cpu_dev),
3596971b3c7SMitsuru IWASAKI 		    "set speed to %d.%d%%\n", CPU_SPEED_PRINTABLE(speed));
360fec754d4SMike Smith     }
361fec754d4SMike Smith     cpu_current_state = speed;
362fec754d4SMike Smith }
363fec754d4SMike Smith 
364fec754d4SMike Smith /*
365fec754d4SMike Smith  * Power profile change hook.
366fec754d4SMike Smith  *
367fec754d4SMike Smith  * Uses the ACPI lock to avoid reentrancy.
368fec754d4SMike Smith  */
369fec754d4SMike Smith static void
370899ccf54SMitsuru IWASAKI acpi_cpu_power_profile(void *arg)
371fec754d4SMike Smith {
372899ccf54SMitsuru IWASAKI     int		state;
373fec754d4SMike Smith     u_int32_t	new;
374fc0ea94aSJohn Baldwin     ACPI_LOCK_DECL;
375fec754d4SMike Smith 
376899ccf54SMitsuru IWASAKI     state = power_profile_get_state();
377be2b1797SNate Lawson     if (state != POWER_PROFILE_PERFORMANCE && state != POWER_PROFILE_ECONOMY)
378899ccf54SMitsuru IWASAKI 	return;
379899ccf54SMitsuru IWASAKI 
380fec754d4SMike Smith     ACPI_LOCK;
381fec754d4SMike Smith 
382899ccf54SMitsuru IWASAKI     switch (state) {
383899ccf54SMitsuru IWASAKI     case POWER_PROFILE_PERFORMANCE:
384899ccf54SMitsuru IWASAKI 	new = cpu_performance_state;
385899ccf54SMitsuru IWASAKI 	break;
386899ccf54SMitsuru IWASAKI     case POWER_PROFILE_ECONOMY:
387899ccf54SMitsuru IWASAKI 	new = cpu_economy_state;
388899ccf54SMitsuru IWASAKI 	break;
389899ccf54SMitsuru IWASAKI     default:
390899ccf54SMitsuru IWASAKI 	new = cpu_current_state;
391899ccf54SMitsuru IWASAKI 	break;
392899ccf54SMitsuru IWASAKI     }
393899ccf54SMitsuru IWASAKI 
394fec754d4SMike Smith     if (cpu_current_state != new)
395fec754d4SMike Smith 	acpi_cpu_set_speed(new);
396fec754d4SMike Smith 
397fec754d4SMike Smith     ACPI_UNLOCK;
398fec754d4SMike Smith }
399fec754d4SMike Smith 
400fec754d4SMike Smith /*
401fec754d4SMike Smith  * Handle changes in the performance/ecomony CPU settings.
402fec754d4SMike Smith  *
403fec754d4SMike Smith  * Does not need the ACPI lock (although setting *argp should
404fec754d4SMike Smith  * probably be atomic).
405fec754d4SMike Smith  */
406fec754d4SMike Smith static int
407fec754d4SMike Smith acpi_cpu_speed_sysctl(SYSCTL_HANDLER_ARGS)
408fec754d4SMike Smith {
409fec754d4SMike Smith     u_int32_t	*argp;
410fec754d4SMike Smith     u_int32_t	arg;
411fec754d4SMike Smith     int		error;
412fec754d4SMike Smith 
413fec754d4SMike Smith     argp = (u_int32_t *)oidp->oid_arg1;
414fec754d4SMike Smith     arg = *argp;
415fec754d4SMike Smith     error = sysctl_handle_int(oidp, &arg, 0, req);
416fec754d4SMike Smith 
417be2b1797SNate Lawson     /* Error or no new value */
418be2b1797SNate Lawson     if (error != 0 || req->newptr == NULL)
419fec754d4SMike Smith 	return (error);
420be2b1797SNate Lawson     if (arg < 1 || arg > cpu_max_state)
421fec754d4SMike Smith 	return (EINVAL);
422fec754d4SMike Smith 
423be2b1797SNate Lawson     /* Set new value and possibly switch */
424fec754d4SMike Smith     *argp = arg;
425899ccf54SMitsuru IWASAKI     acpi_cpu_power_profile(NULL);
426fec754d4SMike Smith 
427fec754d4SMike Smith     return (0);
428fec754d4SMike Smith }
429