pci-v5.6-changes

-----BEGIN PGP SIGNATURE-----
 
 iQJIBAABCgAyFiEEgMe7l+5h9hnxdsnuWYigwDrT+vwFAl40PWgUHGJoZWxnYWFz
 QGdvb2dsZS5jb20ACgkQWYigwDrT+vwclA/+Id/7Uc5S0r7xgFQRr3lbn0hHcx7f
 oBgmm6kGl8bu77MDiY32WLmPsp9e4BlK2M765cKQL5n20y8CzJ+kthZM8tZEDba4
 pnrZnWZ0A2xaBKzJqqYDtCqAeP97noCs4zBLo3JCA6jYCYI5bkvmdMQRlRjTUofO
 tkenGE+vexaJsLB7ghNskL3xGMueXLtLf/hXvaC6WGbSI9/zUmliHDL53DoKDPRo
 /9TGYDMwItZz+BhmBJz8hAL4naQIhIcDk2mz7CzWkY9xDhCJ1yeEwFvtvJwq0uM2
 Nmtq1g6yCB3sjlx+bRzrioLnouflztK1PGRbNugrMkR5XM9HIFmNwaDrqpU11ffA
 LQabMpbS3RWH3hbh4LYVMW13hbO+ld7/NG8jMFce2LHBWaGj6YejUQGdifz6vGRk
 JnDOgP19v5gWw08ibwkdfYzznPfMXp5IzFdJQFKhK+ugGDSJ8VeXiQ/pWtzghl3z
 P/puRw0BiL7ob/FUmhwn4J1Ytml7PZE+cJVN2l4C/CwKxR583GRUDgSHNL7Dky+o
 GcH9Tmjt4hQMNYRP01PACUmFYJwDfB+zgQ64a+uJsQwl/j+yfMnc1t/kqdM6yC9J
 GgkqLp989G/a3n9w5IC1P8aDYiwRqABvAFzlP9OZcIMUwmWbrhH175Qf6skKYIhH
 q9RKcLVXZdRS3mc=
 =fQ0E
 -----END PGP SIGNATURE-----

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

Pull PCI updates from Bjorn Helgaas:

 "Resource management:

   - Improve resource assignment for hot-added nested bridges, e.g.,
     Thunderbolt (Nicholas Johnson)

  Power management:

   - Optionally print config space of devices before suspend (Chen Yu)

   - Increase D3 delay for AMD Ryzen5/7 XHCI controllers (Daniel Drake)

  Virtualization:

   - Generalize DMA alias quirks (James Sewart)

   - Add DMA alias quirk for PLX PEX NTB (James Sewart)

   - Fix IOV memory leak (Navid Emamdoost)

  AER:

   - Log which device prevents error recovery (Yicong Yang)

  Peer-to-peer DMA:

   - Whitelist Intel SkyLake-E (Armen Baloyan)

  Broadcom iProc host bridge driver:

   - Apply PAXC quirk whether driver is built-in or module (Wei Liu)

  Broadcom STB host bridge driver:

   - Add Broadcom STB PCIe host controller driver (Jim Quinlan)

  Intel Gateway SoC host bridge driver:

   - Add driver for Intel Gateway SoC (Dilip Kota)

  Intel VMD host bridge driver:

   - Add support for DMA aliases on other buses (Jon Derrick)

   - Remove dma_map_ops overrides (Jon Derrick)

   - Remove now-unused X86_DEV_DMA_OPS (Christoph Hellwig)

  NVIDIA Tegra host bridge driver:

   - Fix Tegra30 afi_pex2_ctrl register offset (Marcel Ziswiler)

  Panasonic UniPhier host bridge driver:

   - Remove module code since driver can't be built as a module
     (Masahiro Yamada)

  Qualcomm host bridge driver:

   - Add support for SDM845 PCIe controller (Bjorn Andersson)

  TI Keystone host bridge driver:

   - Fix "num-viewport" DT property error handling (Kishon Vijay Abraham I)

   - Fix link training retries initiation (Yurii Monakov)

   - Fix outbound region mapping (Yurii Monakov)

  Misc:

   - Add Switchtec Gen4 support (Kelvin Cao)

   - Add Switchtec Intercomm Notify and Upstream Error Containment
     support (Logan Gunthorpe)

   - Use dma_set_mask_and_coherent() since Switchtec supports 64-bit
     addressing (Wesley Sheng)"

* tag 'pci-v5.6-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci: (60 commits)
  PCI: Allow adjust_bridge_window() to shrink resource if necessary
  PCI: Set resource size directly in adjust_bridge_window()
  PCI: Rename extend_bridge_window() to adjust_bridge_window()
  PCI: Rename extend_bridge_window() parameter
  PCI: Consider alignment of hot-added bridges when assigning resources
  PCI: Remove local variable usage in pci_bus_distribute_available_resources()
  PCI: Pass size + alignment to pci_bus_distribute_available_resources()
  PCI: Rename variables
  PCI: vmd: Add two VMD Device IDs
  PCI: Remove unnecessary braces
  PCI: brcmstb: Add MSI support
  PCI: brcmstb: Add Broadcom STB PCIe host controller driver
  x86/PCI: Remove X86_DEV_DMA_OPS
  PCI: vmd: Remove dma_map_ops overrides
  iommu/vt-d: Remove VMD child device sanity check
  iommu/vt-d: Use pci_real_dma_dev() for mapping
  PCI: Introduce pci_real_dma_dev()
  x86/PCI: Expose VMD's pci_dev in struct pci_sysdata
  x86/PCI: Add to_pci_sysdata() helper
  PCI/AER: Initialize aer_fifo
  ...
This commit is contained in:
Linus Torvalds 2020-01-31 14:48:54 -08:00
commit 26dca6dbd6
42 changed files with 2801 additions and 520 deletions

View File

@ -27,6 +27,8 @@ Andi Shyti <andi@etezian.org> <andi.shyti@samsung.com>
Andreas Herrmann <aherrman@de.ibm.com>
Andrey Ryabinin <ryabinin.a.a@gmail.com> <a.ryabinin@samsung.com>
Andrew Morton <akpm@linux-foundation.org>
Andrew Murray <amurray@thegoodpenguin.co.uk> <andrew.murray@arm.com>
Andrew Murray <amurray@thegoodpenguin.co.uk> <amurray@embedded-bits.co.uk>
Andrew Vasquez <andrew.vasquez@qlogic.com>
Andy Adamson <andros@citi.umich.edu>
Antoine Tenart <antoine.tenart@free-electrons.com>

View File

@ -283,5 +283,5 @@ or disabled (0). If 0 is found in any of the msi_bus files belonging
to bridges between the PCI root and the device, MSIs are disabled.
It is also worth checking the device driver to see whether it supports MSIs.
For example, it may contain calls to pci_irq_alloc_vectors() with the
For example, it may contain calls to pci_alloc_irq_vectors() with the
PCI_IRQ_MSI or PCI_IRQ_MSIX flags.

View File

@ -0,0 +1,97 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/pci/brcm,stb-pcie.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Brcmstb PCIe Host Controller Device Tree Bindings
maintainers:
- Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
allOf:
- $ref: /schemas/pci/pci-bus.yaml#
properties:
compatible:
const: brcm,bcm2711-pcie # The Raspberry Pi 4
reg:
maxItems: 1
interrupts:
minItems: 1
maxItems: 2
items:
- description: PCIe host controller
- description: builtin MSI controller
interrupt-names:
minItems: 1
maxItems: 2
items:
- const: pcie
- const: msi
ranges:
maxItems: 1
dma-ranges:
maxItems: 1
clocks:
maxItems: 1
clock-names:
items:
- const: sw_pcie
msi-controller:
description: Identifies the node as an MSI controller.
msi-parent:
description: MSI controller the device is capable of using.
brcm,enable-ssc:
description: Indicates usage of spread-spectrum clocking.
type: boolean
required:
- reg
- dma-ranges
- "#interrupt-cells"
- interrupts
- interrupt-names
- interrupt-map-mask
- interrupt-map
- msi-controller
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
scb {
#address-cells = <2>;
#size-cells = <1>;
pcie0: pcie@7d500000 {
compatible = "brcm,bcm2711-pcie";
reg = <0x0 0x7d500000 0x9310>;
device_type = "pci";
#address-cells = <3>;
#size-cells = <2>;
#interrupt-cells = <1>;
interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "pcie", "msi";
interrupt-map-mask = <0x0 0x0 0x0 0x7>;
interrupt-map = <0 0 0 1 &gicv2 GIC_SPI 143 IRQ_TYPE_LEVEL_HIGH>;
msi-parent = <&pcie0>;
msi-controller;
ranges = <0x02000000 0x0 0xf8000000 0x6 0x00000000 0x0 0x04000000>;
dma-ranges = <0x02000000 0x0 0x00000000 0x0 0x00000000 0x0 0x80000000>;
brcm,enable-ssc;
};
};

View File

@ -0,0 +1,138 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/pci/intel-gw-pcie.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: PCIe RC controller on Intel Gateway SoCs
maintainers:
- Dilip Kota <eswara.kota@linux.intel.com>
properties:
compatible:
items:
- const: intel,lgm-pcie
- const: snps,dw-pcie
device_type:
const: pci
"#address-cells":
const: 3
"#size-cells":
const: 2
reg:
items:
- description: Controller control and status registers.
- description: PCIe configuration registers.
- description: Controller application registers.
reg-names:
items:
- const: dbi
- const: config
- const: app
ranges:
maxItems: 1
resets:
maxItems: 1
clocks:
maxItems: 1
phys:
maxItems: 1
phy-names:
const: pcie
reset-gpios:
maxItems: 1
linux,pci-domain: true
num-lanes:
maximum: 2
description: Number of lanes to use for this port.
'#interrupt-cells':
const: 1
interrupt-map-mask:
description: Standard PCI IRQ mapping properties.
interrupt-map:
description: Standard PCI IRQ mapping properties.
max-link-speed:
description: Specify PCI Gen for link capability.
allOf:
- $ref: /schemas/types.yaml#/definitions/uint32
- enum: [ 1, 2, 3, 4 ]
- default: 1
bus-range:
description: Range of bus numbers associated with this controller.
reset-assert-ms:
description: |
Delay after asserting reset to the PCIe device.
maximum: 500
default: 100
required:
- compatible
- device_type
- "#address-cells"
- "#size-cells"
- reg
- reg-names
- ranges
- resets
- clocks
- phys
- phy-names
- reset-gpios
- '#interrupt-cells'
- interrupt-map
- interrupt-map-mask
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/clock/intel,lgm-clk.h>
pcie10: pcie@d0e00000 {
compatible = "intel,lgm-pcie", "snps,dw-pcie";
device_type = "pci";
#address-cells = <3>;
#size-cells = <2>;
reg = <0xd0e00000 0x1000>,
<0xd2000000 0x800000>,
<0xd0a41000 0x1000>;
reg-names = "dbi", "config", "app";
linux,pci-domain = <0>;
max-link-speed = <4>;
bus-range = <0x00 0x08>;
interrupt-parent = <&ioapic1>;
#interrupt-cells = <1>;
interrupt-map-mask = <0 0 0 0x7>;
interrupt-map = <0 0 0 1 &ioapic1 27 1>,
<0 0 0 2 &ioapic1 28 1>,
<0 0 0 3 &ioapic1 29 1>,
<0 0 0 4 &ioapic1 30 1>;
ranges = <0x02000000 0 0xd4000000 0xd4000000 0 0x04000000>;
resets = <&rcu0 0x50 0>;
clocks = <&cgu0 LGM_GCLK_PCIE10>;
phys = <&cb0phy0>;
phy-names = "pcie";
reset-assert-ms = <500>;
reset-gpios = <&gpio0 3 GPIO_ACTIVE_LOW>;
num-lanes = <2>;
};

View File

@ -11,6 +11,7 @@
- "qcom,pcie-ipq4019" for ipq4019
- "qcom,pcie-ipq8074" for ipq8074
- "qcom,pcie-qcs404" for qcs404
- "qcom,pcie-sdm845" for sdm845
- reg:
Usage: required
@ -126,6 +127,18 @@
- "master_bus" AXI Master clock
- "slave_bus" AXI Slave clock
-clock-names:
Usage: required for sdm845
Value type: <stringlist>
Definition: Should contain the following entries
- "aux" Auxiliary clock
- "cfg" Configuration clock
- "bus_master" Master AXI clock
- "bus_slave" Slave AXI clock
- "slave_q2a" Slave Q2A clock
- "tbu" PCIe TBU clock
- "pipe" PIPE clock
- resets:
Usage: required
Value type: <prop-encoded-array>
@ -188,6 +201,12 @@
- "pwr" PWR reset
- "ahb" AHB reset
- reset-names:
Usage: required for sdm845
Value type: <stringlist>
Definition: Should contain the following entries
- "pci" PCIe core reset
- power-domains:
Usage: required for apq8084 and msm8996/apq8096
Value type: <prop-encoded-array>

View File

@ -12914,7 +12914,7 @@ F: arch/x86/kernel/early-quirks.c
PCI NATIVE HOST BRIDGE AND ENDPOINT DRIVERS
M: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
R: Andrew Murray <andrew.murray@arm.com>
R: Andrew Murray <amurray@thegoodpenguin.co.uk>
L: linux-pci@vger.kernel.org
Q: http://patchwork.ozlabs.org/project/linux-pci/list/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/lpieralisi/pci.git/

View File

@ -2931,9 +2931,6 @@ config HAVE_ATOMIC_IOMAP
def_bool y
depends on X86_32
config X86_DEV_DMA_OPS
bool
source "drivers/firmware/Kconfig"
source "arch/x86/kvm/Kconfig"

View File

@ -8,16 +8,6 @@ struct dev_archdata {
#endif
};
#if defined(CONFIG_X86_DEV_DMA_OPS) && defined(CONFIG_PCI_DOMAINS)
struct dma_domain {
struct list_head node;
const struct dma_map_ops *dma_ops;
int domain_nr;
};
void add_dma_domain(struct dma_domain *domain);
void del_dma_domain(struct dma_domain *domain);
#endif
struct pdev_archdata {
};

View File

@ -25,7 +25,7 @@ struct pci_sysdata {
void *fwnode; /* IRQ domain for MSI assignment */
#endif
#if IS_ENABLED(CONFIG_VMD)
bool vmd_domain; /* True if in Intel VMD domain */
struct pci_dev *vmd_dev; /* VMD Device if in Intel VMD domain */
#endif
};
@ -35,12 +35,15 @@ extern int noioapicreroute;
#ifdef CONFIG_PCI
static inline struct pci_sysdata *to_pci_sysdata(const struct pci_bus *bus)
{
return bus->sysdata;
}
#ifdef CONFIG_PCI_DOMAINS
static inline int pci_domain_nr(struct pci_bus *bus)
{
struct pci_sysdata *sd = bus->sysdata;
return sd->domain;
return to_pci_sysdata(bus)->domain;
}
static inline int pci_proc_domain(struct pci_bus *bus)
@ -52,24 +55,20 @@ static inline int pci_proc_domain(struct pci_bus *bus)
#ifdef CONFIG_PCI_MSI_IRQ_DOMAIN
static inline void *_pci_root_bus_fwnode(struct pci_bus *bus)
{
struct pci_sysdata *sd = bus->sysdata;
return sd->fwnode;
return to_pci_sysdata(bus)->fwnode;
}
#define pci_root_bus_fwnode _pci_root_bus_fwnode
#endif
#if IS_ENABLED(CONFIG_VMD)
static inline bool is_vmd(struct pci_bus *bus)
{
#if IS_ENABLED(CONFIG_VMD)
struct pci_sysdata *sd = bus->sysdata;
return sd->vmd_domain;
#else
return false;
#endif
return to_pci_sysdata(bus)->vmd_dev != NULL;
}
#else
#define is_vmd(bus) false
#endif /* CONFIG_VMD */
/* Can be used to override the logic in pci_scan_bus for skipping
already-configured bus numbers - to be used for buggy BIOSes
@ -124,9 +123,7 @@ void native_restore_msi_irqs(struct pci_dev *dev);
/* Returns the node based on pci bus */
static inline int __pcibus_to_node(const struct pci_bus *bus)
{
const struct pci_sysdata *sd = bus->sysdata;
return sd->node;
return to_pci_sysdata(bus)->node;
}
static inline const struct cpumask *

View File

