@@ -14,6 +14,7 @@ struct rtw_hci_ops {
int (*start)(struct rtw_dev *rtwdev);
void (*stop)(struct rtw_dev *rtwdev);
void (*deep_ps)(struct rtw_dev *rtwdev, bool enter);
+ void (*link_ps)(struct rtw_dev *rtwdev, bool enter);
int (*write_data_rsvd_page)(struct rtw_dev *rtwdev, u8 *buf, u32 size);
int (*write_data_h2c)(struct rtw_dev *rtwdev, u8 *buf, u32 size);
@@ -53,6 +54,11 @@ static inline void rtw_hci_deep_ps(struct rtw_dev *rtwdev, bool enter)
rtwdev->hci.ops->deep_ps(rtwdev, enter);
}
+static inline void rtw_hci_link_ps(struct rtw_dev *rtwdev, bool enter)
+{
+ rtwdev->hci.ops->link_ps(rtwdev, enter);
+}
+
static inline int
rtw_hci_write_data_rsvd_page(struct rtw_dev *rtwdev, u8 *buf, u32 size)
{
@@ -1153,6 +1153,43 @@ static void rtw_pci_clkreq_set(struct rtw_dev *rtwdev, bool enable)
rtw_dbi_write8(rtwdev, RTK_PCIE_LINK_CFG, value);
}
+static void rtw_pci_aspm_set(struct rtw_dev *rtwdev, bool enable)
+{
+ u8 value;
+ int ret;
+
+ ret = rtw_dbi_read8(rtwdev, RTK_PCIE_LINK_CFG, &value);
+ if (ret) {
+ rtw_err(rtwdev, "failed to read ASPM, ret=%d", ret);
+ return;
+ }
+
+ if (enable)
+ value |= BIT_L1_SW_EN;
+ else
+ value &= ~BIT_L1_SW_EN;
+
+ rtw_dbi_write8(rtwdev, RTK_PCIE_LINK_CFG, value);
+}
+
+static void rtw_pci_link_ps(struct rtw_dev *rtwdev, bool enter)
+{
+ struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv;
+
+ /* Like CLKREQ, ASPM is also implemented by two HW modules, and can
+ * only be enabled when host supports it.
+ *
+ * And ASPM mechanism should be enabled when driver/firmware enters
+ * power save mode, without having heavy traffic. Because we've
+ * experienced some inter-operability issues that the link tends
+ * to enter L1 state on the fly even when driver is having high
+ * throughput. This is probably because the ASPM behavior slightly
+ * varies from different SOC.
+ */
+ if (rtwpci->link_ctrl & PCI_EXP_LNKCTL_ASPM_L1)
+ rtw_pci_aspm_set(rtwdev, enter);
+}
+
static void rtw_pci_link_cfg(struct rtw_dev *rtwdev)
{
struct rtw_pci *rtwpci = (struct rtw_pci *)rtwdev->priv;
@@ -1295,6 +1332,7 @@ static struct rtw_hci_ops rtw_pci_ops = {
.start = rtw_pci_start,
.stop = rtw_pci_stop,
.deep_ps = rtw_pci_deep_ps,
+ .link_ps = rtw_pci_link_ps,
.read8 = rtw_pci_read8,
.read16 = rtw_pci_read16,
@@ -38,6 +38,7 @@
#define RTK_PCIE_LINK_CFG 0x0719
#define BIT_CLKREQ_SW_EN BIT(4)
+#define BIT_L1_SW_EN BIT(3)
#define BIT_PCI_BCNQ_FLAG BIT(4)
#define RTK_PCI_TXBD_DESA_BCNQ 0x308
@@ -32,6 +32,7 @@ int rtw_enter_ips(struct rtw_dev *rtwdev)
rtw_coex_ips_notify(rtwdev, COEX_IPS_ENTER);
rtw_core_stop(rtwdev);
+ rtw_hci_link_ps(rtwdev, true);
return 0;
}
@@ -50,6 +51,8 @@ int rtw_leave_ips(struct rtw_dev *rtwdev)
{
int ret;
+ rtw_hci_link_ps(rtwdev, false);
+
ret = rtw_ips_pwr_up(rtwdev);
if (ret) {
rtw_err(rtwdev, "failed to leave ips state\n");
@@ -151,6 +154,7 @@ static void rtw_leave_lps_core(struct rtw_dev *rtwdev)
conf->rlbm = 0;
conf->smart_ps = 0;
+ rtw_hci_link_ps(rtwdev, false);
rtw_fw_set_pwr_mode(rtwdev);
rtw_fw_leave_lps_state_check(rtwdev);
@@ -188,6 +192,8 @@ static void rtw_enter_lps_core(struct rtw_dev *rtwdev)
rtw_coex_lps_notify(rtwdev, COEX_LPS_ENABLE);
rtw_fw_set_pwr_mode(rtwdev);
+ rtw_hci_link_ps(rtwdev, true);
+
set_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags);
}