ps.c 4.6 KB
Newer Older
1 2 3 4 5
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/* Copyright(c) 2018-2019  Realtek Corporation
 */

#include "main.h"
6
#include "reg.h"
7 8 9
#include "fw.h"
#include "ps.h"
#include "mac.h"
10
#include "coex.h"
11 12 13 14 15 16 17 18 19 20 21
#include "debug.h"

static int rtw_ips_pwr_up(struct rtw_dev *rtwdev)
{
	int ret;

	ret = rtw_core_start(rtwdev);
	if (ret)
		rtw_err(rtwdev, "leave idle state failed\n");

	rtw_set_channel(rtwdev);
22
	clear_bit(RTW_FLAG_INACTIVE_PS, rtwdev->flags);
23 24 25 26 27 28

	return ret;
}

int rtw_enter_ips(struct rtw_dev *rtwdev)
{
29
	set_bit(RTW_FLAG_INACTIVE_PS, rtwdev->flags);
30

31 32
	rtw_coex_ips_notify(rtwdev, COEX_IPS_ENTER);

33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
	rtw_core_stop(rtwdev);

	return 0;
}

static void rtw_restore_port_cfg_iter(void *data, u8 *mac,
				      struct ieee80211_vif *vif)
{
	struct rtw_dev *rtwdev = data;
	struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv;
	u32 config = ~0;

	rtw_vif_port_config(rtwdev, rtwvif, config);
}

int rtw_leave_ips(struct rtw_dev *rtwdev)
{
	int ret;

	ret = rtw_ips_pwr_up(rtwdev);
	if (ret) {
		rtw_err(rtwdev, "failed to leave ips state\n");
		return ret;
	}

	rtw_iterate_vifs_atomic(rtwdev, rtw_restore_port_cfg_iter, rtwdev);

60 61
	rtw_coex_ips_notify(rtwdev, COEX_IPS_LEAVE);

62 63 64
	return 0;
}

65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
void rtw_power_mode_change(struct rtw_dev *rtwdev, bool enter)
{
	u8 request, confirm, polling;
	u8 polling_cnt;
	u8 retry_cnt = 0;

retry:
	request = rtw_read8(rtwdev, rtwdev->hci.rpwm_addr);
	confirm = rtw_read8(rtwdev, rtwdev->hci.cpwm_addr);

	/* toggle to request power mode, others remain 0 */
	request ^= request | BIT_RPWM_TOGGLE;
	if (!enter)
		request |= POWER_MODE_ACK;
	else
		request |= POWER_MODE_LCLK;

	rtw_write8(rtwdev, rtwdev->hci.rpwm_addr, request);

	/* check confirm power mode has left power save state */
	if (!enter) {
		for (polling_cnt = 0; polling_cnt < 3; polling_cnt++) {
			polling = rtw_read8(rtwdev, rtwdev->hci.cpwm_addr);
			if ((polling ^ confirm) & BIT_RPWM_TOGGLE)
				return;
			mdelay(20);
		}

		/* in case of fw/hw missed the request, retry 3 times */
		if (retry_cnt < 3) {
			rtw_warn(rtwdev, "failed to leave deep PS, retry=%d\n",
				 retry_cnt);
			retry_cnt++;
			goto retry;
		}

		/* Hit here means that driver failed to change hardware
		 * power mode to active state after retry 3 times.
		 * If the power state is locked at Deep sleep, most of
		 * the hardware circuits is not working, even register
		 * read/write. It should be treated as fatal error and
		 * requires an entire analysis about the firmware/hardware
		 */
		WARN_ON("Hardware power state locked\n");
	}
}
EXPORT_SYMBOL(rtw_power_mode_change);

static void __rtw_leave_lps_deep(struct rtw_dev *rtwdev)
{
	rtw_hci_deep_ps(rtwdev, false);
}

118 119 120 121 122 123 124 125 126 127
static void rtw_leave_lps_core(struct rtw_dev *rtwdev)
{
	struct rtw_lps_conf *conf = &rtwdev->lps_conf;

	conf->state = RTW_ALL_ON;
	conf->awake_interval = 1;
	conf->rlbm = 0;
	conf->smart_ps = 0;

	rtw_fw_set_pwr_mode(rtwdev);
128
	clear_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags);
129 130

	rtw_coex_lps_notify(rtwdev, COEX_LPS_DISABLE);
131 132
}

133 134 135 136 137 138 139 140 141 142 143
static void __rtw_enter_lps_deep(struct rtw_dev *rtwdev)
{
	if (!test_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags)) {
		rtw_dbg(rtwdev, RTW_DBG_PS,
			"Should enter LPS before entering deep PS\n");
		return;
	}

	rtw_hci_deep_ps(rtwdev, true);
}

144 145 146 147 148 149 150 151 152
static void rtw_enter_lps_core(struct rtw_dev *rtwdev)
{
	struct rtw_lps_conf *conf = &rtwdev->lps_conf;

	conf->state = RTW_RF_OFF;
	conf->awake_interval = 1;
	conf->rlbm = 1;
	conf->smart_ps = 2;

153 154
	rtw_coex_lps_notify(rtwdev, COEX_LPS_ENABLE);

155
	rtw_fw_set_pwr_mode(rtwdev);
156
	set_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags);
157 158
}

159
static void __rtw_enter_lps(struct rtw_dev *rtwdev, u8 port_id)
160 161 162
{
	struct rtw_lps_conf *conf = &rtwdev->lps_conf;

163
	if (test_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags))
164 165 166
		return;

	conf->mode = RTW_MODE_LPS;
167
	conf->port_id = port_id;
168 169 170 171

	rtw_enter_lps_core(rtwdev);
}

172
static void __rtw_leave_lps(struct rtw_dev *rtwdev)
173 174 175
{
	struct rtw_lps_conf *conf = &rtwdev->lps_conf;

176 177 178 179 180
	if (test_and_clear_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags)) {
		rtw_dbg(rtwdev, RTW_DBG_PS,
			"Should leave deep PS before leaving LPS\n");
		__rtw_leave_lps_deep(rtwdev);
	}
181

182
	if (!test_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags))
183 184 185 186 187 188
		return;

	conf->mode = RTW_MODE_ACTIVE;

	rtw_leave_lps_core(rtwdev);
}
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211

void rtw_enter_lps(struct rtw_dev *rtwdev, u8 port_id)
{
	lockdep_assert_held(&rtwdev->mutex);

	__rtw_enter_lps(rtwdev, port_id);
	__rtw_enter_lps_deep(rtwdev);
}

void rtw_leave_lps(struct rtw_dev *rtwdev)
{
	lockdep_assert_held(&rtwdev->mutex);

	__rtw_leave_lps_deep(rtwdev);
	__rtw_leave_lps(rtwdev);
}

void rtw_leave_lps_deep(struct rtw_dev *rtwdev)
{
	lockdep_assert_held(&rtwdev->mutex);

	__rtw_leave_lps_deep(rtwdev);
}