xref: /titanic_44/usr/src/uts/i86pc/io/cpudrv_mach.c (revision 0a1ad920531b37f01f4aa8084737026621c76bdb)
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 /*
27  * CPU power management driver support for i86pc.
28  */
29 
30 #include <sys/ddi.h>
31 #include <sys/sunddi.h>
32 #include <sys/cpupm.h>
33 #include <sys/cpudrv_mach.h>
34 #include <sys/machsystm.h>
35 #include <sys/cpu_pm.h>
36 #include <sys/cpuvar.h>
37 #include <sys/sdt.h>
38 #include <sys/cpu_idle.h>
39 
40 /*
41  * Note that our driver numbers the power levels from lowest to
42  * highest starting at 1 (i.e., the lowest power level is 1 and
43  * the highest power level is cpupm->num_spd). The x86 modules get
44  * their power levels from ACPI which numbers power levels from
45  * highest to lowest starting at 0 (i.e., the lowest power level
46  * is (cpupm->num_spd - 1) and the highest power level is 0). So to
47  * map one of our driver power levels to one understood by ACPI we
48  * simply subtract our driver power level from cpupm->num_spd. Likewise,
49  * to map an ACPI power level to the proper driver power level, we
50  * subtract the ACPI power level from cpupm->num_spd.
51  */
52 #define	PM_2_PLAT_LEVEL(cpupm, pm_level) (cpupm->num_spd - pm_level)
53 #define	PLAT_2_PM_LEVEL(cpupm, plat_level) (cpupm->num_spd - plat_level)
54 
55 /*
56  * Change CPU speed using interface provided by module.
57  */
58 int
59 cpudrv_change_speed(cpudrv_devstate_t *cpudsp, cpudrv_pm_spd_t *new_spd)
60 {
61 	cpu_t *cp = cpudsp->cp;
62 	cpupm_mach_state_t *mach_state =
63 	    (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state;
64 	cpudrv_pm_t *cpupm;
65 	cpuset_t set;
66 	uint32_t plat_level;
67 
68 	if (!(mach_state->ms_caps & CPUPM_P_STATES))
69 		return (DDI_FAILURE);
70 	ASSERT(mach_state->ms_pstate.cma_ops != NULL);
71 	cpupm = &(cpudsp->cpudrv_pm);
72 	plat_level = PM_2_PLAT_LEVEL(cpupm, new_spd->pm_level);
73 	CPUSET_ONLY(set, cp->cpu_id);
74 	mach_state->ms_pstate.cma_ops->cpus_change(set, plat_level);
75 
76 	return (DDI_SUCCESS);
77 }
78 
79 /*
80  * Determine the cpu_id for the CPU device.
81  */
82 boolean_t
83 cpudrv_get_cpu_id(dev_info_t *dip,  processorid_t *cpu_id)
84 {
85 	return ((*cpu_id = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
86 	    DDI_PROP_DONTPASS, "reg", -1)) != -1);
87 
88 }
89 
90 boolean_t
91 cpudrv_is_enabled(cpudrv_devstate_t *cpudsp)
92 {
93 	cpupm_mach_state_t *mach_state;
94 
95 	if (!cpupm_is_enabled(CPUPM_P_STATES) || !cpudrv_enabled)
96 		return (B_FALSE);
97 
98 	/*
99 	 * Only check the instance specific setting it exists.
100 	 */
101 	if (cpudsp != NULL && cpudsp->cp != NULL &&
102 	    cpudsp->cp->cpu_m.mcpu_pm_mach_state != NULL) {
103 		mach_state =
104 		    (cpupm_mach_state_t *)cpudsp->cp->cpu_m.mcpu_pm_mach_state;
105 		return (mach_state->ms_caps & CPUPM_P_STATES);
106 	}
107 
108 	return (B_TRUE);
109 }
110 
111 /*
112  * Is the current thread the thread that is handling the
113  * PPC change notification?
114  */
115 boolean_t
116 cpudrv_is_governor_thread(cpudrv_pm_t *cpupm)
117 {
118 	return (curthread == cpupm->pm_governor_thread);
119 }
120 
121 /*
122  * This routine changes the top speed to which the CPUs can transition by:
123  *
124  * - Resetting the up_spd for all speeds lower than the new top speed
125  *   to point to the new top speed.
126  * - Updating the framework with a new "normal" (maximum power) for this
127  *   device.
128  */
129 void
130 cpudrv_set_topspeed(void *ctx, int plat_level)
131 {
132 	cpudrv_devstate_t *cpudsp;
133 	cpudrv_pm_t *cpupm;
134 	cpudrv_pm_spd_t	*spd;
135 	cpudrv_pm_spd_t	*top_spd;
136 	dev_info_t *dip;
137 	int pm_level;
138 	int instance;
139 	int i;
140 
141 	dip = ctx;
142 	instance = ddi_get_instance(dip);
143 	cpudsp = ddi_get_soft_state(cpudrv_state, instance);
144 	ASSERT(cpudsp != NULL);
145 
146 	mutex_enter(&cpudsp->lock);
147 	cpupm = &(cpudsp->cpudrv_pm);
148 	pm_level = PLAT_2_PM_LEVEL(cpupm, plat_level);
149 	for (i = 0, spd = cpupm->head_spd; spd; i++, spd = spd->down_spd) {
150 		/*
151 		 * Don't mess with speeds that are higher than the new
152 		 * top speed. They should be out of range anyway.
153 		 */
154 		if (spd->pm_level > pm_level)
155 			continue;
156 		/*
157 		 * This is the new top speed.
158 		 */
159 		if (spd->pm_level == pm_level)
160 			top_spd = spd;
161 
162 		spd->up_spd = top_spd;
163 	}
164 	cpupm->top_spd = top_spd;
165 
166 	cpupm->pm_governor_thread = curthread;
167 
168 	mutex_exit(&cpudsp->lock);
169 
170 	(void) pm_update_maxpower(dip, 0, top_spd->pm_level);
171 }
172 
173 /*
174  * This routine reads the ACPI _PPC object. It's accessed as a callback
175  * by the ppm driver whenever a _PPC change notification is received.
176  */
177 int
178 cpudrv_get_topspeed(void *ctx)
179 {
180 	cpu_t *cp;
181 	cpudrv_devstate_t *cpudsp;
182 	dev_info_t *dip;
183 	int instance;
184 	int plat_level;
185 
186 	dip = ctx;
187 	instance = ddi_get_instance(dip);
188 	cpudsp = ddi_get_soft_state(cpudrv_state, instance);
189 	ASSERT(cpudsp != NULL);
190 	cp = cpudsp->cp;
191 	plat_level = cpupm_get_top_speed(cp);
192 
193 	return (plat_level);
194 }
195 
196 
197 /*
198  * This notification handler is called whenever the ACPI _PPC
199  * object changes. The _PPC is a sort of governor on power levels.
200  * It sets an upper threshold on which, _PSS defined, power levels
201  * are usuable. The _PPC value is dynamic and may change as properties
202  * (i.e., thermal or AC source) of the system change.
203  */
204 /* ARGSUSED */
205 static void
206 cpudrv_notify_handler(ACPI_HANDLE obj, UINT32 val, void *ctx)
207 {
208 	cpu_t			*cp;
209 	cpupm_mach_state_t	*mach_state;
210 	cpudrv_devstate_t	*cpudsp;
211 	dev_info_t		*dip;
212 	int			instance;
213 	extern pm_cpupm_t	cpupm;
214 
215 	dip = ctx;
216 	instance = ddi_get_instance(dip);
217 	cpudsp = ddi_get_soft_state(cpudrv_state, instance);
218 	if (cpudsp == NULL)
219 		return;
220 	cp = cpudsp->cp;
221 	mach_state = (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state;
222 	if (mach_state == NULL)
223 		return;
224 
225 	/*
226 	 * We only handle _PPC change notifications.
227 	 */
228 	if (!PM_EVENT_CPUPM && val == CPUPM_PPC_CHANGE_NOTIFICATION &&
229 	    mach_state->ms_caps & CPUPM_P_STATES)
230 		cpudrv_redefine_topspeed(ctx);
231 }
232 
233 void
234 cpudrv_install_notify_handler(cpudrv_devstate_t *cpudsp)
235 {
236 	cpu_t *cp = cpudsp->cp;
237 	cpupm_add_notify_handler(cp, cpudrv_notify_handler,
238 	    cpudsp->dip);
239 }
240 
241 void
242 cpudrv_redefine_topspeed(void *ctx)
243 {
244 	/*
245 	 * This should never happen, unless ppm does not get loaded.
246 	 */
247 	if (cpupm_redefine_topspeed == NULL) {
248 		cmn_err(CE_WARN, "cpudrv_redefine_topspeed: "
249 		    "cpupm_redefine_topspeed has not been initialized - "
250 		    "ignoring notification");
251 		return;
252 	}
253 
254 	/*
255 	 * ppm callback needs to handle redefinition for all CPUs in
256 	 * the domain.
257 	 */
258 	(*cpupm_redefine_topspeed)(ctx);
259 }
260 
261 boolean_t
262 cpudrv_mach_init(cpudrv_devstate_t *cpudsp)
263 {
264 	cpupm_mach_state_t *mach_state;
265 
266 	mutex_enter(&cpu_lock);
267 	cpudsp->cp = cpu_get(cpudsp->cpu_id);
268 	mutex_exit(&cpu_lock);
269 	if (cpudsp->cp == NULL) {
270 		cmn_err(CE_WARN, "cpudrv_mach_pm_init: instance %d: "
271 		    "can't get cpu_t", ddi_get_instance(cpudsp->dip));
272 		return (B_FALSE);
273 	}
274 
275 	mach_state = (cpupm_mach_state_t *)
276 	    (cpudsp->cp->cpu_m.mcpu_pm_mach_state);
277 	mach_state->ms_dip = cpudsp->dip;
278 	return (B_TRUE);
279 }
280 
281 uint_t
282 cpudrv_get_speeds(cpudrv_devstate_t *cpudsp, int **speeds)
283 {
284 	return (cpupm_get_speeds(cpudsp->cp, speeds));
285 }
286 
287 void
288 cpudrv_free_speeds(int *speeds, uint_t nspeeds)
289 {
290 	cpupm_free_speeds(speeds, nspeeds);
291 }
292 
293 boolean_t
294 cpudrv_power_ready(void)
295 {
296 	return (cpupm_power_ready());
297 }
298 
299 /* ARGSUSED */
300 void
301 cpudrv_set_supp_freqs(cpudrv_devstate_t *cpudsp)
302 {
303 }
304