environ.c revision 1341:6d7c4f090a72
1219019Sgabor/* 2219019Sgabor * CDDL HEADER START 3219019Sgabor * 4219019Sgabor * The contents of this file are subject to the terms of the 5219019Sgabor * Common Development and Distribution License (the "License"). 6219019Sgabor * You may not use this file except in compliance with the License. 7219019Sgabor * 8219019Sgabor * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9219019Sgabor * or http://www.opensolaris.org/os/licensing. 10219019Sgabor * See the License for the specific language governing permissions 11219019Sgabor * and limitations under the License. 12219019Sgabor * 13219019Sgabor * When distributing Covered Code, include this CDDL HEADER in each 14219019Sgabor * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15219019Sgabor * If applicable, add the following below this CDDL HEADER, with the 16219019Sgabor * fields enclosed by brackets "[]" replaced with your own identifying 17219019Sgabor * information: Portions Copyright [yyyy] [name of copyright owner] 18219019Sgabor * 19219019Sgabor * CDDL HEADER END 20219019Sgabor */ 21219019Sgabor 22219019Sgabor/* 23219019Sgabor * Copyright 2002 Sun Microsystems, Inc. All rights reserved. 24219019Sgabor * Use is subject to license terms. 25219019Sgabor */ 26219019Sgabor 27219019Sgabor#pragma ident "%Z%%M% %I% %E% SMI" 28219019Sgabor 29219019Sgabor#include <sys/types.h> 30219019Sgabor#include <sys/conf.h> 31219019Sgabor#include <sys/ddi.h> 32219019Sgabor#include <sys/sunddi.h> 33219019Sgabor#include <sys/ddi_impldefs.h> 34219019Sgabor#include <sys/obpdefs.h> 35219019Sgabor#include <sys/cmn_err.h> 36219019Sgabor#include <sys/errno.h> 37219019Sgabor#include <sys/kmem.h> 38219019Sgabor#include <sys/debug.h> 39219019Sgabor#include <sys/sysmacros.h> 40219019Sgabor#include <sys/ivintr.h> 41219019Sgabor#include <sys/callb.h> 42219019Sgabor#include <sys/autoconf.h> 43219019Sgabor#include <sys/intreg.h> 44219019Sgabor#include <sys/modctl.h> 45219019Sgabor#include <sys/proc.h> 46219019Sgabor#include <sys/disp.h> 47219019Sgabor#include <sys/fhc.h> 48219019Sgabor#include <sys/environ.h> 49219019Sgabor 50219019Sgabor/* Useful debugging Stuff */ 51219019Sgabor#include <sys/nexusdebug.h> 52219019Sgabor 53219019Sgabor/* 54219019Sgabor * Function prototypes 55219019Sgabor */ 56219019Sgaborstatic int environ_attach(dev_info_t *devi, ddi_attach_cmd_t cmd); 57219019Sgabor 58219019Sgaborstatic int environ_detach(dev_info_t *devi, ddi_detach_cmd_t cmd); 59219019Sgabor 60219019Sgaborstatic int environ_init(struct environ_soft_state *softsp); 61219019Sgabor 62219019Sgaborvoid environ_add_temp_kstats(struct environ_soft_state *softsp); 63219019Sgabor 64219019Sgaborstatic void overtemp_wakeup(void *); 65219019Sgabor 66219019Sgaborstatic void environ_overtemp_poll(void); 67219019Sgabor 68219019Sgabor/* 69219019Sgabor * Configuration data structures 70219019Sgabor */ 71219019Sgaborstatic struct cb_ops environ_cb_ops = { 72219019Sgabor nulldev, /* open */ 73219019Sgabor nulldev, /* close */ 74219019Sgabor nulldev, /* strategy */ 75219019Sgabor nulldev, /* print */ 76219019Sgabor nodev, /* dump */ 77219019Sgabor nulldev, /* read */ 78219019Sgabor nulldev, /* write */ 79219019Sgabor nulldev, /* ioctl */ 80219019Sgabor nodev, /* devmap */ 81219019Sgabor nodev, /* mmap */ 82219019Sgabor nodev, /* segmap */ 83219019Sgabor nochpoll, /* poll */ 84219019Sgabor ddi_prop_op, /* cb_prop_op */ 85219019Sgabor 0, /* streamtab */ 86219019Sgabor D_MP | D_NEW | D_HOTPLUG, /* Driver compatibility flag */ 87219019Sgabor CB_REV, /* rev */ 88219019Sgabor nodev, /* cb_aread */ 89219019Sgabor nodev /* cb_awrite */ 90219019Sgabor}; 91219019Sgabor 92219019Sgaborstatic struct dev_ops environ_ops = { 93219019Sgabor DEVO_REV, /* devo_rev, */ 94219019Sgabor 0, /* refcnt */ 95219019Sgabor ddi_no_info, /* getinfo */ 96219019Sgabor nulldev, /* identify */ 97219019Sgabor nulldev, /* probe */ 98219019Sgabor environ_attach, /* attach */ 99219019Sgabor environ_detach, /* detach */ 100219019Sgabor nulldev, /* reset */ 101219019Sgabor &environ_cb_ops, /* cb_ops */ 102219019Sgabor (struct bus_ops *)0, /* bus_ops */ 103219019Sgabor nulldev /* power */ 104219019Sgabor}; 105219019Sgabor 106219019Sgaborvoid *environp; /* environ soft state hook */ 107219019Sgabor 108219019Sgabor/* 109219019Sgabor * Mutex used to protect the soft state list and their data. 110219019Sgabor */ 111219019Sgaborstatic kmutex_t overtemp_mutex; 112219019Sgabor 113219019Sgabor/* The CV is used to wakeup the thread when needed. */ 114219019Sgaborstatic kcondvar_t overtemp_cv; 115219019Sgabor 116219019Sgabor/* linked list of all environ soft states */ 117219019Sgaborstruct environ_soft_state *tempsp_list = NULL; 118219019Sgabor 119219019Sgabor/* overtemp polling routine timeout delay */ 120219019Sgaborstatic int overtemp_timeout_sec = OVERTEMP_TIMEOUT_SEC; 121219019Sgabor 122219019Sgabor/* Should the environ_overtemp_poll thread be running? */ 123219019Sgaborstatic int environ_do_overtemp_thread = 1; 124219019Sgabor 125219019Sgabor/* Indicates whether or not the overtemp thread has been started */ 126219019Sgaborstatic int environ_overtemp_thread_started = 0; 127219019Sgabor 128219019Sgaborextern struct mod_ops mod_driverops; 129219019Sgabor 130219019Sgaborstatic struct modldrv modldrv = { 131219019Sgabor &mod_driverops, /* module type, this one is a driver */ 132219019Sgabor "Environment Leaf v%I%", /* name of module */ 133219019Sgabor &environ_ops, /* driver ops */ 134219019Sgabor}; 135219019Sgabor 136219019Sgaborstatic struct modlinkage modlinkage = { 137 MODREV_1, 138 (void *)&modldrv, 139 NULL 140}; 141 142#ifndef lint 143static char _depends_on[] = "drv/fhc"; 144#endif /* lint */ 145 146/* 147 * These are the module initialization routines. 148 */ 149 150int 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 162int 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 174int 175_info(struct modinfo *modinfop) 176{ 177 return (mod_info(&modlinkage, modinfop)); 178} 179 180static int 181environ_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 devi, 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 259bad: 260 ddi_soft_state_free(environp, instance); 261 return (DDI_FAILURE); 262} 263 264/* ARGSUSED */ 265static int 266environ_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 337static int 338environ_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 */ 401static void 402overtemp_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 */ 421static void 422environ_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 461void 462environ_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