17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5ae115bc7Smrj * Common Development and Distribution License (the "License"). 6ae115bc7Smrj * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22ae115bc7Smrj * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 277c478bd9Sstevel@tonic-gate 287c478bd9Sstevel@tonic-gate /* 297c478bd9Sstevel@tonic-gate * CPR driver support routines 307c478bd9Sstevel@tonic-gate */ 317c478bd9Sstevel@tonic-gate 327c478bd9Sstevel@tonic-gate #include <sys/types.h> 337c478bd9Sstevel@tonic-gate #include <sys/errno.h> 347c478bd9Sstevel@tonic-gate #include <sys/kmem.h> 357c478bd9Sstevel@tonic-gate #include <sys/systm.h> 367c478bd9Sstevel@tonic-gate #include <sys/sunddi.h> 377c478bd9Sstevel@tonic-gate #include <sys/ddi_impldefs.h> 387c478bd9Sstevel@tonic-gate #include <sys/epm.h> 397c478bd9Sstevel@tonic-gate #include <sys/cpr.h> 407c478bd9Sstevel@tonic-gate 417c478bd9Sstevel@tonic-gate #define CPR_BUFSIZE 128 427c478bd9Sstevel@tonic-gate 437c478bd9Sstevel@tonic-gate extern int devi_detach(dev_info_t *, int); 447c478bd9Sstevel@tonic-gate extern int devi_attach(dev_info_t *, int); 457c478bd9Sstevel@tonic-gate 467c478bd9Sstevel@tonic-gate static char *devi_string(dev_info_t *, char *); 477c478bd9Sstevel@tonic-gate static int cpr_is_real_device(dev_info_t *); 48*2df1fe9cSrandyf /* 49*2df1fe9cSrandyf * Xen uses this code to suspend _all_ drivers quickly and easily. 50*2df1fe9cSrandyf * Suspend and Resume uses it for the same reason, but also has 51*2df1fe9cSrandyf * to contend with some platform specific code that Xen does not. 52*2df1fe9cSrandyf * it is also used as a test entry point for developers/testers to 53*2df1fe9cSrandyf * execute code without going through a complete suspend. So additions 54*2df1fe9cSrandyf * that have platform implications shall need #if[n]def's. 55*2df1fe9cSrandyf */ 56*2df1fe9cSrandyf #ifndef __xpv 57*2df1fe9cSrandyf extern void i_cpr_save_configuration(dev_info_t *); 58*2df1fe9cSrandyf extern void i_cpr_restore_configuration(dev_info_t *); 59*2df1fe9cSrandyf #endif 607c478bd9Sstevel@tonic-gate 617c478bd9Sstevel@tonic-gate /* 627c478bd9Sstevel@tonic-gate * Traverse the dev info tree: 637c478bd9Sstevel@tonic-gate * Call each device driver in the system via a special case 647c478bd9Sstevel@tonic-gate * of the detach() entry point to quiesce itself. 657c478bd9Sstevel@tonic-gate * Suspend children first. 667c478bd9Sstevel@tonic-gate * 677c478bd9Sstevel@tonic-gate * We only suspend/resume real devices. 687c478bd9Sstevel@tonic-gate */ 697c478bd9Sstevel@tonic-gate 707c478bd9Sstevel@tonic-gate int 717c478bd9Sstevel@tonic-gate cpr_suspend_devices(dev_info_t *dip) 727c478bd9Sstevel@tonic-gate { 737c478bd9Sstevel@tonic-gate int error; 747c478bd9Sstevel@tonic-gate char buf[CPR_BUFSIZE]; 757c478bd9Sstevel@tonic-gate 767c478bd9Sstevel@tonic-gate for (; dip != NULL; dip = ddi_get_next_sibling(dip)) { 777c478bd9Sstevel@tonic-gate if (cpr_suspend_devices(ddi_get_child(dip))) 787c478bd9Sstevel@tonic-gate return (ENXIO); 797c478bd9Sstevel@tonic-gate if (!cpr_is_real_device(dip)) 807c478bd9Sstevel@tonic-gate continue; 81ae115bc7Smrj CPR_DEBUG(CPR_DEBUG2, "Suspending device %s\n", 82ae115bc7Smrj devi_string(dip, buf)); 837c478bd9Sstevel@tonic-gate ASSERT((DEVI(dip)->devi_cpr_flags & DCF_CPR_SUSPENDED) == 0); 847c478bd9Sstevel@tonic-gate 85*2df1fe9cSrandyf #ifndef __xpv 86*2df1fe9cSrandyf i_cpr_save_configuration(dip); 87*2df1fe9cSrandyf #endif 887c478bd9Sstevel@tonic-gate 89*2df1fe9cSrandyf 90*2df1fe9cSrandyf if (!i_ddi_devi_attached(dip)) { 91*2df1fe9cSrandyf error = DDI_FAILURE; 92*2df1fe9cSrandyf } else { 93*2df1fe9cSrandyf #ifndef __xpv 94*2df1fe9cSrandyf if (cpr_test_point != DEVICE_SUSPEND_TO_RAM || 95*2df1fe9cSrandyf (cpr_test_point == DEVICE_SUSPEND_TO_RAM && 96*2df1fe9cSrandyf cpr_device == ddi_driver_major(dip))) { 97*2df1fe9cSrandyf #endif 98*2df1fe9cSrandyf error = devi_detach(dip, DDI_SUSPEND); 99*2df1fe9cSrandyf #ifndef __xpv 100*2df1fe9cSrandyf } else { 101*2df1fe9cSrandyf error = DDI_SUCCESS; 102*2df1fe9cSrandyf } 103*2df1fe9cSrandyf #endif 104*2df1fe9cSrandyf } 105*2df1fe9cSrandyf 106*2df1fe9cSrandyf if (error == DDI_SUCCESS) { 1077c478bd9Sstevel@tonic-gate DEVI(dip)->devi_cpr_flags |= DCF_CPR_SUSPENDED; 108*2df1fe9cSrandyf } 109*2df1fe9cSrandyf 1107c478bd9Sstevel@tonic-gate else { 111ae115bc7Smrj CPR_DEBUG(CPR_DEBUG2, 112ae115bc7Smrj "WARNING: Unable to suspend device %s\n", 113ae115bc7Smrj devi_string(dip, buf)); 1147c478bd9Sstevel@tonic-gate cpr_err(CE_WARN, "Unable to suspend device %s.", 1157c478bd9Sstevel@tonic-gate devi_string(dip, buf)); 1167c478bd9Sstevel@tonic-gate cpr_err(CE_WARN, "Device is busy or does not " 1177c478bd9Sstevel@tonic-gate "support suspend/resume."); 118*2df1fe9cSrandyf #ifndef __xpv 119*2df1fe9cSrandyf /* 120*2df1fe9cSrandyf * the device has failed to suspend however, 121*2df1fe9cSrandyf * if cpr_test_point == FORCE_SUSPEND_TO_RAM 122*2df1fe9cSrandyf * after putting out the warning message above, 123*2df1fe9cSrandyf * we carry on as if suspending the device had 124*2df1fe9cSrandyf * been successful 125*2df1fe9cSrandyf */ 126*2df1fe9cSrandyf if (cpr_test_point == FORCE_SUSPEND_TO_RAM) 127*2df1fe9cSrandyf DEVI(dip)->devi_cpr_flags |= DCF_CPR_SUSPENDED; 128*2df1fe9cSrandyf else 129*2df1fe9cSrandyf #endif 1307c478bd9Sstevel@tonic-gate return (ENXIO); 1317c478bd9Sstevel@tonic-gate } 1327c478bd9Sstevel@tonic-gate } 1337c478bd9Sstevel@tonic-gate return (0); 1347c478bd9Sstevel@tonic-gate } 1357c478bd9Sstevel@tonic-gate 1367c478bd9Sstevel@tonic-gate /* 1377c478bd9Sstevel@tonic-gate * Traverse the dev info tree: 1387c478bd9Sstevel@tonic-gate * Call each device driver in the system via a special case 1397c478bd9Sstevel@tonic-gate * of the attach() entry point to restore itself. 1407c478bd9Sstevel@tonic-gate * This is a little tricky because it has to reverse the traversal 1417c478bd9Sstevel@tonic-gate * order of cpr_suspend_devices(). 1427c478bd9Sstevel@tonic-gate */ 1437c478bd9Sstevel@tonic-gate int 1447c478bd9Sstevel@tonic-gate cpr_resume_devices(dev_info_t *start, int resume_failed) 1457c478bd9Sstevel@tonic-gate { 1467c478bd9Sstevel@tonic-gate dev_info_t *dip, *next, *last = NULL; 1477c478bd9Sstevel@tonic-gate int did_suspend; 1487c478bd9Sstevel@tonic-gate int error = resume_failed; 1497c478bd9Sstevel@tonic-gate char buf[CPR_BUFSIZE]; 1507c478bd9Sstevel@tonic-gate 1517c478bd9Sstevel@tonic-gate while (last != start) { 1527c478bd9Sstevel@tonic-gate dip = start; 1537c478bd9Sstevel@tonic-gate next = ddi_get_next_sibling(dip); 1547c478bd9Sstevel@tonic-gate while (next != last) { 1557c478bd9Sstevel@tonic-gate dip = next; 1567c478bd9Sstevel@tonic-gate next = ddi_get_next_sibling(dip); 1577c478bd9Sstevel@tonic-gate } 1587c478bd9Sstevel@tonic-gate 1597c478bd9Sstevel@tonic-gate /* 1607c478bd9Sstevel@tonic-gate * cpr is the only one that uses this field and the device 1617c478bd9Sstevel@tonic-gate * itself hasn't resumed yet, there is no need to use a 1627c478bd9Sstevel@tonic-gate * lock, even though kernel threads are active by now. 1637c478bd9Sstevel@tonic-gate */ 1647c478bd9Sstevel@tonic-gate did_suspend = DEVI(dip)->devi_cpr_flags & DCF_CPR_SUSPENDED; 1657c478bd9Sstevel@tonic-gate if (did_suspend) 1667c478bd9Sstevel@tonic-gate DEVI(dip)->devi_cpr_flags &= ~DCF_CPR_SUSPENDED; 1677c478bd9Sstevel@tonic-gate 1687c478bd9Sstevel@tonic-gate /* 169*2df1fe9cSrandyf * Always attempt to restore device configuration before 170*2df1fe9cSrandyf * attempting resume 171*2df1fe9cSrandyf */ 172*2df1fe9cSrandyf #ifndef __xpv 173*2df1fe9cSrandyf i_cpr_restore_configuration(dip); 174*2df1fe9cSrandyf #endif 175*2df1fe9cSrandyf 176*2df1fe9cSrandyf /* 1777c478bd9Sstevel@tonic-gate * There may be background attaches happening on devices 1787c478bd9Sstevel@tonic-gate * that were not originally suspended by cpr, so resume 1797c478bd9Sstevel@tonic-gate * only devices that were suspended by cpr. Also, stop 1807c478bd9Sstevel@tonic-gate * resuming after the first resume failure, but traverse 181*2df1fe9cSrandyf * the entire tree to clear the suspend flag unless the 182*2df1fe9cSrandyf * FORCE_SUSPEND_TO_RAM test point is set. 1837c478bd9Sstevel@tonic-gate */ 184*2df1fe9cSrandyf #ifndef __xpv 185*2df1fe9cSrandyf if (did_suspend && (!error || 186*2df1fe9cSrandyf cpr_test_point == FORCE_SUSPEND_TO_RAM)) { 187*2df1fe9cSrandyf #else 1887c478bd9Sstevel@tonic-gate if (did_suspend && !error) { 189*2df1fe9cSrandyf #endif 190ae115bc7Smrj CPR_DEBUG(CPR_DEBUG2, "Resuming device %s\n", 191ae115bc7Smrj devi_string(dip, buf)); 1927c478bd9Sstevel@tonic-gate /* 1937c478bd9Sstevel@tonic-gate * If a device suspended by cpr gets detached during 1947c478bd9Sstevel@tonic-gate * the resume process (for example, due to hotplugging) 1957c478bd9Sstevel@tonic-gate * before cpr gets around to issuing it a DDI_RESUME, 1967c478bd9Sstevel@tonic-gate * we'll have problems. 1977c478bd9Sstevel@tonic-gate */ 198737d277aScth if (!i_ddi_devi_attached(dip)) { 199ae115bc7Smrj CPR_DEBUG(CPR_DEBUG2, "WARNING: Skipping " 200ae115bc7Smrj "%s, device not ready for resume\n", 201ae115bc7Smrj devi_string(dip, buf)); 2027c478bd9Sstevel@tonic-gate cpr_err(CE_WARN, "Skipping %s, device " 2037c478bd9Sstevel@tonic-gate "not ready for resume", 2047c478bd9Sstevel@tonic-gate devi_string(dip, buf)); 205*2df1fe9cSrandyf #ifndef __xpv 206*2df1fe9cSrandyf } else if (cpr_test_point != DEVICE_SUSPEND_TO_RAM || 207*2df1fe9cSrandyf (cpr_test_point == DEVICE_SUSPEND_TO_RAM && 208*2df1fe9cSrandyf cpr_device == ddi_driver_major(dip))) { 209*2df1fe9cSrandyf #else 210*2df1fe9cSrandyf } else { 211*2df1fe9cSrandyf #endif 212*2df1fe9cSrandyf if (devi_attach(dip, DDI_RESUME) != 2137c478bd9Sstevel@tonic-gate DDI_SUCCESS) { 214*2df1fe9cSrandyf error = ENXIO; 215*2df1fe9cSrandyf } 216*2df1fe9cSrandyf } 217*2df1fe9cSrandyf } 218*2df1fe9cSrandyf 219*2df1fe9cSrandyf if (error == ENXIO) { 220ae115bc7Smrj CPR_DEBUG(CPR_DEBUG2, 2217c478bd9Sstevel@tonic-gate "WARNING: Unable to resume device %s\n", 222ae115bc7Smrj devi_string(dip, buf)); 2237c478bd9Sstevel@tonic-gate cpr_err(CE_WARN, "Unable to resume device %s", 2247c478bd9Sstevel@tonic-gate devi_string(dip, buf)); 2257c478bd9Sstevel@tonic-gate } 2267c478bd9Sstevel@tonic-gate 2277c478bd9Sstevel@tonic-gate error = cpr_resume_devices(ddi_get_child(dip), error); 2287c478bd9Sstevel@tonic-gate last = dip; 2297c478bd9Sstevel@tonic-gate } 2307c478bd9Sstevel@tonic-gate 2317c478bd9Sstevel@tonic-gate return (error); 2327c478bd9Sstevel@tonic-gate } 2337c478bd9Sstevel@tonic-gate 2347c478bd9Sstevel@tonic-gate /* 2357c478bd9Sstevel@tonic-gate * Returns a string which contains device name and address. 2367c478bd9Sstevel@tonic-gate */ 2377c478bd9Sstevel@tonic-gate static char * 2387c478bd9Sstevel@tonic-gate devi_string(dev_info_t *devi, char *buf) 2397c478bd9Sstevel@tonic-gate { 2407c478bd9Sstevel@tonic-gate char *name; 2417c478bd9Sstevel@tonic-gate char *address; 2427c478bd9Sstevel@tonic-gate int size; 2437c478bd9Sstevel@tonic-gate 2447c478bd9Sstevel@tonic-gate name = ddi_node_name(devi); 2457c478bd9Sstevel@tonic-gate address = ddi_get_name_addr(devi); 246*2df1fe9cSrandyf size = (name == NULL) ? strlen("<null name>") : strlen(name); 247*2df1fe9cSrandyf size += (address == NULL) ? strlen("<null>") : strlen(address); 2487c478bd9Sstevel@tonic-gate 2497c478bd9Sstevel@tonic-gate /* 2507c478bd9Sstevel@tonic-gate * Make sure that we don't over-run the buffer. 2517c478bd9Sstevel@tonic-gate * There are 2 additional characters in the string. 2527c478bd9Sstevel@tonic-gate */ 2537c478bd9Sstevel@tonic-gate ASSERT((size + 2) <= CPR_BUFSIZE); 2547c478bd9Sstevel@tonic-gate 2557c478bd9Sstevel@tonic-gate if (name == NULL) 2567c478bd9Sstevel@tonic-gate (void) strcpy(buf, "<null name>"); 2577c478bd9Sstevel@tonic-gate else 2587c478bd9Sstevel@tonic-gate (void) strcpy(buf, name); 2597c478bd9Sstevel@tonic-gate 2607c478bd9Sstevel@tonic-gate (void) strcat(buf, "@"); 2617c478bd9Sstevel@tonic-gate if (address == NULL) 2627c478bd9Sstevel@tonic-gate (void) strcat(buf, "<null>"); 2637c478bd9Sstevel@tonic-gate else 2647c478bd9Sstevel@tonic-gate (void) strcat(buf, address); 2657c478bd9Sstevel@tonic-gate 2667c478bd9Sstevel@tonic-gate return (buf); 2677c478bd9Sstevel@tonic-gate } 2687c478bd9Sstevel@tonic-gate 2697c478bd9Sstevel@tonic-gate /* 2707c478bd9Sstevel@tonic-gate * This function determines whether the given device is real (and should 2717c478bd9Sstevel@tonic-gate * be suspended) or not (pseudo like). If the device has a "reg" property 2727c478bd9Sstevel@tonic-gate * then it is presumed to have register state to save/restore. 2737c478bd9Sstevel@tonic-gate */ 2747c478bd9Sstevel@tonic-gate static int 2757c478bd9Sstevel@tonic-gate cpr_is_real_device(dev_info_t *dip) 2767c478bd9Sstevel@tonic-gate { 2777c478bd9Sstevel@tonic-gate struct regspec *regbuf; 2787c478bd9Sstevel@tonic-gate int length; 2797c478bd9Sstevel@tonic-gate int rc; 2807c478bd9Sstevel@tonic-gate 2817c478bd9Sstevel@tonic-gate if (ddi_get_driver(dip) == NULL) 2827c478bd9Sstevel@tonic-gate return (0); 2837c478bd9Sstevel@tonic-gate 2847c478bd9Sstevel@tonic-gate /* 2857c478bd9Sstevel@tonic-gate * First those devices for which special arrangements have been made 2867c478bd9Sstevel@tonic-gate */ 2877c478bd9Sstevel@tonic-gate if (DEVI(dip)->devi_pm_flags & (PMC_NEEDS_SR|PMC_PARENTAL_SR)) 2887c478bd9Sstevel@tonic-gate return (1); 2897c478bd9Sstevel@tonic-gate if (DEVI(dip)->devi_pm_flags & PMC_NO_SR) 2907c478bd9Sstevel@tonic-gate return (0); 2917c478bd9Sstevel@tonic-gate 2927c478bd9Sstevel@tonic-gate /* 2937c478bd9Sstevel@tonic-gate * now the general case 2947c478bd9Sstevel@tonic-gate */ 295a3282898Scth rc = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg", 2967c478bd9Sstevel@tonic-gate (caddr_t)®buf, &length); 2977c478bd9Sstevel@tonic-gate ASSERT(rc != DDI_PROP_NO_MEMORY); 2987c478bd9Sstevel@tonic-gate if (rc != DDI_PROP_SUCCESS) { 2997c478bd9Sstevel@tonic-gate return (0); 3007c478bd9Sstevel@tonic-gate } else { 3017c478bd9Sstevel@tonic-gate kmem_free((caddr_t)regbuf, length); 3027c478bd9Sstevel@tonic-gate return (1); 3037c478bd9Sstevel@tonic-gate } 3047c478bd9Sstevel@tonic-gate } 305