bcm53xx: add upstream PCIe driver
authorHauke Mehrtens <hauke@hauke-m.de>
Thu, 25 Jun 2015 21:56:34 +0000 (21:56 +0000)
committerHauke Mehrtens <hauke@hauke-m.de>
Thu, 25 Jun 2015 21:56:34 +0000 (21:56 +0000)
This patch adds the missing parts to use the upstream Broadcom PCIe
driver and makes use of it.

Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
SVN-Revision: 46130

target/linux/bcm53xx/config-4.1
target/linux/bcm53xx/patches-4.1/030-PCI-iproc-Allow-override-of-device-tree-IRQ-mapping-.patch [new file with mode: 0644]
target/linux/bcm53xx/patches-4.1/031-PCI-iproc-Add-BCMA-PCIe-driver.patch [new file with mode: 0644]
target/linux/bcm53xx/patches-4.1/032-PCI-iproc-Directly-add-PCI-resources.patch [new file with mode: 0644]
target/linux/bcm53xx/patches-4.1/033-PCI-iproc-Free-resource-list-after-registration.patch [new file with mode: 0644]
target/linux/bcm53xx/patches-4.1/170-pcie2-bcma-add-new-PCIe2-driver-for-bcma.patch [deleted file]

index 8daa3aa2a5bc7d3606f0f510526633abd138eb5a..49cdb2173f985fd1744d6c3d79e055bb2db387d2 100644 (file)
@@ -247,9 +247,9 @@ CONFIG_OUTER_CACHE_SYNC=y
 CONFIG_PAGEFLAGS_EXTENDED=y
 CONFIG_PAGE_OFFSET=0xC0000000
 CONFIG_PCI=y
-# CONFIG_PCIE_IPROC is not set
+CONFIG_PCIE_IPROC=y
+CONFIG_PCIE_IPROC_BCMA=y
 # CONFIG_PCIE_IPROC_PLATFORM is not set
-CONFIG_PCI_BCM5301X=y
 CONFIG_PCI_DOMAINS=y
 CONFIG_PCI_DOMAINS_GENERIC=y
 CONFIG_PERF_USE_VMALLOC=y
