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