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