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