1 /*        $NetBSD: lm87.c,v 1.16 2022/07/02 16:28:39 andvar Exp $     */
2 /*        $OpenBSD: lm87.c,v 1.20 2008/11/10 05:19:48 cnst Exp $      */
3 
4 /*
5  * Copyright (c) 2005 Mark Kettenis
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/cdefs.h>
21 __KERNEL_RCSID(0, "$NetBSD: lm87.c,v 1.16 2022/07/02 16:28:39 andvar Exp $");
22 
23 #include <sys/param.h>
24 #include <sys/systm.h>
25 #include <sys/device.h>
26 #include <dev/sysmon/sysmonvar.h>
27 
28 #include <dev/i2c/i2cvar.h>
29 
30 /* LM87 registers */
31 #define LM87_INT_HHIGH_L      0x13      /* Hardware int high limit (lockable) */
32 #define LM87_EXT_HHIGH_L      0x14      /* Hardware ext high limit (lockable) */
33 #define LM87_TEST             0x15
34 #define LM87_CHANNEL                    0x16      /* Dual purpose pin and scaling */
35 #define LM87_INT_HHIGH                  0x17      /* Hardware int temp high limit */
36 #define LM87_EXT_HHIGH                  0x18      /* Hardware ext temp high limit */
37 #define LM87_DAC_DATA                   0x19      /* DAC output scaling */
38 #define LM87_AIN1_LOW                   0x1a      /* Analog in 1 low limit */
39 #define LM87_AIN2_LOW                   0x1b      /* Analog in 2 low limit */
40 #define LM87_2_5V             0x20      /* +2.5V or ext temp 2 reading */
41 #define LM87_VCCP1            0x21      /* Vccp1 reading */
42 #define LM87_VCC              0x22      /* +Vcc reading */
43 #define LM87_5V                         0x23      /* +5V reading */
44 #define LM87_12V              0x24      /* +12V reading */
45 #define LM87_VCCP2            0x25      /* Vccp2 reading */
46 #define LM87_EXT_TEMP                   0x26      /* External temperature 1 reading */
47 #define LM87_INT_TEMP                   0x27      /* Internal temperature reading */
48 #define LM87_FAN1             0x28      /* Fan1 or AIN1 reading */
49 #define LM87_FAN2             0x29      /* Fan2 or AIN2 reading */
50 #define LM87_2_5V_HIGH                  0x2b      /* +2.5V or ext temp 2 high limit */
51 #define LM87_2_5V_LOW                   0x2c      /* +2.5V or ext temp 2 low limit */
52 #define LM87_VCCP1_HIGH                 0x2d      /* Vccp1 high limit */
53 #define LM87_VCCP1_LOW                  0x2e      /* Vccp1 low limit */
54 #define LM87_VCC_HIGH                   0x2f      /* +3.3V (Vcc) high limit */
55 #define LM87_VCC_LOW                    0x30      /* +3.3V (Vcc) low limit */
56 #define LM87_5V_HIGH                    0x31      /* +5V high limit */
57 #define LM87_5V_LOW           0x32      /* +5V low limit */
58 #define LM87_12V_HIGH                   0x33      /* +12V high limit */
59 #define LM87_12V_LOW                    0x34      /* +12V low limit */
60 #define LM87_VCCP2_HIGH                 0x35      /* Vccp2 high limit */
61 #define LM87_VCCP2_LOW                  0x36      /* Vccp2 low limit */
62 #define LM87_EXT_HIGH                   0x37      /* External temperature 1 high limit */
63 #define LM87_EXT_LOW                    0x38      /* External temperature low limit */
64 #define LM87_INT_HIGH                   0x39      /* Internal temperature 1 high limit */
65 #define LM87_INT_LOW                    0x3a      /* Internal temperature low limit */
66 #define LM87_FAN1_HIGH                  0x3b      /* Fan 1 count or AIN1 high limit */
67 #define LM87_FAN2_HIGH                  0x3c      /* Fan 2 count or AIN2 high limit */
68 #define LM87_COMPANY_ID                 0x3e      /* Company ID */
69 #define LM87_REVISION                   0x3f      /* Revision */
70 #define LM87_CONFIG1                    0x40      /* Configuration 1 */
71 #define LM87_INT_STAT1                  0x41      /* Interrupt status 1 */
72 #define LM87_INT_STAT2                  0x42      /* Interrupt status 2 */
73 #define LM87_INT_MASK1                  0x43      /* Interrupt mask 1 */
74 #define LM87_INT_MASK2                  0x44      /* Interrupt mask 2 */
75 #define LM87_CI_CLEAR                   0x46      /* Chassis intrusion */
76 #define LM87_FANDIV           0x47      /* Fan divisor + VID 0-3 */
77 #define LM87_VID4             0x48      /* VID4 */
78 #define LM87_CONFIG2                    0x4a      /* Configuration 2 */
79 #define LM87_INT_MIRR1                  0x4c      /* Interrupt status 1 mirror */
80 #define LM87_INT_MIRR2                  0x4d      /* Interrupt status 2 mirror */
81 #define LM87_ALERT            0x80      /* SMB Alert enable */
82 
83 /* Register contents */
84 #define  LM87_CONFIG1_START   0x01
85 #define  LM87_CONFIG1_INTCLR  0x08
86 
87 #define  LM87_CHANNEL_AIN1    0x01
88 #define  LM87_CHANNEL_AIN2    0x02
89 #define  LM87_CHANNEL_TEMP2   0x04
90 #define  LM87_CHANNEL_VCC5    0x08
91 
92 struct lmenv_id {
93           u_int8_t id, family;
94           const char *name;
95 };
96 
97 static const struct lmenv_id lmenv_ids[] = {
98           { 0x01, 81, "LM81" },
99           { 0x02, 87, "LM87" },         /* LM87 or LM87CIMT */
100           { 0x23, 81, "ADM9240" },
101           { 0xda, 81, "DSL780" },
102           { 0x00, 0, NULL }
103 };
104 
105 /* Sensors */
106 #define LMENV_2_5V            0
107 #define LMENV_VCCP1           1
108 #define LMENV_VCC             2
109 #define LMENV_5V              3
110 #define LMENV_12V             4
111 #define LMENV_VCCP2           5
112 #define LMENV_EXT_TEMP                  6
113 #define LMENV_INT_TEMP                  7
114 #define LMENV_FAN1            8
115 #define LMENV_FAN2            9
116 #define LMENV_NUM_SENSORS     10
117 
118 struct lmenv_softc {
119           i2c_tag_t sc_tag;
120           i2c_addr_t sc_addr;
121 
122           int       sc_fan1_div, sc_fan2_div;
123           int       sc_family;
124           uint8_t   sc_channel;
125 
126           struct sysmon_envsys *sc_sme;
127           envsys_data_t sc_sensor[LMENV_NUM_SENSORS];
128 };
129 
130 int       lmenv_match(device_t, cfdata_t, void *);
131 void      lmenv_attach(device_t, device_t, void *);
132 
133 void      lmenv_refresh(struct sysmon_envsys *, envsys_data_t *);
134 
135 CFATTACH_DECL_NEW(lmenv, sizeof(struct lmenv_softc),
136           lmenv_match, lmenv_attach, NULL, NULL);
137 
138 static const struct device_compatible_entry compat_data[] = {
139           { .compat = "lm87" },
140           { .compat = "lm87cimt" },
141           { .compat = "adm9240" },
142           { .compat = "lm81" },
143           { .compat = "ds1780" },
144           DEVICE_COMPAT_EOL
145 };
146 
147 int
lmenv_match(device_t parent,cfdata_t match,void * aux)148 lmenv_match(device_t parent, cfdata_t match, void *aux)
149 {
150           struct i2c_attach_args *ia = aux;
151           u_int8_t cmd, val;
152           int error, i, match_result;
153 
154           if (iic_use_direct_match(ia, match, compat_data, &match_result))
155                     return match_result;
156 
157           /*
158            * Indirect config - not much we can do!
159            * Check typical addresses and read the Company ID register
160            */
161           if ((ia->ia_addr < 0x2c) || (ia->ia_addr > 0x2f))
162                     return 0;
163 
164           cmd = LM87_COMPANY_ID;
165           if (iic_acquire_bus(ia->ia_tag, 0))
166                     return 0;
167           error = iic_exec(ia->ia_tag, I2C_OP_READ_WITH_STOP, ia->ia_addr,
168               &cmd, 1, &val, 1, 0);
169           iic_release_bus(ia->ia_tag, 0);
170 
171           if (error)
172                     return 0;
173 
174           for (i = 0; lmenv_ids[i].id != 0; i++)
175                     if (lmenv_ids[i].id == val)
176                               return I2C_MATCH_ADDRESS_AND_PROBE;
177 
178           return 0;
179 }
180 
181 void
lmenv_attach(device_t parent,device_t self,void * aux)182 lmenv_attach(device_t parent, device_t self, void *aux)
183 {
184           struct lmenv_softc *sc = device_private(self);
185           struct i2c_attach_args *ia = aux;
186           u_int8_t cmd, data, data2;
187           int i;
188 
189           sc->sc_tag = ia->ia_tag;
190           sc->sc_addr = ia->ia_addr;
191 
192           iic_acquire_bus(sc->sc_tag, 0);
193 
194           cmd = LM87_COMPANY_ID;
195           if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
196               sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
197                     iic_release_bus(sc->sc_tag, 0);
198                     printf(": cannot read ID register\n");
199                     return;
200           }
201           for (i = 0; lmenv_ids[i].id != 0; i++)
202                     if (lmenv_ids[i].id == data)
203                               break;
204 
205           cmd = LM87_REVISION;
206           if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
207               sc->sc_addr, &cmd, sizeof cmd, &data2, sizeof data, 0)) {
208                     iic_release_bus(sc->sc_tag, 0);
209                     printf(": cannot read revision register\n");
210                     return;
211           }
212           printf(": %s rev %x\n", lmenv_ids[i].name, data2);
213           sc->sc_family = lmenv_ids[i].family;
214 
215           cmd = LM87_FANDIV;
216           if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
217               sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
218                     iic_release_bus(sc->sc_tag, 0);
219                     printf(", cannot read Fan Divisor register\n");
220                     return;
221           }
222           sc->sc_fan1_div = 1 << ((data >> 4) & 0x03);
223           sc->sc_fan2_div = 1 << ((data >> 6) & 0x03);
224 
225           if (sc->sc_family == 87) {
226                     cmd = LM87_CHANNEL;
227                     if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
228                         sc->sc_addr, &cmd, sizeof cmd, &sc->sc_channel,
229                         sizeof sc->sc_channel, 0)) {
230                               iic_release_bus(sc->sc_tag, 0);
231                               printf(", cannot read Channel register\n");
232                               return;
233                     }
234           } else
235                     sc->sc_channel = 0;
236 
237           cmd = LM87_CONFIG1;
238           if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
239               sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
240                     iic_release_bus(sc->sc_tag, 0);
241                     printf(", cannot read Configuration Register 1\n");
242                     return;
243           }
244 
245           /*
246            * if chip is not running, try to start it.
247            * if it is stalled doing an interrupt, unstall it
248            */
249           data2 = (data | LM87_CONFIG1_START);
250           data2 = data2 & ~LM87_CONFIG1_INTCLR;
251 
252           if (data != data2) {
253                     if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
254                         sc->sc_addr, &cmd, sizeof cmd, &data2, sizeof data2, 0)) {
255                               iic_release_bus(sc->sc_tag, 0);
256                               printf(", cannot write Configuration Register 1\n");
257                               return;
258                     }
259           }
260           iic_release_bus(sc->sc_tag, 0);
261 
262           /* Initialize sensor data. */
263           sc->sc_sensor[LMENV_2_5V].state = ENVSYS_SINVALID;
264           if (sc->sc_channel & LM87_CHANNEL_TEMP2) {
265                     sc->sc_sensor[LMENV_INT_TEMP].units = ENVSYS_STEMP;
266                     strlcpy(sc->sc_sensor[LMENV_2_5V].desc, "External 2",
267                         sizeof(sc->sc_sensor[LMENV_2_5V].desc));
268           } else {
269                     sc->sc_sensor[LMENV_2_5V].units = ENVSYS_SVOLTS_DC;
270                     strlcpy(sc->sc_sensor[LMENV_2_5V].desc, "+2.5Vin",
271                         sizeof(sc->sc_sensor[LMENV_2_5V].desc));
272           }
273 
274           sc->sc_sensor[LMENV_VCCP1].state = ENVSYS_SINVALID;
275           sc->sc_sensor[LMENV_VCCP1].units = ENVSYS_SVOLTS_DC;
276           strlcpy(sc->sc_sensor[LMENV_VCCP1].desc, "Vccp1",
277               sizeof(sc->sc_sensor[LMENV_VCCP1].desc));
278 
279           sc->sc_sensor[LMENV_VCC].state = ENVSYS_SINVALID;
280           sc->sc_sensor[LMENV_VCC].units = ENVSYS_SVOLTS_DC;
281           strlcpy(sc->sc_sensor[LMENV_VCC].desc, "+Vcc",
282               sizeof(sc->sc_sensor[LMENV_VCC].desc));
283 
284           sc->sc_sensor[LMENV_5V].state = ENVSYS_SINVALID;
285           sc->sc_sensor[LMENV_5V].units = ENVSYS_SVOLTS_DC;
286           strlcpy(sc->sc_sensor[LMENV_5V].desc, "+5Vin/Vcc",
287               sizeof(sc->sc_sensor[LMENV_5V].desc));
288 
289           sc->sc_sensor[LMENV_12V].state = ENVSYS_SINVALID;
290           sc->sc_sensor[LMENV_12V].units = ENVSYS_SVOLTS_DC;
291           strlcpy(sc->sc_sensor[LMENV_12V].desc, "+12Vin",
292               sizeof(sc->sc_sensor[LMENV_12V].desc));
293 
294           sc->sc_sensor[LMENV_VCCP2].state = ENVSYS_SINVALID;
295           if (!(sc->sc_channel & LM87_CHANNEL_TEMP2)) {
296                     sc->sc_sensor[LMENV_VCCP2].units = ENVSYS_SVOLTS_DC;
297                     strlcpy(sc->sc_sensor[LMENV_VCCP2].desc, "Vccp2",
298                         sizeof(sc->sc_sensor[LMENV_VCCP2].desc));
299           }
300 
301           sc->sc_sensor[LMENV_EXT_TEMP].state = ENVSYS_SINVALID;
302           sc->sc_sensor[LMENV_EXT_TEMP].units = ENVSYS_STEMP;
303           if (sc->sc_channel & LM87_CHANNEL_TEMP2)
304                     strlcpy(sc->sc_sensor[LMENV_EXT_TEMP].desc, "External 1",
305                         sizeof(sc->sc_sensor[LMENV_EXT_TEMP].desc));
306           else
307                     strlcpy(sc->sc_sensor[LMENV_EXT_TEMP].desc, "External",
308                         sizeof(sc->sc_sensor[LMENV_EXT_TEMP].desc));
309 
310           sc->sc_sensor[LMENV_INT_TEMP].state = ENVSYS_SINVALID;
311           sc->sc_sensor[LMENV_INT_TEMP].units = ENVSYS_STEMP;
312           strlcpy(sc->sc_sensor[LMENV_INT_TEMP].desc, "Internal",
313               sizeof(sc->sc_sensor[LMENV_INT_TEMP].desc));
314 
315           sc->sc_sensor[LMENV_FAN1].state = ENVSYS_SINVALID;
316           if (sc->sc_channel & LM87_CHANNEL_AIN1) {
317                     sc->sc_sensor[LMENV_FAN1].units = ENVSYS_SVOLTS_DC;
318                     strlcpy(sc->sc_sensor[LMENV_FAN1].desc, "AIN1",
319                         sizeof(sc->sc_sensor[LMENV_FAN1].desc));
320           } else {
321                     sc->sc_sensor[LMENV_FAN1].units = ENVSYS_SFANRPM;
322                     strlcpy(sc->sc_sensor[LMENV_FAN1].desc, "FAN1",
323                         sizeof(sc->sc_sensor[LMENV_FAN1].desc));
324           }
325 
326           sc->sc_sensor[LMENV_FAN2].state = ENVSYS_SINVALID;
327           if (sc->sc_channel & LM87_CHANNEL_AIN2) {
328                     sc->sc_sensor[LMENV_FAN2].units = ENVSYS_SVOLTS_DC;
329                     strlcpy(sc->sc_sensor[LMENV_FAN2].desc, "AIN2",
330                         sizeof(sc->sc_sensor[LMENV_FAN2].desc));
331           } else {
332                     sc->sc_sensor[LMENV_FAN2].units = ENVSYS_SFANRPM;
333                     strlcpy(sc->sc_sensor[LMENV_FAN2].desc, "FAN2",
334                         sizeof(sc->sc_sensor[LMENV_FAN2].desc));
335           }
336 
337           sc->sc_sme = sysmon_envsys_create();
338           for (i = 0; i < LMENV_NUM_SENSORS; i++)
339                     if (sysmon_envsys_sensor_attach(sc->sc_sme,
340                         &sc->sc_sensor[i])) {
341                               sysmon_envsys_destroy(sc->sc_sme);
342                               aprint_error_dev(self,
343                                   "unable to attach sensor %d at sysmon\n", i);
344                               return;
345                     }
346         sc->sc_sme->sme_name = device_xname(self);
347         sc->sc_sme->sme_cookie = sc;
348         sc->sc_sme->sme_refresh = lmenv_refresh;
349           if (sysmon_envsys_register(sc->sc_sme)) {
350                     aprint_error_dev(self,
351                         "unable to register with sysmon\n");
352                     sysmon_envsys_destroy(sc->sc_sme);
353                     return;
354           }
355 }
356 
357 void
lmenv_refresh(struct sysmon_envsys * sme,envsys_data_t * edata)358 lmenv_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
359 {
360           struct lmenv_softc *sc = sme->sme_cookie;
361           u_int8_t cmd, data;
362           u_int tmp;
363 
364           iic_acquire_bus(sc->sc_tag, 0);
365 
366           cmd = LM87_2_5V + edata->sensor;
367           if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP,
368               sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) {
369                     edata->state = ENVSYS_SINVALID;
370                     return;
371           }
372 
373           switch (edata->sensor) {
374           case LMENV_2_5V:
375                     /* Might be external temperature 2 */
376                     if (sc->sc_channel & LM87_CHANNEL_TEMP2) {
377                               if (data == 0x80)
378                                         edata->state = ENVSYS_SINVALID;
379                               else {
380                                         edata->value_cur =
381                                             (int8_t)data * 1000000 + 273150000;
382                                         edata->state = ENVSYS_SVALID;
383                               }
384                               break;
385                     }
386                     edata->value_cur = 2500000 * data / 192;
387                     edata->state = ENVSYS_SVALID;
388                     break;
389           case LMENV_5V:
390                     edata->value_cur = 5000000 * data / 192;
391                     edata->state = ENVSYS_SVALID;
392                     break;
393           case LMENV_12V:
394                     edata->value_cur = 12000000 * data / 192;
395                     edata->state = ENVSYS_SVALID;
396                     break;
397           case LMENV_VCCP1:
398                     edata->value_cur = 2700000 * data / 192;
399                     edata->state = ENVSYS_SVALID;
400                     break;
401           case LMENV_VCCP2:
402                     /* If monitoring external temperature 2, this isn't monitored */
403                     if (sc->sc_channel & LM87_CHANNEL_TEMP2) {
404                               edata->state = ENVSYS_SINVALID;
405                               break;
406                     }
407                     edata->value_cur = 2700000 * data / 192;
408                     edata->state = ENVSYS_SVALID;
409                     break;
410           case LMENV_VCC:
411                     /* Voltage scale selectable (5V or 3.3V) */
412                     edata->value_cur =
413                         (LM87_CHANNEL_VCC5 ? 5000000 : 3300000) * data / 192;
414                     edata->state = ENVSYS_SVALID;
415                     break;
416           case LMENV_EXT_TEMP:
417                     if (sc->sc_family == 81) {
418                               edata->state = ENVSYS_SINVALID;
419                               break;              /* missing on LM81 */
420                     }
421                     /* FALLTHROUGH */
422           case LMENV_INT_TEMP:
423                     if (data == 0x80)
424                               edata->state = ENVSYS_SINVALID;
425                     else {
426                               edata->value_cur = (int8_t)data * 1000000 + 273150000;
427                               edata->state = ENVSYS_SVALID;
428                     }
429                     break;
430           case LMENV_FAN1:
431                     /* Might be analogue input 1 */
432                     if (sc->sc_channel & LM87_CHANNEL_AIN1) {
433                               edata->value_cur = 1870000 * data / 192;
434                               edata->state = ENVSYS_SVALID;
435                               break;
436                     }
437                     if (data == 0xff) {
438                               edata->state = ENVSYS_SINVALID;
439                               break;
440                     }
441                     tmp = data * sc->sc_fan1_div;
442                     if (tmp == 0)
443                               edata->state = ENVSYS_SINVALID;
444                     else {
445                               edata->value_cur = 1350000 / tmp;
446                               edata->state = ENVSYS_SVALID;
447                     }
448                     break;
449           case LMENV_FAN2:
450                     /* Might be analogue input 2 */
451                     if (sc->sc_channel & LM87_CHANNEL_AIN2) {
452                               edata->value_cur = 1870000 * data / 192;
453                               edata->state = ENVSYS_SVALID;
454                               break;
455                     }
456                     if (data == 0xff) {
457                               edata->state = ENVSYS_SINVALID;
458                               break;
459                     }
460                     tmp = data * sc->sc_fan2_div;
461                     if (tmp == 0)
462                               edata->state = ENVSYS_SINVALID;
463                     else {
464                               edata->value_cur = 1350000 / tmp;
465                               edata->state = ENVSYS_SVALID;
466                     }
467                     break;
468           default:
469                     edata->state = ENVSYS_SINVALID;
470                     break;
471           }
472 
473           iic_release_bus(sc->sc_tag, 0);
474 }
475