dstemp.c revision 1.14.6.1 1 /* $NetBSD: dstemp.c,v 1.14.6.1 2021/09/10 15:45:28 thorpej Exp $ */
2
3 /*-
4 * Copyright (c) 2018 Michael Lorenz
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: dstemp.c,v 1.14.6.1 2021/09/10 15:45:28 thorpej Exp $");
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/device.h>
35 #include <sys/conf.h>
36 #include <sys/bus.h>
37
38 #include <dev/i2c/i2cvar.h>
39
40 #include <dev/sysmon/sysmonvar.h>
41
42 #define DSTEMP_CMD_START 0x51 /* command, no data */
43 #define DSTEMP_CMD_POR 0x54 /* command, no data */
44 #define DSTEMP_CMD_STOP 0x22 /* command, no data */
45 #define DSTEMP_TCURRENT 0xaa /* current temperature, 2 bytes */
46 #define DSTEMP_THIGH 0xa1 /* high threshold, 2 bytes */
47 #define DSTEMP_TLOW 0xa2 /* low threshold, 2 bytes */
48 #define DSTEMP_CONFIG 0xac /* 1 byte */
49
50 #define DSTEMP_1SHOT 0x01
51 #define DSTEMP_POL 0x02 /* Tout polarity, 1 - active high */
52 #define DSTEMP_8BIT 0x00
53 #define DSTEMP_10BIT 0x04
54 #define DSTEMP_11BIT 0x08
55 #define DSTEMP_12BIT 0x0c
56 #define DSTEMP_RES_MASK 0x0c
57 #define DSTEMP_NVB 0x10 /* EEPROM busy */
58 #define DSTEMP_TLF 0x20 /* temperature low flag */
59 #define DSTEMP_THF 0x40 /* temperature high flag */
60 #define DSTEMP_DONE 0x80
61
62 struct dstemp_softc {
63 device_t sc_dev;
64 i2c_tag_t sc_i2c;
65 i2c_addr_t sc_addr;
66 prop_dictionary_t sc_prop;
67 struct sysmon_envsys *sc_sme;
68 envsys_data_t sc_sensor_temp;
69 };
70
71 static int dstemp_match(device_t, cfdata_t, void *);
72 static void dstemp_attach(device_t, device_t, void *);
73
74 static void dstemp_sensors_refresh(struct sysmon_envsys *, envsys_data_t *);
75 static void dstemp_init(struct dstemp_softc *);
76
77 CFATTACH_DECL_NEW(dstemp, sizeof(struct dstemp_softc),
78 dstemp_match, dstemp_attach, NULL, NULL);
79
80 static const struct device_compatible_entry compat_data[] = {
81 { .compat = "dallas,ds1631" },
82 { .compat = "ds1631" },
83 DEVICE_COMPAT_EOL
84 };
85
86 static int
87 dstemp_match(device_t parent, cfdata_t match, void *aux)
88 {
89 struct i2c_attach_args *ia = aux;
90 int match_result;
91
92 if (iic_use_direct_match(ia, match, compat_data, &match_result))
93 return match_result;
94
95 if ((ia->ia_addr & 0xf8) == 0x48)
96 return I2C_MATCH_ADDRESS_ONLY;
97
98 return 0;
99 }
100
101 static void
102 dstemp_attach(device_t parent, device_t self, void *aux)
103 {
104 struct dstemp_softc *sc = device_private(self);
105 struct i2c_attach_args *ia = aux;
106 char name[64] = "temperature";
107 const char *desc;
108
109 sc->sc_dev = self;
110 sc->sc_i2c = ia->ia_tag;
111 sc->sc_addr = ia->ia_addr;
112
113 /* XXX XXX XXX */
114 if ((sc->sc_prop = ia->ia_prop) != NULL) {
115 prop_object_retain(sc->sc_prop);
116 }
117
118 aprint_naive("\n");
119 aprint_normal(": DS1361\n");
120
121 dstemp_init(sc);
122
123 sc->sc_sme = sysmon_envsys_create();
124 sc->sc_sme->sme_name = device_xname(self);
125 sc->sc_sme->sme_cookie = sc;
126 sc->sc_sme->sme_refresh = dstemp_sensors_refresh;
127
128 sc->sc_sensor_temp.units = ENVSYS_STEMP;
129 sc->sc_sensor_temp.state = ENVSYS_SINVALID;
130 sc->sc_sensor_temp.flags = ENVSYS_FHAS_ENTROPY;
131
132 if (prop_dictionary_get_string(sc->sc_prop, "s00", &desc)) {
133 strncpy(name, desc, 64);
134 } else if (prop_dictionary_get_string(sc->sc_prop, "saa", &desc)) {
135 strncpy(name, desc, 64);
136 }
137
138 strncpy(sc->sc_sensor_temp.desc, name, sizeof(sc->sc_sensor_temp.desc));
139
140 sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor_temp);
141
142 sysmon_envsys_register(sc->sc_sme);
143 }
144
145 static void
146 dstemp_init(struct dstemp_softc *sc)
147 {
148 int error;
149 uint8_t cmd[2], data;
150
151 if (iic_acquire_bus(sc->sc_i2c, 0))
152 return;
153 cmd[0] = DSTEMP_CONFIG;
154 data = 0;
155 error = iic_exec(sc->sc_i2c, I2C_OP_READ_WITH_STOP,
156 sc->sc_addr, cmd, 1, &data, 1, 0);
157 /* we don't want to change the POL bit, so preserve it */
158 cmd[1] = (data & DSTEMP_POL) | DSTEMP_12BIT;
159 error |= iic_exec(sc->sc_i2c, I2C_OP_WRITE_WITH_STOP,
160 sc->sc_addr, cmd, 2, NULL, 0, 0);
161 /* ... and start converting */
162 cmd[0] = DSTEMP_CMD_START;
163 error |= iic_exec(sc->sc_i2c, I2C_OP_WRITE_WITH_STOP,
164 sc->sc_addr, cmd, 1, NULL, 0, 0);
165 if (error) aprint_error_dev(sc->sc_dev, "chip initialization failed\n");
166 iic_release_bus(sc->sc_i2c, 0);
167 }
168
169 static void
170 dstemp_sensors_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
171 {
172 struct dstemp_softc *sc = sme->sme_cookie;
173 uint8_t cmd = DSTEMP_TCURRENT;
174 uint16_t data = 0;
175 int error;
176
177
178 error = iic_acquire_bus(sc->sc_i2c, 0);
179 if (error == 0) {
180 error = iic_exec(sc->sc_i2c, I2C_OP_READ_WITH_STOP,
181 sc->sc_addr, &cmd, 1, &data, 2, 0);
182 iic_release_bus(sc->sc_i2c, 0);
183 }
184
185 if (error) {
186 edata->state = ENVSYS_SINVALID;
187 } else {
188 edata->value_cur =
189 ((uint64_t)(be16toh(data) >> 4) * 62500) +
190 + 273150000;
191 if (edata->value_cur > (273150000 + 120000000)) {
192 edata->state = ENVSYS_SINVALID;
193 } else
194 edata->state = ENVSYS_SVALID;
195 }
196 }
197