xref: /illumos-gate/usr/src/uts/i86pc/io/ppm_plat.c (revision b8524a1daa6d28203eb527eeae1a325429947758)
15cff7825Smh27603 /*
25cff7825Smh27603  * CDDL HEADER START
35cff7825Smh27603  *
45cff7825Smh27603  * The contents of this file are subject to the terms of the
55cff7825Smh27603  * Common Development and Distribution License (the "License").
65cff7825Smh27603  * You may not use this file except in compliance with the License.
75cff7825Smh27603  *
85cff7825Smh27603  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95cff7825Smh27603  * or http://www.opensolaris.org/os/licensing.
105cff7825Smh27603  * See the License for the specific language governing permissions
115cff7825Smh27603  * and limitations under the License.
125cff7825Smh27603  *
135cff7825Smh27603  * When distributing Covered Code, include this CDDL HEADER in each
145cff7825Smh27603  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155cff7825Smh27603  * If applicable, add the following below this CDDL HEADER, with the
165cff7825Smh27603  * fields enclosed by brackets "[]" replaced with your own identifying
175cff7825Smh27603  * information: Portions Copyright [yyyy] [name of copyright owner]
185cff7825Smh27603  *
195cff7825Smh27603  * CDDL HEADER END
205cff7825Smh27603  */
215cff7825Smh27603 /*
225cff7825Smh27603  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
235cff7825Smh27603  * Use is subject to license terms.
245cff7825Smh27603  */
255cff7825Smh27603 
265cff7825Smh27603 #pragma ident	"%Z%%M%	%I%	%E% SMI"
275cff7825Smh27603 
285cff7825Smh27603 /*
295cff7825Smh27603  * Platform Power Management master pseudo driver platform support.
305cff7825Smh27603  */
315cff7825Smh27603 
325cff7825Smh27603 #include <sys/ddi.h>
335cff7825Smh27603 #include <sys/sunddi.h>
345cff7825Smh27603 #include <sys/ppmvar.h>
355cff7825Smh27603 #include <sys/cpupm.h>
365cff7825Smh27603 
375cff7825Smh27603 static struct ppm_domit *
385cff7825Smh27603 ppm_get_domit_by_model(int model)
395cff7825Smh27603 {
405cff7825Smh27603 	struct ppm_domit *domit_p;
415cff7825Smh27603 	for (domit_p = ppm_domit_data; (domit_p->name &&
425cff7825Smh27603 	    (domit_p->model != model));	domit_p++)
435cff7825Smh27603 		;
445cff7825Smh27603 	ASSERT(domit_p);
455cff7825Smh27603 	return (domit_p);
465cff7825Smh27603 }
475cff7825Smh27603 
485cff7825Smh27603 void
495cff7825Smh27603 ppm_rebuild_cpu_domains(void)
505cff7825Smh27603 {
515cff7825Smh27603 	char *str = "ppm_rebuild_cpu_domains";
525cff7825Smh27603 	cpupm_cpu_dependency_t *dep;
535cff7825Smh27603 	cpupm_cpu_dependency_t *dep_next;
545cff7825Smh27603 	cpupm_cpu_node_t *cpu_next;
555cff7825Smh27603 	struct ppm_domit *domit_p;
565cff7825Smh27603 	ppm_domain_t *domp_old;
575cff7825Smh27603 	ppm_domain_t *domp;
585cff7825Smh27603 	ppm_dev_t *devp;
595cff7825Smh27603 	ppm_db_t *dbp;
605cff7825Smh27603 
615cff7825Smh27603 	/*
625cff7825Smh27603 	 * Get the CPU domain data
635cff7825Smh27603 	 */
645cff7825Smh27603 	domit_p = ppm_get_domit_by_model(PPMD_CPU);
655cff7825Smh27603 
665cff7825Smh27603 	/*
675cff7825Smh27603 	 * Find the CPU domain created from ppm.conf. It's only a
685cff7825Smh27603 	 * temporary domain used to make sure that all CPUs are
695cff7825Smh27603 	 * claimed. There should only be one such domain defined.
705cff7825Smh27603 	 */
715cff7825Smh27603 	for (domp = ppm_domain_p; (domp && (domp->model != PPMD_CPU));
725cff7825Smh27603 	    domp = domp->next)
735cff7825Smh27603 		;
745cff7825Smh27603 	if (domp == NULL) {
755cff7825Smh27603 		cmn_err(CE_WARN, "%s: ppm.conf does not define a CPU domain!",
765cff7825Smh27603 		    str);
775cff7825Smh27603 		return;
785cff7825Smh27603 	}
795cff7825Smh27603 	domp_old = domp;
805cff7825Smh27603 	for (domp = domp->next; domp; domp = domp->next) {
815cff7825Smh27603 		if (domp->model == PPMD_CPU) {
825cff7825Smh27603 			cmn_err(CE_WARN, "%s: Multiple CPU domains defined "
835cff7825Smh27603 			    "in ppm.conf!", str);
845cff7825Smh27603 			return;
855cff7825Smh27603 		}
865cff7825Smh27603 	}
875cff7825Smh27603 
885cff7825Smh27603 	/*
895cff7825Smh27603 	 * It is quite possible that the platform does not contain any
905cff7825Smh27603 	 * power manageable CPUs. If so, devlist will be NULL.
915cff7825Smh27603 	 */
925cff7825Smh27603 	if (domp_old->devlist == NULL) {
935cff7825Smh27603 		PPMD(D_CPU, ("%s: No CPUs claimed by ppm!\n", str));
945cff7825Smh27603 		return;
955cff7825Smh27603 	}
965cff7825Smh27603 
975cff7825Smh27603 	/*
985cff7825Smh27603 	 * Get the CPU dependencies as determined by the CPU driver. If
995cff7825Smh27603 	 * the CPU driver didn't create a valid set of dependencies, then
100422613b3Smh27603 	 * leave the domain as it is (which is unmanageable since
101422613b3Smh27603 	 * PPM_CPU_READY is off).
1025cff7825Smh27603 	 */
1035cff7825Smh27603 	dep = cpupm_get_cpu_dependencies();
1045cff7825Smh27603 	if (dep == NULL) {
105422613b3Smh27603 		PPMD(D_CPU, ("%s: No CPU dependency info!\n", str));
1065cff7825Smh27603 		return;
1075cff7825Smh27603 	}
1085cff7825Smh27603 
1095cff7825Smh27603 	/*
1105cff7825Smh27603 	 * Build real CPU domains. OFFLINE the old one as we don't
1115cff7825Smh27603 	 * want it to be used when we're done.
1125cff7825Smh27603 	 */
1135cff7825Smh27603 	mutex_enter(&domp_old->lock);
1145cff7825Smh27603 	domp_old->dflags |= PPMD_OFFLINE;
1155cff7825Smh27603 	for (dep_next = dep; dep_next; dep_next = dep_next->cd_next) {
1165cff7825Smh27603 		domp = kmem_zalloc(sizeof (*domp), KM_SLEEP);
1175cff7825Smh27603 		domp->name =  kmem_zalloc(MAXNAMELEN, KM_SLEEP);
1185cff7825Smh27603 		(void) snprintf(domp->name, MAXNAMELEN, "acpi_cpu_domain_%d",
1195cff7825Smh27603 		    dep_next->cd_dependency_id);
1205cff7825Smh27603 		mutex_init(&domp->lock, NULL, MUTEX_DRIVER, NULL);
1215cff7825Smh27603 		mutex_enter(&domp->lock);
1225cff7825Smh27603 		domp->dflags = domit_p->dflags | PPMD_CPU_READY;
1235cff7825Smh27603 		domp->pwr_cnt = 0;
1245cff7825Smh27603 		domp->propname = domp_old->propname;
1255cff7825Smh27603 		domp->model = domit_p->model;
1265cff7825Smh27603 		domp->status = domit_p->status;
1275cff7825Smh27603 
1285cff7825Smh27603 		/*
1295cff7825Smh27603 		 * Add devices to new domain. As a precaution,
1305cff7825Smh27603 		 * make sure that the device is currently owned by the
1315cff7825Smh27603 		 * ppm.conf defined CPU domain. Adding the device to the
1325cff7825Smh27603 		 * domain will result in the domain's "devlist" and "owned"
1335cff7825Smh27603 		 * lists being properly formed. It will also update the
1345cff7825Smh27603 		 * dip pointer to the device structure. We have to manually
1355cff7825Smh27603 		 * build the "conflist" for the domain. But conveniently, the
1365cff7825Smh27603 		 * "conflist" data is easily obtainable from the "devlist".
1375cff7825Smh27603 		 */
1385cff7825Smh27603 		for (cpu_next = dep_next->cd_cpu; cpu_next;
1395cff7825Smh27603 		    cpu_next = cpu_next->cn_next) {
1405cff7825Smh27603 			devp = PPM_GET_PRIVATE(cpu_next->cn_dip);
1415cff7825Smh27603 			ASSERT(devp && devp->domp == domp_old);
1425cff7825Smh27603 			devp = ppm_add_dev(cpu_next->cn_dip, domp);
1435cff7825Smh27603 			dbp = kmem_zalloc(sizeof (struct ppm_db), KM_SLEEP);
1445cff7825Smh27603 			dbp->name = kmem_zalloc((strlen(devp->path) + 1),
1455cff7825Smh27603 			    KM_SLEEP);
1465cff7825Smh27603 			(void) strcpy(dbp->name, devp->path);
1475cff7825Smh27603 			dbp->next = domp->conflist;
1485cff7825Smh27603 			domp->conflist = dbp;
1495cff7825Smh27603 		}
1505cff7825Smh27603 
1515cff7825Smh27603 		/*
1525cff7825Smh27603 		 * Note that we do not bother creating a "dc" list as there
1535cff7825Smh27603 		 * isn't one for x86 CPU power management. If this changes
1545cff7825Smh27603 		 * in the future some more work will need to be done to
1555cff7825Smh27603 		 * support it.
1565cff7825Smh27603 		 */
1575cff7825Smh27603 		ASSERT(domp_old->dc == NULL);
1585cff7825Smh27603 
1595cff7825Smh27603 		/*
1605cff7825Smh27603 		 * Add the domain to the live list.
1615cff7825Smh27603 		 */
1625cff7825Smh27603 		domp->next = ppm_domain_p;
1635cff7825Smh27603 		ppm_domain_p = domp;
1645cff7825Smh27603 
1655cff7825Smh27603 		mutex_exit(&domp->lock);
1665cff7825Smh27603 	}
1675cff7825Smh27603 	mutex_exit(&domp_old->lock);
1685cff7825Smh27603 	cpupm_free_cpu_dependencies();
1695cff7825Smh27603 }
1705cff7825Smh27603 
1715cff7825Smh27603 /*
1725cff7825Smh27603  * Used by ppm_redefine_topspeed() to set the highest power level of all CPUs
1735cff7825Smh27603  * in a domain.
1745cff7825Smh27603  */
1755cff7825Smh27603 void
1765cff7825Smh27603 ppm_set_topspeed(ppm_dev_t *cpup, int speed)
1775cff7825Smh27603 {
1785cff7825Smh27603 	for (cpup = cpup->domp->devlist; cpup != NULL; cpup = cpup->next)
1795cff7825Smh27603 		(*cpupm_set_topspeed)(cpup->dip, speed);
1805cff7825Smh27603 }
1815cff7825Smh27603 
1825cff7825Smh27603 /*
1835cff7825Smh27603  * Redefine the highest power level for all CPUs in a domain. This
1845cff7825Smh27603  * functionality is necessary because ACPI uses the _PPC to define
1855cff7825Smh27603  * a CPU's highest power level *and* allows the _PPC to be redefined
1865cff7825Smh27603  * dynamically. _PPC changes are communicated through _PPC change
1875cff7825Smh27603  * notifications caught by the CPU device driver.
1885cff7825Smh27603  */
1895cff7825Smh27603 void
1905cff7825Smh27603 ppm_redefine_topspeed(void *ctx)
1915cff7825Smh27603 {
1925cff7825Smh27603 	char *str = "ppm_redefine_topspeed";
1935cff7825Smh27603 	ppm_dev_t *cpup;
1945cff7825Smh27603 	ppm_dev_t *ncpup;
1955cff7825Smh27603 	int topspeed;
1965cff7825Smh27603 	int newspeed = -1;
1975cff7825Smh27603 
1985cff7825Smh27603 	cpup = PPM_GET_PRIVATE((dev_info_t *)ctx);
1995cff7825Smh27603 
2005cff7825Smh27603 	if (cpupm_get_topspeed == NULL || cpupm_set_topspeed == NULL) {
2015cff7825Smh27603 		cmn_err(CE_WARN, "%s: Cannot process request for instance %d "
2025cff7825Smh27603 		    "since cpupm interfaces are not initialized", str,
2035cff7825Smh27603 		    ddi_get_instance(cpup->dip));
2045cff7825Smh27603 		return;
2055cff7825Smh27603 	}
2065cff7825Smh27603 
2075cff7825Smh27603 	if (!(cpup->domp->dflags & PPMD_CPU_READY)) {
2085cff7825Smh27603 		PPMD(D_CPU, ("%s: instance %d received _PPC change "
2095cff7825Smh27603 		    "notification before PPMD_CPU_READY", str,
2105cff7825Smh27603 		    ddi_get_instance(cpup->dip)));
2115cff7825Smh27603 		return;
2125cff7825Smh27603 	}
2135cff7825Smh27603 
2145cff7825Smh27603 	/*
2155cff7825Smh27603 	 * Process each CPU in the domain.
2165cff7825Smh27603 	 */
2175cff7825Smh27603 	for (ncpup = cpup->domp->devlist; ncpup != NULL; ncpup = ncpup->next) {
2185cff7825Smh27603 		topspeed = (*cpupm_get_topspeed)(ncpup->dip);
2195cff7825Smh27603 		if (newspeed == -1 || topspeed < newspeed)
2205cff7825Smh27603 			newspeed = topspeed;
2215cff7825Smh27603 	}
2225cff7825Smh27603 
2235cff7825Smh27603 	ppm_set_topspeed(cpup, newspeed);
2245cff7825Smh27603 }
2255cff7825Smh27603 
2265cff7825Smh27603 /*
2275cff7825Smh27603  * Traverses all domains looking for CPU domains and for each CPU domain
2285cff7825Smh27603  * redefines the topspeed for that domain. The reason that this is necessary
2295cff7825Smh27603  * is that on x86 platforms ACPI allows the highest power level to be
2305cff7825Smh27603  * redefined dynamically. Once all CPU devices have been started it we
2315cff7825Smh27603  * need to go back and reinitialize the topspeeds (just in case it's changed).
2325cff7825Smh27603  */
2335cff7825Smh27603 void
2345cff7825Smh27603 ppm_init_topspeed(void)
2355cff7825Smh27603 {
2365cff7825Smh27603 	ppm_domain_t *domp;
2375cff7825Smh27603 	for (domp = ppm_domain_p; domp;	domp = domp->next) {
2385cff7825Smh27603 		if (domp->model != PPMD_CPU || !PPM_DOMAIN_UP(domp))
2395cff7825Smh27603 			continue;
2405cff7825Smh27603 		if (domp->devlist == NULL)
2415cff7825Smh27603 			continue;
2425cff7825Smh27603 		ppm_redefine_topspeed(domp->devlist->dip);
2435cff7825Smh27603 	}
2445cff7825Smh27603 }
2455cff7825Smh27603 
2465cff7825Smh27603 /*
2475cff7825Smh27603  * For x86 platforms CPU domains must be built dynamically at bootime.
2485cff7825Smh27603  * Until the domains have been built, refuse all power transition
2495cff7825Smh27603  * requests.
2505cff7825Smh27603  */
2515cff7825Smh27603 /* ARGSUSED */
2525cff7825Smh27603 boolean_t
2535cff7825Smh27603 ppm_manage_early_cpus(dev_info_t *dip, int new, int *result)
2545cff7825Smh27603 {
2555cff7825Smh27603 	ppm_dev_t *ppmd = PPM_GET_PRIVATE(dip);
2565cff7825Smh27603 
2575cff7825Smh27603 	if (!(ppmd->domp->dflags & PPMD_CPU_READY)) {
2585cff7825Smh27603 		PPMD(D_CPU, ("ppm_manage_early_cpus: attempt to manage CPU "
2595cff7825Smh27603 		    "before it was ready dip(0x%p)", (void *)dip));
2605cff7825Smh27603 		return (B_TRUE);
2615cff7825Smh27603 	}
2625cff7825Smh27603 	*result = DDI_FAILURE;
2635cff7825Smh27603 	return (B_FALSE);
2645cff7825Smh27603 }
2655cff7825Smh27603 
2665cff7825Smh27603 int
2675cff7825Smh27603 ppm_change_cpu_power(ppm_dev_t *ppmd, int newlevel)
2685cff7825Smh27603 {
2695cff7825Smh27603 #ifdef DEBUG
2705cff7825Smh27603 	char *str = "ppm_change_cpu_power";
2715cff7825Smh27603 #endif
2725cff7825Smh27603 	ppm_unit_t *unitp;
2735cff7825Smh27603 	ppm_domain_t *domp;
2745cff7825Smh27603 	ppm_dev_t *cpup;
2755cff7825Smh27603 	dev_info_t *dip;
2765cff7825Smh27603 	int oldlevel;
2775cff7825Smh27603 	int ret;
2785cff7825Smh27603 
2795cff7825Smh27603 	unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
2805cff7825Smh27603 	ASSERT(unitp);
2815cff7825Smh27603 	domp = ppmd->domp;
2825cff7825Smh27603 	cpup = domp->devlist;
2835cff7825Smh27603 
2845cff7825Smh27603 	dip = cpup->dip;
2855cff7825Smh27603 	ASSERT(dip);
2865cff7825Smh27603 
2875cff7825Smh27603 	oldlevel = cpup->level;
2885cff7825Smh27603 
2895cff7825Smh27603 	PPMD(D_CPU, ("%s: old %d, new %d\n", str, oldlevel, newlevel))
2905cff7825Smh27603 
2915cff7825Smh27603 	if (newlevel == oldlevel)
2925cff7825Smh27603 		return (DDI_SUCCESS);
2935cff7825Smh27603 
2945cff7825Smh27603 	/* bring each cpu to next level */
2955cff7825Smh27603 	for (; cpup; cpup = cpup->next) {
2965cff7825Smh27603 		ret = pm_power(cpup->dip, 0, newlevel);
2975cff7825Smh27603 		PPMD(D_CPU, ("%s: \"%s\", changed to level %d, ret %d\n",
2985cff7825Smh27603 		    str, cpup->path, newlevel, ret))
2995cff7825Smh27603 		if (ret == DDI_SUCCESS) {
3005cff7825Smh27603 			cpup->level = newlevel;
3015cff7825Smh27603 			cpup->rplvl = PM_LEVEL_UNKNOWN;
3025cff7825Smh27603 			continue;
3035cff7825Smh27603 		}
3045cff7825Smh27603 
3055cff7825Smh27603 		/*
3065cff7825Smh27603 		 * If the driver was unable to lower cpu speed,
307*b8524a1dSmh27603 		 * the cpu probably got busy; set the previous
308*b8524a1dSmh27603 		 * cpus back to the original level
3095cff7825Smh27603 		 */
310*b8524a1dSmh27603 		if (newlevel < oldlevel)
3115cff7825Smh27603 			ret = ppm_revert_cpu_power(cpup, oldlevel);
312*b8524a1dSmh27603 
3135cff7825Smh27603 		return (ret);
3145cff7825Smh27603 	}
3155cff7825Smh27603 
3165cff7825Smh27603 	return (DDI_SUCCESS);
3175cff7825Smh27603 }
318