162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
262306a36Sopenharmony_ci/* Copyright(c) 2020-2022  Realtek Corporation
362306a36Sopenharmony_ci */
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include "chan.h"
662306a36Sopenharmony_ci#include "debug.h"
762306a36Sopenharmony_ci#include "fw.h"
862306a36Sopenharmony_ci#include "ps.h"
962306a36Sopenharmony_ci#include "util.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_cistatic enum rtw89_subband rtw89_get_subband_type(enum rtw89_band band,
1262306a36Sopenharmony_ci						 u8 center_chan)
1362306a36Sopenharmony_ci{
1462306a36Sopenharmony_ci	switch (band) {
1562306a36Sopenharmony_ci	default:
1662306a36Sopenharmony_ci	case RTW89_BAND_2G:
1762306a36Sopenharmony_ci		switch (center_chan) {
1862306a36Sopenharmony_ci		default:
1962306a36Sopenharmony_ci		case 1 ... 14:
2062306a36Sopenharmony_ci			return RTW89_CH_2G;
2162306a36Sopenharmony_ci		}
2262306a36Sopenharmony_ci	case RTW89_BAND_5G:
2362306a36Sopenharmony_ci		switch (center_chan) {
2462306a36Sopenharmony_ci		default:
2562306a36Sopenharmony_ci		case 36 ... 64:
2662306a36Sopenharmony_ci			return RTW89_CH_5G_BAND_1;
2762306a36Sopenharmony_ci		case 100 ... 144:
2862306a36Sopenharmony_ci			return RTW89_CH_5G_BAND_3;
2962306a36Sopenharmony_ci		case 149 ... 177:
3062306a36Sopenharmony_ci			return RTW89_CH_5G_BAND_4;
3162306a36Sopenharmony_ci		}
3262306a36Sopenharmony_ci	case RTW89_BAND_6G:
3362306a36Sopenharmony_ci		switch (center_chan) {
3462306a36Sopenharmony_ci		default:
3562306a36Sopenharmony_ci		case 1 ... 29:
3662306a36Sopenharmony_ci			return RTW89_CH_6G_BAND_IDX0;
3762306a36Sopenharmony_ci		case 33 ... 61:
3862306a36Sopenharmony_ci			return RTW89_CH_6G_BAND_IDX1;
3962306a36Sopenharmony_ci		case 65 ... 93:
4062306a36Sopenharmony_ci			return RTW89_CH_6G_BAND_IDX2;
4162306a36Sopenharmony_ci		case 97 ... 125:
4262306a36Sopenharmony_ci			return RTW89_CH_6G_BAND_IDX3;
4362306a36Sopenharmony_ci		case 129 ... 157:
4462306a36Sopenharmony_ci			return RTW89_CH_6G_BAND_IDX4;
4562306a36Sopenharmony_ci		case 161 ... 189:
4662306a36Sopenharmony_ci			return RTW89_CH_6G_BAND_IDX5;
4762306a36Sopenharmony_ci		case 193 ... 221:
4862306a36Sopenharmony_ci			return RTW89_CH_6G_BAND_IDX6;
4962306a36Sopenharmony_ci		case 225 ... 253:
5062306a36Sopenharmony_ci			return RTW89_CH_6G_BAND_IDX7;
5162306a36Sopenharmony_ci		}
5262306a36Sopenharmony_ci	}
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic enum rtw89_sc_offset rtw89_get_primary_chan_idx(enum rtw89_bandwidth bw,
5662306a36Sopenharmony_ci						       u32 center_freq,
5762306a36Sopenharmony_ci						       u32 primary_freq)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	u8 primary_chan_idx;
6062306a36Sopenharmony_ci	u32 offset;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	switch (bw) {
6362306a36Sopenharmony_ci	default:
6462306a36Sopenharmony_ci	case RTW89_CHANNEL_WIDTH_20:
6562306a36Sopenharmony_ci		primary_chan_idx = RTW89_SC_DONT_CARE;
6662306a36Sopenharmony_ci		break;
6762306a36Sopenharmony_ci	case RTW89_CHANNEL_WIDTH_40:
6862306a36Sopenharmony_ci		if (primary_freq > center_freq)
6962306a36Sopenharmony_ci			primary_chan_idx = RTW89_SC_20_UPPER;
7062306a36Sopenharmony_ci		else
7162306a36Sopenharmony_ci			primary_chan_idx = RTW89_SC_20_LOWER;
7262306a36Sopenharmony_ci		break;
7362306a36Sopenharmony_ci	case RTW89_CHANNEL_WIDTH_80:
7462306a36Sopenharmony_ci	case RTW89_CHANNEL_WIDTH_160:
7562306a36Sopenharmony_ci		if (primary_freq > center_freq) {
7662306a36Sopenharmony_ci			offset = (primary_freq - center_freq - 10) / 20;
7762306a36Sopenharmony_ci			primary_chan_idx = RTW89_SC_20_UPPER + offset * 2;
7862306a36Sopenharmony_ci		} else {
7962306a36Sopenharmony_ci			offset = (center_freq - primary_freq - 10) / 20;
8062306a36Sopenharmony_ci			primary_chan_idx = RTW89_SC_20_LOWER + offset * 2;
8162306a36Sopenharmony_ci		}
8262306a36Sopenharmony_ci		break;
8362306a36Sopenharmony_ci	}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	return primary_chan_idx;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_civoid rtw89_chan_create(struct rtw89_chan *chan, u8 center_chan, u8 primary_chan,
8962306a36Sopenharmony_ci		       enum rtw89_band band, enum rtw89_bandwidth bandwidth)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	enum nl80211_band nl_band = rtw89_hw_to_nl80211_band(band);
9262306a36Sopenharmony_ci	u32 center_freq, primary_freq;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	memset(chan, 0, sizeof(*chan));
9562306a36Sopenharmony_ci	chan->channel = center_chan;
9662306a36Sopenharmony_ci	chan->primary_channel = primary_chan;
9762306a36Sopenharmony_ci	chan->band_type = band;
9862306a36Sopenharmony_ci	chan->band_width = bandwidth;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	center_freq = ieee80211_channel_to_frequency(center_chan, nl_band);
10162306a36Sopenharmony_ci	primary_freq = ieee80211_channel_to_frequency(primary_chan, nl_band);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	chan->freq = center_freq;
10462306a36Sopenharmony_ci	chan->subband_type = rtw89_get_subband_type(band, center_chan);
10562306a36Sopenharmony_ci	chan->pri_ch_idx = rtw89_get_primary_chan_idx(bandwidth, center_freq,
10662306a36Sopenharmony_ci						      primary_freq);
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cibool rtw89_assign_entity_chan(struct rtw89_dev *rtwdev,
11062306a36Sopenharmony_ci			      enum rtw89_sub_entity_idx idx,
11162306a36Sopenharmony_ci			      const struct rtw89_chan *new)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	struct rtw89_hal *hal = &rtwdev->hal;
11462306a36Sopenharmony_ci	struct rtw89_chan *chan = &hal->sub[idx].chan;
11562306a36Sopenharmony_ci	struct rtw89_chan_rcd *rcd = &hal->sub[idx].rcd;
11662306a36Sopenharmony_ci	bool band_changed;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	rcd->prev_primary_channel = chan->primary_channel;
11962306a36Sopenharmony_ci	rcd->prev_band_type = chan->band_type;
12062306a36Sopenharmony_ci	band_changed = new->band_type != chan->band_type;
12162306a36Sopenharmony_ci	rcd->band_changed = band_changed;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	*chan = *new;
12462306a36Sopenharmony_ci	return band_changed;
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic void __rtw89_config_entity_chandef(struct rtw89_dev *rtwdev,
12862306a36Sopenharmony_ci					  enum rtw89_sub_entity_idx idx,
12962306a36Sopenharmony_ci					  const struct cfg80211_chan_def *chandef,
13062306a36Sopenharmony_ci					  bool from_stack)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	struct rtw89_hal *hal = &rtwdev->hal;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	hal->sub[idx].chandef = *chandef;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	if (from_stack)
13762306a36Sopenharmony_ci		set_bit(idx, hal->entity_map);
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_civoid rtw89_config_entity_chandef(struct rtw89_dev *rtwdev,
14162306a36Sopenharmony_ci				 enum rtw89_sub_entity_idx idx,
14262306a36Sopenharmony_ci				 const struct cfg80211_chan_def *chandef)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	__rtw89_config_entity_chandef(rtwdev, idx, chandef, true);
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_civoid rtw89_config_roc_chandef(struct rtw89_dev *rtwdev,
14862306a36Sopenharmony_ci			      enum rtw89_sub_entity_idx idx,
14962306a36Sopenharmony_ci			      const struct cfg80211_chan_def *chandef)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	struct rtw89_hal *hal = &rtwdev->hal;
15262306a36Sopenharmony_ci	enum rtw89_sub_entity_idx cur;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	if (chandef) {
15562306a36Sopenharmony_ci		cur = atomic_cmpxchg(&hal->roc_entity_idx,
15662306a36Sopenharmony_ci				     RTW89_SUB_ENTITY_IDLE, idx);
15762306a36Sopenharmony_ci		if (cur != RTW89_SUB_ENTITY_IDLE) {
15862306a36Sopenharmony_ci			rtw89_debug(rtwdev, RTW89_DBG_TXRX,
15962306a36Sopenharmony_ci				    "ROC still processing on entity %d\n", idx);
16062306a36Sopenharmony_ci			return;
16162306a36Sopenharmony_ci		}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci		hal->roc_chandef = *chandef;
16462306a36Sopenharmony_ci	} else {
16562306a36Sopenharmony_ci		cur = atomic_cmpxchg(&hal->roc_entity_idx, idx,
16662306a36Sopenharmony_ci				     RTW89_SUB_ENTITY_IDLE);
16762306a36Sopenharmony_ci		if (cur == idx)
16862306a36Sopenharmony_ci			return;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci		if (cur == RTW89_SUB_ENTITY_IDLE)
17162306a36Sopenharmony_ci			rtw89_debug(rtwdev, RTW89_DBG_TXRX,
17262306a36Sopenharmony_ci				    "ROC already finished on entity %d\n", idx);
17362306a36Sopenharmony_ci		else
17462306a36Sopenharmony_ci			rtw89_debug(rtwdev, RTW89_DBG_TXRX,
17562306a36Sopenharmony_ci				    "ROC is processing on entity %d\n", cur);
17662306a36Sopenharmony_ci	}
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cistatic void rtw89_config_default_chandef(struct rtw89_dev *rtwdev)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	struct cfg80211_chan_def chandef = {0};
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	rtw89_get_default_chandef(&chandef);
18462306a36Sopenharmony_ci	__rtw89_config_entity_chandef(rtwdev, RTW89_SUB_ENTITY_0, &chandef, false);
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_civoid rtw89_entity_init(struct rtw89_dev *rtwdev)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	struct rtw89_hal *hal = &rtwdev->hal;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	bitmap_zero(hal->entity_map, NUM_OF_RTW89_SUB_ENTITY);
19262306a36Sopenharmony_ci	atomic_set(&hal->roc_entity_idx, RTW89_SUB_ENTITY_IDLE);
19362306a36Sopenharmony_ci	rtw89_config_default_chandef(rtwdev);
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cienum rtw89_entity_mode rtw89_entity_recalc(struct rtw89_dev *rtwdev)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	struct rtw89_hal *hal = &rtwdev->hal;
19962306a36Sopenharmony_ci	const struct cfg80211_chan_def *chandef;
20062306a36Sopenharmony_ci	enum rtw89_entity_mode mode;
20162306a36Sopenharmony_ci	struct rtw89_chan chan;
20262306a36Sopenharmony_ci	u8 weight;
20362306a36Sopenharmony_ci	u8 last;
20462306a36Sopenharmony_ci	u8 idx;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	weight = bitmap_weight(hal->entity_map, NUM_OF_RTW89_SUB_ENTITY);
20762306a36Sopenharmony_ci	switch (weight) {
20862306a36Sopenharmony_ci	default:
20962306a36Sopenharmony_ci		rtw89_warn(rtwdev, "unknown ent chan weight: %d\n", weight);
21062306a36Sopenharmony_ci		bitmap_zero(hal->entity_map, NUM_OF_RTW89_SUB_ENTITY);
21162306a36Sopenharmony_ci		fallthrough;
21262306a36Sopenharmony_ci	case 0:
21362306a36Sopenharmony_ci		rtw89_config_default_chandef(rtwdev);
21462306a36Sopenharmony_ci		fallthrough;
21562306a36Sopenharmony_ci	case 1:
21662306a36Sopenharmony_ci		last = RTW89_SUB_ENTITY_0;
21762306a36Sopenharmony_ci		mode = RTW89_ENTITY_MODE_SCC;
21862306a36Sopenharmony_ci		break;
21962306a36Sopenharmony_ci	case 2:
22062306a36Sopenharmony_ci		last = RTW89_SUB_ENTITY_1;
22162306a36Sopenharmony_ci		mode = rtw89_get_entity_mode(rtwdev);
22262306a36Sopenharmony_ci		if (mode == RTW89_ENTITY_MODE_MCC)
22362306a36Sopenharmony_ci			break;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci		mode = RTW89_ENTITY_MODE_MCC_PREPARE;
22662306a36Sopenharmony_ci		break;
22762306a36Sopenharmony_ci	}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	for (idx = 0; idx <= last; idx++) {
23062306a36Sopenharmony_ci		chandef = rtw89_chandef_get(rtwdev, idx);
23162306a36Sopenharmony_ci		rtw89_get_channel_params(chandef, &chan);
23262306a36Sopenharmony_ci		if (chan.channel == 0) {
23362306a36Sopenharmony_ci			WARN(1, "Invalid channel on chanctx %d\n", idx);
23462306a36Sopenharmony_ci			return RTW89_ENTITY_MODE_INVALID;
23562306a36Sopenharmony_ci		}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci		rtw89_assign_entity_chan(rtwdev, idx, &chan);
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	rtw89_set_entity_mode(rtwdev, mode);
24162306a36Sopenharmony_ci	return mode;
24262306a36Sopenharmony_ci}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cistatic void rtw89_chanctx_notify(struct rtw89_dev *rtwdev,
24562306a36Sopenharmony_ci				 enum rtw89_chanctx_state state)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	const struct rtw89_chip_info *chip = rtwdev->chip;
24862306a36Sopenharmony_ci	const struct rtw89_chanctx_listener *listener = chip->chanctx_listener;
24962306a36Sopenharmony_ci	int i;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	if (!listener)
25262306a36Sopenharmony_ci		return;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	for (i = 0; i < NUM_OF_RTW89_CHANCTX_CALLBACKS; i++) {
25562306a36Sopenharmony_ci		if (!listener->callbacks[i])
25662306a36Sopenharmony_ci			continue;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci		rtw89_debug(rtwdev, RTW89_DBG_CHAN,
25962306a36Sopenharmony_ci			    "chanctx notify listener: cb %d, state %d\n",
26062306a36Sopenharmony_ci			    i, state);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci		listener->callbacks[i](rtwdev, state);
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cistatic int rtw89_mcc_start(struct rtw89_dev *rtwdev)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	if (rtwdev->scanning)
26962306a36Sopenharmony_ci		rtw89_hw_scan_abort(rtwdev, rtwdev->scan_info.scanning_vif);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	rtw89_leave_lps(rtwdev);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	rtw89_debug(rtwdev, RTW89_DBG_CHAN, "MCC start\n");
27462306a36Sopenharmony_ci	rtw89_chanctx_notify(rtwdev, RTW89_CHANCTX_STATE_MCC_START);
27562306a36Sopenharmony_ci	return 0;
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_cistatic void rtw89_mcc_stop(struct rtw89_dev *rtwdev)
27962306a36Sopenharmony_ci{
28062306a36Sopenharmony_ci	rtw89_debug(rtwdev, RTW89_DBG_CHAN, "MCC stop\n");
28162306a36Sopenharmony_ci	rtw89_chanctx_notify(rtwdev, RTW89_CHANCTX_STATE_MCC_STOP);
28262306a36Sopenharmony_ci}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_civoid rtw89_chanctx_work(struct work_struct *work)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,
28762306a36Sopenharmony_ci						chanctx_work.work);
28862306a36Sopenharmony_ci	enum rtw89_entity_mode mode;
28962306a36Sopenharmony_ci	int ret;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	mutex_lock(&rtwdev->mutex);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	mode = rtw89_get_entity_mode(rtwdev);
29462306a36Sopenharmony_ci	switch (mode) {
29562306a36Sopenharmony_ci	case RTW89_ENTITY_MODE_MCC_PREPARE:
29662306a36Sopenharmony_ci		rtw89_set_entity_mode(rtwdev, RTW89_ENTITY_MODE_MCC);
29762306a36Sopenharmony_ci		rtw89_set_channel(rtwdev);
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci		ret = rtw89_mcc_start(rtwdev);
30062306a36Sopenharmony_ci		if (ret)
30162306a36Sopenharmony_ci			rtw89_warn(rtwdev, "failed to start MCC: %d\n", ret);
30262306a36Sopenharmony_ci		break;
30362306a36Sopenharmony_ci	default:
30462306a36Sopenharmony_ci		break;
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	mutex_unlock(&rtwdev->mutex);
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_civoid rtw89_queue_chanctx_work(struct rtw89_dev *rtwdev)
31162306a36Sopenharmony_ci{
31262306a36Sopenharmony_ci	enum rtw89_entity_mode mode;
31362306a36Sopenharmony_ci	u32 delay;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	mode = rtw89_get_entity_mode(rtwdev);
31662306a36Sopenharmony_ci	switch (mode) {
31762306a36Sopenharmony_ci	default:
31862306a36Sopenharmony_ci		return;
31962306a36Sopenharmony_ci	case RTW89_ENTITY_MODE_MCC_PREPARE:
32062306a36Sopenharmony_ci		delay = ieee80211_tu_to_usec(RTW89_CHANCTX_TIME_MCC_PREPARE);
32162306a36Sopenharmony_ci		break;
32262306a36Sopenharmony_ci	}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	rtw89_debug(rtwdev, RTW89_DBG_CHAN,
32562306a36Sopenharmony_ci		    "queue chanctx work for mode %d with delay %d us\n",
32662306a36Sopenharmony_ci		    mode, delay);
32762306a36Sopenharmony_ci	ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->chanctx_work,
32862306a36Sopenharmony_ci				     usecs_to_jiffies(delay));
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ciint rtw89_chanctx_ops_add(struct rtw89_dev *rtwdev,
33262306a36Sopenharmony_ci			  struct ieee80211_chanctx_conf *ctx)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	struct rtw89_hal *hal = &rtwdev->hal;
33562306a36Sopenharmony_ci	struct rtw89_chanctx_cfg *cfg = (struct rtw89_chanctx_cfg *)ctx->drv_priv;
33662306a36Sopenharmony_ci	const struct rtw89_chip_info *chip = rtwdev->chip;
33762306a36Sopenharmony_ci	u8 idx;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	idx = find_first_zero_bit(hal->entity_map, NUM_OF_RTW89_SUB_ENTITY);
34062306a36Sopenharmony_ci	if (idx >= chip->support_chanctx_num)
34162306a36Sopenharmony_ci		return -ENOENT;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	rtw89_config_entity_chandef(rtwdev, idx, &ctx->def);
34462306a36Sopenharmony_ci	rtw89_set_channel(rtwdev);
34562306a36Sopenharmony_ci	cfg->idx = idx;
34662306a36Sopenharmony_ci	hal->sub[idx].cfg = cfg;
34762306a36Sopenharmony_ci	return 0;
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_civoid rtw89_chanctx_ops_remove(struct rtw89_dev *rtwdev,
35162306a36Sopenharmony_ci			      struct ieee80211_chanctx_conf *ctx)
35262306a36Sopenharmony_ci{
35362306a36Sopenharmony_ci	struct rtw89_hal *hal = &rtwdev->hal;
35462306a36Sopenharmony_ci	struct rtw89_chanctx_cfg *cfg = (struct rtw89_chanctx_cfg *)ctx->drv_priv;
35562306a36Sopenharmony_ci	enum rtw89_entity_mode mode;
35662306a36Sopenharmony_ci	struct rtw89_vif *rtwvif;
35762306a36Sopenharmony_ci	u8 drop, roll;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	drop = cfg->idx;
36062306a36Sopenharmony_ci	if (drop != RTW89_SUB_ENTITY_0)
36162306a36Sopenharmony_ci		goto out;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	roll = find_next_bit(hal->entity_map, NUM_OF_RTW89_SUB_ENTITY, drop + 1);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	/* Follow rtw89_config_default_chandef() when rtw89_entity_recalc(). */
36662306a36Sopenharmony_ci	if (roll == NUM_OF_RTW89_SUB_ENTITY)
36762306a36Sopenharmony_ci		goto out;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	/* RTW89_SUB_ENTITY_0 is going to release, and another exists.
37062306a36Sopenharmony_ci	 * Make another roll down to RTW89_SUB_ENTITY_0 to replace.
37162306a36Sopenharmony_ci	 */
37262306a36Sopenharmony_ci	hal->sub[roll].cfg->idx = RTW89_SUB_ENTITY_0;
37362306a36Sopenharmony_ci	hal->sub[RTW89_SUB_ENTITY_0] = hal->sub[roll];
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	rtw89_for_each_rtwvif(rtwdev, rtwvif) {
37662306a36Sopenharmony_ci		if (rtwvif->sub_entity_idx == roll)
37762306a36Sopenharmony_ci			rtwvif->sub_entity_idx = RTW89_SUB_ENTITY_0;
37862306a36Sopenharmony_ci	}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	atomic_cmpxchg(&hal->roc_entity_idx, roll, RTW89_SUB_ENTITY_0);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	drop = roll;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ciout:
38562306a36Sopenharmony_ci	mode = rtw89_get_entity_mode(rtwdev);
38662306a36Sopenharmony_ci	switch (mode) {
38762306a36Sopenharmony_ci	case RTW89_ENTITY_MODE_MCC:
38862306a36Sopenharmony_ci		rtw89_mcc_stop(rtwdev);
38962306a36Sopenharmony_ci		break;
39062306a36Sopenharmony_ci	default:
39162306a36Sopenharmony_ci		break;
39262306a36Sopenharmony_ci	}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	clear_bit(drop, hal->entity_map);
39562306a36Sopenharmony_ci	rtw89_set_channel(rtwdev);
39662306a36Sopenharmony_ci}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_civoid rtw89_chanctx_ops_change(struct rtw89_dev *rtwdev,
39962306a36Sopenharmony_ci			      struct ieee80211_chanctx_conf *ctx,
40062306a36Sopenharmony_ci			      u32 changed)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	struct rtw89_chanctx_cfg *cfg = (struct rtw89_chanctx_cfg *)ctx->drv_priv;
40362306a36Sopenharmony_ci	u8 idx = cfg->idx;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	if (changed & IEEE80211_CHANCTX_CHANGE_WIDTH) {
40662306a36Sopenharmony_ci		rtw89_config_entity_chandef(rtwdev, idx, &ctx->def);
40762306a36Sopenharmony_ci		rtw89_set_channel(rtwdev);
40862306a36Sopenharmony_ci	}
40962306a36Sopenharmony_ci}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ciint rtw89_chanctx_ops_assign_vif(struct rtw89_dev *rtwdev,
41262306a36Sopenharmony_ci				 struct rtw89_vif *rtwvif,
41362306a36Sopenharmony_ci				 struct ieee80211_chanctx_conf *ctx)
41462306a36Sopenharmony_ci{
41562306a36Sopenharmony_ci	struct rtw89_chanctx_cfg *cfg = (struct rtw89_chanctx_cfg *)ctx->drv_priv;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	rtwvif->sub_entity_idx = cfg->idx;
41862306a36Sopenharmony_ci	return 0;
41962306a36Sopenharmony_ci}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_civoid rtw89_chanctx_ops_unassign_vif(struct rtw89_dev *rtwdev,
42262306a36Sopenharmony_ci				    struct rtw89_vif *rtwvif,
42362306a36Sopenharmony_ci				    struct ieee80211_chanctx_conf *ctx)
42462306a36Sopenharmony_ci{
42562306a36Sopenharmony_ci	rtwvif->sub_entity_idx = RTW89_SUB_ENTITY_0;
42662306a36Sopenharmony_ci}
427