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