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 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 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 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 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 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 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 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