From 955268e13f8f9422e7e89ee6350ec793dddd1e94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karel=20Ko=C4=8D=C3=AD?= Date: Tue, 1 Nov 2022 09:44:59 +0100 Subject: nixos: try to fix Turris Omnia PCIe on Linux 6.0 Unfortunatelly this seems to not work. --- ...k-Add-support-for-DLLSC-and-hotplug-inter.patch | 268 +++++++++++++++++++++ 1 file changed, 268 insertions(+) create mode 100644 nixos/modules/omnia-kernel-patches/0050-PCI-aardvark-Add-support-for-DLLSC-and-hotplug-inter.patch (limited to 'nixos/modules/omnia-kernel-patches/0050-PCI-aardvark-Add-support-for-DLLSC-and-hotplug-inter.patch') diff --git a/nixos/modules/omnia-kernel-patches/0050-PCI-aardvark-Add-support-for-DLLSC-and-hotplug-inter.patch b/nixos/modules/omnia-kernel-patches/0050-PCI-aardvark-Add-support-for-DLLSC-and-hotplug-inter.patch new file mode 100644 index 0000000..0ae4669 --- /dev/null +++ b/nixos/modules/omnia-kernel-patches/0050-PCI-aardvark-Add-support-for-DLLSC-and-hotplug-inter.patch @@ -0,0 +1,268 @@ +From acd743b13658e7255b6c5da3be2031b800872190 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= +Date: Wed, 31 Aug 2022 15:55:46 +0200 +Subject: [PATCH 50/53] PCI: aardvark: Add support for DLLSC and hotplug + interrupt +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add support for Data Link Layer State Change in the emulated slot +registers and hotplug interrupt via the emulated root bridge. + +This is mainly useful for when an error causes link down event. With +this change, drivers can try recovery. + +Link down state change can be implemented because Aardvark supports Link +Down event interrupt. Use it for signaling that Data Link Layer Link is +not active anymore via Hot-Plug Interrupt on emulated root bridge. + +Link up interrupt is not available on Aardvark, but we check for whether +link is up in the advk_pcie_link_up() function. By triggering Hot-Plug +Interrupt from this function we achieve Link up event, so long as the +function is called (which it is after probe and when rescanning). +Although it is not ideal, it is better than nothing. + +Since advk_pcie_link_up() is not called from interrupt handler, we +cannot call generic_handle_domain_irq() from it directly. Instead create +a TIMER_IRQSAFE timer and trigger it from advk_pcie_link_up(). + +(We haven't been able to find any documentation for a Link Up interrupt + on Aardvark, but it is possible there is one, in some undocumented + register. If we manage to find this information, this can be + rewritten.) + +Signed-off-by: Pali Rohár +Signed-off-by: Marek Behún +--- + drivers/pci/controller/Kconfig | 3 + + drivers/pci/controller/pci-aardvark.c | 101 ++++++++++++++++++++++++-- + 2 files changed, 99 insertions(+), 5 deletions(-) + +diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig +index 8da2efdc5177..639a68e65363 100644 +--- a/drivers/pci/controller/Kconfig ++++ b/drivers/pci/controller/Kconfig +@@ -24,6 +24,9 @@ config PCI_AARDVARK + depends on OF + depends on PCI_MSI_IRQ_DOMAIN + select PCI_BRIDGE_EMUL ++ select PCIEPORTBUS ++ select HOTPLUG_PCI ++ select HOTPLUG_PCI_PCIE + help + Add support for Aardvark 64bit PCIe Host Controller. This + controller is part of the South Bridge of the Marvel Armada +diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c +index 65cd1095984f..9a7db62982a6 100644 +--- a/drivers/pci/controller/pci-aardvark.c ++++ b/drivers/pci/controller/pci-aardvark.c +@@ -25,6 +25,7 @@ + #include + #include + #include ++#include + + #include "../pci.h" + #include "../pci-bridge-emul.h" +@@ -100,6 +101,7 @@ + #define PCIE_MSG_PM_PME_MASK BIT(7) + #define PCIE_ISR0_MASK_REG (CONTROL_BASE_ADDR + 0x44) + #define PCIE_ISR0_MSI_INT_PENDING BIT(24) ++#define PCIE_ISR0_LINK_DOWN BIT(1) + #define PCIE_ISR0_CORR_ERR BIT(11) + #define PCIE_ISR0_NFAT_ERR BIT(12) + #define PCIE_ISR0_FAT_ERR BIT(13) +@@ -284,6 +286,8 @@ struct advk_pcie { + DECLARE_BITMAP(msi_used, MSI_IRQ_NUM); + struct mutex msi_used_lock; + int link_gen; ++ bool link_was_up; ++ struct timer_list link_irq_timer; + struct pci_bridge_emul bridge; + struct gpio_desc *reset_gpio; + struct phy *phy; +@@ -313,7 +317,24 @@ static inline bool advk_pcie_link_up(struct advk_pcie *pcie) + { + /* check if LTSSM is in normal operation - some L* state */ + u8 ltssm_state = advk_pcie_ltssm_state(pcie); +- return ltssm_state >= LTSSM_L0 && ltssm_state < LTSSM_DISABLED; ++ bool link_is_up; ++ u16 slotsta; ++ ++ link_is_up = ltssm_state >= LTSSM_L0 && ltssm_state < LTSSM_DISABLED; ++ ++ if (link_is_up && !pcie->link_was_up) { ++ dev_info(&pcie->pdev->dev, "link up\n"); ++ ++ pcie->link_was_up = true; ++ ++ slotsta = le16_to_cpu(pcie->bridge.pcie_conf.slotsta); ++ slotsta |= PCI_EXP_SLTSTA_DLLSC; ++ pcie->bridge.pcie_conf.slotsta = cpu_to_le16(slotsta); ++ ++ mod_timer(&pcie->link_irq_timer, jiffies + 1); ++ } ++ ++ return link_is_up; + } + + static inline bool advk_pcie_link_active(struct advk_pcie *pcie) +@@ -442,8 +463,6 @@ static void advk_pcie_train_link(struct advk_pcie *pcie) + ret = advk_pcie_wait_for_link(pcie); + if (ret < 0) + dev_err(dev, "link never came up\n"); +- else +- dev_info(dev, "link up\n"); + } + + /* +@@ -592,6 +611,11 @@ static void advk_pcie_setup_hw(struct advk_pcie *pcie) + reg &= ~PCIE_ISR0_MSI_INT_PENDING; + advk_writel(pcie, reg, PCIE_ISR0_MASK_REG); + ++ /* Unmask Link Down interrupt */ ++ reg = advk_readl(pcie, PCIE_ISR0_MASK_REG); ++ reg &= ~PCIE_ISR0_LINK_DOWN; ++ advk_writel(pcie, reg, PCIE_ISR0_MASK_REG); ++ + /* Unmask PME interrupt for processing of PME requester */ + reg = advk_readl(pcie, PCIE_ISR0_MASK_REG); + reg &= ~PCIE_MSG_PM_PME_MASK; +@@ -918,6 +942,14 @@ advk_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, + advk_pcie_wait_for_retrain(pcie); + break; + ++ case PCI_EXP_SLTCTL: { ++ u16 slotctl = le16_to_cpu(bridge->pcie_conf.slotctl); ++ /* Only emulation of HPIE and DLLSCE bits is provided */ ++ slotctl &= PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_DLLSCE; ++ bridge->pcie_conf.slotctl = cpu_to_le16(slotctl); ++ break; ++ } ++ + case PCI_EXP_RTCTL: { + u16 rootctl = le16_to_cpu(bridge->pcie_conf.rootctl); + /* Only emulation of PMEIE and CRSSVE bits is provided */ +@@ -1035,6 +1067,7 @@ static const struct pci_bridge_emul_ops advk_pci_bridge_emul_ops = { + static int advk_sw_pci_bridge_init(struct advk_pcie *pcie) + { + struct pci_bridge_emul *bridge = &pcie->bridge; ++ u32 slotcap; + + bridge->conf.vendor = + cpu_to_le16(advk_readl(pcie, PCIE_CORE_DEV_ID_REG) & 0xffff); +@@ -1061,6 +1094,13 @@ static int advk_sw_pci_bridge_init(struct advk_pcie *pcie) + bridge->pcie_conf.cap = cpu_to_le16(2 | PCI_EXP_FLAGS_SLOT); + + /* ++ * Mark bridge as Hot Plug Capable since this is the way how to enable ++ * delivering of Data Link Layer State Change interrupts. ++ * ++ * Set No Command Completed Support because bridge does not support ++ * Command Completed Interrupt. Every command is executed immediately ++ * without any delay. ++ * + * Set Presence Detect State bit permanently since there is no support + * for unplugging the card nor detecting whether it is plugged. (If a + * platform exists in the future that supports it, via a GPIO for +@@ -1070,8 +1110,9 @@ static int advk_sw_pci_bridge_init(struct advk_pcie *pcie) + * value is reserved for ports within the same silicon as Root Port + * which is not our case. + */ +- bridge->pcie_conf.slotcap = cpu_to_le32(FIELD_PREP(PCI_EXP_SLTCAP_PSN, +- 1)); ++ slotcap = PCI_EXP_SLTCAP_NCCS | PCI_EXP_SLTCAP_HPC | ++ FIELD_PREP(PCI_EXP_SLTCAP_PSN, 1); ++ bridge->pcie_conf.slotcap = cpu_to_le32(slotcap); + bridge->pcie_conf.slotsta = cpu_to_le16(PCI_EXP_SLTSTA_PDS); + + /* Indicates supports for Completion Retry Status */ +@@ -1582,6 +1623,24 @@ static void advk_pcie_remove_rp_irq_domain(struct advk_pcie *pcie) + irq_domain_remove(pcie->rp_irq_domain); + } + ++static void advk_pcie_link_irq_handler(struct timer_list *timer) ++{ ++ struct advk_pcie *pcie = from_timer(pcie, timer, link_irq_timer); ++ u16 slotctl; ++ ++ slotctl = le16_to_cpu(pcie->bridge.pcie_conf.slotctl); ++ if (!(slotctl & PCI_EXP_SLTCTL_DLLSCE) || ++ !(slotctl & PCI_EXP_SLTCTL_HPIE)) ++ return; ++ ++ /* ++ * Aardvark HW returns zero for PCI_EXP_FLAGS_IRQ, so use PCIe ++ * interrupt 0 ++ */ ++ if (generic_handle_domain_irq(pcie->rp_irq_domain, 0) == -EINVAL) ++ dev_err_ratelimited(&pcie->pdev->dev, "unhandled HP IRQ\n"); ++} ++ + static void advk_pcie_handle_pme(struct advk_pcie *pcie) + { + u32 requester = advk_readl(pcie, PCIE_MSG_LOG_REG) >> 16; +@@ -1633,6 +1692,7 @@ static void advk_pcie_handle_int(struct advk_pcie *pcie) + { + u32 isr0_val, isr0_mask, isr0_status; + u32 isr1_val, isr1_mask, isr1_status; ++ u16 slotsta; + int i; + + isr0_val = advk_readl(pcie, PCIE_ISR0_REG); +@@ -1659,6 +1719,26 @@ static void advk_pcie_handle_int(struct advk_pcie *pcie) + dev_err_ratelimited(&pcie->pdev->dev, "unhandled ERR IRQ\n"); + } + ++ /* Process Link Down interrupt as HP IRQ */ ++ if (isr0_status & PCIE_ISR0_LINK_DOWN) { ++ advk_writel(pcie, PCIE_ISR0_LINK_DOWN, PCIE_ISR0_REG); ++ ++ dev_info(&pcie->pdev->dev, "link down\n"); ++ ++ pcie->link_was_up = false; ++ ++ slotsta = le16_to_cpu(pcie->bridge.pcie_conf.slotsta); ++ slotsta |= PCI_EXP_SLTSTA_DLLSC; ++ pcie->bridge.pcie_conf.slotsta = cpu_to_le16(slotsta); ++ ++ /* ++ * Deactivate timer and call advk_pcie_link_irq_handler() ++ * function directly as we are in the interrupt context. ++ */ ++ del_timer_sync(&pcie->link_irq_timer); ++ advk_pcie_link_irq_handler(&pcie->link_irq_timer); ++ } ++ + /* Process MSI interrupts */ + if (isr0_status & PCIE_ISR0_MSI_INT_PENDING) + advk_pcie_handle_msi(pcie); +@@ -1895,6 +1975,14 @@ static int advk_pcie_probe(struct platform_device *pdev) + if (ret) + return ret; + ++ /* ++ * generic_handle_domain_irq() expects local IRQs to be disabled since ++ * normally it is called from interrupt context, so use TIMER_IRQSAFE ++ * flag for this link_irq_timer. ++ */ ++ timer_setup(&pcie->link_irq_timer, advk_pcie_link_irq_handler, ++ TIMER_IRQSAFE); ++ + advk_pcie_setup_hw(pcie); + + ret = advk_sw_pci_bridge_init(pcie); +@@ -1983,6 +2071,9 @@ static int advk_pcie_remove(struct platform_device *pdev) + advk_pcie_remove_msi_irq_domain(pcie); + advk_pcie_remove_irq_domain(pcie); + ++ /* Deactivate link event timer */ ++ del_timer_sync(&pcie->link_irq_timer); ++ + /* Free config space for emulated root bridge */ + pci_bridge_emul_cleanup(&pcie->bridge); + +-- +2.37.3 + -- cgit v1.2.3