xref: /illumos-gate/usr/src/uts/i86pc/io/ppm_plat.c (revision 444f66e7)
15cff7825Smh /*
25cff7825Smh  * CDDL HEADER START
35cff7825Smh  *
45cff7825Smh  * The contents of this file are subject to the terms of the
55cff7825Smh  * Common Development and Distribution License (the "License").
65cff7825Smh  * You may not use this file except in compliance with the License.
75cff7825Smh  *
85cff7825Smh  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95cff7825Smh  * or http://www.opensolaris.org/os/licensing.
105cff7825Smh  * See the License for the specific language governing permissions
115cff7825Smh  * and limitations under the License.
125cff7825Smh  *
135cff7825Smh  * When distributing Covered Code, include this CDDL HEADER in each
145cff7825Smh  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155cff7825Smh  * If applicable, add the following below this CDDL HEADER, with the
165cff7825Smh  * fields enclosed by brackets "[]" replaced with your own identifying
175cff7825Smh  * information: Portions Copyright [yyyy] [name of copyright owner]
185cff7825Smh  *
195cff7825Smh  * CDDL HEADER END
205cff7825Smh  */
215cff7825Smh /*
220e751525SEric Saxe  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
235cff7825Smh  * Use is subject to license terms.
245cff7825Smh  */
25*444f66e7SMark Haywood /*
26*444f66e7SMark Haywood  * Copyright (c) 2009, Intel Corporation.
27*444f66e7SMark Haywood  * All rights reserved.
28*444f66e7SMark Haywood  */
295cff7825Smh 
305cff7825Smh /*
315cff7825Smh  * Platform Power Management master pseudo driver platform support.
325cff7825Smh  */
335cff7825Smh 
345cff7825Smh #include <sys/ddi.h>
355cff7825Smh #include <sys/sunddi.h>
365cff7825Smh #include <sys/ppmvar.h>
375cff7825Smh #include <sys/cpupm.h>
385cff7825Smh 
39*444f66e7SMark Haywood #define	PPM_CPU_PSTATE_DOMAIN_FLG	0x100
405cff7825Smh 
415cff7825Smh /*
425cff7825Smh  * Used by ppm_redefine_topspeed() to set the highest power level of all CPUs
435cff7825Smh  * in a domain.
445cff7825Smh  */
455cff7825Smh void
ppm_set_topspeed(ppm_dev_t * cpup,int speed)465cff7825Smh ppm_set_topspeed(ppm_dev_t *cpup, int speed)
475cff7825Smh {
485cff7825Smh 	for (cpup = cpup->domp->devlist; cpup != NULL; cpup = cpup->next)
490e751525SEric Saxe 		(*cpupm_set_topspeed_callb)(cpup->dip, speed);
505cff7825Smh }
515cff7825Smh 
525cff7825Smh /*
535cff7825Smh  * Redefine the highest power level for all CPUs in a domain. This
545cff7825Smh  * functionality is necessary because ACPI uses the _PPC to define
555cff7825Smh  * a CPU's highest power level *and* allows the _PPC to be redefined
565cff7825Smh  * dynamically. _PPC changes are communicated through _PPC change
575cff7825Smh  * notifications caught by the CPU device driver.
585cff7825Smh  */
595cff7825Smh void
ppm_redefine_topspeed(void * ctx)605cff7825Smh ppm_redefine_topspeed(void *ctx)
615cff7825Smh {
625cff7825Smh 	char *str = "ppm_redefine_topspeed";
635cff7825Smh 	ppm_dev_t *cpup;
645cff7825Smh 	ppm_dev_t *ncpup;
655cff7825Smh 	int topspeed;
665cff7825Smh 	int newspeed = -1;
675cff7825Smh 
685cff7825Smh 	cpup = PPM_GET_PRIVATE((dev_info_t *)ctx);
695cff7825Smh 
700e751525SEric Saxe 	if (cpupm_get_topspeed_callb == NULL ||
710e751525SEric Saxe 	    cpupm_set_topspeed_callb == NULL) {
725cff7825Smh 		cmn_err(CE_WARN, "%s: Cannot process request for instance %d "
735cff7825Smh 		    "since cpupm interfaces are not initialized", str,
745cff7825Smh 		    ddi_get_instance(cpup->dip));
755cff7825Smh 		return;
765cff7825Smh 	}
775cff7825Smh 
785cff7825Smh 	if (!(cpup->domp->dflags & PPMD_CPU_READY)) {
795cff7825Smh 		PPMD(D_CPU, ("%s: instance %d received _PPC change "
805cff7825Smh 		    "notification before PPMD_CPU_READY", str,
815cff7825Smh 		    ddi_get_instance(cpup->dip)));
825cff7825Smh 		return;
835cff7825Smh 	}
845cff7825Smh 
855cff7825Smh 	/*
865cff7825Smh 	 * Process each CPU in the domain.
875cff7825Smh 	 */
885cff7825Smh 	for (ncpup = cpup->domp->devlist; ncpup != NULL; ncpup = ncpup->next) {
890e751525SEric Saxe 		topspeed = (*cpupm_get_topspeed_callb)(ncpup->dip);
905cff7825Smh 		if (newspeed == -1 || topspeed < newspeed)
915cff7825Smh 			newspeed = topspeed;
925cff7825Smh 	}
935cff7825Smh 
945cff7825Smh 	ppm_set_topspeed(cpup, newspeed);
955cff7825Smh }
965cff7825Smh 
975cff7825Smh /*
985cff7825Smh  * For x86 platforms CPU domains must be built dynamically at bootime.
995cff7825Smh  * Until the domains have been built, refuse all power transition
1005cff7825Smh  * requests.
1015cff7825Smh  */
1025cff7825Smh /* ARGSUSED */
1035cff7825Smh boolean_t
ppm_manage_early_cpus(dev_info_t * dip,int new,int * result)1045cff7825Smh ppm_manage_early_cpus(dev_info_t *dip, int new, int *result)
1055cff7825Smh {
1065cff7825Smh 	ppm_dev_t *ppmd = PPM_GET_PRIVATE(dip);
1075cff7825Smh 
1085cff7825Smh 	if (!(ppmd->domp->dflags & PPMD_CPU_READY)) {
1095cff7825Smh 		PPMD(D_CPU, ("ppm_manage_early_cpus: attempt to manage CPU "
1105cff7825Smh 		    "before it was ready dip(0x%p)", (void *)dip));
1115cff7825Smh 		return (B_TRUE);
1125cff7825Smh 	}
1135cff7825Smh 	*result = DDI_FAILURE;
1145cff7825Smh 	return (B_FALSE);
1155cff7825Smh }
1165cff7825Smh 
1175cff7825Smh int
ppm_change_cpu_power(ppm_dev_t * ppmd,int newlevel)1185cff7825Smh ppm_change_cpu_power(ppm_dev_t *ppmd, int newlevel)
1195cff7825Smh {
1205cff7825Smh #ifdef DEBUG
1215cff7825Smh 	char *str = "ppm_change_cpu_power";
1225cff7825Smh #endif
1235cff7825Smh 	ppm_unit_t *unitp;
1245cff7825Smh 	ppm_domain_t *domp;
1255cff7825Smh 	ppm_dev_t *cpup;
1265cff7825Smh 	dev_info_t *dip;
1275cff7825Smh 	int oldlevel;
1285cff7825Smh 	int ret;
1295cff7825Smh 
1305cff7825Smh 	unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
1315cff7825Smh 	ASSERT(unitp);
1325cff7825Smh 	domp = ppmd->domp;
1335cff7825Smh 	cpup = domp->devlist;
1345cff7825Smh 
1355cff7825Smh 	dip = cpup->dip;
1365cff7825Smh 	ASSERT(dip);
1375cff7825Smh 
1385cff7825Smh 	oldlevel = cpup->level;
1395cff7825Smh 
1405cff7825Smh 	PPMD(D_CPU, ("%s: old %d, new %d\n", str, oldlevel, newlevel))
1415cff7825Smh 
1425cff7825Smh 	if (newlevel == oldlevel)
1435cff7825Smh 		return (DDI_SUCCESS);
1445cff7825Smh 
1455cff7825Smh 	/* bring each cpu to next level */
1465cff7825Smh 	for (; cpup; cpup = cpup->next) {
1475cff7825Smh 		ret = pm_power(cpup->dip, 0, newlevel);
1485cff7825Smh 		PPMD(D_CPU, ("%s: \"%s\", changed to level %d, ret %d\n",
1495cff7825Smh 		    str, cpup->path, newlevel, ret))
1505cff7825Smh 		if (ret == DDI_SUCCESS) {
1515cff7825Smh 			cpup->level = newlevel;
1525cff7825Smh 			cpup->rplvl = PM_LEVEL_UNKNOWN;
1535cff7825Smh 			continue;
1545cff7825Smh 		}
1555cff7825Smh 
1565cff7825Smh 		/*
1575cff7825Smh 		 * If the driver was unable to lower cpu speed,
158b8524a1dSmh 		 * the cpu probably got busy; set the previous
159b8524a1dSmh 		 * cpus back to the original level
1605cff7825Smh 		 */
161b8524a1dSmh 		if (newlevel < oldlevel)
1625cff7825Smh 			ret = ppm_revert_cpu_power(cpup, oldlevel);
163b8524a1dSmh 
1645cff7825Smh 		return (ret);
1655cff7825Smh 	}
1665cff7825Smh 
1675cff7825Smh 	return (DDI_SUCCESS);
1685cff7825Smh }
169*444f66e7SMark Haywood 
170*444f66e7SMark Haywood /*
171*444f66e7SMark Haywood  * allocate ppm CPU pstate domain if non-existence,
172*444f66e7SMark Haywood  * otherwise, add the CPU to the corresponding ppm
173*444f66e7SMark Haywood  * CPU pstate domain.
174*444f66e7SMark Haywood  */
175*444f66e7SMark Haywood void
ppm_alloc_pstate_domains(cpu_t * cp)176*444f66e7SMark Haywood ppm_alloc_pstate_domains(cpu_t *cp)
177*444f66e7SMark Haywood {
178*444f66e7SMark Haywood 	cpupm_mach_state_t	*mach_state;
179*444f66e7SMark Haywood 	uint32_t		pm_domain;
180*444f66e7SMark Haywood 	int			sub_domain;
181*444f66e7SMark Haywood 	ppm_domain_t		*domp;
182*444f66e7SMark Haywood 	dev_info_t		*cpu_dip;
183*444f66e7SMark Haywood 	ppm_db_t		*dbp;
184*444f66e7SMark Haywood 	char			path[MAXNAMELEN];
185*444f66e7SMark Haywood 
186*444f66e7SMark Haywood 	mach_state = (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state);
187*444f66e7SMark Haywood 	ASSERT(mach_state);
188*444f66e7SMark Haywood 	pm_domain = mach_state->ms_pstate.cma_domain->pm_domain;
189*444f66e7SMark Haywood 
190*444f66e7SMark Haywood 	/*
191*444f66e7SMark Haywood 	 * There are two purposes of sub_domain:
192*444f66e7SMark Haywood 	 * 1. skip the orignal ppm CPU domain generated by ppm.conf
193*444f66e7SMark Haywood 	 * 2. A CPU ppm domain could have several pstate domains indeed.
194*444f66e7SMark Haywood 	 */
195*444f66e7SMark Haywood 	sub_domain = pm_domain | PPM_CPU_PSTATE_DOMAIN_FLG;
196*444f66e7SMark Haywood 
197*444f66e7SMark Haywood 	/*
198*444f66e7SMark Haywood 	 * Find ppm CPU pstate domain
199*444f66e7SMark Haywood 	 */
200*444f66e7SMark Haywood 	for (domp = ppm_domain_p; domp; domp = domp->next) {
201*444f66e7SMark Haywood 		if ((domp->model == PPMD_CPU) &&
202*444f66e7SMark Haywood 		    (domp->sub_domain == sub_domain)) {
203*444f66e7SMark Haywood 			break;
204*444f66e7SMark Haywood 		}
205*444f66e7SMark Haywood 	}
206*444f66e7SMark Haywood 
207*444f66e7SMark Haywood 	/*
208*444f66e7SMark Haywood 	 * Create one ppm CPU pstate domain if no found
209*444f66e7SMark Haywood 	 */
210*444f66e7SMark Haywood 	if (domp == NULL) {
211*444f66e7SMark Haywood 		domp = kmem_zalloc(sizeof (*domp), KM_SLEEP);
212*444f66e7SMark Haywood 		mutex_init(&domp->lock, NULL, MUTEX_DRIVER, NULL);
213*444f66e7SMark Haywood 		mutex_enter(&domp->lock);
214*444f66e7SMark Haywood 		domp->name = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
215*444f66e7SMark Haywood 		(void) snprintf(domp->name, MAXNAMELEN, "cpu_pstate_domain_%d",
216*444f66e7SMark Haywood 		    pm_domain);
217*444f66e7SMark Haywood 		domp->sub_domain = sub_domain;
218*444f66e7SMark Haywood 		domp->dflags = PPMD_LOCK_ALL | PPMD_CPU_READY;
219*444f66e7SMark Haywood 		domp->pwr_cnt = 0;
220*444f66e7SMark Haywood 		domp->pwr_cnt++;
221*444f66e7SMark Haywood 		domp->propname = NULL;
222*444f66e7SMark Haywood 		domp->model = PPMD_CPU;
223*444f66e7SMark Haywood 		domp->status = PPMD_ON;
224*444f66e7SMark Haywood 		cpu_dip = mach_state->ms_dip;
225*444f66e7SMark Haywood 		(void) ddi_pathname(cpu_dip, path);
226*444f66e7SMark Haywood 		dbp = kmem_zalloc(sizeof (struct ppm_db), KM_SLEEP);
227*444f66e7SMark Haywood 		dbp->name = kmem_zalloc((strlen(path) + 1),
228*444f66e7SMark Haywood 		    KM_SLEEP);
229*444f66e7SMark Haywood 		(void) strcpy(dbp->name, path);
230*444f66e7SMark Haywood 		dbp->next = domp->conflist;
231*444f66e7SMark Haywood 		domp->conflist = dbp;
232*444f66e7SMark Haywood 		domp->next = ppm_domain_p;
233*444f66e7SMark Haywood 		ppm_domain_p = domp;
234*444f66e7SMark Haywood 		mutex_exit(&domp->lock);
235*444f66e7SMark Haywood 	}
236*444f66e7SMark Haywood 	/*
237*444f66e7SMark Haywood 	 * We found one matched ppm CPU pstate domain,
238*444f66e7SMark Haywood 	 * add cpu to this domain
239*444f66e7SMark Haywood 	 */
240*444f66e7SMark Haywood 	else {
241*444f66e7SMark Haywood 		mutex_enter(&domp->lock);
242*444f66e7SMark Haywood 		cpu_dip = mach_state->ms_dip;
243*444f66e7SMark Haywood 		(void) ddi_pathname(cpu_dip, path);
244*444f66e7SMark Haywood 		dbp = kmem_zalloc(sizeof (struct ppm_db), KM_SLEEP);
245*444f66e7SMark Haywood 		dbp->name = kmem_zalloc((strlen(path) + 1),
246*444f66e7SMark Haywood 		    KM_SLEEP);
247*444f66e7SMark Haywood 		(void) strcpy(dbp->name, path);
248*444f66e7SMark Haywood 		dbp->next = domp->conflist;
249*444f66e7SMark Haywood 		domp->conflist = dbp;
250*444f66e7SMark Haywood 		domp->pwr_cnt++;
251*444f66e7SMark Haywood 		mutex_exit(&domp->lock);
252*444f66e7SMark Haywood 	}
253*444f66e7SMark Haywood }
254*444f66e7SMark Haywood 
255*444f66e7SMark Haywood /*
256*444f66e7SMark Haywood  * remove CPU from the corresponding ppm CPU pstate
257*444f66e7SMark Haywood  * domain. We only remove CPU from conflist here.
258*444f66e7SMark Haywood  */
259*444f66e7SMark Haywood void
ppm_free_pstate_domains(cpu_t * cp)260*444f66e7SMark Haywood ppm_free_pstate_domains(cpu_t *cp)
261*444f66e7SMark Haywood {
262*444f66e7SMark Haywood 	cpupm_mach_state_t	*mach_state;
263*444f66e7SMark Haywood 	ppm_domain_t		*domp;
264*444f66e7SMark Haywood 	ppm_dev_t		*devp;
265*444f66e7SMark Haywood 	dev_info_t		*cpu_dip;
266*444f66e7SMark Haywood 	ppm_db_t		**dbpp, *pconf;
267*444f66e7SMark Haywood 	char			path[MAXNAMELEN];
268*444f66e7SMark Haywood 
269*444f66e7SMark Haywood 	mach_state = (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state);
270*444f66e7SMark Haywood 	ASSERT(mach_state);
271*444f66e7SMark Haywood 	cpu_dip = mach_state->ms_dip;
272*444f66e7SMark Haywood 	(void) ddi_pathname(cpu_dip, path);
273*444f66e7SMark Haywood 
274*444f66e7SMark Haywood 	/*
275*444f66e7SMark Haywood 	 * get ppm CPU pstate domain
276*444f66e7SMark Haywood 	 */
277*444f66e7SMark Haywood 	devp = PPM_GET_PRIVATE(cpu_dip);
278*444f66e7SMark Haywood 	ASSERT(devp);
279*444f66e7SMark Haywood 	domp = devp->domp;
280*444f66e7SMark Haywood 	ASSERT(domp);
281*444f66e7SMark Haywood 
282*444f66e7SMark Haywood 	/*
283*444f66e7SMark Haywood 	 * remove CPU from conflist
284*444f66e7SMark Haywood 	 */
285*444f66e7SMark Haywood 	mutex_enter(&domp->lock);
286*444f66e7SMark Haywood 	for (dbpp = &domp->conflist; (pconf = *dbpp) != NULL; ) {
287*444f66e7SMark Haywood 		if (strcmp(pconf->name, path) != 0) {
288*444f66e7SMark Haywood 			dbpp = &pconf->next;
289*444f66e7SMark Haywood 			continue;
290*444f66e7SMark Haywood 		}
291*444f66e7SMark Haywood 		*dbpp = pconf->next;
292*444f66e7SMark Haywood 		kmem_free(pconf->name, strlen(pconf->name) + 1);
293*444f66e7SMark Haywood 		kmem_free(pconf, sizeof (*pconf));
294*444f66e7SMark Haywood 	}
295*444f66e7SMark Haywood 	mutex_exit(&domp->lock);
296*444f66e7SMark Haywood }
297