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 */ 25*444f66e7SMark Haywood /* 26*444f66e7SMark Haywood * Copyright (c) 2009, Intel Corporation. 27*444f66e7SMark Haywood * All Rights Reserved. 28*444f66e7SMark 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 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 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 2395cff7825Smh27603 _info(struct modinfo *modinfop) 2405cff7825Smh27603 { 2415cff7825Smh27603 return (mod_info(&modlinkage, modinfop)); 2425cff7825Smh27603 } 2435cff7825Smh27603 2445cff7825Smh27603 2455cff7825Smh27603 /* ARGSUSED */ 2465cff7825Smh27603 int 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 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 359*444f66e7SMark Haywood * the device tree, ppm CPU domains can be allocated using ACPI 3605cff7825Smh27603 * data. 3615cff7825Smh27603 */ 362*444f66e7SMark Haywood cpupm_ppm_alloc_pstate_domains = ppm_alloc_pstate_domains; 363*444f66e7SMark 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 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 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 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 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 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 7705cff7825Smh27603 #ifdef DEBUG 7715cff7825Smh27603 char *str = "ppm_ctlops"; 7725cff7825Smh27603 int mask = ppm_debug & (D_CTLOPS1 | D_CTLOPS2); 7735cff7825Smh27603 char *ctlstr = ppm_get_ctlstr(reqp->request_type, mask); 7745cff7825Smh27603 if (mask && ctlstr) 7755cff7825Smh27603 PPMD(mask, ("%s: %s, %s\n", 7765cff7825Smh27603 str, ddi_binding_name(rdip), ctlstr)) 7775cff7825Smh27603 #endif 7785cff7825Smh27603 7792df1fe9cSrandyf if (ctlop != DDI_CTLOPS_POWER) { 7805cff7825Smh27603 return (DDI_FAILURE); 7812df1fe9cSrandyf } 7825cff7825Smh27603 7835cff7825Smh27603 unitp = (ppm_unit_t *)ddi_get_soft_state(ppm_statep, ppm_inst); 7845cff7825Smh27603 7855cff7825Smh27603 switch (reqp->request_type) { 7865cff7825Smh27603 7875cff7825Smh27603 /* attempt to blink led if indeed all at lowest */ 7885cff7825Smh27603 case PMR_PPM_ALL_LOWEST: 7895cff7825Smh27603 mode = (reqp->req.ppm_all_lowest_req.mode == PM_ALL_LOWEST); 7905cff7825Smh27603 if (!(unitp->states & PPM_STATE_SUSPENDED) && mode) 7915cff7825Smh27603 ppm_manage_led(PPM_LED_BLINKING); 7925cff7825Smh27603 else 7935cff7825Smh27603 ppm_manage_led(PPM_LED_SOLIDON); 7945cff7825Smh27603 return (DDI_SUCCESS); 7955cff7825Smh27603 7965cff7825Smh27603 /* undo the claiming of 'rdip' at attach time */ 7975cff7825Smh27603 case PMR_PPM_POST_DETACH: 7985cff7825Smh27603 ASSERT(reqp->req.ppm_set_power_req.who == rdip); 7995cff7825Smh27603 mutex_enter(&unitp->lock); 8005cff7825Smh27603 if (reqp->req.ppm_config_req.result != DDI_SUCCESS || 8015cff7825Smh27603 (PPM_GET_PRIVATE(rdip) == NULL)) { 8025cff7825Smh27603 mutex_exit(&unitp->lock); 8035cff7825Smh27603 return (DDI_FAILURE); 8045cff7825Smh27603 } 8055cff7825Smh27603 mutex_exit(&unitp->lock); 8065cff7825Smh27603 ppm_rem_dev(rdip); 8075cff7825Smh27603 return (DDI_SUCCESS); 8085cff7825Smh27603 8095cff7825Smh27603 /* chance to adjust pwr_cnt if resume is about to power up rdip */ 8105cff7825Smh27603 case PMR_PPM_PRE_RESUME: 8115cff7825Smh27603 ppm_svc_resume_ctlop(rdip, reqp); 8125cff7825Smh27603 return (DDI_SUCCESS); 8135cff7825Smh27603 8145cff7825Smh27603 /* 8155cff7825Smh27603 * synchronizing, so that only the owner of the power lock is 8165cff7825Smh27603 * permitted to change device and component's power level. 8175cff7825Smh27603 */ 8185cff7825Smh27603 case PMR_PPM_UNLOCK_POWER: 8195cff7825Smh27603 case PMR_PPM_TRY_LOCK_POWER: 8205cff7825Smh27603 case PMR_PPM_LOCK_POWER: 8215cff7825Smh27603 ppmd = PPM_GET_PRIVATE(rdip); 8225cff7825Smh27603 if (ppmd) 8235cff7825Smh27603 domp = ppmd->domp; 8245cff7825Smh27603 else if (reqp->request_type != PMR_PPM_UNLOCK_POWER) { 8255cff7825Smh27603 domp = ppm_lookup_dev(rdip); 8265cff7825Smh27603 ASSERT(domp); 8275cff7825Smh27603 ppmd = ppm_get_dev(rdip, domp); 8285cff7825Smh27603 } 8295cff7825Smh27603 8305cff7825Smh27603 PPMD(D_LOCKS, ("ppm_lock_%s: %s, %s\n", 8315cff7825Smh27603 (domp->dflags & PPMD_LOCK_ALL) ? "all" : "one", 8325cff7825Smh27603 ppmd->path, ppm_get_ctlstr(reqp->request_type, D_LOCKS))) 8335cff7825Smh27603 8345cff7825Smh27603 if (domp->dflags & PPMD_LOCK_ALL) 8355cff7825Smh27603 ppm_lock_all(domp, reqp, result); 8365cff7825Smh27603 else 8375cff7825Smh27603 ppm_lock_one(ppmd, reqp, result); 8385cff7825Smh27603 return (DDI_SUCCESS); 8395cff7825Smh27603 8405cff7825Smh27603 case PMR_PPM_POWER_LOCK_OWNER: 8415cff7825Smh27603 ASSERT(reqp->req.ppm_power_lock_owner_req.who == rdip); 8425cff7825Smh27603 ppmd = PPM_GET_PRIVATE(rdip); 8435cff7825Smh27603 if (ppmd) 8445cff7825Smh27603 domp = ppmd->domp; 8455cff7825Smh27603 else { 8465cff7825Smh27603 domp = ppm_lookup_dev(rdip); 8475cff7825Smh27603 ASSERT(domp); 8485cff7825Smh27603 ppmd = ppm_get_dev(rdip, domp); 8495cff7825Smh27603 } 8505cff7825Smh27603 8515cff7825Smh27603 /* 8525cff7825Smh27603 * In case of LOCK_ALL, effective owner of the power lock 8535cff7825Smh27603 * is the owner of the domain lock. otherwise, it is the owner 8545cff7825Smh27603 * of the power lock. 8555cff7825Smh27603 */ 8565cff7825Smh27603 if (domp->dflags & PPMD_LOCK_ALL) 8575cff7825Smh27603 reqp->req.ppm_power_lock_owner_req.owner = 8585cff7825Smh27603 mutex_owner(&domp->lock); 8595cff7825Smh27603 else { 8605cff7825Smh27603 reqp->req.ppm_power_lock_owner_req.owner = 8615cff7825Smh27603 DEVI(rdip)->devi_busy_thread; 8625cff7825Smh27603 } 8635cff7825Smh27603 return (DDI_SUCCESS); 8645cff7825Smh27603 8655cff7825Smh27603 case PMR_PPM_INIT_CHILD: 8665cff7825Smh27603 ASSERT(reqp->req.ppm_lock_power_req.who == rdip); 8675cff7825Smh27603 if ((domp = ppm_lookup_dev(rdip)) == NULL) 8685cff7825Smh27603 return (DDI_SUCCESS); 8695cff7825Smh27603 8705cff7825Smh27603 /* 8715cff7825Smh27603 * We keep track of power-manageable devices starting with 8725cff7825Smh27603 * initialization process. The initializing flag remains 8735cff7825Smh27603 * set until it is cleared by ppm_add_dev(). Power management 8745cff7825Smh27603 * policy for some domains are affected even during device 8755cff7825Smh27603 * initialization. For example, PCI domains should leave 8765cff7825Smh27603 * their clock running meanwhile a device in that domain 8775cff7825Smh27603 * is initializing. 8785cff7825Smh27603 */ 8795cff7825Smh27603 mutex_enter(&domp->lock); 8805cff7825Smh27603 owned = ppm_add_owned(rdip, domp); 8815cff7825Smh27603 ASSERT(owned->initializing == 0); 8825cff7825Smh27603 owned->initializing = 1; 8835cff7825Smh27603 8845cff7825Smh27603 if (PPMD_IS_PCI(domp->model) && domp->status == PPMD_OFF) { 8855cff7825Smh27603 ret = ppm_switch_clock(domp, PPMD_ON); 8865cff7825Smh27603 if (ret == DDI_SUCCESS) 8875cff7825Smh27603 domp->dflags |= PPMD_INITCHILD_CLKON; 8885cff7825Smh27603 } 8895cff7825Smh27603 mutex_exit(&domp->lock); 8905cff7825Smh27603 return (ret); 8915cff7825Smh27603 8925cff7825Smh27603 case PMR_PPM_POST_ATTACH: 8935cff7825Smh27603 ASSERT(reqp->req.ppm_config_req.who == rdip); 8945cff7825Smh27603 domp = ppm_lookup_dev(rdip); 8955cff7825Smh27603 ASSERT(domp); 8965cff7825Smh27603 ASSERT(domp->status == PPMD_ON); 8975cff7825Smh27603 if (reqp->req.ppm_config_req.result == DDI_SUCCESS) { 8985cff7825Smh27603 /* 8995cff7825Smh27603 * call ppm_get_dev, which will increment the 9005cff7825Smh27603 * domain power count by the right number. 9015cff7825Smh27603 * Undo the power count increment, done in PRE_PROBE. 9025cff7825Smh27603 */ 9035cff7825Smh27603 if (PM_GET_PM_INFO(rdip)) 9045cff7825Smh27603 ppmd = ppm_get_dev(rdip, domp); 9055cff7825Smh27603 mutex_enter(&domp->lock); 9065cff7825Smh27603 ASSERT(domp->pwr_cnt > 0); 9075cff7825Smh27603 domp->pwr_cnt--; 9085cff7825Smh27603 mutex_exit(&domp->lock); 9095cff7825Smh27603 return (DDI_SUCCESS); 9105cff7825Smh27603 } 9115cff7825Smh27603 9125cff7825Smh27603 ret = ppm_power_down_domain(rdip); 9135cff7825Smh27603 /* FALLTHROUGH */ 9145cff7825Smh27603 case PMR_PPM_UNINIT_CHILD: 9155cff7825Smh27603 ASSERT(reqp->req.ppm_lock_power_req.who == rdip); 9165cff7825Smh27603 if ((domp = ppm_lookup_dev(rdip)) == NULL) 9175cff7825Smh27603 return (DDI_SUCCESS); 9185cff7825Smh27603 9195cff7825Smh27603 (void) ddi_pathname(rdip, path); 9205cff7825Smh27603 mutex_enter(&domp->lock); 9215cff7825Smh27603 for (owned = domp->owned; owned; owned = owned->next) 9225cff7825Smh27603 if (strcmp(owned->path, path) == 0) 9235cff7825Smh27603 break; 9245cff7825Smh27603 9255cff7825Smh27603 /* 9265cff7825Smh27603 * In case we didn't go through a complete attach and detach, 9275cff7825Smh27603 * the initializing flag will still be set, so clear it. 9285cff7825Smh27603 */ 9295cff7825Smh27603 if ((owned != NULL) && (owned->initializing)) 9305cff7825Smh27603 owned->initializing = 0; 9315cff7825Smh27603 9325cff7825Smh27603 if (PPMD_IS_PCI(domp->model) && 9335cff7825Smh27603 domp->status == PPMD_ON && domp->pwr_cnt == 0 && 9345cff7825Smh27603 (domp->dflags & PPMD_INITCHILD_CLKON) && 9355cff7825Smh27603 ppm_none_else_holds_power(domp)) { 9365cff7825Smh27603 ret = ppm_switch_clock(domp, PPMD_OFF); 9375cff7825Smh27603 if (ret == DDI_SUCCESS) 9385cff7825Smh27603 domp->dflags &= ~PPMD_INITCHILD_CLKON; 9395cff7825Smh27603 } 9405cff7825Smh27603 mutex_exit(&domp->lock); 9415cff7825Smh27603 return (ret); 9425cff7825Smh27603 9435cff7825Smh27603 /* place holders */ 9445cff7825Smh27603 case PMR_PPM_UNMANAGE: 9455cff7825Smh27603 case PMR_PPM_PRE_DETACH: 9465cff7825Smh27603 return (DDI_SUCCESS); 9475cff7825Smh27603 9485cff7825Smh27603 case PMR_PPM_PRE_PROBE: 9495cff7825Smh27603 ASSERT(reqp->req.ppm_config_req.who == rdip); 9505cff7825Smh27603 return (ppm_power_up_domain(rdip)); 9515cff7825Smh27603 9525cff7825Smh27603 case PMR_PPM_POST_PROBE: 9535cff7825Smh27603 ASSERT(reqp->req.ppm_config_req.who == rdip); 9545cff7825Smh27603 if (reqp->req.ppm_config_req.result == DDI_PROBE_SUCCESS || 9555cff7825Smh27603 reqp->req.ppm_config_req.result == DDI_PROBE_DONTCARE) 9565cff7825Smh27603 return (DDI_SUCCESS); 9575cff7825Smh27603 9585cff7825Smh27603 /* Probe failed */ 9595cff7825Smh27603 PPMD(D_CTLOPS1 | D_CTLOPS2, ("%s: probe failed for %s@%s " 9605cff7825Smh27603 "rv %d\n", str, PM_NAME(rdip), PM_ADDR(rdip), 9615cff7825Smh27603 reqp->req.ppm_config_req.result)) 9625cff7825Smh27603 return (ppm_power_down_domain(rdip)); 9635cff7825Smh27603 9645cff7825Smh27603 case PMR_PPM_PRE_ATTACH: 9655cff7825Smh27603 ASSERT(reqp->req.ppm_config_req.who == rdip); 9665cff7825Smh27603 /* Domain has already been powered up in PRE_PROBE */ 9675cff7825Smh27603 domp = ppm_lookup_dev(rdip); 9685cff7825Smh27603 ASSERT(domp); 9695cff7825Smh27603 ASSERT(domp->status == PPMD_ON); 9705cff7825Smh27603 return (DDI_SUCCESS); 9715cff7825Smh27603 9725cff7825Smh27603 /* ppm intercepts power change process to the claimed devices */ 9735cff7825Smh27603 case PMR_PPM_SET_POWER: 9745cff7825Smh27603 case PMR_PPM_POWER_CHANGE_NOTIFY: 9755cff7825Smh27603 if ((ppmd = PPM_GET_PRIVATE(rdip)) == NULL) { 9765cff7825Smh27603 domp = ppm_lookup_dev(rdip); 9775cff7825Smh27603 ASSERT(domp); 9785cff7825Smh27603 ppmd = ppm_get_dev(rdip, domp); 9795cff7825Smh27603 } 9805cff7825Smh27603 switch (ppmd->domp->model) { 9815cff7825Smh27603 case PPMD_CPU: 9825cff7825Smh27603 return (ppm_manage_cpus(rdip, reqp, result)); 9835cff7825Smh27603 case PPMD_FET: 9845cff7825Smh27603 return (ppm_manage_fet(rdip, reqp, result)); 9855cff7825Smh27603 case PPMD_PCI: 9865cff7825Smh27603 case PPMD_PCI_PROP: 9875cff7825Smh27603 return (ppm_manage_pci(rdip, reqp, result)); 9885cff7825Smh27603 case PPMD_PCIE: 9895cff7825Smh27603 return (ppm_manage_pcie(rdip, reqp, result)); 9905cff7825Smh27603 default: 9915cff7825Smh27603 cmn_err(CE_WARN, "ppm_ctlops: domain model %d does" 9925cff7825Smh27603 " not support PMR_PPM_SET_POWER ctlop", 9935cff7825Smh27603 ppmd->domp->model); 9945cff7825Smh27603 return (DDI_FAILURE); 9955cff7825Smh27603 } 9965cff7825Smh27603 9972df1fe9cSrandyf case PMR_PPM_ENTER_SX: 9982df1fe9cSrandyf case PMR_PPM_EXIT_SX: 9992df1fe9cSrandyf s3args.s3a_state = reqp->req.ppm_power_enter_sx_req.sx_state; 10002df1fe9cSrandyf s3args.s3a_test_point = 10012df1fe9cSrandyf reqp->req.ppm_power_enter_sx_req.test_point; 10022df1fe9cSrandyf s3args.s3a_wakephys = reqp->req.ppm_power_enter_sx_req.wakephys; 10032df1fe9cSrandyf s3args.s3a_psr = reqp->req.ppm_power_enter_sx_req.psr; 10042df1fe9cSrandyf ret = ppm_manage_sx(&s3args, 10052df1fe9cSrandyf reqp->request_type == PMR_PPM_ENTER_SX); 10062df1fe9cSrandyf if (ret) { 10072df1fe9cSrandyf PPMD(D_CPR, ("ppm_manage_sx returns %d\n", ret)) 10082df1fe9cSrandyf return (DDI_FAILURE); 10092df1fe9cSrandyf } else { 10102df1fe9cSrandyf return (DDI_SUCCESS); 10112df1fe9cSrandyf } 10122df1fe9cSrandyf 10132df1fe9cSrandyf case PMR_PPM_SEARCH_LIST: 10142df1fe9cSrandyf ret = ppm_search_list(reqp->req.ppm_search_list_req.searchlist); 10152df1fe9cSrandyf reqp->req.ppm_search_list_req.result = ret; 10162df1fe9cSrandyf *res = ret; 10172df1fe9cSrandyf if (ret) { 10182df1fe9cSrandyf PPMD(D_CPR, ("ppm_search_list returns %d\n", ret)) 10192df1fe9cSrandyf return (DDI_FAILURE); 10202df1fe9cSrandyf } else { 10212df1fe9cSrandyf PPMD(D_CPR, ("ppm_search_list returns %d\n", ret)) 10222df1fe9cSrandyf return (DDI_SUCCESS); 10232df1fe9cSrandyf } 10242df1fe9cSrandyf 10255cff7825Smh27603 default: 10265cff7825Smh27603 cmn_err(CE_WARN, "ppm_ctlops: unrecognized ctlops req(%d)", 10275cff7825Smh27603 reqp->request_type); 10285cff7825Smh27603 return (DDI_FAILURE); 10295cff7825Smh27603 } 10305cff7825Smh27603 } 10315cff7825Smh27603 10325cff7825Smh27603 10335cff7825Smh27603 /* 10345cff7825Smh27603 * Raise the power level of a subrange of cpus. Used when cpu driver 10355cff7825Smh27603 * failed an attempt to lower the power of a cpu (probably because 10365cff7825Smh27603 * it got busy). Need to revert the ones we already changed. 10375cff7825Smh27603 * 10385cff7825Smh27603 * ecpup = the ppm_dev_t for the cpu which failed to lower power 10395cff7825Smh27603 * level = power level to reset prior cpus to 10405cff7825Smh27603 */ 10415cff7825Smh27603 int 10425cff7825Smh27603 ppm_revert_cpu_power(ppm_dev_t *ecpup, int level) 10435cff7825Smh27603 { 10445cff7825Smh27603 ppm_dev_t *cpup; 10455cff7825Smh27603 int ret = DDI_SUCCESS; 10465cff7825Smh27603 10475cff7825Smh27603 for (cpup = ecpup->domp->devlist; cpup != ecpup; cpup = cpup->next) { 10485cff7825Smh27603 PPMD(D_CPU, ("ppm_revert_cpu_power: \"%s\", revert to " 10495cff7825Smh27603 "level %d\n", cpup->path, level)) 10505cff7825Smh27603 10515cff7825Smh27603 ret = pm_power(cpup->dip, 0, level); 10525cff7825Smh27603 if (ret == DDI_SUCCESS) { 10535cff7825Smh27603 cpup->level = level; 10545cff7825Smh27603 cpup->rplvl = PM_LEVEL_UNKNOWN; 10555cff7825Smh27603 } 10565cff7825Smh27603 } 10575cff7825Smh27603 return (ret); 10585cff7825Smh27603 } 10595cff7825Smh27603 10605cff7825Smh27603 10615cff7825Smh27603 /* 10625cff7825Smh27603 * ppm_manage_cpus - Process a request to change the power level of a cpu. 10635cff7825Smh27603 * If not all cpus want to be at the same level, OR if we are currently 10645cff7825Smh27603 * refusing slowdown requests due to thermal stress, we cache the request. 10655cff7825Smh27603 * Otherwise, set all cpus to the new power level. 10665cff7825Smh27603 */ 10675cff7825Smh27603 /* ARGSUSED */ 10685cff7825Smh27603 static int 10695cff7825Smh27603 ppm_manage_cpus(dev_info_t *dip, power_req_t *reqp, int *result) 10705cff7825Smh27603 { 10715cff7825Smh27603 #ifdef DEBUG 10725cff7825Smh27603 char *str = "ppm_manage_cpus"; 10735cff7825Smh27603 #endif 10745cff7825Smh27603 int old, new, ret, kmflag; 10755cff7825Smh27603 ppm_dev_t *ppmd, *cpup; 10765cff7825Smh27603 int change_notify = 0; 10775cff7825Smh27603 pm_ppm_devlist_t *devlist = NULL, *p; 10785cff7825Smh27603 int do_rescan = 0; 10795cff7825Smh27603 10805cff7825Smh27603 *result = DDI_SUCCESS; 10815cff7825Smh27603 10825cff7825Smh27603 switch (reqp->request_type) { 10835cff7825Smh27603 case PMR_PPM_SET_POWER: 10845cff7825Smh27603 break; 10855cff7825Smh27603 10865cff7825Smh27603 case PMR_PPM_POWER_CHANGE_NOTIFY: 10875cff7825Smh27603 change_notify = 1; 10885cff7825Smh27603 break; 10895cff7825Smh27603 10905cff7825Smh27603 default: 10915cff7825Smh27603 return (DDI_FAILURE); 10925cff7825Smh27603 } 10935cff7825Smh27603 10945cff7825Smh27603 ppmd = PPM_GET_PRIVATE(dip); 10955cff7825Smh27603 ASSERT(MUTEX_HELD(&ppmd->domp->lock)); 10965cff7825Smh27603 old = reqp->req.ppm_set_power_req.old_level; 10975cff7825Smh27603 new = reqp->req.ppm_set_power_req.new_level; 10985cff7825Smh27603 10995cff7825Smh27603 if (change_notify) { 11005cff7825Smh27603 ppmd->level = new; 11015cff7825Smh27603 ppmd->rplvl = PM_LEVEL_UNKNOWN; 11025cff7825Smh27603 11035cff7825Smh27603 PPMD(D_CPU, ("%s: Notify cpu dip %p power level has changed " 11045cff7825Smh27603 "from %d to %d", str, (void *)dip, old, new)) 11055cff7825Smh27603 return (DDI_SUCCESS); 11065cff7825Smh27603 } 11075cff7825Smh27603 11085cff7825Smh27603 if (ppm_manage_early_cpus(dip, new, result)) 11095cff7825Smh27603 return (*result); 11105cff7825Smh27603 11115cff7825Smh27603 if (new == ppmd->level) { 11125cff7825Smh27603 PPMD(D_CPU, ("%s: already at power level %d\n", str, new)) 11135cff7825Smh27603 return (DDI_SUCCESS); 11145cff7825Smh27603 } 11155cff7825Smh27603 11165cff7825Smh27603 /* 11175cff7825Smh27603 * A request from lower to higher level transition is granted and 11185cff7825Smh27603 * made effective on all cpus. A request from higher to lower must 11195cff7825Smh27603 * be agreed upon by all cpus. 11205cff7825Smh27603 */ 11215cff7825Smh27603 ppmd->rplvl = new; 11225cff7825Smh27603 for (cpup = ppmd->domp->devlist; cpup; cpup = cpup->next) { 11235cff7825Smh27603 if (cpup->rplvl == new) 11245cff7825Smh27603 continue; 11255cff7825Smh27603 11265cff7825Smh27603 if (new < old) { 11275cff7825Smh27603 PPMD(D_SOME, ("%s: not all cpus wants to be at new " 11285cff7825Smh27603 "level %d yet.\n", str, new)) 11295cff7825Smh27603 return (DDI_SUCCESS); 11305cff7825Smh27603 } 11315cff7825Smh27603 11325cff7825Smh27603 /* 11335cff7825Smh27603 * If a single cpu requests power up, honor the request 11345cff7825Smh27603 * powering up all cpus. 11355cff7825Smh27603 */ 11365cff7825Smh27603 if (new > old) { 11375cff7825Smh27603 PPMD(D_SOME, ("%s: powering up device(%s@%s, %p) " 11385cff7825Smh27603 "because of request from dip(%s@%s, %p), " 11395cff7825Smh27603 "need pm_rescan\n", str, PM_NAME(cpup->dip), 11405cff7825Smh27603 PM_ADDR(cpup->dip), (void *)cpup->dip, 11415cff7825Smh27603 PM_NAME(dip), PM_ADDR(dip), (void *)dip)) 11425cff7825Smh27603 do_rescan++; 11435cff7825Smh27603 } 11445cff7825Smh27603 } 11455cff7825Smh27603 11465cff7825Smh27603 PPMD(D_SETLVL, ("%s: \"%s\" set power level old %d, new %d \n", 11475cff7825Smh27603 str, ppmd->path, ppmd->level, new)) 11485cff7825Smh27603 ret = ppm_change_cpu_power(ppmd, new); 11495cff7825Smh27603 *result = ret; 11505cff7825Smh27603 11515cff7825Smh27603 if (ret == DDI_SUCCESS) { 11525cff7825Smh27603 if (reqp->req.ppm_set_power_req.canblock == PM_CANBLOCK_BLOCK) 11535cff7825Smh27603 kmflag = KM_SLEEP; 11545cff7825Smh27603 else 11555cff7825Smh27603 kmflag = KM_NOSLEEP; 11565cff7825Smh27603 11575cff7825Smh27603 for (cpup = ppmd->domp->devlist; cpup; cpup = cpup->next) { 11585cff7825Smh27603 if (cpup->dip == dip) 11595cff7825Smh27603 continue; 11605cff7825Smh27603 11615cff7825Smh27603 if ((p = kmem_zalloc(sizeof (pm_ppm_devlist_t), 11625cff7825Smh27603 kmflag)) == NULL) { 11635cff7825Smh27603 break; 11645cff7825Smh27603 } 11655cff7825Smh27603 p->ppd_who = cpup->dip; 11665cff7825Smh27603 p->ppd_cmpt = cpup->cmpt; 11675cff7825Smh27603 p->ppd_old_level = old; 11685cff7825Smh27603 p->ppd_new_level = new; 11695cff7825Smh27603 p->ppd_next = devlist; 11705cff7825Smh27603 11715cff7825Smh27603 PPMD(D_SETLVL, ("%s: devlist entry[\"%s\"] %d -> %d\n", 11725cff7825Smh27603 str, cpup->path, old, new)) 11735cff7825Smh27603 11745cff7825Smh27603 devlist = p; 11755cff7825Smh27603 } 11765cff7825Smh27603 reqp->req.ppm_set_power_req.cookie = (void *) devlist; 11775cff7825Smh27603 11785cff7825Smh27603 if (do_rescan > 0) { 11795cff7825Smh27603 for (cpup = ppmd->domp->devlist; cpup; 11805cff7825Smh27603 cpup = cpup->next) { 11815cff7825Smh27603 if (cpup->dip == dip) 11825cff7825Smh27603 continue; 11835cff7825Smh27603 pm_rescan(cpup->dip); 11845cff7825Smh27603 } 11855cff7825Smh27603 } 11865cff7825Smh27603 } 11875cff7825Smh27603 11885cff7825Smh27603 return (ret); 11895cff7825Smh27603 } 11905cff7825Smh27603 11915cff7825Smh27603 11925cff7825Smh27603 /* 11935cff7825Smh27603 * ppm_svc_resume_ctlop - this is a small bookkeeping ppm does - 11945cff7825Smh27603 * increments its FET domain power count, in anticipation of that 11955cff7825Smh27603 * the indicated device(dip) would be powered up by its driver as 11965cff7825Smh27603 * a result of cpr resuming. 11975cff7825Smh27603 */ 11985cff7825Smh27603 /* ARGSUSED */ 11995cff7825Smh27603 static void 12005cff7825Smh27603 ppm_svc_resume_ctlop(dev_info_t *dip, power_req_t *reqp) 12015cff7825Smh27603 { 12025cff7825Smh27603 ppm_domain_t *domp; 12035cff7825Smh27603 ppm_dev_t *ppmd; 12045cff7825Smh27603 int powered; /* power up count per dip */ 12055cff7825Smh27603 12065cff7825Smh27603 ppmd = PPM_GET_PRIVATE(dip); 12075cff7825Smh27603 if (ppmd == NULL) 12085cff7825Smh27603 return; 12095cff7825Smh27603 12105cff7825Smh27603 /* 12115cff7825Smh27603 * Maintain correct powered count for domain which cares 12125cff7825Smh27603 */ 12135cff7825Smh27603 powered = 0; 12145cff7825Smh27603 domp = ppmd->domp; 12155cff7825Smh27603 mutex_enter(&domp->lock); 12165cff7825Smh27603 if ((domp->model == PPMD_FET) || PPMD_IS_PCI(domp->model) || 12175cff7825Smh27603 (domp->model == PPMD_PCIE)) { 12185cff7825Smh27603 for (ppmd = domp->devlist; ppmd; ppmd = ppmd->next) { 12195cff7825Smh27603 if (ppmd->dip == dip && ppmd->level) 12205cff7825Smh27603 powered++; 12215cff7825Smh27603 } 12225cff7825Smh27603 12235cff7825Smh27603 /* 12245cff7825Smh27603 * All fets and clocks are held on during suspend - 12255cff7825Smh27603 * resume window regardless their domain devices' power 12265cff7825Smh27603 * level. 12275cff7825Smh27603 */ 12285cff7825Smh27603 ASSERT(domp->status == PPMD_ON); 12295cff7825Smh27603 12305cff7825Smh27603 /* 12315cff7825Smh27603 * The difference indicates the number of components 12325cff7825Smh27603 * being off prior to suspend operation, that is the 12335cff7825Smh27603 * amount needs to be compensated in order to sync up 12345cff7825Smh27603 * bookkeeping with reality, for PROM reset would have 12355cff7825Smh27603 * brought up all devices. 12365cff7825Smh27603 */ 12375cff7825Smh27603 if (powered < PM_NUMCMPTS(dip)) 12385cff7825Smh27603 domp->pwr_cnt += PM_NUMCMPTS(dip) - powered; 12395cff7825Smh27603 } 12405cff7825Smh27603 for (ppmd = domp->devlist; ppmd; ppmd = ppmd->next) { 12415cff7825Smh27603 if (ppmd->dip == dip) 12425cff7825Smh27603 ppmd->level = ppmd->rplvl = PM_LEVEL_UNKNOWN; 12435cff7825Smh27603 } 12445cff7825Smh27603 mutex_exit(&domp->lock); 12455cff7825Smh27603 } 12465cff7825Smh27603 12475cff7825Smh27603 #ifdef DEBUG 12485cff7825Smh27603 static int ppmbringup = 0; 12495cff7825Smh27603 #endif 12505cff7825Smh27603 12515cff7825Smh27603 int 12525cff7825Smh27603 ppm_bringup_domains() 12535cff7825Smh27603 { 12545cff7825Smh27603 #ifdef DEBUG 12555cff7825Smh27603 char *str = "ppm_bringup_domains"; 12565cff7825Smh27603 #endif 12575cff7825Smh27603 ppm_domain_t *domp; 12585cff7825Smh27603 int ret = DDI_SUCCESS; 12595cff7825Smh27603 12605cff7825Smh27603 PPMD(D_CPR, ("%s[%d]: enter\n", str, ++ppmbringup)) 12615cff7825Smh27603 for (domp = ppm_domain_p; domp; domp = domp->next) { 12625cff7825Smh27603 if ((!PPMD_IS_PCI(domp->model) && (domp->model != PPMD_FET) && 12635cff7825Smh27603 (domp->model != PPMD_PCIE)) || (domp->devlist == NULL)) 12645cff7825Smh27603 continue; 12655cff7825Smh27603 12665cff7825Smh27603 mutex_enter(&domp->lock); 12675cff7825Smh27603 if (domp->status == PPMD_ON) { 12685cff7825Smh27603 mutex_exit(&domp->lock); 12695cff7825Smh27603 continue; 12705cff7825Smh27603 } 12715cff7825Smh27603 switch (domp->model) { 12725cff7825Smh27603 case PPMD_FET: 12735cff7825Smh27603 ret = ppm_fetset(domp, PPMD_ON); 12745cff7825Smh27603 break; 12755cff7825Smh27603 case PPMD_PCI: 12765cff7825Smh27603 case PPMD_PCI_PROP: 12775cff7825Smh27603 ret = ppm_switch_clock(domp, PPMD_ON); 12785cff7825Smh27603 break; 12795cff7825Smh27603 case PPMD_PCIE: 12805cff7825Smh27603 ret = ppm_pcie_pwr(domp, PPMD_ON); 12815cff7825Smh27603 break; 12825cff7825Smh27603 default: 12835cff7825Smh27603 break; 12845cff7825Smh27603 } 12855cff7825Smh27603 mutex_exit(&domp->lock); 12865cff7825Smh27603 } 12872df1fe9cSrandyf PPMD(D_CPR, ("%s[%d]: exit\n", str, ppmbringup)) 12885cff7825Smh27603 12895cff7825Smh27603 return (ret); 12905cff7825Smh27603 } 12915cff7825Smh27603 12925cff7825Smh27603 #ifdef DEBUG 12935cff7825Smh27603 static int ppmsyncbp = 0; 12945cff7825Smh27603 #endif 12955cff7825Smh27603 12965cff7825Smh27603 int 12975cff7825Smh27603 ppm_sync_bookkeeping() 12985cff7825Smh27603 { 12995cff7825Smh27603 #ifdef DEBUG 13005cff7825Smh27603 char *str = "ppm_sync_bookkeeping"; 13015cff7825Smh27603 #endif 13025cff7825Smh27603 ppm_domain_t *domp; 13035cff7825Smh27603 int ret = DDI_SUCCESS; 13045cff7825Smh27603 13055cff7825Smh27603 PPMD(D_CPR, ("%s[%d]: enter\n", str, ++ppmsyncbp)) 13065cff7825Smh27603 for (domp = ppm_domain_p; domp; domp = domp->next) { 13075cff7825Smh27603 if ((!PPMD_IS_PCI(domp->model) && (domp->model != PPMD_FET) && 13085cff7825Smh27603 (domp->model != PPMD_PCIE)) || (domp->devlist == NULL)) 13095cff7825Smh27603 continue; 13105cff7825Smh27603 13115cff7825Smh27603 mutex_enter(&domp->lock); 13125cff7825Smh27603 if ((domp->pwr_cnt != 0) || !ppm_none_else_holds_power(domp)) { 13135cff7825Smh27603 mutex_exit(&domp->lock); 13145cff7825Smh27603 continue; 13155cff7825Smh27603 } 13162df1fe9cSrandyf 13172df1fe9cSrandyf /* 13182df1fe9cSrandyf * skip NULL .devlist slot, for some may host pci device 13192df1fe9cSrandyf * that can not tolerate clock off or not even participate 13202df1fe9cSrandyf * in PM. 13212df1fe9cSrandyf */ 13222df1fe9cSrandyf if (domp->devlist == NULL) 13232df1fe9cSrandyf continue; 13242df1fe9cSrandyf 13255cff7825Smh27603 switch (domp->model) { 13265cff7825Smh27603 case PPMD_FET: 13275cff7825Smh27603 ret = ppm_fetset(domp, PPMD_OFF); 13285cff7825Smh27603 break; 13295cff7825Smh27603 case PPMD_PCI: 13305cff7825Smh27603 case PPMD_PCI_PROP: 13315cff7825Smh27603 ret = ppm_switch_clock(domp, PPMD_OFF); 13325cff7825Smh27603 break; 13335cff7825Smh27603 case PPMD_PCIE: 13345cff7825Smh27603 ret = ppm_pcie_pwr(domp, PPMD_OFF); 13355cff7825Smh27603 break; 13365cff7825Smh27603 default: 13375cff7825Smh27603 break; 13385cff7825Smh27603 } 13395cff7825Smh27603 mutex_exit(&domp->lock); 13405cff7825Smh27603 } 13412df1fe9cSrandyf PPMD(D_CPR, ("%s[%d]: exit\n", str, ppmsyncbp)) 13425cff7825Smh27603 13435cff7825Smh27603 return (ret); 13445cff7825Smh27603 } 13455cff7825Smh27603 13465cff7825Smh27603 13475cff7825Smh27603 13485cff7825Smh27603 /* 13495cff7825Smh27603 * pre-suspend window; 13505cff7825Smh27603 * 13515cff7825Smh27603 * power up every FET and PCI clock that are off; 13525cff7825Smh27603 * 13535cff7825Smh27603 * set ppm_cpr_window global flag to indicate 13545cff7825Smh27603 * that even though all pm_scan requested power transitions 13555cff7825Smh27603 * will be honored as usual but that until we're out 13565cff7825Smh27603 * of this window, no FET or clock will be turned off 13575cff7825Smh27603 * for domains with pwr_cnt decremented down to 0. 13585cff7825Smh27603 * Such is to avoid accessing the orthogonal drivers that own 13595cff7825Smh27603 * the FET and clock registers that may not be resumed yet. 13605cff7825Smh27603 * 13615cff7825Smh27603 * at post-resume window, walk through each FET and PCI domains, 13625cff7825Smh27603 * bring pwr_cnt and domp->status to sense: if pwr-cnt == 0, 13635cff7825Smh27603 * and noinvol check okays, power down the FET or PCI. At last, 13645cff7825Smh27603 * clear the global flag ppm_cpr_window. 13655cff7825Smh27603 * 13665cff7825Smh27603 * ASSERT case 1, during cpr window, checks pwr_cnt against power 13675cff7825Smh27603 * transitions; 13685cff7825Smh27603 * ASSERT case 2, out of cpr window, checks four things: 13695cff7825Smh27603 * pwr_cnt <> power transition in/out of 0 13705cff7825Smh27603 * <> status <> record of noinvol device detached 13715cff7825Smh27603 * 13725cff7825Smh27603 */ 13735cff7825Smh27603 /* ARGSUSED */ 13745cff7825Smh27603 static boolean_t 13755cff7825Smh27603 ppm_cpr_callb(void *arg, int code) 13765cff7825Smh27603 { 13775cff7825Smh27603 int ret; 13785cff7825Smh27603 13795cff7825Smh27603 switch (code) { 13805cff7825Smh27603 case CB_CODE_CPR_CHKPT: 13815cff7825Smh27603 13825cff7825Smh27603 /* pre-suspend: start of cpr window */ 13835cff7825Smh27603 mutex_enter(&ppm_cpr_window_lock); 13845cff7825Smh27603 ASSERT(ppm_cpr_window_flag == B_FALSE); 13855cff7825Smh27603 ppm_cpr_window_flag = B_TRUE; 13865cff7825Smh27603 mutex_exit(&ppm_cpr_window_lock); 13875cff7825Smh27603 13885cff7825Smh27603 ret = ppm_bringup_domains(); 13895cff7825Smh27603 13905cff7825Smh27603 break; 13915cff7825Smh27603 13925cff7825Smh27603 case CB_CODE_CPR_RESUME: 13935cff7825Smh27603 13945cff7825Smh27603 /* post-resume: end of cpr window */ 13955cff7825Smh27603 ret = ppm_sync_bookkeeping(); 13965cff7825Smh27603 13975cff7825Smh27603 mutex_enter(&ppm_cpr_window_lock); 13985cff7825Smh27603 ASSERT(ppm_cpr_window_flag == B_TRUE); 13995cff7825Smh27603 ppm_cpr_window_flag = B_FALSE; 14005cff7825Smh27603 mutex_exit(&ppm_cpr_window_lock); 14015cff7825Smh27603 14025cff7825Smh27603 break; 14035cff7825Smh27603 } 14045cff7825Smh27603 14055cff7825Smh27603 return (ret == DDI_SUCCESS); 14065cff7825Smh27603 } 14075cff7825Smh27603 14085cff7825Smh27603 14095cff7825Smh27603 /* 14105cff7825Smh27603 * Initialize our private version of real power level 14115cff7825Smh27603 * as well as lowest and highest levels the device supports; 14125cff7825Smh27603 * relate to ppm_add_dev 14135cff7825Smh27603 */ 14145cff7825Smh27603 void 14155cff7825Smh27603 ppm_dev_init(ppm_dev_t *ppmd) 14165cff7825Smh27603 { 14175cff7825Smh27603 struct pm_component *dcomps; 14185cff7825Smh27603 struct pm_comp *pm_comp; 14195cff7825Smh27603 dev_info_t *dip; 14205cff7825Smh27603 int maxi, i; 14215cff7825Smh27603 14225cff7825Smh27603 ASSERT(MUTEX_HELD(&ppmd->domp->lock)); 14235cff7825Smh27603 ppmd->level = PM_LEVEL_UNKNOWN; 14245cff7825Smh27603 ppmd->rplvl = PM_LEVEL_UNKNOWN; 14255cff7825Smh27603 14265cff7825Smh27603 /* increment pwr_cnt per component */ 14275cff7825Smh27603 if ((ppmd->domp->model == PPMD_FET) || 14285cff7825Smh27603 PPMD_IS_PCI(ppmd->domp->model) || 14295cff7825Smh27603 (ppmd->domp->model == PPMD_PCIE)) 14305cff7825Smh27603 ppmd->domp->pwr_cnt++; 14315cff7825Smh27603 14325cff7825Smh27603 dip = ppmd->dip; 14335cff7825Smh27603 14345cff7825Smh27603 /* 14355cff7825Smh27603 * ppm exists to handle power-manageable devices which require 14365cff7825Smh27603 * special handling on the current platform. However, a 14375cff7825Smh27603 * driver for such a device may choose not to support power 14385cff7825Smh27603 * management on a particular load/attach. In this case we 14395cff7825Smh27603 * we create a structure to represent a single-component device 14405cff7825Smh27603 * for which "level" = PM_LEVEL_UNKNOWN and "lowest" = 0 14415cff7825Smh27603 * are effectively constant. 14425cff7825Smh27603 */ 14435cff7825Smh27603 if (PM_GET_PM_INFO(dip)) { 14445cff7825Smh27603 dcomps = DEVI(dip)->devi_pm_components; 14455cff7825Smh27603 pm_comp = &dcomps[ppmd->cmpt].pmc_comp; 14465cff7825Smh27603 14475cff7825Smh27603 ppmd->lowest = pm_comp->pmc_lvals[0]; 14485cff7825Smh27603 ASSERT(ppmd->lowest >= 0); 14495cff7825Smh27603 maxi = pm_comp->pmc_numlevels - 1; 14505cff7825Smh27603 ppmd->highest = pm_comp->pmc_lvals[maxi]; 14515cff7825Smh27603 14525cff7825Smh27603 /* 14535cff7825Smh27603 * If 66mhz PCI device on pci 66mhz bus supports D2 state 14545cff7825Smh27603 * (config reg PMC bit 10 set), ppm could turn off its bus 14555cff7825Smh27603 * clock once it is at D3hot. 14565cff7825Smh27603 */ 14575cff7825Smh27603 if (ppmd->domp->dflags & PPMD_PCI66MHZ) { 14585cff7825Smh27603 for (i = 0; i < maxi; i++) 14595cff7825Smh27603 if (pm_comp->pmc_lvals[i] == PM_LEVEL_D2) { 14605cff7825Smh27603 ppmd->flags |= PPMDEV_PCI66_D2; 14615cff7825Smh27603 break; 14625cff7825Smh27603 } 14635cff7825Smh27603 } 14645cff7825Smh27603 } 14655cff7825Smh27603 14665cff7825Smh27603 /* 14675cff7825Smh27603 * If device is in PCI_PROP domain and has exported the 14685cff7825Smh27603 * property listed in ppm.conf, its clock will be turned 14695cff7825Smh27603 * off when all pm'able devices in that domain are at D3. 14705cff7825Smh27603 */ 14715cff7825Smh27603 if ((ppmd->domp->model == PPMD_PCI_PROP) && 14725cff7825Smh27603 (ppmd->domp->propname != NULL) && 14735cff7825Smh27603 ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 14745cff7825Smh27603 ppmd->domp->propname)) 14755cff7825Smh27603 ppmd->flags |= PPMDEV_PCI_PROP_CLKPM; 14765cff7825Smh27603 } 14775cff7825Smh27603 14785cff7825Smh27603 14795cff7825Smh27603 /* 14805cff7825Smh27603 * relate to ppm_rem_dev 14815cff7825Smh27603 */ 14825cff7825Smh27603 void 14835cff7825Smh27603 ppm_dev_fini(ppm_dev_t *ppmd) 14845cff7825Smh27603 { 14855cff7825Smh27603 ASSERT(MUTEX_HELD(&ppmd->domp->lock)); 14865cff7825Smh27603 14875cff7825Smh27603 /* decrement pwr_cnt per component */ 14885cff7825Smh27603 if ((ppmd->domp->model == PPMD_FET) || 14895cff7825Smh27603 PPMD_IS_PCI(ppmd->domp->model) || 14905cff7825Smh27603 (ppmd->domp->model == PPMD_PCIE)) 14915cff7825Smh27603 if (ppmd->level != ppmd->lowest) 14925cff7825Smh27603 ppmd->domp->pwr_cnt--; 14935cff7825Smh27603 } 14945cff7825Smh27603 14955cff7825Smh27603 /* 14965cff7825Smh27603 * Each power fet controls the power of one or more platform 14975cff7825Smh27603 * device(s) within their domain. Hence domain devices' power 14985cff7825Smh27603 * level change has been monitored, such that once all devices 14995cff7825Smh27603 * are powered off, the fet is turned off to save more power. 15005cff7825Smh27603 * 15015cff7825Smh27603 * To power on any domain device, the domain power fet 15025cff7825Smh27603 * needs to be turned on first. always one fet per domain. 15035cff7825Smh27603 */ 15045cff7825Smh27603 static int 15055cff7825Smh27603 ppm_manage_fet(dev_info_t *dip, power_req_t *reqp, int *result) 15065cff7825Smh27603 { 15075cff7825Smh27603 #ifdef DEBUG 15085cff7825Smh27603 char *str = "ppm_manage_fet"; 15095cff7825Smh27603 #endif 15105cff7825Smh27603 int (*pwr_func)(ppm_dev_t *, int, int); 15115cff7825Smh27603 int new, old, cmpt; 15125cff7825Smh27603 ppm_dev_t *ppmd; 15135cff7825Smh27603 ppm_domain_t *domp; 15145cff7825Smh27603 int incr = 0; 15155cff7825Smh27603 int dummy_ret; 15165cff7825Smh27603 15175cff7825Smh27603 15185cff7825Smh27603 *result = DDI_SUCCESS; 15195cff7825Smh27603 switch (reqp->request_type) { 15205cff7825Smh27603 case PMR_PPM_SET_POWER: 15215cff7825Smh27603 pwr_func = ppm_change_power_level; 15225cff7825Smh27603 old = reqp->req.ppm_set_power_req.old_level; 15235cff7825Smh27603 new = reqp->req.ppm_set_power_req.new_level; 15245cff7825Smh27603 cmpt = reqp->req.ppm_set_power_req.cmpt; 15255cff7825Smh27603 break; 15265cff7825Smh27603 case PMR_PPM_POWER_CHANGE_NOTIFY: 15275cff7825Smh27603 pwr_func = ppm_record_level_change; 15285cff7825Smh27603 old = reqp->req.ppm_notify_level_req.old_level; 15295cff7825Smh27603 new = reqp->req.ppm_notify_level_req.new_level; 15305cff7825Smh27603 cmpt = reqp->req.ppm_notify_level_req.cmpt; 15315cff7825Smh27603 break; 15325cff7825Smh27603 default: 15335cff7825Smh27603 *result = DDI_FAILURE; 15345cff7825Smh27603 PPMD(D_FET, ("%s: unknown request type %d for %s@%s\n", 15355cff7825Smh27603 str, reqp->request_type, PM_NAME(dip), PM_ADDR(dip))) 15365cff7825Smh27603 return (DDI_FAILURE); 15375cff7825Smh27603 } 15385cff7825Smh27603 15395cff7825Smh27603 for (ppmd = PPM_GET_PRIVATE(dip); ppmd; ppmd = ppmd->next) 15405cff7825Smh27603 if (cmpt == ppmd->cmpt) 15415cff7825Smh27603 break; 15425cff7825Smh27603 if (!ppmd) { 15435cff7825Smh27603 PPMD(D_FET, ("%s: dip(%p): old(%d)->new(%d): no ppm_dev" 15445cff7825Smh27603 " found for cmpt(%d)", str, (void *)dip, old, new, cmpt)) 15455cff7825Smh27603 *result = DDI_FAILURE; 15465cff7825Smh27603 return (DDI_FAILURE); 15475cff7825Smh27603 } 15485cff7825Smh27603 domp = ppmd->domp; 15495cff7825Smh27603 PPMD(D_FET, ("%s: %s@%s %s old %d, new %d, c%d, level %d, " 15505cff7825Smh27603 "status %s\n", str, PM_NAME(dip), PM_ADDR(dip), 15515cff7825Smh27603 ppm_get_ctlstr(reqp->request_type, ~0), old, new, cmpt, 15525cff7825Smh27603 ppmd->level, (domp->status == PPMD_OFF ? "off" : "on"))) 15535cff7825Smh27603 15545cff7825Smh27603 15555cff7825Smh27603 ASSERT(old == ppmd->level); 15565cff7825Smh27603 15575cff7825Smh27603 if (new == ppmd->level) { 15585cff7825Smh27603 PPMD(D_FET, ("nop\n")) 15595cff7825Smh27603 return (DDI_SUCCESS); 15605cff7825Smh27603 } 15615cff7825Smh27603 15625cff7825Smh27603 PPM_LOCK_DOMAIN(domp); 15635cff7825Smh27603 15645cff7825Smh27603 /* 15655cff7825Smh27603 * In general, a device's published lowest power level does not 15665cff7825Smh27603 * have to be 0 if power-off is not tolerated. i.e. a device 15675cff7825Smh27603 * instance may export its lowest level > 0. It is reasonable to 15685cff7825Smh27603 * assume that level 0 indicates off state, positive level values 15695cff7825Smh27603 * indicate power states above off, include full power state. 15705cff7825Smh27603 */ 15715cff7825Smh27603 if (new > 0) { /* device powering up or to different positive level */ 15725cff7825Smh27603 if (domp->status == PPMD_OFF) { 15735cff7825Smh27603 15745cff7825Smh27603 /* can not be in (chpt, resume) window */ 15755cff7825Smh27603 ASSERT(ppm_cpr_window_flag == B_FALSE); 15765cff7825Smh27603 15775cff7825Smh27603 ASSERT(old == 0 && domp->pwr_cnt == 0); 15785cff7825Smh27603 15795cff7825Smh27603 PPMD(D_FET, ("About to turn fet on for %s@%s c%d\n", 15805cff7825Smh27603 PM_NAME(dip), PM_ADDR(dip), cmpt)) 15815cff7825Smh27603 15825cff7825Smh27603 *result = ppm_fetset(domp, PPMD_ON); 15835cff7825Smh27603 if (*result != DDI_SUCCESS) { 15845cff7825Smh27603 PPMD(D_FET, ("\tCan't turn on power FET: " 15855cff7825Smh27603 "ret(%d)\n", *result)) 15865cff7825Smh27603 PPM_UNLOCK_DOMAIN(domp); 15875cff7825Smh27603 return (DDI_FAILURE); 15885cff7825Smh27603 } 15895cff7825Smh27603 } 15905cff7825Smh27603 15915cff7825Smh27603 /* 15925cff7825Smh27603 * If powering up, pre-increment the count before 15935cff7825Smh27603 * calling pwr_func, because we are going to release 15945cff7825Smh27603 * the domain lock and another thread might turn off 15955cff7825Smh27603 * domain power otherwise. 15965cff7825Smh27603 */ 15975cff7825Smh27603 if (old == 0) { 15985cff7825Smh27603 domp->pwr_cnt++; 15995cff7825Smh27603 incr = 1; 16005cff7825Smh27603 } 16015cff7825Smh27603 16025cff7825Smh27603 PPMD(D_FET, ("\t%s domain power count: %d\n", 16035cff7825Smh27603 domp->name, domp->pwr_cnt)) 16045cff7825Smh27603 } 16055cff7825Smh27603 16065cff7825Smh27603 16075cff7825Smh27603 PPM_UNLOCK_DOMAIN(domp); 16085cff7825Smh27603 16095cff7825Smh27603 ASSERT(domp->pwr_cnt > 0); 16105cff7825Smh27603 16115cff7825Smh27603 if ((*result = (*pwr_func)(ppmd, cmpt, new)) != DDI_SUCCESS) { 16125cff7825Smh27603 PPMD(D_FET, ("\t%s power change failed: ret(%d)\n", 16135cff7825Smh27603 ppmd->path, *result)) 16145cff7825Smh27603 } 16155cff7825Smh27603 16165cff7825Smh27603 PPM_LOCK_DOMAIN(domp); 16175cff7825Smh27603 16185cff7825Smh27603 /* 16195cff7825Smh27603 * Decr the power count in two cases: 16205cff7825Smh27603 * 16215cff7825Smh27603 * 1) request was to power device down and was successful 16225cff7825Smh27603 * 2) request was to power up (we pre-incremented count), but failed. 16235cff7825Smh27603 */ 16245cff7825Smh27603 if ((*result == DDI_SUCCESS && ppmd->level == 0) || 16255cff7825Smh27603 (*result != DDI_SUCCESS && incr)) { 16265cff7825Smh27603 ASSERT(domp->pwr_cnt > 0); 16275cff7825Smh27603 domp->pwr_cnt--; 16285cff7825Smh27603 } 16295cff7825Smh27603 16305cff7825Smh27603 PPMD(D_FET, ("\t%s domain power count: %d\n", 16315cff7825Smh27603 domp->name, domp->pwr_cnt)) 16325cff7825Smh27603 16335cff7825Smh27603 /* 16345cff7825Smh27603 * call to pwr_func will update ppm data structures, if it 16355cff7825Smh27603 * succeeds. ppm should return whatever is the return value 16365cff7825Smh27603 * from call to pwr_func. This way pm and ppm data structures 16375cff7825Smh27603 * always in sync. Use dummy_ret from here for any further 16385cff7825Smh27603 * return values. 16395cff7825Smh27603 */ 16405cff7825Smh27603 if ((domp->pwr_cnt == 0) && 16415cff7825Smh27603 (ppm_cpr_window_flag == B_FALSE) && 16425cff7825Smh27603 ppm_none_else_holds_power(domp)) { 16435cff7825Smh27603 16445cff7825Smh27603 PPMD(D_FET, ("About to turn FET off for %s@%s c%d\n", 16455cff7825Smh27603 PM_NAME(dip), PM_ADDR(dip), cmpt)) 16465cff7825Smh27603 16475cff7825Smh27603 dummy_ret = ppm_fetset(domp, PPMD_OFF); 16485cff7825Smh27603 if (dummy_ret != DDI_SUCCESS) { 16495cff7825Smh27603 PPMD(D_FET, ("\tCan't turn off FET: ret(%d)\n", 16505cff7825Smh27603 dummy_ret)) 16515cff7825Smh27603 } 16525cff7825Smh27603 } 16535cff7825Smh27603 16545cff7825Smh27603 PPM_UNLOCK_DOMAIN(domp); 16555cff7825Smh27603 ASSERT(domp->pwr_cnt >= 0); 16565cff7825Smh27603 return (*result); 16575cff7825Smh27603 } 16585cff7825Smh27603 16595cff7825Smh27603 16605cff7825Smh27603 /* 16615cff7825Smh27603 * the actual code that turn on or off domain power fet and 16625cff7825Smh27603 * update domain status 16635cff7825Smh27603 */ 16645cff7825Smh27603 static int 16655cff7825Smh27603 ppm_fetset(ppm_domain_t *domp, uint8_t value) 16665cff7825Smh27603 { 16675cff7825Smh27603 char *str = "ppm_fetset"; 16685cff7825Smh27603 int key; 16695cff7825Smh27603 ppm_dc_t *dc; 16705cff7825Smh27603 int ret; 16715cff7825Smh27603 clock_t temp; 16725cff7825Smh27603 clock_t delay = 0; 16735cff7825Smh27603 16745cff7825Smh27603 key = (value == PPMD_ON) ? PPMDC_FET_ON : PPMDC_FET_OFF; 16755cff7825Smh27603 for (dc = domp->dc; dc; dc = dc->next) 16765cff7825Smh27603 if (dc->cmd == key) 16775cff7825Smh27603 break; 16785cff7825Smh27603 if (!dc || !dc->lh) { 16795cff7825Smh27603 PPMD(D_FET, ("%s: %s domain: NULL ppm_dc handle\n", 16805cff7825Smh27603 str, domp->name)) 16815cff7825Smh27603 return (DDI_FAILURE); 16825cff7825Smh27603 } 16835cff7825Smh27603 16845cff7825Smh27603 if (key == PPMDC_FET_ON) { 16855cff7825Smh27603 PPM_GET_IO_DELAY(dc, delay); 16865cff7825Smh27603 if (delay > 0 && domp->last_off_time > 0) { 16875cff7825Smh27603 /* 16885cff7825Smh27603 * provide any delay required before turning on. 16895cff7825Smh27603 * some devices e.g. Samsung DVD require minimum 16905cff7825Smh27603 * of 1 sec between OFF->ON. no delay is required 16915cff7825Smh27603 * for the first time. 16925cff7825Smh27603 */ 16935cff7825Smh27603 temp = ddi_get_lbolt(); 16945cff7825Smh27603 temp -= domp->last_off_time; 16955cff7825Smh27603 temp = drv_hztousec(temp); 16965cff7825Smh27603 16975cff7825Smh27603 if (temp < delay) { 16985cff7825Smh27603 /* 16995cff7825Smh27603 * busy wait untill we meet the 17005cff7825Smh27603 * required delay. Since we maintain 17015cff7825Smh27603 * time stamps in terms of clock ticks 17025cff7825Smh27603 * we might wait for longer than required 17035cff7825Smh27603 */ 17045cff7825Smh27603 PPMD(D_FET, ("%s : waiting %lu micro seconds " 17055cff7825Smh27603 "before on\n", domp->name, 17062df1fe9cSrandyf delay - temp)); 17075cff7825Smh27603 drv_usecwait(delay - temp); 17085cff7825Smh27603 } 17095cff7825Smh27603 } 17105cff7825Smh27603 } 17115cff7825Smh27603 switch (dc->method) { 17122df1fe9cSrandyf #ifdef sun4u 17135cff7825Smh27603 case PPMDC_I2CKIO: { 17145cff7825Smh27603 i2c_gpio_t i2c_req; 17155cff7825Smh27603 i2c_req.reg_mask = dc->m_un.i2c.mask; 17165cff7825Smh27603 i2c_req.reg_val = dc->m_un.i2c.val; 17175cff7825Smh27603 ret = ldi_ioctl(dc->lh, dc->m_un.i2c.iowr, 17185cff7825Smh27603 (intptr_t)&i2c_req, FWRITE | FKIOCTL, kcred, NULL); 17195cff7825Smh27603 break; 17205cff7825Smh27603 } 17215cff7825Smh27603 #endif 17225cff7825Smh27603 17235cff7825Smh27603 case PPMDC_KIO: 17245cff7825Smh27603 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr, 17255cff7825Smh27603 (intptr_t)&(dc->m_un.kio.val), FWRITE | FKIOCTL, kcred, 17265cff7825Smh27603 NULL); 17275cff7825Smh27603 break; 17285cff7825Smh27603 17295cff7825Smh27603 default: 17305cff7825Smh27603 PPMD(D_FET, ("\t%s: unsupported domain control method %d\n", 17315cff7825Smh27603 str, domp->dc->method)) 17325cff7825Smh27603 return (DDI_FAILURE); 17335cff7825Smh27603 } 17345cff7825Smh27603 17355cff7825Smh27603 PPMD(D_FET, ("%s: %s domain(%s) FET from %s to %s\n", str, 17365cff7825Smh27603 (ret == 0) ? "turned" : "failed to turn", 17375cff7825Smh27603 domp->name, 17385cff7825Smh27603 (domp->status == PPMD_ON) ? "ON" : "OFF", 17395cff7825Smh27603 (value == PPMD_ON) ? "ON" : "OFF")) 17405cff7825Smh27603 17415cff7825Smh27603 if (ret == DDI_SUCCESS) { 17425cff7825Smh27603 domp->status = value; 17435cff7825Smh27603 17445cff7825Smh27603 if (key == PPMDC_FET_OFF) 17455cff7825Smh27603 /* 17465cff7825Smh27603 * record the time, when it is off. time is recorded 17475cff7825Smh27603 * in clock ticks 17485cff7825Smh27603 */ 17495cff7825Smh27603 domp->last_off_time = ddi_get_lbolt(); 17505cff7825Smh27603 17515cff7825Smh27603 /* implement any post op delay. */ 17525cff7825Smh27603 if (key == PPMDC_FET_ON) { 17531159df0bSmh27603 PPM_GET_IO_POST_DELAY(dc, delay); 17545cff7825Smh27603 PPMD(D_FET, ("%s : waiting %lu micro seconds " 17555cff7825Smh27603 "after on\n", domp->name, delay)) 17565cff7825Smh27603 if (delay > 0) 17575cff7825Smh27603 drv_usecwait(delay); 17585cff7825Smh27603 } 17595cff7825Smh27603 } 17605cff7825Smh27603 17615cff7825Smh27603 return (ret); 17625cff7825Smh27603 } 17635cff7825Smh27603 17645cff7825Smh27603 17655cff7825Smh27603 /* 17665cff7825Smh27603 * read power fet status 17675cff7825Smh27603 */ 17685cff7825Smh27603 static int 17695cff7825Smh27603 ppm_fetget(ppm_domain_t *domp, uint8_t *lvl) 17705cff7825Smh27603 { 17715cff7825Smh27603 char *str = "ppm_fetget"; 17725cff7825Smh27603 ppm_dc_t *dc = domp->dc; 17735cff7825Smh27603 uint_t kio_val; 17745cff7825Smh27603 int off_val; 17755cff7825Smh27603 int ret; 17765cff7825Smh27603 17775cff7825Smh27603 if (!dc->lh) { 17785cff7825Smh27603 PPMD(D_FET, ("%s: %s domain NULL ppm_dc layered handle\n", 17795cff7825Smh27603 str, domp->name)) 17805cff7825Smh27603 return (DDI_FAILURE); 17815cff7825Smh27603 } 17825cff7825Smh27603 if (!dc->next) { 17835cff7825Smh27603 cmn_err(CE_WARN, "%s: expect both fet on and fet off ops " 17845cff7825Smh27603 "defined, found only one in domain(%s)", str, domp->name); 17855cff7825Smh27603 return (DDI_FAILURE); 17865cff7825Smh27603 } 17875cff7825Smh27603 17885cff7825Smh27603 switch (dc->method) { 17892df1fe9cSrandyf #ifdef sun4u 17905cff7825Smh27603 case PPMDC_I2CKIO: { 17915cff7825Smh27603 i2c_gpio_t i2c_req; 17925cff7825Smh27603 i2c_req.reg_mask = dc->m_un.i2c.mask; 17935cff7825Smh27603 ret = ldi_ioctl(dc->lh, dc->m_un.i2c.iord, 17945cff7825Smh27603 (intptr_t)&i2c_req, FWRITE | FKIOCTL, kcred, NULL); 17955cff7825Smh27603 17965cff7825Smh27603 if (ret) { 17975cff7825Smh27603 PPMD(D_FET, ("%s: PPMDC_I2CKIO failed: ret(%d)\n", 17985cff7825Smh27603 str, ret)) 17995cff7825Smh27603 return (ret); 18005cff7825Smh27603 } 18015cff7825Smh27603 18025cff7825Smh27603 off_val = (dc->cmd == PPMDC_FET_OFF) ? dc->m_un.i2c.val : 18035cff7825Smh27603 dc->next->m_un.i2c.val; 18045cff7825Smh27603 *lvl = (i2c_req.reg_val == off_val) ? PPMD_OFF : PPMD_ON; 18055cff7825Smh27603 18065cff7825Smh27603 PPMD(D_FET, ("%s: %s domain FET %s\n", str, domp->name, 18075cff7825Smh27603 (i2c_req.reg_val == off_val) ? "OFF" : "ON")) 18085cff7825Smh27603 18095cff7825Smh27603 break; 18105cff7825Smh27603 } 18115cff7825Smh27603 #endif 18125cff7825Smh27603 18135cff7825Smh27603 case PPMDC_KIO: 18145cff7825Smh27603 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iord, 18155cff7825Smh27603 (intptr_t)&kio_val, FWRITE | FKIOCTL, kcred, NULL); 18165cff7825Smh27603 if (ret) { 18175cff7825Smh27603 PPMD(D_FET, ("%s: PPMDC_KIO failed: ret(%d)\n", 18185cff7825Smh27603 str, ret)) 18195cff7825Smh27603 return (ret); 18205cff7825Smh27603 } 18215cff7825Smh27603 18225cff7825Smh27603 off_val = (dc->cmd == PPMDC_FET_OFF) ? dc->m_un.kio.val : 18235cff7825Smh27603 dc->next->m_un.kio.val; 18245cff7825Smh27603 *lvl = (kio_val == off_val) ? PPMD_OFF : PPMD_ON; 18255cff7825Smh27603 18265cff7825Smh27603 PPMD(D_FET, ("%s: %s domain FET %s\n", str, domp->name, 18275cff7825Smh27603 (kio_val == off_val) ? "OFF" : "ON")) 18285cff7825Smh27603 18295cff7825Smh27603 break; 18305cff7825Smh27603 18315cff7825Smh27603 default: 18325cff7825Smh27603 PPMD(D_FET, ("%s: unsupported domain control method %d\n", 18335cff7825Smh27603 str, domp->dc->method)) 18345cff7825Smh27603 return (DDI_FAILURE); 18355cff7825Smh27603 } 18365cff7825Smh27603 18375cff7825Smh27603 return (DDI_SUCCESS); 18385cff7825Smh27603 } 18395cff7825Smh27603 18405cff7825Smh27603 18415cff7825Smh27603 /* 18425cff7825Smh27603 * the actual code that switches pci clock and update domain status 18435cff7825Smh27603 */ 18445cff7825Smh27603 static int 18455cff7825Smh27603 ppm_switch_clock(ppm_domain_t *domp, int onoff) 18465cff7825Smh27603 { 18475cff7825Smh27603 #ifdef DEBUG 18485cff7825Smh27603 char *str = "ppm_switch_clock"; 18495cff7825Smh27603 #endif 18505cff7825Smh27603 int cmd, pio_save; 18515cff7825Smh27603 ppm_dc_t *dc; 18525cff7825Smh27603 int ret; 18535cff7825Smh27603 extern int do_polled_io; 18545cff7825Smh27603 extern uint_t cfb_inuse; 18555cff7825Smh27603 ppm_dev_t *pdev; 18565cff7825Smh27603 18575cff7825Smh27603 cmd = (onoff == PPMD_ON) ? PPMDC_CLK_ON : PPMDC_CLK_OFF; 18585cff7825Smh27603 dc = ppm_lookup_dc(domp, cmd); 18595cff7825Smh27603 if (!dc) { 18605cff7825Smh27603 PPMD(D_PCI, ("%s: no ppm_dc found for domain (%s)\n", 18615cff7825Smh27603 str, domp->name)) 18625cff7825Smh27603 return (DDI_FAILURE); 18635cff7825Smh27603 } 18645cff7825Smh27603 18655cff7825Smh27603 switch (dc->method) { 18665cff7825Smh27603 case PPMDC_KIO: 18675cff7825Smh27603 /* 18685cff7825Smh27603 * If we're powering up cfb on a Stop-A, we only 18695cff7825Smh27603 * want to do polled i/o to turn ON the clock 18705cff7825Smh27603 */ 18715cff7825Smh27603 pio_save = do_polled_io; 18725cff7825Smh27603 if ((cfb_inuse) && (cmd == PPMDC_CLK_ON)) { 18735cff7825Smh27603 for (pdev = domp->devlist; pdev; pdev = pdev->next) { 18745cff7825Smh27603 if (pm_is_cfb(pdev->dip)) { 18755cff7825Smh27603 do_polled_io = 1; 18765cff7825Smh27603 break; 18775cff7825Smh27603 } 18785cff7825Smh27603 } 18795cff7825Smh27603 } 18805cff7825Smh27603 18815cff7825Smh27603 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr, 18825cff7825Smh27603 (intptr_t)&(dc->m_un.kio.val), FWRITE | FKIOCTL, 18835cff7825Smh27603 kcred, NULL); 18845cff7825Smh27603 18855cff7825Smh27603 do_polled_io = pio_save; 18865cff7825Smh27603 18875cff7825Smh27603 if (ret == 0) { 18885cff7825Smh27603 if (cmd == PPMDC_CLK_ON) { 18895cff7825Smh27603 domp->status = PPMD_ON; 18905cff7825Smh27603 18915cff7825Smh27603 /* 18925cff7825Smh27603 * PCI PM spec requires 50ms delay 18935cff7825Smh27603 */ 18945cff7825Smh27603 drv_usecwait(50000); 18955cff7825Smh27603 } else 18965cff7825Smh27603 domp->status = PPMD_OFF; 18975cff7825Smh27603 } 18985cff7825Smh27603 18995cff7825Smh27603 PPMD(D_PCI, ("%s: %s pci clock %s for domain (%s)\n", str, 19005cff7825Smh27603 (ret == 0) ? "turned" : "failed to turn", 19015cff7825Smh27603 (cmd == PPMDC_CLK_OFF) ? "OFF" : "ON", 19025cff7825Smh27603 domp->name)) 19035cff7825Smh27603 19045cff7825Smh27603 break; 19055cff7825Smh27603 19065cff7825Smh27603 default: 19075cff7825Smh27603 PPMD(D_PCI, ("%s: unsupported domain control method %d\n", 19085cff7825Smh27603 str, dc->method)) 19095cff7825Smh27603 return (DDI_FAILURE); 19105cff7825Smh27603 } 19115cff7825Smh27603 19125cff7825Smh27603 return (DDI_SUCCESS); 19135cff7825Smh27603 } 19145cff7825Smh27603 19155cff7825Smh27603 19165cff7825Smh27603 /* 19175cff7825Smh27603 * pci slot domain is formed of pci device(s) reside in a pci slot. 19185cff7825Smh27603 * This function monitors domain device's power level change, such 19195cff7825Smh27603 * that, 19205cff7825Smh27603 * when all domain power count has gone to 0, it attempts to turn off 19215cff7825Smh27603 * the pci slot's clock; 19225cff7825Smh27603 * if any domain device is powering up, it'll turn on the pci slot's 19235cff7825Smh27603 * clock as the first thing. 19245cff7825Smh27603 */ 19255cff7825Smh27603 /* ARGUSED */ 19265cff7825Smh27603 static int 19275cff7825Smh27603 ppm_manage_pci(dev_info_t *dip, power_req_t *reqp, int *result) 19285cff7825Smh27603 { 19295cff7825Smh27603 #ifdef DEBUG 19305cff7825Smh27603 char *str = "ppm_manage_pci"; 19315cff7825Smh27603 #endif 19325cff7825Smh27603 int (*pwr_func)(ppm_dev_t *, int, int); 19335cff7825Smh27603 int old, new, cmpt; 19345cff7825Smh27603 ppm_dev_t *ppmd; 19355cff7825Smh27603 ppm_domain_t *domp; 19365cff7825Smh27603 int incr = 0; 19375cff7825Smh27603 int dummy_ret; 19385cff7825Smh27603 19395cff7825Smh27603 *result = DDI_SUCCESS; 19405cff7825Smh27603 switch (reqp->request_type) { 19415cff7825Smh27603 case PMR_PPM_SET_POWER: 19425cff7825Smh27603 pwr_func = ppm_change_power_level; 19435cff7825Smh27603 old = reqp->req.ppm_set_power_req.old_level; 19445cff7825Smh27603 new = reqp->req.ppm_set_power_req.new_level; 19455cff7825Smh27603 cmpt = reqp->req.ppm_set_power_req.cmpt; 19465cff7825Smh27603 break; 19475cff7825Smh27603 19485cff7825Smh27603 case PMR_PPM_POWER_CHANGE_NOTIFY: 19495cff7825Smh27603 pwr_func = ppm_record_level_change; 19505cff7825Smh27603 old = reqp->req.ppm_notify_level_req.old_level; 19515cff7825Smh27603 new = reqp->req.ppm_notify_level_req.new_level; 19525cff7825Smh27603 cmpt = reqp->req.ppm_notify_level_req.cmpt; 19535cff7825Smh27603 break; 19545cff7825Smh27603 19555cff7825Smh27603 default: 19565cff7825Smh27603 *result = DDI_FAILURE; 19575cff7825Smh27603 return (DDI_FAILURE); 19585cff7825Smh27603 } 19595cff7825Smh27603 19605cff7825Smh27603 for (ppmd = PPM_GET_PRIVATE(dip); ppmd; ppmd = ppmd->next) 19615cff7825Smh27603 if (cmpt == ppmd->cmpt) 19625cff7825Smh27603 break; 19635cff7825Smh27603 if (!ppmd) { 19645cff7825Smh27603 PPMD(D_PCI, ("%s: dip(%p): old(%d), new(%d): no ppm_dev" 19655cff7825Smh27603 " found for cmpt(%d)", str, (void *)dip, old, new, cmpt)) 19665cff7825Smh27603 *result = DDI_FAILURE; 19675cff7825Smh27603 return (DDI_FAILURE); 19685cff7825Smh27603 } 19695cff7825Smh27603 domp = ppmd->domp; 19705cff7825Smh27603 PPMD(D_PCI, ("%s: %s, dev(%s), c%d, old %d, new %d\n", str, 19715cff7825Smh27603 ppm_get_ctlstr(reqp->request_type, ~0), 19725cff7825Smh27603 ppmd->path, cmpt, old, new)) 19735cff7825Smh27603 19745cff7825Smh27603 ASSERT(old == ppmd->level); 19755cff7825Smh27603 if (new == ppmd->level) 19765cff7825Smh27603 return (DDI_SUCCESS); 19775cff7825Smh27603 19785cff7825Smh27603 PPM_LOCK_DOMAIN(domp); 19795cff7825Smh27603 19805cff7825Smh27603 if (new > 0) { /* device powering up */ 19815cff7825Smh27603 if (domp->status == PPMD_OFF) { 19825cff7825Smh27603 19835cff7825Smh27603 /* cannot be off during (chpt, resume) window */ 19845cff7825Smh27603 ASSERT(ppm_cpr_window_flag == B_FALSE); 19855cff7825Smh27603 19865cff7825Smh27603 /* either both OFF or both ON */ 19875cff7825Smh27603 ASSERT(!((old == 0) ^ (domp->pwr_cnt == 0))); 19885cff7825Smh27603 19895cff7825Smh27603 PPMD(D_PCI, ("About to turn clock on for %s@%s c%d\n", 19905cff7825Smh27603 PM_NAME(dip), PM_ADDR(dip), cmpt)) 19915cff7825Smh27603 19925cff7825Smh27603 *result = ppm_switch_clock(domp, PPMD_ON); 19935cff7825Smh27603 if (*result != DDI_SUCCESS) { 19945cff7825Smh27603 PPMD(D_PCI, ("\tcan't switch on pci clock: " 19955cff7825Smh27603 "ret(%d)\n", *result)) 19965cff7825Smh27603 PPM_UNLOCK_DOMAIN(domp); 19975cff7825Smh27603 return (DDI_FAILURE); 19985cff7825Smh27603 } 19995cff7825Smh27603 } 20005cff7825Smh27603 20015cff7825Smh27603 if (old == 0) { 20025cff7825Smh27603 domp->pwr_cnt++; 20035cff7825Smh27603 incr = 1; 20045cff7825Smh27603 } 20055cff7825Smh27603 20065cff7825Smh27603 PPMD(D_PCI, ("\t%s domain power count: %d\n", 20075cff7825Smh27603 domp->name, domp->pwr_cnt)) 20085cff7825Smh27603 } 20095cff7825Smh27603 20105cff7825Smh27603 PPM_UNLOCK_DOMAIN(domp); 20115cff7825Smh27603 20125cff7825Smh27603 ASSERT(domp->pwr_cnt > 0); 20135cff7825Smh27603 20145cff7825Smh27603 if ((*result = (*pwr_func)(ppmd, cmpt, new)) != DDI_SUCCESS) { 20155cff7825Smh27603 PPMD(D_PCI, ("\t%s power change failed: ret(%d)\n", 20165cff7825Smh27603 ppmd->path, *result)) 20175cff7825Smh27603 } 20185cff7825Smh27603 20195cff7825Smh27603 PPM_LOCK_DOMAIN(domp); 20205cff7825Smh27603 20215cff7825Smh27603 /* 20225cff7825Smh27603 * Decr the power count in two cases: 20235cff7825Smh27603 * 20245cff7825Smh27603 * 1) request was to power device down and was successful 20255cff7825Smh27603 * 2) request was to power up (we pre-incremented count), but failed. 20265cff7825Smh27603 */ 20275cff7825Smh27603 if ((*result == DDI_SUCCESS && ppmd->level == 0) || 20285cff7825Smh27603 (*result != DDI_SUCCESS && incr)) { 20295cff7825Smh27603 ASSERT(domp->pwr_cnt > 0); 20305cff7825Smh27603 domp->pwr_cnt--; 20315cff7825Smh27603 } 20325cff7825Smh27603 20335cff7825Smh27603 PPMD(D_PCI, ("\t%s domain power count: %d\n", 20345cff7825Smh27603 domp->name, domp->pwr_cnt)) 20355cff7825Smh27603 20365cff7825Smh27603 /* 20375cff7825Smh27603 * call to pwr_func will update ppm data structures, if it 20385cff7825Smh27603 * succeeds. ppm should return whatever is the return value 20395cff7825Smh27603 * from call to pwr_func. This way pm and ppm data structures 20405cff7825Smh27603 * always in sync. Use dummy_ret from here for any further 20415cff7825Smh27603 * return values. 20425cff7825Smh27603 */ 20435cff7825Smh27603 if ((domp->pwr_cnt == 0) && 20445cff7825Smh27603 (ppm_cpr_window_flag == B_FALSE) && 20455cff7825Smh27603 ppm_none_else_holds_power(domp)) { 20465cff7825Smh27603 20475cff7825Smh27603 PPMD(D_PCI, ("About to turn clock off for %s@%s c%d\n", 20485cff7825Smh27603 PM_NAME(dip), PM_ADDR(dip), cmpt)) 20495cff7825Smh27603 20505cff7825Smh27603 dummy_ret = ppm_switch_clock(domp, PPMD_OFF); 20515cff7825Smh27603 if (dummy_ret != DDI_SUCCESS) { 20525cff7825Smh27603 PPMD(D_PCI, ("\tCan't switch clock off: " 20535cff7825Smh27603 "ret(%d)\n", dummy_ret)) 20545cff7825Smh27603 } 20555cff7825Smh27603 } 20565cff7825Smh27603 20575cff7825Smh27603 PPM_UNLOCK_DOMAIN(domp); 20585cff7825Smh27603 ASSERT(domp->pwr_cnt >= 0); 20595cff7825Smh27603 return (*result); 20605cff7825Smh27603 } 20615cff7825Smh27603 20625cff7825Smh27603 /* 20635cff7825Smh27603 * When the driver for the primary PCI-Express child has set the device to 20645cff7825Smh27603 * lowest power (D3hot), we come here to save even more power by transitioning 20655cff7825Smh27603 * the slot to D3cold. Similarly, if the slot is in D3cold and we need to 20665cff7825Smh27603 * power up the child, we come here first to power up the slot. 20675cff7825Smh27603 */ 20685cff7825Smh27603 /* ARGUSED */ 20695cff7825Smh27603 static int 20705cff7825Smh27603 ppm_manage_pcie(dev_info_t *dip, power_req_t *reqp, int *result) 20715cff7825Smh27603 { 20725cff7825Smh27603 #ifdef DEBUG 20735cff7825Smh27603 char *str = "ppm_manage_pcie"; 20745cff7825Smh27603 #endif 20755cff7825Smh27603 int (*pwr_func)(ppm_dev_t *, int, int); 20765cff7825Smh27603 int old, new, cmpt; 20775cff7825Smh27603 ppm_dev_t *ppmd; 20785cff7825Smh27603 ppm_domain_t *domp; 20795cff7825Smh27603 int incr = 0; 20805cff7825Smh27603 int dummy_ret; 20815cff7825Smh27603 20825cff7825Smh27603 *result = DDI_SUCCESS; 20835cff7825Smh27603 switch (reqp->request_type) { 20845cff7825Smh27603 case PMR_PPM_SET_POWER: 20855cff7825Smh27603 pwr_func = ppm_change_power_level; 20865cff7825Smh27603 old = reqp->req.ppm_set_power_req.old_level; 20875cff7825Smh27603 new = reqp->req.ppm_set_power_req.new_level; 20885cff7825Smh27603 cmpt = reqp->req.ppm_set_power_req.cmpt; 20895cff7825Smh27603 break; 20905cff7825Smh27603 20915cff7825Smh27603 case PMR_PPM_POWER_CHANGE_NOTIFY: 20925cff7825Smh27603 pwr_func = ppm_record_level_change; 20935cff7825Smh27603 old = reqp->req.ppm_notify_level_req.old_level; 20945cff7825Smh27603 new = reqp->req.ppm_notify_level_req.new_level; 20955cff7825Smh27603 cmpt = reqp->req.ppm_notify_level_req.cmpt; 20965cff7825Smh27603 break; 20975cff7825Smh27603 20985cff7825Smh27603 default: 20995cff7825Smh27603 *result = DDI_FAILURE; 21005cff7825Smh27603 return (DDI_FAILURE); 21015cff7825Smh27603 } 21025cff7825Smh27603 21035cff7825Smh27603 for (ppmd = PPM_GET_PRIVATE(dip); ppmd; ppmd = ppmd->next) 21045cff7825Smh27603 if (cmpt == ppmd->cmpt) 21055cff7825Smh27603 break; 21065cff7825Smh27603 if (!ppmd) { 21075cff7825Smh27603 PPMD(D_PCI, ("%s: dip(%p): old(%d), new(%d): no ppm_dev" 21085cff7825Smh27603 " found for cmpt(%d)", str, (void *)dip, old, new, cmpt)) 21095cff7825Smh27603 *result = DDI_FAILURE; 21105cff7825Smh27603 return (DDI_FAILURE); 21115cff7825Smh27603 } 21125cff7825Smh27603 domp = ppmd->domp; 21135cff7825Smh27603 PPMD(D_PCI, ("%s: %s, dev(%s), c%d, old %d, new %d\n", str, 21145cff7825Smh27603 ppm_get_ctlstr(reqp->request_type, ~0), 21155cff7825Smh27603 ppmd->path, cmpt, old, new)) 21165cff7825Smh27603 21175cff7825Smh27603 ASSERT(old == ppmd->level); 21185cff7825Smh27603 if (new == ppmd->level) 21195cff7825Smh27603 return (DDI_SUCCESS); 21205cff7825Smh27603 21215cff7825Smh27603 PPM_LOCK_DOMAIN(domp); 21225cff7825Smh27603 21235cff7825Smh27603 if (new > 0) { /* device powering up */ 21245cff7825Smh27603 if (domp->status == PPMD_OFF) { 21255cff7825Smh27603 21265cff7825Smh27603 /* cannot be off during (chpt, resume) window */ 21275cff7825Smh27603 ASSERT(ppm_cpr_window_flag == B_FALSE); 21285cff7825Smh27603 21295cff7825Smh27603 /* either both OFF or both ON */ 21305cff7825Smh27603 ASSERT(!((old == 0) ^ (domp->pwr_cnt == 0))); 21315cff7825Smh27603 21325cff7825Smh27603 PPMD(D_PCI, ("About to turn on pcie slot for " 21335cff7825Smh27603 "%s@%s c%d\n", PM_NAME(dip), PM_ADDR(dip), cmpt)) 21345cff7825Smh27603 21355cff7825Smh27603 *result = ppm_pcie_pwr(domp, PPMD_ON); 21365cff7825Smh27603 if (*result != DDI_SUCCESS) { 21375cff7825Smh27603 PPMD(D_PCI, ("\tcan't switch on pcie slot: " 21385cff7825Smh27603 "ret(%d)\n", *result)) 21395cff7825Smh27603 PPM_UNLOCK_DOMAIN(domp); 21405cff7825Smh27603 return (DDI_FAILURE); 21415cff7825Smh27603 } 21425cff7825Smh27603 } 21435cff7825Smh27603 21445cff7825Smh27603 if (old == 0) { 21455cff7825Smh27603 domp->pwr_cnt++; 21465cff7825Smh27603 incr = 1; 21475cff7825Smh27603 } 21485cff7825Smh27603 21495cff7825Smh27603 PPMD(D_PCI, ("\t%s domain power count: %d\n", 21505cff7825Smh27603 domp->name, domp->pwr_cnt)) 21515cff7825Smh27603 } 21525cff7825Smh27603 21535cff7825Smh27603 PPM_UNLOCK_DOMAIN(domp); 21545cff7825Smh27603 21555cff7825Smh27603 ASSERT(domp->pwr_cnt > 0); 21565cff7825Smh27603 21575cff7825Smh27603 if ((*result = (*pwr_func)(ppmd, cmpt, new)) != DDI_SUCCESS) { 21585cff7825Smh27603 PPMD(D_PCI, ("\t%s power change failed: ret(%d)\n", 21595cff7825Smh27603 ppmd->path, *result)) 21605cff7825Smh27603 } 21615cff7825Smh27603 21625cff7825Smh27603 PPM_LOCK_DOMAIN(domp); 21635cff7825Smh27603 21645cff7825Smh27603 /* 21655cff7825Smh27603 * Decr the power count in two cases: 21665cff7825Smh27603 * 21675cff7825Smh27603 * 1) request was to power device down and was successful 21685cff7825Smh27603 * 2) request was to power up (we pre-incremented count), but failed. 21695cff7825Smh27603 */ 21705cff7825Smh27603 if ((*result == DDI_SUCCESS && ppmd->level == 0) || 21715cff7825Smh27603 (*result != DDI_SUCCESS && incr)) { 21725cff7825Smh27603 ASSERT(domp->pwr_cnt > 0); 21735cff7825Smh27603 domp->pwr_cnt--; 21745cff7825Smh27603 } 21755cff7825Smh27603 21765cff7825Smh27603 PPMD(D_PCI, ("\t%s domain power count: %d\n", 21775cff7825Smh27603 domp->name, domp->pwr_cnt)) 21785cff7825Smh27603 21795cff7825Smh27603 /* 21805cff7825Smh27603 * call to pwr_func will update ppm data structures, if it 21815cff7825Smh27603 * succeeds. ppm should return whatever is the return value 21825cff7825Smh27603 * from call to pwr_func. This way pm and ppm data structures 21835cff7825Smh27603 * always in sync. Use dummy_ret from here for any further 21845cff7825Smh27603 * return values. 21855cff7825Smh27603 */ 21865cff7825Smh27603 if ((domp->pwr_cnt == 0) && 21875cff7825Smh27603 (ppm_cpr_window_flag == B_FALSE) && 21885cff7825Smh27603 ppm_none_else_holds_power(domp)) { 21895cff7825Smh27603 21905cff7825Smh27603 PPMD(D_PCI, ("About to turn off pcie slot for %s@%s c%d\n", 21915cff7825Smh27603 PM_NAME(dip), PM_ADDR(dip), cmpt)) 21925cff7825Smh27603 21935cff7825Smh27603 dummy_ret = ppm_pcie_pwr(domp, PPMD_OFF); 21945cff7825Smh27603 if (dummy_ret != DDI_SUCCESS) { 21955cff7825Smh27603 PPMD(D_PCI, ("\tCan't switch pcie slot off: " 21965cff7825Smh27603 "ret(%d)\n", dummy_ret)) 21975cff7825Smh27603 } 21985cff7825Smh27603 } 21995cff7825Smh27603 22005cff7825Smh27603 PPM_UNLOCK_DOMAIN(domp); 22015cff7825Smh27603 ASSERT(domp->pwr_cnt >= 0); 22025cff7825Smh27603 return (*result); 22035cff7825Smh27603 22045cff7825Smh27603 } 22055cff7825Smh27603 22065cff7825Smh27603 /* 22075cff7825Smh27603 * Set or clear a bit on a GPIO device. These bits are used for various device- 22085cff7825Smh27603 * specific purposes. 22095cff7825Smh27603 */ 22105cff7825Smh27603 static int 22115cff7825Smh27603 ppm_gpioset(ppm_domain_t *domp, int key) 22125cff7825Smh27603 { 22135cff7825Smh27603 #ifdef DEBUG 22145cff7825Smh27603 char *str = "ppm_gpioset"; 22155cff7825Smh27603 #endif 22165cff7825Smh27603 ppm_dc_t *dc; 22175cff7825Smh27603 int ret; 22185cff7825Smh27603 clock_t delay = 0; 22195cff7825Smh27603 22205cff7825Smh27603 for (dc = domp->dc; dc; dc = dc->next) 22215cff7825Smh27603 if (dc->cmd == key) 22225cff7825Smh27603 break; 22235cff7825Smh27603 if (!dc || !dc->lh) { 22245cff7825Smh27603 PPMD(D_GPIO, ("%s: %s domain: NULL ppm_dc handle\n", 22255cff7825Smh27603 str, domp->name)) 22265cff7825Smh27603 return (DDI_FAILURE); 22275cff7825Smh27603 } 22285cff7825Smh27603 22295cff7825Smh27603 PPM_GET_IO_DELAY(dc, delay); 22305cff7825Smh27603 if (delay > 0) { 22315cff7825Smh27603 PPMD(D_GPIO, ("%s : waiting %lu micro seconds " 22325cff7825Smh27603 "before change\n", domp->name, delay)) 22335cff7825Smh27603 drv_usecwait(delay); 22345cff7825Smh27603 } 22355cff7825Smh27603 22365cff7825Smh27603 switch (dc->method) { 22372df1fe9cSrandyf #ifdef sun4u 22385cff7825Smh27603 case PPMDC_I2CKIO: { 22395cff7825Smh27603 i2c_gpio_t i2c_req; 22405cff7825Smh27603 ppm_dev_t *pdev; 22415cff7825Smh27603 int pio_save; 22425cff7825Smh27603 extern int do_polled_io; 22435cff7825Smh27603 extern uint_t cfb_inuse; 22445cff7825Smh27603 i2c_req.reg_mask = dc->m_un.i2c.mask; 22455cff7825Smh27603 i2c_req.reg_val = dc->m_un.i2c.val; 22465cff7825Smh27603 22475cff7825Smh27603 pio_save = do_polled_io; 22485cff7825Smh27603 if (cfb_inuse) { 22495cff7825Smh27603 for (pdev = domp->devlist; pdev; pdev = pdev->next) { 22505cff7825Smh27603 if (pm_is_cfb(pdev->dip)) { 22515cff7825Smh27603 do_polled_io = 1; 22525cff7825Smh27603 PPMD(D_GPIO, ("%s: cfb is in use, " 22535cff7825Smh27603 "i2c transaction is done in " 22545cff7825Smh27603 "poll-mode.\n", str)) 22555cff7825Smh27603 break; 22565cff7825Smh27603 } 22575cff7825Smh27603 } 22585cff7825Smh27603 } 22595cff7825Smh27603 ret = ldi_ioctl(dc->lh, dc->m_un.i2c.iowr, 22605cff7825Smh27603 (intptr_t)&i2c_req, FWRITE | FKIOCTL, kcred, NULL); 22615cff7825Smh27603 do_polled_io = pio_save; 22625cff7825Smh27603 22635cff7825Smh27603 PPMD(D_GPIO, ("%s: %s domain(%s) from %s by writing %x " 22645cff7825Smh27603 "to gpio\n", 22655cff7825Smh27603 str, (ret == 0) ? "turned" : "FAILed to turn", 22665cff7825Smh27603 domp->name, 22675cff7825Smh27603 (domp->status == PPMD_ON) ? "ON" : "OFF", 22685cff7825Smh27603 dc->m_un.i2c.val)) 22695cff7825Smh27603 22705cff7825Smh27603 break; 22715cff7825Smh27603 } 22725cff7825Smh27603 #endif 22732df1fe9cSrandyf 22745cff7825Smh27603 case PPMDC_KIO: 22755cff7825Smh27603 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr, 22765cff7825Smh27603 (intptr_t)&(dc->m_un.kio.val), FWRITE | FKIOCTL, kcred, 22775cff7825Smh27603 NULL); 22785cff7825Smh27603 22795cff7825Smh27603 PPMD(D_GPIO, ("%s: %s domain(%s) from %s by writing %x " 22805cff7825Smh27603 "to gpio\n", 22815cff7825Smh27603 str, (ret == 0) ? "turned" : "FAILed to turn", 22825cff7825Smh27603 domp->name, 22835cff7825Smh27603 (domp->status == PPMD_ON) ? "ON" : "OFF", 22845cff7825Smh27603 dc->m_un.kio.val)) 22855cff7825Smh27603 22865cff7825Smh27603 break; 22875cff7825Smh27603 22885cff7825Smh27603 default: 22895cff7825Smh27603 PPMD(D_GPIO, ("\t%s: unsupported domain control method %d\n", 22905cff7825Smh27603 str, domp->dc->method)) 22915cff7825Smh27603 return (DDI_FAILURE); 22925cff7825Smh27603 } 22935cff7825Smh27603 22945cff7825Smh27603 /* implement any post op delay. */ 22951159df0bSmh27603 PPM_GET_IO_POST_DELAY(dc, delay); 22965cff7825Smh27603 if (delay > 0) { 22975cff7825Smh27603 PPMD(D_GPIO, ("%s : waiting %lu micro seconds " 22985cff7825Smh27603 "after change\n", domp->name, delay)) 22995cff7825Smh27603 drv_usecwait(delay); 23005cff7825Smh27603 } 23015cff7825Smh27603 23025cff7825Smh27603 return (ret); 23035cff7825Smh27603 } 23045cff7825Smh27603 23055cff7825Smh27603 static int 23065cff7825Smh27603 ppm_pcie_pwr(ppm_domain_t *domp, int onoff) 23075cff7825Smh27603 { 23085cff7825Smh27603 #ifdef DEBUG 23095cff7825Smh27603 char *str = "ppm_pcie_pwr"; 23105cff7825Smh27603 #endif 23115cff7825Smh27603 int ret = DDI_FAILURE; 23125cff7825Smh27603 ppm_dc_t *dc; 23135cff7825Smh27603 clock_t delay; 23145cff7825Smh27603 23155cff7825Smh27603 ASSERT(onoff == PPMD_OFF || onoff == PPMD_ON); 23165cff7825Smh27603 23175cff7825Smh27603 dc = ppm_lookup_dc(domp, 23185cff7825Smh27603 onoff == PPMD_ON ? PPMDC_PRE_PWR_ON : PPMDC_PRE_PWR_OFF); 23195cff7825Smh27603 if (dc) { 23205cff7825Smh27603 23215cff7825Smh27603 /* 23225cff7825Smh27603 * Invoke layered ioctl for pcie root complex nexus to 23235cff7825Smh27603 * transition the link 23245cff7825Smh27603 */ 23255cff7825Smh27603 ASSERT(dc->method == PPMDC_KIO); 23265cff7825Smh27603 delay = dc->m_un.kio.delay; 23275cff7825Smh27603 if (delay > 0) { 23285cff7825Smh27603 PPMD(D_GPIO, ("%s : waiting %lu micro seconds " 23295cff7825Smh27603 "before change\n", domp->name, delay)) 23305cff7825Smh27603 drv_usecwait(delay); 23315cff7825Smh27603 } 23325cff7825Smh27603 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr, 23335cff7825Smh27603 (intptr_t)&(dc->m_un.kio.val), 23345cff7825Smh27603 FWRITE | FKIOCTL, kcred, NULL); 23355cff7825Smh27603 if (ret == DDI_SUCCESS) { 23365cff7825Smh27603 delay = dc->m_un.kio.post_delay; 23375cff7825Smh27603 if (delay > 0) { 23385cff7825Smh27603 PPMD(D_GPIO, ("%s : waiting %lu micro seconds " 23395cff7825Smh27603 "after change\n", domp->name, delay)) 23405cff7825Smh27603 drv_usecwait(delay); 23415cff7825Smh27603 } 23425cff7825Smh27603 } else { 23435cff7825Smh27603 PPMD(D_PCI, ("%s: ldi_ioctl FAILED for domain(%s)\n", 23445cff7825Smh27603 str, domp->name)) 23455cff7825Smh27603 return (ret); 23465cff7825Smh27603 } 23475cff7825Smh27603 } 23485cff7825Smh27603 23495cff7825Smh27603 switch (onoff) { 23505cff7825Smh27603 case PPMD_OFF: 23515cff7825Smh27603 /* Turn off the clock for this slot. */ 23525cff7825Smh27603 if ((ret = ppm_gpioset(domp, PPMDC_CLK_OFF)) != DDI_SUCCESS) { 23535cff7825Smh27603 PPMD(D_GPIO, 23545cff7825Smh27603 ("%s: failed to turn off domain(%s) clock\n", 23555cff7825Smh27603 str, domp->name)) 23565cff7825Smh27603 return (ret); 23575cff7825Smh27603 } 23585cff7825Smh27603 23595cff7825Smh27603 /* Turn off the power to this slot */ 23605cff7825Smh27603 if ((ret = ppm_gpioset(domp, PPMDC_PWR_OFF)) != DDI_SUCCESS) { 23615cff7825Smh27603 PPMD(D_GPIO, 23625cff7825Smh27603 ("%s: failed to turn off domain(%s) power\n", 23635cff7825Smh27603 str, domp->name)) 23645cff7825Smh27603 return (ret); 23655cff7825Smh27603 } 23665cff7825Smh27603 break; 23675cff7825Smh27603 case PPMD_ON: 23685cff7825Smh27603 /* Assert RESET for this slot. */ 23695cff7825Smh27603 if ((ret = ppm_gpioset(domp, PPMDC_RESET_ON)) != DDI_SUCCESS) { 23705cff7825Smh27603 PPMD(D_GPIO, 23715cff7825Smh27603 ("%s: failed to assert reset for domain(%s)\n", 23725cff7825Smh27603 str, domp->name)) 23735cff7825Smh27603 return (ret); 23745cff7825Smh27603 } 23755cff7825Smh27603 23765cff7825Smh27603 /* Turn on the power to this slot */ 23775cff7825Smh27603 if ((ret = ppm_gpioset(domp, PPMDC_PWR_ON)) != DDI_SUCCESS) { 23785cff7825Smh27603 PPMD(D_GPIO, 23795cff7825Smh27603 ("%s: failed to turn on domain(%s) power\n", 23805cff7825Smh27603 str, domp->name)) 23815cff7825Smh27603 return (ret); 23825cff7825Smh27603 } 23835cff7825Smh27603 23845cff7825Smh27603 /* Turn on the clock for this slot */ 23855cff7825Smh27603 if ((ret = ppm_gpioset(domp, PPMDC_CLK_ON)) != DDI_SUCCESS) { 23865cff7825Smh27603 PPMD(D_GPIO, 23875cff7825Smh27603 ("%s: failed to turn on domain(%s) clock\n", 23885cff7825Smh27603 str, domp->name)) 23895cff7825Smh27603 return (ret); 23905cff7825Smh27603 } 23915cff7825Smh27603 23925cff7825Smh27603 /* De-assert RESET for this slot. */ 23935cff7825Smh27603 if ((ret = ppm_gpioset(domp, PPMDC_RESET_OFF)) != DDI_SUCCESS) { 23945cff7825Smh27603 PPMD(D_GPIO, 23955cff7825Smh27603 ("%s: failed to de-assert reset for domain(%s)\n", 23965cff7825Smh27603 str, domp->name)) 23975cff7825Smh27603 return (ret); 23985cff7825Smh27603 } 23995cff7825Smh27603 24005cff7825Smh27603 dc = ppm_lookup_dc(domp, PPMDC_POST_PWR_ON); 24015cff7825Smh27603 if (dc) { 24025cff7825Smh27603 /* 24035cff7825Smh27603 * Invoke layered ioctl to PCIe root complex nexus 24045cff7825Smh27603 * to transition the link. 24055cff7825Smh27603 */ 24065cff7825Smh27603 ASSERT(dc->method == PPMDC_KIO); 24075cff7825Smh27603 delay = dc->m_un.kio.delay; 24085cff7825Smh27603 if (delay > 0) { 24095cff7825Smh27603 PPMD(D_GPIO, ("%s: waiting %lu micro seconds " 24105cff7825Smh27603 "before change\n", domp->name, delay)) 24115cff7825Smh27603 drv_usecwait(delay); 24125cff7825Smh27603 } 24135cff7825Smh27603 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr, 24145cff7825Smh27603 (intptr_t)&(dc->m_un.kio.val), 24155cff7825Smh27603 FWRITE | FKIOCTL, kcred, NULL); 24165cff7825Smh27603 24175cff7825Smh27603 if (ret != DDI_SUCCESS) { 24185cff7825Smh27603 PPMD(D_PCI, ("%s: layered ioctl to PCIe" 24195cff7825Smh27603 "root complex nexus FAILed\n", str)) 24205cff7825Smh27603 return (ret); 24215cff7825Smh27603 } 24225cff7825Smh27603 24235cff7825Smh27603 delay = dc->m_un.kio.post_delay; 24245cff7825Smh27603 if (delay > 0) { 24255cff7825Smh27603 PPMD(D_GPIO, ("%s: waiting %lu micro " 24265cff7825Smh27603 "seconds after change\n", 24275cff7825Smh27603 domp->name, delay)) 24285cff7825Smh27603 drv_usecwait(delay); 24295cff7825Smh27603 } 24305cff7825Smh27603 } 24315cff7825Smh27603 break; 24325cff7825Smh27603 default: 24335cff7825Smh27603 ASSERT(0); 24345cff7825Smh27603 } 24355cff7825Smh27603 24365cff7825Smh27603 PPMD(D_PCI, ("%s: turned domain(%s) PCIe slot power from %s to %s\n", 24375cff7825Smh27603 str, domp->name, (domp->status == PPMD_ON) ? "ON" : "OFF", 24385cff7825Smh27603 onoff == PPMD_ON ? "ON" : "OFF")) 24395cff7825Smh27603 24405cff7825Smh27603 domp->status = onoff; 24415cff7825Smh27603 return (ret); 24425cff7825Smh27603 } 24435cff7825Smh27603 24445cff7825Smh27603 24455cff7825Smh27603 /* 24465cff7825Smh27603 * Change the power level for a component of a device. If the change 24475cff7825Smh27603 * arg is true, we call the framework to actually change the device's 24485cff7825Smh27603 * power; otherwise, we just update our own copy of the power level. 24495cff7825Smh27603 */ 24505cff7825Smh27603 static int 24515cff7825Smh27603 ppm_set_level(ppm_dev_t *ppmd, int cmpt, int level, boolean_t change) 24525cff7825Smh27603 { 24535cff7825Smh27603 #ifdef DEBUG 24545cff7825Smh27603 char *str = "ppm_set_level"; 24555cff7825Smh27603 #endif 24565cff7825Smh27603 int ret; 24575cff7825Smh27603 24585cff7825Smh27603 ret = DDI_SUCCESS; 24595cff7825Smh27603 if (change) 24605cff7825Smh27603 ret = pm_power(ppmd->dip, cmpt, level); 24615cff7825Smh27603 24625cff7825Smh27603 PPMD(D_SETLVL, ("%s: %s change=%d, old %d, new %d, ret %d\n", 24635cff7825Smh27603 str, ppmd->path, change, ppmd->level, level, ret)) 24645cff7825Smh27603 24655cff7825Smh27603 if (ret == DDI_SUCCESS) { 24665cff7825Smh27603 ppmd->level = level; 24675cff7825Smh27603 ppmd->rplvl = PM_LEVEL_UNKNOWN; 24685cff7825Smh27603 } 24695cff7825Smh27603 24705cff7825Smh27603 return (ret); 24715cff7825Smh27603 } 24725cff7825Smh27603 24735cff7825Smh27603 24745cff7825Smh27603 static int 24755cff7825Smh27603 ppm_change_power_level(ppm_dev_t *ppmd, int cmpt, int level) 24765cff7825Smh27603 { 24775cff7825Smh27603 return (ppm_set_level(ppmd, cmpt, level, B_TRUE)); 24785cff7825Smh27603 } 24795cff7825Smh27603 24805cff7825Smh27603 24815cff7825Smh27603 static int 24825cff7825Smh27603 ppm_record_level_change(ppm_dev_t *ppmd, int cmpt, int level) 24835cff7825Smh27603 { 24845cff7825Smh27603 return (ppm_set_level(ppmd, cmpt, level, B_FALSE)); 24855cff7825Smh27603 } 24865cff7825Smh27603 24875cff7825Smh27603 24885cff7825Smh27603 static void 24895cff7825Smh27603 ppm_manage_led(int action) 24905cff7825Smh27603 { 24915cff7825Smh27603 ppm_domain_t *domp; 24925cff7825Smh27603 ppm_unit_t *unitp; 24935cff7825Smh27603 timeout_id_t tid; 24945cff7825Smh27603 24955cff7825Smh27603 24965cff7825Smh27603 PPMD(D_LED, ("ppm_manage_led: action: %s\n", 24975cff7825Smh27603 (action == PPM_LED_BLINKING) ? "PPM_LED_BLINKING" : 24985cff7825Smh27603 "PPM_LED_SOLIDON")) 24995cff7825Smh27603 25005cff7825Smh27603 /* 25015cff7825Smh27603 * test whether led operation is practically supported, 25025cff7825Smh27603 * if not, we waive without pressing for reasons 25035cff7825Smh27603 */ 25045cff7825Smh27603 if (!ppm_lookup_dc(NULL, PPMDC_LED_ON)) 25055cff7825Smh27603 return; 25065cff7825Smh27603 25075cff7825Smh27603 unitp = ddi_get_soft_state(ppm_statep, ppm_inst); 25085cff7825Smh27603 for (domp = ppm_domain_p; (domp && (domp->model != PPMD_LED)); ) 25095cff7825Smh27603 domp = domp->next; 25105cff7825Smh27603 25115cff7825Smh27603 mutex_enter(&unitp->lock); 25125cff7825Smh27603 if (action == PPM_LED_BLINKING) { 25135cff7825Smh27603 ppm_set_led(domp, PPMD_OFF); 25145cff7825Smh27603 unitp->led_tid = timeout( 25155cff7825Smh27603 ppm_blink_led, domp, PPM_LEDOFF_INTERVAL); 25165cff7825Smh27603 25175cff7825Smh27603 } else { /* PPM_LED_SOLIDON */ 25185cff7825Smh27603 ASSERT(action == PPM_LED_SOLIDON); 25195cff7825Smh27603 tid = unitp->led_tid; 25205cff7825Smh27603 unitp->led_tid = 0; 25215cff7825Smh27603 25225cff7825Smh27603 mutex_exit(&unitp->lock); 25235cff7825Smh27603 (void) untimeout(tid); 25245cff7825Smh27603 25255cff7825Smh27603 mutex_enter(&unitp->lock); 25265cff7825Smh27603 ppm_set_led(domp, PPMD_ON); 25275cff7825Smh27603 } 25285cff7825Smh27603 mutex_exit(&unitp->lock); 25295cff7825Smh27603 } 25305cff7825Smh27603 25315cff7825Smh27603 25325cff7825Smh27603 static void 25335cff7825Smh27603 ppm_set_led(ppm_domain_t *domp, int val) 25345cff7825Smh27603 { 25355cff7825Smh27603 int ret; 25365cff7825Smh27603 25375cff7825Smh27603 ret = ppm_gpioset(domp, 25385cff7825Smh27603 (val == PPMD_ON) ? PPMDC_LED_ON : PPMDC_LED_OFF); 25395cff7825Smh27603 25405cff7825Smh27603 PPMD(D_LED, ("ppm_set_led: %s LED from %s\n", 25415cff7825Smh27603 (ret == 0) ? "turned" : "FAILed to turn", 25425cff7825Smh27603 (domp->status == PPMD_ON) ? "ON to OFF" : "OFF to ON")) 25435cff7825Smh27603 25445cff7825Smh27603 if (ret == DDI_SUCCESS) 25455cff7825Smh27603 domp->status = val; 25465cff7825Smh27603 } 25475cff7825Smh27603 25485cff7825Smh27603 25495cff7825Smh27603 static void 25505cff7825Smh27603 ppm_blink_led(void *arg) 25515cff7825Smh27603 { 25525cff7825Smh27603 ppm_unit_t *unitp; 25535cff7825Smh27603 clock_t intvl; 25545cff7825Smh27603 ppm_domain_t *domp = arg; 25555cff7825Smh27603 25565cff7825Smh27603 unitp = ddi_get_soft_state(ppm_statep, ppm_inst); 25575cff7825Smh27603 25585cff7825Smh27603 mutex_enter(&unitp->lock); 25595cff7825Smh27603 if (unitp->led_tid == 0) { 25605cff7825Smh27603 mutex_exit(&unitp->lock); 25615cff7825Smh27603 return; 25625cff7825Smh27603 } 25635cff7825Smh27603 25645cff7825Smh27603 if (domp->status == PPMD_ON) { 25655cff7825Smh27603 ppm_set_led(domp, PPMD_OFF); 25665cff7825Smh27603 intvl = PPM_LEDOFF_INTERVAL; 25675cff7825Smh27603 } else { 25685cff7825Smh27603 ppm_set_led(domp, PPMD_ON); 25695cff7825Smh27603 intvl = PPM_LEDON_INTERVAL; 25705cff7825Smh27603 } 25715cff7825Smh27603 25725cff7825Smh27603 unitp->led_tid = timeout(ppm_blink_led, domp, intvl); 25735cff7825Smh27603 mutex_exit(&unitp->lock); 25745cff7825Smh27603 } 25755cff7825Smh27603 25765cff7825Smh27603 /* 25775cff7825Smh27603 * Function to power up a domain, if required. It also increments the 25785cff7825Smh27603 * domain pwr_cnt to prevent it from going down. 25795cff7825Smh27603 */ 25805cff7825Smh27603 static int 25815cff7825Smh27603 ppm_power_up_domain(dev_info_t *dip) 25825cff7825Smh27603 { 25835cff7825Smh27603 int ret = DDI_SUCCESS; 25845cff7825Smh27603 ppm_domain_t *domp; 25855cff7825Smh27603 char *str = "ppm_power_up_domain"; 25865cff7825Smh27603 25875cff7825Smh27603 domp = ppm_lookup_dev(dip); 25885cff7825Smh27603 ASSERT(domp); 25895cff7825Smh27603 mutex_enter(&domp->lock); 25905cff7825Smh27603 switch (domp->model) { 25915cff7825Smh27603 case PPMD_FET: 25925cff7825Smh27603 if (domp->status == PPMD_OFF) { 25935cff7825Smh27603 if ((ret = ppm_fetset(domp, PPMD_ON)) == 25945cff7825Smh27603 DDI_SUCCESS) { 25955cff7825Smh27603 PPMD(D_FET, ("%s: turned on fet for %s@%s\n", 25965cff7825Smh27603 str, PM_NAME(dip), PM_ADDR(dip))) 25975cff7825Smh27603 } else { 25985cff7825Smh27603 PPMD(D_FET, ("%s: couldn't turn on fet " 25995cff7825Smh27603 "for %s@%s\n", str, PM_NAME(dip), 26005cff7825Smh27603 PM_ADDR(dip))) 26015cff7825Smh27603 } 26025cff7825Smh27603 } 26035cff7825Smh27603 break; 26045cff7825Smh27603 26055cff7825Smh27603 case PPMD_PCI: 26065cff7825Smh27603 case PPMD_PCI_PROP: 26075cff7825Smh27603 if (domp->status == PPMD_OFF) { 26085cff7825Smh27603 if ((ret = ppm_switch_clock(domp, PPMD_ON)) == 26095cff7825Smh27603 DDI_SUCCESS) { 26105cff7825Smh27603 PPMD(D_PCI, ("%s: turned on clock for " 26115cff7825Smh27603 "%s@%s\n", str, PM_NAME(dip), 26125cff7825Smh27603 PM_ADDR(dip))) 26135cff7825Smh27603 } else { 26145cff7825Smh27603 PPMD(D_PCI, ("%s: couldn't turn on clock " 26155cff7825Smh27603 "for %s@%s\n", str, PM_NAME(dip), 26165cff7825Smh27603 PM_ADDR(dip))) 26175cff7825Smh27603 } 26185cff7825Smh27603 } 26195cff7825Smh27603 break; 26205cff7825Smh27603 26215cff7825Smh27603 case PPMD_PCIE: 26225cff7825Smh27603 if (domp->status == PPMD_OFF) { 26235cff7825Smh27603 if ((ret = ppm_pcie_pwr(domp, PPMD_ON)) == 26245cff7825Smh27603 DDI_SUCCESS) { 26255cff7825Smh27603 PPMD(D_PCI, ("%s: turned on link for " 26265cff7825Smh27603 "%s@%s\n", str, PM_NAME(dip), 26275cff7825Smh27603 PM_ADDR(dip))) 26285cff7825Smh27603 } else { 26295cff7825Smh27603 PPMD(D_PCI, ("%s: couldn't turn on link " 26305cff7825Smh27603 "for %s@%s\n", str, PM_NAME(dip), 26315cff7825Smh27603 PM_ADDR(dip))) 26325cff7825Smh27603 } 26335cff7825Smh27603 } 26345cff7825Smh27603 break; 26355cff7825Smh27603 26365cff7825Smh27603 default: 26375cff7825Smh27603 break; 26385cff7825Smh27603 } 26395cff7825Smh27603 if (ret == DDI_SUCCESS) 26405cff7825Smh27603 domp->pwr_cnt++; 26415cff7825Smh27603 mutex_exit(&domp->lock); 26425cff7825Smh27603 return (ret); 26435cff7825Smh27603 } 26445cff7825Smh27603 26455cff7825Smh27603 /* 26465cff7825Smh27603 * Decrements the domain pwr_cnt. if conditions to power down the domain 26475cff7825Smh27603 * are met, powers down the domain,. 26485cff7825Smh27603 */ 26495cff7825Smh27603 static int 26505cff7825Smh27603 ppm_power_down_domain(dev_info_t *dip) 26515cff7825Smh27603 { 26525cff7825Smh27603 int ret = DDI_SUCCESS; 26535cff7825Smh27603 char *str = "ppm_power_down_domain"; 26545cff7825Smh27603 ppm_domain_t *domp; 26555cff7825Smh27603 26565cff7825Smh27603 domp = ppm_lookup_dev(dip); 26575cff7825Smh27603 ASSERT(domp); 26585cff7825Smh27603 mutex_enter(&domp->lock); 26595cff7825Smh27603 ASSERT(domp->pwr_cnt > 0); 26605cff7825Smh27603 domp->pwr_cnt--; 26615cff7825Smh27603 switch (domp->model) { 26625cff7825Smh27603 case PPMD_FET: 26635cff7825Smh27603 if ((domp->pwr_cnt == 0) && 26645cff7825Smh27603 (ppm_cpr_window_flag == B_FALSE) && 26655cff7825Smh27603 ppm_none_else_holds_power(domp)) { 26665cff7825Smh27603 if ((ret = ppm_fetset(domp, PPMD_OFF)) == 26675cff7825Smh27603 DDI_SUCCESS) { 26685cff7825Smh27603 PPMD(D_FET, ("%s: turned off FET for %s@%s \n", 26695cff7825Smh27603 str, PM_NAME(dip), PM_ADDR(dip))) 26705cff7825Smh27603 } else { 26715cff7825Smh27603 PPMD(D_FET, ("%s: couldn't turn off FET for " 26725cff7825Smh27603 " %s@%s\n", str, PM_NAME(dip), 26735cff7825Smh27603 PM_ADDR(dip))) 26745cff7825Smh27603 } 26755cff7825Smh27603 } 26765cff7825Smh27603 break; 26775cff7825Smh27603 26785cff7825Smh27603 case PPMD_PCI: 26795cff7825Smh27603 case PPMD_PCI_PROP: 26805cff7825Smh27603 if ((domp->pwr_cnt == 0) && 26815cff7825Smh27603 (ppm_cpr_window_flag == B_FALSE) && 26825cff7825Smh27603 ppm_none_else_holds_power(domp)) { 26835cff7825Smh27603 if ((ret = ppm_switch_clock(domp, PPMD_OFF)) == 26845cff7825Smh27603 DDI_SUCCESS) { 26855cff7825Smh27603 PPMD(D_PCI, ("%s: turned off clock for %s@%s\n", 26865cff7825Smh27603 str, PM_NAME(dip), PM_ADDR(dip))) 26875cff7825Smh27603 } else { 26885cff7825Smh27603 PPMD(D_PCI, ("%s: couldn't turn off clock " 26895cff7825Smh27603 "for %s@%s\n", str, PM_NAME(dip), 26905cff7825Smh27603 PM_ADDR(dip))) 26915cff7825Smh27603 } 26925cff7825Smh27603 } 26935cff7825Smh27603 break; 26945cff7825Smh27603 26955cff7825Smh27603 case PPMD_PCIE: 26965cff7825Smh27603 if ((domp->pwr_cnt == 0) && 26975cff7825Smh27603 (ppm_cpr_window_flag == B_FALSE) && 26985cff7825Smh27603 ppm_none_else_holds_power(domp)) { 26995cff7825Smh27603 if ((ret = ppm_pcie_pwr(domp, PPMD_OFF)) == 27005cff7825Smh27603 DDI_SUCCESS) { 27015cff7825Smh27603 PPMD(D_PCI, ("%s: turned off link for %s@%s\n", 27025cff7825Smh27603 str, PM_NAME(dip), PM_ADDR(dip))) 27035cff7825Smh27603 } else { 27045cff7825Smh27603 PPMD(D_PCI, ("%s: couldn't turn off link " 27055cff7825Smh27603 "for %s@%s\n", str, PM_NAME(dip), 27065cff7825Smh27603 PM_ADDR(dip))) 27075cff7825Smh27603 } 27085cff7825Smh27603 } 27095cff7825Smh27603 break; 27105cff7825Smh27603 27115cff7825Smh27603 default: 27125cff7825Smh27603 break; 27135cff7825Smh27603 } 27145cff7825Smh27603 mutex_exit(&domp->lock); 27155cff7825Smh27603 return (ret); 27165cff7825Smh27603 } 27172df1fe9cSrandyf 27182df1fe9cSrandyf static int 27192df1fe9cSrandyf ppm_manage_sx(s3a_t *s3ap, int enter) 27202df1fe9cSrandyf { 27212df1fe9cSrandyf ppm_domain_t *domp = ppm_lookup_domain("domain_estar"); 27222df1fe9cSrandyf ppm_dc_t *dc; 27232df1fe9cSrandyf int ret = 0; 27242df1fe9cSrandyf 27252df1fe9cSrandyf if (domp == NULL) { 27262df1fe9cSrandyf PPMD(D_CPR, ("ppm_manage_sx: can't find estar domain\n")) 27272df1fe9cSrandyf return (ENODEV); 27282df1fe9cSrandyf } 27292df1fe9cSrandyf PPMD(D_CPR, ("ppm_manage_sx %x, enter %d\n", s3ap->s3a_state, 27302df1fe9cSrandyf enter)) 27312df1fe9cSrandyf switch (s3ap->s3a_state) { 27322df1fe9cSrandyf case S3: 27332df1fe9cSrandyf if (enter) { 27342df1fe9cSrandyf dc = ppm_lookup_dc(domp, PPMDC_ENTER_S3); 27352df1fe9cSrandyf } else { 27362df1fe9cSrandyf dc = ppm_lookup_dc(domp, PPMDC_EXIT_S3); 27372df1fe9cSrandyf } 27382df1fe9cSrandyf ASSERT(dc && dc->method == PPMDC_KIO); 27392df1fe9cSrandyf PPMD(D_CPR, 27402df1fe9cSrandyf ("ppm_manage_sx: calling acpi driver (handle %p)" 27412df1fe9cSrandyf " with %x\n", (void *)dc->lh, dc->m_un.kio.iowr)) 27422df1fe9cSrandyf ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr, 27432df1fe9cSrandyf (intptr_t)s3ap, FWRITE | FKIOCTL, kcred, NULL); 27442df1fe9cSrandyf break; 27452df1fe9cSrandyf 27462df1fe9cSrandyf case S4: 27472df1fe9cSrandyf /* S4 is not supported yet */ 27482df1fe9cSrandyf return (EINVAL); 27492df1fe9cSrandyf default: 27502df1fe9cSrandyf ASSERT(0); 27512df1fe9cSrandyf } 27522df1fe9cSrandyf return (ret); 27532df1fe9cSrandyf } 27542df1fe9cSrandyf 27552df1fe9cSrandyf /* 27562df1fe9cSrandyf * Search enable/disable lists, which are encoded in ppm.conf as an array 27572df1fe9cSrandyf * of char strings. 27582df1fe9cSrandyf */ 27592df1fe9cSrandyf static int 27602df1fe9cSrandyf ppm_search_list(pm_searchargs_t *sl) 27612df1fe9cSrandyf { 27622df1fe9cSrandyf int i; 27632df1fe9cSrandyf int flags = DDI_PROP_DONTPASS; 27642df1fe9cSrandyf ppm_unit_t *unitp = ddi_get_soft_state(ppm_statep, ppm_inst); 27652df1fe9cSrandyf char **pp; 27662df1fe9cSrandyf char *starp; 27672df1fe9cSrandyf uint_t nelements; 27682df1fe9cSrandyf char *manuf = sl->pms_manufacturer; 27692df1fe9cSrandyf char *prod = sl->pms_product; 27702df1fe9cSrandyf 27712df1fe9cSrandyf if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, unitp->dip, flags, 27722df1fe9cSrandyf sl->pms_listname, &pp, &nelements) != DDI_PROP_SUCCESS) { 27732df1fe9cSrandyf PPMD(D_CPR, ("ppm_search_list prop lookup %s failed--EINVAL\n", 27742df1fe9cSrandyf sl->pms_listname)) 27752df1fe9cSrandyf return (EINVAL); 27762df1fe9cSrandyf } 27772df1fe9cSrandyf ASSERT((nelements & 1) == 0); /* must be even */ 27782df1fe9cSrandyf 27792df1fe9cSrandyf PPMD(D_CPR, ("ppm_search_list looking for %s, %s\n", manuf, prod)) 27802df1fe9cSrandyf 27812df1fe9cSrandyf for (i = 0; i < nelements; i += 2) { 27822df1fe9cSrandyf PPMD(D_CPR, ("checking %s, %s", pp[i], pp[i+1])) 27832df1fe9cSrandyf /* we support only a trailing '*' pattern match */ 27842df1fe9cSrandyf if ((starp = strchr(pp[i], '*')) != NULL && *(starp + 1) == 0) { 27852df1fe9cSrandyf /* LINTED - ptrdiff overflow */ 27862df1fe9cSrandyf if (strncmp(manuf, pp[i], (starp - pp[i])) != 0) { 27872df1fe9cSrandyf PPMD(D_CPR, (" no match %s with %s\n", 27882df1fe9cSrandyf manuf, pp[i + 1])) 27892df1fe9cSrandyf continue; 27902df1fe9cSrandyf } 27912df1fe9cSrandyf } 27922df1fe9cSrandyf if ((starp = strchr(pp[i + 1], '*')) != NULL && 27932df1fe9cSrandyf *(starp + 1) == 0) { 27942df1fe9cSrandyf if (strncmp(prod, 27952df1fe9cSrandyf /* LINTED - ptrdiff overflow */ 27962df1fe9cSrandyf pp[i + 1], (starp - pp[i + 1])) != 0) { 27972df1fe9cSrandyf PPMD(D_CPR, (" no match %s with %s\n", 27982df1fe9cSrandyf prod, pp[i + 1])) 27992df1fe9cSrandyf continue; 28002df1fe9cSrandyf } 28012df1fe9cSrandyf } 28022df1fe9cSrandyf if (strcmp(manuf, pp[i]) == 0 && 28032df1fe9cSrandyf (strcmp(prod, pp[i + 1]) == 0)) { 28042df1fe9cSrandyf PPMD(D_CPR, (" match\n")) 28052df1fe9cSrandyf ddi_prop_free(pp); 28062df1fe9cSrandyf return (0); 28072df1fe9cSrandyf } 28082df1fe9cSrandyf PPMD(D_CPR, (" no match %s with %s or %s with %s\n", 28092df1fe9cSrandyf manuf, pp[i], prod, pp[i + 1])) 28102df1fe9cSrandyf } 28112df1fe9cSrandyf ddi_prop_free(pp); 28122df1fe9cSrandyf return (ENODEV); 28132df1fe9cSrandyf } 2814