1/* $NetBSD: smscmon.c,v 1.3 2018/06/16 21:22:13 thorpej Exp $ */ 2 3/* 4 * Copyright (c) 2009 Takahiro Hayashi 5 * All rights reserved. 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30__KERNEL_RCSID(0, "$NetBSD: smscmon.c,v 1.3 2018/06/16 21:22:13 thorpej Exp $"); 31 32#include <sys/param.h> 33#include <sys/systm.h> 34#include <sys/device.h> 35 36#include <dev/i2c/i2cvar.h> 37#include <dev/sysmon/sysmonvar.h> 38#include <dev/i2c/smscmonvar.h> 39 40/* 41 * A driver for SMSC LPC47M192 hardware monitor at SMBus. 42 * This driver supports 8 Voltage and 3 Temperature sensors. 43 * Fan RPM monitoring is not supported in this driver because 44 * they are seen on ISA bus. 45 * 46 * Datasheet available (as of Feb. 20, 2010) at 47 * http://pdf1.alldatasheet.com/datasheet-pdf/view/109752/SMSC/LPC47M192-NC.html 48 */ 49 50static int smscmon_match(device_t, cfdata_t, void *); 51static void smscmon_attach(device_t, device_t, void *); 52static uint8_t smscmon_readreg(struct smscmon_sc *, int); 53static void smscmon_writereg(struct smscmon_sc *, int, int); 54static void smscmon_sensors_setup(struct smscmon_sc *, struct smscmon_sensor *); 55static void smscmon_refresh_volt(struct smscmon_sc *, envsys_data_t *); 56static void smscmon_refresh_temp(struct smscmon_sc *, envsys_data_t *); 57static void smscmon_refresh(struct sysmon_envsys *, envsys_data_t *); 58 59CFATTACH_DECL_NEW(smscmon, sizeof(struct smscmon_sc), 60 smscmon_match, smscmon_attach, NULL, NULL); 61 62static struct smscmon_sensor smscmon_lpc47m192[] = { 63 { 64 .desc = "+2.5V", 65 .type = ENVSYS_SVOLTS_DC, 66 .reg = 0x20, 67 .refresh = smscmon_refresh_volt, 68 .vmin = 13000, 69 .vmax = 3320000 70 }, 71 { 72 .desc = "Vccp", 73 .type = ENVSYS_SVOLTS_DC, 74 .reg = 0x21, 75 .refresh = smscmon_refresh_volt, 76 .vmin = 12000, 77 .vmax = 2988000 78 }, 79 { 80 .desc = "+3.3V", 81 .type = ENVSYS_SVOLTS_DC, 82 .reg = 0x22, 83 .refresh = smscmon_refresh_volt, 84 .vmin = 17000, 85 .vmax = 4383000 86 }, 87 { 88 .desc = "+5V", 89 .type = ENVSYS_SVOLTS_DC, 90 .reg = 0x23, 91 .refresh = smscmon_refresh_volt, 92 .vmin = 26000, 93 .vmax = 6640000 94 }, 95 { 96 .desc = "+12V", 97 .type = ENVSYS_SVOLTS_DC, 98 .reg = 0x24, 99 .refresh = smscmon_refresh_volt, 100 .vmin = 62000, 101 .vmax = 15938000 102 }, 103 { 104 .desc = "Vcc", 105 .type = ENVSYS_SVOLTS_DC, 106 .reg = 0x25, 107 .refresh = smscmon_refresh_volt, 108 .vmin = 17000, 109 .vmax = 4383000 110 }, 111 { 112 .desc = "+1.5V", 113 .type = ENVSYS_SVOLTS_DC, 114 .reg = 0x50, 115 .refresh = smscmon_refresh_volt, 116 .vmin = 8000, 117 .vmax = 1992000 118 }, 119 { 120 .desc = "+1.8V", 121 .type = ENVSYS_SVOLTS_DC, 122 .reg = 0x51, 123 .refresh = smscmon_refresh_volt, 124 .vmin = 9000, 125 .vmax = 2391000 126 }, 127 { 128 .desc = "Remote Temp1", 129 .type = ENVSYS_STEMP, 130 .reg = 0x26, 131 .refresh = smscmon_refresh_temp, 132 .vmin = 0, 133 .vmax = 0 134 }, 135 { 136 .desc = "Ambient Temp", 137 .type = ENVSYS_STEMP, 138 .reg = 0x27, 139 .refresh = smscmon_refresh_temp, 140 .vmin = 0, 141 .vmax = 0 142 }, 143 { 144 .desc = "Remote Temp2", 145 .type = ENVSYS_STEMP, 146 .reg = 0x52, 147 .refresh = smscmon_refresh_temp, 148 .vmax = 0, 149 .vmin = 0 150 }, 151 152 { .desc = NULL } 153}; 154 155static int 156smscmon_match(device_t parent, cfdata_t match, void *aux) 157{ 158 struct i2c_attach_args *ia = aux; 159 uint8_t cmd, cid, rev; 160 161 /* Address is hardwired to 010_110x */ 162 if ((ia->ia_addr & SMSCMON_ADDR_MASK) != SMSCMON_ADDR) 163 return 0; 164 165 iic_acquire_bus(ia->ia_tag, 0); 166 167 cmd = SMSCMON_REG_COMPANY; 168 if (iic_exec(ia->ia_tag, I2C_OP_READ_WITH_STOP, 169 ia->ia_addr, &cmd, sizeof cmd, &cid, sizeof cid, 0)) { 170 iic_release_bus(ia->ia_tag, 0); 171 return 0; 172 } 173 cmd = SMSCMON_REG_STEPPING; 174 if (iic_exec(ia->ia_tag, I2C_OP_READ_WITH_STOP, 175 ia->ia_addr, &cmd, sizeof cmd, &rev, sizeof rev, 0)) { 176 iic_release_bus(ia->ia_tag, 0); 177 return 0; 178 } 179 180 if ( cid != SMSC_CID_47M192 || rev != SMSC_REV_47M192) { 181 iic_release_bus(ia->ia_tag, 0); 182 return 0; 183 } 184 185 iic_release_bus(ia->ia_tag, 0); 186 return I2C_MATCH_ADDRESS_AND_PROBE; 187} 188 189static void 190smscmon_attach(device_t parent, device_t self, void *aux) 191{ 192 struct smscmon_sc *sc = device_private(self); 193 struct i2c_attach_args *ia = aux; 194 uint8_t cid, rev; 195 int i; 196 197 sc->sc_dev = self; 198 sc->sc_tag = ia->ia_tag; 199 sc->sc_addr = ia->ia_addr; 200 sc->smscmon_readreg = smscmon_readreg; 201 sc->smscmon_writereg = smscmon_writereg; 202 sc->smscmon_sensors = NULL; 203 204 cid = sc->smscmon_readreg(sc, SMSCMON_REG_COMPANY); 205 rev = sc->smscmon_readreg(sc, SMSCMON_REG_STEPPING); 206 switch (cid) { 207 case SMSC_CID_47M192: 208 if (rev == SMSC_REV_47M192) { 209 smscmon_sensors_setup(sc, smscmon_lpc47m192); 210 aprint_normal(": LPC47M192 hardware monitor\n"); 211 } 212 break; 213 default: 214 /* unknown chip */ 215 break; 216 } 217 218 if (sc->smscmon_sensors == NULL) { 219 aprint_normal(": unknown chip: cid 0x%02x rev 0x%02x\n", 220 cid, rev); 221 return; 222 } 223 224 if ((sc->sc_sme = sysmon_envsys_create()) == NULL) { 225 aprint_error_dev(sc->sc_dev, 226 "unable to create sysmon structure\n"); 227 return; 228 } 229 230 for (i = 0; i < sc->numsensors; i++) { 231 if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sensors[i])) { 232 aprint_error_dev(sc->sc_dev, 233 "unable to attach sensor\n"); 234 sysmon_envsys_destroy(sc->sc_sme); 235 return; 236 } 237 } 238 239 sc->sc_sme->sme_name = device_xname(sc->sc_dev); 240 sc->sc_sme->sme_cookie = sc; 241 sc->sc_sme->sme_refresh = smscmon_refresh; 242 if (sysmon_envsys_register(sc->sc_sme)) { 243 aprint_error_dev(sc->sc_dev, 244 "unable to register with sysmon\n"); 245 sysmon_envsys_destroy(sc->sc_sme); 246 return; 247 } 248} 249 250static uint8_t 251smscmon_readreg(struct smscmon_sc *sc, int reg) 252{ 253 uint8_t cmd, data; 254 255 iic_acquire_bus(sc->sc_tag, 0); 256 257 cmd = reg; 258 iic_smbus_read_byte(sc->sc_tag, sc->sc_addr, cmd, &data, 0); 259 260 iic_release_bus(sc->sc_tag, 0); 261 262 return data; 263} 264 265static void 266smscmon_writereg(struct smscmon_sc *sc, int reg, int val) 267{ 268 uint8_t cmd, data; 269 270 iic_acquire_bus(sc->sc_tag, 0); 271 272 cmd = reg; 273 data = val; 274 iic_smbus_write_byte(sc->sc_tag, sc->sc_addr, cmd, data, 0); 275 276 iic_release_bus(sc->sc_tag, 0); 277} 278 279static void 280smscmon_sensors_setup(struct smscmon_sc *sc, struct smscmon_sensor *sens) 281{ 282 int i; 283 284 for (i = 0; sens[i].desc; i++) { 285 strlcpy(sc->sensors[i].desc, sens[i].desc, 286 sizeof(sc->sensors[i].desc)); 287 sc->sensors[i].units = sens[i].type; 288 sc->sensors[i].state = ENVSYS_SINVALID; 289 sc->numsensors++; 290 } 291 sc->smscmon_sensors = sens; 292} 293 294static void 295smscmon_refresh_volt(struct smscmon_sc *sc, envsys_data_t *edata) 296{ 297 struct smscmon_sensor *sens = &sc->smscmon_sensors[edata->sensor]; 298 int data; 299 300 data = (*sc->smscmon_readreg)(sc, sens->reg); 301 if (data == 0xff) { 302 edata->state = ENVSYS_SINVALID; 303 } else { 304 edata->value_cur = 305 (sens->vmax - sens->vmin)/255 * data + sens->vmin; 306 edata->state = ENVSYS_SVALID; 307 } 308} 309 310static void 311smscmon_refresh_temp(struct smscmon_sc *sc, envsys_data_t *edata) 312{ 313 struct smscmon_sensor *sens = &sc->smscmon_sensors[edata->sensor]; 314 int data; 315 316 data = (*sc->smscmon_readreg)(sc, sens->reg); 317 if (data == 0xff) { 318 edata->state = ENVSYS_SINVALID; 319 } else { 320 /* convert data(degC) to uK */ 321 edata->value_cur = 273150000 + 1000000 * data; 322 edata->state = ENVSYS_SVALID; 323 } 324} 325 326static void 327smscmon_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 328{ 329 struct smscmon_sc *sc = sme->sme_cookie; 330 331 sc->smscmon_sensors[edata->sensor].refresh(sc, edata); 332} 333