1 /*
2  * Winbond and Asus hardware monitor chip
3  *
4  ***************************************************************
5  * Before calling these routines, one must call method->Open() *
6  * After calling these routines, one must call method->Close() *
7  ***************************************************************
8  *
9  * Winbond chip: W83781D, W83782D, W83783S, W83627HF, W83697HF
10  * Asus chip   : AS99127F, ASB100
11  * National Semiconductor: LM78, LM78-j, LM79 [+ [2*]LM75]
12  *
13  * Note: LM75 is temperature only sensor chip sometimes used
14  *       with LM78/79.
15  *       Winbond W83781D is LM78/79 + 2 * LM75 more or less.
16  *
17 
18 Winbond
19          Chip            Temp    Volt    Fan     SMBus   IOport
20         W83781D           3       7       3       yes     yes
21         W83782D           3       9       3       yes     yes
22         W83783S           1-2     5-6     3       yes     no
23         W83791D           3      10       5       yes     no
24         W83627HF          3       9       3       yes     yes (LPC)
25         W83627THF         3       7       3       yes     yes (LPC)
26         W83697HF          2       6       2       no      yes (LPC)
27 
28 Asus
29          Chip            Temp    Volt    Fan     SMBus   IOport
30         AS99127F          3       7       3       yes     no
31         ASB100(Bach)      3       7       3       yes     no
32         ASM58(Mozart-2)   2       4       2       yes     no
33 		(Mozart-2 needs a specific treatment)
34 
35 National Semiconductor
36          Chip            Temp    Volt    Fan     SMBus   IOport
37         lm78/lm78-j       1       7       3       yes     yes
38         lm79              1       7       3       yes     yes
39 
40 Analog Devices
41          Chip            Temp    Volt    Fan     SMBus   IOport
42         adm9240           1       6       2       yes     yes
43 
44  *
45  * by YRS
46  */
47 
48 /* To allow "unknown" (fuzzy probing), define this */
49 /*	#define ALLOW_UNKNOWN	*/
50 
51 #include <stdio.h>
52 #include <string.h>
53 #include <unistd.h>
54 #include "pci_pm.h"
55 #include "sensors.h"
56 #include "sens_winbond.h"
57 
58 /* external (global) data */
59 extern int pm_smb_detected;
60 extern int smb_slave;
61 extern int smb_wbtemp1, smb_wbtemp2;
62 extern int smb_wbtemp1_flag, smb_wbtemp2_flag;
63 extern LM_METHODS method_isa, method_smb;
64 extern int numSMBSlave, canSMBSlave[128];
65 extern int TyanTigerMP_flag;
66 
67 #define	LM75_ADDR_START		0x90	/* 0x90-0x9E (0x48-0x4F) */
68 #define	LM75_ADDR_END		0x9E
69 #define	WINBD_ADDR_START	0x50	/* 0x50-0x5E (0x28-0x2F) */
70 #define	WINBD_ADDR_END		0x5E
71 #define	ASUSM_ADDR_FIX		0xEE	/* ASUS Mozart-2, 0xEE (0x77) only */
72 
73 #define	WINBD_CONFIG	0x40
74 #define	WINBD_SMBADDR	0x48
75 #define	WINBD_DEVCID	0x49
76 #define	WINBD_CHIPID	0x58
77 #define	WINBD_VENDEX	0x4E
78 #define	WINBD_VENDID	0x4F
79 #define	ANADM_VENDID	0x3E
80 
81 /* temp nr=0,1,2; volt nr=0,...6; fan nr=0,1,2 */
82 #define	WINBD_TEMP0		0x27
83 #define	ASUSB_TEMP4		0x17
84 #define	ASUSM_TEMP2		0x13
85 #define	WINBD_TEMPADDR	0x4A
86 #define	WINBD_VOLT(nr)	(0x20 + (nr))
87 #define	WINBD_FAN(nr)	(0x28 + (nr))
88 #define	WINBD_FANDIV	0x47
89 #define	WINBD_REGPIN	0x4B
90 #define	ASUSM_FANDIV	0xA1
91 #define	ANADM_TEMPCFG	0x4B
92 
93 #define	WINBD_DIOSEL	0x59
94 #define	WINBD_VMCTRL	0x5D
95 
96 static	int		winbond_probe(LM_METHODS *);
97 static	int		winbond_probe_act(LM_METHODS *, int);
98 static	float	winbond_temp(LM_METHODS *, int);
99 static	int		winbond_fanrpm(LM_METHODS *, int);
100 static	float	winbond_volt(LM_METHODS *, int);
101 
102 #define BUFF_LEN 128
103 static char buff[BUFF_LEN];
104 
105 SENSOR winbond = {
106 	buff,
107 	winbond_probe,
108 	winbond_temp,
109 	winbond_volt,
110 	winbond_fanrpm
111 };
112 
113 /* chip idenfication */
114 static int wbdchipid = 0;
115 static int wbdlmid = 0;
116 
117 /* temp1/2 flags address */
118 static int temp1_flag = 0;	/* = 0 if enabled ! */
119 static int temp2_flag = 0;	/* = 0 if enabled ! */
120 static int temp1_addr = 0;
121 static int temp2_addr = 0;
122 
123 /* fan divisor register */
124 static int fan12div_reg = WINBD_FANDIV;
125 
126 #define WINBD_chkRegNum 8
127 
128 /* Register checked for probing */
129 static int chkReg[] = {
130 	0x40, 0x41, 0x42, 0x43,
131 	0x44, 0x45, 0x46, 0x47,
132 	0x48, 0x49, 0x4A, 0x4B,
133 	0x4C, 0x4D, 0x4E, 0x4F,
134 	0x56, 0x58, 0x59, 0x5D,
135 	0x3E, 0x13, 0x17, 0xA1,
136 	0x20, 0x22, 0x23, 0x24,
137 	0x27, 0x29, 0x2A, 0x2B,
138 	-1 };
139 
140 
Temp_Bipolar(LM_METHODS * method)141 static void Temp_Bipolar(LM_METHODS *method)
142 {
143 	int n;
144 
145 	n = method->Read(WINBD_DIOSEL) & 0x8F;
146 	method->Write(WINBD_DIOSEL, n);
147 	n = method->Read(WINBD_VMCTRL) | 0x0E;
148 	method->Write(WINBD_VMCTRL, n);
149 }
150 
Init_FanDiv(LM_METHODS * method)151 static void Init_FanDiv(LM_METHODS *method)
152 {
153 	int n;
154 
155 	n = (method->Read(WINBD_FANDIV) & 0x0F) | 0xA0;
156 	method->Write(WINBD_FANDIV, n);
157 	n = (method->Read(WINBD_REGPIN) & 0x3F) | 0x80;
158 	method->Write(WINBD_REGPIN, n);
159 }
160 
161 /*
162  *  return 0 if not probed
163  */
winbond_probe(LM_METHODS * method)164 static	int 	winbond_probe(LM_METHODS *method)
165 {
166 	int n, save, slave = 0;
167 
168 	if (method != &method_isa && method != &method_smb)
169 		return 0;
170 
171 	save = smb_slave;
172 
173 	if (method == &method_smb) {
174 		/* check ASUS Mozart Chip, first */
175 		if ((slave = get_smb_slave(ASUSM_ADDR_FIX, ASUSM_ADDR_FIX))) {
176 			if (winbond_probe_act(method, slave))
177 				goto ret1;
178 		}
179 		for (n = WINBD_ADDR_START; n <= WINBD_ADDR_END;) {
180 			if (!(slave = get_smb_slave(n, WINBD_ADDR_END)))
181 				goto ret0;
182 			else {
183 				if (winbond_probe_act(method, slave))
184 					goto ret1;
185 				else
186 					n = slave + 2;
187 			}
188 		}
189 		goto ret0;
190 	} else {
191 		if (winbond_probe_act(method, slave))
192 			goto ret1;
193 	}
194 
195 ret0:
196 	smb_slave = save;
197 	return 0;
198 ret1:
199  	/* this is TyanTigerMP specific treatment */
200 	if (TyanTigerMP_flag) {
201 		Temp_Bipolar(method);
202 		usleep(30000);
203 		Init_FanDiv(method);
204 	}
205 	if (method == &method_smb) {
206 		kill_smb_slave(slave);
207 		if(!smb_wbtemp1_flag)
208 			kill_smb_slave(smb_wbtemp1);
209 		if(!smb_wbtemp2_flag)
210 			kill_smb_slave(smb_wbtemp2);
211 	}
212 	return wbdchipid;
213 }
214 
winbond_probe_act(LM_METHODS * method,int slave)215 static	int 	winbond_probe_act(LM_METHODS *method, int slave)
216 {
217 	int i, n, nd, nc, nvl, nvu, nvx, nva;
218 
219 	if (method == &method_smb)
220 		smb_slave = slave;
221 	else
222 		slave = 0;
223 
224 	if (chkReg_Probe(slave, "Probing Winbond/Asus/LM78/79 chip:\n",
225 			chkReg, method) < WINBD_chkRegNum)
226 		goto ret0;
227 
228 	nd = method->Read(WINBD_DEVCID) & 0xFE;
229 	nc = method->Read(WINBD_CHIPID);
230 	nvx = method->Read(WINBD_VENDEX);
231 	method->Write(WINBD_VENDEX, 0x00);
232 	nvl = method->Read(WINBD_VENDID);
233 	method->Write(WINBD_VENDEX, 0x80);
234 	nvu = method->Read(WINBD_VENDID);
235 	nva = method->Read(ANADM_VENDID);
236 #ifdef DEBUG
237 printf("DEBUG 49:0x%02X 58:0x%02X 4Fl:0x%02X 4Fu:0x%02X\n",nd,nc,nvl,nvu);
238 #endif
239 
240 	if (nvl == 0xA3 && nvu == 0x5C) {	/* Winbond Chip */
241 	  switch (nc & 0xFE) {
242 		case 0x10:	/* 0x10 (or 0x11) */
243 			wbdchipid = W83781D;
244 			break;
245 		case 0x20:	/* 0x20 (or 0x21) 627HF */
246 		case 0x90:	/* 0x90 (or 0x91?) 627THF */
247 		case 0x1A:	/* 0x1A (??)  627THF-A */
248 		case 0xA0:	/* 0xA0 (or 0xA1) */
249 		case 0xC0:	/* 0xC0 (or 0xC1) 627DHG */
250 			wbdchipid = W83627HF;
251 			break;
252 		case 0x30:	/* 0x30 (or 0x31) */
253 			wbdchipid = W83782D;
254 			if (nc == 0x31)
255 				wbdchipid = AS99127F;	/* very special, but ... */
256 			break;
257 		case 0x40:	/* 0x40 (or 0x41) */
258 			wbdchipid = W83783S;
259 			break;
260 		case 0x60:	/* 0x60 (or 0x61) */
261 			wbdchipid = W83697HF;
262 			break;
263 		case 0x70:	/* 0x70 (or 0x71) */
264 			wbdchipid = W83791D;
265 			break;
266 		default:
267 #ifdef ALLOW_UNKNOWN
268 			wbdchipid = WBUNKNOWN;
269 #else
270 			goto ret0;
271 #endif
272 	  }
273 	} else if ((nvl == 0xC3 && nvu == 0x12) && nc == 0x31) {	/* Asus Chip */
274 			wbdchipid = AS99127F;
275 	} else if ((nvl == 0x94 && nvu == 0x06) && nc == 0x31) {	/* Asus Chip */
276 			wbdchipid = ASB100;
277 	} else if ( smb_slave == ASUSM_ADDR_FIX &&		/* Mozart-2, special */
278 				((nvx == 0x94 && nvl == 0x36 && nc == 0x56) ||
279 				 (nvx == 0x94 && nvl == 0x06 && nc == 0x56) ||
280 				 (nvx == 0x5C && nvl == 0xA3 && nc == 0x10))) {
281 			wbdchipid = ASM58;
282 	} else if (nd == 0x20 || nd == 0x40) { /* 0x20, 0x40 */
283 			wbdchipid = LM78;
284 	} else if (nd == 0xC0) { /* 0xC0 */
285 			wbdchipid = LM79;
286 	} else if (nva == 0x23) { /* ADM9240 */
287 			wbdchipid = ADM9240;
288 	} else
289 #ifdef ALLOW_UNKNOWN
290 			wbdchipid = UNKNOWN;
291 #else
292 			goto ret0;
293 #endif
294 
295 	strcpy(buff, winbchip[wbdchipid]);
296 
297 	wbdlmid = wbdchipid;
298 	if (wbdchipid == WBUNKNOWN || wbdchipid >= LM78)
299 		wbdlmid = W83781D;
300 
301 	if (wbdchipid == ASB100)	/* Asus Bach */
302 		wbdlmid = W83781D;
303 
304 	if (wbdchipid == ASM58) {	/* Asus Mozart-2 */
305 		wbdlmid = W83781D;
306 		temp1_flag = temp2_flag = 1;	/* disable! */
307 		fan12div_reg = ASUSM_FANDIV;
308 		method->Write(WINBD_CONFIG, 0x01);	/* init. chip */
309 		goto ret1;
310 	}
311 
312 	if (wbdchipid == ADM9240) {
313 		temp1_flag = temp2_flag = 1;	/* disable! */
314 		method->Write(WINBD_CONFIG, 0x01);	/* init. chip */
315 		goto ret1;
316 	}
317 
318 	if (method == &method_isa && wbdchipid >= LM78) {
319 		temp1_flag = temp2_flag = 1;	/* disable! */
320 		goto ret1;
321 	}
322 
323 /* Checking Extra temperatures Temp1, Temp2 */
324 
325 	if (wbdchipid >= LM78) { /* possibility of LM75 sensor */
326 		i = set_smb_Extemp(LM75_ADDR_START, LM75_ADDR_END,
327 				&smb_wbtemp2, &smb_wbtemp1);
328 		temp2_flag = i >> 1;
329 		temp1_flag = i & 0x01;
330 		info_Extemp(method, temp1_flag, temp2_flag);
331 		if (!temp1_flag || !temp2_flag)
332 			strcat(winbond.Name, "+LM75");
333 		goto ret1;
334 	}
335 
336 	n = method->Read(WINBD_TEMPADDR);
337 	if (!(temp1_flag = (n & 0x08) >> 3)) {
338 		temp1_addr = smb_wbtemp1;
339 		smb_wbtemp1 = 2 * ( 0x48 + (n & 0x07) );
340 		if (method->ReadTemp1() == 0xFFFF) {
341 			temp1_flag = 1;	/* disable! */
342 			smb_wbtemp1 = temp1_addr;
343 		}
344 	}
345 
346 	if (!(temp2_flag = (n & 0x80) >> 7)) {
347 		temp2_addr = smb_wbtemp2;
348 		smb_wbtemp2 = 2 * ( 0x48 + ((n & 0x70) >> 4) );
349 		if (method->ReadTemp2() == 0xFFFF) {
350 			temp2_flag = 1;	/* disable! */
351 			smb_wbtemp2 = temp2_addr;
352 		}
353 	}
354 	info_Extemp(method, temp1_flag, temp2_flag);
355 
356 ret1:
357 	if (method == &method_smb) {
358 		smb_wbtemp1_flag = temp1_flag;
359 		smb_wbtemp2_flag = temp2_flag;
360 	}
361 	return wbdchipid;
362 ret0:
363 	return 0;
364 }
365 
366 
367 /*
368  *	\retval	0xFFFF	no sensor
369  *	\retval	other	temperature
370  *  no = 0,1,2
371  */
winbond_temp(LM_METHODS * method,int no)372 static	float	winbond_temp(LM_METHODS *method, int no)
373 {
374 	int n = 0;
375 	float f;
376 
377 	if (no < 0 || 2 < no)
378 		return 0xFFFF;
379 	if (no == 2 &&
380 		(wbdchipid == W83783S || wbdchipid == W83697HF || wbdchipid == ASM58))
381 		return 0xFFFF;
382 
383 	if (no == 0) {
384 		f = (float) method->Read(WINBD_TEMP0);
385 		if (wbdchipid == ADM9240) {
386 			n = method->Read(ANADM_TEMPCFG);
387 			if (n & 0x80)
388 				f += 0.5;
389 		}
390 		return f;
391 	} else if (no == 1) {
392 		if (wbdchipid == ASB100)
393 			return (float) method->Read(ASUSB_TEMP4);
394 		if (wbdchipid == ASM58)
395 			return (float) method->Read(ASUSM_TEMP2);
396 		else if (!temp1_flag)
397 			n = method->ReadTemp1();
398 #ifdef SYRS
399 	} else if (no == 2 && !temp2_flag) {
400 #else
401 	} else if (no == 2) {
402 		if (wbdchipid == ASB100) {
403 		  if (!temp1_flag)
404 			n = method->ReadTemp1();
405 		} else if (!temp2_flag)
406 #endif
407 			n = method->ReadTemp2();
408 	}
409 
410 	if ((n & 0xFF) >= 0x80)
411 		n = 0;
412 
413 	f = (float) (n & 0xFF) + 0.5 * ((n & 0xFF00) >> 15);
414 
415 	if (wbdchipid == AS99127F && pm_smb_detected == ICH801SMB) {
416 	/* very special treatment for AS99127F with ICH chipsets */
417 		if (no == 1 && (-32.0 < f && f <= 105.0)) {
418 			f *= 0.697;
419 			f += 25.0;
420 		}
421 	}
422 	return f;
423 }
424 
425 
426 /*
427  *	\retval	0x0000FFFF	no sensor
428  *  no = 0,1,2,...,6
429  */
winbond_volt(LM_METHODS * method,int no)430 static	float	winbond_volt(LM_METHODS *method, int no)
431 {
432 	int n;
433 	float f = 0.0;
434 
435 	if (no < 0 || 6 < no)
436 		return 0xFFFF;
437 	if (wbdchipid == ASM58 && (no == 1 || no > 4))
438 		return 0xFFFF;
439 	if (wbdchipid == ADM9240 && (no > 5))
440 		return 0xFFFF;
441 
442 	n = method->Read(WINBD_VOLT(no));
443 	switch (no) {
444 		case 0:
445 		case 1:
446 		case 2:
447 			f = n * 0.016;
448 			break;
449 		case 3:
450 			f = n * 0.016 * 1.68;
451 			break;
452 		case 4:
453 			f = n * 0.016 * 3.800;
454 			break;
455 		case 5:
456 			if (wbdlmid == AS99127F)
457 				f = - n * 0.016 * 3.968;
458 			else if (wbdlmid == W83781D)
459 				f = - n * 0.016 * 3.477;
460 			else
461 				f = ( n * 0.016  - 3.6 * 0.8056) / 0.1944;
462 			break;
463 		case 6:
464 			if (wbdlmid == W83781D || wbdlmid == AS99127F)
465 				f = - n * 0.016 * 1.500;
466 			else
467 				f = ( n * 0.016  - 3.6 * 0.6818) / 0.3182;
468 	}
469 
470 	return f;
471 }
472 
473 
474 /*
475 	Controlling Fan Divisor for 1st/2nd fans: CR = 0x47.
476 	1st two bits for fan1, 2nd two bits for fan2
477 
478          7     4 3     0
479         +-+-+-+-+-+-+-+-+     xx = 00,01,10,11  div1fac = 1,2,4,8
480         |y y|x x| VolID |     yy = 00,01,10,11  div2fac = 1,2,4,8
481         +-+-+-+-+-+-+-+-+    initial values: xx=01, yy=01
482 
483 	Controlling Fan Divisor for 3rd fan: CR = 0x4B.
484 	1st two bits for fan3
485 
486          7 6 5         0
487         +-+-+-+-+-+-+-+-+
488         |z z|           |     zz = 00,01,10,11  div3fac = 1,2,4,8
489         +-+-+-+-+-+-+-+-+    initial values: zz=01
490 
491 	3rd fan divisor available for Winbond (not for LM78/79).
492 
493     Bit 2 of Fan Divisor: CR = 0x5D (Winbond chips except 781D).
494 
495          7 6 5         0
496         +-+-+-+-+-+-+-+-+
497         |z|y|x|         |     x, y, z for bit 2 of fan 1, 2, 3
498         +-+-+-+-+-+-+-+-+
499  */
500 
501 /*
502  *	\retval	0xFFFF no sensor
503  *  no = 0,1,2
504  *
505  *  Clock is 22.5kHz (22,500 x 60 = 1350000 counts/minute)
506  */
winbond_fanrpm(LM_METHODS * method,int no)507 static	int		winbond_fanrpm(LM_METHODS *method, int no)
508 {
509 	int r, n1 = 0x50, n2 = 0x40, n3 = 0x00;
510 	static int div[3] = {1,1,1};
511 
512 	if (no < 0 || 2 < no)
513 		return 0xFFFF;
514 	if (no == 2
515 		&& (wbdchipid == W83697HF || wbdchipid == ASM58
516 			|| wbdchipid == ADM9240))
517 		return 0xFFFF;
518 
519 	if (W83782D <= wbdchipid && wbdchipid <= W83697HF)
520 		n3 = method->Read(WINBD_VMCTRL);	/* bit 2 */
521 	if (no != 2) {
522 		n1 = method->Read(fan12div_reg);	/* bit 0,1 */
523 		div[0] = ((n1 >> 4) & 0x03) | ((n3 & 0x20) >> 3);
524 		div[1] =  (n1 >> 6) | ((n3 & 0x40) >> 4);
525 	} else if (wbdchipid < LM78) {
526 		n2 = method->Read(WINBD_REGPIN);	/* bit 0,1 */
527 		div[2] =  (n2 >> 6) | ((n3 & 0x80) >> 5);
528 	}
529 
530 	r = method->Read(WINBD_FAN(no));
531 	if (r == 0xFF) {
532 		/* change divisor for the sake of next call ! */
533 		if (no != 2) {
534 			if (div[no] < 3)
535 				++(div[no]);
536 			else
537 				div[no] = 0;
538 			r = (n1 & 0x0F) | ((div[0] & 0x03) << 4) | ((div[1] & 0x03) << 6);
539 			method->Write(fan12div_reg, r);
540 		} else if (wbdchipid < LM78) {
541 			if (div[no] < 3)
542 				++(div[no]);
543 			else
544 				div[no] = 0;
545 			r = (n2 & 0x3F) | ((div[2] & 0x03) << 6);
546 			method->Write(WINBD_REGPIN, r);
547 		}
548 		if (W83782D <= wbdchipid && wbdchipid <= W83697HF) {
549 			r = (n3 & 0x1F) | ((div[0] & 0x04) << 3) |
550 				((div[1] & 0x04) << 4) | ((div[2] & 0x04) << 5);
551 			method->Write(WINBD_VMCTRL, r);
552 		}
553 		return 0xFFFF;
554 	} else if (r == 0) {
555 		return 0xFFFF;
556 	}
557 
558 	return 1350000 / (r * (1 << div[no]));
559 }
560