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