diff --git a/target/linux/bcm53xx/patches-4.1/030-PCI-iproc-Allow-override-of-device-tree-IRQ-mapping-.patch b/target/linux/bcm53xx/patches-4.1/030-PCI-iproc-Allow-override-of-device-tree-IRQ-mapping-.patch
new file mode 100644 (file)
index 0000000..9050f72
--- /dev/null
@@ -0,0 +1,53 @@
+From c1e02ceaf5739d32f092ac07bf886a0281ec40b1 Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <hauke@hauke-m.de>
+Date: Tue, 12 May 2015 23:23:00 +0200
+Subject: [PATCH 1/2] PCI: iproc: Allow override of device tree IRQ mapping
+ function
+
+The iProc core PCIe driver defaults to using of_irq_parse_and_map_pci() for
+IRQ mapping.  Add iproc_pcie.map_irq so bus interfaces that don't use
+device tree can override this by supplying their own IRQ mapping function.
+
+[bhelgaas: changelog]
+Posting: http://lkml.kernel.org/r/1431465781-10753-1-git-send-email-hauke@hauke-m.de
+Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Reviewed-by: Ray Jui <rjui@broadcom.com.com>
+---
+ drivers/pci/host/pcie-iproc-platform.c | 2 ++
+ drivers/pci/host/pcie-iproc.c          | 2 +-
+ drivers/pci/host/pcie-iproc.h          | 1 +
+ 3 files changed, 4 insertions(+), 1 deletion(-)
+
+--- a/drivers/pci/host/pcie-iproc-platform.c
++++ b/drivers/pci/host/pcie-iproc-platform.c
+@@ -71,6 +71,8 @@ static int iproc_pcie_pltfm_probe(struct
+       pcie->resources = &res;
++      pcie->map_irq = of_irq_parse_and_map_pci;
++
+       ret = iproc_pcie_setup(pcie);
+       if (ret) {
+               dev_err(pcie->dev, "PCIe controller setup failed\n");
+--- a/drivers/pci/host/pcie-iproc.c
++++ b/drivers/pci/host/pcie-iproc.c
+@@ -229,7 +229,7 @@ int iproc_pcie_setup(struct iproc_pcie *
+       pci_scan_child_bus(bus);
+       pci_assign_unassigned_bus_resources(bus);
+-      pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
++      pci_fixup_irqs(pci_common_swizzle, pcie->map_irq);
+       pci_bus_add_devices(bus);
+       return 0;
+--- a/drivers/pci/host/pcie-iproc.h
++++ b/drivers/pci/host/pcie-iproc.h
+@@ -34,6 +34,7 @@ struct iproc_pcie {
+       struct pci_bus *root_bus;
+       struct phy *phy;
+       int irqs[IPROC_PCIE_MAX_NUM_IRQS];
++      int (*map_irq)(const struct pci_dev *, u8, u8);
+ };
+ int iproc_pcie_setup(struct iproc_pcie *pcie);
diff --git a/target/linux/bcm53xx/patches-4.1/031-PCI-iproc-Add-BCMA-PCIe-driver.patch b/target/linux/bcm53xx/patches-4.1/031-PCI-iproc-Add-BCMA-PCIe-driver.patch
new file mode 100644 (file)
index 0000000..a850baf
--- /dev/null
@@ -0,0 +1,177 @@
+From 4785ffbdc9b52e308e43b9e2dcc1dca44f056d76 Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <hauke@hauke-m.de>
+Date: Tue, 12 May 2015 23:23:01 +0200
+Subject: [PATCH 2/2] PCI: iproc: Add BCMA PCIe driver
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This driver adds support for the PCIe 2.0 controller found on the BCMA bus.
+This controller can be found on (mostly) all Broadcom BCM470X / BCM5301X
+ARM SoCs.
+
+The driver found in the Broadcom SDK does some more stuff, like setting up
+some DMA memory areas, chaining MPS and MRRS to 512 and also some PHY
+changes like "improving" the PCIe jitter and doing some special
+initialization for the 3rd PCIe port.
+
+This was tested on a bcm4708 board with 2 PCIe ports and wireless cards
+connected to them.
+
+PCI_DOMAINS is needed by this driver, because normally there is more than
+one PCIe controller and without PCI_DOMAINS only the first controller gets
+registered.  This controller gets 6 IRQs; the last one is trigged by all
+IRQ events.
+
+[bhelgaas: fix "GPLv2" MODULE_LICENSE typo]
+Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Acked-by: Rafał Miłecki <zajec5@gmail.com>
+Acked-by: Ray Jui <rjui@broadcom.com.com>
+---
+ drivers/pci/host/Kconfig           |  11 ++++
+ drivers/pci/host/Makefile          |   1 +
+ drivers/pci/host/pcie-iproc-bcma.c | 112 +++++++++++++++++++++++++++++++++++++
+ 3 files changed, 124 insertions(+)
+ create mode 100644 drivers/pci/host/pcie-iproc-bcma.c
+
+--- a/drivers/pci/host/Kconfig
++++ b/drivers/pci/host/Kconfig
+@@ -125,4 +125,15 @@ config PCIE_IPROC_PLATFORM
+         Say Y here if you want to use the Broadcom iProc PCIe controller
+         through the generic platform bus interface
++config PCIE_IPROC_BCMA
++      bool "Broadcom iProc PCIe BCMA bus driver"
++      depends on ARCH_BCM_IPROC || (ARM && COMPILE_TEST)
++      select PCIE_IPROC
++      select BCMA
++      select PCI_DOMAINS
++      default ARCH_BCM_5301X
++      help
++        Say Y here if you want to use the Broadcom iProc PCIe controller
++        through the BCMA bus interface
++
+ endmenu
+--- a/drivers/pci/host/Makefile
++++ b/drivers/pci/host/Makefile
+@@ -15,3 +15,4 @@ obj-$(CONFIG_PCI_LAYERSCAPE) += pci-laye
+ obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
+ obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o
+ obj-$(CONFIG_PCIE_IPROC_PLATFORM) += pcie-iproc-platform.o
++obj-$(CONFIG_PCIE_IPROC_BCMA) += pcie-iproc-bcma.o
+--- /dev/null
++++ b/drivers/pci/host/pcie-iproc-bcma.c
+@@ -0,0 +1,112 @@
++/*
++ * Copyright (C) 2015 Broadcom Corporation
++ * Copyright (C) 2015 Hauke Mehrtens <hauke@hauke-m.de>
++ *
++ * 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.
++ *
++ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
++ * kind, whether express or implied; without even the implied warranty
++ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/kernel.h>
++#include <linux/pci.h>
++#include <linux/module.h>
++#include <linux/slab.h>
++#include <linux/phy/phy.h>
++#include <linux/bcma/bcma.h>
++#include <linux/ioport.h>
++
++#include "pcie-iproc.h"
++
++
++/* NS: CLASS field is R/O, and set to wrong 0x200 value */
++static void bcma_pcie2_fixup_class(struct pci_dev *dev)
++{
++      dev->class = PCI_CLASS_BRIDGE_PCI << 8;
++}
++DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x8011, bcma_pcie2_fixup_class);
++DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x8012, bcma_pcie2_fixup_class);
++
++static int iproc_pcie_bcma_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
++{
++      struct pci_sys_data *sys = dev->sysdata;
++      struct iproc_pcie *pcie = sys->private_data;
++      struct bcma_device *bdev = container_of(pcie->dev, struct bcma_device, dev);
++
++      return bcma_core_irq(bdev, 5);
++}
++
++static int iproc_pcie_bcma_probe(struct bcma_device *bdev)
++{
++      struct iproc_pcie *pcie;
++      LIST_HEAD(res);
++      struct resource res_mem;
++      int ret;
++
++      pcie = devm_kzalloc(&bdev->dev, sizeof(*pcie), GFP_KERNEL);
++      if (!pcie)
++              return -ENOMEM;
++
++      pcie->dev = &bdev->dev;
++      bcma_set_drvdata(bdev, pcie);
++
++      pcie->base = bdev->io_addr;
++
++      res_mem.start = bdev->addr_s[0];
++      res_mem.end = bdev->addr_s[0] + SZ_128M - 1;
++      res_mem.name = "PCIe MEM space";
++      res_mem.flags = IORESOURCE_MEM;
++      pci_add_resource(&res, &res_mem);
++
++      pcie->resources = &res;
++
++      pcie->map_irq = iproc_pcie_bcma_map_irq;
++
++      ret = iproc_pcie_setup(pcie);
++      if (ret) {
++              dev_err(pcie->dev, "PCIe controller setup failed\n");
++              return ret;
++      }
++
++      return 0;
++}
++
++static void iproc_pcie_bcma_remove(struct bcma_device *bdev)
++{
++      struct iproc_pcie *pcie = bcma_get_drvdata(bdev);
++
++      iproc_pcie_remove(pcie);
++}
++
++static const struct bcma_device_id iproc_pcie_bcma_table[] = {
++      BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_NS_PCIEG2, BCMA_ANY_REV, BCMA_ANY_CLASS),
++      {},
++};
++MODULE_DEVICE_TABLE(bcma, iproc_pcie_bcma_table);
++
++static struct bcma_driver iproc_pcie_bcma_driver = {
++      .name           = KBUILD_MODNAME,
++      .id_table       = iproc_pcie_bcma_table,
++      .probe          = iproc_pcie_bcma_probe,
++      .remove         = iproc_pcie_bcma_remove,
++};
++
++static int __init iproc_pcie_bcma_init(void)
++{
++      return bcma_driver_register(&iproc_pcie_bcma_driver);
++}
++module_init(iproc_pcie_bcma_init);
++
++static void __exit iproc_pcie_bcma_exit(void)
++{
++      bcma_driver_unregister(&iproc_pcie_bcma_driver);
++}
++module_exit(iproc_pcie_bcma_exit);
++
++MODULE_AUTHOR("Hauke Mehrtens");
++MODULE_DESCRIPTION("Broadcom iProc PCIe BCMA driver");
++MODULE_LICENSE("GPL v2");
diff --git a/target/linux/bcm53xx/patches-4.1/032-PCI-iproc-Directly-add-PCI-resources.patch b/target/linux/bcm53xx/patches-4.1/032-PCI-iproc-Directly-add-PCI-resources.patch
new file mode 100644 (file)
index 0000000..09d8226
--- /dev/null
@@ -0,0 +1,90 @@
+From 18c4342aa56d70176eea85021e6fe8f6f8f39c7b Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <hauke@hauke-m.de>
+Date: Sun, 24 May 2015 22:37:02 +0200
+Subject: [PATCH 1/2] PCI: iproc: Directly add PCI resources
+
+The struct iproc_pcie.resources member was pointing to a stack variable and
+is invalid after the registration function returned.
+
+Remove this pointer and add a parameter to the function.
+
+Tested-by: Ray Jui <rjui@broadcom.com>
+Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Reviewed-by: Ray Jui <rjui@broadcom.com>
+---
+ drivers/pci/host/pcie-iproc-bcma.c     | 4 +---
+ drivers/pci/host/pcie-iproc-platform.c | 4 +---
+ drivers/pci/host/pcie-iproc.c          | 4 ++--
+ drivers/pci/host/pcie-iproc.h          | 3 +--
+ 4 files changed, 5 insertions(+), 10 deletions(-)
+
+--- a/drivers/pci/host/pcie-iproc-bcma.c
++++ b/drivers/pci/host/pcie-iproc-bcma.c
+@@ -62,11 +62,9 @@ static int iproc_pcie_bcma_probe(struct
+       res_mem.flags = IORESOURCE_MEM;
+       pci_add_resource(&res, &res_mem);
+-      pcie->resources = &res;
+-
+       pcie->map_irq = iproc_pcie_bcma_map_irq;
+-      ret = iproc_pcie_setup(pcie);
++      ret = iproc_pcie_setup(pcie, &res);
+       if (ret) {
+               dev_err(pcie->dev, "PCIe controller setup failed\n");
+               return ret;
+--- a/drivers/pci/host/pcie-iproc-platform.c
++++ b/drivers/pci/host/pcie-iproc-platform.c
+@@ -69,11 +69,9 @@ static int iproc_pcie_pltfm_probe(struct
+               return ret;
+       }
+-      pcie->resources = &res;
+-
+       pcie->map_irq = of_irq_parse_and_map_pci;
+-      ret = iproc_pcie_setup(pcie);
++      ret = iproc_pcie_setup(pcie, &res);
+       if (ret) {
+               dev_err(pcie->dev, "PCIe controller setup failed\n");
+               return ret;
+--- a/drivers/pci/host/pcie-iproc.c
++++ b/drivers/pci/host/pcie-iproc.c
+@@ -183,7 +183,7 @@ static void iproc_pcie_enable(struct ipr
+       writel(SYS_RC_INTX_MASK, pcie->base + SYS_RC_INTX_EN);
+ }
+-int iproc_pcie_setup(struct iproc_pcie *pcie)
++int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
+ {
+       int ret;
+       struct pci_bus *bus;
+@@ -211,7 +211,7 @@ int iproc_pcie_setup(struct iproc_pcie *
+       pcie->sysdata.private_data = pcie;
+       bus = pci_create_root_bus(pcie->dev, 0, &iproc_pcie_ops,
+-                                &pcie->sysdata, pcie->resources);
++                                &pcie->sysdata, res);
+       if (!bus) {
+               dev_err(pcie->dev, "unable to create PCI root bus\n");
+               ret = -ENOMEM;
+--- a/drivers/pci/host/pcie-iproc.h
++++ b/drivers/pci/host/pcie-iproc.h
+@@ -29,7 +29,6 @@
+ struct iproc_pcie {
+       struct device *dev;
+       void __iomem *base;
+-      struct list_head *resources;
+       struct pci_sys_data sysdata;
+       struct pci_bus *root_bus;
+       struct phy *phy;
+@@ -37,7 +36,7 @@ struct iproc_pcie {
+       int (*map_irq)(const struct pci_dev *, u8, u8);
+ };
+-int iproc_pcie_setup(struct iproc_pcie *pcie);
++int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res);
+ int iproc_pcie_remove(struct iproc_pcie *pcie);
+ #endif /* _PCIE_IPROC_H */
diff --git a/target/linux/bcm53xx/patches-4.1/033-PCI-iproc-Free-resource-list-after-registration.patch b/target/linux/bcm53xx/patches-4.1/033-PCI-iproc-Free-resource-list-after-registration.patch
new file mode 100644 (file)
index 0000000..bbd3164
--- /dev/null
@@ -0,0 +1,57 @@
+From ef07991a95de76b07594448c3521361831ec2cfe Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <hauke@hauke-m.de>
+Date: Sun, 24 May 2015 22:37:03 +0200
+Subject: [PATCH 2/2] PCI: iproc: Free resource list after registration
+
+The resource list is only used in the setup process and was never freed.
+pci_add_resource() allocates a memory area to store the list item.
+
+Fix the memory leak.
+
+Tested-by: Ray Jui <rjui@broadcom.com>
+Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Reviewed-by: Ray Jui <rjui@broadcom.com>
+---
+ drivers/pci/host/pcie-iproc-bcma.c     | 8 ++++----
+ drivers/pci/host/pcie-iproc-platform.c | 8 ++++----
+ 2 files changed, 8 insertions(+), 8 deletions(-)
+
+--- a/drivers/pci/host/pcie-iproc-bcma.c
++++ b/drivers/pci/host/pcie-iproc-bcma.c
+@@ -65,12 +65,12 @@ static int iproc_pcie_bcma_probe(struct
+       pcie->map_irq = iproc_pcie_bcma_map_irq;
+       ret = iproc_pcie_setup(pcie, &res);
+-      if (ret) {
++      if (ret)
+               dev_err(pcie->dev, "PCIe controller setup failed\n");
+-              return ret;
+-      }
+-      return 0;
++      pci_free_resource_list(&res);
++
++      return ret;
+ }
+ static void iproc_pcie_bcma_remove(struct bcma_device *bdev)
+--- a/drivers/pci/host/pcie-iproc-platform.c
++++ b/drivers/pci/host/pcie-iproc-platform.c
+@@ -72,12 +72,12 @@ static int iproc_pcie_pltfm_probe(struct
+       pcie->map_irq = of_irq_parse_and_map_pci;
+       ret = iproc_pcie_setup(pcie, &res);
+-      if (ret) {
++      if (ret)
+               dev_err(pcie->dev, "PCIe controller setup failed\n");
+-              return ret;
+-      }
+-      return 0;
++      pci_free_resource_list(&res);
++
++      return ret;
+ }
+ static int iproc_pcie_pltfm_remove(struct platform_device *pdev)
diff --git a/target/linux/bcm53xx/patches-4.1/170-pcie2-bcma-add-new-PCIe2-driver-for-bcma.patch b/target/linux/bcm53xx/patches-4.1/170-pcie2-bcma-add-new-PCIe2-driver-for-bcma.patch
deleted file mode 100644 (file)
index d4dedc0..0000000
+++ /dev/null
@@ -1,534 +0,0 @@
-From cf067bf8bb993d6cfdc42d750ae241c43f88403f Mon Sep 17 00:00:00 2001
-From: Hauke Mehrtens <hauke@hauke-m.de>
-Date: Mon, 12 May 2014 11:55:20 +0200
-Subject: [PATCH 1/2] PCI: BCM5301X: add PCIe2 driver for BCM5301X SoCs
-
-This driver supports the PCIe controller found on the BCM4708 and
-similar SoCs. The controller itself is automatically detected by bcma.
-
-This controller is found on SoCs usually used in SOHO routers to
-connect the wifi cards to the SoC. All the of the BCM5301X SoCs I know
-of have 2 or 3 of these controllers in the SoC.
-
-I had to use PCI domains otherwise the pci_create_root_bus() function
-in drivers/pci/probe.c would fail for the second controller being
-registered because pci_find_bus() would find the same PCIe bus again
-and assume it is already registered, which ends up in a kernel panic in
-pcibios_init_hw() in arch/arm/kernel/bios32.c
-
-The ARM PCI code assumes that every controller has an I/O space and
-adds a dummy area if the driver does not specify one. This will work
-for the first controller, but when we register the second one this will
-result in an error. To prevent this problem we add an empty I/O space.
-
-Currently I have problems with probing the devices on the bus, because
-pci_bus_add_devices() is called too early in pci_scan_root_bus() in
-drivers/pci/probe.c, before pci_bus_assign_resources() was called in
-pci_common_init_dev() in arch/arm/kernel/bios32.c. When the devices are
-added too early they do not have any resources and adding fails. I have
-to remove the call to pci_bus_add_devices() in pci_scan_root_bus() to
-make registration work, calling pci_bus_add_devices() later again does
-not fix this problem.
-
-Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
----
- arch/arm/mach-bcm/Kconfig            |   1 +
- drivers/pci/host/Kconfig             |   7 +
- drivers/pci/host/Makefile            |   1 +
- drivers/pci/host/pci-host-bcm5301x.c | 428 +++++++++++++++++++++++++++++++++++
- 4 files changed, 437 insertions(+)
- create mode 100644 drivers/pci/host/pci-host-bcm5301x.c
-
---- a/arch/arm/mach-bcm/Kconfig
-+++ b/arch/arm/mach-bcm/Kconfig
-@@ -38,6 +38,7 @@ config ARCH_BCM_CYGNUS
- config ARCH_BCM_5301X
-       bool "Broadcom BCM470X / BCM5301X ARM SoC" if ARCH_MULTI_V7
-       select ARCH_BCM_IPROC
-+      select PCI_DOMAINS if PCI
-       help
-         Support for Broadcom BCM470X and BCM5301X SoCs with ARM CPU cores.
---- a/drivers/pci/host/Kconfig
-+++ b/drivers/pci/host/Kconfig
-@@ -125,4 +125,11 @@ config PCIE_IPROC_PLATFORM
-         Say Y here if you want to use the Broadcom iProc PCIe controller
-         through the generic platform bus interface
-+config PCI_BCM5301X
-+      bool "BCM5301X PCIe2 host controller"
-+      depends on BCMA && OF && ARM && PCI_DOMAINS
-+      help
-+        Say Y here if you want to support the PCIe host controller found
-+        on Broadcom BCM5301X and BCM470X (Northstar) SoCs.
-+
- endmenu
---- a/drivers/pci/host/Makefile
-+++ b/drivers/pci/host/Makefile
-@@ -15,3 +15,4 @@ obj-$(CONFIG_PCI_LAYERSCAPE) += pci-laye
- obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
- obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o
- obj-$(CONFIG_PCIE_IPROC_PLATFORM) += pcie-iproc-platform.o
-+obj-$(CONFIG_PCI_BCM5301X) += pci-host-bcm5301x.o
---- /dev/null
-+++ b/drivers/pci/host/pci-host-bcm5301x.c
-@@ -0,0 +1,459 @@
-+/*
-+ * Northstar PCI-Express driver
-+ * Only supports Root-Complex (RC) mode
-+ *
-+ * Notes:
-+ * PCI Domains are being used to identify the PCIe port 1:1.
-+ *
-+ * Only MEM access is supported, PAX does not support IO.
-+ *
-+ * Copyright 2012-2014, Broadcom Corporation
-+ * Copyright 2014, Hauke Mehrtens <hauke@hauke-m.de>
-+ *
-+ * Licensed under the GNU/GPL. See COPYING for details.
-+ */
-+
-+#include <linux/kernel.h>
-+#include <linux/module.h>
-+#include <linux/delay.h>
-+#include <linux/pci.h>
-+#include <linux/io.h>
-+#include <linux/ioport.h>
-+#include <linux/bcma/bcma.h>
-+#include <linux/bcma/bcma_driver_pcie2.h>
-+
-+#define       SOC_PCIE_HDR_OFF        0x400   /* 256 bytes per function */
-+
-+#define PCI_LINK_STATUS_CTRL_2_OFFSET 0xDC
-+#define PCI_TARGET_LINK_SPEED_MASK    0xF
-+#define PCI_TARGET_LINK_SPEED_GEN2    0x2
-+#define PCI_TARGET_LINK_SPEED_GEN1    0x1
-+
-+static int bcma_pcie2_map_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
-+{
-+      struct pci_sys_data *sys = pdev->sysdata;
-+      struct bcma_device *bdev = sys->private_data;
-+
-+      return bcma_core_irq(bdev, 5);
-+}
-+
-+static u32 bcma_pcie2_cfg_base(struct bcma_device *bdev, int busno,
-+                             unsigned int devfn, int where)
-+{
-+      int slot = PCI_SLOT(devfn);
-+      int fn = PCI_FUNC(devfn);
-+      u32 addr_reg;
-+
-+      if (busno == 0) {
-+              if (slot >= 1)
-+                      return 0;
-+              bcma_write32(bdev, BCMA_CORE_PCIE2_CONFIGINDADDR,
-+                           where & 0xffc);
-+              return BCMA_CORE_PCIE2_CONFIGINDDATA;
-+      }
-+      if (fn > 1)
-+              return 0;
-+      addr_reg = (busno & 0xff) << 20 | (slot << 15) | (fn << 12) |
-+                 (where & 0xffc) | (1 & 0x3);
-+
-+      bcma_write32(bdev, BCMA_CORE_PCIE2_CFG_ADDR, addr_reg);
-+      return BCMA_CORE_PCIE2_CFG_DATA;
-+}
-+
-+static u32 bcma_pcie2_read_config(struct bcma_device *bdev, int busno,
-+                                unsigned int devfn, int where, int size)
-+{
-+      u32 base;
-+      u32 data_reg;
-+      u32 mask;
-+      int shift;
-+
-+      base = bcma_pcie2_cfg_base(bdev, busno, devfn, where);
-+
-+      if (!base)
-+              return ~0UL;
-+
-+      data_reg = bcma_read32(bdev, base);
-+
-+      if (size == 4)
-+              return data_reg;
-+
-+      mask = (1 << (size * 8)) - 1;
-+      shift = (where % 4) * 8;
-+      return (data_reg >> shift) & mask;
-+}
-+
-+static void bcma_pcie2_write_config(struct bcma_device *bdev, int busno,
-+                                  unsigned int devfn, int where, int size,
-+                                  u32 val)
-+{
-+      u32 base;
-+      u32 data_reg;
-+
-+      base = bcma_pcie2_cfg_base(bdev, busno, devfn, where);
-+
-+      if (!base)
-+              return;
-+
-+      if (size < 4) {
-+              u32 mask = (1 << (size * 8)) - 1;
-+              int shift = (where % 4) * 8;
-+
-+              data_reg = bcma_read32(bdev, base);
-+              data_reg &= ~(mask << shift);
-+              data_reg |= (val & mask) << shift;
-+      } else {
-+              data_reg = val;
-+      }
-+
-+      bcma_write32(bdev, base, data_reg);
-+}
-+
-+static int bcma_pcie2_read_config_pci(struct pci_bus *bus, unsigned int devfn,
-+                                 int where, int size, u32 *val)
-+{
-+      struct pci_sys_data *sys = bus->sysdata;
-+      struct bcma_device *bdev = sys->private_data;
-+
-+      *val = bcma_pcie2_read_config(bdev, bus->number, devfn, where, size);
-+
-+      return PCIBIOS_SUCCESSFUL;
-+}
-+
-+static int bcma_pcie2_write_config_pci(struct pci_bus *bus, unsigned int devfn,
-+                                  int where, int size, u32 val)
-+{
-+      struct pci_sys_data *sys = bus->sysdata;
-+      struct bcma_device *bdev = sys->private_data;
-+
-+      bcma_pcie2_write_config(bdev, bus->number, devfn, where, size, val);
-+
-+      return PCIBIOS_SUCCESSFUL;
-+}
-+
-+/*
-+ * Methods for accessing configuration registers
-+ */
-+static struct pci_ops bcma_pcie2_ops = {
-+      .read = bcma_pcie2_read_config_pci,
-+      .write = bcma_pcie2_write_config_pci,
-+};
-+
-+/* NS: CLASS field is R/O, and set to wrong 0x200 value */
-+static void bcma_pcie2_fixup_class(struct pci_dev *dev)
-+{
-+      dev->class = PCI_CLASS_BRIDGE_PCI << 8;
-+}
-+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x8011, bcma_pcie2_fixup_class);
-+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x8012, bcma_pcie2_fixup_class);
-+
-+/*
-+ * Check link status, return 0 if link is up in RC mode,
-+ * otherwise return non-zero
-+ */
-+static int bcma_pcie2_check_link(struct bcma_device *bdev, struct pci_sys_data *sys)
-+{
-+      u32 tmp32;
-+      u16 tmp16;
-+      u16 pos;
-+      u8 nlw;
-+      /*
-+       * Setup callback (bcma_pcie2_setup) is called in pcibios_init_hw before
-+       * creating bus root, so we don't have it here yet. On the other hand
-+       * we really want to use pci_bus_find_capability helper to check NLW.
-+       * Let's fake simple pci_bus just to query for capabilities.
-+       */
-+      struct pci_bus bus = {
-+              .number = 0,
-+              .ops = &bcma_pcie2_ops,
-+              .sysdata = sys,
-+      };
-+
-+      tmp32 = bcma_read32(bdev, BCMA_CORE_PCIE2_LINK_STATUS);
-+      dev_dbg(&bdev->dev, "link status: 0x%08x\n", tmp32);
-+
-+      tmp32 = bcma_read32(bdev, BCMA_CORE_PCIE2_STRAP_STATUS);
-+      dev_dbg(&bdev->dev, "strap status: 0x%08x\n", tmp32);
-+
-+      /* check link status to see if link is active */
-+      pos = pci_bus_find_capability(&bus, 0, PCI_CAP_ID_EXP);
-+      pci_bus_read_config_word(&bus, 0, pos + PCI_EXP_LNKSTA, &tmp16);
-+      nlw = (tmp16 & PCI_EXP_LNKSTA_NLW) >> PCI_EXP_LNKSTA_NLW_SHIFT;
-+
-+      if (nlw == 0) {
-+              /* try GEN 1 link speed */
-+              tmp32 = bcma_pcie2_read_config(bdev, 0, 0,
-+                                      PCI_LINK_STATUS_CTRL_2_OFFSET, 4);
-+              if ((tmp32 & PCI_TARGET_LINK_SPEED_MASK) ==
-+                              PCI_TARGET_LINK_SPEED_GEN2) {
-+                      tmp32 &= ~PCI_TARGET_LINK_SPEED_MASK;
-+                      tmp32 |= PCI_TARGET_LINK_SPEED_GEN1;
-+                      bcma_pcie2_write_config(bdev, 0, 0,
-+                                      PCI_LINK_STATUS_CTRL_2_OFFSET, 4, tmp32);
-+                      tmp32 = bcma_pcie2_read_config(bdev, 0, 0,
-+                                      PCI_LINK_STATUS_CTRL_2_OFFSET, 4);
-+                      msleep(100);
-+
-+                      pos = pci_bus_find_capability(&bus, 0, PCI_CAP_ID_EXP);
-+                      pci_bus_read_config_word(&bus, 0, pos + PCI_EXP_LNKSTA,
-+                                      &tmp16);
-+                      nlw = (tmp16 & PCI_EXP_LNKSTA_NLW) >>
-+                              PCI_EXP_LNKSTA_NLW_SHIFT;
-+              }
-+      }
-+
-+      dev_info(&bdev->dev, "link: %s\n", nlw ? "UP" : "DOWN");
-+      return nlw ? 0 : -ENODEV;
-+}
-+
-+/*
-+ * Initializte the PCIe controller
-+ */
-+static void bcma_pcie2_hw_init(struct bcma_device *bdev)
-+{
-+      u32 tmp32;
-+      u16 tmp16;
-+
-+      /* Change MPS and MRRS to 512 */
-+      tmp16 = bcma_pcie2_read_config(bdev, 0, 0, 0x4d4, 2);
-+      tmp16 &= ~7;
-+      tmp16 |= 2;
-+      bcma_pcie2_write_config(bdev, 0, 0, 0x4d4, 2, tmp16);
-+
-+      tmp32 = bcma_pcie2_read_config(bdev, 0, 0, 0xb4, 4);
-+      tmp32 &= ~((7 << 12) | (7 << 5));
-+      tmp32 |= (2 << 12) | (2 << 5);
-+      bcma_pcie2_write_config(bdev, 0, 0, 0xb4, 4, tmp32);
-+
-+      /*
-+       * Turn-on Root-Complex (RC) mode, from reset default of EP
-+       * The mode is set by straps, can be overwritten via DMU
-+       * register <cru_straps_control> bit 5, "1" means RC
-+       */
-+
-+      /* Send a downstream reset */
-+      bcma_write32(bdev, BCMA_CORE_PCIE2_CLK_CONTROL,
-+                   PCIE2_CLKC_RST_OE | PCIE2_CLKC_RST);
-+      usleep_range(250, 400);
-+      bcma_write32(bdev, BCMA_CORE_PCIE2_CLK_CONTROL, PCIE2_CLKC_RST_OE);
-+      msleep(250);
-+
-+      /* TBD: take care of PM, check we're on */
-+}
-+
-+/*
-+ * Setup the address translation
-+ *
-+ * NOTE: All PCI-to-CPU address mapping are 1:1 for simplicity
-+ */
-+static int bcma_pcie2_map_init(struct bcma_device *bdev, u32 addr)
-+{
-+      /* 64MB alignment */
-+      if (!addr || (addr & (SZ_64M - 1)))
-+              return -EINVAL;
-+
-+      bcma_write32(bdev, BCMA_CORE_PCIE2_OMAP0_LOWER, addr);
-+      bcma_write32(bdev, BCMA_CORE_PCIE2_OARR0, addr | 0x01);
-+
-+      bcma_write32(bdev, BCMA_CORE_PCIE2_OMAP1_LOWER, addr + SZ_64M);
-+      bcma_write32(bdev, BCMA_CORE_PCIE2_OARR1, (addr + SZ_64M) | 0x01);
-+
-+      /*
-+       * Inbound address translation setup
-+       * Northstar only maps up to 128 MiB inbound, DRAM could be up to 1 GiB.
-+       *
-+       * For now allow access to entire DRAM, assuming it is less than 128MiB,
-+       * otherwise DMA bouncing mechanism may be required.
-+       * Also consider DMA mask to limit DMA physical address
-+       */
-+      /* 64-bit LE regs, write low word, high is 0 at reset */
-+      bcma_write32(bdev, BCMA_CORE_PCIE2_FUNC0_IMAP1, PHYS_OFFSET | 0x1);
-+      bcma_write32(bdev, BCMA_CORE_PCIE2_IARR1_LOWER,
-+                         PHYS_OFFSET | ((SZ_128M >> 20) & 0xff));
-+      return 0;
-+}
-+
-+/*
-+ * Setup PCIE Host bridge
-+ */
-+static int bcma_pcie2_bridge_init(struct bcma_device *bdev, u32 addr, u32 size)
-+{
-+      bcma_pcie2_write_config(bdev, 0, 0, PCI_PRIMARY_BUS, 1, 0);
-+      bcma_pcie2_write_config(bdev, 0, 0, PCI_SECONDARY_BUS, 1, 1);
-+      bcma_pcie2_write_config(bdev, 0, 0, PCI_SUBORDINATE_BUS, 1, 4);
-+
-+      bcma_pcie2_read_config(bdev, 0, 0, PCI_PRIMARY_BUS, 1);
-+      bcma_pcie2_read_config(bdev, 0, 0, PCI_SECONDARY_BUS, 1);
-+      bcma_pcie2_read_config(bdev, 0, 0, PCI_SUBORDINATE_BUS, 1);
-+
-+      /* MEM_BASE, MEM_LIM require 1MB alignment */
-+      if (((addr >> 16) & 0xf) || (((addr + size) >> 16) & 0xf))
-+              return -EINVAL;
-+
-+      bcma_pcie2_write_config(bdev, 0, 0, PCI_MEMORY_BASE, 2, addr >> 16);
-+      bcma_pcie2_write_config(bdev, 0, 0, PCI_MEMORY_LIMIT, 2,
-+                              (addr + size) >> 16);
-+
-+      /* These registers are not supported on the NS */
-+      bcma_pcie2_write_config(bdev, 0, 0, PCI_IO_BASE_UPPER16, 2, 0);
-+      bcma_pcie2_write_config(bdev, 0, 0, PCI_IO_LIMIT_UPPER16, 2, 0);
-+
-+      /* Force class to that of a Bridge */
-+      bcma_pcie2_write_config(bdev, 0, 0, PCI_CLASS_DEVICE, 2,
-+                              PCI_CLASS_BRIDGE_PCI);
-+
-+      bcma_pcie2_read_config(bdev, 0, 0, PCI_CLASS_DEVICE, 2);
-+      bcma_pcie2_read_config(bdev, 0, 0, PCI_MEMORY_BASE, 2);
-+      bcma_pcie2_read_config(bdev, 0, 0, PCI_MEMORY_LIMIT, 2);
-+      return 0;
-+}
-+
-+static void bcma_pcie2_3rd_init(struct bcma_bus *bus)
-+{
-+      /* PCIE PLL block register (base 0x8000) */
-+      bcma_chipco_b_mii_write(&bus->drv_cc_b, 0x00000088, 0x57fe8000);
-+      /* Check PCIE PLL lock status */
-+      bcma_chipco_b_mii_write(&bus->drv_cc_b, 0x00000088, 0x67c60000);
-+}
-+
-+/* To improve PCIE phy jitter */
-+static void bcma_pcie2_improve_phy_jitter(struct bcma_bus *bus, int phyaddr)
-+{
-+      u32 val;
-+
-+      /* Change blkaddr */
-+      val = (1 << 30) | (1 << 28) | (phyaddr << 23) | (0x1f << 18) |
-+              (2 << 16) | (0x863 << 4);
-+      bcma_chipco_b_mii_write(&bus->drv_cc_b, 0x0000009a, val);
-+
-+      /* Write 0x0190 to 0x13 regaddr */
-+      val = (1 << 30) | (1 << 28) | (phyaddr << 23) | (0x13 << 18) |
-+              (2 << 16) | 0x0190;
-+      bcma_chipco_b_mii_write(&bus->drv_cc_b, 0x0000009a, val);
-+
-+      /* Write 0x0191 to 0x19 regaddr */
-+      val = (1 << 30) | (1 << 28) | (phyaddr << 23) | (0x19 << 18) |
-+              (2 << 16) | 0x0191;
-+      bcma_chipco_b_mii_write(&bus->drv_cc_b, 0x0000009a, val);
-+}
-+
-+static int bcma_pcie2_setup(int nr, struct pci_sys_data *sys)
-+{
-+      struct bcma_device *bdev = sys->private_data;
-+      struct bcma_bus *bus = bdev->bus;
-+      struct resource *res;
-+      struct bcma_device *arm_core;
-+      u32 cru_straps_ctrl;
-+      int ret;
-+      int phyaddr;
-+
-+      if (bdev->core_unit == 2) {
-+              arm_core = bcma_find_core(bus, BCMA_CORE_ARMCA9);
-+              cru_straps_ctrl = bcma_read32(arm_core, 0x2a0);
-+
-+              /* 3rd PCIE is not selected */
-+              if (cru_straps_ctrl & 0x10)
-+                      return -ENODEV;
-+
-+              bcma_pcie2_3rd_init(bus);
-+              phyaddr = 0xf;
-+      } else {
-+              phyaddr = bdev->core_unit;
-+      }
-+      bcma_pcie2_improve_phy_jitter(bus, phyaddr);
-+
-+      /* create mem resource */
-+      res = devm_kzalloc(&bdev->dev, sizeof(*res), GFP_KERNEL);
-+      if (!res)
-+              return -EINVAL;
-+
-+      res->start = bdev->addr_s[0];
-+      res->end = bdev->addr_s[0] + SZ_128M -1;
-+      res->name = "PCIe dummy IO space";
-+      res->flags = IORESOURCE_MEM;
-+
-+      pci_add_resource(&sys->resources, res);
-+
-+      /* This PCIe controller does not support IO Mem, so use a dummy one. */
-+      res = devm_kzalloc(&bdev->dev, sizeof(*res), GFP_KERNEL);
-+      if (!res)
-+              return -EINVAL;
-+
-+      res->start = 0;
-+      res->end = 0;
-+      res->name = "PCIe dummy IO space";
-+      res->flags = IORESOURCE_IO;
-+
-+      pci_add_resource(&sys->resources, res);
-+
-+      bcma_pcie2_hw_init(bdev);
-+      ret = bcma_pcie2_map_init(bdev, bdev->addr_s[0]);
-+      if (ret)
-+              return ret;
-+
-+      /*
-+       * Skip inactive ports -
-+       * will need to change this for hot-plugging
-+       */
-+      ret = bcma_pcie2_check_link(bdev, sys);
-+      if (ret)
-+              return ret;
-+
-+      ret = bcma_pcie2_bridge_init(bdev, bdev->addr_s[0], SZ_128M);
-+      if (ret)
-+              return ret;
-+
-+      return 1;
-+}
-+
-+static int bcma_pcie2_probe(struct bcma_device *bdev)
-+{
-+      struct hw_pci hw = {
-+              .nr_controllers = 1,
-+              .private_data   = (void **)&bdev,
-+              .setup          = bcma_pcie2_setup,
-+              .map_irq        = bcma_pcie2_map_irq,
-+              .ops            = &bcma_pcie2_ops,
-+      };
-+
-+      dev_info(&bdev->dev, "initializing PCIe controller\n");
-+
-+      /* Announce this port to ARM/PCI common code */
-+      pci_common_init_dev(&bdev->dev, &hw);
-+
-+      /* Setup virtual-wire interrupts */
-+      bcma_write32(bdev, BCMA_CORE_PCIE2_SYS_RC_INTX_EN, 0xf);
-+
-+      /* Enable memory and bus master */
-+      bcma_write32(bdev, SOC_PCIE_HDR_OFF + 4, 0x6);
-+
-+      return 0;
-+}
-+
-+static const struct bcma_device_id bcma_pcie2_table[] = {
-+      BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_NS_PCIEG2, BCMA_ANY_REV, BCMA_ANY_CLASS),
-+      {},
-+};
-+MODULE_DEVICE_TABLE(bcma, bcma_pcie2_table);
-+
-+static struct bcma_driver bcma_pcie2_driver = {
-+      .name           = KBUILD_MODNAME,
-+      .id_table       = bcma_pcie2_table,
-+      .probe          = bcma_pcie2_probe,
-+};
-+
-+static int __init bcma_pcie2_init(void)
-+{
-+      return bcma_driver_register(&bcma_pcie2_driver);
-+}
-+module_init(bcma_pcie2_init);
-+
-+static void __exit bcma_pcie2_exit(void)
-+{
-+      bcma_driver_unregister(&bcma_pcie2_driver);
-+}
-+module_exit(bcma_pcie2_exit);
-+
-+MODULE_AUTHOR("Hauke Mehrtens");
-+MODULE_DESCRIPTION("BCM5301X PCIe host controller");
-+MODULE_LICENSE("GPLv2");