Merge tag 'pci-v4.4-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 6 Nov 2015 19:29:53 +0000 (11:29 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 6 Nov 2015 19:29:53 +0000 (11:29 -0800)
Pull PCI updates from Bjorn Helgaas:
 "Resource management:
   - Add support for Enhanced Allocation devices (Sean O. Stalley)
   - Add Enhanced Allocation register entries (Sean O. Stalley)
   - Handle IORESOURCE_PCI_FIXED when sizing resources (David Daney)
   - Handle IORESOURCE_PCI_FIXED when assigning resources (David Daney)
   - Handle Enhanced Allocation capability for SR-IOV devices (David Daney)
   - Clear IORESOURCE_UNSET when reverting to firmware-assigned address (Bjorn Helgaas)
   - Make Enhanced Allocation bitmasks more obvious (Bjorn Helgaas)
   - Expand Enhanced Allocation BAR output (Bjorn Helgaas)
   - Add of_pci_check_probe_only to parse "linux,pci-probe-only" (Marc Zyngier)
   - Fix lookup of linux,pci-probe-only property (Marc Zyngier)
   - Add sparc mem64 resource parsing for root bus (Yinghai Lu)

  PCI device hotplug:
   - pciehp: Queue power work requests in dedicated function (Guenter Roeck)

  Driver binding:
   - Add builtin_pci_driver() to avoid registration boilerplate (Paul Gortmaker)

  Virtualization:
   - Set SR-IOV NumVFs to zero after enumeration (Alexander Duyck)
   - Remove redundant validation of SR-IOV offset/stride registers (Alexander Duyck)
   - Remove VFs in reverse order if virtfn_add() fails (Alexander Duyck)
   - Reorder pcibios_sriov_disable() (Alexander Duyck)
   - Wait 1 second between disabling VFs and clearing NumVFs (Alexander Duyck)
   - Fix sriov_enable() error path for pcibios_enable_sriov() failures (Alexander Duyck)
   - Enable SR-IOV ARI Capable Hierarchy before reading TotalVFs (Ben Shelton)
   - Don't try to restore VF BARs (Wei Yang)

  MSI:
   - Don't alloc pcibios-irq when MSI is enabled (Joerg Roedel)
   - Add msi_controller setup_irqs() method for special multivector setup (Lucas Stach)
   - Export all remapped MSIs to sysfs attributes (Romain Bezut)
   - Disable MSI on SiS 761 (Ondrej Zary)

  AER:
   - Clear error status registers during enumeration and restore (Taku Izumi)

  Generic host bridge driver:
   - Fix lookup of linux,pci-probe-only property (Marc Zyngier)
   - Allow multiple hosts with different map_bus() methods (David Daney)
   - Pass starting bus number to pci_scan_root_bus() (David Daney)
   - Fix address window calculation for non-zero starting bus (David Daney)

  Altera host bridge driver:
   - Add msi.h to ARM Kbuild (Ley Foon Tan)
   - Add Altera PCIe host controller driver (Ley Foon Tan)
   - Add Altera PCIe MSI driver (Ley Foon Tan)

  APM X-Gene host bridge driver:
   - Remove msi_controller assignment (Duc Dang)

  Broadcom iProc host bridge driver:
   - Fix header comment "Corporation" misspelling (Florian Fainelli)
   - Fix code comment to match code (Ray Jui)
   - Remove unused struct iproc_pcie.irqs[] (Ray Jui)
   - Call pci_fixup_irqs() for ARM64 as well as ARM (Ray Jui)
   - Fix PCIe reset logic (Ray Jui)
   - Improve link detection logic (Ray Jui)
   - Update PCIe device tree bindings (Ray Jui)
   - Add outbound mapping support (Ray Jui)

  Freescale i.MX6 host bridge driver:
   - Return real error code from imx6_add_pcie_port() (Fabio Estevam)
   - Add PCIE_PHY_RX_ASIC_OUT_VALID definition (Fabio Estevam)

  Freescale Layerscape host bridge driver:
   - Remove ls_pcie_establish_link() (Minghuan Lian)
   - Ignore PCIe controllers in Endpoint mode (Minghuan Lian)
   - Factor out SCFG related function (Minghuan Lian)
   - Update ls_add_pcie_port() (Minghuan Lian)
   - Remove unused fields from struct ls_pcie (Minghuan Lian)
   - Add support for LS1043a and LS2080a (Minghuan Lian)
   - Add ls_pcie_msi_host_init() (Minghuan Lian)

  HiSilicon host bridge driver:
   - Add HiSilicon SoC Hip05 PCIe driver (Zhou Wang)

  Marvell MVEBU host bridge driver:
   - Return zero for reserved or unimplemented config space (Russell King)
   - Use exact config access size; don't read/modify/write (Russell King)
   - Use of_get_available_child_count() (Russell King)
   - Use for_each_available_child_of_node() to walk child nodes (Russell King)
   - Report full node name when reporting a DT error (Russell King)
   - Use port->name rather than "PCIe%d.%d" (Russell King)
   - Move port parsing and resource claiming to  separate function (Russell King)
   - Fix memory leaks and refcount leaks (Russell King)
   - Split port parsing and resource claiming from  port setup (Russell King)
   - Use gpio_set_value_cansleep() (Russell King)
   - Use devm_kcalloc() to allocate an array (Russell King)
   - Use gpio_desc to carry around gpio (Russell King)
   - Improve clock/reset handling (Russell King)
   - Add PCI Express root complex capability block (Russell King)
   - Remove code restricting accesses to slot 0 (Russell King)

  NVIDIA Tegra host bridge driver:
   - Wrap static pgprot_t initializer with __pgprot() (Ard Biesheuvel)

  Renesas R-Car host bridge driver:
   - Build pci-rcar-gen2.c only on ARM (Geert Uytterhoeven)
   - Build pcie-rcar.c only on ARM (Geert Uytterhoeven)
   - Make PCI aware of the I/O resources (Phil Edworthy)
   - Remove dependency on ARM-specific struct hw_pci (Phil Edworthy)
   - Set root bus nr to that provided in DT (Phil Edworthy)
   - Fix I/O offset for multiple host bridges (Phil Edworthy)

  ST Microelectronics SPEAr13xx host bridge driver:
   - Fix dw_pcie_cfg_read/write() usage (Gabriele Paoloni)

  Synopsys DesignWare host bridge driver:
   - Make "clocks" and "clock-names" optional DT properties (Bhupesh Sharma)
   - Use exact access size in dw_pcie_cfg_read() (Gabriele Paoloni)
   - Simplify dw_pcie_cfg_read/write() interfaces (Gabriele Paoloni)
   - Require config accesses to be naturally aligned (Gabriele Paoloni)
   - Make "num-lanes" an optional DT property (Gabriele Paoloni)
   - Move calculation of bus addresses to DRA7xx (Gabriele Paoloni)
   - Replace ARM pci_sys_data->align_resource with global function pointer (Gabriele Paoloni)
   - Factor out MSI msg setup (Lucas Stach)
   - Implement multivector MSI IRQ setup (Lucas Stach)
   - Make get_msi_addr() return phys_addr_t, not u32 (Lucas Stach)
   - Set up high part of MSI target address (Lucas Stach)
   - Fix PORT_LOGIC_LINK_WIDTH_MASK (Zhou Wang)
   - Revert "PCI: designware: Program ATU with untranslated address" (Zhou Wang)
   - Use of_pci_get_host_bridge_resources() to parse DT (Zhou Wang)
   - Make driver arch-agnostic (Zhou Wang)

  Miscellaneous:
   - Make x86 pci_subsys_init() static (Alexander Kuleshov)
   - Turn off Request Attributes to avoid Chelsio T5 Completion erratum (Hariprasad Shenai)"

