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
_init(void)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
_fini(void)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
_info(struct modinfo * modinfop)172 _info(struct modinfo *modinfop)
173 {
174 return (mod_info(&modlinkage, modinfop));
175 }
176
177 static int
environ_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)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
environ_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)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
environ_init(struct environ_soft_state * softsp)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
overtemp_wakeup(void * arg)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
environ_overtemp_poll(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
environ_add_temp_kstats(struct environ_soft_state * softsp)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