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