1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2021 Alstom Group.
5 * Copyright (c) 2021 Semihalf.
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 AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 #include "opt_platform.h"
32
33 #include <sys/param.h>
34 #include <sys/bus.h>
35 #include <sys/lock.h>
36 #include <sys/mutex.h>
37 #include <sys/module.h>
38 #include <sys/ctype.h>
39 #include <sys/kernel.h>
40 #include <sys/libkern.h>
41 #include <sys/sysctl.h>
42
43 #include <dev/iicbus/iicbus.h>
44 #include <dev/iicbus/iiconf.h>
45
46 #include <dev/ofw/ofw_bus_subr.h>
47 #include <dev/ofw/ofw_bus.h>
48
49 #define BIT(x) (1UL << (x))
50
51 /* register map */
52 #define TMP461_LOCAL_TEMP_REG_MSB 0x0
53 #define TMP461_LOCAL_TEMP_REG_LSB 0x15
54 #define TMP461_GLOBAL_TEMP_REG_MSB 0x1
55 #define TMP461_GLOBAL_TEMP_REG_LSB 0x10
56 #define TMP461_STATUS_REG 0x2
57 #define TMP461_STATUS_REG_TEMP_LOCAL BIT(2)
58 #define TMP461_CONFIG_REG_R 0x3
59 #define TMP461_CONFIG_REG_W 0x9
60 #define TMP461_CONFIG_REG_TEMP_RANGE_BIT BIT(2)
61 #define TMP461_CONFIG_REG_STANDBY_BIT BIT(6)
62 #define TMP461_CONVERSION_RATE_REG 0x4
63 #define TMP461_ONESHOT_REG 0xF
64 #define TMP461_EXTENDED_TEMP_MODIFIER 64
65
66 /* 28.4 fixed point representation of 273.15f */
67 #define TMP461_C_TO_K_FIX 4370
68
69 #define TMP461_SENSOR_MAX_CONV_TIME 16000000
70 #define TMP461_LOCAL_MEASURE 0
71 #define TMP461_REMOTE_MEASURE 1
72
73 /* flags */
74 #define TMP461_LOCAL_TEMP_DOUBLE_REG BIT(0)
75 #define TMP461_REMOTE_TEMP_DOUBLE_REG BIT(1)
76
77 static int tmp461_probe(device_t dev);
78 static int tmp461_attach(device_t dev);
79 static int tmp461_read_1(device_t dev, uint8_t reg, uint8_t *data);
80 static int tmp461_write_1(device_t dev, uint8_t reg, uint8_t data);
81 static int tmp461_read_temperature(device_t dev, int32_t *temperature, bool mode);
82 static int tmp461_detach(device_t dev);
83 static int tmp461_sensor_sysctl(SYSCTL_HANDLER_ARGS);
84
85 static device_method_t tmp461_methods[] = {
86 DEVMETHOD(device_probe, tmp461_probe),
87 DEVMETHOD(device_attach, tmp461_attach),
88 DEVMETHOD(device_detach, tmp461_detach),
89
90 DEVMETHOD_END
91 };
92
93 struct tmp461_softc {
94 struct mtx mtx;
95 uint8_t conf;
96 };
97
98 static driver_t tmp461_driver = {
99 "tmp461_dev",
100 tmp461_methods,
101 sizeof(struct tmp461_softc)
102 };
103
104 struct tmp461_data {
105 const char *compat;
106 const char *desc;
107 uint8_t flags;
108 };
109
110 static struct tmp461_data sensor_list[] = {
111 {"adt7461", "ADT7461 Thernal Sensor Information",
112 TMP461_REMOTE_TEMP_DOUBLE_REG},
113 {"tmp461", "TMP461 Thernal Sensor Information",
114 TMP461_LOCAL_TEMP_DOUBLE_REG | TMP461_REMOTE_TEMP_DOUBLE_REG}
115 };
116
117 static struct ofw_compat_data tmp461_compat_data[] = {
118 {"adi,adt7461", (uintptr_t)&sensor_list[0]},
119 {"ti,tmp461", (uintptr_t)&sensor_list[1]},
120 {NULL, 0}
121 };
122
123 DRIVER_MODULE(tmp461, iicbus, tmp461_driver, 0, 0);
124 IICBUS_FDT_PNP_INFO(tmp461_compat_data);
125
126 static int
127 tmp461_attach(device_t dev)
128 {
129 struct sysctl_oid *sensor_root_oid;
130 struct tmp461_data *compat_data;
131 struct sysctl_ctx_list *ctx;
132 struct tmp461_softc *sc;
133 uint8_t data;
134
135 sc = device_get_softc(dev);
136 compat_data = (struct tmp461_data *)
137 ofw_bus_search_compatible(dev, tmp461_compat_data)->ocd_data;
138 sc->conf = compat_data->flags;
139 ctx = device_get_sysctl_ctx(dev);
140
141 mtx_init(&sc->mtx, "tmp461 temperature", "temperature", MTX_DEF);
142
143 sensor_root_oid = SYSCTL_ADD_NODE(ctx, SYSCTL_STATIC_CHILDREN(_hw),
144 OID_AUTO, "temperature", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
145 "Thermal Sensor Information");
146 if (sensor_root_oid == NULL)
147 return (ENXIO);
148
149 SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(sensor_root_oid), OID_AUTO,
150 "local_sensor", CTLTYPE_INT | CTLFLAG_RD, dev,
151 TMP461_LOCAL_MEASURE, tmp461_sensor_sysctl,
152 "IK1", compat_data->desc);
153
154 /* get status register */
155 if (tmp461_read_1(dev, TMP461_STATUS_REG, &data) != 0)
156 return (ENXIO);
157
158 if (!(data & TMP461_STATUS_REG_TEMP_LOCAL))
159 SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(sensor_root_oid), OID_AUTO,
160 "remote_sensor", CTLTYPE_INT | CTLFLAG_RD, dev,
161 TMP461_REMOTE_MEASURE, tmp461_sensor_sysctl,
162 "IK1", compat_data->desc);
163
164 /* set standby mode */
165 if (tmp461_read_1(dev, TMP461_CONFIG_REG_R, &data) != 0)
166 return (ENXIO);
167
168 data |= TMP461_CONFIG_REG_STANDBY_BIT;
169 if (tmp461_write_1(dev, TMP461_CONFIG_REG_W, data) != 0)
170 return (ENXIO);
171
172 return (0);
173 }
174
175 static int
176 tmp461_probe(device_t dev)
177 {
178 struct tmp461_data *compat_data;
179
180 if (!ofw_bus_status_okay(dev))
181 return (ENXIO);
182
183 compat_data = (struct tmp461_data *)
184 ofw_bus_search_compatible(dev, tmp461_compat_data)->ocd_data;
185 if (!compat_data)
186 return (ENXIO);
187
188 device_set_desc(dev, compat_data->compat);
189
190 return (BUS_PROBE_GENERIC);
191 }
192
193 static int
194 tmp461_detach(device_t dev)
195 {
196 struct tmp461_softc *sc;
197
198 sc = device_get_softc(dev);
199 mtx_destroy(&sc->mtx);
200
201 return (0);
202 }
203
204 static int
205 tmp461_read_1(device_t dev, uint8_t reg, uint8_t *data)
206 {
207 int error;
208
209 error = iicdev_readfrom(dev, reg, (void *) data, 1, IIC_DONTWAIT);
210 if (error != 0)
211 device_printf(dev, "Failed to read from device\n");
212
213 return (error);
214 }
215
216 static int
217 tmp461_write_1(device_t dev, uint8_t reg, uint8_t data)
218 {
219 int error;
220
221 error = iicdev_writeto(dev, reg, (void *) &data, 1, IIC_DONTWAIT);
222 if (error != 0)
223 device_printf(dev, "Failed to write to device\n");
224
225 return (error);
226 }
227
228 static int
229 tmp461_read_temperature(device_t dev, int32_t *temperature, bool remote_measure)
230 {
231 uint8_t data, offset, reg;
232 struct tmp461_softc *sc;
233 int error;
234
235 sc = device_get_softc(dev);
236
237 mtx_lock(&sc->mtx);
238
239 error = tmp461_read_1(dev, TMP461_CONVERSION_RATE_REG, &data);
240 if (error != 0)
241 goto fail;
242
243 /* trigger sample*/
244 error = tmp461_write_1(dev, TMP461_ONESHOT_REG, 0xFF);
245 if (error != 0)
246 goto fail;
247
248 /* wait for conversion time */
249 DELAY(TMP461_SENSOR_MAX_CONV_TIME/(1UL<<data));
250
251 /* read config register offset */
252 error = tmp461_read_1(dev, TMP461_CONFIG_REG_R, &data);
253 if (error != 0)
254 goto fail;
255
256 offset = (data & TMP461_CONFIG_REG_TEMP_RANGE_BIT ?
257 TMP461_EXTENDED_TEMP_MODIFIER : 0);
258
259 reg = remote_measure ?
260 TMP461_GLOBAL_TEMP_REG_MSB : TMP461_LOCAL_TEMP_REG_MSB;
261
262 /* read temeperature value*/
263 error = tmp461_read_1(dev, reg, &data);
264 if (error != 0)
265 goto fail;
266
267 data -= offset;
268 *temperature = signed_extend32(data, 0, 8) << 4;
269
270 if (remote_measure) {
271 if (sc->conf & TMP461_REMOTE_TEMP_DOUBLE_REG) {
272 error = tmp461_read_1(dev,
273 TMP461_GLOBAL_TEMP_REG_LSB, &data);
274 if (error != 0)
275 goto fail;
276
277 *temperature |= data >> 4;
278 }
279 } else {
280 if (sc->conf & TMP461_LOCAL_TEMP_DOUBLE_REG) {
281 error = tmp461_read_1(dev,
282 TMP461_LOCAL_TEMP_REG_LSB, &data);
283 if (error != 0)
284 goto fail;
285
286 *temperature |= data >> 4;
287 }
288 }
289 *temperature = (((*temperature + TMP461_C_TO_K_FIX) * 10) >> 4);
290
291 fail:
292 mtx_unlock(&sc->mtx);
293 return (error);
294 }
295
296 static int
297 tmp461_sensor_sysctl(SYSCTL_HANDLER_ARGS)
298 {
299 int32_t temperature;
300 device_t dev;
301 int error;
302 bool mode;
303
304 dev = arg1;
305 mode = arg2;
306
307 error = tmp461_read_temperature(dev, &temperature, mode);
308 if (error != 0)
309 return (error);
310
311 return (sysctl_handle_int(oidp, &temperature, 0, req));
312 }
Cache object: ab654210812e5f32a5668656a85b2d80
|