From c0aba9f328019fa8ba1b771ba0146ac61ce561ad Mon Sep 17 00:00:00 2001 From: Rohit Agarwal Date: Fri, 31 Mar 2023 11:48:18 +0530 Subject: dt-bindings: PCI: qcom: Add SDX65 SoC Add PCIe EP compatible string for SDX65 SoC. Link: https://lore.kernel.org/r/1680243502-23744-2-git-send-email-quic_rohiagar@quicinc.com Signed-off-by: Rohit Agarwal Signed-off-by: Lorenzo Pieralisi Reviewed-by: Manivannan Sadhasivam Acked-by: Krzysztof Kozlowski --- Documentation/devicetree/bindings/pci/qcom,pcie-ep.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-ep.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-ep.yaml index b3c22ebd156c..811112255d7d 100644 --- a/Documentation/devicetree/bindings/pci/qcom,pcie-ep.yaml +++ b/Documentation/devicetree/bindings/pci/qcom,pcie-ep.yaml @@ -13,6 +13,7 @@ properties: compatible: enum: - qcom,sdx55-pcie-ep + - qcom,sdx65-pcie-ep - qcom,sm8450-pcie-ep reg: @@ -109,6 +110,7 @@ allOf: contains: enum: - qcom,sdx55-pcie-ep + - qcom,sdx65-pcie-ep then: properties: clocks: -- cgit v1.2.3 From 0e12f830236928b6fadf40d917a7527f0a048d2f Mon Sep 17 00:00:00 2001 From: Siddharth Vadapalli Date: Wed, 15 Mar 2023 12:38:00 +0530 Subject: PCI: cadence: Fix Gen2 Link Retraining process The Link Retraining process is initiated to account for the Gen2 defect in the Cadence PCIe controller in J721E SoC. The errata corresponding to this is i2085, documented at: https://www.ti.com/lit/er/sprz455c/sprz455c.pdf The existing workaround implemented for the errata waits for the Data Link initialization to complete and assumes that the link retraining process at the Physical Layer has completed. However, it is possible that the Physical Layer training might be ongoing as indicated by the PCI_EXP_LNKSTA_LT bit in the PCI_EXP_LNKSTA register. Fix the existing workaround, to ensure that the Physical Layer training has also completed, in addition to the Data Link initialization. Link: https://lore.kernel.org/r/20230315070800.1615527-1-s-vadapalli@ti.com Fixes: 4740b969aaf5 ("PCI: cadence: Retrain Link to work around Gen2 training defect") Signed-off-by: Siddharth Vadapalli Signed-off-by: Lorenzo Pieralisi Reviewed-by: Vignesh Raghavendra --- drivers/pci/controller/cadence/pcie-cadence-host.c | 27 ++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c index 940c7dd701d6..5b14f7ee3c79 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host.c @@ -12,6 +12,8 @@ #include "pcie-cadence.h" +#define LINK_RETRAIN_TIMEOUT HZ + static u64 bar_max_size[] = { [RP_BAR0] = _ULL(128 * SZ_2G), [RP_BAR1] = SZ_2G, @@ -77,6 +79,27 @@ static struct pci_ops cdns_pcie_host_ops = { .write = pci_generic_config_write, }; +static int cdns_pcie_host_training_complete(struct cdns_pcie *pcie) +{ + u32 pcie_cap_off = CDNS_PCIE_RP_CAP_OFFSET; + unsigned long end_jiffies; + u16 lnk_stat; + + /* Wait for link training to complete. Exit after timeout. */ + end_jiffies = jiffies + LINK_RETRAIN_TIMEOUT; + do { + lnk_stat = cdns_pcie_rp_readw(pcie, pcie_cap_off + PCI_EXP_LNKSTA); + if (!(lnk_stat & PCI_EXP_LNKSTA_LT)) + break; + usleep_range(0, 1000); + } while (time_before(jiffies, end_jiffies)); + + if (!(lnk_stat & PCI_EXP_LNKSTA_LT)) + return 0; + + return -ETIMEDOUT; +} + static int cdns_pcie_host_wait_for_link(struct cdns_pcie *pcie) { struct device *dev = pcie->dev; @@ -118,6 +141,10 @@ static int cdns_pcie_retrain(struct cdns_pcie *pcie) cdns_pcie_rp_writew(pcie, pcie_cap_off + PCI_EXP_LNKCTL, lnk_ctl); + ret = cdns_pcie_host_training_complete(pcie); + if (ret) + return ret; + ret = cdns_pcie_host_wait_for_link(pcie); } return ret; -- cgit v1.2.3 From b61cf04c49c3dfa70a0d6725d3eb40bf9b35cf71 Mon Sep 17 00:00:00 2001 From: Nirmal Patel Date: Fri, 24 Feb 2023 13:28:11 -0700 Subject: PCI: vmd: Reset VMD config register between soft reboots VMD driver can disable or enable MSI remapping by changing VMCONFIG_MSI_REMAP register. This register needs to be set to the default value during soft reboots. Drives failed to enumerate when Windows boots after performing a soft reboot from Linux. Windows doesn't support MSI remapping disable feature and stale register value hinders Windows VMD driver initialization process. Adding vmd_shutdown function to make sure to set the VMCONFIG register to the default value. Link: https://lore.kernel.org/r/20230224202811.644370-1-nirmal.patel@linux.intel.com Fixes: ee81ee84f873 ("PCI: vmd: Disable MSI-X remapping when possible") Signed-off-by: Nirmal Patel Signed-off-by: Lorenzo Pieralisi Reviewed-by: Jon Derrick --- drivers/pci/controller/vmd.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c index 990630ec57c6..30ec18283aaf 100644 --- a/drivers/pci/controller/vmd.c +++ b/drivers/pci/controller/vmd.c @@ -1036,6 +1036,13 @@ static void vmd_remove(struct pci_dev *dev) ida_simple_remove(&vmd_instance_ida, vmd->instance); } +static void vmd_shutdown(struct pci_dev *dev) +{ + struct vmd_dev *vmd = pci_get_drvdata(dev); + + vmd_remove_irq_domain(vmd); +} + #ifdef CONFIG_PM_SLEEP static int vmd_suspend(struct device *dev) { @@ -1101,6 +1108,7 @@ static struct pci_driver vmd_drv = { .id_table = vmd_ids, .probe = vmd_probe, .remove = vmd_remove, + .shutdown = vmd_shutdown, .driver = { .pm = &vmd_dev_pm_ops, }, -- cgit v1.2.3 From 0ac448e0d29d6ba978684b3fa2e3ac7294ec2475 Mon Sep 17 00:00:00 2001 From: Mike Pastore Date: Sun, 7 May 2023 02:35:19 -0500 Subject: PCI: Delay after FLR of Solidigm P44 Pro NVMe Prevent KVM hang when a Solidgm P44 Pro NVMe is passed through to a guest via IOMMU and the guest is subsequently rebooted. A similar issue was identified and patched by 51ba09452d11 ("PCI: Delay after FLR of Intel DC P3700 NVMe") and the same fix can be applied for this case. (Intel spun off their NAND and SSD business as Solidigm and sold it to SK Hynix in late 2021.) Link: https://lore.kernel.org/r/20230507073519.9737-1-mike@oobak.org Signed-off-by: Mike Pastore Signed-off-by: Bjorn Helgaas --- drivers/pci/quirks.c | 10 ++++++---- include/linux/pci_ids.h | 2 ++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index f4e2a88729fd..c1239706eeaf 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -3993,10 +3993,11 @@ static int nvme_disable_and_flr(struct pci_dev *dev, bool probe) } /* - * Intel DC P3700 NVMe controller will timeout waiting for ready status - * to change after NVMe enable if the driver starts interacting with the - * device too soon after FLR. A 250ms delay after FLR has heuristically - * proven to produce reliably working results for device assignment cases. + * Some NVMe controllers such as Intel DC P3700 and Solidigm P44 Pro will + * timeout waiting for ready status to change after NVMe enable if the driver + * starts interacting with the device too soon after FLR. A 250ms delay after + * FLR has heuristically proven to produce reliably working results for device + * assignment cases. */ static int delay_250ms_after_flr(struct pci_dev *dev, bool probe) { @@ -4083,6 +4084,7 @@ static const struct pci_dev_reset_methods pci_dev_reset_methods[] = { { PCI_VENDOR_ID_SAMSUNG, 0xa804, nvme_disable_and_flr }, { PCI_VENDOR_ID_INTEL, 0x0953, delay_250ms_after_flr }, { PCI_VENDOR_ID_INTEL, 0x0a54, delay_250ms_after_flr }, + { PCI_VENDOR_ID_SOLIDIGM, 0xf1ac, delay_250ms_after_flr }, { PCI_VENDOR_ID_CHELSIO, PCI_ANY_ID, reset_chelsio_generic_dev }, { PCI_VENDOR_ID_HUAWEI, PCI_DEVICE_ID_HINIC_VF, diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 45c3d62e616d..20c3403a62cd 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -158,6 +158,8 @@ #define PCI_VENDOR_ID_LOONGSON 0x0014 +#define PCI_VENDOR_ID_SOLIDIGM 0x025e + #define PCI_VENDOR_ID_TTTECH 0x0357 #define PCI_DEVICE_ID_TTTECH_MC322 0x000a -- cgit v1.2.3 From 456d8aa37d0f56fc9e985e812496e861dcd6f2f2 Mon Sep 17 00:00:00 2001 From: Ding Hui Date: Sun, 7 May 2023 11:40:57 +0800 Subject: PCI/ASPM: Disable ASPM on MFD function removal to avoid use-after-free Struct pcie_link_state->downstream is a pointer to the pci_dev of function 0. Previously we retained that pointer when removing function 0, and subsequent ASPM policy changes dereferenced it, resulting in a use-after-free warning from KASAN, e.g.: # echo 1 > /sys/bus/pci/devices/0000:03:00.0/remove # echo powersave > /sys/module/pcie_aspm/parameters/policy BUG: KASAN: slab-use-after-free in pcie_config_aspm_link+0x42d/0x500 Call Trace: kasan_report+0xae/0xe0 pcie_config_aspm_link+0x42d/0x500 pcie_aspm_set_policy+0x8e/0x1a0 param_attr_store+0x162/0x2c0 module_attr_store+0x3e/0x80 PCIe spec r6.0, sec 7.5.3.7, recommends that software program the same ASPM Control value in all functions of multi-function devices. Disable ASPM and free the pcie_link_state when any child function is removed so we can discard the dangling pcie_link_state->downstream pointer and maintain the same ASPM Control configuration for all functions. [bhelgaas: commit log and comment] Debugged-by: Zongquan Qin Suggested-by: Bjorn Helgaas Fixes: b5a0a9b59c81 ("PCI/ASPM: Read and set up L1 substate capabilities") Link: https://lore.kernel.org/r/20230507034057.20970-1-dinghui@sangfor.com.cn Signed-off-by: Ding Hui Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aspm.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 66d7514ca111..db32335039d6 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -1010,21 +1010,24 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev) down_read(&pci_bus_sem); mutex_lock(&aspm_lock); - /* - * All PCIe functions are in one slot, remove one function will remove - * the whole slot, so just wait until we are the last function left. - */ - if (!list_empty(&parent->subordinate->devices)) - goto out; link = parent->link_state; root = link->root; parent_link = link->parent; - /* All functions are removed, so just disable ASPM for the link */ + /* + * link->downstream is a pointer to the pci_dev of function 0. If + * we remove that function, the pci_dev is about to be deallocated, + * so we can't use link->downstream again. Free the link state to + * avoid this. + * + * If we're removing a non-0 function, it's possible we could + * retain the link state, but PCIe r6.0, sec 7.5.3.7, recommends + * programming the same ASPM Control value for all functions of + * multi-function devices, so disable ASPM for all of them. + */ pcie_config_aspm_link(link, 0); list_del(&link->sibling); - /* Clock PM is for endpoint device */ free_link_state(link); /* Recheck latencies and configure upstream links */ @@ -1032,7 +1035,7 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev) pcie_update_aspm_capable(root); pcie_config_aspm_path(parent_link); } -out: + mutex_unlock(&aspm_lock); up_read(&pci_bus_sem); } -- cgit v1.2.3 From fb097dcd5a28c0a2325632405c76a66777a6bed9 Mon Sep 17 00:00:00 2001 From: Ajay Agarwal Date: Thu, 4 May 2023 16:42:57 +0530 Subject: PCI/ASPM: Disable only ASPM_STATE_L1 when driver disables L1 Previously pci_disable_link_state(PCIE_LINK_STATE_L1) disabled L1SS as well as L1. This is unnecessary since pcie_config_aspm_link() takes care that L1SS is not enabled if L1 is disabled. Disable only ASPM_STATE_L1 when the caller disables L1. No functional changes intended. This is consistent with aspm_attr_store_common(), which disables only L1, not L1SS, when L1 is disabled via the sysfs "l1_aspm" file. [bhelgaas: commit log] Link: https://lore.kernel.org/r/20230504111301.229358-2-ajayagarwal@google.com Signed-off-by: Ajay Agarwal Signed-off-by: Bjorn Helgaas Reviewed-by: Kuppuswamy Sathyanarayanan --- drivers/pci/pcie/aspm.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index db32335039d6..8c8352eeee52 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -1098,8 +1098,7 @@ 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) - /* L1 PM substates require L1 */ - link->aspm_disable |= ASPM_STATE_L1 | ASPM_STATE_L1SS; + link->aspm_disable |= ASPM_STATE_L1; if (state & PCIE_LINK_STATE_L1_1) link->aspm_disable |= ASPM_STATE_L1_1; if (state & PCIE_LINK_STATE_L1_2) -- cgit v1.2.3 From 25edb25d7972414022c1fa098e2d85876bd7fab2 Mon Sep 17 00:00:00 2001 From: Ajay Agarwal Date: Thu, 4 May 2023 16:42:58 +0530 Subject: PCI/ASPM: Set only ASPM_STATE_L1 when driver enables L1 Previously pci_enable_link_state(PCIE_LINK_STATE_L1) enabled L1SS as well as L1. Enable only ASPM_STATE_L1 when the caller enables L1. The only current caller is vmd_pm_enable_quirk(), which enables *all* ASPM states, so this should have no functional effect. [bhelgaas: commit log] Link: https://lore.kernel.org/r/20230504111301.229358-3-ajayagarwal@google.com Signed-off-by: Ajay Agarwal Signed-off-by: Bjorn Helgaas Reviewed-by: Kuppuswamy Sathyanarayanan --- drivers/pci/pcie/aspm.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 8c8352eeee52..a341019f9d9b 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -1173,8 +1173,7 @@ int pci_enable_link_state(struct pci_dev *pdev, int state) if (state & PCIE_LINK_STATE_L0S) link->aspm_default |= ASPM_STATE_L0S; if (state & PCIE_LINK_STATE_L1) - /* L1 PM substates require L1 */ - link->aspm_default |= ASPM_STATE_L1 | ASPM_STATE_L1SS; + link->aspm_default |= ASPM_STATE_L1; if (state & PCIE_LINK_STATE_L1_1) link->aspm_default |= ASPM_STATE_L1_1; if (state & PCIE_LINK_STATE_L1_2) -- cgit v1.2.3 From 80950a546089690ff6f02ca15930b9be695a668a Mon Sep 17 00:00:00 2001 From: Ajay Agarwal Date: Thu, 4 May 2023 16:42:59 +0530 Subject: PCI/ASPM: Set ASPM_STATE_L1 when driver enables L1.1 or L1.2 Previously pci_enable_link_state(PCIE_LINK_STATE_L1_1) enabled only ASPM_STATE_L1_1 and did not enable ASPM_STATE_L1. The L1.1 state only works when L1 is enabled, so enable ASPM_STATE_L1 in addition, and do the same for L1.2. The only current caller is vmd_pm_enable_quirk(), which enables *all* ASPM states, so this should have no functional effect. [bhelgaas: commit log] Link: https://lore.kernel.org/r/20230504111301.229358-4-ajayagarwal@google.com Signed-off-by: Ajay Agarwal Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aspm.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index a341019f9d9b..338eedef12f1 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -1174,14 +1174,15 @@ int pci_enable_link_state(struct pci_dev *pdev, int state) link->aspm_default |= ASPM_STATE_L0S; if (state & PCIE_LINK_STATE_L1) link->aspm_default |= ASPM_STATE_L1; + /* L1 PM substates require L1 */ if (state & PCIE_LINK_STATE_L1_1) - link->aspm_default |= ASPM_STATE_L1_1; + link->aspm_default |= ASPM_STATE_L1_1 | ASPM_STATE_L1; if (state & PCIE_LINK_STATE_L1_2) - link->aspm_default |= ASPM_STATE_L1_2; + link->aspm_default |= ASPM_STATE_L1_2 | ASPM_STATE_L1; if (state & PCIE_LINK_STATE_L1_1_PCIPM) - link->aspm_default |= ASPM_STATE_L1_1_PCIPM; + link->aspm_default |= ASPM_STATE_L1_1_PCIPM | ASPM_STATE_L1; if (state & PCIE_LINK_STATE_L1_2_PCIPM) - link->aspm_default |= ASPM_STATE_L1_2_PCIPM; + link->aspm_default |= ASPM_STATE_L1_2_PCIPM | ASPM_STATE_L1; pcie_config_aspm_link(link, policy_to_aspm_state(link)); link->clkpm_default = (state & PCIE_LINK_STATE_CLKPM) ? 1 : 0; -- cgit v1.2.3 From 05a55d9ca1457295db73f127e39ec3b18f0b32b2 Mon Sep 17 00:00:00 2001 From: Ajay Agarwal Date: Thu, 4 May 2023 16:43:00 +0530 Subject: PCI/ASPM: Rename L1.2-specific functions from 'l1ss' to 'l12' The functions aspm_calc_l1ss_info() and calc_l1ss_pwron() perform calculations and register programming specific to L1.2 state. Rename them to aspm_calc_l12_info() and calc_l12_pwron() respectively. Link: https://lore.kernel.org/r/20230504111301.229358-5-ajayagarwal@google.com Signed-off-by: Ajay Agarwal Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aspm.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 338eedef12f1..d3d8dfe1fb73 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -337,7 +337,7 @@ static u32 calc_l1_acceptable(u32 encoding) } /* Convert L1SS T_pwr encoding to usec */ -static u32 calc_l1ss_pwron(struct pci_dev *pdev, u32 scale, u32 val) +static u32 calc_l12_pwron(struct pci_dev *pdev, u32 scale, u32 val) { switch (scale) { case 0: @@ -471,7 +471,7 @@ static void pci_clear_and_set_dword(struct pci_dev *pdev, int pos, } /* Calculate L1.2 PM substate timing parameters */ -static void aspm_calc_l1ss_info(struct pcie_link_state *link, +static void aspm_calc_l12_info(struct pcie_link_state *link, u32 parent_l1ss_cap, u32 child_l1ss_cap) { struct pci_dev *child = link->downstream, *parent = link->pdev; @@ -495,13 +495,13 @@ static void aspm_calc_l1ss_info(struct pcie_link_state *link, val2 = (child_l1ss_cap & PCI_L1SS_CAP_P_PWR_ON_VALUE) >> 19; scale2 = (child_l1ss_cap & PCI_L1SS_CAP_P_PWR_ON_SCALE) >> 16; - if (calc_l1ss_pwron(parent, scale1, val1) > - calc_l1ss_pwron(child, scale2, val2)) { + if (calc_l12_pwron(parent, scale1, val1) > + calc_l12_pwron(child, scale2, val2)) { ctl2 |= scale1 | (val1 << 3); - t_power_on = calc_l1ss_pwron(parent, scale1, val1); + t_power_on = calc_l12_pwron(parent, scale1, val1); } else { ctl2 |= scale2 | (val2 << 3); - t_power_on = calc_l1ss_pwron(child, scale2, val2); + t_power_on = calc_l12_pwron(child, scale2, val2); } /* @@ -617,7 +617,7 @@ static void aspm_l1ss_init(struct pcie_link_state *link) link->aspm_enabled |= ASPM_STATE_L1_2_PCIPM; if (link->aspm_support & ASPM_STATE_L1SS) - aspm_calc_l1ss_info(link, parent_l1ss_cap, child_l1ss_cap); + aspm_calc_l12_info(link, parent_l1ss_cap, child_l1ss_cap); } static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) -- cgit v1.2.3 From 911afb9f9516a8ea2db1d15b18436c19a591dc5c Mon Sep 17 00:00:00 2001 From: Ajay Agarwal Date: Thu, 4 May 2023 16:43:01 +0530 Subject: PCI/ASPM: Remove unnecessary ASPM_STATE_L1SS check Previously aspm_l1ss_init() checked if ASPM_STATE_L1SS is supported before calling aspm_calc_l12_info(), only for that function to return if ASPM_STATE_L1_2_MASK is not supported. Simplify the logic by directly checking for ASPM_STATE_L1_2_MASK. Link: https://lore.kernel.org/r/20230504111301.229358-6-ajayagarwal@google.com Signed-off-by: Ajay Agarwal Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aspm.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index d3d8dfe1fb73..72cdb30a924a 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -481,9 +481,6 @@ static void aspm_calc_l12_info(struct pcie_link_state *link, u32 pctl1, pctl2, cctl1, cctl2; u32 pl1_2_enables, cl1_2_enables; - if (!(link->aspm_support & ASPM_STATE_L1_2_MASK)) - return; - /* 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; @@ -616,7 +613,7 @@ static void aspm_l1ss_init(struct pcie_link_state *link) if (parent_l1ss_ctl1 & child_l1ss_ctl1 & PCI_L1SS_CTL1_PCIPM_L1_2) link->aspm_enabled |= ASPM_STATE_L1_2_PCIPM; - if (link->aspm_support & ASPM_STATE_L1SS) + if (link->aspm_support & ASPM_STATE_L1_2_MASK) aspm_calc_l12_info(link, parent_l1ss_cap, child_l1ss_cap); } -- cgit v1.2.3 From 5054133a88622943783e370ede795e725f39a485 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 22 May 2023 16:27:17 -0500 Subject: PCI: pciehp: Simplify Attention Button logging Previously, pressing the Attention Button always logged two lines, the first from pciehp_ist() and the second from pciehp_handle_button_press(): Attention button pressed Powering on due to button press Since pciehp_handle_button_press() always logs the more detailed message, remove the generic "Attention button pressed" message. Reword the pciehp_handle_button_press() to be of the form: Button press: will power on in 5 sec Button press: will power off in 5 sec Button press: canceling request to power on Button press: canceling request to power off Button press: ignoring invalid state %#x Link: https://lore.kernel.org/r/20230522214051.619337-1-helgaas@kernel.org Signed-off-by: Bjorn Helgaas Reviewed-by: Lukas Wunner --- drivers/pci/hotplug/pciehp_ctrl.c | 13 +++++++------ drivers/pci/hotplug/pciehp_hpc.c | 5 +---- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index 529c34808440..17a9243d98f4 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -166,11 +166,11 @@ void pciehp_handle_button_press(struct controller *ctrl) case ON_STATE: if (ctrl->state == ON_STATE) { ctrl->state = BLINKINGOFF_STATE; - ctrl_info(ctrl, "Slot(%s): Powering off due to button press\n", + ctrl_info(ctrl, "Slot(%s): Button press: will power off in 5 sec\n", slot_name(ctrl)); } else { ctrl->state = BLINKINGON_STATE; - ctrl_info(ctrl, "Slot(%s) Powering on due to button press\n", + ctrl_info(ctrl, "Slot(%s): Button press: will power on in 5 sec\n", slot_name(ctrl)); } /* blink power indicator and turn off attention */ @@ -185,22 +185,23 @@ void pciehp_handle_button_press(struct controller *ctrl) * press the attention again before the 5 sec. limit * expires to cancel hot-add or hot-remove */ - ctrl_info(ctrl, "Slot(%s): Button cancel\n", slot_name(ctrl)); cancel_delayed_work(&ctrl->button_work); if (ctrl->state == BLINKINGOFF_STATE) { ctrl->state = ON_STATE; pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_ON, PCI_EXP_SLTCTL_ATTN_IND_OFF); + ctrl_info(ctrl, "Slot(%s): Button press: canceling request to power off\n", + slot_name(ctrl)); } else { ctrl->state = OFF_STATE; pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF, PCI_EXP_SLTCTL_ATTN_IND_OFF); + ctrl_info(ctrl, "Slot(%s): Button press: canceling request to power on\n", + slot_name(ctrl)); } - ctrl_info(ctrl, "Slot(%s): Action canceled due to button press\n", - slot_name(ctrl)); break; default: - ctrl_err(ctrl, "Slot(%s): Ignoring invalid state %#x\n", + ctrl_err(ctrl, "Slot(%s): Button press: ignoring invalid state %#x\n", slot_name(ctrl), ctrl->state); break; } diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index f8c70115b691..379d2af5c51d 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -722,11 +722,8 @@ static irqreturn_t pciehp_ist(int irq, void *dev_id) } /* Check Attention Button Pressed */ - if (events & PCI_EXP_SLTSTA_ABP) { - ctrl_info(ctrl, "Slot(%s): Attention button pressed\n", - slot_name(ctrl)); + if (events & PCI_EXP_SLTSTA_ABP) pciehp_handle_button_press(ctrl); - } /* Check Power Fault Detected */ if (events & PCI_EXP_SLTSTA_PFD) { -- cgit v1.2.3 From e8afd0d9fccc27c8ad263db5cf5952cfcf72d6fe Mon Sep 17 00:00:00 2001 From: Rongguang Wei Date: Fri, 12 May 2023 10:15:18 +0800 Subject: PCI: pciehp: Cancel bringup sequence if card is not present If a PCIe hotplug slot has an Attention Button, the normal hot-add flow is: - Slot is empty and slot power is off - User inserts card in slot and presses Attention Button - OS blinks Power Indicator for 5 seconds - After 5 seconds, OS turns on Power Indicator, turns on slot power, and enumerates the device Previously, if a user pressed the Attention Button on an *empty* slot, pciehp logged the following messages and blinked the Power Indicator until a second button press: [0.000] pciehp: Button press: will power on in 5 sec [0.001] # Power Indicator starts blinking [5.001] # 5 second timeout; slot is empty, so we should cancel the request to power on and turn off Power Indicator [7.000] # Power Indicator still blinking [8.000] # possible card insertion [9.000] pciehp: Button press: canceling request to power on The first button press incorrectly left the slot in BLINKINGON_STATE, so the second was interpreted as a "cancel power on" event regardless of whether a card was present. If the slot is empty, turn off the Power Indicator and return from BLINKINGON_STATE to OFF_STATE after 5 seconds, effectively canceling the request to power on. Putting the slot in OFF_STATE also means the second button press will correctly request a slot power on if the slot is occupied. [bhelgaas: commit log] Link: https://lore.kernel.org/r/20230512021518.336460-1-clementwei90@163.com Fixes: d331710ea78f ("PCI: pciehp: Become resilient to missed events") Suggested-by: Lukas Wunner Signed-off-by: Rongguang Wei Signed-off-by: Bjorn Helgaas Reviewed-by: Lukas Wunner --- drivers/pci/hotplug/pciehp_ctrl.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index 17a9243d98f4..dcdbfcf404dd 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -257,6 +257,14 @@ void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events) present = pciehp_card_present(ctrl); link_active = pciehp_check_link_active(ctrl); if (present <= 0 && link_active <= 0) { + if (ctrl->state == BLINKINGON_STATE) { + ctrl->state = OFF_STATE; + cancel_delayed_work(&ctrl->button_work); + pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF, + INDICATOR_NOOP); + ctrl_info(ctrl, "Slot(%s): Card not present\n", + slot_name(ctrl)); + } mutex_unlock(&ctrl->state_lock); return; } -- cgit v1.2.3 From 40613da52b13fb21c5566f10b287e0ca8c12c4e9 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Mon, 24 Apr 2023 21:15:57 +0200 Subject: PCI: acpiphp: Reassign resources on bridge if necessary When using ACPI PCI hotplug, hotplugging a device with large BARs may fail if bridge windows programmed by firmware are not large enough. Reproducer: $ qemu-kvm -monitor stdio -M q35 -m 4G \ -global ICH9-LPC.acpi-pci-hotplug-with-bridge-support=on \ -device id=rp1,pcie-root-port,bus=pcie.0,chassis=4 \ disk_image wait till linux guest boots, then hotplug device: (qemu) device_add qxl,bus=rp1 hotplug on guest side fails with: pci 0000:01:00.0: [1b36:0100] type 00 class 0x038000 pci 0000:01:00.0: reg 0x10: [mem 0x00000000-0x03ffffff] pci 0000:01:00.0: reg 0x14: [mem 0x00000000-0x03ffffff] pci 0000:01:00.0: reg 0x18: [mem 0x00000000-0x00001fff] pci 0000:01:00.0: reg 0x1c: [io 0x0000-0x001f] pci 0000:01:00.0: BAR 0: no space for [mem size 0x04000000] pci 0000:01:00.0: BAR 0: failed to assign [mem size 0x04000000] pci 0000:01:00.0: BAR 1: no space for [mem size 0x04000000] pci 0000:01:00.0: BAR 1: failed to assign [mem size 0x04000000] pci 0000:01:00.0: BAR 2: assigned [mem 0xfe800000-0xfe801fff] pci 0000:01:00.0: BAR 3: assigned [io 0x1000-0x101f] qxl 0000:01:00.0: enabling device (0000 -> 0003) Unable to create vram_mapping qxl: probe of 0000:01:00.0 failed with error -12 However when using native PCIe hotplug '-global ICH9-LPC.acpi-pci-hotplug-with-bridge-support=off' it works fine, since kernel attempts to reassign unused resources. Use the same machinery as native PCIe hotplug to (re)assign resources. Link: https://lore.kernel.org/r/20230424191557.2464760-1-imammedo@redhat.com Signed-off-by: Igor Mammedov Signed-off-by: Bjorn Helgaas Acked-by: Michael S. Tsirkin Acked-by: Rafael J. Wysocki Cc: stable@vger.kernel.org --- drivers/pci/hotplug/acpiphp_glue.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 5b1f271c6034..328d1e416014 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -498,7 +498,6 @@ static void enable_slot(struct acpiphp_slot *slot, bool bridge) acpiphp_native_scan_bridge(dev); } } else { - LIST_HEAD(add_list); int max, pass; acpiphp_rescan_slot(slot); @@ -512,12 +511,10 @@ static void enable_slot(struct acpiphp_slot *slot, bool bridge) if (pass && dev->subordinate) { check_hotplug_bridge(slot, dev); pcibios_resource_survey_bus(dev->subordinate); - __pci_bus_size_bridges(dev->subordinate, - &add_list); } } } - __pci_bus_assign_resources(bus, &add_list, NULL); + pci_assign_unassigned_bridge_resources(bus->self); } acpiphp_sanitize_bus(bus); -- cgit v1.2.3 From da56a1bfbab55189595e588f1d984bdfb5cf5924 Mon Sep 17 00:00:00 2001 From: Ajay Agarwal Date: Wed, 12 Apr 2023 15:04:25 +0530 Subject: PCI: dwc: Wait for link up only if link is started In dw_pcie_host_init() regardless of whether the link has been started or not, the code waits for the link to come up. Even in cases where start_link() is not defined the code ends up spinning in a loop for 1 second. Since in some systems dw_pcie_host_init() gets called during probe, this one second loop for each pcie interface instance ends up extending the boot time. Wait for the link up in only if the start_link() is defined. Link: https://lore.kernel.org/r/20230412093425.3659088-1-ajayagarwal@google.com Tested-by: Will McVicker Signed-off-by: Sajid Dalvi Signed-off-by: Ajay Agarwal Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/dwc/pcie-designware-host.c | 13 +++++++++---- drivers/pci/controller/dwc/pcie-designware.c | 20 +++++++++++++------- drivers/pci/controller/dwc/pcie-designware.h | 1 + 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 9952057c8819..cf61733bf78d 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -485,14 +485,19 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp) if (ret) goto err_remove_edma; - if (!dw_pcie_link_up(pci)) { + if (dw_pcie_link_up(pci)) { + dw_pcie_print_link_status(pci); + } else { ret = dw_pcie_start_link(pci); if (ret) goto err_remove_edma; - } - /* Ignore errors, the link may come up later */ - dw_pcie_wait_for_link(pci); + if (pci->ops && pci->ops->start_link) { + ret = dw_pcie_wait_for_link(pci); + if (ret) + goto err_stop_link; + } + } bridge->sysdata = pp; diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index 8e33e6e59e68..df092229e97d 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -644,9 +644,20 @@ void dw_pcie_disable_atu(struct dw_pcie *pci, u32 dir, int index) dw_pcie_writel_atu(pci, dir, index, PCIE_ATU_REGION_CTRL2, 0); } -int dw_pcie_wait_for_link(struct dw_pcie *pci) +void dw_pcie_print_link_status(struct dw_pcie *pci) { u32 offset, val; + + offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); + val = dw_pcie_readw_dbi(pci, offset + PCI_EXP_LNKSTA); + + dev_info(pci->dev, "PCIe Gen.%u x%u link up\n", + FIELD_GET(PCI_EXP_LNKSTA_CLS, val), + FIELD_GET(PCI_EXP_LNKSTA_NLW, val)); +} + +int dw_pcie_wait_for_link(struct dw_pcie *pci) +{ int retries; /* Check if the link is up or not */ @@ -662,12 +673,7 @@ int dw_pcie_wait_for_link(struct dw_pcie *pci) return -ETIMEDOUT; } - offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); - val = dw_pcie_readw_dbi(pci, offset + PCI_EXP_LNKSTA); - - dev_info(pci->dev, "PCIe Gen.%u x%u link up\n", - FIELD_GET(PCI_EXP_LNKSTA_CLS, val), - FIELD_GET(PCI_EXP_LNKSTA_NLW, val)); + dw_pcie_print_link_status(pci); return 0; } diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 79713ce075cc..615660640801 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -429,6 +429,7 @@ void dw_pcie_setup(struct dw_pcie *pci); void dw_pcie_iatu_detect(struct dw_pcie *pci); int dw_pcie_edma_detect(struct dw_pcie *pci); void dw_pcie_edma_remove(struct dw_pcie *pci); +void dw_pcie_print_link_status(struct dw_pcie *pci); static inline void dw_pcie_writel_dbi(struct dw_pcie *pci, u32 reg, u32 val) { -- cgit v1.2.3 From c60738de85f40b0b9f5cb23c21f9246e5a47908c Mon Sep 17 00:00:00 2001 From: Junyan Ye Date: Mon, 8 May 2023 12:36:41 +0800 Subject: PCI: ftpci100: Release the clock resources Smatch reported: 1. drivers/pci/controller/pci-ftpci100.c:526 faraday_pci_probe() warn: 'clk' from clk_prepare_enable() not released on lines: 442,451,462,478,512,517. 2. drivers/pci/controller/pci-ftpci100.c:526 faraday_pci_probe() warn: 'p->bus_clk' from clk_prepare_enable() not released on lines: 451,462,478,512,517. The clock resource is obtained by devm_clk_get(), and then clk_prepare_enable() makes the clock resource ready for use. After that, clk_disable_unprepare() should be called to release the clock resource when it is no longer needed. However, while doing some error handling in faraday_pci_probe(), clk_disable_unprepare() is not called to release clk and p->bus_clk before returning. These return lines are exactly 442, 451, 462, 478, 512, 517. Fix this warning by replacing devm_clk_get() with devm_clk_get_enabled(), which is equivalent to devm_clk_get() + clk_prepare_enable(). And with devm_clk_get_enabled(), the clock will automatically be disabled, unprepared and freed when the device is unbound from the bus. Link: https://lore.kernel.org/r/20230508043641.23807-1-yejunyan@hust.edu.cn Fixes: b3c433efb8a3 ("PCI: faraday: Fix wrong pointer passed to PTR_ERR()") Fixes: 2eeb02b28579 ("PCI: faraday: Add clock handling") Fixes: 783a862563f7 ("PCI: faraday: Use pci_parse_request_of_pci_ranges()") Fixes: d3c68e0a7e34 ("PCI: faraday: Add Faraday Technology FTPCI100 PCI Host Bridge driver") Fixes: f1e8bd21e39e ("PCI: faraday: Convert IRQ masking to raw PCI config accessors") Signed-off-by: Junyan Ye Signed-off-by: Lorenzo Pieralisi Reviewed-by: Dongliang Mu Reviewed-by: Linus Walleij --- drivers/pci/controller/pci-ftpci100.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/drivers/pci/controller/pci-ftpci100.c b/drivers/pci/controller/pci-ftpci100.c index ecd3009df586..6e7981d2ed5e 100644 --- a/drivers/pci/controller/pci-ftpci100.c +++ b/drivers/pci/controller/pci-ftpci100.c @@ -429,22 +429,12 @@ static int faraday_pci_probe(struct platform_device *pdev) p->dev = dev; /* Retrieve and enable optional clocks */ - clk = devm_clk_get(dev, "PCLK"); + clk = devm_clk_get_enabled(dev, "PCLK"); if (IS_ERR(clk)) return PTR_ERR(clk); - ret = clk_prepare_enable(clk); - if (ret) { - dev_err(dev, "could not prepare PCLK\n"); - return ret; - } - p->bus_clk = devm_clk_get(dev, "PCICLK"); + p->bus_clk = devm_clk_get_enabled(dev, "PCICLK"); if (IS_ERR(p->bus_clk)) return PTR_ERR(p->bus_clk); - ret = clk_prepare_enable(p->bus_clk); - if (ret) { - dev_err(dev, "could not prepare PCICLK\n"); - return ret; - } p->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(p->base)) -- cgit v1.2.3 From 56ad9b2110699a80eb5f49413add2bf4b90bb285 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 23 May 2023 17:52:44 +0200 Subject: PCI: rcar-host: Remove unused static pcie_base and pcie_dev After the L1 link state transition exception handler rework, the static copies of the remapped PCIe controller address and the PCIe device pointer became unused. Remove them. Link: https://lore.kernel.org/r/f29a8c37bd906dfbe23208cc2b089da17e339a75.1684857051.git.geert+renesas@glider.be Fixes: 6e36203bc14ce147 ("PCI: rcar: Use PCI_SET_ERROR_RESPONSE after read which triggered an exception") Signed-off-by: Geert Uytterhoeven Signed-off-by: Lorenzo Pieralisi Reviewed-by: Yoshihiro Shimoda --- drivers/pci/controller/pcie-rcar-host.c | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/drivers/pci/controller/pcie-rcar-host.c b/drivers/pci/controller/pcie-rcar-host.c index e80e56b2a842..7ffcd0f5aa45 100644 --- a/drivers/pci/controller/pcie-rcar-host.c +++ b/drivers/pci/controller/pcie-rcar-host.c @@ -41,21 +41,6 @@ struct rcar_msi { int irq2; }; -#ifdef CONFIG_ARM -/* - * Here we keep a static copy of the remapped PCIe controller address. - * This is only used on aarch32 systems, all of which have one single - * PCIe controller, to provide quick access to the PCIe controller in - * the L1 link state fixup function, called from the ARM fault handler. - */ -static void __iomem *pcie_base; -/* - * Static copy of PCIe device pointer, so we can check whether the - * device is runtime suspended or not. - */ -static struct device *pcie_dev; -#endif - /* Structure representing the PCIe interface */ struct rcar_pcie_host { struct rcar_pcie pcie; @@ -879,12 +864,6 @@ static int rcar_pcie_get_resources(struct rcar_pcie_host *host) } host->msi.irq2 = i; -#ifdef CONFIG_ARM - /* Cache static copy for L1 link state fixup hook on aarch32 */ - pcie_base = pcie->base; - pcie_dev = pcie->dev; -#endif - return 0; err_irq2: -- cgit v1.2.3 From d3c6e265681285e046e0725dcbf5465482371e62 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 18 Apr 2023 16:13:00 -0500 Subject: PCI: Expand comment about sorting pci_ids.h entries Clarify the request to sort Vendor ID and Device ID entries by numeric value, not alphabetically. Signed-off-by: Bjorn Helgaas --- include/linux/pci_ids.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 45c3d62e616d..4d2001b86e6b 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2,7 +2,7 @@ /* * PCI Class, Vendor and Device IDs * - * Please keep sorted. + * Please keep sorted by numeric Vendor ID and Device ID. * * Do not add new entries to this file unless the definitions * are shared between multiple drivers. -- cgit v1.2.3 From 2aa5ac633259843f656eb6ecff4cf01e8e810c5e Mon Sep 17 00:00:00 2001 From: Sui Jingfeng Date: Wed, 31 May 2023 18:27:44 +0800 Subject: PCI: Add pci_clear_master() stub for non-CONFIG_PCI Add a pci_clear_master() stub when CONFIG_PCI is not set so drivers that support both PCI and platform devices don't need #ifdefs or extra Kconfig symbols for the PCI parts. [bhelgaas: commit log] Fixes: 6a479079c072 ("PCI: Add pci_clear_master() as opposite of pci_set_master()") Link: https://lore.kernel.org/r/20230531102744.2354313-1-suijingfeng@loongson.cn Signed-off-by: Sui Jingfeng Signed-off-by: Bjorn Helgaas Reviewed-by: Geert Uytterhoeven --- include/linux/pci.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/pci.h b/include/linux/pci.h index 60b8772b5bd4..c69a2cc1f412 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1903,6 +1903,7 @@ static inline int pci_dev_present(const struct pci_device_id *ids) #define pci_dev_put(dev) do { } while (0) static inline void pci_set_master(struct pci_dev *dev) { } +static inline void pci_clear_master(struct pci_dev *dev) { } static inline int pci_enable_device(struct pci_dev *dev) { return -EIO; } static inline void pci_disable_device(struct pci_dev *dev) { } static inline int pcim_enable_device(struct pci_dev *pdev) { return -EIO; } -- cgit v1.2.3 From 0b3dee602abf4a102a7a506d4b1c765355b27685 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 31 May 2023 10:57:13 +0100 Subject: PCI: Add PCI_EXT_CAP_ID_PL_32GT define Add the define for PCI_EXT_CAP_ID_PL_32GT for drivers that will want this whilst doing Gen5/Gen6 accesses. Link: https://lore.kernel.org/r/20230531095713.293229-1-ben.dooks@codethink.co.uk Signed-off-by: Ben Dooks Signed-off-by: Ben Dooks Signed-off-by: Bjorn Helgaas --- include/uapi/linux/pci_regs.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index dc2000e0fe3a..e5f558d96493 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -738,6 +738,7 @@ #define PCI_EXT_CAP_ID_DVSEC 0x23 /* Designated Vendor-Specific */ #define PCI_EXT_CAP_ID_DLF 0x25 /* Data Link Feature */ #define PCI_EXT_CAP_ID_PL_16GT 0x26 /* Physical Layer 16.0 GT/s */ +#define PCI_EXT_CAP_ID_PL_32GT 0x2A /* Physical Layer 32.0 GT/s */ #define PCI_EXT_CAP_ID_DOE 0x2E /* Data Object Exchange */ #define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_DOE -- cgit v1.2.3 From c6f54cf44c3d05510f8f292a1782105c087797ba Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 21 Apr 2023 13:09:39 +0300 Subject: PCI: of: Propagate firmware node by calling device_set_node() Insulate pci_set_of_node() and pci_set_bus_of_node() from possible changes to fwnode_handle implementation by using device_set_node() instead of open-coding dev->dev.fwnode assignments. Link: https://lore.kernel.org/r/20230421100939.68225-1-andriy.shevchenko@linux.intel.com Signed-off-by: Andy Shevchenko Signed-off-by: Bjorn Helgaas --- drivers/pci/of.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/pci/of.c b/drivers/pci/of.c index 2c25f4fa0225..e51219f9f523 100644 --- a/drivers/pci/of.c +++ b/drivers/pci/of.c @@ -39,16 +39,14 @@ int pci_set_of_node(struct pci_dev *dev) return -ENODEV; } - dev->dev.of_node = node; - dev->dev.fwnode = &node->fwnode; + device_set_node(&dev->dev, of_fwnode_handle(node)); return 0; } void pci_release_of_node(struct pci_dev *dev) { of_node_put(dev->dev.of_node); - dev->dev.of_node = NULL; - dev->dev.fwnode = NULL; + device_set_node(&dev->dev, NULL); } void pci_set_bus_of_node(struct pci_bus *bus) @@ -63,17 +61,13 @@ void pci_set_bus_of_node(struct pci_bus *bus) bus->self->external_facing = true; } - bus->dev.of_node = node; - - if (bus->dev.of_node) - bus->dev.fwnode = &bus->dev.of_node->fwnode; + device_set_node(&bus->dev, of_fwnode_handle(node)); } void pci_release_bus_of_node(struct pci_bus *bus) { of_node_put(bus->dev.of_node); - bus->dev.of_node = NULL; - bus->dev.fwnode = NULL; + device_set_node(&bus->dev, NULL); } struct device_node * __weak pcibios_get_phb_of_node(struct pci_bus *bus) -- cgit v1.2.3 From 7b3ba09febf409117a6f5b3e8ae10d503a972fee Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 25 Apr 2023 09:47:51 +0300 Subject: PCI/PM: Shorten pci_bridge_wait_for_secondary_bus() wait time for slow links With slow links (<= 5GT/s) active link reporting is not mandatory, so if a device is disconnected during system sleep we might end up waiting for it to respond for ~60s, which slows down resume time. PCIe r6.0, sec 6.6.1, mandates that software must wait for at least 1s before it can assume a device is broken, so use that minimum requirement for slow links and bail out if the device doesn't respond within 1s. However, if the port supports active link reporting we can wait longer as we do with the fast links. This should make system resume time faster for slow links as well while still following the PCIe spec. While there move the PCI_RESET_WAIT constant into pci.c because it is not used outside of that file anymore. Link: https://lore.kernel.org/r/20230425064751.24951-1-mika.westerberg@linux.intel.com Signed-off-by: Mika Westerberg Signed-off-by: Bjorn Helgaas Reviewed-by: Lukas Wunner Reviewed-by: Kuppuswamy Sathyanarayanan --- drivers/pci/pci.c | 49 +++++++++++++++++++++++++++++++++++++------------ drivers/pci/pci.h | 7 ------- 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 5ede93222bc1..578bf0d3ec3c 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -64,6 +64,13 @@ struct pci_pme_device { #define PME_TIMEOUT 1000 /* How long between PME checks */ +/* + * Following exit from Conventional Reset, devices must be ready within 1 sec + * (PCIe r6.0 sec 6.6.1). A D3cold to D0 transition implies a Conventional + * Reset (PCIe r6.0 sec 5.8). + */ +#define PCI_RESET_WAIT 1000 /* msec */ + /* * Devices may extend the 1 sec period through Request Retry Status * completions (PCIe r6.0 sec 2.3.1). The spec does not provide an upper @@ -5011,11 +5018,9 @@ int pci_bridge_wait_for_secondary_bus(struct pci_dev *dev, char *reset_type) * * However, 100 ms is the minimum and the PCIe spec says the * software must allow at least 1s before it can determine that the - * device that did not respond is a broken device. There is - * evidence that 100 ms is not always enough, for example certain - * Titan Ridge xHCI controller does not always respond to - * configuration requests if we only wait for 100 ms (see - * https://bugzilla.kernel.org/show_bug.cgi?id=203885). + * device that did not respond is a broken device. Also device can + * take longer than that to respond if it indicates so through Request + * Retry Status completions. * * Therefore we wait for 100 ms and check for the device presence * until the timeout expires. @@ -5024,16 +5029,36 @@ int pci_bridge_wait_for_secondary_bus(struct pci_dev *dev, char *reset_type) return 0; if (pcie_get_speed_cap(dev) <= PCIE_SPEED_5_0GT) { + u16 status; + pci_dbg(dev, "waiting %d ms for downstream link\n", delay); msleep(delay); - } else { - pci_dbg(dev, "waiting %d ms for downstream link, after activation\n", - delay); - if (!pcie_wait_for_link_delay(dev, true, delay)) { - /* Did not train, no need to wait any further */ - pci_info(dev, "Data Link Layer Link Active not set in 1000 msec\n"); + + if (!pci_dev_wait(child, reset_type, PCI_RESET_WAIT - delay)) + return 0; + + /* + * If the port supports active link reporting we now check + * whether the link is active and if not bail out early with + * the assumption that the device is not present anymore. + */ + if (!dev->link_active_reporting) return -ENOTTY; - } + + pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &status); + if (!(status & PCI_EXP_LNKSTA_DLLLA)) + return -ENOTTY; + + return pci_dev_wait(child, reset_type, + PCIE_RESET_READY_POLL_MS - PCI_RESET_WAIT); + } + + pci_dbg(dev, "waiting %d ms for downstream link, after activation\n", + delay); + if (!pcie_wait_for_link_delay(dev, true, delay)) { + /* Did not train, no need to wait any further */ + pci_info(dev, "Data Link Layer Link Active not set in 1000 msec\n"); + return -ENOTTY; } return pci_dev_wait(child, reset_type, diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 2475098f6518..d09e8f39e429 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -64,13 +64,6 @@ struct pci_cap_saved_state *pci_find_saved_ext_cap(struct pci_dev *dev, #define PCI_PM_D3HOT_WAIT 10 /* msec */ #define PCI_PM_D3COLD_WAIT 100 /* msec */ -/* - * Following exit from Conventional Reset, devices must be ready within 1 sec - * (PCIe r6.0 sec 6.6.1). A D3cold to D0 transition implies a Conventional - * Reset (PCIe r6.0 sec 5.8). - */ -#define PCI_RESET_WAIT 1000 /* msec */ - void pci_update_current_state(struct pci_dev *dev, pci_power_t state); void pci_refresh_power_state(struct pci_dev *dev); int pci_power_up(struct pci_dev *dev); -- cgit v1.2.3 From 88d341716b83abd355558523186ca488918627ee Mon Sep 17 00:00:00 2001 From: Robin Murphy Date: Wed, 7 Jun 2023 18:18:47 +0100 Subject: PCI: Add function 1 DMA alias quirk for Marvell 88SE9235 Marvell's own product brief implies the 92xx series are a closely related family, and sure enough it turns out that 9235 seems to need the same quirk as the other three, although possibly only when certain ports are used. Link: https://lore.kernel.org/linux-iommu/2a699a99-545c-1324-e052-7d2f41fed1ae@yahoo.co.uk/ Link: https://lore.kernel.org/r/731507e05d70239aec96fcbfab6e65d8ce00edd2.1686157165.git.robin.murphy@arm.com Reported-by: Jason Adriaanse Signed-off-by: Robin Murphy Signed-off-by: Bjorn Helgaas Reviewed-by: Christoph Hellwig Cc: stable@vger.kernel.org --- drivers/pci/quirks.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index c1239706eeaf..d310dca5da41 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -4176,6 +4176,8 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9220, /* https://bugzilla.kernel.org/show_bug.cgi?id=42679#c49 */ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9230, quirk_dma_func1_alias); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9235, + quirk_dma_func1_alias); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_TTI, 0x0642, quirk_dma_func1_alias); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_TTI, 0x0645, -- cgit v1.2.3 From e54223275ba1bc6f704a6bab015fcd2ae4f72572 Mon Sep 17 00:00:00 2001 From: Ross Lagerwall Date: Thu, 25 May 2023 16:32:48 +0100 Subject: PCI: Release resource invalidated by coalescing When contiguous windows are coalesced by pci_register_host_bridge(), the second resource is expanded to include the first, and the first is invalidated and consequently not added to the bus. However, it remains in the resource hierarchy. For example, these windows: fec00000-fec7ffff : PCI Bus 0000:00 fec80000-fecbffff : PCI Bus 0000:00 are coalesced into this, where the first resource remains in the tree with start/end zeroed out: 00000000-00000000 : PCI Bus 0000:00 fec00000-fecbffff : PCI Bus 0000:00 In some cases (e.g. the Xen scratch region), this causes future calls to allocate_resource() to choose an inappropriate location which the caller cannot handle. Fix by releasing the zeroed-out resource and removing it from the resource hierarchy. [bhelgaas: commit log] Fixes: 7c3855c423b1 ("PCI: Coalesce host bridge contiguous apertures") Link: https://lore.kernel.org/r/20230525153248.712779-1-ross.lagerwall@citrix.com Signed-off-by: Ross Lagerwall Signed-off-by: Bjorn Helgaas Cc: stable@vger.kernel.org # v5.16+ --- drivers/pci/probe.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 0b2826c4a832..00ed20ac0dd6 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -997,8 +997,10 @@ static int pci_register_host_bridge(struct pci_host_bridge *bridge) resource_list_for_each_entry_safe(window, n, &resources) { offset = window->offset; res = window->res; - if (!res->flags && !res->start && !res->end) + if (!res->flags && !res->start && !res->end) { + release_resource(res); continue; + } list_move_tail(&window->node, &bridge->windows); -- cgit v1.2.3 From ba3da66783184ca94a6c1be2a6a03d20d8889b14 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 9 Jun 2023 17:24:57 -0500 Subject: PCI: Unexport pci_save_aer_state() pci_save_aer_state() and pci_restore_aer_state() are only used in drivers/pci, so don't expose them to the rest of the kernel. No functional change intended. Link: https://lore.kernel.org/r/20230609222500.1267795-2-helgaas@kernel.org Signed-off-by: Bjorn Helgaas Reviewed-by: Stefan Roese Reviewed-by: Kuppuswamy Sathyanarayanan --- drivers/pci/pci.h | 4 ++++ include/linux/aer.h | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 2475098f6518..a97a735e6623 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -686,6 +686,8 @@ extern const struct attribute_group aer_stats_attr_group; void pci_aer_clear_fatal_status(struct pci_dev *dev); int pci_aer_clear_status(struct pci_dev *dev); int pci_aer_raw_clear_status(struct pci_dev *dev); +void pci_save_aer_state(struct pci_dev *dev); +void pci_restore_aer_state(struct pci_dev *dev); #else static inline void pci_no_aer(void) { } static inline void pci_aer_init(struct pci_dev *d) { } @@ -693,6 +695,8 @@ static inline void pci_aer_exit(struct pci_dev *d) { } static inline void pci_aer_clear_fatal_status(struct pci_dev *dev) { } static inline int pci_aer_clear_status(struct pci_dev *dev) { return -EINVAL; } static inline int pci_aer_raw_clear_status(struct pci_dev *dev) { return -EINVAL; } +static inline void pci_save_aer_state(struct pci_dev *dev) { } +static inline void pci_restore_aer_state(struct pci_dev *dev) { } #endif #ifdef CONFIG_ACPI diff --git a/include/linux/aer.h b/include/linux/aer.h index 97f64ba1b34a..3a3ab05e13fd 100644 --- a/include/linux/aer.h +++ b/include/linux/aer.h @@ -45,8 +45,6 @@ struct aer_capability_regs { int pci_enable_pcie_error_reporting(struct pci_dev *dev); int pci_disable_pcie_error_reporting(struct pci_dev *dev); int pci_aer_clear_nonfatal_status(struct pci_dev *dev); -void pci_save_aer_state(struct pci_dev *dev); -void pci_restore_aer_state(struct pci_dev *dev); #else static inline int pci_enable_pcie_error_reporting(struct pci_dev *dev) { @@ -60,8 +58,6 @@ static inline int pci_aer_clear_nonfatal_status(struct pci_dev *dev) { return -EINVAL; } -static inline void pci_save_aer_state(struct pci_dev *dev) {} -static inline void pci_restore_aer_state(struct pci_dev *dev) {} #endif void cper_print_aer(struct pci_dev *dev, int aer_severity, -- cgit v1.2.3 From a6378a7a1c7d95229f10fa00ce9b1925d6453ff0 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 9 Jun 2023 17:24:58 -0500 Subject: Documentation: PCI: Drop recommendation to configure AER Capability Since f26e58bf6f54 ("PCI/AER: Enable error reporting when AER is native"), the PCI core enables PCIe device error reporting for all devices during enumeration, so drivers don't need to do it. Remove the recommendation for drivers to configure AER and call pci_enable_pcie_error_reporting() themselves. Also remove the suggestion that drivers may change AER mask and severity registers. Ownership of these registers is negotiated between the OS and platform firmware. If firmware owns these registers, the OS must not change them. Link: https://lore.kernel.org/r/20230609222500.1267795-3-helgaas@kernel.org Signed-off-by: Bjorn Helgaas Reviewed-by: Dave Jiang Reviewed-by: Stefan Roese Reviewed-by: Kuppuswamy Sathyanarayanan --- Documentation/PCI/pcieaer-howto.rst | 56 ++----------------------------------- 1 file changed, 2 insertions(+), 54 deletions(-) diff --git a/Documentation/PCI/pcieaer-howto.rst b/Documentation/PCI/pcieaer-howto.rst index 0b36b9ebfa4b..c98a229ea9f5 100644 --- a/Documentation/PCI/pcieaer-howto.rst +++ b/Documentation/PCI/pcieaer-howto.rst @@ -96,8 +96,8 @@ Documentation/ABI/testing/sysfs-bus-pci-devices-aer_stats Developer Guide =============== -To enable AER aware support requires a software driver to configure -the AER capability structure within its device and to provide callbacks. +To enable AER aware support requires a software driver to provide +callbacks. To support AER better, developers need understand how AER does work firstly. @@ -135,15 +135,6 @@ hierarchy and links. These errors do not include any device specific errors because device specific errors will still get sent directly to the device driver. -Configure the AER capability structure --------------------------------------- - -AER aware drivers of PCI Express component need change the device -control registers to enable AER. They also could change AER registers, -including mask and severity registers. Helper function -pci_enable_pcie_error_reporting could be used to enable AER. See -section 3.3. - Provide callbacks ----------------- @@ -212,31 +203,6 @@ to reset the link. If error_detected returns PCI_ERS_RESULT_CAN_RECOVER and reset_link returns PCI_ERS_RESULT_RECOVERED, the error handling goes to mmio_enabled. -helper functions ----------------- -:: - - int pci_enable_pcie_error_reporting(struct pci_dev *dev); - -pci_enable_pcie_error_reporting enables the device to send error -messages to root port when an error is detected. Note that devices -don't enable the error reporting by default, so device drivers need -call this function to enable it. - -:: - - int pci_disable_pcie_error_reporting(struct pci_dev *dev); - -pci_disable_pcie_error_reporting disables the device to send error -messages to root port when an error is detected. - -:: - - int pci_aer_clear_nonfatal_status(struct pci_dev *dev);` - -pci_aer_clear_nonfatal_status clears non-fatal errors in the uncorrectable -error status register. - Frequent Asked Questions ------------------------ @@ -257,24 +223,6 @@ A: Fatal error recovery will fail if the errors are reported by the upstream ports who are attached by the service driver. -Q: - How does this infrastructure deal with driver that is not PCI - Express aware? - -A: - This infrastructure calls the error callback functions of the - driver when an error happens. But if the driver is not aware of - PCI Express, the device might not report its own errors to root - port. - -Q: - What modifications will that driver need to make it compatible - with the PCI Express AER Root driver? - -A: - It could call the helper functions to enable AER in devices and - cleanup uncorrectable status register. Pls. refer to section 3.3. - Software error injection ======================== -- cgit v1.2.3 From f142badf4645bd5ab72987d5595edbfae22a6d0b Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 9 Jun 2023 17:24:59 -0500 Subject: Documentation: PCI: Update cross references to .rst files Change references to *.txt to *.rst to match the current filenames. Link: https://lore.kernel.org/r/20230609222500.1267795-4-helgaas@kernel.org Signed-off-by: Bjorn Helgaas Reviewed-by: Stefan Roese Reviewed-by: Kuppuswamy Sathyanarayanan --- Documentation/PCI/pci-error-recovery.rst | 2 +- Documentation/PCI/pcieaer-howto.rst | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/PCI/pci-error-recovery.rst b/Documentation/PCI/pci-error-recovery.rst index 9981d330da8f..c237596f67e3 100644 --- a/Documentation/PCI/pci-error-recovery.rst +++ b/Documentation/PCI/pci-error-recovery.rst @@ -364,7 +364,7 @@ Note, however, not all failures are truly "permanent". Some are caused by over-heating, some by a poorly seated card. Many PCI error events are caused by software bugs, e.g. DMA's to wild addresses or bogus split transactions due to programming -errors. See the discussion in powerpc/eeh-pci-error-recovery.txt +errors. See the discussion in Documentation/powerpc/eeh-pci-error-recovery.rst for additional detail on real-life experience of the causes of software errors. diff --git a/Documentation/PCI/pcieaer-howto.rst b/Documentation/PCI/pcieaer-howto.rst index c98a229ea9f5..3f91d54af770 100644 --- a/Documentation/PCI/pcieaer-howto.rst +++ b/Documentation/PCI/pcieaer-howto.rst @@ -160,8 +160,8 @@ when performing error recovery actions. Data struct pci_driver has a pointer, err_handler, to point to pci_error_handlers who consists of a couple of callback function pointers. AER driver follows the rules defined in -pci-error-recovery.txt except pci express specific parts (e.g. -reset_link). Pls. refer to pci-error-recovery.txt for detailed +pci-error-recovery.rst except pci express specific parts (e.g. +reset_link). Pls. refer to pci-error-recovery.rst for detailed definitions of the callbacks. Below sections specify when to call the error callback functions. -- cgit v1.2.3 From 11502feab423cbbbaae47c0672409840b04037d5 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 9 Jun 2023 17:25:00 -0500 Subject: Documentation: PCI: Tidy AER documentation Consistently use: PCIe previously PCIe, PCI Express, or pci express Root Port previously Root Port or root port Endpoint previously EndPoint or endpoint AER previously AER or aer please previously pls Also update a few awkward wordings. Link: https://lore.kernel.org/r/20230609222500.1267795-5-helgaas@kernel.org Signed-off-by: Bjorn Helgaas Reviewed-by: Stefan Roese Reviewed-by: Kuppuswamy Sathyanarayanan --- Documentation/PCI/pcieaer-howto.rst | 131 ++++++++++++++++++------------------ 1 file changed, 65 insertions(+), 66 deletions(-) diff --git a/Documentation/PCI/pcieaer-howto.rst b/Documentation/PCI/pcieaer-howto.rst index 3f91d54af770..e00d63971695 100644 --- a/Documentation/PCI/pcieaer-howto.rst +++ b/Documentation/PCI/pcieaer-howto.rst @@ -16,62 +16,61 @@ Overview About this guide ---------------- -This guide describes the basics of the PCI Express Advanced Error +This guide describes the basics of the PCI Express (PCIe) Advanced Error Reporting (AER) driver and provides information on how to use it, as -well as how to enable the drivers of endpoint devices to conform with -PCI Express AER driver. +well as how to enable the drivers of Endpoint devices to conform with +the PCIe AER driver. -What is the PCI Express AER Driver? ------------------------------------ +What is the PCIe AER Driver? +---------------------------- -PCI Express error signaling can occur on the PCI Express link itself -or on behalf of transactions initiated on the link. PCI Express +PCIe error signaling can occur on the PCIe link itself +or on behalf of transactions initiated on the link. PCIe defines two error reporting paradigms: the baseline capability and the Advanced Error Reporting capability. The baseline capability is -required of all PCI Express components providing a minimum defined +required of all PCIe components providing a minimum defined set of error reporting requirements. Advanced Error Reporting -capability is implemented with a PCI Express advanced error reporting +capability is implemented with a PCIe Advanced Error Reporting extended capability structure providing more robust error reporting. -The PCI Express AER driver provides the infrastructure to support PCI -Express Advanced Error Reporting capability. The PCI Express AER -driver provides three basic functions: +The PCIe AER driver provides the infrastructure to support PCIe Advanced +Error Reporting capability. The PCIe AER driver provides three basic +functions: - Gathers the comprehensive error information if errors occurred. - Reports error to the users. - Performs error recovery actions. -AER driver only attaches root ports which support PCI-Express AER -capability. +The AER driver only attaches to Root Ports and RCECs that support the PCIe +AER capability. User Guide ========== -Include the PCI Express AER Root Driver into the Linux Kernel -------------------------------------------------------------- +Include the PCIe AER Root Driver into the Linux Kernel +------------------------------------------------------ -The PCI Express AER Root driver is a Root Port service driver attached -to the PCI Express Port Bus driver. If a user wants to use it, the driver -has to be compiled. Option CONFIG_PCIEAER supports this capability. It -depends on CONFIG_PCIEPORTBUS, so pls. set CONFIG_PCIEPORTBUS=y and -CONFIG_PCIEAER = y. +The PCIe AER driver is a Root Port service driver attached +via the PCIe Port Bus driver. If a user wants to use it, the driver +must be compiled. It is enabled with CONFIG_PCIEAER, which +depends on CONFIG_PCIEPORTBUS. -Load PCI Express AER Root Driver --------------------------------- +Load PCIe AER Root Driver +------------------------- Some systems have AER support in firmware. Enabling Linux AER support at -the same time the firmware handles AER may result in unpredictable +the same time the firmware handles AER would result in unpredictable behavior. Therefore, Linux does not handle AER events unless the firmware -grants AER control to the OS via the ACPI _OSC method. See the PCI FW 3.0 +grants AER control to the OS via the ACPI _OSC method. See the PCI Firmware Specification for details regarding _OSC usage. AER error output ---------------- When a PCIe AER error is captured, an error message will be output to -console. If it's a correctable error, it is output as a warning. +console. If it's a correctable error, it is output as an info message. Otherwise, it is printed as an error. So users could choose different log level to filter out correctable error messages. @@ -82,9 +81,9 @@ Below shows an example:: 0000:50:00.0: [20] Unsupported Request (First) 0000:50:00.0: TLP Header: 04000001 00200a03 05010000 00050100 -In the example, 'Requester ID' means the ID of the device who sends -the error message to root port. Pls. refer to pci express specs for -other fields. +In the example, 'Requester ID' means the ID of the device that sent +the error message to the Root Port. Please refer to PCIe specs for other +fields. AER Statistics / Counters ------------------------- @@ -96,41 +95,41 @@ Documentation/ABI/testing/sysfs-bus-pci-devices-aer_stats Developer Guide =============== -To enable AER aware support requires a software driver to provide -callbacks. +To enable error recovery, a software driver must provide callbacks. -To support AER better, developers need understand how AER does work -firstly. +To support AER better, developers need to understand how AER works. -PCI Express errors are classified into two types: correctable errors -and uncorrectable errors. This classification is based on the impacts +PCIe errors are classified into two types: correctable errors +and uncorrectable errors. This classification is based on the impact of those errors, which may result in degraded performance or function failure. Correctable errors pose no impacts on the functionality of the -interface. The PCI Express protocol can recover without any software +interface. The PCIe protocol can recover without any software intervention or any loss of data. These errors are detected and -corrected by hardware. Unlike correctable errors, uncorrectable +corrected by hardware. + +Unlike correctable errors, uncorrectable errors impact functionality of the interface. Uncorrectable errors -can cause a particular transaction or a particular PCI Express link +can cause a particular transaction or a particular PCIe link to be unreliable. Depending on those error conditions, uncorrectable errors are further classified into non-fatal errors and fatal errors. Non-fatal errors cause the particular transaction to be unreliable, -but the PCI Express link itself is fully functional. Fatal errors, on +but the PCIe link itself is fully functional. Fatal errors, on the other hand, cause the link to be unreliable. -When AER is enabled, a PCI Express device will automatically send an -error message to the PCIe root port above it when the device captures +When PCIe error reporting is enabled, a device will automatically send an +error message to the Root Port above it when it captures an error. The Root Port, upon receiving an error reporting message, -internally processes and logs the error message in its PCI Express -capability structure. Error information being logged includes storing +internally processes and logs the error message in its AER +Capability structure. Error information being logged includes storing the error reporting agent's requestor ID into the Error Source Identification Registers and setting the error bits of the Root Error -Status Register accordingly. If AER error reporting is enabled in Root -Error Command Register, the Root Port generates an interrupt if an +Status Register accordingly. If AER error reporting is enabled in the Root +Error Command Register, the Root Port generates an interrupt when an error is detected. -Note that the errors as described above are related to the PCI Express +Note that the errors as described above are related to the PCIe hierarchy and links. These errors do not include any device specific errors because device specific errors will still get sent directly to the device driver. @@ -138,14 +137,14 @@ the device driver. Provide callbacks ----------------- -callback reset_link to reset pci express link -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +callback reset_link to reset PCIe link +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -This callback is used to reset the pci express physical link when a -fatal error happens. The root port aer service driver provides a -default reset_link function, but different upstream ports might -have different specifications to reset pci express link, so all -upstream ports should provide their own reset_link functions. +This callback is used to reset the PCIe physical link when a +fatal error happens. The Root Port AER service driver provides a +default reset_link function, but different Upstream Ports might +have different specifications to reset the PCIe link, so +Upstream Port drivers may provide their own reset_link functions. Section 3.2.2.2 provides more detailed info on when to call reset_link. @@ -153,24 +152,24 @@ reset_link. PCI error-recovery callbacks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The PCI Express AER Root driver uses error callbacks to coordinate +The PCIe AER Root driver uses error callbacks to coordinate with downstream device drivers associated with a hierarchy in question when performing error recovery actions. Data struct pci_driver has a pointer, err_handler, to point to pci_error_handlers who consists of a couple of callback function -pointers. AER driver follows the rules defined in -pci-error-recovery.rst except pci express specific parts (e.g. -reset_link). Pls. refer to pci-error-recovery.rst for detailed +pointers. The AER driver follows the rules defined in +pci-error-recovery.rst except PCIe-specific parts (e.g. +reset_link). Please refer to pci-error-recovery.rst for detailed definitions of the callbacks. -Below sections specify when to call the error callback functions. +The sections below specify when to call the error callback functions. Correctable errors ~~~~~~~~~~~~~~~~~~ Correctable errors pose no impacts on the functionality of -the interface. The PCI Express protocol can recover without any +the interface. The PCIe protocol can recover without any software intervention or any loss of data. These errors do not require any recovery actions. The AER driver clears the device's correctable error status register accordingly and logs these errors. @@ -181,12 +180,12 @@ Non-correctable (non-fatal and fatal) errors If an error message indicates a non-fatal error, performing link reset at upstream is not required. The AER driver calls error_detected(dev, pci_channel_io_normal) to all drivers associated within a hierarchy in -question. for example:: +question. For example:: - EndPoint<==>DownstreamPort B<==>UpstreamPort A<==>RootPort + Endpoint <==> Downstream Port B <==> Upstream Port A <==> Root Port -If Upstream port A captures an AER error, the hierarchy consists of -Downstream port B and EndPoint. +If Upstream Port A captures an AER error, the hierarchy consists of +Downstream Port B and Endpoint. A driver may return PCI_ERS_RESULT_CAN_RECOVER, PCI_ERS_RESULT_DISCONNECT, or PCI_ERS_RESULT_NEED_RESET, depending on @@ -207,7 +206,7 @@ Frequent Asked Questions ------------------------ Q: - What happens if a PCI Express device driver does not provide an + What happens if a PCIe device driver does not provide an error recovery handler (pci_driver->err_handler is equal to NULL)? A: @@ -244,5 +243,5 @@ from: https://git.kernel.org/cgit/linux/kernel/git/gong.chen/aer-inject.git/ -More information about aer-inject can be found in the document comes -with its source code. +More information about aer-inject can be found in the document in +its source code. -- cgit v1.2.3 From 42adbdc74c9a62ad3587be4d8a18941711023673 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Sun, 11 Jun 2023 18:19:27 +0100 Subject: PCI: Initialize dev->link_active_reporting earlier Determine whether Data Link Layer Link Active Reporting is available before calling any fixups so that the cached value can be used there and later on. [bhelgaas: move to set_pcie_port_type() where other PCIe init is done] Link: https://lore.kernel.org/r/alpine.DEB.2.21.2305310122210.59226@angie.orcam.me.uk Signed-off-by: Maciej W. Rozycki Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 0b2826c4a832..782925bac64a 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -820,7 +820,6 @@ static void pci_set_bus_speed(struct pci_bus *bus) pcie_capability_read_dword(bridge, PCI_EXP_LNKCAP, &linkcap); bus->max_bus_speed = pcie_link_speed[linkcap & PCI_EXP_LNKCAP_SLS]; - bridge->link_active_reporting = !!(linkcap & PCI_EXP_LNKCAP_DLLLARC); pcie_capability_read_word(bridge, PCI_EXP_LNKSTA, &linksta); pcie_update_link_speed(bus, linksta); @@ -1527,6 +1526,7 @@ void set_pcie_port_type(struct pci_dev *pdev) { int pos; u16 reg16; + u32 reg32; int type; struct pci_dev *parent; @@ -1540,6 +1540,10 @@ void set_pcie_port_type(struct pci_dev *pdev) pci_read_config_dword(pdev, pos + PCI_EXP_DEVCAP, &pdev->devcap); pdev->pcie_mpss = FIELD_GET(PCI_EXP_DEVCAP_PAYLOAD, pdev->devcap); + pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, ®32); + if (reg32 & PCI_EXP_LNKCAP_DLLLARC) + pdev->link_active_reporting = 1; + parent = pci_upstream_bridge(pdev); if (!parent) return; -- cgit v1.2.3 From 1f087398dbbefe3fe48fc3816b9603d66fec36fc Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Sun, 11 Jun 2023 18:19:14 +0100 Subject: PCI: pciehp: Rely on dev->link_active_reporting Use dev->link_active_reporting to determine whether Data Link Layer Link Active Reporting is available rather than re-retrieving the capability. Link: https://lore.kernel.org/r/alpine.DEB.2.21.2305310028150.59226@angie.orcam.me.uk Signed-off-by: Maciej W. Rozycki Signed-off-by: Bjorn Helgaas Reviewed-by: Lukas Wunner --- drivers/pci/hotplug/pciehp_hpc.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index f8c70115b691..6bf898e1ca62 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -984,7 +984,7 @@ static inline int pcie_hotplug_depth(struct pci_dev *dev) struct controller *pcie_init(struct pcie_device *dev) { struct controller *ctrl; - u32 slot_cap, slot_cap2, link_cap; + u32 slot_cap, slot_cap2; u8 poweron; struct pci_dev *pdev = dev->port; struct pci_bus *subordinate = pdev->subordinate; @@ -1030,9 +1030,6 @@ struct controller *pcie_init(struct pcie_device *dev) if (dmi_first_match(inband_presence_disabled_dmi_table)) ctrl->inband_presence_disabled = 1; - /* Check if Data Link Layer Link Active Reporting is implemented */ - pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &link_cap); - /* Clear all remaining event bits in Slot Status register. */ pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD | @@ -1051,7 +1048,7 @@ struct controller *pcie_init(struct pcie_device *dev) FLAG(slot_cap, PCI_EXP_SLTCAP_EIP), FLAG(slot_cap, PCI_EXP_SLTCAP_NCCS), FLAG(slot_cap2, PCI_EXP_SLTCAP2_IBPD), - FLAG(link_cap, PCI_EXP_LNKCAP_DLLLARC), + FLAG(pdev->link_active_reporting, true), pdev->broken_cmd_compl ? " (with Cmd Compl erratum)" : ""); /* -- cgit v1.2.3 From 1541a21305ceb10fcf3f7cbb23f3e1a00bbf1789 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Sun, 11 Jun 2023 18:19:32 +0100 Subject: powerpc/eeh: Rely on dev->link_active_reporting Use dev->link_active_reporting to determine whether Data Link Layer Link Active Reporting is available rather than re-retrieving the capability. Link: https://lore.kernel.org/r/alpine.DEB.2.21.2305310124100.59226@angie.orcam.me.uk Signed-off-by: Maciej W. Rozycki Signed-off-by: Bjorn Helgaas --- arch/powerpc/kernel/eeh_pe.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/powerpc/kernel/eeh_pe.c b/arch/powerpc/kernel/eeh_pe.c index d2873d17d2b1..e0ce81279624 100644 --- a/arch/powerpc/kernel/eeh_pe.c +++ b/arch/powerpc/kernel/eeh_pe.c @@ -671,9 +671,8 @@ static void eeh_bridge_check_link(struct eeh_dev *edev) eeh_ops->write_config(edev, cap + PCI_EXP_LNKCTL, 2, val); /* Check link */ - eeh_ops->read_config(edev, cap + PCI_EXP_LNKCAP, 4, &val); - if (!(val & PCI_EXP_LNKCAP_DLLLARC)) { - eeh_edev_dbg(edev, "No link reporting capability (0x%08x) \n", val); + if (!edev->pdev->link_active_reporting) { + eeh_edev_dbg(edev, "No link reporting capability\n"); msleep(1000); return; } -- cgit v1.2.3 From 3bff63ee0303b12bf6727bd18e529bc7f59f6426 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Sun, 11 Jun 2023 18:19:36 +0100 Subject: net/mlx5: Rely on dev->link_active_reporting Use dev->link_active_reporting to determine whether Data Link Layer Link Active Reporting is available rather than re-retrieving the capability. Link: https://lore.kernel.org/r/alpine.DEB.2.21.2305310125370.59226@angie.orcam.me.uk Signed-off-by: Maciej W. Rozycki Signed-off-by: Bjorn Helgaas --- drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c b/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c index 50022e7565f1..9ebebd963dab 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c @@ -307,7 +307,6 @@ static int mlx5_pci_link_toggle(struct mlx5_core_dev *dev) unsigned long timeout; struct pci_dev *sdev; int cap, err; - u32 reg32; /* Check that all functions under the pci bridge are PFs of * this device otherwise fail this function. @@ -346,11 +345,8 @@ static int mlx5_pci_link_toggle(struct mlx5_core_dev *dev) return err; /* Check link */ - err = pci_read_config_dword(bridge, cap + PCI_EXP_LNKCAP, ®32); - if (err) - return err; - if (!(reg32 & PCI_EXP_LNKCAP_DLLLARC)) { - mlx5_core_warn(dev, "No PCI link reporting capability (0x%08x)\n", reg32); + if (!bridge->link_active_reporting) { + mlx5_core_warn(dev, "No PCI link reporting capability\n"); msleep(1000); goto restore; } -- cgit v1.2.3 From b1689799772a6f4180f918b0ff66e264a3db9796 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Sun, 11 Jun 2023 18:19:45 +0100 Subject: PCI/ASPM: Use distinct local vars in pcie_retrain_link() Use separate local variables to hold the respective values retrieved from the Link Control Register and the Link Status Register. Improves readability and it makes it possible for the compiler to detect actual uninitialised use should this code change in the future. [bhelgaas: reorder to clean up before exposing to PCI core] Link: https://lore.kernel.org/r/alpine.DEB.2.21.2306110252260.64925@angie.orcam.me.uk Signed-off-by: Maciej W. Rozycki Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aspm.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 66d7514ca111..0048c417a78d 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -197,30 +197,31 @@ static bool pcie_retrain_link(struct pcie_link_state *link) { struct pci_dev *parent = link->pdev; unsigned long end_jiffies; - u16 reg16; + u16 lnkctl; + u16 lnksta; - pcie_capability_read_word(parent, PCI_EXP_LNKCTL, ®16); - reg16 |= PCI_EXP_LNKCTL_RL; - pcie_capability_write_word(parent, PCI_EXP_LNKCTL, reg16); + pcie_capability_read_word(parent, PCI_EXP_LNKCTL, &lnkctl); + lnkctl |= PCI_EXP_LNKCTL_RL; + pcie_capability_write_word(parent, PCI_EXP_LNKCTL, lnkctl); if (parent->clear_retrain_link) { /* * Due to an erratum in some devices the Retrain Link bit * needs to be cleared again manually to allow the link * training to succeed. */ - reg16 &= ~PCI_EXP_LNKCTL_RL; - pcie_capability_write_word(parent, PCI_EXP_LNKCTL, reg16); + lnkctl &= ~PCI_EXP_LNKCTL_RL; + pcie_capability_write_word(parent, PCI_EXP_LNKCTL, lnkctl); } /* Wait for link training end. Break out after waiting for timeout */ end_jiffies = jiffies + LINK_RETRAIN_TIMEOUT; do { - pcie_capability_read_word(parent, PCI_EXP_LNKSTA, ®16); - if (!(reg16 & PCI_EXP_LNKSTA_LT)) + pcie_capability_read_word(parent, PCI_EXP_LNKSTA, &lnksta); + if (!(lnksta & PCI_EXP_LNKSTA_LT)) break; msleep(1); } while (time_before(jiffies, end_jiffies)); - return !(reg16 & PCI_EXP_LNKSTA_LT); + return !(lnksta & PCI_EXP_LNKSTA_LT); } /* -- cgit v1.2.3 From 3bbc3c72c4b8982ecb719df6685dc7067def0904 Mon Sep 17 00:00:00 2001 From: Richard Zhu Date: Thu, 8 Dec 2022 14:05:34 +0800 Subject: PCI: imx6: Save and restore root port MSI control in suspend and resume The imx6 PCI host controller suffers from a HW integration bug whereby the MSI enable bit in the root port MSI capability enables/disables MSIs interrupts for all downstream components in the PCI tree. This requires, as implemented in 75cb8d20c112 ("PCI: imx: Enable MSI from downstream components") that the root port MSI enable bit should be set in order for downstream PCI devices MSIs to function. The MSI enable bit programming might be lost during the suspend and should be re-stored during resume. Save the MSI control during suspend and restore it in resume. Link: https://lore.kernel.org/r/1670479534-22154-1-git-send-email-hongxing.zhu@nxp.com Signed-off-by: Richard Zhu [lpieralisi@kernel.org: commit log] Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/dwc/pci-imx6.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 52906f999f2b..27aaa2a6bf39 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -80,6 +80,7 @@ struct imx6_pcie { struct clk *pcie; struct clk *pcie_aux; struct regmap *iomuxc_gpr; + u16 msi_ctrl; u32 controller_id; struct reset_control *pciephy_reset; struct reset_control *apps_reset; @@ -1178,6 +1179,26 @@ pm_turnoff_sleep: usleep_range(1000, 10000); } +static void imx6_pcie_msi_save_restore(struct imx6_pcie *imx6_pcie, bool save) +{ + u8 offset; + u16 val; + struct dw_pcie *pci = imx6_pcie->pci; + + if (pci_msi_enabled()) { + offset = dw_pcie_find_capability(pci, PCI_CAP_ID_MSI); + if (save) { + val = dw_pcie_readw_dbi(pci, offset + PCI_MSI_FLAGS); + imx6_pcie->msi_ctrl = val; + } else { + dw_pcie_dbi_ro_wr_en(pci); + val = imx6_pcie->msi_ctrl; + dw_pcie_writew_dbi(pci, offset + PCI_MSI_FLAGS, val); + dw_pcie_dbi_ro_wr_dis(pci); + } + } +} + static int imx6_pcie_suspend_noirq(struct device *dev) { struct imx6_pcie *imx6_pcie = dev_get_drvdata(dev); @@ -1186,6 +1207,7 @@ static int imx6_pcie_suspend_noirq(struct device *dev) if (!(imx6_pcie->drvdata->flags & IMX6_PCIE_FLAG_SUPPORTS_SUSPEND)) return 0; + imx6_pcie_msi_save_restore(imx6_pcie, true); imx6_pcie_pm_turnoff(imx6_pcie); imx6_pcie_stop_link(imx6_pcie->pci); imx6_pcie_host_exit(pp); @@ -1205,6 +1227,7 @@ static int imx6_pcie_resume_noirq(struct device *dev) ret = imx6_pcie_host_init(pp); if (ret) return ret; + imx6_pcie_msi_save_restore(imx6_pcie, false); dw_pcie_setup_rc(pp); if (imx6_pcie->link_is_up) -- cgit v1.2.3 From a33d700e8eea76c62120cb3dbf5e01328f18319a Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 19 Jun 2023 20:34:00 +0530 Subject: PCI: qcom: Disable write access to read only registers for IP v2.3.3 In the post init sequence of v2.9.0, write access to read only registers are not disabled after updating the registers. Fix it by disabling the access after register update. Link: https://lore.kernel.org/r/20230619150408.8468-2-manivannan.sadhasivam@linaro.org Fixes: 5d76117f070d ("PCI: qcom: Add support for IPQ8074 PCIe controller") Signed-off-by: Manivannan Sadhasivam Signed-off-by: Lorenzo Pieralisi Cc: --- drivers/pci/controller/dwc/pcie-qcom.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 4ab30892f6ef..ef385d36d653 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -836,6 +836,8 @@ static int qcom_pcie_post_init_2_3_3(struct qcom_pcie *pcie) writel(PCI_EXP_DEVCTL2_COMP_TMOUT_DIS, pci->dbi_base + offset + PCI_EXP_DEVCTL2); + dw_pcie_dbi_ro_wr_dis(pci); + return 0; } -- cgit v1.2.3 From 60f0072d7fb7996b9a524ef0d152e21205473192 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 19 Jun 2023 20:34:01 +0530 Subject: PCI: qcom: Use DWC helpers for modifying the read-only DBI registers DWC core already exposes dw_pcie_dbi_ro_wr_{en/dis} helper APIs for enabling and disabling the write access to read only DBI registers. So let's use them instead of doing it manually. Also, the existing code doesn't disable the write access when it's done. This is also fixed now. Link: https://lore.kernel.org/r/20230619150408.8468-3-manivannan.sadhasivam@linaro.org Fixes: 5d76117f070d ("PCI: qcom: Add support for IPQ8074 PCIe controller") Signed-off-by: Manivannan Sadhasivam Signed-off-by: Lorenzo Pieralisi Reviewed-by: Dmitry Baryshkov --- drivers/pci/controller/dwc/pcie-qcom.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index ef385d36d653..01795ee7ce45 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -61,7 +61,6 @@ /* DBI registers */ #define AXI_MSTR_RESP_COMP_CTRL0 0x818 #define AXI_MSTR_RESP_COMP_CTRL1 0x81c -#define MISC_CONTROL_1_REG 0x8bc /* MHI registers */ #define PARF_DEBUG_CNT_PM_LINKST_IN_L2 0xc04 @@ -132,9 +131,6 @@ /* AXI_MSTR_RESP_COMP_CTRL1 register fields */ #define CFG_BRIDGE_SB_INIT BIT(0) -/* MISC_CONTROL_1_REG register fields */ -#define DBI_RO_WR_EN 1 - /* PCI_EXP_SLTCAP register fields */ #define PCIE_CAP_SLOT_POWER_LIMIT_VAL FIELD_PREP(PCI_EXP_SLTCAP_SPLV, 250) #define PCIE_CAP_SLOT_POWER_LIMIT_SCALE FIELD_PREP(PCI_EXP_SLTCAP_SPLS, 1) @@ -826,7 +822,9 @@ static int qcom_pcie_post_init_2_3_3(struct qcom_pcie *pcie) writel(0, pcie->parf + PARF_Q2A_FLUSH); writel(PCI_COMMAND_MASTER, pci->dbi_base + PCI_COMMAND); - writel(DBI_RO_WR_EN, pci->dbi_base + MISC_CONTROL_1_REG); + + dw_pcie_dbi_ro_wr_en(pci); + writel(PCIE_CAP_SLOT_VAL, pci->dbi_base + offset + PCI_EXP_SLTCAP); val = readl(pci->dbi_base + offset + PCI_EXP_LNKCAP); -- cgit v1.2.3 From 200b8f85f2021362adcc8efb575652a2aa44c099 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 19 Jun 2023 20:34:02 +0530 Subject: PCI: qcom: Disable write access to read only registers for IP v2.9.0 In the post init sequence of v2.9.0, write access to read only registers are not disabled after updating the registers. Fix it by disabling the access after register update. While at it, let's also add a newline after existing dw_pcie_dbi_ro_wr_en() guard function to align with rest of the driver. Link: https://lore.kernel.org/r/20230619150408.8468-4-manivannan.sadhasivam@linaro.org Fixes: 0cf7c2efe8ac ("PCI: qcom: Add IPQ60xx support") Signed-off-by: Manivannan Sadhasivam Signed-off-by: Lorenzo Pieralisi Reviewed-by: Dmitry Baryshkov --- drivers/pci/controller/dwc/pcie-qcom.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 01795ee7ce45..391a45d1e70a 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -1136,6 +1136,7 @@ static int qcom_pcie_post_init_2_9_0(struct qcom_pcie *pcie) writel(0, pcie->parf + PARF_Q2A_FLUSH); dw_pcie_dbi_ro_wr_en(pci); + writel(PCIE_CAP_SLOT_VAL, pci->dbi_base + offset + PCI_EXP_SLTCAP); val = readl(pci->dbi_base + offset + PCI_EXP_LNKCAP); @@ -1145,6 +1146,8 @@ static int qcom_pcie_post_init_2_9_0(struct qcom_pcie *pcie) writel(PCI_EXP_DEVCTL2_COMP_TMOUT_DIS, pci->dbi_base + offset + PCI_EXP_DEVCTL2); + dw_pcie_dbi_ro_wr_dis(pci); + for (i = 0; i < 256; i++) writel(0, pcie->parf + PARF_BDF_TO_SID_TABLE_N + (4 * i)); -- cgit v1.2.3 From a54db86ddc153484e36266aa2da458a3d9ba0d64 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 19 Jun 2023 20:34:03 +0530 Subject: PCI: qcom: Do not advertise hotplug capability for IPs v2.7.0 and v1.9.0 SoCs making use of Qcom PCIe controller IPs v2.7.0 and v1.9.0 do not support hotplug functionality. But the hotplug capability bit is set by default in the hardware. This causes the kernel PCI core to register hotplug service for the controller and send hotplug commands to it. But those commands will timeout generating messages as below during boot and suspend/resume. [ 5.782159] pcieport 0001:00:00.0: pciehp: Timeout on hotplug command 0x03c0 (issued 2020 msec ago) [ 5.810161] pcieport 0001:00:00.0: pciehp: Timeout on hotplug command 0x03c0 (issued 2048 msec ago) [ 7.838162] pcieport 0001:00:00.0: pciehp: Timeout on hotplug command 0x07c0 (issued 2020 msec ago) [ 7.870159] pcieport 0001:00:00.0: pciehp: Timeout on hotplug command 0x07c0 (issued 2052 msec ago) This not only spams the console output but also induces a delay of a couple of seconds. To fix this issue, let's clear the HPC bit in PCI_EXP_SLTCAP register as a part of the post init sequence to not advertise the hotplug capability for the controller. Link: https://lore.kernel.org/r/20230619150408.8468-5-manivannan.sadhasivam@linaro.org Signed-off-by: Manivannan Sadhasivam Signed-off-by: Lorenzo Pieralisi Reviewed-by: Dmitry Baryshkov --- drivers/pci/controller/dwc/pcie-qcom.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 391a45d1e70a..8f448156eccc 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -270,6 +270,20 @@ static int qcom_pcie_start_link(struct dw_pcie *pci) return 0; } +static void qcom_pcie_clear_hpc(struct dw_pcie *pci) +{ + u16 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); + u32 val; + + dw_pcie_dbi_ro_wr_en(pci); + + val = readl(pci->dbi_base + offset + PCI_EXP_SLTCAP); + val &= ~PCI_EXP_SLTCAP_HPC; + writel(val, pci->dbi_base + offset + PCI_EXP_SLTCAP); + + dw_pcie_dbi_ro_wr_dis(pci); +} + static void qcom_pcie_2_1_0_ltssm_enable(struct qcom_pcie *pcie) { u32 val; @@ -966,6 +980,13 @@ err_disable_regulators: return ret; } +static int qcom_pcie_post_init_2_7_0(struct qcom_pcie *pcie) +{ + qcom_pcie_clear_hpc(pcie->pci); + + return 0; +} + 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; @@ -1272,6 +1293,7 @@ static const struct qcom_pcie_ops ops_2_3_3 = { static const struct qcom_pcie_ops ops_2_7_0 = { .get_resources = qcom_pcie_get_resources_2_7_0, .init = qcom_pcie_init_2_7_0, + .post_init = qcom_pcie_post_init_2_7_0, .deinit = qcom_pcie_deinit_2_7_0, .ltssm_enable = qcom_pcie_2_3_2_ltssm_enable, }; @@ -1280,6 +1302,7 @@ static const struct qcom_pcie_ops ops_2_7_0 = { 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, .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, -- cgit v1.2.3 From 11bce06b21a0d5f002156b2bc6573329f285a927 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 19 Jun 2023 20:34:04 +0530 Subject: PCI: qcom: Do not advertise hotplug capability for IPs v2.3.3 and v2.9.0 SoCs making use of Qcom PCIe controller IPs v2.3.3 and v2.9.0 do not support hotplug functionality. But the hotplug capability bit is set by default in the hardware. This causes the kernel PCI core to register hotplug service for the controller and send hotplug commands to it. But those commands will timeout generating messages as below during boot and suspend/resume. [ 5.782159] pcieport 0001:00:00.0: pciehp: Timeout on hotplug command 0x03c0 (issued 2020 msec ago) [ 5.810161] pcieport 0001:00:00.0: pciehp: Timeout on hotplug command 0x03c0 (issued 2048 msec ago) [ 7.838162] pcieport 0001:00:00.0: pciehp: Timeout on hotplug command 0x07c0 (issued 2020 msec ago) [ 7.870159] pcieport 0001:00:00.0: pciehp: Timeout on hotplug command 0x07c0 (issued 2052 msec ago) This not only spams the console output but also induces a delay of a couple of seconds. To fix this issue, let's not set the HPC bit in PCI_EXP_SLTCAP register as a part of the post init sequence to not advertise the hotplug capability for the controller. Link: https://lore.kernel.org/r/20230619150408.8468-6-manivannan.sadhasivam@linaro.org Tested-by: Sricharan Ramabadhran Signed-off-by: Manivannan Sadhasivam Signed-off-by: Lorenzo Pieralisi Reviewed-by: Dmitry Baryshkov --- drivers/pci/controller/dwc/pcie-qcom.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 8f448156eccc..64b6a8c6a99d 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -140,7 +140,6 @@ PCI_EXP_SLTCAP_AIP | \ PCI_EXP_SLTCAP_PIP | \ PCI_EXP_SLTCAP_HPS | \ - PCI_EXP_SLTCAP_HPC | \ PCI_EXP_SLTCAP_EIP | \ PCIE_CAP_SLOT_POWER_LIMIT_VAL | \ PCIE_CAP_SLOT_POWER_LIMIT_SCALE) -- cgit v1.2.3 From 25966e78d3035b6356d9284ad07b3033212c691b Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 19 Jun 2023 20:34:05 +0530 Subject: PCI: qcom: Do not advertise hotplug capability for IP v2.3.2 SoCs making use of Qcom PCIe controller IP v2.3.2 do not support hotplug functionality. But the hotplug capability bit is set by default in the hardware. This causes the kernel PCI core to register hotplug service for the controller and send hotplug commands to it. But those commands will timeout generating messages as below during boot and suspend/resume. [ 5.782159] pcieport 0001:00:00.0: pciehp: Timeout on hotplug command 0x03c0 (issued 2020 msec ago) [ 5.810161] pcieport 0001:00:00.0: pciehp: Timeout on hotplug command 0x03c0 (issued 2048 msec ago) [ 7.838162] pcieport 0001:00:00.0: pciehp: Timeout on hotplug command 0x07c0 (issued 2020 msec ago) [ 7.870159] pcieport 0001:00:00.0: pciehp: Timeout on hotplug command 0x07c0 (issued 2052 msec ago) This not only spams the console output but also induces a delay of a couple of seconds. To fix this issue, let's clear the HPC bit in PCI_EXP_SLTCAP register as a part of the post init sequence to not advertise the hotplug capability for the controller. Link: https://lore.kernel.org/r/20230619150408.8468-7-manivannan.sadhasivam@linaro.org Signed-off-by: Manivannan Sadhasivam Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/dwc/pcie-qcom.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 64b6a8c6a99d..9c8dfd224e6e 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -616,6 +616,8 @@ static int qcom_pcie_post_init_2_3_2(struct qcom_pcie *pcie) val |= EN; writel(val, pcie->parf + PARF_AXI_MSTR_WR_ADDR_HALT_V2); + qcom_pcie_clear_hpc(pcie->pci); + return 0; } -- cgit v1.2.3 From e35d13a5ff372244c9f9d1ea01532d26698cb046 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 19 Jun 2023 20:34:06 +0530 Subject: PCI: qcom: Use post init sequence of IP v2.3.2 for v2.4.0 The post init sequence of IP v2.4.0 is same as v2.3.2. So let's reuse the v2.3.2 sequence which now also disables hotplug capability of the controller as it is not at all supported on any SoCs making use of this IP. Link: https://lore.kernel.org/r/20230619150408.8468-8-manivannan.sadhasivam@linaro.org Signed-off-by: Manivannan Sadhasivam Signed-off-by: Lorenzo Pieralisi Reviewed-by: Dmitry Baryshkov --- drivers/pci/controller/dwc/pcie-qcom.c | 30 +----------------------------- 1 file changed, 1 insertion(+), 29 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 9c8dfd224e6e..e6db9e551752 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -703,34 +703,6 @@ static int qcom_pcie_init_2_4_0(struct qcom_pcie *pcie) return 0; } -static int qcom_pcie_post_init_2_4_0(struct qcom_pcie *pcie) -{ - u32 val; - - /* enable PCIe clocks and resets */ - val = readl(pcie->parf + PARF_PHY_CTRL); - val &= ~PHY_TEST_PWR_DOWN; - writel(val, pcie->parf + PARF_PHY_CTRL); - - /* change DBI base address */ - writel(0, pcie->parf + PARF_DBI_BASE_ADDR); - - /* MAC PHY_POWERDOWN MUX DISABLE */ - val = readl(pcie->parf + PARF_SYS_CTRL); - val &= ~MAC_PHY_POWERDOWN_IN_P2_D_MUX_EN; - writel(val, pcie->parf + PARF_SYS_CTRL); - - val = readl(pcie->parf + PARF_MHI_CLOCK_RESET_CTRL); - val |= BYPASS; - writel(val, pcie->parf + PARF_MHI_CLOCK_RESET_CTRL); - - val = readl(pcie->parf + PARF_AXI_MSTR_WR_ADDR_HALT_V2); - val |= EN; - writel(val, pcie->parf + PARF_AXI_MSTR_WR_ADDR_HALT_V2); - - return 0; -} - static int qcom_pcie_get_resources_2_3_3(struct qcom_pcie *pcie) { struct qcom_pcie_resources_2_3_3 *res = &pcie->res.v2_3_3; @@ -1276,7 +1248,7 @@ static const struct qcom_pcie_ops ops_2_3_2 = { static const struct qcom_pcie_ops ops_2_4_0 = { .get_resources = qcom_pcie_get_resources_2_4_0, .init = qcom_pcie_init_2_4_0, - .post_init = qcom_pcie_post_init_2_4_0, + .post_init = qcom_pcie_post_init_2_3_2, .deinit = qcom_pcie_deinit_2_4_0, .ltssm_enable = qcom_pcie_2_3_2_ltssm_enable, }; -- cgit v1.2.3 From fa2dc252868403d3de9f3589f725a026b51c6f72 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 19 Jun 2023 20:34:07 +0530 Subject: PCI: qcom: Do not advertise hotplug capability for IP v1.0.0 SoCs making use of Qcom PCIe controller IP v1.0.0 do not support hotplug functionality. But the hotplug capability bit is set by default in the hardware. This causes the kernel PCI core to register hotplug service for the controller and send hotplug commands to it. But those commands will timeout generating messages as below during boot and suspend/resume. [ 5.782159] pcieport 0001:00:00.0: pciehp: Timeout on hotplug command 0x03c0 (issued 2020 msec ago) [ 5.810161] pcieport 0001:00:00.0: pciehp: Timeout on hotplug command 0x03c0 (issued 2048 msec ago) [ 7.838162] pcieport 0001:00:00.0: pciehp: Timeout on hotplug command 0x07c0 (issued 2020 msec ago) [ 7.870159] pcieport 0001:00:00.0: pciehp: Timeout on hotplug command 0x07c0 (issued 2052 msec ago) This not only spams the console output but also induces a delay of a couple of seconds. To fix this issue, let's clear the HPC bit in PCI_EXP_SLTCAP register as a part of the post init sequence to not advertise the hotplug capability for the controller. Link: https://lore.kernel.org/r/20230619150408.8468-9-manivannan.sadhasivam@linaro.org Signed-off-by: Manivannan Sadhasivam Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/dwc/pcie-qcom.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index e6db9e551752..612266fb849a 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -521,6 +521,8 @@ static int qcom_pcie_post_init_1_0_0(struct qcom_pcie *pcie) writel(val, pcie->parf + PARF_AXI_MSTR_WR_ADDR_HALT); } + qcom_pcie_clear_hpc(pcie->pci); + return 0; } -- cgit v1.2.3 From 1fdecc5bc8e81b0afba17876ff99b4131d0e03aa Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 19 Jun 2023 20:34:08 +0530 Subject: PCI: qcom: Do not advertise hotplug capability for IP v2.1.0 SoCs making use of Qcom PCIe controller IP v2.1.0 do not support hotplug functionality. But the hotplug capability bit is set by default in the hardware. This causes the kernel PCI core to register hotplug service for the controller and send hotplug commands to it. But those commands will timeout generating messages as below during boot and suspend/resume. [ 5.782159] pcieport 0001:00:00.0: pciehp: Timeout on hotplug command 0x03c0 (issued 2020 msec ago) [ 5.810161] pcieport 0001:00:00.0: pciehp: Timeout on hotplug command 0x03c0 (issued 2048 msec ago) [ 7.838162] pcieport 0001:00:00.0: pciehp: Timeout on hotplug command 0x07c0 (issued 2020 msec ago) [ 7.870159] pcieport 0001:00:00.0: pciehp: Timeout on hotplug command 0x07c0 (issued 2052 msec ago) This not only spams the console output but also induces a delay of a couple of seconds. To fix this issue, let's clear the HPC bit in PCI_EXP_SLTCAP register as a part of the post init sequence to not advertise the hotplug capability for the controller. Link: https://lore.kernel.org/r/20230619150408.8468-10-manivannan.sadhasivam@linaro.org Signed-off-by: Manivannan Sadhasivam Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/dwc/pcie-qcom.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 612266fb849a..7a87a47eb7ed 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -438,6 +438,8 @@ static int qcom_pcie_post_init_2_1_0(struct qcom_pcie *pcie) writel(CFG_BRIDGE_SB_INIT, pci->dbi_base + AXI_MSTR_RESP_COMP_CTRL1); + qcom_pcie_clear_hpc(pcie->pci); + return 0; } -- cgit v1.2.3 From fd6e6e38ebe5db99b8eeab0abef8cc930301a677 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Wed, 14 Jun 2023 15:09:06 -0500 Subject: PCI/ASPM: Avoid unnecessary pcie_link_state use [bhelgaas: extract from expose patch, reorder to clean up before exposing] Link: https://lore.kernel.org/r/alpine.DEB.2.21.2306110229010.64925@angie.orcam.me.uk Signed-off-by: Maciej W. Rozycki Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aspm.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 0048c417a78d..e2cfff3a0a2e 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -193,30 +193,29 @@ static void pcie_clkpm_cap_init(struct pcie_link_state *link, int blacklist) link->clkpm_disable = blacklist ? 1 : 0; } -static bool pcie_retrain_link(struct pcie_link_state *link) +static bool pcie_retrain_link(struct pci_dev *pdev) { - struct pci_dev *parent = link->pdev; unsigned long end_jiffies; u16 lnkctl; u16 lnksta; - pcie_capability_read_word(parent, PCI_EXP_LNKCTL, &lnkctl); + pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &lnkctl); lnkctl |= PCI_EXP_LNKCTL_RL; - pcie_capability_write_word(parent, PCI_EXP_LNKCTL, lnkctl); - if (parent->clear_retrain_link) { + pcie_capability_write_word(pdev, PCI_EXP_LNKCTL, lnkctl); + if (pdev->clear_retrain_link) { /* * Due to an erratum in some devices the Retrain Link bit * needs to be cleared again manually to allow the link * training to succeed. */ lnkctl &= ~PCI_EXP_LNKCTL_RL; - pcie_capability_write_word(parent, PCI_EXP_LNKCTL, lnkctl); + pcie_capability_write_word(pdev, PCI_EXP_LNKCTL, lnkctl); } /* Wait for link training end. Break out after waiting for timeout */ end_jiffies = jiffies + LINK_RETRAIN_TIMEOUT; do { - pcie_capability_read_word(parent, PCI_EXP_LNKSTA, &lnksta); + pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnksta); if (!(lnksta & PCI_EXP_LNKSTA_LT)) break; msleep(1); @@ -290,7 +289,7 @@ static void pcie_aspm_configure_common_clock(struct pcie_link_state *link) reg16 &= ~PCI_EXP_LNKCTL_CCC; pcie_capability_write_word(parent, PCI_EXP_LNKCTL, reg16); - if (pcie_retrain_link(link)) + if (pcie_retrain_link(link->pdev)) return; /* Training failed. Restore common clock configurations */ -- cgit v1.2.3 From 3c0ec896a4b42bc4751c71cac5996d23d3b648ae Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Sun, 11 Jun 2023 18:19:49 +0100 Subject: PCI/ASPM: Factor out waiting for link training to complete Move code polling for the Link Training bit to clear into a function of its own. [bhelgaas: reorder to clean up before exposing to PCI core] Link: https://lore.kernel.org/r/alpine.DEB.2.21.2306111605060.64925@angie.orcam.me.uk Signed-off-by: Maciej W. Rozycki Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aspm.c | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index e2cfff3a0a2e..eaaacf24e16c 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -193,12 +193,32 @@ static void pcie_clkpm_cap_init(struct pcie_link_state *link, int blacklist) link->clkpm_disable = blacklist ? 1 : 0; } -static bool pcie_retrain_link(struct pci_dev *pdev) +/** + * pcie_wait_for_link_status - Wait for link training end + * @pdev: Device whose link to wait for. + * + * Return TRUE if successful, or FALSE if training has not completed + * within LINK_RETRAIN_TIMEOUT jiffies. + */ +static bool pcie_wait_for_link_status(struct pci_dev *pdev) { unsigned long end_jiffies; - u16 lnkctl; u16 lnksta; + end_jiffies = jiffies + LINK_RETRAIN_TIMEOUT; + do { + pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnksta); + if (!(lnksta & PCI_EXP_LNKSTA_LT)) + break; + msleep(1); + } while (time_before(jiffies, end_jiffies)); + return !(lnksta & PCI_EXP_LNKSTA_LT); +} + +static bool pcie_retrain_link(struct pci_dev *pdev) +{ + u16 lnkctl; + pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &lnkctl); lnkctl |= PCI_EXP_LNKCTL_RL; pcie_capability_write_word(pdev, PCI_EXP_LNKCTL, lnkctl); @@ -212,15 +232,7 @@ static bool pcie_retrain_link(struct pci_dev *pdev) pcie_capability_write_word(pdev, PCI_EXP_LNKCTL, lnkctl); } - /* Wait for link training end. Break out after waiting for timeout */ - end_jiffies = jiffies + LINK_RETRAIN_TIMEOUT; - do { - pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnksta); - if (!(lnksta & PCI_EXP_LNKSTA_LT)) - break; - msleep(1); - } while (time_before(jiffies, end_jiffies)); - return !(lnksta & PCI_EXP_LNKSTA_LT); + return pcie_wait_for_link_status(pdev); } /* -- cgit v1.2.3 From 07a8d698de50c4740ac6f709c43e23a6da6e4dbc Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Sun, 11 Jun 2023 18:19:23 +0100 Subject: PCI: Execute quirk_enable_clear_retrain_link() earlier Make quirk_enable_clear_retrain_link() an early quirk so that any later fixups can rely on dev->clear_retrain_link to have been already initialised. [bhelgaas: reorder to just before it becomes possible to call pcie_retrain_link() earlier] Link: https://lore.kernel.org/r/alpine.DEB.2.21.2305310049000.59226@angie.orcam.me.uk Signed-off-by: Maciej W. Rozycki Signed-off-by: Bjorn Helgaas --- drivers/pci/quirks.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index f4e2a88729fd..5c0a438f7e64 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2420,9 +2420,9 @@ static void quirk_enable_clear_retrain_link(struct pci_dev *dev) dev->clear_retrain_link = 1; pci_info(dev, "Enable PCIe Retrain Link quirk\n"); } -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_PERICOM, 0xe110, quirk_enable_clear_retrain_link); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_PERICOM, 0xe111, quirk_enable_clear_retrain_link); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_PERICOM, 0xe130, quirk_enable_clear_retrain_link); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_PERICOM, 0xe110, quirk_enable_clear_retrain_link); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_PERICOM, 0xe111, quirk_enable_clear_retrain_link); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_PERICOM, 0xe130, quirk_enable_clear_retrain_link); static void fixup_rev1_53c810(struct pci_dev *dev) { -- cgit v1.2.3 From 33a176abcc4cd4ed3d65512ed96d7b73f2565ed7 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Sun, 11 Jun 2023 18:19:19 +0100 Subject: PCI: Export PCIe link retrain timeout Convert LINK_RETRAIN_TIMEOUT from jiffies to milliseconds, accordingly rename to PCIE_LINK_RETRAIN_TIMEOUT_MS, and make available via "pci.h" for the PCI core to use. Use in pcie_wait_for_link_delay(). Link: https://lore.kernel.org/r/alpine.DEB.2.21.2305310030280.59226@angie.orcam.me.uk Signed-off-by: Maciej W. Rozycki Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 2 +- drivers/pci/pci.h | 2 ++ drivers/pci/pcie/aspm.c | 6 ++---- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 5ede93222bc1..71645d568986 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -4867,7 +4867,7 @@ static int pci_pm_reset(struct pci_dev *dev, bool probe) static bool pcie_wait_for_link_delay(struct pci_dev *pdev, bool active, int delay) { - int timeout = 1000; + int timeout = PCIE_LINK_RETRAIN_TIMEOUT_MS; bool ret; u16 lnk_status; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 2475098f6518..d5fe253114f2 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -11,6 +11,8 @@ #define PCI_VSEC_ID_INTEL_TBT 0x1234 /* Thunderbolt */ +#define PCIE_LINK_RETRAIN_TIMEOUT_MS 1000 + extern const unsigned char pcie_link_speed[]; extern bool pci_early_dump; diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index eaaacf24e16c..721e5c787cf3 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -90,8 +90,6 @@ static const char *policy_str[] = { [POLICY_POWER_SUPERSAVE] = "powersupersave" }; -#define LINK_RETRAIN_TIMEOUT HZ - /* * The L1 PM substate capability is only implemented in function 0 in a * multi function device. @@ -198,14 +196,14 @@ static void pcie_clkpm_cap_init(struct pcie_link_state *link, int blacklist) * @pdev: Device whose link to wait for. * * Return TRUE if successful, or FALSE if training has not completed - * within LINK_RETRAIN_TIMEOUT jiffies. + * within PCIE_LINK_RETRAIN_TIMEOUT_MS milliseconds. */ static bool pcie_wait_for_link_status(struct pci_dev *pdev) { unsigned long end_jiffies; u16 lnksta; - end_jiffies = jiffies + LINK_RETRAIN_TIMEOUT; + end_jiffies = jiffies + msecs_to_jiffies(PCIE_LINK_RETRAIN_TIMEOUT_MS); do { pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnksta); if (!(lnksta & PCI_EXP_LNKSTA_LT)) -- cgit v1.2.3 From 37edd87eb621a96d33ee4eefe4b54cfc5a7e03df Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Sun, 11 Jun 2023 18:19:41 +0100 Subject: PCI: Export pcie_retrain_link() for use outside ASPM Export pcie_retrain_link() for link retrain needs outside ASPM. Struct pcie_link_state is local to ASPM and only used by pcie_retrain_link() to get at the associated PCI device, so change the operand and adjust the lone call site accordingly. Document the interface. No functional change at this point. Link: https://lore.kernel.org/r/alpine.DEB.2.21.2306110229010.64925@angie.orcam.me.uk Signed-off-by: Maciej W. Rozycki Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ drivers/pci/pci.h | 1 + drivers/pci/pcie/aspm.c | 42 ------------------------------------------ 3 files changed, 50 insertions(+), 42 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 71645d568986..47ceb8567b2b 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -4856,6 +4856,55 @@ static int pci_pm_reset(struct pci_dev *dev, bool probe) return pci_dev_wait(dev, "PM D3hot->D0", PCIE_RESET_READY_POLL_MS); } +/** + * pcie_wait_for_link_status - Wait for link training end + * @pdev: Device whose link to wait for. + * + * Return TRUE if successful, or FALSE if training has not completed + * within PCIE_LINK_RETRAIN_TIMEOUT_MS milliseconds. + */ +static bool pcie_wait_for_link_status(struct pci_dev *pdev) +{ + unsigned long end_jiffies; + u16 lnksta; + + end_jiffies = jiffies + msecs_to_jiffies(PCIE_LINK_RETRAIN_TIMEOUT_MS); + do { + pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnksta); + if (!(lnksta & PCI_EXP_LNKSTA_LT)) + break; + msleep(1); + } while (time_before(jiffies, end_jiffies)); + return !(lnksta & PCI_EXP_LNKSTA_LT); +} + +/** + * pcie_retrain_link - Request a link retrain and wait for it to complete + * @pdev: Device whose link to retrain. + * + * Return TRUE if successful, or FALSE if training has not completed + * within PCIE_LINK_RETRAIN_TIMEOUT_MS milliseconds. + */ +bool pcie_retrain_link(struct pci_dev *pdev) +{ + u16 lnkctl; + + pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &lnkctl); + lnkctl |= PCI_EXP_LNKCTL_RL; + pcie_capability_write_word(pdev, PCI_EXP_LNKCTL, lnkctl); + if (pdev->clear_retrain_link) { + /* + * Due to an erratum in some devices the Retrain Link bit + * needs to be cleared again manually to allow the link + * training to succeed. + */ + lnkctl &= ~PCI_EXP_LNKCTL_RL; + pcie_capability_write_word(pdev, PCI_EXP_LNKCTL, lnkctl); + } + + return pcie_wait_for_link_status(pdev); +} + /** * pcie_wait_for_link_delay - Wait until link is active or inactive * @pdev: Bridge device diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index d5fe253114f2..0d9671b20d17 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -565,6 +565,7 @@ pci_ers_result_t pcie_do_recovery(struct pci_dev *dev, pci_ers_result_t (*reset_subordinates)(struct pci_dev *pdev)); bool pcie_wait_for_link(struct pci_dev *pdev, bool active); +bool pcie_retrain_link(struct pci_dev *pdev); #ifdef CONFIG_PCIEASPM void pcie_aspm_init_link_state(struct pci_dev *pdev); void pcie_aspm_exit_link_state(struct pci_dev *pdev); diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 721e5c787cf3..0c5d392dc793 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -191,48 +191,6 @@ static void pcie_clkpm_cap_init(struct pcie_link_state *link, int blacklist) link->clkpm_disable = blacklist ? 1 : 0; } -/** - * pcie_wait_for_link_status - Wait for link training end - * @pdev: Device whose link to wait for. - * - * Return TRUE if successful, or FALSE if training has not completed - * within PCIE_LINK_RETRAIN_TIMEOUT_MS milliseconds. - */ -static bool pcie_wait_for_link_status(struct pci_dev *pdev) -{ - unsigned long end_jiffies; - u16 lnksta; - - end_jiffies = jiffies + msecs_to_jiffies(PCIE_LINK_RETRAIN_TIMEOUT_MS); - do { - pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnksta); - if (!(lnksta & PCI_EXP_LNKSTA_LT)) - break; - msleep(1); - } while (time_before(jiffies, end_jiffies)); - return !(lnksta & PCI_EXP_LNKSTA_LT); -} - -static bool pcie_retrain_link(struct pci_dev *pdev) -{ - u16 lnkctl; - - pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &lnkctl); - lnkctl |= PCI_EXP_LNKCTL_RL; - pcie_capability_write_word(pdev, PCI_EXP_LNKCTL, lnkctl); - if (pdev->clear_retrain_link) { - /* - * Due to an erratum in some devices the Retrain Link bit - * needs to be cleared again manually to allow the link - * training to succeed. - */ - lnkctl &= ~PCI_EXP_LNKCTL_RL; - pcie_capability_write_word(pdev, PCI_EXP_LNKCTL, lnkctl); - } - - return pcie_wait_for_link_status(pdev); -} - /* * pcie_aspm_configure_common_clock: check if the 2 ends of a link * could use common clock. If they are, configure them to use the -- cgit v1.2.3 From 680e9c47a2293bcc6a67a6f13f3b23d4c456885b Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Sun, 11 Jun 2023 18:19:53 +0100 Subject: PCI: Add support for polling DLLLA to pcie_retrain_link() Let the caller of pcie_retrain_link() specify whether they want to use the LT bit or the DLLLA bit of the Link Status Register to determine if link training has completed. It is up to the caller to verify whether the use of the DLLLA bit, the implementation of which is optional, is valid for the device requested. Link: https://lore.kernel.org/r/alpine.DEB.2.21.2306110310540.64925@angie.orcam.me.uk Signed-off-by: Maciej W. Rozycki Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 28 ++++++++++++++++++++-------- drivers/pci/pci.h | 2 +- drivers/pci/pcie/aspm.c | 2 +- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 47ceb8567b2b..d576f7fa86cd 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -4857,35 +4857,47 @@ static int pci_pm_reset(struct pci_dev *dev, bool probe) } /** - * pcie_wait_for_link_status - Wait for link training end + * pcie_wait_for_link_status - Wait for link status change * @pdev: Device whose link to wait for. + * @use_lt: Use the LT bit if TRUE, or the DLLLA bit if FALSE. + * @active: Waiting for active or inactive? * - * Return TRUE if successful, or FALSE if training has not completed - * within PCIE_LINK_RETRAIN_TIMEOUT_MS milliseconds. + * Return TRUE if successful, or FALSE if status has not changed within + * PCIE_LINK_RETRAIN_TIMEOUT_MS milliseconds. */ -static bool pcie_wait_for_link_status(struct pci_dev *pdev) +static bool pcie_wait_for_link_status(struct pci_dev *pdev, + bool use_lt, bool active) { + u16 lnksta_mask, lnksta_match; unsigned long end_jiffies; u16 lnksta; + lnksta_mask = use_lt ? PCI_EXP_LNKSTA_LT : PCI_EXP_LNKSTA_DLLLA; + lnksta_match = active ? lnksta_mask : 0; + end_jiffies = jiffies + msecs_to_jiffies(PCIE_LINK_RETRAIN_TIMEOUT_MS); do { pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnksta); - if (!(lnksta & PCI_EXP_LNKSTA_LT)) + if ((lnksta & lnksta_mask) == lnksta_match) break; msleep(1); } while (time_before(jiffies, end_jiffies)); - return !(lnksta & PCI_EXP_LNKSTA_LT); + return (lnksta & lnksta_mask) == lnksta_match; } /** * pcie_retrain_link - Request a link retrain and wait for it to complete * @pdev: Device whose link to retrain. + * @use_lt: Use the LT bit if TRUE, or the DLLLA bit if FALSE, for status. + * + * Retrain completion status is retrieved from the Link Status Register + * according to @use_lt. It is not verified whether the use of the DLLLA + * bit is valid. * * Return TRUE if successful, or FALSE if training has not completed * within PCIE_LINK_RETRAIN_TIMEOUT_MS milliseconds. */ -bool pcie_retrain_link(struct pci_dev *pdev) +bool pcie_retrain_link(struct pci_dev *pdev, bool use_lt) { u16 lnkctl; @@ -4902,7 +4914,7 @@ bool pcie_retrain_link(struct pci_dev *pdev) pcie_capability_write_word(pdev, PCI_EXP_LNKCTL, lnkctl); } - return pcie_wait_for_link_status(pdev); + return pcie_wait_for_link_status(pdev, use_lt, !use_lt); } /** diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 0d9671b20d17..6c257acbae90 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -565,7 +565,7 @@ pci_ers_result_t pcie_do_recovery(struct pci_dev *dev, pci_ers_result_t (*reset_subordinates)(struct pci_dev *pdev)); bool pcie_wait_for_link(struct pci_dev *pdev, bool active); -bool pcie_retrain_link(struct pci_dev *pdev); +bool pcie_retrain_link(struct pci_dev *pdev, bool use_lt); #ifdef CONFIG_PCIEASPM void pcie_aspm_init_link_state(struct pci_dev *pdev); void pcie_aspm_exit_link_state(struct pci_dev *pdev); diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 0c5d392dc793..99b8badddea5 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -257,7 +257,7 @@ static void pcie_aspm_configure_common_clock(struct pcie_link_state *link) reg16 &= ~PCI_EXP_LNKCTL_CCC; pcie_capability_write_word(parent, PCI_EXP_LNKCTL, reg16); - if (pcie_retrain_link(link->pdev)) + if (pcie_retrain_link(link->pdev, true)) return; /* Training failed. Restore common clock configurations */ -- cgit v1.2.3 From 7604bc294c19fe70fb7d9091731a950b16249c51 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Sun, 11 Jun 2023 18:19:57 +0100 Subject: PCI: Use pcie_wait_for_link_status() in pcie_wait_for_link_delay() Remove a DLLLA status bit polling loop from pcie_wait_for_link_delay() and call almost identical code in pcie_wait_for_link_status() instead. This reduces the lower bound on the polling interval from 10ms to 1ms, possibly increasing the CPU load on the system in favour to reducing the wait time. Link: https://lore.kernel.org/r/alpine.DEB.2.21.2306111611170.64925@angie.orcam.me.uk Signed-off-by: Maciej W. Rozycki Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index d576f7fa86cd..62c3a8bc83b3 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -4928,16 +4928,14 @@ bool pcie_retrain_link(struct pci_dev *pdev, bool use_lt) static bool pcie_wait_for_link_delay(struct pci_dev *pdev, bool active, int delay) { - int timeout = PCIE_LINK_RETRAIN_TIMEOUT_MS; bool ret; - u16 lnk_status; /* * Some controllers might not implement link active reporting. In this * case, we wait for 1000 ms + any delay requested by the caller. */ if (!pdev->link_active_reporting) { - msleep(timeout + delay); + msleep(PCIE_LINK_RETRAIN_TIMEOUT_MS + delay); return true; } @@ -4952,20 +4950,11 @@ static bool pcie_wait_for_link_delay(struct pci_dev *pdev, bool active, */ if (active) msleep(20); - for (;;) { - pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status); - ret = !!(lnk_status & PCI_EXP_LNKSTA_DLLLA); - if (ret == active) - break; - if (timeout <= 0) - break; - msleep(10); - timeout -= 10; - } + ret = pcie_wait_for_link_status(pdev, false, active); if (active && ret) msleep(delay); - return ret == active; + return ret; } /** -- cgit v1.2.3 From a89c82249c3763780522f763dd2e615e2ea114de Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Sun, 11 Jun 2023 18:20:10 +0100 Subject: PCI: Work around PCIe link training failures Attempt to handle cases such as with a downstream port of the ASMedia ASM2824 PCIe switch where link training never completes and the link continues switching between speeds indefinitely with the data link layer never reaching the active state. It has been observed with a downstream port of the ASMedia ASM2824 Gen 3 switch wired to the upstream port of the Pericom PI7C9X2G304 Gen 2 switch, using a Delock Riser Card PCI Express x1 > 2 x PCIe x1 device, P/N 41433, wired to a SiFive HiFive Unmatched board. In this setup the switches should negotiate a link speed of 5.0GT/s, falling back to 2.5GT/s if necessary. Instead the link continues oscillating between the two speeds, at the rate of 34-35 times per second, with link training reported repeatedly active ~84% of the time. Limiting the target link speed to 2.5GT/s with the upstream ASM2824 device makes the two switches communicate correctly. Removing the speed restriction afterwards makes the two devices switch to 5.0GT/s then. Make use of these observations and detect the inability to train the link by checking for the Data Link Layer Link Active status bit being off while the Link Bandwidth Management Status indicating that hardware has changed the link speed or width in an attempt to correct unreliable link operation. Restrict the speed to 2.5GT/s then with the Target Link Speed field, request a retrain and wait 200ms for the data link to go up. If this is successful, lift the restriction, letting the devices negotiate a higher speed. Also check for a 2.5GT/s speed restriction the firmware may have already arranged and lift it too with ports of devices known to continue working afterwards (currently only ASM2824), that already report their data link being up. [bhelgaas: reorder and squash stubs from https://lore.kernel.org/r/alpine.DEB.2.21.2306111619570.64925@angie.orcam.me.uk to avoid adding stubs that do nothing] Link: https://lore.kernel.org/r/alpine.DEB.2.21.2203022037020.56670@angie.orcam.me.uk/ Link: https://source.denx.de/u-boot/u-boot/-/commit/a398a51ccc68 Link: https://lore.kernel.org/r/alpine.DEB.2.21.2305310038540.59226@angie.orcam.me.uk Signed-off-by: Maciej W. Rozycki Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 2 ++ drivers/pci/pci.h | 5 +++ drivers/pci/probe.c | 2 ++ drivers/pci/quirks.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 102 insertions(+) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 62c3a8bc83b3..f599d321c881 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -4951,6 +4951,8 @@ static bool pcie_wait_for_link_delay(struct pci_dev *pdev, bool active, if (active) msleep(20); ret = pcie_wait_for_link_status(pdev, false, active); + if (active && !ret) + ret = pcie_failed_link_retrain(pdev); if (active && ret) msleep(delay); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 6c257acbae90..e3a468a58cd2 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -543,6 +543,7 @@ void pci_acs_init(struct pci_dev *dev); int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags); int pci_dev_specific_enable_acs(struct pci_dev *dev); int pci_dev_specific_disable_acs_redir(struct pci_dev *dev); +bool pcie_failed_link_retrain(struct pci_dev *dev); #else static inline int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags) @@ -557,6 +558,10 @@ static inline int pci_dev_specific_disable_acs_redir(struct pci_dev *dev) { return -ENOTTY; } +static inline bool pcie_failed_link_retrain(struct pci_dev *dev) +{ + return false; +} #endif /* PCI error reporting and recovery */ diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 782925bac64a..f547db0a728f 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2550,6 +2550,8 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus) dma_set_max_seg_size(&dev->dev, 65536); dma_set_seg_boundary(&dev->dev, 0xffffffff); + pcie_failed_link_retrain(dev); + /* Fix up broken headers */ pci_fixup_device(pci_fixup_header, dev); diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 5c0a438f7e64..a46678563b33 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -33,6 +33,99 @@ #include #include "pci.h" +/* + * Retrain the link of a downstream PCIe port by hand if necessary. + * + * This is needed at least where a downstream port of the ASMedia ASM2824 + * Gen 3 switch is wired to the upstream port of the Pericom PI7C9X2G304 + * Gen 2 switch, and observed with the Delock Riser Card PCI Express x1 > + * 2 x PCIe x1 device, P/N 41433, plugged into the SiFive HiFive Unmatched + * board. + * + * In such a configuration the switches are supposed to negotiate the link + * speed of preferably 5.0GT/s, falling back to 2.5GT/s. However the link + * continues switching between the two speeds indefinitely and the data + * link layer never reaches the active state, with link training reported + * repeatedly active ~84% of the time. Forcing the target link speed to + * 2.5GT/s with the upstream ASM2824 device makes the two switches talk to + * each other correctly however. And more interestingly retraining with a + * higher target link speed afterwards lets the two successfully negotiate + * 5.0GT/s. + * + * With the ASM2824 we can rely on the otherwise optional Data Link Layer + * Link Active status bit and in the failed link training scenario it will + * be off along with the Link Bandwidth Management Status indicating that + * hardware has changed the link speed or width in an attempt to correct + * unreliable link operation. For a port that has been left unconnected + * both bits will be clear. So use this information to detect the problem + * rather than polling the Link Training bit and watching out for flips or + * at least the active status. + * + * Since the exact nature of the problem isn't known and in principle this + * could trigger where an ASM2824 device is downstream rather upstream, + * apply this erratum workaround to any downstream ports as long as they + * support Link Active reporting and have the Link Control 2 register. + * Restrict the speed to 2.5GT/s then with the Target Link Speed field, + * request a retrain and wait 200ms for the data link to go up. + * + * If this turns out successful and we know by the Vendor:Device ID it is + * safe to do so, then lift the restriction, letting the devices negotiate + * a higher speed. Also check for a similar 2.5GT/s speed restriction the + * firmware may have already arranged and lift it with ports that already + * report their data link being up. + * + * Return TRUE if the link has been successfully retrained, otherwise FALSE. + */ +bool pcie_failed_link_retrain(struct pci_dev *dev) +{ + static const struct pci_device_id ids[] = { + { PCI_VDEVICE(ASMEDIA, 0x2824) }, /* ASMedia ASM2824 */ + {} + }; + u16 lnksta, lnkctl2; + + if (!pci_is_pcie(dev) || !pcie_downstream_port(dev) || + !pcie_cap_has_lnkctl2(dev) || !dev->link_active_reporting) + return false; + + pcie_capability_read_word(dev, PCI_EXP_LNKCTL2, &lnkctl2); + pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta); + if ((lnksta & (PCI_EXP_LNKSTA_LBMS | PCI_EXP_LNKSTA_DLLLA)) == + PCI_EXP_LNKSTA_LBMS) { + pci_info(dev, "broken device, retraining non-functional downstream link at 2.5GT/s\n"); + + lnkctl2 &= ~PCI_EXP_LNKCTL2_TLS; + lnkctl2 |= PCI_EXP_LNKCTL2_TLS_2_5GT; + pcie_capability_write_word(dev, PCI_EXP_LNKCTL2, lnkctl2); + + if (!pcie_retrain_link(dev, false)) { + pci_info(dev, "retraining failed\n"); + return false; + } + + pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta); + } + + if ((lnksta & PCI_EXP_LNKSTA_DLLLA) && + (lnkctl2 & PCI_EXP_LNKCTL2_TLS) == PCI_EXP_LNKCTL2_TLS_2_5GT && + pci_match_id(ids, dev)) { + u32 lnkcap; + + pci_info(dev, "removing 2.5GT/s downstream link speed restriction\n"); + pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &lnkcap); + lnkctl2 &= ~PCI_EXP_LNKCTL2_TLS; + lnkctl2 |= lnkcap & PCI_EXP_LNKCAP_SLS; + pcie_capability_write_word(dev, PCI_EXP_LNKCTL2, lnkctl2); + + if (!pcie_retrain_link(dev, false)) { + pci_info(dev, "retraining failed\n"); + return false; + } + } + + return true; +} + static ktime_t fixup_debug_start(struct pci_dev *dev, void (*fn)(struct pci_dev *dev)) { -- cgit v1.2.3 From 08e3ed12ca8615b078ea19488fb45b084e5de16b Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Sun, 11 Jun 2023 18:20:06 +0100 Subject: PCI: Add failed link recovery for device reset events Request failed link recovery with any upstream PCIe bridge where a device has not come back after reset within PCI_RESET_WAIT time. Reset the polling interval if recovery succeeded, otherwise continue as usual. [bhelgaas: inline pcie_parent_link_retrain()] Link: https://lore.kernel.org/r/alpine.DEB.2.21.2306111631050.64925@angie.orcam.me.uk Signed-off-by: Maciej W. Rozycki Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index f599d321c881..64f1a87902d8 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1156,7 +1156,14 @@ void pci_resume_bus(struct pci_bus *bus) static int pci_dev_wait(struct pci_dev *dev, char *reset_type, int timeout) { int delay = 1; - u32 id; + bool retrain = false; + struct pci_dev *bridge; + + if (pci_is_pcie(dev)) { + bridge = pci_upstream_bridge(dev); + if (bridge) + retrain = true; + } /* * After reset, the device should not silently discard config @@ -1170,21 +1177,33 @@ static int pci_dev_wait(struct pci_dev *dev, char *reset_type, int timeout) * Command register instead of Vendor ID so we don't have to * contend with the CRS SV value. */ - pci_read_config_dword(dev, PCI_COMMAND, &id); - while (PCI_POSSIBLE_ERROR(id)) { + for (;;) { + u32 id; + + pci_read_config_dword(dev, PCI_COMMAND, &id); + if (!PCI_POSSIBLE_ERROR(id)) + break; + if (delay > timeout) { pci_warn(dev, "not ready %dms after %s; giving up\n", delay - 1, reset_type); return -ENOTTY; } - if (delay > PCI_RESET_WAIT) + if (delay > PCI_RESET_WAIT) { + if (retrain) { + retrain = false; + if (pcie_failed_link_retrain(bridge)) { + delay = 1; + continue; + } + } pci_info(dev, "not ready %dms after %s; waiting\n", delay - 1, reset_type); + } msleep(delay); delay *= 2; - pci_read_config_dword(dev, PCI_COMMAND, &id); } if (delay > PCI_RESET_WAIT) -- cgit v1.2.3 From f5297a01ee805d7fa569d288ed65fc0f9ac9b03d Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 20 Jun 2023 14:44:55 -0500 Subject: PCI/ASPM: Return 0 or -ETIMEDOUT from pcie_retrain_link() "pcie_retrain_link" is not a question with a true/false answer, so "bool" isn't quite the right return type. Return 0 for success or -ETIMEDOUT if the retrain failed. No functional change intended. [bhelgaas: based on Ilpo's patch below] Link: https://lore.kernel.org/r/20230502083923.34562-1-ilpo.jarvinen@linux.intel.com Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aspm.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 72cdb30a924a..ee6323ded1c8 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -193,7 +193,7 @@ static void pcie_clkpm_cap_init(struct pcie_link_state *link, int blacklist) link->clkpm_disable = blacklist ? 1 : 0; } -static bool pcie_retrain_link(struct pcie_link_state *link) +static int pcie_retrain_link(struct pcie_link_state *link) { struct pci_dev *parent = link->pdev; unsigned long end_jiffies; @@ -220,7 +220,9 @@ static bool pcie_retrain_link(struct pcie_link_state *link) break; msleep(1); } while (time_before(jiffies, end_jiffies)); - return !(reg16 & PCI_EXP_LNKSTA_LT); + if (reg16 & PCI_EXP_LNKSTA_LT) + return -ETIMEDOUT; + return 0; } /* @@ -289,15 +291,15 @@ static void pcie_aspm_configure_common_clock(struct pcie_link_state *link) reg16 &= ~PCI_EXP_LNKCTL_CCC; pcie_capability_write_word(parent, PCI_EXP_LNKCTL, reg16); - if (pcie_retrain_link(link)) - return; + if (pcie_retrain_link(link)) { - /* Training failed. Restore common clock configurations */ - pci_err(parent, "ASPM: Could not configure common clock\n"); - list_for_each_entry(child, &linkbus->devices, bus_list) - pcie_capability_write_word(child, PCI_EXP_LNKCTL, + /* Training failed. Restore common clock configurations */ + pci_err(parent, "ASPM: Could not configure common clock\n"); + list_for_each_entry(child, &linkbus->devices, bus_list) + pcie_capability_write_word(child, PCI_EXP_LNKCTL, child_reg[PCI_FUNC(child->devfn)]); - pcie_capability_write_word(parent, PCI_EXP_LNKCTL, parent_reg); + pcie_capability_write_word(parent, PCI_EXP_LNKCTL, parent_reg); + } } /* Convert L0s latency encoding to ns */ -- cgit v1.2.3 From 9c7f136433d26592cb4d9cd00b4e15c33d9797c6 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Tue, 20 Jun 2023 14:49:33 -0500 Subject: PCI/ASPM: Factor out pcie_wait_for_retrain() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Factor pcie_wait_for_retrain() out from pcie_retrain_link(). No functional change intended. [bhelgaas: split out from https://lore.kernel.org/r/20230502083923.34562-1-ilpo.jarvinen@linux.intel.com] Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/aspm.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index ee6323ded1c8..954717d7033f 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -193,10 +193,26 @@ static void pcie_clkpm_cap_init(struct pcie_link_state *link, int blacklist) link->clkpm_disable = blacklist ? 1 : 0; } +static int pcie_wait_for_retrain(struct pci_dev *pdev) +{ + unsigned long end_jiffies; + u16 reg16; + + /* Wait for Link Training to be cleared by hardware */ + end_jiffies = jiffies + LINK_RETRAIN_TIMEOUT; + do { + pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, ®16); + if (!(reg16 & PCI_EXP_LNKSTA_LT)) + return 0; + msleep(1); + } while (time_before(jiffies, end_jiffies)); + + return -ETIMEDOUT; +} + static int pcie_retrain_link(struct pcie_link_state *link) { struct pci_dev *parent = link->pdev; - unsigned long end_jiffies; u16 reg16; pcie_capability_read_word(parent, PCI_EXP_LNKCTL, ®16); @@ -212,17 +228,7 @@ static int pcie_retrain_link(struct pcie_link_state *link) pcie_capability_write_word(parent, PCI_EXP_LNKCTL, reg16); } - /* Wait for link training end. Break out after waiting for timeout */ - end_jiffies = jiffies + LINK_RETRAIN_TIMEOUT; - do { - pcie_capability_read_word(parent, PCI_EXP_LNKSTA, ®16); - if (!(reg16 & PCI_EXP_LNKSTA_LT)) - break; - msleep(1); - } while (time_before(jiffies, end_jiffies)); - if (reg16 & PCI_EXP_LNKSTA_LT) - return -ETIMEDOUT; - return 0; + return pcie_wait_for_retrain(parent); } /* -- cgit v1.2.3 From e7e39756363ad5bd83ddeae1063193d0f13870fd Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Tue, 2 May 2023 11:39:23 +0300 Subject: PCI/ASPM: Avoid link retraining race MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PCIe r6.0.1, sec 7.5.3.7, recommends setting the link control parameters, then waiting for the Link Training bit to be clear before setting the Retrain Link bit. This avoids a race where the LTSSM may not use the updated parameters if it is already in the midst of link training because of other normal link activity. Wait for the Link Training bit to be clear before toggling the Retrain Link bit to ensure that the LTSSM uses the updated link control parameters. [bhelgaas: commit log, return 0 (success)/-ETIMEDOUT instead of bool for both pcie_wait_for_retrain() and the existing pcie_retrain_link()] Suggested-by: Lukas Wunner Fixes: 7d715a6c1ae5 ("PCI: add PCI Express ASPM support") Link: https://lore.kernel.org/r/20230502083923.34562-1-ilpo.jarvinen@linux.intel.com Signed-off-by: Ilpo Järvinen Signed-off-by: Bjorn Helgaas Reviewed-by: Lukas Wunner Cc: stable@vger.kernel.org --- drivers/pci/pcie/aspm.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 954717d7033f..3aa73ecdf86f 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -213,8 +213,19 @@ static int pcie_wait_for_retrain(struct pci_dev *pdev) static int pcie_retrain_link(struct pcie_link_state *link) { struct pci_dev *parent = link->pdev; + int rc; u16 reg16; + /* + * Ensure the updated LNKCTL parameters are used during link + * training by checking that there is no ongoing link training to + * avoid LTSSM race as recommended in Implementation Note at the + * end of PCIe r6.0.1 sec 7.5.3.7. + */ + rc = pcie_wait_for_retrain(parent); + if (rc) + return rc; + pcie_capability_read_word(parent, PCI_EXP_LNKCTL, ®16); reg16 |= PCI_EXP_LNKCTL_RL; pcie_capability_write_word(parent, PCI_EXP_LNKCTL, reg16); -- cgit v1.2.3 From 92a9c57c325dd51682d428ba960d961fec3c8a08 Mon Sep 17 00:00:00 2001 From: Rick Wertenbroek Date: Tue, 18 Apr 2023 09:46:48 +0200 Subject: PCI: rockchip: Remove writes to unused registers Remove write accesses to registers that are marked "unused" (and therefore read-only) in the technical reference manual (TRM) (see RK3399 TRM 17.6.8.1) Link: https://lore.kernel.org/r/20230418074700.1083505-2-rick.wertenbroek@gmail.com Tested-by: Damien Le Moal Signed-off-by: Rick Wertenbroek Signed-off-by: Lorenzo Pieralisi Reviewed-by: Damien Le Moal --- drivers/pci/controller/pcie-rockchip-ep.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/pci/controller/pcie-rockchip-ep.c b/drivers/pci/controller/pcie-rockchip-ep.c index d1a200b93b2b..d5c477020417 100644 --- a/drivers/pci/controller/pcie-rockchip-ep.c +++ b/drivers/pci/controller/pcie-rockchip-ep.c @@ -61,10 +61,6 @@ static void rockchip_pcie_clear_ep_ob_atu(struct rockchip_pcie *rockchip, ROCKCHIP_PCIE_AT_OB_REGION_DESC0(region)); rockchip_pcie_write(rockchip, 0, ROCKCHIP_PCIE_AT_OB_REGION_DESC1(region)); - rockchip_pcie_write(rockchip, 0, - ROCKCHIP_PCIE_AT_OB_REGION_CPU_ADDR0(region)); - rockchip_pcie_write(rockchip, 0, - ROCKCHIP_PCIE_AT_OB_REGION_CPU_ADDR1(region)); } static void rockchip_pcie_prog_ep_ob_atu(struct rockchip_pcie *rockchip, u8 fn, @@ -114,12 +110,6 @@ static void rockchip_pcie_prog_ep_ob_atu(struct rockchip_pcie *rockchip, u8 fn, PCIE_CORE_OB_REGION_ADDR0_LO_ADDR); addr1 = upper_32_bits(cpu_addr); } - - /* CPU bus address region */ - rockchip_pcie_write(rockchip, addr0, - ROCKCHIP_PCIE_AT_OB_REGION_CPU_ADDR0(r)); - rockchip_pcie_write(rockchip, addr1, - ROCKCHIP_PCIE_AT_OB_REGION_CPU_ADDR1(r)); } static int rockchip_pcie_ep_write_header(struct pci_epc *epc, u8 fn, u8 vfn, -- cgit v1.2.3 From 1f1c42ece18de365c976a060f3c8eb481b038e3a Mon Sep 17 00:00:00 2001 From: Rick Wertenbroek Date: Tue, 18 Apr 2023 09:46:49 +0200 Subject: PCI: rockchip: Write PCI Device ID to correct register Write PCI Device ID (DID) to the correct register. The Device ID was not updated through the correct register. Device ID was written to a read-only register and therefore did not work. The Device ID is now set through the correct register. This is documented in the RK3399 TRM section 17.6.6.1.1 Link: https://lore.kernel.org/r/20230418074700.1083505-3-rick.wertenbroek@gmail.com Fixes: cf590b078391 ("PCI: rockchip: Add EP driver for Rockchip PCIe controller") Tested-by: Damien Le Moal Signed-off-by: Rick Wertenbroek Signed-off-by: Lorenzo Pieralisi Reviewed-by: Damien Le Moal Cc: stable@vger.kernel.org --- drivers/pci/controller/pcie-rockchip-ep.c | 6 ++++-- drivers/pci/controller/pcie-rockchip.h | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/pci/controller/pcie-rockchip-ep.c b/drivers/pci/controller/pcie-rockchip-ep.c index d5c477020417..9b835377bd9e 100644 --- a/drivers/pci/controller/pcie-rockchip-ep.c +++ b/drivers/pci/controller/pcie-rockchip-ep.c @@ -115,6 +115,7 @@ static void rockchip_pcie_prog_ep_ob_atu(struct rockchip_pcie *rockchip, u8 fn, static int rockchip_pcie_ep_write_header(struct pci_epc *epc, u8 fn, u8 vfn, struct pci_epf_header *hdr) { + u32 reg; struct rockchip_pcie_ep *ep = epc_get_drvdata(epc); struct rockchip_pcie *rockchip = &ep->rockchip; @@ -127,8 +128,9 @@ static int rockchip_pcie_ep_write_header(struct pci_epc *epc, u8 fn, u8 vfn, PCIE_CORE_CONFIG_VENDOR); } - rockchip_pcie_write(rockchip, hdr->deviceid << 16, - ROCKCHIP_PCIE_EP_FUNC_BASE(fn) + PCI_VENDOR_ID); + reg = rockchip_pcie_read(rockchip, PCIE_EP_CONFIG_DID_VID); + reg = (reg & 0xFFFF) | (hdr->deviceid << 16); + rockchip_pcie_write(rockchip, reg, PCIE_EP_CONFIG_DID_VID); rockchip_pcie_write(rockchip, hdr->revid | diff --git a/drivers/pci/controller/pcie-rockchip.h b/drivers/pci/controller/pcie-rockchip.h index 32c3a859c26b..51a123e5c0cf 100644 --- a/drivers/pci/controller/pcie-rockchip.h +++ b/drivers/pci/controller/pcie-rockchip.h @@ -133,6 +133,8 @@ #define PCIE_RC_RP_ATS_BASE 0x400000 #define PCIE_RC_CONFIG_NORMAL_BASE 0x800000 #define PCIE_RC_CONFIG_BASE 0xa00000 +#define PCIE_EP_CONFIG_BASE 0xa00000 +#define PCIE_EP_CONFIG_DID_VID (PCIE_EP_CONFIG_BASE + 0x00) #define PCIE_RC_CONFIG_RID_CCR (PCIE_RC_CONFIG_BASE + 0x08) #define PCIE_RC_CONFIG_DCR (PCIE_RC_CONFIG_BASE + 0xc4) #define PCIE_RC_CONFIG_DCR_CSPL_SHIFT 18 -- cgit v1.2.3 From f397fd4ac1fa3afcabd8cee030f953ccaed2a364 Mon Sep 17 00:00:00 2001 From: Rick Wertenbroek Date: Tue, 18 Apr 2023 09:46:50 +0200 Subject: PCI: rockchip: Assert PCI Configuration Enable bit after probe Assert PCI Configuration Enable bit after probe. When this bit is left to 0 in the endpoint mode, the RK3399 PCIe endpoint core will generate configuration request retry status (CRS) messages back to the root complex. Assert this bit after probe to allow the RK3399 PCIe endpoint core to reply to configuration requests from the root complex. This is documented in section 17.5.8.1.2 of the RK3399 TRM. Link: https://lore.kernel.org/r/20230418074700.1083505-4-rick.wertenbroek@gmail.com Fixes: cf590b078391 ("PCI: rockchip: Add EP driver for Rockchip PCIe controller") Tested-by: Damien Le Moal Signed-off-by: Rick Wertenbroek Signed-off-by: Lorenzo Pieralisi Reviewed-by: Damien Le Moal Cc: stable@vger.kernel.org --- drivers/pci/controller/pcie-rockchip-ep.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/pci/controller/pcie-rockchip-ep.c b/drivers/pci/controller/pcie-rockchip-ep.c index 9b835377bd9e..d00baed65eba 100644 --- a/drivers/pci/controller/pcie-rockchip-ep.c +++ b/drivers/pci/controller/pcie-rockchip-ep.c @@ -623,6 +623,9 @@ static int rockchip_pcie_ep_probe(struct platform_device *pdev) ep->irq_pci_addr = ROCKCHIP_PCIE_EP_DUMMY_IRQ_ADDR; + rockchip_pcie_write(rockchip, PCIE_CLIENT_CONF_ENABLE, + PCIE_CLIENT_CONFIG); + return 0; err_epc_mem_exit: pci_epc_mem_exit(epc); -- cgit v1.2.3 From 9dd3c7c4c8c3f7f010d9cdb7c3f42506d93c9527 Mon Sep 17 00:00:00 2001 From: Rick Wertenbroek Date: Tue, 18 Apr 2023 09:46:51 +0200 Subject: PCI: rockchip: Add poll and timeout to wait for PHY PLLs to be locked The RK3399 PCIe controller should wait until the PHY PLLs are locked. Add poll and timeout to wait for PHY PLLs to be locked. If they cannot be locked generate error message and jump to error handler. Accessing registers in the PHY clock domain when PLLs are not locked causes hang The PHY PLLs status is checked through a side channel register. This is documented in the TRM section 17.5.8.1 "PCIe Initialization Sequence". Link: https://lore.kernel.org/r/20230418074700.1083505-5-rick.wertenbroek@gmail.com Fixes: cf590b078391 ("PCI: rockchip: Add EP driver for Rockchip PCIe controller") Tested-by: Damien Le Moal Signed-off-by: Rick Wertenbroek Signed-off-by: Lorenzo Pieralisi Reviewed-by: Damien Le Moal Cc: stable@vger.kernel.org --- drivers/pci/controller/pcie-rockchip.c | 17 +++++++++++++++++ drivers/pci/controller/pcie-rockchip.h | 2 ++ 2 files changed, 19 insertions(+) diff --git a/drivers/pci/controller/pcie-rockchip.c b/drivers/pci/controller/pcie-rockchip.c index 990a00e08bc5..1aa84035a8bc 100644 --- a/drivers/pci/controller/pcie-rockchip.c +++ b/drivers/pci/controller/pcie-rockchip.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -153,6 +154,12 @@ int rockchip_pcie_parse_dt(struct rockchip_pcie *rockchip) } EXPORT_SYMBOL_GPL(rockchip_pcie_parse_dt); +#define rockchip_pcie_read_addr(addr) rockchip_pcie_read(rockchip, addr) +/* 100 ms max wait time for PHY PLLs to lock */ +#define RK_PHY_PLL_LOCK_TIMEOUT_US 100000 +/* Sleep should be less than 20ms */ +#define RK_PHY_PLL_LOCK_SLEEP_US 1000 + int rockchip_pcie_init_port(struct rockchip_pcie *rockchip) { struct device *dev = rockchip->dev; @@ -254,6 +261,16 @@ int rockchip_pcie_init_port(struct rockchip_pcie *rockchip) } } + err = readx_poll_timeout(rockchip_pcie_read_addr, + PCIE_CLIENT_SIDE_BAND_STATUS, + regs, !(regs & PCIE_CLIENT_PHY_ST), + RK_PHY_PLL_LOCK_SLEEP_US, + RK_PHY_PLL_LOCK_TIMEOUT_US); + if (err) { + dev_err(dev, "PHY PLLs could not lock, %d\n", err); + goto err_power_off_phy; + } + /* * Please don't reorder the deassert sequence of the following * four reset pins. diff --git a/drivers/pci/controller/pcie-rockchip.h b/drivers/pci/controller/pcie-rockchip.h index 51a123e5c0cf..f3a5ff1cf7f4 100644 --- a/drivers/pci/controller/pcie-rockchip.h +++ b/drivers/pci/controller/pcie-rockchip.h @@ -38,6 +38,8 @@ #define PCIE_CLIENT_MODE_EP HIWORD_UPDATE(0x0040, 0) #define PCIE_CLIENT_GEN_SEL_1 HIWORD_UPDATE(0x0080, 0) #define PCIE_CLIENT_GEN_SEL_2 HIWORD_UPDATE_BIT(0x0080) +#define PCIE_CLIENT_SIDE_BAND_STATUS (PCIE_CLIENT_BASE + 0x20) +#define PCIE_CLIENT_PHY_ST BIT(12) #define PCIE_CLIENT_DEBUG_OUT_0 (PCIE_CLIENT_BASE + 0x3c) #define PCIE_CLIENT_DEBUG_LTSSM_MASK GENMASK(5, 0) #define PCIE_CLIENT_DEBUG_LTSSM_L1 0x18 -- cgit v1.2.3 From 21a2960d5ea2e70c15256b73fce1a14999071090 Mon Sep 17 00:00:00 2001 From: Rick Wertenbroek Date: Tue, 18 Apr 2023 09:46:53 +0200 Subject: dt-bindings: PCI: Update the RK3399 example to a valid one Update the example in the documentation to a valid example. Address for mem-base was invalid, it pointed to address 0x8000'0000 which is the upper region of the DDR which is not necessarily populated depending on the board. This address should point to the base of the memory window region of the controller which is 0xfa00'0000. Add missing pinctrl. Link: https://lore.kernel.org/r/20230418074700.1083505-7-rick.wertenbroek@gmail.com Signed-off-by: Rick Wertenbroek Signed-off-by: Lorenzo Pieralisi Acked-by: Krzysztof Kozlowski --- Documentation/devicetree/bindings/pci/rockchip,rk3399-pcie-ep.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/pci/rockchip,rk3399-pcie-ep.yaml b/Documentation/devicetree/bindings/pci/rockchip,rk3399-pcie-ep.yaml index 88386a6d7011..6b62f6f58efe 100644 --- a/Documentation/devicetree/bindings/pci/rockchip,rk3399-pcie-ep.yaml +++ b/Documentation/devicetree/bindings/pci/rockchip,rk3399-pcie-ep.yaml @@ -47,7 +47,7 @@ examples: pcie-ep@f8000000 { compatible = "rockchip,rk3399-pcie-ep"; - reg = <0x0 0xfd000000 0x0 0x1000000>, <0x0 0x80000000 0x0 0x20000>; + reg = <0x0 0xfd000000 0x0 0x1000000>, <0x0 0xfa000000 0x0 0x2000000>; reg-names = "apb-base", "mem-base"; clocks = <&cru ACLK_PCIE>, <&cru ACLK_PERF_PCIE>, <&cru PCLK_PCIE>, <&cru SCLK_PCIE_PM>; @@ -63,6 +63,8 @@ examples: phys = <&pcie_phy 0>, <&pcie_phy 1>, <&pcie_phy 2>, <&pcie_phy 3>; phy-names = "pcie-phy-0", "pcie-phy-1", "pcie-phy-2", "pcie-phy-3"; rockchip,max-outbound-regions = <16>; + pinctrl-names = "default"; + pinctrl-0 = <&pcie_clkreqnb_cpm>; }; }; ... -- cgit v1.2.3 From 166e89d99dd85a856343cca51eee781b793801f2 Mon Sep 17 00:00:00 2001 From: Rick Wertenbroek Date: Tue, 18 Apr 2023 09:46:54 +0200 Subject: PCI: rockchip: Fix legacy IRQ generation for RK3399 PCIe endpoint core Fix legacy IRQ generation for RK3399 PCIe endpoint core according to the technical reference manual (TRM). Assert and deassert legacy interrupt (INTx) through the legacy interrupt control register ("PCIE_CLIENT_LEGACY_INT_CTRL") instead of manually generating a PCIe message. The generation of the legacy interrupt was tested and validated with the PCIe endpoint test driver. Link: https://lore.kernel.org/r/20230418074700.1083505-8-rick.wertenbroek@gmail.com Fixes: cf590b078391 ("PCI: rockchip: Add EP driver for Rockchip PCIe controller") Tested-by: Damien Le Moal Signed-off-by: Rick Wertenbroek Signed-off-by: Lorenzo Pieralisi Reviewed-by: Damien Le Moal Cc: stable@vger.kernel.org --- drivers/pci/controller/pcie-rockchip-ep.c | 45 ++++++++----------------------- drivers/pci/controller/pcie-rockchip.h | 6 ++++- 2 files changed, 16 insertions(+), 35 deletions(-) diff --git a/drivers/pci/controller/pcie-rockchip-ep.c b/drivers/pci/controller/pcie-rockchip-ep.c index d00baed65eba..d6d4b0f2e733 100644 --- a/drivers/pci/controller/pcie-rockchip-ep.c +++ b/drivers/pci/controller/pcie-rockchip-ep.c @@ -337,48 +337,25 @@ static int rockchip_pcie_ep_get_msi(struct pci_epc *epc, u8 fn, u8 vfn) } static void rockchip_pcie_ep_assert_intx(struct rockchip_pcie_ep *ep, u8 fn, - u8 intx, bool is_asserted) + u8 intx, bool do_assert) { struct rockchip_pcie *rockchip = &ep->rockchip; - u32 r = ep->max_regions - 1; - u32 offset; - u32 status; - u8 msg_code; - - if (unlikely(ep->irq_pci_addr != ROCKCHIP_PCIE_EP_PCI_LEGACY_IRQ_ADDR || - ep->irq_pci_fn != fn)) { - rockchip_pcie_prog_ep_ob_atu(rockchip, fn, r, - AXI_WRAPPER_NOR_MSG, - ep->irq_phys_addr, 0, 0); - ep->irq_pci_addr = ROCKCHIP_PCIE_EP_PCI_LEGACY_IRQ_ADDR; - ep->irq_pci_fn = fn; - } intx &= 3; - if (is_asserted) { + + if (do_assert) { ep->irq_pending |= BIT(intx); - msg_code = ROCKCHIP_PCIE_MSG_CODE_ASSERT_INTA + intx; + rockchip_pcie_write(rockchip, + PCIE_CLIENT_INT_IN_ASSERT | + PCIE_CLIENT_INT_PEND_ST_PEND, + PCIE_CLIENT_LEGACY_INT_CTRL); } else { ep->irq_pending &= ~BIT(intx); - msg_code = ROCKCHIP_PCIE_MSG_CODE_DEASSERT_INTA + intx; + rockchip_pcie_write(rockchip, + PCIE_CLIENT_INT_IN_DEASSERT | + PCIE_CLIENT_INT_PEND_ST_NORMAL, + PCIE_CLIENT_LEGACY_INT_CTRL); } - - status = rockchip_pcie_read(rockchip, - ROCKCHIP_PCIE_EP_FUNC_BASE(fn) + - ROCKCHIP_PCIE_EP_CMD_STATUS); - status &= ROCKCHIP_PCIE_EP_CMD_STATUS_IS; - - if ((status != 0) ^ (ep->irq_pending != 0)) { - status ^= ROCKCHIP_PCIE_EP_CMD_STATUS_IS; - rockchip_pcie_write(rockchip, status, - ROCKCHIP_PCIE_EP_FUNC_BASE(fn) + - ROCKCHIP_PCIE_EP_CMD_STATUS); - } - - offset = - ROCKCHIP_PCIE_MSG_ROUTING(ROCKCHIP_PCIE_MSG_ROUTING_LOCAL_INTX) | - ROCKCHIP_PCIE_MSG_CODE(msg_code) | ROCKCHIP_PCIE_MSG_NO_DATA; - writel(0, ep->irq_cpu_addr + offset); } static int rockchip_pcie_ep_send_legacy_irq(struct rockchip_pcie_ep *ep, u8 fn, diff --git a/drivers/pci/controller/pcie-rockchip.h b/drivers/pci/controller/pcie-rockchip.h index f3a5ff1cf7f4..ffc68a3a5fee 100644 --- a/drivers/pci/controller/pcie-rockchip.h +++ b/drivers/pci/controller/pcie-rockchip.h @@ -38,6 +38,11 @@ #define PCIE_CLIENT_MODE_EP HIWORD_UPDATE(0x0040, 0) #define PCIE_CLIENT_GEN_SEL_1 HIWORD_UPDATE(0x0080, 0) #define PCIE_CLIENT_GEN_SEL_2 HIWORD_UPDATE_BIT(0x0080) +#define PCIE_CLIENT_LEGACY_INT_CTRL (PCIE_CLIENT_BASE + 0x0c) +#define PCIE_CLIENT_INT_IN_ASSERT HIWORD_UPDATE_BIT(0x0002) +#define PCIE_CLIENT_INT_IN_DEASSERT HIWORD_UPDATE(0x0002, 0) +#define PCIE_CLIENT_INT_PEND_ST_PEND HIWORD_UPDATE_BIT(0x0001) +#define PCIE_CLIENT_INT_PEND_ST_NORMAL HIWORD_UPDATE(0x0001, 0) #define PCIE_CLIENT_SIDE_BAND_STATUS (PCIE_CLIENT_BASE + 0x20) #define PCIE_CLIENT_PHY_ST BIT(12) #define PCIE_CLIENT_DEBUG_OUT_0 (PCIE_CLIENT_BASE + 0x3c) @@ -227,7 +232,6 @@ #define ROCKCHIP_PCIE_EP_MSI_CTRL_ME BIT(16) #define ROCKCHIP_PCIE_EP_MSI_CTRL_MASK_MSI_CAP BIT(24) #define ROCKCHIP_PCIE_EP_DUMMY_IRQ_ADDR 0x1 -#define ROCKCHIP_PCIE_EP_PCI_LEGACY_IRQ_ADDR 0x3 #define ROCKCHIP_PCIE_EP_FUNC_BASE(fn) (((fn) << 12) & GENMASK(19, 12)) #define ROCKCHIP_PCIE_AT_IB_EP_FUNC_BAR_ADDR0(fn, bar) \ (PCIE_RC_RP_ATS_BASE + 0x0840 + (fn) * 0x0040 + (bar) * 0x0008) -- cgit v1.2.3 From dc73ed0f1b8bddd7f2bf70d123e68ffc99ad71ce Mon Sep 17 00:00:00 2001 From: Rick Wertenbroek Date: Tue, 18 Apr 2023 09:46:55 +0200 Subject: PCI: rockchip: Fix window mapping and address translation for endpoint The RK3399 PCI endpoint core has 33 windows for PCIe space, now in the driver up to 32 fixed size (1M) windows are used and pages are allocated and mapped accordingly. The driver first used a single window and allocated space inside which caused translation issues (between CPU space and PCI space) because a window can only have a single translation at a given time, which if multiple pages are allocated inside will cause conflicts. Now each window is a single region of 1M which will always guarantee that the translation is not in conflict. Set the translation register addresses for physical function. As documented in the technical reference manual (TRM) section 17.5.5 "PCIe Address Translation" and section 17.6.8 "Address Translation Registers Description" Link: https://lore.kernel.org/r/20230418074700.1083505-9-rick.wertenbroek@gmail.com Fixes: cf590b078391 ("PCI: rockchip: Add EP driver for Rockchip PCIe controller") Tested-by: Damien Le Moal Signed-off-by: Rick Wertenbroek Signed-off-by: Lorenzo Pieralisi Reviewed-by: Damien Le Moal Cc: stable@vger.kernel.org --- drivers/pci/controller/pcie-rockchip-ep.c | 128 +++++++++++++----------------- drivers/pci/controller/pcie-rockchip.h | 35 ++++---- 2 files changed, 75 insertions(+), 88 deletions(-) diff --git a/drivers/pci/controller/pcie-rockchip-ep.c b/drivers/pci/controller/pcie-rockchip-ep.c index d6d4b0f2e733..771f1bb93251 100644 --- a/drivers/pci/controller/pcie-rockchip-ep.c +++ b/drivers/pci/controller/pcie-rockchip-ep.c @@ -64,52 +64,29 @@ static void rockchip_pcie_clear_ep_ob_atu(struct rockchip_pcie *rockchip, } static void rockchip_pcie_prog_ep_ob_atu(struct rockchip_pcie *rockchip, u8 fn, - u32 r, u32 type, u64 cpu_addr, - u64 pci_addr, size_t size) + u32 r, u64 cpu_addr, u64 pci_addr, + size_t size) { - u64 sz = 1ULL << fls64(size - 1); - int num_pass_bits = ilog2(sz); - u32 addr0, addr1, desc0, desc1; - bool is_nor_msg = (type == AXI_WRAPPER_NOR_MSG); + int num_pass_bits = fls64(size - 1); + u32 addr0, addr1, desc0; - /* The minimal region size is 1MB */ if (num_pass_bits < 8) num_pass_bits = 8; - cpu_addr -= rockchip->mem_res->start; - addr0 = ((is_nor_msg ? 0x10 : (num_pass_bits - 1)) & - PCIE_CORE_OB_REGION_ADDR0_NUM_BITS) | - (lower_32_bits(cpu_addr) & PCIE_CORE_OB_REGION_ADDR0_LO_ADDR); - addr1 = upper_32_bits(is_nor_msg ? cpu_addr : pci_addr); - desc0 = ROCKCHIP_PCIE_AT_OB_REGION_DESC0_DEVFN(fn) | type; - desc1 = 0; - - if (is_nor_msg) { - rockchip_pcie_write(rockchip, 0, - ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR0(r)); - rockchip_pcie_write(rockchip, 0, - ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR1(r)); - rockchip_pcie_write(rockchip, desc0, - ROCKCHIP_PCIE_AT_OB_REGION_DESC0(r)); - rockchip_pcie_write(rockchip, desc1, - ROCKCHIP_PCIE_AT_OB_REGION_DESC1(r)); - } else { - /* PCI bus address region */ - rockchip_pcie_write(rockchip, addr0, - ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR0(r)); - rockchip_pcie_write(rockchip, addr1, - ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR1(r)); - rockchip_pcie_write(rockchip, desc0, - ROCKCHIP_PCIE_AT_OB_REGION_DESC0(r)); - rockchip_pcie_write(rockchip, desc1, - ROCKCHIP_PCIE_AT_OB_REGION_DESC1(r)); - - addr0 = - ((num_pass_bits - 1) & PCIE_CORE_OB_REGION_ADDR0_NUM_BITS) | - (lower_32_bits(cpu_addr) & - PCIE_CORE_OB_REGION_ADDR0_LO_ADDR); - addr1 = upper_32_bits(cpu_addr); - } + addr0 = ((num_pass_bits - 1) & PCIE_CORE_OB_REGION_ADDR0_NUM_BITS) | + (lower_32_bits(pci_addr) & PCIE_CORE_OB_REGION_ADDR0_LO_ADDR); + addr1 = upper_32_bits(pci_addr); + desc0 = ROCKCHIP_PCIE_AT_OB_REGION_DESC0_DEVFN(fn) | AXI_WRAPPER_MEM_WRITE; + + /* PCI bus address region */ + rockchip_pcie_write(rockchip, addr0, + ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR0(r)); + rockchip_pcie_write(rockchip, addr1, + ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR1(r)); + rockchip_pcie_write(rockchip, desc0, + ROCKCHIP_PCIE_AT_OB_REGION_DESC0(r)); + rockchip_pcie_write(rockchip, 0, + ROCKCHIP_PCIE_AT_OB_REGION_DESC1(r)); } static int rockchip_pcie_ep_write_header(struct pci_epc *epc, u8 fn, u8 vfn, @@ -248,26 +225,20 @@ static void rockchip_pcie_ep_clear_bar(struct pci_epc *epc, u8 fn, u8 vfn, ROCKCHIP_PCIE_AT_IB_EP_FUNC_BAR_ADDR1(fn, bar)); } +static inline u32 rockchip_ob_region(phys_addr_t addr) +{ + return (addr >> ilog2(SZ_1M)) & 0x1f; +} + static int rockchip_pcie_ep_map_addr(struct pci_epc *epc, u8 fn, u8 vfn, phys_addr_t addr, u64 pci_addr, size_t size) { struct rockchip_pcie_ep *ep = epc_get_drvdata(epc); struct rockchip_pcie *pcie = &ep->rockchip; - u32 r; + u32 r = rockchip_ob_region(addr); - r = find_first_zero_bit(&ep->ob_region_map, BITS_PER_LONG); - /* - * Region 0 is reserved for configuration space and shouldn't - * be used elsewhere per TRM, so leave it out. - */ - if (r >= ep->max_regions - 1) { - dev_err(&epc->dev, "no free outbound region\n"); - return -EINVAL; - } - - rockchip_pcie_prog_ep_ob_atu(pcie, fn, r, AXI_WRAPPER_MEM_WRITE, addr, - pci_addr, size); + rockchip_pcie_prog_ep_ob_atu(pcie, fn, r, addr, pci_addr, size); set_bit(r, &ep->ob_region_map); ep->ob_addr[r] = addr; @@ -282,15 +253,11 @@ static void rockchip_pcie_ep_unmap_addr(struct pci_epc *epc, u8 fn, u8 vfn, struct rockchip_pcie *rockchip = &ep->rockchip; u32 r; - for (r = 0; r < ep->max_regions - 1; r++) + for (r = 0; r < ep->max_regions; r++) if (ep->ob_addr[r] == addr) break; - /* - * Region 0 is reserved for configuration space and shouldn't - * be used elsewhere per TRM, so leave it out. - */ - if (r == ep->max_regions - 1) + if (r == ep->max_regions) return; rockchip_pcie_clear_ep_ob_atu(rockchip, r); @@ -387,7 +354,8 @@ static int rockchip_pcie_ep_send_msi_irq(struct rockchip_pcie_ep *ep, u8 fn, struct rockchip_pcie *rockchip = &ep->rockchip; u16 flags, mme, data, data_mask; u8 msi_count; - u64 pci_addr, pci_addr_mask = 0xff; + u64 pci_addr; + u32 r; /* Check MSI enable bit */ flags = rockchip_pcie_read(&ep->rockchip, @@ -421,21 +389,20 @@ static int rockchip_pcie_ep_send_msi_irq(struct rockchip_pcie_ep *ep, u8 fn, ROCKCHIP_PCIE_EP_FUNC_BASE(fn) + ROCKCHIP_PCIE_EP_MSI_CTRL_REG + PCI_MSI_ADDRESS_LO); - pci_addr &= GENMASK_ULL(63, 2); /* Set the outbound region if needed. */ - if (unlikely(ep->irq_pci_addr != (pci_addr & ~pci_addr_mask) || + if (unlikely(ep->irq_pci_addr != (pci_addr & PCIE_ADDR_MASK) || ep->irq_pci_fn != fn)) { - rockchip_pcie_prog_ep_ob_atu(rockchip, fn, ep->max_regions - 1, - AXI_WRAPPER_MEM_WRITE, + r = rockchip_ob_region(ep->irq_phys_addr); + rockchip_pcie_prog_ep_ob_atu(rockchip, fn, r, ep->irq_phys_addr, - pci_addr & ~pci_addr_mask, - pci_addr_mask + 1); - ep->irq_pci_addr = (pci_addr & ~pci_addr_mask); + pci_addr & PCIE_ADDR_MASK, + ~PCIE_ADDR_MASK + 1); + ep->irq_pci_addr = (pci_addr & PCIE_ADDR_MASK); ep->irq_pci_fn = fn; } - writew(data, ep->irq_cpu_addr + (pci_addr & pci_addr_mask)); + writew(data, ep->irq_cpu_addr + (pci_addr & ~PCIE_ADDR_MASK)); return 0; } @@ -516,6 +483,8 @@ static int rockchip_pcie_parse_ep_dt(struct rockchip_pcie *rockchip, if (err < 0 || ep->max_regions > MAX_REGION_LIMIT) ep->max_regions = MAX_REGION_LIMIT; + ep->ob_region_map = 0; + err = of_property_read_u8(dev->of_node, "max-functions", &ep->epc->max_functions); if (err < 0) @@ -536,7 +505,8 @@ static int rockchip_pcie_ep_probe(struct platform_device *pdev) struct rockchip_pcie *rockchip; struct pci_epc *epc; size_t max_regions; - int err; + struct pci_epc_mem_window *windows = NULL; + int err, i; ep = devm_kzalloc(dev, sizeof(*ep), GFP_KERNEL); if (!ep) @@ -583,15 +553,27 @@ static int rockchip_pcie_ep_probe(struct platform_device *pdev) /* Only enable function 0 by default */ rockchip_pcie_write(rockchip, BIT(0), PCIE_CORE_PHY_FUNC_CFG); - err = pci_epc_mem_init(epc, rockchip->mem_res->start, - resource_size(rockchip->mem_res), PAGE_SIZE); + windows = devm_kcalloc(dev, ep->max_regions, + sizeof(struct pci_epc_mem_window), GFP_KERNEL); + if (!windows) { + err = -ENOMEM; + goto err_uninit_port; + } + for (i = 0; i < ep->max_regions; i++) { + windows[i].phys_base = rockchip->mem_res->start + (SZ_1M * i); + windows[i].size = SZ_1M; + windows[i].page_size = SZ_1M; + } + err = pci_epc_multi_mem_init(epc, windows, ep->max_regions); + devm_kfree(dev, windows); + if (err < 0) { dev_err(dev, "failed to initialize the memory space\n"); goto err_uninit_port; } ep->irq_cpu_addr = pci_epc_mem_alloc_addr(epc, &ep->irq_phys_addr, - SZ_128K); + SZ_1M); if (!ep->irq_cpu_addr) { dev_err(dev, "failed to reserve memory space for MSI\n"); err = -ENOMEM; diff --git a/drivers/pci/controller/pcie-rockchip.h b/drivers/pci/controller/pcie-rockchip.h index ffc68a3a5fee..bef6d7098a2f 100644 --- a/drivers/pci/controller/pcie-rockchip.h +++ b/drivers/pci/controller/pcie-rockchip.h @@ -139,6 +139,7 @@ #define PCIE_RC_RP_ATS_BASE 0x400000 #define PCIE_RC_CONFIG_NORMAL_BASE 0x800000 +#define PCIE_EP_PF_CONFIG_REGS_BASE 0x800000 #define PCIE_RC_CONFIG_BASE 0xa00000 #define PCIE_EP_CONFIG_BASE 0xa00000 #define PCIE_EP_CONFIG_DID_VID (PCIE_EP_CONFIG_BASE + 0x00) @@ -157,10 +158,11 @@ #define PCIE_RC_CONFIG_THP_CAP (PCIE_RC_CONFIG_BASE + 0x274) #define PCIE_RC_CONFIG_THP_CAP_NEXT_MASK GENMASK(31, 20) +#define PCIE_ADDR_MASK 0xffffff00 #define PCIE_CORE_AXI_CONF_BASE 0xc00000 #define PCIE_CORE_OB_REGION_ADDR0 (PCIE_CORE_AXI_CONF_BASE + 0x0) #define PCIE_CORE_OB_REGION_ADDR0_NUM_BITS 0x3f -#define PCIE_CORE_OB_REGION_ADDR0_LO_ADDR 0xffffff00 +#define PCIE_CORE_OB_REGION_ADDR0_LO_ADDR PCIE_ADDR_MASK #define PCIE_CORE_OB_REGION_ADDR1 (PCIE_CORE_AXI_CONF_BASE + 0x4) #define PCIE_CORE_OB_REGION_DESC0 (PCIE_CORE_AXI_CONF_BASE + 0x8) #define PCIE_CORE_OB_REGION_DESC1 (PCIE_CORE_AXI_CONF_BASE + 0xc) @@ -168,7 +170,7 @@ #define PCIE_CORE_AXI_INBOUND_BASE 0xc00800 #define PCIE_RP_IB_ADDR0 (PCIE_CORE_AXI_INBOUND_BASE + 0x0) #define PCIE_CORE_IB_REGION_ADDR0_NUM_BITS 0x3f -#define PCIE_CORE_IB_REGION_ADDR0_LO_ADDR 0xffffff00 +#define PCIE_CORE_IB_REGION_ADDR0_LO_ADDR PCIE_ADDR_MASK #define PCIE_RP_IB_ADDR1 (PCIE_CORE_AXI_INBOUND_BASE + 0x4) /* Size of one AXI Region (not Region 0) */ @@ -232,13 +234,15 @@ #define ROCKCHIP_PCIE_EP_MSI_CTRL_ME BIT(16) #define ROCKCHIP_PCIE_EP_MSI_CTRL_MASK_MSI_CAP BIT(24) #define ROCKCHIP_PCIE_EP_DUMMY_IRQ_ADDR 0x1 -#define ROCKCHIP_PCIE_EP_FUNC_BASE(fn) (((fn) << 12) & GENMASK(19, 12)) +#define ROCKCHIP_PCIE_EP_PCI_LEGACY_IRQ_ADDR 0x3 +#define ROCKCHIP_PCIE_EP_FUNC_BASE(fn) \ + (PCIE_EP_PF_CONFIG_REGS_BASE + (((fn) << 12) & GENMASK(19, 12))) +#define ROCKCHIP_PCIE_EP_VIRT_FUNC_BASE(fn) \ + (PCIE_EP_PF_CONFIG_REGS_BASE + 0x10000 + (((fn) << 12) & GENMASK(19, 12))) #define ROCKCHIP_PCIE_AT_IB_EP_FUNC_BAR_ADDR0(fn, bar) \ - (PCIE_RC_RP_ATS_BASE + 0x0840 + (fn) * 0x0040 + (bar) * 0x0008) + (PCIE_CORE_AXI_CONF_BASE + 0x0828 + (fn) * 0x0040 + (bar) * 0x0008) #define ROCKCHIP_PCIE_AT_IB_EP_FUNC_BAR_ADDR1(fn, bar) \ - (PCIE_RC_RP_ATS_BASE + 0x0844 + (fn) * 0x0040 + (bar) * 0x0008) -#define ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR0(r) \ - (PCIE_RC_RP_ATS_BASE + 0x0000 + ((r) & 0x1f) * 0x0020) + (PCIE_CORE_AXI_CONF_BASE + 0x082c + (fn) * 0x0040 + (bar) * 0x0008) #define ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR0_DEVFN_MASK GENMASK(19, 12) #define ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR0_DEVFN(devfn) \ (((devfn) << 12) & \ @@ -246,20 +250,21 @@ #define ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR0_BUS_MASK GENMASK(27, 20) #define ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR0_BUS(bus) \ (((bus) << 20) & ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR0_BUS_MASK) +#define PCIE_RC_EP_ATR_OB_REGIONS_1_32 (PCIE_CORE_AXI_CONF_BASE + 0x0020) +#define ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR0(r) \ + (PCIE_RC_EP_ATR_OB_REGIONS_1_32 + 0x0000 + ((r) & 0x1f) * 0x0020) #define ROCKCHIP_PCIE_AT_OB_REGION_PCI_ADDR1(r) \ - (PCIE_RC_RP_ATS_BASE + 0x0004 + ((r) & 0x1f) * 0x0020) + (PCIE_RC_EP_ATR_OB_REGIONS_1_32 + 0x0004 + ((r) & 0x1f) * 0x0020) #define ROCKCHIP_PCIE_AT_OB_REGION_DESC0_HARDCODED_RID BIT(23) #define ROCKCHIP_PCIE_AT_OB_REGION_DESC0_DEVFN_MASK GENMASK(31, 24) #define ROCKCHIP_PCIE_AT_OB_REGION_DESC0_DEVFN(devfn) \ (((devfn) << 24) & ROCKCHIP_PCIE_AT_OB_REGION_DESC0_DEVFN_MASK) #define ROCKCHIP_PCIE_AT_OB_REGION_DESC0(r) \ - (PCIE_RC_RP_ATS_BASE + 0x0008 + ((r) & 0x1f) * 0x0020) -#define ROCKCHIP_PCIE_AT_OB_REGION_DESC1(r) \ - (PCIE_RC_RP_ATS_BASE + 0x000c + ((r) & 0x1f) * 0x0020) -#define ROCKCHIP_PCIE_AT_OB_REGION_CPU_ADDR0(r) \ - (PCIE_RC_RP_ATS_BASE + 0x0018 + ((r) & 0x1f) * 0x0020) -#define ROCKCHIP_PCIE_AT_OB_REGION_CPU_ADDR1(r) \ - (PCIE_RC_RP_ATS_BASE + 0x001c + ((r) & 0x1f) * 0x0020) + (PCIE_RC_EP_ATR_OB_REGIONS_1_32 + 0x0008 + ((r) & 0x1f) * 0x0020) +#define ROCKCHIP_PCIE_AT_OB_REGION_DESC1(r) \ + (PCIE_RC_EP_ATR_OB_REGIONS_1_32 + 0x000c + ((r) & 0x1f) * 0x0020) +#define ROCKCHIP_PCIE_AT_OB_REGION_DESC2(r) \ + (PCIE_RC_EP_ATR_OB_REGIONS_1_32 + 0x0010 + ((r) & 0x1f) * 0x0020) #define ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG0(fn) \ (PCIE_CORE_CTRL_MGMT_BASE + 0x0240 + (fn) * 0x0008) -- cgit v1.2.3 From 8962b2cb39119cbda4fc69a1f83957824f102f81 Mon Sep 17 00:00:00 2001 From: Rick Wertenbroek Date: Tue, 18 Apr 2023 09:46:56 +0200 Subject: PCI: rockchip: Use u32 variable to access 32-bit registers Previously u16 variables were used to access 32-bit registers, this resulted in not all of the data being read from the registers. Also the left shift of more than 16-bits would result in moving data out of the variable. Use u32 variables to access 32-bit registers Link: https://lore.kernel.org/r/20230418074700.1083505-10-rick.wertenbroek@gmail.com Fixes: cf590b078391 ("PCI: rockchip: Add EP driver for Rockchip PCIe controller") Tested-by: Damien Le Moal Signed-off-by: Rick Wertenbroek Signed-off-by: Lorenzo Pieralisi Reviewed-by: Damien Le Moal Cc: stable@vger.kernel.org --- drivers/pci/controller/pcie-rockchip-ep.c | 10 +++++----- drivers/pci/controller/pcie-rockchip.h | 1 + 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/pci/controller/pcie-rockchip-ep.c b/drivers/pci/controller/pcie-rockchip-ep.c index 771f1bb93251..63fbb379638b 100644 --- a/drivers/pci/controller/pcie-rockchip-ep.c +++ b/drivers/pci/controller/pcie-rockchip-ep.c @@ -271,15 +271,15 @@ static int rockchip_pcie_ep_set_msi(struct pci_epc *epc, u8 fn, u8 vfn, { struct rockchip_pcie_ep *ep = epc_get_drvdata(epc); struct rockchip_pcie *rockchip = &ep->rockchip; - u16 flags; + u32 flags; flags = rockchip_pcie_read(rockchip, ROCKCHIP_PCIE_EP_FUNC_BASE(fn) + ROCKCHIP_PCIE_EP_MSI_CTRL_REG); flags &= ~ROCKCHIP_PCIE_EP_MSI_CTRL_MMC_MASK; flags |= - ((multi_msg_cap << 1) << ROCKCHIP_PCIE_EP_MSI_CTRL_MMC_OFFSET) | - PCI_MSI_FLAGS_64BIT; + (multi_msg_cap << ROCKCHIP_PCIE_EP_MSI_CTRL_MMC_OFFSET) | + (PCI_MSI_FLAGS_64BIT << ROCKCHIP_PCIE_EP_MSI_FLAGS_OFFSET); flags &= ~ROCKCHIP_PCIE_EP_MSI_CTRL_MASK_MSI_CAP; rockchip_pcie_write(rockchip, flags, ROCKCHIP_PCIE_EP_FUNC_BASE(fn) + @@ -291,7 +291,7 @@ static int rockchip_pcie_ep_get_msi(struct pci_epc *epc, u8 fn, u8 vfn) { struct rockchip_pcie_ep *ep = epc_get_drvdata(epc); struct rockchip_pcie *rockchip = &ep->rockchip; - u16 flags; + u32 flags; flags = rockchip_pcie_read(rockchip, ROCKCHIP_PCIE_EP_FUNC_BASE(fn) + @@ -352,7 +352,7 @@ static int rockchip_pcie_ep_send_msi_irq(struct rockchip_pcie_ep *ep, u8 fn, u8 interrupt_num) { struct rockchip_pcie *rockchip = &ep->rockchip; - u16 flags, mme, data, data_mask; + u32 flags, mme, data, data_mask; u8 msi_count; u64 pci_addr; u32 r; diff --git a/drivers/pci/controller/pcie-rockchip.h b/drivers/pci/controller/pcie-rockchip.h index bef6d7098a2f..501d859420b4 100644 --- a/drivers/pci/controller/pcie-rockchip.h +++ b/drivers/pci/controller/pcie-rockchip.h @@ -227,6 +227,7 @@ #define ROCKCHIP_PCIE_EP_CMD_STATUS 0x4 #define ROCKCHIP_PCIE_EP_CMD_STATUS_IS BIT(19) #define ROCKCHIP_PCIE_EP_MSI_CTRL_REG 0x90 +#define ROCKCHIP_PCIE_EP_MSI_FLAGS_OFFSET 16 #define ROCKCHIP_PCIE_EP_MSI_CTRL_MMC_OFFSET 17 #define ROCKCHIP_PCIE_EP_MSI_CTRL_MMC_MASK GENMASK(19, 17) #define ROCKCHIP_PCIE_EP_MSI_CTRL_MME_OFFSET 20 -- cgit v1.2.3 From a52587e0bee14cbeeadf48a24013828cb04b8df8 Mon Sep 17 00:00:00 2001 From: Rick Wertenbroek Date: Tue, 18 Apr 2023 09:46:57 +0200 Subject: PCI: rockchip: Don't advertise MSI-X in PCIe capabilities The RK3399 PCIe endpoint controller cannot generate MSI-X IRQs. This is documented in the RK3399 technical reference manual (TRM) section 17.5.9 "Interrupt Support". MSI-X capability should therefore not be advertised. Remove the MSI-X capability by editing the capability linked-list. The previous entry is the MSI capability, therefore get the next entry from the MSI-X capability entry and set it as next entry for the MSI capability. This in effect removes MSI-X from the list. Linked list before : MSI cap -> MSI-X cap -> PCIe Device cap -> ... Linked list now : MSI cap -> PCIe Device cap -> ... Link: https://lore.kernel.org/r/20230418074700.1083505-11-rick.wertenbroek@gmail.com Fixes: cf590b078391 ("PCI: rockchip: Add EP driver for Rockchip PCIe controller") Tested-by: Damien Le Moal Signed-off-by: Rick Wertenbroek Signed-off-by: Lorenzo Pieralisi Reviewed-by: Damien Le Moal Cc: stable@vger.kernel.org --- drivers/pci/controller/pcie-rockchip-ep.c | 24 ++++++++++++++++++++++++ drivers/pci/controller/pcie-rockchip.h | 5 +++++ 2 files changed, 29 insertions(+) diff --git a/drivers/pci/controller/pcie-rockchip-ep.c b/drivers/pci/controller/pcie-rockchip-ep.c index 63fbb379638b..edfced311a9f 100644 --- a/drivers/pci/controller/pcie-rockchip-ep.c +++ b/drivers/pci/controller/pcie-rockchip-ep.c @@ -507,6 +507,7 @@ static int rockchip_pcie_ep_probe(struct platform_device *pdev) size_t max_regions; struct pci_epc_mem_window *windows = NULL; int err, i; + u32 cfg_msi, cfg_msix_cp; ep = devm_kzalloc(dev, sizeof(*ep), GFP_KERNEL); if (!ep) @@ -582,6 +583,29 @@ static int rockchip_pcie_ep_probe(struct platform_device *pdev) ep->irq_pci_addr = ROCKCHIP_PCIE_EP_DUMMY_IRQ_ADDR; + /* + * MSI-X is not supported but the controller still advertises the MSI-X + * capability by default, which can lead to the Root Complex side + * allocating MSI-X vectors which cannot be used. Avoid this by skipping + * the MSI-X capability entry in the PCIe capabilities linked-list: get + * the next pointer from the MSI-X entry and set that in the MSI + * capability entry (which is the previous entry). This way the MSI-X + * entry is skipped (left out of the linked-list) and not advertised. + */ + cfg_msi = rockchip_pcie_read(rockchip, PCIE_EP_CONFIG_BASE + + ROCKCHIP_PCIE_EP_MSI_CTRL_REG); + + cfg_msi &= ~ROCKCHIP_PCIE_EP_MSI_CP1_MASK; + + cfg_msix_cp = rockchip_pcie_read(rockchip, PCIE_EP_CONFIG_BASE + + ROCKCHIP_PCIE_EP_MSIX_CAP_REG) & + ROCKCHIP_PCIE_EP_MSIX_CAP_CP_MASK; + + cfg_msi |= cfg_msix_cp; + + rockchip_pcie_write(rockchip, cfg_msi, + PCIE_EP_CONFIG_BASE + ROCKCHIP_PCIE_EP_MSI_CTRL_REG); + rockchip_pcie_write(rockchip, PCIE_CLIENT_CONF_ENABLE, PCIE_CLIENT_CONFIG); diff --git a/drivers/pci/controller/pcie-rockchip.h b/drivers/pci/controller/pcie-rockchip.h index 501d859420b4..fe0333778fd9 100644 --- a/drivers/pci/controller/pcie-rockchip.h +++ b/drivers/pci/controller/pcie-rockchip.h @@ -227,6 +227,8 @@ #define ROCKCHIP_PCIE_EP_CMD_STATUS 0x4 #define ROCKCHIP_PCIE_EP_CMD_STATUS_IS BIT(19) #define ROCKCHIP_PCIE_EP_MSI_CTRL_REG 0x90 +#define ROCKCHIP_PCIE_EP_MSI_CP1_OFFSET 8 +#define ROCKCHIP_PCIE_EP_MSI_CP1_MASK GENMASK(15, 8) #define ROCKCHIP_PCIE_EP_MSI_FLAGS_OFFSET 16 #define ROCKCHIP_PCIE_EP_MSI_CTRL_MMC_OFFSET 17 #define ROCKCHIP_PCIE_EP_MSI_CTRL_MMC_MASK GENMASK(19, 17) @@ -234,6 +236,9 @@ #define ROCKCHIP_PCIE_EP_MSI_CTRL_MME_MASK GENMASK(22, 20) #define ROCKCHIP_PCIE_EP_MSI_CTRL_ME BIT(16) #define ROCKCHIP_PCIE_EP_MSI_CTRL_MASK_MSI_CAP BIT(24) +#define ROCKCHIP_PCIE_EP_MSIX_CAP_REG 0xb0 +#define ROCKCHIP_PCIE_EP_MSIX_CAP_CP_OFFSET 8 +#define ROCKCHIP_PCIE_EP_MSIX_CAP_CP_MASK GENMASK(15, 8) #define ROCKCHIP_PCIE_EP_DUMMY_IRQ_ADDR 0x1 #define ROCKCHIP_PCIE_EP_PCI_LEGACY_IRQ_ADDR 0x3 #define ROCKCHIP_PCIE_EP_FUNC_BASE(fn) \ -- cgit v1.2.3 From 7e6689b34a815bd379dfdbe9855d36f395ef056c Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Tue, 18 Apr 2023 09:46:58 +0200 Subject: PCI: rockchip: Set address alignment for endpoint mode The address translation unit of the rockchip EP controller does not use the lower 8 bits of a PCIe-space address to map local memory. Thus we must set the align feature field to 256 to let the user know about this constraint. Link: https://lore.kernel.org/r/20230418074700.1083505-12-rick.wertenbroek@gmail.com Fixes: cf590b078391 ("PCI: rockchip: Add EP driver for Rockchip PCIe controller") Signed-off-by: Damien Le Moal Signed-off-by: Rick Wertenbroek Signed-off-by: Lorenzo Pieralisi Cc: stable@vger.kernel.org --- drivers/pci/controller/pcie-rockchip-ep.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pci/controller/pcie-rockchip-ep.c b/drivers/pci/controller/pcie-rockchip-ep.c index edfced311a9f..0af0e965fb57 100644 --- a/drivers/pci/controller/pcie-rockchip-ep.c +++ b/drivers/pci/controller/pcie-rockchip-ep.c @@ -442,6 +442,7 @@ static const struct pci_epc_features rockchip_pcie_epc_features = { .linkup_notifier = false, .msi_capable = true, .msix_capable = false, + .align = 256, }; static const struct pci_epc_features* -- cgit v1.2.3 From 9e30fd26f43b89cb6b4e850a86caa2e50dedb454 Mon Sep 17 00:00:00 2001 From: Ondrej Zary Date: Wed, 14 Jun 2023 09:42:53 +0200 Subject: PCI/PM: Avoid putting EloPOS E2/S2/H2 PCIe Ports in D3cold The quirk for Elo i2 introduced in commit 92597f97a40b ("PCI/PM: Avoid putting Elo i2 PCIe Ports in D3cold") is also needed by EloPOS E2/S2/H2 which uses the same Continental Z2 board. Change the quirk to match the board instead of system. Link: https://bugzilla.kernel.org/show_bug.cgi?id=215715 Link: https://lore.kernel.org/r/20230614074253.22318-1-linux@zary.sk Signed-off-by: Ondrej Zary Signed-off-by: Bjorn Helgaas Cc: stable@vger.kernel.org --- drivers/pci/pci.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 578bf0d3ec3c..0fb0116ae69f 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2956,13 +2956,13 @@ static const struct dmi_system_id bridge_d3_blacklist[] = { { /* * Downstream device is not accessible after putting a root port - * into D3cold and back into D0 on Elo i2. + * into D3cold and back into D0 on Elo Continental Z2 board */ - .ident = "Elo i2", + .ident = "Elo Continental Z2", .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Elo Touch Solutions"), - DMI_MATCH(DMI_PRODUCT_NAME, "Elo i2"), - DMI_MATCH(DMI_PRODUCT_VERSION, "RevB"), + DMI_MATCH(DMI_BOARD_VENDOR, "Elo Touch Solutions"), + DMI_MATCH(DMI_BOARD_NAME, "Geminilake"), + DMI_MATCH(DMI_BOARD_VERSION, "Continental Z2"), }, }, #endif -- cgit v1.2.3 From 5557b62634abbd55bab7b154ce4bca348ad7f96f Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 21 Jun 2023 16:36:12 -0500 Subject: PCI/ACPI: Validate acpi_pci_set_power_state() parameter Previously acpi_pci_set_power_state() assumed the requested power state was valid (PCI_D0 ... PCI_D3cold). If a caller supplied something else, we could index outside the state_conv[] array and pass junk to acpi_device_set_power(). Validate the pci_power_t parameter and return -EINVAL if it's invalid. Link: https://lore.kernel.org/r/20230621222857.GA122930@bhelgaas Signed-off-by: Bjorn Helgaas Reviewed-by: Mario Limonciello --- drivers/pci/pci-acpi.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 052a611081ec..bf545f719182 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -1053,32 +1053,37 @@ int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state) [PCI_D3hot] = ACPI_STATE_D3_HOT, [PCI_D3cold] = ACPI_STATE_D3_COLD, }; - int error = -EINVAL; + int error; /* If the ACPI device has _EJ0, ignore the device */ if (!adev || acpi_has_method(adev->handle, "_EJ0")) return -ENODEV; switch (state) { - case PCI_D3cold: - if (dev_pm_qos_flags(&dev->dev, PM_QOS_FLAG_NO_POWER_OFF) == - PM_QOS_FLAGS_ALL) { - error = -EBUSY; - break; - } - fallthrough; case PCI_D0: case PCI_D1: case PCI_D2: case PCI_D3hot: - error = acpi_device_set_power(adev, state_conv[state]); + case PCI_D3cold: + break; + default: + return -EINVAL; + } + + if (state == PCI_D3cold) { + if (dev_pm_qos_flags(&dev->dev, PM_QOS_FLAG_NO_POWER_OFF) == + PM_QOS_FLAGS_ALL) + return -EBUSY; } - if (!error) - pci_dbg(dev, "power state changed by ACPI to %s\n", - acpi_power_state_string(adev->power.state)); + error = acpi_device_set_power(adev, state_conv[state]); + if (error) + return error; + + pci_dbg(dev, "power state changed by ACPI to %s\n", + acpi_power_state_string(adev->power.state)); - return error; + return 0; } pci_power_t acpi_pci_get_power_state(struct pci_dev *dev) -- cgit v1.2.3 From 112a7f9c8edbf76f7cb83856a6cb6b60a210b659 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Tue, 20 Jun 2023 09:04:51 -0500 Subject: PCI/ACPI: Call _REG when transitioning D-states MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ACPI r6.5, sec 6.5.4, describes how AML is unable to access an OperationRegion unless _REG has been called to connect a handler: The OS runs _REG control methods to inform AML code of a change in the availability of an operation region. When an operation region handler is unavailable, AML cannot access data fields in that region. (Operation region writes will be ignored and reads will return indeterminate data.) The PCI core does not call _REG at any time, leading to the undefined behavior mentioned in the spec. The spec explains that _REG should be executed to indicate whether a given region can be accessed: Once _REG has been executed for a particular operation region, indicating that the operation region handler is ready, a control method can access fields in the operation region. Conversely, control methods must not access fields in operation regions when _REG method execution has not indicated that the operation region handler is ready. An example included in the spec demonstrates calling _REG when devices are turned off: "when the host controller or bridge controller is turned off or disabled, PCI Config Space Operation Regions for child devices are no longer available. As such, ETH0’s _REG method will be run when it is turned off and will again be run when PCI1 is turned off." It is reported that ASMedia PCIe GPIO controllers fail functional tests after the system has returning from suspend (S3 or s2idle). This is because the BIOS checks whether the OSPM has called the _REG method to determine whether it can interact with the OperationRegion assigned to the device as part of the other AML called for the device. To fix this issue, call acpi_evaluate_reg() when devices are transitioning to D3cold or D0. [bhelgaas: split pci_power_t checking to preliminary patch] Link: https://uefi.org/specs/ACPI/6.5/06_Device_Configuration.html#reg-region Link: https://lore.kernel.org/r/20230620140451.21007-1-mario.limonciello@amd.com Signed-off-by: Mario Limonciello Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki --- drivers/pci/pci-acpi.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index bf545f719182..a05350a4e49c 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -1043,6 +1043,16 @@ bool acpi_pci_bridge_d3(struct pci_dev *dev) return false; } +static void acpi_pci_config_space_access(struct pci_dev *dev, bool enable) +{ + int val = enable ? ACPI_REG_CONNECT : ACPI_REG_DISCONNECT; + int ret = acpi_evaluate_reg(ACPI_HANDLE(&dev->dev), + ACPI_ADR_SPACE_PCI_CONFIG, val); + if (ret) + pci_dbg(dev, "ACPI _REG %s evaluation failed (%d)\n", + enable ? "connect" : "disconnect", ret); +} + int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state) { struct acpi_device *adev = ACPI_COMPANION(&dev->dev); @@ -1074,6 +1084,9 @@ int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state) if (dev_pm_qos_flags(&dev->dev, PM_QOS_FLAG_NO_POWER_OFF) == PM_QOS_FLAGS_ALL) return -EBUSY; + + /* Notify AML lack of PCI config space availability */ + acpi_pci_config_space_access(dev, false); } error = acpi_device_set_power(adev, state_conv[state]); @@ -1083,6 +1096,15 @@ int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state) pci_dbg(dev, "power state changed by ACPI to %s\n", acpi_power_state_string(adev->power.state)); + /* + * Notify AML of PCI config space availability. Config space is + * accessible in all states except D3cold; the only transitions + * that change availability are transitions to D3cold and from + * D3cold to D0. + */ + if (state == PCI_D0) + acpi_pci_config_space_access(dev, true); + return 0; } -- cgit v1.2.3 From 37587673cda963ec950e4983db5023802f9b5ff2 Mon Sep 17 00:00:00 2001 From: Shunsuke Mie Date: Thu, 2 Feb 2023 19:38:32 +0900 Subject: PCI: endpoint: Fix a Kconfig prompt of vNTB driver vNTB driver and NTB driver have same Kconfig prompt. Changed to make it distinguishable. Link: https://lore.kernel.org/r/20230202103832.2038286-1-mie@igel.co.jp Fixes: e35f56bb0330 ("PCI: endpoint: Support NTB transfer between RC and EP") Signed-off-by: Shunsuke Mie Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Reviewed-by: Manivannan Sadhasivam --- drivers/pci/endpoint/functions/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/endpoint/functions/Kconfig b/drivers/pci/endpoint/functions/Kconfig index 9fd560886871..8efb6a869e7c 100644 --- a/drivers/pci/endpoint/functions/Kconfig +++ b/drivers/pci/endpoint/functions/Kconfig @@ -27,7 +27,7 @@ config PCI_EPF_NTB If in doubt, say "N" to disable Endpoint NTB driver. config PCI_EPF_VNTB - tristate "PCI Endpoint NTB driver" + tristate "PCI Endpoint Virtual NTB driver" depends on PCI_ENDPOINT depends on NTB select CONFIGFS_FS -- cgit v1.2.3 From 70b3740f2c1941e2006d61539131b70d20cba9a6 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Sat, 15 Apr 2023 11:35:26 +0900 Subject: PCI: endpoint: Automatically create a function specific attributes group A PCI endpoint function driver can define function specific attributes under its function configfs directory using the add_cfs() endpoint driver operation. This is done by tying up the mkdir operation for the function configfs directory to a call to the add_cfs() operation. However, there are no checks preventing the user from repeatedly creating function specific attribute directories with different names, resulting in the same endpoint specific attributes group being added multiple times, which also result in an invalid reference counting for the attribute groups. E.g., using the pci-epf-ntb function driver as an example, the user creates the function as follows: $ modprobe pci-epf-ntb $ cd /sys/kernel/config/pci_ep/functions/pci_epf_ntb $ mkdir func0 $ tree func0 func0/ |-- baseclass_code |-- cache_line_size |-- ... `-- vendorid $ mkdir func0/attrs $ tree func0 func0/ |-- attrs | |-- db_count | |-- mw1 | |-- mw2 | |-- mw3 | |-- mw4 | |-- num_mws | `-- spad_count |-- baseclass_code |-- cache_line_size |-- ... `-- vendorid At this point, the function can be started by linking the EP controller. However, if the user mistakenly creates again a directory: $ mkdir func0/attrs2 $ tree func0 func0/ |-- attrs | |-- db_count | |-- mw1 | |-- mw2 | |-- mw3 | |-- mw4 | |-- num_mws | `-- spad_count |-- attrs2 | |-- db_count | |-- mw1 | |-- mw2 | |-- mw3 | |-- mw4 | |-- num_mws | `-- spad_count |-- baseclass_code |-- cache_line_size |-- ... `-- vendorid The endpoint function specific attributes are duplicated and cause a crash when the endpoint function device is torn down: refcount_t: addition on 0; use-after-free. WARNING: CPU: 2 PID: 834 at lib/refcount.c:25 refcount_warn_saturate+0xc8/0x144 CPU: 2 PID: 834 Comm: rmdir Not tainted 6.3.0-rc1 #1 Hardware name: Pine64 RockPro64 v2.1 (DT) pstate: 60000005 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--) ... Call trace: refcount_warn_saturate+0xc8/0x144 config_item_get+0x7c/0x80 configfs_rmdir+0x17c/0x30c vfs_rmdir+0x8c/0x204 do_rmdir+0x158/0x184 __arm64_sys_unlinkat+0x64/0x80 invoke_syscall+0x48/0x114 ... Fix this by modifying pci_epf_cfs_work() to execute the new function pci_ep_cfs_add_type_group() which itself calls pci_epf_type_add_cfs() to obtain the function specific attribute group and the group name (directory name) from the endpoint function driver. If the function driver defines an attribute group, pci_ep_cfs_add_type_group() then proceeds to register this group using configfs_register_group(), thus automatically exposing the function type specific configfs attributes to the user. E.g.: $ modprobe pci-epf-ntb $ cd /sys/kernel/config/pci_ep/functions/pci_epf_ntb $ mkdir func0 $ tree func0 func0/ |-- baseclass_code |-- cache_line_size |-- ... |-- pci_epf_ntb.0 | |-- db_count | |-- mw1 | |-- mw2 | |-- mw3 | |-- mw4 | |-- num_mws | `-- spad_count |-- primary |-- ... `-- vendorid With this change, there is no need for the user to create or delete directories in the endpoint function attributes directory. The pci_epf_type_group_ops group operations are thus removed. Also update the documentation for the pci-epf-ntb and pci-epf-vntb function drivers to reflect this change, removing the explanations showing the need to manually create the sub-directory for the function specific attributes. Link: https://lore.kernel.org/r/20230415023542.77601-2-dlemoal@kernel.org Signed-off-by: Damien Le Moal Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Reviewed-by: Manivannan Sadhasivam --- Documentation/PCI/endpoint/pci-ntb-howto.rst | 11 +++---- Documentation/PCI/endpoint/pci-vntb-howto.rst | 13 ++++----- drivers/pci/endpoint/pci-ep-cfs.c | 42 +++++++++++++-------------- 3 files changed, 29 insertions(+), 37 deletions(-) diff --git a/Documentation/PCI/endpoint/pci-ntb-howto.rst b/Documentation/PCI/endpoint/pci-ntb-howto.rst index 1884bf29caba..4261e7157ef1 100644 --- a/Documentation/PCI/endpoint/pci-ntb-howto.rst +++ b/Documentation/PCI/endpoint/pci-ntb-howto.rst @@ -88,13 +88,10 @@ commands can be used:: # echo 0x104c > functions/pci_epf_ntb/func1/vendorid # echo 0xb00d > functions/pci_epf_ntb/func1/deviceid -In order to configure NTB specific attributes, a new sub-directory to func1 -should be created:: - - # mkdir functions/pci_epf_ntb/func1/pci_epf_ntb.0/ - -The NTB function driver will populate this directory with various attributes -that can be configured by the user:: +The PCI endpoint framework also automatically creates a sub-directory in the +function attribute directory. This sub-directory has the same name as the name +of the function device and is populated with the following NTB specific +attributes that can be configured by the user:: # ls functions/pci_epf_ntb/func1/pci_epf_ntb.0/ db_count mw1 mw2 mw3 mw4 num_mws diff --git a/Documentation/PCI/endpoint/pci-vntb-howto.rst b/Documentation/PCI/endpoint/pci-vntb-howto.rst index 4ab8e4a26d4b..70d3bc90893f 100644 --- a/Documentation/PCI/endpoint/pci-vntb-howto.rst +++ b/Documentation/PCI/endpoint/pci-vntb-howto.rst @@ -84,13 +84,10 @@ commands can be used:: # echo 0x1957 > functions/pci_epf_vntb/func1/vendorid # echo 0x0809 > functions/pci_epf_vntb/func1/deviceid -In order to configure NTB specific attributes, a new sub-directory to func1 -should be created:: - - # mkdir functions/pci_epf_vntb/func1/pci_epf_vntb.0/ - -The NTB function driver will populate this directory with various attributes -that can be configured by the user:: +The PCI endpoint framework also automatically creates a sub-directory in the +function attribute directory. This sub-directory has the same name as the name +of the function device and is populated with the following NTB specific +attributes that can be configured by the user:: # ls functions/pci_epf_vntb/func1/pci_epf_vntb.0/ db_count mw1 mw2 mw3 mw4 num_mws @@ -103,7 +100,7 @@ A sample configuration for NTB function is given below:: # echo 1 > functions/pci_epf_vntb/func1/pci_epf_vntb.0/num_mws # echo 0x100000 > functions/pci_epf_vntb/func1/pci_epf_vntb.0/mw1 -A sample configuration for virtual NTB driver for virutal PCI bus:: +A sample configuration for virtual NTB driver for virtual PCI bus:: # echo 0x1957 > functions/pci_epf_vntb/func1/pci_epf_vntb.0/vntb_vid # echo 0x080A > functions/pci_epf_vntb/func1/pci_epf_vntb.0/vntb_pid diff --git a/drivers/pci/endpoint/pci-ep-cfs.c b/drivers/pci/endpoint/pci-ep-cfs.c index 4b8ac0ac84d5..e255a8415bd5 100644 --- a/drivers/pci/endpoint/pci-ep-cfs.c +++ b/drivers/pci/endpoint/pci-ep-cfs.c @@ -23,6 +23,7 @@ struct pci_epf_group { struct config_group group; struct config_group primary_epc_group; struct config_group secondary_epc_group; + struct config_group *type_group; struct delayed_work cfs_work; struct pci_epf *epf; int index; @@ -502,34 +503,29 @@ static struct configfs_item_operations pci_epf_ops = { .release = pci_epf_release, }; -static struct config_group *pci_epf_type_make(struct config_group *group, - const char *name) -{ - struct pci_epf_group *epf_group = to_pci_epf_group(&group->cg_item); - struct config_group *epf_type_group; - - epf_type_group = pci_epf_type_add_cfs(epf_group->epf, group); - return epf_type_group; -} - -static void pci_epf_type_drop(struct config_group *group, - struct config_item *item) -{ - config_item_put(item); -} - -static struct configfs_group_operations pci_epf_type_group_ops = { - .make_group = &pci_epf_type_make, - .drop_item = &pci_epf_type_drop, -}; - static const struct config_item_type pci_epf_type = { - .ct_group_ops = &pci_epf_type_group_ops, .ct_item_ops = &pci_epf_ops, .ct_attrs = pci_epf_attrs, .ct_owner = THIS_MODULE, }; +static void pci_ep_cfs_add_type_group(struct pci_epf_group *epf_group) +{ + struct config_group *group; + + group = pci_epf_type_add_cfs(epf_group->epf, &epf_group->group); + if (!group) + return; + + if (IS_ERR(group)) { + dev_err(&epf_group->epf->dev, + "failed to create epf type specific attributes\n"); + return; + } + + configfs_register_group(&epf_group->group, group); +} + static void pci_epf_cfs_work(struct work_struct *work) { struct pci_epf_group *epf_group; @@ -547,6 +543,8 @@ static void pci_epf_cfs_work(struct work_struct *work) pr_err("failed to create 'secondary' EPC interface\n"); return; } + + pci_ep_cfs_add_type_group(epf_group); } static struct config_group *pci_epf_make(struct config_group *group, -- cgit v1.2.3 From f6ec33979e9ae7fcc2b5582bd3bfdfaa1fc98a78 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Sat, 15 Apr 2023 11:35:27 +0900 Subject: PCI: endpoint: Move pci_epf_type_add_cfs() code pci_epf_type_add_cfs() is called only from pci_ep_cfs_add_type_group() in drivers/pci/endpoint/pci-ep-cfs.c, so there is no need to export this function. Move its code from pci-epf-core.c to pci-ep-cfs.c as a static function. Link: https://lore.kernel.org/r/20230415023542.77601-3-dlemoal@kernel.org Signed-off-by: Damien Le Moal Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Reviewed-by: Manivannan Sadhasivam --- drivers/pci/endpoint/pci-ep-cfs.c | 36 ++++++++++++++++++++++++++++++++++++ drivers/pci/endpoint/pci-epf-core.c | 32 -------------------------------- include/linux/pci-epf.h | 2 -- 3 files changed, 36 insertions(+), 34 deletions(-) diff --git a/drivers/pci/endpoint/pci-ep-cfs.c b/drivers/pci/endpoint/pci-ep-cfs.c index e255a8415bd5..cd99ac8c3794 100644 --- a/drivers/pci/endpoint/pci-ep-cfs.c +++ b/drivers/pci/endpoint/pci-ep-cfs.c @@ -509,6 +509,42 @@ static const struct config_item_type pci_epf_type = { .ct_owner = THIS_MODULE, }; +/** + * pci_epf_type_add_cfs() - Help function drivers to expose function specific + * attributes in configfs + * @epf: the EPF device that has to be configured using configfs + * @group: the parent configfs group (corresponding to entries in + * pci_epf_device_id) + * + * Invoke to expose function specific attributes in configfs. + * + * Return: A pointer to a config_group structure or NULL if the function driver + * does not have anything to expose (attributes configured by user) or if + * the function driver does not implement the add_cfs() method. + * + * Returns an error pointer if this function is called for an unbound EPF device + * or if the EPF driver add_cfs() method fails. + */ +static struct config_group *pci_epf_type_add_cfs(struct pci_epf *epf, + struct config_group *group) +{ + struct config_group *epf_type_group; + + if (!epf->driver) { + dev_err(&epf->dev, "epf device not bound to driver\n"); + return NULL; + } + + if (!epf->driver->ops->add_cfs) + return NULL; + + mutex_lock(&epf->lock); + epf_type_group = epf->driver->ops->add_cfs(epf, group); + mutex_unlock(&epf->lock); + + return epf_type_group; +} + static void pci_ep_cfs_add_type_group(struct pci_epf_group *epf_group) { struct config_group *group; diff --git a/drivers/pci/endpoint/pci-epf-core.c b/drivers/pci/endpoint/pci-epf-core.c index 2036e38be093..355a6f56fcea 100644 --- a/drivers/pci/endpoint/pci-epf-core.c +++ b/drivers/pci/endpoint/pci-epf-core.c @@ -20,38 +20,6 @@ static DEFINE_MUTEX(pci_epf_mutex); static struct bus_type pci_epf_bus_type; static const struct device_type pci_epf_type; -/** - * pci_epf_type_add_cfs() - Help function drivers to expose function specific - * attributes in configfs - * @epf: the EPF device that has to be configured using configfs - * @group: the parent configfs group (corresponding to entries in - * pci_epf_device_id) - * - * Invoke to expose function specific attributes in configfs. If the function - * driver does not have anything to expose (attributes configured by user), - * return NULL. - */ -struct config_group *pci_epf_type_add_cfs(struct pci_epf *epf, - struct config_group *group) -{ - struct config_group *epf_type_group; - - if (!epf->driver) { - dev_err(&epf->dev, "epf device not bound to driver\n"); - return NULL; - } - - if (!epf->driver->ops->add_cfs) - return NULL; - - mutex_lock(&epf->lock); - epf_type_group = epf->driver->ops->add_cfs(epf, group); - mutex_unlock(&epf->lock); - - return epf_type_group; -} -EXPORT_SYMBOL_GPL(pci_epf_type_add_cfs); - /** * pci_epf_unbind() - Notify the function driver that the binding between the * EPF device and EPC device has been lost diff --git a/include/linux/pci-epf.h b/include/linux/pci-epf.h index a215dc8ce693..b8441db2fa52 100644 --- a/include/linux/pci-epf.h +++ b/include/linux/pci-epf.h @@ -214,8 +214,6 @@ void pci_epf_free_space(struct pci_epf *epf, void *addr, enum pci_barno bar, enum pci_epc_interface_type type); int pci_epf_bind(struct pci_epf *epf); void pci_epf_unbind(struct pci_epf *epf); -struct config_group *pci_epf_type_add_cfs(struct pci_epf *epf, - struct config_group *group); int pci_epf_add_vepf(struct pci_epf *epf_pf, struct pci_epf *epf_vf); void pci_epf_remove_vepf(struct pci_epf *epf_pf, struct pci_epf *epf_vf); #endif /* __LINUX_PCI_EPF_H */ -- cgit v1.2.3 From 4aca56f8eae8aa44867ddd6aa107e06f7613226f Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Sat, 15 Apr 2023 11:35:28 +0900 Subject: PCI: epf-test: Fix DMA transfer completion initialization Reinitialize the transfer_complete DMA transfer completion before calling tx_submit(), to avoid seeing the DMA transfer complete before the completion is initialized, thus potentially losing the completion notification. Link: https://lore.kernel.org/r/20230415023542.77601-4-dlemoal@kernel.org Fixes: 8353813c88ef ("PCI: endpoint: Enable DMA tests for endpoints with DMA capabilities") Signed-off-by: Damien Le Moal Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Reviewed-by: Manivannan Sadhasivam Cc: stable@vger.kernel.org --- drivers/pci/endpoint/functions/pci-epf-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index 0f9d2ec822ac..d65419735d2e 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -151,10 +151,10 @@ static int pci_epf_test_data_transfer(struct pci_epf_test *epf_test, return -EIO; } + reinit_completion(&epf_test->transfer_complete); tx->callback = pci_epf_test_dma_callback; tx->callback_param = epf_test; cookie = tx->tx_submit(tx); - reinit_completion(&epf_test->transfer_complete); ret = dma_submit_error(cookie); if (ret) { -- cgit v1.2.3 From 933f31a2fe1f20e5b1ee065579f652cd1b317183 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Sat, 15 Apr 2023 11:35:29 +0900 Subject: PCI: epf-test: Fix DMA transfer completion detection pci_epf_test_data_transfer() and pci_epf_test_dma_callback() are not handling DMA transfer completion correctly, leading to completion notifications to the RC side that are too early. This problem can be detected when the RC side is running an IOMMU with messages such as: pci-endpoint-test 0000:0b:00.0: AMD-Vi: Event logged [IO_PAGE_FAULT domain=0x001c address=0xfff00000 flags=0x0000] When running the pcitest.sh tests: the address used for a previous test transfer generates the above error while the next test transfer is running. Fix this by testing the DMA transfer status in pci_epf_test_dma_callback() and notifying the completion only when the transfer status is DMA_COMPLETE or DMA_ERROR. Furthermore, in pci_epf_test_data_transfer(), be paranoid and check again the transfer status and always call dmaengine_terminate_sync() before returning. Link: https://lore.kernel.org/r/20230415023542.77601-5-dlemoal@kernel.org Fixes: 8353813c88ef ("PCI: endpoint: Enable DMA tests for endpoints with DMA capabilities") Signed-off-by: Damien Le Moal Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Reviewed-by: Manivannan Sadhasivam Cc: stable@vger.kernel.org --- drivers/pci/endpoint/functions/pci-epf-test.c | 38 +++++++++++++++++++-------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index d65419735d2e..dbea6eb0dee7 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -54,6 +54,9 @@ struct pci_epf_test { struct delayed_work cmd_handler; struct dma_chan *dma_chan_tx; struct dma_chan *dma_chan_rx; + struct dma_chan *transfer_chan; + dma_cookie_t transfer_cookie; + enum dma_status transfer_status; struct completion transfer_complete; bool dma_supported; bool dma_private; @@ -85,8 +88,14 @@ static size_t bar_size[] = { 512, 512, 1024, 16384, 131072, 1048576 }; static void pci_epf_test_dma_callback(void *param) { struct pci_epf_test *epf_test = param; - - complete(&epf_test->transfer_complete); + struct dma_tx_state state; + + epf_test->transfer_status = + dmaengine_tx_status(epf_test->transfer_chan, + epf_test->transfer_cookie, &state); + if (epf_test->transfer_status == DMA_COMPLETE || + epf_test->transfer_status == DMA_ERROR) + complete(&epf_test->transfer_complete); } /** @@ -120,7 +129,6 @@ static int pci_epf_test_data_transfer(struct pci_epf_test *epf_test, struct dma_async_tx_descriptor *tx; struct dma_slave_config sconf = {}; struct device *dev = &epf->dev; - dma_cookie_t cookie; int ret; if (IS_ERR_OR_NULL(chan)) { @@ -152,25 +160,33 @@ static int pci_epf_test_data_transfer(struct pci_epf_test *epf_test, } reinit_completion(&epf_test->transfer_complete); + epf_test->transfer_chan = chan; tx->callback = pci_epf_test_dma_callback; tx->callback_param = epf_test; - cookie = tx->tx_submit(tx); + epf_test->transfer_cookie = tx->tx_submit(tx); - ret = dma_submit_error(cookie); + ret = dma_submit_error(epf_test->transfer_cookie); if (ret) { - dev_err(dev, "Failed to do DMA tx_submit %d\n", cookie); - return -EIO; + dev_err(dev, "Failed to do DMA tx_submit %d\n", ret); + goto terminate; } dma_async_issue_pending(chan); ret = wait_for_completion_interruptible(&epf_test->transfer_complete); if (ret < 0) { - dmaengine_terminate_sync(chan); - dev_err(dev, "DMA wait_for_completion_timeout\n"); - return -ETIMEDOUT; + dev_err(dev, "DMA wait_for_completion interrupted\n"); + goto terminate; } - return 0; + if (epf_test->transfer_status == DMA_ERROR) { + dev_err(dev, "DMA transfer failed\n"); + ret = -EIO; + } + +terminate: + dmaengine_terminate_sync(chan); + + return ret; } struct epf_dma_filter { -- cgit v1.2.3 From 349d5c840a5d2608bc910261a176a6bd355fcc73 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Sat, 15 Apr 2023 11:35:30 +0900 Subject: PCI: epf-test: Use dmaengine_submit() to initiate DMA transfer Instead of an open coded call to the tx_submit() operation of struct dma_async_tx_descriptor, use the helper function dmaengine_submit(). No functional change is introduced with this. Link: https://lore.kernel.org/r/20230415023542.77601-6-dlemoal@kernel.org Signed-off-by: Damien Le Moal Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Reviewed-by: Manivannan Sadhasivam --- drivers/pci/endpoint/functions/pci-epf-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index dbea6eb0dee7..7cdc6c915ef5 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -163,7 +163,7 @@ static int pci_epf_test_data_transfer(struct pci_epf_test *epf_test, epf_test->transfer_chan = chan; tx->callback = pci_epf_test_dma_callback; tx->callback_param = epf_test; - epf_test->transfer_cookie = tx->tx_submit(tx); + epf_test->transfer_cookie = dmaengine_submit(tx); ret = dma_submit_error(epf_test->transfer_cookie); if (ret) { -- cgit v1.2.3 From 62d48ec7ef87d65723e3efcbfa6178cf2f7d8156 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Sat, 15 Apr 2023 11:35:31 +0900 Subject: PCI: epf-test: Simplify read/write/copy test functions The function pci_epf_test_cmd_handler() uses the register BAR address as a pointer to a struct pci_epf_test_reg to determine the command sent by the host and to execute the test function accordingly. There is no need for doing this assignment again in each of the read, write and copy test functions. We can simply pass the reg pointer as an argument to the functions pci_epf_test_write(), pci_epf_test_read() and pci_epf_test_copy(). Link: https://lore.kernel.org/r/20230415023542.77601-7-dlemoal@kernel.org Signed-off-by: Damien Le Moal Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Reviewed-by: Manivannan Sadhasivam --- drivers/pci/endpoint/functions/pci-epf-test.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index 7cdc6c915ef5..b8b178ac7cda 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -325,7 +325,8 @@ static void pci_epf_test_print_rate(const char *ops, u64 size, (u64)ts.tv_sec, (u32)ts.tv_nsec, rate / 1024); } -static int pci_epf_test_copy(struct pci_epf_test *epf_test) +static int pci_epf_test_copy(struct pci_epf_test *epf_test, + struct pci_epf_test_reg *reg) { int ret; bool use_dma; @@ -337,8 +338,6 @@ static int pci_epf_test_copy(struct pci_epf_test *epf_test) struct pci_epf *epf = epf_test->epf; struct device *dev = &epf->dev; struct pci_epc *epc = epf->epc; - enum pci_barno test_reg_bar = epf_test->test_reg_bar; - struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar]; src_addr = pci_epc_mem_alloc_addr(epc, &src_phys_addr, reg->size); if (!src_addr) { @@ -424,7 +423,8 @@ err: return ret; } -static int pci_epf_test_read(struct pci_epf_test *epf_test) +static int pci_epf_test_read(struct pci_epf_test *epf_test, + struct pci_epf_test_reg *reg) { int ret; void __iomem *src_addr; @@ -438,8 +438,6 @@ static int pci_epf_test_read(struct pci_epf_test *epf_test) struct device *dev = &epf->dev; struct pci_epc *epc = epf->epc; struct device *dma_dev = epf->epc->dev.parent; - enum pci_barno test_reg_bar = epf_test->test_reg_bar; - struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar]; src_addr = pci_epc_mem_alloc_addr(epc, &phys_addr, reg->size); if (!src_addr) { @@ -514,7 +512,8 @@ err: return ret; } -static int pci_epf_test_write(struct pci_epf_test *epf_test) +static int pci_epf_test_write(struct pci_epf_test *epf_test, + struct pci_epf_test_reg *reg) { int ret; void __iomem *dst_addr; @@ -527,8 +526,6 @@ static int pci_epf_test_write(struct pci_epf_test *epf_test) struct device *dev = &epf->dev; struct pci_epc *epc = epf->epc; struct device *dma_dev = epf->epc->dev.parent; - enum pci_barno test_reg_bar = epf_test->test_reg_bar; - struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar]; dst_addr = pci_epc_mem_alloc_addr(epc, &phys_addr, reg->size); if (!dst_addr) { @@ -673,7 +670,7 @@ static void pci_epf_test_cmd_handler(struct work_struct *work) } if (command & COMMAND_WRITE) { - ret = pci_epf_test_write(epf_test); + ret = pci_epf_test_write(epf_test, reg); if (ret) reg->status |= STATUS_WRITE_FAIL; else @@ -684,7 +681,7 @@ static void pci_epf_test_cmd_handler(struct work_struct *work) } if (command & COMMAND_READ) { - ret = pci_epf_test_read(epf_test); + ret = pci_epf_test_read(epf_test, reg); if (!ret) reg->status |= STATUS_READ_SUCCESS; else @@ -695,7 +692,7 @@ static void pci_epf_test_cmd_handler(struct work_struct *work) } if (command & COMMAND_COPY) { - ret = pci_epf_test_copy(epf_test); + ret = pci_epf_test_copy(epf_test, reg); if (!ret) reg->status |= STATUS_COPY_SUCCESS; else -- cgit v1.2.3 From 5444737e16402db4a62fdf521a02c68fa84153a6 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Sat, 15 Apr 2023 11:35:32 +0900 Subject: PCI: epf-test: Simplify pci_epf_test_raise_irq() Change the interface of the function pci_epf_test_raise_irq() to directly pass a pointer to the struct pci_epf_test_reg defining the test being executed. This avoids the need for grabbing this pointer using the register BAR address and simplifies the call sites as the IRQ type and IRQ numbers do not have to be passed as arguments. Link: https://lore.kernel.org/r/20230415023542.77601-8-dlemoal@kernel.org Signed-off-by: Damien Le Moal Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Reviewed-by: Manivannan Sadhasivam --- drivers/pci/endpoint/functions/pci-epf-test.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index b8b178ac7cda..3835e558937a 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -607,29 +607,27 @@ err: return ret; } -static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test, u8 irq_type, - u16 irq) +static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test, + struct pci_epf_test_reg *reg) { struct pci_epf *epf = epf_test->epf; struct device *dev = &epf->dev; struct pci_epc *epc = epf->epc; - enum pci_barno test_reg_bar = epf_test->test_reg_bar; - struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar]; reg->status |= STATUS_IRQ_RAISED; - switch (irq_type) { + switch (reg->irq_type) { case IRQ_TYPE_LEGACY: pci_epc_raise_irq(epc, epf->func_no, epf->vfunc_no, PCI_EPC_IRQ_LEGACY, 0); break; case IRQ_TYPE_MSI: pci_epc_raise_irq(epc, epf->func_no, epf->vfunc_no, - PCI_EPC_IRQ_MSI, irq); + PCI_EPC_IRQ_MSI, reg->irq_number); break; case IRQ_TYPE_MSIX: pci_epc_raise_irq(epc, epf->func_no, epf->vfunc_no, - PCI_EPC_IRQ_MSIX, irq); + PCI_EPC_IRQ_MSIX, reg->irq_number); break; default: dev_err(dev, "Failed to raise IRQ, unknown type\n"); @@ -675,8 +673,7 @@ static void pci_epf_test_cmd_handler(struct work_struct *work) reg->status |= STATUS_WRITE_FAIL; else reg->status |= STATUS_WRITE_SUCCESS; - pci_epf_test_raise_irq(epf_test, reg->irq_type, - reg->irq_number); + pci_epf_test_raise_irq(epf_test, reg); goto reset_handler; } @@ -686,8 +683,7 @@ static void pci_epf_test_cmd_handler(struct work_struct *work) reg->status |= STATUS_READ_SUCCESS; else reg->status |= STATUS_READ_FAIL; - pci_epf_test_raise_irq(epf_test, reg->irq_type, - reg->irq_number); + pci_epf_test_raise_irq(epf_test, reg); goto reset_handler; } @@ -697,8 +693,7 @@ static void pci_epf_test_cmd_handler(struct work_struct *work) reg->status |= STATUS_COPY_SUCCESS; else reg->status |= STATUS_COPY_FAIL; - pci_epf_test_raise_irq(epf_test, reg->irq_type, - reg->irq_number); + pci_epf_test_raise_irq(epf_test, reg); goto reset_handler; } -- cgit v1.2.3 From 48d19fc6e4a74e8f7d395f0186cd9e6f93c6ee26 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Sat, 15 Apr 2023 11:35:33 +0900 Subject: PCI: epf-test: Simplify IRQ test commands execution For the commands COMMAND_RAISE_LEGACY_IRQ, COMMAND_RAISE_MSI_IRQ and COMMAND_RAISE_MSIX_IRQ, the function pci_epf_test_cmd_handler() sets the STATUS_IRQ_RAISED status flag and calls the epc function pci_epc_raise_irq() directly. However, this is also exactly what the pci_epf_test_raise_irq() function does. Avoid duplicating these operations by directly using pci_epf_test_raise_irq() for the IRQ test commands. It is OK to do so as the host side endpoint test driver always set the correct IRQ type for the IRQ test commands. At the same time, move the IRQ number check done for the COMMAND_RAISE_MSI_IRQ and COMMAND_RAISE_MSIX_IRQ commands to pci_epf_test_raise_irq(), to also check the IRQ number requested by the host for other test commands. This significantly simplifies pci_epf_test_cmd_handler(). Link: https://lore.kernel.org/r/20230415023542.77601-9-dlemoal@kernel.org Signed-off-by: Damien Le Moal Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Reviewed-by: Manivannan Sadhasivam --- drivers/pci/endpoint/functions/pci-epf-test.c | 43 +++++++++++---------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index 3835e558937a..ee90ba3a957b 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -613,6 +613,7 @@ static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test, struct pci_epf *epf = epf_test->epf; struct device *dev = &epf->dev; struct pci_epc *epc = epf->epc; + int count; reg->status |= STATUS_IRQ_RAISED; @@ -622,10 +623,22 @@ static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test, PCI_EPC_IRQ_LEGACY, 0); break; case IRQ_TYPE_MSI: + count = pci_epc_get_msi(epc, epf->func_no, epf->vfunc_no); + if (reg->irq_number > count || count <= 0) { + dev_err(dev, "Invalid MSI IRQ number %d / %d\n", + reg->irq_number, count); + return; + } pci_epc_raise_irq(epc, epf->func_no, epf->vfunc_no, PCI_EPC_IRQ_MSI, reg->irq_number); break; case IRQ_TYPE_MSIX: + count = pci_epc_get_msix(epc, epf->func_no, epf->vfunc_no); + if (reg->irq_number > count || count <= 0) { + dev_err(dev, "Invalid MSIX IRQ number %d / %d\n", + reg->irq_number, count); + return; + } pci_epc_raise_irq(epc, epf->func_no, epf->vfunc_no, PCI_EPC_IRQ_MSIX, reg->irq_number); break; @@ -638,13 +651,11 @@ static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test, static void pci_epf_test_cmd_handler(struct work_struct *work) { int ret; - int count; u32 command; struct pci_epf_test *epf_test = container_of(work, struct pci_epf_test, cmd_handler.work); struct pci_epf *epf = epf_test->epf; struct device *dev = &epf->dev; - struct pci_epc *epc = epf->epc; enum pci_barno test_reg_bar = epf_test->test_reg_bar; struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar]; @@ -660,10 +671,10 @@ static void pci_epf_test_cmd_handler(struct work_struct *work) goto reset_handler; } - if (command & COMMAND_RAISE_LEGACY_IRQ) { - reg->status = STATUS_IRQ_RAISED; - pci_epc_raise_irq(epc, epf->func_no, epf->vfunc_no, - PCI_EPC_IRQ_LEGACY, 0); + if ((command & COMMAND_RAISE_LEGACY_IRQ) || + (command & COMMAND_RAISE_MSI_IRQ) || + (command & COMMAND_RAISE_MSIX_IRQ)) { + pci_epf_test_raise_irq(epf_test, reg); goto reset_handler; } @@ -697,26 +708,6 @@ static void pci_epf_test_cmd_handler(struct work_struct *work) goto reset_handler; } - if (command & COMMAND_RAISE_MSI_IRQ) { - count = pci_epc_get_msi(epc, epf->func_no, epf->vfunc_no); - if (reg->irq_number > count || count <= 0) - goto reset_handler; - reg->status = STATUS_IRQ_RAISED; - pci_epc_raise_irq(epc, epf->func_no, epf->vfunc_no, - PCI_EPC_IRQ_MSI, reg->irq_number); - goto reset_handler; - } - - if (command & COMMAND_RAISE_MSIX_IRQ) { - count = pci_epc_get_msix(epc, epf->func_no, epf->vfunc_no); - if (reg->irq_number > count || count <= 0) - goto reset_handler; - reg->status = STATUS_IRQ_RAISED; - pci_epc_raise_irq(epc, epf->func_no, epf->vfunc_no, - PCI_EPC_IRQ_MSIX, reg->irq_number); - goto reset_handler; - } - reset_handler: queue_delayed_work(kpcitest_workqueue, &epf_test->cmd_handler, msecs_to_jiffies(1)); -- cgit v1.2.3 From fc97f5f7c23735da0c7314533203306d96a038fb Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Sat, 15 Apr 2023 11:35:34 +0900 Subject: PCI: epf-test: Improve handling of command and status registers The pci-epf-test driver uses the test register BAR memory directly to get and execute a test registers set by the RC side and defined using a struct pci_epf_test_reg. This direct use relies on using the register BAR address as a pointer to a struct pci_epf_test_reg to execute the test case and to send back the test result through the status field of struct pci_epf_test_reg. In practice, the status field is always updated before an interrupt is raised in pci_epf_test_raise_irq(), to ensure that the RC side sees the updated status when receiving an interrupt. However, such assignment direct access does not ensure that changes to the status register make it to memory, and so visible to the host, before an interrupt is raised, thus potentially resulting in the RC host not seeing the correct status result for a test. Avoid this potential problem by using READ_ONCE()/WRITE_ONCE() when accessing the command and status fields of a pci_epf_test_reg structure. This ensure that a test start (pci_epf_test_cmd_handler() function) and completion (with the function pci_epf_test_raise_irq()) achieve a correct synchronization with the MMIO register accesses on the RC host. Link: https://lore.kernel.org/r/20230415023542.77601-10-dlemoal@kernel.org Signed-off-by: Damien Le Moal Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Reviewed-by: Manivannan Sadhasivam --- drivers/pci/endpoint/functions/pci-epf-test.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index ee90ba3a957b..fa48e9b3c393 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -613,9 +613,14 @@ static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test, struct pci_epf *epf = epf_test->epf; struct device *dev = &epf->dev; struct pci_epc *epc = epf->epc; + u32 status = reg->status | STATUS_IRQ_RAISED; int count; - reg->status |= STATUS_IRQ_RAISED; + /* + * Set the status before raising the IRQ to ensure that the host sees + * the updated value when it gets the IRQ. + */ + WRITE_ONCE(reg->status, status); switch (reg->irq_type) { case IRQ_TYPE_LEGACY: @@ -659,12 +664,12 @@ static void pci_epf_test_cmd_handler(struct work_struct *work) enum pci_barno test_reg_bar = epf_test->test_reg_bar; struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar]; - command = reg->command; + command = READ_ONCE(reg->command); if (!command) goto reset_handler; - reg->command = 0; - reg->status = 0; + WRITE_ONCE(reg->command, 0); + WRITE_ONCE(reg->status, 0); if (reg->irq_type > IRQ_TYPE_MSIX) { dev_err(dev, "Failed to detect IRQ type\n"); -- cgit v1.2.3 From 96d513f5ed4cafafa31ed99f74ad527f6b0ff47b Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Sat, 15 Apr 2023 11:35:35 +0900 Subject: PCI: epf-test: Cleanup pci_epf_test_cmd_handler() Command codes are never combined together as flags into a single value. Thus we can replace the series of "if" tests in pci_epf_test_cmd_handler() with a cleaner switch-case statement. This also allows checking that we got a valid command and print an error message if we did not. Link: https://lore.kernel.org/r/20230415023542.77601-11-dlemoal@kernel.org Signed-off-by: Damien Le Moal Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Reviewed-by: Manivannan Sadhasivam --- drivers/pci/endpoint/functions/pci-epf-test.c | 30 +++++++++++++-------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index fa48e9b3c393..7f482ec08754 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -676,41 +676,39 @@ static void pci_epf_test_cmd_handler(struct work_struct *work) goto reset_handler; } - if ((command & COMMAND_RAISE_LEGACY_IRQ) || - (command & COMMAND_RAISE_MSI_IRQ) || - (command & COMMAND_RAISE_MSIX_IRQ)) { + switch (command) { + case COMMAND_RAISE_LEGACY_IRQ: + case COMMAND_RAISE_MSI_IRQ: + case COMMAND_RAISE_MSIX_IRQ: pci_epf_test_raise_irq(epf_test, reg); - goto reset_handler; - } - - if (command & COMMAND_WRITE) { + break; + case COMMAND_WRITE: ret = pci_epf_test_write(epf_test, reg); if (ret) reg->status |= STATUS_WRITE_FAIL; else reg->status |= STATUS_WRITE_SUCCESS; pci_epf_test_raise_irq(epf_test, reg); - goto reset_handler; - } - - if (command & COMMAND_READ) { + break; + case COMMAND_READ: ret = pci_epf_test_read(epf_test, reg); if (!ret) reg->status |= STATUS_READ_SUCCESS; else reg->status |= STATUS_READ_FAIL; pci_epf_test_raise_irq(epf_test, reg); - goto reset_handler; - } - - if (command & COMMAND_COPY) { + break; + case COMMAND_COPY: ret = pci_epf_test_copy(epf_test, reg); if (!ret) reg->status |= STATUS_COPY_SUCCESS; else reg->status |= STATUS_COPY_FAIL; pci_epf_test_raise_irq(epf_test, reg); - goto reset_handler; + break; + default: + dev_err(dev, "Invalid command 0x%x\n", command); + break; } reset_handler: -- cgit v1.2.3 From 2eec4bec3574ce1a8b0fcf568cebc82c1ad7ec80 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Sat, 15 Apr 2023 11:35:36 +0900 Subject: PCI: epf-test: Cleanup request result handling Each of the test functions pci_epf_test_write(), pci_epf_test_read() and pci_epf_test_copy() return an int result which is used by pci_epf_test_cmd_handler() to set a success or error bit in the request status. In the spirit of keeping the processing of each test case self-contained within its own test function, move the request status field update from pci_epf_test_cmd_handler() to each of these test functions and change these functions declaration to returning void. Link: https://lore.kernel.org/r/20230415023542.77601-12-dlemoal@kernel.org Signed-off-by: Damien Le Moal Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Reviewed-by: Manivannan Sadhasivam --- drivers/pci/endpoint/functions/pci-epf-test.c | 46 ++++++++++++--------------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index 7f482ec08754..e528b0915444 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -325,8 +325,8 @@ static void pci_epf_test_print_rate(const char *ops, u64 size, (u64)ts.tv_sec, (u32)ts.tv_nsec, rate / 1024); } -static int pci_epf_test_copy(struct pci_epf_test *epf_test, - struct pci_epf_test_reg *reg) +static void pci_epf_test_copy(struct pci_epf_test *epf_test, + struct pci_epf_test_reg *reg) { int ret; bool use_dma; @@ -420,11 +420,14 @@ err_src_addr: pci_epc_mem_free_addr(epc, src_phys_addr, src_addr, reg->size); err: - return ret; + if (!ret) + reg->status |= STATUS_COPY_SUCCESS; + else + reg->status |= STATUS_COPY_FAIL; } -static int pci_epf_test_read(struct pci_epf_test *epf_test, - struct pci_epf_test_reg *reg) +static void pci_epf_test_read(struct pci_epf_test *epf_test, + struct pci_epf_test_reg *reg) { int ret; void __iomem *src_addr; @@ -509,11 +512,14 @@ err_addr: pci_epc_mem_free_addr(epc, phys_addr, src_addr, reg->size); err: - return ret; + if (!ret) + reg->status |= STATUS_READ_SUCCESS; + else + reg->status |= STATUS_READ_FAIL; } -static int pci_epf_test_write(struct pci_epf_test *epf_test, - struct pci_epf_test_reg *reg) +static void pci_epf_test_write(struct pci_epf_test *epf_test, + struct pci_epf_test_reg *reg) { int ret; void __iomem *dst_addr; @@ -604,7 +610,10 @@ err_addr: pci_epc_mem_free_addr(epc, phys_addr, dst_addr, reg->size); err: - return ret; + if (!ret) + reg->status |= STATUS_WRITE_SUCCESS; + else + reg->status |= STATUS_WRITE_FAIL; } static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test, @@ -655,7 +664,6 @@ static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test, static void pci_epf_test_cmd_handler(struct work_struct *work) { - int ret; u32 command; struct pci_epf_test *epf_test = container_of(work, struct pci_epf_test, cmd_handler.work); @@ -683,27 +691,15 @@ static void pci_epf_test_cmd_handler(struct work_struct *work) pci_epf_test_raise_irq(epf_test, reg); break; case COMMAND_WRITE: - ret = pci_epf_test_write(epf_test, reg); - if (ret) - reg->status |= STATUS_WRITE_FAIL; - else - reg->status |= STATUS_WRITE_SUCCESS; + pci_epf_test_write(epf_test, reg); pci_epf_test_raise_irq(epf_test, reg); break; case COMMAND_READ: - ret = pci_epf_test_read(epf_test, reg); - if (!ret) - reg->status |= STATUS_READ_SUCCESS; - else - reg->status |= STATUS_READ_FAIL; + pci_epf_test_read(epf_test, reg); pci_epf_test_raise_irq(epf_test, reg); break; case COMMAND_COPY: - ret = pci_epf_test_copy(epf_test, reg); - if (!ret) - reg->status |= STATUS_COPY_SUCCESS; - else - reg->status |= STATUS_COPY_FAIL; + pci_epf_test_copy(epf_test, reg); pci_epf_test_raise_irq(epf_test, reg); break; default: -- cgit v1.2.3 From 2566cbea69ab8dad4996ab4b4840fd952e62e5b4 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Sat, 15 Apr 2023 11:35:37 +0900 Subject: PCI: epf-test: Simplify DMA support checks There is no need to have each read, write and copy test functions check for the FLAG_USE_DMA flag against the DMA support status indicated by epf_test->dma_supported. Move this test to the command handler function pci_epf_test_cmd_handler() to check once for all cases. Link: https://lore.kernel.org/r/20230415023542.77601-13-dlemoal@kernel.org Signed-off-by: Damien Le Moal Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Reviewed-by: Manivannan Sadhasivam --- drivers/pci/endpoint/functions/pci-epf-test.c | 45 +++++++++------------------ 1 file changed, 15 insertions(+), 30 deletions(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index e528b0915444..909e3e8ac01c 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -329,7 +329,6 @@ static void pci_epf_test_copy(struct pci_epf_test *epf_test, struct pci_epf_test_reg *reg) { int ret; - bool use_dma; void __iomem *src_addr; void __iomem *dst_addr; phys_addr_t src_phys_addr; @@ -372,14 +371,7 @@ static void pci_epf_test_copy(struct pci_epf_test *epf_test, } ktime_get_ts64(&start); - use_dma = !!(reg->flags & FLAG_USE_DMA); - if (use_dma) { - if (!epf_test->dma_supported) { - dev_err(dev, "Cannot transfer data using DMA\n"); - ret = -EINVAL; - goto err_map_addr; - } - + if (reg->flags & FLAG_USE_DMA) { if (epf_test->dma_private) { dev_err(dev, "Cannot transfer data using DMA\n"); ret = -EINVAL; @@ -405,7 +397,8 @@ static void pci_epf_test_copy(struct pci_epf_test *epf_test, kfree(buf); } ktime_get_ts64(&end); - pci_epf_test_print_rate("COPY", reg->size, &start, &end, use_dma); + pci_epf_test_print_rate("COPY", reg->size, &start, &end, + reg->flags & FLAG_USE_DMA); err_map_addr: pci_epc_unmap_addr(epc, epf->func_no, epf->vfunc_no, dst_phys_addr); @@ -433,7 +426,6 @@ static void pci_epf_test_read(struct pci_epf_test *epf_test, void __iomem *src_addr; void *buf; u32 crc32; - bool use_dma; phys_addr_t phys_addr; phys_addr_t dst_phys_addr; struct timespec64 start, end; @@ -464,14 +456,7 @@ static void pci_epf_test_read(struct pci_epf_test *epf_test, goto err_map_addr; } - use_dma = !!(reg->flags & FLAG_USE_DMA); - if (use_dma) { - if (!epf_test->dma_supported) { - dev_err(dev, "Cannot transfer data using DMA\n"); - ret = -EINVAL; - goto err_dma_map; - } - + if (reg->flags & FLAG_USE_DMA) { dst_phys_addr = dma_map_single(dma_dev, buf, reg->size, DMA_FROM_DEVICE); if (dma_mapping_error(dma_dev, dst_phys_addr)) { @@ -496,7 +481,8 @@ static void pci_epf_test_read(struct pci_epf_test *epf_test, ktime_get_ts64(&end); } - pci_epf_test_print_rate("READ", reg->size, &start, &end, use_dma); + pci_epf_test_print_rate("READ", reg->size, &start, &end, + reg->flags & FLAG_USE_DMA); crc32 = crc32_le(~0, buf, reg->size); if (crc32 != reg->checksum) @@ -524,7 +510,6 @@ static void pci_epf_test_write(struct pci_epf_test *epf_test, int ret; void __iomem *dst_addr; void *buf; - bool use_dma; phys_addr_t phys_addr; phys_addr_t src_phys_addr; struct timespec64 start, end; @@ -558,14 +543,7 @@ static void pci_epf_test_write(struct pci_epf_test *epf_test, get_random_bytes(buf, reg->size); reg->checksum = crc32_le(~0, buf, reg->size); - use_dma = !!(reg->flags & FLAG_USE_DMA); - if (use_dma) { - if (!epf_test->dma_supported) { - dev_err(dev, "Cannot transfer data using DMA\n"); - ret = -EINVAL; - goto err_dma_map; - } - + if (reg->flags & FLAG_USE_DMA) { src_phys_addr = dma_map_single(dma_dev, buf, reg->size, DMA_TO_DEVICE); if (dma_mapping_error(dma_dev, src_phys_addr)) { @@ -592,7 +570,8 @@ static void pci_epf_test_write(struct pci_epf_test *epf_test, ktime_get_ts64(&end); } - pci_epf_test_print_rate("WRITE", reg->size, &start, &end, use_dma); + pci_epf_test_print_rate("WRITE", reg->size, &start, &end, + reg->flags & FLAG_USE_DMA); /* * wait 1ms inorder for the write to complete. Without this delay L3 @@ -679,6 +658,12 @@ static void pci_epf_test_cmd_handler(struct work_struct *work) WRITE_ONCE(reg->command, 0); WRITE_ONCE(reg->status, 0); + if ((READ_ONCE(reg->flags) & FLAG_USE_DMA) && + !epf_test->dma_supported) { + dev_err(dev, "Cannot transfer data using DMA\n"); + goto reset_handler; + } + if (reg->irq_type > IRQ_TYPE_MSIX) { dev_err(dev, "Failed to detect IRQ type\n"); goto reset_handler; -- cgit v1.2.3 From 1754dfd2e7931f60d199a9cb044991ab80cdfe0b Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Sat, 15 Apr 2023 11:35:38 +0900 Subject: PCI: epf-test: Simplify transfers result print In pci_epf_test_print_rate(), instead of open coding a reduction loop to allow for a division by a 32-bits ns value, simply use div64_u64() to calculate the transfer rate. To match the printed unit of KB/s, this calculation divides the rate by 1000 instead of 1024 (that would be KiB/s unit). Change the format of the results printed by pci_epf_test_print_rate() to be more compact without the double new line. Also use dev_info() instead of pr_info(). Link: https://lore.kernel.org/r/20230415023542.77601-14-dlemoal@kernel.org Signed-off-by: Damien Le Moal Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Reviewed-by: Manivannan Sadhasivam --- drivers/pci/endpoint/functions/pci-epf-test.c | 39 ++++++++++----------------- 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index 909e3e8ac01c..0bb14fb21edd 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -295,34 +295,23 @@ static void pci_epf_test_clean_dma_chan(struct pci_epf_test *epf_test) return; } -static void pci_epf_test_print_rate(const char *ops, u64 size, +static void pci_epf_test_print_rate(struct pci_epf_test *epf_test, + const char *op, u64 size, struct timespec64 *start, struct timespec64 *end, bool dma) { - struct timespec64 ts; - u64 rate, ns; - - ts = timespec64_sub(*end, *start); - - /* convert both size (stored in 'rate') and time in terms of 'ns' */ - ns = timespec64_to_ns(&ts); - rate = size * NSEC_PER_SEC; - - /* Divide both size (stored in 'rate') and ns by a common factor */ - while (ns > UINT_MAX) { - rate >>= 1; - ns >>= 1; - } - - if (!ns) - return; + struct timespec64 ts = timespec64_sub(*end, *start); + u64 rate = 0, ns; /* calculate the rate */ - do_div(rate, (uint32_t)ns); + ns = timespec64_to_ns(&ts); + if (ns) + rate = div64_u64(size * NSEC_PER_SEC, ns * 1000); - pr_info("\n%s => Size: %llu bytes\t DMA: %s\t Time: %llu.%09u seconds\t" - "Rate: %llu KB/s\n", ops, size, dma ? "YES" : "NO", - (u64)ts.tv_sec, (u32)ts.tv_nsec, rate / 1024); + dev_info(&epf_test->epf->dev, + "%s => Size: %llu B, DMA: %s, Time: %llu.%09u s, Rate: %llu KB/s\n", + op, size, dma ? "YES" : "NO", + (u64)ts.tv_sec, (u32)ts.tv_nsec, rate); } static void pci_epf_test_copy(struct pci_epf_test *epf_test, @@ -397,7 +386,7 @@ static void pci_epf_test_copy(struct pci_epf_test *epf_test, kfree(buf); } ktime_get_ts64(&end); - pci_epf_test_print_rate("COPY", reg->size, &start, &end, + pci_epf_test_print_rate(epf_test, "COPY", reg->size, &start, &end, reg->flags & FLAG_USE_DMA); err_map_addr: @@ -481,7 +470,7 @@ static void pci_epf_test_read(struct pci_epf_test *epf_test, ktime_get_ts64(&end); } - pci_epf_test_print_rate("READ", reg->size, &start, &end, + pci_epf_test_print_rate(epf_test, "READ", reg->size, &start, &end, reg->flags & FLAG_USE_DMA); crc32 = crc32_le(~0, buf, reg->size); @@ -570,7 +559,7 @@ static void pci_epf_test_write(struct pci_epf_test *epf_test, ktime_get_ts64(&end); } - pci_epf_test_print_rate("WRITE", reg->size, &start, &end, + pci_epf_test_print_rate(epf_test, "WRITE", reg->size, &start, &end, reg->flags & FLAG_USE_DMA); /* -- cgit v1.2.3 From f61b7634a3249d12b9daa36ffbdb9965b6f24c6c Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Sat, 15 Apr 2023 11:35:39 +0900 Subject: misc: pci_endpoint_test: Free IRQs before removing the device In pci_endpoint_test_remove(), freeing the IRQs after removing the device creates a small race window for IRQs to be received with the test device memory already released, causing the IRQ handler to access invalid memory, resulting in an oops. Free the device IRQs before removing the device to avoid this issue. Link: https://lore.kernel.org/r/20230415023542.77601-15-dlemoal@kernel.org Fixes: e03327122e2c ("pci_endpoint_test: Add 2 ioctl commands") Signed-off-by: Damien Le Moal Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Reviewed-by: Manivannan Sadhasivam Cc: stable@vger.kernel.org --- drivers/misc/pci_endpoint_test.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index a7244de081ec..01235236e9bc 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -938,6 +938,9 @@ static void pci_endpoint_test_remove(struct pci_dev *pdev) if (id < 0) return; + pci_endpoint_test_release_irq(test); + pci_endpoint_test_free_irq_vectors(test); + misc_deregister(&test->miscdev); kfree(misc_device->name); kfree(test->name); @@ -947,9 +950,6 @@ static void pci_endpoint_test_remove(struct pci_dev *pdev) pci_iounmap(pdev, test->bar[bar]); } - pci_endpoint_test_release_irq(test); - pci_endpoint_test_free_irq_vectors(test); - pci_release_regions(pdev); pci_disable_device(pdev); } -- cgit v1.2.3 From fb620ae73b70c2f57b9d3e911fc24c024ba2324f Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Sat, 15 Apr 2023 11:35:40 +0900 Subject: misc: pci_endpoint_test: Re-init completion for every test The irq_raised completion used to detect the end of a test case is initialized when the test device is probed, but never reinitialized again before a test case. As a result, the irq_raised completion synchronization is effective only for the first ioctl test case executed. Any subsequent call to wait_for_completion() by another ioctl() call will immediately return, potentially too early, leading to false positive failures. Fix this by reinitializing the irq_raised completion before starting a new ioctl() test command. Link: https://lore.kernel.org/r/20230415023542.77601-16-dlemoal@kernel.org Fixes: 2c156ac71c6b ("misc: Add host side PCI driver for PCI test function device") Signed-off-by: Damien Le Moal Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Reviewed-by: Manivannan Sadhasivam Cc: stable@vger.kernel.org --- drivers/misc/pci_endpoint_test.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index 01235236e9bc..24efe3b88a1f 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -729,6 +729,10 @@ static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd, struct pci_dev *pdev = test->pdev; mutex_lock(&test->mutex); + + reinit_completion(&test->irq_raised); + test->last_irq = -ENODATA; + switch (cmd) { case PCITEST_BAR: bar = arg; -- cgit v1.2.3 From 168e6f62e4298815125591ff9c85d374b2a93c6c Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Sat, 15 Apr 2023 11:35:41 +0900 Subject: misc: pci_endpoint_test: Do not write status in IRQ handler pci_endpoint_test_irqhandler() always rewrites the status register when an IRQ is raised, either as-is if STATUS_IRQ_RAISED is not set, or with STATUS_IRQ_RAISED cleared if that flag is set. The first case creates a race window with the endpoint side, meaning that the host side test driver may end up reading what it just wrote, thus losing the real status as set by the endpoint side before raising the next interrupt. This can prevent detecting that the STATUS_IRQ_RAISED flag was set by the endpoint. Remove this race window by not clearing the STATUS_IRQ_RAISED status flag and not rewriting that register for every IRQ received. Link: https://lore.kernel.org/r/20230415023542.77601-17-dlemoal@kernel.org Signed-off-by: Damien Le Moal Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Reviewed-by: Manivannan Sadhasivam --- drivers/misc/pci_endpoint_test.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index 24efe3b88a1f..afd2577261f8 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -159,10 +159,7 @@ static irqreturn_t pci_endpoint_test_irqhandler(int irq, void *dev_id) if (reg & STATUS_IRQ_RAISED) { test->last_irq = irq; complete(&test->irq_raised); - reg &= ~STATUS_IRQ_RAISED; } - pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_STATUS, - reg); return IRQ_HANDLED; } -- cgit v1.2.3 From 4c50f933f50e018653a11bd77eb872d46d67c193 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Sat, 15 Apr 2023 11:35:42 +0900 Subject: misc: pci_endpoint_test: Simplify pci_endpoint_test_msi_irq() Simplify the code of pci_endpoint_test_msi_irq() by correctly using booleans: remove the msix comparison to false as that variable is already a boolean, and directly return the result of the comparison of the raised interrupt number. Link: https://lore.kernel.org/r/20230415023542.77601-18-dlemoal@kernel.org Signed-off-by: Damien Le Moal Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Reviewed-by: Manivannan Sadhasivam --- drivers/misc/pci_endpoint_test.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index afd2577261f8..ed4d0ef5e5c3 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -313,21 +313,17 @@ static bool pci_endpoint_test_msi_irq(struct pci_endpoint_test *test, struct pci_dev *pdev = test->pdev; pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, - msix == false ? IRQ_TYPE_MSI : - IRQ_TYPE_MSIX); + msix ? IRQ_TYPE_MSIX : IRQ_TYPE_MSI); pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, msi_num); pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, - msix == false ? COMMAND_RAISE_MSI_IRQ : - COMMAND_RAISE_MSIX_IRQ); + msix ? COMMAND_RAISE_MSIX_IRQ : + COMMAND_RAISE_MSI_IRQ); val = wait_for_completion_timeout(&test->irq_raised, msecs_to_jiffies(1000)); if (!val) return false; - if (pci_irq_vector(pdev, msi_num - 1) == test->last_irq) - return true; - - return false; + return pci_irq_vector(pdev, msi_num - 1) == test->last_irq; } static int pci_endpoint_test_validate_xfer_params(struct device *dev, -- cgit v1.2.3 From 880d51c729a3fa944794feb19f605eefe55916fc Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Wed, 12 Apr 2023 15:34:47 +0900 Subject: PCI: endpoint: functions/pci-epf-test: Fix dma_chan direction In pci_epf_test_init_dma_chan() epf_test->dma_chan_rx is assigned from dma_request_channel() with DMA_DEV_TO_MEM as filter.dma_mask. However, in pci_epf_test_data_transfer() if the dir is DMA_DEV_TO_MEM, epf->dma_chan_rx should be used but instead we are using epf_test->dma_chan_tx. Fix it. Link: https://lore.kernel.org/r/20230412063447.2841177-1-yoshihiro.shimoda.uh@renesas.com Fixes: 8353813c88ef ("PCI: endpoint: Enable DMA tests for endpoints with DMA capabilities") Tested-by: Kunihiko Hayashi Signed-off-by: Yoshihiro Shimoda Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Reviewed-by: Frank Li --- drivers/pci/endpoint/functions/pci-epf-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index 0bb14fb21edd..623b08caa998 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -121,7 +121,7 @@ static int pci_epf_test_data_transfer(struct pci_epf_test *epf_test, size_t len, dma_addr_t dma_remote, enum dma_transfer_direction dir) { - struct dma_chan *chan = (dir == DMA_DEV_TO_MEM) ? + struct dma_chan *chan = (dir == DMA_MEM_TO_DEV) ? epf_test->dma_chan_tx : epf_test->dma_chan_rx; dma_addr_t dma_local = (dir == DMA_MEM_TO_DEV) ? dma_src : dma_dst; enum dma_ctrl_flags flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; -- cgit v1.2.3 From b6a6e0331fad61e38316a00e14ef6381d9f03161 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Mon, 15 May 2023 16:43:47 +0900 Subject: PCI: endpoint: Improve pci_epf_type_add_cfs() pci_epf_type_add_cfs() should not be called with an unbound EPF device, that is, an epf device with epf->driver not set. For such case, replace the NULL return in pci_epf_type_add_cfs() with a clear ERR_PTR(-ENODEV) pointer error return. Link: https://lore.kernel.org/r/20230515074348.595704-2-dlemoal@kernel.org Signed-off-by: Damien Le Moal Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Reviewed-by: Manivannan Sadhasivami --- drivers/pci/endpoint/pci-ep-cfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/endpoint/pci-ep-cfs.c b/drivers/pci/endpoint/pci-ep-cfs.c index cd99ac8c3794..18e061af1d8e 100644 --- a/drivers/pci/endpoint/pci-ep-cfs.c +++ b/drivers/pci/endpoint/pci-ep-cfs.c @@ -532,7 +532,7 @@ static struct config_group *pci_epf_type_add_cfs(struct pci_epf *epf, if (!epf->driver) { dev_err(&epf->dev, "epf device not bound to driver\n"); - return NULL; + return ERR_PTR(-ENODEV); } if (!epf->driver->ops->add_cfs) -- cgit v1.2.3 From ff2f19d6f1fcb6128950263c3ea46ff1aefec54f Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Fri, 2 Jun 2023 17:17:48 +0530 Subject: PCI: endpoint: Add missing documentation about the MSI/MSI-X range Both pci_epc_raise_irq() and pci_epc_map_msi_irq() APIs expect the MSI/MSI-X vectors to start from 1 but it is not documented. Add the range info to the kdoc of the APIs to make it clear. Link: https://lore.kernel.org/r/20230602114756.36586-2-manivannan.sadhasivam@linaro.org Fixes: 5e8cb4033807 ("PCI: endpoint: Add EP core layer to enable EP controller and EP functions") Fixes: 87d5972e476f ("PCI: endpoint: Add pci_epc_ops to map MSI IRQ") Signed-off-by: Manivannan Sadhasivam Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Reviewed-by: Damien Le Moal --- drivers/pci/endpoint/pci-epc-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index 46c9a5c3ca14..0cf602c83d4a 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -213,7 +213,7 @@ EXPORT_SYMBOL_GPL(pci_epc_start); * @func_no: the physical endpoint function number in the EPC device * @vfunc_no: the virtual endpoint function number in the physical function * @type: specify the type of interrupt; legacy, MSI or MSI-X - * @interrupt_num: the MSI or MSI-X interrupt number + * @interrupt_num: the MSI or MSI-X interrupt number with range (1-N) * * Invoke to raise an legacy, MSI or MSI-X interrupt */ @@ -246,7 +246,7 @@ EXPORT_SYMBOL_GPL(pci_epc_raise_irq); * @func_no: the physical endpoint function number in the EPC device * @vfunc_no: the virtual endpoint function number in the physical function * @phys_addr: the physical address of the outbound region - * @interrupt_num: the MSI interrupt number + * @interrupt_num: the MSI interrupt number with range (1-N) * @entry_size: Size of Outbound address region for each interrupt * @msi_data: the data that should be written in order to raise MSI interrupt * with interrupt number as 'interrupt num' -- cgit v1.2.3 From 081c715dfd50542e89df5ee12a8e32e7ed936cd1 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Fri, 2 Jun 2023 17:17:49 +0530 Subject: PCI: endpoint: Pass EPF device ID to the probe function Currently, the EPF probe function doesn't get the device ID argument needed to correctly identify the device table ID of the EPF device. When multiple entries are added to the "struct pci_epf_device_id" table, the probe function needs to identify the correct one. This is achieved by modifying the pci_epf_match_id() function to return the match ID pointer and passing it to the driver's probe function. pci_epf_device_match() function can return bool based on the return value of pci_epf_match_id(). Link: https://lore.kernel.org/r/20230602114756.36586-3-manivannan.sadhasivam@linaro.org Signed-off-by: Manivannan Sadhasivam Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Reviewed-by: Kishon Vijay Abraham I Reviewed-by: Damien Le Moal --- drivers/pci/endpoint/functions/pci-epf-ntb.c | 4 +++- drivers/pci/endpoint/functions/pci-epf-test.c | 3 ++- drivers/pci/endpoint/functions/pci-epf-vntb.c | 4 +++- drivers/pci/endpoint/pci-epf-core.c | 10 +++++----- include/linux/pci-epf.h | 5 ++++- 5 files changed, 17 insertions(+), 9 deletions(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-ntb.c b/drivers/pci/endpoint/functions/pci-epf-ntb.c index 9a00448c7e61..9aac2c6f3bb9 100644 --- a/drivers/pci/endpoint/functions/pci-epf-ntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-ntb.c @@ -2075,11 +2075,13 @@ static struct config_group *epf_ntb_add_cfs(struct pci_epf *epf, /** * epf_ntb_probe() - Probe NTB function driver * @epf: NTB endpoint function device + * @id: NTB endpoint function device ID * * Probe NTB function driver when endpoint function bus detects a NTB * endpoint function. */ -static int epf_ntb_probe(struct pci_epf *epf) +static int epf_ntb_probe(struct pci_epf *epf, + const struct pci_epf_device_id *id) { struct epf_ntb *ntb; struct device *dev; diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index 623b08caa998..1f0d2b84296a 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -952,7 +952,8 @@ static const struct pci_epf_device_id pci_epf_test_ids[] = { {}, }; -static int pci_epf_test_probe(struct pci_epf *epf) +static int pci_epf_test_probe(struct pci_epf *epf, + const struct pci_epf_device_id *id) { struct pci_epf_test *epf_test; struct device *dev = &epf->dev; diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c index b7c7a8af99f4..6b433cfbe471 100644 --- a/drivers/pci/endpoint/functions/pci-epf-vntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c @@ -1395,13 +1395,15 @@ static struct pci_epf_ops epf_ntb_ops = { /** * epf_ntb_probe() - Probe NTB function driver * @epf: NTB endpoint function device + * @id: NTB endpoint function device ID * * Probe NTB function driver when endpoint function bus detects a NTB * endpoint function. * * Returns: Zero for success, or an error code in case of failure */ -static int epf_ntb_probe(struct pci_epf *epf) +static int epf_ntb_probe(struct pci_epf *epf, + const struct pci_epf_device_id *id) { struct epf_ntb *ntb; struct device *dev; diff --git a/drivers/pci/endpoint/pci-epf-core.c b/drivers/pci/endpoint/pci-epf-core.c index 355a6f56fcea..2c32de667937 100644 --- a/drivers/pci/endpoint/pci-epf-core.c +++ b/drivers/pci/endpoint/pci-epf-core.c @@ -461,16 +461,16 @@ static const struct device_type pci_epf_type = { .release = pci_epf_dev_release, }; -static int +static const struct pci_epf_device_id * pci_epf_match_id(const struct pci_epf_device_id *id, const struct pci_epf *epf) { while (id->name[0]) { if (strcmp(epf->name, id->name) == 0) - return true; + return id; id++; } - return false; + return NULL; } static int pci_epf_device_match(struct device *dev, struct device_driver *drv) @@ -479,7 +479,7 @@ static int pci_epf_device_match(struct device *dev, struct device_driver *drv) struct pci_epf_driver *driver = to_pci_epf_driver(drv); if (driver->id_table) - return pci_epf_match_id(driver->id_table, epf); + return !!pci_epf_match_id(driver->id_table, epf); return !strcmp(epf->name, drv->name); } @@ -494,7 +494,7 @@ static int pci_epf_device_probe(struct device *dev) epf->driver = driver; - return driver->probe(epf); + return driver->probe(epf, pci_epf_match_id(driver->id_table, epf)); } static void pci_epf_device_remove(struct device *dev) diff --git a/include/linux/pci-epf.h b/include/linux/pci-epf.h index b8441db2fa52..a3bad37ccae6 100644 --- a/include/linux/pci-epf.h +++ b/include/linux/pci-epf.h @@ -89,7 +89,8 @@ struct pci_epc_event_ops { * @id_table: identifies EPF devices for probing */ struct pci_epf_driver { - int (*probe)(struct pci_epf *epf); + int (*probe)(struct pci_epf *epf, + const struct pci_epf_device_id *id); void (*remove)(struct pci_epf *epf); struct device_driver driver; @@ -131,6 +132,7 @@ struct pci_epf_bar { * @epc: the EPC device to which this EPF device is bound * @epf_pf: the physical EPF device to which this virtual EPF device is bound * @driver: the EPF driver to which this EPF device is bound + * @id: Pointer to the EPF device ID * @list: to add pci_epf as a list of PCI endpoint functions to pci_epc * @lock: mutex to protect pci_epf_ops * @sec_epc: the secondary EPC device to which this EPF device is bound @@ -158,6 +160,7 @@ struct pci_epf { struct pci_epc *epc; struct pci_epf *epf_pf; struct pci_epf_driver *driver; + const struct pci_epf_device_id *id; struct list_head list; /* mutex to protect against concurrent access of pci_epf_ops */ struct mutex lock; -- cgit v1.2.3 From a504c965588b781f864364e897917a2c7b48ea5b Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Fri, 2 Jun 2023 17:17:50 +0530 Subject: PCI: endpoint: Return error if EPC is started/stopped multiple times When the EPC is started or stopped multiple times from configfs, just return -EALREADY. There is no need to call the EPC start/stop functions in those cases. Link: https://lore.kernel.org/r/20230602114756.36586-4-manivannan.sadhasivam@linaro.org Signed-off-by: Manivannan Sadhasivam Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Reviewed-by: Kishon Vijay Abraham I Reviewed-by: Damien Le Moal --- drivers/pci/endpoint/pci-ep-cfs.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/pci/endpoint/pci-ep-cfs.c b/drivers/pci/endpoint/pci-ep-cfs.c index 18e061af1d8e..0ea64e24ed61 100644 --- a/drivers/pci/endpoint/pci-ep-cfs.c +++ b/drivers/pci/endpoint/pci-ep-cfs.c @@ -179,6 +179,9 @@ static ssize_t pci_epc_start_store(struct config_item *item, const char *page, if (kstrtobool(page, &start) < 0) return -EINVAL; + if (start == epc_group->start) + return -EALREADY; + if (!start) { pci_epc_stop(epc); epc_group->start = 0; -- cgit v1.2.3 From a1f6c3d7d3a2fdcb7bf77da17a17944c81ca13de Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Fri, 2 Jun 2023 17:17:51 +0530 Subject: PCI: endpoint: Add linkdown notifier support Add support to notify the EPF device about the linkdown event from the EPC device. Link: https://lore.kernel.org/r/20230602114756.36586-5-manivannan.sadhasivam@linaro.org Signed-off-by: Manivannan Sadhasivam Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Reviewed-by: Kishon Vijay Abraham I Reviewed-by: Damien Le Moal --- drivers/pci/endpoint/pci-epc-core.c | 26 ++++++++++++++++++++++++++ include/linux/pci-epc.h | 1 + include/linux/pci-epf.h | 2 ++ 3 files changed, 29 insertions(+) diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index 0cf602c83d4a..e0570b52698d 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -706,6 +706,32 @@ void pci_epc_linkup(struct pci_epc *epc) } EXPORT_SYMBOL_GPL(pci_epc_linkup); +/** + * pci_epc_linkdown() - Notify the EPF device that EPC device has dropped the + * connection with the Root Complex. + * @epc: the EPC device which has dropped the link with the host + * + * Invoke to Notify the EPF device that the EPC device has dropped the + * connection with the Root Complex. + */ +void pci_epc_linkdown(struct pci_epc *epc) +{ + struct pci_epf *epf; + + if (!epc || IS_ERR(epc)) + return; + + mutex_lock(&epc->list_lock); + list_for_each_entry(epf, &epc->pci_epf, list) { + mutex_lock(&epf->lock); + if (epf->event_ops && epf->event_ops->link_down) + epf->event_ops->link_down(epf); + mutex_unlock(&epf->lock); + } + mutex_unlock(&epc->list_lock); +} +EXPORT_SYMBOL_GPL(pci_epc_linkdown); + /** * pci_epc_init_notify() - Notify the EPF device that EPC device's core * initialization is completed. diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index 301bb0e53707..63a6cc5e5282 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -203,6 +203,7 @@ void pci_epc_destroy(struct pci_epc *epc); int pci_epc_add_epf(struct pci_epc *epc, struct pci_epf *epf, enum pci_epc_interface_type type); void pci_epc_linkup(struct pci_epc *epc); +void pci_epc_linkdown(struct pci_epc *epc); void pci_epc_init_notify(struct pci_epc *epc); void pci_epc_remove_epf(struct pci_epc *epc, struct pci_epf *epf, enum pci_epc_interface_type type); diff --git a/include/linux/pci-epf.h b/include/linux/pci-epf.h index a3bad37ccae6..73d783af4d56 100644 --- a/include/linux/pci-epf.h +++ b/include/linux/pci-epf.h @@ -71,10 +71,12 @@ struct pci_epf_ops { * struct pci_epf_event_ops - Callbacks for capturing the EPC events * @core_init: Callback for the EPC initialization complete event * @link_up: Callback for the EPC link up event + * @link_down: Callback for the EPC link down event */ struct pci_epc_event_ops { int (*core_init)(struct pci_epf *epf); int (*link_up)(struct pci_epf *epf); + int (*link_down)(struct pci_epf *epf); }; /** -- cgit v1.2.3 From 6360efb96b19d89990b2a5bf3a73c689a429f5da Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Fri, 2 Jun 2023 17:17:52 +0530 Subject: PCI: endpoint: Add BME notifier support Add support to notify the EPF device about the Bus Master Enable (BME) event received by the EPC device from the Root complex. Link: https://lore.kernel.org/r/20230602114756.36586-6-manivannan.sadhasivam@linaro.org Signed-off-by: Manivannan Sadhasivam Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Reviewed-by: Kishon Vijay Abraham I Reviewed-by: Damien Le Moal --- drivers/pci/endpoint/pci-epc-core.c | 26 ++++++++++++++++++++++++++ include/linux/pci-epc.h | 1 + include/linux/pci-epf.h | 2 ++ 3 files changed, 29 insertions(+) diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index e0570b52698d..6c54fa5684d2 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -758,6 +758,32 @@ void pci_epc_init_notify(struct pci_epc *epc) } EXPORT_SYMBOL_GPL(pci_epc_init_notify); +/** + * pci_epc_bme_notify() - Notify the EPF device that the EPC device has received + * the BME event from the Root complex + * @epc: the EPC device that received the BME event + * + * Invoke to Notify the EPF device that the EPC device has received the Bus + * Master Enable (BME) event from the Root complex + */ +void pci_epc_bme_notify(struct pci_epc *epc) +{ + struct pci_epf *epf; + + if (!epc || IS_ERR(epc)) + return; + + mutex_lock(&epc->list_lock); + list_for_each_entry(epf, &epc->pci_epf, list) { + mutex_lock(&epf->lock); + if (epf->event_ops && epf->event_ops->bme) + epf->event_ops->bme(epf); + mutex_unlock(&epf->lock); + } + mutex_unlock(&epc->list_lock); +} +EXPORT_SYMBOL_GPL(pci_epc_bme_notify); + /** * pci_epc_destroy() - destroy the EPC device * @epc: the EPC device that has to be destroyed diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index 63a6cc5e5282..5cb694031072 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -205,6 +205,7 @@ int pci_epc_add_epf(struct pci_epc *epc, struct pci_epf *epf, void pci_epc_linkup(struct pci_epc *epc); void pci_epc_linkdown(struct pci_epc *epc); void pci_epc_init_notify(struct pci_epc *epc); +void pci_epc_bme_notify(struct pci_epc *epc); void pci_epc_remove_epf(struct pci_epc *epc, struct pci_epf *epf, enum pci_epc_interface_type type); int pci_epc_write_header(struct pci_epc *epc, u8 func_no, u8 vfunc_no, diff --git a/include/linux/pci-epf.h b/include/linux/pci-epf.h index 73d783af4d56..3f44b6aec477 100644 --- a/include/linux/pci-epf.h +++ b/include/linux/pci-epf.h @@ -72,11 +72,13 @@ struct pci_epf_ops { * @core_init: Callback for the EPC initialization complete event * @link_up: Callback for the EPC link up event * @link_down: Callback for the EPC link down event + * @bme: Callback for the EPC BME (Bus Master Enable) event */ struct pci_epc_event_ops { int (*core_init)(struct pci_epf *epf); int (*link_up)(struct pci_epf *epf); int (*link_down)(struct pci_epf *epf); + int (*bme)(struct pci_epf *epf); }; /** -- cgit v1.2.3 From c47c74b7217a3a142d6c7f0371d8e0240acb21a0 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Fri, 2 Jun 2023 17:17:53 +0530 Subject: PCI: qcom-ep: Add support for Link down notification Add support to pass Link down notification to Endpoint function driver so that the LINK_DOWN event can be processed by the function. Link: https://lore.kernel.org/r/20230602114756.36586-7-manivannan.sadhasivam@linaro.org Signed-off-by: Manivannan Sadhasivam Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Reviewed-by: Kishon Vijay Abraham I Reviewed-by: Damien Le Moal --- drivers/pci/controller/dwc/pcie-qcom-ep.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c index 19b32839ea26..4ce01ff7527c 100644 --- a/drivers/pci/controller/dwc/pcie-qcom-ep.c +++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c @@ -569,6 +569,7 @@ static irqreturn_t qcom_pcie_ep_global_irq_thread(int irq, void *data) if (FIELD_GET(PARF_INT_ALL_LINK_DOWN, status)) { dev_dbg(dev, "Received Linkdown event\n"); pcie_ep->link_status = QCOM_PCIE_EP_LINK_DOWN; + pci_epc_linkdown(pci->ep.epc); } else if (FIELD_GET(PARF_INT_ALL_BME, status)) { dev_dbg(dev, "Received BME event. Link is enabled!\n"); pcie_ep->link_status = QCOM_PCIE_EP_LINK_ENABLED; -- cgit v1.2.3 From 8f05cd35c73b97b9df759dd70e3ad26bc7482a7d Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Fri, 2 Jun 2023 17:17:54 +0530 Subject: PCI: qcom-ep: Add support for BME notification Add support to pass BME (Bus Master Enable) notification to Endpoint function driver so that the BME event can be processed by the function. Link: https://lore.kernel.org/r/20230602114756.36586-8-manivannan.sadhasivam@linaro.org Signed-off-by: Manivannan Sadhasivam Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Reviewed-by: Kishon Vijay Abraham I Reviewed-by: Damien Le Moal --- drivers/pci/controller/dwc/pcie-qcom-ep.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c index 4ce01ff7527c..1435f516d3f7 100644 --- a/drivers/pci/controller/dwc/pcie-qcom-ep.c +++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c @@ -573,6 +573,7 @@ static irqreturn_t qcom_pcie_ep_global_irq_thread(int irq, void *data) } else if (FIELD_GET(PARF_INT_ALL_BME, status)) { dev_dbg(dev, "Received BME event. Link is enabled!\n"); pcie_ep->link_status = QCOM_PCIE_EP_LINK_ENABLED; + pci_epc_bme_notify(pci->ep.epc); } else if (FIELD_GET(PARF_INT_ALL_PM_TURNOFF, status)) { dev_dbg(dev, "Received PM Turn-off event! Entering L23\n"); val = readl_relaxed(pcie_ep->parf + PARF_PM_CTRL); -- cgit v1.2.3 From 1bf5f25324f7f6a52c3eb566ec5f78f6a901db96 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Fri, 2 Jun 2023 17:17:55 +0530 Subject: PCI: endpoint: Add PCI Endpoint function driver for MHI bus Add PCI Endpoint driver for the Qualcomm MHI (Modem Host Interface) bus. The driver implements the MHI function over PCI in the endpoint device such as SDX55 modem. The MHI endpoint function driver acts as a controller driver for the MHI Endpoint stack and carries out all PCI related functionality. Link: https://lore.kernel.org/r/20230602114756.36586-9-manivannan.sadhasivam@linaro.org Signed-off-by: Manivannan Sadhasivam Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Reviewed-by: Kishon Vijay Abraham I Reviewed-by: Damien Le Moal --- drivers/pci/endpoint/functions/Kconfig | 10 + drivers/pci/endpoint/functions/Makefile | 1 + drivers/pci/endpoint/functions/pci-epf-mhi.c | 458 +++++++++++++++++++++++++++ 3 files changed, 469 insertions(+) create mode 100644 drivers/pci/endpoint/functions/pci-epf-mhi.c diff --git a/drivers/pci/endpoint/functions/Kconfig b/drivers/pci/endpoint/functions/Kconfig index 8efb6a869e7c..0c9cea0698d7 100644 --- a/drivers/pci/endpoint/functions/Kconfig +++ b/drivers/pci/endpoint/functions/Kconfig @@ -37,3 +37,13 @@ config PCI_EPF_VNTB between PCI Root Port and PCIe Endpoint. If in doubt, say "N" to disable Endpoint NTB driver. + +config PCI_EPF_MHI + tristate "PCI Endpoint driver for MHI bus" + depends on PCI_ENDPOINT && MHI_BUS_EP + help + Enable this configuration option to enable the PCI Endpoint + driver for Modem Host Interface (MHI) bus in Qualcomm Endpoint + devices such as SDX55. + + If in doubt, say "N" to disable Endpoint driver for MHI bus. diff --git a/drivers/pci/endpoint/functions/Makefile b/drivers/pci/endpoint/functions/Makefile index 5c13001deaba..696473fce50e 100644 --- a/drivers/pci/endpoint/functions/Makefile +++ b/drivers/pci/endpoint/functions/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_PCI_EPF_TEST) += pci-epf-test.o obj-$(CONFIG_PCI_EPF_NTB) += pci-epf-ntb.o obj-$(CONFIG_PCI_EPF_VNTB) += pci-epf-vntb.o +obj-$(CONFIG_PCI_EPF_MHI) += pci-epf-mhi.o diff --git a/drivers/pci/endpoint/functions/pci-epf-mhi.c b/drivers/pci/endpoint/functions/pci-epf-mhi.c new file mode 100644 index 000000000000..9c1f5a154fbd --- /dev/null +++ b/drivers/pci/endpoint/functions/pci-epf-mhi.c @@ -0,0 +1,458 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCI EPF driver for MHI Endpoint devices + * + * Copyright (C) 2023 Linaro Ltd. + * Author: Manivannan Sadhasivam + */ + +#include +#include +#include +#include +#include + +#define MHI_VERSION_1_0 0x01000000 + +#define to_epf_mhi(cntrl) container_of(cntrl, struct pci_epf_mhi, cntrl) + +struct pci_epf_mhi_ep_info { + const struct mhi_ep_cntrl_config *config; + struct pci_epf_header *epf_header; + enum pci_barno bar_num; + u32 epf_flags; + u32 msi_count; + u32 mru; +}; + +#define MHI_EP_CHANNEL_CONFIG(ch_num, ch_name, direction) \ + { \ + .num = ch_num, \ + .name = ch_name, \ + .dir = direction, \ + } + +#define MHI_EP_CHANNEL_CONFIG_UL(ch_num, ch_name) \ + MHI_EP_CHANNEL_CONFIG(ch_num, ch_name, DMA_TO_DEVICE) + +#define MHI_EP_CHANNEL_CONFIG_DL(ch_num, ch_name) \ + MHI_EP_CHANNEL_CONFIG(ch_num, ch_name, DMA_FROM_DEVICE) + +static const struct mhi_ep_channel_config mhi_v1_channels[] = { + MHI_EP_CHANNEL_CONFIG_UL(0, "LOOPBACK"), + MHI_EP_CHANNEL_CONFIG_DL(1, "LOOPBACK"), + MHI_EP_CHANNEL_CONFIG_UL(2, "SAHARA"), + MHI_EP_CHANNEL_CONFIG_DL(3, "SAHARA"), + MHI_EP_CHANNEL_CONFIG_UL(4, "DIAG"), + MHI_EP_CHANNEL_CONFIG_DL(5, "DIAG"), + MHI_EP_CHANNEL_CONFIG_UL(6, "SSR"), + MHI_EP_CHANNEL_CONFIG_DL(7, "SSR"), + MHI_EP_CHANNEL_CONFIG_UL(8, "QDSS"), + MHI_EP_CHANNEL_CONFIG_DL(9, "QDSS"), + MHI_EP_CHANNEL_CONFIG_UL(10, "EFS"), + MHI_EP_CHANNEL_CONFIG_DL(11, "EFS"), + MHI_EP_CHANNEL_CONFIG_UL(12, "MBIM"), + MHI_EP_CHANNEL_CONFIG_DL(13, "MBIM"), + MHI_EP_CHANNEL_CONFIG_UL(14, "QMI"), + MHI_EP_CHANNEL_CONFIG_DL(15, "QMI"), + MHI_EP_CHANNEL_CONFIG_UL(16, "QMI"), + MHI_EP_CHANNEL_CONFIG_DL(17, "QMI"), + MHI_EP_CHANNEL_CONFIG_UL(18, "IP-CTRL-1"), + MHI_EP_CHANNEL_CONFIG_DL(19, "IP-CTRL-1"), + MHI_EP_CHANNEL_CONFIG_UL(20, "IPCR"), + MHI_EP_CHANNEL_CONFIG_DL(21, "IPCR"), + MHI_EP_CHANNEL_CONFIG_UL(32, "DUN"), + MHI_EP_CHANNEL_CONFIG_DL(33, "DUN"), + MHI_EP_CHANNEL_CONFIG_UL(46, "IP_SW0"), + MHI_EP_CHANNEL_CONFIG_DL(47, "IP_SW0"), +}; + +static const struct mhi_ep_cntrl_config mhi_v1_config = { + .max_channels = 128, + .num_channels = ARRAY_SIZE(mhi_v1_channels), + .ch_cfg = mhi_v1_channels, + .mhi_version = MHI_VERSION_1_0, +}; + +static struct pci_epf_header sdx55_header = { + .vendorid = PCI_VENDOR_ID_QCOM, + .deviceid = 0x0306, + .baseclass_code = PCI_BASE_CLASS_COMMUNICATION, + .subclass_code = PCI_CLASS_COMMUNICATION_MODEM & 0xff, + .interrupt_pin = PCI_INTERRUPT_INTA, +}; + +static const struct pci_epf_mhi_ep_info sdx55_info = { + .config = &mhi_v1_config, + .epf_header = &sdx55_header, + .bar_num = BAR_0, + .epf_flags = PCI_BASE_ADDRESS_MEM_TYPE_32, + .msi_count = 32, + .mru = 0x8000, +}; + +struct pci_epf_mhi { + const struct pci_epf_mhi_ep_info *info; + struct mhi_ep_cntrl mhi_cntrl; + struct pci_epf *epf; + struct mutex lock; + void __iomem *mmio; + resource_size_t mmio_phys; + u32 mmio_size; + int irq; +}; + +static int __pci_epf_mhi_alloc_map(struct mhi_ep_cntrl *mhi_cntrl, u64 pci_addr, + phys_addr_t *paddr, void __iomem **vaddr, + size_t offset, size_t size) +{ + struct pci_epf_mhi *epf_mhi = to_epf_mhi(mhi_cntrl); + struct pci_epf *epf = epf_mhi->epf; + struct pci_epc *epc = epf->epc; + int ret; + + *vaddr = pci_epc_mem_alloc_addr(epc, paddr, size + offset); + if (!*vaddr) + return -ENOMEM; + + ret = pci_epc_map_addr(epc, epf->func_no, epf->vfunc_no, *paddr, + pci_addr - offset, size + offset); + if (ret) { + pci_epc_mem_free_addr(epc, *paddr, *vaddr, size + offset); + return ret; + } + + *paddr = *paddr + offset; + *vaddr = *vaddr + offset; + + return 0; +} + +static int pci_epf_mhi_alloc_map(struct mhi_ep_cntrl *mhi_cntrl, u64 pci_addr, + phys_addr_t *paddr, void __iomem **vaddr, + size_t size) +{ + struct pci_epf_mhi *epf_mhi = to_epf_mhi(mhi_cntrl); + struct pci_epc *epc = epf_mhi->epf->epc; + size_t offset = pci_addr & (epc->mem->window.page_size - 1); + + return __pci_epf_mhi_alloc_map(mhi_cntrl, pci_addr, paddr, vaddr, + offset, size); +} + +static void __pci_epf_mhi_unmap_free(struct mhi_ep_cntrl *mhi_cntrl, + u64 pci_addr, phys_addr_t paddr, + void __iomem *vaddr, size_t offset, + size_t size) +{ + struct pci_epf_mhi *epf_mhi = to_epf_mhi(mhi_cntrl); + struct pci_epf *epf = epf_mhi->epf; + struct pci_epc *epc = epf->epc; + + pci_epc_unmap_addr(epc, epf->func_no, epf->vfunc_no, paddr - offset); + pci_epc_mem_free_addr(epc, paddr - offset, vaddr - offset, + size + offset); +} + +static void pci_epf_mhi_unmap_free(struct mhi_ep_cntrl *mhi_cntrl, u64 pci_addr, + phys_addr_t paddr, void __iomem *vaddr, + size_t size) +{ + struct pci_epf_mhi *epf_mhi = to_epf_mhi(mhi_cntrl); + struct pci_epf *epf = epf_mhi->epf; + struct pci_epc *epc = epf->epc; + size_t offset = pci_addr & (epc->mem->window.page_size - 1); + + __pci_epf_mhi_unmap_free(mhi_cntrl, pci_addr, paddr, vaddr, offset, + size); +} + +static void pci_epf_mhi_raise_irq(struct mhi_ep_cntrl *mhi_cntrl, u32 vector) +{ + struct pci_epf_mhi *epf_mhi = to_epf_mhi(mhi_cntrl); + struct pci_epf *epf = epf_mhi->epf; + struct pci_epc *epc = epf->epc; + + /* + * MHI supplies 0 based MSI vectors but the API expects the vector + * number to start from 1, so we need to increment the vector by 1. + */ + pci_epc_raise_irq(epc, epf->func_no, epf->vfunc_no, PCI_EPC_IRQ_MSI, + vector + 1); +} + +static int pci_epf_mhi_read_from_host(struct mhi_ep_cntrl *mhi_cntrl, u64 from, + void *to, size_t size) +{ + struct pci_epf_mhi *epf_mhi = to_epf_mhi(mhi_cntrl); + size_t offset = from % SZ_4K; + void __iomem *tre_buf; + phys_addr_t tre_phys; + int ret; + + mutex_lock(&epf_mhi->lock); + + ret = __pci_epf_mhi_alloc_map(mhi_cntrl, from, &tre_phys, &tre_buf, + offset, size); + if (ret) { + mutex_unlock(&epf_mhi->lock); + return ret; + } + + memcpy_fromio(to, tre_buf, size); + + __pci_epf_mhi_unmap_free(mhi_cntrl, from, tre_phys, tre_buf, offset, + size); + + mutex_unlock(&epf_mhi->lock); + + return 0; +} + +static int pci_epf_mhi_write_to_host(struct mhi_ep_cntrl *mhi_cntrl, + void *from, u64 to, size_t size) +{ + struct pci_epf_mhi *epf_mhi = to_epf_mhi(mhi_cntrl); + size_t offset = to % SZ_4K; + void __iomem *tre_buf; + phys_addr_t tre_phys; + int ret; + + mutex_lock(&epf_mhi->lock); + + ret = __pci_epf_mhi_alloc_map(mhi_cntrl, to, &tre_phys, &tre_buf, + offset, size); + if (ret) { + mutex_unlock(&epf_mhi->lock); + return ret; + } + + memcpy_toio(tre_buf, from, size); + + __pci_epf_mhi_unmap_free(mhi_cntrl, to, tre_phys, tre_buf, offset, + size); + + mutex_unlock(&epf_mhi->lock); + + return 0; +} + +static int pci_epf_mhi_core_init(struct pci_epf *epf) +{ + struct pci_epf_mhi *epf_mhi = epf_get_drvdata(epf); + const struct pci_epf_mhi_ep_info *info = epf_mhi->info; + struct pci_epf_bar *epf_bar = &epf->bar[info->bar_num]; + struct pci_epc *epc = epf->epc; + struct device *dev = &epf->dev; + int ret; + + epf_bar->phys_addr = epf_mhi->mmio_phys; + epf_bar->size = epf_mhi->mmio_size; + epf_bar->barno = info->bar_num; + epf_bar->flags = info->epf_flags; + ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, epf_bar); + if (ret) { + dev_err(dev, "Failed to set BAR: %d\n", ret); + return ret; + } + + ret = pci_epc_set_msi(epc, epf->func_no, epf->vfunc_no, + order_base_2(info->msi_count)); + if (ret) { + dev_err(dev, "Failed to set MSI configuration: %d\n", ret); + return ret; + } + + ret = pci_epc_write_header(epc, epf->func_no, epf->vfunc_no, + epf->header); + if (ret) { + dev_err(dev, "Failed to set Configuration header: %d\n", ret); + return ret; + } + + return 0; +} + +static int pci_epf_mhi_link_up(struct pci_epf *epf) +{ + struct pci_epf_mhi *epf_mhi = epf_get_drvdata(epf); + const struct pci_epf_mhi_ep_info *info = epf_mhi->info; + struct mhi_ep_cntrl *mhi_cntrl = &epf_mhi->mhi_cntrl; + struct pci_epc *epc = epf->epc; + struct device *dev = &epf->dev; + int ret; + + mhi_cntrl->mmio = epf_mhi->mmio; + mhi_cntrl->irq = epf_mhi->irq; + mhi_cntrl->mru = info->mru; + + /* Assign the struct dev of PCI EP as MHI controller device */ + mhi_cntrl->cntrl_dev = epc->dev.parent; + mhi_cntrl->raise_irq = pci_epf_mhi_raise_irq; + mhi_cntrl->alloc_map = pci_epf_mhi_alloc_map; + mhi_cntrl->unmap_free = pci_epf_mhi_unmap_free; + mhi_cntrl->read_from_host = pci_epf_mhi_read_from_host; + mhi_cntrl->write_to_host = pci_epf_mhi_write_to_host; + + /* Register the MHI EP controller */ + ret = mhi_ep_register_controller(mhi_cntrl, info->config); + if (ret) { + dev_err(dev, "Failed to register MHI EP controller: %d\n", ret); + return ret; + } + + return 0; +} + +static int pci_epf_mhi_link_down(struct pci_epf *epf) +{ + struct pci_epf_mhi *epf_mhi = epf_get_drvdata(epf); + struct mhi_ep_cntrl *mhi_cntrl = &epf_mhi->mhi_cntrl; + + if (mhi_cntrl->mhi_dev) { + mhi_ep_power_down(mhi_cntrl); + mhi_ep_unregister_controller(mhi_cntrl); + } + + return 0; +} + +static int pci_epf_mhi_bme(struct pci_epf *epf) +{ + struct pci_epf_mhi *epf_mhi = epf_get_drvdata(epf); + struct mhi_ep_cntrl *mhi_cntrl = &epf_mhi->mhi_cntrl; + struct device *dev = &epf->dev; + int ret; + + /* + * Power up the MHI EP stack if link is up and stack is in power down + * state. + */ + if (!mhi_cntrl->enabled && mhi_cntrl->mhi_dev) { + ret = mhi_ep_power_up(mhi_cntrl); + if (ret) { + dev_err(dev, "Failed to power up MHI EP: %d\n", ret); + mhi_ep_unregister_controller(mhi_cntrl); + } + } + + return 0; +} + +static int pci_epf_mhi_bind(struct pci_epf *epf) +{ + struct pci_epf_mhi *epf_mhi = epf_get_drvdata(epf); + struct pci_epc *epc = epf->epc; + struct platform_device *pdev = to_platform_device(epc->dev.parent); + struct resource *res; + int ret; + + /* Get MMIO base address from Endpoint controller */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mmio"); + epf_mhi->mmio_phys = res->start; + epf_mhi->mmio_size = resource_size(res); + + epf_mhi->mmio = ioremap(epf_mhi->mmio_phys, epf_mhi->mmio_size); + if (!epf_mhi->mmio) + return -ENOMEM; + + ret = platform_get_irq_byname(pdev, "doorbell"); + if (ret < 0) { + iounmap(epf_mhi->mmio); + return ret; + } + + epf_mhi->irq = ret; + + return 0; +} + +static void pci_epf_mhi_unbind(struct pci_epf *epf) +{ + struct pci_epf_mhi *epf_mhi = epf_get_drvdata(epf); + const struct pci_epf_mhi_ep_info *info = epf_mhi->info; + struct pci_epf_bar *epf_bar = &epf->bar[info->bar_num]; + struct mhi_ep_cntrl *mhi_cntrl = &epf_mhi->mhi_cntrl; + struct pci_epc *epc = epf->epc; + + /* + * Forcefully power down the MHI EP stack. Only way to bring the MHI EP + * stack back to working state after successive bind is by getting BME + * from host. + */ + if (mhi_cntrl->mhi_dev) { + mhi_ep_power_down(mhi_cntrl); + mhi_ep_unregister_controller(mhi_cntrl); + } + + iounmap(epf_mhi->mmio); + pci_epc_clear_bar(epc, epf->func_no, epf->vfunc_no, epf_bar); +} + +static struct pci_epc_event_ops pci_epf_mhi_event_ops = { + .core_init = pci_epf_mhi_core_init, + .link_up = pci_epf_mhi_link_up, + .link_down = pci_epf_mhi_link_down, + .bme = pci_epf_mhi_bme, +}; + +static int pci_epf_mhi_probe(struct pci_epf *epf, + const struct pci_epf_device_id *id) +{ + struct pci_epf_mhi_ep_info *info = + (struct pci_epf_mhi_ep_info *)id->driver_data; + struct pci_epf_mhi *epf_mhi; + struct device *dev = &epf->dev; + + epf_mhi = devm_kzalloc(dev, sizeof(*epf_mhi), GFP_KERNEL); + if (!epf_mhi) + return -ENOMEM; + + epf->header = info->epf_header; + epf_mhi->info = info; + epf_mhi->epf = epf; + + epf->event_ops = &pci_epf_mhi_event_ops; + + mutex_init(&epf_mhi->lock); + + epf_set_drvdata(epf, epf_mhi); + + return 0; +} + +static const struct pci_epf_device_id pci_epf_mhi_ids[] = { + { + .name = "sdx55", .driver_data = (kernel_ulong_t)&sdx55_info, + }, + {}, +}; + +static struct pci_epf_ops pci_epf_mhi_ops = { + .unbind = pci_epf_mhi_unbind, + .bind = pci_epf_mhi_bind, +}; + +static struct pci_epf_driver pci_epf_mhi_driver = { + .driver.name = "pci_epf_mhi", + .probe = pci_epf_mhi_probe, + .id_table = pci_epf_mhi_ids, + .ops = &pci_epf_mhi_ops, + .owner = THIS_MODULE, +}; + +static int __init pci_epf_mhi_init(void) +{ + return pci_epf_register_driver(&pci_epf_mhi_driver); +} +module_init(pci_epf_mhi_init); + +static void __exit pci_epf_mhi_exit(void) +{ + pci_epf_unregister_driver(&pci_epf_mhi_driver); +} +module_exit(pci_epf_mhi_exit); + +MODULE_DESCRIPTION("PCI EPF driver for MHI Endpoint devices"); +MODULE_AUTHOR("Manivannan Sadhasivam "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 39cce0875121ce31b90467811de807c46aed9f0f Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Fri, 2 Jun 2023 17:17:56 +0530 Subject: MAINTAINERS: Add PCI MHI endpoint function driver under MHI bus Add PCI endpoint driver for MHI bus under the MHI bus entry in MAINTAINERS file. Link: https://lore.kernel.org/r/20230602114756.36586-10-manivannan.sadhasivam@linaro.org Signed-off-by: Manivannan Sadhasivam Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Reviewed-by: Damien Le Moal --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 7e0b87d5aa2e..9c14c3dbdb20 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13629,6 +13629,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/mani/mhi.git F: Documentation/ABI/stable/sysfs-bus-mhi F: Documentation/mhi/ F: drivers/bus/mhi/ +F: drivers/pci/endpoint/functions/pci-epf-mhi.c F: include/linux/mhi.h MICROBLAZE ARCHITECTURE -- cgit v1.2.3 From 4ab91207fe37cb0b17d73aa7b042218a83908971 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Wed, 14 Dec 2022 12:22:54 -0500 Subject: PCI: endpoint: pci-epf-vntb: Fix typo in comments Replace "Span" with "Spad". Link: https://lore.kernel.org/r/20221214172254.668282-1-Frank.Li@nxp.com Signed-off-by: Frank Li Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Reviewed-by: Manivannan Sadhasivam --- drivers/pci/endpoint/functions/pci-epf-vntb.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c index 6b433cfbe471..0f5c8f8be847 100644 --- a/drivers/pci/endpoint/functions/pci-epf-vntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c @@ -84,15 +84,15 @@ enum epf_ntb_bar { * | | * | | * | | - * +-----------------------+--------------------------+ Base+span_offset + * +-----------------------+--------------------------+ Base+spad_offset * | | | - * | Peer Span Space | Span Space | + * | Peer Spad Space | Spad Space | * | | | * | | | - * +-----------------------+--------------------------+ Base+span_offset - * | | | +span_count * 4 + * +-----------------------+--------------------------+ Base+spad_offset + * | | | +spad_count * 4 * | | | - * | Span Space | Peer Span Space | + * | Spad Space | Peer Spad Space | * | | | * +-----------------------+--------------------------+ * Virtual PCI PCIe Endpoint -- cgit v1.2.3 From 061cbfab09fb35898f2907d42f936cf9ae271d93 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Mon, 15 May 2023 11:10:49 -0400 Subject: PCI: layerscape: Add the endpoint linkup notifier support Layerscape has PME interrupt, which can be used as linkup notifier. Set CFG_READY bit of PEX_PF0_CONFIG to enable accesses from root complex when linkup detected. Link: https://lore.kernel.org/r/20230515151049.2797105-1-Frank.Li@nxp.com Signed-off-by: Xiaowei Bao Signed-off-by: Frank Li Signed-off-by: Lorenzo Pieralisi Signed-off-by: Bjorn Helgaas Acked-by: Manivannan Sadhasivam --- drivers/pci/controller/dwc/pci-layerscape-ep.c | 100 ++++++++++++++++++++++++- 1 file changed, 99 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/dwc/pci-layerscape-ep.c b/drivers/pci/controller/dwc/pci-layerscape-ep.c index c640db60edc6..de4c1758a6c3 100644 --- a/drivers/pci/controller/dwc/pci-layerscape-ep.c +++ b/drivers/pci/controller/dwc/pci-layerscape-ep.c @@ -18,6 +18,20 @@ #include "pcie-designware.h" +#define PEX_PF0_CONFIG 0xC0014 +#define PEX_PF0_CFG_READY BIT(0) + +/* PEX PFa PCIE PME and message interrupt registers*/ +#define PEX_PF0_PME_MES_DR 0xC0020 +#define PEX_PF0_PME_MES_DR_LUD BIT(7) +#define PEX_PF0_PME_MES_DR_LDD BIT(9) +#define PEX_PF0_PME_MES_DR_HRD BIT(10) + +#define PEX_PF0_PME_MES_IER 0xC0028 +#define PEX_PF0_PME_MES_IER_LUDIE BIT(7) +#define PEX_PF0_PME_MES_IER_LDDIE BIT(9) +#define PEX_PF0_PME_MES_IER_HRDIE BIT(10) + #define to_ls_pcie_ep(x) dev_get_drvdata((x)->dev) struct ls_pcie_ep_drvdata { @@ -30,8 +44,84 @@ struct ls_pcie_ep { struct dw_pcie *pci; struct pci_epc_features *ls_epc; const struct ls_pcie_ep_drvdata *drvdata; + int irq; + bool big_endian; }; +static u32 ls_lut_readl(struct ls_pcie_ep *pcie, u32 offset) +{ + struct dw_pcie *pci = pcie->pci; + + if (pcie->big_endian) + return ioread32be(pci->dbi_base + offset); + else + return ioread32(pci->dbi_base + offset); +} + +static void ls_lut_writel(struct ls_pcie_ep *pcie, u32 offset, u32 value) +{ + struct dw_pcie *pci = pcie->pci; + + if (pcie->big_endian) + iowrite32be(value, pci->dbi_base + offset); + else + iowrite32(value, pci->dbi_base + offset); +} + +static irqreturn_t ls_pcie_ep_event_handler(int irq, void *dev_id) +{ + struct ls_pcie_ep *pcie = dev_id; + struct dw_pcie *pci = pcie->pci; + u32 val, cfg; + + val = ls_lut_readl(pcie, PEX_PF0_PME_MES_DR); + ls_lut_writel(pcie, PEX_PF0_PME_MES_DR, val); + + if (!val) + return IRQ_NONE; + + if (val & PEX_PF0_PME_MES_DR_LUD) { + cfg = ls_lut_readl(pcie, PEX_PF0_CONFIG); + cfg |= PEX_PF0_CFG_READY; + ls_lut_writel(pcie, PEX_PF0_CONFIG, cfg); + dw_pcie_ep_linkup(&pci->ep); + + dev_dbg(pci->dev, "Link up\n"); + } else if (val & PEX_PF0_PME_MES_DR_LDD) { + dev_dbg(pci->dev, "Link down\n"); + } else if (val & PEX_PF0_PME_MES_DR_HRD) { + dev_dbg(pci->dev, "Hot reset\n"); + } + + return IRQ_HANDLED; +} + +static int ls_pcie_ep_interrupt_init(struct ls_pcie_ep *pcie, + struct platform_device *pdev) +{ + u32 val; + int ret; + + pcie->irq = platform_get_irq_byname(pdev, "pme"); + if (pcie->irq < 0) + return pcie->irq; + + ret = devm_request_irq(&pdev->dev, pcie->irq, ls_pcie_ep_event_handler, + IRQF_SHARED, pdev->name, pcie); + if (ret) { + dev_err(&pdev->dev, "Can't register PCIe IRQ\n"); + return ret; + } + + /* Enable interrupts */ + val = ls_lut_readl(pcie, PEX_PF0_PME_MES_IER); + val |= PEX_PF0_PME_MES_IER_LDDIE | PEX_PF0_PME_MES_IER_HRDIE | + PEX_PF0_PME_MES_IER_LUDIE; + ls_lut_writel(pcie, PEX_PF0_PME_MES_IER, val); + + return 0; +} + static const struct pci_epc_features* ls_pcie_ep_get_features(struct dw_pcie_ep *ep) { @@ -125,6 +215,7 @@ static int __init ls_pcie_ep_probe(struct platform_device *pdev) struct ls_pcie_ep *pcie; struct pci_epc_features *ls_epc; struct resource *dbi_base; + int ret; pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); if (!pcie) @@ -144,6 +235,7 @@ static int __init ls_pcie_ep_probe(struct platform_device *pdev) pci->ops = pcie->drvdata->dw_pcie_ops; ls_epc->bar_fixed_64bit = (1 << BAR_2) | (1 << BAR_4); + ls_epc->linkup_notifier = true; pcie->pci = pci; pcie->ls_epc = ls_epc; @@ -155,9 +247,15 @@ static int __init ls_pcie_ep_probe(struct platform_device *pdev) pci->ep.ops = &ls_pcie_ep_ops; + pcie->big_endian = of_property_read_bool(dev->of_node, "big-endian"); + platform_set_drvdata(pdev, pcie); - return dw_pcie_ep_init(&pci->ep); + ret = dw_pcie_ep_init(&pci->ep); + if (ret) + return ret; + + return ls_pcie_ep_interrupt_init(pcie, pdev); } static struct platform_driver ls_pcie_ep_driver = { -- cgit v1.2.3 From e28e75e9f589324a76bf31e2b2bbdc264549f86b Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 7 Jun 2023 22:47:50 +0200 Subject: PCI: rcar: Use correct product family name for Renesas R-Car MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Renesas uses "R-Car" as the name for their product family and development platform. Thus, correct other variants such as "rcar", "RCar", "Rcar", etc., to the preferred spelling. [kwilczynski: commit log] Link: https://lore.kernel.org/linux-pci/20230607204750.27837-1-wsa+renesas@sang-engineering.com Signed-off-by: Wolfram Sang Signed-off-by: Krzysztof Wilczyński Reviewed-by: Yoshihiro Shimoda Reviewed-by: Geert Uytterhoeven --- drivers/pci/controller/pcie-rcar-host.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pci/controller/pcie-rcar-host.c b/drivers/pci/controller/pcie-rcar-host.c index 7ffcd0f5aa45..88975e40ee2f 100644 --- a/drivers/pci/controller/pcie-rcar-host.c +++ b/drivers/pci/controller/pcie-rcar-host.c @@ -669,7 +669,7 @@ static void rcar_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) } static struct irq_chip rcar_msi_bottom_chip = { - .name = "Rcar MSI", + .name = "R-Car MSI", .irq_ack = rcar_msi_irq_ack, .irq_mask = rcar_msi_irq_mask, .irq_unmask = rcar_msi_irq_unmask, @@ -798,7 +798,7 @@ static int rcar_pcie_enable_msi(struct rcar_pcie_host *host) /* * Setup MSI data target using RC base address address, which - * is guaranteed to be in the low 32bit range on any RCar HW. + * is guaranteed to be in the low 32bit range on any R-Car HW. */ rcar_pci_write_reg(pcie, lower_32_bits(res.start) | MSIFE, PCIEMSIALR); rcar_pci_write_reg(pcie, upper_32_bits(res.start), PCIEMSIAUR); -- cgit v1.2.3 From b11c76db97e76f2160c0f7d523e788c0327521b7 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 21 Mar 2023 20:31:54 +0100 Subject: PCI: aardvark: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Link: https://lore.kernel.org/linux-pci/20230321193208.366561-2-u.kleine-koenig@pengutronix.de Signed-off-by: Uwe Kleine-König Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/pci-aardvark.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c index 513d8edf3a5c..71ecd7ddcc8a 100644 --- a/drivers/pci/controller/pci-aardvark.c +++ b/drivers/pci/controller/pci-aardvark.c @@ -1927,7 +1927,7 @@ static int advk_pcie_probe(struct platform_device *pdev) return 0; } -static int advk_pcie_remove(struct platform_device *pdev) +static void advk_pcie_remove(struct platform_device *pdev) { struct advk_pcie *pcie = platform_get_drvdata(pdev); struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie); @@ -1989,8 +1989,6 @@ static int advk_pcie_remove(struct platform_device *pdev) /* Disable phy */ advk_pcie_disable_phy(pcie); - - return 0; } static const struct of_device_id advk_pcie_of_match_table[] = { @@ -2005,7 +2003,7 @@ static struct platform_driver advk_pcie_driver = { .of_match_table = advk_pcie_of_match_table, }, .probe = advk_pcie_probe, - .remove = advk_pcie_remove, + .remove_new = advk_pcie_remove, }; module_platform_driver(advk_pcie_driver); -- cgit v1.2.3 From 3a610560aa4fc8f2afe606d3314aeb20c167ff26 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 21 Mar 2023 20:31:55 +0100 Subject: PCI: altera: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Link: https://lore.kernel.org/linux-pci/20230321193208.366561-3-u.kleine-koenig@pengutronix.de Signed-off-by: Uwe Kleine-König Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/pcie-altera.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/pci/controller/pcie-altera.c b/drivers/pci/controller/pcie-altera.c index 18b2361d6462..c95a29fff8bf 100644 --- a/drivers/pci/controller/pcie-altera.c +++ b/drivers/pci/controller/pcie-altera.c @@ -806,7 +806,7 @@ static int altera_pcie_probe(struct platform_device *pdev) return pci_host_probe(bridge); } -static int altera_pcie_remove(struct platform_device *pdev) +static void altera_pcie_remove(struct platform_device *pdev) { struct altera_pcie *pcie = platform_get_drvdata(pdev); struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie); @@ -814,13 +814,11 @@ static int altera_pcie_remove(struct platform_device *pdev) pci_stop_root_bus(bridge->bus); pci_remove_root_bus(bridge->bus); altera_pcie_irq_teardown(pcie); - - return 0; } static struct platform_driver altera_pcie_driver = { .probe = altera_pcie_probe, - .remove = altera_pcie_remove, + .remove_new = altera_pcie_remove, .driver = { .name = "altera-pcie", .of_match_table = altera_pcie_of_match, -- cgit v1.2.3 From 60d03f70455c7704cb76737ff947b75391934954 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 21 Mar 2023 20:31:56 +0100 Subject: PCI: altera-msi: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Link: https://lore.kernel.org/linux-pci/20230321193208.366561-4-u.kleine-koenig@pengutronix.de Signed-off-by: Uwe Kleine-König Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/pcie-altera-msi.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/pci/controller/pcie-altera-msi.c b/drivers/pci/controller/pcie-altera-msi.c index 65e8a20cc442..6ad5427490b5 100644 --- a/drivers/pci/controller/pcie-altera-msi.c +++ b/drivers/pci/controller/pcie-altera-msi.c @@ -197,7 +197,7 @@ static void altera_free_domains(struct altera_msi *msi) irq_domain_remove(msi->inner_domain); } -static int altera_msi_remove(struct platform_device *pdev) +static void altera_msi_remove(struct platform_device *pdev) { struct altera_msi *msi = platform_get_drvdata(pdev); @@ -207,7 +207,6 @@ static int altera_msi_remove(struct platform_device *pdev) altera_free_domains(msi); platform_set_drvdata(pdev, NULL); - return 0; } static int altera_msi_probe(struct platform_device *pdev) @@ -275,7 +274,7 @@ static struct platform_driver altera_msi_driver = { .of_match_table = altera_msi_of_match, }, .probe = altera_msi_probe, - .remove = altera_msi_remove, + .remove_new = altera_msi_remove, }; static int __init altera_msi_init(void) -- cgit v1.2.3 From b169c576ad0cd341badb866d0808ae32c7bf8c2c Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 21 Mar 2023 20:31:57 +0100 Subject: PCI: brcmstb: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Link: https://lore.kernel.org/linux-pci/20230321193208.366561-5-u.kleine-koenig@pengutronix.de Signed-off-by: Uwe Kleine-König Signed-off-by: Krzysztof Wilczyński Acked-by: Florian Fainelli --- drivers/pci/controller/pcie-brcmstb.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index edf283e2b5dd..f593a422bd63 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -1396,7 +1396,7 @@ static void __brcm_pcie_remove(struct brcm_pcie *pcie) clk_disable_unprepare(pcie->clk); } -static int brcm_pcie_remove(struct platform_device *pdev) +static void brcm_pcie_remove(struct platform_device *pdev) { struct brcm_pcie *pcie = platform_get_drvdata(pdev); struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie); @@ -1404,8 +1404,6 @@ static int brcm_pcie_remove(struct platform_device *pdev) pci_stop_root_bus(bridge->bus); pci_remove_root_bus(bridge->bus); __brcm_pcie_remove(pcie); - - return 0; } static const int pcie_offsets[] = { @@ -1612,7 +1610,7 @@ static const struct dev_pm_ops brcm_pcie_pm_ops = { static struct platform_driver brcm_pcie_driver = { .probe = brcm_pcie_probe, - .remove = brcm_pcie_remove, + .remove_new = brcm_pcie_remove, .driver = { .name = "brcm-pcie", .of_match_table = brcm_pcie_match, -- cgit v1.2.3 From c86f4bd6008e7e9bd561e1f1ec3889cc0a5925e8 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 21 Mar 2023 20:31:58 +0100 Subject: PCI: j721e: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. [kwilczynski: commit log] Link: https://lore.kernel.org/linux-pci/20230321193208.366561-6-u.kleine-koenig@pengutronix.de Signed-off-by: Uwe Kleine-König Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/cadence/pci-j721e.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/pci/controller/cadence/pci-j721e.c b/drivers/pci/controller/cadence/pci-j721e.c index cc83a8925ce0..e70213c9060a 100644 --- a/drivers/pci/controller/cadence/pci-j721e.c +++ b/drivers/pci/controller/cadence/pci-j721e.c @@ -542,7 +542,7 @@ err_get_sync: return ret; } -static int j721e_pcie_remove(struct platform_device *pdev) +static void j721e_pcie_remove(struct platform_device *pdev) { struct j721e_pcie *pcie = platform_get_drvdata(pdev); struct cdns_pcie *cdns_pcie = pcie->cdns_pcie; @@ -552,13 +552,11 @@ static int j721e_pcie_remove(struct platform_device *pdev) cdns_pcie_disable_phy(cdns_pcie); pm_runtime_put(dev); pm_runtime_disable(dev); - - return 0; } static struct platform_driver j721e_pcie_driver = { .probe = j721e_pcie_probe, - .remove = j721e_pcie_remove, + .remove_new = j721e_pcie_remove, .driver = { .name = "j721e-pcie", .of_match_table = of_j721e_pcie_match, -- cgit v1.2.3 From 221879c986980c04923cd6a202a1aafb51e0bc7d Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 21 Mar 2023 20:31:59 +0100 Subject: PCI: dwc: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert the dwc drivers from always returning zero in the remove callback to the void returning variant. [kwilczynski: commit log] Link: https://lore.kernel.org/linux-pci/20230321193208.366561-7-u.kleine-koenig@pengutronix.de Signed-off-by: Uwe Kleine-König Signed-off-by: Krzysztof Wilczyński Acked-by: Serge Semin --- drivers/pci/controller/dwc/pcie-bt1.c | 6 ++---- drivers/pci/controller/dwc/pcie-histb.c | 6 ++---- drivers/pci/controller/dwc/pcie-intel-gw.c | 6 ++---- drivers/pci/controller/dwc/pcie-qcom-ep.c | 8 +++----- drivers/pci/controller/dwc/pcie-tegra194.c | 8 +++----- 5 files changed, 12 insertions(+), 22 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-bt1.c b/drivers/pci/controller/dwc/pcie-bt1.c index 95a723a6fd46..17e696797ff5 100644 --- a/drivers/pci/controller/dwc/pcie-bt1.c +++ b/drivers/pci/controller/dwc/pcie-bt1.c @@ -617,13 +617,11 @@ static int bt1_pcie_probe(struct platform_device *pdev) return bt1_pcie_add_port(btpci); } -static int bt1_pcie_remove(struct platform_device *pdev) +static void bt1_pcie_remove(struct platform_device *pdev) { struct bt1_pcie *btpci = platform_get_drvdata(pdev); bt1_pcie_del_port(btpci); - - return 0; } static const struct of_device_id bt1_pcie_of_match[] = { @@ -634,7 +632,7 @@ MODULE_DEVICE_TABLE(of, bt1_pcie_of_match); static struct platform_driver bt1_pcie_driver = { .probe = bt1_pcie_probe, - .remove = bt1_pcie_remove, + .remove_new = bt1_pcie_remove, .driver = { .name = "bt1-pcie", .of_match_table = bt1_pcie_of_match, diff --git a/drivers/pci/controller/dwc/pcie-histb.c b/drivers/pci/controller/dwc/pcie-histb.c index 927ae05dc920..fd484cc7c481 100644 --- a/drivers/pci/controller/dwc/pcie-histb.c +++ b/drivers/pci/controller/dwc/pcie-histb.c @@ -421,7 +421,7 @@ static int histb_pcie_probe(struct platform_device *pdev) return 0; } -static int histb_pcie_remove(struct platform_device *pdev) +static void histb_pcie_remove(struct platform_device *pdev) { struct histb_pcie *hipcie = platform_get_drvdata(pdev); @@ -429,8 +429,6 @@ static int histb_pcie_remove(struct platform_device *pdev) if (hipcie->phy) phy_exit(hipcie->phy); - - return 0; } static const struct of_device_id histb_pcie_of_match[] = { @@ -441,7 +439,7 @@ MODULE_DEVICE_TABLE(of, histb_pcie_of_match); static struct platform_driver histb_pcie_platform_driver = { .probe = histb_pcie_probe, - .remove = histb_pcie_remove, + .remove_new = histb_pcie_remove, .driver = { .name = "histb-pcie", .of_match_table = histb_pcie_of_match, diff --git a/drivers/pci/controller/dwc/pcie-intel-gw.c b/drivers/pci/controller/dwc/pcie-intel-gw.c index 333c33d98a70..9c7caed9e706 100644 --- a/drivers/pci/controller/dwc/pcie-intel-gw.c +++ b/drivers/pci/controller/dwc/pcie-intel-gw.c @@ -340,15 +340,13 @@ static void __intel_pcie_remove(struct intel_pcie *pcie) phy_exit(pcie->phy); } -static int intel_pcie_remove(struct platform_device *pdev) +static void intel_pcie_remove(struct platform_device *pdev) { struct intel_pcie *pcie = platform_get_drvdata(pdev); struct dw_pcie_rp *pp = &pcie->pci.pp; dw_pcie_host_deinit(pp); __intel_pcie_remove(pcie); - - return 0; } static int intel_pcie_suspend_noirq(struct device *dev) @@ -443,7 +441,7 @@ static const struct of_device_id of_intel_pcie_match[] = { static struct platform_driver intel_pcie_driver = { .probe = intel_pcie_probe, - .remove = intel_pcie_remove, + .remove_new = intel_pcie_remove, .driver = { .name = "intel-gw-pcie", .of_match_table = of_intel_pcie_match, diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c index 19b32839ea26..3e5f1b637aeb 100644 --- a/drivers/pci/controller/dwc/pcie-qcom-ep.c +++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c @@ -784,7 +784,7 @@ err_disable_resources: return ret; } -static int qcom_pcie_ep_remove(struct platform_device *pdev) +static void qcom_pcie_ep_remove(struct platform_device *pdev) { struct qcom_pcie_ep *pcie_ep = platform_get_drvdata(pdev); @@ -794,11 +794,9 @@ static int qcom_pcie_ep_remove(struct platform_device *pdev) debugfs_remove_recursive(pcie_ep->debugfs); if (pcie_ep->link_status == QCOM_PCIE_EP_LINK_DISABLED) - return 0; + return; qcom_pcie_disable_resources(pcie_ep); - - return 0; } static const struct of_device_id qcom_pcie_ep_match[] = { @@ -810,7 +808,7 @@ MODULE_DEVICE_TABLE(of, qcom_pcie_ep_match); static struct platform_driver qcom_pcie_ep_driver = { .probe = qcom_pcie_ep_probe, - .remove = qcom_pcie_ep_remove, + .remove_new = qcom_pcie_ep_remove, .driver = { .name = "qcom-pcie-ep", .of_match_table = qcom_pcie_ep_match, diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index 09825b4a075e..f373a00e2ea3 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -2268,13 +2268,13 @@ fail: return ret; } -static int tegra_pcie_dw_remove(struct platform_device *pdev) +static void tegra_pcie_dw_remove(struct platform_device *pdev) { struct tegra_pcie_dw *pcie = platform_get_drvdata(pdev); if (pcie->of_data->mode == DW_PCIE_RC_TYPE) { if (!pcie->link_state) - return 0; + return; debugfs_remove_recursive(pcie->debugfs); tegra_pcie_deinit_controller(pcie); @@ -2288,8 +2288,6 @@ static int tegra_pcie_dw_remove(struct platform_device *pdev) tegra_bpmp_put(pcie->bpmp); if (pcie->pex_refclk_sel_gpiod) gpiod_set_value(pcie->pex_refclk_sel_gpiod, 0); - - return 0; } static int tegra_pcie_dw_suspend_late(struct device *dev) @@ -2483,7 +2481,7 @@ static const struct dev_pm_ops tegra_pcie_dw_pm_ops = { static struct platform_driver tegra_pcie_dw_driver = { .probe = tegra_pcie_dw_probe, - .remove = tegra_pcie_dw_remove, + .remove_new = tegra_pcie_dw_remove, .shutdown = tegra_pcie_dw_shutdown, .driver = { .name = "tegra194-pcie", -- cgit v1.2.3 From 9a285fbbb591428de0cde7f553130e7c728a2e19 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 21 Mar 2023 20:32:00 +0100 Subject: PCI: hisi-error: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Link: https://lore.kernel.org/linux-pci/20230321193208.366561-8-u.kleine-koenig@pengutronix.de Signed-off-by: Uwe Kleine-König Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/pcie-hisi-error.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/pci/controller/pcie-hisi-error.c b/drivers/pci/controller/pcie-hisi-error.c index 7d88eb696b06..ad9d5ffcd9e3 100644 --- a/drivers/pci/controller/pcie-hisi-error.c +++ b/drivers/pci/controller/pcie-hisi-error.c @@ -299,13 +299,11 @@ static int hisi_pcie_error_handler_probe(struct platform_device *pdev) return 0; } -static int hisi_pcie_error_handler_remove(struct platform_device *pdev) +static void hisi_pcie_error_handler_remove(struct platform_device *pdev) { struct hisi_pcie_error_private *priv = platform_get_drvdata(pdev); ghes_unregister_vendor_record_notifier(&priv->nb); - - return 0; } static const struct acpi_device_id hisi_pcie_acpi_match[] = { @@ -319,7 +317,7 @@ static struct platform_driver hisi_pcie_error_handler_driver = { .acpi_match_table = hisi_pcie_acpi_match, }, .probe = hisi_pcie_error_handler_probe, - .remove = hisi_pcie_error_handler_remove, + .remove_new = hisi_pcie_error_handler_remove, }; module_platform_driver(hisi_pcie_error_handler_driver); -- cgit v1.2.3 From 6f1c0a046048ec647299c5f9a135002393941c99 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 21 Mar 2023 20:32:01 +0100 Subject: PCI: iproc: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. The iproc driver always returns 0, it's just a bit hidden. So make iproc_pcie_remove() return void instead of always zero and convert the platform driver to the alternative remove callback that returns void and eventually replaces the int returning callback. Link: https://lore.kernel.org/linux-pci/20230321193208.366561-9-u.kleine-koenig@pengutronix.de Signed-off-by: Uwe Kleine-König Signed-off-by: Krzysztof Wilczyński Acked-by: Florian Fainelli --- drivers/pci/controller/pcie-iproc-platform.c | 6 +++--- drivers/pci/controller/pcie-iproc.c | 4 +--- drivers/pci/controller/pcie-iproc.h | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/pci/controller/pcie-iproc-platform.c b/drivers/pci/controller/pcie-iproc-platform.c index 4142a73e611d..acdc583d2980 100644 --- a/drivers/pci/controller/pcie-iproc-platform.c +++ b/drivers/pci/controller/pcie-iproc-platform.c @@ -114,11 +114,11 @@ static int iproc_pltfm_pcie_probe(struct platform_device *pdev) return 0; } -static int iproc_pltfm_pcie_remove(struct platform_device *pdev) +static void iproc_pltfm_pcie_remove(struct platform_device *pdev) { struct iproc_pcie *pcie = platform_get_drvdata(pdev); - return iproc_pcie_remove(pcie); + iproc_pcie_remove(pcie); } static void iproc_pltfm_pcie_shutdown(struct platform_device *pdev) @@ -134,7 +134,7 @@ static struct platform_driver iproc_pltfm_pcie_driver = { .of_match_table = of_match_ptr(iproc_pcie_of_match_table), }, .probe = iproc_pltfm_pcie_probe, - .remove = iproc_pltfm_pcie_remove, + .remove_new = iproc_pltfm_pcie_remove, .shutdown = iproc_pltfm_pcie_shutdown, }; module_platform_driver(iproc_pltfm_pcie_driver); diff --git a/drivers/pci/controller/pcie-iproc.c b/drivers/pci/controller/pcie-iproc.c index 83029bdfd884..bd1c98b68851 100644 --- a/drivers/pci/controller/pcie-iproc.c +++ b/drivers/pci/controller/pcie-iproc.c @@ -1537,7 +1537,7 @@ err_exit_phy: } EXPORT_SYMBOL(iproc_pcie_setup); -int iproc_pcie_remove(struct iproc_pcie *pcie) +void iproc_pcie_remove(struct iproc_pcie *pcie) { struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); @@ -1548,8 +1548,6 @@ int iproc_pcie_remove(struct iproc_pcie *pcie) phy_power_off(pcie->phy); phy_exit(pcie->phy); - - return 0; } EXPORT_SYMBOL(iproc_pcie_remove); diff --git a/drivers/pci/controller/pcie-iproc.h b/drivers/pci/controller/pcie-iproc.h index dcca315897c8..969ded03b8c2 100644 --- a/drivers/pci/controller/pcie-iproc.h +++ b/drivers/pci/controller/pcie-iproc.h @@ -111,7 +111,7 @@ struct iproc_pcie { }; int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res); -int iproc_pcie_remove(struct iproc_pcie *pcie); +void iproc_pcie_remove(struct iproc_pcie *pcie); int iproc_pcie_shutdown(struct iproc_pcie *pcie); #ifdef CONFIG_PCIE_IPROC_MSI -- cgit v1.2.3 From 5e0005409427ec7ca988c6315ebf746f8fb24139 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 21 Mar 2023 20:32:02 +0100 Subject: PCI: mediatek: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Link: https://lore.kernel.org/linux-pci/20230321193208.366561-10-u.kleine-koenig@pengutronix.de Signed-off-by: Uwe Kleine-König Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/pcie-mediatek.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c index 31de7a29192c..66a8f73296fc 100644 --- a/drivers/pci/controller/pcie-mediatek.c +++ b/drivers/pci/controller/pcie-mediatek.c @@ -1134,7 +1134,7 @@ static void mtk_pcie_free_resources(struct mtk_pcie *pcie) pci_free_resource_list(windows); } -static int mtk_pcie_remove(struct platform_device *pdev) +static void mtk_pcie_remove(struct platform_device *pdev) { struct mtk_pcie *pcie = platform_get_drvdata(pdev); struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); @@ -1146,8 +1146,6 @@ static int mtk_pcie_remove(struct platform_device *pdev) mtk_pcie_irq_teardown(pcie); mtk_pcie_put_resources(pcie); - - return 0; } static int mtk_pcie_suspend_noirq(struct device *dev) @@ -1239,7 +1237,7 @@ MODULE_DEVICE_TABLE(of, mtk_pcie_ids); static struct platform_driver mtk_pcie_driver = { .probe = mtk_pcie_probe, - .remove = mtk_pcie_remove, + .remove_new = mtk_pcie_remove, .driver = { .name = "mtk-pcie", .of_match_table = mtk_pcie_ids, -- cgit v1.2.3 From 22626c46bb32a7953995637a3f062b0f0d74cc80 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 21 Mar 2023 20:32:03 +0100 Subject: PCI: mediatek-gen3: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Link: https://lore.kernel.org/linux-pci/20230321193208.366561-11-u.kleine-koenig@pengutronix.de Signed-off-by: Uwe Kleine-König Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/pcie-mediatek-gen3.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/pci/controller/pcie-mediatek-gen3.c b/drivers/pci/controller/pcie-mediatek-gen3.c index b8612ce5f4d0..e0e27645fdf4 100644 --- a/drivers/pci/controller/pcie-mediatek-gen3.c +++ b/drivers/pci/controller/pcie-mediatek-gen3.c @@ -943,7 +943,7 @@ static int mtk_pcie_probe(struct platform_device *pdev) return 0; } -static int mtk_pcie_remove(struct platform_device *pdev) +static void mtk_pcie_remove(struct platform_device *pdev) { struct mtk_gen3_pcie *pcie = platform_get_drvdata(pdev); struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); @@ -955,8 +955,6 @@ static int mtk_pcie_remove(struct platform_device *pdev) mtk_pcie_irq_teardown(pcie); mtk_pcie_power_down(pcie); - - return 0; } static void mtk_pcie_irq_save(struct mtk_gen3_pcie *pcie) @@ -1069,7 +1067,7 @@ MODULE_DEVICE_TABLE(of, mtk_pcie_of_match); static struct platform_driver mtk_pcie_driver = { .probe = mtk_pcie_probe, - .remove = mtk_pcie_remove, + .remove_new = mtk_pcie_remove, .driver = { .name = "mtk-pcie-gen3", .of_match_table = mtk_pcie_of_match, -- cgit v1.2.3 From 8c47ac2a66c4a8372ecee4a5f0cc7c03c14de353 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 21 Mar 2023 20:32:04 +0100 Subject: PCI: mt7621: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Link: https://lore.kernel.org/linux-pci/20230321193208.366561-12-u.kleine-koenig@pengutronix.de Signed-off-by: Uwe Kleine-König Signed-off-by: Krzysztof Wilczyński Reviewed-by: Sergio Paracuellos --- drivers/pci/controller/pcie-mt7621.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/pci/controller/pcie-mt7621.c b/drivers/pci/controller/pcie-mt7621.c index a445ec314375..79e225edb42a 100644 --- a/drivers/pci/controller/pcie-mt7621.c +++ b/drivers/pci/controller/pcie-mt7621.c @@ -524,15 +524,13 @@ remove_resets: return err; } -static int mt7621_pcie_remove(struct platform_device *pdev) +static void mt7621_pcie_remove(struct platform_device *pdev) { struct mt7621_pcie *pcie = platform_get_drvdata(pdev); struct mt7621_pcie_port *port; list_for_each_entry(port, &pcie->ports, list) reset_control_put(port->pcie_rst); - - return 0; } static const struct of_device_id mt7621_pcie_ids[] = { @@ -543,7 +541,7 @@ MODULE_DEVICE_TABLE(of, mt7621_pcie_ids); static struct platform_driver mt7621_pcie_driver = { .probe = mt7621_pcie_probe, - .remove = mt7621_pcie_remove, + .remove_new = mt7621_pcie_remove, .driver = { .name = "mt7621-pci", .of_match_table = mt7621_pcie_ids, -- cgit v1.2.3 From 4c3bc1b41b8f9f96a75eacff8582ee811aeda83c Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 21 Mar 2023 20:32:05 +0100 Subject: PCI: mvebu: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Link: https://lore.kernel.org/linux-pci/20230321193208.366561-13-u.kleine-koenig@pengutronix.de Signed-off-by: Uwe Kleine-König Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/pci-mvebu.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c index 1dc209f6f53a..c931b1b07b1d 100644 --- a/drivers/pci/controller/pci-mvebu.c +++ b/drivers/pci/controller/pci-mvebu.c @@ -1649,7 +1649,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev) return pci_host_probe(bridge); } -static int mvebu_pcie_remove(struct platform_device *pdev) +static void mvebu_pcie_remove(struct platform_device *pdev) { struct mvebu_pcie *pcie = platform_get_drvdata(pdev); struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie); @@ -1707,8 +1707,6 @@ static int mvebu_pcie_remove(struct platform_device *pdev) /* Power down card and disable clocks. Must be the last step. */ mvebu_pcie_powerdown(port); } - - return 0; } static const struct of_device_id mvebu_pcie_of_match_table[] = { @@ -1730,7 +1728,7 @@ static struct platform_driver mvebu_pcie_driver = { .pm = &mvebu_pcie_pm_ops, }, .probe = mvebu_pcie_probe, - .remove = mvebu_pcie_remove, + .remove_new = mvebu_pcie_remove, }; module_platform_driver(mvebu_pcie_driver); -- cgit v1.2.3 From 2998efcd8e73e5ab0ac82b79a087fc3747cd4d03 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 21 Mar 2023 20:32:06 +0100 Subject: PCI: rockchip-host: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Link: https://lore.kernel.org/linux-pci/20230321193208.366561-14-u.kleine-koenig@pengutronix.de Signed-off-by: Uwe Kleine-König Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/pcie-rockchip-host.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/pci/controller/pcie-rockchip-host.c b/drivers/pci/controller/pcie-rockchip-host.c index c96c0f454570..2438bc9b3a1a 100644 --- a/drivers/pci/controller/pcie-rockchip-host.c +++ b/drivers/pci/controller/pcie-rockchip-host.c @@ -1009,7 +1009,7 @@ err_set_vpcie: return err; } -static int rockchip_pcie_remove(struct platform_device *pdev) +static void rockchip_pcie_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct rockchip_pcie *rockchip = dev_get_drvdata(dev); @@ -1029,8 +1029,6 @@ static int rockchip_pcie_remove(struct platform_device *pdev) regulator_disable(rockchip->vpcie3v3); regulator_disable(rockchip->vpcie1v8); regulator_disable(rockchip->vpcie0v9); - - return 0; } static const struct dev_pm_ops rockchip_pcie_pm_ops = { @@ -1051,7 +1049,7 @@ static struct platform_driver rockchip_pcie_driver = { .pm = &rockchip_pcie_pm_ops, }, .probe = rockchip_pcie_probe, - .remove = rockchip_pcie_remove, + .remove_new = rockchip_pcie_remove, }; module_platform_driver(rockchip_pcie_driver); -- cgit v1.2.3 From c7fd95cda648a0df45b9748bbbef1a62099f3f32 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 21 Mar 2023 20:32:07 +0100 Subject: PCI: tegra: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Link: https://lore.kernel.org/linux-pci/20230321193208.366561-15-u.kleine-koenig@pengutronix.de Signed-off-by: Uwe Kleine-König Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/pci-tegra.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index 79630885b9c8..038d974a318e 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -2680,7 +2680,7 @@ put_resources: return err; } -static int tegra_pcie_remove(struct platform_device *pdev) +static void tegra_pcie_remove(struct platform_device *pdev) { struct tegra_pcie *pcie = platform_get_drvdata(pdev); struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); @@ -2701,8 +2701,6 @@ static int tegra_pcie_remove(struct platform_device *pdev) list_for_each_entry_safe(port, tmp, &pcie->ports, list) tegra_pcie_port_free(port); - - return 0; } static int tegra_pcie_pm_suspend(struct device *dev) @@ -2808,6 +2806,6 @@ static struct platform_driver tegra_pcie_driver = { .pm = &tegra_pcie_pm_ops, }, .probe = tegra_pcie_probe, - .remove = tegra_pcie_remove, + .remove_new = tegra_pcie_remove, }; module_platform_driver(tegra_pcie_driver); -- cgit v1.2.3 From afbb9130d2bf04888e91bddc4127d7121b9aee3d Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 21 Mar 2023 20:32:08 +0100 Subject: PCI: xgene-msi: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Link: https://lore.kernel.org/linux-pci/20230321193208.366561-16-u.kleine-koenig@pengutronix.de Signed-off-by: Uwe Kleine-König Signed-off-by: Krzysztof Wilczyński --- drivers/pci/controller/pci-xgene-msi.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/pci/controller/pci-xgene-msi.c b/drivers/pci/controller/pci-xgene-msi.c index d7987b281f79..0234e528b9a5 100644 --- a/drivers/pci/controller/pci-xgene-msi.c +++ b/drivers/pci/controller/pci-xgene-msi.c @@ -348,7 +348,7 @@ static void xgene_msi_isr(struct irq_desc *desc) static enum cpuhp_state pci_xgene_online; -static int xgene_msi_remove(struct platform_device *pdev) +static void xgene_msi_remove(struct platform_device *pdev) { struct xgene_msi *msi = platform_get_drvdata(pdev); @@ -362,8 +362,6 @@ static int xgene_msi_remove(struct platform_device *pdev) msi->bitmap = NULL; xgene_free_domains(msi); - - return 0; } static int xgene_msi_hwirq_alloc(unsigned int cpu) @@ -521,7 +519,7 @@ static struct platform_driver xgene_msi_driver = { .of_match_table = xgene_msi_match_table, }, .probe = xgene_msi_probe, - .remove = xgene_msi_remove, + .remove_new = xgene_msi_remove, }; static int __init xgene_pcie_msi_init(void) -- cgit v1.2.3 From 0c0206dc4f5ba2d18b15e24d2047487d6f73916b Mon Sep 17 00:00:00 2001 From: Xinghui Li Date: Thu, 20 Apr 2023 17:43:31 +0800 Subject: PCI: vmd: Fix uninitialized variable usage in vmd_enable_domain() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ret variable in the vmd_enable_domain() function was used uninitialized when printing a warning message upon failure of the pci_reset_bus() function. Thus, fix the issue by assigning ret with the value returned from pci_reset_bus() before referencing it in the warning message. This was detected by Smatch: drivers/pci/controller/vmd.c:931 vmd_enable_domain() error: uninitialized symbol 'ret'. [kwilczynski: drop the second patch from the series, add missing reported by tag, commit log] Fixes: 0a584655ef89 ("PCI: vmd: Fix secondary bus reset for Intel bridges") Link: https://lore.kernel.org/all/202305270219.B96IiIfv-lkp@intel.com Link: https://lore.kernel.org/linux-pci/20230420094332.1507900-2-korantwork@gmail.com Reported-by: kernel test robot Reported-by: Dan Carpenter Signed-off-by: Xinghui Li Signed-off-by: Krzysztof Wilczyński Reviewed-by: Nirmal Patel --- drivers/pci/controller/vmd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c index 30ec18283aaf..e718a816d481 100644 --- a/drivers/pci/controller/vmd.c +++ b/drivers/pci/controller/vmd.c @@ -927,7 +927,8 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features) if (!list_empty(&child->devices)) { dev = list_first_entry(&child->devices, struct pci_dev, bus_list); - if (pci_reset_bus(dev)) + ret = pci_reset_bus(dev); + if (ret) pci_warn(dev, "can't reset device: %d\n", ret); break; -- cgit v1.2.3 From b58d6d89ae020b107b2afa945a873dcadab44062 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 8 Feb 2023 23:13:49 -0800 Subject: Documentation: PCI: correct spelling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Correct spelling problems for Documentation/PCI/ as reported by codespell. Link: https://lore.kernel.org/linux-pci/20230209071400.31476-14-rdunlap@infradead.org Signed-off-by: Randy Dunlap Signed-off-by: Krzysztof Wilczyński Acked-by: Bjorn Helgaas --- Documentation/PCI/endpoint/pci-vntb-howto.rst | 2 +- Documentation/PCI/msi-howto.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/PCI/endpoint/pci-vntb-howto.rst b/Documentation/PCI/endpoint/pci-vntb-howto.rst index 4ab8e4a26d4b..94f37c60f2dc 100644 --- a/Documentation/PCI/endpoint/pci-vntb-howto.rst +++ b/Documentation/PCI/endpoint/pci-vntb-howto.rst @@ -103,7 +103,7 @@ A sample configuration for NTB function is given below:: # echo 1 > functions/pci_epf_vntb/func1/pci_epf_vntb.0/num_mws # echo 0x100000 > functions/pci_epf_vntb/func1/pci_epf_vntb.0/mw1 -A sample configuration for virtual NTB driver for virutal PCI bus:: +A sample configuration for virtual NTB driver for virtual PCI bus:: # echo 0x1957 > functions/pci_epf_vntb/func1/pci_epf_vntb.0/vntb_vid # echo 0x080A > functions/pci_epf_vntb/func1/pci_epf_vntb.0/vntb_pid diff --git a/Documentation/PCI/msi-howto.rst b/Documentation/PCI/msi-howto.rst index 8ae461e97c54..c9400f02333b 100644 --- a/Documentation/PCI/msi-howto.rst +++ b/Documentation/PCI/msi-howto.rst @@ -290,7 +290,7 @@ PCI_IRQ_MSI or PCI_IRQ_MSIX flags. List of device drivers MSI(-X) APIs =================================== -The PCI/MSI subystem has a dedicated C file for its exported device driver +The PCI/MSI subsystem has a dedicated C file for its exported device driver APIs — `drivers/pci/msi/api.c`. The following functions are exported: .. kernel-doc:: drivers/pci/msi/api.c -- cgit v1.2.3