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 */
26
27
28 #include <sys/types.h>
29 #include <sys/conf.h>
30 #include <sys/ddi.h>
31 #include <sys/sunddi.h>
32 #include <sys/ddi_impldefs.h>
33 #include <sys/obpdefs.h>
34 #include <sys/cmn_err.h>
35 #include <sys/errno.h>
36 #include <sys/kmem.h>
37 #include <sys/debug.h>
38 #include <sys/sysmacros.h>
39 #include <sys/ivintr.h>
40 #include <sys/callb.h>
41 #include <sys/autoconf.h>
42 #include <sys/intreg.h>
43 #include <sys/modctl.h>
44 #include <sys/proc.h>
45 #include <sys/disp.h>
46 #include <sys/fhc.h>
47 #include <sys/environ.h>
48
49 /* Useful debugging Stuff */
50 #include <sys/nexusdebug.h>
51
52 /*
53 * Function prototypes
54 */
55 static int environ_attach(dev_info_t *devi, ddi_attach_cmd_t cmd);
56
57 static int environ_detach(dev_info_t *devi, ddi_detach_cmd_t cmd);
58
59 static int environ_init(struct environ_soft_state *softsp);
60
61 void environ_add_temp_kstats(struct environ_soft_state *softsp);
62
63 static void overtemp_wakeup(void *);
64
65 static void environ_overtemp_poll(void);
66
67 /*
68 * Configuration data structures
69 */
70 static struct cb_ops environ_cb_ops = {
71 nulldev, /* open */
72 nulldev, /* close */
73 nulldev, /* strategy */
74 nulldev, /* print */
75 nodev, /* dump */
76 nulldev, /* read */
77 nulldev, /* write */
78 nulldev, /* ioctl */
79 nodev, /* devmap */
80 nodev, /* mmap */
81 nodev, /* segmap */
82 nochpoll, /* poll */
83 ddi_prop_op, /* cb_prop_op */
84 0, /* streamtab */
85 D_MP | D_NEW | D_HOTPLUG, /* Driver compatibility flag */
86 CB_REV, /* rev */
87 nodev, /* cb_aread */
88 nodev /* cb_awrite */
89 };
90
91 static struct dev_ops environ_ops = {
92 DEVO_REV, /* devo_rev, */
93 0, /* refcnt */
94 ddi_no_info, /* getinfo */
95 nulldev, /* identify */
96 nulldev, /* probe */
97 environ_attach, /* attach */
98 environ_detach, /* detach */
99 nulldev, /* reset */
100 &environ_cb_ops, /* cb_ops */
101 (struct bus_ops *)0, /* bus_ops */
102 nulldev, /* power */
103 ddi_quiesce_not_needed, /* quiesce */
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", /* 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
_init(void)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
_fini(void)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
_info(struct modinfo * modinfop)175 _info(struct modinfo *modinfop)
176 {
177 return (mod_info(&modlinkage, modinfop));
178 }
179
180 static int
environ_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)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 (void *)devi, (void *)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
environ_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)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
environ_init(struct environ_soft_state * softsp)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
overtemp_wakeup(void * arg)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
environ_overtemp_poll(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
environ_add_temp_kstats(struct environ_soft_state * softsp)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