diff options
author | Hauke Mehrtens <hauke@openwrt.org> | 2014-12-07 21:54:53 +0000 |
---|---|---|
committer | Hauke Mehrtens <hauke@openwrt.org> | 2014-12-07 21:54:53 +0000 |
commit | d45feeef92225ce024e936fcb49d5c9838cce2a2 (patch) | |
tree | 69bad8de3bce72b0bbbc1c836d3957410d33193d /target/linux/bcm53xx | |
parent | 270356800f2196d87243be36d430dbc8ecea9e12 (diff) | |
download | master-187ad058-d45feeef92225ce024e936fcb49d5c9838cce2a2.tar.gz master-187ad058-d45feeef92225ce024e936fcb49d5c9838cce2a2.tar.bz2 master-187ad058-d45feeef92225ce024e936fcb49d5c9838cce2a2.zip |
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>
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@43545 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'target/linux/bcm53xx')
4 files changed, 292 insertions, 634 deletions
diff --git a/target/linux/bcm53xx/config-3.14 b/target/linux/bcm53xx/config-3.14 index 322da68e1b..4c9ec0cc57 100644 --- a/target/linux/bcm53xx/config-3.14 +++ b/target/linux/bcm53xx/config-3.14 @@ -110,7 +110,6 @@ CONFIG_GENERIC_CLOCKEVENTS_BUILD=y CONFIG_GENERIC_IDLE_POLL_SETUP=y CONFIG_GENERIC_IO=y CONFIG_GENERIC_IRQ_SHOW=y -CONFIG_GENERIC_NET_UTILS=y CONFIG_GENERIC_PCI_IOMAP=y CONFIG_GENERIC_SCHED_CLOCK=y CONFIG_GENERIC_SMP_IDLE_THREAD=y @@ -220,7 +219,7 @@ CONFIG_OUTER_CACHE_SYNC=y CONFIG_PAGEFLAGS_EXTENDED=y CONFIG_PAGE_OFFSET=0xC0000000 CONFIG_PCI=y -CONFIG_PCI_BCMA=y +CONFIG_PCI_BCM5301X=y CONFIG_PCI_DOMAINS=y CONFIG_PERF_USE_VMALLOC=y CONFIG_PHYLIB=y diff --git a/target/linux/bcm53xx/config-3.18 b/target/linux/bcm53xx/config-3.18 index 3d6f3cdef7..f42639a9f2 100644 --- a/target/linux/bcm53xx/config-3.18 +++ b/target/linux/bcm53xx/config-3.18 @@ -10,18 +10,13 @@ CONFIG_ARCH_HAS_SG_CHAIN=y CONFIG_ARCH_HAS_TICK_BROADCAST=y CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=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_MSM is not set CONFIG_ARCH_MULTIPLATFORM=y # CONFIG_ARCH_MULTI_CPU_AUTO is not set CONFIG_ARCH_MULTI_V6_V7=y CONFIG_ARCH_MULTI_V7=y # CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set CONFIG_ARCH_NR_GPIO=0 -# CONFIG_ARCH_QCOM is not set # CONFIG_ARCH_SELECT_MEMORY_MODEL is not set # CONFIG_ARCH_SPARSEMEM_DEFAULT is not set CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y @@ -122,7 +117,6 @@ CONFIG_GENERIC_CLOCKEVENTS_BUILD=y CONFIG_GENERIC_IDLE_POLL_SETUP=y CONFIG_GENERIC_IO=y CONFIG_GENERIC_IRQ_SHOW=y -CONFIG_GENERIC_NET_UTILS=y CONFIG_GENERIC_PCI_IOMAP=y CONFIG_GENERIC_SCHED_CLOCK=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_SPI_BCM53XXSPIFLASH=y CONFIG_MTD_SPI_NOR=y -# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set CONFIG_MTD_UBI=y CONFIG_MTD_UBI_BEB_LIMIT=20 CONFIG_MTD_UBI_BLOCK=y @@ -237,7 +230,7 @@ CONFIG_OUTER_CACHE_SYNC=y CONFIG_PAGEFLAGS_EXTENDED=y CONFIG_PAGE_OFFSET=0xC0000000 CONFIG_PCI=y -CONFIG_PCI_BCMA=y +CONFIG_PCI_BCM5301X=y CONFIG_PCI_DOMAINS=y CONFIG_PERF_USE_VMALLOC=y CONFIG_PHYLIB=y diff --git a/target/linux/bcm53xx/patches-3.14/170-pcie2-bcma-add-new-PCIe2-driver-for-bcma.patch b/target/linux/bcm53xx/patches-3.14/170-pcie2-bcma-add-new-PCIe2-driver-for-bcma.patch index 1b6149f20f..dcdbb8f967 100644 --- a/target/linux/bcm53xx/patches-3.14/170-pcie2-bcma-add-new-PCIe2-driver-for-bcma.patch +++ b/target/linux/bcm53xx/patches-3.14/170-pcie2-bcma-add-new-PCIe2-driver-for-bcma.patch @@ -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> 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 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 | 2 + - drivers/pci/host/Kconfig | 7 + - drivers/pci/host/Makefile | 1 + - drivers/pci/host/pcie2-bcma.c | 591 ++++++++++++++++++++++++++++++++++++++++++ - 4 files changed, 601 insertions(+) - create mode 100644 drivers/pci/host/pcie2-bcma.c + 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 @@ -31,12 +55,12 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> There are 3 internal PCI controllers available with a single built-in EHCI/OHCI host controller present on each one. -+config PCI_BCMA -+ bool "BCMA PCIe2 host controller" -+ depends on BCMA && OF ++config PCI_BCM5301X ++ bool "BCM5301X PCIe2 host controller" ++ depends on BCMA && OF && ARM && PCI_DOMAINS + help -+ Say Y here if you want to support a simple generic PCI host -+ controller, such as the one emulated by kvmtool. ++ 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 @@ -45,10 +69,10 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o obj-$(CONFIG_PCI_TEGRA) += pci-tegra.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 -+++ b/drivers/pci/host/pcie2-bcma.c -@@ -0,0 +1,619 @@ ++++ b/drivers/pci/host/pci-host-bcm5301x.c +@@ -0,0 +1,428 @@ +/* + * Northstar PCI-Express driver + * 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. + * -+ * TODO: -+ * MSI interrupts, -+ * DRAM > 128 MBytes (e.g. DMA zones) ++ * 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/bug.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/bcma/bcma.h> -+ -+#define SI_ENUM_BASE 0x18000000 /* Enumeration space base */ -+ -+/* -+ * 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 ++#include <linux/bcma/bcma_driver_pcie2.h> ++#include <linux/of_irq.h> + +#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) +{ + struct pci_sys_data *sys = pdev->sysdata; @@ -133,16 +119,17 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> + if (busno == 0) { + if (slot >= 1) + return 0; -+ bcma_write32(bdev, SOC_PCIE_EXT_CFG_ADDR, where & 0xffc); -+ return SOC_PCIE_EXT_CFG_DATA; ++ 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, SOC_PCIE_CFG_ADDR, addr_reg); -+ return SOC_PCIE_CFG_DATA; ++ 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, @@ -160,20 +147,6 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> + + 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) + return data_reg; + @@ -208,42 +181,6 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> + 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, + 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, + * otherwise return non-zero + */ -+static int bcma_pcie2_check_link(struct bcma_device *bdev, -+ struct pci_sys_data *sys, u32 allow_gen2) ++static int bcma_pcie2_check_link(struct bcma_device *bdev, bool allow_gen2) +{ + u32 devfn = 0; + u32 tmp32; -+ u16 tmp16; + 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; + if (allow_gen2) + tmp32 |= 2; @@ -299,28 +236,17 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> + /* force PCIE GEN1 */ + 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 */ -+ 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) { + dev_info(&bdev->dev, "Port %d in End-Point mode - ignored\n", + bdev->core_unit); + return -ENODEV; + } + -+ /* NS PAX only changes NLW field when card is present */ -+ 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; ++ return 0; +} + +/* @@ -328,67 +254,52 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> + */ +static void bcma_pcie2_hw_init(struct bcma_device *bdev) +{ -+ u32 devfn = 0; + u32 tmp32; + u16 tmp16; + + /* 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 |= 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 |= (2 << 12) | (2 << 5); -+ bcma_pcie2_write_config32(bdev, 0, devfn, 0xb4, tmp32); -+ -+ /* Turn-on Root-Complex (RC) mode, from reset defailt of EP */ ++ bcma_pcie2_write_config(bdev, 0, 0, 0xb4, 4, tmp32); + -+ /* The mode is set by straps, can be overwritten via DMU -+ register <cru_straps_control> bit 5, "1" means RC ++ /* ++ * 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, SOC_PCIE_CONTROL, 0x3); -+ udelay(250); -+ bcma_write32(bdev, SOC_PCIE_CONTROL, 0x1); -+ mdelay(250); ++ 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 void bcma_pcie2_map_init(struct bcma_device *bdev) ++static int bcma_pcie2_map_init(struct bcma_device *bdev, u32 addr) +{ -+ unsigned size, i; -+ u32 addr; ++ /* 64MB alignment */ ++ if (!addr || (addr & (SZ_64M - 1))) ++ return -EINVAL; + -+ /* -+ * NOTE: -+ * All PCI-to-CPU address mapping are 1:1 for simplicity -+ */ ++ bcma_write32(bdev, BCMA_CORE_PCIE2_OMAP0_LOWER, addr); ++ bcma_write32(bdev, BCMA_CORE_PCIE2_OARR0, addr | 0x01); + -+ /* Outbound address translation setup */ -+ size = SZ_128M; -+ 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); ++ 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 @@ -398,107 +309,46 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> + * otherwise DMA bouncing mechanism may be required. + * 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 */ -+ bcma_write32(bdev, SOC_PCIE_SYS_IMAP1(0), addr | 0x1); -+ bcma_write32(bdev, SOC_PCIE_SYS_IARR(1), addr | size); -+ -+#ifdef CONFIG_SPARSEMEM -+ addr = PHYS_OFFSET2; -+ bcma_write32(bdev, SOC_PCIE_SYS_IMAP2(0), addr | 0x1); -+ bcma_write32(bdev, SOC_PCIE_SYS_IARR(2), addr | size); -+#endif ++ 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 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; -+ u8 tmp8; -+ u16 tmp16; ++ 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_write_config8(bdev, 0, devfn, PCI_PRIMARY_BUS, 0); -+ bcma_pcie2_write_config8(bdev, 0, devfn, PCI_SECONDARY_BUS, 1); -+ bcma_pcie2_write_config8(bdev, 0, devfn, PCI_SUBORDINATE_BUS, 4); -+ -+ 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); ++ 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 */ -+ BUG_ON((bdev->addr_s[0] >> 16) & 0xf); -+ bcma_pcie2_write_config16(bdev, 0, devfn, PCI_MEMORY_BASE, -+ bdev->addr_s[0] >> 16); -+ BUG_ON(((bdev->addr_s[0] + SZ_128M) >> 16) & 0xf); -+ bcma_pcie2_write_config16(bdev, 0, devfn, PCI_MEMORY_LIMIT, -+ (bdev->addr_s[0] + SZ_128M) >> 16); ++ 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_config16(bdev, 0, devfn, PCI_IO_BASE_UPPER16, 0); -+ bcma_pcie2_write_config16(bdev, 0, devfn, PCI_IO_LIMIT_UPPER16, 0); ++ 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_config16(bdev, 0, devfn, PCI_CLASS_DEVICE, -+ PCI_CLASS_BRIDGE_PCI); -+ -+ tmp16 = bcma_pcie2_read_config16(bdev, 0, devfn, PCI_CLASS_DEVICE); -+ tmp16 = bcma_pcie2_read_config16(bdev, 0, devfn, PCI_MEMORY_BASE); -+ tmp16 = bcma_pcie2_read_config16(bdev, 0, devfn, PCI_MEMORY_LIMIT); -+} ++ bcma_pcie2_write_config(bdev, 0, 0, PCI_CLASS_DEVICE, 2, ++ PCI_CLASS_BRIDGE_PCI); + -+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; ++ 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) @@ -537,7 +387,7 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> + struct resource *res; + struct bcma_device *arm_core; + u32 cru_straps_ctrl; -+ int allow_gen2, linkfail; ++ int ret; + int phyaddr; + + if (bdev->core_unit == 2) { @@ -561,8 +411,8 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> + return -EINVAL; + + res->start = bdev->addr_s[0]; -+ res->end = res->start + SZ_128M - 1; -+ res->name = "PCIe Configuration Space"; ++ 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); @@ -572,55 +422,36 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> + if (!res) + return -EINVAL; + -+ res->start = bdev->addr_s[0]; -+ res->end = res->start + SZ_128M - 1; -+ res->name = "PCIe Configuration Space"; ++ res->start = 0; ++ res->end = 0; ++ res->name = "PCIe dummy IO space"; + res->flags = IORESOURCE_IO; + + pci_add_resource(&sys->resources, res); + -+ for (allow_gen2 = 0; allow_gen2 <= 1; allow_gen2++) { -+ bcma_pcie2_hw_init(bdev); -+ bcma_pcie2_map_init(bdev); -+ -+ /* -+ * Skip inactive ports - -+ * will need to change this for hot-plugging -+ */ -+ linkfail = bcma_pcie2_check_link(bdev, sys, allow_gen2); -+ if (linkfail) -+ break; -+ -+ bcma_pcie2_bridge_init(bdev); -+ -+ if (allow_gen2 == 0) { -+ if (bcma_pcie2_allow_gen2_rc(bdev) == 0) -+ break; -+ dev_info(&bdev->dev, "switching to GEN2\n"); -+ } -+ } ++ 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, true); ++ if (ret) ++ return ret; + -+ if (linkfail) -+ return -1; ++ ret = bcma_pcie2_bridge_init(bdev, bdev->addr_s[0], SZ_128M); ++ if (ret) ++ return ret; + + 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) +{ -+ struct hw_pci hw; -+ -+ dev_info(&bdev->dev, "scanning bus\n"); -+ -+ hw = (struct hw_pci) { ++ struct hw_pci hw = { + .nr_controllers = 1, + .domain = bdev->core_unit, + .private_data = (void **)&bdev, @@ -629,11 +460,13 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> + .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, SOC_PCIE_SYS_RC_INTX_EN, 0xf); ++ 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); @@ -666,5 +499,5 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> +module_exit(bcma_pcie2_exit); + +MODULE_AUTHOR("Hauke Mehrtens"); -+MODULE_DESCRIPTION("PCIe Gen2 driver for BCMA"); ++MODULE_DESCRIPTION("BCM5301X PCIe host controller"); +MODULE_LICENSE("GPLv2"); diff --git a/target/linux/bcm53xx/patches-3.18/170-pcie2-bcma-add-new-PCIe2-driver-for-bcma.patch b/target/linux/bcm53xx/patches-3.18/170-pcie2-bcma-add-new-PCIe2-driver-for-bcma.patch index 3efdeea218..fc616d7cd3 100644 --- a/target/linux/bcm53xx/patches-3.18/170-pcie2-bcma-add-new-PCIe2-driver-for-bcma.patch +++ b/target/linux/bcm53xx/patches-3.18/170-pcie2-bcma-add-new-PCIe2-driver-for-bcma.patch @@ -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> 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 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 | 2 + - drivers/pci/host/Kconfig | 7 + - drivers/pci/host/Makefile | 1 + - drivers/pci/host/pcie2-bcma.c | 591 ++++++++++++++++++++++++++++++++++++++++++ - 4 files changed, 601 insertions(+) - create mode 100644 drivers/pci/host/pcie2-bcma.c + 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 @@ -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 and have varied lanes from x1 to x8. -+config PCI_BCMA -+ bool "BCMA PCIe2 host controller" -+ depends on BCMA && OF ++config PCI_BCM5301X ++ bool "BCM5301X PCIe2 host controller" ++ depends on BCMA && OF && ARM && PCI_DOMAINS + help -+ Say Y here if you want to support a simple generic PCI host -+ controller, such as the one emulated by kvmtool. ++ 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 @@ -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_PCIE_XILINX) += pcie-xilinx.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 -+++ b/drivers/pci/host/pcie2-bcma.c -@@ -0,0 +1,619 @@ ++++ b/drivers/pci/host/pci-host-bcm5301x.c +@@ -0,0 +1,428 @@ +/* + * Northstar PCI-Express driver + * 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. + * -+ * TODO: -+ * MSI interrupts, -+ * DRAM > 128 MBytes (e.g. DMA zones) ++ * 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/bug.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/bcma/bcma.h> -+ -+#define SI_ENUM_BASE 0x18000000 /* Enumeration space base */ -+ -+/* -+ * 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 ++#include <linux/bcma/bcma_driver_pcie2.h> ++#include <linux/of_irq.h> + +#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) +{ + struct pci_sys_data *sys = pdev->sysdata; @@ -133,16 +119,17 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> + if (busno == 0) { + if (slot >= 1) + return 0; -+ bcma_write32(bdev, SOC_PCIE_EXT_CFG_ADDR, where & 0xffc); -+ return SOC_PCIE_EXT_CFG_DATA; ++ 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, SOC_PCIE_CFG_ADDR, addr_reg); -+ return SOC_PCIE_CFG_DATA; ++ 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, @@ -160,20 +147,6 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> + + 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) + return data_reg; + @@ -208,42 +181,6 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> + 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, + 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, + * otherwise return non-zero + */ -+static int bcma_pcie2_check_link(struct bcma_device *bdev, -+ struct pci_sys_data *sys, u32 allow_gen2) ++static int bcma_pcie2_check_link(struct bcma_device *bdev, bool allow_gen2) +{ + u32 devfn = 0; + u32 tmp32; -+ u16 tmp16; + 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; + if (allow_gen2) + tmp32 |= 2; @@ -299,28 +236,17 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> + /* force PCIE GEN1 */ + 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 */ -+ 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) { + dev_info(&bdev->dev, "Port %d in End-Point mode - ignored\n", + bdev->core_unit); + return -ENODEV; + } + -+ /* NS PAX only changes NLW field when card is present */ -+ 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; ++ return 0; +} + +/* @@ -328,67 +254,52 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> + */ +static void bcma_pcie2_hw_init(struct bcma_device *bdev) +{ -+ u32 devfn = 0; + u32 tmp32; + u16 tmp16; + + /* 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 |= 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 |= (2 << 12) | (2 << 5); -+ bcma_pcie2_write_config32(bdev, 0, devfn, 0xb4, tmp32); -+ -+ /* Turn-on Root-Complex (RC) mode, from reset defailt of EP */ ++ bcma_pcie2_write_config(bdev, 0, 0, 0xb4, 4, tmp32); + -+ /* The mode is set by straps, can be overwritten via DMU -+ register <cru_straps_control> bit 5, "1" means RC ++ /* ++ * 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, SOC_PCIE_CONTROL, 0x3); -+ udelay(250); -+ bcma_write32(bdev, SOC_PCIE_CONTROL, 0x1); -+ mdelay(250); ++ 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 void bcma_pcie2_map_init(struct bcma_device *bdev) ++static int bcma_pcie2_map_init(struct bcma_device *bdev, u32 addr) +{ -+ unsigned size, i; -+ u32 addr; ++ /* 64MB alignment */ ++ if (!addr || (addr & (SZ_64M - 1))) ++ return -EINVAL; + -+ /* -+ * NOTE: -+ * All PCI-to-CPU address mapping are 1:1 for simplicity -+ */ ++ bcma_write32(bdev, BCMA_CORE_PCIE2_OMAP0_LOWER, addr); ++ bcma_write32(bdev, BCMA_CORE_PCIE2_OARR0, addr | 0x01); + -+ /* Outbound address translation setup */ -+ size = SZ_128M; -+ 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); ++ 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 @@ -398,107 +309,46 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> + * otherwise DMA bouncing mechanism may be required. + * 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 */ -+ bcma_write32(bdev, SOC_PCIE_SYS_IMAP1(0), addr | 0x1); -+ bcma_write32(bdev, SOC_PCIE_SYS_IARR(1), addr | size); -+ -+#ifdef CONFIG_SPARSEMEM -+ addr = PHYS_OFFSET2; -+ bcma_write32(bdev, SOC_PCIE_SYS_IMAP2(0), addr | 0x1); -+ bcma_write32(bdev, SOC_PCIE_SYS_IARR(2), addr | size); -+#endif ++ 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 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; -+ u8 tmp8; -+ u16 tmp16; ++ 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_write_config8(bdev, 0, devfn, PCI_PRIMARY_BUS, 0); -+ bcma_pcie2_write_config8(bdev, 0, devfn, PCI_SECONDARY_BUS, 1); -+ bcma_pcie2_write_config8(bdev, 0, devfn, PCI_SUBORDINATE_BUS, 4); -+ -+ 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); ++ 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 */ -+ BUG_ON((bdev->addr_s[0] >> 16) & 0xf); -+ bcma_pcie2_write_config16(bdev, 0, devfn, PCI_MEMORY_BASE, -+ bdev->addr_s[0] >> 16); -+ BUG_ON(((bdev->addr_s[0] + SZ_128M) >> 16) & 0xf); -+ bcma_pcie2_write_config16(bdev, 0, devfn, PCI_MEMORY_LIMIT, -+ (bdev->addr_s[0] + SZ_128M) >> 16); ++ 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_config16(bdev, 0, devfn, PCI_IO_BASE_UPPER16, 0); -+ bcma_pcie2_write_config16(bdev, 0, devfn, PCI_IO_LIMIT_UPPER16, 0); ++ 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_config16(bdev, 0, devfn, PCI_CLASS_DEVICE, -+ PCI_CLASS_BRIDGE_PCI); -+ -+ tmp16 = bcma_pcie2_read_config16(bdev, 0, devfn, PCI_CLASS_DEVICE); -+ tmp16 = bcma_pcie2_read_config16(bdev, 0, devfn, PCI_MEMORY_BASE); -+ tmp16 = bcma_pcie2_read_config16(bdev, 0, devfn, PCI_MEMORY_LIMIT); -+} ++ bcma_pcie2_write_config(bdev, 0, 0, PCI_CLASS_DEVICE, 2, ++ PCI_CLASS_BRIDGE_PCI); + -+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; ++ 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) @@ -537,7 +387,7 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> + struct resource *res; + struct bcma_device *arm_core; + u32 cru_straps_ctrl; -+ int allow_gen2, linkfail; ++ int ret; + int phyaddr; + + if (bdev->core_unit == 2) { @@ -561,8 +411,8 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> + return -EINVAL; + + res->start = bdev->addr_s[0]; -+ res->end = res->start + SZ_128M - 1; -+ res->name = "PCIe Configuration Space"; ++ 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); @@ -572,55 +422,36 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> + if (!res) + return -EINVAL; + -+ res->start = bdev->addr_s[0]; -+ res->end = res->start + SZ_128M - 1; -+ res->name = "PCIe Configuration Space"; ++ res->start = 0; ++ res->end = 0; ++ res->name = "PCIe dummy IO space"; + res->flags = IORESOURCE_IO; + + pci_add_resource(&sys->resources, res); + -+ for (allow_gen2 = 0; allow_gen2 <= 1; allow_gen2++) { -+ bcma_pcie2_hw_init(bdev); -+ bcma_pcie2_map_init(bdev); -+ -+ /* -+ * Skip inactive ports - -+ * will need to change this for hot-plugging -+ */ -+ linkfail = bcma_pcie2_check_link(bdev, sys, allow_gen2); -+ if (linkfail) -+ break; -+ -+ bcma_pcie2_bridge_init(bdev); -+ -+ if (allow_gen2 == 0) { -+ if (bcma_pcie2_allow_gen2_rc(bdev) == 0) -+ break; -+ dev_info(&bdev->dev, "switching to GEN2\n"); -+ } -+ } ++ 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, true); ++ if (ret) ++ return ret; + -+ if (linkfail) -+ return -1; ++ ret = bcma_pcie2_bridge_init(bdev, bdev->addr_s[0], SZ_128M); ++ if (ret) ++ return ret; + + 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) +{ -+ struct hw_pci hw; -+ -+ dev_info(&bdev->dev, "scanning bus\n"); -+ -+ hw = (struct hw_pci) { ++ struct hw_pci hw = { + .nr_controllers = 1, + .domain = bdev->core_unit, + .private_data = (void **)&bdev, @@ -629,11 +460,13 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> + .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, SOC_PCIE_SYS_RC_INTX_EN, 0xf); ++ 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); @@ -666,5 +499,5 @@ Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> +module_exit(bcma_pcie2_exit); + +MODULE_AUTHOR("Hauke Mehrtens"); -+MODULE_DESCRIPTION("PCIe Gen2 driver for BCMA"); ++MODULE_DESCRIPTION("BCM5301X PCIe host controller"); +MODULE_LICENSE("GPLv2"); |