new stupid southbridge.

This commit is contained in:
Ronald G. Minnich 2003-08-05 21:50:35 +00:00
commit fcd39ac7ad
21 changed files with 1235 additions and 0 deletions

View file

@ -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)

View file

@ -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

View file

@ -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 <pc80/mc146818rtc.h>
#include <part/fallback_boot.h>
#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

View file

@ -0,0 +1,31 @@
#include <pci.h>
#include <arch/io.h>
#include <printk.h>
#include <pci_ids.h>
#include <southbridge/intel/82801.h>
#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
}

View file

@ -0,0 +1,30 @@
#include <pci.h>
#include <arch/io.h>
#include <printk.h>
#include <pci_ids.h>
#include <southbridge/intel/82801.h>
#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);
}

View file

@ -0,0 +1,37 @@
#include <pci.h>
#include <pci_ids.h>
#include <southbridge/intel/82801.h>
#include <printk.h>
#include <part/hard_reset.h>
#include <part/fallback_boot.h>
#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);
}

View file

@ -0,0 +1,24 @@
#include <pci.h>
#include <pci_ids.h>
#include <southbridge/intel/82801.h>
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);
}
}

View file

@ -0,0 +1,45 @@
#include <pci.h>
#include <arch/io.h>
#include <printk.h>
#include <pci_ids.h>
#include <southbridge/intel/82801.h>
#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(;;);
}

View file

@ -0,0 +1,33 @@
#include <pci.h>
#include <pci_ids.h>
#include <southbridge/intel/82801.h>
#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);
}

View file

@ -0,0 +1,21 @@
#include <southbridge/intel/82801.h>
#include <pci.h>
#include <pci_ids.h>
#include <printk.h>
#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");
}

View file

@ -0,0 +1,9 @@
#include <arch/io.h>
#include <southbridge/intel/82801.h>
void ich3_hard_reset(void)
{
/* Try rebooting through port 0xcf9 */
outb((0 <<3)|(1<<2)|(1<<1), 0xcf9);
}

View file

@ -0,0 +1,23 @@
#include <southbridge/intel/82801.h>
#include <pc80/mc146818rtc.h>
#include <pci.h>
#include <pci_ids.h>
#include <printk.h>
#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);
}

View file

@ -0,0 +1,185 @@
#include <smbus.h>
#include <arch/io.h>
#include <pci.h>
#include <southbridge/intel/82801.h>
#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

View file

@ -0,0 +1,7 @@
#include <part/nvram.h>
#include <printk.h>
void nvram_on(void)
{
return;
}

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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