@ -625,43 +625,6 @@ unsigned int pcibios_assign_all_busses(void)
return (pci_probe & PCI_ASSIGN_ALL_BUSSES) ? 1 : 0;
}
#if defined(CONFIG_X86_DEV_DMA_OPS) && defined(CONFIG_PCI_DOMAINS)
static LIST_HEAD(dma_domain_list);
static DEFINE_SPINLOCK(dma_domain_list_lock);
void add_dma_domain(struct dma_domain *domain)
{
spin_lock(&dma_domain_list_lock);
list_add(&domain->node, &dma_domain_list);
spin_unlock(&dma_domain_list_lock);
}
EXPORT_SYMBOL_GPL(add_dma_domain);
void del_dma_domain(struct dma_domain *domain)
{
spin_lock(&dma_domain_list_lock);
list_del(&domain->node);
spin_unlock(&dma_domain_list_lock);
}
EXPORT_SYMBOL_GPL(del_dma_domain);
static void set_dma_domain_ops(struct pci_dev *pdev)
{
struct dma_domain *domain;
spin_lock(&dma_domain_list_lock);
list_for_each_entry(domain, &dma_domain_list, node) {
if (pci_domain_nr(pdev->bus) == domain->domain_nr) {
pdev->dev.dma_ops = domain->dma_ops;
break;
}
}
spin_unlock(&dma_domain_list_lock);
}
#else
static void set_dma_domain_ops(struct pci_dev *pdev) {}
#endif
static void set_dev_domain_options(struct pci_dev *pdev)
{
if (is_vmd(pdev->bus))
@ -697,7 +660,6 @@ int pcibios_add_device(struct pci_dev *dev)
pa_data = data->next;
memunmap(data);
}
set_dma_domain_ops(dev);
set_dev_domain_options(dev);
return 0;
}
@ -736,3 +698,13 @@ int pci_ext_cfg_avail(void)
else
return 0;
}
#if IS_ENABLED(CONFIG_VMD)
struct pci_dev *pci_real_dma_dev(struct pci_dev *dev)
{
if (is_vmd(dev->bus))
return to_pci_sysdata(dev->bus)->vmd_dev;
return dev;
}
#endif

View File

@ -230,11 +230,8 @@ static struct pci_dev *setup_aliases(struct device *dev)
*/
ivrs_alias = amd_iommu_alias_table[pci_dev_id(pdev)];
if (ivrs_alias != pci_dev_id(pdev) &&
PCI_BUS_NUM(ivrs_alias) == pdev->bus->number) {
pci_add_dma_alias(pdev, ivrs_alias & 0xff);
pci_info(pdev, "Added PCI DMA alias %02x.%d\n",
PCI_SLOT(ivrs_alias), PCI_FUNC(ivrs_alias));
}
PCI_BUS_NUM(ivrs_alias) == pdev->bus->number)
pci_add_dma_alias(pdev, ivrs_alias & 0xff, 1);
clone_aliases(pdev);

View File

@ -774,13 +774,7 @@ static struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devf
if (dev_is_pci(dev)) {
struct pci_dev *pf_pdev;
pdev = to_pci_dev(dev);
#ifdef CONFIG_X86
/* VMD child devices currently cannot be handled individually */
if (is_vmd(pdev->bus))
return NULL;
#endif
pdev = pci_real_dma_dev(to_pci_dev(dev));
/* VFs aren't listed in scope tables; we need to look up
* the PF instead to find the IOMMU. */
@ -2428,6 +2422,9 @@ static struct dmar_domain *find_domain(struct device *dev)
dev->archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO))
return NULL;
if (dev_is_pci(dev))
dev = &pci_real_dma_dev(to_pci_dev(dev))->dev;
/* No lock here, assumes no domain exit in normal case */
info = dev->archdata.iommu;
if (likely(info))

View File

@ -239,7 +239,6 @@ config PCIE_TANGO_SMP8759
config VMD
depends on PCI_MSI && X86_64 && SRCU
select X86_DEV_DMA_OPS
tristate "Intel Volume Management Device Driver"
---help---
Adds support for the Intel Volume Management Device (VMD). VMD is a
@ -253,6 +252,15 @@ config VMD
To compile this driver as a module, choose M here: the
module will be called vmd.
config PCIE_BRCMSTB
tristate "Broadcom Brcmstb PCIe host controller"
depends on ARCH_BCM2835 || COMPILE_TEST
depends on OF
depends on PCI_MSI_IRQ_DOMAIN
help
Say Y here to enable PCIe host controller support for
Broadcom STB based SoCs, like the Raspberry Pi 4.
config PCI_HYPERV_INTERFACE
tristate "Hyper-V PCI Interface"
depends on X86 && HYPERV && PCI_MSI && PCI_MSI_IRQ_DOMAIN && X86_64

View File

@ -28,6 +28,7 @@ obj-$(CONFIG_PCIE_MEDIATEK) += pcie-mediatek.o
obj-$(CONFIG_PCIE_MOBIVEIL) += pcie-mobiveil.o
obj-$(CONFIG_PCIE_TANGO_SMP8759) += pcie-tango.o
obj-$(CONFIG_VMD) += vmd.o
obj-$(CONFIG_PCIE_BRCMSTB) += pcie-brcmstb.o
# pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW
obj-y += dwc/

View File

@ -209,6 +209,17 @@ config PCIE_ARTPEC6_EP
Enables support for the PCIe controller in the ARTPEC-6 SoC to work in
endpoint mode. This uses the DesignWare core.
config PCIE_INTEL_GW
bool "Intel Gateway PCIe host controller support"
depends on OF && (X86 || COMPILE_TEST)
depends on PCI_MSI_IRQ_DOMAIN
select PCIE_DW_HOST
help
Say 'Y' here to enable PCIe Host controller support on Intel
Gateway SoCs.
The PCIe controller uses the DesignWare core plus Intel-specific
hardware wrappers.
config PCIE_KIRIN
depends on OF && (ARM64 || COMPILE_TEST)
bool "HiSilicon Kirin series SoCs PCIe controllers"

View File

@ -13,6 +13,7 @@ obj-$(CONFIG_PCI_LAYERSCAPE_EP) += pci-layerscape-ep.o
obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o
obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o
obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o
obj-$(CONFIG_PCIE_INTEL_GW) += pcie-intel-gw.o
obj-$(CONFIG_PCIE_KIRIN) += pcie-kirin.o
obj-$(CONFIG_PCIE_HISI_STB) += pcie-histb.o
obj-$(CONFIG_PCI_MESON) += pci-meson.o

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
* PCIe host controller driver for Samsung EXYNOS SoCs
* PCIe host controller driver for Samsung Exynos SoCs
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com

View File

@ -422,7 +422,7 @@ static void ks_pcie_setup_rc_app_regs(struct keystone_pcie *ks_pcie)
lower_32_bits(start) | OB_ENABLEN);
ks_pcie_app_writel(ks_pcie, OB_OFFSET_HI(i),
upper_32_bits(start));
start += OB_WIN_SIZE;
start += OB_WIN_SIZE * SZ_1M;
}
val = ks_pcie_app_readl(ks_pcie, CMD_STATUS);
@ -510,7 +510,7 @@ static void ks_pcie_stop_link(struct dw_pcie *pci)
/* Disable Link training */
val = ks_pcie_app_readl(ks_pcie, CMD_STATUS);
val &= ~LTSSM_EN_VAL;
ks_pcie_app_writel(ks_pcie, CMD_STATUS, LTSSM_EN_VAL | val);
ks_pcie_app_writel(ks_pcie, CMD_STATUS, val);
}
static int ks_pcie_start_link(struct dw_pcie *pci)
@ -1354,7 +1354,7 @@ static int __init ks_pcie_probe(struct platform_device *pdev)
ret = of_property_read_u32(np, "num-viewport", &num_viewport);
if (ret < 0) {
dev_err(dev, "unable to read *num-viewport* property\n");
return ret;
goto err_get_sync;
}
/*

View File

@ -51,9 +51,6 @@ static const struct of_device_id artpec6_pcie_of_match[];
#define ACK_N_FTS_MASK GENMASK(15, 8)
#define ACK_N_FTS(x) (((x) << 8) & ACK_N_FTS_MASK)
#define FAST_TRAINING_SEQ_MASK GENMASK(7, 0)
#define FAST_TRAINING_SEQ(x) (((x) << 0) & FAST_TRAINING_SEQ_MASK)
/* ARTPEC-6 specific registers */
#define PCIECFG 0x18
#define PCIECFG_DBG_OEN BIT(24)
@ -313,10 +310,7 @@ static void artpec6_pcie_set_nfts(struct artpec6_pcie *artpec6_pcie)
* Set the Number of Fast Training Sequences that the core
* advertises as its N_FTS during Gen2 or Gen3 link training.
*/
val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
val &= ~FAST_TRAINING_SEQ_MASK;
val |= FAST_TRAINING_SEQ(180);
dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
dw_pcie_link_set_n_fts(pci, 180);
}
static void artpec6_pcie_assert_core_reset(struct artpec6_pcie *artpec6_pcie)

View File

