#ifndef __KERNEL__ #include #endif #include #ifndef __KERNEL__ typedef unsigned short u16; typedef unsigned long u32; #define CONFIG_CMD(bus,devfn, where) (0x80000000 | (bus << 16) | (devfn << 8) | (where & ~3)) static int pci_conf1_write_config_byte(unsigned char bus, int devfn, int where, unsigned char value) { outl(CONFIG_CMD(bus,devfn,where), 0xCF8); outb(value, (0xCFC + (where&2))); return 0; } static int pci_conf1_write_config_word(unsigned char bus, int devfn, int where, u16 value) { outl(CONFIG_CMD(bus,devfn,where), 0xCF8); outw(value, (0xCFC + (where&2))); return 0; } static int pci_conf1_write_config_dword(unsigned char bus, int devfn, int where, u32 value) { outl(CONFIG_CMD(bus,devfn,where), 0xCF8); outl(value, 0xcfc); return 0; } static int pci_conf1_read_config_word(unsigned char bus, int devfn, int where, u16 *value) { outl(CONFIG_CMD(bus,devfn,where), 0xCF8); *value = inw(0xCFC + (where&2)); return 0; } static int pci_conf1_read_config_dword(unsigned char bus, int devfn, int where, u32 *value) { u32 retval; int i, j; outl(CONFIG_CMD(bus,devfn,where), 0xCF8); retval = inl(0xCFC); *value = retval; return 0; } #endif #include /* Define register offsets */ #define SMBHSTSTS 0x00 #define SMBHSTCTL 0x02 #define SMBHSTCMD 0x03 // Register tells what data to get #define SMBHSTADD 0x04 #define SMBHSTDAT0 0x05 #define SMBHSTDAT1 0x06 #define SMBBLKDAT 0x07 /* Define register settings */ #define HOST_RESET 0x8e #define DIMM_BASE 0xa0 // 1010000 is base for DIMM in SMBus #define READ_CMD 0x01 // 1 in the 0 bit of SMBHSTADD states to READ /* Define SPD Data locations */ #define MEM_TYPE 2 // Memory Type - EDO, FPM, SDRAM #define NUM_ROWS 3 // Number of Row Addresses #define NUM_COLS 4 // Number of Column Addresses #define NUM_MOD_ROWS 5 // Number of Module Rows #define CAS_LAT 18 // CAS Latencies Supported #define MOD_ATTR 21 // SDRAM Module Attributes #define BANK_DENSITY 31 // Module Bank Density #define SPD_REV 62 // SPD Revision #define SPEC_FREQ 126 // Specification Frequency - 66, 100, ... inline void waitbusy(unsigned short baseaddr); inline int waitstatus(unsigned short baseaddr); int error_check(unsigned short baseaddr); int main() { int devfn; unsigned short baseaddr; int dimm; int status; int data; int rows, density; // For RAM size calcs u32 val; int i; iopl(3); // Find the PIIX4 Power Management function for (devfn = 0; devfn < 256; devfn++) { pci_conf1_read_config_dword(0, devfn, 0, &val); if (val == 0x71138086) break; } if (devfn == 256) return(1); // Get SMBus IO Base register pci_conf1_read_config_word(0, devfn, 0x90, &baseaddr); baseaddr &= 0xfff0; printf("got devfn 0x%x baseaddr 0x%x\n", devfn, baseaddr); if (!baseaddr) // Still in default return(2); pci_conf1_write_config_word(0, devfn, 0xd2, (4<<1)|1); /* Communicate on the SMBus through the SMBus IO Space Registers * See the System Management Bus Specification [1] for general * information about the bus. See the PIIX4 datasheet [2] for * information about the SMBus IO Space Registers (p. 148) */ waitbusy(baseaddr); for(i = 0x1; i < 0x100; i += 2) { if (i == 0xd3) continue; outb(HOST_RESET, baseaddr + SMBHSTSTS); outb(i, baseaddr + SMBHSTADD); outb(0x3, baseaddr + SMBHSTCMD); outb(0, baseaddr + SMBHSTDAT0); outb((2<<2)|(1<<6), baseaddr + SMBHSTCTL); /* Check DIMM status once */ status = waitstatus(baseaddr); printf("status 0x%x ", status); if (status == 4) {printf("no device at 0x%x\n", i);} else printf("device at 0x%x\n", i); } #if 0 #endif /* Select the DIMM chip to read SPD data (cycle through all of them) */ for (dimm = 0; dimm < 8; dimm++) { /* Reset the controller status before using */ outb(HOST_RESET, baseaddr + SMBHSTSTS); printf("DIMM #%d:\n", dimm); /* Set up SPD for READ's */ outb(DIMM_BASE | (dimm << 1) | READ_CMD, baseaddr + SMBHSTADD); /* For each piece of Data below, we select the data number in the Command Register (SBDHSTCMD) and tell the Control Register (SMBHSTCTL) we are reading a byte of data. Then grab the data from data register 0 (SMBHSTDAT0). */ #if 0 /* Specification Revision */ outb(SPD_REV, baseaddr + SMBHSTCMD); outb(0x48, baseaddr + SMBHSTCTL); #endif outb(0x3, baseaddr + SMBHSTCMD); outb(0, baseaddr + SMBHSTDAT0); outb((2<<2)|(1<<6), baseaddr + SMBHSTCTL); /* Check DIMM status once */ status = waitstatus(baseaddr); if (status & 0x04) { printf("No SPD present at DIMM\n\n"); continue; } else if (error_check(baseaddr)) return (3); data = inb(baseaddr + SMBHSTDAT0); printf("SPD Revision: %d.%d\n", (data & 0xf0) >> 4, data & 0x0f); /* Memory Type */ outb(HOST_RESET, baseaddr + SMBHSTSTS); outb(MEM_TYPE, baseaddr + SMBHSTCMD); outb(0x48, baseaddr + SMBHSTCTL); if (error_check(baseaddr)) return(4); data = inb(baseaddr + SMBHSTDAT0); switch(data) { case 0x02: { printf("DIMM #%d contains EDO\n", dimm); break; } case 0x04: { printf("DIMM #%d contains SDRAM\n", dimm); break; } default: { printf("DIMM %d contains unknown (0x%02X) type\n", dimm, data); break; } } /* Memory Size */ /* Start w/ number of module rows */ outb(HOST_RESET, baseaddr + SMBHSTSTS); outb(NUM_MOD_ROWS, baseaddr + SMBHSTCMD); outb(0x48, baseaddr + SMBHSTCTL); if (error_check(baseaddr)) return(5); rows = inb(baseaddr + SMBHSTDAT0); if (rows == 0) { printf("Undefined number of rows"); } else { printf("%d module rows on DIMM\n", rows); } /* Now do size of row */ outb(HOST_RESET, baseaddr + SMBHSTSTS); outb(BANK_DENSITY, baseaddr + SMBHSTCMD); outb(0x48, baseaddr + SMBHSTCTL); if (error_check(baseaddr)) return(6); density = inb(baseaddr + SMBHSTDAT0); switch (density) { case 0x01: case 0x02: case 0x04: case 0x08: case 0x10: case 0x20: case 0x40: case 0x80: { printf("DIMM has %dMByte per row.\n", density << 2); printf("DIMM has %dMByte total.\n", (density << 2) * rows); break; } default: { printf("DIMM has mixed memory per row.\n"); printf("Unknown total memory.\n"); break; } } } return(0); } /* Waitbusy... could possibly hang CPU, so insert timeout */ inline void waitbusy (unsigned short baseaddr) { while(1) if ( !(inb(baseaddr + SMBHSTSTS) & 0x01) ) return; } /* waitstatus... could also hang CPU */ inline int waitstatus (unsigned short baseaddr) { int status; do status = inb(baseaddr + SMBHSTSTS); while ( (status & 0x01) != 0 || status == 0); return (status); } /* Error check for transaction errors */ int error_check (unsigned short baseaddr) { int status; status = waitstatus(baseaddr); if ( !(status & 0x02) ) { printf("Transaction error 0x%02X\n", status); return(1); } return(0); }