bcm53xx: update the PCIe driver

This adds some updates to the PCIe driver and refreshed the config.

Most of these changes are done in preparation for mainling it.

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

SVN-Revision: 43545
This commit is contained in:
Hauke Mehrtens 2014-12-07 21:54:53 +00:00
parent d9bb8326fa
commit 664ae86dde
4 changed files with 290 additions and 632 deletions

View file

@ -110,7 +110,6 @@ CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
CONFIG_GENERIC_IDLE_POLL_SETUP=y CONFIG_GENERIC_IDLE_POLL_SETUP=y
CONFIG_GENERIC_IO=y CONFIG_GENERIC_IO=y
CONFIG_GENERIC_IRQ_SHOW=y CONFIG_GENERIC_IRQ_SHOW=y
CONFIG_GENERIC_NET_UTILS=y
CONFIG_GENERIC_PCI_IOMAP=y CONFIG_GENERIC_PCI_IOMAP=y
CONFIG_GENERIC_SCHED_CLOCK=y CONFIG_GENERIC_SCHED_CLOCK=y
CONFIG_GENERIC_SMP_IDLE_THREAD=y CONFIG_GENERIC_SMP_IDLE_THREAD=y
@ -220,7 +219,7 @@ CONFIG_OUTER_CACHE_SYNC=y
CONFIG_PAGEFLAGS_EXTENDED=y CONFIG_PAGEFLAGS_EXTENDED=y
CONFIG_PAGE_OFFSET=0xC0000000 CONFIG_PAGE_OFFSET=0xC0000000
CONFIG_PCI=y CONFIG_PCI=y
CONFIG_PCI_BCMA=y CONFIG_PCI_BCM5301X=y
CONFIG_PCI_DOMAINS=y CONFIG_PCI_DOMAINS=y
CONFIG_PERF_USE_VMALLOC=y CONFIG_PERF_USE_VMALLOC=y
CONFIG_PHYLIB=y CONFIG_PHYLIB=y

View file

@ -10,18 +10,13 @@ CONFIG_ARCH_HAS_SG_CHAIN=y
CONFIG_ARCH_HAS_TICK_BROADCAST=y CONFIG_ARCH_HAS_TICK_BROADCAST=y
CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y
CONFIG_ARCH_HIBERNATION_POSSIBLE=y CONFIG_ARCH_HIBERNATION_POSSIBLE=y
# CONFIG_ARCH_HISI is not set
# CONFIG_ARCH_MEDIATEK is not set
# CONFIG_ARCH_MESON is not set
CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y
# CONFIG_ARCH_MSM is not set
CONFIG_ARCH_MULTIPLATFORM=y CONFIG_ARCH_MULTIPLATFORM=y
# CONFIG_ARCH_MULTI_CPU_AUTO is not set # CONFIG_ARCH_MULTI_CPU_AUTO is not set
CONFIG_ARCH_MULTI_V6_V7=y CONFIG_ARCH_MULTI_V6_V7=y
CONFIG_ARCH_MULTI_V7=y CONFIG_ARCH_MULTI_V7=y
# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set # CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set
CONFIG_ARCH_NR_GPIO=0 CONFIG_ARCH_NR_GPIO=0
# CONFIG_ARCH_QCOM is not set
# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set # CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set # CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y
@ -122,7 +117,6 @@ CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
CONFIG_GENERIC_IDLE_POLL_SETUP=y CONFIG_GENERIC_IDLE_POLL_SETUP=y
CONFIG_GENERIC_IO=y CONFIG_GENERIC_IO=y
CONFIG_GENERIC_IRQ_SHOW=y CONFIG_GENERIC_IRQ_SHOW=y
CONFIG_GENERIC_NET_UTILS=y
CONFIG_GENERIC_PCI_IOMAP=y CONFIG_GENERIC_PCI_IOMAP=y
CONFIG_GENERIC_SCHED_CLOCK=y CONFIG_GENERIC_SCHED_CLOCK=y
CONFIG_GENERIC_SMP_IDLE_THREAD=y CONFIG_GENERIC_SMP_IDLE_THREAD=y
@ -204,7 +198,6 @@ CONFIG_MTD_NAND_ECC=y
# CONFIG_MTD_PHYSMAP_OF is not set # CONFIG_MTD_PHYSMAP_OF is not set
CONFIG_MTD_SPI_BCM53XXSPIFLASH=y CONFIG_MTD_SPI_BCM53XXSPIFLASH=y
CONFIG_MTD_SPI_NOR=y CONFIG_MTD_SPI_NOR=y
# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set
CONFIG_MTD_UBI=y CONFIG_MTD_UBI=y
CONFIG_MTD_UBI_BEB_LIMIT=20 CONFIG_MTD_UBI_BEB_LIMIT=20
CONFIG_MTD_UBI_BLOCK=y CONFIG_MTD_UBI_BLOCK=y
@ -237,7 +230,7 @@ CONFIG_OUTER_CACHE_SYNC=y
CONFIG_PAGEFLAGS_EXTENDED=y CONFIG_PAGEFLAGS_EXTENDED=y
CONFIG_PAGE_OFFSET=0xC0000000 CONFIG_PAGE_OFFSET=0xC0000000
CONFIG_PCI=y CONFIG_PCI=y
CONFIG_PCI_BCMA=y CONFIG_PCI_BCM5301X=y
CONFIG_PCI_DOMAINS=y CONFIG_PCI_DOMAINS=y
CONFIG_PERF_USE_VMALLOC=y CONFIG_PERF_USE_VMALLOC=y
CONFIG_PHYLIB=y CONFIG_PHYLIB=y

View file

