1 /* $OpenBSD: fintek.c,v 1.7 2007/10/31 20:46:17 cnst Exp $ */ 2 /* 3 * Copyright (c) 2006 Dale Rahn <drahn@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/systm.h> 20 #include <sys/device.h> 21 #include <sys/sensors.h> 22 23 #include <dev/i2c/i2cvar.h> 24 25 /* Sensors */ 26 #define F_VCC 0 27 #define F_V1 1 28 #define F_V2 2 29 #define F_V3 3 30 #define F_TEMP1 4 31 #define F_TEMP2 5 32 #define F_FAN1 6 33 #define F_FAN2 7 34 #define F_NUM_SENSORS 8 35 36 struct fintek_softc { 37 struct device sc_dev; 38 i2c_tag_t sc_tag; 39 i2c_addr_t sc_addr; 40 41 struct ksensor sc_sensor[F_NUM_SENSORS]; 42 struct ksensordev sc_sensordev; 43 }; 44 45 int fintek_match(struct device *, void *, void *); 46 void fintek_attach(struct device *, struct device *, void *); 47 48 void fintek_refresh(void *); 49 int fintek_read_reg(struct fintek_softc *sc, u_int8_t cmd, u_int8_t *data, 50 size_t size); 51 int fintek_write_reg(struct fintek_softc *sc, u_int8_t cmd, u_int8_t *data, 52 size_t size); 53 void fintek_fullspeed(struct fintek_softc *sc); 54 55 struct cfattach fintek_ca = { 56 sizeof(struct fintek_softc), fintek_match, fintek_attach 57 }; 58 59 struct cfdriver fintek_cd = { 60 NULL, "fintek", DV_DULL 61 }; 62 63 #define FINTEK_CONFIG1 0x01 64 #define FINTEK_FAN1_LINEAR_MODE 0x10 65 #define FINTEK_FAN2_LINEAR_MODE 0x20 66 #define FINTEK_VOLT0 0x10 67 #define FINTEK_VOLT1 0x11 68 #define FINTEK_VOLT2 0x12 69 #define FINTEK_VOLT3 0x13 70 #define FINTEK_TEMP1 0x14 71 #define FINTEK_TEMP2 0x15 72 #define FINTEK_FAN1 0x16 73 #define FINTEK_FAN2 0x18 74 #define FINTEK_VERSION 0x5c 75 #define FINTEK_RSTCR 0x60 76 #define FINTEK_FAN1_MODE_MANUAL 0x30 77 #define FINTEK_FAN2_MODE_MANUAL 0xc0 78 #define FINTEK_PWM_DUTY1 0x76 79 #define FINTEK_PWM_DUTY2 0x86 80 81 /* Options passed via the 'flags' config keyword. */ 82 #define FINTEK_OPTION_FULLSPEED 0x0001 83 84 int 85 fintek_match(struct device *parent, void *match, void *aux) 86 { 87 struct i2c_attach_args *ia = aux; 88 89 if (strcmp(ia->ia_name, "f75375") == 0) 90 return (1); 91 return (0); 92 } 93 94 int 95 fintek_read_reg(struct fintek_softc *sc, u_int8_t cmd, u_int8_t *data, 96 size_t size) 97 { 98 return iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 99 sc->sc_addr, &cmd, sizeof cmd, data, size, 0); 100 } 101 102 int 103 fintek_write_reg(struct fintek_softc *sc, u_int8_t cmd, u_int8_t *data, 104 size_t size) 105 { 106 return iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 107 sc->sc_addr, &cmd, sizeof cmd, data, size, 0); 108 } 109 110 void 111 fintek_attach(struct device *parent, struct device *self, void *aux) 112 { 113 struct fintek_softc *sc = (struct fintek_softc *)self; 114 struct i2c_attach_args *ia = aux; 115 u_int8_t cmd, data; 116 int i; 117 118 sc->sc_tag = ia->ia_tag; 119 sc->sc_addr = ia->ia_addr; 120 121 iic_acquire_bus(sc->sc_tag, 0); 122 123 cmd = FINTEK_VERSION; 124 if (fintek_read_reg(sc, cmd, &data, sizeof data)) 125 goto failread; 126 127 printf(": F75375 rev %d.%d", data>> 4, data & 0xf); 128 129 /* 130 * It seems the fan in the Thecus n2100 doesn't provide a 131 * reliable fan count. As a result the automatic fan 132 * controlling mode that the chip comes up in after reset 133 * doesn't work reliably. So we have a flag to drive the fan 134 * at maximum voltage such that the box doesn't overheat. 135 */ 136 if (sc->sc_dev.dv_cfdata->cf_flags & FINTEK_OPTION_FULLSPEED) 137 fintek_fullspeed(sc); 138 139 iic_release_bus(sc->sc_tag, 0); 140 141 strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, 142 sizeof(sc->sc_sensordev.xname)); 143 144 sc->sc_sensor[F_VCC].type = SENSOR_VOLTS_DC; 145 strlcpy(sc->sc_sensor[F_VCC].desc, "Vcc", 146 sizeof(sc->sc_sensor[F_VCC].desc)); 147 148 sc->sc_sensor[F_V1].type = SENSOR_VOLTS_DC; 149 sc->sc_sensor[F_V2].type = SENSOR_VOLTS_DC; 150 sc->sc_sensor[F_V3].type = SENSOR_VOLTS_DC; 151 152 sc->sc_sensor[F_TEMP1].type = SENSOR_TEMP; 153 sc->sc_sensor[F_TEMP2].type = SENSOR_TEMP; 154 155 sc->sc_sensor[F_FAN1].type = SENSOR_FANRPM; 156 sc->sc_sensor[F_FAN2].type = SENSOR_FANRPM; 157 158 if (sensor_task_register(sc, fintek_refresh, 5) == NULL) { 159 printf(", unable to register update task\n"); 160 return; 161 } 162 163 for (i = 0; i < F_NUM_SENSORS; i++) 164 sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i]); 165 sensordev_install(&sc->sc_sensordev); 166 167 printf("\n"); 168 return; 169 170 failread: 171 printf("unable to read reg %d\n", cmd); 172 iic_release_bus(sc->sc_tag, 0); 173 return; 174 } 175 176 177 struct { 178 char sensor; 179 u_int8_t cmd; 180 } fintek_worklist[] = { 181 { F_VCC, FINTEK_VOLT0 }, 182 { F_V1, FINTEK_VOLT1 }, 183 { F_V2, FINTEK_VOLT2 }, 184 { F_V3, FINTEK_VOLT3 }, 185 { F_TEMP1, FINTEK_TEMP1 }, 186 { F_TEMP2, FINTEK_TEMP2 }, 187 { F_FAN1, FINTEK_FAN1 }, 188 { F_FAN2, FINTEK_FAN2 } 189 }; 190 #define FINTEK_WORKLIST_SZ (sizeof(fintek_worklist) / sizeof(fintek_worklist[0])) 191 192 void 193 fintek_refresh(void *arg) 194 { 195 struct fintek_softc *sc = arg; 196 u_int8_t cmd, data, data2; 197 int i; 198 199 iic_acquire_bus(sc->sc_tag, 0); 200 201 for (i = 0; i < FINTEK_WORKLIST_SZ; i++){ 202 cmd = fintek_worklist[i].cmd; 203 if (fintek_read_reg(sc, cmd, &data, sizeof data)) { 204 sc->sc_sensor[i].flags |= SENSOR_FINVALID; 205 continue; 206 } 207 sc->sc_sensor[i].flags &= ~SENSOR_FINVALID; 208 switch (fintek_worklist[i].sensor) { 209 case F_VCC: 210 sc->sc_sensor[i].value = data * 16000; 211 break; 212 case F_V1: 213 /* FALLTHROUGH */ 214 case F_V2: 215 /* FALLTHROUGH */ 216 case F_V3: 217 sc->sc_sensor[i].value = data * 8000; 218 break; 219 case F_TEMP1: 220 /* FALLTHROUGH */ 221 case F_TEMP2: 222 sc->sc_sensor[i].value = 273150000 + data * 1000000; 223 break; 224 case F_FAN1: 225 /* FALLTHROUGH */ 226 case F_FAN2: 227 /* FANx LSB follows FANx MSB */ 228 cmd = fintek_worklist[i].cmd + 1; 229 if (fintek_read_reg(sc, cmd, &data2, sizeof data2)) { 230 sc->sc_sensor[i].flags |= SENSOR_FINVALID; 231 continue; 232 } 233 if ((data == 0xff && data2 == 0xff) || 234 (data == 0 && data2 == 0)) 235 sc->sc_sensor[i].value = 0; 236 else 237 sc->sc_sensor[i].value = 1500000 / 238 (data << 8 | data2); 239 break; 240 default: 241 sc->sc_sensor[i].flags |= SENSOR_FINVALID; 242 break; 243 } 244 } 245 246 iic_release_bus(sc->sc_tag, 0); 247 } 248 249 void 250 fintek_fullspeed(struct fintek_softc *sc) 251 { 252 u_int8_t data; 253 254 data = FINTEK_FAN1_LINEAR_MODE | FINTEK_FAN2_LINEAR_MODE; 255 fintek_write_reg(sc, FINTEK_CONFIG1, &data, sizeof data); 256 257 data = FINTEK_FAN1_MODE_MANUAL | FINTEK_FAN2_MODE_MANUAL; 258 fintek_write_reg(sc, FINTEK_RSTCR, &data, sizeof data); 259 260 data = 0xff; /* Maximum voltage */ 261 fintek_write_reg(sc, FINTEK_PWM_DUTY1, &data, sizeof data); 262 fintek_write_reg(sc, FINTEK_PWM_DUTY2, &data, sizeof data); 263 } 264