1*4667Smh27603 /*
2*4667Smh27603 * CDDL HEADER START
3*4667Smh27603 *
4*4667Smh27603 * The contents of this file are subject to the terms of the
5*4667Smh27603 * Common Development and Distribution License (the "License").
6*4667Smh27603 * You may not use this file except in compliance with the License.
7*4667Smh27603 *
8*4667Smh27603 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*4667Smh27603 * or http://www.opensolaris.org/os/licensing.
10*4667Smh27603 * See the License for the specific language governing permissions
11*4667Smh27603 * and limitations under the License.
12*4667Smh27603 *
13*4667Smh27603 * When distributing Covered Code, include this CDDL HEADER in each
14*4667Smh27603 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*4667Smh27603 * If applicable, add the following below this CDDL HEADER, with the
16*4667Smh27603 * fields enclosed by brackets "[]" replaced with your own identifying
17*4667Smh27603 * information: Portions Copyright [yyyy] [name of copyright owner]
18*4667Smh27603 *
19*4667Smh27603 * CDDL HEADER END
20*4667Smh27603 */
21*4667Smh27603 /*
22*4667Smh27603 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23*4667Smh27603 * Use is subject to license terms.
24*4667Smh27603 */
25*4667Smh27603
26*4667Smh27603 #pragma ident "%Z%%M% %I% %E% SMI"
27*4667Smh27603
28*4667Smh27603 /*
29*4667Smh27603 * Platform Power Management master pseudo driver platform support.
30*4667Smh27603 */
31*4667Smh27603
32*4667Smh27603 #include <sys/file.h>
33*4667Smh27603 #include <sys/ddi.h>
34*4667Smh27603 #include <sys/sunddi.h>
35*4667Smh27603 #include <sys/ppmvar.h>
36*4667Smh27603
37*4667Smh27603 /*
38*4667Smh27603 * This flag disables vcore/vid feature by default.
39*4667Smh27603 */
40*4667Smh27603 uint_t ppm_do_vcore = 0;
41*4667Smh27603
42*4667Smh27603 /*
43*4667Smh27603 * PPMDC_CPU_NEXT operation
44*4667Smh27603 */
45*4667Smh27603 static int
ppm_cpu_next(ppm_domain_t * domp,int level)46*4667Smh27603 ppm_cpu_next(ppm_domain_t *domp, int level)
47*4667Smh27603 {
48*4667Smh27603 #ifdef DEBUG
49*4667Smh27603 char *str = "ppm_cpu_next";
50*4667Smh27603 #endif
51*4667Smh27603 ppm_dc_t *dc;
52*4667Smh27603 int index = level - 1;
53*4667Smh27603 int ret = 0;
54*4667Smh27603
55*4667Smh27603 dc = ppm_lookup_dc(domp, PPMDC_CPU_NEXT);
56*4667Smh27603 for (; dc && (dc->cmd == PPMDC_CPU_NEXT); dc = dc->next) {
57*4667Smh27603 switch (dc->method) {
58*4667Smh27603 case PPMDC_CPUSPEEDKIO:
59*4667Smh27603 ret = ldi_ioctl(dc->lh, dc->m_un.cpu.iowr,
60*4667Smh27603 (intptr_t)index, FWRITE | FKIOCTL, kcred, NULL);
61*4667Smh27603 if (ret)
62*4667Smh27603 return (ret);
63*4667Smh27603 break;
64*4667Smh27603
65*4667Smh27603 default:
66*4667Smh27603 PPMD(D_CPU, ("%s: unsupported method(0x%x)\n",
67*4667Smh27603 str, dc->method))
68*4667Smh27603 return (-1);
69*4667Smh27603 }
70*4667Smh27603 }
71*4667Smh27603 return (ret);
72*4667Smh27603 }
73*4667Smh27603
74*4667Smh27603 /*
75*4667Smh27603 * PPMDC_PRE_CHNG operation
76*4667Smh27603 */
77*4667Smh27603 static int
ppm_cpu_pre_chng(ppm_domain_t * domp,int oldl,int speedup)78*4667Smh27603 ppm_cpu_pre_chng(ppm_domain_t *domp, int oldl, int speedup)
79*4667Smh27603 {
80*4667Smh27603 #ifdef DEBUG
81*4667Smh27603 char *str = "ppm_cpu_pre_chng";
82*4667Smh27603 #endif
83*4667Smh27603 ppm_dc_t *dc;
84*4667Smh27603 int lowest;
85*4667Smh27603 int ret = 0;
86*4667Smh27603
87*4667Smh27603 dc = ppm_lookup_dc(domp, PPMDC_PRE_CHNG);
88*4667Smh27603 for (; dc && (dc->cmd == PPMDC_PRE_CHNG); dc = dc->next) {
89*4667Smh27603
90*4667Smh27603 switch (dc->method) {
91*4667Smh27603 case PPMDC_VCORE:
92*4667Smh27603 lowest = domp->devlist->lowest;
93*4667Smh27603 if ((oldl != lowest) || (speedup != 1))
94*4667Smh27603 break;
95*4667Smh27603
96*4667Smh27603 /* raise core voltage */
97*4667Smh27603 if (ppm_do_vcore > 0) {
98*4667Smh27603 ret = ldi_ioctl(dc->lh,
99*4667Smh27603 dc->m_un.cpu.iowr,
100*4667Smh27603 (intptr_t)&dc->m_un.cpu.val,
101*4667Smh27603 FWRITE | FKIOCTL, kcred, NULL);
102*4667Smh27603 if (ret != 0)
103*4667Smh27603 return (ret);
104*4667Smh27603 if (dc->m_un.cpu.delay > 0)
105*4667Smh27603 drv_usecwait(dc->m_un.cpu.delay);
106*4667Smh27603 }
107*4667Smh27603 break;
108*4667Smh27603
109*4667Smh27603 default:
110*4667Smh27603 PPMD(D_CPU, ("%s: unsupported method(0x%x)\n",
111*4667Smh27603 str, dc->method))
112*4667Smh27603 return (-1);
113*4667Smh27603 }
114*4667Smh27603 }
115*4667Smh27603
116*4667Smh27603 return (ret);
117*4667Smh27603 }
118*4667Smh27603
119*4667Smh27603 /*
120*4667Smh27603 * PPMDC_CPU_GO operation
121*4667Smh27603 */
122*4667Smh27603 /* ARGSUSED */
123*4667Smh27603 static int
ppm_cpu_go(ppm_domain_t * domp,int level)124*4667Smh27603 ppm_cpu_go(ppm_domain_t *domp, int level)
125*4667Smh27603 {
126*4667Smh27603 ppm_dc_t *dc;
127*4667Smh27603 int ret = 0;
128*4667Smh27603
129*4667Smh27603 dc = ppm_lookup_dc(domp, PPMDC_CPU_GO);
130*4667Smh27603 if (dc == NULL) {
131*4667Smh27603 return (ret);
132*4667Smh27603 }
133*4667Smh27603 switch (dc->method) {
134*4667Smh27603 case PPMDC_KIO:
135*4667Smh27603 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
136*4667Smh27603 (intptr_t)dc->m_un.kio.val, FWRITE | FKIOCTL,
137*4667Smh27603 kcred, NULL);
138*4667Smh27603 break;
139*4667Smh27603 default:
140*4667Smh27603 return (-1);
141*4667Smh27603 }
142*4667Smh27603
143*4667Smh27603 return (ret);
144*4667Smh27603 }
145*4667Smh27603
146*4667Smh27603 /*
147*4667Smh27603 * PPMDC_POST_CHNG operation
148*4667Smh27603 */
149*4667Smh27603 static int
ppm_cpu_post_chng(ppm_domain_t * domp,int newl,int speedup)150*4667Smh27603 ppm_cpu_post_chng(ppm_domain_t *domp, int newl, int speedup)
151*4667Smh27603 {
152*4667Smh27603 #ifdef DEBUG
153*4667Smh27603 char *str = "ppm_cpu_post_chng";
154*4667Smh27603 #endif
155*4667Smh27603 ppm_dc_t *dc;
156*4667Smh27603 int lowest;
157*4667Smh27603 int ret = 0;
158*4667Smh27603
159*4667Smh27603 dc = ppm_lookup_dc(domp, PPMDC_POST_CHNG);
160*4667Smh27603 for (; dc && (dc->cmd == PPMDC_POST_CHNG); dc = dc->next) {
161*4667Smh27603
162*4667Smh27603 switch (dc->method) {
163*4667Smh27603 case PPMDC_VCORE:
164*4667Smh27603 lowest = domp->devlist->lowest;
165*4667Smh27603 if ((newl != lowest) || (speedup != 0))
166*4667Smh27603 break;
167*4667Smh27603
168*4667Smh27603 /* lower core voltage */
169*4667Smh27603 if (ppm_do_vcore > 0) {
170*4667Smh27603 ret = ldi_ioctl(dc->lh,
171*4667Smh27603 dc->m_un.cpu.iowr,
172*4667Smh27603 (intptr_t)&dc->m_un.cpu.val,
173*4667Smh27603 FWRITE | FKIOCTL, kcred, NULL);
174*4667Smh27603 if (ret != 0)
175*4667Smh27603 return (ret);
176*4667Smh27603 if (dc->m_un.cpu.delay > 0)
177*4667Smh27603 drv_usecwait(dc->m_un.cpu.delay);
178*4667Smh27603 }
179*4667Smh27603 break;
180*4667Smh27603
181*4667Smh27603 default:
182*4667Smh27603 PPMD(D_CPU, ("%s: unsupported method(0x%x)\n",
183*4667Smh27603 str, dc->method))
184*4667Smh27603 return (-1);
185*4667Smh27603 }
186*4667Smh27603 }
187*4667Smh27603 return (ret);
188*4667Smh27603 }
189*4667Smh27603
190*4667Smh27603 /*
191*4667Smh27603 * The effective cpu estar model is: program all cpus to be ready to go
192*4667Smh27603 * the same next(or new) speed level, program all other system bus resident
193*4667Smh27603 * devices to the same next speed level. At last, pull the trigger to
194*4667Smh27603 * initiate the speed change for all system bus resident devices
195*4667Smh27603 * simultaneously.
196*4667Smh27603 *
197*4667Smh27603 * On Excalibur, the Safari bus resident devices are Cheetah/Cheetah+ and
198*4667Smh27603 * Schizo. On Enchilada, the JBus resident devides are Jalapeno(s) and
199*4667Smh27603 * Tomatillo(s).
200*4667Smh27603 */
201*4667Smh27603 int
ppm_change_cpu_power(ppm_dev_t * ppmd,int newlevel)202*4667Smh27603 ppm_change_cpu_power(ppm_dev_t *ppmd, int newlevel)
203*4667Smh27603 {
204*4667Smh27603 #ifdef DEBUG
205*4667Smh27603 char *str = "ppm_change_cpu_power";
206*4667Smh27603 #endif
207*4667Smh27603 ppm_unit_t *unitp;
208*4667Smh27603 ppm_domain_t *domp;
209*4667Smh27603 ppm_dev_t *cpup;
210*4667Smh27603 dev_info_t *dip;
211*4667Smh27603 int level, oldlevel;
212*4667Smh27603 int speedup, incr, lowest, highest;
213*4667Smh27603 char *chstr;
214*4667Smh27603 int ret;
215*4667Smh27603
216*4667Smh27603 unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
217*4667Smh27603 ASSERT(unitp);
218*4667Smh27603 domp = ppmd->domp;
219*4667Smh27603 cpup = domp->devlist;
220*4667Smh27603 lowest = cpup->lowest;
221*4667Smh27603 highest = cpup->highest;
222*4667Smh27603
223*4667Smh27603 /*
224*4667Smh27603 * Not all cpus may have transitioned to a known level by this time
225*4667Smh27603 */
226*4667Smh27603 oldlevel = (cpup->level == PM_LEVEL_UNKNOWN) ? highest : cpup->level;
227*4667Smh27603 dip = cpup->dip;
228*4667Smh27603 ASSERT(dip);
229*4667Smh27603
230*4667Smh27603 PPMD(D_CPU, ("%s: old %d, new %d, highest %d, lowest %d\n",
231*4667Smh27603 str, oldlevel, newlevel, highest, lowest))
232*4667Smh27603
233*4667Smh27603 if (newlevel > oldlevel) {
234*4667Smh27603 chstr = "UP";
235*4667Smh27603 speedup = 1;
236*4667Smh27603 incr = 1;
237*4667Smh27603 } else if (newlevel < oldlevel) {
238*4667Smh27603 chstr = "DOWN";
239*4667Smh27603 speedup = 0;
240*4667Smh27603 incr = -1;
241*4667Smh27603 } else
242*4667Smh27603 return (DDI_SUCCESS);
243*4667Smh27603
244*4667Smh27603 /*
245*4667Smh27603 * This loop will execute 1x or 2x depending on
246*4667Smh27603 * number of times we need to change clock rates
247*4667Smh27603 */
248*4667Smh27603 for (level = oldlevel+incr; level != newlevel+incr; level += incr) {
249*4667Smh27603 /* bring each cpu to next level */
250*4667Smh27603 for (; cpup; cpup = cpup->next) {
251*4667Smh27603 if (cpup->level == level)
252*4667Smh27603 continue;
253*4667Smh27603
254*4667Smh27603 ret = pm_power(cpup->dip, 0, level);
255*4667Smh27603 PPMD(D_CPU, ("%s: \"%s\", %s to level %d, ret %d\n",
256*4667Smh27603 str, cpup->path, chstr, level, ret))
257*4667Smh27603 if (ret == DDI_SUCCESS) {
258*4667Smh27603 cpup->level = level;
259*4667Smh27603 cpup->rplvl = PM_LEVEL_UNKNOWN;
260*4667Smh27603 continue;
261*4667Smh27603 }
262*4667Smh27603
263*4667Smh27603 /*
264*4667Smh27603 * if the driver was unable to lower cpu speed,
265*4667Smh27603 * the cpu probably got busy; set the previous
266*4667Smh27603 * cpus back to the original level
267*4667Smh27603 */
268*4667Smh27603 if (speedup == 0)
269*4667Smh27603 ret = ppm_revert_cpu_power(cpup, level - incr);
270*4667Smh27603 return (ret);
271*4667Smh27603 }
272*4667Smh27603 cpup = domp->devlist;
273*4667Smh27603
274*4667Smh27603 /*
275*4667Smh27603 * set bus resident devices at next speed level
276*4667Smh27603 */
277*4667Smh27603 ret = ppm_cpu_next(domp, level);
278*4667Smh27603 if (ret != 0) {
279*4667Smh27603 (void) ppm_revert_cpu_power(cpup, level - incr);
280*4667Smh27603 return (ret);
281*4667Smh27603 }
282*4667Smh27603
283*4667Smh27603 /*
284*4667Smh27603 * platform dependent various operations before
285*4667Smh27603 * initiating cpu speed change
286*4667Smh27603 */
287*4667Smh27603 ret = ppm_cpu_pre_chng(domp, level - incr, speedup);
288*4667Smh27603 if (ret != 0) {
289*4667Smh27603 (void) ppm_revert_cpu_power(cpup, level - incr);
290*4667Smh27603 (void) ppm_cpu_next(domp, level - incr);
291*4667Smh27603 return (ret);
292*4667Smh27603 }
293*4667Smh27603
294*4667Smh27603 /*
295*4667Smh27603 * the following 1us delay is actually required for us3i only.
296*4667Smh27603 * on us3i system, entering estar mode from full requires
297*4667Smh27603 * to set mcu to single fsm state followed by 1us delay
298*4667Smh27603 * before trigger actual transition. The mcu part is
299*4667Smh27603 * handled in us_drv, the delay is here.
300*4667Smh27603 */
301*4667Smh27603 if ((oldlevel == highest) && (speedup == 0))
302*4667Smh27603 drv_usecwait(1);
303*4667Smh27603
304*4667Smh27603 /*
305*4667Smh27603 * initiate cpu speed change
306*4667Smh27603 */
307*4667Smh27603 ret = ppm_cpu_go(domp, level);
308*4667Smh27603 if (ret != 0) {
309*4667Smh27603 (void) ppm_revert_cpu_power(cpup, level - incr);
310*4667Smh27603 (void) ppm_cpu_next(domp, level - incr);
311*4667Smh27603 return (ret);
312*4667Smh27603 }
313*4667Smh27603
314*4667Smh27603 /*
315*4667Smh27603 * platform dependent operations post cpu speed change
316*4667Smh27603 */
317*4667Smh27603 ret = ppm_cpu_post_chng(domp, level, speedup);
318*4667Smh27603 if (ret != 0)
319*4667Smh27603 return (ret);
320*4667Smh27603
321*4667Smh27603 } /* end of looping each level */
322*4667Smh27603
323*4667Smh27603 return (DDI_SUCCESS);
324*4667Smh27603 }
325*4667Smh27603
326*4667Smh27603 /*
327*4667Smh27603 * This handles the power-on case where cpu power level is
328*4667Smh27603 * PM_LEVEL_UNKNOWN. Per agreement with OBP, cpus always
329*4667Smh27603 * boot up at full speed. In fact, we must not making calls
330*4667Smh27603 * into tomtppm or schppm to trigger cpu speed change to a
331*4667Smh27603 * different level at early boot time since some cpu may not
332*4667Smh27603 * be ready, causing xc_one() to fail silently.
333*4667Smh27603 *
334*4667Smh27603 * Here we simply call pm_power() to get the power level updated
335*4667Smh27603 * in pm and ppm. Had xc_one() failed silently inside us_power()
336*4667Smh27603 * at this time we're unaffected.
337*4667Smh27603 */
338*4667Smh27603 boolean_t
ppm_manage_early_cpus(dev_info_t * dip,int new,int * result)339*4667Smh27603 ppm_manage_early_cpus(dev_info_t *dip, int new, int *result)
340*4667Smh27603 {
341*4667Smh27603 ppm_dev_t *ppmd = PPM_GET_PRIVATE(dip);
342*4667Smh27603 int ret;
343*4667Smh27603
344*4667Smh27603 if (ppmd->level == PM_LEVEL_UNKNOWN && new == ppmd->highest) {
345*4667Smh27603 ret = pm_power(dip, 0, new);
346*4667Smh27603 if (ret != DDI_SUCCESS) {
347*4667Smh27603 PPMD(D_CPU, ("ppm_manage_early_cpus: pm_power() "
348*4667Smh27603 "failed to change power level to %d", new))
349*4667Smh27603 } else {
350*4667Smh27603 ppmd->level = new;
351*4667Smh27603 ppmd->rplvl = PM_LEVEL_UNKNOWN;
352*4667Smh27603 }
353*4667Smh27603 *result = ret;
354*4667Smh27603 return (B_TRUE);
355*4667Smh27603 }
356*4667Smh27603 return (B_FALSE);
357*4667Smh27603 }
358