fintek.c revision 1.2
1/* $OpenBSD: fintek.c,v 1.2 2006/08/26 10:42:57 kettenis 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 36struct fintek_softc { 37 struct device sc_dev; 38 i2c_tag_t sc_tag; 39 i2c_addr_t sc_addr; 40 41 struct sensor sc_sensor[F_NUM_SENSORS]; 42}; 43 44int fintek_match(struct device *, void *, void *); 45void fintek_attach(struct device *, struct device *, void *); 46 47void fintek_refresh(void *); 48int fintek_read_reg(struct fintek_softc *sc, u_int8_t cmd, u_int8_t *data, 49 size_t size); 50int fintek_write_reg(struct fintek_softc *sc, u_int8_t cmd, u_int8_t *data, 51 size_t size); 52void fintek_fullspeed(struct fintek_softc *sc); 53 54struct cfattach fintek_ca = { 55 sizeof(struct fintek_softc), fintek_match, fintek_attach 56}; 57 58struct cfdriver fintek_cd = { 59 NULL, "fintek", DV_DULL 60}; 61 62#define FINTEK_CONFIG1 0x01 63#define FINTEK_FAN1_LINEAR_MODE 0x10 64#define FINTEK_FAN2_LINEAR_MODE 0x20 65#define FINTEK_VOLT0 0x10 66#define FINTEK_VOLT1 0x11 67#define FINTEK_VOLT2 0x12 68#define FINTEK_VOLT3 0x13 69#define FINTEK_TEMP1 0x14 70#define FINTEK_TEMP2 0x15 71#define FINTEK_FAN1 0x16 72#define FINTEK_FAN2 0x18 73#define FINTEK_VERSION 0x5c 74#define FINTEK_RSTCR 0x60 75#define FINTEK_FAN1_MODE_MANUAL 0x30 76#define FINTEK_FAN2_MODE_MANUAL 0xc0 77#define FINTEK_PWM_DUTY1 0x76 78#define FINTEK_PWM_DUTY2 0x86 79 80/* Options passed via the 'flags' config keyword. */ 81#define FINTEK_OPTION_FULLSPEED 0x0001 82 83int 84fintek_match(struct device *parent, void *match, void *aux) 85{ 86 struct i2c_attach_args *ia = aux; 87 88 if (strcmp(ia->ia_name, "f75375") == 0) 89 return (1); 90 return (0); 91} 92 93int 94fintek_read_reg(struct fintek_softc *sc, u_int8_t cmd, u_int8_t *data, 95 size_t size) 96{ 97 return iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 98 sc->sc_addr, &cmd, sizeof cmd, data, size, 0); 99} 100 101int 102fintek_write_reg(struct fintek_softc *sc, u_int8_t cmd, u_int8_t *data, 103 size_t size) 104{ 105 return iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 106 sc->sc_addr, &cmd, sizeof cmd, data, size, 0); 107} 108 109void 110fintek_attach(struct device *parent, struct device *self, void *aux) 111{ 112 struct fintek_softc *sc = (struct fintek_softc *)self; 113 struct i2c_attach_args *ia = aux; 114 u_int8_t cmd, data; 115 int i; 116 117 sc->sc_tag = ia->ia_tag; 118 sc->sc_addr = ia->ia_addr; 119 120 iic_acquire_bus(sc->sc_tag, 0); 121 122 cmd = FINTEK_VERSION; 123 if (fintek_read_reg(sc, cmd, &data, sizeof data)) 124 goto failread; 125 126 printf(": F75375 rev %d.%d", data>> 4, data & 0xf); 127 128 /* 129 * It seems the fan in the Thecus n2100 doesn't provide a 130 * reliable fan count. As a result the automatic fan 131 * controlling mode that the chip comes up in after reset 132 * doesn't work reliably. So we have a flag to drive the fan 133 * at maximum voltage such that the box doesn't overheat. 134 */ 135 if (sc->sc_dev.dv_cfdata->cf_flags & FINTEK_OPTION_FULLSPEED) 136 fintek_fullspeed(sc); 137 138 iic_release_bus(sc->sc_tag, 0); 139 140 for (i = 0; i < F_NUM_SENSORS; i++) 141 strlcpy(sc->sc_sensor[i].device, sc->sc_dev.dv_xname, 142 sizeof(sc->sc_sensor[i].device)); 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 strlcpy(sc->sc_sensor[F_V1].desc, "Volt1", 150 sizeof(sc->sc_sensor[F_V1].desc)); 151 152 sc->sc_sensor[F_V2].type = SENSOR_VOLTS_DC; 153 strlcpy(sc->sc_sensor[F_V2].desc, "Volt2", 154 sizeof(sc->sc_sensor[F_V2].desc)); 155 156 sc->sc_sensor[F_V3].type = SENSOR_VOLTS_DC; 157 strlcpy(sc->sc_sensor[F_V3].desc, "Volt3", 158 sizeof(sc->sc_sensor[F_V3].desc)); 159 160 sc->sc_sensor[F_TEMP1].type = SENSOR_TEMP; 161 strlcpy(sc->sc_sensor[F_TEMP1].desc, "Temp1", 162 sizeof(sc->sc_sensor[F_TEMP1].desc)); 163 164 sc->sc_sensor[F_TEMP2].type = SENSOR_TEMP; 165 strlcpy(sc->sc_sensor[F_TEMP2].desc, "Temp2", 166 sizeof(sc->sc_sensor[F_TEMP2].desc)); 167 168 sc->sc_sensor[F_FAN1].type = SENSOR_FANRPM; 169 strlcpy(sc->sc_sensor[F_FAN1].desc, "Fan1", 170 sizeof(sc->sc_sensor[F_FAN1].desc)); 171 172 sc->sc_sensor[F_FAN2].type = SENSOR_FANRPM; 173 strlcpy(sc->sc_sensor[F_FAN2].desc, "Fan2", 174 sizeof(sc->sc_sensor[F_FAN2].desc)); 175 176 if (sensor_task_register(sc, fintek_refresh, 5)) { 177 printf(", unable to register update task\n"); 178 return; 179 } 180 181 for (i = 0; i < F_NUM_SENSORS; i++) { 182 sc->sc_sensor[i].flags &= ~SENSOR_FINVALID; 183 sensor_add(&sc->sc_sensor[i]); 184 } 185 186 printf("\n"); 187 return; 188 189failread: 190 printf("unable to read reg %d\n", cmd); 191 iic_release_bus(sc->sc_tag, 0); 192 return; 193} 194 195 196struct { 197 char sensor; 198 u_int8_t cmd; 199} fintek_worklist[] = { 200 { F_VCC, FINTEK_VOLT0 }, 201 { F_V1, FINTEK_VOLT1 }, 202 { F_V2, FINTEK_VOLT2 }, 203 { F_V3, FINTEK_VOLT3 }, 204 { F_TEMP1, FINTEK_TEMP1 }, 205 { F_TEMP2, FINTEK_TEMP2 }, 206 { F_FAN1, FINTEK_FAN1 }, 207 { F_FAN2, FINTEK_FAN2 } 208}; 209#define FINTEK_WORKLIST_SZ (sizeof(fintek_worklist) / sizeof(fintek_worklist[0])) 210 211void 212fintek_refresh(void *arg) 213{ 214 struct fintek_softc *sc = arg; 215 u_int8_t cmd, data, data2; 216 int i; 217 218 iic_acquire_bus(sc->sc_tag, 0); 219 220 for (i = 0; i < FINTEK_WORKLIST_SZ; i++){ 221 cmd = fintek_worklist[i].cmd; 222 if (fintek_read_reg(sc, cmd, &data, sizeof data)) { 223 sc->sc_sensor[i].flags |= SENSOR_FINVALID; 224 continue; 225 } 226 sc->sc_sensor[i].flags &= ~SENSOR_FINVALID; 227 switch (fintek_worklist[i].sensor) { 228 case F_VCC: 229 sc->sc_sensor[i].value = data * 16000; 230 break; 231 case F_V1: 232 /* FALLTHROUGH */ 233 case F_V2: 234 /* FALLTHROUGH */ 235 case F_V3: 236 sc->sc_sensor[i].value = data * 8000; 237 break; 238 case F_TEMP1: 239 /* FALLTHROUGH */ 240 case F_TEMP2: 241 sc->sc_sensor[i].value = 273150000 + data * 1000000; 242 break; 243 case F_FAN1: 244 /* FALLTHROUGH */ 245 case F_FAN2: 246 /* FANx LSB follows FANx MSB */ 247 cmd = fintek_worklist[i].cmd + 1; 248 if (fintek_read_reg(sc, cmd, &data2, sizeof data2)) { 249 sc->sc_sensor[i].flags |= SENSOR_FINVALID; 250 continue; 251 } 252 if ((data == 0xff && data2 == 0xff) || 253 (data == 0 && data2 == 0)) 254 sc->sc_sensor[i].value = 0; 255 else 256 sc->sc_sensor[i].value = 1500000 / 257 (data << 8 | data2); 258 break; 259 default: 260 sc->sc_sensor[i].flags |= SENSOR_FINVALID; 261 break; 262 } 263 } 264 265 iic_release_bus(sc->sc_tag, 0); 266} 267 268void 269fintek_fullspeed(struct fintek_softc *sc) 270{ 271 u_int8_t data; 272 273 data = FINTEK_FAN1_LINEAR_MODE | FINTEK_FAN2_LINEAR_MODE; 274 fintek_write_reg(sc, FINTEK_CONFIG1, &data, sizeof data); 275 276 data = FINTEK_FAN1_MODE_MANUAL | FINTEK_FAN2_MODE_MANUAL; 277 fintek_write_reg(sc, FINTEK_RSTCR, &data, sizeof data); 278 279 data = 0xff; /* Maximum voltage */ 280 fintek_write_reg(sc, FINTEK_PWM_DUTY1, &data, sizeof data); 281 fintek_write_reg(sc, FINTEK_PWM_DUTY2, &data, sizeof data); 282} 283