diff options
author | Hauke Mehrtens <hauke@hauke-m.de> | 2015-06-25 21:56:34 +0000 |
---|---|---|
committer | Hauke Mehrtens <hauke@hauke-m.de> | 2015-06-25 21:56:34 +0000 |
commit | 164e82099da1c0626514710ad119e401cd369a0d (patch) | |
tree | b9aa7aefe177d19d49f67886b194f0e560b3ccea /target/linux | |
parent | ca49bcadae57798588a8f4c50916f71b1da2b86d (diff) | |
download | upstream-164e82099da1c0626514710ad119e401cd369a0d.tar.gz upstream-164e82099da1c0626514710ad119e401cd369a0d.tar.bz2 upstream-164e82099da1c0626514710ad119e401cd369a0d.zip |
bcm53xx: add upstream PCIe driver
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
Diffstat (limited to 'target/linux')
6 files changed, 379 insertions, 536 deletions
diff --git a/target/linux/bcm53xx/config-4.1 b/target/linux/bcm53xx/config-4.1 index 8daa3aa2a5..49cdb2173f 100644 --- a/target/linux/bcm53xx/config-4.1 +++ b/target/linux/bcm53xx/config-4.1 @@ -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 index 0000000000..9050f72451 --- /dev/null +++ b/target/linux/bcm53xx/patches-4.1/030-PCI-iproc-Allow-override-of-device-tree-IRQ-mapping-.patch @@ -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 index 0000000000..a850bafd1b --- /dev/null +++ b/target/linux/bcm53xx/patches-4.1/031-PCI-iproc-Add-BCMA-PCIe-driver.patch @@ -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 index 0000000000..09d82263a9 --- /dev/null +++ b/target/linux/bcm53xx/patches-4.1/032-PCI-iproc-Directly-add-PCI-resources.patch @@ -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 index 0000000000..bbd3164eb2 --- /dev/null +++ b/target/linux/bcm53xx/patches-4.1/033-PCI-iproc-Free-resource-list-after-registration.patch @@ -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 index d4dedc0365..0000000000 --- a/target/linux/bcm53xx/patches-4.1/170-pcie2-bcma-add-new-PCIe2-driver-for-bcma.patch +++ /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"); |