1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * Platform Power Management master pseudo driver platform support. 30 */ 31 32 #include <sys/ddi.h> 33 #include <sys/sunddi.h> 34 #include <sys/ppmvar.h> 35 #include <sys/cpupm.h> 36 37 static struct ppm_domit * 38 ppm_get_domit_by_model(int model) 39 { 40 struct ppm_domit *domit_p; 41 for (domit_p = ppm_domit_data; (domit_p->name && 42 (domit_p->model != model)); domit_p++) 43 ; 44 ASSERT(domit_p); 45 return (domit_p); 46 } 47 48 void 49 ppm_rebuild_cpu_domains(void) 50 { 51 char *str = "ppm_rebuild_cpu_domains"; 52 cpupm_cpu_dependency_t *dep; 53 cpupm_cpu_dependency_t *dep_next; 54 cpupm_cpu_node_t *cpu_next; 55 struct ppm_domit *domit_p; 56 ppm_domain_t *domp_old; 57 ppm_domain_t *domp; 58 ppm_dev_t *devp; 59 ppm_db_t *dbp; 60 61 /* 62 * Get the CPU domain data 63 */ 64 domit_p = ppm_get_domit_by_model(PPMD_CPU); 65 66 /* 67 * Find the CPU domain created from ppm.conf. It's only a 68 * temporary domain used to make sure that all CPUs are 69 * claimed. There should only be one such domain defined. 70 */ 71 for (domp = ppm_domain_p; (domp && (domp->model != PPMD_CPU)); 72 domp = domp->next) 73 ; 74 if (domp == NULL) { 75 cmn_err(CE_WARN, "%s: ppm.conf does not define a CPU domain!", 76 str); 77 return; 78 } 79 domp_old = domp; 80 for (domp = domp->next; domp; domp = domp->next) { 81 if (domp->model == PPMD_CPU) { 82 cmn_err(CE_WARN, "%s: Multiple CPU domains defined " 83 "in ppm.conf!", str); 84 return; 85 } 86 } 87 88 /* 89 * It is quite possible that the platform does not contain any 90 * power manageable CPUs. If so, devlist will be NULL. 91 */ 92 if (domp_old->devlist == NULL) { 93 PPMD(D_CPU, ("%s: No CPUs claimed by ppm!\n", str)); 94 return; 95 } 96 97 /* 98 * Get the CPU dependencies as determined by the CPU driver. If 99 * the CPU driver didn't create a valid set of dependencies, then 100 * default to all CPUs in one domain. 101 */ 102 dep = cpupm_get_cpu_dependencies(); 103 if (dep == NULL) { 104 cmn_err(CE_WARN, "%s: Could not retrieve CPU dependency info!", 105 str); 106 domp_old->dflags |= PPMD_CPU_READY; 107 return; 108 } 109 110 /* 111 * Build real CPU domains. OFFLINE the old one as we don't 112 * want it to be used when we're done. 113 */ 114 mutex_enter(&domp_old->lock); 115 domp_old->dflags |= PPMD_OFFLINE; 116 for (dep_next = dep; dep_next; dep_next = dep_next->cd_next) { 117 domp = kmem_zalloc(sizeof (*domp), KM_SLEEP); 118 domp->name = kmem_zalloc(MAXNAMELEN, KM_SLEEP); 119 (void) snprintf(domp->name, MAXNAMELEN, "acpi_cpu_domain_%d", 120 dep_next->cd_dependency_id); 121 mutex_init(&domp->lock, NULL, MUTEX_DRIVER, NULL); 122 mutex_enter(&domp->lock); 123 domp->dflags = domit_p->dflags | PPMD_CPU_READY; 124 domp->pwr_cnt = 0; 125 domp->propname = domp_old->propname; 126 domp->model = domit_p->model; 127 domp->status = domit_p->status; 128 129 /* 130 * Add devices to new domain. As a precaution, 131 * make sure that the device is currently owned by the 132 * ppm.conf defined CPU domain. Adding the device to the 133 * domain will result in the domain's "devlist" and "owned" 134 * lists being properly formed. It will also update the 135 * dip pointer to the device structure. We have to manually 136 * build the "conflist" for the domain. But conveniently, the 137 * "conflist" data is easily obtainable from the "devlist". 138 */ 139 for (cpu_next = dep_next->cd_cpu; cpu_next; 140 cpu_next = cpu_next->cn_next) { 141 devp = PPM_GET_PRIVATE(cpu_next->cn_dip); 142 ASSERT(devp && devp->domp == domp_old); 143 devp = ppm_add_dev(cpu_next->cn_dip, domp); 144 dbp = kmem_zalloc(sizeof (struct ppm_db), KM_SLEEP); 145 dbp->name = kmem_zalloc((strlen(devp->path) + 1), 146 KM_SLEEP); 147 (void) strcpy(dbp->name, devp->path); 148 dbp->next = domp->conflist; 149 domp->conflist = dbp; 150 } 151 152 /* 153 * Note that we do not bother creating a "dc" list as there 154 * isn't one for x86 CPU power management. If this changes 155 * in the future some more work will need to be done to 156 * support it. 157 */ 158 ASSERT(domp_old->dc == NULL); 159 160 /* 161 * Add the domain to the live list. 162 */ 163 domp->next = ppm_domain_p; 164 ppm_domain_p = domp; 165 166 mutex_exit(&domp->lock); 167 } 168 mutex_exit(&domp_old->lock); 169 cpupm_free_cpu_dependencies(); 170 } 171 172 /* 173 * Used by ppm_redefine_topspeed() to set the highest power level of all CPUs 174 * in a domain. 175 */ 176 void 177 ppm_set_topspeed(ppm_dev_t *cpup, int speed) 178 { 179 for (cpup = cpup->domp->devlist; cpup != NULL; cpup = cpup->next) 180 (*cpupm_set_topspeed)(cpup->dip, speed); 181 } 182 183 /* 184 * Redefine the highest power level for all CPUs in a domain. This 185 * functionality is necessary because ACPI uses the _PPC to define 186 * a CPU's highest power level *and* allows the _PPC to be redefined 187 * dynamically. _PPC changes are communicated through _PPC change 188 * notifications caught by the CPU device driver. 189 */ 190 void 191 ppm_redefine_topspeed(void *ctx) 192 { 193 char *str = "ppm_redefine_topspeed"; 194 ppm_dev_t *cpup; 195 ppm_dev_t *ncpup; 196 int topspeed; 197 int newspeed = -1; 198 199 cpup = PPM_GET_PRIVATE((dev_info_t *)ctx); 200 201 if (cpupm_get_topspeed == NULL || cpupm_set_topspeed == NULL) { 202 cmn_err(CE_WARN, "%s: Cannot process request for instance %d " 203 "since cpupm interfaces are not initialized", str, 204 ddi_get_instance(cpup->dip)); 205 return; 206 } 207 208 if (!(cpup->domp->dflags & PPMD_CPU_READY)) { 209 PPMD(D_CPU, ("%s: instance %d received _PPC change " 210 "notification before PPMD_CPU_READY", str, 211 ddi_get_instance(cpup->dip))); 212 return; 213 } 214 215 /* 216 * Process each CPU in the domain. 217 */ 218 for (ncpup = cpup->domp->devlist; ncpup != NULL; ncpup = ncpup->next) { 219 topspeed = (*cpupm_get_topspeed)(ncpup->dip); 220 if (newspeed == -1 || topspeed < newspeed) 221 newspeed = topspeed; 222 } 223 224 ppm_set_topspeed(cpup, newspeed); 225 } 226 227 /* 228 * Traverses all domains looking for CPU domains and for each CPU domain 229 * redefines the topspeed for that domain. The reason that this is necessary 230 * is that on x86 platforms ACPI allows the highest power level to be 231 * redefined dynamically. Once all CPU devices have been started it we 232 * need to go back and reinitialize the topspeeds (just in case it's changed). 233 */ 234 void 235 ppm_init_topspeed(void) 236 { 237 ppm_domain_t *domp; 238 for (domp = ppm_domain_p; domp; domp = domp->next) { 239 if (domp->model != PPMD_CPU || !PPM_DOMAIN_UP(domp)) 240 continue; 241 if (domp->devlist == NULL) 242 continue; 243 ppm_redefine_topspeed(domp->devlist->dip); 244 } 245 } 246 247 /* 248 * For x86 platforms CPU domains must be built dynamically at bootime. 249 * Until the domains have been built, refuse all power transition 250 * requests. 251 */ 252 /* ARGSUSED */ 253 boolean_t 254 ppm_manage_early_cpus(dev_info_t *dip, int new, int *result) 255 { 256 ppm_dev_t *ppmd = PPM_GET_PRIVATE(dip); 257 258 if (!(ppmd->domp->dflags & PPMD_CPU_READY)) { 259 PPMD(D_CPU, ("ppm_manage_early_cpus: attempt to manage CPU " 260 "before it was ready dip(0x%p)", (void *)dip)); 261 return (B_TRUE); 262 } 263 *result = DDI_FAILURE; 264 return (B_FALSE); 265 } 266 267 int 268 ppm_change_cpu_power(ppm_dev_t *ppmd, int newlevel) 269 { 270 #ifdef DEBUG 271 char *str = "ppm_change_cpu_power"; 272 #endif 273 ppm_unit_t *unitp; 274 ppm_domain_t *domp; 275 ppm_dev_t *cpup; 276 dev_info_t *dip; 277 int oldlevel; 278 int ret; 279 280 unitp = ddi_get_soft_state(ppm_statep, ppm_inst); 281 ASSERT(unitp); 282 domp = ppmd->domp; 283 cpup = domp->devlist; 284 285 dip = cpup->dip; 286 ASSERT(dip); 287 288 oldlevel = cpup->level; 289 290 PPMD(D_CPU, ("%s: old %d, new %d\n", str, oldlevel, newlevel)) 291 292 if (newlevel == oldlevel) 293 return (DDI_SUCCESS); 294 295 /* bring each cpu to next level */ 296 for (; cpup; cpup = cpup->next) { 297 ret = pm_power(cpup->dip, 0, newlevel); 298 PPMD(D_CPU, ("%s: \"%s\", changed to level %d, ret %d\n", 299 str, cpup->path, newlevel, ret)) 300 if (ret == DDI_SUCCESS) { 301 cpup->level = newlevel; 302 cpup->rplvl = PM_LEVEL_UNKNOWN; 303 continue; 304 } 305 306 /* 307 * If the driver was unable to lower cpu speed, 308 * the cpu probably got busy. Best to change 309 * speed back to normal. 310 */ 311 if (newlevel < oldlevel) { 312 oldlevel = pm_get_normal_power(dip, 0); 313 ret = ppm_revert_cpu_power(cpup, oldlevel); 314 } 315 return (ret); 316 } 317 318 return (DDI_SUCCESS); 319 } 320