linux_dsm_epyc7002/drivers/pci/host/pcie-altera-msi.c
Linus Torvalds 3c87b79188 PCI changes for the v4.4 merge window:
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)
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJWPM9aAAoJEFmIoMA60/r89f8QALFMHegqKk5M08ZcCaG7unLF
 5U5t88y6KF/kNYF6HOqLQiHh79U3ToU5HdrNaAtutr0UnvgbFC2WulLqKYgLiq6Y
 YJnwR3EfgGmdG7DKAVAXq19+nc2hgTPAEe8sciU7HKlTbqQmGj6//y3sQULGNLx3
 zur0C33DCtrDgKDP7to273lkHO8Vl0YuLzyqwt0ePCMNcXR0h1dK8QxSTjuXBxaR
 c+T1V1Hw64MTxLz3xJd1/1ipy32u+9LnqqcdRz0zRq6qi48G9ch/i4Z6DHa8kTbj
 DUZrrTYKILQ2TcjcZSBmTueX11Z1Xa4/I/45sehIi6gVWL9qQbmGpt2E5YtFED+4
 GdcmBSbWG/qsNsabXk38uiM3ww7+ltXEOhTXbcK+EgjvIhE6gSK/plYG0fU9pybs
 AKViEXVdHoT1X0N1dLK12mq7kvDCQvShHn08lz97Q9YrZ32wv1Fnij6WVSbJvfWt
 DubtPtisVM+rVy+VTpOImNR9wO54lTmG5jK53yNqH7I20K89y1kqARlN9nMXMB1a
 2nQnwe9yWlsGj9gVNCn1KmyQSPOWjg+3Z+ekfwbxpca14s1AaN3Jm0N9Z61dXFoF
 y2ygoQtZ/z9BHr3quBpxXGt+aVUg2kcNw5GYeDYiALxXdJSObyzRrZ6HDb/zicU2
 ZH9hBj0ctXvucmy6I2mt
 =uZrt
 -----END PGP SIGNATURE-----

Merge tag 'pci-v4.4-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci

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
  ...
2015-11-06 11:29:53 -08:00

315 lines
7.9 KiB
C

/*
* 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");