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 100*422613b3Smh27603 * leave the domain as it is (which is unmanageable since 101*422613b3Smh27603 * PPM_CPU_READY is off). 1025cff7825Smh27603 */ 1035cff7825Smh27603 dep = cpupm_get_cpu_dependencies(); 1045cff7825Smh27603 if (dep == NULL) { 105*422613b3Smh27603 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, 3075cff7825Smh27603 * the cpu probably got busy. Best to change 3085cff7825Smh27603 * speed back to normal. 3095cff7825Smh27603 */ 3105cff7825Smh27603 if (newlevel < oldlevel) { 3115cff7825Smh27603 oldlevel = pm_get_normal_power(dip, 0); 3125cff7825Smh27603 ret = ppm_revert_cpu_power(cpup, oldlevel); 3135cff7825Smh27603 } 3145cff7825Smh27603 return (ret); 3155cff7825Smh27603 } 3165cff7825Smh27603 3175cff7825Smh27603 return (DDI_SUCCESS); 3185cff7825Smh27603 } 319