diff --git a/src/southbridge/intel/82801db/82801.h b/src/southbridge/intel/82801db/82801.h new file mode 100644 index 0000000000..ad2405587c --- /dev/null +++ b/src/southbridge/intel/82801db/82801.h @@ -0,0 +1,58 @@ +#define PCI_DMA_CFG 0x90 +#define SERIRQ_CNTL 0x64 +#define GEN_CNTL 0xd0 +#define GEN_STS 0xd4 +#define RTC_CONF 0xd8 +#define GEN_PMCON_3 0xa4 + +#define PCICMD 0x04 +#define PMBASE 0x40 +#define ACPI_CNTL 0x44 +#define BIOS_CNTL 0x4E +#define GPIO_BASE 0x58 +#define GPIO_CNTL 0x5C +#define PIRQA_ROUT 0x60 +#define PIRQE_ROUT 0x68 +#define COM_DEC 0xE0 +#define LPC_EN 0xE6 +#define FUNC_DIS 0xF2 + +/* 1e f0 244e */ + +#define CMD 0x04 +#define SBUS_NUM 0x19 +#define SUB_BUS_NUM 0x1A +#define SMLT 0x1B +#define IOBASE 0x1C +#define IOLIM 0x1D +#define MEMBASE 0x20 +#define MEMLIM 0x22 +#define CNF 0x50 +#define MTT 0x70 +#define PCI_MAST_STS 0x82 + +#define RTC_BUS 0 +#define RTC_DEVFN ((0x1f << 3) + 0) +#define RTC_FAILED (1 <<2) + + +#define SMBUS_BUS 0 +#define SMBUS_DEVFN ((0x1f << 3) + 3) +#define SMBUS_IO_BASE 0x1000 + +#define SMBHSTSTAT 0x0 +#define SMBHSTCTL 0x2 +#define SMBHSTCMD 0x3 +#define SMBXMITADD 0x4 +#define SMBHSTDAT0 0x5 +#define SMBHSTDAT1 0x6 +#define SMBBLKDAT 0x7 +#define SMBTRNSADD 0x9 +#define SMBSLVDATA 0xa +#define SMLINK_PIN_CTL 0xe +#define SMBUS_PIN_CTL 0xf + +/* Between 1-10 seconds, We should never timeout normally + * Longer than this is just painful when a timeout condition occurs. + */ +#define SMBUS_TIMEOUT (100*1000) diff --git a/src/southbridge/intel/82801db/Config b/src/southbridge/intel/82801db/Config new file mode 100644 index 0000000000..c6adb65c7d --- /dev/null +++ b/src/southbridge/intel/82801db/Config @@ -0,0 +1,11 @@ +object nvram.o +object ich3_ioapic.o +object ich3_lpc.o +object ich3_ide.o +object ich3_reset.o +object ich3_smbus.o +object ich3_cpu.o +object ich3_rtc.o +object ich3_power.o +object ich3_1e0_misc.o +object ich3_1f0_misc.o diff --git a/src/southbridge/intel/82801db/cmos_failover.inc b/src/southbridge/intel/82801db/cmos_failover.inc new file mode 100644 index 0000000000..e68c376a25 --- /dev/null +++ b/src/southbridge/intel/82801db/cmos_failover.inc @@ -0,0 +1,148 @@ +/* The algorithm is as follows: + * + * Step 1: Test for cpu reset + * That is, did I just boot or is this a later boot since power on. + * The result of this test in %al + * %al == 1 -- We are rebooting + * %al == 0 -- This is the initial boot + * + * Step 2: Cpu filter + * On an initial boot if we are not the bootstrap CPU go to + * sleep. + * + * Step 3: Test for CMOS validity. + * On an initial boot if the CMOS checksum or the CMOS error + * condition is signaled clear the CMOS ram. + * + * Step 4: Test for which copy of linuxbios to boot. + * + * We use 6 bits of CMOS ram. + * Bit 0: Initial boot direction + * 0 - Boot the failsafe image. + * 1 - Boot the other image. + * Bit 1: Reboot direction + * 0 - Boot the failsafe image. + * 1 - Boot the other image. + * Bits 4-7: Reboot count + * + * On initial boot we read bit0, and write it to bit1, and clear + * bit0. Additionally we clear bit1 if we bit0 was initially set + * and we have reached the maximum boot count. + * + * On a reboot the bits are not touched. + * + * Then the appropriate image is jumped to. + * + */ +#include "82801.h" +#include +#include + +#ifndef MAX_REBOOT_CNT +#error "MAX_REBOOT_CNT not defined" +#endif +#if MAX_REBOOT_CNT > 15 +#error "MAX_REBOOT_CNT to large" +#endif + + /* Intel systems magically stop the second cpu in hardware */ + testb %al, %al + jz __failover_boot + +__failover_reset: + movb $RTC_BOOT_BYTE, %al + outb %al, $0x70 + inb $0x71, %al + testb $(1<<1), %al + jnz __normal_image + jmp __cpu_reset + + +__failover_boot: + /* See if the cmos clear jumper has been set */ + movl $((RTC_DEVFN << 8) | GEN_PMCON_3), %eax + PCI_READ_CONFIG_DWORD + testl $RTC_FAILED, %eax + jz __cs_test + + /* There are no impossible values, no checksums + * so just trust whatever value we have in the + * cmos. + */ +__rtc_failed: + movb $RTC_BOOT_BYTE, %al + outb %al, $0x70 + inb $0x71, %al + andb $0xfc, %al + outb %al, $0x71 + jmp __cs_test + + /* test the checksum */ +__cs_test: + movl $77,%ecx + xor %ebx,%ebx + movl $RTC_BOOT_BYTE, %edx +1: + addl $1, %edx + movl %edx, %eax + outb %al, $0x70 + inb $0x71, %al + addl %eax,%ebx + subl $1,%ecx + jnz 1b + not %ebx + addl $1, %edx + movl %edx, %eax + outb %al, $0x70 + inb $0x71, %al + movb %al,%ch + addl $1, %edx + movl %edx, %eax + outb %al, $0x70 + inb $0x71, %al + movb %ch,%ah + cmpw %ax,%bx + jz __rtc_ok + /* Set to fall back mode */ + movb $RTC_BOOT_BYTE, %al + outb %al, $0x70 + inb $0x71, %al + andb $0xfc, %al + outb %al, $0x71 + + /* The byte is o.k. see where to go */ +__rtc_ok: + movb $RTC_BOOT_BYTE, %al + outb %al, $0x70 + inb $0x71, %al + + movb %al, %bl + movb %al, %cl + andb $0x0c, %al /* Remove the boot bits from %al */ + shrb $4, %cl /* Isolate the boot count */ + + /* If we are in fallback mode set the reboot count to max */ + testb $1, %bl + jnz __reboot_cnt_read + movb $MAX_REBOOT_CNT, %cl +__reboot_cnt_read: + /* Increment the reboot count and see if we have hit the limit */ + incb %cl + cmpb $MAX_REBOOT_CNT, %cl + jb __reboot_cnt_ok +__reboot_cnt_bad: + xorb %cl, %cl /* clear the boot count in fallback */ + xorb %bl, %bl /* clear the boot bits in fallback */ + jmp __reboot_cnt_set + +__reboot_cnt_ok: + movb $((1<<1)|(1<<0)), %bl /* Set boot_option=1 last_boot=1 */ + /* Save the new boot bits */ +__reboot_cnt_set: + shlb $4, %cl + orb %bl, %al + orb %cl, %al + outb %al, $0x71 + + testb $(1<<1), %al + jnz __normal_image diff --git a/src/southbridge/intel/82801db/ich4_1e0_misc.c b/src/southbridge/intel/82801db/ich4_1e0_misc.c new file mode 100644 index 0000000000..7d4bd53f62 --- /dev/null +++ b/src/southbridge/intel/82801db/ich4_1e0_misc.c @@ -0,0 +1,31 @@ +#include +#include +#include +#include +#include +#include "82801.h" + +void ich3_1e0_misc(void) +{ + struct pci_dev *dev; + + dev = pci_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82801CA_1E0, 0); + if (!dev) { + printk_debug("*** ERROR Southbridge device %x not found\n", + PCI_DEVICE_ID_INTEL_82801CA_1E0); + return; + } +#if 0 + pci_write_config_word(dev, CMD, 0x0147); + pci_write_config_byte(dev, SMLT, 0x40); + pci_write_config_byte(dev, IOBASE, 0x20); + pci_write_config_byte(dev, IOLIM, 0x20); + pci_write_config_word(dev, MEMBASE, 0xd410); + pci_write_config_word(dev, MEMLIM, 0xd5f0); + pci_write_config_byte(dev, CNF, 0x02); + pci_write_config_byte(dev, MTT, 0x40); + pci_write_config_byte(dev, PCI_MAST_STS, 0x86); +#endif + +} diff --git a/src/southbridge/intel/82801db/ich4_1f0_misc.c b/src/southbridge/intel/82801db/ich4_1f0_misc.c new file mode 100644 index 0000000000..0ca71987de --- /dev/null +++ b/src/southbridge/intel/82801db/ich4_1f0_misc.c @@ -0,0 +1,30 @@ +#include +#include +#include +#include +#include +#include "82801.h" + +void ich3_1f0_misc(void) +{ + struct pci_dev *dev; + + dev = pci_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82801CA_1F0, 0); + if (!dev) { + printk_debug("*** ERROR Southbridge device %x not found\n", + PCI_DEVICE_ID_INTEL_82801CA_1F0); + return; + } + pci_write_config_word(dev, PCICMD, 0x014f); + pci_write_config_dword(dev, PMBASE, 0x00001001); + pci_write_config_byte(dev, ACPI_CNTL, 0x10); + pci_write_config_dword(dev, GPIO_BASE, 0x00001181); + pci_write_config_byte(dev, GPIO_CNTL, 0x10); + pci_write_config_dword(dev, PIRQA_ROUT, 0x0A05030B); + pci_write_config_byte(dev, PIRQE_ROUT, 0x07); + pci_write_config_byte(dev, RTC_CONF, 0x04); + pci_write_config_byte(dev, COM_DEC, 0xE0); + pci_write_config_word(dev, LPC_EN, 0x000D); + pci_write_config_word(dev, FUNC_DIS, 0x8060); +} diff --git a/src/southbridge/intel/82801db/ich4_cpu.c b/src/southbridge/intel/82801db/ich4_cpu.c new file mode 100644 index 0000000000..eed76fc148 --- /dev/null +++ b/src/southbridge/intel/82801db/ich4_cpu.c @@ -0,0 +1,37 @@ +#include +#include +#include +#include +#include +#include +#include "82801.h" + +void ich3_set_cpu_multiplier(unsigned multiplier) +{ + u32 dword, old_dword; + struct pci_dev *dev; + unsigned old_multiplier; + dev = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_1F0, 0); + if (!dev) { + printk_err("Cannot find device %08x:%08x\n", + PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_1F0); + return; + } + pci_read_config_dword(dev, GEN_STS, &old_dword); + dword = old_dword; + dword &= ~((1 << 12) - (1 << 8)); +#if 0 + dword |= (multiplier & 0xf) << 8; +#else + dword |= (0x0c) << 8; +#endif + if (dword != old_dword) { + dword |= (1<<1); + pci_write_config_dword(dev, GEN_STS, dword); + printk_info("Rebooting to change the cpu multiplier\n"); + boot_successful(); + hard_reset(); + } + dword |= (1<<1); + pci_write_config_dword(dev, GEN_STS, dword); +} diff --git a/src/southbridge/intel/82801db/ich4_ide.c b/src/southbridge/intel/82801db/ich4_ide.c new file mode 100644 index 0000000000..169ec0083b --- /dev/null +++ b/src/southbridge/intel/82801db/ich4_ide.c @@ -0,0 +1,24 @@ +#include +#include +#include + +void ich3_enable_ide(int enable_a, int enable_b) +{ + struct pci_dev *dev; + dev = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_1F1, 0); + if (dev != NULL) { + /* Enable ide devices so the linux ide driver will work */ + u16 word; + /* enable ide0 */ + pci_read_config_word(dev, 0x40, &word); + word &= ~(1 << 15); + word |= ((!!enable_a) << 15); + pci_write_config_word(dev, 0x40, word); + /* enable ide1 */ + pci_read_config_word(dev, 0x42, &word); + word &= ~(1 << 15); + word |= ((!!enable_b) << 15); + pci_write_config_word(dev, 0x42, word); + } + +} diff --git a/src/southbridge/intel/82801db/ich4_ioapic.c b/src/southbridge/intel/82801db/ich4_ioapic.c new file mode 100644 index 0000000000..546bf7331d --- /dev/null +++ b/src/southbridge/intel/82801db/ich4_ioapic.c @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#include +#include "82801.h" + +void ich3_enable_ioapic(void) +{ + struct pci_dev *dev; + u32 dword; + volatile u32 *ioapic_sba = (volatile u32 *)0xfec00000; + volatile u32 *ioapic_sbd = (volatile u32 *)0xfec00010; + + dev = pci_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82801CA_1F0, 0); + if (!dev) { + printk_debug("*** ERROR Southbridge device %x not found\n", + PCI_DEVICE_ID_INTEL_82801CA_1F0); + return; + } + pci_read_config_dword(dev, GEN_CNTL, &dword); + dword |= (3 << 7); /* enable ioapic */ + dword |= (1 <<13); /* coprocessor error enable */ + dword |= (1 << 1); /* delay transaction enable */ + dword |= (1 << 2); /* DMA collection buf enable */ + pci_write_config_dword(dev, GEN_CNTL, dword); + printk_debug("ioapic southbridge enabled %x\n",dword); + *ioapic_sba=0; + *ioapic_sbd=(2<<24); + *ioapic_sba=3; + *ioapic_sbd=1; + *ioapic_sba=0; + dword=*ioapic_sbd; + printk_debug("Southbridge apic id = %x\n",dword); + if(dword!=(2<<24)) + for(;;); + *ioapic_sba=3; + dword=*ioapic_sbd; + printk_debug("Southbridge apic DT = %x\n",dword); + if(dword!=1) + for(;;); + + +} diff --git a/src/southbridge/intel/82801db/ich4_lpc.c b/src/southbridge/intel/82801db/ich4_lpc.c new file mode 100644 index 0000000000..8548736d26 --- /dev/null +++ b/src/southbridge/intel/82801db/ich4_lpc.c @@ -0,0 +1,33 @@ +#include +#include +#include +#include "82801.h" + +void ich3_enable_serial_irqs(void) +{ + struct pci_dev *dev; + dev = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_1F0, 0); + if (!dev) { + return; + } + pci_write_config_byte(dev, SERIRQ_CNTL, (1 << 7)|(1 << 6)|((21 - 17) << 2)|(0 << 0)); +} + +void ich3_lpc_route_dma(unsigned char mask) +{ + struct pci_dev *dev; + u16 word; + int i; + dev = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_1F0, 0); + if (!dev) { + return; + } + pci_read_config_word(dev, PCI_DMA_CFG, &word); + word &= ((1 << 10) - (1 << 8)); + for(i = 0; i < 8; i++) { + if (i == 4) + continue; + word |= ((mask & (1 << i))? 3:1) << (i*2); + } + pci_write_config_word(dev, PCI_DMA_CFG, word); +} diff --git a/src/southbridge/intel/82801db/ich4_power.c b/src/southbridge/intel/82801db/ich4_power.c new file mode 100644 index 0000000000..0323b0b7b8 --- /dev/null +++ b/src/southbridge/intel/82801db/ich4_power.c @@ -0,0 +1,21 @@ +#include +#include +#include +#include +#include "82801.h" + +void ich3_power_after_power_fail(int on) +{ + struct pci_dev *dev; + dev = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_1F0, 0); + if (!dev) { + return; + } + /* FIXME this doesn't work! */ + /* Which state do we want to goto after g3 (power restored)? + * 0 == S0 Full On + * 1 == S5 Soft Off + */ + pci_write_config_byte(dev, GEN_PMCON_3, on?0:1); + printk_info("set power %s after power fail\n", on?"on":"off"); +} diff --git a/src/southbridge/intel/82801db/ich4_reset.c b/src/southbridge/intel/82801db/ich4_reset.c new file mode 100644 index 0000000000..3d3271982e --- /dev/null +++ b/src/southbridge/intel/82801db/ich4_reset.c @@ -0,0 +1,9 @@ +#include +#include + +void ich3_hard_reset(void) +{ + /* Try rebooting through port 0xcf9 */ + outb((0 <<3)|(1<<2)|(1<<1), 0xcf9); +} + diff --git a/src/southbridge/intel/82801db/ich4_rtc.c b/src/southbridge/intel/82801db/ich4_rtc.c new file mode 100644 index 0000000000..90d61da2b6 --- /dev/null +++ b/src/southbridge/intel/82801db/ich4_rtc.c @@ -0,0 +1,23 @@ +#include +#include +#include +#include +#include +#include "82801.h" + + +void ich3_rtc_init(void) +{ + unsigned char byte; + u32 dword; + int rtc_failed; + pcibios_read_config_byte(RTC_BUS, RTC_DEVFN, GEN_PMCON_3, &byte); + rtc_failed = byte & RTC_FAILED; + if (rtc_failed) { + byte &= ~(1 << 1); /* preserve the power fail state */ + pcibios_write_config_byte(RTC_BUS, RTC_DEVFN, GEN_PMCON_3, byte); + } + pcibios_read_config_dword(RTC_BUS, RTC_DEVFN, GEN_STS, &dword); + rtc_failed |= dword & (1 << 2); + rtc_init(rtc_failed); +} diff --git a/src/southbridge/intel/82801db/ich4_smbus.c b/src/southbridge/intel/82801db/ich4_smbus.c new file mode 100644 index 0000000000..116489b2aa --- /dev/null +++ b/src/southbridge/intel/82801db/ich4_smbus.c @@ -0,0 +1,185 @@ +#include +#include +#include +#include +#include "82801.h" + +void smbus_setup(void) +{ + pcibios_write_config_dword(SMBUS_BUS, SMBUS_DEVFN, 0x20, SMBUS_IO_BASE | 1); + pcibios_write_config_byte(SMBUS_BUS, SMBUS_DEVFN, 0x40, 1); + pcibios_write_config_word(SMBUS_BUS, SMBUS_DEVFN, 0x4, 1); + + /* Disable interrupt generation */ + outb(0, SMBUS_IO_BASE + SMBHSTCTL); +} + +static inline void smbus_delay(void) +{ + outb(0x80, 0x80); +} + +static int smbus_wait_until_ready(void) +{ + unsigned loops = SMBUS_TIMEOUT; + unsigned char byte; + do { + smbus_delay(); + if (--loops == 0) + break; + byte = inb(SMBUS_IO_BASE + SMBHSTSTAT); + } while(byte & 1); + return loops?0:-1; +} + +static int smbus_wait_until_done(void) +{ + unsigned loops = SMBUS_TIMEOUT; + unsigned char byte; + do { + smbus_delay(); + if (--loops == 0) + break; + byte = inb(SMBUS_IO_BASE + SMBHSTSTAT); + } while((byte & 1) || (byte & ~((1<<6)|(1<<0))) == 0); + return loops?0:-1; +} + +static int smbus_wait_until_next(void) +{ + unsigned loops = SMBUS_TIMEOUT; + unsigned char byte; + do { + smbus_delay(); + if (--loops == 0) + break; + byte = inb(SMBUS_IO_BASE + SMBHSTSTAT); + } while((byte & ~((1<<6)|(1<<1)|(1<<0))) == 0); + return loops?0:-1; +} + +#if 0 +static void smbus_print_error(unsigned char host_status_register) +{ + + printk_debug("smbus_error: 0x%02x\n", host_status_register); + if (host_status_register & (1 << 7)) { + printk_debug("Byte Done Status\n"); + } + if (host_status_register & (1 << 6)) { + printk_debug("In Use Status\n"); + } + if (host_status_register & (1 << 5)) { + printk_debug("SMBus Alert Status\n"); + } + if (host_status_register & (1 << 4)) { + printk_debug("Interrup/SMI# was Failed Bus Transaction\n"); + } + if (host_status_register & (1 << 3)) { + printk_debug("Bus Error\n"); + } + if (host_status_register & (1 << 2)) { + printk_debug("Device Error\n"); + } + if (host_status_register & (1 << 1)) { + printk_debug("Interrupt/SMI# was Successful Completion\n"); + } + if (host_status_register & (1 << 0)) { + printk_debug("Host Busy\n"); + } +} +#endif + +int smbus_read_byte(unsigned device, unsigned address, unsigned char *result) +{ + unsigned char host_status_register; + unsigned char byte; + + if (smbus_wait_until_ready() < 0) + return -1; + + /* setup transaction */ + /* disable interrupts */ + outb(inb(SMBUS_IO_BASE + SMBHSTCTL) & (~1), SMBUS_IO_BASE + SMBHSTCTL); + /* set the device I'm talking too */ + outb(((device & 0x7f) << 1) | 1, SMBUS_IO_BASE + SMBXMITADD); + /* set the command/address... */ + outb(address & 0xFF, SMBUS_IO_BASE + SMBHSTCMD); + /* set up for a byte data read */ + outb((inb(SMBUS_IO_BASE + SMBHSTCTL) & 0xE3) | (0x2 << 2), SMBUS_IO_BASE + SMBHSTCTL); + + /* clear any lingering errors, so the transaction will run */ + outb(inb(SMBUS_IO_BASE + SMBHSTSTAT), SMBUS_IO_BASE + SMBHSTSTAT); + + /* clear the data byte...*/ + outb(0, SMBUS_IO_BASE + SMBHSTDAT0); + + /* start the command */ + outb((inb(SMBUS_IO_BASE + SMBHSTCTL) | 0x40), SMBUS_IO_BASE + SMBHSTCTL); + + /* poll for transaction completion */ + if (smbus_wait_until_done() < 0) + return -1; + + host_status_register = inb(SMBUS_IO_BASE + SMBHSTSTAT); + + /* Ignore the In Use Status... */ + host_status_register &= ~(1 << 6); + + /* read results of transaction */ + byte = inb(SMBUS_IO_BASE + SMBHSTDAT0); + + *result = byte; + return host_status_register != 0x02; +} + +#if 0 +int smbus_read_block(unsigned device, unsigned address, unsigned bytes, unsigned char *results) +{ + unsigned char host_status_register; + unsigned char byte; + int count; + + if (smbus_wait_until_ready() < 0) + return 0; + + /* setup transaction */ + /* disable interrupts */ + outb(inb(SMBUS_IO_BASE + SMBHSTCTL) & (~1), SMBUS_IO_BASE + SMBHSTCTL); + /* set the device I'm talking too */ + outb(((device & 0x7f) << 1) | 1, SMBUS_IO_BASE + SMBXMITADD); + /* set the command/address... */ + outb(address & 0xFF, SMBUS_IO_BASE + SMBHSTCMD); + /* set up for a block data read/write */ + outb((inb(SMBUS_IO_BASE + SMBHSTCTL) & 0xE3) | (0x5 << 2), SMBUS_IO_BASE + SMBHSTCTL); + + /* set the block count */ + outb(bytes & 0xff, SMBUS_IO_BASE + SMBHSTDAT0); + + /* clear any lingering errors, so the transaction will run */ + outb(inb(SMBUS_IO_BASE + SMBHSTSTAT), SMBUS_IO_BASE + SMBHSTSTAT); + + /* start the command */ + outb((inb(SMBUS_IO_BASE + SMBHSTCTL) | 0x40), SMBUS_IO_BASE + SMBHSTCTL); + + for(count = 0; count < bytes; count++) { + /* Wait until the controller has more data ready */ + if (smbus_wait_until_next() < 0) { + break; + } + host_status_register = inb(SMBUS_IO_BASE + SMBHSTSTAT); + + /* Test the status to see if an error occured */ + if (host_status_register & (1 << 7)) + break; + + /* read the next byte */ + byte = inb(SMBUS_IO_BASE + SMBBLKDAT); + results[count] = byte; + + /* finish this byte read */ + outb((1<<7), SMBUS_IO_BASE + SMBHSTSTAT); + } + return count; +} +#endif diff --git a/src/southbridge/intel/82801db/nvram.c b/src/southbridge/intel/82801db/nvram.c new file mode 100644 index 0000000000..318f20e723 --- /dev/null +++ b/src/southbridge/intel/82801db/nvram.c @@ -0,0 +1,7 @@ +#include +#include + +void nvram_on(void) +{ + return; +} diff --git a/src/southbridge/intel/82801db/smbus.inc b/src/southbridge/intel/82801db/smbus.inc new file mode 100644 index 0000000000..40c32ec0a6 --- /dev/null +++ b/src/southbridge/intel/82801db/smbus.inc @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2002 Eric Biederman + */ + +#include "82801.h" + +/* jump around these subrs */ +jmp smbus_end + + /* + * Routine: smbus_setup + * Arguments: none + * Results: none + * Trashed: eax, edx, ecx + * Effects: The smbus is enabled + */ + +smbus_setup: + /* set smbus iobase */ + movl $((SMBUS_DEVFN << 8) + 0x20), %eax + movl $(SMBUS_IO_BASE | 1), %ecx + PCI_WRITE_CONFIG_DWORD + + /* Set smbus enable */ + movl $((SMBUS_DEVFN << 8) + 0x40), %eax + movl $1, %edx + PCI_WRITE_CONFIG_BYTE + + /* Set smbus iospace enable */ + movl $((SMBUS_DEVFN << 8) + 0x4), %eax + movl $1, %ecx + PCI_WRITE_CONFIG_WORD + + /* Disable interrupt generation */ + movl $(SMBUS_IO_BASE + SMBHSTCTL), %edx + xorl %eax, %eax + outb %al, %dx + + RET_LABEL(smbus_setup) + + + /* + * Routine: smbus_wait_until_ready + * Arguments: none + * Results: Carry set on timeout + * Trashed: eax, edx + * Effects: Upon return the smbus is ready to accept commands + */ +#define SMBUS_WAIT_UNTIL_READY() \ + movl $(SMBUS_TIMEOUT << 8), %eax ; \ + movl $(SMBUS_IO_BASE + SMBHSTSTAT), %edx ; \ +1: outb %al, $0x80 ; \ + inb %dx, %al ; \ + subl $0x00000100, %eax ; \ + testl $0xffffff00, %eax ; \ + stc ; \ + jz 2f ; \ + testb $(1<<0), %al ; \ + jnz 1b ; \ + clc ; \ +2: + + /* + * Routine: smbus_wait_until_done + * Arguments: none + * Results: carry set on timeout + * Trashed: eax, edx + * Effects: Upon return the smbus has completed it's most recent transation + */ +#define SMBUS_WAIT_UNTIL_DONE() \ + movl $(SMBUS_TIMEOUT << 8), %eax ; \ + movl $(SMBUS_IO_BASE + SMBHSTSTAT), %edx ; \ +1: outb %al, $0x80 ; \ + inb %dx, %al ; \ + subl $0x00000100, %eax ; \ + testl $0xffffff00, %eax ; \ + stc ; \ + jz 2f ; \ + testb $1, %al ; \ + jnz 1b ; \ + testb $~((1<<6)|(1<<0)), %al ; \ + jz 1b ; \ + clc ; \ +2: + + /* + * Routine: smbus_wait_until_next + * Arguments: none + * Results: al smbus status + * edx status register addr + * Trashed: eax, edx + * Effects: Upon return the smbus is ready for the next byte + */ +#define SMBUS_WAIT_UNTIL_NEXT() \ + movl $(SMBUS_TIMEOUT << 8), %eax ; \ + movl $(SMBUS_IO_BASE + SMBHSTSTAT), %edx ; \ +1: outb %al, $0x80 ; \ + inb %dx, %al ; \ + subl $0x00000100, %eax ; \ + testl $0xffffff00, %eax ; \ + stc ; \ + jz 2f ; \ + testb $~((1<<6)|(1<<1)|(1<<0)), %al ; \ + jz 1b ; \ + clc ; \ +2: + + /* + * Routine: smbus_kill_command + * Arguments: none + * Results: none + * Trashed: eax, edx, flags + * Effects: Upon return the smbus is ready to accept commands + */ + +#define SMBUS_KILL_COMMAND() \ + movl $(SMBUS_IO_BASE + SMBHSTCTL), %edx ; \ + inb %dx, %al ; \ + orb $(1<<1), %al ; \ + outb %al, %dx ; \ + SMBUS_WAIT_UNTIL_DONE() ; \ + movl $(SMBUS_IO_BASE + SMBHSTCTL), %edx ; \ + inb %dx, %al ; \ + andb $~(1<<1), %al ; \ + outb %al, %dx + + +smbus_end: +CALL_LABEL(smbus_setup) diff --git a/src/southbridge/intel/82801db/smbus_noop_read_block.inc b/src/southbridge/intel/82801db/smbus_noop_read_block.inc new file mode 100644 index 0000000000..c8e746b201 --- /dev/null +++ b/src/southbridge/intel/82801db/smbus_noop_read_block.inc @@ -0,0 +1,83 @@ +.section ".rom.data" + +/* smbus read a block of data and discard it + input: bl device, bh command, cl count, + output: cf set on error +*/ +smbus_noop_read_block: + /* poll until the smbus is ready for commands */ + SMBUS_WAIT_UNTIL_READY() + jc smbus_block_noop_read_error + + /* setup transaction */ + /* disable interrupts */ + movl $(SMBUS_IO_BASE + SMBHSTCTL), %edx + inb %dx, %al + andb $0xFE, %al + outb %al, %dx + + /* set the device I'm talking to and specify a read */ + movl $(SMBUS_IO_BASE + SMBXMITADD), %edx + movb %bl /* device */, %al + shlb $1, %al + orb $1, %al + outb %al, %dx + + /* set the command address... */ + movl $(SMBUS_IO_BASE + SMBHSTCMD), %edx + movb %bh /* address */, %al + outb %al, %dx + + /* setup for a block data read/write */ + movl $(SMBUS_IO_BASE + SMBHSTCTL), %edx + inb %dx, %al + andb $0xE3, %al + orb $(0x5 << 2), %al + outb %al, %dx + + /* set the block count */ + movl $(SMBUS_IO_BASE + SMBHSTDAT0), %edx + movb %cl /* count */, %al + outb %al, %dx + + /* clear any lingering errors, so the transaction will run */ + movl $(SMBUS_IO_BASE + SMBHSTSTAT), %edx + inb %dx, %al + outb %al, %dx + + /* start a block read, with interrupts disabled */ + movl $(SMBUS_IO_BASE + SMBHSTCTL), %edx + inb %dx, %al + orb $(1 << 6), %al + outb %al, %dx + +smbus_block_noop_read_next: + /* wait until the controller has more data ready */ + SMBUS_WAIT_UNTIL_NEXT() + jc smbus_block_noop_read_error + + /* Test the results to see if we succeeded */ + testb $(1<<7), %al + jz smbus_block_noop_read_error + + /* read the next byte */ + movl $(SMBUS_IO_BASE + SMBBLKDAT), %edx + inb %dx, %al + + /* finish this byte read */ + movl $(SMBUS_IO_BASE + SMBHSTSTAT), %edx + movb $(1<<7), %al + outb %al, %dx + decb %cl + jnz smbus_block_noop_read_next + + /* No error I am done */ + clc +smbus_block_noop_read_end: + RETSP +smbus_block_noop_read_error: + SMBUS_KILL_COMMAND() + stc + jmp smbus_block_noop_read_end + +.previous diff --git a/src/southbridge/intel/82801db/smbus_print_block.inc b/src/southbridge/intel/82801db/smbus_print_block.inc new file mode 100644 index 0000000000..a249dd3e3a --- /dev/null +++ b/src/southbridge/intel/82801db/smbus_print_block.inc @@ -0,0 +1,86 @@ +.section ".rom.data" + +/* smbus print a block of data + input: bl device, bh command, cl count, + output: cf set on error + trashed: eax,edx +*/ +smbus_print_block: + /* poll until the smbus is ready for commands */ + SMBUS_WAIT_UNTIL_READY() + jc smbus_block_print_error + + /* setup transaction */ + /* disable interrupts */ + movl $(SMBUS_IO_BASE + SMBHSTCTL), %edx + inb %dx, %al + andb $0xFE, %al + outb %al, %dx + + /* set the device I'm talking to and specify a read */ + movl $(SMBUS_IO_BASE + SMBXMITADD), %edx + movb %bl /* device */, %al + shlb $1, %al + orb $1, %al + outb %al, %dx + + /* set the command address... */ + movl $(SMBUS_IO_BASE + SMBHSTCMD), %edx + movb %bh /* address */, %al + outb %al, %dx + + /* setup for a block data read/write */ + movl $(SMBUS_IO_BASE + SMBHSTCTL), %edx + inb %dx, %al + andb $0xE3, %al + orb $(0x5 << 2), %al + outb %al, %dx + + /* set the block count */ + movl $(SMBUS_IO_BASE + SMBHSTDAT0), %edx + movb %cl /* count */, %al + outb %al, %dx + + /* clear any lingering errors, so the transaction will run */ + movl $(SMBUS_IO_BASE + SMBHSTSTAT), %edx + inb %dx, %al + outb %al, %dx + + /* start a block read, with interrupts disabled */ + movl $(SMBUS_IO_BASE + SMBHSTCTL), %edx + inb %dx, %al + orb $(1 << 6), %al + outb %al, %dx + +smbus_block_print_next: + /* wait until the controller has more data ready */ + SMBUS_WAIT_UNTIL_NEXT() + jc smbus_block_print_error + + /* Test the results to see if we succeeded */ + testb $(1<<7), %al + jz smbus_block_print_error + + /* read the next byte */ + movl $(SMBUS_IO_BASE + SMBBLKDAT), %edx + inb %dx, %al + CONSOLE_DEBUG_INLINE_TX_HEX8(%al) + CONSOLE_DEBUG_INLINE_TX_CHAR($',') + + /* finish this byte read */ + movl $(SMBUS_IO_BASE + SMBHSTSTAT), %edx + movb $(1<<7), %al + outb %al, %dx + decb %cl + jnz smbus_block_print_next + + /* No error I am done */ + clc +smbus_block_print_end: + RETSP +smbus_block_print_error: + SMBUS_KILL_COMMAND() + stc + jmp smbus_block_print_end + +.previous diff --git a/src/southbridge/intel/82801db/smbus_read_block.inc b/src/southbridge/intel/82801db/smbus_read_block.inc new file mode 100644 index 0000000000..c86e105ec6 --- /dev/null +++ b/src/southbridge/intel/82801db/smbus_read_block.inc @@ -0,0 +1,85 @@ +.section ".rom.data" + +/* smbus read a block of data + input: bl device, bh command, cl count, + esi pointer to data block + output: cf set on error +*/ +smbus_read_block: + /* poll until the smbus is ready for commands */ + SMBUS_WAIT_UNTIL_READY() + jc smbus_block_read_error + + /* setup transaction */ + /* disable interrupts */ + movl $(SMBUS_IO_BASE + SMBHSTCTL), %edx + inb %dx, %al + andb $0xFE, %al + outb %al, %dx + + /* set the device I'm talking to and specify a read */ + movl $(SMBUS_IO_BASE + SMBXMITADD), %edx + movb %bl /* device */, %al + shlb $1, %al + orb $1, %al + outb %al, %dx + + /* set the command address... */ + movl $(SMBUS_IO_BASE + SMBHSTCMD), %edx + movb %bh /* address */, %al + outb %al, %dx + + /* setup for a block data read/write */ + movl $(SMBUS_IO_BASE + SMBHSTCTL), %edx + inb %dx, %al + andb $0xE3, %al + orb $(0x5 << 2), %al + outb %al, %dx + + /* set the block count */ + movl $(SMBUS_IO_BASE + SMBHSTDAT0), %edx + movb %cl /* count */, %al + outb %al, %dx + + /* clear any lingering errors, so the transaction will run */ + movl $(SMBUS_IO_BASE + SMBHSTSTAT), %edx + inb %dx, %al + outb %al, %dx + + /* start a block read, with interrupts disabled */ + movl $(SMBUS_IO_BASE + SMBHSTCTL), %edx + inb %dx, %al + orb $(1 << 6), %al + outb %al, %dx + +smbus_block_read_next: + /* wait until the controller has more data ready */ + SMBUS_WAIT_UNTIL_NEXT() + jc smbus_block_read_error + + /* Test the results to see if we succeeded */ + testb $(1<<7), %al + jz smbus_block_read_error + + /* read the next byte */ + movl $(SMBUS_IO_BASE + SMBBLKDAT), %edx + inb %dx, %al + stosb %al, (%esi) + + /* finish this byte read */ + movl $(SMBUS_IO_BASE + SMBHSTSTAT), %edx + movb $(1<<7), %al + outb %al, %dx + decb %cl + jnz smbus_block_read_next + + /* No error I am done */ + clc +smbus_block_read_end: + RETSP +smbus_block_read_error: + SMBUS_KILL_COMMAND() + stc + jmp smbus_block_noop_read_end + +.previous diff --git a/src/southbridge/intel/82801db/smbus_read_byte.inc b/src/southbridge/intel/82801db/smbus_read_byte.inc new file mode 100644 index 0000000000..300778ab83 --- /dev/null +++ b/src/southbridge/intel/82801db/smbus_read_byte.inc @@ -0,0 +1,91 @@ +.section ".rom.data" + + /* + * Routine: smbus_read_byte + * Arguments: %esp return address + * %bl device on the smbus to read from + * %bh address on the smbus to read + * + * Results: zf clear + * byte read %eax + * On Error: + * zf set + * %eax trashed + * + * Trashed: %edx, %eax + * Effects: reads a byte off of the smbus + */ + +#define SMBUS_READ_BYTE(device, address) \ + movl $( (device) | ((address) << 8)), %ebx ; \ + CALLSP(smbus_read_byte) + +smbus_read_byte: + /* poll until the smbus is ready for commands */ + SMBUS_WAIT_UNTIL_READY() + jc smbus_read_byte_failed + + /* setup transaction */ + /* disable interrupts */ + movl $(SMBUS_IO_BASE + SMBHSTCTL), %edx + inb %dx, %al + andb $0xFE, %al + outb %al, %dx + + /* set the device I'm talking to */ + movl $(SMBUS_IO_BASE + SMBXMITADD), %edx + movb %bl /* device */, %al + shlb $1, %al + orb $1, %al + outb %al, %dx + + /* set the command address... */ + movl $(SMBUS_IO_BASE + SMBHSTCMD), %edx + movb %bh /* address */, %al + outb %al, %dx + + /* setup for a byte data read */ + movl $(SMBUS_IO_BASE + SMBHSTCTL), %edx + inb %dx, %al + andb $0xE3, %al + orb $(0x2 << 2), %al + outb %al, %dx + + /* clear any lingering errors, so the transaction will run */ + movl $(SMBUS_IO_BASE + SMBHSTSTAT), %edx + inb %dx, %al + outb %al, %dx + + /* clear the data byte... */ + movl $(SMBUS_IO_BASE + SMBHSTDAT0), %edx + xorl %eax, %eax + outb %al, %dx + + /* start a byte read, with interrupts disabled */ + movl $(SMBUS_IO_BASE + SMBHSTCTL), %edx + inb %dx, %al + orb $0x40, %al + outb %al, %dx + + /* poll for transaction completion */ + SMBUS_WAIT_UNTIL_DONE() + jc smbus_read_byte_failed + + /* read the results and see if we succeded */ + movl $(SMBUS_IO_BASE + SMBHSTSTAT), %edx + inb %dx, %al + andb $~(1 << 6), %al /* Ignore the In Use Status... */ + cmpb $0x02, %al + jne smbus_read_byte_failed + + movl $(SMBUS_IO_BASE + SMBHSTDAT0), %edx + testl %edx, %edx /* clear zf */ + inb %dx, %al +smbus_read_byte_done: + RETSP +smbus_read_byte_failed: + SMBUS_KILL_COMMAND() + xorl %eax, %eax /* set zf */ + jmp smbus_read_byte_done + +.previous diff --git a/src/southbridge/intel/82801db/smbus_write_block.inc b/src/southbridge/intel/82801db/smbus_write_block.inc new file mode 100644 index 0000000000..5541c8f6ff --- /dev/null +++ b/src/southbridge/intel/82801db/smbus_write_block.inc @@ -0,0 +1,90 @@ +.section ".rom.data" + +/* smbus write a block of data + input: bl device, bh command, cl count, + esi pointer to data block + output: cf set on error +*/ +smbus_write_block: + /* poll until the smbus is ready for commands */ + SMBUS_WAIT_UNTIL_READY() + jc smbus_block_write_end + + /* setup transaction */ + /* disable interrupts */ + movl $(SMBUS_IO_BASE + SMBHSTCTL), %edx + inb %dx, %al + andb $0xFE, %al + outb %al, %dx + + /* set the device I'm talking to, and specify a write */ + movl $(SMBUS_IO_BASE + SMBXMITADD), %edx + movb %bl /* device */, %al + shlb $1, %al + orb $0, %al + outb %al, %dx + + /* set the command address... */ + movl $(SMBUS_IO_BASE + SMBHSTCMD), %edx + movb %bh /* address */, %al + outb %al, %dx + + /* setup for block read/write */ + movl $(SMBUS_IO_BASE + SMBHSTCTL), %edx + inb %dx, %al + andb $0xE3, %al + orb $(0x5 << 2), %al + outb %al, %dx + + /* set the block count */ + movl $(SMBUS_IO_BASE + SMBHSTDAT0), %edx + movb %cl /* count */, %al + outb %al, %dx + + /* clear any lingering errors, so the transaction will run */ + movl $(SMBUS_IO_BASE + SMBHSTSTAT), %edx + inb %dx, %al + outb %al, %dx + + /* load the data byte in the block data reg */ + cld + movl $(SMBUS_IO_BASE + SMBBLKDAT), %edx + lodsb (%esi), %al + outb %al, %dx + decb %cl + + /* start a block write, with interrupts disabled */ + movl $(SMBUS_IO_BASE + SMBHSTCTL), %edx + inb %dx, %al + orb $0x40, %al + outb %al, %dx + + +smbus_block_write_next: + /* wait until the controller is ready for more */ + SMBUS_WAIT_UNTIL_NEXT() + jc smbus_block_write_end + + /* Test the results to see if we succeeded */ + testb $(1<<7), %al + stc + jz smbus_block_write_end + + /* load the next byte */ + movl $(SMBUS_IO_BASE + SMBBLKDAT), %edx + lodsb (%esi), %al + outb %al, %dx + + /* start the next byte write */ + movl $(SMBUS_IO_BASE + SMBHSTSTAT), %edx + movb $(1<<7), %al + outb %al, %dx + decb %cl + jnz smbus_block_write_next + + /* No error I am done */ + clc +smbus_block_write_end: + RETSP + +.previous diff --git a/src/southbridge/intel/82801db/watchdog_disable.inc b/src/southbridge/intel/82801db/watchdog_disable.inc new file mode 100644 index 0000000000..c95895824b --- /dev/null +++ b/src/southbridge/intel/82801db/watchdog_disable.inc @@ -0,0 +1,9 @@ +/* Disable the TCO watch dog timer in the southbridge */ +/* bridge 0, device 1f, function 0, byte d4, bit 1 */ + + movl $0x8000f8d4,%eax + movl $0x0cf8,%edx + outl %eax,%dx + movl $0x0cfc,%edx + movb $2,%al + outb %al,%dx