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