xref: /illumos-gate/usr/src/uts/sun4u/sunfire/io/environ.c (revision 1a220b56b93ff1dc80855691548503117af4cc10)
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 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
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 };
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 v%I%",	/* 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 		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 
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