xref: /onnv-gate/usr/src/uts/i86pc/io/ppm_plat.c (revision 10488:296c315b92df)
14667Smh27603 /*
24667Smh27603  * CDDL HEADER START
34667Smh27603  *
44667Smh27603  * The contents of this file are subject to the terms of the
54667Smh27603  * Common Development and Distribution License (the "License").
64667Smh27603  * You may not use this file except in compliance with the License.
74667Smh27603  *
84667Smh27603  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94667Smh27603  * or http://www.opensolaris.org/os/licensing.
104667Smh27603  * See the License for the specific language governing permissions
114667Smh27603  * and limitations under the License.
124667Smh27603  *
134667Smh27603  * When distributing Covered Code, include this CDDL HEADER in each
144667Smh27603  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154667Smh27603  * If applicable, add the following below this CDDL HEADER, with the
164667Smh27603  * fields enclosed by brackets "[]" replaced with your own identifying
174667Smh27603  * information: Portions Copyright [yyyy] [name of copyright owner]
184667Smh27603  *
194667Smh27603  * CDDL HEADER END
204667Smh27603  */
214667Smh27603 /*
228906SEric.Saxe@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
234667Smh27603  * Use is subject to license terms.
244667Smh27603  */
25*10488SMark.Haywood@Sun.COM /*
26*10488SMark.Haywood@Sun.COM  * Copyright (c) 2009, Intel Corporation.
27*10488SMark.Haywood@Sun.COM  * All rights reserved.
28*10488SMark.Haywood@Sun.COM  */
294667Smh27603 
304667Smh27603 /*
314667Smh27603  * Platform Power Management master pseudo driver platform support.
324667Smh27603  */
334667Smh27603 
344667Smh27603 #include <sys/ddi.h>
354667Smh27603 #include <sys/sunddi.h>
364667Smh27603 #include <sys/ppmvar.h>
374667Smh27603 #include <sys/cpupm.h>
384667Smh27603 
39*10488SMark.Haywood@Sun.COM #define	PPM_CPU_PSTATE_DOMAIN_FLG	0x100
404667Smh27603 
414667Smh27603 /*
424667Smh27603  * Used by ppm_redefine_topspeed() to set the highest power level of all CPUs
434667Smh27603  * in a domain.
444667Smh27603  */
454667Smh27603 void
ppm_set_topspeed(ppm_dev_t * cpup,int speed)464667Smh27603 ppm_set_topspeed(ppm_dev_t *cpup, int speed)
474667Smh27603 {
484667Smh27603 	for (cpup = cpup->domp->devlist; cpup != NULL; cpup = cpup->next)
498906SEric.Saxe@Sun.COM 		(*cpupm_set_topspeed_callb)(cpup->dip, speed);
504667Smh27603 }
514667Smh27603 
524667Smh27603 /*
534667Smh27603  * Redefine the highest power level for all CPUs in a domain. This
544667Smh27603  * functionality is necessary because ACPI uses the _PPC to define
554667Smh27603  * a CPU's highest power level *and* allows the _PPC to be redefined
564667Smh27603  * dynamically. _PPC changes are communicated through _PPC change
574667Smh27603  * notifications caught by the CPU device driver.
584667Smh27603  */
594667Smh27603 void
ppm_redefine_topspeed(void * ctx)604667Smh27603 ppm_redefine_topspeed(void *ctx)
614667Smh27603 {
624667Smh27603 	char *str = "ppm_redefine_topspeed";
634667Smh27603 	ppm_dev_t *cpup;
644667Smh27603 	ppm_dev_t *ncpup;
654667Smh27603 	int topspeed;
664667Smh27603 	int newspeed = -1;
674667Smh27603 
684667Smh27603 	cpup = PPM_GET_PRIVATE((dev_info_t *)ctx);
694667Smh27603 
708906SEric.Saxe@Sun.COM 	if (cpupm_get_topspeed_callb == NULL ||
718906SEric.Saxe@Sun.COM 	    cpupm_set_topspeed_callb == NULL) {
724667Smh27603 		cmn_err(CE_WARN, "%s: Cannot process request for instance %d "
734667Smh27603 		    "since cpupm interfaces are not initialized", str,
744667Smh27603 		    ddi_get_instance(cpup->dip));
754667Smh27603 		return;
764667Smh27603 	}
774667Smh27603 
784667Smh27603 	if (!(cpup->domp->dflags & PPMD_CPU_READY)) {
794667Smh27603 		PPMD(D_CPU, ("%s: instance %d received _PPC change "
804667Smh27603 		    "notification before PPMD_CPU_READY", str,
814667Smh27603 		    ddi_get_instance(cpup->dip)));
824667Smh27603 		return;
834667Smh27603 	}
844667Smh27603 
854667Smh27603 	/*
864667Smh27603 	 * Process each CPU in the domain.
874667Smh27603 	 */
884667Smh27603 	for (ncpup = cpup->domp->devlist; ncpup != NULL; ncpup = ncpup->next) {
898906SEric.Saxe@Sun.COM 		topspeed = (*cpupm_get_topspeed_callb)(ncpup->dip);
904667Smh27603 		if (newspeed == -1 || topspeed < newspeed)
914667Smh27603 			newspeed = topspeed;
924667Smh27603 	}
934667Smh27603 
944667Smh27603 	ppm_set_topspeed(cpup, newspeed);
954667Smh27603 }
964667Smh27603 
974667Smh27603 /*
984667Smh27603  * For x86 platforms CPU domains must be built dynamically at bootime.
994667Smh27603  * Until the domains have been built, refuse all power transition
1004667Smh27603  * requests.
1014667Smh27603  */
1024667Smh27603 /* ARGSUSED */
1034667Smh27603 boolean_t
ppm_manage_early_cpus(dev_info_t * dip,int new,int * result)1044667Smh27603 ppm_manage_early_cpus(dev_info_t *dip, int new, int *result)
1054667Smh27603 {
1064667Smh27603 	ppm_dev_t *ppmd = PPM_GET_PRIVATE(dip);
1074667Smh27603 
1084667Smh27603 	if (!(ppmd->domp->dflags & PPMD_CPU_READY)) {
1094667Smh27603 		PPMD(D_CPU, ("ppm_manage_early_cpus: attempt to manage CPU "
1104667Smh27603 		    "before it was ready dip(0x%p)", (void *)dip));
1114667Smh27603 		return (B_TRUE);
1124667Smh27603 	}
1134667Smh27603 	*result = DDI_FAILURE;
1144667Smh27603 	return (B_FALSE);
1154667Smh27603 }
1164667Smh27603 
1174667Smh27603 int
ppm_change_cpu_power(ppm_dev_t * ppmd,int newlevel)1184667Smh27603 ppm_change_cpu_power(ppm_dev_t *ppmd, int newlevel)
1194667Smh27603 {
1204667Smh27603 #ifdef DEBUG
1214667Smh27603 	char *str = "ppm_change_cpu_power";
1224667Smh27603 #endif
1234667Smh27603 	ppm_unit_t *unitp;
1244667Smh27603 	ppm_domain_t *domp;
1254667Smh27603 	ppm_dev_t *cpup;
1264667Smh27603 	dev_info_t *dip;
1274667Smh27603 	int oldlevel;
1284667Smh27603 	int ret;
1294667Smh27603 
1304667Smh27603 	unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
1314667Smh27603 	ASSERT(unitp);
1324667Smh27603 	domp = ppmd->domp;
1334667Smh27603 	cpup = domp->devlist;
1344667Smh27603 
1354667Smh27603 	dip = cpup->dip;
1364667Smh27603 	ASSERT(dip);
1374667Smh27603 
1384667Smh27603 	oldlevel = cpup->level;
1394667Smh27603 
1404667Smh27603 	PPMD(D_CPU, ("%s: old %d, new %d\n", str, oldlevel, newlevel))
1414667Smh27603 
1424667Smh27603 	if (newlevel == oldlevel)
1434667Smh27603 		return (DDI_SUCCESS);
1444667Smh27603 
1454667Smh27603 	/* bring each cpu to next level */
1464667Smh27603 	for (; cpup; cpup = cpup->next) {
1474667Smh27603 		ret = pm_power(cpup->dip, 0, newlevel);
1484667Smh27603 		PPMD(D_CPU, ("%s: \"%s\", changed to level %d, ret %d\n",
1494667Smh27603 		    str, cpup->path, newlevel, ret))
1504667Smh27603 		if (ret == DDI_SUCCESS) {
1514667Smh27603 			cpup->level = newlevel;
1524667Smh27603 			cpup->rplvl = PM_LEVEL_UNKNOWN;
1534667Smh27603 			continue;
1544667Smh27603 		}
1554667Smh27603 
1564667Smh27603 		/*
1574667Smh27603 		 * If the driver was unable to lower cpu speed,
1585711Smh27603 		 * the cpu probably got busy; set the previous
1595711Smh27603 		 * cpus back to the original level
1604667Smh27603 		 */
1615711Smh27603 		if (newlevel < oldlevel)
1624667Smh27603 			ret = ppm_revert_cpu_power(cpup, oldlevel);
1635711Smh27603 
1644667Smh27603 		return (ret);
1654667Smh27603 	}
1664667Smh27603 
1674667Smh27603 	return (DDI_SUCCESS);
1684667Smh27603 }
169*10488SMark.Haywood@Sun.COM 
170*10488SMark.Haywood@Sun.COM /*
171*10488SMark.Haywood@Sun.COM  * allocate ppm CPU pstate domain if non-existence,
172*10488SMark.Haywood@Sun.COM  * otherwise, add the CPU to the corresponding ppm
173*10488SMark.Haywood@Sun.COM  * CPU pstate domain.
174*10488SMark.Haywood@Sun.COM  */
175*10488SMark.Haywood@Sun.COM void
ppm_alloc_pstate_domains(cpu_t * cp)176*10488SMark.Haywood@Sun.COM ppm_alloc_pstate_domains(cpu_t *cp)
177*10488SMark.Haywood@Sun.COM {
178*10488SMark.Haywood@Sun.COM 	cpupm_mach_state_t	*mach_state;
179*10488SMark.Haywood@Sun.COM 	uint32_t		pm_domain;
180*10488SMark.Haywood@Sun.COM 	int			sub_domain;
181*10488SMark.Haywood@Sun.COM 	ppm_domain_t		*domp;
182*10488SMark.Haywood@Sun.COM 	dev_info_t		*cpu_dip;
183*10488SMark.Haywood@Sun.COM 	ppm_db_t		*dbp;
184*10488SMark.Haywood@Sun.COM 	char			path[MAXNAMELEN];
185*10488SMark.Haywood@Sun.COM 
186*10488SMark.Haywood@Sun.COM 	mach_state = (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state);
187*10488SMark.Haywood@Sun.COM 	ASSERT(mach_state);
188*10488SMark.Haywood@Sun.COM 	pm_domain = mach_state->ms_pstate.cma_domain->pm_domain;
189*10488SMark.Haywood@Sun.COM 
190*10488SMark.Haywood@Sun.COM 	/*
191*10488SMark.Haywood@Sun.COM 	 * There are two purposes of sub_domain:
192*10488SMark.Haywood@Sun.COM 	 * 1. skip the orignal ppm CPU domain generated by ppm.conf
193*10488SMark.Haywood@Sun.COM 	 * 2. A CPU ppm domain could have several pstate domains indeed.
194*10488SMark.Haywood@Sun.COM 	 */
195*10488SMark.Haywood@Sun.COM 	sub_domain = pm_domain | PPM_CPU_PSTATE_DOMAIN_FLG;
196*10488SMark.Haywood@Sun.COM 
197*10488SMark.Haywood@Sun.COM 	/*
198*10488SMark.Haywood@Sun.COM 	 * Find ppm CPU pstate domain
199*10488SMark.Haywood@Sun.COM 	 */
200*10488SMark.Haywood@Sun.COM 	for (domp = ppm_domain_p; domp; domp = domp->next) {
201*10488SMark.Haywood@Sun.COM 		if ((domp->model == PPMD_CPU) &&
202*10488SMark.Haywood@Sun.COM 		    (domp->sub_domain == sub_domain)) {
203*10488SMark.Haywood@Sun.COM 			break;
204*10488SMark.Haywood@Sun.COM 		}
205*10488SMark.Haywood@Sun.COM 	}
206*10488SMark.Haywood@Sun.COM 
207*10488SMark.Haywood@Sun.COM 	/*
208*10488SMark.Haywood@Sun.COM 	 * Create one ppm CPU pstate domain if no found
209*10488SMark.Haywood@Sun.COM 	 */
210*10488SMark.Haywood@Sun.COM 	if (domp == NULL) {
211*10488SMark.Haywood@Sun.COM 		domp = kmem_zalloc(sizeof (*domp), KM_SLEEP);
212*10488SMark.Haywood@Sun.COM 		mutex_init(&domp->lock, NULL, MUTEX_DRIVER, NULL);
213*10488SMark.Haywood@Sun.COM 		mutex_enter(&domp->lock);
214*10488SMark.Haywood@Sun.COM 		domp->name = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
215*10488SMark.Haywood@Sun.COM 		(void) snprintf(domp->name, MAXNAMELEN, "cpu_pstate_domain_%d",
216*10488SMark.Haywood@Sun.COM 		    pm_domain);
217*10488SMark.Haywood@Sun.COM 		domp->sub_domain = sub_domain;
218*10488SMark.Haywood@Sun.COM 		domp->dflags = PPMD_LOCK_ALL | PPMD_CPU_READY;
219*10488SMark.Haywood@Sun.COM 		domp->pwr_cnt = 0;
220*10488SMark.Haywood@Sun.COM 		domp->pwr_cnt++;
221*10488SMark.Haywood@Sun.COM 		domp->propname = NULL;
222*10488SMark.Haywood@Sun.COM 		domp->model = PPMD_CPU;
223*10488SMark.Haywood@Sun.COM 		domp->status = PPMD_ON;
224*10488SMark.Haywood@Sun.COM 		cpu_dip = mach_state->ms_dip;
225*10488SMark.Haywood@Sun.COM 		(void) ddi_pathname(cpu_dip, path);
226*10488SMark.Haywood@Sun.COM 		dbp = kmem_zalloc(sizeof (struct ppm_db), KM_SLEEP);
227*10488SMark.Haywood@Sun.COM 		dbp->name = kmem_zalloc((strlen(path) + 1),
228*10488SMark.Haywood@Sun.COM 		    KM_SLEEP);
229*10488SMark.Haywood@Sun.COM 		(void) strcpy(dbp->name, path);
230*10488SMark.Haywood@Sun.COM 		dbp->next = domp->conflist;
231*10488SMark.Haywood@Sun.COM 		domp->conflist = dbp;
232*10488SMark.Haywood@Sun.COM 		domp->next = ppm_domain_p;
233*10488SMark.Haywood@Sun.COM 		ppm_domain_p = domp;
234*10488SMark.Haywood@Sun.COM 		mutex_exit(&domp->lock);
235*10488SMark.Haywood@Sun.COM 	}
236*10488SMark.Haywood@Sun.COM 	/*
237*10488SMark.Haywood@Sun.COM 	 * We found one matched ppm CPU pstate domain,
238*10488SMark.Haywood@Sun.COM 	 * add cpu to this domain
239*10488SMark.Haywood@Sun.COM 	 */
240*10488SMark.Haywood@Sun.COM 	else {
241*10488SMark.Haywood@Sun.COM 		mutex_enter(&domp->lock);
242*10488SMark.Haywood@Sun.COM 		cpu_dip = mach_state->ms_dip;
243*10488SMark.Haywood@Sun.COM 		(void) ddi_pathname(cpu_dip, path);
244*10488SMark.Haywood@Sun.COM 		dbp = kmem_zalloc(sizeof (struct ppm_db), KM_SLEEP);
245*10488SMark.Haywood@Sun.COM 		dbp->name = kmem_zalloc((strlen(path) + 1),
246*10488SMark.Haywood@Sun.COM 		    KM_SLEEP);
247*10488SMark.Haywood@Sun.COM 		(void) strcpy(dbp->name, path);
248*10488SMark.Haywood@Sun.COM 		dbp->next = domp->conflist;
249*10488SMark.Haywood@Sun.COM 		domp->conflist = dbp;
250*10488SMark.Haywood@Sun.COM 		domp->pwr_cnt++;
251*10488SMark.Haywood@Sun.COM 		mutex_exit(&domp->lock);
252*10488SMark.Haywood@Sun.COM 	}
253*10488SMark.Haywood@Sun.COM }
254*10488SMark.Haywood@Sun.COM 
255*10488SMark.Haywood@Sun.COM /*
256*10488SMark.Haywood@Sun.COM  * remove CPU from the corresponding ppm CPU pstate
257*10488SMark.Haywood@Sun.COM  * domain. We only remove CPU from conflist here.
258*10488SMark.Haywood@Sun.COM  */
259*10488SMark.Haywood@Sun.COM void
ppm_free_pstate_domains(cpu_t * cp)260*10488SMark.Haywood@Sun.COM ppm_free_pstate_domains(cpu_t *cp)
261*10488SMark.Haywood@Sun.COM {
262*10488SMark.Haywood@Sun.COM 	cpupm_mach_state_t	*mach_state;
263*10488SMark.Haywood@Sun.COM 	ppm_domain_t		*domp;
264*10488SMark.Haywood@Sun.COM 	ppm_dev_t		*devp;
265*10488SMark.Haywood@Sun.COM 	dev_info_t		*cpu_dip;
266*10488SMark.Haywood@Sun.COM 	ppm_db_t		**dbpp, *pconf;
267*10488SMark.Haywood@Sun.COM 	char			path[MAXNAMELEN];
268*10488SMark.Haywood@Sun.COM 
269*10488SMark.Haywood@Sun.COM 	mach_state = (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state);
270*10488SMark.Haywood@Sun.COM 	ASSERT(mach_state);
271*10488SMark.Haywood@Sun.COM 	cpu_dip = mach_state->ms_dip;
272*10488SMark.Haywood@Sun.COM 	(void) ddi_pathname(cpu_dip, path);
273*10488SMark.Haywood@Sun.COM 
274*10488SMark.Haywood@Sun.COM 	/*
275*10488SMark.Haywood@Sun.COM 	 * get ppm CPU pstate domain
276*10488SMark.Haywood@Sun.COM 	 */
277*10488SMark.Haywood@Sun.COM 	devp = PPM_GET_PRIVATE(cpu_dip);
278*10488SMark.Haywood@Sun.COM 	ASSERT(devp);
279*10488SMark.Haywood@Sun.COM 	domp = devp->domp;
280*10488SMark.Haywood@Sun.COM 	ASSERT(domp);
281*10488SMark.Haywood@Sun.COM 
282*10488SMark.Haywood@Sun.COM 	/*
283*10488SMark.Haywood@Sun.COM 	 * remove CPU from conflist
284*10488SMark.Haywood@Sun.COM 	 */
285*10488SMark.Haywood@Sun.COM 	mutex_enter(&domp->lock);
286*10488SMark.Haywood@Sun.COM 	for (dbpp = &domp->conflist; (pconf = *dbpp) != NULL; ) {
287*10488SMark.Haywood@Sun.COM 		if (strcmp(pconf->name, path) != 0) {
288*10488SMark.Haywood@Sun.COM 			dbpp = &pconf->next;
289*10488SMark.Haywood@Sun.COM 			continue;
290*10488SMark.Haywood@Sun.COM 		}
291*10488SMark.Haywood@Sun.COM 		*dbpp = pconf->next;
292*10488SMark.Haywood@Sun.COM 		kmem_free(pconf->name, strlen(pconf->name) + 1);
293*10488SMark.Haywood@Sun.COM 		kmem_free(pconf, sizeof (*pconf));
294*10488SMark.Haywood@Sun.COM 	}
295*10488SMark.Haywood@Sun.COM 	mutex_exit(&domp->lock);
296*10488SMark.Haywood@Sun.COM }
297