xref: /linux/drivers/net/wireless/realtek/rtw88/util.c (revision a1c613ae4c322ddd58d5a8539dbfba2a0380a8c0)
1e3037485SYan-Hsuan Chuang // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2e3037485SYan-Hsuan Chuang /* Copyright(c) 2018-2019  Realtek Corporation
3e3037485SYan-Hsuan Chuang  */
4e3037485SYan-Hsuan Chuang 
5e3037485SYan-Hsuan Chuang #include "main.h"
6e3037485SYan-Hsuan Chuang #include "util.h"
7e3037485SYan-Hsuan Chuang #include "reg.h"
8e3037485SYan-Hsuan Chuang 
check_hw_ready(struct rtw_dev * rtwdev,u32 addr,u32 mask,u32 target)9e3037485SYan-Hsuan Chuang bool check_hw_ready(struct rtw_dev *rtwdev, u32 addr, u32 mask, u32 target)
10e3037485SYan-Hsuan Chuang {
11e3037485SYan-Hsuan Chuang 	u32 cnt;
12e3037485SYan-Hsuan Chuang 
13e3037485SYan-Hsuan Chuang 	for (cnt = 0; cnt < 1000; cnt++) {
14e3037485SYan-Hsuan Chuang 		if (rtw_read32_mask(rtwdev, addr, mask) == target)
15e3037485SYan-Hsuan Chuang 			return true;
16e3037485SYan-Hsuan Chuang 
17e3037485SYan-Hsuan Chuang 		udelay(10);
18e3037485SYan-Hsuan Chuang 	}
19e3037485SYan-Hsuan Chuang 
20e3037485SYan-Hsuan Chuang 	return false;
21e3037485SYan-Hsuan Chuang }
22449be866SZong-Zhe Yang EXPORT_SYMBOL(check_hw_ready);
23e3037485SYan-Hsuan Chuang 
ltecoex_read_reg(struct rtw_dev * rtwdev,u16 offset,u32 * val)24e3037485SYan-Hsuan Chuang bool ltecoex_read_reg(struct rtw_dev *rtwdev, u16 offset, u32 *val)
25e3037485SYan-Hsuan Chuang {
26dcbf179cSPing-Ke Shih 	const struct rtw_chip_info *chip = rtwdev->chip;
277e149368SPing-Ke Shih 	const struct rtw_ltecoex_addr *ltecoex = chip->ltecoex_addr;
287e149368SPing-Ke Shih 
297e149368SPing-Ke Shih 	if (!check_hw_ready(rtwdev, ltecoex->ctrl, LTECOEX_READY, 1))
30e3037485SYan-Hsuan Chuang 		return false;
31e3037485SYan-Hsuan Chuang 
327e149368SPing-Ke Shih 	rtw_write32(rtwdev, ltecoex->ctrl, 0x800F0000 | offset);
337e149368SPing-Ke Shih 	*val = rtw_read32(rtwdev, ltecoex->rdata);
34e3037485SYan-Hsuan Chuang 
35e3037485SYan-Hsuan Chuang 	return true;
36e3037485SYan-Hsuan Chuang }
37e3037485SYan-Hsuan Chuang 
ltecoex_reg_write(struct rtw_dev * rtwdev,u16 offset,u32 value)38e3037485SYan-Hsuan Chuang bool ltecoex_reg_write(struct rtw_dev *rtwdev, u16 offset, u32 value)
39e3037485SYan-Hsuan Chuang {
40dcbf179cSPing-Ke Shih 	const struct rtw_chip_info *chip = rtwdev->chip;
417e149368SPing-Ke Shih 	const struct rtw_ltecoex_addr *ltecoex = chip->ltecoex_addr;
427e149368SPing-Ke Shih 
437e149368SPing-Ke Shih 	if (!check_hw_ready(rtwdev, ltecoex->ctrl, LTECOEX_READY, 1))
44e3037485SYan-Hsuan Chuang 		return false;
45e3037485SYan-Hsuan Chuang 
467e149368SPing-Ke Shih 	rtw_write32(rtwdev, ltecoex->wdata, value);
477e149368SPing-Ke Shih 	rtw_write32(rtwdev, ltecoex->ctrl, 0xC00F0000 | offset);
48e3037485SYan-Hsuan Chuang 
49e3037485SYan-Hsuan Chuang 	return true;
50e3037485SYan-Hsuan Chuang }
51e3037485SYan-Hsuan Chuang 
rtw_restore_reg(struct rtw_dev * rtwdev,struct rtw_backup_info * bckp,u32 num)52e3037485SYan-Hsuan Chuang void rtw_restore_reg(struct rtw_dev *rtwdev,
53e3037485SYan-Hsuan Chuang 		     struct rtw_backup_info *bckp, u32 num)
54e3037485SYan-Hsuan Chuang {
55e3037485SYan-Hsuan Chuang 	u8 len;
56e3037485SYan-Hsuan Chuang 	u32 reg;
57e3037485SYan-Hsuan Chuang 	u32 val;
58e3037485SYan-Hsuan Chuang 	int i;
59e3037485SYan-Hsuan Chuang 
60e3037485SYan-Hsuan Chuang 	for (i = 0; i < num; i++, bckp++) {
61e3037485SYan-Hsuan Chuang 		len = bckp->len;
62e3037485SYan-Hsuan Chuang 		reg = bckp->reg;
63e3037485SYan-Hsuan Chuang 		val = bckp->val;
64e3037485SYan-Hsuan Chuang 
65e3037485SYan-Hsuan Chuang 		switch (len) {
66e3037485SYan-Hsuan Chuang 		case 1:
67e3037485SYan-Hsuan Chuang 			rtw_write8(rtwdev, reg, (u8)val);
68e3037485SYan-Hsuan Chuang 			break;
69e3037485SYan-Hsuan Chuang 		case 2:
70e3037485SYan-Hsuan Chuang 			rtw_write16(rtwdev, reg, (u16)val);
71e3037485SYan-Hsuan Chuang 			break;
72e3037485SYan-Hsuan Chuang 		case 4:
73e3037485SYan-Hsuan Chuang 			rtw_write32(rtwdev, reg, (u32)val);
74e3037485SYan-Hsuan Chuang 			break;
75e3037485SYan-Hsuan Chuang 		default:
76e3037485SYan-Hsuan Chuang 			break;
77e3037485SYan-Hsuan Chuang 		}
78e3037485SYan-Hsuan Chuang 	}
79e3037485SYan-Hsuan Chuang }
80449be866SZong-Zhe Yang EXPORT_SYMBOL(rtw_restore_reg);
81699c7730STzu-En Huang 
rtw_desc_to_mcsrate(u16 rate,u8 * mcs,u8 * nss)82699c7730STzu-En Huang void rtw_desc_to_mcsrate(u16 rate, u8 *mcs, u8 *nss)
83699c7730STzu-En Huang {
84699c7730STzu-En Huang 	if (rate <= DESC_RATE54M)
85699c7730STzu-En Huang 		return;
86699c7730STzu-En Huang 
87699c7730STzu-En Huang 	if (rate >= DESC_RATEVHT1SS_MCS0 &&
88699c7730STzu-En Huang 	    rate <= DESC_RATEVHT1SS_MCS9) {
89699c7730STzu-En Huang 		*nss = 1;
90699c7730STzu-En Huang 		*mcs = rate - DESC_RATEVHT1SS_MCS0;
91699c7730STzu-En Huang 	} else if (rate >= DESC_RATEVHT2SS_MCS0 &&
92699c7730STzu-En Huang 		   rate <= DESC_RATEVHT2SS_MCS9) {
93699c7730STzu-En Huang 		*nss = 2;
94699c7730STzu-En Huang 		*mcs = rate - DESC_RATEVHT2SS_MCS0;
95699c7730STzu-En Huang 	} else if (rate >= DESC_RATEVHT3SS_MCS0 &&
96699c7730STzu-En Huang 		   rate <= DESC_RATEVHT3SS_MCS9) {
97699c7730STzu-En Huang 		*nss = 3;
98699c7730STzu-En Huang 		*mcs = rate - DESC_RATEVHT3SS_MCS0;
99699c7730STzu-En Huang 	} else if (rate >= DESC_RATEVHT4SS_MCS0 &&
100699c7730STzu-En Huang 		   rate <= DESC_RATEVHT4SS_MCS9) {
101699c7730STzu-En Huang 		*nss = 4;
102699c7730STzu-En Huang 		*mcs = rate - DESC_RATEVHT4SS_MCS0;
103699c7730STzu-En Huang 	} else if (rate >= DESC_RATEMCS0 &&
104699c7730STzu-En Huang 		   rate <= DESC_RATEMCS15) {
105699c7730STzu-En Huang 		*mcs = rate - DESC_RATEMCS0;
106699c7730STzu-En Huang 	}
107699c7730STzu-En Huang }
10878d5bf92SSascha Hauer 
10978d5bf92SSascha Hauer struct rtw_stas_entry {
11078d5bf92SSascha Hauer 	struct list_head list;
11178d5bf92SSascha Hauer 	struct ieee80211_sta *sta;
11278d5bf92SSascha Hauer };
11378d5bf92SSascha Hauer 
11478d5bf92SSascha Hauer struct rtw_iter_stas_data {
11578d5bf92SSascha Hauer 	struct rtw_dev *rtwdev;
11678d5bf92SSascha Hauer 	struct list_head list;
11778d5bf92SSascha Hauer };
11878d5bf92SSascha Hauer 
rtw_collect_sta_iter(void * data,struct ieee80211_sta * sta)11978d5bf92SSascha Hauer static void rtw_collect_sta_iter(void *data, struct ieee80211_sta *sta)
12078d5bf92SSascha Hauer {
12178d5bf92SSascha Hauer 	struct rtw_iter_stas_data *iter_stas = data;
12278d5bf92SSascha Hauer 	struct rtw_stas_entry *stas_entry;
12378d5bf92SSascha Hauer 
12478d5bf92SSascha Hauer 	stas_entry = kmalloc(sizeof(*stas_entry), GFP_ATOMIC);
12578d5bf92SSascha Hauer 	if (!stas_entry)
12678d5bf92SSascha Hauer 		return;
12778d5bf92SSascha Hauer 
12878d5bf92SSascha Hauer 	stas_entry->sta = sta;
12978d5bf92SSascha Hauer 	list_add_tail(&stas_entry->list, &iter_stas->list);
13078d5bf92SSascha Hauer }
13178d5bf92SSascha Hauer 
rtw_iterate_stas(struct rtw_dev * rtwdev,void (* iterator)(void * data,struct ieee80211_sta * sta),void * data)13278d5bf92SSascha Hauer void rtw_iterate_stas(struct rtw_dev *rtwdev,
13378d5bf92SSascha Hauer 		      void (*iterator)(void *data,
13478d5bf92SSascha Hauer 				       struct ieee80211_sta *sta),
13578d5bf92SSascha Hauer 		      void *data)
13678d5bf92SSascha Hauer {
13778d5bf92SSascha Hauer 	struct rtw_iter_stas_data iter_data;
13878d5bf92SSascha Hauer 	struct rtw_stas_entry *sta_entry, *tmp;
13978d5bf92SSascha Hauer 
14078d5bf92SSascha Hauer 	/* &rtwdev->mutex makes sure no stations can be removed between
14178d5bf92SSascha Hauer 	 * collecting the stations and iterating over them.
14278d5bf92SSascha Hauer 	 */
14378d5bf92SSascha Hauer 	lockdep_assert_held(&rtwdev->mutex);
14478d5bf92SSascha Hauer 
14578d5bf92SSascha Hauer 	iter_data.rtwdev = rtwdev;
14678d5bf92SSascha Hauer 	INIT_LIST_HEAD(&iter_data.list);
14778d5bf92SSascha Hauer 
14878d5bf92SSascha Hauer 	ieee80211_iterate_stations_atomic(rtwdev->hw, rtw_collect_sta_iter,
14978d5bf92SSascha Hauer 					  &iter_data);
15078d5bf92SSascha Hauer 
15178d5bf92SSascha Hauer 	list_for_each_entry_safe(sta_entry, tmp, &iter_data.list,
15278d5bf92SSascha Hauer 				 list) {
15378d5bf92SSascha Hauer 		list_del_init(&sta_entry->list);
15478d5bf92SSascha Hauer 		iterator(data, sta_entry->sta);
15578d5bf92SSascha Hauer 		kfree(sta_entry);
15678d5bf92SSascha Hauer 	}
15778d5bf92SSascha Hauer }
15878d5bf92SSascha Hauer 
15978d5bf92SSascha Hauer struct rtw_vifs_entry {
16078d5bf92SSascha Hauer 	struct list_head list;
16178d5bf92SSascha Hauer 	struct ieee80211_vif *vif;
16278d5bf92SSascha Hauer };
16378d5bf92SSascha Hauer 
16478d5bf92SSascha Hauer struct rtw_iter_vifs_data {
16578d5bf92SSascha Hauer 	struct rtw_dev *rtwdev;
16678d5bf92SSascha Hauer 	struct list_head list;
16778d5bf92SSascha Hauer };
16878d5bf92SSascha Hauer 
rtw_collect_vif_iter(void * data,u8 * mac,struct ieee80211_vif * vif)16978d5bf92SSascha Hauer static void rtw_collect_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
17078d5bf92SSascha Hauer {
17178d5bf92SSascha Hauer 	struct rtw_iter_vifs_data *iter_stas = data;
17278d5bf92SSascha Hauer 	struct rtw_vifs_entry *vifs_entry;
17378d5bf92SSascha Hauer 
17478d5bf92SSascha Hauer 	vifs_entry = kmalloc(sizeof(*vifs_entry), GFP_ATOMIC);
17578d5bf92SSascha Hauer 	if (!vifs_entry)
17678d5bf92SSascha Hauer 		return;
17778d5bf92SSascha Hauer 
17878d5bf92SSascha Hauer 	vifs_entry->vif = vif;
17978d5bf92SSascha Hauer 	list_add_tail(&vifs_entry->list, &iter_stas->list);
18078d5bf92SSascha Hauer }
18178d5bf92SSascha Hauer 
rtw_iterate_vifs(struct rtw_dev * rtwdev,void (* iterator)(void * data,struct ieee80211_vif * vif),void * data)18278d5bf92SSascha Hauer void rtw_iterate_vifs(struct rtw_dev *rtwdev,
183*8986f0a9SDmitry Antipov 		      void (*iterator)(void *data, struct ieee80211_vif *vif),
18478d5bf92SSascha Hauer 		      void *data)
18578d5bf92SSascha Hauer {
18678d5bf92SSascha Hauer 	struct rtw_iter_vifs_data iter_data;
18778d5bf92SSascha Hauer 	struct rtw_vifs_entry *vif_entry, *tmp;
18878d5bf92SSascha Hauer 
18978d5bf92SSascha Hauer 	/* &rtwdev->mutex makes sure no interfaces can be removed between
19078d5bf92SSascha Hauer 	 * collecting the interfaces and iterating over them.
19178d5bf92SSascha Hauer 	 */
19278d5bf92SSascha Hauer 	lockdep_assert_held(&rtwdev->mutex);
19378d5bf92SSascha Hauer 
19478d5bf92SSascha Hauer 	iter_data.rtwdev = rtwdev;
19578d5bf92SSascha Hauer 	INIT_LIST_HEAD(&iter_data.list);
19678d5bf92SSascha Hauer 
19778d5bf92SSascha Hauer 	ieee80211_iterate_active_interfaces_atomic(rtwdev->hw,
19878d5bf92SSascha Hauer 						   IEEE80211_IFACE_ITER_NORMAL,
19978d5bf92SSascha Hauer 						   rtw_collect_vif_iter, &iter_data);
20078d5bf92SSascha Hauer 
20178d5bf92SSascha Hauer 	list_for_each_entry_safe(vif_entry, tmp, &iter_data.list,
20278d5bf92SSascha Hauer 				 list) {
20378d5bf92SSascha Hauer 		list_del_init(&vif_entry->list);
204*8986f0a9SDmitry Antipov 		iterator(data, vif_entry->vif);
20578d5bf92SSascha Hauer 		kfree(vif_entry);
20678d5bf92SSascha Hauer 	}
20778d5bf92SSascha Hauer }
208