1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * CPR driver support routines 30 */ 31 32 #include <sys/types.h> 33 #include <sys/errno.h> 34 #include <sys/kmem.h> 35 #include <sys/systm.h> 36 #include <sys/sunddi.h> 37 #include <sys/ddi_impldefs.h> 38 #include <sys/epm.h> 39 #include <sys/cpr.h> 40 41 #define CPR_BUFSIZE 128 42 43 extern int devi_detach(dev_info_t *, int); 44 extern int devi_attach(dev_info_t *, int); 45 46 static char *devi_string(dev_info_t *, char *); 47 static int cpr_is_real_device(dev_info_t *); 48 49 /* 50 * Traverse the dev info tree: 51 * Call each device driver in the system via a special case 52 * of the detach() entry point to quiesce itself. 53 * Suspend children first. 54 * 55 * We only suspend/resume real devices. 56 */ 57 58 int 59 cpr_suspend_devices(dev_info_t *dip) 60 { 61 int error; 62 char buf[CPR_BUFSIZE]; 63 64 for (; dip != NULL; dip = ddi_get_next_sibling(dip)) { 65 if (cpr_suspend_devices(ddi_get_child(dip))) 66 return (ENXIO); 67 if (!cpr_is_real_device(dip)) 68 continue; 69 CPR_DEBUG(CPR_DEBUG2, "Suspending device %s\n", 70 devi_string(dip, buf)); 71 ASSERT((DEVI(dip)->devi_cpr_flags & DCF_CPR_SUSPENDED) == 0); 72 73 if (!i_ddi_devi_attached(dip)) 74 error = DDI_FAILURE; 75 else 76 error = devi_detach(dip, DDI_SUSPEND); 77 78 if (error == DDI_SUCCESS) 79 DEVI(dip)->devi_cpr_flags |= DCF_CPR_SUSPENDED; 80 else { 81 CPR_DEBUG(CPR_DEBUG2, 82 "WARNING: Unable to suspend device %s\n", 83 devi_string(dip, buf)); 84 cpr_err(CE_WARN, "Unable to suspend device %s.", 85 devi_string(dip, buf)); 86 cpr_err(CE_WARN, "Device is busy or does not " 87 "support suspend/resume."); 88 return (ENXIO); 89 } 90 } 91 return (0); 92 } 93 94 /* 95 * Traverse the dev info tree: 96 * Call each device driver in the system via a special case 97 * of the attach() entry point to restore itself. 98 * This is a little tricky because it has to reverse the traversal 99 * order of cpr_suspend_devices(). 100 */ 101 int 102 cpr_resume_devices(dev_info_t *start, int resume_failed) 103 { 104 dev_info_t *dip, *next, *last = NULL; 105 int did_suspend; 106 int error = resume_failed; 107 char buf[CPR_BUFSIZE]; 108 109 while (last != start) { 110 dip = start; 111 next = ddi_get_next_sibling(dip); 112 while (next != last) { 113 dip = next; 114 next = ddi_get_next_sibling(dip); 115 } 116 117 /* 118 * cpr is the only one that uses this field and the device 119 * itself hasn't resumed yet, there is no need to use a 120 * lock, even though kernel threads are active by now. 121 */ 122 did_suspend = DEVI(dip)->devi_cpr_flags & DCF_CPR_SUSPENDED; 123 if (did_suspend) 124 DEVI(dip)->devi_cpr_flags &= ~DCF_CPR_SUSPENDED; 125 126 /* 127 * There may be background attaches happening on devices 128 * that were not originally suspended by cpr, so resume 129 * only devices that were suspended by cpr. Also, stop 130 * resuming after the first resume failure, but traverse 131 * the entire tree to clear the suspend flag. 132 */ 133 if (did_suspend && !error) { 134 CPR_DEBUG(CPR_DEBUG2, "Resuming device %s\n", 135 devi_string(dip, buf)); 136 /* 137 * If a device suspended by cpr gets detached during 138 * the resume process (for example, due to hotplugging) 139 * before cpr gets around to issuing it a DDI_RESUME, 140 * we'll have problems. 141 */ 142 if (!i_ddi_devi_attached(dip)) { 143 CPR_DEBUG(CPR_DEBUG2, "WARNING: Skipping " 144 "%s, device not ready for resume\n", 145 devi_string(dip, buf)); 146 cpr_err(CE_WARN, "Skipping %s, device " 147 "not ready for resume", 148 devi_string(dip, buf)); 149 } else if (devi_attach(dip, DDI_RESUME) != 150 DDI_SUCCESS) { 151 CPR_DEBUG(CPR_DEBUG2, 152 "WARNING: Unable to resume device %s\n", 153 devi_string(dip, buf)); 154 cpr_err(CE_WARN, "Unable to resume device %s", 155 devi_string(dip, buf)); 156 error = ENXIO; 157 } 158 } 159 160 error = cpr_resume_devices(ddi_get_child(dip), error); 161 last = dip; 162 } 163 164 return (error); 165 } 166 167 /* 168 * Returns a string which contains device name and address. 169 */ 170 static char * 171 devi_string(dev_info_t *devi, char *buf) 172 { 173 char *name; 174 char *address; 175 int size; 176 177 name = ddi_node_name(devi); 178 address = ddi_get_name_addr(devi); 179 size = (name == NULL) ? 180 strlen("<null name>") : strlen(name); 181 size += (address == NULL) ? 182 strlen("<null>") : strlen(address); 183 184 /* 185 * Make sure that we don't over-run the buffer. 186 * There are 2 additional characters in the string. 187 */ 188 ASSERT((size + 2) <= CPR_BUFSIZE); 189 190 if (name == NULL) 191 (void) strcpy(buf, "<null name>"); 192 else 193 (void) strcpy(buf, name); 194 195 (void) strcat(buf, "@"); 196 if (address == NULL) 197 (void) strcat(buf, "<null>"); 198 else 199 (void) strcat(buf, address); 200 201 return (buf); 202 } 203 204 /* 205 * This function determines whether the given device is real (and should 206 * be suspended) or not (pseudo like). If the device has a "reg" property 207 * then it is presumed to have register state to save/restore. 208 */ 209 static int 210 cpr_is_real_device(dev_info_t *dip) 211 { 212 struct regspec *regbuf; 213 int length; 214 int rc; 215 216 if (ddi_get_driver(dip) == NULL) 217 return (0); 218 219 /* 220 * First those devices for which special arrangements have been made 221 */ 222 if (DEVI(dip)->devi_pm_flags & (PMC_NEEDS_SR|PMC_PARENTAL_SR)) 223 return (1); 224 if (DEVI(dip)->devi_pm_flags & PMC_NO_SR) 225 return (0); 226 227 /* 228 * now the general case 229 */ 230 rc = ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg", 231 (caddr_t)®buf, &length); 232 ASSERT(rc != DDI_PROP_NO_MEMORY); 233 if (rc != DDI_PROP_SUCCESS) { 234 return (0); 235 } else { 236 kmem_free((caddr_t)regbuf, length); 237 return (1); 238 } 239 } 240 241 /* 242 * Power down the system. 243 */ 244 void 245 cpr_power_down(void) 246 { 247 #if defined(__sparc) 248 /* 249 * XXX This platform firmware implementation dependency 250 * doesn't belong in common code! 251 */ 252 int is_defined = 0; 253 char *wordexists = "p\" power-off\" find nip swap l! "; 254 char *req = "power-off"; 255 256 /* 257 * is_defined has value -1 when defined 258 */ 259 prom_interpret(wordexists, (uintptr_t)&is_defined, 0, 0, 0, 0); 260 if (is_defined) { 261 CPR_DEBUG(CPR_DEBUG1, "\ncpr: %s...\n", req); 262 prom_interpret(req, 0, 0, 0, 0, 0); 263 } 264 #endif 265 } 266