From a8e1168064b34b46494b58480411a11bc98340f6 Mon Sep 17 00:00:00 2001 From: Stefan Reinauer Date: Wed, 11 Mar 2009 14:54:18 +0000 Subject: This patch contains some significant updates to the i82801gx component and will be required for a series of later patches. Roughly it contains: * fixed SMBus driver (was not compiled in before) * fixed S-ATA/P-ATA combination * Added warnings to drivers being called with a NULL dev->chip_info * Set subsystem ids for those boards that have none specified in Options.lb * Fix license headers. The code was originally released under GPL v2 but some files sneaked in with a v2 or later header. * some attempts to fix azalia/Intel HDA.. not working yet * clean up and fix pci bridge handling code * Add Config based GPI handling to LPC driver * Add HPET enable function * Enable clock gating where appropriate * first attempt at USB debug console support (not working yet) * Add required options to kontron board * many other minor changes Signed-off-by: Stefan Reinauer Acked-by: Myles Watson git-svn-id: svn://svn.coreboot.org/coreboot/trunk@3991 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1 --- src/southbridge/intel/i82801gx/i82801gx_azalia.c | 226 ++++++++++++++++------- 1 file changed, 158 insertions(+), 68 deletions(-) (limited to 'src/southbridge/intel/i82801gx/i82801gx_azalia.c') diff --git a/src/southbridge/intel/i82801gx/i82801gx_azalia.c b/src/southbridge/intel/i82801gx/i82801gx_azalia.c index 067d0f3980..fba46bae7c 100644 --- a/src/southbridge/intel/i82801gx/i82801gx_azalia.c +++ b/src/southbridge/intel/i82801gx/i82801gx_azalia.c @@ -31,61 +31,63 @@ #define HDA_ICII_BUSY (1 << 0) #define HDA_ICII_VALID (1 << 1) +typedef struct southbridge_intel_i82801gx_config config_t; + static int set_bits(u8 * port, u32 mask, u32 val) { - u32 dword; + u32 reg32; int count; + /* Write (val & mask) to port */ val &= mask; - dword = readl(port); - dword &= ~mask; - dword |= val; - writel(dword, port); - + reg32 = readl(port); + reg32 &= ~mask; + reg32 |= val; + writel(reg32, port); + + /* Wait for readback of register to + * match what was just written to it + */ count = 50; do { - dword = readl(port); - dword &= mask; - udelay(100); - } while ((dword != val) && --count); + /* Wait 1ms based on BKDG wait time */ + mdelay(1); + reg32 = readl(port); + reg32 &= mask; + } while ((reg32 != val) && --count); + /* Timeout occured */ if (!count) return -1; - - udelay(540); return 0; } static int codec_detect(u8 * base) { - u32 dword; - - /* 1 */ - set_bits(base + 0x08, 1, 1); - - /* 2 */ - dword = readl(base + 0x0e); - dword |= 7; - writel(dword, base + 0x0e); - - /* 3 */ + u32 reg32; + + /* Set Bit0 to 0 to enter reset state (BAR + 0x8)[0] */ + if (set_bits(base + 0x08, 1, 0) == -1) + goto no_codec; + + /* Set Bit 0 to 1 to exit reset state (BAR + 0x8)[0] */ + if (set_bits(base + 0x08, 1, 1) == -1) + goto no_codec; + + /* Read in Codec location (BAR + 0xe)[2..0]*/ + reg32 = readl(base + 0xe); + reg32 &= 0x0f; + if (!reg32) + goto no_codec; + + return reg32; + +no_codec: + /* Codec Not found */ + /* Put HDA back in reset (BAR + 0x8) [0] */ set_bits(base + 0x08, 1, 0); - - /* 4 */ - set_bits(base + 0x08, 1, 1); - - /* 5 */ - dword = readl(base + 0xe); - dword &= 7; - - /* 6 */ - if (!dword) { - set_bits(base + 0x08, 1, 0); - printk_debug("No codec!\n"); - return 0; - } - return dword; - + printk_debug("Azalia: No codec!\n"); + return 0; } static u32 cim_verb_data[] = { @@ -156,19 +158,24 @@ static u32 cim_verb_data[] = { 0x01F71F41, }; -static unsigned find_verb(u32 viddid, u32 ** verb) +static unsigned find_verb(struct device *dev, u32 viddid, u32 ** verb) { - device_t azalia_dev = dev_find_slot(0, PCI_DEVFN(0x14, 2)); - struct southbridge_intel_i82801gx_config *cfg = - (struct southbridge_intel_i82801gx_config *)azalia_dev->chip_info; - printk_debug("Dev=%s\n", dev_path(azalia_dev)); - printk_debug("Default viddid=%x\n", cfg->hda_viddid); - printk_debug("Reading viddid=%x\n", viddid); - if (!cfg) + config_t *config = dev->chip_info; + + if (config == NULL) { + printk_err("\ni82801gx_azalia: Not mentioned in mainboard's Config.lb!\n"); return 0; - if (viddid != cfg->hda_viddid) + } + + printk_debug("Azalia: dev=%s\n", dev_path(dev)); + printk_debug("Azalia: Default viddid=%x\n", (u32)config->hda_viddid); + printk_debug("Azalia: Reading viddid=%x\n", viddid); + + if (viddid != config->hda_viddid) return 0; + *verb = (u32 *) cim_verb_data; + return sizeof(cim_verb_data) / sizeof(u32); } @@ -185,8 +192,8 @@ static int wait_for_ready(u8 *base) int timeout = 50; while(timeout--) { - u32 dword=readl(base + HDA_ICII_REG); - if (!(dword & HDA_ICII_BUSY)) + u32 reg32 = readl(base + HDA_ICII_REG); + if (!(reg32 & HDA_ICII_BUSY)) return 0; udelay(1); } @@ -207,8 +214,8 @@ static int wait_for_valid(u8 *base) int timeout = 50; while(timeout--) { - u32 dword = readl(base + HDA_ICII_REG); - if ((dword & (HDA_ICII_VALID | HDA_ICII_BUSY)) == + u32 reg32 = readl(base + HDA_ICII_REG); + if ((reg32 & (HDA_ICII_VALID | HDA_ICII_BUSY)) == HDA_ICII_VALID) return 0; udelay(1); @@ -217,9 +224,9 @@ static int wait_for_valid(u8 *base) return 1; } -static void codec_init(u8 * base, int addr) +static void codec_init(struct device *dev, u8 * base, int addr) { - u32 dword; + u32 reg32; u32 *verb; u32 verb_size; int i; @@ -228,24 +235,24 @@ static void codec_init(u8 * base, int addr) if (wait_for_ready(base) == -1) return; - dword = (addr << 28) | 0x000f0000; - writel(dword, base + 0x60); + reg32 = (addr << 28) | 0x000f0000; + writel(reg32, base + 0x60); if (wait_for_valid(base) == -1) return; - dword = readl(base + 0x64); + reg32 = readl(base + 0x64); /* 2 */ - printk_debug("codec viddid: %08x\n", dword); - verb_size = find_verb(dword, &verb); + printk_debug("Azalia: codec viddid: %08x\n", reg32); + verb_size = find_verb(dev, reg32, &verb); if (!verb_size) { - printk_debug("No verb!\n"); + printk_debug("Azalia: No verb!\n"); return; } - printk_debug("verb_size: %d\n", verb_size); + printk_debug("Azalia: verb_size: %d\n", verb_size); /* 3 */ for (i = 0; i < verb_size; i++) { if (wait_for_ready(base) == -1) @@ -256,15 +263,15 @@ static void codec_init(u8 * base, int addr) if (wait_for_valid(base) == -1) return; } - printk_debug("verb loaded!\n"); + printk_debug("Azalia: verb loaded.\n"); } -static void codecs_init(u8 * base, u32 codec_mask) +static void codecs_init(struct device *dev, u8 * base, u32 codec_mask) { int i; for (i = 2; i >= 0; i--) { if (codec_mask & (1 << i)) - codec_init(base, i); + codec_init(dev, base, i); } } @@ -273,7 +280,71 @@ static void azalia_init(struct device *dev) u8 *base; struct resource *res; u32 codec_mask; - + u8 reg8; + u32 reg32; + +#if MMCONF_SUPPORT + // ESD + reg32 = pci_mmio_read_config32(dev, 0x134); + reg32 &= 0xff00ffff; + reg32 |= (2 << 16); + pci_mmio_write_config32(dev, 0x134, reg32); + + // Link1 description + reg32 = pci_mmio_read_config32(dev, 0x140); + reg32 &= 0xff00ffff; + reg32 |= (2 << 16); + pci_mmio_write_config32(dev, 0x140, reg32); + + // Port VC0 Resource Control Register + reg32 = pci_mmio_read_config32(dev, 0x114); + reg32 &= 0xffffff00; + reg32 |= 1; + pci_mmio_write_config32(dev, 0x114, reg32); + + // VCi traffic class + reg8 = pci_mmio_read_config8(dev, 0x44); + reg8 |= (7 << 0); // TC7 + pci_mmio_write_config8(dev, 0x44, reg8); + + // VCi Resource Control + reg32 = pci_mmio_read_config32(dev, 0x120); + reg32 |= (1 << 31); + reg32 |= (1 << 24); // VCi ID + reg32 |= (0x80 << 0); // VCi map + pci_mmio_write_config32(dev, 0x120, reg32); +#else +#error ICH7 Azalia required MMCONF_SUPPORT +#endif + + /* Set Bus Master */ + reg32 = pci_read_config32(dev, PCI_COMMAND); + pci_write_config32(dev, PCI_COMMAND, reg32 | PCI_COMMAND_MASTER); + + pci_write_config8(dev, 0x3c, 0x0a); // unused? + + // TODO Actually check if we're AC97 or HDA instead of hardcoding this + // here, in Config.lb and/or auto.c. + reg8 = pci_read_config8(dev, 0x40); + reg8 |= (1 << 3); // Clear Clock Detect Bit + pci_write_config8(dev, 0x40, reg8); + reg8 &= ~(1 << 3); // Keep CLKDETCLR from clearing the bit over and over + pci_write_config8(dev, 0x40, reg8); + reg8 |= (1 << 2); // Enable clock detection + pci_write_config8(dev, 0x40, reg8); + mdelay(1); + reg8 = pci_read_config8(dev, 0x40); + printk_debug("Azalia: codec type: %s\n", (reg8 & (1 << 1))?"Azalia":"AC97"); + + // + reg8 = pci_read_config8(dev, 0x40); // Audio Control + reg8 |= 1; // Select Azalia mode. This needs to be controlled via Config.lb + pci_write_config8(dev, 0x40, reg8); + + reg8 = pci_read_config8(dev, 0x4d); // Docking Status + reg8 &= ~(1 << 7); // Docking not supported + pci_write_config8(dev, 0x4d, reg8); +#if 0 /* Set routing pin */ pci_write_config32(dev, 0xf8, 0x0); pci_write_config8(dev, 0xfc, 0xAA); @@ -283,21 +354,39 @@ static void azalia_init(struct device *dev) /* Enable azalia, disable ac97 */ // pm_iowrite(0x59, 0xB); +#endif res = find_resource(dev, 0x10); if (!res) return; + // NOTE this will break as soon as the Azalia get's a bar above + // 4G. Is there anything we can do about it? base = (u8 *) ((u32)res->base); - printk_debug("base = %08x\n", base); + printk_debug("Azalia: base = %08x\n", (u32)base); codec_mask = codec_detect(base); if (codec_mask) { - printk_debug("codec_mask = %02x\n", codec_mask); - codecs_init(base, codec_mask); + printk_debug("Azalia: codec_mask = %02x\n", codec_mask); + codecs_init(dev, base, codec_mask); } } +static void azalia_set_subsystem(device_t dev, unsigned vendor, unsigned device) +{ + if (!vendor || !device) { + pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID, + pci_read_config32(dev, PCI_VENDOR_ID)); + } else { + pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID, + ((device & 0xffff) << 16) | (vendor & 0xffff)); + } +} + +static struct pci_operations azalia_pci_ops = { + .set_subsystem = azalia_set_subsystem, +}; + static struct device_operations azalia_ops = { .read_resources = pci_dev_read_resources, .set_resources = pci_dev_set_resources, @@ -305,6 +394,7 @@ static struct device_operations azalia_ops = { .init = azalia_init, .scan_bus = 0, .enable = i82801gx_enable, + .ops_pci = &azalia_pci_ops, }; /* 82801GB/GR/GDH/GBM/GHM (ICH7/ICH7R/ICH7DH/ICH7-M/ICH7-M DH) */ -- cgit v1.2.3