aboutsummaryrefslogtreecommitdiff
path: root/pkgs/patches-linux-5.15/0049-PCI-mvebu-Add-support-for-sending-Set_Slot_Power_Lim.patch
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs/patches-linux-5.15/0049-PCI-mvebu-Add-support-for-sending-Set_Slot_Power_Lim.patch')
-rw-r--r--pkgs/patches-linux-5.15/0049-PCI-mvebu-Add-support-for-sending-Set_Slot_Power_Lim.patch210
1 files changed, 210 insertions, 0 deletions
diff --git a/pkgs/patches-linux-5.15/0049-PCI-mvebu-Add-support-for-sending-Set_Slot_Power_Lim.patch b/pkgs/patches-linux-5.15/0049-PCI-mvebu-Add-support-for-sending-Set_Slot_Power_Lim.patch
new file mode 100644
index 0000000..4c526f6
--- /dev/null
+++ b/pkgs/patches-linux-5.15/0049-PCI-mvebu-Add-support-for-sending-Set_Slot_Power_Lim.patch
@@ -0,0 +1,210 @@
+From 5c87d5ff707236098214303289cd5724aebbd0ab Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Pali=20Roh=C3=A1r?= <pali@kernel.org>
+Date: Fri, 17 Sep 2021 14:25:33 +0200
+Subject: [PATCH 49/90] PCI: mvebu: Add support for sending
+ Set_Slot_Power_Limit message
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+If DT supplies the 'slot-power-limit-milliwatt' property, program
+the value in the Slot Power Limit in the Slot Capabilities register
+and program the Root Port to send a Set_Slot_Power_Limit Message
+when the Link transitions to DL_Up.
+
+Signed-off-by: Pali Rohár <pali@kernel.org>
+Reviewed-by: Rob Herring <robh@kernel.org>
+---
+ drivers/pci/controller/pci-mvebu.c | 97 ++++++++++++++++++++++++++++--
+ 1 file changed, 92 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/pci/controller/pci-mvebu.c b/drivers/pci/controller/pci-mvebu.c
+index c9311829dfe3..3c48b15e3948 100644
+--- a/drivers/pci/controller/pci-mvebu.c
++++ b/drivers/pci/controller/pci-mvebu.c
+@@ -8,6 +8,7 @@
+ #include <linux/kernel.h>
+ #include <linux/module.h>
+ #include <linux/pci.h>
++#include <linux/bitfield.h>
+ #include <linux/clk.h>
+ #include <linux/delay.h>
+ #include <linux/gpio.h>
+@@ -66,6 +67,12 @@
+ #define PCIE_STAT_BUS 0xff00
+ #define PCIE_STAT_DEV 0x1f0000
+ #define PCIE_STAT_LINK_DOWN BIT(0)
++#define PCIE_SSPL_OFF 0x1a0c
++#define PCIE_SSPL_VALUE_SHIFT 0
++#define PCIE_SSPL_VALUE_MASK GENMASK(7, 0)
++#define PCIE_SSPL_SCALE_SHIFT 8
++#define PCIE_SSPL_SCALE_MASK GENMASK(9, 8)
++#define PCIE_SSPL_ENABLE BIT(16)
+ #define PCIE_RC_RTSTA 0x1a14
+ #define PCIE_DEBUG_CTRL 0x1a60
+ #define PCIE_DEBUG_SOFT_RESET BIT(20)
+@@ -111,6 +118,8 @@ struct mvebu_pcie_port {
+ struct mvebu_pcie_window iowin;
+ u32 saved_pcie_stat;
+ struct resource regs;
++ u8 slot_power_limit_value;
++ u8 slot_power_limit_scale;
+ struct irq_domain *intx_irq_domain;
+ raw_spinlock_t irq_lock;
+ int intx_irq;
+@@ -239,7 +248,7 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)
+
+ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)
+ {
+- u32 ctrl, lnkcap, cmd, dev_rev, unmask;
++ u32 ctrl, lnkcap, cmd, dev_rev, unmask, sspl;
+
+ /* Setup PCIe controller to Root Complex mode. */
+ ctrl = mvebu_readl(port, PCIE_CTRL_OFF);
+@@ -292,6 +301,20 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)
+ /* Point PCIe unit MBUS decode windows to DRAM space. */
+ mvebu_pcie_setup_wins(port);
+
++ /*
++ * Program Root Port to automatically send Set_Slot_Power_Limit
++ * PCIe Message when changing status from Dl_Down to Dl_Up and valid
++ * slot power limit was specified.
++ */
++ sspl = mvebu_readl(port, PCIE_SSPL_OFF);
++ sspl &= ~(PCIE_SSPL_VALUE_MASK | PCIE_SSPL_SCALE_MASK | PCIE_SSPL_ENABLE);
++ if (port->slot_power_limit_value) {
++ sspl |= port->slot_power_limit_value << PCIE_SSPL_VALUE_SHIFT;
++ sspl |= port->slot_power_limit_scale << PCIE_SSPL_SCALE_SHIFT;
++ sspl |= PCIE_SSPL_ENABLE;
++ }
++ mvebu_writel(port, sspl, PCIE_SSPL_OFF);
++
+ /* Mask all interrupt sources. */
+ mvebu_writel(port, ~PCIE_INT_ALL_MASK, PCIE_INT_UNMASK_OFF);
+
+@@ -633,9 +656,24 @@ mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge,
+ (PCI_EXP_LNKSTA_DLLLA << 16) : 0);
+ break;
+
+- case PCI_EXP_SLTCTL:
+- *value = PCI_EXP_SLTSTA_PDS << 16;
++ case PCI_EXP_SLTCTL: {
++ u16 slotctl = le16_to_cpu(bridge->pcie_conf.slotctl);
++ u16 slotsta = le16_to_cpu(bridge->pcie_conf.slotsta);
++ u32 val = 0;
++ /*
++ * When slot power limit was not specified in DT then
++ * ASPL_DISABLE bit is stored only in emulated config space.
++ * Otherwise reflect status of PCIE_SSPL_ENABLE bit in HW.
++ */
++ if (!port->slot_power_limit_value)
++ val |= slotctl & PCI_EXP_SLTCTL_ASPL_DISABLE;
++ else if (!(mvebu_readl(port, PCIE_SSPL_OFF) & PCIE_SSPL_ENABLE))
++ val |= PCI_EXP_SLTCTL_ASPL_DISABLE;
++ /* This callback is 32-bit and in high bits is slot status. */
++ val |= slotsta << 16;
++ *value = val;
+ break;
++ }
+
+ case PCI_EXP_RTSTA:
+ *value = mvebu_readl(port, PCIE_RC_RTSTA);
+@@ -779,6 +817,22 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge,
+ mvebu_writel(port, new, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL);
+ break;
+
++ case PCI_EXP_SLTCTL:
++ /*
++ * Allow to change PCIE_SSPL_ENABLE bit only when slot power
++ * limit was specified in DT and configured into HW.
++ */
++ if ((mask & PCI_EXP_SLTCTL_ASPL_DISABLE) &&
++ port->slot_power_limit_value) {
++ u32 sspl = mvebu_readl(port, PCIE_SSPL_OFF);
++ if (new & PCI_EXP_SLTCTL_ASPL_DISABLE)
++ sspl &= ~PCIE_SSPL_ENABLE;
++ else
++ sspl |= PCIE_SSPL_ENABLE;
++ mvebu_writel(port, sspl, PCIE_SSPL_OFF);
++ }
++ break;
++
+ case PCI_EXP_RTSTA:
+ /*
+ * PME Status bit in Root Status Register (PCIE_RC_RTSTA)
+@@ -873,8 +927,26 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port)
+ /*
+ * Older mvebu hardware provides PCIe Capability structure only in
+ * version 1. New hardware provides it in version 2.
++ * Enable slot support which is emulated.
+ */
+- bridge->pcie_conf.cap = cpu_to_le16(pcie_cap_ver);
++ bridge->pcie_conf.cap = cpu_to_le16(pcie_cap_ver | PCI_EXP_FLAGS_SLOT);
++
++ /*
++ * 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.
++ *
++ * Set physical slot number to port+1 as mvebu ports are indexed from
++ * zero and zero value is reserved for ports within the same silicon
++ * as Root Port which is not mvebu case.
++ *
++ * Also set correct slot power limit.
++ */
++ bridge->pcie_conf.slotcap = cpu_to_le32(
++ 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));
++ bridge->pcie_conf.slotsta = cpu_to_le16(PCI_EXP_SLTSTA_PDS);
+
+ bridge->subsystem_vendor_id = ssdev_id & 0xffff;
+ bridge->subsystem_id = ssdev_id >> 16;
+@@ -1198,6 +1270,7 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie,
+ {
+ struct device *dev = &pcie->pdev->dev;
+ enum of_gpio_flags flags;
++ u32 slot_power_limit;
+ int reset_gpio, ret;
+ u32 num_lanes;
+
+@@ -1298,6 +1371,15 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie,
+ port->reset_gpio = gpio_to_desc(reset_gpio);
+ }
+
++ slot_power_limit = of_pci_get_slot_power_limit(child,
++ &port->slot_power_limit_value,
++ &port->slot_power_limit_scale);
++ if (slot_power_limit)
++ dev_info(dev, "%s: Slot power limit %u.%uW\n",
++ port->name,
++ slot_power_limit / 1000,
++ (slot_power_limit / 100) % 10);
++
+ port->clk = of_clk_get_by_name(child, NULL);
+ if (IS_ERR(port->clk)) {
+ dev_err(dev, "%s: cannot get clock\n", port->name);
+@@ -1595,7 +1677,7 @@ static int 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);
+- u32 cmd;
++ u32 cmd, sspl;
+ int i;
+
+ /* Remove PCI bus with all devices. */
+@@ -1632,6 +1714,11 @@ static int mvebu_pcie_remove(struct platform_device *pdev)
+ /* Free config space for emulated root bridge. */
+ pci_bridge_emul_cleanup(&port->bridge);
+
++ /* Disable sending Set_Slot_Power_Limit PCIe Message. */
++ sspl = mvebu_readl(port, PCIE_SSPL_OFF);
++ sspl &= ~(PCIE_SSPL_VALUE_MASK | PCIE_SSPL_SCALE_MASK | PCIE_SSPL_ENABLE);
++ mvebu_writel(port, sspl, PCIE_SSPL_OFF);
++
+ /* Disable and clear BARs and windows. */
+ mvebu_pcie_disable_wins(port);
+
+--
+2.34.1
+