From f0e7802be643bff7a89a39ab16744400406b2ec2 Mon Sep 17 00:00:00 2001 From: sbwml <984419930@qq.com> Date: Wed, 22 Feb 2023 08:42:46 +0800 Subject: [PATCH] rockchip: Add ROCKCHIP PCIe ASPM interface --- armv8/config-6.1 | 3 +- armv8/config-6.2 | 5 +- ...ip-fix-spdif-fe460000-ordering-on-rk.patch | 59 +++ ...rockchip-RK356x-Add-I2S2-device-node.patch | 45 ++ ...PCI-Add-ROCKCHIP-PCIe-ASPM-interface.patch | 402 ++++++++++++++++++ ...enable-LRT-for-L1SS-after-power-loss.patch | 24 ++ ...m_ext-Fix-Add-missing-MODULE_LICENSE.patch | 38 ++ ...mshc-Update-DLL-and-pre-change-delay.patch | 58 +++ ...ci-of-dwcmshc-Add-runtime-PM-support.patch | 93 ++++ ...mshc-Add-host-software-queue-support.patch | 90 ++++ ...PCI-Add-ROCKCHIP-PCIe-ASPM-interface.patch | 402 ++++++++++++++++++ ...enable-LRT-for-L1SS-after-power-loss.patch | 24 ++ ...m_ext-Fix-Add-missing-MODULE_LICENSE.patch | 38 ++ 13 files changed, 1278 insertions(+), 3 deletions(-) create mode 100644 patches-6.1/212-arm64-dts-rockchip-fix-spdif-fe460000-ordering-on-rk.patch create mode 100644 patches-6.1/213-arm64-dts-rockchip-RK356x-Add-I2S2-device-node.patch create mode 100644 patches-6.1/220-PCI-Add-ROCKCHIP-PCIe-ASPM-interface.patch create mode 100644 patches-6.1/221-PCI-aspm_ext-Re-enable-LRT-for-L1SS-after-power-loss.patch create mode 100644 patches-6.1/222-PCI-aspm_ext-Fix-Add-missing-MODULE_LICENSE.patch create mode 100644 patches-6.1/225-1-mmc-sdhci-of-dwcmshc-Update-DLL-and-pre-change-delay.patch create mode 100644 patches-6.1/225-2-mmc-sdhci-of-dwcmshc-Add-runtime-PM-support.patch create mode 100644 patches-6.1/225-3-mmc-sdhci-of-dwcmshc-Add-host-software-queue-support.patch create mode 100644 patches-6.2/220-PCI-Add-ROCKCHIP-PCIe-ASPM-interface.patch create mode 100644 patches-6.2/221-PCI-aspm_ext-Re-enable-LRT-for-L1SS-after-power-loss.patch create mode 100644 patches-6.2/222-PCI-aspm_ext-Fix-Add-missing-MODULE_LICENSE.patch diff --git a/armv8/config-6.1 b/armv8/config-6.1 index df4cec7..aa44e82 100644 --- a/armv8/config-6.1 +++ b/armv8/config-6.1 @@ -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 diff --git a/armv8/config-6.2 b/armv8/config-6.2 index 88993a1..cd298bd 100644 --- a/armv8/config-6.2 +++ b/armv8/config-6.2 @@ -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 diff --git a/patches-6.1/212-arm64-dts-rockchip-fix-spdif-fe460000-ordering-on-rk.patch b/patches-6.1/212-arm64-dts-rockchip-fix-spdif-fe460000-ordering-on-rk.patch new file mode 100644 index 0000000..349275f --- /dev/null +++ b/patches-6.1/212-arm64-dts-rockchip-fix-spdif-fe460000-ordering-on-rk.patch @@ -0,0 +1,59 @@ +From 59e0ec5e5916ed4fac238a3da39aa0659831c41c Mon Sep 17 00:00:00 2001 +From: Heiko Stuebner +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 +--- + 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 = ; +- 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 = ; ++ 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"; diff --git a/patches-6.1/213-arm64-dts-rockchip-RK356x-Add-I2S2-device-node.patch b/patches-6.1/213-arm64-dts-rockchip-RK356x-Add-I2S2-device-node.patch new file mode 100644 index 0000000..a8a4378 --- /dev/null +++ b/patches-6.1/213-arm64-dts-rockchip-RK356x-Add-I2S2-device-node.patch @@ -0,0 +1,45 @@ +From 6c51234cd4e1bfd637c3aab0a94893e832670fe5 Mon Sep 17 00:00:00 2001 +From: Shengyu Qu +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 +Link: https://lore.kernel.org/r/OS3P286MB259771C12F2B15A4DDF435FE98359@OS3P286MB2597.JPNP286.PROD.OUTLOOK.COM +Signed-off-by: Heiko Stuebner +--- + 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 = ; ++ 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>; diff --git a/patches-6.1/220-PCI-Add-ROCKCHIP-PCIe-ASPM-interface.patch b/patches-6.1/220-PCI-Add-ROCKCHIP-PCIe-ASPM-interface.patch new file mode 100644 index 0000000..62e84a5 --- /dev/null +++ b/patches-6.1/220-PCI-Add-ROCKCHIP-PCIe-ASPM-interface.patch @@ -0,0 +1,402 @@ +From d591c3f6efef5f50fc970aeeedbf9e03b7bd5d21 Mon Sep 17 00:00:00 2001 +From: Jon Lin +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 +--- + 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 ++#include ++#include ++#include ++ ++ ++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 diff --git a/patches-6.1/221-PCI-aspm_ext-Re-enable-LRT-for-L1SS-after-power-loss.patch b/patches-6.1/221-PCI-aspm_ext-Re-enable-LRT-for-L1SS-after-power-loss.patch new file mode 100644 index 0000000..990bf19 --- /dev/null +++ b/patches-6.1/221-PCI-aspm_ext-Re-enable-LRT-for-L1SS-after-power-loss.patch @@ -0,0 +1,24 @@ +From a6c71606de486944c5fb028f47604fb22292312b Mon Sep 17 00:00:00 2001 +From: Jon Lin +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 +--- + 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); diff --git a/patches-6.1/222-PCI-aspm_ext-Fix-Add-missing-MODULE_LICENSE.patch b/patches-6.1/222-PCI-aspm_ext-Fix-Add-missing-MODULE_LICENSE.patch new file mode 100644 index 0000000..3bf9e9a --- /dev/null +++ b/patches-6.1/222-PCI-aspm_ext-Fix-Add-missing-MODULE_LICENSE.patch @@ -0,0 +1,38 @@ +From cf03561e5ec9f2f8b41a992f3b8ca19b9c3b9e47 Mon Sep 17 00:00:00 2001 +From: Tao Huang +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 +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 ++#include + #include + #include + #include +@@ -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"); diff --git a/patches-6.1/225-1-mmc-sdhci-of-dwcmshc-Update-DLL-and-pre-change-delay.patch b/patches-6.1/225-1-mmc-sdhci-of-dwcmshc-Update-DLL-and-pre-change-delay.patch new file mode 100644 index 0000000..eb129b0 --- /dev/null +++ b/patches-6.1/225-1-mmc-sdhci-of-dwcmshc-Update-DLL-and-pre-change-delay.patch @@ -0,0 +1,58 @@ +From 6e57e8b78939b4d849511fa96467d09d77ce8a26 Mon Sep 17 00:00:00 2001 +From: Shawn Lin +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 +--- + 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); + diff --git a/patches-6.1/225-2-mmc-sdhci-of-dwcmshc-Add-runtime-PM-support.patch b/patches-6.1/225-2-mmc-sdhci-of-dwcmshc-Add-runtime-PM-support.patch new file mode 100644 index 0000000..7782a11 --- /dev/null +++ b/patches-6.1/225-2-mmc-sdhci-of-dwcmshc-Add-runtime-PM-support.patch @@ -0,0 +1,93 @@ +From ce8180045daea68c45ba5bd2495885410b7b9dc8 Mon Sep 17 00:00:00 2001 +From: Shawn Lin +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 +--- + 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 + #include + #include ++#include + #include + #include + +@@ -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 = { diff --git a/patches-6.1/225-3-mmc-sdhci-of-dwcmshc-Add-host-software-queue-support.patch b/patches-6.1/225-3-mmc-sdhci-of-dwcmshc-Add-host-software-queue-support.patch new file mode 100644 index 0000000..8a56059 --- /dev/null +++ b/patches-6.1/225-3-mmc-sdhci-of-dwcmshc-Add-host-software-queue-support.patch @@ -0,0 +1,90 @@ +From 55c88b9f9ed2ba17fe5a4b095fd0fbd9c38b1c4b Mon Sep 17 00:00:00 2001 +From: Shawn Lin +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 +--- + 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 + + #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 + diff --git a/patches-6.2/220-PCI-Add-ROCKCHIP-PCIe-ASPM-interface.patch b/patches-6.2/220-PCI-Add-ROCKCHIP-PCIe-ASPM-interface.patch new file mode 100644 index 0000000..2a471cd --- /dev/null +++ b/patches-6.2/220-PCI-Add-ROCKCHIP-PCIe-ASPM-interface.patch @@ -0,0 +1,402 @@ +From d591c3f6efef5f50fc970aeeedbf9e03b7bd5d21 Mon Sep 17 00:00:00 2001 +From: Jon Lin +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 +--- + 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 ++#include ++#include ++#include ++ ++ ++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 diff --git a/patches-6.2/221-PCI-aspm_ext-Re-enable-LRT-for-L1SS-after-power-loss.patch b/patches-6.2/221-PCI-aspm_ext-Re-enable-LRT-for-L1SS-after-power-loss.patch new file mode 100644 index 0000000..990bf19 --- /dev/null +++ b/patches-6.2/221-PCI-aspm_ext-Re-enable-LRT-for-L1SS-after-power-loss.patch @@ -0,0 +1,24 @@ +From a6c71606de486944c5fb028f47604fb22292312b Mon Sep 17 00:00:00 2001 +From: Jon Lin +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 +--- + 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); diff --git a/patches-6.2/222-PCI-aspm_ext-Fix-Add-missing-MODULE_LICENSE.patch b/patches-6.2/222-PCI-aspm_ext-Fix-Add-missing-MODULE_LICENSE.patch new file mode 100644 index 0000000..3bf9e9a --- /dev/null +++ b/patches-6.2/222-PCI-aspm_ext-Fix-Add-missing-MODULE_LICENSE.patch @@ -0,0 +1,38 @@ +From cf03561e5ec9f2f8b41a992f3b8ca19b9c3b9e47 Mon Sep 17 00:00:00 2001 +From: Tao Huang +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 +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 ++#include + #include + #include + #include +@@ -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");