xref: /onnv-gate/usr/src/uts/sun4u/sunfire/io/environ.c (revision 11311:639e7bc0b42f)
11341Sstevel /*
21341Sstevel  * CDDL HEADER START
31341Sstevel  *
41341Sstevel  * The contents of this file are subject to the terms of the
51341Sstevel  * Common Development and Distribution License (the "License").
61341Sstevel  * You may not use this file except in compliance with the License.
71341Sstevel  *
81341Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91341Sstevel  * or http://www.opensolaris.org/os/licensing.
101341Sstevel  * See the License for the specific language governing permissions
111341Sstevel  * and limitations under the License.
121341Sstevel  *
131341Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
141341Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151341Sstevel  * If applicable, add the following below this CDDL HEADER, with the
161341Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
171341Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
181341Sstevel  *
191341Sstevel  * CDDL HEADER END
201341Sstevel  */
211341Sstevel 
221341Sstevel /*
23*11311SSurya.Prakki@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
241341Sstevel  * Use is subject to license terms.
251341Sstevel  */
261341Sstevel 
271341Sstevel 
281341Sstevel #include <sys/types.h>
291341Sstevel #include <sys/conf.h>
301341Sstevel #include <sys/ddi.h>
311341Sstevel #include <sys/sunddi.h>
321341Sstevel #include <sys/ddi_impldefs.h>
331341Sstevel #include <sys/obpdefs.h>
341341Sstevel #include <sys/cmn_err.h>
351341Sstevel #include <sys/errno.h>
361341Sstevel #include <sys/kmem.h>
371341Sstevel #include <sys/debug.h>
381341Sstevel #include <sys/sysmacros.h>
391341Sstevel #include <sys/ivintr.h>
401341Sstevel #include <sys/callb.h>
411341Sstevel #include <sys/autoconf.h>
421341Sstevel #include <sys/intreg.h>
431341Sstevel #include <sys/modctl.h>
441341Sstevel #include <sys/proc.h>
451341Sstevel #include <sys/disp.h>
461341Sstevel #include <sys/fhc.h>
471341Sstevel #include <sys/environ.h>
481341Sstevel 
491341Sstevel /* Useful debugging Stuff */
501341Sstevel #include <sys/nexusdebug.h>
511341Sstevel 
521341Sstevel /*
531341Sstevel  * Function prototypes
541341Sstevel  */
551341Sstevel static int environ_attach(dev_info_t *devi, ddi_attach_cmd_t cmd);
561341Sstevel 
571341Sstevel static int environ_detach(dev_info_t *devi, ddi_detach_cmd_t cmd);
581341Sstevel 
591341Sstevel static int environ_init(struct environ_soft_state *softsp);
601341Sstevel 
611341Sstevel void environ_add_temp_kstats(struct environ_soft_state *softsp);
621341Sstevel 
631341Sstevel static void overtemp_wakeup(void *);
641341Sstevel 
651341Sstevel static void environ_overtemp_poll(void);
661341Sstevel 
671341Sstevel /*
681341Sstevel  * Configuration data structures
691341Sstevel  */
701341Sstevel static struct cb_ops environ_cb_ops = {
711341Sstevel 	nulldev,			/* open */
721341Sstevel 	nulldev,			/* close */
731341Sstevel 	nulldev,			/* strategy */
741341Sstevel 	nulldev,			/* print */
751341Sstevel 	nodev,				/* dump */
761341Sstevel 	nulldev,			/* read */
771341Sstevel 	nulldev,			/* write */
781341Sstevel 	nulldev,			/* ioctl */
791341Sstevel 	nodev,				/* devmap */
801341Sstevel 	nodev,				/* mmap */
811341Sstevel 	nodev,				/* segmap */
821341Sstevel 	nochpoll,			/* poll */
831341Sstevel 	ddi_prop_op,			/* cb_prop_op */
841341Sstevel 	0,				/* streamtab */
851341Sstevel 	D_MP | D_NEW | D_HOTPLUG,	/* Driver compatibility flag */
861341Sstevel 	CB_REV,				/* rev */
871341Sstevel 	nodev,				/* cb_aread */
881341Sstevel 	nodev				/* cb_awrite */
891341Sstevel };
901341Sstevel 
911341Sstevel static struct dev_ops environ_ops = {
921341Sstevel 	DEVO_REV,			/* devo_rev, */
931341Sstevel 	0,				/* refcnt */
941341Sstevel 	ddi_no_info,			/* getinfo */
951341Sstevel 	nulldev,			/* identify */
961341Sstevel 	nulldev,			/* probe */
971341Sstevel 	environ_attach,			/* attach */
981341Sstevel 	environ_detach,			/* detach */
991341Sstevel 	nulldev,			/* reset */
1001341Sstevel 	&environ_cb_ops,		/* cb_ops */
1011341Sstevel 	(struct bus_ops *)0,		/* bus_ops */
1027656SSherry.Moore@Sun.COM 	nulldev,			/* power */
1037656SSherry.Moore@Sun.COM 	ddi_quiesce_not_needed,			/* quiesce */
1041341Sstevel };
1051341Sstevel 
1061341Sstevel void *environp;			/* environ soft state hook */
1071341Sstevel 
1081341Sstevel /*
1091341Sstevel  * Mutex used to protect the soft state list and their data.
1101341Sstevel  */
1111341Sstevel static kmutex_t overtemp_mutex;
1121341Sstevel 
1131341Sstevel /* The CV is used to wakeup the thread when needed. */
1141341Sstevel static kcondvar_t overtemp_cv;
1151341Sstevel 
1161341Sstevel /* linked list of all environ soft states */
1171341Sstevel struct environ_soft_state *tempsp_list = NULL;
1181341Sstevel 
1191341Sstevel /* overtemp polling routine timeout delay */
1201341Sstevel static int overtemp_timeout_sec = OVERTEMP_TIMEOUT_SEC;
1211341Sstevel 
1221341Sstevel /* Should the environ_overtemp_poll thread be running? */
1231341Sstevel static int environ_do_overtemp_thread = 1;
1241341Sstevel 
1251341Sstevel /* Indicates whether or not the overtemp thread has been started */
1261341Sstevel static int environ_overtemp_thread_started = 0;
1271341Sstevel 
1281341Sstevel extern struct mod_ops mod_driverops;
1291341Sstevel 
1301341Sstevel static struct modldrv modldrv = {
1317656SSherry.Moore@Sun.COM 	&mod_driverops,		/* module type, this one is a driver */
1327656SSherry.Moore@Sun.COM 	"Environment Leaf",	/* name of module */
1337656SSherry.Moore@Sun.COM 	&environ_ops,		/* driver ops */
1341341Sstevel };
1351341Sstevel 
1361341Sstevel static struct modlinkage modlinkage = {
1371341Sstevel 	MODREV_1,
1381341Sstevel 	(void *)&modldrv,
1391341Sstevel 	NULL
1401341Sstevel };
1411341Sstevel 
1421341Sstevel #ifndef lint
1431366Spetede char _depends_on[] = "drv/fhc";
1441341Sstevel #endif  /* lint */
1451341Sstevel 
1461341Sstevel /*
1471341Sstevel  * These are the module initialization routines.
1481341Sstevel  */
1491341Sstevel 
1501341Sstevel int
_init(void)1511341Sstevel _init(void)
1521341Sstevel {
1531341Sstevel 	int error;
1541341Sstevel 
1551341Sstevel 	if ((error = ddi_soft_state_init(&environp,
1561341Sstevel 	    sizeof (struct environ_soft_state), 1)) != 0)
1571341Sstevel 		return (error);
1581341Sstevel 
1591341Sstevel 	return (mod_install(&modlinkage));
1601341Sstevel }
1611341Sstevel 
1621341Sstevel int
_fini(void)1631341Sstevel _fini(void)
1641341Sstevel {
1651341Sstevel 	int error;
1661341Sstevel 
1671341Sstevel 	if ((error = mod_remove(&modlinkage)) != 0)
1681341Sstevel 		return (error);
1691341Sstevel 
1701341Sstevel 	ddi_soft_state_fini(&environp);
1711341Sstevel 	return (0);
1721341Sstevel }
1731341Sstevel 
1741341Sstevel int
_info(struct modinfo * modinfop)1751341Sstevel _info(struct modinfo *modinfop)
1761341Sstevel {
1771341Sstevel 	return (mod_info(&modlinkage, modinfop));
1781341Sstevel }
1791341Sstevel 
1801341Sstevel static int
environ_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)1811341Sstevel environ_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
1821341Sstevel {
1831341Sstevel 	struct environ_soft_state *softsp;
1841341Sstevel 	int instance;
1851341Sstevel 
1861341Sstevel 	switch (cmd) {
1871341Sstevel 	case DDI_ATTACH:
1881341Sstevel 		break;
1891341Sstevel 
1901341Sstevel 	case DDI_RESUME:
1911341Sstevel 		return (DDI_SUCCESS);
1921341Sstevel 
1931341Sstevel 	default:
1941341Sstevel 		return (DDI_FAILURE);
1951341Sstevel 	}
1961341Sstevel 
1971341Sstevel 	instance = ddi_get_instance(devi);
1981341Sstevel 
1991341Sstevel 	if (ddi_soft_state_zalloc(environp, instance) != DDI_SUCCESS)
2001341Sstevel 		return (DDI_FAILURE);
2011341Sstevel 
2021341Sstevel 	softsp = ddi_get_soft_state(environp, instance);
2031341Sstevel 
2041341Sstevel 	/* Set the dip in the soft state */
2051341Sstevel 	softsp->dip = devi;
2061341Sstevel 
2071341Sstevel 	/*
2081341Sstevel 	 * The DDI documentation on ddi_getprop() routine says that
2091341Sstevel 	 * you should always use the real dev_t when calling it,
2101341Sstevel 	 * but all calls found in uts use either DDI_DEV_T_ANY
2111341Sstevel 	 * or DDI_DEV_T_NONE. No notes either on how to find the real
2121341Sstevel 	 * dev_t. So we are doing it in two steps.
2131341Sstevel 	 */
2141341Sstevel 	softsp->pdip = ddi_get_parent(softsp->dip);
2151341Sstevel 
2161341Sstevel 	if ((softsp->board = (int)ddi_getprop(DDI_DEV_T_ANY, softsp->pdip,
2171341Sstevel 	    DDI_PROP_DONTPASS, OBP_BOARDNUM, -1)) == -1) {
2181341Sstevel 		cmn_err(CE_WARN, "environ%d: unable to retrieve %s property",
2197656SSherry.Moore@Sun.COM 		    instance, OBP_BOARDNUM);
2201341Sstevel 		goto bad;
2211341Sstevel 	}
2221341Sstevel 
2231341Sstevel 	DPRINTF(ENVIRON_ATTACH_DEBUG, ("environ: devi= 0x%p\n, softsp=0x%p,",
224*11311SSurya.Prakki@Sun.COM 	    (void *)devi, (void *)softsp));
2251341Sstevel 
2261341Sstevel 	/*
2271341Sstevel 	 * Init the temperature device here. We start the overtemp
2281341Sstevel 	 * polling thread here.
2291341Sstevel 	 */
2301341Sstevel 	if (environ_init(softsp) != DDI_SUCCESS)
2311341Sstevel 		goto bad;
2321341Sstevel 
2331341Sstevel 	/* nothing to suspend/resume here */
2341341Sstevel 	(void) ddi_prop_update_string(DDI_DEV_T_NONE, devi,
2357656SSherry.Moore@Sun.COM 	    "pm-hardware-state", "no-suspend-resume");
2361341Sstevel 
2371341Sstevel 	ddi_report_dev(devi);
2381341Sstevel 
2391341Sstevel 	if (environ_overtemp_thread_started == 0) {
2401341Sstevel 		/*
2411341Sstevel 		 * set up the overtemp mutex and condition variable before
2421341Sstevel 		 * starting the thread.
2431341Sstevel 		 */
2441341Sstevel 		mutex_init(&overtemp_mutex, NULL, MUTEX_DEFAULT, NULL);
2451341Sstevel 		cv_init(&overtemp_cv, NULL, CV_DRIVER, NULL);
2461341Sstevel 
2471341Sstevel 		/* Start the overtemp polling thread now. */
2481341Sstevel 		(void) thread_create(NULL, 0, (void (*)())environ_overtemp_poll,
2491341Sstevel 		    NULL, 0, &p0, TS_RUN, minclsyspri);
2501341Sstevel 		environ_overtemp_thread_started++;
2511341Sstevel 	}
2521341Sstevel 
2531341Sstevel 	(void) fhc_bdlist_lock(softsp->board);
2541341Sstevel 	fhc_bd_env_set(softsp->board, (void *)softsp);
2551341Sstevel 	fhc_bdlist_unlock();
2561341Sstevel 
2571341Sstevel 	return (DDI_SUCCESS);
2581341Sstevel 
2591341Sstevel bad:
2601341Sstevel 	ddi_soft_state_free(environp, instance);
2611341Sstevel 	return (DDI_FAILURE);
2621341Sstevel }
2631341Sstevel 
2641341Sstevel /* ARGSUSED */
2651341Sstevel static int
environ_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)2661341Sstevel environ_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
2671341Sstevel {
2681341Sstevel 	int instance;
2691341Sstevel 	struct environ_soft_state *softsp;
2701341Sstevel 	struct environ_soft_state **vect;	/* used in list deletion */
2711341Sstevel 	struct environ_soft_state *temp;	/* used in list deletion */
2721341Sstevel 
2731341Sstevel 	/* get the instance of this devi */
2741341Sstevel 	instance = ddi_get_instance(devi);
2751341Sstevel 
2761341Sstevel 	/* get the soft state pointer for this device node */
2771341Sstevel 	softsp = ddi_get_soft_state(environp, instance);
2781341Sstevel 
2791341Sstevel 	switch (cmd) {
2801341Sstevel 	case DDI_SUSPEND:
2811341Sstevel 		return (DDI_SUCCESS);
2821341Sstevel 
2831341Sstevel 	case DDI_DETACH:
2841341Sstevel 		(void) fhc_bdlist_lock(softsp->board);
2851341Sstevel 		if (fhc_bd_detachable(softsp->board))
2861341Sstevel 			break;
2871341Sstevel 		else
2881341Sstevel 			fhc_bdlist_unlock();
2891341Sstevel 		/* FALLTHROUGH */
2901341Sstevel 
2911341Sstevel 	default:
2921341Sstevel 		return (DDI_FAILURE);
2931341Sstevel 	}
2941341Sstevel 
2951341Sstevel 	fhc_bd_env_set(softsp->board, NULL);
2961341Sstevel 
2971341Sstevel 	fhc_bdlist_unlock();
2981341Sstevel 
2991341Sstevel 	/* remove the environmental kstats if they were allocated */
3001341Sstevel 	if (softsp->environ_ksp)
3011341Sstevel 		kstat_delete(softsp->environ_ksp);
3021341Sstevel 	if (softsp->environ_oksp)
3031341Sstevel 		kstat_delete(softsp->environ_oksp);
3041341Sstevel 
3051341Sstevel 	/*
3061341Sstevel 	 * remove from soft state pointer from the singly linked list of
3071341Sstevel 	 * soft state pointers for temperature monitoring.
3081341Sstevel 	 */
3091341Sstevel 	mutex_enter(&overtemp_mutex);
3101341Sstevel 
3111341Sstevel 	/*
3121341Sstevel 	 * find the soft state for this instance in the soft state list
3131341Sstevel 	 * and remove it from the list
3141341Sstevel 	 */
3151341Sstevel 	for (temp = tempsp_list, vect = &tempsp_list; temp != NULL;
3161341Sstevel 	    vect = &temp->next, temp = temp->next) {
3171341Sstevel 		if (temp == softsp) {
3181341Sstevel 			*vect = temp->next;
3191341Sstevel 			break;
3201341Sstevel 		}
3211341Sstevel 	}
3221341Sstevel 
3231341Sstevel 	mutex_exit(&overtemp_mutex);
3241341Sstevel 
3251341Sstevel 	/* unmap the registers (if they have been mapped) */
3261341Sstevel 	if (softsp->temp_reg)
3271341Sstevel 		ddi_unmap_regs(devi, 0, (caddr_t *)&softsp->temp_reg, 0, 0);
3281341Sstevel 
3291341Sstevel 	/* deallocate the soft state instance */
3301341Sstevel 	ddi_soft_state_free(environp, instance);
3311341Sstevel 
3321341Sstevel 	ddi_prop_remove_all(devi);
3331341Sstevel 
3341341Sstevel 	return (DDI_SUCCESS);
3351341Sstevel }
3361341Sstevel 
3371341Sstevel static int
environ_init(struct environ_soft_state * softsp)3381341Sstevel environ_init(struct environ_soft_state *softsp)
3391341Sstevel {
3401341Sstevel 	uchar_t tmp;
3411341Sstevel 
3421341Sstevel 	/*
3431341Sstevel 	 * If this environment node is on a CPU-less system board, i.e.,
3441341Sstevel 	 * board type MEM_TYPE, then we do not want to map in, read
3451341Sstevel 	 * the temperature register, create the polling entry for
3461341Sstevel 	 * the overtemp polling thread, or create a kstat entry.
3471341Sstevel 	 *
3481341Sstevel 	 * The reason for this is that when no CPU modules are present
3491341Sstevel 	 * on a CPU/Memory board, then the thermistors are not present,
3501341Sstevel 	 * and the output of the A/D convertor is the max 8 bit value (0xFF)
3511341Sstevel 	 */
3521341Sstevel 	if (fhc_bd_type(softsp->board) == MEM_BOARD) {
3531341Sstevel 		return (DDI_SUCCESS);
3541341Sstevel 	}
3551341Sstevel 
3561341Sstevel 	/*
3571341Sstevel 	 * Map in the temperature register. Once the temperature register
3581341Sstevel 	 * is mapped, the timeout thread can read the temperature and
3591341Sstevel 	 * update the temperature in the softsp.
3601341Sstevel 	 */
3611341Sstevel 	if (ddi_map_regs(softsp->dip, 0,
3621341Sstevel 	    (caddr_t *)&softsp->temp_reg, 0, 0)) {
3631341Sstevel 		cmn_err(CE_WARN, "environ%d: unable to map temperature "
3647656SSherry.Moore@Sun.COM 		    "register", ddi_get_instance(softsp->dip));
3651341Sstevel 		return (DDI_FAILURE);
3661341Sstevel 	}
3671341Sstevel 
3681341Sstevel 	/* Initialize the temperature */
3691341Sstevel 	init_temp_arrays(&softsp->tempstat);
3701341Sstevel 
3711341Sstevel 	/*
3721341Sstevel 	 * Do a priming read on the ADC, and throw away the first value
3731341Sstevel 	 * read. This is a feature of the ADC hardware. After a power cycle
3741341Sstevel 	 * it does not contains valid data until a read occurs.
3751341Sstevel 	 */
3761341Sstevel 	tmp = *(softsp->temp_reg);
3771341Sstevel 
3781341Sstevel 	/* Wait 30 usec for ADC hardware to stabilize. */
3791341Sstevel 	DELAY(30);
3801341Sstevel 
3811341Sstevel #ifdef lint
3821341Sstevel 	tmp = tmp;
3831341Sstevel #endif
3841341Sstevel 
3851341Sstevel 	/*
3861341Sstevel 	 * Now add this soft state structure to the front of the linked list
3871341Sstevel 	 * of soft state structures.
3881341Sstevel 	 */
3891341Sstevel 	mutex_enter(&overtemp_mutex);
3901341Sstevel 	softsp->next = tempsp_list;
3911341Sstevel 	tempsp_list = softsp;
3921341Sstevel 	mutex_exit(&overtemp_mutex);
3931341Sstevel 
3941341Sstevel 	/* Create kstats for this instance of the environ driver */
3951341Sstevel 	environ_add_temp_kstats(softsp);
3961341Sstevel 
3971341Sstevel 	return (DDI_SUCCESS);
3981341Sstevel }
3991341Sstevel 
4001341Sstevel /* ARGSUSED */
4011341Sstevel static void
overtemp_wakeup(void * arg)4021341Sstevel overtemp_wakeup(void *arg)
4031341Sstevel {
4041341Sstevel 	/*
4051341Sstevel 	 * grab mutex to guarantee that our wakeup call
4061341Sstevel 	 * arrives after we go to sleep -- so we can't sleep forever.
4071341Sstevel 	 */
4081341Sstevel 	mutex_enter(&overtemp_mutex);
4091341Sstevel 	cv_signal(&overtemp_cv);
4101341Sstevel 	mutex_exit(&overtemp_mutex);
4111341Sstevel }
4121341Sstevel 
4131341Sstevel /*
4141341Sstevel  * This function polls all the system board digital temperature registers
4151341Sstevel  * and stores them in the history buffers using the fhc driver support
4161341Sstevel  * routines.
4171341Sstevel  * The temperature detected must then be checked against our current
4181341Sstevel  * policy for what to do in the case of overtemperature situations. We
4191341Sstevel  * must also allow for manufacturing's use of a heat chamber.
4201341Sstevel  */
4211341Sstevel static void
environ_overtemp_poll(void)4221341Sstevel environ_overtemp_poll(void)
4231341Sstevel {
4241341Sstevel 	struct environ_soft_state *list;
4251341Sstevel 	callb_cpr_t cprinfo;
4261341Sstevel 
4271341Sstevel 	CALLB_CPR_INIT(&cprinfo, &overtemp_mutex, callb_generic_cpr, "environ");
4281341Sstevel 
4291341Sstevel 	/* The overtemp data strcutures are protected by a mutex. */
4301341Sstevel 	mutex_enter(&overtemp_mutex);
4311341Sstevel 
4321341Sstevel 	while (environ_do_overtemp_thread) {
4331341Sstevel 
4341341Sstevel 		/*
4351341Sstevel 		 * for each environment node that has been attached,
4361341Sstevel 		 * read it and check for overtemp.
4371341Sstevel 		 */
4381341Sstevel 		for (list = tempsp_list; list != NULL; list = list->next) {
4391341Sstevel 			if (list->temp_reg == NULL) {
4401341Sstevel 				continue;
4411341Sstevel 			}
4421341Sstevel 
4431341Sstevel 			update_temp(list->pdip, &list->tempstat,
4447656SSherry.Moore@Sun.COM 			    *(list->temp_reg));
4451341Sstevel 		}
4461341Sstevel 
4471341Sstevel 		CALLB_CPR_SAFE_BEGIN(&cprinfo);
4481341Sstevel 
4491341Sstevel 		/* now have this thread sleep for a while */
4501341Sstevel 		(void) timeout(overtemp_wakeup, NULL, overtemp_timeout_sec*hz);
4511341Sstevel 
4521341Sstevel 		cv_wait(&overtemp_cv, &overtemp_mutex);
4531341Sstevel 
4541341Sstevel 		CALLB_CPR_SAFE_END(&cprinfo, &overtemp_mutex);
4551341Sstevel 	}
4561341Sstevel 	CALLB_CPR_EXIT(&cprinfo);
4571341Sstevel 	thread_exit();
4581341Sstevel 	/* NOTREACHED */
4591341Sstevel }
4601341Sstevel 
4611341Sstevel void
environ_add_temp_kstats(struct environ_soft_state * softsp)4621341Sstevel environ_add_temp_kstats(struct environ_soft_state *softsp)
4631341Sstevel {
4641341Sstevel 	struct  kstat   *tksp;
4651341Sstevel 	struct  kstat   *ttsp;	/* environ temperature test kstat */
4661341Sstevel 
4671341Sstevel 	/*
4681341Sstevel 	 * Create the overtemp kstat required for the environment driver.
4691341Sstevel 	 * The kstat instances are tagged with the physical board number
4701341Sstevel 	 * instead of ddi instance number.
4711341Sstevel 	 */
4721341Sstevel 	if ((tksp = kstat_create("unix", softsp->board,
4731341Sstevel 	    OVERTEMP_KSTAT_NAME, "misc", KSTAT_TYPE_RAW,
4741341Sstevel 	    sizeof (struct temp_stats), KSTAT_FLAG_PERSISTENT)) == NULL) {
4751341Sstevel 		cmn_err(CE_WARN, "environ%d: temp kstat_create failed",
4761341Sstevel 			ddi_get_instance(softsp->dip));
4771341Sstevel 	} else {
4781341Sstevel 		tksp->ks_update = overtemp_kstat_update;
4791341Sstevel 		tksp->ks_private = (void *) &softsp->tempstat;
4801341Sstevel 		softsp->environ_ksp = tksp;
4811341Sstevel 		kstat_install(tksp);
4821341Sstevel 	}
4831341Sstevel 
4841341Sstevel 	/*
4851341Sstevel 	 * Create the temperature override kstat, for testability.
4861341Sstevel 	 * The kstat instances are tagged with the physical board number
4871341Sstevel 	 * instead of ddi instance number.
4881341Sstevel 	 */
4891341Sstevel 	if ((ttsp = kstat_create("unix", softsp->board,
4901341Sstevel 	    TEMP_OVERRIDE_KSTAT_NAME, "misc", KSTAT_TYPE_RAW, sizeof (short),
4917656SSherry.Moore@Sun.COM 	    KSTAT_FLAG_PERSISTENT | KSTAT_FLAG_WRITABLE)) == NULL) {
4921341Sstevel 		cmn_err(CE_WARN, "environ%d: temp override kstat_create failed",
4937656SSherry.Moore@Sun.COM 		    ddi_get_instance(softsp->dip));
4941341Sstevel 	} else {
4951341Sstevel 		ttsp->ks_update = temp_override_kstat_update;
4961341Sstevel 		ttsp->ks_private = (void *) &softsp->tempstat.override;
4971341Sstevel 		softsp->environ_oksp = ttsp;
4981341Sstevel 		kstat_install(ttsp);
4991341Sstevel 	}
5001341Sstevel }
501