rockchip: Add ROCKCHIP PCIe ASPM interface

This commit is contained in:
sbwml 2023-02-22 08:42:46 +08:00
parent 7901702c49
commit f0e7802be6
13 changed files with 1278 additions and 3 deletions

View File

@ -513,7 +513,8 @@ CONFIG_PCI=y
CONFIG_PCIEAER=y
CONFIG_PCIEASPM=y
CONFIG_PCIEASPM_DEFAULT=y
# CONFIG_PCIEASPM_PERFORMANCE is not set
CONFIG_PCIEASPM_EXT=y
CONFIG_PCIEASPM_PERFORMANCE=y
# CONFIG_PCIEASPM_POWERSAVE is not set
# CONFIG_PCIEASPM_POWER_SUPERSAVE is not set
CONFIG_PCIEPORTBUS=y

View File

@ -529,8 +529,9 @@ CONFIG_PARTITION_PERCPU=y
CONFIG_PCI=y
CONFIG_PCIEAER=y
CONFIG_PCIEASPM=y
CONFIG_PCIEASPM_DEFAULT=y
# CONFIG_PCIEASPM_PERFORMANCE is not set
# CONFIG_PCIEASPM_DEFAULT is not set
CONFIG_PCIEASPM_EXT=y
CONFIG_PCIEASPM_PERFORMANCE=y
# CONFIG_PCIEASPM_POWERSAVE is not set
# CONFIG_PCIEASPM_POWER_SUPERSAVE is not set
CONFIG_PCIEPORTBUS=y

View File

