This patch implements MBI (modular bios interface) support to the i830 chipset.
This is needed on the IP1000T to get VGA output. The VGA option rom will ask
through an SMI for hardware specifics (in form of a VBT, video bios table)
which the SMI handler copies into the VGA option rom. 

Signed-off-by: Stefan Reinauer <stepan@coresystems.de>
Acked-by: Ronald G. Minnich <rminnich@gmail.com>



git-svn-id: svn://svn.coreboot.org/coreboot/trunk@5177 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
diff --git a/src/southbridge/intel/i82801dx/i82801dx_ac97.c b/src/southbridge/intel/i82801dx/i82801dx_ac97.c
index 7524498..10b100b 100644
--- a/src/southbridge/intel/i82801dx/i82801dx_ac97.c
+++ b/src/southbridge/intel/i82801dx/i82801dx_ac97.c
@@ -1,41 +1,285 @@
 /*
- * (C) 2003 Linux Networx
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2008-2009 coresystems GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of
+ * the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
+
 #include <console/console.h>
 #include <device/device.h>
 #include <device/pci.h>
 #include <device/pci_ids.h>
-#include <device/pci_ops.h>
+#include <arch/io.h>
+#include <delay.h>
 #include "i82801dx.h"
 
+#define NAMBAR		0x10
+#define   MASTER_VOL	0x02
+#define   PAGING	0x24
+#define   EXT_AUDIO	0x28
+#define   FUNC_SEL	0x66
+#define   INFO_IO	0x68
+#define   CONNECTOR	0x6a
+#define   VENDOR_ID1	0x7c
+#define   VENDOR_ID2	0x7e
+#define   SEC_VENDOR_ID1 0xfc
+#define   SEC_VENDOR_ID2 0xfe
 
-static struct device_operations ac97audio_ops  = {
+#define NABMBAR		0x14
+#define   GLOB_CNT	0x2c
+#define   GLOB_STA	0x30
+#define   CAS		0x34
+
+#define MMBAR		0x10
+#define   EXT_MODEM_ID1	0x3c
+#define   EXT_MODEM_ID2	0xbc
+
+#define MBAR		0x14
+#define   SEC_CODEC	0x40
+
+
+/* FIXME. This table is probably mainboard specific */
+static u16 ac97_function[16*2][4] = {
+	{ (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+	{ (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+	{ (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+	{ (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+	{ (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+	{ (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+	{ (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+	{ (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+	{ (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+	{ (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+	{ (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+	{ (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+	{ (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+	{ (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+	{ (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+	{ (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+	{ (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+	{ (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+	{ (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+	{ (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+	{ (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+	{ (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+	{ (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+	{ (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+	{ (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+	{ (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+	{ (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+	{ (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+	{ (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+	{ (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+	{ (1 << 5), (2 << 11), (1 << 10), (3 << 13) },
+	{ (1 << 5), (2 << 11), (1 << 10), (3 << 13) }
+};
+
+static u16 nabmbar;
+static u16 nambar;
+
+static int ac97_semaphore(void)
+{
+	int timeout;
+	u8 reg8;
+
+	timeout = 0xffff;
+	do {
+		reg8 = inb(nabmbar + CAS);
+		timeout--;
+	} while ((reg8 & 1) && timeout);
+	if (! timeout) {
+		printk_debug("Timeout!\n");
+	}
+
+	return (!timeout);
+}
+
+static void init_cnr(void)
+{
+	// TODO
+}
+
+static void program_sigid(struct device *dev, u32 id)
+{
+	pci_write_config32(dev, 0x2c, id);
+}
+
+static void ac97_audio_init(struct device *dev)
+{
+	u16 reg16;
+	u32 reg32;
+	int i;
+
+	printk_debug("Initializing AC'97 Audio.\n");
+
+	/* top 16 bits are zero, so don't read them */
+	nabmbar = pci_read_config16(dev, NABMBAR) & 0xfffe;
+	nambar = pci_read_config16(dev, NAMBAR) & 0xfffe;
+
+	reg16 = inw(nabmbar + GLOB_CNT);
+	reg16 |= (1 << 1); /* Remove AC_RESET# */
+	outw(reg16, nabmbar + GLOB_CNT);
+
+	/* Wait 600ms. Ouch. */
+	udelay(600 * 1000);
+
+	init_cnr();
+
+	/* Detect Primary AC'97 Codec */
+	reg32 = inl(nabmbar + GLOB_STA);
+	if ((reg32 & ((1 << 28) | (1 << 9) | (1 << 8))) == 0) {
+		/* Primary Codec not found */
+		printk_debug("No primary codec. Disabling AC'97 Audio.\n");
+		return;
+	}
+
+	ac97_semaphore();
+
+	/* Detect if codec is programmable */
+	outw(0x8000, nambar + MASTER_VOL);
+	ac97_semaphore();
+	if (inw(nambar + MASTER_VOL) != 0x8000) {
+		printk_debug("Codec not programmable. Disabling AC'97 Audio.\n");
+		return;
+	}
+
+	/* Program Vendor IDs */
+	reg32 = inw(nambar + VENDOR_ID1);
+	reg32 <<= 16;
+	reg32 |= (u16)inw(nambar + VENDOR_ID2);
+
+	program_sigid(dev, reg32);
+
+	/* Is Codec AC'97 2.3 compliant? */
+	reg16 = inw(nambar + EXT_AUDIO);
+	/* [11:10] = 10b -> AC'97 2.3 */
+	if ((reg16 & 0x0c00) != 0x0800) {
+		/* No 2.3 Codec. We're done */
+		return;
+	}
+
+	/* Select Page 1 */
+	reg16 = inw(nambar + PAGING);
+	reg16 &= 0xfff0;
+	reg16 |= 0x0001;
+	outw(reg16, nambar + PAGING);
+
+	for (i = 0x0a * 2; i > 0; i--) {
+		outw(i, nambar + FUNC_SEL);
+
+		/* Function could not be selected. Next one */
+		if (inw(nambar + FUNC_SEL) != i)
+			continue;
+
+		reg16 = inw(nambar + INFO_IO);
+
+		/* Function Information present? */
+		if (!(reg16 & (1 << 0)))
+			continue;
+
+		/* Function Information valid? */
+		if (!(reg16 & (1 << 4)))
+			continue;
+
+		/* Program Buffer Delay [9:5] */
+		reg16 &= 0x03e0;
+		reg16 |= ac97_function[i][0];
+
+		/* Program Gain [15:11] */
+		reg16 |= ac97_function[i][1];
+
+		/* Program Inversion [10] */
+		reg16 |= ac97_function[i][2];
+
+		outw(reg16, nambar + INFO_IO);
+
+		/* Program Connector / Jack Location */
+		reg16 = inw(nambar + CONNECTOR);
+		reg16 &= 0x1fff;
+		reg16 |= ac97_function[i][3];
+		outw(reg16, nambar + CONNECTOR);
+	}
+}
+
+static void ac97_modem_init(struct device *dev)
+{
+	u16 reg16;
+	u32 reg32;
+	u16 mmbar, mbar;
+
+	mmbar = pci_read_config16(dev, MMBAR) & 0xfffe;
+	mbar = pci_read_config16(dev, MBAR) & 0xfffe;
+
+	reg16 = inw(mmbar + EXT_MODEM_ID1);
+	if ((reg16 & 0xc000) != 0xc000 ) {
+		if (reg16 & (1 << 0)) {
+			reg32 = inw(mmbar + VENDOR_ID2);
+			reg32 <<= 16;
+			reg32 |= (u16)inw(mmbar + VENDOR_ID1);
+			program_sigid(dev, reg32);
+			return;
+		}
+	}
+
+	/* Secondary codec? */
+	reg16 = inw(mbar + SEC_CODEC);
+	if ((reg16 & (1 << 9)) == 0)
+		return;
+
+	reg16 = inw(mmbar + EXT_MODEM_ID2);
+	if ((reg16 & 0xc000) == 0x4000) {
+		if (reg16 & (1 << 0)) {
+			reg32 = inw(mmbar + SEC_VENDOR_ID2);
+			reg32 <<= 16;
+			reg32 |= (u16)inw(mmbar + SEC_VENDOR_ID1);
+			program_sigid(dev, reg32);
+			return;
+		}
+	}
+}
+
+static struct device_operations ac97_audio_ops  = {
 	.read_resources   = pci_dev_read_resources,
 	.set_resources    = pci_dev_set_resources,
 	.enable_resources = pci_dev_enable_resources,
 	.enable           = i82801dx_enable,
-	.init             = 0,
+	.init             = ac97_audio_init,
 	.scan_bus         = 0,
 };
 
-static const struct pci_driver ac97audio_driver __pci_driver = {
-	.ops    = &ac97audio_ops,
-	.vendor = PCI_VENDOR_ID_INTEL,
-	.device = PCI_DEVICE_ID_INTEL_82801DBM_AC97_AUDIO,
-};
-
-
-static struct device_operations ac97modem_ops  = {
+static struct device_operations ac97_modem_ops  = {
 	.read_resources   = pci_dev_read_resources,
 	.set_resources    = pci_dev_set_resources,
 	.enable_resources = pci_dev_enable_resources,
 	.enable           = i82801dx_enable,
-	.init             = 0,
+	.init             = ac97_modem_init,
 	.scan_bus         = 0,
 };
 
-static const struct pci_driver ac97modem_driver __pci_driver = {
-	.ops    = &ac97modem_ops,
-	.vendor = PCI_VENDOR_ID_INTEL,
-	.device = PCI_DEVICE_ID_INTEL_82801DBM_AC97_MODEM,
+/* 82801DB/DBL/DBM (ICH4/ICH4-L/ICH4-M) */
+static const struct pci_driver i82801db_ac97_audio __pci_driver = {
+	.ops	= &ac97_audio_ops,
+	.vendor	= PCI_VENDOR_ID_INTEL,
+	.device	= PCI_DEVICE_ID_INTEL_82801DB_AC97_AUDIO,
 };
+
+static const struct pci_driver i82801db_ac97_modem __pci_driver = {
+	.ops	= &ac97_modem_ops,
+	.vendor	= PCI_VENDOR_ID_INTEL,
+	.device	= PCI_DEVICE_ID_INTEL_82801DB_AC97_MODEM,
+};
+
+