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