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