diff options
Diffstat (limited to 'src/southbridge/intel')
-rw-r--r-- | src/southbridge/intel/i440bx/chip.h | 13 | ||||
-rw-r--r-- | src/southbridge/intel/i440bx/i440bx_early_smbus.c | 45 | ||||
-rw-r--r-- | src/southbridge/intel/i440bx/i440bx_smbus.c | 43 | ||||
-rw-r--r-- | src/southbridge/intel/i440bx/i440bx_smbus.h | 230 |
4 files changed, 331 insertions, 0 deletions
diff --git a/src/southbridge/intel/i440bx/chip.h b/src/southbridge/intel/i440bx/chip.h new file mode 100644 index 0000000000..5952e38582 --- /dev/null +++ b/src/southbridge/intel/i440bx/chip.h @@ -0,0 +1,13 @@ +#ifndef I440BX_CHIP_H +#define I440BX_CHIP_H + +struct southbridge_intel_i440bx_config +{ + unsigned int ide0_enable : 1; + unsigned int ide1_enable : 1; +}; + +struct chip_operations; +extern struct chip_operations southbridge_intel_i440bx_ops; + +#endif /* I440BX_CHIP_H */ diff --git a/src/southbridge/intel/i440bx/i440bx_early_smbus.c b/src/southbridge/intel/i440bx/i440bx_early_smbus.c new file mode 100644 index 0000000000..a3db7b88c0 --- /dev/null +++ b/src/southbridge/intel/i440bx/i440bx_early_smbus.c @@ -0,0 +1,45 @@ +#include "i440bx_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 ); + // 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 + SMBGSTATUS); +} + +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); +} + +static int smbus_read_byte(unsigned device, unsigned address) +{ + return do_smbus_read_byte(SMBUS_IO_BASE, device, address); +} + +static int smbus_write_byte(unsigned device, unsigned address, unsigned char val) +{ + return do_smbus_write_byte(SMBUS_IO_BASE, device, address, val); +} diff --git a/src/southbridge/intel/i440bx/i440bx_smbus.c b/src/southbridge/intel/i440bx/i440bx_smbus.c new file mode 100644 index 0000000000..9609e357c1 --- /dev/null +++ b/src/southbridge/intel/i440bx/i440bx_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 "i440bx.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 = i440bx_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/i440bx/i440bx_smbus.h b/src/southbridge/intel/i440bx/i440bx_smbus.h new file mode 100644 index 0000000000..9a100c0f8b --- /dev/null +++ b/src/southbridge/intel/i440bx/i440bx_smbus.h @@ -0,0 +1,230 @@ +#include <device/smbus_def.h> + +#define SMBGSTATUS 0x0 +#define SMBGCTL 0x2 +#define SMBHSTCMD 0x3 +#define SMBHSTADDR 0x4 +#define SMBHSTDAT 0x5 + +#define SMBUS_TIMEOUT (100*1000*10) +#define SMBUS_STATUS_MASK 0x1e + +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 + SMBGSTATUS); + if ((val & 0x1) == 0) { + break; + } + if(loops == (SMBUS_TIMEOUT / 2)) { + outw(inw(smbus_io_base + SMBGSTATUS), + smbus_io_base + SMBGSTATUS); + } + } 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 + SMBGSTATUS); + // 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 + SMBGCTL) & ~((1<<10)|(1<<9)|(1<<8)|(1<<4)), smbus_io_base + SMBGCTL); + /* set the device I'm talking too */ + outw(((device & 0x7f) << 1) | 1, smbus_io_base + SMBHSTADDR); + /* set the command/address... */ + outb(0, smbus_io_base + SMBHSTCMD); + /* set up for a send byte */ + outw((inw(smbus_io_base + SMBGCTL) & ~7) | (0x1), smbus_io_base + SMBGCTL); + + /* 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 + SMBGSTATUS), smbus_io_base + SMBGSTATUS); + + /* set the data word...*/ + outw(0, smbus_io_base + SMBHSTDAT); + + /* start the command */ + outw((inw(smbus_io_base + SMBGCTL) | (1 << 3)), smbus_io_base + SMBGCTL); + + + /* 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 + SMBGSTATUS); + + /* read results of transaction */ + byte = inb(smbus_io_base + SMBHSTDAT) & 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 + SMBGCTL) & ~((1<<10)|(1<<9)|(1<<8)|(1<<4)), smbus_io_base + SMBGCTL); + /* set the device I'm talking too */ + outw(((device & 0x7f) << 1) | 0, smbus_io_base + SMBHSTADDR); + /* set the command/address... */ + outb(0, smbus_io_base + SMBHSTCMD); + /* set up for a send byte */ + outw((inw(smbus_io_base + SMBGCTL) & ~7) | (0x1), smbus_io_base + SMBGCTL); + + /* 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 + SMBGSTATUS), smbus_io_base + SMBGSTATUS); + + /* set the data word...*/ + outw(value, smbus_io_base + SMBHSTDAT); + + /* start the command */ + outw((inw(smbus_io_base + SMBGCTL) | (1 << 3)), smbus_io_base + SMBGCTL); + + + /* 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 + SMBGSTATUS); + + 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 global_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 + SMBGSTATUS); + + /* set the device I'm talking too */ + outb(((device & 0x7f) << 1) | 1, smbus_io_base + SMBHSTADDR); + /* set the command/address... */ + outb(address & 0xFF, smbus_io_base + SMBHSTCMD); + + /* clear the data word...*/ + outb(0, smbus_io_base + SMBHSTDAT); + + /* start a byte read with interrupts disabled */ + outb( (0x02 << 2)|(1<<6), smbus_io_base + SMBGCTL); + + /* 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 + SMBGSTATUS); + + /* read results of transaction */ + byte = inw(smbus_io_base + SMBHSTDAT) & 0xff; + + if ((global_status_register & SMBUS_STATUS_MASK) != (1 << 4)) { + 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 + SMBGCTL) & ~((1<<10)|(1<<9)|(1<<8)|(1<<4)), smbus_io_base + SMBGCTL); + /* set the device I'm talking too */ + outw(((device & 0x7f) << 1) | 0, smbus_io_base + SMBHSTADDR); + outb(address & 0xFF, smbus_io_base + SMBHSTCMD); + /* set up for a byte data write */ /* FIXME */ + outw((inw(smbus_io_base + SMBGCTL) & ~7) | (0x2), smbus_io_base + SMBGCTL); + /* 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 + SMBGSTATUS), smbus_io_base + SMBGSTATUS); + + /* write the data word...*/ + outw(val, smbus_io_base + SMBHSTDAT); + + /* start the command */ + outw((inw(smbus_io_base + SMBGCTL) | (1 << 3)), smbus_io_base + SMBGCTL); + + /* 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 + SMBGSTATUS); + + if ((global_status_register & SMBUS_STATUS_MASK) != (1 << 4)) { + return SMBUS_ERROR; + } + return 0; +} + |