typedef unsigned char uint8_t; typedef signed char int8_t; typedef unsigned short uint16_t; typedef signed short int16_t; typedef unsigned int uint32_t; typedef signed int int32_t; typedef unsigned char uint_least8_t; typedef signed char int_least8_t; typedef unsigned short uint_least16_t; typedef signed short int_least16_t; typedef unsigned int uint_least32_t; typedef signed int int_least32_t; typedef unsigned char uint_fast8_t; typedef signed char int_fast8_t; typedef unsigned int uint_fast16_t; typedef signed int int_fast16_t; typedef unsigned int uint_fast32_t; typedef signed int int_fast32_t; typedef int intptr_t; typedef unsigned int uintptr_t; typedef long int intmax_t; typedef unsigned long int uintmax_t; static inline unsigned long apic_read(unsigned long reg) { return *((volatile unsigned long *)(0xfee00000 +reg)); } static inline void apic_write(unsigned long reg, unsigned long v) { *((volatile unsigned long *)(0xfee00000 +reg)) = v; } static inline void apic_wait_icr_idle(void) { do { } while ( apic_read( 0x300 ) & 0x01000 ); } static void outb(unsigned char value, unsigned short port) { __builtin_outb(value, port); } static void outw(unsigned short value, unsigned short port) { __builtin_outw(value, port); } static void outl(unsigned int value, unsigned short port) { __builtin_outl(value, port); } static unsigned char inb(unsigned short port) { return __builtin_inb(port); } static unsigned char inw(unsigned short port) { return __builtin_inw(port); } static unsigned char inl(unsigned short port) { return __builtin_inl(port); } static inline void outsb(uint16_t port, const void *addr, unsigned long count) { __asm__ __volatile__ ( "cld ; rep ; outsb " : "=S" (addr), "=c" (count) : "d"(port), "0"(addr), "1" (count) ); } static inline void outsw(uint16_t port, const void *addr, unsigned long count) { __asm__ __volatile__ ( "cld ; rep ; outsw " : "=S" (addr), "=c" (count) : "d"(port), "0"(addr), "1" (count) ); } static inline void outsl(uint16_t port, const void *addr, unsigned long count) { __asm__ __volatile__ ( "cld ; rep ; outsl " : "=S" (addr), "=c" (count) : "d"(port), "0"(addr), "1" (count) ); } static inline void insb(uint16_t port, void *addr, unsigned long count) { __asm__ __volatile__ ( "cld ; rep ; insb " : "=D" (addr), "=c" (count) : "d"(port), "0"(addr), "1" (count) ); } static inline void insw(uint16_t port, void *addr, unsigned long count) { __asm__ __volatile__ ( "cld ; rep ; insw " : "=D" (addr), "=c" (count) : "d"(port), "0"(addr), "1" (count) ); } static inline void insl(uint16_t port, void *addr, unsigned long count) { __asm__ __volatile__ ( "cld ; rep ; insl " : "=D" (addr), "=c" (count) : "d"(port), "0"(addr), "1" (count) ); } static inline void pnp_write_config(unsigned char port, unsigned char value, unsigned char reg) { outb(reg, port); outb(value, port +1); } static inline unsigned char pnp_read_config(unsigned char port, unsigned char reg) { outb(reg, port); return inb(port +1); } static inline void pnp_set_logical_device(unsigned char port, int device) { pnp_write_config(port, device, 0x07); } static inline void pnp_set_enable(unsigned char port, int enable) { pnp_write_config(port, enable?0x1:0x0, 0x30); } static inline int pnp_read_enable(unsigned char port) { return !!pnp_read_config(port, 0x30); } static inline void pnp_set_iobase0(unsigned char port, unsigned iobase) { pnp_write_config(port, (iobase >> 8) & 0xff, 0x60); pnp_write_config(port, iobase & 0xff, 0x61); } static inline void pnp_set_iobase1(unsigned char port, unsigned iobase) { pnp_write_config(port, (iobase >> 8) & 0xff, 0x62); pnp_write_config(port, iobase & 0xff, 0x63); } static inline void pnp_set_irq0(unsigned char port, unsigned irq) { pnp_write_config(port, irq, 0x70); } static inline void pnp_set_irq1(unsigned char port, unsigned irq) { pnp_write_config(port, irq, 0x72); } static inline void pnp_set_drq(unsigned char port, unsigned drq) { pnp_write_config(port, drq & 0xff, 0x74); } static void hlt(void) { __builtin_hlt(); } typedef __builtin_div_t div_t; typedef __builtin_ldiv_t ldiv_t; typedef __builtin_udiv_t udiv_t; typedef __builtin_uldiv_t uldiv_t; static div_t div(int numer, int denom) { return __builtin_div(numer, denom); } static ldiv_t ldiv(long numer, long denom) { return __builtin_ldiv(numer, denom); } static udiv_t udiv(unsigned numer, unsigned denom) { return __builtin_udiv(numer, denom); } static uldiv_t uldiv(unsigned long numer, unsigned long denom) { return __builtin_uldiv(numer, denom); } int log2(int value) { return __builtin_bsr(value); } typedef unsigned device_t; static unsigned char pci_read_config8(device_t dev, unsigned where) { unsigned addr; addr = dev | where; outl(0x80000000 | (addr & ~3), 0xCF8); return inb(0xCFC + (addr & 3)); } static unsigned short pci_read_config16(device_t dev, unsigned where) { unsigned addr; addr = dev | where; outl(0x80000000 | (addr & ~3), 0xCF8); return inw(0xCFC + (addr & 2)); } static unsigned int pci_read_config32(device_t dev, unsigned where) { unsigned addr; addr = dev | where; outl(0x80000000 | (addr & ~3), 0xCF8); return inl(0xCFC); } static void pci_write_config8(device_t dev, unsigned where, unsigned char value) { unsigned addr; addr = dev | where; outl(0x80000000 | (addr & ~3), 0xCF8); outb(value, 0xCFC + (addr & 3)); } static void pci_write_config16(device_t dev, unsigned where, unsigned short value) { unsigned addr; addr = dev | where; outl(0x80000000 | (addr & ~3), 0xCF8); outw(value, 0xCFC + (addr & 2)); } static void pci_write_config32(device_t dev, unsigned where, unsigned int value) { unsigned addr; addr = dev | where; outl(0x80000000 | (addr & ~3), 0xCF8); outl(value, 0xCFC); } static device_t pci_locate_device(unsigned pci_id, device_t dev) { for(; dev <= ( ((( 255 ) & 0xFF) << 16) | ((( 31 ) & 0x1f) << 11) | ((( 7 ) & 0x7) << 8)) ; dev += ( ((( 0 ) & 0xFF) << 16) | ((( 0 ) & 0x1f) << 11) | ((( 1 ) & 0x7) << 8)) ) { unsigned int id; id = pci_read_config32(dev, 0); if (id == pci_id) { return dev; } } return (0xffffffffU) ; } static int uart_can_tx_byte(void) { return inb(1016 + 0x05 ) & 0x20; } static void uart_wait_to_tx_byte(void) { while(!uart_can_tx_byte()) ; } static void uart_wait_until_sent(void) { while(!(inb(1016 + 0x05 ) & 0x40)) ; } static void uart_tx_byte(unsigned char data) { uart_wait_to_tx_byte(); outb(data, 1016 + 0x00 ); uart_wait_until_sent(); } static void uart_init(void) { outb(0x0, 1016 + 0x01 ); outb(0x01, 1016 + 0x02 ); outb(0x80 | 3 , 1016 + 0x03 ); outb((115200/ 115200 ) & 0xFF, 1016 + 0x00 ); outb(((115200/ 115200 ) >> 8) & 0xFF, 1016 + 0x01 ); outb(3 , 1016 + 0x03 ); } static void __console_tx_byte(unsigned char byte) { uart_tx_byte(byte); } static void __console_tx_nibble(unsigned nibble) { unsigned char digit; digit = nibble + '0'; if (digit > '9') { digit += 39; } __console_tx_byte(digit); } static void __console_tx_char(int loglevel, unsigned char byte) { if (8 > loglevel) { uart_tx_byte(byte); } } static void __console_tx_hex8(int loglevel, unsigned char value) { if (8 > loglevel) { __console_tx_nibble((value >> 4U) & 0x0fU); __console_tx_nibble(value & 0x0fU); } } static void __console_tx_hex16(int loglevel, unsigned short value) { if (8 > loglevel) { __console_tx_nibble((value >> 12U) & 0x0fU); __console_tx_nibble((value >> 8U) & 0x0fU); __console_tx_nibble((value >> 4U) & 0x0fU); __console_tx_nibble(value & 0x0fU); } } static void __console_tx_hex32(int loglevel, unsigned int value) { if (8 > loglevel) { __console_tx_nibble((value >> 28U) & 0x0fU); __console_tx_nibble((value >> 24U) & 0x0fU); __console_tx_nibble((value >> 20U) & 0x0fU); __console_tx_nibble((value >> 16U) & 0x0fU); __console_tx_nibble((value >> 12U) & 0x0fU); __console_tx_nibble((value >> 8U) & 0x0fU); __console_tx_nibble((value >> 4U) & 0x0fU); __console_tx_nibble(value & 0x0fU); } } static void __console_tx_string(int loglevel, const char *str) { if (8 > loglevel) { unsigned char ch; while((ch = *str++) != '\0') { __console_tx_byte(ch); } } } static void print_emerg_char(unsigned char byte) { __console_tx_char(0 , byte); } static void print_emerg_hex8(unsigned char value){ __console_tx_hex8(0 , value); } static void print_emerg_hex16(unsigned short value){ __console_tx_hex16(0 , value); } static void print_emerg_hex32(unsigned int value) { __console_tx_hex32(0 , value); } static void print_emerg(const char *str) { __console_tx_string(0 , str); } static void print_alert_char(unsigned char byte) { __console_tx_char(1 , byte); } static void print_alert_hex8(unsigned char value) { __console_tx_hex8(1 , value); } static void print_alert_hex16(unsigned short value){ __console_tx_hex16(1 , value); } static void print_alert_hex32(unsigned int value) { __console_tx_hex32(1 , value); } static void print_alert(const char *str) { __console_tx_string(1 , str); } static void print_crit_char(unsigned char byte) { __console_tx_char(2 , byte); } static void print_crit_hex8(unsigned char value) { __console_tx_hex8(2 , value); } static void print_crit_hex16(unsigned short value){ __console_tx_hex16(2 , value); } static void print_crit_hex32(unsigned int value) { __console_tx_hex32(2 , value); } static void print_crit(const char *str) { __console_tx_string(2 , str); } static void print_err_char(unsigned char byte) { __console_tx_char(3 , byte); } static void print_err_hex8(unsigned char value) { __console_tx_hex8(3 , value); } static void print_err_hex16(unsigned short value){ __console_tx_hex16(3 , value); } static void print_err_hex32(unsigned int value) { __console_tx_hex32(3 , value); } static void print_err(const char *str) { __console_tx_string(3 , str); } static void print_warning_char(unsigned char byte) { __console_tx_char(4 , byte); } static void print_warning_hex8(unsigned char value) { __console_tx_hex8(4 , value); } static void print_warning_hex16(unsigned short value){ __console_tx_hex16(4 , value); } static void print_warning_hex32(unsigned int value) { __console_tx_hex32(4 , value); } static void print_warning(const char *str) { __console_tx_string(4 , str); } static void print_notice_char(unsigned char byte) { __console_tx_char(5 , byte); } static void print_notice_hex8(unsigned char value) { __console_tx_hex8(5 , value); } static void print_notice_hex16(unsigned short value){ __console_tx_hex16(5 , value); } static void print_notice_hex32(unsigned int value) { __console_tx_hex32(5 , value); } static void print_notice(const char *str) { __console_tx_string(5 , str); } static void print_info_char(unsigned char byte) { __console_tx_char(6 , byte); } static void print_info_hex8(unsigned char value) { __console_tx_hex8(6 , value); } static void print_info_hex16(unsigned short value){ __console_tx_hex16(6 , value); } static void print_info_hex32(unsigned int value) { __console_tx_hex32(6 , value); } static void print_info(const char *str) { __console_tx_string(6 , str); } static void print_debug_char(unsigned char byte) { __console_tx_char(7 , byte); } static void print_debug_hex8(unsigned char value) { __console_tx_hex8(7 , value); } static void print_debug_hex16(unsigned short value){ __console_tx_hex16(7 , value); } static void print_debug_hex32(unsigned int value) { __console_tx_hex32(7 , value); } static void print_debug(const char *str) { __console_tx_string(7 , str); } static void print_spew_char(unsigned char byte) { __console_tx_char(8 , byte); } static void print_spew_hex8(unsigned char value) { __console_tx_hex8(8 , value); } static void print_spew_hex16(unsigned short value){ __console_tx_hex16(8 , value); } static void print_spew_hex32(unsigned int value) { __console_tx_hex32(8 , value); } static void print_spew(const char *str) { __console_tx_string(8 , str); } static void console_init(void) { static const char console_test[] = "\r\n\r\nLinuxBIOS-" "1.1.4" ".0Fallback" " " "Thu Oct 9 20:29:48 MDT 2003" " starting...\r\n"; print_info(console_test); } static void die(const char *str) { print_emerg(str); do { hlt(); } while(1); } static void write_phys(unsigned long addr, unsigned long value) { asm volatile( "movnti %1, (%0)" : : "r" (addr), "r" (value) : ); } static unsigned long read_phys(unsigned long addr) { volatile unsigned long *ptr; ptr = (void *)addr; return *ptr; } static void ram_fill(unsigned long start, unsigned long stop) { unsigned long addr; print_debug("DRAM fill: "); print_debug_hex32(start); print_debug("-"); print_debug_hex32(stop); print_debug("\r\n"); for(addr = start; addr < stop ; addr += 4) { if (!(addr & 0xffff)) { print_debug_hex32(addr); print_debug("\r"); } write_phys(addr, addr); }; print_debug_hex32(addr); print_debug("\r\nDRAM filled\r\n"); } static void ram_verify(unsigned long start, unsigned long stop) { unsigned long addr; print_debug("DRAM verify: "); print_debug_hex32(start); print_debug_char('-'); print_debug_hex32(stop); print_debug("\r\n"); for(addr = start; addr < stop ; addr += 4) { unsigned long value; if (!(addr & 0xffff)) { print_debug_hex32(addr); print_debug("\r"); } value = read_phys(addr); if (value != addr) { print_err_hex32(addr); print_err_char(':'); print_err_hex32(value); print_err("\r\n"); } } print_debug_hex32(addr); print_debug("\r\nDRAM verified\r\n"); } void ram_check(unsigned long start, unsigned long stop) { int result; print_debug("Testing DRAM : "); print_debug_hex32(start); print_debug("-"); print_debug_hex32(stop); print_debug("\r\n"); ram_fill(start, stop); ram_verify(start, stop); print_debug("Done.\r\n"); } static int enumerate_ht_chain(unsigned link) { unsigned next_unitid, last_unitid; int reset_needed = 0; next_unitid = 1; do { uint32_t id; uint8_t hdr_type, pos; last_unitid = next_unitid; id = pci_read_config32(( ((( 0 ) & 0xFF) << 16) | ((( 0 ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) , 0x00 ); if (((id & 0xffff) == 0x0000) || ((id & 0xffff) == 0xffff) || (((id >> 16) & 0xffff) == 0xffff) || (((id >> 16) & 0xffff) == 0x0000)) { break; } hdr_type = pci_read_config8(( ((( 0 ) & 0xFF) << 16) | ((( 0 ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) , 0x0e ); pos = 0; hdr_type &= 0x7f; if ((hdr_type == 0 ) || (hdr_type == 1 )) { pos = pci_read_config8(( ((( 0 ) & 0xFF) << 16) | ((( 0 ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) , 0x34 ); } while(pos != 0) { uint8_t cap; cap = pci_read_config8(( ((( 0 ) & 0xFF) << 16) | ((( 0 ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) , pos + 0 ); if (cap == 0x08 ) { uint16_t flags; flags = pci_read_config16(( ((( 0 ) & 0xFF) << 16) | ((( 0 ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) , pos + 2 ); if ((flags >> 13) == 0) { unsigned count; flags &= ~0x1f; flags |= next_unitid & 0x1f; count = (flags >> 5) & 0x1f; pci_write_config16(( ((( 0 ) & 0xFF) << 16) | ((( 0 ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) , pos + 2 , flags); next_unitid += count; break; } } pos = pci_read_config8(( ((( 0 ) & 0xFF) << 16) | ((( 0 ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) , pos + 1 ); } } while((last_unitid != next_unitid) && (next_unitid <= 0x1f)); return reset_needed; } static void enable_smbus(void) { device_t dev; dev = pci_locate_device((((( 0x746b ) & 0xFFFF) << 16) | (( 0x1022 ) & 0xFFFF)) , 0); if (dev == (0xffffffffU) ) { die("SMBUS controller not found\r\n"); } uint8_t enable; print_debug("SMBus controller enabled\r\n"); pci_write_config32(dev, 0x58, 0x0f00 | 1); enable = pci_read_config8(dev, 0x41); pci_write_config8(dev, 0x41, enable | (1 << 7)); outw(inw(0x0f00 + 0xe0 ), 0x0f00 + 0xe0 ); } static inline void smbus_delay(void) { outb(0x80, 0x80); } static int smbus_wait_until_ready(void) { unsigned long loops; loops = (100*1000*10) ; do { unsigned short val; smbus_delay(); val = inw(0x0f00 + 0xe0 ); if ((val & 0x800) == 0) { break; } if(loops == ((100*1000*10) / 2)) { outw(inw(0x0f00 + 0xe0 ), 0x0f00 + 0xe0 ); } } while(--loops); return loops?0:-2; } static int smbus_wait_until_done(void) { unsigned long loops; loops = (100*1000*10) ; do { unsigned short val; smbus_delay(); val = inw(0x0f00 + 0xe0 ); if (((val & 0x8) == 0) | ((val & 0x437) != 0)) { break; } } while(--loops); return loops?0:-3; } static int smbus_read_byte(unsigned device, unsigned address) { unsigned char global_control_register; unsigned char global_status_register; unsigned char byte; if (smbus_wait_until_ready() < 0) { return -2; } outw(inw(0x0f00 + 0xe2 ) & ~((1<<10)|(1<<9)|(1<<8)|(1<<4)), 0x0f00 + 0xe2 ); outw(((device & 0x7f) << 1) | 1, 0x0f00 + 0xe4 ); outb(address & 0xFF, 0x0f00 + 0xe8 ); outw((inw(0x0f00 + 0xe2 ) & ~7) | (0x2), 0x0f00 + 0xe2 ); outw(inw(0x0f00 + 0xe0 ), 0x0f00 + 0xe0 ); outw(0, 0x0f00 + 0xe6 ); outw((inw(0x0f00 + 0xe2 ) | (1 << 3)), 0x0f00 + 0xe2 ); if (smbus_wait_until_done() < 0) { return -3; } global_status_register = inw(0x0f00 + 0xe0 ); byte = inw(0x0f00 + 0xe6 ) & 0xff; if (global_status_register != (1 << 4)) { return -1; } return byte; } static void smbus_write_byte(unsigned device, unsigned address, unsigned char val) { return; } struct mem_controller { unsigned node_id; device_t f0, f1, f2, f3; uint8_t channel0[4]; uint8_t channel1[4]; }; typedef __builtin_msr_t msr_t; static msr_t rdmsr(unsigned long index) { return __builtin_rdmsr(index); } static void wrmsr(unsigned long index, msr_t msr) { __builtin_wrmsr(index, msr.lo, msr.hi); } struct tsc_struct { unsigned lo; unsigned hi; }; typedef struct tsc_struct tsc_t; static tsc_t rdtsc(void) { tsc_t res; asm ("rdtsc" : "=a" (res.lo), "=d"(res.hi) : : ); return res; } void init_timer(void) { apic_write(0x320 , (1 << 17)|(1<< 16)|(0 << 12)|(0 << 0)); apic_write(0x3E0 , 0xB ); apic_write(0x380 , 0xffffffff); } void udelay(unsigned usecs) { uint32_t start, value, ticks; ticks = usecs * 200; start = apic_read(0x390 ); do { value = apic_read(0x390 ); } while((start - value) < ticks); } void mdelay(unsigned msecs) { unsigned i; for(i = 0; i < msecs; i++) { udelay(1000); } } void delay(unsigned secs) { unsigned i; for(i = 0; i < secs; i++) { mdelay(1000); } } int boot_cpu(void) { volatile unsigned long *local_apic; unsigned long apic_id; int bsp; msr_t msr; msr = rdmsr(0x1b); bsp = !!(msr.lo & (1 << 8)); return bsp; } static int cpu_init_detected(void) { unsigned long htic; htic = pci_read_config32(( ((( 0 ) & 0xFF) << 16) | ((( 0x18 ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) , 0x6c ); return !!(htic & (1<<6) ); } static int bios_reset_detected(void) { unsigned long htic; htic = pci_read_config32(( ((( 0 ) & 0xFF) << 16) | ((( 0x18 ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) , 0x6c ); return (htic & (1<<4) ) && !(htic & (1<<5) ); } static int cold_reset_detected(void) { unsigned long htic; htic = pci_read_config32(( ((( 0 ) & 0xFF) << 16) | ((( 0x18 ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) , 0x6c ); return !(htic & (1<<4) ); } static void distinguish_cpu_resets(unsigned node_id) { uint32_t htic; device_t device; device = ( ((( 0 ) & 0xFF) << 16) | ((( 0x18 + node_id ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) ; htic = pci_read_config32(device, 0x6c ); htic |= (1<<4) | (1<<5) | (1<<6) ; pci_write_config32(device, 0x6c , htic); } static void set_bios_reset(void) { unsigned long htic; htic = pci_read_config32(( ((( 0 ) & 0xFF) << 16) | ((( 0x18 ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) , 0x6c ); htic &= ~(1<<5) ; pci_write_config32(( ((( 0 ) & 0xFF) << 16) | ((( 0x18 ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) , 0x6c , htic); } static void print_debug_pci_dev(unsigned dev) { print_debug("PCI: "); print_debug_hex8((dev >> 16) & 0xff); print_debug_char(':'); print_debug_hex8((dev >> 11) & 0x1f); print_debug_char('.'); print_debug_hex8((dev >> 8) & 7); } static void print_pci_devices(void) { device_t dev; for(dev = ( ((( 0 ) & 0xFF) << 16) | ((( 0 ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) ; dev <= ( ((( 0 ) & 0xFF) << 16) | ((( 0x1f ) & 0x1f) << 11) | ((( 0x7 ) & 0x7) << 8)) ; dev += ( ((( 0 ) & 0xFF) << 16) | ((( 0 ) & 0x1f) << 11) | ((( 1 ) & 0x7) << 8)) ) { uint32_t id; id = pci_read_config32(dev, 0x00 ); if (((id & 0xffff) == 0x0000) || ((id & 0xffff) == 0xffff) || (((id >> 16) & 0xffff) == 0xffff) || (((id >> 16) & 0xffff) == 0x0000)) { continue; } print_debug_pci_dev(dev); print_debug("\r\n"); } } static void dump_pci_device(unsigned dev) { int i; print_debug_pci_dev(dev); print_debug("\r\n"); for(i = 0; i <= 255; i++) { unsigned char val; if ((i & 0x0f) == 0) { print_debug_hex8(i); print_debug_char(':'); } val = pci_read_config8(dev, i); print_debug_char(' '); print_debug_hex8(val); if ((i & 0x0f) == 0x0f) { print_debug("\r\n"); } } } static void dump_pci_devices(void) { device_t dev; for(dev = ( ((( 0 ) & 0xFF) << 16) | ((( 0 ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) ; dev <= ( ((( 0 ) & 0xFF) << 16) | ((( 0x1f ) & 0x1f) << 11) | ((( 0x7 ) & 0x7) << 8)) ; dev += ( ((( 0 ) & 0xFF) << 16) | ((( 0 ) & 0x1f) << 11) | ((( 1 ) & 0x7) << 8)) ) { uint32_t id; id = pci_read_config32(dev, 0x00 ); if (((id & 0xffff) == 0x0000) || ((id & 0xffff) == 0xffff) || (((id >> 16) & 0xffff) == 0xffff) || (((id >> 16) & 0xffff) == 0x0000)) { continue; } dump_pci_device(dev); } } static void dump_spd_registers(const struct mem_controller *ctrl) { int i; print_debug("\r\n"); for(i = 0; i < 4; i++) { unsigned device; device = ctrl->channel0[i]; if (device) { int j; print_debug("dimm: "); print_debug_hex8(i); print_debug(".0: "); print_debug_hex8(device); for(j = 0; j < 256; j++) { int status; unsigned char byte; if ((j & 0xf) == 0) { print_debug("\r\n"); print_debug_hex8(j); print_debug(": "); } status = smbus_read_byte(device, j); if (status < 0) { print_debug("bad device\r\n"); break; } byte = status & 0xff; print_debug_hex8(byte); print_debug_char(' '); } print_debug("\r\n"); } device = ctrl->channel1[i]; if (device) { int j; print_debug("dimm: "); print_debug_hex8(i); print_debug(".1: "); print_debug_hex8(device); for(j = 0; j < 256; j++) { int status; unsigned char byte; if ((j & 0xf) == 0) { print_debug("\r\n"); print_debug_hex8(j); print_debug(": "); } status = smbus_read_byte(device, j); if (status < 0) { print_debug("bad device\r\n"); break; } byte = status & 0xff; print_debug_hex8(byte); print_debug_char(' '); } print_debug("\r\n"); } } } static unsigned int cpuid(unsigned int op) { unsigned int ret; unsigned dummy2,dummy3,dummy4; asm volatile ( "cpuid" : "=a" (ret), "=b" (dummy2), "=c" (dummy3), "=d" (dummy4) : "a" (op) ); return ret; } static int is_cpu_rev_a0(void) { return (cpuid(1) & 0xffff) == 0x0f10; } static int is_cpu_pre_c0(void) { return (cpuid(1) & 0xffef) < 0x0f48; } static void memreset_setup(void) { if (is_cpu_pre_c0()) { outb((0 << 7)|(0 << 6)|(0<<5)|(0<<4)|(1<<2)|(0<<0), 0x0f00 + 0xc0 + 28); outb((0 << 7)|(0 << 6)|(0<<5)|(0<<4)|(1<<2)|(0<<0), 0x0f00 + 0xc0 + 29); } else { outb((0 << 7)|(0 << 6)|(0<<5)|(0<<4)|(1<<2)|(1<<0), 0x0f00 + 0xc0 + 29); } } static void memreset(int controllers, const struct mem_controller *ctrl) { if (is_cpu_pre_c0()) { udelay(800); outb((0<<7)|(0<<6)|(0<<5)|(0<<4)|(1<<2)|(1<<0), 0x0f00 + 0xc0 + 28); udelay(90); } } static unsigned int generate_row(uint8_t node, uint8_t row, uint8_t maxnodes) { uint32_t ret=0x00010101; static const unsigned int rows_2p[2][2] = { { 0x00050101, 0x00010404 }, { 0x00010404, 0x00050101 } }; if(maxnodes>2) { print_debug("this mainboard is only designed for 2 cpus\r\n"); maxnodes=2; } if (!(node>=maxnodes || row>=maxnodes)) { ret=rows_2p[node][row]; } return ret; } static inline int spd_read_byte(unsigned device, unsigned address) { return smbus_read_byte(device, address); } static void coherent_ht_mainboard(unsigned cpus) { } void cpu_ldtstop(unsigned cpus) { uint32_t tmp; device_t dev; unsigned cnt; for(cnt=0; cntnode_id); print_debug(" northbridge registers\r\n"); max = sizeof(register_values)/sizeof(register_values[0]); for(i = 0; i < max; i += 3) { device_t dev; unsigned where; unsigned long reg; dev = (register_values[i] & ~0xff) - ( ((( 0 ) & 0xFF) << 16) | ((( 0x18 ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) + ctrl->f0; where = register_values[i] & 0xff; reg = pci_read_config32(dev, where); reg &= register_values[i+1]; reg |= register_values[i+2]; pci_write_config32(dev, where, reg); } print_debug("done.\r\n"); } static int is_dual_channel(const struct mem_controller *ctrl) { uint32_t dcl; dcl = pci_read_config32(ctrl->f2, 0x90 ); return dcl & (1<<16) ; } static int is_opteron(const struct mem_controller *ctrl) { uint32_t nbcap; nbcap = pci_read_config32(ctrl->f3, 0xE8 ); return !!(nbcap & 0x0001 ); } static int is_registered(const struct mem_controller *ctrl) { uint32_t dcl; dcl = pci_read_config32(ctrl->f2, 0x90 ); return !(dcl & (1<<18) ); } struct dimm_size { unsigned long side1; unsigned long side2; }; static struct dimm_size spd_get_dimm_size(unsigned device) { struct dimm_size sz; int value, low; sz.side1 = 0; sz.side2 = 0; value = spd_read_byte(device, 3); if (value < 0) goto out; sz.side1 += value & 0xf; value = spd_read_byte(device, 4); if (value < 0) goto out; sz.side1 += value & 0xf; value = spd_read_byte(device, 17); if (value < 0) goto out; sz.side1 += log2(value & 0xff); value = spd_read_byte(device, 7); if (value < 0) goto out; value &= 0xff; value <<= 8; low = spd_read_byte(device, 6); if (low < 0) goto out; value = value | (low & 0xff); sz.side1 += log2(value); value = spd_read_byte(device, 5); if (value <= 1) goto out; sz.side2 = sz.side1; value = spd_read_byte(device, 3); if (value < 0) goto out; if ((value & 0xf0) == 0) goto out; sz.side2 -= (value & 0x0f); sz.side2 += ((value >> 4) & 0x0f); value = spd_read_byte(device, 4); if (value < 0) goto out; sz.side2 -= (value & 0x0f); sz.side2 += ((value >> 4) & 0x0f); out: return sz; } static void set_dimm_size(const struct mem_controller *ctrl, struct dimm_size sz, unsigned index) { uint32_t base0, base1, map; uint32_t dch; if (sz.side1 != sz.side2) { sz.side2 = 0; } map = pci_read_config32(ctrl->f2, 0x80 ); map &= ~(0xf << (index + 4)); base0 = base1 = 0; if (sz.side1 >= (25 +3)) { map |= (sz.side1 - (25 + 3)) << (index *4); base0 = (1 << ((sz.side1 - (25 + 3)) + 21)) | 1; } if (sz.side2 >= (25 + 3)) { base1 = (1 << ((sz.side2 - (25 + 3)) + 21)) | 1; } if (is_dual_channel(ctrl)) { base0 = (base0 << 1) | (base0 & 1); base1 = (base1 << 1) | (base1 & 1); } base0 &= ~0x001ffffe; base1 &= ~0x001ffffe; pci_write_config32(ctrl->f2, 0x40 + (((index << 1)+0)<<2), base0); pci_write_config32(ctrl->f2, 0x40 + (((index << 1)+1)<<2), base1); pci_write_config32(ctrl->f2, 0x80 , map); if (base0) { dch = pci_read_config32(ctrl->f2, 0x94 ); dch |= (1 << 26) << index; pci_write_config32(ctrl->f2, 0x94 , dch); } } static void spd_set_ram_size(const struct mem_controller *ctrl) { int i; for(i = 0; (i < 4) && (ctrl->channel0[i]); i++) { struct dimm_size sz; sz = spd_get_dimm_size(ctrl->channel0[i]); set_dimm_size(ctrl, sz, i); } } static void route_dram_accesses(const struct mem_controller *ctrl, unsigned long base_k, unsigned long limit_k) { unsigned node_id; unsigned limit; unsigned base; unsigned index; unsigned limit_reg, base_reg; device_t device; node_id = ctrl->node_id; index = (node_id << 3); limit = (limit_k << 2); limit &= 0xffff0000; limit -= 0x00010000; limit |= ( 0 << 8) | (node_id << 0); base = (base_k << 2); base &= 0xffff0000; base |= (0 << 8) | (1<<1) | (1<<0); limit_reg = 0x44 + index; base_reg = 0x40 + index; for(device = ( ((( 0 ) & 0xFF) << 16) | ((( 0x18 ) & 0x1f) << 11) | ((( 1 ) & 0x7) << 8)) ; device <= ( ((( 0 ) & 0xFF) << 16) | ((( 0x1f ) & 0x1f) << 11) | ((( 1 ) & 0x7) << 8)) ; device += ( ((( 0 ) & 0xFF) << 16) | ((( 1 ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) ) { pci_write_config32(device, limit_reg, limit); pci_write_config32(device, base_reg, base); } } static void set_top_mem(unsigned tom_k) { if (!tom_k) { set_bios_reset(); print_debug("No memory - reset"); pci_write_config8(( ((( 0 ) & 0xFF) << 16) | ((( 0x04 ) & 0x1f) << 11) | ((( 3 ) & 0x7) << 8)) , 0x41, 0xf1); outb(0x0e, 0x0cf9); } print_debug("RAM: 0x"); print_debug_hex32(tom_k); print_debug(" KB\r\n"); msr_t msr; msr.lo = (tom_k & 0x003fffff) << 10; msr.hi = (tom_k & 0xffc00000) >> 22; wrmsr(0xC001001D , msr); if (tom_k >= 0x003f0000) { tom_k = 0x3f0000; } msr.lo = (tom_k & 0x003fffff) << 10; msr.hi = (tom_k & 0xffc00000) >> 22; wrmsr(0xC001001A , msr); } static unsigned long interleave_chip_selects(const struct mem_controller *ctrl) { static const uint32_t csbase_low[] = { (1 << (13 - 4)), (1 << (14 - 4)), (1 << (14 - 4)), (1 << (15 - 4)), (1 << (15 - 4)), (1 << (16 - 4)), (1 << (16 - 4)), }; uint32_t csbase_inc; int chip_selects, index; int bits; int dual_channel; unsigned common_size; uint32_t csbase, csmask; chip_selects = 0; common_size = 0; for(index = 0; index < 8; index++) { unsigned size; uint32_t value; value = pci_read_config32(ctrl->f2, 0x40 + (index << 2)); if (!(value & 1)) { continue; } chip_selects++; size = value >> 21; if (common_size == 0) { common_size = size; } if (common_size != size) { return 0; } } bits = log2(chip_selects); if (((1 << bits) != chip_selects) || (bits < 1) || (bits > 3)) { return 0; } if ((bits == 3) && (common_size == (1 << (32 - 3)))) { print_debug("8 4GB chip selects cannot be interleaved\r\n"); return 0; } if (is_dual_channel(ctrl)) { csbase_inc = csbase_low[log2(common_size) - 1] << 1; } else { csbase_inc = csbase_low[log2(common_size)]; } csbase = 0 | 1; csmask = (((common_size << bits) - 1) << 21); csmask |= 0xfe00 & ~((csbase_inc << bits) - csbase_inc); for(index = 0; index < 8; index++) { uint32_t value; value = pci_read_config32(ctrl->f2, 0x40 + (index << 2)); if (!(value & 1)) { continue; } pci_write_config32(ctrl->f2, 0x40 + (index << 2), csbase); pci_write_config32(ctrl->f2, 0x60 + (index << 2), csmask); csbase += csbase_inc; } print_debug("Interleaved\r\n"); return common_size << (15 + bits); } static unsigned long order_chip_selects(const struct mem_controller *ctrl) { unsigned long tom; tom = 0; for(;;) { unsigned index, canidate; uint32_t csbase, csmask; unsigned size; csbase = 0; canidate = 0; for(index = 0; index < 8; index++) { uint32_t value; value = pci_read_config32(ctrl->f2, 0x40 + (index << 2)); if (!(value & 1)) { continue; } if (value <= csbase) { continue; } if (tom & (1 << (index + 24))) { continue; } csbase = value; canidate = index; } if (csbase == 0) { break; } size = csbase >> 21; tom |= (1 << (canidate + 24)); csbase = (tom << 21) | 1; tom += size; csmask = ((size -1) << 21); csmask |= 0xfe00; pci_write_config32(ctrl->f2, 0x40 + (canidate << 2), csbase); pci_write_config32(ctrl->f2, 0x60 + (canidate << 2), csmask); } return (tom & ~0xff000000) << 15; } static void order_dimms(const struct mem_controller *ctrl) { unsigned long tom, tom_k, base_k; unsigned node_id; tom_k = interleave_chip_selects(ctrl); if (!tom_k) { tom_k = order_chip_selects(ctrl); } base_k = 0; for(node_id = 0; node_id < ctrl->node_id; node_id++) { uint32_t limit, base; unsigned index; index = node_id << 3; base = pci_read_config32(ctrl->f1, 0x40 + index); if ((base & 3) == 3) { limit = pci_read_config32(ctrl->f1, 0x44 + index); base_k = ((limit + 0x00010000) & 0xffff0000) >> 2; } } tom_k += base_k; route_dram_accesses(ctrl, base_k, tom_k); set_top_mem(tom_k); } static void disable_dimm(const struct mem_controller *ctrl, unsigned index) { print_debug("disabling dimm"); print_debug_hex8(index); print_debug("\r\n"); pci_write_config32(ctrl->f2, 0x40 + (((index << 1)+0)<<2), 0); pci_write_config32(ctrl->f2, 0x40 + (((index << 1)+1)<<2), 0); } static void spd_handle_unbuffered_dimms(const struct mem_controller *ctrl) { int i; int registered; int unbuffered; uint32_t dcl; unbuffered = 0; registered = 0; for(i = 0; (i < 4) && (ctrl->channel0[i]); i++) { int value; value = spd_read_byte(ctrl->channel0[i], 21); if (value < 0) { disable_dimm(ctrl, i); continue; } if (value & (1 << 1)) { registered = 1; } else { unbuffered = 1; } } if (unbuffered && registered) { die("Mixed buffered and registered dimms not supported"); } if (unbuffered && is_opteron(ctrl)) { die("Unbuffered Dimms not supported on Opteron"); } dcl = pci_read_config32(ctrl->f2, 0x90 ); dcl &= ~(1<<18) ; if (unbuffered) { dcl |= (1<<18) ; } pci_write_config32(ctrl->f2, 0x90 , dcl); } static void spd_enable_2channels(const struct mem_controller *ctrl) { int i; uint32_t nbcap; static const unsigned addresses[] = { 2, 3, 4, 5, 6, 7, 9, 11, 13, 17, 18, 21, 23, 26, 27, 28, 29, 30, 41, 42, }; nbcap = pci_read_config32(ctrl->f3, 0xE8 ); if (!(nbcap & 0x0001 )) { return; } for(i = 0; (i < 4) && (ctrl->channel0[i]); i++) { unsigned device0, device1; int value0, value1; int j; device0 = ctrl->channel0[i]; device1 = ctrl->channel1[i]; if (!device1) return; for(j = 0; j < sizeof(addresses)/sizeof(addresses[0]); j++) { unsigned addr; addr = addresses[j]; value0 = spd_read_byte(device0, addr); if (value0 < 0) { break; } value1 = spd_read_byte(device1, addr); if (value1 < 0) { return; } if (value0 != value1) { return; } } } print_debug("Enabling dual channel memory\r\n"); uint32_t dcl; dcl = pci_read_config32(ctrl->f2, 0x90 ); dcl &= ~(1<<19) ; dcl |= (1<<16) ; pci_write_config32(ctrl->f2, 0x90 , dcl); } struct mem_param { uint8_t cycle_time; uint8_t divisor; uint8_t tRC; uint8_t tRFC; uint32_t dch_memclk; uint16_t dch_tref4k, dch_tref8k; uint8_t dtl_twr; char name[9]; }; static const struct mem_param *get_mem_param(unsigned min_cycle_time) { static const struct mem_param speed[] = { { .name = "100Mhz\r\n", .cycle_time = 0xa0, .divisor = (10 <<1), .tRC = 0x46, .tRFC = 0x50, .dch_memclk = 0 << 20 , .dch_tref4k = 0x00 , .dch_tref8k = 0x08 , .dtl_twr = 2, }, { .name = "133Mhz\r\n", .cycle_time = 0x75, .divisor = (7<<1)+1, .tRC = 0x41, .tRFC = 0x4B, .dch_memclk = 2 << 20 , .dch_tref4k = 0x01 , .dch_tref8k = 0x09 , .dtl_twr = 2, }, { .name = "166Mhz\r\n", .cycle_time = 0x60, .divisor = (6<<1), .tRC = 0x3C, .tRFC = 0x48, .dch_memclk = 5 << 20 , .dch_tref4k = 0x02 , .dch_tref8k = 0x0A , .dtl_twr = 3, }, { .name = "200Mhz\r\n", .cycle_time = 0x50, .divisor = (5<<1), .tRC = 0x37, .tRFC = 0x46, .dch_memclk = 7 << 20 , .dch_tref4k = 0x03 , .dch_tref8k = 0x0B , .dtl_twr = 3, }, { .cycle_time = 0x00, }, }; const struct mem_param *param; for(param = &speed[0]; param->cycle_time ; param++) { if (min_cycle_time > (param+1)->cycle_time) { break; } } if (!param->cycle_time) { die("min_cycle_time to low"); } print_debug(param->name); return param; } static const struct mem_param *spd_set_memclk(const struct mem_controller *ctrl) { const struct mem_param *param; unsigned min_cycle_time, min_latency; int i; uint32_t value; static const int latency_indicies[] = { 26, 23, 9 }; static const unsigned char min_cycle_times[] = { [0 ] = 0x50, [1 ] = 0x60, [2 ] = 0x75, [3 ] = 0xa0, }; value = pci_read_config32(ctrl->f3, 0xE8 ); min_cycle_time = min_cycle_times[(value >> 5 ) & 3 ]; min_latency = 2; for(i = 0; (i < 4) && (ctrl->channel0[i]); i++) { int new_cycle_time, new_latency; int index; int latencies; int latency; new_cycle_time = 0xa0; new_latency = 5; latencies = spd_read_byte(ctrl->channel0[i], 18); if (latencies <= 0) continue; latency = log2(latencies) -2; for(index = 0; index < 3; index++, latency++) { int value; if ((latency < 2) || (latency > 4) || (!(latencies & (1 << latency)))) { continue; } value = spd_read_byte(ctrl->channel0[i], latency_indicies[index]); if (value < 0) { continue; } if ((value >= min_cycle_time) && (value < new_cycle_time)) { new_cycle_time = value; new_latency = latency; } } if (new_latency > 4){ continue; } if (new_cycle_time > min_cycle_time) { min_cycle_time = new_cycle_time; } if (new_latency > min_latency) { min_latency = new_latency; } } for(i = 0; (i < 4) && (ctrl->channel0[i]); i++) { int latencies; int latency; int index; int value; int dimm; latencies = spd_read_byte(ctrl->channel0[i], 18); if (latencies <= 0) { goto dimm_err; } latency = log2(latencies) -2; for(index = 0; index < 3; index++, latency++) { if (!(latencies & (1 << latency))) { continue; } if (latency == min_latency) break; } if ((latency != min_latency) || (index >= 3)) { goto dimm_err; } value = spd_read_byte(ctrl->channel0[i], latency_indicies[index]); if (value <= min_cycle_time) { continue; } dimm_err: disable_dimm(ctrl, i); } param = get_mem_param(min_cycle_time); value = pci_read_config32(ctrl->f2, 0x94 ); value &= ~(0x7 << 20 ); value |= param->dch_memclk; pci_write_config32(ctrl->f2, 0x94 , value); static const unsigned latencies[] = { 1 , 5 , 2 }; value = pci_read_config32(ctrl->f2, 0x88 ); value &= ~(0x7 << 0 ); value |= latencies[min_latency - 2] << 0 ; pci_write_config32(ctrl->f2, 0x88 , value); return param; } static int update_dimm_Trc(const struct mem_controller *ctrl, const struct mem_param *param, int i) { unsigned clocks, old_clocks; uint32_t dtl; int value; value = spd_read_byte(ctrl->channel0[i], 41); if (value < 0) return -1; if ((value == 0) || (value == 0xff)) { value = param->tRC; } clocks = ((value << 1) + param->divisor - 1)/param->divisor; if (clocks < 7 ) { clocks = 7 ; } if (clocks > 22 ) { return -1; } dtl = pci_read_config32(ctrl->f2, 0x88 ); old_clocks = ((dtl >> 4 ) & 0xf ) + 7 ; if (old_clocks > clocks) { clocks = old_clocks; } dtl &= ~(0xf << 4 ); dtl |= ((clocks - 7 ) << 4 ); pci_write_config32(ctrl->f2, 0x88 , dtl); return 0; } static int update_dimm_Trfc(const struct mem_controller *ctrl, const struct mem_param *param, int i) { unsigned clocks, old_clocks; uint32_t dtl; int value; value = spd_read_byte(ctrl->channel0[i], 42); if (value < 0) return -1; if ((value == 0) || (value == 0xff)) { value = param->tRFC; } clocks = ((value << 1) + param->divisor - 1)/param->divisor; if (clocks < 9 ) { clocks = 9 ; } if (clocks > 24 ) { return -1; } dtl = pci_read_config32(ctrl->f2, 0x88 ); old_clocks = ((dtl >> 8 ) & 0xf ) + 9 ; if (old_clocks > clocks) { clocks = old_clocks; } dtl &= ~(0xf << 8 ); dtl |= ((clocks - 9 ) << 8 ); pci_write_config32(ctrl->f2, 0x88 , dtl); return 0; } static int update_dimm_Trcd(const struct mem_controller *ctrl, const struct mem_param *param, int i) { unsigned clocks, old_clocks; uint32_t dtl; int value; value = spd_read_byte(ctrl->channel0[i], 29); if (value < 0) return -1; clocks = (value + ((param->divisor & 0xff) << 1) -1)/((param->divisor & 0xff) << 1); if (clocks < 2 ) { clocks = 2 ; } if (clocks > 6 ) { return -1; } dtl = pci_read_config32(ctrl->f2, 0x88 ); old_clocks = ((dtl >> 12 ) & 0x7 ) + 0 ; if (old_clocks > clocks) { clocks = old_clocks; } dtl &= ~(0x7 << 12 ); dtl |= ((clocks - 0 ) << 12 ); pci_write_config32(ctrl->f2, 0x88 , dtl); return 0; } static int update_dimm_Trrd(const struct mem_controller *ctrl, const struct mem_param *param, int i) { unsigned clocks, old_clocks; uint32_t dtl; int value; value = spd_read_byte(ctrl->channel0[i], 28); if (value < 0) return -1; clocks = (value + ((param->divisor & 0xff) << 1) -1)/((param->divisor & 0xff) << 1); if (clocks < 2 ) { clocks = 2 ; } if (clocks > 4 ) { return -1; } dtl = pci_read_config32(ctrl->f2, 0x88 ); old_clocks = ((dtl >> 16 ) & 0x7 ) + 0 ; if (old_clocks > clocks) { clocks = old_clocks; } dtl &= ~(0x7 << 16 ); dtl |= ((clocks - 0 ) << 16 ); pci_write_config32(ctrl->f2, 0x88 , dtl); return 0; } static int update_dimm_Tras(const struct mem_controller *ctrl, const struct mem_param *param, int i) { unsigned clocks, old_clocks; uint32_t dtl; int value; value = spd_read_byte(ctrl->channel0[i], 30); if (value < 0) return -1; clocks = ((value << 1) + param->divisor - 1)/param->divisor; if (clocks < 5 ) { clocks = 5 ; } if (clocks > 15 ) { return -1; } dtl = pci_read_config32(ctrl->f2, 0x88 ); old_clocks = ((dtl >> 20 ) & 0xf ) + 0 ; if (old_clocks > clocks) { clocks = old_clocks; } dtl &= ~(0xf << 20 ); dtl |= ((clocks - 0 ) << 20 ); pci_write_config32(ctrl->f2, 0x88 , dtl); return 0; } static int update_dimm_Trp(const struct mem_controller *ctrl, const struct mem_param *param, int i) { unsigned clocks, old_clocks; uint32_t dtl; int value; value = spd_read_byte(ctrl->channel0[i], 27); if (value < 0) return -1; clocks = (value + ((param->divisor & 0xff) << 1) - 1)/((param->divisor & 0xff) << 1); if (clocks < 2 ) { clocks = 2 ; } if (clocks > 6 ) { return -1; } dtl = pci_read_config32(ctrl->f2, 0x88 ); old_clocks = ((dtl >> 24 ) & 0x7 ) + 0 ; if (old_clocks > clocks) { clocks = old_clocks; } dtl &= ~(0x7 << 24 ); dtl |= ((clocks - 0 ) << 24 ); pci_write_config32(ctrl->f2, 0x88 , dtl); return 0; } static void set_Twr(const struct mem_controller *ctrl, const struct mem_param *param) { uint32_t dtl; dtl = pci_read_config32(ctrl->f2, 0x88 ); dtl &= ~(0x1 << 28 ); dtl |= (param->dtl_twr - 2 ) << 28 ; pci_write_config32(ctrl->f2, 0x88 , dtl); } static void init_Tref(const struct mem_controller *ctrl, const struct mem_param *param) { uint32_t dth; dth = pci_read_config32(ctrl->f2, 0x8c ); dth &= ~(0x1f << 8 ); dth |= (param->dch_tref4k << 8 ); pci_write_config32(ctrl->f2, 0x8c , dth); } static int update_dimm_Tref(const struct mem_controller *ctrl, const struct mem_param *param, int i) { uint32_t dth; int value; unsigned tref, old_tref; value = spd_read_byte(ctrl->channel0[i], 3); if (value < 0) return -1; value &= 0xf; tref = param->dch_tref8k; if (value == 12) { tref = param->dch_tref4k; } dth = pci_read_config32(ctrl->f2, 0x8c ); old_tref = (dth >> 8 ) & 0x1f ; if ((value == 12) && (old_tref == param->dch_tref4k)) { tref = param->dch_tref4k; } else { tref = param->dch_tref8k; } dth &= ~(0x1f << 8 ); dth |= (tref << 8 ); pci_write_config32(ctrl->f2, 0x8c , dth); return 0; } static int update_dimm_x4(const struct mem_controller *ctrl, const struct mem_param *param, int i) { uint32_t dcl; int value; int dimm; value = spd_read_byte(ctrl->channel0[i], 13); if (value < 0) { return -1; } dimm = i; dimm += 20 ; dcl = pci_read_config32(ctrl->f2, 0x90 ); dcl &= ~(1 << dimm); if (value == 4) { dcl |= (1 << dimm); } pci_write_config32(ctrl->f2, 0x90 , dcl); return 0; } static int update_dimm_ecc(const struct mem_controller *ctrl, const struct mem_param *param, int i) { uint32_t dcl; int value; value = spd_read_byte(ctrl->channel0[i], 11); if (value < 0) { return -1; } if (value != 2) { dcl = pci_read_config32(ctrl->f2, 0x90 ); dcl &= ~(1<<17) ; pci_write_config32(ctrl->f2, 0x90 , dcl); } return 0; } static int count_dimms(const struct mem_controller *ctrl) { int dimms; unsigned index; dimms = 0; for(index = 0; index < 8; index += 2) { uint32_t csbase; csbase = pci_read_config32(ctrl->f2, (0x40 + index << 2)); if (csbase & 1) { dimms += 1; } } return dimms; } static void set_Twtr(const struct mem_controller *ctrl, const struct mem_param *param) { uint32_t dth; unsigned clocks; clocks = 1; dth = pci_read_config32(ctrl->f2, 0x8c ); dth &= ~(0x1 << 0 ); dth |= ((clocks - 1 ) << 0 ); pci_write_config32(ctrl->f2, 0x8c , dth); } static void set_Trwt(const struct mem_controller *ctrl, const struct mem_param *param) { uint32_t dth, dtl; unsigned divisor; unsigned latency; unsigned clocks; clocks = 0; dtl = pci_read_config32(ctrl->f2, 0x88 ); latency = (dtl >> 0 ) & 0x7 ; divisor = param->divisor; if (is_opteron(ctrl)) { if (latency == 1 ) { if (divisor == ((6 << 0) + 0)) { clocks = 3; } else if (divisor > ((6 << 0)+0)) { clocks = 2; } } else if (latency == 5 ) { clocks = 3; } else if (latency == 2 ) { if (divisor == ((6 << 0)+0)) { clocks = 4; } else if (divisor > ((6 << 0)+0)) { clocks = 3; } } } else { if (is_registered(ctrl)) { if (latency == 1 ) { clocks = 2; } else if (latency == 5 ) { clocks = 3; } else if (latency == 2 ) { clocks = 3; } } else { if (latency == 1 ) { clocks = 3; } else if (latency == 5 ) { clocks = 4; } else if (latency == 2 ) { clocks = 4; } } } if ((clocks < 1 ) || (clocks > 6 )) { die("Unknown Trwt"); } dth = pci_read_config32(ctrl->f2, 0x8c ); dth &= ~(0x7 << 4 ); dth |= ((clocks - 1 ) << 4 ); pci_write_config32(ctrl->f2, 0x8c , dth); return; } static void set_Twcl(const struct mem_controller *ctrl, const struct mem_param *param) { uint32_t dth; unsigned clocks; if (is_registered(ctrl)) { clocks = 2; } else { clocks = 1; } dth = pci_read_config32(ctrl->f2, 0x8c ); dth &= ~(0x7 << 20 ); dth |= ((clocks - 1 ) << 20 ); pci_write_config32(ctrl->f2, 0x8c , dth); } static void set_read_preamble(const struct mem_controller *ctrl, const struct mem_param *param) { uint32_t dch; unsigned divisor; unsigned rdpreamble; divisor = param->divisor; dch = pci_read_config32(ctrl->f2, 0x94 ); dch &= ~(0xf << 8 ); rdpreamble = 0; if (is_registered(ctrl)) { if (divisor == ((10 << 1)+0)) { rdpreamble = ((9 << 1)+ 0); } else if (divisor == ((7 << 1)+1)) { rdpreamble = ((8 << 1)+0); } else if (divisor == ((6 << 1)+0)) { rdpreamble = ((7 << 1)+1); } else if (divisor == ((5 << 1)+0)) { rdpreamble = ((7 << 1)+0); } } else { int slots; int i; slots = 0; for(i = 0; i < 4; i++) { if (ctrl->channel0[i]) { slots += 1; } } if (divisor == ((10 << 1)+0)) { if (slots <= 2) { rdpreamble = ((9 << 1)+0); } else { rdpreamble = ((14 << 1)+0); } } else if (divisor == ((7 << 1)+1)) { if (slots <= 2) { rdpreamble = ((7 << 1)+0); } else { rdpreamble = ((11 << 1)+0); } } else if (divisor == ((6 << 1)+0)) { if (slots <= 2) { rdpreamble = ((7 << 1)+0); } else { rdpreamble = ((9 << 1)+0); } } else if (divisor == ((5 << 1)+0)) { if (slots <= 2) { rdpreamble = ((5 << 1)+0); } else { rdpreamble = ((7 << 1)+0); } } } if ((rdpreamble < ((2<<1)+0) ) || (rdpreamble > ((9<<1)+1) )) { die("Unknown rdpreamble"); } dch |= (rdpreamble - ((2<<1)+0) ) << 8 ; pci_write_config32(ctrl->f2, 0x94 , dch); } static void set_max_async_latency(const struct mem_controller *ctrl, const struct mem_param *param) { uint32_t dch; int i; unsigned async_lat; int dimms; dimms = count_dimms(ctrl); dch = pci_read_config32(ctrl->f2, 0x94 ); dch &= ~(0xf << 0 ); async_lat = 0; if (is_registered(ctrl)) { if (dimms == 4) { async_lat = 9; } else { async_lat = 8; } } else { if (dimms > 3) { die("Too many unbuffered dimms"); } else if (dimms == 3) { async_lat = 7; } else { async_lat = 6; } } dch |= ((async_lat - 0 ) << 0 ); pci_write_config32(ctrl->f2, 0x94 , dch); } static void set_idle_cycle_limit(const struct mem_controller *ctrl, const struct mem_param *param) { uint32_t dch; dch = pci_read_config32(ctrl->f2, 0x94 ); dch &= ~(0x7 << 16 ); dch |= 3 << 16 ; dch |= (1 << 19) ; pci_write_config32(ctrl->f2, 0x94 , dch); } static void spd_set_dram_timing(const struct mem_controller *ctrl, const struct mem_param *param) { int dimms; int i; int rc; init_Tref(ctrl, param); for(i = 0; (i < 4) && ctrl->channel0[i]; i++) { int rc; if (update_dimm_Trc (ctrl, param, i) < 0) goto dimm_err; if (update_dimm_Trfc(ctrl, param, i) < 0) goto dimm_err; if (update_dimm_Trcd(ctrl, param, i) < 0) goto dimm_err; if (update_dimm_Trrd(ctrl, param, i) < 0) goto dimm_err; if (update_dimm_Tras(ctrl, param, i) < 0) goto dimm_err; if (update_dimm_Trp (ctrl, param, i) < 0) goto dimm_err; if (update_dimm_Tref(ctrl, param, i) < 0) goto dimm_err; if (update_dimm_x4 (ctrl, param, i) < 0) goto dimm_err; if (update_dimm_ecc(ctrl, param, i) < 0) goto dimm_err; continue; dimm_err: disable_dimm(ctrl, i); } set_Twr(ctrl, param); set_Twtr(ctrl, param); set_Trwt(ctrl, param); set_Twcl(ctrl, param); set_read_preamble(ctrl, param); set_max_async_latency(ctrl, param); set_idle_cycle_limit(ctrl, param); } static void sdram_set_spd_registers(const struct mem_controller *ctrl) { const struct mem_param *param; spd_enable_2channels(ctrl); spd_set_ram_size(ctrl); spd_handle_unbuffered_dimms(ctrl); param = spd_set_memclk(ctrl); spd_set_dram_timing(ctrl, param); order_dimms(ctrl); } static void sdram_enable(int controllers, const struct mem_controller *ctrl) { int i; for(i = 0; i < controllers; i++) { uint32_t dch; dch = pci_read_config32(ctrl[i].f2, 0x94 ); dch |= (1 << 25) ; pci_write_config32(ctrl[i].f2, 0x94 , dch); } memreset(controllers, ctrl); for(i = 0; i < controllers; i++) { uint32_t dcl; dcl = pci_read_config32(ctrl[i].f2, 0x90 ); if (dcl & (1<<17) ) { uint32_t mnc; print_debug("ECC enabled\r\n"); mnc = pci_read_config32(ctrl[i].f3, 0x44 ); mnc |= (1 << 22) ; if (dcl & (1<<16) ) { mnc |= (1 << 23) ; } pci_write_config32(ctrl[i].f3, 0x44 , mnc); } dcl |= (1<<3) ; pci_write_config32(ctrl[i].f2, 0x90 , dcl); dcl &= ~(1<<3) ; dcl &= ~(1<<0) ; dcl &= ~(1<<1) ; dcl &= ~(1<<2) ; dcl |= (1<<8) ; pci_write_config32(ctrl[i].f2, 0x90 , dcl); } for(i = 0; i < controllers; i++) { uint32_t dcl; print_debug("Initializing memory: "); int loops = 0; do { dcl = pci_read_config32(ctrl[i].f2, 0x90 ); loops += 1; if ((loops & 1023) == 0) { print_debug("."); } } while(((dcl & (1<<8) ) != 0) && (loops < 300000 )); if (loops >= 300000 ) { print_debug(" failed\r\n"); } else { print_debug(" done\r\n"); } if (dcl & (1<<17) ) { print_debug("Clearing memory: "); if (!is_cpu_pre_c0()) { dcl &= ~((1<<11) | (1<<10) ); pci_write_config32(ctrl[i].f2, 0x90 , dcl); do { dcl = pci_read_config32(ctrl[i].f2, 0x90 ); } while(((dcl & (1<<11) ) == 0) || ((dcl & (1<<10) ) == 0) ); } uint32_t base, last_scrub_k, scrub_k; uint32_t cnt,zstart,zend; msr_t msr,msr_201; pci_write_config32(ctrl[i].f3, 0x58 , (0 << 16) | (0 << 8) | (0 << 0)); msr_201 = rdmsr(0x201); zstart = pci_read_config32(ctrl[0].f1, 0x40 + (i*8)); zend = pci_read_config32(ctrl[0].f1, 0x44 + (i*8)); zstart >>= 16; zend >>=16; print_debug("addr "); print_debug_hex32(zstart); print_debug("-"); print_debug_hex32(zend); print_debug("\r\n"); msr = rdmsr(0x2ff ); msr.lo &= ~(1<<10); wrmsr(0x2ff , msr); msr = rdmsr(0xc0010015); msr.lo |= (1<<17); wrmsr(0xc0010015,msr); for(;zstart>8; wrmsr(0x200,msr); msr.hi = 0x000000ff; msr.lo = 0xfc000800; wrmsr(0x201,msr); __asm__ volatile( "movl %%cr0, %0\n\t" "andl $0x9fffffff, %0\n\t" "movl %0, %%cr0\n\t" :"=r" (cnt) ); msr.lo = (zstart&0xff) << 24; msr.hi = (zstart&0xff00) >> 8; wrmsr(0xc0000100,msr); print_debug_char((zstart > 0x0ff)?'+':'-'); __asm__ volatile( "1: \n\t" "movl %0, %%fs:(%1)\n\t" "addl $4,%1\n\t" "subl $1,%2\n\t" "jnz 1b\n\t" : : "a" (0), "D" (0), "c" (0x01000000) ); } __asm__ volatile( "movl %%cr0, %0\n\t" "orl $0x40000000, %0\n\t" "movl %0, %%cr0\n\t" :"=r" (cnt) ); msr = rdmsr(0x2ff ); msr.lo |= 0x0400; wrmsr(0x2ff , msr); msr.lo = 6; msr.hi = 0; wrmsr(0x200,msr); wrmsr(0x201,msr_201); msr.lo = 0; msr.hi = 0; wrmsr(0xc0000100,msr); __asm__ volatile( "movl %%cr0, %0\n\t" "andl $0x9fffffff, %0\n\t" "movl %0, %%cr0\n\t" :"=r" (cnt) ); msr = rdmsr(0xc0010015); msr.lo &= ~(1<<17); wrmsr(0xc0010015,msr); base = pci_read_config32(ctrl[i].f1, 0x40 + (ctrl[i].node_id << 3)); base &= 0xffff0000; pci_write_config32(ctrl[i].f3, 0x5C , base << 8); pci_write_config32(ctrl[i].f3, 0x60 , base >> 24); pci_write_config32(ctrl[i].f3, 0x58 , (22 << 16) | (22 << 8) | (22 << 0)); print_debug("done\r\n"); } } } typedef uint8_t u8; typedef uint32_t u32; typedef int8_t bool; static void disable_probes(void) { u32 val; print_debug("Disabling read/write/fill probes for UP... "); val=pci_read_config32(( ((( 0 ) & 0xFF) << 16) | ((( 24+ 0 ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) , 0x68); val |= (1<<10)|(1<<9)|(1<<8)|(1<<4)|(1<<3)|(1<<2)|(1<<1)|(1 << 0); pci_write_config32(( ((( 0 ) & 0xFF) << 16) | ((( 24+ 0 ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) , 0x68, val); print_debug("done.\r\n"); } static void wait_ap_stop(u8 node) { unsigned long reg; unsigned long i; for(i=0;i< 1000 ;i++) { unsigned long regx; regx = pci_read_config32(( ((( 0 ) & 0xFF) << 16) | ((( 24+ node ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) ,0x6c); if((regx & (1<<4))==1) break; } reg = pci_read_config32(( ((( 0 ) & 0xFF) << 16) | ((( 24+ node ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) ,0x6c); reg &= ~(1<<4); pci_write_config32(( ((( 0 ) & 0xFF) << 16) | ((( 24+ node ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) , 0x6c, reg); } static void notify_bsp_ap_is_stopped(void) { unsigned long reg; unsigned long apic_id; apic_id = *((volatile unsigned long *)(0xfee00000 + 0x020 )); apic_id >>= 24; if(apic_id != 0) { reg = pci_read_config32(( ((( 0 ) & 0xFF) << 16) | ((( 24+ apic_id ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) , 0x6C); reg |= 1<<4; pci_write_config32(( ((( 0 ) & 0xFF) << 16) | ((( 24+ apic_id ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) , 0x6C, reg); } } static void enable_routing(u8 node) { u32 val; print_debug("Enabling routing table for node "); print_debug_hex32(node); val=pci_read_config32(( ((( 0 ) & 0xFF) << 16) | ((( 24+ node ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) , 0x6c); val &= ~((1<<6)|(1<<5)|(1<<4)|(1<<1)|(1<<0)); pci_write_config32(( ((( 0 ) & 0xFF) << 16) | ((( 24+ node ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) , 0x6c, val); if(node!=0) { wait_ap_stop(node); } print_debug(" done.\r\n"); } static void rename_temp_node(u8 node) { uint32_t val; print_debug("Renaming current temp node to "); print_debug_hex32(node); val=pci_read_config32(( ((( 0 ) & 0xFF) << 16) | ((( 24+ 7 ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) , 0x60); val &= (~7); val |= node; pci_write_config32(( ((( 0 ) & 0xFF) << 16) | ((( 24+ 7 ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) , 0x60, val); print_debug(" done.\r\n"); } static bool check_connection(u8 src, u8 dest, u8 link) { u32 val; val=pci_read_config32(( ((( 0 ) & 0xFF) << 16) | ((( 24+ src ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) , 0x98+link); if ( (val&0x17) != 0x03) return 0; val=pci_read_config32(( ((( 0 ) & 0xFF) << 16) | ((( 24+ dest ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) ,0); if(val != 0x11001022) return 0; return 1; } static void optimize_connection(u8 node1, u8 link1, u8 node2, u8 link2) { static const uint8_t link_width_to_pow2[]= { 3, 4, 0, 5, 1, 2, 0, 0 }; static const uint8_t pow2_to_link_width[] = { 0x7, 4, 5, 0, 1, 3 }; uint16_t freq_cap1, freq_cap2, freq_cap, freq_mask; uint8_t width_cap1, width_cap2, width_cap, width, ln_width1, ln_width2; uint8_t freq; freq_cap1 = pci_read_config16(( ((( 0 ) & 0xFF) << 16) | ((( 24+ node1 ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) , 0x80 + link1 + 0x0a ); freq_cap2 = pci_read_config16(( ((( 0 ) & 0xFF) << 16) | ((( 24+ node2 ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) , 0x80 + link2 + 0x0a ); freq = log2(freq_cap1 & freq_cap2 & 0xff); pci_write_config8(( ((( 0 ) & 0xFF) << 16) | ((( 24+ node1 ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) , 0x80 + link1 + 0x09 , freq); pci_write_config8(( ((( 0 ) & 0xFF) << 16) | ((( 24+ node2 ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) , 0x80 + link2 + 0x09 , freq); width_cap1 = pci_read_config8(( ((( 0 ) & 0xFF) << 16) | ((( 24+ node1 ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) , 0x80 + link1 + 6 ); width_cap2 = pci_read_config8(( ((( 0 ) & 0xFF) << 16) | ((( 24+ node2 ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) , 0x80 + link2 + 6 ); ln_width1 = link_width_to_pow2[width_cap1 & 7]; ln_width2 = link_width_to_pow2[(width_cap2 >> 4) & 7]; if (ln_width1 > ln_width2) { ln_width1 = ln_width2; } width = pow2_to_link_width[ln_width1]; ln_width1 = link_width_to_pow2[(width_cap1 >> 4) & 7]; ln_width2 = link_width_to_pow2[width_cap2 & 7]; if (ln_width1 > ln_width2) { ln_width1 = ln_width2; } width |= pow2_to_link_width[ln_width1] << 4; pci_write_config8(( ((( 0 ) & 0xFF) << 16) | ((( 24+ node1 ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) , 0x80 + link1 + 6 + 1, width); width = ((width & 0x70) >> 4) | ((width & 0x7) << 4); pci_write_config8(( ((( 0 ) & 0xFF) << 16) | ((( 24+ node2 ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) , 0x80 + link2 + 6 + 1, width); } static void fill_row(u8 node, u8 row, u32 value) { pci_write_config32(( ((( 0 ) & 0xFF) << 16) | ((( 24+ node ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) , 0x40+(row<<2), value); } static void setup_row(u8 source, u8 dest, u8 cpus) { fill_row(source,dest,generate_row(source,dest,cpus)); } static void setup_temp_row(u8 source, u8 dest, u8 cpus) { fill_row(source,7,((generate_row( source,dest,cpus )&(~0x0f0000))|0x010000) ); } static void setup_node(u8 node, u8 cpus) { u8 row; for(row=0; row2) mask=0x06; else mask=0x02; for (node=0; node0; node--) for (row=cpus; row>0; row--) fill_row(( ((( 0 ) & 0xFF) << 16) | ((( 24+ node-1 ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) , row-1, 0x00010101 ); return setup_uniprocessor(); } static void coherent_ht_finalize(unsigned cpus) { int node; bool rev_a0; print_debug("coherent_ht_finalize\r\n"); rev_a0= is_cpu_rev_a0(); for (node=0; node> 24; apic_write(0x310 , (( apicid )<<24) ); apic_write(0x300 , 0x08000 | 0x04000 | 0x00500 ); apic_wait_icr_idle(); apic_write(0x310 , (( apicid )<<24) ); apic_write(0x300 , 0x08000 | 0x00500 ); apic_wait_icr_idle(); for(;;) { hlt(); } } static void pc87360_enable_serial(void) { pnp_set_logical_device(0x2e , 0x03 ); pnp_set_enable(0x2e , 1); pnp_set_iobase0(0x2e , 0x3f8); } static void main(void) { static const struct mem_controller cpu[] = { { .node_id = 0, .f0 = ( ((( 0 ) & 0xFF) << 16) | ((( 0x18 ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) , .f1 = ( ((( 0 ) & 0xFF) << 16) | ((( 0x18 ) & 0x1f) << 11) | ((( 1 ) & 0x7) << 8)) , .f2 = ( ((( 0 ) & 0xFF) << 16) | ((( 0x18 ) & 0x1f) << 11) | ((( 2 ) & 0x7) << 8)) , .f3 = ( ((( 0 ) & 0xFF) << 16) | ((( 0x18 ) & 0x1f) << 11) | ((( 3 ) & 0x7) << 8)) , .channel0 = { (0xa<<3)|0, (0xa<<3)|2, 0, 0 }, .channel1 = { (0xa<<3)|1, (0xa<<3)|3, 0, 0 }, }, { .node_id = 1, .f0 = ( ((( 0 ) & 0xFF) << 16) | ((( 0x19 ) & 0x1f) << 11) | ((( 0 ) & 0x7) << 8)) , .f1 = ( ((( 0 ) & 0xFF) << 16) | ((( 0x19 ) & 0x1f) << 11) | ((( 1 ) & 0x7) << 8)) , .f2 = ( ((( 0 ) & 0xFF) << 16) | ((( 0x19 ) & 0x1f) << 11) | ((( 2 ) & 0x7) << 8)) , .f3 = ( ((( 0 ) & 0xFF) << 16) | ((( 0x19 ) & 0x1f) << 11) | ((( 3 ) & 0x7) << 8)) , .channel0 = { (0xa<<3)|4, (0xa<<3)|6, 0, 0 }, .channel1 = { (0xa<<3)|5, (0xa<<3)|7, 0, 0 }, }, }; if (cpu_init_detected()) { asm("jmp __cpu_reset"); } enable_lapic(); init_timer(); if (!boot_cpu()) { stop_this_cpu(); } pc87360_enable_serial(); uart_init(); console_init(); setup_default_resource_map(); setup_coherent_ht_domain(); enumerate_ht_chain(0); distinguish_cpu_resets(0); enable_smbus(); memreset_setup(); sdram_initialize(sizeof(cpu)/sizeof(cpu[0]), cpu); }