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); }
I found a solution to my problem. My I2C code relies on using several "static int" variables. In order to get the code to build I had to change them to global and add the VAR16VISIBLE definition.
int i2c_inited VAR16VISIBLE = 0;
My "static" i2c_inited variable doesn't need global visibility. Is there a better way to do this in SeaBIOS so that they are static local variables rather than global and still satisfy the 16/32 segment stuff?
thanks in advance, Dave