4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 * Copyright (c) 2009, Intel Corporation.
27 * All Rights Reserved.
31 * CPU power management driver support for i86pc.
35 #include <sys/sunddi.h>
36 #include <sys/cpupm.h>
37 #include <sys/cpudrv_mach.h>
38 #include <sys/machsystm.h>
39 #include <sys/cpu_pm.h>
40 #include <sys/cpuvar.h>
42 #include <sys/cpu_idle.h>
45 * Note that our driver numbers the power levels from lowest to
46 * highest starting at 1 (i.e., the lowest power level is 1 and
47 * the highest power level is cpupm->num_spd). The x86 modules get
48 * their power levels from ACPI which numbers power levels from
49 * highest to lowest starting at 0 (i.e., the lowest power level
50 * is (cpupm->num_spd - 1) and the highest power level is 0). So to
51 * map one of our driver power levels to one understood by ACPI we
52 * simply subtract our driver power level from cpupm->num_spd. Likewise,
53 * to map an ACPI power level to the proper driver power level, we
54 * subtract the ACPI power level from cpupm->num_spd.
56 #define PM_2_PLAT_LEVEL(cpupm, pm_level) (cpupm->num_spd - pm_level)
57 #define PLAT_2_PM_LEVEL(cpupm, plat_level) (cpupm->num_spd - plat_level)
60 * Change CPU speed using interface provided by module.
63 cpudrv_change_speed(cpudrv_devstate_t
*cpudsp
, cpudrv_pm_spd_t
*new_spd
)
65 cpu_t
*cp
= cpudsp
->cp
;
66 cpupm_mach_state_t
*mach_state
=
67 (cpupm_mach_state_t
*)cp
->cpu_m
.mcpu_pm_mach_state
;
72 if (!(mach_state
->ms_caps
& CPUPM_P_STATES
))
74 ASSERT(mach_state
->ms_pstate
.cma_ops
!= NULL
);
75 cpupm
= &(cpudsp
->cpudrv_pm
);
76 plat_level
= PM_2_PLAT_LEVEL(cpupm
, new_spd
->pm_level
);
77 CPUSET_ONLY(set
, cp
->cpu_id
);
78 mach_state
->ms_pstate
.cma_ops
->cpus_change(set
, plat_level
);
84 * Determine the cpu_id for the CPU device.
87 cpudrv_get_cpu_id(dev_info_t
*dip
, processorid_t
*cpu_id
)
89 return ((*cpu_id
= ddi_prop_get_int(DDI_DEV_T_ANY
, dip
,
90 DDI_PROP_DONTPASS
, "reg", -1)) != -1);
95 cpudrv_is_enabled(cpudrv_devstate_t
*cpudsp
)
97 cpupm_mach_state_t
*mach_state
;
99 if (!cpupm_is_enabled(CPUPM_P_STATES
) || !cpudrv_enabled
)
103 * Only check the instance specific setting it exists.
105 if (cpudsp
!= NULL
&& cpudsp
->cp
!= NULL
&&
106 cpudsp
->cp
->cpu_m
.mcpu_pm_mach_state
!= NULL
) {
108 (cpupm_mach_state_t
*)cpudsp
->cp
->cpu_m
.mcpu_pm_mach_state
;
109 return (mach_state
->ms_caps
& CPUPM_P_STATES
);
116 * Is the current thread the thread that is handling the
117 * PPC change notification?
120 cpudrv_is_governor_thread(cpudrv_pm_t
*cpupm
)
122 return (curthread
== cpupm
->pm_governor_thread
);
126 * This routine changes the top speed to which the CPUs can transition by:
128 * - Resetting the up_spd for all speeds lower than the new top speed
129 * to point to the new top speed.
130 * - Updating the framework with a new "normal" (maximum power) for this
134 cpudrv_set_topspeed(void *ctx
, int plat_level
)
136 cpudrv_devstate_t
*cpudsp
;
138 cpudrv_pm_spd_t
*spd
;
139 cpudrv_pm_spd_t
*top_spd
;
146 instance
= ddi_get_instance(dip
);
147 cpudsp
= ddi_get_soft_state(cpudrv_state
, instance
);
148 ASSERT(cpudsp
!= NULL
);
150 mutex_enter(&cpudsp
->lock
);
151 cpupm
= &(cpudsp
->cpudrv_pm
);
152 pm_level
= PLAT_2_PM_LEVEL(cpupm
, plat_level
);
153 for (i
= 0, spd
= cpupm
->head_spd
; spd
; i
++, spd
= spd
->down_spd
) {
155 * Don't mess with speeds that are higher than the new
156 * top speed. They should be out of range anyway.
158 if (spd
->pm_level
> pm_level
)
161 * This is the new top speed.
163 if (spd
->pm_level
== pm_level
)
166 spd
->up_spd
= top_spd
;
168 cpupm
->top_spd
= top_spd
;
170 cpupm
->pm_governor_thread
= curthread
;
172 mutex_exit(&cpudsp
->lock
);
174 (void) pm_update_maxpower(dip
, 0, top_spd
->pm_level
);
178 * This routine reads the ACPI _PPC object. It's accessed as a callback
179 * by the ppm driver whenever a _PPC change notification is received.
182 cpudrv_get_topspeed(void *ctx
)
185 cpudrv_devstate_t
*cpudsp
;
191 instance
= ddi_get_instance(dip
);
192 cpudsp
= ddi_get_soft_state(cpudrv_state
, instance
);
193 ASSERT(cpudsp
!= NULL
);
195 plat_level
= cpupm_get_top_speed(cp
);
202 * This notification handler is called whenever the ACPI _PPC
203 * object changes. The _PPC is a sort of governor on power levels.
204 * It sets an upper threshold on which, _PSS defined, power levels
205 * are usuable. The _PPC value is dynamic and may change as properties
206 * (i.e., thermal or AC source) of the system change.
210 cpudrv_notify_handler(ACPI_HANDLE obj
, UINT32 val
, void *ctx
)
213 cpupm_mach_state_t
*mach_state
;
214 cpudrv_devstate_t
*cpudsp
;
217 extern pm_cpupm_t cpupm
;
220 instance
= ddi_get_instance(dip
);
221 cpudsp
= ddi_get_soft_state(cpudrv_state
, instance
);
225 mach_state
= (cpupm_mach_state_t
*)cp
->cpu_m
.mcpu_pm_mach_state
;
226 if (mach_state
== NULL
)
230 * We only handle _PPC change notifications.
232 if (!PM_EVENT_CPUPM
&& val
== CPUPM_PPC_CHANGE_NOTIFICATION
&&
233 mach_state
->ms_caps
& CPUPM_P_STATES
)
234 cpudrv_redefine_topspeed(ctx
);
238 cpudrv_install_notify_handler(cpudrv_devstate_t
*cpudsp
)
240 cpu_t
*cp
= cpudsp
->cp
;
241 cpupm_add_notify_handler(cp
, cpudrv_notify_handler
,
246 cpudrv_uninstall_notify_handler(cpudrv_devstate_t
*cpudsp
)
248 cpu_t
*cp
= cpudsp
->cp
;
249 cpupm_notification_t
*entry
, **next
;
252 cpupm_mach_state_t
*mach_state
=
253 (cpupm_mach_state_t
*)cp
->cpu_m
.mcpu_pm_mach_state
;
255 mutex_enter(&mach_state
->ms_lock
);
256 if (mach_state
->ms_handlers
== NULL
) {
257 mutex_exit(&mach_state
->ms_lock
);
261 for (next
= &mach_state
->ms_handlers
; (entry
= *next
) != NULL
; ) {
262 if (entry
->nq_handler
!= cpudrv_notify_handler
) {
263 next
= &entry
->nq_next
;
266 *next
= entry
->nq_next
;
267 kmem_free(entry
, sizeof (cpupm_notification_t
));
269 mutex_exit(&mach_state
->ms_lock
);
273 cpudrv_redefine_topspeed(void *ctx
)
276 * This should never happen, unless ppm does not get loaded.
278 if (cpupm_redefine_topspeed
== NULL
) {
279 cmn_err(CE_WARN
, "cpudrv_redefine_topspeed: "
280 "cpupm_redefine_topspeed has not been initialized - "
281 "ignoring notification");
286 * ppm callback needs to handle redefinition for all CPUs in
289 (*cpupm_redefine_topspeed
)(ctx
);
293 cpudrv_mach_init(cpudrv_devstate_t
*cpudsp
)
295 cpupm_mach_state_t
*mach_state
;
300 mach_state
= (cpupm_mach_state_t
*)
301 (cpudsp
->cp
->cpu_m
.mcpu_pm_mach_state
);
302 mach_state
->ms_dip
= cpudsp
->dip
;
304 * allocate ppm CPU domain and initialize the topspeed
305 * only if P-states are enabled.
307 if (cpudrv_power_ready(cpudsp
->cp
)) {
308 (*cpupm_ppm_alloc_pstate_domains
)(cpudsp
->cp
);
309 topspeed
= cpudrv_get_topspeed(cpudsp
->dip
);
310 cpudrv_set_topspeed(cpudsp
->dip
, topspeed
);
317 cpudrv_mach_fini(cpudrv_devstate_t
*cpudsp
)
320 * return TRUE if cpu pointer is NULL
322 if (cpudsp
->cp
== NULL
)
325 * free ppm cpu pstate domains only if
326 * P-states are enabled
328 if (cpudrv_power_ready(cpudsp
->cp
)) {
329 (*cpupm_ppm_free_pstate_domains
)(cpudsp
->cp
);
336 cpudrv_get_speeds(cpudrv_devstate_t
*cpudsp
, int **speeds
)
339 * return nspeeds = 0 if can't get cpu_t
341 if (cpudrv_get_cpu(cpudsp
) != DDI_SUCCESS
)
344 return (cpupm_get_speeds(cpudsp
->cp
, speeds
));
348 cpudrv_free_speeds(int *speeds
, uint_t nspeeds
)
350 cpupm_free_speeds(speeds
, nspeeds
);
354 cpudrv_power_ready(cpu_t
*cp
)
356 return (cpupm_power_ready(cp
));
361 cpudrv_set_supp_freqs(cpudrv_devstate_t
*cpudsp
)