1 /* $OpenBSD: gl518sm.c,v 1.6 2007/06/24 05:34:35 dlg Exp $ */ 2 3 /* 4 * Copyright (c) 2006 Mark Kettenis 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/param.h> 20 #include <sys/systm.h> 21 #include <sys/device.h> 22 #include <sys/sensors.h> 23 24 #include <dev/i2c/i2cvar.h> 25 26 /* GL518SM registers */ 27 #define GL518SM_CHIPID 0x00 28 #define GL518SM_REVISION 0x01 29 #define GL518SM_VENDORID 0x02 30 #define GL518SM_CONFIG 0x03 31 #define GL518SM_CONFIG_START 0x40 32 #define GL518SM_CONFIG_CLEARST 0x20 33 #define GL518SM_CONFIG_NOFAN2 0x10 34 #define GL518SM_TEMP 0x04 35 #define GL518SM_TEMP_OVER 0x05 36 #define GL518SM_TEMP_HYST 0x06 37 #define GL518SM_FAN_COUNT 0x07 38 #define GL518SM_FAN_LIMIT 0x08 39 #define GL518SM_VIN1_LIMIT 0x09 40 #define GL518SM_VIN2_LIMIT 0x0a 41 #define GL518SM_VIN3_LIMIT 0x0b 42 #define GL518SM_VDD_LIMIT 0x0c 43 #define GL518SM_VOLTMETER 0x0d 44 #define GL518SM_MISC 0x0f 45 #define GL518SM_ALARM 0x10 46 #define GL518SM_MASK 0x11 47 #define GL518SM_INTSTAT 0x12 48 49 /* Sensors */ 50 #define GLENV_VIN3 0 51 #define GLENV_TEMP 1 52 #define GLENV_FAN1 2 53 #define GLENV_FAN2 3 54 #define GLENV_NUM_SENSORS 4 55 56 struct glenv_softc { 57 struct device sc_dev; 58 59 i2c_tag_t sc_tag; 60 i2c_addr_t sc_addr; 61 62 struct ksensor sc_sensor[GLENV_NUM_SENSORS]; 63 struct ksensordev sc_sensordev; 64 int sc_fan1_div, sc_fan2_div; 65 }; 66 67 int glenv_match(struct device *, void *, void *); 68 void glenv_attach(struct device *, struct device *, void *); 69 70 void glenv_refresh(void *); 71 72 struct cfattach glenv_ca = { 73 sizeof(struct glenv_softc), glenv_match, glenv_attach 74 }; 75 76 struct cfdriver glenv_cd = { 77 NULL, "glenv", DV_DULL 78 }; 79 80 int 81 glenv_match(struct device *parent, void *match, void *aux) 82 { 83 struct i2c_attach_args *ia = aux; 84 85 if (strcmp(ia->ia_name, "gl518sm") == 0) 86 return (1); 87 return (0); 88 } 89 90 void 91 glenv_attach(struct device *parent, struct device *self, void *aux) 92 { 93 struct glenv_softc *sc = (struct glenv_softc *)self; 94 struct i2c_attach_args *ia = aux; 95 u_int8_t cmd, data; 96 int i; 97 98 sc->sc_tag = ia->ia_tag; 99 sc->sc_addr = ia->ia_addr; 100 101 iic_acquire_bus(sc->sc_tag, 0); 102 103 cmd = GL518SM_REVISION; 104 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 105 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) { 106 iic_release_bus(sc->sc_tag, 0); 107 printf(": cannot read revision register\n"); 108 return; 109 } 110 111 printf(": GL518SM rev 0x%02x", data); 112 113 cmd = GL518SM_MISC; 114 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 115 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) { 116 iic_release_bus(sc->sc_tag, 0); 117 printf(", cannot read misc register\n"); 118 return; 119 } 120 sc->sc_fan1_div = 1 << ((data >> 6) & 0x03); 121 sc->sc_fan2_div = 1 << ((data >> 4) & 0x03); 122 123 cmd = GL518SM_CONFIG; 124 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 125 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) { 126 iic_release_bus(sc->sc_tag, 0); 127 printf(", cannot read configuration register\n"); 128 return; 129 } 130 if (data & GL518SM_CONFIG_NOFAN2) 131 sc->sc_fan2_div = 0; 132 133 /* Start monitoring and clear interrupt status. */ 134 data = (data | GL518SM_CONFIG_START | GL518SM_CONFIG_CLEARST); 135 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 136 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) { 137 iic_release_bus(sc->sc_tag, 0); 138 printf(", cannot write configuration register\n"); 139 return; 140 } 141 142 iic_release_bus(sc->sc_tag, 0); 143 144 /* Initialize sensor data. */ 145 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, 146 sizeof(sc->sc_sensordev.xname)); 147 148 sc->sc_sensor[GLENV_VIN3].type = SENSOR_VOLTS_DC; 149 150 sc->sc_sensor[GLENV_TEMP].type = SENSOR_TEMP; 151 152 sc->sc_sensor[GLENV_FAN1].type = SENSOR_FANRPM; 153 154 sc->sc_sensor[GLENV_FAN2].type = SENSOR_FANRPM; 155 if (sc->sc_fan2_div == -1) 156 sc->sc_sensor[GLENV_FAN2].flags |= SENSOR_FINVALID; 157 158 if (sensor_task_register(sc, glenv_refresh, 5) == NULL) { 159 printf(", unable to register update task\n"); 160 return; 161 } 162 163 for (i = 0; i < GLENV_NUM_SENSORS; i++) 164 sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]); 165 sensordev_install(&sc->sc_sensordev); 166 167 printf("\n"); 168 } 169 170 void 171 glenv_refresh(void *arg) 172 { 173 struct glenv_softc *sc = arg; 174 u_int8_t cmd, data, data2[2]; 175 u_int tmp; 176 177 iic_acquire_bus(sc->sc_tag, 0); 178 179 cmd = GL518SM_VOLTMETER; 180 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 181 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) { 182 sc->sc_sensor[GLENV_VIN3].flags |= SENSOR_FINVALID; 183 } else { 184 sc->sc_sensor[GLENV_VIN3].flags &= ~SENSOR_FINVALID; 185 sc->sc_sensor[GLENV_VIN3].value = data * 19000; 186 } 187 188 cmd = GL518SM_TEMP; 189 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 190 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) { 191 sc->sc_sensor[GLENV_TEMP].flags |= SENSOR_FINVALID; 192 } else { 193 sc->sc_sensor[GLENV_TEMP].flags &= ~SENSOR_FINVALID; 194 sc->sc_sensor[GLENV_TEMP].value = 195 (data - 119) * 1000000 + 273150000; 196 } 197 198 cmd = GL518SM_FAN_COUNT; 199 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 200 sc->sc_addr, &cmd, sizeof cmd, &data2, sizeof data2, 0)) { 201 sc->sc_sensor[GLENV_FAN1].flags |= SENSOR_FINVALID; 202 sc->sc_sensor[GLENV_FAN2].flags |= SENSOR_FINVALID; 203 } else { 204 sc->sc_sensor[GLENV_FAN1].flags &= ~SENSOR_FINVALID; 205 tmp = data2[0] * sc->sc_fan1_div * 2; 206 if (tmp == 0) 207 sc->sc_sensor[GLENV_FAN1].flags |= SENSOR_FINVALID; 208 else 209 sc->sc_sensor[GLENV_FAN1].value = 960000 / tmp; 210 211 sc->sc_sensor[GLENV_FAN2].flags &= ~SENSOR_FINVALID; 212 tmp = data2[1] * sc->sc_fan2_div * 2; 213 if (tmp == 0) 214 sc->sc_sensor[GLENV_FAN2].flags |= SENSOR_FINVALID; 215 else 216 sc->sc_sensor[GLENV_FAN2].value = 960000 / tmp; 217 } 218 219 iic_release_bus(sc->sc_tag, 0); 220 } 221