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 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 28 #include <sys/types.h> 29 #include <sys/conf.h> 30 #include <sys/ddi.h> 31 #include <sys/sunddi.h> 32 #include <sys/ddi_impldefs.h> 33 #include <sys/obpdefs.h> 34 #include <sys/cmn_err.h> 35 #include <sys/errno.h> 36 #include <sys/kmem.h> 37 #include <sys/debug.h> 38 #include <sys/sysmacros.h> 39 #include <sys/ivintr.h> 40 #include <sys/callb.h> 41 #include <sys/autoconf.h> 42 #include <sys/intreg.h> 43 #include <sys/modctl.h> 44 #include <sys/proc.h> 45 #include <sys/disp.h> 46 #include <sys/fhc.h> 47 #include <sys/environ.h> 48 49 /* Useful debugging Stuff */ 50 #include <sys/nexusdebug.h> 51 52 /* 53 * Function prototypes 54 */ 55 static int environ_attach(dev_info_t *devi, ddi_attach_cmd_t cmd); 56 57 static int environ_detach(dev_info_t *devi, ddi_detach_cmd_t cmd); 58 59 static int environ_init(struct environ_soft_state *softsp); 60 61 void environ_add_temp_kstats(struct environ_soft_state *softsp); 62 63 static void overtemp_wakeup(void *); 64 65 static void environ_overtemp_poll(void); 66 67 /* 68 * Configuration data structures 69 */ 70 static struct cb_ops environ_cb_ops = { 71 nulldev, /* open */ 72 nulldev, /* close */ 73 nulldev, /* strategy */ 74 nulldev, /* print */ 75 nodev, /* dump */ 76 nulldev, /* read */ 77 nulldev, /* write */ 78 nulldev, /* ioctl */ 79 nodev, /* devmap */ 80 nodev, /* mmap */ 81 nodev, /* segmap */ 82 nochpoll, /* poll */ 83 ddi_prop_op, /* cb_prop_op */ 84 0, /* streamtab */ 85 D_MP | D_NEW | D_HOTPLUG, /* Driver compatibility flag */ 86 CB_REV, /* rev */ 87 nodev, /* cb_aread */ 88 nodev /* cb_awrite */ 89 }; 90 91 static struct dev_ops environ_ops = { 92 DEVO_REV, /* devo_rev, */ 93 0, /* refcnt */ 94 ddi_no_info, /* getinfo */ 95 nulldev, /* identify */ 96 nulldev, /* probe */ 97 environ_attach, /* attach */ 98 environ_detach, /* detach */ 99 nulldev, /* reset */ 100 &environ_cb_ops, /* cb_ops */ 101 (struct bus_ops *)0, /* bus_ops */ 102 nulldev, /* power */ 103 ddi_quiesce_not_needed, /* quiesce */ 104 }; 105 106 void *environp; /* environ soft state hook */ 107 108 /* 109 * Mutex used to protect the soft state list and their data. 110 */ 111 static kmutex_t overtemp_mutex; 112 113 /* The CV is used to wakeup the thread when needed. */ 114 static kcondvar_t overtemp_cv; 115 116 /* linked list of all environ soft states */ 117 struct environ_soft_state *tempsp_list = NULL; 118 119 /* overtemp polling routine timeout delay */ 120 static int overtemp_timeout_sec = OVERTEMP_TIMEOUT_SEC; 121 122 /* Should the environ_overtemp_poll thread be running? */ 123 static int environ_do_overtemp_thread = 1; 124 125 /* Indicates whether or not the overtemp thread has been started */ 126 static int environ_overtemp_thread_started = 0; 127 128 extern struct mod_ops mod_driverops; 129 130 static struct modldrv modldrv = { 131 &mod_driverops, /* module type, this one is a driver */ 132 "Environment Leaf", /* name of module */ 133 &environ_ops, /* driver ops */ 134 }; 135 136 static struct modlinkage modlinkage = { 137 MODREV_1, 138 (void *)&modldrv, 139 NULL 140 }; 141 142 #ifndef lint 143 char _depends_on[] = "drv/fhc"; 144 #endif /* lint */ 145 146 /* 147 * These are the module initialization routines. 148 */ 149 150 int 151 _init(void) 152 { 153 int error; 154 155 if ((error = ddi_soft_state_init(&environp, 156 sizeof (struct environ_soft_state), 1)) != 0) 157 return (error); 158 159 return (mod_install(&modlinkage)); 160 } 161 162 int 163 _fini(void) 164 { 165 int error; 166 167 if ((error = mod_remove(&modlinkage)) != 0) 168 return (error); 169 170 ddi_soft_state_fini(&environp); 171 return (0); 172 } 173 174 int 175 _info(struct modinfo *modinfop) 176 { 177 return (mod_info(&modlinkage, modinfop)); 178 } 179 180 static int 181 environ_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 182 { 183 struct environ_soft_state *softsp; 184 int instance; 185 186 switch (cmd) { 187 case DDI_ATTACH: 188 break; 189 190 case DDI_RESUME: 191 return (DDI_SUCCESS); 192 193 default: 194 return (DDI_FAILURE); 195 } 196 197 instance = ddi_get_instance(devi); 198 199 if (ddi_soft_state_zalloc(environp, instance) != DDI_SUCCESS) 200 return (DDI_FAILURE); 201 202 softsp = ddi_get_soft_state(environp, instance); 203 204 /* Set the dip in the soft state */ 205 softsp->dip = devi; 206 207 /* 208 * The DDI documentation on ddi_getprop() routine says that 209 * you should always use the real dev_t when calling it, 210 * but all calls found in uts use either DDI_DEV_T_ANY 211 * or DDI_DEV_T_NONE. No notes either on how to find the real 212 * dev_t. So we are doing it in two steps. 213 */ 214 softsp->pdip = ddi_get_parent(softsp->dip); 215 216 if ((softsp->board = (int)ddi_getprop(DDI_DEV_T_ANY, softsp->pdip, 217 DDI_PROP_DONTPASS, OBP_BOARDNUM, -1)) == -1) { 218 cmn_err(CE_WARN, "environ%d: unable to retrieve %s property", 219 instance, OBP_BOARDNUM); 220 goto bad; 221 } 222 223 DPRINTF(ENVIRON_ATTACH_DEBUG, ("environ: devi= 0x%p\n, softsp=0x%p,", 224 (void *)devi, (void *)softsp)); 225 226 /* 227 * Init the temperature device here. We start the overtemp 228 * polling thread here. 229 */ 230 if (environ_init(softsp) != DDI_SUCCESS) 231 goto bad; 232 233 /* nothing to suspend/resume here */ 234 (void) ddi_prop_update_string(DDI_DEV_T_NONE, devi, 235 "pm-hardware-state", "no-suspend-resume"); 236 237 ddi_report_dev(devi); 238 239 if (environ_overtemp_thread_started == 0) { 240 /* 241 * set up the overtemp mutex and condition variable before 242 * starting the thread. 243 */ 244 mutex_init(&overtemp_mutex, NULL, MUTEX_DEFAULT, NULL); 245 cv_init(&overtemp_cv, NULL, CV_DRIVER, NULL); 246 247 /* Start the overtemp polling thread now. */ 248 (void) thread_create(NULL, 0, (void (*)())environ_overtemp_poll, 249 NULL, 0, &p0, TS_RUN, minclsyspri); 250 environ_overtemp_thread_started++; 251 } 252 253 (void) fhc_bdlist_lock(softsp->board); 254 fhc_bd_env_set(softsp->board, (void *)softsp); 255 fhc_bdlist_unlock(); 256 257 return (DDI_SUCCESS); 258 259 bad: 260 ddi_soft_state_free(environp, instance); 261 return (DDI_FAILURE); 262 } 263 264 /* ARGSUSED */ 265 static int 266 environ_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 267 { 268 int instance; 269 struct environ_soft_state *softsp; 270 struct environ_soft_state **vect; /* used in list deletion */ 271 struct environ_soft_state *temp; /* used in list deletion */ 272 273 /* get the instance of this devi */ 274 instance = ddi_get_instance(devi); 275 276 /* get the soft state pointer for this device node */ 277 softsp = ddi_get_soft_state(environp, instance); 278 279 switch (cmd) { 280 case DDI_SUSPEND: 281 return (DDI_SUCCESS); 282 283 case DDI_DETACH: 284 (void) fhc_bdlist_lock(softsp->board); 285 if (fhc_bd_detachable(softsp->board)) 286 break; 287 else 288 fhc_bdlist_unlock(); 289 /* FALLTHROUGH */ 290 291 default: 292 return (DDI_FAILURE); 293 } 294 295 fhc_bd_env_set(softsp->board, NULL); 296 297 fhc_bdlist_unlock(); 298 299 /* remove the environmental kstats if they were allocated */ 300 if (softsp->environ_ksp) 301 kstat_delete(softsp->environ_ksp); 302 if (softsp->environ_oksp) 303 kstat_delete(softsp->environ_oksp); 304 305 /* 306 * remove from soft state pointer from the singly linked list of 307 * soft state pointers for temperature monitoring. 308 */ 309 mutex_enter(&overtemp_mutex); 310 311 /* 312 * find the soft state for this instance in the soft state list 313 * and remove it from the list 314 */ 315 for (temp = tempsp_list, vect = &tempsp_list; temp != NULL; 316 vect = &temp->next, temp = temp->next) { 317 if (temp == softsp) { 318 *vect = temp->next; 319 break; 320 } 321 } 322 323 mutex_exit(&overtemp_mutex); 324 325 /* unmap the registers (if they have been mapped) */ 326 if (softsp->temp_reg) 327 ddi_unmap_regs(devi, 0, (caddr_t *)&softsp->temp_reg, 0, 0); 328 329 /* deallocate the soft state instance */ 330 ddi_soft_state_free(environp, instance); 331 332 ddi_prop_remove_all(devi); 333 334 return (DDI_SUCCESS); 335 } 336 337 static int 338 environ_init(struct environ_soft_state *softsp) 339 { 340 uchar_t tmp; 341 342 /* 343 * If this environment node is on a CPU-less system board, i.e., 344 * board type MEM_TYPE, then we do not want to map in, read 345 * the temperature register, create the polling entry for 346 * the overtemp polling thread, or create a kstat entry. 347 * 348 * The reason for this is that when no CPU modules are present 349 * on a CPU/Memory board, then the thermistors are not present, 350 * and the output of the A/D convertor is the max 8 bit value (0xFF) 351 */ 352 if (fhc_bd_type(softsp->board) == MEM_BOARD) { 353 return (DDI_SUCCESS); 354 } 355 356 /* 357 * Map in the temperature register. Once the temperature register 358 * is mapped, the timeout thread can read the temperature and 359 * update the temperature in the softsp. 360 */ 361 if (ddi_map_regs(softsp->dip, 0, 362 (caddr_t *)&softsp->temp_reg, 0, 0)) { 363 cmn_err(CE_WARN, "environ%d: unable to map temperature " 364 "register", ddi_get_instance(softsp->dip)); 365 return (DDI_FAILURE); 366 } 367 368 /* Initialize the temperature */ 369 init_temp_arrays(&softsp->tempstat); 370 371 /* 372 * Do a priming read on the ADC, and throw away the first value 373 * read. This is a feature of the ADC hardware. After a power cycle 374 * it does not contains valid data until a read occurs. 375 */ 376 tmp = *(softsp->temp_reg); 377 378 /* Wait 30 usec for ADC hardware to stabilize. */ 379 DELAY(30); 380 381 #ifdef lint 382 tmp = tmp; 383 #endif 384 385 /* 386 * Now add this soft state structure to the front of the linked list 387 * of soft state structures. 388 */ 389 mutex_enter(&overtemp_mutex); 390 softsp->next = tempsp_list; 391 tempsp_list = softsp; 392 mutex_exit(&overtemp_mutex); 393 394 /* Create kstats for this instance of the environ driver */ 395 environ_add_temp_kstats(softsp); 396 397 return (DDI_SUCCESS); 398 } 399 400 /* ARGSUSED */ 401 static void 402 overtemp_wakeup(void *arg) 403 { 404 /* 405 * grab mutex to guarantee that our wakeup call 406 * arrives after we go to sleep -- so we can't sleep forever. 407 */ 408 mutex_enter(&overtemp_mutex); 409 cv_signal(&overtemp_cv); 410 mutex_exit(&overtemp_mutex); 411 } 412 413 /* 414 * This function polls all the system board digital temperature registers 415 * and stores them in the history buffers using the fhc driver support 416 * routines. 417 * The temperature detected must then be checked against our current 418 * policy for what to do in the case of overtemperature situations. We 419 * must also allow for manufacturing's use of a heat chamber. 420 */ 421 static void 422 environ_overtemp_poll(void) 423 { 424 struct environ_soft_state *list; 425 callb_cpr_t cprinfo; 426 427 CALLB_CPR_INIT(&cprinfo, &overtemp_mutex, callb_generic_cpr, "environ"); 428 429 /* The overtemp data strcutures are protected by a mutex. */ 430 mutex_enter(&overtemp_mutex); 431 432 while (environ_do_overtemp_thread) { 433 434 /* 435 * for each environment node that has been attached, 436 * read it and check for overtemp. 437 */ 438 for (list = tempsp_list; list != NULL; list = list->next) { 439 if (list->temp_reg == NULL) { 440 continue; 441 } 442 443 update_temp(list->pdip, &list->tempstat, 444 *(list->temp_reg)); 445 } 446 447 CALLB_CPR_SAFE_BEGIN(&cprinfo); 448 449 /* now have this thread sleep for a while */ 450 (void) timeout(overtemp_wakeup, NULL, overtemp_timeout_sec*hz); 451 452 cv_wait(&overtemp_cv, &overtemp_mutex); 453 454 CALLB_CPR_SAFE_END(&cprinfo, &overtemp_mutex); 455 } 456 CALLB_CPR_EXIT(&cprinfo); 457 thread_exit(); 458 /* NOTREACHED */ 459 } 460 461 void 462 environ_add_temp_kstats(struct environ_soft_state *softsp) 463 { 464 struct kstat *tksp; 465 struct kstat *ttsp; /* environ temperature test kstat */ 466 467 /* 468 * Create the overtemp kstat required for the environment driver. 469 * The kstat instances are tagged with the physical board number 470 * instead of ddi instance number. 471 */ 472 if ((tksp = kstat_create("unix", softsp->board, 473 OVERTEMP_KSTAT_NAME, "misc", KSTAT_TYPE_RAW, 474 sizeof (struct temp_stats), KSTAT_FLAG_PERSISTENT)) == NULL) { 475 cmn_err(CE_WARN, "environ%d: temp kstat_create failed", 476 ddi_get_instance(softsp->dip)); 477 } else { 478 tksp->ks_update = overtemp_kstat_update; 479 tksp->ks_private = (void *) &softsp->tempstat; 480 softsp->environ_ksp = tksp; 481 kstat_install(tksp); 482 } 483 484 /* 485 * Create the temperature override kstat, for testability. 486 * The kstat instances are tagged with the physical board number 487 * instead of ddi instance number. 488 */ 489 if ((ttsp = kstat_create("unix", softsp->board, 490 TEMP_OVERRIDE_KSTAT_NAME, "misc", KSTAT_TYPE_RAW, sizeof (short), 491 KSTAT_FLAG_PERSISTENT | KSTAT_FLAG_WRITABLE)) == NULL) { 492 cmn_err(CE_WARN, "environ%d: temp override kstat_create failed", 493 ddi_get_instance(softsp->dip)); 494 } else { 495 ttsp->ks_update = temp_override_kstat_update; 496 ttsp->ks_private = (void *) &softsp->tempstat.override; 497 softsp->environ_oksp = ttsp; 498 kstat_install(ttsp); 499 } 500 } 501