You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge tag 'pci-v4.7-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci
Pull PCI updates from Bjorn Helgaas: "Enumeration: - Refine PCI support check in pcibios_init() (Adrian-Ken Rueegsegger) - Provide common functions for ECAM mapping (Jayachandran C) - Allow all PCIe services on non-ACPI host bridges (Jon Derrick) - Remove return values from pcie_port_platform_notify() and relatives (Jon Derrick) - Widen portdrv service type from 4 bits to 8 bits (Keith Busch) - Add Downstream Port Containment portdrv service type (Keith Busch) - Add Downstream Port Containment driver (Keith Busch) Resource management: - Identify Enhanced Allocation (EA) BAR Equivalent resources in sysfs (Alex Williamson) - Supply CPU physical address (not bus address) to iomem_is_exclusive() (Bjorn Helgaas) - alpha: Call iomem_is_exclusive() for IORESOURCE_MEM, but not IORESOURCE_IO (Bjorn Helgaas) - Mark Broadwell-EP Home Agent 1 as having non-compliant BARs (Prarit Bhargava) - Disable all BAR sizing for devices with non-compliant BARs (Prarit Bhargava) - Move PCI I/O space management from OF to PCI core code (Tomasz Nowicki) PCI device hotplug: - acpiphp_ibm: Avoid uninitialized variable reference (Dan Carpenter) - Use cached copy of PCI_EXP_SLTCAP_HPC bit (Lukas Wunner) Virtualization: - Mark Intel i40e NIC INTx masking as broken (Alex Williamson) - Reverse standard ACS vs device-specific ACS enabling (Alex Williamson) - Work around Intel Sunrise Point PCH incorrect ACS capability (Alex Williamson) IOMMU: - Add pci_add_dma_alias() to abstract implementation (Bjorn Helgaas) - Move informational printk to pci_add_dma_alias() (Bjorn Helgaas) - Add support for multiple DMA aliases (Jacek Lawrynowicz) - Add DMA alias quirk for mic_x200_dma (Jacek Lawrynowicz) Thunderbolt: - Fix double free of drom buffer (Andreas Noever) - Add Intel Thunderbolt device IDs (Lukas Wunner) - Fix typos and magic number (Lukas Wunner) - Support 1st gen Light Ridge controller (Lukas Wunner) Generic host bridge driver: - Use generic ECAM API (Jayachandran C) Cavium ThunderX host bridge driver: - Don't clobber read-only bits in bridge config registers (David Daney) - Use generic ECAM API (Jayachandran C) Freescale i.MX6 host bridge driver: - Use enum instead of bool for variant indicator (Andrey Smirnov) - Implement reset sequence for i.MX6+ (Andrey Smirnov) - Factor out ref clock enable (Bjorn Helgaas) - Add initial imx6sx support (Christoph Fritz) - Add reset-gpio-active-high boolean property to DT (Petr Štetiar) - Add DT property for link gen, default to Gen1 (Tim Harvey) - dts: Specify imx6qp version of PCIe core (Andrey Smirnov) - dts: Fix PCIe reset GPIO polarity on Toradex Apalis Ixora (Petr Štetiar) Marvell Armada host bridge driver: - add DT binding for Marvell Armada 7K/8K PCIe controller (Thomas Petazzoni) - Add driver for Marvell Armada 7K/8K PCIe controller (Thomas Petazzoni) Marvell MVEBU host bridge driver: - Constify mvebu_pcie_pm_ops structure (Jisheng Zhang) - Use SET_NOIRQ_SYSTEM_SLEEP_PM_OPS for mvebu_pcie_pm_ops (Jisheng Zhang) Microsoft Hyper-V host bridge driver: - Report resources release after stopping the bus (Vitaly Kuznetsov) - Add explicit barriers to config space access (Vitaly Kuznetsov) Renesas R-Car host bridge driver: - Select PCI_MSI_IRQ_DOMAIN (Arnd Bergmann) Synopsys DesignWare host bridge driver: - Remove incorrect RC memory base/limit configuration (Gabriele Paoloni) - Move Root Complex setup code to dw_pcie_setup_rc() (Jisheng Zhang) TI Keystone host bridge driver: - Add error IRQ handler (Murali Karicheri) - Remove unnecessary goto statement (Murali Karicheri) Miscellaneous: - Fix spelling errors (Colin Ian King)" * tag 'pci-v4.7-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci: (48 commits) PCI: Disable all BAR sizing for devices with non-compliant BARs x86/PCI: Mark Broadwell-EP Home Agent 1 as having non-compliant BARs PCI: Identify Enhanced Allocation (EA) BAR Equivalent resources in sysfs PCI, of: Move PCI I/O space management to PCI core code PCI: generic, thunder: Use generic ECAM API PCI: Provide common functions for ECAM mapping PCI: hv: Add explicit barriers to config space access PCI: Use cached copy of PCI_EXP_SLTCAP_HPC bit PCI: Add Downstream Port Containment driver PCI: Add Downstream Port Containment portdrv service type PCI: Widen portdrv service type from 4 bits to 8 bits PCI: designware: Remove incorrect RC memory base/limit configuration PCI: hv: Report resources release after stopping the bus ARM: dts: imx6qp: Specify imx6qp version of PCIe core PCI: imx6: Implement reset sequence for i.MX6+ PCI: imx6: Use enum instead of bool for variant indicator PCI: thunder: Don't clobber read-only bits in bridge config registers thunderbolt: Fix double free of drom buffer PCI: rcar: Select PCI_MSI_IRQ_DOMAIN PCI: armada: Add driver for Marvell Armada 7K/8K PCIe controller ...
This commit is contained in:
@@ -4,8 +4,8 @@ This PCIe host controller is based on the Synopsis Designware PCIe IP
|
||||
and thus inherits all the common properties defined in designware-pcie.txt.
|
||||
|
||||
Required properties:
|
||||
- compatible: "fsl,imx6q-pcie"
|
||||
- reg: base addresse and length of the pcie controller
|
||||
- compatible: "fsl,imx6q-pcie", "fsl,imx6sx-pcie", "fsl,imx6qp-pcie"
|
||||
- reg: base address and length of the PCIe controller
|
||||
- interrupts: A list of interrupt outputs of the controller. Must contain an
|
||||
entry for each entry in the interrupt-names property.
|
||||
- interrupt-names: Must include the following entries:
|
||||
@@ -19,6 +19,20 @@ Optional properties:
|
||||
- fsl,tx-deemph-gen2-6db: Gen2 (6db) De-emphasis value. Default: 20
|
||||
- fsl,tx-swing-full: Gen2 TX SWING FULL value. Default: 127
|
||||
- fsl,tx-swing-low: TX launch amplitude swing_low value. Default: 127
|
||||
- fsl,max-link-speed: Specify PCI gen for link capability. Must be '2' for
|
||||
gen2, otherwise will default to gen1. Note that the IMX6 LVDS clock outputs
|
||||
do not meet gen2 jitter requirements and thus for gen2 capability a gen2
|
||||
compliant clock generator should be used and configured.
|
||||
- reset-gpio: Should specify the GPIO for controlling the PCI bus device reset
|
||||
signal. It's not polarity aware and defaults to active-low reset sequence
|
||||
(L=reset state, H=operation state).
|
||||
- reset-gpio-active-high: If present then the reset sequence using the GPIO
|
||||
specified in the "reset-gpio" property is reversed (H=reset state,
|
||||
L=operation state).
|
||||
|
||||
Additional required properties for imx6sx-pcie:
|
||||
- clock names: Must include the following additional entries:
|
||||
- "pcie_inbound_axi"
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
* Marvell Armada 7K/8K PCIe interface
|
||||
|
||||
This PCIe host controller is based on the Synopsis Designware PCIe IP
|
||||
and thus inherits all the common properties defined in designware-pcie.txt.
|
||||
|
||||
Required properties:
|
||||
- compatible: "marvell,armada8k-pcie"
|
||||
- reg: must contain two register regions
|
||||
- the control register region
|
||||
- the config space region
|
||||
- reg-names:
|
||||
- "ctrl" for the control register region
|
||||
- "config" for the config space region
|
||||
- interrupts: Interrupt specifier for the PCIe controler
|
||||
- clocks: reference to the PCIe controller clock
|
||||
|
||||
Example:
|
||||
|
||||
pcie@f2600000 {
|
||||
compatible = "marvell,armada8k-pcie", "snps,dw-pcie";
|
||||
reg = <0 0xf2600000 0 0x10000>, <0 0xf6f00000 0 0x80000>;
|
||||
reg-names = "ctrl", "config";
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
#interrupt-cells = <1>;
|
||||
device_type = "pci";
|
||||
dma-coherent;
|
||||
|
||||
bus-range = <0 0xff>;
|
||||
ranges = <0x81000000 0 0xf9000000 0 0xf9000000 0 0x10000 /* downstream I/O */
|
||||
0x82000000 0 0xf6000000 0 0xf6000000 0 0xf00000>; /* non-prefetchable memory */
|
||||
interrupt-map-mask = <0 0 0 0>;
|
||||
interrupt-map = <0 0 0 0 &gic 0 GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
|
||||
num-lanes = <1>;
|
||||
clocks = <&cpm_syscon0 1 13>;
|
||||
status = "disabled";
|
||||
};
|
||||
@@ -56,6 +56,7 @@ Optional properties:-
|
||||
phy-names: name of the Generic Keystine SerDes phy for PCI
|
||||
- If boot loader already does PCI link establishment, then phys and
|
||||
phy-names shouldn't be present.
|
||||
interrupts: platform interrupt for error interrupts.
|
||||
|
||||
Designware DT Properties not applicable for Keystone PCI
|
||||
|
||||
|
||||
@@ -77,10 +77,10 @@ static int pci_mmap_resource(struct kobject *kobj,
|
||||
if (i >= PCI_ROM_RESOURCE)
|
||||
return -ENODEV;
|
||||
|
||||
if (!__pci_mmap_fits(pdev, i, vma, sparse))
|
||||
if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(res->start))
|
||||
return -EINVAL;
|
||||
|
||||
if (iomem_is_exclusive(res->start))
|
||||
if (!__pci_mmap_fits(pdev, i, vma, sparse))
|
||||
return -EINVAL;
|
||||
|
||||
pcibios_resource_to_bus(pdev->bus, &bar, res);
|
||||
|
||||
@@ -219,8 +219,9 @@
|
||||
};
|
||||
|
||||
&pcie {
|
||||
/* active-low meaning opposite of regular PERST# active-low polarity */
|
||||
reset-gpio = <&gpio1 28 GPIO_ACTIVE_LOW>;
|
||||
/* active-high meaning opposite of regular PERST# active-low polarity */
|
||||
reset-gpio = <&gpio1 28 GPIO_ACTIVE_HIGH>;
|
||||
reset-gpio-active-high;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
|
||||
@@ -82,5 +82,8 @@
|
||||
"ldb_di0", "ldb_di1", "prg";
|
||||
};
|
||||
|
||||
pcie: pcie@0x01000000 {
|
||||
compatible = "fsl,imx6qp-pcie", "snps,dw-pcie";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -516,7 +516,7 @@ void __init pcibios_set_cache_line_size(void)
|
||||
|
||||
int __init pcibios_init(void)
|
||||
{
|
||||
if (!raw_pci_ops) {
|
||||
if (!raw_pci_ops && !raw_pci_ext_ops) {
|
||||
printk(KERN_WARNING "PCI: System does not support PCI\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -552,9 +552,16 @@ static void twinhead_reserve_killing_zone(struct pci_dev *dev)
|
||||
}
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x27B9, twinhead_reserve_killing_zone);
|
||||
|
||||
/*
|
||||
* Broadwell EP Home Agent BARs erroneously return non-zero values when read.
|
||||
*
|
||||
* See http://www.intel.com/content/www/us/en/processors/xeon/xeon-e5-v4-spec-update.html
|
||||
* entry BDF2.
|
||||
*/
|
||||
static void pci_bdwep_bar(struct pci_dev *dev)
|
||||
{
|
||||
dev->non_compliant_bars = 1;
|
||||
}
|
||||
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x6f60, pci_bdwep_bar);
|
||||
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x6fa0, pci_bdwep_bar);
|
||||
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x6fc0, pci_bdwep_bar);
|
||||
|
||||
@@ -263,8 +263,7 @@ static u16 get_alias(struct device *dev)
|
||||
*/
|
||||
if (pci_alias == devid &&
|
||||
PCI_BUS_NUM(ivrs_alias) == pdev->bus->number) {
|
||||
pdev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVFN;
|
||||
pdev->dma_alias_devfn = ivrs_alias & 0xff;
|
||||
pci_add_dma_alias(pdev, ivrs_alias & 0xff);
|
||||
pr_info("AMD-Vi: Added PCI DMA alias %02x.%d for %s\n",
|
||||
PCI_SLOT(ivrs_alias), PCI_FUNC(ivrs_alias),
|
||||
dev_name(dev));
|
||||
|
||||
@@ -660,8 +660,8 @@ static struct iommu_group *get_pci_function_alias_group(struct pci_dev *pdev,
|
||||
}
|
||||
|
||||
/*
|
||||
* Look for aliases to or from the given device for exisiting groups. The
|
||||
* dma_alias_devfn only supports aliases on the same bus, therefore the search
|
||||
* Look for aliases to or from the given device for existing groups. DMA
|
||||
* aliases are only supported on the same bus, therefore the search
|
||||
* space is quite small (especially since we're really only looking at pcie
|
||||
* device, and therefore only expect multiple slots on the root complex or
|
||||
* downstream switch ports). It's conceivable though that a pair of
|
||||
@@ -686,11 +686,7 @@ static struct iommu_group *get_pci_alias_group(struct pci_dev *pdev,
|
||||
continue;
|
||||
|
||||
/* We alias them or they alias us */
|
||||
if (((pdev->dev_flags & PCI_DEV_FLAGS_DMA_ALIAS_DEVFN) &&
|
||||
pdev->dma_alias_devfn == tmp->devfn) ||
|
||||
((tmp->dev_flags & PCI_DEV_FLAGS_DMA_ALIAS_DEVFN) &&
|
||||
tmp->dma_alias_devfn == pdev->devfn)) {
|
||||
|
||||
if (pci_devs_are_dma_aliases(pdev, tmp)) {
|
||||
group = get_pci_alias_group(tmp, devfns);
|
||||
if (group) {
|
||||
pci_dev_put(tmp);
|
||||
|
||||
+1
-115
@@ -4,6 +4,7 @@
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci_regs.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/slab.h>
|
||||
@@ -673,121 +674,6 @@ const __be32 *of_get_address(struct device_node *dev, int index, u64 *size,
|
||||
}
|
||||
EXPORT_SYMBOL(of_get_address);
|
||||
|
||||
#ifdef PCI_IOBASE
|
||||
struct io_range {
|
||||
struct list_head list;
|
||||
phys_addr_t start;
|
||||
resource_size_t size;
|
||||
};
|
||||
|
||||
static LIST_HEAD(io_range_list);
|
||||
static DEFINE_SPINLOCK(io_range_lock);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Record the PCI IO range (expressed as CPU physical address + size).
|
||||
* Return a negative value if an error has occured, zero otherwise
|
||||
*/
|
||||
int __weak pci_register_io_range(phys_addr_t addr, resource_size_t size)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
#ifdef PCI_IOBASE
|
||||
struct io_range *range;
|
||||
resource_size_t allocated_size = 0;
|
||||
|
||||
/* check if the range hasn't been previously recorded */
|
||||
spin_lock(&io_range_lock);
|
||||
list_for_each_entry(range, &io_range_list, list) {
|
||||
if (addr >= range->start && addr + size <= range->start + size) {
|
||||
/* range already registered, bail out */
|
||||
goto end_register;
|
||||
}
|
||||
allocated_size += range->size;
|
||||
}
|
||||
|
||||
/* range not registed yet, check for available space */
|
||||
if (allocated_size + size - 1 > IO_SPACE_LIMIT) {
|
||||
/* if it's too big check if 64K space can be reserved */
|
||||
if (allocated_size + SZ_64K - 1 > IO_SPACE_LIMIT) {
|
||||
err = -E2BIG;
|
||||
goto end_register;
|
||||
}
|
||||
|
||||
size = SZ_64K;
|
||||
pr_warn("Requested IO range too big, new size set to 64K\n");
|
||||
}
|
||||
|
||||
/* add the range to the list */
|
||||
range = kzalloc(sizeof(*range), GFP_ATOMIC);
|
||||
if (!range) {
|
||||
err = -ENOMEM;
|
||||
goto end_register;
|
||||
}
|
||||
|
||||
range->start = addr;
|
||||
range->size = size;
|
||||
|
||||
list_add_tail(&range->list, &io_range_list);
|
||||
|
||||
end_register:
|
||||
spin_unlock(&io_range_lock);
|
||||
#endif
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
phys_addr_t pci_pio_to_address(unsigned long pio)
|
||||
{
|
||||
phys_addr_t address = (phys_addr_t)OF_BAD_ADDR;
|
||||
|
||||
#ifdef PCI_IOBASE
|
||||
struct io_range *range;
|
||||
resource_size_t allocated_size = 0;
|
||||
|
||||
if (pio > IO_SPACE_LIMIT)
|
||||
return address;
|
||||
|
||||
spin_lock(&io_range_lock);
|
||||
list_for_each_entry(range, &io_range_list, list) {
|
||||
if (pio >= allocated_size && pio < allocated_size + range->size) {
|
||||
address = range->start + pio - allocated_size;
|
||||
break;
|
||||
}
|
||||
allocated_size += range->size;
|
||||
}
|
||||
spin_unlock(&io_range_lock);
|
||||
#endif
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
unsigned long __weak pci_address_to_pio(phys_addr_t address)
|
||||
{
|
||||
#ifdef PCI_IOBASE
|
||||
struct io_range *res;
|
||||
resource_size_t offset = 0;
|
||||
unsigned long addr = -1;
|
||||
|
||||
spin_lock(&io_range_lock);
|
||||
list_for_each_entry(res, &io_range_list, list) {
|
||||
if (address >= res->start && address < res->start + res->size) {
|
||||
addr = address - res->start + offset;
|
||||
break;
|
||||
}
|
||||
offset += res->size;
|
||||
}
|
||||
spin_unlock(&io_range_lock);
|
||||
|
||||
return addr;
|
||||
#else
|
||||
if (address > IO_SPACE_LIMIT)
|
||||
return (unsigned long)-1;
|
||||
|
||||
return (unsigned long) address;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int __of_address_to_resource(struct device_node *dev,
|
||||
const __be32 *addrp, u64 size, unsigned int flags,
|
||||
const char *name, struct resource *r)
|
||||
|
||||
@@ -83,6 +83,9 @@ config HT_IRQ
|
||||
config PCI_ATS
|
||||
bool
|
||||
|
||||
config PCI_ECAM
|
||||
bool
|
||||
|
||||
config PCI_IOV
|
||||
bool "PCI IOV support"
|
||||
depends on PCI
|
||||
|
||||
@@ -55,6 +55,8 @@ obj-$(CONFIG_PCI_SYSCALL) += syscall.o
|
||||
|
||||
obj-$(CONFIG_PCI_STUB) += pci-stub.o
|
||||
|
||||
obj-$(CONFIG_PCI_ECAM) += ecam.o
|
||||
|
||||
obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o
|
||||
|
||||
obj-$(CONFIG_OF) += of.o
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright 2016 Broadcom
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2, as
|
||||
* published by the Free Software Foundation (the "GPL").
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License version 2 (GPLv2) for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 (GPLv2) along with this source code.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "ecam.h"
|
||||
|
||||
/*
|
||||
* On 64-bit systems, we do a single ioremap for the whole config space
|
||||
* since we have enough virtual address range available. On 32-bit, we
|
||||
* ioremap the config space for each bus individually.
|
||||
*/
|
||||
static const bool per_bus_mapping = !config_enabled(CONFIG_64BIT);
|
||||
|
||||
/*
|
||||
* Create a PCI config space window
|
||||
* - reserve mem region
|
||||
* - alloc struct pci_config_window with space for all mappings
|
||||
* - ioremap the config space
|
||||
*/
|
||||
struct pci_config_window *pci_ecam_create(struct device *dev,
|
||||
struct resource *cfgres, struct resource *busr,
|
||||
struct pci_ecam_ops *ops)
|
||||
{
|
||||
struct pci_config_window *cfg;
|
||||
unsigned int bus_range, bus_range_max, bsz;
|
||||
struct resource *conflict;
|
||||
int i, err;
|
||||
|
||||
if (busr->start > busr->end)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
|
||||
if (!cfg)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
cfg->ops = ops;
|
||||
cfg->busr.start = busr->start;
|
||||
cfg->busr.end = busr->end;
|
||||
cfg->busr.flags = IORESOURCE_BUS;
|
||||
bus_range = resource_size(&cfg->busr);
|
||||
bus_range_max = resource_size(cfgres) >> ops->bus_shift;
|
||||
if (bus_range > bus_range_max) {
|
||||
bus_range = bus_range_max;
|
||||
cfg->busr.end = busr->start + bus_range - 1;
|
||||
dev_warn(dev, "ECAM area %pR can only accommodate %pR (reduced from %pR desired)\n",
|
||||
cfgres, &cfg->busr, busr);
|
||||
}
|
||||
bsz = 1 << ops->bus_shift;
|
||||
|
||||
cfg->res.start = cfgres->start;
|
||||
cfg->res.end = cfgres->end;
|
||||
cfg->res.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
|
||||
cfg->res.name = "PCI ECAM";
|
||||
|
||||
conflict = request_resource_conflict(&iomem_resource, &cfg->res);
|
||||
if (conflict) {
|
||||
err = -EBUSY;
|
||||
dev_err(dev, "can't claim ECAM area %pR: address conflict with %s %pR\n",
|
||||
&cfg->res, conflict->name, conflict);
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
if (per_bus_mapping) {
|
||||
cfg->winp = kcalloc(bus_range, sizeof(*cfg->winp), GFP_KERNEL);
|
||||
if (!cfg->winp)
|
||||
goto err_exit_malloc;
|
||||
for (i = 0; i < bus_range; i++) {
|
||||
cfg->winp[i] = ioremap(cfgres->start + i * bsz, bsz);
|
||||
if (!cfg->winp[i])
|
||||
goto err_exit_iomap;
|
||||
}
|
||||
} else {
|
||||
cfg->win = ioremap(cfgres->start, bus_range * bsz);
|
||||
if (!cfg->win)
|
||||
goto err_exit_iomap;
|
||||
}
|
||||
|
||||
if (ops->init) {
|
||||
err = ops->init(dev, cfg);
|
||||
if (err)
|
||||
goto err_exit;
|
||||
}
|
||||
dev_info(dev, "ECAM at %pR for %pR\n", &cfg->res, &cfg->busr);
|
||||
return cfg;
|
||||
|
||||
err_exit_iomap:
|
||||
dev_err(dev, "ECAM ioremap failed\n");
|
||||
err_exit_malloc:
|
||||
err = -ENOMEM;
|
||||
err_exit:
|
||||
pci_ecam_free(cfg);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
void pci_ecam_free(struct pci_config_window *cfg)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (per_bus_mapping) {
|
||||
if (cfg->winp) {
|
||||
for (i = 0; i < resource_size(&cfg->busr); i++)
|
||||
if (cfg->winp[i])
|
||||
iounmap(cfg->winp[i]);
|
||||
kfree(cfg->winp);
|
||||
}
|
||||
} else {
|
||||
if (cfg->win)
|
||||
iounmap(cfg->win);
|
||||
}
|
||||
if (cfg->res.parent)
|
||||
release_resource(&cfg->res);
|
||||
kfree(cfg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function to implement the pci_ops ->map_bus method
|
||||
*/
|
||||
void __iomem *pci_ecam_map_bus(struct pci_bus *bus, unsigned int devfn,
|
||||
int where)
|
||||
{
|
||||
struct pci_config_window *cfg = bus->sysdata;
|
||||
unsigned int devfn_shift = cfg->ops->bus_shift - 8;
|
||||
unsigned int busn = bus->number;
|
||||
void __iomem *base;
|
||||
|
||||
if (busn < cfg->busr.start || busn > cfg->busr.end)
|
||||
return NULL;
|
||||
|
||||
busn -= cfg->busr.start;
|
||||
if (per_bus_mapping)
|
||||
base = cfg->winp[busn];
|
||||
else
|
||||
base = cfg->win + (busn << cfg->ops->bus_shift);
|
||||
return base + (devfn << devfn_shift) + where;
|
||||
}
|
||||
|
||||
/* ECAM ops */
|
||||
struct pci_ecam_ops pci_generic_ecam_ops = {
|
||||
.bus_shift = 20,
|
||||
.pci_ops = {
|
||||
.map_bus = pci_ecam_map_bus,
|
||||
.read = pci_generic_config_read,
|
||||
.write = pci_generic_config_write,
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2016 Broadcom
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2, as
|
||||
* published by the Free Software Foundation (the "GPL").
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License version 2 (GPLv2) for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 (GPLv2) along with this source code.
|
||||
*/
|
||||
#ifndef DRIVERS_PCI_ECAM_H
|
||||
#define DRIVERS_PCI_ECAM_H
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
/*
|
||||
* struct to hold pci ops and bus shift of the config window
|
||||
* for a PCI controller.
|
||||
*/
|
||||
struct pci_config_window;
|
||||
struct pci_ecam_ops {
|
||||
unsigned int bus_shift;
|
||||
struct pci_ops pci_ops;
|
||||
int (*init)(struct device *,
|
||||
struct pci_config_window *);
|
||||
};
|
||||
|
||||
/*
|
||||
* struct to hold the mappings of a config space window. This
|
||||
* is expected to be used as sysdata for PCI controllers that
|
||||
* use ECAM.
|
||||
*/
|
||||
struct pci_config_window {
|
||||
struct resource res;
|
||||
struct resource busr;
|
||||
void *priv;
|
||||
struct pci_ecam_ops *ops;
|
||||
union {
|
||||
void __iomem *win; /* 64-bit single mapping */
|
||||
void __iomem **winp; /* 32-bit per-bus mapping */
|
||||
};
|
||||
};
|
||||
|
||||
/* create and free pci_config_window */
|
||||
struct pci_config_window *pci_ecam_create(struct device *dev,
|
||||
struct resource *cfgres, struct resource *busr,
|
||||
struct pci_ecam_ops *ops);
|
||||
void pci_ecam_free(struct pci_config_window *cfg);
|
||||
|
||||
/* map_bus when ->sysdata is an instance of pci_config_window */
|
||||
void __iomem *pci_ecam_map_bus(struct pci_bus *bus, unsigned int devfn,
|
||||
int where);
|
||||
/* default ECAM ops */
|
||||
extern struct pci_ecam_ops pci_generic_ecam_ops;
|
||||
|
||||
#ifdef CONFIG_PCI_HOST_GENERIC
|
||||
/* for DT-based PCI controllers that support ECAM */
|
||||
int pci_host_common_probe(struct platform_device *pdev,
|
||||
struct pci_ecam_ops *ops);
|
||||
#endif
|
||||
#endif
|
||||
@@ -72,11 +72,14 @@ config PCI_RCAR_GEN2
|
||||
config PCIE_RCAR
|
||||
bool "Renesas R-Car PCIe controller"
|
||||
depends on ARCH_RENESAS || (ARM && COMPILE_TEST)
|
||||
select PCI_MSI
|
||||
select PCI_MSI_IRQ_DOMAIN
|
||||
help
|
||||
Say Y here if you want PCIe controller support on R-Car SoCs.
|
||||
|
||||
config PCI_HOST_COMMON
|
||||
bool
|
||||
select PCI_ECAM
|
||||
|
||||
config PCI_HOST_GENERIC
|
||||
bool "Generic PCI host controller"
|
||||
@@ -231,4 +234,15 @@ config PCI_HOST_THUNDER_ECAM
|
||||
help
|
||||
Say Y here if you want ECAM support for CN88XX-Pass-1.x Cavium Thunder SoCs.
|
||||
|
||||
config PCIE_ARMADA_8K
|
||||
bool "Marvell Armada-8K PCIe controller"
|
||||
depends on ARCH_MVEBU
|
||||
select PCIE_DW
|
||||
select PCIEPORTBUS
|
||||
help
|
||||
Say Y here if you want to enable PCIe controller support on
|
||||
Armada-8K SoCs. The PCIe controller on Armada-8K is based on
|
||||
Designware hardware and therefore the driver re-uses the
|
||||
Designware core functions to implement the driver.
|
||||
|
||||
endmenu
|
||||
|
||||
@@ -28,3 +28,4 @@ obj-$(CONFIG_PCI_HISI) += pcie-hisi.o
|
||||
obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o
|
||||
obj-$(CONFIG_PCI_HOST_THUNDER_ECAM) += pci-thunder-ecam.o
|
||||
obj-$(CONFIG_PCI_HOST_THUNDER_PEM) += pci-thunder-pem.o
|
||||
obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o
|
||||
|
||||
@@ -142,13 +142,13 @@ static void dra7xx_pcie_enable_interrupts(struct pcie_port *pp)
|
||||
|
||||
static void dra7xx_pcie_host_init(struct pcie_port *pp)
|
||||
{
|
||||
dw_pcie_setup_rc(pp);
|
||||
|
||||
pp->io_base &= DRA7XX_CPU_TO_BUS_ADDR;
|
||||
pp->mem_base &= DRA7XX_CPU_TO_BUS_ADDR;
|
||||
pp->cfg0_base &= DRA7XX_CPU_TO_BUS_ADDR;
|
||||
pp->cfg1_base &= DRA7XX_CPU_TO_BUS_ADDR;
|
||||
|
||||
dw_pcie_setup_rc(pp);
|
||||
|
||||
dra7xx_pcie_establish_link(pp);
|
||||
if (IS_ENABLED(CONFIG_PCI_MSI))
|
||||
dw_pcie_msi_init(pp);
|
||||
|
||||
@@ -22,27 +22,21 @@
|
||||
#include <linux/of_pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "pci-host-common.h"
|
||||
#include "../ecam.h"
|
||||
|
||||
static void gen_pci_release_of_pci_ranges(struct gen_pci *pci)
|
||||
{
|
||||
pci_free_resource_list(&pci->resources);
|
||||
}
|
||||
|
||||
static int gen_pci_parse_request_of_pci_ranges(struct gen_pci *pci)
|
||||
static int gen_pci_parse_request_of_pci_ranges(struct device *dev,
|
||||
struct list_head *resources, struct resource **bus_range)
|
||||
{
|
||||
int err, res_valid = 0;
|
||||
struct device *dev = pci->host.dev.parent;
|
||||
struct device_node *np = dev->of_node;
|
||||
resource_size_t iobase;
|
||||
struct resource_entry *win;
|
||||
|
||||
err = of_pci_get_host_bridge_resources(np, 0, 0xff, &pci->resources,
|
||||
&iobase);
|
||||
err = of_pci_get_host_bridge_resources(np, 0, 0xff, resources, &iobase);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
resource_list_for_each_entry(win, &pci->resources) {
|
||||
resource_list_for_each_entry(win, resources) {
|
||||
struct resource *parent, *res = win->res;
|
||||
|
||||
switch (resource_type(res)) {
|
||||
@@ -60,7 +54,7 @@ static int gen_pci_parse_request_of_pci_ranges(struct gen_pci *pci)
|
||||
res_valid |= !(res->flags & IORESOURCE_PREFETCH);
|
||||
break;
|
||||
case IORESOURCE_BUS:
|
||||
pci->cfg.bus_range = res;
|
||||
*bus_range = res;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
@@ -79,65 +73,60 @@ static int gen_pci_parse_request_of_pci_ranges(struct gen_pci *pci)
|
||||
return 0;
|
||||
|
||||
out_release_res:
|
||||
gen_pci_release_of_pci_ranges(pci);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int gen_pci_parse_map_cfg_windows(struct gen_pci *pci)
|
||||
static void gen_pci_unmap_cfg(void *ptr)
|
||||
{
|
||||
pci_ecam_free((struct pci_config_window *)ptr);
|
||||
}
|
||||
|
||||
static struct pci_config_window *gen_pci_init(struct device *dev,
|
||||
struct list_head *resources, struct pci_ecam_ops *ops)
|
||||
{
|
||||
int err;
|
||||
u8 bus_max;
|
||||
resource_size_t busn;
|
||||
struct resource *bus_range;
|
||||
struct device *dev = pci->host.dev.parent;
|
||||
struct device_node *np = dev->of_node;
|
||||
u32 sz = 1 << pci->cfg.ops->bus_shift;
|
||||
struct resource cfgres;
|
||||
struct resource *bus_range = NULL;
|
||||
struct pci_config_window *cfg;
|
||||
|
||||
err = of_address_to_resource(np, 0, &pci->cfg.res);
|
||||
/* Parse our PCI ranges and request their resources */
|
||||
err = gen_pci_parse_request_of_pci_ranges(dev, resources, &bus_range);
|
||||
if (err)
|
||||
goto err_out;
|
||||
|
||||
err = of_address_to_resource(dev->of_node, 0, &cfgres);
|
||||
if (err) {
|
||||
dev_err(dev, "missing \"reg\" property\n");
|
||||
return err;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
/* Limit the bus-range to fit within reg */
|
||||
bus_max = pci->cfg.bus_range->start +
|
||||
(resource_size(&pci->cfg.res) >> pci->cfg.ops->bus_shift) - 1;
|
||||
pci->cfg.bus_range->end = min_t(resource_size_t,
|
||||
pci->cfg.bus_range->end, bus_max);
|
||||
|
||||
pci->cfg.win = devm_kcalloc(dev, resource_size(pci->cfg.bus_range),
|
||||
sizeof(*pci->cfg.win), GFP_KERNEL);
|
||||
if (!pci->cfg.win)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Map our Configuration Space windows */
|
||||
if (!devm_request_mem_region(dev, pci->cfg.res.start,
|
||||
resource_size(&pci->cfg.res),
|
||||
"Configuration Space"))
|
||||
return -ENOMEM;
|
||||
|
||||
bus_range = pci->cfg.bus_range;
|
||||
for (busn = bus_range->start; busn <= bus_range->end; ++busn) {
|
||||
u32 idx = busn - bus_range->start;
|
||||
|
||||
pci->cfg.win[idx] = devm_ioremap(dev,
|
||||
pci->cfg.res.start + idx * sz,
|
||||
sz);
|
||||
if (!pci->cfg.win[idx])
|
||||
return -ENOMEM;
|
||||
cfg = pci_ecam_create(dev, &cfgres, bus_range, ops);
|
||||
if (IS_ERR(cfg)) {
|
||||
err = PTR_ERR(cfg);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err = devm_add_action(dev, gen_pci_unmap_cfg, cfg);
|
||||
if (err) {
|
||||
gen_pci_unmap_cfg(cfg);
|
||||
goto err_out;
|
||||
}
|
||||
return cfg;
|
||||
|
||||
err_out:
|
||||
pci_free_resource_list(resources);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
int pci_host_common_probe(struct platform_device *pdev,
|
||||
struct gen_pci *pci)
|
||||
struct pci_ecam_ops *ops)
|
||||
{
|
||||
int err;
|
||||
const char *type;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct pci_bus *bus, *child;
|
||||
struct pci_config_window *cfg;
|
||||
struct list_head resources;
|
||||
|
||||
type = of_get_property(np, "device_type", NULL);
|
||||
if (!type || strcmp(type, "pci")) {
|
||||
@@ -147,29 +136,18 @@ int pci_host_common_probe(struct platform_device *pdev,
|
||||
|
||||
of_pci_check_probe_only();
|
||||
|
||||
pci->host.dev.parent = dev;
|
||||
INIT_LIST_HEAD(&pci->host.windows);
|
||||
INIT_LIST_HEAD(&pci->resources);
|
||||
|
||||
/* Parse our PCI ranges and request their resources */
|
||||
err = gen_pci_parse_request_of_pci_ranges(pci);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Parse and map our Configuration Space windows */
|
||||
err = gen_pci_parse_map_cfg_windows(pci);
|
||||
if (err) {
|
||||
gen_pci_release_of_pci_ranges(pci);
|
||||
return err;
|
||||
}
|
||||
INIT_LIST_HEAD(&resources);
|
||||
cfg = gen_pci_init(dev, &resources, ops);
|
||||
if (IS_ERR(cfg))
|
||||
return PTR_ERR(cfg);
|
||||
|
||||
/* Do not reassign resources if probe only */
|
||||
if (!pci_has_flag(PCI_PROBE_ONLY))
|
||||
pci_add_flags(PCI_REASSIGN_ALL_RSRC | PCI_REASSIGN_ALL_BUS);
|
||||
|
||||
|
||||
bus = pci_scan_root_bus(dev, pci->cfg.bus_range->start,
|
||||
&pci->cfg.ops->ops, pci, &pci->resources);
|
||||
bus = pci_scan_root_bus(dev, cfg->busr.start, &ops->pci_ops, cfg,
|
||||
&resources);
|
||||
if (!bus) {
|
||||
dev_err(dev, "Scanning rootbus failed");
|
||||
return -ENODEV;
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright (C) 2014 ARM Limited
|
||||
*
|
||||
* Author: Will Deacon <will.deacon@arm.com>
|
||||
*/
|
||||
|
||||
#ifndef _PCI_HOST_COMMON_H
|
||||
#define _PCI_HOST_COMMON_H
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
struct gen_pci_cfg_bus_ops {
|
||||
u32 bus_shift;
|
||||
struct pci_ops ops;
|
||||
};
|
||||
|
||||
struct gen_pci_cfg_windows {
|
||||
struct resource res;
|
||||
struct resource *bus_range;
|
||||
void __iomem **win;
|
||||
|
||||
struct gen_pci_cfg_bus_ops *ops;
|
||||
};
|
||||
|
||||
struct gen_pci {
|
||||
struct pci_host_bridge host;
|
||||
struct gen_pci_cfg_windows cfg;
|
||||
struct list_head resources;
|
||||
};
|
||||
|
||||
int pci_host_common_probe(struct platform_device *pdev,
|
||||
struct gen_pci *pci);
|
||||
|
||||
#endif /* _PCI_HOST_COMMON_H */
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user