@ -12,6 +12,7 @@
#include <linux/of.h>
#include <linux/types.h>
#include "../../pci.h"
#include "pcie-designware.h"
/*
@ -474,6 +475,61 @@ int dw_pcie_link_up(struct dw_pcie *pci)
(!(val & PCIE_PORT_DEBUG1_LINK_IN_TRAINING)));
}
void dw_pcie_upconfig_setup(struct dw_pcie *pci)
{
u32 val;
val = dw_pcie_readl_dbi(pci, PCIE_PORT_MULTI_LANE_CTRL);
val |= PORT_MLTI_UPCFG_SUPPORT;
dw_pcie_writel_dbi(pci, PCIE_PORT_MULTI_LANE_CTRL, val);
}
EXPORT_SYMBOL_GPL(dw_pcie_upconfig_setup);
void dw_pcie_link_set_max_speed(struct dw_pcie *pci, u32 link_gen)
{
u32 reg, val;
u8 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
reg = dw_pcie_readl_dbi(pci, offset + PCI_EXP_LNKCTL2);
reg &= ~PCI_EXP_LNKCTL2_TLS;
switch (pcie_link_speed[link_gen]) {
case PCIE_SPEED_2_5GT:
reg |= PCI_EXP_LNKCTL2_TLS_2_5GT;
break;
case PCIE_SPEED_5_0GT:
reg |= PCI_EXP_LNKCTL2_TLS_5_0GT;
break;
case PCIE_SPEED_8_0GT:
reg |= PCI_EXP_LNKCTL2_TLS_8_0GT;
break;
case PCIE_SPEED_16_0GT:
reg |= PCI_EXP_LNKCTL2_TLS_16_0GT;
break;
default:
/* Use hardware capability */
val = dw_pcie_readl_dbi(pci, offset + PCI_EXP_LNKCAP);
val = FIELD_GET(PCI_EXP_LNKCAP_SLS, val);
reg &= ~PCI_EXP_LNKCTL2_HASD;
reg |= FIELD_PREP(PCI_EXP_LNKCTL2_TLS, val);
break;
}
dw_pcie_writel_dbi(pci, offset + PCI_EXP_LNKCTL2, reg);
}
EXPORT_SYMBOL_GPL(dw_pcie_link_set_max_speed);
void dw_pcie_link_set_n_fts(struct dw_pcie *pci, u32 n_fts)
{
u32 val;
val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
val &= ~PORT_LOGIC_N_FTS_MASK;
val |= n_fts & PORT_LOGIC_N_FTS_MASK;
dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
}
EXPORT_SYMBOL_GPL(dw_pcie_link_set_n_fts);
static u8 dw_pcie_iatu_unroll_enabled(struct dw_pcie *pci)
{
u32 val;

View File

@ -30,7 +30,12 @@
#define LINK_WAIT_IATU 9
/* Synopsys-specific PCIe configuration registers */
#define PCIE_PORT_AFR 0x70C
#define PORT_AFR_N_FTS_MASK GENMASK(15, 8)
#define PORT_AFR_CC_N_FTS_MASK GENMASK(23, 16)
#define PCIE_PORT_LINK_CONTROL 0x710
#define PORT_LINK_DLL_LINK_EN BIT(5)
#define PORT_LINK_MODE_MASK GENMASK(21, 16)
#define PORT_LINK_MODE(n) FIELD_PREP(PORT_LINK_MODE_MASK, n)
#define PORT_LINK_MODE_1_LANES PORT_LINK_MODE(0x1)
@ -46,6 +51,7 @@
#define PCIE_PORT_DEBUG1_LINK_IN_TRAINING BIT(29)
#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C
#define PORT_LOGIC_N_FTS_MASK GENMASK(7, 0)
#define PORT_LOGIC_SPEED_CHANGE BIT(17)
#define PORT_LOGIC_LINK_WIDTH_MASK GENMASK(12, 8)
#define PORT_LOGIC_LINK_WIDTH(n) FIELD_PREP(PORT_LOGIC_LINK_WIDTH_MASK, n)
@ -60,6 +66,9 @@
#define PCIE_MSI_INTR0_MASK 0x82C
#define PCIE_MSI_INTR0_STATUS 0x830
#define PCIE_PORT_MULTI_LANE_CTRL 0x8C0
#define PORT_MLTI_UPCFG_SUPPORT BIT(7)
#define PCIE_ATU_VIEWPORT 0x900
#define PCIE_ATU_REGION_INBOUND BIT(31)
#define PCIE_ATU_REGION_OUTBOUND 0
@ -273,6 +282,9 @@ void dw_pcie_write_dbi2(struct dw_pcie *pci, u32 reg, size_t size, u32 val);
u32 dw_pcie_read_atu(struct dw_pcie *pci, u32 reg, size_t size);
void dw_pcie_write_atu(struct dw_pcie *pci, u32 reg, size_t size, u32 val);
int dw_pcie_link_up(struct dw_pcie *pci);
void dw_pcie_upconfig_setup(struct dw_pcie *pci);
void dw_pcie_link_set_max_speed(struct dw_pcie *pci, u32 link_gen);
void dw_pcie_link_set_n_fts(struct dw_pcie *pci, u32 n_fts);
int dw_pcie_wait_for_link(struct dw_pcie *pci);
void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index,
int type, u64 cpu_addr, u64 pci_addr,

View File

@ -0,0 +1,545 @@
// SPDX-License-Identifier: GPL-2.0
/*
* PCIe host controller driver for Intel Gateway SoCs
*
* Copyright (c) 2019 Intel Corporation.
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/gpio/consumer.h>
#include <linux/iopoll.h>
#include <linux/pci_regs.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include "../../pci.h"
#include "pcie-designware.h"
#define PORT_AFR_N_FTS_GEN12_DFT (SZ_128 - 1)
#define PORT_AFR_N_FTS_GEN3 180
#define PORT_AFR_N_FTS_GEN4 196
/* PCIe Application logic Registers */
#define PCIE_APP_CCR 0x10
#define PCIE_APP_CCR_LTSSM_ENABLE BIT(0)
#define PCIE_APP_MSG_CR 0x30
#define PCIE_APP_MSG_XMT_PM_TURNOFF BIT(0)
#define PCIE_APP_PMC 0x44
#define PCIE_APP_PMC_IN_L2 BIT(20)
#define PCIE_APP_IRNEN 0xF4
#define PCIE_APP_IRNCR 0xF8
#define PCIE_APP_IRN_AER_REPORT BIT(0)
#define PCIE_APP_IRN_PME BIT(2)
#define PCIE_APP_IRN_RX_VDM_MSG BIT(4)
#define PCIE_APP_IRN_PM_TO_ACK BIT(9)
#define PCIE_APP_IRN_LINK_AUTO_BW_STAT BIT(11)
#define PCIE_APP_IRN_BW_MGT BIT(12)
#define PCIE_APP_IRN_MSG_LTR BIT(18)
#define PCIE_APP_IRN_SYS_ERR_RC BIT(29)
#define PCIE_APP_INTX_OFST 12
#define PCIE_APP_IRN_INT \
(PCIE_APP_IRN_AER_REPORT | PCIE_APP_IRN_PME | \
PCIE_APP_IRN_RX_VDM_MSG | PCIE_APP_IRN_SYS_ERR_RC | \
PCIE_APP_IRN_PM_TO_ACK | PCIE_APP_IRN_MSG_LTR | \
PCIE_APP_IRN_BW_MGT | PCIE_APP_IRN_LINK_AUTO_BW_STAT | \
(PCIE_APP_INTX_OFST + PCI_INTERRUPT_INTA) | \
(PCIE_APP_INTX_OFST + PCI_INTERRUPT_INTB) | \
(PCIE_APP_INTX_OFST + PCI_INTERRUPT_INTC) | \
(PCIE_APP_INTX_OFST + PCI_INTERRUPT_INTD))
#define BUS_IATU_OFFSET SZ_256M
#define RESET_INTERVAL_MS 100
struct intel_pcie_soc {
unsigned int pcie_ver;
unsigned int pcie_atu_offset;
u32 num_viewport;
};
struct intel_pcie_port {
struct dw_pcie pci;
void __iomem *app_base;
struct gpio_desc *reset_gpio;
u32 rst_intrvl;
u32 max_speed;
u32 link_gen;
u32 max_width;
u32 n_fts;
struct clk *core_clk;
struct reset_control *core_rst;
struct phy *phy;
u8 pcie_cap_ofst;
};
static void pcie_update_bits(void __iomem *base, u32 ofs, u32 mask, u32 val)
{
u32 old;
old = readl(base + ofs);
val = (old & ~mask) | (val & mask);
if (val != old)
writel(val, base + ofs);
}
static inline u32 pcie_app_rd(struct intel_pcie_port *lpp, u32 ofs)
{
return readl(lpp->app_base + ofs);
}
static inline void pcie_app_wr(struct intel_pcie_port *lpp, u32 ofs, u32 val)
{
writel(val, lpp->app_base + ofs);
}
static void pcie_app_wr_mask(struct intel_pcie_port *lpp, u32 ofs,
u32 mask, u32 val)
{
pcie_update_bits(lpp->app_base, ofs, mask, val);
}
static inline u32 pcie_rc_cfg_rd(struct intel_pcie_port *lpp, u32 ofs)
{
return dw_pcie_readl_dbi(&lpp->pci, ofs);
}
static inline void pcie_rc_cfg_wr(struct intel_pcie_port *lpp, u32 ofs, u32 val)
{
dw_pcie_writel_dbi(&lpp->pci, ofs, val);
}
static void pcie_rc_cfg_wr_mask(struct intel_pcie_port *lpp, u32 ofs,
u32 mask, u32 val)
{
pcie_update_bits(lpp->pci.dbi_base, ofs, mask, val);
}
static void intel_pcie_ltssm_enable(struct intel_pcie_port *lpp)
{
pcie_app_wr_mask(lpp, PCIE_APP_CCR, PCIE_APP_CCR_LTSSM_ENABLE,
PCIE_APP_CCR_LTSSM_ENABLE);
}
static void intel_pcie_ltssm_disable(struct intel_pcie_port *lpp)
{
pcie_app_wr_mask(lpp, PCIE_APP_CCR, PCIE_APP_CCR_LTSSM_ENABLE, 0);
}
static void intel_pcie_link_setup(struct intel_pcie_port *lpp)
{
u32 val;
u8 offset = lpp->pcie_cap_ofst;
val = pcie_rc_cfg_rd(lpp, offset + PCI_EXP_LNKCAP);
lpp->max_speed = FIELD_GET(PCI_EXP_LNKCAP_SLS, val);
lpp->max_width = FIELD_GET(PCI_EXP_LNKCAP_MLW, val);
val = pcie_rc_cfg_rd(lpp, offset + PCI_EXP_LNKCTL);
val &= ~(PCI_EXP_LNKCTL_LD | PCI_EXP_LNKCTL_ASPMC);
pcie_rc_cfg_wr(lpp, offset + PCI_EXP_LNKCTL, val);
}
static void intel_pcie_port_logic_setup(struct intel_pcie_port *lpp)
{
u32 val, mask;
switch (pcie_link_speed[lpp->max_speed]) {
case PCIE_SPEED_8_0GT:
lpp->n_fts = PORT_AFR_N_FTS_GEN3;
break;
case PCIE_SPEED_16_0GT:
lpp->n_fts = PORT_AFR_N_FTS_GEN4;
break;
default:
lpp->n_fts = PORT_AFR_N_FTS_GEN12_DFT;
break;
}
mask = PORT_AFR_N_FTS_MASK | PORT_AFR_CC_N_FTS_MASK;
val = FIELD_PREP(PORT_AFR_N_FTS_MASK, lpp->n_fts) |
FIELD_PREP(PORT_AFR_CC_N_FTS_MASK, lpp->n_fts);
pcie_rc_cfg_wr_mask(lpp, PCIE_PORT_AFR, mask, val);
/* Port Link Control Register */
pcie_rc_cfg_wr_mask(lpp, PCIE_PORT_LINK_CONTROL, PORT_LINK_DLL_LINK_EN,
PORT_LINK_DLL_LINK_EN);
}
static void intel_pcie_rc_setup(struct intel_pcie_port *lpp)
{
intel_pcie_ltssm_disable(lpp);
intel_pcie_link_setup(lpp);
dw_pcie_setup_rc(&lpp->pci.pp);
dw_pcie_upconfig_setup(&lpp->pci);
intel_pcie_port_logic_setup(lpp);
dw_pcie_link_set_max_speed(&lpp->pci, lpp->link_gen);
dw_pcie_link_set_n_fts(&lpp->pci, lpp->n_fts);
}
static int intel_pcie_ep_rst_init(struct intel_pcie_port *lpp)
{
struct device *dev = lpp->pci.dev;
int ret;
lpp->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(lpp->reset_gpio)) {
ret = PTR_ERR(lpp->reset_gpio);
if (ret != -EPROBE_DEFER)
dev_err(dev, "Failed to request PCIe GPIO: %d\n", ret);
return ret;
}
/* Make initial reset last for 100us */
usleep_range(100, 200);
return 0;
}
static void intel_pcie_core_rst_assert(struct intel_pcie_port *lpp)
{
reset_control_assert(lpp->core_rst);
}
static void intel_pcie_core_rst_deassert(struct intel_pcie_port *lpp)
{
/*
* One micro-second delay to make sure the reset pulse
* wide enough so that core reset is clean.
*/
udelay(1);
reset_control_deassert(lpp->core_rst);
/*
* Some SoC core reset also reset PHY, more delay needed
* to make sure the reset process is done.
*/
usleep_range(1000, 2000);
}
static void intel_pcie_device_rst_assert(struct intel_pcie_port *lpp)
{
gpiod_set_value_cansleep(lpp->reset_gpio, 1);
}
static void intel_pcie_device_rst_deassert(struct intel_pcie_port *lpp)
{
msleep(lpp->rst_intrvl);
gpiod_set_value_cansleep(lpp->reset_gpio, 0);
}
static int intel_pcie_app_logic_setup(struct intel_pcie_port *lpp)
{
intel_pcie_device_rst_deassert(lpp);
intel_pcie_ltssm_enable(lpp);
return dw_pcie_wait_for_link(&lpp->pci);
}
static void intel_pcie_core_irq_disable(struct intel_pcie_port *lpp)
{
pcie_app_wr(lpp, PCIE_APP_IRNEN, 0);
pcie_app_wr(lpp, PCIE_APP_IRNCR, PCIE_APP_IRN_INT);
}
static int intel_pcie_get_resources(struct platform_device *pdev)
{
struct intel_pcie_port *lpp = platform_get_drvdata(pdev);
struct dw_pcie *pci = &lpp->pci;
struct device *dev = pci->dev;
struct resource *res;
int ret;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
pci->dbi_base = devm_ioremap_resource(dev, res);
if (IS_ERR(pci->dbi_base))
return PTR_ERR(pci->dbi_base);
lpp->core_clk = devm_clk_get(dev, NULL);
if (IS_ERR(lpp->core_clk)) {
ret = PTR_ERR(lpp->core_clk);
if (ret != -EPROBE_DEFER)
dev_err(dev, "Failed to get clks: %d\n", ret);
return ret;
}
lpp->core_rst = devm_reset_control_get(dev, NULL);
if (IS_ERR(lpp->core_rst)) {
ret = PTR_ERR(lpp->core_rst);
if (ret != -EPROBE_DEFER)
dev_err(dev, "Failed to get resets: %d\n", ret);
return ret;
}
ret = device_property_match_string(dev, "device_type", "pci");
if (ret) {
dev_err(dev, "Failed to find pci device type: %d\n", ret);
return ret;
}
ret = device_property_read_u32(dev, "reset-assert-ms",
&lpp->rst_intrvl);
if (ret)
lpp->rst_intrvl = RESET_INTERVAL_MS;
ret = of_pci_get_max_link_speed(dev->of_node);
lpp->link_gen = ret < 0 ? 0 : ret;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "app");
lpp->app_base = devm_ioremap_resource(dev, res);
if (IS_ERR(lpp->app_base))
return PTR_ERR(lpp->app_base);
lpp->phy = devm_phy_get(dev, "pcie");
if (IS_ERR(lpp->phy)) {
ret = PTR_ERR(lpp->phy);
if (ret != -EPROBE_DEFER)
dev_err(dev, "Couldn't get pcie-phy: %d\n", ret);
return ret;
}
return 0;
}
static void intel_pcie_deinit_phy(struct intel_pcie_port *lpp)
{
phy_exit(lpp->phy);
}
static int intel_pcie_wait_l2(struct intel_pcie_port *lpp)
{
u32 value;
int ret;
if (pcie_link_speed[lpp->max_speed] < PCIE_SPEED_8_0GT)
return 0;
/* Send PME_TURN_OFF message */
pcie_app_wr_mask(lpp, PCIE_APP_MSG_CR, PCIE_APP_MSG_XMT_PM_TURNOFF,
PCIE_APP_MSG_XMT_PM_TURNOFF);
/* Read PMC status and wait for falling into L2 link state */
ret = readl_poll_timeout(lpp->app_base + PCIE_APP_PMC, value,
value & PCIE_APP_PMC_IN_L2, 20,
jiffies_to_usecs(5 * HZ));
if (ret)
dev_err(lpp->pci.dev, "PCIe link enter L2 timeout!\n");
return ret;
}
static void intel_pcie_turn_off(struct intel_pcie_port *lpp)
{
if (dw_pcie_link_up(&lpp->pci))
intel_pcie_wait_l2(lpp);
/* Put endpoint device in reset state */
intel_pcie_device_rst_assert(lpp);
pcie_rc_cfg_wr_mask(lpp, PCI_COMMAND, PCI_COMMAND_MEMORY, 0);
}
static int intel_pcie_host_setup(struct intel_pcie_port *lpp)
{
struct device *dev = lpp->pci.dev;
int ret;
intel_pcie_core_rst_assert(lpp);
intel_pcie_device_rst_assert(lpp);
ret = phy_init(lpp->phy);
if (ret)
return ret;
intel_pcie_core_rst_deassert(lpp);
ret = clk_prepare_enable(lpp->core_clk);
if (ret) {
dev_err(lpp->pci.dev, "Core clock enable failed: %d\n", ret);
goto clk_err;
}
if (!lpp->pcie_cap_ofst) {
ret = dw_pcie_find_capability(&lpp->pci, PCI_CAP_ID_EXP);
if (!ret) {
ret = -ENXIO;
dev_err(dev, "Invalid PCIe capability offset\n");
goto app_init_err;
}
lpp->pcie_cap_ofst = ret;
}
intel_pcie_rc_setup(lpp);
ret = intel_pcie_app_logic_setup(lpp);
if (ret)
goto app_init_err;
/* Enable integrated interrupts */
pcie_app_wr_mask(lpp, PCIE_APP_IRNEN, PCIE_APP_IRN_INT,
PCIE_APP_IRN_INT);
return 0;
app_init_err:
clk_disable_unprepare(lpp->core_clk);
clk_err:
intel_pcie_core_rst_assert(lpp);
intel_pcie_deinit_phy(lpp);
return ret;
}
static void __intel_pcie_remove(struct intel_pcie_port *lpp)
{
intel_pcie_core_irq_disable(lpp);
intel_pcie_turn_off(lpp);
clk_disable_unprepare(lpp->core_clk);
intel_pcie_core_rst_assert(lpp);
intel_pcie_deinit_phy(lpp);
}
static int intel_pcie_remove(struct platform_device *pdev)
{
struct intel_pcie_port *lpp = platform_get_drvdata(pdev);
struct pcie_port *pp = &lpp->pci.pp;
dw_pcie_host_deinit(pp);
__intel_pcie_remove(lpp);
return 0;
}
static int __maybe_unused intel_pcie_suspend_noirq(struct device *dev)
{
struct intel_pcie_port *lpp = dev_get_drvdata(dev);
int ret;
intel_pcie_core_irq_disable(lpp);
ret = intel_pcie_wait_l2(lpp);
if (ret)
return ret;
intel_pcie_deinit_phy(lpp);
clk_disable_unprepare(lpp->core_clk);
return ret;
}
static int __maybe_unused intel_pcie_resume_noirq(struct device *dev)
{
struct intel_pcie_port *lpp = dev_get_drvdata(dev);
return intel_pcie_host_setup(lpp);
}
static int intel_pcie_rc_init(struct pcie_port *pp)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
struct intel_pcie_port *lpp = dev_get_drvdata(pci->dev);
return intel_pcie_host_setup(lpp);
}
/*
* Dummy function so that DW core doesn't configure MSI
*/
static int intel_pcie_msi_init(struct pcie_port *pp)
{
return 0;
}
u64 intel_pcie_cpu_addr(struct dw_pcie *pcie, u64 cpu_addr)
{
return cpu_addr + BUS_IATU_OFFSET;
}
static const struct dw_pcie_ops intel_pcie_ops = {
.cpu_addr_fixup = intel_pcie_cpu_addr,
};
static const struct dw_pcie_host_ops intel_pcie_dw_ops = {
.host_init = intel_pcie_rc_init,
.msi_host_init = intel_pcie_msi_init,
};
static const struct intel_pcie_soc pcie_data = {
.pcie_ver = 0x520A,
.pcie_atu_offset = 0xC0000,
.num_viewport = 3,
};
static int intel_pcie_probe(struct platform_device *pdev)
{
const struct intel_pcie_soc *data;
struct device *dev = &pdev->dev;
struct intel_pcie_port *lpp;
struct pcie_port *pp;
struct dw_pcie *pci;
int ret;
lpp = devm_kzalloc(dev, sizeof(*lpp), GFP_KERNEL);
if (!lpp)
return -ENOMEM;
platform_set_drvdata(pdev, lpp);
pci = &lpp->pci;
pci->dev = dev;
pp = &pci->pp;
ret = intel_pcie_get_resources(pdev);
if (ret)
return ret;
ret = intel_pcie_ep_rst_init(lpp);
if (ret)
return ret;
data = device_get_match_data(dev);
if (!data)
return -ENODEV;
pci->ops = &intel_pcie_ops;
pci->version = data->pcie_ver;
pci->atu_base = pci->dbi_base + data->pcie_atu_offset;
pp->ops = &intel_pcie_dw_ops;
ret = dw_pcie_host_init(pp);
if (ret) {
dev_err(dev, "Cannot initialize host\n");
return ret;
}
/*
* Intel PCIe doesn't configure IO region, so set viewport
* to not perform IO region access.
*/
pci->num_viewport = data->num_viewport;
return 0;
}
static const struct dev_pm_ops intel_pcie_pm_ops = {
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(intel_pcie_suspend_noirq,
intel_pcie_resume_noirq)
};
static const struct of_device_id of_intel_pcie_match[] = {
{ .compatible = "intel,lgm-pcie", .data = &pcie_data },
{}
};
static struct platform_driver intel_pcie_driver = {
.probe = intel_pcie_probe,
.remove = intel_pcie_remove,
.driver = {
.name = "intel-gw-pcie",
.of_match_table = of_intel_pcie_match,
.pm = &intel_pcie_pm_ops,
},
};
builtin_platform_driver(intel_pcie_driver);

View File

