All,

I've added code to output.c to implement a I2C console interface.
Everything was working great on my AMD/Persimmon until I added additional code
to determine what southbridge I was on. I'm evidently not correctly handling
the 16/32 bit code segments correctly. It's evident when I run the debugger
and watch the disassembly. Not only is the disassembly wrong but it isn't
even consistent between runs.

The new code is below, along with the modified putc_debug().

Any suggestions would be appreciated.

Thanks,
Dave


#if MODE16
#define FIXUP_CODE_SEG_LOCATION VAR16
#elif MODESEGMENT
#define FIXUP_CODE_SEG_LOCATION VAR32SEG
#else
#define FIXUP_CODE_SEG_LOCATION
#endif

struct amd_i2c_list{
    u16 sb_ven;
    u16 sb_dev;
    u8  sb_ver;
    u8 base_access;
};

/* send a debug character to the i2c port */
static void debug_i2c(char data)
{
    static int FIXUP_CODE_SEG_LOCATION i2c_amd_iobase = 0;
    static int FIXUP_CODE_SEG_LOCATION i2c_inited = 0;

    static const FIXUP_CODE_SEG_LOCATION struct amd_i2c_list amd_smbus_table[] = {
        { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, 0x39, 2 }, /* ATI/AMD SB700 base in pci space */
        { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, 0x42, 1 }, /* ATI/AMD SB800 base in pmreg */
        { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SB900_SM,    0x14, 1 }, /* ATI/AMD SB900 base in pmreg */
    };

    volatile u16 rd_vendor, rd_device;
    volatile u8 rd_ver, base_access;
    int i;
    u32 x;

    if (!CONFIG_DEBUG_I2C)
        return;

    /* check to see if we already tried init and it failed */
    if (i2c_inited && !i2c_amd_iobase)
        return;

    /* check to see if the base address has been initialized */
    if (!i2c_inited) {
        i2c_inited=1;
        /* check for the AMD/ATI southbridges */
        rd_vendor = pci_config_readw(0xA0, 0x00);
        rd_device = pci_config_readw(0xA0, 0x02);
        rd_ver = pci_config_readb(0xA0, 0x08);
        base_access=0;
        for (i=0;i < sizeof(amd_smbus_table)/sizeof(amd_smbus_table[0]);++i) {
            if ((amd_smbus_table[i].sb_ven==rd_vendor)
                && (amd_smbus_table[i].sb_dev==rd_device)
                && (amd_smbus_table[i].sb_ver==rd_ver)) {
                base_access = amd_smbus_table[i].base_access;
                break;
            }
        }

        switch (base_access) {
        case 1:
            /* check the PM regs for the SM bus base address */
            outb (0x2D, 0xCD6);
            i2c_amd_iobase = inb(0xCD7)<<8;
            outb (0x2C, 0xCD6);
            i2c_amd_iobase |= inb(0xCD7);
            if ((i2c_amd_iobase!=0) && ((i2c_amd_iobase&0x01)==0x01)) {
                /* Found a SBx00 base address in PM reg space */
                i2c_amd_iobase &= 0xffe0;
            }
            else {
                /* can not find a valid base address */
                i2c_amd_iobase = 0;
            }
            break;
        case 2:
            /* check PCI config space for SM bus base address */
            x = pci_config_readl(0xA0, 0x90);
            if ((x!=0) && ((x&0x01)==0x01)) {
                /* Found a SBx00 base address in PCI cfg space */
                i2c_amd_iobase = (int)(x & 0xffe0);
            }
            else {
                /* can not find a valid base address */
                i2c_amd_iobase = 0;
            }
            break;
        default:
            i2c_amd_iobase = 0;
            break;
        }
        /* add checks for other vendors SMBUS controllers here */
    }

    if (i2c_amd_iobase) {
        /* this sequence will send out addr/data on the AMD SBx00 smbus */
        int timeout=DEBUG_TIMEOUT;
        /* check to see if the h/w is idle */
        do {
            if (!timeout--)
                return; // Ran out of time.
        }
        while ((inb(i2c_amd_iobase) & 0x01)!=0x00);
        outb (0xFF, i2c_amd_iobase + 0);     // clear error status
        outb (0x1F, i2c_amd_iobase + 1);     // clear error status
        outb (data, i2c_amd_iobase + 3);     // tx index
        outb (CONFIG_DEBUG_I2C_DEVICE_ADDR, i2c_amd_iobase + 4);  // slave address and write bit
        outb (0x44, i2c_amd_iobase + 2);     // command it to go
        /* check to see if the h/w is done */
        do {
            if (!timeout--)
                return; // Ran out of time.
        }
        while ((inb(i2c_amd_iobase) & 0x01)!=0x00);
    }
}

// Write a character to debug port(s).
static void
putc_debug(struct putcinfo *action, char c)
{
    if (! CONFIG_DEBUG_LEVEL)
        return;
    if (CONFIG_DEBUG_IO)
        // Send character to debug port.
        outb(c, GET_GLOBAL(DebugOutputPort));
    if (CONFIG_DEBUG_I2C)
        debug_i2c(c);
    if (c == '\n')
        debug_serial('\r');
    debug_serial(c);
}