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 south bridge 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);
}