[coreboot-gerrit] New patch to review for coreboot: 7fddce3 lynxpoint: Basic configuration of SerialIO devices

Stefan Reinauer (stefan.reinauer@coreboot.org) gerrit at coreboot.org
Fri Mar 29 22:10:55 CET 2013


Stefan Reinauer (stefan.reinauer at coreboot.org) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/2971

-gerrit

commit 7fddce33641774903053fe5db7617a3300b46eb4
Author: Duncan Laurie <dlaurie at chromium.org>
Date:   Fri Mar 22 11:21:14 2013 -0700

    lynxpoint: Basic configuration of SerialIO devices
    
    This adds configuration of SerialIO devices in the Lynxpoint-LP
    chipset.  This includes DMA, I2C, SPI, UART, and SDIO controllers.
    
    There is assorted magic setup necessary for the devices and
    while it is similar for each device there are subtle differences
    in some register settings.
    
    These devices must be put into "ACPI Mode" in order to take
    advantage of S0ix.  When in ACPI mode the allocated PCI BARs
    must be passed to ACPI so it can be relayed to the OS.  When
    the devices are in ACPI mode BAR0+BAR1 is saved into ACPI NVS
    and then updated and returned when the OS calls _CRS.
    
    Note that is is not entirely complete yet.  We need to update
    the IASL compiler in our build environment to support ACPI 5.0
    in order to be able to pass the FixedDMA entries to the kernel.
    There are also no ACPI methods defined yet to do D0->D3->D0
    transitions for actually entering/exiting S0ix states.
    
    This is hard to test right now because our kernel does not support
    any of these devices in ACPI mode.  I was able to build and test
    the upstream bleeding-edge branch of the linux-pm git tree.  With
    that tree I was able to enumerate and load the driver for the
    DesignWare I2C driver and attempt to probe the I2C bus -- although
    there are no devices attatched.
    
    I am also able to see the resources from ACPI in /proc/iomem get
    reserved properly in the kernel.
    
    Change-Id: Ie311addd6a25f3b7edf3388fe68c1cd691a0a500
    Signed-off-by: Duncan Laurie <dlaurie at chromium.org>
---
 src/southbridge/intel/lynxpoint/Makefile.inc      |   1 +
 src/southbridge/intel/lynxpoint/acpi/pch.asl      |   5 +
 src/southbridge/intel/lynxpoint/acpi/serialio.asl | 470 ++++++++++++++++++++++
 src/southbridge/intel/lynxpoint/chip.h            |   7 +
 src/southbridge/intel/lynxpoint/pch.h             |  31 ++
 src/southbridge/intel/lynxpoint/serialio.c        | 267 ++++++++++++
 6 files changed, 781 insertions(+)

diff --git a/src/southbridge/intel/lynxpoint/Makefile.inc b/src/southbridge/intel/lynxpoint/Makefile.inc
index 04fa2f8..7d1f894 100644
--- a/src/southbridge/intel/lynxpoint/Makefile.inc
+++ b/src/southbridge/intel/lynxpoint/Makefile.inc
@@ -32,6 +32,7 @@ ramstage-y += sata.c
 ramstage-y += usb_ehci.c
 ramstage-y += me_9.x.c
 ramstage-y += smbus.c
+ramstage-$(CONFIG_INTEL_LYNXPOINT_LP) += serialio.c
 
 ramstage-y += rcba.c
 ramstage-y += me_status.c
diff --git a/src/southbridge/intel/lynxpoint/acpi/pch.asl b/src/southbridge/intel/lynxpoint/acpi/pch.asl
index ce8f0e0..f5cf6ae 100644
--- a/src/southbridge/intel/lynxpoint/acpi/pch.asl
+++ b/src/southbridge/intel/lynxpoint/acpi/pch.asl
@@ -267,6 +267,11 @@ Scope(\)
 // SMBus 0:1f.3
 #include "smbus.asl"
 
