From 3f957e2dc8477f00f6d3a9491d81399ee750c725 Mon Sep 17 00:00:00 2001
Message-Id: <3f957e2dc8477f00f6d3a9491d81399ee750c725.1330890410.git.blauwirbel@gmail.com>
From: Blue Swirl <blauwirbel@gmail.com>
Date: Sun, 4 Mar 2012 19:46:38 +0000
Subject: [PATCH] pci: fix BAR setup

A change in QEMU on how PCI bridges are setup revealed
a bug in OpenBIOS PCI setup. On Sparc64, the BARs just
happened to get somewhat correct values by accident before
the commit but not after the change.

Avoid to set up BARs for host bridge. Fix bridge
check, this lead to setting up 6 BARs instead of more
correct 2. If a bridge doesn't have any devices behind it,
disable it entirely. Fix Sparc64 PCI memory base.

Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
---
 arch/sparc64/openbios.c |    2 +-
 drivers/pci.c           |   67 ++++++++++++++++++++++++++++++++++------------
 drivers/pci.h           |    7 +++++
 3 files changed, 57 insertions(+), 19 deletions(-)

diff --git a/arch/sparc64/openbios.c b/arch/sparc64/openbios.c
index ac709fe..a1544a8 100644
--- a/arch/sparc64/openbios.c
+++ b/arch/sparc64/openbios.c
@@ -64,7 +64,7 @@ static const struct hwdef hwdefs[] = {
             .cfg_base = APB_SPECIAL_BASE,
             .cfg_len = 0x2000000,
             .host_mem_base = APB_MEM_BASE,
-            .pci_mem_base = 0,
+            .pci_mem_base = 0x10000000,
             .mem_len = 0x10000000,
             .io_base = APB_SPECIAL_BASE + 0x2000000ULL, // PCI Bus I/O space
             .io_len = 0x10000,
diff --git a/drivers/pci.c b/drivers/pci.c
index f8c6414..6ed0c03 100644
--- a/drivers/pci.c
+++ b/drivers/pci.c
@@ -966,11 +966,18 @@ static void ob_pci_configure_bar(pci_addr addr, pci_config_t *config,
                 size = min_align;
         reloc = (reloc + size -1) & ~(size - 1);
         if (*io_base == base) {
+                PCI_DPRINTF("changing io_base from 0x%lx to 0x%x\n",
+                            *io_base, reloc + size);
                 *io_base = reloc + size;
                 reloc -= arch->io_base;
         } else {
+                PCI_DPRINTF("changing mem_base from 0x%lx to 0x%x\n",
+                            *mem_base, reloc + size);
                 *mem_base = reloc + size;
         }
+        PCI_DPRINTF("Configuring BARs for %s: reloc 0x%x omask 0x%x "
+                    "io_base 0x%lx mem_base 0x%lx\n",
+                    config->path, reloc, *p_omask, *io_base, *mem_base);
         pci_config_write32(addr, config_addr, reloc | *p_omask);
         config->assigned[reg] = reloc | *p_omask;
 }
@@ -1021,26 +1028,30 @@ ob_pci_configure(pci_addr addr, pci_config_t *config, int num_regs, int rom_bar,
         pci_config_write16(addr, PCI_COMMAND, cmd);
 }
 
-static void ob_configure_pci_device(const char* parent_path,
-        int *bus_num, unsigned long *mem_base, unsigned long *io_base,
-        int bus, int devnum, int fn, int *p_is_multi);
+static int ob_configure_pci_device(const char* parent_path,
+                                   int *bus_num, unsigned long *mem_base,
+                                   unsigned long *io_base, int bus, int devnum,
+                                   int fn, int *p_is_multi);
 
-static void ob_scan_pci_bus(int *bus_num, unsigned long *mem_base,
-                            unsigned long *io_base, const char *path,
-                            int bus)
+static int ob_scan_pci_bus(int *bus_num, unsigned long *mem_base,
+                           unsigned long *io_base, const char *path,
+                           int bus)
 {
-	int devnum, fn, is_multi;
+        int devnum, fn, is_multi, ndevices = 0;
 
 	PCI_DPRINTF("\nScanning bus %d at %s...\n", bus, path);
 
 	for (devnum = 0; devnum < 32; devnum++) {
 		is_multi = 0;
 		for (fn = 0; fn==0 || (is_multi && fn<8); fn++) {
-		    ob_configure_pci_device(path, bus_num, mem_base, io_base,
-		            bus, devnum, fn, &is_multi);
+		    ndevices += ob_configure_pci_device(path, bus_num,
+                                                        mem_base, io_base,
+                                                        bus, devnum, fn,
+                                                        &is_multi);
 
 		}
 	}
+        return ndevices;
 }
 
 static void ob_configure_pci_bridge(pci_addr addr,
@@ -1048,6 +1059,9 @@ static void ob_configure_pci_bridge(pci_addr addr,
                                     unsigned long *io_base,
                                     int primary_bus, pci_config_t *config)
 {
+    int ndevices;
+    uint8_t command;
+
     config->primary_bus = primary_bus;
     pci_config_write8(addr, PCI_PRIMARY_BUS, config->primary_bus);
 
@@ -1062,16 +1076,30 @@ static void ob_configure_pci_bridge(pci_addr addr,
 
     /* make pci bridge parent device, prepare for recursion */
 
-    ob_scan_pci_bus(bus_num, mem_base, io_base,
-                    config->path, config->secondary_bus);
+    ndevices = ob_scan_pci_bus(bus_num, mem_base, io_base,
+                               config->path, config->secondary_bus);
+    if (!ndevices) {
+        /* no devices, disable bridging */
+        PCI_DPRINTF("disabling bridge %s\n", config->path);
+        command = pci_config_read8(addr, PCI_COMMAND);
+        command &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
+                     PCI_COMMAND_VGA_PALETTE | PCI_COMMAND_MASTER);
+        pci_config_write8(addr, PCI_COMMAND, command);
+        pci_config_write8(addr, PCI_IO_BASE, 0);
+        pci_config_write8(addr, PCI_IO_LIMIT, 0);
+        pci_config_write8(addr, PCI_MEMORY_BASE, 0);
+        pci_config_write8(addr, PCI_MEMORY_LIMIT, 0);
+        return;
+    }
 
     /* bus scan updates *bus_num to last revealed pci bus number */
     config->subordinate_bus = *bus_num;
     pci_config_write8(addr, PCI_SUBORDINATE_BUS, config->subordinate_bus);
 
-    PCI_DPRINTF("bridge %s PCI bus primary=%d secondary=%d subordinate=%d\n",
-            config->path, config->primary_bus, config->secondary_bus,
-            config->subordinate_bus);
+    PCI_DPRINTF("bridge %s PCI bus primary=%d secondary=%d subordinate=%d"
+                " ndev=%d\n",
+                config->path, config->primary_bus, config->secondary_bus,
+                config->subordinate_bus, ndevices);
 
     pci_set_bus_range(config);
 }
@@ -1117,7 +1145,7 @@ static int ob_pci_read_identification(int bus, int devnum, int fn,
     return 1;
 }
 
-static void ob_configure_pci_device(const char* parent_path,
+static int ob_configure_pci_device(const char* parent_path,
         int *bus_num, unsigned long *mem_base, unsigned long *io_base,
         int bus, int devnum, int fn, int *p_is_multi)
 {
@@ -1133,7 +1161,7 @@ static void ob_configure_pci_device(const char* parent_path,
     int is_host_bridge = 0;
 
     if (!ob_pci_read_identification(bus, devnum, fn, &vid, &did, &class, &subclass)) {
-        return;
+        return 0;
     }
 
     addr = PCI_ADDR(bus, devnum, fn);
@@ -1195,16 +1223,18 @@ static void ob_configure_pci_device(const char* parent_path,
 
         if (get_property(phandle, "vendor-id", NULL)) {
             PCI_DPRINTF("host bridge already configured\n");
-            return;
+            return 0;
         }
     }
 
     activate_dev(phandle);
 
-    if (htype & PCI_HEADER_TYPE_BRIDGE) {
+    if (htype & PCI_HEADER_TYPE_BRIDGE || (class == PCI_BASE_CLASS_BRIDGE)) {
+        PCI_DPRINTF("Bridge 2 bars, htype %x\n", htype);
         num_bars = 2;
         rom_bar  = PCI_ROM_ADDRESS1;
     } else {
+        PCI_DPRINTF("Device 6 bars, htype %x\n", htype);
         num_bars = 6;
         rom_bar  = PCI_ROM_ADDRESS;
     }
@@ -1240,6 +1270,7 @@ static void ob_configure_pci_device(const char* parent_path,
 
         ob_configure_pci_bridge(addr, bus_num, mem_base, io_base, bus, &config);
     }
+    return 1;
 }
 
 int ob_pci_init(void)
diff --git a/drivers/pci.h b/drivers/pci.h
index 0f6ae1f..4314507 100644
--- a/drivers/pci.h
+++ b/drivers/pci.h
@@ -7,6 +7,8 @@
 #define PCI_COMMAND		0x04
 #define  PCI_COMMAND_IO		0x01
 #define  PCI_COMMAND_MEMORY	0x02
+#define  PCI_COMMAND_MASTER     0x4     /* Enable bus mastering */
+#define  PCI_COMMAND_VGA_PALETTE 0x20   /* Enable palette snooping */
 
 #define PCI_STATUS              0x06    /* 16 bits */
 #define  PCI_STATUS_CAP_LIST    0x10    /* Support Capability List */
@@ -44,6 +46,11 @@
 #define PCI_BASE_ADDR_4		0x20
 #define PCI_BASE_ADDR_5		0x24
 
+#define PCI_IO_BASE             0x1c    /* I/O range behind the bridge */
+#define PCI_IO_LIMIT            0x1d
+#define PCI_MEMORY_BASE         0x20    /* Memory range behind */
+#define PCI_MEMORY_LIMIT        0x22
+
 #define PCI_SUBSYSTEM_VENDOR_ID 0x2c
 #define PCI_SUBSYSTEM_ID        0x2e
 
-- 
1.7.2.5
