brintos

brintos / linux-shallow public Read only

0
0
Text · 7.9 KiB · add5a20 Raw
344 lines · c
1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause2/* Copyright(c) 2018-2019  Realtek Corporation3 */4 5#include "main.h"6#include "reg.h"7#include "fw.h"8#include "ps.h"9#include "mac.h"10#include "coex.h"11#include "debug.h"12 13static int rtw_ips_pwr_up(struct rtw_dev *rtwdev)14{15	int ret;16 17	ret = rtw_core_start(rtwdev);18	if (ret)19		rtw_err(rtwdev, "leave idle state failed\n");20 21	rtw_coex_ips_notify(rtwdev, COEX_IPS_LEAVE);22	rtw_set_channel(rtwdev);23 24	return ret;25}26 27int rtw_enter_ips(struct rtw_dev *rtwdev)28{29	if (!test_bit(RTW_FLAG_POWERON, rtwdev->flags))30		return 0;31 32	rtw_coex_ips_notify(rtwdev, COEX_IPS_ENTER);33 34	rtw_core_stop(rtwdev);35	rtw_hci_link_ps(rtwdev, true);36 37	return 0;38}39 40static void rtw_restore_port_cfg_iter(void *data, struct ieee80211_vif *vif)41{42	struct rtw_dev *rtwdev = data;43	struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv;44	u32 config = ~0;45 46	rtw_vif_port_config(rtwdev, rtwvif, config);47}48 49int rtw_leave_ips(struct rtw_dev *rtwdev)50{51	int ret;52 53	if (test_bit(RTW_FLAG_POWERON, rtwdev->flags))54		return 0;55 56	rtw_hci_link_ps(rtwdev, false);57 58	ret = rtw_ips_pwr_up(rtwdev);59	if (ret) {60		rtw_err(rtwdev, "failed to leave ips state\n");61		return ret;62	}63 64	rtw_iterate_vifs(rtwdev, rtw_restore_port_cfg_iter, rtwdev);65 66	return 0;67}68 69void rtw_power_mode_change(struct rtw_dev *rtwdev, bool enter)70{71	u8 request, confirm, polling;72	int ret;73 74	request = rtw_read8(rtwdev, rtwdev->hci.rpwm_addr);75	confirm = rtw_read8(rtwdev, rtwdev->hci.cpwm_addr);76 77	/* toggle to request power mode, others remain 0 */78	request ^= request | BIT_RPWM_TOGGLE;79	if (enter) {80		request |= POWER_MODE_LCLK;81		if (rtw_get_lps_deep_mode(rtwdev) == LPS_DEEP_MODE_PG)82			request |= POWER_MODE_PG;83	}84	/* Each request require an ack from firmware */85	request |= POWER_MODE_ACK;86 87	if (rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_TX_WAKE))88		request |= POWER_TX_WAKE;89 90	rtw_write8(rtwdev, rtwdev->hci.rpwm_addr, request);91 92	/* Check firmware get the power requset and ack via cpwm register */93	ret = read_poll_timeout_atomic(rtw_read8, polling,94				       (polling ^ confirm) & BIT_RPWM_TOGGLE,95				       100, 15000, true, rtwdev,96				       rtwdev->hci.cpwm_addr);97	if (ret) {98		/* Hit here means that driver failed to get an ack from firmware.99		 * The reason could be that hardware is locked at Deep sleep,100		 * so most of the hardware circuits are not working, even101		 * register read/write; or firmware is locked in some state and102		 * cannot get the request. It should be treated as fatal error103		 * and requires an entire analysis about the firmware/hardware.104		 */105		WARN(1, "firmware failed to ack driver for %s Deep Power mode\n",106		     enter ? "entering" : "leaving");107		rtw_fw_dump_dbg_info(rtwdev);108	}109}110EXPORT_SYMBOL(rtw_power_mode_change);111 112static void __rtw_leave_lps_deep(struct rtw_dev *rtwdev)113{114	rtw_hci_deep_ps(rtwdev, false);115}116 117static int __rtw_fw_leave_lps_check_reg(struct rtw_dev *rtwdev)118{119	int i;120 121	/* Driver needs to wait for firmware to leave LPS state122	 * successfully. Firmware will send null packet to inform AP,123	 * and see if AP sends an ACK back, then firmware will restore124	 * the REG_TCR register.125	 *126	 * If driver does not wait for firmware, null packet with127	 * PS bit could be sent due to incorrect REG_TCR setting.128	 *129	 * In our test, 100ms should be enough for firmware to finish130	 * the flow. If REG_TCR Register is still incorrect after 100ms,131	 * just modify it directly, and throw a warn message.132	 */133	for (i = 0 ; i < LEAVE_LPS_TRY_CNT; i++) {134		if (rtw_read32_mask(rtwdev, REG_TCR, BIT_PWRMGT_HWDATA_EN) == 0)135			return 0;136		msleep(20);137	}138 139	return -EBUSY;140}141 142static  int __rtw_fw_leave_lps_check_c2h(struct rtw_dev *rtwdev)143{144	if (wait_for_completion_timeout(&rtwdev->lps_leave_check,145					LEAVE_LPS_TIMEOUT))146		return 0;147	return -EBUSY;148}149 150static void rtw_fw_leave_lps_check(struct rtw_dev *rtwdev)151{152	bool ret = false;153	struct rtw_fw_state *fw;154 155	if (test_bit(RTW_FLAG_WOWLAN, rtwdev->flags))156		fw = &rtwdev->wow_fw;157	else158		fw = &rtwdev->fw;159 160	if (rtw_fw_feature_check(fw, FW_FEATURE_LPS_C2H))161		ret = __rtw_fw_leave_lps_check_c2h(rtwdev);162	else163		ret = __rtw_fw_leave_lps_check_reg(rtwdev);164 165	if (ret) {166		rtw_write32_clr(rtwdev, REG_TCR, BIT_PWRMGT_HWDATA_EN);167		rtw_warn(rtwdev, "firmware failed to leave lps state\n");168		rtw_fw_dump_dbg_info(rtwdev);169	}170}171 172static void rtw_fw_leave_lps_check_prepare(struct rtw_dev *rtwdev)173{174	struct rtw_fw_state *fw;175 176	if (test_bit(RTW_FLAG_WOWLAN, rtwdev->flags))177		fw = &rtwdev->wow_fw;178	else179		fw = &rtwdev->fw;180 181	if (rtw_fw_feature_check(fw, FW_FEATURE_LPS_C2H))182		reinit_completion(&rtwdev->lps_leave_check);183}184 185static void rtw_leave_lps_core(struct rtw_dev *rtwdev)186{187	struct rtw_lps_conf *conf = &rtwdev->lps_conf;188 189	conf->state = RTW_ALL_ON;190	conf->awake_interval = 1;191	conf->rlbm = 0;192	conf->smart_ps = 0;193 194	rtw_hci_link_ps(rtwdev, false);195	rtw_fw_leave_lps_check_prepare(rtwdev);196	rtw_fw_set_pwr_mode(rtwdev);197	rtw_fw_leave_lps_check(rtwdev);198 199	clear_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags);200 201	rtw_coex_lps_notify(rtwdev, COEX_LPS_DISABLE);202}203 204enum rtw_lps_deep_mode rtw_get_lps_deep_mode(struct rtw_dev *rtwdev)205{206	if (test_bit(RTW_FLAG_WOWLAN, rtwdev->flags))207		return rtwdev->lps_conf.wow_deep_mode;208	else209		return rtwdev->lps_conf.deep_mode;210}211 212static void __rtw_enter_lps_deep(struct rtw_dev *rtwdev)213{214	if (rtw_get_lps_deep_mode(rtwdev) == LPS_DEEP_MODE_NONE)215		return;216 217	if (!test_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags)) {218		rtw_dbg(rtwdev, RTW_DBG_PS,219			"Should enter LPS before entering deep PS\n");220		return;221	}222 223	if (rtw_get_lps_deep_mode(rtwdev) == LPS_DEEP_MODE_PG)224		rtw_fw_set_pg_info(rtwdev);225 226	rtw_hci_deep_ps(rtwdev, true);227}228 229static void rtw_enter_lps_core(struct rtw_dev *rtwdev)230{231	struct rtw_lps_conf *conf = &rtwdev->lps_conf;232 233	conf->state = RTW_RF_OFF;234	conf->awake_interval = 1;235	conf->rlbm = 1;236	conf->smart_ps = 2;237 238	rtw_coex_lps_notify(rtwdev, COEX_LPS_ENABLE);239 240	rtw_fw_set_pwr_mode(rtwdev);241	rtw_hci_link_ps(rtwdev, true);242 243	set_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags);244}245 246static void __rtw_enter_lps(struct rtw_dev *rtwdev, u8 port_id)247{248	struct rtw_lps_conf *conf = &rtwdev->lps_conf;249 250	if (test_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags))251		return;252 253	conf->mode = RTW_MODE_LPS;254	conf->port_id = port_id;255 256	rtw_enter_lps_core(rtwdev);257}258 259static void __rtw_leave_lps(struct rtw_dev *rtwdev)260{261	struct rtw_lps_conf *conf = &rtwdev->lps_conf;262 263	if (test_and_clear_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags)) {264		rtw_dbg(rtwdev, RTW_DBG_PS,265			"Should leave deep PS before leaving LPS\n");266		__rtw_leave_lps_deep(rtwdev);267	}268 269	if (!test_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags))270		return;271 272	conf->mode = RTW_MODE_ACTIVE;273 274	rtw_leave_lps_core(rtwdev);275}276 277void rtw_enter_lps(struct rtw_dev *rtwdev, u8 port_id)278{279	lockdep_assert_held(&rtwdev->mutex);280 281	if (rtwdev->coex.stat.wl_force_lps_ctrl)282		return;283 284	__rtw_enter_lps(rtwdev, port_id);285	__rtw_enter_lps_deep(rtwdev);286}287 288void rtw_leave_lps(struct rtw_dev *rtwdev)289{290	lockdep_assert_held(&rtwdev->mutex);291 292	__rtw_leave_lps_deep(rtwdev);293	__rtw_leave_lps(rtwdev);294}295 296void rtw_leave_lps_deep(struct rtw_dev *rtwdev)297{298	lockdep_assert_held(&rtwdev->mutex);299 300	__rtw_leave_lps_deep(rtwdev);301}302 303struct rtw_vif_recalc_lps_iter_data {304	struct rtw_dev *rtwdev;305	struct ieee80211_vif *found_vif;306	int count;307};308 309static void __rtw_vif_recalc_lps(struct rtw_vif_recalc_lps_iter_data *data,310				 struct ieee80211_vif *vif)311{312	if (data->count < 0)313		return;314 315	if (vif->type != NL80211_IFTYPE_STATION) {316		data->count = -1;317		return;318	}319 320	data->count++;321	data->found_vif = vif;322}323 324static void rtw_vif_recalc_lps_iter(void *data, struct ieee80211_vif *vif)325{326	__rtw_vif_recalc_lps(data, vif);327}328 329void rtw_recalc_lps(struct rtw_dev *rtwdev, struct ieee80211_vif *new_vif)330{331	struct rtw_vif_recalc_lps_iter_data data = { .rtwdev = rtwdev };332 333	if (new_vif)334		__rtw_vif_recalc_lps(&data, new_vif);335	rtw_iterate_vifs(rtwdev, rtw_vif_recalc_lps_iter, &data);336 337	if (data.count == 1 && data.found_vif->cfg.ps) {338		rtwdev->ps_enabled = true;339	} else {340		rtwdev->ps_enabled = false;341		rtw_leave_lps(rtwdev);342	}343}344