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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * Platform Power Management master pseudo driver platform support. 28 */ 29 30 #include <sys/ddi.h> 31 #include <sys/sunddi.h> 32 #include <sys/ppmvar.h> 33 #include <sys/cpupm.h> 34 35 static struct ppm_domit * 36 ppm_get_domit_by_model(int model) 37 { 38 struct ppm_domit *domit_p; 39 for (domit_p = ppm_domit_data; (domit_p->name && 40 (domit_p->model != model)); domit_p++) 41 ; 42 ASSERT(domit_p); 43 return (domit_p); 44 } 45 46 void 47 ppm_rebuild_cpu_domains(void) 48 { 49 char *str = "ppm_rebuild_cpu_domains"; 50 cpupm_state_domains_t *dep; 51 cpupm_state_domains_t *dep_next; 52 struct ppm_domit *domit_p; 53 ppm_domain_t *domp_old; 54 ppm_domain_t *domp; 55 ppm_dev_t *devp; 56 ppm_db_t *dbp; 57 uint_t cpu_id; 58 cpuset_t dom_cpu_set; 59 int result; 60 dev_info_t *cpu_dip; 61 62 /* 63 * Get the CPU domain data 64 */ 65 domit_p = ppm_get_domit_by_model(PPMD_CPU); 66 67 /* 68 * Find the CPU domain created from ppm.conf. It's only a 69 * temporary domain used to make sure that all CPUs are 70 * claimed. There should only be one such domain defined. 71 */ 72 for (domp = ppm_domain_p; (domp && (domp->model != PPMD_CPU)); 73 domp = domp->next) 74 ; 75 if (domp == NULL) { 76 cmn_err(CE_WARN, "%s: ppm.conf does not define a CPU domain!", 77 str); 78 return; 79 } 80 domp_old = domp; 81 for (domp = domp->next; domp; domp = domp->next) { 82 if (domp->model == PPMD_CPU) { 83 cmn_err(CE_WARN, "%s: Multiple CPU domains defined " 84 "in ppm.conf!", str); 85 return; 86 } 87 } 88 89 /* 90 * It is quite possible that the platform does not contain any 91 * power manageable CPUs. If so, devlist will be NULL. 92 */ 93 if (domp_old->devlist == NULL) { 94 PPMD(D_CPU, ("%s: No CPUs claimed by ppm!\n", str)); 95 return; 96 } 97 98 /* 99 * Get the CPU dependencies as determined by the CPU driver. If 100 * the CPU driver didn't create a valid set of dependencies, then 101 * leave the domain as it is (which is unmanageable since 102 * PPM_CPU_READY is off). 103 */ 104 dep = cpupm_pstate_domains; 105 if (dep == NULL) { 106 PPMD(D_CPU, ("%s: No CPU dependency info!\n", str)); 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->pm_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->pm_domain); 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 dom_cpu_set = dep_next->pm_cpus; 140 do { 141 CPUSET_FIND(dom_cpu_set, cpu_id); 142 if (cpu_id == CPUSET_NOTINSET) 143 break; 144 145 ASSERT(cpu_id < NCPU); 146 cpu_dip = ((cpupm_mach_state_t *) 147 (cpu[cpu_id]->cpu_m.mcpu_pm_mach_state))->ms_dip; 148 devp = PPM_GET_PRIVATE(cpu_dip); 149 ASSERT(devp && devp->domp == domp_old); 150 devp = ppm_add_dev(cpu_dip, domp); 151 dbp = kmem_zalloc(sizeof (struct ppm_db), KM_SLEEP); 152 dbp->name = kmem_zalloc((strlen(devp->path) + 1), 153 KM_SLEEP); 154 (void) strcpy(dbp->name, devp->path); 155 dbp->next = domp->conflist; 156 domp->conflist = dbp; 157 158 CPUSET_ATOMIC_XDEL(dom_cpu_set, cpu_id, result); 159 } while (result == 0); 160 161 /* 162 * Note that we do not bother creating a "dc" list as there 163 * isn't one for x86 CPU power management. If this changes 164 * in the future some more work will need to be done to 165 * support it. 166 */ 167 ASSERT(domp_old->dc == NULL); 168 169 /* 170 * Add the domain to the live list. 171 */ 172 domp->next = ppm_domain_p; 173 ppm_domain_p = domp; 174 175 mutex_exit(&domp->lock); 176 } 177 mutex_exit(&domp_old->lock); 178 } 179 180 /* 181 * Used by ppm_redefine_topspeed() to set the highest power level of all CPUs 182 * in a domain. 183 */ 184 void 185 ppm_set_topspeed(ppm_dev_t *cpup, int speed) 186 { 187 for (cpup = cpup->domp->devlist; cpup != NULL; cpup = cpup->next) 188 (*cpupm_set_topspeed_callb)(cpup->dip, speed); 189 } 190 191 /* 192 * Redefine the highest power level for all CPUs in a domain. This 193 * functionality is necessary because ACPI uses the _PPC to define 194 * a CPU's highest power level *and* allows the _PPC to be redefined 195 * dynamically. _PPC changes are communicated through _PPC change 196 * notifications caught by the CPU device driver. 197 */ 198 void 199 ppm_redefine_topspeed(void *ctx) 200 { 201 char *str = "ppm_redefine_topspeed"; 202 ppm_dev_t *cpup; 203 ppm_dev_t *ncpup; 204 int topspeed; 205 int newspeed = -1; 206 207 cpup = PPM_GET_PRIVATE((dev_info_t *)ctx); 208 209 if (cpupm_get_topspeed_callb == NULL || 210 cpupm_set_topspeed_callb == NULL) { 211 cmn_err(CE_WARN, "%s: Cannot process request for instance %d " 212 "since cpupm interfaces are not initialized", str, 213 ddi_get_instance(cpup->dip)); 214 return; 215 } 216 217 if (!(cpup->domp->dflags & PPMD_CPU_READY)) { 218 PPMD(D_CPU, ("%s: instance %d received _PPC change " 219 "notification before PPMD_CPU_READY", str, 220 ddi_get_instance(cpup->dip))); 221 return; 222 } 223 224 /* 225 * Process each CPU in the domain. 226 */ 227 for (ncpup = cpup->domp->devlist; ncpup != NULL; ncpup = ncpup->next) { 228 topspeed = (*cpupm_get_topspeed_callb)(ncpup->dip); 229 if (newspeed == -1 || topspeed < newspeed) 230 newspeed = topspeed; 231 } 232 233 ppm_set_topspeed(cpup, newspeed); 234 } 235 236 /* 237 * Traverses all domains looking for CPU domains and for each CPU domain 238 * redefines the topspeed for that domain. The reason that this is necessary 239 * is that on x86 platforms ACPI allows the highest power level to be 240 * redefined dynamically. Once all CPU devices have been started it we 241 * need to go back and reinitialize the topspeeds (just in case it's changed). 242 */ 243 void 244 ppm_init_topspeed(void) 245 { 246 ppm_domain_t *domp; 247 for (domp = ppm_domain_p; domp; domp = domp->next) { 248 if (domp->model != PPMD_CPU || !PPM_DOMAIN_UP(domp)) 249 continue; 250 if (domp->devlist == NULL) 251 continue; 252 ppm_redefine_topspeed(domp->devlist->dip); 253 } 254 } 255 256 /* 257 * For x86 platforms CPU domains must be built dynamically at bootime. 258 * Until the domains have been built, refuse all power transition 259 * requests. 260 */ 261 /* ARGSUSED */ 262 boolean_t 263 ppm_manage_early_cpus(dev_info_t *dip, int new, int *result) 264 { 265 ppm_dev_t *ppmd = PPM_GET_PRIVATE(dip); 266 267 if (!(ppmd->domp->dflags & PPMD_CPU_READY)) { 268 PPMD(D_CPU, ("ppm_manage_early_cpus: attempt to manage CPU " 269 "before it was ready dip(0x%p)", (void *)dip)); 270 return (B_TRUE); 271 } 272 *result = DDI_FAILURE; 273 return (B_FALSE); 274 } 275 276 int 277 ppm_change_cpu_power(ppm_dev_t *ppmd, int newlevel) 278 { 279 #ifdef DEBUG 280 char *str = "ppm_change_cpu_power"; 281 #endif 282 ppm_unit_t *unitp; 283 ppm_domain_t *domp; 284 ppm_dev_t *cpup; 285 dev_info_t *dip; 286 int oldlevel; 287 int ret; 288 289 unitp = ddi_get_soft_state(ppm_statep, ppm_inst); 290 ASSERT(unitp); 291 domp = ppmd->domp; 292 cpup = domp->devlist; 293 294 dip = cpup->dip; 295 ASSERT(dip); 296 297 oldlevel = cpup->level; 298 299 PPMD(D_CPU, ("%s: old %d, new %d\n", str, oldlevel, newlevel)) 300 301 if (newlevel == oldlevel) 302 return (DDI_SUCCESS); 303 304 /* bring each cpu to next level */ 305 for (; cpup; cpup = cpup->next) { 306 ret = pm_power(cpup->dip, 0, newlevel); 307 PPMD(D_CPU, ("%s: \"%s\", changed to level %d, ret %d\n", 308 str, cpup->path, newlevel, ret)) 309 if (ret == DDI_SUCCESS) { 310 cpup->level = newlevel; 311 cpup->rplvl = PM_LEVEL_UNKNOWN; 312 continue; 313 } 314 315 /* 316 * If the driver was unable to lower cpu speed, 317 * the cpu probably got busy; set the previous 318 * cpus back to the original level 319 */ 320 if (newlevel < oldlevel) 321 ret = ppm_revert_cpu_power(cpup, oldlevel); 322 323 return (ret); 324 } 325 326 return (DDI_SUCCESS); 327 } 328