1 /* $NetBSD: dstemp.c,v 1.14 2021/06/21 03:12:54 christos 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 2021/06/21 03:12:54 christos 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
dstemp_match(device_t parent,cfdata_t match,void * aux)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
dstemp_attach(device_t parent,device_t self,void * aux)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 sc->sc_prop = ia->ia_prop;
113 prop_object_retain(sc->sc_prop);
114
115 aprint_naive("\n");
116 aprint_normal(": DS1361\n");
117
118 dstemp_init(sc);
119
120 sc->sc_sme = sysmon_envsys_create();
121 sc->sc_sme->sme_name = device_xname(self);
122 sc->sc_sme->sme_cookie = sc;
123 sc->sc_sme->sme_refresh = dstemp_sensors_refresh;
124
125 sc->sc_sensor_temp.units = ENVSYS_STEMP;
126 sc->sc_sensor_temp.state = ENVSYS_SINVALID;
127 sc->sc_sensor_temp.flags = ENVSYS_FHAS_ENTROPY;
128
129 if (prop_dictionary_get_string(sc->sc_prop, "s00", &desc)) {
130 strncpy(name, desc, 64);
131 } else if (prop_dictionary_get_string(sc->sc_prop, "saa", &desc)) {
132 strncpy(name, desc, 64);
133 }
134
135 strncpy(sc->sc_sensor_temp.desc, name, sizeof(sc->sc_sensor_temp.desc));
136
137 sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor_temp);
138
139 sysmon_envsys_register(sc->sc_sme);
140 }
141
142 static void
dstemp_init(struct dstemp_softc * sc)143 dstemp_init(struct dstemp_softc *sc)
144 {
145 int error;
146 uint8_t cmd[2], data;
147
148 if (iic_acquire_bus(sc->sc_i2c, 0))
149 return;
150 cmd[0] = DSTEMP_CONFIG;
151 data = 0;
152 error = iic_exec(sc->sc_i2c, I2C_OP_READ_WITH_STOP,
153 sc->sc_addr, cmd, 1, &data, 1, 0);
154 /* we don't want to change the POL bit, so preserve it */
155 cmd[1] = (data & DSTEMP_POL) | DSTEMP_12BIT;
156 error |= iic_exec(sc->sc_i2c, I2C_OP_WRITE_WITH_STOP,
157 sc->sc_addr, cmd, 2, NULL, 0, 0);
158 /* ... and start converting */
159 cmd[0] = DSTEMP_CMD_START;
160 error |= iic_exec(sc->sc_i2c, I2C_OP_WRITE_WITH_STOP,
161 sc->sc_addr, cmd, 1, NULL, 0, 0);
162 if (error) aprint_error_dev(sc->sc_dev, "chip initialization failed\n");
163 iic_release_bus(sc->sc_i2c, 0);
164 }
165
166 static void
dstemp_sensors_refresh(struct sysmon_envsys * sme,envsys_data_t * edata)167 dstemp_sensors_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
168 {
169 struct dstemp_softc *sc = sme->sme_cookie;
170 uint8_t cmd = DSTEMP_TCURRENT;
171 uint16_t data = 0;
172 int error;
173
174
175 error = iic_acquire_bus(sc->sc_i2c, 0);
176 if (error == 0) {
177 error = iic_exec(sc->sc_i2c, I2C_OP_READ_WITH_STOP,
178 sc->sc_addr, &cmd, 1, &data, 2, 0);
179 iic_release_bus(sc->sc_i2c, 0);
180 }
181
182 if (error) {
183 edata->state = ENVSYS_SINVALID;
184 } else {
185 edata->value_cur =
186 ((uint64_t)(be16toh(data) >> 4) * 62500) +
187 + 273150000;
188 if (edata->value_cur > (273150000 + 120000000)) {
189 edata->state = ENVSYS_SINVALID;
190 } else
191 edata->state = ENVSYS_SVALID;
192 }
193 }
194