xref: /illumos-gate/usr/src/uts/i86pc/io/cpudrv_mach.c (revision fb2a9bae0030340ad72b9c26ba1ffee2ee3cafec)
1 /*
2  * CDDL HEADER START
3  *
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.
7  *
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.
12  *
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]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 /*
26  * Copyright (c) 2009,  Intel Corporation.
27  * All Rights Reserved.
28  */
29 
30 /*
31  * CPU power management driver support for i86pc.
32  */
33 
34 #include <sys/ddi.h>
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>
41 #include <sys/sdt.h>
42 #include <sys/cpu_idle.h>
43 
44 /*
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.
55  */
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)
58 
59 /*
60  * Change CPU speed using interface provided by module.
61  */
62 int
63 cpudrv_change_speed(cpudrv_devstate_t *cpudsp, cpudrv_pm_spd_t *new_spd)
64 {
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;
68 	cpudrv_pm_t *cpupm;
69 	cpuset_t set;
70 	uint32_t plat_level;
71 
72 	if (!(mach_state->ms_caps & CPUPM_P_STATES))
73 		return (DDI_FAILURE);
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);
79 
80 	return (DDI_SUCCESS);
81 }
82 
83 /*
84  * Determine the cpu_id for the CPU device.
85  */
86 boolean_t
87 cpudrv_get_cpu_id(dev_info_t *dip,  processorid_t *cpu_id)
88 {
89 	return ((*cpu_id = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
90 	    DDI_PROP_DONTPASS, "reg", -1)) != -1);
91 
92 }
93 
94 boolean_t
95 cpudrv_is_enabled(cpudrv_devstate_t *cpudsp)
96 {
97 	cpupm_mach_state_t *mach_state;
98 
99 	if (!cpupm_is_enabled(CPUPM_P_STATES) || !cpudrv_enabled)
100 		return (B_FALSE);
101 
102 	/*
103 	 * Only check the instance specific setting it exists.
104 	 */
105 	if (cpudsp != NULL && cpudsp->cp != NULL &&
106 	    cpudsp->cp->cpu_m.mcpu_pm_mach_state != NULL) {
107 		mach_state =
108 		    (cpupm_mach_state_t *)cpudsp->cp->cpu_m.mcpu_pm_mach_state;
109 		return (mach_state->ms_caps & CPUPM_P_STATES);
110 	}
111 
112 	return (B_TRUE);
113 }
114 
115 /*
116  * Is the current thread the thread that is handling the
117  * PPC change notification?
118  */
119 boolean_t
120 cpudrv_is_governor_thread(cpudrv_pm_t *cpupm)
121 {
122 	return (curthread == cpupm->pm_governor_thread);
123 }
124 
125 /*
126  * This routine changes the top speed to which the CPUs can transition by:
127  *
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
131  *   device.
132  */
133 void
134 cpudrv_set_topspeed(void *ctx, int plat_level)
135 {
136 	cpudrv_devstate_t *cpudsp;
137 	cpudrv_pm_t *cpupm;
138 	cpudrv_pm_spd_t	*spd;
139 	cpudrv_pm_spd_t	*top_spd;
140 	dev_info_t *dip;
141 	int pm_level;
142 	int instance;
143 	int i;
144 
145 	dip = ctx;
146 	instance = ddi_get_instance(dip);
147 	cpudsp = ddi_get_soft_state(cpudrv_state, instance);
148 	ASSERT(cpudsp != NULL);
149 
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) {
154 		/*
155 		 * Don't mess with speeds that are higher than the new
156 		 * top speed. They should be out of range anyway.
157 		 */
158 		if (spd->pm_level > pm_level)
159 			continue;
160 		/*
161 		 * This is the new top speed.
162 		 */
163 		if (spd->pm_level == pm_level)
164 			top_spd = spd;
165 
166 		spd->up_spd = top_spd;
167 	}
168 	cpupm->top_spd = top_spd;
169 
170 	cpupm->pm_governor_thread = curthread;
171 
172 	mutex_exit(&cpudsp->lock);
173 
174 	(void) pm_update_maxpower(dip, 0, top_spd->pm_level);
175 }
176 
177 /*
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.
180  */
181 int
182 cpudrv_get_topspeed(void *ctx)
183 {
184 	cpu_t *cp;
185 	cpudrv_devstate_t *cpudsp;
186 	dev_info_t *dip;
187 	int instance;
188 	int plat_level;
189 
190 	dip = ctx;
191 	instance = ddi_get_instance(dip);
192 	cpudsp = ddi_get_soft_state(cpudrv_state, instance);
193 	ASSERT(cpudsp != NULL);
194 	cp = cpudsp->cp;
195 	plat_level = cpupm_get_top_speed(cp);
196 
197 	return (plat_level);
198 }
199 
200 
201 /*
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.
207  */
208 /* ARGSUSED */
209 static void
210 cpudrv_notify_handler(ACPI_HANDLE obj, UINT32 val, void *ctx)
211 {
212 	cpu_t			*cp;
213 	cpupm_mach_state_t	*mach_state;
214 	cpudrv_devstate_t	*cpudsp;
215 	dev_info_t		*dip;
216 	int			instance;
217 	extern pm_cpupm_t	cpupm;
218 
219 	dip = ctx;
220 	instance = ddi_get_instance(dip);
221 	cpudsp = ddi_get_soft_state(cpudrv_state, instance);
222 	if (cpudsp == NULL)
223 		return;
224 	cp = cpudsp->cp;
225 	mach_state = (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state;
226 	if (mach_state == NULL)
227 		return;
228 
229 	/*
230 	 * We only handle _PPC change notifications.
231 	 */
232 	if (!PM_EVENT_CPUPM && val == CPUPM_PPC_CHANGE_NOTIFICATION &&
233 	    mach_state->ms_caps & CPUPM_P_STATES)
234 		cpudrv_redefine_topspeed(ctx);
235 }
236 
237 void
238 cpudrv_install_notify_handler(cpudrv_devstate_t *cpudsp)
239 {
240 	cpu_t *cp = cpudsp->cp;
241 	cpupm_add_notify_handler(cp, cpudrv_notify_handler,
242 	    cpudsp->dip);
243 }
244 
245 void
246 cpudrv_uninstall_notify_handler(cpudrv_devstate_t *cpudsp)
247 {
248 	cpu_t *cp = cpudsp->cp;
249 	cpupm_notification_t *entry, **next;
250 
251 	ASSERT(cp != NULL);
252 	cpupm_mach_state_t *mach_state =
253 	    (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state;
254 
255 	mutex_enter(&mach_state->ms_lock);
256 	if (mach_state->ms_handlers == NULL) {
257 		mutex_exit(&mach_state->ms_lock);
258 		return;
259 	}
260 
261 	for (next = &mach_state->ms_handlers; (entry = *next) != NULL; ) {
262 		if (entry->nq_handler != cpudrv_notify_handler) {
263 			next = &entry->nq_next;
264 			continue;
265 		}
266 		*next = entry->nq_next;
267 		kmem_free(entry, sizeof (cpupm_notification_t));
268 	}
269 	mutex_exit(&mach_state->ms_lock);
270 }
271 
272 void
273 cpudrv_redefine_topspeed(void *ctx)
274 {
275 	/*
276 	 * This should never happen, unless ppm does not get loaded.
277 	 */
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");
282 		return;
283 	}
284 
285 	/*
286 	 * ppm callback needs to handle redefinition for all CPUs in
287 	 * the domain.
288 	 */
289 	(*cpupm_redefine_topspeed)(ctx);
290 }
291 
292 boolean_t
293 cpudrv_mach_init(cpudrv_devstate_t *cpudsp)
294 {
295 	cpupm_mach_state_t *mach_state;
296 	int topspeed;
297 
298 	ASSERT(cpudsp->cp);
299 
300 	mach_state = (cpupm_mach_state_t *)
301 	    (cpudsp->cp->cpu_m.mcpu_pm_mach_state);
302 	mach_state->ms_dip = cpudsp->dip;
303 	/*
304 	 * allocate ppm CPU domain and initialize the topspeed
305 	 * only if P-states are enabled.
306 	 */
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);
311 	}
312 
313 	return (B_TRUE);
314 }
315 
316 boolean_t
317 cpudrv_mach_fini(cpudrv_devstate_t *cpudsp)
318 {
319 	/*
320 	 * return TRUE if cpu pointer is NULL
321 	 */
322 	if (cpudsp->cp == NULL)
323 		return (B_TRUE);
324 	/*
325 	 * free ppm cpu pstate domains only if
326 	 * P-states are enabled
327 	 */
328 	if (cpudrv_power_ready(cpudsp->cp)) {
329 		(*cpupm_ppm_free_pstate_domains)(cpudsp->cp);
330 	}
331 
332 	return (B_TRUE);
333 }
334 
335 uint_t
336 cpudrv_get_speeds(cpudrv_devstate_t *cpudsp, int **speeds)
337 {
338 	/*
339 	 * return nspeeds = 0 if can't get cpu_t
340 	 */
341 	if (cpudrv_get_cpu(cpudsp) != DDI_SUCCESS)
342 		return (0);
343 
344 	return (cpupm_get_speeds(cpudsp->cp, speeds));
345 }
346 
347 void
348 cpudrv_free_speeds(int *speeds, uint_t nspeeds)
349 {
350 	cpupm_free_speeds(speeds, nspeeds);
351 }
352 
353 boolean_t
354 cpudrv_power_ready(cpu_t *cp)
355 {
356 	return (cpupm_power_ready(cp));
357 }
358 
359 /* ARGSUSED */
360 void
361 cpudrv_set_supp_freqs(cpudrv_devstate_t *cpudsp)
362 {
363 }
364