Perform major simplification in smbus.c to reduce code size while keeping the functions readable, removing several unused strings and unnecessary arguments passed to several functions.

Add a bunch of PCI device IDs and driver indications for other SMBus controllers, so as to avoid other persons having to do that work again.
Add support for two PIIX4 devices: the standard PIIX4 PCI device ID, and the ServerWorks CSB5, which has a slight twist.

Co-authored-by: Lionel Debroux <lionel_debroux@yahoo.fr>
Co-authored-by: Sam Demeulemeester <sam@x86.fr>
This commit is contained in:
Lionel Debroux 2022-06-06 10:24:45 +02:00 committed by Sam Demeulemeester
parent 6cd356f831
commit e86b04a14a
2 changed files with 305 additions and 138 deletions

View File

@ -22,6 +22,7 @@ ram_info ram = { 0, 0, 0, 0, 0, "N/A"};
int smbdev, smbfun;
unsigned short smbusbase = 0;
uint32_t smbus_id;
static uint16_t extra_initial_sleep_for_smb_transaction = 0;
static int8_t spd_page = -1;
static int8_t last_adr = -1;
@ -37,117 +38,22 @@ static spd_info parse_spd_ddr4 (uint8_t slot_idx);
static spd_info parse_spd_ddr5 (uint8_t slot_idx);
static void print_spdi(spd_info spdi, uint8_t lidx);
static int find_smb_controller(void);
static bool setup_smb_controller(void);
static bool find_smb_controller(uint16_t vid, uint16_t did);
static uint8_t get_spd(uint8_t slot_idx, uint16_t spd_adr);
static void nv_mcp_get_smb(void);
static void amd_sb_get_smb(void);
static void fch_zen_get_smb(void);
static void piix4_get_smb(void);
static void ich5_get_smb(void);
static bool nv_mcp_get_smb(void);
static bool amd_sb_get_smb(void);
static bool fch_zen_get_smb(void);
static bool piix4_get_smb(void);
static bool ich5_get_smb(void);
static uint8_t ich5_process(void);
static uint8_t ich5_read_spd_byte(uint8_t adr, uint16_t cmd);
static uint8_t nf_read_spd_byte(uint8_t smbus_adr, uint8_t spd_adr);
// ----------------------------------------------------------
// WARNING: Be careful when adding a controller ID!
// Incorrect SMB accesses (ie: on bank switch) can brick your
// motherboard or your memory module.
// ----
// No Pull Request including a new SMBUS Controller will be
// accepted without a proof (screenshot) that it has been
// tested successfully on a real motherboard.
// ----------------------------------------------------------
static const struct pci_smbus_controller smbcontrollers[] = {
{0x8086, 0x2413, ich5_get_smb}, // 82801AA (ICH)
{0x8086, 0x2423, ich5_get_smb}, // 82801AB (ICH)
{0x8086, 0x2443, ich5_get_smb}, // 82801BA (ICH2)
{0x8086, 0x2483, ich5_get_smb}, // 82801CA (ICH3)
{0x8086, 0x24C3, ich5_get_smb}, // 82801DB (ICH4)
{0x8086, 0x24D3, ich5_get_smb}, // 82801E (ICH5)
{0x8086, 0x25A4, ich5_get_smb}, // 6300ESB
{0x8086, 0x266A, ich5_get_smb}, // 82801F (ICH6)
{0x8086, 0x269B, ich5_get_smb}, // 6310ESB/6320ESB
{0x8086, 0x27DA, ich5_get_smb}, // 82801G (ICH7)
{0x8086, 0x283E, ich5_get_smb}, // 82801H (ICH8)
{0x8086, 0x2930, ich5_get_smb}, // 82801I (ICH9)
{0x8086, 0x5032, ich5_get_smb}, // EP80579 (Tolapai)
{0x8086, 0x3A30, ich5_get_smb}, // ICH10
{0x8086, 0x3A60, ich5_get_smb}, // ICH10
{0x8086, 0x3B30, ich5_get_smb}, // 5/3400 Series (PCH)
{0x8086, 0x1C22, ich5_get_smb}, // 6 Series (PCH)
{0x8086, 0x1D22, ich5_get_smb}, // Patsburg (PCH)
{0x8086, 0x1D70, ich5_get_smb}, // Patsburg (PCH) IDF
{0x8086, 0x1D71, ich5_get_smb}, // Patsburg (PCH) IDF
{0x8086, 0x1D72, ich5_get_smb}, // Patsburg (PCH) IDF
{0x8086, 0x2330, ich5_get_smb}, // DH89xxCC (PCH)
{0x8086, 0x1E22, ich5_get_smb}, // Panther Point (PCH)
{0x8086, 0x8C22, ich5_get_smb}, // Lynx Point (PCH)
{0x8086, 0x9C22, ich5_get_smb}, // Lynx Point-LP (PCH)
{0x8086, 0x1F3C, ich5_get_smb}, // Avoton (SOC)
{0x8086, 0x8D22, ich5_get_smb}, // Wellsburg (PCH)
{0x8086, 0x8D7D, ich5_get_smb}, // Wellsburg (PCH) MS
{0x8086, 0x8D7E, ich5_get_smb}, // Wellsburg (PCH) MS
{0x8086, 0x8D7F, ich5_get_smb}, // Wellsburg (PCH) MS
{0x8086, 0x23B0, ich5_get_smb}, // Coleto Creek (PCH)
{0x8086, 0x8CA2, ich5_get_smb}, // Wildcat Point (PCH)
{0x8086, 0x9CA2, ich5_get_smb}, // Wildcat Point-LP (PCH)
{0x8086, 0x0F12, ich5_get_smb}, // BayTrail (SOC)
{0x8086, 0x2292, ich5_get_smb}, // Braswell (SOC)
{0x8086, 0xA123, ich5_get_smb}, // Sunrise Point-H (PCH)
{0x8086, 0x9D23, ich5_get_smb}, // Sunrise Point-LP (PCH)
{0x8086, 0x19DF, ich5_get_smb}, // Denverton (SOC)
{0x8086, 0x1BC9, ich5_get_smb}, // Emmitsburg (PCH)
{0x8086, 0xA1A3, ich5_get_smb}, // Lewisburg (PCH)
{0x8086, 0xA223, ich5_get_smb}, // Lewisburg Super (PCH)
{0x8086, 0xA2A3, ich5_get_smb}, // Kaby Lake (PCH-H)
{0x8086, 0x31D4, ich5_get_smb}, // Gemini Lake (SOC)
{0x8086, 0xA323, ich5_get_smb}, // Cannon Lake-H (PCH)
{0x8086, 0x9DA3, ich5_get_smb}, // Cannon Lake-LP (PCH)
{0x8086, 0x18DF, ich5_get_smb}, // Cedar Fork (PCH)
{0x8086, 0x34A3, ich5_get_smb}, // Ice Lake-LP (PCH)
{0x8086, 0x38A3, ich5_get_smb}, // Ice Lake-N (PCH)
{0x8086, 0x02A3, ich5_get_smb}, // Comet Lake (PCH)
{0x8086, 0x06A3, ich5_get_smb}, // Comet Lake-H (PCH)
{0x8086, 0x4B23, ich5_get_smb}, // Elkhart Lake (PCH)
{0x8086, 0xA0A3, ich5_get_smb}, // Tiger Lake-LP (PCH)
{0x8086, 0x43A3, ich5_get_smb}, // Tiger Lake-H (PCH)
{0x8086, 0x4DA3, ich5_get_smb}, // Jasper Lake (SOC)
{0x8086, 0xA3A3, ich5_get_smb}, // Comet Lake-V (PCH)
{0x8086, 0x7AA3, ich5_get_smb}, // Alder Lake-S (PCH)
{0x8086, 0x51A3, ich5_get_smb}, // Alder Lake-P (PCH)
{0x8086, 0x54A3, ich5_get_smb}, // Alder Lake-M (PCH)
{0x8086, 0x7A23, ich5_get_smb}, // Raptor Lake-S (PCH)
// ATI SMBUS
{0x1002, 0x4385, amd_sb_get_smb}, // ATI SB600+ (Now AMD)
// AMD SMBUS
{0x1022, 0x780B, amd_sb_get_smb}, // AMD FCH (Pre-Zen)
{0x1022, 0x790B, fch_zen_get_smb}, // AMD FCH (Zen)
// nVidia SMBUS
// {0x10DE, 0x01B4, nv_mcp_get_smb}, // nForce
{0x10DE, 0x0064, nv_mcp_get_smb}, // nForce 2
// {0x10DE, 0x0084, nv_mcp_get_smb}, // nForce 2 Mobile
// {0x10DE, 0x00E4, nv_mcp_get_smb}, // nForce 3
// {0x10DE, 0x0052, nv_mcp_get_smb}, // nForce 4
// {0x10DE, 0x0264, nv_mcp_get_smb}, // nForce 410/430 MCP
// {0x10DE, 0x0446, nv_mcp_get_smb}, // nForce 520
// {0x10DE, 0x0542, nv_mcp_get_smb}, // nForce 560
// {0x10DE, 0x07D8, nv_mcp_get_smb}, // nForce 630i
{0x10DE, 0x03EB, nv_mcp_get_smb}, // nForce 630a
{0x10DE, 0x0752, nv_mcp_get_smb}, // nForce 720a
// {0x10DE, 0x0AA2, nv_mcp_get_smb}, // nForce 730i
// {0x10DE, 0x0368, nv_mcp_get_smb}, // nForce 790i Ultra
{0, 0, NULL}
};
void print_smbus_startup_info(void) {
int8_t index;
void print_smbus_startup_info(void)
{
uint8_t spdidx = 0, spd_line_idx = 0;
spd_info curspd;
@ -158,15 +64,7 @@ void print_smbus_startup_info(void) {
quirk.process();
}
index = find_smb_controller();
if (index == -1) {
return;
}
smbcontrollers[index].get_adr();
if (smbusbase == 0) {
if (!setup_smb_controller() || smbusbase == 0) {
return;
}
@ -1236,52 +1134,295 @@ static spd_info parse_spd_sdram(uint8_t slot_idx)
return spdi;
}
// --------------------------
// SMBUS Controller Functions
// --------------------------
static int find_smb_controller(void)
static bool setup_smb_controller(void)
{
int i = 0;
unsigned long valuev, valued;
uint16_t vid, did;
for (smbdev = 0; smbdev < 32; smbdev++) {
for (smbfun = 0; smbfun < 8; smbfun++) {
valuev = pci_config_read16(0, smbdev, smbfun, 0);
if (valuev != 0xFFFF) {
for (i = 0; smbcontrollers[i].vendor > 0; i++) {
if (valuev == smbcontrollers[i].vendor) {
valued = pci_config_read16(0, smbdev, smbfun, 2);
if (valued == smbcontrollers[i].device) {
smbus_id = (valuev << 16) | valued;
return i;
}
vid = pci_config_read16(0, smbdev, smbfun, 0);
if (vid != 0xFFFF) {
did = pci_config_read16(0, smbdev, smbfun, 2);
if (did != 0xFFFF) {
if (find_smb_controller(vid, did)) {
return true;
}
}
}
}
}
return -1;
return false;
}
// ----------------------------------------------------------
// WARNING: Be careful when adding a controller ID!
// Incorrect SMB accesses (ie: on bank switch) can brick your
// motherboard or your memory module.
// ----
// No Pull Request including a new SMBUS Controller will be
// accepted without a proof (screenshot) that it has been
// tested successfully on a real motherboard.
// ----------------------------------------------------------
// PCI device IDs for Intel i801 SMBus controller.
static const uint16_t intel_ich5_dids[] =
{
0x2413, // 82801AA (ICH)
0x2423, // 82801AB (ICH)
0x2443, // 82801BA (ICH2)
0x2483, // 82801CA (ICH3)
0x24C3, // 82801DB (ICH4)
0x24D3, // 82801E (ICH5)
0x25A4, // 6300ESB
0x266A, // 82801F (ICH6)
0x269B, // 6310ESB/6320ESB
0x27DA, // 82801G (ICH7)
0x283E, // 82801H (ICH8)
0x2930, // 82801I (ICH9)
0x5032, // EP80579 (Tolapai)
0x3A30, // ICH10
0x3A60, // ICH10
0x3B30, // 5/3400 Series (PCH)
0x1C22, // 6 Series (PCH)
0x1D22, // Patsburg (PCH)
0x1D70, // Patsburg (PCH) IDF
0x1D71, // Patsburg (PCH) IDF
0x1D72, // Patsburg (PCH) IDF
0x2330, // DH89xxCC (PCH)
0x1E22, // Panther Point (PCH)
0x8C22, // Lynx Point (PCH)
0x9C22, // Lynx Point-LP (PCH)
0x1F3C, // Avoton (SOC)
0x8D22, // Wellsburg (PCH)
0x8D7D, // Wellsburg (PCH) MS
0x8D7E, // Wellsburg (PCH) MS
0x8D7F, // Wellsburg (PCH) MS
0x23B0, // Coleto Creek (PCH)
0x8CA2, // Wildcat Point (PCH)
0x9CA2, // Wildcat Point-LP (PCH)
0x0F12, // BayTrail (SOC)
0x2292, // Braswell (SOC)
0xA123, // Sunrise Point-H (PCH)
0x9D23, // Sunrise Point-LP (PCH)
0x19DF, // Denverton (SOC)
0x1BC9, // Emmitsburg (PCH)
0xA1A3, // Lewisburg (PCH)
0xA223, // Lewisburg Super (PCH)
0xA2A3, // Kaby Lake (PCH-H)
0x31D4, // Gemini Lake (SOC)
0xA323, // Cannon Lake-H (PCH)
0x9DA3, // Cannon Lake-LP (PCH)
0x18DF, // Cedar Fork (PCH)
0x34A3, // Ice Lake-LP (PCH)
0x38A3, // Ice Lake-N (PCH)
0x02A3, // Comet Lake (PCH)
0x06A3, // Comet Lake-H (PCH)
0x4B23, // Elkhart Lake (PCH)
0xA0A3, // Tiger Lake-LP (PCH)
0x43A3, // Tiger Lake-H (PCH)
0x4DA3, // Jasper Lake (SOC)
0xA3A3, // Comet Lake-V (PCH)
0x7AA3, // Alder Lake-S (PCH)
0x51A3, // Alder Lake-P (PCH)
0x54A3, // Alder Lake-M (PCH)
0x7A23, // Raptor Lake-S (PCH)
};
static bool find_in_did_array(uint16_t did, const uint16_t * ids, unsigned int size)
{
for (unsigned int i = 0; i < size; i++) {
if (*ids++ == did) {
return true;
}
}
return false;
}
static bool find_smb_controller(uint16_t vid, uint16_t did)
{
switch(vid)
{
case VID_INTEL:
{
if (find_in_did_array(did, intel_ich5_dids, sizeof(intel_ich5_dids) / sizeof(intel_ich5_dids[0]))) {
return ich5_get_smb();
}
if ( did == 0x7113 // 82371AB/EB/MB PIIX4
// || did == 0x719B // 82440/82443MX PMC
) {
return piix4_get_smb();
}
// 0x0C59-0x0C5E, 0x18AC, 0x19AC, 0x1BFF, 0x1F15, 0x1F3D Atom S1200 / C2000 / C3000 SMBus controllers may not be SPD-related.
// 0x0F13 ValleyView SMBus Controller ?
// 0x7603 82372FB PIIX5 ?
// 0x8119 US15W ?
return false;
}
case VID_AMD:
switch(did)
{
#if 0
case 0x740B: // AMD756
case 0x7413: // AMD766
case 0x7443: // AMD768
case 0x746B: // AMD8111_SMBUS
// amd756 / nforce SMB controller
case 0x746A: // AMD8111_SMBUS2
// amd8111 SMB controller
#endif
case 0x780B: // AMD FCH (Pre-Zen)
return amd_sb_get_smb();
case 0x790B: // AMD FCH (Zen 2/3)
return fch_zen_get_smb();
default:
return false;
}
break;
case VID_HYGON:
switch(did)
{
case 0x780B: // AMD FCH (Pre-Zen)
return amd_sb_get_smb();
case 0x790B: // AMD FCH (Zen 2/3)
return fch_zen_get_smb();
default:
return false;
}
break;
case VID_ATI:
switch(did)
{
// case 0x4353: // SB200
// case 0x4363: // SB300
// case 0x4372: // IXP SB4x0
case 0x4385: // SB600+
return amd_sb_get_smb();
default:
return false;
}
break;
case VID_NVIDIA:
switch(did)
{
#if 0
case 0x01B4: // nForce
// amd756 / nforce SMB controller
#endif
// case 0x0034: // MCP04
// case 0x0052: // CK804
case 0x0064: // nForce2 (MCP)
// case 0x0084: // MCP2A
// case 0x00D4: // nForce3
// case 0x0264: // MCP51
// case 0x0368: // MCP55
case 0x03EB: // MCP61
// case 0x0446: // MCP65
// case 0x0542: // MCP67
case 0x0752: // MCP78S
// case 0x07D8: // MCP73
// case 0x0AA2: // MCP79
// case 0x0D79: // MCP89
return nv_mcp_get_smb();
default:
return false;
}
break;
#if 0
case VID_SIS:
switch(did)
{
case 0x0016: // SiS961/2/3
// sis96x SMBus controller.
// There are also sis630 and sis5595 SMBus controllers.
default:
return false;
}
break;
#endif
#if 0
case VID_VIA:
switch(did)
{
case 0x3040: // 82C586_3
// via SMBus controller.
case 0x3050: // 82C596_3
// Try SMB base address = 0x90, then SMB base address = 0x80
// viapro SMBus controller.
case 0x3051: // 82C596B_3
case 0x3057: // 82C686_4
case 0x8235: // 8231_4
// SMB base address = 0x90
// viapro SMBus controller.
case 0x3074: // 8233_0
case 0x3147: // 8233A
case 0x3177: // 8235
case 0x3227: // 8237
case 0x3337: // 8237A
case 0x3372: // 8237S
case 0x3287: // 8251
case 0x8324: // CX700
case 0x8353: // VX800
case 0x8409: // VX855
case 0x8410: // VX900
// SMB base address = 0xD0
// viapro I2C controller.
default:
return false;
}
break;
#endif
case VID_SERVERWORKS:
switch(did)
{
case 0x0201: // CSB5
// From Linux i2c-piix4 driver: unlike its siblings, this model needs a quirk.
extra_initial_sleep_for_smb_transaction = 2100 - 500;
// Fall through.
// case 0x0200: // OSB4
// case 0x0203: // CSB6
// case 0x0205: // HT1000SB
// case 0x0408: // HT1100LD
return piix4_get_smb();
default:
return false;
}
break;
default:
return false;
}
return false;
}
// ----------------------
// PIIX4 SMBUS Controller
// ----------------------
static void piix4_get_smb(void)
static bool piix4_get_smb(void)
{
uint16_t x = pci_config_read16(0, smbdev, smbfun, 0x90) & 0xFFF0;
if (x != 0) {
smbusbase = x;
return true;
}
return false;
}
// ----------------------------
// i801 / ICH5 SMBUS Controller
// ----------------------------
static void ich5_get_smb(void)
static bool ich5_get_smb(void)
{
uint16_t x;
@ -1297,13 +1438,15 @@ static void ich5_get_smb(void)
// Reset SMBUS Controller
__outb(__inb(SMBHSTSTS) & 0x1F, SMBHSTSTS);
usleep(1000);
return (smbusbase != 0);
}
// --------------------
// AMD SMBUS Controller
// --------------------
static void amd_sb_get_smb(void)
static bool amd_sb_get_smb(void)
{
uint8_t rev_id;
uint16_t pm_reg;
@ -1312,10 +1455,10 @@ static void amd_sb_get_smb(void)
if ((smbus_id & 0xFFFF) == 0x4385 && rev_id <= 0x3D) {
// Older AMD SouthBridge (SB700 & older) use PIIX4 registers
piix4_get_smb();
return piix4_get_smb();
} else if ((smbus_id & 0xFFFF) == 0x780B && rev_id == 0x42) {
// Latest Pre-Zen APUs use the newer Zen PM registers
fch_zen_get_smb();
return fch_zen_get_smb();
} else {
// AMD SB (SB800 up to pre-FT3/FP4/AM4) uses specific registers
__outb(AMD_SMBUS_BASE_REG + 1, AMD_INDEX_IO_PORT);
@ -1325,11 +1468,14 @@ static void amd_sb_get_smb(void)
if (pm_reg != 0xFFE0 && pm_reg != 0) {
smbusbase = pm_reg;
return true;
}
}
return false;
}
static void fch_zen_get_smb(void)
static bool fch_zen_get_smb(void)
{
uint16_t pm_reg;
@ -1341,24 +1487,27 @@ static void fch_zen_get_smb(void)
// Special case for AMD Cezanne (get smb address in memory)
if (imc_type == IMC_K19_CZN && pm_reg == 0xFFFF) {
smbusbase = ((*(const uint32_t *)(0xFED80000 + 0x300) >> 8) & 0xFF) << 8;
return;
return true;
}
// Check if IO Smbus is enabled.
if ((pm_reg & 0x10) == 0) {
return;
return false;
}
if ((pm_reg & 0xFF00) != 0) {
smbusbase = pm_reg & 0xFF00;
return true;
}
return false;
}
// -----------------------
// nVidia SMBUS Controller
// -----------------------
static void nv_mcp_get_smb(void)
static bool nv_mcp_get_smb(void)
{
int smbus_base_adr;
@ -1373,7 +1522,10 @@ static void nv_mcp_get_smb(void)
if (x != 0) {
smbusbase = x;
return true;
}
return false;
}
// ------------------
@ -1383,7 +1535,7 @@ static void nv_mcp_get_smb(void)
static uint8_t get_spd(uint8_t slot_idx, uint16_t spd_adr)
{
switch ((smbus_id >> 16) & 0xFFFF) {
case 0x10DE:
case VID_NVIDIA:
return nf_read_spd_byte(slot_idx, (uint8_t)spd_adr);
default:
return ich5_read_spd_byte(slot_idx, spd_adr);
@ -1470,6 +1622,11 @@ static uint8_t ich5_process(void)
__outb(__inb(SMBHSTCNT) | SMBHSTCNT_START, SMBHSTCNT);
// Some SMB controllers need this quirk.
if (extra_initial_sleep_for_smb_transaction) {
usleep(extra_initial_sleep_for_smb_transaction);
}
do {
usleep(500);
status = __inb(SMBHSTSTS);

View File

@ -10,6 +10,16 @@
* Copyright (C) 2004-2022 Samuel Demeulemeester.
*/
/* Vendor IDs */
#define VID_ATI 0x1002
#define VID_AMD 0x1022
#define VID_SIS 0x1039
#define VID_NVIDIA 0x10DE
#define VID_VIA 0x1106
#define VID_SERVERWORKS 0x1166
#define VID_HYGON 0x1D94
#define VID_INTEL 0x8086
#define I2C_WRITE 0
#define I2C_READ 1
@ -74,7 +84,7 @@
#define NVSMBSTS_RES 0x20
#define NVSMBSTS_STATUS 0x1f
struct pci_smbus_controller{
struct pci_smbus_controller {
unsigned vendor;
unsigned device;
void (*get_adr)(void);