1 /* $NetBSD: lm75.c,v 1.29 2016/01/11 18:23:11 jdc Exp $ */
2
3 /*
4 * Copyright (c) 2003 Wasabi Systems, Inc.
5 * All rights reserved.
6 *
7 * Written by Jason R. Thorpe for Wasabi Systems, Inc.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed for the NetBSD Project by
20 * Wasabi Systems, Inc.
21 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
22 * or promote products derived from this software without specific prior
23 * written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
36 */
37
38 #include <sys/cdefs.h>
39 __KERNEL_RCSID(0, "$NetBSD: lm75.c,v 1.29 2016/01/11 18:23:11 jdc Exp $");
40
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/device.h>
44 #include <sys/kernel.h>
45 #include <sys/sysctl.h>
46
47 #include <dev/sysmon/sysmonvar.h>
48
49 #include <dev/i2c/i2cvar.h>
50 #include <dev/i2c/lm75reg.h>
51
52 struct lmtemp_softc {
53 device_t sc_dev;
54 i2c_tag_t sc_tag;
55 int sc_address;
56
57 struct sysmon_envsys *sc_sme;
58 envsys_data_t sc_sensor;
59 int sc_tmax;
60 uint32_t sc_smax, sc_smin, sc_scrit;
61
62 uint32_t (*sc_lmtemp_decode)(const uint8_t *, int);
63 void (*sc_lmtemp_encode)(const uint32_t, uint8_t *, int);
64 };
65
66 static int lmtemp_match(device_t, cfdata_t, void *);
67 static void lmtemp_attach(device_t, device_t, void *);
68
69 CFATTACH_DECL_NEW(lmtemp, sizeof(struct lmtemp_softc),
70 lmtemp_match, lmtemp_attach, NULL, NULL);
71
72 static void lmtemp_refresh(struct sysmon_envsys *, envsys_data_t *);
73 static int lmtemp_config_write(struct lmtemp_softc *, uint8_t);
74 static int lmtemp_temp_write(struct lmtemp_softc *, uint8_t, uint32_t,
75 int);
76 static int lmtemp_temp_read(struct lmtemp_softc *, uint8_t, uint32_t *,
77 int);
78 static uint32_t lmtemp_decode_lm75(const uint8_t *, int);
79 static uint32_t lmtemp_decode_ds75(const uint8_t *, int);
80 static uint32_t lmtemp_decode_lm77(const uint8_t *, int);
81 static void lmtemp_encode_lm75(const uint32_t, uint8_t *, int);
82 static void lmtemp_encode_ds75(const uint32_t, uint8_t *, int);
83 static void lmtemp_encode_lm77(const uint32_t, uint8_t *, int);
84 static void lmtemp_getlim_lm75(struct sysmon_envsys *, envsys_data_t *,
85 sysmon_envsys_lim_t *, uint32_t *);
86 static void lmtemp_getlim_lm77(struct sysmon_envsys *, envsys_data_t *,
87 sysmon_envsys_lim_t *, uint32_t *);
88 static void lmtemp_setlim_lm75(struct sysmon_envsys *, envsys_data_t *,
89 sysmon_envsys_lim_t *, uint32_t *);
90 static void lmtemp_setlim_lm77(struct sysmon_envsys *, envsys_data_t *,
91 sysmon_envsys_lim_t *, uint32_t *);
92
93 static void lmtemp_setup_sysctl(struct lmtemp_softc *);
94 static int sysctl_lm75_temp(SYSCTLFN_ARGS);
95
96 static const char * lmtemp_compats[] = {
97 "i2c-lm75",
98 /*
99 * see XXX in _attach() below: add code once non-lm75 matches are
100 * added here!
101 */
102 NULL
103 };
104
105 enum {
106 lmtemp_lm75 = 0,
107 lmtemp_ds75,
108 lmtemp_lm77,
109 };
110 static const struct {
111 int lmtemp_type;
112 const char *lmtemp_name;
113 int lmtemp_addrmask;
114 int lmtemp_addr;
115 uint32_t (*lmtemp_decode)(const uint8_t *, int);
116 void (*lmtemp_encode)(const uint32_t, uint8_t *, int);
117 void (*lmtemp_getlim)(struct sysmon_envsys *, envsys_data_t *,
118 sysmon_envsys_lim_t *, uint32_t *);
119 void (*lmtemp_setlim)(struct sysmon_envsys *, envsys_data_t *,
120 sysmon_envsys_lim_t *, uint32_t *);
121 } lmtemptbl[] = {
122 { lmtemp_lm75, "LM75", LM75_ADDRMASK, LM75_ADDR,
123 lmtemp_decode_lm75, lmtemp_encode_lm75,
124 lmtemp_getlim_lm75, lmtemp_setlim_lm75 },
125 { lmtemp_ds75, "DS75", LM75_ADDRMASK, LM75_ADDR,
126 lmtemp_decode_ds75, lmtemp_encode_ds75,
127 lmtemp_getlim_lm75, lmtemp_setlim_lm75 },
128 { lmtemp_lm77, "LM77", LM77_ADDRMASK, LM77_ADDR,
129 lmtemp_decode_lm77, lmtemp_encode_lm77,
130 lmtemp_getlim_lm77, lmtemp_setlim_lm77 },
131 { -1, NULL, 0, 0,
132 NULL, NULL,
133 NULL, NULL }
134 };
135
136 static int
lmtemp_match(device_t parent,cfdata_t cf,void * aux)137 lmtemp_match(device_t parent, cfdata_t cf, void *aux)
138 {
139 struct i2c_attach_args *ia = aux;
140 int i;
141
142 if (ia->ia_name == NULL) {
143 /*
144 * Indirect config - not much we can do!
145 */
146 for (i = 0; lmtemptbl[i].lmtemp_type != -1 ; i++)
147 if (lmtemptbl[i].lmtemp_type == cf->cf_flags)
148 break;
149 if (lmtemptbl[i].lmtemp_type == -1)
150 return 0;
151
152 if ((ia->ia_addr & lmtemptbl[i].lmtemp_addrmask) ==
153 lmtemptbl[i].lmtemp_addr)
154 return 1;
155 } else {
156 /*
157 * Direct config - match via the list of compatible
158 * hardware or simply match the device name.
159 */
160 if (ia->ia_ncompat > 0) {
161 if (iic_compat_match(ia, lmtemp_compats))
162 return 1;
163 } else {
164 if (strcmp(ia->ia_name, "lmtemp") == 0)
165 return 1;
166 }
167 }
168
169
170 return 0;
171 }
172
173 static void
lmtemp_attach(device_t parent,device_t self,void * aux)174 lmtemp_attach(device_t parent, device_t self, void *aux)
175 {
176 struct lmtemp_softc *sc = device_private(self);
177 struct i2c_attach_args *ia = aux;
178 int i;
179
180 sc->sc_dev = self;
181 if (ia->ia_name == NULL) {
182 for (i = 0; lmtemptbl[i].lmtemp_type != -1 ; i++)
183 if (lmtemptbl[i].lmtemp_type ==
184 device_cfdata(self)->cf_flags)
185 break;
186 } else {
187 /* XXX - add code when adding other direct matches! */
188 i = 0;
189 }
190
191 sc->sc_tag = ia->ia_tag;
192 sc->sc_address = ia->ia_addr;
193
194 aprint_naive(": Temperature Sensor\n");
195 if (ia->ia_name) {
196 aprint_normal(": %s %s Temperature Sensor\n", ia->ia_name,
197 lmtemptbl[i].lmtemp_name);
198 } else {
199 aprint_normal(": %s Temperature Sensor\n",
200 lmtemptbl[i].lmtemp_name);
201 }
202
203 sc->sc_lmtemp_decode = lmtemptbl[i].lmtemp_decode;
204 sc->sc_lmtemp_encode = lmtemptbl[i].lmtemp_encode;
205
206 iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
207
208 /* Read temperature limit(s) and remember initial value(s). */
209 if (i == lmtemp_lm77) {
210 if (lmtemp_temp_read(sc, LM77_REG_TCRIT_SET_POINT,
211 &sc->sc_scrit, 1) != 0) {
212 aprint_error_dev(self,
213 "unable to read low register\n");
214 iic_release_bus(sc->sc_tag, I2C_F_POLL);
215 return;
216 }
217 if (lmtemp_temp_read(sc, LM77_REG_TLOW_SET_POINT,
218 &sc->sc_smin, 1) != 0) {
219 aprint_error_dev(self,
220 "unable to read low register\n");
221 iic_release_bus(sc->sc_tag, I2C_F_POLL);
222 return;
223 }
224 if (lmtemp_temp_read(sc, LM77_REG_THIGH_SET_POINT,
225 &sc->sc_smax, 1) != 0) {
226 aprint_error_dev(self,
227 "unable to read high register\n");
228 iic_release_bus(sc->sc_tag, I2C_F_POLL);
229 return;
230 }
231 } else { /* LM75 or compatible */
232 if (lmtemp_temp_read(sc, LM75_REG_TOS_SET_POINT,
233 &sc->sc_smax, 1) != 0) {
234 aprint_error_dev(self, "unable to read Tos register\n");
235 iic_release_bus(sc->sc_tag, I2C_F_POLL);
236 return;
237 }
238 }
239 sc->sc_tmax = sc->sc_smax;
240
241 if (i == lmtemp_lm75)
242 lmtemp_setup_sysctl(sc);
243
244 /* Set the configuration of the LM75 to defaults. */
245 if (lmtemp_config_write(sc, LM75_CONFIG_FAULT_QUEUE_4) != 0) {
246 aprint_error_dev(self, "unable to write config register\n");
247 iic_release_bus(sc->sc_tag, I2C_F_POLL);
248 return;
249 }
250 iic_release_bus(sc->sc_tag, I2C_F_POLL);
251
252 sc->sc_sme = sysmon_envsys_create();
253 /* Initialize sensor data. */
254 sc->sc_sensor.units = ENVSYS_STEMP;
255 sc->sc_sensor.state = ENVSYS_SINVALID;
256 sc->sc_sensor.flags = ENVSYS_FMONLIMITS;
257 (void)strlcpy(sc->sc_sensor.desc,
258 ia->ia_name? ia->ia_name : device_xname(self),
259 sizeof(sc->sc_sensor.desc));
260 if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor)) {
261 sysmon_envsys_destroy(sc->sc_sme);
262 return;
263 }
264
265 /* Hook into system monitor. */
266 sc->sc_sme->sme_name = device_xname(self);
267 sc->sc_sme->sme_cookie = sc;
268 sc->sc_sme->sme_refresh = lmtemp_refresh;
269 sc->sc_sme->sme_get_limits = lmtemptbl[i].lmtemp_getlim;
270 sc->sc_sme->sme_set_limits = lmtemptbl[i].lmtemp_setlim;
271
272 if (sysmon_envsys_register(sc->sc_sme)) {
273 aprint_error_dev(self, "unable to register with sysmon\n");
274 sysmon_envsys_destroy(sc->sc_sme);
275 }
276 }
277
278 static int
lmtemp_config_write(struct lmtemp_softc * sc,uint8_t val)279 lmtemp_config_write(struct lmtemp_softc *sc, uint8_t val)
280 {
281 uint8_t cmdbuf[2];
282
283 cmdbuf[0] = LM75_REG_CONFIG;
284 cmdbuf[1] = val;
285
286 return iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
287 sc->sc_address, cmdbuf, 1, &cmdbuf[1], 1, I2C_F_POLL);
288 }
289
290 static int
lmtemp_temp_write(struct lmtemp_softc * sc,uint8_t reg,uint32_t val,int degc)291 lmtemp_temp_write(struct lmtemp_softc *sc, uint8_t reg, uint32_t val, int degc)
292 {
293 uint8_t cmdbuf[3];
294
295 cmdbuf[0] = reg;
296 sc->sc_lmtemp_encode(val, &cmdbuf[1], degc);
297
298 return iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
299 sc->sc_address, cmdbuf, 1, &cmdbuf[1], 2, I2C_F_POLL);
300 }
301
302 static int
lmtemp_temp_read(struct lmtemp_softc * sc,uint8_t which,uint32_t * valp,int degc)303 lmtemp_temp_read(struct lmtemp_softc *sc, uint8_t which, uint32_t *valp,
304 int degc)
305 {
306 int error;
307 uint8_t cmdbuf[1];
308 uint8_t buf[LM75_TEMP_LEN];
309
310 cmdbuf[0] = which;
311
312 error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
313 sc->sc_address, cmdbuf, 1, buf, LM75_TEMP_LEN, 0);
314 if (error)
315 return error;
316
317 *valp = sc->sc_lmtemp_decode(buf, degc);
318 return 0;
319 }
320
321 static void
lmtemp_refresh_sensor_data(struct lmtemp_softc * sc)322 lmtemp_refresh_sensor_data(struct lmtemp_softc *sc)
323 {
324 uint32_t val;
325 int error;
326
327 error = lmtemp_temp_read(sc, LM75_REG_TEMP, &val, 0);
328 if (error) {
329 #if 0
330 aprint_error_dev(sc->sc_dev, "unable to read temperature, error = %d\n",
331 error);
332 #endif
333 sc->sc_sensor.state = ENVSYS_SINVALID;
334 return;
335 }
336
337 sc->sc_sensor.value_cur = val;
338 sc->sc_sensor.state = ENVSYS_SVALID;
339 }
340
341 static void
lmtemp_refresh(struct sysmon_envsys * sme,envsys_data_t * edata)342 lmtemp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
343 {
344 struct lmtemp_softc *sc = sme->sme_cookie;
345
346 iic_acquire_bus(sc->sc_tag, 0); /* also locks our instance */
347 lmtemp_refresh_sensor_data(sc);
348 iic_release_bus(sc->sc_tag, 0); /* also unlocks our instance */
349 }
350
351 static void
lmtemp_getlim_lm75(struct sysmon_envsys * sme,envsys_data_t * edata,sysmon_envsys_lim_t * limits,uint32_t * props)352 lmtemp_getlim_lm75(struct sysmon_envsys *sme, envsys_data_t *edata,
353 sysmon_envsys_lim_t *limits, uint32_t *props)
354 {
355 struct lmtemp_softc *sc = sme->sme_cookie;
356 uint32_t val;
357
358 *props &= ~(PROP_CRITMAX);
359
360 iic_acquire_bus(sc->sc_tag, 0);
361 if (lmtemp_temp_read(sc, LM75_REG_TOS_SET_POINT, &val, 0) == 0) {
362 limits->sel_critmax = val;
363 *props |= PROP_CRITMAX;
364 }
365 iic_release_bus(sc->sc_tag, 0);
366 }
367
368 static void
lmtemp_getlim_lm77(struct sysmon_envsys * sme,envsys_data_t * edata,sysmon_envsys_lim_t * limits,uint32_t * props)369 lmtemp_getlim_lm77(struct sysmon_envsys *sme, envsys_data_t *edata,
370 sysmon_envsys_lim_t *limits, uint32_t *props)
371 {
372 struct lmtemp_softc *sc = sme->sme_cookie;
373 uint32_t val;
374
375 *props &= ~(PROP_CRITMAX | PROP_WARNMAX | PROP_WARNMIN);
376
377 iic_acquire_bus(sc->sc_tag, 0);
378 if (lmtemp_temp_read(sc, LM77_REG_TCRIT_SET_POINT, &val, 0) == 0) {
379 limits->sel_critmax = val;
380 *props |= PROP_CRITMAX;
381 }
382 if (lmtemp_temp_read(sc, LM77_REG_THIGH_SET_POINT, &val, 0) == 0) {
383 limits->sel_warnmax = val;
384 *props |= PROP_WARNMAX;
385 }
386 if (lmtemp_temp_read(sc, LM77_REG_TLOW_SET_POINT, &val, 0) == 0) {
387 limits->sel_warnmin = val;
388 *props |= PROP_WARNMIN;
389 }
390 iic_release_bus(sc->sc_tag, 0);
391 }
392
393 static void
lmtemp_setlim_lm75(struct sysmon_envsys * sme,envsys_data_t * edata,sysmon_envsys_lim_t * limits,uint32_t * props)394 lmtemp_setlim_lm75(struct sysmon_envsys *sme, envsys_data_t *edata,
395 sysmon_envsys_lim_t *limits, uint32_t *props)
396 {
397 struct lmtemp_softc *sc = sme->sme_cookie;
398 int32_t limit;
399
400 if (*props & PROP_CRITMAX) {
401 if (limits == NULL) /* Restore defaults */
402 limit = sc->sc_smax;
403 else
404 limit = limits->sel_critmax;
405 iic_acquire_bus(sc->sc_tag, 0);
406 lmtemp_temp_write(sc, LM75_REG_THYST_SET_POINT,
407 limit - 5000000, 0);
408 lmtemp_temp_write(sc, LM75_REG_TOS_SET_POINT, limit, 0);
409 iic_release_bus(sc->sc_tag, 0);
410
411 /* Synchronise sysctl */
412 sc->sc_tmax = (limit - 273150000) / 1000000;
413 }
414 }
415
416 static void
lmtemp_setlim_lm77(struct sysmon_envsys * sme,envsys_data_t * edata,sysmon_envsys_lim_t * limits,uint32_t * props)417 lmtemp_setlim_lm77(struct sysmon_envsys *sme, envsys_data_t *edata,
418 sysmon_envsys_lim_t *limits, uint32_t *props)
419 {
420 struct lmtemp_softc *sc = sme->sme_cookie;
421 int32_t limit;
422
423 iic_acquire_bus(sc->sc_tag, 0);
424 if (*props & PROP_CRITMAX) {
425 if (limits == NULL) /* Restore defaults */
426 limit = sc->sc_scrit;
427 else
428 limit = limits->sel_critmax;
429 lmtemp_temp_write(sc, LM77_REG_TCRIT_SET_POINT, limit, 0);
430 }
431 if (*props & PROP_WARNMAX) {
432 if (limits == NULL) /* Restore defaults */
433 limit = sc->sc_smax;
434 else
435 limit = limits->sel_warnmax;
436 lmtemp_temp_write(sc, LM77_REG_THIGH_SET_POINT, limit, 0);
437 }
438 if (*props & PROP_WARNMIN) {
439 if (limits == NULL) /* Restore defaults */
440 limit = sc->sc_smin;
441 else
442 limit = limits->sel_warnmin;
443 lmtemp_temp_write(sc, LM77_REG_TLOW_SET_POINT, limit, 0);
444 }
445 iic_release_bus(sc->sc_tag, 0);
446 }
447
448 static uint32_t
lmtemp_decode_lm75(const uint8_t * buf,int degc)449 lmtemp_decode_lm75(const uint8_t *buf, int degc)
450 {
451 int temp;
452 uint32_t val;
453
454 /*
455 * LM75 temps are the most-significant 9 bits of a 16-bit reg.
456 * sign-extend the MSB and add in the 0.5 from the LSB
457 */
458 temp = (int8_t) buf[0];
459 temp = (temp << 1) + ((buf[1] >> 7) & 0x1);
460
461 /* Temp is given in 1/2 deg. C, we convert to C or uK. */
462 if (degc)
463 val = temp / 2;
464 else
465 val = temp * 500000 + 273150000;
466
467 return val;
468 }
469
470 static uint32_t
lmtemp_decode_ds75(const uint8_t * buf,int degc)471 lmtemp_decode_ds75(const uint8_t *buf, int degc)
472 {
473 int temp;
474
475 /*
476 * Sign-extend the MSB byte, and add in the fractions of a
477 * degree contained in the LSB (precision 1/16th DegC).
478 */
479 temp = (int8_t)buf[0];
480 temp = (temp << 4) | ((buf[1] >> 4) & 0xf);
481
482 /*
483 * Conversion to C or uK is simple.
484 */
485 if (degc)
486 return temp / 16;
487 else
488 return (temp * 62500 + 273150000);
489 }
490
491 static uint32_t
lmtemp_decode_lm77(const uint8_t * buf,int degc)492 lmtemp_decode_lm77(const uint8_t *buf, int degc)
493 {
494 int temp;
495 uint32_t val;
496
497 /*
498 * Describe each bits of temperature registers on LM77.
499 * D15 - D12: Sign
500 * D11 - D3 : Bit8(MSB) - Bit0
501 */
502 temp = (int8_t)buf[0];
503 temp = (temp << 5) | ((buf[1] >> 3) & 0x1f);
504
505 /* Temp is given in 1/2 deg. C, we convert to C or uK. */
506 if (degc)
507 val = temp / 2;
508 else
509 val = temp * 500000 + 273150000;
510
511 return val;
512 }
513
lmtemp_encode_lm75(const uint32_t val,uint8_t * buf,int degc)514 static void lmtemp_encode_lm75(const uint32_t val, uint8_t *buf, int degc)
515 {
516 int temp;
517
518 /* Convert from C or uK to register format */
519 if (degc)
520 temp = val * 2;
521 else
522 temp = (val - 273150000) / 500000;
523 buf[0] = (temp >> 1) & 0xff;
524 buf[1] = (temp & 1) << 7;
525 }
526
lmtemp_encode_ds75(const uint32_t val,uint8_t * buf,int degc)527 static void lmtemp_encode_ds75(const uint32_t val, uint8_t *buf, int degc)
528 {
529 int temp;
530
531 /* Convert from C or uK to register format */
532 if (degc)
533 temp = val * 16;
534 else
535 temp = (val - 273150000) / 62500;
536 buf[0] = (temp >> 4) & 0xff;
537 buf[1] = (temp & 0xf) << 4;
538 }
539
lmtemp_encode_lm77(const uint32_t val,uint8_t * buf,int degc)540 static void lmtemp_encode_lm77(const uint32_t val, uint8_t *buf, int degc)
541 {
542 int temp;
543
544 /* Convert from C or uK to register format */
545 if (degc)
546 temp = val * 2;
547 else
548 temp = (val - 273150000) / 500000;
549 buf[0] = (temp >> 5) & 0xff;
550 buf[1] = (temp & 0x1f) << 3;
551 }
552
553 static void
lmtemp_setup_sysctl(struct lmtemp_softc * sc)554 lmtemp_setup_sysctl(struct lmtemp_softc *sc)
555 {
556 const struct sysctlnode *me = NULL, *node = NULL;
557
558 sysctl_createv(NULL, 0, NULL, &me,
559 CTLFLAG_READWRITE,
560 CTLTYPE_NODE, device_xname(sc->sc_dev), NULL,
561 NULL, 0, NULL, 0,
562 CTL_MACHDEP, CTL_CREATE, CTL_EOL);
563
564 sysctl_createv(NULL, 0, NULL, &node,
565 CTLFLAG_READWRITE | CTLFLAG_OWNDESC,
566 CTLTYPE_INT, "temp", "Threshold temperature",
567 sysctl_lm75_temp, 1, (void *)sc, 0,
568 CTL_MACHDEP, me->sysctl_num, CTL_CREATE, CTL_EOL);
569 }
570
571 static int
sysctl_lm75_temp(SYSCTLFN_ARGS)572 sysctl_lm75_temp(SYSCTLFN_ARGS)
573 {
574 struct sysctlnode node = *rnode;
575 struct lmtemp_softc *sc = node.sysctl_data;
576 int temp;
577
578 if (newp) {
579
580 /* we're asked to write */
581 node.sysctl_data = &sc->sc_tmax;
582 if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) {
583
584 temp = *(int *)node.sysctl_data;
585 sc->sc_tmax = temp;
586 iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
587 lmtemp_temp_write(sc, LM75_REG_THYST_SET_POINT,
588 sc->sc_tmax - 5, 1);
589 lmtemp_temp_write(sc, LM75_REG_TOS_SET_POINT,
590 sc->sc_tmax, 1);
591 iic_release_bus(sc->sc_tag, I2C_F_POLL);
592
593 /* Synchronise envsys - calls lmtemp_getlim_lm75() */
594 sysmon_envsys_update_limits(sc->sc_sme, &sc->sc_sensor);
595 return 0;
596 }
597 return EINVAL;
598 } else {
599
600 node.sysctl_data = &sc->sc_tmax;
601 node.sysctl_size = 4;
602 return (sysctl_lookup(SYSCTLFN_CALL(&node)));
603 }
604
605 return 0;
606 }
607
608 SYSCTL_SETUP(sysctl_lmtemp_setup, "sysctl lmtemp subtree setup")
609 {
610
611 sysctl_createv(NULL, 0, NULL, NULL,
612 CTLFLAG_PERMANENT,
613 CTLTYPE_NODE, "machdep", NULL,
614 NULL, 0, NULL, 0,
615 CTL_MACHDEP, CTL_EOL);
616 }
617
618
619