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