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 domp = NULL;
771 #ifdef DEBUG
772 char *str = "ppm_ctlops";
773 int mask = ppm_debug & (D_CTLOPS1 | D_CTLOPS2);
774 char *ctlstr = ppm_get_ctlstr(reqp->request_type, mask);
775 if (mask && ctlstr)
776 PPMD(mask, ("%s: %s, %s\n",
777 str, ddi_binding_name(rdip), ctlstr))
778 #endif
779
780 if (ctlop != DDI_CTLOPS_POWER) {
781 return (DDI_FAILURE);
782 }
783
784 unitp = (ppm_unit_t *)ddi_get_soft_state(ppm_statep, ppm_inst);
785
786 switch (reqp->request_type) {
787
788 /* attempt to blink led if indeed all at lowest */
789 case PMR_PPM_ALL_LOWEST:
790 mode = (reqp->req.ppm_all_lowest_req.mode == PM_ALL_LOWEST);
791 if (!(unitp->states & PPM_STATE_SUSPENDED) && mode)
792 ppm_manage_led(PPM_LED_BLINKING);
793 else
794 ppm_manage_led(PPM_LED_SOLIDON);
795 return (DDI_SUCCESS);
796
797 /* undo the claiming of 'rdip' at attach time */
798 case PMR_PPM_POST_DETACH:
799 ASSERT(reqp->req.ppm_set_power_req.who == rdip);
800 mutex_enter(&unitp->lock);
801 if (reqp->req.ppm_config_req.result != DDI_SUCCESS ||
802 (PPM_GET_PRIVATE(rdip) == NULL)) {
803 mutex_exit(&unitp->lock);
804 return (DDI_FAILURE);
805 }
806 mutex_exit(&unitp->lock);
807 ppm_rem_dev(rdip);
808 return (DDI_SUCCESS);
809
810 /* chance to adjust pwr_cnt if resume is about to power up rdip */
811 case PMR_PPM_PRE_RESUME:
812 ppm_svc_resume_ctlop(rdip, reqp);
813 return (DDI_SUCCESS);
814
815 /*
816 * synchronizing, so that only the owner of the power lock is
817 * permitted to change device and component's power level.
818 */
819 case PMR_PPM_UNLOCK_POWER:
820 case PMR_PPM_TRY_LOCK_POWER:
821 case PMR_PPM_LOCK_POWER:
822 ppmd = PPM_GET_PRIVATE(rdip);
823 if (ppmd)
824 domp = ppmd->domp;
825 else if (reqp->request_type != PMR_PPM_UNLOCK_POWER) {
826 domp = ppm_lookup_dev(rdip);
827 ASSERT(domp);
828 ppmd = ppm_get_dev(rdip, domp);
829 }
830
831 if (domp == NULL)
832 return (DDI_FAILURE);
833
834 PPMD(D_LOCKS, ("ppm_lock_%s: %s, %s\n",
835 (domp->dflags & PPMD_LOCK_ALL) ? "all" : "one",
836 ppmd->path, ppm_get_ctlstr(reqp->request_type, D_LOCKS)))
837
838 if (domp->dflags & PPMD_LOCK_ALL)
839 ppm_lock_all(domp, reqp, result);
840 else
841 ppm_lock_one(ppmd, reqp, result);
842 return (DDI_SUCCESS);
843
844 case PMR_PPM_POWER_LOCK_OWNER:
845 ASSERT(reqp->req.ppm_power_lock_owner_req.who == rdip);
846 ppmd = PPM_GET_PRIVATE(rdip);
847 if (ppmd) {
848 domp = ppmd->domp;
849 } else {
850 domp = ppm_lookup_dev(rdip);
851 ASSERT(domp);
852 ppmd = ppm_get_dev(rdip, domp);
853 }
854
855 if (domp == NULL)
856 return (DDI_FAILURE);
857
858 /*
859 * In case of LOCK_ALL, effective owner of the power lock
860 * is the owner of the domain lock. otherwise, it is the owner
861 * of the power lock.
862 */
863 if (domp->dflags & PPMD_LOCK_ALL)
864 reqp->req.ppm_power_lock_owner_req.owner =
865 mutex_owner(&domp->lock);
866 else {
867 reqp->req.ppm_power_lock_owner_req.owner =
868 DEVI(rdip)->devi_busy_thread;
869 }
870 return (DDI_SUCCESS);
871
872 case PMR_PPM_INIT_CHILD:
873 ASSERT(reqp->req.ppm_lock_power_req.who == rdip);
874 if ((domp = ppm_lookup_dev(rdip)) == NULL)
875 return (DDI_SUCCESS);
876
877 /*
878 * We keep track of power-manageable devices starting with
879 * initialization process. The initializing flag remains
880 * set until it is cleared by ppm_add_dev(). Power management
881 * policy for some domains are affected even during device
882 * initialization. For example, PCI domains should leave
883 * their clock running meanwhile a device in that domain
884 * is initializing.
885 */
886 mutex_enter(&domp->lock);
887 owned = ppm_add_owned(rdip, domp);
888 ASSERT(owned->initializing == 0);
889 owned->initializing = 1;
890
891 if (PPMD_IS_PCI(domp->model) && domp->status == PPMD_OFF) {
892 ret = ppm_switch_clock(domp, PPMD_ON);
893 if (ret == DDI_SUCCESS)
894 domp->dflags |= PPMD_INITCHILD_CLKON;
895 }
896 mutex_exit(&domp->lock);
897 return (ret);
898
899 case PMR_PPM_POST_ATTACH:
900 ASSERT(reqp->req.ppm_config_req.who == rdip);
901 domp = ppm_lookup_dev(rdip);
902 ASSERT(domp);
903 ASSERT(domp->status == PPMD_ON);
904 if (reqp->req.ppm_config_req.result == DDI_SUCCESS) {
905 /*
906 * call ppm_get_dev, which will increment the
907 * domain power count by the right number.
908 * Undo the power count increment, done in PRE_PROBE.
909 */
910 if (PM_GET_PM_INFO(rdip))
911 ppmd = ppm_get_dev(rdip, domp);
912 mutex_enter(&domp->lock);
913 ASSERT(domp->pwr_cnt > 0);
914 domp->pwr_cnt--;
915 mutex_exit(&domp->lock);
916 return (DDI_SUCCESS);
917 }
918
919 ret = ppm_power_down_domain(rdip);
920 /* FALLTHROUGH */
921 case PMR_PPM_UNINIT_CHILD:
922 ASSERT(reqp->req.ppm_lock_power_req.who == rdip);
923 if ((domp = ppm_lookup_dev(rdip)) == NULL)
924 return (DDI_SUCCESS);
925
926 (void) ddi_pathname(rdip, path);
927 mutex_enter(&domp->lock);
928 for (owned = domp->owned; owned; owned = owned->next)
929 if (strcmp(owned->path, path) == 0)
930 break;
931
932 /*
933 * In case we didn't go through a complete attach and detach,
934 * the initializing flag will still be set, so clear it.
935 */
936 if ((owned != NULL) && (owned->initializing))
937 owned->initializing = 0;
938
939 if (PPMD_IS_PCI(domp->model) &&
940 domp->status == PPMD_ON && domp->pwr_cnt == 0 &&
941 (domp->dflags & PPMD_INITCHILD_CLKON) &&
942 ppm_none_else_holds_power(domp)) {
943 ret = ppm_switch_clock(domp, PPMD_OFF);
944 if (ret == DDI_SUCCESS)
945 domp->dflags &= ~PPMD_INITCHILD_CLKON;
946 }
947 mutex_exit(&domp->lock);
948 return (ret);
949
950 /* place holders */
951 case PMR_PPM_UNMANAGE:
952 case PMR_PPM_PRE_DETACH:
953 return (DDI_SUCCESS);
954
955 case PMR_PPM_PRE_PROBE:
956 ASSERT(reqp->req.ppm_config_req.who == rdip);
957 return (ppm_power_up_domain(rdip));
958
959 case PMR_PPM_POST_PROBE:
960 ASSERT(reqp->req.ppm_config_req.who == rdip);
961 if (reqp->req.ppm_config_req.result == DDI_PROBE_SUCCESS ||
962 reqp->req.ppm_config_req.result == DDI_PROBE_DONTCARE)
963 return (DDI_SUCCESS);
964
965 /* Probe failed */
966 PPMD(D_CTLOPS1 | D_CTLOPS2, ("%s: probe failed for %s@%s "
967 "rv %d\n", str, PM_NAME(rdip), PM_ADDR(rdip),
968 reqp->req.ppm_config_req.result))
969 return (ppm_power_down_domain(rdip));
970
971 case PMR_PPM_PRE_ATTACH:
972 ASSERT(reqp->req.ppm_config_req.who == rdip);
973 /* Domain has already been powered up in PRE_PROBE */
974 domp = ppm_lookup_dev(rdip);
975 ASSERT(domp);
976 ASSERT(domp->status == PPMD_ON);
977 return (DDI_SUCCESS);
978
979 /* ppm intercepts power change process to the claimed devices */
980 case PMR_PPM_SET_POWER:
981 case PMR_PPM_POWER_CHANGE_NOTIFY:
982 if ((ppmd = PPM_GET_PRIVATE(rdip)) == NULL) {
983 domp = ppm_lookup_dev(rdip);
984 ASSERT(domp);
985 ppmd = ppm_get_dev(rdip, domp);
986 }
987 switch (ppmd->domp->model) {
988 case PPMD_CPU:
989 return (ppm_manage_cpus(rdip, reqp, result));
990 case PPMD_FET:
991 return (ppm_manage_fet(rdip, reqp, result));
992 case PPMD_PCI:
993 case PPMD_PCI_PROP:
994 return (ppm_manage_pci(rdip, reqp, result));
995 case PPMD_PCIE:
996 return (ppm_manage_pcie(rdip, reqp, result));
997 default:
998 cmn_err(CE_WARN, "ppm_ctlops: domain model %d does"
999 " not support PMR_PPM_SET_POWER ctlop",
1000 ppmd->domp->model);
1001 return (DDI_FAILURE);
1002 }
1003
1004 case PMR_PPM_ENTER_SX:
1005 case PMR_PPM_EXIT_SX:
1006 s3args.s3a_state = reqp->req.ppm_power_enter_sx_req.sx_state;
1007 s3args.s3a_test_point =
1008 reqp->req.ppm_power_enter_sx_req.test_point;
1009 s3args.s3a_wakephys = reqp->req.ppm_power_enter_sx_req.wakephys;
1010 s3args.s3a_psr = reqp->req.ppm_power_enter_sx_req.psr;
1011 ret = ppm_manage_sx(&s3args,
1012 reqp->request_type == PMR_PPM_ENTER_SX);
1013 if (ret) {
1014 PPMD(D_CPR, ("ppm_manage_sx returns %d\n", ret))
1015 return (DDI_FAILURE);
1016 } else {
1017 return (DDI_SUCCESS);
1018 }
1019
1020 case PMR_PPM_SEARCH_LIST:
1021 ret = ppm_search_list(reqp->req.ppm_search_list_req.searchlist);
1022 reqp->req.ppm_search_list_req.result = ret;
1023 *res = ret;
1024 if (ret) {
1025 PPMD(D_CPR, ("ppm_search_list returns %d\n", ret))
1026 return (DDI_FAILURE);
1027 } else {
1028 PPMD(D_CPR, ("ppm_search_list returns %d\n", ret))
1029 return (DDI_SUCCESS);
1030 }
1031
1032 default:
1033 cmn_err(CE_WARN, "ppm_ctlops: unrecognized ctlops req(%d)",
1034 reqp->request_type);
1035 return (DDI_FAILURE);
1036 }
1037 }
1038
1039
1040 /*
1041 * Raise the power level of a subrange of cpus. Used when cpu driver
1042 * failed an attempt to lower the power of a cpu (probably because
1043 * it got busy). Need to revert the ones we already changed.
1044 *
1045 * ecpup = the ppm_dev_t for the cpu which failed to lower power
1046 * level = power level to reset prior cpus to
1047 */
1048 int
ppm_revert_cpu_power(ppm_dev_t * ecpup,int level)1049 ppm_revert_cpu_power(ppm_dev_t *ecpup, int level)
1050 {
1051 ppm_dev_t *cpup;
1052 int ret = DDI_SUCCESS;
1053
1054 for (cpup = ecpup->domp->devlist; cpup != ecpup; cpup = cpup->next) {
1055 PPMD(D_CPU, ("ppm_revert_cpu_power: \"%s\", revert to "
1056 "level %d\n", cpup->path, level))
1057
1058 ret = pm_power(cpup->dip, 0, level);
1059 if (ret == DDI_SUCCESS) {
1060 cpup->level = level;
1061 cpup->rplvl = PM_LEVEL_UNKNOWN;
1062 }
1063 }
1064 return (ret);
1065 }
1066
1067
1068 /*
1069 * ppm_manage_cpus - Process a request to change the power level of a cpu.
1070 * If not all cpus want to be at the same level, OR if we are currently
1071 * refusing slowdown requests due to thermal stress, we cache the request.
1072 * Otherwise, set all cpus to the new power level.
1073 */
1074 /* ARGSUSED */
1075 static int
ppm_manage_cpus(dev_info_t * dip,power_req_t * reqp,int * result)1076 ppm_manage_cpus(dev_info_t *dip, power_req_t *reqp, int *result)
1077 {
1078 #ifdef DEBUG
1079 char *str = "ppm_manage_cpus";
1080 #endif
1081 int old, new, ret, kmflag;
1082 ppm_dev_t *ppmd, *cpup;
1083 int change_notify = 0;
1084 pm_ppm_devlist_t *devlist = NULL, *p;
1085 int do_rescan = 0;
1086
1087 *result = DDI_SUCCESS;
1088
1089 switch (reqp->request_type) {
1090 case PMR_PPM_SET_POWER:
1091 break;
1092
1093 case PMR_PPM_POWER_CHANGE_NOTIFY:
1094 change_notify = 1;
1095 break;
1096
1097 default:
1098 return (DDI_FAILURE);
1099 }
1100
1101 ppmd = PPM_GET_PRIVATE(dip);
1102 ASSERT(MUTEX_HELD(&ppmd->domp->lock));
1103 old = reqp->req.ppm_set_power_req.old_level;
1104 new = reqp->req.ppm_set_power_req.new_level;
1105
1106 if (change_notify) {
1107 ppmd->level = new;
1108 ppmd->rplvl = PM_LEVEL_UNKNOWN;
1109
1110 PPMD(D_CPU, ("%s: Notify cpu dip %p power level has changed "
1111 "from %d to %d", str, (void *)dip, old, new))
1112 return (DDI_SUCCESS);
1113 }
1114
1115 if (ppm_manage_early_cpus(dip, new, result))
1116 return (*result);
1117
1118 if (new == ppmd->level) {
1119 PPMD(D_CPU, ("%s: already at power level %d\n", str, new))
1120 return (DDI_SUCCESS);
1121 }
1122
1123 /*
1124 * A request from lower to higher level transition is granted and
1125 * made effective on all cpus. A request from higher to lower must
1126 * be agreed upon by all cpus.
1127 */
1128 ppmd->rplvl = new;
1129 for (cpup = ppmd->domp->devlist; cpup; cpup = cpup->next) {
1130 if (cpup->rplvl == new)
1131 continue;
1132
1133 if (new < old) {
1134 PPMD(D_SOME, ("%s: not all cpus wants to be at new "
1135 "level %d yet.\n", str, new))
1136 return (DDI_SUCCESS);
1137 }
1138
1139 /*
1140 * If a single cpu requests power up, honor the request
1141 * powering up all cpus.
1142 */
1143 if (new > old) {
1144 PPMD(D_SOME, ("%s: powering up device(%s@%s, %p) "
1145 "because of request from dip(%s@%s, %p), "
1146 "need pm_rescan\n", str, PM_NAME(cpup->dip),
1147 PM_ADDR(cpup->dip), (void *)cpup->dip,
1148 PM_NAME(dip), PM_ADDR(dip), (void *)dip))
1149 do_rescan++;
1150 }
1151 }
1152
1153 PPMD(D_SETLVL, ("%s: \"%s\" set power level old %d, new %d \n",
1154 str, ppmd->path, ppmd->level, new))
1155 ret = ppm_change_cpu_power(ppmd, new);
1156 *result = ret;
1157
1158 if (ret == DDI_SUCCESS) {
1159 if (reqp->req.ppm_set_power_req.canblock == PM_CANBLOCK_BLOCK)
1160 kmflag = KM_SLEEP;
1161 else
1162 kmflag = KM_NOSLEEP;
1163
1164 for (cpup = ppmd->domp->devlist; cpup; cpup = cpup->next) {
1165 if (cpup->dip == dip)
1166 continue;
1167
1168 if ((p = kmem_zalloc(sizeof (pm_ppm_devlist_t),
1169 kmflag)) == NULL) {
1170 break;
1171 }
1172 p->ppd_who = cpup->dip;
1173 p->ppd_cmpt = cpup->cmpt;
1174 p->ppd_old_level = old;
1175 p->ppd_new_level = new;
1176 p->ppd_next = devlist;
1177
1178 PPMD(D_SETLVL, ("%s: devlist entry[\"%s\"] %d -> %d\n",
1179 str, cpup->path, old, new))
1180
1181 devlist = p;
1182 }
1183 reqp->req.ppm_set_power_req.cookie = (void *) devlist;
1184
1185 if (do_rescan > 0) {
1186 for (cpup = ppmd->domp->devlist; cpup;
1187 cpup = cpup->next) {
1188 if (cpup->dip == dip)
1189 continue;
1190 pm_rescan(cpup->dip);
1191 }
1192 }
1193 }
1194
1195 return (ret);
1196 }
1197
1198
1199 /*
1200 * ppm_svc_resume_ctlop - this is a small bookkeeping ppm does -
1201 * increments its FET domain power count, in anticipation of that
1202 * the indicated device(dip) would be powered up by its driver as
1203 * a result of cpr resuming.
1204 */
1205 /* ARGSUSED */
1206 static void
ppm_svc_resume_ctlop(dev_info_t * dip,power_req_t * reqp)1207 ppm_svc_resume_ctlop(dev_info_t *dip, power_req_t *reqp)
1208 {
1209 ppm_domain_t *domp;
1210 ppm_dev_t *ppmd;
1211 int powered; /* power up count per dip */
1212
1213 ppmd = PPM_GET_PRIVATE(dip);
1214 if (ppmd == NULL)
1215 return;
1216
1217 /*
1218 * Maintain correct powered count for domain which cares
1219 */
1220 powered = 0;
1221 domp = ppmd->domp;
1222 mutex_enter(&domp->lock);
1223 if ((domp->model == PPMD_FET) || PPMD_IS_PCI(domp->model) ||
1224 (domp->model == PPMD_PCIE)) {
1225 for (ppmd = domp->devlist; ppmd; ppmd = ppmd->next) {
1226 if (ppmd->dip == dip && ppmd->level)
1227 powered++;
1228 }
1229
1230 /*
1231 * All fets and clocks are held on during suspend -
1232 * resume window regardless their domain devices' power
1233 * level.
1234 */
1235 ASSERT(domp->status == PPMD_ON);
1236
1237 /*
1238 * The difference indicates the number of components
1239 * being off prior to suspend operation, that is the
1240 * amount needs to be compensated in order to sync up
1241 * bookkeeping with reality, for PROM reset would have
1242 * brought up all devices.
1243 */
1244 if (powered < PM_NUMCMPTS(dip))
1245 domp->pwr_cnt += PM_NUMCMPTS(dip) - powered;
1246 }
1247 for (ppmd = domp->devlist; ppmd; ppmd = ppmd->next) {
1248 if (ppmd->dip == dip)
1249 ppmd->level = ppmd->rplvl = PM_LEVEL_UNKNOWN;
1250 }
1251 mutex_exit(&domp->lock);
1252 }
1253
1254 #ifdef DEBUG
1255 static int ppmbringup = 0;
1256 #endif
1257
1258 int
ppm_bringup_domains()1259 ppm_bringup_domains()
1260 {
1261 #ifdef DEBUG
1262 char *str = "ppm_bringup_domains";
1263 #endif
1264 ppm_domain_t *domp;
1265 int ret = DDI_SUCCESS;
1266
1267 PPMD(D_CPR, ("%s[%d]: enter\n", str, ++ppmbringup))
1268 for (domp = ppm_domain_p; domp; domp = domp->next) {
1269 if ((!PPMD_IS_PCI(domp->model) && (domp->model != PPMD_FET) &&
1270 (domp->model != PPMD_PCIE)) || (domp->devlist == NULL))
1271 continue;
1272
1273 mutex_enter(&domp->lock);
1274 if (domp->status == PPMD_ON) {
1275 mutex_exit(&domp->lock);
1276 continue;
1277 }
1278 switch (domp->model) {
1279 case PPMD_FET:
1280 ret = ppm_fetset(domp, PPMD_ON);
1281 break;
1282 case PPMD_PCI:
1283 case PPMD_PCI_PROP:
1284 ret = ppm_switch_clock(domp, PPMD_ON);
1285 break;
1286 case PPMD_PCIE:
1287 ret = ppm_pcie_pwr(domp, PPMD_ON);
1288 break;
1289 default:
1290 break;
1291 }
1292 mutex_exit(&domp->lock);
1293 }
1294 PPMD(D_CPR, ("%s[%d]: exit\n", str, ppmbringup))
1295
1296 return (ret);
1297 }
1298
1299 #ifdef DEBUG
1300 static int ppmsyncbp = 0;
1301 #endif
1302
1303 int
ppm_sync_bookkeeping()1304 ppm_sync_bookkeeping()
1305 {
1306 #ifdef DEBUG
1307 char *str = "ppm_sync_bookkeeping";
1308 #endif
1309 ppm_domain_t *domp;
1310 int ret = DDI_SUCCESS;
1311
1312 PPMD(D_CPR, ("%s[%d]: enter\n", str, ++ppmsyncbp))
1313 for (domp = ppm_domain_p; domp; domp = domp->next) {
1314 if ((!PPMD_IS_PCI(domp->model) && (domp->model != PPMD_FET) &&
1315 (domp->model != PPMD_PCIE)) || (domp->devlist == NULL))
1316 continue;
1317
1318 mutex_enter(&domp->lock);
1319 if ((domp->pwr_cnt != 0) || !ppm_none_else_holds_power(domp)) {
1320 mutex_exit(&domp->lock);
1321 continue;
1322 }
1323
1324 /*
1325 * skip NULL .devlist slot, for some may host pci device
1326 * that can not tolerate clock off or not even participate
1327 * in PM.
1328 */
1329 if (domp->devlist == NULL)
1330 continue;
1331
1332 switch (domp->model) {
1333 case PPMD_FET:
1334 ret = ppm_fetset(domp, PPMD_OFF);
1335 break;
1336 case PPMD_PCI:
1337 case PPMD_PCI_PROP:
1338 ret = ppm_switch_clock(domp, PPMD_OFF);
1339 break;
1340 case PPMD_PCIE:
1341 ret = ppm_pcie_pwr(domp, PPMD_OFF);
1342 break;
1343 default:
1344 break;
1345 }
1346 mutex_exit(&domp->lock);
1347 }
1348 PPMD(D_CPR, ("%s[%d]: exit\n", str, ppmsyncbp))
1349
1350 return (ret);
1351 }
1352
1353
1354
1355 /*
1356 * pre-suspend window;
1357 *
1358 * power up every FET and PCI clock that are off;
1359 *
1360 * set ppm_cpr_window global flag to indicate
1361 * that even though all pm_scan requested power transitions
1362 * will be honored as usual but that until we're out
1363 * of this window, no FET or clock will be turned off
1364 * for domains with pwr_cnt decremented down to 0.
1365 * Such is to avoid accessing the orthogonal drivers that own
1366 * the FET and clock registers that may not be resumed yet.
1367 *
1368 * at post-resume window, walk through each FET and PCI domains,
1369 * bring pwr_cnt and domp->status to sense: if pwr-cnt == 0,
1370 * and noinvol check okays, power down the FET or PCI. At last,
1371 * clear the global flag ppm_cpr_window.
1372 *
1373 * ASSERT case 1, during cpr window, checks pwr_cnt against power
1374 * transitions;
1375 * ASSERT case 2, out of cpr window, checks four things:
1376 * pwr_cnt <> power transition in/out of 0
1377 * <> status <> record of noinvol device detached
1378 *
1379 */
1380 /* ARGSUSED */
1381 static boolean_t
ppm_cpr_callb(void * arg,int code)1382 ppm_cpr_callb(void *arg, int code)
1383 {
1384 int ret;
1385
1386 switch (code) {
1387 case CB_CODE_CPR_CHKPT:
1388
1389 /* pre-suspend: start of cpr window */
1390 mutex_enter(&ppm_cpr_window_lock);
1391 ASSERT(ppm_cpr_window_flag == B_FALSE);
1392 ppm_cpr_window_flag = B_TRUE;
1393 mutex_exit(&ppm_cpr_window_lock);
1394
1395 ret = ppm_bringup_domains();
1396
1397 break;
1398
1399 case CB_CODE_CPR_RESUME:
1400
1401 /* post-resume: end of cpr window */
1402 ret = ppm_sync_bookkeeping();
1403
1404 mutex_enter(&ppm_cpr_window_lock);
1405 ASSERT(ppm_cpr_window_flag == B_TRUE);
1406 ppm_cpr_window_flag = B_FALSE;
1407 mutex_exit(&ppm_cpr_window_lock);
1408
1409 break;
1410 default:
1411 ret = DDI_SUCCESS;
1412 break;
1413 }
1414
1415 return (ret == DDI_SUCCESS);
1416 }
1417
1418
1419 /*
1420 * Initialize our private version of real power level
1421 * as well as lowest and highest levels the device supports;
1422 * relate to ppm_add_dev
1423 */
1424 void
ppm_dev_init(ppm_dev_t * ppmd)1425 ppm_dev_init(ppm_dev_t *ppmd)
1426 {
1427 struct pm_component *dcomps;
1428 struct pm_comp *pm_comp;
1429 dev_info_t *dip;
1430 int maxi, i;
1431
1432 ASSERT(MUTEX_HELD(&ppmd->domp->lock));
1433 ppmd->level = PM_LEVEL_UNKNOWN;
1434 ppmd->rplvl = PM_LEVEL_UNKNOWN;
1435
1436 /* increment pwr_cnt per component */
1437 if ((ppmd->domp->model == PPMD_FET) ||
1438 PPMD_IS_PCI(ppmd->domp->model) ||
1439 (ppmd->domp->model == PPMD_PCIE))
1440 ppmd->domp->pwr_cnt++;
1441
1442 dip = ppmd->dip;
1443
1444 /*
1445 * ppm exists to handle power-manageable devices which require
1446 * special handling on the current platform. However, a
1447 * driver for such a device may choose not to support power
1448 * management on a particular load/attach. In this case we
1449 * we create a structure to represent a single-component device
1450 * for which "level" = PM_LEVEL_UNKNOWN and "lowest" = 0
1451 * are effectively constant.
1452 */
1453 if (PM_GET_PM_INFO(dip)) {
1454 dcomps = DEVI(dip)->devi_pm_components;
1455 pm_comp = &dcomps[ppmd->cmpt].pmc_comp;
1456
1457 ppmd->lowest = pm_comp->pmc_lvals[0];
1458 ASSERT(ppmd->lowest >= 0);
1459 maxi = pm_comp->pmc_numlevels - 1;
1460 ppmd->highest = pm_comp->pmc_lvals[maxi];
1461
1462 /*
1463 * If 66mhz PCI device on pci 66mhz bus supports D2 state
1464 * (config reg PMC bit 10 set), ppm could turn off its bus
1465 * clock once it is at D3hot.
1466 */
1467 if (ppmd->domp->dflags & PPMD_PCI66MHZ) {
1468 for (i = 0; i < maxi; i++)
1469 if (pm_comp->pmc_lvals[i] == PM_LEVEL_D2) {
1470 ppmd->flags |= PPMDEV_PCI66_D2;
1471 break;
1472 }
1473 }
1474 }
1475
1476 /*
1477 * If device is in PCI_PROP domain and has exported the
1478 * property listed in ppm.conf, its clock will be turned
1479 * off when all pm'able devices in that domain are at D3.
1480 */
1481 if ((ppmd->domp->model == PPMD_PCI_PROP) &&
1482 (ppmd->domp->propname != NULL) &&
1483 ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1484 ppmd->domp->propname))
1485 ppmd->flags |= PPMDEV_PCI_PROP_CLKPM;
1486 }
1487
1488
1489 /*
1490 * relate to ppm_rem_dev
1491 */
1492 void
ppm_dev_fini(ppm_dev_t * ppmd)1493 ppm_dev_fini(ppm_dev_t *ppmd)
1494 {
1495 ASSERT(MUTEX_HELD(&ppmd->domp->lock));
1496
1497 /* decrement pwr_cnt per component */
1498 if ((ppmd->domp->model == PPMD_FET) ||
1499 PPMD_IS_PCI(ppmd->domp->model) ||
1500 (ppmd->domp->model == PPMD_PCIE))
1501 if (ppmd->level != ppmd->lowest)
1502 ppmd->domp->pwr_cnt--;
1503 }
1504
1505 /*
1506 * Each power fet controls the power of one or more platform
1507 * device(s) within their domain. Hence domain devices' power
1508 * level change has been monitored, such that once all devices
1509 * are powered off, the fet is turned off to save more power.
1510 *
1511 * To power on any domain device, the domain power fet
1512 * needs to be turned on first. always one fet per domain.
1513 */
1514 static int
ppm_manage_fet(dev_info_t * dip,power_req_t * reqp,int * result)1515 ppm_manage_fet(dev_info_t *dip, power_req_t *reqp, int *result)
1516 {
1517 #ifdef DEBUG
1518 char *str = "ppm_manage_fet";
1519 #endif
1520 int (*pwr_func)(ppm_dev_t *, int, int);
1521 int new, old, cmpt;
1522 ppm_dev_t *ppmd;
1523 ppm_domain_t *domp;
1524 int incr = 0;
1525 int dummy_ret;
1526
1527
1528 *result = DDI_SUCCESS;
1529 switch (reqp->request_type) {
1530 case PMR_PPM_SET_POWER:
1531 pwr_func = ppm_change_power_level;
1532 old = reqp->req.ppm_set_power_req.old_level;
1533 new = reqp->req.ppm_set_power_req.new_level;
1534 cmpt = reqp->req.ppm_set_power_req.cmpt;
1535 break;
1536 case PMR_PPM_POWER_CHANGE_NOTIFY:
1537 pwr_func = ppm_record_level_change;
1538 old = reqp->req.ppm_notify_level_req.old_level;
1539 new = reqp->req.ppm_notify_level_req.new_level;
1540 cmpt = reqp->req.ppm_notify_level_req.cmpt;
1541 break;
1542 default:
1543 *result = DDI_FAILURE;
1544 PPMD(D_FET, ("%s: unknown request type %d for %s@%s\n",
1545 str, reqp->request_type, PM_NAME(dip), PM_ADDR(dip)))
1546 return (DDI_FAILURE);
1547 }
1548
1549 for (ppmd = PPM_GET_PRIVATE(dip); ppmd; ppmd = ppmd->next)
1550 if (cmpt == ppmd->cmpt)
1551 break;
1552 if (!ppmd) {
1553 PPMD(D_FET, ("%s: dip(%p): old(%d)->new(%d): no ppm_dev"
1554 " found for cmpt(%d)", str, (void *)dip, old, new, cmpt))
1555 *result = DDI_FAILURE;
1556 return (DDI_FAILURE);
1557 }
1558 domp = ppmd->domp;
1559 PPMD(D_FET, ("%s: %s@%s %s old %d, new %d, c%d, level %d, "
1560 "status %s\n", str, PM_NAME(dip), PM_ADDR(dip),
1561 ppm_get_ctlstr(reqp->request_type, ~0), old, new, cmpt,
1562 ppmd->level, (domp->status == PPMD_OFF ? "off" : "on")))
1563
1564
1565 ASSERT(old == ppmd->level);
1566
1567 if (new == ppmd->level) {
1568 PPMD(D_FET, ("nop\n"))
1569 return (DDI_SUCCESS);
1570 }
1571
1572 PPM_LOCK_DOMAIN(domp);
1573
1574 /*
1575 * In general, a device's published lowest power level does not
1576 * have to be 0 if power-off is not tolerated. i.e. a device
1577 * instance may export its lowest level > 0. It is reasonable to
1578 * assume that level 0 indicates off state, positive level values
1579 * indicate power states above off, include full power state.
1580 */
1581 if (new > 0) { /* device powering up or to different positive level */
1582 if (domp->status == PPMD_OFF) {
1583
1584 /* can not be in (chpt, resume) window */
1585 ASSERT(ppm_cpr_window_flag == B_FALSE);
1586
1587 ASSERT(old == 0 && domp->pwr_cnt == 0);
1588
1589 PPMD(D_FET, ("About to turn fet on for %s@%s c%d\n",
1590 PM_NAME(dip), PM_ADDR(dip), cmpt))
1591
1592 *result = ppm_fetset(domp, PPMD_ON);
1593 if (*result != DDI_SUCCESS) {
1594 PPMD(D_FET, ("\tCan't turn on power FET: "
1595 "ret(%d)\n", *result))
1596 PPM_UNLOCK_DOMAIN(domp);
1597 return (DDI_FAILURE);
1598 }
1599 }
1600
1601 /*
1602 * If powering up, pre-increment the count before
1603 * calling pwr_func, because we are going to release
1604 * the domain lock and another thread might turn off
1605 * domain power otherwise.
1606 */
1607 if (old == 0) {
1608 domp->pwr_cnt++;
1609 incr = 1;
1610 }
1611
1612 PPMD(D_FET, ("\t%s domain power count: %d\n",
1613 domp->name, domp->pwr_cnt))
1614 }
1615
1616
1617 PPM_UNLOCK_DOMAIN(domp);
1618
1619 ASSERT(domp->pwr_cnt > 0);
1620
1621 if ((*result = (*pwr_func)(ppmd, cmpt, new)) != DDI_SUCCESS) {
1622 PPMD(D_FET, ("\t%s power change failed: ret(%d)\n",
1623 ppmd->path, *result))
1624 }
1625
1626 PPM_LOCK_DOMAIN(domp);
1627
1628 /*
1629 * Decr the power count in two cases:
1630 *
1631 * 1) request was to power device down and was successful
1632 * 2) request was to power up (we pre-incremented count), but failed.
1633 */
1634 if ((*result == DDI_SUCCESS && ppmd->level == 0) ||
1635 (*result != DDI_SUCCESS && incr)) {
1636 ASSERT(domp->pwr_cnt > 0);
1637 domp->pwr_cnt--;
1638 }
1639
1640 PPMD(D_FET, ("\t%s domain power count: %d\n",
1641 domp->name, domp->pwr_cnt))
1642
1643 /*
1644 * call to pwr_func will update ppm data structures, if it
1645 * succeeds. ppm should return whatever is the return value
1646 * from call to pwr_func. This way pm and ppm data structures
1647 * always in sync. Use dummy_ret from here for any further
1648 * return values.
1649 */
1650 if ((domp->pwr_cnt == 0) &&
1651 (ppm_cpr_window_flag == B_FALSE) &&
1652 ppm_none_else_holds_power(domp)) {
1653
1654 PPMD(D_FET, ("About to turn FET off for %s@%s c%d\n",
1655 PM_NAME(dip), PM_ADDR(dip), cmpt))
1656
1657 dummy_ret = ppm_fetset(domp, PPMD_OFF);
1658 if (dummy_ret != DDI_SUCCESS) {
1659 PPMD(D_FET, ("\tCan't turn off FET: ret(%d)\n",
1660 dummy_ret))
1661 }
1662 }
1663
1664 PPM_UNLOCK_DOMAIN(domp);
1665 ASSERT(domp->pwr_cnt >= 0);
1666 return (*result);
1667 }
1668
1669
1670 /*
1671 * the actual code that turn on or off domain power fet and
1672 * update domain status
1673 */
1674 static int
ppm_fetset(ppm_domain_t * domp,uint8_t value)1675 ppm_fetset(ppm_domain_t *domp, uint8_t value)
1676 {
1677 char *str = "ppm_fetset";
1678 int key;
1679 ppm_dc_t *dc;
1680 int ret;
1681 clock_t temp;
1682 clock_t delay = 0;
1683
1684 key = (value == PPMD_ON) ? PPMDC_FET_ON : PPMDC_FET_OFF;
1685 for (dc = domp->dc; dc; dc = dc->next)
1686 if (dc->cmd == key)
1687 break;
1688 if (!dc || !dc->lh) {
1689 PPMD(D_FET, ("%s: %s domain: NULL ppm_dc handle\n",
1690 str, domp->name))
1691 return (DDI_FAILURE);
1692 }
1693
1694 if (key == PPMDC_FET_ON) {
1695 PPM_GET_IO_DELAY(dc, delay);
1696 if (delay > 0 && domp->last_off_time > 0) {
1697 /*
1698 * provide any delay required before turning on.
1699 * some devices e.g. Samsung DVD require minimum
1700 * of 1 sec between OFF->ON. no delay is required
1701 * for the first time.
1702 */
1703 temp = ddi_get_lbolt();
1704 temp -= domp->last_off_time;
1705 temp = drv_hztousec(temp);
1706
1707 if (temp < delay) {
1708 /*
1709 * busy wait untill we meet the
1710 * required delay. Since we maintain
1711 * time stamps in terms of clock ticks
1712 * we might wait for longer than required
1713 */
1714 PPMD(D_FET, ("%s : waiting %lu micro seconds "
1715 "before on\n", domp->name,
1716 delay - temp));
1717 drv_usecwait(delay - temp);
1718 }
1719 }
1720 }
1721 switch (dc->method) {
1722 #ifdef sun4u
1723 case PPMDC_I2CKIO: {
1724 i2c_gpio_t i2c_req;
1725 i2c_req.reg_mask = dc->m_un.i2c.mask;
1726 i2c_req.reg_val = dc->m_un.i2c.val;
1727 ret = ldi_ioctl(dc->lh, dc->m_un.i2c.iowr,
1728 (intptr_t)&i2c_req, FWRITE | FKIOCTL, kcred, NULL);
1729 break;
1730 }
1731 #endif
1732
1733 case PPMDC_KIO:
1734 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
1735 (intptr_t)&(dc->m_un.kio.val), FWRITE | FKIOCTL, kcred,
1736 NULL);
1737 break;
1738
1739 default:
1740 PPMD(D_FET, ("\t%s: unsupported domain control method %d\n",
1741 str, domp->dc->method))
1742 return (DDI_FAILURE);
1743 }
1744
1745 PPMD(D_FET, ("%s: %s domain(%s) FET from %s to %s\n", str,
1746 (ret == 0) ? "turned" : "failed to turn",
1747 domp->name,
1748 (domp->status == PPMD_ON) ? "ON" : "OFF",
1749 (value == PPMD_ON) ? "ON" : "OFF"))
1750
1751 if (ret == DDI_SUCCESS) {
1752 domp->status = value;
1753
1754 if (key == PPMDC_FET_OFF)
1755 /*
1756 * record the time, when it is off. time is recorded
1757 * in clock ticks
1758 */
1759 domp->last_off_time = ddi_get_lbolt();
1760
1761 /* implement any post op delay. */
1762 if (key == PPMDC_FET_ON) {
1763 PPM_GET_IO_POST_DELAY(dc, delay);
1764 PPMD(D_FET, ("%s : waiting %lu micro seconds "
1765 "after on\n", domp->name, delay))
1766 if (delay > 0)
1767 drv_usecwait(delay);
1768 }
1769 }
1770
1771 return (ret);
1772 }
1773
1774
1775 /*
1776 * read power fet status
1777 */
1778 static int
ppm_fetget(ppm_domain_t * domp,uint8_t * lvl)1779 ppm_fetget(ppm_domain_t *domp, uint8_t *lvl)
1780 {
1781 char *str = "ppm_fetget";
1782 ppm_dc_t *dc = domp->dc;
1783 uint_t kio_val;
1784 int off_val;
1785 int ret;
1786
1787 if (!dc->lh) {
1788 PPMD(D_FET, ("%s: %s domain NULL ppm_dc layered handle\n",
1789 str, domp->name))
1790 return (DDI_FAILURE);
1791 }
1792 if (!dc->next) {
1793 cmn_err(CE_WARN, "%s: expect both fet on and fet off ops "
1794 "defined, found only one in domain(%s)", str, domp->name);
1795 return (DDI_FAILURE);
1796 }
1797
1798 switch (dc->method) {
1799 #ifdef sun4u
1800 case PPMDC_I2CKIO: {
1801 i2c_gpio_t i2c_req;
1802 i2c_req.reg_mask = dc->m_un.i2c.mask;
1803 ret = ldi_ioctl(dc->lh, dc->m_un.i2c.iord,
1804 (intptr_t)&i2c_req, FWRITE | FKIOCTL, kcred, NULL);
1805
1806 if (ret) {
1807 PPMD(D_FET, ("%s: PPMDC_I2CKIO failed: ret(%d)\n",
1808 str, ret))
1809 return (ret);
1810 }
1811
1812 off_val = (dc->cmd == PPMDC_FET_OFF) ? dc->m_un.i2c.val :
1813 dc->next->m_un.i2c.val;
1814 *lvl = (i2c_req.reg_val == off_val) ? PPMD_OFF : PPMD_ON;
1815
1816 PPMD(D_FET, ("%s: %s domain FET %s\n", str, domp->name,
1817 (i2c_req.reg_val == off_val) ? "OFF" : "ON"))
1818
1819 break;
1820 }
1821 #endif
1822
1823 case PPMDC_KIO:
1824 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iord,
1825 (intptr_t)&kio_val, FWRITE | FKIOCTL, kcred, NULL);
1826 if (ret) {
1827 PPMD(D_FET, ("%s: PPMDC_KIO failed: ret(%d)\n",
1828 str, ret))
1829 return (ret);
1830 }
1831
1832 off_val = (dc->cmd == PPMDC_FET_OFF) ? dc->m_un.kio.val :
1833 dc->next->m_un.kio.val;
1834 *lvl = (kio_val == off_val) ? PPMD_OFF : PPMD_ON;
1835
1836 PPMD(D_FET, ("%s: %s domain FET %s\n", str, domp->name,
1837 (kio_val == off_val) ? "OFF" : "ON"))
1838
1839 break;
1840
1841 default:
1842 PPMD(D_FET, ("%s: unsupported domain control method %d\n",
1843 str, domp->dc->method))
1844 return (DDI_FAILURE);
1845 }
1846
1847 return (DDI_SUCCESS);
1848 }
1849
1850
1851 /*
1852 * the actual code that switches pci clock and update domain status
1853 */
1854 static int
ppm_switch_clock(ppm_domain_t * domp,int onoff)1855 ppm_switch_clock(ppm_domain_t *domp, int onoff)
1856 {
1857 #ifdef DEBUG
1858 char *str = "ppm_switch_clock";
1859 #endif
1860 int cmd, pio_save;
1861 ppm_dc_t *dc;
1862 int ret;
1863 extern int do_polled_io;
1864 extern uint_t cfb_inuse;
1865 ppm_dev_t *pdev;
1866
1867 cmd = (onoff == PPMD_ON) ? PPMDC_CLK_ON : PPMDC_CLK_OFF;
1868 dc = ppm_lookup_dc(domp, cmd);
1869 if (!dc) {
1870 PPMD(D_PCI, ("%s: no ppm_dc found for domain (%s)\n",
1871 str, domp->name))
1872 return (DDI_FAILURE);
1873 }
1874
1875 switch (dc->method) {
1876 case PPMDC_KIO:
1877 /*
1878 * If we're powering up cfb on a Stop-A, we only
1879 * want to do polled i/o to turn ON the clock
1880 */
1881 pio_save = do_polled_io;
1882 if ((cfb_inuse) && (cmd == PPMDC_CLK_ON)) {
1883 for (pdev = domp->devlist; pdev; pdev = pdev->next) {
1884 if (pm_is_cfb(pdev->dip)) {
1885 do_polled_io = 1;
1886 break;
1887 }
1888 }
1889 }
1890
1891 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
1892 (intptr_t)&(dc->m_un.kio.val), FWRITE | FKIOCTL,
1893 kcred, NULL);
1894
1895 do_polled_io = pio_save;
1896
1897 if (ret == 0) {
1898 if (cmd == PPMDC_CLK_ON) {
1899 domp->status = PPMD_ON;
1900
1901 /*
1902 * PCI PM spec requires 50ms delay
1903 */
1904 drv_usecwait(50000);
1905 } else
1906 domp->status = PPMD_OFF;
1907 }
1908
1909 PPMD(D_PCI, ("%s: %s pci clock %s for domain (%s)\n", str,
1910 (ret == 0) ? "turned" : "failed to turn",
1911 (cmd == PPMDC_CLK_OFF) ? "OFF" : "ON",
1912 domp->name))
1913
1914 break;
1915
1916 default:
1917 PPMD(D_PCI, ("%s: unsupported domain control method %d\n",
1918 str, dc->method))
1919 return (DDI_FAILURE);
1920 }
1921
1922 return (DDI_SUCCESS);
1923 }
1924
1925
1926 /*
1927 * pci slot domain is formed of pci device(s) reside in a pci slot.
1928 * This function monitors domain device's power level change, such
1929 * that,
1930 * when all domain power count has gone to 0, it attempts to turn off
1931 * the pci slot's clock;
1932 * if any domain device is powering up, it'll turn on the pci slot's
1933 * clock as the first thing.
1934 */
1935 /* ARGUSED */
1936 static int
ppm_manage_pci(dev_info_t * dip,power_req_t * reqp,int * result)1937 ppm_manage_pci(dev_info_t *dip, power_req_t *reqp, int *result)
1938 {
1939 #ifdef DEBUG
1940 char *str = "ppm_manage_pci";
1941 #endif
1942 int (*pwr_func)(ppm_dev_t *, int, int);
1943 int old, new, cmpt;
1944 ppm_dev_t *ppmd;
1945 ppm_domain_t *domp;
1946 int incr = 0;
1947 int dummy_ret;
1948
1949 *result = DDI_SUCCESS;
1950 switch (reqp->request_type) {
1951 case PMR_PPM_SET_POWER:
1952 pwr_func = ppm_change_power_level;
1953 old = reqp->req.ppm_set_power_req.old_level;
1954 new = reqp->req.ppm_set_power_req.new_level;
1955 cmpt = reqp->req.ppm_set_power_req.cmpt;
1956 break;
1957
1958 case PMR_PPM_POWER_CHANGE_NOTIFY:
1959 pwr_func = ppm_record_level_change;
1960 old = reqp->req.ppm_notify_level_req.old_level;
1961 new = reqp->req.ppm_notify_level_req.new_level;
1962 cmpt = reqp->req.ppm_notify_level_req.cmpt;
1963 break;
1964
1965 default:
1966 *result = DDI_FAILURE;
1967 return (DDI_FAILURE);
1968 }
1969
1970 for (ppmd = PPM_GET_PRIVATE(dip); ppmd; ppmd = ppmd->next)
1971 if (cmpt == ppmd->cmpt)
1972 break;
1973 if (!ppmd) {
1974 PPMD(D_PCI, ("%s: dip(%p): old(%d), new(%d): no ppm_dev"
1975 " found for cmpt(%d)", str, (void *)dip, old, new, cmpt))
1976 *result = DDI_FAILURE;
1977 return (DDI_FAILURE);
1978 }
1979 domp = ppmd->domp;
1980 PPMD(D_PCI, ("%s: %s, dev(%s), c%d, old %d, new %d\n", str,
1981 ppm_get_ctlstr(reqp->request_type, ~0),
1982 ppmd->path, cmpt, old, new))
1983
1984 ASSERT(old == ppmd->level);
1985 if (new == ppmd->level)
1986 return (DDI_SUCCESS);
1987
1988 PPM_LOCK_DOMAIN(domp);
1989
1990 if (new > 0) { /* device powering up */
1991 if (domp->status == PPMD_OFF) {
1992
1993 /* cannot be off during (chpt, resume) window */
1994 ASSERT(ppm_cpr_window_flag == B_FALSE);
1995
1996 /* either both OFF or both ON */
1997 ASSERT(!((old == 0) ^ (domp->pwr_cnt == 0)));
1998
1999 PPMD(D_PCI, ("About to turn clock on for %s@%s c%d\n",
2000 PM_NAME(dip), PM_ADDR(dip), cmpt))
2001
2002 *result = ppm_switch_clock(domp, PPMD_ON);
2003 if (*result != DDI_SUCCESS) {
2004 PPMD(D_PCI, ("\tcan't switch on pci clock: "
2005 "ret(%d)\n", *result))
2006 PPM_UNLOCK_DOMAIN(domp);
2007 return (DDI_FAILURE);
2008 }
2009 }
2010
2011 if (old == 0) {
2012 domp->pwr_cnt++;
2013 incr = 1;
2014 }
2015
2016 PPMD(D_PCI, ("\t%s domain power count: %d\n",
2017 domp->name, domp->pwr_cnt))
2018 }
2019
2020 PPM_UNLOCK_DOMAIN(domp);
2021
2022 ASSERT(domp->pwr_cnt > 0);
2023
2024 if ((*result = (*pwr_func)(ppmd, cmpt, new)) != DDI_SUCCESS) {
2025 PPMD(D_PCI, ("\t%s power change failed: ret(%d)\n",
2026 ppmd->path, *result))
2027 }
2028
2029 PPM_LOCK_DOMAIN(domp);
2030
2031 /*
2032 * Decr the power count in two cases:
2033 *
2034 * 1) request was to power device down and was successful
2035 * 2) request was to power up (we pre-incremented count), but failed.
2036 */
2037 if ((*result == DDI_SUCCESS && ppmd->level == 0) ||
2038 (*result != DDI_SUCCESS && incr)) {
2039 ASSERT(domp->pwr_cnt > 0);
2040 domp->pwr_cnt--;
2041 }
2042
2043 PPMD(D_PCI, ("\t%s domain power count: %d\n",
2044 domp->name, domp->pwr_cnt))
2045
2046 /*
2047 * call to pwr_func will update ppm data structures, if it
2048 * succeeds. ppm should return whatever is the return value
2049 * from call to pwr_func. This way pm and ppm data structures
2050 * always in sync. Use dummy_ret from here for any further
2051 * return values.
2052 */
2053 if ((domp->pwr_cnt == 0) &&
2054 (ppm_cpr_window_flag == B_FALSE) &&
2055 ppm_none_else_holds_power(domp)) {
2056
2057 PPMD(D_PCI, ("About to turn clock off for %s@%s c%d\n",
2058 PM_NAME(dip), PM_ADDR(dip), cmpt))
2059
2060 dummy_ret = ppm_switch_clock(domp, PPMD_OFF);
2061 if (dummy_ret != DDI_SUCCESS) {
2062 PPMD(D_PCI, ("\tCan't switch clock off: "
2063 "ret(%d)\n", dummy_ret))
2064 }
2065 }
2066
2067 PPM_UNLOCK_DOMAIN(domp);
2068 ASSERT(domp->pwr_cnt >= 0);
2069 return (*result);
2070 }
2071
2072 /*
2073 * When the driver for the primary PCI-Express child has set the device to
2074 * lowest power (D3hot), we come here to save even more power by transitioning
2075 * the slot to D3cold. Similarly, if the slot is in D3cold and we need to
2076 * power up the child, we come here first to power up the slot.
2077 */
2078 /* ARGUSED */
2079 static int
ppm_manage_pcie(dev_info_t * dip,power_req_t * reqp,int * result)2080 ppm_manage_pcie(dev_info_t *dip, power_req_t *reqp, int *result)
2081 {
2082 #ifdef DEBUG
2083 char *str = "ppm_manage_pcie";
2084 #endif
2085 int (*pwr_func)(ppm_dev_t *, int, int);
2086 int old, new, cmpt;
2087 ppm_dev_t *ppmd;
2088 ppm_domain_t *domp;
2089 int incr = 0;
2090 int dummy_ret;
2091
2092 *result = DDI_SUCCESS;
2093 switch (reqp->request_type) {
2094 case PMR_PPM_SET_POWER:
2095 pwr_func = ppm_change_power_level;
2096 old = reqp->req.ppm_set_power_req.old_level;
2097 new = reqp->req.ppm_set_power_req.new_level;
2098 cmpt = reqp->req.ppm_set_power_req.cmpt;
2099 break;
2100
2101 case PMR_PPM_POWER_CHANGE_NOTIFY:
2102 pwr_func = ppm_record_level_change;
2103 old = reqp->req.ppm_notify_level_req.old_level;
2104 new = reqp->req.ppm_notify_level_req.new_level;
2105 cmpt = reqp->req.ppm_notify_level_req.cmpt;
2106 break;
2107
2108 default:
2109 *result = DDI_FAILURE;
2110 return (DDI_FAILURE);
2111 }
2112
2113 for (ppmd = PPM_GET_PRIVATE(dip); ppmd; ppmd = ppmd->next)
2114 if (cmpt == ppmd->cmpt)
2115 break;
2116 if (!ppmd) {
2117 PPMD(D_PCI, ("%s: dip(%p): old(%d), new(%d): no ppm_dev"
2118 " found for cmpt(%d)", str, (void *)dip, old, new, cmpt))
2119 *result = DDI_FAILURE;
2120 return (DDI_FAILURE);
2121 }
2122 domp = ppmd->domp;
2123 PPMD(D_PCI, ("%s: %s, dev(%s), c%d, old %d, new %d\n", str,
2124 ppm_get_ctlstr(reqp->request_type, ~0),
2125 ppmd->path, cmpt, old, new))
2126
2127 ASSERT(old == ppmd->level);
2128 if (new == ppmd->level)
2129 return (DDI_SUCCESS);
2130
2131 PPM_LOCK_DOMAIN(domp);
2132
2133 if (new > 0) { /* device powering up */
2134 if (domp->status == PPMD_OFF) {
2135
2136 /* cannot be off during (chpt, resume) window */
2137 ASSERT(ppm_cpr_window_flag == B_FALSE);
2138
2139 /* either both OFF or both ON */
2140 ASSERT(!((old == 0) ^ (domp->pwr_cnt == 0)));
2141
2142 PPMD(D_PCI, ("About to turn on pcie slot for "
2143 "%s@%s c%d\n", PM_NAME(dip), PM_ADDR(dip), cmpt))
2144
2145 *result = ppm_pcie_pwr(domp, PPMD_ON);
2146 if (*result != DDI_SUCCESS) {
2147 PPMD(D_PCI, ("\tcan't switch on pcie slot: "
2148 "ret(%d)\n", *result))
2149 PPM_UNLOCK_DOMAIN(domp);
2150 return (DDI_FAILURE);
2151 }
2152 }
2153
2154 if (old == 0) {
2155 domp->pwr_cnt++;
2156 incr = 1;
2157 }
2158
2159 PPMD(D_PCI, ("\t%s domain power count: %d\n",
2160 domp->name, domp->pwr_cnt))
2161 }
2162
2163 PPM_UNLOCK_DOMAIN(domp);
2164
2165 ASSERT(domp->pwr_cnt > 0);
2166
2167 if ((*result = (*pwr_func)(ppmd, cmpt, new)) != DDI_SUCCESS) {
2168 PPMD(D_PCI, ("\t%s power change failed: ret(%d)\n",
2169 ppmd->path, *result))
2170 }
2171
2172 PPM_LOCK_DOMAIN(domp);
2173
2174 /*
2175 * Decr the power count in two cases:
2176 *
2177 * 1) request was to power device down and was successful
2178 * 2) request was to power up (we pre-incremented count), but failed.
2179 */
2180 if ((*result == DDI_SUCCESS && ppmd->level == 0) ||
2181 (*result != DDI_SUCCESS && incr)) {
2182 ASSERT(domp->pwr_cnt > 0);
2183 domp->pwr_cnt--;
2184 }
2185
2186 PPMD(D_PCI, ("\t%s domain power count: %d\n",
2187 domp->name, domp->pwr_cnt))
2188
2189 /*
2190 * call to pwr_func will update ppm data structures, if it
2191 * succeeds. ppm should return whatever is the return value
2192 * from call to pwr_func. This way pm and ppm data structures
2193 * always in sync. Use dummy_ret from here for any further
2194 * return values.
2195 */
2196 if ((domp->pwr_cnt == 0) &&
2197 (ppm_cpr_window_flag == B_FALSE) &&
2198 ppm_none_else_holds_power(domp)) {
2199
2200 PPMD(D_PCI, ("About to turn off pcie slot for %s@%s c%d\n",
2201 PM_NAME(dip), PM_ADDR(dip), cmpt))
2202
2203 dummy_ret = ppm_pcie_pwr(domp, PPMD_OFF);
2204 if (dummy_ret != DDI_SUCCESS) {
2205 PPMD(D_PCI, ("\tCan't switch pcie slot off: "
2206 "ret(%d)\n", dummy_ret))
2207 }
2208 }
2209
2210 PPM_UNLOCK_DOMAIN(domp);
2211 ASSERT(domp->pwr_cnt >= 0);
2212 return (*result);
2213
2214 }
2215
2216 /*
2217 * Set or clear a bit on a GPIO device. These bits are used for various device-
2218 * specific purposes.
2219 */
2220 static int
ppm_gpioset(ppm_domain_t * domp,int key)2221 ppm_gpioset(ppm_domain_t *domp, int key)
2222 {
2223 #ifdef DEBUG
2224 char *str = "ppm_gpioset";
2225 #endif
2226 ppm_dc_t *dc;
2227 int ret;
2228 clock_t delay = 0;
2229
2230 for (dc = domp->dc; dc; dc = dc->next)
2231 if (dc->cmd == key)
2232 break;
2233 if (!dc || !dc->lh) {
2234 PPMD(D_GPIO, ("%s: %s domain: NULL ppm_dc handle\n",
2235 str, domp->name))
2236 return (DDI_FAILURE);
2237 }
2238
2239 PPM_GET_IO_DELAY(dc, delay);
2240 if (delay > 0) {
2241 PPMD(D_GPIO, ("%s : waiting %lu micro seconds "
2242 "before change\n", domp->name, delay))
2243 drv_usecwait(delay);
2244 }
2245
2246 switch (dc->method) {
2247 #ifdef sun4u
2248 case PPMDC_I2CKIO: {
2249 i2c_gpio_t i2c_req;
2250 ppm_dev_t *pdev;
2251 int pio_save;
2252 extern int do_polled_io;
2253 extern uint_t cfb_inuse;
2254 i2c_req.reg_mask = dc->m_un.i2c.mask;
2255 i2c_req.reg_val = dc->m_un.i2c.val;
2256
2257 pio_save = do_polled_io;
2258 if (cfb_inuse) {
2259 for (pdev = domp->devlist; pdev; pdev = pdev->next) {
2260 if (pm_is_cfb(pdev->dip)) {
2261 do_polled_io = 1;
2262 PPMD(D_GPIO, ("%s: cfb is in use, "
2263 "i2c transaction is done in "
2264 "poll-mode.\n", str))
2265 break;
2266 }
2267 }
2268 }
2269 ret = ldi_ioctl(dc->lh, dc->m_un.i2c.iowr,
2270 (intptr_t)&i2c_req, FWRITE | FKIOCTL, kcred, NULL);
2271 do_polled_io = pio_save;
2272
2273 PPMD(D_GPIO, ("%s: %s domain(%s) from %s by writing %x "
2274 "to gpio\n",
2275 str, (ret == 0) ? "turned" : "FAILed to turn",
2276 domp->name,
2277 (domp->status == PPMD_ON) ? "ON" : "OFF",
2278 dc->m_un.i2c.val))
2279
2280 break;
2281 }
2282 #endif
2283
2284 case PPMDC_KIO:
2285 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
2286 (intptr_t)&(dc->m_un.kio.val), FWRITE | FKIOCTL, kcred,
2287 NULL);
2288
2289 PPMD(D_GPIO, ("%s: %s domain(%s) from %s by writing %x "
2290 "to gpio\n",
2291 str, (ret == 0) ? "turned" : "FAILed to turn",
2292 domp->name,
2293 (domp->status == PPMD_ON) ? "ON" : "OFF",
2294 dc->m_un.kio.val))
2295
2296 break;
2297
2298 default:
2299 PPMD(D_GPIO, ("\t%s: unsupported domain control method %d\n",
2300 str, domp->dc->method))
2301 return (DDI_FAILURE);
2302 }
2303
2304 /* implement any post op delay. */
2305 PPM_GET_IO_POST_DELAY(dc, delay);
2306 if (delay > 0) {
2307 PPMD(D_GPIO, ("%s : waiting %lu micro seconds "
2308 "after change\n", domp->name, delay))
2309 drv_usecwait(delay);
2310 }
2311
2312 return (ret);
2313 }
2314
2315 static int
ppm_pcie_pwr(ppm_domain_t * domp,int onoff)2316 ppm_pcie_pwr(ppm_domain_t *domp, int onoff)
2317 {
2318 #ifdef DEBUG
2319 char *str = "ppm_pcie_pwr";
2320 #endif
2321 int ret = DDI_FAILURE;
2322 ppm_dc_t *dc;
2323 clock_t delay;
2324
2325 ASSERT(onoff == PPMD_OFF || onoff == PPMD_ON);
2326
2327 dc = ppm_lookup_dc(domp,
2328 onoff == PPMD_ON ? PPMDC_PRE_PWR_ON : PPMDC_PRE_PWR_OFF);
2329 if (dc) {
2330
2331 /*
2332 * Invoke layered ioctl for pcie root complex nexus to
2333 * transition the link
2334 */
2335 ASSERT(dc->method == PPMDC_KIO);
2336 delay = dc->m_un.kio.delay;
2337 if (delay > 0) {
2338 PPMD(D_GPIO, ("%s : waiting %lu micro seconds "
2339 "before change\n", domp->name, delay))
2340 drv_usecwait(delay);
2341 }
2342 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
2343 (intptr_t)&(dc->m_un.kio.val),
2344 FWRITE | FKIOCTL, kcred, NULL);
2345 if (ret == DDI_SUCCESS) {
2346 delay = dc->m_un.kio.post_delay;
2347 if (delay > 0) {
2348 PPMD(D_GPIO, ("%s : waiting %lu micro seconds "
2349 "after change\n", domp->name, delay))
2350 drv_usecwait(delay);
2351 }
2352 } else {
2353 PPMD(D_PCI, ("%s: ldi_ioctl FAILED for domain(%s)\n",
2354 str, domp->name))
2355 return (ret);
2356 }
2357 }
2358
2359 switch (onoff) {
2360 case PPMD_OFF:
2361 /* Turn off the clock for this slot. */
2362 if ((ret = ppm_gpioset(domp, PPMDC_CLK_OFF)) != DDI_SUCCESS) {
2363 PPMD(D_GPIO,
2364 ("%s: failed to turn off domain(%s) clock\n",
2365 str, domp->name))
2366 return (ret);
2367 }
2368
2369 /* Turn off the power to this slot */
2370 if ((ret = ppm_gpioset(domp, PPMDC_PWR_OFF)) != DDI_SUCCESS) {
2371 PPMD(D_GPIO,
2372 ("%s: failed to turn off domain(%s) power\n",
2373 str, domp->name))
2374 return (ret);
2375 }
2376 break;
2377 case PPMD_ON:
2378 /* Assert RESET for this slot. */
2379 if ((ret = ppm_gpioset(domp, PPMDC_RESET_ON)) != DDI_SUCCESS) {
2380 PPMD(D_GPIO,
2381 ("%s: failed to assert reset for domain(%s)\n",
2382 str, domp->name))
2383 return (ret);
2384 }
2385
2386 /* Turn on the power to this slot */
2387 if ((ret = ppm_gpioset(domp, PPMDC_PWR_ON)) != DDI_SUCCESS) {
2388 PPMD(D_GPIO,
2389 ("%s: failed to turn on domain(%s) power\n",
2390 str, domp->name))
2391 return (ret);
2392 }
2393
2394 /* Turn on the clock for this slot */
2395 if ((ret = ppm_gpioset(domp, PPMDC_CLK_ON)) != DDI_SUCCESS) {
2396 PPMD(D_GPIO,
2397 ("%s: failed to turn on domain(%s) clock\n",
2398 str, domp->name))
2399 return (ret);
2400 }
2401
2402 /* De-assert RESET for this slot. */
2403 if ((ret = ppm_gpioset(domp, PPMDC_RESET_OFF)) != DDI_SUCCESS) {
2404 PPMD(D_GPIO,
2405 ("%s: failed to de-assert reset for domain(%s)\n",
2406 str, domp->name))
2407 return (ret);
2408 }
2409
2410 dc = ppm_lookup_dc(domp, PPMDC_POST_PWR_ON);
2411 if (dc) {
2412 /*
2413 * Invoke layered ioctl to PCIe root complex nexus
2414 * to transition the link.
2415 */
2416 ASSERT(dc->method == PPMDC_KIO);
2417 delay = dc->m_un.kio.delay;
2418 if (delay > 0) {
2419 PPMD(D_GPIO, ("%s: waiting %lu micro seconds "
2420 "before change\n", domp->name, delay))
2421 drv_usecwait(delay);
2422 }
2423 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
2424 (intptr_t)&(dc->m_un.kio.val),
2425 FWRITE | FKIOCTL, kcred, NULL);
2426
2427 if (ret != DDI_SUCCESS) {
2428 PPMD(D_PCI, ("%s: layered ioctl to PCIe"
2429 "root complex nexus FAILed\n", str))
2430 return (ret);
2431 }
2432
2433 delay = dc->m_un.kio.post_delay;
2434 if (delay > 0) {
2435 PPMD(D_GPIO, ("%s: waiting %lu micro "
2436 "seconds after change\n",
2437 domp->name, delay))
2438 drv_usecwait(delay);
2439 }
2440 }
2441 break;
2442 default:
2443 ASSERT(0);
2444 }
2445
2446 PPMD(D_PCI, ("%s: turned domain(%s) PCIe slot power from %s to %s\n",
2447 str, domp->name, (domp->status == PPMD_ON) ? "ON" : "OFF",
2448 onoff == PPMD_ON ? "ON" : "OFF"))
2449
2450 domp->status = onoff;
2451 return (ret);
2452 }
2453
2454
2455 /*
2456 * Change the power level for a component of a device. If the change
2457 * arg is true, we call the framework to actually change the device's
2458 * power; otherwise, we just update our own copy of the power level.
2459 */
2460 static int
ppm_set_level(ppm_dev_t * ppmd,int cmpt,int level,boolean_t change)2461 ppm_set_level(ppm_dev_t *ppmd, int cmpt, int level, boolean_t change)
2462 {
2463 #ifdef DEBUG
2464 char *str = "ppm_set_level";
2465 #endif
2466 int ret;
2467
2468 ret = DDI_SUCCESS;
2469 if (change)
2470 ret = pm_power(ppmd->dip, cmpt, level);
2471
2472 PPMD(D_SETLVL, ("%s: %s change=%d, old %d, new %d, ret %d\n",
2473 str, ppmd->path, change, ppmd->level, level, ret))
2474
2475 if (ret == DDI_SUCCESS) {
2476 ppmd->level = level;
2477 ppmd->rplvl = PM_LEVEL_UNKNOWN;
2478 }
2479
2480 return (ret);
2481 }
2482
2483
2484 static int
ppm_change_power_level(ppm_dev_t * ppmd,int cmpt,int level)2485 ppm_change_power_level(ppm_dev_t *ppmd, int cmpt, int level)
2486 {
2487 return (ppm_set_level(ppmd, cmpt, level, B_TRUE));
2488 }
2489
2490
2491 static int
ppm_record_level_change(ppm_dev_t * ppmd,int cmpt,int level)2492 ppm_record_level_change(ppm_dev_t *ppmd, int cmpt, int level)
2493 {
2494 return (ppm_set_level(ppmd, cmpt, level, B_FALSE));
2495 }
2496
2497
2498 static void
ppm_manage_led(int action)2499 ppm_manage_led(int action)
2500 {
2501 ppm_domain_t *domp;
2502 ppm_unit_t *unitp;
2503 timeout_id_t tid;
2504
2505
2506 PPMD(D_LED, ("ppm_manage_led: action: %s\n",
2507 (action == PPM_LED_BLINKING) ? "PPM_LED_BLINKING" :
2508 "PPM_LED_SOLIDON"))
2509
2510 /*
2511 * test whether led operation is practically supported,
2512 * if not, we waive without pressing for reasons
2513 */
2514 if (!ppm_lookup_dc(NULL, PPMDC_LED_ON))
2515 return;
2516
2517 unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
2518 for (domp = ppm_domain_p; (domp && (domp->model != PPMD_LED)); )
2519 domp = domp->next;
2520
2521 mutex_enter(&unitp->lock);
2522 if (action == PPM_LED_BLINKING) {
2523 ppm_set_led(domp, PPMD_OFF);
2524 unitp->led_tid = timeout(
2525 ppm_blink_led, domp, PPM_LEDOFF_INTERVAL);
2526
2527 } else { /* PPM_LED_SOLIDON */
2528 ASSERT(action == PPM_LED_SOLIDON);
2529 tid = unitp->led_tid;
2530 unitp->led_tid = 0;
2531
2532 mutex_exit(&unitp->lock);
2533 (void) untimeout(tid);
2534
2535 mutex_enter(&unitp->lock);
2536 ppm_set_led(domp, PPMD_ON);
2537 }
2538 mutex_exit(&unitp->lock);
2539 }
2540
2541
2542 static void
ppm_set_led(ppm_domain_t * domp,int val)2543 ppm_set_led(ppm_domain_t *domp, int val)
2544 {
2545 int ret;
2546
2547 ret = ppm_gpioset(domp,
2548 (val == PPMD_ON) ? PPMDC_LED_ON : PPMDC_LED_OFF);
2549
2550 PPMD(D_LED, ("ppm_set_led: %s LED from %s\n",
2551 (ret == 0) ? "turned" : "FAILed to turn",
2552 (domp->status == PPMD_ON) ? "ON to OFF" : "OFF to ON"))
2553
2554 if (ret == DDI_SUCCESS)
2555 domp->status = val;
2556 }
2557
2558
2559 static void
ppm_blink_led(void * arg)2560 ppm_blink_led(void *arg)
2561 {
2562 ppm_unit_t *unitp;
2563 clock_t intvl;
2564 ppm_domain_t *domp = arg;
2565
2566 unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
2567
2568 mutex_enter(&unitp->lock);
2569 if (unitp->led_tid == 0) {
2570 mutex_exit(&unitp->lock);
2571 return;
2572 }
2573
2574 if (domp->status == PPMD_ON) {
2575 ppm_set_led(domp, PPMD_OFF);
2576 intvl = PPM_LEDOFF_INTERVAL;
2577 } else {
2578 ppm_set_led(domp, PPMD_ON);
2579 intvl = PPM_LEDON_INTERVAL;
2580 }
2581
2582 unitp->led_tid = timeout(ppm_blink_led, domp, intvl);
2583 mutex_exit(&unitp->lock);
2584 }
2585
2586 /*
2587 * Function to power up a domain, if required. It also increments the
2588 * domain pwr_cnt to prevent it from going down.
2589 */
2590 static int
ppm_power_up_domain(dev_info_t * dip)2591 ppm_power_up_domain(dev_info_t *dip)
2592 {
2593 int ret = DDI_SUCCESS;
2594 ppm_domain_t *domp;
2595 char *str = "ppm_power_up_domain";
2596
2597 domp = ppm_lookup_dev(dip);
2598 ASSERT(domp);
2599 mutex_enter(&domp->lock);
2600 switch (domp->model) {
2601 case PPMD_FET:
2602 if (domp->status == PPMD_OFF) {
2603 if ((ret = ppm_fetset(domp, PPMD_ON)) ==
2604 DDI_SUCCESS) {
2605 PPMD(D_FET, ("%s: turned on fet for %s@%s\n",
2606 str, PM_NAME(dip), PM_ADDR(dip)))
2607 } else {
2608 PPMD(D_FET, ("%s: couldn't turn on fet "
2609 "for %s@%s\n", str, PM_NAME(dip),
2610 PM_ADDR(dip)))
2611 }
2612 }
2613 break;
2614
2615 case PPMD_PCI:
2616 case PPMD_PCI_PROP:
2617 if (domp->status == PPMD_OFF) {
2618 if ((ret = ppm_switch_clock(domp, PPMD_ON)) ==
2619 DDI_SUCCESS) {
2620 PPMD(D_PCI, ("%s: turned on clock for "
2621 "%s@%s\n", str, PM_NAME(dip),
2622 PM_ADDR(dip)))
2623 } else {
2624 PPMD(D_PCI, ("%s: couldn't turn on clock "
2625 "for %s@%s\n", str, PM_NAME(dip),
2626 PM_ADDR(dip)))
2627 }
2628 }
2629 break;
2630
2631 case PPMD_PCIE:
2632 if (domp->status == PPMD_OFF) {
2633 if ((ret = ppm_pcie_pwr(domp, PPMD_ON)) ==
2634 DDI_SUCCESS) {
2635 PPMD(D_PCI, ("%s: turned on link for "
2636 "%s@%s\n", str, PM_NAME(dip),
2637 PM_ADDR(dip)))
2638 } else {
2639 PPMD(D_PCI, ("%s: couldn't turn on link "
2640 "for %s@%s\n", str, PM_NAME(dip),
2641 PM_ADDR(dip)))
2642 }
2643 }
2644 break;
2645
2646 default:
2647 break;
2648 }
2649 if (ret == DDI_SUCCESS)
2650 domp->pwr_cnt++;
2651 mutex_exit(&domp->lock);
2652 return (ret);
2653 }
2654
2655 /*
2656 * Decrements the domain pwr_cnt. if conditions to power down the domain
2657 * are met, powers down the domain,.
2658 */
2659 static int
ppm_power_down_domain(dev_info_t * dip)2660 ppm_power_down_domain(dev_info_t *dip)
2661 {
2662 int ret = DDI_SUCCESS;
2663 char *str = "ppm_power_down_domain";
2664 ppm_domain_t *domp;
2665
2666 domp = ppm_lookup_dev(dip);
2667 ASSERT(domp);
2668 mutex_enter(&domp->lock);
2669 ASSERT(domp->pwr_cnt > 0);
2670 domp->pwr_cnt--;
2671 switch (domp->model) {
2672 case PPMD_FET:
2673 if ((domp->pwr_cnt == 0) &&
2674 (ppm_cpr_window_flag == B_FALSE) &&
2675 ppm_none_else_holds_power(domp)) {
2676 if ((ret = ppm_fetset(domp, PPMD_OFF)) ==
2677 DDI_SUCCESS) {
2678 PPMD(D_FET, ("%s: turned off FET for %s@%s \n",
2679 str, PM_NAME(dip), PM_ADDR(dip)))
2680 } else {
2681 PPMD(D_FET, ("%s: couldn't turn off FET for "
2682 " %s@%s\n", str, PM_NAME(dip),
2683 PM_ADDR(dip)))
2684 }
2685 }
2686 break;
2687
2688 case PPMD_PCI:
2689 case PPMD_PCI_PROP:
2690 if ((domp->pwr_cnt == 0) &&
2691 (ppm_cpr_window_flag == B_FALSE) &&
2692 ppm_none_else_holds_power(domp)) {
2693 if ((ret = ppm_switch_clock(domp, PPMD_OFF)) ==
2694 DDI_SUCCESS) {
2695 PPMD(D_PCI, ("%s: turned off clock for %s@%s\n",
2696 str, PM_NAME(dip), PM_ADDR(dip)))
2697 } else {
2698 PPMD(D_PCI, ("%s: couldn't turn off clock "
2699 "for %s@%s\n", str, PM_NAME(dip),
2700 PM_ADDR(dip)))
2701 }
2702 }
2703 break;
2704
2705 case PPMD_PCIE:
2706 if ((domp->pwr_cnt == 0) &&
2707 (ppm_cpr_window_flag == B_FALSE) &&
2708 ppm_none_else_holds_power(domp)) {
2709 if ((ret = ppm_pcie_pwr(domp, PPMD_OFF)) ==
2710 DDI_SUCCESS) {
2711 PPMD(D_PCI, ("%s: turned off link for %s@%s\n",
2712 str, PM_NAME(dip), PM_ADDR(dip)))
2713 } else {
2714 PPMD(D_PCI, ("%s: couldn't turn off link "
2715 "for %s@%s\n", str, PM_NAME(dip),
2716 PM_ADDR(dip)))
2717 }
2718 }
2719 break;
2720
2721 default:
2722 break;
2723 }
2724 mutex_exit(&domp->lock);
2725 return (ret);
2726 }
2727
2728 static int
ppm_manage_sx(s3a_t * s3ap,int enter)2729 ppm_manage_sx(s3a_t *s3ap, int enter)
2730 {
2731 ppm_domain_t *domp = ppm_lookup_domain("domain_estar");
2732 ppm_dc_t *dc;
2733 int ret = 0;
2734
2735 if (domp == NULL) {
2736 PPMD(D_CPR, ("ppm_manage_sx: can't find estar domain\n"))
2737 return (ENODEV);
2738 }
2739 PPMD(D_CPR, ("ppm_manage_sx %x, enter %d\n", s3ap->s3a_state,
2740 enter))
2741 switch (s3ap->s3a_state) {
2742 case S3:
2743 if (enter) {
2744 dc = ppm_lookup_dc(domp, PPMDC_ENTER_S3);
2745 } else {
2746 dc = ppm_lookup_dc(domp, PPMDC_EXIT_S3);
2747 }
2748 ASSERT(dc && dc->method == PPMDC_KIO);
2749 PPMD(D_CPR,
2750 ("ppm_manage_sx: calling acpi driver (handle %p)"
2751 " with %x\n", (void *)dc->lh, dc->m_un.kio.iowr))
2752 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
2753 (intptr_t)s3ap, FWRITE | FKIOCTL, kcred, NULL);
2754 break;
2755
2756 case S4:
2757 /* S4 is not supported yet */
2758 return (EINVAL);
2759 default:
2760 ASSERT(0);
2761 }
2762 return (ret);
2763 }
2764
2765 /*
2766 * Search enable/disable lists, which are encoded in ppm.conf as an array
2767 * of char strings.
2768 */
2769 static int
ppm_search_list(pm_searchargs_t * sl)2770 ppm_search_list(pm_searchargs_t *sl)
2771 {
2772 int i;
2773 int flags = DDI_PROP_DONTPASS;
2774 ppm_unit_t *unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
2775 char **pp;
2776 char *starp;
2777 uint_t nelements;
2778 char *manuf = sl->pms_manufacturer;
2779 char *prod = sl->pms_product;
2780
2781 if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, unitp->dip, flags,
2782 sl->pms_listname, &pp, &nelements) != DDI_PROP_SUCCESS) {
2783 PPMD(D_CPR, ("ppm_search_list prop lookup %s failed--EINVAL\n",
2784 sl->pms_listname))
2785 return (EINVAL);
2786 }
2787 ASSERT((nelements & 1) == 0); /* must be even */
2788
2789 PPMD(D_CPR, ("ppm_search_list looking for %s, %s\n", manuf, prod))
2790
2791 for (i = 0; i < nelements; i += 2) {
2792 PPMD(D_CPR, ("checking %s, %s", pp[i], pp[i+1]))
2793 /* we support only a trailing '*' pattern match */
2794 if ((starp = strchr(pp[i], '*')) != NULL && *(starp + 1) == 0) {
2795 /* LINTED - ptrdiff overflow */
2796 if (strncmp(manuf, pp[i], (starp - pp[i])) != 0) {
2797 PPMD(D_CPR, (" no match %s with %s\n",
2798 manuf, pp[i + 1]))
2799 continue;
2800 }
2801 }
2802 if ((starp = strchr(pp[i + 1], '*')) != NULL &&
2803 *(starp + 1) == 0) {
2804 if (strncmp(prod,
2805 /* LINTED - ptrdiff overflow */
2806 pp[i + 1], (starp - pp[i + 1])) != 0) {
2807 PPMD(D_CPR, (" no match %s with %s\n",
2808 prod, pp[i + 1]))
2809 continue;
2810 }
2811 }
2812 if (strcmp(manuf, pp[i]) == 0 &&
2813 (strcmp(prod, pp[i + 1]) == 0)) {
2814 PPMD(D_CPR, (" match\n"))
2815 ddi_prop_free(pp);
2816 return (0);
2817 }
2818 PPMD(D_CPR, (" no match %s with %s or %s with %s\n",
2819 manuf, pp[i], prod, pp[i + 1]))
2820 }
2821 ddi_prop_free(pp);
2822 return (ENODEV);
2823 }
2824