diff --git a/src/northbridge/intel/430tx/raminit.inc b/src/northbridge/intel/430tx/raminit.inc index f20e986e99..8d7c1a16cd 100644 --- a/src/northbridge/intel/430tx/raminit.inc +++ b/src/northbridge/intel/430tx/raminit.inc @@ -1,510 +1,1020 @@ jmp intel_430_out + + #define USE_SPD 1 + + #define CS_WRITE_BYTE(addr, byte) \ + movl $addr, %eax ; \ + movl $byte, %edx ; \ + PCI_WRITE_CONFIG_BYTE + + #define CS_WRITE_WORD(addr, word) \ + movl $addr, %eax ; \ + movl $word, %ecx ; \ + PCI_WRITE_CONFIG_WORD + + #define CS_WRITE_LONG(addr, dword) \ + movl $addr, %eax ; \ + movl $dword, %ecx ; \ + PCI_WRITE_CONFIG_DWORD + + /* Default memory set to 0 */ + #define DRB 0x00 + + /* The DRB register for the first row */ + #define DRB_REG0 $0x60 + + /* The maximum allowed rows of memory banks */ + #define MAX_ROWS $0x6 + + #define RAM_READ 0x0400 + + #define DIMM0_BASE \ + xorl %eax, %eax + + #define DIMM_BASE(n) \ + movl $(0x60 + ((n) -1)), %eax ; \ + PCI_READ_CONFIG_BYTE ; \ + andl $0xFF, %eax ; \ + /* 4 MB granularity for the 430TX */ + shll $22, %eax ; \ + + #define DIMM_READ \ + addl %ebx, %eax ; \ + movl (%eax), %edx ; \ + xorl $0xdff8, %eax ; \ + movl (%eax), %edx + + #define DIMM0_READ DIMM0_BASE ; DIMM_READ + #define DIMM1_READ DIMM_BASE(1) ; DIMM_READ + #define DIMM2_READ DIMM_BASE(2) ; DIMM_READ + #define DIMM3_READ DIMM_BASE(3) ; DIMM_READ + #define DIMM4_READ DIMM_BASE(4) ; DIMM_READ + #define DIMM5_READ DIMM_BASE(5) ; DIMM_READ + + #define DIMMS_READ_EBX_OFFSET \ + DIMM0_READ ; \ + DIMM1_READ ; \ + DIMM2_READ ; \ + DIMM3_READ ; \ + DIMM4_READ ; \ + DIMM5_READ ; \ + + #define DIMMS_READ(offset) \ + movl $offset, %ebx ; \ + DIMMS_READ_EBX_OFFSET + + #define RAM_COMMAND_NONE 0x0 + #define RAM_COMMAND_NOP 0x1 + #define RAM_COMMAND_PRECHARGE 0x2 + #define RAM_COMMAND_MRS 0x3 + #define RAM_COMMAND_CBR 0x4 + + #define SET_RAM_COMMAND(command) \ + movl $0x54, %eax ; \ + PCI_READ_CONFIG_BYTE ; \ + andl $0x1F, %eax ; \ + orl $((command) << 5), %eax ; \ + movl %eax, %edx ; \ + movl $0x54, %eax ; \ + PCI_WRITE_CONFIG_BYTE + + #define COMPUTE_CAS_MODE \ + movl $0x54, %eax ; \ + PCI_READ_CONFIG_BYTE ; \ + andl $0x10, %eax ; \ + xorl $0x10, %eax ; \ + shll $2, %eax ; \ + orl $0x2a, %eax ; \ + + #define SET_RAM_MODE_REGISTER \ + SET_RAM_COMMAND(RAM_COMMAND_MRS) ; \ + COMPUTE_CAS_MODE ; \ + shll $3, %eax ; \ + movl %eax, %ebx ; \ + DIMMS_READ_EBX_OFFSET + + #define ASSERT_RAM_COMMAND() DIMMS_READ(RAM_READ) + #define ASSERT_MRS_RAM_COMMAND(mode) DIMMS_READ(mode) + #define ENABLE_REFRESH() CALL_LABEL(spd_enable_refresh) + + + /* Default values for config registers */ + + /* #define SET_NBXCFG \ + CS_WRITE_LONG(0x50, 0xff00a00c) */ + + #define SET_DRAMC \ - CS_WRITE_BYTE(0x67, 0xB0) \ - CS_WRITE_BYTE(0x68, 0xF0) \ + + CS_WRITE_BYTE(0x67, 0xB0) ;\ + + CS_WRITE_BYTE(0x68, 0xF0) + + /* PAM - Programmable Attribute Map Registers */ + /* Ideally we want to enable all of these as DRAM and teach + * linux it is o.k. to use them... + */ + #define SET_PAM \ + CS_WRITE_BYTE(0x59, 0x00) ; \ + CS_WRITE_BYTE(0x5a, 0x00) ; \ + CS_WRITE_BYTE(0x5b, 0x00) ; \ + CS_WRITE_BYTE(0x5c, 0x00) ; \ + CS_WRITE_BYTE(0x5d, 0x00) ; \ + CS_WRITE_BYTE(0x5e, 0x00) ; \ + CS_WRITE_BYTE(0x5f, 0x00) + + #define SET_DRB \ + /* DRB - DRAM Row Boundary Registers */ \ + CS_WRITE_BYTE(0x60, DRB) ; \ + CS_WRITE_BYTE(0x61, DRB) ; \ + CS_WRITE_BYTE(0x62, DRB) ; \ + CS_WRITE_BYTE(0x63, DRB) ; \ + CS_WRITE_BYTE(0x64, DRB) ; \ + CS_WRITE_BYTE(0x65, DRB) ; \ + + #define SET_FDHC \ + CS_WRITE_BYTE(0x57, 0x01) + + /* #define SET_RPS \ + CS_WRITE_WORD(0x74, 0x0000)*/ + + #define SET_SDRAMC \ + CS_WRITE_BYTE(0x54, 0x00) + + /* #define SET_PGPOL \ + CS_WRITE_WORD(0x78, 0xff00)*/ + + /* PMCR - Power Management Control Register + Enable normal refresh operation and + the gated clock */ + #define SET_PMCR \ + CS_WRITE_BYTE(0x79, 0x10) + + ram_set_registers: + /* SET_NBXCFG */ + SET_DRAMC + SET_PAM + SET_DRB + SET_FDHC + /* SET_RPS */ + SET_SDRAMC + /* SET_PGPOL */ + SET_PMCR + RET_LABEL(ram_set_registers) + + + #define DEVFN(device, function) (((device) << 3) + (function)) + #define CONFIG_ADDR(bus,devfn,where) (((bus) << 16) | ((devfn) << 8) | (where)) + #define PM_FUNCTION CONFIG_ADDR(0, PIIX4_DEVFN+3, 0) + + #define SMBUS_IO_BASE 0x1000 + #define SMBHSTSTAT 0 + #define SMBHSTCTL 2 + #define SMBHSTCMD 3 + #define SMBHSTADD 4 + #define SMBHSTDAT0 5 + #define SMBHSTDAT1 6 + #define SMBBLKDAT 7 + + enable_smbus: + CS_WRITE_LONG(PM_FUNCTION + 0x90, SMBUS_IO_BASE | 1) /* iobase addr */ + CS_WRITE_BYTE(PM_FUNCTION + 0xd2, (0x4 << 1) | 1) /* smbus enable */ + CS_WRITE_WORD(PM_FUNCTION + 0x4, 1) /* iospace enable */ + RET_LABEL(enable_smbus) + + /* + * Routine: setup_smbus + * Arguments: none + * Results: none + * Trashed: eax, edx + * Effects: The smbus is enabled + */ + setup_smbus: + mov $0x1e, %al + movl $(SMBUS_IO_BASE + SMBHSTSTAT), %edx + outb %al, %dx + RET_LABEL(setup_smbus) + + #define SMBUS_MEM_DEVICE_0 (0xa << 3) + #define SMBUS_MEM_DEVICE_1 (SMBUS_MEM_DEVICE_0 +1) + #define SMBUS_MEM_DEVICE_2 (SMBUS_MEM_DEVICE_0 +2) + #define SMBUS_MEM_DEVICE_3 (SMBUS_MEM_DEVICE_0 +3) + + + smbus_wait_until_ready: + movl $(SMBUS_IO_BASE + SMBHSTSTAT), %edx + 1: inb %dx, %al + testb $1, %al + jnz 1b + RET_LABEL(smbus_wait_until_ready) + + smbus_wait_until_done: + movl $(SMBUS_IO_BASE + SMBHSTSTAT), %edx + 1: inb %dx, %al + testb $1, %al + jnz 1b + testb $0xfe, %al + jz 1b /* wait until interrupt status is set */ + RET_LABEL(smbus_wait_until_done) + + smbus_read_byte: + /* poll until the smbus is ready for commands */ + CALL_LABEL(smbus_wait_until_ready) + + /* clear any lingering errors, so that the transaction will run */ + movl $(SMBUS_IO_BASE + SMBHSTSTAT), %edx + mov $0x1e, %al + outb %al, %dx + + /* set the device I'm talking to */ + movl $(SMBUS_IO_BASE + SMBHSTADD), %edx + movb %bl /* device */, %al + shlb $1, %al + orb $1, %al + outb %al, %dx + + /* set the command address... */ + movl $(SMBUS_IO_BASE + SMBHSTCMD), %edx + movb %bh /* address */, %al + outb %al, %dx + + /* clear the data byte */ + movl $(SMBUS_IO_BASE + SMBHSTDAT0), %edx + xorl %eax, %eax + outb %al, %dx + + /* start a byte read, with interrupts disabled */ + movl $(SMBUS_IO_BASE + SMBHSTCTL), %edx + movl $((0x2 << 2) | (1 << 6)), %eax + outb %al, %dx + + /* poll for transaction completion */ + CALL_LABEL(smbus_wait_until_done) + + /* read the results and see if we succeded */ + 1: movl $(SMBUS_IO_BASE + SMBHSTSTAT), %edx + inb %dx, %al + testb $0x04, %al + jnz 1f + movl $(SMBUS_IO_BASE + SMBHSTDAT0), %edx + inb %dx, %al + 1: + RETSP + + configure_rps_pgpol_drb: + + /* %bl is the row index */ + xorl %ebx, %ebx + + /* %si is the aggregation of bits for RPS */ + xorl %esi, %esi + + /* %di has bits to be set in PGPOL */ + xorl %edi, %edi + + /* %cx holds the cumulative memory size of DIMMs */ + xorl %ecx, %ecx + + + next_row: + movzx %bl, %dx + shl $16, %ebx + shr $1, %dx + mov $((5 << 8) | SMBUS_MEM_DEVICE_0), %bx + or %dx, %bx + CALLSP(smbus_read_byte) + jnz shift_before_moving_on + shr $16, %ebx + + testb $2, %al + jnz two_rows + testb $1, %bl + /* this used to be jnz but seems it should be jz */ + jz ready_for_next_row + + two_rows: + movzx %bl, %dx + shl $16, %ebx + shr $1, %dx + mov $((4 << 8) | SMBUS_MEM_DEVICE_0), %bx + or %dx, %bx + CALLSP(smbus_read_byte) + shr $16, %ebx + + subb $8, %al + shrd $2, %ax, %si + + movzx %bl, %dx + shl $16, %ebx + shr $1, %dx + mov $((17 << 8) | SMBUS_MEM_DEVICE_0), %bx + or %dx, %bx + CALLSP(smbus_read_byte) + shr $16, %ebx + + cmpb $4, %al + sete %al + shrd $1, %ax, %di + + movzx %bl, %dx + shl $16, %ebx + shr $1, %dx + mov $((31 << 8) | SMBUS_MEM_DEVICE_0), %bx + or %dx, %bx + CALLSP(smbus_read_byte) + shr $16, %ebx + + shr $1, %al + xorb %ah, %ah + add %ax, %cx + movzx %cx, %edx + movzx %bl, %eax + addl $0x60, %eax + PCI_WRITE_CONFIG_BYTE + + jmp 1f + + shift_before_moving_on: + shr $16, %ebx + ready_for_next_row: + shr $2, %si + shr $1, %di + movzx %cx, %edx + movzx %bl, %eax + addl $0x60, %eax + PCI_WRITE_CONFIG_BYTE + + 1: + inc %bl + cmp MAX_ROWS, %bl + jb next_row + + /* Now to finally write the RPS and PGPOL registers */ + + movzx %si, %ecx + movl $0x74, %eax + PCI_WRITE_CONFIG_WORD + + mov %di, %cx + movzx %ch, %edx + movl $0x79, %eax + PCI_WRITE_CONFIG_BYTE + + RET_LABEL(configure_rps_pgpol_drb) + + configure_sdramc: + movl $((18 << 8) | SMBUS_MEM_DEVICE_0), %ebx + 0: CALLSP(smbus_read_byte) + jnz 1f + + testb $2, %al + jz 2f /* Default settings are OK if only CAS=3 is supported */ + movl $0x06, %edx + movl $0x76, %eax + PCI_WRITE_CONFIG_BYTE + jmp 2f + + 1: addl $1, %ebx /* increment the device */ + cmpb $SMBUS_MEM_DEVICE_3, %bl + jbe 0b + 2: + RET_LABEL(configure_sdramc) + + spd_set_dramc: + movl $((21 << 8) | SMBUS_MEM_DEVICE_0), %ebx + 1: CALLSP(smbus_read_byte) + jnz 2f + andl $0x12, %eax + jmp spd_set_dramc_out + + 2: addl $1, %ebx /* increment the device */ + cmpb $SMBUS_MEM_DEVICE_3, %bl + jbe 1b + jmp no_memory + + spd_set_dramc_out: + testb $0x2, %al + movl $8, %eax + jz 1f + movl $0x10, %eax + 1: movl %eax, %edx + movl $0x57, %eax + PCI_WRITE_CONFIG_BYTE + RET_LABEL(spd_set_dramc) + + + /* + * Routine: spd_enable_refresh + * Arguments: None + * + * Trashed: %eax, %ebx, %ecx, %edx, %esp, %eflags + * Effects: Uses serial presence detect to set the + * refresh rate in the DRAMC register. + * see spd_set_dramc for the other values. + * FIXME: Check for illegal/unsupported ram configurations and abort + */ + + refresh_rates: + .byte 0x01 /* Normal 15.625 us -> 15.6 us */ + .byte 0x05 /* Reduced(.25X) 3.9 us -> 7.8 us */ + .byte 0x05 /* Reduced(.5X) 7.8 us -> 7.8 us */ + .byte 0x02 /* Extended(2x) 31.3 us -> 31.2 us */ + .byte 0x03 /* Extended(4x) 62.5 us -> 62.4 us */ + .byte 0x04 /* Extended(8x) 125 us -> 124.8 us */ + + spd_enable_refresh: + /* Find the first dimm and assume the rest are the same */ + /* Load the smbus device and port int %ebx */ + movl $((12 << 8) | SMBUS_MEM_DEVICE_0), %ebx + 1: CALLSP(smbus_read_byte) + jnz 2f + andl $0x7f, %eax + jmp spd_enable_refresh_out + + 2: addl $1, %ebx /* increment the device */ + cmpb $SMBUS_MEM_DEVICE_3, %bl + jbe 1b + /* We couldn't find anything we must have no memory */ + jmp no_memory + + spd_enable_refresh_out: + cmpb $0x06, %al + ja 1f + addl $refresh_rates, %eax + movb (%eax), %cl + jmp 2f + 1: movb $0x05, %cl + 2: movl $0x57, %eax + PCI_READ_CONFIG_BYTE + andb $0xf8, %al + orb %cl, %al + movb %al, %dl + movl $0x57, %eax + PCI_WRITE_CONFIG_BYTE + RET_LABEL(spd_enable_refresh) + + spd_set_nbxcfg: + /* say all dimms have no ECC support */ + movl $0xFF, %esi + /* Index into %esi of bit to set */ + movl $0 , %ecx + /* Load the smbus device into %ebx */ + movl $SMBUS_MEM_DEVICE_0, %ebx + + 1: movb $11, %bh + CALLSP(smbus_read_byte) /* module error correction type */ + jnz 2f + cmp $0x2, %eax /* 0 == None, 1 == Parity, 2 == ECC */ + jne 2f + movl $0x1, %eax + shll %cl, %eax + xorl %eax, %esi + + /* side two */ + movb $5, %bh + CALLSP(smbus_read_byte) /* number of physical banks */ + cmp $1, %al + jbe 2f + /* The only is the symmtrical case */ + movl $0x2, %eax + shll %cl, %eax + xorl %eax, %esi + + 2: addl $1, %ebx /* increment the device */ + addl $2, %ecx /* increment the shift count */ + cmpb $SMBUS_MEM_DEVICE_3, %bl + jbe 1b + + movl %esi, %edx + movl $0x53, %eax + PCI_WRITE_CONFIG_BYTE + + movl $((126 << 8) | SMBUS_MEM_DEVICE_0), %ebx + CALLSP(smbus_read_byte) + + RET_LABEL(spd_set_nbxcfg) + + ram_set_spd_registers: + CALL_LABEL(enable_smbus) + CALL_LABEL(setup_smbus) + CALL_LABEL(configure_sdramc) + CALL_LABEL(configure_rps_pgpol_drb) + CALL_LABEL(spd_set_dramc) + CALL_LABEL(spd_set_nbxcfg) + + RET_LABEL(ram_set_spd_registers) + + + /* things that are not used */ + #define FIRST_NORMAL_REFERENCE() + #define SPECIAL_FINISHUP() + + intel_430_out: + +