@ -54,6 +54,7 @@
#define PCIE20_PARF_LTSSM 0x1B0
#define PCIE20_PARF_SID_OFFSET 0x234
#define PCIE20_PARF_BDF_TRANSLATE_CFG 0x24C
#define PCIE20_PARF_DEVICE_TYPE 0x1000
#define PCIE20_ELBI_SYS_CTRL 0x04
#define PCIE20_ELBI_SYS_CTRL_LT_ENABLE BIT(0)
@ -80,6 +81,8 @@
#define PCIE20_v3_PARF_SLV_ADDR_SPACE_SIZE 0x358
#define SLV_ADDR_SPACE_SZ 0x10000000
#define DEVICE_TYPE_RC 0x4
#define QCOM_PCIE_2_1_0_MAX_SUPPLY 3
struct qcom_pcie_resources_2_1_0 {
struct clk *iface_clk;
@ -139,12 +142,20 @@ struct qcom_pcie_resources_2_3_3 {
struct reset_control *rst[7];
};
struct qcom_pcie_resources_2_7_0 {
struct clk_bulk_data clks[6];
struct regulator_bulk_data supplies[2];
struct reset_control *pci_reset;
struct clk *pipe_clk;
};
union qcom_pcie_resources {
struct qcom_pcie_resources_1_0_0 v1_0_0;
struct qcom_pcie_resources_2_1_0 v2_1_0;
struct qcom_pcie_resources_2_3_2 v2_3_2;
struct qcom_pcie_resources_2_3_3 v2_3_3;
struct qcom_pcie_resources_2_4_0 v2_4_0;
struct qcom_pcie_resources_2_7_0 v2_7_0;
};
struct qcom_pcie;
@ -1068,6 +1079,134 @@ err_clk_iface:
return ret;
}
static int qcom_pcie_get_resources_2_7_0(struct qcom_pcie *pcie)
{
struct qcom_pcie_resources_2_7_0 *res = &pcie->res.v2_7_0;
struct dw_pcie *pci = pcie->pci;
struct device *dev = pci->dev;
int ret;
res->pci_reset = devm_reset_control_get_exclusive(dev, "pci");
if (IS_ERR(res->pci_reset))
return PTR_ERR(res->pci_reset);
res->supplies[0].supply = "vdda";
res->supplies[1].supply = "vddpe-3v3";
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(res->supplies),
res->supplies);
if (ret)
return ret;
res->clks[0].id = "aux";
res->clks[1].id = "cfg";
res->clks[2].id = "bus_master";
res->clks[3].id = "bus_slave";
res->clks[4].id = "slave_q2a";
res->clks[5].id = "tbu";
ret = devm_clk_bulk_get(dev, ARRAY_SIZE(res->clks), res->clks);
if (ret < 0)
return ret;
res->pipe_clk = devm_clk_get(dev, "pipe");
return PTR_ERR_OR_ZERO(res->pipe_clk);
}
static int qcom_pcie_init_2_7_0(struct qcom_pcie *pcie)
{
struct qcom_pcie_resources_2_7_0 *res = &pcie->res.v2_7_0;
struct dw_pcie *pci = pcie->pci;
struct device *dev = pci->dev;
u32 val;
int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(res->supplies), res->supplies);
if (ret < 0) {
dev_err(dev, "cannot enable regulators\n");
return ret;
}
ret = clk_bulk_prepare_enable(ARRAY_SIZE(res->clks), res->clks);
if (ret < 0)
goto err_disable_regulators;
ret = reset_control_assert(res->pci_reset);
if (ret < 0) {
dev_err(dev, "cannot deassert pci reset\n");
goto err_disable_clocks;
}
usleep_range(1000, 1500);
ret = reset_control_deassert(res->pci_reset);
if (ret < 0) {
dev_err(dev, "cannot deassert pci reset\n");
goto err_disable_clocks;
}
ret = clk_prepare_enable(res->pipe_clk);
if (ret) {
dev_err(dev, "cannot prepare/enable pipe clock\n");
goto err_disable_clocks;
}
/* configure PCIe to RC mode */
writel(DEVICE_TYPE_RC, pcie->parf + PCIE20_PARF_DEVICE_TYPE);
/* enable PCIe clocks and resets */
val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL);
val &= ~BIT(0);
writel(val, pcie->parf + PCIE20_PARF_PHY_CTRL);
/* change DBI base address */
writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR);
/* MAC PHY_POWERDOWN MUX DISABLE */
val = readl(pcie->parf + PCIE20_PARF_SYS_CTRL);
val &= ~BIT(29);
writel(val, pcie->parf + PCIE20_PARF_SYS_CTRL);
val = readl(pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL);
val |= BIT(4);
writel(val, pcie->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL);
if (IS_ENABLED(CONFIG_PCI_MSI)) {
val = readl(pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT);
val |= BIT(31);
writel(val, pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT);
}
return 0;
err_disable_clocks:
clk_bulk_disable_unprepare(ARRAY_SIZE(res->clks), res->clks);
err_disable_regulators:
regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies);
return ret;
}
static void qcom_pcie_deinit_2_7_0(struct qcom_pcie *pcie)
{
struct qcom_pcie_resources_2_7_0 *res = &pcie->res.v2_7_0;
clk_bulk_disable_unprepare(ARRAY_SIZE(res->clks), res->clks);
regulator_bulk_disable(ARRAY_SIZE(res->supplies), res->supplies);
}
static int qcom_pcie_post_init_2_7_0(struct qcom_pcie *pcie)
{
struct qcom_pcie_resources_2_7_0 *res = &pcie->res.v2_7_0;
return clk_prepare_enable(res->pipe_clk);
}
static void qcom_pcie_post_deinit_2_7_0(struct qcom_pcie *pcie)
{
struct qcom_pcie_resources_2_7_0 *res = &pcie->res.v2_7_0;
clk_disable_unprepare(res->pipe_clk);
}
static int qcom_pcie_link_up(struct dw_pcie *pci)
{
u16 val = readw(pci->dbi_base + PCIE20_CAP + PCI_EXP_LNKSTA);
@ -1167,6 +1306,16 @@ static const struct qcom_pcie_ops ops_2_3_3 = {
.ltssm_enable = qcom_pcie_2_3_2_ltssm_enable,
};
/* Qcom IP rev.: 2.7.0 Synopsys IP rev.: 4.30a */
static const struct qcom_pcie_ops ops_2_7_0 = {
.get_resources = qcom_pcie_get_resources_2_7_0,
.init = qcom_pcie_init_2_7_0,
.deinit = qcom_pcie_deinit_2_7_0,
.ltssm_enable = qcom_pcie_2_3_2_ltssm_enable,
.post_init = qcom_pcie_post_init_2_7_0,
.post_deinit = qcom_pcie_post_deinit_2_7_0,
};
static const struct dw_pcie_ops dw_pcie_ops = {
.link_up = qcom_pcie_link_up,
};
@ -1282,6 +1431,7 @@ static const struct of_device_id qcom_pcie_match[] = {
{ .compatible = "qcom,pcie-ipq8074", .data = &ops_2_3_3 },
{ .compatible = "qcom,pcie-ipq4019", .data = &ops_2_4_0 },
{ .compatible = "qcom,pcie-qcs404", .data = &ops_2_4_0 },
{ .compatible = "qcom,pcie-sdm845", .data = &ops_2_7_0 },
{ }
};

View File

@ -9,11 +9,11 @@
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/iopoll.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqdomain.h>
#include <linux/module.h>
#include <linux/of_irq.h>
#include <linux/pci.h>
#include <linux/phy/phy.h>
@ -171,12 +171,6 @@ static void uniphier_pcie_irq_enable(struct uniphier_pcie_priv *priv)
writel(PCL_RCV_INTX_ALL_ENABLE, priv->base + PCL_RCV_INTX);
}
static void uniphier_pcie_irq_disable(struct uniphier_pcie_priv *priv)
{
writel(0, priv->base + PCL_RCV_INT);
writel(0, priv->base + PCL_RCV_INTX);
}
static void uniphier_pcie_irq_ack(struct irq_data *d)
{
struct pcie_port *pp = irq_data_get_irq_chip_data(d);
@ -397,14 +391,6 @@ out_clk_disable:
return ret;
}
static void uniphier_pcie_host_disable(struct uniphier_pcie_priv *priv)
{
uniphier_pcie_irq_disable(priv);
phy_exit(priv->phy);
reset_control_assert(priv->rst);
clk_disable_unprepare(priv->clk);
}
static const struct dw_pcie_ops dw_pcie_ops = {
.start_link = uniphier_pcie_establish_link,
.stop_link = uniphier_pcie_stop_link,
@ -456,31 +442,16 @@ static int uniphier_pcie_probe(struct platform_device *pdev)
return uniphier_add_pcie_port(priv, pdev);
}
static int uniphier_pcie_remove(struct platform_device *pdev)
{
struct uniphier_pcie_priv *priv = platform_get_drvdata(pdev);
uniphier_pcie_host_disable(priv);
return 0;
}
static const struct of_device_id uniphier_pcie_match[] = {
{ .compatible = "socionext,uniphier-pcie", },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, uniphier_pcie_match);
static struct platform_driver uniphier_pcie_driver = {
.probe = uniphier_pcie_probe,
.remove = uniphier_pcie_remove,
.driver = {
.name = "uniphier-pcie",
.of_match_table = uniphier_pcie_match,
},
};
builtin_platform_driver(uniphier_pcie_driver);
MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>");
MODULE_DESCRIPTION("UniPhier PCIe host controller driver");
MODULE_LICENSE("GPL v2");

View File

@ -2499,7 +2499,6 @@ static const struct tegra_pcie_soc tegra20_pcie = {
.num_ports = 2,
.ports = tegra20_pcie_ports,
.msi_base_shift = 0,
.afi_pex2_ctrl = 0x128,
.pads_pll_ctl = PADS_PLL_CTL_TEGRA20,
.tx_ref_sel = PADS_PLL_CTL_TXCLKREF_DIV10,
.pads_refclk_cfg0 = 0xfa5cfa5c,
@ -2528,6 +2527,7 @@ static const struct tegra_pcie_soc tegra30_pcie = {
.num_ports = 3,
.ports = tegra30_pcie_ports,
.msi_base_shift = 8,
.afi_pex2_ctrl = 0x128,
.pads_pll_ctl = PADS_PLL_CTL_TEGRA30,
.tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN,
.pads_refclk_cfg0 = 0xfa5cfa5c,
@ -2798,7 +2798,7 @@ static int tegra_pcie_probe(struct platform_device *pdev)
pm_runtime_enable(pcie->dev);
err = pm_runtime_get_sync(pcie->dev);
if (err) {
if (err < 0) {
dev_err(dev, "fail to enable pcie controller: %d\n", err);
goto teardown_msi;
}

File diff suppressed because it is too large Load Diff

View File

@ -1588,6 +1588,30 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd802,
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd804,
quirk_paxc_disable_msi_parsing);
static void quirk_paxc_bridge(struct pci_dev *pdev)
{
/*
* The PCI config space is shared with the PAXC root port and the first
* Ethernet device. So, we need to workaround this by telling the PCI
* code that the bridge is not an Ethernet device.
*/
if (pdev->hdr_type == PCI_HEADER_TYPE_BRIDGE)
pdev->class = PCI_CLASS_BRIDGE_PCI << 8;
/*
* MPSS is not being set properly (as it is currently 0). This is
* because that area of the PCI config space is hard coded to zero, and
* is not modifiable by firmware. Set this to 2 (e.g., 512 byte MPS)
* so that the MPS can be set to the real max value.
*/
pdev->pcie_mpss = 2;
}
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x16cd, quirk_paxc_bridge);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x16f0, quirk_paxc_bridge);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd750, quirk_paxc_bridge);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd802, quirk_paxc_bridge);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd804, quirk_paxc_bridge);
MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>");
MODULE_DESCRIPTION("Broadcom iPROC PCIe common driver");
MODULE_LICENSE("GPL v2");

View File

@ -98,9 +98,6 @@ struct vmd_dev {
struct irq_domain *irq_domain;
struct pci_bus *bus;
u8 busn_start;
struct dma_map_ops dma_ops;
struct dma_domain dma_domain;
};
static inline struct vmd_dev *vmd_from_bus(struct pci_bus *bus)
@ -295,151 +292,6 @@ static struct msi_domain_info vmd_msi_domain_info = {
.chip = &vmd_msi_controller,
};
/*
* VMD replaces the requester ID with its own. DMA mappings for devices in a
* VMD domain need to be mapped for the VMD, not the device requiring
* the mapping.
*/
static struct device *to_vmd_dev(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct vmd_dev *vmd = vmd_from_bus(pdev->bus);
return &vmd->dev->dev;
}
static void *vmd_alloc(struct device *dev, size_t size, dma_addr_t *addr,
gfp_t flag, unsigned long attrs)
{
return dma_alloc_attrs(to_vmd_dev(dev), size, addr, flag, attrs);
}
static void vmd_free(struct device *dev, size_t size, void *vaddr,
dma_addr_t addr, unsigned long attrs)
{
return dma_free_attrs(to_vmd_dev(dev), size, vaddr, addr, attrs);
}
static int vmd_mmap(struct device *dev, struct vm_area_struct *vma,
void *cpu_addr, dma_addr_t addr, size_t size,
unsigned long attrs)
{
return dma_mmap_attrs(to_vmd_dev(dev), vma, cpu_addr, addr, size,
attrs);
}
static int vmd_get_sgtable(struct device *dev, struct sg_table *sgt,
void *cpu_addr, dma_addr_t addr, size_t size,
unsigned long attrs)
{
return dma_get_sgtable_attrs(to_vmd_dev(dev), sgt, cpu_addr, addr, size,
attrs);
}
static dma_addr_t vmd_map_page(struct device *dev, struct page *page,
unsigned long offset, size_t size,
enum dma_data_direction dir,
unsigned long attrs)
{
return dma_map_page_attrs(to_vmd_dev(dev), page, offset, size, dir,
attrs);
}
static void vmd_unmap_page(struct device *dev, dma_addr_t addr, size_t size,
enum dma_data_direction dir, unsigned long attrs)
{
dma_unmap_page_attrs(to_vmd_dev(dev), addr, size, dir, attrs);
}
static int vmd_map_sg(struct device *dev, struct scatterlist *sg, int nents,
enum dma_data_direction dir, unsigned long attrs)
{
return dma_map_sg_attrs(to_vmd_dev(dev), sg, nents, dir, attrs);
}
static void vmd_unmap_sg(struct device *dev, struct scatterlist *sg, int nents,
enum dma_data_direction dir, unsigned long attrs)
{
dma_unmap_sg_attrs(to_vmd_dev(dev), sg, nents, dir, attrs);
}
static void vmd_sync_single_for_cpu(struct device *dev, dma_addr_t addr,
size_t size, enum dma_data_direction dir)
{
dma_sync_single_for_cpu(to_vmd_dev(dev), addr, size, dir);
}
static void vmd_sync_single_for_device(struct device *dev, dma_addr_t addr,
size_t size, enum dma_data_direction dir)
{
dma_sync_single_for_device(to_vmd_dev(dev), addr, size, dir);
}
static void vmd_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg,
int nents, enum dma_data_direction dir)
{
dma_sync_sg_for_cpu(to_vmd_dev(dev), sg, nents, dir);
}
static void vmd_sync_sg_for_device(struct device *dev, struct scatterlist *sg,
int nents, enum dma_data_direction dir)
{
dma_sync_sg_for_device(to_vmd_dev(dev), sg, nents, dir);
}
static int vmd_dma_supported(struct device *dev, u64 mask)
{
return dma_supported(to_vmd_dev(dev), mask);
}
static u64 vmd_get_required_mask(struct device *dev)
{
return dma_get_required_mask(to_vmd_dev(dev));
}
static void vmd_teardown_dma_ops(struct vmd_dev *vmd)
{
struct dma_domain *domain = &vmd->dma_domain;
if (get_dma_ops(&vmd->dev->dev))
del_dma_domain(domain);
}
#define ASSIGN_VMD_DMA_OPS(source, dest, fn) \
do { \
if (source->fn) \
dest->fn = vmd_##fn; \
} while (0)
static void vmd_setup_dma_ops(struct vmd_dev *vmd)
{
const struct dma_map_ops *source = get_dma_ops(&vmd->dev->dev);
struct dma_map_ops *dest = &vmd->dma_ops;
struct dma_domain *domain = &vmd->dma_domain;
domain->domain_nr = vmd->sysdata.domain;
domain->dma_ops = dest;
if (!source)
return;
ASSIGN_VMD_DMA_OPS(source, dest, alloc);
ASSIGN_VMD_DMA_OPS(source, dest, free);
ASSIGN_VMD_DMA_OPS(source, dest, mmap);
ASSIGN_VMD_DMA_OPS(source, dest, get_sgtable);
ASSIGN_VMD_DMA_OPS(source, dest, map_page);
ASSIGN_VMD_DMA_OPS(source, dest, unmap_page);
ASSIGN_VMD_DMA_OPS(source, dest, map_sg);
ASSIGN_VMD_DMA_OPS(source, dest, unmap_sg);
ASSIGN_VMD_DMA_OPS(source, dest, sync_single_for_cpu);
ASSIGN_VMD_DMA_OPS(source, dest, sync_single_for_device);
ASSIGN_VMD_DMA_OPS(source, dest, sync_sg_for_cpu);
ASSIGN_VMD_DMA_OPS(source, dest, sync_sg_for_device);
ASSIGN_VMD_DMA_OPS(source, dest, dma_supported);
ASSIGN_VMD_DMA_OPS(source, dest, get_required_mask);
add_dma_domain(domain);
}
#undef ASSIGN_VMD_DMA_OPS
static char __iomem *vmd_cfg_addr(struct vmd_dev *vmd, struct pci_bus *bus,
unsigned int devfn, int reg, int len)
{
@ -679,7 +531,7 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
.parent = res,
};
sd->vmd_domain = true;
sd->vmd_dev = vmd->dev;
sd->domain = vmd_find_free_domain();
if (sd->domain < 0)
return sd->domain;
@ -709,7 +561,6 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
}
vmd_attach_resources(vmd);
vmd_setup_dma_ops(vmd);
dev_set_msi_domain(&vmd->bus->dev, vmd->irq_domain);
pci_scan_child_bus(vmd->bus);
@ -824,7 +675,6 @@ static void vmd_remove(struct pci_dev *dev)
pci_stop_root_bus(vmd->bus);
pci_remove_root_bus(vmd->bus);
vmd_cleanup_srcu(vmd);
vmd_teardown_dma_ops(vmd);
vmd_detach_resources(vmd);
irq_domain_remove(vmd->irq_domain);
}
@ -868,6 +718,10 @@ static const struct pci_device_id vmd_ids[] = {
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_VMD_28C0),
.driver_data = VMD_FEAT_HAS_MEMBAR_SHADOW |
VMD_FEAT_HAS_BUS_RESTRICTIONS,},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x467f),
.driver_data = VMD_FEAT_HAS_BUS_RESTRICTIONS,},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x4c3d),
.driver_data = VMD_FEAT_HAS_BUS_RESTRICTIONS,},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_VMD_9A0B),
.driver_data = VMD_FEAT_HAS_BUS_RESTRICTIONS,},
{0,}

View File

@ -186,10 +186,10 @@ int pci_iov_add_virtfn(struct pci_dev *dev, int id)
sprintf(buf, "virtfn%u", id);
rc = sysfs_create_link(&dev->dev.kobj, &virtfn->dev.kobj, buf);
if (rc)
goto failed2;
goto failed1;
rc = sysfs_create_link(&virtfn->dev.kobj, &dev->dev.kobj, "physfn");
if (rc)
goto failed3;
goto failed2;
kobject_uevent(&virtfn->dev.kobj, KOBJ_CHANGE);
@ -197,11 +197,10 @@ int pci_iov_add_virtfn(struct pci_dev *dev, int id)
return 0;
failed3:
sysfs_remove_link(&dev->dev.kobj, buf);
failed2:
pci_stop_and_remove_bus_device(virtfn);
sysfs_remove_link(&dev->dev.kobj, buf);
failed1:
pci_stop_and_remove_bus_device(virtfn);
pci_dev_put(dev);
failed0:
virtfn_remove_bus(dev->bus, bus);

