diff options
Diffstat (limited to 'src/southbridge/intel/i82371eb')
-rw-r--r-- | src/southbridge/intel/i82371eb/chip.h | 13 | ||||
-rw-r--r-- | src/southbridge/intel/i82371eb/i8371eb_early_smbus.c | 53 | ||||
-rw-r--r-- | src/southbridge/intel/i82371eb/i8371eb_smbus.c | 43 | ||||
-rw-r--r-- | src/southbridge/intel/i82371eb/i8371eb_smbus.h | 239 |
4 files changed, 348 insertions, 0 deletions
diff --git a/src/southbridge/intel/i82371eb/chip.h b/src/southbridge/intel/i82371eb/chip.h new file mode 100644 index 0000000000..9516f3c73b --- /dev/null +++ b/src/southbridge/intel/i82371eb/chip.h @@ -0,0 +1,13 @@ +#ifndef I8371EB_CHIP_H +#define I8371EB_CHIP_H + +struct southbridge_intel_i8371eb_config +{ + unsigned int ide0_enable : 1; + unsigned int ide1_enable : 1; +}; + +struct chip_operations; +extern struct chip_operations southbridge_intel_i8371eb_ops; + +#endif /* I8371EB_CHIP_H */ diff --git a/src/southbridge/intel/i82371eb/i8371eb_early_smbus.c b/src/southbridge/intel/i82371eb/i8371eb_early_smbus.c new file mode 100644 index 0000000000..4b12048016 --- /dev/null +++ b/src/southbridge/intel/i82371eb/i8371eb_early_smbus.c @@ -0,0 +1,53 @@ +#include "i8371eb_smbus.h" + +#define SMBUS_IO_BASE 0x0f00 + +static void enable_smbus(void) +{ + device_t dev; + dev = pci_locate_device(PCI_ID(0x8086, 0x7113), 0); + if (dev == PCI_DEV_INVALID) { + die("SMBUS controller not found\r\n"); + } + uint8_t enable; + print_spew("SMBus controller enabled\r\n"); + pci_write_config32(dev, 0x90, SMBUS_IO_BASE | 1 ); + // Enable and set SMBBus + // 0x01 Interrupt to SMI# + // (0x4<<1)|1 set interrupt to IRQ9 + pci_write_config8(dev, 0xd2, (0x4<<1)|1); + + // Enable the IO space + pci_write_config16(dev, 0x04, 1); + + /* clear any lingering errors, so the transaction will run */ + outb(0x1e, SMBUS_IO_BASE + SMBHST_STATUS); +} + + + +static int smbus_read_byte(unsigned device, unsigned address) +{ + return do_smbus_read_byte(SMBUS_IO_BASE, device, address); +} + + +// The following functions are broken. Do no use until you +// have fixed the low level code to do the right thing. +// +#if 0 +static int smbus_write_byte(unsigned device, unsigned address, unsigned char val) +{ + return do_smbus_write_byte(SMBUS_IO_BASE, device, address, val); +} + +static int smbus_recv_byte(unsigned device) +{ + return do_smbus_recv_byte(SMBUS_IO_BASE, device); +} + +static int smbus_send_byte(unsigned device, unsigned char val) +{ + return do_smbus_send_byte(SMBUS_IO_BASE, device, val); +} +#endif diff --git a/src/southbridge/intel/i82371eb/i8371eb_smbus.c b/src/southbridge/intel/i82371eb/i8371eb_smbus.c new file mode 100644 index 0000000000..7a09cce555 --- /dev/null +++ b/src/southbridge/intel/i82371eb/i8371eb_smbus.c @@ -0,0 +1,43 @@ +/* + * (C) 2004 Linux Networx + * (C) 2005 Bitworks +*/ + +#include <console/console.h> +#include <device/device.h> +#include <device/pci.h> +#include <device/pci_ids.h> +#include <device/pci_ops.h> +#include <device/smbus.h> +#include <arch/io.h> +#include "i8371eb.h" + + +static void lpci_set_subsystem(device_t dev, unsigned vendor, unsigned device) +{ + pci_write_config32(dev, 0x44, + ((device & 0xffff) << 16) | (vendor & 0xffff)); +} + +static struct smbus_bus_operations lops_smbus_bus = { +}; + +static struct pci_operations lops_pci = { + .set_subsystem = lpci_set_subsystem, +}; +static struct device_operations smbus_ops = { + .read_resources = pci_dev_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_enable_resources, + .init = 0, + .scan_bus = scan_static_bus, + .enable = i8371eb_enable, + .ops_pci = &lops_pci, + .ops_smbus_bus = &lops_smbus_bus, +}; + +static struct pci_driver smbus_driver __pci_driver = { + .ops = &smbus_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_INTEL_440BX_SMB, +}; diff --git a/src/southbridge/intel/i82371eb/i8371eb_smbus.h b/src/southbridge/intel/i82371eb/i8371eb_smbus.h new file mode 100644 index 0000000000..e1893c5cb1 --- /dev/null +++ b/src/southbridge/intel/i82371eb/i8371eb_smbus.h @@ -0,0 +1,239 @@ +#include <device/smbus_def.h> + +#define SMBHST_STATUS 0x0 +#define SMBHST_CTL 0x2 +#define SMBHST_CMD 0x3 +#define SMBHST_ADDR 0x4 +#define SMBHST_DAT 0x5 + +#define SMBUS_TIMEOUT (100*1000*10) +#define SMBUS_STATUS_MASK 0x1e +#define SMBUS_ERROR_FLAG (1<<2) + +static inline void smbus_delay(void) +{ + outb(0x80, 0x80); + outb(0x80, 0x80); + outb(0x80, 0x80); + outb(0x80, 0x80); + outb(0x80, 0x80); + outb(0x80, 0x80); +} + +static int smbus_wait_until_ready(unsigned smbus_io_base) +{ + unsigned long loops; + loops = SMBUS_TIMEOUT; + do { + unsigned char val; + smbus_delay(); + val = inb(smbus_io_base + SMBHST_STATUS); + if ((val & 0x1) == 0) { + break; + } +#if 0 + if(loops == (SMBUS_TIMEOUT / 2)) { + outw(inw(smbus_io_base + SMBHST_STATUS), + smbus_io_base + SMBHST_STATUS); + } +#endif + } while(--loops); + return loops?0:SMBUS_WAIT_UNTIL_READY_TIMEOUT; +} + +static int smbus_wait_until_done(unsigned smbus_io_base) +{ + unsigned long loops; + loops = SMBUS_TIMEOUT; + do { + unsigned short val; + smbus_delay(); + + val = inb(smbus_io_base + SMBHST_STATUS); + // Make sure the command is done + if ((val & 0x1) != 0) { + continue; + } + // Don't break out until one of the interrupt + // flags is set. + if (val & 0xfe) { + break; + } + } while(--loops); + return loops?0:SMBUS_WAIT_UNTIL_DONE_TIMEOUT; +} + +static int do_smbus_recv_byte(unsigned smbus_io_base, unsigned device) +{ + unsigned global_status_register; + unsigned byte; + + if (smbus_wait_until_ready(smbus_io_base) < 0) { + return SMBUS_WAIT_UNTIL_READY_TIMEOUT; + } + + /* setup transaction */ + /* disable interrupts */ + outw(inw(smbus_io_base + SMBHST_CTL) & ~((1<<10)|(1<<9)|(1<<8)|(1<<4)), smbus_io_base + SMBHST_CTL); + /* set the device I'm talking too */ + outw(((device & 0x7f) << 1) | 1, smbus_io_base + SMBHST_ADDR); + /* set the command/address... */ + outb(0, smbus_io_base + SMBHST_CMD); + /* set up for a send byte */ + outw((inw(smbus_io_base + SMBHST_CTL) & ~7) | (0x1), smbus_io_base + SMBHST_CTL); + + /* clear any lingering errors, so the transaction will run */ + /* Do I need to write the bits to a 1 to clear an error? */ + outw(inw(smbus_io_base + SMBHST_STATUS), smbus_io_base + SMBHST_STATUS); + + /* set the data word...*/ + outw(0, smbus_io_base + SMBHST_DAT); + + /* start the command */ + outw((inw(smbus_io_base + SMBHST_CTL) | (1 << 3)), smbus_io_base + SMBHST_CTL); + + + /* poll for transaction completion */ + if (smbus_wait_until_done(smbus_io_base) < 0) { + return SMBUS_WAIT_UNTIL_DONE_TIMEOUT; + } + + global_status_register = inw(smbus_io_base + SMBHST_STATUS); + + /* read results of transaction */ + byte = inb(smbus_io_base + SMBHST_DAT) & 0xff; + + // Check for any result other than a command completion + if ((global_status_register & SMBUS_STATUS_MASK) != (1 << 1)) { + return SMBUS_ERROR; + } + return byte; +} + +static int do_smbus_send_byte(unsigned smbus_io_base, unsigned device, unsigned value) +{ + unsigned global_status_register; + + if (smbus_wait_until_ready(smbus_io_base) < 0) { + return SMBUS_WAIT_UNTIL_READY_TIMEOUT; + } + + /* setup transaction */ + /* disable interrupts */ + outw(inw(smbus_io_base + SMBHST_CTL) & ~((1<<10)|(1<<9)|(1<<8)|(1<<4)), smbus_io_base + SMBHST_CTL); + /* set the device I'm talking too */ + outw(((device & 0x7f) << 1) | 0, smbus_io_base + SMBHST_ADDR); + /* set the command/address... */ + outb(0, smbus_io_base + SMBHST_CMD); + /* set up for a send byte */ + outw((inw(smbus_io_base + SMBHST_CTL) & ~7) | (0x1), smbus_io_base + SMBHST_CTL); + + /* clear any lingering errors, so the transaction will run */ + /* Do I need to write the bits to a 1 to clear an error? */ + outw(inw(smbus_io_base + SMBHST_STATUS), smbus_io_base + SMBHST_STATUS); + + /* set the data word...*/ + outw(value, smbus_io_base + SMBHST_DAT); + + /* start the command */ + outw((inw(smbus_io_base + SMBHST_CTL) | (1 << 3)), smbus_io_base + SMBHST_CTL); + + + /* poll for transaction completion */ + if (smbus_wait_until_done(smbus_io_base) < 0) { + return SMBUS_WAIT_UNTIL_DONE_TIMEOUT; + } + global_status_register = inw(smbus_io_base + SMBHST_STATUS); + + if ((global_status_register & SMBUS_STATUS_MASK) != (1 << 4)) { + return SMBUS_ERROR; + } + return 0; +} + + +static int do_smbus_read_byte(unsigned smbus_io_base, unsigned device, unsigned address) +{ + unsigned status_register; + unsigned byte; + + if (smbus_wait_until_ready(smbus_io_base) < 0) { + return SMBUS_WAIT_UNTIL_READY_TIMEOUT; + } + + /* setup transaction */ + + /* clear any lingering errors, so the transaction will run */ + outb(0x1e, smbus_io_base + SMBHST_STATUS); + + /* set the device I'm talking too */ + outb(((device & 0x7f) << 1) | 1, smbus_io_base + SMBHST_ADDR); + + /* set the command/address... */ + outb(address & 0xff, smbus_io_base + SMBHST_CMD); + + /* clear the data word...*/ + outb(0, smbus_io_base + SMBHST_DAT); + + /* start a byte read with interrupts disabled */ + outb( (0x02 << 2)|(1<<6), smbus_io_base + SMBHST_CTL); + + /* poll for transaction completion */ + if (smbus_wait_until_done(smbus_io_base) < 0) { + return SMBUS_WAIT_UNTIL_DONE_TIMEOUT; + } + + status_register = inw(smbus_io_base + SMBHST_STATUS); + + /* read results of transaction */ + byte = inw(smbus_io_base + SMBHST_DAT) & 0xff; + + if (status_register & 0x04) { +#if 0 + print_debug("Read fail "); + print_debug_hex16(status_register); + print_debug("\r\n"); +#endif + return SMBUS_ERROR; + } + return byte; +} + +static int do_smbus_write_byte(unsigned smbus_io_base, unsigned device, unsigned address, unsigned char val) +{ + unsigned global_status_register; + + if (smbus_wait_until_ready(smbus_io_base) < 0) { + return SMBUS_WAIT_UNTIL_READY_TIMEOUT; + } + + /* setup transaction */ + /* disable interrupts */ + outw(inw(smbus_io_base + SMBHST_CTL) & ~((1<<10)|(1<<9)|(1<<8)|(1<<4)), smbus_io_base + SMBHST_CTL); + /* set the device I'm talking too */ + outw(((device & 0x7f) << 1) | 0, smbus_io_base + SMBHST_ADDR); + outb(address & 0xFF, smbus_io_base + SMBHST_CMD); + /* set up for a byte data write */ /* FIXME */ + outw((inw(smbus_io_base + SMBHST_CTL) & ~7) | (0x2), smbus_io_base + SMBHST_CTL); + /* clear any lingering errors, so the transaction will run */ + /* Do I need to write the bits to a 1 to clear an error? */ + outw(inw(smbus_io_base + SMBHST_STATUS), smbus_io_base + SMBHST_STATUS); + + /* write the data word...*/ + outw(val, smbus_io_base + SMBHST_DAT); + + /* start the command */ + outw((inw(smbus_io_base + SMBHST_CTL) | (1 << 3)), smbus_io_base + SMBHST_CTL); + + /* poll for transaction completion */ + if (smbus_wait_until_done(smbus_io_base) < 0) { + return SMBUS_WAIT_UNTIL_DONE_TIMEOUT; + } + global_status_register = inw(smbus_io_base + SMBHST_STATUS); + + if ((global_status_register & SMBUS_STATUS_MASK) != (1 << 1)) { + return SMBUS_ERROR; + } + return 0; +} + |