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