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