* tag 'pci-v4.4-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci: (94 commits)
  PCI: altera: Add Altera PCIe MSI driver
  PCI: hisi: Add HiSilicon SoC Hip05 PCIe driver
  PCI: layerscape: Add ls_pcie_msi_host_init()
  PCI: layerscape: Add support for LS1043a and LS2080a
  PCI: layerscape: Remove unused fields from struct ls_pcie
  PCI: layerscape: Update ls_add_pcie_port()
  PCI: layerscape: Factor out SCFG related function
  PCI: layerscape: Ignore PCIe controllers in Endpoint mode
  PCI: layerscape: Remove ls_pcie_establish_link()
  PCI: designware: Make "clocks" and "clock-names" optional DT properties
  PCI: designware: Make driver arch-agnostic
  ARM/PCI: Replace pci_sys_data->align_resource with global function pointer
  PCI: designware: Use of_pci_get_host_bridge_resources() to parse DT
  Revert "PCI: designware: Program ATU with untranslated address"
  PCI: designware: Move calculation of bus addresses to DRA7xx
  PCI: designware: Make "num-lanes" an optional DT property
  PCI: designware: Require config accesses to be naturally aligned
  PCI: designware: Simplify dw_pcie_cfg_read/write() interfaces
  PCI: designware: Use exact access size in dw_pcie_cfg_read()
  PCI: spear: Fix dw_pcie_cfg_read/write() usage
  ...

