1 /* $NetBSD: sdtemp.c,v 1.6 2009/06/14 19:44:46 pgoyette Exp $ */ 2 3 /* 4 * Copyright (c) 2009 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Paul Goyette. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: sdtemp.c,v 1.6 2009/06/14 19:44:46 pgoyette Exp $"); 34 35 #include <sys/param.h> 36 #include <sys/systm.h> 37 #include <sys/kmem.h> 38 #include <sys/device.h> 39 #include <sys/kernel.h> 40 #include <sys/endian.h> 41 42 #include <dev/sysmon/sysmonvar.h> 43 44 #include <dev/i2c/i2cvar.h> 45 #include <dev/i2c/sdtemp_reg.h> 46 47 struct sdtemp_softc { 48 device_t sc_dev; 49 i2c_tag_t sc_tag; 50 int sc_address; 51 52 struct sysmon_envsys *sc_sme; 53 envsys_data_t *sc_sensor; 54 int sc_resolution; 55 uint16_t sc_capability; 56 }; 57 58 static int sdtemp_match(device_t, cfdata_t, void *); 59 static void sdtemp_attach(device_t, device_t, void *); 60 61 CFATTACH_DECL_NEW(sdtemp, sizeof(struct sdtemp_softc), 62 sdtemp_match, sdtemp_attach, NULL, NULL); 63 64 static void sdtemp_refresh(struct sysmon_envsys *, envsys_data_t *); 65 static void sdtemp_get_limits(struct sysmon_envsys *, envsys_data_t *, 66 sysmon_envsys_lim_t *); 67 static void sdtemp_set_limits(struct sysmon_envsys *, envsys_data_t *, 68 sysmon_envsys_lim_t *); 69 #ifdef NOT_YET 70 static int sdtemp_read_8(struct sdtemp_softc *, uint8_t, uint8_t *); 71 static int sdtemp_write_8(struct sdtemp_softc *, uint8_t, uint8_t); 72 #endif /* NOT YET */ 73 static int sdtemp_read_16(struct sdtemp_softc *, uint8_t, uint16_t *); 74 static int sdtemp_write_16(struct sdtemp_softc *, uint8_t, uint16_t); 75 static uint32_t sdtemp_decode_temp(struct sdtemp_softc *, uint16_t); 76 static bool sdtemp_pmf_suspend(device_t PMF_FN_PROTO); 77 static bool sdtemp_pmf_resume(device_t PMF_FN_PROTO); 78 79 struct sdtemp_dev_entry { 80 const uint16_t sdtemp_mfg_id; 81 const uint8_t sdtemp_dev_id; 82 const uint8_t sdtemp_rev_id; 83 const uint8_t sdtemp_resolution; 84 const char *sdtemp_desc; 85 }; 86 87 /* Convert sysmon_envsys uKelvin value to simple degC */ 88 89 #define __UK2C(uk) (((uk) - 273150000) / 1000000) 90 91 /* 92 * List of devices known to conform to JEDEC JC42.4 93 * 94 * NOTE: A non-negative value for resolution indicates that the sensor 95 * resolution is fixed at that number of fractional bits; a negative 96 * value indicates that the sensor needs to be configured. In either 97 * case, trip-point registers are fixed at two-bit (0.25C) resolution. 98 */ 99 static const struct sdtemp_dev_entry 100 sdtemp_dev_table[] = { 101 { MAXIM_MANUFACTURER_ID, MAX_6604_DEVICE_ID, 0xff, 3, 102 "Maxim MAX604" }, 103 { MCP_MANUFACTURER_ID, MCP_9805_DEVICE_ID, 0xff, 2, 104 "Microchip Tech MCP9805" }, 105 { MCP_MANUFACTURER_ID, MCP_98242_DEVICE_ID, 0xff, -4, 106 "Microchip Tech MCP98242" }, 107 { ADT_MANUFACTURER_ID, ADT_7408_DEVICE_ID, 0xff, 4, 108 "Analog Devices ADT7408" }, 109 { NXP_MANUFACTURER_ID, NXP_SE97_DEVICE_ID, 0xff, 3, 110 "NXP Semiconductors SE97/SE98" }, 111 { STTS_MANUFACTURER_ID, STTS_424E02_DEVICE_ID, 0x00, 2, 112 "STmicroelectronics STTS424E02-DA" }, 113 { STTS_MANUFACTURER_ID, STTS_424E02_DEVICE_ID, 0x01, 2, 114 "STmicroelectronics STTS424E02-DN" }, 115 { CAT_MANUFACTURER_ID, CAT_34TS02_DEVICE_ID, 0xff, 4, 116 "Catalyst CAT34TS02/CAT6095" }, 117 { 0, 0, 0, 2, "Unknown" } 118 }; 119 120 static int 121 sdtemp_lookup(uint16_t mfg, uint16_t dev, uint16_t rev) 122 { 123 int i; 124 125 for (i = 0; sdtemp_dev_table[i].sdtemp_mfg_id; i++) 126 if (sdtemp_dev_table[i].sdtemp_mfg_id == mfg && 127 sdtemp_dev_table[i].sdtemp_dev_id == dev && 128 (sdtemp_dev_table[i].sdtemp_rev_id == 0xff || 129 sdtemp_dev_table[i].sdtemp_rev_id == rev)) 130 break; 131 132 return i; 133 } 134 135 static int 136 sdtemp_match(device_t parent, cfdata_t cf, void *aux) 137 { 138 struct i2c_attach_args *ia = aux; 139 uint16_t mfgid, devid; 140 struct sdtemp_softc sc; 141 int i, error; 142 143 sc.sc_tag = ia->ia_tag; 144 sc.sc_address = ia->ia_addr; 145 146 if ((ia->ia_addr & SDTEMP_ADDRMASK) != SDTEMP_ADDR) 147 return 0; 148 149 /* Verify that we can read the manufacturer ID & Device ID */ 150 iic_acquire_bus(sc.sc_tag, 0); 151 error = sdtemp_read_16(&sc, SDTEMP_REG_MFG_ID, &mfgid) | 152 sdtemp_read_16(&sc, SDTEMP_REG_DEV_REV, &devid); 153 iic_release_bus(sc.sc_tag, 0); 154 155 if (error) 156 return 0; 157 158 i = sdtemp_lookup(mfgid, devid >> 8, devid & 0xff); 159 if (sdtemp_dev_table[i].sdtemp_mfg_id == 0) { 160 aprint_debug("sdtemp: No match for mfg 0x%04x dev 0x%02x " 161 "rev 0x%02x at address 0x%02x\n", mfgid, devid >> 8, 162 devid & 0xff, sc.sc_address); 163 return 0; 164 } 165 166 return 1; 167 } 168 169 static void 170 sdtemp_attach(device_t parent, device_t self, void *aux) 171 { 172 struct sdtemp_softc *sc = device_private(self); 173 struct i2c_attach_args *ia = aux; 174 sysmon_envsys_lim_t limits; 175 uint16_t mfgid, devid; 176 int i, error; 177 178 sc->sc_tag = ia->ia_tag; 179 sc->sc_address = ia->ia_addr; 180 sc->sc_dev = self; 181 182 iic_acquire_bus(sc->sc_tag, 0); 183 if ((error = sdtemp_read_16(sc, SDTEMP_REG_MFG_ID, &mfgid)) != 0 || 184 (error = sdtemp_read_16(sc, SDTEMP_REG_DEV_REV, &devid)) != 0) { 185 iic_release_bus(sc->sc_tag, 0); 186 aprint_error(": attach error %d\n", error); 187 return; 188 } 189 i = sdtemp_lookup(mfgid, devid >> 8, devid & 0xff); 190 sc->sc_resolution = 191 sdtemp_dev_table[i].sdtemp_resolution; 192 193 aprint_naive(": Temp Sensor\n"); 194 aprint_normal(": %s Temp Sensor\n", sdtemp_dev_table[i].sdtemp_desc); 195 196 if (sdtemp_dev_table[i].sdtemp_mfg_id == 0) 197 aprint_debug_dev(self, 198 "mfg 0x%04x dev 0x%02x rev 0x%02x at addr 0x%02x\n", 199 mfgid, devid >> 8, devid & 0xff, ia->ia_addr); 200 201 /* 202 * Alarm capability is required; if not present, this is likely 203 * not a real sdtemp device. 204 */ 205 error = sdtemp_read_16(sc, SDTEMP_REG_CAPABILITY, &sc->sc_capability); 206 if (error != 0 || (sc->sc_capability & SDTEMP_CAP_HAS_ALARM) == 0) { 207 iic_release_bus(sc->sc_tag, 0); 208 aprint_error_dev(self, 209 "required alarm capability not present!\n"); 210 return; 211 } 212 /* Set the configuration to defaults. */ 213 error = sdtemp_write_16(sc, SDTEMP_REG_CONFIG, 0); 214 if (error != 0) { 215 iic_release_bus(sc->sc_tag, 0); 216 aprint_error_dev(self, "error %d writing config register\n", 217 error); 218 return; 219 } 220 /* If variable resolution, set to max */ 221 if (sc->sc_resolution < 0) { 222 sc->sc_resolution = ~sc->sc_resolution; 223 error = sdtemp_write_16(sc, SDTEMP_REG_RESOLUTION, 224 sc->sc_resolution & 0x3); 225 if (error != 0) { 226 iic_release_bus(sc->sc_tag, 0); 227 aprint_error_dev(self, 228 "error %d writing resolution register\n", error); 229 return; 230 } else 231 sc->sc_resolution++; 232 } 233 iic_release_bus(sc->sc_tag, 0); 234 235 /* Hook us into the sysmon_envsys subsystem */ 236 sc->sc_sme = sysmon_envsys_create(); 237 sc->sc_sme->sme_name = device_xname(self); 238 sc->sc_sme->sme_cookie = sc; 239 sc->sc_sme->sme_refresh = sdtemp_refresh; 240 sc->sc_sme->sme_get_limits = sdtemp_get_limits; 241 sc->sc_sme->sme_set_limits = sdtemp_set_limits; 242 243 sc->sc_sensor = kmem_zalloc(sizeof(envsys_data_t), KM_NOSLEEP); 244 if (!sc->sc_sensor) { 245 aprint_error_dev(self, "unable to allocate sc_sensor\n"); 246 goto bad2; 247 } 248 249 /* Initialize sensor data. */ 250 sc->sc_sensor->units = ENVSYS_STEMP; 251 sc->sc_sensor->state = ENVSYS_SINVALID; 252 sc->sc_sensor->flags |= ENVSYS_FMONLIMITS; 253 sc->sc_sensor->monitor = true; 254 (void)strlcpy(sc->sc_sensor->desc, device_xname(self), 255 sizeof(sc->sc_sensor->desc)); 256 257 /* Now attach the sensor */ 258 if (sysmon_envsys_sensor_attach(sc->sc_sme, sc->sc_sensor)) { 259 aprint_error_dev(self, "unable to attach sensor\n"); 260 goto bad; 261 } 262 263 /* Register the device */ 264 error = sysmon_envsys_register(sc->sc_sme); 265 if (error) { 266 aprint_error_dev(self, "error %d registering with sysmon\n", 267 error); 268 goto bad; 269 } 270 271 if (!pmf_device_register(self, sdtemp_pmf_suspend, sdtemp_pmf_resume)) 272 aprint_error_dev(self, "couldn't establish power handler\n"); 273 274 /* Retrieve and display hardware monitor limits */ 275 sdtemp_get_limits(sc->sc_sme, sc->sc_sensor, &limits); 276 aprint_normal_dev(self, ""); 277 i = 0; 278 if (limits.sel_flags & PROP_WARNMIN) { 279 aprint_normal("low limit %dC", __UK2C(limits.sel_warnmin)); 280 i++; 281 } 282 if (limits.sel_flags & PROP_WARNMAX) { 283 aprint_normal("%shigh limit %dC ", (i)?", ":"", 284 __UK2C(limits.sel_warnmax)); 285 i++; 286 } 287 if (limits.sel_flags & PROP_CRITMAX) { 288 aprint_normal("%scritical limit %dC ", (i)?", ":"", 289 __UK2C(limits.sel_critmax)); 290 i++; 291 } 292 if (i == 0) 293 aprint_normal("no hardware limits set\n"); 294 else 295 aprint_normal("\n"); 296 297 return; 298 299 bad: 300 kmem_free(sc->sc_sensor, sizeof(envsys_data_t)); 301 bad2: 302 sysmon_envsys_destroy(sc->sc_sme); 303 } 304 305 /* Retrieve current limits from device, and encode in uKelvins */ 306 static void 307 sdtemp_get_limits(struct sysmon_envsys *sme, envsys_data_t *edata, 308 sysmon_envsys_lim_t *limits) 309 { 310 struct sdtemp_softc *sc = sme->sme_cookie; 311 uint16_t lim; 312 313 limits->sel_flags = PROP_DRIVER_LIMITS; 314 iic_acquire_bus(sc->sc_tag, 0); 315 if (sdtemp_read_16(sc, SDTEMP_REG_LOWER_LIM, &lim) == 0 && lim != 0) { 316 limits->sel_warnmin = sdtemp_decode_temp(sc, lim); 317 limits->sel_flags |= PROP_WARNMIN; 318 } 319 if (sdtemp_read_16(sc, SDTEMP_REG_UPPER_LIM, &lim) == 0 && lim != 0) { 320 limits->sel_warnmax = sdtemp_decode_temp(sc, lim); 321 limits->sel_flags |= PROP_WARNMAX; 322 } 323 if (sdtemp_read_16(sc, SDTEMP_REG_CRIT_LIM, &lim) == 0 && lim != 0) { 324 limits->sel_critmax = sdtemp_decode_temp(sc, lim); 325 limits->sel_flags |= PROP_CRITMAX; 326 } 327 iic_release_bus(sc->sc_tag, 0); 328 } 329 330 /* Send current limit values to the device */ 331 static void 332 sdtemp_set_limits(struct sysmon_envsys *sme, envsys_data_t *edata, 333 sysmon_envsys_lim_t *limits) 334 { 335 uint16_t val; 336 struct sdtemp_softc *sc = sme->sme_cookie; 337 338 iic_acquire_bus(sc->sc_tag, 0); 339 if (limits->sel_flags & PROP_WARNMIN) { 340 val = __UK2C(limits->sel_warnmin); 341 (void)sdtemp_write_16(sc, SDTEMP_REG_LOWER_LIM, 342 (val << 4) & SDTEMP_TEMP_MASK); 343 } 344 if (limits->sel_flags & PROP_WARNMAX) { 345 val = __UK2C(limits->sel_warnmax); 346 (void)sdtemp_write_16(sc, SDTEMP_REG_UPPER_LIM, 347 (val << 4) & SDTEMP_TEMP_MASK); 348 } 349 if (limits->sel_flags & PROP_CRITMAX) { 350 val = __UK2C(limits->sel_critmax); 351 (void)sdtemp_write_16(sc, SDTEMP_REG_CRIT_LIM, 352 (val << 4) & SDTEMP_TEMP_MASK); 353 } 354 iic_release_bus(sc->sc_tag, 0); 355 356 /* 357 * If at least one limit is set that we can handle, and no 358 * limits are set that we cannot handle, tell sysmon that 359 * the driver will take care of monitoring the limits! 360 */ 361 if (limits->sel_flags & (PROP_CRITMIN | PROP_BATTCAP | PROP_BATTWARN)) 362 limits->sel_flags &= ~PROP_DRIVER_LIMITS; 363 else if (limits->sel_flags & PROP_LIMITS) 364 limits->sel_flags |= PROP_DRIVER_LIMITS; 365 else 366 limits->sel_flags &= ~PROP_DRIVER_LIMITS; 367 } 368 369 #ifdef NOT_YET /* All registers on these sensors are 16-bits */ 370 371 /* Read a 8-bit value from a register */ 372 static int 373 sdtemp_read_8(struct sdtemp_softc *sc, uint8_t reg, uint8_t *valp) 374 { 375 int error; 376 377 error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 378 sc->sc_address, ®, 1, valp, sizeof(*valp), 0); 379 380 return error; 381 } 382 383 static int 384 sdtemp_write_8(struct sdtemp_softc *sc, uint8_t reg, uint8_t val) 385 { 386 return iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 387 sc->sc_address, ®, 1, &val, sizeof(val), 0); 388 } 389 #endif /* NOT_YET */ 390 391 /* Read a 16-bit value from a register */ 392 static int 393 sdtemp_read_16(struct sdtemp_softc *sc, uint8_t reg, uint16_t *valp) 394 { 395 int error; 396 397 error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 398 sc->sc_address, ®, 1, valp, sizeof(*valp), 0); 399 if (error) 400 return error; 401 402 *valp = be16toh(*valp); 403 404 return 0; 405 } 406 407 static int 408 sdtemp_write_16(struct sdtemp_softc *sc, uint8_t reg, uint16_t val) 409 { 410 uint16_t temp; 411 412 temp = htobe16(val); 413 return iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 414 sc->sc_address, ®, 1, &temp, sizeof(temp), 0); 415 } 416 417 static uint32_t 418 sdtemp_decode_temp(struct sdtemp_softc *sc, uint16_t temp) 419 { 420 uint32_t val; 421 int32_t stemp; 422 423 /* Get only the temperature bits */ 424 temp &= SDTEMP_TEMP_MASK; 425 426 /* If necessary, extend the sign bit */ 427 if ((sc->sc_capability & SDTEMP_CAP_WIDER_RANGE) && 428 (temp & SDTEMP_TEMP_NEGATIVE)) 429 temp |= SDTEMP_TEMP_SIGN_EXT; 430 431 /* Mask off only bits valid within current resolution */ 432 temp &= ~(0xf >> sc->sc_resolution); 433 434 /* Treat as signed and extend to 32-bits */ 435 stemp = (int16_t)temp; 436 437 /* Now convert from 0.0625 (1/16) deg C increments to microKelvins */ 438 val = (stemp * 62500) + 273150000; 439 440 return val; 441 } 442 443 static void 444 sdtemp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 445 { 446 struct sdtemp_softc *sc = sme->sme_cookie; 447 uint16_t val; 448 int error; 449 450 iic_acquire_bus(sc->sc_tag, 0); 451 error = sdtemp_read_16(sc, SDTEMP_REG_AMBIENT_TEMP, &val); 452 iic_release_bus(sc->sc_tag, 0); 453 454 if (error) { 455 edata->state = ENVSYS_SINVALID; 456 return; 457 } 458 459 edata->value_cur = sdtemp_decode_temp(sc, val); 460 461 /* Now check for limits */ 462 if ((edata->upropset & PROP_DRIVER_LIMITS) == 0) 463 edata->state = ENVSYS_SVALID; 464 else if (val & SDTEMP_ABOVE_CRIT) 465 edata->state = ENVSYS_SCRITOVER; 466 else if (val & SDTEMP_ABOVE_UPPER) 467 edata->state = ENVSYS_SWARNOVER; 468 else if (val & SDTEMP_BELOW_LOWER) 469 edata->state = ENVSYS_SWARNUNDER; 470 else 471 edata->state = ENVSYS_SVALID; 472 } 473 474 /* 475 * power management functions 476 * 477 * We go into "shutdown" mode at suspend time, and return to normal 478 * mode upon resume. This reduces power consumption by disabling 479 * the A/D converter. 480 */ 481 482 static bool 483 sdtemp_pmf_suspend(device_t dev PMF_FN_ARGS) 484 { 485 struct sdtemp_softc *sc = device_private(dev); 486 int error; 487 uint16_t config; 488 489 iic_acquire_bus(sc->sc_tag, 0); 490 error = sdtemp_read_16(sc, SDTEMP_REG_CONFIG, &config); 491 if (error == 0) { 492 config |= SDTEMP_CONFIG_SHUTDOWN_MODE; 493 error = sdtemp_write_16(sc, SDTEMP_REG_CONFIG, config); 494 } 495 iic_release_bus(sc->sc_tag, 0); 496 return (error == 0); 497 } 498 499 static bool 500 sdtemp_pmf_resume(device_t dev PMF_FN_ARGS) 501 { 502 struct sdtemp_softc *sc = device_private(dev); 503 int error; 504 uint16_t config; 505 506 iic_acquire_bus(sc->sc_tag, 0); 507 error = sdtemp_read_16(sc, SDTEMP_REG_CONFIG, &config); 508 if (error == 0) { 509 config &= ~SDTEMP_CONFIG_SHUTDOWN_MODE; 510 error = sdtemp_write_16(sc, SDTEMP_REG_CONFIG, config); 511 } 512 iic_release_bus(sc->sc_tag, 0); 513 return (error == 0); 514 } 515