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