1 /* $NetBSD: ibmhawk.c,v 1.8 2018/09/03 16:29:31 riastradh Exp $ */ 2 3 /*- 4 * Copyright (c) 2011 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Juergen Hannken-Illjes. 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/systm.h> 33 #include <sys/param.h> 34 #include <sys/kernel.h> 35 #include <sys/device.h> 36 #include <sys/bswap.h> 37 38 #include <dev/sysmon/sysmonvar.h> 39 #include <dev/i2c/i2cvar.h> 40 #include <dev/i2c/ibmhawkreg.h> 41 #include <dev/i2c/ibmhawkvar.h> 42 43 #if !defined(IBMHAWK_DEBUG) /* Set to 2 for verbose debug. */ 44 #if defined(DEBUG) 45 #define IBMHAWK_DEBUG 1 46 #else 47 #define IBMHAWK_DEBUG 0 48 #endif 49 #endif 50 51 /* 52 * Known sensors. 53 */ 54 static struct ibmhawk_sensordesc { 55 const char *desc; 56 uint32_t units; 57 int offset; 58 } ibmhawk_sensors[] = { 59 { "Ambient temperature", ENVSYS_STEMP, IBMHAWK_T_AMBIENT }, 60 { "CPU 1 temperature", ENVSYS_STEMP, IBMHAWK_T_CPU }, 61 { "CPU 2 temperature", ENVSYS_STEMP, IBMHAWK_T_CPU+1 }, 62 { "12 Voltage sensor", ENVSYS_SVOLTS_DC, IBMHAWK_V_VOLTAGE }, 63 { "5 Voltage sensor", ENVSYS_SVOLTS_DC, IBMHAWK_V_VOLTAGE+1 }, 64 { "3.3 Voltage sensor", ENVSYS_SVOLTS_DC, IBMHAWK_V_VOLTAGE+2 }, 65 { "2.5 Voltage sensor", ENVSYS_SVOLTS_DC, IBMHAWK_V_VOLTAGE+3 }, 66 { "1.5 Voltage sensor", ENVSYS_SVOLTS_DC, IBMHAWK_V_VOLTAGE+4 }, 67 { "1.25 Voltage sensor", ENVSYS_SVOLTS_DC, IBMHAWK_V_VOLTAGE+5 }, 68 { "VRM 1", ENVSYS_SVOLTS_DC, IBMHAWK_V_VOLTAGE+6 }, 69 { "Fan 1", ENVSYS_SFANRPM, IBMHAWK_F_FAN }, 70 { "Fan 2", ENVSYS_SFANRPM, IBMHAWK_F_FAN+1 }, 71 { "Fan 3", ENVSYS_SFANRPM, IBMHAWK_F_FAN+2 }, 72 { "Fan 4", ENVSYS_SFANRPM, IBMHAWK_F_FAN+3 }, 73 { "Fan 5", ENVSYS_SFANRPM, IBMHAWK_F_FAN+4 }, 74 { "Fan 6", ENVSYS_SFANRPM, IBMHAWK_F_FAN+5 }, 75 }; 76 static const int ibmhawk_num_sensors = 77 (sizeof(ibmhawk_sensors)/sizeof(ibmhawk_sensors[0])); 78 79 static int ibmhawk_match(device_t, cfdata_t, void *); 80 static void ibmhawk_attach(device_t, device_t, void *); 81 static int ibmhawk_detach(device_t, int); 82 static uint8_t ibmhawk_cksum(uint8_t *); 83 static int ibmhawk_request(struct ibmhawk_softc *, 84 uint8_t, ibmhawk_response_t *); 85 static uint32_t ibmhawk_normalize(int, uint32_t); 86 static void ibmhawk_set(struct ibmhawk_softc *, int, int, bool, bool); 87 static void ibmhawk_refreshall(struct ibmhawk_softc *, bool); 88 static void ibmhawk_refresh(struct sysmon_envsys *, envsys_data_t *); 89 static void ibmhawk_get_limits(struct sysmon_envsys *, envsys_data_t *, 90 sysmon_envsys_lim_t *, uint32_t *); 91 92 CFATTACH_DECL_NEW(ibmhawk, sizeof(struct ibmhawk_softc), 93 ibmhawk_match, ibmhawk_attach, ibmhawk_detach, NULL); 94 95 static int 96 ibmhawk_match(device_t parent, cfdata_t match, void *aux) 97 { 98 struct i2c_attach_args *ia = aux; 99 ibmhawk_response_t resp; 100 static struct ibmhawk_softc sc; 101 102 /* There is an expected address for this device: */ 103 if (ia->ia_addr != 0x37) 104 return 0; 105 106 /* XXX Probe is potentially destructive. */ 107 108 sc.sc_tag = ia->ia_tag; 109 sc.sc_addr = ia->ia_addr; 110 if (ibmhawk_request(&sc, IHR_EQUIP, &resp)) 111 return 0; 112 113 return I2C_MATCH_ADDRESS_AND_PROBE; 114 } 115 116 static void 117 ibmhawk_attach(device_t parent, device_t self, void *aux) 118 { 119 struct ibmhawk_softc *sc = device_private(self); 120 struct i2c_attach_args *ia = aux; 121 ibmhawk_response_t resp; 122 int i; 123 124 sc->sc_dev = self; 125 sc->sc_tag = ia->ia_tag; 126 sc->sc_addr = ia->ia_addr; 127 128 if (!pmf_device_register(self, NULL, NULL)) 129 aprint_error_dev(self, "couldn't establish power handler\n"); 130 if (ibmhawk_request(sc, IHR_NAME, &resp)) { 131 aprint_error_dev(self, "communication failed\n"); 132 return; 133 } 134 aprint_normal(": IBM Hawk \"%.16s\"\n", resp.ihr_name); 135 if (ibmhawk_request(sc, IHR_EQUIP, &resp)) { 136 aprint_error_dev(sc->sc_dev, "equip query failed\n"); 137 return; 138 } 139 sc->sc_numcpus = uimin(resp.ihr_numcpus, IBMHAWK_MAX_CPU); 140 sc->sc_numfans = uimin(resp.ihr_numfans, IBMHAWK_MAX_FAN); 141 #if IBMHAWK_DEBUG > 0 142 aprint_normal_dev(sc->sc_dev, "monitoring %d/%d cpu(s) %d/%d fan(s)\n", 143 sc->sc_numcpus, resp.ihr_numcpus, sc->sc_numfans, resp.ihr_numfans); 144 #endif 145 /* Request and set sensor thresholds. */ 146 if (ibmhawk_request(sc, IHR_TEMP_THR, &resp)) { 147 aprint_error_dev(sc->sc_dev, "temp threshold query failed\n"); 148 return; 149 } 150 for (i = 0; i < sc->sc_numcpus; i++) 151 sc->sc_sensordata[IBMHAWK_T_CPU+i].ihs_warnmax = 152 resp.ihr_t_warn_thr; 153 if (ibmhawk_request(sc, IHR_VOLT_THR, &resp)) { 154 aprint_error_dev(sc->sc_dev, "volt threshold query failed\n"); 155 return; 156 } 157 for (i = 0; i < IBMHAWK_MAX_VOLTAGE; i++) { 158 sc->sc_sensordata[IBMHAWK_V_VOLTAGE+i].ihs_warnmax = 159 bswap16(resp.ihr_v_voltage_thr[i*2]); 160 sc->sc_sensordata[IBMHAWK_V_VOLTAGE+i].ihs_warnmin = 161 bswap16(resp.ihr_v_voltage_thr[i*2+1]); 162 } 163 if ((sc->sc_sme = sysmon_envsys_create()) == NULL) { 164 aprint_error_dev(sc->sc_dev, "sysmon_envsys_create failed\n"); 165 return; 166 } 167 ibmhawk_refreshall(sc, true); 168 sc->sc_sme->sme_name = device_xname(sc->sc_dev); 169 sc->sc_sme->sme_cookie = sc; 170 sc->sc_sme->sme_refresh = ibmhawk_refresh; 171 sc->sc_sme->sme_get_limits = ibmhawk_get_limits; 172 if (sysmon_envsys_register(sc->sc_sme)) { 173 aprint_error_dev(sc->sc_dev, "sysmon_envsys_register failed\n"); 174 sysmon_envsys_destroy(sc->sc_sme); 175 sc->sc_sme = NULL; 176 return; 177 } 178 } 179 180 static int 181 ibmhawk_detach(device_t self, int flags) 182 { 183 struct ibmhawk_softc *sc = device_private(self); 184 185 if (sc->sc_sme) 186 sysmon_envsys_destroy(sc->sc_sme); 187 return 0; 188 } 189 190 191 /* 192 * Compute the message checksum. 193 */ 194 static uint8_t 195 ibmhawk_cksum(uint8_t *buf) 196 { 197 int len = *buf++; 198 int s = 0; 199 200 while (--len > 0) 201 s += *buf++; 202 return -s; 203 } 204 205 /* 206 * Request information from the management processor. 207 * The response will be zeroed on error. 208 * Request and response have the form <n> <data 0:n-2> <checksum>. 209 */ 210 static int 211 ibmhawk_request(struct ibmhawk_softc *sc, uint8_t request, 212 ibmhawk_response_t *response) 213 { 214 int i, error, retries; 215 uint8_t buf[sizeof(ibmhawk_response_t)+3], dummy; 216 217 error = EIO; /* Fail until we have a valid response. */ 218 retries = 0; 219 220 if (iic_acquire_bus(sc->sc_tag, 0)) 221 return error; 222 223 again: 224 memset(response, 0, sizeof(*response)); 225 226 /* Build and send the request. */ 227 buf[0] = 2; 228 buf[1] = request; 229 buf[2] = ibmhawk_cksum(buf); 230 #if IBMHAWK_DEBUG > 1 231 printf("["); 232 for (i = 0; i < 3; i++) 233 printf(" %02x", buf[i]); 234 printf(" ]"); 235 #endif 236 for (i = 0; i < 3; i++) 237 if (iic_smbus_send_byte(sc->sc_tag, sc->sc_addr, buf[i], 0)) 238 goto bad; 239 240 /* Receive and check the response. */ 241 #if IBMHAWK_DEBUG > 1 242 printf(" => ["); 243 #endif 244 if (iic_smbus_receive_byte(sc->sc_tag, sc->sc_addr, &buf[0], 0)) 245 goto bad; 246 if (buf[0] == 0 || buf[0] == 255) 247 goto bad; 248 for (i = 1; i < buf[0]+1; i++) 249 if (iic_smbus_receive_byte(sc->sc_tag, sc->sc_addr, 250 (i < sizeof buf ? &buf[i] : &dummy), 0)) 251 goto bad; 252 if (buf[0] >= sizeof(buf) || buf[1] != request || 253 ibmhawk_cksum(buf) != buf[buf[0]]) 254 goto bad; 255 if (buf[0] > 2) 256 memcpy(response, buf+2, buf[0]-2); 257 error = 0; 258 259 bad: 260 #if IBMHAWK_DEBUG > 1 261 for (i = 0; i < uimin(buf[0]+1, sizeof buf); i++) 262 printf(" %02x", buf[i]); 263 printf(" ] => %d\n", error); 264 #endif 265 if (error != 0 && retries++ < 3) 266 goto again; 267 268 iic_release_bus(sc->sc_tag, 0); 269 return error; 270 } 271 272 static uint32_t 273 ibmhawk_normalize(int value, uint32_t units) 274 { 275 276 if (value == 0) 277 return 0; 278 279 switch (units) { 280 case ENVSYS_STEMP: 281 return 273150000+1000000*value; 282 case ENVSYS_SVOLTS_DC: 283 return 10000*value; 284 default: 285 return value; 286 } 287 } 288 289 static void 290 ibmhawk_set(struct ibmhawk_softc *sc, 291 int offset, int value, bool valid, bool create) 292 { 293 int i; 294 struct ibmhawk_sensordesc *sp; 295 struct ibmhawk_sensordata *sd; 296 envsys_data_t *dp; 297 298 sd = &sc->sc_sensordata[offset]; 299 dp = &sd->ihs_edata; 300 sp = NULL; 301 if (create) { 302 for (i = 0; i < ibmhawk_num_sensors; i++) 303 if (ibmhawk_sensors[i].offset == offset) { 304 sp = ibmhawk_sensors+i; 305 break; 306 } 307 if (sp == NULL) { 308 #if IBMHAWK_DEBUG > 0 309 aprint_error_dev(sc->sc_dev, 310 "offset %d: no sensor found\n", offset); 311 #endif 312 return; 313 } 314 strlcpy(dp->desc, sp->desc, sizeof(dp->desc)); 315 dp->units = sp->units; 316 if (sd->ihs_warnmin != 0 || sd->ihs_warnmax != 0) { 317 sd->ihs_warnmin = 318 ibmhawk_normalize(sd->ihs_warnmin, dp->units); 319 sd->ihs_warnmax = 320 ibmhawk_normalize(sd->ihs_warnmax, dp->units); 321 dp->flags |= ENVSYS_FMONLIMITS; 322 } 323 } 324 325 if (valid) { 326 dp->value_cur = ibmhawk_normalize(value, dp->units); 327 dp->state = ENVSYS_SVALID; 328 } else 329 dp->state = ENVSYS_SINVALID; 330 331 if (create) { 332 if (sysmon_envsys_sensor_attach(sc->sc_sme, dp)) 333 aprint_error_dev(sc->sc_dev, 334 "failed to attach \"%s\"\n", dp->desc); 335 } 336 } 337 338 static void 339 ibmhawk_refreshall(struct ibmhawk_softc *sc, bool create) 340 { 341 int i; 342 bool valid; 343 ibmhawk_response_t resp; 344 345 valid = (ibmhawk_request(sc, IHR_TEMP, &resp) == 0); 346 ibmhawk_set(sc, IBMHAWK_T_AMBIENT, resp.ihr_t_ambient, valid, create); 347 for (i = 0; i < sc->sc_numcpus; i++) 348 ibmhawk_set(sc, IBMHAWK_T_CPU+i, 349 resp.ihr_t_cpu[i], valid, create); 350 351 valid = (ibmhawk_request(sc, IHR_FANRPM, &resp) == 0); 352 for (i = 0; i < sc->sc_numfans; i++) 353 ibmhawk_set(sc, IBMHAWK_F_FAN+i, 354 bswap16(resp.ihr_fanrpm[i]), valid, create); 355 356 valid = (ibmhawk_request(sc, IHR_VOLT, &resp) == 0); 357 for (i = 0; i < IBMHAWK_MAX_VOLTAGE; i++) 358 ibmhawk_set(sc, IBMHAWK_V_VOLTAGE+i, 359 bswap16(resp.ihr_v_voltage[i]), valid, create); 360 } 361 362 static void 363 ibmhawk_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 364 { 365 struct ibmhawk_softc *sc = sme->sme_cookie; 366 367 /* No more than two refreshes per second. */ 368 if (hardclock_ticks-sc->sc_refresh < hz/2) 369 return; 370 #if IBMHAWK_DEBUG > 1 371 aprint_normal_dev(sc->sc_dev, "refresh \"%s\" delta %d\n", 372 edata->desc, hardclock_ticks-sc->sc_refresh); 373 #endif 374 sc->sc_refresh = hardclock_ticks; 375 ibmhawk_refreshall(sc, false); 376 } 377 378 static void 379 ibmhawk_get_limits(struct sysmon_envsys *sme, envsys_data_t *edata, 380 sysmon_envsys_lim_t *limits, uint32_t *props) 381 { 382 struct ibmhawk_sensordata *sd = (struct ibmhawk_sensordata *)edata; 383 384 if (sd->ihs_warnmin != 0) { 385 limits->sel_warnmin = sd->ihs_warnmin; 386 *props |= PROP_WARNMIN; 387 } 388 if (sd->ihs_warnmax != 0) { 389 limits->sel_warnmax = sd->ihs_warnmax; 390 *props |= PROP_WARNMAX; 391 } 392 } 393