mirror of
https://github.com/fail0verflow/switch-coreboot.git
synced 2025-05-04 01:39:18 -04:00
missing file.
This commit is contained in:
parent
02846565a9
commit
11c8c6f387
1 changed files with 542 additions and 0 deletions
542
src/mainboard/via/epia-m/smbus.inc
Normal file
542
src/mainboard/via/epia-m/smbus.inc
Normal file
|
@ -0,0 +1,542 @@
|
|||
/* Useful macros PCIBUS, and SMBUS functions for getting DRAM going. */
|
||||
/* courtesy Eric Biederman of linuxnetworx.com */
|
||||
|
||||
#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
|
||||
|
||||
#define DEVFN(device, function) (((device) << 3) + (function))
|
||||
#ifndef CONFIG_ADDR
|
||||
#define CONFIG_ADDR(bus,devfn,where) (((bus) << 16) | ((devfn) << 8) | (where))
|
||||
#endif
|
||||
|
||||
/* jump around these subrs */
|
||||
jmp smbus_pcibus_end
|
||||
|
||||
/* generic SMB routines that work for many systems. The only one that might
|
||||
* not work is the enable_smbus.
|
||||
* you have to define PM_FUNCTION for this to work.
|
||||
*/#define SMBUS_IO_BASE 0xf00
|
||||
#define SMBHSTSTAT 0
|
||||
#define SMBHSTCTL 2
|
||||
#define SMBHSTCMD 3
|
||||
#define SMBHSTADD 4
|
||||
#define SMBHSTDAT0 5
|
||||
#define SMBHSTDAT1 6
|
||||
#define SMBBLKDAT 7
|
||||
|
||||
/* (DA) Lines added to get this to compile */
|
||||
#define PM_DEVFN CONFIG_ADDR(0,0x11*8,0)
|
||||
#define DRAM_CONFIG_PORT 0x5a
|
||||
#define SMBUS_MEM_DEVICE_0 0x50
|
||||
#define LAST_SMBUS_MEM_DEVICE SMBUS_MEM_DEVICE_0
|
||||
#define REGISTERED_DRAM_REGISTER $0x69
|
||||
#define REGISTERED_DRAM $0x2d
|
||||
#define NONREGISTERED_DRAM $0
|
||||
|
||||
|
||||
enable_smbus:
|
||||
/* put the SMBUS at port 0xf00 */
|
||||
CS_WRITE_WORD(PM_DEVFN+ 0xd0, SMBUS_IO_BASE|1) /* iobase addr */
|
||||
CS_WRITE_BYTE(PM_DEVFN + 0xd2, (0x4 << 1) | 1) /* smbus enable */
|
||||
CS_WRITE_WORD(PM_DEVFN + 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:
|
||||
xor %eax,%eax
|
||||
movl $(SMBUS_IO_BASE +SMBHSTSTAT), %edx
|
||||
outb %al, %dx
|
||||
RET_LABEL(setup_smbus)
|
||||
|
||||
#ifndef SMBUS_MEM_DEVICE_0
|
||||
#define SMBUS_MEM_DEVICE_0 (0xa << 3)
|
||||
#endif
|
||||
#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)
|
||||
|
||||
/*
|
||||
* Routine: smbus_wait_until_ready
|
||||
* Arguments: none
|
||||
* Results: none
|
||||
* Trashed: eax, edx
|
||||
* Effects: Upon return the smbus is ready to accept commands
|
||||
*/
|
||||
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)
|
||||
|
||||
/*
|
||||
* Routine: smbus_wait_until_done
|
||||
* Arguments: none
|
||||
* Results: none
|
||||
* Trashed: eax, edx
|
||||
* Effects: Upon return the smbus has completed it's most recent transation
|
||||
*/
|
||||
smbus_wait_until_done:
|
||||
movl $(SMBUS_IO_BASE + SMBHSTSTAT), %edx
|
||||
1: inb %dx, %al
|
||||
testb $1, %al
|
||||
jnz 1b
|
||||
2: testb $0xFE, %al
|
||||
jnz 3f
|
||||
inb %dx, %al
|
||||
testb $0xFE, %al
|
||||
jz 2b
|
||||
3: RET_LABEL(smbus_wait_until_done)
|
||||
|
||||
|
||||
/*
|
||||
* Routine: smbus_read_byte
|
||||
* Arguments: %esp return address
|
||||
* %bl device on the smbus to read from
|
||||
* %bh address on the smbus to read
|
||||
*
|
||||
* Results: zf clear
|
||||
* byte read %eax
|
||||
* On Error:
|
||||
* zf set
|
||||
* %eax trashed
|
||||
*
|
||||
* Trashed: %edx, %eax
|
||||
* Effects: reads a byte off of the smbus
|
||||
*/
|
||||
|
||||
#define SMBUS_READ_BYTE(device, address) \
|
||||
movl $( (device) | ((address) << 8)), %ebx ; \
|
||||
CALLSP(smbus_read_byte)
|
||||
|
||||
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
|
||||
inb %dx, %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 */
|
||||
movl $(SMBUS_IO_BASE + SMBHSTSTAT), %edx
|
||||
inb %dx, %al
|
||||
testb $0x02, %al
|
||||
jz 1f
|
||||
movl $(SMBUS_IO_BASE + SMBHSTDAT0), %edx
|
||||
inb %dx, %al
|
||||
1:
|
||||
RETSP
|
||||
|
||||
#if 1 // TEMPORARY (DA) 20030718
|
||||
|
||||
/* now for code to actually do the deed. Eric did such a good job that
|
||||
* this stuff is basically generic.
|
||||
*/
|
||||
/*
|
||||
* Routine: spd_set_drb
|
||||
* Arguments: None
|
||||
*
|
||||
* Trashed: %eax, %ebx, %ecx, %edx, %esi, %edi, %ebp, %esp, %eflags
|
||||
* Effects: Uses serial presence detect to set the
|
||||
* DRB registers which holds the ending memory address assigned
|
||||
* to each DIMM.
|
||||
* Notes: %ebp holds the currently detected end of memory.
|
||||
* %ebx holds the configuration port & SMBUS_MEM_DEVICE for
|
||||
* the current iteration through the loop.
|
||||
* %edi holds the memory size for the first side of the DIMM.
|
||||
* %esi holds the memory size for the second side of the DIMM.
|
||||
* memory size is represent as a power of 2.
|
||||
* An unset memory size is represented as -1 ie. 0xFFFFFFFF
|
||||
*/
|
||||
|
||||
spd_set_drb:
|
||||
xorl %ebp, %ebp /* clear the memory address */
|
||||
movl $((DRAM_CONFIG_PORT << 16) |SMBUS_MEM_DEVICE_0), %ebx
|
||||
spd_set_drb_loop_top:
|
||||
// set -1 power-of-two for side 1 (called bank0 in most chipset docs)
|
||||
xorl %edi, %edi
|
||||
subl $1, %edi
|
||||
// set -1 power-of-two for side 2 (called bank1 in most chipset docs)
|
||||
xorl %esi, %esi
|
||||
subl $1, %esi
|
||||
|
||||
movb $3, %bh /* rows */
|
||||
CALLSP(smbus_read_byte)
|
||||
// If it's zero, then we just set current %ebp into the row
|
||||
// end register
|
||||
jz 20f
|
||||
andl $0xf, %eax
|
||||
addl %eax, %edi
|
||||
|
||||
movb $4, %bh /* columns */
|
||||
CALLSP(smbus_read_byte)
|
||||
andl $0xf, %eax
|
||||
addl %eax, %edi
|
||||
|
||||
movb $17, %bh /* banks */
|
||||
CALLSP(smbus_read_byte)
|
||||
andl $0xff, %eax
|
||||
bsrl %eax, %ecx
|
||||
addl %ecx, %edi
|
||||
|
||||
/* Get the module data width and convert it to a power of two */
|
||||
movb $7, %bh /* (high byte) */
|
||||
CALLSP(smbus_read_byte)
|
||||
andl $0xff, %eax
|
||||
movl %eax, %ecx
|
||||
shll $8, %ecx
|
||||
|
||||
movb $6, %bh /* (low byte) */
|
||||
CALLSP(smbus_read_byte)
|
||||
andl $0xff, %eax
|
||||
orl %eax, %ecx
|
||||
bsrl %ecx, %eax
|
||||
addl %eax, %edi
|
||||
|
||||
/* now I have the ram size in bits as a power of two (less 1) */
|
||||
// It is less 1 since we started with -1 above.
|
||||
// OK, BITS as power of two (but minus 1)
|
||||
// So, e.g., 8 MB is 64 Mb, 64 Mb is 26 bits. Subtract
|
||||
// (26-1) or 25
|
||||
subl $25, %edi /* Make it multiples of 8MB */
|
||||
|
||||
/* side two */
|
||||
movb $5, %bh /* number of physical banks */
|
||||
CALLSP(smbus_read_byte)
|
||||
cmp $1, %al
|
||||
// it's only one bank
|
||||
jbe 20f
|
||||
// It's two banks. So assign edi to esi
|
||||
/* for now only handle the symmetrical case */
|
||||
// it's two banks, assume however that they're the same size.
|
||||
// it's stupid to have any other kind, right?
|
||||
movl %edi, %esi
|
||||
20:
|
||||
/* Compute the end address for the DRB register */
|
||||
// If it is >= 8, i.e. >= 2^8 or 256, skip it.
|
||||
// >= 8 is a bogus value.
|
||||
cmpl $8, %edi
|
||||
jae 21f
|
||||
movl $1, %eax
|
||||
movl %edi, %ecx
|
||||
shll %cl, %eax
|
||||
// increment row-end by the size of this DIMM half
|
||||
addl %eax, %ebp
|
||||
21:
|
||||
/* Write the comuputed value for the first half of the DIMM */
|
||||
movl %ebp, %edx /* value to write into %edx */
|
||||
movl %ebx, %eax
|
||||
shrl $16, %eax /* port address into %eax */
|
||||
PCI_WRITE_CONFIG_BYTE
|
||||
|
||||
/* Compute the end address for the DRB register */
|
||||
cmpl $8, %esi
|
||||
jae 30f
|
||||
mov $1, %eax
|
||||
movl %esi, %ecx
|
||||
shll %cl, %eax
|
||||
addl %eax, %ebp
|
||||
30:
|
||||
/* Write the comuputed value for the second half of the DIMM */
|
||||
movl %ebp, %edx /* value to write into %edx */
|
||||
movl %ebx, %eax
|
||||
shrl $16, %eax /* port address into %eax */
|
||||
addl $1, %eax /* The second half uses one port high */
|
||||
PCI_WRITE_CONFIG_BYTE
|
||||
|
||||
addl $0x00020001, %ebx /* increment the smbus device & the config port */
|
||||
cmpb $LAST_SMBUS_MEM_DEVICE, %bl /* see if I have reached the end */
|
||||
jbe spd_set_drb_loop_top
|
||||
|
||||
/* o.k. I'm done return now */
|
||||
RET_LABEL(spd_set_drb)
|
||||
|
||||
/*
|
||||
* Routine: spd_set_dramc
|
||||
* Arguments: None
|
||||
*
|
||||
* Trashed: %eax, %ebx, %edx, %esp, %eflags
|
||||
* Effects: Uses serial presence detect to set the
|
||||
* DRAMC register, which records if ram is registerd or not,
|
||||
* and controls the refresh rate.
|
||||
* The refresh rate is not set here, as memory refresh
|
||||
* cannot be enbaled until after memory is initialized.
|
||||
* see spd_enable_refresh.
|
||||
* Notes:
|
||||
* FIXME: Check for illegal/unsupported ram configurations and abort
|
||||
* FIXME: won't work with non-contiguous DRAM size regs (like VIA)
|
||||
* need an indirect pointer to an array of reg #s
|
||||
*/
|
||||
|
||||
spd_set_dramc:
|
||||
/* auto detect if ram is registered or not. */
|
||||
/* The DRAMC register also contorls the refresh rate but we can't
|
||||
* set that here because we must leave refresh disabled.
|
||||
* see: spd_enable_refresh
|
||||
*/
|
||||
/* Find the first dimm and assume the rest are the same */
|
||||
/* Load the smbus device and port int %ebx */
|
||||
movl $((21 << 8) | SMBUS_MEM_DEVICE_0), %ebx
|
||||
1: CALLSP(smbus_read_byte)
|
||||
jz 2f
|
||||
andl $0x12, %eax
|
||||
jmp spd_set_dramc_out
|
||||
|
||||
2: addl $1, %ebx /* increment the device */
|
||||
cmpb $LAST_SMBUS_MEM_DEVICE, %bl
|
||||
jbe 1b
|
||||
/* We couldn't find anything we must have no memory */
|
||||
jmp no_memory
|
||||
|
||||
spd_set_dramc_out:
|
||||
testb $0x12, %al
|
||||
jz 2f
|
||||
movl REGISTERED_DRAM, %eax
|
||||
jmp 1f
|
||||
2: movl NONREGISTERED_DRAM, %eax
|
||||
1: movl %eax, %edx
|
||||
movl REGISTERED_DRAM_REGISTER, %eax
|
||||
PCI_WRITE_CONFIG_BYTE
|
||||
RET_LABEL(spd_set_dramc)
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Routine: spd_read_refresh
|
||||
* Arguments: None
|
||||
*
|
||||
* Trashed: %eax, %ebx, %ecx, %edx, %esp, %eflags
|
||||
* Effects: Uses serial presence detect to find refresh rates.
|
||||
* returns the rate in %eax
|
||||
* It's up to you to set hardware up.
|
||||
* FIXME: Check for illegal/unsupported ram configurations and abort
|
||||
*/
|
||||
|
||||
|
||||
spd_read_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)
|
||||
jz 2f
|
||||
andl $0x7f, %eax
|
||||
jmp spd_enable_refresh_out
|
||||
|
||||
2: addl $1, %ebx /* increment the device */
|
||||
cmpb $LAST_SMBUS_MEM_DEVICE, %bl
|
||||
jbe 1b
|
||||
/* We couldn't find anything we must have no memory */
|
||||
xorl %eax, %eax
|
||||
spd_enable_refresh_out:
|
||||
RET_LABEL(spd_enable_refresh)
|
||||
|
||||
/*
|
||||
* Routine: spd_set_rps
|
||||
* Arguments: None
|
||||
*
|
||||
* Trashed: %eax, %ebx, %ecx, %edx, %esi, %edi, %esp, %eflags
|
||||
* Effects: Uses serial presence detect to set the row size
|
||||
* on a given DIMM
|
||||
* Notes: %esi accumulates the row sizes of all of the DIMMs
|
||||
* %ecx holds the current bit into into %esi
|
||||
* %bl holds the current SMBUS device
|
||||
* FIXME: Check for illegal/unsupported ram configurations and abort
|
||||
*/
|
||||
|
||||
spd_set_rps:
|
||||
/* The RPS register holds the size of a ``page'' of DRAM on each DIMM */
|
||||
/* default all page sizes to 2KB */
|
||||
xorl %esi, %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 $3, %bh
|
||||
CALLSP(smbus_read_byte) /* row address bits */
|
||||
jz 2f
|
||||
andl $0xf, %eax
|
||||
movl %eax, %edi
|
||||
/* I now have the row page size as a power of 2 */
|
||||
subl $11, %edi /* Now make it in multiples of 2Kb */
|
||||
jbe 2f
|
||||
/* FIXME: do something with page sizes greather than 8KB!! */
|
||||
shll %cl, %edi
|
||||
orl %edi, %esi
|
||||
/* side two */
|
||||
movb $5, %bh
|
||||
CALLSP(smbus_read_byte) /* number of physical banks */
|
||||
cmp $1, %al
|
||||
jbe 2f
|
||||
/* for now only handle the symmtrical case */
|
||||
shll $2, %edi
|
||||
shll %cl, %edi
|
||||
orl %edi, %esi
|
||||
|
||||
2: addl $1, %ebx /* increment the device */
|
||||
addl $4, %ecx /* increment the shift count */
|
||||
cmpb $LAST_SMBUS_MEM_DEVICE, %bl
|
||||
jbe 1b
|
||||
|
||||
movl $0x7f, %eax
|
||||
/* I'm not sure what we should do here. Do nothing. */
|
||||
/* PCI_WRITE_CONFIG_WORD*/
|
||||
RET_LABEL(spd_set_rps)
|
||||
|
||||
/*
|
||||
* Routine: spd_set_pgpol
|
||||
* Arguments: None
|
||||
*
|
||||
* Trashed: %eax, %ebx, %ecx, %edx, %esi, %esp, %eflags
|
||||
* Effects: Uses serial presence detect to set the number of banks
|
||||
* on a given DIMM
|
||||
* Notes: %esi accumulates the banks sizes of all of the DIMMs
|
||||
* %ecx holds the current bit into into %esi
|
||||
* %bl holds the current SMBUS device
|
||||
* FIXME: Check for illegal/unsupported ram configurations and abort
|
||||
*/
|
||||
|
||||
spd_set_pgpol:
|
||||
/* The PGPOL register stores the number of logical banks per DIMM,
|
||||
* and number of clocks the DRAM controller waits in the idle
|
||||
* state.
|
||||
*/
|
||||
/* default all bank counts 2 */
|
||||
xorl %esi, %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 $17, %bh
|
||||
CALLSP(smbus_read_byte) /* logical banks */
|
||||
jz 2f
|
||||
cmp $0x4, %eax
|
||||
jl 2f
|
||||
movl $0x1, %eax
|
||||
shll %cl, %eax
|
||||
orl %eax, %esi
|
||||
/* side two */
|
||||
movb $5, %bh
|
||||
CALLSP(smbus_read_byte) /* number of physical banks */
|
||||
cmp $1, %al
|
||||
jbe 2f
|
||||
/* for now only handle the symmtrical case */
|
||||
movl $0x2, %eax
|
||||
shll %cl, %eax
|
||||
orl %eax, %esi
|
||||
|
||||
2: addl $1, %ebx /* increment the device */
|
||||
addl $2, %ecx /* increment the shift count */
|
||||
cmpb $LAST_SMBUS_MEM_DEVICE, %bl
|
||||
jbe 1b
|
||||
|
||||
shll $8, %esi
|
||||
orl $0x7, %esi /* 32 clocks idle time */
|
||||
movl %esi, %ecx
|
||||
movl $0x78, %eax
|
||||
/* I'm unclear on the concept for non-intel devices */
|
||||
/* PCI_WRITE_CONFIG_WORD*/
|
||||
RET_LABEL(spd_set_pgpol)
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Routine: spd_enable_nbxcfg
|
||||
* Arguments: None
|
||||
*
|
||||
* Trashed: %eax, %ebx, %ecx, %edx, %esi, %esp, %eflags
|
||||
* Effects: Uses serial presence detect to set the
|
||||
* ECC support flags in the NBXCFG register
|
||||
* Notes: %esi accumulates the ECC support of the individual DIMMs.
|
||||
* %ecx holds the bit that should be flipped for the current DIMM.
|
||||
* %bl holds the smbus device that corresponds to the current DIMM.
|
||||
* FIXME: Check for illegal/unsupported ram configurations and abort
|
||||
*/
|
||||
|
||||
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 */
|
||||
jz 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 $LAST_SMBUS_MEM_DEVICE, %bl
|
||||
jbe 1b
|
||||
|
||||
movl %esi, %edx
|
||||
/* at some point, we need to indicate how to turn ECC on. Not yet.
|
||||
movl $0x53, %eax
|
||||
PCI_WRITE_CONFIG_BYTE
|
||||
*/
|
||||
RET_LABEL(spd_set_nbxcfg)
|
||||
|
||||
#endif
|
||||
|
||||
smbus_pcibus_end:
|
Loading…
Add table
Reference in a new issue