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 * Copyright (c) 2009, Intel Corporation. 27 * All rights reserved. 28 */ 29 30 /* 31 * Platform Power Management master pseudo driver platform support. 32 */ 33 34 #include <sys/ddi.h> 35 #include <sys/sunddi.h> 36 #include <sys/ppmvar.h> 37 #include <sys/cpupm.h> 38 39 #define PPM_CPU_PSTATE_DOMAIN_FLG 0x100 40 41 /* 42 * Used by ppm_redefine_topspeed() to set the highest power level of all CPUs 43 * in a domain. 44 */ 45 void 46 ppm_set_topspeed(ppm_dev_t *cpup, int speed) 47 { 48 for (cpup = cpup->domp->devlist; cpup != NULL; cpup = cpup->next) 49 (*cpupm_set_topspeed_callb)(cpup->dip, speed); 50 } 51 52 /* 53 * Redefine the highest power level for all CPUs in a domain. This 54 * functionality is necessary because ACPI uses the _PPC to define 55 * a CPU's highest power level *and* allows the _PPC to be redefined 56 * dynamically. _PPC changes are communicated through _PPC change 57 * notifications caught by the CPU device driver. 58 */ 59 void 60 ppm_redefine_topspeed(void *ctx) 61 { 62 char *str = "ppm_redefine_topspeed"; 63 ppm_dev_t *cpup; 64 ppm_dev_t *ncpup; 65 int topspeed; 66 int newspeed = -1; 67 68 cpup = PPM_GET_PRIVATE((dev_info_t *)ctx); 69 70 if (cpupm_get_topspeed_callb == NULL || 71 cpupm_set_topspeed_callb == NULL) { 72 cmn_err(CE_WARN, "%s: Cannot process request for instance %d " 73 "since cpupm interfaces are not initialized", str, 74 ddi_get_instance(cpup->dip)); 75 return; 76 } 77 78 if (!(cpup->domp->dflags & PPMD_CPU_READY)) { 79 PPMD(D_CPU, ("%s: instance %d received _PPC change " 80 "notification before PPMD_CPU_READY", str, 81 ddi_get_instance(cpup->dip))); 82 return; 83 } 84 85 /* 86 * Process each CPU in the domain. 87 */ 88 for (ncpup = cpup->domp->devlist; ncpup != NULL; ncpup = ncpup->next) { 89 topspeed = (*cpupm_get_topspeed_callb)(ncpup->dip); 90 if (newspeed == -1 || topspeed < newspeed) 91 newspeed = topspeed; 92 } 93 94 ppm_set_topspeed(cpup, newspeed); 95 } 96 97 /* 98 * For x86 platforms CPU domains must be built dynamically at bootime. 99 * Until the domains have been built, refuse all power transition 100 * requests. 101 */ 102 /* ARGSUSED */ 103 boolean_t 104 ppm_manage_early_cpus(dev_info_t *dip, int new, int *result) 105 { 106 ppm_dev_t *ppmd = PPM_GET_PRIVATE(dip); 107 108 if (!(ppmd->domp->dflags & PPMD_CPU_READY)) { 109 PPMD(D_CPU, ("ppm_manage_early_cpus: attempt to manage CPU " 110 "before it was ready dip(0x%p)", (void *)dip)); 111 return (B_TRUE); 112 } 113 *result = DDI_FAILURE; 114 return (B_FALSE); 115 } 116 117 int 118 ppm_change_cpu_power(ppm_dev_t *ppmd, int newlevel) 119 { 120 #ifdef DEBUG 121 char *str = "ppm_change_cpu_power"; 122 #endif 123 ppm_unit_t *unitp; 124 ppm_domain_t *domp; 125 ppm_dev_t *cpup; 126 dev_info_t *dip; 127 int oldlevel; 128 int ret; 129 130 unitp = ddi_get_soft_state(ppm_statep, ppm_inst); 131 ASSERT(unitp); 132 domp = ppmd->domp; 133 cpup = domp->devlist; 134 135 dip = cpup->dip; 136 ASSERT(dip); 137 138 oldlevel = cpup->level; 139 140 PPMD(D_CPU, ("%s: old %d, new %d\n", str, oldlevel, newlevel)) 141 142 if (newlevel == oldlevel) 143 return (DDI_SUCCESS); 144 145 /* bring each cpu to next level */ 146 for (; cpup; cpup = cpup->next) { 147 ret = pm_power(cpup->dip, 0, newlevel); 148 PPMD(D_CPU, ("%s: \"%s\", changed to level %d, ret %d\n", 149 str, cpup->path, newlevel, ret)) 150 if (ret == DDI_SUCCESS) { 151 cpup->level = newlevel; 152 cpup->rplvl = PM_LEVEL_UNKNOWN; 153 continue; 154 } 155 156 /* 157 * If the driver was unable to lower cpu speed, 158 * the cpu probably got busy; set the previous 159 * cpus back to the original level 160 */ 161 if (newlevel < oldlevel) 162 ret = ppm_revert_cpu_power(cpup, oldlevel); 163 164 return (ret); 165 } 166 167 return (DDI_SUCCESS); 168 } 169 170 /* 171 * allocate ppm CPU pstate domain if non-existence, 172 * otherwise, add the CPU to the corresponding ppm 173 * CPU pstate domain. 174 */ 175 void 176 ppm_alloc_pstate_domains(cpu_t *cp) 177 { 178 cpupm_mach_state_t *mach_state; 179 uint32_t pm_domain; 180 int sub_domain; 181 ppm_domain_t *domp; 182 dev_info_t *cpu_dip; 183 ppm_db_t *dbp; 184 char path[MAXNAMELEN]; 185 186 mach_state = (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state); 187 ASSERT(mach_state); 188 pm_domain = mach_state->ms_pstate.cma_domain->pm_domain; 189 190 /* 191 * There are two purposes of sub_domain: 192 * 1. skip the orignal ppm CPU domain generated by ppm.conf 193 * 2. A CPU ppm domain could have several pstate domains indeed. 194 */ 195 sub_domain = pm_domain | PPM_CPU_PSTATE_DOMAIN_FLG; 196 197 /* 198 * Find ppm CPU pstate domain 199 */ 200 for (domp = ppm_domain_p; domp; domp = domp->next) { 201 if ((domp->model == PPMD_CPU) && 202 (domp->sub_domain == sub_domain)) { 203 break; 204 } 205 } 206 207 /* 208 * Create one ppm CPU pstate domain if no found 209 */ 210 if (domp == NULL) { 211 domp = kmem_zalloc(sizeof (*domp), KM_SLEEP); 212 mutex_init(&domp->lock, NULL, MUTEX_DRIVER, NULL); 213 mutex_enter(&domp->lock); 214 domp->name = kmem_zalloc(MAXNAMELEN, KM_SLEEP); 215 (void) snprintf(domp->name, MAXNAMELEN, "cpu_pstate_domain_%d", 216 pm_domain); 217 domp->sub_domain = sub_domain; 218 domp->dflags = PPMD_LOCK_ALL | PPMD_CPU_READY; 219 domp->pwr_cnt = 0; 220 domp->pwr_cnt++; 221 domp->propname = NULL; 222 domp->model = PPMD_CPU; 223 domp->status = PPMD_ON; 224 cpu_dip = mach_state->ms_dip; 225 (void) ddi_pathname(cpu_dip, path); 226 dbp = kmem_zalloc(sizeof (struct ppm_db), KM_SLEEP); 227 dbp->name = kmem_zalloc((strlen(path) + 1), 228 KM_SLEEP); 229 (void) strcpy(dbp->name, path); 230 dbp->next = domp->conflist; 231 domp->conflist = dbp; 232 domp->next = ppm_domain_p; 233 ppm_domain_p = domp; 234 mutex_exit(&domp->lock); 235 } 236 /* 237 * We found one matched ppm CPU pstate domain, 238 * add cpu to this domain 239 */ 240 else { 241 mutex_enter(&domp->lock); 242 cpu_dip = mach_state->ms_dip; 243 (void) ddi_pathname(cpu_dip, path); 244 dbp = kmem_zalloc(sizeof (struct ppm_db), KM_SLEEP); 245 dbp->name = kmem_zalloc((strlen(path) + 1), 246 KM_SLEEP); 247 (void) strcpy(dbp->name, path); 248 dbp->next = domp->conflist; 249 domp->conflist = dbp; 250 domp->pwr_cnt++; 251 mutex_exit(&domp->lock); 252 } 253 } 254 255 /* 256 * remove CPU from the corresponding ppm CPU pstate 257 * domain. We only remove CPU from conflist here. 258 */ 259 void 260 ppm_free_pstate_domains(cpu_t *cp) 261 { 262 cpupm_mach_state_t *mach_state; 263 ppm_domain_t *domp; 264 ppm_dev_t *devp; 265 dev_info_t *cpu_dip; 266 ppm_db_t **dbpp, *pconf; 267 char path[MAXNAMELEN]; 268 269 mach_state = (cpupm_mach_state_t *)(cp->cpu_m.mcpu_pm_mach_state); 270 ASSERT(mach_state); 271 cpu_dip = mach_state->ms_dip; 272 (void) ddi_pathname(cpu_dip, path); 273 274 /* 275 * get ppm CPU pstate domain 276 */ 277 devp = PPM_GET_PRIVATE(cpu_dip); 278 ASSERT(devp); 279 domp = devp->domp; 280 ASSERT(domp); 281 282 /* 283 * remove CPU from conflist 284 */ 285 mutex_enter(&domp->lock); 286 for (dbpp = &domp->conflist; (pconf = *dbpp) != NULL; ) { 287 if (strcmp(pconf->name, path) != 0) { 288 dbpp = &pconf->next; 289 continue; 290 } 291 *dbpp = pconf->next; 292 kmem_free(pconf->name, strlen(pconf->name) + 1); 293 kmem_free(pconf, sizeof (*pconf)); 294 } 295 mutex_exit(&domp->lock); 296 } 297