View File

@ -289,6 +289,9 @@ static const struct pci_p2pdma_whitelist_entry {
/* Intel Xeon E7 v3/Xeon E5 v3/Core i7 */
{PCI_VENDOR_ID_INTEL, 0x2f00, REQ_SAME_HOST_BRIDGE},
{PCI_VENDOR_ID_INTEL, 0x2f01, REQ_SAME_HOST_BRIDGE},
/* Intel SkyLake-E */
{PCI_VENDOR_ID_INTEL, 0x2030, 0},
{PCI_VENDOR_ID_INTEL, 0x2020, 0},
{}
};

View File

@ -1372,8 +1372,11 @@ int pci_save_state(struct pci_dev *dev)
{
int i;
/* XXX: 100% dword access ok here? */
for (i = 0; i < 16; i++)
for (i = 0; i < 16; i++) {
pci_read_config_dword(dev, i * 4, &dev->saved_config_space[i]);
pci_dbg(dev, "saving config space at offset %#x (reading %#x)\n",
i * 4, dev->saved_config_space[i]);
}
dev->state_saved = true;
i = pci_save_pcie_state(dev);
@ -5998,7 +6001,8 @@ EXPORT_SYMBOL_GPL(pci_pr3_present);
/**
* 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
* @devfn_from: alias slot and function
* @nr_devfns: number of subsequent devfns to alias
*
* This helper encodes an 8-bit devfn as a bit number in dma_alias_mask
* which is used to program permissible bus-devfn source addresses for DMA
@ -6014,18 +6018,29 @@ EXPORT_SYMBOL_GPL(pci_pr3_present);
* cannot be left as a userspace activity). DMA aliases should therefore
* be configured via quirks, such as the PCI fixup header quirk.
*/
void pci_add_dma_alias(struct pci_dev *dev, u8 devfn)
void pci_add_dma_alias(struct pci_dev *dev, u8 devfn_from, unsigned nr_devfns)
{
int devfn_to;
nr_devfns = min(nr_devfns, (unsigned) MAX_NR_DEVFNS - devfn_from);
devfn_to = devfn_from + nr_devfns - 1;
if (!dev->dma_alias_mask)
dev->dma_alias_mask = bitmap_zalloc(U8_MAX, GFP_KERNEL);
dev->dma_alias_mask = bitmap_zalloc(MAX_NR_DEVFNS, GFP_KERNEL);
if (!dev->dma_alias_mask) {
pci_warn(dev, "Unable to allocate DMA alias mask\n");
return;
}
set_bit(devfn, dev->dma_alias_mask);
pci_info(dev, "Enabling fixed DMA alias to %02x.%d\n",
PCI_SLOT(devfn), PCI_FUNC(devfn));
bitmap_set(dev->dma_alias_mask, devfn_from, nr_devfns);
if (nr_devfns == 1)
pci_info(dev, "Enabling fixed DMA alias to %02x.%d\n",
PCI_SLOT(devfn_from), PCI_FUNC(devfn_from));
else if (nr_devfns > 1)
pci_info(dev, "Enabling fixed DMA alias for devfn range from %02x.%d to %02x.%d\n",
PCI_SLOT(devfn_from), PCI_FUNC(devfn_from),
PCI_SLOT(devfn_to), PCI_FUNC(devfn_to));
}
bool pci_devs_are_dma_aliases(struct pci_dev *dev1, struct pci_dev *dev2)
@ -6033,7 +6048,9 @@ 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));
test_bit(dev1->devfn, dev2->dma_alias_mask)) ||
pci_real_dma_dev(dev1) == dev2 ||
pci_real_dma_dev(dev2) == dev1;
}
bool pci_device_is_present(struct pci_dev *pdev)
@ -6057,6 +6074,21 @@ void pci_ignore_hotplug(struct pci_dev *dev)
}
EXPORT_SYMBOL_GPL(pci_ignore_hotplug);
/**
* pci_real_dma_dev - Get PCI DMA device for PCI device
* @dev: the PCI device that may have a PCI DMA alias
*
* Permits the platform to provide architecture-specific functionality to
* devices needing to alias DMA to another PCI device on another PCI bus. If
* the PCI device is on the same bus, it is recommended to use
* pci_add_dma_alias(). This is the default implementation. Architecture
* implementations can override this.
*/
struct pci_dev __weak *pci_real_dma_dev(struct pci_dev *dev)
{
return dev;
}
resource_size_t __weak pcibios_default_alignment(void)
{
return 0;

View File

@ -4,6 +4,9 @@
#include <linux/pci.h>
/* Number of possible devfns: 0.0 to 1f.7 inclusive */
#define MAX_NR_DEVFNS 256
#define PCI_FIND_CAP_TTL 48
#define PCI_VSEC_ID_INTEL_TBT 0x1234 /* Thunderbolt */

View File

@ -1445,6 +1445,7 @@ static int aer_probe(struct pcie_device *dev)
return -ENOMEM;
rpc->rpd = port;
INIT_KFIFO(rpc->aer_fifo);
set_service_data(dev, rpc);
status = devm_request_threaded_irq(device, dev->irq, aer_irq, aer_isr,

View File

@ -10,6 +10,8 @@
* Zhang Yanmin (yanmin.zhang@intel.com)
*/
#define dev_fmt(fmt) "AER: " fmt
#include <linux/pci.h>
#include <linux/module.h>
#include <linux/kernel.h>
@ -61,10 +63,12 @@ static int report_error_detected(struct pci_dev *dev,
* error callbacks of "any" device in the subtree, and will
* exit in the disconnected error state.
*/
if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE)
if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {
vote = PCI_ERS_RESULT_NO_AER_DRIVER;
else
pci_info(dev, "can't recover (no error_detected callback)\n");
} else {
vote = PCI_ERS_RESULT_NONE;
}
} else {
err_handler = dev->driver->err_handler;
vote = err_handler->error_detected(dev, state);
@ -233,12 +237,12 @@ void pcie_do_recovery(struct pci_dev *dev, enum pci_channel_state state,
pci_aer_clear_device_status(dev);
pci_cleanup_aer_uncorrect_error_status(dev);
pci_info(dev, "AER: Device recovery successful\n");
pci_info(dev, "device recovery successful\n");
return;
failed:
pci_uevent_ers(dev, PCI_ERS_RESULT_DISCONNECT);
/* TODO: Should kernel panic here? */
pci_info(dev, "AER: Device recovery failed\n");
pci_info(dev, "device recovery failed\n");
}

View File

@ -1871,19 +1871,40 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x2609, quirk_intel_pcie_pm);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x260a, quirk_intel_pcie_pm);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x260b, quirk_intel_pcie_pm);
static void quirk_d3hot_delay(struct pci_dev *dev, unsigned int delay)
{
if (dev->d3_delay >= delay)
return;
dev->d3_delay = delay;
pci_info(dev, "extending delay after power-on from D3hot to %d msec\n",
dev->d3_delay);
}
static void quirk_radeon_pm(struct pci_dev *dev)
{
if (dev->subsystem_vendor == PCI_VENDOR_ID_APPLE &&
dev->subsystem_device == 0x00e2) {
if (dev->d3_delay < 20) {
dev->d3_delay = 20;
pci_info(dev, "extending delay after power-on from D3 to %d msec\n",
dev->d3_delay);
}
}
dev->subsystem_device == 0x00e2)
quirk_d3hot_delay(dev, 20);
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x6741, quirk_radeon_pm);
/*
* Ryzen5/7 XHCI controllers fail upon resume from runtime suspend or s2idle.
* https://bugzilla.kernel.org/show_bug.cgi?id=205587
*
* The kernel attempts to transition these devices to D3cold, but that seems
* to be ineffective on the platforms in question; the PCI device appears to
* remain on in D3hot state. The D3hot-to-D0 transition then requires an
* extended delay in order to succeed.
*/
static void quirk_ryzen_xhci_d3hot(struct pci_dev *dev)
{
quirk_d3hot_delay(dev, 20);
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, 0x15e0, quirk_ryzen_xhci_d3hot);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, 0x15e1, quirk_ryzen_xhci_d3hot);
#ifdef CONFIG_X86_IO_APIC
static int dmi_disable_ioapicreroute(const struct dmi_system_id *d)
{
@ -2381,32 +2402,6 @@ DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_BROADCOM,
PCI_DEVICE_ID_TIGON3_5719,
quirk_brcm_5719_limit_mrrs);
#ifdef CONFIG_PCIE_IPROC_PLATFORM
static void quirk_paxc_bridge(struct pci_dev *pdev)
{
/*
* The PCI config space is shared with the PAXC root port and the first
* Ethernet device. So, we need to workaround this by telling the PCI
* code that the bridge is not an Ethernet device.
*/
if (pdev->hdr_type == PCI_HEADER_TYPE_BRIDGE)
pdev->class = PCI_CLASS_BRIDGE_PCI << 8;
/*
* MPSS is not being set properly (as it is currently 0). This is
* because that area of the PCI config space is hard coded to zero, and
* is not modifiable by firmware. Set this to 2 (e.g., 512 byte MPS)
* so that the MPS can be set to the real max value.
*/
pdev->pcie_mpss = 2;
}
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x16cd, quirk_paxc_bridge);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x16f0, quirk_paxc_bridge);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd750, quirk_paxc_bridge);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd802, quirk_paxc_bridge);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd804, quirk_paxc_bridge);
#endif
/*
* Originally in EDAC sources for i82875P: Intel tells BIOS developers to
* hide device 6 which configures the overflow device access containing the
@ -3932,7 +3927,7 @@ int pci_dev_specific_reset(struct pci_dev *dev, int probe)
static void quirk_dma_func0_alias(struct pci_dev *dev)
{
if (PCI_FUNC(dev->devfn) != 0)
pci_add_dma_alias(dev, PCI_DEVFN(PCI_SLOT(dev->devfn), 0));
pci_add_dma_alias(dev, PCI_DEVFN(PCI_SLOT(dev->devfn), 0), 1);
}
/*
@ -3946,7 +3941,7 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_RICOH, 0xe476, quirk_dma_func0_alias);
static void quirk_dma_func1_alias(struct pci_dev *dev)
{
if (PCI_FUNC(dev->devfn) != 1)
pci_add_dma_alias(dev, PCI_DEVFN(PCI_SLOT(dev->devfn), 1));
pci_add_dma_alias(dev, PCI_DEVFN(PCI_SLOT(dev->devfn), 1), 1);
}
/*
@ -4031,7 +4026,7 @@ static void quirk_fixed_dma_alias(struct pci_dev *dev)
id = pci_match_id(fixed_dma_alias_tbl, dev);
if (id)
pci_add_dma_alias(dev, id->driver_data);
pci_add_dma_alias(dev, id->driver_data, 1);
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ADAPTEC2, 0x0285, quirk_fixed_dma_alias);
@ -4072,9 +4067,9 @@ DECLARE_PCI_FIXUP_HEADER(0x8086, 0x244e, quirk_use_pcie_bridge_dma_alias);
*/
static void quirk_mic_x200_dma_alias(struct pci_dev *pdev)
{
pci_add_dma_alias(pdev, PCI_DEVFN(0x10, 0x0));
pci_add_dma_alias(pdev, PCI_DEVFN(0x11, 0x0));
pci_add_dma_alias(pdev, PCI_DEVFN(0x12, 0x3));
pci_add_dma_alias(pdev, PCI_DEVFN(0x10, 0x0), 1);
pci_add_dma_alias(pdev, PCI_DEVFN(0x11, 0x0), 1);
pci_add_dma_alias(pdev, PCI_DEVFN(0x12, 0x3), 1);
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2260, quirk_mic_x200_dma_alias);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2264, quirk_mic_x200_dma_alias);
@ -4098,13 +4093,8 @@ static void quirk_pex_vca_alias(struct pci_dev *pdev)
const unsigned int num_pci_slots = 0x20;
unsigned int slot;
for (slot = 0; slot < num_pci_slots; slot++) {
pci_add_dma_alias(pdev, PCI_DEVFN(slot, 0x0));
pci_add_dma_alias(pdev, PCI_DEVFN(slot, 0x1));
pci_add_dma_alias(pdev, PCI_DEVFN(slot, 0x2));
pci_add_dma_alias(pdev, PCI_DEVFN(slot, 0x3));
pci_add_dma_alias(pdev, PCI_DEVFN(slot, 0x4));
}
for (slot = 0; slot < num_pci_slots; slot++)
pci_add_dma_alias(pdev, PCI_DEVFN(slot, 0x0), 5);
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2954, quirk_pex_vca_alias);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2955, quirk_pex_vca_alias);
@ -5339,7 +5329,7 @@ static void quirk_switchtec_ntb_dma_alias(struct pci_dev *pdev)
pci_dbg(pdev,
"Aliasing Partition %d Proxy ID %02x.%d\n",
pp, PCI_SLOT(devfn), PCI_FUNC(devfn));
pci_add_dma_alias(pdev, devfn);
pci_add_dma_alias(pdev, devfn, 1);
}
}
@ -5380,6 +5370,39 @@ SWITCHTEC_QUIRK(0x8573); /* PFXI 48XG3 */
SWITCHTEC_QUIRK(0x8574); /* PFXI 64XG3 */
SWITCHTEC_QUIRK(0x8575); /* PFXI 80XG3 */
SWITCHTEC_QUIRK(0x8576); /* PFXI 96XG3 */
SWITCHTEC_QUIRK(0x4000); /* PFX 100XG4 */
SWITCHTEC_QUIRK(0x4084); /* PFX 84XG4 */
SWITCHTEC_QUIRK(0x4068); /* PFX 68XG4 */
SWITCHTEC_QUIRK(0x4052); /* PFX 52XG4 */
SWITCHTEC_QUIRK(0x4036); /* PFX 36XG4 */
SWITCHTEC_QUIRK(0x4028); /* PFX 28XG4 */
SWITCHTEC_QUIRK(0x4100); /* PSX 100XG4 */
SWITCHTEC_QUIRK(0x4184); /* PSX 84XG4 */
SWITCHTEC_QUIRK(0x4168); /* PSX 68XG4 */
SWITCHTEC_QUIRK(0x4152); /* PSX 52XG4 */
SWITCHTEC_QUIRK(0x4136); /* PSX 36XG4 */
SWITCHTEC_QUIRK(0x4128); /* PSX 28XG4 */
SWITCHTEC_QUIRK(0x4200); /* PAX 100XG4 */
SWITCHTEC_QUIRK(0x4284); /* PAX 84XG4 */
SWITCHTEC_QUIRK(0x4268); /* PAX 68XG4 */
SWITCHTEC_QUIRK(0x4252); /* PAX 52XG4 */
SWITCHTEC_QUIRK(0x4236); /* PAX 36XG4 */
SWITCHTEC_QUIRK(0x4228); /* PAX 28XG4 */
/*
* The PLX NTB uses devfn proxy IDs to move TLPs between NT endpoints.
* These IDs are used to forward responses to the originator on the other
* side of the NTB. Alias all possible IDs to the NTB to permit access when
* the IOMMU is turned on.
*/
static void quirk_plx_ntb_dma_alias(struct pci_dev *pdev)
{
pci_info(pdev, "Setting PLX NTB proxy ID aliases\n");
/* PLX NTB may use all 256 devfns */
pci_add_dma_alias(pdev, 0, 256);
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_PLX, 0x87b0, quirk_plx_ntb_dma_alias);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_PLX, 0x87b1, quirk_plx_ntb_dma_alias);
/*
* On Lenovo Thinkpad P50 SKUs with a Nvidia Quadro M1000M, the BIOS does

View File

@ -32,6 +32,12 @@ int pci_for_each_dma_alias(struct pci_dev *pdev,
struct pci_bus *bus;
int ret;
/*
* The device may have an explicit alias requester ID for DMA where the
* requester is on another PCI bus.
*/
pdev = pci_real_dma_dev(pdev);
ret = fn(pdev, pci_dev_id(pdev), data);
if (ret)
return ret;
@ -41,9 +47,9 @@ int pci_for_each_dma_alias(struct pci_dev *pdev,
* DMA, iterate over that too.
*/
if (unlikely(pdev->dma_alias_mask)) {
u8 devfn;
unsigned int devfn;
for_each_set_bit(devfn, pdev->dma_alias_mask, U8_MAX) {
for_each_set_bit(devfn, pdev->dma_alias_mask, MAX_NR_DEVFNS) {
ret = fn(pdev, PCI_DEVID(pdev->bus->number, devfn),
data);
if (ret)

View File

@ -1803,12 +1803,18 @@ again:
/* Restore size and flags */
list_for_each_entry(fail_res, &fail_head, list) {
struct resource *res = fail_res->res;
int idx;
res->start = fail_res->start;
res->end = fail_res->end;
res->flags = fail_res->flags;
if (fail_res->dev->subordinate)
res->flags = 0;
if (pci_is_bridge(fail_res->dev)) {
idx = res - &fail_res->dev->resource[0];
if (idx >= PCI_BRIDGE_RESOURCES &&
idx <= PCI_BRIDGE_RESOURCE_END)
res->flags = 0;
}
}
free_list(&fail_head);
@ -1832,56 +1838,72 @@ void __init pci_assign_unassigned_resources(void)
}
}
static void extend_bridge_window(struct pci_dev *bridge, struct resource *res,
static void adjust_bridge_window(struct pci_dev *bridge, struct resource *res,
struct list_head *add_list,
resource_size_t available)
resource_size_t new_size)
{
struct pci_dev_resource *dev_res;
resource_size_t add_size, size = resource_size(res);
if (res->parent)
return;
if (resource_size(res) >= available)
if (!new_size)
return;
dev_res = res_to_dev_res(add_list, res);
if (!dev_res)
return;
if (new_size > size) {
add_size = new_size - size;
pci_dbg(bridge, "bridge window %pR extended by %pa\n", res,
&add_size);
} else if (new_size < size) {
add_size = size - new_size;
pci_dbg(bridge, "bridge window %pR shrunken by %pa\n", res,
&add_size);
}
/* Is there room to extend the window? */
if (available - resource_size(res) <= dev_res->add_size)
return;
dev_res->add_size = available - resource_size(res);
pci_dbg(bridge, "bridge window %pR extended by %pa\n", res,
&dev_res->add_size);
res->end = res->start + new_size - 1;
remove_from_list(add_list, res);
}
static void pci_bus_distribute_available_resources(struct pci_bus *bus,
struct list_head *add_list,
resource_size_t available_io,
resource_size_t available_mmio,
resource_size_t available_mmio_pref)
struct resource io,
struct resource mmio,
struct resource mmio_pref)
{
resource_size_t remaining_io, remaining_mmio, remaining_mmio_pref;
unsigned int normal_bridges = 0, hotplug_bridges = 0;
struct resource *io_res, *mmio_res, *mmio_pref_res;
struct pci_dev *dev, *bridge = bus->self;
resource_size_t io_per_hp, mmio_per_hp, mmio_pref_per_hp, align;
io_res = &bridge->resource[PCI_BRIDGE_RESOURCES + 0];
mmio_res = &bridge->resource[PCI_BRIDGE_RESOURCES + 1];
mmio_pref_res = &bridge->resource[PCI_BRIDGE_RESOURCES + 2];
/*
* Update additional resource list (add_list) to fill all the
* extra resource space available for this port except the space
* calculated in __pci_bus_size_bridges() which covers all the
* devices currently connected to the port and below.
* The alignment of this bridge is yet to be considered, hence it must
* be done now before extending its bridge window.
*/
extend_bridge_window(bridge, io_res, add_list, available_io);
extend_bridge_window(bridge, mmio_res, add_list, available_mmio);
extend_bridge_window(bridge, mmio_pref_res, add_list,
available_mmio_pref);
align = pci_resource_alignment(bridge, io_res);
if (!io_res->parent && align)
io.start = min(ALIGN(io.start, align), io.end + 1);
align = pci_resource_alignment(bridge, mmio_res);
if (!mmio_res->parent && align)
mmio.start = min(ALIGN(mmio.start, align), mmio.end + 1);
align = pci_resource_alignment(bridge, mmio_pref_res);
if (!mmio_pref_res->parent && align)
mmio_pref.start = min(ALIGN(mmio_pref.start, align),
mmio_pref.end + 1);
/*
* Now that we have adjusted for alignment, update the bridge window
* resources to fill as much remaining resource space as possible.
*/
adjust_bridge_window(bridge, io_res, add_list, resource_size(&io));
adjust_bridge_window(bridge, mmio_res, add_list, resource_size(&mmio));
adjust_bridge_window(bridge, mmio_pref_res, add_list,
resource_size(&mmio_pref));
/*
* Calculate how many hotplug bridges and normal bridges there
@ -1902,11 +1924,9 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus,
*/
if (hotplug_bridges + normal_bridges == 1) {
dev = list_first_entry(&bus->devices, struct pci_dev, bus_list);
if (dev->subordinate) {
if (dev->subordinate)
pci_bus_distribute_available_resources(dev->subordinate,
add_list, available_io, available_mmio,
available_mmio_pref);
}
add_list, io, mmio, mmio_pref);
return;
}
@ -1919,12 +1939,9 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus,
* extra space reduced by the minimal required space for the
* non-hotplug bridges.
*/
remaining_io = available_io;
remaining_mmio = available_mmio;
remaining_mmio_pref = available_mmio_pref;
for_each_pci_bridge(dev, bus) {
const struct resource *res;
resource_size_t used_size;
struct resource *res;
if (dev->is_hotplug_bridge)
continue;
@ -1934,24 +1951,39 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus,
* bridge and devices below it occupy.
*/
res = &dev->resource[PCI_BRIDGE_RESOURCES + 0];
if (!res->parent && available_io > resource_size(res))
remaining_io -= resource_size(res);
align = pci_resource_alignment(dev, res);
align = align ? ALIGN(io.start, align) - io.start : 0;
used_size = align + resource_size(res);
if (!res->parent)
io.start = min(io.start + used_size, io.end + 1);
res = &dev->resource[PCI_BRIDGE_RESOURCES + 1];
if (!res->parent && available_mmio > resource_size(res))
remaining_mmio -= resource_size(res);
align = pci_resource_alignment(dev, res);
align = align ? ALIGN(mmio.start, align) - mmio.start : 0;
used_size = align + resource_size(res);
if (!res->parent)
mmio.start = min(mmio.start + used_size, mmio.end + 1);
res = &dev->resource[PCI_BRIDGE_RESOURCES + 2];
if (!res->parent && available_mmio_pref > resource_size(res))
remaining_mmio_pref -= resource_size(res);
align = pci_resource_alignment(dev, res);
align = align ? ALIGN(mmio_pref.start, align) -
mmio_pref.start : 0;
used_size = align + resource_size(res);
if (!res->parent)
mmio_pref.start = min(mmio_pref.start + used_size,
mmio_pref.end + 1);
}
io_per_hp = div64_ul(resource_size(&io), hotplug_bridges);
mmio_per_hp = div64_ul(resource_size(&mmio), hotplug_bridges);
mmio_pref_per_hp = div64_ul(resource_size(&mmio_pref),
hotplug_bridges);
/*
* Go over devices on this bus and distribute the remaining
* resource space between hotplug bridges.
*/
for_each_pci_bridge(dev, bus) {
resource_size_t align, io, mmio, mmio_pref;
struct pci_bus *b;
b = dev->subordinate;
@ -1963,42 +1995,31 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus,
* hotplug-capable downstream ports taking alignment into
* account.
*/
align = pci_resource_alignment(bridge, io_res);
io = div64_ul(available_io, hotplug_bridges);
io = min(ALIGN(io, align), remaining_io);
remaining_io -= io;
align = pci_resource_alignment(bridge, mmio_res);
mmio = div64_ul(available_mmio, hotplug_bridges);
mmio = min(ALIGN(mmio, align), remaining_mmio);
remaining_mmio -= mmio;
align = pci_resource_alignment(bridge, mmio_pref_res);
mmio_pref = div64_ul(available_mmio_pref, hotplug_bridges);
mmio_pref = min(ALIGN(mmio_pref, align), remaining_mmio_pref);
remaining_mmio_pref -= mmio_pref;
io.end = io.start + io_per_hp - 1;
mmio.end = mmio.start + mmio_per_hp - 1;
mmio_pref.end = mmio_pref.start + mmio_pref_per_hp - 1;
pci_bus_distribute_available_resources(b, add_list, io, mmio,
mmio_pref);
io.start += io_per_hp;
mmio.start += mmio_per_hp;
mmio_pref.start += mmio_pref_per_hp;
}
}
static void pci_bridge_distribute_available_resources(struct pci_dev *bridge,
struct list_head *add_list)
{
resource_size_t available_io, available_mmio, available_mmio_pref;
const struct resource *res;
struct resource available_io, available_mmio, available_mmio_pref;
if (!bridge->is_hotplug_bridge)
return;
/* Take the initial extra resources from the hotplug port */
res = &bridge->resource[PCI_BRIDGE_RESOURCES + 0];
available_io = resource_size(res);
res = &bridge->resource[PCI_BRIDGE_RESOURCES + 1];
available_mmio = resource_size(res);
res = &bridge->resource[PCI_BRIDGE_RESOURCES + 2];
available_mmio_pref = resource_size(res);
available_io = bridge->resource[PCI_BRIDGE_RESOURCES + 0];
available_mmio = bridge->resource[PCI_BRIDGE_RESOURCES + 1];
available_mmio_pref = bridge->resource[PCI_BRIDGE_RESOURCES + 2];
pci_bus_distribute_available_resources(bridge->subordinate,
add_list, available_io,
@ -2055,12 +2076,18 @@ again:
/* Restore size and flags */
list_for_each_entry(fail_res, &fail_head, list) {
struct resource *res = fail_res->res;
int idx;
res->start = fail_res->start;
res->end = fail_res->end;
res->flags = fail_res->flags;
if (fail_res->dev->subordinate)
res->flags = 0;
if (pci_is_bridge(fail_res->dev)) {
idx = res - &fail_res->dev->resource[0];
if (idx >= PCI_BRIDGE_RESOURCES &&
idx <= PCI_BRIDGE_RESOURCE_END)
res->flags = 0;
}
}
free_list(&fail_head);

View File

@ -317,8 +317,15 @@ static ssize_t field ## _show(struct device *dev, \
struct device_attribute *attr, char *buf) \
{ \
struct switchtec_dev *stdev = to_stdev(dev); \
return io_string_show(buf, &stdev->mmio_sys_info->field, \
sizeof(stdev->mmio_sys_info->field)); \
struct sys_info_regs __iomem *si = stdev->mmio_sys_info; \
if (stdev->gen == SWITCHTEC_GEN3) \
return io_string_show(buf, &si->gen3.field, \
sizeof(si->gen3.field)); \
else if (stdev->gen == SWITCHTEC_GEN4) \
return io_string_show(buf, &si->gen4.field, \
sizeof(si->gen4.field)); \
else \
return -ENOTSUPP; \
} \
\
static DEVICE_ATTR_RO(field)
@ -326,13 +333,31 @@ static DEVICE_ATTR_RO(field)
DEVICE_ATTR_SYS_INFO_STR(vendor_id);
DEVICE_ATTR_SYS_INFO_STR(product_id);
DEVICE_ATTR_SYS_INFO_STR(product_revision);
DEVICE_ATTR_SYS_INFO_STR(component_vendor);
static ssize_t component_vendor_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct switchtec_dev *stdev = to_stdev(dev);
struct sys_info_regs __iomem *si = stdev->mmio_sys_info;
/* component_vendor field not supported after gen3 */
if (stdev->gen != SWITCHTEC_GEN3)
return sprintf(buf, "none\n");
return io_string_show(buf, &si->gen3.component_vendor,
sizeof(si->gen3.component_vendor));
}
static DEVICE_ATTR_RO(component_vendor);
static ssize_t component_id_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct switchtec_dev *stdev = to_stdev(dev);
int id = ioread16(&stdev->mmio_sys_info->component_id);
int id = ioread16(&stdev->mmio_sys_info->gen3.component_id);
/* component_id field not supported after gen3 */
if (stdev->gen != SWITCHTEC_GEN3)
return sprintf(buf, "none\n");
return sprintf(buf, "PM%04X\n", id);
}
@ -342,7 +367,11 @@ static ssize_t component_revision_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct switchtec_dev *stdev = to_stdev(dev);
int rev = ioread8(&stdev->mmio_sys_info->component_revision);
int rev = ioread8(&stdev->mmio_sys_info->gen3.component_revision);
/* component_revision field not supported after gen3 */
if (stdev->gen != SWITCHTEC_GEN3)
return sprintf(buf, "255\n");
return sprintf(buf, "%d\n", rev);
}
@ -450,6 +479,12 @@ static ssize_t switchtec_dev_write(struct file *filp, const char __user *data,
rc = -EFAULT;
goto out;
}
if (((MRPC_CMD_ID(stuser->cmd) == MRPC_GAS_WRITE) ||
(MRPC_CMD_ID(stuser->cmd) == MRPC_GAS_READ)) &&
!capable(CAP_SYS_ADMIN)) {
rc = -EPERM;
goto out;
}
data += sizeof(stuser->cmd);
rc = copy_from_user(&stuser->data, data, size - sizeof(stuser->cmd));
@ -568,8 +603,15 @@ static int ioctl_flash_info(struct switchtec_dev *stdev,
struct switchtec_ioctl_flash_info info = {0};
struct flash_info_regs __iomem *fi = stdev->mmio_flash_info;
info.flash_length = ioread32(&fi->flash_length);
info.num_partitions = SWITCHTEC_IOCTL_NUM_PARTITIONS;
if (stdev->gen == SWITCHTEC_GEN3) {
info.flash_length = ioread32(&fi->gen3.flash_length);
info.num_partitions = SWITCHTEC_NUM_PARTITIONS_GEN3;
} else if (stdev->gen == SWITCHTEC_GEN4) {
info.flash_length = ioread32(&fi->gen4.flash_length);
info.num_partitions = SWITCHTEC_NUM_PARTITIONS_GEN4;
} else {
return -ENOTSUPP;
}
if (copy_to_user(uinfo, &info, sizeof(info)))
return -EFAULT;
@ -584,75 +626,200 @@ static void set_fw_info_part(struct switchtec_ioctl_flash_part_info *info,
info->length = ioread32(&pi->length);
}
static int ioctl_flash_part_info(struct switchtec_dev *stdev,
struct switchtec_ioctl_flash_part_info __user *uinfo)
static int flash_part_info_gen3(struct switchtec_dev *stdev,
struct switchtec_ioctl_flash_part_info *info)
{
struct switchtec_ioctl_flash_part_info info = {0};
struct flash_info_regs __iomem *fi = stdev->mmio_flash_info;
struct sys_info_regs __iomem *si = stdev->mmio_sys_info;
struct flash_info_regs_gen3 __iomem *fi =
&stdev->mmio_flash_info->gen3;
struct sys_info_regs_gen3 __iomem *si = &stdev->mmio_sys_info->gen3;
u32 active_addr = -1;
if (copy_from_user(&info, uinfo, sizeof(info)))
return -EFAULT;
switch (info.flash_partition) {
switch (info->flash_partition) {
case SWITCHTEC_IOCTL_PART_CFG0:
active_addr = ioread32(&fi->active_cfg);
set_fw_info_part(&info, &fi->cfg0);
if (ioread16(&si->cfg_running) == SWITCHTEC_CFG0_RUNNING)
info.active |= SWITCHTEC_IOCTL_PART_RUNNING;
set_fw_info_part(info, &fi->cfg0);
if (ioread16(&si->cfg_running) == SWITCHTEC_GEN3_CFG0_RUNNING)
info->active |= SWITCHTEC_IOCTL_PART_RUNNING;
break;
case SWITCHTEC_IOCTL_PART_CFG1:
active_addr = ioread32(&fi->active_cfg);
set_fw_info_part(&info, &fi->cfg1);
if (ioread16(&si->cfg_running) == SWITCHTEC_CFG1_RUNNING)
info.active |= SWITCHTEC_IOCTL_PART_RUNNING;
set_fw_info_part(info, &fi->cfg1);
if (ioread16(&si->cfg_running) == SWITCHTEC_GEN3_CFG1_RUNNING)
info->active |= SWITCHTEC_IOCTL_PART_RUNNING;
break;
case SWITCHTEC_IOCTL_PART_IMG0:
active_addr = ioread32(&fi->active_img);
set_fw_info_part(&info, &fi->img0);
if (ioread16(&si->img_running) == SWITCHTEC_IMG0_RUNNING)
info.active |= SWITCHTEC_IOCTL_PART_RUNNING;
set_fw_info_part(info, &fi->img0);
if (ioread16(&si->img_running) == SWITCHTEC_GEN3_IMG0_RUNNING)
info->active |= SWITCHTEC_IOCTL_PART_RUNNING;
break;
case SWITCHTEC_IOCTL_PART_IMG1:
active_addr = ioread32(&fi->active_img);
set_fw_info_part(&info, &fi->img1);
if (ioread16(&si->img_running) == SWITCHTEC_IMG1_RUNNING)
info.active |= SWITCHTEC_IOCTL_PART_RUNNING;
set_fw_info_part(info, &fi->img1);
if (ioread16(&si->img_running) == SWITCHTEC_GEN3_IMG1_RUNNING)
info->active |= SWITCHTEC_IOCTL_PART_RUNNING;
break;
case SWITCHTEC_IOCTL_PART_NVLOG:
set_fw_info_part(&info, &fi->nvlog);
set_fw_info_part(info, &fi->nvlog);
break;
case SWITCHTEC_IOCTL_PART_VENDOR0:
set_fw_info_part(&info, &fi->vendor[0]);
set_fw_info_part(info, &fi->vendor[0]);
break;
case SWITCHTEC_IOCTL_PART_VENDOR1:
set_fw_info_part(&info, &fi->vendor[1]);
set_fw_info_part(info, &fi->vendor[1]);
break;
case SWITCHTEC_IOCTL_PART_VENDOR2:
set_fw_info_part(&info, &fi->vendor[2]);
set_fw_info_part(info, &fi->vendor[2]);
break;
case SWITCHTEC_IOCTL_PART_VENDOR3:
set_fw_info_part(&info, &fi->vendor[3]);
set_fw_info_part(info, &fi->vendor[3]);
break;
case SWITCHTEC_IOCTL_PART_VENDOR4:
set_fw_info_part(&info, &fi->vendor[4]);
set_fw_info_part(info, &fi->vendor[4]);
break;
case SWITCHTEC_IOCTL_PART_VENDOR5:
set_fw_info_part(&info, &fi->vendor[5]);
set_fw_info_part(info, &fi->vendor[5]);
break;
case SWITCHTEC_IOCTL_PART_VENDOR6:
set_fw_info_part(&info, &fi->vendor[6]);
set_fw_info_part(info, &fi->vendor[6]);
break;
case SWITCHTEC_IOCTL_PART_VENDOR7:
set_fw_info_part(&info, &fi->vendor[7]);
set_fw_info_part(info, &fi->vendor[7]);
break;
default:
return -EINVAL;
}
if (info.address == active_addr)
info.active |= SWITCHTEC_IOCTL_PART_ACTIVE;
if (info->address == active_addr)
info->active |= SWITCHTEC_IOCTL_PART_ACTIVE;
return 0;
}
static int flash_part_info_gen4(struct switchtec_dev *stdev,
struct switchtec_ioctl_flash_part_info *info)
{
struct flash_info_regs_gen4 __iomem *fi = &stdev->mmio_flash_info->gen4;
struct sys_info_regs_gen4 __iomem *si = &stdev->mmio_sys_info->gen4;
struct active_partition_info_gen4 __iomem *af = &fi->active_flag;
switch (info->flash_partition) {
case SWITCHTEC_IOCTL_PART_MAP_0:
set_fw_info_part(info, &fi->map0);
break;
case SWITCHTEC_IOCTL_PART_MAP_1:
set_fw_info_part(info, &fi->map1);
break;
case SWITCHTEC_IOCTL_PART_KEY_0:
set_fw_info_part(info, &fi->key0);
if (ioread8(&af->key) == SWITCHTEC_GEN4_KEY0_ACTIVE)
info->active |= SWITCHTEC_IOCTL_PART_ACTIVE;
if (ioread16(&si->key_running) == SWITCHTEC_GEN4_KEY0_RUNNING)
info->active |= SWITCHTEC_IOCTL_PART_RUNNING;
break;
case SWITCHTEC_IOCTL_PART_KEY_1:
set_fw_info_part(info, &fi->key1);
if (ioread8(&af->key) == SWITCHTEC_GEN4_KEY1_ACTIVE)
info->active |= SWITCHTEC_IOCTL_PART_ACTIVE;
if (ioread16(&si->key_running) == SWITCHTEC_GEN4_KEY1_RUNNING)
info->active |= SWITCHTEC_IOCTL_PART_RUNNING;
break;
case SWITCHTEC_IOCTL_PART_BL2_0:
set_fw_info_part(info, &fi->bl2_0);
if (ioread8(&af->bl2) == SWITCHTEC_GEN4_BL2_0_ACTIVE)
info->active |= SWITCHTEC_IOCTL_PART_ACTIVE;
if (ioread16(&si->bl2_running) == SWITCHTEC_GEN4_BL2_0_RUNNING)
info->active |= SWITCHTEC_IOCTL_PART_RUNNING;
break;
case SWITCHTEC_IOCTL_PART_BL2_1:
set_fw_info_part(info, &fi->bl2_1);
if (ioread8(&af->bl2) == SWITCHTEC_GEN4_BL2_1_ACTIVE)
info->active |= SWITCHTEC_IOCTL_PART_ACTIVE;
if (ioread16(&si->bl2_running) == SWITCHTEC_GEN4_BL2_1_RUNNING)
info->active |= SWITCHTEC_IOCTL_PART_RUNNING;
break;
case SWITCHTEC_IOCTL_PART_CFG0:
set_fw_info_part(info, &fi->cfg0);
if (ioread8(&af->cfg) == SWITCHTEC_GEN4_CFG0_ACTIVE)
info->active |= SWITCHTEC_IOCTL_PART_ACTIVE;
if (ioread16(&si->cfg_running) == SWITCHTEC_GEN4_CFG0_RUNNING)
info->active |= SWITCHTEC_IOCTL_PART_RUNNING;
break;
case SWITCHTEC_IOCTL_PART_CFG1:
set_fw_info_part(info, &fi->cfg1);
if (ioread8(&af->cfg) == SWITCHTEC_GEN4_CFG1_ACTIVE)
info->active |= SWITCHTEC_IOCTL_PART_ACTIVE;
if (ioread16(&si->cfg_running) == SWITCHTEC_GEN4_CFG1_RUNNING)
info->active |= SWITCHTEC_IOCTL_PART_RUNNING;
break;
case SWITCHTEC_IOCTL_PART_IMG0:
set_fw_info_part(info, &fi->img0);
if (ioread8(&af->img) == SWITCHTEC_GEN4_IMG0_ACTIVE)
info->active |= SWITCHTEC_IOCTL_PART_ACTIVE;
if (ioread16(&si->img_running) == SWITCHTEC_GEN4_IMG0_RUNNING)
info->active |= SWITCHTEC_IOCTL_PART_RUNNING;
break;
case SWITCHTEC_IOCTL_PART_IMG1:
set_fw_info_part(info, &fi->img1);
if (ioread8(&af->img) == SWITCHTEC_GEN4_IMG1_ACTIVE)
info->active |= SWITCHTEC_IOCTL_PART_ACTIVE;
if (ioread16(&si->img_running) == SWITCHTEC_GEN4_IMG1_RUNNING)
info->active |= SWITCHTEC_IOCTL_PART_RUNNING;
break;
case SWITCHTEC_IOCTL_PART_NVLOG:
set_fw_info_part(info, &fi->nvlog);
break;
case SWITCHTEC_IOCTL_PART_VENDOR0:
set_fw_info_part(info, &fi->vendor[0]);
break;
case SWITCHTEC_IOCTL_PART_VENDOR1:
set_fw_info_part(info, &fi->vendor[1]);
break;
case SWITCHTEC_IOCTL_PART_VENDOR2:
set_fw_info_part(info, &fi->vendor[2]);
break;
case SWITCHTEC_IOCTL_PART_VENDOR3:
set_fw_info_part(info, &fi->vendor[3]);
break;
case SWITCHTEC_IOCTL_PART_VENDOR4:
set_fw_info_part(info, &fi->vendor[4]);
break;
case SWITCHTEC_IOCTL_PART_VENDOR5:
set_fw_info_part(info, &fi->vendor[5]);
break;
case SWITCHTEC_IOCTL_PART_VENDOR6:
set_fw_info_part(info, &fi->vendor[6]);
break;
case SWITCHTEC_IOCTL_PART_VENDOR7:
set_fw_info_part(info, &fi->vendor[7]);
break;
default:
return -EINVAL;
}
return 0;
}
static int ioctl_flash_part_info(struct switchtec_dev *stdev,
struct switchtec_ioctl_flash_part_info __user *uinfo)
{
int ret;
struct switchtec_ioctl_flash_part_info info = {0};
if (copy_from_user(&info, uinfo, sizeof(info)))
return -EFAULT;
if (stdev->gen == SWITCHTEC_GEN3) {
ret = flash_part_info_gen3(stdev, &info);
if (ret)
return ret;
} else if (stdev->gen == SWITCHTEC_GEN4) {
ret = flash_part_info_gen4(stdev, &info);
if (ret)
return ret;
} else {
return -ENOTSUPP;
}
if (copy_to_user(uinfo, &info, sizeof(info)))
return -EFAULT;
@ -683,11 +850,7 @@ static int ioctl_event_summary(struct switchtec_dev *stdev,
s->part[i] = reg;
}
for (i = 0; i < SWITCHTEC_MAX_PFF_CSR; i++) {
reg = ioread16(&stdev->mmio_pff_csr[i].vendor_id);
if (reg != PCI_VENDOR_ID_MICROSEMI)
break;
for (i = 0; i < stdev->pff_csr_count; i++) {
reg = ioread32(&stdev->mmio_pff_csr[i].pff_event_summary);
s->pff[i] = reg;
}
@ -751,10 +914,13 @@ static const struct event_reg {
EV_PAR(SWITCHTEC_IOCTL_EVENT_MRPC_COMP, mrpc_comp_hdr),
EV_PAR(SWITCHTEC_IOCTL_EVENT_MRPC_COMP_ASYNC, mrpc_comp_async_hdr),
EV_PAR(SWITCHTEC_IOCTL_EVENT_DYN_PART_BIND_COMP, dyn_binding_hdr),
EV_PAR(SWITCHTEC_IOCTL_EVENT_INTERCOMM_REQ_NOTIFY,
intercomm_notify_hdr),
EV_PFF(SWITCHTEC_IOCTL_EVENT_AER_IN_P2P, aer_in_p2p_hdr),
EV_PFF(SWITCHTEC_IOCTL_EVENT_AER_IN_VEP, aer_in_vep_hdr),
EV_PFF(SWITCHTEC_IOCTL_EVENT_DPC, dpc_hdr),
EV_PFF(SWITCHTEC_IOCTL_EVENT_CTS, cts_hdr),
EV_PFF(SWITCHTEC_IOCTL_EVENT_UEC, uec_hdr),
EV_PFF(SWITCHTEC_IOCTL_EVENT_HOTPLUG, hotplug_hdr),
EV_PFF(SWITCHTEC_IOCTL_EVENT_IER, ier_hdr),
EV_PFF(SWITCHTEC_IOCTL_EVENT_THRESH, threshold_hdr),
@ -1181,10 +1347,6 @@ static int mask_event(struct switchtec_dev *stdev, int eid, int idx)
if (!(hdr & SWITCHTEC_EVENT_OCCURRED && hdr & SWITCHTEC_EVENT_EN_IRQ))
return 0;
if (eid == SWITCHTEC_IOCTL_EVENT_LINK_STATE ||
eid == SWITCHTEC_IOCTL_EVENT_MRPC_COMP)
return 0;
dev_dbg(&stdev->dev, "%s: %d %d %x\n", __func__, eid, idx, hdr);
hdr &= ~(SWITCHTEC_EVENT_EN_IRQ | SWITCHTEC_EVENT_OCCURRED);
iowrite32(hdr, hdr_reg);
@ -1231,8 +1393,13 @@ static irqreturn_t switchtec_event_isr(int irq, void *dev)
check_link_state_events(stdev);
for (eid = 0; eid < SWITCHTEC_IOCTL_MAX_EVENTS; eid++)
for (eid = 0; eid < SWITCHTEC_IOCTL_MAX_EVENTS; eid++) {
if (eid == SWITCHTEC_IOCTL_EVENT_LINK_STATE ||
eid == SWITCHTEC_IOCTL_EVENT_MRPC_COMP)
continue;
event_count += mask_all_events(stdev, eid);
}
if (event_count) {
atomic_inc(&stdev->event_cnt);
@ -1276,7 +1443,7 @@ static int switchtec_init_isr(struct switchtec_dev *stdev)
if (nvecs < 0)
return nvecs;
event_irq = ioread32(&stdev->mmio_part_cfg->vep_vector_number);
event_irq = ioread16(&stdev->mmio_part_cfg->vep_vector_number);
if (event_irq < 0 || event_irq >= nvecs)
return -EFAULT;
@ -1324,16 +1491,16 @@ static void init_pff(struct switchtec_dev *stdev)
stdev->pff_csr_count = i;
reg = ioread32(&pcfg->usp_pff_inst_id);
if (reg < SWITCHTEC_MAX_PFF_CSR)
if (reg < stdev->pff_csr_count)
stdev->pff_local[reg] = 1;
reg = ioread32(&pcfg->vep_pff_inst_id);
if (reg < SWITCHTEC_MAX_PFF_CSR)
if (reg < stdev->pff_csr_count)
stdev->pff_local[reg] = 1;
for (i = 0; i < ARRAY_SIZE(pcfg->dsp_pff_inst_id); i++) {
reg = ioread32(&pcfg->dsp_pff_inst_id[i]);
if (reg < SWITCHTEC_MAX_PFF_CSR)
if (reg < stdev->pff_csr_count)
stdev->pff_local[reg] = 1;
}
}
@ -1344,12 +1511,13 @@ static int switchtec_init_pci(struct switchtec_dev *stdev,
int rc;
void __iomem *map;
unsigned long res_start, res_len;
u32 __iomem *part_id;
rc = pcim_enable_device(pdev);
if (rc)
return rc;
rc = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64));
rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
if (rc)
return rc;
@ -1378,7 +1546,15 @@ static int switchtec_init_pci(struct switchtec_dev *stdev,
stdev->mmio_sys_info = stdev->mmio + SWITCHTEC_GAS_SYS_INFO_OFFSET;
stdev->mmio_flash_info = stdev->mmio + SWITCHTEC_GAS_FLASH_INFO_OFFSET;
stdev->mmio_ntb = stdev->mmio + SWITCHTEC_GAS_NTB_OFFSET;
stdev->partition = ioread8(&stdev->mmio_sys_info->partition_id);
if (stdev->gen == SWITCHTEC_GEN3)
part_id = &stdev->mmio_sys_info->gen3.partition_id;
else if (stdev->gen == SWITCHTEC_GEN4)
part_id = &stdev->mmio_sys_info->gen4.partition_id;
else
return -ENOTSUPP;
stdev->partition = ioread8(part_id);
stdev->partition_count = ioread8(&stdev->mmio_ntb->partition_count);
stdev->mmio_part_cfg_all = stdev->mmio + SWITCHTEC_GAS_PART_CFG_OFFSET;
stdev->mmio_part_cfg = &stdev->mmio_part_cfg_all[stdev->partition];
@ -1420,6 +1596,8 @@ static int switchtec_pci_probe(struct pci_dev *pdev,
if (IS_ERR(stdev))
return PTR_ERR(stdev);
stdev->gen = id->driver_data;
rc = switchtec_init_pci(stdev, pdev);
if (rc)
goto err_put;
@ -1467,7 +1645,7 @@ static void switchtec_pci_remove(struct pci_dev *pdev)
put_device(&stdev->dev);
}
#define SWITCHTEC_PCI_DEVICE(device_id) \
#define SWITCHTEC_PCI_DEVICE(device_id, gen) \
{ \
.vendor = PCI_VENDOR_ID_MICROSEMI, \
.device = device_id, \
@ -1475,6 +1653,7 @@ static void switchtec_pci_remove(struct pci_dev *pdev)
.subdevice = PCI_ANY_ID, \
.class = (PCI_CLASS_MEMORY_OTHER << 8), \
.class_mask = 0xFFFFFFFF, \
.driver_data = gen, \
}, \
{ \
.vendor = PCI_VENDOR_ID_MICROSEMI, \
@ -1483,39 +1662,58 @@ static void switchtec_pci_remove(struct pci_dev *pdev)
.subdevice = PCI_ANY_ID, \
.class = (PCI_CLASS_BRIDGE_OTHER << 8), \
.class_mask = 0xFFFFFFFF, \
.driver_data = gen, \
}
static const struct pci_device_id switchtec_pci_tbl[] = {
SWITCHTEC_PCI_DEVICE(0x8531), //PFX 24xG3
SWITCHTEC_PCI_DEVICE(0x8532), //PFX 32xG3
SWITCHTEC_PCI_DEVICE(0x8533), //PFX 48xG3
SWITCHTEC_PCI_DEVICE(0x8534), //PFX 64xG3
SWITCHTEC_PCI_DEVICE(0x8535), //PFX 80xG3
SWITCHTEC_PCI_DEVICE(0x8536), //PFX 96xG3
SWITCHTEC_PCI_DEVICE(0x8541), //PSX 24xG3
SWITCHTEC_PCI_DEVICE(0x8542), //PSX 32xG3
SWITCHTEC_PCI_DEVICE(0x8543), //PSX 48xG3
SWITCHTEC_PCI_DEVICE(0x8544), //PSX 64xG3
SWITCHTEC_PCI_DEVICE(0x8545), //PSX 80xG3
SWITCHTEC_PCI_DEVICE(0x8546), //PSX 96xG3
SWITCHTEC_PCI_DEVICE(0x8551), //PAX 24XG3
SWITCHTEC_PCI_DEVICE(0x8552), //PAX 32XG3
SWITCHTEC_PCI_DEVICE(0x8553), //PAX 48XG3
SWITCHTEC_PCI_DEVICE(0x8554), //PAX 64XG3
SWITCHTEC_PCI_DEVICE(0x8555), //PAX 80XG3
SWITCHTEC_PCI_DEVICE(0x8556), //PAX 96XG3
SWITCHTEC_PCI_DEVICE(0x8561), //PFXL 24XG3
SWITCHTEC_PCI_DEVICE(0x8562), //PFXL 32XG3
SWITCHTEC_PCI_DEVICE(0x8563), //PFXL 48XG3
SWITCHTEC_PCI_DEVICE(0x8564), //PFXL 64XG3
SWITCHTEC_PCI_DEVICE(0x8565), //PFXL 80XG3
SWITCHTEC_PCI_DEVICE(0x8566), //PFXL 96XG3
SWITCHTEC_PCI_DEVICE(0x8571), //PFXI 24XG3
SWITCHTEC_PCI_DEVICE(0x8572), //PFXI 32XG3
SWITCHTEC_PCI_DEVICE(0x8573), //PFXI 48XG3
SWITCHTEC_PCI_DEVICE(0x8574), //PFXI 64XG3
SWITCHTEC_PCI_DEVICE(0x8575), //PFXI 80XG3
SWITCHTEC_PCI_DEVICE(0x8576), //PFXI 96XG3
SWITCHTEC_PCI_DEVICE(0x8531, SWITCHTEC_GEN3), //PFX 24xG3
SWITCHTEC_PCI_DEVICE(0x8532, SWITCHTEC_GEN3), //PFX 32xG3
SWITCHTEC_PCI_DEVICE(0x8533, SWITCHTEC_GEN3), //PFX 48xG3
SWITCHTEC_PCI_DEVICE(0x8534, SWITCHTEC_GEN3), //PFX 64xG3
SWITCHTEC_PCI_DEVICE(0x8535, SWITCHTEC_GEN3), //PFX 80xG3
SWITCHTEC_PCI_DEVICE(0x8536, SWITCHTEC_GEN3), //PFX 96xG3
SWITCHTEC_PCI_DEVICE(0x8541, SWITCHTEC_GEN3), //PSX 24xG3
SWITCHTEC_PCI_DEVICE(0x8542, SWITCHTEC_GEN3), //PSX 32xG3
SWITCHTEC_PCI_DEVICE(0x8543, SWITCHTEC_GEN3), //PSX 48xG3
SWITCHTEC_PCI_DEVICE(0x8544, SWITCHTEC_GEN3), //PSX 64xG3
SWITCHTEC_PCI_DEVICE(0x8545, SWITCHTEC_GEN3), //PSX 80xG3
SWITCHTEC_PCI_DEVICE(0x8546, SWITCHTEC_GEN3), //PSX 96xG3
SWITCHTEC_PCI_DEVICE(0x8551, SWITCHTEC_GEN3), //PAX 24XG3
SWITCHTEC_PCI_DEVICE(0x8552, SWITCHTEC_GEN3), //PAX 32XG3
SWITCHTEC_PCI_DEVICE(0x8553, SWITCHTEC_GEN3), //PAX 48XG3
SWITCHTEC_PCI_DEVICE(0x8554, SWITCHTEC_GEN3), //PAX 64XG3
SWITCHTEC_PCI_DEVICE(0x8555, SWITCHTEC_GEN3), //PAX 80XG3
SWITCHTEC_PCI_DEVICE(0x8556, SWITCHTEC_GEN3), //PAX 96XG3
SWITCHTEC_PCI_DEVICE(0x8561, SWITCHTEC_GEN3), //PFXL 24XG3
SWITCHTEC_PCI_DEVICE(0x8562, SWITCHTEC_GEN3), //PFXL 32XG3
SWITCHTEC_PCI_DEVICE(0x8563, SWITCHTEC_GEN3), //PFXL 48XG3
SWITCHTEC_PCI_DEVICE(0x8564, SWITCHTEC_GEN3), //PFXL 64XG3
SWITCHTEC_PCI_DEVICE(0x8565, SWITCHTEC_GEN3), //PFXL 80XG3
SWITCHTEC_PCI_DEVICE(0x8566, SWITCHTEC_GEN3), //PFXL 96XG3
SWITCHTEC_PCI_DEVICE(0x8571, SWITCHTEC_GEN3), //PFXI 24XG3
SWITCHTEC_PCI_DEVICE(0x8572, SWITCHTEC_GEN3), //PFXI 32XG3
SWITCHTEC_PCI_DEVICE(0x8573, SWITCHTEC_GEN3), //PFXI 48XG3
SWITCHTEC_PCI_DEVICE(0x8574, SWITCHTEC_GEN3), //PFXI 64XG3
SWITCHTEC_PCI_DEVICE(0x8575, SWITCHTEC_GEN3), //PFXI 80XG3
SWITCHTEC_PCI_DEVICE(0x8576, SWITCHTEC_GEN3), //PFXI 96XG3
SWITCHTEC_PCI_DEVICE(0x4000, SWITCHTEC_GEN4), //PFX 100XG4
SWITCHTEC_PCI_DEVICE(0x4084, SWITCHTEC_GEN4), //PFX 84XG4
SWITCHTEC_PCI_DEVICE(0x4068, SWITCHTEC_GEN4), //PFX 68XG4
SWITCHTEC_PCI_DEVICE(0x4052, SWITCHTEC_GEN4), //PFX 52XG4
SWITCHTEC_PCI_DEVICE(0x4036, SWITCHTEC_GEN4), //PFX 36XG4
SWITCHTEC_PCI_DEVICE(0x4028, SWITCHTEC_GEN4), //PFX 28XG4
SWITCHTEC_PCI_DEVICE(0x4100, SWITCHTEC_GEN4), //PSX 100XG4
SWITCHTEC_PCI_DEVICE(0x4184, SWITCHTEC_GEN4), //PSX 84XG4
SWITCHTEC_PCI_DEVICE(0x4168, SWITCHTEC_GEN4), //PSX 68XG4
SWITCHTEC_PCI_DEVICE(0x4152, SWITCHTEC_GEN4), //PSX 52XG4
SWITCHTEC_PCI_DEVICE(0x4136, SWITCHTEC_GEN4), //PSX 36XG4
SWITCHTEC_PCI_DEVICE(0x4128, SWITCHTEC_GEN4), //PSX 28XG4
SWITCHTEC_PCI_DEVICE(0x4200, SWITCHTEC_GEN4), //PAX 100XG4
SWITCHTEC_PCI_DEVICE(0x4284, SWITCHTEC_GEN4), //PAX 84XG4
SWITCHTEC_PCI_DEVICE(0x4268, SWITCHTEC_GEN4), //PAX 68XG4
SWITCHTEC_PCI_DEVICE(0x4252, SWITCHTEC_GEN4), //PAX 52XG4
SWITCHTEC_PCI_DEVICE(0x4236, SWITCHTEC_GEN4), //PAX 36XG4
SWITCHTEC_PCI_DEVICE(0x4228, SWITCHTEC_GEN4), //PAX 28XG4
{0}
};
MODULE_DEVICE_TABLE(pci, switchtec_pci_tbl);

View File

@ -1202,6 +1202,7 @@ int __must_check pci_resize_resource(struct pci_dev *dev, int i, int size);
int pci_select_bars(struct pci_dev *dev, unsigned long flags);
bool pci_device_is_present(struct pci_dev *pdev);
void pci_ignore_hotplug(struct pci_dev *dev);
struct pci_dev *pci_real_dma_dev(struct pci_dev *dev);
int __printf(6, 7) pci_request_irq(struct pci_dev *dev, unsigned int nr,
irq_handler_t handler, irq_handler_t thread_fn, void *dev_id,
@ -2310,7 +2311,7 @@ static inline struct eeh_dev *pci_dev_to_eeh_dev(struct pci_dev *pdev)
}
#endif
void pci_add_dma_alias(struct pci_dev *dev, u8 devfn);
void pci_add_dma_alias(struct pci_dev *dev, u8 devfn_from, unsigned nr_devfns);
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 (*fn)(struct pci_dev *pdev,

View File

@ -21,6 +21,11 @@
#define SWITCHTEC_EVENT_FATAL BIT(4)
#define SWITCHTEC_DMA_MRPC_EN BIT(0)
#define MRPC_GAS_READ 0x29
#define MRPC_GAS_WRITE 0x87
#define MRPC_CMD_ID(x) ((x) & 0xffff)
enum {
SWITCHTEC_GAS_MRPC_OFFSET = 0x0000,
SWITCHTEC_GAS_TOP_CFG_OFFSET = 0x1000,
@ -32,6 +37,11 @@ enum {
SWITCHTEC_GAS_PFF_CSR_OFFSET = 0x134000,
};
enum switchtec_gen {
SWITCHTEC_GEN3,
SWITCHTEC_GEN4,
};
struct mrpc_regs {
u8 input_data[SWITCHTEC_MRPC_PAYLOAD_SIZE];
u8 output_data[SWITCHTEC_MRPC_PAYLOAD_SIZE];
@ -98,16 +108,37 @@ struct sw_event_regs {
} __packed;
enum {
SWITCHTEC_CFG0_RUNNING = 0x04,
SWITCHTEC_CFG1_RUNNING = 0x05,
SWITCHTEC_IMG0_RUNNING = 0x03,
SWITCHTEC_IMG1_RUNNING = 0x07,
SWITCHTEC_GEN3_CFG0_RUNNING = 0x04,
SWITCHTEC_GEN3_CFG1_RUNNING = 0x05,
SWITCHTEC_GEN3_IMG0_RUNNING = 0x03,
SWITCHTEC_GEN3_IMG1_RUNNING = 0x07,
};
struct sys_info_regs {
u32 device_id;
u32 device_version;
u32 firmware_version;
enum {
SWITCHTEC_GEN4_MAP0_RUNNING = 0x00,
SWITCHTEC_GEN4_MAP1_RUNNING = 0x01,
SWITCHTEC_GEN4_KEY0_RUNNING = 0x02,
SWITCHTEC_GEN4_KEY1_RUNNING = 0x03,
SWITCHTEC_GEN4_BL2_0_RUNNING = 0x04,
SWITCHTEC_GEN4_BL2_1_RUNNING = 0x05,
SWITCHTEC_GEN4_CFG0_RUNNING = 0x06,
SWITCHTEC_GEN4_CFG1_RUNNING = 0x07,
SWITCHTEC_GEN4_IMG0_RUNNING = 0x08,
SWITCHTEC_GEN4_IMG1_RUNNING = 0x09,
};
enum {
SWITCHTEC_GEN4_KEY0_ACTIVE = 0,
SWITCHTEC_GEN4_KEY1_ACTIVE = 1,
SWITCHTEC_GEN4_BL2_0_ACTIVE = 0,
SWITCHTEC_GEN4_BL2_1_ACTIVE = 1,
SWITCHTEC_GEN4_CFG0_ACTIVE = 0,
SWITCHTEC_GEN4_CFG1_ACTIVE = 1,
SWITCHTEC_GEN4_IMG0_ACTIVE = 0,
SWITCHTEC_GEN4_IMG1_ACTIVE = 1,
};
struct sys_info_regs_gen3 {
u32 reserved1;
u32 vendor_table_revision;
u32 table_format_version;
@ -124,26 +155,78 @@ struct sys_info_regs {
u8 component_revision;
} __packed;
struct flash_info_regs {
struct sys_info_regs_gen4 {
u16 gas_layout_ver;
u8 evlist_ver;
u8 reserved1;
u16 mgmt_cmd_set_ver;
u16 fabric_cmd_set_ver;
u32 reserved2[2];
u8 mrpc_uart_ver;
u8 mrpc_twi_ver;
u8 mrpc_eth_ver;
u8 mrpc_inband_ver;
u32 reserved3[7];
u32 fw_update_tmo;
u32 xml_version_cfg;
u32 xml_version_img;
u32 partition_id;
u16 bl2_running;
u16 cfg_running;
u16 img_running;
u16 key_running;
u32 reserved4[43];
u32 vendor_seeprom_twi;
u32 vendor_table_revision;
u32 vendor_specific_info[2];
u16 p2p_vendor_id;
u16 p2p_device_id;
u8 p2p_revision_id;
u8 reserved5[3];
u32 p2p_class_id;
u16 subsystem_vendor_id;
u16 subsystem_id;
u32 p2p_serial_number[2];
u8 mac_addr[6];
u8 reserved6[2];
u32 reserved7[3];
char vendor_id[8];
char product_id[24];
char product_revision[2];
u16 reserved8;
} __packed;
struct sys_info_regs {
u32 device_id;
u32 device_version;
u32 firmware_version;
union {
struct sys_info_regs_gen3 gen3;
struct sys_info_regs_gen4 gen4;
};
} __packed;
struct partition_info {
u32 address;
u32 length;
};
struct flash_info_regs_gen3 {
u32 flash_part_map_upd_idx;
struct active_partition_info {
struct active_partition_info_gen3 {
u32 address;
u32 build_version;
u32 build_string;
} active_img;
struct active_partition_info active_cfg;
struct active_partition_info inactive_img;
struct active_partition_info inactive_cfg;
struct active_partition_info_gen3 active_cfg;
struct active_partition_info_gen3 inactive_img;
struct active_partition_info_gen3 inactive_cfg;
u32 flash_length;
struct partition_info {
u32 address;
u32 length;
} cfg0;
struct partition_info cfg0;
struct partition_info cfg1;
struct partition_info img0;
struct partition_info img1;
@ -151,6 +234,40 @@ struct flash_info_regs {
struct partition_info vendor[8];
};
struct flash_info_regs_gen4 {
u32 flash_address;
u32 flash_length;
struct active_partition_info_gen4 {
unsigned char bl2;
unsigned char cfg;
unsigned char img;
unsigned char key;
} active_flag;
u32 reserved[3];
struct partition_info map0;
struct partition_info map1;
struct partition_info key0;
struct partition_info key1;
struct partition_info bl2_0;
struct partition_info bl2_1;
struct partition_info cfg0;
struct partition_info cfg1;
struct partition_info img0;
struct partition_info img1;
struct partition_info nvlog;
struct partition_info vendor[8];
};
struct flash_info_regs {
union {
struct flash_info_regs_gen3 gen3;
struct flash_info_regs_gen4 gen4;
};
};
enum {
SWITCHTEC_NTB_REG_INFO_OFFSET = 0x0000,
SWITCHTEC_NTB_REG_CTRL_OFFSET = 0x4000,
@ -196,7 +313,9 @@ struct part_cfg_regs {
u32 mrpc_comp_async_data[5];
u32 dyn_binding_hdr;
u32 dyn_binding_data[5];
u32 reserved4[159];
u32 intercomm_notify_hdr;
u32 intercomm_notify_data[5];
u32 reserved4[153];
} __packed;
enum {
@ -320,7 +439,8 @@ struct pff_csr_regs {
u32 dpc_data[5];
u32 cts_hdr;
u32 cts_data[5];
u32 reserved3[6];
u32 uec_hdr;
u32 uec_data[5];
u32 hotplug_hdr;
u32 hotplug_data[5];
u32 ier_hdr;
@ -355,6 +475,8 @@ struct switchtec_dev {
struct device dev;
struct cdev cdev;
enum switchtec_gen gen;
int partition;
int partition_count;
int pff_csr_count;

View File

@ -676,6 +676,7 @@
#define PCI_EXP_LNKCTL2_TLS_32_0GT 0x0005 /* Supported Speed 32GT/s */
#define PCI_EXP_LNKCTL2_ENTER_COMP 0x0010 /* Enter Compliance */
#define PCI_EXP_LNKCTL2_TX_MARGIN 0x0380 /* Transmit Margin */
#define PCI_EXP_LNKCTL2_HASD 0x0020 /* HW Autonomous Speed Disable */
#define PCI_EXP_LNKSTA2 50 /* Link Status 2 */
#define PCI_CAP_EXP_ENDPOINT_SIZEOF_V2 52 /* v2 endpoints with link end here */
#define PCI_EXP_SLTCAP2 52 /* Slot Capabilities 2 */

View File

@ -32,7 +32,18 @@
#define SWITCHTEC_IOCTL_PART_VENDOR5 10
#define SWITCHTEC_IOCTL_PART_VENDOR6 11
#define SWITCHTEC_IOCTL_PART_VENDOR7 12
#define SWITCHTEC_IOCTL_NUM_PARTITIONS 13
#define SWITCHTEC_IOCTL_PART_BL2_0 13
#define SWITCHTEC_IOCTL_PART_BL2_1 14
#define SWITCHTEC_IOCTL_PART_MAP_0 15
#define SWITCHTEC_IOCTL_PART_MAP_1 16
#define SWITCHTEC_IOCTL_PART_KEY_0 17
#define SWITCHTEC_IOCTL_PART_KEY_1 18
#define SWITCHTEC_NUM_PARTITIONS_GEN3 13
#define SWITCHTEC_NUM_PARTITIONS_GEN4 19
/* obsolete: for compatibility with old userspace software */
#define SWITCHTEC_IOCTL_NUM_PARTITIONS SWITCHTEC_NUM_PARTITIONS_GEN3
struct switchtec_ioctl_flash_info {
__u64 flash_length;
@ -98,7 +109,9 @@ struct switchtec_ioctl_event_summary {
#define SWITCHTEC_IOCTL_EVENT_CREDIT_TIMEOUT 27
#define SWITCHTEC_IOCTL_EVENT_LINK_STATE 28
#define SWITCHTEC_IOCTL_EVENT_GFMS 29
#define SWITCHTEC_IOCTL_MAX_EVENTS 30
#define SWITCHTEC_IOCTL_EVENT_INTERCOMM_REQ_NOTIFY 30
#define SWITCHTEC_IOCTL_EVENT_UEC 31
#define SWITCHTEC_IOCTL_MAX_EVENTS 32
#define SWITCHTEC_IOCTL_EVENT_LOCAL_PART_IDX -1
#define SWITCHTEC_IOCTL_EVENT_IDX_ALL -2