Commit 7afd16f8 authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents a37571a2 e257ef55
...@@ -4,8 +4,8 @@ This PCIe host controller is based on the Synopsis Designware PCIe IP ...@@ -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: ...@@ -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:
......
* 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:- ...@@ -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, ...@@ -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 @@ &ldb { ...@@ -219,8 +219,9 @@ &ldb {
}; };
&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 @@ ipu2: ipu@02800000 { ...@@ -82,5 +82,8 @@ ipu2: ipu@02800000 {
"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) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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);
......
...@@ -4,6 +4,7 @@ ...@@ -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, ...@@ -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 ...@@ -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 ...@@ -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
......
/*
* 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,
}
};
/*
* 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 ...@@ -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 ...@@ -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 ...@@ -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) ...@@ -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 @@ ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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;
......
/*
* 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 @@ ...@@ -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[] = { ...@@ -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)
return -ENOMEM;
of_id = of_match_node(gen_pci_of_match, dev->of_node); of_id = of_match_node(gen_pci_of_match, pdev->dev.of_node);
pci->cfg.ops = (struct gen_pci_cfg_bus_ops *)of_id->data; ops = (struct pci_ecam_ops *)of_id->data;
return pci_host_common_probe(pdev, pci); return pci_host_common_probe(pdev, ops);
} }
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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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. */
......
This diff is collapsed.
...@@ -14,6 +14,7 @@ ...@@ -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 @@ ...@@ -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) ...@@ -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 @@ ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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 = { ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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 { ...@@ -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); ...@@ -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) ...@@ -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) ...@@ -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[] = { ...@@ -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 @@ ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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 @@ ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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 = {
......
/*
* 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) ...@@ -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) ...@@ -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 = { ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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);
} }
......
...@@ -2228,7 +2228,7 @@ void pci_pm_init(struct pci_dev *dev) ...@@ -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 @@ static int pci_ea_read(struct pci_dev *dev, int offset) ...@@ -2389,7 +2389,7 @@ static int pci_ea_read(struct pci_dev *dev, int offset)
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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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 ...@@ -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 ...@@ -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
/*
* 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 @@ ...@@ -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) {} ...@@ -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 @@ ...@@ -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,38 +254,28 @@ static void cleanup_service_irqs(struct pci_dev *dev) ...@@ -254,38 +254,28 @@ 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) { services |= PCIE_PORT_SERVICE_HP;
pcie_capability_read_dword(dev, PCI_EXP_SLTCAP, &reg32); /*
if (reg32 & PCI_EXP_SLTCAP_HPC) { * Disable hot-plug interrupts in case they have been enabled
services |= PCIE_PORT_SERVICE_HP; * by the BIOS and the hot-plug service driver is not loaded.
/* */
* Disable hot-plug interrupts in case they have been pcie_capability_clear_word(dev, PCI_EXP_SLTCTL,
* enabled by the BIOS and the hot-plug service driver PCI_EXP_SLTCTL_CCIE | PCI_EXP_SLTCTL_HPIE);
* is not loaded.
*/
pcie_capability_clear_word(dev, PCI_EXP_SLTCTL,
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)
...@@ -311,6 +301,8 @@ static int get_port_device_capability(struct pci_dev *dev) ...@@ -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) ...@@ -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;
......
...@@ -179,9 +179,6 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, ...@@ -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) ...@@ -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) ...@@ -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);
} }
......
This diff is collapsed.
...@@ -40,11 +40,15 @@ int pci_for_each_dma_alias(struct pci_dev *pdev, ...@@ -40,11 +40,15 @@ 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);
if (ret) for_each_set_bit(devfn, pdev->dma_alias_mask, U8_MAX) {
return ret; ret = fn(pdev, PCI_DEVID(pdev->bus->number, devfn),
data);
if (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) {
......
...@@ -249,7 +249,7 @@ static void tb_cfg_print_error(struct tb_ctl *ctl, ...@@ -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 { ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 = { ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 @@ struct tb_switch *tb_switch_alloc(struct tb *tb, u64 route) ...@@ -425,9 +427,9 @@ struct tb_switch *tb_switch_alloc(struct tb *tb, u64 route)
} }
/** /**
* 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) ...@@ -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) ...@@ -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) ...@@ -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); ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 @@ ...@@ -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, ...@@ -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, ...@@ -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)
{ {
......
...@@ -166,8 +166,6 @@ enum pci_dev_flags { ...@@ -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 { ...@@ -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, ...@@ -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) ...@@ -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 { ...@@ -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, ...@@ -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) ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 @@ ...@@ -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 */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment