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 /*
32 * Platform Power Management master pseudo driver -
33 * - attaches only when ppm.conf file is present, indicating a
34 * workstation (since Excalibur era ) that is designed to
35 * be MOU-3 EPA compliant and which uses platform-specific
36 * hardware to do so;
37 * - this pseudo driver uses a set of simple satellite
38 * device drivers responsible for accessing platform
39 * specific devices to modify the registers they own.
40 * ppm drivers tells these satellite drivers what to do
41 * according to using command values taken from ppm.conf.
42 */
43 #include <sys/conf.h>
44 #include <sys/stat.h>
45 #include <sys/file.h>
46 #include <sys/types.h>
47 #include <sys/param.h>
48 #include <sys/open.h>
49 #include <sys/callb.h>
50 #include <sys/va_list.h>
51 #include <sys/errno.h>
52 #include <sys/modctl.h>
53 #include <sys/sysmacros.h>
54 #include <sys/ddi_impldefs.h>
55 #include <sys/promif.h>
56 #include <sys/epm.h>
57 #include <sys/sunpm.h>
58 #include <sys/ppmio.h>
59 #include <sys/sunldi.h>
60 #include <sys/ppmvar.h>
61 #include <sys/ddi.h>
62 #include <sys/sunddi.h>
63 #include <sys/ppm_plat.h>
64
65 /*
66 * Note: When pm_power() is called (directly or indirectly) to change the
67 * power level of a device and the call returns failure, DO NOT assume the
68 * level is unchanged. Doublecheck it against ppmd->level.
69 */
70
71 /*
72 * cb_ops
73 */
74 static int ppm_open(dev_t *, int, int, cred_t *);
75 static int ppm_close(dev_t, int, int, cred_t *);
76 static int ppm_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
77
78 static struct cb_ops ppm_cb_ops = {
79 ppm_open, /* open */
80 ppm_close, /* close */
81 nodev, /* strategy */
82 nodev, /* print */
83 nodev, /* dump */
84 nodev, /* read */
85 nodev, /* write */
86 ppm_ioctl, /* ioctl */
87 nodev, /* devmap */
88 nodev, /* mmap */
89 nodev, /* segmap */
90 nochpoll, /* poll */
91 ddi_prop_op, /* prop_op */
92 NULL, /* streamtab */
93 D_MP | D_NEW, /* driver compatibility flag */
94 CB_REV, /* cb_ops revision */
95 nodev, /* async read */
96 nodev /* async write */
97 };
98
99 /*
100 * bus_ops
101 */
102 static int ppm_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *,
103 void *);
104
105 static struct bus_ops ppm_bus_ops = {
106 BUSO_REV, /* busops_rev */
107 0, /* bus_map */
108 0, /* bus_get_intrspec */
109 0, /* bus_add_intrspec */
110 0, /* bus_remove_intrspec */
111 0, /* bus_map_fault */
112 ddi_no_dma_map, /* bus_dma_map */
113 ddi_no_dma_allochdl, /* bus_dma_allochdl */
114 NULL, /* bus_dma_freehdl */
115 NULL, /* bus_dma_bindhdl */
116 NULL, /* bus_dma_unbindhdl */
117 NULL, /* bus_dma_flush */
118 NULL, /* bus_dma_win */
119 NULL, /* bus_dma_ctl */
120 ppm_ctlops, /* bus_ctl */
121 0, /* bus_prop_op */
122 0, /* bus_get_eventcookie */
123 0, /* bus_add_eventcall */
124 0, /* bus_remove_eventcall */
125 0, /* bus_post_event */
126 0 /* bus_intr_ctl */
127 };
128
129 /*
130 * dev_ops
131 */
132 static int ppm_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
133 static int ppm_attach(dev_info_t *, ddi_attach_cmd_t);
134 static int ppm_detach(dev_info_t *, ddi_detach_cmd_t);
135
136 static struct dev_ops ppm_ops = {
137 DEVO_REV, /* devo_rev */
138 0, /* refcnt */
139 ppm_getinfo, /* info */
140 nulldev, /* identify */
141 nulldev, /* probe */
142 ppm_attach, /* attach */
143 ppm_detach, /* detach */
144 nodev, /* reset */
145 &ppm_cb_ops, /* cb_ops */
146 &ppm_bus_ops, /* bus_ops */
147 nulldev, /* power */
148 ddi_quiesce_not_needed, /* quiesce */
149 };
150
151 extern struct mod_ops mod_driverops;
152
153 static struct modldrv modldrv = {
154 &mod_driverops,
155 "platform pm driver",
156 &ppm_ops
157 };
158
159 static struct modlinkage modlinkage = {
160 MODREV_1,
161 &modldrv,
162 NULL
163 };
164
165 /*
166 * Global data structure and variables
167 */
168 int ppm_inst = -1;
169 void *ppm_statep;
170 ppm_domain_t *ppm_domain_p;
171 callb_id_t *ppm_cprcb_id;
172 static kmutex_t ppm_cpr_window_lock; /* guard ppm_cpr_window_flag */
173 static boolean_t ppm_cpr_window_flag; /* set indicating chpt-resume period */
174
175 /* LED actions */
176 #define PPM_LED_SOLIDON 0
177 #define PPM_LED_BLINKING 1
178
179 /*
180 * Debug
181 */
182 #ifdef DEBUG
183 uint_t ppm_debug = 0;
184 #endif
185
186 /*
187 * Local function prototypes and data
188 */
189 static boolean_t ppm_cpr_callb(void *, int);
190 static int ppm_fetset(ppm_domain_t *, uint8_t);
191 static int ppm_fetget(ppm_domain_t *, uint8_t *);
192 static int ppm_gpioset(ppm_domain_t *, int);
193 static int ppm_manage_cpus(dev_info_t *, power_req_t *, int *);
194 static int ppm_manage_pci(dev_info_t *, power_req_t *, int *);
195 static int ppm_manage_pcie(dev_info_t *, power_req_t *, int *);
196 static int ppm_manage_fet(dev_info_t *, power_req_t *, int *);
197 static void ppm_manage_led(int);
198 static void ppm_set_led(ppm_domain_t *, int);
199 static void ppm_blink_led(void *);
200 static void ppm_svc_resume_ctlop(dev_info_t *, power_req_t *);
201 static int ppm_set_level(ppm_dev_t *, int, int, boolean_t);
202 static int ppm_change_power_level(ppm_dev_t *, int, int);
203 static int ppm_record_level_change(ppm_dev_t *, int, int);
204 static int ppm_switch_clock(ppm_domain_t *, int);
205 static int ppm_pcie_pwr(ppm_domain_t *, int);
206 static int ppm_power_up_domain(dev_info_t *dip);
207 static int ppm_power_down_domain(dev_info_t *dip);
208
209 int
_init(void)210 _init(void)
211 {
212 if (ddi_soft_state_init(
213 &ppm_statep, sizeof (ppm_unit_t), 1) != DDI_SUCCESS) {
214 PPMD(D_INIT, ("ppm: soft state init\n"))
215 return (DDI_FAILURE);
216 }
217
218 if (mod_install(&modlinkage) != DDI_SUCCESS) {
219 ddi_soft_state_fini(&ppm_statep);
220 return (DDI_FAILURE);
221 }
222 return (DDI_SUCCESS);
223 }
224
225
226 int
_fini(void)227 _fini(void)
228 {
229 int error;
230
231 if ((error = mod_remove(&modlinkage)) == DDI_SUCCESS)
232 ddi_soft_state_fini(&ppm_statep);
233
234 return (error);
235 }
236
237
238 int
_info(struct modinfo * modinfop)239 _info(struct modinfo *modinfop)
240 {
241 return (mod_info(&modlinkage, modinfop));
242 }
243
244
245 /* ARGSUSED */
246 int
ppm_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** resultp)247 ppm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
248 {
249 struct ppm_unit *unitp;
250 dev_t dev;
251 int instance;
252 int rval;
253
254 if (ppm_inst == -1)
255 return (DDI_FAILURE);
256
257 switch (cmd) {
258 case DDI_INFO_DEVT2DEVINFO:
259 if (unitp = ddi_get_soft_state(ppm_statep, (dev_t)arg)) {
260 *resultp = unitp->dip;
261 rval = DDI_SUCCESS;
262 } else
263 rval = DDI_FAILURE;
264
265 return (rval);
266
267 case DDI_INFO_DEVT2INSTANCE:
268 dev = (dev_t)arg;
269 instance = getminor(dev);
270 *resultp = (void *)(uintptr_t)instance;
271 return (DDI_SUCCESS);
272
273 default:
274 return (DDI_FAILURE);
275 }
276 }
277
278
279 /*
280 * attach(9E)
281 */
282 static int
ppm_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)283 ppm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
284 {
285 ppm_unit_t *unitp;
286 int ret;
287 #ifdef DEBUG
288 char *str = "ppm_attach";
289 #endif
290
291
292 switch (cmd) {
293 case DDI_ATTACH:
294 PPMD(D_ATTACH, ("%s: attaching ...\n", str))
295 break;
296
297 case DDI_RESUME:
298 PPMD(D_ATTACH, ("%s: Resuming ...\n", str))
299 unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
300 mutex_enter(&unitp->lock);
301 unitp->states &= ~PPM_STATE_SUSPENDED;
302 mutex_exit(&unitp->lock);
303 return (DDI_SUCCESS);
304
305 default:
306 cmn_err(CE_WARN, "ppm_attach: unknown command %d, dip(0x%p)",
307 cmd, (void *)dip);
308 return (DDI_FAILURE);
309 }
310
311 if (ppm_inst != -1) {
312 PPMD(D_ATTACH, ("%s: Already attached !", str))
313 return (DDI_FAILURE);
314 }
315
316 ppm_inst = ddi_get_instance(dip);
317 if (ddi_soft_state_zalloc(ppm_statep, ppm_inst) != DDI_SUCCESS) {
318 PPMD(D_ATTACH, ("%s: soft states alloc error!\n", str))
319 return (DDI_FAILURE);
320 }
321 unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
322
323 ret = ddi_create_minor_node(dip, "ppm", S_IFCHR, ppm_inst,
324 "ddi_ppm", 0);
325 if (ret != DDI_SUCCESS) {
326 PPMD(D_ATTACH, ("%s: can't create minor node!\n", str))
327 goto fail1;
328 }
329
330 unitp->dip = dip;
331 mutex_init(&unitp->lock, NULL, MUTEX_DRIVER, NULL);
332
333 /*
334 * read ppm.conf, construct ppm_domain data structure and
335 * their sub data structure.
336 */
337 if ((ret = ppm_create_db(dip)) != DDI_SUCCESS)
338 goto fail2;
339
340 /*
341 * walk down ppm domain control from each domain, initialize
342 * domain control orthogonal function call handle
343 */
344 ppm_init_cb(dip);
345
346 if ((ret = pm_register_ppm(ppm_claim_dev, dip)) != DDI_SUCCESS) {
347 cmn_err(CE_WARN, "ppm_attach: can't register ppm handler!");
348 goto fail2;
349 }
350
351 mutex_init(&ppm_cpr_window_lock, NULL, MUTEX_DRIVER, NULL);
352 ppm_cpr_window_flag = B_FALSE;
353 ppm_cprcb_id = callb_add(ppm_cpr_callb, (void *)NULL,
354 CB_CL_CPR_PM, "ppm_cpr");
355
356 #if defined(__x86)
357 /*
358 * Register callback so that once CPUs have been added to
359 * the device tree, ppm CPU domains can be allocated using ACPI
360 * data.
361 */
362 cpupm_ppm_alloc_pstate_domains = ppm_alloc_pstate_domains;
363 cpupm_ppm_free_pstate_domains = ppm_free_pstate_domains;
364
365 /*
366 * Register callback so that whenever max speed throttle requests
367 * are received, ppm can redefine the high power level for
368 * all CPUs in the domain.
369 */
370 cpupm_redefine_topspeed = ppm_redefine_topspeed;
371 #endif
372
373 ddi_report_dev(dip);
374 return (DDI_SUCCESS);
375
376 fail2:
377 ddi_remove_minor_node(dip, "ddi_ppm");
378 mutex_destroy(&unitp->lock);
379 fail1:
380 ddi_soft_state_free(ppm_statep, ppm_inst);
381 ppm_inst = -1;
382 return (DDI_FAILURE);
383 }
384
385
386 /* ARGSUSED */
387 static int
ppm_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)388 ppm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
389 {
390 ppm_unit_t *unitp;
391 #ifdef DEBUG
392 char *str = "ppm_detach";
393 #endif
394
395 switch (cmd) {
396 case DDI_DETACH:
397 PPMD(D_DETACH, ("%s: detach not allowed.\n", str))
398 return (DDI_FAILURE);
399
400 case DDI_SUSPEND:
401 PPMD(D_DETACH, ("%s: suspending ...\n", str))
402 unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
403 mutex_enter(&unitp->lock);
404 unitp->states |= PPM_STATE_SUSPENDED;
405 mutex_exit(&unitp->lock);
406
407 /*
408 * Suspend requires that timeout callouts to be canceled.
409 * Turning off the LED blinking will cancel the timeout.
410 */
411 ppm_manage_led(PPM_LED_SOLIDON);
412 return (DDI_SUCCESS);
413
414 default:
415 cmn_err(CE_WARN, "ppm_detach: unsupported command %d, dip(%p)",
416 cmd, (void *)dip);
417 return (DDI_FAILURE);
418 }
419 }
420
421
422 /* ARGSUSED */
423 int
ppm_open(dev_t * devp,int flag,int otyp,cred_t * cred_p)424 ppm_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
425 {
426 if (otyp != OTYP_CHR)
427 return (EINVAL);
428 PPMD(D_OPEN, ("ppm_open: devp 0x%p, flag 0x%x, otyp %d\n",
429 (void *)devp, flag, otyp))
430 return (0);
431 }
432
433
434 /* ARGSUSED */
435 int
ppm_close(dev_t dev,int flag,int otyp,cred_t * credp)436 ppm_close(dev_t dev, int flag, int otyp, cred_t *credp)
437 {
438 PPMD(D_CLOSE, ("ppm_close: dev 0x%lx, flag 0x%x, otyp %d\n",
439 dev, flag, otyp))
440 return (0);
441 }
442
443
444 /* ARGSUSED */
445 int
ppm_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * cred_p,int * rval_p)446 ppm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred_p,
447 int *rval_p)
448 {
449 #ifdef DEBUG
450 char *str = "ppm_ioctl";
451 #endif
452 ppm_domain_t *domp = NULL;
453 uint8_t level, lvl;
454 int ret = 0;
455
456 PPMD(D_IOCTL, ("%s: dev 0x%lx, cmd 0x%x, mode 0x%x\n",
457 str, dev, cmd, mode))
458
459 switch (cmd) {
460 case PPMGET_DPWR:
461 {
462 STRUCT_DECL(ppm_dpwr, dpwr);
463 struct ppm_unit *unitp;
464 char *domain;
465
466 STRUCT_INIT(dpwr, mode);
467 ret = ddi_copyin((caddr_t)arg, STRUCT_BUF(dpwr),
468 STRUCT_SIZE(dpwr), mode);
469 if (ret != 0)
470 return (EFAULT);
471
472 /* copyin domain name */
473 domain = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
474 ret = copyinstr(
475 STRUCT_FGETP(dpwr, domain), domain, MAXNAMELEN, NULL);
476 if (ret != 0) {
477 PPMD(D_IOCTL, ("%s: can't copyin domain, line(%d)\n",
478 str, __LINE__))
479 ret = EFAULT;
480 goto err_dpwr;
481 }
482
483 /* locate domain */
484 if ((domp = ppm_lookup_domain(domain)) == NULL) {
485 PPMD(D_IOCTL, ("%s: no such domain %s\n", str, domain))
486 ret = ENODEV;
487 goto err_dpwr;
488 }
489
490 switch (domp->model) {
491 case PPMD_FET: /* report power fet ON or OFF */
492 if ((ret = ppm_fetget(domp, &lvl)) != 0) {
493 ret = EIO;
494 goto err_dpwr;
495 }
496 level = (lvl == PPMD_ON) ?
497 PPMIO_POWER_ON : PPMIO_POWER_OFF;
498 break;
499
500 case PPMD_PCI: /* report pci slot clock ON or OFF */
501 case PPMD_PCI_PROP:
502 case PPMD_PCIE:
503 level = (domp->status == PPMD_ON) ?
504 PPMIO_POWER_ON : PPMIO_POWER_OFF;
505 break;
506
507 case PPMD_LED: /* report LED blinking or solid on */
508
509 unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
510 if (unitp->led_tid == 0)
511 level = PPMIO_LED_SOLIDON;
512 else
513 level = PPMIO_LED_BLINKING;
514 break;
515
516 case PPMD_CPU: /* report cpu speed divisor */
517 level = domp->devlist->level;
518 break;
519
520 default:
521 ret = EINVAL;
522 goto err_dpwr;
523 }
524
525 STRUCT_FSET(dpwr, level, level);
526 ret = ddi_copyout(STRUCT_BUF(dpwr), (caddr_t)arg,
527 STRUCT_SIZE(dpwr), mode);
528 if (ret != 0) {
529 PPMD(D_IOCTL, ("%s: can't copyout, line(%d)\n",
530 str, __LINE__))
531 ret = EFAULT;
532 }
533 err_dpwr:
534 kmem_free(domain, MAXNAMELEN);
535
536 break;
537 }
538
539 case PPMGET_DOMBYDEV:
540 {
541 STRUCT_DECL(ppm_bydev, bydev);
542 char *path = NULL;
543 size_t size, l;
544
545 STRUCT_INIT(bydev, mode);
546 ret = ddi_copyin((caddr_t)arg, STRUCT_BUF(bydev),
547 STRUCT_SIZE(bydev), mode);
548 if (ret != 0)
549 return (EFAULT);
550
551 /* copyin .path */
552 path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
553 ret = copyinstr(
554 STRUCT_FGETP(bydev, path), path, MAXPATHLEN, NULL);
555 if (ret != 0) {
556 PPMD(D_IOCTL, ("%s: can't copyin path, line(%d)\n",
557 str, __LINE__))
558 kmem_free(path, MAXPATHLEN);
559 return (EFAULT);
560 }
561
562 /* so far we have up to one domain for a given device */
563 size = STRUCT_FGET(bydev, size);
564 domp = ppm_get_domain_by_dev(path);
565 kmem_free(path, MAXPATHLEN);
566 if (domp != NULL) {
567 l = strlen(domp->name) + 1;
568 if (l > size) {
569 PPMD(D_IOCTL, ("%s: buffer too small\n", str))
570 return ((size == 0) ? EINVAL : EFAULT);
571 }
572 } else /* no domain found to be associated with given device */
573 return (ENODEV);
574
575 ret = copyoutstr(
576 domp->name, STRUCT_FGETP(bydev, domlist), l, &l);
577 if (ret != 0) {
578 PPMD(D_IOCTL, ("%s: can't copyout domlist, line(%d)"
579 " \n", str, __LINE__))
580 return (EFAULT);
581 }
582
583 break;
584 }
585
586
587 case PPMGET_DEVBYDOM:
588 {
589 STRUCT_DECL(ppm_bydom, bydom);
590 char *domain = NULL;
591 char *devlist = NULL;
592 ppm_dev_t *ppmd;
593 dev_info_t *odip = NULL;
594 char *s, *d;
595 size_t size, l;
596
597 STRUCT_INIT(bydom, mode);
598 ret = ddi_copyin((caddr_t)arg, STRUCT_BUF(bydom),
599 STRUCT_SIZE(bydom), mode);
600 if (ret != 0)
601 return (EFAULT);
602
603 /* copyin .domain */
604 domain = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
605 ret = copyinstr(STRUCT_FGETP(bydom, domain), domain,
606 MAXNAMELEN, NULL);
607 if (ret != 0) {
608 PPMD(D_IOCTL, ("%s: can't copyin domain, line(%d)\n",
609 str, __LINE__))
610 ret = EFAULT;
611 goto err_bydom;
612 }
613
614 /* locate domain */
615 if ((domp = ppm_lookup_domain(domain)) == NULL) {
616 ret = ENODEV;
617 goto err_bydom;
618 }
619
620 l = 0;
621 if ((size = STRUCT_FGET(bydom, size)) == 0)
622 ret = EINVAL;
623 else
624 if ((d = devlist = kmem_zalloc(size, KM_SLEEP)) == NULL)
625 ret = EFAULT;
626 if (ret != 0)
627 goto err_bydom;
628
629 for (ppmd = domp->devlist; ppmd;
630 odip = ppmd->dip, ppmd = ppmd->next) {
631
632 if (ppmd->dip == odip)
633 continue;
634 if (ppmd != domp->devlist)
635 *d++ = ' ';
636
637 l += strlen(ppmd->path) + 1;
638 if (l > size) {
639 PPMD(D_IOCTL, ("%s: buffer overflow\n", str))
640 ret = EFAULT;
641 goto err_bydom;
642 }
643
644 for (s = ppmd->path; *s != 0; )
645 *d++ = *s++;
646 }
647 *d = 0;
648
649 if (*devlist == 0)
650 goto err_bydom;
651
652 ret = copyoutstr(
653 devlist, STRUCT_FGETP(bydom, devlist), l, &l);
654 if (ret != 0) {
655 PPMD(D_IOCTL, ("%s: can't copyout devlist, line(%d)"
656 " \n", str, __LINE__))
657 ret = EFAULT;
658 }
659
660 err_bydom:
661 if (devlist)
662 kmem_free(devlist, size);
663 if (domain)
664 kmem_free(domain, MAXNAMELEN);
665
666 break;
667 }
668
669 #if defined(__x86)
670 /*
671 * Note that these two ioctls exist for test purposes only.
672 * Unfortunately, there really isn't any other good way of
673 * unit testing the dynamic redefinition of the top speed as it
674 * usually occurs due to environmental conditions.
675 */
676 case PPMGET_NORMAL:
677 case PPMSET_NORMAL:
678 {
679 STRUCT_DECL(ppm_norm, norm);
680 char *path = NULL;
681 struct pm_component *dcomps;
682 struct pm_comp *pm_comp;
683 ppm_dev_t *ppmd;
684 int i;
685
686 STRUCT_INIT(norm, mode);
687 ret = ddi_copyin((caddr_t)arg, STRUCT_BUF(norm),
688 STRUCT_SIZE(norm), mode);
689 if (ret != 0)
690 return (EFAULT);
691
692 /* copyin .path */
693 path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
694 ret = copyinstr(
695 STRUCT_FGETP(norm, path), path, MAXPATHLEN, NULL);
696 if (ret != 0) {
697 PPMD(D_IOCTL, ("%s: can't copyin path, line(%d)\n",
698 str, __LINE__))
699 kmem_free(path, MAXPATHLEN);
700 return (EFAULT);
701 }
702
703 domp = ppm_get_domain_by_dev(path);
704 kmem_free(path, MAXPATHLEN);
705
706 if (domp == NULL)
707 return (ENODEV);
708
709 ppmd = domp->devlist;
710 if (cmd == PPMSET_NORMAL) {
711 if (domp->model != PPMD_CPU)
712 return (EINVAL);
713 level = STRUCT_FGET(norm, norm);
714 dcomps = DEVI(ppmd->dip)->devi_pm_components;
715 pm_comp = &dcomps[ppmd->cmpt].pmc_comp;
716 for (i = pm_comp->pmc_numlevels; i > 0; i--) {
717 if (pm_comp->pmc_lvals[i-1] == level)
718 break;
719 }
720 if (i == 0)
721 return (EINVAL);
722
723 ppm_set_topspeed(ppmd, pm_comp->pmc_numlevels - i);
724 }
725
726 level = pm_get_normal_power(ppmd->dip, 0);
727
728 STRUCT_FSET(norm, norm, level);
729 ret = ddi_copyout(STRUCT_BUF(norm), (caddr_t)arg,
730 STRUCT_SIZE(norm), mode);
731 if (ret != 0) {
732 PPMD(D_IOCTL, ("%s: can't copyout, line(%d)\n",
733 str, __LINE__))
734 ret = EFAULT;
735 }
736 break;
737 }
738 #endif
739 default:
740 PPMD(D_IOCTL, ("%s: unsupported ioctl command(%d)\n", str, cmd))
741 return (EINVAL);
742 }
743
744 return (ret);
745 }
746
747
748 static int ppm_manage_sx(s3a_t *, int);
749 static int ppm_search_list(pm_searchargs_t *);
750
751 /*
752 * interface between pm framework and ppm driver
753 */
754 /* ARGSUSED */
755 static int
ppm_ctlops(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t ctlop,void * arg,void * result)756 ppm_ctlops(dev_info_t *dip, dev_info_t *rdip,
757 ddi_ctl_enum_t ctlop, void *arg, void *result)
758 {
759 power_req_t *reqp = (power_req_t *)arg;
760 ppm_unit_t *unitp;
761 ppm_domain_t *domp;
762 ppm_dev_t *ppmd;
763 char path[MAXNAMELEN];
764 ppm_owned_t *owned;
765 int mode;
766 int ret = DDI_SUCCESS;
767 int *res = (int *)result;
768 s3a_t s3args;
769
770 #ifdef DEBUG
771 char *str = "ppm_ctlops";
772 int mask = ppm_debug & (D_CTLOPS1 | D_CTLOPS2);
773 char *ctlstr = ppm_get_ctlstr(reqp->request_type, mask);
774 if (mask && ctlstr)
775 PPMD(mask, ("%s: %s, %s\n",
776 str, ddi_binding_name(rdip), ctlstr))
777 #endif
778
779 if (ctlop != DDI_CTLOPS_POWER) {
780 return (DDI_FAILURE);
781 }
782
783 unitp = (ppm_unit_t *)ddi_get_soft_state(ppm_statep, ppm_inst);
784
785 switch (reqp->request_type) {
786
787 /* attempt to blink led if indeed all at lowest */
788 case PMR_PPM_ALL_LOWEST:
789 mode = (reqp->req.ppm_all_lowest_req.mode == PM_ALL_LOWEST);
790 if (!(unitp->states & PPM_STATE_SUSPENDED) && mode)
791 ppm_manage_led(PPM_LED_BLINKING);
792 else
793 ppm_manage_led(PPM_LED_SOLIDON);
794 return (DDI_SUCCESS);
795
796 /* undo the claiming of 'rdip' at attach time */
797 case PMR_PPM_POST_DETACH:
798 ASSERT(reqp->req.ppm_set_power_req.who == rdip);
799 mutex_enter(&unitp->lock);
800 if (reqp->req.ppm_config_req.result != DDI_SUCCESS ||
801 (PPM_GET_PRIVATE(rdip) == NULL)) {
802 mutex_exit(&unitp->lock);
803 return (DDI_FAILURE);
804 }
805 mutex_exit(&unitp->lock);
806 ppm_rem_dev(rdip);
807 return (DDI_SUCCESS);
808
809 /* chance to adjust pwr_cnt if resume is about to power up rdip */
810 case PMR_PPM_PRE_RESUME:
811 ppm_svc_resume_ctlop(rdip, reqp);
812 return (DDI_SUCCESS);
813
814 /*
815 * synchronizing, so that only the owner of the power lock is
816 * permitted to change device and component's power level.
817 */
818 case PMR_PPM_UNLOCK_POWER:
819 case PMR_PPM_TRY_LOCK_POWER:
820 case PMR_PPM_LOCK_POWER:
821 ppmd = PPM_GET_PRIVATE(rdip);
822 if (ppmd)
823 domp = ppmd->domp;
824 else if (reqp->request_type != PMR_PPM_UNLOCK_POWER) {
825 domp = ppm_lookup_dev(rdip);
826 ASSERT(domp);
827 ppmd = ppm_get_dev(rdip, domp);
828 }
829
830 PPMD(D_LOCKS, ("ppm_lock_%s: %s, %s\n",
831 (domp->dflags & PPMD_LOCK_ALL) ? "all" : "one",
832 ppmd->path, ppm_get_ctlstr(reqp->request_type, D_LOCKS)))
833
834 if (domp->dflags & PPMD_LOCK_ALL)
835 ppm_lock_all(domp, reqp, result);
836 else
837 ppm_lock_one(ppmd, reqp, result);
838 return (DDI_SUCCESS);
839
840 case PMR_PPM_POWER_LOCK_OWNER:
841 ASSERT(reqp->req.ppm_power_lock_owner_req.who == rdip);
842 ppmd = PPM_GET_PRIVATE(rdip);
843 if (ppmd)
844 domp = ppmd->domp;
845 else {
846 domp = ppm_lookup_dev(rdip);
847 ASSERT(domp);
848 ppmd = ppm_get_dev(rdip, domp);
849 }
850
851 /*
852 * In case of LOCK_ALL, effective owner of the power lock
853 * is the owner of the domain lock. otherwise, it is the owner
854 * of the power lock.
855 */
856 if (domp->dflags & PPMD_LOCK_ALL)
857 reqp->req.ppm_power_lock_owner_req.owner =
858 mutex_owner(&domp->lock);
859 else {
860 reqp->req.ppm_power_lock_owner_req.owner =
861 DEVI(rdip)->devi_busy_thread;
862 }
863 return (DDI_SUCCESS);
864
865 case PMR_PPM_INIT_CHILD:
866 ASSERT(reqp->req.ppm_lock_power_req.who == rdip);
867 if ((domp = ppm_lookup_dev(rdip)) == NULL)
868 return (DDI_SUCCESS);
869
870 /*
871 * We keep track of power-manageable devices starting with
872 * initialization process. The initializing flag remains
873 * set until it is cleared by ppm_add_dev(). Power management
874 * policy for some domains are affected even during device
875 * initialization. For example, PCI domains should leave
876 * their clock running meanwhile a device in that domain
877 * is initializing.
878 */
879 mutex_enter(&domp->lock);
880 owned = ppm_add_owned(rdip, domp);
881 ASSERT(owned->initializing == 0);
882 owned->initializing = 1;
883
884 if (PPMD_IS_PCI(domp->model) && domp->status == PPMD_OFF) {
885 ret = ppm_switch_clock(domp, PPMD_ON);
886 if (ret == DDI_SUCCESS)
887 domp->dflags |= PPMD_INITCHILD_CLKON;
888 }
889 mutex_exit(&domp->lock);
890 return (ret);
891
892 case PMR_PPM_POST_ATTACH:
893 ASSERT(reqp->req.ppm_config_req.who == rdip);
894 domp = ppm_lookup_dev(rdip);
895 ASSERT(domp);
896 ASSERT(domp->status == PPMD_ON);
897 if (reqp->req.ppm_config_req.result == DDI_SUCCESS) {
898 /*
899 * call ppm_get_dev, which will increment the
900 * domain power count by the right number.
901 * Undo the power count increment, done in PRE_PROBE.
902 */
903 if (PM_GET_PM_INFO(rdip))
904 ppmd = ppm_get_dev(rdip, domp);
905 mutex_enter(&domp->lock);
906 ASSERT(domp->pwr_cnt > 0);
907 domp->pwr_cnt--;
908 mutex_exit(&domp->lock);
909 return (DDI_SUCCESS);
910 }
911
912 ret = ppm_power_down_domain(rdip);
913 /* FALLTHROUGH */
914 case PMR_PPM_UNINIT_CHILD:
915 ASSERT(reqp->req.ppm_lock_power_req.who == rdip);
916 if ((domp = ppm_lookup_dev(rdip)) == NULL)
917 return (DDI_SUCCESS);
918
919 (void) ddi_pathname(rdip, path);
920 mutex_enter(&domp->lock);
921 for (owned = domp->owned; owned; owned = owned->next)
922 if (strcmp(owned->path, path) == 0)
923 break;
924
925 /*
926 * In case we didn't go through a complete attach and detach,
927 * the initializing flag will still be set, so clear it.
928 */
929 if ((owned != NULL) && (owned->initializing))
930 owned->initializing = 0;
931
932 if (PPMD_IS_PCI(domp->model) &&
933 domp->status == PPMD_ON && domp->pwr_cnt == 0 &&
934 (domp->dflags & PPMD_INITCHILD_CLKON) &&
935 ppm_none_else_holds_power(domp)) {
936 ret = ppm_switch_clock(domp, PPMD_OFF);
937 if (ret == DDI_SUCCESS)
938 domp->dflags &= ~PPMD_INITCHILD_CLKON;
939 }
940 mutex_exit(&domp->lock);
941 return (ret);
942
943 /* place holders */
944 case PMR_PPM_UNMANAGE:
945 case PMR_PPM_PRE_DETACH:
946 return (DDI_SUCCESS);
947
948 case PMR_PPM_PRE_PROBE:
949 ASSERT(reqp->req.ppm_config_req.who == rdip);
950 return (ppm_power_up_domain(rdip));
951
952 case PMR_PPM_POST_PROBE:
953 ASSERT(reqp->req.ppm_config_req.who == rdip);
954 if (reqp->req.ppm_config_req.result == DDI_PROBE_SUCCESS ||
955 reqp->req.ppm_config_req.result == DDI_PROBE_DONTCARE)
956 return (DDI_SUCCESS);
957
958 /* Probe failed */
959 PPMD(D_CTLOPS1 | D_CTLOPS2, ("%s: probe failed for %s@%s "
960 "rv %d\n", str, PM_NAME(rdip), PM_ADDR(rdip),
961 reqp->req.ppm_config_req.result))
962 return (ppm_power_down_domain(rdip));
963
964 case PMR_PPM_PRE_ATTACH:
965 ASSERT(reqp->req.ppm_config_req.who == rdip);
966 /* Domain has already been powered up in PRE_PROBE */
967 domp = ppm_lookup_dev(rdip);
968 ASSERT(domp);
969 ASSERT(domp->status == PPMD_ON);
970 return (DDI_SUCCESS);
971
972 /* ppm intercepts power change process to the claimed devices */
973 case PMR_PPM_SET_POWER:
974 case PMR_PPM_POWER_CHANGE_NOTIFY:
975 if ((ppmd = PPM_GET_PRIVATE(rdip)) == NULL) {
976 domp = ppm_lookup_dev(rdip);
977 ASSERT(domp);
978 ppmd = ppm_get_dev(rdip, domp);
979 }
980 switch (ppmd->domp->model) {
981 case PPMD_CPU:
982 return (ppm_manage_cpus(rdip, reqp, result));
983 case PPMD_FET:
984 return (ppm_manage_fet(rdip, reqp, result));
985 case PPMD_PCI:
986 case PPMD_PCI_PROP:
987 return (ppm_manage_pci(rdip, reqp, result));
988 case PPMD_PCIE:
989 return (ppm_manage_pcie(rdip, reqp, result));
990 default:
991 cmn_err(CE_WARN, "ppm_ctlops: domain model %d does"
992 " not support PMR_PPM_SET_POWER ctlop",
993 ppmd->domp->model);
994 return (DDI_FAILURE);
995 }
996
997 case PMR_PPM_ENTER_SX:
998 case PMR_PPM_EXIT_SX:
999 s3args.s3a_state = reqp->req.ppm_power_enter_sx_req.sx_state;
1000 s3args.s3a_test_point =
1001 reqp->req.ppm_power_enter_sx_req.test_point;
1002 s3args.s3a_wakephys = reqp->req.ppm_power_enter_sx_req.wakephys;
1003 s3args.s3a_psr = reqp->req.ppm_power_enter_sx_req.psr;
1004 ret = ppm_manage_sx(&s3args,
1005 reqp->request_type == PMR_PPM_ENTER_SX);
1006 if (ret) {
1007 PPMD(D_CPR, ("ppm_manage_sx returns %d\n", ret))
1008 return (DDI_FAILURE);
1009 } else {
1010 return (DDI_SUCCESS);
1011 }
1012
1013 case PMR_PPM_SEARCH_LIST:
1014 ret = ppm_search_list(reqp->req.ppm_search_list_req.searchlist);
1015 reqp->req.ppm_search_list_req.result = ret;
1016 *res = ret;
1017 if (ret) {
1018 PPMD(D_CPR, ("ppm_search_list returns %d\n", ret))
1019 return (DDI_FAILURE);
1020 } else {
1021 PPMD(D_CPR, ("ppm_search_list returns %d\n", ret))
1022 return (DDI_SUCCESS);
1023 }
1024
1025 default:
1026 cmn_err(CE_WARN, "ppm_ctlops: unrecognized ctlops req(%d)",
1027 reqp->request_type);
1028 return (DDI_FAILURE);
1029 }
1030 }
1031
1032
1033 /*
1034 * Raise the power level of a subrange of cpus. Used when cpu driver
1035 * failed an attempt to lower the power of a cpu (probably because
1036 * it got busy). Need to revert the ones we already changed.
1037 *
1038 * ecpup = the ppm_dev_t for the cpu which failed to lower power
1039 * level = power level to reset prior cpus to
1040 */
1041 int
ppm_revert_cpu_power(ppm_dev_t * ecpup,int level)1042 ppm_revert_cpu_power(ppm_dev_t *ecpup, int level)
1043 {
1044 ppm_dev_t *cpup;
1045 int ret = DDI_SUCCESS;
1046
1047 for (cpup = ecpup->domp->devlist; cpup != ecpup; cpup = cpup->next) {
1048 PPMD(D_CPU, ("ppm_revert_cpu_power: \"%s\", revert to "
1049 "level %d\n", cpup->path, level))
1050
1051 ret = pm_power(cpup->dip, 0, level);
1052 if (ret == DDI_SUCCESS) {
1053 cpup->level = level;
1054 cpup->rplvl = PM_LEVEL_UNKNOWN;
1055 }
1056 }
1057 return (ret);
1058 }
1059
1060
1061 /*
1062 * ppm_manage_cpus - Process a request to change the power level of a cpu.
1063 * If not all cpus want to be at the same level, OR if we are currently
1064 * refusing slowdown requests due to thermal stress, we cache the request.
1065 * Otherwise, set all cpus to the new power level.
1066 */
1067 /* ARGSUSED */
1068 static int
ppm_manage_cpus(dev_info_t * dip,power_req_t * reqp,int * result)1069 ppm_manage_cpus(dev_info_t *dip, power_req_t *reqp, int *result)
1070 {
1071 #ifdef DEBUG
1072 char *str = "ppm_manage_cpus";
1073 #endif
1074 int old, new, ret, kmflag;
1075 ppm_dev_t *ppmd, *cpup;
1076 int change_notify = 0;
1077 pm_ppm_devlist_t *devlist = NULL, *p;
1078 int do_rescan = 0;
1079
1080 *result = DDI_SUCCESS;
1081
1082 switch (reqp->request_type) {
1083 case PMR_PPM_SET_POWER:
1084 break;
1085
1086 case PMR_PPM_POWER_CHANGE_NOTIFY:
1087 change_notify = 1;
1088 break;
1089
1090 default:
1091 return (DDI_FAILURE);
1092 }
1093
1094 ppmd = PPM_GET_PRIVATE(dip);
1095 ASSERT(MUTEX_HELD(&ppmd->domp->lock));
1096 old = reqp->req.ppm_set_power_req.old_level;
1097 new = reqp->req.ppm_set_power_req.new_level;
1098
1099 if (change_notify) {
1100 ppmd->level = new;
1101 ppmd->rplvl = PM_LEVEL_UNKNOWN;
1102
1103 PPMD(D_CPU, ("%s: Notify cpu dip %p power level has changed "
1104 "from %d to %d", str, (void *)dip, old, new))
1105 return (DDI_SUCCESS);
1106 }
1107
1108 if (ppm_manage_early_cpus(dip, new, result))
1109 return (*result);
1110
1111 if (new == ppmd->level) {
1112 PPMD(D_CPU, ("%s: already at power level %d\n", str, new))
1113 return (DDI_SUCCESS);
1114 }
1115
1116 /*
1117 * A request from lower to higher level transition is granted and
1118 * made effective on all cpus. A request from higher to lower must
1119 * be agreed upon by all cpus.
1120 */
1121 ppmd->rplvl = new;
1122 for (cpup = ppmd->domp->devlist; cpup; cpup = cpup->next) {
1123 if (cpup->rplvl == new)
1124 continue;
1125
1126 if (new < old) {
1127 PPMD(D_SOME, ("%s: not all cpus wants to be at new "
1128 "level %d yet.\n", str, new))
1129 return (DDI_SUCCESS);
1130 }
1131
1132 /*
1133 * If a single cpu requests power up, honor the request
1134 * powering up all cpus.
1135 */
1136 if (new > old) {
1137 PPMD(D_SOME, ("%s: powering up device(%s@%s, %p) "
1138 "because of request from dip(%s@%s, %p), "
1139 "need pm_rescan\n", str, PM_NAME(cpup->dip),
1140 PM_ADDR(cpup->dip), (void *)cpup->dip,
1141 PM_NAME(dip), PM_ADDR(dip), (void *)dip))
1142 do_rescan++;
1143 }
1144 }
1145
1146 PPMD(D_SETLVL, ("%s: \"%s\" set power level old %d, new %d \n",
1147 str, ppmd->path, ppmd->level, new))
1148 ret = ppm_change_cpu_power(ppmd, new);
1149 *result = ret;
1150
1151 if (ret == DDI_SUCCESS) {
1152 if (reqp->req.ppm_set_power_req.canblock == PM_CANBLOCK_BLOCK)
1153 kmflag = KM_SLEEP;
1154 else
1155 kmflag = KM_NOSLEEP;
1156
1157 for (cpup = ppmd->domp->devlist; cpup; cpup = cpup->next) {
1158 if (cpup->dip == dip)
1159 continue;
1160
1161 if ((p = kmem_zalloc(sizeof (pm_ppm_devlist_t),
1162 kmflag)) == NULL) {
1163 break;
1164 }
1165 p->ppd_who = cpup->dip;
1166 p->ppd_cmpt = cpup->cmpt;
1167 p->ppd_old_level = old;
1168 p->ppd_new_level = new;
1169 p->ppd_next = devlist;
1170
1171 PPMD(D_SETLVL, ("%s: devlist entry[\"%s\"] %d -> %d\n",
1172 str, cpup->path, old, new))
1173
1174 devlist = p;
1175 }
1176 reqp->req.ppm_set_power_req.cookie = (void *) devlist;
1177
1178 if (do_rescan > 0) {
1179 for (cpup = ppmd->domp->devlist; cpup;
1180 cpup = cpup->next) {
1181 if (cpup->dip == dip)
1182 continue;
1183 pm_rescan(cpup->dip);
1184 }
1185 }
1186 }
1187
1188 return (ret);
1189 }
1190
1191
1192 /*
1193 * ppm_svc_resume_ctlop - this is a small bookkeeping ppm does -
1194 * increments its FET domain power count, in anticipation of that
1195 * the indicated device(dip) would be powered up by its driver as
1196 * a result of cpr resuming.
1197 */
1198 /* ARGSUSED */
1199 static void
ppm_svc_resume_ctlop(dev_info_t * dip,power_req_t * reqp)1200 ppm_svc_resume_ctlop(dev_info_t *dip, power_req_t *reqp)
1201 {
1202 ppm_domain_t *domp;
1203 ppm_dev_t *ppmd;
1204 int powered; /* power up count per dip */
1205
1206 ppmd = PPM_GET_PRIVATE(dip);
1207 if (ppmd == NULL)
1208 return;
1209
1210 /*
1211 * Maintain correct powered count for domain which cares
1212 */
1213 powered = 0;
1214 domp = ppmd->domp;
1215 mutex_enter(&domp->lock);
1216 if ((domp->model == PPMD_FET) || PPMD_IS_PCI(domp->model) ||
1217 (domp->model == PPMD_PCIE)) {
1218 for (ppmd = domp->devlist; ppmd; ppmd = ppmd->next) {
1219 if (ppmd->dip == dip && ppmd->level)
1220 powered++;
1221 }
1222
1223 /*
1224 * All fets and clocks are held on during suspend -
1225 * resume window regardless their domain devices' power
1226 * level.
1227 */
1228 ASSERT(domp->status == PPMD_ON);
1229
1230 /*
1231 * The difference indicates the number of components
1232 * being off prior to suspend operation, that is the
1233 * amount needs to be compensated in order to sync up
1234 * bookkeeping with reality, for PROM reset would have
1235 * brought up all devices.
1236 */
1237 if (powered < PM_NUMCMPTS(dip))
1238 domp->pwr_cnt += PM_NUMCMPTS(dip) - powered;
1239 }
1240 for (ppmd = domp->devlist; ppmd; ppmd = ppmd->next) {
1241 if (ppmd->dip == dip)
1242 ppmd->level = ppmd->rplvl = PM_LEVEL_UNKNOWN;
1243 }
1244 mutex_exit(&domp->lock);
1245 }
1246
1247 #ifdef DEBUG
1248 static int ppmbringup = 0;
1249 #endif
1250
1251 int
ppm_bringup_domains()1252 ppm_bringup_domains()
1253 {
1254 #ifdef DEBUG
1255 char *str = "ppm_bringup_domains";
1256 #endif
1257 ppm_domain_t *domp;
1258 int ret = DDI_SUCCESS;
1259
1260 PPMD(D_CPR, ("%s[%d]: enter\n", str, ++ppmbringup))
1261 for (domp = ppm_domain_p; domp; domp = domp->next) {
1262 if ((!PPMD_IS_PCI(domp->model) && (domp->model != PPMD_FET) &&
1263 (domp->model != PPMD_PCIE)) || (domp->devlist == NULL))
1264 continue;
1265
1266 mutex_enter(&domp->lock);
1267 if (domp->status == PPMD_ON) {
1268 mutex_exit(&domp->lock);
1269 continue;
1270 }
1271 switch (domp->model) {
1272 case PPMD_FET:
1273 ret = ppm_fetset(domp, PPMD_ON);
1274 break;
1275 case PPMD_PCI:
1276 case PPMD_PCI_PROP:
1277 ret = ppm_switch_clock(domp, PPMD_ON);
1278 break;
1279 case PPMD_PCIE:
1280 ret = ppm_pcie_pwr(domp, PPMD_ON);
1281 break;
1282 default:
1283 break;
1284 }
1285 mutex_exit(&domp->lock);
1286 }
1287 PPMD(D_CPR, ("%s[%d]: exit\n", str, ppmbringup))
1288
1289 return (ret);
1290 }
1291
1292 #ifdef DEBUG
1293 static int ppmsyncbp = 0;
1294 #endif
1295
1296 int
ppm_sync_bookkeeping()1297 ppm_sync_bookkeeping()
1298 {
1299 #ifdef DEBUG
1300 char *str = "ppm_sync_bookkeeping";
1301 #endif
1302 ppm_domain_t *domp;
1303 int ret = DDI_SUCCESS;
1304
1305 PPMD(D_CPR, ("%s[%d]: enter\n", str, ++ppmsyncbp))
1306 for (domp = ppm_domain_p; domp; domp = domp->next) {
1307 if ((!PPMD_IS_PCI(domp->model) && (domp->model != PPMD_FET) &&
1308 (domp->model != PPMD_PCIE)) || (domp->devlist == NULL))
1309 continue;
1310
1311 mutex_enter(&domp->lock);
1312 if ((domp->pwr_cnt != 0) || !ppm_none_else_holds_power(domp)) {
1313 mutex_exit(&domp->lock);
1314 continue;
1315 }
1316
1317 /*
1318 * skip NULL .devlist slot, for some may host pci device
1319 * that can not tolerate clock off or not even participate
1320 * in PM.
1321 */
1322 if (domp->devlist == NULL)
1323 continue;
1324
1325 switch (domp->model) {
1326 case PPMD_FET:
1327 ret = ppm_fetset(domp, PPMD_OFF);
1328 break;
1329 case PPMD_PCI:
1330 case PPMD_PCI_PROP:
1331 ret = ppm_switch_clock(domp, PPMD_OFF);
1332 break;
1333 case PPMD_PCIE:
1334 ret = ppm_pcie_pwr(domp, PPMD_OFF);
1335 break;
1336 default:
1337 break;
1338 }
1339 mutex_exit(&domp->lock);
1340 }
1341 PPMD(D_CPR, ("%s[%d]: exit\n", str, ppmsyncbp))
1342
1343 return (ret);
1344 }
1345
1346
1347
1348 /*
1349 * pre-suspend window;
1350 *
1351 * power up every FET and PCI clock that are off;
1352 *
1353 * set ppm_cpr_window global flag to indicate
1354 * that even though all pm_scan requested power transitions
1355 * will be honored as usual but that until we're out
1356 * of this window, no FET or clock will be turned off
1357 * for domains with pwr_cnt decremented down to 0.
1358 * Such is to avoid accessing the orthogonal drivers that own
1359 * the FET and clock registers that may not be resumed yet.
1360 *
1361 * at post-resume window, walk through each FET and PCI domains,
1362 * bring pwr_cnt and domp->status to sense: if pwr-cnt == 0,
1363 * and noinvol check okays, power down the FET or PCI. At last,
1364 * clear the global flag ppm_cpr_window.
1365 *
1366 * ASSERT case 1, during cpr window, checks pwr_cnt against power
1367 * transitions;
1368 * ASSERT case 2, out of cpr window, checks four things:
1369 * pwr_cnt <> power transition in/out of 0
1370 * <> status <> record of noinvol device detached
1371 *
1372 */
1373 /* ARGSUSED */
1374 static boolean_t
ppm_cpr_callb(void * arg,int code)1375 ppm_cpr_callb(void *arg, int code)
1376 {
1377 int ret;
1378
1379 switch (code) {
1380 case CB_CODE_CPR_CHKPT:
1381
1382 /* pre-suspend: start of cpr window */
1383 mutex_enter(&ppm_cpr_window_lock);
1384 ASSERT(ppm_cpr_window_flag == B_FALSE);
1385 ppm_cpr_window_flag = B_TRUE;
1386 mutex_exit(&ppm_cpr_window_lock);
1387
1388 ret = ppm_bringup_domains();
1389
1390 break;
1391
1392 case CB_CODE_CPR_RESUME:
1393
1394 /* post-resume: end of cpr window */
1395 ret = ppm_sync_bookkeeping();
1396
1397 mutex_enter(&ppm_cpr_window_lock);
1398 ASSERT(ppm_cpr_window_flag == B_TRUE);
1399 ppm_cpr_window_flag = B_FALSE;
1400 mutex_exit(&ppm_cpr_window_lock);
1401
1402 break;
1403 }
1404
1405 return (ret == DDI_SUCCESS);
1406 }
1407
1408
1409 /*
1410 * Initialize our private version of real power level
1411 * as well as lowest and highest levels the device supports;
1412 * relate to ppm_add_dev
1413 */
1414 void
ppm_dev_init(ppm_dev_t * ppmd)1415 ppm_dev_init(ppm_dev_t *ppmd)
1416 {
1417 struct pm_component *dcomps;
1418 struct pm_comp *pm_comp;
1419 dev_info_t *dip;
1420 int maxi, i;
1421
1422 ASSERT(MUTEX_HELD(&ppmd->domp->lock));
1423 ppmd->level = PM_LEVEL_UNKNOWN;
1424 ppmd->rplvl = PM_LEVEL_UNKNOWN;
1425
1426 /* increment pwr_cnt per component */
1427 if ((ppmd->domp->model == PPMD_FET) ||
1428 PPMD_IS_PCI(ppmd->domp->model) ||
1429 (ppmd->domp->model == PPMD_PCIE))
1430 ppmd->domp->pwr_cnt++;
1431
1432 dip = ppmd->dip;
1433
1434 /*
1435 * ppm exists to handle power-manageable devices which require
1436 * special handling on the current platform. However, a
1437 * driver for such a device may choose not to support power
1438 * management on a particular load/attach. In this case we
1439 * we create a structure to represent a single-component device
1440 * for which "level" = PM_LEVEL_UNKNOWN and "lowest" = 0
1441 * are effectively constant.
1442 */
1443 if (PM_GET_PM_INFO(dip)) {
1444 dcomps = DEVI(dip)->devi_pm_components;
1445 pm_comp = &dcomps[ppmd->cmpt].pmc_comp;
1446
1447 ppmd->lowest = pm_comp->pmc_lvals[0];
1448 ASSERT(ppmd->lowest >= 0);
1449 maxi = pm_comp->pmc_numlevels - 1;
1450 ppmd->highest = pm_comp->pmc_lvals[maxi];
1451
1452 /*
1453 * If 66mhz PCI device on pci 66mhz bus supports D2 state
1454 * (config reg PMC bit 10 set), ppm could turn off its bus
1455 * clock once it is at D3hot.
1456 */
1457 if (ppmd->domp->dflags & PPMD_PCI66MHZ) {
1458 for (i = 0; i < maxi; i++)
1459 if (pm_comp->pmc_lvals[i] == PM_LEVEL_D2) {
1460 ppmd->flags |= PPMDEV_PCI66_D2;
1461 break;
1462 }
1463 }
1464 }
1465
1466 /*
1467 * If device is in PCI_PROP domain and has exported the
1468 * property listed in ppm.conf, its clock will be turned
1469 * off when all pm'able devices in that domain are at D3.
1470 */
1471 if ((ppmd->domp->model == PPMD_PCI_PROP) &&
1472 (ppmd->domp->propname != NULL) &&
1473 ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1474 ppmd->domp->propname))
1475 ppmd->flags |= PPMDEV_PCI_PROP_CLKPM;
1476 }
1477
1478
1479 /*
1480 * relate to ppm_rem_dev
1481 */
1482 void
ppm_dev_fini(ppm_dev_t * ppmd)1483 ppm_dev_fini(ppm_dev_t *ppmd)
1484 {
1485 ASSERT(MUTEX_HELD(&ppmd->domp->lock));
1486
1487 /* decrement pwr_cnt per component */
1488 if ((ppmd->domp->model == PPMD_FET) ||
1489 PPMD_IS_PCI(ppmd->domp->model) ||
1490 (ppmd->domp->model == PPMD_PCIE))
1491 if (ppmd->level != ppmd->lowest)
1492 ppmd->domp->pwr_cnt--;
1493 }
1494
1495 /*
1496 * Each power fet controls the power of one or more platform
1497 * device(s) within their domain. Hence domain devices' power
1498 * level change has been monitored, such that once all devices
1499 * are powered off, the fet is turned off to save more power.
1500 *
1501 * To power on any domain device, the domain power fet
1502 * needs to be turned on first. always one fet per domain.
1503 */
1504 static int
ppm_manage_fet(dev_info_t * dip,power_req_t * reqp,int * result)1505 ppm_manage_fet(dev_info_t *dip, power_req_t *reqp, int *result)
1506 {
1507 #ifdef DEBUG
1508 char *str = "ppm_manage_fet";
1509 #endif
1510 int (*pwr_func)(ppm_dev_t *, int, int);
1511 int new, old, cmpt;
1512 ppm_dev_t *ppmd;
1513 ppm_domain_t *domp;
1514 int incr = 0;
1515 int dummy_ret;
1516
1517
1518 *result = DDI_SUCCESS;
1519 switch (reqp->request_type) {
1520 case PMR_PPM_SET_POWER:
1521 pwr_func = ppm_change_power_level;
1522 old = reqp->req.ppm_set_power_req.old_level;
1523 new = reqp->req.ppm_set_power_req.new_level;
1524 cmpt = reqp->req.ppm_set_power_req.cmpt;
1525 break;
1526 case PMR_PPM_POWER_CHANGE_NOTIFY:
1527 pwr_func = ppm_record_level_change;
1528 old = reqp->req.ppm_notify_level_req.old_level;
1529 new = reqp->req.ppm_notify_level_req.new_level;
1530 cmpt = reqp->req.ppm_notify_level_req.cmpt;
1531 break;
1532 default:
1533 *result = DDI_FAILURE;
1534 PPMD(D_FET, ("%s: unknown request type %d for %s@%s\n",
1535 str, reqp->request_type, PM_NAME(dip), PM_ADDR(dip)))
1536 return (DDI_FAILURE);
1537 }
1538
1539 for (ppmd = PPM_GET_PRIVATE(dip); ppmd; ppmd = ppmd->next)
1540 if (cmpt == ppmd->cmpt)
1541 break;
1542 if (!ppmd) {
1543 PPMD(D_FET, ("%s: dip(%p): old(%d)->new(%d): no ppm_dev"
1544 " found for cmpt(%d)", str, (void *)dip, old, new, cmpt))
1545 *result = DDI_FAILURE;
1546 return (DDI_FAILURE);
1547 }
1548 domp = ppmd->domp;
1549 PPMD(D_FET, ("%s: %s@%s %s old %d, new %d, c%d, level %d, "
1550 "status %s\n", str, PM_NAME(dip), PM_ADDR(dip),
1551 ppm_get_ctlstr(reqp->request_type, ~0), old, new, cmpt,
1552 ppmd->level, (domp->status == PPMD_OFF ? "off" : "on")))
1553
1554
1555 ASSERT(old == ppmd->level);
1556
1557 if (new == ppmd->level) {
1558 PPMD(D_FET, ("nop\n"))
1559 return (DDI_SUCCESS);
1560 }
1561
1562 PPM_LOCK_DOMAIN(domp);
1563
1564 /*
1565 * In general, a device's published lowest power level does not
1566 * have to be 0 if power-off is not tolerated. i.e. a device
1567 * instance may export its lowest level > 0. It is reasonable to
1568 * assume that level 0 indicates off state, positive level values
1569 * indicate power states above off, include full power state.
1570 */
1571 if (new > 0) { /* device powering up or to different positive level */
1572 if (domp->status == PPMD_OFF) {
1573
1574 /* can not be in (chpt, resume) window */
1575 ASSERT(ppm_cpr_window_flag == B_FALSE);
1576
1577 ASSERT(old == 0 && domp->pwr_cnt == 0);
1578
1579 PPMD(D_FET, ("About to turn fet on for %s@%s c%d\n",
1580 PM_NAME(dip), PM_ADDR(dip), cmpt))
1581
1582 *result = ppm_fetset(domp, PPMD_ON);
1583 if (*result != DDI_SUCCESS) {
1584 PPMD(D_FET, ("\tCan't turn on power FET: "
1585 "ret(%d)\n", *result))
1586 PPM_UNLOCK_DOMAIN(domp);
1587 return (DDI_FAILURE);
1588 }
1589 }
1590
1591 /*
1592 * If powering up, pre-increment the count before
1593 * calling pwr_func, because we are going to release
1594 * the domain lock and another thread might turn off
1595 * domain power otherwise.
1596 */
1597 if (old == 0) {
1598 domp->pwr_cnt++;
1599 incr = 1;
1600 }
1601
1602 PPMD(D_FET, ("\t%s domain power count: %d\n",
1603 domp->name, domp->pwr_cnt))
1604 }
1605
1606
1607 PPM_UNLOCK_DOMAIN(domp);
1608
1609 ASSERT(domp->pwr_cnt > 0);
1610
1611 if ((*result = (*pwr_func)(ppmd, cmpt, new)) != DDI_SUCCESS) {
1612 PPMD(D_FET, ("\t%s power change failed: ret(%d)\n",
1613 ppmd->path, *result))
1614 }
1615
1616 PPM_LOCK_DOMAIN(domp);
1617
1618 /*
1619 * Decr the power count in two cases:
1620 *
1621 * 1) request was to power device down and was successful
1622 * 2) request was to power up (we pre-incremented count), but failed.
1623 */
1624 if ((*result == DDI_SUCCESS && ppmd->level == 0) ||
1625 (*result != DDI_SUCCESS && incr)) {
1626 ASSERT(domp->pwr_cnt > 0);
1627 domp->pwr_cnt--;
1628 }
1629
1630 PPMD(D_FET, ("\t%s domain power count: %d\n",
1631 domp->name, domp->pwr_cnt))
1632
1633 /*
1634 * call to pwr_func will update ppm data structures, if it
1635 * succeeds. ppm should return whatever is the return value
1636 * from call to pwr_func. This way pm and ppm data structures
1637 * always in sync. Use dummy_ret from here for any further
1638 * return values.
1639 */
1640 if ((domp->pwr_cnt == 0) &&
1641 (ppm_cpr_window_flag == B_FALSE) &&
1642 ppm_none_else_holds_power(domp)) {
1643
1644 PPMD(D_FET, ("About to turn FET off for %s@%s c%d\n",
1645 PM_NAME(dip), PM_ADDR(dip), cmpt))
1646
1647 dummy_ret = ppm_fetset(domp, PPMD_OFF);
1648 if (dummy_ret != DDI_SUCCESS) {
1649 PPMD(D_FET, ("\tCan't turn off FET: ret(%d)\n",
1650 dummy_ret))
1651 }
1652 }
1653
1654 PPM_UNLOCK_DOMAIN(domp);
1655 ASSERT(domp->pwr_cnt >= 0);
1656 return (*result);
1657 }
1658
1659
1660 /*
1661 * the actual code that turn on or off domain power fet and
1662 * update domain status
1663 */
1664 static int
ppm_fetset(ppm_domain_t * domp,uint8_t value)1665 ppm_fetset(ppm_domain_t *domp, uint8_t value)
1666 {
1667 char *str = "ppm_fetset";
1668 int key;
1669 ppm_dc_t *dc;
1670 int ret;
1671 clock_t temp;
1672 clock_t delay = 0;
1673
1674 key = (value == PPMD_ON) ? PPMDC_FET_ON : PPMDC_FET_OFF;
1675 for (dc = domp->dc; dc; dc = dc->next)
1676 if (dc->cmd == key)
1677 break;
1678 if (!dc || !dc->lh) {
1679 PPMD(D_FET, ("%s: %s domain: NULL ppm_dc handle\n",
1680 str, domp->name))
1681 return (DDI_FAILURE);
1682 }
1683
1684 if (key == PPMDC_FET_ON) {
1685 PPM_GET_IO_DELAY(dc, delay);
1686 if (delay > 0 && domp->last_off_time > 0) {
1687 /*
1688 * provide any delay required before turning on.
1689 * some devices e.g. Samsung DVD require minimum
1690 * of 1 sec between OFF->ON. no delay is required
1691 * for the first time.
1692 */
1693 temp = ddi_get_lbolt();
1694 temp -= domp->last_off_time;
1695 temp = drv_hztousec(temp);
1696
1697 if (temp < delay) {
1698 /*
1699 * busy wait untill we meet the
1700 * required delay. Since we maintain
1701 * time stamps in terms of clock ticks
1702 * we might wait for longer than required
1703 */
1704 PPMD(D_FET, ("%s : waiting %lu micro seconds "
1705 "before on\n", domp->name,
1706 delay - temp));
1707 drv_usecwait(delay - temp);
1708 }
1709 }
1710 }
1711 switch (dc->method) {
1712 #ifdef sun4u
1713 case PPMDC_I2CKIO: {
1714 i2c_gpio_t i2c_req;
1715 i2c_req.reg_mask = dc->m_un.i2c.mask;
1716 i2c_req.reg_val = dc->m_un.i2c.val;
1717 ret = ldi_ioctl(dc->lh, dc->m_un.i2c.iowr,
1718 (intptr_t)&i2c_req, FWRITE | FKIOCTL, kcred, NULL);
1719 break;
1720 }
1721 #endif
1722
1723 case PPMDC_KIO:
1724 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
1725 (intptr_t)&(dc->m_un.kio.val), FWRITE | FKIOCTL, kcred,
1726 NULL);
1727 break;
1728
1729 default:
1730 PPMD(D_FET, ("\t%s: unsupported domain control method %d\n",
1731 str, domp->dc->method))
1732 return (DDI_FAILURE);
1733 }
1734
1735 PPMD(D_FET, ("%s: %s domain(%s) FET from %s to %s\n", str,
1736 (ret == 0) ? "turned" : "failed to turn",
1737 domp->name,
1738 (domp->status == PPMD_ON) ? "ON" : "OFF",
1739 (value == PPMD_ON) ? "ON" : "OFF"))
1740
1741 if (ret == DDI_SUCCESS) {
1742 domp->status = value;
1743
1744 if (key == PPMDC_FET_OFF)
1745 /*
1746 * record the time, when it is off. time is recorded
1747 * in clock ticks
1748 */
1749 domp->last_off_time = ddi_get_lbolt();
1750
1751 /* implement any post op delay. */
1752 if (key == PPMDC_FET_ON) {
1753 PPM_GET_IO_POST_DELAY(dc, delay);
1754 PPMD(D_FET, ("%s : waiting %lu micro seconds "
1755 "after on\n", domp->name, delay))
1756 if (delay > 0)
1757 drv_usecwait(delay);
1758 }
1759 }
1760
1761 return (ret);
1762 }
1763
1764
1765 /*
1766 * read power fet status
1767 */
1768 static int
ppm_fetget(ppm_domain_t * domp,uint8_t * lvl)1769 ppm_fetget(ppm_domain_t *domp, uint8_t *lvl)
1770 {
1771 char *str = "ppm_fetget";
1772 ppm_dc_t *dc = domp->dc;
1773 uint_t kio_val;
1774 int off_val;
1775 int ret;
1776
1777 if (!dc->lh) {
1778 PPMD(D_FET, ("%s: %s domain NULL ppm_dc layered handle\n",
1779 str, domp->name))
1780 return (DDI_FAILURE);
1781 }
1782 if (!dc->next) {
1783 cmn_err(CE_WARN, "%s: expect both fet on and fet off ops "
1784 "defined, found only one in domain(%s)", str, domp->name);
1785 return (DDI_FAILURE);
1786 }
1787
1788 switch (dc->method) {
1789 #ifdef sun4u
1790 case PPMDC_I2CKIO: {
1791 i2c_gpio_t i2c_req;
1792 i2c_req.reg_mask = dc->m_un.i2c.mask;
1793 ret = ldi_ioctl(dc->lh, dc->m_un.i2c.iord,
1794 (intptr_t)&i2c_req, FWRITE | FKIOCTL, kcred, NULL);
1795
1796 if (ret) {
1797 PPMD(D_FET, ("%s: PPMDC_I2CKIO failed: ret(%d)\n",
1798 str, ret))
1799 return (ret);
1800 }
1801
1802 off_val = (dc->cmd == PPMDC_FET_OFF) ? dc->m_un.i2c.val :
1803 dc->next->m_un.i2c.val;
1804 *lvl = (i2c_req.reg_val == off_val) ? PPMD_OFF : PPMD_ON;
1805
1806 PPMD(D_FET, ("%s: %s domain FET %s\n", str, domp->name,
1807 (i2c_req.reg_val == off_val) ? "OFF" : "ON"))
1808
1809 break;
1810 }
1811 #endif
1812
1813 case PPMDC_KIO:
1814 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iord,
1815 (intptr_t)&kio_val, FWRITE | FKIOCTL, kcred, NULL);
1816 if (ret) {
1817 PPMD(D_FET, ("%s: PPMDC_KIO failed: ret(%d)\n",
1818 str, ret))
1819 return (ret);
1820 }
1821
1822 off_val = (dc->cmd == PPMDC_FET_OFF) ? dc->m_un.kio.val :
1823 dc->next->m_un.kio.val;
1824 *lvl = (kio_val == off_val) ? PPMD_OFF : PPMD_ON;
1825
1826 PPMD(D_FET, ("%s: %s domain FET %s\n", str, domp->name,
1827 (kio_val == off_val) ? "OFF" : "ON"))
1828
1829 break;
1830
1831 default:
1832 PPMD(D_FET, ("%s: unsupported domain control method %d\n",
1833 str, domp->dc->method))
1834 return (DDI_FAILURE);
1835 }
1836
1837 return (DDI_SUCCESS);
1838 }
1839
1840
1841 /*
1842 * the actual code that switches pci clock and update domain status
1843 */
1844 static int
ppm_switch_clock(ppm_domain_t * domp,int onoff)1845 ppm_switch_clock(ppm_domain_t *domp, int onoff)
1846 {
1847 #ifdef DEBUG
1848 char *str = "ppm_switch_clock";
1849 #endif
1850 int cmd, pio_save;
1851 ppm_dc_t *dc;
1852 int ret;
1853 extern int do_polled_io;
1854 extern uint_t cfb_inuse;
1855 ppm_dev_t *pdev;
1856
1857 cmd = (onoff == PPMD_ON) ? PPMDC_CLK_ON : PPMDC_CLK_OFF;
1858 dc = ppm_lookup_dc(domp, cmd);
1859 if (!dc) {
1860 PPMD(D_PCI, ("%s: no ppm_dc found for domain (%s)\n",
1861 str, domp->name))
1862 return (DDI_FAILURE);
1863 }
1864
1865 switch (dc->method) {
1866 case PPMDC_KIO:
1867 /*
1868 * If we're powering up cfb on a Stop-A, we only
1869 * want to do polled i/o to turn ON the clock
1870 */
1871 pio_save = do_polled_io;
1872 if ((cfb_inuse) && (cmd == PPMDC_CLK_ON)) {
1873 for (pdev = domp->devlist; pdev; pdev = pdev->next) {
1874 if (pm_is_cfb(pdev->dip)) {
1875 do_polled_io = 1;
1876 break;
1877 }
1878 }
1879 }
1880
1881 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
1882 (intptr_t)&(dc->m_un.kio.val), FWRITE | FKIOCTL,
1883 kcred, NULL);
1884
1885 do_polled_io = pio_save;
1886
1887 if (ret == 0) {
1888 if (cmd == PPMDC_CLK_ON) {
1889 domp->status = PPMD_ON;
1890
1891 /*
1892 * PCI PM spec requires 50ms delay
1893 */
1894 drv_usecwait(50000);
1895 } else
1896 domp->status = PPMD_OFF;
1897 }
1898
1899 PPMD(D_PCI, ("%s: %s pci clock %s for domain (%s)\n", str,
1900 (ret == 0) ? "turned" : "failed to turn",
1901 (cmd == PPMDC_CLK_OFF) ? "OFF" : "ON",
1902 domp->name))
1903
1904 break;
1905
1906 default:
1907 PPMD(D_PCI, ("%s: unsupported domain control method %d\n",
1908 str, dc->method))
1909 return (DDI_FAILURE);
1910 }
1911
1912 return (DDI_SUCCESS);
1913 }
1914
1915
1916 /*
1917 * pci slot domain is formed of pci device(s) reside in a pci slot.
1918 * This function monitors domain device's power level change, such
1919 * that,
1920 * when all domain power count has gone to 0, it attempts to turn off
1921 * the pci slot's clock;
1922 * if any domain device is powering up, it'll turn on the pci slot's
1923 * clock as the first thing.
1924 */
1925 /* ARGUSED */
1926 static int
ppm_manage_pci(dev_info_t * dip,power_req_t * reqp,int * result)1927 ppm_manage_pci(dev_info_t *dip, power_req_t *reqp, int *result)
1928 {
1929 #ifdef DEBUG
1930 char *str = "ppm_manage_pci";
1931 #endif
1932 int (*pwr_func)(ppm_dev_t *, int, int);
1933 int old, new, cmpt;
1934 ppm_dev_t *ppmd;
1935 ppm_domain_t *domp;
1936 int incr = 0;
1937 int dummy_ret;
1938
1939 *result = DDI_SUCCESS;
1940 switch (reqp->request_type) {
1941 case PMR_PPM_SET_POWER:
1942 pwr_func = ppm_change_power_level;
1943 old = reqp->req.ppm_set_power_req.old_level;
1944 new = reqp->req.ppm_set_power_req.new_level;
1945 cmpt = reqp->req.ppm_set_power_req.cmpt;
1946 break;
1947
1948 case PMR_PPM_POWER_CHANGE_NOTIFY:
1949 pwr_func = ppm_record_level_change;
1950 old = reqp->req.ppm_notify_level_req.old_level;
1951 new = reqp->req.ppm_notify_level_req.new_level;
1952 cmpt = reqp->req.ppm_notify_level_req.cmpt;
1953 break;
1954
1955 default:
1956 *result = DDI_FAILURE;
1957 return (DDI_FAILURE);
1958 }
1959
1960 for (ppmd = PPM_GET_PRIVATE(dip); ppmd; ppmd = ppmd->next)
1961 if (cmpt == ppmd->cmpt)
1962 break;
1963 if (!ppmd) {
1964 PPMD(D_PCI, ("%s: dip(%p): old(%d), new(%d): no ppm_dev"
1965 " found for cmpt(%d)", str, (void *)dip, old, new, cmpt))
1966 *result = DDI_FAILURE;
1967 return (DDI_FAILURE);
1968 }
1969 domp = ppmd->domp;
1970 PPMD(D_PCI, ("%s: %s, dev(%s), c%d, old %d, new %d\n", str,
1971 ppm_get_ctlstr(reqp->request_type, ~0),
1972 ppmd->path, cmpt, old, new))
1973
1974 ASSERT(old == ppmd->level);
1975 if (new == ppmd->level)
1976 return (DDI_SUCCESS);
1977
1978 PPM_LOCK_DOMAIN(domp);
1979
1980 if (new > 0) { /* device powering up */
1981 if (domp->status == PPMD_OFF) {
1982
1983 /* cannot be off during (chpt, resume) window */
1984 ASSERT(ppm_cpr_window_flag == B_FALSE);
1985
1986 /* either both OFF or both ON */
1987 ASSERT(!((old == 0) ^ (domp->pwr_cnt == 0)));
1988
1989 PPMD(D_PCI, ("About to turn clock on for %s@%s c%d\n",
1990 PM_NAME(dip), PM_ADDR(dip), cmpt))
1991
1992 *result = ppm_switch_clock(domp, PPMD_ON);
1993 if (*result != DDI_SUCCESS) {
1994 PPMD(D_PCI, ("\tcan't switch on pci clock: "
1995 "ret(%d)\n", *result))
1996 PPM_UNLOCK_DOMAIN(domp);
1997 return (DDI_FAILURE);
1998 }
1999 }
2000
2001 if (old == 0) {
2002 domp->pwr_cnt++;
2003 incr = 1;
2004 }
2005
2006 PPMD(D_PCI, ("\t%s domain power count: %d\n",
2007 domp->name, domp->pwr_cnt))
2008 }
2009
2010 PPM_UNLOCK_DOMAIN(domp);
2011
2012 ASSERT(domp->pwr_cnt > 0);
2013
2014 if ((*result = (*pwr_func)(ppmd, cmpt, new)) != DDI_SUCCESS) {
2015 PPMD(D_PCI, ("\t%s power change failed: ret(%d)\n",
2016 ppmd->path, *result))
2017 }
2018
2019 PPM_LOCK_DOMAIN(domp);
2020
2021 /*
2022 * Decr the power count in two cases:
2023 *
2024 * 1) request was to power device down and was successful
2025 * 2) request was to power up (we pre-incremented count), but failed.
2026 */
2027 if ((*result == DDI_SUCCESS && ppmd->level == 0) ||
2028 (*result != DDI_SUCCESS && incr)) {
2029 ASSERT(domp->pwr_cnt > 0);
2030 domp->pwr_cnt--;
2031 }
2032
2033 PPMD(D_PCI, ("\t%s domain power count: %d\n",
2034 domp->name, domp->pwr_cnt))
2035
2036 /*
2037 * call to pwr_func will update ppm data structures, if it
2038 * succeeds. ppm should return whatever is the return value
2039 * from call to pwr_func. This way pm and ppm data structures
2040 * always in sync. Use dummy_ret from here for any further
2041 * return values.
2042 */
2043 if ((domp->pwr_cnt == 0) &&
2044 (ppm_cpr_window_flag == B_FALSE) &&
2045 ppm_none_else_holds_power(domp)) {
2046
2047 PPMD(D_PCI, ("About to turn clock off for %s@%s c%d\n",
2048 PM_NAME(dip), PM_ADDR(dip), cmpt))
2049
2050 dummy_ret = ppm_switch_clock(domp, PPMD_OFF);
2051 if (dummy_ret != DDI_SUCCESS) {
2052 PPMD(D_PCI, ("\tCan't switch clock off: "
2053 "ret(%d)\n", dummy_ret))
2054 }
2055 }
2056
2057 PPM_UNLOCK_DOMAIN(domp);
2058 ASSERT(domp->pwr_cnt >= 0);
2059 return (*result);
2060 }
2061
2062 /*
2063 * When the driver for the primary PCI-Express child has set the device to
2064 * lowest power (D3hot), we come here to save even more power by transitioning
2065 * the slot to D3cold. Similarly, if the slot is in D3cold and we need to
2066 * power up the child, we come here first to power up the slot.
2067 */
2068 /* ARGUSED */
2069 static int
ppm_manage_pcie(dev_info_t * dip,power_req_t * reqp,int * result)2070 ppm_manage_pcie(dev_info_t *dip, power_req_t *reqp, int *result)
2071 {
2072 #ifdef DEBUG
2073 char *str = "ppm_manage_pcie";
2074 #endif
2075 int (*pwr_func)(ppm_dev_t *, int, int);
2076 int old, new, cmpt;
2077 ppm_dev_t *ppmd;
2078 ppm_domain_t *domp;
2079 int incr = 0;
2080 int dummy_ret;
2081
2082 *result = DDI_SUCCESS;
2083 switch (reqp->request_type) {
2084 case PMR_PPM_SET_POWER:
2085 pwr_func = ppm_change_power_level;
2086 old = reqp->req.ppm_set_power_req.old_level;
2087 new = reqp->req.ppm_set_power_req.new_level;
2088 cmpt = reqp->req.ppm_set_power_req.cmpt;
2089 break;
2090
2091 case PMR_PPM_POWER_CHANGE_NOTIFY:
2092 pwr_func = ppm_record_level_change;
2093 old = reqp->req.ppm_notify_level_req.old_level;
2094 new = reqp->req.ppm_notify_level_req.new_level;
2095 cmpt = reqp->req.ppm_notify_level_req.cmpt;
2096 break;
2097
2098 default:
2099 *result = DDI_FAILURE;
2100 return (DDI_FAILURE);
2101 }
2102
2103 for (ppmd = PPM_GET_PRIVATE(dip); ppmd; ppmd = ppmd->next)
2104 if (cmpt == ppmd->cmpt)
2105 break;
2106 if (!ppmd) {
2107 PPMD(D_PCI, ("%s: dip(%p): old(%d), new(%d): no ppm_dev"
2108 " found for cmpt(%d)", str, (void *)dip, old, new, cmpt))
2109 *result = DDI_FAILURE;
2110 return (DDI_FAILURE);
2111 }
2112 domp = ppmd->domp;
2113 PPMD(D_PCI, ("%s: %s, dev(%s), c%d, old %d, new %d\n", str,
2114 ppm_get_ctlstr(reqp->request_type, ~0),
2115 ppmd->path, cmpt, old, new))
2116
2117 ASSERT(old == ppmd->level);
2118 if (new == ppmd->level)
2119 return (DDI_SUCCESS);
2120
2121 PPM_LOCK_DOMAIN(domp);
2122
2123 if (new > 0) { /* device powering up */
2124 if (domp->status == PPMD_OFF) {
2125
2126 /* cannot be off during (chpt, resume) window */
2127 ASSERT(ppm_cpr_window_flag == B_FALSE);
2128
2129 /* either both OFF or both ON */
2130 ASSERT(!((old == 0) ^ (domp->pwr_cnt == 0)));
2131
2132 PPMD(D_PCI, ("About to turn on pcie slot for "
2133 "%s@%s c%d\n", PM_NAME(dip), PM_ADDR(dip), cmpt))
2134
2135 *result = ppm_pcie_pwr(domp, PPMD_ON);
2136 if (*result != DDI_SUCCESS) {
2137 PPMD(D_PCI, ("\tcan't switch on pcie slot: "
2138 "ret(%d)\n", *result))
2139 PPM_UNLOCK_DOMAIN(domp);
2140 return (DDI_FAILURE);
2141 }
2142 }
2143
2144 if (old == 0) {
2145 domp->pwr_cnt++;
2146 incr = 1;
2147 }
2148
2149 PPMD(D_PCI, ("\t%s domain power count: %d\n",
2150 domp->name, domp->pwr_cnt))
2151 }
2152
2153 PPM_UNLOCK_DOMAIN(domp);
2154
2155 ASSERT(domp->pwr_cnt > 0);
2156
2157 if ((*result = (*pwr_func)(ppmd, cmpt, new)) != DDI_SUCCESS) {
2158 PPMD(D_PCI, ("\t%s power change failed: ret(%d)\n",
2159 ppmd->path, *result))
2160 }
2161
2162 PPM_LOCK_DOMAIN(domp);
2163
2164 /*
2165 * Decr the power count in two cases:
2166 *
2167 * 1) request was to power device down and was successful
2168 * 2) request was to power up (we pre-incremented count), but failed.
2169 */
2170 if ((*result == DDI_SUCCESS && ppmd->level == 0) ||
2171 (*result != DDI_SUCCESS && incr)) {
2172 ASSERT(domp->pwr_cnt > 0);
2173 domp->pwr_cnt--;
2174 }
2175
2176 PPMD(D_PCI, ("\t%s domain power count: %d\n",
2177 domp->name, domp->pwr_cnt))
2178
2179 /*
2180 * call to pwr_func will update ppm data structures, if it
2181 * succeeds. ppm should return whatever is the return value
2182 * from call to pwr_func. This way pm and ppm data structures
2183 * always in sync. Use dummy_ret from here for any further
2184 * return values.
2185 */
2186 if ((domp->pwr_cnt == 0) &&
2187 (ppm_cpr_window_flag == B_FALSE) &&
2188 ppm_none_else_holds_power(domp)) {
2189
2190 PPMD(D_PCI, ("About to turn off pcie slot for %s@%s c%d\n",
2191 PM_NAME(dip), PM_ADDR(dip), cmpt))
2192
2193 dummy_ret = ppm_pcie_pwr(domp, PPMD_OFF);
2194 if (dummy_ret != DDI_SUCCESS) {
2195 PPMD(D_PCI, ("\tCan't switch pcie slot off: "
2196 "ret(%d)\n", dummy_ret))
2197 }
2198 }
2199
2200 PPM_UNLOCK_DOMAIN(domp);
2201 ASSERT(domp->pwr_cnt >= 0);
2202 return (*result);
2203
2204 }
2205
2206 /*
2207 * Set or clear a bit on a GPIO device. These bits are used for various device-
2208 * specific purposes.
2209 */
2210 static int
ppm_gpioset(ppm_domain_t * domp,int key)2211 ppm_gpioset(ppm_domain_t *domp, int key)
2212 {
2213 #ifdef DEBUG
2214 char *str = "ppm_gpioset";
2215 #endif
2216 ppm_dc_t *dc;
2217 int ret;
2218 clock_t delay = 0;
2219
2220 for (dc = domp->dc; dc; dc = dc->next)
2221 if (dc->cmd == key)
2222 break;
2223 if (!dc || !dc->lh) {
2224 PPMD(D_GPIO, ("%s: %s domain: NULL ppm_dc handle\n",
2225 str, domp->name))
2226 return (DDI_FAILURE);
2227 }
2228
2229 PPM_GET_IO_DELAY(dc, delay);
2230 if (delay > 0) {
2231 PPMD(D_GPIO, ("%s : waiting %lu micro seconds "
2232 "before change\n", domp->name, delay))
2233 drv_usecwait(delay);
2234 }
2235
2236 switch (dc->method) {
2237 #ifdef sun4u
2238 case PPMDC_I2CKIO: {
2239 i2c_gpio_t i2c_req;
2240 ppm_dev_t *pdev;
2241 int pio_save;
2242 extern int do_polled_io;
2243 extern uint_t cfb_inuse;
2244 i2c_req.reg_mask = dc->m_un.i2c.mask;
2245 i2c_req.reg_val = dc->m_un.i2c.val;
2246
2247 pio_save = do_polled_io;
2248 if (cfb_inuse) {
2249 for (pdev = domp->devlist; pdev; pdev = pdev->next) {
2250 if (pm_is_cfb(pdev->dip)) {
2251 do_polled_io = 1;
2252 PPMD(D_GPIO, ("%s: cfb is in use, "
2253 "i2c transaction is done in "
2254 "poll-mode.\n", str))
2255 break;
2256 }
2257 }
2258 }
2259 ret = ldi_ioctl(dc->lh, dc->m_un.i2c.iowr,
2260 (intptr_t)&i2c_req, FWRITE | FKIOCTL, kcred, NULL);
2261 do_polled_io = pio_save;
2262
2263 PPMD(D_GPIO, ("%s: %s domain(%s) from %s by writing %x "
2264 "to gpio\n",
2265 str, (ret == 0) ? "turned" : "FAILed to turn",
2266 domp->name,
2267 (domp->status == PPMD_ON) ? "ON" : "OFF",
2268 dc->m_un.i2c.val))
2269
2270 break;
2271 }
2272 #endif
2273
2274 case PPMDC_KIO:
2275 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
2276 (intptr_t)&(dc->m_un.kio.val), FWRITE | FKIOCTL, kcred,
2277 NULL);
2278
2279 PPMD(D_GPIO, ("%s: %s domain(%s) from %s by writing %x "
2280 "to gpio\n",
2281 str, (ret == 0) ? "turned" : "FAILed to turn",
2282 domp->name,
2283 (domp->status == PPMD_ON) ? "ON" : "OFF",
2284 dc->m_un.kio.val))
2285
2286 break;
2287
2288 default:
2289 PPMD(D_GPIO, ("\t%s: unsupported domain control method %d\n",
2290 str, domp->dc->method))
2291 return (DDI_FAILURE);
2292 }
2293
2294 /* implement any post op delay. */
2295 PPM_GET_IO_POST_DELAY(dc, delay);
2296 if (delay > 0) {
2297 PPMD(D_GPIO, ("%s : waiting %lu micro seconds "
2298 "after change\n", domp->name, delay))
2299 drv_usecwait(delay);
2300 }
2301
2302 return (ret);
2303 }
2304
2305 static int
ppm_pcie_pwr(ppm_domain_t * domp,int onoff)2306 ppm_pcie_pwr(ppm_domain_t *domp, int onoff)
2307 {
2308 #ifdef DEBUG
2309 char *str = "ppm_pcie_pwr";
2310 #endif
2311 int ret = DDI_FAILURE;
2312 ppm_dc_t *dc;
2313 clock_t delay;
2314
2315 ASSERT(onoff == PPMD_OFF || onoff == PPMD_ON);
2316
2317 dc = ppm_lookup_dc(domp,
2318 onoff == PPMD_ON ? PPMDC_PRE_PWR_ON : PPMDC_PRE_PWR_OFF);
2319 if (dc) {
2320
2321 /*
2322 * Invoke layered ioctl for pcie root complex nexus to
2323 * transition the link
2324 */
2325 ASSERT(dc->method == PPMDC_KIO);
2326 delay = dc->m_un.kio.delay;
2327 if (delay > 0) {
2328 PPMD(D_GPIO, ("%s : waiting %lu micro seconds "
2329 "before change\n", domp->name, delay))
2330 drv_usecwait(delay);
2331 }
2332 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
2333 (intptr_t)&(dc->m_un.kio.val),
2334 FWRITE | FKIOCTL, kcred, NULL);
2335 if (ret == DDI_SUCCESS) {
2336 delay = dc->m_un.kio.post_delay;
2337 if (delay > 0) {
2338 PPMD(D_GPIO, ("%s : waiting %lu micro seconds "
2339 "after change\n", domp->name, delay))
2340 drv_usecwait(delay);
2341 }
2342 } else {
2343 PPMD(D_PCI, ("%s: ldi_ioctl FAILED for domain(%s)\n",
2344 str, domp->name))
2345 return (ret);
2346 }
2347 }
2348
2349 switch (onoff) {
2350 case PPMD_OFF:
2351 /* Turn off the clock for this slot. */
2352 if ((ret = ppm_gpioset(domp, PPMDC_CLK_OFF)) != DDI_SUCCESS) {
2353 PPMD(D_GPIO,
2354 ("%s: failed to turn off domain(%s) clock\n",
2355 str, domp->name))
2356 return (ret);
2357 }
2358
2359 /* Turn off the power to this slot */
2360 if ((ret = ppm_gpioset(domp, PPMDC_PWR_OFF)) != DDI_SUCCESS) {
2361 PPMD(D_GPIO,
2362 ("%s: failed to turn off domain(%s) power\n",
2363 str, domp->name))
2364 return (ret);
2365 }
2366 break;
2367 case PPMD_ON:
2368 /* Assert RESET for this slot. */
2369 if ((ret = ppm_gpioset(domp, PPMDC_RESET_ON)) != DDI_SUCCESS) {
2370 PPMD(D_GPIO,
2371 ("%s: failed to assert reset for domain(%s)\n",
2372 str, domp->name))
2373 return (ret);
2374 }
2375
2376 /* Turn on the power to this slot */
2377 if ((ret = ppm_gpioset(domp, PPMDC_PWR_ON)) != DDI_SUCCESS) {
2378 PPMD(D_GPIO,
2379 ("%s: failed to turn on domain(%s) power\n",
2380 str, domp->name))
2381 return (ret);
2382 }
2383
2384 /* Turn on the clock for this slot */
2385 if ((ret = ppm_gpioset(domp, PPMDC_CLK_ON)) != DDI_SUCCESS) {
2386 PPMD(D_GPIO,
2387 ("%s: failed to turn on domain(%s) clock\n",
2388 str, domp->name))
2389 return (ret);
2390 }
2391
2392 /* De-assert RESET for this slot. */
2393 if ((ret = ppm_gpioset(domp, PPMDC_RESET_OFF)) != DDI_SUCCESS) {
2394 PPMD(D_GPIO,
2395 ("%s: failed to de-assert reset for domain(%s)\n",
2396 str, domp->name))
2397 return (ret);
2398 }
2399
2400 dc = ppm_lookup_dc(domp, PPMDC_POST_PWR_ON);
2401 if (dc) {
2402 /*
2403 * Invoke layered ioctl to PCIe root complex nexus
2404 * to transition the link.
2405 */
2406 ASSERT(dc->method == PPMDC_KIO);
2407 delay = dc->m_un.kio.delay;
2408 if (delay > 0) {
2409 PPMD(D_GPIO, ("%s: waiting %lu micro seconds "
2410 "before change\n", domp->name, delay))
2411 drv_usecwait(delay);
2412 }
2413 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
2414 (intptr_t)&(dc->m_un.kio.val),
2415 FWRITE | FKIOCTL, kcred, NULL);
2416
2417 if (ret != DDI_SUCCESS) {
2418 PPMD(D_PCI, ("%s: layered ioctl to PCIe"
2419 "root complex nexus FAILed\n", str))
2420 return (ret);
2421 }
2422
2423 delay = dc->m_un.kio.post_delay;
2424 if (delay > 0) {
2425 PPMD(D_GPIO, ("%s: waiting %lu micro "
2426 "seconds after change\n",
2427 domp->name, delay))
2428 drv_usecwait(delay);
2429 }
2430 }
2431 break;
2432 default:
2433 ASSERT(0);
2434 }
2435
2436 PPMD(D_PCI, ("%s: turned domain(%s) PCIe slot power from %s to %s\n",
2437 str, domp->name, (domp->status == PPMD_ON) ? "ON" : "OFF",
2438 onoff == PPMD_ON ? "ON" : "OFF"))
2439
2440 domp->status = onoff;
2441 return (ret);
2442 }
2443
2444
2445 /*
2446 * Change the power level for a component of a device. If the change
2447 * arg is true, we call the framework to actually change the device's
2448 * power; otherwise, we just update our own copy of the power level.
2449 */
2450 static int
ppm_set_level(ppm_dev_t * ppmd,int cmpt,int level,boolean_t change)2451 ppm_set_level(ppm_dev_t *ppmd, int cmpt, int level, boolean_t change)
2452 {
2453 #ifdef DEBUG
2454 char *str = "ppm_set_level";
2455 #endif
2456 int ret;
2457
2458 ret = DDI_SUCCESS;
2459 if (change)
2460 ret = pm_power(ppmd->dip, cmpt, level);
2461
2462 PPMD(D_SETLVL, ("%s: %s change=%d, old %d, new %d, ret %d\n",
2463 str, ppmd->path, change, ppmd->level, level, ret))
2464
2465 if (ret == DDI_SUCCESS) {
2466 ppmd->level = level;
2467 ppmd->rplvl = PM_LEVEL_UNKNOWN;
2468 }
2469
2470 return (ret);
2471 }
2472
2473
2474 static int
ppm_change_power_level(ppm_dev_t * ppmd,int cmpt,int level)2475 ppm_change_power_level(ppm_dev_t *ppmd, int cmpt, int level)
2476 {
2477 return (ppm_set_level(ppmd, cmpt, level, B_TRUE));
2478 }
2479
2480
2481 static int
ppm_record_level_change(ppm_dev_t * ppmd,int cmpt,int level)2482 ppm_record_level_change(ppm_dev_t *ppmd, int cmpt, int level)
2483 {
2484 return (ppm_set_level(ppmd, cmpt, level, B_FALSE));
2485 }
2486
2487
2488 static void
ppm_manage_led(int action)2489 ppm_manage_led(int action)
2490 {
2491 ppm_domain_t *domp;
2492 ppm_unit_t *unitp;
2493 timeout_id_t tid;
2494
2495
2496 PPMD(D_LED, ("ppm_manage_led: action: %s\n",
2497 (action == PPM_LED_BLINKING) ? "PPM_LED_BLINKING" :
2498 "PPM_LED_SOLIDON"))
2499
2500 /*
2501 * test whether led operation is practically supported,
2502 * if not, we waive without pressing for reasons
2503 */
2504 if (!ppm_lookup_dc(NULL, PPMDC_LED_ON))
2505 return;
2506
2507 unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
2508 for (domp = ppm_domain_p; (domp && (domp->model != PPMD_LED)); )
2509 domp = domp->next;
2510
2511 mutex_enter(&unitp->lock);
2512 if (action == PPM_LED_BLINKING) {
2513 ppm_set_led(domp, PPMD_OFF);
2514 unitp->led_tid = timeout(
2515 ppm_blink_led, domp, PPM_LEDOFF_INTERVAL);
2516
2517 } else { /* PPM_LED_SOLIDON */
2518 ASSERT(action == PPM_LED_SOLIDON);
2519 tid = unitp->led_tid;
2520 unitp->led_tid = 0;
2521
2522 mutex_exit(&unitp->lock);
2523 (void) untimeout(tid);
2524
2525 mutex_enter(&unitp->lock);
2526 ppm_set_led(domp, PPMD_ON);
2527 }
2528 mutex_exit(&unitp->lock);
2529 }
2530
2531
2532 static void
ppm_set_led(ppm_domain_t * domp,int val)2533 ppm_set_led(ppm_domain_t *domp, int val)
2534 {
2535 int ret;
2536
2537 ret = ppm_gpioset(domp,
2538 (val == PPMD_ON) ? PPMDC_LED_ON : PPMDC_LED_OFF);
2539
2540 PPMD(D_LED, ("ppm_set_led: %s LED from %s\n",
2541 (ret == 0) ? "turned" : "FAILed to turn",
2542 (domp->status == PPMD_ON) ? "ON to OFF" : "OFF to ON"))
2543
2544 if (ret == DDI_SUCCESS)
2545 domp->status = val;
2546 }
2547
2548
2549 static void
ppm_blink_led(void * arg)2550 ppm_blink_led(void *arg)
2551 {
2552 ppm_unit_t *unitp;
2553 clock_t intvl;
2554 ppm_domain_t *domp = arg;
2555
2556 unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
2557
2558 mutex_enter(&unitp->lock);
2559 if (unitp->led_tid == 0) {
2560 mutex_exit(&unitp->lock);
2561 return;
2562 }
2563
2564 if (domp->status == PPMD_ON) {
2565 ppm_set_led(domp, PPMD_OFF);
2566 intvl = PPM_LEDOFF_INTERVAL;
2567 } else {
2568 ppm_set_led(domp, PPMD_ON);
2569 intvl = PPM_LEDON_INTERVAL;
2570 }
2571
2572 unitp->led_tid = timeout(ppm_blink_led, domp, intvl);
2573 mutex_exit(&unitp->lock);
2574 }
2575
2576 /*
2577 * Function to power up a domain, if required. It also increments the
2578 * domain pwr_cnt to prevent it from going down.
2579 */
2580 static int
ppm_power_up_domain(dev_info_t * dip)2581 ppm_power_up_domain(dev_info_t *dip)
2582 {
2583 int ret = DDI_SUCCESS;
2584 ppm_domain_t *domp;
2585 char *str = "ppm_power_up_domain";
2586
2587 domp = ppm_lookup_dev(dip);
2588 ASSERT(domp);
2589 mutex_enter(&domp->lock);
2590 switch (domp->model) {
2591 case PPMD_FET:
2592 if (domp->status == PPMD_OFF) {
2593 if ((ret = ppm_fetset(domp, PPMD_ON)) ==
2594 DDI_SUCCESS) {
2595 PPMD(D_FET, ("%s: turned on fet for %s@%s\n",
2596 str, PM_NAME(dip), PM_ADDR(dip)))
2597 } else {
2598 PPMD(D_FET, ("%s: couldn't turn on fet "
2599 "for %s@%s\n", str, PM_NAME(dip),
2600 PM_ADDR(dip)))
2601 }
2602 }
2603 break;
2604
2605 case PPMD_PCI:
2606 case PPMD_PCI_PROP:
2607 if (domp->status == PPMD_OFF) {
2608 if ((ret = ppm_switch_clock(domp, PPMD_ON)) ==
2609 DDI_SUCCESS) {
2610 PPMD(D_PCI, ("%s: turned on clock for "
2611 "%s@%s\n", str, PM_NAME(dip),
2612 PM_ADDR(dip)))
2613 } else {
2614 PPMD(D_PCI, ("%s: couldn't turn on clock "
2615 "for %s@%s\n", str, PM_NAME(dip),
2616 PM_ADDR(dip)))
2617 }
2618 }
2619 break;
2620
2621 case PPMD_PCIE:
2622 if (domp->status == PPMD_OFF) {
2623 if ((ret = ppm_pcie_pwr(domp, PPMD_ON)) ==
2624 DDI_SUCCESS) {
2625 PPMD(D_PCI, ("%s: turned on link for "
2626 "%s@%s\n", str, PM_NAME(dip),
2627 PM_ADDR(dip)))
2628 } else {
2629 PPMD(D_PCI, ("%s: couldn't turn on link "
2630 "for %s@%s\n", str, PM_NAME(dip),
2631 PM_ADDR(dip)))
2632 }
2633 }
2634 break;
2635
2636 default:
2637 break;
2638 }
2639 if (ret == DDI_SUCCESS)
2640 domp->pwr_cnt++;
2641 mutex_exit(&domp->lock);
2642 return (ret);
2643 }
2644
2645 /*
2646 * Decrements the domain pwr_cnt. if conditions to power down the domain
2647 * are met, powers down the domain,.
2648 */
2649 static int
ppm_power_down_domain(dev_info_t * dip)2650 ppm_power_down_domain(dev_info_t *dip)
2651 {
2652 int ret = DDI_SUCCESS;
2653 char *str = "ppm_power_down_domain";
2654 ppm_domain_t *domp;
2655
2656 domp = ppm_lookup_dev(dip);
2657 ASSERT(domp);
2658 mutex_enter(&domp->lock);
2659 ASSERT(domp->pwr_cnt > 0);
2660 domp->pwr_cnt--;
2661 switch (domp->model) {
2662 case PPMD_FET:
2663 if ((domp->pwr_cnt == 0) &&
2664 (ppm_cpr_window_flag == B_FALSE) &&
2665 ppm_none_else_holds_power(domp)) {
2666 if ((ret = ppm_fetset(domp, PPMD_OFF)) ==
2667 DDI_SUCCESS) {
2668 PPMD(D_FET, ("%s: turned off FET for %s@%s \n",
2669 str, PM_NAME(dip), PM_ADDR(dip)))
2670 } else {
2671 PPMD(D_FET, ("%s: couldn't turn off FET for "
2672 " %s@%s\n", str, PM_NAME(dip),
2673 PM_ADDR(dip)))
2674 }
2675 }
2676 break;
2677
2678 case PPMD_PCI:
2679 case PPMD_PCI_PROP:
2680 if ((domp->pwr_cnt == 0) &&
2681 (ppm_cpr_window_flag == B_FALSE) &&
2682 ppm_none_else_holds_power(domp)) {
2683 if ((ret = ppm_switch_clock(domp, PPMD_OFF)) ==
2684 DDI_SUCCESS) {
2685 PPMD(D_PCI, ("%s: turned off clock for %s@%s\n",
2686 str, PM_NAME(dip), PM_ADDR(dip)))
2687 } else {
2688 PPMD(D_PCI, ("%s: couldn't turn off clock "
2689 "for %s@%s\n", str, PM_NAME(dip),
2690 PM_ADDR(dip)))
2691 }
2692 }
2693 break;
2694
2695 case PPMD_PCIE:
2696 if ((domp->pwr_cnt == 0) &&
2697 (ppm_cpr_window_flag == B_FALSE) &&
2698 ppm_none_else_holds_power(domp)) {
2699 if ((ret = ppm_pcie_pwr(domp, PPMD_OFF)) ==
2700 DDI_SUCCESS) {
2701 PPMD(D_PCI, ("%s: turned off link for %s@%s\n",
2702 str, PM_NAME(dip), PM_ADDR(dip)))
2703 } else {
2704 PPMD(D_PCI, ("%s: couldn't turn off link "
2705 "for %s@%s\n", str, PM_NAME(dip),
2706 PM_ADDR(dip)))
2707 }
2708 }
2709 break;
2710
2711 default:
2712 break;
2713 }
2714 mutex_exit(&domp->lock);
2715 return (ret);
2716 }
2717
2718 static int
ppm_manage_sx(s3a_t * s3ap,int enter)2719 ppm_manage_sx(s3a_t *s3ap, int enter)
2720 {
2721 ppm_domain_t *domp = ppm_lookup_domain("domain_estar");
2722 ppm_dc_t *dc;
2723 int ret = 0;
2724
2725 if (domp == NULL) {
2726 PPMD(D_CPR, ("ppm_manage_sx: can't find estar domain\n"))
2727 return (ENODEV);
2728 }
2729 PPMD(D_CPR, ("ppm_manage_sx %x, enter %d\n", s3ap->s3a_state,
2730 enter))
2731 switch (s3ap->s3a_state) {
2732 case S3:
2733 if (enter) {
2734 dc = ppm_lookup_dc(domp, PPMDC_ENTER_S3);
2735 } else {
2736 dc = ppm_lookup_dc(domp, PPMDC_EXIT_S3);
2737 }
2738 ASSERT(dc && dc->method == PPMDC_KIO);
2739 PPMD(D_CPR,
2740 ("ppm_manage_sx: calling acpi driver (handle %p)"
2741 " with %x\n", (void *)dc->lh, dc->m_un.kio.iowr))
2742 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
2743 (intptr_t)s3ap, FWRITE | FKIOCTL, kcred, NULL);
2744 break;
2745
2746 case S4:
2747 /* S4 is not supported yet */
2748 return (EINVAL);
2749 default:
2750 ASSERT(0);
2751 }
2752 return (ret);
2753 }
2754
2755 /*
2756 * Search enable/disable lists, which are encoded in ppm.conf as an array
2757 * of char strings.
2758 */
2759 static int
ppm_search_list(pm_searchargs_t * sl)2760 ppm_search_list(pm_searchargs_t *sl)
2761 {
2762 int i;
2763 int flags = DDI_PROP_DONTPASS;
2764 ppm_unit_t *unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
2765 char **pp;
2766 char *starp;
2767 uint_t nelements;
2768 char *manuf = sl->pms_manufacturer;
2769 char *prod = sl->pms_product;
2770
2771 if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, unitp->dip, flags,
2772 sl->pms_listname, &pp, &nelements) != DDI_PROP_SUCCESS) {
2773 PPMD(D_CPR, ("ppm_search_list prop lookup %s failed--EINVAL\n",
2774 sl->pms_listname))
2775 return (EINVAL);
2776 }
2777 ASSERT((nelements & 1) == 0); /* must be even */
2778
2779 PPMD(D_CPR, ("ppm_search_list looking for %s, %s\n", manuf, prod))
2780
2781 for (i = 0; i < nelements; i += 2) {
2782 PPMD(D_CPR, ("checking %s, %s", pp[i], pp[i+1]))
2783 /* we support only a trailing '*' pattern match */
2784 if ((starp = strchr(pp[i], '*')) != NULL && *(starp + 1) == 0) {
2785 /* LINTED - ptrdiff overflow */
2786 if (strncmp(manuf, pp[i], (starp - pp[i])) != 0) {
2787 PPMD(D_CPR, (" no match %s with %s\n",
2788 manuf, pp[i + 1]))
2789 continue;
2790 }
2791 }
2792 if ((starp = strchr(pp[i + 1], '*')) != NULL &&
2793 *(starp + 1) == 0) {
2794 if (strncmp(prod,
2795 /* LINTED - ptrdiff overflow */
2796 pp[i + 1], (starp - pp[i + 1])) != 0) {
2797 PPMD(D_CPR, (" no match %s with %s\n",
2798 prod, pp[i + 1]))
2799 continue;
2800 }
2801 }
2802 if (strcmp(manuf, pp[i]) == 0 &&
2803 (strcmp(prod, pp[i + 1]) == 0)) {
2804 PPMD(D_CPR, (" match\n"))
2805 ddi_prop_free(pp);
2806 return (0);
2807 }
2808 PPMD(D_CPR, (" no match %s with %s or %s with %s\n",
2809 manuf, pp[i], prod, pp[i + 1]))
2810 }
2811 ddi_prop_free(pp);
2812 return (ENODEV);
2813 }
2814