summaryrefslogtreecommitdiff
path: root/src/soc/intel/baytrail/southcluster.c
diff options
context:
space:
mode:
authorAaron Durbin <adurbin@chromium.org>2013-10-29 16:37:10 -0500
committerAaron Durbin <adurbin@google.com>2014-02-27 06:12:43 +0100
commitd7bc23ac8e034de2bf33c50a1f209d7d23250e6b (patch)
tree5e41823bfbb6cdc6b3ef4ef71dfa98164b8b6c61 /src/soc/intel/baytrail/southcluster.c
parent67633a558a2cc55215565ee8e9079c97acc87171 (diff)
downloadcoreboot-d7bc23ac8e034de2bf33c50a1f209d7d23250e6b.tar.xz
baytrail: add support for disabling south cluster pci devices
When the southcluster pci devices are listed in the devicetree add the ability to perform the proper disabling sequence for turning off devices. This only turns off the pci device interface as well as put the device into D3Hot. It is not yet known how to put the TXE device into D3Hot so it's currently not possible to disable that device. Also, expose the southcluster_enable_dev() function so that other devices can call this if they require doing specific things before disabling the device. The southcluster_enable_dev() is only called on devices found in the devicetree and if they currently have no ops associated with them. BUG=chrome-os-partner:22871 BRANCH=None TEST=Built and booted through depthcharge. Interrogated output to ensure devices were being properly disabled. Change-Id: I537ddcb9379907af2fe012948542b6150a8bf7c5 Signed-off-by: Aaron Durbin <adurbin@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/174644 Reviewed-by: Duncan Laurie <dlaurie@chromium.org> Reviewed-on: http://review.coreboot.org/4911 Tested-by: build bot (Jenkins) Reviewed-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
Diffstat (limited to 'src/soc/intel/baytrail/southcluster.c')
-rw-r--r--src/soc/intel/baytrail/southcluster.c268
1 files changed, 267 insertions, 1 deletions
diff --git a/src/soc/intel/baytrail/southcluster.c b/src/soc/intel/baytrail/southcluster.c
index 32b8799e58..b58f0e084f 100644
--- a/src/soc/intel/baytrail/southcluster.c
+++ b/src/soc/intel/baytrail/southcluster.c
@@ -20,6 +20,7 @@
#include <stdint.h>
#include <arch/io.h>
+#include <console/console.h>
#include <device/device.h>
#include <device/pci.h>
#include <device/pci_ids.h>
@@ -29,6 +30,7 @@
#include <baytrail/lpc.h>
#include <baytrail/nvs.h>
#include <baytrail/pci_devs.h>
+#include <baytrail/pmc.h>
#include <baytrail/ramstage.h>
static inline void
@@ -113,12 +115,276 @@ static void sc_read_resources(device_t dev)
sc_add_io_resources(dev);
}
+/*
+ * Common code for the south cluster devices.
+ */
+
+/* Set bit in function disble register to hide this device. */
+static void sc_disable_devfn(device_t dev)
+{
+ const unsigned long func_dis = PMC_BASE_ADDRESS + FUNC_DIS;
+ const unsigned long func_dis2 = PMC_BASE_ADDRESS + FUNC_DIS2;
+ uint32_t mask = 0;
+ uint32_t mask2 = 0;
+
+ switch (dev->path.pci.devfn) {
+ case PCI_DEVFN(SDIO_DEV, SDIO_FUNC):
+ mask |= SDIO_DIS;
+ break;
+ case PCI_DEVFN(SD_DEV, SD_FUNC):
+ mask |= SD_DIS;
+ break;
+ case PCI_DEVFN(SATA_DEV, SATA_FUNC):
+ mask |= SATA_DIS;
+ break;
+ case PCI_DEVFN(XHCI_DEV, XHCI_FUNC):
+ mask |= XHCI_DIS;
+ /* Disable super speed PHY when XHCI is not available. */
+ mask2 |= USH_SS_PHY_DIS;
+ break;
+ case PCI_DEVFN(LPE_DEV, LPE_FUNC):
+ mask |= LPE_DIS;
+ break;
+ case PCI_DEVFN(MMC_DEV, MMC_FUNC):
+ mask |= MMC_DIS;
+ break;
+ case PCI_DEVFN(SIO_DMA1_DEV, SIO_DMA1_FUNC):
+ mask |= SIO_DMA1_DIS;
+ break;
+ case PCI_DEVFN(I2C1_DEV, I2C1_FUNC):
+ mask |= I2C1_DIS;
+ break;
+ case PCI_DEVFN(I2C2_DEV, I2C2_FUNC):
+ mask |= I2C1_DIS;
+ break;
+ case PCI_DEVFN(I2C3_DEV, I2C3_FUNC):
+ mask |= I2C3_DIS;
+ break;
+ case PCI_DEVFN(I2C4_DEV, I2C4_FUNC):
+ mask |= I2C4_DIS;
+ break;
+ case PCI_DEVFN(I2C5_DEV, I2C5_FUNC):
+ mask |= I2C5_DIS;
+ break;
+ case PCI_DEVFN(I2C6_DEV, I2C6_FUNC):
+ mask |= I2C6_DIS;
+ break;
+ case PCI_DEVFN(I2C7_DEV, I2C7_FUNC):
+ mask |= I2C7_DIS;
+ break;
+ case PCI_DEVFN(TXE_DEV, TXE_FUNC):
+ mask |= TXE_DIS;
+ break;
+ case PCI_DEVFN(HDA_DEV, HDA_FUNC):
+ mask |= HDA_DIS;
+ break;
+ case PCI_DEVFN(PCIE_PORT1_DEV, PCIE_PORT1_FUNC):
+ mask |= PCIE_PORT1_DIS;
+ break;
+ case PCI_DEVFN(PCIE_PORT2_DEV, PCIE_PORT2_FUNC):
+ mask |= PCIE_PORT2_DIS;
+ break;
+ case PCI_DEVFN(PCIE_PORT3_DEV, PCIE_PORT3_FUNC):
+ mask |= PCIE_PORT3_DIS;
+ break;
+ case PCI_DEVFN(PCIE_PORT4_DEV, PCIE_PORT4_FUNC):
+ mask |= PCIE_PORT4_DIS;
+ break;
+ case PCI_DEVFN(EHCI_DEV, EHCI_FUNC):
+ mask |= EHCI_DIS;
+ break;
+ case PCI_DEVFN(SIO_DMA2_DEV, SIO_DMA2_FUNC):
+ mask |= SIO_DMA2_DIS;
+ break;
+ case PCI_DEVFN(PWM1_DEV, PWM1_FUNC):
+ mask |= PWM1_DIS;
+ break;
+ case PCI_DEVFN(PWM2_DEV, PWM2_FUNC):
+ mask |= PWM2_DIS;
+ break;
+ case PCI_DEVFN(HSUART1_DEV, HSUART1_FUNC):
+ mask |= HSUART1_DIS;
+ break;
+ case PCI_DEVFN(HSUART2_DEV, HSUART2_FUNC):
+ mask |= HSUART2_DIS;
+ break;
+ case PCI_DEVFN(SPI_DEV, SPI_FUNC):
+ mask |= SPI_DIS;
+ break;
+ case PCI_DEVFN(SMBUS_DEV, SMBUS_FUNC):
+ mask2 |= SMBUS_DIS;
+ break;
+ }
+
+ if (mask != 0) {
+ write32(func_dis, read32(func_dis) | mask);
+ /* Ensure posted write hits. */
+ read32(func_dis);
+ }
+
+ if (mask2 != 0) {
+ write32(func_dis2, read32(func_dis2) | mask2);
+ /* Ensure posted write hits. */
+ read32(func_dis2);
+ }
+}
+
+static inline void set_d3hot_bits(device_t dev, int offset)
+{
+ uint32_t reg8;
+ printk(BIOS_DEBUG, "Power management CAP offset 0x%x.\n", offset);
+ reg8 = pci_read_config8(dev, offset + 4);
+ reg8 |= 0x3;
+ pci_write_config8(dev, offset + 4, reg8);
+}
+
+static int place_device_in_d3hot(device_t dev)
+{
+ unsigned offset;
+
+ offset = pci_find_capability(dev, PCI_CAP_ID_PM);
+
+ if (offset != 0) {
+ set_d3hot_bits(dev, offset);
+ return 0;
+ }
+
+ /* For some reason some of the devices don't have the capability
+ * pointer set correctly. Work around this by hard coding the offset. */
+ switch (dev->path.pci.devfn) {
+ case PCI_DEVFN(SDIO_DEV, SDIO_FUNC):
+ offset = 0x80;
+ break;
+ case PCI_DEVFN(SD_DEV, SD_FUNC):
+ offset = 0x80;
+ break;
+ case PCI_DEVFN(MMC_DEV, MMC_FUNC):
+ offset = 0x80;
+ break;
+ case PCI_DEVFN(LPE_DEV, LPE_FUNC):
+ offset = 0x80;
+ break;
+ case PCI_DEVFN(SIO_DMA1_DEV, SIO_DMA1_FUNC):
+ offset = 0x80;
+ break;
+ case PCI_DEVFN(I2C1_DEV, I2C1_FUNC):
+ offset = 0x80;
+ break;
+ case PCI_DEVFN(I2C2_DEV, I2C2_FUNC):
+ offset = 0x80;
+ break;
+ case PCI_DEVFN(I2C3_DEV, I2C3_FUNC):
+ offset = 0x80;
+ break;
+ case PCI_DEVFN(I2C4_DEV, I2C4_FUNC):
+ offset = 0x80;
+ break;
+ case PCI_DEVFN(I2C5_DEV, I2C5_FUNC):
+ offset = 0x80;
+ break;
+ case PCI_DEVFN(I2C6_DEV, I2C6_FUNC):
+ offset = 0x80;
+ break;
+ case PCI_DEVFN(I2C7_DEV, I2C7_FUNC):
+ offset = 0x80;
+ break;
+ case PCI_DEVFN(SIO_DMA2_DEV, SIO_DMA2_FUNC):
+ offset = 0x80;
+ break;
+ case PCI_DEVFN(PWM1_DEV, PWM1_FUNC):
+ offset = 0x80;
+ break;
+ case PCI_DEVFN(PWM2_DEV, PWM2_FUNC):
+ offset = 0x80;
+ break;
+ case PCI_DEVFN(HSUART1_DEV, HSUART1_FUNC):
+ offset = 0x80;
+ break;
+ case PCI_DEVFN(HSUART2_DEV, HSUART2_FUNC):
+ offset = 0x80;
+ break;
+ case PCI_DEVFN(SPI_DEV, SPI_FUNC):
+ offset = 0x80;
+ break;
+ case PCI_DEVFN(SATA_DEV, SATA_FUNC):
+ offset = 0x70;
+ break;
+ case PCI_DEVFN(XHCI_DEV, XHCI_FUNC):
+ offset = 0x70;
+ break;
+ case PCI_DEVFN(EHCI_DEV, EHCI_FUNC):
+ offset = 0x70;
+ break;
+ case PCI_DEVFN(HDA_DEV, HDA_FUNC):
+ offset = 0x50;
+ break;
+ case PCI_DEVFN(SMBUS_DEV, SMBUS_FUNC):
+ offset = 0x50;
+ break;
+ case PCI_DEVFN(TXE_DEV, TXE_FUNC):
+ break;
+ case PCI_DEVFN(PCIE_PORT1_DEV, PCIE_PORT1_FUNC):
+ offset = 0xa0;
+ break;
+ case PCI_DEVFN(PCIE_PORT2_DEV, PCIE_PORT2_FUNC):
+ offset = 0xa0;
+ break;
+ case PCI_DEVFN(PCIE_PORT3_DEV, PCIE_PORT3_FUNC):
+ offset = 0xa0;
+ break;
+ case PCI_DEVFN(PCIE_PORT4_DEV, PCIE_PORT4_FUNC):
+ offset = 0xa0;
+ break;
+ }
+
+ if (offset != 0) {
+ set_d3hot_bits(dev, offset);
+ return 0;
+ }
+
+ return -1;
+}
+
+/* Common PCI device function disable. */
+void southcluster_enable_dev(device_t dev)
+{
+ uint32_t reg32;
+
+ if (!dev->enabled) {
+ int slot = PCI_SLOT(dev->path.pci.devfn);
+ int func = PCI_FUNC(dev->path.pci.devfn);
+ printk(BIOS_DEBUG, "%s: Disabling device: %02x.%01x\n",
+ dev_path(dev), slot, func);
+
+ /* Ensure memory, io, and bus master are all disabled */
+ reg32 = pci_read_config32(dev, PCI_COMMAND);
+ reg32 &= ~(PCI_COMMAND_MASTER |
+ PCI_COMMAND_MEMORY | PCI_COMMAND_IO);
+ pci_write_config32(dev, PCI_COMMAND, reg32);
+
+ /* Place device in D3Hot */
+ if (place_device_in_d3hot(dev) < 0) {
+ printk(BIOS_WARNING,
+ "Could not place %02x.%01x into D3Hot. "
+ "Keeping device visible.\n", slot, func);
+ return;
+ }
+ /* Disable this device if possible */
+ sc_disable_devfn(dev);
+ } else {
+ /* Enable SERR */
+ reg32 = pci_read_config32(dev, PCI_COMMAND);
+ reg32 |= PCI_COMMAND_SERR;
+ pci_write_config32(dev, PCI_COMMAND, reg32);
+ }
+}
+
static struct device_operations device_ops = {
.read_resources = sc_read_resources,
.set_resources = pci_dev_set_resources,
.enable_resources = NULL,
.init = NULL,
- .enable = NULL,
+ .enable = southcluster_enable_dev,
.scan_bus = NULL,
.ops_pci = &soc_pci_ops,
};