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 * Platform Power Management master pseudo driver platform support.
35 #include <sys/sunddi.h>
36 #include <sys/ppmvar.h>
37 #include <sys/cpupm.h>
39 #define PPM_CPU_PSTATE_DOMAIN_FLG 0x100
42 * Used by ppm_redefine_topspeed() to set the highest power level of all CPUs
46 ppm_set_topspeed(ppm_dev_t
*cpup
, int speed
)
48 for (cpup
= cpup
->domp
->devlist
; cpup
!= NULL
; cpup
= cpup
->next
)
49 (*cpupm_set_topspeed_callb
)(cpup
->dip
, speed
);
53 * Redefine the highest power level for all CPUs in a domain. This
54 * functionality is necessary because ACPI uses the _PPC to define
55 * a CPU's highest power level *and* allows the _PPC to be redefined
56 * dynamically. _PPC changes are communicated through _PPC change
57 * notifications caught by the CPU device driver.
60 ppm_redefine_topspeed(void *ctx
)
62 char *str
= "ppm_redefine_topspeed";
68 cpup
= PPM_GET_PRIVATE((dev_info_t
*)ctx
);
70 if (cpupm_get_topspeed_callb
== NULL
||
71 cpupm_set_topspeed_callb
== NULL
) {
72 cmn_err(CE_WARN
, "%s: Cannot process request for instance %d "
73 "since cpupm interfaces are not initialized", str
,
74 ddi_get_instance(cpup
->dip
));
78 if (!(cpup
->domp
->dflags
& PPMD_CPU_READY
)) {
79 PPMD(D_CPU
, ("%s: instance %d received _PPC change "
80 "notification before PPMD_CPU_READY", str
,
81 ddi_get_instance(cpup
->dip
)));
86 * Process each CPU in the domain.
88 for (ncpup
= cpup
->domp
->devlist
; ncpup
!= NULL
; ncpup
= ncpup
->next
) {
89 topspeed
= (*cpupm_get_topspeed_callb
)(ncpup
->dip
);
90 if (newspeed
== -1 || topspeed
< newspeed
)
94 ppm_set_topspeed(cpup
, newspeed
);
98 * For x86 platforms CPU domains must be built dynamically at bootime.
99 * Until the domains have been built, refuse all power transition
104 ppm_manage_early_cpus(dev_info_t
*dip
, int new, int *result
)
106 ppm_dev_t
*ppmd
= PPM_GET_PRIVATE(dip
);
108 if (!(ppmd
->domp
->dflags
& PPMD_CPU_READY
)) {
109 PPMD(D_CPU
, ("ppm_manage_early_cpus: attempt to manage CPU "
110 "before it was ready dip(0x%p)", (void *)dip
));
113 *result
= DDI_FAILURE
;
118 ppm_change_cpu_power(ppm_dev_t
*ppmd
, int newlevel
)
121 char *str
= "ppm_change_cpu_power";
130 unitp
= ddi_get_soft_state(ppm_statep
, ppm_inst
);
133 cpup
= domp
->devlist
;
138 oldlevel
= cpup
->level
;
140 PPMD(D_CPU
, ("%s: old %d, new %d\n", str
, oldlevel
, newlevel
))
142 if (newlevel
== oldlevel
)
143 return (DDI_SUCCESS
);
145 /* bring each cpu to next level */
146 for (; cpup
; cpup
= cpup
->next
) {
147 ret
= pm_power(cpup
->dip
, 0, newlevel
);
148 PPMD(D_CPU
, ("%s: \"%s\", changed to level %d, ret %d\n",
149 str
, cpup
->path
, newlevel
, ret
))
150 if (ret
== DDI_SUCCESS
) {
151 cpup
->level
= newlevel
;
152 cpup
->rplvl
= PM_LEVEL_UNKNOWN
;
157 * If the driver was unable to lower cpu speed,
158 * the cpu probably got busy; set the previous
159 * cpus back to the original level
161 if (newlevel
< oldlevel
)
162 ret
= ppm_revert_cpu_power(cpup
, oldlevel
);
167 return (DDI_SUCCESS
);
171 * allocate ppm CPU pstate domain if non-existence,
172 * otherwise, add the CPU to the corresponding ppm
176 ppm_alloc_pstate_domains(cpu_t
*cp
)
178 cpupm_mach_state_t
*mach_state
;
184 char path
[MAXNAMELEN
];
186 mach_state
= (cpupm_mach_state_t
*)(cp
->cpu_m
.mcpu_pm_mach_state
);
188 pm_domain
= mach_state
->ms_pstate
.cma_domain
->pm_domain
;
191 * There are two purposes of sub_domain:
192 * 1. skip the orignal ppm CPU domain generated by ppm.conf
193 * 2. A CPU ppm domain could have several pstate domains indeed.
195 sub_domain
= pm_domain
| PPM_CPU_PSTATE_DOMAIN_FLG
;
198 * Find ppm CPU pstate domain
200 for (domp
= ppm_domain_p
; domp
; domp
= domp
->next
) {
201 if ((domp
->model
== PPMD_CPU
) &&
202 (domp
->sub_domain
== sub_domain
)) {
208 * Create one ppm CPU pstate domain if no found
211 domp
= kmem_zalloc(sizeof (*domp
), KM_SLEEP
);
212 mutex_init(&domp
->lock
, NULL
, MUTEX_DRIVER
, NULL
);
213 mutex_enter(&domp
->lock
);
214 domp
->name
= kmem_zalloc(MAXNAMELEN
, KM_SLEEP
);
215 (void) snprintf(domp
->name
, MAXNAMELEN
, "cpu_pstate_domain_%d",
217 domp
->sub_domain
= sub_domain
;
218 domp
->dflags
= PPMD_LOCK_ALL
| PPMD_CPU_READY
;
221 domp
->propname
= NULL
;
222 domp
->model
= PPMD_CPU
;
223 domp
->status
= PPMD_ON
;
224 cpu_dip
= mach_state
->ms_dip
;
225 (void) ddi_pathname(cpu_dip
, path
);
226 dbp
= kmem_zalloc(sizeof (struct ppm_db
), KM_SLEEP
);
227 dbp
->name
= kmem_zalloc((strlen(path
) + 1),
229 (void) strcpy(dbp
->name
, path
);
230 dbp
->next
= domp
->conflist
;
231 domp
->conflist
= dbp
;
232 domp
->next
= ppm_domain_p
;
234 mutex_exit(&domp
->lock
);
237 * We found one matched ppm CPU pstate domain,
238 * add cpu to this domain
241 mutex_enter(&domp
->lock
);
242 cpu_dip
= mach_state
->ms_dip
;
243 (void) ddi_pathname(cpu_dip
, path
);
244 dbp
= kmem_zalloc(sizeof (struct ppm_db
), KM_SLEEP
);
245 dbp
->name
= kmem_zalloc((strlen(path
) + 1),
247 (void) strcpy(dbp
->name
, path
);
248 dbp
->next
= domp
->conflist
;
249 domp
->conflist
= dbp
;
251 mutex_exit(&domp
->lock
);
256 * remove CPU from the corresponding ppm CPU pstate
257 * domain. We only remove CPU from conflist here.
260 ppm_free_pstate_domains(cpu_t
*cp
)
262 cpupm_mach_state_t
*mach_state
;
266 ppm_db_t
**dbpp
, *pconf
;
267 char path
[MAXNAMELEN
];
269 mach_state
= (cpupm_mach_state_t
*)(cp
->cpu_m
.mcpu_pm_mach_state
);
271 cpu_dip
= mach_state
->ms_dip
;
272 (void) ddi_pathname(cpu_dip
, path
);
275 * get ppm CPU pstate domain
277 devp
= PPM_GET_PRIVATE(cpu_dip
);
283 * remove CPU from conflist
285 mutex_enter(&domp
->lock
);
286 for (dbpp
= &domp
->conflist
; (pconf
= *dbpp
) != NULL
; ) {
287 if (strcmp(pconf
->name
, path
) != 0) {
292 kmem_free(pconf
->name
, strlen(pconf
->name
) + 1);
293 kmem_free(pconf
, sizeof (*pconf
));
295 mutex_exit(&domp
->lock
);