1*e1f5be9aSotto /* $OpenBSD: kb3310.c,v 1.1 2010/02/23 21:04:16 otto Exp $ */ 2*e1f5be9aSotto /* 3*e1f5be9aSotto * Copyright (c) 2010 Otto Moerbeek <otto@drijf.net> 4*e1f5be9aSotto * 5*e1f5be9aSotto * Permission to use, copy, modify, and distribute this software for any 6*e1f5be9aSotto * purpose with or without fee is hereby granted, provided that the above 7*e1f5be9aSotto * copyright notice and this permission notice appear in all copies. 8*e1f5be9aSotto * 9*e1f5be9aSotto * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10*e1f5be9aSotto * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11*e1f5be9aSotto * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12*e1f5be9aSotto * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13*e1f5be9aSotto * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14*e1f5be9aSotto * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15*e1f5be9aSotto * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16*e1f5be9aSotto */ 17*e1f5be9aSotto 18*e1f5be9aSotto #include <sys/param.h> 19*e1f5be9aSotto #include <sys/kernel.h> 20*e1f5be9aSotto #include <sys/systm.h> 21*e1f5be9aSotto #include <sys/device.h> 22*e1f5be9aSotto #include <sys/sensors.h> 23*e1f5be9aSotto 24*e1f5be9aSotto #include <machine/bus.h> 25*e1f5be9aSotto #include <dev/isa/isavar.h> 26*e1f5be9aSotto 27*e1f5be9aSotto struct cfdriver ykbec_cd = { 28*e1f5be9aSotto NULL, "ykbec", DV_DULL, 29*e1f5be9aSotto }; 30*e1f5be9aSotto 31*e1f5be9aSotto #define IO_YKBEC 0x381 32*e1f5be9aSotto #define IO_YKBECSIZE 0x3 33*e1f5be9aSotto 34*e1f5be9aSotto #define KB3310_NUM_SENSORS 10 35*e1f5be9aSotto 36*e1f5be9aSotto struct ykbec_softc { 37*e1f5be9aSotto struct device sc_dev; 38*e1f5be9aSotto bus_space_tag_t sc_iot; 39*e1f5be9aSotto bus_space_handle_t sc_ioh; 40*e1f5be9aSotto struct ksensor sc_sensor[KB3310_NUM_SENSORS]; 41*e1f5be9aSotto struct ksensordev sc_sensordev; 42*e1f5be9aSotto 43*e1f5be9aSotto }; 44*e1f5be9aSotto 45*e1f5be9aSotto int ykbec_match(struct device *, void *, void *); 46*e1f5be9aSotto void ykbec_attach(struct device *, struct device *, void *); 47*e1f5be9aSotto void ykbec_refresh(void *arg); 48*e1f5be9aSotto 49*e1f5be9aSotto const struct cfattach ykbec_ca = { 50*e1f5be9aSotto sizeof(struct ykbec_softc), ykbec_match, ykbec_attach 51*e1f5be9aSotto }; 52*e1f5be9aSotto 53*e1f5be9aSotto void ykbec_write(struct ykbec_softc *, u_int, u_int); 54*e1f5be9aSotto u_int ykbec_read(struct ykbec_softc *, u_int); 55*e1f5be9aSotto u_int ykbec_read16(struct ykbec_softc *, u_int); 56*e1f5be9aSotto 57*e1f5be9aSotto int 58*e1f5be9aSotto ykbec_match(struct device *parent, void *match, void *aux) 59*e1f5be9aSotto { 60*e1f5be9aSotto struct isa_attach_args *ia = aux; 61*e1f5be9aSotto bus_space_handle_t ioh; 62*e1f5be9aSotto 63*e1f5be9aSotto if ((ia->ia_iobase != IOBASEUNK && ia->ia_iobase != IO_YKBEC) || 64*e1f5be9aSotto /* (ia->ia_iosize != 0 && ia->ia_iosize != IO_YKBECSIZE) || XXX isa.c */ 65*e1f5be9aSotto ia->ia_maddr != MADDRUNK || ia->ia_msize != 0 || 66*e1f5be9aSotto ia->ia_irq != IRQUNK || ia->ia_drq != DRQUNK) 67*e1f5be9aSotto return (0); 68*e1f5be9aSotto 69*e1f5be9aSotto if (bus_space_map(ia->ia_iot, IO_YKBEC, IO_YKBECSIZE, 0, &ioh)) 70*e1f5be9aSotto return (0); 71*e1f5be9aSotto 72*e1f5be9aSotto bus_space_unmap(ia->ia_iot, ioh, IO_YKBECSIZE); 73*e1f5be9aSotto 74*e1f5be9aSotto ia->ia_iobase = IO_YKBEC; 75*e1f5be9aSotto ia->ia_iosize = IO_YKBECSIZE; 76*e1f5be9aSotto 77*e1f5be9aSotto return (1); 78*e1f5be9aSotto } 79*e1f5be9aSotto 80*e1f5be9aSotto 81*e1f5be9aSotto void 82*e1f5be9aSotto ykbec_attach( struct device *parent, struct device *self, void *aux) 83*e1f5be9aSotto { 84*e1f5be9aSotto struct isa_attach_args *ia = aux; 85*e1f5be9aSotto struct ykbec_softc *sc = (struct ykbec_softc *)self; 86*e1f5be9aSotto 87*e1f5be9aSotto sc->sc_iot = ia->ia_iot; 88*e1f5be9aSotto if (bus_space_map(sc->sc_iot, ia->ia_iobase, ia->ia_iosize, 0, 89*e1f5be9aSotto &sc->sc_ioh)) { 90*e1f5be9aSotto printf(": couldn't map I/O space"); 91*e1f5be9aSotto return; 92*e1f5be9aSotto } 93*e1f5be9aSotto 94*e1f5be9aSotto /* Initialize sensor data. */ 95*e1f5be9aSotto strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, 96*e1f5be9aSotto sizeof(sc->sc_sensordev.xname)); 97*e1f5be9aSotto if (sensor_task_register(sc, ykbec_refresh, 5) == NULL) { 98*e1f5be9aSotto printf(", unable to register update task\n"); 99*e1f5be9aSotto return; 100*e1f5be9aSotto } 101*e1f5be9aSotto sc->sc_sensor[0].type = SENSOR_FANRPM; 102*e1f5be9aSotto sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[0]); 103*e1f5be9aSotto 104*e1f5be9aSotto sc->sc_sensor[1].type = SENSOR_TEMP; 105*e1f5be9aSotto strlcpy(sc->sc_sensor[1].desc, "Internal temperature", 106*e1f5be9aSotto sizeof(sc->sc_sensor[1].desc)); 107*e1f5be9aSotto sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[1]); 108*e1f5be9aSotto 109*e1f5be9aSotto sc->sc_sensor[2].type = SENSOR_AMPHOUR; 110*e1f5be9aSotto strlcpy(sc->sc_sensor[2].desc, "Battery design capacity", 111*e1f5be9aSotto sizeof(sc->sc_sensor[2].desc)); 112*e1f5be9aSotto sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[2]); 113*e1f5be9aSotto 114*e1f5be9aSotto sc->sc_sensor[3].type = SENSOR_AMPHOUR; 115*e1f5be9aSotto strlcpy(sc->sc_sensor[3].desc, "Battery full charge capacity", 116*e1f5be9aSotto sizeof(sc->sc_sensor[3].desc)); 117*e1f5be9aSotto sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[3]); 118*e1f5be9aSotto 119*e1f5be9aSotto sc->sc_sensor[4].type = SENSOR_VOLTS_DC; 120*e1f5be9aSotto strlcpy(sc->sc_sensor[4].desc, "Battery design voltage", 121*e1f5be9aSotto sizeof(sc->sc_sensor[4].desc)); 122*e1f5be9aSotto sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[4]); 123*e1f5be9aSotto 124*e1f5be9aSotto sc->sc_sensor[5].type = SENSOR_AMPS; 125*e1f5be9aSotto strlcpy(sc->sc_sensor[5].desc, "Battery current", 126*e1f5be9aSotto sizeof(sc->sc_sensor[5].desc)); 127*e1f5be9aSotto sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[5]); 128*e1f5be9aSotto 129*e1f5be9aSotto sc->sc_sensor[6].type = SENSOR_VOLTS_DC; 130*e1f5be9aSotto strlcpy(sc->sc_sensor[6].desc, "Battery voltage", 131*e1f5be9aSotto sizeof(sc->sc_sensor[6].desc)); 132*e1f5be9aSotto sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[6]); 133*e1f5be9aSotto 134*e1f5be9aSotto sc->sc_sensor[7].type = SENSOR_TEMP; 135*e1f5be9aSotto strlcpy(sc->sc_sensor[7].desc, "Battery temperature", 136*e1f5be9aSotto sizeof(sc->sc_sensor[7].desc)); 137*e1f5be9aSotto sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[7]); 138*e1f5be9aSotto 139*e1f5be9aSotto sc->sc_sensor[8].type = SENSOR_PERCENT; 140*e1f5be9aSotto strlcpy(sc->sc_sensor[8].desc, "Battery capacity", 141*e1f5be9aSotto sizeof(sc->sc_sensor[8].desc)); 142*e1f5be9aSotto sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[8]); 143*e1f5be9aSotto 144*e1f5be9aSotto sc->sc_sensor[9].type = SENSOR_INTEGER; 145*e1f5be9aSotto sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[9]); 146*e1f5be9aSotto 147*e1f5be9aSotto sensordev_install(&sc->sc_sensordev); 148*e1f5be9aSotto 149*e1f5be9aSotto printf("\n"); 150*e1f5be9aSotto } 151*e1f5be9aSotto 152*e1f5be9aSotto void 153*e1f5be9aSotto ykbec_write(struct ykbec_softc *mcsc, u_int reg, u_int datum) 154*e1f5be9aSotto { 155*e1f5be9aSotto struct ykbec_softc *sc = (struct ykbec_softc *)mcsc; 156*e1f5be9aSotto bus_space_tag_t iot = sc->sc_iot; 157*e1f5be9aSotto bus_space_handle_t ioh = sc->sc_ioh; 158*e1f5be9aSotto 159*e1f5be9aSotto bus_space_write_1(iot, ioh, 0, (reg >> 8) & 0xff); 160*e1f5be9aSotto bus_space_write_1(iot, ioh, 1, (reg >> 0) & 0xff); 161*e1f5be9aSotto bus_space_write_1(iot, ioh, 2, datum); 162*e1f5be9aSotto } 163*e1f5be9aSotto 164*e1f5be9aSotto u_int 165*e1f5be9aSotto ykbec_read(struct ykbec_softc *mcsc, u_int reg) 166*e1f5be9aSotto { 167*e1f5be9aSotto struct ykbec_softc *sc = (struct ykbec_softc *)mcsc; 168*e1f5be9aSotto bus_space_tag_t iot = sc->sc_iot; 169*e1f5be9aSotto bus_space_handle_t ioh = sc->sc_ioh; 170*e1f5be9aSotto 171*e1f5be9aSotto bus_space_write_1(iot, ioh, 0, (reg >> 8) & 0xff); 172*e1f5be9aSotto bus_space_write_1(iot, ioh, 1, (reg >> 0) & 0xff); 173*e1f5be9aSotto return bus_space_read_1(iot, ioh, 2); 174*e1f5be9aSotto } 175*e1f5be9aSotto 176*e1f5be9aSotto u_int 177*e1f5be9aSotto ykbec_read16(struct ykbec_softc *mcsc, u_int reg) 178*e1f5be9aSotto { 179*e1f5be9aSotto u_int val; 180*e1f5be9aSotto 181*e1f5be9aSotto val = ykbec_read(mcsc, reg); 182*e1f5be9aSotto return (val << 8) | ykbec_read(mcsc, reg + 1); 183*e1f5be9aSotto } 184*e1f5be9aSotto 185*e1f5be9aSotto #define KB3310_FAN_SPEED_DIVIDER 480000 186*e1f5be9aSotto 187*e1f5be9aSotto #define ECTEMP_CURRENT_REG 0xf458 188*e1f5be9aSotto #define REG_FAN_SPEED_HIGH 0xfe22 189*e1f5be9aSotto #define REG_FAN_SPEED_LOW 0xfe23 190*e1f5be9aSotto 191*e1f5be9aSotto #define REG_DESIGN_CAP_HIGH 0xf77d 192*e1f5be9aSotto #define REG_DESIGN_CAP_LOW 0xf77e 193*e1f5be9aSotto #define REG_FULLCHG_CAP_HIGH 0xf780 194*e1f5be9aSotto #define REG_FULLCHG_CAP_LOW 0xf781 195*e1f5be9aSotto 196*e1f5be9aSotto #define REG_DESIGN_VOL_HIGH 0xf782 197*e1f5be9aSotto #define REG_DESIGN_VOL_LOW 0xf783 198*e1f5be9aSotto #define REG_CURRENT_HIGH 0xf784 199*e1f5be9aSotto #define REG_CURRENT_LOW 0xf785 200*e1f5be9aSotto #define REG_VOLTAGE_HIGH 0xf786 201*e1f5be9aSotto #define REG_VOLTAGE_LOW 0xf787 202*e1f5be9aSotto #define REG_TEMPERATURE_HIGH 0xf788 203*e1f5be9aSotto #define REG_TEMPERATURE_LOW 0xf789 204*e1f5be9aSotto #define REG_RELATIVE_CAT_HIGH 0xf492 205*e1f5be9aSotto #define REG_RELATIVE_CAT_LOW 0xf493 206*e1f5be9aSotto #define REG_BAT_VENDOR 0xf4c4 207*e1f5be9aSotto #define REG_BAT_CELL_COUNT 0xf4c6 208*e1f5be9aSotto #define REG_BAT_CHARGE 0xf4a2 209*e1f5be9aSotto #define REG_POWER_FLAG 0xf440 210*e1f5be9aSotto 211*e1f5be9aSotto #define REG_BAT_STATUS 0xf4b0 212*e1f5be9aSotto #define BAT_STATUS_BAT_EXISTS (1<<0) 213*e1f5be9aSotto #define BAT_STATUS_BAT_FULL (1<<1) 214*e1f5be9aSotto #define BAT_STATUS_BAT_DESTROY (1<<2) 215*e1f5be9aSotto #define BAT_STATUS_BAT_LOW (1<<5) 216*e1f5be9aSotto 217*e1f5be9aSotto #define REG_CHARGE_STATUS 0xf4b1 218*e1f5be9aSotto #define CHARGE_STATUS_PRECHARGE (1<<1) 219*e1f5be9aSotto #define CHARGE_STATUS_OVERHEAT (1<<2) 220*e1f5be9aSotto 221*e1f5be9aSotto #define REG_BAT_STATE 0xf482 222*e1f5be9aSotto #define BAT_STATE_DISCHARGING (1<<0) 223*e1f5be9aSotto #define BAT_STATE_CHARGING (1<<1) 224*e1f5be9aSotto 225*e1f5be9aSotto const char *REG_BAT_CHARGE_state[] = { 226*e1f5be9aSotto "AC-power", "Battery discharging", "Battery charging" 227*e1f5be9aSotto }; 228*e1f5be9aSotto 229*e1f5be9aSotto #define STATUS(a, i) ((i) >= nitems(a) ? "unknown" : (a)[i]) 230*e1f5be9aSotto 231*e1f5be9aSotto void 232*e1f5be9aSotto ykbec_refresh(void *arg) 233*e1f5be9aSotto { 234*e1f5be9aSotto struct ykbec_softc *sc = (struct ykbec_softc *)arg; 235*e1f5be9aSotto u_int val; 236*e1f5be9aSotto int current; 237*e1f5be9aSotto 238*e1f5be9aSotto val = ykbec_read16(sc, REG_FAN_SPEED_HIGH); 239*e1f5be9aSotto if (val != 0) 240*e1f5be9aSotto val = KB3310_FAN_SPEED_DIVIDER / val; 241*e1f5be9aSotto else 242*e1f5be9aSotto val = UINT_MAX; 243*e1f5be9aSotto sc->sc_sensor[0].value = val; 244*e1f5be9aSotto 245*e1f5be9aSotto val = ykbec_read(sc, ECTEMP_CURRENT_REG); 246*e1f5be9aSotto sc->sc_sensor[1].value = val * 1000000 + 273150000; 247*e1f5be9aSotto 248*e1f5be9aSotto sc->sc_sensor[2].value = ykbec_read16(sc, REG_DESIGN_CAP_HIGH) * 1000; 249*e1f5be9aSotto sc->sc_sensor[3].value = ykbec_read16(sc, REG_FULLCHG_CAP_HIGH) * 1000; 250*e1f5be9aSotto sc->sc_sensor[4].value = ykbec_read16(sc, REG_DESIGN_VOL_HIGH) * 1000; 251*e1f5be9aSotto current = ykbec_read16(sc, REG_CURRENT_HIGH); 252*e1f5be9aSotto if (current & 0x8000) 253*e1f5be9aSotto sc->sc_sensor[5].value = (0xffff - current) * 1000; 254*e1f5be9aSotto else 255*e1f5be9aSotto sc->sc_sensor[5].value = current * 1000; 256*e1f5be9aSotto sc->sc_sensor[6].value = ykbec_read16(sc, REG_VOLTAGE_HIGH) * 1000; 257*e1f5be9aSotto 258*e1f5be9aSotto val = ykbec_read16(sc, REG_TEMPERATURE_HIGH); 259*e1f5be9aSotto sc->sc_sensor[7].value = val * 1000000 + 273150000; 260*e1f5be9aSotto 261*e1f5be9aSotto sc->sc_sensor[8].value = ykbec_read16(sc, REG_RELATIVE_CAT_HIGH) * 1000; 262*e1f5be9aSotto 263*e1f5be9aSotto val = ykbec_read(sc, REG_BAT_CHARGE); 264*e1f5be9aSotto strlcpy(sc->sc_sensor[9].desc, STATUS(REG_BAT_CHARGE_state, val), 265*e1f5be9aSotto sizeof(sc->sc_sensor[9].desc)); 266*e1f5be9aSotto 267*e1f5be9aSotto val = (val << 8) | ykbec_read(sc, REG_BAT_STATUS); 268*e1f5be9aSotto if (!(val & BAT_STATUS_BAT_EXISTS)) 269*e1f5be9aSotto strlcat(sc->sc_sensor[9].desc, ",not available", 270*e1f5be9aSotto sizeof(sc->sc_sensor[9].desc)); 271*e1f5be9aSotto if (val & BAT_STATUS_BAT_FULL) 272*e1f5be9aSotto strlcat(sc->sc_sensor[9].desc, ",full", 273*e1f5be9aSotto sizeof(sc->sc_sensor[9].desc)); 274*e1f5be9aSotto if (val & BAT_STATUS_BAT_DESTROY) 275*e1f5be9aSotto strlcat(sc->sc_sensor[9].desc, ",bad", 276*e1f5be9aSotto sizeof(sc->sc_sensor[9].desc)); 277*e1f5be9aSotto if (val & BAT_STATUS_BAT_LOW) 278*e1f5be9aSotto strlcat(sc->sc_sensor[9].desc, ",low", 279*e1f5be9aSotto sizeof(sc->sc_sensor[9].desc)); 280*e1f5be9aSotto 281*e1f5be9aSotto val = (val << 8) | ykbec_read(sc, REG_CHARGE_STATUS); 282*e1f5be9aSotto if (val & CHARGE_STATUS_PRECHARGE) 283*e1f5be9aSotto strlcat(sc->sc_sensor[9].desc, ",precharge", 284*e1f5be9aSotto sizeof(sc->sc_sensor[9].desc)); 285*e1f5be9aSotto if (val & CHARGE_STATUS_OVERHEAT) 286*e1f5be9aSotto strlcat(sc->sc_sensor[9].desc, ",overheat", 287*e1f5be9aSotto sizeof(sc->sc_sensor[9].desc)); 288*e1f5be9aSotto #if 0 289*e1f5be9aSotto val = ykbec_read(sc, REG_BAT_STATE); 290*e1f5be9aSotto if (val & BAT_STATE_CHARGING) 291*e1f5be9aSotto strlcat(sc->sc_sensor[9].desc, ",charging", 292*e1f5be9aSotto sizeof(sc->sc_sensor[9].desc)); 293*e1f5be9aSotto if (val & BAT_STATE_DISCHARGING) 294*e1f5be9aSotto strlcat(sc->sc_sensor[9].desc, ",discharging", 295*e1f5be9aSotto sizeof(sc->sc_sensor[9].desc)); 296*e1f5be9aSotto #endif 297*e1f5be9aSotto sc->sc_sensor[9].value = val; 298*e1f5be9aSotto } 299