xref: /titanic_52/usr/src/uts/i86pc/io/ppm_plat.c (revision ef56a3c55098f8a52f056c7aa6ab084bfebef4e7)
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  * Platform Power Management master pseudo driver platform support.
28  */
29 
30 #include <sys/ddi.h>
31 #include <sys/sunddi.h>
32 #include <sys/ppmvar.h>
33 #include <sys/cpupm.h>
34 
35 static struct ppm_domit *
36 ppm_get_domit_by_model(int model)
37 {
38 	struct ppm_domit *domit_p;
39 	for (domit_p = ppm_domit_data; (domit_p->name &&
40 	    (domit_p->model != model));	domit_p++)
41 		;
42 	ASSERT(domit_p);
43 	return (domit_p);
44 }
45 
46 void
47 ppm_rebuild_cpu_domains(void)
48 {
49 	char *str = "ppm_rebuild_cpu_domains";
50 	cpupm_state_domains_t *dep;
51 	cpupm_state_domains_t *dep_next;
52 	struct ppm_domit *domit_p;
53 	ppm_domain_t *domp_old;
54 	ppm_domain_t *domp;
55 	ppm_dev_t *devp;
56 	ppm_db_t *dbp;
57 	uint_t cpu_id;
58 	cpuset_t dom_cpu_set;
59 	int result;
60 	dev_info_t *cpu_dip;
61 
62 	/*
63 	 * Get the CPU domain data
64 	 */
65 	domit_p = ppm_get_domit_by_model(PPMD_CPU);
66 
67 	/*
68 	 * Find the CPU domain created from ppm.conf. It's only a
69 	 * temporary domain used to make sure that all CPUs are
70 	 * claimed. There should only be one such domain defined.
71 	 */
72 	for (domp = ppm_domain_p; (domp && (domp->model != PPMD_CPU));
73 	    domp = domp->next)
74 		;
75 	if (domp == NULL) {
76 		cmn_err(CE_WARN, "%s: ppm.conf does not define a CPU domain!",
77 		    str);
78 		return;
79 	}
80 	domp_old = domp;
81 	for (domp = domp->next; domp; domp = domp->next) {
82 		if (domp->model == PPMD_CPU) {
83 			cmn_err(CE_WARN, "%s: Multiple CPU domains defined "
84 			    "in ppm.conf!", str);
85 			return;
86 		}
87 	}
88 
89 	/*
90 	 * It is quite possible that the platform does not contain any
91 	 * power manageable CPUs. If so, devlist will be NULL.
92 	 */
93 	if (domp_old->devlist == NULL) {
94 		PPMD(D_CPU, ("%s: No CPUs claimed by ppm!\n", str));
95 		return;
96 	}
97 
98 	/*
99 	 * Get the CPU dependencies as determined by the CPU driver. If
100 	 * the CPU driver didn't create a valid set of dependencies, then
101 	 * leave the domain as it is (which is unmanageable since
102 	 * PPM_CPU_READY is off).
103 	 */
104 	dep = cpupm_pstate_domains;
105 	if (dep == NULL) {
106 		PPMD(D_CPU, ("%s: No CPU dependency info!\n", str));
107 		return;
108 	}
109 
110 	/*
111 	 * Build real CPU domains. OFFLINE the old one as we don't
112 	 * want it to be used when we're done.
113 	 */
114 	mutex_enter(&domp_old->lock);
115 	domp_old->dflags |= PPMD_OFFLINE;
116 	for (dep_next = dep; dep_next; dep_next = dep_next->pm_next) {
117 		domp = kmem_zalloc(sizeof (*domp), KM_SLEEP);
118 		domp->name =  kmem_zalloc(MAXNAMELEN, KM_SLEEP);
119 		(void) snprintf(domp->name, MAXNAMELEN, "acpi_cpu_domain_%d",
120 		    dep_next->pm_domain);
121 		mutex_init(&domp->lock, NULL, MUTEX_DRIVER, NULL);
122 		mutex_enter(&domp->lock);
123 		domp->dflags = domit_p->dflags | PPMD_CPU_READY;
124 		domp->pwr_cnt = 0;
125 		domp->propname = domp_old->propname;
126 		domp->model = domit_p->model;
127 		domp->status = domit_p->status;
128 
129 		/*
130 		 * Add devices to new domain. As a precaution,
131 		 * make sure that the device is currently owned by the
132 		 * ppm.conf defined CPU domain. Adding the device to the
133 		 * domain will result in the domain's "devlist" and "owned"
134 		 * lists being properly formed. It will also update the
135 		 * dip pointer to the device structure. We have to manually
136 		 * build the "conflist" for the domain. But conveniently, the
137 		 * "conflist" data is easily obtainable from the "devlist".
138 		 */
139 		dom_cpu_set = dep_next->pm_cpus;
140 		do {
141 			CPUSET_FIND(dom_cpu_set, cpu_id);
142 			if (cpu_id == CPUSET_NOTINSET)
143 				break;
144 
145 			ASSERT(cpu_id < NCPU);
146 			cpu_dip = ((cpupm_mach_state_t *)
147 			    (cpu[cpu_id]->cpu_m.mcpu_pm_mach_state))->ms_dip;
148 			devp = PPM_GET_PRIVATE(cpu_dip);
149 			ASSERT(devp && devp->domp == domp_old);
150 			devp = ppm_add_dev(cpu_dip, domp);
151 			dbp = kmem_zalloc(sizeof (struct ppm_db), KM_SLEEP);
152 			dbp->name = kmem_zalloc((strlen(devp->path) + 1),
153 			    KM_SLEEP);
154 			(void) strcpy(dbp->name, devp->path);
155 			dbp->next = domp->conflist;
156 			domp->conflist = dbp;
157 
158 			CPUSET_ATOMIC_XDEL(dom_cpu_set, cpu_id, result);
159 		} while (result == 0);
160 
161 		/*
162 		 * Note that we do not bother creating a "dc" list as there
163 		 * isn't one for x86 CPU power management. If this changes
164 		 * in the future some more work will need to be done to
165 		 * support it.
166 		 */
167 		ASSERT(domp_old->dc == NULL);
168 
169 		/*
170 		 * Add the domain to the live list.
171 		 */
172 		domp->next = ppm_domain_p;
173 		ppm_domain_p = domp;
174 
175 		mutex_exit(&domp->lock);
176 	}
177 	mutex_exit(&domp_old->lock);
178 }
179 
180 /*
181  * Used by ppm_redefine_topspeed() to set the highest power level of all CPUs
182  * in a domain.
183  */
184 void
185 ppm_set_topspeed(ppm_dev_t *cpup, int speed)
186 {
187 	for (cpup = cpup->domp->devlist; cpup != NULL; cpup = cpup->next)
188 		(*cpupm_set_topspeed_callb)(cpup->dip, speed);
189 }
190 
191 /*
192  * Redefine the highest power level for all CPUs in a domain. This
193  * functionality is necessary because ACPI uses the _PPC to define
194  * a CPU's highest power level *and* allows the _PPC to be redefined
195  * dynamically. _PPC changes are communicated through _PPC change
196  * notifications caught by the CPU device driver.
197  */
198 void
199 ppm_redefine_topspeed(void *ctx)
200 {
201 	char *str = "ppm_redefine_topspeed";
202 	ppm_dev_t *cpup;
203 	ppm_dev_t *ncpup;
204 	int topspeed;
205 	int newspeed = -1;
206 
207 	cpup = PPM_GET_PRIVATE((dev_info_t *)ctx);
208 
209 	if (cpupm_get_topspeed_callb == NULL ||
210 	    cpupm_set_topspeed_callb == NULL) {
211 		cmn_err(CE_WARN, "%s: Cannot process request for instance %d "
212 		    "since cpupm interfaces are not initialized", str,
213 		    ddi_get_instance(cpup->dip));
214 		return;
215 	}
216 
217 	if (!(cpup->domp->dflags & PPMD_CPU_READY)) {
218 		PPMD(D_CPU, ("%s: instance %d received _PPC change "
219 		    "notification before PPMD_CPU_READY", str,
220 		    ddi_get_instance(cpup->dip)));
221 		return;
222 	}
223 
224 	/*
225 	 * Process each CPU in the domain.
226 	 */
227 	for (ncpup = cpup->domp->devlist; ncpup != NULL; ncpup = ncpup->next) {
228 		topspeed = (*cpupm_get_topspeed_callb)(ncpup->dip);
229 		if (newspeed == -1 || topspeed < newspeed)
230 			newspeed = topspeed;
231 	}
232 
233 	ppm_set_topspeed(cpup, newspeed);
234 }
235 
236 /*
237  * Traverses all domains looking for CPU domains and for each CPU domain
238  * redefines the topspeed for that domain. The reason that this is necessary
239  * is that on x86 platforms ACPI allows the highest power level to be
240  * redefined dynamically. Once all CPU devices have been started it we
241  * need to go back and reinitialize the topspeeds (just in case it's changed).
242  */
243 void
244 ppm_init_topspeed(void)
245 {
246 	ppm_domain_t *domp;
247 	for (domp = ppm_domain_p; domp;	domp = domp->next) {
248 		if (domp->model != PPMD_CPU || !PPM_DOMAIN_UP(domp))
249 			continue;
250 		if (domp->devlist == NULL)
251 			continue;
252 		ppm_redefine_topspeed(domp->devlist->dip);
253 	}
254 }
255 
256 /*
257  * For x86 platforms CPU domains must be built dynamically at bootime.
258  * Until the domains have been built, refuse all power transition
259  * requests.
260  */
261 /* ARGSUSED */
262 boolean_t
263 ppm_manage_early_cpus(dev_info_t *dip, int new, int *result)
264 {
265 	ppm_dev_t *ppmd = PPM_GET_PRIVATE(dip);
266 
267 	if (!(ppmd->domp->dflags & PPMD_CPU_READY)) {
268 		PPMD(D_CPU, ("ppm_manage_early_cpus: attempt to manage CPU "
269 		    "before it was ready dip(0x%p)", (void *)dip));
270 		return (B_TRUE);
271 	}
272 	*result = DDI_FAILURE;
273 	return (B_FALSE);
274 }
275 
276 int
277 ppm_change_cpu_power(ppm_dev_t *ppmd, int newlevel)
278 {
279 #ifdef DEBUG
280 	char *str = "ppm_change_cpu_power";
281 #endif
282 	ppm_unit_t *unitp;
283 	ppm_domain_t *domp;
284 	ppm_dev_t *cpup;
285 	dev_info_t *dip;
286 	int oldlevel;
287 	int ret;
288 
289 	unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
290 	ASSERT(unitp);
291 	domp = ppmd->domp;
292 	cpup = domp->devlist;
293 
294 	dip = cpup->dip;
295 	ASSERT(dip);
296 
297 	oldlevel = cpup->level;
298 
299 	PPMD(D_CPU, ("%s: old %d, new %d\n", str, oldlevel, newlevel))
300 
301 	if (newlevel == oldlevel)
302 		return (DDI_SUCCESS);
303 
304 	/* bring each cpu to next level */
305 	for (; cpup; cpup = cpup->next) {
306 		ret = pm_power(cpup->dip, 0, newlevel);
307 		PPMD(D_CPU, ("%s: \"%s\", changed to level %d, ret %d\n",
308 		    str, cpup->path, newlevel, ret))
309 		if (ret == DDI_SUCCESS) {
310 			cpup->level = newlevel;
311 			cpup->rplvl = PM_LEVEL_UNKNOWN;
312 			continue;
313 		}
314 
315 		/*
316 		 * If the driver was unable to lower cpu speed,
317 		 * the cpu probably got busy; set the previous
318 		 * cpus back to the original level
319 		 */
320 		if (newlevel < oldlevel)
321 			ret = ppm_revert_cpu_power(cpup, oldlevel);
322 
323 		return (ret);
324 	}
325 
326 	return (DDI_SUCCESS);
327 }
328