1  2 
MAINTAINERS
arch/powerpc/platforms/pseries/setup.c
arch/x86/pci/common.c
drivers/pci/host/pcie-altera-msi.c
drivers/pci/msi.c
drivers/pci/pci-driver.c
drivers/pci/pci.c
drivers/pci/pci.h
drivers/pci/probe.c
drivers/pci/quirks.c
include/linux/msi.h

diff --cc MAINTAINERS
Simple merge
Simple merge
index 0000000,2c37e86..99177f4
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,312 +1,314 @@@
 -      msi->msi_domain = pci_msi_create_irq_domain(msi->pdev->dev.of_node,
+ /*
+  * Copyright Altera Corporation (C) 2013-2015. All rights reserved
+  *
+  * This program is free software; you can redistribute it and/or modify it
+  * under the terms and conditions of the GNU General Public License,
+  * version 2, as published by the Free Software Foundation.
+  *
+  * This program is distributed in the hope 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/>.
+  */
+ #include <linux/interrupt.h>
+ #include <linux/irqchip/chained_irq.h>
+ #include <linux/module.h>
+ #include <linux/msi.h>
+ #include <linux/of_address.h>
+ #include <linux/of_irq.h>
+ #include <linux/of_pci.h>
+ #include <linux/pci.h>
+ #include <linux/platform_device.h>
+ #include <linux/slab.h>
+ #define MSI_STATUS            0x0
+ #define MSI_ERROR             0x4
+ #define MSI_INTMASK           0x8
+ #define MAX_MSI_VECTORS               32
+ struct altera_msi {
+       DECLARE_BITMAP(used, MAX_MSI_VECTORS);
+       struct mutex            lock;   /* protect "used" bitmap */
+       struct platform_device  *pdev;
+       struct irq_domain       *msi_domain;
+       struct irq_domain       *inner_domain;
+       void __iomem            *csr_base;
+       void __iomem            *vector_base;
+       phys_addr_t             vector_phy;
+       u32                     num_of_vectors;
+       int                     irq;
+ };
+ static inline void msi_writel(struct altera_msi *msi, const u32 value,
+                             const u32 reg)
+ {
+       writel_relaxed(value, msi->csr_base + reg);
+ }
+ static inline u32 msi_readl(struct altera_msi *msi, const u32 reg)
+ {
+       return readl_relaxed(msi->csr_base + reg);
+ }
+ static void altera_msi_isr(struct irq_desc *desc)
+ {
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+       struct altera_msi *msi;
+       unsigned long status;
+       u32 num_of_vectors;
+       u32 bit;
+       u32 virq;
+       chained_irq_enter(chip, desc);
+       msi = irq_desc_get_handler_data(desc);
+       num_of_vectors = msi->num_of_vectors;
+       while ((status = msi_readl(msi, MSI_STATUS)) != 0) {
+               for_each_set_bit(bit, &status, msi->num_of_vectors) {
+                       /* Dummy read from vector to clear the interrupt */
+                       readl_relaxed(msi->vector_base + (bit * sizeof(u32)));
+                       virq = irq_find_mapping(msi->inner_domain, bit);
+                       if (virq)
+                               generic_handle_irq(virq);
+                       else
+                               dev_err(&msi->pdev->dev, "unexpected MSI\n");
+               }
+       }
+       chained_irq_exit(chip, desc);
+ }
+ static struct irq_chip altera_msi_irq_chip = {
+       .name = "Altera PCIe MSI",
+       .irq_mask = pci_msi_mask_irq,
+       .irq_unmask = pci_msi_unmask_irq,
+ };
+ static struct msi_domain_info altera_msi_domain_info = {
+       .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+                    MSI_FLAG_PCI_MSIX),
+       .chip   = &altera_msi_irq_chip,
+ };
+ static void altera_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
+ {
+       struct altera_msi *msi = irq_data_get_irq_chip_data(data);
+       phys_addr_t addr = msi->vector_phy + (data->hwirq * sizeof(u32));
+       msg->address_lo = lower_32_bits(addr);
+       msg->address_hi = upper_32_bits(addr);
+       msg->data = data->hwirq;
+       dev_dbg(&msi->pdev->dev, "msi#%d address_hi %#x address_lo %#x\n",
+               (int)data->hwirq, msg->address_hi, msg->address_lo);
+ }
+ static int altera_msi_set_affinity(struct irq_data *irq_data,
+                                  const struct cpumask *mask, bool force)
+ {
+        return -EINVAL;
+ }
+ static struct irq_chip altera_msi_bottom_irq_chip = {
+       .name                   = "Altera MSI",
+       .irq_compose_msi_msg    = altera_compose_msi_msg,
+       .irq_set_affinity       = altera_msi_set_affinity,
+ };
+ static int altera_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
+                                  unsigned int nr_irqs, void *args)
+ {
+       struct altera_msi *msi = domain->host_data;
+       unsigned long bit;
+       u32 mask;
+       WARN_ON(nr_irqs != 1);
+       mutex_lock(&msi->lock);
+       bit = find_first_zero_bit(msi->used, msi->num_of_vectors);
+       if (bit >= msi->num_of_vectors) {
+               mutex_unlock(&msi->lock);
+               return -ENOSPC;
+       }
+       set_bit(bit, msi->used);
+       mutex_unlock(&msi->lock);
+       irq_domain_set_info(domain, virq, bit, &altera_msi_bottom_irq_chip,
+                           domain->host_data, handle_simple_irq,
+                           NULL, NULL);
+       mask = msi_readl(msi, MSI_INTMASK);
+       mask |= 1 << bit;
+       msi_writel(msi, mask, MSI_INTMASK);
+       return 0;
+ }
+ static void altera_irq_domain_free(struct irq_domain *domain,
+                                  unsigned int virq, unsigned int nr_irqs)
+ {
+       struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+       struct altera_msi *msi = irq_data_get_irq_chip_data(d);
+       u32 mask;
+       mutex_lock(&msi->lock);
+       if (!test_bit(d->hwirq, msi->used)) {
+               dev_err(&msi->pdev->dev, "trying to free unused MSI#%lu\n",
+                       d->hwirq);
+       } else {
+               __clear_bit(d->hwirq, msi->used);
+               mask = msi_readl(msi, MSI_INTMASK);
+               mask &= ~(1 << d->hwirq);
+               msi_writel(msi, mask, MSI_INTMASK);
+       }
+       mutex_unlock(&msi->lock);
+ }
+ static const struct irq_domain_ops msi_domain_ops = {
+       .alloc  = altera_irq_domain_alloc,
+       .free   = altera_irq_domain_free,
+ };
+ static int altera_allocate_domains(struct altera_msi *msi)
+ {
++      struct fwnode_handle *fwnode = of_node_to_fwnode(msi->pdev->dev.of_node);
++
+       msi->inner_domain = irq_domain_add_linear(NULL, msi->num_of_vectors,
+                                            &msi_domain_ops, msi);
+       if (!msi->inner_domain) {
+               dev_err(&msi->pdev->dev, "failed to create IRQ domain\n");
+               return -ENOMEM;
+       }
++      msi->msi_domain = pci_msi_create_irq_domain(fwnode,
+                               &altera_msi_domain_info, msi->inner_domain);
+       if (!msi->msi_domain) {
+               dev_err(&msi->pdev->dev, "failed to create MSI domain\n");
+               irq_domain_remove(msi->inner_domain);
+               return -ENOMEM;
+       }
+       return 0;
+ }
+ static void altera_free_domains(struct altera_msi *msi)
+ {
+       irq_domain_remove(msi->msi_domain);
+       irq_domain_remove(msi->inner_domain);
+ }
+ static int altera_msi_remove(struct platform_device *pdev)
+ {
+       struct altera_msi *msi = platform_get_drvdata(pdev);
+       msi_writel(msi, 0, MSI_INTMASK);
+       irq_set_chained_handler(msi->irq, NULL);
+       irq_set_handler_data(msi->irq, NULL);
+       altera_free_domains(msi);
+       platform_set_drvdata(pdev, NULL);
+       return 0;
+ }
+ static int altera_msi_probe(struct platform_device *pdev)
+ {
+       struct altera_msi *msi;
+       struct device_node *np = pdev->dev.of_node;
+       struct resource *res;
+       int ret;
+       msi = devm_kzalloc(&pdev->dev, sizeof(struct altera_msi),
+                          GFP_KERNEL);
+       if (!msi)
+               return -ENOMEM;
+       mutex_init(&msi->lock);
+       msi->pdev = pdev;
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr");
+       if (!res) {
+               dev_err(&pdev->dev, "no csr memory resource defined\n");
+               return -ENODEV;
+       }
+       msi->csr_base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(msi->csr_base)) {
+               dev_err(&pdev->dev, "failed to map csr memory\n");
+               return PTR_ERR(msi->csr_base);
+       }
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                          "vector_slave");
+       if (!res) {
+               dev_err(&pdev->dev, "no vector_slave memory resource defined\n");
+               return -ENODEV;
+       }
+       msi->vector_base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(msi->vector_base)) {
+               dev_err(&pdev->dev, "failed to map vector_slave memory\n");
+               return PTR_ERR(msi->vector_base);
+       }
+       msi->vector_phy = res->start;
+       if (of_property_read_u32(np, "num-vectors", &msi->num_of_vectors)) {
+               dev_err(&pdev->dev, "failed to parse the number of vectors\n");
+               return -EINVAL;
+       }
+       ret = altera_allocate_domains(msi);
+       if (ret)
+               return ret;
+       msi->irq = platform_get_irq(pdev, 0);
+       if (msi->irq <= 0) {
+               dev_err(&pdev->dev, "failed to map IRQ: %d\n", msi->irq);
+               ret = -ENODEV;
+               goto err;
+       }
+       irq_set_chained_handler_and_data(msi->irq, altera_msi_isr, msi);
+       platform_set_drvdata(pdev, msi);
+       return 0;
+ err:
+       altera_msi_remove(pdev);
+       return ret;
+ }
+ static const struct of_device_id altera_msi_of_match[] = {
+       { .compatible = "altr,msi-1.0", NULL },
+       { },
+ };
+ static struct platform_driver altera_msi_driver = {
+       .driver = {
+               .name = "altera-msi",
+               .of_match_table = altera_msi_of_match,
+       },
+       .probe = altera_msi_probe,
+       .remove = altera_msi_remove,
+ };
+ static int __init altera_msi_init(void)
+ {
+       return platform_driver_register(&altera_msi_driver);
+ }
+ subsys_initcall(altera_msi_init);
+ MODULE_AUTHOR("Ley Foon Tan <lftan@altera.com>");
+ MODULE_DESCRIPTION("Altera PCIe MSI support");
+ MODULE_LICENSE("GPL v2");
Simple merge
Simple merge
Simple merge
Simple merge
@@@ -1620,50 -1625,19 +1624,52 @@@ static void pci_init_capabilities(struc
  
        /* Enable ACS P2P upstream forwarding */
        pci_enable_acs(dev);
+       pci_cleanup_aer_error_status_regs(dev);
  }
  
 +/*
 + * This is the equivalent of pci_host_bridge_msi_domain that acts on
 + * devices. Firmware interfaces that can select the MSI domain on a
 + * per-device basis should be called from here.
 + */
 +static struct irq_domain *pci_dev_msi_domain(struct pci_dev *dev)
 +{
 +      struct irq_domain *d;
 +
 +      /*
 +       * If a domain has been set through the pcibios_add_device
 +       * callback, then this is the one (platform code knows best).
 +       */
 +      d = dev_get_msi_domain(&dev->dev);
 +      if (d)
 +              return d;
 +
 +      /*
 +       * Let's see if we have a firmware interface able to provide
 +       * the domain.
 +       */
 +      d = pci_msi_get_device_domain(dev);
 +      if (d)
 +              return d;
 +
 +      return NULL;
 +}
 +
  static void pci_set_msi_domain(struct pci_dev *dev)
  {
 +      struct irq_domain *d;
 +
        /*
 -       * If no domain has been set through the pcibios_add_device
 -       * callback, inherit the default from the bus device.
 +       * If the platform or firmware interfaces cannot supply a
 +       * device-specific MSI domain, then inherit the default domain
 +       * from the host bridge itself.
         */
 -      if (!dev_get_msi_domain(&dev->dev))
 -              dev_set_msi_domain(&dev->dev,
 -                                 dev_get_msi_domain(&dev->bus->dev));
 +      d = pci_dev_msi_domain(dev);
 +      if (!d)
 +              d = dev_get_msi_domain(&dev->bus->dev);
 +
 +      dev_set_msi_domain(&dev->dev, d);
  }
  
  void pci_device_add(struct pci_dev *dev, struct pci_bus *bus)
Simple merge
Simple merge