@ -1,19 +1,43 @@
From cc2cda651fcbc498bf513a6b802dca19944bcb37 Mon Sep 17 00:00:00 2001 From cf067bf8bb993d6cfdc42d750ae241c43f88403f Mon Sep 17 00:00:00 2001
From: Hauke Mehrtens <hauke@hauke-m.de> From: Hauke Mehrtens <hauke@hauke-m.de>
Date: Mon, 12 May 2014 11:55:20 +0200 Date: Mon, 12 May 2014 11:55:20 +0200
Subject: [PATCH 13/17] pcie2-bcma: add new PCIe2 driver for bcma Subject: [PATCH 1/2] PCI: BCM5301X: add PCIe2 driver for BCM5301X SoCs
This driver supports the PCIe controller found on the BCM4708 and This driver supports the PCIe controller found on the BCM4708 and
similar SoCs. The controller itself is automatically detected by bcma. 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> Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
--- ---
arch/arm/mach-bcm/Kconfig | 2 + arch/arm/mach-bcm/Kconfig | 1 +
drivers/pci/host/Kconfig | 7 + drivers/pci/host/Kconfig | 7 +
drivers/pci/host/Makefile | 1 + drivers/pci/host/Makefile | 1 +
drivers/pci/host/pcie2-bcma.c | 591 ++++++++++++++++++++++++++++++++++++++++++ drivers/pci/host/pci-host-bcm5301x.c | 428 +++++++++++++++++++++++++++++++++++
4 files changed, 601 insertions(+) 4 files changed, 437 insertions(+)
create mode 100644 drivers/pci/host/pcie2-bcma.c create mode 100644 drivers/pci/host/pci-host-bcm5301x.c
--- a/arch/arm/mach-bcm/Kconfig --- a/arch/arm/mach-bcm/Kconfig
+++ b/arch/arm/mach-bcm/Kconfig +++ b/arch/arm/mach-bcm/Kconfig
@ -31,12 +55,12 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
There are 3 internal PCI controllers available with a single There are 3 internal PCI controllers available with a single
built-in EHCI/OHCI host controller present on each one. built-in EHCI/OHCI host controller present on each one.
+config PCI_BCMA +config PCI_BCM5301X
+ bool "BCMA PCIe2 host controller" + bool "BCM5301X PCIe2 host controller"
+ depends on BCMA && OF + depends on BCMA && OF && ARM && PCI_DOMAINS
+ help + help
+ Say Y here if you want to support a simple generic PCI host + Say Y here if you want to support the PCIe host controller found
+ controller, such as the one emulated by kvmtool. + on Broadcom BCM5301X and BCM470X (Northstar) SoCs.
+ +
endmenu endmenu
--- a/drivers/pci/host/Makefile --- a/drivers/pci/host/Makefile
@ -45,10 +69,10 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o
obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o
obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o
+obj-$(CONFIG_PCI_BCMA) += pcie2-bcma.o +obj-$(CONFIG_PCI_BCM5301X) += pci-host-bcm5301x.o
--- /dev/null --- /dev/null
+++ b/drivers/pci/host/pcie2-bcma.c +++ b/drivers/pci/host/pci-host-bcm5301x.c
@@ -0,0 +1,619 @@ @@ -0,0 +1,428 @@
+/* +/*
+ * Northstar PCI-Express driver + * Northstar PCI-Express driver
+ * Only supports Root-Complex (RC) mode + * Only supports Root-Complex (RC) mode
@ -58,63 +82,25 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+ * + *
+ * Only MEM access is supported, PAX does not support IO. + * Only MEM access is supported, PAX does not support IO.
+ * + *
+ * TODO: + * Copyright 2012-2014, Broadcom Corporation
+ * MSI interrupts, + * Copyright 2014, Hauke Mehrtens <hauke@hauke-m.de>
+ * DRAM > 128 MBytes (e.g. DMA zones) + *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */ + */
+ +
+#include <linux/kernel.h> +#include <linux/kernel.h>
+#include <linux/module.h> +#include <linux/module.h>
+#include <linux/bug.h>
+#include <linux/delay.h> +#include <linux/delay.h>
+#include <linux/pci.h> +#include <linux/pci.h>
+#include <linux/io.h> +#include <linux/io.h>
+#include <linux/ioport.h> +#include <linux/ioport.h>
+#include <linux/interrupt.h> +#include <linux/interrupt.h>
+#include <linux/bcma/bcma.h> +#include <linux/bcma/bcma.h>
+ +#include <linux/bcma/bcma_driver_pcie2.h>
+#define SI_ENUM_BASE 0x18000000 /* Enumeration space base */ +#include <linux/of_irq.h>
+
+/*
+ * Register offset definitions
+ */
+#define SOC_PCIE_CONTROL 0x000 /* a.k.a. CLK_CONTROL reg */
+#define SOC_PCIE_PM_STATUS 0x008
+#define SOC_PCIE_PM_CONTROL 0x00c /* in EP mode only ! */
+
+#define SOC_PCIE_EXT_CFG_ADDR 0x120
+#define SOC_PCIE_EXT_CFG_DATA 0x124
+#define SOC_PCIE_CFG_ADDR 0x1f8
+#define SOC_PCIE_CFG_DATA 0x1fc
+
+#define SOC_PCIE_SYS_RC_INTX_EN 0x330
+#define SOC_PCIE_SYS_RC_INTX_CSR 0x334
+#define SOC_PCIE_SYS_HOST_INTR_EN 0x344
+#define SOC_PCIE_SYS_HOST_INTR_CSR 0x348
+ +
+#define SOC_PCIE_HDR_OFF 0x400 /* 256 bytes per function */ +#define SOC_PCIE_HDR_OFF 0x400 /* 256 bytes per function */
+ +
+/* 32-bit 4KB in-bound mapping windows for Function 0..3, n=0..7 */
+#define SOC_PCIE_SYS_IMAP0(f, n) (0xc00 + ((f) << 9)((n) << 2))
+/* 64-bit in-bound mapping windows for func 0..3 */
+#define SOC_PCIE_SYS_IMAP1(f) (0xc80 + ((f) << 3))
+#define SOC_PCIE_SYS_IMAP2(f) (0xcc0 + ((f) << 3))
+/* 64-bit in-bound address range n=0..2 */
+#define SOC_PCIE_SYS_IARR(n) (0xd00 + ((n) << 3))
+/* 64-bit out-bound address filter n=0..2 */
+#define SOC_PCIE_SYS_OARR(n) (0xd20 + ((n) << 3))
+/* 64-bit out-bound mapping windows n=0..2 */
+#define SOC_PCIE_SYS_OMAP(n) (0xd40 + ((n) << 3))
+
+#define BCM4360_D11AC_ID 0x43a0
+#define BCM4360_D11AC2G_ID 0x43a1
+#define BCM4360_D11AC5G_ID 0x43a2
+#define BCM4352_D11AC_ID 0x43b1 /* 4352 802.11ac dualband device */
+#define BCM4352_D11AC2G_ID 0x43b2 /* 4352 802.11ac 2.4G device */
+#define BCM4352_D11AC5G_ID 0x43b3 /* 4352 802.11ac 5G device */
+
+static struct pci_ops bcma_pcie2_ops;
+
+static int bcma_pcie2_map_irq(const struct pci_dev *pdev, u8 slot, u8 pin) +static int bcma_pcie2_map_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
+{ +{
+ struct pci_sys_data *sys = pdev->sysdata; + struct pci_sys_data *sys = pdev->sysdata;
@ -133,16 +119,17 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+ if (busno == 0) { + if (busno == 0) {
+ if (slot >= 1) + if (slot >= 1)
+ return 0; + return 0;
+ bcma_write32(bdev, SOC_PCIE_EXT_CFG_ADDR, where & 0xffc); + bcma_write32(bdev, BCMA_CORE_PCIE2_CONFIGINDADDR,
+ return SOC_PCIE_EXT_CFG_DATA; + where & 0xffc);
+ return BCMA_CORE_PCIE2_CONFIGINDDATA;
+ } + }
+ if (fn > 1) + if (fn > 1)
+ return 0; + return 0;
+ addr_reg = (busno & 0xff) << 20 | (slot << 15) | (fn << 12) | + addr_reg = (busno & 0xff) << 20 | (slot << 15) | (fn << 12) |
+ (where & 0xffc) | (1 & 0x3); + (where & 0xffc) | (1 & 0x3);
+ +
+ bcma_write32(bdev, SOC_PCIE_CFG_ADDR, addr_reg); + bcma_write32(bdev, BCMA_CORE_PCIE2_CFG_ADDR, addr_reg);
+ return SOC_PCIE_CFG_DATA; + return BCMA_CORE_PCIE2_CFG_DATA;
+} +}
+ +
+static u32 bcma_pcie2_read_config(struct bcma_device *bdev, int busno, +static u32 bcma_pcie2_read_config(struct bcma_device *bdev, int busno,
@ -160,20 +147,6 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+ +
+ data_reg = bcma_read32(bdev, base); + data_reg = bcma_read32(bdev, base);
+ +
+ /* NS: CLASS field is R/O, and set to wrong 0x200 value */
+ if (busno == 0 && devfn == 0) {
+ /*
+ * RC's class is 0x0280, but Linux PCI driver needs 0x604
+ * for a PCIe bridge. So we must fixup the class code
+ * to 0x604 here.
+ */
+ if ((where & 0xffc) == PCI_CLASS_REVISION) {
+ data_reg &= 0xff;
+ data_reg |= 0x604 << 16;
+ }
+ }
+ /* HEADER_TYPE=00 indicates the port in EP mode */
+
+ if (size == 4) + if (size == 4)
+ return data_reg; + return data_reg;
+ +
@ -208,42 +181,6 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+ bcma_write32(bdev, base, data_reg); + bcma_write32(bdev, base, data_reg);
+} +}
+ +
+static u8 bcma_pcie2_read_config8(struct bcma_device *bdev, int busno,
+ unsigned int devfn, int where)
+{
+ return bcma_pcie2_read_config(bdev, busno, devfn, where, 1);
+}
+
+static u16 bcma_pcie2_read_config16(struct bcma_device *bdev, int busno,
+ unsigned int devfn, int where)
+{
+ return bcma_pcie2_read_config(bdev, busno, devfn, where, 2);
+}
+
+static u32 bcma_pcie2_read_config32(struct bcma_device *bdev, int busno,
+ unsigned int devfn, int where)
+{
+ return bcma_pcie2_read_config(bdev, busno, devfn, where, 4);
+}
+
+static void bcma_pcie2_write_config8(struct bcma_device *bdev, int busno,
+ unsigned int devfn, int where, u8 val)
+{
+ return bcma_pcie2_write_config(bdev, busno, devfn, where, 1, val);
+}
+
+static void bcma_pcie2_write_config16(struct bcma_device *bdev, int busno,
+ unsigned int devfn, int where, u16 val)
+{
+ return bcma_pcie2_write_config(bdev, busno, devfn, where, 2, val);
+}
+
+static void bcma_pcie2_write_config32(struct bcma_device *bdev, int busno,
+ unsigned int devfn, int where, u32 val)
+{
+ return bcma_pcie2_write_config(bdev, busno, devfn, where, 4, val);
+}
+
+static int bcma_pcie2_read_config_pci(struct pci_bus *bus, unsigned int devfn, +static int bcma_pcie2_read_config_pci(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 *val) + int where, int size, u32 *val)
+{ +{
@ -267,31 +204,31 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+} +}
+ +
+/* +/*
+ * 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);
+
+/*
+ * Check link status, return 0 if link is up in RC mode, + * Check link status, return 0 if link is up in RC mode,
+ * otherwise return non-zero + * otherwise return non-zero
+ */ + */
+static int bcma_pcie2_check_link(struct bcma_device *bdev, +static int bcma_pcie2_check_link(struct bcma_device *bdev, bool allow_gen2)
+ struct pci_sys_data *sys, u32 allow_gen2)
+{ +{
+ u32 devfn = 0; + u32 devfn = 0;
+ u32 tmp32; + u32 tmp32;
+ u16 tmp16;
+ u8 tmp8; + u8 tmp8;
+ int pos;
+ bool link = false;
+ /*
+ * 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_pcie2_read_config32(bdev, 0, devfn, 0xdc); + tmp32 = bcma_pcie2_read_config(bdev, 0, devfn, 0xdc, 4);
+ tmp32 &= ~0xf; + tmp32 &= ~0xf;
+ if (allow_gen2) + if (allow_gen2)
+ tmp32 |= 2; + tmp32 |= 2;
@ -299,28 +236,17 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+ /* force PCIE GEN1 */ + /* force PCIE GEN1 */
+ tmp32 |= 1; + tmp32 |= 1;
+ } + }
+ bcma_pcie2_write_config32(bdev, 0, devfn, 0xdc, tmp32); + bcma_pcie2_write_config(bdev, 0, devfn, 0xdc, 4, tmp32);
+ +
+ /* See if the port is in EP mode, indicated by header type 00 */ + /* See if the port is in EP mode, indicated by header type 00 */
+ tmp8 = bcma_pcie2_read_config8(bdev, 0, devfn, PCI_HEADER_TYPE); + tmp8 = bcma_pcie2_read_config(bdev, 0, devfn, PCI_HEADER_TYPE, 1);
+ if (tmp8 != PCI_HEADER_TYPE_BRIDGE) { + if (tmp8 != PCI_HEADER_TYPE_BRIDGE) {
+ dev_info(&bdev->dev, "Port %d in End-Point mode - ignored\n", + dev_info(&bdev->dev, "Port %d in End-Point mode - ignored\n",
+ bdev->core_unit); + bdev->core_unit);
+ return -ENODEV; + return -ENODEV;
+ } + }
+ +
+ /* NS PAX only changes NLW field when card is present */ + return 0;
+ pos = pci_bus_find_capability(&bus, devfn, PCI_CAP_ID_EXP);
+ if (pos) {
+ u8 nlw;
+
+ pci_bus_read_config_word(&bus, devfn, pos + PCI_EXP_LNKSTA,
+ &tmp16);
+ nlw = (tmp16 & PCI_EXP_LNKSTA_NLW) >> PCI_EXP_LNKSTA_NLW_SHIFT;
+ link = (tmp16 & PCI_EXP_LNKSTA_DLLLA) || nlw != 0;
+ }
+
+ return link ? 0 : -ENODEV;
+} +}
+ +
+/* +/*
@ -328,67 +254,52 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+ */ + */
+static void bcma_pcie2_hw_init(struct bcma_device *bdev) +static void bcma_pcie2_hw_init(struct bcma_device *bdev)
+{ +{
+ u32 devfn = 0;
+ u32 tmp32; + u32 tmp32;
+ u16 tmp16; + u16 tmp16;
+ +
+ /* Change MPS and MRRS to 512 */ + /* Change MPS and MRRS to 512 */
+ tmp16 = bcma_pcie2_read_config16(bdev, 0, devfn, 0x4d4); + tmp16 = bcma_pcie2_read_config(bdev, 0, 0, 0x4d4, 2);
+ tmp16 &= ~7; + tmp16 &= ~7;
+ tmp16 |= 2; + tmp16 |= 2;
+ bcma_pcie2_write_config16(bdev, 0, devfn, 0x4d4, tmp16); + bcma_pcie2_write_config(bdev, 0, 0, 0x4d4, 2, tmp16);
+ +
+ tmp32 = bcma_pcie2_read_config32(bdev, 0, devfn, 0xb4); + tmp32 = bcma_pcie2_read_config(bdev, 0, 0, 0xb4, 4);
+ tmp32 &= ~((7 << 12) | (7 << 5)); + tmp32 &= ~((7 << 12) | (7 << 5));
+ tmp32 |= (2 << 12) | (2 << 5); + tmp32 |= (2 << 12) | (2 << 5);
+ bcma_pcie2_write_config32(bdev, 0, devfn, 0xb4, tmp32); + bcma_pcie2_write_config(bdev, 0, 0, 0xb4, 4, tmp32);
+ +
+ /* Turn-on Root-Complex (RC) mode, from reset defailt of EP */ + /*
+ + * Turn-on Root-Complex (RC) mode, from reset default of EP
+ /* The mode is set by straps, can be overwritten via DMU + * The mode is set by straps, can be overwritten via DMU
+ register <cru_straps_control> bit 5, "1" means RC + * register <cru_straps_control> bit 5, "1" means RC
+ */ + */
+ +
+ /* Send a downstream reset */ + /* Send a downstream reset */
+ bcma_write32(bdev, SOC_PCIE_CONTROL, 0x3); + bcma_write32(bdev, BCMA_CORE_PCIE2_CLK_CONTROL,
+ udelay(250); + PCIE2_CLKC_RST_OE | PCIE2_CLKC_RST);
+ bcma_write32(bdev, SOC_PCIE_CONTROL, 0x1); + usleep_range(250, 400);
+ mdelay(250); + bcma_write32(bdev, BCMA_CORE_PCIE2_CLK_CONTROL, PCIE2_CLKC_RST_OE);
+ msleep(250);
+ +
+ /* TBD: take care of PM, check we're on */ + /* TBD: take care of PM, check we're on */
+} +}
+ +
+/* +/*
+ * Setup the address translation + * Setup the address translation
+ *
+ * NOTE: All PCI-to-CPU address mapping are 1:1 for simplicity
+ */ + */
+static void bcma_pcie2_map_init(struct bcma_device *bdev) +static int bcma_pcie2_map_init(struct bcma_device *bdev, u32 addr)
+{ +{
+ unsigned size, i; + /* 64MB alignment */
+ u32 addr; + if (!addr || (addr & (SZ_64M - 1)))
+ return -EINVAL;
+ +
+ /* + bcma_write32(bdev, BCMA_CORE_PCIE2_OMAP0_LOWER, addr);
+ * NOTE: + bcma_write32(bdev, BCMA_CORE_PCIE2_OARR0, addr | 0x01);
+ * All PCI-to-CPU address mapping are 1:1 for simplicity
+ */
+ +
+ /* Outbound address translation setup */ + bcma_write32(bdev, BCMA_CORE_PCIE2_OMAP1_LOWER, addr + SZ_64M);
+ size = SZ_128M; + bcma_write32(bdev, BCMA_CORE_PCIE2_OARR1, (addr + SZ_64M) | 0x01);
+ addr = bdev->addr_s[0];
+ BUG_ON(!addr);
+ BUG_ON(addr & ((1 << 25) - 1)); /* 64MB alignment */
+
+ for (i = 0; i < 3; i++) {
+ const unsigned win_size = SZ_64M;
+ /* 64-bit LE regs, write low word, high is 0 at reset */
+ bcma_write32(bdev, SOC_PCIE_SYS_OMAP(i), addr);
+ bcma_write32(bdev, SOC_PCIE_SYS_OARR(i), addr|0x1);
+ addr += win_size;
+ if (size >= win_size)
+ size -= win_size;
+ if (size == 0)
+ break;
+ }
+ WARN_ON(size > 0);
+ +
+ /* + /*
+ * Inbound address translation setup + * Inbound address translation setup
@ -398,107 +309,46 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+ * otherwise DMA bouncing mechanism may be required. + * otherwise DMA bouncing mechanism may be required.
+ * Also consider DMA mask to limit DMA physical address + * Also consider DMA mask to limit DMA physical address
+ */ + */
+ size = SZ_128M;
+ addr = PHYS_OFFSET;
+
+ size >>= 20; /* In MB */
+ size &= 0xff; /* Size is an 8-bit field */
+
+ WARN_ON(size == 0);
+ /* 64-bit LE regs, write low word, high is 0 at reset */ + /* 64-bit LE regs, write low word, high is 0 at reset */
+ bcma_write32(bdev, SOC_PCIE_SYS_IMAP1(0), addr | 0x1); + bcma_write32(bdev, BCMA_CORE_PCIE2_FUNC0_IMAP1, PHYS_OFFSET | 0x1);
+ bcma_write32(bdev, SOC_PCIE_SYS_IARR(1), addr | size); + bcma_write32(bdev, BCMA_CORE_PCIE2_IARR1_LOWER,
+ + PHYS_OFFSET | ((SZ_128M >> 20) & 0xff));
+#ifdef CONFIG_SPARSEMEM + return 0;
+ addr = PHYS_OFFSET2;
+ bcma_write32(bdev, SOC_PCIE_SYS_IMAP2(0), addr | 0x1);
+ bcma_write32(bdev, SOC_PCIE_SYS_IARR(2), addr | size);
+#endif
+} +}
+ +
+/* +/*
+ * Setup PCIE Host bridge + * Setup PCIE Host bridge
+ */ + */
+static void bcma_pcie2_bridge_init(struct bcma_device *bdev) +static int bcma_pcie2_bridge_init(struct bcma_device *bdev, u32 addr, u32 size)
+{ +{
+ u32 devfn = 0; + bcma_pcie2_write_config(bdev, 0, 0, PCI_PRIMARY_BUS, 1, 0);
+ u8 tmp8; + bcma_pcie2_write_config(bdev, 0, 0, PCI_SECONDARY_BUS, 1, 1);
+ u16 tmp16; + bcma_pcie2_write_config(bdev, 0, 0, PCI_SUBORDINATE_BUS, 1, 4);
+ +
+ bcma_pcie2_write_config8(bdev, 0, devfn, PCI_PRIMARY_BUS, 0); + bcma_pcie2_read_config(bdev, 0, 0, PCI_PRIMARY_BUS, 1);
+ bcma_pcie2_write_config8(bdev, 0, devfn, PCI_SECONDARY_BUS, 1); + bcma_pcie2_read_config(bdev, 0, 0, PCI_SECONDARY_BUS, 1);
+ bcma_pcie2_write_config8(bdev, 0, devfn, PCI_SUBORDINATE_BUS, 4); + bcma_pcie2_read_config(bdev, 0, 0, PCI_SUBORDINATE_BUS, 1);
+
+ tmp8 = bcma_pcie2_read_config8(bdev, 0, devfn, PCI_PRIMARY_BUS);
+ tmp8 = bcma_pcie2_read_config8(bdev, 0, devfn, PCI_SECONDARY_BUS);
+ tmp8 = bcma_pcie2_read_config8(bdev, 0, devfn, PCI_SUBORDINATE_BUS);
+ +
+ /* MEM_BASE, MEM_LIM require 1MB alignment */ + /* MEM_BASE, MEM_LIM require 1MB alignment */
+ BUG_ON((bdev->addr_s[0] >> 16) & 0xf); + if (((addr >> 16) & 0xf) || (((addr + size) >> 16) & 0xf))
+ bcma_pcie2_write_config16(bdev, 0, devfn, PCI_MEMORY_BASE, + return -EINVAL;
+ bdev->addr_s[0] >> 16); +
+ BUG_ON(((bdev->addr_s[0] + SZ_128M) >> 16) & 0xf); + bcma_pcie2_write_config(bdev, 0, 0, PCI_MEMORY_BASE, 2, addr >> 16);
+ bcma_pcie2_write_config16(bdev, 0, devfn, PCI_MEMORY_LIMIT, + bcma_pcie2_write_config(bdev, 0, 0, PCI_MEMORY_LIMIT, 2,
+ (bdev->addr_s[0] + SZ_128M) >> 16); + (addr + size) >> 16);
+ +
+ /* These registers are not supported on the NS */ + /* These registers are not supported on the NS */
+ bcma_pcie2_write_config16(bdev, 0, devfn, PCI_IO_BASE_UPPER16, 0); + bcma_pcie2_write_config(bdev, 0, 0, PCI_IO_BASE_UPPER16, 2, 0);
+ bcma_pcie2_write_config16(bdev, 0, devfn, PCI_IO_LIMIT_UPPER16, 0); + bcma_pcie2_write_config(bdev, 0, 0, PCI_IO_LIMIT_UPPER16, 2, 0);
+ +
+ /* Force class to that of a Bridge */ + /* Force class to that of a Bridge */
+ bcma_pcie2_write_config16(bdev, 0, devfn, PCI_CLASS_DEVICE, + bcma_pcie2_write_config(bdev, 0, 0, PCI_CLASS_DEVICE, 2,
+ PCI_CLASS_BRIDGE_PCI); + PCI_CLASS_BRIDGE_PCI);
+ +
+ tmp16 = bcma_pcie2_read_config16(bdev, 0, devfn, PCI_CLASS_DEVICE); + bcma_pcie2_read_config(bdev, 0, 0, PCI_CLASS_DEVICE, 2);
+ tmp16 = bcma_pcie2_read_config16(bdev, 0, devfn, PCI_MEMORY_BASE); + bcma_pcie2_read_config(bdev, 0, 0, PCI_MEMORY_BASE, 2);
+ tmp16 = bcma_pcie2_read_config16(bdev, 0, devfn, PCI_MEMORY_LIMIT); + bcma_pcie2_read_config(bdev, 0, 0, PCI_MEMORY_LIMIT, 2);
+} + return 0;
+
+static int bcma_pcie2_allow_gen2_rc(struct bcma_device *bdev)
+{
+ u32 vendorid, devid, chipid, chiprev;
+ u32 val, bar;
+ void __iomem *base;
+ int allow = 1;
+
+ /* Read PCI vendor/device ID's */
+ bcma_write32(bdev, SOC_PCIE_CFG_ADDR, 0x0);
+ val = bcma_read32(bdev, SOC_PCIE_CFG_DATA);
+ vendorid = val & 0xffff;
+ devid = val >> 16;
+ if (vendorid == PCI_VENDOR_ID_BROADCOM &&
+ (devid == BCMA_CHIP_ID_BCM4360 || devid == BCM4360_D11AC_ID ||
+ devid == BCM4360_D11AC2G_ID || devid == BCM4360_D11AC5G_ID ||
+ devid == BCM4352_D11AC_ID || devid == BCM4352_D11AC2G_ID ||
+ devid == BCM4352_D11AC5G_ID)) {
+ /* Config BAR0 */
+ bar = bdev->addr_s[0];
+ bcma_write32(bdev, SOC_PCIE_CFG_ADDR, 0x10);
+ bcma_write32(bdev, SOC_PCIE_CFG_DATA, bar);
+ /* Config BAR0 window to access chipc */
+ bcma_write32(bdev, SOC_PCIE_CFG_ADDR, 0x80);
+ bcma_write32(bdev, SOC_PCIE_CFG_DATA, SI_ENUM_BASE);
+
+ /* Enable memory resource */
+ bcma_write32(bdev, SOC_PCIE_CFG_ADDR, 0x4);
+ val = bcma_read32(bdev, SOC_PCIE_CFG_DATA);
+ val |= PCI_COMMAND_MEMORY;
+ bcma_write32(bdev, SOC_PCIE_CFG_DATA, val);
+ /* Enable memory and bus master */
+ bcma_write32(bdev, SOC_PCIE_HDR_OFF + 4, 0x6);
+
+ /* Read CHIP ID */
+ base = ioremap(bar, 0x1000);
+ val = __raw_readl(base);
+ iounmap(base);
+ chipid = val & 0xffff;
+ chiprev = (val >> 16) & 0xf;
+ if ((chipid == BCMA_CHIP_ID_BCM4360 ||
+ chipid == BCMA_CHIP_ID_BCM43460 ||
+ chipid == BCMA_CHIP_ID_BCM4352) && (chiprev < 3))
+ allow = 0;
+ }
+ return allow;
+} +}
+ +
+static void bcma_pcie2_3rd_init(struct bcma_bus *bus) +static void bcma_pcie2_3rd_init(struct bcma_bus *bus)
@ -537,7 +387,7 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+ struct resource *res; + struct resource *res;
+ struct bcma_device *arm_core; + struct bcma_device *arm_core;
+ u32 cru_straps_ctrl; + u32 cru_straps_ctrl;
+ int allow_gen2, linkfail; + int ret;
+ int phyaddr; + int phyaddr;
+ +
+ if (bdev->core_unit == 2) { + if (bdev->core_unit == 2) {
@ -561,8 +411,8 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+ return -EINVAL; + return -EINVAL;
+ +
+ res->start = bdev->addr_s[0]; + res->start = bdev->addr_s[0];
+ res->end = res->start + SZ_128M - 1; + res->end = bdev->addr_s[0] + SZ_128M -1;
+ res->name = "PCIe Configuration Space"; + res->name = "PCIe dummy IO space";
+ res->flags = IORESOURCE_MEM; + res->flags = IORESOURCE_MEM;
+ +
+ pci_add_resource(&sys->resources, res); + pci_add_resource(&sys->resources, res);
@ -572,55 +422,36 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+ if (!res) + if (!res)
+ return -EINVAL; + return -EINVAL;
+ +
+ res->start = bdev->addr_s[0]; + res->start = 0;
+ res->end = res->start + SZ_128M - 1; + res->end = 0;
+ res->name = "PCIe Configuration Space"; + res->name = "PCIe dummy IO space";
+ res->flags = IORESOURCE_IO; + res->flags = IORESOURCE_IO;
+ +
+ pci_add_resource(&sys->resources, res); + pci_add_resource(&sys->resources, res);
+ +
+ for (allow_gen2 = 0; allow_gen2 <= 1; allow_gen2++) { + bcma_pcie2_hw_init(bdev);
+ bcma_pcie2_hw_init(bdev); + ret = bcma_pcie2_map_init(bdev, bdev->addr_s[0]);
+ bcma_pcie2_map_init(bdev); + if (ret)
+ return ret;
+ +
+ /* + /*
+ * Skip inactive ports - + * Skip inactive ports -
+ * will need to change this for hot-plugging + * will need to change this for hot-plugging
+ */ + */
+ linkfail = bcma_pcie2_check_link(bdev, sys, allow_gen2); + ret = bcma_pcie2_check_link(bdev, true);
+ if (linkfail) + if (ret)
+ break; + return ret;
+ +
+ bcma_pcie2_bridge_init(bdev); + ret = bcma_pcie2_bridge_init(bdev, bdev->addr_s[0], SZ_128M);
+ + if (ret)
+ if (allow_gen2 == 0) { + return ret;
+ if (bcma_pcie2_allow_gen2_rc(bdev) == 0)
+ break;
+ dev_info(&bdev->dev, "switching to GEN2\n");
+ }
+ }
+
+ if (linkfail)
+ return -1;
+ +
+ return 1; + return 1;
+} +}
+ +
+/*
+ * Methods for accessing configuration registers
+ */
+static struct pci_ops bcma_pcie2_ops = {
+ .read = bcma_pcie2_read_config_pci,
+ .write = bcma_pcie2_write_config_pci,
+};
+
+static int bcma_pcie2_probe(struct bcma_device *bdev) +static int bcma_pcie2_probe(struct bcma_device *bdev)
+{ +{
+ struct hw_pci hw; + struct hw_pci hw = {
+
+ dev_info(&bdev->dev, "scanning bus\n");
+
+ hw = (struct hw_pci) {
+ .nr_controllers = 1, + .nr_controllers = 1,
+ .domain = bdev->core_unit, + .domain = bdev->core_unit,
+ .private_data = (void **)&bdev, + .private_data = (void **)&bdev,
@ -629,11 +460,13 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+ .ops = &bcma_pcie2_ops, + .ops = &bcma_pcie2_ops,
+ }; + };
+ +
+ dev_info(&bdev->dev, "initializing PCIe controller\n");
+
+ /* Announce this port to ARM/PCI common code */ + /* Announce this port to ARM/PCI common code */
+ pci_common_init_dev(&bdev->dev, &hw); + pci_common_init_dev(&bdev->dev, &hw);
+ +
+ /* Setup virtual-wire interrupts */ + /* Setup virtual-wire interrupts */
+ bcma_write32(bdev, SOC_PCIE_SYS_RC_INTX_EN, 0xf); + bcma_write32(bdev, BCMA_CORE_PCIE2_SYS_RC_INTX_EN, 0xf);
+ +
+ /* Enable memory and bus master */ + /* Enable memory and bus master */
+ bcma_write32(bdev, SOC_PCIE_HDR_OFF + 4, 0x6); + bcma_write32(bdev, SOC_PCIE_HDR_OFF + 4, 0x6);
@ -666,5 +499,5 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+module_exit(bcma_pcie2_exit); +module_exit(bcma_pcie2_exit);
+ +
+MODULE_AUTHOR("Hauke Mehrtens"); +MODULE_AUTHOR("Hauke Mehrtens");
+MODULE_DESCRIPTION("PCIe Gen2 driver for BCMA"); +MODULE_DESCRIPTION("BCM5301X PCIe host controller");
+MODULE_LICENSE("GPLv2"); +MODULE_LICENSE("GPLv2");

View file

@ -1,19 +1,43 @@
From cc2cda651fcbc498bf513a6b802dca19944bcb37 Mon Sep 17 00:00:00 2001 From cf067bf8bb993d6cfdc42d750ae241c43f88403f Mon Sep 17 00:00:00 2001
From: Hauke Mehrtens <hauke@hauke-m.de> From: Hauke Mehrtens <hauke@hauke-m.de>
Date: Mon, 12 May 2014 11:55:20 +0200 Date: Mon, 12 May 2014 11:55:20 +0200
Subject: [PATCH 13/17] pcie2-bcma: add new PCIe2 driver for bcma Subject: [PATCH 1/2] PCI: BCM5301X: add PCIe2 driver for BCM5301X SoCs
This driver supports the PCIe controller found on the BCM4708 and This driver supports the PCIe controller found on the BCM4708 and
similar SoCs. The controller itself is automatically detected by bcma. 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> Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
--- ---
arch/arm/mach-bcm/Kconfig | 2 + arch/arm/mach-bcm/Kconfig | 1 +
drivers/pci/host/Kconfig | 7 + drivers/pci/host/Kconfig | 7 +
drivers/pci/host/Makefile | 1 + drivers/pci/host/Makefile | 1 +
drivers/pci/host/pcie2-bcma.c | 591 ++++++++++++++++++++++++++++++++++++++++++ drivers/pci/host/pci-host-bcm5301x.c | 428 +++++++++++++++++++++++++++++++++++
4 files changed, 601 insertions(+) 4 files changed, 437 insertions(+)
create mode 100644 drivers/pci/host/pcie2-bcma.c create mode 100644 drivers/pci/host/pci-host-bcm5301x.c
--- a/arch/arm/mach-bcm/Kconfig --- a/arch/arm/mach-bcm/Kconfig
+++ b/arch/arm/mach-bcm/Kconfig +++ b/arch/arm/mach-bcm/Kconfig
@ -31,12 +55,12 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
There are 5 internal PCIe ports available. Each port is GEN3 capable There are 5 internal PCIe ports available. Each port is GEN3 capable
and have varied lanes from x1 to x8. and have varied lanes from x1 to x8.
+config PCI_BCMA +config PCI_BCM5301X
+ bool "BCMA PCIe2 host controller" + bool "BCM5301X PCIe2 host controller"
+ depends on BCMA && OF + depends on BCMA && OF && ARM && PCI_DOMAINS
+ help + help
+ Say Y here if you want to support a simple generic PCI host + Say Y here if you want to support the PCIe host controller found
+ controller, such as the one emulated by kvmtool. + on Broadcom BCM5301X and BCM470X (Northstar) SoCs.
+ +
endmenu endmenu
--- a/drivers/pci/host/Makefile --- a/drivers/pci/host/Makefile
@ -45,10 +69,10 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o
obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
obj-$(CONFIG_PCI_XGENE) += pci-xgene.o obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
+obj-$(CONFIG_PCI_BCMA) += pcie2-bcma.o +obj-$(CONFIG_PCI_BCM5301X) += pci-host-bcm5301x.o
--- /dev/null --- /dev/null
+++ b/drivers/pci/host/pcie2-bcma.c +++ b/drivers/pci/host/pci-host-bcm5301x.c
@@ -0,0 +1,619 @@ @@ -0,0 +1,428 @@
+/* +/*
+ * Northstar PCI-Express driver + * Northstar PCI-Express driver
+ * Only supports Root-Complex (RC) mode + * Only supports Root-Complex (RC) mode
@ -58,63 +82,25 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+ * + *
+ * Only MEM access is supported, PAX does not support IO. + * Only MEM access is supported, PAX does not support IO.
+ * + *
+ * TODO: + * Copyright 2012-2014, Broadcom Corporation
+ * MSI interrupts, + * Copyright 2014, Hauke Mehrtens <hauke@hauke-m.de>
+ * DRAM > 128 MBytes (e.g. DMA zones) + *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */ + */
+ +
+#include <linux/kernel.h> +#include <linux/kernel.h>
+#include <linux/module.h> +#include <linux/module.h>
+#include <linux/bug.h>
+#include <linux/delay.h> +#include <linux/delay.h>
+#include <linux/pci.h> +#include <linux/pci.h>
+#include <linux/io.h> +#include <linux/io.h>
+#include <linux/ioport.h> +#include <linux/ioport.h>
+#include <linux/interrupt.h> +#include <linux/interrupt.h>
+#include <linux/bcma/bcma.h> +#include <linux/bcma/bcma.h>
+ +#include <linux/bcma/bcma_driver_pcie2.h>
+#define SI_ENUM_BASE 0x18000000 /* Enumeration space base */ +#include <linux/of_irq.h>
+
+/*
+ * Register offset definitions
+ */
+#define SOC_PCIE_CONTROL 0x000 /* a.k.a. CLK_CONTROL reg */
+#define SOC_PCIE_PM_STATUS 0x008
+#define SOC_PCIE_PM_CONTROL 0x00c /* in EP mode only ! */
+
+#define SOC_PCIE_EXT_CFG_ADDR 0x120
+#define SOC_PCIE_EXT_CFG_DATA 0x124
+#define SOC_PCIE_CFG_ADDR 0x1f8
+#define SOC_PCIE_CFG_DATA 0x1fc
+
+#define SOC_PCIE_SYS_RC_INTX_EN 0x330
+#define SOC_PCIE_SYS_RC_INTX_CSR 0x334
+#define SOC_PCIE_SYS_HOST_INTR_EN 0x344
+#define SOC_PCIE_SYS_HOST_INTR_CSR 0x348
+ +
+#define SOC_PCIE_HDR_OFF 0x400 /* 256 bytes per function */ +#define SOC_PCIE_HDR_OFF 0x400 /* 256 bytes per function */
+ +
+/* 32-bit 4KB in-bound mapping windows for Function 0..3, n=0..7 */
+#define SOC_PCIE_SYS_IMAP0(f, n) (0xc00 + ((f) << 9)((n) << 2))
+/* 64-bit in-bound mapping windows for func 0..3 */
+#define SOC_PCIE_SYS_IMAP1(f) (0xc80 + ((f) << 3))
+#define SOC_PCIE_SYS_IMAP2(f) (0xcc0 + ((f) << 3))
+/* 64-bit in-bound address range n=0..2 */
+#define SOC_PCIE_SYS_IARR(n) (0xd00 + ((n) << 3))
+/* 64-bit out-bound address filter n=0..2 */
+#define SOC_PCIE_SYS_OARR(n) (0xd20 + ((n) << 3))
+/* 64-bit out-bound mapping windows n=0..2 */
+#define SOC_PCIE_SYS_OMAP(n) (0xd40 + ((n) << 3))
+
+#define BCM4360_D11AC_ID 0x43a0
+#define BCM4360_D11AC2G_ID 0x43a1
+#define BCM4360_D11AC5G_ID 0x43a2
+#define BCM4352_D11AC_ID 0x43b1 /* 4352 802.11ac dualband device */
+#define BCM4352_D11AC2G_ID 0x43b2 /* 4352 802.11ac 2.4G device */
+#define BCM4352_D11AC5G_ID 0x43b3 /* 4352 802.11ac 5G device */
+
+static struct pci_ops bcma_pcie2_ops;
+
+static int bcma_pcie2_map_irq(const struct pci_dev *pdev, u8 slot, u8 pin) +static int bcma_pcie2_map_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
+{ +{
+ struct pci_sys_data *sys = pdev->sysdata; + struct pci_sys_data *sys = pdev->sysdata;
@ -133,16 +119,17 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+ if (busno == 0) { + if (busno == 0) {
+ if (slot >= 1) + if (slot >= 1)
+ return 0; + return 0;
+ bcma_write32(bdev, SOC_PCIE_EXT_CFG_ADDR, where & 0xffc); + bcma_write32(bdev, BCMA_CORE_PCIE2_CONFIGINDADDR,
+ return SOC_PCIE_EXT_CFG_DATA; + where & 0xffc);
+ return BCMA_CORE_PCIE2_CONFIGINDDATA;
+ } + }
+ if (fn > 1) + if (fn > 1)
+ return 0; + return 0;
+ addr_reg = (busno & 0xff) << 20 | (slot << 15) | (fn << 12) | + addr_reg = (busno & 0xff) << 20 | (slot << 15) | (fn << 12) |
+ (where & 0xffc) | (1 & 0x3); + (where & 0xffc) | (1 & 0x3);
+ +
+ bcma_write32(bdev, SOC_PCIE_CFG_ADDR, addr_reg); + bcma_write32(bdev, BCMA_CORE_PCIE2_CFG_ADDR, addr_reg);
+ return SOC_PCIE_CFG_DATA; + return BCMA_CORE_PCIE2_CFG_DATA;
+} +}
+ +
+static u32 bcma_pcie2_read_config(struct bcma_device *bdev, int busno, +static u32 bcma_pcie2_read_config(struct bcma_device *bdev, int busno,
@ -160,20 +147,6 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+ +
+ data_reg = bcma_read32(bdev, base); + data_reg = bcma_read32(bdev, base);
+ +
+ /* NS: CLASS field is R/O, and set to wrong 0x200 value */
+ if (busno == 0 && devfn == 0) {
+ /*
+ * RC's class is 0x0280, but Linux PCI driver needs 0x604
+ * for a PCIe bridge. So we must fixup the class code
+ * to 0x604 here.
+ */
+ if ((where & 0xffc) == PCI_CLASS_REVISION) {
+ data_reg &= 0xff;
+ data_reg |= 0x604 << 16;
+ }
+ }
+ /* HEADER_TYPE=00 indicates the port in EP mode */
+
+ if (size == 4) + if (size == 4)
+ return data_reg; + return data_reg;
+ +
@ -208,42 +181,6 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+ bcma_write32(bdev, base, data_reg); + bcma_write32(bdev, base, data_reg);
+} +}
+ +
+static u8 bcma_pcie2_read_config8(struct bcma_device *bdev, int busno,
+ unsigned int devfn, int where)
+{
+ return bcma_pcie2_read_config(bdev, busno, devfn, where, 1);
+}
+
+static u16 bcma_pcie2_read_config16(struct bcma_device *bdev, int busno,
+ unsigned int devfn, int where)
+{
+ return bcma_pcie2_read_config(bdev, busno, devfn, where, 2);
+}
+
+static u32 bcma_pcie2_read_config32(struct bcma_device *bdev, int busno,
+ unsigned int devfn, int where)
+{
+ return bcma_pcie2_read_config(bdev, busno, devfn, where, 4);
+}
+
+static void bcma_pcie2_write_config8(struct bcma_device *bdev, int busno,
+ unsigned int devfn, int where, u8 val)
+{
+ return bcma_pcie2_write_config(bdev, busno, devfn, where, 1, val);
+}
+
+static void bcma_pcie2_write_config16(struct bcma_device *bdev, int busno,
+ unsigned int devfn, int where, u16 val)
+{
+ return bcma_pcie2_write_config(bdev, busno, devfn, where, 2, val);
+}
+
+static void bcma_pcie2_write_config32(struct bcma_device *bdev, int busno,
+ unsigned int devfn, int where, u32 val)
+{
+ return bcma_pcie2_write_config(bdev, busno, devfn, where, 4, val);
+}
+
+static int bcma_pcie2_read_config_pci(struct pci_bus *bus, unsigned int devfn, +static int bcma_pcie2_read_config_pci(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 *val) + int where, int size, u32 *val)
+{ +{
@ -267,31 +204,31 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+} +}
+ +
+/* +/*
+ * 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);
+
+/*
+ * Check link status, return 0 if link is up in RC mode, + * Check link status, return 0 if link is up in RC mode,
+ * otherwise return non-zero + * otherwise return non-zero
+ */ + */
+static int bcma_pcie2_check_link(struct bcma_device *bdev, +static int bcma_pcie2_check_link(struct bcma_device *bdev, bool allow_gen2)
+ struct pci_sys_data *sys, u32 allow_gen2)
+{ +{
+ u32 devfn = 0; + u32 devfn = 0;
+ u32 tmp32; + u32 tmp32;
+ u16 tmp16;
+ u8 tmp8; + u8 tmp8;
+ int pos;
+ bool link = false;
+ /*
+ * 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_pcie2_read_config32(bdev, 0, devfn, 0xdc); + tmp32 = bcma_pcie2_read_config(bdev, 0, devfn, 0xdc, 4);
+ tmp32 &= ~0xf; + tmp32 &= ~0xf;
+ if (allow_gen2) + if (allow_gen2)
+ tmp32 |= 2; + tmp32 |= 2;
@ -299,28 +236,17 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+ /* force PCIE GEN1 */ + /* force PCIE GEN1 */
+ tmp32 |= 1; + tmp32 |= 1;
+ } + }
+ bcma_pcie2_write_config32(bdev, 0, devfn, 0xdc, tmp32); + bcma_pcie2_write_config(bdev, 0, devfn, 0xdc, 4, tmp32);
+ +
+ /* See if the port is in EP mode, indicated by header type 00 */ + /* See if the port is in EP mode, indicated by header type 00 */
+ tmp8 = bcma_pcie2_read_config8(bdev, 0, devfn, PCI_HEADER_TYPE); + tmp8 = bcma_pcie2_read_config(bdev, 0, devfn, PCI_HEADER_TYPE, 1);
+ if (tmp8 != PCI_HEADER_TYPE_BRIDGE) { + if (tmp8 != PCI_HEADER_TYPE_BRIDGE) {
+ dev_info(&bdev->dev, "Port %d in End-Point mode - ignored\n", + dev_info(&bdev->dev, "Port %d in End-Point mode - ignored\n",
+ bdev->core_unit); + bdev->core_unit);
+ return -ENODEV; + return -ENODEV;
+ } + }
+ +
+ /* NS PAX only changes NLW field when card is present */ + return 0;
+ pos = pci_bus_find_capability(&bus, devfn, PCI_CAP_ID_EXP);
+ if (pos) {
+ u8 nlw;
+
+ pci_bus_read_config_word(&bus, devfn, pos + PCI_EXP_LNKSTA,
+ &tmp16);
+ nlw = (tmp16 & PCI_EXP_LNKSTA_NLW) >> PCI_EXP_LNKSTA_NLW_SHIFT;
+ link = (tmp16 & PCI_EXP_LNKSTA_DLLLA) || nlw != 0;
+ }
+
+ return link ? 0 : -ENODEV;
+} +}
+ +
+/* +/*
@ -328,67 +254,52 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+ */ + */
+static void bcma_pcie2_hw_init(struct bcma_device *bdev) +static void bcma_pcie2_hw_init(struct bcma_device *bdev)
+{ +{
+ u32 devfn = 0;
+ u32 tmp32; + u32 tmp32;
+ u16 tmp16; + u16 tmp16;
+ +
+ /* Change MPS and MRRS to 512 */ + /* Change MPS and MRRS to 512 */
+ tmp16 = bcma_pcie2_read_config16(bdev, 0, devfn, 0x4d4); + tmp16 = bcma_pcie2_read_config(bdev, 0, 0, 0x4d4, 2);
+ tmp16 &= ~7; + tmp16 &= ~7;
+ tmp16 |= 2; + tmp16 |= 2;
+ bcma_pcie2_write_config16(bdev, 0, devfn, 0x4d4, tmp16); + bcma_pcie2_write_config(bdev, 0, 0, 0x4d4, 2, tmp16);
+ +
+ tmp32 = bcma_pcie2_read_config32(bdev, 0, devfn, 0xb4); + tmp32 = bcma_pcie2_read_config(bdev, 0, 0, 0xb4, 4);
+ tmp32 &= ~((7 << 12) | (7 << 5)); + tmp32 &= ~((7 << 12) | (7 << 5));
+ tmp32 |= (2 << 12) | (2 << 5); + tmp32 |= (2 << 12) | (2 << 5);
+ bcma_pcie2_write_config32(bdev, 0, devfn, 0xb4, tmp32); + bcma_pcie2_write_config(bdev, 0, 0, 0xb4, 4, tmp32);
+ +
+ /* Turn-on Root-Complex (RC) mode, from reset defailt of EP */ + /*
+ + * Turn-on Root-Complex (RC) mode, from reset default of EP
+ /* The mode is set by straps, can be overwritten via DMU + * The mode is set by straps, can be overwritten via DMU
+ register <cru_straps_control> bit 5, "1" means RC + * register <cru_straps_control> bit 5, "1" means RC
+ */ + */
+ +
+ /* Send a downstream reset */ + /* Send a downstream reset */
+ bcma_write32(bdev, SOC_PCIE_CONTROL, 0x3); + bcma_write32(bdev, BCMA_CORE_PCIE2_CLK_CONTROL,
+ udelay(250); + PCIE2_CLKC_RST_OE | PCIE2_CLKC_RST);
+ bcma_write32(bdev, SOC_PCIE_CONTROL, 0x1); + usleep_range(250, 400);
+ mdelay(250); + bcma_write32(bdev, BCMA_CORE_PCIE2_CLK_CONTROL, PCIE2_CLKC_RST_OE);
+ msleep(250);
+ +
+ /* TBD: take care of PM, check we're on */ + /* TBD: take care of PM, check we're on */
+} +}
+ +
+/* +/*
+ * Setup the address translation + * Setup the address translation
+ *
+ * NOTE: All PCI-to-CPU address mapping are 1:1 for simplicity
+ */ + */
+static void bcma_pcie2_map_init(struct bcma_device *bdev) +static int bcma_pcie2_map_init(struct bcma_device *bdev, u32 addr)
+{ +{
+ unsigned size, i; + /* 64MB alignment */
+ u32 addr; + if (!addr || (addr & (SZ_64M - 1)))
+ return -EINVAL;
+ +
+ /* + bcma_write32(bdev, BCMA_CORE_PCIE2_OMAP0_LOWER, addr);
+ * NOTE: + bcma_write32(bdev, BCMA_CORE_PCIE2_OARR0, addr | 0x01);
+ * All PCI-to-CPU address mapping are 1:1 for simplicity
+ */
+ +
+ /* Outbound address translation setup */ + bcma_write32(bdev, BCMA_CORE_PCIE2_OMAP1_LOWER, addr + SZ_64M);
+ size = SZ_128M; + bcma_write32(bdev, BCMA_CORE_PCIE2_OARR1, (addr + SZ_64M) | 0x01);
+ addr = bdev->addr_s[0];
+ BUG_ON(!addr);
+ BUG_ON(addr & ((1 << 25) - 1)); /* 64MB alignment */
+
+ for (i = 0; i < 3; i++) {
+ const unsigned win_size = SZ_64M;
+ /* 64-bit LE regs, write low word, high is 0 at reset */
+ bcma_write32(bdev, SOC_PCIE_SYS_OMAP(i), addr);
+ bcma_write32(bdev, SOC_PCIE_SYS_OARR(i), addr|0x1);
+ addr += win_size;
+ if (size >= win_size)
+ size -= win_size;
+ if (size == 0)
+ break;
+ }
+ WARN_ON(size > 0);
+ +
+ /* + /*
+ * Inbound address translation setup + * Inbound address translation setup
@ -398,107 +309,46 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+ * otherwise DMA bouncing mechanism may be required. + * otherwise DMA bouncing mechanism may be required.
+ * Also consider DMA mask to limit DMA physical address + * Also consider DMA mask to limit DMA physical address
+ */ + */
+ size = SZ_128M;
+ addr = PHYS_OFFSET;
+
+ size >>= 20; /* In MB */
+ size &= 0xff; /* Size is an 8-bit field */
+
+ WARN_ON(size == 0);
+ /* 64-bit LE regs, write low word, high is 0 at reset */ + /* 64-bit LE regs, write low word, high is 0 at reset */
+ bcma_write32(bdev, SOC_PCIE_SYS_IMAP1(0), addr | 0x1); + bcma_write32(bdev, BCMA_CORE_PCIE2_FUNC0_IMAP1, PHYS_OFFSET | 0x1);
+ bcma_write32(bdev, SOC_PCIE_SYS_IARR(1), addr | size); + bcma_write32(bdev, BCMA_CORE_PCIE2_IARR1_LOWER,
+ + PHYS_OFFSET | ((SZ_128M >> 20) & 0xff));
+#ifdef CONFIG_SPARSEMEM + return 0;
+ addr = PHYS_OFFSET2;
+ bcma_write32(bdev, SOC_PCIE_SYS_IMAP2(0), addr | 0x1);
+ bcma_write32(bdev, SOC_PCIE_SYS_IARR(2), addr | size);
+#endif
+} +}
+ +
+/* +/*
+ * Setup PCIE Host bridge + * Setup PCIE Host bridge
+ */ + */
+static void bcma_pcie2_bridge_init(struct bcma_device *bdev) +static int bcma_pcie2_bridge_init(struct bcma_device *bdev, u32 addr, u32 size)
+{ +{
+ u32 devfn = 0; + bcma_pcie2_write_config(bdev, 0, 0, PCI_PRIMARY_BUS, 1, 0);
+ u8 tmp8; + bcma_pcie2_write_config(bdev, 0, 0, PCI_SECONDARY_BUS, 1, 1);
+ u16 tmp16; + bcma_pcie2_write_config(bdev, 0, 0, PCI_SUBORDINATE_BUS, 1, 4);
+ +
+ bcma_pcie2_write_config8(bdev, 0, devfn, PCI_PRIMARY_BUS, 0); + bcma_pcie2_read_config(bdev, 0, 0, PCI_PRIMARY_BUS, 1);
+ bcma_pcie2_write_config8(bdev, 0, devfn, PCI_SECONDARY_BUS, 1); + bcma_pcie2_read_config(bdev, 0, 0, PCI_SECONDARY_BUS, 1);
+ bcma_pcie2_write_config8(bdev, 0, devfn, PCI_SUBORDINATE_BUS, 4); + bcma_pcie2_read_config(bdev, 0, 0, PCI_SUBORDINATE_BUS, 1);
+
+ tmp8 = bcma_pcie2_read_config8(bdev, 0, devfn, PCI_PRIMARY_BUS);
+ tmp8 = bcma_pcie2_read_config8(bdev, 0, devfn, PCI_SECONDARY_BUS);
+ tmp8 = bcma_pcie2_read_config8(bdev, 0, devfn, PCI_SUBORDINATE_BUS);
+ +
+ /* MEM_BASE, MEM_LIM require 1MB alignment */ + /* MEM_BASE, MEM_LIM require 1MB alignment */
+ BUG_ON((bdev->addr_s[0] >> 16) & 0xf); + if (((addr >> 16) & 0xf) || (((addr + size) >> 16) & 0xf))
+ bcma_pcie2_write_config16(bdev, 0, devfn, PCI_MEMORY_BASE, + return -EINVAL;
+ bdev->addr_s[0] >> 16); +
+ BUG_ON(((bdev->addr_s[0] + SZ_128M) >> 16) & 0xf); + bcma_pcie2_write_config(bdev, 0, 0, PCI_MEMORY_BASE, 2, addr >> 16);
+ bcma_pcie2_write_config16(bdev, 0, devfn, PCI_MEMORY_LIMIT, + bcma_pcie2_write_config(bdev, 0, 0, PCI_MEMORY_LIMIT, 2,
+ (bdev->addr_s[0] + SZ_128M) >> 16); + (addr + size) >> 16);
+ +
+ /* These registers are not supported on the NS */ + /* These registers are not supported on the NS */
+ bcma_pcie2_write_config16(bdev, 0, devfn, PCI_IO_BASE_UPPER16, 0); + bcma_pcie2_write_config(bdev, 0, 0, PCI_IO_BASE_UPPER16, 2, 0);
+ bcma_pcie2_write_config16(bdev, 0, devfn, PCI_IO_LIMIT_UPPER16, 0); + bcma_pcie2_write_config(bdev, 0, 0, PCI_IO_LIMIT_UPPER16, 2, 0);
+ +
+ /* Force class to that of a Bridge */ + /* Force class to that of a Bridge */
+ bcma_pcie2_write_config16(bdev, 0, devfn, PCI_CLASS_DEVICE, + bcma_pcie2_write_config(bdev, 0, 0, PCI_CLASS_DEVICE, 2,
+ PCI_CLASS_BRIDGE_PCI); + PCI_CLASS_BRIDGE_PCI);
+ +
+ tmp16 = bcma_pcie2_read_config16(bdev, 0, devfn, PCI_CLASS_DEVICE); + bcma_pcie2_read_config(bdev, 0, 0, PCI_CLASS_DEVICE, 2);
+ tmp16 = bcma_pcie2_read_config16(bdev, 0, devfn, PCI_MEMORY_BASE); + bcma_pcie2_read_config(bdev, 0, 0, PCI_MEMORY_BASE, 2);
+ tmp16 = bcma_pcie2_read_config16(bdev, 0, devfn, PCI_MEMORY_LIMIT); + bcma_pcie2_read_config(bdev, 0, 0, PCI_MEMORY_LIMIT, 2);
+} + return 0;
+
+static int bcma_pcie2_allow_gen2_rc(struct bcma_device *bdev)
+{
+ u32 vendorid, devid, chipid, chiprev;
+ u32 val, bar;
+ void __iomem *base;
+ int allow = 1;
+
+ /* Read PCI vendor/device ID's */
+ bcma_write32(bdev, SOC_PCIE_CFG_ADDR, 0x0);
+ val = bcma_read32(bdev, SOC_PCIE_CFG_DATA);
+ vendorid = val & 0xffff;
+ devid = val >> 16;
+ if (vendorid == PCI_VENDOR_ID_BROADCOM &&
+ (devid == BCMA_CHIP_ID_BCM4360 || devid == BCM4360_D11AC_ID ||
+ devid == BCM4360_D11AC2G_ID || devid == BCM4360_D11AC5G_ID ||
+ devid == BCM4352_D11AC_ID || devid == BCM4352_D11AC2G_ID ||
+ devid == BCM4352_D11AC5G_ID)) {
+ /* Config BAR0 */
+ bar = bdev->addr_s[0];
+ bcma_write32(bdev, SOC_PCIE_CFG_ADDR, 0x10);
+ bcma_write32(bdev, SOC_PCIE_CFG_DATA, bar);
+ /* Config BAR0 window to access chipc */
+ bcma_write32(bdev, SOC_PCIE_CFG_ADDR, 0x80);
+ bcma_write32(bdev, SOC_PCIE_CFG_DATA, SI_ENUM_BASE);
+
+ /* Enable memory resource */
+ bcma_write32(bdev, SOC_PCIE_CFG_ADDR, 0x4);
+ val = bcma_read32(bdev, SOC_PCIE_CFG_DATA);
+ val |= PCI_COMMAND_MEMORY;
+ bcma_write32(bdev, SOC_PCIE_CFG_DATA, val);
+ /* Enable memory and bus master */
+ bcma_write32(bdev, SOC_PCIE_HDR_OFF + 4, 0x6);
+
+ /* Read CHIP ID */
+ base = ioremap(bar, 0x1000);
+ val = __raw_readl(base);
+ iounmap(base);
+ chipid = val & 0xffff;
+ chiprev = (val >> 16) & 0xf;
+ if ((chipid == BCMA_CHIP_ID_BCM4360 ||
+ chipid == BCMA_CHIP_ID_BCM43460 ||
+ chipid == BCMA_CHIP_ID_BCM4352) && (chiprev < 3))
+ allow = 0;
+ }
+ return allow;
+} +}
+ +
+static void bcma_pcie2_3rd_init(struct bcma_bus *bus) +static void bcma_pcie2_3rd_init(struct bcma_bus *bus)
@ -537,7 +387,7 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+ struct resource *res; + struct resource *res;
+ struct bcma_device *arm_core; + struct bcma_device *arm_core;
+ u32 cru_straps_ctrl; + u32 cru_straps_ctrl;
+ int allow_gen2, linkfail; + int ret;
+ int phyaddr; + int phyaddr;
+ +
+ if (bdev->core_unit == 2) { + if (bdev->core_unit == 2) {
@ -561,8 +411,8 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+ return -EINVAL; + return -EINVAL;
+ +
+ res->start = bdev->addr_s[0]; + res->start = bdev->addr_s[0];
+ res->end = res->start + SZ_128M - 1; + res->end = bdev->addr_s[0] + SZ_128M -1;
+ res->name = "PCIe Configuration Space"; + res->name = "PCIe dummy IO space";
+ res->flags = IORESOURCE_MEM; + res->flags = IORESOURCE_MEM;
+ +
+ pci_add_resource(&sys->resources, res); + pci_add_resource(&sys->resources, res);
@ -572,55 +422,36 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+ if (!res) + if (!res)
+ return -EINVAL; + return -EINVAL;
+ +
+ res->start = bdev->addr_s[0]; + res->start = 0;
+ res->end = res->start + SZ_128M - 1; + res->end = 0;
+ res->name = "PCIe Configuration Space"; + res->name = "PCIe dummy IO space";
+ res->flags = IORESOURCE_IO; + res->flags = IORESOURCE_IO;
+ +
+ pci_add_resource(&sys->resources, res); + pci_add_resource(&sys->resources, res);
+ +
+ for (allow_gen2 = 0; allow_gen2 <= 1; allow_gen2++) { + bcma_pcie2_hw_init(bdev);
+ bcma_pcie2_hw_init(bdev); + ret = bcma_pcie2_map_init(bdev, bdev->addr_s[0]);
+ bcma_pcie2_map_init(bdev); + if (ret)
+ return ret;
+ +
+ /* + /*
+ * Skip inactive ports - + * Skip inactive ports -
+ * will need to change this for hot-plugging + * will need to change this for hot-plugging
+ */ + */
+ linkfail = bcma_pcie2_check_link(bdev, sys, allow_gen2); + ret = bcma_pcie2_check_link(bdev, true);
+ if (linkfail) + if (ret)
+ break; + return ret;
+ +
+ bcma_pcie2_bridge_init(bdev); + ret = bcma_pcie2_bridge_init(bdev, bdev->addr_s[0], SZ_128M);
+ + if (ret)
+ if (allow_gen2 == 0) { + return ret;
+ if (bcma_pcie2_allow_gen2_rc(bdev) == 0)
+ break;
+ dev_info(&bdev->dev, "switching to GEN2\n");
+ }
+ }
+
+ if (linkfail)
+ return -1;
+ +
+ return 1; + return 1;
+} +}
+ +
+/*
+ * Methods for accessing configuration registers
+ */
+static struct pci_ops bcma_pcie2_ops = {
+ .read = bcma_pcie2_read_config_pci,
+ .write = bcma_pcie2_write_config_pci,
+};
+
+static int bcma_pcie2_probe(struct bcma_device *bdev) +static int bcma_pcie2_probe(struct bcma_device *bdev)
+{ +{
+ struct hw_pci hw; + struct hw_pci hw = {
+
+ dev_info(&bdev->dev, "scanning bus\n");
+
+ hw = (struct hw_pci) {
+ .nr_controllers = 1, + .nr_controllers = 1,
+ .domain = bdev->core_unit, + .domain = bdev->core_unit,
+ .private_data = (void **)&bdev, + .private_data = (void **)&bdev,
@ -629,11 +460,13 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+ .ops = &bcma_pcie2_ops, + .ops = &bcma_pcie2_ops,
+ }; + };
+ +
+ dev_info(&bdev->dev, "initializing PCIe controller\n");
+
+ /* Announce this port to ARM/PCI common code */ + /* Announce this port to ARM/PCI common code */
+ pci_common_init_dev(&bdev->dev, &hw); + pci_common_init_dev(&bdev->dev, &hw);
+ +
+ /* Setup virtual-wire interrupts */ + /* Setup virtual-wire interrupts */
+ bcma_write32(bdev, SOC_PCIE_SYS_RC_INTX_EN, 0xf); + bcma_write32(bdev, BCMA_CORE_PCIE2_SYS_RC_INTX_EN, 0xf);
+ +
+ /* Enable memory and bus master */ + /* Enable memory and bus master */
+ bcma_write32(bdev, SOC_PCIE_HDR_OFF + 4, 0x6); + bcma_write32(bdev, SOC_PCIE_HDR_OFF + 4, 0x6);
@ -666,5 +499,5 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
+module_exit(bcma_pcie2_exit); +module_exit(bcma_pcie2_exit);
+ +
+MODULE_AUTHOR("Hauke Mehrtens"); +MODULE_AUTHOR("Hauke Mehrtens");
+MODULE_DESCRIPTION("PCIe Gen2 driver for BCMA"); +MODULE_DESCRIPTION("BCM5301X PCIe host controller");
+MODULE_LICENSE("GPLv2"); +MODULE_LICENSE("GPLv2");