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.
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.
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]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 * Copyright (c) 2009, Intel Corporation.
27 * All Rights Reserved.
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
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.
46 #include <sys/types.h>
47 #include <sys/param.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>
57 #include <sys/sunpm.h>
58 #include <sys/ppmio.h>
59 #include <sys/sunldi.h>
60 #include <sys/ppmvar.h>
62 #include <sys/sunddi.h>
63 #include <sys/ppm_plat.h>
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.
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 *);
78 static struct cb_ops ppm_cb_ops
= {
80 ppm_close
, /* close */
86 ppm_ioctl
, /* ioctl */
91 ddi_prop_op
, /* prop_op */
93 D_MP
| D_NEW
, /* driver compatibility flag */
94 CB_REV
, /* cb_ops revision */
95 nodev
, /* async read */
96 nodev
/* async write */
102 static int ppm_ctlops(dev_info_t
*, dev_info_t
*, ddi_ctl_enum_t
, void *,
105 static struct bus_ops ppm_bus_ops
= {
106 BUSO_REV
, /* busops_rev */
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 */
122 0, /* bus_get_eventcookie */
123 0, /* bus_add_eventcall */
124 0, /* bus_remove_eventcall */
125 0, /* bus_post_event */
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
);
136 static struct dev_ops ppm_ops
= {
137 DEVO_REV
, /* devo_rev */
139 ppm_getinfo
, /* info */
140 nulldev
, /* identify */
142 ppm_attach
, /* attach */
143 ppm_detach
, /* detach */
145 &ppm_cb_ops
, /* cb_ops */
146 &ppm_bus_ops
, /* bus_ops */
148 ddi_quiesce_not_needed
, /* quiesce */
151 extern struct mod_ops mod_driverops
;
153 static struct modldrv modldrv
= {
155 "platform pm driver",
159 static struct modlinkage modlinkage
= {
166 * Global data structure and variables
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 */
176 #define PPM_LED_SOLIDON 0
177 #define PPM_LED_BLINKING 1
183 uint_t ppm_debug
= 0;
187 * Local function prototypes and data
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
);
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
);
218 if (mod_install(&modlinkage
) != DDI_SUCCESS
) {
219 ddi_soft_state_fini(&ppm_statep
);
220 return (DDI_FAILURE
);
222 return (DDI_SUCCESS
);
231 if ((error
= mod_remove(&modlinkage
)) == DDI_SUCCESS
)
232 ddi_soft_state_fini(&ppm_statep
);
239 _info(struct modinfo
*modinfop
)
241 return (mod_info(&modlinkage
, modinfop
));
247 ppm_getinfo(dev_info_t
*dip
, ddi_info_cmd_t cmd
, void *arg
, void **resultp
)
249 struct ppm_unit
*unitp
;
255 return (DDI_FAILURE
);
258 case DDI_INFO_DEVT2DEVINFO
:
259 if (unitp
= ddi_get_soft_state(ppm_statep
, (dev_t
)arg
)) {
260 *resultp
= unitp
->dip
;
267 case DDI_INFO_DEVT2INSTANCE
:
269 instance
= getminor(dev
);
270 *resultp
= (void *)(uintptr_t)instance
;
271 return (DDI_SUCCESS
);
274 return (DDI_FAILURE
);
283 ppm_attach(dev_info_t
*dip
, ddi_attach_cmd_t cmd
)
288 char *str
= "ppm_attach";
294 PPMD(D_ATTACH
, ("%s: attaching ...\n", str
))
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
);
306 cmn_err(CE_WARN
, "ppm_attach: unknown command %d, dip(0x%p)",
308 return (DDI_FAILURE
);
311 if (ppm_inst
!= -1) {
312 PPMD(D_ATTACH
, ("%s: Already attached !", str
))
313 return (DDI_FAILURE
);
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
);
321 unitp
= ddi_get_soft_state(ppm_statep
, ppm_inst
);
323 ret
= ddi_create_minor_node(dip
, "ppm", S_IFCHR
, ppm_inst
,
325 if (ret
!= DDI_SUCCESS
) {
326 PPMD(D_ATTACH
, ("%s: can't create minor node!\n", str
))
331 mutex_init(&unitp
->lock
, NULL
, MUTEX_DRIVER
, NULL
);
334 * read ppm.conf, construct ppm_domain data structure and
335 * their sub data structure.
337 if ((ret
= ppm_create_db(dip
)) != DDI_SUCCESS
)
341 * walk down ppm domain control from each domain, initialize
342 * domain control orthogonal function call handle
346 if ((ret
= pm_register_ppm(ppm_claim_dev
, dip
)) != DDI_SUCCESS
) {
347 cmn_err(CE_WARN
, "ppm_attach: can't register ppm handler!");
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
, NULL
,
354 CB_CL_CPR_PM
, "ppm_cpr");
358 * Register callback so that once CPUs have been added to
359 * the device tree, ppm CPU domains can be allocated using ACPI
362 cpupm_ppm_alloc_pstate_domains
= ppm_alloc_pstate_domains
;
363 cpupm_ppm_free_pstate_domains
= ppm_free_pstate_domains
;
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.
370 cpupm_redefine_topspeed
= ppm_redefine_topspeed
;
374 return (DDI_SUCCESS
);
377 ddi_remove_minor_node(dip
, "ddi_ppm");
378 mutex_destroy(&unitp
->lock
);
380 ddi_soft_state_free(ppm_statep
, ppm_inst
);
382 return (DDI_FAILURE
);
388 ppm_detach(dev_info_t
*dip
, ddi_detach_cmd_t cmd
)
392 char *str
= "ppm_detach";
397 PPMD(D_DETACH
, ("%s: detach not allowed.\n", str
))
398 return (DDI_FAILURE
);
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
);
408 * Suspend requires that timeout callouts to be canceled.
409 * Turning off the LED blinking will cancel the timeout.
411 ppm_manage_led(PPM_LED_SOLIDON
);
412 return (DDI_SUCCESS
);
415 cmn_err(CE_WARN
, "ppm_detach: unsupported command %d, dip(%p)",
417 return (DDI_FAILURE
);
424 ppm_open(dev_t
*devp
, int flag
, int otyp
, cred_t
*cred_p
)
426 if (otyp
!= OTYP_CHR
)
428 PPMD(D_OPEN
, ("ppm_open: devp 0x%p, flag 0x%x, otyp %d\n",
429 (void *)devp
, flag
, otyp
))
436 ppm_close(dev_t dev
, int flag
, int otyp
, cred_t
*credp
)
438 PPMD(D_CLOSE
, ("ppm_close: dev 0x%lx, flag 0x%x, otyp %d\n",
446 ppm_ioctl(dev_t dev
, int cmd
, intptr_t arg
, int mode
, cred_t
*cred_p
,
450 char *str
= "ppm_ioctl";
452 ppm_domain_t
*domp
= NULL
;
456 PPMD(D_IOCTL
, ("%s: dev 0x%lx, cmd 0x%x, mode 0x%x\n",
457 str
, dev
, cmd
, mode
))
462 STRUCT_DECL(ppm_dpwr
, dpwr
);
463 struct ppm_unit
*unitp
;
466 STRUCT_INIT(dpwr
, mode
);
467 ret
= ddi_copyin((caddr_t
)arg
, STRUCT_BUF(dpwr
),
468 STRUCT_SIZE(dpwr
), mode
);
472 /* copyin domain name */
473 domain
= kmem_zalloc(MAXNAMELEN
, KM_SLEEP
);
475 STRUCT_FGETP(dpwr
, domain
), domain
, MAXNAMELEN
, NULL
);
477 PPMD(D_IOCTL
, ("%s: can't copyin domain, line(%d)\n",
484 if ((domp
= ppm_lookup_domain(domain
)) == NULL
) {
485 PPMD(D_IOCTL
, ("%s: no such domain %s\n", str
, domain
))
490 switch (domp
->model
) {
491 case PPMD_FET
: /* report power fet ON or OFF */
492 if ((ret
= ppm_fetget(domp
, &lvl
)) != 0) {
496 level
= (lvl
== PPMD_ON
) ?
497 PPMIO_POWER_ON
: PPMIO_POWER_OFF
;
500 case PPMD_PCI
: /* report pci slot clock ON or OFF */
503 level
= (domp
->status
== PPMD_ON
) ?
504 PPMIO_POWER_ON
: PPMIO_POWER_OFF
;
507 case PPMD_LED
: /* report LED blinking or solid on */
509 unitp
= ddi_get_soft_state(ppm_statep
, ppm_inst
);
510 if (unitp
->led_tid
== 0)
511 level
= PPMIO_LED_SOLIDON
;
513 level
= PPMIO_LED_BLINKING
;
516 case PPMD_CPU
: /* report cpu speed divisor */
517 level
= domp
->devlist
->level
;
525 STRUCT_FSET(dpwr
, level
, level
);
526 ret
= ddi_copyout(STRUCT_BUF(dpwr
), (caddr_t
)arg
,
527 STRUCT_SIZE(dpwr
), mode
);
529 PPMD(D_IOCTL
, ("%s: can't copyout, line(%d)\n",
534 kmem_free(domain
, MAXNAMELEN
);
539 case PPMGET_DOMBYDEV
:
541 STRUCT_DECL(ppm_bydev
, bydev
);
545 STRUCT_INIT(bydev
, mode
);
546 ret
= ddi_copyin((caddr_t
)arg
, STRUCT_BUF(bydev
),
547 STRUCT_SIZE(bydev
), mode
);
552 path
= kmem_zalloc(MAXPATHLEN
, KM_SLEEP
);
554 STRUCT_FGETP(bydev
, path
), path
, MAXPATHLEN
, NULL
);
556 PPMD(D_IOCTL
, ("%s: can't copyin path, line(%d)\n",
558 kmem_free(path
, MAXPATHLEN
);
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
);
567 l
= strlen(domp
->name
) + 1;
569 PPMD(D_IOCTL
, ("%s: buffer too small\n", str
))
570 return ((size
== 0) ? EINVAL
: EFAULT
);
572 } else /* no domain found to be associated with given device */
576 domp
->name
, STRUCT_FGETP(bydev
, domlist
), l
, &l
);
578 PPMD(D_IOCTL
, ("%s: can't copyout domlist, line(%d)"
579 " \n", str
, __LINE__
))
587 case PPMGET_DEVBYDOM
:
589 STRUCT_DECL(ppm_bydom
, bydom
);
591 char *devlist
= NULL
;
593 dev_info_t
*odip
= NULL
;
597 STRUCT_INIT(bydom
, mode
);
598 ret
= ddi_copyin((caddr_t
)arg
, STRUCT_BUF(bydom
),
599 STRUCT_SIZE(bydom
), mode
);
604 domain
= kmem_zalloc(MAXNAMELEN
, KM_SLEEP
);
605 ret
= copyinstr(STRUCT_FGETP(bydom
, domain
), domain
,
608 PPMD(D_IOCTL
, ("%s: can't copyin domain, line(%d)\n",
615 if ((domp
= ppm_lookup_domain(domain
)) == NULL
) {
621 if ((size
= STRUCT_FGET(bydom
, size
)) == 0)
624 if ((d
= devlist
= kmem_zalloc(size
, KM_SLEEP
)) == NULL
)
629 for (ppmd
= domp
->devlist
; ppmd
;
630 odip
= ppmd
->dip
, ppmd
= ppmd
->next
) {
632 if (ppmd
->dip
== odip
)
634 if (ppmd
!= domp
->devlist
)
637 l
+= strlen(ppmd
->path
) + 1;
639 PPMD(D_IOCTL
, ("%s: buffer overflow\n", str
))
644 for (s
= ppmd
->path
; *s
!= 0; )
653 devlist
, STRUCT_FGETP(bydom
, devlist
), l
, &l
);
655 PPMD(D_IOCTL
, ("%s: can't copyout devlist, line(%d)"
656 " \n", str
, __LINE__
))
662 kmem_free(devlist
, size
);
664 kmem_free(domain
, MAXNAMELEN
);
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.
679 STRUCT_DECL(ppm_norm
, norm
);
681 struct pm_component
*dcomps
;
682 struct pm_comp
*pm_comp
;
686 STRUCT_INIT(norm
, mode
);
687 ret
= ddi_copyin((caddr_t
)arg
, STRUCT_BUF(norm
),
688 STRUCT_SIZE(norm
), mode
);
693 path
= kmem_zalloc(MAXPATHLEN
, KM_SLEEP
);
695 STRUCT_FGETP(norm
, path
), path
, MAXPATHLEN
, NULL
);
697 PPMD(D_IOCTL
, ("%s: can't copyin path, line(%d)\n",
699 kmem_free(path
, MAXPATHLEN
);
703 domp
= ppm_get_domain_by_dev(path
);
704 kmem_free(path
, MAXPATHLEN
);
709 ppmd
= domp
->devlist
;
710 if (cmd
== PPMSET_NORMAL
) {
711 if (domp
->model
!= PPMD_CPU
)
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
)
723 ppm_set_topspeed(ppmd
, pm_comp
->pmc_numlevels
- i
);
726 level
= pm_get_normal_power(ppmd
->dip
, 0);
728 STRUCT_FSET(norm
, norm
, level
);
729 ret
= ddi_copyout(STRUCT_BUF(norm
), (caddr_t
)arg
,
730 STRUCT_SIZE(norm
), mode
);
732 PPMD(D_IOCTL
, ("%s: can't copyout, line(%d)\n",
740 PPMD(D_IOCTL
, ("%s: unsupported ioctl command(%d)\n", str
, cmd
))
748 static int ppm_manage_sx(s3a_t
*, int);
749 static int ppm_search_list(pm_searchargs_t
*);
752 * interface between pm framework and ppm driver
756 ppm_ctlops(dev_info_t
*dip
, dev_info_t
*rdip
,
757 ddi_ctl_enum_t ctlop
, void *arg
, void *result
)
759 power_req_t
*reqp
= (power_req_t
*)arg
;
763 char path
[MAXNAMELEN
];
766 int ret
= DDI_SUCCESS
;
767 int *res
= (int *)result
;
771 char *str
= "ppm_ctlops";
772 int mask
= ppm_debug
& (D_CTLOPS1
| D_CTLOPS2
);
773 char *ctlstr
= ppm_get_ctlstr(reqp
->request_type
, mask
);
775 PPMD(mask
, ("%s: %s, %s\n",
776 str
, ddi_binding_name(rdip
), ctlstr
))
779 if (ctlop
!= DDI_CTLOPS_POWER
) {
780 return (DDI_FAILURE
);
783 unitp
= (ppm_unit_t
*)ddi_get_soft_state(ppm_statep
, ppm_inst
);
785 switch (reqp
->request_type
) {
787 /* attempt to blink led if indeed all at lowest */
788 case PMR_PPM_ALL_LOWEST
:
789 mode
= (reqp
->req
.ppm_all_lowest_req
.mode
== PM_ALL_LOWEST
);
790 if (!(unitp
->states
& PPM_STATE_SUSPENDED
) && mode
)
791 ppm_manage_led(PPM_LED_BLINKING
);
793 ppm_manage_led(PPM_LED_SOLIDON
);
794 return (DDI_SUCCESS
);
796 /* undo the claiming of 'rdip' at attach time */
797 case PMR_PPM_POST_DETACH
:
798 ASSERT(reqp
->req
.ppm_set_power_req
.who
== rdip
);
799 mutex_enter(&unitp
->lock
);
800 if (reqp
->req
.ppm_config_req
.result
!= DDI_SUCCESS
||
801 (PPM_GET_PRIVATE(rdip
) == NULL
)) {
802 mutex_exit(&unitp
->lock
);
803 return (DDI_FAILURE
);
805 mutex_exit(&unitp
->lock
);
807 return (DDI_SUCCESS
);
809 /* chance to adjust pwr_cnt if resume is about to power up rdip */
810 case PMR_PPM_PRE_RESUME
:
811 ppm_svc_resume_ctlop(rdip
, reqp
);
812 return (DDI_SUCCESS
);
815 * synchronizing, so that only the owner of the power lock is
816 * permitted to change device and component's power level.
818 case PMR_PPM_UNLOCK_POWER
:
819 case PMR_PPM_TRY_LOCK_POWER
:
820 case PMR_PPM_LOCK_POWER
:
821 ppmd
= PPM_GET_PRIVATE(rdip
);
824 else if (reqp
->request_type
!= PMR_PPM_UNLOCK_POWER
) {
825 domp
= ppm_lookup_dev(rdip
);
827 ppmd
= ppm_get_dev(rdip
, domp
);
830 PPMD(D_LOCKS
, ("ppm_lock_%s: %s, %s\n",
831 (domp
->dflags
& PPMD_LOCK_ALL
) ? "all" : "one",
832 ppmd
->path
, ppm_get_ctlstr(reqp
->request_type
, D_LOCKS
)))
834 if (domp
->dflags
& PPMD_LOCK_ALL
)
835 ppm_lock_all(domp
, reqp
, result
);
837 ppm_lock_one(ppmd
, reqp
, result
);
838 return (DDI_SUCCESS
);
840 case PMR_PPM_POWER_LOCK_OWNER
:
841 ASSERT(reqp
->req
.ppm_power_lock_owner_req
.who
== rdip
);
842 ppmd
= PPM_GET_PRIVATE(rdip
);
846 domp
= ppm_lookup_dev(rdip
);
848 ppmd
= ppm_get_dev(rdip
, domp
);
852 * In case of LOCK_ALL, effective owner of the power lock
853 * is the owner of the domain lock. otherwise, it is the owner
856 if (domp
->dflags
& PPMD_LOCK_ALL
)
857 reqp
->req
.ppm_power_lock_owner_req
.owner
=
858 mutex_owner(&domp
->lock
);
860 reqp
->req
.ppm_power_lock_owner_req
.owner
=
861 DEVI(rdip
)->devi_busy_thread
;
863 return (DDI_SUCCESS
);
865 case PMR_PPM_INIT_CHILD
:
866 ASSERT(reqp
->req
.ppm_lock_power_req
.who
== rdip
);
867 if ((domp
= ppm_lookup_dev(rdip
)) == NULL
)
868 return (DDI_SUCCESS
);
871 * We keep track of power-manageable devices starting with
872 * initialization process. The initializing flag remains
873 * set until it is cleared by ppm_add_dev(). Power management
874 * policy for some domains are affected even during device
875 * initialization. For example, PCI domains should leave
876 * their clock running meanwhile a device in that domain
879 mutex_enter(&domp
->lock
);
880 owned
= ppm_add_owned(rdip
, domp
);
881 ASSERT(owned
->initializing
== 0);
882 owned
->initializing
= 1;
884 if (PPMD_IS_PCI(domp
->model
) && domp
->status
== PPMD_OFF
) {
885 ret
= ppm_switch_clock(domp
, PPMD_ON
);
886 if (ret
== DDI_SUCCESS
)
887 domp
->dflags
|= PPMD_INITCHILD_CLKON
;
889 mutex_exit(&domp
->lock
);
892 case PMR_PPM_POST_ATTACH
:
893 ASSERT(reqp
->req
.ppm_config_req
.who
== rdip
);
894 domp
= ppm_lookup_dev(rdip
);
896 ASSERT(domp
->status
== PPMD_ON
);
897 if (reqp
->req
.ppm_config_req
.result
== DDI_SUCCESS
) {
899 * call ppm_get_dev, which will increment the
900 * domain power count by the right number.
901 * Undo the power count increment, done in PRE_PROBE.
903 if (PM_GET_PM_INFO(rdip
))
904 ppmd
= ppm_get_dev(rdip
, domp
);
905 mutex_enter(&domp
->lock
);
906 ASSERT(domp
->pwr_cnt
> 0);
908 mutex_exit(&domp
->lock
);
909 return (DDI_SUCCESS
);
912 ret
= ppm_power_down_domain(rdip
);
914 case PMR_PPM_UNINIT_CHILD
:
915 ASSERT(reqp
->req
.ppm_lock_power_req
.who
== rdip
);
916 if ((domp
= ppm_lookup_dev(rdip
)) == NULL
)
917 return (DDI_SUCCESS
);
919 (void) ddi_pathname(rdip
, path
);
920 mutex_enter(&domp
->lock
);
921 for (owned
= domp
->owned
; owned
; owned
= owned
->next
)
922 if (strcmp(owned
->path
, path
) == 0)
926 * In case we didn't go through a complete attach and detach,
927 * the initializing flag will still be set, so clear it.
929 if ((owned
!= NULL
) && (owned
->initializing
))
930 owned
->initializing
= 0;
932 if (PPMD_IS_PCI(domp
->model
) &&
933 domp
->status
== PPMD_ON
&& domp
->pwr_cnt
== 0 &&
934 (domp
->dflags
& PPMD_INITCHILD_CLKON
) &&
935 ppm_none_else_holds_power(domp
)) {
936 ret
= ppm_switch_clock(domp
, PPMD_OFF
);
937 if (ret
== DDI_SUCCESS
)
938 domp
->dflags
&= ~PPMD_INITCHILD_CLKON
;
940 mutex_exit(&domp
->lock
);
944 case PMR_PPM_UNMANAGE
:
945 case PMR_PPM_PRE_DETACH
:
946 return (DDI_SUCCESS
);
948 case PMR_PPM_PRE_PROBE
:
949 ASSERT(reqp
->req
.ppm_config_req
.who
== rdip
);
950 return (ppm_power_up_domain(rdip
));
952 case PMR_PPM_POST_PROBE
:
953 ASSERT(reqp
->req
.ppm_config_req
.who
== rdip
);
954 if (reqp
->req
.ppm_config_req
.result
== DDI_PROBE_SUCCESS
||
955 reqp
->req
.ppm_config_req
.result
== DDI_PROBE_DONTCARE
)
956 return (DDI_SUCCESS
);
959 PPMD(D_CTLOPS1
| D_CTLOPS2
, ("%s: probe failed for %s@%s "
960 "rv %d\n", str
, PM_NAME(rdip
), PM_ADDR(rdip
),
961 reqp
->req
.ppm_config_req
.result
))
962 return (ppm_power_down_domain(rdip
));
964 case PMR_PPM_PRE_ATTACH
:
965 ASSERT(reqp
->req
.ppm_config_req
.who
== rdip
);
966 /* Domain has already been powered up in PRE_PROBE */
967 domp
= ppm_lookup_dev(rdip
);
969 ASSERT(domp
->status
== PPMD_ON
);
970 return (DDI_SUCCESS
);
972 /* ppm intercepts power change process to the claimed devices */
973 case PMR_PPM_SET_POWER
:
974 case PMR_PPM_POWER_CHANGE_NOTIFY
:
975 if ((ppmd
= PPM_GET_PRIVATE(rdip
)) == NULL
) {
976 domp
= ppm_lookup_dev(rdip
);
978 ppmd
= ppm_get_dev(rdip
, domp
);
980 switch (ppmd
->domp
->model
) {
982 return (ppm_manage_cpus(rdip
, reqp
, result
));
984 return (ppm_manage_fet(rdip
, reqp
, result
));
987 return (ppm_manage_pci(rdip
, reqp
, result
));
989 return (ppm_manage_pcie(rdip
, reqp
, result
));
991 cmn_err(CE_WARN
, "ppm_ctlops: domain model %d does"
992 " not support PMR_PPM_SET_POWER ctlop",
994 return (DDI_FAILURE
);
997 case PMR_PPM_ENTER_SX
:
998 case PMR_PPM_EXIT_SX
:
999 s3args
.s3a_state
= reqp
->req
.ppm_power_enter_sx_req
.sx_state
;
1000 s3args
.s3a_test_point
=
1001 reqp
->req
.ppm_power_enter_sx_req
.test_point
;
1002 s3args
.s3a_wakephys
= reqp
->req
.ppm_power_enter_sx_req
.wakephys
;
1003 s3args
.s3a_psr
= reqp
->req
.ppm_power_enter_sx_req
.psr
;
1004 ret
= ppm_manage_sx(&s3args
,
1005 reqp
->request_type
== PMR_PPM_ENTER_SX
);
1007 PPMD(D_CPR
, ("ppm_manage_sx returns %d\n", ret
))
1008 return (DDI_FAILURE
);
1010 return (DDI_SUCCESS
);
1013 case PMR_PPM_SEARCH_LIST
:
1014 ret
= ppm_search_list(reqp
->req
.ppm_search_list_req
.searchlist
);
1015 reqp
->req
.ppm_search_list_req
.result
= ret
;
1018 PPMD(D_CPR
, ("ppm_search_list returns %d\n", ret
))
1019 return (DDI_FAILURE
);
1021 PPMD(D_CPR
, ("ppm_search_list returns %d\n", ret
))
1022 return (DDI_SUCCESS
);
1026 cmn_err(CE_WARN
, "ppm_ctlops: unrecognized ctlops req(%d)",
1027 reqp
->request_type
);
1028 return (DDI_FAILURE
);
1034 * Raise the power level of a subrange of cpus. Used when cpu driver
1035 * failed an attempt to lower the power of a cpu (probably because
1036 * it got busy). Need to revert the ones we already changed.
1038 * ecpup = the ppm_dev_t for the cpu which failed to lower power
1039 * level = power level to reset prior cpus to
1042 ppm_revert_cpu_power(ppm_dev_t
*ecpup
, int level
)
1045 int ret
= DDI_SUCCESS
;
1047 for (cpup
= ecpup
->domp
->devlist
; cpup
!= ecpup
; cpup
= cpup
->next
) {
1048 PPMD(D_CPU
, ("ppm_revert_cpu_power: \"%s\", revert to "
1049 "level %d\n", cpup
->path
, level
))
1051 ret
= pm_power(cpup
->dip
, 0, level
);
1052 if (ret
== DDI_SUCCESS
) {
1053 cpup
->level
= level
;
1054 cpup
->rplvl
= PM_LEVEL_UNKNOWN
;
1062 * ppm_manage_cpus - Process a request to change the power level of a cpu.
1063 * If not all cpus want to be at the same level, OR if we are currently
1064 * refusing slowdown requests due to thermal stress, we cache the request.
1065 * Otherwise, set all cpus to the new power level.
1069 ppm_manage_cpus(dev_info_t
*dip
, power_req_t
*reqp
, int *result
)
1072 char *str
= "ppm_manage_cpus";
1074 int old
, new, ret
, kmflag
;
1075 ppm_dev_t
*ppmd
, *cpup
;
1076 int change_notify
= 0;
1077 pm_ppm_devlist_t
*devlist
= NULL
, *p
;
1080 *result
= DDI_SUCCESS
;
1082 switch (reqp
->request_type
) {
1083 case PMR_PPM_SET_POWER
:
1086 case PMR_PPM_POWER_CHANGE_NOTIFY
:
1091 return (DDI_FAILURE
);
1094 ppmd
= PPM_GET_PRIVATE(dip
);
1095 ASSERT(MUTEX_HELD(&ppmd
->domp
->lock
));
1096 old
= reqp
->req
.ppm_set_power_req
.old_level
;
1097 new = reqp
->req
.ppm_set_power_req
.new_level
;
1099 if (change_notify
) {
1101 ppmd
->rplvl
= PM_LEVEL_UNKNOWN
;
1103 PPMD(D_CPU
, ("%s: Notify cpu dip %p power level has changed "
1104 "from %d to %d", str
, (void *)dip
, old
, new))
1105 return (DDI_SUCCESS
);
1108 if (ppm_manage_early_cpus(dip
, new, result
))
1111 if (new == ppmd
->level
) {
1112 PPMD(D_CPU
, ("%s: already at power level %d\n", str
, new))
1113 return (DDI_SUCCESS
);
1117 * A request from lower to higher level transition is granted and
1118 * made effective on all cpus. A request from higher to lower must
1119 * be agreed upon by all cpus.
1122 for (cpup
= ppmd
->domp
->devlist
; cpup
; cpup
= cpup
->next
) {
1123 if (cpup
->rplvl
== new)
1127 PPMD(D_SOME
, ("%s: not all cpus wants to be at new "
1128 "level %d yet.\n", str
, new))
1129 return (DDI_SUCCESS
);
1133 * If a single cpu requests power up, honor the request
1134 * powering up all cpus.
1137 PPMD(D_SOME
, ("%s: powering up device(%s@%s, %p) "
1138 "because of request from dip(%s@%s, %p), "
1139 "need pm_rescan\n", str
, PM_NAME(cpup
->dip
),
1140 PM_ADDR(cpup
->dip
), (void *)cpup
->dip
,
1141 PM_NAME(dip
), PM_ADDR(dip
), (void *)dip
))
1146 PPMD(D_SETLVL
, ("%s: \"%s\" set power level old %d, new %d \n",
1147 str
, ppmd
->path
, ppmd
->level
, new))
1148 ret
= ppm_change_cpu_power(ppmd
, new);
1151 if (ret
== DDI_SUCCESS
) {
1152 if (reqp
->req
.ppm_set_power_req
.canblock
== PM_CANBLOCK_BLOCK
)
1155 kmflag
= KM_NOSLEEP
;
1157 for (cpup
= ppmd
->domp
->devlist
; cpup
; cpup
= cpup
->next
) {
1158 if (cpup
->dip
== dip
)
1161 if ((p
= kmem_zalloc(sizeof (pm_ppm_devlist_t
),
1165 p
->ppd_who
= cpup
->dip
;
1166 p
->ppd_cmpt
= cpup
->cmpt
;
1167 p
->ppd_old_level
= old
;
1168 p
->ppd_new_level
= new;
1169 p
->ppd_next
= devlist
;
1171 PPMD(D_SETLVL
, ("%s: devlist entry[\"%s\"] %d -> %d\n",
1172 str
, cpup
->path
, old
, new))
1176 reqp
->req
.ppm_set_power_req
.cookie
= (void *) devlist
;
1178 if (do_rescan
> 0) {
1179 for (cpup
= ppmd
->domp
->devlist
; cpup
;
1180 cpup
= cpup
->next
) {
1181 if (cpup
->dip
== dip
)
1183 pm_rescan(cpup
->dip
);
1193 * ppm_svc_resume_ctlop - this is a small bookkeeping ppm does -
1194 * increments its FET domain power count, in anticipation of that
1195 * the indicated device(dip) would be powered up by its driver as
1196 * a result of cpr resuming.
1200 ppm_svc_resume_ctlop(dev_info_t
*dip
, power_req_t
*reqp
)
1204 int powered
; /* power up count per dip */
1206 ppmd
= PPM_GET_PRIVATE(dip
);
1211 * Maintain correct powered count for domain which cares
1215 mutex_enter(&domp
->lock
);
1216 if ((domp
->model
== PPMD_FET
) || PPMD_IS_PCI(domp
->model
) ||
1217 (domp
->model
== PPMD_PCIE
)) {
1218 for (ppmd
= domp
->devlist
; ppmd
; ppmd
= ppmd
->next
) {
1219 if (ppmd
->dip
== dip
&& ppmd
->level
)
1224 * All fets and clocks are held on during suspend -
1225 * resume window regardless their domain devices' power
1228 ASSERT(domp
->status
== PPMD_ON
);
1231 * The difference indicates the number of components
1232 * being off prior to suspend operation, that is the
1233 * amount needs to be compensated in order to sync up
1234 * bookkeeping with reality, for PROM reset would have
1235 * brought up all devices.
1237 if (powered
< PM_NUMCMPTS(dip
))
1238 domp
->pwr_cnt
+= PM_NUMCMPTS(dip
) - powered
;
1240 for (ppmd
= domp
->devlist
; ppmd
; ppmd
= ppmd
->next
) {
1241 if (ppmd
->dip
== dip
)
1242 ppmd
->level
= ppmd
->rplvl
= PM_LEVEL_UNKNOWN
;
1244 mutex_exit(&domp
->lock
);
1248 static int ppmbringup
= 0;
1252 ppm_bringup_domains()
1255 char *str
= "ppm_bringup_domains";
1258 int ret
= DDI_SUCCESS
;
1260 PPMD(D_CPR
, ("%s[%d]: enter\n", str
, ++ppmbringup
))
1261 for (domp
= ppm_domain_p
; domp
; domp
= domp
->next
) {
1262 if ((!PPMD_IS_PCI(domp
->model
) && (domp
->model
!= PPMD_FET
) &&
1263 (domp
->model
!= PPMD_PCIE
)) || (domp
->devlist
== NULL
))
1266 mutex_enter(&domp
->lock
);
1267 if (domp
->status
== PPMD_ON
) {
1268 mutex_exit(&domp
->lock
);
1271 switch (domp
->model
) {
1273 ret
= ppm_fetset(domp
, PPMD_ON
);
1277 ret
= ppm_switch_clock(domp
, PPMD_ON
);
1280 ret
= ppm_pcie_pwr(domp
, PPMD_ON
);
1285 mutex_exit(&domp
->lock
);
1287 PPMD(D_CPR
, ("%s[%d]: exit\n", str
, ppmbringup
))
1293 static int ppmsyncbp
= 0;
1297 ppm_sync_bookkeeping()
1300 char *str
= "ppm_sync_bookkeeping";
1303 int ret
= DDI_SUCCESS
;
1305 PPMD(D_CPR
, ("%s[%d]: enter\n", str
, ++ppmsyncbp
))
1306 for (domp
= ppm_domain_p
; domp
; domp
= domp
->next
) {
1307 if ((!PPMD_IS_PCI(domp
->model
) && (domp
->model
!= PPMD_FET
) &&
1308 (domp
->model
!= PPMD_PCIE
)) || (domp
->devlist
== NULL
))
1311 mutex_enter(&domp
->lock
);
1312 if ((domp
->pwr_cnt
!= 0) || !ppm_none_else_holds_power(domp
)) {
1313 mutex_exit(&domp
->lock
);
1318 * skip NULL .devlist slot, for some may host pci device
1319 * that can not tolerate clock off or not even participate
1322 if (domp
->devlist
== NULL
)
1325 switch (domp
->model
) {
1327 ret
= ppm_fetset(domp
, PPMD_OFF
);
1331 ret
= ppm_switch_clock(domp
, PPMD_OFF
);
1334 ret
= ppm_pcie_pwr(domp
, PPMD_OFF
);
1339 mutex_exit(&domp
->lock
);
1341 PPMD(D_CPR
, ("%s[%d]: exit\n", str
, ppmsyncbp
))
1349 * pre-suspend window;
1351 * power up every FET and PCI clock that are off;
1353 * set ppm_cpr_window global flag to indicate
1354 * that even though all pm_scan requested power transitions
1355 * will be honored as usual but that until we're out
1356 * of this window, no FET or clock will be turned off
1357 * for domains with pwr_cnt decremented down to 0.
1358 * Such is to avoid accessing the orthogonal drivers that own
1359 * the FET and clock registers that may not be resumed yet.
1361 * at post-resume window, walk through each FET and PCI domains,
1362 * bring pwr_cnt and domp->status to sense: if pwr-cnt == 0,
1363 * and noinvol check okays, power down the FET or PCI. At last,
1364 * clear the global flag ppm_cpr_window.
1366 * ASSERT case 1, during cpr window, checks pwr_cnt against power
1368 * ASSERT case 2, out of cpr window, checks four things:
1369 * pwr_cnt <> power transition in/out of 0
1370 * <> status <> record of noinvol device detached
1375 ppm_cpr_callb(void *arg
, int code
)
1380 case CB_CODE_CPR_CHKPT
:
1382 /* pre-suspend: start of cpr window */
1383 mutex_enter(&ppm_cpr_window_lock
);
1384 ASSERT(ppm_cpr_window_flag
== B_FALSE
);
1385 ppm_cpr_window_flag
= B_TRUE
;
1386 mutex_exit(&ppm_cpr_window_lock
);
1388 ret
= ppm_bringup_domains();
1392 case CB_CODE_CPR_RESUME
:
1394 /* post-resume: end of cpr window */
1395 ret
= ppm_sync_bookkeeping();
1397 mutex_enter(&ppm_cpr_window_lock
);
1398 ASSERT(ppm_cpr_window_flag
== B_TRUE
);
1399 ppm_cpr_window_flag
= B_FALSE
;
1400 mutex_exit(&ppm_cpr_window_lock
);
1405 return (ret
== DDI_SUCCESS
);
1410 * Initialize our private version of real power level
1411 * as well as lowest and highest levels the device supports;
1412 * relate to ppm_add_dev
1415 ppm_dev_init(ppm_dev_t
*ppmd
)
1417 struct pm_component
*dcomps
;
1418 struct pm_comp
*pm_comp
;
1422 ASSERT(MUTEX_HELD(&ppmd
->domp
->lock
));
1423 ppmd
->level
= PM_LEVEL_UNKNOWN
;
1424 ppmd
->rplvl
= PM_LEVEL_UNKNOWN
;
1426 /* increment pwr_cnt per component */
1427 if ((ppmd
->domp
->model
== PPMD_FET
) ||
1428 PPMD_IS_PCI(ppmd
->domp
->model
) ||
1429 (ppmd
->domp
->model
== PPMD_PCIE
))
1430 ppmd
->domp
->pwr_cnt
++;
1435 * ppm exists to handle power-manageable devices which require
1436 * special handling on the current platform. However, a
1437 * driver for such a device may choose not to support power
1438 * management on a particular load/attach. In this case we
1439 * we create a structure to represent a single-component device
1440 * for which "level" = PM_LEVEL_UNKNOWN and "lowest" = 0
1441 * are effectively constant.
1443 if (PM_GET_PM_INFO(dip
)) {
1444 dcomps
= DEVI(dip
)->devi_pm_components
;
1445 pm_comp
= &dcomps
[ppmd
->cmpt
].pmc_comp
;
1447 ppmd
->lowest
= pm_comp
->pmc_lvals
[0];
1448 ASSERT(ppmd
->lowest
>= 0);
1449 maxi
= pm_comp
->pmc_numlevels
- 1;
1450 ppmd
->highest
= pm_comp
->pmc_lvals
[maxi
];
1453 * If 66mhz PCI device on pci 66mhz bus supports D2 state
1454 * (config reg PMC bit 10 set), ppm could turn off its bus
1455 * clock once it is at D3hot.
1457 if (ppmd
->domp
->dflags
& PPMD_PCI66MHZ
) {
1458 for (i
= 0; i
< maxi
; i
++)
1459 if (pm_comp
->pmc_lvals
[i
] == PM_LEVEL_D2
) {
1460 ppmd
->flags
|= PPMDEV_PCI66_D2
;
1467 * If device is in PCI_PROP domain and has exported the
1468 * property listed in ppm.conf, its clock will be turned
1469 * off when all pm'able devices in that domain are at D3.
1471 if ((ppmd
->domp
->model
== PPMD_PCI_PROP
) &&
1472 (ppmd
->domp
->propname
!= NULL
) &&
1473 ddi_prop_exists(DDI_DEV_T_ANY
, dip
, DDI_PROP_DONTPASS
,
1474 ppmd
->domp
->propname
))
1475 ppmd
->flags
|= PPMDEV_PCI_PROP_CLKPM
;
1480 * relate to ppm_rem_dev
1483 ppm_dev_fini(ppm_dev_t
*ppmd
)
1485 ASSERT(MUTEX_HELD(&ppmd
->domp
->lock
));
1487 /* decrement pwr_cnt per component */
1488 if ((ppmd
->domp
->model
== PPMD_FET
) ||
1489 PPMD_IS_PCI(ppmd
->domp
->model
) ||
1490 (ppmd
->domp
->model
== PPMD_PCIE
))
1491 if (ppmd
->level
!= ppmd
->lowest
)
1492 ppmd
->domp
->pwr_cnt
--;
1496 * Each power fet controls the power of one or more platform
1497 * device(s) within their domain. Hence domain devices' power
1498 * level change has been monitored, such that once all devices
1499 * are powered off, the fet is turned off to save more power.
1501 * To power on any domain device, the domain power fet
1502 * needs to be turned on first. always one fet per domain.
1505 ppm_manage_fet(dev_info_t
*dip
, power_req_t
*reqp
, int *result
)
1508 char *str
= "ppm_manage_fet";
1510 int (*pwr_func
)(ppm_dev_t
*, int, int);
1518 *result
= DDI_SUCCESS
;
1519 switch (reqp
->request_type
) {
1520 case PMR_PPM_SET_POWER
:
1521 pwr_func
= ppm_change_power_level
;
1522 old
= reqp
->req
.ppm_set_power_req
.old_level
;
1523 new = reqp
->req
.ppm_set_power_req
.new_level
;
1524 cmpt
= reqp
->req
.ppm_set_power_req
.cmpt
;
1526 case PMR_PPM_POWER_CHANGE_NOTIFY
:
1527 pwr_func
= ppm_record_level_change
;
1528 old
= reqp
->req
.ppm_notify_level_req
.old_level
;
1529 new = reqp
->req
.ppm_notify_level_req
.new_level
;
1530 cmpt
= reqp
->req
.ppm_notify_level_req
.cmpt
;
1533 *result
= DDI_FAILURE
;
1534 PPMD(D_FET
, ("%s: unknown request type %d for %s@%s\n",
1535 str
, reqp
->request_type
, PM_NAME(dip
), PM_ADDR(dip
)))
1536 return (DDI_FAILURE
);
1539 for (ppmd
= PPM_GET_PRIVATE(dip
); ppmd
; ppmd
= ppmd
->next
)
1540 if (cmpt
== ppmd
->cmpt
)
1543 PPMD(D_FET
, ("%s: dip(%p): old(%d)->new(%d): no ppm_dev"
1544 " found for cmpt(%d)", str
, (void *)dip
, old
, new, cmpt
))
1545 *result
= DDI_FAILURE
;
1546 return (DDI_FAILURE
);
1549 PPMD(D_FET
, ("%s: %s@%s %s old %d, new %d, c%d, level %d, "
1550 "status %s\n", str
, PM_NAME(dip
), PM_ADDR(dip
),
1551 ppm_get_ctlstr(reqp
->request_type
, ~0), old
, new, cmpt
,
1552 ppmd
->level
, (domp
->status
== PPMD_OFF
? "off" : "on")))
1555 ASSERT(old
== ppmd
->level
);
1557 if (new == ppmd
->level
) {
1558 PPMD(D_FET
, ("nop\n"))
1559 return (DDI_SUCCESS
);
1562 PPM_LOCK_DOMAIN(domp
);
1565 * In general, a device's published lowest power level does not
1566 * have to be 0 if power-off is not tolerated. i.e. a device
1567 * instance may export its lowest level > 0. It is reasonable to
1568 * assume that level 0 indicates off state, positive level values
1569 * indicate power states above off, include full power state.
1571 if (new > 0) { /* device powering up or to different positive level */
1572 if (domp
->status
== PPMD_OFF
) {
1574 /* can not be in (chpt, resume) window */
1575 ASSERT(ppm_cpr_window_flag
== B_FALSE
);
1577 ASSERT(old
== 0 && domp
->pwr_cnt
== 0);
1579 PPMD(D_FET
, ("About to turn fet on for %s@%s c%d\n",
1580 PM_NAME(dip
), PM_ADDR(dip
), cmpt
))
1582 *result
= ppm_fetset(domp
, PPMD_ON
);
1583 if (*result
!= DDI_SUCCESS
) {
1584 PPMD(D_FET
, ("\tCan't turn on power FET: "
1585 "ret(%d)\n", *result
))
1586 PPM_UNLOCK_DOMAIN(domp
);
1587 return (DDI_FAILURE
);
1592 * If powering up, pre-increment the count before
1593 * calling pwr_func, because we are going to release
1594 * the domain lock and another thread might turn off
1595 * domain power otherwise.
1602 PPMD(D_FET
, ("\t%s domain power count: %d\n",
1603 domp
->name
, domp
->pwr_cnt
))
1607 PPM_UNLOCK_DOMAIN(domp
);
1609 ASSERT(domp
->pwr_cnt
> 0);
1611 if ((*result
= (*pwr_func
)(ppmd
, cmpt
, new)) != DDI_SUCCESS
) {
1612 PPMD(D_FET
, ("\t%s power change failed: ret(%d)\n",
1613 ppmd
->path
, *result
))
1616 PPM_LOCK_DOMAIN(domp
);
1619 * Decr the power count in two cases:
1621 * 1) request was to power device down and was successful
1622 * 2) request was to power up (we pre-incremented count), but failed.
1624 if ((*result
== DDI_SUCCESS
&& ppmd
->level
== 0) ||
1625 (*result
!= DDI_SUCCESS
&& incr
)) {
1626 ASSERT(domp
->pwr_cnt
> 0);
1630 PPMD(D_FET
, ("\t%s domain power count: %d\n",
1631 domp
->name
, domp
->pwr_cnt
))
1634 * call to pwr_func will update ppm data structures, if it
1635 * succeeds. ppm should return whatever is the return value
1636 * from call to pwr_func. This way pm and ppm data structures
1637 * always in sync. Use dummy_ret from here for any further
1640 if ((domp
->pwr_cnt
== 0) &&
1641 (ppm_cpr_window_flag
== B_FALSE
) &&
1642 ppm_none_else_holds_power(domp
)) {
1644 PPMD(D_FET
, ("About to turn FET off for %s@%s c%d\n",
1645 PM_NAME(dip
), PM_ADDR(dip
), cmpt
))
1647 dummy_ret
= ppm_fetset(domp
, PPMD_OFF
);
1648 if (dummy_ret
!= DDI_SUCCESS
) {
1649 PPMD(D_FET
, ("\tCan't turn off FET: ret(%d)\n",
1654 PPM_UNLOCK_DOMAIN(domp
);
1655 ASSERT(domp
->pwr_cnt
>= 0);
1661 * the actual code that turn on or off domain power fet and
1662 * update domain status
1665 ppm_fetset(ppm_domain_t
*domp
, uint8_t value
)
1667 char *str
= "ppm_fetset";
1674 key
= (value
== PPMD_ON
) ? PPMDC_FET_ON
: PPMDC_FET_OFF
;
1675 for (dc
= domp
->dc
; dc
; dc
= dc
->next
)
1678 if (!dc
|| !dc
->lh
) {
1679 PPMD(D_FET
, ("%s: %s domain: NULL ppm_dc handle\n",
1681 return (DDI_FAILURE
);
1684 if (key
== PPMDC_FET_ON
) {
1685 PPM_GET_IO_DELAY(dc
, delay
);
1686 if (delay
> 0 && domp
->last_off_time
> 0) {
1688 * provide any delay required before turning on.
1689 * some devices e.g. Samsung DVD require minimum
1690 * of 1 sec between OFF->ON. no delay is required
1691 * for the first time.
1693 temp
= ddi_get_lbolt();
1694 temp
-= domp
->last_off_time
;
1695 temp
= drv_hztousec(temp
);
1699 * busy wait untill we meet the
1700 * required delay. Since we maintain
1701 * time stamps in terms of clock ticks
1702 * we might wait for longer than required
1704 PPMD(D_FET
, ("%s : waiting %lu micro seconds "
1705 "before on\n", domp
->name
,
1707 drv_usecwait(delay
- temp
);
1711 switch (dc
->method
) {
1713 case PPMDC_I2CKIO
: {
1715 i2c_req
.reg_mask
= dc
->m_un
.i2c
.mask
;
1716 i2c_req
.reg_val
= dc
->m_un
.i2c
.val
;
1717 ret
= ldi_ioctl(dc
->lh
, dc
->m_un
.i2c
.iowr
,
1718 (intptr_t)&i2c_req
, FWRITE
| FKIOCTL
, kcred
, NULL
);
1724 ret
= ldi_ioctl(dc
->lh
, dc
->m_un
.kio
.iowr
,
1725 (intptr_t)&(dc
->m_un
.kio
.val
), FWRITE
| FKIOCTL
, kcred
,
1730 PPMD(D_FET
, ("\t%s: unsupported domain control method %d\n",
1731 str
, domp
->dc
->method
))
1732 return (DDI_FAILURE
);
1735 PPMD(D_FET
, ("%s: %s domain(%s) FET from %s to %s\n", str
,
1736 (ret
== 0) ? "turned" : "failed to turn",
1738 (domp
->status
== PPMD_ON
) ? "ON" : "OFF",
1739 (value
== PPMD_ON
) ? "ON" : "OFF"))
1741 if (ret
== DDI_SUCCESS
) {
1742 domp
->status
= value
;
1744 if (key
== PPMDC_FET_OFF
)
1746 * record the time, when it is off. time is recorded
1749 domp
->last_off_time
= ddi_get_lbolt();
1751 /* implement any post op delay. */
1752 if (key
== PPMDC_FET_ON
) {
1753 PPM_GET_IO_POST_DELAY(dc
, delay
);
1754 PPMD(D_FET
, ("%s : waiting %lu micro seconds "
1755 "after on\n", domp
->name
, delay
))
1757 drv_usecwait(delay
);
1766 * read power fet status
1769 ppm_fetget(ppm_domain_t
*domp
, uint8_t *lvl
)
1771 char *str
= "ppm_fetget";
1772 ppm_dc_t
*dc
= domp
->dc
;
1778 PPMD(D_FET
, ("%s: %s domain NULL ppm_dc layered handle\n",
1780 return (DDI_FAILURE
);
1783 cmn_err(CE_WARN
, "%s: expect both fet on and fet off ops "
1784 "defined, found only one in domain(%s)", str
, domp
->name
);
1785 return (DDI_FAILURE
);
1788 switch (dc
->method
) {
1790 case PPMDC_I2CKIO
: {
1792 i2c_req
.reg_mask
= dc
->m_un
.i2c
.mask
;
1793 ret
= ldi_ioctl(dc
->lh
, dc
->m_un
.i2c
.iord
,
1794 (intptr_t)&i2c_req
, FWRITE
| FKIOCTL
, kcred
, NULL
);
1797 PPMD(D_FET
, ("%s: PPMDC_I2CKIO failed: ret(%d)\n",
1802 off_val
= (dc
->cmd
== PPMDC_FET_OFF
) ? dc
->m_un
.i2c
.val
:
1803 dc
->next
->m_un
.i2c
.val
;
1804 *lvl
= (i2c_req
.reg_val
== off_val
) ? PPMD_OFF
: PPMD_ON
;
1806 PPMD(D_FET
, ("%s: %s domain FET %s\n", str
, domp
->name
,
1807 (i2c_req
.reg_val
== off_val
) ? "OFF" : "ON"))
1814 ret
= ldi_ioctl(dc
->lh
, dc
->m_un
.kio
.iord
,
1815 (intptr_t)&kio_val
, FWRITE
| FKIOCTL
, kcred
, NULL
);
1817 PPMD(D_FET
, ("%s: PPMDC_KIO failed: ret(%d)\n",
1822 off_val
= (dc
->cmd
== PPMDC_FET_OFF
) ? dc
->m_un
.kio
.val
:
1823 dc
->next
->m_un
.kio
.val
;
1824 *lvl
= (kio_val
== off_val
) ? PPMD_OFF
: PPMD_ON
;
1826 PPMD(D_FET
, ("%s: %s domain FET %s\n", str
, domp
->name
,
1827 (kio_val
== off_val
) ? "OFF" : "ON"))
1832 PPMD(D_FET
, ("%s: unsupported domain control method %d\n",
1833 str
, domp
->dc
->method
))
1834 return (DDI_FAILURE
);
1837 return (DDI_SUCCESS
);
1842 * the actual code that switches pci clock and update domain status
1845 ppm_switch_clock(ppm_domain_t
*domp
, int onoff
)
1848 char *str
= "ppm_switch_clock";
1853 extern int do_polled_io
;
1854 extern uint_t cfb_inuse
;
1857 cmd
= (onoff
== PPMD_ON
) ? PPMDC_CLK_ON
: PPMDC_CLK_OFF
;
1858 dc
= ppm_lookup_dc(domp
, cmd
);
1860 PPMD(D_PCI
, ("%s: no ppm_dc found for domain (%s)\n",
1862 return (DDI_FAILURE
);
1865 switch (dc
->method
) {
1868 * If we're powering up cfb on a Stop-A, we only
1869 * want to do polled i/o to turn ON the clock
1871 pio_save
= do_polled_io
;
1872 if ((cfb_inuse
) && (cmd
== PPMDC_CLK_ON
)) {
1873 for (pdev
= domp
->devlist
; pdev
; pdev
= pdev
->next
) {
1874 if (pm_is_cfb(pdev
->dip
)) {
1881 ret
= ldi_ioctl(dc
->lh
, dc
->m_un
.kio
.iowr
,
1882 (intptr_t)&(dc
->m_un
.kio
.val
), FWRITE
| FKIOCTL
,
1885 do_polled_io
= pio_save
;
1888 if (cmd
== PPMDC_CLK_ON
) {
1889 domp
->status
= PPMD_ON
;
1892 * PCI PM spec requires 50ms delay
1894 drv_usecwait(50000);
1896 domp
->status
= PPMD_OFF
;
1899 PPMD(D_PCI
, ("%s: %s pci clock %s for domain (%s)\n", str
,
1900 (ret
== 0) ? "turned" : "failed to turn",
1901 (cmd
== PPMDC_CLK_OFF
) ? "OFF" : "ON",
1907 PPMD(D_PCI
, ("%s: unsupported domain control method %d\n",
1909 return (DDI_FAILURE
);
1912 return (DDI_SUCCESS
);
1917 * pci slot domain is formed of pci device(s) reside in a pci slot.
1918 * This function monitors domain device's power level change, such
1920 * when all domain power count has gone to 0, it attempts to turn off
1921 * the pci slot's clock;
1922 * if any domain device is powering up, it'll turn on the pci slot's
1923 * clock as the first thing.
1927 ppm_manage_pci(dev_info_t
*dip
, power_req_t
*reqp
, int *result
)
1930 char *str
= "ppm_manage_pci";
1932 int (*pwr_func
)(ppm_dev_t
*, int, int);
1939 *result
= DDI_SUCCESS
;
1940 switch (reqp
->request_type
) {
1941 case PMR_PPM_SET_POWER
:
1942 pwr_func
= ppm_change_power_level
;
1943 old
= reqp
->req
.ppm_set_power_req
.old_level
;
1944 new = reqp
->req
.ppm_set_power_req
.new_level
;
1945 cmpt
= reqp
->req
.ppm_set_power_req
.cmpt
;
1948 case PMR_PPM_POWER_CHANGE_NOTIFY
:
1949 pwr_func
= ppm_record_level_change
;
1950 old
= reqp
->req
.ppm_notify_level_req
.old_level
;
1951 new = reqp
->req
.ppm_notify_level_req
.new_level
;
1952 cmpt
= reqp
->req
.ppm_notify_level_req
.cmpt
;
1956 *result
= DDI_FAILURE
;
1957 return (DDI_FAILURE
);
1960 for (ppmd
= PPM_GET_PRIVATE(dip
); ppmd
; ppmd
= ppmd
->next
)
1961 if (cmpt
== ppmd
->cmpt
)
1964 PPMD(D_PCI
, ("%s: dip(%p): old(%d), new(%d): no ppm_dev"
1965 " found for cmpt(%d)", str
, (void *)dip
, old
, new, cmpt
))
1966 *result
= DDI_FAILURE
;
1967 return (DDI_FAILURE
);
1970 PPMD(D_PCI
, ("%s: %s, dev(%s), c%d, old %d, new %d\n", str
,
1971 ppm_get_ctlstr(reqp
->request_type
, ~0),
1972 ppmd
->path
, cmpt
, old
, new))
1974 ASSERT(old
== ppmd
->level
);
1975 if (new == ppmd
->level
)
1976 return (DDI_SUCCESS
);
1978 PPM_LOCK_DOMAIN(domp
);
1980 if (new > 0) { /* device powering up */
1981 if (domp
->status
== PPMD_OFF
) {
1983 /* cannot be off during (chpt, resume) window */
1984 ASSERT(ppm_cpr_window_flag
== B_FALSE
);
1986 /* either both OFF or both ON */
1987 ASSERT(!((old
== 0) ^ (domp
->pwr_cnt
== 0)));
1989 PPMD(D_PCI
, ("About to turn clock on for %s@%s c%d\n",
1990 PM_NAME(dip
), PM_ADDR(dip
), cmpt
))
1992 *result
= ppm_switch_clock(domp
, PPMD_ON
);
1993 if (*result
!= DDI_SUCCESS
) {
1994 PPMD(D_PCI
, ("\tcan't switch on pci clock: "
1995 "ret(%d)\n", *result
))
1996 PPM_UNLOCK_DOMAIN(domp
);
1997 return (DDI_FAILURE
);
2006 PPMD(D_PCI
, ("\t%s domain power count: %d\n",
2007 domp
->name
, domp
->pwr_cnt
))
2010 PPM_UNLOCK_DOMAIN(domp
);
2012 ASSERT(domp
->pwr_cnt
> 0);
2014 if ((*result
= (*pwr_func
)(ppmd
, cmpt
, new)) != DDI_SUCCESS
) {
2015 PPMD(D_PCI
, ("\t%s power change failed: ret(%d)\n",
2016 ppmd
->path
, *result
))
2019 PPM_LOCK_DOMAIN(domp
);
2022 * Decr the power count in two cases:
2024 * 1) request was to power device down and was successful
2025 * 2) request was to power up (we pre-incremented count), but failed.
2027 if ((*result
== DDI_SUCCESS
&& ppmd
->level
== 0) ||
2028 (*result
!= DDI_SUCCESS
&& incr
)) {
2029 ASSERT(domp
->pwr_cnt
> 0);
2033 PPMD(D_PCI
, ("\t%s domain power count: %d\n",
2034 domp
->name
, domp
->pwr_cnt
))
2037 * call to pwr_func will update ppm data structures, if it
2038 * succeeds. ppm should return whatever is the return value
2039 * from call to pwr_func. This way pm and ppm data structures
2040 * always in sync. Use dummy_ret from here for any further
2043 if ((domp
->pwr_cnt
== 0) &&
2044 (ppm_cpr_window_flag
== B_FALSE
) &&
2045 ppm_none_else_holds_power(domp
)) {
2047 PPMD(D_PCI
, ("About to turn clock off for %s@%s c%d\n",
2048 PM_NAME(dip
), PM_ADDR(dip
), cmpt
))
2050 dummy_ret
= ppm_switch_clock(domp
, PPMD_OFF
);
2051 if (dummy_ret
!= DDI_SUCCESS
) {
2052 PPMD(D_PCI
, ("\tCan't switch clock off: "
2053 "ret(%d)\n", dummy_ret
))
2057 PPM_UNLOCK_DOMAIN(domp
);
2058 ASSERT(domp
->pwr_cnt
>= 0);
2063 * When the driver for the primary PCI-Express child has set the device to
2064 * lowest power (D3hot), we come here to save even more power by transitioning
2065 * the slot to D3cold. Similarly, if the slot is in D3cold and we need to
2066 * power up the child, we come here first to power up the slot.
2070 ppm_manage_pcie(dev_info_t
*dip
, power_req_t
*reqp
, int *result
)
2073 char *str
= "ppm_manage_pcie";
2075 int (*pwr_func
)(ppm_dev_t
*, int, int);
2082 *result
= DDI_SUCCESS
;
2083 switch (reqp
->request_type
) {
2084 case PMR_PPM_SET_POWER
:
2085 pwr_func
= ppm_change_power_level
;
2086 old
= reqp
->req
.ppm_set_power_req
.old_level
;
2087 new = reqp
->req
.ppm_set_power_req
.new_level
;
2088 cmpt
= reqp
->req
.ppm_set_power_req
.cmpt
;
2091 case PMR_PPM_POWER_CHANGE_NOTIFY
:
2092 pwr_func
= ppm_record_level_change
;
2093 old
= reqp
->req
.ppm_notify_level_req
.old_level
;
2094 new = reqp
->req
.ppm_notify_level_req
.new_level
;
2095 cmpt
= reqp
->req
.ppm_notify_level_req
.cmpt
;
2099 *result
= DDI_FAILURE
;
2100 return (DDI_FAILURE
);
2103 for (ppmd
= PPM_GET_PRIVATE(dip
); ppmd
; ppmd
= ppmd
->next
)
2104 if (cmpt
== ppmd
->cmpt
)
2107 PPMD(D_PCI
, ("%s: dip(%p): old(%d), new(%d): no ppm_dev"
2108 " found for cmpt(%d)", str
, (void *)dip
, old
, new, cmpt
))
2109 *result
= DDI_FAILURE
;
2110 return (DDI_FAILURE
);
2113 PPMD(D_PCI
, ("%s: %s, dev(%s), c%d, old %d, new %d\n", str
,
2114 ppm_get_ctlstr(reqp
->request_type
, ~0),
2115 ppmd
->path
, cmpt
, old
, new))
2117 ASSERT(old
== ppmd
->level
);
2118 if (new == ppmd
->level
)
2119 return (DDI_SUCCESS
);
2121 PPM_LOCK_DOMAIN(domp
);
2123 if (new > 0) { /* device powering up */
2124 if (domp
->status
== PPMD_OFF
) {
2126 /* cannot be off during (chpt, resume) window */
2127 ASSERT(ppm_cpr_window_flag
== B_FALSE
);
2129 /* either both OFF or both ON */
2130 ASSERT(!((old
== 0) ^ (domp
->pwr_cnt
== 0)));
2132 PPMD(D_PCI
, ("About to turn on pcie slot for "
2133 "%s@%s c%d\n", PM_NAME(dip
), PM_ADDR(dip
), cmpt
))
2135 *result
= ppm_pcie_pwr(domp
, PPMD_ON
);
2136 if (*result
!= DDI_SUCCESS
) {
2137 PPMD(D_PCI
, ("\tcan't switch on pcie slot: "
2138 "ret(%d)\n", *result
))
2139 PPM_UNLOCK_DOMAIN(domp
);
2140 return (DDI_FAILURE
);
2149 PPMD(D_PCI
, ("\t%s domain power count: %d\n",
2150 domp
->name
, domp
->pwr_cnt
))
2153 PPM_UNLOCK_DOMAIN(domp
);
2155 ASSERT(domp
->pwr_cnt
> 0);
2157 if ((*result
= (*pwr_func
)(ppmd
, cmpt
, new)) != DDI_SUCCESS
) {
2158 PPMD(D_PCI
, ("\t%s power change failed: ret(%d)\n",
2159 ppmd
->path
, *result
))
2162 PPM_LOCK_DOMAIN(domp
);
2165 * Decr the power count in two cases:
2167 * 1) request was to power device down and was successful
2168 * 2) request was to power up (we pre-incremented count), but failed.
2170 if ((*result
== DDI_SUCCESS
&& ppmd
->level
== 0) ||
2171 (*result
!= DDI_SUCCESS
&& incr
)) {
2172 ASSERT(domp
->pwr_cnt
> 0);
2176 PPMD(D_PCI
, ("\t%s domain power count: %d\n",
2177 domp
->name
, domp
->pwr_cnt
))
2180 * call to pwr_func will update ppm data structures, if it
2181 * succeeds. ppm should return whatever is the return value
2182 * from call to pwr_func. This way pm and ppm data structures
2183 * always in sync. Use dummy_ret from here for any further
2186 if ((domp
->pwr_cnt
== 0) &&
2187 (ppm_cpr_window_flag
== B_FALSE
) &&
2188 ppm_none_else_holds_power(domp
)) {
2190 PPMD(D_PCI
, ("About to turn off pcie slot for %s@%s c%d\n",
2191 PM_NAME(dip
), PM_ADDR(dip
), cmpt
))
2193 dummy_ret
= ppm_pcie_pwr(domp
, PPMD_OFF
);
2194 if (dummy_ret
!= DDI_SUCCESS
) {
2195 PPMD(D_PCI
, ("\tCan't switch pcie slot off: "
2196 "ret(%d)\n", dummy_ret
))
2200 PPM_UNLOCK_DOMAIN(domp
);
2201 ASSERT(domp
->pwr_cnt
>= 0);
2207 * Set or clear a bit on a GPIO device. These bits are used for various device-
2208 * specific purposes.
2211 ppm_gpioset(ppm_domain_t
*domp
, int key
)
2214 char *str
= "ppm_gpioset";
2220 for (dc
= domp
->dc
; dc
; dc
= dc
->next
)
2223 if (!dc
|| !dc
->lh
) {
2224 PPMD(D_GPIO
, ("%s: %s domain: NULL ppm_dc handle\n",
2226 return (DDI_FAILURE
);
2229 PPM_GET_IO_DELAY(dc
, delay
);
2231 PPMD(D_GPIO
, ("%s : waiting %lu micro seconds "
2232 "before change\n", domp
->name
, delay
))
2233 drv_usecwait(delay
);
2236 switch (dc
->method
) {
2238 case PPMDC_I2CKIO
: {
2242 extern int do_polled_io
;
2243 extern uint_t cfb_inuse
;
2244 i2c_req
.reg_mask
= dc
->m_un
.i2c
.mask
;
2245 i2c_req
.reg_val
= dc
->m_un
.i2c
.val
;
2247 pio_save
= do_polled_io
;
2249 for (pdev
= domp
->devlist
; pdev
; pdev
= pdev
->next
) {
2250 if (pm_is_cfb(pdev
->dip
)) {
2252 PPMD(D_GPIO
, ("%s: cfb is in use, "
2253 "i2c transaction is done in "
2254 "poll-mode.\n", str
))
2259 ret
= ldi_ioctl(dc
->lh
, dc
->m_un
.i2c
.iowr
,
2260 (intptr_t)&i2c_req
, FWRITE
| FKIOCTL
, kcred
, NULL
);
2261 do_polled_io
= pio_save
;
2263 PPMD(D_GPIO
, ("%s: %s domain(%s) from %s by writing %x "
2265 str
, (ret
== 0) ? "turned" : "FAILed to turn",
2267 (domp
->status
== PPMD_ON
) ? "ON" : "OFF",
2275 ret
= ldi_ioctl(dc
->lh
, dc
->m_un
.kio
.iowr
,
2276 (intptr_t)&(dc
->m_un
.kio
.val
), FWRITE
| FKIOCTL
, kcred
,
2279 PPMD(D_GPIO
, ("%s: %s domain(%s) from %s by writing %x "
2281 str
, (ret
== 0) ? "turned" : "FAILed to turn",
2283 (domp
->status
== PPMD_ON
) ? "ON" : "OFF",
2289 PPMD(D_GPIO
, ("\t%s: unsupported domain control method %d\n",
2290 str
, domp
->dc
->method
))
2291 return (DDI_FAILURE
);
2294 /* implement any post op delay. */
2295 PPM_GET_IO_POST_DELAY(dc
, delay
);
2297 PPMD(D_GPIO
, ("%s : waiting %lu micro seconds "
2298 "after change\n", domp
->name
, delay
))
2299 drv_usecwait(delay
);
2306 ppm_pcie_pwr(ppm_domain_t
*domp
, int onoff
)
2309 char *str
= "ppm_pcie_pwr";
2311 int ret
= DDI_FAILURE
;
2315 ASSERT(onoff
== PPMD_OFF
|| onoff
== PPMD_ON
);
2317 dc
= ppm_lookup_dc(domp
,
2318 onoff
== PPMD_ON
? PPMDC_PRE_PWR_ON
: PPMDC_PRE_PWR_OFF
);
2322 * Invoke layered ioctl for pcie root complex nexus to
2323 * transition the link
2325 ASSERT(dc
->method
== PPMDC_KIO
);
2326 delay
= dc
->m_un
.kio
.delay
;
2328 PPMD(D_GPIO
, ("%s : waiting %lu micro seconds "
2329 "before change\n", domp
->name
, delay
))
2330 drv_usecwait(delay
);
2332 ret
= ldi_ioctl(dc
->lh
, dc
->m_un
.kio
.iowr
,
2333 (intptr_t)&(dc
->m_un
.kio
.val
),
2334 FWRITE
| FKIOCTL
, kcred
, NULL
);
2335 if (ret
== DDI_SUCCESS
) {
2336 delay
= dc
->m_un
.kio
.post_delay
;
2338 PPMD(D_GPIO
, ("%s : waiting %lu micro seconds "
2339 "after change\n", domp
->name
, delay
))
2340 drv_usecwait(delay
);
2343 PPMD(D_PCI
, ("%s: ldi_ioctl FAILED for domain(%s)\n",
2351 /* Turn off the clock for this slot. */
2352 if ((ret
= ppm_gpioset(domp
, PPMDC_CLK_OFF
)) != DDI_SUCCESS
) {
2354 ("%s: failed to turn off domain(%s) clock\n",
2359 /* Turn off the power to this slot */
2360 if ((ret
= ppm_gpioset(domp
, PPMDC_PWR_OFF
)) != DDI_SUCCESS
) {
2362 ("%s: failed to turn off domain(%s) power\n",
2368 /* Assert RESET for this slot. */
2369 if ((ret
= ppm_gpioset(domp
, PPMDC_RESET_ON
)) != DDI_SUCCESS
) {
2371 ("%s: failed to assert reset for domain(%s)\n",
2376 /* Turn on the power to this slot */
2377 if ((ret
= ppm_gpioset(domp
, PPMDC_PWR_ON
)) != DDI_SUCCESS
) {
2379 ("%s: failed to turn on domain(%s) power\n",
2384 /* Turn on the clock for this slot */
2385 if ((ret
= ppm_gpioset(domp
, PPMDC_CLK_ON
)) != DDI_SUCCESS
) {
2387 ("%s: failed to turn on domain(%s) clock\n",
2392 /* De-assert RESET for this slot. */
2393 if ((ret
= ppm_gpioset(domp
, PPMDC_RESET_OFF
)) != DDI_SUCCESS
) {
2395 ("%s: failed to de-assert reset for domain(%s)\n",
2400 dc
= ppm_lookup_dc(domp
, PPMDC_POST_PWR_ON
);
2403 * Invoke layered ioctl to PCIe root complex nexus
2404 * to transition the link.
2406 ASSERT(dc
->method
== PPMDC_KIO
);
2407 delay
= dc
->m_un
.kio
.delay
;
2409 PPMD(D_GPIO
, ("%s: waiting %lu micro seconds "
2410 "before change\n", domp
->name
, delay
))
2411 drv_usecwait(delay
);
2413 ret
= ldi_ioctl(dc
->lh
, dc
->m_un
.kio
.iowr
,
2414 (intptr_t)&(dc
->m_un
.kio
.val
),
2415 FWRITE
| FKIOCTL
, kcred
, NULL
);
2417 if (ret
!= DDI_SUCCESS
) {
2418 PPMD(D_PCI
, ("%s: layered ioctl to PCIe"
2419 "root complex nexus FAILed\n", str
))
2423 delay
= dc
->m_un
.kio
.post_delay
;
2425 PPMD(D_GPIO
, ("%s: waiting %lu micro "
2426 "seconds after change\n",
2428 drv_usecwait(delay
);
2436 PPMD(D_PCI
, ("%s: turned domain(%s) PCIe slot power from %s to %s\n",
2437 str
, domp
->name
, (domp
->status
== PPMD_ON
) ? "ON" : "OFF",
2438 onoff
== PPMD_ON
? "ON" : "OFF"))
2440 domp
->status
= onoff
;
2446 * Change the power level for a component of a device. If the change
2447 * arg is true, we call the framework to actually change the device's
2448 * power; otherwise, we just update our own copy of the power level.
2451 ppm_set_level(ppm_dev_t
*ppmd
, int cmpt
, int level
, boolean_t change
)
2454 char *str
= "ppm_set_level";
2460 ret
= pm_power(ppmd
->dip
, cmpt
, level
);
2462 PPMD(D_SETLVL
, ("%s: %s change=%d, old %d, new %d, ret %d\n",
2463 str
, ppmd
->path
, change
, ppmd
->level
, level
, ret
))
2465 if (ret
== DDI_SUCCESS
) {
2466 ppmd
->level
= level
;
2467 ppmd
->rplvl
= PM_LEVEL_UNKNOWN
;
2475 ppm_change_power_level(ppm_dev_t
*ppmd
, int cmpt
, int level
)
2477 return (ppm_set_level(ppmd
, cmpt
, level
, B_TRUE
));
2482 ppm_record_level_change(ppm_dev_t
*ppmd
, int cmpt
, int level
)
2484 return (ppm_set_level(ppmd
, cmpt
, level
, B_FALSE
));
2489 ppm_manage_led(int action
)
2496 PPMD(D_LED
, ("ppm_manage_led: action: %s\n",
2497 (action
== PPM_LED_BLINKING
) ? "PPM_LED_BLINKING" :
2501 * test whether led operation is practically supported,
2502 * if not, we waive without pressing for reasons
2504 if (!ppm_lookup_dc(NULL
, PPMDC_LED_ON
))
2507 unitp
= ddi_get_soft_state(ppm_statep
, ppm_inst
);
2508 for (domp
= ppm_domain_p
; (domp
&& (domp
->model
!= PPMD_LED
)); )
2511 mutex_enter(&unitp
->lock
);
2512 if (action
== PPM_LED_BLINKING
) {
2513 ppm_set_led(domp
, PPMD_OFF
);
2514 unitp
->led_tid
= timeout(
2515 ppm_blink_led
, domp
, PPM_LEDOFF_INTERVAL
);
2517 } else { /* PPM_LED_SOLIDON */
2518 ASSERT(action
== PPM_LED_SOLIDON
);
2519 tid
= unitp
->led_tid
;
2522 mutex_exit(&unitp
->lock
);
2523 (void) untimeout(tid
);
2525 mutex_enter(&unitp
->lock
);
2526 ppm_set_led(domp
, PPMD_ON
);
2528 mutex_exit(&unitp
->lock
);
2533 ppm_set_led(ppm_domain_t
*domp
, int val
)
2537 ret
= ppm_gpioset(domp
,
2538 (val
== PPMD_ON
) ? PPMDC_LED_ON
: PPMDC_LED_OFF
);
2540 PPMD(D_LED
, ("ppm_set_led: %s LED from %s\n",
2541 (ret
== 0) ? "turned" : "FAILed to turn",
2542 (domp
->status
== PPMD_ON
) ? "ON to OFF" : "OFF to ON"))
2544 if (ret
== DDI_SUCCESS
)
2550 ppm_blink_led(void *arg
)
2554 ppm_domain_t
*domp
= arg
;
2556 unitp
= ddi_get_soft_state(ppm_statep
, ppm_inst
);
2558 mutex_enter(&unitp
->lock
);
2559 if (unitp
->led_tid
== 0) {
2560 mutex_exit(&unitp
->lock
);
2564 if (domp
->status
== PPMD_ON
) {
2565 ppm_set_led(domp
, PPMD_OFF
);
2566 intvl
= PPM_LEDOFF_INTERVAL
;
2568 ppm_set_led(domp
, PPMD_ON
);
2569 intvl
= PPM_LEDON_INTERVAL
;
2572 unitp
->led_tid
= timeout(ppm_blink_led
, domp
, intvl
);
2573 mutex_exit(&unitp
->lock
);
2577 * Function to power up a domain, if required. It also increments the
2578 * domain pwr_cnt to prevent it from going down.
2581 ppm_power_up_domain(dev_info_t
*dip
)
2583 int ret
= DDI_SUCCESS
;
2585 char *str
= "ppm_power_up_domain";
2587 domp
= ppm_lookup_dev(dip
);
2589 mutex_enter(&domp
->lock
);
2590 switch (domp
->model
) {
2592 if (domp
->status
== PPMD_OFF
) {
2593 if ((ret
= ppm_fetset(domp
, PPMD_ON
)) ==
2595 PPMD(D_FET
, ("%s: turned on fet for %s@%s\n",
2596 str
, PM_NAME(dip
), PM_ADDR(dip
)))
2598 PPMD(D_FET
, ("%s: couldn't turn on fet "
2599 "for %s@%s\n", str
, PM_NAME(dip
),
2607 if (domp
->status
== PPMD_OFF
) {
2608 if ((ret
= ppm_switch_clock(domp
, PPMD_ON
)) ==
2610 PPMD(D_PCI
, ("%s: turned on clock for "
2611 "%s@%s\n", str
, PM_NAME(dip
),
2614 PPMD(D_PCI
, ("%s: couldn't turn on clock "
2615 "for %s@%s\n", str
, PM_NAME(dip
),
2622 if (domp
->status
== PPMD_OFF
) {
2623 if ((ret
= ppm_pcie_pwr(domp
, PPMD_ON
)) ==
2625 PPMD(D_PCI
, ("%s: turned on link for "
2626 "%s@%s\n", str
, PM_NAME(dip
),
2629 PPMD(D_PCI
, ("%s: couldn't turn on link "
2630 "for %s@%s\n", str
, PM_NAME(dip
),
2639 if (ret
== DDI_SUCCESS
)
2641 mutex_exit(&domp
->lock
);
2646 * Decrements the domain pwr_cnt. if conditions to power down the domain
2647 * are met, powers down the domain,.
2650 ppm_power_down_domain(dev_info_t
*dip
)
2652 int ret
= DDI_SUCCESS
;
2653 char *str
= "ppm_power_down_domain";
2656 domp
= ppm_lookup_dev(dip
);
2658 mutex_enter(&domp
->lock
);
2659 ASSERT(domp
->pwr_cnt
> 0);
2661 switch (domp
->model
) {
2663 if ((domp
->pwr_cnt
== 0) &&
2664 (ppm_cpr_window_flag
== B_FALSE
) &&
2665 ppm_none_else_holds_power(domp
)) {
2666 if ((ret
= ppm_fetset(domp
, PPMD_OFF
)) ==
2668 PPMD(D_FET
, ("%s: turned off FET for %s@%s \n",
2669 str
, PM_NAME(dip
), PM_ADDR(dip
)))
2671 PPMD(D_FET
, ("%s: couldn't turn off FET for "
2672 " %s@%s\n", str
, PM_NAME(dip
),
2680 if ((domp
->pwr_cnt
== 0) &&
2681 (ppm_cpr_window_flag
== B_FALSE
) &&
2682 ppm_none_else_holds_power(domp
)) {
2683 if ((ret
= ppm_switch_clock(domp
, PPMD_OFF
)) ==
2685 PPMD(D_PCI
, ("%s: turned off clock for %s@%s\n",
2686 str
, PM_NAME(dip
), PM_ADDR(dip
)))
2688 PPMD(D_PCI
, ("%s: couldn't turn off clock "
2689 "for %s@%s\n", str
, PM_NAME(dip
),
2696 if ((domp
->pwr_cnt
== 0) &&
2697 (ppm_cpr_window_flag
== B_FALSE
) &&
2698 ppm_none_else_holds_power(domp
)) {
2699 if ((ret
= ppm_pcie_pwr(domp
, PPMD_OFF
)) ==
2701 PPMD(D_PCI
, ("%s: turned off link for %s@%s\n",
2702 str
, PM_NAME(dip
), PM_ADDR(dip
)))
2704 PPMD(D_PCI
, ("%s: couldn't turn off link "
2705 "for %s@%s\n", str
, PM_NAME(dip
),
2714 mutex_exit(&domp
->lock
);
2719 ppm_manage_sx(s3a_t
*s3ap
, int enter
)
2721 ppm_domain_t
*domp
= ppm_lookup_domain("domain_estar");
2726 PPMD(D_CPR
, ("ppm_manage_sx: can't find estar domain\n"))
2729 PPMD(D_CPR
, ("ppm_manage_sx %x, enter %d\n", s3ap
->s3a_state
,
2731 switch (s3ap
->s3a_state
) {
2734 dc
= ppm_lookup_dc(domp
, PPMDC_ENTER_S3
);
2736 dc
= ppm_lookup_dc(domp
, PPMDC_EXIT_S3
);
2738 ASSERT(dc
&& dc
->method
== PPMDC_KIO
);
2740 ("ppm_manage_sx: calling acpi driver (handle %p)"
2741 " with %x\n", (void *)dc
->lh
, dc
->m_un
.kio
.iowr
))
2742 ret
= ldi_ioctl(dc
->lh
, dc
->m_un
.kio
.iowr
,
2743 (intptr_t)s3ap
, FWRITE
| FKIOCTL
, kcred
, NULL
);
2747 /* S4 is not supported yet */
2756 * Search enable/disable lists, which are encoded in ppm.conf as an array
2760 ppm_search_list(pm_searchargs_t
*sl
)
2763 int flags
= DDI_PROP_DONTPASS
;
2764 ppm_unit_t
*unitp
= ddi_get_soft_state(ppm_statep
, ppm_inst
);
2768 char *manuf
= sl
->pms_manufacturer
;
2769 char *prod
= sl
->pms_product
;
2771 if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY
, unitp
->dip
, flags
,
2772 sl
->pms_listname
, &pp
, &nelements
) != DDI_PROP_SUCCESS
) {
2773 PPMD(D_CPR
, ("ppm_search_list prop lookup %s failed--EINVAL\n",
2777 ASSERT((nelements
& 1) == 0); /* must be even */
2779 PPMD(D_CPR
, ("ppm_search_list looking for %s, %s\n", manuf
, prod
))
2781 for (i
= 0; i
< nelements
; i
+= 2) {
2782 PPMD(D_CPR
, ("checking %s, %s", pp
[i
], pp
[i
+1]))
2783 /* we support only a trailing '*' pattern match */
2784 if ((starp
= strchr(pp
[i
], '*')) != NULL
&& *(starp
+ 1) == 0) {
2785 /* LINTED - ptrdiff overflow */
2786 if (strncmp(manuf
, pp
[i
], (starp
- pp
[i
])) != 0) {
2787 PPMD(D_CPR
, (" no match %s with %s\n",
2792 if ((starp
= strchr(pp
[i
+ 1], '*')) != NULL
&&
2793 *(starp
+ 1) == 0) {
2795 /* LINTED - ptrdiff overflow */
2796 pp
[i
+ 1], (starp
- pp
[i
+ 1])) != 0) {
2797 PPMD(D_CPR
, (" no match %s with %s\n",
2802 if (strcmp(manuf
, pp
[i
]) == 0 &&
2803 (strcmp(prod
, pp
[i
+ 1]) == 0)) {
2804 PPMD(D_CPR
, (" match\n"))
2808 PPMD(D_CPR
, (" no match %s with %s or %s with %s\n",
2809 manuf
, pp
[i
], prod
, pp
[i
+ 1]))