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.
|
and thus inherits all the common properties defined in designware-pcie.txt.
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible: "fsl,imx6q-pcie"
|
- compatible: "fsl,imx6q-pcie", "fsl,imx6sx-pcie", "fsl,imx6qp-pcie"
|
||||||
- reg: base addresse and length of the pcie controller
|
- reg: base address and length of the PCIe controller
|
||||||
- interrupts: A list of interrupt outputs of the controller. Must contain an
|
- interrupts: A list of interrupt outputs of the controller. Must contain an
|
||||||
entry for each entry in the interrupt-names property.
|
entry for each entry in the interrupt-names property.
|
||||||
- interrupt-names: Must include the following entries:
|
- 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-deemph-gen2-6db: Gen2 (6db) De-emphasis value. Default: 20
|
||||||
- fsl,tx-swing-full: Gen2 TX SWING FULL value. Default: 127
|
- fsl,tx-swing-full: Gen2 TX SWING FULL value. Default: 127
|
||||||
- fsl,tx-swing-low: TX launch amplitude swing_low 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:
|
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
|
phy-names: name of the Generic Keystine SerDes phy for PCI
|
||||||
- If boot loader already does PCI link establishment, then phys and
|
- If boot loader already does PCI link establishment, then phys and
|
||||||
phy-names shouldn't be present.
|
phy-names shouldn't be present.
|
||||||
|
interrupts: platform interrupt for error interrupts.
|
||||||
|
|
||||||
Designware DT Properties not applicable for Keystone PCI
|
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)
|
if (i >= PCI_ROM_RESOURCE)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
if (!__pci_mmap_fits(pdev, i, vma, sparse))
|
if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(res->start))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (iomem_is_exclusive(res->start))
|
if (!__pci_mmap_fits(pdev, i, vma, sparse))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
pcibios_resource_to_bus(pdev->bus, &bar, res);
|
pcibios_resource_to_bus(pdev->bus, &bar, res);
|
||||||
|
|||||||
@@ -219,8 +219,9 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
&pcie {
|
&pcie {
|
||||||
/* active-low meaning opposite of regular PERST# active-low polarity */
|
/* active-high meaning opposite of regular PERST# active-low polarity */
|
||||||
reset-gpio = <&gpio1 28 GPIO_ACTIVE_LOW>;
|
reset-gpio = <&gpio1 28 GPIO_ACTIVE_HIGH>;
|
||||||
|
reset-gpio-active-high;
|
||||||
status = "okay";
|
status = "okay";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -82,5 +82,8 @@
|
|||||||
"ldb_di0", "ldb_di1", "prg";
|
"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)
|
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");
|
printk(KERN_WARNING "PCI: System does not support PCI\n");
|
||||||
return 0;
|
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);
|
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)
|
static void pci_bdwep_bar(struct pci_dev *dev)
|
||||||
{
|
{
|
||||||
dev->non_compliant_bars = 1;
|
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, 0x6fa0, pci_bdwep_bar);
|
||||||
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x6fc0, 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 &&
|
if (pci_alias == devid &&
|
||||||
PCI_BUS_NUM(ivrs_alias) == pdev->bus->number) {
|
PCI_BUS_NUM(ivrs_alias) == pdev->bus->number) {
|
||||||
pdev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVFN;
|
pci_add_dma_alias(pdev, ivrs_alias & 0xff);
|
||||||
pdev->dma_alias_devfn = ivrs_alias & 0xff;
|
|
||||||
pr_info("AMD-Vi: Added PCI DMA alias %02x.%d for %s\n",
|
pr_info("AMD-Vi: Added PCI DMA alias %02x.%d for %s\n",
|
||||||
PCI_SLOT(ivrs_alias), PCI_FUNC(ivrs_alias),
|
PCI_SLOT(ivrs_alias), PCI_FUNC(ivrs_alias),
|
||||||
dev_name(dev));
|
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
|
* Look for aliases to or from the given device for existing groups. DMA
|
||||||
* dma_alias_devfn only supports aliases on the same bus, therefore the search
|
* aliases are only supported on the same bus, therefore the search
|
||||||
* space is quite small (especially since we're really only looking at pcie
|
* 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
|
* device, and therefore only expect multiple slots on the root complex or
|
||||||
* downstream switch ports). It's conceivable though that a pair of
|
* 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;
|
continue;
|
||||||
|
|
||||||
/* We alias them or they alias us */
|
/* We alias them or they alias us */
|
||||||
if (((pdev->dev_flags & PCI_DEV_FLAGS_DMA_ALIAS_DEVFN) &&
|
if (pci_devs_are_dma_aliases(pdev, tmp)) {
|
||||||
pdev->dma_alias_devfn == tmp->devfn) ||
|
|
||||||
((tmp->dev_flags & PCI_DEV_FLAGS_DMA_ALIAS_DEVFN) &&
|
|
||||||
tmp->dma_alias_devfn == pdev->devfn)) {
|
|
||||||
|
|
||||||
group = get_pci_alias_group(tmp, devfns);
|
group = get_pci_alias_group(tmp, devfns);
|
||||||
if (group) {
|
if (group) {
|
||||||
pci_dev_put(tmp);
|
pci_dev_put(tmp);
|
||||||
|
|||||||
+1
-115
@@ -4,6 +4,7 @@
|
|||||||
#include <linux/ioport.h>
|
#include <linux/ioport.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of_address.h>
|
#include <linux/of_address.h>
|
||||||
|
#include <linux/pci.h>
|
||||||
#include <linux/pci_regs.h>
|
#include <linux/pci_regs.h>
|
||||||
#include <linux/sizes.h>
|
#include <linux/sizes.h>
|
||||||
#include <linux/slab.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);
|
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,
|
static int __of_address_to_resource(struct device_node *dev,
|
||||||
const __be32 *addrp, u64 size, unsigned int flags,
|
const __be32 *addrp, u64 size, unsigned int flags,
|
||||||
const char *name, struct resource *r)
|
const char *name, struct resource *r)
|
||||||
|
|||||||
@@ -83,6 +83,9 @@ config HT_IRQ
|
|||||||
config PCI_ATS
|
config PCI_ATS
|
||||||
bool
|
bool
|
||||||
|
|
||||||
|
config PCI_ECAM
|
||||||
|
bool
|
||||||
|
|
||||||
config PCI_IOV
|
config PCI_IOV
|
||||||
bool "PCI IOV support"
|
bool "PCI IOV support"
|
||||||
depends on PCI
|
depends on PCI
|
||||||
|
|||||||
@@ -55,6 +55,8 @@ obj-$(CONFIG_PCI_SYSCALL) += syscall.o
|
|||||||
|
|
||||||
obj-$(CONFIG_PCI_STUB) += pci-stub.o
|
obj-$(CONFIG_PCI_STUB) += pci-stub.o
|
||||||
|
|
||||||
|
obj-$(CONFIG_PCI_ECAM) += ecam.o
|
||||||
|
|
||||||
obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o
|
obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o
|
||||||
|
|
||||||
obj-$(CONFIG_OF) += of.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
|
config PCIE_RCAR
|
||||||
bool "Renesas R-Car PCIe controller"
|
bool "Renesas R-Car PCIe controller"
|
||||||
depends on ARCH_RENESAS || (ARM && COMPILE_TEST)
|
depends on ARCH_RENESAS || (ARM && COMPILE_TEST)
|
||||||
|
select PCI_MSI
|
||||||
|
select PCI_MSI_IRQ_DOMAIN
|
||||||
help
|
help
|
||||||
Say Y here if you want PCIe controller support on R-Car SoCs.
|
Say Y here if you want PCIe controller support on R-Car SoCs.
|
||||||
|
|
||||||
config PCI_HOST_COMMON
|
config PCI_HOST_COMMON
|
||||||
bool
|
bool
|
||||||
|
select PCI_ECAM
|
||||||
|
|
||||||
config PCI_HOST_GENERIC
|
config PCI_HOST_GENERIC
|
||||||
bool "Generic PCI host controller"
|
bool "Generic PCI host controller"
|
||||||
@@ -231,4 +234,15 @@ config PCI_HOST_THUNDER_ECAM
|
|||||||
help
|
help
|
||||||
Say Y here if you want ECAM support for CN88XX-Pass-1.x Cavium Thunder SoCs.
|
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
|
endmenu
|
||||||
|
|||||||
@@ -28,3 +28,4 @@ obj-$(CONFIG_PCI_HISI) += pcie-hisi.o
|
|||||||
obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o
|
obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o
|
||||||
obj-$(CONFIG_PCI_HOST_THUNDER_ECAM) += pci-thunder-ecam.o
|
obj-$(CONFIG_PCI_HOST_THUNDER_ECAM) += pci-thunder-ecam.o
|
||||||
obj-$(CONFIG_PCI_HOST_THUNDER_PEM) += pci-thunder-pem.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)
|
static void dra7xx_pcie_host_init(struct pcie_port *pp)
|
||||||
{
|
{
|
||||||
dw_pcie_setup_rc(pp);
|
|
||||||
|
|
||||||
pp->io_base &= DRA7XX_CPU_TO_BUS_ADDR;
|
pp->io_base &= DRA7XX_CPU_TO_BUS_ADDR;
|
||||||
pp->mem_base &= DRA7XX_CPU_TO_BUS_ADDR;
|
pp->mem_base &= DRA7XX_CPU_TO_BUS_ADDR;
|
||||||
pp->cfg0_base &= DRA7XX_CPU_TO_BUS_ADDR;
|
pp->cfg0_base &= DRA7XX_CPU_TO_BUS_ADDR;
|
||||||
pp->cfg1_base &= DRA7XX_CPU_TO_BUS_ADDR;
|
pp->cfg1_base &= DRA7XX_CPU_TO_BUS_ADDR;
|
||||||
|
|
||||||
|
dw_pcie_setup_rc(pp);
|
||||||
|
|
||||||
dra7xx_pcie_establish_link(pp);
|
dra7xx_pcie_establish_link(pp);
|
||||||
if (IS_ENABLED(CONFIG_PCI_MSI))
|
if (IS_ENABLED(CONFIG_PCI_MSI))
|
||||||
dw_pcie_msi_init(pp);
|
dw_pcie_msi_init(pp);
|
||||||
|
|||||||
@@ -22,27 +22,21 @@
|
|||||||
#include <linux/of_pci.h>
|
#include <linux/of_pci.h>
|
||||||
#include <linux/platform_device.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)
|
static int gen_pci_parse_request_of_pci_ranges(struct device *dev,
|
||||||
{
|
struct list_head *resources, struct resource **bus_range)
|
||||||
pci_free_resource_list(&pci->resources);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int gen_pci_parse_request_of_pci_ranges(struct gen_pci *pci)
|
|
||||||
{
|
{
|
||||||
int err, res_valid = 0;
|
int err, res_valid = 0;
|
||||||
struct device *dev = pci->host.dev.parent;
|
|
||||||
struct device_node *np = dev->of_node;
|
struct device_node *np = dev->of_node;
|
||||||
resource_size_t iobase;
|
resource_size_t iobase;
|
||||||
struct resource_entry *win;
|
struct resource_entry *win;
|
||||||
|
|
||||||
err = of_pci_get_host_bridge_resources(np, 0, 0xff, &pci->resources,
|
err = of_pci_get_host_bridge_resources(np, 0, 0xff, resources, &iobase);
|
||||||
&iobase);
|
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
resource_list_for_each_entry(win, &pci->resources) {
|
resource_list_for_each_entry(win, resources) {
|
||||||
struct resource *parent, *res = win->res;
|
struct resource *parent, *res = win->res;
|
||||||
|
|
||||||
switch (resource_type(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);
|
res_valid |= !(res->flags & IORESOURCE_PREFETCH);
|
||||||
break;
|
break;
|
||||||
case IORESOURCE_BUS:
|
case IORESOURCE_BUS:
|
||||||
pci->cfg.bus_range = res;
|
*bus_range = res;
|
||||||
default:
|
default:
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -79,65 +73,60 @@ static int gen_pci_parse_request_of_pci_ranges(struct gen_pci *pci)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out_release_res:
|
out_release_res:
|
||||||
gen_pci_release_of_pci_ranges(pci);
|
|
||||||
return err;
|
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;
|
int err;
|
||||||
u8 bus_max;
|
struct resource cfgres;
|
||||||
resource_size_t busn;
|
struct resource *bus_range = NULL;
|
||||||
struct resource *bus_range;
|
struct pci_config_window *cfg;
|
||||||
struct device *dev = pci->host.dev.parent;
|
|
||||||
struct device_node *np = dev->of_node;
|
|
||||||
u32 sz = 1 << pci->cfg.ops->bus_shift;
|
|
||||||
|
|
||||||
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) {
|
if (err) {
|
||||||
dev_err(dev, "missing \"reg\" property\n");
|
dev_err(dev, "missing \"reg\" property\n");
|
||||||
return err;
|
goto err_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Limit the bus-range to fit within reg */
|
cfg = pci_ecam_create(dev, &cfgres, bus_range, ops);
|
||||||
bus_max = pci->cfg.bus_range->start +
|
if (IS_ERR(cfg)) {
|
||||||
(resource_size(&pci->cfg.res) >> pci->cfg.ops->bus_shift) - 1;
|
err = PTR_ERR(cfg);
|
||||||
pci->cfg.bus_range->end = min_t(resource_size_t,
|
goto err_out;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
int pci_host_common_probe(struct platform_device *pdev,
|
||||||
struct gen_pci *pci)
|
struct pci_ecam_ops *ops)
|
||||||
{
|
{
|
||||||
int err;
|
|
||||||
const char *type;
|
const char *type;
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct device_node *np = dev->of_node;
|
struct device_node *np = dev->of_node;
|
||||||
struct pci_bus *bus, *child;
|
struct pci_bus *bus, *child;
|
||||||
|
struct pci_config_window *cfg;
|
||||||
|
struct list_head resources;
|
||||||
|
|
||||||
type = of_get_property(np, "device_type", NULL);
|
type = of_get_property(np, "device_type", NULL);
|
||||||
if (!type || strcmp(type, "pci")) {
|
if (!type || strcmp(type, "pci")) {
|
||||||
@@ -147,29 +136,18 @@ int pci_host_common_probe(struct platform_device *pdev,
|
|||||||
|
|
||||||
of_pci_check_probe_only();
|
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 */
|
/* Parse and map our Configuration Space windows */
|
||||||
err = gen_pci_parse_map_cfg_windows(pci);
|
INIT_LIST_HEAD(&resources);
|
||||||
if (err) {
|
cfg = gen_pci_init(dev, &resources, ops);
|
||||||
gen_pci_release_of_pci_ranges(pci);
|
if (IS_ERR(cfg))
|
||||||
return err;
|
return PTR_ERR(cfg);
|
||||||
}
|
|
||||||
|
|
||||||
/* Do not reassign resources if probe only */
|
/* Do not reassign resources if probe only */
|
||||||
if (!pci_has_flag(PCI_PROBE_ONLY))
|
if (!pci_has_flag(PCI_PROBE_ONLY))
|
||||||
pci_add_flags(PCI_REASSIGN_ALL_RSRC | PCI_REASSIGN_ALL_BUS);
|
pci_add_flags(PCI_REASSIGN_ALL_RSRC | PCI_REASSIGN_ALL_BUS);
|
||||||
|
|
||||||
|
bus = pci_scan_root_bus(dev, cfg->busr.start, &ops->pci_ops, cfg,
|
||||||
bus = pci_scan_root_bus(dev, pci->cfg.bus_range->start,
|
&resources);
|
||||||
&pci->cfg.ops->ops, pci, &pci->resources);
|
|
||||||
if (!bus) {
|
if (!bus) {
|
||||||
dev_err(dev, "Scanning rootbus failed");
|
dev_err(dev, "Scanning rootbus failed");
|
||||||
return -ENODEV;
|
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 */
|
|
||||||
@@ -25,41 +25,12 @@
|
|||||||
#include <linux/of_pci.h>
|
#include <linux/of_pci.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
|
||||||
#include "pci-host-common.h"
|
#include "../ecam.h"
|
||||||
|
|
||||||
static void __iomem *gen_pci_map_cfg_bus_cam(struct pci_bus *bus,
|
static struct pci_ecam_ops gen_pci_cfg_cam_bus_ops = {
|
||||||
unsigned int devfn,
|
|
||||||
int where)
|
|
||||||
{
|
|
||||||
struct gen_pci *pci = bus->sysdata;
|
|
||||||
resource_size_t idx = bus->number - pci->cfg.bus_range->start;
|
|
||||||
|
|
||||||
return pci->cfg.win[idx] + ((devfn << 8) | where);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct gen_pci_cfg_bus_ops gen_pci_cfg_cam_bus_ops = {
|
|
||||||
.bus_shift = 16,
|
.bus_shift = 16,
|
||||||
.ops = {
|
.pci_ops = {
|
||||||
.map_bus = gen_pci_map_cfg_bus_cam,
|
.map_bus = pci_ecam_map_bus,
|
||||||
.read = pci_generic_config_read,
|
|
||||||
.write = pci_generic_config_write,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static void __iomem *gen_pci_map_cfg_bus_ecam(struct pci_bus *bus,
|
|
||||||
unsigned int devfn,
|
|
||||||
int where)
|
|
||||||
{
|
|
||||||
struct gen_pci *pci = bus->sysdata;
|
|
||||||
resource_size_t idx = bus->number - pci->cfg.bus_range->start;
|
|
||||||
|
|
||||||
return pci->cfg.win[idx] + ((devfn << 12) | where);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct gen_pci_cfg_bus_ops gen_pci_cfg_ecam_bus_ops = {
|
|
||||||
.bus_shift = 20,
|
|
||||||
.ops = {
|
|
||||||
.map_bus = gen_pci_map_cfg_bus_ecam,
|
|
||||||
.read = pci_generic_config_read,
|
.read = pci_generic_config_read,
|
||||||
.write = pci_generic_config_write,
|
.write = pci_generic_config_write,
|
||||||
}
|
}
|
||||||
@@ -70,25 +41,22 @@ static const struct of_device_id gen_pci_of_match[] = {
|
|||||||
.data = &gen_pci_cfg_cam_bus_ops },
|
.data = &gen_pci_cfg_cam_bus_ops },
|
||||||
|
|
||||||
{ .compatible = "pci-host-ecam-generic",
|
{ .compatible = "pci-host-ecam-generic",
|
||||||
.data = &gen_pci_cfg_ecam_bus_ops },
|
.data = &pci_generic_ecam_ops },
|
||||||
|
|
||||||
{ },
|
{ },
|
||||||
};
|
};
|
||||||
|
|
||||||
MODULE_DEVICE_TABLE(of, gen_pci_of_match);
|
MODULE_DEVICE_TABLE(of, gen_pci_of_match);
|
||||||
|
|
||||||
static int gen_pci_probe(struct platform_device *pdev)
|
static int gen_pci_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device *dev = &pdev->dev;
|
|
||||||
const struct of_device_id *of_id;
|
const struct of_device_id *of_id;
|
||||||
struct gen_pci *pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
|
struct pci_ecam_ops *ops;
|
||||||
|
|
||||||
if (!pci)
|
of_id = of_match_node(gen_pci_of_match, pdev->dev.of_node);
|
||||||
return -ENOMEM;
|
ops = (struct pci_ecam_ops *)of_id->data;
|
||||||
|
|
||||||
of_id = of_match_node(gen_pci_of_match, dev->of_node);
|
return pci_host_common_probe(pdev, ops);
|
||||||
pci->cfg.ops = (struct gen_pci_cfg_bus_ops *)of_id->data;
|
|
||||||
|
|
||||||
return pci_host_common_probe(pdev, pci);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct platform_driver gen_pci_driver = {
|
static struct platform_driver gen_pci_driver = {
|
||||||
|
|||||||
@@ -553,6 +553,8 @@ static void _hv_pcifront_read_config(struct hv_pci_dev *hpdev, int where,
|
|||||||
spin_lock_irqsave(&hpdev->hbus->config_lock, flags);
|
spin_lock_irqsave(&hpdev->hbus->config_lock, flags);
|
||||||
/* Choose the function to be read. (See comment above) */
|
/* Choose the function to be read. (See comment above) */
|
||||||
writel(hpdev->desc.win_slot.slot, hpdev->hbus->cfg_addr);
|
writel(hpdev->desc.win_slot.slot, hpdev->hbus->cfg_addr);
|
||||||
|
/* Make sure the function was chosen before we start reading. */
|
||||||
|
mb();
|
||||||
/* Read from that function's config space. */
|
/* Read from that function's config space. */
|
||||||
switch (size) {
|
switch (size) {
|
||||||
case 1:
|
case 1:
|
||||||
@@ -565,6 +567,11 @@ static void _hv_pcifront_read_config(struct hv_pci_dev *hpdev, int where,
|
|||||||
*val = readl(addr);
|
*val = readl(addr);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
* Make sure the write was done before we release the spinlock
|
||||||
|
* allowing consecutive reads/writes.
|
||||||
|
*/
|
||||||
|
mb();
|
||||||
spin_unlock_irqrestore(&hpdev->hbus->config_lock, flags);
|
spin_unlock_irqrestore(&hpdev->hbus->config_lock, flags);
|
||||||
} else {
|
} else {
|
||||||
dev_err(&hpdev->hbus->hdev->device,
|
dev_err(&hpdev->hbus->hdev->device,
|
||||||
@@ -592,6 +599,8 @@ static void _hv_pcifront_write_config(struct hv_pci_dev *hpdev, int where,
|
|||||||
spin_lock_irqsave(&hpdev->hbus->config_lock, flags);
|
spin_lock_irqsave(&hpdev->hbus->config_lock, flags);
|
||||||
/* Choose the function to be written. (See comment above) */
|
/* Choose the function to be written. (See comment above) */
|
||||||
writel(hpdev->desc.win_slot.slot, hpdev->hbus->cfg_addr);
|
writel(hpdev->desc.win_slot.slot, hpdev->hbus->cfg_addr);
|
||||||
|
/* Make sure the function was chosen before we start writing. */
|
||||||
|
wmb();
|
||||||
/* Write to that function's config space. */
|
/* Write to that function's config space. */
|
||||||
switch (size) {
|
switch (size) {
|
||||||
case 1:
|
case 1:
|
||||||
@@ -604,6 +613,11 @@ static void _hv_pcifront_write_config(struct hv_pci_dev *hpdev, int where,
|
|||||||
writel(val, addr);
|
writel(val, addr);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
* Make sure the write was done before we release the spinlock
|
||||||
|
* allowing consecutive reads/writes.
|
||||||
|
*/
|
||||||
|
mb();
|
||||||
spin_unlock_irqrestore(&hpdev->hbus->config_lock, flags);
|
spin_unlock_irqrestore(&hpdev->hbus->config_lock, flags);
|
||||||
} else {
|
} else {
|
||||||
dev_err(&hpdev->hbus->hdev->device,
|
dev_err(&hpdev->hbus->hdev->device,
|
||||||
@@ -2268,11 +2282,6 @@ static int hv_pci_remove(struct hv_device *hdev)
|
|||||||
|
|
||||||
hbus = hv_get_drvdata(hdev);
|
hbus = hv_get_drvdata(hdev);
|
||||||
|
|
||||||
ret = hv_send_resources_released(hdev);
|
|
||||||
if (ret)
|
|
||||||
dev_err(&hdev->device,
|
|
||||||
"Couldn't send resources released packet(s)\n");
|
|
||||||
|
|
||||||
memset(&pkt.teardown_packet, 0, sizeof(pkt.teardown_packet));
|
memset(&pkt.teardown_packet, 0, sizeof(pkt.teardown_packet));
|
||||||
init_completion(&comp_pkt.host_event);
|
init_completion(&comp_pkt.host_event);
|
||||||
pkt.teardown_packet.completion_func = hv_pci_generic_compl;
|
pkt.teardown_packet.completion_func = hv_pci_generic_compl;
|
||||||
@@ -2295,6 +2304,11 @@ static int hv_pci_remove(struct hv_device *hdev)
|
|||||||
pci_unlock_rescan_remove();
|
pci_unlock_rescan_remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = hv_send_resources_released(hdev);
|
||||||
|
if (ret)
|
||||||
|
dev_err(&hdev->device,
|
||||||
|
"Couldn't send resources released packet(s)\n");
|
||||||
|
|
||||||
vmbus_close(hdev->channel);
|
vmbus_close(hdev->channel);
|
||||||
|
|
||||||
/* Delete any children which might still exist. */
|
/* Delete any children which might still exist. */
|
||||||
|
|||||||
+140
-27
@@ -19,6 +19,7 @@
|
|||||||
#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
|
#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of_gpio.h>
|
#include <linux/of_gpio.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
@@ -31,19 +32,29 @@
|
|||||||
|
|
||||||
#define to_imx6_pcie(x) container_of(x, struct imx6_pcie, pp)
|
#define to_imx6_pcie(x) container_of(x, struct imx6_pcie, pp)
|
||||||
|
|
||||||
|
enum imx6_pcie_variants {
|
||||||
|
IMX6Q,
|
||||||
|
IMX6SX,
|
||||||
|
IMX6QP,
|
||||||
|
};
|
||||||
|
|
||||||
struct imx6_pcie {
|
struct imx6_pcie {
|
||||||
int reset_gpio;
|
int reset_gpio;
|
||||||
|
bool gpio_active_high;
|
||||||
struct clk *pcie_bus;
|
struct clk *pcie_bus;
|
||||||
struct clk *pcie_phy;
|
struct clk *pcie_phy;
|
||||||
|
struct clk *pcie_inbound_axi;
|
||||||
struct clk *pcie;
|
struct clk *pcie;
|
||||||
struct pcie_port pp;
|
struct pcie_port pp;
|
||||||
struct regmap *iomuxc_gpr;
|
struct regmap *iomuxc_gpr;
|
||||||
|
enum imx6_pcie_variants variant;
|
||||||
void __iomem *mem_base;
|
void __iomem *mem_base;
|
||||||
u32 tx_deemph_gen1;
|
u32 tx_deemph_gen1;
|
||||||
u32 tx_deemph_gen2_3p5db;
|
u32 tx_deemph_gen2_3p5db;
|
||||||
u32 tx_deemph_gen2_6db;
|
u32 tx_deemph_gen2_6db;
|
||||||
u32 tx_swing_full;
|
u32 tx_swing_full;
|
||||||
u32 tx_swing_low;
|
u32 tx_swing_low;
|
||||||
|
int link_gen;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* PCIe Root Complex registers (memory-mapped) */
|
/* PCIe Root Complex registers (memory-mapped) */
|
||||||
@@ -236,16 +247,34 @@ static int imx6_pcie_assert_core_reset(struct pcie_port *pp)
|
|||||||
struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
|
struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
|
||||||
u32 val, gpr1, gpr12;
|
u32 val, gpr1, gpr12;
|
||||||
|
|
||||||
|
switch (imx6_pcie->variant) {
|
||||||
|
case IMX6SX:
|
||||||
|
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
|
||||||
|
IMX6SX_GPR12_PCIE_TEST_POWERDOWN,
|
||||||
|
IMX6SX_GPR12_PCIE_TEST_POWERDOWN);
|
||||||
|
/* Force PCIe PHY reset */
|
||||||
|
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR5,
|
||||||
|
IMX6SX_GPR5_PCIE_BTNRST_RESET,
|
||||||
|
IMX6SX_GPR5_PCIE_BTNRST_RESET);
|
||||||
|
break;
|
||||||
|
case IMX6QP:
|
||||||
|
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
|
||||||
|
IMX6Q_GPR1_PCIE_SW_RST,
|
||||||
|
IMX6Q_GPR1_PCIE_SW_RST);
|
||||||
|
break;
|
||||||
|
case IMX6Q:
|
||||||
/*
|
/*
|
||||||
* If the bootloader already enabled the link we need some special
|
* If the bootloader already enabled the link we need some
|
||||||
* handling to get the core back into a state where it is safe to
|
* special handling to get the core back into a state where
|
||||||
* touch it for configuration. As there is no dedicated reset signal
|
* it is safe to touch it for configuration. As there is
|
||||||
* wired up for MX6QDL, we need to manually force LTSSM into "detect"
|
* no dedicated reset signal wired up for MX6QDL, we need
|
||||||
* state before completely disabling LTSSM, which is a prerequisite
|
* to manually force LTSSM into "detect" state before
|
||||||
* for core configuration.
|
* completely disabling LTSSM, which is a prerequisite for
|
||||||
|
* core configuration.
|
||||||
*
|
*
|
||||||
* If both LTSSM_ENABLE and REF_SSP_ENABLE are active we have a strong
|
* If both LTSSM_ENABLE and REF_SSP_ENABLE are active we
|
||||||
* indication that the bootloader activated the link.
|
* have a strong indication that the bootloader activated
|
||||||
|
* the link.
|
||||||
*/
|
*/
|
||||||
regmap_read(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, &gpr1);
|
regmap_read(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1, &gpr1);
|
||||||
regmap_read(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, &gpr12);
|
regmap_read(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12, &gpr12);
|
||||||
@@ -265,10 +294,48 @@ static int imx6_pcie_assert_core_reset(struct pcie_port *pp)
|
|||||||
IMX6Q_GPR1_PCIE_TEST_PD, 1 << 18);
|
IMX6Q_GPR1_PCIE_TEST_PD, 1 << 18);
|
||||||
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
|
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
|
||||||
IMX6Q_GPR1_PCIE_REF_CLK_EN, 0 << 16);
|
IMX6Q_GPR1_PCIE_REF_CLK_EN, 0 << 16);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie)
|
||||||
|
{
|
||||||
|
struct pcie_port *pp = &imx6_pcie->pp;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
switch (imx6_pcie->variant) {
|
||||||
|
case IMX6SX:
|
||||||
|
ret = clk_prepare_enable(imx6_pcie->pcie_inbound_axi);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(pp->dev, "unable to enable pcie_axi clock\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
|
||||||
|
IMX6SX_GPR12_PCIE_TEST_POWERDOWN, 0);
|
||||||
|
break;
|
||||||
|
case IMX6QP: /* FALLTHROUGH */
|
||||||
|
case IMX6Q:
|
||||||
|
/* power up core phy and enable ref clock */
|
||||||
|
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
|
||||||
|
IMX6Q_GPR1_PCIE_TEST_PD, 0 << 18);
|
||||||
|
/*
|
||||||
|
* the async reset input need ref clock to sync internally,
|
||||||
|
* when the ref clock comes after reset, internal synced
|
||||||
|
* reset time is too short, cannot meet the requirement.
|
||||||
|
* add one ~10us delay here.
|
||||||
|
*/
|
||||||
|
udelay(10);
|
||||||
|
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
|
||||||
|
IMX6Q_GPR1_PCIE_REF_CLK_EN, 1 << 16);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int imx6_pcie_deassert_core_reset(struct pcie_port *pp)
|
static int imx6_pcie_deassert_core_reset(struct pcie_port *pp)
|
||||||
{
|
{
|
||||||
struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
|
struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
|
||||||
@@ -292,43 +359,60 @@ static int imx6_pcie_deassert_core_reset(struct pcie_port *pp)
|
|||||||
goto err_pcie;
|
goto err_pcie;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* power up core phy and enable ref clock */
|
ret = imx6_pcie_enable_ref_clk(imx6_pcie);
|
||||||
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
|
if (ret) {
|
||||||
IMX6Q_GPR1_PCIE_TEST_PD, 0 << 18);
|
dev_err(pp->dev, "unable to enable pcie ref clock\n");
|
||||||
/*
|
goto err_ref_clk;
|
||||||
* the async reset input need ref clock to sync internally,
|
}
|
||||||
* when the ref clock comes after reset, internal synced
|
|
||||||
* reset time is too short, cannot meet the requirement.
|
|
||||||
* add one ~10us delay here.
|
|
||||||
*/
|
|
||||||
udelay(10);
|
|
||||||
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
|
|
||||||
IMX6Q_GPR1_PCIE_REF_CLK_EN, 1 << 16);
|
|
||||||
|
|
||||||
/* allow the clocks to stabilize */
|
/* allow the clocks to stabilize */
|
||||||
usleep_range(200, 500);
|
usleep_range(200, 500);
|
||||||
|
|
||||||
/* Some boards don't have PCIe reset GPIO. */
|
/* Some boards don't have PCIe reset GPIO. */
|
||||||
if (gpio_is_valid(imx6_pcie->reset_gpio)) {
|
if (gpio_is_valid(imx6_pcie->reset_gpio)) {
|
||||||
gpio_set_value_cansleep(imx6_pcie->reset_gpio, 0);
|
gpio_set_value_cansleep(imx6_pcie->reset_gpio,
|
||||||
|
imx6_pcie->gpio_active_high);
|
||||||
msleep(100);
|
msleep(100);
|
||||||
gpio_set_value_cansleep(imx6_pcie->reset_gpio, 1);
|
gpio_set_value_cansleep(imx6_pcie->reset_gpio,
|
||||||
|
!imx6_pcie->gpio_active_high);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (imx6_pcie->variant) {
|
||||||
|
case IMX6SX:
|
||||||
|
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR5,
|
||||||
|
IMX6SX_GPR5_PCIE_BTNRST_RESET, 0);
|
||||||
|
break;
|
||||||
|
case IMX6QP:
|
||||||
|
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
|
||||||
|
IMX6Q_GPR1_PCIE_SW_RST, 0);
|
||||||
|
|
||||||
|
usleep_range(200, 500);
|
||||||
|
break;
|
||||||
|
case IMX6Q: /* Nothing to do */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
err_ref_clk:
|
||||||
|
clk_disable_unprepare(imx6_pcie->pcie);
|
||||||
err_pcie:
|
err_pcie:
|
||||||
clk_disable_unprepare(imx6_pcie->pcie_bus);
|
clk_disable_unprepare(imx6_pcie->pcie_bus);
|
||||||
err_pcie_bus:
|
err_pcie_bus:
|
||||||
clk_disable_unprepare(imx6_pcie->pcie_phy);
|
clk_disable_unprepare(imx6_pcie->pcie_phy);
|
||||||
err_pcie_phy:
|
err_pcie_phy:
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void imx6_pcie_init_phy(struct pcie_port *pp)
|
static void imx6_pcie_init_phy(struct pcie_port *pp)
|
||||||
{
|
{
|
||||||
struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
|
struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
|
||||||
|
|
||||||
|
if (imx6_pcie->variant == IMX6SX)
|
||||||
|
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
|
||||||
|
IMX6SX_GPR12_PCIE_RX_EQ_MASK,
|
||||||
|
IMX6SX_GPR12_PCIE_RX_EQ_2);
|
||||||
|
|
||||||
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
|
regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
|
||||||
IMX6Q_GPR12_PCIE_CTL_2, 0 << 10);
|
IMX6Q_GPR12_PCIE_CTL_2, 0 << 10);
|
||||||
|
|
||||||
@@ -417,11 +501,15 @@ static int imx6_pcie_establish_link(struct pcie_port *pp)
|
|||||||
goto err_reset_phy;
|
goto err_reset_phy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (imx6_pcie->link_gen == 2) {
|
||||||
/* Allow Gen2 mode after the link is up. */
|
/* Allow Gen2 mode after the link is up. */
|
||||||
tmp = readl(pp->dbi_base + PCIE_RC_LCR);
|
tmp = readl(pp->dbi_base + PCIE_RC_LCR);
|
||||||
tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK;
|
tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK;
|
||||||
tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2;
|
tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2;
|
||||||
writel(tmp, pp->dbi_base + PCIE_RC_LCR);
|
writel(tmp, pp->dbi_base + PCIE_RC_LCR);
|
||||||
|
} else {
|
||||||
|
dev_info(pp->dev, "Link: Gen2 disabled\n");
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Start Directed Speed Change so the best possible speed both link
|
* Start Directed Speed Change so the best possible speed both link
|
||||||
@@ -445,8 +533,7 @@ static int imx6_pcie_establish_link(struct pcie_port *pp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
tmp = readl(pp->dbi_base + PCIE_RC_LCSR);
|
tmp = readl(pp->dbi_base + PCIE_RC_LCSR);
|
||||||
dev_dbg(pp->dev, "Link up, Gen=%i\n", (tmp >> 16) & 0xf);
|
dev_info(pp->dev, "Link up, Gen%i\n", (tmp >> 16) & 0xf);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_reset_phy:
|
err_reset_phy:
|
||||||
@@ -535,6 +622,9 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
|
|||||||
pp = &imx6_pcie->pp;
|
pp = &imx6_pcie->pp;
|
||||||
pp->dev = &pdev->dev;
|
pp->dev = &pdev->dev;
|
||||||
|
|
||||||
|
imx6_pcie->variant =
|
||||||
|
(enum imx6_pcie_variants)of_device_get_match_data(&pdev->dev);
|
||||||
|
|
||||||
/* Added for PCI abort handling */
|
/* Added for PCI abort handling */
|
||||||
hook_fault_code(16 + 6, imx6q_pcie_abort_handler, SIGBUS, 0,
|
hook_fault_code(16 + 6, imx6q_pcie_abort_handler, SIGBUS, 0,
|
||||||
"imprecise external abort");
|
"imprecise external abort");
|
||||||
@@ -546,9 +636,14 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
/* Fetch GPIOs */
|
/* Fetch GPIOs */
|
||||||
imx6_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
|
imx6_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
|
||||||
|
imx6_pcie->gpio_active_high = of_property_read_bool(np,
|
||||||
|
"reset-gpio-active-high");
|
||||||
if (gpio_is_valid(imx6_pcie->reset_gpio)) {
|
if (gpio_is_valid(imx6_pcie->reset_gpio)) {
|
||||||
ret = devm_gpio_request_one(&pdev->dev, imx6_pcie->reset_gpio,
|
ret = devm_gpio_request_one(&pdev->dev, imx6_pcie->reset_gpio,
|
||||||
GPIOF_OUT_INIT_LOW, "PCIe reset");
|
imx6_pcie->gpio_active_high ?
|
||||||
|
GPIOF_OUT_INIT_HIGH :
|
||||||
|
GPIOF_OUT_INIT_LOW,
|
||||||
|
"PCIe reset");
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "unable to get reset gpio\n");
|
dev_err(&pdev->dev, "unable to get reset gpio\n");
|
||||||
return ret;
|
return ret;
|
||||||
@@ -577,6 +672,16 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
|
|||||||
return PTR_ERR(imx6_pcie->pcie);
|
return PTR_ERR(imx6_pcie->pcie);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (imx6_pcie->variant == IMX6SX) {
|
||||||
|
imx6_pcie->pcie_inbound_axi = devm_clk_get(&pdev->dev,
|
||||||
|
"pcie_inbound_axi");
|
||||||
|
if (IS_ERR(imx6_pcie->pcie_inbound_axi)) {
|
||||||
|
dev_err(&pdev->dev,
|
||||||
|
"pcie_incbound_axi clock missing or invalid\n");
|
||||||
|
return PTR_ERR(imx6_pcie->pcie_inbound_axi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Grab GPR config register range */
|
/* Grab GPR config register range */
|
||||||
imx6_pcie->iomuxc_gpr =
|
imx6_pcie->iomuxc_gpr =
|
||||||
syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
|
syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
|
||||||
@@ -606,6 +711,12 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
|
|||||||
&imx6_pcie->tx_swing_low))
|
&imx6_pcie->tx_swing_low))
|
||||||
imx6_pcie->tx_swing_low = 127;
|
imx6_pcie->tx_swing_low = 127;
|
||||||
|
|
||||||
|
/* Limit link speed */
|
||||||
|
ret = of_property_read_u32(pp->dev->of_node, "fsl,max-link-speed",
|
||||||
|
&imx6_pcie->link_gen);
|
||||||
|
if (ret)
|
||||||
|
imx6_pcie->link_gen = 1;
|
||||||
|
|
||||||
ret = imx6_add_pcie_port(pp, pdev);
|
ret = imx6_add_pcie_port(pp, pdev);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
@@ -623,7 +734,9 @@ static void imx6_pcie_shutdown(struct platform_device *pdev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static const struct of_device_id imx6_pcie_of_match[] = {
|
static const struct of_device_id imx6_pcie_of_match[] = {
|
||||||
{ .compatible = "fsl,imx6q-pcie", },
|
{ .compatible = "fsl,imx6q-pcie", .data = (void *)IMX6Q, },
|
||||||
|
{ .compatible = "fsl,imx6sx-pcie", .data = (void *)IMX6SX, },
|
||||||
|
{ .compatible = "fsl,imx6qp-pcie", .data = (void *)IMX6QP, },
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, imx6_pcie_of_match);
|
MODULE_DEVICE_TABLE(of, imx6_pcie_of_match);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
#include <linux/irqdomain.h>
|
#include <linux/irqdomain.h>
|
||||||
|
#include <linux/irqreturn.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/of_pci.h>
|
#include <linux/of_pci.h>
|
||||||
@@ -53,6 +54,21 @@
|
|||||||
#define IRQ_STATUS 0x184
|
#define IRQ_STATUS 0x184
|
||||||
#define MSI_IRQ_OFFSET 4
|
#define MSI_IRQ_OFFSET 4
|
||||||
|
|
||||||
|
/* Error IRQ bits */
|
||||||
|
#define ERR_AER BIT(5) /* ECRC error */
|
||||||
|
#define ERR_AXI BIT(4) /* AXI tag lookup fatal error */
|
||||||
|
#define ERR_CORR BIT(3) /* Correctable error */
|
||||||
|
#define ERR_NONFATAL BIT(2) /* Non-fatal error */
|
||||||
|
#define ERR_FATAL BIT(1) /* Fatal error */
|
||||||
|
#define ERR_SYS BIT(0) /* System (fatal, non-fatal, or correctable) */
|
||||||
|
#define ERR_IRQ_ALL (ERR_AER | ERR_AXI | ERR_CORR | \
|
||||||
|
ERR_NONFATAL | ERR_FATAL | ERR_SYS)
|
||||||
|
#define ERR_FATAL_IRQ (ERR_FATAL | ERR_AXI)
|
||||||
|
#define ERR_IRQ_STATUS_RAW 0x1c0
|
||||||
|
#define ERR_IRQ_STATUS 0x1c4
|
||||||
|
#define ERR_IRQ_ENABLE_SET 0x1c8
|
||||||
|
#define ERR_IRQ_ENABLE_CLR 0x1cc
|
||||||
|
|
||||||
/* Config space registers */
|
/* Config space registers */
|
||||||
#define DEBUG0 0x728
|
#define DEBUG0 0x728
|
||||||
|
|
||||||
@@ -243,6 +259,28 @@ void ks_dw_pcie_handle_legacy_irq(struct keystone_pcie *ks_pcie, int offset)
|
|||||||
writel(offset, ks_pcie->va_app_base + IRQ_EOI);
|
writel(offset, ks_pcie->va_app_base + IRQ_EOI);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ks_dw_pcie_enable_error_irq(void __iomem *reg_base)
|
||||||
|
{
|
||||||
|
writel(ERR_IRQ_ALL, reg_base + ERR_IRQ_ENABLE_SET);
|
||||||
|
}
|
||||||
|
|
||||||
|
irqreturn_t ks_dw_pcie_handle_error_irq(struct device *dev,
|
||||||
|
void __iomem *reg_base)
|
||||||
|
{
|
||||||
|
u32 status;
|
||||||
|
|
||||||
|
status = readl(reg_base + ERR_IRQ_STATUS_RAW) & ERR_IRQ_ALL;
|
||||||
|
if (!status)
|
||||||
|
return IRQ_NONE;
|
||||||
|
|
||||||
|
if (status & ERR_FATAL_IRQ)
|
||||||
|
dev_err(dev, "fatal error (status %#010x)\n", status);
|
||||||
|
|
||||||
|
/* Ack the IRQ; status bits are RW1C */
|
||||||
|
writel(status, reg_base + ERR_IRQ_STATUS);
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
static void ks_dw_pcie_ack_legacy_irq(struct irq_data *d)
|
static void ks_dw_pcie_ack_legacy_irq(struct irq_data *d)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
#include <linux/irqchip/chained_irq.h>
|
#include <linux/irqchip/chained_irq.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
#include <linux/irqdomain.h>
|
#include <linux/irqdomain.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/msi.h>
|
#include <linux/msi.h>
|
||||||
@@ -159,7 +160,7 @@ static void ks_pcie_legacy_irq_handler(struct irq_desc *desc)
|
|||||||
static int ks_pcie_get_irq_controller_info(struct keystone_pcie *ks_pcie,
|
static int ks_pcie_get_irq_controller_info(struct keystone_pcie *ks_pcie,
|
||||||
char *controller, int *num_irqs)
|
char *controller, int *num_irqs)
|
||||||
{
|
{
|
||||||
int temp, max_host_irqs, legacy = 1, *host_irqs, ret = -EINVAL;
|
int temp, max_host_irqs, legacy = 1, *host_irqs;
|
||||||
struct device *dev = ks_pcie->pp.dev;
|
struct device *dev = ks_pcie->pp.dev;
|
||||||
struct device_node *np_pcie = dev->of_node, **np_temp;
|
struct device_node *np_pcie = dev->of_node, **np_temp;
|
||||||
|
|
||||||
@@ -180,11 +181,15 @@ static int ks_pcie_get_irq_controller_info(struct keystone_pcie *ks_pcie,
|
|||||||
*np_temp = of_find_node_by_name(np_pcie, controller);
|
*np_temp = of_find_node_by_name(np_pcie, controller);
|
||||||
if (!(*np_temp)) {
|
if (!(*np_temp)) {
|
||||||
dev_err(dev, "Node for %s is absent\n", controller);
|
dev_err(dev, "Node for %s is absent\n", controller);
|
||||||
goto out;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
temp = of_irq_count(*np_temp);
|
temp = of_irq_count(*np_temp);
|
||||||
if (!temp)
|
if (!temp) {
|
||||||
goto out;
|
dev_err(dev, "No IRQ entries in %s\n", controller);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
if (temp > max_host_irqs)
|
if (temp > max_host_irqs)
|
||||||
dev_warn(dev, "Too many %s interrupts defined %u\n",
|
dev_warn(dev, "Too many %s interrupts defined %u\n",
|
||||||
(legacy ? "legacy" : "MSI"), temp);
|
(legacy ? "legacy" : "MSI"), temp);
|
||||||
@@ -198,12 +203,13 @@ static int ks_pcie_get_irq_controller_info(struct keystone_pcie *ks_pcie,
|
|||||||
if (!host_irqs[temp])
|
if (!host_irqs[temp])
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (temp) {
|
if (temp) {
|
||||||
*num_irqs = temp;
|
*num_irqs = temp;
|
||||||
ret = 0;
|
return 0;
|
||||||
}
|
}
|
||||||
out:
|
|
||||||
return ret;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ks_pcie_setup_interrupts(struct keystone_pcie *ks_pcie)
|
static void ks_pcie_setup_interrupts(struct keystone_pcie *ks_pcie)
|
||||||
@@ -226,6 +232,9 @@ static void ks_pcie_setup_interrupts(struct keystone_pcie *ks_pcie)
|
|||||||
ks_pcie);
|
ks_pcie);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ks_pcie->error_irq > 0)
|
||||||
|
ks_dw_pcie_enable_error_irq(ks_pcie->va_app_base);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -289,6 +298,14 @@ static struct pcie_host_ops keystone_pcie_host_ops = {
|
|||||||
.scan_bus = ks_dw_pcie_v3_65_scan_bus,
|
.scan_bus = ks_dw_pcie_v3_65_scan_bus,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static irqreturn_t pcie_err_irq_handler(int irq, void *priv)
|
||||||
|
{
|
||||||
|
struct keystone_pcie *ks_pcie = priv;
|
||||||
|
|
||||||
|
return ks_dw_pcie_handle_error_irq(ks_pcie->pp.dev,
|
||||||
|
ks_pcie->va_app_base);
|
||||||
|
}
|
||||||
|
|
||||||
static int __init ks_add_pcie_port(struct keystone_pcie *ks_pcie,
|
static int __init ks_add_pcie_port(struct keystone_pcie *ks_pcie,
|
||||||
struct platform_device *pdev)
|
struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
@@ -309,6 +326,22 @@ static int __init ks_add_pcie_port(struct keystone_pcie *ks_pcie,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Index 0 is the platform interrupt for error interrupt
|
||||||
|
* from RC. This is optional.
|
||||||
|
*/
|
||||||
|
ks_pcie->error_irq = irq_of_parse_and_map(ks_pcie->np, 0);
|
||||||
|
if (ks_pcie->error_irq <= 0)
|
||||||
|
dev_info(&pdev->dev, "no error IRQ defined\n");
|
||||||
|
else {
|
||||||
|
if (request_irq(ks_pcie->error_irq, pcie_err_irq_handler,
|
||||||
|
IRQF_SHARED, "pcie-error-irq", ks_pcie) < 0) {
|
||||||
|
dev_err(&pdev->dev, "failed to request error IRQ %d\n",
|
||||||
|
ks_pcie->error_irq);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pp->root_bus_nr = -1;
|
pp->root_bus_nr = -1;
|
||||||
pp->ops = &keystone_pcie_host_ops;
|
pp->ops = &keystone_pcie_host_ops;
|
||||||
ret = ks_dw_pcie_host_init(ks_pcie, ks_pcie->msi_intc_np);
|
ret = ks_dw_pcie_host_init(ks_pcie, ks_pcie->msi_intc_np);
|
||||||
@@ -317,7 +350,7 @@ static int __init ks_add_pcie_port(struct keystone_pcie *ks_pcie,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct of_device_id ks_pcie_of_match[] = {
|
static const struct of_device_id ks_pcie_of_match[] = {
|
||||||
@@ -346,7 +379,7 @@ static int __init ks_pcie_probe(struct platform_device *pdev)
|
|||||||
struct resource *res;
|
struct resource *res;
|
||||||
void __iomem *reg_p;
|
void __iomem *reg_p;
|
||||||
struct phy *phy;
|
struct phy *phy;
|
||||||
int ret = 0;
|
int ret;
|
||||||
|
|
||||||
ks_pcie = devm_kzalloc(&pdev->dev, sizeof(*ks_pcie),
|
ks_pcie = devm_kzalloc(&pdev->dev, sizeof(*ks_pcie),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
@@ -376,6 +409,7 @@ static int __init ks_pcie_probe(struct platform_device *pdev)
|
|||||||
devm_release_mem_region(dev, res->start, resource_size(res));
|
devm_release_mem_region(dev, res->start, resource_size(res));
|
||||||
|
|
||||||
pp->dev = dev;
|
pp->dev = dev;
|
||||||
|
ks_pcie->np = dev->of_node;
|
||||||
platform_set_drvdata(pdev, ks_pcie);
|
platform_set_drvdata(pdev, ks_pcie);
|
||||||
ks_pcie->clk = devm_clk_get(dev, "pcie");
|
ks_pcie->clk = devm_clk_get(dev, "pcie");
|
||||||
if (IS_ERR(ks_pcie->clk)) {
|
if (IS_ERR(ks_pcie->clk)) {
|
||||||
|
|||||||
@@ -29,6 +29,9 @@ struct keystone_pcie {
|
|||||||
int msi_host_irqs[MAX_MSI_HOST_IRQS];
|
int msi_host_irqs[MAX_MSI_HOST_IRQS];
|
||||||
struct device_node *msi_intc_np;
|
struct device_node *msi_intc_np;
|
||||||
struct irq_domain *legacy_irq_domain;
|
struct irq_domain *legacy_irq_domain;
|
||||||
|
struct device_node *np;
|
||||||
|
|
||||||
|
int error_irq;
|
||||||
|
|
||||||
/* Application register space */
|
/* Application register space */
|
||||||
void __iomem *va_app_base;
|
void __iomem *va_app_base;
|
||||||
@@ -42,6 +45,9 @@ phys_addr_t ks_dw_pcie_get_msi_addr(struct pcie_port *pp);
|
|||||||
/* Keystone specific PCI controller APIs */
|
/* Keystone specific PCI controller APIs */
|
||||||
void ks_dw_pcie_enable_legacy_irqs(struct keystone_pcie *ks_pcie);
|
void ks_dw_pcie_enable_legacy_irqs(struct keystone_pcie *ks_pcie);
|
||||||
void ks_dw_pcie_handle_legacy_irq(struct keystone_pcie *ks_pcie, int offset);
|
void ks_dw_pcie_handle_legacy_irq(struct keystone_pcie *ks_pcie, int offset);
|
||||||
|
void ks_dw_pcie_enable_error_irq(void __iomem *reg_base);
|
||||||
|
irqreturn_t ks_dw_pcie_handle_error_irq(struct device *dev,
|
||||||
|
void __iomem *reg_base);
|
||||||
int ks_dw_pcie_host_init(struct keystone_pcie *ks_pcie,
|
int ks_dw_pcie_host_init(struct keystone_pcie *ks_pcie,
|
||||||
struct device_node *msi_intc_np);
|
struct device_node *msi_intc_np);
|
||||||
int ks_dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
|
int ks_dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
|
||||||
|
|||||||
@@ -1003,6 +1003,7 @@ static void mvebu_pcie_msi_enable(struct mvebu_pcie *pcie)
|
|||||||
pcie->msi->dev = &pcie->pdev->dev;
|
pcie->msi->dev = &pcie->pdev->dev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_SLEEP
|
||||||
static int mvebu_pcie_suspend(struct device *dev)
|
static int mvebu_pcie_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct mvebu_pcie *pcie;
|
struct mvebu_pcie *pcie;
|
||||||
@@ -1031,6 +1032,7 @@ static int mvebu_pcie_resume(struct device *dev)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static void mvebu_pcie_port_clk_put(void *data)
|
static void mvebu_pcie_port_clk_put(void *data)
|
||||||
{
|
{
|
||||||
@@ -1298,9 +1300,8 @@ static const struct of_device_id mvebu_pcie_of_match_table[] = {
|
|||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, mvebu_pcie_of_match_table);
|
MODULE_DEVICE_TABLE(of, mvebu_pcie_of_match_table);
|
||||||
|
|
||||||
static struct dev_pm_ops mvebu_pcie_pm_ops = {
|
static const struct dev_pm_ops mvebu_pcie_pm_ops = {
|
||||||
.suspend_noirq = mvebu_pcie_suspend,
|
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(mvebu_pcie_suspend, mvebu_pcie_resume)
|
||||||
.resume_noirq = mvebu_pcie_resume,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct platform_driver mvebu_pcie_driver = {
|
static struct platform_driver mvebu_pcie_driver = {
|
||||||
|
|||||||
@@ -13,18 +13,7 @@
|
|||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
|
||||||
#include "pci-host-common.h"
|
#include "../ecam.h"
|
||||||
|
|
||||||
/* Mapping is standard ECAM */
|
|
||||||
static void __iomem *thunder_ecam_map_bus(struct pci_bus *bus,
|
|
||||||
unsigned int devfn,
|
|
||||||
int where)
|
|
||||||
{
|
|
||||||
struct gen_pci *pci = bus->sysdata;
|
|
||||||
resource_size_t idx = bus->number - pci->cfg.bus_range->start;
|
|
||||||
|
|
||||||
return pci->cfg.win[idx] + ((devfn << 12) | where);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void set_val(u32 v, int where, int size, u32 *val)
|
static void set_val(u32 v, int where, int size, u32 *val)
|
||||||
{
|
{
|
||||||
@@ -99,7 +88,7 @@ static int handle_ea_bar(u32 e0, int bar, struct pci_bus *bus,
|
|||||||
static int thunder_ecam_p2_config_read(struct pci_bus *bus, unsigned int devfn,
|
static int thunder_ecam_p2_config_read(struct pci_bus *bus, unsigned int devfn,
|
||||||
int where, int size, u32 *val)
|
int where, int size, u32 *val)
|
||||||
{
|
{
|
||||||
struct gen_pci *pci = bus->sysdata;
|
struct pci_config_window *cfg = bus->sysdata;
|
||||||
int where_a = where & ~3;
|
int where_a = where & ~3;
|
||||||
void __iomem *addr;
|
void __iomem *addr;
|
||||||
u32 node_bits;
|
u32 node_bits;
|
||||||
@@ -129,7 +118,7 @@ static int thunder_ecam_p2_config_read(struct pci_bus *bus, unsigned int devfn,
|
|||||||
* the config space access window. Since we are working with
|
* the config space access window. Since we are working with
|
||||||
* the high-order 32 bits, shift everything down by 32 bits.
|
* the high-order 32 bits, shift everything down by 32 bits.
|
||||||
*/
|
*/
|
||||||
node_bits = (pci->cfg.res.start >> 32) & (1 << 12);
|
node_bits = (cfg->res.start >> 32) & (1 << 12);
|
||||||
|
|
||||||
v |= node_bits;
|
v |= node_bits;
|
||||||
set_val(v, where, size, val);
|
set_val(v, where, size, val);
|
||||||
@@ -358,36 +347,24 @@ static int thunder_ecam_config_write(struct pci_bus *bus, unsigned int devfn,
|
|||||||
return pci_generic_config_write(bus, devfn, where, size, val);
|
return pci_generic_config_write(bus, devfn, where, size, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct gen_pci_cfg_bus_ops thunder_ecam_bus_ops = {
|
static struct pci_ecam_ops pci_thunder_ecam_ops = {
|
||||||
.bus_shift = 20,
|
.bus_shift = 20,
|
||||||
.ops = {
|
.pci_ops = {
|
||||||
.map_bus = thunder_ecam_map_bus,
|
.map_bus = pci_ecam_map_bus,
|
||||||
.read = thunder_ecam_config_read,
|
.read = thunder_ecam_config_read,
|
||||||
.write = thunder_ecam_config_write,
|
.write = thunder_ecam_config_write,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct of_device_id thunder_ecam_of_match[] = {
|
static const struct of_device_id thunder_ecam_of_match[] = {
|
||||||
{ .compatible = "cavium,pci-host-thunder-ecam",
|
{ .compatible = "cavium,pci-host-thunder-ecam" },
|
||||||
.data = &thunder_ecam_bus_ops },
|
|
||||||
|
|
||||||
{ },
|
{ },
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, thunder_ecam_of_match);
|
MODULE_DEVICE_TABLE(of, thunder_ecam_of_match);
|
||||||
|
|
||||||
static int thunder_ecam_probe(struct platform_device *pdev)
|
static int thunder_ecam_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device *dev = &pdev->dev;
|
return pci_host_common_probe(pdev, &pci_thunder_ecam_ops);
|
||||||
const struct of_device_id *of_id;
|
|
||||||
struct gen_pci *pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
|
|
||||||
|
|
||||||
if (!pci)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
of_id = of_match_node(thunder_ecam_of_match, dev->of_node);
|
|
||||||
pci->cfg.ops = (struct gen_pci_cfg_bus_ops *)of_id->data;
|
|
||||||
|
|
||||||
return pci_host_common_probe(pdev, pci);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct platform_driver thunder_ecam_driver = {
|
static struct platform_driver thunder_ecam_driver = {
|
||||||
|
|||||||
@@ -20,34 +20,22 @@
|
|||||||
#include <linux/of_pci.h>
|
#include <linux/of_pci.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
|
||||||
#include "pci-host-common.h"
|
#include "../ecam.h"
|
||||||
|
|
||||||
#define PEM_CFG_WR 0x28
|
#define PEM_CFG_WR 0x28
|
||||||
#define PEM_CFG_RD 0x30
|
#define PEM_CFG_RD 0x30
|
||||||
|
|
||||||
struct thunder_pem_pci {
|
struct thunder_pem_pci {
|
||||||
struct gen_pci gen_pci;
|
|
||||||
u32 ea_entry[3];
|
u32 ea_entry[3];
|
||||||
void __iomem *pem_reg_base;
|
void __iomem *pem_reg_base;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void __iomem *thunder_pem_map_bus(struct pci_bus *bus,
|
|
||||||
unsigned int devfn, int where)
|
|
||||||
{
|
|
||||||
struct gen_pci *pci = bus->sysdata;
|
|
||||||
resource_size_t idx = bus->number - pci->cfg.bus_range->start;
|
|
||||||
|
|
||||||
return pci->cfg.win[idx] + ((devfn << 16) | where);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int thunder_pem_bridge_read(struct pci_bus *bus, unsigned int devfn,
|
static int thunder_pem_bridge_read(struct pci_bus *bus, unsigned int devfn,
|
||||||
int where, int size, u32 *val)
|
int where, int size, u32 *val)
|
||||||
{
|
{
|
||||||
u64 read_val;
|
u64 read_val;
|
||||||
struct thunder_pem_pci *pem_pci;
|
struct pci_config_window *cfg = bus->sysdata;
|
||||||
struct gen_pci *pci = bus->sysdata;
|
struct thunder_pem_pci *pem_pci = (struct thunder_pem_pci *)cfg->priv;
|
||||||
|
|
||||||
pem_pci = container_of(pci, struct thunder_pem_pci, gen_pci);
|
|
||||||
|
|
||||||
if (devfn != 0 || where >= 2048) {
|
if (devfn != 0 || where >= 2048) {
|
||||||
*val = ~0;
|
*val = ~0;
|
||||||
@@ -132,17 +120,17 @@ static int thunder_pem_bridge_read(struct pci_bus *bus, unsigned int devfn,
|
|||||||
static int thunder_pem_config_read(struct pci_bus *bus, unsigned int devfn,
|
static int thunder_pem_config_read(struct pci_bus *bus, unsigned int devfn,
|
||||||
int where, int size, u32 *val)
|
int where, int size, u32 *val)
|
||||||
{
|
{
|
||||||
struct gen_pci *pci = bus->sysdata;
|
struct pci_config_window *cfg = bus->sysdata;
|
||||||
|
|
||||||
if (bus->number < pci->cfg.bus_range->start ||
|
if (bus->number < cfg->busr.start ||
|
||||||
bus->number > pci->cfg.bus_range->end)
|
bus->number > cfg->busr.end)
|
||||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The first device on the bus is the PEM PCIe bridge.
|
* The first device on the bus is the PEM PCIe bridge.
|
||||||
* Special case its config access.
|
* Special case its config access.
|
||||||
*/
|
*/
|
||||||
if (bus->number == pci->cfg.bus_range->start)
|
if (bus->number == cfg->busr.start)
|
||||||
return thunder_pem_bridge_read(bus, devfn, where, size, val);
|
return thunder_pem_bridge_read(bus, devfn, where, size, val);
|
||||||
|
|
||||||
return pci_generic_config_read(bus, devfn, where, size, val);
|
return pci_generic_config_read(bus, devfn, where, size, val);
|
||||||
@@ -153,11 +141,11 @@ static int thunder_pem_config_read(struct pci_bus *bus, unsigned int devfn,
|
|||||||
* reserved bits, this makes the code simpler and is OK as the bits
|
* reserved bits, this makes the code simpler and is OK as the bits
|
||||||
* are not affected by writing zeros to them.
|
* are not affected by writing zeros to them.
|
||||||
*/
|
*/
|
||||||
static u32 thunder_pem_bridge_w1c_bits(int where)
|
static u32 thunder_pem_bridge_w1c_bits(u64 where_aligned)
|
||||||
{
|
{
|
||||||
u32 w1c_bits = 0;
|
u32 w1c_bits = 0;
|
||||||
|
|
||||||
switch (where & ~3) {
|
switch (where_aligned) {
|
||||||
case 0x04: /* Command/Status */
|
case 0x04: /* Command/Status */
|
||||||
case 0x1c: /* Base and I/O Limit/Secondary Status */
|
case 0x1c: /* Base and I/O Limit/Secondary Status */
|
||||||
w1c_bits = 0xff000000;
|
w1c_bits = 0xff000000;
|
||||||
@@ -184,15 +172,36 @@ static u32 thunder_pem_bridge_w1c_bits(int where)
|
|||||||
return w1c_bits;
|
return w1c_bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Some bits must be written to one so they appear to be read-only. */
|
||||||
|
static u32 thunder_pem_bridge_w1_bits(u64 where_aligned)
|
||||||
|
{
|
||||||
|
u32 w1_bits;
|
||||||
|
|
||||||
|
switch (where_aligned) {
|
||||||
|
case 0x1c: /* I/O Base / I/O Limit, Secondary Status */
|
||||||
|
/* Force 32-bit I/O addressing. */
|
||||||
|
w1_bits = 0x0101;
|
||||||
|
break;
|
||||||
|
case 0x24: /* Prefetchable Memory Base / Prefetchable Memory Limit */
|
||||||
|
/* Force 64-bit addressing */
|
||||||
|
w1_bits = 0x00010001;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
w1_bits = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return w1_bits;
|
||||||
|
}
|
||||||
|
|
||||||
static int thunder_pem_bridge_write(struct pci_bus *bus, unsigned int devfn,
|
static int thunder_pem_bridge_write(struct pci_bus *bus, unsigned int devfn,
|
||||||
int where, int size, u32 val)
|
int where, int size, u32 val)
|
||||||
{
|
{
|
||||||
struct gen_pci *pci = bus->sysdata;
|
struct pci_config_window *cfg = bus->sysdata;
|
||||||
struct thunder_pem_pci *pem_pci;
|
struct thunder_pem_pci *pem_pci = (struct thunder_pem_pci *)cfg->priv;
|
||||||
u64 write_val, read_val;
|
u64 write_val, read_val;
|
||||||
|
u64 where_aligned = where & ~3ull;
|
||||||
u32 mask = 0;
|
u32 mask = 0;
|
||||||
|
|
||||||
pem_pci = container_of(pci, struct thunder_pem_pci, gen_pci);
|
|
||||||
|
|
||||||
if (devfn != 0 || where >= 2048)
|
if (devfn != 0 || where >= 2048)
|
||||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||||
@@ -205,8 +214,7 @@ static int thunder_pem_bridge_write(struct pci_bus *bus, unsigned int devfn,
|
|||||||
*/
|
*/
|
||||||
switch (size) {
|
switch (size) {
|
||||||
case 1:
|
case 1:
|
||||||
read_val = where & ~3ull;
|
writeq(where_aligned, pem_pci->pem_reg_base + PEM_CFG_RD);
|
||||||
writeq(read_val, pem_pci->pem_reg_base + PEM_CFG_RD);
|
|
||||||
read_val = readq(pem_pci->pem_reg_base + PEM_CFG_RD);
|
read_val = readq(pem_pci->pem_reg_base + PEM_CFG_RD);
|
||||||
read_val >>= 32;
|
read_val >>= 32;
|
||||||
mask = ~(0xff << (8 * (where & 3)));
|
mask = ~(0xff << (8 * (where & 3)));
|
||||||
@@ -215,8 +223,7 @@ static int thunder_pem_bridge_write(struct pci_bus *bus, unsigned int devfn,
|
|||||||
val |= (u32)read_val;
|
val |= (u32)read_val;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
read_val = where & ~3ull;
|
writeq(where_aligned, pem_pci->pem_reg_base + PEM_CFG_RD);
|
||||||
writeq(read_val, pem_pci->pem_reg_base + PEM_CFG_RD);
|
|
||||||
read_val = readq(pem_pci->pem_reg_base + PEM_CFG_RD);
|
read_val = readq(pem_pci->pem_reg_base + PEM_CFG_RD);
|
||||||
read_val >>= 32;
|
read_val >>= 32;
|
||||||
mask = ~(0xffff << (8 * (where & 3)));
|
mask = ~(0xffff << (8 * (where & 3)));
|
||||||
@@ -243,12 +250,18 @@ static int thunder_pem_bridge_write(struct pci_bus *bus, unsigned int devfn,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Some bits must be read-only with value of one. Since the
|
||||||
|
* access method allows these to be cleared if a zero is
|
||||||
|
* written, force them to one before writing.
|
||||||
|
*/
|
||||||
|
val |= thunder_pem_bridge_w1_bits(where_aligned);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Low order bits are the config address, the high order 32
|
* Low order bits are the config address, the high order 32
|
||||||
* bits are the data to be written.
|
* bits are the data to be written.
|
||||||
*/
|
*/
|
||||||
write_val = where & ~3ull;
|
write_val = (((u64)val) << 32) | where_aligned;
|
||||||
write_val |= (((u64)val) << 32);
|
|
||||||
writeq(write_val, pem_pci->pem_reg_base + PEM_CFG_WR);
|
writeq(write_val, pem_pci->pem_reg_base + PEM_CFG_WR);
|
||||||
return PCIBIOS_SUCCESSFUL;
|
return PCIBIOS_SUCCESSFUL;
|
||||||
}
|
}
|
||||||
@@ -256,53 +269,38 @@ static int thunder_pem_bridge_write(struct pci_bus *bus, unsigned int devfn,
|
|||||||
static int thunder_pem_config_write(struct pci_bus *bus, unsigned int devfn,
|
static int thunder_pem_config_write(struct pci_bus *bus, unsigned int devfn,
|
||||||
int where, int size, u32 val)
|
int where, int size, u32 val)
|
||||||
{
|
{
|
||||||
struct gen_pci *pci = bus->sysdata;
|
struct pci_config_window *cfg = bus->sysdata;
|
||||||
|
|
||||||
if (bus->number < pci->cfg.bus_range->start ||
|
if (bus->number < cfg->busr.start ||
|
||||||
bus->number > pci->cfg.bus_range->end)
|
bus->number > cfg->busr.end)
|
||||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||||
/*
|
/*
|
||||||
* The first device on the bus is the PEM PCIe bridge.
|
* The first device on the bus is the PEM PCIe bridge.
|
||||||
* Special case its config access.
|
* Special case its config access.
|
||||||
*/
|
*/
|
||||||
if (bus->number == pci->cfg.bus_range->start)
|
if (bus->number == cfg->busr.start)
|
||||||
return thunder_pem_bridge_write(bus, devfn, where, size, val);
|
return thunder_pem_bridge_write(bus, devfn, where, size, val);
|
||||||
|
|
||||||
|
|
||||||
return pci_generic_config_write(bus, devfn, where, size, val);
|
return pci_generic_config_write(bus, devfn, where, size, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct gen_pci_cfg_bus_ops thunder_pem_bus_ops = {
|
static int thunder_pem_init(struct device *dev, struct pci_config_window *cfg)
|
||||||
.bus_shift = 24,
|
|
||||||
.ops = {
|
|
||||||
.map_bus = thunder_pem_map_bus,
|
|
||||||
.read = thunder_pem_config_read,
|
|
||||||
.write = thunder_pem_config_write,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct of_device_id thunder_pem_of_match[] = {
|
|
||||||
{ .compatible = "cavium,pci-host-thunder-pem",
|
|
||||||
.data = &thunder_pem_bus_ops },
|
|
||||||
|
|
||||||
{ },
|
|
||||||
};
|
|
||||||
MODULE_DEVICE_TABLE(of, thunder_pem_of_match);
|
|
||||||
|
|
||||||
static int thunder_pem_probe(struct platform_device *pdev)
|
|
||||||
{
|
{
|
||||||
struct device *dev = &pdev->dev;
|
|
||||||
const struct of_device_id *of_id;
|
|
||||||
resource_size_t bar4_start;
|
resource_size_t bar4_start;
|
||||||
struct resource *res_pem;
|
struct resource *res_pem;
|
||||||
struct thunder_pem_pci *pem_pci;
|
struct thunder_pem_pci *pem_pci;
|
||||||
|
struct platform_device *pdev;
|
||||||
|
|
||||||
|
/* Only OF support for now */
|
||||||
|
if (!dev->of_node)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
pem_pci = devm_kzalloc(dev, sizeof(*pem_pci), GFP_KERNEL);
|
pem_pci = devm_kzalloc(dev, sizeof(*pem_pci), GFP_KERNEL);
|
||||||
if (!pem_pci)
|
if (!pem_pci)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
of_id = of_match_node(thunder_pem_of_match, dev->of_node);
|
pdev = to_platform_device(dev);
|
||||||
pem_pci->gen_pci.cfg.ops = (struct gen_pci_cfg_bus_ops *)of_id->data;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The second register range is the PEM bridge to the PCIe
|
* The second register range is the PEM bridge to the PCIe
|
||||||
@@ -330,7 +328,29 @@ static int thunder_pem_probe(struct platform_device *pdev)
|
|||||||
pem_pci->ea_entry[1] = (u32)(res_pem->end - bar4_start) & ~3u;
|
pem_pci->ea_entry[1] = (u32)(res_pem->end - bar4_start) & ~3u;
|
||||||
pem_pci->ea_entry[2] = (u32)(bar4_start >> 32);
|
pem_pci->ea_entry[2] = (u32)(bar4_start >> 32);
|
||||||
|
|
||||||
return pci_host_common_probe(pdev, &pem_pci->gen_pci);
|
cfg->priv = pem_pci;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct pci_ecam_ops pci_thunder_pem_ops = {
|
||||||
|
.bus_shift = 24,
|
||||||
|
.init = thunder_pem_init,
|
||||||
|
.pci_ops = {
|
||||||
|
.map_bus = pci_ecam_map_bus,
|
||||||
|
.read = thunder_pem_config_read,
|
||||||
|
.write = thunder_pem_config_write,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct of_device_id thunder_pem_of_match[] = {
|
||||||
|
{ .compatible = "cavium,pci-host-thunder-pem" },
|
||||||
|
{ },
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, thunder_pem_of_match);
|
||||||
|
|
||||||
|
static int thunder_pem_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
return pci_host_common_probe(pdev, &pci_thunder_pem_ops);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct platform_driver thunder_pem_driver = {
|
static struct platform_driver thunder_pem_driver = {
|
||||||
|
|||||||
@@ -0,0 +1,262 @@
|
|||||||
|
/*
|
||||||
|
* PCIe host controller driver for Marvell Armada-8K SoCs
|
||||||
|
*
|
||||||
|
* Armada-8K PCIe Glue Layer Source Code
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 Marvell Technology Group Ltd.
|
||||||
|
*
|
||||||
|
* This file is licensed under the terms of the GNU General Public
|
||||||
|
* License version 2. This program is licensed "as is" without any
|
||||||
|
* warranty of any kind, whether express or implied.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/pci.h>
|
||||||
|
#include <linux/phy/phy.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/resource.h>
|
||||||
|
#include <linux/of_pci.h>
|
||||||
|
#include <linux/of_irq.h>
|
||||||
|
|
||||||
|
#include "pcie-designware.h"
|
||||||
|
|
||||||
|
struct armada8k_pcie {
|
||||||
|
void __iomem *base;
|
||||||
|
struct clk *clk;
|
||||||
|
struct pcie_port pp;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define PCIE_VENDOR_REGS_OFFSET 0x8000
|
||||||
|
|
||||||
|
#define PCIE_GLOBAL_CONTROL_REG 0x0
|
||||||
|
#define PCIE_APP_LTSSM_EN BIT(2)
|
||||||
|
#define PCIE_DEVICE_TYPE_SHIFT 4
|
||||||
|
#define PCIE_DEVICE_TYPE_MASK 0xF
|
||||||
|
#define PCIE_DEVICE_TYPE_RC 0x4 /* Root complex */
|
||||||
|
|
||||||
|
#define PCIE_GLOBAL_STATUS_REG 0x8
|
||||||
|
#define PCIE_GLB_STS_RDLH_LINK_UP BIT(1)
|
||||||
|
#define PCIE_GLB_STS_PHY_LINK_UP BIT(9)
|
||||||
|
|
||||||
|
#define PCIE_GLOBAL_INT_CAUSE1_REG 0x1C
|
||||||
|
#define PCIE_GLOBAL_INT_MASK1_REG 0x20
|
||||||
|
#define PCIE_INT_A_ASSERT_MASK BIT(9)
|
||||||
|
#define PCIE_INT_B_ASSERT_MASK BIT(10)
|
||||||
|
#define PCIE_INT_C_ASSERT_MASK BIT(11)
|
||||||
|
#define PCIE_INT_D_ASSERT_MASK BIT(12)
|
||||||
|
|
||||||
|
#define PCIE_ARCACHE_TRC_REG 0x50
|
||||||
|
#define PCIE_AWCACHE_TRC_REG 0x54
|
||||||
|
#define PCIE_ARUSER_REG 0x5C
|
||||||
|
#define PCIE_AWUSER_REG 0x60
|
||||||
|
/*
|
||||||
|
* AR/AW Cache defauls: Normal memory, Write-Back, Read / Write
|
||||||
|
* allocate
|
||||||
|
*/
|
||||||
|
#define ARCACHE_DEFAULT_VALUE 0x3511
|
||||||
|
#define AWCACHE_DEFAULT_VALUE 0x5311
|
||||||
|
|
||||||
|
#define DOMAIN_OUTER_SHAREABLE 0x2
|
||||||
|
#define AX_USER_DOMAIN_MASK 0x3
|
||||||
|
#define AX_USER_DOMAIN_SHIFT 4
|
||||||
|
|
||||||
|
#define to_armada8k_pcie(x) container_of(x, struct armada8k_pcie, pp)
|
||||||
|
|
||||||
|
static int armada8k_pcie_link_up(struct pcie_port *pp)
|
||||||
|
{
|
||||||
|
struct armada8k_pcie *pcie = to_armada8k_pcie(pp);
|
||||||
|
u32 reg;
|
||||||
|
u32 mask = PCIE_GLB_STS_RDLH_LINK_UP | PCIE_GLB_STS_PHY_LINK_UP;
|
||||||
|
|
||||||
|
reg = readl(pcie->base + PCIE_GLOBAL_STATUS_REG);
|
||||||
|
|
||||||
|
if ((reg & mask) == mask)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
dev_dbg(pp->dev, "No link detected (Global-Status: 0x%08x).\n", reg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void armada8k_pcie_establish_link(struct pcie_port *pp)
|
||||||
|
{
|
||||||
|
struct armada8k_pcie *pcie = to_armada8k_pcie(pp);
|
||||||
|
void __iomem *base = pcie->base;
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
if (!dw_pcie_link_up(pp)) {
|
||||||
|
/* Disable LTSSM state machine to enable configuration */
|
||||||
|
reg = readl(base + PCIE_GLOBAL_CONTROL_REG);
|
||||||
|
reg &= ~(PCIE_APP_LTSSM_EN);
|
||||||
|
writel(reg, base + PCIE_GLOBAL_CONTROL_REG);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the device to root complex mode */
|
||||||
|
reg = readl(base + PCIE_GLOBAL_CONTROL_REG);
|
||||||
|
reg &= ~(PCIE_DEVICE_TYPE_MASK << PCIE_DEVICE_TYPE_SHIFT);
|
||||||
|
reg |= PCIE_DEVICE_TYPE_RC << PCIE_DEVICE_TYPE_SHIFT;
|
||||||
|
writel(reg, base + PCIE_GLOBAL_CONTROL_REG);
|
||||||
|
|
||||||
|
/* Set the PCIe master AxCache attributes */
|
||||||
|
writel(ARCACHE_DEFAULT_VALUE, base + PCIE_ARCACHE_TRC_REG);
|
||||||
|
writel(AWCACHE_DEFAULT_VALUE, base + PCIE_AWCACHE_TRC_REG);
|
||||||
|
|
||||||
|
/* Set the PCIe master AxDomain attributes */
|
||||||
|
reg = readl(base + PCIE_ARUSER_REG);
|
||||||
|
reg &= ~(AX_USER_DOMAIN_MASK << AX_USER_DOMAIN_SHIFT);
|
||||||
|
reg |= DOMAIN_OUTER_SHAREABLE << AX_USER_DOMAIN_SHIFT;
|
||||||
|
writel(reg, base + PCIE_ARUSER_REG);
|
||||||
|
|
||||||
|
reg = readl(base + PCIE_AWUSER_REG);
|
||||||
|
reg &= ~(AX_USER_DOMAIN_MASK << AX_USER_DOMAIN_SHIFT);
|
||||||
|
reg |= DOMAIN_OUTER_SHAREABLE << AX_USER_DOMAIN_SHIFT;
|
||||||
|
writel(reg, base + PCIE_AWUSER_REG);
|
||||||
|
|
||||||
|
/* Enable INT A-D interrupts */
|
||||||
|
reg = readl(base + PCIE_GLOBAL_INT_MASK1_REG);
|
||||||
|
reg |= PCIE_INT_A_ASSERT_MASK | PCIE_INT_B_ASSERT_MASK |
|
||||||
|
PCIE_INT_C_ASSERT_MASK | PCIE_INT_D_ASSERT_MASK;
|
||||||
|
writel(reg, base + PCIE_GLOBAL_INT_MASK1_REG);
|
||||||
|
|
||||||
|
if (!dw_pcie_link_up(pp)) {
|
||||||
|
/* Configuration done. Start LTSSM */
|
||||||
|
reg = readl(base + PCIE_GLOBAL_CONTROL_REG);
|
||||||
|
reg |= PCIE_APP_LTSSM_EN;
|
||||||
|
writel(reg, base + PCIE_GLOBAL_CONTROL_REG);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait until the link becomes active again */
|
||||||
|
if (dw_pcie_wait_for_link(pp))
|
||||||
|
dev_err(pp->dev, "Link not up after reconfiguration\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void armada8k_pcie_host_init(struct pcie_port *pp)
|
||||||
|
{
|
||||||
|
dw_pcie_setup_rc(pp);
|
||||||
|
armada8k_pcie_establish_link(pp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t armada8k_pcie_irq_handler(int irq, void *arg)
|
||||||
|
{
|
||||||
|
struct pcie_port *pp = arg;
|
||||||
|
struct armada8k_pcie *pcie = to_armada8k_pcie(pp);
|
||||||
|
void __iomem *base = pcie->base;
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Interrupts are directly handled by the device driver of the
|
||||||
|
* PCI device. However, they are also latched into the PCIe
|
||||||
|
* controller, so we simply discard them.
|
||||||
|
*/
|
||||||
|
val = readl(base + PCIE_GLOBAL_INT_CAUSE1_REG);
|
||||||
|
writel(val, base + PCIE_GLOBAL_INT_CAUSE1_REG);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct pcie_host_ops armada8k_pcie_host_ops = {
|
||||||
|
.link_up = armada8k_pcie_link_up,
|
||||||
|
.host_init = armada8k_pcie_host_init,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int armada8k_add_pcie_port(struct pcie_port *pp,
|
||||||
|
struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
pp->root_bus_nr = -1;
|
||||||
|
pp->ops = &armada8k_pcie_host_ops;
|
||||||
|
|
||||||
|
pp->irq = platform_get_irq(pdev, 0);
|
||||||
|
if (!pp->irq) {
|
||||||
|
dev_err(dev, "failed to get irq for port\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = devm_request_irq(dev, pp->irq, armada8k_pcie_irq_handler,
|
||||||
|
IRQF_SHARED, "armada8k-pcie", pp);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "failed to request irq %d\n", pp->irq);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = dw_pcie_host_init(pp);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "failed to initialize host: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int armada8k_pcie_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct armada8k_pcie *pcie;
|
||||||
|
struct pcie_port *pp;
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
struct resource *base;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
|
||||||
|
if (!pcie)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
pcie->clk = devm_clk_get(dev, NULL);
|
||||||
|
if (IS_ERR(pcie->clk))
|
||||||
|
return PTR_ERR(pcie->clk);
|
||||||
|
|
||||||
|
clk_prepare_enable(pcie->clk);
|
||||||
|
|
||||||
|
pp = &pcie->pp;
|
||||||
|
pp->dev = dev;
|
||||||
|
platform_set_drvdata(pdev, pcie);
|
||||||
|
|
||||||
|
/* Get the dw-pcie unit configuration/control registers base. */
|
||||||
|
base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl");
|
||||||
|
pp->dbi_base = devm_ioremap_resource(dev, base);
|
||||||
|
if (IS_ERR(pp->dbi_base)) {
|
||||||
|
dev_err(dev, "couldn't remap regs base %p\n", base);
|
||||||
|
ret = PTR_ERR(pp->dbi_base);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
pcie->base = pp->dbi_base + PCIE_VENDOR_REGS_OFFSET;
|
||||||
|
|
||||||
|
ret = armada8k_add_pcie_port(pp, pdev);
|
||||||
|
if (ret)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
if (!IS_ERR(pcie->clk))
|
||||||
|
clk_disable_unprepare(pcie->clk);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id armada8k_pcie_of_match[] = {
|
||||||
|
{ .compatible = "marvell,armada8k-pcie", },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, armada8k_pcie_of_match);
|
||||||
|
|
||||||
|
static struct platform_driver armada8k_pcie_driver = {
|
||||||
|
.probe = armada8k_pcie_probe,
|
||||||
|
.driver = {
|
||||||
|
.name = "armada8k-pcie",
|
||||||
|
.of_match_table = of_match_ptr(armada8k_pcie_of_match),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module_platform_driver(armada8k_pcie_driver);
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("Armada 8k PCIe host controller driver");
|
||||||
|
MODULE_AUTHOR("Yehuda Yitshak <yehuday@marvell.com>");
|
||||||
|
MODULE_AUTHOR("Shadi Ammouri <shadi@marvell.com>");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
||||||
@@ -434,7 +434,6 @@ int dw_pcie_host_init(struct pcie_port *pp)
|
|||||||
struct platform_device *pdev = to_platform_device(pp->dev);
|
struct platform_device *pdev = to_platform_device(pp->dev);
|
||||||
struct pci_bus *bus, *child;
|
struct pci_bus *bus, *child;
|
||||||
struct resource *cfg_res;
|
struct resource *cfg_res;
|
||||||
u32 val;
|
|
||||||
int i, ret;
|
int i, ret;
|
||||||
LIST_HEAD(res);
|
LIST_HEAD(res);
|
||||||
struct resource_entry *win;
|
struct resource_entry *win;
|
||||||
@@ -544,25 +543,6 @@ int dw_pcie_host_init(struct pcie_port *pp)
|
|||||||
if (pp->ops->host_init)
|
if (pp->ops->host_init)
|
||||||
pp->ops->host_init(pp);
|
pp->ops->host_init(pp);
|
||||||
|
|
||||||
/*
|
|
||||||
* If the platform provides ->rd_other_conf, it means the platform
|
|
||||||
* uses its own address translation component rather than ATU, so
|
|
||||||
* we should not program the ATU here.
|
|
||||||
*/
|
|
||||||
if (!pp->ops->rd_other_conf)
|
|
||||||
dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX1,
|
|
||||||
PCIE_ATU_TYPE_MEM, pp->mem_base,
|
|
||||||
pp->mem_bus_addr, pp->mem_size);
|
|
||||||
|
|
||||||
dw_pcie_wr_own_conf(pp, PCI_BASE_ADDRESS_0, 4, 0);
|
|
||||||
|
|
||||||
/* program correct class for RC */
|
|
||||||
dw_pcie_wr_own_conf(pp, PCI_CLASS_DEVICE, 2, PCI_CLASS_BRIDGE_PCI);
|
|
||||||
|
|
||||||
dw_pcie_rd_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, &val);
|
|
||||||
val |= PORT_LOGIC_SPEED_CHANGE;
|
|
||||||
dw_pcie_wr_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, val);
|
|
||||||
|
|
||||||
pp->root_bus_nr = pp->busn->start;
|
pp->root_bus_nr = pp->busn->start;
|
||||||
if (IS_ENABLED(CONFIG_PCI_MSI)) {
|
if (IS_ENABLED(CONFIG_PCI_MSI)) {
|
||||||
bus = pci_scan_root_bus_msi(pp->dev, pp->root_bus_nr,
|
bus = pci_scan_root_bus_msi(pp->dev, pp->root_bus_nr,
|
||||||
@@ -728,8 +708,6 @@ static struct pci_ops dw_pcie_ops = {
|
|||||||
void dw_pcie_setup_rc(struct pcie_port *pp)
|
void dw_pcie_setup_rc(struct pcie_port *pp)
|
||||||
{
|
{
|
||||||
u32 val;
|
u32 val;
|
||||||
u32 membase;
|
|
||||||
u32 memlimit;
|
|
||||||
|
|
||||||
/* set the number of lanes */
|
/* set the number of lanes */
|
||||||
dw_pcie_readl_rc(pp, PCIE_PORT_LINK_CONTROL, &val);
|
dw_pcie_readl_rc(pp, PCIE_PORT_LINK_CONTROL, &val);
|
||||||
@@ -788,18 +766,31 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
|
|||||||
val |= 0x00010100;
|
val |= 0x00010100;
|
||||||
dw_pcie_writel_rc(pp, val, PCI_PRIMARY_BUS);
|
dw_pcie_writel_rc(pp, val, PCI_PRIMARY_BUS);
|
||||||
|
|
||||||
/* setup memory base, memory limit */
|
|
||||||
membase = ((u32)pp->mem_base & 0xfff00000) >> 16;
|
|
||||||
memlimit = (pp->mem_size + (u32)pp->mem_base) & 0xfff00000;
|
|
||||||
val = memlimit | membase;
|
|
||||||
dw_pcie_writel_rc(pp, val, PCI_MEMORY_BASE);
|
|
||||||
|
|
||||||
/* setup command register */
|
/* setup command register */
|
||||||
dw_pcie_readl_rc(pp, PCI_COMMAND, &val);
|
dw_pcie_readl_rc(pp, PCI_COMMAND, &val);
|
||||||
val &= 0xffff0000;
|
val &= 0xffff0000;
|
||||||
val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
|
val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
|
||||||
PCI_COMMAND_MASTER | PCI_COMMAND_SERR;
|
PCI_COMMAND_MASTER | PCI_COMMAND_SERR;
|
||||||
dw_pcie_writel_rc(pp, val, PCI_COMMAND);
|
dw_pcie_writel_rc(pp, val, PCI_COMMAND);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the platform provides ->rd_other_conf, it means the platform
|
||||||
|
* uses its own address translation component rather than ATU, so
|
||||||
|
* we should not program the ATU here.
|
||||||
|
*/
|
||||||
|
if (!pp->ops->rd_other_conf)
|
||||||
|
dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX1,
|
||||||
|
PCIE_ATU_TYPE_MEM, pp->mem_base,
|
||||||
|
pp->mem_bus_addr, pp->mem_size);
|
||||||
|
|
||||||
|
dw_pcie_wr_own_conf(pp, PCI_BASE_ADDRESS_0, 4, 0);
|
||||||
|
|
||||||
|
/* program correct class for RC */
|
||||||
|
dw_pcie_wr_own_conf(pp, PCI_CLASS_DEVICE, 2, PCI_CLASS_BRIDGE_PCI);
|
||||||
|
|
||||||
|
dw_pcie_rd_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, &val);
|
||||||
|
val |= PORT_LOGIC_SPEED_CHANGE;
|
||||||
|
dw_pcie_wr_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
|
MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
|
||||||
|
|||||||
@@ -819,7 +819,7 @@ static int nwl_pcie_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
err = nwl_pcie_bridge_init(pcie);
|
err = nwl_pcie_bridge_init(pcie);
|
||||||
if (err) {
|
if (err) {
|
||||||
dev_err(pcie->dev, "HW Initalization failed\n");
|
dev_err(pcie->dev, "HW Initialization failed\n");
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -138,6 +138,8 @@ static union apci_descriptor *ibm_slot_from_id(int id)
|
|||||||
char *table;
|
char *table;
|
||||||
|
|
||||||
size = ibm_get_table_from_acpi(&table);
|
size = ibm_get_table_from_acpi(&table);
|
||||||
|
if (size < 0)
|
||||||
|
return NULL;
|
||||||
des = (union apci_descriptor *)table;
|
des = (union apci_descriptor *)table;
|
||||||
if (memcmp(des->header.sig, "aPCI", 4) != 0)
|
if (memcmp(des->header.sig, "aPCI", 4) != 0)
|
||||||
goto ibm_slot_done;
|
goto ibm_slot_done;
|
||||||
|
|||||||
@@ -1008,6 +1008,9 @@ static int pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
|
|||||||
if (i >= PCI_ROM_RESOURCE)
|
if (i >= PCI_ROM_RESOURCE)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
|
if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(res->start))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
if (!pci_mmap_fits(pdev, i, vma, PCI_MMAP_SYSFS)) {
|
if (!pci_mmap_fits(pdev, i, vma, PCI_MMAP_SYSFS)) {
|
||||||
WARN(1, "process \"%s\" tried to map 0x%08lx bytes at page 0x%08lx on %s BAR %d (start 0x%16Lx, size 0x%16Lx)\n",
|
WARN(1, "process \"%s\" tried to map 0x%08lx bytes at page 0x%08lx on %s BAR %d (start 0x%16Lx, size 0x%16Lx)\n",
|
||||||
current->comm, vma->vm_end-vma->vm_start, vma->vm_pgoff,
|
current->comm, vma->vm_end-vma->vm_start, vma->vm_pgoff,
|
||||||
@@ -1024,10 +1027,6 @@ static int pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
|
|||||||
pci_resource_to_user(pdev, i, res, &start, &end);
|
pci_resource_to_user(pdev, i, res, &start, &end);
|
||||||
vma->vm_pgoff += start >> PAGE_SHIFT;
|
vma->vm_pgoff += start >> PAGE_SHIFT;
|
||||||
mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io;
|
mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io;
|
||||||
|
|
||||||
if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(start))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
return pci_mmap_page_range(pdev, vma, mmap_type, write_combine);
|
return pci_mmap_page_range(pdev, vma, mmap_type, write_combine);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+152
-8
@@ -2228,7 +2228,7 @@ void pci_pm_init(struct pci_dev *dev)
|
|||||||
|
|
||||||
static unsigned long pci_ea_flags(struct pci_dev *dev, u8 prop)
|
static unsigned long pci_ea_flags(struct pci_dev *dev, u8 prop)
|
||||||
{
|
{
|
||||||
unsigned long flags = IORESOURCE_PCI_FIXED;
|
unsigned long flags = IORESOURCE_PCI_FIXED | IORESOURCE_PCI_EA_BEI;
|
||||||
|
|
||||||
switch (prop) {
|
switch (prop) {
|
||||||
case PCI_EA_P_MEM:
|
case PCI_EA_P_MEM:
|
||||||
@@ -2389,7 +2389,7 @@ out:
|
|||||||
return offset + ent_size;
|
return offset + ent_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Enhanced Allocation Initalization */
|
/* Enhanced Allocation Initialization */
|
||||||
void pci_ea_init(struct pci_dev *dev)
|
void pci_ea_init(struct pci_dev *dev)
|
||||||
{
|
{
|
||||||
int ea;
|
int ea;
|
||||||
@@ -2547,7 +2547,7 @@ void pci_request_acs(void)
|
|||||||
* pci_std_enable_acs - enable ACS on devices using standard ACS capabilites
|
* pci_std_enable_acs - enable ACS on devices using standard ACS capabilites
|
||||||
* @dev: the PCI device
|
* @dev: the PCI device
|
||||||
*/
|
*/
|
||||||
static int pci_std_enable_acs(struct pci_dev *dev)
|
static void pci_std_enable_acs(struct pci_dev *dev)
|
||||||
{
|
{
|
||||||
int pos;
|
int pos;
|
||||||
u16 cap;
|
u16 cap;
|
||||||
@@ -2555,7 +2555,7 @@ static int pci_std_enable_acs(struct pci_dev *dev)
|
|||||||
|
|
||||||
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS);
|
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS);
|
||||||
if (!pos)
|
if (!pos)
|
||||||
return -ENODEV;
|
return;
|
||||||
|
|
||||||
pci_read_config_word(dev, pos + PCI_ACS_CAP, &cap);
|
pci_read_config_word(dev, pos + PCI_ACS_CAP, &cap);
|
||||||
pci_read_config_word(dev, pos + PCI_ACS_CTRL, &ctrl);
|
pci_read_config_word(dev, pos + PCI_ACS_CTRL, &ctrl);
|
||||||
@@ -2573,8 +2573,6 @@ static int pci_std_enable_acs(struct pci_dev *dev)
|
|||||||
ctrl |= (cap & PCI_ACS_UF);
|
ctrl |= (cap & PCI_ACS_UF);
|
||||||
|
|
||||||
pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl);
|
pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl);
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2586,10 +2584,10 @@ void pci_enable_acs(struct pci_dev *dev)
|
|||||||
if (!pci_acs_enable)
|
if (!pci_acs_enable)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!pci_std_enable_acs(dev))
|
if (!pci_dev_specific_enable_acs(dev))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
pci_dev_specific_enable_acs(dev);
|
pci_std_enable_acs(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool pci_acs_flags_enabled(struct pci_dev *pdev, u16 acs_flags)
|
static bool pci_acs_flags_enabled(struct pci_dev *pdev, u16 acs_flags)
|
||||||
@@ -3021,6 +3019,121 @@ int pci_request_regions_exclusive(struct pci_dev *pdev, const char *res_name)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(pci_request_regions_exclusive);
|
EXPORT_SYMBOL(pci_request_regions_exclusive);
|
||||||
|
|
||||||
|
#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
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* pci_remap_iospace - Remap the memory mapped I/O space
|
* pci_remap_iospace - Remap the memory mapped I/O space
|
||||||
* @res: Resource describing the I/O space
|
* @res: Resource describing the I/O space
|
||||||
@@ -4578,6 +4691,37 @@ int pci_set_vga_state(struct pci_dev *dev, bool decode,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pci_add_dma_alias - Add a DMA devfn alias for a device
|
||||||
|
* @dev: the PCI device for which alias is added
|
||||||
|
* @devfn: alias slot and function
|
||||||
|
*
|
||||||
|
* This helper encodes 8-bit devfn as bit number in dma_alias_mask.
|
||||||
|
* It should be called early, preferably as PCI fixup header quirk.
|
||||||
|
*/
|
||||||
|
void pci_add_dma_alias(struct pci_dev *dev, u8 devfn)
|
||||||
|
{
|
||||||
|
if (!dev->dma_alias_mask)
|
||||||
|
dev->dma_alias_mask = kcalloc(BITS_TO_LONGS(U8_MAX),
|
||||||
|
sizeof(long), GFP_KERNEL);
|
||||||
|
if (!dev->dma_alias_mask) {
|
||||||
|
dev_warn(&dev->dev, "Unable to allocate DMA alias mask\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
set_bit(devfn, dev->dma_alias_mask);
|
||||||
|
dev_info(&dev->dev, "Enabling fixed DMA alias to %02x.%d\n",
|
||||||
|
PCI_SLOT(devfn), PCI_FUNC(devfn));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pci_devs_are_dma_aliases(struct pci_dev *dev1, struct pci_dev *dev2)
|
||||||
|
{
|
||||||
|
return (dev1->dma_alias_mask &&
|
||||||
|
test_bit(dev2->devfn, dev1->dma_alias_mask)) ||
|
||||||
|
(dev2->dma_alias_mask &&
|
||||||
|
test_bit(dev1->devfn, dev2->dma_alias_mask));
|
||||||
|
}
|
||||||
|
|
||||||
bool pci_device_is_present(struct pci_dev *pdev)
|
bool pci_device_is_present(struct pci_dev *pdev)
|
||||||
{
|
{
|
||||||
u32 v;
|
u32 v;
|
||||||
|
|||||||
@@ -81,3 +81,17 @@ endchoice
|
|||||||
config PCIE_PME
|
config PCIE_PME
|
||||||
def_bool y
|
def_bool y
|
||||||
depends on PCIEPORTBUS && PM
|
depends on PCIEPORTBUS && PM
|
||||||
|
|
||||||
|
config PCIE_DPC
|
||||||
|
tristate "PCIe Downstream Port Containment support"
|
||||||
|
depends on PCIEPORTBUS
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
This enables PCI Express Downstream Port Containment (DPC)
|
||||||
|
driver support. DPC events from Root and Downstream ports
|
||||||
|
will be handled by the DPC driver. If your system doesn't
|
||||||
|
have this capability or you do not want to use this feature,
|
||||||
|
it is safe to answer N.
|
||||||
|
|
||||||
|
To compile this driver as a module, choose M here: the module
|
||||||
|
will be called pcie-dpc.
|
||||||
|
|||||||
@@ -14,3 +14,5 @@ obj-$(CONFIG_PCIEPORTBUS) += pcieportdrv.o
|
|||||||
obj-$(CONFIG_PCIEAER) += aer/
|
obj-$(CONFIG_PCIEAER) += aer/
|
||||||
|
|
||||||
obj-$(CONFIG_PCIE_PME) += pme.o
|
obj-$(CONFIG_PCIE_PME) += pme.o
|
||||||
|
|
||||||
|
obj-$(CONFIG_PCIE_DPC) += pcie-dpc.o
|
||||||
|
|||||||
@@ -0,0 +1,163 @@
|
|||||||
|
/*
|
||||||
|
* PCI Express Downstream Port Containment services driver
|
||||||
|
* Copyright (C) 2016 Intel Corp.
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of the GNU General Public
|
||||||
|
* License. See the file "COPYING" in the main directory of this archive
|
||||||
|
* for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/pci.h>
|
||||||
|
#include <linux/pcieport_if.h>
|
||||||
|
|
||||||
|
struct dpc_dev {
|
||||||
|
struct pcie_device *dev;
|
||||||
|
struct work_struct work;
|
||||||
|
int cap_pos;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void dpc_wait_link_inactive(struct pci_dev *pdev)
|
||||||
|
{
|
||||||
|
unsigned long timeout = jiffies + HZ;
|
||||||
|
u16 lnk_status;
|
||||||
|
|
||||||
|
pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
|
||||||
|
while (lnk_status & PCI_EXP_LNKSTA_DLLLA &&
|
||||||
|
!time_after(jiffies, timeout)) {
|
||||||
|
msleep(10);
|
||||||
|
pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
|
||||||
|
}
|
||||||
|
if (lnk_status & PCI_EXP_LNKSTA_DLLLA)
|
||||||
|
dev_warn(&pdev->dev, "Link state not disabled for DPC event");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void interrupt_event_handler(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct dpc_dev *dpc = container_of(work, struct dpc_dev, work);
|
||||||
|
struct pci_dev *dev, *temp, *pdev = dpc->dev->port;
|
||||||
|
struct pci_bus *parent = pdev->subordinate;
|
||||||
|
|
||||||
|
pci_lock_rescan_remove();
|
||||||
|
list_for_each_entry_safe_reverse(dev, temp, &parent->devices,
|
||||||
|
bus_list) {
|
||||||
|
pci_dev_get(dev);
|
||||||
|
pci_stop_and_remove_bus_device(dev);
|
||||||
|
pci_dev_put(dev);
|
||||||
|
}
|
||||||
|
pci_unlock_rescan_remove();
|
||||||
|
|
||||||
|
dpc_wait_link_inactive(pdev);
|
||||||
|
pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_STATUS,
|
||||||
|
PCI_EXP_DPC_STATUS_TRIGGER | PCI_EXP_DPC_STATUS_INTERRUPT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t dpc_irq(int irq, void *context)
|
||||||
|
{
|
||||||
|
struct dpc_dev *dpc = (struct dpc_dev *)context;
|
||||||
|
struct pci_dev *pdev = dpc->dev->port;
|
||||||
|
u16 status, source;
|
||||||
|
|
||||||
|
pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_STATUS, &status);
|
||||||
|
pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_SOURCE_ID,
|
||||||
|
&source);
|
||||||
|
if (!status)
|
||||||
|
return IRQ_NONE;
|
||||||
|
|
||||||
|
dev_info(&dpc->dev->device, "DPC containment event, status:%#06x source:%#06x\n",
|
||||||
|
status, source);
|
||||||
|
|
||||||
|
if (status & PCI_EXP_DPC_STATUS_TRIGGER) {
|
||||||
|
u16 reason = (status >> 1) & 0x3;
|
||||||
|
|
||||||
|
dev_warn(&dpc->dev->device, "DPC %s triggered, remove downstream devices\n",
|
||||||
|
(reason == 0) ? "unmasked uncorrectable error" :
|
||||||
|
(reason == 1) ? "ERR_NONFATAL" :
|
||||||
|
(reason == 2) ? "ERR_FATAL" : "extended error");
|
||||||
|
schedule_work(&dpc->work);
|
||||||
|
}
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define FLAG(x, y) (((x) & (y)) ? '+' : '-')
|
||||||
|
static int dpc_probe(struct pcie_device *dev)
|
||||||
|
{
|
||||||
|
struct dpc_dev *dpc;
|
||||||
|
struct pci_dev *pdev = dev->port;
|
||||||
|
int status;
|
||||||
|
u16 ctl, cap;
|
||||||
|
|
||||||
|
dpc = kzalloc(sizeof(*dpc), GFP_KERNEL);
|
||||||
|
if (!dpc)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
dpc->cap_pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_DPC);
|
||||||
|
dpc->dev = dev;
|
||||||
|
INIT_WORK(&dpc->work, interrupt_event_handler);
|
||||||
|
set_service_data(dev, dpc);
|
||||||
|
|
||||||
|
status = request_irq(dev->irq, dpc_irq, IRQF_SHARED, "pcie-dpc", dpc);
|
||||||
|
if (status) {
|
||||||
|
dev_warn(&dev->device, "request IRQ%d failed: %d\n", dev->irq,
|
||||||
|
status);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CAP, &cap);
|
||||||
|
pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, &ctl);
|
||||||
|
|
||||||
|
ctl |= PCI_EXP_DPC_CTL_EN_NONFATAL | PCI_EXP_DPC_CTL_INT_EN;
|
||||||
|
pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, ctl);
|
||||||
|
|
||||||
|
dev_info(&dev->device, "DPC error containment capabilities: Int Msg #%d, RPExt%c PoisonedTLP%c SwTrigger%c RP PIO Log %d, DL_ActiveErr%c\n",
|
||||||
|
cap & 0xf, FLAG(cap, PCI_EXP_DPC_CAP_RP_EXT),
|
||||||
|
FLAG(cap, PCI_EXP_DPC_CAP_POISONED_TLP),
|
||||||
|
FLAG(cap, PCI_EXP_DPC_CAP_SW_TRIGGER), (cap >> 8) & 0xf,
|
||||||
|
FLAG(cap, PCI_EXP_DPC_CAP_DL_ACTIVE));
|
||||||
|
return status;
|
||||||
|
out:
|
||||||
|
kfree(dpc);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dpc_remove(struct pcie_device *dev)
|
||||||
|
{
|
||||||
|
struct dpc_dev *dpc = get_service_data(dev);
|
||||||
|
struct pci_dev *pdev = dev->port;
|
||||||
|
u16 ctl;
|
||||||
|
|
||||||
|
pci_read_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, &ctl);
|
||||||
|
ctl &= ~(PCI_EXP_DPC_CTL_EN_NONFATAL | PCI_EXP_DPC_CTL_INT_EN);
|
||||||
|
pci_write_config_word(pdev, dpc->cap_pos + PCI_EXP_DPC_CTL, ctl);
|
||||||
|
|
||||||
|
free_irq(dev->irq, dpc);
|
||||||
|
kfree(dpc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct pcie_port_service_driver dpcdriver = {
|
||||||
|
.name = "dpc",
|
||||||
|
.port_type = PCI_EXP_TYPE_ROOT_PORT | PCI_EXP_TYPE_DOWNSTREAM,
|
||||||
|
.service = PCIE_PORT_SERVICE_DPC,
|
||||||
|
.probe = dpc_probe,
|
||||||
|
.remove = dpc_remove,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init dpc_service_init(void)
|
||||||
|
{
|
||||||
|
return pcie_port_service_register(&dpcdriver);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit dpc_service_exit(void)
|
||||||
|
{
|
||||||
|
pcie_port_service_unregister(&dpcdriver);
|
||||||
|
}
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("PCI Express Downstream Port Containment driver");
|
||||||
|
MODULE_AUTHOR("Keith Busch <keith.busch@intel.com>");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_VERSION("0.1");
|
||||||
|
|
||||||
|
module_init(dpc_service_init);
|
||||||
|
module_exit(dpc_service_exit);
|
||||||
@@ -11,14 +11,14 @@
|
|||||||
|
|
||||||
#include <linux/compiler.h>
|
#include <linux/compiler.h>
|
||||||
|
|
||||||
#define PCIE_PORT_DEVICE_MAXSERVICES 4
|
#define PCIE_PORT_DEVICE_MAXSERVICES 5
|
||||||
/*
|
/*
|
||||||
* According to the PCI Express Base Specification 2.0, the indices of
|
* According to the PCI Express Base Specification 2.0, the indices of
|
||||||
* the MSI-X table entries used by port services must not exceed 31
|
* the MSI-X table entries used by port services must not exceed 31
|
||||||
*/
|
*/
|
||||||
#define PCIE_PORT_MAX_MSIX_ENTRIES 32
|
#define PCIE_PORT_MAX_MSIX_ENTRIES 32
|
||||||
|
|
||||||
#define get_descriptor_id(type, service) (((type - 4) << 4) | service)
|
#define get_descriptor_id(type, service) (((type - 4) << 8) | service)
|
||||||
|
|
||||||
extern struct bus_type pcie_port_bus_type;
|
extern struct bus_type pcie_port_bus_type;
|
||||||
int pcie_port_device_register(struct pci_dev *dev);
|
int pcie_port_device_register(struct pci_dev *dev);
|
||||||
@@ -67,17 +67,14 @@ static inline void pcie_pme_interrupt_enable(struct pci_dev *dev, bool en) {}
|
|||||||
#endif /* !CONFIG_PCIE_PME */
|
#endif /* !CONFIG_PCIE_PME */
|
||||||
|
|
||||||
#ifdef CONFIG_ACPI
|
#ifdef CONFIG_ACPI
|
||||||
int pcie_port_acpi_setup(struct pci_dev *port, int *mask);
|
void pcie_port_acpi_setup(struct pci_dev *port, int *mask);
|
||||||
|
|
||||||
static inline int pcie_port_platform_notify(struct pci_dev *port, int *mask)
|
static inline void pcie_port_platform_notify(struct pci_dev *port, int *mask)
|
||||||
{
|
{
|
||||||
return pcie_port_acpi_setup(port, mask);
|
pcie_port_acpi_setup(port, mask);
|
||||||
}
|
}
|
||||||
#else /* !CONFIG_ACPI */
|
#else /* !CONFIG_ACPI */
|
||||||
static inline int pcie_port_platform_notify(struct pci_dev *port, int *mask)
|
static inline void pcie_port_platform_notify(struct pci_dev *port, int *mask){}
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif /* !CONFIG_ACPI */
|
#endif /* !CONFIG_ACPI */
|
||||||
|
|
||||||
#endif /* _PORTDRV_H_ */
|
#endif /* _PORTDRV_H_ */
|
||||||
|
|||||||
@@ -32,32 +32,30 @@
|
|||||||
* NOTE: It turns out that we cannot do that for individual port services
|
* NOTE: It turns out that we cannot do that for individual port services
|
||||||
* separately, because that would make some systems work incorrectly.
|
* separately, because that would make some systems work incorrectly.
|
||||||
*/
|
*/
|
||||||
int pcie_port_acpi_setup(struct pci_dev *port, int *srv_mask)
|
void pcie_port_acpi_setup(struct pci_dev *port, int *srv_mask)
|
||||||
{
|
{
|
||||||
struct acpi_pci_root *root;
|
struct acpi_pci_root *root;
|
||||||
acpi_handle handle;
|
acpi_handle handle;
|
||||||
u32 flags;
|
u32 flags;
|
||||||
|
|
||||||
if (acpi_pci_disabled)
|
if (acpi_pci_disabled)
|
||||||
return 0;
|
return;
|
||||||
|
|
||||||
handle = acpi_find_root_bridge_handle(port);
|
handle = acpi_find_root_bridge_handle(port);
|
||||||
if (!handle)
|
if (!handle)
|
||||||
return -EINVAL;
|
return;
|
||||||
|
|
||||||
root = acpi_pci_find_root(handle);
|
root = acpi_pci_find_root(handle);
|
||||||
if (!root)
|
if (!root)
|
||||||
return -ENODEV;
|
return;
|
||||||
|
|
||||||
flags = root->osc_control_set;
|
flags = root->osc_control_set;
|
||||||
|
|
||||||
*srv_mask = PCIE_PORT_SERVICE_VC;
|
*srv_mask = PCIE_PORT_SERVICE_VC | PCIE_PORT_SERVICE_DPC;
|
||||||
if (flags & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL)
|
if (flags & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL)
|
||||||
*srv_mask |= PCIE_PORT_SERVICE_HP;
|
*srv_mask |= PCIE_PORT_SERVICE_HP;
|
||||||
if (flags & OSC_PCI_EXPRESS_PME_CONTROL)
|
if (flags & OSC_PCI_EXPRESS_PME_CONTROL)
|
||||||
*srv_mask |= PCIE_PORT_SERVICE_PME;
|
*srv_mask |= PCIE_PORT_SERVICE_PME;
|
||||||
if (flags & OSC_PCI_EXPRESS_AER_CONTROL)
|
if (flags & OSC_PCI_EXPRESS_AER_CONTROL)
|
||||||
*srv_mask |= PCIE_PORT_SERVICE_AER;
|
*srv_mask |= PCIE_PORT_SERVICE_AER;
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -254,39 +254,29 @@ static void cleanup_service_irqs(struct pci_dev *dev)
|
|||||||
static int get_port_device_capability(struct pci_dev *dev)
|
static int get_port_device_capability(struct pci_dev *dev)
|
||||||
{
|
{
|
||||||
int services = 0;
|
int services = 0;
|
||||||
u32 reg32;
|
|
||||||
int cap_mask = 0;
|
int cap_mask = 0;
|
||||||
int err;
|
|
||||||
|
|
||||||
if (pcie_ports_disabled)
|
if (pcie_ports_disabled)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
cap_mask = PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP
|
cap_mask = PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP
|
||||||
| PCIE_PORT_SERVICE_VC;
|
| PCIE_PORT_SERVICE_VC | PCIE_PORT_SERVICE_DPC;
|
||||||
if (pci_aer_available())
|
if (pci_aer_available())
|
||||||
cap_mask |= PCIE_PORT_SERVICE_AER;
|
cap_mask |= PCIE_PORT_SERVICE_AER;
|
||||||
|
|
||||||
if (pcie_ports_auto) {
|
if (pcie_ports_auto)
|
||||||
err = pcie_port_platform_notify(dev, &cap_mask);
|
pcie_port_platform_notify(dev, &cap_mask);
|
||||||
if (err)
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hot-Plug Capable */
|
/* Hot-Plug Capable */
|
||||||
if ((cap_mask & PCIE_PORT_SERVICE_HP) &&
|
if ((cap_mask & PCIE_PORT_SERVICE_HP) && dev->is_hotplug_bridge) {
|
||||||
pcie_caps_reg(dev) & PCI_EXP_FLAGS_SLOT) {
|
|
||||||
pcie_capability_read_dword(dev, PCI_EXP_SLTCAP, ®32);
|
|
||||||
if (reg32 & PCI_EXP_SLTCAP_HPC) {
|
|
||||||
services |= PCIE_PORT_SERVICE_HP;
|
services |= PCIE_PORT_SERVICE_HP;
|
||||||
/*
|
/*
|
||||||
* Disable hot-plug interrupts in case they have been
|
* Disable hot-plug interrupts in case they have been enabled
|
||||||
* enabled by the BIOS and the hot-plug service driver
|
* by the BIOS and the hot-plug service driver is not loaded.
|
||||||
* is not loaded.
|
|
||||||
*/
|
*/
|
||||||
pcie_capability_clear_word(dev, PCI_EXP_SLTCTL,
|
pcie_capability_clear_word(dev, PCI_EXP_SLTCTL,
|
||||||
PCI_EXP_SLTCTL_CCIE | PCI_EXP_SLTCTL_HPIE);
|
PCI_EXP_SLTCTL_CCIE | PCI_EXP_SLTCTL_HPIE);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
/* AER capable */
|
/* AER capable */
|
||||||
if ((cap_mask & PCIE_PORT_SERVICE_AER)
|
if ((cap_mask & PCIE_PORT_SERVICE_AER)
|
||||||
&& pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR)) {
|
&& pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR)) {
|
||||||
@@ -311,6 +301,8 @@ static int get_port_device_capability(struct pci_dev *dev)
|
|||||||
*/
|
*/
|
||||||
pcie_pme_interrupt_enable(dev, false);
|
pcie_pme_interrupt_enable(dev, false);
|
||||||
}
|
}
|
||||||
|
if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC))
|
||||||
|
services |= PCIE_PORT_SERVICE_DPC;
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
@@ -338,7 +330,7 @@ static int pcie_device_init(struct pci_dev *pdev, int service, int irq)
|
|||||||
device = &pcie->device;
|
device = &pcie->device;
|
||||||
device->bus = &pcie_port_bus_type;
|
device->bus = &pcie_port_bus_type;
|
||||||
device->release = release_pcie_device; /* callback to free pcie dev */
|
device->release = release_pcie_device; /* callback to free pcie dev */
|
||||||
dev_set_name(device, "%s:pcie%02x",
|
dev_set_name(device, "%s:pcie%03x",
|
||||||
pci_name(pdev),
|
pci_name(pdev),
|
||||||
get_descriptor_id(pci_pcie_type(pdev), service));
|
get_descriptor_id(pci_pcie_type(pdev), service));
|
||||||
device->parent = &pdev->dev;
|
device->parent = &pdev->dev;
|
||||||
|
|||||||
+4
-3
@@ -179,9 +179,6 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
|
|||||||
u16 orig_cmd;
|
u16 orig_cmd;
|
||||||
struct pci_bus_region region, inverted_region;
|
struct pci_bus_region region, inverted_region;
|
||||||
|
|
||||||
if (dev->non_compliant_bars)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
mask = type ? PCI_ROM_ADDRESS_MASK : ~0;
|
mask = type ? PCI_ROM_ADDRESS_MASK : ~0;
|
||||||
|
|
||||||
/* No printks while decoding is disabled! */
|
/* No printks while decoding is disabled! */
|
||||||
@@ -322,6 +319,9 @@ static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom)
|
|||||||
{
|
{
|
||||||
unsigned int pos, reg;
|
unsigned int pos, reg;
|
||||||
|
|
||||||
|
if (dev->non_compliant_bars)
|
||||||
|
return;
|
||||||
|
|
||||||
for (pos = 0; pos < howmany; pos++) {
|
for (pos = 0; pos < howmany; pos++) {
|
||||||
struct resource *res = &dev->resource[pos];
|
struct resource *res = &dev->resource[pos];
|
||||||
reg = PCI_BASE_ADDRESS_0 + (pos << 2);
|
reg = PCI_BASE_ADDRESS_0 + (pos << 2);
|
||||||
@@ -1537,6 +1537,7 @@ static void pci_release_dev(struct device *dev)
|
|||||||
pcibios_release_device(pci_dev);
|
pcibios_release_device(pci_dev);
|
||||||
pci_bus_put(pci_dev->bus);
|
pci_bus_put(pci_dev->bus);
|
||||||
kfree(pci_dev->driver_override);
|
kfree(pci_dev->driver_override);
|
||||||
|
kfree(pci_dev->dma_alias_mask);
|
||||||
kfree(pci_dev);
|
kfree(pci_dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+171
-21
@@ -3150,6 +3150,39 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_REALTEK, 0x8169,
|
|||||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MELLANOX, PCI_ANY_ID,
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MELLANOX, PCI_ANY_ID,
|
||||||
quirk_broken_intx_masking);
|
quirk_broken_intx_masking);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Intel i40e (XL710/X710) 10/20/40GbE NICs all have broken INTx masking,
|
||||||
|
* DisINTx can be set but the interrupt status bit is non-functional.
|
||||||
|
*/
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1572,
|
||||||
|
quirk_broken_intx_masking);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1574,
|
||||||
|
quirk_broken_intx_masking);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1580,
|
||||||
|
quirk_broken_intx_masking);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1581,
|
||||||
|
quirk_broken_intx_masking);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1583,
|
||||||
|
quirk_broken_intx_masking);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1584,
|
||||||
|
quirk_broken_intx_masking);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1585,
|
||||||
|
quirk_broken_intx_masking);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1586,
|
||||||
|
quirk_broken_intx_masking);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1587,
|
||||||
|
quirk_broken_intx_masking);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1588,
|
||||||
|
quirk_broken_intx_masking);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1589,
|
||||||
|
quirk_broken_intx_masking);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x37d0,
|
||||||
|
quirk_broken_intx_masking);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x37d1,
|
||||||
|
quirk_broken_intx_masking);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x37d2,
|
||||||
|
quirk_broken_intx_masking);
|
||||||
|
|
||||||
static void quirk_no_bus_reset(struct pci_dev *dev)
|
static void quirk_no_bus_reset(struct pci_dev *dev)
|
||||||
{
|
{
|
||||||
dev->dev_flags |= PCI_DEV_FLAGS_NO_BUS_RESET;
|
dev->dev_flags |= PCI_DEV_FLAGS_NO_BUS_RESET;
|
||||||
@@ -3185,6 +3218,29 @@ static void quirk_no_pm_reset(struct pci_dev *dev)
|
|||||||
DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_VENDOR_ID_ATI, PCI_ANY_ID,
|
DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_VENDOR_ID_ATI, PCI_ANY_ID,
|
||||||
PCI_CLASS_DISPLAY_VGA, 8, quirk_no_pm_reset);
|
PCI_CLASS_DISPLAY_VGA, 8, quirk_no_pm_reset);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Thunderbolt controllers with broken MSI hotplug signaling:
|
||||||
|
* Entire 1st generation (Light Ridge, Eagle Ridge, Light Peak) and part
|
||||||
|
* of the 2nd generation (Cactus Ridge 4C up to revision 1, Port Ridge).
|
||||||
|
*/
|
||||||
|
static void quirk_thunderbolt_hotplug_msi(struct pci_dev *pdev)
|
||||||
|
{
|
||||||
|
if (pdev->is_hotplug_bridge &&
|
||||||
|
(pdev->device != PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C ||
|
||||||
|
pdev->revision <= 1))
|
||||||
|
pdev->no_msi = 1;
|
||||||
|
}
|
||||||
|
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LIGHT_RIDGE,
|
||||||
|
quirk_thunderbolt_hotplug_msi);
|
||||||
|
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EAGLE_RIDGE,
|
||||||
|
quirk_thunderbolt_hotplug_msi);
|
||||||
|
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LIGHT_PEAK,
|
||||||
|
quirk_thunderbolt_hotplug_msi);
|
||||||
|
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C,
|
||||||
|
quirk_thunderbolt_hotplug_msi);
|
||||||
|
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PORT_RIDGE,
|
||||||
|
quirk_thunderbolt_hotplug_msi);
|
||||||
|
|
||||||
#ifdef CONFIG_ACPI
|
#ifdef CONFIG_ACPI
|
||||||
/*
|
/*
|
||||||
* Apple: Shutdown Cactus Ridge Thunderbolt controller.
|
* Apple: Shutdown Cactus Ridge Thunderbolt controller.
|
||||||
@@ -3232,7 +3288,8 @@ static void quirk_apple_poweroff_thunderbolt(struct pci_dev *dev)
|
|||||||
acpi_execute_simple_method(SXIO, NULL, 0);
|
acpi_execute_simple_method(SXIO, NULL, 0);
|
||||||
acpi_execute_simple_method(SXLV, NULL, 0);
|
acpi_execute_simple_method(SXLV, NULL, 0);
|
||||||
}
|
}
|
||||||
DECLARE_PCI_FIXUP_SUSPEND_LATE(PCI_VENDOR_ID_INTEL, 0x1547,
|
DECLARE_PCI_FIXUP_SUSPEND_LATE(PCI_VENDOR_ID_INTEL,
|
||||||
|
PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C,
|
||||||
quirk_apple_poweroff_thunderbolt);
|
quirk_apple_poweroff_thunderbolt);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -3266,7 +3323,9 @@ static void quirk_apple_wait_for_thunderbolt(struct pci_dev *dev)
|
|||||||
if (!nhi)
|
if (!nhi)
|
||||||
goto out;
|
goto out;
|
||||||
if (nhi->vendor != PCI_VENDOR_ID_INTEL
|
if (nhi->vendor != PCI_VENDOR_ID_INTEL
|
||||||
|| (nhi->device != 0x1547 && nhi->device != 0x156c)
|
|| (nhi->device != PCI_DEVICE_ID_INTEL_LIGHT_RIDGE &&
|
||||||
|
nhi->device != PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C &&
|
||||||
|
nhi->device != PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI)
|
||||||
|| nhi->subsystem_vendor != 0x2222
|
|| nhi->subsystem_vendor != 0x2222
|
||||||
|| nhi->subsystem_device != 0x1111)
|
|| nhi->subsystem_device != 0x1111)
|
||||||
goto out;
|
goto out;
|
||||||
@@ -3276,9 +3335,14 @@ out:
|
|||||||
pci_dev_put(nhi);
|
pci_dev_put(nhi);
|
||||||
pci_dev_put(sibling);
|
pci_dev_put(sibling);
|
||||||
}
|
}
|
||||||
DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, 0x1547,
|
DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL,
|
||||||
|
PCI_DEVICE_ID_INTEL_LIGHT_RIDGE,
|
||||||
quirk_apple_wait_for_thunderbolt);
|
quirk_apple_wait_for_thunderbolt);
|
||||||
DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, 0x156d,
|
DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL,
|
||||||
|
PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C,
|
||||||
|
quirk_apple_wait_for_thunderbolt);
|
||||||
|
DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL,
|
||||||
|
PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_BRIDGE,
|
||||||
quirk_apple_wait_for_thunderbolt);
|
quirk_apple_wait_for_thunderbolt);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -3610,10 +3674,8 @@ int pci_dev_specific_reset(struct pci_dev *dev, int probe)
|
|||||||
|
|
||||||
static void quirk_dma_func0_alias(struct pci_dev *dev)
|
static void quirk_dma_func0_alias(struct pci_dev *dev)
|
||||||
{
|
{
|
||||||
if (PCI_FUNC(dev->devfn) != 0) {
|
if (PCI_FUNC(dev->devfn) != 0)
|
||||||
dev->dma_alias_devfn = PCI_DEVFN(PCI_SLOT(dev->devfn), 0);
|
pci_add_dma_alias(dev, PCI_DEVFN(PCI_SLOT(dev->devfn), 0));
|
||||||
dev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVFN;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -3626,10 +3688,8 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_RICOH, 0xe476, quirk_dma_func0_alias);
|
|||||||
|
|
||||||
static void quirk_dma_func1_alias(struct pci_dev *dev)
|
static void quirk_dma_func1_alias(struct pci_dev *dev)
|
||||||
{
|
{
|
||||||
if (PCI_FUNC(dev->devfn) != 1) {
|
if (PCI_FUNC(dev->devfn) != 1)
|
||||||
dev->dma_alias_devfn = PCI_DEVFN(PCI_SLOT(dev->devfn), 1);
|
pci_add_dma_alias(dev, PCI_DEVFN(PCI_SLOT(dev->devfn), 1));
|
||||||
dev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVFN;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -3695,13 +3755,8 @@ static void quirk_fixed_dma_alias(struct pci_dev *dev)
|
|||||||
const struct pci_device_id *id;
|
const struct pci_device_id *id;
|
||||||
|
|
||||||
id = pci_match_id(fixed_dma_alias_tbl, dev);
|
id = pci_match_id(fixed_dma_alias_tbl, dev);
|
||||||
if (id) {
|
if (id)
|
||||||
dev->dma_alias_devfn = id->driver_data;
|
pci_add_dma_alias(dev, id->driver_data);
|
||||||
dev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVFN;
|
|
||||||
dev_info(&dev->dev, "Enabling fixed DMA alias to %02x.%d\n",
|
|
||||||
PCI_SLOT(dev->dma_alias_devfn),
|
|
||||||
PCI_FUNC(dev->dma_alias_devfn));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ADAPTEC2, 0x0285, quirk_fixed_dma_alias);
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ADAPTEC2, 0x0285, quirk_fixed_dma_alias);
|
||||||
@@ -3733,6 +3788,21 @@ DECLARE_PCI_FIXUP_HEADER(0x1283, 0x8892, quirk_use_pcie_bridge_dma_alias);
|
|||||||
/* Intel 82801, https://bugzilla.kernel.org/show_bug.cgi?id=44881#c49 */
|
/* Intel 82801, https://bugzilla.kernel.org/show_bug.cgi?id=44881#c49 */
|
||||||
DECLARE_PCI_FIXUP_HEADER(0x8086, 0x244e, quirk_use_pcie_bridge_dma_alias);
|
DECLARE_PCI_FIXUP_HEADER(0x8086, 0x244e, quirk_use_pcie_bridge_dma_alias);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MIC x200 NTB forwards PCIe traffic using multiple alien RIDs. They have to
|
||||||
|
* be added as aliases to the DMA device in order to allow buffer access
|
||||||
|
* when IOMMU is enabled. Following devfns have to match RIT-LUT table
|
||||||
|
* programmed in the EEPROM.
|
||||||
|
*/
|
||||||
|
static void quirk_mic_x200_dma_alias(struct pci_dev *pdev)
|
||||||
|
{
|
||||||
|
pci_add_dma_alias(pdev, PCI_DEVFN(0x10, 0x0));
|
||||||
|
pci_add_dma_alias(pdev, PCI_DEVFN(0x11, 0x0));
|
||||||
|
pci_add_dma_alias(pdev, PCI_DEVFN(0x12, 0x3));
|
||||||
|
}
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2260, quirk_mic_x200_dma_alias);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2264, quirk_mic_x200_dma_alias);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Intersil/Techwell TW686[4589]-based video capture cards have an empty (zero)
|
* Intersil/Techwell TW686[4589]-based video capture cards have an empty (zero)
|
||||||
* class code. Fix it.
|
* class code. Fix it.
|
||||||
@@ -3936,6 +4006,55 @@ static int pci_quirk_intel_pch_acs(struct pci_dev *dev, u16 acs_flags)
|
|||||||
return acs_flags & ~flags ? 0 : 1;
|
return acs_flags & ~flags ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sunrise Point PCH root ports implement ACS, but unfortunately as shown in
|
||||||
|
* the datasheet (Intel 100 Series Chipset Family PCH Datasheet, Vol. 2,
|
||||||
|
* 12.1.46, 12.1.47)[1] this chipset uses dwords for the ACS capability and
|
||||||
|
* control registers whereas the PCIe spec packs them into words (Rev 3.0,
|
||||||
|
* 7.16 ACS Extended Capability). The bit definitions are correct, but the
|
||||||
|
* control register is at offset 8 instead of 6 and we should probably use
|
||||||
|
* dword accesses to them. This applies to the following PCI Device IDs, as
|
||||||
|
* found in volume 1 of the datasheet[2]:
|
||||||
|
*
|
||||||
|
* 0xa110-0xa11f Sunrise Point-H PCI Express Root Port #{0-16}
|
||||||
|
* 0xa167-0xa16a Sunrise Point-H PCI Express Root Port #{17-20}
|
||||||
|
*
|
||||||
|
* N.B. This doesn't fix what lspci shows.
|
||||||
|
*
|
||||||
|
* [1] http://www.intel.com/content/www/us/en/chipsets/100-series-chipset-datasheet-vol-2.html
|
||||||
|
* [2] http://www.intel.com/content/www/us/en/chipsets/100-series-chipset-datasheet-vol-1.html
|
||||||
|
*/
|
||||||
|
static bool pci_quirk_intel_spt_pch_acs_match(struct pci_dev *dev)
|
||||||
|
{
|
||||||
|
return pci_is_pcie(dev) &&
|
||||||
|
pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT &&
|
||||||
|
((dev->device & ~0xf) == 0xa110 ||
|
||||||
|
(dev->device >= 0xa167 && dev->device <= 0xa16a));
|
||||||
|
}
|
||||||
|
|
||||||
|
#define INTEL_SPT_ACS_CTRL (PCI_ACS_CAP + 4)
|
||||||
|
|
||||||
|
static int pci_quirk_intel_spt_pch_acs(struct pci_dev *dev, u16 acs_flags)
|
||||||
|
{
|
||||||
|
int pos;
|
||||||
|
u32 cap, ctrl;
|
||||||
|
|
||||||
|
if (!pci_quirk_intel_spt_pch_acs_match(dev))
|
||||||
|
return -ENOTTY;
|
||||||
|
|
||||||
|
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS);
|
||||||
|
if (!pos)
|
||||||
|
return -ENOTTY;
|
||||||
|
|
||||||
|
/* see pci_acs_flags_enabled() */
|
||||||
|
pci_read_config_dword(dev, pos + PCI_ACS_CAP, &cap);
|
||||||
|
acs_flags &= (cap | PCI_ACS_EC);
|
||||||
|
|
||||||
|
pci_read_config_dword(dev, pos + INTEL_SPT_ACS_CTRL, &ctrl);
|
||||||
|
|
||||||
|
return acs_flags & ~ctrl ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
static int pci_quirk_mf_endpoint_acs(struct pci_dev *dev, u16 acs_flags)
|
static int pci_quirk_mf_endpoint_acs(struct pci_dev *dev, u16 acs_flags)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@@ -4024,6 +4143,7 @@ static const struct pci_dev_acs_enabled {
|
|||||||
{ PCI_VENDOR_ID_INTEL, 0x15b8, pci_quirk_mf_endpoint_acs },
|
{ PCI_VENDOR_ID_INTEL, 0x15b8, pci_quirk_mf_endpoint_acs },
|
||||||
/* Intel PCH root ports */
|
/* Intel PCH root ports */
|
||||||
{ PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_pch_acs },
|
{ PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_pch_acs },
|
||||||
|
{ PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_spt_pch_acs },
|
||||||
{ 0x19a2, 0x710, pci_quirk_mf_endpoint_acs }, /* Emulex BE3-R */
|
{ 0x19a2, 0x710, pci_quirk_mf_endpoint_acs }, /* Emulex BE3-R */
|
||||||
{ 0x10df, 0x720, pci_quirk_mf_endpoint_acs }, /* Emulex Skyhawk-R */
|
{ 0x10df, 0x720, pci_quirk_mf_endpoint_acs }, /* Emulex Skyhawk-R */
|
||||||
/* Cavium ThunderX */
|
/* Cavium ThunderX */
|
||||||
@@ -4159,16 +4279,44 @@ static int pci_quirk_enable_intel_pch_acs(struct pci_dev *dev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int pci_quirk_enable_intel_spt_pch_acs(struct pci_dev *dev)
|
||||||
|
{
|
||||||
|
int pos;
|
||||||
|
u32 cap, ctrl;
|
||||||
|
|
||||||
|
if (!pci_quirk_intel_spt_pch_acs_match(dev))
|
||||||
|
return -ENOTTY;
|
||||||
|
|
||||||
|
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS);
|
||||||
|
if (!pos)
|
||||||
|
return -ENOTTY;
|
||||||
|
|
||||||
|
pci_read_config_dword(dev, pos + PCI_ACS_CAP, &cap);
|
||||||
|
pci_read_config_dword(dev, pos + INTEL_SPT_ACS_CTRL, &ctrl);
|
||||||
|
|
||||||
|
ctrl |= (cap & PCI_ACS_SV);
|
||||||
|
ctrl |= (cap & PCI_ACS_RR);
|
||||||
|
ctrl |= (cap & PCI_ACS_CR);
|
||||||
|
ctrl |= (cap & PCI_ACS_UF);
|
||||||
|
|
||||||
|
pci_write_config_dword(dev, pos + INTEL_SPT_ACS_CTRL, ctrl);
|
||||||
|
|
||||||
|
dev_info(&dev->dev, "Intel SPT PCH root port ACS workaround enabled\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct pci_dev_enable_acs {
|
static const struct pci_dev_enable_acs {
|
||||||
u16 vendor;
|
u16 vendor;
|
||||||
u16 device;
|
u16 device;
|
||||||
int (*enable_acs)(struct pci_dev *dev);
|
int (*enable_acs)(struct pci_dev *dev);
|
||||||
} pci_dev_enable_acs[] = {
|
} pci_dev_enable_acs[] = {
|
||||||
{ PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_enable_intel_pch_acs },
|
{ PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_enable_intel_pch_acs },
|
||||||
|
{ PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_enable_intel_spt_pch_acs },
|
||||||
{ 0 }
|
{ 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
void pci_dev_specific_enable_acs(struct pci_dev *dev)
|
int pci_dev_specific_enable_acs(struct pci_dev *dev)
|
||||||
{
|
{
|
||||||
const struct pci_dev_enable_acs *i;
|
const struct pci_dev_enable_acs *i;
|
||||||
int ret;
|
int ret;
|
||||||
@@ -4180,9 +4328,11 @@ void pci_dev_specific_enable_acs(struct pci_dev *dev)
|
|||||||
i->device == (u16)PCI_ANY_ID)) {
|
i->device == (u16)PCI_ANY_ID)) {
|
||||||
ret = i->enable_acs(dev);
|
ret = i->enable_acs(dev);
|
||||||
if (ret >= 0)
|
if (ret >= 0)
|
||||||
return;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return -ENOTTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -40,12 +40,16 @@ int pci_for_each_dma_alias(struct pci_dev *pdev,
|
|||||||
* If the device is broken and uses an alias requester ID for
|
* If the device is broken and uses an alias requester ID for
|
||||||
* DMA, iterate over that too.
|
* DMA, iterate over that too.
|
||||||
*/
|
*/
|
||||||
if (unlikely(pdev->dev_flags & PCI_DEV_FLAGS_DMA_ALIAS_DEVFN)) {
|
if (unlikely(pdev->dma_alias_mask)) {
|
||||||
ret = fn(pdev, PCI_DEVID(pdev->bus->number,
|
u8 devfn;
|
||||||
pdev->dma_alias_devfn), data);
|
|
||||||
|
for_each_set_bit(devfn, pdev->dma_alias_mask, U8_MAX) {
|
||||||
|
ret = fn(pdev, PCI_DEVID(pdev->bus->number, devfn),
|
||||||
|
data);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (bus = pdev->bus; !pci_is_root_bus(bus); bus = bus->parent) {
|
for (bus = pdev->bus; !pci_is_root_bus(bus); bus = bus->parent) {
|
||||||
struct pci_dev *tmp;
|
struct pci_dev *tmp;
|
||||||
|
|||||||
@@ -249,7 +249,7 @@ static void tb_cfg_print_error(struct tb_ctl *ctl,
|
|||||||
* cfg_read/cfg_write.
|
* cfg_read/cfg_write.
|
||||||
*/
|
*/
|
||||||
tb_ctl_WARN(ctl,
|
tb_ctl_WARN(ctl,
|
||||||
"CFG_ERROR(%llx:%x): Invalid config space of offset\n",
|
"CFG_ERROR(%llx:%x): Invalid config space or offset\n",
|
||||||
res->response_route, res->response_port);
|
res->response_route, res->response_port);
|
||||||
return;
|
return;
|
||||||
case TB_CFG_ERROR_NO_SUCH_PORT:
|
case TB_CFG_ERROR_NO_SUCH_PORT:
|
||||||
|
|||||||
@@ -221,7 +221,7 @@ struct tb_drom_entry_port {
|
|||||||
u8 micro1:4;
|
u8 micro1:4;
|
||||||
u8 micro3;
|
u8 micro3;
|
||||||
|
|
||||||
/* BYTES 5-6, TODO: verify (find hardware that has these set) */
|
/* BYTES 6-7, TODO: verify (find hardware that has these set) */
|
||||||
u8 peer_port_rid:4;
|
u8 peer_port_rid:4;
|
||||||
u8 unknown3:3;
|
u8 unknown3:3;
|
||||||
bool has_peer_port:1;
|
bool has_peer_port:1;
|
||||||
@@ -388,6 +388,11 @@ int tb_drom_read(struct tb_switch *sw)
|
|||||||
sw->ports[4].link_nr = 1;
|
sw->ports[4].link_nr = 1;
|
||||||
sw->ports[3].dual_link_port = &sw->ports[4];
|
sw->ports[3].dual_link_port = &sw->ports[4];
|
||||||
sw->ports[4].dual_link_port = &sw->ports[3];
|
sw->ports[4].dual_link_port = &sw->ports[3];
|
||||||
|
|
||||||
|
/* Port 5 is inaccessible on this gen 1 controller */
|
||||||
|
if (sw->config.device_id == PCI_DEVICE_ID_INTEL_LIGHT_RIDGE)
|
||||||
|
sw->ports[5].disabled = true;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -444,6 +449,7 @@ int tb_drom_read(struct tb_switch *sw)
|
|||||||
return tb_drom_parse_entries(sw);
|
return tb_drom_parse_entries(sw);
|
||||||
err:
|
err:
|
||||||
kfree(sw->drom);
|
kfree(sw->drom);
|
||||||
|
sw->drom = NULL;
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,8 @@ static int ring_interrupt_index(struct tb_ring *ring)
|
|||||||
*/
|
*/
|
||||||
static void ring_interrupt_active(struct tb_ring *ring, bool active)
|
static void ring_interrupt_active(struct tb_ring *ring, bool active)
|
||||||
{
|
{
|
||||||
int reg = REG_RING_INTERRUPT_BASE + ring_interrupt_index(ring) / 32;
|
int reg = REG_RING_INTERRUPT_BASE +
|
||||||
|
ring_interrupt_index(ring) / 32 * 4;
|
||||||
int bit = ring_interrupt_index(ring) & 31;
|
int bit = ring_interrupt_index(ring) & 31;
|
||||||
int mask = 1 << bit;
|
int mask = 1 << bit;
|
||||||
u32 old, new;
|
u32 old, new;
|
||||||
@@ -564,7 +565,7 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|||||||
/* cannot fail - table is allocated bin pcim_iomap_regions */
|
/* cannot fail - table is allocated bin pcim_iomap_regions */
|
||||||
nhi->iobase = pcim_iomap_table(pdev)[0];
|
nhi->iobase = pcim_iomap_table(pdev)[0];
|
||||||
nhi->hop_count = ioread32(nhi->iobase + REG_HOP_COUNT) & 0x3ff;
|
nhi->hop_count = ioread32(nhi->iobase + REG_HOP_COUNT) & 0x3ff;
|
||||||
if (nhi->hop_count != 12)
|
if (nhi->hop_count != 12 && nhi->hop_count != 32)
|
||||||
dev_warn(&pdev->dev, "unexpected hop count: %d\n",
|
dev_warn(&pdev->dev, "unexpected hop count: %d\n",
|
||||||
nhi->hop_count);
|
nhi->hop_count);
|
||||||
INIT_WORK(&nhi->interrupt_work, nhi_interrupt_work);
|
INIT_WORK(&nhi->interrupt_work, nhi_interrupt_work);
|
||||||
@@ -633,16 +634,24 @@ static const struct dev_pm_ops nhi_pm_ops = {
|
|||||||
static struct pci_device_id nhi_ids[] = {
|
static struct pci_device_id nhi_ids[] = {
|
||||||
/*
|
/*
|
||||||
* We have to specify class, the TB bridges use the same device and
|
* We have to specify class, the TB bridges use the same device and
|
||||||
* vendor (sub)id.
|
* vendor (sub)id on gen 1 and gen 2 controllers.
|
||||||
*/
|
*/
|
||||||
{
|
{
|
||||||
.class = PCI_CLASS_SYSTEM_OTHER << 8, .class_mask = ~0,
|
.class = PCI_CLASS_SYSTEM_OTHER << 8, .class_mask = ~0,
|
||||||
.vendor = PCI_VENDOR_ID_INTEL, .device = 0x1547,
|
.vendor = PCI_VENDOR_ID_INTEL,
|
||||||
|
.device = PCI_DEVICE_ID_INTEL_LIGHT_RIDGE,
|
||||||
.subvendor = 0x2222, .subdevice = 0x1111,
|
.subvendor = 0x2222, .subdevice = 0x1111,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.class = PCI_CLASS_SYSTEM_OTHER << 8, .class_mask = ~0,
|
.class = PCI_CLASS_SYSTEM_OTHER << 8, .class_mask = ~0,
|
||||||
.vendor = PCI_VENDOR_ID_INTEL, .device = 0x156c,
|
.vendor = PCI_VENDOR_ID_INTEL,
|
||||||
|
.device = PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C,
|
||||||
|
.subvendor = 0x2222, .subdevice = 0x1111,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.class = PCI_CLASS_SYSTEM_OTHER << 8, .class_mask = ~0,
|
||||||
|
.vendor = PCI_VENDOR_ID_INTEL,
|
||||||
|
.device = PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI,
|
||||||
.subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID,
|
.subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID,
|
||||||
},
|
},
|
||||||
{ 0,}
|
{ 0,}
|
||||||
|
|||||||
@@ -293,9 +293,9 @@ static int tb_plug_events_active(struct tb_switch *sw, bool active)
|
|||||||
if (active) {
|
if (active) {
|
||||||
data = data & 0xFFFFFF83;
|
data = data & 0xFFFFFF83;
|
||||||
switch (sw->config.device_id) {
|
switch (sw->config.device_id) {
|
||||||
case 0x1513:
|
case PCI_DEVICE_ID_INTEL_LIGHT_RIDGE:
|
||||||
case 0x151a:
|
case PCI_DEVICE_ID_INTEL_EAGLE_RIDGE:
|
||||||
case 0x1549:
|
case PCI_DEVICE_ID_INTEL_PORT_RIDGE:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
data |= 4;
|
data |= 4;
|
||||||
@@ -350,7 +350,7 @@ struct tb_switch *tb_switch_alloc(struct tb *tb, u64 route)
|
|||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
sw->tb = tb;
|
sw->tb = tb;
|
||||||
if (tb_cfg_read(tb->ctl, &sw->config, route, 0, 2, 0, 5))
|
if (tb_cfg_read(tb->ctl, &sw->config, route, 0, TB_CFG_SWITCH, 0, 5))
|
||||||
goto err;
|
goto err;
|
||||||
tb_info(tb,
|
tb_info(tb,
|
||||||
"initializing Switch at %#llx (depth: %d, up port: %d)\n",
|
"initializing Switch at %#llx (depth: %d, up port: %d)\n",
|
||||||
@@ -370,7 +370,9 @@ struct tb_switch *tb_switch_alloc(struct tb *tb, u64 route)
|
|||||||
tb_sw_warn(sw, "unknown switch vendor id %#x\n",
|
tb_sw_warn(sw, "unknown switch vendor id %#x\n",
|
||||||
sw->config.vendor_id);
|
sw->config.vendor_id);
|
||||||
|
|
||||||
if (sw->config.device_id != 0x1547 && sw->config.device_id != 0x1549)
|
if (sw->config.device_id != PCI_DEVICE_ID_INTEL_LIGHT_RIDGE &&
|
||||||
|
sw->config.device_id != PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C &&
|
||||||
|
sw->config.device_id != PCI_DEVICE_ID_INTEL_PORT_RIDGE)
|
||||||
tb_sw_warn(sw, "unsupported switch device id %#x\n",
|
tb_sw_warn(sw, "unsupported switch device id %#x\n",
|
||||||
sw->config.device_id);
|
sw->config.device_id);
|
||||||
|
|
||||||
@@ -425,9 +427,9 @@ err:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* tb_sw_set_unpplugged() - set is_unplugged on switch and downstream switches
|
* tb_sw_set_unplugged() - set is_unplugged on switch and downstream switches
|
||||||
*/
|
*/
|
||||||
void tb_sw_set_unpplugged(struct tb_switch *sw)
|
void tb_sw_set_unplugged(struct tb_switch *sw)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
if (sw == sw->tb->root_switch) {
|
if (sw == sw->tb->root_switch) {
|
||||||
@@ -441,7 +443,7 @@ void tb_sw_set_unpplugged(struct tb_switch *sw)
|
|||||||
sw->is_unplugged = true;
|
sw->is_unplugged = true;
|
||||||
for (i = 0; i <= sw->config.max_port_number; i++) {
|
for (i = 0; i <= sw->config.max_port_number; i++) {
|
||||||
if (!tb_is_upstream_port(&sw->ports[i]) && sw->ports[i].remote)
|
if (!tb_is_upstream_port(&sw->ports[i]) && sw->ports[i].remote)
|
||||||
tb_sw_set_unpplugged(sw->ports[i].remote->sw);
|
tb_sw_set_unplugged(sw->ports[i].remote->sw);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -483,7 +485,7 @@ int tb_switch_resume(struct tb_switch *sw)
|
|||||||
|| tb_switch_resume(port->remote->sw)) {
|
|| tb_switch_resume(port->remote->sw)) {
|
||||||
tb_port_warn(port,
|
tb_port_warn(port,
|
||||||
"lost during suspend, disconnecting\n");
|
"lost during suspend, disconnecting\n");
|
||||||
tb_sw_set_unpplugged(port->remote->sw);
|
tb_sw_set_unplugged(port->remote->sw);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -246,7 +246,7 @@ static void tb_handle_hotplug(struct work_struct *work)
|
|||||||
if (ev->unplug) {
|
if (ev->unplug) {
|
||||||
if (port->remote) {
|
if (port->remote) {
|
||||||
tb_port_info(port, "unplugged\n");
|
tb_port_info(port, "unplugged\n");
|
||||||
tb_sw_set_unpplugged(port->remote->sw);
|
tb_sw_set_unplugged(port->remote->sw);
|
||||||
tb_free_invalid_tunnels(tb);
|
tb_free_invalid_tunnels(tb);
|
||||||
tb_switch_free(port->remote->sw);
|
tb_switch_free(port->remote->sw);
|
||||||
port->remote = NULL;
|
port->remote = NULL;
|
||||||
|
|||||||
@@ -226,7 +226,7 @@ void tb_switch_free(struct tb_switch *sw);
|
|||||||
void tb_switch_suspend(struct tb_switch *sw);
|
void tb_switch_suspend(struct tb_switch *sw);
|
||||||
int tb_switch_resume(struct tb_switch *sw);
|
int tb_switch_resume(struct tb_switch *sw);
|
||||||
int tb_switch_reset(struct tb *tb, u64 route);
|
int tb_switch_reset(struct tb *tb, u64 route);
|
||||||
void tb_sw_set_unpplugged(struct tb_switch *sw);
|
void tb_sw_set_unplugged(struct tb_switch *sw);
|
||||||
struct tb_switch *get_switch_at_route(struct tb_switch *sw, u64 route);
|
struct tb_switch *get_switch_at_route(struct tb_switch *sw, u64 route);
|
||||||
|
|
||||||
int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged);
|
int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged);
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ enum tb_cap {
|
|||||||
TB_CAP_I2C = 0x0005,
|
TB_CAP_I2C = 0x0005,
|
||||||
TB_CAP_PLUG_EVENTS = 0x0105, /* also EEPROM */
|
TB_CAP_PLUG_EVENTS = 0x0105, /* also EEPROM */
|
||||||
TB_CAP_TIME2 = 0x0305,
|
TB_CAP_TIME2 = 0x0305,
|
||||||
TB_CAL_IECS = 0x0405,
|
TB_CAP_IECS = 0x0405,
|
||||||
TB_CAP_LINK_CONTROLLER = 0x0605, /* also IECS */
|
TB_CAP_LINK_CONTROLLER = 0x0605, /* also IECS */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,9 @@ struct resource {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* IO resources have these defined flags.
|
* IO resources have these defined flags.
|
||||||
|
*
|
||||||
|
* PCI devices expose these flags to userspace in the "resource" sysfs file,
|
||||||
|
* so don't move them.
|
||||||
*/
|
*/
|
||||||
#define IORESOURCE_BITS 0x000000ff /* Bus-specific bits */
|
#define IORESOURCE_BITS 0x000000ff /* Bus-specific bits */
|
||||||
|
|
||||||
@@ -110,6 +113,7 @@ struct resource {
|
|||||||
|
|
||||||
/* PCI control bits. Shares IORESOURCE_BITS with above PCI ROM. */
|
/* PCI control bits. Shares IORESOURCE_BITS with above PCI ROM. */
|
||||||
#define IORESOURCE_PCI_FIXED (1<<4) /* Do not move resource */
|
#define IORESOURCE_PCI_FIXED (1<<4) /* Do not move resource */
|
||||||
|
#define IORESOURCE_PCI_EA_BEI (1<<5) /* BAR Equivalent Indicator */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* I/O Resource Descriptors
|
* I/O Resource Descriptors
|
||||||
|
|||||||
@@ -95,6 +95,7 @@
|
|||||||
#define IMX6Q_GPR0_DMAREQ_MUX_SEL0_IOMUX BIT(0)
|
#define IMX6Q_GPR0_DMAREQ_MUX_SEL0_IOMUX BIT(0)
|
||||||
|
|
||||||
#define IMX6Q_GPR1_PCIE_REQ_MASK (0x3 << 30)
|
#define IMX6Q_GPR1_PCIE_REQ_MASK (0x3 << 30)
|
||||||
|
#define IMX6Q_GPR1_PCIE_SW_RST BIT(29)
|
||||||
#define IMX6Q_GPR1_PCIE_EXIT_L1 BIT(28)
|
#define IMX6Q_GPR1_PCIE_EXIT_L1 BIT(28)
|
||||||
#define IMX6Q_GPR1_PCIE_RDY_L23 BIT(27)
|
#define IMX6Q_GPR1_PCIE_RDY_L23 BIT(27)
|
||||||
#define IMX6Q_GPR1_PCIE_ENTER_L1 BIT(26)
|
#define IMX6Q_GPR1_PCIE_ENTER_L1 BIT(26)
|
||||||
|
|||||||
@@ -47,10 +47,6 @@ void __iomem *of_io_request_and_map(struct device_node *device,
|
|||||||
extern const __be32 *of_get_address(struct device_node *dev, int index,
|
extern const __be32 *of_get_address(struct device_node *dev, int index,
|
||||||
u64 *size, unsigned int *flags);
|
u64 *size, unsigned int *flags);
|
||||||
|
|
||||||
extern int pci_register_io_range(phys_addr_t addr, resource_size_t size);
|
|
||||||
extern unsigned long pci_address_to_pio(phys_addr_t addr);
|
|
||||||
extern phys_addr_t pci_pio_to_address(unsigned long pio);
|
|
||||||
|
|
||||||
extern int of_pci_range_parser_init(struct of_pci_range_parser *parser,
|
extern int of_pci_range_parser_init(struct of_pci_range_parser *parser,
|
||||||
struct device_node *node);
|
struct device_node *node);
|
||||||
extern struct of_pci_range *of_pci_range_parser_one(
|
extern struct of_pci_range *of_pci_range_parser_one(
|
||||||
@@ -86,11 +82,6 @@ static inline const __be32 *of_get_address(struct device_node *dev, int index,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline phys_addr_t pci_pio_to_address(unsigned long pio)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int of_pci_range_parser_init(struct of_pci_range_parser *parser,
|
static inline int of_pci_range_parser_init(struct of_pci_range_parser *parser,
|
||||||
struct device_node *node)
|
struct device_node *node)
|
||||||
{
|
{
|
||||||
|
|||||||
+13
-5
@@ -166,8 +166,6 @@ enum pci_dev_flags {
|
|||||||
PCI_DEV_FLAGS_ASSIGNED = (__force pci_dev_flags_t) (1 << 2),
|
PCI_DEV_FLAGS_ASSIGNED = (__force pci_dev_flags_t) (1 << 2),
|
||||||
/* Flag for quirk use to store if quirk-specific ACS is enabled */
|
/* Flag for quirk use to store if quirk-specific ACS is enabled */
|
||||||
PCI_DEV_FLAGS_ACS_ENABLED_QUIRK = (__force pci_dev_flags_t) (1 << 3),
|
PCI_DEV_FLAGS_ACS_ENABLED_QUIRK = (__force pci_dev_flags_t) (1 << 3),
|
||||||
/* Flag to indicate the device uses dma_alias_devfn */
|
|
||||||
PCI_DEV_FLAGS_DMA_ALIAS_DEVFN = (__force pci_dev_flags_t) (1 << 4),
|
|
||||||
/* Use a PCIe-to-PCI bridge alias even if !pci_is_pcie */
|
/* Use a PCIe-to-PCI bridge alias even if !pci_is_pcie */
|
||||||
PCI_DEV_FLAG_PCIE_BRIDGE_ALIAS = (__force pci_dev_flags_t) (1 << 5),
|
PCI_DEV_FLAG_PCIE_BRIDGE_ALIAS = (__force pci_dev_flags_t) (1 << 5),
|
||||||
/* Do not use bus resets for device */
|
/* Do not use bus resets for device */
|
||||||
@@ -273,7 +271,7 @@ struct pci_dev {
|
|||||||
u8 rom_base_reg; /* which config register controls the ROM */
|
u8 rom_base_reg; /* which config register controls the ROM */
|
||||||
u8 pin; /* which interrupt pin this device uses */
|
u8 pin; /* which interrupt pin this device uses */
|
||||||
u16 pcie_flags_reg; /* cached PCIe Capabilities Register */
|
u16 pcie_flags_reg; /* cached PCIe Capabilities Register */
|
||||||
u8 dma_alias_devfn;/* devfn of DMA alias, if any */
|
unsigned long *dma_alias_mask;/* mask of enabled devfn aliases */
|
||||||
|
|
||||||
struct pci_driver *driver; /* which driver has allocated this device */
|
struct pci_driver *driver; /* which driver has allocated this device */
|
||||||
u64 dma_mask; /* Mask of the bits of bus address this
|
u64 dma_mask; /* Mask of the bits of bus address this
|
||||||
@@ -1165,6 +1163,9 @@ int __must_check pci_bus_alloc_resource(struct pci_bus *bus,
|
|||||||
void *alignf_data);
|
void *alignf_data);
|
||||||
|
|
||||||
|
|
||||||
|
int pci_register_io_range(phys_addr_t addr, resource_size_t size);
|
||||||
|
unsigned long pci_address_to_pio(phys_addr_t addr);
|
||||||
|
phys_addr_t pci_pio_to_address(unsigned long pio);
|
||||||
int pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr);
|
int pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr);
|
||||||
|
|
||||||
static inline pci_bus_addr_t pci_bus_address(struct pci_dev *pdev, int bar)
|
static inline pci_bus_addr_t pci_bus_address(struct pci_dev *pdev, int bar)
|
||||||
@@ -1481,6 +1482,8 @@ static inline int pci_request_regions(struct pci_dev *dev, const char *res_name)
|
|||||||
{ return -EIO; }
|
{ return -EIO; }
|
||||||
static inline void pci_release_regions(struct pci_dev *dev) { }
|
static inline void pci_release_regions(struct pci_dev *dev) { }
|
||||||
|
|
||||||
|
static inline unsigned long pci_address_to_pio(phys_addr_t addr) { return -1; }
|
||||||
|
|
||||||
static inline void pci_block_cfg_access(struct pci_dev *dev) { }
|
static inline void pci_block_cfg_access(struct pci_dev *dev) { }
|
||||||
static inline int pci_block_cfg_access_in_atomic(struct pci_dev *dev)
|
static inline int pci_block_cfg_access_in_atomic(struct pci_dev *dev)
|
||||||
{ return 0; }
|
{ return 0; }
|
||||||
@@ -1664,7 +1667,7 @@ enum pci_fixup_pass {
|
|||||||
#ifdef CONFIG_PCI_QUIRKS
|
#ifdef CONFIG_PCI_QUIRKS
|
||||||
void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev);
|
void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev);
|
||||||
int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags);
|
int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags);
|
||||||
void pci_dev_specific_enable_acs(struct pci_dev *dev);
|
int pci_dev_specific_enable_acs(struct pci_dev *dev);
|
||||||
#else
|
#else
|
||||||
static inline void pci_fixup_device(enum pci_fixup_pass pass,
|
static inline void pci_fixup_device(enum pci_fixup_pass pass,
|
||||||
struct pci_dev *dev) { }
|
struct pci_dev *dev) { }
|
||||||
@@ -1673,7 +1676,10 @@ static inline int pci_dev_specific_acs_enabled(struct pci_dev *dev,
|
|||||||
{
|
{
|
||||||
return -ENOTTY;
|
return -ENOTTY;
|
||||||
}
|
}
|
||||||
static inline void pci_dev_specific_enable_acs(struct pci_dev *dev) { }
|
static inline int pci_dev_specific_enable_acs(struct pci_dev *dev)
|
||||||
|
{
|
||||||
|
return -ENOTTY;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void __iomem *pcim_iomap(struct pci_dev *pdev, int bar, unsigned long maxlen);
|
void __iomem *pcim_iomap(struct pci_dev *pdev, int bar, unsigned long maxlen);
|
||||||
@@ -1989,6 +1995,8 @@ static inline struct eeh_dev *pci_dev_to_eeh_dev(struct pci_dev *pdev)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void pci_add_dma_alias(struct pci_dev *dev, u8 devfn);
|
||||||
|
bool pci_devs_are_dma_aliases(struct pci_dev *dev1, struct pci_dev *dev2);
|
||||||
int pci_for_each_dma_alias(struct pci_dev *pdev,
|
int pci_for_each_dma_alias(struct pci_dev *pdev,
|
||||||
int (*fn)(struct pci_dev *pdev,
|
int (*fn)(struct pci_dev *pdev,
|
||||||
u16 alias, void *data), void *data);
|
u16 alias, void *data), void *data);
|
||||||
|
|||||||
@@ -2604,6 +2604,24 @@
|
|||||||
#define PCI_DEVICE_ID_INTEL_82441 0x1237
|
#define PCI_DEVICE_ID_INTEL_82441 0x1237
|
||||||
#define PCI_DEVICE_ID_INTEL_82380FB 0x124b
|
#define PCI_DEVICE_ID_INTEL_82380FB 0x124b
|
||||||
#define PCI_DEVICE_ID_INTEL_82439 0x1250
|
#define PCI_DEVICE_ID_INTEL_82439 0x1250
|
||||||
|
#define PCI_DEVICE_ID_INTEL_LIGHT_RIDGE 0x1513 /* Tbt 1 Gen 1 */
|
||||||
|
#define PCI_DEVICE_ID_INTEL_EAGLE_RIDGE 0x151a
|
||||||
|
#define PCI_DEVICE_ID_INTEL_LIGHT_PEAK 0x151b
|
||||||
|
#define PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C 0x1547 /* Tbt 1 Gen 2 */
|
||||||
|
#define PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_2C 0x1548
|
||||||
|
#define PCI_DEVICE_ID_INTEL_PORT_RIDGE 0x1549
|
||||||
|
#define PCI_DEVICE_ID_INTEL_REDWOOD_RIDGE_2C_NHI 0x1566 /* Tbt 1 Gen 3 */
|
||||||
|
#define PCI_DEVICE_ID_INTEL_REDWOOD_RIDGE_2C_BRIDGE 0x1567
|
||||||
|
#define PCI_DEVICE_ID_INTEL_REDWOOD_RIDGE_4C_NHI 0x1568
|
||||||
|
#define PCI_DEVICE_ID_INTEL_REDWOOD_RIDGE_4C_BRIDGE 0x1569
|
||||||
|
#define PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_NHI 0x156a /* Thunderbolt 2 */
|
||||||
|
#define PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_BRIDGE 0x156b
|
||||||
|
#define PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI 0x156c
|
||||||
|
#define PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_BRIDGE 0x156d
|
||||||
|
#define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_NHI 0x1575 /* Thunderbolt 3 */
|
||||||
|
#define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_BRIDGE 0x1576
|
||||||
|
#define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_NHI 0x1577
|
||||||
|
#define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_BRIDGE 0x1578
|
||||||
#define PCI_DEVICE_ID_INTEL_80960_RP 0x1960
|
#define PCI_DEVICE_ID_INTEL_80960_RP 0x1960
|
||||||
#define PCI_DEVICE_ID_INTEL_82840_HB 0x1a21
|
#define PCI_DEVICE_ID_INTEL_82840_HB 0x1a21
|
||||||
#define PCI_DEVICE_ID_INTEL_82845_HB 0x1a30
|
#define PCI_DEVICE_ID_INTEL_82845_HB 0x1a30
|
||||||
|
|||||||
@@ -21,6 +21,8 @@
|
|||||||
#define PCIE_PORT_SERVICE_HP (1 << PCIE_PORT_SERVICE_HP_SHIFT)
|
#define PCIE_PORT_SERVICE_HP (1 << PCIE_PORT_SERVICE_HP_SHIFT)
|
||||||
#define PCIE_PORT_SERVICE_VC_SHIFT 3 /* Virtual Channel */
|
#define PCIE_PORT_SERVICE_VC_SHIFT 3 /* Virtual Channel */
|
||||||
#define PCIE_PORT_SERVICE_VC (1 << PCIE_PORT_SERVICE_VC_SHIFT)
|
#define PCIE_PORT_SERVICE_VC (1 << PCIE_PORT_SERVICE_VC_SHIFT)
|
||||||
|
#define PCIE_PORT_SERVICE_DPC_SHIFT 4 /* Downstream Port Containment */
|
||||||
|
#define PCIE_PORT_SERVICE_DPC (1 << PCIE_PORT_SERVICE_DPC_SHIFT)
|
||||||
|
|
||||||
struct pcie_device {
|
struct pcie_device {
|
||||||
int irq; /* Service IRQ/MSI/MSI-X Vector */
|
int irq; /* Service IRQ/MSI/MSI-X Vector */
|
||||||
|
|||||||
@@ -670,7 +670,8 @@
|
|||||||
#define PCI_EXT_CAP_ID_SECPCI 0x19 /* Secondary PCIe Capability */
|
#define PCI_EXT_CAP_ID_SECPCI 0x19 /* Secondary PCIe Capability */
|
||||||
#define PCI_EXT_CAP_ID_PMUX 0x1A /* Protocol Multiplexing */
|
#define PCI_EXT_CAP_ID_PMUX 0x1A /* Protocol Multiplexing */
|
||||||
#define PCI_EXT_CAP_ID_PASID 0x1B /* Process Address Space ID */
|
#define PCI_EXT_CAP_ID_PASID 0x1B /* Process Address Space ID */
|
||||||
#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_PASID
|
#define PCI_EXT_CAP_ID_DPC 0x1D /* Downstream Port Containment */
|
||||||
|
#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_DPC
|
||||||
|
|
||||||
#define PCI_EXT_CAP_DSN_SIZEOF 12
|
#define PCI_EXT_CAP_DSN_SIZEOF 12
|
||||||
#define PCI_EXT_CAP_MCAST_ENDPOINT_SIZEOF 40
|
#define PCI_EXT_CAP_MCAST_ENDPOINT_SIZEOF 40
|
||||||
@@ -946,4 +947,21 @@
|
|||||||
#define PCI_TPH_CAP_ST_SHIFT 16 /* st table shift */
|
#define PCI_TPH_CAP_ST_SHIFT 16 /* st table shift */
|
||||||
#define PCI_TPH_BASE_SIZEOF 12 /* size with no st table */
|
#define PCI_TPH_BASE_SIZEOF 12 /* size with no st table */
|
||||||
|
|
||||||
|
/* Downstream Port Containment */
|
||||||
|
#define PCI_EXP_DPC_CAP 4 /* DPC Capability */
|
||||||
|
#define PCI_EXP_DPC_CAP_RP_EXT 0x20 /* Root Port Extensions for DPC */
|
||||||
|
#define PCI_EXP_DPC_CAP_POISONED_TLP 0x40 /* Poisoned TLP Egress Blocking Supported */
|
||||||
|
#define PCI_EXP_DPC_CAP_SW_TRIGGER 0x80 /* Software Triggering Supported */
|
||||||
|
#define PCI_EXP_DPC_CAP_DL_ACTIVE 0x1000 /* ERR_COR signal on DL_Active supported */
|
||||||
|
|
||||||
|
#define PCI_EXP_DPC_CTL 6 /* DPC control */
|
||||||
|
#define PCI_EXP_DPC_CTL_EN_NONFATAL 0x02 /* Enable trigger on ERR_NONFATAL message */
|
||||||
|
#define PCI_EXP_DPC_CTL_INT_EN 0x08 /* DPC Interrupt Enable */
|
||||||
|
|
||||||
|
#define PCI_EXP_DPC_STATUS 8 /* DPC Status */
|
||||||
|
#define PCI_EXP_DPC_STATUS_TRIGGER 0x01 /* Trigger Status */
|
||||||
|
#define PCI_EXP_DPC_STATUS_INTERRUPT 0x08 /* Interrupt Status */
|
||||||
|
|
||||||
|
#define PCI_EXP_DPC_SOURCE_ID 10 /* DPC Source Identifier */
|
||||||
|
|
||||||
#endif /* LINUX_PCI_REGS_H */
|
#endif /* LINUX_PCI_REGS_H */
|
||||||
|
|||||||
Reference in New Issue
Block a user