15cff7825Smh27603 /*
25cff7825Smh27603 * CDDL HEADER START
35cff7825Smh27603 *
45cff7825Smh27603 * The contents of this file are subject to the terms of the
55cff7825Smh27603 * Common Development and Distribution License (the "License").
65cff7825Smh27603 * You may not use this file except in compliance with the License.
75cff7825Smh27603 *
85cff7825Smh27603 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95cff7825Smh27603 * or http://www.opensolaris.org/os/licensing.
105cff7825Smh27603 * See the License for the specific language governing permissions
115cff7825Smh27603 * and limitations under the License.
125cff7825Smh27603 *
135cff7825Smh27603 * When distributing Covered Code, include this CDDL HEADER in each
145cff7825Smh27603 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155cff7825Smh27603 * If applicable, add the following below this CDDL HEADER, with the
165cff7825Smh27603 * fields enclosed by brackets "[]" replaced with your own identifying
175cff7825Smh27603 * information: Portions Copyright [yyyy] [name of copyright owner]
185cff7825Smh27603 *
195cff7825Smh27603 * CDDL HEADER END
205cff7825Smh27603 */
215cff7825Smh27603 /*
22d67944fbSScott Rotondo * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
235cff7825Smh27603 * Use is subject to license terms.
245cff7825Smh27603 */
25444f66e7SMark Haywood /*
26444f66e7SMark Haywood * Copyright (c) 2009, Intel Corporation.
27444f66e7SMark Haywood * All Rights Reserved.
28444f66e7SMark Haywood */
295cff7825Smh27603
305cff7825Smh27603
315cff7825Smh27603 /*
325cff7825Smh27603 * Platform Power Management master pseudo driver -
335cff7825Smh27603 * - attaches only when ppm.conf file is present, indicating a
345cff7825Smh27603 * workstation (since Excalibur era ) that is designed to
355cff7825Smh27603 * be MOU-3 EPA compliant and which uses platform-specific
365cff7825Smh27603 * hardware to do so;
375cff7825Smh27603 * - this pseudo driver uses a set of simple satellite
385cff7825Smh27603 * device drivers responsible for accessing platform
395cff7825Smh27603 * specific devices to modify the registers they own.
405cff7825Smh27603 * ppm drivers tells these satellite drivers what to do
415cff7825Smh27603 * according to using command values taken from ppm.conf.
425cff7825Smh27603 */
435cff7825Smh27603 #include <sys/conf.h>
445cff7825Smh27603 #include <sys/stat.h>
455cff7825Smh27603 #include <sys/file.h>
465cff7825Smh27603 #include <sys/types.h>
475cff7825Smh27603 #include <sys/param.h>
485cff7825Smh27603 #include <sys/open.h>
495cff7825Smh27603 #include <sys/callb.h>
505cff7825Smh27603 #include <sys/va_list.h>
515cff7825Smh27603 #include <sys/errno.h>
525cff7825Smh27603 #include <sys/modctl.h>
535cff7825Smh27603 #include <sys/sysmacros.h>
545cff7825Smh27603 #include <sys/ddi_impldefs.h>
555cff7825Smh27603 #include <sys/promif.h>
565cff7825Smh27603 #include <sys/epm.h>
575cff7825Smh27603 #include <sys/sunpm.h>
585cff7825Smh27603 #include <sys/ppmio.h>
595cff7825Smh27603 #include <sys/sunldi.h>
605cff7825Smh27603 #include <sys/ppmvar.h>
615cff7825Smh27603 #include <sys/ddi.h>
625cff7825Smh27603 #include <sys/sunddi.h>
635cff7825Smh27603 #include <sys/ppm_plat.h>
645cff7825Smh27603
655cff7825Smh27603 /*
665cff7825Smh27603 * Note: When pm_power() is called (directly or indirectly) to change the
675cff7825Smh27603 * power level of a device and the call returns failure, DO NOT assume the
685cff7825Smh27603 * level is unchanged. Doublecheck it against ppmd->level.
695cff7825Smh27603 */
705cff7825Smh27603
715cff7825Smh27603 /*
725cff7825Smh27603 * cb_ops
735cff7825Smh27603 */
745cff7825Smh27603 static int ppm_open(dev_t *, int, int, cred_t *);
755cff7825Smh27603 static int ppm_close(dev_t, int, int, cred_t *);
765cff7825Smh27603 static int ppm_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
775cff7825Smh27603
785cff7825Smh27603 static struct cb_ops ppm_cb_ops = {
795cff7825Smh27603 ppm_open, /* open */
805cff7825Smh27603 ppm_close, /* close */
815cff7825Smh27603 nodev, /* strategy */
825cff7825Smh27603 nodev, /* print */
835cff7825Smh27603 nodev, /* dump */
845cff7825Smh27603 nodev, /* read */
855cff7825Smh27603 nodev, /* write */
865cff7825Smh27603 ppm_ioctl, /* ioctl */
875cff7825Smh27603 nodev, /* devmap */
885cff7825Smh27603 nodev, /* mmap */
895cff7825Smh27603 nodev, /* segmap */
905cff7825Smh27603 nochpoll, /* poll */
915cff7825Smh27603 ddi_prop_op, /* prop_op */
925cff7825Smh27603 NULL, /* streamtab */
935cff7825Smh27603 D_MP | D_NEW, /* driver compatibility flag */
945cff7825Smh27603 CB_REV, /* cb_ops revision */
955cff7825Smh27603 nodev, /* async read */
965cff7825Smh27603 nodev /* async write */
975cff7825Smh27603 };
985cff7825Smh27603
995cff7825Smh27603 /*
1005cff7825Smh27603 * bus_ops
1015cff7825Smh27603 */
1025cff7825Smh27603 static int ppm_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *,
1035cff7825Smh27603 void *);
1045cff7825Smh27603
1055cff7825Smh27603 static struct bus_ops ppm_bus_ops = {
1065cff7825Smh27603 BUSO_REV, /* busops_rev */
1075cff7825Smh27603 0, /* bus_map */
1085cff7825Smh27603 0, /* bus_get_intrspec */
1095cff7825Smh27603 0, /* bus_add_intrspec */
1105cff7825Smh27603 0, /* bus_remove_intrspec */
1115cff7825Smh27603 0, /* bus_map_fault */
1125cff7825Smh27603 ddi_no_dma_map, /* bus_dma_map */
1135cff7825Smh27603 ddi_no_dma_allochdl, /* bus_dma_allochdl */
1145cff7825Smh27603 NULL, /* bus_dma_freehdl */
1155cff7825Smh27603 NULL, /* bus_dma_bindhdl */
1165cff7825Smh27603 NULL, /* bus_dma_unbindhdl */
1175cff7825Smh27603 NULL, /* bus_dma_flush */
1185cff7825Smh27603 NULL, /* bus_dma_win */
1195cff7825Smh27603 NULL, /* bus_dma_ctl */
1205cff7825Smh27603 ppm_ctlops, /* bus_ctl */
1215cff7825Smh27603 0, /* bus_prop_op */
1225cff7825Smh27603 0, /* bus_get_eventcookie */
1235cff7825Smh27603 0, /* bus_add_eventcall */
1245cff7825Smh27603 0, /* bus_remove_eventcall */
1255cff7825Smh27603 0, /* bus_post_event */
1265cff7825Smh27603 0 /* bus_intr_ctl */
1275cff7825Smh27603 };
1285cff7825Smh27603
1295cff7825Smh27603 /*
1305cff7825Smh27603 * dev_ops
1315cff7825Smh27603 */
1325cff7825Smh27603 static int ppm_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
1335cff7825Smh27603 static int ppm_attach(dev_info_t *, ddi_attach_cmd_t);
1345cff7825Smh27603 static int ppm_detach(dev_info_t *, ddi_detach_cmd_t);
1355cff7825Smh27603
1365cff7825Smh27603 static struct dev_ops ppm_ops = {
1375cff7825Smh27603 DEVO_REV, /* devo_rev */
1385cff7825Smh27603 0, /* refcnt */
1395cff7825Smh27603 ppm_getinfo, /* info */
1405cff7825Smh27603 nulldev, /* identify */
1415cff7825Smh27603 nulldev, /* probe */
1425cff7825Smh27603 ppm_attach, /* attach */
1435cff7825Smh27603 ppm_detach, /* detach */
1445cff7825Smh27603 nodev, /* reset */
1455cff7825Smh27603 &ppm_cb_ops, /* cb_ops */
1465cff7825Smh27603 &ppm_bus_ops, /* bus_ops */
14719397407SSherry Moore nulldev, /* power */
14819397407SSherry Moore ddi_quiesce_not_needed, /* quiesce */
1495cff7825Smh27603 };
1505cff7825Smh27603
1515cff7825Smh27603 extern struct mod_ops mod_driverops;
1525cff7825Smh27603
1535cff7825Smh27603 static struct modldrv modldrv = {
1545cff7825Smh27603 &mod_driverops,
15519397407SSherry Moore "platform pm driver",
1565cff7825Smh27603 &ppm_ops
1575cff7825Smh27603 };
1585cff7825Smh27603
1595cff7825Smh27603 static struct modlinkage modlinkage = {
1605cff7825Smh27603 MODREV_1,
1615cff7825Smh27603 &modldrv,
1625cff7825Smh27603 NULL
1635cff7825Smh27603 };
1645cff7825Smh27603
1655cff7825Smh27603 /*
1665cff7825Smh27603 * Global data structure and variables
1675cff7825Smh27603 */
1685cff7825Smh27603 int ppm_inst = -1;
1695cff7825Smh27603 void *ppm_statep;
1705cff7825Smh27603 ppm_domain_t *ppm_domain_p;
1715cff7825Smh27603 callb_id_t *ppm_cprcb_id;
1725cff7825Smh27603 static kmutex_t ppm_cpr_window_lock; /* guard ppm_cpr_window_flag */
1735cff7825Smh27603 static boolean_t ppm_cpr_window_flag; /* set indicating chpt-resume period */
1745cff7825Smh27603
1755cff7825Smh27603 /* LED actions */
1765cff7825Smh27603 #define PPM_LED_SOLIDON 0
1775cff7825Smh27603 #define PPM_LED_BLINKING 1
1785cff7825Smh27603
1795cff7825Smh27603 /*
1805cff7825Smh27603 * Debug
1815cff7825Smh27603 */
1825cff7825Smh27603 #ifdef DEBUG
1835cff7825Smh27603 uint_t ppm_debug = 0;
1845cff7825Smh27603 #endif
1855cff7825Smh27603
1865cff7825Smh27603 /*
1875cff7825Smh27603 * Local function prototypes and data
1885cff7825Smh27603 */
1895cff7825Smh27603 static boolean_t ppm_cpr_callb(void *, int);
1905cff7825Smh27603 static int ppm_fetset(ppm_domain_t *, uint8_t);
1915cff7825Smh27603 static int ppm_fetget(ppm_domain_t *, uint8_t *);
1925cff7825Smh27603 static int ppm_gpioset(ppm_domain_t *, int);
1935cff7825Smh27603 static int ppm_manage_cpus(dev_info_t *, power_req_t *, int *);
1945cff7825Smh27603 static int ppm_manage_pci(dev_info_t *, power_req_t *, int *);
1955cff7825Smh27603 static int ppm_manage_pcie(dev_info_t *, power_req_t *, int *);
1965cff7825Smh27603 static int ppm_manage_fet(dev_info_t *, power_req_t *, int *);
1975cff7825Smh27603 static void ppm_manage_led(int);
1985cff7825Smh27603 static void ppm_set_led(ppm_domain_t *, int);
1995cff7825Smh27603 static void ppm_blink_led(void *);
2005cff7825Smh27603 static void ppm_svc_resume_ctlop(dev_info_t *, power_req_t *);
2015cff7825Smh27603 static int ppm_set_level(ppm_dev_t *, int, int, boolean_t);
2025cff7825Smh27603 static int ppm_change_power_level(ppm_dev_t *, int, int);
2035cff7825Smh27603 static int ppm_record_level_change(ppm_dev_t *, int, int);
2045cff7825Smh27603 static int ppm_switch_clock(ppm_domain_t *, int);
2055cff7825Smh27603 static int ppm_pcie_pwr(ppm_domain_t *, int);
2065cff7825Smh27603 static int ppm_power_up_domain(dev_info_t *dip);
2075cff7825Smh27603 static int ppm_power_down_domain(dev_info_t *dip);
2085cff7825Smh27603
2095cff7825Smh27603 int
_init(void)2105cff7825Smh27603 _init(void)
2115cff7825Smh27603 {
2125cff7825Smh27603 if (ddi_soft_state_init(
2132df1fe9cSrandyf &ppm_statep, sizeof (ppm_unit_t), 1) != DDI_SUCCESS) {
2142df1fe9cSrandyf PPMD(D_INIT, ("ppm: soft state init\n"))
2155cff7825Smh27603 return (DDI_FAILURE);
2162df1fe9cSrandyf }
2175cff7825Smh27603
2185cff7825Smh27603 if (mod_install(&modlinkage) != DDI_SUCCESS) {
2195cff7825Smh27603 ddi_soft_state_fini(&ppm_statep);
2205cff7825Smh27603 return (DDI_FAILURE);
2215cff7825Smh27603 }
2225cff7825Smh27603 return (DDI_SUCCESS);
2235cff7825Smh27603 }
2245cff7825Smh27603
2255cff7825Smh27603
2265cff7825Smh27603 int
_fini(void)2275cff7825Smh27603 _fini(void)
2285cff7825Smh27603 {
2292df1fe9cSrandyf int error;
2302df1fe9cSrandyf
2312df1fe9cSrandyf if ((error = mod_remove(&modlinkage)) == DDI_SUCCESS)
2322df1fe9cSrandyf ddi_soft_state_fini(&ppm_statep);
2332df1fe9cSrandyf
2342df1fe9cSrandyf return (error);
2355cff7825Smh27603 }
2365cff7825Smh27603
2375cff7825Smh27603
2385cff7825Smh27603 int
_info(struct modinfo * modinfop)2395cff7825Smh27603 _info(struct modinfo *modinfop)
2405cff7825Smh27603 {
2415cff7825Smh27603 return (mod_info(&modlinkage, modinfop));
2425cff7825Smh27603 }
2435cff7825Smh27603
2445cff7825Smh27603
2455cff7825Smh27603 /* ARGSUSED */
2465cff7825Smh27603 int
ppm_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** resultp)2475cff7825Smh27603 ppm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
2485cff7825Smh27603 {
2495cff7825Smh27603 struct ppm_unit *unitp;
2505cff7825Smh27603 dev_t dev;
2515cff7825Smh27603 int instance;
2525cff7825Smh27603 int rval;
2535cff7825Smh27603
2545cff7825Smh27603 if (ppm_inst == -1)
2555cff7825Smh27603 return (DDI_FAILURE);
2565cff7825Smh27603
2575cff7825Smh27603 switch (cmd) {
2585cff7825Smh27603 case DDI_INFO_DEVT2DEVINFO:
2595cff7825Smh27603 if (unitp = ddi_get_soft_state(ppm_statep, (dev_t)arg)) {
2605cff7825Smh27603 *resultp = unitp->dip;
2615cff7825Smh27603 rval = DDI_SUCCESS;
2625cff7825Smh27603 } else
2635cff7825Smh27603 rval = DDI_FAILURE;
2645cff7825Smh27603
2655cff7825Smh27603 return (rval);
2665cff7825Smh27603
2675cff7825Smh27603 case DDI_INFO_DEVT2INSTANCE:
2685cff7825Smh27603 dev = (dev_t)arg;
2695cff7825Smh27603 instance = getminor(dev);
2705cff7825Smh27603 *resultp = (void *)(uintptr_t)instance;
2715cff7825Smh27603 return (DDI_SUCCESS);
2725cff7825Smh27603
2735cff7825Smh27603 default:
2745cff7825Smh27603 return (DDI_FAILURE);
2755cff7825Smh27603 }
2765cff7825Smh27603 }
2775cff7825Smh27603
2785cff7825Smh27603
2795cff7825Smh27603 /*
2805cff7825Smh27603 * attach(9E)
2815cff7825Smh27603 */
2825cff7825Smh27603 static int
ppm_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)2835cff7825Smh27603 ppm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2845cff7825Smh27603 {
2855cff7825Smh27603 ppm_unit_t *unitp;
2865cff7825Smh27603 int ret;
2875cff7825Smh27603 #ifdef DEBUG
2885cff7825Smh27603 char *str = "ppm_attach";
2895cff7825Smh27603 #endif
2905cff7825Smh27603
2915cff7825Smh27603
2925cff7825Smh27603 switch (cmd) {
2935cff7825Smh27603 case DDI_ATTACH:
2945cff7825Smh27603 PPMD(D_ATTACH, ("%s: attaching ...\n", str))
2955cff7825Smh27603 break;
2965cff7825Smh27603
2975cff7825Smh27603 case DDI_RESUME:
2985cff7825Smh27603 PPMD(D_ATTACH, ("%s: Resuming ...\n", str))
2995cff7825Smh27603 unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
3005cff7825Smh27603 mutex_enter(&unitp->lock);
3015cff7825Smh27603 unitp->states &= ~PPM_STATE_SUSPENDED;
3025cff7825Smh27603 mutex_exit(&unitp->lock);
3035cff7825Smh27603 return (DDI_SUCCESS);
3045cff7825Smh27603
3055cff7825Smh27603 default:
3065cff7825Smh27603 cmn_err(CE_WARN, "ppm_attach: unknown command %d, dip(0x%p)",
3075cff7825Smh27603 cmd, (void *)dip);
3085cff7825Smh27603 return (DDI_FAILURE);
3095cff7825Smh27603 }
3105cff7825Smh27603
3115cff7825Smh27603 if (ppm_inst != -1) {
3125cff7825Smh27603 PPMD(D_ATTACH, ("%s: Already attached !", str))
3135cff7825Smh27603 return (DDI_FAILURE);
3145cff7825Smh27603 }
3155cff7825Smh27603
3165cff7825Smh27603 ppm_inst = ddi_get_instance(dip);
3175cff7825Smh27603 if (ddi_soft_state_zalloc(ppm_statep, ppm_inst) != DDI_SUCCESS) {
3185cff7825Smh27603 PPMD(D_ATTACH, ("%s: soft states alloc error!\n", str))
3195cff7825Smh27603 return (DDI_FAILURE);
3205cff7825Smh27603 }
3215cff7825Smh27603 unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
3225cff7825Smh27603
3235cff7825Smh27603 ret = ddi_create_minor_node(dip, "ppm", S_IFCHR, ppm_inst,
3245cff7825Smh27603 "ddi_ppm", 0);
3255cff7825Smh27603 if (ret != DDI_SUCCESS) {
3265cff7825Smh27603 PPMD(D_ATTACH, ("%s: can't create minor node!\n", str))
3275cff7825Smh27603 goto fail1;
3285cff7825Smh27603 }
3295cff7825Smh27603
3305cff7825Smh27603 unitp->dip = dip;
3315cff7825Smh27603 mutex_init(&unitp->lock, NULL, MUTEX_DRIVER, NULL);
3325cff7825Smh27603
3335cff7825Smh27603 /*
3345cff7825Smh27603 * read ppm.conf, construct ppm_domain data structure and
3355cff7825Smh27603 * their sub data structure.
3365cff7825Smh27603 */
3375cff7825Smh27603 if ((ret = ppm_create_db(dip)) != DDI_SUCCESS)
3385cff7825Smh27603 goto fail2;
3395cff7825Smh27603
3405cff7825Smh27603 /*
3415cff7825Smh27603 * walk down ppm domain control from each domain, initialize
3425cff7825Smh27603 * domain control orthogonal function call handle
3435cff7825Smh27603 */
3445cff7825Smh27603 ppm_init_cb(dip);
3455cff7825Smh27603
3465cff7825Smh27603 if ((ret = pm_register_ppm(ppm_claim_dev, dip)) != DDI_SUCCESS) {
3475cff7825Smh27603 cmn_err(CE_WARN, "ppm_attach: can't register ppm handler!");
3485cff7825Smh27603 goto fail2;
3495cff7825Smh27603 }
3505cff7825Smh27603
3515cff7825Smh27603 mutex_init(&ppm_cpr_window_lock, NULL, MUTEX_DRIVER, NULL);
3525cff7825Smh27603 ppm_cpr_window_flag = B_FALSE;
3535cff7825Smh27603 ppm_cprcb_id = callb_add(ppm_cpr_callb, (void *)NULL,
3545cff7825Smh27603 CB_CL_CPR_PM, "ppm_cpr");
3555cff7825Smh27603
3565cff7825Smh27603 #if defined(__x86)
3575cff7825Smh27603 /*
3585cff7825Smh27603 * Register callback so that once CPUs have been added to
359444f66e7SMark Haywood * the device tree, ppm CPU domains can be allocated using ACPI
3605cff7825Smh27603 * data.
3615cff7825Smh27603 */
362444f66e7SMark Haywood cpupm_ppm_alloc_pstate_domains = ppm_alloc_pstate_domains;
363444f66e7SMark Haywood cpupm_ppm_free_pstate_domains = ppm_free_pstate_domains;
3645cff7825Smh27603
3655cff7825Smh27603 /*
3665cff7825Smh27603 * Register callback so that whenever max speed throttle requests
3675cff7825Smh27603 * are received, ppm can redefine the high power level for
3685cff7825Smh27603 * all CPUs in the domain.
3695cff7825Smh27603 */
3705cff7825Smh27603 cpupm_redefine_topspeed = ppm_redefine_topspeed;
3715cff7825Smh27603 #endif
3725cff7825Smh27603
3735cff7825Smh27603 ddi_report_dev(dip);
3745cff7825Smh27603 return (DDI_SUCCESS);
3755cff7825Smh27603
3765cff7825Smh27603 fail2:
3775cff7825Smh27603 ddi_remove_minor_node(dip, "ddi_ppm");
3785cff7825Smh27603 mutex_destroy(&unitp->lock);
3795cff7825Smh27603 fail1:
3805cff7825Smh27603 ddi_soft_state_free(ppm_statep, ppm_inst);
3815cff7825Smh27603 ppm_inst = -1;
3825cff7825Smh27603 return (DDI_FAILURE);
3835cff7825Smh27603 }
3845cff7825Smh27603
3855cff7825Smh27603
3865cff7825Smh27603 /* ARGSUSED */
3875cff7825Smh27603 static int
ppm_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)3885cff7825Smh27603 ppm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
3895cff7825Smh27603 {
3905cff7825Smh27603 ppm_unit_t *unitp;
3915cff7825Smh27603 #ifdef DEBUG
3925cff7825Smh27603 char *str = "ppm_detach";
3935cff7825Smh27603 #endif
3945cff7825Smh27603
3955cff7825Smh27603 switch (cmd) {
3965cff7825Smh27603 case DDI_DETACH:
3975cff7825Smh27603 PPMD(D_DETACH, ("%s: detach not allowed.\n", str))
3985cff7825Smh27603 return (DDI_FAILURE);
3995cff7825Smh27603
4005cff7825Smh27603 case DDI_SUSPEND:
4015cff7825Smh27603 PPMD(D_DETACH, ("%s: suspending ...\n", str))
4025cff7825Smh27603 unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
4035cff7825Smh27603 mutex_enter(&unitp->lock);
4045cff7825Smh27603 unitp->states |= PPM_STATE_SUSPENDED;
4055cff7825Smh27603 mutex_exit(&unitp->lock);
4065cff7825Smh27603
4075cff7825Smh27603 /*
4085cff7825Smh27603 * Suspend requires that timeout callouts to be canceled.
4095cff7825Smh27603 * Turning off the LED blinking will cancel the timeout.
4105cff7825Smh27603 */
4115cff7825Smh27603 ppm_manage_led(PPM_LED_SOLIDON);
4125cff7825Smh27603 return (DDI_SUCCESS);
4135cff7825Smh27603
4145cff7825Smh27603 default:
4155cff7825Smh27603 cmn_err(CE_WARN, "ppm_detach: unsupported command %d, dip(%p)",
4165cff7825Smh27603 cmd, (void *)dip);
4175cff7825Smh27603 return (DDI_FAILURE);
4185cff7825Smh27603 }
4195cff7825Smh27603 }
4205cff7825Smh27603
4215cff7825Smh27603
4225cff7825Smh27603 /* ARGSUSED */
4235cff7825Smh27603 int
ppm_open(dev_t * devp,int flag,int otyp,cred_t * cred_p)4245cff7825Smh27603 ppm_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
4255cff7825Smh27603 {
4265cff7825Smh27603 if (otyp != OTYP_CHR)
4275cff7825Smh27603 return (EINVAL);
4285cff7825Smh27603 PPMD(D_OPEN, ("ppm_open: devp 0x%p, flag 0x%x, otyp %d\n",
4295cff7825Smh27603 (void *)devp, flag, otyp))
4305cff7825Smh27603 return (0);
4315cff7825Smh27603 }
4325cff7825Smh27603
4335cff7825Smh27603
4345cff7825Smh27603 /* ARGSUSED */
4355cff7825Smh27603 int
ppm_close(dev_t dev,int flag,int otyp,cred_t * credp)4365cff7825Smh27603 ppm_close(dev_t dev, int flag, int otyp, cred_t *credp)
4375cff7825Smh27603 {
4385cff7825Smh27603 PPMD(D_CLOSE, ("ppm_close: dev 0x%lx, flag 0x%x, otyp %d\n",
4395cff7825Smh27603 dev, flag, otyp))
4405cff7825Smh27603 return (0);
4415cff7825Smh27603 }
4425cff7825Smh27603
4435cff7825Smh27603
4445cff7825Smh27603 /* ARGSUSED */
4455cff7825Smh27603 int
ppm_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * cred_p,int * rval_p)4465cff7825Smh27603 ppm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred_p,
4475cff7825Smh27603 int *rval_p)
4485cff7825Smh27603 {
4495cff7825Smh27603 #ifdef DEBUG
4505cff7825Smh27603 char *str = "ppm_ioctl";
4515cff7825Smh27603 #endif
4525cff7825Smh27603 ppm_domain_t *domp = NULL;
4535cff7825Smh27603 uint8_t level, lvl;
4545cff7825Smh27603 int ret = 0;
4555cff7825Smh27603
4565cff7825Smh27603 PPMD(D_IOCTL, ("%s: dev 0x%lx, cmd 0x%x, mode 0x%x\n",
4575cff7825Smh27603 str, dev, cmd, mode))
4585cff7825Smh27603
4595cff7825Smh27603 switch (cmd) {
4605cff7825Smh27603 case PPMGET_DPWR:
4615cff7825Smh27603 {
4625cff7825Smh27603 STRUCT_DECL(ppm_dpwr, dpwr);
4635cff7825Smh27603 struct ppm_unit *unitp;
4645cff7825Smh27603 char *domain;
4655cff7825Smh27603
4665cff7825Smh27603 STRUCT_INIT(dpwr, mode);
4675cff7825Smh27603 ret = ddi_copyin((caddr_t)arg, STRUCT_BUF(dpwr),
4685cff7825Smh27603 STRUCT_SIZE(dpwr), mode);
4695cff7825Smh27603 if (ret != 0)
4705cff7825Smh27603 return (EFAULT);
4715cff7825Smh27603
4725cff7825Smh27603 /* copyin domain name */
4735cff7825Smh27603 domain = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
4745cff7825Smh27603 ret = copyinstr(
4755cff7825Smh27603 STRUCT_FGETP(dpwr, domain), domain, MAXNAMELEN, NULL);
4765cff7825Smh27603 if (ret != 0) {
4775cff7825Smh27603 PPMD(D_IOCTL, ("%s: can't copyin domain, line(%d)\n",
4785cff7825Smh27603 str, __LINE__))
4795cff7825Smh27603 ret = EFAULT;
4805cff7825Smh27603 goto err_dpwr;
4815cff7825Smh27603 }
4825cff7825Smh27603
4835cff7825Smh27603 /* locate domain */
4845cff7825Smh27603 if ((domp = ppm_lookup_domain(domain)) == NULL) {
4855cff7825Smh27603 PPMD(D_IOCTL, ("%s: no such domain %s\n", str, domain))
4865cff7825Smh27603 ret = ENODEV;
4875cff7825Smh27603 goto err_dpwr;
4885cff7825Smh27603 }
4895cff7825Smh27603
4905cff7825Smh27603 switch (domp->model) {
4915cff7825Smh27603 case PPMD_FET: /* report power fet ON or OFF */
4925cff7825Smh27603 if ((ret = ppm_fetget(domp, &lvl)) != 0) {
4935cff7825Smh27603 ret = EIO;
4945cff7825Smh27603 goto err_dpwr;
4955cff7825Smh27603 }
4965cff7825Smh27603 level = (lvl == PPMD_ON) ?
4975cff7825Smh27603 PPMIO_POWER_ON : PPMIO_POWER_OFF;
4985cff7825Smh27603 break;
4995cff7825Smh27603
5005cff7825Smh27603 case PPMD_PCI: /* report pci slot clock ON or OFF */
5015cff7825Smh27603 case PPMD_PCI_PROP:
5025cff7825Smh27603 case PPMD_PCIE:
5035cff7825Smh27603 level = (domp->status == PPMD_ON) ?
5045cff7825Smh27603 PPMIO_POWER_ON : PPMIO_POWER_OFF;
5055cff7825Smh27603 break;
5065cff7825Smh27603
5075cff7825Smh27603 case PPMD_LED: /* report LED blinking or solid on */
5085cff7825Smh27603
5095cff7825Smh27603 unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
5105cff7825Smh27603 if (unitp->led_tid == 0)
5115cff7825Smh27603 level = PPMIO_LED_SOLIDON;
5125cff7825Smh27603 else
5135cff7825Smh27603 level = PPMIO_LED_BLINKING;
5145cff7825Smh27603 break;
5155cff7825Smh27603
5165cff7825Smh27603 case PPMD_CPU: /* report cpu speed divisor */
5175cff7825Smh27603 level = domp->devlist->level;
5185cff7825Smh27603 break;
5195cff7825Smh27603
5205cff7825Smh27603 default:
5215cff7825Smh27603 ret = EINVAL;
5225cff7825Smh27603 goto err_dpwr;
5235cff7825Smh27603 }
5245cff7825Smh27603
5255cff7825Smh27603 STRUCT_FSET(dpwr, level, level);
5265cff7825Smh27603 ret = ddi_copyout(STRUCT_BUF(dpwr), (caddr_t)arg,
5275cff7825Smh27603 STRUCT_SIZE(dpwr), mode);
5285cff7825Smh27603 if (ret != 0) {
5295cff7825Smh27603 PPMD(D_IOCTL, ("%s: can't copyout, line(%d)\n",
5305cff7825Smh27603 str, __LINE__))
5315cff7825Smh27603 ret = EFAULT;
5325cff7825Smh27603 }
5335cff7825Smh27603 err_dpwr:
5345cff7825Smh27603 kmem_free(domain, MAXNAMELEN);
5355cff7825Smh27603
5365cff7825Smh27603 break;
5375cff7825Smh27603 }
5385cff7825Smh27603
5395cff7825Smh27603 case PPMGET_DOMBYDEV:
5405cff7825Smh27603 {
5415cff7825Smh27603 STRUCT_DECL(ppm_bydev, bydev);
5425cff7825Smh27603 char *path = NULL;
5435cff7825Smh27603 size_t size, l;
5445cff7825Smh27603
5455cff7825Smh27603 STRUCT_INIT(bydev, mode);
5465cff7825Smh27603 ret = ddi_copyin((caddr_t)arg, STRUCT_BUF(bydev),
5475cff7825Smh27603 STRUCT_SIZE(bydev), mode);
5485cff7825Smh27603 if (ret != 0)
5495cff7825Smh27603 return (EFAULT);
5505cff7825Smh27603
5515cff7825Smh27603 /* copyin .path */
5525cff7825Smh27603 path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
5535cff7825Smh27603 ret = copyinstr(
5545cff7825Smh27603 STRUCT_FGETP(bydev, path), path, MAXPATHLEN, NULL);
5555cff7825Smh27603 if (ret != 0) {
5565cff7825Smh27603 PPMD(D_IOCTL, ("%s: can't copyin path, line(%d)\n",
5575cff7825Smh27603 str, __LINE__))
5585cff7825Smh27603 kmem_free(path, MAXPATHLEN);
5595cff7825Smh27603 return (EFAULT);
5605cff7825Smh27603 }
5615cff7825Smh27603
5625cff7825Smh27603 /* so far we have up to one domain for a given device */
5635cff7825Smh27603 size = STRUCT_FGET(bydev, size);
5645cff7825Smh27603 domp = ppm_get_domain_by_dev(path);
5655cff7825Smh27603 kmem_free(path, MAXPATHLEN);
5665cff7825Smh27603 if (domp != NULL) {
5675cff7825Smh27603 l = strlen(domp->name) + 1;
5685cff7825Smh27603 if (l > size) {
5695cff7825Smh27603 PPMD(D_IOCTL, ("%s: buffer too small\n", str))
5705cff7825Smh27603 return ((size == 0) ? EINVAL : EFAULT);
5715cff7825Smh27603 }
5725cff7825Smh27603 } else /* no domain found to be associated with given device */
5735cff7825Smh27603 return (ENODEV);
5745cff7825Smh27603
5755cff7825Smh27603 ret = copyoutstr(
5765cff7825Smh27603 domp->name, STRUCT_FGETP(bydev, domlist), l, &l);
5775cff7825Smh27603 if (ret != 0) {
5785cff7825Smh27603 PPMD(D_IOCTL, ("%s: can't copyout domlist, line(%d)"
5795cff7825Smh27603 " \n", str, __LINE__))
5805cff7825Smh27603 return (EFAULT);
5815cff7825Smh27603 }
5825cff7825Smh27603
5835cff7825Smh27603 break;
5845cff7825Smh27603 }
5855cff7825Smh27603
5865cff7825Smh27603
5875cff7825Smh27603 case PPMGET_DEVBYDOM:
5885cff7825Smh27603 {
5895cff7825Smh27603 STRUCT_DECL(ppm_bydom, bydom);
5905cff7825Smh27603 char *domain = NULL;
5915cff7825Smh27603 char *devlist = NULL;
5925cff7825Smh27603 ppm_dev_t *ppmd;
5935cff7825Smh27603 dev_info_t *odip = NULL;
5945cff7825Smh27603 char *s, *d;
5955cff7825Smh27603 size_t size, l;
5965cff7825Smh27603
5975cff7825Smh27603 STRUCT_INIT(bydom, mode);
5985cff7825Smh27603 ret = ddi_copyin((caddr_t)arg, STRUCT_BUF(bydom),
5995cff7825Smh27603 STRUCT_SIZE(bydom), mode);
6005cff7825Smh27603 if (ret != 0)
6015cff7825Smh27603 return (EFAULT);
6025cff7825Smh27603
6035cff7825Smh27603 /* copyin .domain */
6045cff7825Smh27603 domain = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
6055cff7825Smh27603 ret = copyinstr(STRUCT_FGETP(bydom, domain), domain,
6065cff7825Smh27603 MAXNAMELEN, NULL);
6075cff7825Smh27603 if (ret != 0) {
6085cff7825Smh27603 PPMD(D_IOCTL, ("%s: can't copyin domain, line(%d)\n",
6095cff7825Smh27603 str, __LINE__))
6105cff7825Smh27603 ret = EFAULT;
6115cff7825Smh27603 goto err_bydom;
6125cff7825Smh27603 }
6135cff7825Smh27603
6145cff7825Smh27603 /* locate domain */
6155cff7825Smh27603 if ((domp = ppm_lookup_domain(domain)) == NULL) {
6165cff7825Smh27603 ret = ENODEV;
6175cff7825Smh27603 goto err_bydom;
6185cff7825Smh27603 }
6195cff7825Smh27603
6205cff7825Smh27603 l = 0;
6215cff7825Smh27603 if ((size = STRUCT_FGET(bydom, size)) == 0)
6225cff7825Smh27603 ret = EINVAL;
6235cff7825Smh27603 else
6245cff7825Smh27603 if ((d = devlist = kmem_zalloc(size, KM_SLEEP)) == NULL)
6255cff7825Smh27603 ret = EFAULT;
6265cff7825Smh27603 if (ret != 0)
6275cff7825Smh27603 goto err_bydom;
6285cff7825Smh27603
6295cff7825Smh27603 for (ppmd = domp->devlist; ppmd;
6305cff7825Smh27603 odip = ppmd->dip, ppmd = ppmd->next) {
6315cff7825Smh27603
6325cff7825Smh27603 if (ppmd->dip == odip)
6335cff7825Smh27603 continue;
6345cff7825Smh27603 if (ppmd != domp->devlist)
6355cff7825Smh27603 *d++ = ' ';
6365cff7825Smh27603
6375cff7825Smh27603 l += strlen(ppmd->path) + 1;
6385cff7825Smh27603 if (l > size) {
6395cff7825Smh27603 PPMD(D_IOCTL, ("%s: buffer overflow\n", str))
6405cff7825Smh27603 ret = EFAULT;
6415cff7825Smh27603 goto err_bydom;
6425cff7825Smh27603 }
6435cff7825Smh27603
6445cff7825Smh27603 for (s = ppmd->path; *s != 0; )
6455cff7825Smh27603 *d++ = *s++;
6465cff7825Smh27603 }
6475cff7825Smh27603 *d = 0;
6485cff7825Smh27603
6495cff7825Smh27603 if (*devlist == 0)
6505cff7825Smh27603 goto err_bydom;
6515cff7825Smh27603
6525cff7825Smh27603 ret = copyoutstr(
6535cff7825Smh27603 devlist, STRUCT_FGETP(bydom, devlist), l, &l);
6545cff7825Smh27603 if (ret != 0) {
6555cff7825Smh27603 PPMD(D_IOCTL, ("%s: can't copyout devlist, line(%d)"
6565cff7825Smh27603 " \n", str, __LINE__))
6575cff7825Smh27603 ret = EFAULT;
6585cff7825Smh27603 }
6595cff7825Smh27603
6605cff7825Smh27603 err_bydom:
6615cff7825Smh27603 if (devlist)
6625cff7825Smh27603 kmem_free(devlist, size);
6635cff7825Smh27603 if (domain)
6645cff7825Smh27603 kmem_free(domain, MAXNAMELEN);
6655cff7825Smh27603
6665cff7825Smh27603 break;
6675cff7825Smh27603 }
6685cff7825Smh27603
6695cff7825Smh27603 #if defined(__x86)
6705cff7825Smh27603 /*
6715cff7825Smh27603 * Note that these two ioctls exist for test purposes only.
6725cff7825Smh27603 * Unfortunately, there really isn't any other good way of
6735cff7825Smh27603 * unit testing the dynamic redefinition of the top speed as it
6745cff7825Smh27603 * usually occurs due to environmental conditions.
6755cff7825Smh27603 */
6765cff7825Smh27603 case PPMGET_NORMAL:
6775cff7825Smh27603 case PPMSET_NORMAL:
6785cff7825Smh27603 {
6795cff7825Smh27603 STRUCT_DECL(ppm_norm, norm);
6805cff7825Smh27603 char *path = NULL;
6815cff7825Smh27603 struct pm_component *dcomps;
6825cff7825Smh27603 struct pm_comp *pm_comp;
6835cff7825Smh27603 ppm_dev_t *ppmd;
6845cff7825Smh27603 int i;
6855cff7825Smh27603
6865cff7825Smh27603 STRUCT_INIT(norm, mode);
6875cff7825Smh27603 ret = ddi_copyin((caddr_t)arg, STRUCT_BUF(norm),
6885cff7825Smh27603 STRUCT_SIZE(norm), mode);
6895cff7825Smh27603 if (ret != 0)
6905cff7825Smh27603 return (EFAULT);
6915cff7825Smh27603
6925cff7825Smh27603 /* copyin .path */
6935cff7825Smh27603 path = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
6945cff7825Smh27603 ret = copyinstr(
6955cff7825Smh27603 STRUCT_FGETP(norm, path), path, MAXPATHLEN, NULL);
6965cff7825Smh27603 if (ret != 0) {
6975cff7825Smh27603 PPMD(D_IOCTL, ("%s: can't copyin path, line(%d)\n",
6985cff7825Smh27603 str, __LINE__))
6995cff7825Smh27603 kmem_free(path, MAXPATHLEN);
7005cff7825Smh27603 return (EFAULT);
7015cff7825Smh27603 }
7025cff7825Smh27603
7035cff7825Smh27603 domp = ppm_get_domain_by_dev(path);
7045cff7825Smh27603 kmem_free(path, MAXPATHLEN);
7055cff7825Smh27603
7065cff7825Smh27603 if (domp == NULL)
7075cff7825Smh27603 return (ENODEV);
7085cff7825Smh27603
7095cff7825Smh27603 ppmd = domp->devlist;
7105cff7825Smh27603 if (cmd == PPMSET_NORMAL) {
7115cff7825Smh27603 if (domp->model != PPMD_CPU)
7125cff7825Smh27603 return (EINVAL);
7135cff7825Smh27603 level = STRUCT_FGET(norm, norm);
7145cff7825Smh27603 dcomps = DEVI(ppmd->dip)->devi_pm_components;
7155cff7825Smh27603 pm_comp = &dcomps[ppmd->cmpt].pmc_comp;
7165cff7825Smh27603 for (i = pm_comp->pmc_numlevels; i > 0; i--) {
7175cff7825Smh27603 if (pm_comp->pmc_lvals[i-1] == level)
7185cff7825Smh27603 break;
7195cff7825Smh27603 }
7205cff7825Smh27603 if (i == 0)
7215cff7825Smh27603 return (EINVAL);
7225cff7825Smh27603
7235cff7825Smh27603 ppm_set_topspeed(ppmd, pm_comp->pmc_numlevels - i);
7245cff7825Smh27603 }
7255cff7825Smh27603
7265cff7825Smh27603 level = pm_get_normal_power(ppmd->dip, 0);
7275cff7825Smh27603
7285cff7825Smh27603 STRUCT_FSET(norm, norm, level);
7295cff7825Smh27603 ret = ddi_copyout(STRUCT_BUF(norm), (caddr_t)arg,
7305cff7825Smh27603 STRUCT_SIZE(norm), mode);
7315cff7825Smh27603 if (ret != 0) {
7325cff7825Smh27603 PPMD(D_IOCTL, ("%s: can't copyout, line(%d)\n",
7335cff7825Smh27603 str, __LINE__))
7345cff7825Smh27603 ret = EFAULT;
7355cff7825Smh27603 }
7365cff7825Smh27603 break;
7375cff7825Smh27603 }
7385cff7825Smh27603 #endif
7395cff7825Smh27603 default:
7405cff7825Smh27603 PPMD(D_IOCTL, ("%s: unsupported ioctl command(%d)\n", str, cmd))
7415cff7825Smh27603 return (EINVAL);
7425cff7825Smh27603 }
7435cff7825Smh27603
7445cff7825Smh27603 return (ret);
7455cff7825Smh27603 }
7465cff7825Smh27603
7475cff7825Smh27603
748d67944fbSScott Rotondo static int ppm_manage_sx(s3a_t *, int);
749d67944fbSScott Rotondo static int ppm_search_list(pm_searchargs_t *);
750d67944fbSScott Rotondo
7515cff7825Smh27603 /*
7525cff7825Smh27603 * interface between pm framework and ppm driver
7535cff7825Smh27603 */
7545cff7825Smh27603 /* ARGSUSED */
7555cff7825Smh27603 static int
ppm_ctlops(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t ctlop,void * arg,void * result)7565cff7825Smh27603 ppm_ctlops(dev_info_t *dip, dev_info_t *rdip,
7575cff7825Smh27603 ddi_ctl_enum_t ctlop, void *arg, void *result)
7585cff7825Smh27603 {
7595cff7825Smh27603 power_req_t *reqp = (power_req_t *)arg;
7605cff7825Smh27603 ppm_unit_t *unitp;
7615cff7825Smh27603 ppm_domain_t *domp;
7625cff7825Smh27603 ppm_dev_t *ppmd;
7635cff7825Smh27603 char path[MAXNAMELEN];
7645cff7825Smh27603 ppm_owned_t *owned;
7655cff7825Smh27603 int mode;
7665cff7825Smh27603 int ret = DDI_SUCCESS;
7672df1fe9cSrandyf int *res = (int *)result;
7682df1fe9cSrandyf s3a_t s3args;
7695cff7825Smh27603
770*584b574aSToomas Soome domp = NULL;
7715cff7825Smh27603 #ifdef DEBUG
7725cff7825Smh27603 char *str = "ppm_ctlops";
7735cff7825Smh27603 int mask = ppm_debug & (D_CTLOPS1 | D_CTLOPS2);
7745cff7825Smh27603 char *ctlstr = ppm_get_ctlstr(reqp->request_type, mask);
7755cff7825Smh27603 if (mask && ctlstr)
7765cff7825Smh27603 PPMD(mask, ("%s: %s, %s\n",
7775cff7825Smh27603 str, ddi_binding_name(rdip), ctlstr))
7785cff7825Smh27603 #endif
7795cff7825Smh27603
7802df1fe9cSrandyf if (ctlop != DDI_CTLOPS_POWER) {
7815cff7825Smh27603 return (DDI_FAILURE);
7822df1fe9cSrandyf }
7835cff7825Smh27603
7845cff7825Smh27603 unitp = (ppm_unit_t *)ddi_get_soft_state(ppm_statep, ppm_inst);
7855cff7825Smh27603
7865cff7825Smh27603 switch (reqp->request_type) {
7875cff7825Smh27603
7885cff7825Smh27603 /* attempt to blink led if indeed all at lowest */
7895cff7825Smh27603 case PMR_PPM_ALL_LOWEST:
7905cff7825Smh27603 mode = (reqp->req.ppm_all_lowest_req.mode == PM_ALL_LOWEST);
7915cff7825Smh27603 if (!(unitp->states & PPM_STATE_SUSPENDED) && mode)
7925cff7825Smh27603 ppm_manage_led(PPM_LED_BLINKING);
7935cff7825Smh27603 else
7945cff7825Smh27603 ppm_manage_led(PPM_LED_SOLIDON);
7955cff7825Smh27603 return (DDI_SUCCESS);
7965cff7825Smh27603
7975cff7825Smh27603 /* undo the claiming of 'rdip' at attach time */
7985cff7825Smh27603 case PMR_PPM_POST_DETACH:
7995cff7825Smh27603 ASSERT(reqp->req.ppm_set_power_req.who == rdip);
8005cff7825Smh27603 mutex_enter(&unitp->lock);
8015cff7825Smh27603 if (reqp->req.ppm_config_req.result != DDI_SUCCESS ||
8025cff7825Smh27603 (PPM_GET_PRIVATE(rdip) == NULL)) {
8035cff7825Smh27603 mutex_exit(&unitp->lock);
8045cff7825Smh27603 return (DDI_FAILURE);
8055cff7825Smh27603 }
8065cff7825Smh27603 mutex_exit(&unitp->lock);
8075cff7825Smh27603 ppm_rem_dev(rdip);
8085cff7825Smh27603 return (DDI_SUCCESS);
8095cff7825Smh27603
8105cff7825Smh27603 /* chance to adjust pwr_cnt if resume is about to power up rdip */
8115cff7825Smh27603 case PMR_PPM_PRE_RESUME:
8125cff7825Smh27603 ppm_svc_resume_ctlop(rdip, reqp);
8135cff7825Smh27603 return (DDI_SUCCESS);
8145cff7825Smh27603
8155cff7825Smh27603 /*
8165cff7825Smh27603 * synchronizing, so that only the owner of the power lock is
8175cff7825Smh27603 * permitted to change device and component's power level.
8185cff7825Smh27603 */
8195cff7825Smh27603 case PMR_PPM_UNLOCK_POWER:
8205cff7825Smh27603 case PMR_PPM_TRY_LOCK_POWER:
8215cff7825Smh27603 case PMR_PPM_LOCK_POWER:
8225cff7825Smh27603 ppmd = PPM_GET_PRIVATE(rdip);
8235cff7825Smh27603 if (ppmd)
8245cff7825Smh27603 domp = ppmd->domp;
8255cff7825Smh27603 else if (reqp->request_type != PMR_PPM_UNLOCK_POWER) {
8265cff7825Smh27603 domp = ppm_lookup_dev(rdip);
8275cff7825Smh27603 ASSERT(domp);
8285cff7825Smh27603 ppmd = ppm_get_dev(rdip, domp);
8295cff7825Smh27603 }
8305cff7825Smh27603
831*584b574aSToomas Soome if (domp == NULL)
832*584b574aSToomas Soome return (DDI_FAILURE);
833*584b574aSToomas Soome
8345cff7825Smh27603 PPMD(D_LOCKS, ("ppm_lock_%s: %s, %s\n",
8355cff7825Smh27603 (domp->dflags & PPMD_LOCK_ALL) ? "all" : "one",
8365cff7825Smh27603 ppmd->path, ppm_get_ctlstr(reqp->request_type, D_LOCKS)))
8375cff7825Smh27603
8385cff7825Smh27603 if (domp->dflags & PPMD_LOCK_ALL)
8395cff7825Smh27603 ppm_lock_all(domp, reqp, result);
8405cff7825Smh27603 else
8415cff7825Smh27603 ppm_lock_one(ppmd, reqp, result);
8425cff7825Smh27603 return (DDI_SUCCESS);
8435cff7825Smh27603
8445cff7825Smh27603 case PMR_PPM_POWER_LOCK_OWNER:
8455cff7825Smh27603 ASSERT(reqp->req.ppm_power_lock_owner_req.who == rdip);
8465cff7825Smh27603 ppmd = PPM_GET_PRIVATE(rdip);
847*584b574aSToomas Soome if (ppmd) {
8485cff7825Smh27603 domp = ppmd->domp;
849*584b574aSToomas Soome } else {
8505cff7825Smh27603 domp = ppm_lookup_dev(rdip);
8515cff7825Smh27603 ASSERT(domp);
8525cff7825Smh27603 ppmd = ppm_get_dev(rdip, domp);
8535cff7825Smh27603 }
8545cff7825Smh27603
855*584b574aSToomas Soome if (domp == NULL)
856*584b574aSToomas Soome return (DDI_FAILURE);
857*584b574aSToomas Soome
8585cff7825Smh27603 /*
8595cff7825Smh27603 * In case of LOCK_ALL, effective owner of the power lock
8605cff7825Smh27603 * is the owner of the domain lock. otherwise, it is the owner
8615cff7825Smh27603 * of the power lock.
8625cff7825Smh27603 */
8635cff7825Smh27603 if (domp->dflags & PPMD_LOCK_ALL)
8645cff7825Smh27603 reqp->req.ppm_power_lock_owner_req.owner =
8655cff7825Smh27603 mutex_owner(&domp->lock);
8665cff7825Smh27603 else {
8675cff7825Smh27603 reqp->req.ppm_power_lock_owner_req.owner =
8685cff7825Smh27603 DEVI(rdip)->devi_busy_thread;
8695cff7825Smh27603 }
8705cff7825Smh27603 return (DDI_SUCCESS);
8715cff7825Smh27603
8725cff7825Smh27603 case PMR_PPM_INIT_CHILD:
8735cff7825Smh27603 ASSERT(reqp->req.ppm_lock_power_req.who == rdip);
8745cff7825Smh27603 if ((domp = ppm_lookup_dev(rdip)) == NULL)
8755cff7825Smh27603 return (DDI_SUCCESS);
8765cff7825Smh27603
8775cff7825Smh27603 /*
8785cff7825Smh27603 * We keep track of power-manageable devices starting with
8795cff7825Smh27603 * initialization process. The initializing flag remains
8805cff7825Smh27603 * set until it is cleared by ppm_add_dev(). Power management
8815cff7825Smh27603 * policy for some domains are affected even during device
8825cff7825Smh27603 * initialization. For example, PCI domains should leave
8835cff7825Smh27603 * their clock running meanwhile a device in that domain
8845cff7825Smh27603 * is initializing.
8855cff7825Smh27603 */
8865cff7825Smh27603 mutex_enter(&domp->lock);
8875cff7825Smh27603 owned = ppm_add_owned(rdip, domp);
8885cff7825Smh27603 ASSERT(owned->initializing == 0);
8895cff7825Smh27603 owned->initializing = 1;
8905cff7825Smh27603
8915cff7825Smh27603 if (PPMD_IS_PCI(domp->model) && domp->status == PPMD_OFF) {
8925cff7825Smh27603 ret = ppm_switch_clock(domp, PPMD_ON);
8935cff7825Smh27603 if (ret == DDI_SUCCESS)
8945cff7825Smh27603 domp->dflags |= PPMD_INITCHILD_CLKON;
8955cff7825Smh27603 }
8965cff7825Smh27603 mutex_exit(&domp->lock);
8975cff7825Smh27603 return (ret);
8985cff7825Smh27603
8995cff7825Smh27603 case PMR_PPM_POST_ATTACH:
9005cff7825Smh27603 ASSERT(reqp->req.ppm_config_req.who == rdip);
9015cff7825Smh27603 domp = ppm_lookup_dev(rdip);
9025cff7825Smh27603 ASSERT(domp);
9035cff7825Smh27603 ASSERT(domp->status == PPMD_ON);
9045cff7825Smh27603 if (reqp->req.ppm_config_req.result == DDI_SUCCESS) {
9055cff7825Smh27603 /*
9065cff7825Smh27603 * call ppm_get_dev, which will increment the
9075cff7825Smh27603 * domain power count by the right number.
9085cff7825Smh27603 * Undo the power count increment, done in PRE_PROBE.
9095cff7825Smh27603 */
9105cff7825Smh27603 if (PM_GET_PM_INFO(rdip))
9115cff7825Smh27603 ppmd = ppm_get_dev(rdip, domp);
9125cff7825Smh27603 mutex_enter(&domp->lock);
9135cff7825Smh27603 ASSERT(domp->pwr_cnt > 0);
9145cff7825Smh27603 domp->pwr_cnt--;
9155cff7825Smh27603 mutex_exit(&domp->lock);
9165cff7825Smh27603 return (DDI_SUCCESS);
9175cff7825Smh27603 }
9185cff7825Smh27603
9195cff7825Smh27603 ret = ppm_power_down_domain(rdip);
9205cff7825Smh27603 /* FALLTHROUGH */
9215cff7825Smh27603 case PMR_PPM_UNINIT_CHILD:
9225cff7825Smh27603 ASSERT(reqp->req.ppm_lock_power_req.who == rdip);
9235cff7825Smh27603 if ((domp = ppm_lookup_dev(rdip)) == NULL)
9245cff7825Smh27603 return (DDI_SUCCESS);
9255cff7825Smh27603
9265cff7825Smh27603 (void) ddi_pathname(rdip, path);
9275cff7825Smh27603 mutex_enter(&domp->lock);
9285cff7825Smh27603 for (owned = domp->owned; owned; owned = owned->next)
9295cff7825Smh27603 if (strcmp(owned->path, path) == 0)
9305cff7825Smh27603 break;
9315cff7825Smh27603
9325cff7825Smh27603 /*
9335cff7825Smh27603 * In case we didn't go through a complete attach and detach,
9345cff7825Smh27603 * the initializing flag will still be set, so clear it.
9355cff7825Smh27603 */
9365cff7825Smh27603 if ((owned != NULL) && (owned->initializing))
9375cff7825Smh27603 owned->initializing = 0;
9385cff7825Smh27603
9395cff7825Smh27603 if (PPMD_IS_PCI(domp->model) &&
9405cff7825Smh27603 domp->status == PPMD_ON && domp->pwr_cnt == 0 &&
9415cff7825Smh27603 (domp->dflags & PPMD_INITCHILD_CLKON) &&
9425cff7825Smh27603 ppm_none_else_holds_power(domp)) {
9435cff7825Smh27603 ret = ppm_switch_clock(domp, PPMD_OFF);
9445cff7825Smh27603 if (ret == DDI_SUCCESS)
9455cff7825Smh27603 domp->dflags &= ~PPMD_INITCHILD_CLKON;
9465cff7825Smh27603 }
9475cff7825Smh27603 mutex_exit(&domp->lock);
9485cff7825Smh27603 return (ret);
9495cff7825Smh27603
9505cff7825Smh27603 /* place holders */
9515cff7825Smh27603 case PMR_PPM_UNMANAGE:
9525cff7825Smh27603 case PMR_PPM_PRE_DETACH:
9535cff7825Smh27603 return (DDI_SUCCESS);
9545cff7825Smh27603
9555cff7825Smh27603 case PMR_PPM_PRE_PROBE:
9565cff7825Smh27603 ASSERT(reqp->req.ppm_config_req.who == rdip);
9575cff7825Smh27603 return (ppm_power_up_domain(rdip));
9585cff7825Smh27603
9595cff7825Smh27603 case PMR_PPM_POST_PROBE:
9605cff7825Smh27603 ASSERT(reqp->req.ppm_config_req.who == rdip);
9615cff7825Smh27603 if (reqp->req.ppm_config_req.result == DDI_PROBE_SUCCESS ||
9625cff7825Smh27603 reqp->req.ppm_config_req.result == DDI_PROBE_DONTCARE)
9635cff7825Smh27603 return (DDI_SUCCESS);
9645cff7825Smh27603
9655cff7825Smh27603 /* Probe failed */
9665cff7825Smh27603 PPMD(D_CTLOPS1 | D_CTLOPS2, ("%s: probe failed for %s@%s "
9675cff7825Smh27603 "rv %d\n", str, PM_NAME(rdip), PM_ADDR(rdip),
9685cff7825Smh27603 reqp->req.ppm_config_req.result))
9695cff7825Smh27603 return (ppm_power_down_domain(rdip));
9705cff7825Smh27603
9715cff7825Smh27603 case PMR_PPM_PRE_ATTACH:
9725cff7825Smh27603 ASSERT(reqp->req.ppm_config_req.who == rdip);
9735cff7825Smh27603 /* Domain has already been powered up in PRE_PROBE */
9745cff7825Smh27603 domp = ppm_lookup_dev(rdip);
9755cff7825Smh27603 ASSERT(domp);
9765cff7825Smh27603 ASSERT(domp->status == PPMD_ON);
9775cff7825Smh27603 return (DDI_SUCCESS);
9785cff7825Smh27603
9795cff7825Smh27603 /* ppm intercepts power change process to the claimed devices */
9805cff7825Smh27603 case PMR_PPM_SET_POWER:
9815cff7825Smh27603 case PMR_PPM_POWER_CHANGE_NOTIFY:
9825cff7825Smh27603 if ((ppmd = PPM_GET_PRIVATE(rdip)) == NULL) {
9835cff7825Smh27603 domp = ppm_lookup_dev(rdip);
9845cff7825Smh27603 ASSERT(domp);
9855cff7825Smh27603 ppmd = ppm_get_dev(rdip, domp);
9865cff7825Smh27603 }
9875cff7825Smh27603 switch (ppmd->domp->model) {
9885cff7825Smh27603 case PPMD_CPU:
9895cff7825Smh27603 return (ppm_manage_cpus(rdip, reqp, result));
9905cff7825Smh27603 case PPMD_FET:
9915cff7825Smh27603 return (ppm_manage_fet(rdip, reqp, result));
9925cff7825Smh27603 case PPMD_PCI:
9935cff7825Smh27603 case PPMD_PCI_PROP:
9945cff7825Smh27603 return (ppm_manage_pci(rdip, reqp, result));
9955cff7825Smh27603 case PPMD_PCIE:
9965cff7825Smh27603 return (ppm_manage_pcie(rdip, reqp, result));
9975cff7825Smh27603 default:
9985cff7825Smh27603 cmn_err(CE_WARN, "ppm_ctlops: domain model %d does"
9995cff7825Smh27603 " not support PMR_PPM_SET_POWER ctlop",
10005cff7825Smh27603 ppmd->domp->model);
10015cff7825Smh27603 return (DDI_FAILURE);
10025cff7825Smh27603 }
10035cff7825Smh27603
10042df1fe9cSrandyf case PMR_PPM_ENTER_SX:
10052df1fe9cSrandyf case PMR_PPM_EXIT_SX:
10062df1fe9cSrandyf s3args.s3a_state = reqp->req.ppm_power_enter_sx_req.sx_state;
10072df1fe9cSrandyf s3args.s3a_test_point =
10082df1fe9cSrandyf reqp->req.ppm_power_enter_sx_req.test_point;
10092df1fe9cSrandyf s3args.s3a_wakephys = reqp->req.ppm_power_enter_sx_req.wakephys;
10102df1fe9cSrandyf s3args.s3a_psr = reqp->req.ppm_power_enter_sx_req.psr;
10112df1fe9cSrandyf ret = ppm_manage_sx(&s3args,
10122df1fe9cSrandyf reqp->request_type == PMR_PPM_ENTER_SX);
10132df1fe9cSrandyf if (ret) {
10142df1fe9cSrandyf PPMD(D_CPR, ("ppm_manage_sx returns %d\n", ret))
10152df1fe9cSrandyf return (DDI_FAILURE);
10162df1fe9cSrandyf } else {
10172df1fe9cSrandyf return (DDI_SUCCESS);
10182df1fe9cSrandyf }
10192df1fe9cSrandyf
10202df1fe9cSrandyf case PMR_PPM_SEARCH_LIST:
10212df1fe9cSrandyf ret = ppm_search_list(reqp->req.ppm_search_list_req.searchlist);
10222df1fe9cSrandyf reqp->req.ppm_search_list_req.result = ret;
10232df1fe9cSrandyf *res = ret;
10242df1fe9cSrandyf if (ret) {
10252df1fe9cSrandyf PPMD(D_CPR, ("ppm_search_list returns %d\n", ret))
10262df1fe9cSrandyf return (DDI_FAILURE);
10272df1fe9cSrandyf } else {
10282df1fe9cSrandyf PPMD(D_CPR, ("ppm_search_list returns %d\n", ret))
10292df1fe9cSrandyf return (DDI_SUCCESS);
10302df1fe9cSrandyf }
10312df1fe9cSrandyf
10325cff7825Smh27603 default:
10335cff7825Smh27603 cmn_err(CE_WARN, "ppm_ctlops: unrecognized ctlops req(%d)",
10345cff7825Smh27603 reqp->request_type);
10355cff7825Smh27603 return (DDI_FAILURE);
10365cff7825Smh27603 }
10375cff7825Smh27603 }
10385cff7825Smh27603
10395cff7825Smh27603
10405cff7825Smh27603 /*
10415cff7825Smh27603 * Raise the power level of a subrange of cpus. Used when cpu driver
10425cff7825Smh27603 * failed an attempt to lower the power of a cpu (probably because
10435cff7825Smh27603 * it got busy). Need to revert the ones we already changed.
10445cff7825Smh27603 *
10455cff7825Smh27603 * ecpup = the ppm_dev_t for the cpu which failed to lower power
10465cff7825Smh27603 * level = power level to reset prior cpus to
10475cff7825Smh27603 */
10485cff7825Smh27603 int
ppm_revert_cpu_power(ppm_dev_t * ecpup,int level)10495cff7825Smh27603 ppm_revert_cpu_power(ppm_dev_t *ecpup, int level)
10505cff7825Smh27603 {
10515cff7825Smh27603 ppm_dev_t *cpup;
10525cff7825Smh27603 int ret = DDI_SUCCESS;
10535cff7825Smh27603
10545cff7825Smh27603 for (cpup = ecpup->domp->devlist; cpup != ecpup; cpup = cpup->next) {
10555cff7825Smh27603 PPMD(D_CPU, ("ppm_revert_cpu_power: \"%s\", revert to "
10565cff7825Smh27603 "level %d\n", cpup->path, level))
10575cff7825Smh27603
10585cff7825Smh27603 ret = pm_power(cpup->dip, 0, level);
10595cff7825Smh27603 if (ret == DDI_SUCCESS) {
10605cff7825Smh27603 cpup->level = level;
10615cff7825Smh27603 cpup->rplvl = PM_LEVEL_UNKNOWN;
10625cff7825Smh27603 }
10635cff7825Smh27603 }
10645cff7825Smh27603 return (ret);
10655cff7825Smh27603 }
10665cff7825Smh27603
10675cff7825Smh27603
10685cff7825Smh27603 /*
10695cff7825Smh27603 * ppm_manage_cpus - Process a request to change the power level of a cpu.
10705cff7825Smh27603 * If not all cpus want to be at the same level, OR if we are currently
10715cff7825Smh27603 * refusing slowdown requests due to thermal stress, we cache the request.
10725cff7825Smh27603 * Otherwise, set all cpus to the new power level.
10735cff7825Smh27603 */
10745cff7825Smh27603 /* ARGSUSED */
10755cff7825Smh27603 static int
ppm_manage_cpus(dev_info_t * dip,power_req_t * reqp,int * result)10765cff7825Smh27603 ppm_manage_cpus(dev_info_t *dip, power_req_t *reqp, int *result)
10775cff7825Smh27603 {
10785cff7825Smh27603 #ifdef DEBUG
10795cff7825Smh27603 char *str = "ppm_manage_cpus";
10805cff7825Smh27603 #endif
10815cff7825Smh27603 int old, new, ret, kmflag;
10825cff7825Smh27603 ppm_dev_t *ppmd, *cpup;
10835cff7825Smh27603 int change_notify = 0;
10845cff7825Smh27603 pm_ppm_devlist_t *devlist = NULL, *p;
10855cff7825Smh27603 int do_rescan = 0;
10865cff7825Smh27603
10875cff7825Smh27603 *result = DDI_SUCCESS;
10885cff7825Smh27603
10895cff7825Smh27603 switch (reqp->request_type) {
10905cff7825Smh27603 case PMR_PPM_SET_POWER:
10915cff7825Smh27603 break;
10925cff7825Smh27603
10935cff7825Smh27603 case PMR_PPM_POWER_CHANGE_NOTIFY:
10945cff7825Smh27603 change_notify = 1;
10955cff7825Smh27603 break;
10965cff7825Smh27603
10975cff7825Smh27603 default:
10985cff7825Smh27603 return (DDI_FAILURE);
10995cff7825Smh27603 }
11005cff7825Smh27603
11015cff7825Smh27603 ppmd = PPM_GET_PRIVATE(dip);
11025cff7825Smh27603 ASSERT(MUTEX_HELD(&ppmd->domp->lock));
11035cff7825Smh27603 old = reqp->req.ppm_set_power_req.old_level;
11045cff7825Smh27603 new = reqp->req.ppm_set_power_req.new_level;
11055cff7825Smh27603
11065cff7825Smh27603 if (change_notify) {
11075cff7825Smh27603 ppmd->level = new;
11085cff7825Smh27603 ppmd->rplvl = PM_LEVEL_UNKNOWN;
11095cff7825Smh27603
11105cff7825Smh27603 PPMD(D_CPU, ("%s: Notify cpu dip %p power level has changed "
11115cff7825Smh27603 "from %d to %d", str, (void *)dip, old, new))
11125cff7825Smh27603 return (DDI_SUCCESS);
11135cff7825Smh27603 }
11145cff7825Smh27603
11155cff7825Smh27603 if (ppm_manage_early_cpus(dip, new, result))
11165cff7825Smh27603 return (*result);
11175cff7825Smh27603
11185cff7825Smh27603 if (new == ppmd->level) {
11195cff7825Smh27603 PPMD(D_CPU, ("%s: already at power level %d\n", str, new))
11205cff7825Smh27603 return (DDI_SUCCESS);
11215cff7825Smh27603 }
11225cff7825Smh27603
11235cff7825Smh27603 /*
11245cff7825Smh27603 * A request from lower to higher level transition is granted and
11255cff7825Smh27603 * made effective on all cpus. A request from higher to lower must
11265cff7825Smh27603 * be agreed upon by all cpus.
11275cff7825Smh27603 */
11285cff7825Smh27603 ppmd->rplvl = new;
11295cff7825Smh27603 for (cpup = ppmd->domp->devlist; cpup; cpup = cpup->next) {
11305cff7825Smh27603 if (cpup->rplvl == new)
11315cff7825Smh27603 continue;
11325cff7825Smh27603
11335cff7825Smh27603 if (new < old) {
11345cff7825Smh27603 PPMD(D_SOME, ("%s: not all cpus wants to be at new "
11355cff7825Smh27603 "level %d yet.\n", str, new))
11365cff7825Smh27603 return (DDI_SUCCESS);
11375cff7825Smh27603 }
11385cff7825Smh27603
11395cff7825Smh27603 /*
11405cff7825Smh27603 * If a single cpu requests power up, honor the request
11415cff7825Smh27603 * powering up all cpus.
11425cff7825Smh27603 */
11435cff7825Smh27603 if (new > old) {
11445cff7825Smh27603 PPMD(D_SOME, ("%s: powering up device(%s@%s, %p) "
11455cff7825Smh27603 "because of request from dip(%s@%s, %p), "
11465cff7825Smh27603 "need pm_rescan\n", str, PM_NAME(cpup->dip),
11475cff7825Smh27603 PM_ADDR(cpup->dip), (void *)cpup->dip,
11485cff7825Smh27603 PM_NAME(dip), PM_ADDR(dip), (void *)dip))
11495cff7825Smh27603 do_rescan++;
11505cff7825Smh27603 }
11515cff7825Smh27603 }
11525cff7825Smh27603
11535cff7825Smh27603 PPMD(D_SETLVL, ("%s: \"%s\" set power level old %d, new %d \n",
11545cff7825Smh27603 str, ppmd->path, ppmd->level, new))
11555cff7825Smh27603 ret = ppm_change_cpu_power(ppmd, new);
11565cff7825Smh27603 *result = ret;
11575cff7825Smh27603
11585cff7825Smh27603 if (ret == DDI_SUCCESS) {
11595cff7825Smh27603 if (reqp->req.ppm_set_power_req.canblock == PM_CANBLOCK_BLOCK)
11605cff7825Smh27603 kmflag = KM_SLEEP;
11615cff7825Smh27603 else
11625cff7825Smh27603 kmflag = KM_NOSLEEP;
11635cff7825Smh27603
11645cff7825Smh27603 for (cpup = ppmd->domp->devlist; cpup; cpup = cpup->next) {
11655cff7825Smh27603 if (cpup->dip == dip)
11665cff7825Smh27603 continue;
11675cff7825Smh27603
11685cff7825Smh27603 if ((p = kmem_zalloc(sizeof (pm_ppm_devlist_t),
11695cff7825Smh27603 kmflag)) == NULL) {
11705cff7825Smh27603 break;
11715cff7825Smh27603 }
11725cff7825Smh27603 p->ppd_who = cpup->dip;
11735cff7825Smh27603 p->ppd_cmpt = cpup->cmpt;
11745cff7825Smh27603 p->ppd_old_level = old;
11755cff7825Smh27603 p->ppd_new_level = new;
11765cff7825Smh27603 p->ppd_next = devlist;
11775cff7825Smh27603
11785cff7825Smh27603 PPMD(D_SETLVL, ("%s: devlist entry[\"%s\"] %d -> %d\n",
11795cff7825Smh27603 str, cpup->path, old, new))
11805cff7825Smh27603
11815cff7825Smh27603 devlist = p;
11825cff7825Smh27603 }
11835cff7825Smh27603 reqp->req.ppm_set_power_req.cookie = (void *) devlist;
11845cff7825Smh27603
11855cff7825Smh27603 if (do_rescan > 0) {
11865cff7825Smh27603 for (cpup = ppmd->domp->devlist; cpup;
11875cff7825Smh27603 cpup = cpup->next) {
11885cff7825Smh27603 if (cpup->dip == dip)
11895cff7825Smh27603 continue;
11905cff7825Smh27603 pm_rescan(cpup->dip);
11915cff7825Smh27603 }
11925cff7825Smh27603 }
11935cff7825Smh27603 }
11945cff7825Smh27603
11955cff7825Smh27603 return (ret);
11965cff7825Smh27603 }
11975cff7825Smh27603
11985cff7825Smh27603
11995cff7825Smh27603 /*
12005cff7825Smh27603 * ppm_svc_resume_ctlop - this is a small bookkeeping ppm does -
12015cff7825Smh27603 * increments its FET domain power count, in anticipation of that
12025cff7825Smh27603 * the indicated device(dip) would be powered up by its driver as
12035cff7825Smh27603 * a result of cpr resuming.
12045cff7825Smh27603 */
12055cff7825Smh27603 /* ARGSUSED */
12065cff7825Smh27603 static void
ppm_svc_resume_ctlop(dev_info_t * dip,power_req_t * reqp)12075cff7825Smh27603 ppm_svc_resume_ctlop(dev_info_t *dip, power_req_t *reqp)
12085cff7825Smh27603 {
12095cff7825Smh27603 ppm_domain_t *domp;
12105cff7825Smh27603 ppm_dev_t *ppmd;
12115cff7825Smh27603 int powered; /* power up count per dip */
12125cff7825Smh27603
12135cff7825Smh27603 ppmd = PPM_GET_PRIVATE(dip);
12145cff7825Smh27603 if (ppmd == NULL)
12155cff7825Smh27603 return;
12165cff7825Smh27603
12175cff7825Smh27603 /*
12185cff7825Smh27603 * Maintain correct powered count for domain which cares
12195cff7825Smh27603 */
12205cff7825Smh27603 powered = 0;
12215cff7825Smh27603 domp = ppmd->domp;
12225cff7825Smh27603 mutex_enter(&domp->lock);
12235cff7825Smh27603 if ((domp->model == PPMD_FET) || PPMD_IS_PCI(domp->model) ||
12245cff7825Smh27603 (domp->model == PPMD_PCIE)) {
12255cff7825Smh27603 for (ppmd = domp->devlist; ppmd; ppmd = ppmd->next) {
12265cff7825Smh27603 if (ppmd->dip == dip && ppmd->level)
12275cff7825Smh27603 powered++;
12285cff7825Smh27603 }
12295cff7825Smh27603
12305cff7825Smh27603 /*
12315cff7825Smh27603 * All fets and clocks are held on during suspend -
12325cff7825Smh27603 * resume window regardless their domain devices' power
12335cff7825Smh27603 * level.
12345cff7825Smh27603 */
12355cff7825Smh27603 ASSERT(domp->status == PPMD_ON);
12365cff7825Smh27603
12375cff7825Smh27603 /*
12385cff7825Smh27603 * The difference indicates the number of components
12395cff7825Smh27603 * being off prior to suspend operation, that is the
12405cff7825Smh27603 * amount needs to be compensated in order to sync up
12415cff7825Smh27603 * bookkeeping with reality, for PROM reset would have
12425cff7825Smh27603 * brought up all devices.
12435cff7825Smh27603 */
12445cff7825Smh27603 if (powered < PM_NUMCMPTS(dip))
12455cff7825Smh27603 domp->pwr_cnt += PM_NUMCMPTS(dip) - powered;
12465cff7825Smh27603 }
12475cff7825Smh27603 for (ppmd = domp->devlist; ppmd; ppmd = ppmd->next) {
12485cff7825Smh27603 if (ppmd->dip == dip)
12495cff7825Smh27603 ppmd->level = ppmd->rplvl = PM_LEVEL_UNKNOWN;
12505cff7825Smh27603 }
12515cff7825Smh27603 mutex_exit(&domp->lock);
12525cff7825Smh27603 }
12535cff7825Smh27603
12545cff7825Smh27603 #ifdef DEBUG
12555cff7825Smh27603 static int ppmbringup = 0;
12565cff7825Smh27603 #endif
12575cff7825Smh27603
12585cff7825Smh27603 int
ppm_bringup_domains()12595cff7825Smh27603 ppm_bringup_domains()
12605cff7825Smh27603 {
12615cff7825Smh27603 #ifdef DEBUG
12625cff7825Smh27603 char *str = "ppm_bringup_domains";
12635cff7825Smh27603 #endif
12645cff7825Smh27603 ppm_domain_t *domp;
12655cff7825Smh27603 int ret = DDI_SUCCESS;
12665cff7825Smh27603
12675cff7825Smh27603 PPMD(D_CPR, ("%s[%d]: enter\n", str, ++ppmbringup))
12685cff7825Smh27603 for (domp = ppm_domain_p; domp; domp = domp->next) {
12695cff7825Smh27603 if ((!PPMD_IS_PCI(domp->model) && (domp->model != PPMD_FET) &&
12705cff7825Smh27603 (domp->model != PPMD_PCIE)) || (domp->devlist == NULL))
12715cff7825Smh27603 continue;
12725cff7825Smh27603
12735cff7825Smh27603 mutex_enter(&domp->lock);
12745cff7825Smh27603 if (domp->status == PPMD_ON) {
12755cff7825Smh27603 mutex_exit(&domp->lock);
12765cff7825Smh27603 continue;
12775cff7825Smh27603 }
12785cff7825Smh27603 switch (domp->model) {
12795cff7825Smh27603 case PPMD_FET:
12805cff7825Smh27603 ret = ppm_fetset(domp, PPMD_ON);
12815cff7825Smh27603 break;
12825cff7825Smh27603 case PPMD_PCI:
12835cff7825Smh27603 case PPMD_PCI_PROP:
12845cff7825Smh27603 ret = ppm_switch_clock(domp, PPMD_ON);
12855cff7825Smh27603 break;
12865cff7825Smh27603 case PPMD_PCIE:
12875cff7825Smh27603 ret = ppm_pcie_pwr(domp, PPMD_ON);
12885cff7825Smh27603 break;
12895cff7825Smh27603 default:
12905cff7825Smh27603 break;
12915cff7825Smh27603 }
12925cff7825Smh27603 mutex_exit(&domp->lock);
12935cff7825Smh27603 }
12942df1fe9cSrandyf PPMD(D_CPR, ("%s[%d]: exit\n", str, ppmbringup))
12955cff7825Smh27603
12965cff7825Smh27603 return (ret);
12975cff7825Smh27603 }
12985cff7825Smh27603
12995cff7825Smh27603 #ifdef DEBUG
13005cff7825Smh27603 static int ppmsyncbp = 0;
13015cff7825Smh27603 #endif
13025cff7825Smh27603
13035cff7825Smh27603 int
ppm_sync_bookkeeping()13045cff7825Smh27603 ppm_sync_bookkeeping()
13055cff7825Smh27603 {
13065cff7825Smh27603 #ifdef DEBUG
13075cff7825Smh27603 char *str = "ppm_sync_bookkeeping";
13085cff7825Smh27603 #endif
13095cff7825Smh27603 ppm_domain_t *domp;
13105cff7825Smh27603 int ret = DDI_SUCCESS;
13115cff7825Smh27603
13125cff7825Smh27603 PPMD(D_CPR, ("%s[%d]: enter\n", str, ++ppmsyncbp))
13135cff7825Smh27603 for (domp = ppm_domain_p; domp; domp = domp->next) {
13145cff7825Smh27603 if ((!PPMD_IS_PCI(domp->model) && (domp->model != PPMD_FET) &&
13155cff7825Smh27603 (domp->model != PPMD_PCIE)) || (domp->devlist == NULL))
13165cff7825Smh27603 continue;
13175cff7825Smh27603
13185cff7825Smh27603 mutex_enter(&domp->lock);
13195cff7825Smh27603 if ((domp->pwr_cnt != 0) || !ppm_none_else_holds_power(domp)) {
13205cff7825Smh27603 mutex_exit(&domp->lock);
13215cff7825Smh27603 continue;
13225cff7825Smh27603 }
13232df1fe9cSrandyf
13242df1fe9cSrandyf /*
13252df1fe9cSrandyf * skip NULL .devlist slot, for some may host pci device
13262df1fe9cSrandyf * that can not tolerate clock off or not even participate
13272df1fe9cSrandyf * in PM.
13282df1fe9cSrandyf */
13292df1fe9cSrandyf if (domp->devlist == NULL)
13302df1fe9cSrandyf continue;
13312df1fe9cSrandyf
13325cff7825Smh27603 switch (domp->model) {
13335cff7825Smh27603 case PPMD_FET:
13345cff7825Smh27603 ret = ppm_fetset(domp, PPMD_OFF);
13355cff7825Smh27603 break;
13365cff7825Smh27603 case PPMD_PCI:
13375cff7825Smh27603 case PPMD_PCI_PROP:
13385cff7825Smh27603 ret = ppm_switch_clock(domp, PPMD_OFF);
13395cff7825Smh27603 break;
13405cff7825Smh27603 case PPMD_PCIE:
13415cff7825Smh27603 ret = ppm_pcie_pwr(domp, PPMD_OFF);
13425cff7825Smh27603 break;
13435cff7825Smh27603 default:
13445cff7825Smh27603 break;
13455cff7825Smh27603 }
13465cff7825Smh27603 mutex_exit(&domp->lock);
13475cff7825Smh27603 }
13482df1fe9cSrandyf PPMD(D_CPR, ("%s[%d]: exit\n", str, ppmsyncbp))
13495cff7825Smh27603
13505cff7825Smh27603 return (ret);
13515cff7825Smh27603 }
13525cff7825Smh27603
13535cff7825Smh27603
13545cff7825Smh27603
13555cff7825Smh27603 /*
13565cff7825Smh27603 * pre-suspend window;
13575cff7825Smh27603 *
13585cff7825Smh27603 * power up every FET and PCI clock that are off;
13595cff7825Smh27603 *
13605cff7825Smh27603 * set ppm_cpr_window global flag to indicate
13615cff7825Smh27603 * that even though all pm_scan requested power transitions
13625cff7825Smh27603 * will be honored as usual but that until we're out
13635cff7825Smh27603 * of this window, no FET or clock will be turned off
13645cff7825Smh27603 * for domains with pwr_cnt decremented down to 0.
13655cff7825Smh27603 * Such is to avoid accessing the orthogonal drivers that own
13665cff7825Smh27603 * the FET and clock registers that may not be resumed yet.
13675cff7825Smh27603 *
13685cff7825Smh27603 * at post-resume window, walk through each FET and PCI domains,
13695cff7825Smh27603 * bring pwr_cnt and domp->status to sense: if pwr-cnt == 0,
13705cff7825Smh27603 * and noinvol check okays, power down the FET or PCI. At last,
13715cff7825Smh27603 * clear the global flag ppm_cpr_window.
13725cff7825Smh27603 *
13735cff7825Smh27603 * ASSERT case 1, during cpr window, checks pwr_cnt against power
13745cff7825Smh27603 * transitions;
13755cff7825Smh27603 * ASSERT case 2, out of cpr window, checks four things:
13765cff7825Smh27603 * pwr_cnt <> power transition in/out of 0
13775cff7825Smh27603 * <> status <> record of noinvol device detached
13785cff7825Smh27603 *
13795cff7825Smh27603 */
13805cff7825Smh27603 /* ARGSUSED */
13815cff7825Smh27603 static boolean_t
ppm_cpr_callb(void * arg,int code)13825cff7825Smh27603 ppm_cpr_callb(void *arg, int code)
13835cff7825Smh27603 {
13845cff7825Smh27603 int ret;
13855cff7825Smh27603
13865cff7825Smh27603 switch (code) {
13875cff7825Smh27603 case CB_CODE_CPR_CHKPT:
13885cff7825Smh27603
13895cff7825Smh27603 /* pre-suspend: start of cpr window */
13905cff7825Smh27603 mutex_enter(&ppm_cpr_window_lock);
13915cff7825Smh27603 ASSERT(ppm_cpr_window_flag == B_FALSE);
13925cff7825Smh27603 ppm_cpr_window_flag = B_TRUE;
13935cff7825Smh27603 mutex_exit(&ppm_cpr_window_lock);
13945cff7825Smh27603
13955cff7825Smh27603 ret = ppm_bringup_domains();
13965cff7825Smh27603
13975cff7825Smh27603 break;
13985cff7825Smh27603
13995cff7825Smh27603 case CB_CODE_CPR_RESUME:
14005cff7825Smh27603
14015cff7825Smh27603 /* post-resume: end of cpr window */
14025cff7825Smh27603 ret = ppm_sync_bookkeeping();
14035cff7825Smh27603
14045cff7825Smh27603 mutex_enter(&ppm_cpr_window_lock);
14055cff7825Smh27603 ASSERT(ppm_cpr_window_flag == B_TRUE);
14065cff7825Smh27603 ppm_cpr_window_flag = B_FALSE;
14075cff7825Smh27603 mutex_exit(&ppm_cpr_window_lock);
14085cff7825Smh27603
14095cff7825Smh27603 break;
1410*584b574aSToomas Soome default:
1411*584b574aSToomas Soome ret = DDI_SUCCESS;
1412*584b574aSToomas Soome break;
14135cff7825Smh27603 }
14145cff7825Smh27603
14155cff7825Smh27603 return (ret == DDI_SUCCESS);
14165cff7825Smh27603 }
14175cff7825Smh27603
14185cff7825Smh27603
14195cff7825Smh27603 /*
14205cff7825Smh27603 * Initialize our private version of real power level
14215cff7825Smh27603 * as well as lowest and highest levels the device supports;
14225cff7825Smh27603 * relate to ppm_add_dev
14235cff7825Smh27603 */
14245cff7825Smh27603 void
ppm_dev_init(ppm_dev_t * ppmd)14255cff7825Smh27603 ppm_dev_init(ppm_dev_t *ppmd)
14265cff7825Smh27603 {
14275cff7825Smh27603 struct pm_component *dcomps;
14285cff7825Smh27603 struct pm_comp *pm_comp;
14295cff7825Smh27603 dev_info_t *dip;
14305cff7825Smh27603 int maxi, i;
14315cff7825Smh27603
14325cff7825Smh27603 ASSERT(MUTEX_HELD(&ppmd->domp->lock));
14335cff7825Smh27603 ppmd->level = PM_LEVEL_UNKNOWN;
14345cff7825Smh27603 ppmd->rplvl = PM_LEVEL_UNKNOWN;
14355cff7825Smh27603
14365cff7825Smh27603 /* increment pwr_cnt per component */
14375cff7825Smh27603 if ((ppmd->domp->model == PPMD_FET) ||
14385cff7825Smh27603 PPMD_IS_PCI(ppmd->domp->model) ||
14395cff7825Smh27603 (ppmd->domp->model == PPMD_PCIE))
14405cff7825Smh27603 ppmd->domp->pwr_cnt++;
14415cff7825Smh27603
14425cff7825Smh27603 dip = ppmd->dip;
14435cff7825Smh27603
14445cff7825Smh27603 /*
14455cff7825Smh27603 * ppm exists to handle power-manageable devices which require
14465cff7825Smh27603 * special handling on the current platform. However, a
14475cff7825Smh27603 * driver for such a device may choose not to support power
14485cff7825Smh27603 * management on a particular load/attach. In this case we
14495cff7825Smh27603 * we create a structure to represent a single-component device
14505cff7825Smh27603 * for which "level" = PM_LEVEL_UNKNOWN and "lowest" = 0
14515cff7825Smh27603 * are effectively constant.
14525cff7825Smh27603 */
14535cff7825Smh27603 if (PM_GET_PM_INFO(dip)) {
14545cff7825Smh27603 dcomps = DEVI(dip)->devi_pm_components;
14555cff7825Smh27603 pm_comp = &dcomps[ppmd->cmpt].pmc_comp;
14565cff7825Smh27603
14575cff7825Smh27603 ppmd->lowest = pm_comp->pmc_lvals[0];
14585cff7825Smh27603 ASSERT(ppmd->lowest >= 0);
14595cff7825Smh27603 maxi = pm_comp->pmc_numlevels - 1;
14605cff7825Smh27603 ppmd->highest = pm_comp->pmc_lvals[maxi];
14615cff7825Smh27603
14625cff7825Smh27603 /*
14635cff7825Smh27603 * If 66mhz PCI device on pci 66mhz bus supports D2 state
14645cff7825Smh27603 * (config reg PMC bit 10 set), ppm could turn off its bus
14655cff7825Smh27603 * clock once it is at D3hot.
14665cff7825Smh27603 */
14675cff7825Smh27603 if (ppmd->domp->dflags & PPMD_PCI66MHZ) {
14685cff7825Smh27603 for (i = 0; i < maxi; i++)
14695cff7825Smh27603 if (pm_comp->pmc_lvals[i] == PM_LEVEL_D2) {
14705cff7825Smh27603 ppmd->flags |= PPMDEV_PCI66_D2;
14715cff7825Smh27603 break;
14725cff7825Smh27603 }
14735cff7825Smh27603 }
14745cff7825Smh27603 }
14755cff7825Smh27603
14765cff7825Smh27603 /*
14775cff7825Smh27603 * If device is in PCI_PROP domain and has exported the
14785cff7825Smh27603 * property listed in ppm.conf, its clock will be turned
14795cff7825Smh27603 * off when all pm'able devices in that domain are at D3.
14805cff7825Smh27603 */
14815cff7825Smh27603 if ((ppmd->domp->model == PPMD_PCI_PROP) &&
14825cff7825Smh27603 (ppmd->domp->propname != NULL) &&
14835cff7825Smh27603 ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
14845cff7825Smh27603 ppmd->domp->propname))
14855cff7825Smh27603 ppmd->flags |= PPMDEV_PCI_PROP_CLKPM;
14865cff7825Smh27603 }
14875cff7825Smh27603
14885cff7825Smh27603
14895cff7825Smh27603 /*
14905cff7825Smh27603 * relate to ppm_rem_dev
14915cff7825Smh27603 */
14925cff7825Smh27603 void
ppm_dev_fini(ppm_dev_t * ppmd)14935cff7825Smh27603 ppm_dev_fini(ppm_dev_t *ppmd)
14945cff7825Smh27603 {
14955cff7825Smh27603 ASSERT(MUTEX_HELD(&ppmd->domp->lock));
14965cff7825Smh27603
14975cff7825Smh27603 /* decrement pwr_cnt per component */
14985cff7825Smh27603 if ((ppmd->domp->model == PPMD_FET) ||
14995cff7825Smh27603 PPMD_IS_PCI(ppmd->domp->model) ||
15005cff7825Smh27603 (ppmd->domp->model == PPMD_PCIE))
15015cff7825Smh27603 if (ppmd->level != ppmd->lowest)
15025cff7825Smh27603 ppmd->domp->pwr_cnt--;
15035cff7825Smh27603 }
15045cff7825Smh27603
15055cff7825Smh27603 /*
15065cff7825Smh27603 * Each power fet controls the power of one or more platform
15075cff7825Smh27603 * device(s) within their domain. Hence domain devices' power
15085cff7825Smh27603 * level change has been monitored, such that once all devices
15095cff7825Smh27603 * are powered off, the fet is turned off to save more power.
15105cff7825Smh27603 *
15115cff7825Smh27603 * To power on any domain device, the domain power fet
15125cff7825Smh27603 * needs to be turned on first. always one fet per domain.
15135cff7825Smh27603 */
15145cff7825Smh27603 static int
ppm_manage_fet(dev_info_t * dip,power_req_t * reqp,int * result)15155cff7825Smh27603 ppm_manage_fet(dev_info_t *dip, power_req_t *reqp, int *result)
15165cff7825Smh27603 {
15175cff7825Smh27603 #ifdef DEBUG
15185cff7825Smh27603 char *str = "ppm_manage_fet";
15195cff7825Smh27603 #endif
15205cff7825Smh27603 int (*pwr_func)(ppm_dev_t *, int, int);
15215cff7825Smh27603 int new, old, cmpt;
15225cff7825Smh27603 ppm_dev_t *ppmd;
15235cff7825Smh27603 ppm_domain_t *domp;
15245cff7825Smh27603 int incr = 0;
15255cff7825Smh27603 int dummy_ret;
15265cff7825Smh27603
15275cff7825Smh27603
15285cff7825Smh27603 *result = DDI_SUCCESS;
15295cff7825Smh27603 switch (reqp->request_type) {
15305cff7825Smh27603 case PMR_PPM_SET_POWER:
15315cff7825Smh27603 pwr_func = ppm_change_power_level;
15325cff7825Smh27603 old = reqp->req.ppm_set_power_req.old_level;
15335cff7825Smh27603 new = reqp->req.ppm_set_power_req.new_level;
15345cff7825Smh27603 cmpt = reqp->req.ppm_set_power_req.cmpt;
15355cff7825Smh27603 break;
15365cff7825Smh27603 case PMR_PPM_POWER_CHANGE_NOTIFY:
15375cff7825Smh27603 pwr_func = ppm_record_level_change;
15385cff7825Smh27603 old = reqp->req.ppm_notify_level_req.old_level;
15395cff7825Smh27603 new = reqp->req.ppm_notify_level_req.new_level;
15405cff7825Smh27603 cmpt = reqp->req.ppm_notify_level_req.cmpt;
15415cff7825Smh27603 break;
15425cff7825Smh27603 default:
15435cff7825Smh27603 *result = DDI_FAILURE;
15445cff7825Smh27603 PPMD(D_FET, ("%s: unknown request type %d for %s@%s\n",
15455cff7825Smh27603 str, reqp->request_type, PM_NAME(dip), PM_ADDR(dip)))
15465cff7825Smh27603 return (DDI_FAILURE);
15475cff7825Smh27603 }
15485cff7825Smh27603
15495cff7825Smh27603 for (ppmd = PPM_GET_PRIVATE(dip); ppmd; ppmd = ppmd->next)
15505cff7825Smh27603 if (cmpt == ppmd->cmpt)
15515cff7825Smh27603 break;
15525cff7825Smh27603 if (!ppmd) {
15535cff7825Smh27603 PPMD(D_FET, ("%s: dip(%p): old(%d)->new(%d): no ppm_dev"
15545cff7825Smh27603 " found for cmpt(%d)", str, (void *)dip, old, new, cmpt))
15555cff7825Smh27603 *result = DDI_FAILURE;
15565cff7825Smh27603 return (DDI_FAILURE);
15575cff7825Smh27603 }
15585cff7825Smh27603 domp = ppmd->domp;
15595cff7825Smh27603 PPMD(D_FET, ("%s: %s@%s %s old %d, new %d, c%d, level %d, "
15605cff7825Smh27603 "status %s\n", str, PM_NAME(dip), PM_ADDR(dip),
15615cff7825Smh27603 ppm_get_ctlstr(reqp->request_type, ~0), old, new, cmpt,
15625cff7825Smh27603 ppmd->level, (domp->status == PPMD_OFF ? "off" : "on")))
15635cff7825Smh27603
15645cff7825Smh27603
15655cff7825Smh27603 ASSERT(old == ppmd->level);
15665cff7825Smh27603
15675cff7825Smh27603 if (new == ppmd->level) {
15685cff7825Smh27603 PPMD(D_FET, ("nop\n"))
15695cff7825Smh27603 return (DDI_SUCCESS);
15705cff7825Smh27603 }
15715cff7825Smh27603
15725cff7825Smh27603 PPM_LOCK_DOMAIN(domp);
15735cff7825Smh27603
15745cff7825Smh27603 /*
15755cff7825Smh27603 * In general, a device's published lowest power level does not
15765cff7825Smh27603 * have to be 0 if power-off is not tolerated. i.e. a device
15775cff7825Smh27603 * instance may export its lowest level > 0. It is reasonable to
15785cff7825Smh27603 * assume that level 0 indicates off state, positive level values
15795cff7825Smh27603 * indicate power states above off, include full power state.
15805cff7825Smh27603 */
15815cff7825Smh27603 if (new > 0) { /* device powering up or to different positive level */
15825cff7825Smh27603 if (domp->status == PPMD_OFF) {
15835cff7825Smh27603
15845cff7825Smh27603 /* can not be in (chpt, resume) window */
15855cff7825Smh27603 ASSERT(ppm_cpr_window_flag == B_FALSE);
15865cff7825Smh27603
15875cff7825Smh27603 ASSERT(old == 0 && domp->pwr_cnt == 0);
15885cff7825Smh27603
15895cff7825Smh27603 PPMD(D_FET, ("About to turn fet on for %s@%s c%d\n",
15905cff7825Smh27603 PM_NAME(dip), PM_ADDR(dip), cmpt))
15915cff7825Smh27603
15925cff7825Smh27603 *result = ppm_fetset(domp, PPMD_ON);
15935cff7825Smh27603 if (*result != DDI_SUCCESS) {
15945cff7825Smh27603 PPMD(D_FET, ("\tCan't turn on power FET: "
15955cff7825Smh27603 "ret(%d)\n", *result))
15965cff7825Smh27603 PPM_UNLOCK_DOMAIN(domp);
15975cff7825Smh27603 return (DDI_FAILURE);
15985cff7825Smh27603 }
15995cff7825Smh27603 }
16005cff7825Smh27603
16015cff7825Smh27603 /*
16025cff7825Smh27603 * If powering up, pre-increment the count before
16035cff7825Smh27603 * calling pwr_func, because we are going to release
16045cff7825Smh27603 * the domain lock and another thread might turn off
16055cff7825Smh27603 * domain power otherwise.
16065cff7825Smh27603 */
16075cff7825Smh27603 if (old == 0) {
16085cff7825Smh27603 domp->pwr_cnt++;
16095cff7825Smh27603 incr = 1;
16105cff7825Smh27603 }
16115cff7825Smh27603
16125cff7825Smh27603 PPMD(D_FET, ("\t%s domain power count: %d\n",
16135cff7825Smh27603 domp->name, domp->pwr_cnt))
16145cff7825Smh27603 }
16155cff7825Smh27603
16165cff7825Smh27603
16175cff7825Smh27603 PPM_UNLOCK_DOMAIN(domp);
16185cff7825Smh27603
16195cff7825Smh27603 ASSERT(domp->pwr_cnt > 0);
16205cff7825Smh27603
16215cff7825Smh27603 if ((*result = (*pwr_func)(ppmd, cmpt, new)) != DDI_SUCCESS) {
16225cff7825Smh27603 PPMD(D_FET, ("\t%s power change failed: ret(%d)\n",
16235cff7825Smh27603 ppmd->path, *result))
16245cff7825Smh27603 }
16255cff7825Smh27603
16265cff7825Smh27603 PPM_LOCK_DOMAIN(domp);
16275cff7825Smh27603
16285cff7825Smh27603 /*
16295cff7825Smh27603 * Decr the power count in two cases:
16305cff7825Smh27603 *
16315cff7825Smh27603 * 1) request was to power device down and was successful
16325cff7825Smh27603 * 2) request was to power up (we pre-incremented count), but failed.
16335cff7825Smh27603 */
16345cff7825Smh27603 if ((*result == DDI_SUCCESS && ppmd->level == 0) ||
16355cff7825Smh27603 (*result != DDI_SUCCESS && incr)) {
16365cff7825Smh27603 ASSERT(domp->pwr_cnt > 0);
16375cff7825Smh27603 domp->pwr_cnt--;
16385cff7825Smh27603 }
16395cff7825Smh27603
16405cff7825Smh27603 PPMD(D_FET, ("\t%s domain power count: %d\n",
16415cff7825Smh27603 domp->name, domp->pwr_cnt))
16425cff7825Smh27603
16435cff7825Smh27603 /*
16445cff7825Smh27603 * call to pwr_func will update ppm data structures, if it
16455cff7825Smh27603 * succeeds. ppm should return whatever is the return value
16465cff7825Smh27603 * from call to pwr_func. This way pm and ppm data structures
16475cff7825Smh27603 * always in sync. Use dummy_ret from here for any further
16485cff7825Smh27603 * return values.
16495cff7825Smh27603 */
16505cff7825Smh27603 if ((domp->pwr_cnt == 0) &&
16515cff7825Smh27603 (ppm_cpr_window_flag == B_FALSE) &&
16525cff7825Smh27603 ppm_none_else_holds_power(domp)) {
16535cff7825Smh27603
16545cff7825Smh27603 PPMD(D_FET, ("About to turn FET off for %s@%s c%d\n",
16555cff7825Smh27603 PM_NAME(dip), PM_ADDR(dip), cmpt))
16565cff7825Smh27603
16575cff7825Smh27603 dummy_ret = ppm_fetset(domp, PPMD_OFF);
16585cff7825Smh27603 if (dummy_ret != DDI_SUCCESS) {
16595cff7825Smh27603 PPMD(D_FET, ("\tCan't turn off FET: ret(%d)\n",
16605cff7825Smh27603 dummy_ret))
16615cff7825Smh27603 }
16625cff7825Smh27603 }
16635cff7825Smh27603
16645cff7825Smh27603 PPM_UNLOCK_DOMAIN(domp);
16655cff7825Smh27603 ASSERT(domp->pwr_cnt >= 0);
16665cff7825Smh27603 return (*result);
16675cff7825Smh27603 }
16685cff7825Smh27603
16695cff7825Smh27603
16705cff7825Smh27603 /*
16715cff7825Smh27603 * the actual code that turn on or off domain power fet and
16725cff7825Smh27603 * update domain status
16735cff7825Smh27603 */
16745cff7825Smh27603 static int
ppm_fetset(ppm_domain_t * domp,uint8_t value)16755cff7825Smh27603 ppm_fetset(ppm_domain_t *domp, uint8_t value)
16765cff7825Smh27603 {
16775cff7825Smh27603 char *str = "ppm_fetset";
16785cff7825Smh27603 int key;
16795cff7825Smh27603 ppm_dc_t *dc;
16805cff7825Smh27603 int ret;
16815cff7825Smh27603 clock_t temp;
16825cff7825Smh27603 clock_t delay = 0;
16835cff7825Smh27603
16845cff7825Smh27603 key = (value == PPMD_ON) ? PPMDC_FET_ON : PPMDC_FET_OFF;
16855cff7825Smh27603 for (dc = domp->dc; dc; dc = dc->next)
16865cff7825Smh27603 if (dc->cmd == key)
16875cff7825Smh27603 break;
16885cff7825Smh27603 if (!dc || !dc->lh) {
16895cff7825Smh27603 PPMD(D_FET, ("%s: %s domain: NULL ppm_dc handle\n",
16905cff7825Smh27603 str, domp->name))
16915cff7825Smh27603 return (DDI_FAILURE);
16925cff7825Smh27603 }
16935cff7825Smh27603
16945cff7825Smh27603 if (key == PPMDC_FET_ON) {
16955cff7825Smh27603 PPM_GET_IO_DELAY(dc, delay);
16965cff7825Smh27603 if (delay > 0 && domp->last_off_time > 0) {
16975cff7825Smh27603 /*
16985cff7825Smh27603 * provide any delay required before turning on.
16995cff7825Smh27603 * some devices e.g. Samsung DVD require minimum
17005cff7825Smh27603 * of 1 sec between OFF->ON. no delay is required
17015cff7825Smh27603 * for the first time.
17025cff7825Smh27603 */
17035cff7825Smh27603 temp = ddi_get_lbolt();
17045cff7825Smh27603 temp -= domp->last_off_time;
17055cff7825Smh27603 temp = drv_hztousec(temp);
17065cff7825Smh27603
17075cff7825Smh27603 if (temp < delay) {
17085cff7825Smh27603 /*
17095cff7825Smh27603 * busy wait untill we meet the
17105cff7825Smh27603 * required delay. Since we maintain
17115cff7825Smh27603 * time stamps in terms of clock ticks
17125cff7825Smh27603 * we might wait for longer than required
17135cff7825Smh27603 */
17145cff7825Smh27603 PPMD(D_FET, ("%s : waiting %lu micro seconds "
17155cff7825Smh27603 "before on\n", domp->name,
17162df1fe9cSrandyf delay - temp));
17175cff7825Smh27603 drv_usecwait(delay - temp);
17185cff7825Smh27603 }
17195cff7825Smh27603 }
17205cff7825Smh27603 }
17215cff7825Smh27603 switch (dc->method) {
17222df1fe9cSrandyf #ifdef sun4u
17235cff7825Smh27603 case PPMDC_I2CKIO: {
17245cff7825Smh27603 i2c_gpio_t i2c_req;
17255cff7825Smh27603 i2c_req.reg_mask = dc->m_un.i2c.mask;
17265cff7825Smh27603 i2c_req.reg_val = dc->m_un.i2c.val;
17275cff7825Smh27603 ret = ldi_ioctl(dc->lh, dc->m_un.i2c.iowr,
17285cff7825Smh27603 (intptr_t)&i2c_req, FWRITE | FKIOCTL, kcred, NULL);
17295cff7825Smh27603 break;
17305cff7825Smh27603 }
17315cff7825Smh27603 #endif
17325cff7825Smh27603
17335cff7825Smh27603 case PPMDC_KIO:
17345cff7825Smh27603 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
17355cff7825Smh27603 (intptr_t)&(dc->m_un.kio.val), FWRITE | FKIOCTL, kcred,
17365cff7825Smh27603 NULL);
17375cff7825Smh27603 break;
17385cff7825Smh27603
17395cff7825Smh27603 default:
17405cff7825Smh27603 PPMD(D_FET, ("\t%s: unsupported domain control method %d\n",
17415cff7825Smh27603 str, domp->dc->method))
17425cff7825Smh27603 return (DDI_FAILURE);
17435cff7825Smh27603 }
17445cff7825Smh27603
17455cff7825Smh27603 PPMD(D_FET, ("%s: %s domain(%s) FET from %s to %s\n", str,
17465cff7825Smh27603 (ret == 0) ? "turned" : "failed to turn",
17475cff7825Smh27603 domp->name,
17485cff7825Smh27603 (domp->status == PPMD_ON) ? "ON" : "OFF",
17495cff7825Smh27603 (value == PPMD_ON) ? "ON" : "OFF"))
17505cff7825Smh27603
17515cff7825Smh27603 if (ret == DDI_SUCCESS) {
17525cff7825Smh27603 domp->status = value;
17535cff7825Smh27603
17545cff7825Smh27603 if (key == PPMDC_FET_OFF)
17555cff7825Smh27603 /*
17565cff7825Smh27603 * record the time, when it is off. time is recorded
17575cff7825Smh27603 * in clock ticks
17585cff7825Smh27603 */
17595cff7825Smh27603 domp->last_off_time = ddi_get_lbolt();
17605cff7825Smh27603
17615cff7825Smh27603 /* implement any post op delay. */
17625cff7825Smh27603 if (key == PPMDC_FET_ON) {
17631159df0bSmh27603 PPM_GET_IO_POST_DELAY(dc, delay);
17645cff7825Smh27603 PPMD(D_FET, ("%s : waiting %lu micro seconds "
17655cff7825Smh27603 "after on\n", domp->name, delay))
17665cff7825Smh27603 if (delay > 0)
17675cff7825Smh27603 drv_usecwait(delay);
17685cff7825Smh27603 }
17695cff7825Smh27603 }
17705cff7825Smh27603
17715cff7825Smh27603 return (ret);
17725cff7825Smh27603 }
17735cff7825Smh27603
17745cff7825Smh27603
17755cff7825Smh27603 /*
17765cff7825Smh27603 * read power fet status
17775cff7825Smh27603 */
17785cff7825Smh27603 static int
ppm_fetget(ppm_domain_t * domp,uint8_t * lvl)17795cff7825Smh27603 ppm_fetget(ppm_domain_t *domp, uint8_t *lvl)
17805cff7825Smh27603 {
17815cff7825Smh27603 char *str = "ppm_fetget";
17825cff7825Smh27603 ppm_dc_t *dc = domp->dc;
17835cff7825Smh27603 uint_t kio_val;
17845cff7825Smh27603 int off_val;
17855cff7825Smh27603 int ret;
17865cff7825Smh27603
17875cff7825Smh27603 if (!dc->lh) {
17885cff7825Smh27603 PPMD(D_FET, ("%s: %s domain NULL ppm_dc layered handle\n",
17895cff7825Smh27603 str, domp->name))
17905cff7825Smh27603 return (DDI_FAILURE);
17915cff7825Smh27603 }
17925cff7825Smh27603 if (!dc->next) {
17935cff7825Smh27603 cmn_err(CE_WARN, "%s: expect both fet on and fet off ops "
17945cff7825Smh27603 "defined, found only one in domain(%s)", str, domp->name);
17955cff7825Smh27603 return (DDI_FAILURE);
17965cff7825Smh27603 }
17975cff7825Smh27603
17985cff7825Smh27603 switch (dc->method) {
17992df1fe9cSrandyf #ifdef sun4u
18005cff7825Smh27603 case PPMDC_I2CKIO: {
18015cff7825Smh27603 i2c_gpio_t i2c_req;
18025cff7825Smh27603 i2c_req.reg_mask = dc->m_un.i2c.mask;
18035cff7825Smh27603 ret = ldi_ioctl(dc->lh, dc->m_un.i2c.iord,
18045cff7825Smh27603 (intptr_t)&i2c_req, FWRITE | FKIOCTL, kcred, NULL);
18055cff7825Smh27603
18065cff7825Smh27603 if (ret) {
18075cff7825Smh27603 PPMD(D_FET, ("%s: PPMDC_I2CKIO failed: ret(%d)\n",
18085cff7825Smh27603 str, ret))
18095cff7825Smh27603 return (ret);
18105cff7825Smh27603 }
18115cff7825Smh27603
18125cff7825Smh27603 off_val = (dc->cmd == PPMDC_FET_OFF) ? dc->m_un.i2c.val :
18135cff7825Smh27603 dc->next->m_un.i2c.val;
18145cff7825Smh27603 *lvl = (i2c_req.reg_val == off_val) ? PPMD_OFF : PPMD_ON;
18155cff7825Smh27603
18165cff7825Smh27603 PPMD(D_FET, ("%s: %s domain FET %s\n", str, domp->name,
18175cff7825Smh27603 (i2c_req.reg_val == off_val) ? "OFF" : "ON"))
18185cff7825Smh27603
18195cff7825Smh27603 break;
18205cff7825Smh27603 }
18215cff7825Smh27603 #endif
18225cff7825Smh27603
18235cff7825Smh27603 case PPMDC_KIO:
18245cff7825Smh27603 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iord,
18255cff7825Smh27603 (intptr_t)&kio_val, FWRITE | FKIOCTL, kcred, NULL);
18265cff7825Smh27603 if (ret) {
18275cff7825Smh27603 PPMD(D_FET, ("%s: PPMDC_KIO failed: ret(%d)\n",
18285cff7825Smh27603 str, ret))
18295cff7825Smh27603 return (ret);
18305cff7825Smh27603 }
18315cff7825Smh27603
18325cff7825Smh27603 off_val = (dc->cmd == PPMDC_FET_OFF) ? dc->m_un.kio.val :
18335cff7825Smh27603 dc->next->m_un.kio.val;
18345cff7825Smh27603 *lvl = (kio_val == off_val) ? PPMD_OFF : PPMD_ON;
18355cff7825Smh27603
18365cff7825Smh27603 PPMD(D_FET, ("%s: %s domain FET %s\n", str, domp->name,
18375cff7825Smh27603 (kio_val == off_val) ? "OFF" : "ON"))
18385cff7825Smh27603
18395cff7825Smh27603 break;
18405cff7825Smh27603
18415cff7825Smh27603 default:
18425cff7825Smh27603 PPMD(D_FET, ("%s: unsupported domain control method %d\n",
18435cff7825Smh27603 str, domp->dc->method))
18445cff7825Smh27603 return (DDI_FAILURE);
18455cff7825Smh27603 }
18465cff7825Smh27603
18475cff7825Smh27603 return (DDI_SUCCESS);
18485cff7825Smh27603 }
18495cff7825Smh27603
18505cff7825Smh27603
18515cff7825Smh27603 /*
18525cff7825Smh27603 * the actual code that switches pci clock and update domain status
18535cff7825Smh27603 */
18545cff7825Smh27603 static int
ppm_switch_clock(ppm_domain_t * domp,int onoff)18555cff7825Smh27603 ppm_switch_clock(ppm_domain_t *domp, int onoff)
18565cff7825Smh27603 {
18575cff7825Smh27603 #ifdef DEBUG
18585cff7825Smh27603 char *str = "ppm_switch_clock";
18595cff7825Smh27603 #endif
18605cff7825Smh27603 int cmd, pio_save;
18615cff7825Smh27603 ppm_dc_t *dc;
18625cff7825Smh27603 int ret;
18635cff7825Smh27603 extern int do_polled_io;
18645cff7825Smh27603 extern uint_t cfb_inuse;
18655cff7825Smh27603 ppm_dev_t *pdev;
18665cff7825Smh27603
18675cff7825Smh27603 cmd = (onoff == PPMD_ON) ? PPMDC_CLK_ON : PPMDC_CLK_OFF;
18685cff7825Smh27603 dc = ppm_lookup_dc(domp, cmd);
18695cff7825Smh27603 if (!dc) {
18705cff7825Smh27603 PPMD(D_PCI, ("%s: no ppm_dc found for domain (%s)\n",
18715cff7825Smh27603 str, domp->name))
18725cff7825Smh27603 return (DDI_FAILURE);
18735cff7825Smh27603 }
18745cff7825Smh27603
18755cff7825Smh27603 switch (dc->method) {
18765cff7825Smh27603 case PPMDC_KIO:
18775cff7825Smh27603 /*
18785cff7825Smh27603 * If we're powering up cfb on a Stop-A, we only
18795cff7825Smh27603 * want to do polled i/o to turn ON the clock
18805cff7825Smh27603 */
18815cff7825Smh27603 pio_save = do_polled_io;
18825cff7825Smh27603 if ((cfb_inuse) && (cmd == PPMDC_CLK_ON)) {
18835cff7825Smh27603 for (pdev = domp->devlist; pdev; pdev = pdev->next) {
18845cff7825Smh27603 if (pm_is_cfb(pdev->dip)) {
18855cff7825Smh27603 do_polled_io = 1;
18865cff7825Smh27603 break;
18875cff7825Smh27603 }
18885cff7825Smh27603 }
18895cff7825Smh27603 }
18905cff7825Smh27603
18915cff7825Smh27603 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
18925cff7825Smh27603 (intptr_t)&(dc->m_un.kio.val), FWRITE | FKIOCTL,
18935cff7825Smh27603 kcred, NULL);
18945cff7825Smh27603
18955cff7825Smh27603 do_polled_io = pio_save;
18965cff7825Smh27603
18975cff7825Smh27603 if (ret == 0) {
18985cff7825Smh27603 if (cmd == PPMDC_CLK_ON) {
18995cff7825Smh27603 domp->status = PPMD_ON;
19005cff7825Smh27603
19015cff7825Smh27603 /*
19025cff7825Smh27603 * PCI PM spec requires 50ms delay
19035cff7825Smh27603 */
19045cff7825Smh27603 drv_usecwait(50000);
19055cff7825Smh27603 } else
19065cff7825Smh27603 domp->status = PPMD_OFF;
19075cff7825Smh27603 }
19085cff7825Smh27603
19095cff7825Smh27603 PPMD(D_PCI, ("%s: %s pci clock %s for domain (%s)\n", str,
19105cff7825Smh27603 (ret == 0) ? "turned" : "failed to turn",
19115cff7825Smh27603 (cmd == PPMDC_CLK_OFF) ? "OFF" : "ON",
19125cff7825Smh27603 domp->name))
19135cff7825Smh27603
19145cff7825Smh27603 break;
19155cff7825Smh27603
19165cff7825Smh27603 default:
19175cff7825Smh27603 PPMD(D_PCI, ("%s: unsupported domain control method %d\n",
19185cff7825Smh27603 str, dc->method))
19195cff7825Smh27603 return (DDI_FAILURE);
19205cff7825Smh27603 }
19215cff7825Smh27603
19225cff7825Smh27603 return (DDI_SUCCESS);
19235cff7825Smh27603 }
19245cff7825Smh27603
19255cff7825Smh27603
19265cff7825Smh27603 /*
19275cff7825Smh27603 * pci slot domain is formed of pci device(s) reside in a pci slot.
19285cff7825Smh27603 * This function monitors domain device's power level change, such
19295cff7825Smh27603 * that,
19305cff7825Smh27603 * when all domain power count has gone to 0, it attempts to turn off
19315cff7825Smh27603 * the pci slot's clock;
19325cff7825Smh27603 * if any domain device is powering up, it'll turn on the pci slot's
19335cff7825Smh27603 * clock as the first thing.
19345cff7825Smh27603 */
19355cff7825Smh27603 /* ARGUSED */
19365cff7825Smh27603 static int
ppm_manage_pci(dev_info_t * dip,power_req_t * reqp,int * result)19375cff7825Smh27603 ppm_manage_pci(dev_info_t *dip, power_req_t *reqp, int *result)
19385cff7825Smh27603 {
19395cff7825Smh27603 #ifdef DEBUG
19405cff7825Smh27603 char *str = "ppm_manage_pci";
19415cff7825Smh27603 #endif
19425cff7825Smh27603 int (*pwr_func)(ppm_dev_t *, int, int);
19435cff7825Smh27603 int old, new, cmpt;
19445cff7825Smh27603 ppm_dev_t *ppmd;
19455cff7825Smh27603 ppm_domain_t *domp;
19465cff7825Smh27603 int incr = 0;
19475cff7825Smh27603 int dummy_ret;
19485cff7825Smh27603
19495cff7825Smh27603 *result = DDI_SUCCESS;
19505cff7825Smh27603 switch (reqp->request_type) {
19515cff7825Smh27603 case PMR_PPM_SET_POWER:
19525cff7825Smh27603 pwr_func = ppm_change_power_level;
19535cff7825Smh27603 old = reqp->req.ppm_set_power_req.old_level;
19545cff7825Smh27603 new = reqp->req.ppm_set_power_req.new_level;
19555cff7825Smh27603 cmpt = reqp->req.ppm_set_power_req.cmpt;
19565cff7825Smh27603 break;
19575cff7825Smh27603
19585cff7825Smh27603 case PMR_PPM_POWER_CHANGE_NOTIFY:
19595cff7825Smh27603 pwr_func = ppm_record_level_change;
19605cff7825Smh27603 old = reqp->req.ppm_notify_level_req.old_level;
19615cff7825Smh27603 new = reqp->req.ppm_notify_level_req.new_level;
19625cff7825Smh27603 cmpt = reqp->req.ppm_notify_level_req.cmpt;
19635cff7825Smh27603 break;
19645cff7825Smh27603
19655cff7825Smh27603 default:
19665cff7825Smh27603 *result = DDI_FAILURE;
19675cff7825Smh27603 return (DDI_FAILURE);
19685cff7825Smh27603 }
19695cff7825Smh27603
19705cff7825Smh27603 for (ppmd = PPM_GET_PRIVATE(dip); ppmd; ppmd = ppmd->next)
19715cff7825Smh27603 if (cmpt == ppmd->cmpt)
19725cff7825Smh27603 break;
19735cff7825Smh27603 if (!ppmd) {
19745cff7825Smh27603 PPMD(D_PCI, ("%s: dip(%p): old(%d), new(%d): no ppm_dev"
19755cff7825Smh27603 " found for cmpt(%d)", str, (void *)dip, old, new, cmpt))
19765cff7825Smh27603 *result = DDI_FAILURE;
19775cff7825Smh27603 return (DDI_FAILURE);
19785cff7825Smh27603 }
19795cff7825Smh27603 domp = ppmd->domp;
19805cff7825Smh27603 PPMD(D_PCI, ("%s: %s, dev(%s), c%d, old %d, new %d\n", str,
19815cff7825Smh27603 ppm_get_ctlstr(reqp->request_type, ~0),
19825cff7825Smh27603 ppmd->path, cmpt, old, new))
19835cff7825Smh27603
19845cff7825Smh27603 ASSERT(old == ppmd->level);
19855cff7825Smh27603 if (new == ppmd->level)
19865cff7825Smh27603 return (DDI_SUCCESS);
19875cff7825Smh27603
19885cff7825Smh27603 PPM_LOCK_DOMAIN(domp);
19895cff7825Smh27603
19905cff7825Smh27603 if (new > 0) { /* device powering up */
19915cff7825Smh27603 if (domp->status == PPMD_OFF) {
19925cff7825Smh27603
19935cff7825Smh27603 /* cannot be off during (chpt, resume) window */
19945cff7825Smh27603 ASSERT(ppm_cpr_window_flag == B_FALSE);
19955cff7825Smh27603
19965cff7825Smh27603 /* either both OFF or both ON */
19975cff7825Smh27603 ASSERT(!((old == 0) ^ (domp->pwr_cnt == 0)));
19985cff7825Smh27603
19995cff7825Smh27603 PPMD(D_PCI, ("About to turn clock on for %s@%s c%d\n",
20005cff7825Smh27603 PM_NAME(dip), PM_ADDR(dip), cmpt))
20015cff7825Smh27603
20025cff7825Smh27603 *result = ppm_switch_clock(domp, PPMD_ON);
20035cff7825Smh27603 if (*result != DDI_SUCCESS) {
20045cff7825Smh27603 PPMD(D_PCI, ("\tcan't switch on pci clock: "
20055cff7825Smh27603 "ret(%d)\n", *result))
20065cff7825Smh27603 PPM_UNLOCK_DOMAIN(domp);
20075cff7825Smh27603 return (DDI_FAILURE);
20085cff7825Smh27603 }
20095cff7825Smh27603 }
20105cff7825Smh27603
20115cff7825Smh27603 if (old == 0) {
20125cff7825Smh27603 domp->pwr_cnt++;
20135cff7825Smh27603 incr = 1;
20145cff7825Smh27603 }
20155cff7825Smh27603
20165cff7825Smh27603 PPMD(D_PCI, ("\t%s domain power count: %d\n",
20175cff7825Smh27603 domp->name, domp->pwr_cnt))
20185cff7825Smh27603 }
20195cff7825Smh27603
20205cff7825Smh27603 PPM_UNLOCK_DOMAIN(domp);
20215cff7825Smh27603
20225cff7825Smh27603 ASSERT(domp->pwr_cnt > 0);
20235cff7825Smh27603
20245cff7825Smh27603 if ((*result = (*pwr_func)(ppmd, cmpt, new)) != DDI_SUCCESS) {
20255cff7825Smh27603 PPMD(D_PCI, ("\t%s power change failed: ret(%d)\n",
20265cff7825Smh27603 ppmd->path, *result))
20275cff7825Smh27603 }
20285cff7825Smh27603
20295cff7825Smh27603 PPM_LOCK_DOMAIN(domp);
20305cff7825Smh27603
20315cff7825Smh27603 /*
20325cff7825Smh27603 * Decr the power count in two cases:
20335cff7825Smh27603 *
20345cff7825Smh27603 * 1) request was to power device down and was successful
20355cff7825Smh27603 * 2) request was to power up (we pre-incremented count), but failed.
20365cff7825Smh27603 */
20375cff7825Smh27603 if ((*result == DDI_SUCCESS && ppmd->level == 0) ||
20385cff7825Smh27603 (*result != DDI_SUCCESS && incr)) {
20395cff7825Smh27603 ASSERT(domp->pwr_cnt > 0);
20405cff7825Smh27603 domp->pwr_cnt--;
20415cff7825Smh27603 }
20425cff7825Smh27603
20435cff7825Smh27603 PPMD(D_PCI, ("\t%s domain power count: %d\n",
20445cff7825Smh27603 domp->name, domp->pwr_cnt))
20455cff7825Smh27603
20465cff7825Smh27603 /*
20475cff7825Smh27603 * call to pwr_func will update ppm data structures, if it
20485cff7825Smh27603 * succeeds. ppm should return whatever is the return value
20495cff7825Smh27603 * from call to pwr_func. This way pm and ppm data structures
20505cff7825Smh27603 * always in sync. Use dummy_ret from here for any further
20515cff7825Smh27603 * return values.
20525cff7825Smh27603 */
20535cff7825Smh27603 if ((domp->pwr_cnt == 0) &&
20545cff7825Smh27603 (ppm_cpr_window_flag == B_FALSE) &&
20555cff7825Smh27603 ppm_none_else_holds_power(domp)) {
20565cff7825Smh27603
20575cff7825Smh27603 PPMD(D_PCI, ("About to turn clock off for %s@%s c%d\n",
20585cff7825Smh27603 PM_NAME(dip), PM_ADDR(dip), cmpt))
20595cff7825Smh27603
20605cff7825Smh27603 dummy_ret = ppm_switch_clock(domp, PPMD_OFF);
20615cff7825Smh27603 if (dummy_ret != DDI_SUCCESS) {
20625cff7825Smh27603 PPMD(D_PCI, ("\tCan't switch clock off: "
20635cff7825Smh27603 "ret(%d)\n", dummy_ret))
20645cff7825Smh27603 }
20655cff7825Smh27603 }
20665cff7825Smh27603
20675cff7825Smh27603 PPM_UNLOCK_DOMAIN(domp);
20685cff7825Smh27603 ASSERT(domp->pwr_cnt >= 0);
20695cff7825Smh27603 return (*result);
20705cff7825Smh27603 }
20715cff7825Smh27603
20725cff7825Smh27603 /*
20735cff7825Smh27603 * When the driver for the primary PCI-Express child has set the device to
20745cff7825Smh27603 * lowest power (D3hot), we come here to save even more power by transitioning
20755cff7825Smh27603 * the slot to D3cold. Similarly, if the slot is in D3cold and we need to
20765cff7825Smh27603 * power up the child, we come here first to power up the slot.
20775cff7825Smh27603 */
20785cff7825Smh27603 /* ARGUSED */
20795cff7825Smh27603 static int
ppm_manage_pcie(dev_info_t * dip,power_req_t * reqp,int * result)20805cff7825Smh27603 ppm_manage_pcie(dev_info_t *dip, power_req_t *reqp, int *result)
20815cff7825Smh27603 {
20825cff7825Smh27603 #ifdef DEBUG
20835cff7825Smh27603 char *str = "ppm_manage_pcie";
20845cff7825Smh27603 #endif
20855cff7825Smh27603 int (*pwr_func)(ppm_dev_t *, int, int);
20865cff7825Smh27603 int old, new, cmpt;
20875cff7825Smh27603 ppm_dev_t *ppmd;
20885cff7825Smh27603 ppm_domain_t *domp;
20895cff7825Smh27603 int incr = 0;
20905cff7825Smh27603 int dummy_ret;
20915cff7825Smh27603
20925cff7825Smh27603 *result = DDI_SUCCESS;
20935cff7825Smh27603 switch (reqp->request_type) {
20945cff7825Smh27603 case PMR_PPM_SET_POWER:
20955cff7825Smh27603 pwr_func = ppm_change_power_level;
20965cff7825Smh27603 old = reqp->req.ppm_set_power_req.old_level;
20975cff7825Smh27603 new = reqp->req.ppm_set_power_req.new_level;
20985cff7825Smh27603 cmpt = reqp->req.ppm_set_power_req.cmpt;
20995cff7825Smh27603 break;
21005cff7825Smh27603
21015cff7825Smh27603 case PMR_PPM_POWER_CHANGE_NOTIFY:
21025cff7825Smh27603 pwr_func = ppm_record_level_change;
21035cff7825Smh27603 old = reqp->req.ppm_notify_level_req.old_level;
21045cff7825Smh27603 new = reqp->req.ppm_notify_level_req.new_level;
21055cff7825Smh27603 cmpt = reqp->req.ppm_notify_level_req.cmpt;
21065cff7825Smh27603 break;
21075cff7825Smh27603
21085cff7825Smh27603 default:
21095cff7825Smh27603 *result = DDI_FAILURE;
21105cff7825Smh27603 return (DDI_FAILURE);
21115cff7825Smh27603 }
21125cff7825Smh27603
21135cff7825Smh27603 for (ppmd = PPM_GET_PRIVATE(dip); ppmd; ppmd = ppmd->next)
21145cff7825Smh27603 if (cmpt == ppmd->cmpt)
21155cff7825Smh27603 break;
21165cff7825Smh27603 if (!ppmd) {
21175cff7825Smh27603 PPMD(D_PCI, ("%s: dip(%p): old(%d), new(%d): no ppm_dev"
21185cff7825Smh27603 " found for cmpt(%d)", str, (void *)dip, old, new, cmpt))
21195cff7825Smh27603 *result = DDI_FAILURE;
21205cff7825Smh27603 return (DDI_FAILURE);
21215cff7825Smh27603 }
21225cff7825Smh27603 domp = ppmd->domp;
21235cff7825Smh27603 PPMD(D_PCI, ("%s: %s, dev(%s), c%d, old %d, new %d\n", str,
21245cff7825Smh27603 ppm_get_ctlstr(reqp->request_type, ~0),
21255cff7825Smh27603 ppmd->path, cmpt, old, new))
21265cff7825Smh27603
21275cff7825Smh27603 ASSERT(old == ppmd->level);
21285cff7825Smh27603 if (new == ppmd->level)
21295cff7825Smh27603 return (DDI_SUCCESS);
21305cff7825Smh27603
21315cff7825Smh27603 PPM_LOCK_DOMAIN(domp);
21325cff7825Smh27603
21335cff7825Smh27603 if (new > 0) { /* device powering up */
21345cff7825Smh27603 if (domp->status == PPMD_OFF) {
21355cff7825Smh27603
21365cff7825Smh27603 /* cannot be off during (chpt, resume) window */
21375cff7825Smh27603 ASSERT(ppm_cpr_window_flag == B_FALSE);
21385cff7825Smh27603
21395cff7825Smh27603 /* either both OFF or both ON */
21405cff7825Smh27603 ASSERT(!((old == 0) ^ (domp->pwr_cnt == 0)));
21415cff7825Smh27603
21425cff7825Smh27603 PPMD(D_PCI, ("About to turn on pcie slot for "
21435cff7825Smh27603 "%s@%s c%d\n", PM_NAME(dip), PM_ADDR(dip), cmpt))
21445cff7825Smh27603
21455cff7825Smh27603 *result = ppm_pcie_pwr(domp, PPMD_ON);
21465cff7825Smh27603 if (*result != DDI_SUCCESS) {
21475cff7825Smh27603 PPMD(D_PCI, ("\tcan't switch on pcie slot: "
21485cff7825Smh27603 "ret(%d)\n", *result))
21495cff7825Smh27603 PPM_UNLOCK_DOMAIN(domp);
21505cff7825Smh27603 return (DDI_FAILURE);
21515cff7825Smh27603 }
21525cff7825Smh27603 }
21535cff7825Smh27603
21545cff7825Smh27603 if (old == 0) {
21555cff7825Smh27603 domp->pwr_cnt++;
21565cff7825Smh27603 incr = 1;
21575cff7825Smh27603 }
21585cff7825Smh27603
21595cff7825Smh27603 PPMD(D_PCI, ("\t%s domain power count: %d\n",
21605cff7825Smh27603 domp->name, domp->pwr_cnt))
21615cff7825Smh27603 }
21625cff7825Smh27603
21635cff7825Smh27603 PPM_UNLOCK_DOMAIN(domp);
21645cff7825Smh27603
21655cff7825Smh27603 ASSERT(domp->pwr_cnt > 0);
21665cff7825Smh27603
21675cff7825Smh27603 if ((*result = (*pwr_func)(ppmd, cmpt, new)) != DDI_SUCCESS) {
21685cff7825Smh27603 PPMD(D_PCI, ("\t%s power change failed: ret(%d)\n",
21695cff7825Smh27603 ppmd->path, *result))
21705cff7825Smh27603 }
21715cff7825Smh27603
21725cff7825Smh27603 PPM_LOCK_DOMAIN(domp);
21735cff7825Smh27603
21745cff7825Smh27603 /*
21755cff7825Smh27603 * Decr the power count in two cases:
21765cff7825Smh27603 *
21775cff7825Smh27603 * 1) request was to power device down and was successful
21785cff7825Smh27603 * 2) request was to power up (we pre-incremented count), but failed.
21795cff7825Smh27603 */
21805cff7825Smh27603 if ((*result == DDI_SUCCESS && ppmd->level == 0) ||
21815cff7825Smh27603 (*result != DDI_SUCCESS && incr)) {
21825cff7825Smh27603 ASSERT(domp->pwr_cnt > 0);
21835cff7825Smh27603 domp->pwr_cnt--;
21845cff7825Smh27603 }
21855cff7825Smh27603
21865cff7825Smh27603 PPMD(D_PCI, ("\t%s domain power count: %d\n",
21875cff7825Smh27603 domp->name, domp->pwr_cnt))
21885cff7825Smh27603
21895cff7825Smh27603 /*
21905cff7825Smh27603 * call to pwr_func will update ppm data structures, if it
21915cff7825Smh27603 * succeeds. ppm should return whatever is the return value
21925cff7825Smh27603 * from call to pwr_func. This way pm and ppm data structures
21935cff7825Smh27603 * always in sync. Use dummy_ret from here for any further
21945cff7825Smh27603 * return values.
21955cff7825Smh27603 */
21965cff7825Smh27603 if ((domp->pwr_cnt == 0) &&
21975cff7825Smh27603 (ppm_cpr_window_flag == B_FALSE) &&
21985cff7825Smh27603 ppm_none_else_holds_power(domp)) {
21995cff7825Smh27603
22005cff7825Smh27603 PPMD(D_PCI, ("About to turn off pcie slot for %s@%s c%d\n",
22015cff7825Smh27603 PM_NAME(dip), PM_ADDR(dip), cmpt))
22025cff7825Smh27603
22035cff7825Smh27603 dummy_ret = ppm_pcie_pwr(domp, PPMD_OFF);
22045cff7825Smh27603 if (dummy_ret != DDI_SUCCESS) {
22055cff7825Smh27603 PPMD(D_PCI, ("\tCan't switch pcie slot off: "
22065cff7825Smh27603 "ret(%d)\n", dummy_ret))
22075cff7825Smh27603 }
22085cff7825Smh27603 }
22095cff7825Smh27603
22105cff7825Smh27603 PPM_UNLOCK_DOMAIN(domp);
22115cff7825Smh27603 ASSERT(domp->pwr_cnt >= 0);
22125cff7825Smh27603 return (*result);
22135cff7825Smh27603
22145cff7825Smh27603 }
22155cff7825Smh27603
22165cff7825Smh27603 /*
22175cff7825Smh27603 * Set or clear a bit on a GPIO device. These bits are used for various device-
22185cff7825Smh27603 * specific purposes.
22195cff7825Smh27603 */
22205cff7825Smh27603 static int
ppm_gpioset(ppm_domain_t * domp,int key)22215cff7825Smh27603 ppm_gpioset(ppm_domain_t *domp, int key)
22225cff7825Smh27603 {
22235cff7825Smh27603 #ifdef DEBUG
22245cff7825Smh27603 char *str = "ppm_gpioset";
22255cff7825Smh27603 #endif
22265cff7825Smh27603 ppm_dc_t *dc;
22275cff7825Smh27603 int ret;
22285cff7825Smh27603 clock_t delay = 0;
22295cff7825Smh27603
22305cff7825Smh27603 for (dc = domp->dc; dc; dc = dc->next)
22315cff7825Smh27603 if (dc->cmd == key)
22325cff7825Smh27603 break;
22335cff7825Smh27603 if (!dc || !dc->lh) {
22345cff7825Smh27603 PPMD(D_GPIO, ("%s: %s domain: NULL ppm_dc handle\n",
22355cff7825Smh27603 str, domp->name))
22365cff7825Smh27603 return (DDI_FAILURE);
22375cff7825Smh27603 }
22385cff7825Smh27603
22395cff7825Smh27603 PPM_GET_IO_DELAY(dc, delay);
22405cff7825Smh27603 if (delay > 0) {
22415cff7825Smh27603 PPMD(D_GPIO, ("%s : waiting %lu micro seconds "
22425cff7825Smh27603 "before change\n", domp->name, delay))
22435cff7825Smh27603 drv_usecwait(delay);
22445cff7825Smh27603 }
22455cff7825Smh27603
22465cff7825Smh27603 switch (dc->method) {
22472df1fe9cSrandyf #ifdef sun4u
22485cff7825Smh27603 case PPMDC_I2CKIO: {
22495cff7825Smh27603 i2c_gpio_t i2c_req;
22505cff7825Smh27603 ppm_dev_t *pdev;
22515cff7825Smh27603 int pio_save;
22525cff7825Smh27603 extern int do_polled_io;
22535cff7825Smh27603 extern uint_t cfb_inuse;
22545cff7825Smh27603 i2c_req.reg_mask = dc->m_un.i2c.mask;
22555cff7825Smh27603 i2c_req.reg_val = dc->m_un.i2c.val;
22565cff7825Smh27603
22575cff7825Smh27603 pio_save = do_polled_io;
22585cff7825Smh27603 if (cfb_inuse) {
22595cff7825Smh27603 for (pdev = domp->devlist; pdev; pdev = pdev->next) {
22605cff7825Smh27603 if (pm_is_cfb(pdev->dip)) {
22615cff7825Smh27603 do_polled_io = 1;
22625cff7825Smh27603 PPMD(D_GPIO, ("%s: cfb is in use, "
22635cff7825Smh27603 "i2c transaction is done in "
22645cff7825Smh27603 "poll-mode.\n", str))
22655cff7825Smh27603 break;
22665cff7825Smh27603 }
22675cff7825Smh27603 }
22685cff7825Smh27603 }
22695cff7825Smh27603 ret = ldi_ioctl(dc->lh, dc->m_un.i2c.iowr,
22705cff7825Smh27603 (intptr_t)&i2c_req, FWRITE | FKIOCTL, kcred, NULL);
22715cff7825Smh27603 do_polled_io = pio_save;
22725cff7825Smh27603
22735cff7825Smh27603 PPMD(D_GPIO, ("%s: %s domain(%s) from %s by writing %x "
22745cff7825Smh27603 "to gpio\n",
22755cff7825Smh27603 str, (ret == 0) ? "turned" : "FAILed to turn",
22765cff7825Smh27603 domp->name,
22775cff7825Smh27603 (domp->status == PPMD_ON) ? "ON" : "OFF",
22785cff7825Smh27603 dc->m_un.i2c.val))
22795cff7825Smh27603
22805cff7825Smh27603 break;
22815cff7825Smh27603 }
22825cff7825Smh27603 #endif
22832df1fe9cSrandyf
22845cff7825Smh27603 case PPMDC_KIO:
22855cff7825Smh27603 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
22865cff7825Smh27603 (intptr_t)&(dc->m_un.kio.val), FWRITE | FKIOCTL, kcred,
22875cff7825Smh27603 NULL);
22885cff7825Smh27603
22895cff7825Smh27603 PPMD(D_GPIO, ("%s: %s domain(%s) from %s by writing %x "
22905cff7825Smh27603 "to gpio\n",
22915cff7825Smh27603 str, (ret == 0) ? "turned" : "FAILed to turn",
22925cff7825Smh27603 domp->name,
22935cff7825Smh27603 (domp->status == PPMD_ON) ? "ON" : "OFF",
22945cff7825Smh27603 dc->m_un.kio.val))
22955cff7825Smh27603
22965cff7825Smh27603 break;
22975cff7825Smh27603
22985cff7825Smh27603 default:
22995cff7825Smh27603 PPMD(D_GPIO, ("\t%s: unsupported domain control method %d\n",
23005cff7825Smh27603 str, domp->dc->method))
23015cff7825Smh27603 return (DDI_FAILURE);
23025cff7825Smh27603 }
23035cff7825Smh27603
23045cff7825Smh27603 /* implement any post op delay. */
23051159df0bSmh27603 PPM_GET_IO_POST_DELAY(dc, delay);
23065cff7825Smh27603 if (delay > 0) {
23075cff7825Smh27603 PPMD(D_GPIO, ("%s : waiting %lu micro seconds "
23085cff7825Smh27603 "after change\n", domp->name, delay))
23095cff7825Smh27603 drv_usecwait(delay);
23105cff7825Smh27603 }
23115cff7825Smh27603
23125cff7825Smh27603 return (ret);
23135cff7825Smh27603 }
23145cff7825Smh27603
23155cff7825Smh27603 static int
ppm_pcie_pwr(ppm_domain_t * domp,int onoff)23165cff7825Smh27603 ppm_pcie_pwr(ppm_domain_t *domp, int onoff)
23175cff7825Smh27603 {
23185cff7825Smh27603 #ifdef DEBUG
23195cff7825Smh27603 char *str = "ppm_pcie_pwr";
23205cff7825Smh27603 #endif
23215cff7825Smh27603 int ret = DDI_FAILURE;
23225cff7825Smh27603 ppm_dc_t *dc;
23235cff7825Smh27603 clock_t delay;
23245cff7825Smh27603
23255cff7825Smh27603 ASSERT(onoff == PPMD_OFF || onoff == PPMD_ON);
23265cff7825Smh27603
23275cff7825Smh27603 dc = ppm_lookup_dc(domp,
23285cff7825Smh27603 onoff == PPMD_ON ? PPMDC_PRE_PWR_ON : PPMDC_PRE_PWR_OFF);
23295cff7825Smh27603 if (dc) {
23305cff7825Smh27603
23315cff7825Smh27603 /*
23325cff7825Smh27603 * Invoke layered ioctl for pcie root complex nexus to
23335cff7825Smh27603 * transition the link
23345cff7825Smh27603 */
23355cff7825Smh27603 ASSERT(dc->method == PPMDC_KIO);
23365cff7825Smh27603 delay = dc->m_un.kio.delay;
23375cff7825Smh27603 if (delay > 0) {
23385cff7825Smh27603 PPMD(D_GPIO, ("%s : waiting %lu micro seconds "
23395cff7825Smh27603 "before change\n", domp->name, delay))
23405cff7825Smh27603 drv_usecwait(delay);
23415cff7825Smh27603 }
23425cff7825Smh27603 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
23435cff7825Smh27603 (intptr_t)&(dc->m_un.kio.val),
23445cff7825Smh27603 FWRITE | FKIOCTL, kcred, NULL);
23455cff7825Smh27603 if (ret == DDI_SUCCESS) {
23465cff7825Smh27603 delay = dc->m_un.kio.post_delay;
23475cff7825Smh27603 if (delay > 0) {
23485cff7825Smh27603 PPMD(D_GPIO, ("%s : waiting %lu micro seconds "
23495cff7825Smh27603 "after change\n", domp->name, delay))
23505cff7825Smh27603 drv_usecwait(delay);
23515cff7825Smh27603 }
23525cff7825Smh27603 } else {
23535cff7825Smh27603 PPMD(D_PCI, ("%s: ldi_ioctl FAILED for domain(%s)\n",
23545cff7825Smh27603 str, domp->name))
23555cff7825Smh27603 return (ret);
23565cff7825Smh27603 }
23575cff7825Smh27603 }
23585cff7825Smh27603
23595cff7825Smh27603 switch (onoff) {
23605cff7825Smh27603 case PPMD_OFF:
23615cff7825Smh27603 /* Turn off the clock for this slot. */
23625cff7825Smh27603 if ((ret = ppm_gpioset(domp, PPMDC_CLK_OFF)) != DDI_SUCCESS) {
23635cff7825Smh27603 PPMD(D_GPIO,
23645cff7825Smh27603 ("%s: failed to turn off domain(%s) clock\n",
23655cff7825Smh27603 str, domp->name))
23665cff7825Smh27603 return (ret);
23675cff7825Smh27603 }
23685cff7825Smh27603
23695cff7825Smh27603 /* Turn off the power to this slot */
23705cff7825Smh27603 if ((ret = ppm_gpioset(domp, PPMDC_PWR_OFF)) != DDI_SUCCESS) {
23715cff7825Smh27603 PPMD(D_GPIO,
23725cff7825Smh27603 ("%s: failed to turn off domain(%s) power\n",
23735cff7825Smh27603 str, domp->name))
23745cff7825Smh27603 return (ret);
23755cff7825Smh27603 }
23765cff7825Smh27603 break;
23775cff7825Smh27603 case PPMD_ON:
23785cff7825Smh27603 /* Assert RESET for this slot. */
23795cff7825Smh27603 if ((ret = ppm_gpioset(domp, PPMDC_RESET_ON)) != DDI_SUCCESS) {
23805cff7825Smh27603 PPMD(D_GPIO,
23815cff7825Smh27603 ("%s: failed to assert reset for domain(%s)\n",
23825cff7825Smh27603 str, domp->name))
23835cff7825Smh27603 return (ret);
23845cff7825Smh27603 }
23855cff7825Smh27603
23865cff7825Smh27603 /* Turn on the power to this slot */
23875cff7825Smh27603 if ((ret = ppm_gpioset(domp, PPMDC_PWR_ON)) != DDI_SUCCESS) {
23885cff7825Smh27603 PPMD(D_GPIO,
23895cff7825Smh27603 ("%s: failed to turn on domain(%s) power\n",
23905cff7825Smh27603 str, domp->name))
23915cff7825Smh27603 return (ret);
23925cff7825Smh27603 }
23935cff7825Smh27603
23945cff7825Smh27603 /* Turn on the clock for this slot */
23955cff7825Smh27603 if ((ret = ppm_gpioset(domp, PPMDC_CLK_ON)) != DDI_SUCCESS) {
23965cff7825Smh27603 PPMD(D_GPIO,
23975cff7825Smh27603 ("%s: failed to turn on domain(%s) clock\n",
23985cff7825Smh27603 str, domp->name))
23995cff7825Smh27603 return (ret);
24005cff7825Smh27603 }
24015cff7825Smh27603
24025cff7825Smh27603 /* De-assert RESET for this slot. */
24035cff7825Smh27603 if ((ret = ppm_gpioset(domp, PPMDC_RESET_OFF)) != DDI_SUCCESS) {
24045cff7825Smh27603 PPMD(D_GPIO,
24055cff7825Smh27603 ("%s: failed to de-assert reset for domain(%s)\n",
24065cff7825Smh27603 str, domp->name))
24075cff7825Smh27603 return (ret);
24085cff7825Smh27603 }
24095cff7825Smh27603
24105cff7825Smh27603 dc = ppm_lookup_dc(domp, PPMDC_POST_PWR_ON);
24115cff7825Smh27603 if (dc) {
24125cff7825Smh27603 /*
24135cff7825Smh27603 * Invoke layered ioctl to PCIe root complex nexus
24145cff7825Smh27603 * to transition the link.
24155cff7825Smh27603 */
24165cff7825Smh27603 ASSERT(dc->method == PPMDC_KIO);
24175cff7825Smh27603 delay = dc->m_un.kio.delay;
24185cff7825Smh27603 if (delay > 0) {
24195cff7825Smh27603 PPMD(D_GPIO, ("%s: waiting %lu micro seconds "
24205cff7825Smh27603 "before change\n", domp->name, delay))
24215cff7825Smh27603 drv_usecwait(delay);
24225cff7825Smh27603 }
24235cff7825Smh27603 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
24245cff7825Smh27603 (intptr_t)&(dc->m_un.kio.val),
24255cff7825Smh27603 FWRITE | FKIOCTL, kcred, NULL);
24265cff7825Smh27603
24275cff7825Smh27603 if (ret != DDI_SUCCESS) {
24285cff7825Smh27603 PPMD(D_PCI, ("%s: layered ioctl to PCIe"
24295cff7825Smh27603 "root complex nexus FAILed\n", str))
24305cff7825Smh27603 return (ret);
24315cff7825Smh27603 }
24325cff7825Smh27603
24335cff7825Smh27603 delay = dc->m_un.kio.post_delay;
24345cff7825Smh27603 if (delay > 0) {
24355cff7825Smh27603 PPMD(D_GPIO, ("%s: waiting %lu micro "
24365cff7825Smh27603 "seconds after change\n",
24375cff7825Smh27603 domp->name, delay))
24385cff7825Smh27603 drv_usecwait(delay);
24395cff7825Smh27603 }
24405cff7825Smh27603 }
24415cff7825Smh27603 break;
24425cff7825Smh27603 default:
24435cff7825Smh27603 ASSERT(0);
24445cff7825Smh27603 }
24455cff7825Smh27603
24465cff7825Smh27603 PPMD(D_PCI, ("%s: turned domain(%s) PCIe slot power from %s to %s\n",
24475cff7825Smh27603 str, domp->name, (domp->status == PPMD_ON) ? "ON" : "OFF",
24485cff7825Smh27603 onoff == PPMD_ON ? "ON" : "OFF"))
24495cff7825Smh27603
24505cff7825Smh27603 domp->status = onoff;
24515cff7825Smh27603 return (ret);
24525cff7825Smh27603 }
24535cff7825Smh27603
24545cff7825Smh27603
24555cff7825Smh27603 /*
24565cff7825Smh27603 * Change the power level for a component of a device. If the change
24575cff7825Smh27603 * arg is true, we call the framework to actually change the device's
24585cff7825Smh27603 * power; otherwise, we just update our own copy of the power level.
24595cff7825Smh27603 */
24605cff7825Smh27603 static int
ppm_set_level(ppm_dev_t * ppmd,int cmpt,int level,boolean_t change)24615cff7825Smh27603 ppm_set_level(ppm_dev_t *ppmd, int cmpt, int level, boolean_t change)
24625cff7825Smh27603 {
24635cff7825Smh27603 #ifdef DEBUG
24645cff7825Smh27603 char *str = "ppm_set_level";
24655cff7825Smh27603 #endif
24665cff7825Smh27603 int ret;
24675cff7825Smh27603
24685cff7825Smh27603 ret = DDI_SUCCESS;
24695cff7825Smh27603 if (change)
24705cff7825Smh27603 ret = pm_power(ppmd->dip, cmpt, level);
24715cff7825Smh27603
24725cff7825Smh27603 PPMD(D_SETLVL, ("%s: %s change=%d, old %d, new %d, ret %d\n",
24735cff7825Smh27603 str, ppmd->path, change, ppmd->level, level, ret))
24745cff7825Smh27603
24755cff7825Smh27603 if (ret == DDI_SUCCESS) {
24765cff7825Smh27603 ppmd->level = level;
24775cff7825Smh27603 ppmd->rplvl = PM_LEVEL_UNKNOWN;
24785cff7825Smh27603 }
24795cff7825Smh27603
24805cff7825Smh27603 return (ret);
24815cff7825Smh27603 }
24825cff7825Smh27603
24835cff7825Smh27603
24845cff7825Smh27603 static int
ppm_change_power_level(ppm_dev_t * ppmd,int cmpt,int level)24855cff7825Smh27603 ppm_change_power_level(ppm_dev_t *ppmd, int cmpt, int level)
24865cff7825Smh27603 {
24875cff7825Smh27603 return (ppm_set_level(ppmd, cmpt, level, B_TRUE));
24885cff7825Smh27603 }
24895cff7825Smh27603
24905cff7825Smh27603
24915cff7825Smh27603 static int
ppm_record_level_change(ppm_dev_t * ppmd,int cmpt,int level)24925cff7825Smh27603 ppm_record_level_change(ppm_dev_t *ppmd, int cmpt, int level)
24935cff7825Smh27603 {
24945cff7825Smh27603 return (ppm_set_level(ppmd, cmpt, level, B_FALSE));
24955cff7825Smh27603 }
24965cff7825Smh27603
24975cff7825Smh27603
24985cff7825Smh27603 static void
ppm_manage_led(int action)24995cff7825Smh27603 ppm_manage_led(int action)
25005cff7825Smh27603 {
25015cff7825Smh27603 ppm_domain_t *domp;
25025cff7825Smh27603 ppm_unit_t *unitp;
25035cff7825Smh27603 timeout_id_t tid;
25045cff7825Smh27603
25055cff7825Smh27603
25065cff7825Smh27603 PPMD(D_LED, ("ppm_manage_led: action: %s\n",
25075cff7825Smh27603 (action == PPM_LED_BLINKING) ? "PPM_LED_BLINKING" :
25085cff7825Smh27603 "PPM_LED_SOLIDON"))
25095cff7825Smh27603
25105cff7825Smh27603 /*
25115cff7825Smh27603 * test whether led operation is practically supported,
25125cff7825Smh27603 * if not, we waive without pressing for reasons
25135cff7825Smh27603 */
25145cff7825Smh27603 if (!ppm_lookup_dc(NULL, PPMDC_LED_ON))
25155cff7825Smh27603 return;
25165cff7825Smh27603
25175cff7825Smh27603 unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
25185cff7825Smh27603 for (domp = ppm_domain_p; (domp && (domp->model != PPMD_LED)); )
25195cff7825Smh27603 domp = domp->next;
25205cff7825Smh27603
25215cff7825Smh27603 mutex_enter(&unitp->lock);
25225cff7825Smh27603 if (action == PPM_LED_BLINKING) {
25235cff7825Smh27603 ppm_set_led(domp, PPMD_OFF);
25245cff7825Smh27603 unitp->led_tid = timeout(
25255cff7825Smh27603 ppm_blink_led, domp, PPM_LEDOFF_INTERVAL);
25265cff7825Smh27603
25275cff7825Smh27603 } else { /* PPM_LED_SOLIDON */
25285cff7825Smh27603 ASSERT(action == PPM_LED_SOLIDON);
25295cff7825Smh27603 tid = unitp->led_tid;
25305cff7825Smh27603 unitp->led_tid = 0;
25315cff7825Smh27603
25325cff7825Smh27603 mutex_exit(&unitp->lock);
25335cff7825Smh27603 (void) untimeout(tid);
25345cff7825Smh27603
25355cff7825Smh27603 mutex_enter(&unitp->lock);
25365cff7825Smh27603 ppm_set_led(domp, PPMD_ON);
25375cff7825Smh27603 }
25385cff7825Smh27603 mutex_exit(&unitp->lock);
25395cff7825Smh27603 }
25405cff7825Smh27603
25415cff7825Smh27603
25425cff7825Smh27603 static void
ppm_set_led(ppm_domain_t * domp,int val)25435cff7825Smh27603 ppm_set_led(ppm_domain_t *domp, int val)
25445cff7825Smh27603 {
25455cff7825Smh27603 int ret;
25465cff7825Smh27603
25475cff7825Smh27603 ret = ppm_gpioset(domp,
25485cff7825Smh27603 (val == PPMD_ON) ? PPMDC_LED_ON : PPMDC_LED_OFF);
25495cff7825Smh27603
25505cff7825Smh27603 PPMD(D_LED, ("ppm_set_led: %s LED from %s\n",
25515cff7825Smh27603 (ret == 0) ? "turned" : "FAILed to turn",
25525cff7825Smh27603 (domp->status == PPMD_ON) ? "ON to OFF" : "OFF to ON"))
25535cff7825Smh27603
25545cff7825Smh27603 if (ret == DDI_SUCCESS)
25555cff7825Smh27603 domp->status = val;
25565cff7825Smh27603 }
25575cff7825Smh27603
25585cff7825Smh27603
25595cff7825Smh27603 static void
ppm_blink_led(void * arg)25605cff7825Smh27603 ppm_blink_led(void *arg)
25615cff7825Smh27603 {
25625cff7825Smh27603 ppm_unit_t *unitp;
25635cff7825Smh27603 clock_t intvl;
25645cff7825Smh27603 ppm_domain_t *domp = arg;
25655cff7825Smh27603
25665cff7825Smh27603 unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
25675cff7825Smh27603
25685cff7825Smh27603 mutex_enter(&unitp->lock);
25695cff7825Smh27603 if (unitp->led_tid == 0) {
25705cff7825Smh27603 mutex_exit(&unitp->lock);
25715cff7825Smh27603 return;
25725cff7825Smh27603 }
25735cff7825Smh27603
25745cff7825Smh27603 if (domp->status == PPMD_ON) {
25755cff7825Smh27603 ppm_set_led(domp, PPMD_OFF);
25765cff7825Smh27603 intvl = PPM_LEDOFF_INTERVAL;
25775cff7825Smh27603 } else {
25785cff7825Smh27603 ppm_set_led(domp, PPMD_ON);
25795cff7825Smh27603 intvl = PPM_LEDON_INTERVAL;
25805cff7825Smh27603 }
25815cff7825Smh27603
25825cff7825Smh27603 unitp->led_tid = timeout(ppm_blink_led, domp, intvl);
25835cff7825Smh27603 mutex_exit(&unitp->lock);
25845cff7825Smh27603 }
25855cff7825Smh27603
25865cff7825Smh27603 /*
25875cff7825Smh27603 * Function to power up a domain, if required. It also increments the
25885cff7825Smh27603 * domain pwr_cnt to prevent it from going down.
25895cff7825Smh27603 */
25905cff7825Smh27603 static int
ppm_power_up_domain(dev_info_t * dip)25915cff7825Smh27603 ppm_power_up_domain(dev_info_t *dip)
25925cff7825Smh27603 {
25935cff7825Smh27603 int ret = DDI_SUCCESS;
25945cff7825Smh27603 ppm_domain_t *domp;
25955cff7825Smh27603 char *str = "ppm_power_up_domain";
25965cff7825Smh27603
25975cff7825Smh27603 domp = ppm_lookup_dev(dip);
25985cff7825Smh27603 ASSERT(domp);
25995cff7825Smh27603 mutex_enter(&domp->lock);
26005cff7825Smh27603 switch (domp->model) {
26015cff7825Smh27603 case PPMD_FET:
26025cff7825Smh27603 if (domp->status == PPMD_OFF) {
26035cff7825Smh27603 if ((ret = ppm_fetset(domp, PPMD_ON)) ==
26045cff7825Smh27603 DDI_SUCCESS) {
26055cff7825Smh27603 PPMD(D_FET, ("%s: turned on fet for %s@%s\n",
26065cff7825Smh27603 str, PM_NAME(dip), PM_ADDR(dip)))
26075cff7825Smh27603 } else {
26085cff7825Smh27603 PPMD(D_FET, ("%s: couldn't turn on fet "
26095cff7825Smh27603 "for %s@%s\n", str, PM_NAME(dip),
26105cff7825Smh27603 PM_ADDR(dip)))
26115cff7825Smh27603 }
26125cff7825Smh27603 }
26135cff7825Smh27603 break;
26145cff7825Smh27603
26155cff7825Smh27603 case PPMD_PCI:
26165cff7825Smh27603 case PPMD_PCI_PROP:
26175cff7825Smh27603 if (domp->status == PPMD_OFF) {
26185cff7825Smh27603 if ((ret = ppm_switch_clock(domp, PPMD_ON)) ==
26195cff7825Smh27603 DDI_SUCCESS) {
26205cff7825Smh27603 PPMD(D_PCI, ("%s: turned on clock for "
26215cff7825Smh27603 "%s@%s\n", str, PM_NAME(dip),
26225cff7825Smh27603 PM_ADDR(dip)))
26235cff7825Smh27603 } else {
26245cff7825Smh27603 PPMD(D_PCI, ("%s: couldn't turn on clock "
26255cff7825Smh27603 "for %s@%s\n", str, PM_NAME(dip),
26265cff7825Smh27603 PM_ADDR(dip)))
26275cff7825Smh27603 }
26285cff7825Smh27603 }
26295cff7825Smh27603 break;
26305cff7825Smh27603
26315cff7825Smh27603 case PPMD_PCIE:
26325cff7825Smh27603 if (domp->status == PPMD_OFF) {
26335cff7825Smh27603 if ((ret = ppm_pcie_pwr(domp, PPMD_ON)) ==
26345cff7825Smh27603 DDI_SUCCESS) {
26355cff7825Smh27603 PPMD(D_PCI, ("%s: turned on link for "
26365cff7825Smh27603 "%s@%s\n", str, PM_NAME(dip),
26375cff7825Smh27603 PM_ADDR(dip)))
26385cff7825Smh27603 } else {
26395cff7825Smh27603 PPMD(D_PCI, ("%s: couldn't turn on link "
26405cff7825Smh27603 "for %s@%s\n", str, PM_NAME(dip),
26415cff7825Smh27603 PM_ADDR(dip)))
26425cff7825Smh27603 }
26435cff7825Smh27603 }
26445cff7825Smh27603 break;
26455cff7825Smh27603
26465cff7825Smh27603 default:
26475cff7825Smh27603 break;
26485cff7825Smh27603 }
26495cff7825Smh27603 if (ret == DDI_SUCCESS)
26505cff7825Smh27603 domp->pwr_cnt++;
26515cff7825Smh27603 mutex_exit(&domp->lock);
26525cff7825Smh27603 return (ret);
26535cff7825Smh27603 }
26545cff7825Smh27603
26555cff7825Smh27603 /*
26565cff7825Smh27603 * Decrements the domain pwr_cnt. if conditions to power down the domain
26575cff7825Smh27603 * are met, powers down the domain,.
26585cff7825Smh27603 */
26595cff7825Smh27603 static int
ppm_power_down_domain(dev_info_t * dip)26605cff7825Smh27603 ppm_power_down_domain(dev_info_t *dip)
26615cff7825Smh27603 {
26625cff7825Smh27603 int ret = DDI_SUCCESS;
26635cff7825Smh27603 char *str = "ppm_power_down_domain";
26645cff7825Smh27603 ppm_domain_t *domp;
26655cff7825Smh27603
26665cff7825Smh27603 domp = ppm_lookup_dev(dip);
26675cff7825Smh27603 ASSERT(domp);
26685cff7825Smh27603 mutex_enter(&domp->lock);
26695cff7825Smh27603 ASSERT(domp->pwr_cnt > 0);
26705cff7825Smh27603 domp->pwr_cnt--;
26715cff7825Smh27603 switch (domp->model) {
26725cff7825Smh27603 case PPMD_FET:
26735cff7825Smh27603 if ((domp->pwr_cnt == 0) &&
26745cff7825Smh27603 (ppm_cpr_window_flag == B_FALSE) &&
26755cff7825Smh27603 ppm_none_else_holds_power(domp)) {
26765cff7825Smh27603 if ((ret = ppm_fetset(domp, PPMD_OFF)) ==
26775cff7825Smh27603 DDI_SUCCESS) {
26785cff7825Smh27603 PPMD(D_FET, ("%s: turned off FET for %s@%s \n",
26795cff7825Smh27603 str, PM_NAME(dip), PM_ADDR(dip)))
26805cff7825Smh27603 } else {
26815cff7825Smh27603 PPMD(D_FET, ("%s: couldn't turn off FET for "
26825cff7825Smh27603 " %s@%s\n", str, PM_NAME(dip),
26835cff7825Smh27603 PM_ADDR(dip)))
26845cff7825Smh27603 }
26855cff7825Smh27603 }
26865cff7825Smh27603 break;
26875cff7825Smh27603
26885cff7825Smh27603 case PPMD_PCI:
26895cff7825Smh27603 case PPMD_PCI_PROP:
26905cff7825Smh27603 if ((domp->pwr_cnt == 0) &&
26915cff7825Smh27603 (ppm_cpr_window_flag == B_FALSE) &&
26925cff7825Smh27603 ppm_none_else_holds_power(domp)) {
26935cff7825Smh27603 if ((ret = ppm_switch_clock(domp, PPMD_OFF)) ==
26945cff7825Smh27603 DDI_SUCCESS) {
26955cff7825Smh27603 PPMD(D_PCI, ("%s: turned off clock for %s@%s\n",
26965cff7825Smh27603 str, PM_NAME(dip), PM_ADDR(dip)))
26975cff7825Smh27603 } else {
26985cff7825Smh27603 PPMD(D_PCI, ("%s: couldn't turn off clock "
26995cff7825Smh27603 "for %s@%s\n", str, PM_NAME(dip),
27005cff7825Smh27603 PM_ADDR(dip)))
27015cff7825Smh27603 }
27025cff7825Smh27603 }
27035cff7825Smh27603 break;
27045cff7825Smh27603
27055cff7825Smh27603 case PPMD_PCIE:
27065cff7825Smh27603 if ((domp->pwr_cnt == 0) &&
27075cff7825Smh27603 (ppm_cpr_window_flag == B_FALSE) &&
27085cff7825Smh27603 ppm_none_else_holds_power(domp)) {
27095cff7825Smh27603 if ((ret = ppm_pcie_pwr(domp, PPMD_OFF)) ==
27105cff7825Smh27603 DDI_SUCCESS) {
27115cff7825Smh27603 PPMD(D_PCI, ("%s: turned off link for %s@%s\n",
27125cff7825Smh27603 str, PM_NAME(dip), PM_ADDR(dip)))
27135cff7825Smh27603 } else {
27145cff7825Smh27603 PPMD(D_PCI, ("%s: couldn't turn off link "
27155cff7825Smh27603 "for %s@%s\n", str, PM_NAME(dip),
27165cff7825Smh27603 PM_ADDR(dip)))
27175cff7825Smh27603 }
27185cff7825Smh27603 }
27195cff7825Smh27603 break;
27205cff7825Smh27603
27215cff7825Smh27603 default:
27225cff7825Smh27603 break;
27235cff7825Smh27603 }
27245cff7825Smh27603 mutex_exit(&domp->lock);
27255cff7825Smh27603 return (ret);
27265cff7825Smh27603 }
27272df1fe9cSrandyf
27282df1fe9cSrandyf static int
ppm_manage_sx(s3a_t * s3ap,int enter)27292df1fe9cSrandyf ppm_manage_sx(s3a_t *s3ap, int enter)
27302df1fe9cSrandyf {
27312df1fe9cSrandyf ppm_domain_t *domp = ppm_lookup_domain("domain_estar");
27322df1fe9cSrandyf ppm_dc_t *dc;
27332df1fe9cSrandyf int ret = 0;
27342df1fe9cSrandyf
27352df1fe9cSrandyf if (domp == NULL) {
27362df1fe9cSrandyf PPMD(D_CPR, ("ppm_manage_sx: can't find estar domain\n"))
27372df1fe9cSrandyf return (ENODEV);
27382df1fe9cSrandyf }
27392df1fe9cSrandyf PPMD(D_CPR, ("ppm_manage_sx %x, enter %d\n", s3ap->s3a_state,
27402df1fe9cSrandyf enter))
27412df1fe9cSrandyf switch (s3ap->s3a_state) {
27422df1fe9cSrandyf case S3:
27432df1fe9cSrandyf if (enter) {
27442df1fe9cSrandyf dc = ppm_lookup_dc(domp, PPMDC_ENTER_S3);
27452df1fe9cSrandyf } else {
27462df1fe9cSrandyf dc = ppm_lookup_dc(domp, PPMDC_EXIT_S3);
27472df1fe9cSrandyf }
27482df1fe9cSrandyf ASSERT(dc && dc->method == PPMDC_KIO);
27492df1fe9cSrandyf PPMD(D_CPR,
27502df1fe9cSrandyf ("ppm_manage_sx: calling acpi driver (handle %p)"
27512df1fe9cSrandyf " with %x\n", (void *)dc->lh, dc->m_un.kio.iowr))
27522df1fe9cSrandyf ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr,
27532df1fe9cSrandyf (intptr_t)s3ap, FWRITE | FKIOCTL, kcred, NULL);
27542df1fe9cSrandyf break;
27552df1fe9cSrandyf
27562df1fe9cSrandyf case S4:
27572df1fe9cSrandyf /* S4 is not supported yet */
27582df1fe9cSrandyf return (EINVAL);
27592df1fe9cSrandyf default:
27602df1fe9cSrandyf ASSERT(0);
27612df1fe9cSrandyf }
27622df1fe9cSrandyf return (ret);
27632df1fe9cSrandyf }
27642df1fe9cSrandyf
27652df1fe9cSrandyf /*
27662df1fe9cSrandyf * Search enable/disable lists, which are encoded in ppm.conf as an array
27672df1fe9cSrandyf * of char strings.
27682df1fe9cSrandyf */
27692df1fe9cSrandyf static int
ppm_search_list(pm_searchargs_t * sl)27702df1fe9cSrandyf ppm_search_list(pm_searchargs_t *sl)
27712df1fe9cSrandyf {
27722df1fe9cSrandyf int i;
27732df1fe9cSrandyf int flags = DDI_PROP_DONTPASS;
27742df1fe9cSrandyf ppm_unit_t *unitp = ddi_get_soft_state(ppm_statep, ppm_inst);
27752df1fe9cSrandyf char **pp;
27762df1fe9cSrandyf char *starp;
27772df1fe9cSrandyf uint_t nelements;
27782df1fe9cSrandyf char *manuf = sl->pms_manufacturer;
27792df1fe9cSrandyf char *prod = sl->pms_product;
27802df1fe9cSrandyf
27812df1fe9cSrandyf if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, unitp->dip, flags,
27822df1fe9cSrandyf sl->pms_listname, &pp, &nelements) != DDI_PROP_SUCCESS) {
27832df1fe9cSrandyf PPMD(D_CPR, ("ppm_search_list prop lookup %s failed--EINVAL\n",
27842df1fe9cSrandyf sl->pms_listname))
27852df1fe9cSrandyf return (EINVAL);
27862df1fe9cSrandyf }
27872df1fe9cSrandyf ASSERT((nelements & 1) == 0); /* must be even */
27882df1fe9cSrandyf
27892df1fe9cSrandyf PPMD(D_CPR, ("ppm_search_list looking for %s, %s\n", manuf, prod))
27902df1fe9cSrandyf
27912df1fe9cSrandyf for (i = 0; i < nelements; i += 2) {
27922df1fe9cSrandyf PPMD(D_CPR, ("checking %s, %s", pp[i], pp[i+1]))
27932df1fe9cSrandyf /* we support only a trailing '*' pattern match */
27942df1fe9cSrandyf if ((starp = strchr(pp[i], '*')) != NULL && *(starp + 1) == 0) {
27952df1fe9cSrandyf /* LINTED - ptrdiff overflow */
27962df1fe9cSrandyf if (strncmp(manuf, pp[i], (starp - pp[i])) != 0) {
27972df1fe9cSrandyf PPMD(D_CPR, (" no match %s with %s\n",
27982df1fe9cSrandyf manuf, pp[i + 1]))
27992df1fe9cSrandyf continue;
28002df1fe9cSrandyf }
28012df1fe9cSrandyf }
28022df1fe9cSrandyf if ((starp = strchr(pp[i + 1], '*')) != NULL &&
28032df1fe9cSrandyf *(starp + 1) == 0) {
28042df1fe9cSrandyf if (strncmp(prod,
28052df1fe9cSrandyf /* LINTED - ptrdiff overflow */
28062df1fe9cSrandyf pp[i + 1], (starp - pp[i + 1])) != 0) {
28072df1fe9cSrandyf PPMD(D_CPR, (" no match %s with %s\n",
28082df1fe9cSrandyf prod, pp[i + 1]))
28092df1fe9cSrandyf continue;
28102df1fe9cSrandyf }
28112df1fe9cSrandyf }
28122df1fe9cSrandyf if (strcmp(manuf, pp[i]) == 0 &&
28132df1fe9cSrandyf (strcmp(prod, pp[i + 1]) == 0)) {
28142df1fe9cSrandyf PPMD(D_CPR, (" match\n"))
28152df1fe9cSrandyf ddi_prop_free(pp);
28162df1fe9cSrandyf return (0);
28172df1fe9cSrandyf }
28182df1fe9cSrandyf PPMD(D_CPR, (" no match %s with %s or %s with %s\n",
28192df1fe9cSrandyf manuf, pp[i], prod, pp[i + 1]))
28202df1fe9cSrandyf }
28212df1fe9cSrandyf ddi_prop_free(pp);
28222df1fe9cSrandyf return (ENODEV);
28232df1fe9cSrandyf }
2824