/**
* $Id: common.c 757 2012-07-01 06:02:26Z elkner $
*
* AMD K8+ sensor processing. Called k10sensor, since K10 is the first
* microarch, which provides reliable sensor data (with some exceptions - see
* erratum #319).
*
* Work is solely based on the information found in the
* AMD BIOS and Kernel Developers Guides (BKDG), Revision Guides, Power and
* Thermal Data Sheets (PTDS) and Functional Data Sheets (FDS). See:
*
*
* * CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each file.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* All Rights reserved.
* CDDL HEADER END
*/
/*
* Copyright (c) 2011,2012 by Jens Elkner,
* Otto-von-Guericke Universitaet Magdeburg. All rights reserved.
*/
#ifndef _KERNEL
#include <strings.h>
#endif
#include "k10sensor.h"
/* Function 03 stuff */
/** Set the category index (v->catIdx) wrt. cat_range_map.
* @param v cpu vars: requires familyModel
*/
void
uint32_t i;
for (i = 0; i < max; i++) {
v->catIdx = i;
break;
}
}
}
}
/*
* Set the silicon version string (v->revision).
*
* @param v cpu vars: requires xfamilyModel, modelStepping
*/
void
if (v->xfamilyModel < 4) {
/* Athlon64 + Opteron RevGuide - Last Update: 07/20/2009 3.79
* @@
* BH-E4: 02-B1 939
* CH-CG: 00-82,00-B2 754,939
* CH-D0: 01-80,01-B0 754,939
* DH-CG: 00-C0,00-E0,00-F0 754,754,939
* DH-D0: 01-C0,01-F0 754,939
* DH-E3: 02-C0,02-F0 754,939
* DH-E6: 02-C2,02-F2 754,939
* JH-E1: 02-10 940
* JH-E6: 02-12,02-32 940,939
* SH-B0: 00-40,00-50 754,940
* SH-B3: 00-51 940
* SH-C0: 00-48,00-58 754,940
* SH-CG: 00-4A,00-5A,00-7A 754,940,939
* SH-D0: 01-40,01-50,01-70 754,940,939
* SH-E4: 02-51,02-71 940,939
* SH-E5: 02-42 754
* ?H-E4: 03-72 ?
* ?H-E6: 03-F2 ?
* Sockets: 940pin, 939pin, 754pin
*/
if (v->xfamilyModel == 0x03) {
} else if (v->modelStepping < 0x40) {
if (stepping < 0x2) {
}
} else if (v->modelStepping < 0x80) {
if (v->xfamilyModel == 0x02) {
} else if (v->xfamilyModel == 0x01) {
} else if (stepping >= 0x8) {
} else if (stepping >= 0x1) {
}
} else if (v->modelStepping == 0xB1) {
} else if (v->modelStepping < 0xC0) {
} else /* DH-* */ if (v->xfamilyModel == 0) {
} else if (v->xfamilyModel == 1) {
} else {
}
} else if (v->xfamilyModel < 0x10) {
/* 0Fh RevGuide - Last Update: 09/22/2009 3.46
* @@
* BH-F2: 04-82,04-B2 S1g1,AM2dc
* BH-G1: 06-81,06-B1 S1g1,AM2dc
* BH-G2: 06-82,06-B2 S1g1,AM2dc|ASB1
* JH-F2: 04-12,04-32 F,AM2dc
* JH-F3: 04-13,04-33,05-D3,0C-13 F,AM2dc,F,Fr3
* DH-F2: 04-C2,04-F2,05-F2 S1g1,AM2sc,AM2sc
* DH-F3: 05-F3 AM2sc
* DH-G1: 07-F1 AM2sc
* DH-G2: 06-C2,06-F2,07-C2,07-F2 S1g1,AM2sc|ASB1,S1g1,AM2sc|ASB1
* Sockets: F, Fr3, AM2 (dc=DualCore|sc=SingleCore), ASB1, S1g1 (638pin)
*/
if (v->xfamilyModel != 0x0C) {
if (v->xfamilyModel > 0x05) {
}
/* now the remaining first letter */
switch (v->modelStepping & 0xf0) {
case 0xB0:
case 0x80:
v->revision[0] = 'B'; break;
case 0xC0:
case 0xF0:
v->revision[0] = 'D'; break;
}
}
} else if (v->xfamilyModel == 0x10) {
/* 10h RevGuide - Last Update: 3/21/2012 3.92
* @@ Rev. cnames
* DR-B1: 10-21 Fr2|AM2r2 :B
* DR-B2: 10-22 Fr2|AM2r2
* DR-B3: 10-23 Fr2|AM2r2
* DR-BA: 10-2A Fr2
* RB-C2: 10-42 Fr2|Fr5|AM2r2|AM3 :C
* BL-C2: 10-52 AM3
* DA-C2: 10-62 AM3|S1g3 :DA-C
* RB-C3: 10-43 AM3 :C3
* BL-C3: 10-53 AM3|S1g4
* DA-C3: 10-63 AM3|S1g4|ASB2
* HY-D0: 10-80 Fr6|C32r1 :D
* HY-D1: 10-81,10-91 C32r1,G34r1 :D1
* PH-E0: 10-A0 AM3 :E
* Sockets: Fr2, Fr5, Fr6, AM2r2, AM3, G34r1, C32r1, ASB2, S1g3, S1g4
*/
switch(v->modelStepping & 0xf0) {
case 0x80:
}
} else if (v->xfamilyModel == 0x20) {
/* 11h RevGuide - Last Update: 12/16/2011 3.10
* @@
* LG-B1: 20-31 S1g2
* Sockets: S1g2
*/
} else if (v->xfamilyModel == 0x30) {
/* 12h RevGuide - Last Update: 03/21/2012 3.10
* @@
* LN-B0: 30-10 FS1|FM1
* Sockets: FS1, FM1
*/
} else if (v->xfamilyModel == 0x50) {
/* 14h RevGuide - Last Update: 03/29/2012 3.12
* @@
* ON-B0: 50-10 FT1
* ON-C0: 50-20 FT1
* Sockets: FT1
*/
if (v->modelStepping == 0x20) {
}
} else if (v->xfamilyModel == 0x60) {
/* 15h RevGuide - Last Update: 11/14/2011 3.04
* @@
* OR-B2: 60-12 AM3r2|C32r1|G34r1
* Sockets: AM3r2, C32r1, G34r1
*/
} else {
}
}
/**
* Check, whether an AMD erratum applies, which indicates, that the obtained
* temperature values are inaccurate and thus should not be used for overheat
* detection. If none applies, v->erratum gets set to 0, to the related erratum
* number otherwise.
*
* Erratum 141 applies to all revisions of 0x0Fh.
*
* Erratum 319 applies to some revisions of 0x10h.
* For now (Feb-2011) these are: DR-BA, DR-B2, DR-B3, RB-C2, HY-D0
* and these are not: BL-C2, DA-C2, RB-C3, BL-C3, DA-C3, HY-D1, PH-E0
*
* @param v cpu vars: requires xfamilyModel, modelStepping, ebx
*/
void
v->erratum = 0;
/* 0Fh */
v->erratum = 141;
}
if (v->xfamilyModel != 0x10) {
return;
}
/* AMD suggestion to find affected revisions:
* (((CPUID Fn8000_0001_EBX[PkgType, bits 31:28] == 1 (AM2r2 or AM3))
* && (F2x[1, 0]94[Ddr3Mode, bit 8] == 0))
* || (CPUID Fn8000_0001_EBX[31:28] == 0 (Fr{2|5|6}(1207))))
*/
if (pkgType == 0) { /* AMD_SOCKET_F */
v->erratum = 319;
/* remaining: DR-B2, DR-B3 (AM2r2); RB-C2 (AM2r2, AM3)
* AMD suggests to deduce: DDR2 usage == affected, however AM3 can use
* DDR2 as well, and even if DDR3 is in use, it would not catch RB-C2.
* Just in case we change our mind, we need to check:
* Func2, AMD_DRAM_CONFIG_HIGH_REG 0x94, AMD_DDR3_MODE_MASK (1 << 8)
*/
v->erratum = 319;
}
{
v->erratum = 319;
}
}
/**
* Fill in thermtrip register relevant data. Depending on the architecture
* this includes v->{t_case_max, diode_offset, flags[HTC_THERMTRIP]}.
*
* @param pci_hdl handle to the PCI device to query.
* @param v cpu vars: requires xfamilyModel, modelStepping, revision, ebx
*/
void
v->t_case_max = INT32_MAX;
v->diode_offset = 0;
v->flags &= ~HTC_THERMTRIP;
/* RO: Thermtrip enabled ? */
v->flags |= HTC_THERMTRIP;
}
/* T case max. The only place, where we really need it, is to determine
* whether we need to correct the diode offset for JH-E6 chips below */
if (v->xfamilyModel == 0x20) {
/* 11h: TCaseDie */
} else if (v->xfamilyModel < 0x04) {
{
} else {
/* in most cases it is right, but not always - use with care */
}
} else {
}
}
/* RO: Diode offset */
if (v->xfamilyModel > 0x20) {
/* > 11h */
offset = 0;
} else if (v->xfamilyModel >= 0x10) {
/* 10h, 11h {-52..10C} */
offset = 0;
} else {
}
} else if (v->xfamilyModel >= 0x04) {
/* 0Fh: BDGK says, offset needs to be added - so we change the sign */
if (offset == 0) {
/* S1g1 aka S1 == lidless {0..32C} */
{ /* ABS1 == lidless {0..32C} */
} else {
/* lidded {-52..10C} */
if (offset != 0) {
}
}
} else {
/* 940pin FDS Note6: for Rev < 'CG' _accuraccy_ (after applying
* possible ideality factor) is +- 10 degrees C. */
/* obey sign bit */
offset *= -1000;
} else {
offset *= 1000;
}
/* DC Opteron 150,165,170,175,180: erratum 154 (JH-E6) workaround */
offset += 10000;
}
}
v->diode_offset = offset;
}
/**
* Check Northbridge register and populate
* v->flags[HTC_CAPABLE,LHTC_CAPABLE,DC_0Fh].
*
* @param pci_hdl handle to the PCI device to query.
* @param v cpu vars: requires xfamilyModel
*/
void
{
v->flags &= ~HTC_CAPABLE;
v->flags &= ~LHTC_CAPABLE;
/* 0Fh, RO: DualCore Capability */
{
}
/* 0Fh+, RO: HTC capable */
v->flags |= HTC_CAPABLE;
}
/* 12h, 14h, RO: LHTC capable */
{
v->flags |= LHTC_CAPABLE;
}
}
/**
* Check the HTC register and populate v->limits[HTC].
*
* @param pci_hdl handle to the PCI device to query.
* @param v cpu vars: requires xfamilyModel, modelStepping, revision, ebx,
* flags[HTC_CAPABLE]
*/
void
if (v->xfamilyModel < 0x04) {
versions Tcrit is specified in the PTDS. */
switch (v->revision[4]) {
case 'G':
/* AthlonFX53 = 70 and AthlonFX55 = 63 */
{
} else {
}
break;
case '0':
}
break;
}
/* but not specified for Turion64 */
}
return;
}
/* 0Fh+ */
if ((v->flags & HTC_CAPABLE) == 0) {
/* not allowed to access HW_THERMAL_CTRL_REG if uncapable */
return;
}
/* RW: HTC enabled */
}
if (v->xfamilyModel < 0x10) {
/* 0Fh has no further HTC info */
return;
}
/* 10h+, RW: slew-controlled temperature */
}
/* RW: HTC temperature limit */
/* RW: HTC hysteresis */
}
/**
* Check the STC or LHTC register and populate v->limits[STC].
*
* @param pci_hdl handle to the PCI device to query.
* @param v cpu vars: requires xfamilyModel, flags[HTC_CAPABLE,LHTC_CAPABLE]
*/
void
{
* for 0Fh, 10h it must be HTC_CAPABLE,
* for 12h, 14h it must be LHTC_CAPABLE */
{
return;
}
return; )
if (v->xfamilyModel < 0x10) {
/* 0Fh, RW: STC Temperature Limit */
if (val != 0) {
/* RW: STC Hysteresis Limit */
}
} else if (v->xfamilyModel == 0x10) {
/* 10h, RW: STC temperature limit */
if (val != 0) {
/* RW: STC hysteresis */
}
/* RW: STC slew-controlled temperature */
}
} else {
/* 12h, 14h, RW: local HTC temperature limit */
/* RW: local HTC hysteresis */
/* RW: local HTC enable */
}
/* RW: local HTC slew-controlled temperature */
}
}
}
/**
* Get the current raw control temperature (Tctl). I.e. to get the final
* control temperature, which can be used e.g. to compare against T control max
* etc., one needs to substract v->diode_offset from the returned value.
*
* @param pci_hdl handle to the PCI device to query.
* @param v cpu vars: requires flags[DC_0Fh], xfamilyModel, revision
* @param core1 if {@code B_TRUE} read sensor data from core1, otherwise from
* core0 (ignored if not a family 0Fh DualCore CPU).
* @see v->flags & DC_0Fh
* @return 0 if n/a or an error occured, the control temperature in 1/1000 C
* otherwise.
*/
{
if (v->xfamilyModel >= 0x10) {
}
if (v->xfamilyModel < 0x04) {
/* n/a for < 0Fh */
return data;
}
/* F0h */
/* Bit: 2 = core selection (0 == core0, 1 == core1) */
/* Bit: 6 = sensor selection (0 == sensor0, 1 == sensor1) */
/* never saw a 2nd sensor, so we always take sensor0 */
/* select the intended core and refresh */
}
}
} else {
}
if (data != 0) {
data -= 49000;
}
return data;
}
} else { \
x += x < 0 ? -50 : + 50; \
x /= 100; \
}
/**
* Dump all values of the given parameter as a human readable string by
* appending them to the given buffer. The buffer is expected to have at least
* space for 800 chars.
* @param buf where to append the human readable string
* @param v cpu data to dump
* @return pointer to the end of the appended string (the NULL char)
*/
char *
int stc;
return buf;
}
buf[0] = '\0';
if (v->erratum > 0) {
v->erratum);
}
}
/**
* Dump the current final T control values as a human readable string by
* appending them to the given buffer. The buffer is expected to have at least
* space for 80 chars.
* @param buf where to append the human readable string
* @param pci_hdl handle to the PCI device to query.
* @return pointer to the end of the appended string (the NULL char)
*/
char *
int32_t t;
return buf;
}
buf[0] = '\0';
if (v->xfamilyModel < 0x04) {
t = INT32_MAX; /* n/a */
/* 0Fh DualCore */
t = getControlTemp(pci_hdl, v, 0);
} else {
/* 10h + or 0Fh single core */
t = getControlTemp(pci_hdl, v, 0);
}
}
/* vim: tabstop=4:shiftwidth=4:noexpandtab
*/