2 * Copyright (c) 2015 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Sepherosa Ziehau <sepherosa@gmail.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 #include <sys/param.h>
37 #include <sys/kernel.h>
39 #include <sys/malloc.h>
40 #include <sys/module.h>
41 #include <sys/sensors.h>
42 #include <sys/sysctl.h>
43 #include <sys/systm.h>
45 #include <dev/misc/dimm/dimm.h>
47 #define DIMM_TEMP_HIWAT_DEFAULT 85
48 #define DIMM_TEMP_LOWAT_DEFAULT 75
51 TAILQ_ENTRY(dimm_softc) dimm_link;
60 struct ksensordev dimm_sensdev;
61 uint32_t dimm_sens_taskflags; /* DIMM_SENS_TF_ */
63 struct sysctl_ctx_list dimm_sysctl_ctx;
64 struct sysctl_oid *dimm_sysctl_tree;
66 TAILQ_HEAD(dimm_softc_list, dimm_softc);
68 #define DIMM_SENS_TF_TEMP_CRIT 0x1
70 static void dimm_mod_unload(void);
72 /* In the ascending order of dimm_softc.dimm_id */
73 static struct dimm_softc_list dimm_softc_list;
75 static SYSCTL_NODE(_hw, OID_AUTO, dimminfo, CTLFLAG_RD, NULL,
79 dimm_create(int node, int chan, int slot)
81 struct dimm_softc *sc, *after = NULL;
86 TAILQ_FOREACH(sc, &dimm_softc_list, dimm_link) {
88 * Already exists; done.
90 if (sc->dimm_node == node && sc->dimm_chan == chan &&
91 sc->dimm_slot == slot) {
92 KASSERT(sc->dimm_ref > 0, ("invalid dimm reference %d",
100 * Find the lowest usable id.
102 if (sc->dimm_id == dimm_id) {
108 sc = kmalloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO);
109 sc->dimm_node = node;
110 sc->dimm_chan = chan;
111 sc->dimm_slot = slot;
112 sc->dimm_id = dimm_id;
114 sc->dimm_temp_hiwat = DIMM_TEMP_HIWAT_DEFAULT;
115 sc->dimm_temp_lowat = DIMM_TEMP_LOWAT_DEFAULT;
117 ksnprintf(sc->dimm_sensdev.xname, sizeof(sc->dimm_sensdev.xname),
118 "dimm%d", sc->dimm_id);
121 * Create sysctl tree for the location information. Use
122 * same name as the sensor device.
124 sysctl_ctx_init(&sc->dimm_sysctl_ctx);
125 sc->dimm_sysctl_tree = SYSCTL_ADD_NODE(&sc->dimm_sysctl_ctx,
126 SYSCTL_STATIC_CHILDREN(_hw_dimminfo), OID_AUTO,
127 sc->dimm_sensdev.xname, CTLFLAG_RD, 0, "");
128 if (sc->dimm_sysctl_tree != NULL) {
129 SYSCTL_ADD_INT(&sc->dimm_sysctl_ctx,
130 SYSCTL_CHILDREN(sc->dimm_sysctl_tree), OID_AUTO,
131 "node", CTLFLAG_RD, &sc->dimm_node, 0,
132 "CPU node of this DIMM");
133 SYSCTL_ADD_INT(&sc->dimm_sysctl_ctx,
134 SYSCTL_CHILDREN(sc->dimm_sysctl_tree), OID_AUTO,
135 "chan", CTLFLAG_RD, &sc->dimm_chan, 0,
136 "channel of this DIMM");
137 SYSCTL_ADD_INT(&sc->dimm_sysctl_ctx,
138 SYSCTL_CHILDREN(sc->dimm_sysctl_tree), OID_AUTO,
139 "slot", CTLFLAG_RD, &sc->dimm_slot, 0,
140 "slot of this DIMM");
141 SYSCTL_ADD_INT(&sc->dimm_sysctl_ctx,
142 SYSCTL_CHILDREN(sc->dimm_sysctl_tree), OID_AUTO,
143 "temp_hiwat", CTLFLAG_RW, &sc->dimm_temp_hiwat, 0,
144 "Raise alarm once DIMM temperature is above this value "
146 SYSCTL_ADD_INT(&sc->dimm_sysctl_ctx,
147 SYSCTL_CHILDREN(sc->dimm_sysctl_tree), OID_AUTO,
148 "temp_lowat", CTLFLAG_RW, &sc->dimm_temp_lowat, 0,
149 "Cancel alarm once DIMM temperature is below this value "
154 KKASSERT(sc->dimm_id == 0);
155 TAILQ_INSERT_HEAD(&dimm_softc_list, sc, dimm_link);
157 TAILQ_INSERT_AFTER(&dimm_softc_list, after, sc, dimm_link);
160 sensordev_install(&sc->dimm_sensdev);
167 dimm_destroy(struct dimm_softc *sc)
171 KASSERT(sc->dimm_ref > 0, ("invalid dimm reference %d", sc->dimm_ref));
173 if (sc->dimm_ref > 0) {
178 sensordev_deinstall(&sc->dimm_sensdev);
180 TAILQ_REMOVE(&dimm_softc_list, sc, dimm_link);
181 if (sc->dimm_sysctl_tree != NULL)
182 sysctl_ctx_free(&sc->dimm_sysctl_ctx);
190 dimm_sensor_attach(struct dimm_softc *sc, struct ksensor *sens)
192 sensor_attach(&sc->dimm_sensdev, sens);
196 dimm_sensor_detach(struct dimm_softc *sc, struct ksensor *sens)
198 sensor_detach(&sc->dimm_sensdev, sens);
202 dimm_set_temp_thresh(struct dimm_softc *sc, int hiwat, int lowat)
204 sc->dimm_temp_hiwat = hiwat;
205 sc->dimm_temp_lowat = lowat;
209 dimm_sensor_temp(struct dimm_softc *sc, struct ksensor *sens, int temp)
211 if (temp >= sc->dimm_temp_hiwat &&
212 (sc->dimm_sens_taskflags & DIMM_SENS_TF_TEMP_CRIT) == 0) {
213 char temp_str[16], data[64];
215 ksnprintf(temp_str, sizeof(temp_str), "%d", temp);
216 ksnprintf(data, sizeof(data), "node=%d channel=%d dimm=%d",
217 sc->dimm_node, sc->dimm_chan, sc->dimm_slot);
218 devctl_notify("memtemp", "Thermal", temp_str, data);
220 kprintf("dimm%d: node%d channel%d DIMM%d "
221 "temperature (%dC) is too high (>= %dC)\n",
222 sc->dimm_id, sc->dimm_node, sc->dimm_chan, sc->dimm_slot,
223 temp, sc->dimm_temp_hiwat);
225 sc->dimm_sens_taskflags |= DIMM_SENS_TF_TEMP_CRIT;
226 } else if ((sc->dimm_sens_taskflags & DIMM_SENS_TF_TEMP_CRIT) &&
227 temp < sc->dimm_temp_lowat) {
228 sc->dimm_sens_taskflags &= ~DIMM_SENS_TF_TEMP_CRIT;
231 if (sc->dimm_sens_taskflags & DIMM_SENS_TF_TEMP_CRIT)
232 sens->status = SENSOR_S_CRIT;
234 sens->status = SENSOR_S_OK;
235 sens->flags &= ~SENSOR_FINVALID;
236 sens->value = (temp * 1000000) + 273150000;
240 dimm_mod_unload(void)
242 struct dimm_softc *sc;
246 while ((sc = TAILQ_FIRST(&dimm_softc_list)) != NULL) {
249 error = dimm_destroy(sc);
250 KASSERT(!error, ("dimm%d is still referenced, ref %d",
251 sc->dimm_id, sc->dimm_ref));
258 dimm_mod_event(module_t mod, int type, void *unused)
262 TAILQ_INIT(&dimm_softc_list);
274 static moduledata_t dimm_mod = {
279 DECLARE_MODULE(dimm, dimm_mod, SI_SUB_PRE_DRIVERS, SI_ORDER_ANY);
280 MODULE_VERSION(dimm, 1);