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 /* 220e751525SEric Saxe * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 235cff7825Smh27603 * Use is subject to license terms. 245cff7825Smh27603 */ 25*444f66e7SMark Haywood /* 26*444f66e7SMark Haywood * Copyright (c) 2009, Intel Corporation. 27*444f66e7SMark Haywood * All rights reserved. 28*444f66e7SMark Haywood */ 295cff7825Smh27603 305cff7825Smh27603 /* 315cff7825Smh27603 * Platform Power Management master pseudo driver platform support. 325cff7825Smh27603 */ 335cff7825Smh27603 345cff7825Smh27603 #include <sys/ddi.h> 355cff7825Smh27603 #include <sys/sunddi.h> 365cff7825Smh27603 #include <sys/ppmvar.h> 375cff7825Smh27603 #include <sys/cpupm.h> 385cff7825Smh27603 39*444f66e7SMark Haywood #define PPM_CPU_PSTATE_DOMAIN_FLG 0x100 405cff7825Smh27603 415cff7825Smh27603 /* 425cff7825Smh27603 * Used by ppm_redefine_topspeed() to set the highest power level of all CPUs 435cff7825Smh27603 * in a domain. 445cff7825Smh27603 */ 455cff7825Smh27603 void 465cff7825Smh27603 ppm_set_topspeed(ppm_dev_t *cpup, int speed) 475cff7825Smh27603 { 485cff7825Smh27603 for (cpup = cpup->domp->devlist; cpup != NULL; cpup = cpup->next) 490e751525SEric Saxe (*cpupm_set_topspeed_callb)(cpup->dip, speed); 505cff7825Smh27603 } 515cff7825Smh27603 525cff7825Smh27603 /* 535cff7825Smh27603 * Redefine the highest power level for all CPUs in a domain. This 545cff7825Smh27603 * functionality is necessary because ACPI uses the _PPC to define 555cff7825Smh27603 * a CPU's highest power level *and* allows the _PPC to be redefined 565cff7825Smh27603 * dynamically. _PPC changes are communicated through _PPC change 575cff7825Smh27603 * notifications caught by the CPU device driver. 585cff7825Smh27603 */ 595cff7825Smh27603 void 605cff7825Smh27603 ppm_redefine_topspeed(void *ctx) 615cff7825Smh27603 { 625cff7825Smh27603 char *str = "ppm_redefine_topspeed"; 635cff7825Smh27603 ppm_dev_t *cpup; 645cff7825Smh27603 ppm_dev_t *ncpup; 655cff7825Smh27603 int topspeed; 665cff7825Smh27603 int newspeed = -1; 675cff7825Smh27603 685cff7825Smh27603 cpup = PPM_GET_PRIVATE((dev_info_t *)ctx); 695cff7825Smh27603 700e751525SEric Saxe if (cpupm_get_topspeed_callb == NULL || 710e751525SEric Saxe cpupm_set_topspeed_callb == NULL) { 725cff7825Smh27603 cmn_err(CE_WARN, "%s: Cannot process request for instance %d " 735cff7825Smh27603 "since cpupm interfaces are not initialized", str, 745cff7825Smh27603 ddi_get_instance(cpup->dip)); 755cff7825Smh27603 return; 765cff7825Smh27603 } 775cff7825Smh27603 785cff7825Smh27603 if (!(cpup->domp->dflags & PPMD_CPU_READY)) { 795cff7825Smh27603 PPMD(D_CPU, ("%s: instance %d received _PPC change " 805cff7825Smh27603 "notification before PPMD_CPU_READY", str, 815cff7825Smh27603 ddi_get_instance(cpup->dip))); 825cff7825Smh27603 return; 835cff7825Smh27603 } 845cff7825Smh27603 855cff7825Smh27603 /* 865cff7825Smh27603 * Process each CPU in the domain. 875cff7825Smh27603 */ 885cff7825Smh27603 for (ncpup = cpup->domp->devlist; ncpup != NULL; ncpup = ncpup->next) { 890e751525SEric Saxe topspeed = (*cpupm_get_topspeed_callb)(ncpup->dip); 905cff7825Smh27603 if (newspeed == -1 || topspeed < newspeed) 915cff7825Smh27603 newspeed = topspeed; 925cff7825Smh27603 } 935cff7825Smh27603 945cff7825Smh27603 ppm_set_topspeed(cpup, newspeed); 955cff7825Smh27603 } 965cff7825Smh27603 975cff7825Smh27603 /* 985cff7825Smh27603 * For x86 platforms CPU domains must be built dynamically at bootime. 995cff7825Smh27603 * Until the domains have been built, refuse all power transition 1005cff7825Smh27603 * requests. 1015cff7825Smh27603 */ 1025cff7825Smh27603 /* ARGSUSED */ 1035cff7825Smh27603 boolean_t 1045cff7825Smh27603 ppm_manage_early_cpus(dev_info_t *dip, int new, int *result) 1055cff7825Smh27603 { 1065cff7825Smh27603 ppm_dev_t *ppmd = PPM_GET_PRIVATE(dip); 1075cff7825Smh27603 1085cff7825Smh27603 if (!(ppmd->domp->dflags & PPMD_CPU_READY)) { 1095cff7825Smh27603 PPMD(D_CPU, ("ppm_manage_early_cpus: attempt to manage CPU " 1105cff7825Smh27603 "before it was ready dip(0x%p)", (void *)dip)); 1115cff7825Smh27603 return (B_TRUE); 1125cff7825Smh27603 } 1135cff7825Smh27603 *result = DDI_FAILURE; 1145cff7825Smh27603 return (B_FALSE); 1155cff7825Smh27603 } 1165cff7825Smh27603 1175cff7825Smh27603 int 1185cff7825Smh27603 ppm_change_cpu_power(ppm_dev_t *ppmd, int newlevel) 1195cff7825Smh27603 { 1205cff7825Smh27603 #ifdef DEBUG 1215cff7825Smh27603 char *str = "ppm_change_cpu_power"; 1225cff7825Smh27603 #endif 1235cff7825Smh27603 ppm_unit_t *unitp; 1245cff7825Smh27603 ppm_domain_t *domp; 1255cff7825Smh27603 ppm_dev_t *cpup; 1265cff7825Smh27603 dev_info_t *dip; 1275cff7825Smh27603 int oldlevel; 1285cff7825Smh27603 int ret; 1295cff7825Smh27603 1305cff7825Smh27603 unitp = ddi_get_soft_state(ppm_statep, ppm_inst); 1315cff7825Smh27603 ASSERT(unitp); 1325cff7825Smh27603 domp = ppmd->domp; 1335cff7825Smh27603 cpup = domp->devlist; 1345cff7825Smh27603 1355cff7825Smh27603 dip = cpup->dip; 1365cff7825Smh27603 ASSERT(dip); 1375cff7825Smh27603 1385cff7825Smh27603 oldlevel = cpup->level; 1395cff7825Smh27603 1405cff7825Smh27603 PPMD(D_CPU, ("%s: old %d, new %d\n", str, oldlevel, newlevel)) 1415cff7825Smh27603 1425cff7825Smh27603 if (newlevel == oldlevel) 1435cff7825Smh27603 return (DDI_SUCCESS); 1445cff7825Smh27603 1455cff7825Smh27603 /* bring each cpu to next level */ 1465cff7825Smh27603 for (; cpup; cpup = cpup->next) { 1475cff7825Smh27603 ret = pm_power(cpup->dip, 0, newlevel); 1485cff7825Smh27603 PPMD(D_CPU, ("%s: \"%s\", changed to level %d, ret %d\n", 1495cff7825Smh27603 str, cpup->path, newlevel, ret)) 1505cff7825Smh27603 if (ret == DDI_SUCCESS) { 1515cff7825Smh27603 cpup->level = newlevel; 1525cff7825Smh27603 cpup->rplvl = PM_LEVEL_UNKNOWN; 1535cff7825Smh27603 continue; 1545cff7825Smh27603 } 1555cff7825Smh27603 1565cff7825Smh27603 /* 1575cff7825Smh27603 * If the driver was unable to lower cpu speed, 158b8524a1dSmh27603 * the cpu probably got busy; set the previous 159b8524a1dSmh27603 * cpus back to the original level 1605cff7825Smh27603 */ 161b8524a1dSmh27603 if (newlevel < oldlevel) 1625cff7825Smh27603 ret = ppm_revert_cpu_power(cpup, oldlevel); 163b8524a1dSmh27603 1645cff7825Smh27603 return (ret); 1655cff7825Smh27603 } 1665cff7825Smh27603 1675cff7825Smh27603 return (DDI_SUCCESS); 1685cff7825Smh27603 } 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 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 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