From 213042bba186b995bc2f25c8c2d06a9652177fa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karel=20Ko=C4=8D=C3=AD?= Date: Tue, 27 Sep 2022 17:09:49 +0200 Subject: Import Turris OS kernel patches --- ...dd-support-for-PCI_EXP_SLTSTA_DLLSC-via-h.patch | 297 +++++++++++++++++++++ 1 file changed, 297 insertions(+) create mode 100644 nixos/modules/kernel-patches/0014-PCI-mvebu-Add-support-for-PCI_EXP_SLTSTA_DLLSC-via-h.patch (limited to 'nixos/modules/kernel-patches/0014-PCI-mvebu-Add-support-for-PCI_EXP_SLTSTA_DLLSC-via-h.patch') diff --git a/nixos/modules/kernel-patches/0014-PCI-mvebu-Add-support-for-PCI_EXP_SLTSTA_DLLSC-via-h.patch b/nixos/modules/kernel-patches/0014-PCI-mvebu-Add-support-for-PCI_EXP_SLTSTA_DLLSC-via-h.patch new file mode 100644 index 0000000..febba42 --- /dev/null +++ b/nixos/modules/kernel-patches/0014-PCI-mvebu-Add-support-for-PCI_EXP_SLTSTA_DLLSC-via-h.patch @@ -0,0 +1,297 @@ +From 1acf23b5be75ad9981eafb94044e507a53cb29c8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pali=20Roh=C3=A1r?= +Date: Fri, 17 Sep 2021 14:53:11 +0200 +Subject: [PATCH 14/96] PCI: mvebu: Add support for PCI_EXP_SLTSTA_DLLSC via + hot plug interrupt +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +If link up/down state is changed in mvebu_pcie_link_up() then trigger +hot plug interrupt with DLLSC state change. + +Also triggers hot plug interrupt when mvebu triggers Link Failure interrupt +which indicates that link was changed from active state or when mvebu +triggers TxReq No Link interrupt which indicates that link is down while +trying to transmit PCIe transaction. + +And this hot plug interrupt also when explicit Link Disable or PCIe Host +Reset is issued as mvebu does not trigger Link Failure when dropping to +Detect via Hot Reset or Link Disable. + +Signed-off-by: Pali Rohár +--- + drivers/pci/controller/Kconfig | 3 + + drivers/pci/controller/pci-mvebu.c | 147 ++++++++++++++++++++++++++++- + 2 files changed, 149 insertions(+), 1 deletion(-) + +diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig +index b8d96d38064d..e4635712dbce 100644 +--- a/drivers/pci/controller/Kconfig ++++ b/drivers/pci/controller/Kconfig +@@ -10,6 +10,9 @@ config PCI_MVEBU + depends on ARM + depends on OF + select PCI_BRIDGE_EMUL ++ select PCIEPORTBUS ++ select HOTPLUG_PCI ++ select HOTPLUG_PCI_PCIE + help + Add support for Marvell EBU PCIe controller. This PCIe controller + is used on 32-bit Marvell ARM SoCs: Dove, Kirkwood, Armada 370, +diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c +index 56924b0a1969..d832c5135d5c 100644 +--- a/drivers/pci/controller/pci-mvebu.c ++++ b/drivers/pci/controller/pci-mvebu.c +@@ -56,12 +56,14 @@ + #define PCIE_CONF_DATA_OFF 0x18fc + #define PCIE_INT_CAUSE_OFF 0x1900 + #define PCIE_INT_UNMASK_OFF 0x1910 ++#define PCIE_INT_TXREQ_NOLINK BIT(0) + #define PCIE_INT_DET_COR BIT(8) + #define PCIE_INT_DET_NONFATAL BIT(9) + #define PCIE_INT_DET_FATAL BIT(10) + #define PCIE_INT_ERR_FATAL BIT(16) + #define PCIE_INT_ERR_NONFATAL BIT(17) + #define PCIE_INT_ERR_COR BIT(18) ++#define PCIE_INT_LINK_FAIL BIT(23) + #define PCIE_INT_INTX(i) BIT(24+i) + #define PCIE_INT_PM_PME BIT(28) + #define PCIE_INT_DET_MASK (PCIE_INT_DET_COR | PCIE_INT_DET_NONFATAL | PCIE_INT_DET_FATAL) +@@ -134,6 +136,8 @@ struct mvebu_pcie_port { + int error_irq; + int intx_irq; + bool pme_pending; ++ struct timer_list link_irq_timer; ++ bool link_was_up; + }; + + static inline void mvebu_writel(struct mvebu_pcie_port *port, u32 val, u32 reg) +@@ -153,7 +157,26 @@ static inline bool mvebu_has_ioport(struct mvebu_pcie_port *port) + + static bool mvebu_pcie_link_up(struct mvebu_pcie_port *port) + { +- return !(mvebu_readl(port, PCIE_STAT_OFF) & PCIE_STAT_LINK_DOWN); ++ bool link_is_up; ++ u16 slotsta; ++ ++ link_is_up = !(mvebu_readl(port, PCIE_STAT_OFF) & PCIE_STAT_LINK_DOWN); ++ ++ if (link_is_up != port->link_was_up) { ++ port->link_was_up = link_is_up; ++ /* ++ * Link IRQ timer/handler is available only when "error" ++ * interrupt was specified in DT. ++ */ ++ if (port->error_irq > 0) { ++ slotsta = le16_to_cpu(port->bridge.pcie_conf.slotsta); ++ port->bridge.pcie_conf.slotsta = ++ cpu_to_le16(slotsta | PCI_EXP_SLTSTA_DLLSC); ++ mod_timer(&port->link_irq_timer, jiffies + 1); ++ } ++ } ++ ++ return link_is_up; + } + + static u8 mvebu_pcie_get_local_bus_nr(struct mvebu_pcie_port *port) +@@ -346,6 +369,19 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port) + mvebu_writel(port, unmask, PCIE_INT_UNMASK_OFF); + } + ++ /* ++ * Unmask No Link and Link Failure interrupts to process Link Down ++ * events. These events are reported as Data Link Layer State Changed ++ * notification via Hot Plug Interrupt. Other parts of Link change ++ * events are available only when "error" interrupt was specified in DT. ++ * So enable these interrupts under same conditions. ++ */ ++ if (port->error_irq > 0) { ++ unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF); ++ unmask |= PCIE_INT_TXREQ_NOLINK | PCIE_INT_LINK_FAIL; ++ mvebu_writel(port, unmask, PCIE_INT_UNMASK_OFF); ++ } ++ + /* + * Fallback code when "intx" interrupt was not specified in DT: + * Unmask all legacy INTx interrupts as driver does not provide a way +@@ -692,6 +728,14 @@ mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge, + val |= slotctl & PCI_EXP_SLTCTL_ASPL_DISABLE; + else if (!(mvebu_readl(port, PCIE_SSPL_OFF) & PCIE_SSPL_ENABLE)) + val |= PCI_EXP_SLTCTL_ASPL_DISABLE; ++ /* ++ * HPIE and DLLSCE bits are stored only in emulated config ++ * space buffer and are supported only when or "error" interrupt ++ * was specified in DT. ++ */ ++ if (port->error_irq > 0) ++ val |= slotctl & (PCI_EXP_SLTCTL_HPIE | ++ PCI_EXP_SLTCTL_DLLSCE); + /* This callback is 32-bit and in high bits is slot status. */ + val |= slotsta << 16; + *value = val; +@@ -823,6 +867,25 @@ mvebu_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge, + else + ctrl &= ~PCIE_CTRL_MASTER_HOT_RESET; + mvebu_writel(port, ctrl, PCIE_CTRL_OFF); ++ /* ++ * When dropping to Detect via Hot Reset, Disable Link ++ * or Loopback states, the Link Failure interrupt is not ++ * asserted. So when setting Secondary Bus Reset / Hot ++ * Reset bit, call link IRQ timer/handler manually. ++ */ ++ if ((ctrl & PCIE_CTRL_MASTER_HOT_RESET) && port->link_was_up) { ++ port->link_was_up = false; ++ /* ++ * Link IRQ timer/handler is available only when ++ * "error" interrupt was specified in DT. ++ */ ++ if (port->error_irq > 0) { ++ u16 slotsta = le16_to_cpu(port->bridge.pcie_conf.slotsta); ++ port->bridge.pcie_conf.slotsta = ++ cpu_to_le16(slotsta | PCI_EXP_SLTSTA_DLLSC); ++ mod_timer(&port->link_irq_timer, jiffies + 1); ++ } ++ } + } + break; + +@@ -851,6 +914,25 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge, + new &= ~PCI_EXP_LNKCTL_CLKREQ_EN; + + mvebu_writel(port, new, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL); ++ /* ++ * When dropping to Detect via Hot Reset, Disable Link ++ * or Loopback states, the Link Failure interrupt is not ++ * asserted. So when setting Link Disable bit, call link ++ * IRQ timer/handler manually. ++ */ ++ if ((new & PCI_EXP_LNKCTL_LD) && port->link_was_up) { ++ port->link_was_up = false; ++ /* ++ * Link IRQ timer/handler is available only when ++ * "error" interrupt was specified in DT. ++ */ ++ if (port->error_irq > 0) { ++ u16 slotsta = le16_to_cpu(port->bridge.pcie_conf.slotsta); ++ port->bridge.pcie_conf.slotsta = ++ cpu_to_le16(slotsta | PCI_EXP_SLTSTA_DLLSC); ++ mod_timer(&port->link_irq_timer, jiffies + 1); ++ } ++ } + break; + + case PCI_EXP_SLTCTL: +@@ -991,6 +1073,15 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port) + bridge->pcie_conf.cap = cpu_to_le16(pcie_cap_ver | PCI_EXP_FLAGS_SLOT); + + /* ++ * When "error" interrupt was specified in DT then driver is able to ++ * deliver Data Link Layer State Change interrupt. So in this case mark ++ * bridge as Hot Plug Capable as this is the way how to enable ++ * delivering of Data Link Layer State Change interrupts. ++ * ++ * No Command Completed Support is set because bridge does not support ++ * Command Completed Interrupt. Every command is executed immediately ++ * without any delay. ++ * + * Set Presence Detect State bit permanently as there is no support for + * unplugging PCIe card from the slot. Assume that PCIe card is always + * connected in slot. +@@ -1002,6 +1093,8 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port) + * Also set correct slot power limit. + */ + bridge->pcie_conf.slotcap = cpu_to_le32( ++ PCI_EXP_SLTCAP_NCCS | ++ (port->error_irq > 0 ? PCI_EXP_SLTCAP_HPC : 0) | + FIELD_PREP(PCI_EXP_SLTCAP_SPLV, port->slot_power_limit_value) | + FIELD_PREP(PCI_EXP_SLTCAP_SPLS, port->slot_power_limit_scale) | + FIELD_PREP(PCI_EXP_SLTCAP_PSN, port->port+1)); +@@ -1191,11 +1284,29 @@ static int mvebu_pcie_init_irq_domain(struct mvebu_pcie_port *port) + return 0; + } + ++static void mvebu_pcie_link_irq_handler(struct timer_list *timer) ++{ ++ struct mvebu_pcie_port *port = from_timer(port, timer, link_irq_timer); ++ struct device *dev = &port->pcie->pdev->dev; ++ u16 slotctl; ++ ++ dev_info(dev, "%s: link %s\n", port->name, port->link_was_up ? "up" : "down"); ++ ++ slotctl = le16_to_cpu(port->bridge.pcie_conf.slotctl); ++ if (!(slotctl & PCI_EXP_SLTCTL_DLLSCE) || ++ !(slotctl & PCI_EXP_SLTCTL_HPIE)) ++ return; ++ ++ if (generic_handle_domain_irq(port->rp_irq_domain, 0) == -EINVAL) ++ dev_err_ratelimited(dev, "unhandled HP IRQ\n"); ++} ++ + static irqreturn_t mvebu_pcie_error_irq_handler(int irq, void *arg) + { + struct mvebu_pcie_port *port = arg; + struct device *dev = &port->pcie->pdev->dev; + u32 cause, unmask, status; ++ u16 slotsta; + + cause = mvebu_readl(port, PCIE_INT_CAUSE_OFF); + unmask = mvebu_readl(port, PCIE_INT_UNMASK_OFF); +@@ -1233,6 +1344,25 @@ static irqreturn_t mvebu_pcie_error_irq_handler(int irq, void *arg) + dev_err_ratelimited(dev, "unhandled ERR IRQ\n"); + } + ++ /* Process No Link and Link Failure interrupts as HP IRQ */ ++ if (status & (PCIE_INT_TXREQ_NOLINK | PCIE_INT_LINK_FAIL)) { ++ mvebu_writel(port, ++ ~(PCIE_INT_TXREQ_NOLINK | PCIE_INT_LINK_FAIL), ++ PCIE_INT_CAUSE_OFF); ++ if (port->link_was_up) { ++ port->link_was_up = false; ++ slotsta = le16_to_cpu(port->bridge.pcie_conf.slotsta); ++ port->bridge.pcie_conf.slotsta = ++ cpu_to_le16(slotsta | PCI_EXP_SLTSTA_DLLSC); ++ /* ++ * Deactivate timer and call mvebu_pcie_link_irq_handler() ++ * function directly as we are in the interrupt context. ++ */ ++ del_timer_sync(&port->link_irq_timer); ++ mvebu_pcie_link_irq_handler(&port->link_irq_timer); ++ } ++ } ++ + return status ? IRQ_HANDLED : IRQ_NONE; + } + +@@ -1798,6 +1928,18 @@ static int mvebu_pcie_probe(struct platform_device *pdev) + } + } + ++ /* ++ * Function mvebu_pcie_link_irq_handler() calls function ++ * generic_handle_irq() and it expects local IRQs to be disabled ++ * as normally generic_handle_irq() is called from the interrupt ++ * context. So use TIMER_IRQSAFE flag for this link_irq_timer. ++ * Available only if "or "error" interrupt was specified. ++ */ ++ if (port->error_irq > 0) ++ timer_setup(&port->link_irq_timer, ++ mvebu_pcie_link_irq_handler, ++ TIMER_IRQSAFE); ++ + /* + * PCIe topology exported by mvebu hw is quite complicated. In + * reality has something like N fully independent host bridges +@@ -1934,6 +2076,9 @@ static int mvebu_pcie_remove(struct platform_device *pdev) + irq_domain_remove(port->rp_irq_domain); + } + ++ if (port->error_irq > 0) ++ del_timer_sync(&port->link_irq_timer); ++ + /* Free config space for emulated root bridge. */ + pci_bridge_emul_cleanup(&port->bridge); + +-- +2.37.2 + -- cgit v1.2.3