summaryrefslogtreecommitdiff
path: root/src/southbridge/intel/i82801ca
diff options
context:
space:
mode:
authorSteven J. Magnani <steve@digidescorp.com>2005-09-14 15:34:03 +0000
committerSteven J. Magnani <steve@digidescorp.com>2005-09-14 15:34:03 +0000
commit706aed8eb9c1836d1b6c53b081f789a1d3afaa25 (patch)
tree953355608f5491e7e046a30e0cba007e27522bf9 /src/southbridge/intel/i82801ca
parent09e4ef670245566f1ee50759976babac17aae55d (diff)
Initial revision.
Based on i82801er and LB v1 code. git-svn-id: svn://svn.coreboot.org/coreboot/trunk@2036 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
Diffstat (limited to 'src/southbridge/intel/i82801ca')
-rw-r--r--src/southbridge/intel/i82801ca/Config.lb17
-rw-r--r--src/southbridge/intel/i82801ca/chip.h9
-rw-r--r--src/southbridge/intel/i82801ca/cmos_failover.c19
-rw-r--r--src/southbridge/intel/i82801ca/i82801ca.c53
-rw-r--r--src/southbridge/intel/i82801ca/i82801ca.h78
-rw-r--r--src/southbridge/intel/i82801ca/i82801ca_ac97.c41
-rw-r--r--src/southbridge/intel/i82801ca/i82801ca_early_smbus.c138
-rw-r--r--src/southbridge/intel/i82801ca/i82801ca_ide.c48
-rw-r--r--src/southbridge/intel/i82801ca/i82801ca_lpc.c240
-rw-r--r--src/southbridge/intel/i82801ca/i82801ca_nic.c21
-rw-r--r--src/southbridge/intel/i82801ca/i82801ca_pci.c30
-rw-r--r--src/southbridge/intel/i82801ca/i82801ca_reset.c8
-rw-r--r--src/southbridge/intel/i82801ca/i82801ca_smbus.c83
-rw-r--r--src/southbridge/intel/i82801ca/i82801ca_usb.c49
14 files changed, 834 insertions, 0 deletions
diff --git a/src/southbridge/intel/i82801ca/Config.lb b/src/southbridge/intel/i82801ca/Config.lb
new file mode 100644
index 0000000000..60ae38cb35
--- /dev/null
+++ b/src/southbridge/intel/i82801ca/Config.lb
@@ -0,0 +1,17 @@
+uses CONFIG_IDE
+
+config chip.h
+driver i82801ca.o
+
+driver i82801ca_usb.o
+
+driver i82801ca_lpc.o
+
+if CONFIG_IDE
+ driver i82801ca_ide.o
+end
+
+driver i82801ca_ac97.o
+#driver i82801ca_nic.o
+driver i82801ca_pci.o
+object i82801ca_reset.o
diff --git a/src/southbridge/intel/i82801ca/chip.h b/src/southbridge/intel/i82801ca/chip.h
new file mode 100644
index 0000000000..f9583ca1fd
--- /dev/null
+++ b/src/southbridge/intel/i82801ca/chip.h
@@ -0,0 +1,9 @@
+#ifndef I82801CA_CHIP_H
+#define I82801CA_CHIP_H
+
+struct southbridge_intel_i82801ca_config
+{
+};
+extern struct chip_operations southbridge_intel_i82801ca_ops;
+
+#endif /* I82801CA_CHIP_H */
diff --git a/src/southbridge/intel/i82801ca/cmos_failover.c b/src/southbridge/intel/i82801ca/cmos_failover.c
new file mode 100644
index 0000000000..6197ef677a
--- /dev/null
+++ b/src/southbridge/intel/i82801ca/cmos_failover.c
@@ -0,0 +1,19 @@
+//kind of cmos_err for ich3
+
+#include "i82801ca.h"
+
+static void check_cmos_failed(void)
+{
+#if HAVE_OPTION_TABLE
+ uint8_t byte = pci_read_config8(PCI_DEV(0,0x1f,0),GEN_PMCON_3);
+
+ if( byte & RTC_BATTERY_DEAD) {
+ // Set boot_option and last_boot to 'Fallback',
+ // clear reboot_bits
+ byte = cmos_read(RTC_BOOT_BYTE);
+ byte &= 0x0c;
+ byte |= MAX_REBOOT_CNT << 4;
+ cmos_write(byte, RTC_BOOT_BYTE);
+ }
+#endif
+}
diff --git a/src/southbridge/intel/i82801ca/i82801ca.c b/src/southbridge/intel/i82801ca/i82801ca.c
new file mode 100644
index 0000000000..27432d95fb
--- /dev/null
+++ b/src/southbridge/intel/i82801ca/i82801ca.c
@@ -0,0 +1,53 @@
+#include <console/console.h>
+#include <device/device.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
+#include <assert.h>
+#include "i82801ca.h"
+
+void i82801ca_enable(device_t dev)
+{
+ unsigned int index = 0;
+ uint8_t bHasDisableBit = 0;
+ uint16_t cur_disable_mask, new_disable_mask;
+
+// all 82801ca devices are in bus 0
+ unsigned int devfn = PCI_DEVFN(0x1f, 0); // lpc
+ device_t lpc_dev = dev_find_slot(0, devfn); // 0
+ if (!lpc_dev)
+ return;
+
+ // Calculate disable bit position for specified device:function
+ // NOTE: For ICH-3, only the following devices can be disabled:
+ // D31:F1, D31:F3, D31:F5, D31:F6,
+ // D29:F0, D29:F1, D29:F2
+
+ if (PCI_SLOT(dev->path.u.pci.devfn) == 31) {
+ index = PCI_FUNC(dev->path.u.pci.devfn);
+
+ if ((index == 1) || (index == 3) || (index == 5) || (index == 6))
+ bHasDisableBit = 1;
+
+ } else if (PCI_SLOT(dev->path.u.pci.devfn) == 29) {
+ index = 8 + PCI_FUNC(dev->path.u.pci.devfn);
+
+ if (PCI_FUNC(dev->path.u.pci.devfn) < 3)
+ bHasDisableBit = 1;
+ }
+
+ if (bHasDisableBit) {
+ cur_disable_mask = pci_read_config16(lpc_dev, FUNC_DIS);
+ new_disable_mask = cur_disable_mask & ~(1<<index); // enable it
+ if (!dev->enabled) {
+ new_disable_mask |= (1<<index); // disable it
+ }
+ if (new_disable_mask != cur_disable_mask) {
+ pci_write_config16(lpc_dev, FUNC_DIS, new_disable_mask);
+ }
+ }
+}
+
+struct chip_operations southbridge_intel_i82801ca_ops = {
+ CHIP_NAME("Intel 82801ca Southbridge")
+ .enable_dev = i82801ca_enable,
+};
diff --git a/src/southbridge/intel/i82801ca/i82801ca.h b/src/southbridge/intel/i82801ca/i82801ca.h
new file mode 100644
index 0000000000..a5117f23b6
--- /dev/null
+++ b/src/southbridge/intel/i82801ca/i82801ca.h
@@ -0,0 +1,78 @@
+#ifndef I82801CA_H
+#define I82801CA_H
+
+#ifndef __ROMCC__
+#include "chip.h"
+extern void i82801ca_enable(device_t dev);
+#endif
+
+
+#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 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
+
+// GEN_PMCON_3 bits
+#define RTC_BATTERY_DEAD (1<<2)
+#define RTC_POWER_FAILED (1<<1)
+#define SLEEP_AFTER_POWER_FAIL (1<<0)
+
+/********************************************************************/
+/* IDE Controller */
+/********************************************************************/
+
+// PCI Configuration Space (D31:F1)
+#define IDE_TIM_PRI 0x40 // IDE timings, primary
+#define IDE_TIM_SEC 0x42 // IDE timings, secondary
+
+
+// IDE_TIM bits
+#define IDE_DECODE_ENABLE (1<<15)
+
+/********************************************************************/
+/* SMBus */
+/********************************************************************/
+
+// PCI Configuration Space (D31:F3)
+#define SMB_BASE 0x20
+#define HOSTC 0x40
+
+// HOSTC bits
+#define I2C_EN (1<<2)
+#define SMB_SMI_EN (1<<1)
+#define HST_EN (1<<0)
+
+#define SMBUS_IO_BASE 0x1000
+
+// I/O registers (relative to SMBUS_IO_BASE)
+#define SMBHSTSTAT 0
+#define SMBHSTCTL 2
+#define SMBHSTCMD 3
+#define SMBXMITADD 4
+#define SMBHSTDAT0 5
+#define SMBHSTDAT1 6
+#define SMBBLKDAT 7
+#define SMBTRNSADD 9
+#define SMBSLVDATA 10
+#define SMLINK_PIN_CTL 14
+#define SMBUS_PIN_CTL 15
+
+/* 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)
+
+#endif /* I82801CA_H */
diff --git a/src/southbridge/intel/i82801ca/i82801ca_ac97.c b/src/southbridge/intel/i82801ca/i82801ca_ac97.c
new file mode 100644
index 0000000000..0bef1f4f68
--- /dev/null
+++ b/src/southbridge/intel/i82801ca/i82801ca_ac97.c
@@ -0,0 +1,41 @@
+/*
+ * (C) 2003 Linux Networx
+ */
+#include <console/console.h>
+#include <device/device.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
+#include <device/pci_ops.h>
+#include "i82801ca.h"
+
+
+static struct device_operations ac97audio_ops = {
+ .read_resources = pci_dev_read_resources,
+ .set_resources = pci_dev_set_resources,
+ .enable_resources = pci_dev_enable_resources,
+ .enable = i82801ca_enable,
+ .init = 0,
+ .scan_bus = 0,
+};
+
+static struct pci_driver ac97audio_driver __pci_driver = {
+ .ops = &ac97audio_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = PCI_DEVICE_ID_INTEL_82801CA_AC97_AUDIO,
+};
+
+
+static struct device_operations ac97modem_ops = {
+ .read_resources = pci_dev_read_resources,
+ .set_resources = pci_dev_set_resources,
+ .enable_resources = pci_dev_enable_resources,
+ .enable = i82801ca_enable,
+ .init = 0,
+ .scan_bus = 0,
+};
+
+static struct pci_driver ac97modem_driver __pci_driver = {
+ .ops = &ac97modem_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = PCI_DEVICE_ID_INTEL_82801CA_AC97_MODEM,
+};
diff --git a/src/southbridge/intel/i82801ca/i82801ca_early_smbus.c b/src/southbridge/intel/i82801ca/i82801ca_early_smbus.c
new file mode 100644
index 0000000000..054af75089
--- /dev/null
+++ b/src/southbridge/intel/i82801ca/i82801ca_early_smbus.c
@@ -0,0 +1,138 @@
+#include <device/pci_ids.h>
+#include "i82801ca.h"
+
+static void enable_smbus(void)
+{
+ device_t dev;
+ dev = pci_locate_device(PCI_ID(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_SMB), 0);
+ if (dev == PCI_DEV_INVALID) {
+ die("SMBUS controller not found\r\n");
+ }
+
+ print_debug("SMBus controller enabled\r\n");
+ /* set smbus iobase */
+ pci_write_config32(dev, SMB_BASE, SMBUS_IO_BASE | PCI_BASE_ADDRESS_SPACE_IO);
+ /* Set smbus enable */
+ pci_write_config8(dev, HOSTC, HST_EN);
+ /* Set smbus iospace enable */
+ pci_write_config16(dev, PCI_COMMAND, PCI_COMMAND_IO);
+ /* Disable interrupt generation */
+ outb(0, SMBUS_IO_BASE + SMBHSTCTL);
+ /* clear any lingering errors, so the transaction will run */
+ outb(inb(SMBUS_IO_BASE + SMBHSTSTAT), SMBUS_IO_BASE + SMBHSTSTAT);
+}
+
+
+static inline void smbus_delay(void)
+{
+ outb(0x80, 0x80);
+}
+
+// See http://openbios.org/pipermail/linuxbios/2004-September/009077.html
+// for a description of this function.
+static int smbus_wait_until_active(void)
+{
+ unsigned long loops;
+ loops = SMBUS_TIMEOUT;
+ do {
+ unsigned char val;
+ smbus_delay();
+ val = inb(SMBUS_IO_BASE + SMBHSTSTAT);
+ if ((val & 1)) {
+ break;
+ }
+ } while (--loops);
+ return loops ? 0 : -4;
+}
+
+static int smbus_wait_until_ready(void)
+{
+ unsigned long loops;
+ loops = SMBUS_TIMEOUT;
+ do {
+ unsigned char val;
+ smbus_delay();
+ val = inb(SMBUS_IO_BASE + SMBHSTSTAT);
+ // !HOST_BUSY?
+ if ((val & 1) == 0) {
+ break;
+ }
+ if(loops == (SMBUS_TIMEOUT / 2)) {
+ // Clear status flags
+ outb(inb(SMBUS_IO_BASE + SMBHSTSTAT),
+ SMBUS_IO_BASE + SMBHSTSTAT);
+ }
+ } while(--loops);
+ return loops?0:-2;
+}
+
+static int smbus_wait_until_done(void)
+{
+ unsigned long loops;
+ loops = SMBUS_TIMEOUT;
+ do {
+ unsigned char val;
+ smbus_delay();
+
+ val = inb(SMBUS_IO_BASE + SMBHSTSTAT);
+ // !HOST_BUSY?
+ if ( (val & 1) == 0) {
+ break;
+ }
+ // BYTE_DONE or SUCCESS or error?
+ if ((val & ~((1<<6)|(1<<0)) ) != 0 ) {
+ break;
+ }
+ } while(--loops);
+ return loops?0:-3;
+}
+
+static int smbus_read_byte(unsigned device, unsigned address)
+{
+ unsigned char global_control_register;
+ unsigned char global_status_register;
+ unsigned char byte;
+
+ if (smbus_wait_until_ready() < 0) {
+ return -2;
+ }
+
+ /* setup transaction */
+ /* disable interrupts */
+ outb(inb(SMBUS_IO_BASE + SMBHSTCTL) & 0xfe, SMBUS_IO_BASE + SMBHSTCTL);
+ /* set to read from the specified device */
+ 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 a byte read, with interrupts disabled */
+ outb((inb(SMBUS_IO_BASE + SMBHSTCTL) | 0x40), SMBUS_IO_BASE + SMBHSTCTL);
+ /* poll for it to start */
+ if (smbus_wait_until_active() < 0) {
+ return -4;
+ }
+
+ /* poll for transaction completion */
+ if (smbus_wait_until_done() < 0) {
+ return -3;
+ }
+
+ global_status_register = inb(SMBUS_IO_BASE + SMBHSTSTAT) & ~(1<<6); /* Ignore the In Use Status... */
+
+ /* read results of transaction */
+ byte = inb(SMBUS_IO_BASE + SMBHSTDAT0);
+
+ // SUCCESS?
+ if (global_status_register != 2) {
+ return -1;
+ }
+ return byte;
+}
diff --git a/src/southbridge/intel/i82801ca/i82801ca_ide.c b/src/southbridge/intel/i82801ca/i82801ca_ide.c
new file mode 100644
index 0000000000..50539fc5f7
--- /dev/null
+++ b/src/southbridge/intel/i82801ca/i82801ca_ide.c
@@ -0,0 +1,48 @@
+#include <console/console.h>
+#include <device/device.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
+#include <device/pci_ops.h>
+#include "i82801ca.h"
+
+
+static void ide_init(struct device *dev)
+{
+ /* Enable ide devices so the linux ide driver will work */
+ uint16_t ideTimingConfig;
+ int enable_primary = 1;
+ int enable_secondary = 1;
+
+ ideTimingConfig = pci_read_config16(dev, IDE_TIM_PRI);
+ ideTimingConfig &= ~IDE_DECODE_ENABLE;
+ if (enable_primary) {
+ /* Enable first ide interface */
+ ideTimingConfig |= IDE_DECODE_ENABLE;
+ printk_debug("IDE0 ");
+ }
+ pci_write_config16(dev, IDE_TIM_PRI, ideTimingConfig);
+
+ ideTimingConfig = pci_read_config16(dev, IDE_TIM_SEC);
+ ideTimingConfig &= ~IDE_DECODE_ENABLE;
+ if (enable_secondary) {
+ /* Enable secondary ide interface */
+ ideTimingConfig |= IDE_DECODE_ENABLE;
+ printk_debug("IDE1 ");
+ }
+ pci_write_config16(dev, IDE_TIM_SEC, ideTimingConfig);
+}
+
+static struct device_operations ide_ops = {
+ .read_resources = pci_dev_read_resources,
+ .set_resources = pci_dev_set_resources,
+ .enable_resources = pci_dev_enable_resources,
+ .init = ide_init,
+ .scan_bus = 0,
+ .enable = i82801ca_enable,
+};
+
+static struct pci_driver ide_driver __pci_driver = {
+ .ops = &ide_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = PCI_DEVICE_ID_INTEL_82801CA_IDE,
+};
diff --git a/src/southbridge/intel/i82801ca/i82801ca_lpc.c b/src/southbridge/intel/i82801ca/i82801ca_lpc.c
new file mode 100644
index 0000000000..306d01a972
--- /dev/null
+++ b/src/southbridge/intel/i82801ca/i82801ca_lpc.c
@@ -0,0 +1,240 @@
+/*
+ * (C) 2003 Linux Networx, SuSE Linux AG
+ * (C) 2004 Tyan Computer
+ * (c) 2005 Digital Design Corporation
+ */
+#include <console/console.h>
+#include <device/device.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
+#include <device/pci_ops.h>
+#include <pc80/mc146818rtc.h>
+#include <pc80/isa-dma.h>
+#include <arch/io.h>
+#include "i82801ca.h"
+
+#define NMI_OFF 0
+
+#ifndef MAINBOARD_POWER_ON_AFTER_POWER_FAIL
+#define MAINBOARD_POWER_ON_AFTER_POWER_FAIL MAINBOARD_POWER_ON
+#endif
+
+#define MAINBOARD_POWER_OFF 0
+#define MAINBOARD_POWER_ON 1
+
+
+void i82801ca_enable_ioapic( struct device *dev)
+{
+ uint32_t dword;
+ volatile uint32_t* ioapic_index = (volatile uint32_t*)0xfec00000;
+ volatile uint32_t* ioapic_data = (volatile uint32_t*)0xfec00010;
+
+ dword = pci_read_config32(dev, GEN_CNTL);
+ dword |= (3 << 7); /* enable ioapic & disable SMBus interrupts */
+ dword |= (1 <<13); /* coprocessor error enable */
+ dword |= (1 << 1); /* delay transaction enable */
+ dword |= (1 << 2); /* DMA collection buf enable */
+ pci_write_config32(dev, GEN_CNTL, dword);
+ printk_debug("ioapic southbridge enabled %x\n",dword);
+
+ // Must program the APIC's ID before using it
+
+ *ioapic_index = 0; // Select APIC ID register
+ *ioapic_data = (2<<24);
+
+ // Hang if the ID didn't take (chip not present?)
+ *ioapic_index = 0;
+ dword = *ioapic_data;
+ printk_debug("Southbridge apic id = %x\n", (dword>>24) & 0xF);
+ if(dword != (2<<24))
+ die("");
+
+ *ioapic_index = 3; // Select Boot Configuration register
+ *ioapic_data = 1; // Use Processor System Bus to deliver interrupts
+}
+
+// This is how interrupts are received from the Super I/O chip
+void i82801ca_enable_serial_irqs( struct device *dev)
+{
+ // Recognize serial IRQs, continuous mode, frame size 21, 4 clock start frame pulse width
+ pci_write_config8(dev, SERIRQ_CNTL, (1 << 7)|(1 << 6)|((21 - 17) << 2)|(0<< 0));
+}
+
+//----------------------------------------------------------------------------------
+// Function: i82801ca_lpc_route_dma
+// Parameters: dev
+// mask - identifies whether each channel should be used for PCI DMA
+// (bit = 0) or LPC DMA (bit = 1). The LSb controls channel 0.
+// Channel 4 is not used (reserved).
+// Return Value: None
+// Description: Route all DMA channels to either PCI or LPC.
+//
+void i82801ca_lpc_route_dma( struct device *dev, uint8_t mask)
+{
+ uint16_t dmaConfig;
+ int channelIndex;
+
+ dmaConfig = pci_read_config16(dev, PCI_DMA_CFG);
+ dmaConfig &= 0x300; // Preserve reserved bits
+ for(channelIndex = 0; channelIndex < 8; channelIndex++) {
+ if (channelIndex == 4)
+ continue; // Register doesn't support channel 4
+ dmaConfig |= ((mask & (1 << channelIndex))? 3:1) << (channelIndex*2);
+ }
+ pci_write_config16(dev, PCI_DMA_CFG, dmaConfig);
+}
+
+void i82801ca_rtc_init(struct device *dev)
+{
+ uint32_t dword;
+ int rtc_failed;
+ int pwr_on = MAINBOARD_POWER_ON_AFTER_POWER_FAIL;
+ uint8_t pmcon3 = pci_read_config8(dev, GEN_PMCON_3);
+
+ rtc_failed = pmcon3 & RTC_BATTERY_DEAD;
+ if (rtc_failed) {
+ // Clear the RTC_BATTERY_DEAD bit, but preserve
+ // the RTC_POWER_FAILED, G3 state, and reserved bits
+ // NOTE: RTC_BATTERY_DEAD and RTC_POWER_FAILED are "write-1-clear" bits
+ pmcon3 &= ~RTC_POWER_FAILED;
+ }
+
+ get_option(&pwr_on, "power_on_after_fail");
+ pmcon3 &= ~SLEEP_AFTER_POWER_FAIL;
+ if (!pwr_on) {
+ pmcon3 |= SLEEP_AFTER_POWER_FAIL;
+ }
+ pci_write_config8(dev, GEN_PMCON_3, pmcon3);
+ printk_info("set power %s after power fail\n",
+ pwr_on ? "on" : "off");
+
+ // See if the Safe Mode jumper is set
+ dword = pci_read_config32(dev, GEN_STS);
+ rtc_failed |= dword & (1 << 2);
+
+ rtc_init(rtc_failed);
+}
+
+
+void i82801ca_1f0_misc(struct device *dev)
+{
+ // Prevent LPC disabling, enable parity errors, and SERR# (System Error)
+ pci_write_config16(dev, PCI_COMMAND, 0x014f);
+
+ // Set ACPI base address to 0x1000 (I/O space)
+ pci_write_config32(dev, PMBASE, 0x00001001);
+
+ // Enable ACPI I/O and power management
+ pci_write_config8(dev, ACPI_CNTL, 0x10);
+
+ // Set GPIO base address to 0x1180 (I/O space)
+ pci_write_config32(dev, GPIO_BASE, 0x00001181);
+
+ // Enable GPIO
+ pci_write_config8(dev, GPIO_CNTL, 0x10);
+
+ // Route PIRQA to IRQ11, PIRQB to IRQ3, PIRQC to IRQ5, PIRQD to IRQ10
+ pci_write_config32(dev, PIRQA_ROUT, 0x0A05030B);
+
+ // Route PIRQE to IRQ7. Leave PIRQF - PIRQH unrouted.
+ pci_write_config8(dev, PIRQE_ROUT, 0x07);
+
+ // Enable access to the upper 128 byte bank of CMOS RAM
+ pci_write_config8(dev, RTC_CONF, 0x04);
+
+ // Decode 0x3F8-0x3FF (COM1) for COMA port,
+ // 0x2F8-0x2FF (COM2) for COMB
+ pci_write_config8(dev, COM_DEC, 0x10);
+
+ // LPT decode defaults to 0x378-0x37F and 0x778-0x77F
+ // Floppy decode defaults to 0x3F0-0x3F5, 0x3F7
+
+ // Enable COMA, COMB, LPT, floppy;
+ // disable microcontroller, Super I/O, sound, gameport
+ pci_write_config16(dev, LPC_EN, 0x000F);
+}
+
+static void lpc_init(struct device *dev)
+{
+ uint8_t byte;
+ int pwr_on=-1;
+ int nmi_option;
+
+ /* IO APIC initialization */
+ i82801ca_enable_ioapic(dev);
+
+ i82801ca_enable_serial_irqs(dev);
+
+ /* power after power fail */
+ /* FIXME this doesn't work! */
+ /* Which state do we want to goto after g3 (power restored)?
+ * 0 == S0 Full On
+ * 1 == S5 Soft Off
+ */
+ byte = pci_read_config8(dev, GEN_PMCON_3);
+ if (pwr_on)
+ byte &= ~1; // Return to S0 (boot) after power is re-applied
+ else
+ byte |= 1; // Return to S5
+ pci_write_config8(dev, GEN_PMCON_3, byte);
+ printk_info("set power %s after power fail\n", pwr_on?"on":"off");
+
+ /* Set up NMI on errors */
+ byte = inb(0x61);
+ byte &= ~(1 << 3); /* IOCHK# NMI Enable */
+ byte &= ~(1 << 2); /* PCI SERR# Enable */
+ outb(byte, 0x61);
+ byte = inb(0x70);
+ nmi_option = NMI_OFF;
+ get_option(&nmi_option, "nmi");
+ if (nmi_option) {
+ byte &= ~(1 << 7); /* set NMI */
+ outb(byte, 0x70);
+ }
+
+ /* Initialize the real time clock */
+ i82801ca_rtc_init(dev);
+
+ i82801ca_lpc_route_dma(dev, 0xff);
+
+ /* Initialize isa dma */
+ isa_dma_init();
+
+ i82801ca_1f0_misc(dev);
+}
+
+static void i82801ca_lpc_read_resources(device_t dev)
+{
+ struct resource *res;
+
+ /* Get the normal pci resources of this device */
+ pci_dev_read_resources(dev);
+
+ /* Add an extra subtractive resource for both memory and I/O */
+ res = new_resource(dev, IOINDEX_SUBTRACTIVE(0, 0));
+ res->flags = IORESOURCE_IO | IORESOURCE_SUBTRACTIVE | IORESOURCE_ASSIGNED;
+
+ res = new_resource(dev, IOINDEX_SUBTRACTIVE(1, 0));
+ res->flags = IORESOURCE_IO | IORESOURCE_SUBTRACTIVE | IORESOURCE_ASSIGNED;
+}
+
+static void i82801ca_lpc_enable_resources(device_t dev)
+{
+ pci_dev_enable_resources(dev);
+ enable_childrens_resources(dev);
+}
+
+static struct device_operations lpc_ops = {
+ .read_resources = i82801ca_lpc_read_resources,
+ .set_resources = pci_dev_set_resources,
+ .enable_resources = i82801ca_lpc_enable_resources,
+ .init = lpc_init,
+ .scan_bus = scan_static_bus,
+ .enable = 0,
+};
+
+static struct pci_driver lpc_driver __pci_driver = {
+ .ops = &lpc_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = PCI_DEVICE_ID_INTEL_82801CA_LPC,
+};
diff --git a/src/southbridge/intel/i82801ca/i82801ca_nic.c b/src/southbridge/intel/i82801ca/i82801ca_nic.c
new file mode 100644
index 0000000000..61d3977158
--- /dev/null
+++ b/src/southbridge/intel/i82801ca/i82801ca_nic.c
@@ -0,0 +1,21 @@
+#include <console/console.h>
+#include <device/device.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
+#include <device/pci_ops.h>
+#include "i82801ca.h"
+
+
+static struct device_operations nic_ops = {
+ .read_resources = pci_dev_read_resources,
+ .set_resources = pci_dev_set_resources,
+ .enable_resources = pci_dev_enable_resources,
+ .init = 0,
+ .scan_bus = 0,
+};
+
+static struct pci_driver nic_driver __pci_driver = {
+ .ops = &nic_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = PCI_DEVICE_ID_INTEL_82801CA_LAN,
+};
diff --git a/src/southbridge/intel/i82801ca/i82801ca_pci.c b/src/southbridge/intel/i82801ca/i82801ca_pci.c
new file mode 100644
index 0000000000..d5355cb62a
--- /dev/null
+++ b/src/southbridge/intel/i82801ca/i82801ca_pci.c
@@ -0,0 +1,30 @@
+#include <console/console.h>
+#include <device/device.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
+#include <device/pci_ops.h>
+#include "i82801ca.h"
+
+static void pci_init(struct device *dev)
+{
+ // NOTE: the original (v1) 'CA code set these in the bridge register (0x3E-3F)
+ /* Enable pci error detecting */
+ uint32_t dword = pci_read_config32(dev, PCI_COMMAND);
+ dword |= (PCI_COMMAND_SERR | PCI_COMMAND_PARITY);
+ pci_write_config32(dev, PCI_COMMAND, dword);
+}
+
+static struct device_operations pci_ops = {
+ .read_resources = pci_bus_read_resources,
+ .set_resources = pci_dev_set_resources,
+ .enable_resources = pci_bus_enable_resources,
+ .init = pci_init,
+ .scan_bus = pci_scan_bridge,
+};
+
+static struct pci_driver pci_driver __pci_driver = {
+ .ops = &pci_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = PCI_DEVICE_ID_INTEL_82801CA_PCI,
+};
+
diff --git a/src/southbridge/intel/i82801ca/i82801ca_reset.c b/src/southbridge/intel/i82801ca/i82801ca_reset.c
new file mode 100644
index 0000000000..93ef6d9f23
--- /dev/null
+++ b/src/southbridge/intel/i82801ca/i82801ca_reset.c
@@ -0,0 +1,8 @@
+#include <arch/io.h>
+
+void i82801ca_hard_reset(void)
+{
+ /* Try rebooting through port 0xcf9 */
+ // Hard reset without power cycle
+ outb((0 <<3)|(1<<2)|(1<<1), 0xcf9);
+}
diff --git a/src/southbridge/intel/i82801ca/i82801ca_smbus.c b/src/southbridge/intel/i82801ca/i82801ca_smbus.c
new file mode 100644
index 0000000000..ab95e69e70
--- /dev/null
+++ b/src/southbridge/intel/i82801ca/i82801ca_smbus.c
@@ -0,0 +1,83 @@
+#include <smbus.h>
+#include <pci.h>
+#include <arch/io.h>
+#include "i82801ca.h"
+
+#define PM_BUS 0
+#define PM_DEVFN PCI_DEVFN(0x1f,3)
+
+void smbus_enable(void)
+{
+ /* iobase addr */
+ pcibios_write_config_dword(PM_BUS, PM_DEVFN, SMB_BASE,
+ SMBUS_IO_BASE | PCI_BASE_ADDRESS_SPACE_IO);
+ /* smbus enable */
+ pcibios_write_config_byte(PM_BUS, PM_DEVFN, HOSTC, HST_EN);
+ /* iospace enable */
+ pcibios_write_config_word(PM_BUS, PM_DEVFN, PCI_COMMAND, PCI_COMMAND_IO);
+
+ /* Disable interrupt generation */
+ outb(0, SMBUS_IO_BASE + SMBHSTCTL);
+}
+
+static void smbus_wait_until_ready(void)
+{
+ // Loop while HOST_BUSY
+ while((inb(SMBUS_IO_BASE + SMBHSTSTAT) & 1) == 1) {
+ /* nop */
+ }
+}
+
+static void smbus_wait_until_done(void)
+{
+ unsigned char byte;
+
+ // Loop while HOST_BUSY
+ do {
+ byte = inb(SMBUS_IO_BASE + SMBHSTSTAT);
+ }
+ while((byte &1) == 1);
+
+ // Wait for SUCCESS or error or BYTE_DONE
+ while( (byte & ~1) == 0) {
+ byte = inb(SMBUS_IO_BASE + SMBHSTSTAT);
+ }
+}
+
+int smbus_read_byte(unsigned device, unsigned address, unsigned char *result)
+{
+ unsigned char host_status_register;
+ unsigned char byte;
+
+ smbus_wait_until_ready();
+
+ /* setup transaction */
+ /* disable interrupts */
+ outb(inb(SMBUS_IO_BASE + SMBHSTCTL) & (~1), SMBUS_IO_BASE + SMBHSTCTL);
+ /* set to read from the specified device */
+ outb(((device & 0x7f) << 1) | 1, SMBUS_IO_BASE + SMBHSTADD);
+ /* 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 */
+ smbus_wait_until_done();
+
+ host_status_register = inb(SMBUS_IO_BASE + SMBHSTSTAT);
+
+ /* read results of transaction */
+ byte = inb(SMBUS_IO_BASE + SMBHSTDAT0);
+
+ *result = byte;
+ return host_status_register != 0x02; // return true if !SUCCESS
+}
diff --git a/src/southbridge/intel/i82801ca/i82801ca_usb.c b/src/southbridge/intel/i82801ca/i82801ca_usb.c
new file mode 100644
index 0000000000..fd8d3fb45a
--- /dev/null
+++ b/src/southbridge/intel/i82801ca/i82801ca_usb.c
@@ -0,0 +1,49 @@
+#include <console/console.h>
+#include <device/device.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
+#include <device/pci_ops.h>
+#include "i82801ca.h"
+
+static void usb_init(struct device *dev)
+{
+
+#if 0
+ uint32_t cmd;
+ printk_debug("USB: Setting up controller.. ");
+ cmd = pci_read_config32(dev, PCI_COMMAND);
+ pci_write_config32(dev, PCI_COMMAND,
+ cmd | PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
+ PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE);
+
+
+ printk_debug("done.\n");
+#endif
+
+}
+
+static struct device_operations usb_ops = {
+ .read_resources = pci_dev_read_resources,
+ .set_resources = pci_dev_set_resources,
+ .enable_resources = pci_dev_enable_resources,
+ .init = usb_init,
+ .scan_bus = 0,
+ .enable = i82801ca_enable,
+};
+
+static struct pci_driver usb_driver_1 __pci_driver = {
+ .ops = &usb_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = PCI_DEVICE_ID_INTEL_82801CA_USB,
+};
+static struct pci_driver usb_driver_2 __pci_driver = {
+ .ops = &usb_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = PCI_DEVICE_ID_INTEL_82801CA_USB2,
+};
+static struct pci_driver usb_driver_3 __pci_driver = {
+ .ops = &usb_ops,
+ .vendor = PCI_VENDOR_ID_INTEL,
+ .device = PCI_DEVICE_ID_INTEL_82801CA_USB3,
+};
+