@ -0,0 +1,59 @@
From 59e0ec5e5916ed4fac238a3da39aa0659831c41c Mon Sep 17 00:00:00 2001
From: Heiko Stuebner <heiko@sntech.de>
Date: Sun, 30 Oct 2022 20:34:42 +0100
Subject: [PATCH 6/7] arm64: dts: rockchip: fix spdif@fe460000 ordering on
rk356x
Move the node to its correct position, based on its
mmio-address.
Link: https://lore.kernel.org/all/20221030193708.1671069-1-heiko@sntech.de
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
arch/arm64/boot/dts/rockchip/rk356x.dtsi | 28 ++++++++++++------------
1 file changed, 14 insertions(+), 14 deletions(-)
--- a/arch/arm64/boot/dts/rockchip/rk356x.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk356x.dtsi
@@ -1049,20 +1049,6 @@
status = "disabled";
};
- spdif: spdif@fe460000 {
- compatible = "rockchip,rk3568-spdif";
- reg = <0x0 0xfe460000 0x0 0x1000>;
- interrupts = <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>;
- clock-names = "mclk", "hclk";
- clocks = <&cru MCLK_SPDIF_8CH>, <&cru HCLK_SPDIF_8CH>;
- dmas = <&dmac1 1>;
- dma-names = "tx";
- pinctrl-names = "default";
- pinctrl-0 = <&spdifm0_tx>;
- #sound-dai-cells = <0>;
- status = "disabled";
- };
-
i2s0_8ch: i2s@fe400000 {
compatible = "rockchip,rk3568-i2s-tdm";
reg = <0x0 0xfe400000 0x0 0x1000>;
@@ -1141,6 +1127,20 @@
#sound-dai-cells = <0>;
status = "disabled";
};
+
+ spdif: spdif@fe460000 {
+ compatible = "rockchip,rk3568-spdif";
+ reg = <0x0 0xfe460000 0x0 0x1000>;
+ interrupts = <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>;
+ clock-names = "mclk", "hclk";
+ clocks = <&cru MCLK_SPDIF_8CH>, <&cru HCLK_SPDIF_8CH>;
+ dmas = <&dmac1 1>;
+ dma-names = "tx";
+ pinctrl-names = "default";
+ pinctrl-0 = <&spdifm0_tx>;
+ #sound-dai-cells = <0>;
+ status = "disabled";
+ };
dmac0: dma-controller@fe530000 {
compatible = "arm,pl330", "arm,primecell";

View File

@ -0,0 +1,45 @@
From 6c51234cd4e1bfd637c3aab0a94893e832670fe5 Mon Sep 17 00:00:00 2001
From: Shengyu Qu <wiagn233@outlook.com>
Date: Sun, 30 Oct 2022 01:09:04 +0800
Subject: [PATCH 7/7] arm64: dts: rockchip: RK356x: Add I2S2 device node
This patch adds I2S2 device tree node for RK3566/RK3568.
Signed-off-by: Shengyu Qu <wiagn233@outlook.com>
Link: https://lore.kernel.org/r/OS3P286MB259771C12F2B15A4DDF435FE98359@OS3P286MB2597.JPNP286.PROD.OUTLOOK.COM
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
arch/arm64/boot/dts/rockchip/rk356x.dtsi | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
--- a/arch/arm64/boot/dts/rockchip/rk356x.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk356x.dtsi
@@ -1091,6 +1091,28 @@
status = "disabled";
};
+ i2s2_2ch: i2s@fe420000 {
+ compatible = "rockchip,rk3568-i2s-tdm";
+ reg = <0x0 0xfe420000 0x0 0x1000>;
+ interrupts = <GIC_SPI 54 IRQ_TYPE_LEVEL_HIGH>;
+ assigned-clocks = <&cru CLK_I2S2_2CH_SRC>;
+ assigned-clock-rates = <1188000000>;
+ clocks = <&cru MCLK_I2S2_2CH>, <&cru MCLK_I2S2_2CH>, <&cru HCLK_I2S2_2CH>;
+ clock-names = "mclk_tx", "mclk_rx", "hclk";
+ dmas = <&dmac1 4>, <&dmac1 5>;
+ dma-names = "tx", "rx";
+ resets = <&cru SRST_M_I2S2_2CH>;
+ reset-names = "m";
+ rockchip,grf = <&grf>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2s2m0_sclktx
+ &i2s2m0_lrcktx
+ &i2s2m0_sdi
+ &i2s2m0_sdo>;
+ #sound-dai-cells = <0>;
+ status = "disabled";
+ };
+
i2s3_2ch: i2s@fe430000 {
compatible = "rockchip,rk3568-i2s-tdm";
reg = <0x0 0xfe430000 0x0 0x1000>;

View File

@ -0,0 +1,402 @@
From d591c3f6efef5f50fc970aeeedbf9e03b7bd5d21 Mon Sep 17 00:00:00 2001
From: Jon Lin <jon.lin@rock-chips.com>
Date: Fri, 17 Jun 2022 10:38:30 +0800
Subject: [PATCH] PCI: Add ROCKCHIP PCIe ASPM interface
Change-Id: I1156bd10e352145d745899067bf43afda92d5a30
Signed-off-by: Jon Lin <jon.lin@rock-chips.com>
---
drivers/pci/pcie/Kconfig | 6 +
drivers/pci/pcie/Makefile | 1 +
drivers/pci/pcie/aspm_ext.c | 339 ++++++++++++++++++++++++++++++++++++
include/linux/aspm_ext.h | 16 ++
4 files changed, 362 insertions(+)
create mode 100644 drivers/pci/pcie/aspm_ext.c
create mode 100644 include/linux/aspm_ext.h
--- a/drivers/pci/pcie/Kconfig
+++ b/drivers/pci/pcie/Kconfig
@@ -110,6 +110,12 @@ config PCIEASPM_PERFORMANCE
Disable PCI Express ASPM L0s and L1, even if the BIOS enabled them.
endchoice
+config PCIEASPM_EXT
+ tristate "Extend ASPM function"
+ depends on PCIEASPM
+ help
+ This enables the extensions APIs for ASPM control.
+
config PCIE_PME
def_bool y
depends on PCIEPORTBUS && PM
--- a/drivers/pci/pcie/Makefile
+++ b/drivers/pci/pcie/Makefile
@@ -7,6 +7,7 @@ pcieportdrv-y := portdrv_core.o portdr
obj-$(CONFIG_PCIEPORTBUS) += pcieportdrv.o
obj-$(CONFIG_PCIEASPM) += aspm.o
+obj-$(CONFIG_PCIEASPM_EXT) += aspm_ext.o
obj-$(CONFIG_PCIEAER) += aer.o err.o
obj-$(CONFIG_PCIEAER_INJECT) += aer_inject.o
obj-$(CONFIG_PCIE_PME) += pme.o
--- /dev/null
+++ b/drivers/pci/pcie/aspm_ext.c
@@ -0,0 +1,339 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Rockchip PCIe Apis For WIFI
+ *
+ * Copyright (c) 2022 Rockchip Electronics Co., Ltd.
+ */
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/aspm_ext.h>
+#include <linux/errno.h>
+
+
+static u32 rockchip_pcie_pcie_access_cap(struct pci_dev *pdev, int cap, uint offset,
+ bool is_ext, bool is_write, u32 writeval)
+{
+ int cap_ptr = 0;
+ u32 ret = -1;
+ u32 readval;
+
+ if (!(pdev)) {
+ pci_err(pdev, "%s: pdev is NULL\n", __func__);
+ return ret;
+ }
+
+ /* Find Capability offset */
+ if (is_ext) {
+ /* removing max EXT_CAP_ID check as
+ * linux kernel definition's max value is not updated yet as per spec
+ */
+ cap_ptr = pci_find_ext_capability(pdev, cap);
+
+ } else {
+ /* removing max PCI_CAP_ID_MAX check as
+ * previous kernel versions dont have this definition
+ */
+ cap_ptr = pci_find_capability(pdev, cap);
+ }
+
+ /* Return if capability with given ID not found */
+ if (cap_ptr == 0) {
+ pci_err(pdev, "%s: PCI Cap(0x%02x) not supported.\n",
+ __func__, cap);
+ return -EINVAL;
+ }
+
+ if (is_write) {
+ pci_write_config_dword(pdev, (cap_ptr + offset), writeval);
+ ret = 0;
+
+ } else {
+ pci_read_config_dword(pdev, (cap_ptr + offset), &readval);
+ ret = readval;
+ }
+
+ return ret;
+}
+
+static bool rockchip_pcie_bus_aspm_enable_dev(char *device, struct pci_dev *dev, bool enable)
+{
+ u32 linkctrl_before;
+ u32 linkctrl_after = 0;
+ u8 linkctrl_asm;
+
+ linkctrl_before = rockchip_pcie_pcie_access_cap(dev, PCI_CAP_ID_EXP, PCI_EXP_LNKCTL,
+ false, false, 0);
+ linkctrl_asm = (linkctrl_before & PCI_EXP_LNKCTL_ASPMC);
+
+ if (enable) {
+ if (linkctrl_asm == PCI_EXP_LNKCTL_ASPM_L1) {
+ pci_err(dev, "%s: %s already enabled linkctrl: 0x%x\n",
+ __func__, device, linkctrl_before);
+ return false;
+ }
+ /* Enable only L1 ASPM (bit 1) */
+ rockchip_pcie_pcie_access_cap(dev, PCI_CAP_ID_EXP, PCI_EXP_LNKCTL, false,
+ true, (linkctrl_before | PCI_EXP_LNKCTL_ASPM_L1));
+ } else {
+ if (linkctrl_asm == 0) {
+ pci_err(dev, "%s: %s already disabled linkctrl: 0x%x\n",
+ __func__, device, linkctrl_before);
+ return false;
+ }
+ /* Disable complete ASPM (bit 1 and bit 0) */
+ rockchip_pcie_pcie_access_cap(dev, PCI_CAP_ID_EXP, PCI_EXP_LNKCTL, false,
+ true, (linkctrl_before & (~PCI_EXP_LNKCTL_ASPMC)));
+ }
+
+ linkctrl_after = rockchip_pcie_pcie_access_cap(dev, PCI_CAP_ID_EXP, PCI_EXP_LNKCTL,
+ false, false, 0);
+ pci_err(dev, "%s: %s %s, linkctrl_before: 0x%x linkctrl_after: 0x%x\n",
+ __func__, device, (enable ? "ENABLE " : "DISABLE"),
+ linkctrl_before, linkctrl_after);
+
+ return true;
+}
+
+bool rockchip_pcie_bus_aspm_enable_rc_ep(struct pci_dev *child, struct pci_dev *parent, bool enable)
+{
+ bool ret;
+
+ if (enable) {
+ /* Enable only L1 ASPM first RC then EP */
+ ret = rockchip_pcie_bus_aspm_enable_dev("RC", parent, enable);
+ ret = rockchip_pcie_bus_aspm_enable_dev("EP", child, enable);
+ } else {
+ /* Disable complete ASPM first EP then RC */
+ ret = rockchip_pcie_bus_aspm_enable_dev("EP", child, enable);
+ ret = rockchip_pcie_bus_aspm_enable_dev("RC", parent, enable);
+ }
+
+ return ret;
+}
+
+static void pci_clear_and_set_dword(struct pci_dev *pdev, int pos,
+ u32 clear, u32 set)
+{
+ u32 val;
+
+ pci_read_config_dword(pdev, pos, &val);
+ val &= ~clear;
+ val |= set;
+ pci_write_config_dword(pdev, pos, val);
+}
+
+/* Convert L1SS T_pwr encoding to usec */
+static u32 calc_l1ss_pwron(struct pci_dev *pdev, u32 scale, u32 val)
+{
+ switch (scale) {
+ case 0:
+ return val * 2;
+ case 1:
+ return val * 10;
+ case 2:
+ return val * 100;
+ }
+
+ return 0;
+}
+
+static void encode_l12_threshold(u32 threshold_us, u32 *scale, u32 *value)
+{
+ u32 threshold_ns = threshold_us * 1000;
+
+ /* See PCIe r3.1, sec 7.33.3 and sec 6.18 */
+ if (threshold_ns < 32) {
+ *scale = 0;
+ *value = threshold_ns;
+ } else if (threshold_ns < 1024) {
+ *scale = 1;
+ *value = threshold_ns >> 5;
+ } else if (threshold_ns < 32768) {
+ *scale = 2;
+ *value = threshold_ns >> 10;
+ } else if (threshold_ns < 1048576) {
+ *scale = 3;
+ *value = threshold_ns >> 15;
+ } else if (threshold_ns < 33554432) {
+ *scale = 4;
+ *value = threshold_ns >> 20;
+ } else {
+ *scale = 5;
+ *value = threshold_ns >> 25;
+ }
+}
+
+/* Calculate L1.2 PM substate timing parameters */
+static void aspm_calc_l1ss_info(struct pci_dev *child, struct pci_dev *parent)
+{
+ u32 val1, val2, scale1, scale2;
+ u32 t_common_mode, t_power_on, l1_2_threshold, scale, value;
+ u32 ctl1 = 0, ctl2 = 0;
+ u32 pctl1, pctl2, cctl1, cctl2;
+ u32 pl1_2_enables, cl1_2_enables;
+ u32 parent_l1ss_cap, child_l1ss_cap;
+
+ /* Setup L1 substate */
+ pci_read_config_dword(parent, parent->l1ss + PCI_L1SS_CAP,
+ &parent_l1ss_cap);
+ pci_read_config_dword(child, child->l1ss + PCI_L1SS_CAP,
+ &child_l1ss_cap);
+
+ /* 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;
+ t_common_mode = max(val1, val2);
+
+ /* Choose the greater of the two Port T_POWER_ON times */
+ val1 = (parent_l1ss_cap & PCI_L1SS_CAP_P_PWR_ON_VALUE) >> 19;
+ scale1 = (parent_l1ss_cap & PCI_L1SS_CAP_P_PWR_ON_SCALE) >> 16;
+ val2 = (child_l1ss_cap & PCI_L1SS_CAP_P_PWR_ON_VALUE) >> 19;
+ scale2 = (child_l1ss_cap & PCI_L1SS_CAP_P_PWR_ON_SCALE) >> 16;
+
+ if (calc_l1ss_pwron(parent, scale1, val1) >
+ calc_l1ss_pwron(child, scale2, val2)) {
+ ctl2 |= scale1 | (val1 << 3);
+ t_power_on = calc_l1ss_pwron(parent, scale1, val1);
+ } else {
+ ctl2 |= scale2 | (val2 << 3);
+ t_power_on = calc_l1ss_pwron(child, scale2, val2);
+ }
+
+ /* Set LTR_L1.2_THRESHOLD to the time required to transition the
+ * Link from L0 to L1.2 and back to L0 so we enter L1.2 only if
+ * downstream devices report (via LTR) that they can tolerate at
+ * least that much latency.
+ *
+ * Based on PCIe r3.1, sec 5.5.3.3.1, Figures 5-16 and 5-17, and
+ * Table 5-11. T(POWER_OFF) is at most 2us and T(L1.2) is at
+ * least 4us.
+ */
+ l1_2_threshold = 2 + 4 + t_common_mode + t_power_on;
+ encode_l12_threshold(l1_2_threshold, &scale, &value);
+ ctl1 |= t_common_mode << 8 | scale << 29 | value << 16;
+
+ pci_read_config_dword(parent, parent->l1ss + PCI_L1SS_CTL1, &pctl1);
+ pci_read_config_dword(parent, parent->l1ss + PCI_L1SS_CTL2, &pctl2);
+ pci_read_config_dword(child, child->l1ss + PCI_L1SS_CTL1, &cctl1);
+ pci_read_config_dword(child, child->l1ss + PCI_L1SS_CTL2, &cctl2);
+
+ if (ctl1 == pctl1 && ctl1 == cctl1 &&
+ ctl2 == pctl2 && ctl2 == cctl2)
+ return;
+
+ /* Disable L1.2 while updating. See PCIe r5.0, sec 5.5.4, 7.8.3.3 */
+ pl1_2_enables = pctl1 & PCI_L1SS_CTL1_L1_2_MASK;
+ cl1_2_enables = cctl1 & PCI_L1SS_CTL1_L1_2_MASK;
+
+ if (pl1_2_enables || cl1_2_enables) {
+ pci_clear_and_set_dword(child, child->l1ss + PCI_L1SS_CTL1,
+ PCI_L1SS_CTL1_L1_2_MASK, 0);
+ pci_clear_and_set_dword(parent, parent->l1ss + PCI_L1SS_CTL1,
+ PCI_L1SS_CTL1_L1_2_MASK, 0);
+ }
+
+ /* Program T_POWER_ON times in both ports */
+ pci_write_config_dword(parent, parent->l1ss + PCI_L1SS_CTL2, ctl2);
+ pci_write_config_dword(child, child->l1ss + PCI_L1SS_CTL2, ctl2);
+
+ /* Program Common_Mode_Restore_Time in upstream device */
+ pci_clear_and_set_dword(parent, parent->l1ss + PCI_L1SS_CTL1,
+ PCI_L1SS_CTL1_CM_RESTORE_TIME, ctl1);
+
+ /* Program LTR_L1.2_THRESHOLD time in both ports */
+ pci_clear_and_set_dword(parent, parent->l1ss + PCI_L1SS_CTL1,
+ PCI_L1SS_CTL1_LTR_L12_TH_VALUE |
+ PCI_L1SS_CTL1_LTR_L12_TH_SCALE, ctl1);
+ pci_clear_and_set_dword(child, child->l1ss + PCI_L1SS_CTL1,
+ PCI_L1SS_CTL1_LTR_L12_TH_VALUE |
+ PCI_L1SS_CTL1_LTR_L12_TH_SCALE, ctl1);
+
+ if (pl1_2_enables || cl1_2_enables) {
+ pci_clear_and_set_dword(parent, parent->l1ss + PCI_L1SS_CTL1, 0,
+ pl1_2_enables);
+ pci_clear_and_set_dword(child, child->l1ss + PCI_L1SS_CTL1, 0,
+ cl1_2_enables);
+ }
+}
+
+static void rockchip_pcie_bus_l1ss_enable_dev(char *device, struct pci_dev *dev, bool enable)
+{
+ u32 l1ssctrl_before;
+ u32 l1ssctrl_after = 0;
+ u8 l1ss_ep;
+
+ /* Extendend Capacility Reg */
+ l1ssctrl_before = rockchip_pcie_pcie_access_cap(dev, PCI_EXT_CAP_ID_L1SS,
+ PCI_L1SS_CTL1, true, false, 0);
+ l1ss_ep = (l1ssctrl_before & PCI_L1SS_CTL1_L1SS_MASK);
+
+ if (enable) {
+ if (l1ss_ep == PCI_L1SS_CTL1_L1SS_MASK) {
+ pci_err(dev, "%s: %s already enabled, l1ssctrl: 0x%x\n",
+ __func__, device, l1ssctrl_before);
+ return;
+ }
+ rockchip_pcie_pcie_access_cap(dev, PCI_EXT_CAP_ID_L1SS, PCI_L1SS_CTL1,
+ true, true, (l1ssctrl_before | PCI_L1SS_CTL1_L1SS_MASK));
+ } else {
+ if (l1ss_ep == 0) {
+ pci_err(dev, "%s: %s already disabled, l1ssctrl: 0x%x\n",
+ __func__, device, l1ssctrl_before);
+ return;
+ }
+ rockchip_pcie_pcie_access_cap(dev, PCI_EXT_CAP_ID_L1SS, PCI_L1SS_CTL1,
+ true, true, (l1ssctrl_before & (~PCI_L1SS_CTL1_L1SS_MASK)));
+ }
+ l1ssctrl_after = rockchip_pcie_pcie_access_cap(dev, PCI_EXT_CAP_ID_L1SS,
+ PCI_L1SS_CTL1, true, false, 0);
+ pci_err(dev, "%s: %s %s, l1ssctrl_before: 0x%x l1ssctrl_after: 0x%x\n",
+ __func__, device, (enable ? "ENABLE " : "DISABLE"),
+ l1ssctrl_before, l1ssctrl_after);
+}
+
+bool pcie_aspm_ext_is_rc_ep_l1ss_capable(struct pci_dev *child, struct pci_dev *parent)
+{
+ u32 parent_l1ss_cap, child_l1ss_cap;
+
+ /* Setup L1 substate */
+ pci_read_config_dword(parent, parent->l1ss + PCI_L1SS_CAP,
+ &parent_l1ss_cap);
+ pci_read_config_dword(child, child->l1ss + PCI_L1SS_CAP,
+ &child_l1ss_cap);
+
+ if (!(parent_l1ss_cap & PCI_L1SS_CAP_L1_PM_SS))
+ parent_l1ss_cap = 0;
+ if (!(child_l1ss_cap & PCI_L1SS_CAP_L1_PM_SS))
+ child_l1ss_cap = 0;
+
+ if (parent_l1ss_cap && child_l1ss_cap)
+ return true;
+ else
+ return false;
+}
+EXPORT_SYMBOL(pcie_aspm_ext_is_rc_ep_l1ss_capable);
+
+void pcie_aspm_ext_l1ss_enable(struct pci_dev *child, struct pci_dev *parent, bool enable)
+{
+ bool ret;
+
+ /* Disable ASPM of RC and EP */
+ ret = rockchip_pcie_bus_aspm_enable_rc_ep(child, parent, false);
+
+ if (enable) {
+ /* Enable RC then EP */
+ aspm_calc_l1ss_info(child, parent);
+ rockchip_pcie_bus_l1ss_enable_dev("RC", parent, enable);
+ rockchip_pcie_bus_l1ss_enable_dev("EP", child, enable);
+ } else {
+ /* Disable EP then RC */
+ rockchip_pcie_bus_l1ss_enable_dev("EP", child, enable);
+ rockchip_pcie_bus_l1ss_enable_dev("RC", parent, enable);
+ }
+
+ /* Enable ASPM of RC and EP only if this API disabled */
+ if (ret)
+ rockchip_pcie_bus_aspm_enable_rc_ep(child, parent, true);
+}
+EXPORT_SYMBOL(pcie_aspm_ext_l1ss_enable);
--- /dev/null
+++ b/include/linux/aspm_ext.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/* Copyright (c) 2022 Rockchip Electronics Co., Ltd. */
+
+#ifndef _ASPM_EXT_H
+#define _ASPM_EXT_H
+
+#if IS_REACHABLE(CONFIG_PCIEASPM_EXT)
+bool pcie_aspm_ext_is_rc_ep_l1ss_capable(struct pci_dev *child, struct pci_dev *parent);
+void pcie_aspm_ext_l1ss_enable(struct pci_dev *child, struct pci_dev *parent, bool enable);
+#else
+static inline bool pcie_aspm_ext_is_rc_ep_l1ss_capable(struct pci_dev *child, struct pci_dev *parent) { return false; }
+static inline void pcie_aspm_ext_l1ss_enable(struct pci_dev *child, struct pci_dev *parent, bool enable) {}
+#endif
+
+#endif

View File

@ -0,0 +1,24 @@
From a6c71606de486944c5fb028f47604fb22292312b Mon Sep 17 00:00:00 2001
From: Jon Lin <jon.lin@rock-chips.com>
Date: Fri, 24 Jun 2022 21:32:11 +0800
Subject: [PATCH] PCI: aspm_ext: Re-enable LRT for L1SS after power loss
Change-Id: Iedb72ee74660a8f11f38895e06766c3b77728ba3
Signed-off-by: Jon Lin <jon.lin@rock-chips.com>
---
drivers/pci/pcie/aspm_ext.c | 4 ++++
1 file changed, 4 insertions(+)
--- a/drivers/pci/pcie/aspm_ext.c
+++ b/drivers/pci/pcie/aspm_ext.c
@@ -322,6 +322,10 @@ void pcie_aspm_ext_l1ss_enable(struct pc
ret = rockchip_pcie_bus_aspm_enable_rc_ep(child, parent, false);
if (enable) {
+ /* LRT enable bits loss after wifi off, enable it after power on */
+ if (parent->ltr_path)
+ pcie_capability_set_word(parent, PCI_EXP_DEVCTL2, PCI_EXP_DEVCTL2_LTR_EN);
+
/* Enable RC then EP */
aspm_calc_l1ss_info(child, parent);
rockchip_pcie_bus_l1ss_enable_dev("RC", parent, enable);

View File

@ -0,0 +1,38 @@
From cf03561e5ec9f2f8b41a992f3b8ca19b9c3b9e47 Mon Sep 17 00:00:00 2001
From: Tao Huang <huangtao@rock-chips.com>
Date: Fri, 15 Jul 2022 20:56:15 +0800
Subject: [PATCH] PCI: aspm_ext: Fix Add missing MODULE_LICENSE()
ERROR: modpost: missing MODULE_LICENSE() in drivers/pci/pcie/aspm_ext.o
Signed-off-by: Tao Huang <huangtao@rock-chips.com>
Change-Id: Id365aba7a73f02cc2c61882b46937250e64af01c
---
drivers/pci/pcie/aspm_ext.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
--- a/drivers/pci/pcie/aspm_ext.c
+++ b/drivers/pci/pcie/aspm_ext.c
@@ -6,6 +6,7 @@
*/
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/pci.h>
#include <linux/aspm_ext.h>
#include <linux/errno.h>
@@ -95,7 +96,7 @@ static bool rockchip_pcie_bus_aspm_enabl
return true;
}
-bool rockchip_pcie_bus_aspm_enable_rc_ep(struct pci_dev *child, struct pci_dev *parent, bool enable)
+static bool rockchip_pcie_bus_aspm_enable_rc_ep(struct pci_dev *child, struct pci_dev *parent, bool enable)
{
bool ret;
@@ -341,3 +342,5 @@ void pcie_aspm_ext_l1ss_enable(struct pc
rockchip_pcie_bus_aspm_enable_rc_ep(child, parent, true);
}
EXPORT_SYMBOL(pcie_aspm_ext_l1ss_enable);
+
+MODULE_LICENSE("GPL");

View File

@ -0,0 +1,58 @@
From 6e57e8b78939b4d849511fa96467d09d77ce8a26 Mon Sep 17 00:00:00 2001
From: Shawn Lin <shawn.lin@rock-chips.com>
Date: Tue, 31 Jan 2023 08:46:29 +0800
Subject: [PATCH 1/3] mmc: sdhci-of-dwcmshc: Update DLL and pre-change delay
for rockchip platform
For Rockchip platform, DLL bypass bit and start bit need to be set if
DLL is not locked. And adjust pre-change delay to 0x3 for better signal
test result.
Signed-off-by: Shawn Lin <shawn.lin@rock-chips.com>
---
drivers/mmc/host/sdhci-of-dwcmshc.c | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
--- a/drivers/mmc/host/sdhci-of-dwcmshc.c
+++ b/drivers/mmc/host/sdhci-of-dwcmshc.c
@@ -48,6 +48,7 @@
#define DWCMSHC_EMMC_DLL_RXCLK_SRCSEL 29
#define DWCMSHC_EMMC_DLL_START_POINT 16
#define DWCMSHC_EMMC_DLL_INC 8
+#define DWCMSHC_EMMC_DLL_BYPASS BIT(24)
#define DWCMSHC_EMMC_DLL_DLYENA BIT(27)
#define DLL_TXCLK_TAPNUM_DEFAULT 0x10
#define DLL_TXCLK_TAPNUM_90_DEGREES 0xA
@@ -60,6 +61,7 @@
#define DLL_RXCLK_NO_INVERTER 1
#define DLL_RXCLK_INVERTER 0
#define DLL_CMDOUT_TAPNUM_90_DEGREES 0x8
+#define DLL_RXCLK_ORI_GATE BIT(31)
#define DLL_CMDOUT_TAPNUM_FROM_SW BIT(24)
#define DLL_CMDOUT_SRC_CLK_NEG BIT(28)
#define DLL_CMDOUT_EN_SRC_CLK_NEG BIT(29)
@@ -234,9 +236,12 @@ static void dwcmshc_rk3568_set_clock(str
sdhci_writel(host, extra, reg);
if (clock <= 52000000) {
- /* Disable DLL and reset both of sample and drive clock */
- sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_CTRL);
- sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_RXCLK);
+ /*
+ * Disable DLL and reset both of sample and drive clock.
+ * The bypass bit and start bit need to be set if DLL is not locked.
+ */
+ sdhci_writel(host, DWCMSHC_EMMC_DLL_BYPASS | DWCMSHC_EMMC_DLL_START, DWCMSHC_EMMC_DLL_CTRL);
+ sdhci_writel(host, DLL_RXCLK_ORI_GATE, DWCMSHC_EMMC_DLL_RXCLK);
sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_TXCLK);
sdhci_writel(host, 0, DECMSHC_EMMC_DLL_CMDOUT);
/*
@@ -279,7 +284,7 @@ static void dwcmshc_rk3568_set_clock(str
}
extra = 0x1 << 16 | /* tune clock stop en */
- 0x2 << 17 | /* pre-change delay */
+ 0x3 << 17 | /* pre-change delay */
0x3 << 19; /* post-change delay */
sdhci_writel(host, extra, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL);

View File

@ -0,0 +1,93 @@
From ce8180045daea68c45ba5bd2495885410b7b9dc8 Mon Sep 17 00:00:00 2001
From: Shawn Lin <shawn.lin@rock-chips.com>
Date: Tue, 31 Jan 2023 08:46:30 +0800
Subject: [PATCH 2/3] mmc: sdhci-of-dwcmshc: Add runtime PM support
This patch adds runtime PM support.
Signed-off-by: Shawn Lin <shawn.lin@rock-chips.com>
---
drivers/mmc/host/sdhci-of-dwcmshc.c | 51 ++++++++++++++++++++++++++++-
1 file changed, 50 insertions(+), 1 deletion(-)
--- a/drivers/mmc/host/sdhci-of-dwcmshc.c
+++ b/drivers/mmc/host/sdhci-of-dwcmshc.c
@@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/sizes.h>
@@ -546,6 +547,13 @@ static int dwcmshc_probe(struct platform
if (err)
goto err_setup_host;
+ pm_runtime_get_noresume(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_put_autosuspend(&pdev->dev);
+
return 0;
err_setup_host:
@@ -575,6 +583,11 @@ static int dwcmshc_remove(struct platfor
if (rk_priv)
clk_bulk_disable_unprepare(RK35xx_MAX_CLKS,
rk_priv->rockchip_clks);
+
+ pm_runtime_get_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+
sdhci_pltfm_free(pdev);
return 0;
@@ -633,7 +646,43 @@ static int dwcmshc_resume(struct device
}
#endif
-static SIMPLE_DEV_PM_OPS(dwcmshc_pmops, dwcmshc_suspend, dwcmshc_resume);
+#ifdef CONFIG_PM
+static int dwcmshc_runtime_suspend(struct device *dev)
+{
+ struct sdhci_host *host = dev_get_drvdata(dev);
+ u16 data;
+ int ret;
+
+ ret = sdhci_runtime_suspend_host(host);
+ if (ret)
+ return ret;
+
+ data = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+ data &= ~SDHCI_CLOCK_CARD_EN;
+ sdhci_writew(host, data, SDHCI_CLOCK_CONTROL);
+
+ return 0;
+}
+
+static int dwcmshc_runtime_resume(struct device *dev)
+{
+ struct sdhci_host *host = dev_get_drvdata(dev);
+ u16 data;
+
+ data = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+ data |= SDHCI_CLOCK_CARD_EN;
+ sdhci_writew(host, data, SDHCI_CLOCK_CONTROL);
+
+ return sdhci_runtime_resume_host(host, 0);
+}
+#endif
+
+static const struct dev_pm_ops dwcmshc_pmops = {
+ SET_SYSTEM_SLEEP_PM_OPS(dwcmshc_suspend,
+ dwcmshc_resume)
+ SET_RUNTIME_PM_OPS(dwcmshc_runtime_suspend,
+ dwcmshc_runtime_resume, NULL)
+};
static struct platform_driver sdhci_dwcmshc_driver = {
.driver = {

View File

@ -0,0 +1,90 @@
From 55c88b9f9ed2ba17fe5a4b095fd0fbd9c38b1c4b Mon Sep 17 00:00:00 2001
From: Shawn Lin <shawn.lin@rock-chips.com>
Date: Tue, 31 Jan 2023 08:46:31 +0800
Subject: [PATCH 3/3] mmc: sdhci-of-dwcmshc: Add host software queue support
Signed-off-by: Shawn Lin <shawn.lin@rock-chips.com>
---
drivers/mmc/host/sdhci-of-dwcmshc.c | 29 ++++++++++++++++++++++++++++-
1 file changed, 28 insertions(+), 1 deletion(-)
--- a/drivers/mmc/host/sdhci-of-dwcmshc.c
+++ b/drivers/mmc/host/sdhci-of-dwcmshc.c
@@ -20,6 +20,7 @@
#include <linux/sizes.h>
#include "sdhci-pltfm.h"
+#include "mmc_hsq.h"
#define SDHCI_DWCMSHC_ARG2_STUFF GENMASK(31, 16)
@@ -331,6 +332,14 @@ static void rk35xx_sdhci_reset(struct sd
sdhci_reset(host, mask);
}
+static void sdhci_dwcmshc_request_done(struct sdhci_host *host, struct mmc_request *mrq)
+{
+ if (mmc_hsq_finalize_request(host->mmc, mrq))
+ return;
+
+ mmc_request_done(host->mmc, mrq);
+}
+
static const struct sdhci_ops sdhci_dwcmshc_ops = {
.set_clock = sdhci_set_clock,
.set_bus_width = sdhci_set_bus_width,
@@ -347,6 +356,7 @@ static const struct sdhci_ops sdhci_dwcm
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
.reset = rk35xx_sdhci_reset,
.adma_write_desc = dwcmshc_adma_write_desc,
+ .request_done = sdhci_dwcmshc_request_done,
};
static const struct sdhci_pltfm_data sdhci_dwcmshc_pdata = {
@@ -462,6 +472,7 @@ static int dwcmshc_probe(struct platform
struct dwcmshc_priv *priv;
struct rk35xx_priv *rk_priv = NULL;
const struct sdhci_pltfm_data *pltfm_data;
+ struct mmc_hsq *hsq;
int err;
u32 extra;
@@ -515,6 +526,16 @@ static int dwcmshc_probe(struct platform
host->mmc_host_ops.request = dwcmshc_request;
host->mmc_host_ops.hs400_enhanced_strobe = dwcmshc_hs400_enhanced_strobe;
+ hsq = devm_kzalloc(&pdev->dev, sizeof(*hsq), GFP_KERNEL);
+ if (!hsq) {
+ err = -ENOMEM;
+ goto err_clk;
+ }
+
+ err = mmc_hsq_init(hsq, host->mmc);
+ if (err)
+ goto err_clk;
+
if (pltfm_data == &sdhci_dwcmshc_rk35xx_pdata) {
rk_priv = devm_kzalloc(&pdev->dev, sizeof(struct rk35xx_priv), GFP_KERNEL);
if (!rk_priv) {
@@ -602,6 +623,8 @@ static int dwcmshc_suspend(struct device
struct rk35xx_priv *rk_priv = priv->priv;
int ret;
+ mmc_hsq_suspend(host->mmc);
+
ret = sdhci_suspend_host(host);
if (ret)
return ret;
@@ -642,7 +665,11 @@ static int dwcmshc_resume(struct device
return ret;
}
- return sdhci_resume_host(host);
+ ret = sdhci_resume_host(host);
+ if (ret)
+ return ret;
+
+ return mmc_hsq_resume(host->mmc);
}
#endif

View File

@ -0,0 +1,402 @@
From d591c3f6efef5f50fc970aeeedbf9e03b7bd5d21 Mon Sep 17 00:00:00 2001
From: Jon Lin <jon.lin@rock-chips.com>
Date: Fri, 17 Jun 2022 10:38:30 +0800
Subject: [PATCH] PCI: Add ROCKCHIP PCIe ASPM interface
Change-Id: I1156bd10e352145d745899067bf43afda92d5a30
Signed-off-by: Jon Lin <jon.lin@rock-chips.com>
---
drivers/pci/pcie/Kconfig | 6 +
drivers/pci/pcie/Makefile | 1 +
drivers/pci/pcie/aspm_ext.c | 339 ++++++++++++++++++++++++++++++++++++
include/linux/aspm_ext.h | 16 ++
4 files changed, 362 insertions(+)
create mode 100644 drivers/pci/pcie/aspm_ext.c
create mode 100644 include/linux/aspm_ext.h
--- a/drivers/pci/pcie/Kconfig
+++ b/drivers/pci/pcie/Kconfig
@@ -114,6 +114,12 @@ config PCIEASPM_PERFORMANCE
Disable PCI Express ASPM L0s and L1, even if the BIOS enabled them.
endchoice
+config PCIEASPM_EXT
+ tristate "Extend ASPM function"
+ depends on PCIEASPM
+ help
+ This enables the extensions APIs for ASPM control.
+
config PCIE_PME
def_bool y
depends on PCIEPORTBUS && PM
--- a/drivers/pci/pcie/Makefile
+++ b/drivers/pci/pcie/Makefile
@@ -7,6 +7,7 @@ pcieportdrv-y := portdrv.o rcec.o
obj-$(CONFIG_PCIEPORTBUS) += pcieportdrv.o
obj-$(CONFIG_PCIEASPM) += aspm.o
+obj-$(CONFIG_PCIEASPM_EXT) += aspm_ext.o
obj-$(CONFIG_PCIEAER) += aer.o err.o
obj-$(CONFIG_PCIEAER_INJECT) += aer_inject.o
obj-$(CONFIG_PCIE_PME) += pme.o
--- /dev/null
+++ b/drivers/pci/pcie/aspm_ext.c
@@ -0,0 +1,339 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Rockchip PCIe Apis For WIFI
+ *
+ * Copyright (c) 2022 Rockchip Electronics Co., Ltd.
+ */
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/aspm_ext.h>
+#include <linux/errno.h>
+
+
+static u32 rockchip_pcie_pcie_access_cap(struct pci_dev *pdev, int cap, uint offset,
+ bool is_ext, bool is_write, u32 writeval)
+{
+ int cap_ptr = 0;
+ u32 ret = -1;
+ u32 readval;
+
+ if (!(pdev)) {
+ pci_err(pdev, "%s: pdev is NULL\n", __func__);
+ return ret;
+ }
+
+ /* Find Capability offset */
+ if (is_ext) {
+ /* removing max EXT_CAP_ID check as
+ * linux kernel definition's max value is not updated yet as per spec
+ */
+ cap_ptr = pci_find_ext_capability(pdev, cap);
+
+ } else {
+ /* removing max PCI_CAP_ID_MAX check as
+ * previous kernel versions dont have this definition
+ */
+ cap_ptr = pci_find_capability(pdev, cap);
+ }
+
+ /* Return if capability with given ID not found */
+ if (cap_ptr == 0) {
+ pci_err(pdev, "%s: PCI Cap(0x%02x) not supported.\n",
+ __func__, cap);
+ return -EINVAL;
+ }
+
+ if (is_write) {
+ pci_write_config_dword(pdev, (cap_ptr + offset), writeval);
+ ret = 0;
+
+ } else {
+ pci_read_config_dword(pdev, (cap_ptr + offset), &readval);
+ ret = readval;
+ }
+
+ return ret;
+}
+
+static bool rockchip_pcie_bus_aspm_enable_dev(char *device, struct pci_dev *dev, bool enable)
+{
+ u32 linkctrl_before;
+ u32 linkctrl_after = 0;
+ u8 linkctrl_asm;
+
+ linkctrl_before = rockchip_pcie_pcie_access_cap(dev, PCI_CAP_ID_EXP, PCI_EXP_LNKCTL,
+ false, false, 0);
+ linkctrl_asm = (linkctrl_before & PCI_EXP_LNKCTL_ASPMC);
+
+ if (enable) {
+ if (linkctrl_asm == PCI_EXP_LNKCTL_ASPM_L1) {
+ pci_err(dev, "%s: %s already enabled linkctrl: 0x%x\n",
+ __func__, device, linkctrl_before);
+ return false;
+ }
+ /* Enable only L1 ASPM (bit 1) */
+ rockchip_pcie_pcie_access_cap(dev, PCI_CAP_ID_EXP, PCI_EXP_LNKCTL, false,
+ true, (linkctrl_before | PCI_EXP_LNKCTL_ASPM_L1));
+ } else {
+ if (linkctrl_asm == 0) {
+ pci_err(dev, "%s: %s already disabled linkctrl: 0x%x\n",
+ __func__, device, linkctrl_before);
+ return false;
+ }
+ /* Disable complete ASPM (bit 1 and bit 0) */
+ rockchip_pcie_pcie_access_cap(dev, PCI_CAP_ID_EXP, PCI_EXP_LNKCTL, false,
+ true, (linkctrl_before & (~PCI_EXP_LNKCTL_ASPMC)));
+ }
+
+ linkctrl_after = rockchip_pcie_pcie_access_cap(dev, PCI_CAP_ID_EXP, PCI_EXP_LNKCTL,
+ false, false, 0);
+ pci_err(dev, "%s: %s %s, linkctrl_before: 0x%x linkctrl_after: 0x%x\n",
+ __func__, device, (enable ? "ENABLE " : "DISABLE"),
+ linkctrl_before, linkctrl_after);
+
+ return true;
+}
+
+bool rockchip_pcie_bus_aspm_enable_rc_ep(struct pci_dev *child, struct pci_dev *parent, bool enable)
+{
+ bool ret;
+
+ if (enable) {
+ /* Enable only L1 ASPM first RC then EP */
+ ret = rockchip_pcie_bus_aspm_enable_dev("RC", parent, enable);
+ ret = rockchip_pcie_bus_aspm_enable_dev("EP", child, enable);
+ } else {
+ /* Disable complete ASPM first EP then RC */
+ ret = rockchip_pcie_bus_aspm_enable_dev("EP", child, enable);
+ ret = rockchip_pcie_bus_aspm_enable_dev("RC", parent, enable);
+ }
+
+ return ret;
+}
+
+static void pci_clear_and_set_dword(struct pci_dev *pdev, int pos,
+ u32 clear, u32 set)
+{
+ u32 val;
+
+ pci_read_config_dword(pdev, pos, &val);
+ val &= ~clear;
+ val |= set;
+ pci_write_config_dword(pdev, pos, val);
+}
+
+/* Convert L1SS T_pwr encoding to usec */
+static u32 calc_l1ss_pwron(struct pci_dev *pdev, u32 scale, u32 val)
+{
+ switch (scale) {
+ case 0:
+ return val * 2;
+ case 1:
+ return val * 10;
+ case 2:
+ return val * 100;
+ }
+
+ return 0;
+}
+
+static void encode_l12_threshold(u32 threshold_us, u32 *scale, u32 *value)
+{
+ u32 threshold_ns = threshold_us * 1000;
+
+ /* See PCIe r3.1, sec 7.33.3 and sec 6.18 */
+ if (threshold_ns < 32) {
+ *scale = 0;
+ *value = threshold_ns;
+ } else if (threshold_ns < 1024) {
+ *scale = 1;
+ *value = threshold_ns >> 5;
+ } else if (threshold_ns < 32768) {
+ *scale = 2;
+ *value = threshold_ns >> 10;
+ } else if (threshold_ns < 1048576) {
+ *scale = 3;
+ *value = threshold_ns >> 15;
+ } else if (threshold_ns < 33554432) {
+ *scale = 4;
+ *value = threshold_ns >> 20;
+ } else {
+ *scale = 5;
+ *value = threshold_ns >> 25;
+ }
+}
+
+/* Calculate L1.2 PM substate timing parameters */
+static void aspm_calc_l1ss_info(struct pci_dev *child, struct pci_dev *parent)
+{
+ u32 val1, val2, scale1, scale2;
+ u32 t_common_mode, t_power_on, l1_2_threshold, scale, value;
+ u32 ctl1 = 0, ctl2 = 0;
+ u32 pctl1, pctl2, cctl1, cctl2;
+ u32 pl1_2_enables, cl1_2_enables;
+ u32 parent_l1ss_cap, child_l1ss_cap;
+
+ /* Setup L1 substate */
+ pci_read_config_dword(parent, parent->l1ss + PCI_L1SS_CAP,
+ &parent_l1ss_cap);
+ pci_read_config_dword(child, child->l1ss + PCI_L1SS_CAP,
+ &child_l1ss_cap);
+
+ /* 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;
+ t_common_mode = max(val1, val2);
+
+ /* Choose the greater of the two Port T_POWER_ON times */
+ val1 = (parent_l1ss_cap & PCI_L1SS_CAP_P_PWR_ON_VALUE) >> 19;
+ scale1 = (parent_l1ss_cap & PCI_L1SS_CAP_P_PWR_ON_SCALE) >> 16;
+ val2 = (child_l1ss_cap & PCI_L1SS_CAP_P_PWR_ON_VALUE) >> 19;
+ scale2 = (child_l1ss_cap & PCI_L1SS_CAP_P_PWR_ON_SCALE) >> 16;
+
+ if (calc_l1ss_pwron(parent, scale1, val1) >
+ calc_l1ss_pwron(child, scale2, val2)) {
+ ctl2 |= scale1 | (val1 << 3);
+ t_power_on = calc_l1ss_pwron(parent, scale1, val1);
+ } else {
+ ctl2 |= scale2 | (val2 << 3);
+ t_power_on = calc_l1ss_pwron(child, scale2, val2);
+ }
+
+ /* Set LTR_L1.2_THRESHOLD to the time required to transition the
+ * Link from L0 to L1.2 and back to L0 so we enter L1.2 only if
+ * downstream devices report (via LTR) that they can tolerate at
+ * least that much latency.
+ *
+ * Based on PCIe r3.1, sec 5.5.3.3.1, Figures 5-16 and 5-17, and
+ * Table 5-11. T(POWER_OFF) is at most 2us and T(L1.2) is at
+ * least 4us.
+ */
+ l1_2_threshold = 2 + 4 + t_common_mode + t_power_on;
+ encode_l12_threshold(l1_2_threshold, &scale, &value);
+ ctl1 |= t_common_mode << 8 | scale << 29 | value << 16;
+
+ pci_read_config_dword(parent, parent->l1ss + PCI_L1SS_CTL1, &pctl1);
+ pci_read_config_dword(parent, parent->l1ss + PCI_L1SS_CTL2, &pctl2);
+ pci_read_config_dword(child, child->l1ss + PCI_L1SS_CTL1, &cctl1);
+ pci_read_config_dword(child, child->l1ss + PCI_L1SS_CTL2, &cctl2);
+
+ if (ctl1 == pctl1 && ctl1 == cctl1 &&
+ ctl2 == pctl2 && ctl2 == cctl2)
+ return;
+
+ /* Disable L1.2 while updating. See PCIe r5.0, sec 5.5.4, 7.8.3.3 */
+ pl1_2_enables = pctl1 & PCI_L1SS_CTL1_L1_2_MASK;
+ cl1_2_enables = cctl1 & PCI_L1SS_CTL1_L1_2_MASK;
+
+ if (pl1_2_enables || cl1_2_enables) {
+ pci_clear_and_set_dword(child, child->l1ss + PCI_L1SS_CTL1,
+ PCI_L1SS_CTL1_L1_2_MASK, 0);
+ pci_clear_and_set_dword(parent, parent->l1ss + PCI_L1SS_CTL1,
+ PCI_L1SS_CTL1_L1_2_MASK, 0);
+ }
+
+ /* Program T_POWER_ON times in both ports */
+ pci_write_config_dword(parent, parent->l1ss + PCI_L1SS_CTL2, ctl2);
+ pci_write_config_dword(child, child->l1ss + PCI_L1SS_CTL2, ctl2);
+
+ /* Program Common_Mode_Restore_Time in upstream device */
+ pci_clear_and_set_dword(parent, parent->l1ss + PCI_L1SS_CTL1,
+ PCI_L1SS_CTL1_CM_RESTORE_TIME, ctl1);
+
+ /* Program LTR_L1.2_THRESHOLD time in both ports */
+ pci_clear_and_set_dword(parent, parent->l1ss + PCI_L1SS_CTL1,
+ PCI_L1SS_CTL1_LTR_L12_TH_VALUE |
+ PCI_L1SS_CTL1_LTR_L12_TH_SCALE, ctl1);
+ pci_clear_and_set_dword(child, child->l1ss + PCI_L1SS_CTL1,
+ PCI_L1SS_CTL1_LTR_L12_TH_VALUE |
+ PCI_L1SS_CTL1_LTR_L12_TH_SCALE, ctl1);
+
+ if (pl1_2_enables || cl1_2_enables) {
+ pci_clear_and_set_dword(parent, parent->l1ss + PCI_L1SS_CTL1, 0,
+ pl1_2_enables);
+ pci_clear_and_set_dword(child, child->l1ss + PCI_L1SS_CTL1, 0,
+ cl1_2_enables);
+ }
+}
+
+static void rockchip_pcie_bus_l1ss_enable_dev(char *device, struct pci_dev *dev, bool enable)
+{
+ u32 l1ssctrl_before;
+ u32 l1ssctrl_after = 0;
+ u8 l1ss_ep;
+
+ /* Extendend Capacility Reg */
+ l1ssctrl_before = rockchip_pcie_pcie_access_cap(dev, PCI_EXT_CAP_ID_L1SS,
+ PCI_L1SS_CTL1, true, false, 0);
+ l1ss_ep = (l1ssctrl_before & PCI_L1SS_CTL1_L1SS_MASK);
+
+ if (enable) {
+ if (l1ss_ep == PCI_L1SS_CTL1_L1SS_MASK) {
+ pci_err(dev, "%s: %s already enabled, l1ssctrl: 0x%x\n",
+ __func__, device, l1ssctrl_before);
+ return;
+ }
+ rockchip_pcie_pcie_access_cap(dev, PCI_EXT_CAP_ID_L1SS, PCI_L1SS_CTL1,
+ true, true, (l1ssctrl_before | PCI_L1SS_CTL1_L1SS_MASK));
+ } else {
+ if (l1ss_ep == 0) {
+ pci_err(dev, "%s: %s already disabled, l1ssctrl: 0x%x\n",
+ __func__, device, l1ssctrl_before);
+ return;
+ }
+ rockchip_pcie_pcie_access_cap(dev, PCI_EXT_CAP_ID_L1SS, PCI_L1SS_CTL1,
+ true, true, (l1ssctrl_before & (~PCI_L1SS_CTL1_L1SS_MASK)));
+ }
+ l1ssctrl_after = rockchip_pcie_pcie_access_cap(dev, PCI_EXT_CAP_ID_L1SS,
+ PCI_L1SS_CTL1, true, false, 0);
+ pci_err(dev, "%s: %s %s, l1ssctrl_before: 0x%x l1ssctrl_after: 0x%x\n",
+ __func__, device, (enable ? "ENABLE " : "DISABLE"),
+ l1ssctrl_before, l1ssctrl_after);
+}
+
+bool pcie_aspm_ext_is_rc_ep_l1ss_capable(struct pci_dev *child, struct pci_dev *parent)
+{
+ u32 parent_l1ss_cap, child_l1ss_cap;
+
+ /* Setup L1 substate */
+ pci_read_config_dword(parent, parent->l1ss + PCI_L1SS_CAP,
+ &parent_l1ss_cap);
+ pci_read_config_dword(child, child->l1ss + PCI_L1SS_CAP,
+ &child_l1ss_cap);
+
+ if (!(parent_l1ss_cap & PCI_L1SS_CAP_L1_PM_SS))
+ parent_l1ss_cap = 0;
+ if (!(child_l1ss_cap & PCI_L1SS_CAP_L1_PM_SS))
+ child_l1ss_cap = 0;
+
+ if (parent_l1ss_cap && child_l1ss_cap)
+ return true;
+ else
+ return false;
+}
+EXPORT_SYMBOL(pcie_aspm_ext_is_rc_ep_l1ss_capable);
+
+void pcie_aspm_ext_l1ss_enable(struct pci_dev *child, struct pci_dev *parent, bool enable)
+{
+ bool ret;
+
+ /* Disable ASPM of RC and EP */
+ ret = rockchip_pcie_bus_aspm_enable_rc_ep(child, parent, false);
+
+ if (enable) {
+ /* Enable RC then EP */
+ aspm_calc_l1ss_info(child, parent);
+ rockchip_pcie_bus_l1ss_enable_dev("RC", parent, enable);
+ rockchip_pcie_bus_l1ss_enable_dev("EP", child, enable);
+ } else {
+ /* Disable EP then RC */
+ rockchip_pcie_bus_l1ss_enable_dev("EP", child, enable);
+ rockchip_pcie_bus_l1ss_enable_dev("RC", parent, enable);
+ }
+
+ /* Enable ASPM of RC and EP only if this API disabled */
+ if (ret)
+ rockchip_pcie_bus_aspm_enable_rc_ep(child, parent, true);
+}
+EXPORT_SYMBOL(pcie_aspm_ext_l1ss_enable);
--- /dev/null
+++ b/include/linux/aspm_ext.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/* Copyright (c) 2022 Rockchip Electronics Co., Ltd. */
+
+#ifndef _ASPM_EXT_H
+#define _ASPM_EXT_H
+
+#if IS_REACHABLE(CONFIG_PCIEASPM_EXT)
+bool pcie_aspm_ext_is_rc_ep_l1ss_capable(struct pci_dev *child, struct pci_dev *parent);
+void pcie_aspm_ext_l1ss_enable(struct pci_dev *child, struct pci_dev *parent, bool enable);
+#else
+static inline bool pcie_aspm_ext_is_rc_ep_l1ss_capable(struct pci_dev *child, struct pci_dev *parent) { return false; }
+static inline void pcie_aspm_ext_l1ss_enable(struct pci_dev *child, struct pci_dev *parent, bool enable) {}
+#endif
+
+#endif

View File

@ -0,0 +1,24 @@
From a6c71606de486944c5fb028f47604fb22292312b Mon Sep 17 00:00:00 2001
From: Jon Lin <jon.lin@rock-chips.com>
Date: Fri, 24 Jun 2022 21:32:11 +0800
Subject: [PATCH] PCI: aspm_ext: Re-enable LRT for L1SS after power loss
Change-Id: Iedb72ee74660a8f11f38895e06766c3b77728ba3
Signed-off-by: Jon Lin <jon.lin@rock-chips.com>
---
drivers/pci/pcie/aspm_ext.c | 4 ++++
1 file changed, 4 insertions(+)
--- a/drivers/pci/pcie/aspm_ext.c
+++ b/drivers/pci/pcie/aspm_ext.c
@@ -322,6 +322,10 @@ void pcie_aspm_ext_l1ss_enable(struct pc
ret = rockchip_pcie_bus_aspm_enable_rc_ep(child, parent, false);
if (enable) {
+ /* LRT enable bits loss after wifi off, enable it after power on */
+ if (parent->ltr_path)
+ pcie_capability_set_word(parent, PCI_EXP_DEVCTL2, PCI_EXP_DEVCTL2_LTR_EN);
+
/* Enable RC then EP */
aspm_calc_l1ss_info(child, parent);
rockchip_pcie_bus_l1ss_enable_dev("RC", parent, enable);

View File

@ -0,0 +1,38 @@
From cf03561e5ec9f2f8b41a992f3b8ca19b9c3b9e47 Mon Sep 17 00:00:00 2001
From: Tao Huang <huangtao@rock-chips.com>
Date: Fri, 15 Jul 2022 20:56:15 +0800
Subject: [PATCH] PCI: aspm_ext: Fix Add missing MODULE_LICENSE()
ERROR: modpost: missing MODULE_LICENSE() in drivers/pci/pcie/aspm_ext.o
Signed-off-by: Tao Huang <huangtao@rock-chips.com>
Change-Id: Id365aba7a73f02cc2c61882b46937250e64af01c
---
drivers/pci/pcie/aspm_ext.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
--- a/drivers/pci/pcie/aspm_ext.c
+++ b/drivers/pci/pcie/aspm_ext.c
@@ -6,6 +6,7 @@
*/
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/pci.h>
#include <linux/aspm_ext.h>
#include <linux/errno.h>
@@ -95,7 +96,7 @@ static bool rockchip_pcie_bus_aspm_enabl
return true;
}
-bool rockchip_pcie_bus_aspm_enable_rc_ep(struct pci_dev *child, struct pci_dev *parent, bool enable)
+static bool rockchip_pcie_bus_aspm_enable_rc_ep(struct pci_dev *child, struct pci_dev *parent, bool enable)
{
bool ret;
@@ -341,3 +342,5 @@ void pcie_aspm_ext_l1ss_enable(struct pc
rockchip_pcie_bus_aspm_enable_rc_ep(child, parent, true);
}
EXPORT_SYMBOL(pcie_aspm_ext_l1ss_enable);
+
+MODULE_LICENSE("GPL");