+// Serial IO
+#if CONFIG_INTEL_LYNXPOINT_LP
+#include "serialio.asl"
+#endif
+
 Method (_OSC, 4)
 {
 	/* Check for proper GUID */
diff --git a/src/southbridge/intel/lynxpoint/acpi/serialio.asl b/src/southbridge/intel/lynxpoint/acpi/serialio.asl
new file mode 100644
index 0000000..6ea23c6
--- /dev/null
+++ b/src/southbridge/intel/lynxpoint/acpi/serialio.asl
@@ -0,0 +1,470 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2013 Google Inc.
+ *
+ * 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
+ */
+
+// Intel LynxPoint Serial IO Devices in ACPI Mode
+
+// Serial IO Device BAR0 and BAR1 is 4KB
+#define SIO_BAR_LEN 0x1000
+
+// Serial IO Resource Consumption for BAR1
+Device (SIOR)
+{
+	Name (_HID, EISAID("PNP0C02"))
+	Name (_UID, 4)
+
+	Name (RBUF, ResourceTemplate()
+	{
+		// Serial IO BAR1 (PCI config space) resources
+		Memory32Fixed (ReadWrite, 0x00000000, 0x00000000, B1D0) // SDMA
+		Memory32Fixed (ReadWrite, 0x00000000, 0x00000000, B1D1) // I2C0
+		Memory32Fixed (ReadWrite, 0x00000000, 0x00000000, B1D2) // I2C1
+		Memory32Fixed (ReadWrite, 0x00000000, 0x00000000, B1D3) // SPI0
+		Memory32Fixed (ReadWrite, 0x00000000, 0x00000000, B1D4) // SPI1
+		Memory32Fixed (ReadWrite, 0x00000000, 0x00000000, B1D5) // UART0
+		Memory32Fixed (ReadWrite, 0x00000000, 0x00000000, B1D6) // UART1
+		Memory32Fixed (ReadWrite, 0x00000000, 0x00000000, B1D7) // SDIO
+	})
+
+	// Update BAR1 address and length if set in NVS
+	Method (_CRS, 0, NotSerialized)
+	{
+		// SDMA
+		If (LNotEqual (\S0B1, Zero)) {
+			CreateDwordField (^RBUF, ^B1D0._BAS, B0AD)
+			CreateDwordField (^RBUF, ^B1D0._LEN, B0LN)
+			Store (\S0B1, B0AD)
+			Store (SIO_BAR_LEN, B0LN)
+		}
+
+		// I2C0
+		If (LNotEqual (\S1B1, Zero)) {
+			CreateDwordField (^RBUF, ^B1D1._BAS, B1AD)
+			CreateDwordField (^RBUF, ^B1D1._LEN, B1LN)
+			Store (\S1B1, B1AD)
+			Store (SIO_BAR_LEN, B1LN)
+		}
+
+		// I2C1
+		If (LNotEqual (\S2B1, Zero)) {
+			CreateDwordField (^RBUF, ^B1D2._BAS, B2AD)
+			CreateDwordField (^RBUF, ^B1D2._LEN, B2LN)
+			Store (\S2B1, B2AD)
+			Store (SIO_BAR_LEN, B2LN)
+		}
+
+		// SPI0
+		If (LNotEqual (\S3B1, Zero)) {
+			CreateDwordField (^RBUF, ^B1D3._BAS, B3AD)
+			CreateDwordField (^RBUF, ^B1D3._LEN, B3LN)
+			Store (\S3B1, B3AD)
+			Store (SIO_BAR_LEN, B3LN)
+		}
+
+		// SPI1
+		If (LNotEqual (\S4B1, Zero)) {
+			CreateDwordField (^RBUF, ^B1D4._BAS, B4AD)
+			CreateDwordField (^RBUF, ^B1D4._LEN, B4LN)
+			Store (\S4B1, B4AD)
+			Store (SIO_BAR_LEN, B4LN)
+		}
+
+		// UART0
+		If (LNotEqual (\S5B1, Zero)) {
+			CreateDwordField (^RBUF, ^B1D5._BAS, B5AD)
+			CreateDwordField (^RBUF, ^B1D5._LEN, B5LN)
+			Store (\S5B1, B5AD)
+			Store (SIO_BAR_LEN, B5LN)
+		}
+
+		// UART1
+		If (LNotEqual (\S6B1, Zero)) {
+			CreateDwordField (^RBUF, ^B1D6._BAS, B6AD)
+			CreateDwordField (^RBUF, ^B1D6._LEN, B6LN)
+			Store (\S6B1, B6AD)
+			Store (SIO_BAR_LEN, B6LN)
+		}
+
+		// SDIO
+		If (LNotEqual (\S7B1, Zero)) {
+			CreateDwordField (^RBUF, ^B1D7._BAS, B7AD)
+			CreateDwordField (^RBUF, ^B1D7._LEN, B7LN)
+			Store (\S7B1, B7AD)
+			Store (SIO_BAR_LEN, B7LN)
+		}
+
+		Return (RBUF)
+	}
+}
+
+Device (SDMA)
+{
+	// Serial IO DMA Controller
+	Name (_HID, "INTL9C60")
+	Name (_UID, 1)
+	Name (_ADR, 0x00150000)
+
+	// BAR0 is assigned during PCI enumeration and saved into NVS
+	Name (RBUF, ResourceTemplate ()
+	{
+		Memory32Fixed (ReadWrite, 0x00000000, 0x00000000, BAR0)
+		Interrupt (ResourceConsumer, Level, ActiveLow, Shared, , , ) {7}
+	})
+
+	Method (_CRS, 0, NotSerialized)
+	{
+		// Update BAR0 address and length if set in NVS
+		If (LNotEqual (\S0B0, Zero)) {
+			CreateDwordField (^RBUF, ^BAR0._BAS, B0AD)
+			CreateDwordField (^RBUF, ^BAR0._LEN, B0LN)
+			Store (\S0B0, B0AD)
+			Store (SIO_BAR_LEN, B0LN)
+		}
+
+		Return (RBUF)
+	}
+
+	Method (_STA, 0, NotSerialized)
+	{
+		If (LEqual (\S0B0, 0)) {
+			Return (0x0)
+		} Else {
+			Return (0xF)
+		}
+	}
+}
+
+Device (I2C0)
+{
+	// Serial IO I2C0 Controller
+	Name (_HID, "INT33C2")
+	Name (_CID, "INT33C2")
+	Name (_UID, 1)
+	Name (_ADR, 0x00150001)
+
+	// BAR0 is assigned during PCI enumeration and saved into NVS
+	Name (RBUF, ResourceTemplate ()
+	{
+		Memory32Fixed (ReadWrite, 0x00000000, 0x00000000, BAR0)
+		Interrupt (ResourceConsumer, Level, ActiveLow, Shared, , , ) {7}
+	})
+
+	// DMA channels are only used if Serial IO DMA controller is enabled
+	Name (DBUF, ResourceTemplate ()
+	{
+		// TODO: Need to update IASL to support FixedDMA
+		//FixedDMA (0x18, 4, Width32Bit, DMA1) // Tx
+		//FixedDMA (0x19, 5, Width32Bit, DMA2) // Rx
+	})
+
+	Method (_CRS, 0, NotSerialized)
+	{
+		// Update BAR0 address and length if set in NVS
+		If (LNotEqual (\S1B0, Zero)) {
+			CreateDwordField (^RBUF, ^BAR0._BAS, B0AD)
+			CreateDwordField (^RBUF, ^BAR0._LEN, B0LN)
+			Store (\S1B0, B0AD)
+			Store (SIO_BAR_LEN, B0LN)
+		}
+
+		// Check if Serial IO DMA Controller is enabled
+		If (LNotEqual (\_SB.PCI0.SDMA._STA, Zero)) {
+			Return (ConcatenateResTemplate (RBUF, DBUF))
+		} Else {
+			Return (RBUF)
+		}
+	}
+
+	Method (_STA, 0, NotSerialized)
+	{
+		If (LEqual (\S1B0, 0)) {
+			Return (0x0)
+		} Else {
+			Return (0xF)
+		}
+	}
+}
+
+Device (I2C1)
+{
+	// Serial IO I2C1 Controller
+	Name (_HID, "INT33C3")
+	Name (_CID, "INT33C3")
+	Name (_UID, 1)
+	Name (_ADR, 0x00150002)
+
+	// BAR0 is assigned during PCI enumeration and saved into NVS
+	Name (RBUF, ResourceTemplate ()
+	{
+		Memory32Fixed (ReadWrite, 0x00000000, 0x00000000, BAR0)
+		Interrupt (ResourceConsumer, Level, ActiveLow, Shared, , , ) {7}
+	})
+
+	// DMA channels are only used if Serial IO DMA controller is enabled
+	Name (DBUF, ResourceTemplate ()
+	{
+		// TODO: Need to update IASL to support FixedDMA
+		//FixedDMA (0x1A, 6, Width32Bit, DMA1) // Tx
+		//FixedDMA (0x1B, 7, Width32Bit, DMA2) // Rx
+	})
+
+	Method (_CRS, 0, NotSerialized)
+	{
+		// Update BAR0 address and length if set in NVS
+		If (LNotEqual (\S2B0, Zero)) {
+			CreateDwordField (^RBUF, ^BAR0._BAS, B0AD)
+			CreateDwordField (^RBUF, ^BAR0._LEN, B0LN)
+			Store (\S2B0, B0AD)
+			Store (SIO_BAR_LEN, B0LN)
+		}
+
+		// Check if Serial IO DMA Controller is enabled
+		If (LNotEqual (\_SB.PCI0.SDMA._STA, Zero)) {
+			Return (ConcatenateResTemplate (RBUF, DBUF))
+		} Else {
+			Return (RBUF)
+		}
+	}
+
+	Method (_STA, 0, NotSerialized)
+	{
+		If (LEqual (\S2B0, 0)) {
+			Return (0x0)
+		} Else {
+			Return (0xF)
+		}
+	}
+}
+
+Device (SPI0)
+{
+	// Serial IO SPI0 Controller
+	Name (_HID, "INT33C0")
+	Name (_CID, "INT33C0")
+	Name (_UID, 1)
+	Name (_ADR, 0x00150003)
+
+	// BAR0 is assigned during PCI enumeration and saved into NVS
+	Name (RBUF, ResourceTemplate ()
+	{
+		Memory32Fixed (ReadWrite, 0x00000000, 0x00000000, BAR0)
+		Interrupt (ResourceConsumer, Level, ActiveLow, Shared, , , ) {7}
+	})
+
+	Method (_CRS, 0, NotSerialized)
+	{
+		// Update BAR0 address and length if set in NVS
+		If (LNotEqual (\S3B0, Zero)) {
+			CreateDwordField (^RBUF, ^BAR0._BAS, B0AD)
+			CreateDwordField (^RBUF, ^BAR0._LEN, B0LN)
+			Store (\S3B0, B0AD)
+			Store (SIO_BAR_LEN, B0LN)
+		}
+
+		Return (RBUF)
+	}
+
+	Method (_STA, 0, NotSerialized)
+	{
+		If (LEqual (\S3B0, 0)) {
+			Return (0x0)
+		} Else {
+			Return (0xF)
+		}
+	}
+}
+
+Device (SPI1)
+{
+	// Serial IO SPI1 Controller
+	Name (_HID, "INT33C1")
+	Name (_CID, "INT33C1")
+	Name (_UID, 1)
+	Name (_ADR, 0x00150004)
+
+	// BAR0 is assigned during PCI enumeration and saved into NVS
+	Name (RBUF, ResourceTemplate ()
+	{
+		Memory32Fixed (ReadWrite, 0x00000000, 0x00000000, BAR0)
+		Interrupt (ResourceConsumer, Level, ActiveLow, Shared, , , ) {7}
+	})
+
+	// DMA channels are only used if Serial IO DMA controller is enabled
+	Name (DBUF, ResourceTemplate ()
+	{
+		// TODO: Need to update IASL to support FixedDMA
+		//FixedDMA (0x10, 0, Width32Bit, DMA1) // Tx
+		//FixedDMA (0x11, 1, Width32Bit, DMA2) // Rx
+	})
+
+	Method (_CRS, 0, NotSerialized)
+	{
+		// Update BAR0 address and length if set in NVS
+		If (LNotEqual (\S4B0, Zero)) {
+			CreateDwordField (^RBUF, ^BAR0._BAS, B0AD)
+			CreateDwordField (^RBUF, ^BAR0._LEN, B0LN)
+			Store (\S4B0, B0AD)
+			Store (SIO_BAR_LEN, B0LN)
+		}
+
+		// Check if Serial IO DMA Controller is enabled
+		If (LNotEqual (\_SB.PCI0.SDMA._STA, Zero)) {
+			Return (ConcatenateResTemplate (RBUF, DBUF))
+		} Else {
+			Return (RBUF)
+		}
+	}
+
+	Method (_STA, 0, NotSerialized)
+	{
+		If (LEqual (\S4B0, 0)) {
+			Return (0x0)
+		} Else {
+			Return (0xF)
+		}
+	}
+}
+
+Device (UAR0)
+{
+	// Serial IO UART0 Controller
+	Name (_HID, "INT33C4")
+	Name (_CID, "INT33C4")
+	Name (_UID, 1)
+	Name (_ADR, 0x00150005)
+
+	// BAR0 is assigned during PCI enumeration and saved into NVS
+	Name (RBUF, ResourceTemplate ()
+	{
+		Memory32Fixed (ReadWrite, 0x00000000, 0x00000000, BAR0)
+		Interrupt (ResourceConsumer, Level, ActiveLow, Shared, , , ) {13}
+	})
+
+	// DMA channels are only used if Serial IO DMA controller is enabled
+	Name (DBUF, ResourceTemplate ()
+	{
+		// TODO: Need to update IASL to support FixedDMA
+		//FixedDMA (0x16, 2, Width32Bit, DMA1) // Tx
+		//FixedDMA (0x17, 3, Width32Bit, DMA2) // Rx
+	})
+
+	Method (_CRS, 0, NotSerialized)
+	{
+		// Update BAR0 address and length if set in NVS
+		If (LNotEqual (\S5B0, Zero)) {
+			CreateDwordField (^RBUF, ^BAR0._BAS, B0AD)
+			CreateDwordField (^RBUF, ^BAR0._LEN, B0LN)
+			Store (\S5B0, B0AD)
+			Store (SIO_BAR_LEN, B0LN)
+		}
+
+		// Check if Serial IO DMA Controller is enabled
+		If (LNotEqual (\_SB.PCI0.SDMA._STA, Zero)) {
+			Return (ConcatenateResTemplate (RBUF, DBUF))
+		} Else {
+			Return (RBUF)
+		}
+	}
+
+	Method (_STA, 0, NotSerialized)
+	{
+		If (LEqual (\S5B0, 0)) {
+			Return (0x0)
+		} Else {
+			Return (0xF)
+		}
+	}
+}
+
+Device (UAR1)
+{
+	// Serial IO UART1 Controller
+	Name (_HID, "INT33C5")
+	Name (_CID, "INT33C5")
+	Name (_UID, 1)
+	Name (_ADR, 0x00150006)
+
+	// BAR0 is assigned during PCI enumeration and saved into NVS
+	Name (RBUF, ResourceTemplate ()
+	{
+		Memory32Fixed (ReadWrite, 0x00000000, 0x00000000, BAR0)
+		Interrupt (ResourceConsumer, Level, ActiveLow, Shared, , , ) {13}
+	})
+
+	Method (_CRS, 0, NotSerialized)
+	{
+		// Update BAR0 address and length if set in NVS
+		If (LNotEqual (\S6B0, Zero)) {
+			CreateDwordField (^RBUF, ^BAR0._BAS, B0AD)
+			CreateDwordField (^RBUF, ^BAR0._LEN, B0LN)
+			Store (\S6B0, B0AD)
+			Store (SIO_BAR_LEN, B0LN)
+		}
+
+		Return (RBUF)
+	}
+
+	Method (_STA, 0, NotSerialized)
+	{
+		If (LEqual (\S6B0, 0)) {
+			Return (0x0)
+		} Else {
+			Return (0xF)
+		}
+	}
+}
+
+Device (SDIO)
+{
+	// Serial IO SDIO Controller
+	Name (_HID, "INT33C6")
+	Name (_CID, "PNP0D40")
+	Name (_UID, 1)
+	Name (_ADR, 0x00170000)
+
+	// BAR0 is assigned during PCI enumeration and saved into NVS
+	Name (RBUF, ResourceTemplate ()
+	{
+		Memory32Fixed (ReadWrite, 0x00000000, 0x00000000, BAR0)
+		Interrupt (ResourceConsumer, Level, ActiveLow, Shared, , , ) {5}
+	})
+
+	Method (_CRS, 0, NotSerialized)
+	{
+		// Update BAR0 address and length if set in NVS
+		If (LNotEqual (\S7B0, Zero)) {
+			CreateDwordField (^RBUF, ^BAR0._BAS, B0AD)
+			CreateDwordField (^RBUF, ^BAR0._LEN, B0LN)
+			Store (\S7B0, B0AD)
+			Store (SIO_BAR_LEN, B0LN)
+		}
+
+		Return (RBUF)
+	}
+
+	Method (_STA, 0, NotSerialized)
+	{
+		If (LEqual (\S7B0, 0)) {
+			Return (0x0)
+		} Else {
+			Return (0xF)
+		}
+	}
+}
diff --git a/src/southbridge/intel/lynxpoint/chip.h b/src/southbridge/intel/lynxpoint/chip.h
index 95bd087..70f3e63 100644
--- a/src/southbridge/intel/lynxpoint/chip.h
+++ b/src/southbridge/intel/lynxpoint/chip.h
@@ -85,6 +85,13 @@ struct southbridge_intel_lynxpoint_config {
 
 	/* Enable linear PCIe Root Port function numbers starting at zero */
 	uint8_t pcie_port_coalesce;
+
+	/* Serial IO configuration */
+	/* Put devices into ACPI mode instead of a PCI device */
+	uint8_t sio_acpi_mode;
+	/* I2C voltage select: 0=3.3V 1=1.8V */
+	uint8_t sio_i2c0_voltage;
+	uint8_t sio_i2c1_voltage;
 };
 
 extern struct chip_operations southbridge_intel_lynxpoint_ops;
diff --git a/src/southbridge/intel/lynxpoint/pch.h b/src/southbridge/intel/lynxpoint/pch.h
index ee2efd5..76672bc 100644
--- a/src/southbridge/intel/lynxpoint/pch.h
+++ b/src/southbridge/intel/lynxpoint/pch.h
@@ -341,11 +341,13 @@ unsigned get_gpios(const int *gpio_num_array);
 #define SIO_IOBP_PORTCTRL6	0xcb000260	/* SPI1 D21:F4 */
 #define SIO_IOBP_PORTCTRL7	0xcb000268	/* UART0 D21:F5 */
 #define SIO_IOBP_PORTCTRL8	0xcb000270	/* UART1 D21:F6 */
+#define SIO_IOBP_PORTCTRLX(x)	(0xcb000240 + ((x) * 8))
 /* PORTCTRL 2-8 have the same layout */
 #define  SIO_IOBP_PORTCTRL_ACPI_IRQ_EN		(1 << 21)
 #define  SIO_IOBP_PORTCTRL_PCI_CONF_DIS		(1 << 20)
 #define  SIO_IOBP_PORTCTRL_SNOOP_SELECT(x)	(((x) & 3) << 18)
 #define  SIO_IOBP_PORTCTRL_INT_PIN(x)		(((x) & 0xf) << 2)
+#define  SIO_IOBP_PORTCTRL_PM_CAP_PRSNT		(1 << 1)
 #define SIO_IOBP_FUNCDIS0	0xce00aa07	/* DMA D21:F0 */
 #define SIO_IOBP_FUNCDIS1	0xce00aa47	/* I2C0 D21:F1 */
 #define SIO_IOBP_FUNCDIS2	0xce00aa87	/* I2C1 D21:F2 */
@@ -356,6 +358,34 @@ unsigned get_gpios(const int *gpio_num_array);
 #define SIO_IOBP_FUNCDIS7	0xce00ae07	/* SDIO D23:F0 */
 #define  SIO_IOBP_FUNCDIS_DIS			(1 << 8)
 
+/* Serial IO Devices */
+#define SIO_ID_SDMA		0 /* D21:F0 */
+#define SIO_ID_I2C0		1 /* D21:F1 */
+#define SIO_ID_I2C1		2 /* D21:F2 */
+#define SIO_ID_SPI0		3 /* D21:F3 */
+#define SIO_ID_SPI1		4 /* D21:F4 */
+#define SIO_ID_UART0		5 /* D21:F5 */
+#define SIO_ID_UART1		6 /* D21:F6 */
+#define SIO_ID_SDIO		7 /* D23:F0 */
+
+#define SIO_REG_PPR_RST			0x804
+#define  SIO_REG_PPR_RST_ASSERT		 0x3
+#define SIO_REG_PPR_GEN			0x808
+#define  SIO_REG_PPR_GEN_LTR_MODE_MASK	 (1 << 2)
+#define  SIO_REG_PPR_GEN_VOLTAGE_MASK	 (1 << 3)
+#define  SIO_REG_PPR_GEN_VOLTAGE(x)	 ((x & 1) << 3)
+#define SIO_REG_AUTO_LTR		0x814
+
+#define SIO_REG_SDIO_PPR_GEN		0x1008
+#define SIO_REG_SDIO_PPR_SW_LTR		0x1010
+#define SIO_REG_SDIO_PPR_CMD12		0x3c
+#define  SIO_REG_SDIO_PPR_CMD12_B30	 (1 << 30)
+
+#define SIO_PIN_INTA 1 /* IRQ5 in ACPI mode */
+#define SIO_PIN_INTB 2 /* IRQ6 in ACPI mode */
+#define SIO_PIN_INTC 3 /* IRQ7 in ACPI mode */
+#define SIO_PIN_INTD 4 /* IRQ13 in ACPI mode */
+
 /* PCI Configuration Space (D31:F3): SMBus */
 #define PCH_SMBUS_DEV		PCI_DEV(0, 0x1f, 3)
 #define SMB_BASE		0x20
@@ -534,6 +564,7 @@ unsigned get_gpios(const int *gpio_num_array);
 #define D20IR		0x3160	/* 16bit */
 #define D21IR		0x3164	/* 16bit */
 #define D19IR		0x3168	/* 16bit */
+#define ACPIIRQEN	0x31e0	/* 32bit */
 #define OIC		0x31fe	/* 16bit */
 #define SOFT_RESET_CTRL 0x38f4
 #define SOFT_RESET_DATA 0x38f8
diff --git a/src/southbridge/intel/lynxpoint/serialio.c b/src/southbridge/intel/lynxpoint/serialio.c
new file mode 100644
index 0000000..11d6a36
--- /dev/null
+++ b/src/southbridge/intel/lynxpoint/serialio.c
@@ -0,0 +1,267 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright 2013 Google Inc.
+ *
+ * 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 <arch/io.h>
+#include <cbmem.h>
+#include <console/console.h>
+#include <device/device.h>
+#include <device/pci.h>
+#include <device/pciexp.h>
+#include <device/pci_ids.h>
+#include <stdlib.h>
+#include "pch.h"
+#include "nvs.h"
+
+/* Put Serial IO D21:F0-F6 device into desired mode. */
+static void serialio_d21_mode(int sio_index, int int_pin, int acpi_mode)
+{
+	u32 portctrl = SIO_IOBP_PORTCTRL_PM_CAP_PRSNT;
+
+	/* Snoop select 1. */
+	portctrl |= SIO_IOBP_PORTCTRL_SNOOP_SELECT(1);
+
+	/* Set interrupt pin. */
+	portctrl |= SIO_IOBP_PORTCTRL_INT_PIN(int_pin);
+
+	if (acpi_mode) {
+		/* Enable ACPI interrupt mode. */
+		portctrl |= SIO_IOBP_PORTCTRL_ACPI_IRQ_EN;
+
+		/* Disable PCI config space. */
+		portctrl |= SIO_IOBP_PORTCTRL_PCI_CONF_DIS;
+	}
+
+	pch_iobp_update(SIO_IOBP_PORTCTRLX(sio_index), 0, portctrl);
+}
+
+/* Put Serial IO D23:F0 device into desired mode. */
+static void serialio_d23_mode(int acpi_mode)
+{
+	u32 portctrl = 0;
+
+	/* Snoop select 1. */
+	pch_iobp_update(SIO_IOBP_PORTCTRL1, 0,
+			SIO_IOBP_PORTCTRL1_SNOOP_SELECT(1));
+
+	if (acpi_mode) {
+		/* Enable ACPI interrupt mode. */
+		portctrl |= SIO_IOBP_PORTCTRL0_ACPI_IRQ_EN;
+
+		/* Disable PCI config space. */
+		portctrl |= SIO_IOBP_PORTCTRL0_PCI_CONF_DIS;
+	}
+
+	pch_iobp_update(SIO_IOBP_PORTCTRL0, 0, portctrl);
+}
+
+/* Enable LTR Auto Mode for D21:F1-F6. */
+static void serialio_d21_ltr(struct resource *bar0)
+{
+	u32 reg;
+
+	/* 1. Program BAR0 + 808h[2] = 0b */
+	reg = read32(bar0->base + SIO_REG_PPR_GEN);
+	reg &= ~SIO_REG_PPR_GEN_LTR_MODE_MASK;
+	write32(bar0->base + SIO_REG_PPR_GEN, reg);
+
+	/* 2. Program BAR0 + 804h[1:0] = 00b */
+	reg = read32(bar0->base + SIO_REG_PPR_RST);
+	reg &= ~SIO_REG_PPR_RST_ASSERT;
+	write32(bar0->base + SIO_REG_PPR_RST, reg);
+
+	/* 3. Program BAR0 + 804h[1:0] = 11b */
+	reg = read32(bar0->base + SIO_REG_PPR_RST);
+	reg |= SIO_REG_PPR_RST_ASSERT;
+	write32(bar0->base + SIO_REG_PPR_RST, reg);
+
+	/* 4. Program BAR0 + 814h[31:0] = 00000000h */
+	write32(bar0->base + SIO_REG_AUTO_LTR, 0);
+}
+
+/* Enable LTR Auto Mode for D23:F0. */
+static void serialio_d23_ltr(struct resource *bar0)
+{
+	u32 reg;
+
+	/* Program BAR0 + 1008h[2] = 1b */
+	reg = read32(bar0->base + SIO_REG_SDIO_PPR_GEN);
+	reg |= SIO_REG_PPR_GEN_LTR_MODE_MASK;
+	write32(bar0->base + SIO_REG_SDIO_PPR_GEN, reg);
+
+	/* Program BAR0 + 1010h = 0x00000000 */
+	write32(bar0->base + SIO_REG_SDIO_PPR_SW_LTR, 0);
+
+	/* Program BAR0 + 3Ch[30] = 1b */
+	reg = read32(bar0->base + SIO_REG_SDIO_PPR_CMD12);
+	reg |= SIO_REG_SDIO_PPR_CMD12_B30;
+	write32(bar0->base + SIO_REG_SDIO_PPR_CMD12, reg);
+}
+
+/* Select I2C voltage of 1.8V or 3.3V. */
+static void serialio_i2c_voltage_sel(struct resource *bar0, u8 voltage)
+{
+	u32 reg32 = read32(bar0->base + SIO_REG_PPR_GEN);
+	reg32 &= ~SIO_REG_PPR_GEN_VOLTAGE_MASK;
+	reg32 |= SIO_REG_PPR_GEN_VOLTAGE(voltage);
+	write32(bar0->base + SIO_REG_PPR_GEN, reg32);
+}
+
+/* Init sequence to be run once, done as part of D21:F0 (SDMA) init. */
+static void serialio_init_once(int acpi_mode)
+{
+	if (acpi_mode) {
+		/* Enable ACPI IRQ for IRQ13, IRQ7, IRQ6, IRQ5 in RCBA. */
+		RCBA32_OR(ACPIIRQEN, (1 << 13)|(1 << 7)|(1 << 6)|(1 << 5));
+	}
+
+	/* Program IOBP CB000154h[12,9:8,4:0] = 1001100011111b. */
+	pch_iobp_update(SIO_IOBP_GPIODF, ~0x0000131f, 0x0000131f);
+
+	/* Program IOBP CB000180h[5:0] = 111111b (undefined register) */
+	pch_iobp_update(0xcb000180, ~0x0000003f, 0x0000003f);
+}
+
+static void serialio_init(struct device *dev)
+{
+	struct southbridge_intel_lynxpoint_config *config = dev->chip_info;
+	struct resource *bar0, *bar1;
+	int sio_index = -1;
+
+	printk(BIOS_DEBUG, "Initializing Serial IO device\n");
+
+	/* Find BAR0 and BAR1 */
+	bar0 = find_resource(dev, PCI_BASE_ADDRESS_0);
+	if (!bar0)
+		return;
+	bar1 = find_resource(dev, PCI_BASE_ADDRESS_1);
+	if (!bar1)
+		return;
+
+	switch (dev->path.pci.devfn) {
+	case PCI_DEVFN(21, 0): /* SDMA */
+		sio_index = SIO_ID_SDMA;
+		serialio_init_once(config->sio_acpi_mode);
+		serialio_d21_mode(sio_index, SIO_PIN_INTB,
+				  config->sio_acpi_mode);
+		break;
+	case PCI_DEVFN(21, 1): /* I2C0 */
+		sio_index = SIO_ID_I2C0;
+		serialio_d21_ltr(bar0);
+		serialio_i2c_voltage_sel(bar0, config->sio_i2c0_voltage);
+		serialio_d21_mode(sio_index, SIO_PIN_INTC,
+				  config->sio_acpi_mode);
+		break;
+	case PCI_DEVFN(21, 2): /* I2C1 */
+		sio_index = SIO_ID_I2C1;
+		serialio_d21_ltr(bar0);
+		serialio_i2c_voltage_sel(bar0, config->sio_i2c1_voltage);
+		serialio_d21_mode(sio_index, SIO_PIN_INTC,
+				  config->sio_acpi_mode);
+		break;
+	case PCI_DEVFN(21, 3): /* SPI0 */
+		sio_index = SIO_ID_SPI0;
+		serialio_d21_ltr(bar0);
+		serialio_d21_mode(sio_index, SIO_PIN_INTC,
+				  config->sio_acpi_mode);
+		break;
+	case PCI_DEVFN(21, 4): /* SPI1 */
+		sio_index = SIO_ID_SPI1;
+		serialio_d21_ltr(bar0);
+		serialio_d21_mode(sio_index, SIO_PIN_INTC,
+				  config->sio_acpi_mode);
+		break;
+	case PCI_DEVFN(21, 5): /* UART0 */
+		sio_index = SIO_ID_UART0;
+		serialio_d21_ltr(bar0);
+		serialio_d21_mode(sio_index, SIO_PIN_INTD,
+				  config->sio_acpi_mode);
+		break;
+	case PCI_DEVFN(21, 6): /* UART1 */
+		sio_index = SIO_ID_UART1;
+		serialio_d21_ltr(bar0);
+		serialio_d21_mode(sio_index, SIO_PIN_INTD,
+				  config->sio_acpi_mode);
+		break;
+	case PCI_DEVFN(23, 0): /* SDIO */
+		sio_index = SIO_ID_SDIO;
+		serialio_d23_ltr(bar0);
+		serialio_d23_mode(config->sio_acpi_mode);
+		break;
+	default:
+		return;
+	}
+
+	if (config->sio_acpi_mode) {
+		global_nvs_t *gnvs;
+
+		/* Find ACPI NVS to update BARs */
+		gnvs = (global_nvs_t *)cbmem_find(CBMEM_ID_ACPI_GNVS);
+		if (!gnvs) {
+			printk(BIOS_ERR, "Unable to locate Global NVS\n");
+			return;
+		}
+
+		/* Save BAR0 and BAR1 to ACPI NVS */
+		gnvs->s0b[sio_index] = (u32)bar0->base;
+		gnvs->s1b[sio_index] = (u32)bar1->base;
+	}
+}
+
+static void serialio_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 pci_ops = {
+	.set_subsystem		= serialio_set_subsystem,
+};
+
+static struct device_operations device_ops = {
+	.read_resources		= pci_bus_read_resources,
+	.set_resources		= pci_dev_set_resources,
+	.enable_resources	= pci_bus_enable_resources,
+	.init			= serialio_init,
+	.ops_pci		= &pci_ops,
+};
+
+static const unsigned short pci_device_ids[] = {
+	0x9c60, /* 0:15.0 - SDMA */
+	0x9c61, /* 0:15.1 - I2C0 */
+	0x9c62, /* 0:15.2 - I2C1 */
+	0x9c65, /* 0:15.3 - SPI0 */
+	0x9c66, /* 0:15.4 - SPI1 */
+	0x9c63, /* 0:15.5 - UART0 */
+	0x9c64, /* 0:15.6 - UART1 */
+	0x9c35, /* 0:17.0 - SDIO */
+	0
+};
+
+static const struct pci_driver pch_pcie __pci_driver = {
+	.ops	 = &device_ops,
+	.vendor	 = PCI_VENDOR_ID_INTEL,
+	.devices = pci_device_ids,
+};



More information about the coreboot-gerrit mailing list