Merge 4ea4ed22b5 ("Merge tag 'for-linus-2023110101' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid") into android-mainline
Steps on the way to 6.7-rc1 Change-Id: I6d128aba90aaa9f8839859e786b3071b93072b47 Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
# Copyright (C) 2022-2023 Renesas Electronics Corp.
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/pci/rcar-gen4-pci-ep.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Renesas R-Car Gen4 PCIe Endpoint
|
||||
|
||||
maintainers:
|
||||
- Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
|
||||
|
||||
allOf:
|
||||
- $ref: snps,dw-pcie-ep.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- const: renesas,r8a779f0-pcie-ep # R-Car S4-8
|
||||
- const: renesas,rcar-gen4-pcie-ep # R-Car Gen4
|
||||
|
||||
reg:
|
||||
maxItems: 7
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: dbi
|
||||
- const: dbi2
|
||||
- const: atu
|
||||
- const: dma
|
||||
- const: app
|
||||
- const: phy
|
||||
- const: addr_space
|
||||
|
||||
interrupts:
|
||||
maxItems: 3
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: dma
|
||||
- const: sft_ce
|
||||
- const: app
|
||||
|
||||
clocks:
|
||||
maxItems: 2
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: core
|
||||
- const: ref
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
reset-names:
|
||||
items:
|
||||
- const: pwr
|
||||
|
||||
max-link-speed:
|
||||
maximum: 4
|
||||
|
||||
num-lanes:
|
||||
maximum: 4
|
||||
|
||||
max-functions:
|
||||
maximum: 2
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- reg-names
|
||||
- interrupts
|
||||
- interrupt-names
|
||||
- clocks
|
||||
- clock-names
|
||||
- power-domains
|
||||
- resets
|
||||
- reset-names
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/r8a779f0-cpg-mssr.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/power/r8a779f0-sysc.h>
|
||||
|
||||
soc {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
pcie0_ep: pcie-ep@e65d0000 {
|
||||
compatible = "renesas,r8a779f0-pcie-ep", "renesas,rcar-gen4-pcie-ep";
|
||||
reg = <0 0xe65d0000 0 0x2000>, <0 0xe65d2000 0 0x1000>,
|
||||
<0 0xe65d3000 0 0x2000>, <0 0xe65d5000 0 0x1200>,
|
||||
<0 0xe65d6200 0 0x0e00>, <0 0xe65d7000 0 0x0400>,
|
||||
<0 0xfe000000 0 0x400000>;
|
||||
reg-names = "dbi", "dbi2", "atu", "dma", "app", "phy", "addr_space";
|
||||
interrupts = <GIC_SPI 417 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 418 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 422 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "dma", "sft_ce", "app";
|
||||
clocks = <&cpg CPG_MOD 624>, <&pcie0_clkref>;
|
||||
clock-names = "core", "ref";
|
||||
power-domains = <&sysc R8A779F0_PD_ALWAYS_ON>;
|
||||
resets = <&cpg 624>;
|
||||
reset-names = "pwr";
|
||||
max-link-speed = <4>;
|
||||
num-lanes = <2>;
|
||||
max-functions = /bits/ 8 <2>;
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,127 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
# Copyright (C) 2022-2023 Renesas Electronics Corp.
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/pci/rcar-gen4-pci-host.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Renesas R-Car Gen4 PCIe Host
|
||||
|
||||
maintainers:
|
||||
- Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
|
||||
|
||||
allOf:
|
||||
- $ref: snps,dw-pcie.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- const: renesas,r8a779f0-pcie # R-Car S4-8
|
||||
- const: renesas,rcar-gen4-pcie # R-Car Gen4
|
||||
|
||||
reg:
|
||||
maxItems: 7
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: dbi
|
||||
- const: dbi2
|
||||
- const: atu
|
||||
- const: dma
|
||||
- const: app
|
||||
- const: phy
|
||||
- const: config
|
||||
|
||||
interrupts:
|
||||
maxItems: 4
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: msi
|
||||
- const: dma
|
||||
- const: sft_ce
|
||||
- const: app
|
||||
|
||||
clocks:
|
||||
maxItems: 2
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: core
|
||||
- const: ref
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
reset-names:
|
||||
items:
|
||||
- const: pwr
|
||||
|
||||
max-link-speed:
|
||||
maximum: 4
|
||||
|
||||
num-lanes:
|
||||
maximum: 4
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- reg-names
|
||||
- interrupts
|
||||
- interrupt-names
|
||||
- clocks
|
||||
- clock-names
|
||||
- power-domains
|
||||
- resets
|
||||
- reset-names
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/r8a779f0-cpg-mssr.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/power/r8a779f0-sysc.h>
|
||||
|
||||
soc {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
pcie: pcie@e65d0000 {
|
||||
compatible = "renesas,r8a779f0-pcie", "renesas,rcar-gen4-pcie";
|
||||
reg = <0 0xe65d0000 0 0x1000>, <0 0xe65d2000 0 0x0800>,
|
||||
<0 0xe65d3000 0 0x2000>, <0 0xe65d5000 0 0x1200>,
|
||||
<0 0xe65d6200 0 0x0e00>, <0 0xe65d7000 0 0x0400>,
|
||||
<0 0xfe000000 0 0x400000>;
|
||||
reg-names = "dbi", "dbi2", "atu", "dma", "app", "phy", "config";
|
||||
interrupts = <GIC_SPI 416 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 417 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 418 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 422 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "msi", "dma", "sft_ce", "app";
|
||||
clocks = <&cpg CPG_MOD 624>, <&pcie0_clkref>;
|
||||
clock-names = "core", "ref";
|
||||
power-domains = <&sysc R8A779F0_PD_ALWAYS_ON>;
|
||||
resets = <&cpg 624>;
|
||||
reset-names = "pwr";
|
||||
max-link-speed = <4>;
|
||||
num-lanes = <2>;
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
bus-range = <0x00 0xff>;
|
||||
device_type = "pci";
|
||||
ranges = <0x01000000 0 0x00000000 0 0xfe000000 0 0x00400000>,
|
||||
<0x02000000 0 0x30000000 0 0x30000000 0 0x10000000>;
|
||||
dma-ranges = <0x42000000 0 0x00000000 0 0x00000000 1 0x00000000>;
|
||||
#interrupt-cells = <1>;
|
||||
interrupt-map-mask = <0 0 0 7>;
|
||||
interrupt-map = <0 0 0 1 &gic GIC_SPI 416 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<0 0 0 2 &gic GIC_SPI 416 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<0 0 0 3 &gic GIC_SPI 416 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<0 0 0 4 &gic GIC_SPI 416 IRQ_TYPE_LEVEL_HIGH>;
|
||||
snps,enable-cdm-check;
|
||||
};
|
||||
};
|
||||
@@ -33,11 +33,11 @@ properties:
|
||||
specific for each activated function, while the rest of the sub-spaces
|
||||
are common for all of them (if there are more than one).
|
||||
minItems: 2
|
||||
maxItems: 6
|
||||
maxItems: 7
|
||||
|
||||
reg-names:
|
||||
minItems: 2
|
||||
maxItems: 6
|
||||
maxItems: 7
|
||||
|
||||
interrupts:
|
||||
description:
|
||||
|
||||
@@ -33,11 +33,11 @@ properties:
|
||||
normal controller functioning. iATU memory IO region is also required
|
||||
if the space is unrolled (IP-core version >= 4.80a).
|
||||
minItems: 2
|
||||
maxItems: 5
|
||||
maxItems: 7
|
||||
|
||||
reg-names:
|
||||
minItems: 2
|
||||
maxItems: 5
|
||||
maxItems: 7
|
||||
items:
|
||||
oneOf:
|
||||
- description:
|
||||
|
||||
@@ -42,11 +42,11 @@ properties:
|
||||
are required for the normal controller work. iATU memory IO region is
|
||||
also required if the space is unrolled (IP-core version >= 4.80a).
|
||||
minItems: 2
|
||||
maxItems: 5
|
||||
maxItems: 7
|
||||
|
||||
reg-names:
|
||||
minItems: 2
|
||||
maxItems: 5
|
||||
maxItems: 7
|
||||
items:
|
||||
oneOf:
|
||||
- description:
|
||||
|
||||
@@ -118,7 +118,7 @@ examples:
|
||||
compatible = "xlnx,nwl-pcie-2.11";
|
||||
reg = <0x0 0xfd0e0000 0x0 0x1000>,
|
||||
<0x0 0xfd480000 0x0 0x1000>,
|
||||
<0x80 0x00000000 0x0 0x1000000>;
|
||||
<0x80 0x00000000 0x0 0x10000000>;
|
||||
reg-names = "breg", "pcireg", "cfg";
|
||||
ranges = <0x02000000 0x0 0xe0000000 0x0 0xe0000000 0x0 0x10000000>,
|
||||
<0x43000000 0x00000006 0x0 0x00000006 0x0 0x00000002 0x0>;
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/pci/xlnx,xdma-host.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Xilinx XDMA PL PCIe Root Port Bridge
|
||||
|
||||
maintainers:
|
||||
- Thippeswamy Havalige <thippeswamy.havalige@amd.com>
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/pci/pci-bus.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: xlnx,xdma-host-3.00
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
ranges:
|
||||
maxItems: 2
|
||||
|
||||
interrupts:
|
||||
items:
|
||||
- description: interrupt asserted when miscellaneous interrupt is received.
|
||||
- description: msi0 interrupt asserted when an MSI is received.
|
||||
- description: msi1 interrupt asserted when an MSI is received.
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: misc
|
||||
- const: msi0
|
||||
- const: msi1
|
||||
|
||||
interrupt-map-mask:
|
||||
items:
|
||||
- const: 0
|
||||
- const: 0
|
||||
- const: 0
|
||||
- const: 7
|
||||
|
||||
interrupt-map:
|
||||
maxItems: 4
|
||||
|
||||
"#interrupt-cells":
|
||||
const: 1
|
||||
|
||||
interrupt-controller:
|
||||
description: identifies the node as an interrupt controller
|
||||
type: object
|
||||
properties:
|
||||
interrupt-controller: true
|
||||
|
||||
"#address-cells":
|
||||
const: 0
|
||||
|
||||
"#interrupt-cells":
|
||||
const: 1
|
||||
|
||||
required:
|
||||
- interrupt-controller
|
||||
- "#address-cells"
|
||||
- "#interrupt-cells"
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- ranges
|
||||
- interrupts
|
||||
- interrupt-map
|
||||
- interrupt-map-mask
|
||||
- "#interrupt-cells"
|
||||
- interrupt-controller
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
soc {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
pcie@a0000000 {
|
||||
compatible = "xlnx,xdma-host-3.00";
|
||||
reg = <0x0 0xa0000000 0x0 0x10000000>;
|
||||
ranges = <0x2000000 0x0 0xb0000000 0x0 0xb0000000 0x0 0x1000000>,
|
||||
<0x43000000 0x5 0x0 0x5 0x0 0x0 0x1000000>;
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
#interrupt-cells = <1>;
|
||||
device_type = "pci";
|
||||
interrupt-parent = <&gic>;
|
||||
interrupts = <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "misc", "msi0", "msi1";
|
||||
interrupt-map-mask = <0x0 0x0 0x0 0x7>;
|
||||
interrupt-map = <0 0 0 1 &pcie_intc_0 0>,
|
||||
<0 0 0 2 &pcie_intc_0 1>,
|
||||
<0 0 0 3 &pcie_intc_0 2>,
|
||||
<0 0 0 4 &pcie_intc_0 3>;
|
||||
pcie_intc_0: interrupt-controller {
|
||||
#address-cells = <0>;
|
||||
#interrupt-cells = <1>;
|
||||
interrupt-controller;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -16533,6 +16533,7 @@ L: linux-renesas-soc@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/pci/*rcar*
|
||||
F: drivers/pci/controller/*rcar*
|
||||
F: drivers/pci/controller/dwc/*rcar*
|
||||
|
||||
PCI DRIVER FOR SAMSUNG EXYNOS
|
||||
M: Jingoo Han <jingoohan1@gmail.com>
|
||||
|
||||
@@ -183,16 +183,17 @@ miata_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
|
||||
the 2nd 8259 controller. So we have to check for it first. */
|
||||
|
||||
if((slot == 7) && (PCI_FUNC(dev->devfn) == 3)) {
|
||||
u8 irq=0;
|
||||
struct pci_dev *pdev = pci_get_slot(dev->bus, dev->devfn & ~7);
|
||||
if(pdev == NULL || pci_read_config_byte(pdev, 0x40,&irq) != PCIBIOS_SUCCESSFUL) {
|
||||
pci_dev_put(pdev);
|
||||
u8 irq = 0;
|
||||
int ret;
|
||||
|
||||
if (!pdev)
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
pci_dev_put(pdev);
|
||||
return irq;
|
||||
}
|
||||
|
||||
ret = pci_read_config_byte(pdev, 0x40, &irq);
|
||||
pci_dev_put(pdev);
|
||||
|
||||
return ret == PCIBIOS_SUCCESSFUL ? irq : -1;
|
||||
}
|
||||
|
||||
return COMMON_TABLE_LOOKUP;
|
||||
|
||||
@@ -50,20 +50,21 @@ int __init pci_is_66mhz_capable(struct pci_channel *hose,
|
||||
int top_bus, int current_bus)
|
||||
{
|
||||
u32 pci_devfn;
|
||||
unsigned short vid;
|
||||
u16 vid;
|
||||
int cap66 = -1;
|
||||
u16 stat;
|
||||
int ret;
|
||||
|
||||
pr_info("PCI: Checking 66MHz capabilities...\n");
|
||||
|
||||
for (pci_devfn = 0; pci_devfn < 0xff; pci_devfn++) {
|
||||
if (PCI_FUNC(pci_devfn))
|
||||
continue;
|
||||
if (early_read_config_word(hose, top_bus, current_bus,
|
||||
pci_devfn, PCI_VENDOR_ID, &vid) !=
|
||||
PCIBIOS_SUCCESSFUL)
|
||||
ret = early_read_config_word(hose, top_bus, current_bus,
|
||||
pci_devfn, PCI_VENDOR_ID, &vid);
|
||||
if (ret != PCIBIOS_SUCCESSFUL)
|
||||
continue;
|
||||
if (vid == 0xffff)
|
||||
if (PCI_POSSIBLE_ERROR(vid))
|
||||
continue;
|
||||
|
||||
/* check 66MHz capability */
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
* Exceptions for specific devices. Usually work-arounds for fatal design flaws.
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/vgaarb.h>
|
||||
#include <asm/amd_nb.h>
|
||||
#include <asm/hpet.h>
|
||||
@@ -904,3 +906,60 @@ static void chromeos_fixup_apl_pci_l1ss_capability(struct pci_dev *dev)
|
||||
}
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x5ad6, chromeos_save_apl_pci_l1ss_capability);
|
||||
DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, 0x5ad6, chromeos_fixup_apl_pci_l1ss_capability);
|
||||
|
||||
#ifdef CONFIG_SUSPEND
|
||||
/*
|
||||
* Root Ports on some AMD SoCs advertise PME_Support for D3hot and D3cold, but
|
||||
* if the SoC is put into a hardware sleep state by the amd-pmc driver, the
|
||||
* Root Ports don't generate wakeup interrupts for USB devices.
|
||||
*
|
||||
* When suspending, remove D3hot and D3cold from the PME_Support advertised
|
||||
* by the Root Port so we don't use those states if we're expecting wakeup
|
||||
* interrupts. Restore the advertised PME_Support when resuming.
|
||||
*/
|
||||
static void amd_rp_pme_suspend(struct pci_dev *dev)
|
||||
{
|
||||
struct pci_dev *rp;
|
||||
|
||||
/*
|
||||
* PM_SUSPEND_ON means we're doing runtime suspend, which means
|
||||
* amd-pmc will not be involved so PMEs during D3 work as advertised.
|
||||
*
|
||||
* The PMEs *do* work if amd-pmc doesn't put the SoC in the hardware
|
||||
* sleep state, but we assume amd-pmc is always present.
|
||||
*/
|
||||
if (pm_suspend_target_state == PM_SUSPEND_ON)
|
||||
return;
|
||||
|
||||
rp = pcie_find_root_port(dev);
|
||||
if (!rp->pm_cap)
|
||||
return;
|
||||
|
||||
rp->pme_support &= ~((PCI_PM_CAP_PME_D3hot|PCI_PM_CAP_PME_D3cold) >>
|
||||
PCI_PM_CAP_PME_SHIFT);
|
||||
dev_info_once(&rp->dev, "quirk: disabling D3cold for suspend\n");
|
||||
}
|
||||
|
||||
static void amd_rp_pme_resume(struct pci_dev *dev)
|
||||
{
|
||||
struct pci_dev *rp;
|
||||
u16 pmc;
|
||||
|
||||
rp = pcie_find_root_port(dev);
|
||||
if (!rp->pm_cap)
|
||||
return;
|
||||
|
||||
pci_read_config_word(rp, rp->pm_cap + PCI_PM_PMC, &pmc);
|
||||
rp->pme_support = FIELD_GET(PCI_PM_CAP_PME_MASK, pmc);
|
||||
}
|
||||
/* Rembrandt (yellow_carp) */
|
||||
DECLARE_PCI_FIXUP_SUSPEND(PCI_VENDOR_ID_AMD, 0x162e, amd_rp_pme_suspend);
|
||||
DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, 0x162e, amd_rp_pme_resume);
|
||||
DECLARE_PCI_FIXUP_SUSPEND(PCI_VENDOR_ID_AMD, 0x162f, amd_rp_pme_suspend);
|
||||
DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, 0x162f, amd_rp_pme_resume);
|
||||
/* Phoenix (pink_sardine) */
|
||||
DECLARE_PCI_FIXUP_SUSPEND(PCI_VENDOR_ID_AMD, 0x1668, amd_rp_pme_suspend);
|
||||
DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, 0x1668, amd_rp_pme_resume);
|
||||
DECLARE_PCI_FIXUP_SUSPEND(PCI_VENDOR_ID_AMD, 0x1669, amd_rp_pme_suspend);
|
||||
DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, 0x1669, amd_rp_pme_resume);
|
||||
#endif /* CONFIG_SUSPEND */
|
||||
|
||||
+11
-9
@@ -2291,19 +2291,21 @@ static int get_esi(struct atm_dev *dev)
|
||||
static int reset_sar(struct atm_dev *dev)
|
||||
{
|
||||
IADEV *iadev;
|
||||
int i, error = 1;
|
||||
int i, error;
|
||||
unsigned int pci[64];
|
||||
|
||||
iadev = INPH_IA_DEV(dev);
|
||||
for(i=0; i<64; i++)
|
||||
if ((error = pci_read_config_dword(iadev->pci,
|
||||
i*4, &pci[i])) != PCIBIOS_SUCCESSFUL)
|
||||
return error;
|
||||
for (i = 0; i < 64; i++) {
|
||||
error = pci_read_config_dword(iadev->pci, i * 4, &pci[i]);
|
||||
if (error != PCIBIOS_SUCCESSFUL)
|
||||
return error;
|
||||
}
|
||||
writel(0, iadev->reg+IPHASE5575_EXT_RESET);
|
||||
for(i=0; i<64; i++)
|
||||
if ((error = pci_write_config_dword(iadev->pci,
|
||||
i*4, pci[i])) != PCIBIOS_SUCCESSFUL)
|
||||
return error;
|
||||
for (i = 0; i < 64; i++) {
|
||||
error = pci_write_config_dword(iadev->pci, i * 4, pci[i]);
|
||||
if (error != PCIBIOS_SUCCESSFUL)
|
||||
return error;
|
||||
}
|
||||
udelay(5);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1393,14 +1393,11 @@ void amdgpu_acpi_detect(void)
|
||||
struct pci_dev *pdev = NULL;
|
||||
int ret;
|
||||
|
||||
while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
|
||||
if (!atif->handle)
|
||||
amdgpu_atif_pci_probe_handle(pdev);
|
||||
if (!atcs->handle)
|
||||
amdgpu_atcs_pci_probe_handle(pdev);
|
||||
}
|
||||
while ((pdev = pci_get_base_class(PCI_BASE_CLASS_DISPLAY, pdev))) {
|
||||
if ((pdev->class != PCI_CLASS_DISPLAY_VGA << 8) &&
|
||||
(pdev->class != PCI_CLASS_DISPLAY_OTHER << 8))
|
||||
continue;
|
||||
|
||||
while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_OTHER << 8, pdev)) != NULL) {
|
||||
if (!atif->handle)
|
||||
amdgpu_atif_pci_probe_handle(pdev);
|
||||
if (!atcs->handle)
|
||||
|
||||
@@ -287,7 +287,11 @@ static bool amdgpu_atrm_get_bios(struct amdgpu_device *adev)
|
||||
if (adev->flags & AMD_IS_APU)
|
||||
return false;
|
||||
|
||||
while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
|
||||
while ((pdev = pci_get_base_class(PCI_BASE_CLASS_DISPLAY, pdev))) {
|
||||
if ((pdev->class != PCI_CLASS_DISPLAY_VGA << 8) &&
|
||||
(pdev->class != PCI_CLASS_DISPLAY_OTHER << 8))
|
||||
continue;
|
||||
|
||||
dhandle = ACPI_HANDLE(&pdev->dev);
|
||||
if (!dhandle)
|
||||
continue;
|
||||
@@ -299,20 +303,6 @@ static bool amdgpu_atrm_get_bios(struct amdgpu_device *adev)
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_OTHER << 8, pdev)) != NULL) {
|
||||
dhandle = ACPI_HANDLE(&pdev->dev);
|
||||
if (!dhandle)
|
||||
continue;
|
||||
|
||||
status = acpi_get_handle(dhandle, "ATRM", &atrm_handle);
|
||||
if (ACPI_SUCCESS(status)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
return false;
|
||||
pci_dev_put(pdev);
|
||||
|
||||
@@ -284,14 +284,11 @@ static bool nouveau_dsm_detect(void)
|
||||
printk("MXM: GUID detected in BIOS\n");
|
||||
|
||||
/* now do DSM detection */
|
||||
while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
|
||||
vga_count++;
|
||||
while ((pdev = pci_get_base_class(PCI_BASE_CLASS_DISPLAY, pdev))) {
|
||||
if ((pdev->class != PCI_CLASS_DISPLAY_VGA << 8) &&
|
||||
(pdev->class != PCI_CLASS_DISPLAY_3D << 8))
|
||||
continue;
|
||||
|
||||
nouveau_dsm_pci_probe(pdev, &dhandle, &has_mux, &has_optimus,
|
||||
&has_optimus_flags, &has_power_resources);
|
||||
}
|
||||
|
||||
while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_3D << 8, pdev)) != NULL) {
|
||||
vga_count++;
|
||||
|
||||
nouveau_dsm_pci_probe(pdev, &dhandle, &has_mux, &has_optimus,
|
||||
|
||||
@@ -68,11 +68,6 @@ module_param_named(num_heads, qxl_num_crtc, int, 0400);
|
||||
static struct drm_driver qxl_driver;
|
||||
static struct pci_driver qxl_pci_driver;
|
||||
|
||||
static bool is_vga(struct pci_dev *pdev)
|
||||
{
|
||||
return pdev->class == PCI_CLASS_DISPLAY_VGA << 8;
|
||||
}
|
||||
|
||||
static int
|
||||
qxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
{
|
||||
@@ -100,7 +95,7 @@ qxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
if (ret)
|
||||
goto disable_pci;
|
||||
|
||||
if (is_vga(pdev) && pdev->revision < 5) {
|
||||
if (pci_is_vga(pdev) && pdev->revision < 5) {
|
||||
ret = vga_get_interruptible(pdev, VGA_RSRC_LEGACY_IO);
|
||||
if (ret) {
|
||||
DRM_ERROR("can't get legacy vga ioports\n");
|
||||
@@ -131,7 +126,7 @@ modeset_cleanup:
|
||||
unload:
|
||||
qxl_device_fini(qdev);
|
||||
put_vga:
|
||||
if (is_vga(pdev) && pdev->revision < 5)
|
||||
if (pci_is_vga(pdev) && pdev->revision < 5)
|
||||
vga_put(pdev, VGA_RSRC_LEGACY_IO);
|
||||
disable_pci:
|
||||
pci_disable_device(pdev);
|
||||
@@ -159,7 +154,7 @@ qxl_pci_remove(struct pci_dev *pdev)
|
||||
|
||||
drm_dev_unregister(dev);
|
||||
drm_atomic_helper_shutdown(dev);
|
||||
if (is_vga(pdev) && pdev->revision < 5)
|
||||
if (pci_is_vga(pdev) && pdev->revision < 5)
|
||||
vga_put(pdev, VGA_RSRC_LEGACY_IO);
|
||||
}
|
||||
|
||||
|
||||
@@ -199,7 +199,11 @@ static bool radeon_atrm_get_bios(struct radeon_device *rdev)
|
||||
if (rdev->flags & RADEON_IS_IGP)
|
||||
return false;
|
||||
|
||||
while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
|
||||
while ((pdev = pci_get_base_class(PCI_BASE_CLASS_DISPLAY, pdev))) {
|
||||
if ((pdev->class != PCI_CLASS_DISPLAY_VGA << 8) &&
|
||||
(pdev->class != PCI_CLASS_DISPLAY_OTHER << 8))
|
||||
continue;
|
||||
|
||||
dhandle = ACPI_HANDLE(&pdev->dev);
|
||||
if (!dhandle)
|
||||
continue;
|
||||
@@ -211,20 +215,6 @@ static bool radeon_atrm_get_bios(struct radeon_device *rdev)
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_OTHER << 8, pdev)) != NULL) {
|
||||
dhandle = ACPI_HANDLE(&pdev->dev);
|
||||
if (!dhandle)
|
||||
continue;
|
||||
|
||||
status = acpi_get_handle(dhandle, "ATRM", &atrm_handle);
|
||||
if (ACPI_SUCCESS(status)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
return false;
|
||||
pci_dev_put(pdev);
|
||||
|
||||
@@ -51,7 +51,7 @@ static int virtio_gpu_pci_quirk(struct drm_device *dev)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev->dev);
|
||||
const char *pname = dev_name(&pdev->dev);
|
||||
bool vga = (pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA;
|
||||
bool vga = pci_is_vga(pdev);
|
||||
int ret;
|
||||
|
||||
DRM_INFO("pci: %s detected at %s\n",
|
||||
|
||||
@@ -1151,8 +1151,6 @@ static unsigned int cp2112_gpio_irq_startup(struct irq_data *d)
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct cp2112_device *dev = gpiochip_get_data(gc);
|
||||
|
||||
INIT_DELAYED_WORK(&dev->gpio_poll_worker, cp2112_gpio_poll_callback);
|
||||
|
||||
if (!dev->gpio_poll) {
|
||||
dev->gpio_poll = true;
|
||||
schedule_delayed_work(&dev->gpio_poll_worker, 0);
|
||||
@@ -1168,7 +1166,11 @@ static void cp2112_gpio_irq_shutdown(struct irq_data *d)
|
||||
struct cp2112_device *dev = gpiochip_get_data(gc);
|
||||
|
||||
cp2112_gpio_irq_mask(d);
|
||||
cancel_delayed_work_sync(&dev->gpio_poll_worker);
|
||||
|
||||
if (!dev->irq_mask) {
|
||||
dev->gpio_poll = false;
|
||||
cancel_delayed_work_sync(&dev->gpio_poll_worker);
|
||||
}
|
||||
}
|
||||
|
||||
static int cp2112_gpio_irq_type(struct irq_data *d, unsigned int type)
|
||||
@@ -1307,6 +1309,8 @@ static int cp2112_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
girq->handler = handle_simple_irq;
|
||||
girq->threaded = true;
|
||||
|
||||
INIT_DELAYED_WORK(&dev->gpio_poll_worker, cp2112_gpio_poll_callback);
|
||||
|
||||
ret = gpiochip_add_data(&dev->gc, dev);
|
||||
if (ret < 0) {
|
||||
hid_err(hdev, "error registering gpio chip\n");
|
||||
|
||||
@@ -366,6 +366,7 @@
|
||||
|
||||
#define USB_VENDOR_ID_DELL 0x413c
|
||||
#define USB_DEVICE_ID_DELL_PIXART_USB_OPTICAL_MOUSE 0x301a
|
||||
#define USB_DEVICE_ID_DELL_PRO_WIRELESS_KM5221W 0x4503
|
||||
|
||||
#define USB_VENDOR_ID_DELORME 0x1163
|
||||
#define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100
|
||||
|
||||
+81
-41
@@ -51,7 +51,12 @@ struct lenovo_drvdata {
|
||||
int select_right;
|
||||
int sensitivity;
|
||||
int press_speed;
|
||||
u8 middlebutton_state; /* 0:Up, 1:Down (undecided), 2:Scrolling */
|
||||
/* 0: Up
|
||||
* 1: Down (undecided)
|
||||
* 2: Scrolling
|
||||
* 3: Patched firmware, disable workaround
|
||||
*/
|
||||
u8 middlebutton_state;
|
||||
bool fn_lock;
|
||||
};
|
||||
|
||||
@@ -521,6 +526,19 @@ static void lenovo_features_set_cptkbd(struct hid_device *hdev)
|
||||
int ret;
|
||||
struct lenovo_drvdata *cptkbd_data = hid_get_drvdata(hdev);
|
||||
|
||||
/*
|
||||
* Tell the keyboard a driver understands it, and turn F7, F9, F11 into
|
||||
* regular keys
|
||||
*/
|
||||
ret = lenovo_send_cmd_cptkbd(hdev, 0x01, 0x03);
|
||||
if (ret)
|
||||
hid_warn(hdev, "Failed to switch F7/9/11 mode: %d\n", ret);
|
||||
|
||||
/* Switch middle button to native mode */
|
||||
ret = lenovo_send_cmd_cptkbd(hdev, 0x09, 0x01);
|
||||
if (ret)
|
||||
hid_warn(hdev, "Failed to switch middle button: %d\n", ret);
|
||||
|
||||
ret = lenovo_send_cmd_cptkbd(hdev, 0x05, cptkbd_data->fn_lock);
|
||||
if (ret)
|
||||
hid_err(hdev, "Fn-lock setting failed: %d\n", ret);
|
||||
@@ -668,31 +686,48 @@ static int lenovo_event_cptkbd(struct hid_device *hdev,
|
||||
{
|
||||
struct lenovo_drvdata *cptkbd_data = hid_get_drvdata(hdev);
|
||||
|
||||
/* "wheel" scroll events */
|
||||
if (usage->type == EV_REL && (usage->code == REL_WHEEL ||
|
||||
usage->code == REL_HWHEEL)) {
|
||||
/* Scroll events disable middle-click event */
|
||||
cptkbd_data->middlebutton_state = 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Middle click events */
|
||||
if (usage->type == EV_KEY && usage->code == BTN_MIDDLE) {
|
||||
if (value == 1) {
|
||||
cptkbd_data->middlebutton_state = 1;
|
||||
} else if (value == 0) {
|
||||
if (cptkbd_data->middlebutton_state == 1) {
|
||||
/* No scrolling inbetween, send middle-click */
|
||||
input_event(field->hidinput->input,
|
||||
EV_KEY, BTN_MIDDLE, 1);
|
||||
input_sync(field->hidinput->input);
|
||||
input_event(field->hidinput->input,
|
||||
EV_KEY, BTN_MIDDLE, 0);
|
||||
input_sync(field->hidinput->input);
|
||||
}
|
||||
cptkbd_data->middlebutton_state = 0;
|
||||
if (cptkbd_data->middlebutton_state != 3) {
|
||||
/* REL_X and REL_Y events during middle button pressed
|
||||
* are only possible on patched, bug-free firmware
|
||||
* so set middlebutton_state to 3
|
||||
* to never apply workaround anymore
|
||||
*/
|
||||
if (cptkbd_data->middlebutton_state == 1 &&
|
||||
usage->type == EV_REL &&
|
||||
(usage->code == REL_X || usage->code == REL_Y)) {
|
||||
cptkbd_data->middlebutton_state = 3;
|
||||
/* send middle button press which was hold before */
|
||||
input_event(field->hidinput->input,
|
||||
EV_KEY, BTN_MIDDLE, 1);
|
||||
input_sync(field->hidinput->input);
|
||||
}
|
||||
|
||||
/* "wheel" scroll events */
|
||||
if (usage->type == EV_REL && (usage->code == REL_WHEEL ||
|
||||
usage->code == REL_HWHEEL)) {
|
||||
/* Scroll events disable middle-click event */
|
||||
cptkbd_data->middlebutton_state = 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Middle click events */
|
||||
if (usage->type == EV_KEY && usage->code == BTN_MIDDLE) {
|
||||
if (value == 1) {
|
||||
cptkbd_data->middlebutton_state = 1;
|
||||
} else if (value == 0) {
|
||||
if (cptkbd_data->middlebutton_state == 1) {
|
||||
/* No scrolling inbetween, send middle-click */
|
||||
input_event(field->hidinput->input,
|
||||
EV_KEY, BTN_MIDDLE, 1);
|
||||
input_sync(field->hidinput->input);
|
||||
input_event(field->hidinput->input,
|
||||
EV_KEY, BTN_MIDDLE, 0);
|
||||
input_sync(field->hidinput->input);
|
||||
}
|
||||
cptkbd_data->middlebutton_state = 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (usage->type == EV_KEY && usage->code == KEY_FN_ESC && value == 1) {
|
||||
@@ -1126,22 +1161,6 @@ static int lenovo_probe_cptkbd(struct hid_device *hdev)
|
||||
}
|
||||
hid_set_drvdata(hdev, cptkbd_data);
|
||||
|
||||
/*
|
||||
* Tell the keyboard a driver understands it, and turn F7, F9, F11 into
|
||||
* regular keys (Compact only)
|
||||
*/
|
||||
if (hdev->product == USB_DEVICE_ID_LENOVO_CUSBKBD ||
|
||||
hdev->product == USB_DEVICE_ID_LENOVO_CBTKBD) {
|
||||
ret = lenovo_send_cmd_cptkbd(hdev, 0x01, 0x03);
|
||||
if (ret)
|
||||
hid_warn(hdev, "Failed to switch F7/9/11 mode: %d\n", ret);
|
||||
}
|
||||
|
||||
/* Switch middle button to native mode */
|
||||
ret = lenovo_send_cmd_cptkbd(hdev, 0x09, 0x01);
|
||||
if (ret)
|
||||
hid_warn(hdev, "Failed to switch middle button: %d\n", ret);
|
||||
|
||||
/* Set keyboard settings to known state */
|
||||
cptkbd_data->middlebutton_state = 0;
|
||||
cptkbd_data->fn_lock = true;
|
||||
@@ -1264,6 +1283,24 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int lenovo_reset_resume(struct hid_device *hdev)
|
||||
{
|
||||
switch (hdev->product) {
|
||||
case USB_DEVICE_ID_LENOVO_CUSBKBD:
|
||||
case USB_DEVICE_ID_LENOVO_TPIIUSBKBD:
|
||||
if (hdev->type == HID_TYPE_USBMOUSE)
|
||||
lenovo_features_set_cptkbd(hdev);
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void lenovo_remove_tpkbd(struct hid_device *hdev)
|
||||
{
|
||||
struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
|
||||
@@ -1380,6 +1417,9 @@ static struct hid_driver lenovo_driver = {
|
||||
.raw_event = lenovo_raw_event,
|
||||
.event = lenovo_event,
|
||||
.report_fixup = lenovo_report_fixup,
|
||||
#ifdef CONFIG_PM
|
||||
.reset_resume = lenovo_reset_resume,
|
||||
#endif
|
||||
};
|
||||
module_hid_driver(lenovo_driver);
|
||||
|
||||
|
||||
@@ -69,12 +69,11 @@ MODULE_PARM_DESC(disable_tap_to_click,
|
||||
#define HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS BIT(22)
|
||||
#define HIDPP_QUIRK_DELAYED_INIT BIT(23)
|
||||
#define HIDPP_QUIRK_FORCE_OUTPUT_REPORTS BIT(24)
|
||||
#define HIDPP_QUIRK_UNIFYING BIT(25)
|
||||
#define HIDPP_QUIRK_HIDPP_WHEELS BIT(26)
|
||||
#define HIDPP_QUIRK_HIDPP_EXTRA_MOUSE_BTNS BIT(27)
|
||||
#define HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS BIT(28)
|
||||
#define HIDPP_QUIRK_HI_RES_SCROLL_1P0 BIT(29)
|
||||
#define HIDPP_QUIRK_WIRELESS_STATUS BIT(30)
|
||||
#define HIDPP_QUIRK_HIDPP_WHEELS BIT(25)
|
||||
#define HIDPP_QUIRK_HIDPP_EXTRA_MOUSE_BTNS BIT(26)
|
||||
#define HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS BIT(27)
|
||||
#define HIDPP_QUIRK_HI_RES_SCROLL_1P0 BIT(28)
|
||||
#define HIDPP_QUIRK_WIRELESS_STATUS BIT(29)
|
||||
|
||||
/* These are just aliases for now */
|
||||
#define HIDPP_QUIRK_KBD_SCROLL_WHEEL HIDPP_QUIRK_HIDPP_WHEELS
|
||||
@@ -194,7 +193,6 @@ struct hidpp_device {
|
||||
|
||||
struct work_struct work;
|
||||
struct kfifo delayed_work_fifo;
|
||||
atomic_t connected;
|
||||
struct input_dev *delayed_input;
|
||||
|
||||
unsigned long quirks;
|
||||
@@ -235,8 +233,6 @@ struct hidpp_device {
|
||||
#define HIDPP20_ERROR_UNSUPPORTED 0x09
|
||||
#define HIDPP20_ERROR 0xff
|
||||
|
||||
static void hidpp_connect_event(struct hidpp_device *hidpp_dev);
|
||||
|
||||
static int __hidpp_send_report(struct hid_device *hdev,
|
||||
struct hidpp_report *hidpp_report)
|
||||
{
|
||||
@@ -450,13 +446,6 @@ static int hidpp_send_rap_command_sync(struct hidpp_device *hidpp_dev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void delayed_work_cb(struct work_struct *work)
|
||||
{
|
||||
struct hidpp_device *hidpp = container_of(work, struct hidpp_device,
|
||||
work);
|
||||
hidpp_connect_event(hidpp);
|
||||
}
|
||||
|
||||
static inline bool hidpp_match_answer(struct hidpp_report *question,
|
||||
struct hidpp_report *answer)
|
||||
{
|
||||
@@ -1835,15 +1824,14 @@ static int hidpp_battery_get_property(struct power_supply *psy,
|
||||
/* -------------------------------------------------------------------------- */
|
||||
#define HIDPP_PAGE_WIRELESS_DEVICE_STATUS 0x1d4b
|
||||
|
||||
static int hidpp_set_wireless_feature_index(struct hidpp_device *hidpp)
|
||||
static int hidpp_get_wireless_feature_index(struct hidpp_device *hidpp, u8 *feature_index)
|
||||
{
|
||||
u8 feature_type;
|
||||
int ret;
|
||||
|
||||
ret = hidpp_root_get_feature(hidpp,
|
||||
HIDPP_PAGE_WIRELESS_DEVICE_STATUS,
|
||||
&hidpp->wireless_feature_index,
|
||||
&feature_type);
|
||||
feature_index, &feature_type);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -3142,7 +3130,7 @@ static int wtp_allocate(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
return 0;
|
||||
};
|
||||
|
||||
static int wtp_connect(struct hid_device *hdev, bool connected)
|
||||
static int wtp_connect(struct hid_device *hdev)
|
||||
{
|
||||
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
|
||||
struct wtp_data *wd = hidpp->private_data;
|
||||
@@ -3204,7 +3192,7 @@ static const u8 m560_config_parameter[] = {0x00, 0xaf, 0x03};
|
||||
#define M560_SUB_ID 0x0a
|
||||
#define M560_BUTTON_MODE_REGISTER 0x35
|
||||
|
||||
static int m560_send_config_command(struct hid_device *hdev, bool connected)
|
||||
static int m560_send_config_command(struct hid_device *hdev)
|
||||
{
|
||||
struct hidpp_report response;
|
||||
struct hidpp_device *hidpp_dev;
|
||||
@@ -3399,7 +3387,7 @@ static int k400_allocate(struct hid_device *hdev)
|
||||
return 0;
|
||||
};
|
||||
|
||||
static int k400_connect(struct hid_device *hdev, bool connected)
|
||||
static int k400_connect(struct hid_device *hdev)
|
||||
{
|
||||
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
|
||||
|
||||
@@ -3894,8 +3882,6 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data,
|
||||
}
|
||||
|
||||
if (unlikely(hidpp_report_is_connect_event(hidpp, report))) {
|
||||
atomic_set(&hidpp->connected,
|
||||
!(report->rap.params[0] & (1 << 6)));
|
||||
if (schedule_work(&hidpp->work) == 0)
|
||||
dbg_hid("%s: connect event already queued\n", __func__);
|
||||
return 1;
|
||||
@@ -4131,24 +4117,22 @@ static int hidpp_initialize_battery(struct hidpp_device *hidpp)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void hidpp_overwrite_name(struct hid_device *hdev)
|
||||
/* Get name + serial for USB and Bluetooth HID++ devices */
|
||||
static void hidpp_non_unifying_init(struct hidpp_device *hidpp)
|
||||
{
|
||||
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
|
||||
struct hid_device *hdev = hidpp->hid_dev;
|
||||
char *name;
|
||||
|
||||
if (hidpp->protocol_major < 2)
|
||||
return;
|
||||
/* Bluetooth devices already have their serialnr set */
|
||||
if (hid_is_usb(hdev))
|
||||
hidpp_serial_init(hidpp);
|
||||
|
||||
name = hidpp_get_device_name(hidpp);
|
||||
|
||||
if (!name) {
|
||||
hid_err(hdev, "unable to retrieve the name of the device");
|
||||
} else {
|
||||
if (name) {
|
||||
dbg_hid("HID++: Got name: %s\n", name);
|
||||
snprintf(hdev->name, sizeof(hdev->name), "%s", name);
|
||||
kfree(name);
|
||||
}
|
||||
|
||||
kfree(name);
|
||||
}
|
||||
|
||||
static int hidpp_input_open(struct input_dev *dev)
|
||||
@@ -4189,15 +4173,18 @@ static struct input_dev *hidpp_allocate_input(struct hid_device *hdev)
|
||||
return input_dev;
|
||||
}
|
||||
|
||||
static void hidpp_connect_event(struct hidpp_device *hidpp)
|
||||
static void hidpp_connect_event(struct work_struct *work)
|
||||
{
|
||||
struct hidpp_device *hidpp = container_of(work, struct hidpp_device, work);
|
||||
struct hid_device *hdev = hidpp->hid_dev;
|
||||
int ret = 0;
|
||||
bool connected = atomic_read(&hidpp->connected);
|
||||
struct input_dev *input;
|
||||
char *name, *devm_name;
|
||||
int ret;
|
||||
|
||||
if (!connected) {
|
||||
/* Get device version to check if it is connected */
|
||||
ret = hidpp_root_get_protocol_version(hidpp);
|
||||
if (ret) {
|
||||
hid_info(hidpp->hid_dev, "Disconnected\n");
|
||||
if (hidpp->battery.ps) {
|
||||
hidpp->battery.online = false;
|
||||
hidpp->battery.status = POWER_SUPPLY_STATUS_UNKNOWN;
|
||||
@@ -4208,15 +4195,15 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
|
||||
}
|
||||
|
||||
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) {
|
||||
ret = wtp_connect(hdev, connected);
|
||||
ret = wtp_connect(hdev);
|
||||
if (ret)
|
||||
return;
|
||||
} else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560) {
|
||||
ret = m560_send_config_command(hdev, connected);
|
||||
ret = m560_send_config_command(hdev);
|
||||
if (ret)
|
||||
return;
|
||||
} else if (hidpp->quirks & HIDPP_QUIRK_CLASS_K400) {
|
||||
ret = k400_connect(hdev, connected);
|
||||
ret = k400_connect(hdev);
|
||||
if (ret)
|
||||
return;
|
||||
}
|
||||
@@ -4239,14 +4226,11 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
|
||||
return;
|
||||
}
|
||||
|
||||
/* the device is already connected, we can ask for its name and
|
||||
* protocol */
|
||||
if (!hidpp->protocol_major) {
|
||||
ret = hidpp_root_get_protocol_version(hidpp);
|
||||
if (ret) {
|
||||
hid_err(hdev, "Can not get the protocol version.\n");
|
||||
return;
|
||||
}
|
||||
if (hidpp->protocol_major >= 2) {
|
||||
u8 feature_index;
|
||||
|
||||
if (!hidpp_get_wireless_feature_index(hidpp, &feature_index))
|
||||
hidpp->wireless_feature_index = feature_index;
|
||||
}
|
||||
|
||||
if (hidpp->name == hdev->name && hidpp->protocol_major >= 2) {
|
||||
@@ -4391,10 +4375,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
{
|
||||
struct hidpp_device *hidpp;
|
||||
int ret;
|
||||
bool connected;
|
||||
unsigned int connect_mask = HID_CONNECT_DEFAULT;
|
||||
struct hidpp_ff_private_data data;
|
||||
bool will_restart = false;
|
||||
|
||||
/* report_fixup needs drvdata to be set before we call hid_parse */
|
||||
hidpp = devm_kzalloc(&hdev->dev, sizeof(*hidpp), GFP_KERNEL);
|
||||
@@ -4423,9 +4404,6 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
|
||||
}
|
||||
|
||||
if (id->group == HID_GROUP_LOGITECH_DJ_DEVICE)
|
||||
hidpp->quirks |= HIDPP_QUIRK_UNIFYING;
|
||||
|
||||
if (id->group == HID_GROUP_LOGITECH_27MHZ_DEVICE &&
|
||||
hidpp_application_equals(hdev, HID_GD_MOUSE))
|
||||
hidpp->quirks |= HIDPP_QUIRK_HIDPP_WHEELS |
|
||||
@@ -4445,11 +4423,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT ||
|
||||
hidpp->quirks & HIDPP_QUIRK_UNIFYING)
|
||||
will_restart = true;
|
||||
|
||||
INIT_WORK(&hidpp->work, delayed_work_cb);
|
||||
INIT_WORK(&hidpp->work, hidpp_connect_event);
|
||||
mutex_init(&hidpp->send_mutex);
|
||||
init_waitqueue_head(&hidpp->wait);
|
||||
|
||||
@@ -4460,10 +4434,12 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
hdev->name);
|
||||
|
||||
/*
|
||||
* Plain USB connections need to actually call start and open
|
||||
* on the transport driver to allow incoming data.
|
||||
* First call hid_hw_start(hdev, 0) to allow IO without connecting any
|
||||
* hid subdrivers (hid-input, hidraw). This allows retrieving the dev's
|
||||
* name and serial number and store these in hdev->name and hdev->uniq,
|
||||
* before the hid-input and hidraw drivers expose these to userspace.
|
||||
*/
|
||||
ret = hid_hw_start(hdev, will_restart ? 0 : connect_mask);
|
||||
ret = hid_hw_start(hdev, 0);
|
||||
if (ret) {
|
||||
hid_err(hdev, "hw start failed\n");
|
||||
goto hid_hw_start_fail;
|
||||
@@ -4479,70 +4455,46 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
/* Allow incoming packets */
|
||||
hid_device_io_start(hdev);
|
||||
|
||||
if (hidpp->quirks & HIDPP_QUIRK_UNIFYING)
|
||||
/* Get name + serial, store in hdev->name + hdev->uniq */
|
||||
if (id->group == HID_GROUP_LOGITECH_DJ_DEVICE)
|
||||
hidpp_unifying_init(hidpp);
|
||||
else if (hid_is_usb(hidpp->hid_dev))
|
||||
hidpp_serial_init(hidpp);
|
||||
else
|
||||
hidpp_non_unifying_init(hidpp);
|
||||
|
||||
connected = hidpp_root_get_protocol_version(hidpp) == 0;
|
||||
atomic_set(&hidpp->connected, connected);
|
||||
if (!(hidpp->quirks & HIDPP_QUIRK_UNIFYING)) {
|
||||
if (!connected) {
|
||||
ret = -ENODEV;
|
||||
hid_err(hdev, "Device not connected");
|
||||
goto hid_hw_init_fail;
|
||||
}
|
||||
if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT)
|
||||
connect_mask &= ~HID_CONNECT_HIDINPUT;
|
||||
|
||||
hidpp_overwrite_name(hdev);
|
||||
}
|
||||
|
||||
if (connected && hidpp->protocol_major >= 2) {
|
||||
ret = hidpp_set_wireless_feature_index(hidpp);
|
||||
if (ret == -ENOENT)
|
||||
hidpp->wireless_feature_index = 0;
|
||||
else if (ret)
|
||||
goto hid_hw_init_fail;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
if (connected && (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)) {
|
||||
ret = wtp_get_config(hidpp);
|
||||
if (ret)
|
||||
goto hid_hw_init_fail;
|
||||
} else if (connected && (hidpp->quirks & HIDPP_QUIRK_CLASS_G920)) {
|
||||
ret = g920_get_config(hidpp, &data);
|
||||
if (ret)
|
||||
goto hid_hw_init_fail;
|
||||
/* Now export the actual inputs and hidraw nodes to the world */
|
||||
hid_device_io_stop(hdev);
|
||||
ret = hid_connect(hdev, connect_mask);
|
||||
if (ret) {
|
||||
hid_err(hdev, "%s:hid_connect returned error %d\n", __func__, ret);
|
||||
goto hid_hw_init_fail;
|
||||
}
|
||||
|
||||
/* Check for connected devices now that incoming packets will not be disabled again */
|
||||
hid_device_io_start(hdev);
|
||||
schedule_work(&hidpp->work);
|
||||
flush_work(&hidpp->work);
|
||||
|
||||
if (will_restart) {
|
||||
/* Reset the HID node state */
|
||||
hid_device_io_stop(hdev);
|
||||
hid_hw_close(hdev);
|
||||
hid_hw_stop(hdev);
|
||||
|
||||
if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT)
|
||||
connect_mask &= ~HID_CONNECT_HIDINPUT;
|
||||
|
||||
/* Now export the actual inputs and hidraw nodes to the world */
|
||||
ret = hid_hw_start(hdev, connect_mask);
|
||||
if (ret) {
|
||||
hid_err(hdev, "%s:hid_hw_start returned error\n", __func__);
|
||||
goto hid_hw_start_fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) {
|
||||
ret = hidpp_ff_init(hidpp, &data);
|
||||
struct hidpp_ff_private_data data;
|
||||
|
||||
ret = g920_get_config(hidpp, &data);
|
||||
if (!ret)
|
||||
ret = hidpp_ff_init(hidpp, &data);
|
||||
|
||||
if (ret)
|
||||
hid_warn(hidpp->hid_dev,
|
||||
"Unable to initialize force feedback support, errno %d\n",
|
||||
ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* This relies on logi_dj_ll_close() being a no-op so that DJ connection
|
||||
* events will still be received.
|
||||
*/
|
||||
hid_hw_close(hdev);
|
||||
return ret;
|
||||
|
||||
hid_hw_init_fail:
|
||||
|
||||
@@ -1802,7 +1802,6 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int mt_suspend(struct hid_device *hdev, pm_message_t state)
|
||||
{
|
||||
struct mt_device *td = hid_get_drvdata(hdev);
|
||||
@@ -1836,7 +1835,6 @@ static int mt_resume(struct hid_device *hdev)
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void mt_remove(struct hid_device *hdev)
|
||||
{
|
||||
@@ -2259,10 +2257,8 @@ static struct hid_driver mt_driver = {
|
||||
.usage_table = mt_grabbed_usages,
|
||||
.event = mt_event,
|
||||
.report = mt_report,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = mt_suspend,
|
||||
.reset_resume = mt_reset_resume,
|
||||
.resume = mt_resume,
|
||||
#endif
|
||||
.suspend = pm_ptr(mt_suspend),
|
||||
.reset_resume = pm_ptr(mt_reset_resume),
|
||||
.resume = pm_ptr(mt_resume),
|
||||
};
|
||||
module_hid_driver(mt_driver);
|
||||
|
||||
+76
-57
@@ -410,6 +410,18 @@ static const char * const joycon_player_led_names[] = {
|
||||
LED_FUNCTION_PLAYER4,
|
||||
};
|
||||
#define JC_NUM_LEDS ARRAY_SIZE(joycon_player_led_names)
|
||||
#define JC_NUM_LED_PATTERNS 8
|
||||
/* Taken from https://www.nintendo.com/my/support/qa/detail/33822 */
|
||||
static const enum led_brightness joycon_player_led_patterns[JC_NUM_LED_PATTERNS][JC_NUM_LEDS] = {
|
||||
{ 1, 0, 0, 0 },
|
||||
{ 1, 1, 0, 0 },
|
||||
{ 1, 1, 1, 0 },
|
||||
{ 1, 1, 1, 1 },
|
||||
{ 1, 0, 0, 1 },
|
||||
{ 1, 0, 1, 0 },
|
||||
{ 1, 0, 1, 1 },
|
||||
{ 0, 1, 1, 0 },
|
||||
};
|
||||
|
||||
/* Each physical controller is associated with a joycon_ctlr struct */
|
||||
struct joycon_ctlr {
|
||||
@@ -699,6 +711,25 @@ static int joycon_set_player_leds(struct joycon_ctlr *ctlr, u8 flash, u8 on)
|
||||
return joycon_send_subcmd(ctlr, req, 1, HZ/4);
|
||||
}
|
||||
|
||||
static int joycon_set_home_led(struct joycon_ctlr *ctlr, enum led_brightness brightness)
|
||||
{
|
||||
struct joycon_subcmd_request *req;
|
||||
u8 buffer[sizeof(*req) + 5] = { 0 };
|
||||
u8 *data;
|
||||
|
||||
req = (struct joycon_subcmd_request *)buffer;
|
||||
req->subcmd_id = JC_SUBCMD_SET_HOME_LIGHT;
|
||||
data = req->data;
|
||||
data[0] = 0x01;
|
||||
data[1] = brightness << 4;
|
||||
data[2] = brightness | (brightness << 4);
|
||||
data[3] = 0x11;
|
||||
data[4] = 0x11;
|
||||
|
||||
hid_dbg(ctlr->hdev, "setting home led brightness\n");
|
||||
return joycon_send_subcmd(ctlr, req, 5, HZ/4);
|
||||
}
|
||||
|
||||
static int joycon_request_spi_flash_read(struct joycon_ctlr *ctlr,
|
||||
u32 start_addr, u8 size, u8 **reply)
|
||||
{
|
||||
@@ -1840,6 +1871,7 @@ static int joycon_input_create(struct joycon_ctlr *ctlr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Because the subcommand sets all the leds at once, the brightness argument is ignored */
|
||||
static int joycon_player_led_brightness_set(struct led_classdev *led,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
@@ -1849,7 +1881,6 @@ static int joycon_player_led_brightness_set(struct led_classdev *led,
|
||||
int val = 0;
|
||||
int i;
|
||||
int ret;
|
||||
int num;
|
||||
|
||||
ctlr = hid_get_drvdata(hdev);
|
||||
if (!ctlr) {
|
||||
@@ -1857,21 +1888,10 @@ static int joycon_player_led_brightness_set(struct led_classdev *led,
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* determine which player led this is */
|
||||
for (num = 0; num < JC_NUM_LEDS; num++) {
|
||||
if (&ctlr->leds[num] == led)
|
||||
break;
|
||||
}
|
||||
if (num >= JC_NUM_LEDS)
|
||||
return -EINVAL;
|
||||
for (i = 0; i < JC_NUM_LEDS; i++)
|
||||
val |= ctlr->leds[i].brightness << i;
|
||||
|
||||
mutex_lock(&ctlr->output_mutex);
|
||||
for (i = 0; i < JC_NUM_LEDS; i++) {
|
||||
if (i == num)
|
||||
val |= brightness << i;
|
||||
else
|
||||
val |= ctlr->leds[i].brightness << i;
|
||||
}
|
||||
ret = joycon_set_player_leds(ctlr, 0, val);
|
||||
mutex_unlock(&ctlr->output_mutex);
|
||||
|
||||
@@ -1884,9 +1904,6 @@ static int joycon_home_led_brightness_set(struct led_classdev *led,
|
||||
struct device *dev = led->dev->parent;
|
||||
struct hid_device *hdev = to_hid_device(dev);
|
||||
struct joycon_ctlr *ctlr;
|
||||
struct joycon_subcmd_request *req;
|
||||
u8 buffer[sizeof(*req) + 5] = { 0 };
|
||||
u8 *data;
|
||||
int ret;
|
||||
|
||||
ctlr = hid_get_drvdata(hdev);
|
||||
@@ -1894,43 +1911,35 @@ static int joycon_home_led_brightness_set(struct led_classdev *led,
|
||||
hid_err(hdev, "No controller data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
req = (struct joycon_subcmd_request *)buffer;
|
||||
req->subcmd_id = JC_SUBCMD_SET_HOME_LIGHT;
|
||||
data = req->data;
|
||||
data[0] = 0x01;
|
||||
data[1] = brightness << 4;
|
||||
data[2] = brightness | (brightness << 4);
|
||||
data[3] = 0x11;
|
||||
data[4] = 0x11;
|
||||
|
||||
hid_dbg(hdev, "setting home led brightness\n");
|
||||
mutex_lock(&ctlr->output_mutex);
|
||||
ret = joycon_send_subcmd(ctlr, req, 5, HZ/4);
|
||||
ret = joycon_set_home_led(ctlr, brightness);
|
||||
mutex_unlock(&ctlr->output_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static DEFINE_MUTEX(joycon_input_num_mutex);
|
||||
static DEFINE_SPINLOCK(joycon_input_num_spinlock);
|
||||
static int joycon_leds_create(struct joycon_ctlr *ctlr)
|
||||
{
|
||||
struct hid_device *hdev = ctlr->hdev;
|
||||
struct device *dev = &hdev->dev;
|
||||
const char *d_name = dev_name(dev);
|
||||
struct led_classdev *led;
|
||||
int led_val = 0;
|
||||
char *name;
|
||||
int ret = 0;
|
||||
int ret;
|
||||
int i;
|
||||
static int input_num = 1;
|
||||
unsigned long flags;
|
||||
int player_led_pattern;
|
||||
static int input_num;
|
||||
|
||||
/* Set the default controller player leds based on controller number */
|
||||
mutex_lock(&joycon_input_num_mutex);
|
||||
mutex_lock(&ctlr->output_mutex);
|
||||
ret = joycon_set_player_leds(ctlr, 0, 0xF >> (4 - input_num));
|
||||
if (ret)
|
||||
hid_warn(ctlr->hdev, "Failed to set leds; ret=%d\n", ret);
|
||||
mutex_unlock(&ctlr->output_mutex);
|
||||
/*
|
||||
* Set the player leds based on controller number
|
||||
* Because there is no standard concept of "player number", the pattern
|
||||
* number will simply increase by 1 every time a controller is connected.
|
||||
*/
|
||||
spin_lock_irqsave(&joycon_input_num_spinlock, flags);
|
||||
player_led_pattern = input_num++ % JC_NUM_LED_PATTERNS;
|
||||
spin_unlock_irqrestore(&joycon_input_num_spinlock, flags);
|
||||
|
||||
/* configure the player LEDs */
|
||||
for (i = 0; i < JC_NUM_LEDS; i++) {
|
||||
@@ -1938,31 +1947,37 @@ static int joycon_leds_create(struct joycon_ctlr *ctlr)
|
||||
d_name,
|
||||
"green",
|
||||
joycon_player_led_names[i]);
|
||||
if (!name) {
|
||||
mutex_unlock(&joycon_input_num_mutex);
|
||||
if (!name)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
led = &ctlr->leds[i];
|
||||
led->name = name;
|
||||
led->brightness = ((i + 1) <= input_num) ? 1 : 0;
|
||||
led->brightness = joycon_player_led_patterns[player_led_pattern][i];
|
||||
led->max_brightness = 1;
|
||||
led->brightness_set_blocking =
|
||||
joycon_player_led_brightness_set;
|
||||
led->flags = LED_CORE_SUSPENDRESUME | LED_HW_PLUGGABLE;
|
||||
|
||||
led_val |= joycon_player_led_patterns[player_led_pattern][i] << i;
|
||||
}
|
||||
mutex_lock(&ctlr->output_mutex);
|
||||
ret = joycon_set_player_leds(ctlr, 0, led_val);
|
||||
mutex_unlock(&ctlr->output_mutex);
|
||||
if (ret) {
|
||||
hid_warn(hdev, "Failed to set players LEDs, skipping registration; ret=%d\n", ret);
|
||||
goto home_led;
|
||||
}
|
||||
|
||||
for (i = 0; i < JC_NUM_LEDS; i++) {
|
||||
led = &ctlr->leds[i];
|
||||
ret = devm_led_classdev_register(&hdev->dev, led);
|
||||
if (ret) {
|
||||
hid_err(hdev, "Failed registering %s LED\n", led->name);
|
||||
mutex_unlock(&joycon_input_num_mutex);
|
||||
hid_err(hdev, "Failed to register player %d LED; ret=%d\n", i + 1, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (++input_num > 4)
|
||||
input_num = 1;
|
||||
mutex_unlock(&joycon_input_num_mutex);
|
||||
|
||||
home_led:
|
||||
/* configure the home LED */
|
||||
if (jc_type_has_right(ctlr)) {
|
||||
name = devm_kasprintf(dev, GFP_KERNEL, "%s:%s:%s",
|
||||
@@ -1978,17 +1993,21 @@ static int joycon_leds_create(struct joycon_ctlr *ctlr)
|
||||
led->max_brightness = 0xF;
|
||||
led->brightness_set_blocking = joycon_home_led_brightness_set;
|
||||
led->flags = LED_CORE_SUSPENDRESUME | LED_HW_PLUGGABLE;
|
||||
|
||||
/* Set the home LED to 0 as default state */
|
||||
mutex_lock(&ctlr->output_mutex);
|
||||
ret = joycon_set_home_led(ctlr, 0);
|
||||
mutex_unlock(&ctlr->output_mutex);
|
||||
if (ret) {
|
||||
hid_warn(hdev, "Failed to set home LED, skipping registration; ret=%d\n", ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = devm_led_classdev_register(&hdev->dev, led);
|
||||
if (ret) {
|
||||
hid_err(hdev, "Failed registering home led\n");
|
||||
hid_err(hdev, "Failed to register home LED; ret=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
/* Set the home LED to 0 as default state */
|
||||
ret = joycon_home_led_brightness_set(led, 0);
|
||||
if (ret) {
|
||||
hid_warn(hdev, "Failed to set home LED default, unregistering home LED");
|
||||
devm_led_classdev_unregister(&hdev->dev, led);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -915,6 +915,15 @@ err_id:
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static void thunderstrike_destroy(struct thunderstrike *ts)
|
||||
{
|
||||
led_classdev_unregister(&ts->led_dev);
|
||||
power_supply_unregister(ts->base.battery_dev.psy);
|
||||
if (ts->haptics_dev)
|
||||
input_unregister_device(ts->haptics_dev);
|
||||
ida_free(&thunderstrike_ida, ts->id);
|
||||
}
|
||||
|
||||
static int android_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
struct hid_field *field,
|
||||
struct hid_usage *usage, unsigned long **bit,
|
||||
@@ -1074,11 +1083,7 @@ static int shield_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
err_stop:
|
||||
hid_hw_stop(hdev);
|
||||
err_ts_create:
|
||||
power_supply_unregister(ts->base.battery_dev.psy);
|
||||
if (ts->haptics_dev)
|
||||
input_unregister_device(ts->haptics_dev);
|
||||
led_classdev_unregister(&ts->led_dev);
|
||||
ida_free(&thunderstrike_ida, ts->id);
|
||||
thunderstrike_destroy(ts);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1090,11 +1095,7 @@ static void shield_remove(struct hid_device *hdev)
|
||||
ts = container_of(dev, struct thunderstrike, base);
|
||||
|
||||
hid_hw_close(hdev);
|
||||
power_supply_unregister(dev->battery_dev.psy);
|
||||
if (ts->haptics_dev)
|
||||
input_unregister_device(ts->haptics_dev);
|
||||
led_classdev_unregister(&ts->led_dev);
|
||||
ida_free(&thunderstrike_ida, ts->id);
|
||||
thunderstrike_destroy(ts);
|
||||
del_timer_sync(&ts->psy_stats_timer);
|
||||
cancel_work_sync(&ts->hostcmd_req_work);
|
||||
hid_hw_stop(hdev);
|
||||
|
||||
@@ -66,6 +66,7 @@ static const struct hid_device_id hid_quirks[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_STRAFE), HID_QUIRK_NO_INIT_REPORTS | HID_QUIRK_ALWAYS_POLL },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_CREATIVE_SB_OMNI_SURROUND_51), HID_QUIRK_NOGET },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_PIXART_USB_OPTICAL_MOUSE), HID_QUIRK_ALWAYS_POLL },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_PRO_WIRELESS_KM5221W), HID_QUIRK_ALWAYS_POLL },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC), HID_QUIRK_NOGET },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DRACAL_RAPHNET, USB_DEVICE_ID_RAPHNET_2NES2SNES), HID_QUIRK_MULTI_INPUT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DRACAL_RAPHNET, USB_DEVICE_ID_RAPHNET_4NES4SNES), HID_QUIRK_MULTI_INPUT },
|
||||
|
||||
@@ -436,7 +436,6 @@ static void rmi_report(struct hid_device *hid, struct hid_report *report)
|
||||
input_sync(field->hidinput->input);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int rmi_suspend(struct hid_device *hdev, pm_message_t message)
|
||||
{
|
||||
struct rmi_data *data = hid_get_drvdata(hdev);
|
||||
@@ -483,7 +482,6 @@ out:
|
||||
hid_hw_close(hdev);
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static int rmi_hid_reset(struct rmi_transport_dev *xport, u16 reset_addr)
|
||||
{
|
||||
@@ -774,11 +772,9 @@ static struct hid_driver rmi_driver = {
|
||||
.report = rmi_report,
|
||||
.input_mapping = rmi_input_mapping,
|
||||
.input_configured = rmi_input_configured,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = rmi_suspend,
|
||||
.resume = rmi_post_resume,
|
||||
.reset_resume = rmi_post_resume,
|
||||
#endif
|
||||
.suspend = pm_ptr(rmi_suspend),
|
||||
.resume = pm_ptr(rmi_post_resume),
|
||||
.reset_resume = pm_ptr(rmi_post_resume),
|
||||
};
|
||||
|
||||
module_hid_driver(rmi_driver);
|
||||
|
||||
@@ -56,6 +56,11 @@ static struct uclogic_raw_event_hook_test test_events[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static void fake_work(struct work_struct *work)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void hid_test_uclogic_exec_event_hook_test(struct kunit *test)
|
||||
{
|
||||
struct uclogic_params p = {0, };
|
||||
@@ -77,6 +82,8 @@ static void hid_test_uclogic_exec_event_hook_test(struct kunit *test)
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filter->event);
|
||||
memcpy(filter->event, &hook_events[n].event[0], filter->size);
|
||||
|
||||
INIT_WORK(&filter->work, fake_work);
|
||||
|
||||
list_add_tail(&filter->list, &p.event_hooks->list);
|
||||
}
|
||||
|
||||
|
||||
@@ -174,12 +174,26 @@ static void hid_test_uclogic_parse_ugee_v2_desc(struct kunit *test)
|
||||
KUNIT_EXPECT_EQ(test, params->frame_type, frame_type);
|
||||
}
|
||||
|
||||
struct fake_device {
|
||||
unsigned long quirks;
|
||||
};
|
||||
|
||||
static void hid_test_uclogic_params_cleanup_event_hooks(struct kunit *test)
|
||||
{
|
||||
int res, n;
|
||||
struct hid_device *hdev;
|
||||
struct fake_device *fake_dev;
|
||||
struct uclogic_params p = {0, };
|
||||
|
||||
res = uclogic_params_ugee_v2_init_event_hooks(NULL, &p);
|
||||
hdev = kunit_kzalloc(test, sizeof(struct hid_device), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, hdev);
|
||||
|
||||
fake_dev = kunit_kzalloc(test, sizeof(struct fake_device), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fake_dev);
|
||||
|
||||
hid_set_drvdata(hdev, fake_dev);
|
||||
|
||||
res = uclogic_params_ugee_v2_init_event_hooks(hdev, &p);
|
||||
KUNIT_ASSERT_EQ(test, res, 0);
|
||||
|
||||
/* Check that the function can be called repeatedly */
|
||||
|
||||
@@ -1562,7 +1562,6 @@ static int hid_post_reset(struct usb_interface *intf)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int hid_resume_common(struct hid_device *hid, bool driver_suspended)
|
||||
{
|
||||
int status = 0;
|
||||
@@ -1654,8 +1653,6 @@ static int hid_reset_resume(struct usb_interface *intf)
|
||||
return status;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static const struct usb_device_id hid_usb_ids[] = {
|
||||
{ .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
|
||||
.bInterfaceClass = USB_INTERFACE_CLASS_HID },
|
||||
@@ -1668,11 +1665,9 @@ static struct usb_driver hid_driver = {
|
||||
.name = "usbhid",
|
||||
.probe = usbhid_probe,
|
||||
.disconnect = usbhid_disconnect,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = hid_suspend,
|
||||
.resume = hid_resume,
|
||||
.reset_resume = hid_reset_resume,
|
||||
#endif
|
||||
.suspend = pm_ptr(hid_suspend),
|
||||
.resume = pm_ptr(hid_resume),
|
||||
.reset_resume = pm_ptr(hid_reset_resume),
|
||||
.pre_reset = hid_pre_reset,
|
||||
.post_reset = hid_post_reset,
|
||||
.id_table = hid_usb_ids,
|
||||
|
||||
@@ -81,6 +81,7 @@
|
||||
#define PCI_DEVICE_ID_RENESAS_R8A774B1 0x002b
|
||||
#define PCI_DEVICE_ID_RENESAS_R8A774C0 0x002d
|
||||
#define PCI_DEVICE_ID_RENESAS_R8A774E1 0x0025
|
||||
#define PCI_DEVICE_ID_RENESAS_R8A779F0 0x0031
|
||||
|
||||
static DEFINE_IDA(pci_endpoint_test_ida);
|
||||
|
||||
@@ -990,6 +991,9 @@ static const struct pci_device_id pci_endpoint_test_tbl[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774B1),},
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774C0),},
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A774E1),},
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_R8A779F0),
|
||||
.driver_data = (kernel_ulong_t)&default_data,
|
||||
},
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_J721E),
|
||||
.driver_data = (kernel_ulong_t)&j721e_data,
|
||||
},
|
||||
|
||||
+1
-1
@@ -170,7 +170,7 @@ config PCI_P2PDMA
|
||||
select GENERIC_ALLOCATOR
|
||||
select NEED_SG_DMA_FLAGS
|
||||
help
|
||||
Enableѕ drivers to do PCI peer-to-peer transactions to and from
|
||||
Enables drivers to do PCI peer-to-peer transactions to and from
|
||||
BARs that are exposed in other devices that are the part of
|
||||
the hierarchy where peer-to-peer DMA is guaranteed by the PCI
|
||||
specification to work (ie. anything below a single PCI bridge).
|
||||
|
||||
+2
-5
@@ -9,6 +9,7 @@
|
||||
* Copyright (C) 2011 Advanced Micro Devices,
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/pci-ats.h>
|
||||
#include <linux/pci.h>
|
||||
@@ -480,8 +481,6 @@ int pci_pasid_features(struct pci_dev *pdev)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_pasid_features);
|
||||
|
||||
#define PASID_NUMBER_SHIFT 8
|
||||
#define PASID_NUMBER_MASK (0x1f << PASID_NUMBER_SHIFT)
|
||||
/**
|
||||
* pci_max_pasids - Get maximum number of PASIDs supported by device
|
||||
* @pdev: PCI device structure
|
||||
@@ -503,9 +502,7 @@ int pci_max_pasids(struct pci_dev *pdev)
|
||||
|
||||
pci_read_config_word(pdev, pasid + PCI_PASID_CAP, &supported);
|
||||
|
||||
supported = (supported & PASID_NUMBER_MASK) >> PASID_NUMBER_SHIFT;
|
||||
|
||||
return (1 << supported);
|
||||
return (1 << FIELD_GET(PCI_PASID_CAP_WIDTH, supported));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_max_pasids);
|
||||
#endif /* CONFIG_PCI_PASID */
|
||||
|
||||
@@ -324,6 +324,17 @@ config PCIE_XILINX
|
||||
Say 'Y' here if you want kernel to support the Xilinx AXI PCIe
|
||||
Host Bridge driver.
|
||||
|
||||
config PCIE_XILINX_DMA_PL
|
||||
bool "Xilinx DMA PL PCIe host bridge support"
|
||||
depends on ARCH_ZYNQMP || COMPILE_TEST
|
||||
depends on PCI_MSI
|
||||
select PCI_HOST_COMMON
|
||||
help
|
||||
Say 'Y' here if you want kernel support for the Xilinx PL DMA
|
||||
PCIe host bridge. The controller is a Soft IP which can act as
|
||||
Root Port. If your system provides Xilinx PCIe host controller
|
||||
bridge DMA as Soft IP say 'Y'; if you are not sure, say 'N'.
|
||||
|
||||
config PCIE_XILINX_NWL
|
||||
bool "Xilinx NWL PCIe controller"
|
||||
depends on ARCH_ZYNQMP || COMPILE_TEST
|
||||
|
||||
@@ -17,6 +17,7 @@ obj-$(CONFIG_PCI_HOST_THUNDER_PEM) += pci-thunder-pem.o
|
||||
obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
|
||||
obj-$(CONFIG_PCIE_XILINX_NWL) += pcie-xilinx-nwl.o
|
||||
obj-$(CONFIG_PCIE_XILINX_CPM) += pcie-xilinx-cpm.o
|
||||
obj-$(CONFIG_PCIE_XILINX_DMA_PL) += pcie-xilinx-dma-pl.o
|
||||
obj-$(CONFIG_PCI_V3_SEMI) += pci-v3-semi.o
|
||||
obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
|
||||
obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Cadence PCIe endpoint controller driver.
|
||||
// Author: Cyrille Pitchen <cyrille.pitchen@free-electrons.com>
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of.h>
|
||||
@@ -262,7 +263,7 @@ static int cdns_pcie_ep_get_msi(struct pci_epc *epc, u8 fn, u8 vfn)
|
||||
* Get the Multiple Message Enable bitfield from the Message Control
|
||||
* register.
|
||||
*/
|
||||
mme = (flags & PCI_MSI_FLAGS_QSIZE) >> 4;
|
||||
mme = FIELD_GET(PCI_MSI_FLAGS_QSIZE, flags);
|
||||
|
||||
return mme;
|
||||
}
|
||||
@@ -394,7 +395,7 @@ static int cdns_pcie_ep_send_msi_irq(struct cdns_pcie_ep *ep, u8 fn, u8 vfn,
|
||||
return -EINVAL;
|
||||
|
||||
/* Get the number of enabled MSIs */
|
||||
mme = (flags & PCI_MSI_FLAGS_QSIZE) >> 4;
|
||||
mme = FIELD_GET(PCI_MSI_FLAGS_QSIZE, flags);
|
||||
msi_count = 1 << mme;
|
||||
if (!interrupt_num || interrupt_num > msi_count)
|
||||
return -EINVAL;
|
||||
@@ -449,7 +450,7 @@ static int cdns_pcie_ep_map_msi_irq(struct pci_epc *epc, u8 fn, u8 vfn,
|
||||
return -EINVAL;
|
||||
|
||||
/* Get the number of enabled MSIs */
|
||||
mme = (flags & PCI_MSI_FLAGS_QSIZE) >> 4;
|
||||
mme = FIELD_GET(PCI_MSI_FLAGS_QSIZE, flags);
|
||||
msi_count = 1 << mme;
|
||||
if (!interrupt_num || interrupt_num > msi_count)
|
||||
return -EINVAL;
|
||||
@@ -506,7 +507,7 @@ static int cdns_pcie_ep_send_msix_irq(struct cdns_pcie_ep *ep, u8 fn, u8 vfn,
|
||||
|
||||
reg = cap + PCI_MSIX_TABLE;
|
||||
tbl_offset = cdns_pcie_ep_fn_readl(pcie, fn, reg);
|
||||
bir = tbl_offset & PCI_MSIX_TABLE_BIR;
|
||||
bir = FIELD_GET(PCI_MSIX_TABLE_BIR, tbl_offset);
|
||||
tbl_offset &= PCI_MSIX_TABLE_OFFSET;
|
||||
|
||||
msix_tbl = epf->epf_bar[bir]->addr + tbl_offset;
|
||||
|
||||
@@ -17,12 +17,9 @@
|
||||
/**
|
||||
* struct cdns_plat_pcie - private data for this PCIe platform driver
|
||||
* @pcie: Cadence PCIe controller
|
||||
* @is_rc: Set to 1 indicates the PCIe controller mode is Root Complex,
|
||||
* if 0 it is in Endpoint mode.
|
||||
*/
|
||||
struct cdns_plat_pcie {
|
||||
struct cdns_pcie *pcie;
|
||||
bool is_rc;
|
||||
};
|
||||
|
||||
struct cdns_plat_pcie_of_data {
|
||||
@@ -76,7 +73,6 @@ static int cdns_plat_pcie_probe(struct platform_device *pdev)
|
||||
rc->pcie.dev = dev;
|
||||
rc->pcie.ops = &cdns_plat_ops;
|
||||
cdns_plat_pcie->pcie = &rc->pcie;
|
||||
cdns_plat_pcie->is_rc = is_rc;
|
||||
|
||||
ret = cdns_pcie_init_phy(dev, cdns_plat_pcie->pcie);
|
||||
if (ret) {
|
||||
@@ -104,7 +100,6 @@ static int cdns_plat_pcie_probe(struct platform_device *pdev)
|
||||
ep->pcie.dev = dev;
|
||||
ep->pcie.ops = &cdns_plat_ops;
|
||||
cdns_plat_pcie->pcie = &ep->pcie;
|
||||
cdns_plat_pcie->is_rc = is_rc;
|
||||
|
||||
ret = cdns_pcie_init_phy(dev, cdns_plat_pcie->pcie);
|
||||
if (ret) {
|
||||
|
||||
@@ -286,6 +286,31 @@ config PCIE_QCOM_EP
|
||||
to work in endpoint mode. The PCIe controller uses the DesignWare core
|
||||
plus Qualcomm-specific hardware wrappers.
|
||||
|
||||
config PCIE_RCAR_GEN4
|
||||
tristate
|
||||
|
||||
config PCIE_RCAR_GEN4_HOST
|
||||
tristate "Renesas R-Car Gen4 PCIe controller (host mode)"
|
||||
depends on ARCH_RENESAS || COMPILE_TEST
|
||||
depends on PCI_MSI
|
||||
select PCIE_DW_HOST
|
||||
select PCIE_RCAR_GEN4
|
||||
help
|
||||
Say Y here if you want PCIe controller (host mode) on R-Car Gen4 SoCs.
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called pcie-rcar-gen4.ko. This uses the DesignWare core.
|
||||
|
||||
config PCIE_RCAR_GEN4_EP
|
||||
tristate "Renesas R-Car Gen4 PCIe controller (endpoint mode)"
|
||||
depends on ARCH_RENESAS || COMPILE_TEST
|
||||
depends on PCI_ENDPOINT
|
||||
select PCIE_DW_EP
|
||||
select PCIE_RCAR_GEN4
|
||||
help
|
||||
Say Y here if you want PCIe controller (endpoint mode) on R-Car Gen4
|
||||
SoCs. To compile this driver as a module, choose M here: the module
|
||||
will be called pcie-rcar-gen4.ko. This uses the DesignWare core.
|
||||
|
||||
config PCIE_ROCKCHIP_DW_HOST
|
||||
bool "Rockchip DesignWare PCIe controller"
|
||||
select PCIE_DW
|
||||
|
||||
@@ -26,6 +26,7 @@ obj-$(CONFIG_PCIE_TEGRA194) += pcie-tegra194.o
|
||||
obj-$(CONFIG_PCIE_UNIPHIER) += pcie-uniphier.o
|
||||
obj-$(CONFIG_PCIE_UNIPHIER_EP) += pcie-uniphier-ep.o
|
||||
obj-$(CONFIG_PCIE_VISCONTI_HOST) += pcie-visconti.o
|
||||
obj-$(CONFIG_PCIE_RCAR_GEN4) += pcie-rcar-gen4.o
|
||||
|
||||
# The following drivers are for devices that use the generic ACPI
|
||||
# pci_root.c driver but don't support standard ECAM config access.
|
||||
|
||||
@@ -375,7 +375,7 @@ fail_probe:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __exit exynos_pcie_remove(struct platform_device *pdev)
|
||||
static int exynos_pcie_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct exynos_pcie *ep = platform_get_drvdata(pdev);
|
||||
|
||||
@@ -431,7 +431,7 @@ static const struct of_device_id exynos_pcie_of_match[] = {
|
||||
|
||||
static struct platform_driver exynos_pcie_driver = {
|
||||
.probe = exynos_pcie_probe,
|
||||
.remove = __exit_p(exynos_pcie_remove),
|
||||
.remove = exynos_pcie_remove,
|
||||
.driver = {
|
||||
.name = "exynos-pcie",
|
||||
.of_match_table = exynos_pcie_of_match,
|
||||
|
||||
@@ -1100,7 +1100,7 @@ static const struct of_device_id ks_pcie_of_match[] = {
|
||||
{ },
|
||||
};
|
||||
|
||||
static int __init ks_pcie_probe(struct platform_device *pdev)
|
||||
static int ks_pcie_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct dw_pcie_host_ops *host_ops;
|
||||
const struct dw_pcie_ep_ops *ep_ops;
|
||||
@@ -1302,7 +1302,7 @@ err_link:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __exit ks_pcie_remove(struct platform_device *pdev)
|
||||
static int ks_pcie_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct keystone_pcie *ks_pcie = platform_get_drvdata(pdev);
|
||||
struct device_link **link = ks_pcie->link;
|
||||
@@ -1318,9 +1318,9 @@ static int __exit ks_pcie_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver ks_pcie_driver __refdata = {
|
||||
static struct platform_driver ks_pcie_driver = {
|
||||
.probe = ks_pcie_probe,
|
||||
.remove = __exit_p(ks_pcie_remove),
|
||||
.remove = ks_pcie_remove,
|
||||
.driver = {
|
||||
.name = "keystone-pcie",
|
||||
.of_match_table = ks_pcie_of_match,
|
||||
|
||||
@@ -266,6 +266,8 @@ static int __init ls_pcie_ep_probe(struct platform_device *pdev)
|
||||
|
||||
pcie->big_endian = of_property_read_bool(dev->of_node, "big-endian");
|
||||
|
||||
dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
|
||||
|
||||
platform_set_drvdata(pdev, pcie);
|
||||
|
||||
offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
|
||||
|
||||
@@ -58,7 +58,7 @@ static bool ls_pcie_is_bridge(struct ls_pcie *pcie)
|
||||
u32 header_type;
|
||||
|
||||
header_type = ioread8(pci->dbi_base + PCI_HEADER_TYPE);
|
||||
header_type &= 0x7f;
|
||||
header_type &= PCI_HEADER_TYPE_MASK;
|
||||
|
||||
return header_type == PCI_HEADER_TYPE_BRIDGE;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
* Author: Kishon Vijay Abraham I <kishon@ti.com>
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
@@ -52,21 +53,35 @@ static unsigned int dw_pcie_ep_func_select(struct dw_pcie_ep *ep, u8 func_no)
|
||||
return func_offset;
|
||||
}
|
||||
|
||||
static unsigned int dw_pcie_ep_get_dbi2_offset(struct dw_pcie_ep *ep, u8 func_no)
|
||||
{
|
||||
unsigned int dbi2_offset = 0;
|
||||
|
||||
if (ep->ops->get_dbi2_offset)
|
||||
dbi2_offset = ep->ops->get_dbi2_offset(ep, func_no);
|
||||
else if (ep->ops->func_conf_select) /* for backward compatibility */
|
||||
dbi2_offset = ep->ops->func_conf_select(ep, func_no);
|
||||
|
||||
return dbi2_offset;
|
||||
}
|
||||
|
||||
static void __dw_pcie_ep_reset_bar(struct dw_pcie *pci, u8 func_no,
|
||||
enum pci_barno bar, int flags)
|
||||
{
|
||||
u32 reg;
|
||||
unsigned int func_offset = 0;
|
||||
unsigned int func_offset, dbi2_offset;
|
||||
struct dw_pcie_ep *ep = &pci->ep;
|
||||
u32 reg, reg_dbi2;
|
||||
|
||||
func_offset = dw_pcie_ep_func_select(ep, func_no);
|
||||
dbi2_offset = dw_pcie_ep_get_dbi2_offset(ep, func_no);
|
||||
|
||||
reg = func_offset + PCI_BASE_ADDRESS_0 + (4 * bar);
|
||||
reg_dbi2 = dbi2_offset + PCI_BASE_ADDRESS_0 + (4 * bar);
|
||||
dw_pcie_dbi_ro_wr_en(pci);
|
||||
dw_pcie_writel_dbi2(pci, reg, 0x0);
|
||||
dw_pcie_writel_dbi2(pci, reg_dbi2, 0x0);
|
||||
dw_pcie_writel_dbi(pci, reg, 0x0);
|
||||
if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) {
|
||||
dw_pcie_writel_dbi2(pci, reg + 4, 0x0);
|
||||
dw_pcie_writel_dbi2(pci, reg_dbi2 + 4, 0x0);
|
||||
dw_pcie_writel_dbi(pci, reg + 4, 0x0);
|
||||
}
|
||||
dw_pcie_dbi_ro_wr_dis(pci);
|
||||
@@ -228,16 +243,18 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
|
||||
{
|
||||
struct dw_pcie_ep *ep = epc_get_drvdata(epc);
|
||||
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
|
||||
unsigned int func_offset, dbi2_offset;
|
||||
enum pci_barno bar = epf_bar->barno;
|
||||
size_t size = epf_bar->size;
|
||||
int flags = epf_bar->flags;
|
||||
unsigned int func_offset = 0;
|
||||
u32 reg, reg_dbi2;
|
||||
int ret, type;
|
||||
u32 reg;
|
||||
|
||||
func_offset = dw_pcie_ep_func_select(ep, func_no);
|
||||
dbi2_offset = dw_pcie_ep_get_dbi2_offset(ep, func_no);
|
||||
|
||||
reg = PCI_BASE_ADDRESS_0 + (4 * bar) + func_offset;
|
||||
reg_dbi2 = PCI_BASE_ADDRESS_0 + (4 * bar) + dbi2_offset;
|
||||
|
||||
if (!(flags & PCI_BASE_ADDRESS_SPACE))
|
||||
type = PCIE_ATU_TYPE_MEM;
|
||||
@@ -253,11 +270,11 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
|
||||
|
||||
dw_pcie_dbi_ro_wr_en(pci);
|
||||
|
||||
dw_pcie_writel_dbi2(pci, reg, lower_32_bits(size - 1));
|
||||
dw_pcie_writel_dbi2(pci, reg_dbi2, lower_32_bits(size - 1));
|
||||
dw_pcie_writel_dbi(pci, reg, flags);
|
||||
|
||||
if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) {
|
||||
dw_pcie_writel_dbi2(pci, reg + 4, upper_32_bits(size - 1));
|
||||
dw_pcie_writel_dbi2(pci, reg_dbi2 + 4, upper_32_bits(size - 1));
|
||||
dw_pcie_writel_dbi(pci, reg + 4, 0);
|
||||
}
|
||||
|
||||
@@ -334,7 +351,7 @@ static int dw_pcie_ep_get_msi(struct pci_epc *epc, u8 func_no, u8 vfunc_no)
|
||||
if (!(val & PCI_MSI_FLAGS_ENABLE))
|
||||
return -EINVAL;
|
||||
|
||||
val = (val & PCI_MSI_FLAGS_QSIZE) >> 4;
|
||||
val = FIELD_GET(PCI_MSI_FLAGS_QSIZE, val);
|
||||
|
||||
return val;
|
||||
}
|
||||
@@ -357,7 +374,7 @@ static int dw_pcie_ep_set_msi(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
|
||||
reg = ep_func->msi_cap + func_offset + PCI_MSI_FLAGS;
|
||||
val = dw_pcie_readw_dbi(pci, reg);
|
||||
val &= ~PCI_MSI_FLAGS_QMASK;
|
||||
val |= (interrupts << 1) & PCI_MSI_FLAGS_QMASK;
|
||||
val |= FIELD_PREP(PCI_MSI_FLAGS_QMASK, interrupts);
|
||||
dw_pcie_dbi_ro_wr_en(pci);
|
||||
dw_pcie_writew_dbi(pci, reg, val);
|
||||
dw_pcie_dbi_ro_wr_dis(pci);
|
||||
@@ -584,7 +601,7 @@ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no,
|
||||
|
||||
reg = ep_func->msix_cap + func_offset + PCI_MSIX_TABLE;
|
||||
tbl_offset = dw_pcie_readl_dbi(pci, reg);
|
||||
bir = (tbl_offset & PCI_MSIX_TABLE_BIR);
|
||||
bir = FIELD_GET(PCI_MSIX_TABLE_BIR, tbl_offset);
|
||||
tbl_offset &= PCI_MSIX_TABLE_OFFSET;
|
||||
|
||||
msix_tbl = ep->epf_bar[bir]->addr + tbl_offset;
|
||||
@@ -621,7 +638,11 @@ void dw_pcie_ep_exit(struct dw_pcie_ep *ep)
|
||||
epc->mem->window.page_size);
|
||||
|
||||
pci_epc_mem_exit(epc);
|
||||
|
||||
if (ep->ops->deinit)
|
||||
ep->ops->deinit(ep);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dw_pcie_ep_exit);
|
||||
|
||||
static unsigned int dw_pcie_ep_find_ext_capability(struct dw_pcie *pci, int cap)
|
||||
{
|
||||
@@ -723,6 +744,9 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)
|
||||
ep->phys_base = res->start;
|
||||
ep->addr_size = resource_size(res);
|
||||
|
||||
if (ep->ops->pre_init)
|
||||
ep->ops->pre_init(ep);
|
||||
|
||||
dw_pcie_version_detect(pci);
|
||||
|
||||
dw_pcie_iatu_detect(pci);
|
||||
@@ -777,7 +801,7 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)
|
||||
ep->page_size);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to initialize address space\n");
|
||||
return ret;
|
||||
goto err_ep_deinit;
|
||||
}
|
||||
|
||||
ep->msi_mem = pci_epc_mem_alloc_addr(epc, &ep->msi_mem_phys,
|
||||
@@ -814,6 +838,10 @@ err_free_epc_mem:
|
||||
err_exit_epc_mem:
|
||||
pci_epc_mem_exit(epc);
|
||||
|
||||
err_ep_deinit:
|
||||
if (ep->ops->deinit)
|
||||
ep->ops->deinit(ep);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dw_pcie_ep_init);
|
||||
|
||||
@@ -503,6 +503,9 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)
|
||||
if (ret)
|
||||
goto err_stop_link;
|
||||
|
||||
if (pp->ops->host_post_init)
|
||||
pp->ops->host_post_init(pp);
|
||||
|
||||
return 0;
|
||||
|
||||
err_stop_link:
|
||||
|
||||
@@ -365,6 +365,7 @@ void dw_pcie_write_dbi2(struct dw_pcie *pci, u32 reg, size_t size, u32 val)
|
||||
if (ret)
|
||||
dev_err(pci->dev, "write DBI address failed\n");
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dw_pcie_write_dbi2);
|
||||
|
||||
static inline void __iomem *dw_pcie_select_atu(struct dw_pcie *pci, u32 dir,
|
||||
u32 index)
|
||||
@@ -732,6 +733,53 @@ static void dw_pcie_link_set_max_speed(struct dw_pcie *pci, u32 link_gen)
|
||||
|
||||
}
|
||||
|
||||
static void dw_pcie_link_set_max_link_width(struct dw_pcie *pci, u32 num_lanes)
|
||||
{
|
||||
u32 lnkcap, lwsc, plc;
|
||||
u8 cap;
|
||||
|
||||
if (!num_lanes)
|
||||
return;
|
||||
|
||||
/* Set the number of lanes */
|
||||
plc = dw_pcie_readl_dbi(pci, PCIE_PORT_LINK_CONTROL);
|
||||
plc &= ~PORT_LINK_FAST_LINK_MODE;
|
||||
plc &= ~PORT_LINK_MODE_MASK;
|
||||
|
||||
/* Set link width speed control register */
|
||||
lwsc = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
|
||||
lwsc &= ~PORT_LOGIC_LINK_WIDTH_MASK;
|
||||
switch (num_lanes) {
|
||||
case 1:
|
||||
plc |= PORT_LINK_MODE_1_LANES;
|
||||
lwsc |= PORT_LOGIC_LINK_WIDTH_1_LANES;
|
||||
break;
|
||||
case 2:
|
||||
plc |= PORT_LINK_MODE_2_LANES;
|
||||
lwsc |= PORT_LOGIC_LINK_WIDTH_2_LANES;
|
||||
break;
|
||||
case 4:
|
||||
plc |= PORT_LINK_MODE_4_LANES;
|
||||
lwsc |= PORT_LOGIC_LINK_WIDTH_4_LANES;
|
||||
break;
|
||||
case 8:
|
||||
plc |= PORT_LINK_MODE_8_LANES;
|
||||
lwsc |= PORT_LOGIC_LINK_WIDTH_8_LANES;
|
||||
break;
|
||||
default:
|
||||
dev_err(pci->dev, "num-lanes %u: invalid value\n", num_lanes);
|
||||
return;
|
||||
}
|
||||
dw_pcie_writel_dbi(pci, PCIE_PORT_LINK_CONTROL, plc);
|
||||
dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, lwsc);
|
||||
|
||||
cap = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
|
||||
lnkcap = dw_pcie_readl_dbi(pci, cap + PCI_EXP_LNKCAP);
|
||||
lnkcap &= ~PCI_EXP_LNKCAP_MLW;
|
||||
lnkcap |= FIELD_PREP(PCI_EXP_LNKCAP_MLW, num_lanes);
|
||||
dw_pcie_writel_dbi(pci, cap + PCI_EXP_LNKCAP, lnkcap);
|
||||
}
|
||||
|
||||
void dw_pcie_iatu_detect(struct dw_pcie *pci)
|
||||
{
|
||||
int max_region, ob, ib;
|
||||
@@ -840,8 +888,14 @@ static int dw_pcie_edma_find_chip(struct dw_pcie *pci)
|
||||
* Indirect eDMA CSRs access has been completely removed since v5.40a
|
||||
* thus no space is now reserved for the eDMA channels viewport and
|
||||
* former DMA CTRL register is no longer fixed to FFs.
|
||||
*
|
||||
* Note that Renesas R-Car S4-8's PCIe controllers for unknown reason
|
||||
* have zeros in the eDMA CTRL register even though the HW-manual
|
||||
* explicitly states there must FFs if the unrolled mapping is enabled.
|
||||
* For such cases the low-level drivers are supposed to manually
|
||||
* activate the unrolled mapping to bypass the auto-detection procedure.
|
||||
*/
|
||||
if (dw_pcie_ver_is_ge(pci, 540A))
|
||||
if (dw_pcie_ver_is_ge(pci, 540A) || dw_pcie_cap_is(pci, EDMA_UNROLL))
|
||||
val = 0xFFFFFFFF;
|
||||
else
|
||||
val = dw_pcie_readl_dbi(pci, PCIE_DMA_VIEWPORT_BASE + PCIE_DMA_CTRL);
|
||||
@@ -1013,49 +1067,5 @@ void dw_pcie_setup(struct dw_pcie *pci)
|
||||
val |= PORT_LINK_DLL_LINK_EN;
|
||||
dw_pcie_writel_dbi(pci, PCIE_PORT_LINK_CONTROL, val);
|
||||
|
||||
if (!pci->num_lanes) {
|
||||
dev_dbg(pci->dev, "Using h/w default number of lanes\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set the number of lanes */
|
||||
val &= ~PORT_LINK_FAST_LINK_MODE;
|
||||
val &= ~PORT_LINK_MODE_MASK;
|
||||
switch (pci->num_lanes) {
|
||||
case 1:
|
||||
val |= PORT_LINK_MODE_1_LANES;
|
||||
break;
|
||||
case 2:
|
||||
val |= PORT_LINK_MODE_2_LANES;
|
||||
break;
|
||||
case 4:
|
||||
val |= PORT_LINK_MODE_4_LANES;
|
||||
break;
|
||||
case 8:
|
||||
val |= PORT_LINK_MODE_8_LANES;
|
||||
break;
|
||||
default:
|
||||
dev_err(pci->dev, "num-lanes %u: invalid value\n", pci->num_lanes);
|
||||
return;
|
||||
}
|
||||
dw_pcie_writel_dbi(pci, PCIE_PORT_LINK_CONTROL, val);
|
||||
|
||||
/* Set link width speed control register */
|
||||
val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
|
||||
val &= ~PORT_LOGIC_LINK_WIDTH_MASK;
|
||||
switch (pci->num_lanes) {
|
||||
case 1:
|
||||
val |= PORT_LOGIC_LINK_WIDTH_1_LANES;
|
||||
break;
|
||||
case 2:
|
||||
val |= PORT_LOGIC_LINK_WIDTH_2_LANES;
|
||||
break;
|
||||
case 4:
|
||||
val |= PORT_LOGIC_LINK_WIDTH_4_LANES;
|
||||
break;
|
||||
case 8:
|
||||
val |= PORT_LOGIC_LINK_WIDTH_8_LANES;
|
||||
break;
|
||||
}
|
||||
dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
|
||||
dw_pcie_link_set_max_link_width(pci, pci->num_lanes);
|
||||
}
|
||||
|
||||
@@ -51,8 +51,9 @@
|
||||
|
||||
/* DWC PCIe controller capabilities */
|
||||
#define DW_PCIE_CAP_REQ_RES 0
|
||||
#define DW_PCIE_CAP_IATU_UNROLL 1
|
||||
#define DW_PCIE_CAP_CDM_CHECK 2
|
||||
#define DW_PCIE_CAP_EDMA_UNROLL 1
|
||||
#define DW_PCIE_CAP_IATU_UNROLL 2
|
||||
#define DW_PCIE_CAP_CDM_CHECK 3
|
||||
|
||||
#define dw_pcie_cap_is(_pci, _cap) \
|
||||
test_bit(DW_PCIE_CAP_ ## _cap, &(_pci)->caps)
|
||||
@@ -301,6 +302,7 @@ enum dw_pcie_ltssm {
|
||||
struct dw_pcie_host_ops {
|
||||
int (*host_init)(struct dw_pcie_rp *pp);
|
||||
void (*host_deinit)(struct dw_pcie_rp *pp);
|
||||
void (*host_post_init)(struct dw_pcie_rp *pp);
|
||||
int (*msi_host_init)(struct dw_pcie_rp *pp);
|
||||
void (*pme_turn_off)(struct dw_pcie_rp *pp);
|
||||
};
|
||||
@@ -329,7 +331,9 @@ struct dw_pcie_rp {
|
||||
};
|
||||
|
||||
struct dw_pcie_ep_ops {
|
||||
void (*pre_init)(struct dw_pcie_ep *ep);
|
||||
void (*ep_init)(struct dw_pcie_ep *ep);
|
||||
void (*deinit)(struct dw_pcie_ep *ep);
|
||||
int (*raise_irq)(struct dw_pcie_ep *ep, u8 func_no,
|
||||
enum pci_epc_irq_type type, u16 interrupt_num);
|
||||
const struct pci_epc_features* (*get_features)(struct dw_pcie_ep *ep);
|
||||
@@ -341,6 +345,7 @@ struct dw_pcie_ep_ops {
|
||||
* driver.
|
||||
*/
|
||||
unsigned int (*func_conf_select)(struct dw_pcie_ep *ep, u8 func_no);
|
||||
unsigned int (*get_dbi2_offset)(struct dw_pcie_ep *ep, u8 func_no);
|
||||
};
|
||||
|
||||
struct dw_pcie_ep_func {
|
||||
|
||||
@@ -741,7 +741,7 @@ err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __exit kirin_pcie_remove(struct platform_device *pdev)
|
||||
static int kirin_pcie_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct kirin_pcie *kirin_pcie = platform_get_drvdata(pdev);
|
||||
|
||||
@@ -818,7 +818,7 @@ static int kirin_pcie_probe(struct platform_device *pdev)
|
||||
|
||||
static struct platform_driver kirin_pcie_driver = {
|
||||
.probe = kirin_pcie_probe,
|
||||
.remove = __exit_p(kirin_pcie_remove),
|
||||
.remove = kirin_pcie_remove,
|
||||
.driver = {
|
||||
.name = "kirin-pcie",
|
||||
.of_match_table = kirin_pcie_match,
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <linux/reset.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "../../pci.h"
|
||||
#include "pcie-designware.h"
|
||||
|
||||
/* PARF registers */
|
||||
@@ -123,6 +124,7 @@
|
||||
|
||||
/* ELBI registers */
|
||||
#define ELBI_SYS_STTS 0x08
|
||||
#define ELBI_CS2_ENABLE 0xa4
|
||||
|
||||
/* DBI registers */
|
||||
#define DBI_CON_STATUS 0x44
|
||||
@@ -135,10 +137,8 @@
|
||||
#define CORE_RESET_TIME_US_MAX 1005
|
||||
#define WAKE_DELAY_US 2000 /* 2 ms */
|
||||
|
||||
#define PCIE_GEN1_BW_MBPS 250
|
||||
#define PCIE_GEN2_BW_MBPS 500
|
||||
#define PCIE_GEN3_BW_MBPS 985
|
||||
#define PCIE_GEN4_BW_MBPS 1969
|
||||
#define QCOM_PCIE_LINK_SPEED_TO_BW(speed) \
|
||||
Mbps_to_icc(PCIE_SPEED2MBS_ENC(pcie_link_speed[speed]))
|
||||
|
||||
#define to_pcie_ep(x) dev_get_drvdata((x)->dev)
|
||||
|
||||
@@ -263,10 +263,25 @@ static void qcom_pcie_dw_stop_link(struct dw_pcie *pci)
|
||||
disable_irq(pcie_ep->perst_irq);
|
||||
}
|
||||
|
||||
static void qcom_pcie_dw_write_dbi2(struct dw_pcie *pci, void __iomem *base,
|
||||
u32 reg, size_t size, u32 val)
|
||||
{
|
||||
struct qcom_pcie_ep *pcie_ep = to_pcie_ep(pci);
|
||||
int ret;
|
||||
|
||||
writel(1, pcie_ep->elbi + ELBI_CS2_ENABLE);
|
||||
|
||||
ret = dw_pcie_write(pci->dbi_base2 + reg, size, val);
|
||||
if (ret)
|
||||
dev_err(pci->dev, "Failed to write DBI2 register (0x%x): %d\n", reg, ret);
|
||||
|
||||
writel(0, pcie_ep->elbi + ELBI_CS2_ENABLE);
|
||||
}
|
||||
|
||||
static void qcom_pcie_ep_icc_update(struct qcom_pcie_ep *pcie_ep)
|
||||
{
|
||||
struct dw_pcie *pci = &pcie_ep->pci;
|
||||
u32 offset, status, bw;
|
||||
u32 offset, status;
|
||||
int speed, width;
|
||||
int ret;
|
||||
|
||||
@@ -279,25 +294,7 @@ static void qcom_pcie_ep_icc_update(struct qcom_pcie_ep *pcie_ep)
|
||||
speed = FIELD_GET(PCI_EXP_LNKSTA_CLS, status);
|
||||
width = FIELD_GET(PCI_EXP_LNKSTA_NLW, status);
|
||||
|
||||
switch (speed) {
|
||||
case 1:
|
||||
bw = MBps_to_icc(PCIE_GEN1_BW_MBPS);
|
||||
break;
|
||||
case 2:
|
||||
bw = MBps_to_icc(PCIE_GEN2_BW_MBPS);
|
||||
break;
|
||||
case 3:
|
||||
bw = MBps_to_icc(PCIE_GEN3_BW_MBPS);
|
||||
break;
|
||||
default:
|
||||
dev_warn(pci->dev, "using default GEN4 bandwidth\n");
|
||||
fallthrough;
|
||||
case 4:
|
||||
bw = MBps_to_icc(PCIE_GEN4_BW_MBPS);
|
||||
break;
|
||||
}
|
||||
|
||||
ret = icc_set_bw(pcie_ep->icc_mem, 0, width * bw);
|
||||
ret = icc_set_bw(pcie_ep->icc_mem, 0, width * QCOM_PCIE_LINK_SPEED_TO_BW(speed));
|
||||
if (ret)
|
||||
dev_err(pci->dev, "failed to set interconnect bandwidth: %d\n",
|
||||
ret);
|
||||
@@ -335,7 +332,7 @@ static int qcom_pcie_enable_resources(struct qcom_pcie_ep *pcie_ep)
|
||||
* Set an initial peak bandwidth corresponding to single-lane Gen 1
|
||||
* for the pcie-mem path.
|
||||
*/
|
||||
ret = icc_set_bw(pcie_ep->icc_mem, 0, MBps_to_icc(PCIE_GEN1_BW_MBPS));
|
||||
ret = icc_set_bw(pcie_ep->icc_mem, 0, QCOM_PCIE_LINK_SPEED_TO_BW(1));
|
||||
if (ret) {
|
||||
dev_err(pci->dev, "failed to set interconnect bandwidth: %d\n",
|
||||
ret);
|
||||
@@ -519,6 +516,7 @@ static const struct dw_pcie_ops pci_ops = {
|
||||
.link_up = qcom_pcie_dw_link_up,
|
||||
.start_link = qcom_pcie_dw_start_link,
|
||||
.stop_link = qcom_pcie_dw_stop_link,
|
||||
.write_dbi2 = qcom_pcie_dw_write_dbi2,
|
||||
};
|
||||
|
||||
static int qcom_pcie_ep_get_io_resources(struct platform_device *pdev,
|
||||
|
||||
@@ -147,6 +147,9 @@
|
||||
|
||||
#define QCOM_PCIE_CRC8_POLYNOMIAL (BIT(2) | BIT(1) | BIT(0))
|
||||
|
||||
#define QCOM_PCIE_LINK_SPEED_TO_BW(speed) \
|
||||
Mbps_to_icc(PCIE_SPEED2MBS_ENC(pcie_link_speed[speed]))
|
||||
|
||||
#define QCOM_PCIE_1_0_0_MAX_CLOCKS 4
|
||||
struct qcom_pcie_resources_1_0_0 {
|
||||
struct clk_bulk_data clks[QCOM_PCIE_1_0_0_MAX_CLOCKS];
|
||||
@@ -218,6 +221,7 @@ struct qcom_pcie_ops {
|
||||
int (*get_resources)(struct qcom_pcie *pcie);
|
||||
int (*init)(struct qcom_pcie *pcie);
|
||||
int (*post_init)(struct qcom_pcie *pcie);
|
||||
void (*host_post_init)(struct qcom_pcie *pcie);
|
||||
void (*deinit)(struct qcom_pcie *pcie);
|
||||
void (*ltssm_enable)(struct qcom_pcie *pcie);
|
||||
int (*config_sid)(struct qcom_pcie *pcie);
|
||||
@@ -962,6 +966,22 @@ static int qcom_pcie_post_init_2_7_0(struct qcom_pcie *pcie)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_pcie_enable_aspm(struct pci_dev *pdev, void *userdata)
|
||||
{
|
||||
/* Downstream devices need to be in D0 state before enabling PCI PM substates */
|
||||
pci_set_power_state(pdev, PCI_D0);
|
||||
pci_enable_link_state(pdev, PCIE_LINK_STATE_ALL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qcom_pcie_host_post_init_2_7_0(struct qcom_pcie *pcie)
|
||||
{
|
||||
struct dw_pcie_rp *pp = &pcie->pci->pp;
|
||||
|
||||
pci_walk_bus(pp->bridge->bus, qcom_pcie_enable_aspm, NULL);
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -1214,9 +1234,19 @@ static void qcom_pcie_host_deinit(struct dw_pcie_rp *pp)
|
||||
pcie->cfg->ops->deinit(pcie);
|
||||
}
|
||||
|
||||
static void qcom_pcie_host_post_init(struct dw_pcie_rp *pp)
|
||||
{
|
||||
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
|
||||
struct qcom_pcie *pcie = to_qcom_pcie(pci);
|
||||
|
||||
if (pcie->cfg->ops->host_post_init)
|
||||
pcie->cfg->ops->host_post_init(pcie);
|
||||
}
|
||||
|
||||
static const struct dw_pcie_host_ops qcom_pcie_dw_ops = {
|
||||
.host_init = qcom_pcie_host_init,
|
||||
.host_deinit = qcom_pcie_host_deinit,
|
||||
.host_post_init = qcom_pcie_host_post_init,
|
||||
};
|
||||
|
||||
/* Qcom IP rev.: 2.1.0 Synopsys IP rev.: 4.01a */
|
||||
@@ -1278,6 +1308,7 @@ static const struct qcom_pcie_ops ops_1_9_0 = {
|
||||
.get_resources = qcom_pcie_get_resources_2_7_0,
|
||||
.init = qcom_pcie_init_2_7_0,
|
||||
.post_init = qcom_pcie_post_init_2_7_0,
|
||||
.host_post_init = qcom_pcie_host_post_init_2_7_0,
|
||||
.deinit = qcom_pcie_deinit_2_7_0,
|
||||
.ltssm_enable = qcom_pcie_2_3_2_ltssm_enable,
|
||||
.config_sid = qcom_pcie_config_sid_1_9_0,
|
||||
@@ -1345,7 +1376,7 @@ static int qcom_pcie_icc_init(struct qcom_pcie *pcie)
|
||||
* Set an initial peak bandwidth corresponding to single-lane Gen 1
|
||||
* for the pcie-mem path.
|
||||
*/
|
||||
ret = icc_set_bw(pcie->icc_mem, 0, MBps_to_icc(250));
|
||||
ret = icc_set_bw(pcie->icc_mem, 0, QCOM_PCIE_LINK_SPEED_TO_BW(1));
|
||||
if (ret) {
|
||||
dev_err(pci->dev, "failed to set interconnect bandwidth: %d\n",
|
||||
ret);
|
||||
@@ -1358,7 +1389,7 @@ static int qcom_pcie_icc_init(struct qcom_pcie *pcie)
|
||||
static void qcom_pcie_icc_update(struct qcom_pcie *pcie)
|
||||
{
|
||||
struct dw_pcie *pci = pcie->pci;
|
||||
u32 offset, status, bw;
|
||||
u32 offset, status;
|
||||
int speed, width;
|
||||
int ret;
|
||||
|
||||
@@ -1375,22 +1406,7 @@ static void qcom_pcie_icc_update(struct qcom_pcie *pcie)
|
||||
speed = FIELD_GET(PCI_EXP_LNKSTA_CLS, status);
|
||||
width = FIELD_GET(PCI_EXP_LNKSTA_NLW, status);
|
||||
|
||||
switch (speed) {
|
||||
case 1:
|
||||
bw = MBps_to_icc(250);
|
||||
break;
|
||||
case 2:
|
||||
bw = MBps_to_icc(500);
|
||||
break;
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
fallthrough;
|
||||
case 3:
|
||||
bw = MBps_to_icc(985);
|
||||
break;
|
||||
}
|
||||
|
||||
ret = icc_set_bw(pcie->icc_mem, 0, width * bw);
|
||||
ret = icc_set_bw(pcie->icc_mem, 0, width * QCOM_PCIE_LINK_SPEED_TO_BW(speed));
|
||||
if (ret) {
|
||||
dev_err(pci->dev, "failed to set interconnect bandwidth: %d\n",
|
||||
ret);
|
||||
|
||||
@@ -0,0 +1,527 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* PCIe controller driver for Renesas R-Car Gen4 Series SoCs
|
||||
* Copyright (C) 2022-2023 Renesas Electronics Corporation
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include "../../pci.h"
|
||||
#include "pcie-designware.h"
|
||||
|
||||
/* Renesas-specific */
|
||||
/* PCIe Mode Setting Register 0 */
|
||||
#define PCIEMSR0 0x0000
|
||||
#define BIFUR_MOD_SET_ON BIT(0)
|
||||
#define DEVICE_TYPE_EP 0
|
||||
#define DEVICE_TYPE_RC BIT(4)
|
||||
|
||||
/* PCIe Interrupt Status 0 */
|
||||
#define PCIEINTSTS0 0x0084
|
||||
|
||||
/* PCIe Interrupt Status 0 Enable */
|
||||
#define PCIEINTSTS0EN 0x0310
|
||||
#define MSI_CTRL_INT BIT(26)
|
||||
#define SMLH_LINK_UP BIT(7)
|
||||
#define RDLH_LINK_UP BIT(6)
|
||||
|
||||
/* PCIe DMA Interrupt Status Enable */
|
||||
#define PCIEDMAINTSTSEN 0x0314
|
||||
#define PCIEDMAINTSTSEN_INIT GENMASK(15, 0)
|
||||
|
||||
/* PCIe Reset Control Register 1 */
|
||||
#define PCIERSTCTRL1 0x0014
|
||||
#define APP_HOLD_PHY_RST BIT(16)
|
||||
#define APP_LTSSM_ENABLE BIT(0)
|
||||
|
||||
#define RCAR_NUM_SPEED_CHANGE_RETRIES 10
|
||||
#define RCAR_MAX_LINK_SPEED 4
|
||||
|
||||
#define RCAR_GEN4_PCIE_EP_FUNC_DBI_OFFSET 0x1000
|
||||
#define RCAR_GEN4_PCIE_EP_FUNC_DBI2_OFFSET 0x800
|
||||
|
||||
struct rcar_gen4_pcie {
|
||||
struct dw_pcie dw;
|
||||
void __iomem *base;
|
||||
struct platform_device *pdev;
|
||||
enum dw_pcie_device_mode mode;
|
||||
};
|
||||
#define to_rcar_gen4_pcie(_dw) container_of(_dw, struct rcar_gen4_pcie, dw)
|
||||
|
||||
/* Common */
|
||||
static void rcar_gen4_pcie_ltssm_enable(struct rcar_gen4_pcie *rcar,
|
||||
bool enable)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl(rcar->base + PCIERSTCTRL1);
|
||||
if (enable) {
|
||||
val |= APP_LTSSM_ENABLE;
|
||||
val &= ~APP_HOLD_PHY_RST;
|
||||
} else {
|
||||
/*
|
||||
* Since the datasheet of R-Car doesn't mention how to assert
|
||||
* the APP_HOLD_PHY_RST, don't assert it again. Otherwise,
|
||||
* hang-up issue happened in the dw_edma_core_off() when
|
||||
* the controller didn't detect a PCI device.
|
||||
*/
|
||||
val &= ~APP_LTSSM_ENABLE;
|
||||
}
|
||||
writel(val, rcar->base + PCIERSTCTRL1);
|
||||
}
|
||||
|
||||
static int rcar_gen4_pcie_link_up(struct dw_pcie *dw)
|
||||
{
|
||||
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
|
||||
u32 val, mask;
|
||||
|
||||
val = readl(rcar->base + PCIEINTSTS0);
|
||||
mask = RDLH_LINK_UP | SMLH_LINK_UP;
|
||||
|
||||
return (val & mask) == mask;
|
||||
}
|
||||
|
||||
/*
|
||||
* Manually initiate the speed change. Return 0 if change succeeded; otherwise
|
||||
* -ETIMEDOUT.
|
||||
*/
|
||||
static int rcar_gen4_pcie_speed_change(struct dw_pcie *dw)
|
||||
{
|
||||
u32 val;
|
||||
int i;
|
||||
|
||||
val = dw_pcie_readl_dbi(dw, PCIE_LINK_WIDTH_SPEED_CONTROL);
|
||||
val &= ~PORT_LOGIC_SPEED_CHANGE;
|
||||
dw_pcie_writel_dbi(dw, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
|
||||
|
||||
val = dw_pcie_readl_dbi(dw, PCIE_LINK_WIDTH_SPEED_CONTROL);
|
||||
val |= PORT_LOGIC_SPEED_CHANGE;
|
||||
dw_pcie_writel_dbi(dw, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
|
||||
|
||||
for (i = 0; i < RCAR_NUM_SPEED_CHANGE_RETRIES; i++) {
|
||||
val = dw_pcie_readl_dbi(dw, PCIE_LINK_WIDTH_SPEED_CONTROL);
|
||||
if (!(val & PORT_LOGIC_SPEED_CHANGE))
|
||||
return 0;
|
||||
usleep_range(10000, 11000);
|
||||
}
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable LTSSM of this controller and manually initiate the speed change.
|
||||
* Always return 0.
|
||||
*/
|
||||
static int rcar_gen4_pcie_start_link(struct dw_pcie *dw)
|
||||
{
|
||||
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
|
||||
int i, changes;
|
||||
|
||||
rcar_gen4_pcie_ltssm_enable(rcar, true);
|
||||
|
||||
/*
|
||||
* Require direct speed change with retrying here if the link_gen is
|
||||
* PCIe Gen2 or higher.
|
||||
*/
|
||||
changes = min_not_zero(dw->link_gen, RCAR_MAX_LINK_SPEED) - 1;
|
||||
|
||||
/*
|
||||
* Since dw_pcie_setup_rc() sets it once, PCIe Gen2 will be trained.
|
||||
* So, this needs remaining times for up to PCIe Gen4 if RC mode.
|
||||
*/
|
||||
if (changes && rcar->mode == DW_PCIE_RC_TYPE)
|
||||
changes--;
|
||||
|
||||
for (i = 0; i < changes; i++) {
|
||||
/* It may not be connected in EP mode yet. So, break the loop */
|
||||
if (rcar_gen4_pcie_speed_change(dw))
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rcar_gen4_pcie_stop_link(struct dw_pcie *dw)
|
||||
{
|
||||
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
|
||||
|
||||
rcar_gen4_pcie_ltssm_enable(rcar, false);
|
||||
}
|
||||
|
||||
static int rcar_gen4_pcie_common_init(struct rcar_gen4_pcie *rcar)
|
||||
{
|
||||
struct dw_pcie *dw = &rcar->dw;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
ret = clk_bulk_prepare_enable(DW_PCIE_NUM_CORE_CLKS, dw->core_clks);
|
||||
if (ret) {
|
||||
dev_err(dw->dev, "Enabling core clocks failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!reset_control_status(dw->core_rsts[DW_PCIE_PWR_RST].rstc))
|
||||
reset_control_assert(dw->core_rsts[DW_PCIE_PWR_RST].rstc);
|
||||
|
||||
val = readl(rcar->base + PCIEMSR0);
|
||||
if (rcar->mode == DW_PCIE_RC_TYPE) {
|
||||
val |= DEVICE_TYPE_RC;
|
||||
} else if (rcar->mode == DW_PCIE_EP_TYPE) {
|
||||
val |= DEVICE_TYPE_EP;
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
goto err_unprepare;
|
||||
}
|
||||
|
||||
if (dw->num_lanes < 4)
|
||||
val |= BIFUR_MOD_SET_ON;
|
||||
|
||||
writel(val, rcar->base + PCIEMSR0);
|
||||
|
||||
ret = reset_control_deassert(dw->core_rsts[DW_PCIE_PWR_RST].rstc);
|
||||
if (ret)
|
||||
goto err_unprepare;
|
||||
|
||||
return 0;
|
||||
|
||||
err_unprepare:
|
||||
clk_bulk_disable_unprepare(DW_PCIE_NUM_CORE_CLKS, dw->core_clks);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void rcar_gen4_pcie_common_deinit(struct rcar_gen4_pcie *rcar)
|
||||
{
|
||||
struct dw_pcie *dw = &rcar->dw;
|
||||
|
||||
reset_control_assert(dw->core_rsts[DW_PCIE_PWR_RST].rstc);
|
||||
clk_bulk_disable_unprepare(DW_PCIE_NUM_CORE_CLKS, dw->core_clks);
|
||||
}
|
||||
|
||||
static int rcar_gen4_pcie_prepare(struct rcar_gen4_pcie *rcar)
|
||||
{
|
||||
struct device *dev = rcar->dw.dev;
|
||||
int err;
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
err = pm_runtime_resume_and_get(dev);
|
||||
if (err < 0) {
|
||||
dev_err(dev, "Runtime resume failed\n");
|
||||
pm_runtime_disable(dev);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void rcar_gen4_pcie_unprepare(struct rcar_gen4_pcie *rcar)
|
||||
{
|
||||
struct device *dev = rcar->dw.dev;
|
||||
|
||||
pm_runtime_put(dev);
|
||||
pm_runtime_disable(dev);
|
||||
}
|
||||
|
||||
static int rcar_gen4_pcie_get_resources(struct rcar_gen4_pcie *rcar)
|
||||
{
|
||||
/* Renesas-specific registers */
|
||||
rcar->base = devm_platform_ioremap_resource_byname(rcar->pdev, "app");
|
||||
|
||||
return PTR_ERR_OR_ZERO(rcar->base);
|
||||
}
|
||||
|
||||
static const struct dw_pcie_ops dw_pcie_ops = {
|
||||
.start_link = rcar_gen4_pcie_start_link,
|
||||
.stop_link = rcar_gen4_pcie_stop_link,
|
||||
.link_up = rcar_gen4_pcie_link_up,
|
||||
};
|
||||
|
||||
static struct rcar_gen4_pcie *rcar_gen4_pcie_alloc(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct rcar_gen4_pcie *rcar;
|
||||
|
||||
rcar = devm_kzalloc(dev, sizeof(*rcar), GFP_KERNEL);
|
||||
if (!rcar)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
rcar->dw.ops = &dw_pcie_ops;
|
||||
rcar->dw.dev = dev;
|
||||
rcar->pdev = pdev;
|
||||
dw_pcie_cap_set(&rcar->dw, EDMA_UNROLL);
|
||||
dw_pcie_cap_set(&rcar->dw, REQ_RES);
|
||||
platform_set_drvdata(pdev, rcar);
|
||||
|
||||
return rcar;
|
||||
}
|
||||
|
||||
/* Host mode */
|
||||
static int rcar_gen4_pcie_host_init(struct dw_pcie_rp *pp)
|
||||
{
|
||||
struct dw_pcie *dw = to_dw_pcie_from_pp(pp);
|
||||
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
gpiod_set_value_cansleep(dw->pe_rst, 1);
|
||||
|
||||
ret = rcar_gen4_pcie_common_init(rcar);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* According to the section 3.5.7.2 "RC Mode" in DWC PCIe Dual Mode
|
||||
* Rev.5.20a and 3.5.6.1 "RC mode" in DWC PCIe RC databook v5.20a, we
|
||||
* should disable two BARs to avoid unnecessary memory assignment
|
||||
* during device enumeration.
|
||||
*/
|
||||
dw_pcie_writel_dbi2(dw, PCI_BASE_ADDRESS_0, 0x0);
|
||||
dw_pcie_writel_dbi2(dw, PCI_BASE_ADDRESS_1, 0x0);
|
||||
|
||||
/* Enable MSI interrupt signal */
|
||||
val = readl(rcar->base + PCIEINTSTS0EN);
|
||||
val |= MSI_CTRL_INT;
|
||||
writel(val, rcar->base + PCIEINTSTS0EN);
|
||||
|
||||
msleep(PCIE_T_PVPERL_MS); /* pe_rst requires 100msec delay */
|
||||
|
||||
gpiod_set_value_cansleep(dw->pe_rst, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rcar_gen4_pcie_host_deinit(struct dw_pcie_rp *pp)
|
||||
{
|
||||
struct dw_pcie *dw = to_dw_pcie_from_pp(pp);
|
||||
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
|
||||
|
||||
gpiod_set_value_cansleep(dw->pe_rst, 1);
|
||||
rcar_gen4_pcie_common_deinit(rcar);
|
||||
}
|
||||
|
||||
static const struct dw_pcie_host_ops rcar_gen4_pcie_host_ops = {
|
||||
.host_init = rcar_gen4_pcie_host_init,
|
||||
.host_deinit = rcar_gen4_pcie_host_deinit,
|
||||
};
|
||||
|
||||
static int rcar_gen4_add_dw_pcie_rp(struct rcar_gen4_pcie *rcar)
|
||||
{
|
||||
struct dw_pcie_rp *pp = &rcar->dw.pp;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_PCIE_RCAR_GEN4_HOST))
|
||||
return -ENODEV;
|
||||
|
||||
pp->num_vectors = MAX_MSI_IRQS;
|
||||
pp->ops = &rcar_gen4_pcie_host_ops;
|
||||
|
||||
return dw_pcie_host_init(pp);
|
||||
}
|
||||
|
||||
static void rcar_gen4_remove_dw_pcie_rp(struct rcar_gen4_pcie *rcar)
|
||||
{
|
||||
dw_pcie_host_deinit(&rcar->dw.pp);
|
||||
}
|
||||
|
||||
/* Endpoint mode */
|
||||
static void rcar_gen4_pcie_ep_pre_init(struct dw_pcie_ep *ep)
|
||||
{
|
||||
struct dw_pcie *dw = to_dw_pcie_from_ep(ep);
|
||||
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
|
||||
int ret;
|
||||
|
||||
ret = rcar_gen4_pcie_common_init(rcar);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
writel(PCIEDMAINTSTSEN_INIT, rcar->base + PCIEDMAINTSTSEN);
|
||||
}
|
||||
|
||||
static void rcar_gen4_pcie_ep_init(struct dw_pcie_ep *ep)
|
||||
{
|
||||
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
|
||||
enum pci_barno bar;
|
||||
|
||||
for (bar = 0; bar < PCI_STD_NUM_BARS; bar++)
|
||||
dw_pcie_ep_reset_bar(pci, bar);
|
||||
}
|
||||
|
||||
static void rcar_gen4_pcie_ep_deinit(struct dw_pcie_ep *ep)
|
||||
{
|
||||
struct dw_pcie *dw = to_dw_pcie_from_ep(ep);
|
||||
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
|
||||
|
||||
writel(0, rcar->base + PCIEDMAINTSTSEN);
|
||||
rcar_gen4_pcie_common_deinit(rcar);
|
||||
}
|
||||
|
||||
static int rcar_gen4_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
|
||||
enum pci_epc_irq_type type,
|
||||
u16 interrupt_num)
|
||||
{
|
||||
struct dw_pcie *dw = to_dw_pcie_from_ep(ep);
|
||||
|
||||
switch (type) {
|
||||
case PCI_EPC_IRQ_LEGACY:
|
||||
return dw_pcie_ep_raise_legacy_irq(ep, func_no);
|
||||
case PCI_EPC_IRQ_MSI:
|
||||
return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num);
|
||||
default:
|
||||
dev_err(dw->dev, "Unknown IRQ type\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct pci_epc_features rcar_gen4_pcie_epc_features = {
|
||||
.linkup_notifier = false,
|
||||
.msi_capable = true,
|
||||
.msix_capable = false,
|
||||
.reserved_bar = 1 << BAR_1 | 1 << BAR_3 | 1 << BAR_5,
|
||||
.align = SZ_1M,
|
||||
};
|
||||
|
||||
static const struct pci_epc_features*
|
||||
rcar_gen4_pcie_ep_get_features(struct dw_pcie_ep *ep)
|
||||
{
|
||||
return &rcar_gen4_pcie_epc_features;
|
||||
}
|
||||
|
||||
static unsigned int rcar_gen4_pcie_ep_func_conf_select(struct dw_pcie_ep *ep,
|
||||
u8 func_no)
|
||||
{
|
||||
return func_no * RCAR_GEN4_PCIE_EP_FUNC_DBI_OFFSET;
|
||||
}
|
||||
|
||||
static unsigned int rcar_gen4_pcie_ep_get_dbi2_offset(struct dw_pcie_ep *ep,
|
||||
u8 func_no)
|
||||
{
|
||||
return func_no * RCAR_GEN4_PCIE_EP_FUNC_DBI2_OFFSET;
|
||||
}
|
||||
|
||||
static const struct dw_pcie_ep_ops pcie_ep_ops = {
|
||||
.pre_init = rcar_gen4_pcie_ep_pre_init,
|
||||
.ep_init = rcar_gen4_pcie_ep_init,
|
||||
.deinit = rcar_gen4_pcie_ep_deinit,
|
||||
.raise_irq = rcar_gen4_pcie_ep_raise_irq,
|
||||
.get_features = rcar_gen4_pcie_ep_get_features,
|
||||
.func_conf_select = rcar_gen4_pcie_ep_func_conf_select,
|
||||
.get_dbi2_offset = rcar_gen4_pcie_ep_get_dbi2_offset,
|
||||
};
|
||||
|
||||
static int rcar_gen4_add_dw_pcie_ep(struct rcar_gen4_pcie *rcar)
|
||||
{
|
||||
struct dw_pcie_ep *ep = &rcar->dw.ep;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_PCIE_RCAR_GEN4_EP))
|
||||
return -ENODEV;
|
||||
|
||||
ep->ops = &pcie_ep_ops;
|
||||
|
||||
return dw_pcie_ep_init(ep);
|
||||
}
|
||||
|
||||
static void rcar_gen4_remove_dw_pcie_ep(struct rcar_gen4_pcie *rcar)
|
||||
{
|
||||
dw_pcie_ep_exit(&rcar->dw.ep);
|
||||
}
|
||||
|
||||
/* Common */
|
||||
static int rcar_gen4_add_dw_pcie(struct rcar_gen4_pcie *rcar)
|
||||
{
|
||||
rcar->mode = (enum dw_pcie_device_mode)of_device_get_match_data(&rcar->pdev->dev);
|
||||
|
||||
switch (rcar->mode) {
|
||||
case DW_PCIE_RC_TYPE:
|
||||
return rcar_gen4_add_dw_pcie_rp(rcar);
|
||||
case DW_PCIE_EP_TYPE:
|
||||
return rcar_gen4_add_dw_pcie_ep(rcar);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int rcar_gen4_pcie_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct rcar_gen4_pcie *rcar;
|
||||
int err;
|
||||
|
||||
rcar = rcar_gen4_pcie_alloc(pdev);
|
||||
if (IS_ERR(rcar))
|
||||
return PTR_ERR(rcar);
|
||||
|
||||
err = rcar_gen4_pcie_get_resources(rcar);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = rcar_gen4_pcie_prepare(rcar);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = rcar_gen4_add_dw_pcie(rcar);
|
||||
if (err)
|
||||
goto err_unprepare;
|
||||
|
||||
return 0;
|
||||
|
||||
err_unprepare:
|
||||
rcar_gen4_pcie_unprepare(rcar);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void rcar_gen4_remove_dw_pcie(struct rcar_gen4_pcie *rcar)
|
||||
{
|
||||
switch (rcar->mode) {
|
||||
case DW_PCIE_RC_TYPE:
|
||||
rcar_gen4_remove_dw_pcie_rp(rcar);
|
||||
break;
|
||||
case DW_PCIE_EP_TYPE:
|
||||
rcar_gen4_remove_dw_pcie_ep(rcar);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void rcar_gen4_pcie_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rcar_gen4_pcie *rcar = platform_get_drvdata(pdev);
|
||||
|
||||
rcar_gen4_remove_dw_pcie(rcar);
|
||||
rcar_gen4_pcie_unprepare(rcar);
|
||||
}
|
||||
|
||||
static const struct of_device_id rcar_gen4_pcie_of_match[] = {
|
||||
{
|
||||
.compatible = "renesas,rcar-gen4-pcie",
|
||||
.data = (void *)DW_PCIE_RC_TYPE,
|
||||
},
|
||||
{
|
||||
.compatible = "renesas,rcar-gen4-pcie-ep",
|
||||
.data = (void *)DW_PCIE_EP_TYPE,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rcar_gen4_pcie_of_match);
|
||||
|
||||
static struct platform_driver rcar_gen4_pcie_driver = {
|
||||
.driver = {
|
||||
.name = "pcie-rcar-gen4",
|
||||
.of_match_table = rcar_gen4_pcie_of_match,
|
||||
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||
},
|
||||
.probe = rcar_gen4_pcie_probe,
|
||||
.remove_new = rcar_gen4_pcie_remove,
|
||||
};
|
||||
module_platform_driver(rcar_gen4_pcie_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Renesas R-Car Gen4 PCIe controller driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -9,6 +9,7 @@
|
||||
* Author: Vidya Sagar <vidyas@nvidia.com>
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/delay.h>
|
||||
@@ -125,7 +126,7 @@
|
||||
|
||||
#define APPL_LTR_MSG_1 0xC4
|
||||
#define LTR_MSG_REQ BIT(15)
|
||||
#define LTR_MST_NO_SNOOP_SHIFT 16
|
||||
#define LTR_NOSNOOP_MSG_REQ BIT(31)
|
||||
|
||||
#define APPL_LTR_MSG_2 0xC8
|
||||
#define APPL_LTR_MSG_2_LTR_MSG_REQ_STATE BIT(3)
|
||||
@@ -321,9 +322,9 @@ static void tegra_pcie_icc_set(struct tegra_pcie_dw *pcie)
|
||||
speed = FIELD_GET(PCI_EXP_LNKSTA_CLS, val);
|
||||
width = FIELD_GET(PCI_EXP_LNKSTA_NLW, val);
|
||||
|
||||
val = width * (PCIE_SPEED2MBS_ENC(pcie_link_speed[speed]) / BITS_PER_BYTE);
|
||||
val = width * PCIE_SPEED2MBS_ENC(pcie_link_speed[speed]);
|
||||
|
||||
if (icc_set_bw(pcie->icc_path, MBps_to_icc(val), 0))
|
||||
if (icc_set_bw(pcie->icc_path, Mbps_to_icc(val), 0))
|
||||
dev_err(pcie->dev, "can't set bw[%u]\n", val);
|
||||
|
||||
if (speed >= ARRAY_SIZE(pcie_gen_freq))
|
||||
@@ -346,8 +347,7 @@ static void apply_bad_link_workaround(struct dw_pcie_rp *pp)
|
||||
*/
|
||||
val = dw_pcie_readw_dbi(pci, pcie->pcie_cap_base + PCI_EXP_LNKSTA);
|
||||
if (val & PCI_EXP_LNKSTA_LBMS) {
|
||||
current_link_width = (val & PCI_EXP_LNKSTA_NLW) >>
|
||||
PCI_EXP_LNKSTA_NLW_SHIFT;
|
||||
current_link_width = FIELD_GET(PCI_EXP_LNKSTA_NLW, val);
|
||||
if (pcie->init_link_width > current_link_width) {
|
||||
dev_warn(pci->dev, "PCIe link is bad, width reduced\n");
|
||||
val = dw_pcie_readw_dbi(pci, pcie->pcie_cap_base +
|
||||
@@ -496,8 +496,12 @@ static irqreturn_t tegra_pcie_ep_irq_thread(int irq, void *arg)
|
||||
ktime_t timeout;
|
||||
|
||||
/* 110us for both snoop and no-snoop */
|
||||
val = 110 | (2 << PCI_LTR_SCALE_SHIFT) | LTR_MSG_REQ;
|
||||
val |= (val << LTR_MST_NO_SNOOP_SHIFT);
|
||||
val = FIELD_PREP(PCI_LTR_VALUE_MASK, 110) |
|
||||
FIELD_PREP(PCI_LTR_SCALE_MASK, 2) |
|
||||
LTR_MSG_REQ |
|
||||
FIELD_PREP(PCI_LTR_NOSNOOP_VALUE, 110) |
|
||||
FIELD_PREP(PCI_LTR_NOSNOOP_SCALE, 2) |
|
||||
LTR_NOSNOOP_MSG_REQ;
|
||||
appl_writel(pcie, val, APPL_LTR_MSG_1);
|
||||
|
||||
/* Send LTR upstream */
|
||||
@@ -760,8 +764,7 @@ static void tegra_pcie_enable_system_interrupts(struct dw_pcie_rp *pp)
|
||||
|
||||
val_w = dw_pcie_readw_dbi(&pcie->pci, pcie->pcie_cap_base +
|
||||
PCI_EXP_LNKSTA);
|
||||
pcie->init_link_width = (val_w & PCI_EXP_LNKSTA_NLW) >>
|
||||
PCI_EXP_LNKSTA_NLW_SHIFT;
|
||||
pcie->init_link_width = FIELD_GET(PCI_EXP_LNKSTA_NLW, val_w);
|
||||
|
||||
val_w = dw_pcie_readw_dbi(&pcie->pci, pcie->pcie_cap_base +
|
||||
PCI_EXP_LNKCTL);
|
||||
@@ -917,12 +920,6 @@ static int tegra_pcie_dw_host_init(struct dw_pcie_rp *pp)
|
||||
AMBA_ERROR_RESPONSE_CRS_SHIFT);
|
||||
dw_pcie_writel_dbi(pci, PORT_LOGIC_AMBA_ERROR_RESPONSE_DEFAULT, val);
|
||||
|
||||
/* Configure Max lane width from DT */
|
||||
val = dw_pcie_readl_dbi(pci, pcie->pcie_cap_base + PCI_EXP_LNKCAP);
|
||||
val &= ~PCI_EXP_LNKCAP_MLW;
|
||||
val |= (pcie->num_lanes << PCI_EXP_LNKSTA_NLW_SHIFT);
|
||||
dw_pcie_writel_dbi(pci, pcie->pcie_cap_base + PCI_EXP_LNKCAP, val);
|
||||
|
||||
/* Clear Slot Clock Configuration bit if SRNS configuration */
|
||||
if (pcie->enable_srns) {
|
||||
val_16 = dw_pcie_readw_dbi(pci, pcie->pcie_cap_base +
|
||||
|
||||
@@ -539,7 +539,7 @@ static bool mobiveil_pcie_is_bridge(struct mobiveil_pcie *pcie)
|
||||
u32 header_type;
|
||||
|
||||
header_type = mobiveil_csr_readb(pcie, PCI_HEADER_TYPE);
|
||||
header_type &= 0x7f;
|
||||
header_type &= PCI_HEADER_TYPE_MASK;
|
||||
|
||||
return header_type == PCI_HEADER_TYPE_BRIDGE;
|
||||
}
|
||||
|
||||
@@ -545,7 +545,7 @@ struct hv_pcidev_description {
|
||||
struct hv_dr_state {
|
||||
struct list_head list_entry;
|
||||
u32 device_count;
|
||||
struct hv_pcidev_description func[];
|
||||
struct hv_pcidev_description func[] __counted_by(device_count);
|
||||
};
|
||||
|
||||
struct hv_pci_dev {
|
||||
|
||||
@@ -264,7 +264,7 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)
|
||||
*/
|
||||
lnkcap = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP);
|
||||
lnkcap &= ~PCI_EXP_LNKCAP_MLW;
|
||||
lnkcap |= (port->is_x4 ? 4 : 1) << 4;
|
||||
lnkcap |= FIELD_PREP(PCI_EXP_LNKCAP_MLW, port->is_x4 ? 4 : 1);
|
||||
mvebu_writel(port, lnkcap, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP);
|
||||
|
||||
/* Disable Root Bridge I/O space, memory space and bus mastering. */
|
||||
|
||||
@@ -163,10 +163,11 @@ static int xgene_pcie_config_read32(struct pci_bus *bus, unsigned int devfn,
|
||||
int where, int size, u32 *val)
|
||||
{
|
||||
struct xgene_pcie *port = pcie_bus_to_port(bus);
|
||||
int ret;
|
||||
|
||||
if (pci_generic_config_read32(bus, devfn, where & ~0x3, 4, val) !=
|
||||
PCIBIOS_SUCCESSFUL)
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
ret = pci_generic_config_read32(bus, devfn, where & ~0x3, 4, val);
|
||||
if (ret != PCIBIOS_SUCCESSFUL)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* The v1 controller has a bug in its Configuration Request Retry
|
||||
|
||||
@@ -783,7 +783,7 @@ static int iproc_pcie_check_link(struct iproc_pcie *pcie)
|
||||
|
||||
/* make sure we are not in EP mode */
|
||||
iproc_pci_raw_config_read32(pcie, 0, PCI_HEADER_TYPE, 1, &hdr_type);
|
||||
if ((hdr_type & 0x7f) != PCI_HEADER_TYPE_BRIDGE) {
|
||||
if ((hdr_type & PCI_HEADER_TYPE_MASK) != PCI_HEADER_TYPE_BRIDGE) {
|
||||
dev_err(dev, "in EP mode, hdr=%#02x\n", hdr_type);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ static void rcar_pcie_ep_hw_init(struct rcar_pcie *pcie)
|
||||
rcar_rmw32(pcie, REXPCAP(0), 0xff, PCI_CAP_ID_EXP);
|
||||
rcar_rmw32(pcie, REXPCAP(PCI_EXP_FLAGS),
|
||||
PCI_EXP_FLAGS_TYPE, PCI_EXP_TYPE_ENDPOINT << 4);
|
||||
rcar_rmw32(pcie, RCONF(PCI_HEADER_TYPE), 0x7f,
|
||||
rcar_rmw32(pcie, RCONF(PCI_HEADER_TYPE), PCI_HEADER_TYPE_MASK,
|
||||
PCI_HEADER_TYPE_NORMAL);
|
||||
|
||||
/* Write out the physical slot number = 0 */
|
||||
|
||||
@@ -460,7 +460,7 @@ static int rcar_pcie_hw_init(struct rcar_pcie *pcie)
|
||||
rcar_rmw32(pcie, REXPCAP(0), 0xff, PCI_CAP_ID_EXP);
|
||||
rcar_rmw32(pcie, REXPCAP(PCI_EXP_FLAGS),
|
||||
PCI_EXP_FLAGS_TYPE, PCI_EXP_TYPE_ROOT_PORT << 4);
|
||||
rcar_rmw32(pcie, RCONF(PCI_HEADER_TYPE), 0x7f,
|
||||
rcar_rmw32(pcie, RCONF(PCI_HEADER_TYPE), PCI_HEADER_TYPE_MASK,
|
||||
PCI_HEADER_TYPE_BRIDGE);
|
||||
|
||||
/* Enable data link layer active state reporting */
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* (C) Copyright 2023, Xilinx, Inc.
|
||||
*/
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci-ecam.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
/* Interrupt registers definitions */
|
||||
#define XILINX_PCIE_INTR_LINK_DOWN 0
|
||||
#define XILINX_PCIE_INTR_HOT_RESET 3
|
||||
#define XILINX_PCIE_INTR_CFG_PCIE_TIMEOUT 4
|
||||
#define XILINX_PCIE_INTR_CFG_TIMEOUT 8
|
||||
#define XILINX_PCIE_INTR_CORRECTABLE 9
|
||||
#define XILINX_PCIE_INTR_NONFATAL 10
|
||||
#define XILINX_PCIE_INTR_FATAL 11
|
||||
#define XILINX_PCIE_INTR_CFG_ERR_POISON 12
|
||||
#define XILINX_PCIE_INTR_PME_TO_ACK_RCVD 15
|
||||
#define XILINX_PCIE_INTR_INTX 16
|
||||
#define XILINX_PCIE_INTR_PM_PME_RCVD 17
|
||||
#define XILINX_PCIE_INTR_MSI 17
|
||||
#define XILINX_PCIE_INTR_SLV_UNSUPP 20
|
||||
#define XILINX_PCIE_INTR_SLV_UNEXP 21
|
||||
#define XILINX_PCIE_INTR_SLV_COMPL 22
|
||||
#define XILINX_PCIE_INTR_SLV_ERRP 23
|
||||
#define XILINX_PCIE_INTR_SLV_CMPABT 24
|
||||
#define XILINX_PCIE_INTR_SLV_ILLBUR 25
|
||||
#define XILINX_PCIE_INTR_MST_DECERR 26
|
||||
#define XILINX_PCIE_INTR_MST_SLVERR 27
|
||||
#define XILINX_PCIE_INTR_SLV_PCIE_TIMEOUT 28
|
||||
@@ -16,11 +16,9 @@
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_pci.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pci-ecam.h>
|
||||
|
||||
#include "../pci.h"
|
||||
#include "pcie-xilinx-common.h"
|
||||
|
||||
/* Register definitions */
|
||||
#define XILINX_CPM_PCIE_REG_IDR 0x00000E10
|
||||
@@ -38,29 +36,7 @@
|
||||
#define XILINX_CPM_PCIE_IR_ENABLE 0x000002A8
|
||||
#define XILINX_CPM_PCIE_IR_LOCAL BIT(0)
|
||||
|
||||
/* Interrupt registers definitions */
|
||||
#define XILINX_CPM_PCIE_INTR_LINK_DOWN 0
|
||||
#define XILINX_CPM_PCIE_INTR_HOT_RESET 3
|
||||
#define XILINX_CPM_PCIE_INTR_CFG_PCIE_TIMEOUT 4
|
||||
#define XILINX_CPM_PCIE_INTR_CFG_TIMEOUT 8
|
||||
#define XILINX_CPM_PCIE_INTR_CORRECTABLE 9
|
||||
#define XILINX_CPM_PCIE_INTR_NONFATAL 10
|
||||
#define XILINX_CPM_PCIE_INTR_FATAL 11
|
||||
#define XILINX_CPM_PCIE_INTR_CFG_ERR_POISON 12
|
||||
#define XILINX_CPM_PCIE_INTR_PME_TO_ACK_RCVD 15
|
||||
#define XILINX_CPM_PCIE_INTR_INTX 16
|
||||
#define XILINX_CPM_PCIE_INTR_PM_PME_RCVD 17
|
||||
#define XILINX_CPM_PCIE_INTR_SLV_UNSUPP 20
|
||||
#define XILINX_CPM_PCIE_INTR_SLV_UNEXP 21
|
||||
#define XILINX_CPM_PCIE_INTR_SLV_COMPL 22
|
||||
#define XILINX_CPM_PCIE_INTR_SLV_ERRP 23
|
||||
#define XILINX_CPM_PCIE_INTR_SLV_CMPABT 24
|
||||
#define XILINX_CPM_PCIE_INTR_SLV_ILLBUR 25
|
||||
#define XILINX_CPM_PCIE_INTR_MST_DECERR 26
|
||||
#define XILINX_CPM_PCIE_INTR_MST_SLVERR 27
|
||||
#define XILINX_CPM_PCIE_INTR_SLV_PCIE_TIMEOUT 28
|
||||
|
||||
#define IMR(x) BIT(XILINX_CPM_PCIE_INTR_ ##x)
|
||||
#define IMR(x) BIT(XILINX_PCIE_INTR_ ##x)
|
||||
|
||||
#define XILINX_CPM_PCIE_IMR_ALL_MASK \
|
||||
( \
|
||||
@@ -323,7 +299,7 @@ static void xilinx_cpm_pcie_event_flow(struct irq_desc *desc)
|
||||
}
|
||||
|
||||
#define _IC(x, s) \
|
||||
[XILINX_CPM_PCIE_INTR_ ## x] = { __stringify(x), s }
|
||||
[XILINX_PCIE_INTR_ ## x] = { __stringify(x), s }
|
||||
|
||||
static const struct {
|
||||
const char *sym;
|
||||
@@ -359,9 +335,9 @@ static irqreturn_t xilinx_cpm_pcie_intr_handler(int irq, void *dev_id)
|
||||
d = irq_domain_get_irq_data(port->cpm_domain, irq);
|
||||
|
||||
switch (d->hwirq) {
|
||||
case XILINX_CPM_PCIE_INTR_CORRECTABLE:
|
||||
case XILINX_CPM_PCIE_INTR_NONFATAL:
|
||||
case XILINX_CPM_PCIE_INTR_FATAL:
|
||||
case XILINX_PCIE_INTR_CORRECTABLE:
|
||||
case XILINX_PCIE_INTR_NONFATAL:
|
||||
case XILINX_PCIE_INTR_FATAL:
|
||||
cpm_pcie_clear_err_interrupts(port);
|
||||
fallthrough;
|
||||
|
||||
@@ -466,7 +442,7 @@ static int xilinx_cpm_setup_irq(struct xilinx_cpm_pcie *port)
|
||||
}
|
||||
|
||||
port->intx_irq = irq_create_mapping(port->cpm_domain,
|
||||
XILINX_CPM_PCIE_INTR_INTX);
|
||||
XILINX_PCIE_INTR_INTX);
|
||||
if (!port->intx_irq) {
|
||||
dev_err(dev, "Failed to map INTx interrupt\n");
|
||||
return -ENXIO;
|
||||
|
||||
@@ -0,0 +1,814 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* PCIe host controller driver for Xilinx XDMA PCIe Bridge
|
||||
*
|
||||
* Copyright (C) 2023 Xilinx, Inc. All rights reserved.
|
||||
*/
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/msi.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_pci.h>
|
||||
|
||||
#include "../pci.h"
|
||||
#include "pcie-xilinx-common.h"
|
||||
|
||||
/* Register definitions */
|
||||
#define XILINX_PCIE_DMA_REG_IDR 0x00000138
|
||||
#define XILINX_PCIE_DMA_REG_IMR 0x0000013c
|
||||
#define XILINX_PCIE_DMA_REG_PSCR 0x00000144
|
||||
#define XILINX_PCIE_DMA_REG_RPSC 0x00000148
|
||||
#define XILINX_PCIE_DMA_REG_MSIBASE1 0x0000014c
|
||||
#define XILINX_PCIE_DMA_REG_MSIBASE2 0x00000150
|
||||
#define XILINX_PCIE_DMA_REG_RPEFR 0x00000154
|
||||
#define XILINX_PCIE_DMA_REG_IDRN 0x00000160
|
||||
#define XILINX_PCIE_DMA_REG_IDRN_MASK 0x00000164
|
||||
#define XILINX_PCIE_DMA_REG_MSI_LOW 0x00000170
|
||||
#define XILINX_PCIE_DMA_REG_MSI_HI 0x00000174
|
||||
#define XILINX_PCIE_DMA_REG_MSI_LOW_MASK 0x00000178
|
||||
#define XILINX_PCIE_DMA_REG_MSI_HI_MASK 0x0000017c
|
||||
|
||||
#define IMR(x) BIT(XILINX_PCIE_INTR_ ##x)
|
||||
|
||||
#define XILINX_PCIE_INTR_IMR_ALL_MASK \
|
||||
( \
|
||||
IMR(LINK_DOWN) | \
|
||||
IMR(HOT_RESET) | \
|
||||
IMR(CFG_TIMEOUT) | \
|
||||
IMR(CORRECTABLE) | \
|
||||
IMR(NONFATAL) | \
|
||||
IMR(FATAL) | \
|
||||
IMR(INTX) | \
|
||||
IMR(MSI) | \
|
||||
IMR(SLV_UNSUPP) | \
|
||||
IMR(SLV_UNEXP) | \
|
||||
IMR(SLV_COMPL) | \
|
||||
IMR(SLV_ERRP) | \
|
||||
IMR(SLV_CMPABT) | \
|
||||
IMR(SLV_ILLBUR) | \
|
||||
IMR(MST_DECERR) | \
|
||||
IMR(MST_SLVERR) | \
|
||||
)
|
||||
|
||||
#define XILINX_PCIE_DMA_IMR_ALL_MASK 0x0ff30fe9
|
||||
#define XILINX_PCIE_DMA_IDR_ALL_MASK 0xffffffff
|
||||
#define XILINX_PCIE_DMA_IDRN_MASK GENMASK(19, 16)
|
||||
|
||||
/* Root Port Error Register definitions */
|
||||
#define XILINX_PCIE_DMA_RPEFR_ERR_VALID BIT(18)
|
||||
#define XILINX_PCIE_DMA_RPEFR_REQ_ID GENMASK(15, 0)
|
||||
#define XILINX_PCIE_DMA_RPEFR_ALL_MASK 0xffffffff
|
||||
|
||||
/* Root Port Interrupt Register definitions */
|
||||
#define XILINX_PCIE_DMA_IDRN_SHIFT 16
|
||||
|
||||
/* Root Port Status/control Register definitions */
|
||||
#define XILINX_PCIE_DMA_REG_RPSC_BEN BIT(0)
|
||||
|
||||
/* Phy Status/Control Register definitions */
|
||||
#define XILINX_PCIE_DMA_REG_PSCR_LNKUP BIT(11)
|
||||
|
||||
/* Number of MSI IRQs */
|
||||
#define XILINX_NUM_MSI_IRQS 64
|
||||
|
||||
struct xilinx_msi {
|
||||
struct irq_domain *msi_domain;
|
||||
unsigned long *bitmap;
|
||||
struct irq_domain *dev_domain;
|
||||
struct mutex lock; /* Protect bitmap variable */
|
||||
int irq_msi0;
|
||||
int irq_msi1;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct pl_dma_pcie - PCIe port information
|
||||
* @dev: Device pointer
|
||||
* @reg_base: IO Mapped Register Base
|
||||
* @irq: Interrupt number
|
||||
* @cfg: Holds mappings of config space window
|
||||
* @phys_reg_base: Physical address of reg base
|
||||
* @intx_domain: Legacy IRQ domain pointer
|
||||
* @pldma_domain: PL DMA IRQ domain pointer
|
||||
* @resources: Bus Resources
|
||||
* @msi: MSI information
|
||||
* @intx_irq: INTx error interrupt number
|
||||
* @lock: Lock protecting shared register access
|
||||
*/
|
||||
struct pl_dma_pcie {
|
||||
struct device *dev;
|
||||
void __iomem *reg_base;
|
||||
int irq;
|
||||
struct pci_config_window *cfg;
|
||||
phys_addr_t phys_reg_base;
|
||||
struct irq_domain *intx_domain;
|
||||
struct irq_domain *pldma_domain;
|
||||
struct list_head resources;
|
||||
struct xilinx_msi msi;
|
||||
int intx_irq;
|
||||
raw_spinlock_t lock;
|
||||
};
|
||||
|
||||
static inline u32 pcie_read(struct pl_dma_pcie *port, u32 reg)
|
||||
{
|
||||
return readl(port->reg_base + reg);
|
||||
}
|
||||
|
||||
static inline void pcie_write(struct pl_dma_pcie *port, u32 val, u32 reg)
|
||||
{
|
||||
writel(val, port->reg_base + reg);
|
||||
}
|
||||
|
||||
static inline bool xilinx_pl_dma_pcie_link_up(struct pl_dma_pcie *port)
|
||||
{
|
||||
return (pcie_read(port, XILINX_PCIE_DMA_REG_PSCR) &
|
||||
XILINX_PCIE_DMA_REG_PSCR_LNKUP) ? true : false;
|
||||
}
|
||||
|
||||
static void xilinx_pl_dma_pcie_clear_err_interrupts(struct pl_dma_pcie *port)
|
||||
{
|
||||
unsigned long val = pcie_read(port, XILINX_PCIE_DMA_REG_RPEFR);
|
||||
|
||||
if (val & XILINX_PCIE_DMA_RPEFR_ERR_VALID) {
|
||||
dev_dbg(port->dev, "Requester ID %lu\n",
|
||||
val & XILINX_PCIE_DMA_RPEFR_REQ_ID);
|
||||
pcie_write(port, XILINX_PCIE_DMA_RPEFR_ALL_MASK,
|
||||
XILINX_PCIE_DMA_REG_RPEFR);
|
||||
}
|
||||
}
|
||||
|
||||
static bool xilinx_pl_dma_pcie_valid_device(struct pci_bus *bus,
|
||||
unsigned int devfn)
|
||||
{
|
||||
struct pl_dma_pcie *port = bus->sysdata;
|
||||
|
||||
if (!pci_is_root_bus(bus)) {
|
||||
/*
|
||||
* Checking whether the link is up is the last line of
|
||||
* defense, and this check is inherently racy by definition.
|
||||
* Sending a PIO request to a downstream device when the link is
|
||||
* down causes an unrecoverable error, and a reset of the entire
|
||||
* PCIe controller will be needed. We can reduce the likelihood
|
||||
* of that unrecoverable error by checking whether the link is
|
||||
* up, but we can't completely prevent it because the link may
|
||||
* go down between the link-up check and the PIO request.
|
||||
*/
|
||||
if (!xilinx_pl_dma_pcie_link_up(port))
|
||||
return false;
|
||||
} else if (devfn > 0)
|
||||
/* Only one device down on each root port */
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __iomem *xilinx_pl_dma_pcie_map_bus(struct pci_bus *bus,
|
||||
unsigned int devfn, int where)
|
||||
{
|
||||
struct pl_dma_pcie *port = bus->sysdata;
|
||||
|
||||
if (!xilinx_pl_dma_pcie_valid_device(bus, devfn))
|
||||
return NULL;
|
||||
|
||||
return port->reg_base + PCIE_ECAM_OFFSET(bus->number, devfn, where);
|
||||
}
|
||||
|
||||
/* PCIe operations */
|
||||
static struct pci_ecam_ops xilinx_pl_dma_pcie_ops = {
|
||||
.pci_ops = {
|
||||
.map_bus = xilinx_pl_dma_pcie_map_bus,
|
||||
.read = pci_generic_config_read,
|
||||
.write = pci_generic_config_write,
|
||||
}
|
||||
};
|
||||
|
||||
static void xilinx_pl_dma_pcie_enable_msi(struct pl_dma_pcie *port)
|
||||
{
|
||||
phys_addr_t msi_addr = port->phys_reg_base;
|
||||
|
||||
pcie_write(port, upper_32_bits(msi_addr), XILINX_PCIE_DMA_REG_MSIBASE1);
|
||||
pcie_write(port, lower_32_bits(msi_addr), XILINX_PCIE_DMA_REG_MSIBASE2);
|
||||
}
|
||||
|
||||
static void xilinx_mask_intx_irq(struct irq_data *data)
|
||||
{
|
||||
struct pl_dma_pcie *port = irq_data_get_irq_chip_data(data);
|
||||
unsigned long flags;
|
||||
u32 mask, val;
|
||||
|
||||
mask = BIT(data->hwirq + XILINX_PCIE_DMA_IDRN_SHIFT);
|
||||
raw_spin_lock_irqsave(&port->lock, flags);
|
||||
val = pcie_read(port, XILINX_PCIE_DMA_REG_IDRN_MASK);
|
||||
pcie_write(port, (val & (~mask)), XILINX_PCIE_DMA_REG_IDRN_MASK);
|
||||
raw_spin_unlock_irqrestore(&port->lock, flags);
|
||||
}
|
||||
|
||||
static void xilinx_unmask_intx_irq(struct irq_data *data)
|
||||
{
|
||||
struct pl_dma_pcie *port = irq_data_get_irq_chip_data(data);
|
||||
unsigned long flags;
|
||||
u32 mask, val;
|
||||
|
||||
mask = BIT(data->hwirq + XILINX_PCIE_DMA_IDRN_SHIFT);
|
||||
raw_spin_lock_irqsave(&port->lock, flags);
|
||||
val = pcie_read(port, XILINX_PCIE_DMA_REG_IDRN_MASK);
|
||||
pcie_write(port, (val | mask), XILINX_PCIE_DMA_REG_IDRN_MASK);
|
||||
raw_spin_unlock_irqrestore(&port->lock, flags);
|
||||
}
|
||||
|
||||
static struct irq_chip xilinx_leg_irq_chip = {
|
||||
.name = "pl_dma:INTx",
|
||||
.irq_mask = xilinx_mask_intx_irq,
|
||||
.irq_unmask = xilinx_unmask_intx_irq,
|
||||
};
|
||||
|
||||
static int xilinx_pl_dma_pcie_intx_map(struct irq_domain *domain,
|
||||
unsigned int irq, irq_hw_number_t hwirq)
|
||||
{
|
||||
irq_set_chip_and_handler(irq, &xilinx_leg_irq_chip, handle_level_irq);
|
||||
irq_set_chip_data(irq, domain->host_data);
|
||||
irq_set_status_flags(irq, IRQ_LEVEL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* INTx IRQ Domain operations */
|
||||
static const struct irq_domain_ops intx_domain_ops = {
|
||||
.map = xilinx_pl_dma_pcie_intx_map,
|
||||
};
|
||||
|
||||
static irqreturn_t xilinx_pl_dma_pcie_msi_handler_high(int irq, void *args)
|
||||
{
|
||||
struct xilinx_msi *msi;
|
||||
unsigned long status;
|
||||
u32 bit, virq;
|
||||
struct pl_dma_pcie *port = args;
|
||||
|
||||
msi = &port->msi;
|
||||
|
||||
while ((status = pcie_read(port, XILINX_PCIE_DMA_REG_MSI_HI)) != 0) {
|
||||
for_each_set_bit(bit, &status, 32) {
|
||||
pcie_write(port, 1 << bit, XILINX_PCIE_DMA_REG_MSI_HI);
|
||||
bit = bit + 32;
|
||||
virq = irq_find_mapping(msi->dev_domain, bit);
|
||||
if (virq)
|
||||
generic_handle_irq(virq);
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t xilinx_pl_dma_pcie_msi_handler_low(int irq, void *args)
|
||||
{
|
||||
struct pl_dma_pcie *port = args;
|
||||
struct xilinx_msi *msi;
|
||||
unsigned long status;
|
||||
u32 bit, virq;
|
||||
|
||||
msi = &port->msi;
|
||||
|
||||
while ((status = pcie_read(port, XILINX_PCIE_DMA_REG_MSI_LOW)) != 0) {
|
||||
for_each_set_bit(bit, &status, 32) {
|
||||
pcie_write(port, 1 << bit, XILINX_PCIE_DMA_REG_MSI_LOW);
|
||||
virq = irq_find_mapping(msi->dev_domain, bit);
|
||||
if (virq)
|
||||
generic_handle_irq(virq);
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t xilinx_pl_dma_pcie_event_flow(int irq, void *args)
|
||||
{
|
||||
struct pl_dma_pcie *port = args;
|
||||
unsigned long val;
|
||||
int i;
|
||||
|
||||
val = pcie_read(port, XILINX_PCIE_DMA_REG_IDR);
|
||||
val &= pcie_read(port, XILINX_PCIE_DMA_REG_IMR);
|
||||
for_each_set_bit(i, &val, 32)
|
||||
generic_handle_domain_irq(port->pldma_domain, i);
|
||||
|
||||
pcie_write(port, val, XILINX_PCIE_DMA_REG_IDR);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
#define _IC(x, s) \
|
||||
[XILINX_PCIE_INTR_ ## x] = { __stringify(x), s }
|
||||
|
||||
static const struct {
|
||||
const char *sym;
|
||||
const char *str;
|
||||
} intr_cause[32] = {
|
||||
_IC(LINK_DOWN, "Link Down"),
|
||||
_IC(HOT_RESET, "Hot reset"),
|
||||
_IC(CFG_TIMEOUT, "ECAM access timeout"),
|
||||
_IC(CORRECTABLE, "Correctable error message"),
|
||||
_IC(NONFATAL, "Non fatal error message"),
|
||||
_IC(FATAL, "Fatal error message"),
|
||||
_IC(SLV_UNSUPP, "Slave unsupported request"),
|
||||
_IC(SLV_UNEXP, "Slave unexpected completion"),
|
||||
_IC(SLV_COMPL, "Slave completion timeout"),
|
||||
_IC(SLV_ERRP, "Slave Error Poison"),
|
||||
_IC(SLV_CMPABT, "Slave Completer Abort"),
|
||||
_IC(SLV_ILLBUR, "Slave Illegal Burst"),
|
||||
_IC(MST_DECERR, "Master decode error"),
|
||||
_IC(MST_SLVERR, "Master slave error"),
|
||||
};
|
||||
|
||||
static irqreturn_t xilinx_pl_dma_pcie_intr_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct pl_dma_pcie *port = (struct pl_dma_pcie *)dev_id;
|
||||
struct device *dev = port->dev;
|
||||
struct irq_data *d;
|
||||
|
||||
d = irq_domain_get_irq_data(port->pldma_domain, irq);
|
||||
switch (d->hwirq) {
|
||||
case XILINX_PCIE_INTR_CORRECTABLE:
|
||||
case XILINX_PCIE_INTR_NONFATAL:
|
||||
case XILINX_PCIE_INTR_FATAL:
|
||||
xilinx_pl_dma_pcie_clear_err_interrupts(port);
|
||||
fallthrough;
|
||||
|
||||
default:
|
||||
if (intr_cause[d->hwirq].str)
|
||||
dev_warn(dev, "%s\n", intr_cause[d->hwirq].str);
|
||||
else
|
||||
dev_warn(dev, "Unknown IRQ %ld\n", d->hwirq);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static struct irq_chip xilinx_msi_irq_chip = {
|
||||
.name = "pl_dma:PCIe MSI",
|
||||
.irq_enable = pci_msi_unmask_irq,
|
||||
.irq_disable = pci_msi_mask_irq,
|
||||
.irq_mask = pci_msi_mask_irq,
|
||||
.irq_unmask = pci_msi_unmask_irq,
|
||||
};
|
||||
|
||||
static struct msi_domain_info xilinx_msi_domain_info = {
|
||||
.flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
|
||||
MSI_FLAG_MULTI_PCI_MSI),
|
||||
.chip = &xilinx_msi_irq_chip,
|
||||
};
|
||||
|
||||
static void xilinx_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
|
||||
{
|
||||
struct pl_dma_pcie *pcie = irq_data_get_irq_chip_data(data);
|
||||
phys_addr_t msi_addr = pcie->phys_reg_base;
|
||||
|
||||
msg->address_lo = lower_32_bits(msi_addr);
|
||||
msg->address_hi = upper_32_bits(msi_addr);
|
||||
msg->data = data->hwirq;
|
||||
}
|
||||
|
||||
static int xilinx_msi_set_affinity(struct irq_data *irq_data,
|
||||
const struct cpumask *mask, bool force)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static struct irq_chip xilinx_irq_chip = {
|
||||
.name = "pl_dma:MSI",
|
||||
.irq_compose_msi_msg = xilinx_compose_msi_msg,
|
||||
.irq_set_affinity = xilinx_msi_set_affinity,
|
||||
};
|
||||
|
||||
static int xilinx_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
|
||||
unsigned int nr_irqs, void *args)
|
||||
{
|
||||
struct pl_dma_pcie *pcie = domain->host_data;
|
||||
struct xilinx_msi *msi = &pcie->msi;
|
||||
int bit, i;
|
||||
|
||||
mutex_lock(&msi->lock);
|
||||
bit = bitmap_find_free_region(msi->bitmap, XILINX_NUM_MSI_IRQS,
|
||||
get_count_order(nr_irqs));
|
||||
if (bit < 0) {
|
||||
mutex_unlock(&msi->lock);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
for (i = 0; i < nr_irqs; i++) {
|
||||
irq_domain_set_info(domain, virq + i, bit + i, &xilinx_irq_chip,
|
||||
domain->host_data, handle_simple_irq,
|
||||
NULL, NULL);
|
||||
}
|
||||
mutex_unlock(&msi->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xilinx_irq_domain_free(struct irq_domain *domain, unsigned int virq,
|
||||
unsigned int nr_irqs)
|
||||
{
|
||||
struct irq_data *data = irq_domain_get_irq_data(domain, virq);
|
||||
struct pl_dma_pcie *pcie = irq_data_get_irq_chip_data(data);
|
||||
struct xilinx_msi *msi = &pcie->msi;
|
||||
|
||||
mutex_lock(&msi->lock);
|
||||
bitmap_release_region(msi->bitmap, data->hwirq,
|
||||
get_count_order(nr_irqs));
|
||||
mutex_unlock(&msi->lock);
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops dev_msi_domain_ops = {
|
||||
.alloc = xilinx_irq_domain_alloc,
|
||||
.free = xilinx_irq_domain_free,
|
||||
};
|
||||
|
||||
static void xilinx_pl_dma_pcie_free_irq_domains(struct pl_dma_pcie *port)
|
||||
{
|
||||
struct xilinx_msi *msi = &port->msi;
|
||||
|
||||
if (port->intx_domain) {
|
||||
irq_domain_remove(port->intx_domain);
|
||||
port->intx_domain = NULL;
|
||||
}
|
||||
|
||||
if (msi->dev_domain) {
|
||||
irq_domain_remove(msi->dev_domain);
|
||||
msi->dev_domain = NULL;
|
||||
}
|
||||
|
||||
if (msi->msi_domain) {
|
||||
irq_domain_remove(msi->msi_domain);
|
||||
msi->msi_domain = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int xilinx_pl_dma_pcie_init_msi_irq_domain(struct pl_dma_pcie *port)
|
||||
{
|
||||
struct device *dev = port->dev;
|
||||
struct xilinx_msi *msi = &port->msi;
|
||||
int size = BITS_TO_LONGS(XILINX_NUM_MSI_IRQS) * sizeof(long);
|
||||
struct fwnode_handle *fwnode = of_node_to_fwnode(port->dev->of_node);
|
||||
|
||||
msi->dev_domain = irq_domain_add_linear(NULL, XILINX_NUM_MSI_IRQS,
|
||||
&dev_msi_domain_ops, port);
|
||||
if (!msi->dev_domain)
|
||||
goto out;
|
||||
|
||||
msi->msi_domain = pci_msi_create_irq_domain(fwnode,
|
||||
&xilinx_msi_domain_info,
|
||||
msi->dev_domain);
|
||||
if (!msi->msi_domain)
|
||||
goto out;
|
||||
|
||||
mutex_init(&msi->lock);
|
||||
msi->bitmap = kzalloc(size, GFP_KERNEL);
|
||||
if (!msi->bitmap)
|
||||
goto out;
|
||||
|
||||
raw_spin_lock_init(&port->lock);
|
||||
xilinx_pl_dma_pcie_enable_msi(port);
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
xilinx_pl_dma_pcie_free_irq_domains(port);
|
||||
dev_err(dev, "Failed to allocate MSI IRQ domains\n");
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/*
|
||||
* INTx error interrupts are Xilinx controller specific interrupt, used to
|
||||
* notify user about errors such as cfg timeout, slave unsupported requests,
|
||||
* fatal and non fatal error etc.
|
||||
*/
|
||||
|
||||
static irqreturn_t xilinx_pl_dma_pcie_intx_flow(int irq, void *args)
|
||||
{
|
||||
unsigned long val;
|
||||
int i;
|
||||
struct pl_dma_pcie *port = args;
|
||||
|
||||
val = FIELD_GET(XILINX_PCIE_DMA_IDRN_MASK,
|
||||
pcie_read(port, XILINX_PCIE_DMA_REG_IDRN));
|
||||
|
||||
for_each_set_bit(i, &val, PCI_NUM_INTX)
|
||||
generic_handle_domain_irq(port->intx_domain, i);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void xilinx_pl_dma_pcie_mask_event_irq(struct irq_data *d)
|
||||
{
|
||||
struct pl_dma_pcie *port = irq_data_get_irq_chip_data(d);
|
||||
u32 val;
|
||||
|
||||
raw_spin_lock(&port->lock);
|
||||
val = pcie_read(port, XILINX_PCIE_DMA_REG_IMR);
|
||||
val &= ~BIT(d->hwirq);
|
||||
pcie_write(port, val, XILINX_PCIE_DMA_REG_IMR);
|
||||
raw_spin_unlock(&port->lock);
|
||||
}
|
||||
|
||||
static void xilinx_pl_dma_pcie_unmask_event_irq(struct irq_data *d)
|
||||
{
|
||||
struct pl_dma_pcie *port = irq_data_get_irq_chip_data(d);
|
||||
u32 val;
|
||||
|
||||
raw_spin_lock(&port->lock);
|
||||
val = pcie_read(port, XILINX_PCIE_DMA_REG_IMR);
|
||||
val |= BIT(d->hwirq);
|
||||
pcie_write(port, val, XILINX_PCIE_DMA_REG_IMR);
|
||||
raw_spin_unlock(&port->lock);
|
||||
}
|
||||
|
||||
static struct irq_chip xilinx_pl_dma_pcie_event_irq_chip = {
|
||||
.name = "pl_dma:RC-Event",
|
||||
.irq_mask = xilinx_pl_dma_pcie_mask_event_irq,
|
||||
.irq_unmask = xilinx_pl_dma_pcie_unmask_event_irq,
|
||||
};
|
||||
|
||||
static int xilinx_pl_dma_pcie_event_map(struct irq_domain *domain,
|
||||
unsigned int irq, irq_hw_number_t hwirq)
|
||||
{
|
||||
irq_set_chip_and_handler(irq, &xilinx_pl_dma_pcie_event_irq_chip,
|
||||
handle_level_irq);
|
||||
irq_set_chip_data(irq, domain->host_data);
|
||||
irq_set_status_flags(irq, IRQ_LEVEL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops event_domain_ops = {
|
||||
.map = xilinx_pl_dma_pcie_event_map,
|
||||
};
|
||||
|
||||
/**
|
||||
* xilinx_pl_dma_pcie_init_irq_domain - Initialize IRQ domain
|
||||
* @port: PCIe port information
|
||||
*
|
||||
* Return: '0' on success and error value on failure.
|
||||
*/
|
||||
static int xilinx_pl_dma_pcie_init_irq_domain(struct pl_dma_pcie *port)
|
||||
{
|
||||
struct device *dev = port->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
struct device_node *pcie_intc_node;
|
||||
int ret;
|
||||
|
||||
/* Setup INTx */
|
||||
pcie_intc_node = of_get_child_by_name(node, "interrupt-controller");
|
||||
if (!pcie_intc_node) {
|
||||
dev_err(dev, "No PCIe Intc node found\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
port->pldma_domain = irq_domain_add_linear(pcie_intc_node, 32,
|
||||
&event_domain_ops, port);
|
||||
if (!port->pldma_domain)
|
||||
return -ENOMEM;
|
||||
|
||||
irq_domain_update_bus_token(port->pldma_domain, DOMAIN_BUS_NEXUS);
|
||||
|
||||
port->intx_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX,
|
||||
&intx_domain_ops, port);
|
||||
if (!port->intx_domain) {
|
||||
dev_err(dev, "Failed to get a INTx IRQ domain\n");
|
||||
return PTR_ERR(port->intx_domain);
|
||||
}
|
||||
|
||||
irq_domain_update_bus_token(port->intx_domain, DOMAIN_BUS_WIRED);
|
||||
|
||||
ret = xilinx_pl_dma_pcie_init_msi_irq_domain(port);
|
||||
if (ret != 0) {
|
||||
irq_domain_remove(port->intx_domain);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
of_node_put(pcie_intc_node);
|
||||
raw_spin_lock_init(&port->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xilinx_pl_dma_pcie_setup_irq(struct pl_dma_pcie *port)
|
||||
{
|
||||
struct device *dev = port->dev;
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
int i, irq, err;
|
||||
|
||||
port->irq = platform_get_irq(pdev, 0);
|
||||
if (port->irq < 0)
|
||||
return port->irq;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(intr_cause); i++) {
|
||||
int err;
|
||||
|
||||
if (!intr_cause[i].str)
|
||||
continue;
|
||||
|
||||
irq = irq_create_mapping(port->pldma_domain, i);
|
||||
if (!irq) {
|
||||
dev_err(dev, "Failed to map interrupt\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
err = devm_request_irq(dev, irq,
|
||||
xilinx_pl_dma_pcie_intr_handler,
|
||||
IRQF_SHARED | IRQF_NO_THREAD,
|
||||
intr_cause[i].sym, port);
|
||||
if (err) {
|
||||
dev_err(dev, "Failed to request IRQ %d\n", irq);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
port->intx_irq = irq_create_mapping(port->pldma_domain,
|
||||
XILINX_PCIE_INTR_INTX);
|
||||
if (!port->intx_irq) {
|
||||
dev_err(dev, "Failed to map INTx interrupt\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
err = devm_request_irq(dev, port->intx_irq, xilinx_pl_dma_pcie_intx_flow,
|
||||
IRQF_SHARED | IRQF_NO_THREAD, NULL, port);
|
||||
if (err) {
|
||||
dev_err(dev, "Failed to request INTx IRQ %d\n", irq);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = devm_request_irq(dev, port->irq, xilinx_pl_dma_pcie_event_flow,
|
||||
IRQF_SHARED | IRQF_NO_THREAD, NULL, port);
|
||||
if (err) {
|
||||
dev_err(dev, "Failed to request event IRQ %d\n", irq);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xilinx_pl_dma_pcie_init_port(struct pl_dma_pcie *port)
|
||||
{
|
||||
if (xilinx_pl_dma_pcie_link_up(port))
|
||||
dev_info(port->dev, "PCIe Link is UP\n");
|
||||
else
|
||||
dev_info(port->dev, "PCIe Link is DOWN\n");
|
||||
|
||||
/* Disable all interrupts */
|
||||
pcie_write(port, ~XILINX_PCIE_DMA_IDR_ALL_MASK,
|
||||
XILINX_PCIE_DMA_REG_IMR);
|
||||
|
||||
/* Clear pending interrupts */
|
||||
pcie_write(port, pcie_read(port, XILINX_PCIE_DMA_REG_IDR) &
|
||||
XILINX_PCIE_DMA_IMR_ALL_MASK,
|
||||
XILINX_PCIE_DMA_REG_IDR);
|
||||
|
||||
/* Needed for MSI DECODE MODE */
|
||||
pcie_write(port, XILINX_PCIE_DMA_IDR_ALL_MASK,
|
||||
XILINX_PCIE_DMA_REG_MSI_LOW_MASK);
|
||||
pcie_write(port, XILINX_PCIE_DMA_IDR_ALL_MASK,
|
||||
XILINX_PCIE_DMA_REG_MSI_HI_MASK);
|
||||
|
||||
/* Set the Bridge enable bit */
|
||||
pcie_write(port, pcie_read(port, XILINX_PCIE_DMA_REG_RPSC) |
|
||||
XILINX_PCIE_DMA_REG_RPSC_BEN,
|
||||
XILINX_PCIE_DMA_REG_RPSC);
|
||||
}
|
||||
|
||||
static int xilinx_request_msi_irq(struct pl_dma_pcie *port)
|
||||
{
|
||||
struct device *dev = port->dev;
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
int ret;
|
||||
|
||||
port->msi.irq_msi0 = platform_get_irq_byname(pdev, "msi0");
|
||||
if (port->msi.irq_msi0 <= 0) {
|
||||
dev_err(dev, "Unable to find msi0 IRQ line\n");
|
||||
return port->msi.irq_msi0;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(dev, port->msi.irq_msi0, xilinx_pl_dma_pcie_msi_handler_low,
|
||||
IRQF_SHARED | IRQF_NO_THREAD, "xlnx-pcie-dma-pl",
|
||||
port);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to register interrupt\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
port->msi.irq_msi1 = platform_get_irq_byname(pdev, "msi1");
|
||||
if (port->msi.irq_msi1 <= 0) {
|
||||
dev_err(dev, "Unable to find msi1 IRQ line\n");
|
||||
return port->msi.irq_msi1;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(dev, port->msi.irq_msi1, xilinx_pl_dma_pcie_msi_handler_high,
|
||||
IRQF_SHARED | IRQF_NO_THREAD, "xlnx-pcie-dma-pl",
|
||||
port);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to register interrupt\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xilinx_pl_dma_pcie_parse_dt(struct pl_dma_pcie *port,
|
||||
struct resource *bus_range)
|
||||
{
|
||||
struct device *dev = port->dev;
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct resource *res;
|
||||
int err;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "Missing \"reg\" property\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
port->phys_reg_base = res->start;
|
||||
|
||||
port->cfg = pci_ecam_create(dev, res, bus_range, &xilinx_pl_dma_pcie_ops);
|
||||
if (IS_ERR(port->cfg))
|
||||
return PTR_ERR(port->cfg);
|
||||
|
||||
port->reg_base = port->cfg->win;
|
||||
|
||||
err = xilinx_request_msi_irq(port);
|
||||
if (err) {
|
||||
pci_ecam_free(port->cfg);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xilinx_pl_dma_pcie_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct pl_dma_pcie *port;
|
||||
struct pci_host_bridge *bridge;
|
||||
struct resource_entry *bus;
|
||||
int err;
|
||||
|
||||
bridge = devm_pci_alloc_host_bridge(dev, sizeof(*port));
|
||||
if (!bridge)
|
||||
return -ENODEV;
|
||||
|
||||
port = pci_host_bridge_priv(bridge);
|
||||
|
||||
port->dev = dev;
|
||||
|
||||
bus = resource_list_first_type(&bridge->windows, IORESOURCE_BUS);
|
||||
if (!bus)
|
||||
return -ENODEV;
|
||||
|
||||
err = xilinx_pl_dma_pcie_parse_dt(port, bus->res);
|
||||
if (err) {
|
||||
dev_err(dev, "Parsing DT failed\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
xilinx_pl_dma_pcie_init_port(port);
|
||||
|
||||
err = xilinx_pl_dma_pcie_init_irq_domain(port);
|
||||
if (err)
|
||||
goto err_irq_domain;
|
||||
|
||||
err = xilinx_pl_dma_pcie_setup_irq(port);
|
||||
|
||||
bridge->sysdata = port;
|
||||
bridge->ops = &xilinx_pl_dma_pcie_ops.pci_ops;
|
||||
|
||||
err = pci_host_probe(bridge);
|
||||
if (err < 0)
|
||||
goto err_host_bridge;
|
||||
|
||||
return 0;
|
||||
|
||||
err_host_bridge:
|
||||
xilinx_pl_dma_pcie_free_irq_domains(port);
|
||||
|
||||
err_irq_domain:
|
||||
pci_ecam_free(port->cfg);
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct of_device_id xilinx_pl_dma_pcie_of_match[] = {
|
||||
{
|
||||
.compatible = "xlnx,xdma-host-3.00",
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver xilinx_pl_dma_pcie_driver = {
|
||||
.driver = {
|
||||
.name = "xilinx-xdma-pcie",
|
||||
.of_match_table = xilinx_pl_dma_pcie_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.probe = xilinx_pl_dma_pcie_probe,
|
||||
};
|
||||
|
||||
builtin_platform_driver(xilinx_pl_dma_pcie_driver);
|
||||
@@ -126,7 +126,7 @@
|
||||
#define E_ECAM_CR_ENABLE BIT(0)
|
||||
#define E_ECAM_SIZE_LOC GENMASK(20, 16)
|
||||
#define E_ECAM_SIZE_SHIFT 16
|
||||
#define NWL_ECAM_VALUE_DEFAULT 12
|
||||
#define NWL_ECAM_MAX_SIZE 16
|
||||
|
||||
#define CFG_DMA_REG_BAR GENMASK(2, 0)
|
||||
#define CFG_PCIE_CACHE GENMASK(7, 0)
|
||||
@@ -165,8 +165,6 @@ struct nwl_pcie {
|
||||
u32 ecam_size;
|
||||
int irq_intx;
|
||||
int irq_misc;
|
||||
u32 ecam_value;
|
||||
u8 last_busno;
|
||||
struct nwl_msi msi;
|
||||
struct irq_domain *legacy_irq_domain;
|
||||
struct clk *clk;
|
||||
@@ -625,7 +623,7 @@ static int nwl_pcie_bridge_init(struct nwl_pcie *pcie)
|
||||
{
|
||||
struct device *dev = pcie->dev;
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
u32 breg_val, ecam_val, first_busno = 0;
|
||||
u32 breg_val, ecam_val;
|
||||
int err;
|
||||
|
||||
breg_val = nwl_bridge_readl(pcie, E_BREG_CAPABILITIES) & BREG_PRESENT;
|
||||
@@ -675,7 +673,7 @@ static int nwl_pcie_bridge_init(struct nwl_pcie *pcie)
|
||||
E_ECAM_CR_ENABLE, E_ECAM_CONTROL);
|
||||
|
||||
nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, E_ECAM_CONTROL) |
|
||||
(pcie->ecam_value << E_ECAM_SIZE_SHIFT),
|
||||
(NWL_ECAM_MAX_SIZE << E_ECAM_SIZE_SHIFT),
|
||||
E_ECAM_CONTROL);
|
||||
|
||||
nwl_bridge_writel(pcie, lower_32_bits(pcie->phys_ecam_base),
|
||||
@@ -683,15 +681,6 @@ static int nwl_pcie_bridge_init(struct nwl_pcie *pcie)
|
||||
nwl_bridge_writel(pcie, upper_32_bits(pcie->phys_ecam_base),
|
||||
E_ECAM_BASE_HI);
|
||||
|
||||
/* Get bus range */
|
||||
ecam_val = nwl_bridge_readl(pcie, E_ECAM_CONTROL);
|
||||
pcie->last_busno = (ecam_val & E_ECAM_SIZE_LOC) >> E_ECAM_SIZE_SHIFT;
|
||||
/* Write primary, secondary and subordinate bus numbers */
|
||||
ecam_val = first_busno;
|
||||
ecam_val |= (first_busno + 1) << 8;
|
||||
ecam_val |= (pcie->last_busno << E_ECAM_SIZE_SHIFT);
|
||||
writel(ecam_val, (pcie->ecam_base + PCI_PRIMARY_BUS));
|
||||
|
||||
if (nwl_pcie_link_up(pcie))
|
||||
dev_info(dev, "Link is UP\n");
|
||||
else
|
||||
@@ -792,7 +781,6 @@ static int nwl_pcie_probe(struct platform_device *pdev)
|
||||
pcie = pci_host_bridge_priv(bridge);
|
||||
|
||||
pcie->dev = dev;
|
||||
pcie->ecam_value = NWL_ECAM_VALUE_DEFAULT;
|
||||
|
||||
err = nwl_pcie_parse_dt(pcie, pdev);
|
||||
if (err) {
|
||||
|
||||
@@ -525,10 +525,9 @@ static void vmd_domain_reset(struct vmd_dev *vmd)
|
||||
base = vmd->cfgbar + PCIE_ECAM_OFFSET(bus,
|
||||
PCI_DEVFN(dev, 0), 0);
|
||||
|
||||
hdr_type = readb(base + PCI_HEADER_TYPE) &
|
||||
PCI_HEADER_TYPE_MASK;
|
||||
hdr_type = readb(base + PCI_HEADER_TYPE);
|
||||
|
||||
functions = (hdr_type & 0x80) ? 8 : 1;
|
||||
functions = (hdr_type & PCI_HEADER_TYPE_MFD) ? 8 : 1;
|
||||
for (fn = 0; fn < functions; fn++) {
|
||||
base = vmd->cfgbar + PCIE_ECAM_OFFSET(bus,
|
||||
PCI_DEVFN(dev, fn), 0);
|
||||
@@ -1078,10 +1077,7 @@ static int vmd_resume(struct device *dev)
|
||||
struct vmd_dev *vmd = pci_get_drvdata(pdev);
|
||||
int err, i;
|
||||
|
||||
if (vmd->irq_domain)
|
||||
vmd_set_msi_remapping(vmd, true);
|
||||
else
|
||||
vmd_set_msi_remapping(vmd, false);
|
||||
vmd_set_msi_remapping(vmd, !!vmd->irq_domain);
|
||||
|
||||
for (i = 0; i < vmd->msix_count; i++) {
|
||||
err = devm_request_irq(dev, vmd->irqs[i].virq,
|
||||
|
||||
@@ -38,7 +38,7 @@ static int devm_pci_epc_match(struct device *dev, void *res, void *match_data)
|
||||
*/
|
||||
void pci_epc_put(struct pci_epc *epc)
|
||||
{
|
||||
if (!epc || IS_ERR(epc))
|
||||
if (IS_ERR_OR_NULL(epc))
|
||||
return;
|
||||
|
||||
module_put(epc->ops->owner);
|
||||
@@ -660,7 +660,7 @@ void pci_epc_remove_epf(struct pci_epc *epc, struct pci_epf *epf,
|
||||
struct list_head *list;
|
||||
u32 func_no = 0;
|
||||
|
||||
if (!epc || IS_ERR(epc) || !epf)
|
||||
if (IS_ERR_OR_NULL(epc) || !epf)
|
||||
return;
|
||||
|
||||
if (type == PRIMARY_INTERFACE) {
|
||||
@@ -691,7 +691,7 @@ void pci_epc_linkup(struct pci_epc *epc)
|
||||
{
|
||||
struct pci_epf *epf;
|
||||
|
||||
if (!epc || IS_ERR(epc))
|
||||
if (IS_ERR_OR_NULL(epc))
|
||||
return;
|
||||
|
||||
mutex_lock(&epc->list_lock);
|
||||
@@ -717,7 +717,7 @@ void pci_epc_linkdown(struct pci_epc *epc)
|
||||
{
|
||||
struct pci_epf *epf;
|
||||
|
||||
if (!epc || IS_ERR(epc))
|
||||
if (IS_ERR_OR_NULL(epc))
|
||||
return;
|
||||
|
||||
mutex_lock(&epc->list_lock);
|
||||
@@ -743,7 +743,7 @@ void pci_epc_init_notify(struct pci_epc *epc)
|
||||
{
|
||||
struct pci_epf *epf;
|
||||
|
||||
if (!epc || IS_ERR(epc))
|
||||
if (IS_ERR_OR_NULL(epc))
|
||||
return;
|
||||
|
||||
mutex_lock(&epc->list_lock);
|
||||
@@ -769,7 +769,7 @@ void pci_epc_bme_notify(struct pci_epc *epc)
|
||||
{
|
||||
struct pci_epf *epf;
|
||||
|
||||
if (!epc || IS_ERR(epc))
|
||||
if (IS_ERR_OR_NULL(epc))
|
||||
return;
|
||||
|
||||
mutex_lock(&epc->list_lock);
|
||||
@@ -869,7 +869,6 @@ __pci_epc_create(struct device *dev, const struct pci_epc_ops *ops,
|
||||
|
||||
put_dev:
|
||||
put_device(&epc->dev);
|
||||
kfree(epc);
|
||||
|
||||
err_ret:
|
||||
return ERR_PTR(ret);
|
||||
|
||||
@@ -61,6 +61,18 @@ config HOTPLUG_PCI_ACPI
|
||||
|
||||
When in doubt, say N.
|
||||
|
||||
config HOTPLUG_PCI_ACPI_AMPERE_ALTRA
|
||||
tristate "ACPI PCI Hotplug driver Ampere Altra extensions"
|
||||
depends on HOTPLUG_PCI_ACPI
|
||||
depends on HAVE_ARM_SMCCC_DISCOVERY
|
||||
help
|
||||
Say Y here if you have an Ampere Altra system.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called acpiphp_ampere_altra.
|
||||
|
||||
When in doubt, say N.
|
||||
|
||||
config HOTPLUG_PCI_ACPI_IBM
|
||||
tristate "ACPI PCI Hotplug driver IBM extensions"
|
||||
depends on HOTPLUG_PCI_ACPI
|
||||
|
||||
@@ -23,6 +23,7 @@ obj-$(CONFIG_HOTPLUG_PCI_S390) += s390_pci_hpc.o
|
||||
|
||||
# acpiphp_ibm extends acpiphp, so should be linked afterwards.
|
||||
|
||||
obj-$(CONFIG_HOTPLUG_PCI_ACPI_AMPERE_ALTRA) += acpiphp_ampere_altra.o
|
||||
obj-$(CONFIG_HOTPLUG_PCI_ACPI_IBM) += acpiphp_ibm.o
|
||||
|
||||
pci_hotplug-objs := pci_hotplug_core.o
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* ACPI PCI Hot Plug Extension for Ampere Altra. Allows control of
|
||||
* attention LEDs via requests to system firmware.
|
||||
*
|
||||
* Copyright (C) 2023 Ampere Computing LLC
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "acpiphp_ampere_altra: " fmt
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci_hotplug.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "acpiphp.h"
|
||||
|
||||
#define HANDLE_OPEN 0xb0200000
|
||||
#define HANDLE_CLOSE 0xb0300000
|
||||
#define REQUEST 0xf0700000
|
||||
#define LED_CMD 0x00000004
|
||||
#define LED_ATTENTION 0x00000002
|
||||
#define LED_SET_ON 0x00000001
|
||||
#define LED_SET_OFF 0x00000002
|
||||
#define LED_SET_BLINK 0x00000003
|
||||
|
||||
static u32 led_service_id[4];
|
||||
|
||||
static int led_status(u8 status)
|
||||
{
|
||||
switch (status) {
|
||||
case 1: return LED_SET_ON;
|
||||
case 2: return LED_SET_BLINK;
|
||||
default: return LED_SET_OFF;
|
||||
}
|
||||
}
|
||||
|
||||
static int set_attention_status(struct hotplug_slot *slot, u8 status)
|
||||
{
|
||||
struct arm_smccc_res res;
|
||||
struct pci_bus *bus;
|
||||
struct pci_dev *root_port;
|
||||
unsigned long flags;
|
||||
u32 handle;
|
||||
int ret = 0;
|
||||
|
||||
bus = slot->pci_slot->bus;
|
||||
root_port = pcie_find_root_port(bus->self);
|
||||
if (!root_port)
|
||||
return -ENODEV;
|
||||
|
||||
local_irq_save(flags);
|
||||
arm_smccc_smc(HANDLE_OPEN, led_service_id[0], led_service_id[1],
|
||||
led_service_id[2], led_service_id[3], 0, 0, 0, &res);
|
||||
if (res.a0) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
handle = res.a1 & 0xffff0000;
|
||||
|
||||
arm_smccc_smc(REQUEST, LED_CMD, led_status(status), LED_ATTENTION,
|
||||
(PCI_SLOT(root_port->devfn) << 4) | (pci_domain_nr(bus) & 0xf),
|
||||
0, 0, handle, &res);
|
||||
if (res.a0)
|
||||
ret = -ENODEV;
|
||||
|
||||
arm_smccc_smc(HANDLE_CLOSE, handle, 0, 0, 0, 0, 0, 0, &res);
|
||||
|
||||
out:
|
||||
local_irq_restore(flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int get_attention_status(struct hotplug_slot *slot, u8 *status)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static struct acpiphp_attention_info ampere_altra_attn = {
|
||||
.set_attn = set_attention_status,
|
||||
.get_attn = get_attention_status,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int altra_led_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct fwnode_handle *fwnode = dev_fwnode(&pdev->dev);
|
||||
int ret;
|
||||
|
||||
ret = fwnode_property_read_u32_array(fwnode, "uuid", led_service_id, 4);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "can't find uuid\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = acpiphp_register_attention(&ere_altra_attn);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "can't register driver\n");
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void altra_led_remove(struct platform_device *pdev)
|
||||
{
|
||||
acpiphp_unregister_attention(&ere_altra_attn);
|
||||
}
|
||||
|
||||
static const struct acpi_device_id altra_led_ids[] = {
|
||||
{ "AMPC0008", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, altra_led_ids);
|
||||
|
||||
static struct platform_driver altra_led_driver = {
|
||||
.driver = {
|
||||
.name = "ampere-altra-leds",
|
||||
.acpi_match_table = altra_led_ids,
|
||||
},
|
||||
.probe = altra_led_probe,
|
||||
.remove_new = altra_led_remove,
|
||||
};
|
||||
module_platform_driver(altra_led_driver);
|
||||
|
||||
MODULE_AUTHOR("D Scott Phillips <scott@os.amperecomputing.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -78,8 +78,7 @@ int acpiphp_register_attention(struct acpiphp_attention_info *info)
|
||||
{
|
||||
int retval = -EINVAL;
|
||||
|
||||
if (info && info->owner && info->set_attn &&
|
||||
info->get_attn && !attention_info) {
|
||||
if (info && info->set_attn && info->get_attn && !attention_info) {
|
||||
retval = 0;
|
||||
attention_info = info;
|
||||
}
|
||||
|
||||
@@ -2059,7 +2059,7 @@ int cpqhp_process_SS(struct controller *ctrl, struct pci_func *func)
|
||||
return rc;
|
||||
|
||||
/* If it's a bridge, check the VGA Enable bit */
|
||||
if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
|
||||
if ((header_type & PCI_HEADER_TYPE_MASK) == PCI_HEADER_TYPE_BRIDGE) {
|
||||
rc = pci_bus_read_config_byte(pci_bus, devfn, PCI_BRIDGE_CONTROL, &BCR);
|
||||
if (rc)
|
||||
return rc;
|
||||
@@ -2342,7 +2342,7 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
|
||||
if ((temp_byte & PCI_HEADER_TYPE_MASK) == PCI_HEADER_TYPE_BRIDGE) {
|
||||
/* set Primary bus */
|
||||
dbg("set Primary bus = %d\n", func->bus);
|
||||
rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_PRIMARY_BUS, func->bus);
|
||||
@@ -2739,7 +2739,7 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func
|
||||
* PCI_BRIDGE_CTL_SERR |
|
||||
* PCI_BRIDGE_CTL_NO_ISA */
|
||||
rc = pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, command);
|
||||
} else if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_NORMAL) {
|
||||
} else if ((temp_byte & PCI_HEADER_TYPE_MASK) == PCI_HEADER_TYPE_NORMAL) {
|
||||
/* Standard device */
|
||||
rc = pci_bus_read_config_byte(pci_bus, devfn, 0x0B, &class_code);
|
||||
|
||||
|
||||
@@ -363,7 +363,7 @@ int cpqhp_save_config(struct controller *ctrl, int busnumber, int is_hot_plug)
|
||||
return rc;
|
||||
|
||||
/* If multi-function device, set max_functions to 8 */
|
||||
if (header_type & 0x80)
|
||||
if (header_type & PCI_HEADER_TYPE_MFD)
|
||||
max_functions = 8;
|
||||
else
|
||||
max_functions = 1;
|
||||
@@ -372,7 +372,7 @@ int cpqhp_save_config(struct controller *ctrl, int busnumber, int is_hot_plug)
|
||||
|
||||
do {
|
||||
DevError = 0;
|
||||
if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
|
||||
if ((header_type & PCI_HEADER_TYPE_MASK) == PCI_HEADER_TYPE_BRIDGE) {
|
||||
/* Recurse the subordinate bus
|
||||
* get the subordinate bus number
|
||||
*/
|
||||
@@ -487,13 +487,13 @@ int cpqhp_save_slot_config(struct controller *ctrl, struct pci_func *new_slot)
|
||||
pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), 0x0B, &class_code);
|
||||
pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), PCI_HEADER_TYPE, &header_type);
|
||||
|
||||
if (header_type & 0x80) /* Multi-function device */
|
||||
if (header_type & PCI_HEADER_TYPE_MFD)
|
||||
max_functions = 8;
|
||||
else
|
||||
max_functions = 1;
|
||||
|
||||
while (function < max_functions) {
|
||||
if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
|
||||
if ((header_type & PCI_HEADER_TYPE_MASK) == PCI_HEADER_TYPE_BRIDGE) {
|
||||
/* Recurse the subordinate bus */
|
||||
pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_SECONDARY_BUS, &secondary_bus);
|
||||
|
||||
@@ -571,7 +571,7 @@ int cpqhp_save_base_addr_length(struct controller *ctrl, struct pci_func *func)
|
||||
/* Check for Bridge */
|
||||
pci_bus_read_config_byte(pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
|
||||
|
||||
if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
|
||||
if ((header_type & PCI_HEADER_TYPE_MASK) == PCI_HEADER_TYPE_BRIDGE) {
|
||||
pci_bus_read_config_byte(pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus);
|
||||
|
||||
sub_bus = (int) secondary_bus;
|
||||
@@ -625,7 +625,7 @@ int cpqhp_save_base_addr_length(struct controller *ctrl, struct pci_func *func)
|
||||
|
||||
} /* End of base register loop */
|
||||
|
||||
} else if ((header_type & 0x7F) == 0x00) {
|
||||
} else if ((header_type & PCI_HEADER_TYPE_MASK) == PCI_HEADER_TYPE_NORMAL) {
|
||||
/* Figure out IO and memory base lengths */
|
||||
for (cloop = 0x10; cloop <= 0x24; cloop += 4) {
|
||||
temp_register = 0xFFFFFFFF;
|
||||
@@ -723,7 +723,7 @@ int cpqhp_save_used_resources(struct controller *ctrl, struct pci_func *func)
|
||||
/* Check for Bridge */
|
||||
pci_bus_read_config_byte(pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
|
||||
|
||||
if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
|
||||
if ((header_type & PCI_HEADER_TYPE_MASK) == PCI_HEADER_TYPE_BRIDGE) {
|
||||
/* Clear Bridge Control Register */
|
||||
command = 0x00;
|
||||
pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, command);
|
||||
@@ -858,7 +858,7 @@ int cpqhp_save_used_resources(struct controller *ctrl, struct pci_func *func)
|
||||
}
|
||||
} /* End of base register loop */
|
||||
/* Standard header */
|
||||
} else if ((header_type & 0x7F) == 0x00) {
|
||||
} else if ((header_type & PCI_HEADER_TYPE_MASK) == PCI_HEADER_TYPE_NORMAL) {
|
||||
/* Figure out IO and memory base lengths */
|
||||
for (cloop = 0x10; cloop <= 0x24; cloop += 4) {
|
||||
pci_bus_read_config_dword(pci_bus, devfn, cloop, &save_base);
|
||||
@@ -975,7 +975,7 @@ int cpqhp_configure_board(struct controller *ctrl, struct pci_func *func)
|
||||
pci_bus_read_config_byte(pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
|
||||
|
||||
/* If this is a bridge device, restore subordinate devices */
|
||||
if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
|
||||
if ((header_type & PCI_HEADER_TYPE_MASK) == PCI_HEADER_TYPE_BRIDGE) {
|
||||
pci_bus_read_config_byte(pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus);
|
||||
|
||||
sub_bus = (int) secondary_bus;
|
||||
@@ -1067,7 +1067,7 @@ int cpqhp_valid_replace(struct controller *ctrl, struct pci_func *func)
|
||||
/* Check for Bridge */
|
||||
pci_bus_read_config_byte(pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
|
||||
|
||||
if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
|
||||
if ((header_type & PCI_HEADER_TYPE_MASK) == PCI_HEADER_TYPE_BRIDGE) {
|
||||
/* In order to continue checking, we must program the
|
||||
* bus registers in the bridge to respond to accesses
|
||||
* for its subordinate bus(es)
|
||||
@@ -1090,7 +1090,7 @@ int cpqhp_valid_replace(struct controller *ctrl, struct pci_func *func)
|
||||
|
||||
}
|
||||
/* Check to see if it is a standard config header */
|
||||
else if ((header_type & 0x7F) == PCI_HEADER_TYPE_NORMAL) {
|
||||
else if ((header_type & PCI_HEADER_TYPE_MASK) == PCI_HEADER_TYPE_NORMAL) {
|
||||
/* Check subsystem vendor and ID */
|
||||
pci_bus_read_config_dword(pci_bus, devfn, PCI_SUBSYSTEM_VENDOR_ID, &temp_register);
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/pci_hotplug.h>
|
||||
#include <linux/pci_regs.h>
|
||||
|
||||
extern int ibmphp_debug;
|
||||
|
||||
@@ -286,8 +287,8 @@ int ibmphp_register_pci(void);
|
||||
|
||||
/* pci specific defines */
|
||||
#define PCI_VENDOR_ID_NOTVALID 0xFFFF
|
||||
#define PCI_HEADER_TYPE_MULTIDEVICE 0x80
|
||||
#define PCI_HEADER_TYPE_MULTIBRIDGE 0x81
|
||||
#define PCI_HEADER_TYPE_MULTIDEVICE (PCI_HEADER_TYPE_MFD|PCI_HEADER_TYPE_NORMAL)
|
||||
#define PCI_HEADER_TYPE_MULTIBRIDGE (PCI_HEADER_TYPE_MFD|PCI_HEADER_TYPE_BRIDGE)
|
||||
|
||||
#define LATENCY 0x64
|
||||
#define CACHE 64
|
||||
|
||||
@@ -1087,7 +1087,7 @@ static struct res_needed *scan_behind_bridge(struct pci_func *func, u8 busno)
|
||||
pci_bus_read_config_dword(ibmphp_pci_bus, devfn, PCI_CLASS_REVISION, &class);
|
||||
|
||||
debug("hdr_type behind the bridge is %x\n", hdr_type);
|
||||
if ((hdr_type & 0x7f) == PCI_HEADER_TYPE_BRIDGE) {
|
||||
if ((hdr_type & PCI_HEADER_TYPE_MASK) == PCI_HEADER_TYPE_BRIDGE) {
|
||||
err("embedded bridges not supported for hot-plugging.\n");
|
||||
amount->not_correct = 1;
|
||||
return amount;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#define pr_fmt(fmt) "pciehp: " fmt
|
||||
#define dev_fmt pr_fmt
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
@@ -103,7 +104,7 @@ static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
|
||||
struct pci_dev *pdev = ctrl->pcie->port;
|
||||
|
||||
if (status)
|
||||
status <<= PCI_EXP_SLTCTL_ATTN_IND_SHIFT;
|
||||
status = FIELD_PREP(PCI_EXP_SLTCTL_AIC, status);
|
||||
else
|
||||
status = PCI_EXP_SLTCTL_ATTN_IND_OFF;
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#define dev_fmt(fmt) "pciehp: " fmt
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
@@ -484,7 +485,7 @@ int pciehp_set_raw_indicator_status(struct hotplug_slot *hotplug_slot,
|
||||
struct pci_dev *pdev = ctrl_dev(ctrl);
|
||||
|
||||
pci_config_pm_runtime_get(pdev);
|
||||
pcie_write_cmd_nowait(ctrl, status << 6,
|
||||
pcie_write_cmd_nowait(ctrl, FIELD_PREP(PCI_EXP_SLTCTL_AIC, status),
|
||||
PCI_EXP_SLTCTL_AIC | PCI_EXP_SLTCTL_PIC);
|
||||
pci_config_pm_runtime_put(pdev);
|
||||
return 0;
|
||||
@@ -1028,7 +1029,7 @@ struct controller *pcie_init(struct pcie_device *dev)
|
||||
PCI_EXP_SLTSTA_DLLSC | PCI_EXP_SLTSTA_PDC);
|
||||
|
||||
ctrl_info(ctrl, "Slot #%d AttnBtn%c PwrCtrl%c MRL%c AttnInd%c PwrInd%c HotPlug%c Surprise%c Interlock%c NoCompl%c IbPresDis%c LLActRep%c%s\n",
|
||||
(slot_cap & PCI_EXP_SLTCAP_PSN) >> 19,
|
||||
FIELD_GET(PCI_EXP_SLTCAP_PSN, slot_cap),
|
||||
FLAG(slot_cap, PCI_EXP_SLTCAP_ABP),
|
||||
FLAG(slot_cap, PCI_EXP_SLTCAP_PCP),
|
||||
FLAG(slot_cap, PCI_EXP_SLTCAP_MRLSP),
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* Copyright Gavin Shan, IBM Corporation 2016.
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/libfdt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
@@ -731,7 +732,7 @@ static int pnv_php_enable_msix(struct pnv_php_slot *php_slot)
|
||||
|
||||
/* Check hotplug MSIx entry is in range */
|
||||
pcie_capability_read_word(pdev, PCI_EXP_FLAGS, &pcie_flag);
|
||||
entry.entry = (pcie_flag & PCI_EXP_FLAGS_IRQ) >> 9;
|
||||
entry.entry = FIELD_GET(PCI_EXP_FLAGS_IRQ, pcie_flag);
|
||||
if (entry.entry >= nr_entries)
|
||||
return -ERANGE;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
* Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
|
||||
* Copyright (C) 2016 Christoph Hellwig.
|
||||
*/
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/irq.h>
|
||||
@@ -188,7 +189,7 @@ static inline void pci_write_msg_msi(struct pci_dev *dev, struct msi_desc *desc,
|
||||
|
||||
pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &msgctl);
|
||||
msgctl &= ~PCI_MSI_FLAGS_QSIZE;
|
||||
msgctl |= desc->pci.msi_attrib.multiple << 4;
|
||||
msgctl |= FIELD_PREP(PCI_MSI_FLAGS_QSIZE, desc->pci.msi_attrib.multiple);
|
||||
pci_write_config_word(dev, pos + PCI_MSI_FLAGS, msgctl);
|
||||
|
||||
pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_LO, msg->address_lo);
|
||||
@@ -299,7 +300,7 @@ static int msi_setup_msi_desc(struct pci_dev *dev, int nvec,
|
||||
desc.pci.msi_attrib.is_64 = !!(control & PCI_MSI_FLAGS_64BIT);
|
||||
desc.pci.msi_attrib.can_mask = !!(control & PCI_MSI_FLAGS_MASKBIT);
|
||||
desc.pci.msi_attrib.default_irq = dev->irq;
|
||||
desc.pci.msi_attrib.multi_cap = (control & PCI_MSI_FLAGS_QMASK) >> 1;
|
||||
desc.pci.msi_attrib.multi_cap = FIELD_GET(PCI_MSI_FLAGS_QMASK, control);
|
||||
desc.pci.msi_attrib.multiple = ilog2(__roundup_pow_of_two(nvec));
|
||||
desc.affinity = masks;
|
||||
|
||||
@@ -478,7 +479,7 @@ int pci_msi_vec_count(struct pci_dev *dev)
|
||||
return -EINVAL;
|
||||
|
||||
pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &msgctl);
|
||||
ret = 1 << ((msgctl & PCI_MSI_FLAGS_QMASK) >> 1);
|
||||
ret = 1 << FIELD_GET(PCI_MSI_FLAGS_QMASK, msgctl);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -511,7 +512,8 @@ void __pci_restore_msi_state(struct pci_dev *dev)
|
||||
pci_read_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, &control);
|
||||
pci_msi_update_mask(entry, 0, 0);
|
||||
control &= ~PCI_MSI_FLAGS_QSIZE;
|
||||
control |= (entry->pci.msi_attrib.multiple << 4) | PCI_MSI_FLAGS_ENABLE;
|
||||
control |= PCI_MSI_FLAGS_ENABLE |
|
||||
FIELD_PREP(PCI_MSI_FLAGS_QSIZE, entry->pci.msi_attrib.multiple);
|
||||
pci_write_config_word(dev, dev->msi_cap + PCI_MSI_FLAGS, control);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,9 +28,9 @@ struct pci_p2pdma {
|
||||
};
|
||||
|
||||
struct pci_p2pdma_pagemap {
|
||||
struct dev_pagemap pgmap;
|
||||
struct pci_dev *provider;
|
||||
u64 bus_offset;
|
||||
struct dev_pagemap pgmap;
|
||||
};
|
||||
|
||||
static struct pci_p2pdma_pagemap *to_p2p_pgmap(struct dev_pagemap *pgmap)
|
||||
@@ -837,7 +837,6 @@ void *pci_alloc_p2pmem(struct pci_dev *pdev, size_t size)
|
||||
if (unlikely(!percpu_ref_tryget_live_rcu(ref))) {
|
||||
gen_pool_free(p2pdma->pool, (unsigned long) ret, size);
|
||||
ret = NULL;
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
|
||||
@@ -911,7 +911,7 @@ pci_power_t acpi_pci_choose_state(struct pci_dev *pdev)
|
||||
{
|
||||
int acpi_state, d_max;
|
||||
|
||||
if (pdev->no_d3cold)
|
||||
if (pdev->no_d3cold || !pdev->d3cold_allowed)
|
||||
d_max = ACPI_STATE_D3_HOT;
|
||||
else
|
||||
d_max = ACPI_STATE_D3_COLD;
|
||||
@@ -1215,12 +1215,12 @@ void acpi_pci_add_bus(struct pci_bus *bus)
|
||||
if (!pci_is_root_bus(bus))
|
||||
return;
|
||||
|
||||
obj = acpi_evaluate_dsm(ACPI_HANDLE(bus->bridge), &pci_acpi_dsm_guid, 3,
|
||||
DSM_PCI_POWER_ON_RESET_DELAY, NULL);
|
||||
obj = acpi_evaluate_dsm_typed(ACPI_HANDLE(bus->bridge), &pci_acpi_dsm_guid, 3,
|
||||
DSM_PCI_POWER_ON_RESET_DELAY, NULL, ACPI_TYPE_INTEGER);
|
||||
if (!obj)
|
||||
return;
|
||||
|
||||
if (obj->type == ACPI_TYPE_INTEGER && obj->integer.value == 1) {
|
||||
if (obj->integer.value == 1) {
|
||||
bridge = pci_find_host_bridge(bus);
|
||||
bridge->ignore_reset_delay = 1;
|
||||
}
|
||||
@@ -1376,12 +1376,13 @@ static void pci_acpi_optimize_delay(struct pci_dev *pdev,
|
||||
if (bridge->ignore_reset_delay)
|
||||
pdev->d3cold_delay = 0;
|
||||
|
||||
obj = acpi_evaluate_dsm(handle, &pci_acpi_dsm_guid, 3,
|
||||
DSM_PCI_DEVICE_READINESS_DURATIONS, NULL);
|
||||
obj = acpi_evaluate_dsm_typed(handle, &pci_acpi_dsm_guid, 3,
|
||||
DSM_PCI_DEVICE_READINESS_DURATIONS, NULL,
|
||||
ACPI_TYPE_PACKAGE);
|
||||
if (!obj)
|
||||
return;
|
||||
|
||||
if (obj->type == ACPI_TYPE_PACKAGE && obj->package.count == 5) {
|
||||
if (obj->package.count == 5) {
|
||||
elements = obj->package.elements;
|
||||
if (elements[0].type == ACPI_TYPE_INTEGER) {
|
||||
value = (int)elements[0].integer.value / 1000;
|
||||
|
||||
+6
-11
@@ -12,7 +12,7 @@
|
||||
* Modeled after usb's driverfs.c
|
||||
*/
|
||||
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/pci.h>
|
||||
@@ -230,8 +230,7 @@ static ssize_t current_link_width_show(struct device *dev,
|
||||
if (err)
|
||||
return -EINVAL;
|
||||
|
||||
return sysfs_emit(buf, "%u\n",
|
||||
(linkstat & PCI_EXP_LNKSTA_NLW) >> PCI_EXP_LNKSTA_NLW_SHIFT);
|
||||
return sysfs_emit(buf, "%u\n", FIELD_GET(PCI_EXP_LNKSTA_NLW, linkstat));
|
||||
}
|
||||
static DEVICE_ATTR_RO(current_link_width);
|
||||
|
||||
@@ -530,10 +529,7 @@ static ssize_t d3cold_allowed_store(struct device *dev,
|
||||
return -EINVAL;
|
||||
|
||||
pdev->d3cold_allowed = !!val;
|
||||
if (pdev->d3cold_allowed)
|
||||
pci_d3cold_enable(pdev);
|
||||
else
|
||||
pci_d3cold_disable(pdev);
|
||||
pci_bridge_d3_update(pdev);
|
||||
|
||||
pm_runtime_resume(dev);
|
||||
|
||||
@@ -1551,11 +1547,10 @@ static umode_t pci_dev_attrs_are_visible(struct kobject *kobj,
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
|
||||
if (a == &dev_attr_boot_vga.attr)
|
||||
if ((pdev->class >> 8) != PCI_CLASS_DISPLAY_VGA)
|
||||
return 0;
|
||||
if (a == &dev_attr_boot_vga.attr && pci_is_vga(pdev))
|
||||
return a->mode;
|
||||
|
||||
return a->mode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct attribute *pci_dev_hp_attrs[] = {
|
||||
|
||||
+35
-34
@@ -534,7 +534,7 @@ u8 pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap)
|
||||
|
||||
pci_bus_read_config_byte(bus, devfn, PCI_HEADER_TYPE, &hdr_type);
|
||||
|
||||
pos = __pci_bus_find_cap_start(bus, devfn, hdr_type & 0x7f);
|
||||
pos = __pci_bus_find_cap_start(bus, devfn, hdr_type & PCI_HEADER_TYPE_MASK);
|
||||
if (pos)
|
||||
pos = __pci_find_next_cap(bus, devfn, pos, cap);
|
||||
|
||||
@@ -732,15 +732,18 @@ u16 pci_find_vsec_capability(struct pci_dev *dev, u16 vendor, int cap)
|
||||
{
|
||||
u16 vsec = 0;
|
||||
u32 header;
|
||||
int ret;
|
||||
|
||||
if (vendor != dev->vendor)
|
||||
return 0;
|
||||
|
||||
while ((vsec = pci_find_next_ext_capability(dev, vsec,
|
||||
PCI_EXT_CAP_ID_VNDR))) {
|
||||
if (pci_read_config_dword(dev, vsec + PCI_VNDR_HEADER,
|
||||
&header) == PCIBIOS_SUCCESSFUL &&
|
||||
PCI_VNDR_HEADER_ID(header) == cap)
|
||||
ret = pci_read_config_dword(dev, vsec + PCI_VNDR_HEADER, &header);
|
||||
if (ret != PCIBIOS_SUCCESSFUL)
|
||||
continue;
|
||||
|
||||
if (PCI_VNDR_HEADER_ID(header) == cap)
|
||||
return vsec;
|
||||
}
|
||||
|
||||
@@ -1775,8 +1778,7 @@ static void pci_restore_rebar_state(struct pci_dev *pdev)
|
||||
return;
|
||||
|
||||
pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
|
||||
nbars = (ctrl & PCI_REBAR_CTRL_NBAR_MASK) >>
|
||||
PCI_REBAR_CTRL_NBAR_SHIFT;
|
||||
nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl);
|
||||
|
||||
for (i = 0; i < nbars; i++, pos += 8) {
|
||||
struct resource *res;
|
||||
@@ -1787,7 +1789,7 @@ static void pci_restore_rebar_state(struct pci_dev *pdev)
|
||||
res = pdev->resource + bar_idx;
|
||||
size = pci_rebar_bytes_to_size(resource_size(res));
|
||||
ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
|
||||
ctrl |= size << PCI_REBAR_CTRL_BAR_SHIFT;
|
||||
ctrl |= FIELD_PREP(PCI_REBAR_CTRL_BAR_SIZE, size);
|
||||
pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
|
||||
}
|
||||
}
|
||||
@@ -3228,7 +3230,7 @@ void pci_pm_init(struct pci_dev *dev)
|
||||
(pmc & PCI_PM_CAP_PME_D2) ? " D2" : "",
|
||||
(pmc & PCI_PM_CAP_PME_D3hot) ? " D3hot" : "",
|
||||
(pmc & PCI_PM_CAP_PME_D3cold) ? " D3cold" : "");
|
||||
dev->pme_support = pmc >> PCI_PM_CAP_PME_SHIFT;
|
||||
dev->pme_support = FIELD_GET(PCI_PM_CAP_PME_MASK, pmc);
|
||||
dev->pme_poll = true;
|
||||
/*
|
||||
* Make device's PM flags reflect the wake-up capability, but
|
||||
@@ -3299,20 +3301,20 @@ static int pci_ea_read(struct pci_dev *dev, int offset)
|
||||
ent_offset += 4;
|
||||
|
||||
/* Entry size field indicates DWORDs after 1st */
|
||||
ent_size = ((dw0 & PCI_EA_ES) + 1) << 2;
|
||||
ent_size = (FIELD_GET(PCI_EA_ES, dw0) + 1) << 2;
|
||||
|
||||
if (!(dw0 & PCI_EA_ENABLE)) /* Entry not enabled */
|
||||
goto out;
|
||||
|
||||
bei = (dw0 & PCI_EA_BEI) >> 4;
|
||||
prop = (dw0 & PCI_EA_PP) >> 8;
|
||||
bei = FIELD_GET(PCI_EA_BEI, dw0);
|
||||
prop = FIELD_GET(PCI_EA_PP, dw0);
|
||||
|
||||
/*
|
||||
* If the Property is in the reserved range, try the Secondary
|
||||
* Property instead.
|
||||
*/
|
||||
if (prop > PCI_EA_P_BRIDGE_IO && prop < PCI_EA_P_MEM_RESERVED)
|
||||
prop = (dw0 & PCI_EA_SP) >> 16;
|
||||
prop = FIELD_GET(PCI_EA_SP, dw0);
|
||||
if (prop > PCI_EA_P_BRIDGE_IO)
|
||||
goto out;
|
||||
|
||||
@@ -3719,14 +3721,13 @@ static int pci_rebar_find_pos(struct pci_dev *pdev, int bar)
|
||||
return -ENOTSUPP;
|
||||
|
||||
pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
|
||||
nbars = (ctrl & PCI_REBAR_CTRL_NBAR_MASK) >>
|
||||
PCI_REBAR_CTRL_NBAR_SHIFT;
|
||||
nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, ctrl);
|
||||
|
||||
for (i = 0; i < nbars; i++, pos += 8) {
|
||||
int bar_idx;
|
||||
|
||||
pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
|
||||
bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX;
|
||||
bar_idx = FIELD_GET(PCI_REBAR_CTRL_BAR_IDX, ctrl);
|
||||
if (bar_idx == bar)
|
||||
return pos;
|
||||
}
|
||||
@@ -3752,14 +3753,14 @@ u32 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar)
|
||||
return 0;
|
||||
|
||||
pci_read_config_dword(pdev, pos + PCI_REBAR_CAP, &cap);
|
||||
cap &= PCI_REBAR_CAP_SIZES;
|
||||
cap = FIELD_GET(PCI_REBAR_CAP_SIZES, cap);
|
||||
|
||||
/* Sapphire RX 5600 XT Pulse has an invalid cap dword for BAR 0 */
|
||||
if (pdev->vendor == PCI_VENDOR_ID_ATI && pdev->device == 0x731f &&
|
||||
bar == 0 && cap == 0x7000)
|
||||
cap = 0x3f000;
|
||||
bar == 0 && cap == 0x700)
|
||||
return 0x3f00;
|
||||
|
||||
return cap >> 4;
|
||||
return cap;
|
||||
}
|
||||
EXPORT_SYMBOL(pci_rebar_get_possible_sizes);
|
||||
|
||||
@@ -3781,7 +3782,7 @@ int pci_rebar_get_current_size(struct pci_dev *pdev, int bar)
|
||||
return pos;
|
||||
|
||||
pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
|
||||
return (ctrl & PCI_REBAR_CTRL_BAR_SIZE) >> PCI_REBAR_CTRL_BAR_SHIFT;
|
||||
return FIELD_GET(PCI_REBAR_CTRL_BAR_SIZE, ctrl);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3804,7 +3805,7 @@ int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size)
|
||||
|
||||
pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl);
|
||||
ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE;
|
||||
ctrl |= size << PCI_REBAR_CTRL_BAR_SHIFT;
|
||||
ctrl |= FIELD_PREP(PCI_REBAR_CTRL_BAR_SIZE, size);
|
||||
pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl);
|
||||
return 0;
|
||||
}
|
||||
@@ -6042,7 +6043,7 @@ int pcix_get_max_mmrbc(struct pci_dev *dev)
|
||||
if (pci_read_config_dword(dev, cap + PCI_X_STATUS, &stat))
|
||||
return -EINVAL;
|
||||
|
||||
return 512 << ((stat & PCI_X_STATUS_MAX_READ) >> 21);
|
||||
return 512 << FIELD_GET(PCI_X_STATUS_MAX_READ, stat);
|
||||
}
|
||||
EXPORT_SYMBOL(pcix_get_max_mmrbc);
|
||||
|
||||
@@ -6065,7 +6066,7 @@ int pcix_get_mmrbc(struct pci_dev *dev)
|
||||
if (pci_read_config_word(dev, cap + PCI_X_CMD, &cmd))
|
||||
return -EINVAL;
|
||||
|
||||
return 512 << ((cmd & PCI_X_CMD_MAX_READ) >> 2);
|
||||
return 512 << FIELD_GET(PCI_X_CMD_MAX_READ, cmd);
|
||||
}
|
||||
EXPORT_SYMBOL(pcix_get_mmrbc);
|
||||
|
||||
@@ -6096,19 +6097,19 @@ int pcix_set_mmrbc(struct pci_dev *dev, int mmrbc)
|
||||
if (pci_read_config_dword(dev, cap + PCI_X_STATUS, &stat))
|
||||
return -EINVAL;
|
||||
|
||||
if (v > (stat & PCI_X_STATUS_MAX_READ) >> 21)
|
||||
if (v > FIELD_GET(PCI_X_STATUS_MAX_READ, stat))
|
||||
return -E2BIG;
|
||||
|
||||
if (pci_read_config_word(dev, cap + PCI_X_CMD, &cmd))
|
||||
return -EINVAL;
|
||||
|
||||
o = (cmd & PCI_X_CMD_MAX_READ) >> 2;
|
||||
o = FIELD_GET(PCI_X_CMD_MAX_READ, cmd);
|
||||
if (o != v) {
|
||||
if (v > o && (dev->bus->bus_flags & PCI_BUS_FLAGS_NO_MMRBC))
|
||||
return -EIO;
|
||||
|
||||
cmd &= ~PCI_X_CMD_MAX_READ;
|
||||
cmd |= v << 2;
|
||||
cmd |= FIELD_PREP(PCI_X_CMD_MAX_READ, v);
|
||||
if (pci_write_config_word(dev, cap + PCI_X_CMD, cmd))
|
||||
return -EIO;
|
||||
}
|
||||
@@ -6128,7 +6129,7 @@ int pcie_get_readrq(struct pci_dev *dev)
|
||||
|
||||
pcie_capability_read_word(dev, PCI_EXP_DEVCTL, &ctl);
|
||||
|
||||
return 128 << ((ctl & PCI_EXP_DEVCTL_READRQ) >> 12);
|
||||
return 128 << FIELD_GET(PCI_EXP_DEVCTL_READRQ, ctl);
|
||||
}
|
||||
EXPORT_SYMBOL(pcie_get_readrq);
|
||||
|
||||
@@ -6161,7 +6162,7 @@ int pcie_set_readrq(struct pci_dev *dev, int rq)
|
||||
rq = mps;
|
||||
}
|
||||
|
||||
v = (ffs(rq) - 8) << 12;
|
||||
v = FIELD_PREP(PCI_EXP_DEVCTL_READRQ, ffs(rq) - 8);
|
||||
|
||||
if (bridge->no_inc_mrrs) {
|
||||
int max_mrrs = pcie_get_readrq(dev);
|
||||
@@ -6191,7 +6192,7 @@ int pcie_get_mps(struct pci_dev *dev)
|
||||
|
||||
pcie_capability_read_word(dev, PCI_EXP_DEVCTL, &ctl);
|
||||
|
||||
return 128 << ((ctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5);
|
||||
return 128 << FIELD_GET(PCI_EXP_DEVCTL_PAYLOAD, ctl);
|
||||
}
|
||||
EXPORT_SYMBOL(pcie_get_mps);
|
||||
|
||||
@@ -6214,7 +6215,7 @@ int pcie_set_mps(struct pci_dev *dev, int mps)
|
||||
v = ffs(mps) - 8;
|
||||
if (v > dev->pcie_mpss)
|
||||
return -EINVAL;
|
||||
v <<= 5;
|
||||
v = FIELD_PREP(PCI_EXP_DEVCTL_PAYLOAD, v);
|
||||
|
||||
ret = pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL,
|
||||
PCI_EXP_DEVCTL_PAYLOAD, v);
|
||||
@@ -6256,9 +6257,9 @@ u32 pcie_bandwidth_available(struct pci_dev *dev, struct pci_dev **limiting_dev,
|
||||
while (dev) {
|
||||
pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta);
|
||||
|
||||
next_speed = pcie_link_speed[lnksta & PCI_EXP_LNKSTA_CLS];
|
||||
next_width = (lnksta & PCI_EXP_LNKSTA_NLW) >>
|
||||
PCI_EXP_LNKSTA_NLW_SHIFT;
|
||||
next_speed = pcie_link_speed[FIELD_GET(PCI_EXP_LNKSTA_CLS,
|
||||
lnksta)];
|
||||
next_width = FIELD_GET(PCI_EXP_LNKSTA_NLW, lnksta);
|
||||
|
||||
next_bw = next_width * PCIE_SPEED2MBS_ENC(next_speed);
|
||||
|
||||
@@ -6330,7 +6331,7 @@ enum pcie_link_width pcie_get_width_cap(struct pci_dev *dev)
|
||||
|
||||
pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &lnkcap);
|
||||
if (lnkcap)
|
||||
return (lnkcap & PCI_EXP_LNKCAP_MLW) >> 4;
|
||||
return FIELD_GET(PCI_EXP_LNKCAP_MLW, lnkcap);
|
||||
|
||||
return PCIE_LNK_WIDTH_UNKNOWN;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
|
||||
#define PCIE_LINK_RETRAIN_TIMEOUT_MS 1000
|
||||
|
||||
/* Power stable to PERST# inactive from PCIe card Electromechanical Spec */
|
||||
#define PCIE_T_PVPERL_MS 100
|
||||
|
||||
/*
|
||||
* PCIe r6.0, sec 5.3.3.2.1 <PME Synchronization>
|
||||
* Recommends 1ms to 10ms timeout to check L2 ready.
|
||||
|
||||
+27
-18
@@ -1224,6 +1224,28 @@ static irqreturn_t aer_irq(int irq, void *context)
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
static void aer_enable_irq(struct pci_dev *pdev)
|
||||
{
|
||||
int aer = pdev->aer_cap;
|
||||
u32 reg32;
|
||||
|
||||
/* Enable Root Port's interrupt in response to error messages */
|
||||
pci_read_config_dword(pdev, aer + PCI_ERR_ROOT_COMMAND, ®32);
|
||||
reg32 |= ROOT_PORT_INTR_ON_MESG_MASK;
|
||||
pci_write_config_dword(pdev, aer + PCI_ERR_ROOT_COMMAND, reg32);
|
||||
}
|
||||
|
||||
static void aer_disable_irq(struct pci_dev *pdev)
|
||||
{
|
||||
int aer = pdev->aer_cap;
|
||||
u32 reg32;
|
||||
|
||||
/* Disable Root's interrupt in response to error messages */
|
||||
pci_read_config_dword(pdev, aer + PCI_ERR_ROOT_COMMAND, ®32);
|
||||
reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK;
|
||||
pci_write_config_dword(pdev, aer + PCI_ERR_ROOT_COMMAND, reg32);
|
||||
}
|
||||
|
||||
/**
|
||||
* aer_enable_rootport - enable Root Port's interrupts when receiving messages
|
||||
* @rpc: pointer to a Root Port data structure
|
||||
@@ -1253,10 +1275,7 @@ static void aer_enable_rootport(struct aer_rpc *rpc)
|
||||
pci_read_config_dword(pdev, aer + PCI_ERR_UNCOR_STATUS, ®32);
|
||||
pci_write_config_dword(pdev, aer + PCI_ERR_UNCOR_STATUS, reg32);
|
||||
|
||||
/* Enable Root Port's interrupt in response to error messages */
|
||||
pci_read_config_dword(pdev, aer + PCI_ERR_ROOT_COMMAND, ®32);
|
||||
reg32 |= ROOT_PORT_INTR_ON_MESG_MASK;
|
||||
pci_write_config_dword(pdev, aer + PCI_ERR_ROOT_COMMAND, reg32);
|
||||
aer_enable_irq(pdev);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1271,10 +1290,7 @@ static void aer_disable_rootport(struct aer_rpc *rpc)
|
||||
int aer = pdev->aer_cap;
|
||||
u32 reg32;
|
||||
|
||||
/* Disable Root's interrupt in response to error messages */
|
||||
pci_read_config_dword(pdev, aer + PCI_ERR_ROOT_COMMAND, ®32);
|
||||
reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK;
|
||||
pci_write_config_dword(pdev, aer + PCI_ERR_ROOT_COMMAND, reg32);
|
||||
aer_disable_irq(pdev);
|
||||
|
||||
/* Clear Root's error status reg */
|
||||
pci_read_config_dword(pdev, aer + PCI_ERR_ROOT_STATUS, ®32);
|
||||
@@ -1369,12 +1385,8 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
|
||||
*/
|
||||
aer = root ? root->aer_cap : 0;
|
||||
|
||||
if ((host->native_aer || pcie_ports_native) && aer) {
|
||||
/* Disable Root's interrupt in response to error messages */
|
||||
pci_read_config_dword(root, aer + PCI_ERR_ROOT_COMMAND, ®32);
|
||||
reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK;
|
||||
pci_write_config_dword(root, aer + PCI_ERR_ROOT_COMMAND, reg32);
|
||||
}
|
||||
if ((host->native_aer || pcie_ports_native) && aer)
|
||||
aer_disable_irq(root);
|
||||
|
||||
if (type == PCI_EXP_TYPE_RC_EC || type == PCI_EXP_TYPE_RC_END) {
|
||||
rc = pcie_reset_flr(dev, PCI_RESET_DO_RESET);
|
||||
@@ -1393,10 +1405,7 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
|
||||
pci_read_config_dword(root, aer + PCI_ERR_ROOT_STATUS, ®32);
|
||||
pci_write_config_dword(root, aer + PCI_ERR_ROOT_STATUS, reg32);
|
||||
|
||||
/* Enable Root Port's interrupt in response to error messages */
|
||||
pci_read_config_dword(root, aer + PCI_ERR_ROOT_COMMAND, ®32);
|
||||
reg32 |= ROOT_PORT_INTR_ON_MESG_MASK;
|
||||
pci_write_config_dword(root, aer + PCI_ERR_ROOT_COMMAND, reg32);
|
||||
aer_enable_irq(root);
|
||||
}
|
||||
|
||||
return rc ? PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_RECOVERED;
|
||||
|
||||
+47
-37
@@ -7,7 +7,9 @@
|
||||
* Copyright (C) Shaohua Li (shaohua.li@intel.com)
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/math.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
@@ -16,9 +18,10 @@
|
||||
#include <linux/errno.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/time.h>
|
||||
|
||||
#include "../pci.h"
|
||||
|
||||
#ifdef MODULE_PARAM_PREFIX
|
||||
@@ -267,10 +270,10 @@ static void pcie_aspm_configure_common_clock(struct pcie_link_state *link)
|
||||
/* Convert L0s latency encoding to ns */
|
||||
static u32 calc_l0s_latency(u32 lnkcap)
|
||||
{
|
||||
u32 encoding = (lnkcap & PCI_EXP_LNKCAP_L0SEL) >> 12;
|
||||
u32 encoding = FIELD_GET(PCI_EXP_LNKCAP_L0SEL, lnkcap);
|
||||
|
||||
if (encoding == 0x7)
|
||||
return (5 * 1000); /* > 4us */
|
||||
return 5 * NSEC_PER_USEC; /* > 4us */
|
||||
return (64 << encoding);
|
||||
}
|
||||
|
||||
@@ -278,26 +281,26 @@ static u32 calc_l0s_latency(u32 lnkcap)
|
||||
static u32 calc_l0s_acceptable(u32 encoding)
|
||||
{
|
||||
if (encoding == 0x7)
|
||||
return -1U;
|
||||
return U32_MAX;
|
||||
return (64 << encoding);
|
||||
}
|
||||
|
||||
/* Convert L1 latency encoding to ns */
|
||||
static u32 calc_l1_latency(u32 lnkcap)
|
||||
{
|
||||
u32 encoding = (lnkcap & PCI_EXP_LNKCAP_L1EL) >> 15;
|
||||
u32 encoding = FIELD_GET(PCI_EXP_LNKCAP_L1EL, lnkcap);
|
||||
|
||||
if (encoding == 0x7)
|
||||
return (65 * 1000); /* > 64us */
|
||||
return (1000 << encoding);
|
||||
return 65 * NSEC_PER_USEC; /* > 64us */
|
||||
return NSEC_PER_USEC << encoding;
|
||||
}
|
||||
|
||||
/* Convert L1 acceptable latency encoding to ns */
|
||||
static u32 calc_l1_acceptable(u32 encoding)
|
||||
{
|
||||
if (encoding == 0x7)
|
||||
return -1U;
|
||||
return (1000 << encoding);
|
||||
return U32_MAX;
|
||||
return NSEC_PER_USEC << encoding;
|
||||
}
|
||||
|
||||
/* Convert L1SS T_pwr encoding to usec */
|
||||
@@ -325,33 +328,33 @@ static u32 calc_l12_pwron(struct pci_dev *pdev, u32 scale, u32 val)
|
||||
*/
|
||||
static void encode_l12_threshold(u32 threshold_us, u32 *scale, u32 *value)
|
||||
{
|
||||
u64 threshold_ns = (u64) threshold_us * 1000;
|
||||
u64 threshold_ns = (u64)threshold_us * NSEC_PER_USEC;
|
||||
|
||||
/*
|
||||
* LTR_L1.2_THRESHOLD_Value ("value") is a 10-bit field with max
|
||||
* value of 0x3ff.
|
||||
*/
|
||||
if (threshold_ns <= 0x3ff * 1) {
|
||||
if (threshold_ns <= 1 * FIELD_MAX(PCI_L1SS_CTL1_LTR_L12_TH_VALUE)) {
|
||||
*scale = 0; /* Value times 1ns */
|
||||
*value = threshold_ns;
|
||||
} else if (threshold_ns <= 0x3ff * 32) {
|
||||
} else if (threshold_ns <= 32 * FIELD_MAX(PCI_L1SS_CTL1_LTR_L12_TH_VALUE)) {
|
||||
*scale = 1; /* Value times 32ns */
|
||||
*value = roundup(threshold_ns, 32) / 32;
|
||||
} else if (threshold_ns <= 0x3ff * 1024) {
|
||||
} else if (threshold_ns <= 1024 * FIELD_MAX(PCI_L1SS_CTL1_LTR_L12_TH_VALUE)) {
|
||||
*scale = 2; /* Value times 1024ns */
|
||||
*value = roundup(threshold_ns, 1024) / 1024;
|
||||
} else if (threshold_ns <= 0x3ff * 32768) {
|
||||
} else if (threshold_ns <= 32768 * FIELD_MAX(PCI_L1SS_CTL1_LTR_L12_TH_VALUE)) {
|
||||
*scale = 3; /* Value times 32768ns */
|
||||
*value = roundup(threshold_ns, 32768) / 32768;
|
||||
} else if (threshold_ns <= 0x3ff * 1048576) {
|
||||
} else if (threshold_ns <= 1048576 * FIELD_MAX(PCI_L1SS_CTL1_LTR_L12_TH_VALUE)) {
|
||||
*scale = 4; /* Value times 1048576ns */
|
||||
*value = roundup(threshold_ns, 1048576) / 1048576;
|
||||
} else if (threshold_ns <= 0x3ff * (u64) 33554432) {
|
||||
} else if (threshold_ns <= (u64)33554432 * FIELD_MAX(PCI_L1SS_CTL1_LTR_L12_TH_VALUE)) {
|
||||
*scale = 5; /* Value times 33554432ns */
|
||||
*value = roundup(threshold_ns, 33554432) / 33554432;
|
||||
} else {
|
||||
*scale = 5;
|
||||
*value = 0x3ff; /* Max representable value */
|
||||
*value = FIELD_MAX(PCI_L1SS_CTL1_LTR_L12_TH_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,11 +374,11 @@ static void pcie_aspm_check_latency(struct pci_dev *endpoint)
|
||||
link = endpoint->bus->self->link_state;
|
||||
|
||||
/* Calculate endpoint L0s acceptable latency */
|
||||
encoding = (endpoint->devcap & PCI_EXP_DEVCAP_L0S) >> 6;
|
||||
encoding = FIELD_GET(PCI_EXP_DEVCAP_L0S, endpoint->devcap);
|
||||
acceptable_l0s = calc_l0s_acceptable(encoding);
|
||||
|
||||
/* Calculate endpoint L1 acceptable latency */
|
||||
encoding = (endpoint->devcap & PCI_EXP_DEVCAP_L1) >> 9;
|
||||
encoding = FIELD_GET(PCI_EXP_DEVCAP_L1, endpoint->devcap);
|
||||
acceptable_l1 = calc_l1_acceptable(encoding);
|
||||
|
||||
while (link) {
|
||||
@@ -417,7 +420,7 @@ static void pcie_aspm_check_latency(struct pci_dev *endpoint)
|
||||
if ((link->aspm_capable & ASPM_STATE_L1) &&
|
||||
(latency + l1_switch_latency > acceptable_l1))
|
||||
link->aspm_capable &= ~ASPM_STATE_L1;
|
||||
l1_switch_latency += 1000;
|
||||
l1_switch_latency += NSEC_PER_USEC;
|
||||
|
||||
link = link->parent;
|
||||
}
|
||||
@@ -446,22 +449,24 @@ static void aspm_calc_l12_info(struct pcie_link_state *link,
|
||||
u32 pl1_2_enables, cl1_2_enables;
|
||||
|
||||
/* Choose the greater of the two Port Common_Mode_Restore_Times */
|
||||
val1 = (parent_l1ss_cap & PCI_L1SS_CAP_CM_RESTORE_TIME) >> 8;
|
||||
val2 = (child_l1ss_cap & PCI_L1SS_CAP_CM_RESTORE_TIME) >> 8;
|
||||
val1 = FIELD_GET(PCI_L1SS_CAP_CM_RESTORE_TIME, parent_l1ss_cap);
|
||||
val2 = FIELD_GET(PCI_L1SS_CAP_CM_RESTORE_TIME, child_l1ss_cap);
|
||||
t_common_mode = max(val1, val2);
|
||||
|
||||
/* Choose the greater of the two Port T_POWER_ON times */
|
||||
val1 = (parent_l1ss_cap & PCI_L1SS_CAP_P_PWR_ON_VALUE) >> 19;
|
||||
scale1 = (parent_l1ss_cap & PCI_L1SS_CAP_P_PWR_ON_SCALE) >> 16;
|
||||
val2 = (child_l1ss_cap & PCI_L1SS_CAP_P_PWR_ON_VALUE) >> 19;
|
||||
scale2 = (child_l1ss_cap & PCI_L1SS_CAP_P_PWR_ON_SCALE) >> 16;
|
||||
val1 = FIELD_GET(PCI_L1SS_CAP_P_PWR_ON_VALUE, parent_l1ss_cap);
|
||||
scale1 = FIELD_GET(PCI_L1SS_CAP_P_PWR_ON_SCALE, parent_l1ss_cap);
|
||||
val2 = FIELD_GET(PCI_L1SS_CAP_P_PWR_ON_VALUE, child_l1ss_cap);
|
||||
scale2 = FIELD_GET(PCI_L1SS_CAP_P_PWR_ON_SCALE, child_l1ss_cap);
|
||||
|
||||
if (calc_l12_pwron(parent, scale1, val1) >
|
||||
calc_l12_pwron(child, scale2, val2)) {
|
||||
ctl2 |= scale1 | (val1 << 3);
|
||||
ctl2 |= FIELD_PREP(PCI_L1SS_CTL2_T_PWR_ON_SCALE, scale1) |
|
||||
FIELD_PREP(PCI_L1SS_CTL2_T_PWR_ON_VALUE, val1);
|
||||
t_power_on = calc_l12_pwron(parent, scale1, val1);
|
||||
} else {
|
||||
ctl2 |= scale2 | (val2 << 3);
|
||||
ctl2 |= FIELD_PREP(PCI_L1SS_CTL2_T_PWR_ON_SCALE, scale2) |
|
||||
FIELD_PREP(PCI_L1SS_CTL2_T_PWR_ON_VALUE, val2);
|
||||
t_power_on = calc_l12_pwron(child, scale2, val2);
|
||||
}
|
||||
|
||||
@@ -477,7 +482,9 @@ static void aspm_calc_l12_info(struct pcie_link_state *link,
|
||||
*/
|
||||
l1_2_threshold = 2 + 4 + t_common_mode + t_power_on;
|
||||
encode_l12_threshold(l1_2_threshold, &scale, &value);
|
||||
ctl1 |= t_common_mode << 8 | scale << 29 | value << 16;
|
||||
ctl1 |= FIELD_PREP(PCI_L1SS_CTL1_CM_RESTORE_TIME, t_common_mode) |
|
||||
FIELD_PREP(PCI_L1SS_CTL1_LTR_L12_TH_VALUE, value) |
|
||||
FIELD_PREP(PCI_L1SS_CTL1_LTR_L12_TH_SCALE, scale);
|
||||
|
||||
/* Some broken devices only support dword access to L1 SS */
|
||||
pci_read_config_dword(parent, parent->l1ss + PCI_L1SS_CTL1, &pctl1);
|
||||
@@ -689,10 +696,10 @@ static void pcie_config_aspm_l1ss(struct pcie_link_state *link, u32 state)
|
||||
* in pcie_config_aspm_link().
|
||||
*/
|
||||
if (enable_req & (ASPM_STATE_L1_1 | ASPM_STATE_L1_2)) {
|
||||
pcie_capability_clear_and_set_word(child, PCI_EXP_LNKCTL,
|
||||
PCI_EXP_LNKCTL_ASPM_L1, 0);
|
||||
pcie_capability_clear_and_set_word(parent, PCI_EXP_LNKCTL,
|
||||
PCI_EXP_LNKCTL_ASPM_L1, 0);
|
||||
pcie_capability_clear_word(child, PCI_EXP_LNKCTL,
|
||||
PCI_EXP_LNKCTL_ASPM_L1);
|
||||
pcie_capability_clear_word(parent, PCI_EXP_LNKCTL,
|
||||
PCI_EXP_LNKCTL_ASPM_L1);
|
||||
}
|
||||
|
||||
val = 0;
|
||||
@@ -1059,7 +1066,8 @@ static int __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem)
|
||||
if (state & PCIE_LINK_STATE_L0S)
|
||||
link->aspm_disable |= ASPM_STATE_L0S;
|
||||
if (state & PCIE_LINK_STATE_L1)
|
||||
link->aspm_disable |= ASPM_STATE_L1;
|
||||
/* L1 PM substates require L1 */
|
||||
link->aspm_disable |= ASPM_STATE_L1 | ASPM_STATE_L1SS;
|
||||
if (state & PCIE_LINK_STATE_L1_1)
|
||||
link->aspm_disable |= ASPM_STATE_L1_1;
|
||||
if (state & PCIE_LINK_STATE_L1_2)
|
||||
@@ -1247,6 +1255,8 @@ static ssize_t aspm_attr_store_common(struct device *dev,
|
||||
link->aspm_disable &= ~ASPM_STATE_L1;
|
||||
} else {
|
||||
link->aspm_disable |= state;
|
||||
if (state & ASPM_STATE_L1)
|
||||
link->aspm_disable |= ASPM_STATE_L1SS;
|
||||
}
|
||||
|
||||
pcie_config_aspm_link(link, policy_to_aspm_state(link));
|
||||
@@ -1361,10 +1371,10 @@ static int __init pcie_aspm_disable(char *str)
|
||||
aspm_policy = POLICY_DEFAULT;
|
||||
aspm_disabled = 1;
|
||||
aspm_support_enabled = false;
|
||||
printk(KERN_INFO "PCIe ASPM is disabled\n");
|
||||
pr_info("PCIe ASPM is disabled\n");
|
||||
} else if (!strcmp(str, "force")) {
|
||||
aspm_force = 1;
|
||||
printk(KERN_INFO "PCIe ASPM is forcibly enabled\n");
|
||||
pr_info("PCIe ASPM is forcibly enabled\n");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
+27
-15
@@ -9,6 +9,7 @@
|
||||
#define dev_fmt(fmt) "DPC: " fmt
|
||||
|
||||
#include <linux/aer.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/init.h>
|
||||
@@ -17,6 +18,9 @@
|
||||
#include "portdrv.h"
|
||||
#include "../pci.h"
|
||||
|
||||
#define PCI_EXP_DPC_CTL_EN_MASK (PCI_EXP_DPC_CTL_EN_FATAL | \
|
||||
PCI_EXP_DPC_CTL_EN_NONFATAL)
|
||||
|
||||
static const char * const rp_pio_error_string[] = {
|
||||
"Configuration Request received UR Completion", /* Bit Position 0 */
|
||||
"Configuration Request received CA Completion", /* Bit Position 1 */
|
||||
@@ -202,7 +206,7 @@ static void dpc_process_rp_pio_error(struct pci_dev *pdev)
|
||||
|
||||
/* Get First Error Pointer */
|
||||
pci_read_config_word(pdev, cap + PCI_EXP_DPC_STATUS, &dpc_status);
|
||||
first_error = (dpc_status & 0x1f00) >> 8;
|
||||
first_error = FIELD_GET(PCI_EXP_DPC_RP_PIO_FEP, dpc_status);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(rp_pio_error_string); i++) {
|
||||
if ((status & ~mask) & (1 << i))
|
||||
@@ -270,20 +274,27 @@ void dpc_process_error(struct pci_dev *pdev)
|
||||
pci_info(pdev, "containment event, status:%#06x source:%#06x\n",
|
||||
status, source);
|
||||
|
||||
reason = (status & PCI_EXP_DPC_STATUS_TRIGGER_RSN) >> 1;
|
||||
ext_reason = (status & PCI_EXP_DPC_STATUS_TRIGGER_RSN_EXT) >> 5;
|
||||
reason = status & PCI_EXP_DPC_STATUS_TRIGGER_RSN;
|
||||
ext_reason = status & PCI_EXP_DPC_STATUS_TRIGGER_RSN_EXT;
|
||||
pci_warn(pdev, "%s detected\n",
|
||||
(reason == 0) ? "unmasked uncorrectable error" :
|
||||
(reason == 1) ? "ERR_NONFATAL" :
|
||||
(reason == 2) ? "ERR_FATAL" :
|
||||
(ext_reason == 0) ? "RP PIO error" :
|
||||
(ext_reason == 1) ? "software trigger" :
|
||||
"reserved error");
|
||||
(reason == PCI_EXP_DPC_STATUS_TRIGGER_RSN_UNCOR) ?
|
||||
"unmasked uncorrectable error" :
|
||||
(reason == PCI_EXP_DPC_STATUS_TRIGGER_RSN_NFE) ?
|
||||
"ERR_NONFATAL" :
|
||||
(reason == PCI_EXP_DPC_STATUS_TRIGGER_RSN_FE) ?
|
||||
"ERR_FATAL" :
|
||||
(ext_reason == PCI_EXP_DPC_STATUS_TRIGGER_RSN_RP_PIO) ?
|
||||
"RP PIO error" :
|
||||
(ext_reason == PCI_EXP_DPC_STATUS_TRIGGER_RSN_SW_TRIGGER) ?
|
||||
"software trigger" :
|
||||
"reserved error");
|
||||
|
||||
/* show RP PIO error detail information */
|
||||
if (pdev->dpc_rp_extensions && reason == 3 && ext_reason == 0)
|
||||
if (pdev->dpc_rp_extensions &&
|
||||
reason == PCI_EXP_DPC_STATUS_TRIGGER_RSN_IN_EXT &&
|
||||
ext_reason == PCI_EXP_DPC_STATUS_TRIGGER_RSN_RP_PIO)
|
||||
dpc_process_rp_pio_error(pdev);
|
||||
else if (reason == 0 &&
|
||||
else if (reason == PCI_EXP_DPC_STATUS_TRIGGER_RSN_UNCOR &&
|
||||
dpc_get_aer_uncorrect_severity(pdev, &info) &&
|
||||
aer_get_device_error_info(pdev, &info)) {
|
||||
aer_print_error(pdev, &info);
|
||||
@@ -338,7 +349,7 @@ void pci_dpc_init(struct pci_dev *pdev)
|
||||
/* Quirks may set dpc_rp_log_size if device or firmware is buggy */
|
||||
if (!pdev->dpc_rp_log_size) {
|
||||
pdev->dpc_rp_log_size =
|
||||
(cap & PCI_EXP_DPC_RP_PIO_LOG_SIZE) >> 8;
|
||||
FIELD_GET(PCI_EXP_DPC_RP_PIO_LOG_SIZE, cap);
|
||||
if (pdev->dpc_rp_log_size < 4 || pdev->dpc_rp_log_size > 9) {
|
||||
pci_err(pdev, "RP PIO log size %u is invalid\n",
|
||||
pdev->dpc_rp_log_size);
|
||||
@@ -368,12 +379,13 @@ static int dpc_probe(struct pcie_device *dev)
|
||||
}
|
||||
|
||||
pci_read_config_word(pdev, pdev->dpc_cap + PCI_EXP_DPC_CAP, &cap);
|
||||
|
||||
pci_read_config_word(pdev, pdev->dpc_cap + PCI_EXP_DPC_CTL, &ctl);
|
||||
|
||||
ctl = (ctl & 0xfff4) | PCI_EXP_DPC_CTL_EN_FATAL | PCI_EXP_DPC_CTL_INT_EN;
|
||||
ctl &= ~PCI_EXP_DPC_CTL_EN_MASK;
|
||||
ctl |= PCI_EXP_DPC_CTL_EN_FATAL | PCI_EXP_DPC_CTL_INT_EN;
|
||||
pci_write_config_word(pdev, pdev->dpc_cap + PCI_EXP_DPC_CTL, ctl);
|
||||
pci_info(pdev, "enabled with IRQ %d\n", dev->irq);
|
||||
|
||||
pci_info(pdev, "enabled with IRQ %d\n", dev->irq);
|
||||
pci_info(pdev, "error containment capabilities: Int Msg #%d, RPExt%c PoisonedTLP%c SwTrigger%c RP PIO Log %d, DL_ActiveErr%c\n",
|
||||
cap & PCI_EXP_DPC_IRQ, FLAG(cap, PCI_EXP_DPC_CAP_RP_EXT),
|
||||
FLAG(cap, PCI_EXP_DPC_CAP_POISONED_TLP),
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#define dev_fmt(fmt) "PME: " fmt
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
@@ -235,7 +236,8 @@ static void pcie_pme_work_fn(struct work_struct *work)
|
||||
pcie_clear_root_pme_status(port);
|
||||
|
||||
spin_unlock_irq(&data->lock);
|
||||
pcie_pme_handle_request(port, rtsta & 0xffff);
|
||||
pcie_pme_handle_request(port,
|
||||
FIELD_GET(PCI_EXP_RTSTA_PME_RQ_ID, rtsta));
|
||||
spin_lock_irq(&data->lock);
|
||||
|
||||
continue;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
* Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
@@ -69,7 +70,7 @@ static int pcie_message_numbers(struct pci_dev *dev, int mask,
|
||||
if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP |
|
||||
PCIE_PORT_SERVICE_BWNOTIF)) {
|
||||
pcie_capability_read_word(dev, PCI_EXP_FLAGS, ®16);
|
||||
*pme = (reg16 & PCI_EXP_FLAGS_IRQ) >> 9;
|
||||
*pme = FIELD_GET(PCI_EXP_FLAGS_IRQ, reg16);
|
||||
nvec = *pme + 1;
|
||||
}
|
||||
|
||||
@@ -81,7 +82,7 @@ static int pcie_message_numbers(struct pci_dev *dev, int mask,
|
||||
if (pos) {
|
||||
pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS,
|
||||
®32);
|
||||
*aer = (reg32 & PCI_ERR_ROOT_AER_IRQ) >> 27;
|
||||
*aer = FIELD_GET(PCI_ERR_ROOT_AER_IRQ, reg32);
|
||||
nvec = max(nvec, *aer + 1);
|
||||
}
|
||||
}
|
||||
@@ -92,7 +93,7 @@ static int pcie_message_numbers(struct pci_dev *dev, int mask,
|
||||
if (pos) {
|
||||
pci_read_config_word(dev, pos + PCI_EXP_DPC_CAP,
|
||||
®16);
|
||||
*dpc = reg16 & PCI_EXP_DPC_IRQ;
|
||||
*dpc = FIELD_GET(PCI_EXP_DPC_IRQ, reg16);
|
||||
nvec = max(nvec, *dpc + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* Copyright (c) 2016, Intel Corporation.
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/pci.h>
|
||||
@@ -53,7 +54,7 @@ void pci_ptm_init(struct pci_dev *dev)
|
||||
pci_add_ext_cap_save_buffer(dev, PCI_EXT_CAP_ID_PTM, sizeof(u32));
|
||||
|
||||
pci_read_config_dword(dev, ptm + PCI_PTM_CAP, &cap);
|
||||
dev->ptm_granularity = (cap & PCI_PTM_GRANULARITY_MASK) >> 8;
|
||||
dev->ptm_granularity = FIELD_GET(PCI_PTM_GRANULARITY_MASK, cap);
|
||||
|
||||
/*
|
||||
* Per the spec recommendation (PCIe r6.0, sec 7.9.15.3), select the
|
||||
@@ -146,7 +147,7 @@ static int __pci_enable_ptm(struct pci_dev *dev)
|
||||
|
||||
ctrl |= PCI_PTM_CTRL_ENABLE;
|
||||
ctrl &= ~PCI_PTM_GRANULARITY_MASK;
|
||||
ctrl |= dev->ptm_granularity << 8;
|
||||
ctrl |= FIELD_PREP(PCI_PTM_GRANULARITY_MASK, dev->ptm_granularity);
|
||||
if (dev->ptm_root)
|
||||
ctrl |= PCI_PTM_CTRL_ROOT;
|
||||
|
||||
|
||||
+7
-7
@@ -807,8 +807,8 @@ static void pci_set_bus_speed(struct pci_bus *bus)
|
||||
}
|
||||
|
||||
bus->max_bus_speed = max;
|
||||
bus->cur_bus_speed = pcix_bus_speed[
|
||||
(status & PCI_X_SSTATUS_FREQ) >> 6];
|
||||
bus->cur_bus_speed =
|
||||
pcix_bus_speed[FIELD_GET(PCI_X_SSTATUS_FREQ, status)];
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -1217,8 +1217,8 @@ static bool pci_ea_fixed_busnrs(struct pci_dev *dev, u8 *sec, u8 *sub)
|
||||
|
||||
offset = ea + PCI_EA_FIRST_ENT;
|
||||
pci_read_config_dword(dev, offset, &dw);
|
||||
ea_sec = dw & PCI_EA_SEC_BUS_MASK;
|
||||
ea_sub = (dw & PCI_EA_SUB_BUS_MASK) >> PCI_EA_SUB_BUS_SHIFT;
|
||||
ea_sec = FIELD_GET(PCI_EA_SEC_BUS_MASK, dw);
|
||||
ea_sub = FIELD_GET(PCI_EA_SUB_BUS_MASK, dw);
|
||||
if (ea_sec == 0 || ea_sub < ea_sec)
|
||||
return false;
|
||||
|
||||
@@ -1652,15 +1652,15 @@ static void pci_set_removable(struct pci_dev *dev)
|
||||
static bool pci_ext_cfg_is_aliased(struct pci_dev *dev)
|
||||
{
|
||||
#ifdef CONFIG_PCI_QUIRKS
|
||||
int pos;
|
||||
int pos, ret;
|
||||
u32 header, tmp;
|
||||
|
||||
pci_read_config_dword(dev, PCI_VENDOR_ID, &header);
|
||||
|
||||
for (pos = PCI_CFG_SPACE_SIZE;
|
||||
pos < PCI_CFG_SPACE_EXP_SIZE; pos += PCI_CFG_SPACE_SIZE) {
|
||||
if (pci_read_config_dword(dev, pos, &tmp) != PCIBIOS_SUCCESSFUL
|
||||
|| header != tmp)
|
||||
ret = pci_read_config_dword(dev, pos, &tmp);
|
||||
if ((ret != PCIBIOS_SUCCESSFUL) || (header != tmp))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+55
-20
@@ -690,7 +690,7 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RS100, quirk_ati_
|
||||
/*
|
||||
* In the AMD NL platform, this device ([1022:7912]) has a class code of
|
||||
* PCI_CLASS_SERIAL_USB_XHCI (0x0c0330), which means the xhci driver will
|
||||
* claim it.
|
||||
* claim it. The same applies on the VanGogh platform device ([1022:163a]).
|
||||
*
|
||||
* But the dwc3 driver is a more specific driver for this device, and we'd
|
||||
* prefer to use it instead of xhci. To prevent xhci from claiming the
|
||||
@@ -698,7 +698,7 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RS100, quirk_ati_
|
||||
* defines as "USB device (not host controller)". The dwc3 driver can then
|
||||
* claim it based on its Vendor and Device ID.
|
||||
*/
|
||||
static void quirk_amd_nl_class(struct pci_dev *pdev)
|
||||
static void quirk_amd_dwc_class(struct pci_dev *pdev)
|
||||
{
|
||||
u32 class = pdev->class;
|
||||
|
||||
@@ -708,7 +708,9 @@ static void quirk_amd_nl_class(struct pci_dev *pdev)
|
||||
class, pdev->class);
|
||||
}
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_NL_USB,
|
||||
quirk_amd_nl_class);
|
||||
quirk_amd_dwc_class);
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VANGOGH_USB,
|
||||
quirk_amd_dwc_class);
|
||||
|
||||
/*
|
||||
* Synopsys USB 3.x host HAPS platform has a class code of
|
||||
@@ -1844,8 +1846,8 @@ static void quirk_jmicron_ata(struct pci_dev *pdev)
|
||||
|
||||
/* Update pdev accordingly */
|
||||
pci_read_config_byte(pdev, PCI_HEADER_TYPE, &hdr);
|
||||
pdev->hdr_type = hdr & 0x7f;
|
||||
pdev->multifunction = !!(hdr & 0x80);
|
||||
pdev->hdr_type = hdr & PCI_HEADER_TYPE_MASK;
|
||||
pdev->multifunction = FIELD_GET(PCI_HEADER_TYPE_MFD, hdr);
|
||||
|
||||
pci_read_config_dword(pdev, PCI_CLASS_REVISION, &class);
|
||||
pdev->class = class >> 8;
|
||||
@@ -4553,9 +4555,9 @@ static void quirk_disable_root_port_attributes(struct pci_dev *pdev)
|
||||
|
||||
pci_info(root_port, "Disabling No Snoop/Relaxed Ordering Attributes to avoid PCIe Completion erratum in %s\n",
|
||||
dev_name(&pdev->dev));
|
||||
pcie_capability_clear_and_set_word(root_port, PCI_EXP_DEVCTL,
|
||||
PCI_EXP_DEVCTL_RELAX_EN |
|
||||
PCI_EXP_DEVCTL_NOSNOOP_EN, 0);
|
||||
pcie_capability_clear_word(root_port, PCI_EXP_DEVCTL,
|
||||
PCI_EXP_DEVCTL_RELAX_EN |
|
||||
PCI_EXP_DEVCTL_NOSNOOP_EN);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -5383,7 +5385,7 @@ int pci_dev_specific_disable_acs_redir(struct pci_dev *dev)
|
||||
*/
|
||||
static void quirk_intel_qat_vf_cap(struct pci_dev *pdev)
|
||||
{
|
||||
int pos, i = 0;
|
||||
int pos, i = 0, ret;
|
||||
u8 next_cap;
|
||||
u16 reg16, *cap;
|
||||
struct pci_cap_saved_state *state;
|
||||
@@ -5429,8 +5431,8 @@ static void quirk_intel_qat_vf_cap(struct pci_dev *pdev)
|
||||
pdev->pcie_mpss = reg16 & PCI_EXP_DEVCAP_PAYLOAD;
|
||||
|
||||
pdev->cfg_size = PCI_CFG_SPACE_EXP_SIZE;
|
||||
if (pci_read_config_dword(pdev, PCI_CFG_SPACE_SIZE, &status) !=
|
||||
PCIBIOS_SUCCESSFUL || (status == 0xffffffff))
|
||||
ret = pci_read_config_dword(pdev, PCI_CFG_SPACE_SIZE, &status);
|
||||
if ((ret != PCIBIOS_SUCCESSFUL) || (PCI_POSSIBLE_ERROR(status)))
|
||||
pdev->cfg_size = PCI_CFG_SPACE_SIZE;
|
||||
|
||||
if (pci_find_saved_cap(pdev, PCI_CAP_ID_EXP))
|
||||
@@ -5507,6 +5509,12 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, 0x0420, quirk_no_ext_tags);
|
||||
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, 0x0422, quirk_no_ext_tags);
|
||||
|
||||
#ifdef CONFIG_PCI_ATS
|
||||
static void quirk_no_ats(struct pci_dev *pdev)
|
||||
{
|
||||
pci_info(pdev, "disabling ATS\n");
|
||||
pdev->ats_cap = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Some devices require additional driver setup to enable ATS. Don't use
|
||||
* ATS for those devices as ATS will be enabled before the driver has had a
|
||||
@@ -5520,14 +5528,10 @@ static void quirk_amd_harvest_no_ats(struct pci_dev *pdev)
|
||||
(pdev->subsystem_device == 0xce19 ||
|
||||
pdev->subsystem_device == 0xcc10 ||
|
||||
pdev->subsystem_device == 0xcc08))
|
||||
goto no_ats;
|
||||
else
|
||||
return;
|
||||
quirk_no_ats(pdev);
|
||||
} else {
|
||||
quirk_no_ats(pdev);
|
||||
}
|
||||
|
||||
no_ats:
|
||||
pci_info(pdev, "disabling ATS\n");
|
||||
pdev->ats_cap = 0;
|
||||
}
|
||||
|
||||
/* AMD Stoney platform GPU */
|
||||
@@ -5550,6 +5554,25 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x7347, quirk_amd_harvest_no_ats);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x734f, quirk_amd_harvest_no_ats);
|
||||
/* AMD Raven platform iGPU */
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x15d8, quirk_amd_harvest_no_ats);
|
||||
|
||||
/*
|
||||
* Intel IPU E2000 revisions before C0 implement incorrect endianness
|
||||
* in ATS Invalidate Request message body. Disable ATS for those devices.
|
||||
*/
|
||||
static void quirk_intel_e2000_no_ats(struct pci_dev *pdev)
|
||||
{
|
||||
if (pdev->revision < 0x20)
|
||||
quirk_no_ats(pdev);
|
||||
}
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1451, quirk_intel_e2000_no_ats);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1452, quirk_intel_e2000_no_ats);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1453, quirk_intel_e2000_no_ats);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1454, quirk_intel_e2000_no_ats);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1455, quirk_intel_e2000_no_ats);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1457, quirk_intel_e2000_no_ats);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1459, quirk_intel_e2000_no_ats);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x145a, quirk_intel_e2000_no_ats);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x145c, quirk_intel_e2000_no_ats);
|
||||
#endif /* CONFIG_PCI_ATS */
|
||||
|
||||
/* Freescale PCIe doesn't support MSI in RC mode */
|
||||
@@ -5666,7 +5689,7 @@ static void quirk_nvidia_hda(struct pci_dev *gpu)
|
||||
|
||||
/* The GPU becomes a multi-function device when the HDA is enabled */
|
||||
pci_read_config_byte(gpu, PCI_HEADER_TYPE, &hdr_type);
|
||||
gpu->multifunction = !!(hdr_type & 0x80);
|
||||
gpu->multifunction = FIELD_GET(PCI_HEADER_TYPE_MFD, hdr_type);
|
||||
}
|
||||
DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID,
|
||||
PCI_BASE_CLASS_DISPLAY, 16, quirk_nvidia_hda);
|
||||
@@ -6154,7 +6177,7 @@ static void dpc_log_size(struct pci_dev *dev)
|
||||
if (!(val & PCI_EXP_DPC_CAP_RP_EXT))
|
||||
return;
|
||||
|
||||
if (!((val & PCI_EXP_DPC_RP_PIO_LOG_SIZE) >> 8)) {
|
||||
if (FIELD_GET(PCI_EXP_DPC_RP_PIO_LOG_SIZE, val) == 0) {
|
||||
pci_info(dev, "Overriding RP PIO Log Size to 4\n");
|
||||
dev->dpc_rp_log_size = 4;
|
||||
}
|
||||
@@ -6188,3 +6211,15 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9a31, dpc_log_size);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_XILINX, 0x5020, of_pci_make_dev_node);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_XILINX, 0x5021, of_pci_make_dev_node);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_REDHAT, 0x0005, of_pci_make_dev_node);
|
||||
|
||||
/*
|
||||
* Devices known to require a longer delay before first config space access
|
||||
* after reset recovery or resume from D3cold:
|
||||
*
|
||||
* VideoPropulsion (aka Genroco) Torrent QN16e MPEG QAM Modulator
|
||||
*/
|
||||
static void pci_fixup_d3cold_delay_1sec(struct pci_dev *pdev)
|
||||
{
|
||||
pdev->d3cold_delay = 1000;
|
||||
}
|
||||
DECLARE_PCI_FIXUP_FINAL(0x5555, 0x0004, pci_fixup_d3cold_delay_1sec);
|
||||
|
||||
@@ -363,6 +363,37 @@ struct pci_dev *pci_get_class(unsigned int class, struct pci_dev *from)
|
||||
}
|
||||
EXPORT_SYMBOL(pci_get_class);
|
||||
|
||||
/**
|
||||
* pci_get_base_class - searching for a PCI device by matching against the base class code only
|
||||
* @class: search for a PCI device with this base class code
|
||||
* @from: Previous PCI device found in search, or %NULL for new search.
|
||||
*
|
||||
* Iterates through the list of known PCI devices. If a PCI device is found
|
||||
* with a matching base class code, the reference count to the device is
|
||||
* incremented. See pci_match_one_device() to figure out how does this works.
|
||||
* A new search is initiated by passing %NULL as the @from argument.
|
||||
* Otherwise if @from is not %NULL, searches continue from next device on the
|
||||
* global list. The reference count for @from is always decremented if it is
|
||||
* not %NULL.
|
||||
*
|
||||
* Returns:
|
||||
* A pointer to a matched PCI device, %NULL Otherwise.
|
||||
*/
|
||||
struct pci_dev *pci_get_base_class(unsigned int class, struct pci_dev *from)
|
||||
{
|
||||
struct pci_device_id id = {
|
||||
.vendor = PCI_ANY_ID,
|
||||
.device = PCI_ANY_ID,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.class_mask = 0xFF0000,
|
||||
.class = class << 16,
|
||||
};
|
||||
|
||||
return pci_get_dev_by_id(&id, from);
|
||||
}
|
||||
EXPORT_SYMBOL(pci_get_base_class);
|
||||
|
||||
/**
|
||||
* pci_dev_present - Returns 1 if device matching the device list is present, 0 if not.
|
||||
* @ids: A pointer to a null terminated list of struct pci_device_id structures
|
||||
|
||||
@@ -2129,7 +2129,7 @@ dump:
|
||||
pci_bus_dump_resources(bus);
|
||||
}
|
||||
|
||||
void __init pci_assign_unassigned_resources(void)
|
||||
void pci_assign_unassigned_resources(void)
|
||||
{
|
||||
struct pci_bus *root_bus;
|
||||
|
||||
|
||||
+5
-4
@@ -6,6 +6,7 @@
|
||||
* Author: Alex Williamson <alex.williamson@redhat.com>
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
@@ -201,9 +202,9 @@ static int pci_vc_do_save_buffer(struct pci_dev *dev, int pos,
|
||||
/* Extended VC Count (not counting VC0) */
|
||||
evcc = cap1 & PCI_VC_CAP1_EVCC;
|
||||
/* Low Priority Extended VC Count (not counting VC0) */
|
||||
lpevcc = (cap1 & PCI_VC_CAP1_LPEVCC) >> 4;
|
||||
lpevcc = FIELD_GET(PCI_VC_CAP1_LPEVCC, cap1);
|
||||
/* Port Arbitration Table Entry Size (bits) */
|
||||
parb_size = 1 << ((cap1 & PCI_VC_CAP1_ARB_SIZE) >> 10);
|
||||
parb_size = 1 << FIELD_GET(PCI_VC_CAP1_ARB_SIZE, cap1);
|
||||
|
||||
/*
|
||||
* Port VC Control Register contains VC Arbitration Select, which
|
||||
@@ -231,7 +232,7 @@ static int pci_vc_do_save_buffer(struct pci_dev *dev, int pos,
|
||||
int vcarb_offset;
|
||||
|
||||
pci_read_config_dword(dev, pos + PCI_VC_PORT_CAP2, &cap2);
|
||||
vcarb_offset = ((cap2 & PCI_VC_CAP2_ARB_OFF) >> 24) * 16;
|
||||
vcarb_offset = FIELD_GET(PCI_VC_CAP2_ARB_OFF, cap2) * 16;
|
||||
|
||||
if (vcarb_offset) {
|
||||
int size, vcarb_phases = 0;
|
||||
@@ -277,7 +278,7 @@ static int pci_vc_do_save_buffer(struct pci_dev *dev, int pos,
|
||||
|
||||
pci_read_config_dword(dev, pos + PCI_VC_RES_CAP +
|
||||
(i * PCI_CAP_VC_PER_VC_SIZEOF), &cap);
|
||||
parb_offset = ((cap & PCI_VC_RES_CAP_ARB_OFF) >> 24) * 16;
|
||||
parb_offset = FIELD_GET(PCI_VC_RES_CAP_ARB_OFF, cap) * 16;
|
||||
if (parb_offset) {
|
||||
int size, parb_phases = 0;
|
||||
|
||||
|
||||
@@ -764,10 +764,6 @@ static bool vga_arbiter_add_pci_device(struct pci_dev *pdev)
|
||||
struct pci_dev *bridge;
|
||||
u16 cmd;
|
||||
|
||||
/* Only deal with VGA class devices */
|
||||
if ((pdev->class >> 8) != PCI_CLASS_DISPLAY_VGA)
|
||||
return false;
|
||||
|
||||
/* Allocate structure */
|
||||
vgadev = kzalloc(sizeof(struct vga_device), GFP_KERNEL);
|
||||
if (vgadev == NULL) {
|
||||
@@ -1503,6 +1499,10 @@ static int pci_notify(struct notifier_block *nb, unsigned long action,
|
||||
|
||||
vgaarb_dbg(dev, "%s\n", __func__);
|
||||
|
||||
/* Only deal with VGA class devices */
|
||||
if (!pci_is_vga(pdev))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* For now, we're only interested in devices added and removed.
|
||||
* I didn't test this thing here, so someone needs to double check
|
||||
@@ -1550,8 +1550,10 @@ static int __init vga_arb_device_init(void)
|
||||
pdev = NULL;
|
||||
while ((pdev =
|
||||
pci_get_subsys(PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
|
||||
PCI_ANY_ID, pdev)) != NULL)
|
||||
vga_arbiter_add_pci_device(pdev);
|
||||
PCI_ANY_ID, pdev)) != NULL) {
|
||||
if (pci_is_vga(pdev))
|
||||
vga_arbiter_add_pci_device(pdev);
|
||||
}
|
||||
|
||||
pr_info("loaded\n");
|
||||
return rc;
|
||||
|
||||
+8
-4
@@ -761,12 +761,14 @@ static void ipr_mask_and_clear_interrupts(struct ipr_ioa_cfg *ioa_cfg,
|
||||
static int ipr_save_pcix_cmd_reg(struct ipr_ioa_cfg *ioa_cfg)
|
||||
{
|
||||
int pcix_cmd_reg = pci_find_capability(ioa_cfg->pdev, PCI_CAP_ID_PCIX);
|
||||
int rc;
|
||||
|
||||
if (pcix_cmd_reg == 0)
|
||||
return 0;
|
||||
|
||||
if (pci_read_config_word(ioa_cfg->pdev, pcix_cmd_reg + PCI_X_CMD,
|
||||
&ioa_cfg->saved_pcix_cmd_reg) != PCIBIOS_SUCCESSFUL) {
|
||||
rc = pci_read_config_word(ioa_cfg->pdev, pcix_cmd_reg + PCI_X_CMD,
|
||||
&ioa_cfg->saved_pcix_cmd_reg);
|
||||
if (rc != PCIBIOS_SUCCESSFUL) {
|
||||
dev_err(&ioa_cfg->pdev->dev, "Failed to save PCI-X command register\n");
|
||||
return -EIO;
|
||||
}
|
||||
@@ -785,10 +787,12 @@ static int ipr_save_pcix_cmd_reg(struct ipr_ioa_cfg *ioa_cfg)
|
||||
static int ipr_set_pcix_cmd_reg(struct ipr_ioa_cfg *ioa_cfg)
|
||||
{
|
||||
int pcix_cmd_reg = pci_find_capability(ioa_cfg->pdev, PCI_CAP_ID_PCIX);
|
||||
int rc;
|
||||
|
||||
if (pcix_cmd_reg) {
|
||||
if (pci_write_config_word(ioa_cfg->pdev, pcix_cmd_reg + PCI_X_CMD,
|
||||
ioa_cfg->saved_pcix_cmd_reg) != PCIBIOS_SUCCESSFUL) {
|
||||
rc = pci_write_config_word(ioa_cfg->pdev, pcix_cmd_reg + PCI_X_CMD,
|
||||
ioa_cfg->saved_pcix_cmd_reg);
|
||||
if (rc != PCIBIOS_SUCCESSFUL) {
|
||||
dev_err(&ioa_cfg->pdev->dev, "Failed to setup PCI-X command register\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
+107
-107
@@ -81,34 +81,34 @@ ext2_last_byte(struct inode *inode, unsigned long page_nr)
|
||||
return last_byte;
|
||||
}
|
||||
|
||||
static void ext2_commit_chunk(struct page *page, loff_t pos, unsigned len)
|
||||
static void ext2_commit_chunk(struct folio *folio, loff_t pos, unsigned len)
|
||||
{
|
||||
struct address_space *mapping = page->mapping;
|
||||
struct address_space *mapping = folio->mapping;
|
||||
struct inode *dir = mapping->host;
|
||||
|
||||
inode_inc_iversion(dir);
|
||||
block_write_end(NULL, mapping, pos, len, len, page, NULL);
|
||||
block_write_end(NULL, mapping, pos, len, len, &folio->page, NULL);
|
||||
|
||||
if (pos+len > dir->i_size) {
|
||||
i_size_write(dir, pos+len);
|
||||
mark_inode_dirty(dir);
|
||||
}
|
||||
unlock_page(page);
|
||||
folio_unlock(folio);
|
||||
}
|
||||
|
||||
static bool ext2_check_page(struct page *page, int quiet, char *kaddr)
|
||||
static bool ext2_check_folio(struct folio *folio, int quiet, char *kaddr)
|
||||
{
|
||||
struct inode *dir = page->mapping->host;
|
||||
struct inode *dir = folio->mapping->host;
|
||||
struct super_block *sb = dir->i_sb;
|
||||
unsigned chunk_size = ext2_chunk_size(dir);
|
||||
u32 max_inumber = le32_to_cpu(EXT2_SB(sb)->s_es->s_inodes_count);
|
||||
unsigned offs, rec_len;
|
||||
unsigned limit = PAGE_SIZE;
|
||||
unsigned limit = folio_size(folio);
|
||||
ext2_dirent *p;
|
||||
char *error;
|
||||
|
||||
if ((dir->i_size >> PAGE_SHIFT) == page->index) {
|
||||
limit = dir->i_size & ~PAGE_MASK;
|
||||
if (dir->i_size < folio_pos(folio) + limit) {
|
||||
limit = offset_in_folio(folio, dir->i_size);
|
||||
if (limit & (chunk_size - 1))
|
||||
goto Ebadsize;
|
||||
if (!limit)
|
||||
@@ -132,7 +132,7 @@ static bool ext2_check_page(struct page *page, int quiet, char *kaddr)
|
||||
if (offs != limit)
|
||||
goto Eend;
|
||||
out:
|
||||
SetPageChecked(page);
|
||||
folio_set_checked(folio);
|
||||
return true;
|
||||
|
||||
/* Too bad, we had an error */
|
||||
@@ -160,51 +160,52 @@ Einumber:
|
||||
bad_entry:
|
||||
if (!quiet)
|
||||
ext2_error(sb, __func__, "bad entry in directory #%lu: : %s - "
|
||||
"offset=%lu, inode=%lu, rec_len=%d, name_len=%d",
|
||||
dir->i_ino, error, (page->index<<PAGE_SHIFT)+offs,
|
||||
"offset=%llu, inode=%lu, rec_len=%d, name_len=%d",
|
||||
dir->i_ino, error, folio_pos(folio) + offs,
|
||||
(unsigned long) le32_to_cpu(p->inode),
|
||||
rec_len, p->name_len);
|
||||
goto fail;
|
||||
Eend:
|
||||
if (!quiet) {
|
||||
p = (ext2_dirent *)(kaddr + offs);
|
||||
ext2_error(sb, "ext2_check_page",
|
||||
ext2_error(sb, "ext2_check_folio",
|
||||
"entry in directory #%lu spans the page boundary"
|
||||
"offset=%lu, inode=%lu",
|
||||
dir->i_ino, (page->index<<PAGE_SHIFT)+offs,
|
||||
"offset=%llu, inode=%lu",
|
||||
dir->i_ino, folio_pos(folio) + offs,
|
||||
(unsigned long) le32_to_cpu(p->inode));
|
||||
}
|
||||
fail:
|
||||
SetPageError(page);
|
||||
folio_set_error(folio);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calls to ext2_get_page()/ext2_put_page() must be nested according to the
|
||||
* rules documented in kmap_local_page()/kunmap_local().
|
||||
* Calls to ext2_get_folio()/folio_release_kmap() must be nested according
|
||||
* to the rules documented in kmap_local_folio()/kunmap_local().
|
||||
*
|
||||
* NOTE: ext2_find_entry() and ext2_dotdot() act as a call to ext2_get_page()
|
||||
* and should be treated as a call to ext2_get_page() for nesting purposes.
|
||||
* NOTE: ext2_find_entry() and ext2_dotdot() act as a call
|
||||
* to folio_release_kmap() and should be treated as a call to
|
||||
* folio_release_kmap() for nesting purposes.
|
||||
*/
|
||||
static void *ext2_get_page(struct inode *dir, unsigned long n,
|
||||
int quiet, struct page **page)
|
||||
static void *ext2_get_folio(struct inode *dir, unsigned long n,
|
||||
int quiet, struct folio **foliop)
|
||||
{
|
||||
struct address_space *mapping = dir->i_mapping;
|
||||
struct folio *folio = read_mapping_folio(mapping, n, NULL);
|
||||
void *page_addr;
|
||||
void *kaddr;
|
||||
|
||||
if (IS_ERR(folio))
|
||||
return ERR_CAST(folio);
|
||||
page_addr = kmap_local_folio(folio, n & (folio_nr_pages(folio) - 1));
|
||||
kaddr = kmap_local_folio(folio, 0);
|
||||
if (unlikely(!folio_test_checked(folio))) {
|
||||
if (!ext2_check_page(&folio->page, quiet, page_addr))
|
||||
if (!ext2_check_folio(folio, quiet, kaddr))
|
||||
goto fail;
|
||||
}
|
||||
*page = &folio->page;
|
||||
return page_addr;
|
||||
*foliop = folio;
|
||||
return kaddr;
|
||||
|
||||
fail:
|
||||
ext2_put_page(&folio->page, page_addr);
|
||||
folio_release_kmap(folio, kaddr);
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
|
||||
@@ -274,8 +275,8 @@ ext2_readdir(struct file *file, struct dir_context *ctx)
|
||||
|
||||
for ( ; n < npages; n++, offset = 0) {
|
||||
ext2_dirent *de;
|
||||
struct page *page;
|
||||
char *kaddr = ext2_get_page(inode, n, 0, &page);
|
||||
struct folio *folio;
|
||||
char *kaddr = ext2_get_folio(inode, n, 0, &folio);
|
||||
char *limit;
|
||||
|
||||
if (IS_ERR(kaddr)) {
|
||||
@@ -299,7 +300,7 @@ ext2_readdir(struct file *file, struct dir_context *ctx)
|
||||
if (de->rec_len == 0) {
|
||||
ext2_error(sb, __func__,
|
||||
"zero-length directory entry");
|
||||
ext2_put_page(page, de);
|
||||
folio_release_kmap(folio, de);
|
||||
return -EIO;
|
||||
}
|
||||
if (de->inode) {
|
||||
@@ -311,13 +312,13 @@ ext2_readdir(struct file *file, struct dir_context *ctx)
|
||||
if (!dir_emit(ctx, de->name, de->name_len,
|
||||
le32_to_cpu(de->inode),
|
||||
d_type)) {
|
||||
ext2_put_page(page, de);
|
||||
folio_release_kmap(folio, de);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
ctx->pos += ext2_rec_len_from_disk(de->rec_len);
|
||||
}
|
||||
ext2_put_page(page, kaddr);
|
||||
folio_release_kmap(folio, kaddr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -330,38 +331,35 @@ ext2_readdir(struct file *file, struct dir_context *ctx)
|
||||
* and the entry itself. Page is returned mapped and unlocked.
|
||||
* Entry is guaranteed to be valid.
|
||||
*
|
||||
* On Success ext2_put_page() should be called on *res_page.
|
||||
* On Success folio_release_kmap() should be called on *foliop.
|
||||
*
|
||||
* NOTE: Calls to ext2_get_page()/ext2_put_page() must be nested according to
|
||||
* the rules documented in kmap_local_page()/kunmap_local().
|
||||
* NOTE: Calls to ext2_get_folio()/folio_release_kmap() must be nested
|
||||
* according to the rules documented in kmap_local_folio()/kunmap_local().
|
||||
*
|
||||
* ext2_find_entry() and ext2_dotdot() act as a call to ext2_get_page() and
|
||||
* should be treated as a call to ext2_get_page() for nesting purposes.
|
||||
* ext2_find_entry() and ext2_dotdot() act as a call to ext2_get_folio()
|
||||
* and should be treated as a call to ext2_get_folio() for nesting
|
||||
* purposes.
|
||||
*/
|
||||
struct ext2_dir_entry_2 *ext2_find_entry (struct inode *dir,
|
||||
const struct qstr *child, struct page **res_page)
|
||||
const struct qstr *child, struct folio **foliop)
|
||||
{
|
||||
const char *name = child->name;
|
||||
int namelen = child->len;
|
||||
unsigned reclen = EXT2_DIR_REC_LEN(namelen);
|
||||
unsigned long start, n;
|
||||
unsigned long npages = dir_pages(dir);
|
||||
struct page *page = NULL;
|
||||
struct ext2_inode_info *ei = EXT2_I(dir);
|
||||
ext2_dirent * de;
|
||||
|
||||
if (npages == 0)
|
||||
goto out;
|
||||
|
||||
/* OFFSET_CACHE */
|
||||
*res_page = NULL;
|
||||
|
||||
start = ei->i_dir_start_lookup;
|
||||
if (start >= npages)
|
||||
start = 0;
|
||||
n = start;
|
||||
do {
|
||||
char *kaddr = ext2_get_page(dir, n, 0, &page);
|
||||
char *kaddr = ext2_get_folio(dir, n, 0, foliop);
|
||||
if (IS_ERR(kaddr))
|
||||
return ERR_CAST(kaddr);
|
||||
|
||||
@@ -371,18 +369,18 @@ struct ext2_dir_entry_2 *ext2_find_entry (struct inode *dir,
|
||||
if (de->rec_len == 0) {
|
||||
ext2_error(dir->i_sb, __func__,
|
||||
"zero-length directory entry");
|
||||
ext2_put_page(page, de);
|
||||
folio_release_kmap(*foliop, de);
|
||||
goto out;
|
||||
}
|
||||
if (ext2_match(namelen, name, de))
|
||||
goto found;
|
||||
de = ext2_next_entry(de);
|
||||
}
|
||||
ext2_put_page(page, kaddr);
|
||||
folio_release_kmap(*foliop, kaddr);
|
||||
|
||||
if (++n >= npages)
|
||||
n = 0;
|
||||
/* next page is past the blocks we've got */
|
||||
/* next folio is past the blocks we've got */
|
||||
if (unlikely(n > (dir->i_blocks >> (PAGE_SHIFT - 9)))) {
|
||||
ext2_error(dir->i_sb, __func__,
|
||||
"dir %lu size %lld exceeds block count %llu",
|
||||
@@ -395,7 +393,6 @@ out:
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
found:
|
||||
*res_page = page;
|
||||
ei->i_dir_start_lookup = n;
|
||||
return de;
|
||||
}
|
||||
@@ -404,17 +401,18 @@ found:
|
||||
* Return the '..' directory entry and the page in which the entry was found
|
||||
* (as a parameter - p).
|
||||
*
|
||||
* On Success ext2_put_page() should be called on *p.
|
||||
* On Success folio_release_kmap() should be called on *foliop.
|
||||
*
|
||||
* NOTE: Calls to ext2_get_page()/ext2_put_page() must be nested according to
|
||||
* the rules documented in kmap_local_page()/kunmap_local().
|
||||
* NOTE: Calls to ext2_get_folio()/folio_release_kmap() must be nested
|
||||
* according to the rules documented in kmap_local_folio()/kunmap_local().
|
||||
*
|
||||
* ext2_find_entry() and ext2_dotdot() act as a call to ext2_get_page() and
|
||||
* should be treated as a call to ext2_get_page() for nesting purposes.
|
||||
* ext2_find_entry() and ext2_dotdot() act as a call to ext2_get_folio()
|
||||
* and should be treated as a call to ext2_get_folio() for nesting
|
||||
* purposes.
|
||||
*/
|
||||
struct ext2_dir_entry_2 *ext2_dotdot(struct inode *dir, struct page **p)
|
||||
struct ext2_dir_entry_2 *ext2_dotdot(struct inode *dir, struct folio **foliop)
|
||||
{
|
||||
ext2_dirent *de = ext2_get_page(dir, 0, 0, p);
|
||||
ext2_dirent *de = ext2_get_folio(dir, 0, 0, foliop);
|
||||
|
||||
if (!IS_ERR(de))
|
||||
return ext2_next_entry(de);
|
||||
@@ -424,23 +422,22 @@ struct ext2_dir_entry_2 *ext2_dotdot(struct inode *dir, struct page **p)
|
||||
int ext2_inode_by_name(struct inode *dir, const struct qstr *child, ino_t *ino)
|
||||
{
|
||||
struct ext2_dir_entry_2 *de;
|
||||
struct page *page;
|
||||
|
||||
de = ext2_find_entry(dir, child, &page);
|
||||
struct folio *folio;
|
||||
|
||||
de = ext2_find_entry(dir, child, &folio);
|
||||
if (IS_ERR(de))
|
||||
return PTR_ERR(de);
|
||||
|
||||
*ino = le32_to_cpu(de->inode);
|
||||
ext2_put_page(page, de);
|
||||
folio_release_kmap(folio, de);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ext2_prepare_chunk(struct page *page, loff_t pos, unsigned len)
|
||||
static int ext2_prepare_chunk(struct folio *folio, loff_t pos, unsigned len)
|
||||
{
|
||||
return __block_write_begin(page, pos, len, ext2_get_block);
|
||||
return __block_write_begin(&folio->page, pos, len, ext2_get_block);
|
||||
}
|
||||
|
||||
|
||||
static int ext2_handle_dirsync(struct inode *dir)
|
||||
{
|
||||
int err;
|
||||
@@ -452,21 +449,21 @@ static int ext2_handle_dirsync(struct inode *dir)
|
||||
}
|
||||
|
||||
int ext2_set_link(struct inode *dir, struct ext2_dir_entry_2 *de,
|
||||
struct page *page, struct inode *inode, bool update_times)
|
||||
struct folio *folio, struct inode *inode, bool update_times)
|
||||
{
|
||||
loff_t pos = page_offset(page) + offset_in_page(de);
|
||||
loff_t pos = folio_pos(folio) + offset_in_folio(folio, de);
|
||||
unsigned len = ext2_rec_len_from_disk(de->rec_len);
|
||||
int err;
|
||||
|
||||
lock_page(page);
|
||||
err = ext2_prepare_chunk(page, pos, len);
|
||||
folio_lock(folio);
|
||||
err = ext2_prepare_chunk(folio, pos, len);
|
||||
if (err) {
|
||||
unlock_page(page);
|
||||
folio_unlock(folio);
|
||||
return err;
|
||||
}
|
||||
de->inode = cpu_to_le32(inode->i_ino);
|
||||
ext2_set_de_type(de, inode);
|
||||
ext2_commit_chunk(page, pos, len);
|
||||
ext2_commit_chunk(folio, pos, len);
|
||||
if (update_times)
|
||||
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
|
||||
EXT2_I(dir)->i_flags &= ~EXT2_BTREE_FL;
|
||||
@@ -485,7 +482,7 @@ int ext2_add_link (struct dentry *dentry, struct inode *inode)
|
||||
unsigned chunk_size = ext2_chunk_size(dir);
|
||||
unsigned reclen = EXT2_DIR_REC_LEN(namelen);
|
||||
unsigned short rec_len, name_len;
|
||||
struct page *page = NULL;
|
||||
struct folio *folio = NULL;
|
||||
ext2_dirent * de;
|
||||
unsigned long npages = dir_pages(dir);
|
||||
unsigned long n;
|
||||
@@ -494,19 +491,19 @@ int ext2_add_link (struct dentry *dentry, struct inode *inode)
|
||||
|
||||
/*
|
||||
* We take care of directory expansion in the same loop.
|
||||
* This code plays outside i_size, so it locks the page
|
||||
* This code plays outside i_size, so it locks the folio
|
||||
* to protect that region.
|
||||
*/
|
||||
for (n = 0; n <= npages; n++) {
|
||||
char *kaddr = ext2_get_page(dir, n, 0, &page);
|
||||
char *kaddr = ext2_get_folio(dir, n, 0, &folio);
|
||||
char *dir_end;
|
||||
|
||||
if (IS_ERR(kaddr))
|
||||
return PTR_ERR(kaddr);
|
||||
lock_page(page);
|
||||
folio_lock(folio);
|
||||
dir_end = kaddr + ext2_last_byte(dir, n);
|
||||
de = (ext2_dirent *)kaddr;
|
||||
kaddr += PAGE_SIZE - reclen;
|
||||
kaddr += folio_size(folio) - reclen;
|
||||
while ((char *)de <= kaddr) {
|
||||
if ((char *)de == dir_end) {
|
||||
/* We hit i_size */
|
||||
@@ -533,15 +530,15 @@ int ext2_add_link (struct dentry *dentry, struct inode *inode)
|
||||
goto got_it;
|
||||
de = (ext2_dirent *) ((char *) de + rec_len);
|
||||
}
|
||||
unlock_page(page);
|
||||
ext2_put_page(page, kaddr);
|
||||
folio_unlock(folio);
|
||||
folio_release_kmap(folio, kaddr);
|
||||
}
|
||||
BUG();
|
||||
return -EINVAL;
|
||||
|
||||
got_it:
|
||||
pos = page_offset(page) + offset_in_page(de);
|
||||
err = ext2_prepare_chunk(page, pos, rec_len);
|
||||
pos = folio_pos(folio) + offset_in_folio(folio, de);
|
||||
err = ext2_prepare_chunk(folio, pos, rec_len);
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
if (de->inode) {
|
||||
@@ -554,17 +551,17 @@ got_it:
|
||||
memcpy(de->name, name, namelen);
|
||||
de->inode = cpu_to_le32(inode->i_ino);
|
||||
ext2_set_de_type (de, inode);
|
||||
ext2_commit_chunk(page, pos, rec_len);
|
||||
ext2_commit_chunk(folio, pos, rec_len);
|
||||
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
|
||||
EXT2_I(dir)->i_flags &= ~EXT2_BTREE_FL;
|
||||
mark_inode_dirty(dir);
|
||||
err = ext2_handle_dirsync(dir);
|
||||
/* OFFSET_CACHE */
|
||||
out_put:
|
||||
ext2_put_page(page, de);
|
||||
folio_release_kmap(folio, de);
|
||||
return err;
|
||||
out_unlock:
|
||||
unlock_page(page);
|
||||
folio_unlock(folio);
|
||||
goto out_put;
|
||||
}
|
||||
|
||||
@@ -572,18 +569,21 @@ out_unlock:
|
||||
* ext2_delete_entry deletes a directory entry by merging it with the
|
||||
* previous entry. Page is up-to-date.
|
||||
*/
|
||||
int ext2_delete_entry(struct ext2_dir_entry_2 *dir, struct page *page)
|
||||
int ext2_delete_entry(struct ext2_dir_entry_2 *dir, struct folio *folio)
|
||||
{
|
||||
struct inode *inode = page->mapping->host;
|
||||
char *kaddr = (char *)((unsigned long)dir & PAGE_MASK);
|
||||
unsigned from = offset_in_page(dir) & ~(ext2_chunk_size(inode)-1);
|
||||
unsigned to = offset_in_page(dir) +
|
||||
ext2_rec_len_from_disk(dir->rec_len);
|
||||
struct inode *inode = folio->mapping->host;
|
||||
size_t from, to;
|
||||
char *kaddr;
|
||||
loff_t pos;
|
||||
ext2_dirent *pde = NULL;
|
||||
ext2_dirent *de = (ext2_dirent *)(kaddr + from);
|
||||
ext2_dirent *de, *pde = NULL;
|
||||
int err;
|
||||
|
||||
from = offset_in_folio(folio, dir);
|
||||
to = from + ext2_rec_len_from_disk(dir->rec_len);
|
||||
kaddr = (char *)dir - from;
|
||||
from &= ~(ext2_chunk_size(inode)-1);
|
||||
de = (ext2_dirent *)(kaddr + from);
|
||||
|
||||
while ((char*)de < (char*)dir) {
|
||||
if (de->rec_len == 0) {
|
||||
ext2_error(inode->i_sb, __func__,
|
||||
@@ -594,18 +594,18 @@ int ext2_delete_entry(struct ext2_dir_entry_2 *dir, struct page *page)
|
||||
de = ext2_next_entry(de);
|
||||
}
|
||||
if (pde)
|
||||
from = offset_in_page(pde);
|
||||
pos = page_offset(page) + from;
|
||||
lock_page(page);
|
||||
err = ext2_prepare_chunk(page, pos, to - from);
|
||||
from = offset_in_folio(folio, pde);
|
||||
pos = folio_pos(folio) + from;
|
||||
folio_lock(folio);
|
||||
err = ext2_prepare_chunk(folio, pos, to - from);
|
||||
if (err) {
|
||||
unlock_page(page);
|
||||
folio_unlock(folio);
|
||||
return err;
|
||||
}
|
||||
if (pde)
|
||||
pde->rec_len = ext2_rec_len_to_disk(to - from);
|
||||
dir->inode = 0;
|
||||
ext2_commit_chunk(page, pos, to - from);
|
||||
ext2_commit_chunk(folio, pos, to - from);
|
||||
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
|
||||
EXT2_I(inode)->i_flags &= ~EXT2_BTREE_FL;
|
||||
mark_inode_dirty(inode);
|
||||
@@ -617,21 +617,21 @@ int ext2_delete_entry(struct ext2_dir_entry_2 *dir, struct page *page)
|
||||
*/
|
||||
int ext2_make_empty(struct inode *inode, struct inode *parent)
|
||||
{
|
||||
struct page *page = grab_cache_page(inode->i_mapping, 0);
|
||||
struct folio *folio = filemap_grab_folio(inode->i_mapping, 0);
|
||||
unsigned chunk_size = ext2_chunk_size(inode);
|
||||
struct ext2_dir_entry_2 * de;
|
||||
int err;
|
||||
void *kaddr;
|
||||
|
||||
if (!page)
|
||||
return -ENOMEM;
|
||||
if (IS_ERR(folio))
|
||||
return PTR_ERR(folio);
|
||||
|
||||
err = ext2_prepare_chunk(page, 0, chunk_size);
|
||||
err = ext2_prepare_chunk(folio, 0, chunk_size);
|
||||
if (err) {
|
||||
unlock_page(page);
|
||||
folio_unlock(folio);
|
||||
goto fail;
|
||||
}
|
||||
kaddr = kmap_local_page(page);
|
||||
kaddr = kmap_local_folio(folio, 0);
|
||||
memset(kaddr, 0, chunk_size);
|
||||
de = (struct ext2_dir_entry_2 *)kaddr;
|
||||
de->name_len = 1;
|
||||
@@ -647,26 +647,26 @@ int ext2_make_empty(struct inode *inode, struct inode *parent)
|
||||
memcpy (de->name, "..\0", 4);
|
||||
ext2_set_de_type (de, inode);
|
||||
kunmap_local(kaddr);
|
||||
ext2_commit_chunk(page, 0, chunk_size);
|
||||
ext2_commit_chunk(folio, 0, chunk_size);
|
||||
err = ext2_handle_dirsync(inode);
|
||||
fail:
|
||||
put_page(page);
|
||||
folio_put(folio);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* routine to check that the specified directory is empty (for rmdir)
|
||||
*/
|
||||
int ext2_empty_dir (struct inode * inode)
|
||||
int ext2_empty_dir(struct inode *inode)
|
||||
{
|
||||
struct page *page;
|
||||
struct folio *folio;
|
||||
char *kaddr;
|
||||
unsigned long i, npages = dir_pages(inode);
|
||||
|
||||
for (i = 0; i < npages; i++) {
|
||||
ext2_dirent *de;
|
||||
|
||||
kaddr = ext2_get_page(inode, i, 0, &page);
|
||||
kaddr = ext2_get_folio(inode, i, 0, &folio);
|
||||
if (IS_ERR(kaddr))
|
||||
return 0;
|
||||
|
||||
@@ -695,12 +695,12 @@ int ext2_empty_dir (struct inode * inode)
|
||||
}
|
||||
de = ext2_next_entry(de);
|
||||
}
|
||||
ext2_put_page(page, kaddr);
|
||||
folio_release_kmap(folio, kaddr);
|
||||
}
|
||||
return 1;
|
||||
|
||||
not_empty:
|
||||
ext2_put_page(page, kaddr);
|
||||
folio_release_kmap(folio, kaddr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+9
-14
@@ -717,22 +717,17 @@ extern void ext2_init_block_alloc_info(struct inode *);
|
||||
extern void ext2_rsv_window_add(struct super_block *sb, struct ext2_reserve_window_node *rsv);
|
||||
|
||||
/* dir.c */
|
||||
extern int ext2_add_link (struct dentry *, struct inode *);
|
||||
extern int ext2_inode_by_name(struct inode *dir,
|
||||
int ext2_add_link(struct dentry *, struct inode *);
|
||||
int ext2_inode_by_name(struct inode *dir,
|
||||
const struct qstr *child, ino_t *ino);
|
||||
extern int ext2_make_empty(struct inode *, struct inode *);
|
||||
extern struct ext2_dir_entry_2 *ext2_find_entry(struct inode *, const struct qstr *,
|
||||
struct page **);
|
||||
extern int ext2_delete_entry(struct ext2_dir_entry_2 *dir, struct page *page);
|
||||
extern int ext2_empty_dir (struct inode *);
|
||||
extern struct ext2_dir_entry_2 *ext2_dotdot(struct inode *dir, struct page **p);
|
||||
int ext2_make_empty(struct inode *, struct inode *);
|
||||
struct ext2_dir_entry_2 *ext2_find_entry(struct inode *, const struct qstr *,
|
||||
struct folio **foliop);
|
||||
int ext2_delete_entry(struct ext2_dir_entry_2 *dir, struct folio *folio);
|
||||
int ext2_empty_dir(struct inode *);
|
||||
struct ext2_dir_entry_2 *ext2_dotdot(struct inode *dir, struct folio **foliop);
|
||||
int ext2_set_link(struct inode *dir, struct ext2_dir_entry_2 *de,
|
||||
struct page *page, struct inode *inode, bool update_times);
|
||||
static inline void ext2_put_page(struct page *page, void *page_addr)
|
||||
{
|
||||
kunmap_local(page_addr);
|
||||
put_page(page);
|
||||
}
|
||||
struct folio *folio, struct inode *inode, bool update_times);
|
||||
|
||||
/* ialloc.c */
|
||||
extern struct inode * ext2_new_inode (struct inode *, umode_t, const struct qstr *);
|
||||
|
||||
+16
-16
@@ -273,21 +273,21 @@ static int ext2_unlink(struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
struct inode *inode = d_inode(dentry);
|
||||
struct ext2_dir_entry_2 *de;
|
||||
struct page *page;
|
||||
struct folio *folio;
|
||||
int err;
|
||||
|
||||
err = dquot_initialize(dir);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
de = ext2_find_entry(dir, &dentry->d_name, &page);
|
||||
de = ext2_find_entry(dir, &dentry->d_name, &folio);
|
||||
if (IS_ERR(de)) {
|
||||
err = PTR_ERR(de);
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = ext2_delete_entry(de, page);
|
||||
ext2_put_page(page, de);
|
||||
err = ext2_delete_entry(de, folio);
|
||||
folio_release_kmap(folio, de);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
@@ -321,9 +321,9 @@ static int ext2_rename (struct mnt_idmap * idmap,
|
||||
{
|
||||
struct inode * old_inode = d_inode(old_dentry);
|
||||
struct inode * new_inode = d_inode(new_dentry);
|
||||
struct page * dir_page = NULL;
|
||||
struct folio *dir_folio = NULL;
|
||||
struct ext2_dir_entry_2 * dir_de = NULL;
|
||||
struct page * old_page;
|
||||
struct folio * old_folio;
|
||||
struct ext2_dir_entry_2 * old_de;
|
||||
int err;
|
||||
|
||||
@@ -338,19 +338,19 @@ static int ext2_rename (struct mnt_idmap * idmap,
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
old_de = ext2_find_entry(old_dir, &old_dentry->d_name, &old_page);
|
||||
old_de = ext2_find_entry(old_dir, &old_dentry->d_name, &old_folio);
|
||||
if (IS_ERR(old_de))
|
||||
return PTR_ERR(old_de);
|
||||
|
||||
if (S_ISDIR(old_inode->i_mode)) {
|
||||
err = -EIO;
|
||||
dir_de = ext2_dotdot(old_inode, &dir_page);
|
||||
dir_de = ext2_dotdot(old_inode, &dir_folio);
|
||||
if (!dir_de)
|
||||
goto out_old;
|
||||
}
|
||||
|
||||
if (new_inode) {
|
||||
struct page *new_page;
|
||||
struct folio *new_folio;
|
||||
struct ext2_dir_entry_2 *new_de;
|
||||
|
||||
err = -ENOTEMPTY;
|
||||
@@ -358,13 +358,13 @@ static int ext2_rename (struct mnt_idmap * idmap,
|
||||
goto out_dir;
|
||||
|
||||
new_de = ext2_find_entry(new_dir, &new_dentry->d_name,
|
||||
&new_page);
|
||||
&new_folio);
|
||||
if (IS_ERR(new_de)) {
|
||||
err = PTR_ERR(new_de);
|
||||
goto out_dir;
|
||||
}
|
||||
err = ext2_set_link(new_dir, new_de, new_page, old_inode, true);
|
||||
ext2_put_page(new_page, new_de);
|
||||
err = ext2_set_link(new_dir, new_de, new_folio, old_inode, true);
|
||||
folio_release_kmap(new_folio, new_de);
|
||||
if (err)
|
||||
goto out_dir;
|
||||
inode_set_ctime_current(new_inode);
|
||||
@@ -386,19 +386,19 @@ static int ext2_rename (struct mnt_idmap * idmap,
|
||||
inode_set_ctime_current(old_inode);
|
||||
mark_inode_dirty(old_inode);
|
||||
|
||||
err = ext2_delete_entry(old_de, old_page);
|
||||
err = ext2_delete_entry(old_de, old_folio);
|
||||
if (!err && dir_de) {
|
||||
if (old_dir != new_dir)
|
||||
err = ext2_set_link(old_inode, dir_de, dir_page,
|
||||
err = ext2_set_link(old_inode, dir_de, dir_folio,
|
||||
new_dir, false);
|
||||
|
||||
inode_dec_link_count(old_dir);
|
||||
}
|
||||
out_dir:
|
||||
if (dir_de)
|
||||
ext2_put_page(dir_page, dir_de);
|
||||
folio_release_kmap(dir_folio, dir_de);
|
||||
out_old:
|
||||
ext2_put_page(old_page, old_de);
|
||||
folio_release_kmap(old_folio, old_de);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
@@ -275,9 +275,9 @@ static inline void fanotify_init_event(struct fanotify_event *event,
|
||||
|
||||
#define FANOTIFY_INLINE_FH(name, size) \
|
||||
struct { \
|
||||
struct fanotify_fh (name); \
|
||||
struct fanotify_fh name; \
|
||||
/* Space for object_fh.buf[] - access with fanotify_fh_buf() */ \
|
||||
unsigned char _inline_fh_buf[(size)]; \
|
||||
unsigned char _inline_fh_buf[size]; \
|
||||
}
|
||||
|
||||
struct fanotify_fid_event {
|
||||
|
||||
@@ -2351,6 +2351,20 @@ static int vfs_setup_quota_inode(struct inode *inode, int type)
|
||||
if (sb_has_quota_loaded(sb, type))
|
||||
return -EBUSY;
|
||||
|
||||
/*
|
||||
* Quota files should never be encrypted. They should be thought of as
|
||||
* filesystem metadata, not user data. New-style internal quota files
|
||||
* cannot be encrypted by users anyway, but old-style external quota
|
||||
* files could potentially be incorrectly created in an encrypted
|
||||
* directory, hence this explicit check. Some reasons why encrypted
|
||||
* quota files don't work include: (1) some filesystems that support
|
||||
* encryption don't handle it in their quota_read and quota_write, and
|
||||
* (2) cleaning up encrypted quota files at unmount would need special
|
||||
* consideration, as quota files are cleaned up later than user files.
|
||||
*/
|
||||
if (IS_ENCRYPTED(inode))
|
||||
return -EINVAL;
|
||||
|
||||
dqopt->files[type] = igrab(inode);
|
||||
if (!dqopt->files[type])
|
||||
return -EIO;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user