diff --git a/src/arch/i386/include/arch/asm.h b/src/arch/i386/include/arch/asm.h new file mode 100644 index 0000000000..ea63e2d4eb --- /dev/null +++ b/src/arch/i386/include/arch/asm.h @@ -0,0 +1,30 @@ +#ifndef ASM_H +#define ASM_H + +#define ASSEMBLER + +/* + * Bootstrap code for the STPC Consumer + * Copyright (c) 1999 by Net Insight AB. All Rights Reserved. + * + * $Id$ + * + */ + +#define I386_ALIGN_TEXT 0 +#define I386_ALIGN_DATA 0 + +/* + * XXX + */ +#ifdef __ELF__ +#define EXT(x) x +#else +#define EXT(x) _ ## x +#endif + +#define STATIC(x) .align I386_ALIGN_TEXT; EXT(x): +#define GLOBAL(x) .globl EXT(x); STATIC(x) +#define ENTRY(x) .text; GLOBAL(x) + +#endif /* ASM_H */ diff --git a/src/arch/i386/include/arch/i386_subr.h b/src/arch/i386/include/arch/i386_subr.h new file mode 100644 index 0000000000..4a6c2c4c73 --- /dev/null +++ b/src/arch/i386/include/arch/i386_subr.h @@ -0,0 +1,7 @@ +#ifndef I386_SUBR_H +#define I386_SUBR_H + +void cache_on(unsigned long totalram); +void interrupts_on(void); + +#endif /* I386_SUBR_H */ diff --git a/src/arch/i386/include/arch/intel.h b/src/arch/i386/include/arch/intel.h new file mode 100644 index 0000000000..df6604e08e --- /dev/null +++ b/src/arch/i386/include/arch/intel.h @@ -0,0 +1,369 @@ +/* +This software and ancillary information (herein called SOFTWARE ) +called LinuxBIOS is made available under the terms described +here. The SOFTWARE has been approved for release with associated +LA-CC Number 00-34 . Unless otherwise indicated, this SOFTWARE has +been authored by an employee or employees of the University of +California, operator of the Los Alamos National Laboratory under +Contract No. W-7405-ENG-36 with the U.S. Department of Energy. The +U.S. Government has rights to use, reproduce, and distribute this +SOFTWARE. The public may copy, distribute, prepare derivative works +and publicly display this SOFTWARE without charge, provided that this +Notice and any statement of authorship are reproduced on all copies. +Neither the Government nor the University makes any warranty, express +or implied, or assumes any liability or responsibility for the use of +this SOFTWARE. If SOFTWARE is modified to produce derivative works, +such modified SOFTWARE should be clearly marked, so as not to confuse +it with the version available from LANL. + */ +/* Copyright 2000, Ron Minnich, Advanced Computing Lab, LANL + * rminnich@lanl.gov + */ + + +#ifndef ROM_INTEL_H +#define ROM_INTEL_H + +/* + * Bootstrap code for the Intel + * + * $Id$ + * + */ + +/* + * Config registers. + */ +/* yeah, yeah, I know these are macros, which is bad. Don't forget: + * we have almost no assembly, so I am not worrying just yet about this. + * we'll fix it someday if we care. My guess is we won't. + */ + +/* well we want functions. But first we want to see it work at all. */ +#undef FUNCTIONS +#ifndef FUNCTIONS + + +#define RET_LABEL(label) \ + jmp label##_done + +#define CALL_LABEL(label) \ + jmp label ;\ +label##_done: + +#define CALLSP(func) \ + lea 0f, %esp ; \ + jmp func ; \ +0: + +#define RETSP \ + jmp *%esp + + +#define DELAY(x) mov x, %ecx ;\ + 1: loop 1b ;\ + + + /* + * Macro: PCI_WRITE_CONFIG_BYTE + * Arguments: %eax address to write to (includes bus, device, function, &offset) + * %dl byte to write + * + * Results: none + * + * Trashed: %eax, %edx + * Effects: writes a single byte to pci config space + * + * Notes: This routine is optimized for minimal register usage. + * And the tricks it does cannot scale beyond writing a single byte. + * + * What it does is almost simple. + * It preserves %eax (baring special bits) until it is written + * out to the appropriate port. And hides the data byte + * in the high half of edx. + * + * In %edx[3] it stores the byte to write. + * In %edx[2] it stores the lower three bits of the address. + */ + + +#define PCI_WRITE_CONFIG_BYTE \ + shll $8, %edx ; \ + movb %al, %dl ; \ + andb $0x3, %dl ; \ + shll $16, %edx ; \ + \ + orl $0x80000000, %eax ; \ + andl $0xfffffffc, %eax ; \ + movw $0xcf8, %dx ; \ + outl %eax, %dx ; \ + \ + shrl $16, %edx ; \ + movb %dh, %al ; \ + movb $0, %dh ; \ + addl $0xcfc, %edx ; \ + outb %al, %dx + + + /* + * Macro: PCI_WRITE_CONFIG_WORD + * Arguments: %eax address to write to (includes bus, device, function, &offset) + * %ecx word to write + * + * Results: none + * + * Trashed: %eax, %edx + * Preserved: %ecx + * Effects: writes a single byte to pci config space + * + * Notes: This routine is optimized for minimal register usage. + * + * What it does is almost simple. + * It preserves %eax (baring special bits) until it is written + * out to the appropriate port. And hides the least significant + * bits of the address in the high half of edx. + * + * In %edx[2] it stores the lower three bits of the address. + */ + + +#define PCI_WRITE_CONFIG_WORD \ + movb %al, %dl ; \ + andl $0x3, %edx ; \ + shll $16, %edx ; \ + \ + orl $0x80000000, %eax ; \ + andl $0xfffffffc, %eax ; \ + movw $0xcf8, %dx ; \ + outl %eax, %dx ; \ + \ + shrl $16, %edx ; \ + movl %ecx, %eax ; \ + addl $0xcfc, %edx ; \ + outw %ax, %dx + + + + /* + * Macro: PCI_WRITE_CONFIG_DWORD + * Arguments: %eax address to write to (includes bus, device, function, &offset) + * %ecx dword to write + * + * Results: none + * + * Trashed: %eax, %edx + * Preserved: %ecx + * Effects: writes a single byte to pci config space + * + * Notes: This routine is optimized for minimal register usage. + * + * What it does is almost simple. + * It preserves %eax (baring special bits) until it is written + * out to the appropriate port. And hides the least significant + * bits of the address in the high half of edx. + * + * In %edx[2] it stores the lower three bits of the address. + */ + + +#define PCI_WRITE_CONFIG_DWORD \ + movb %al, %dl ; \ + andl $0x3, %edx ; \ + shll $16, %edx ; \ + \ + orl $0x80000000, %eax ; \ + andl $0xfffffffc, %eax ; \ + movw $0xcf8, %dx ; \ + outl %eax, %dx ; \ + \ + shrl $16, %edx ; \ + movl %ecx, %eax ; \ + addl $0xcfc, %edx ; \ + outl %eax, %dx + + + + + /* + * Macro: PCI_READ_CONFIG_BYTE + * Arguments: %eax address to read from (includes bus, device, function, &offset) + * + * Results: %al Byte read + * + * Trashed: %eax, %edx + * Effects: reads a single byte from pci config space + * + * Notes: This routine is optimized for minimal register usage. + * + * What it does is almost simple. + * It preserves %eax (baring special bits) until it is written + * out to the appropriate port. And hides the least significant + * bits of the address in the high half of edx. + * + * In %edx[2] it stores the lower three bits of the address. + */ + + +#define PCI_READ_CONFIG_BYTE \ + movb %al, %dl ; \ + andl $0x3, %edx ; \ + shll $16, %edx ; \ + \ + orl $0x80000000, %eax ; \ + andl $0xfffffffc, %eax ; \ + movw $0xcf8, %dx ; \ + outl %eax, %dx ; \ + \ + shrl $16, %edx ; \ + addl $0xcfc, %edx ; \ + inb %dx, %al + + + + /* + * Macro: PCI_READ_CONFIG_WORD + * Arguments: %eax address to read from (includes bus, device, function, &offset) + * + * Results: %ax word read + * + * Trashed: %eax, %edx + * Effects: reads a 2 bytes from pci config space + * + * Notes: This routine is optimized for minimal register usage. + * + * What it does is almost simple. + * It preserves %eax (baring special bits) until it is written + * out to the appropriate port. And hides the least significant + * bits of the address in the high half of edx. + * + * In %edx[2] it stores the lower three bits of the address. + */ + + +#define PCI_READ_CONFIG_WORD \ + movb %al, %dl ; \ + andl $0x3, %edx ; \ + shll $16, %edx ; \ + \ + orl $0x80000000, %eax ; \ + andl $0xfffffffc, %eax ; \ + movw $0xcf8, %dx ; \ + outl %eax, %dx ; \ + \ + shrl $16, %edx ; \ + addl $0xcfc, %edx ; \ + inw %dx, %ax + + + + /* + * Macro: PCI_READ_CONFIG_DWORD + * Arguments: %eax address to read from (includes bus, device, function, &offset) + * + * Results: %eax + * + * Trashed: %edx + * Effects: reads 4 bytes from pci config space + * + * Notes: This routine is optimized for minimal register usage. + * + * What it does is almost simple. + * It preserves %eax (baring special bits) until it is written + * out to the appropriate port. And hides the least significant + * bits of the address in the high half of edx. + * + * In %edx[2] it stores the lower three bits of the address. + */ + + +#define PCI_READ_CONFIG_DWORD \ + movb %al, %dl ; \ + andl $0x3, %edx ; \ + shll $16, %edx ; \ + \ + orl $0x80000000, %eax ; \ + andl $0xfffffffc, %eax ; \ + movw $0xcf8, %dx ; \ + outl %eax, %dx ; \ + \ + shrl $16, %edx ; \ + addl $0xcfc, %edx ; \ + inl %dx, %eax + + + + +#define CS_READ(which) \ + mov $0x80000000,%eax ; \ + mov which,%ax ; \ + and $0xfc,%al /* clear bits 1-0 */ ; \ + mov $0xcf8,%dx /* port 0xcf8 ?*/ ; \ + outl %eax,%dx /* open up CS config */ ; \ + add $0x4,%dl /* 0xcfc data port 0 */ ; \ + mov which,%al ; \ + and $0x3,%al /* only bits 1-0 */ ; \ + add %al,%dl ; \ + inb %dx,%al /* read */ ; \ + + +#define CS_WRITE(which, data) \ + mov $0x80000000,%eax /* 32bit word with bit 31 set */ ; \ + mov which,%ax /* put the reg# in the low part */ ; \ + and $0xfc,%al /* dword align the reg# */ ; \ + mov $0xcf8,%dx /* enable port */ ; \ + outl %eax,%dx ; \ + add $0x4,%dl /* 1st data port */ ; \ + mov which,%ax /* register# */ ; \ + and $0x3,%ax ; \ + add %al,%dl ; \ + mov data, %al ; \ + outb %al,%dx /* write to reg */ + +#define REGBIS(which, bis) \ + CS_READ(which) ;\ + movb bis, %cl ;\ + orb %al, %cl ;\ + CS_WRITE(which, %cl) + +#define REGBIC(which, bic) \ + CS_READ(which) ;\ + movb bic, %cl ;\ + notb %cl ;\ + andb %al, %cl ;\ + CS_WRITE(which, %cl) + + +/* macro to BIC and BIS a reg. calls read a reg, + * does a BIC and then a BIS on it. + * to clear no bits, make BIC 0. + * to set no bits, make BIS 0 + */ +#define REGBICBIS(which, bic, bis) \ + CS_READ(which) ;\ + movb bic, %cl ;\ + notb %cl ;\ + andb %cl, %al ;\ + movb bis, %cl ;\ + orb %al, %cl ;\ + CS_WRITE(which, %cl) + +#else +NO FUNCTIONS YET! +#endif + + + +/* originally this macro was from STPC BIOS */ +#define intel_chip_post_macro(value) \ + movb $value, %al ; \ + outb %al, $0x80 + +#define INTEL_PDATA_MAGIC 0xdeadbeef + +/* SLOW_DOWN_IO is a delay we can use that is roughly cpu neutral, + * and can be used before memory or timer chips come up. + * Since this hits the isa bus it's roughly + */ +#define SLOW_DOWN_IO inb $0x80, %al + +#endif /* ROM_INTEL_H */ diff --git a/src/arch/i386/include/arch/ioapic.h b/src/arch/i386/include/arch/ioapic.h new file mode 100644 index 0000000000..6c22975f07 --- /dev/null +++ b/src/arch/i386/include/arch/ioapic.h @@ -0,0 +1,11 @@ +#ifndef ARCH_IOAPIC_H +#define ARCH_IOAPIC_H + + +#ifdef IOAPIC +extern void setup_ioapic(void); +#else +#define setup_ioapic() do {} while(0) +#endif + +#endif /* ARCH_IOAPIC_H */ diff --git a/src/arch/i386/include/arch/pirq_routing.h b/src/arch/i386/include/arch/pirq_routing.h new file mode 100644 index 0000000000..0165b5786e --- /dev/null +++ b/src/arch/i386/include/arch/pirq_routing.h @@ -0,0 +1,21 @@ +#ifndef ARCH_PIRQ_ROUTING_H +#define ARCH_PIRQ_ROUTING_H + +#define PIRQ_SIGNATURE (('$' << 0) + ('P' << 8) + ('I' << 16) + ('R' << 24)) +#define PIRQ_VERSION 0x0100 + +extern const struct irq_routing_table intel_irq_routing_table; + +#if !defined(DEBUG) && defined(HAVE_PIRQ_TABLE) +void check_pirq_routing_table(void); +#else +#define check_pirq_routing_table() do {} while(0) +#endif + +#if defined(HAVE_PIRQ_ROUTING_TABLE) +void copy_pirq_routing_table(void); +#else +#define copy_pirq_routing_table() do {} while(0) +#endif + +#endif /* ARCH_PIRQ_ROUTING_H */ diff --git a/src/arch/i386/include/arch/smp/atomic.h b/src/arch/i386/include/arch/smp/atomic.h new file mode 100644 index 0000000000..7b68377c22 --- /dev/null +++ b/src/arch/i386/include/arch/smp/atomic.h @@ -0,0 +1,69 @@ +#ifndef ARCH_SMP_ATOMIC_H +#define ARCH_SMP_ATOMIC_H + +/* + * Make sure gcc doesn't try to be clever and move things around + * on us. We need to use _exactly_ the address the user gave us, + * not some alias that contains the same information. + */ +typedef struct { volatile int counter; } atomic_t; + +#define ATOMIC_INIT(i) { (i) } + +/* + * Atomic operations that C can't guarantee us. Useful for + * resource counting etc.. + */ + +/** + * atomic_read - read atomic variable + * @v: pointer of type atomic_t + * + * Atomically reads the value of @v. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +#define atomic_read(v) ((v)->counter) + +/** + * atomic_set - set atomic variable + * @v: pointer of type atomic_t + * @i: required value + * + * Atomically sets the value of @v to @i. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +#define atomic_set(v,i) (((v)->counter) = (i)) + +/** + * atomic_inc - increment atomic variable + * @v: pointer of type atomic_t + * + * Atomically increments @v by 1. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +static __inline__ void atomic_inc(atomic_t *v) +{ + __asm__ __volatile__( + "lock ; incl %0" + :"=m" (v->counter) + :"m" (v->counter)); +} + +/** + * atomic_dec - decrement atomic variable + * @v: pointer of type atomic_t + * + * Atomically decrements @v by 1. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +static __inline__ void atomic_dec(atomic_t *v) +{ + __asm__ __volatile__( + "lock ; decl %0" + :"=m" (v->counter) + :"m" (v->counter)); +} + + + +#endif /* ARCH_SMP_ATOMIC_H */ diff --git a/src/arch/i386/include/arch/smp/mpspec.h b/src/arch/i386/include/arch/smp/mpspec.h new file mode 100644 index 0000000000..be2acf43b5 --- /dev/null +++ b/src/arch/i386/include/arch/smp/mpspec.h @@ -0,0 +1,275 @@ +#ifndef __ASM_MPSPEC_H +#define __ASM_MPSPEC_H + +#ifdef HAVE_MP_TABLE + +/* + * Structure definitions for SMP machines following the + * Intel Multiprocessing Specification 1.1 and 1.4. + */ + +/* + * This tag identifies where the SMP configuration + * information is. + */ + +#define SMP_MAGIC_IDENT (('_'<<24)|('P'<<16)|('M'<<8)|'_') + +/* + * a maximum of 16 APICs with the current APIC ID architecture. + */ +#define MAX_APICS 16 + + +#define SMP_FLOATING_TABLE_LEN sizeof(struct intel_mp_floating) + +struct intel_mp_floating +{ + char mpf_signature[4]; /* "_MP_" */ + unsigned long mpf_physptr; /* Configuration table address */ + unsigned char mpf_length; /* Our length (paragraphs) */ + unsigned char mpf_specification;/* Specification version */ + unsigned char mpf_checksum; /* Checksum (makes sum 0) */ + unsigned char mpf_feature1; /* Standard or configuration ? */ + unsigned char mpf_feature2; /* Bit7 set for IMCR|PIC */ + unsigned char mpf_feature3; /* Unused (0) */ + unsigned char mpf_feature4; /* Unused (0) */ + unsigned char mpf_feature5; /* Unused (0) */ +}; + +struct mp_config_table +{ + char mpc_signature[4]; +#define MPC_SIGNATURE "PCMP" + unsigned short mpc_length; /* Size of table */ + char mpc_spec; /* 0x01 */ + char mpc_checksum; + char mpc_oem[8]; + char mpc_productid[12]; + unsigned long mpc_oemptr; /* 0 if not present */ + unsigned short mpc_oemsize; /* 0 if not present */ + unsigned short mpc_entry_count; + unsigned long mpc_lapic; /* APIC address */ + unsigned short mpe_length; /* Extended Table size */ + unsigned char mpe_checksum; /* Extended Table checksum */ + unsigned char reserved; +}; + +/* Followed by entries */ + +#define MP_PROCESSOR 0 +#define MP_BUS 1 +#define MP_IOAPIC 2 +#define MP_INTSRC 3 +#define MP_LINTSRC 4 + +struct mpc_config_processor +{ + unsigned char mpc_type; + unsigned char mpc_apicid; /* Local APIC number */ + unsigned char mpc_apicver; /* Its versions */ + unsigned char mpc_cpuflag; +#define CPU_ENABLED 1 /* Processor is available */ +#define CPU_BOOTPROCESSOR 2 /* Processor is the BP */ + unsigned long mpc_cpufeature; +#define CPU_STEPPING_MASK 0x0F +#define CPU_MODEL_MASK 0xF0 +#define CPU_FAMILY_MASK 0xF00 + unsigned long mpc_featureflag; /* CPUID feature value */ + unsigned long mpc_reserved[2]; +}; + +struct mpc_config_bus +{ + unsigned char mpc_type; + unsigned char mpc_busid; + unsigned char mpc_bustype[6] __attribute((packed)); +}; + +#define BUSTYPE_EISA "EISA" +#define BUSTYPE_ISA "ISA" +#define BUSTYPE_INTERN "INTERN" /* Internal BUS */ +#define BUSTYPE_MCA "MCA" +#define BUSTYPE_VL "VL" /* Local bus */ +#define BUSTYPE_PCI "PCI" +#define BUSTYPE_PCMCIA "PCMCIA" + +struct mpc_config_ioapic +{ + unsigned char mpc_type; + unsigned char mpc_apicid; + unsigned char mpc_apicver; + unsigned char mpc_flags; +#define MPC_APIC_USABLE 0x01 + unsigned long mpc_apicaddr; +}; + +struct mpc_config_intsrc +{ + unsigned char mpc_type; + unsigned char mpc_irqtype; + unsigned short mpc_irqflag; + unsigned char mpc_srcbus; + unsigned char mpc_srcbusirq; + unsigned char mpc_dstapic; + unsigned char mpc_dstirq; +}; + +enum mp_irq_source_types { + mp_INT = 0, + mp_NMI = 1, + mp_SMI = 2, + mp_ExtINT = 3 +}; + +#define MP_IRQ_POLARITY_DEFAULT 0x0 +#define MP_IRQ_POLARITY_HIGH 0x1 +#define MP_IRQ_POLARITY_LOW 0x3 +#define MP_IRQ_POLARITY_MASK 0x3 +#define MP_IRQ_TRIGGER_DEFAULT 0x0 +#define MP_IRQ_TRIGGER_EDGE 0x4 +#define MP_IRQ_TRIGGER_LEVEL 0xc +#define MP_IRQ_TRIGGER_MASK 0xc + + +struct mpc_config_lintsrc +{ + unsigned char mpc_type; + unsigned char mpc_irqtype; + unsigned short mpc_irqflag; + unsigned char mpc_srcbusid; + unsigned char mpc_srcbusirq; + unsigned char mpc_destapic; +#define MP_APIC_ALL 0xFF + unsigned char mpc_destapiclint; +}; + +/* + * Default configurations + * + * 1 2 CPU ISA 82489DX + * 2 2 CPU EISA 82489DX neither IRQ 0 timer nor IRQ 13 DMA chaining + * 3 2 CPU EISA 82489DX + * 4 2 CPU MCA 82489DX + * 5 2 CPU ISA+PCI + * 6 2 CPU EISA+PCI + * 7 2 CPU MCA+PCI + */ + +#define MAX_IRQ_SOURCES 128 +#define MAX_MP_BUSSES 32 +enum mp_bustype { + MP_BUS_ISA, + MP_BUS_EISA, + MP_BUS_PCI, + MP_BUS_MCA +}; + +/* Followed by entries */ + +#define MPE_SYSTEM_ADDRESS_SPACE 0x80 +#define MPE_BUS_HIERARCHY 0x81 +#define MPE_COMPATIBILITY_ADDRESS_SPACE 0x82 + +struct mp_exten_config { + unsigned char mpe_type; + unsigned char mpe_length; +}; + +typedef struct mp_exten_config *mpe_t; + +struct mp_exten_system_address_space { + unsigned char mpe_type; + unsigned char mpe_length; + unsigned char mpe_busid; + unsigned char mpe_address_type; +#define ADDRESS_TYPE_IO 0 +#define ADDRESS_TYPE_MEM 1 +#define ADDRESS_TYPE_PREFETCH 2 + unsigned int mpe_address_base_low; + unsigned int mpe_address_base_high; + unsigned int mpe_address_length_low; + unsigned int mpe_address_length_high; +}; + +struct mp_exten_bus_hierarchy { + unsigned char mpe_type; + unsigned char mpe_length; + unsigned char mpe_busid; + unsigned char mpe_bus_info; +#define BUS_SUBTRACTIVE_DECODE 1 + unsigned char mpe_parent_busid; + unsigned char reserved[3]; +}; + +struct mp_exten_compatibility_address_space { + unsigned char mpe_type; + unsigned char mpe_length; + unsigned char mpe_busid; + unsigned char mpe_address_modifier; +#define ADDRESS_RANGE_SUBTRACT 1 +#define ADDRESS_RANGE_ADD 0 + unsigned int mpe_range_list; +#define RANGE_LIST_IO_ISA 0 + /* X100 - X3FF + * X500 - X7FF + * X900 - XBFF + * XD00 - XFFF + */ +#define RANGE_LIST_IO_VGA 1 + /* X3B0 - X3BB + * X3C0 - X3DF + * X7B0 - X7BB + * X7C0 - X7DF + * XBB0 - XBBB + * XBC0 - XBDF + * XFB0 - XFBB + * XFC0 - XCDF + */ +}; + +/* Default local apic addr */ +#define LAPIC_ADDR 0xFEE00000 + +void *smp_next_mpc_entry(struct mp_config_table *mc); +void *smp_next_mpe_entry(struct mp_config_table *mc); + +void smp_write_processor(struct mp_config_table *mc, + unsigned char apicid, unsigned char apicver, + unsigned char cpuflag, unsigned int cpufeature, + unsigned int featureflag); +void smp_write_processors(struct mp_config_table *mc); +void smp_write_bus(struct mp_config_table *mc, + unsigned char id, unsigned char *bustype); +void smp_write_ioapic(struct mp_config_table *mc, + unsigned char id, unsigned char ver, + unsigned long apicaddr); +void smp_write_intsrc(struct mp_config_table *mc, + unsigned char irqtype, unsigned short irqflag, + unsigned char srcbus, unsigned char srcbusirq, + unsigned char dstapic, unsigned char dstirq); +void smp_write_lintsrc(struct mp_config_table *mc, + unsigned char irqtype, unsigned short irqflag, + unsigned char srcbusid, unsigned char srcbusirq, + unsigned char destapic, unsigned char destapiclint); +void smp_write_address_space(struct mp_config_table *mc, + unsigned char busid, unsigned char address_type, + unsigned int address_base_low, unsigned int address_base_high, + unsigned int address_length_low, unsigned int address_length_high); +void smp_write_bus_hierarchy(struct mp_config_table *mc, + unsigned char busid, unsigned char bus_info, + unsigned char parent_busid); +void smp_write_compatibility_address_space(struct mp_config_table *mc, + unsigned char busid, unsigned char address_modifier, + unsigned int range_list); +unsigned char smp_compute_checksum(void *v, int len); +void smp_write_floating_table(void *v); +void write_smp_table(void *v); + + +#else /* HAVE_MP_TABLE */ +#define write_smp_table(v) do {} while(0) +#endif /* HAVE_MP_TABLE */ + +#endif + diff --git a/src/arch/i386/include/arch/smp/spinlock.h b/src/arch/i386/include/arch/smp/spinlock.h new file mode 100644 index 0000000000..d0cc11c6a7 --- /dev/null +++ b/src/arch/i386/include/arch/smp/spinlock.h @@ -0,0 +1,57 @@ +#ifndef ARCH_SMP_SPINLOCK_H +#define ARCH_SMP_SPINLOCK_H + +/* + * Your basic SMP spinlocks, allowing only a single CPU anywhere + */ + +typedef struct { + volatile unsigned int lock; +} spinlock_t; + + +#define SPIN_LOCK_UNLOCKED (spinlock_t) { 1 } + +/* + * Simple spin lock operations. There are two variants, one clears IRQ's + * on the local processor, one does not. + * + * We make no fairness assumptions. They have a cost. + */ +#define barrier() __asm__ __volatile__("": : :"memory") +#define spin_is_locked(x) (*(volatile char *)(&(x)->lock) <= 0) +#define spin_unlock_wait(x) do { barrier(); } while(spin_is_locked(x)) + +#define spin_lock_string \ + "\n1:\t" \ + "lock ; decb %0\n\t" \ + "js 2f\n" \ + ".section .text.lock,\"ax\"\n" \ + "2:\t" \ + "cmpb $0,%0\n\t" \ + "rep;nop\n\t" \ + "jle 2b\n\t" \ + "jmp 1b\n" \ + ".previous" + +/* + * This works. Despite all the confusion. + */ +#define spin_unlock_string \ + "movb $1,%0" + +static inline void spin_lock(spinlock_t *lock) +{ + __asm__ __volatile__( + spin_lock_string + :"=m" (lock->lock) : : "memory"); +} + +static inline void spin_unlock(spinlock_t *lock) +{ + __asm__ __volatile__( + spin_unlock_string + :"=m" (lock->lock) : : "memory"); +} + +#endif /* ARCH_SMP_SPINLOCK_H */ diff --git a/src/arch/i386/lib/cpu_reset.inc b/src/arch/i386/lib/cpu_reset.inc new file mode 100644 index 0000000000..ad20ceed83 --- /dev/null +++ b/src/arch/i386/lib/cpu_reset.inc @@ -0,0 +1,55 @@ +jmp cpu_reset_out + +#ifdef DEBUG +cpu_reset_str: .string "cpu_reset\r\n"; +cpu_size_set_str: .string "cpu memory size set\r\n"; +#endif + + +__cpu_reset: +#ifdef DEBUG + TTYS0_TX_STRING($cpu_reset_str); +#endif /* DEBUG */ + + CALLSP(set_memory_size) + +#ifdef DEBUG + TTYS0_TX_STRING($cpu_size_set_str); +#endif /* DEBUG */ + +#ifdef SMP + /* Test to see if we are the boot strap processor. + * If so the boot must be complete. + */ + movl $0x1b, %ecx + rdmsr + testl $0x100, %eax + jnz __reboot + + /* Fixed mtrrs are enabled by the C code so if they + * aren't enabled yet we must be a secondary + * processor initializing in an SMP system. + */ + mov $MTRRdefType_MSR, %ecx + rdmsr + testl $0x400, %eax + jnz __reboot + + /* set the stack pointer */ + movl $_estack, %esp + movl $APIC_DEFAULT_BASE, %edi + movl APIC_ID(%edi), %eax + shrl $24, %eax + movl $STACK_SIZE, %ebx + mull %ebx + subl %eax, %esp + + call EXT(secondary_cpu_init) + /* Fall through in case we somehow return */ +#endif /* SMP */ + +__reboot: + movl $0xffffffff, %ebp + jmp __main + +cpu_reset_out: diff --git a/src/arch/i386/lib/pirq_routing.c b/src/arch/i386/lib/pirq_routing.c new file mode 100644 index 0000000000..513debda79 --- /dev/null +++ b/src/arch/i386/lib/pirq_routing.c @@ -0,0 +1,65 @@ +#ifdef HAVE_PIRQ_TABLE +#include +#include +#include + +#ifdef DEBUG +void check_pirq_routing_table(void) +{ + const u8 *addr; + const struct irq_routing_table *rt; + int i; + u8 sum; + + printk_info("Checking IRQ routing tables..."); + + rt = &intel_irq_routing_table; + addr = (u8 *)rt; + + sum = 0; + for (i = 0; i < rt->size; i++) + sum += addr[i]; + + printk_debug("%s:%6d:%s() - irq_routing_table located at: 0x%p\n", + __FILE__, __LINE__, __FUNCTION__, addr); + + sum = (unsigned char)(rt->checksum-sum); + + if (sum != rt->checksum) { + printk_warning("%s:%6d:%s() - " + "checksum is: 0x%02x but should be: 0x%02x\n", + __FILE__, __LINE__, __FUNCTION__, rt->checksum, sum); + } + + if (rt->signature != PIRQ_SIGNATURE || rt->version != PIRQ_VERSION || + rt->size % 16 || rt->size < sizeof(struct irq_routing_table)) { + printk_warning("%s:%6d:%s() - " + "Interrupt Routing Table not valid\n", + __FILE__, __LINE__, __FUNCTION__); + return; + } + + sum = 0; + for (i=0; isize; i++) + sum += addr[i]; + + if (sum) { + printk_warning("%s:%6d:%s() - " + "checksum error in irq routing table\n", + __FILE__, __LINE__, __FUNCTION__); + } + + printk_info("done.\n"); +} +#endif + +#define RTABLE_DEST 0xf0000 + +void copy_pirq_routing_table(void) +{ + printk_info("Copying IRQ routing tables..."); + memcpy((char *) RTABLE_DEST, &intel_irq_routing_table, intel_irq_routing_table.size); + printk_info("done.\n"); +} +#endif /* HAVE_PIRQ_TABLE */ + diff --git a/src/arch/i386/smp/Config b/src/arch/i386/smp/Config new file mode 100644 index 0000000000..9c1f39007f --- /dev/null +++ b/src/arch/i386/smp/Config @@ -0,0 +1,3 @@ +object mpspec.o +object ioapic.o +object start_stop.o diff --git a/src/arch/i386/smp/ioapic.c b/src/arch/i386/smp/ioapic.c new file mode 100644 index 0000000000..a4256b0037 --- /dev/null +++ b/src/arch/i386/smp/ioapic.c @@ -0,0 +1,137 @@ +#if defined(IOAPIC) +#include +#include + +/* TODO: this must move to chip/intel */ +/* we have to do more than we thought. I assumed Linux would do all the + * interesting parts, and I was wrong. + */ +struct ioapicreg { + unsigned int reg; + unsigned int value_low, value_high; +}; +struct ioapicreg ioapicregvalues[] = { +#define ALL (0xff << 24) +#define NONE (0) +#define DISABLED (1 << 16) +#define ENABLED (0 << 16) +#define TRIGGER_EDGE (0 << 15) +#define TRIGGER_LEVEL (1 << 15) +#define POLARITY_HIGH (0 << 13) +#define POLARITY_LOW (1 << 13) +#define PHYSICAL_DEST (0 << 11) +#define LOGICAL_DEST (1 << 11) +#define ExtINT (7 << 8) +#define NMI (4 << 8) +#define SMI (2 << 8) +#define INT (1 << 8) +#if 0 /* L440GX */ + {0x00, DISABLED, NONE}, + {0x01, ENABLED|TRIGGER_EDGE|POLARITY_HIGH|LOGICAL_DEST|INT|0x59, ALL}, + {0x02, ENABLED|TRIGGER_EDGE|POLARITY_HIGH|LOGICAL_DEST|INT|0x51, ALL}, + {0x03, ENABLED|TRIGGER_EDGE|POLARITY_HIGH|LOGICAL_DEST|INT|0x61, NONE}, + {0x04, ENABLED|TRIGGER_EDGE|POLARITY_HIGH|LOGICAL_DEST|INT|0x69, ALL}, + {0x05, ENABLED|TRIGGER_EDGE|POLARITY_HIGH|LOGICAL_DEST|INT|0x71, NONE}, + {0x06, ENABLED|TRIGGER_EDGE|POLARITY_HIGH|LOGICAL_DEST|INT|0x79, NONE}, + {0x07, ENABLED|TRIGGER_EDGE|POLARITY_HIGH|LOGICAL_DEST|INT|0x81, NONE}, + {0x08, ENABLED|TRIGGER_EDGE|POLARITY_HIGH|LOGICAL_DEST|INT|0x89, ALL}, + {0x09, DISABLED, NONE}, + {0x0a, DISABLED, NONE}, + {0x0b, DISABLED, NONE}, + {0x0c, ENABLED|TRIGGER_EDGE|POLARITY_HIGH|LOGICAL_DEST|INT|0x91, ALL}, + {0x0d, DISABLED, NONE}, + {0x0e, ENABLED|TRIGGER_EDGE|POLARITY_HIGH|LOGICAL_DEST|INT|0x99, ALL}, + {0x0f, ENABLED|TRIGGER_EDGE|POLARITY_HIGH|LOGICAL_DEST|INT|0xa1, ALL}, + {0x10, DISABLED, NONE}, + {0x11, DISABLED, NONE}, + {0x12, DISABLED, NONE}, + {0x13, ENABLED|TRIGGER_LEVEL|POLARITY_LOW|LOGICAL_DEST|INT|0xa9, ALL}, + {0x14, DISABLED, NONE}, + {0x15, ENABLED|TRIGGER_LEVEL|POLARITY_LOW|LOGICAL_DEST|INT|0xb1, ALL}, + {0x16, DISABLED, NONE}, + {0x17, DISABLED, NONE} +#endif +#if 0 /* tyan guinness */ + /* mask, trigger, polarity, destination, delivery, vector */ + {0x00, ENABLED|TRIGGER_EDGE|POLARITY_HIGH|LOGICAL_DEST|ExtINT|0x0, ALL}, + {0x01, ENABLED|TRIGGER_EDGE|POLARITY_HIGH|LOGICAL_DEST|INT|0x1, ALL}, + {0x02, ENABLED|TRIGGER_EDGE|POLARITY_HIGH|LOGICAL_DEST|INT|0x0, ALL}, + {0x03, ENABLED|TRIGGER_EDGE|POLARITY_HIGH|LOGICAL_DEST|INT|0x3, ALL}, + {0x04, ENABLED|TRIGGER_EDGE|POLARITY_HIGH|LOGICAL_DEST|INT|0x4, ALL}, + {0x05, ENABLED|TRIGGER_EDGE|POLARITY_HIGH|LOGICAL_DEST|INT|0x5, ALL}, + {0x06, ENABLED|TRIGGER_EDGE|POLARITY_HIGH|LOGICAL_DEST|INT|0x6, ALL}, + {0x07, ENABLED|TRIGGER_EDGE|POLARITY_HIGH|LOGICAL_DEST|INT|0x7, ALL}, + {0x08, ENABLED|TRIGGER_EDGE|POLARITY_HIGH|LOGICAL_DEST|INT|0x8, ALL}, + {0x09, ENABLED|TRIGGER_EDGE|POLARITY_HIGH|LOGICAL_DEST|INT|0x9, ALL}, + {0x0a, ENABLED|TRIGGER_EDGE|POLARITY_HIGH|LOGICAL_DEST|INT|0xa, ALL}, + {0x0b, ENABLED|TRIGGER_EDGE|POLARITY_HIGH|LOGICAL_DEST|INT|0xb, ALL}, + {0x0c, ENABLED|TRIGGER_EDGE|POLARITY_HIGH|LOGICAL_DEST|INT|0xc, ALL}, + {0x0d, ENABLED|TRIGGER_EDGE|POLARITY_HIGH|LOGICAL_DEST|INT|0xd, ALL}, + {0x0e, ENABLED|TRIGGER_EDGE|POLARITY_HIGH|LOGICAL_DEST|INT|0xe, ALL}, + {0x0f, ENABLED|TRIGGER_EDGE|POLARITY_HIGH|LOGICAL_DEST|INT|0xf, ALL}, + {0x10, ENABLED|TRIGGER_LEVEL|POLARITY_LOW|LOGICAL_DEST|INT|0x10, ALL}, + {0x11, ENABLED|TRIGGER_LEVEL|POLARITY_LOW|LOGICAL_DEST|INT|0x11, ALL}, + {0x12, ENABLED|TRIGGER_LEVEL|POLARITY_LOW|LOGICAL_DEST|INT|0x12, ALL}, + {0x13, ENABLED|TRIGGER_LEVEL|POLARITY_LOW|LOGICAL_DEST|INT|0x13, ALL}, + {0x14, DISABLED, NONE}, + {0x14, DISABLED, NONE}, + {0x15, DISABLED, NONE}, + {0x16, DISABLED, NONE}, + {0x17, DISABLED, NONE}, +#endif +#if 1 + /* mask, trigger, polarity, destination, delivery, vector */ + {0x00, DISABLED, NONE}, + {0x01, DISABLED, NONE}, + {0x02, DISABLED, NONE}, + {0x03, DISABLED, NONE}, + {0x04, DISABLED, NONE}, + {0x05, DISABLED, NONE}, + {0x06, DISABLED, NONE}, + {0x07, DISABLED, NONE}, + {0x08, DISABLED, NONE}, + {0x09, DISABLED, NONE}, + {0x0a, DISABLED, NONE}, + {0x0b, DISABLED, NONE}, + {0x0c, DISABLED, NONE}, + {0x0d, DISABLED, NONE}, + {0x0e, DISABLED, NONE}, + {0x0f, DISABLED, NONE}, + {0x10, DISABLED, NONE}, + {0x11, DISABLED, NONE}, + {0x12, DISABLED, NONE}, + {0x13, DISABLED, NONE}, + {0x14, DISABLED, NONE}, + {0x14, DISABLED, NONE}, + {0x15, DISABLED, NONE}, + {0x16, DISABLED, NONE}, + {0x17, DISABLED, NONE}, +#endif +}; + +void setup_ioapic(void) +{ + int i; + unsigned long value_low, value_high; + unsigned long nvram = 0xfec00000; + volatile unsigned long *l; + struct ioapicreg *a = ioapicregvalues; + + l = (unsigned long *) nvram; + for (i = 0; i < sizeof(ioapicregvalues) / sizeof(ioapicregvalues[0]); + i++, a++) { + l[0] = (a->reg * 2) + 0x10; + l[4] = a->value_low; + value_low = l[4]; + l[0] = (a->reg *2) + 0x11; + l[4] = a->value_high; + value_high = l[4]; + if ((i==0) && (value_low == 0xffffffff)) { + printk_warning("IO APIC not responding.\n"); + return; + } + printk_spew("for IRQ, reg 0x%08x value 0x%08x 0x%08x\n", + a->reg, a->value_low, a->value_high); + } +} +#endif /* IOAPIC */ diff --git a/src/arch/i386/smp/mpspec.c b/src/arch/i386/smp/mpspec.c new file mode 100644 index 0000000000..5d1f3b31aa --- /dev/null +++ b/src/arch/i386/smp/mpspec.c @@ -0,0 +1,243 @@ +#ifdef HAVE_MP_TABLE + +#ifndef lint +static char rcsid[] = "$Id$"; +#endif + +#include +#include +#include +#include +#include +#include + +unsigned char smp_compute_checksum(void *v, int len) +{ + unsigned char *bytes; + unsigned char checksum; + int i; + bytes = v; + checksum = 0; + for(i = 0; i < len; i++) { + checksum -= bytes[i]; + } + return checksum; +} + +void smp_write_floating_table(void *v) +{ + struct intel_mp_floating *mf; + + mf = v; + mf->mpf_signature[0] = '_'; + mf->mpf_signature[1] = 'M'; + mf->mpf_signature[2] = 'P'; + mf->mpf_signature[3] = '_'; + mf->mpf_physptr = (unsigned long)(((char *)v) + SMP_FLOATING_TABLE_LEN); + mf->mpf_length = 1; + mf->mpf_specification = 4; + mf->mpf_checksum = 0; + mf->mpf_feature1 = 0; + mf->mpf_feature2 = 0; + mf->mpf_feature3 = 0; + mf->mpf_feature4 = 0; + mf->mpf_feature5 = 0; + mf->mpf_checksum = smp_compute_checksum(mf, mf->mpf_length*16); +} + +void *smp_next_mpc_entry(struct mp_config_table *mc) +{ + void *v; + v = (void *)(((char *)mc) + mc->mpc_length); + return v; +} +static void smp_add_mpc_entry(struct mp_config_table *mc, unsigned length) +{ + mc->mpc_length += length; + mc->mpc_entry_count++; +} + +void *smp_next_mpe_entry(struct mp_config_table *mc) +{ + void *v; + v = (void *)(((char *)mc) + mc->mpc_length + mc->mpe_length); + return v; +} +static void smp_add_mpe_entry(struct mp_config_table *mc, mpe_t mpe) +{ + mc->mpe_length += mpe->mpe_length; +} + +void smp_write_processor(struct mp_config_table *mc, + unsigned char apicid, unsigned char apicver, + unsigned char cpuflag, unsigned int cpufeature, + unsigned int featureflag) +{ + struct mpc_config_processor *mpc; + mpc = smp_next_mpc_entry(mc); + memset(mpc, '\0', sizeof(*mpc)); + mpc->mpc_type = MP_PROCESSOR; + mpc->mpc_apicid = apicid; + mpc->mpc_apicver = apicver; + mpc->mpc_cpuflag = cpuflag; + mpc->mpc_cpufeature = cpufeature; + mpc->mpc_featureflag = featureflag; + smp_add_mpc_entry(mc, sizeof(*mpc)); +} + +/* If we assume a symmetric processor configuration we can + * get all of the information we need to write the processor + * entry from the bootstrap processor. + * Plus I don't think linux really even cares. + * Having the proper apicid's in the table so the non-bootstrap + * processors can be woken up should be enough. + */ +void smp_write_processors(struct mp_config_table *mc) +{ + int i; + int processor_id; + unsigned apic_version; + unsigned cpu_features; + unsigned cpu_feature_flags; + int eax, ebx, ecx, edx; + processor_id = this_processors_id(); + apic_version = apic_read(APIC_LVR) & 0xff; + cpuid(1, &eax, &ebx, &ecx, &edx); + cpu_features = eax; + cpu_feature_flags = edx; + for(i = 0; i < MAX_CPUS; i++) { + smp_write_processor(mc, i, apic_version, + ((processor_id == i)? CPU_BOOTPROCESSOR:0) | CPU_ENABLED, + cpu_features, cpu_feature_flags + ); + + } +} + +void smp_write_bus(struct mp_config_table *mc, + unsigned char id, unsigned char *bustype) +{ + struct mpc_config_bus *mpc; + mpc = smp_next_mpc_entry(mc); + memset(mpc, '\0', sizeof(*mpc)); + mpc->mpc_type = MP_BUS; + mpc->mpc_busid = id; + memcpy(mpc->mpc_bustype, bustype, sizeof(mpc->mpc_bustype)); + smp_add_mpc_entry(mc, sizeof(*mpc)); +} + +void smp_write_ioapic(struct mp_config_table *mc, + unsigned char id, unsigned char ver, + unsigned long apicaddr) +{ + struct mpc_config_ioapic *mpc; + mpc = smp_next_mpc_entry(mc); + memset(mpc, '\0', sizeof(*mpc)); + mpc->mpc_type = MP_IOAPIC; + mpc->mpc_apicid = id; + mpc->mpc_apicver = ver; + mpc->mpc_flags = MPC_APIC_USABLE; + mpc->mpc_apicaddr = apicaddr; + smp_add_mpc_entry(mc, sizeof(*mpc)); +} + +void smp_write_intsrc(struct mp_config_table *mc, + unsigned char irqtype, unsigned short irqflag, + unsigned char srcbus, unsigned char srcbusirq, + unsigned char dstapic, unsigned char dstirq) +{ + struct mpc_config_intsrc *mpc; + mpc = smp_next_mpc_entry(mc); + memset(mpc, '\0', sizeof(*mpc)); + mpc->mpc_type = MP_INTSRC; + mpc->mpc_irqtype = irqtype; + mpc->mpc_irqflag = irqflag; + mpc->mpc_srcbus = srcbus; + mpc->mpc_srcbusirq = srcbusirq; + mpc->mpc_dstapic = dstapic; + mpc->mpc_dstirq = dstirq; + smp_add_mpc_entry(mc, sizeof(*mpc)); +} + + +void smp_write_lintsrc(struct mp_config_table *mc, + unsigned char irqtype, unsigned short irqflag, + unsigned char srcbusid, unsigned char srcbusirq, + unsigned char destapic, unsigned char destapiclint) +{ + struct mpc_config_lintsrc *mpc; + mpc = smp_next_mpc_entry(mc); + memset(mpc, '\0', sizeof(*mpc)); + mpc->mpc_type = MP_LINTSRC; + mpc->mpc_irqtype = irqtype; + mpc->mpc_irqflag = irqflag; + mpc->mpc_srcbusid = srcbusid; + mpc->mpc_srcbusirq = srcbusirq; + mpc->mpc_destapic = destapic; + mpc->mpc_destapiclint = destapiclint; + smp_add_mpc_entry(mc, sizeof(*mpc)); +} + +void smp_write_address_space(struct mp_config_table *mc, + unsigned char busid, unsigned char address_type, + unsigned int address_base_low, unsigned int address_base_high, + unsigned int address_length_low, unsigned int address_length_high) +{ + struct mp_exten_system_address_space *mpe; + mpe = smp_next_mpe_entry(mc); + memset(mpe, '\0', sizeof(*mpe)); + mpe->mpe_type = MPE_SYSTEM_ADDRESS_SPACE; + mpe->mpe_length = sizeof(*mpe); + mpe->mpe_busid = busid; + mpe->mpe_address_type = address_type; + mpe->mpe_address_base_low = address_base_low; + mpe->mpe_address_base_high = address_base_high; + mpe->mpe_address_length_low = address_length_low; + mpe->mpe_address_length_high = address_length_high; + smp_add_mpe_entry(mc, (mpe_t)mpe); +} + + +void smp_write_bus_hierarchy(struct mp_config_table *mc, + unsigned char busid, unsigned char bus_info, + unsigned char parent_busid) +{ + struct mp_exten_bus_hierarchy *mpe; + mpe = smp_next_mpe_entry(mc); + memset(mpe, '\0', sizeof(*mpe)); + mpe->mpe_type = MPE_BUS_HIERARCHY; + mpe->mpe_length = sizeof(*mpe); + mpe->mpe_busid = busid; + mpe->mpe_bus_info = bus_info; + mpe->mpe_parent_busid = parent_busid; + smp_add_mpe_entry(mc, (mpe_t)mpe); +} + +void smp_write_compatibility_address_space(struct mp_config_table *mc, + unsigned char busid, unsigned char address_modifier, + unsigned int range_list) +{ + struct mp_exten_compatibility_address_space *mpe; + mpe = smp_next_mpe_entry(mc); + memset(mpe, '\0', sizeof(*mpe)); + mpe->mpe_type = MPE_COMPATIBILITY_ADDRESS_SPACE; + mpe->mpe_length = sizeof(*mpe); + mpe->mpe_busid = busid; + mpe->mpe_address_modifier = address_modifier; + mpe->mpe_range_list = range_list; + smp_add_mpe_entry(mc, (mpe_t)mpe); +} + +#if 0 +/* memcpy standard block */ +const static char smpblock[] = +{0x5F, 0x4D, 0x50, 0x5F, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x04, 0x9B, 0x05, 0x00, 0x00, 0x00, 0x00 +}; +void write_smp_table(void *v) +{ + memcpy(v, smpblock, sizeof(smpblock)); +} +#endif /* 0 */ + +#endif /* HAVE_MP_TABLE */ diff --git a/src/arch/i386/smp/start_stop.c b/src/arch/i386/smp/start_stop.c new file mode 100644 index 0000000000..fd9aa8dcc9 --- /dev/null +++ b/src/arch/i386/smp/start_stop.c @@ -0,0 +1,194 @@ +#include +#include +#include + +static inline void hlt(void) +{ + asm("hlt"); + return; +} + +int this_processors_id(void) +{ + return apic_read(APIC_ID) >> 24; +} + +void stop_cpu(int apicid) +{ + int timeout; + unsigned long send_status; + + /* send an APIC INIT to myself */ + apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(apicid)); + apic_write_around(APIC_ICR, APIC_INT_LEVELTRIG | APIC_INT_ASSERT | APIC_DM_INIT); + + /* wait for the ipi send to finish */ + printk_spew("Waiting for send to finish...\n"); + timeout = 0; + do { + printk_spew("+"); + udelay(100); + send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY; + } while (send_status && (timeout++ < 1000)); + if (timeout >= 1000) { + printk_spew("timed out\n"); + } + mdelay(10); + + printk_spew("Deasserting INIT.\n"); + /* Deassert the APIC INIT */ + apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(apicid)); + apic_write_around(APIC_ICR, APIC_INT_LEVELTRIG | APIC_DM_INIT); + + printk_spew("Waiting for send to finish...\n"); + timeout = 0; + do { + printk_spew("+"); + udelay(100); + send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY; + } while (send_status && (timeout++ < 1000)); + if (timeout >= 1000) { + printk_debug("timed out\n"); + } + + while(1) { + hlt(); + } + +} + +void start_cpu(int apicid) +{ + int timeout; + unsigned long send_status, accept_status, start_eip; + int j, num_starts, maxlvt; + /* + * Starting actual IPI sequence... + */ + + printk_spew("Asserting INIT.\n"); + + /* + * Turn INIT on target chip + */ + apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(apicid)); + + /* + * Send IPI + */ + apic_write_around(APIC_ICR, APIC_INT_LEVELTRIG | APIC_INT_ASSERT + | APIC_DM_INIT); + + printk_spew("Waiting for send to finish...\n"); + timeout = 0; + do { + printk_spew("+"); + udelay(100); + send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY; + } while (send_status && (timeout++ < 1000)); + if (timeout >= 1000) { + printk_spew("timed out\n"); + } + + mdelay(10); + + printk_spew("Deasserting INIT.\n"); + + /* Target chip */ + apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(apicid)); + + /* Send IPI */ + apic_write_around(APIC_ICR, APIC_INT_LEVELTRIG | APIC_DM_INIT); + + printk_spew("Waiting for send to finish...\n"); + timeout = 0; + do { + printk_spew("+"); + udelay(100); + send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY; + } while (send_status && (timeout++ < 1000)); + if (timeout >= 1000) { + printk_spew("timed out\n"); + } + + /* FIXME start_eip should be more flexible! */ + start_eip = 0xf0000; + printk_spew("start eip=0x%08lx\n", start_eip); + + num_starts = 2; + + /* + * Run STARTUP IPI loop. + */ + printk_spew("#startup loops: %d.\n", num_starts); + + maxlvt = 4; + + for (j = 1; j <= num_starts; j++) { + printk_spew("Sending STARTUP #%d.\n",j); + apic_read_around(APIC_SPIV); + apic_write(APIC_ESR, 0); + apic_read(APIC_ESR); + printk_spew("After apic_write.\n"); + + /* + * STARTUP IPI + */ + + /* Target chip */ + apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(apicid)); + + /* Boot on the stack */ + /* Kick the second */ + apic_write_around(APIC_ICR, APIC_DM_STARTUP + | (start_eip >> 12)); + + /* + * Give the other CPU some time to accept the IPI. + */ + udelay(300); + + printk_spew("Startup point 1.\n"); + + printk_spew("Waiting for send to finish...\n"); + timeout = 0; + do { + printk_spew("+"); + udelay(100); + send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY; + } while (send_status && (timeout++ < 1000)); + + /* + * Give the other CPU some time to accept the IPI. + */ + udelay(200); + /* + * Due to the Pentium erratum 3AP. + */ + if (maxlvt > 3) { + apic_read_around(APIC_SPIV); + apic_write(APIC_ESR, 0); + } + accept_status = (apic_read(APIC_ESR) & 0xEF); + if (send_status || accept_status) + break; + } + printk_spew("After Startup.\n"); + if (send_status) + printk_warning("APIC never delivered???\n"); + if (accept_status) + printk_warning("APIC delivery error (%lx).\n", accept_status); +} + +void startup_other_cpus(void) +{ + int apicid = this_processors_id(); + int i; + /* Assume the cpus are densly packed by apicid */ + for(i = 0; i < MAX_CPUS; i++) { + if (i == apicid ) { + continue; + } + start_cpu(i); + } +} diff --git a/src/cpu/k7/earlymtrr.inc b/src/cpu/k7/earlymtrr.inc new file mode 100644 index 0000000000..59c7bf78fa --- /dev/null +++ b/src/cpu/k7/earlymtrr.inc @@ -0,0 +1,91 @@ +#include + +/* The fixed and variable MTRRs are powered-up with random values, clear them to + * MTRR_TYPE_UNCACHABLE for safty reason + */ + + +earlymtrr_start: + xorl %eax, %eax # clear %eax and %edx + xorl %edx, %edx # + movl $fixed_mtrr_msr, %esi + +clear_fixed_var_mtrr: + lodsl (%esi), %eax + testl %eax, %eax + jz clear_fixed_var_mtrr_out + + movl %eax, %ecx + xorl %eax, %eax + wrmsr + + jmp clear_fixed_var_mtrr +clear_fixed_var_mtrr_out: + + +set_var_mtrr: + /* enable caching for 0 - 128MB using variable mtrr */ + movl $0x200, %ecx + rdmsr + andl $0xfffffff0, %edx + orl $0x00000000, %edx + andl $0x00000f00, %eax + orl $0x00000006, %eax + wrmsr + + movl $0x201, %ecx + rdmsr + andl $0xfffffff0, %edx + orl $0x0000000f, %edx + andl $0x000007ff, %eax + orl $0xf0000800, %eax + wrmsr + +#if defined(XIP_ROM_SIZE) && defined(XIP_ROM_BASE) + /* enable write protect caching so we can do execute in place + * on the flash rom. + */ + movl $0x202, %ecx + xorl %edx, %edx + movl $(XIP_ROM_BASE | 0x005), %eax + wrmsr + + movl $0x203, %ecx + movl $0x0000000f, %edx + movl $(~(XIP_ROM_SIZE - 1) | 0x800), %eax + wrmsr +#endif /* XIP_ROM_SIZE && XIP_ROM_BASE */ + +enable_mtrr: + /* Set the default memory type and enable fixed and variable MTRRs */ + movl $0x2ff, %ecx + xorl %edx, %edx + /* Enable Variable MTRRs */ + movl $0x00000800, %eax + wrmsr + + /* Enable the MTRRs in SYSCFG */ +#if USE_AMD_NDA_CODE +#endif /* USE_AMD_NDA_CODE */ + + /* enable cache */ + movl %cr0, %eax + andl $0x9fffffff,%eax + movl %eax, %cr0 + + jmp earlymtrr_end + +fixed_mtrr_msr: + .long 0x250, 0x258, 0x259 + .long 0x268, 0x269, 0x26A + .long 0x26B, 0x26C, 0x26D + .long 0x26E, 0x26F +var_mtrr_msr: + .long 0x200, 0x201, 0x202, 0x203 + .long 0x204, 0x205, 0x206, 0x207 + .long 0x208, 0x209, 0x20A, 0x20B + .long 0x20C, 0x20D, 0x20E, 0x20F +#if USE_AMD_NDA_CODE +#endif /* USE_AMD_NDA_CODE */ + .long 0x000 /* NULL, end of table */ +earlymtrr_end: diff --git a/src/include/cpu/cpufixup.h b/src/include/cpu/cpufixup.h new file mode 100644 index 0000000000..5f4206d52a --- /dev/null +++ b/src/include/cpu/cpufixup.h @@ -0,0 +1,17 @@ +#ifndef CPU_CPUFIXUP_H +#define CPU_CPUFIXUP_H + +#include +#include + +#ifdef CPU_FIXUP +# if defined(k7) +# define cpufixup(totalram) k7_cpufixup(totalram) +# elif defined(i686) +# define cpufixup(totalram) p6_cpufixup(totalram) +# endif +#else +# define cpu_fixup(totalram) do {} while(0) +#endif + +#endif /* CPU_CPUFIXUP_H */ diff --git a/src/include/cpu/k7/cpufixup.h b/src/include/cpu/k7/cpufixup.h new file mode 100644 index 0000000000..b4d8335a62 --- /dev/null +++ b/src/include/cpu/k7/cpufixup.h @@ -0,0 +1,6 @@ +#ifndef CPU_K7_CPUFIXUP_H +#define CPU_K7_CPUFIXUP_H + +void k7_cpufixup(unsigned long totalram); + +#endif /* CPU_K7_CPUFIXUP_H */ diff --git a/src/include/cpu/k7/mtrr.h b/src/include/cpu/k7/mtrr.h new file mode 100644 index 0000000000..8ba2f58fe5 --- /dev/null +++ b/src/include/cpu/k7/mtrr.h @@ -0,0 +1,9 @@ +#ifndef CPU_K7_MTRR_H +#define CPU_K7_MTRR_H + +#include + +# if USE_AMD_NDA_CODE +# endif /* USE_AMD_NDA_CODE */ + +#endif /* CPU_K7_MTRR_H */ diff --git a/src/include/cpu/l2_cache.h b/src/include/cpu/l2_cache.h new file mode 100644 index 0000000000..dd7d3600c3 --- /dev/null +++ b/src/include/cpu/l2_cache.h @@ -0,0 +1,14 @@ +#ifndef CPU_L2_CACHE_H +#define CPU_L2_CACHE_H + +#include + +#ifdef CONFIGURE_L2_CACHE +# if defined(i686) +# define configure_l2_cache() p6_configure_l2_cache() +# endif +#else +# define configure_l2_cache() do {} while(0) +#endif + +#endif /* CPU_L2_CACHE_H */ diff --git a/src/include/cpu/p6/apic.h b/src/include/cpu/p6/apic.h new file mode 100644 index 0000000000..4fe9961991 --- /dev/null +++ b/src/include/cpu/p6/apic.h @@ -0,0 +1,176 @@ +#ifndef APIC_H +#define APIC_H + +#define APIC_BASE_MSR 0x1B +#define APIC_BASE_MSR_BOOTSTRAP_PROCESSOR (1 << 8) +#define APIC_BASE_MSR_ENABLE (1 << 11) +#define APIC_BASE_MSR_ADDR_MASK 0xFFFFF000 + +#define APIC_DEFAULT_BASE 0xfee00000 + +#define APIC_ID 0x020 +#define APIC_LVR 0x030 +#define APIC_ARBID 0x090 +#define APIC_RRR 0x0C0 +#define APIC_SVR 0x0f0 +#define APIC_SPIV 0x0f0 +#define APIC_SPIV_ENABLE 0x100 +#define APIC_ESR 0x280 +#define APIC_ESR_SEND_CS 0x00001 +#define APIC_ESR_RECV_CS 0x00002 +#define APIC_ESR_SEND_ACC 0x00004 +#define APIC_ESR_RECV_ACC 0x00008 +#define APIC_ESR_SENDILL 0x00020 +#define APIC_ESR_RECVILL 0x00040 +#define APIC_ESR_ILLREGA 0x00080 +#define APIC_ICR 0x300 +#define APIC_DEST_SELF 0x40000 +#define APIC_DEST_ALLINC 0x80000 +#define APIC_DEST_ALLBUT 0xC0000 +#define APIC_ICR_RR_MASK 0x30000 +#define APIC_ICR_RR_INVALID 0x00000 +#define APIC_ICR_RR_INPROG 0x10000 +#define APIC_ICR_RR_VALID 0x20000 +#define APIC_INT_LEVELTRIG 0x08000 +#define APIC_INT_ASSERT 0x04000 +#define APIC_ICR_BUSY 0x01000 +#define APIC_DEST_LOGICAL 0x00800 +#define APIC_DM_FIXED 0x00000 +#define APIC_DM_LOWEST 0x00100 +#define APIC_DM_SMI 0x00200 +#define APIC_DM_REMRD 0x00300 +#define APIC_DM_NMI 0x00400 +#define APIC_DM_INIT 0x00500 +#define APIC_DM_STARTUP 0x00600 +#define APIC_DM_EXTINT 0x00700 +#define APIC_VECTOR_MASK 0x000FF +#define APIC_ICR2 0x310 +#define GET_APIC_DEST_FIELD(x) (((x)>>24)&0xFF) +#define SET_APIC_DEST_FIELD(x) ((x)<<24) +#define APIC_LVTT 0x320 +#define APIC_LVTPC 0x340 +#define APIC_LVT0 0x350 +#define APIC_LVT_TIMER_BASE_MASK (0x3<<18) +#define GET_APIC_TIMER_BASE(x) (((x)>>18)&0x3) +#define SET_APIC_TIMER_BASE(x) (((x)<<18)) +#define APIC_TIMER_BASE_CLKIN 0x0 +#define APIC_TIMER_BASE_TMBASE 0x1 +#define APIC_TIMER_BASE_DIV 0x2 +#define APIC_LVT_TIMER_PERIODIC (1<<17) +#define APIC_LVT_MASKED (1<<16) +#define APIC_LVT_LEVEL_TRIGGER (1<<15) +#define APIC_LVT_REMOTE_IRR (1<<14) +#define APIC_INPUT_POLARITY (1<<13) +#define APIC_SEND_PENDING (1<<12) +#define APIC_LVT_RESERVED_1 (1<<11) +#define APIC_DELIVERY_MODE_MASK (7<<8) +#define APIC_DELIVERY_MODE_FIXED (0<<8) +#define APIC_DELIVERY_MODE_NMI (4<<8) +#define APIC_DELIVERY_MODE_EXTINT (7<<8) +#define GET_APIC_DELIVERY_MODE(x) (((x)>>8)&0x7) +#define SET_APIC_DELIVERY_MODE(x,y) (((x)&~0x700)|((y)<<8)) +#define APIC_MODE_FIXED 0x0 +#define APIC_MODE_NMI 0x4 +#define APIC_MODE_EXINT 0x7 +#define APIC_LVT1 0x360 +#define APIC_LVTERR 0x370 + + +#if !defined(ASSEMBLY) + +#include + + +#define xchg(ptr,v) ((__typeof__(*(ptr)))__xchg((unsigned long)(v),(ptr),sizeof(*(ptr)))) + +struct __xchg_dummy { unsigned long a[100]; }; +#define __xg(x) ((struct __xchg_dummy *)(x)) + +/* + * Note: no "lock" prefix even on SMP: xchg always implies lock anyway + * Note 2: xchg has side effect, so that attribute volatile is necessary, + * but generally the primitive is invalid, *ptr is output argument. --ANK + */ +static inline unsigned long __xchg(unsigned long x, volatile void * ptr, int size) +{ + switch (size) { + case 1: + __asm__ __volatile__("xchgb %b0,%1" + :"=q" (x) + :"m" (*__xg(ptr)), "0" (x) + :"memory"); + break; + case 2: + __asm__ __volatile__("xchgw %w0,%1" + :"=r" (x) + :"m" (*__xg(ptr)), "0" (x) + :"memory"); + break; + case 4: + __asm__ __volatile__("xchgl %0,%1" + :"=r" (x) + :"m" (*__xg(ptr)), "0" (x) + :"memory"); + break; + } + return x; +} + + +static inline unsigned long apic_read(unsigned long reg) +{ + return *((volatile unsigned long *)(APIC_DEFAULT_BASE+reg)); +} + +extern inline void apic_write_atomic(unsigned long reg, unsigned long v) +{ + xchg((volatile unsigned long *)(APIC_DEFAULT_BASE+reg), v); +} + +static inline void apic_write(unsigned long reg, unsigned long v) +{ + *((volatile unsigned long *)(APIC_DEFAULT_BASE+reg)) = v; +} + +static inline void apic_wait_icr_idle(void) +{ + do { } while ( apic_read( APIC_ICR ) & APIC_ICR_BUSY ); +} + +#ifdef CONFIG_X86_GOOD_APIC +# define FORCE_READ_AROUND_WRITE 0 +# define apic_read_around(x) +# define apic_write_around(x,y) apic_write((x),(y)) +#else +# define FORCE_READ_AROUND_WRITE 1 +# define apic_read_around(x) apic_read(x) +# define apic_write_around(x,y) apic_write_atomic((x),(y)) +#endif + +static inline unsigned long apic_remote_read(int apicid, int reg) +{ + int timeout; + unsigned long status, result; + apic_wait_icr_idle(); + apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(apicid)); + apic_write_around(APIC_ICR, APIC_DM_REMRD | (reg >> 4)); + timeout = 0; + do { +#if 0 + udelay(100); +#endif + status = apic_read(APIC_ICR) & APIC_ICR_RR_MASK; + } while (status == APIC_ICR_RR_INPROG && timeout++ < 1000); + + result = -1; + if (status == APIC_ICR_RR_VALID) { + result = apic_read(APIC_RRR); + } + else { + printk_err("remote apic read failed\n"); + } + return result; +} +#endif /* ASSEMBLY */ + +#endif /* APIC_H */ diff --git a/src/include/cpu/p6/cpufixup.h b/src/include/cpu/p6/cpufixup.h new file mode 100644 index 0000000000..0b21209702 --- /dev/null +++ b/src/include/cpu/p6/cpufixup.h @@ -0,0 +1,10 @@ +#ifndef CPU_P6_CPUFIXUP_H +#define CPU_P6_CPUFIXUP_H + +#ifdef UPDATE_MICROCODE +#define CPU_FIXUP +#endif + +void p6_cpufixup(unsigned long totalram); + +#endif /* CPU_P6_CPUFIXUP_H */ diff --git a/src/include/delay.h b/src/include/delay.h new file mode 100644 index 0000000000..a498ce370d --- /dev/null +++ b/src/include/delay.h @@ -0,0 +1,7 @@ +#ifndef DELAY_H +#define DELAY_H + +void udelay(int usecs); +void mdelay(int msecs); + +#endif /* DELAY_H */ diff --git a/src/include/part/floppy.h b/src/include/part/floppy.h new file mode 100644 index 0000000000..2c1a5194f1 --- /dev/null +++ b/src/include/part/floppy.h @@ -0,0 +1,10 @@ +#ifndef PART_FLOPPY_H +#define PART_FLOPPY_H + +#ifdef MUST_ENABLE_FLOPPY +void enable_floppy(void); +#else +# define enable_floppy() do {} while(0) +#endif + +#endif /* PART_FLOPPY_H */ diff --git a/src/include/part/framebuffer.h b/src/include/part/framebuffer.h new file mode 100644 index 0000000000..f0538ecee7 --- /dev/null +++ b/src/include/part/framebuffer.h @@ -0,0 +1,10 @@ +#ifndef PART_FRAMEBUFFER_H +#define PART_FRAMEBUFFER_H + +#ifdef HAVE_FRAMEBUFFER +void framebuffer_on(void) +#else +# define framebuffer_on() do {} while(0) +#endif /* HAVE_FRAMEBUFFER */ + +#endif /* PART_FRAMEBUFFER_H */ diff --git a/src/include/part/hard_reset.h b/src/include/part/hard_reset.h new file mode 100644 index 0000000000..51237677ad --- /dev/null +++ b/src/include/part/hard_reset.h @@ -0,0 +1,11 @@ +#ifndef PART_HARD_RESET_H +#define PART_HARD_RESET_H + +#ifdef HAVE_HARD_RESET +void hard_reset(void); +#else +#define hard_reset() do {} while(0) +#endif + + +#endif diff --git a/src/include/part/keyboard.h b/src/include/part/keyboard.h new file mode 100644 index 0000000000..50127239e1 --- /dev/null +++ b/src/include/part/keyboard.h @@ -0,0 +1,10 @@ +#ifndef PART_KEYBOARD_H +#define KEYBOARD_H + +#ifndef NO_KEYBOARD +void keyboard_on(void); +#else +# define keyboard_on() do {} while(0) +#endif + +#endif /* PART_KEYBOARD_H */ diff --git a/src/include/part/mainboard.h b/src/include/part/mainboard.h new file mode 100644 index 0000000000..1a364560e8 --- /dev/null +++ b/src/include/part/mainboard.h @@ -0,0 +1,12 @@ +#ifndef PART_MAINBOARD_H +#define PART_MAINBOARD_H + +void mainboard_fixup(void); + +#ifdef FINAL_MAINBOARD_FIXUP +void final_mainboard_fixup(void); +#else +# define final_mainboard_fixup() do {} while(0) +#endif /* FINAL_MAINBOARD_FIXUP */ + +#endif /* PART_MAINBOARD_H */ diff --git a/src/include/part/nvram.h b/src/include/part/nvram.h new file mode 100644 index 0000000000..b59c91a8c3 --- /dev/null +++ b/src/include/part/nvram.h @@ -0,0 +1,7 @@ +#ifndef PART_NVRAM_H +#define PART_NVRAM_H + +/* Turn on full access to the flash chip */ +void nvram_on(void); + +#endif /* PART_NVRAM_H */ diff --git a/src/include/part/sizeram.h b/src/include/part/sizeram.h new file mode 100644 index 0000000000..0312cb2a1f --- /dev/null +++ b/src/include/part/sizeram.h @@ -0,0 +1,6 @@ +#ifndef PART_SIZERAM_H +#define PART_SIZERAM_H + +unsigned long sizeram(void); + +#endif /* PART_SIZERAM_H */ diff --git a/src/include/pc80/i8259.h b/src/include/pc80/i8259.h new file mode 100644 index 0000000000..f2cdcaa422 --- /dev/null +++ b/src/include/pc80/i8259.h @@ -0,0 +1,11 @@ +#ifndef PC80_I8259 +#define PC80_I8259 + + +#ifdef I8259 +void setup_i8259(void); +#else +#define setup_i8259() do {} while(0) +#endif + +#endif diff --git a/src/include/pc80/keyboard.h b/src/include/pc80/keyboard.h index 8f2e9122a3..364e01f58e 100644 --- a/src/include/pc80/keyboard.h +++ b/src/include/pc80/keyboard.h @@ -1 +1,6 @@ +#ifndef PC80_KEYBOARD_H +#define PC80_KEYBOARD_H + void pc_keyboard_init(void); + +#endif /* PC80_KEYBOARD_H */ diff --git a/src/include/smp/atomic.h b/src/include/smp/atomic.h new file mode 100644 index 0000000000..c838c55034 --- /dev/null +++ b/src/include/smp/atomic.h @@ -0,0 +1,53 @@ +#ifndef SMP_ATOMIC_H +#define SMP_ATOMIC_H + +#ifdef SMP +#include +#else + +typedef struct { int counter } atomic_t; +#define ATOMIC_INIT(i) { (i) } + +/** + * atomic_read - read atomic variable + * @v: pointer of type atomic_t + * + * Atomically reads the value of @v. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +#define atomic_read(v) ((v)->counter) + +/** + * atomic_set - set atomic variable + * @v: pointer of type atomic_t + * @i: required value + * + * Atomically sets the value of @v to @i. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +#define atomic_set(v,i) (((v)->counter) = (i)) + + +/** + * atomic_inc - increment atomic variable + * @v: pointer of type atomic_t + * + * Atomically increments @v by 1. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +#define atomic_inc(v) (((v)->counter)++) + + +/** + * atomic_dec - decrement atomic variable + * @v: pointer of type atomic_t + * + * Atomically decrements @v by 1. Note that the guaranteed + * useful range of an atomic_t is only 24 bits. + */ +#define atomic_inc(v) (((v)->counter)--) + + +#endif /* SMP */ + +#endif /* SMP_ATOMIC_H */ diff --git a/src/include/smp/spinlock.h b/src/include/smp/spinlock.h new file mode 100644 index 0000000000..9e0f8af0a9 --- /dev/null +++ b/src/include/smp/spinlock.h @@ -0,0 +1,24 @@ +#ifndef SMP_SPINLOCK_H +#define SMP_SPINLOCK_H + +#ifdef SMP +#include +#else /* !SMP */ + +/* Most GCC versions have a nasty bug with empty initializers */ +#if (__GNUC__ > 2) +typedef struct { } spinlock_t; +#define SPIN_LOCK_UNLOCKED (spinlock_t) { } +#else +typedef struct { int gcc_is_buggy; } spinlock_t; +#define SPIN_LOCK_UNLOCKED (spinlock_t) { 0 } +#endif + +#define barrier() do {} while(0) +#define spin_is_locked(lock) 0 +#define spin_unlock_wait(lock) do {} while(0) +#define spin_lock(lock) do {} while(0) +#define spin_unlock(lock) do {} while(0) +#endif + +#endif /* SMP_SPINLOCK_H */ diff --git a/src/include/smp/start_stop.h b/src/include/smp/start_stop.h new file mode 100644 index 0000000000..91fc26aa45 --- /dev/null +++ b/src/include/smp/start_stop.h @@ -0,0 +1,15 @@ +#ifndef SMP_START_STOP_H +#define SMP_START_STOP_H + +#ifdef SMP +#include +int this_processors_id(void); +void stop_cpu(int processor_id); +void start_cpu(int processor_id); +void startup_other_cpus(void); +#else +#define this_processors_id() 0 +#define startup_other_cpus() do {} while(0) +#endif + +#endif /* SMP_START_STOP_H */ diff --git a/src/lib/delay.c b/src/lib/delay.c new file mode 100644 index 0000000000..14e00e7633 --- /dev/null +++ b/src/lib/delay.c @@ -0,0 +1,63 @@ +#include +#include + +/* Ports for the 8254 timer chip */ +#define TIMER2_PORT 0x42 +#define TIMER_MODE_PORT 0x43 + +/* Meaning of the mode bits */ +#define TIMER0_SEL 0x00 +#define TIMER1_SEL 0x40 +#define TIMER2_SEL 0x80 +#define READBACK_SEL 0xC0 + +#define LATCH_COUNT 0x00 +#define LOBYTE_ACCESS 0x10 +#define HIBYTE_ACCESS 0x20 +#define WORD_ACCESS 0x30 + +#define MODE0 0x00 +#define MODE1 0x02 +#define MODE2 0x04 +#define MODE3 0x06 +#define MODE4 0x08 +#define MODE5 0x0A + +#define BINARY_COUNT 0x00 +#define BCD_COUNT 0x01 + +/* Timers tick over at this rate */ +#define TICKS_PER_MS 1193 + +/* Parallel Peripheral Controller Port B */ +#define PPC_PORTB 0x61 + +/* Meaning of the port bits */ +#define PPCB_T2OUT 0x20 /* Bit 5 */ +#define PPCB_SPKR 0x02 /* Bit 1 */ +#define PPCB_T2GATE 0x01 /* Bit 0 */ + + +static void load_timer2(unsigned int ticks) +{ + /* Set up the timer gate, turn off the speaker */ + outb((inb(PPC_PORTB) & ~PPCB_SPKR) | PPCB_T2GATE, PPC_PORTB); + outb(TIMER2_SEL|WORD_ACCESS|MODE0|BINARY_COUNT, TIMER_MODE_PORT); + outb(ticks & 0xFF, TIMER2_PORT); + outb(ticks >> 8, TIMER2_PORT); +} + + +void udelay(int usecs) +{ + load_timer2((usecs*TICKS_PER_MS)/1000); + while ((inb(PPC_PORTB) & PPCB_T2OUT) == 0) + ; +} +void mdelay(int msecs) +{ + int i; + for(i = 0; i < msecs; i++) { + udelay(1000); + } +} diff --git a/src/mainboard/intel/l440gx/mptable.c b/src/mainboard/intel/l440gx/mptable.c new file mode 100644 index 0000000000..c80157671b --- /dev/null +++ b/src/mainboard/intel/l440gx/mptable.c @@ -0,0 +1,137 @@ +#include +#include + +void smp_write_config_table(void *v) +{ + int ioapicid = 0; // JAMES_HACK + static const char sig[4] = "PCMP"; + static const char oem[8] = "LNXI "; + static const char productid[12] = "L440GX "; + struct mp_config_table *mc; + + mc = (void *)(((char *)v) + SMP_FLOATING_TABLE_LEN); + memset(mc, 0, sizeof(*mc)); + + memcpy(mc->mpc_signature, sig, sizeof(sig)); + mc->mpc_length = sizeof(*mc); /* initially just the header */ + mc->mpc_spec = 0x04; + mc->mpc_checksum = 0; /* not yet computed */ + memcpy(mc->mpc_oem, oem, sizeof(oem)); + memcpy(mc->mpc_productid, productid, sizeof(productid)); + mc->mpc_oemptr = 0; + mc->mpc_oemsize = 0; + mc->mpc_entry_count = 0; /* No entries yet... */ + mc->mpc_lapic = LAPIC_ADDR; + mc->mpe_length = 0; + mc->mpe_checksum = 0; + mc->reserved = 0; + +#ifndef JAMES_HACK + smp_write_processor(mc, 0x01, 0x11, + CPU_ENABLED | CPU_BOOTPROCESSOR, 0x00000683, 0x0387fbff); +#else + printk("James: Setting up 2 proc. SMP table"); + + smp_write_processor(mc, 0x1, 0x11, + CPU_ENABLED | CPU_BOOTPROCESSOR, 0x00000683, 0x0387fbff); + smp_write_processor(mc, 0x0, 0x11, + CPU_ENABLED, 0x00000683, 0x0387fbff); + ioapicid = 2; +#endif + + smp_write_bus(mc, 0, "PCI "); + smp_write_bus(mc, 1, "PCI "); + smp_write_bus(mc, 2, "PCI "); + smp_write_bus(mc, 3, "ISA "); + + smp_write_ioapic(mc, ioapicid, 0x11, 0xfec00000); + + /* ISA backward compatibility interrupts */ + smp_write_intsrc(mc, mp_ExtINT, 0x05, 0x03, 0x00, ioapicid, 0x00); + smp_write_intsrc(mc, mp_INT, 0x05, 0x03, 0x01, ioapicid, 0x01); + smp_write_intsrc(mc, mp_INT, 0x05, 0x03, 0x00, ioapicid, 0x02); + smp_write_intsrc(mc, mp_INT, 0x05, 0x03, 0x03, ioapicid, 0x03); + smp_write_intsrc(mc, mp_INT, 0x05, 0x03, 0x04, ioapicid, 0x03); + smp_write_intsrc(mc, mp_INT, 0x05, 0x03, 0x04, ioapicid, 0x04); + smp_write_intsrc(mc, mp_INT, 0x05, 0x03, 0x06, ioapicid, 0x06); + smp_write_intsrc(mc, mp_INT, 0x05, 0x03, 0x07, ioapicid, 0x07); + smp_write_intsrc(mc, mp_INT, 0x05, 0x03, 0x08, ioapicid, 0x08); + smp_write_intsrc(mc, mp_INT, 0x05, 0x03, 0x0c, ioapicid, 0x0c); + smp_write_intsrc(mc, mp_INT, 0x05, 0x03, 0x0d, ioapicid, 0x0d); + smp_write_intsrc(mc, mp_INT, 0x05, 0x03, 0x0e, ioapicid, 0x0e); + smp_write_intsrc(mc, mp_INT, 0x05, 0x03, 0x0f, ioapicid, 0x0f); + + /* On bus 0 device 1 is the 440GX AGP/PCI bridge to bus 1 */ + /* On bus 1 device f is a DEC PCI bridge to bus 2 */ + /* Onboard pci devices */ + smp_write_intsrc(mc, mp_INT, 0x0f, 0x00, 0x30, ioapicid, 0x13); /* onboard SCSI */ + smp_write_intsrc(mc, mp_INT, 0x0f, 0x00, 0x38, ioapicid, 0x15); /* onboard NIC */ + smp_write_intsrc(mc, mp_INT, 0x0f, 0x00, 0x4b, ioapicid, 0x15); /* onboard PIIX4 */ + + /* PCI card slots */ + smp_write_intsrc(mc, mp_INT, 0x0f, 0x02, 0x1c, ioapicid, 0x17); /* slot 6 A */ + smp_write_intsrc(mc, mp_INT, 0x0f, 0x02, 0x1d, ioapicid, 0x14); /* slot 6 B */ + smp_write_intsrc(mc, mp_INT, 0x0f, 0x02, 0x1e, ioapicid, 0x15); /* slot 6 C */ + smp_write_intsrc(mc, mp_INT, 0x0f, 0x02, 0x1f, ioapicid, 0x16); /* slot 6 D */ + + smp_write_intsrc(mc, mp_INT, 0x0f, 0x02, 0x10, ioapicid, 0x14); /* slot 5 A */ + smp_write_intsrc(mc, mp_INT, 0x0f, 0x02, 0x11, ioapicid, 0x15); /* slot 5 B */ + smp_write_intsrc(mc, mp_INT, 0x0f, 0x02, 0x12, ioapicid, 0x16); /* slot 5 C */ + smp_write_intsrc(mc, mp_INT, 0x0f, 0x02, 0x13, ioapicid, 0x17); /* slot 5 D */ + /* 64bit slot */ + smp_write_intsrc(mc, mp_INT, 0x0f, 0x00, 0x24, ioapicid, 0x13); /* slot 4 A */ + smp_write_intsrc(mc, mp_INT, 0x0f, 0x00, 0x25, ioapicid, 0x15); /* slot 4 B */ + smp_write_intsrc(mc, mp_INT, 0x0f, 0x00, 0x26, ioapicid, 0x16); /* slot 4 C */ + smp_write_intsrc(mc, mp_INT, 0x0f, 0x00, 0x27, ioapicid, 0x17); /* slot 4 D */ + + smp_write_intsrc(mc, mp_INT, 0x0f, 0x00, 0x2c, ioapicid, 0x12); /* slot 3 A */ + smp_write_intsrc(mc, mp_INT, 0x0f, 0x00, 0x2d, ioapicid, 0x17); /* slot 3 B */ + smp_write_intsrc(mc, mp_INT, 0x0f, 0x00, 0x2e, ioapicid, 0x15); /* slot 3 C */ + smp_write_intsrc(mc, mp_INT, 0x0f, 0x00, 0x2f, ioapicid, 0x16); /* slot 3 D */ + + smp_write_intsrc(mc, mp_INT, 0x0f, 0x00, 0x34, ioapicid, 0x11); /* slot 2 A */ + smp_write_intsrc(mc, mp_INT, 0x0f, 0x00, 0x35, ioapicid, 0x16); /* slot 2 B */ + smp_write_intsrc(mc, mp_INT, 0x0f, 0x00, 0x36, ioapicid, 0x17); /* slot 2 C */ + smp_write_intsrc(mc, mp_INT, 0x0f, 0x00, 0x37, ioapicid, 0x15); /* slot 2 D */ + + /* If this slot hadn't been removed I'd know it's PCI address, + * I'm guessing it's slot 0x0f (i.e. 0x3c >> 2) but I haven't + * confirmed that. + */ + /* Slot one is the slot farthest from the processor */ + smp_write_intsrc(mc, mp_INT, 0x0f, 0x00, 0x3c, ioapicid, 0x10); /* slot 1 A */ + smp_write_intsrc(mc, mp_INT, 0x0f, 0x00, 0x3d, ioapicid, 0x15); /* slot 1 B */ + smp_write_intsrc(mc, mp_INT, 0x0f, 0x00, 0x3e, ioapicid, 0x16); /* slot 1 C */ + smp_write_intsrc(mc, mp_INT, 0x0f, 0x00, 0x3f, ioapicid, 0x17); /* slot 1 D */ + + /* Standard local interrupt assignments */ + smp_write_lintsrc(mc, mp_ExtINT, 0x05, 0x03, 0x00, MP_APIC_ALL, 0x00); + smp_write_lintsrc(mc, mp_NMI, 0x05, 0x00, 0x00, MP_APIC_ALL, 0x01); + + /* The following information in the extension section linux doesn't currnetly need + * and has just been copied from the bios for now. + */ + smp_write_address_space(mc, 0x00, ADDRESS_TYPE_IO, 0x00000000, 0, 0x00010000, 0); + smp_write_address_space(mc, 0x00, ADDRESS_TYPE_MEM, 0x08000000, 0, 0xee000000, 0); + smp_write_address_space(mc, 0x00, ADDRESS_TYPE_PREFETCH, 0xf6000000, 0, 0x06000000, 0); + smp_write_address_space(mc, 0x00, ADDRESS_TYPE_MEM, 0xfc000000, 0, 0x02e00000, 0); + smp_write_address_space(mc, 0x00, ADDRESS_TYPE_MEM, 0xfef00000, 0, 0x01100000, 0); + smp_write_address_space(mc, 0x00, ADDRESS_TYPE_MEM, 0x000a0000, 0, 0x00200000, 0); + smp_write_address_space(mc, 0x00, ADDRESS_TYPE_MEM, 0x000d4000, 0, 0x00014000, 0); + + smp_write_bus_hierarchy(mc, 0x03, BUS_SUBTRACTIVE_DECODE, 0x00); + + smp_write_compatibility_address_space(mc, 0x00, ADDRESS_RANGE_ADD, RANGE_LIST_IO_ISA); + smp_write_compatibility_address_space(mc, 0x00, ADDRESS_RANGE_ADD, RANGE_LIST_IO_VGA); + + mc->mpe_checksum = smp_compute_checksum(smp_next_mpc_entry(mc), mc->mpe_length); + mc->mpc_checksum = smp_compute_checksum(mc, mc->mpc_length); +} + +void write_smp_table(void *v) +{ + smp_write_floating_table(v); + smp_write_config_table(v); +} + + diff --git a/src/mainboard/tyan/guiness/Config b/src/mainboard/tyan/guiness/Config new file mode 100644 index 0000000000..e105f3f65d --- /dev/null +++ b/src/mainboard/tyan/guiness/Config @@ -0,0 +1,55 @@ +arch i386 +mainboardinit northbridge/amd/amd76x/reset_test.inc +mainboardinit cpu/k7/earlymtrr.inc +mainboardinit northbridge/amd/amd76x/mpinit.inc +#mainboardinit southbridge/amd/amd766/disable_watchdog.inc +mainboardinit southbridge/amd/amd766/lpc_com1.inc +mainboardinit superio/winbond/w83627hf/setup_serial.inc +mainboardinit pc80/serial.inc +#mainboardinit ram/ramtest.inc +#mainboardinit mainboard/tyan/guiness/do_ramtest.inc +mainboardinit southbridge/amd/amd766/smbus.inc +#mainboardinit sdram/generic_dump_spd.inc + +mainboardinit mainboard/tyan/guiness/mainboard_raminit.inc +northbridge amd/amd76x +#mainboardinit mainboard/tyan/guiness/do_ramtest.inc +#mainboardinit cpu/k7/earlymtrr.inc +southbridge amd/amd766 + + +nsuperio winbond/w83627hf com1={1} com2={0} floppy=1 lpt=1 + +option RAMTEST=1 +option NO_KEYBOARD +option ENABLE_FIXED_AND_VARIABLE_MTRRS +#option FINAL_MAINBOARD_FIXUP + + +object mainboard.o +object mptable.o +#object irq_tables.o +#keyboard pc80 +dir ../../../pc80 + +option AMD766_DEV=0x3800 +# FIXME are the SMBUS DIMM locations documented anywhere? +option SMBUS_MEM_DEVICE_START=(0xa << 3) +option SMBUS_MEM_DEVICE_END=(SMBUS_MEM_DEVICE_START +3) +option SMBUS_MEM_DEVICE_INC=1 +option SIO_BASE=0x2e +option SMP=1 +option IOAPIC=1 +option USE_AMD_NDA_CODE=1 +option FINAL_MAINBOARD_FIXUP=1 +option HAVE_HARD_RESET=1 +option HAVE_MP_TABLE=1 +option STACK_SIZE=0x10000 +option MAX_CPUS=2 +option XIP_ROM_SIZE=0x8000 +option XIP_ROM_BASE=0xffff8000 +nooption MEMORY_HOLE + +cpu p5 +cpu p6 +cpu k7 diff --git a/src/mainboard/tyan/guiness/do_ramtest.inc b/src/mainboard/tyan/guiness/do_ramtest.inc new file mode 100644 index 0000000000..35ccad56d6 --- /dev/null +++ b/src/mainboard/tyan/guiness/do_ramtest.inc @@ -0,0 +1,28 @@ +#if 0 + movl $0x000f0000, %eax + movl $0x000f1000, %ebx + CALLSP(ramtest) +#endif + +#if 1 + movl $0x00100000, %eax + movl $0x00180000, %ebx + CALLSP(ramtest) + + xorl %eax, %eax + movl $0x00080000, %ebx + CALLSP(ramtest) +#endif + +#if 1 + + xorl %eax, %eax + movl $0x00001000, %ebx + CALLSP(ramtest) +#endif + +#if 0 + movl $0xffe00000, %eax + movl $0xfff00000, %ebx + CALLSP(ramtest) +#endif diff --git a/src/mainboard/tyan/guiness/mainboard.c b/src/mainboard/tyan/guiness/mainboard.c new file mode 100644 index 0000000000..8c9e43e8a4 --- /dev/null +++ b/src/mainboard/tyan/guiness/mainboard.c @@ -0,0 +1,546 @@ +#include +#include +#include +#include +#include +#include +#include + +static void lpc_routing_fixup(void) +{ + struct pci_dev *dev; + dev = pci_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7410,0); + if (dev != NULL) { +#if 0 + /* Send ACPI, keyboard controller, + * FDC2, FDC1, ECP + * to the LPC bus + */ + pci_write_config_byte(dev, 0x51, (1<<7)|(1<<6)|(1<<5)|(1<<4)|(1<<3)|(1)); +#else + /* Send keyboard controller, + * FDC1, ECP + * to the LPC bus + * Send APCI ports 0x62 & 0x66 to the ISA bus + */ + pci_write_config_byte(dev, 0x51, (0<<7)|(1<<6)|(0<<5)|(1<<4)|(0<<3)|(1)); +#endif + /* Route io for both serial ports to the LPC bus */ + pci_write_config_byte(dev, 0x52, (1<<7)|(1<<4)|(1<<3)|(0<<0)); + /* Don't route any audio ports to the LPC bus */ + pci_write_config_byte(dev, 0x53, (0<<6)|(0<<4)|(0<<3)|(0<<2)|(0<<0)); + /* Route superio configuration accesses to the LPC bus */ + pci_write_config_byte(dev, 0x54, (0<<5)|(1<<4)|(0<<2)|(0<<0)); + /* Don't use LPC decode register 4 */ + pci_write_config_byte(dev, 0x55, (0<<4)|(0<<0)); + /* Don't use LPC decode register 5 */ + pci_write_config_byte(dev, 0x56, (0<<4)|(0<<0)); + /* Route 512 byte io address range 0x0c00 - 0xc200 the LPC bus */ + pci_write_config_dword(dev, 0x58, 0x00000c01); + /* Route 1MB memory address range 0 - 0 to the LPC bus */ + pci_write_config_dword(dev, 0x5c, 0x00000000); + + } +} + + +static void enable_ioapic(void) +{ + struct pci_dev *dev; + dev = pci_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7410,0); + if (dev != NULL) { + /* Enable the ioapic */ + pci_write_config_byte(dev, 0x4b, (0 << 3)|(0 <<2)|(0 << 1)|(1<< 0)); + } + +} + +static void serial_irq_fixup(void) +{ + struct pci_dev *dev; + dev = pci_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7413, 0); + if (dev != NULL) { + /* Setup serial irq's for the LPC bus. */ + pci_write_config_byte(dev, 0x4a, (1<<6)|(0<<2)|(0<<0)); + } +} + +static void mouse_sends_irq12(void) +{ + struct pci_dev *dev; + dev = pci_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7413, 0); + if (dev != NULL) { + unsigned short word; + /* Setup so irq12 is sent by the ps2 mouse port. */ + pci_read_config_word(dev, 0x46, &word); + pci_write_config_word(dev, 0x46, word | (1<<9)); + } +} + +static void disable_watchdog(void) +{ + struct pci_dev *dev; + dev = pci_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7413, 0); + if (dev != NULL) { + unsigned char byte; + /* Disable the watchdog timer */ + pci_read_config_byte(dev, 0x41, &byte); + pci_write_config_byte(dev, 0x41, byte | (1<<6)|(1<<2)); + } +} + +static void enable_reset_port_0xcf9(void) +{ + /* FIXME this code is correct but the bit isn't getting set on my test machine. */ + struct pci_dev *dev; + dev = pci_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7413, 0); + if (dev != NULL) { + unsigned char byte; + /* Enable reset port 0xcf9 */ + pci_read_config_byte(dev, 0x41, &byte); + pci_write_config_byte(dev, 0x41, byte | (1<<5)); + } +} + +static void enable_port92_reset(void) +{ + struct pci_dev *dev; + dev = pci_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7410, 0); + if (dev != NULL) { + unsigned char byte; + /* Enable reset using port 0x92 */ + pci_read_config_byte(dev, 0x41, &byte); + pci_write_config_byte(dev, 0x41, byte | (1<<5)); + } +} + +static void print_whami(void) +{ + struct pci_dev *dev; + u32 whami; + /* Find out which processor I'm running on, and if it was the boot + * procesor so I can verify everything was setup correctly + */ + dev = pci_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FE_GATE_700C,0); + if (dev != NULL) { + pci_read_config_dword(dev, 0x80, &whami); + printk_spew("whami = 0x%08lx\n", whami); + } +} + + +#define IDE_ERR_ICRC 0x80 /* ATA Ultra DMA bad CRC */ +#define IDE_ERR_BBK 0x80 /* ATA bad block */ +#define IDE_ERR_UNC 0x40 /* ATA uncorrected error */ +#define IDE_ERR_MC 0x20 /* ATA media change */ +#define IDE_ERR_IDNF 0x10 /* ATA id not found */ +#define IDE_ERR_MCR 0x08 /* ATA media change request */ +#define IDE_ERR_ABRT 0x04 /* ATA command aborted */ +#define IDE_ERR_NTK0 0x02 /* ATA track 0 not found */ +#define IDE_ERR_NDAM 0x01 /* ATA address mark not found */ + +#define IDE_STATUS_BSY 0x80 /* busy */ +#define IDE_STATUS_RDY 0x40 /* ready */ +#define IDE_STATUS_DF 0x20 /* device fault */ +#define IDE_STATUS_WFT 0x20 /* write fault (old name) */ +#define IDE_STATUS_SKC 0x10 /* seek complete */ +#define IDE_STATUS_DRQ 0x08 /* data request */ +#define IDE_STATUS_CORR 0x04 /* corrected */ +#define IDE_STATUS_IDX 0x02 /* index */ +#define IDE_STATUS_ERR 0x01 /* error (ATA) */ +#define IDE_STATUS_CHK 0x01 /* check (ATAPI) */ + +#define IDE_CTRL_HD15 0x08 /* bit should always be set to one */ +#define IDE_CTRL_SRST 0x04 /* soft reset */ +#define IDE_CTRL_NIEN 0x02 /* disable interrupts */ + +/* Most mandtory and optional ATA commands (from ATA-3), */ + +#define IDE_CMD_CFA_ERASE_SECTORS 0xC0 +#define IDE_CMD_CFA_REQUEST_EXT_ERR_CODE 0x03 +#define IDE_CMD_CFA_TRANSLATE_SECTOR 0x87 +#define IDE_CMD_CFA_WRITE_MULTIPLE_WO_ERASE 0xCD +#define IDE_CMD_CFA_WRITE_SECTORS_WO_ERASE 0x38 +#define IDE_CMD_CHECK_POWER_MODE1 0xE5 +#define IDE_CMD_CHECK_POWER_MODE2 0x98 +#define IDE_CMD_DEVICE_RESET 0x08 +#define IDE_CMD_EXECUTE_DEVICE_DIAGNOSTIC 0x90 +#define IDE_CMD_FLUSH_CACHE 0xE7 +#define IDE_CMD_FORMAT_TRACK 0x50 +#define IDE_CMD_IDENTIFY_DEVICE 0xEC +#define IDE_CMD_IDENTIFY_DEVICE_PACKET 0xA1 +#define IDE_CMD_IDENTIFY_PACKET_DEVICE 0xA1 +#define IDE_CMD_IDLE1 0xE3 +#define IDE_CMD_IDLE2 0x97 +#define IDE_CMD_IDLE_IMMEDIATE1 0xE1 +#define IDE_CMD_IDLE_IMMEDIATE2 0x95 +#define IDE_CMD_INITIALIZE_DRIVE_PARAMETERS 0x91 +#define IDE_CMD_INITIALIZE_DEVICE_PARAMETERS 0x91 +#define IDE_CMD_NOP 0x00 +#define IDE_CMD_PACKET 0xA0 +#define IDE_CMD_READ_BUFFER 0xE4 +#define IDE_CMD_READ_DMA 0xC8 +#define IDE_CMD_READ_DMA_QUEUED 0xC7 +#define IDE_CMD_READ_MULTIPLE 0xC4 +#define IDE_CMD_READ_SECTORS 0x20 +#define IDE_CMD_READ_VERIFY_SECTORS 0x40 +#define IDE_CMD_RECALIBRATE 0x10 +#define IDE_CMD_SEEK 0x70 +#define IDE_CMD_SET_FEATURES 0xEF +#define IDE_CMD_SET_MULTIPLE_MODE 0xC6 +#define IDE_CMD_SLEEP1 0xE6 +#define IDE_CMD_SLEEP2 0x99 +#define IDE_CMD_STANDBY1 0xE2 +#define IDE_CMD_STANDBY2 0x96 +#define IDE_CMD_STANDBY_IMMEDIATE1 0xE0 +#define IDE_CMD_STANDBY_IMMEDIATE2 0x94 +#define IDE_CMD_WRITE_BUFFER 0xE8 +#define IDE_CMD_WRITE_DMA 0xCA +#define IDE_CMD_WRITE_DMA_QUEUED 0xCC +#define IDE_CMD_WRITE_MULTIPLE 0xC5 +#define IDE_CMD_WRITE_SECTORS 0x30 +#define IDE_CMD_WRITE_VERIFY 0x3C + +static void reset_ide_controller(unsigned ide_ctl, unsigned ide_data) +{ + unsigned char status, error; + printk_spew("resetting ide controller at 0x%04x\n", ide_ctl); + outb(IDE_CTRL_HD15 | IDE_CTRL_SRST | IDE_CTRL_NIEN, ide_ctl); + printk_spew("1\n"); + udelay(25); + printk_spew("2\n"); + outb(IDE_CTRL_HD15 | IDE_CTRL_NIEN, ide_ctl); + printk_spew("3\n"); + do { + mdelay(2); + printk_spew("4\n"); + error = inb_p(ide_data+1); + status = inb_p(ide_data+7); + printk_spew("error = 0x%02x status = 0x%02x\n", error, status); + } while(status & IDE_STATUS_BSY); + printk_spew("reset ide controller at 0x%04x\n", ide_ctl); + +} + +static void setup_ide_devices(void) +{ + struct pci_dev *dev; + dev = pci_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7411, 0); + if (dev != NULL) { + /* Enable ide devices so the linux ide driver will work */ + u16 word; + pci_read_config_word(dev, 0x40, &word); + /* enable: + * both devices + * both devices posted write buffers + */ + pci_write_config_word(dev, 0x40, word |(1<<14)|(1<<12)|(1<<1)|(1<<0)); + +#if 1 + /* Setup the appropriate ide timing */ + pci_write_config_dword(dev, 0x48, 0x5e5e5e5e); + pci_write_config_byte(dev, 0x4c, 0xaa); +#endif + + + +#if 0 + /* Enable native ide native mode, at the legacy addresses */ + /* This would be cool if I could receive an interrupt from + * the ide controller. + */ + { + u32 class_revision; + + pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_revision); + pci_write_config_dword(dev, PCI_CLASS_REVISION, + class_revision | ( 1 << 8) | (1 << 10)); + pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, + 0x1f0 | PCI_BASE_ADDRESS_SPACE_IO); + pci_write_config_dword(dev, PCI_BASE_ADDRESS_1, + 0x3f6 | PCI_BASE_ADDRESS_SPACE_IO); + pci_write_config_dword(dev, PCI_BASE_ADDRESS_2, + 0x170 | PCI_BASE_ADDRESS_SPACE_IO); + pci_write_config_dword(dev, PCI_BASE_ADDRESS_3, + 0x376 | PCI_BASE_ADDRESS_SPACE_IO); + } +#endif + } + dev = pci_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7410, 0); + if (dev != NULL) { + unsigned char byte; + /* optimize ide dma acknowledge */ + pci_read_config_byte(dev, 0x4a, &byte); + pci_write_config_byte(dev, 0x4a, byte & ~(1<<7)); + } +} + +static void enable_ide_devices(void) +{ + u32 ide0_data = 0, ide0_ctl = 0, ide1_data = 0, ide1_ctl = 0, dma_base = 0; + struct pci_dev *dev; + dev = pci_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7411, 0); + if (dev != NULL) { + ide0_data = 0x1f0; + ide0_ctl = 0x3f6; + ide1_data = 0x170; + ide1_ctl = 0x376; + pci_read_config_dword(dev, PCI_BASE_ADDRESS_4, &dma_base); + if (dma_base & PCI_BASE_ADDRESS_SPACE_IO) { + dma_base &= ~PCI_BASE_ADDRESS_SPACE_IO; + } else { + dma_base = 0; + } + } +#if 0 + /* IDE resets take forever, plus my reset code looks broken... */ + if (ide0_data && ide0_ctl) { + reset_ide_controller(ide0_ctl,ide0_data + 7); + } + if (ide0_data && ide1_ctl) { + reset_ide_controller(ide1_ctl, ide1_data + 7); + } +#endif + if (dma_base) { + printk_debug("ide dma_base = 0x%08lx\n", dma_base); +#if 1 + /* Disable DMA */ + outb_p(inb_p(dma_base + 2) & ~((1<<5)|(1<<6)), dma_base +2); + outb_p(inb_p(dma_base+8 + 2) & ~((1<<5)|(1<<6)), dma_base+8 +2); +#endif +#if 0 + /* Enable DMA */ + outb_p(inb_p(dma_base + 2) | ((1<<5)|(1<<6)), dma_base +2); + outb_p(inb_p(dma_base+8 + 2) | ((1<<5)|(1<<6)), dma_base+8 +2); +#endif + } +} + + +static void fixup_adaptec_7899P(struct pci_dev *pdev) +{ + /* Enable writes to the device id */ + pci_write_config_byte(pdev, 0xff, 1); + /* Set the device id */ + pci_write_config_word(pdev, PCI_DEVICE_ID, PCI_DEVICE_ID_ADAPTEC2_7899P); + /* Set the subsytem vendor id */ + pci_write_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, PCI_VENDOR_ID_TYAN); + /* Set the subsytem id */ + pci_write_config_word(pdev, PCI_SUBSYSTEM_ID, 0x2462); + /* Disable writes to the device id */ + pci_write_config_byte(pdev, 0xff, 0); +} + +static void onboard_scsi_fixup(void) +{ + struct pci_dev *dev; + + /* Set the scsi device id's */ + dev = pci_find_slot(0, PCI_DEVFN(0xd, 0)); + if (dev != NULL) { + fixup_adaptec_7899P(dev); + } + /* Set the scsi device id's */ + dev = pci_find_slot(0, PCI_DEVFN(0xd, 1)); + if (dev != NULL) { + fixup_adaptec_7899P(dev); + } +} + + +static void cpu_reset_sends_init(void) +{ + struct pci_dev *dev; + dev = pci_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7410, 0); + if (dev != NULL) { + unsigned char byte; + pci_read_config_byte(dev, 0x47, &byte); + pci_write_config_byte(dev, 0x47, byte | (1<<7)); + } + +} + +static void decode_stop_grant_fixup(void) +{ + struct pci_dev *dev; + dev = pci_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7413, 0); + if (dev != NULL) { + unsigned char byte; + pci_read_config_byte(dev, 0x41, &byte); + pci_write_config_byte(dev, 0x41, byte | (1<<1)); + } + +} + +static void pm_controller_classcode_fixup(void) +{ + struct pci_dev *dev; + dev = pci_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7413, 0); + if (dev != NULL) { + pci_write_config_dword(dev, 0x60, 0x06800000); + } + +} + +#if 0 +static void set_power_on_after_power_fail(int enable) +{ + /* FIXME: This may be part of the picture but it isn't + * the whole story :( + */ + struct pci_dev *dev; + dev = pci_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7413, 0); + if (dev != NULL) { + unsigned char byte; + pci_read_config_byte(dev, 0x43, &byte); + if (enable) { + byte &= ~(1<<6); + } + else { + byte |= (1<<6); + } + pci_write_config_byte(dev, 0x43, byte); + } +} +#endif + +static void posted_memory_write_enable(void) +{ + struct pci_dev *dev; + dev = pci_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7410, 0); + if (dev != NULL) { + unsigned char byte; + pci_read_config_byte(dev, 0x46, &byte); + pci_write_config_byte(dev, 0x46, byte | (1<<0)); + } + +} + +static void setup_pci_irq_to_isa_routing(void) +{ + struct pci_dev *dev; + dev = pci_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7413, 0); + if (dev != NULL) { + /* + * PIRQA -> 5 + * PIRQB -> 10 + * PIRQC -> 11 + * PIRQD -> 3 + */ + pci_write_config_word(dev, 0x56, 0x3ba5); + } +} + +static void setup_pci_arbiter(void) +{ + struct pci_dev *dev; + dev = pci_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FE_GATE_700C, 0); + if (dev) { + /* Enable: + * PCI parking + * memory prefetching + * EV6 mode + * Enable power management registers + * The southbridge lock + * Read data error disable + * PCI retries + * AGP retries + * AGP chaining + * PCI chaining + */ + pci_write_config_dword(dev, 0x84, + (0<<24) + |(1<<23) + |(1<<17)|(1<<16) + |(0<<15)|(1<<14)|(1<<13)|(1<<12) + |(0<<11)|(0<<10)|(0<<9)|(0<<8) + |(1<<7)|(0<<6)|(0<<5)|(1<<4) + |(0<<3)|(1<<2)|(1<<1)|(1<<0)); + } +} + +static void usb_setup(void) +{ + /* FIXME this is untested incomplete implementation. */ + struct pci_dev *dev; + dev = pci_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7414, 0); + if (dev) { + u32 cmd; + pci_read_config_dword(dev, PCI_COMMAND, &cmd); + pci_write_config_dword(dev, PCI_COMMAND, + cmd | PCI_COMMAND_IO | PCI_COMMAND_MEMORY | + PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE); + } +} + +static void hide_devices(void) +{ + struct pci_dev *dev; + dev = pci_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7410, 0); + if (dev) { + u8 byte; + pci_read_config_byte(dev, 0x48, &byte); +#if 0 + /* Hide both the ide controller and the usb controller */ + pci_write_config_byte(dev, 0x48, byte | (0<<3)|(1<<2)|(1<<1)); +#endif +#if 0 + /* Hide the usb controller */ + pci_write_config_byte(dev, 0x48, byte | (0<<3)|(1<<2)|(0<<1)); +#endif +#if 1 + /* Hide no devices */ + pci_write_config_byte(dev, 0x48, byte | (0<<3)|(0<<2)|(0<<1)); +#endif + } +} + +void mainboard_fixup(void) +{ + disable_watchdog(); + lpc_routing_fixup(); + enable_ioapic(); + serial_irq_fixup(); + enable_reset_port_0xcf9(); + enable_port92_reset(); +#if 0 + print_whami(); +#endif + setup_ide_devices(); + onboard_scsi_fixup(); + cpu_reset_sends_init(); + rtc_init(); + decode_stop_grant_fixup(); + posted_memory_write_enable(); + pm_controller_classcode_fixup(); +#if 1 + mouse_sends_irq12(); +#endif + setup_pci_irq_to_isa_routing(); + setup_pci_arbiter(); + isa_dma_init(); +#if 0 + usb_setup(); +#endif +#if 0 + hide_devices(); +#endif +} + + +void final_mainboard_fixup(void) +{ +#if 1 + enable_ide_devices(); +#endif +} + +void hard_reset(void) +{ + pci_set_method(); + /* Allow the watchdog timer to reboot us, and enable 0xcf9 */ + pcibios_write_config_byte(0, (AMD766_DEV >> 8) | 3, 0x41, (1<<5)|(1<<1)); + /* Try rebooting though port 0xcf9 */ + outb((0<<3)|(1<<2)|(1<<1), 0xcf9); + return; +} diff --git a/src/mainboard/tyan/guiness/mainboard_raminit.inc b/src/mainboard/tyan/guiness/mainboard_raminit.inc new file mode 100644 index 0000000000..0166b0e925 --- /dev/null +++ b/src/mainboard/tyan/guiness/mainboard_raminit.inc @@ -0,0 +1,140 @@ +jmp mainboard_raminit_out +#define DEFAULT_RAM_TRACE_SETTINGS 0 +#define USE_ECC_SDRAM 1 + + + + /* + * Routine: spd_to_dimm_side0 + * Arguments: %bl SMBUS_MEM_DEVICE + * + * Results: %edx DIMM register index + * + * Used: %ebx, %edx, %esp + * Trashed: %eflags + * Preserved: %eax, %ebx, %ecx, %esi, %edi, %ebp + * + * Effects: Dimms are not necessarily in the same order on the smbus + * as they are in chipset register indexes. This function + * maps the SMBUS device id to the logical index in + * the chipset, that is used to refer to a particular dimm. + */ +spd_to_dimm_side0: + movl %ebx, %edx + andl $0xff, %edx + subl $(SMBUS_MEM_DEVICE_START), %edx + /* 0 -> 0 */ + cmpl $0, %edx + jne 1f + movl $0, %edx + RETSP + /* 1 -> 6 */ +1: cmpl $1, %edx + jne 1f + movl $6, %edx + RETSP + /* 2 -> 4 */ +1: cmpl $2, %edx + jne 1f + movl $4, %edx + RETSP + /* 3 -> 2 */ +1: movl $2, %edx + RETSP + + + /* + * Routine: spd_to_dimm_side1 + * Arguments: %bl SMBUS_MEM_DEVICE + * + * Results: %edx DIMM register index + * + * Used: %ebx, %edx, %esp + * Trashed: %eflags + * Preserved: %eax, %ebx, %ecx, %esi, %edi, %ebp + * + * Effects: Dimms are not necessarily in the same order on the smbus + * as they are in chipset register indexes. This function + * maps the SMBUS device id to the logical index in + * the chipset, that is used to refer to a particular dimm. + */ +spd_to_dimm_side1: + movl %ebx, %edx + andl $0xff, %edx + subl $(SMBUS_MEM_DEVICE_START), %edx + /* 0 -> 1 */ + cmpl $0, %edx + jne 1f + movl $1, %edx + RETSP + /* 1 -> 7 */ +1: cmpl $1, %edx + jne 1f + movl $7, %edx + RETSP + /* 2 -> 5 */ +1: cmpl $2, %edx + jne 1f + movl $5, %edx + RETSP + /* 3 -> 3 */ +1: movl $3, %edx + RETSP + + +/* Set the calibration delay. These values may need to change per mainboard + * so we put them here. + */ + +sdram_software_calibration_delay: +#if DEFAULT_RAM_TRACE_SETTINGS + .byte 0x69, 0x00, 0x00, 0x6b +#else + .byte 0x69, 0x00, 0x00, 0x54 +#endif + +mainboard_constant_register_values: +#if DEFAULT_RAM_TRACE_SETTINGS +#else + .long 0x18c, 0x090e2d0e + .long 0x190, 0x3f0f2d0e + .long 0x194, 0x2d0e2d0e + .long 0x198, 0x2d0e2d0e +#endif +#if USE_ECC_SDRAM + .long 0x48, (3 << 14)|(2 << 10)|(0 << 8)|(0 << 4)|(0 << 0) +#else + .long 0x48, 0 +#endif +mainboard_constant_register_values_end: + + + /* + * Routine: mainboard_verify_dram_timing + * Arguments: %edi the computed timing for the current dimm. + * Trashed: %eflags + * Results: cf clear + * %edi has a timing supported by this motherboard + * On Error: + * cf set + * %edi holds a timing not supported by this motherboard + * + * Effects: Verifies we can use the current dimm settings + * on the tyan guinness motherboard. + * Currently the only potential problem is putting + * in unregistered SDRAM. + */ +mainboard_verify_dram_timing: + testl $(1<<27), %edi + jnz mainboard_verify_dram_timing_ok +mainboard_verify_dram_timing_error: + stc + jmp mainboard_verify_dram_timing_out +mainboard_verify_dram_timing_ok: + clc +mainboard_verify_dram_timing_out: + RET_LABEL(mainboard_verify_dram_timing) + +#undef DEFAULT_RAM_TRACE_SETTINGS +mainboard_raminit_out: + diff --git a/src/mainboard/tyan/guiness/mptable.c b/src/mainboard/tyan/guiness/mptable.c new file mode 100644 index 0000000000..d90827bd1d --- /dev/null +++ b/src/mainboard/tyan/guiness/mptable.c @@ -0,0 +1,158 @@ +#include +#include +#include +#include + +#define USE_ALL_CPUS 1 +void smp_write_config_table(void *v) +{ + static const char sig[4] = "PCMP"; + static const char oem[8] = "TYAN "; + static const char productid[12] = "GUINESS "; + struct mp_config_table *mc; + + mc = (void *)(((char *)v) + SMP_FLOATING_TABLE_LEN); + memset(mc, 0, sizeof(*mc)); + + memcpy(mc->mpc_signature, sig, sizeof(sig)); + mc->mpc_length = sizeof(*mc); /* initially just the header */ + mc->mpc_spec = 0x04; + mc->mpc_checksum = 0; /* not yet computed */ + memcpy(mc->mpc_oem, oem, sizeof(oem)); + memcpy(mc->mpc_productid, productid, sizeof(productid)); + mc->mpc_oemptr = 0; + mc->mpc_oemsize = 0; + mc->mpc_entry_count = 0; /* No entries yet... */ + mc->mpc_lapic = LAPIC_ADDR; + mc->mpe_length = 0; + mc->mpe_checksum = 0; + mc->reserved = 0; + + + smp_write_processors(mc); + smp_write_bus(mc, 0, "PCI "); + smp_write_bus(mc, 1, "PCI "); + smp_write_bus(mc, 2, "ISA "); + + smp_write_ioapic(mc, 2, 0x11, 0xfec00000); + + /* ISA backward compatibility interrupts */ + smp_write_intsrc(mc, mp_ExtINT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, + 0x02, 0x00, 0x02, 0x00); + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, + 0x02, 0x01, 0x02, 0x01); + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, + 0x02, 0x00, 0x02, 0x02); + /* PCI 0x03 <-> 0x13 ? */ + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_LEVEL|MP_IRQ_POLARITY_LOW, + 0x02, 0x03, 0x02, 0x03); + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, + 0x02, 0x04, 0x02, 0x04); + /* PCI 0x05 <-> 0x12 ? */ + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_LEVEL|MP_IRQ_POLARITY_LOW, + 0x02, 0x05, 0x02, 0x05); + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, + 0x02, 0x06, 0x02, 0x06); + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, + 0x02, 0x07, 0x02, 0x07); + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, + 0x02, 0x08, 0x02, 0x08); + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, + 0x02, 0x09, 0x02, 0x09); + /* PCI 0x0a <-> 0x11 ? */ + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_LEVEL|MP_IRQ_POLARITY_LOW, + 0x02, 0x0a, 0x02, 0x0a); + /* PCI 0x0b <-> 0x10 ? */ + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_LEVEL|MP_IRQ_POLARITY_LOW, + 0x02, 0x0b, 0x02, 0x0b); + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, + 0x02, 0x0c, 0x02, 0x0c); + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, + 0x02, 0x0d, 0x02, 0x0d); + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, + 0x02, 0x0e, 0x02, 0x0e); + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, + 0x02, 0x0f, 0x02, 0x0f); + + + /* Onboard SCSI 0 */ + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_LEVEL|MP_IRQ_POLARITY_LOW, + 0x00, (0xd<<2)|0, 0x02, 0x10); + /* Onboard SCSI 1 */ + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_LEVEL|MP_IRQ_POLARITY_LOW, + 0x00, (0xd<<2)|1, 0x02, 0x11); + /* Onboard eth0 */ + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_LEVEL|MP_IRQ_POLARITY_LOW, + 0x00, (0x0f<<2)|0, 0x02, 0x12); + /* Onboard eth1 */ + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_LEVEL|MP_IRQ_POLARITY_LOW, + 0x00, (0x10<<2)|0, 0x02, 0x13); + +#if 0 + /* Onboard IDE0 */ + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_LEVEL|MP_IRQ_POLARITY_LOW, + 0x00, (0x7<<2)|1, 0x02, 0x10); +#endif + + /* PCI slot 0 */ + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_LEVEL|MP_IRQ_POLARITY_LOW, + 0x00, (0x08<<2)|0, 0x02, 0x10); + /* PCI slot 1 */ + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_LEVEL|MP_IRQ_POLARITY_LOW, + 0x00, (0x09<<2)|0, 0x02, 0x11); + /* PCI Slot 2 */ + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_LEVEL|MP_IRQ_POLARITY_HIGH, + 0x00, (0x0a<<2)|0, 0x02, 0x12); + /* PCI Slot 3 */ + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_LEVEL|MP_IRQ_POLARITY_HIGH, + 0x00, (0x0b<<2)|0, 0x02, 0x13); + /* PCI Slot 4 */ + smp_write_intsrc(mc, mp_INT, MP_IRQ_TRIGGER_LEVEL|MP_IRQ_POLARITY_HIGH, + 0x00, (0x0c<<2)|0, 0x02, 0x10); + + + /* Standard local interrupt assignments */ + smp_write_lintsrc(mc, mp_ExtINT, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, + 0x02, 0x00, MP_APIC_ALL, 0x00); + smp_write_lintsrc(mc, mp_NMI, MP_IRQ_TRIGGER_EDGE|MP_IRQ_POLARITY_HIGH, + 0x01, 0x00, MP_APIC_ALL, 0x01); + + + /* The following information in the extension section linux doesn't currnetly need + * and has just been copied from the bios for now. + */ + smp_write_address_space(mc, 0x00, ADDRESS_TYPE_IO, 0x00000000, 0x00000000, 0x00010000, 0x00000000); + smp_write_address_space(mc, 0x00, ADDRESS_TYPE_MEM, 0x10000000, 0x00000000, 0xe4100000, 0x00000000); + smp_write_address_space(mc, 0x00, ADDRESS_TYPE_PREFETCH, 0xf4100000, 0x00000000, 0x07f00000, 0x00000000); + smp_write_address_space(mc, 0x00, ADDRESS_TYPE_MEM, 0xfc000000, 0x00000000, 0x02e00000, 0x00000000); + smp_write_address_space(mc, 0x00, ADDRESS_TYPE_MEM, 0xfee01000, 0x00000000, 0x011ff000, 0x00000000); + smp_write_address_space(mc, 0x00, ADDRESS_TYPE_MEM, 0x000a0000, 0x00000000, 0x00024000, 0x00000000); + smp_write_address_space(mc, 0x00, ADDRESS_TYPE_MEM, 0x000c8000, 0x00000000, 0x00002000, 0x00000000); + smp_write_address_space(mc, 0x00, ADDRESS_TYPE_MEM, 0x000cc000, 0x00000000, 0x00002000, 0x00000000); + smp_write_address_space(mc, 0x00, ADDRESS_TYPE_MEM, 0x000d0000, 0x00000000, 0x00001000, 0x00000000); + smp_write_address_space(mc, 0x00, ADDRESS_TYPE_MEM, 0x000d2000, 0x00000000, 0x00001000, 0x00000000); + smp_write_address_space(mc, 0x00, ADDRESS_TYPE_MEM, 0x000d4000, 0x00000000, 0x00001000, 0x00000000); + smp_write_address_space(mc, 0x00, ADDRESS_TYPE_MEM, 0x000d6000, 0x00000000, 0x00001000, 0x00000000); + smp_write_address_space(mc, 0x00, ADDRESS_TYPE_MEM, 0x000d8000, 0x00000000, 0x00002000, 0x00000000); + smp_write_address_space(mc, 0x00, ADDRESS_TYPE_MEM, 0x000e0000, 0x00000000, 0x00012000, 0x00000000); + smp_write_address_space(mc, 0x00, ADDRESS_TYPE_MEM, 0x000f4000, 0x00000000, 0x00002000, 0x00000000); + smp_write_address_space(mc, 0x00, ADDRESS_TYPE_MEM, 0x000f8000, 0x00000000, 0x00004000, 0x00000000); + smp_write_bus_hierarchy(mc, 0x02, BUS_SUBTRACTIVE_DECODE, 0x00); + smp_write_compatibility_address_space(mc, 0x00, ADDRESS_RANGE_ADD, RANGE_LIST_IO_ISA); + smp_write_compatibility_address_space(mc, 0x00, ADDRESS_RANGE_ADD, RANGE_LIST_IO_VGA); + + + mc->mpe_checksum = smp_compute_checksum(smp_next_mpc_entry(mc), mc->mpe_length); + mc->mpc_checksum = smp_compute_checksum(mc, mc->mpc_length); + printk_debug("Wrote the mp table end at: %p - %p\n", + mc, smp_next_mpe_entry(mc)); +} + +void write_smp_table(void *v) +{ + printk_debug("Writing the mp table\n"); + smp_write_floating_table(v); + smp_write_config_table(v); +} + + diff --git a/src/northbridge/amd/amd76x/Config b/src/northbridge/amd/amd76x/Config new file mode 100644 index 0000000000..e7140ddefa --- /dev/null +++ b/src/northbridge/amd/amd76x/Config @@ -0,0 +1,7 @@ +mainboardinit northbridge/amd/amd76x/set_memory_size.inc +mainboardinit northbridge/amd/amd76x/raminit.inc +mainboardinit sdram/generic_sdram.inc +mainboardinit sdram/generic_zero_ecc_sdram.inc +mainboardinit arch/i386/lib/cpu_reset.inc + +object northbridge.o \ No newline at end of file diff --git a/src/northbridge/amd/amd76x/mpinit.inc b/src/northbridge/amd/amd76x/mpinit.inc new file mode 100644 index 0000000000..44192d074c --- /dev/null +++ b/src/northbridge/amd/amd76x/mpinit.inc @@ -0,0 +1,256 @@ +/* Very early on the K7 needs to setup it's multiprocessor state */ + + /* Extended BIU Control + * 0x44 + * [23:20] P1 Speculative Read Data Movement Enable + * 0000 == Function Disabled + * 0001 == One Clock + * 0010 == Two Clocks + * .... + * 1111 == Fifteen Clocks + * + * [19:16] P0 Speculate Read Data Movement Enable + * 0000 == Function Disabled + * 0001 == One Clock + * 0010 == Two Clocks + * .... + * 1111 == Fifteen Clocks + * + * [13:11] P1 Write Data Delay (Status Register) + * [10: 8] P0 Write Data Delay (Status Register) + * [ 7: 7] 1 == Defer Write Data Movement + * [ 4: 4] P1 2 Bit Times Per Frame Enable + * Use 0 for alpha 1 for athlon + * [ 3: 3] P0 2 Bit Times Per Frame Enable + * Use 0 for alpha 1 for athlon + * + */ + + /* BIU[01] Status/Control + * 0x60, 0x68 + * [31:31] Probe_Enable + * 1 == Probes are sent to this processor + * 0 == Probes are not sent to this processor + * [27:25] Xca_Probe_Count + * Must be non-zero + * Recommended value 0x2 + * [24:22] Xca_Read_Count + * Must be non-zero + * Recommended value 0x6 + * [21:19] Xca_Write_Count + * Must be non-zero + * Recommended value 0x6 + * [18:18] Halt_Disconnect_Enable + * 0 == No AMD Athlon system bus disconnect following HALT + * 1 == AMD Athlon system bus discconnect following HALT + * [17:17] Stop_Grant_Disconnect_Enable + * 0 == No AMD Athlon system bus disconnect following Stop/Grant + * 1 == AMD Athlon system bus disconnect following Stop/Grant + * special cycle + * [16:14] Probe_Limit + * 000 = 1 probe + * 001 = 2 probes + * ... + * 111 = 8 probes + * [13:10] Ack_Limit (Read only) + * 0000 = 1 unacknowledged command + * 0001 = 2 unacknowledged commands + * ... + * 1111 = 16 unacknowledged commands + * [09:09] Bypass_Enable + * 0 == No don't bypass certain memory pipe stages + * 1 == bypass some memory stages for optimal performace + * - Requires single CPU system + * - Requires clock multipler >= 4 + * [08:07] SysDC_Out_Enable + * Initialized by pinstrapping during reset + * 00 == Reserved + * 01 == 1 clock + * 10 == 2 clocks + * 11 == 3 clocks + * [06:03] SysDC_In_Enable + * Initialized during pinstrapping + * 0000 == 1 clock + * 0001 == 2 clocks + * 1111 == 16 clocks + * [02:02] WR2_Read + * Initialized during pinstrapping + * [01:00] WR2_Write + * Initialized during pinstrapping + */ + +#include +#include + +#define BIU_CONTROL_MASK 0x00003dff +#define BIU_CONTROL ((1 << 31)|(0x2 << 25)|(0x6 << 22)|(0x6 << 19)|(0 << 18)\ + |(1 << 17)|(7<<14)|(0<<9)) +#define EBIU_MASK 0xffffe4ff +#define EBIU_VALUE ((0<<20)|(0<<16)|(1<<4)|(1<<3)) + +#define PCI_CONFIG_ADDR 0xFE000000 +#define PCI_IO_ADDR 0xFC000000 +#define PCI_IACK_ADDR 0xF8000000 +#define PCI_SPEC_ADDR 0xF8000000 + + /* Read the northbridge whami register + */ + movl $0x80000080, %eax + movw $0xcf8, %dx + outl %eax, %dx + movw $0xcfc, %dx + inl %dx, %eax + + /* Isolate the processor id */ + movl %eax, %ebx + movl %eax, %esi + andl $0xf, %esi + +#if defined(USE_AMD_NDA_CODE) +#endif + + + /* Figure out how many unacknowledged acks + * the AMD762 northbridge can support + */ + shll $3, %esi + movl $0x80000060, %eax + orl %esi, %eax + shrl $3, %esi + movw $0xcf8, %dx + outl %eax, %dx + movw $0xcfc, %dx + inl %dx, %eax + + shrl $10, %eax + andl $0xf, %eax + addl $1, %eax + + /* Move the count into a safe place. */ + movl %eax, %ebp + + /* Tell the processor how many unacknoledged probes the AMD762 can + * support + */ +#if defined(USE_AMD_NDA_CODE) +#endif /* USE_AMD_NDA_CODE */ + + /* Enable the local apic, and map it where we expect it. */ + movl $APIC_BASE_MSR, %ecx + rdmsr + orl $APIC_BASE_MSR_ENABLE, %eax + andl $(~APIC_BASE_MSR_ADDR_MASK), %eax + orl $APIC_DEFAULT_BASE, %eax + xorl %edx, %edx + wrmsr + /* Save off the apic address */ + movl %eax, %edi + andl $APIC_BASE_MSR_ADDR_MASK, %edi + + /* Set the local apicid */ + xorl %eax, %eax + shll $24, %esi + movl %esi, APIC_ID(%edi) + shrl $24, %esi + + /* Set the enabled bit for the local apic + * and clear the spurious vector. + */ + movl APIC_SPIV(%edi), %eax + andl $0xfffffe0f, %eax + orl $APIC_SPIV_ENABLE, %eax + movl %eax, APIC_SPIV(%edi) + + /* See if I'm the first processor to initialize. */ + cmpb %bl, %bh + je bootstrap_cpu + + /* I'm not the bootstrap processor */ + /* clear the BSP bit */ + movl $APIC_BASE_MSR, %ecx + rdmsr + andl $(~APIC_BASE_MSR_BOOTSTRAP_PROCESSOR), %eax + wrmsr + + /* Send an APIC INIT to myself */ + movl %esi, %eax + shll $24, %eax + movl %eax, APIC_ICR2(%edi) + + movl $((0<<18)|(1<<15)|(1<<14)|(0<<11)|(5 << 8)|(0<<0)), %eax + movl %eax, APIC_ICR(%edi) + + /* Wait for the ipi send to finish */ +1: movl APIC_ICR(%edi), %eax + testl $APIC_ICR_BUSY, %eax + jnz 1b + + /* Deassert the APIC INIT */ + movl %esi, %eax + shll $24, %eax + movl %eax, APIC_ICR2(%edi) + + movl $((0<<18)|(1<<15)|(0<<14)|(0<<11)|(5 << 8)|(0<<0)), %eax + movl %eax, APIC_ICR(%edi) + + /* Wait for the ipi send to finish */ +1: movl APIC_ICR(%edi), %eax + testl $APIC_ICR_BUSY, %eax + jnz 1b + +wait_for_startup_ipi: + hlt + jmp wait_for_startup_ipi + + +bootstrap_cpu: + /* Set the bootstrap processor flag */ + movl $APIC_BASE_MSR, %ecx + rdmsr + orl $APIC_BASE_MSR_BOOTSTRAP_PROCESSOR, %eax + wrmsr + + +init_cpu_bus_all: + /* Setup Common AMD Athlon system bus */ + movl $0x80000044, %eax + mov $0xcf8, %dx + outl %eax, %dx + mov $0xcfc, %dx + inl %dx, %eax + andl $EBIU_MASK, %eax + orl $EBIU_VALUE, %eax + outl %eax, %dx + +init_cpu_bus_0: + testl $(1<<16), %ebx + jz init_cpu_bus_0_done + /* Setup the AMD Athlon system bus */ + movl $0x80000060, %eax + movw $0xcf8, %dx + outl %eax, %dx + movw $0xcfc, %dx + inl %dx, %eax + andl $BIU_CONTROL_MASK, %eax + orl $BIU_CONTROL, %eax + outl %eax, %dx +init_cpu_bus_0_done: + +init_cpu_bus_1: + testl $(2<<16), %ebx + jz init_cpu_bus_1_done + /* Setup the AMD Athlon system bus */ + movl $0x80000068, %eax + movw $0xcf8, %dx + outl %eax, %dx + movw $0xcfc, %dx + inl %dx, %eax + andl $BIU_CONTROL_MASK, %eax + orl $BIU_CONTROL, %eax + outl %eax, %dx +init_cpu_bus_1_done: + + +/* FIXME enable superbypass memory accesses if only cpu0 is present, + * and we have a cpu multiplier > 4. + */ diff --git a/src/northbridge/amd/amd76x/northbridge.c b/src/northbridge/amd/amd76x/northbridge.c new file mode 100644 index 0000000000..d6a0dfce47 --- /dev/null +++ b/src/northbridge/amd/amd76x/northbridge.c @@ -0,0 +1,14 @@ +#include +#include +#include + +unsigned long sizeram(void) +{ + unsigned long size; + /* Use the PCI top memory register */ + pcibios_read_config_dword(0, 0, 0x9c, &size); + /* Convert size in bytes to size in K */ + size = size >> 10; + return size; +} + diff --git a/src/northbridge/amd/amd76x/raminit.inc b/src/northbridge/amd/amd76x/raminit.inc new file mode 100644 index 0000000000..2f60900b7b --- /dev/null +++ b/src/northbridge/amd/amd76x/raminit.inc @@ -0,0 +1,2114 @@ +jmp amd76x_out + +#define DEBUG_RAM_CONFIG 0 + +#if DEBUG_RAM_CONFIG + /* + * Routine: dump_sdram_config + * Arguments: none + * Results: none + * Trashed: %eax, %ebx, %edx, %esp, %eflags + * Effects: Writes pci config space to the serial port for + * device 0 functions 0&1 + * Notes: + */ +dump_sdram_config: + movl $0x00000000, %ebx +dump_sdram_config_byte_header: + testb $0x0f,%bl + jnz dump_sdram_config_byte + TTYS0_TX_CHAR($'\r') + TTYS0_TX_CHAR($'\n') + movl %ebx, %eax + CALLSP(ttys0_tx_hex32) + TTYS0_TX_CHAR($':') + TTYS0_TX_CHAR($' ') + +dump_sdram_config_byte: + TTYS0_TX_CHAR($' ') + movl %ebx, %eax + PCI_READ_CONFIG_BYTE + CALLSP(ttys0_tx_hex8) +dump_sdram_next_byte: + incl %ebx + cmpl $0x00000200, %ebx + jne dump_sdram_config_byte_header + TTYS0_TX_CHAR($'\r') + TTYS0_TX_CHAR($'\n') + RET_LABEL(dump_sdram_config) +#endif + + /* Config Status (Read Only) + * 0x88 + * [31:29] AGP Clock Mux + * [28:26] System Clock Mux + * [25:25] Type Detect + * 0 == AGP card uses 1.5V signalling + * 1 == AGP card uses 3.3V signalling + * [24:24] AMD Athlon Processor System Bus Threshold + * 0 == Bus input threshold between: 1.3V and 1.9V + * 1 == Bus input threshold between: 2.0V and 2.2V + * [23:23] AMD Athlon Processor Push-Pull Driver + * 0 == Disabled + * 1 == Enabled + * [22:22] AMD-762 System Controller Push-Pull Driver + * 0 == Disabled + * 1 == Enabled + * [21:20] AMD-762 Clock Speed + * 00 == 100Mhz + * 01 == 66Mhz + * 10 == Reserved + * 11 == 133Mhz + * [19:18] AMD Athlon Processor 1 System Bus Length + * 00 == Short + * ... + * 11 == Long + * [17:16] AMD Athlon Procesor 0 System Bus Length + * 00 == Short + * ... + * 11 == Long + * [15:15] Tristate Enable + * [14:14] NAND Enable + * [13:13] Bypass_PLLs + * 0 == AMD-762 PLLs enabled + * 1 == AMD-762 PLLs bypassed + * [12:12] Disable Divider + * [11:8] CPU1_Divider + * 0000 == 11.0 + * 0001 == 11.5 + * 0010 == 12.0 + * 0011 == 12.5 + * 0100 == 5.0 + * 0101 == 5.5 + * 0110 == 6.0 + * 0111 == 6.5 + * 1000 == 7.0 + * 1001 == 7.5 + * 1010 == 8.0 + * 1011 == 8.5 + * 1100 == 9.0 + * 1101 == 9.5 + * 1110 == 10.0 + * 1111 == 10.5 + * [7:7] External SIP ROM Enabled + * [6:6] 66MhzPCI + * 0 == This platform only supports a 33Mhz PCI bus + * 1 == This platfrom supports a 66/33Mhz PCI bus + * [5:5] INCLK Enable + * [4:4] OUTCLCK Enable + * [3:0] CPU0_Divider + * 0000 == 11.0 + * 0001 == 11.5 + * 0010 == 12.0 + * 0011 == 12.5 + * 0100 == 5.0 + * 0101 == 5.5 + * 0110 == 6.0 + * 0111 == 6.5 + * 1000 == 7.0 + * 1001 == 7.5 + * 1010 == 8.0 + * 1011 == 8.5 + * 1100 == 9.0 + * 1101 == 9.5 + * 1110 == 10.0 + * 1111 == 10.5 + */ + + /* DDR PDL Configuration Registers + * 0x144 - 0x188 + * [31:24] Clock Delay + * This field provides the number of buffers that one-half of + * the period of the system clock + * [23:16] Software Calibration Delay + * This bit field represents the amount of delay that is + * required for the corresponding DQS. + * 256 times a percentage of the half period of the system clock. + * [15:8] Calibration Delay + * Number of buffers used for last calibration delay + * [7:0] Actual Delay + * The calibration delay value in buffers that is in effect. + */ + + /* + * Routine: sdram_dynamic_pdl_config + * Arguments: none + * Results: none + * Trashed: %eax, %ebx, %edx, %esi, %eflags + * Effects: Based upon clock speed and mainboard + * set the timing parameters appropriately. + * Notes: The table ``sdram_software_calibration_delay'' + * comes from mainboard_raminit.inc as the needed + * values may changed depending upon your motherboard. + * + */ +sdram_dynamic_pdl_config: + /* read the config status to see if we are at 100 or 133 Mhz */ + movl $0x80000088, %eax + movw $0xcf8, %dx + outl %eax, %dx + movw $0xcfc, %dx + inl %dx, %eax + /* Now isolate the appropriate bit field */ + shrl $20, %eax + andl $3, %eax + /* Now get the actual delay to use */ + xorl %ebx, %ebx + movb sdram_software_calibration_delay(%eax), %bl + /* Adjust the delay so it is in the right bits */ + shll $16, %ebx + + /* Now set the configuration registers */ + movl $0x80000144, %esi +sdram_set_pdl_config: + movl %esi, %eax + movw $0xcf8, %dx + outl %eax, %dx + movw $0xcfc, %dx + movl %ebx, %eax + outl %eax, %dx + + addl $0x4, %esi + cmpl $0x8000018c, %esi + jl sdram_set_pdl_config + RET_LABEL(sdram_dynamic_pdl_config) + + /* + * Table: constant_register_values + */ + .p2align 3 +constant_register_values: + /* ECC Mode/Status + * 0x48 + * [15:14] Serr_Enable + * 00 == SERR# Assertion is disabled + * x1 == Muliple bit errors force SERR assertion + * 1x == Single bit errors force SERR assertion + * [12:12] ECC_Debug 1 == Enable ECC debugging mode + * + * [11:10] ECC_Mode + * 00 == ECC disabled + * 01 == EC_Hiperf Error checking and status reporting is enabled + * 10 == ECC_Hiperf Error checking, status reporting, + * and PCI/AGP destined memory is corrected. + * 11 == ECC_Scrub Error checking, status reporting, + * PCI/AGP destined memory is corrected, and + * All corrected errors are written back to main memory. + * [09:08] ECC_Status + * 00 == No error + * x1 == Multi bit error occured + * 1x == Single bit error occured + * + * [07:04] ECC_CS_MED + * Chip on which first multi bit ECC error occured + * [03:00] ECC_CS_SED + * Chip on which first single bit ECC error occured + * + * Current settings: + * - All ECC errors trigger an exception. + * - Use hiperformace ECC error checking, and PCI/AGP error correction. + * - Clear the ECC_Status + * + */ +.long 0x48, (3 << 14)|(2 << 10)|(3 << 8)|(0 << 4)|(0 << 0) + /* PCI_Control */ + /* 0x4C + * [02:02] 1 == PCI Delayed Transaction Enable + * [01:01] 1 == PCI Ordering Rules Compliance Enable + * [00:00] 1 == Function 1 Enable + */ +#if 0 +.long 0x4C, (1 << 2)|(1 << 1)|(1 << 0) +#else +.long 0x4C, (0 << 2)|(0 << 1)|(1 << 0) +#endif +#if 0 /* Calculated via SPD */ + /* DRAM Timing + * 0x54 + * [31:31] 1 == Add wait state on all superbypass reads + * [30:30] Address Timing for copy A + * 0 == No extra delay + * 1 == XXps delay + * [29:29] Address Timing for copy B + * 0 == No extra delay + * 1 == XXps delay + * [28:28] 1 == Add read wait state + * + * [27:27] 1 == Registered DIMM enabled + * [26:26] tWTR: Write to Read Delay + * 0 == tWTR duration is 1 clock cycle + * 1 == tWTR duration is 2 clock cycles + * [25:24] tWR: Write recovery time + * 00 == tWR duration is 1 clock cycle + * 01 == Reserved + * 10 == tWR duration is 2 clock cycles + * 11 == tWR duration is 3 clock cycles + * [23:23] tRRD: Activate Bank A to Activate Bank B Command Delay + * 0 == tRRD duration is 2 clock cycles + * 1 == tRRD duration is 3 clock cycles + * + * [18:16] Idle Cycle Limit + * 111 = Disable idle precharge + * 110 = 48 cycles + * 101 = 32 cycles + * 100 = 24 cycles + * 011 = 16 cycles + * 010 = 12 cycles + * 001 = 8 cycles (recommended "safe" configuration) + * 000 = 0 cycles + * + * [15:14] Page Hit limit + * 00 == 1 cycle + * 01 == 4 cycles + * 10 == 8 cycles (recommended "safe" configuration) + * 11 == 16 cycles + * + * [11:09] tRC: minimum time from activate to activate of the same bank + * 111 = 10 cycles + * 110 = 9 cycles + * 101 = 8 cycles (recommended "safe" configuration) + * 100 = 7 cycles + * 011 = 6 cycles + * 010 = 5 cycles + * 001 = 4 cycles + * 000 = 3 cycles + * [08:07] tRP: precharge time + * 00 = 3 cycles + * 01 = 2 cycles + * 10 = 1 cycles + * 11 = 4 cycles + * [06:04] tRAS: minimum bank active time (time between activate & precharge) + * 111 = 9 cycles + * 110 = 8 cycles + * 101 = 7 cycles (recommeded "safe" confiuration) + * 100 = 6 cycles + * 011 = 5 cycles + * 010 = 4 cycles + * 001 = 3 cycles + * 000 = 2 cycles + * [03:02] CAS latency of SDRAM + * 11 = Reserved + * 10 = 2.5 cycles + * 01 = 2 cycles + * 00 = 3 cycles + * [01:00] tRCD: Ras to Cas Delay (from activate to r/w command) + * 11 = 4 cycles + * 10 = 3 cycles (recommeded "safe" configuration) + * 01 = 2 cycles + * 00 = 1 cycles + * + * Current settings: + * tRCD = ceil(20ns/ 7.5ns) = 3 cycles + * CAS = 2.5 cycles + * tRAS = ceil(45ns / 7.5ns) = 6 cycles + * tRP = ceil(20ns / 7.5ns) = 3 cycles + * tRC = != 0 -> tRAS + tRP = ceil((20ns + 45ns) / 7.5 ns) = 9 cycles + * + * Page Hit Limit = 8 cycles + * Idle Cycle Limit = 8 cycles + * + * tRRD = ceil(15ns / 7.5ns) = 2 cycles + * + * tWR = guess = 2 cycles + * tWTR = guess = 2 cycles + * Registered DIMM enabled + * Add Read Wait State + * Address Timing for copy B XXps delay because of registered DIMMs + * Address Timing for copy A XXps delay because of registered DIMMs + * Superbypass Wait State Enabled because we are 133Mhz not 100Mhz + */ +.long 0x54, (1 << 31)|(1 << 30)|(1 << 29)|(1 << 28)|(1 << 27)|(1 << 26)|(2 << 24)|(0 << 23)|(1 << 16)|(2 << 14)|(6 << 9)|(0 << 7)|(4 << 4)|(2 << 2)|(2 << 0) +#else +.long 0x54, 0 +#endif + /* DRAM Mode/Status + * 0x58 + * [31:26] Clock Disable 0-5 + * 0 = Clock Pair Enabled + * 1 = Clock Pair Disabled + * [25:25] SDRAM initialization (Set to one to begin SDRAM initialization) + * [23:23] Mode Register Status 1 == Set and Writing + * [22:21] Suspend to Ram Control + * 00 == Not yet programmed. (Won't do memory inialization) + * 01 == Resume from suspend to disk or normal startup + * 1X == Resume from suspend to ram. + * [20:20] 1 == Burst Refresh Enable + * [19:19] 1 == Refresh Disable + * [17:16] Cycles per refresh + * System Clock + * 66Mhz 100Mhz 133Mhz + * 00 = 30.72us 20.48us 15.36us + * 01 = 23.34us 15.36us 11.52us + * 10 = 15.36us 10.24us 7.68us + * 11 = 7.68us 7.68us 3.84us + * [07:00] Chip Select N X4 mode Enable + * 0 == This chip select consists of non X4 device + * 1 == This chip select consists of X4 device + * + * Current values: + * No X4 devices present + * Refresh rate needed 15.6us using 15.36us + * Initially refresh is disabled + * Burst refresh disabled (documented as buggy) + * Not doing a Load Mode register yet. + * Not doing SDRAM initialization yet. + * All clock lines enabled. + */ + /* 0x02210000 */ +.long 0x58, (0 << 31)|(0 << 30)|(0 << 29)|(0 << 28)|(0 << 27)|(0 << 26)|(0 << 25)|(0 << 23)|(1 << 21)|(0 << 20)|(1 << 19)|(00 << 16)|(0 << 7)|(0 << 6)|(0 << 5)|(0 << 4)|(0 << 3)|(0 << 2)|(0 << 1)|(0 << 0) + /* Memory Status/Control + * 0x70 + * [18:18] == 1 Ram Self-Refresh Enable + * [10:10] == 1 PCI Pipeline Memory Accesses + * [09:09] == 1 PCI Block Write Enable + */ +.long 0x70, ( 1 << 18) | (1 << 10) | ( 1 << 9) + + /* Memory Base Address Registers + * 0xC0, 0xC4, 0xC8, 0xCC, 0xD0, 0xD4, 0xD8, 0xDC + * [31:23] Chip Select Base (8MB aligned) + * [15:07] Chip Select Mask (Set bits are ignored) + * [02:01] Addressing Mode + * 00 == Reserved + * 01 == Used for 64MB & 128MB SDRAM + * 10 == Used for 256MB & 512MB SDRAM + * 11 == Reserved + * [00:00] Chip Select Enable + * + * Current Setting: + * All banks empty + * This is configured by the SPD code + */ +.long 0xC0, (0x000 << 23)|(0x000 << 7)|(0 << 1)|(0 << 0) +.long 0xC4, (0x000 << 23)|(0x000 << 7)|(0 << 1)|(0 << 0) +.long 0xC8, (0x000 << 23)|(0x000 << 7)|(0 << 1)|(0 << 0) +.long 0xCC, (0x000 << 23)|(0x000 << 7)|(0 << 1)|(0 << 0) +.long 0xD0, (0x000 << 23)|(0x000 << 7)|(0 << 1)|(0 << 0) +.long 0xD4, (0x000 << 23)|(0x000 << 7)|(0 << 1)|(0 << 0) +.long 0xD8, (0x000 << 23)|(0x000 << 7)|(0 << 1)|(0 << 0) +.long 0xDC, (0x000 << 23)|(0x000 << 7)|(0 << 1)|(0 << 0) + + /* DDR PDL Calibration Control + * 0x140 + * [7:7] Software Recalibration + * 0 == Calibration complete (default) + * 1 == Calibration inprogress / Start Calibration + * [6:6] Use Actual Delay + * Writing a 1 says to use the manually calibrated values. + * [5:5] Auto Calibration mode + * 0 == Auto-calibration mode off(default) + * 1 == Auto-Calibration mode on + * [4:4] Actual Delay Update Inhibit + * 0 == Update all the PDL's after calibration + * 1 == Do not update the Actual PDL delay values + * [1:0] Auto Calibratio Period + * 00 == 10000 system clocks + * 01 == 1000000 system clocks + * 10 == 10000000 system clocks + * 11 == reserved + */ +.long 0x140, ((0<<7)|(0<<6)|(0<<5)|(0<<4)|(1<<0)) + + /* DDR Pad Configuration + * 0x8C [31:16] DQS Pad + * 0x8C [15:00] MDAT Pad + * 0x90 [31:16] CLK Pad + * 0x90 [15:00] CS Pad + * 0x94 [31:16] CMDB Pad + * 0x94 [15:00] CMDA Pad + * 0x98 [31:16] MAB Pad + * 0x98 [15:00] MAA Pad + * + * [13:11] Rising (P) Edge Slew Rate + * 000 == Slew rate 0 (slowest) + * 001 == Slew rate 1 + * ... + * 111 == Slew rate 7 (fastest) + * [10:08] Falling (N) Edge Slew Rate + * 000 == Slew rate 0 (slowest) + * 001 == Slew rate 1 + * ... + * 111 == Slew rate 7 (fastest) + * [03:02] P Transitor Drive Strength + * 00 == Drive Strength 0 (weakest) + * 01 == Drive Strength 1 + * 10 == Drive Strength 2 + * 11 == Drive Strength 3 (strongest) + * [01:00] N Transitor Drive Strength + * 00 == Drive Strength 0 (weakest) + * 01 == Drive Strength 1 + * 10 == Drive Strength 2 + * 11 == Drive Strength 3 (strongest) + */ +#define DEFAULT_PAD_CONFIG ((5<<27)|(5 <<24)|(3 <<18)|(2<<16)|(5<<11)|(5<<8)|(3<<2)|(2<<0)) +.long 0x18c, DEFAULT_PAD_CONFIG +.long 0x190, DEFAULT_PAD_CONFIG +.long 0x194, DEFAULT_PAD_CONFIG +.long 0x198, DEFAULT_PAD_CONFIG + +#if 0 + /* disable AGP */ +.long 0xA8, 0 +.long 0xAC, 0 +.long 0xB0, 0 +#endif + +constant_register_values_end: + + /* + * Routine: ram_set_registers + * Arguments: none + * Results: none + * Trashed: %eax, %ebx, %ecx, %edx, %esi, %eflags + * Effects: Do basic ram setup that does not depend on serial + * presence detect information. + * This sets PCI configuration registers to known good + * values based on the tables: + * constant_register_values + * mainboard_constant_register_values + * Which are a pair of configuration regiser, and value. + * + * sdram_dynamic_pdl_config is called, to set near PCI configuration + * restigers from the near constants that depend on FSB speed. + * + * Notes: The table ``mainboard_constant_register_values'' + * comes from mainboard_raminit.inc as the needed + * values may changed depending upon your motherboard. + * + */ + .p2align 3 +ram_set_registers: + movl $constant_register_values, %ebx + jmp ram_set_one_register_start +ram_set_one_register: +#if DEBUG_RAM_CONFIG + movl %ebx, %esi + TTYS0_TX_CHAR($'C') + TTYS0_TX_CHAR($':') + movl 0(%esi), %eax + CALLSP(ttys0_tx_hex32) + TTYS0_TX_CHAR($':') + movl 4(%esi), %eax + CALLSP(ttys0_tx_hex32) + TTYS0_TX_CHAR($'\r') + TTYS0_TX_CHAR($'\n') + movl %esi, %ebx +#endif /* DEBUG_RAM_CONFIG */ + movl 0(%ebx), %eax + movl 4(%ebx), %ecx + PCI_WRITE_CONFIG_DWORD + addl $8, %ebx +ram_set_one_register_start: + cmpl $constant_register_values_end, %ebx + jb ram_set_one_register + + movl $mainboard_constant_register_values, %ebx + jmp ram_set_one_mainboard_register_start +ram_set_one_mainboard_register: +#if DEBUG_RAM_CONFIG + movl %ebx, %esi + TTYS0_TX_CHAR($'M') + TTYS0_TX_CHAR($'C') + TTYS0_TX_CHAR($':') + movl 0(%esi), %eax + CALLSP(ttys0_tx_hex32) + TTYS0_TX_CHAR($':') + movl 4(%esi), %eax + CALLSP(ttys0_tx_hex32) + TTYS0_TX_CHAR($'\r') + TTYS0_TX_CHAR($'\n') + movl %esi, %ebx +#endif /* DEBUG_RAM_CONFIG */ + movl 0(%ebx), %eax + movl 4(%ebx), %ecx + PCI_WRITE_CONFIG_DWORD + addl $8, %ebx +ram_set_one_mainboard_register_start: + cmpl $mainboard_constant_register_values_end, %ebx + jb ram_set_one_mainboard_register + + CALL_LABEL(sdram_dynamic_pdl_config) +RET_LABEL(ram_set_registers) + + + /* + * Routine: sdram_spd_get_dimm_size + * Arguments: %bl SMBUS_MEM_DEVICE + * Results: + * %edi log base 2 size of DIMM side 1 in bits + * %esi log base 2 size of DIMM side 2 in bits + * + * Preserved: %ebx (except %bh), %ebp + * + * Trashed: %eax, %bh, %ecx, %edx, %esp, %eflags + * Used: %eax, %ebx, %ecx, %edx, %esi, %edi, %esp, %eflags + * + * Effects: Uses serial presence detect to set %edi & %esi + * the size of a dimm. + * Notes: + * %bl SMBUS_MEM_DEVICE + * %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. + * + * This routine may be worth moving into generic code somewhere. + */ + +sdram_spd_get_dimm_size: + xorl %edi, %edi + xorl %esi, %esi + + /* Note it might be easier to use byte 31 here, it has the DIMM size as + * a multiple of 4MB. The way we do it now we can size both + * sides of an assymetric dimm. + */ + movb $3, %bh /* rows */ + CALLSP(smbus_read_byte) + jz sdram_spd_get_dimm_size_out + andl $0xf, %eax + addl %eax, %edi + + movb $4, %bh /* columns */ + CALLSP(smbus_read_byte) + jz sdram_spd_get_dimm_size_out + andl $0xf, %eax + addl %eax, %edi + + movb $17, %bh /* banks */ + CALLSP(smbus_read_byte) + jz sdram_spd_get_dimm_size_out + andl $0xff, %eax + bsrl %eax, %ecx /* compute cheap log base 2 */ + 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) + jz sdram_spd_get_dimm_size_out + andl $0xff, %eax + movl %eax, %ecx + shll $8, %ecx + + movb $6, %bh /* (low byte) */ + CALLSP(smbus_read_byte) + jz sdram_spd_get_dimm_size_out + andl $0xff, %eax + orl %eax, %ecx + bsrl %ecx, %eax /* compute cheap log base 2 */ + addl %eax, %edi + + /* side two */ + movb $5, %bh /* number of physical banks */ + CALLSP(smbus_read_byte) + jz sdram_spd_get_dimm_size_out + cmp $1, %al + jbe sdram_spd_get_dimm_size_out + + /* Start with the symmetrical case */ + movl %edi, %esi + + movb $3, %bh /* rows */ + CALLSP(smbus_read_byte) + jz sdram_spd_get_dimm_size_out + testb $0xf0, %al + jz sdram_spd_get_dimm_size_out + movl %eax, %ecx + andl $0xf, %ecx + subl %ecx, %esi /* Subtract out rows on side 1 */ + shrl $4, %eax + andl $0xf, %eax + addl %eax, %esi /* Add in rows on side 2 */ + + movb $4, %bh /* columns */ + CALLSP(smbus_read_byte) + jz sdram_spd_get_dimm_size_out + movl %eax, %ecx + andl $0xf, %ecx + subl %ecx, %esi /* Subtract out columns on side 1 */ + shrl $4, %eax + andl $0xf, %eax + addl %eax, %esi /* Add in columns on side 2 */ + +sdram_spd_get_dimm_size_out: + RET_LABEL(sdram_spd_get_dimm_size) + + + /* + * Routine: set_dimm_size + * Arguments: %esp return address + * %edi width of the dimm in bits + * %edx dim base register + * + * Preserved: %ebx, %esi + * Used: %eax, %ecx, %edx, %esp, %edi, %eflags + * Trashed: %eax, %ecx, %edx, %esp, %edi, %eflags + * Effects: Set the specified dimm base register + * + */ + +set_dimm_size: + /* Convert ram width in bits to log base 2 ram size in 8MB quantities */ + xorl %eax, %eax + subl $26, %edi + jb 1f + + /* Compute the ignore bits for the DIMM side 1 */ + movl %edi, %ecx + incl %eax + shll %cl, %eax + decl %eax + andl $0x1ff, %eax + + /* Add in the addressing mode */ + shll $7, %eax + orl $3, %eax /* Set the enable and mode 1 */ + cmpl $32, %edi /* 256MB */ + jb 1f + xorl $6, %eax /* Clear mode 1 set mode 2 */ +1: + movl %eax, %ecx /* Value to write into ecx */ + movl %edx, %eax /* Address to write to is in eax */ + PCI_WRITE_CONFIG_DWORD + RETSP + + /* + * Routine: verify_dimm_enabled + * Arguments: %esp return address + * %edx DIMM register index + * Results: %ecx -- Or'd with a nonzero value when the + * dimm is enabled. + * + * Trashed: %eax, %edx + * + * Results: zf clear when the dimm is enabled + * zf set when the dimm is not enabled + * + * Effects: Tests to see if a DIMM has already been disabled. + */ +verify_dimm_enabled: + shll $2, %edx + movl $0xC0, %eax + addl %edx, %eax + PCI_READ_CONFIG_DWORD + /* Only keep the enable bit */ + andl $1, %eax + /* Merge the result into %ecx */ + orl %eax, %ecx + RETSP + + + /* + * Routine: disable_dimm + * Arguments: %esp return address + * %edx DIMM register index + * + * Trashed: %eax, %edx + * + * Effects: Clears the base address register enable bit + * for the specified dimm thereby making certain + * that the specified dimm will not get used. + * Notes: The PCI configuration access are hand rolled + * to minimize the register pressure. + */ +disable_dimm: + shll $2, %edx + + movl $0x800000C0, %eax + addl %edx, %eax + movw $0xcf8, %dx + outl %eax, %dx + movw $0xcfc, %dx + xorl %eax, %eax + outl %eax, %dx + RETSP + + /* + * Routine: spd_set_ram_size + * Arguments: None + * + * Preserved: %ebp + * Used: %eax, %ebx, %ecx, %edx, %esi, %edi, %esp, %eflags + * Trashed: %eax, %ebx, %ecx, %edx, %esi, %edi, %esp, %eflags + * Effects: Uses serial presence detect to set the Memory + * Base Address registers which hold the starting + * address and a mask for each DIMM. + * Only the mask which holds the size of the DIMM + * is set, + * Notes: + * %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 represented as the log base 2 + * width of the dimm in bits. + * An unset memory size is represented as -1 ie. 0xFFFFFFFF + * + * The order of the DIMMS in the configuration + * registers is not necessarily the order of the + * DIMMS on the smbus. So spd_to_dimm_side0 and + * spd_to_dimm_side1 must be called to map the + * address from one form to the other. + * + * spd_to_dimm_side0 and spd_to_dimm_side1 is + * defined in mainboard_raminit.inc + */ +spd_set_ram_size: + movl $(SMBUS_MEM_DEVICE_START), %ebx + /* Read the necessary SPD bytes for the first dimm */ +spd_get_dimm_size: + CALL_LABEL(sdram_spd_get_dimm_size) + + CALLSP(spd_to_dimm_side0) + shll $2, %edx + addl $0xC0, %edx + CALLSP(set_dimm_size) + + CALLSP(spd_to_dimm_side1) + shll $2, %edx + addl $0xC0, %edx + movl %esi, %edi + CALLSP(set_dimm_size) + + /* go to the next DIMM */ + addb $(SMBUS_MEM_DEVICE_INC), %bl /* increment the smbus device */ + cmpb $SMBUS_MEM_DEVICE_END, %bl + jbe spd_get_dimm_size +spd_set_ram_size_out: + RET_LABEL(spd_set_ram_size) + + /* + * Routine: order_dimms + * 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 + */ +order_dimms: + xorl %ebp, %ebp /* Top of memory & registers used */ + +order_try_dimms: + xorl %ecx, %ecx /* Initialize the start register */ + xorl %edi, %edi /* largest config register */ + xorl %esi, %esi /* laregest config register value */ +order_try_dimm: + movl %ecx, %eax + shll $2, %eax + addl $0xC0, %eax + PCI_READ_CONFIG_DWORD + + testl $0x00000001, %eax /* Is it enabled? */ + jz order_next_dimm + + cmpl %esi, %eax /* Is it greater? */ + jbe order_next_dimm + + btl %ecx, %ebp /* Has it already been selected? */ + jc order_next_dimm + + /* I have a new canidate save it */ + movl %ecx, %edi /* config register */ + movl %eax, %esi /* config register value */ + +order_next_dimm: + incl %ecx /* next config register */ + cmpl $8, %ecx + jb order_try_dimm + + testl %esi, %esi + jz order_dimms_out + + /* Set the highest register */ + btsl %edi, %ebp /* remember I have used this register */ + + /* Compute the address to write to */ + movl %edi, %eax + shll $2, %eax + addl $0xC0, %eax + + /* Compute the memory address increment */ + movl %esi, %edx + shrl $7, %edx + incl %edx + shll $23, %edx + + /* Compute the memory base register */ + movl %ebp, %ecx + andl $0xff800000, %ecx + orl %esi, %ecx + + /* Increment the top of memory */ + addl %edx, %ebp + + /* Write the base register */ + PCI_WRITE_CONFIG_DWORD + jmp order_try_dimms + +order_dimms_out: + /* clear the extra bytes from %ebp */ + andl $0xff800000, %ebp + + /* If my memory size is zero go complian */ + testl %ebp, %ebp + jz no_memory + + /* Set the PCI top of memory register */ + movl $0x9C, %eax + movl %ebp, %ecx + PCI_WRITE_CONFIG_DWORD + + CALLSP(set_memory_size) + RET_LABEL(order_dimms) + + /* + * Routine: spd_set_ecc_mode + * Arguments: None + * Results: None + * + * Trashed: %eax, %ebx, %ecx, %edx, %esi, %edi, %ebp, %esp, %eflags + * Effects: Disabled ECC if not all of the dimms support it. + * + * Notes: %bl device on the smbus that is being processed. + * %esi Current computed ECC mode register + * + */ +spd_set_ecc_mode: + /* Read the initial ECC SDRAM mode */ + movl $0x48, %eax + PCI_READ_CONFIG_DWORD + movl %eax, %esi + movl $SMBUS_MEM_DEVICE_START, %ebx + +spd_get_ecc_mode: + /* Now test each DIMM in turn to see if ECC is supported. + * If they all don't support ECC we need to disable it. + */ + movb $11, %bh /* ECC type */ + CALLSP(smbus_read_byte) + jz spd_get_ecc_mode_error + + /* If we support ECC sdram continue on. */ + cmpb $2, %al + je spd_get_ecc_mode_next_dimm + + /* We don't support ECC so disable it. */ + andl $0xfffff3ff, %esi + + jmp spd_get_ecc_mode_next_dimm +spd_get_ecc_mode_error: + /* Disable dimms that give us fatal errors */ + CALLSP(spd_to_dimm_side0) + CALLSP(disable_dimm) + CALLSP(spd_to_dimm_side0) + CALLSP(disable_dimm) + +spd_get_ecc_mode_next_dimm: + addb $(SMBUS_MEM_DEVICE_INC), %bl /* increment the smbus device */ + cmpb $SMBUS_MEM_DEVICE_END, %bl + jbe spd_get_ecc_mode + + /* After we have looked at all of the dimms it is time + * write back the ECC configuration. + */ + movl %esi, %ecx + movl $0x48, %eax + PCI_WRITE_CONFIG_DWORD +spd_set_ecc_mode_out: + RET_LABEL(spd_set_ecc_mode) + + + + /* + * Routine: get_memory_bus_config + * Arguments: %esp return address + * Results: + * cf clear + * %ebp contains a pointer to a table of configuration + * information for the current memory bus. + * On Error: + * cf set + * We have an unsupported memory bus speed. + * + * + * Used: %eax, %edx, %ebp, %esp, %eflags + * Trashed: %eax, %edx + * Preserved: %ebx, %ecx, %esi, %edi + * + * Notes: The memory bus configuration is a set of tables that + * depend on the speed of the memory bus. + * Currently those tables contain a pointer + * to an array of cas latency spd index, and the maximum values + * that apply at those various bus speeds. + */ + +/* Memory Bus Configuration. + * A set of tables for values that depend upon the speed of the memory bus. + * Structure. + * Pointer to cas latency indexes and the maximum values that apply at various bus speeds. + */ + +cas_latency_index_bus_133: + .byte 25, (7<<2)|2, 23, (7<<4)|5, 9, (7<<4)|5 + +cas_latency_index_bus_100: + .byte 25, (10<<2)|0, 23, (10<<4)|0, 9, (10<<4)|0 + +#define MAX_SPD_REFRESH_RATE_INDEX 5 +#define MIN_CHIPSET_REFRESH_RATE 0 +refresh_rate_index_bus_133: + .byte 0, 3, 2, 0, 0, 0 + +refresh_rate_index_bus_100: + .byte 1, 3, 3, 0, 0, 0 + +#if 0 +refresh_rate_index_bus_66: + .byte 2, 3, 3, 0, 0, 0 +#endif + +#define MEM_CAS_LATENCY_INDEXES 0x00 +#define MEM_QUATER_NS_PER_CYCLE 0x04 +#define MEM_NS_PER_CYCLE 0x08 +#define MEM_SUPERBYPASS_WAIT 0x0c +#define MEM_READ_WAIT_STATE 0x10 +#define MEM_REFRESH_RATE 0x14 + +memory_bus133_config: + .long cas_latency_index_bus_133 + .long 0x1e /* 30.0 quater ns per cycle */ + .long 0x08 /* |~7.51 ns~| per cycle */ + .long 1 /* Needed only at 133Mhz */ + .long 1 /* A read wait state is needed at 100 & 133Mhz */ + .long refresh_rate_index_bus_133 +memory_bus100_config: + .long cas_latency_index_bus_100 + .long 0x28 /* 40.0 quater ns per cycle */ + .long 0x0a /* 10.0 ns per cycle */ + .long 0 /* Not needed below 133Mhz */ + .long 1 /* A read wait state is needed at 100 & 133Mhz */ + .long refresh_rate_index_bus_100 + +get_memory_bus_config: + movl $0x88, %eax + PCI_READ_CONFIG_DWORD + shrl $20, %eax + andl $3, %eax +memory_bus100: + cmpl $0, %eax + jnz memory_bus133 + movl $memory_bus100_config, %ebp + clc + jmp get_memory_bus_config_out +memory_bus133: + cmpl $3, %eax + jnz memory_bus_error + movl $memory_bus133_config, %ebp + clc + jmp get_memory_bus_config_out +memory_bus_error: + stc +get_memory_bus_config_out: + RETSP + + /* + * Routine: get_dimm_cas_latency_setting + * Arguments: %bl device on the smbus to read from + * %edi an initially zeroed copy of the DRAM + * timing register that applies to this + * particular chip. + * %ebp Pointer to information that varies by memory bus speed + * + * Preserved: %bl, %esi, %ebp + * Updated: %edi + * Used: %eax, %ebx, %ecx, %edx, %edi, %ebp, %esp, %eflags + * Trashed: %eax, %ebx[31:8], %ecx, %edx, %esp, + * Results: cf clear + * %edi updated with the CAS latency settings + * On Error: + * cf set + * We have an unsupported cas latency + * + * Effects: Computes the CAS latency settings for the DRAM timing register. + * The results of the computation are placed in %edi + * Notes: + * %ebx[31:24] smbus index of byte to read + * %ebx[23:16] device on the smbus + * %ebx[15: 8] bit of slowest latency the dimm might support + * %ebx[ 7: 0] bitset of latency the dimm supports + * %ecx cas latency SPD index , and max speed table + * + */ +get_dimm_cas_latency_setting: + /* Get the best CAS latency supported by the AMD760 and the Dimm */ + /* first find the supported CAS latencies */ + /* Byte 18 for SDRAM is interpreted: + * Bit 0 == CAS Latency = 1 + * Bit 1 == CAS Latency = 2 + * Bit 2 == CAS Latency = 3 + * Bit 3 == CAS Latency = 4 + * Bit 4 == CAS Latency = 5 + * Bit 5 == CAS Latency = 6 + * Bit 6 == CAS Latency = 7 + * Bit 7 == TBD + */ + /* Byte 18 for DDR SDRAM seems to be interpreted: + * Bit 0 == CAS Latency = 1.0 + * Bit 1 == CAS Latency = 1.5 + * Bit 2 == CAS Latency = 2.0 + * Bit 3 == CAS Latency = 2.5 + * Bit 4 == CAS Latency = 3.0 + * Bit 5 == TBD + * Bit 6 == TBD + * Bit 7 == TBD + */ + /* Timing information on when you can use the supported CAS Latency + * is held in byte pairs 9,10 23,24 25,26. + * Let X be the CAS highest CAS latency. + * The first pair holds information on CAS Latency X. + * The second pair holds information on CAS Latency X - 1 (SDR) or X - 0.5 (DDR) + * The third pair holds information on CAS Latency X - 2 (SDR) or X - 1.0 (DDR) + */ + /* Read the supported CAS latencies */ + movb $18, %bh + CALLSP(smbus_read_byte) + jz get_dimm_cas_latency_error + roll $16, %ebx + movb %al, %bl /* place the supported latencies in %bl */ + bsrl %eax, %edx /* find the highest bit set */ + /* Go back two steps to find the fastest speed we can try and support. */ + subl $2, %edx + /* Place the fasted speed in %bh */ + movb %dl, %bh + movl MEM_CAS_LATENCY_INDEXES(%ebp), %ecx +cas_latency_1_0: + cmpb $0, %bh + ja cas_latency_1_5 + /* I can't support this so go to the next configuration */ + addl $2, %ecx +cas_latency_1_5: + cmpb $1, %bh + ja cas_latency_2_0 + /* I can't support this so go to the next configuration */ + addl $2, %ecx +cas_latency_2_0: + cmpb $2, %bh + ja cas_latency_2_5 + + /* Increment the latency index in case we exit early */ + addl $2, %ecx + /* Is this latency supported by the dimm? */ + testb $(1 << 2), %bl + jz cas_latency_2_5 + /* Read the fastest bus speed at which this latency is supported */ + roll $16, %ebx + movb -2(%ecx), %bh + CALLSP(smbus_read_byte) + jz get_dimm_cas_latency_error + roll $16, %ebx + /* Is the memory bus too fast to use this latency */ + cmpb -1(%ecx), %al + ja cas_latency_2_5 + + /* We can use a cas_latency of 2.0 mark it in %edi */ + orl $(1 << 2), %edi + jmp found_dimm_cas_latency + +cas_latency_2_5: + cmpb $3, %bh + ja cas_latency_3_0 + cmpb $1, %bh + jb get_dimm_cas_latency_error_fixup + + /* Increment the latency index in case we exit early */ + addl $2, %ecx + /* Is this latency supported by the dimm? */ + testb $(1 << 3), %bl + jz cas_latency_3_0 + /* Read the fastest bus speed at which this latency is supported */ + roll $16, %ebx + movb -2(%ecx), %bh + CALLSP(smbus_read_byte) + jz get_dimm_cas_latency_error + roll $16, %ebx + /* Is the memory bus to fast to use this latency */ + cmpb -1(%ecx), %al + ja cas_latency_3_0 + + /* We can use a cas_latency of 2.5 mark it in %edi */ + orl $(2 << 2), %edi + jmp found_dimm_cas_latency + +cas_latency_3_0: + cmpb $4, %ch + ja get_dimm_cas_latency_error_fixup + cmpb $2, %ch + jb get_dimm_cas_latency_error_fixup + + /* Increment the latency index in case we exit early */ + addl $2, %ecx + /* Is this latency supported by the dimm? */ + testb $(1 << 4), %cl + jz get_dimm_cas_latency_error_fixup + /* Read the fastest bus speed at which this latency is supported */ + roll $16, %ebx + movb -2(%ecx), %bh + CALLSP(smbus_read_byte) + jz get_dimm_cas_latency_error + roll $16, %ebx + /* Is the memory bus to fast to use this latency */ + cmpb -1(%ecx), %al + ja get_dimm_cas_latency_error_fixup + + /* We can use a case latency of 3.0 mark it in %edi */ + orl $(0 << 2), %edi + jmp found_dimm_cas_latency + +found_dimm_cas_latency: + roll $16, %ebx + clc + jmp get_dimm_cas_latency_out +get_dimm_cas_latency_error_fixup: + roll $16, %ebx +get_dimm_cas_latency_error: + stc +get_dimm_cas_latency_out: + RET_LABEL(get_dimm_cas_latency_setting) + + + /* + * Routine: get_dimm_registered_setting + * Arguments: %bl device on the smbus to read from + * %edi an initially zeroed copy of the DRAM + * timing register that applies to this + * particular dimm + * %ebp Pointer to information that varies by memory bus speed + * + * Preserved: %bl, %esi, %ebp, %ecx + * Updated: %edi + * Used: %eax, %ebx, %edx, %edi, %esp, %eflags + * Trashed: %eax, %edx, %esp + * Results: cf clear + * %edi updated with the registered dimm settings + * On Error: + * cf set + * We have an unsupported registered dimm setting + * + * Effects: Test to see if a DIMM is registered or not. + * + */ + +get_dimm_registered_setting: + /* Are the dimms registered? */ + movb $21, %bh + CALLSP(smbus_read_byte) + jz get_dimm_registered_setting_error + /* It is unclear what constitutes a registered dimm, so assume + * any dimm that has some lines registered is a registered dimm. + */ + testl $((1<<4)|(1<<1)), %eax + jz get_dimm_registered_setting_ok + orl $((1<<30)|(1<<29)|(1<<27)), %edi +get_dimm_registered_setting_ok: + clc + jmp get_dimm_registered_setting_out +get_dimm_registered_setting_error: + stc +get_dimm_registered_setting_out: + RET_LABEL(get_dimm_registered_setting) + + + /* + * Routine: get_dimm_ras_to_cas_delay + * Arguments: %bl device on the smbus to read from + * %edi an initially zeroed copy of the DRAM + * timing register that applies to this + * particular dimm + * %ebp Pointer to information that varies by memory bus speed + * + * Preserved: %bl, %esi, %ebp, %ecx + * Updated: %edi + * Used: %eax, %ebx, %edx, %edi, %esp, %eflags + * Trashed: %eax, %edx, %esp + * Results: cf clear + * %edi updated with tRCD setting + * On Error: + * cf set + * We have an unsupported tRCD setting + * + * Effects: Get the Ras to cas delay of a dimm + * + */ +get_dimm_ras_to_cas_delay: + movb $29, %bh + CALLSP(smbus_read_byte) + jz get_dimm_ras_to_cas_delay_error + movl MEM_QUATER_NS_PER_CYCLE(%ebp), %edx + addb %dl, %al /* Make certain to round up */ + subb $1, %al + divb %dl, %al + cmpb $4, %al + ja get_dimm_ras_to_cas_delay_error + subb $1, %al + andl $3, %eax + orl %eax, %edi +get_dimm_ras_to_cas_delay_ok: + clc + jmp get_dimm_ras_to_cas_delay_out +get_dimm_ras_to_cas_delay_error: + stc +get_dimm_ras_to_cas_delay_out: + RET_LABEL(get_dimm_ras_to_cas_delay) + + + + /* + * Routine: get_dimm_ras_setting + * Arguments: %bl device on the smbus to read from + * %edi an initially zeroed copy of the DRAM + * timing register that applies to this + * particular dimm + * %ebp Pointer to information that varies by memory bus speed + * + * Preserved: %bl, %esi, %ebp, %ecx + * Updated: %edi + * Used: %eax, %ebx, %edx, %edi, %esp, %eflags + * Trashed: %eax, %edx, %esp + * Results: cf clear + * %edi updated with tRAS setting + * On Error: + * cf set + * We have an unsupported tRAS setting + * + * Effects: Get the tRAS setting for a dimm + * + */ +get_dimm_ras_setting: + movb $30, %bh + CALLSP(smbus_read_byte) + jz get_dimm_ras_setting_error + movl MEM_NS_PER_CYCLE(%ebp), %edx + addb %dl, %al /* Make certain to round up */ + subb $1, %al + divb %dl, %al + cmpb $2, %al + jb get_dimm_ras_setting_low + cmpb $9, %al + ja get_dimm_ras_setting_error + subl $2, %eax +set_dimm_ras_setting: + andl $7, %eax + shll $4, %eax + orl %eax, %edi +get_dimm_ras_setting_ok: + clc + jmp get_dimm_ras_setting_out +get_dimm_ras_setting_low: + xorl %eax, %eax + jmp set_dimm_ras_setting +get_dimm_ras_setting_error: + stc +get_dimm_ras_setting_out: + RET_LABEL(get_dimm_ras_setting) + + + + + /* + * Routine: get_dimm_row_precharge_setting + * Arguments: %bl device on the smbus to read from + * %edi an initially zeroed copy of the DRAM + * timing register that applies to this + * particular dimm + * %ebp Pointer to information that varies by memory bus speed + * + * Preserved: %bl, %esi, %ebp, %ecx + * Updated: %edi + * Used: %eax, %ebx, %edx, %edi, %esp, %eflags + * Trashed: %eax, %edx, %esp + * Results: cf clear + * %edi updated with tRP setting + * On Error: + * cf set + * We have an unsupported tRP setting + * + * Effects: Get the tRP setting for a dimm + * + */ +dimm_row_precharge_translate: + .byte 2, 1, 0, 3 +get_dimm_row_precharge_setting: + movb $27, %bh + CALLSP(smbus_read_byte) + jz get_dimm_row_precharge_setting_error + movl MEM_QUATER_NS_PER_CYCLE(%ebp), %edx + addb %dl, %al /* Make certain to round up */ + subb $1, %al + divb %dl, %al + cmpb $1, %al + jb get_dimm_row_precharge_setting_low + cmpb $4, %al + ja get_dimm_row_precharge_setting_error + subb $1, %al +set_dimm_row_precharge_setting: + andl $3, %eax + movb dimm_row_precharge_translate(%eax), %al + shll $7, %eax + orl %eax, %edi +get_dimm_row_precharge_setting_ok: + clc + jmp get_dimm_row_precharge_setting_out +get_dimm_row_precharge_setting_low: + xorl %eax, %eax + jmp set_dimm_row_precharge_setting +get_dimm_row_precharge_setting_error: + stc +get_dimm_row_precharge_setting_out: + RET_LABEL(get_dimm_row_precharge_setting) + + + /* + * Routine: get_dimm_row_cycle_setting + * Arguments: %bl device on the smbus to read from + * %edi an initially zeroed copy of the DRAM + * timing register that applies to this + * particular dimm + * %ebp Pointer to information that varies by memory bus speed + * + * Preserved: %bl, %esi, %ebp + * Updated: %edi + * Used: %eax, %ebx, %ecx, %edx, %edi, %esp, %eflags + * Trashed: %eax, %ecx, %edx, %esp + * Results: cf clear + * %edi updated with tRC setting + * On Error: + * cf set + * We have an unsupported tRC setting + * Note: tRC = tRAS + tRP unless byte 0x41 is defined + * In which case byte 0x41 should be used. + * + * Effects: Get the tRC setting for a dimm + * + */ +get_dimm_row_cycle_setting: + movb $41, %bh + CALLSP(smbus_read_byte) + jz get_dimm_row_cycle_setting_error + testb %al, %al + /* error out if we have the new SPD setting + * This needs to be fixed as soon as we know what kind of + * units SPD byte 41 is in + */ + jnz get_dimm_row_cycle_setting_new + movb $30, %bh + CALLSP(smbus_read_byte) + jz get_dimm_row_cycle_setting_error + movl %eax, %ecx + movb $27, %bh + CALLSP(smbus_read_byte) + jz get_dimm_row_cycle_setting_error + shrl $2, %eax + addb %cl, %al + movl MEM_NS_PER_CYCLE(%ebp), %edx + addb %dl, %al /* Make certain to round up */ + subb $1, %al + divb %dl, %al + cmpb $3, %al + jb get_dimm_row_cycle_setting_low + cmpb $10, %al + ja get_dimm_row_cycle_setting_error + subb $3, %al +set_dimm_row_cycle_setting: + andl $7, %eax + shll $9, %eax + orl %eax, %edi +get_dimm_row_cycle_setting_ok: + clc + jmp get_dimm_row_cycle_setting_out + +get_dimm_row_cycle_setting_low: + /* Low values can always be rounded up */ + xorl %eax, %eax + jmp set_dimm_row_cycle_setting + +dimm_row_new_cycle_setting_msg: + .string "\r\n Newly defined SPD byte 41 populated please update the code to handle this\r\n" +get_dimm_row_cycle_setting_new: + TTYS0_TX_STRING($dimm_row_new_cycle_setting_msg) + /* fall through */ + +get_dimm_row_cycle_setting_error: + stc + /* fall through */ + +get_dimm_row_cycle_setting_out: + RET_LABEL(get_dimm_row_cycle_setting) + + + + /* + * Routine: get_dimm_row_to_row_delay_setting + * Arguments: %bl device on the smbus to read from + * %edi an initially zeroed copy of the DRAM + * timing register that applies to this + * particular dimm + * %ebp Pointer to information that varies by memory bus speed + * + * Preserved: %bl, %esi, %ebp, %ecx + * Updated: %edi + * Used: %eax, %ebx, %edx, %edi, %esp, %eflags + * Trashed: %eax, %edx, %esp + * Results: cf clear + * %edi updated with tRRD setting + * On Error: + * cf set + * We have an unsupported tRRD setting + * + * Effects: Get the tRRD setting for a dimm + * + */ +get_dimm_row_to_row_delay_setting: + movb $28, %bh + CALLSP(smbus_read_byte) + jz get_dimm_row_to_row_delay_setting_error + movl MEM_QUATER_NS_PER_CYCLE(%ebp), %edx + addb %dl, %al /* Make certain to round up */ + subb $1, %al + divb %dl, %al + cmpb $2, %al + jb get_dimm_row_to_row_delay_setting_low + cmpb $3, %al + ja get_dimm_row_to_row_delay_setting_error + subb $2, %al +set_dimm_row_to_row_delay_setting: + andl $1, %eax + shll $23, %eax + orl %eax, %edi +get_dimm_row_to_row_delay_setting_ok: + clc + jmp get_dimm_row_to_row_delay_setting_out +get_dimm_row_to_row_delay_setting_low: + xorl %eax, %eax + jmp set_dimm_row_to_row_delay_setting +get_dimm_row_to_row_delay_setting_error: + stc +get_dimm_row_to_row_delay_setting_out: + RET_LABEL(get_dimm_row_to_row_delay_setting) + + + + /* + * Routine: get_dimm_page_hit_limit_setting + * Arguments: %bl device on the smbus to read from + * %edi an initially zeroed copy of the DRAM + * timing register that applies to this + * particular dimm + * %ebp Pointer to information that varies by memory bus speed + * + * Preserved: %bl, %esi, %ebp, %ecx + * Updated: %edi + * Used: %eax, %ebx, %edx, %edi, %esp, %eflags + * Trashed: %eax, %edx, %esp + * Results: cf clear + * %edi updated with page hit limit setting + * On Error: + * cf set + * We have an unsupported page hit limit setting + * + * Notes: The page hit limit is hard coded at 8 cycles. + * As this is recommeded and we have no need to change it. + * + */ +get_dimm_page_hit_limit_setting: + movl $2, %eax /* recommeded setting of 8 cycles */ + shll $14, %eax + orl %eax, %edi + clc + RET_LABEL(get_dimm_page_hit_limit_setting) + + + + /* + * Routine: get_dimm_idle_cycle_hit_limit_setting + * Arguments: %bl device on the smbus to read from + * %edi an initially zeroed copy of the DRAM + * timing register that applies to this + * particular dimm + * %ebp Pointer to information that varies by memory bus speed + * + * Preserved: %bl, %esi, %ebp, %ecx + * Updated: %edi + * Used: %eax, %ebx, %edx, %edi, %esp, %eflags + * Trashed: %eax, %edx, %esp + * Results: cf clear + * %edi updated with idle cycle limmit setting + * On Error: + * cf set + * We have an unsupported idle cycle limit setting + * + * Notes: The page idle cycle limit is hard coded at 8 cycles. + * This is recommeded and there is no compelling reason + * to change it. + * + */ +get_dimm_idle_cycle_limit_setting: + movl $1, %eax /* recommeded setting of 8 cycles */ + shll $16, %eax + orl %eax, %edi + clc + RET_LABEL(get_dimm_idle_cycle_limit_setting) + + + /* + * Routine: get_dimm_write_recovery_setting + * Arguments: %bl device on the smbus to read from + * %edi an initially zeroed copy of the DRAM + * timing register that applies to this + * particular dimm + * %ebp Pointer to information that varies by memory bus speed + * + * Preserved: %bl, %esi, %ebp, %ecx + * Updated: %edi + * Used: %eax, %ebx, %edx, %edi, %esp, %eflags + * Trashed: %eax, %edx, %esp + * Results: cf clear + * %edi updated with tWR setting + * On Error: + * cf set + * We have an tWR setting + * + * Notes: Hardcoded to recomended 2 cycles. + * + */ +get_dimm_write_recovery_setting: + movl $2, %eax /* recommeded setting of 2 cycles */ + shll $24, %eax + orl %eax, %edi + clc + RET_LABEL(get_dimm_write_recovery_setting) + + /* + * Routine: get_dimm_write_to_read_setting + * Arguments: %bl device on the smbus to read from + * %edi an initially zeroed copy of the DRAM + * timing register that applies to this + * particular dimm + * %ebp Pointer to information that varies by memory bus speed + * + * Preserved: %bl, %esi, %ebp, %ecx + * Updated: %edi + * Used: %eax, %ebx, %edx, %edi, %esp, %eflags + * Trashed: %eax, %edx, %esp + * Results: cf clear + * %edi updated with tWTR setting + * On Error: + * cf set + * We have an unsupported tWTR setting + * + * Notes: Hardcoded to the recomended 2 cycles. + * + */ +get_dimm_write_to_read_setting: + movl $1, %eax /* recommeded setting of 2 cycles */ + shll $26, %eax + orl %eax, %edi + clc + RET_LABEL(get_dimm_write_to_read_setting) + + + /* + * Routine: get_superbypass_wait_setting + * Arguments: %bl device on the smbus to read from + * %edi an initially zeroed copy of the DRAM + * timing register that applies to this + * particular dimm + * %ebp Pointer to information that varies by memory bus speed + * + * Preserved: %bl, %esi, %ebp, %ecx + * Updated: %edi + * Used: %eax, %ebx, %edx, %edi, %esp, %eflags + * Trashed: %eax, %edx, %esp + * Results: cf clear + * %edi updated with superbypass wait setting + * On Error: + * cf set + * We have an unsupported superbypass wait setting + * + * Notes: The superbypass wait setting is determined soly by the + * memory bus clock speed. + * + */ +get_superbypass_wait_setting: + movl MEM_SUPERBYPASS_WAIT(%ebp), %eax + shll $31, %eax + orl %eax, %edi + clc + RET_LABEL(get_superbypass_wait_setting) + + + /* + * Routine: get_read_wait_state + * Arguments: %bl device on the smbus to read from + * %edi an initially zeroed copy of the DRAM + * timing register that applies to this + * particular dimm + * %ebp Pointer to information that varies by memory bus speed + * + * Preserved: %bl, %esi, %ebp, %ecx + * Updated: %edi + * Used: %eax, %ebx, %edx, %edi, %esp, %eflags + * Trashed: %eax, %edx, %esp + * Results: cf clear + * %edi updated with read wait state setting + * On Error: + * cf set + * We have an unsupported read wait state setting + * + * Notes: The read wait state setting is determined soly by the + * memory bus clock speed. + * + */ +get_read_wait_state_setting: + movl MEM_READ_WAIT_STATE(%ebp), %eax + shll $28, %eax + orl %eax, %edi + clc + RET_LABEL(get_read_wait_state_setting) + + + /* + * Routine: spd_set_dram_timing + * Arguments: None + * Results: None + * Effect: The DRAM timing register 0x54 is populated with + * a workable value + * + * Trashed: %eax, %ebx, %ecx, %edx, %esi, %edi, %esp, %ebp, %eflags + * + * Notes: + * %bl device on the smbus to read from + * %edi Accumulator for DIMM settings + * %esi A copy of the dim setting we used. + * %ebp Pointer to information that varies by memory bus speed + * + * Currently we handle mixed dimms by disabling those + * dimms whose timing does not match the timing of the + * first dimm found. Since many settings can be pessimized + * it would be nice if we found the best and + * worst possible timing for a set of DIMMS and + * only threw out those dimms that could not + * possibly fit. + * + */ + +spd_set_dram_timing: + /* Initial value for the DRAM Timing Register 0x54 */ + xorl %edi, %edi + xorl %esi, %esi + movl $SMBUS_MEM_DEVICE_START, %ebx + CALLSP(get_memory_bus_config) + jc spd_set_dram_timing_out + +spd_get_dram_timing: + xorl %ecx, %ecx + CALLSP(spd_to_dimm_side0) + CALLSP(verify_dimm_enabled) + CALLSP(spd_to_dimm_side1) + CALLSP(verify_dimm_enabled) + testl %ecx, %ecx + jz spd_dram_timing_error + + CALL_LABEL(get_dimm_registered_setting) + jc spd_dram_timing_error + + CALL_LABEL(get_dimm_cas_latency_setting) + jc spd_dram_timing_error + + CALL_LABEL(get_dimm_ras_to_cas_delay) + jc spd_dram_timing_error + + CALL_LABEL(get_dimm_ras_setting) + jc spd_dram_timing_error + + CALL_LABEL(get_dimm_row_precharge_setting) + jc spd_dram_timing_error + + CALL_LABEL(get_dimm_row_cycle_setting) + jc spd_dram_timing_error + + CALL_LABEL(get_dimm_row_to_row_delay_setting) + jc spd_dram_timing_error + + CALL_LABEL(get_dimm_page_hit_limit_setting) + jc spd_dram_timing_error + + CALL_LABEL(get_dimm_idle_cycle_limit_setting) + jc spd_dram_timing_error + + CALL_LABEL(get_dimm_write_recovery_setting) + jc spd_dram_timing_error + + CALL_LABEL(get_dimm_write_to_read_setting) + jc spd_dram_timing_error + + CALL_LABEL(get_superbypass_wait_setting) + jc spd_dram_timing_error + + CALL_LABEL(get_read_wait_state_setting) + jc spd_dram_timing_error + + CALL_LABEL(mainboard_verify_dram_timing) + jc spd_dram_timing_error + + /* Is this the first timing we have generated */ + testl %esi,%esi + jz set_dram_timing + + /* Is this timing the same as the last timing we generated? */ + cmpl %esi, %edi + je spd_get_dram_timing_next_dimm +spd_dram_timing_error: + /* The two timings are not equal so disable the current dimm */ + CALLSP(spd_to_dimm_side0) + CALLSP(disable_dimm) + CALLSP(spd_to_dimm_side1) + CALLSP(disable_dimm) + +spd_get_dram_timing_next_dimm: + xorl %edi, %edi + addb $(SMBUS_MEM_DEVICE_INC), %bl /* increment the smbus device */ + cmpb $SMBUS_MEM_DEVICE_END, %bl + jbe spd_get_dram_timing + /* After we have passed by all of the dimms + * it is time to leave. + */ + jmp spd_set_dram_timing_out + +set_dram_timing: + movl %edi, %esi /* Save the value of the timing register */ + movl $0x54, %eax + movl %edi, %ecx + PCI_WRITE_CONFIG_DWORD + jmp spd_get_dram_timing_next_dimm + +spd_set_dram_timing_out: + RET_LABEL(spd_set_dram_timing) + + + /* + * Routine: spd_get_dimm_width + * Arguments: %bl device on the smbus to read from + * %esi an initially zeroed copy of the + * DRAM mode register. + * %ebp Pointer to infomration that varies by memory bus speed + * + * Results: cf clear + * %esi updated with dimm with setting + * On Error: + * cf set + * We have an unsupported/unknown dimm width setting + * + * Trashed: %eax, %bh, %ecx, %edx, %edi, %eflags + * Effects: The size of each DIMM is set appropriately. + * + */ +spd_get_dimm_width: + /* Read by 13 to see how wide I am */ + movb $13, %bh + CALLSP (smbus_read_byte) + jz spd_get_dimm_width_error + + /* Now see if I'm an X4 device */ + xorl %edi, %edi + cmpb $4, %al + jne 1f + movl $0x1, %edi +1: + /* Now set the appropriate bits */ + CALLSP(spd_to_dimm_side0) + movl %edx, %ecx + movl $1, %eax + movl %edi, %edx + shll %cl, %eax + shll %cl, %edx + notl %eax + andl %eax, %esi + orl %edx, %esi + CALLSP(spd_to_dimm_side1) + movl %edx, %ecx + movl $1, %eax + movl %edi, %edx + shll %cl, %eax + shll %cl, %edx + notl %eax + andl %eax, %esi + orl %edx, %esi + + clc + jmp spd_get_dimm_width_out +spd_get_dimm_width_error: + stc +spd_get_dimm_width_out: + RET_LABEL(spd_get_dimm_width) + + + + /* + * Routine: spd_get_dimm_refresh_rate + * Arguments: %bl device on the smbus to read from + * %esi an initially zeroed copy of the + * DRAM mode register. + * %ebp Pointer to infomration that varies by memory bus speed + * + * Results: cf clear + * %esi updated with dimm workable dimm refresh rate + * On Error: + * cf set + * We have an unsupported/unknown dimm refresh rate + * + * Trashed: %eax, %bh, %ecx, %edx, %edi, %eflags + * Effects: The refresh rate is set appropriately for the given + * DIMM. The refresh rate is never slowed down, just + * speeded up. + * + */ + +spd_get_dimm_refresh_rate: + /* Read byte 12 to get the dimm's refresh rate */ + movb $12, %bh + CALLSP (smbus_read_byte) + jz spd_get_dimm_refresh_rate_error + + /* Now lookup up the chipset setting for this refresh rate */ + andl $0x7f, %eax + cmpl $MAX_SPD_REFRESH_RATE_INDEX, %eax + ja spd_get_dimm_refresh_rate_error + /* Get the refresh rate table */ + movl MEM_REFRESH_RATE(%ebp), %edx + /* Now get the refresh rate */ + movb (%edx,%eax,1), %al + andl $0x000000ff, %eax + + /* Isolate the old refresh rate setting */ + movl %esi, %edx + shrl $16, %edx + andl $3, %edx + + /* See if the new refresh rate is higher than the old + * refresh rate setting. + */ + cmpl %eax, %edx + ja spd_get_dimm_refresh_rate_ok + + /* Correctly position the new refresh rate */ + shll $16, %eax + /* Clear the old refresh rate */ + andl $0xFFFCFFFF, %esi + /* Insert the new refresh rate */ + orl %eax, %esi + jmp spd_get_dimm_refresh_rate_ok + +spd_get_dimm_refresh_rate_error: + stc + jmp spd_get_dimm_refresh_rate_out + +spd_get_dimm_refresh_rate_ok: + clc +spd_get_dimm_refresh_rate_out: + RET_LABEL(spd_get_dimm_refresh_rate) + + + /* + * Routine: spd_set_dram_mode + * Arguments: None + * Results: None + * + * Trashed: %eax, %ebx, %ecx, %edx, %esi, %edi, %ebp, %esp, %eflags + * Effects: Sets the DRAM Mode register according to the SPD bytes. + * + * Notes: %bl device on the smbus that is being processed. + * %esi Current computed DRAM mode register + * %ebp Pointer to information that varies by memory bus speed. + * + */ +spd_set_dram_mode: + /* Read the initial SDRAM mode */ + movl $0x58, %eax + PCI_READ_CONFIG_DWORD + /* Initialize the values we are setting to a known state. */ + andl $0xfffcff00, %eax + movl %eax, %esi + movl $(SMBUS_MEM_DEVICE_START), %ebx + CALLSP(get_memory_bus_config) + jc spd_set_dram_mode_out + +spd_get_dram_mode: + xorl %ecx, %ecx + CALLSP(spd_to_dimm_side0) + CALLSP(verify_dimm_enabled) + CALLSP(spd_to_dimm_side1) + CALLSP(verify_dimm_enabled) + testl %ecx, %ecx + jz spd_get_dram_mode_error + + CALL_LABEL(spd_get_dimm_width) + jc spd_get_dram_mode_error + CALL_LABEL(spd_get_dimm_refresh_rate) + jc spd_get_dram_mode_error + + jmp spd_get_dram_mode_next_dimm + +spd_get_dram_mode_error: + /* We have an error getting information for this DIMM disable it */ + CALLSP(spd_to_dimm_side0) + CALLSP(disable_dimm) + CALLSP(spd_to_dimm_side1) + CALLSP(disable_dimm) + +spd_get_dram_mode_next_dimm: + addb $(SMBUS_MEM_DEVICE_INC), %bl /* increment the smbus device */ + cmpb $SMBUS_MEM_DEVICE_END, %bl + jbe spd_get_dram_mode + + /* After we have looked at all of the dimms it is + * time to set the mode. + */ +spd_set_dram_mode_out: + movl %esi, %ecx + movl $0x58, %eax + PCI_WRITE_CONFIG_DWORD + RET_LABEL(spd_set_dram_mode) + + /* + * Routine: ram_set_spd_registers + * Arguments: None + * Results: None + * + * Preserved: None + * Effects: Information is read from the SPD roms on every + * dimm, and used to populate the appropriate + * configuration ram registers. When done all + * that should be left to do is enable the RAM. + */ + +ram_set_spd_registers: + /* 0xC0 - 0xDC Memory Base Address Registers */ + CALL_LABEL(spd_set_ram_size) + /* 0x54 DRAM Timing */ + CALL_LABEL(spd_set_dram_timing) + /* 0x58 DRAM Mode/Status */ + CALL_LABEL(spd_set_dram_mode) + /* 0x48 ECC Mode/Status */ + CALL_LABEL(spd_set_ecc_mode) + /* Order the RAM base address registers */ + CALL_LABEL(order_dimms) + + RET_LABEL(ram_set_spd_registers) + + + /* + * Routine: enable_sdram + * Arguments: None + * Results: None + * + * Preserved: None + * Effects: After this routine has been run you can access + * memory on the currently plugged in DIMMS. + */ +ram_enabled: .string "Ram Enabled\r\n" + +enable_sdram: + /* Recalibrate the PDL timing registers */ + movl $0x80000140, %eax + movw $0xcf8, %dx + outl %eax, %dx + movw $0xcfc, %dx + inl %dx, %eax + /* Force a manual recalibration */ + orl $0x80, %eax + outl %eax, %dx +recalibrate_sdram_wait: + inl %dx, %eax + testl $0x80, %eax + jnz recalibrate_sdram_wait + + /* Enable auto Recalibration */ + orl $(1<<5), %eax + outl %eax, %dx + + /* Enable the SDRAM */ + movl $0x80000058, %eax + movw $0xcf8, %dx + outl %eax, %dx + movw $0xcfc, %dx + inl %dx, %eax /* Read the register */ + orl $((1 <<25)|(1 << 23)), %eax /* Set the enables */ + outl %eax, %dx /* Start initializing SDRAM */ +enable_sdram_wait: + movw $0xcfc, %dx + inl %dx, %eax + testl $(1 << 23), %eax /* Wait until the mode register is set */ + jnz enable_sdram_wait + + TTYS0_TX_STRING($ram_enabled) + + RET_LABEL(enable_sdram) + + + /* + * Routine: enable_refresh + * Arguments: None + * Results: None + * + * Preserved: None + * Effects: During initialization refresh of the DIMMS has + * been disabled so that it won't get in the way + * of the initialization logic. Now we enable + * refresh so the DIMMS will preserve their contents. + */ +enable_refresh: + movl $0x80000058, %eax + movw $0xcf8, %dx + outl %eax, %dx + movw $0xcfc, %dx + inl %dx, %eax + andl $(~(1 << 19)), %eax + outl %eax, %dx + RET_LABEL(enable_refresh) + +#define ENABLE_REFRESH() CALL_LABEL(enable_refresh) + +#define FIRST_NORMAL_REFERENCE() + + + +#if DEBUG_RAM_CONFIG +#define SPECIAL_FINISHUP() CALL_LABEL(dump_sdram_config) +#else +#define SPECIAL_FINISHUP() +#endif + +get_ecc_ram_size_bytes_ebx: + movl $0x48, %eax + PCI_READ_CONFIG_DWORD + shrl $10, %eax + and $3, %eax + xorl %ebx, %ebx /* clear %ebx in case I don't have ecc enabled */ + /* See if ECC is enabled for the SDRAM */ + testl %eax, %eax + jz get_ecc_ram_size_bytes_ebx_out + + movl $0x9c, %eax + PCI_READ_CONFIG_DWORD + movl %eax, %ebx +get_ecc_ram_size_bytes_ebx_out: + RET_LABEL(get_ecc_ram_size_bytes_ebx) + +amd76x_out: diff --git a/src/northbridge/amd/amd76x/reset_test.inc b/src/northbridge/amd/amd76x/reset_test.inc new file mode 100644 index 0000000000..3c174bb11e --- /dev/null +++ b/src/northbridge/amd/amd76x/reset_test.inc @@ -0,0 +1,8 @@ + /* If I have already booted once skip a bunch of initialization */ + /* To see if I have already booted I check to see if memory + * has been enabled. + */ + movl $0x58, %eax + PCI_READ_CONFIG_DWORD + testl $(1<<25), %eax + jnz __cpu_reset diff --git a/src/northbridge/amd/amd76x/set_memory_size.inc b/src/northbridge/amd/amd76x/set_memory_size.inc new file mode 100644 index 0000000000..2506ec869a --- /dev/null +++ b/src/northbridge/amd/amd76x/set_memory_size.inc @@ -0,0 +1,14 @@ +jmp set_memory_size_out + +#include +set_memory_size: + /* Read the top of memory */ + movl $0x9c, %eax + PCI_READ_CONFIG_DWORD + +#if USE_AMD_NDA_CODE +#endif /* USE_AMD_NDA_CODE */ + +set_memory_size_end: + RETSP +set_memory_size_out: diff --git a/src/pc80/i8259.c b/src/pc80/i8259.c new file mode 100644 index 0000000000..95be6f554e --- /dev/null +++ b/src/pc80/i8259.c @@ -0,0 +1,44 @@ +#ifdef I8259 + +#include +#include +/* code taken from: +! +! setup.S Copyright (C) 1991, 1992 Linus Torvalds +! +! setup.s is responsible for getting the system data from the BIOS, +! and putting them into the appropriate places in system memory. +! both setup.s and system has been loaded by the bootblock. + */ +/* we're getting screwed again and again by this problem of the 8259. + * so we're going to leave this lying around for inclusion into + * crt0.S on an as-needed basis. +! well, that went ok, I hope. Now we have to reprogram the interrupts :-( +! we put them right after the intel-reserved hardware interrupts, at +! int 0x20-0x2F. There they won't mess up anything. Sadly IBM really +! messed this up with the original PC, and they haven't been able to +! rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f, +! which is used for the internal hardware interrupts as well. We just +! have to reprogram the 8259's, and it isn't fun. + */ + +void setup_i8259(void) +{ + outb(0x11, 0x20); /*! initialization sequence to 8259A-1*/ + outb(0x11, 0xA0); /*! and to 8259A-2*/ + outb(0x20, 0x21); /*! start of hardware int's (0x20)*/ + outb(0x28, 0xA1); /*! start of hardware int's 2 (0x28)*/ + outb(0x04, 0x21); /*! 8259-1 is master*/ + outb(0x02, 0xA1); /*! 8259-2 is slave*/ + outb(0x01, 0x21); /*! 8086 mode for both*/ + outb(0x01, 0xA1); + outb(0xFF, 0xA1); /*! mask off all interrupts for now*/ + outb(0xFB, 0x21); /*! mask all irq's but irq2 which is cascaded*/ +} + +/* + * I like the way Linus says it: +! Well, that certainly wasn't fun :-(. Hopefully it works, and we don't +! need no steenking BIOS anyway (except for the initial loading :-). +*/ +#endif /* I8259 */ diff --git a/src/pc80/isa-dma.c b/src/pc80/isa-dma.c new file mode 100644 index 0000000000..45cfb88a97 --- /dev/null +++ b/src/pc80/isa-dma.c @@ -0,0 +1,43 @@ +#include + +/* DMA controller registers */ +#define DMA1_CMD_REG 0x08 /* command register (w) */ +#define DMA1_STAT_REG 0x08 /* status register (r) */ +#define DMA1_REQ_REG 0x09 /* request register (w) */ +#define DMA1_MASK_REG 0x0A /* single-channel mask (w) */ +#define DMA1_MODE_REG 0x0B /* mode register (w) */ +#define DMA1_CLEAR_FF_REG 0x0C /* clear pointer flip-flop (w) */ +#define DMA1_TEMP_REG 0x0D /* Temporary Register (r) */ +#define DMA1_RESET_REG 0x0D /* Master Clear (w) */ +#define DMA1_CLR_MASK_REG 0x0E /* Clear Mask */ +#define DMA1_MASK_ALL_REG 0x0F /* all-channels mask (w) */ + +#define DMA2_CMD_REG 0xD0 /* command register (w) */ +#define DMA2_STAT_REG 0xD0 /* status register (r) */ +#define DMA2_REQ_REG 0xD2 /* request register (w) */ +#define DMA2_MASK_REG 0xD4 /* single-channel mask (w) */ +#define DMA2_MODE_REG 0xD6 /* mode register (w) */ +#define DMA2_CLEAR_FF_REG 0xD8 /* clear pointer flip-flop (w) */ +#define DMA2_TEMP_REG 0xDA /* Temporary Register (r) */ +#define DMA2_RESET_REG 0xDA /* Master Clear (w) */ +#define DMA2_CLR_MASK_REG 0xDC /* Clear Mask */ +#define DMA2_MASK_ALL_REG 0xDE /* all-channels mask (w) */ + +#define DMA_MODE_READ 0x44 /* I/O to memory, no autoinit, increment, single mode */ +#define DMA_MODE_WRITE 0x48 /* memory to I/O, no autoinit, increment, single mode */ +#define DMA_MODE_CASCADE 0xC0 /* pass thru DREQ->HRQ, DACK<-HLDA only */ + +#define DMA_AUTOINIT 0x10 + + +void isa_dma_init(void) +{ + /* slave at 0x00 - 0x0f */ + /* master at 0xc0 - 0xdf */ + /* 0x80 - 0x8f DMA page registers */ + /* DMA: 0x00, 0x02, 0x4, 0x06 base address for DMA channel */ + outb(0, DMA1_RESET_REG); + outb(0, DMA2_RESET_REG); + outb(DMA_MODE_CASCADE, DMA2_MODE_REG); + outb(0, DMA2_MASK_REG); +} diff --git a/src/pc80/keyboard.c b/src/pc80/keyboard.c index 044a7238ce..9dc56b5698 100644 --- a/src/pc80/keyboard.c +++ b/src/pc80/keyboard.c @@ -2,7 +2,7 @@ static char rcsid[] = "$Id$"; #endif -#include +#include #include /* much better keyboard init courtesy ollie@sis.com.tw TODO: Typematic Setting, the keyboard is too slow for me */ @@ -13,7 +13,7 @@ void pc_keyboard_init() /* send cmd = 0xAA, self test 8042 */ outb(0xaa, 0x64); - /* empty inut bufferm or any other command/data will be lost */ + /* empty input buffer or any other command/data will be lost */ while ((inb(0x64) & 0x02)) post_code(0); /* empty output buffer or any other command/data will be lost */ @@ -52,3 +52,4 @@ void pc_keyboard_init() if ((regval = inb(0x60) != 0xaa)) return; } + diff --git a/src/pc80/mc146818rtc.c b/src/pc80/mc146818rtc.c new file mode 100644 index 0000000000..8f18e461fb --- /dev/null +++ b/src/pc80/mc146818rtc.c @@ -0,0 +1,161 @@ +#include +#include + +#define RTC_PORT(x) (0x70 + (x)) + +#define CMOS_READ(addr) ({ \ +outb_p((addr),RTC_PORT(0)); \ +inb_p(RTC_PORT(1)); \ +}) + +#define CMOS_WRITE(val, addr) ({ \ +outb_p((addr),RTC_PORT(0)); \ +outb_p((val),RTC_PORT(1)); \ +}) + +/* control registers - Moto names + */ +#define RTC_REG_A 10 +#define RTC_REG_B 11 +#define RTC_REG_C 12 +#define RTC_REG_D 13 + + +/********************************************************************** + * register details + **********************************************************************/ +#define RTC_FREQ_SELECT RTC_REG_A + +/* update-in-progress - set to "1" 244 microsecs before RTC goes off the bus, + * reset after update (may take 1.984ms @ 32768Hz RefClock) is complete, + * totalling to a max high interval of 2.228 ms. + */ +# define RTC_UIP 0x80 +# define RTC_DIV_CTL 0x70 + /* divider control: refclock values 4.194 / 1.049 MHz / 32.768 kHz */ +# define RTC_REF_CLCK_4MHZ 0x00 +# define RTC_REF_CLCK_1MHZ 0x10 +# define RTC_REF_CLCK_32KHZ 0x20 + /* 2 values for divider stage reset, others for "testing purposes only" */ +# define RTC_DIV_RESET1 0x60 +# define RTC_DIV_RESET2 0x70 + /* Periodic intr. / Square wave rate select. 0=none, 1=32.8kHz,... 15=2Hz */ +# define RTC_RATE_SELECT 0x0F +# define RTC_RATE_NONE 0x00 +# define RTC_RATE_32786HZ 0x01 +# define RTC_RATE_16384HZ 0x02 +# define RTC_RATE_8192HZ 0x03 +# define RTC_RATE_4096HZ 0x04 +# define RTC_RATE_2048HZ 0x05 +# define RTC_RATE_1024HZ 0x06 +# define RTC_RATE_512HZ 0x07 +# define RTC_RATE_256HZ 0x08 +# define RTC_RATE_128HZ 0x09 +# define RTC_RATE_64HZ 0x0a +# define RTC_RATE_32HZ 0x0b +# define RTC_RATE_16HZ 0x0c +# define RTC_RATE_8HZ 0x0d +# define RTC_RATE_4HZ 0x0e +# define RTC_RATE_2HZ 0x0f + +/**********************************************************************/ +#define RTC_CONTROL RTC_REG_B +# define RTC_SET 0x80 /* disable updates for clock setting */ +# define RTC_PIE 0x40 /* periodic interrupt enable */ +# define RTC_AIE 0x20 /* alarm interrupt enable */ +# define RTC_UIE 0x10 /* update-finished interrupt enable */ +# define RTC_SQWE 0x08 /* enable square-wave output */ +# define RTC_DM_BINARY 0x04 /* all time/date values are BCD if clear */ +# define RTC_24H 0x02 /* 24 hour mode - else hours bit 7 means pm */ +# define RTC_DST_EN 0x01 /* auto switch DST - works f. USA only */ + +/**********************************************************************/ +#define RTC_INTR_FLAGS RTC_REG_C +/* caution - cleared by read */ +# define RTC_IRQF 0x80 /* any of the following 3 is active */ +# define RTC_PF 0x40 +# define RTC_AF 0x20 +# define RTC_UF 0x10 + +/**********************************************************************/ +#define RTC_VALID RTC_REG_D +# define RTC_VRT 0x80 /* valid RAM and time */ +/**********************************************************************/ + + +/* On PCs, the checksum is built only over bytes 16..45 */ +#define PC_CKS_RANGE_START 16 +#define PC_CKS_RANGE_END 45 +#define PC_CKS_LOC 46 + +static int rtc_checksum_valid(void) +{ + int i; + unsigned sum, old_sum; + sum = 0; + for(i = PC_CKS_RANGE_START; i <= PC_CKS_RANGE_END; i++) { + sum += CMOS_READ(i); + } + sum &= 0xffff; + old_sum = (CMOS_READ(PC_CKS_LOC) << 8) | CMOS_READ(PC_CKS_LOC+1); + return sum == old_sum; +} + +static void rtc_set_checksum(void) +{ + int i; + unsigned sum; + sum = 0; + for(i = PC_CKS_RANGE_START; i <= PC_CKS_RANGE_END; i++) { + sum += CMOS_READ(i); + } + sum &= 0xffff; + CMOS_WRITE(((sum >> 8) & 0xff), PC_CKS_LOC); + CMOS_WRITE(((sum >> 0) & 0xff), PC_CKS_LOC+1); +} + +#define RTC_CONTROL_DEFAULT (RTC_24H) +#define RTC_FREQ_SELECT_DEFAULT (RTC_REF_CLCK_32KHZ | RTC_RATE_1024HZ) + +#if 0 /* alpha setup */ +#undef RTC_CONTROL_DEFAULT +#undef RTC_FREQ_SELECT_DEFAULT +#define RTC_CONTROL_DEFAULT (RTC_SQWE | RTC_24H) +#define RTC_FREQ_SELECT_DEFAULT (RTC_REF_CLCK_32KHZ | RTC_RATE_1024HZ) +#endif +void rtc_init(void) +{ + unsigned char x; + int cmos_valid; + /* See if there has been a CMOS power problem. */ + x = CMOS_READ(RTC_VALID); + cmos_valid = !(x & RTC_VRT); + + /* See if there is a CMOS checksum error */ + cmos_valid = rtc_checksum_valid(); + + if (!cmos_valid) { + int i; + printk_warning("RTC power problem, zeroing cmos\n"); + for(i = 0x0; i < 128; i++) { + CMOS_WRITE(0, i); + } + + /* Now setup a default date of Sat 1 January 2000 */ + CMOS_WRITE(0, 0x00); /* seconds */ + CMOS_WRITE(0, 0x02); /* minutes */ + CMOS_WRITE(1, 0x04); /* hours */ + CMOS_WRITE(7, 0x06); /* day of week */ + CMOS_WRITE(1, 0x07); /* day of month */ + CMOS_WRITE(1, 0x08); /* month */ + CMOS_WRITE(0, 0x09); /* year */ + } + /* Setup the real time clock */ + CMOS_WRITE(RTC_CONTROL_DEFAULT, RTC_CONTROL); + /* Setup the frequency it operates at */ + CMOS_WRITE(RTC_FREQ_SELECT_DEFAULT, RTC_FREQ_SELECT); + /* Make certain we have a valid checksum */ + rtc_set_checksum(); + /* Clear any pending interrupts */ + (void) CMOS_READ(RTC_INTR_FLAGS); +} diff --git a/src/sdram/generic_dump_spd.inc b/src/sdram/generic_dump_spd.inc new file mode 100644 index 0000000000..9f8981d54d --- /dev/null +++ b/src/sdram/generic_dump_spd.inc @@ -0,0 +1,38 @@ +dump_spd_registers: + movl $((0 << 8) | SMBUS_MEM_DEVICE_START), %ebx +dump_spd_reg_dimm: + TTYS0_TX_CHAR($'\r') + TTYS0_TX_CHAR($'\n') + TTYS0_TX_CHAR($'d') + TTYS0_TX_CHAR($'i') + TTYS0_TX_CHAR($'m') + TTYS0_TX_CHAR($'m') + TTYS0_TX_CHAR($' ') + movb %bl, %al + CALLSP(ttys0_tx_hex8) + TTYS0_TX_CHAR($'\r') + TTYS0_TX_CHAR($'\n') +dump_spd_reg_byte: + CALLSP(smbus_read_byte) + jz dump_spd_reg_next_dimm + + CALLSP(ttys0_tx_hex8) + TTYS0_TX_CHAR($' ') + incb %bh + testb $0x0F, %bh + jnz dump_spd_reg_next_byte + TTYS0_TX_CHAR($'\r') + TTYS0_TX_CHAR($'\n') + +dump_spd_reg_next_byte: + cmpb $0, %bh + jne dump_spd_reg_byte + +dump_spd_reg_next_dimm: + TTYS0_TX_CHAR($'\r') + TTYS0_TX_CHAR($'\n') + xorb %bh, %bh + add $SMBUS_MEM_DEVICE_INC, %bl + cmpb $(SMBUS_MEM_DEVICE_END + SMBUS_MEM_DEVICE_INC), %bl + jne dump_spd_reg_dimm +dump_spd_registers_out: diff --git a/src/sdram/generic_sdram_enable.inc b/src/sdram/generic_sdram_enable.inc new file mode 100644 index 0000000000..1866bb8100 --- /dev/null +++ b/src/sdram/generic_sdram_enable.inc @@ -0,0 +1,73 @@ +jmp generic_sdram_enable_out + +ram_enable_1: .string "Ram Enable 1\r\n" +ram_enable_2: .string "Ram Enable 2\r\n" +ram_enable_3: .string "Ram Enable 3\r\n" +ram_enable_4: .string "Ram Enable 4\r\n" +ram_enable_5: .string "Ram Enable 5\r\n" + + /* Estimate that SLOW_DOWN_IO takes about 50&76us*/ + /* delay for 200us */ + +#define DO_DELAY \ + movl $4, %edi ; \ +1: SLOW_DOWN_IO ; \ + decl %edi ; \ + jnz 1b + + +enable_sdram: +/* now the fun begins. + turn on the dram and wait a while (this from the intel book) + turn power on and set the nop bit too + */ + TTYS0_TX_STRING($ram_enable_1) + /* SDRAMC */ + SET_RAM_COMMAND(RAM_COMMAND_NOP) + + DO_DELAY + + ASSERT_RAM_COMMAND() /* nop command */ + + /* Precharge all */ + SET_RAM_COMMAND(RAM_COMMAND_PRECHARGE) + ASSERT_RAM_COMMAND() + + /* wait until the all banks idle state... */ + + TTYS0_TX_STRING($ram_enable_2) + + /* Now we need 8 AUTO REFRESH / CBR cycles to be performed */ + + SET_RAM_COMMAND(RAM_COMMAND_CBR) + ASSERT_RAM_COMMAND() + ASSERT_RAM_COMMAND() + ASSERT_RAM_COMMAND() + ASSERT_RAM_COMMAND() + ASSERT_RAM_COMMAND() + ASSERT_RAM_COMMAND() + ASSERT_RAM_COMMAND() + ASSERT_RAM_COMMAND() + + TTYS0_TX_STRING($ram_enable_3) + + /* mode register set */ + SET_RAM_MODE_REGISTER + + /* MAx[14:0] lines, + * MAx[2:0 ] 010 == burst mode of 4 + * MAx[3:3 ] 1 == interleave wrap type + * MAx[4:4 ] == CAS# latency bit + * MAx[6:5 ] == 01 + * MAx[12:7] == 0 + */ + + TTYS0_TX_STRING($ram_enable_4) + + /* normal operation */ + SET_RAM_COMMAND(RAM_COMMAND_NONE) + + TTYS0_TX_STRING($ram_enable_5) + RET_LABEL(enable_sdram) + +generic_sdram_enable_out: diff --git a/src/sdram/generic_zero_ecc_sdram.inc b/src/sdram/generic_zero_ecc_sdram.inc new file mode 100644 index 0000000000..1257714232 --- /dev/null +++ b/src/sdram/generic_zero_ecc_sdram.inc @@ -0,0 +1,102 @@ +jmp ecc_ram_initialize + +ecc_ram_1: .string "ecc_ram_1\r\n" +ecc_ram_2: .string "ecc_ram_2\r\n" +ecc_ram_3: .string "ecc_ram_3\r\n" +ecc_ram_4: .string "ecc_ram_4\r\n" + +ecc_ram_initialize: + TTYS0_TX_STRING($ecc_ram_1) + CALL_LABEL(get_ecc_ram_size_bytes_ebx) + + /* If we don't have an ECC SDRAM size skip the zeroing */ + testl %ebx, %ebx + jz zero_ecc_ram_out + movl %ebx, %ebp + + /* Compute the next greater power of two memory size, to use in the mtrrs */ + bsrl %ebp, %ecx + movl $1, %esi + shll %cl, %esi + /* See if I need to round up */ + subl $1, %esi + testl %esi, %ebp + jz 1f + incl %ecx +1: movl $1, %esi + shll %cl, %esi + + /* Set caching on all of memory into write-combining mode. + * So we can zero it quickly. + */ + /* Disable the cache while we set up a new MTRR over memory */ + movl %cr0, %eax + orl $0x40000000, %eax + movl %eax, %cr0 + + movl $0x200, %ecx /* mtrr[0] physical base register */ + movl $0x00000000, %edx + movl $0x00000001, %eax + wrmsr + + movl $0x201, %ecx /* mtrr[0] physical mask register */ + movl $0x0000000f, %edx + xorl %eax, %eax + subl %esi, %eax + andl $0xfffff000, %eax + orl $0x800, %eax + wrmsr + + /* Reenable the cache now that the mtrr is set up */ + movl %cr0, %eax + andl $0x9fffffff, %eax + movl %eax, %cr0 + + /* Now zero the memory */ + TTYS0_TX_STRING($ecc_ram_2) + cld + +#if !defined(HAVE_PC80_MEMORY_HOLE) + /* The 640KB - 1MB memory should not be enabled at this point. */ + xorl %eax, %eax + xorl %edi, %edi + movl %ebp, %ecx + shrl $2, %ecx + rep stosl + +#else /* HAVE_PC80_MEMORY_HOLE */ + + xorl %eax, %eax /* zero */ + xorl %edi, %edi /* destination */ + movl $0x28000,%ecx + rep stosl + + xorl %eax, %eax + movl $0x100000, %edi + movl %ebp, %ecx + subl %edi, %ecx + shrl $2, %ecx + rep stosl +#endif /* HAVE_PC80_MEMORY_HOLE */ + + TTYS0_TX_STRING($ecc_ram_3) + + /* Change caching on memory from write-combining to write-back. */ + /* Disable the cache while we set up a new MTRR over memory */ + movl %cr0, %eax + orl $0x40000000, %eax + movl %eax, %cr0 + + movl $0x200, %ecx + movl $0x00000000, %edx + movl $0x00000006, %eax + wrmsr + + /* Reenable the cache now that the mtrr is set up */ + movl %cr0, %eax + andl $0x9fffffff, %eax + movl %eax, %cr0 + +zero_ecc_ram_out: + TTYS0_TX_STRING($ecc_ram_4) + diff --git a/src/sdram/smbus_pcibus.inc b/src/sdram/smbus_pcibus.inc index 7e01016f41..0298082229 100644 --- a/src/sdram/smbus_pcibus.inc +++ b/src/sdram/smbus_pcibus.inc @@ -1,5 +1,5 @@ /* Useful macros PCIBUS, and SMBUS functions for getting DRAM going. */ -/* courtesy Eric Beiderman of linuxnetworx.com */ +/* courtesy Eric Biederman of linuxnetworx.com */ #define CS_WRITE_BYTE(addr, byte) \ movl $addr, %eax ; \ diff --git a/src/southbridge/amd/amd766/Config b/src/southbridge/amd/amd766/Config new file mode 100644 index 0000000000..aff8055c22 --- /dev/null +++ b/src/southbridge/amd/amd766/Config @@ -0,0 +1,3 @@ +object southbridge.o +object nvram.o + diff --git a/src/southbridge/amd/amd766/disable_watchdog.inc b/src/southbridge/amd/amd766/disable_watchdog.inc new file mode 100644 index 0000000000..d74d5169ec --- /dev/null +++ b/src/southbridge/amd/amd766/disable_watchdog.inc @@ -0,0 +1,10 @@ +#define PM_DEV_FN (AMD766_DEV + 0x300) + /* Disable the watchdog timer */ + movl $(0x80000000 | PM_DEV_FN | 0x40), %eax + movw $0xcf8, %dx + outl %eax, %dx + movw $0xcfd, %dx + inb %dx, %al + orb $0x44, %al + outb %al, %dx + diff --git a/src/southbridge/amd/amd766/lpc_com1.inc b/src/southbridge/amd/amd766/lpc_com1.inc new file mode 100644 index 0000000000..71607eda13 --- /dev/null +++ b/src/southbridge/amd/amd766/lpc_com1.inc @@ -0,0 +1,16 @@ + /* enable LPC superio on the AMD 766 south bridge */ + movl $(0x80000000 | AMD766_DEV | 0x54), %eax + movw $0xcf8, %dx + outl %eax, %dx + movw $0xcfc, %dx + inb %dx, %al + orb $0x30, %al + outb %al, %dx + + /* enable LPC serial port 0x3f8 */ + movl $(0x80000000 | AMD766_DEV | 0x50), %eax + movw $0xcf8, %dx + outl %eax, %dx + movw $0xcfe, %dx + movb $0x8, %al + outb %al, %dx diff --git a/src/southbridge/amd/amd766/nvram.c b/src/southbridge/amd/amd766/nvram.c new file mode 100644 index 0000000000..2826349742 --- /dev/null +++ b/src/southbridge/amd/amd766/nvram.c @@ -0,0 +1,18 @@ +#include +#include +#include +#include + +void nvram_on(void) +{ + struct pci_dev *dev; + dev = pci_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7410, 0); + if (dev != NULL) { + u8 segen; + u32 regval; + pci_read_config_byte(dev, 0x43, &segen); + /* Enable 4MB rom access at 0xFFC00000 - 0xFFFFFFFF */ + segen |= 0x80; + pci_write_config_byte(dev, 0x43, segen); + } +} diff --git a/src/southbridge/amd/amd766/smbus.inc b/src/southbridge/amd/amd766/smbus.inc new file mode 100644 index 0000000000..d164565329 --- /dev/null +++ b/src/southbridge/amd/amd766/smbus.inc @@ -0,0 +1,182 @@ +jmp smbus_code_end + +#define PM_DEV_FN (AMD766_DEV + 0x300) + + +#define PM_BASE 0xDD00 +#define SMBUS_IO_BASE (PM_BASE + 0xE0) +#define SMB_GSTAT (SMBUS_IO_BASE + 0x0) +#define SMB_GCTL (SMBUS_IO_BASE + 0x2) +#define SMB_HOST_ADDR (SMBUS_IO_BASE + 0x4) +#define SMB_HOST_DAT (SMBUS_IO_BASE + 0x6) +#define SMB_HOST_CMD (SMBUS_IO_BASE + 0x8) +#define SMB_BLKDAT (SMBUS_IO_BASE + 0x9) +#define SMB_SLAVE_DAT (SMBUS_IO_BASE + 0xa) +#define SMB_SLAVE_DEV (SMBUS_IO_BASE + 0xc) +#define SMB_SLAVE_ADDR (SMBUS_IO_BASE + 0xe) +#define SMB_SNOOP_ADDR (SMBUS_IO_BASE + 0xf) + +#define SMB_GSTAT_ABORT (1 << 0) +#define SMB_GSTAT_COLLISION (1 << 1) +#define SMB_GSTAT_PROTO_ERROR (1 << 2) +#define SMB_GSTAT_HOST_BUSY (1 << 3) +#define SMB_GSTAT_COMPLETE (1 << 4) +#define SMB_GSTAT_TIMEOUT (1 << 5) +#define SMB_GSTAT_SNOOP_MATCH (1 << 8) +#define SMB_GSTAT_SLAVE_MATCH (1 << 9) +#define SMB_GSTAT_ALERT (1 << 10) +#define SMB_GSTAT_BUSY (1 << 11) + +/* The low bit of the address specifies read or write */ +#define SMB_GCTL_QUICK_COMMAND 0 +#define SMB_GCTL_SEND_RECV_BYTE 1 +#define SMB_GCTL_WRITE_READ_BYTE 2 +#define SMB_GCTL_WRITE_READ_WORD 3 +#define SMB_GCTL_PROCESS_CALL 4 +#define SMB_GCTL_WRITE_READ_BLOCK 5 + +#define SMB_GCTL_HOST_START (1 << 3) +#define SMB_GCTL_HOST_INTERRUPT (1 << 4) +#define SMB_GCTL_ABORT (1 << 5) +#define SMB_GCTL_SNOOP (1 << 8) +#define SMB_GCTL_SLAVE_INTERRUPT (1 << 9) +#define SMB_GCTL_ALERT_INTERRUPT (1 << 10) + + +enable_smbus: + /* Enable PM IO C3A41 */ + movl $(0x80000000 | PM_DEV_FN | 0x40), %eax + movw $0xcf8, %dx + outl %eax, %dx + movw $0xcfd, %dx + inb %dx, %al + orb $0x80, %al + outb %al, %dx + + /* Set the PM registers to 0xDD00 */ + movl $(0x80000000 | PM_DEV_FN | 0x58), %eax + mov $0xcf8, %dx + outl %eax, %dx + movw $0xcfc, %dx + movl $(PM_BASE | 0x01), %eax + outl %eax, %dx + + RET_LABEL(enable_smbus) + + /* + * Routine: setup_smbus + * Arguments: none + * Results: none + * Trashed: eax, edx + * Effects: The smbus is enabled + */ +setup_smbus: + movl $(SMB_GSTAT_ABORT | SMB_GSTAT_COLLISION | \ + SMB_GSTAT_PROTO_ERROR | SMB_GSTAT_COMPLETE | \ + SMB_GSTAT_TIMEOUT | SMB_GSTAT_SNOOP_MATCH | \ + SMB_GSTAT_SLAVE_MATCH | SMB_GSTAT_ALERT),%eax + movw $SMB_GSTAT, %dx + outb %al, %dx + RET_LABEL(setup_smbus) + + /* + * 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 $SMB_GSTAT, %edx +1: inb %dx, %al + testb $SMB_GSTAT_HOST_BUSY, %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 $SMB_GSTAT, %edx +1: inb %dx, %al + testb $(SMB_GSTAT_HOST_BUSY), %al + jnz 1b +2: testb $(~(SMB_GSTAT_HOST_BUSY)), %al + jnz 3f + inb %dx, %al + testb $(~(SMB_GSTAT_HOST_BUSY)), %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 + */ + +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 */ + movw $SMB_GSTAT, %dx + inw %dx, %ax + outw %ax, %dx + + /* set the device I'm talking to, and set the low bit for a read */ + movw $SMB_HOST_ADDR, %dx + xorl %eax, %eax + movb %bl /* device */, %al + shlb $1, %al + orb $1, %al + outw %ax, %dx + + /* set the command address... */ + movw $SMB_HOST_CMD, %dx + movb %bh /* address */, %al + outb %al, %dx + + /* clear the data byte */ + movw $SMB_HOST_DAT, %dx + xorl %eax, %eax + outw %ax, %dx + + /* start a byte read, with interrupts disabled */ + movw $SMB_GCTL, %dx + movl $(SMB_GCTL_HOST_START | SMB_GCTL_WRITE_READ_BYTE), %eax + outw %ax, %dx + + /* poll for transaction completion */ + CALL_LABEL(smbus_wait_until_done) + + /* read the results and see if we succeded */ + movl $SMB_GSTAT, %edx + inb %dx, %al + testb $(SMB_GSTAT_COMPLETE), %al + jz 1f + movw $SMB_HOST_DAT, %dx + inb %dx, %al +1: + RETSP + + + +smbus_code_end: +CALL_LABEL(enable_smbus) +CALL_LABEL(setup_smbus) \ No newline at end of file diff --git a/src/southbridge/amd/amd766/southbridge.c b/src/southbridge/amd/amd766/southbridge.c new file mode 100644 index 0000000000..aab2cd275f --- /dev/null +++ b/src/southbridge/amd/amd766/southbridge.c @@ -0,0 +1,7 @@ +void southbridge_fixup(void) +{ + +} + + + diff --git a/src/superio/winbond/w83627hf/setup_serial.inc b/src/superio/winbond/w83627hf/setup_serial.inc new file mode 100644 index 0000000000..81375823e8 --- /dev/null +++ b/src/superio/winbond/w83627hf/setup_serial.inc @@ -0,0 +1,51 @@ +/* + * Enable the peripheral devices on the windbond w83627hf + */ + + +/* The base address is 0x2e,0x4e depending on config bytes */ + +#define SIO_INDEX SIO_BASE +#define SIO_DATA SIO_BASE+1 + +#define SIO_COM1_DEVICE 2 + +#define SIO_ENTER_PNP_MODE() \ + movw $SIO_BASE, %dx ; \ + movb $0x87, %al ; \ + outb %al, %dx ; \ + outb %al, %dx + + +#define SIO_EXIT_PNP_MODE() \ + movw $SIO_BASE, %dx ; \ + movb $0xaa, %al ; \ + outb %al, %dx + +#define SIO_WRITE_CONFIG(value, reg) \ + movw $SIO_BASE, %dx ; \ + movb $reg, %al ; \ + outb %al, %dx ; \ + incw %dx ; \ + movb $value, %al ; \ + outb %al, %dx + +#define SIO_READ_CONFIG(value, reg) \ + movw $SIO_BASE, %dx ; \ + movb $reg, %al ; \ + outb %al, %dx ; \ + incw %dx ; \ + inb %al, %dx + +#define SIO_SET_LOGICAL_DEVICE(device) \ + SIO_WRITE_CONFIG(device, 0x07) + + + /* enable serial 1 */ + SIO_ENTER_PNP_MODE() + SIO_SET_LOGICAL_DEVICE(SIO_COM1_DEVICE) + SIO_WRITE_CONFIG(1, 0x30) + SIO_WRITE_CONFIG(0x3, 0x60) + SIO_WRITE_CONFIG(0xf8, 0x61) + SIO_EXIT_PNP_MODE() + diff --git a/src/superio/winbond/w83627hf/superio.c b/src/superio/winbond/w83627hf/superio.c new file mode 100644 index 0000000000..8f6fa740bb --- /dev/null +++ b/src/superio/winbond/w83627hf/superio.c @@ -0,0 +1,259 @@ +#include +#include +#include +#include + +#define FLOPPY_DEVICE 0 +#define PARALLEL_DEVICE 1 +#define COM1_DEVICE 2 +#define COM2_DEVICE 3 +#define KBC_DEVICE 5 +#define CIR_DEVICE 6 +#define GAME_PORT_DEVICE 7 +#define GPIO_PORT2_DEVICE 8 +#define GPIO_PORT3_DEVICE 9 +#define ACPI_DEVICE 0xa +#define HW_MONITOR_DEVICE 0xb + + +#define FLOPPY_DEFAULT_IOBASE 0x3f0 +#define FLOPPY_DEFAULT_IRQ 6 +#define FLOPPY_DEFAULT_DRQ 2 +#define PARALLEL_DEFAULT_IOBASE 0x378 +#define PARALLEL_DEFAULT_IRQ 7 +#define PARALLEL_DEFAULT_DRQ 4 /* No dma */ +#define COM1_DEFAULT_IOBASE 0x3f8 +#define COM1_DEFAULT_IRQ 4 +#define COM1_DEFAULT_BAUD 115200 +#define COM2_DEFAULT_IOBASE 0x2f8 +#define COM2_DEFAULT_IRQ 3 +#define COM2_DEFAULT_BAUD 115200 +#define KBC_DEFAULT_IOBASE0 0x60 +#define KBC_DEFAULT_IOBASE1 0x64 +#define KBC_DEFAULT_IRQ0 0x1 +#define KBC_DEFAULT_IRQ1 0xc + + +static void enter_pnp(struct superio *sio) +{ + outb(0x87, sio->port); + outb(0x87, sio->port); +} + +static void exit_pnp(struct superio *sio) +{ + outb(0xaa, sio->port); +} + +static void write_config(struct superio *sio, + unsigned char value, unsigned char reg) +{ + outb(reg, sio->port); + outb(value, sio->port +1); +} + +static unsigned char read_config(struct superio *sio, unsigned char reg) +{ + outb(reg, sio->port); + return inb(sio->port +1); +} +static void set_logical_device(struct superio *sio, int device) +{ + write_config(sio, device, 0x07); +} + +static void set_enable(struct superio *sio, int enable) +{ + write_config(sio, enable?0x1:0x0, 0x30); +#if 0 + if (enable) { + printk_debug("enabled superio device: %d\n", + read_config(sio, 0x07)); + } +#endif +} + +static void set_iobase0(struct superio *sio, unsigned iobase) +{ + write_config(sio, (iobase >> 8) & 0xff, 0x60); + write_config(sio, iobase & 0xff, 0x61); +} + +static void set_iobase1(struct superio *sio, unsigned iobase) +{ + write_config(sio, (iobase >> 8) & 0xff, 0x62); + write_config(sio, iobase & 0xff, 0x63); +} + +static void set_irq0(struct superio *sio, unsigned irq) +{ + write_config(sio, irq, 0x70); +} + +static void set_irq1(struct superio *sio, unsigned irq) +{ + write_config(sio, irq, 0x72); +} + +static void set_drq(struct superio *sio, unsigned drq) +{ + write_config(sio, drq & 0xff, 0x74); +} + +static void enable_com(struct superio *sio, + struct com_ports *com, int device) +{ + int divisor = 115200/com->baud; + printk_debug("Enabling com device: %02x\n", device); + printk_debug(" iobase = 0x%04x irq=%d\n", com->base, com->irq); + /* Select the device */ + set_logical_device(sio, device); + /* Disable it while it is initialized */ + set_enable(sio, 0); + if (com->enable) { + set_iobase0(sio, com->base); + set_irq0(sio, com->irq); + /* We are initialized so enable the device */ + set_enable(sio, 1); + /* Now initialize the com port */ + uart_init(com->base, divisor); + } +} + +static void enable_floppy(struct superio *sio) +{ + /* Remember the default resources */ + unsigned iobase = FLOPPY_DEFAULT_IOBASE; + unsigned irq = FLOPPY_DEFAULT_IRQ; + unsigned drq = FLOPPY_DEFAULT_DRQ; + /* Select the device */ + set_logical_device(sio, FLOPPY_DEVICE); + /* Disable it while initializing */ + set_enable(sio, 0); + if (sio->lpt) { + set_iobase0(sio, iobase); + set_irq0(sio, irq); + set_drq(sio, drq); + set_enable(sio, 1); + } +} + +static void enable_parallel(struct superio *sio) +{ + /* Remember the default resources */ + unsigned iobase = PARALLEL_DEFAULT_IOBASE; + unsigned irq = PARALLEL_DEFAULT_IRQ; + unsigned drq = PARALLEL_DEFAULT_DRQ; + /* Select the device */ + set_logical_device(sio, PARALLEL_DEVICE); + /* Disable it while initializing */ + set_enable(sio, 0); + if (sio->lpt) { + set_iobase0(sio, iobase); + set_irq0(sio, irq); + set_drq(sio, drq); + set_enable(sio, 1); + } +} + +static void enable_keyboard(struct superio *sio) +{ + /* Remember the default resources */ + unsigned iobase0 = KBC_DEFAULT_IOBASE0; + unsigned iobase1 = KBC_DEFAULT_IOBASE1; + unsigned irq0 = KBC_DEFAULT_IRQ0; + unsigned irq1 = KBC_DEFAULT_IRQ1; + /* Select the device */ + set_logical_device(sio, KBC_DEVICE); + /* Disable it while initializing */ + set_enable(sio, 0); + if (sio->lpt) { + set_iobase0(sio, iobase0); + set_iobase1(sio, iobase1); + set_irq0(sio, irq0); + set_irq1(sio, irq1); + set_enable(sio, 1); + /* Initialize the keyboard */ + pc_keyboard_init(); + } +} + + +#if 0 +static void setup_acpi_registers(struct superio *sio) +{ + set_logical_device(sio, ACPI_DEVICE); + /* Enable power on after power fail */ + write_config(sio, (1 << 7)|(0 <<5), 0xe4); + set_enable(sio, 1); +} +#endif + +static void enable_devices(struct superio *sio) +{ + if (sio->port == 0) { + sio->port = sio->super->defaultport; + } + if (sio->com1.base == 0) sio->com1.base = COM1_DEFAULT_IOBASE; + if (sio->com1.irq == 0) sio->com1.irq = COM1_DEFAULT_IRQ; + if (sio->com1.baud == 0) sio->com1.baud = COM1_DEFAULT_BAUD; + if (sio->com2.base == 0) sio->com2.base = COM2_DEFAULT_IOBASE; + if (sio->com2.irq == 0) sio->com2.irq = COM2_DEFAULT_IRQ; + if (sio->com2.baud == 0) sio->com2.baud = COM2_DEFAULT_BAUD; + + enter_pnp(sio); + + /* enable floppy */ + enable_floppy(sio); + + /* enable parallel */ + enable_parallel(sio); + + /* enable com1 */ + enable_com(sio, &sio->com1, COM1_DEVICE); + + /* enable com2 */ + enable_com(sio, &sio->com2, COM2_DEVICE); + + /* enable keyboard */ + enable_keyboard(sio); + + /* disable cir */ + set_logical_device(sio, CIR_DEVICE); + set_enable(sio, 0); + + /* disable game */ + set_logical_device(sio, GAME_PORT_DEVICE); + set_enable(sio, 0); + + /* disable gpio_port2 */ + set_logical_device(sio, GPIO_PORT2_DEVICE); + set_enable(sio, 0); + + /* disable gpio_port3 */ + set_logical_device(sio, GPIO_PORT3_DEVICE); + set_enable(sio, 0); + + /* disable acpi */ + set_logical_device(sio, ACPI_DEVICE); + set_enable(sio, 0); + + /* disable hw monitor */ + set_logical_device(sio, HW_MONITOR_DEVICE); + set_enable(sio, 0); + +#if 0 + /* setup acpi registers so I am certain to get + * power on after power fail. + */ + setup_acpi_registers(sio); +#endif + + write_config(sio, 1, 0x30); + exit_pnp(sio); +} + +/* The base address is either 0x2e or 0x4e */ +struct superio_control superio_winbond_w83627hf_control = { + (void *)0, enable_devices, (void *)0, 0x2e, "w83627hf" +};