1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 /* Copyright(c) 2018-2019 Realtek Corporation 3 */ 4 5 #include "main.h" 6 #include "fw.h" 7 #include "ps.h" 8 #include "mac.h" 9 #include "coex.h" 10 #include "debug.h" 11 12 static int rtw_ips_pwr_up(struct rtw_dev *rtwdev) 13 { 14 int ret; 15 16 ret = rtw_core_start(rtwdev); 17 if (ret) 18 rtw_err(rtwdev, "leave idle state failed\n"); 19 20 rtw_set_channel(rtwdev); 21 rtw_flag_clear(rtwdev, RTW_FLAG_INACTIVE_PS); 22 23 return ret; 24 } 25 26 int rtw_enter_ips(struct rtw_dev *rtwdev) 27 { 28 rtw_flag_set(rtwdev, RTW_FLAG_INACTIVE_PS); 29 30 rtw_coex_ips_notify(rtwdev, COEX_IPS_ENTER); 31 32 rtw_core_stop(rtwdev); 33 34 return 0; 35 } 36 37 static void rtw_restore_port_cfg_iter(void *data, u8 *mac, 38 struct ieee80211_vif *vif) 39 { 40 struct rtw_dev *rtwdev = data; 41 struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv; 42 u32 config = ~0; 43 44 rtw_vif_port_config(rtwdev, rtwvif, config); 45 } 46 47 int rtw_leave_ips(struct rtw_dev *rtwdev) 48 { 49 int ret; 50 51 ret = rtw_ips_pwr_up(rtwdev); 52 if (ret) { 53 rtw_err(rtwdev, "failed to leave ips state\n"); 54 return ret; 55 } 56 57 rtw_iterate_vifs_atomic(rtwdev, rtw_restore_port_cfg_iter, rtwdev); 58 59 rtw_coex_ips_notify(rtwdev, COEX_IPS_LEAVE); 60 61 return 0; 62 } 63 64 static void rtw_leave_lps_core(struct rtw_dev *rtwdev) 65 { 66 struct rtw_lps_conf *conf = &rtwdev->lps_conf; 67 68 conf->state = RTW_ALL_ON; 69 conf->awake_interval = 1; 70 conf->rlbm = 0; 71 conf->smart_ps = 0; 72 73 rtw_fw_set_pwr_mode(rtwdev); 74 rtw_flag_clear(rtwdev, RTW_FLAG_LEISURE_PS); 75 76 rtw_coex_lps_notify(rtwdev, COEX_LPS_DISABLE); 77 } 78 79 static void rtw_enter_lps_core(struct rtw_dev *rtwdev) 80 { 81 struct rtw_lps_conf *conf = &rtwdev->lps_conf; 82 83 conf->state = RTW_RF_OFF; 84 conf->awake_interval = 1; 85 conf->rlbm = 1; 86 conf->smart_ps = 2; 87 88 rtw_coex_lps_notify(rtwdev, COEX_LPS_ENABLE); 89 90 rtw_fw_set_pwr_mode(rtwdev); 91 rtw_flag_set(rtwdev, RTW_FLAG_LEISURE_PS); 92 } 93 94 void rtw_lps_work(struct work_struct *work) 95 { 96 struct rtw_dev *rtwdev = container_of(work, struct rtw_dev, 97 lps_work.work); 98 struct rtw_lps_conf *conf = &rtwdev->lps_conf; 99 struct rtw_vif *rtwvif = conf->rtwvif; 100 101 if (WARN_ON(!rtwvif)) 102 return; 103 104 if (conf->mode == RTW_MODE_LPS) 105 rtw_enter_lps_core(rtwdev); 106 else 107 rtw_leave_lps_core(rtwdev); 108 } 109 110 void rtw_enter_lps_irqsafe(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif) 111 { 112 struct rtw_lps_conf *conf = &rtwdev->lps_conf; 113 114 if (rtwvif->in_lps) 115 return; 116 117 conf->mode = RTW_MODE_LPS; 118 conf->rtwvif = rtwvif; 119 rtwvif->in_lps = true; 120 121 ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->lps_work, 0); 122 } 123 124 void rtw_leave_lps_irqsafe(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif) 125 { 126 struct rtw_lps_conf *conf = &rtwdev->lps_conf; 127 128 if (!rtwvif->in_lps) 129 return; 130 131 conf->mode = RTW_MODE_ACTIVE; 132 conf->rtwvif = rtwvif; 133 rtwvif->in_lps = false; 134 135 ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->lps_work, 0); 136 } 137 138 bool rtw_in_lps(struct rtw_dev *rtwdev) 139 { 140 return rtw_flag_check(rtwdev, RTW_FLAG_LEISURE_PS); 141 } 142 143 void rtw_enter_lps(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif) 144 { 145 struct rtw_lps_conf *conf = &rtwdev->lps_conf; 146 147 if (WARN_ON(!rtwvif)) 148 return; 149 150 if (rtwvif->in_lps) 151 return; 152 153 conf->mode = RTW_MODE_LPS; 154 conf->rtwvif = rtwvif; 155 rtwvif->in_lps = true; 156 157 rtw_enter_lps_core(rtwdev); 158 } 159 160 void rtw_leave_lps(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif) 161 { 162 struct rtw_lps_conf *conf = &rtwdev->lps_conf; 163 164 if (WARN_ON(!rtwvif)) 165 return; 166 167 if (!rtwvif->in_lps) 168 return; 169 170 conf->mode = RTW_MODE_ACTIVE; 171 conf->rtwvif = rtwvif; 172 rtwvif->in_lps = false; 173 174 rtw_leave_lps_core(rtwdev); 175 } 176