1 /* $NetBSD: sdtemp.c,v 1.11 2010/02/24 22:37:57 dyoung 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.11 2010/02/24 22:37:57 dyoung 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 *, uint32_t *); 67 static void sdtemp_set_limits(struct sysmon_envsys *, envsys_data_t *, 68 sysmon_envsys_lim_t *, uint32_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, const pmf_qual_t *); 77 static bool sdtemp_pmf_resume(device_t, const pmf_qual_t *); 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 uint32_t props; 176 uint16_t mfgid, devid; 177 int i, error; 178 179 sc->sc_tag = ia->ia_tag; 180 sc->sc_address = ia->ia_addr; 181 sc->sc_dev = self; 182 183 iic_acquire_bus(sc->sc_tag, 0); 184 if ((error = sdtemp_read_16(sc, SDTEMP_REG_MFG_ID, &mfgid)) != 0 || 185 (error = sdtemp_read_16(sc, SDTEMP_REG_DEV_REV, &devid)) != 0) { 186 iic_release_bus(sc->sc_tag, 0); 187 aprint_error(": attach error %d\n", error); 188 return; 189 } 190 i = sdtemp_lookup(mfgid, devid >> 8, devid & 0xff); 191 sc->sc_resolution = 192 sdtemp_dev_table[i].sdtemp_resolution; 193 194 aprint_naive(": Temp Sensor\n"); 195 aprint_normal(": %s Temp Sensor\n", sdtemp_dev_table[i].sdtemp_desc); 196 197 if (sdtemp_dev_table[i].sdtemp_mfg_id == 0) 198 aprint_debug_dev(self, 199 "mfg 0x%04x dev 0x%02x rev 0x%02x at addr 0x%02x\n", 200 mfgid, devid >> 8, devid & 0xff, ia->ia_addr); 201 202 /* 203 * Alarm capability is required; if not present, this is likely 204 * not a real sdtemp device. 205 */ 206 error = sdtemp_read_16(sc, SDTEMP_REG_CAPABILITY, &sc->sc_capability); 207 if (error != 0 || (sc->sc_capability & SDTEMP_CAP_HAS_ALARM) == 0) { 208 iic_release_bus(sc->sc_tag, 0); 209 aprint_error_dev(self, 210 "required alarm capability not present!\n"); 211 return; 212 } 213 /* Set the configuration to defaults. */ 214 error = sdtemp_write_16(sc, SDTEMP_REG_CONFIG, 0); 215 if (error != 0) { 216 iic_release_bus(sc->sc_tag, 0); 217 aprint_error_dev(self, "error %d writing config register\n", 218 error); 219 return; 220 } 221 /* If variable resolution, set to max */ 222 if (sc->sc_resolution < 0) { 223 sc->sc_resolution = ~sc->sc_resolution; 224 error = sdtemp_write_16(sc, SDTEMP_REG_RESOLUTION, 225 sc->sc_resolution & 0x3); 226 if (error != 0) { 227 iic_release_bus(sc->sc_tag, 0); 228 aprint_error_dev(self, 229 "error %d writing resolution register\n", error); 230 return; 231 } else 232 sc->sc_resolution++; 233 } 234 iic_release_bus(sc->sc_tag, 0); 235 236 /* Hook us into the sysmon_envsys subsystem */ 237 sc->sc_sme = sysmon_envsys_create(); 238 sc->sc_sme->sme_name = device_xname(self); 239 sc->sc_sme->sme_cookie = sc; 240 sc->sc_sme->sme_refresh = sdtemp_refresh; 241 sc->sc_sme->sme_get_limits = sdtemp_get_limits; 242 sc->sc_sme->sme_set_limits = sdtemp_set_limits; 243 244 sc->sc_sensor = kmem_zalloc(sizeof(envsys_data_t), KM_NOSLEEP); 245 if (!sc->sc_sensor) { 246 aprint_error_dev(self, "unable to allocate sc_sensor\n"); 247 goto bad2; 248 } 249 250 /* Initialize sensor data. */ 251 sc->sc_sensor->units = ENVSYS_STEMP; 252 sc->sc_sensor->state = ENVSYS_SINVALID; 253 sc->sc_sensor->flags |= ENVSYS_FMONLIMITS; 254 sc->sc_sensor->monitor = true; 255 (void)strlcpy(sc->sc_sensor->desc, device_xname(self), 256 sizeof(sc->sc_sensor->desc)); 257 258 /* Now attach the sensor */ 259 if (sysmon_envsys_sensor_attach(sc->sc_sme, sc->sc_sensor)) { 260 aprint_error_dev(self, "unable to attach sensor\n"); 261 goto bad; 262 } 263 264 /* Register the device */ 265 error = sysmon_envsys_register(sc->sc_sme); 266 if (error) { 267 aprint_error_dev(self, "error %d registering with sysmon\n", 268 error); 269 goto bad; 270 } 271 272 if (!pmf_device_register(self, sdtemp_pmf_suspend, sdtemp_pmf_resume)) 273 aprint_error_dev(self, "couldn't establish power handler\n"); 274 275 /* Retrieve and display hardware monitor limits */ 276 sdtemp_get_limits(sc->sc_sme, sc->sc_sensor, &limits, &props); 277 aprint_normal_dev(self, ""); 278 i = 0; 279 if (props & PROP_WARNMIN) { 280 aprint_normal("low limit %dC", __UK2C(limits.sel_warnmin)); 281 i++; 282 } 283 if (props & PROP_WARNMAX) { 284 aprint_normal("%shigh limit %dC ", (i)?", ":"", 285 __UK2C(limits.sel_warnmax)); 286 i++; 287 } 288 if (props & PROP_CRITMAX) { 289 aprint_normal("%scritical limit %dC ", (i)?", ":"", 290 __UK2C(limits.sel_critmax)); 291 i++; 292 } 293 if (i == 0) 294 aprint_normal("no hardware limits set\n"); 295 else 296 aprint_normal("\n"); 297 298 return; 299 300 bad: 301 kmem_free(sc->sc_sensor, sizeof(envsys_data_t)); 302 bad2: 303 sysmon_envsys_destroy(sc->sc_sme); 304 } 305 306 /* Retrieve current limits from device, and encode in uKelvins */ 307 static void 308 sdtemp_get_limits(struct sysmon_envsys *sme, envsys_data_t *edata, 309 sysmon_envsys_lim_t *limits, uint32_t *props) 310 { 311 struct sdtemp_softc *sc = sme->sme_cookie; 312 uint16_t lim; 313 314 *props = 0; 315 iic_acquire_bus(sc->sc_tag, 0); 316 if (sdtemp_read_16(sc, SDTEMP_REG_LOWER_LIM, &lim) == 0 && lim != 0) { 317 limits->sel_warnmin = sdtemp_decode_temp(sc, lim); 318 *props |= PROP_WARNMIN; 319 } 320 if (sdtemp_read_16(sc, SDTEMP_REG_UPPER_LIM, &lim) == 0 && lim != 0) { 321 limits->sel_warnmax = sdtemp_decode_temp(sc, lim); 322 *props |= PROP_WARNMAX; 323 } 324 if (sdtemp_read_16(sc, SDTEMP_REG_CRIT_LIM, &lim) == 0 && lim != 0) { 325 limits->sel_critmax = sdtemp_decode_temp(sc, lim); 326 *props |= PROP_CRITMAX; 327 } 328 iic_release_bus(sc->sc_tag, 0); 329 if (*props != 0) 330 *props |= PROP_DRIVER_LIMITS; 331 } 332 333 /* Send current limit values to the device */ 334 static void 335 sdtemp_set_limits(struct sysmon_envsys *sme, envsys_data_t *edata, 336 sysmon_envsys_lim_t *limits, uint32_t *props) 337 { 338 uint16_t val; 339 struct sdtemp_softc *sc = sme->sme_cookie; 340 341 iic_acquire_bus(sc->sc_tag, 0); 342 if (*props & PROP_WARNMIN) { 343 val = __UK2C(limits->sel_warnmin); 344 (void)sdtemp_write_16(sc, SDTEMP_REG_LOWER_LIM, 345 (val << 4) & SDTEMP_TEMP_MASK); 346 } 347 if (*props & PROP_WARNMAX) { 348 val = __UK2C(limits->sel_warnmax); 349 (void)sdtemp_write_16(sc, SDTEMP_REG_UPPER_LIM, 350 (val << 4) & SDTEMP_TEMP_MASK); 351 } 352 if (*props & PROP_CRITMAX) { 353 val = __UK2C(limits->sel_critmax); 354 (void)sdtemp_write_16(sc, SDTEMP_REG_CRIT_LIM, 355 (val << 4) & SDTEMP_TEMP_MASK); 356 } 357 iic_release_bus(sc->sc_tag, 0); 358 359 /* 360 * If at least one limit is set that we can handle, and no 361 * limits are set that we cannot handle, tell sysmon that 362 * the driver will take care of monitoring the limits! 363 */ 364 if (*props & (PROP_CRITMIN | PROP_BATTCAP | PROP_BATTWARN)) 365 *props &= ~PROP_DRIVER_LIMITS; 366 else if (*props & PROP_LIMITS) 367 *props |= PROP_DRIVER_LIMITS; 368 else 369 *props &= ~PROP_DRIVER_LIMITS; 370 } 371 372 #ifdef NOT_YET /* All registers on these sensors are 16-bits */ 373 374 /* Read a 8-bit value from a register */ 375 static int 376 sdtemp_read_8(struct sdtemp_softc *sc, uint8_t reg, uint8_t *valp) 377 { 378 int error; 379 380 error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 381 sc->sc_address, ®, 1, valp, sizeof(*valp), 0); 382 383 return error; 384 } 385 386 static int 387 sdtemp_write_8(struct sdtemp_softc *sc, uint8_t reg, uint8_t val) 388 { 389 return iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 390 sc->sc_address, ®, 1, &val, sizeof(val), 0); 391 } 392 #endif /* NOT_YET */ 393 394 /* Read a 16-bit value from a register */ 395 static int 396 sdtemp_read_16(struct sdtemp_softc *sc, uint8_t reg, uint16_t *valp) 397 { 398 int error; 399 400 error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 401 sc->sc_address, ®, 1, valp, sizeof(*valp), 0); 402 if (error) 403 return error; 404 405 *valp = be16toh(*valp); 406 407 return 0; 408 } 409 410 static int 411 sdtemp_write_16(struct sdtemp_softc *sc, uint8_t reg, uint16_t val) 412 { 413 uint16_t temp; 414 415 temp = htobe16(val); 416 return iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 417 sc->sc_address, ®, 1, &temp, sizeof(temp), 0); 418 } 419 420 static uint32_t 421 sdtemp_decode_temp(struct sdtemp_softc *sc, uint16_t temp) 422 { 423 uint32_t val; 424 int32_t stemp; 425 426 /* Get only the temperature bits */ 427 temp &= SDTEMP_TEMP_MASK; 428 429 /* If necessary, extend the sign bit */ 430 if ((sc->sc_capability & SDTEMP_CAP_WIDER_RANGE) && 431 (temp & SDTEMP_TEMP_NEGATIVE)) 432 temp |= SDTEMP_TEMP_SIGN_EXT; 433 434 /* Mask off only bits valid within current resolution */ 435 temp &= ~(0xf >> sc->sc_resolution); 436 437 /* Treat as signed and extend to 32-bits */ 438 stemp = (int16_t)temp; 439 440 /* Now convert from 0.0625 (1/16) deg C increments to microKelvins */ 441 val = (stemp * 62500) + 273150000; 442 443 return val; 444 } 445 446 static void 447 sdtemp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 448 { 449 struct sdtemp_softc *sc = sme->sme_cookie; 450 uint16_t val; 451 int error; 452 453 iic_acquire_bus(sc->sc_tag, 0); 454 error = sdtemp_read_16(sc, SDTEMP_REG_AMBIENT_TEMP, &val); 455 iic_release_bus(sc->sc_tag, 0); 456 457 if (error) { 458 edata->state = ENVSYS_SINVALID; 459 return; 460 } 461 462 edata->value_cur = sdtemp_decode_temp(sc, val); 463 464 /* Now check for limits */ 465 if ((edata->upropset & PROP_DRIVER_LIMITS) == 0) 466 edata->state = ENVSYS_SVALID; 467 else if (val & SDTEMP_ABOVE_CRIT) 468 edata->state = ENVSYS_SCRITOVER; 469 else if (val & SDTEMP_ABOVE_UPPER) 470 edata->state = ENVSYS_SWARNOVER; 471 else if (val & SDTEMP_BELOW_LOWER) 472 edata->state = ENVSYS_SWARNUNDER; 473 else 474 edata->state = ENVSYS_SVALID; 475 } 476 477 /* 478 * power management functions 479 * 480 * We go into "shutdown" mode at suspend time, and return to normal 481 * mode upon resume. This reduces power consumption by disabling 482 * the A/D converter. 483 */ 484 485 static bool 486 sdtemp_pmf_suspend(device_t dev, const pmf_qual_t *qual) 487 { 488 struct sdtemp_softc *sc = device_private(dev); 489 int error; 490 uint16_t config; 491 492 iic_acquire_bus(sc->sc_tag, 0); 493 error = sdtemp_read_16(sc, SDTEMP_REG_CONFIG, &config); 494 if (error == 0) { 495 config |= SDTEMP_CONFIG_SHUTDOWN_MODE; 496 error = sdtemp_write_16(sc, SDTEMP_REG_CONFIG, config); 497 } 498 iic_release_bus(sc->sc_tag, 0); 499 return (error == 0); 500 } 501 502 static bool 503 sdtemp_pmf_resume(device_t dev, const pmf_qual_t *qual) 504 { 505 struct sdtemp_softc *sc = device_private(dev); 506 int error; 507 uint16_t config; 508 509 iic_acquire_bus(sc->sc_tag, 0); 510 error = sdtemp_read_16(sc, SDTEMP_REG_CONFIG, &config); 511 if (error == 0) { 512 config &= ~SDTEMP_CONFIG_SHUTDOWN_MODE; 513 error = sdtemp_write_16(sc, SDTEMP_REG_CONFIG, config); 514 } 515 iic_release_bus(sc->sc_tag, 0); 516 return (error == 0); 517 } 518