diff options
Diffstat (limited to 'sys/contrib/dev/rtw89')
62 files changed, 14308 insertions, 5120 deletions
diff --git a/sys/contrib/dev/rtw89/Kconfig b/sys/contrib/dev/rtw89/Kconfig index b1c86cdd9c0e..4288c30b400a 100644 --- a/sys/contrib/dev/rtw89/Kconfig +++ b/sys/contrib/dev/rtw89/Kconfig @@ -17,6 +17,9 @@ config RTW89_CORE  config RTW89_PCI  	tristate +config RTW89_USB +	tristate +  config RTW89_8851B  	tristate @@ -49,6 +52,17 @@ config RTW89_8851BE  	  802.11ax PCIe wireless network (Wi-Fi 6) adapter +config RTW89_8851BU +	tristate "Realtek 8851BU USB wireless network (Wi-Fi 6) adapter" +	depends on USB +	select RTW89_CORE +	select RTW89_USB +	select RTW89_8851B +	help +	  Select this option will enable support for 8851BU chipset + +	  802.11ax USB wireless network (Wi-Fi 6) adapter +  config RTW89_8852AE  	tristate "Realtek 8852AE PCI wireless network (Wi-Fi 6) adapter"  	depends on PCI @@ -72,6 +86,18 @@ config RTW89_8852BE  	  802.11ax PCIe wireless network (Wi-Fi 6) adapter +config RTW89_8852BU +	tristate "Realtek 8852BU USB wireless network (Wi-Fi 6) adapter" +	depends on USB +	select RTW89_CORE +	select RTW89_USB +	select RTW89_8852B +	select RTW89_8852B_COMMON +	help +	  Select this option will enable support for 8852BU chipset + +	  802.11ax USB wireless network (Wi-Fi 6) adapter +  config RTW89_8852BTE  	tristate "Realtek 8852BE-VT PCI wireless network (Wi-Fi 6) adapter"  	depends on PCI @@ -123,7 +149,7 @@ config RTW89_DEBUGMSG  config RTW89_DEBUGFS  	bool "Realtek rtw89 debugfs support" -	depends on RTW89_CORE +	depends on RTW89_CORE && CFG80211_DEBUGFS  	select RTW89_DEBUG  	help  	  Enable debugfs support diff --git a/sys/contrib/dev/rtw89/Makefile b/sys/contrib/dev/rtw89/Makefile index c751013e811e..23e43c444f69 100644 --- a/sys/contrib/dev/rtw89/Makefile +++ b/sys/contrib/dev/rtw89/Makefile @@ -31,6 +31,9 @@ rtw89_8851b-objs := rtw8851b.o \  obj-$(CONFIG_RTW89_8851BE) += rtw89_8851be.o  rtw89_8851be-objs := rtw8851be.o +obj-$(CONFIG_RTW89_8851BU) += rtw89_8851bu.o +rtw89_8851bu-objs := rtw8851bu.o +  obj-$(CONFIG_RTW89_8852A) += rtw89_8852a.o  rtw89_8852a-objs := rtw8852a.o \  		    rtw8852a_table.o \ @@ -52,6 +55,9 @@ rtw89_8852b-objs := rtw8852b.o \  obj-$(CONFIG_RTW89_8852BE) += rtw89_8852be.o  rtw89_8852be-objs := rtw8852be.o +obj-$(CONFIG_RTW89_8852BU) += rtw89_8852bu.o +rtw89_8852bu-objs := rtw8852bu.o +  obj-$(CONFIG_RTW89_8852BT) += rtw89_8852bt.o  rtw89_8852bt-objs := rtw8852bt.o \  		    rtw8852bt_rfk.o \ @@ -81,3 +87,6 @@ rtw89_core-$(CONFIG_RTW89_DEBUG) += debug.o  obj-$(CONFIG_RTW89_PCI) += rtw89_pci.o  rtw89_pci-y := pci.o pci_be.o +obj-$(CONFIG_RTW89_USB) += rtw89_usb.o +rtw89_usb-y := usb.o + diff --git a/sys/contrib/dev/rtw89/acpi.c b/sys/contrib/dev/rtw89/acpi.c index f5dedb12c129..fdba1ea46ec6 100644 --- a/sys/contrib/dev/rtw89/acpi.c +++ b/sys/contrib/dev/rtw89/acpi.c @@ -12,6 +12,125 @@ static const guid_t rtw89_guid = GUID_INIT(0xD2A8C3E8, 0x4B69, 0x4F00,  					   0x82, 0xBD, 0xFE, 0x86,  					   0x07, 0x80, 0x3A, 0xA7); +static u32 rtw89_acpi_traversal_object(struct rtw89_dev *rtwdev, +				       const union acpi_object *obj, u8 *pos) +{ +	const union acpi_object *elm; +	unsigned int i; +	u32 sub_len; +	u32 len = 0; +	u8 *tmp; + +	switch (obj->type) { +	case ACPI_TYPE_INTEGER: +		if (pos) +			pos[len] = obj->integer.value; + +		len++; +		break; +	case ACPI_TYPE_BUFFER: +		if (unlikely(obj->buffer.length == 0)) { +			rtw89_debug(rtwdev, RTW89_DBG_ACPI, +				    "%s: invalid buffer type\n", __func__); +			goto err; +		} + +		if (pos) +			memcpy(pos, obj->buffer.pointer, obj->buffer.length); + +		len += obj->buffer.length; +		break; +	case ACPI_TYPE_PACKAGE: +		if (unlikely(obj->package.count == 0)) { +			rtw89_debug(rtwdev, RTW89_DBG_ACPI, +				    "%s: invalid package type\n", __func__); +			goto err; +		} + +		for (i = 0; i < obj->package.count; i++) { +			elm = &obj->package.elements[i]; +			tmp = pos ? pos + len : NULL; + +			sub_len = rtw89_acpi_traversal_object(rtwdev, elm, tmp); +			if (unlikely(sub_len == 0)) +				goto err; + +			len += sub_len; +		} +		break; +	default: +		rtw89_debug(rtwdev, RTW89_DBG_ACPI, "%s: unhandled type: %d\n", +			    __func__, obj->type); +		goto err; +	} + +	return len; + +err: +	return 0; +} + +static u32 rtw89_acpi_calculate_object_length(struct rtw89_dev *rtwdev, +					      const union acpi_object *obj) +{ +	return rtw89_acpi_traversal_object(rtwdev, obj, NULL); +} + +static struct rtw89_acpi_data * +rtw89_acpi_evaluate_method(struct rtw89_dev *rtwdev, const char *method) +{ +	struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL}; +	struct rtw89_acpi_data *data = NULL; +	acpi_handle root, handle; +	union acpi_object *obj; +	acpi_status status; +	u32 len; + +	root = ACPI_HANDLE(rtwdev->dev); +	if (!root) { +		rtw89_debug(rtwdev, RTW89_DBG_ACPI, +			    "acpi (%s): failed to get root\n", method); +		return NULL; +	} + +#if defined(__linux__) +	status = acpi_get_handle(root, (acpi_string)method, &handle); +#elif defined(__FreeBSD__) +	status = acpi_get_handle(root, method, &handle); +#endif +	if (ACPI_FAILURE(status)) { +		rtw89_debug(rtwdev, RTW89_DBG_ACPI, +			    "acpi (%s): failed to get handle\n", method); +		return NULL; +	} + +	status = acpi_evaluate_object(handle, NULL, NULL, &buf); +	if (ACPI_FAILURE(status)) { +		rtw89_debug(rtwdev, RTW89_DBG_ACPI, +			    "acpi (%s): failed to evaluate object\n", method); +		return NULL; +	} + +	obj = buf.pointer; +	len = rtw89_acpi_calculate_object_length(rtwdev, obj); +	if (unlikely(len == 0)) { +		rtw89_debug(rtwdev, RTW89_DBG_ACPI, +			    "acpi (%s): failed to traversal obj len\n", method); +		goto out; +	} + +	data = kzalloc(struct_size(data, buf, len), GFP_KERNEL); +	if (!data) +		goto out; + +	data->len = len; +	rtw89_acpi_traversal_object(rtwdev, obj, data->buf); + +out: +	ACPI_FREE(obj); +	return data; +} +  static  int rtw89_acpi_dsm_get_value(struct rtw89_dev *rtwdev, union acpi_object *obj,  			     u8 *value) @@ -121,6 +240,138 @@ int rtw89_acpi_dsm_get_policy_6ghz_sp(struct rtw89_dev *rtwdev,  	return 0;  } +static bool chk_acpi_policy_6ghz_vlp_sig(const struct rtw89_acpi_policy_6ghz_vlp *p) +{ +	return p->signature[0] == 0x52 && +	       p->signature[1] == 0x54 && +	       p->signature[2] == 0x4B && +	       p->signature[3] == 0x0B; +} + +static +int rtw89_acpi_dsm_get_policy_6ghz_vlp(struct rtw89_dev *rtwdev, +				       union acpi_object *obj, +				       struct rtw89_acpi_policy_6ghz_vlp **policy) +{ +	const struct rtw89_acpi_policy_6ghz_vlp *ptr; +	u32 buf_len; + +	if (obj->type != ACPI_TYPE_BUFFER) { +		rtw89_debug(rtwdev, RTW89_DBG_ACPI, +			    "acpi: expect buffer but type: %d\n", obj->type); +		return -EINVAL; +	} + +	buf_len = obj->buffer.length; +	if (buf_len < sizeof(*ptr)) { +		rtw89_debug(rtwdev, RTW89_DBG_ACPI, "%s: invalid buffer length: %u\n", +			    __func__, buf_len); +		return -EINVAL; +	} + +	ptr = (typeof(ptr))obj->buffer.pointer; +	if (!chk_acpi_policy_6ghz_vlp_sig(ptr)) { +		rtw89_debug(rtwdev, RTW89_DBG_ACPI, "%s: bad signature\n", __func__); +		return -EINVAL; +	} + +	*policy = kmemdup(ptr, sizeof(*ptr), GFP_KERNEL); +	if (!*policy) +		return -ENOMEM; + +	rtw89_hex_dump(rtwdev, RTW89_DBG_ACPI, "policy_6ghz_vlp: ", *policy, +		       sizeof(*ptr)); +	return 0; +} + +static bool chk_acpi_policy_tas_sig(const struct rtw89_acpi_policy_tas *p) +{ +	return p->signature[0] == 0x52 && +	       p->signature[1] == 0x54 && +	       p->signature[2] == 0x4B && +	       p->signature[3] == 0x05; +} + +static int rtw89_acpi_dsm_get_policy_tas(struct rtw89_dev *rtwdev, +					 union acpi_object *obj, +					 struct rtw89_acpi_policy_tas **policy) +{ +	const struct rtw89_acpi_policy_tas *ptr; +	u32 buf_len; + +	if (obj->type != ACPI_TYPE_BUFFER) { +		rtw89_debug(rtwdev, RTW89_DBG_ACPI, +			    "acpi: expect buffer but type: %d\n", obj->type); +		return -EINVAL; +	} + +	buf_len = obj->buffer.length; +	if (buf_len < sizeof(*ptr)) { +		rtw89_debug(rtwdev, RTW89_DBG_ACPI, "%s: invalid buffer length: %u\n", +			    __func__, buf_len); +		return -EINVAL; +	} + +	ptr = (typeof(ptr))obj->buffer.pointer; +	if (!chk_acpi_policy_tas_sig(ptr)) { +		rtw89_debug(rtwdev, RTW89_DBG_ACPI, "%s: bad signature\n", __func__); +		return -EINVAL; +	} + +	*policy = kmemdup(ptr, sizeof(*ptr), GFP_KERNEL); +	if (!*policy) +		return -ENOMEM; + +	rtw89_hex_dump(rtwdev, RTW89_DBG_ACPI, "policy_tas: ", *policy, +		       sizeof(*ptr)); +	return 0; +} + +static +bool chk_acpi_policy_reg_rules_sig(const struct rtw89_acpi_policy_reg_rules *p) +{ +	return p->signature[0] == 0x52 && +	       p->signature[1] == 0x54 && +	       p->signature[2] == 0x4B && +	       p->signature[3] == 0x0A; +} + +static +int rtw89_acpi_dsm_get_policy_reg_rules(struct rtw89_dev *rtwdev, +					union acpi_object *obj, +					struct rtw89_acpi_policy_reg_rules **policy) +{ +	const struct rtw89_acpi_policy_reg_rules *ptr; +	u32 buf_len; + +	if (obj->type != ACPI_TYPE_BUFFER) { +		rtw89_debug(rtwdev, RTW89_DBG_ACPI, +			    "acpi: expect buffer but type: %d\n", obj->type); +		return -EINVAL; +	} + +	buf_len = obj->buffer.length; +	if (buf_len < sizeof(*ptr)) { +		rtw89_debug(rtwdev, RTW89_DBG_ACPI, "%s: invalid buffer length: %u\n", +			    __func__, buf_len); +		return -EINVAL; +	} + +	ptr = (typeof(ptr))obj->buffer.pointer; +	if (!chk_acpi_policy_reg_rules_sig(ptr)) { +		rtw89_debug(rtwdev, RTW89_DBG_ACPI, "%s: bad signature\n", __func__); +		return -EINVAL; +	} + +	*policy = kmemdup(ptr, sizeof(*ptr), GFP_KERNEL); +	if (!*policy) +		return -ENOMEM; + +	rtw89_hex_dump(rtwdev, RTW89_DBG_ACPI, "policy_reg_rules: ", *policy, +		       sizeof(*ptr)); +	return 0; +} +  int rtw89_acpi_evaluate_dsm(struct rtw89_dev *rtwdev,  			    enum rtw89_acpi_dsm_func func,  			    struct rtw89_acpi_dsm_result *res) @@ -142,6 +393,14 @@ int rtw89_acpi_evaluate_dsm(struct rtw89_dev *rtwdev,  	else if (func == RTW89_ACPI_DSM_FUNC_6GHZ_SP_SUP)  		ret = rtw89_acpi_dsm_get_policy_6ghz_sp(rtwdev, obj,  							&res->u.policy_6ghz_sp); +	else if (func == RTW89_ACPI_DSM_FUNC_6GHZ_VLP_SUP) +		ret = rtw89_acpi_dsm_get_policy_6ghz_vlp(rtwdev, obj, +							 &res->u.policy_6ghz_vlp); +	else if (func == RTW89_ACPI_DSM_FUNC_TAS_EN) +		ret = rtw89_acpi_dsm_get_policy_tas(rtwdev, obj, &res->u.policy_tas); +	else if (func == RTW89_ACPI_DSM_FUNC_REG_RULES_EN) +		ret = rtw89_acpi_dsm_get_policy_reg_rules(rtwdev, obj, +							  &res->u.policy_reg_rules);  	else  		ret = rtw89_acpi_dsm_get_value(rtwdev, obj, &res->u.value); @@ -152,46 +411,879 @@ int rtw89_acpi_evaluate_dsm(struct rtw89_dev *rtwdev,  int rtw89_acpi_evaluate_rtag(struct rtw89_dev *rtwdev,  			     struct rtw89_acpi_rtag_result *res)  { -	struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL}; -	acpi_handle root, handle; -	union acpi_object *obj; -	acpi_status status; +#if defined(__linux__) +	const struct rtw89_acpi_data *data; +#elif defined(__FreeBSD__) +	struct rtw89_acpi_data *data; +#endif  	u32 buf_len;  	int ret = 0; -	root = ACPI_HANDLE(rtwdev->dev); -	if (!root) -		return -EOPNOTSUPP; - -	status = acpi_get_handle(root, (acpi_string)"RTAG", &handle); -	if (ACPI_FAILURE(status)) +	data = rtw89_acpi_evaluate_method(rtwdev, "RTAG"); +	if (!data)  		return -EIO; -	status = acpi_evaluate_object(handle, NULL, NULL, &buf); -	if (ACPI_FAILURE(status)) -		return -EIO; +	buf_len = data->len; +	if (buf_len != sizeof(*res)) { +		rtw89_debug(rtwdev, RTW89_DBG_ACPI, "%s: invalid buffer length: %u\n", +			    __func__, buf_len); +		ret = -EINVAL; +		goto out; +	} -	obj = buf.pointer; -	if (obj->type != ACPI_TYPE_BUFFER) { +	*res = *(struct rtw89_acpi_rtag_result *)data->buf; + +	rtw89_hex_dump(rtwdev, RTW89_DBG_ACPI, "antenna_gain: ", res, sizeof(*res)); + +out: +	kfree(data); +	return ret; +} + +enum rtw89_acpi_sar_subband rtw89_acpi_sar_get_subband(struct rtw89_dev *rtwdev, +						       u32 center_freq) +{ +	switch (center_freq) { +	default:  		rtw89_debug(rtwdev, RTW89_DBG_ACPI, -			    "acpi: expect buffer but type: %d\n", obj->type); -		ret = -EINVAL; +			    "center freq %u to ACPI SAR subband is unhandled\n", +			    center_freq); +		fallthrough; +	case 2412 ... 2484: +		return RTW89_ACPI_SAR_2GHZ_SUBBAND; +	case 5180 ... 5240: +		return RTW89_ACPI_SAR_5GHZ_SUBBAND_1; +	case 5250 ... 5320: +		return RTW89_ACPI_SAR_5GHZ_SUBBAND_2; +	case 5500 ... 5720: +		return RTW89_ACPI_SAR_5GHZ_SUBBAND_2E; +	case 5745 ... 5885: +		return RTW89_ACPI_SAR_5GHZ_SUBBAND_3_4; +	case 5955 ... 6155: +		return RTW89_ACPI_SAR_6GHZ_SUBBAND_5_L; +	case 6175 ... 6415: +		return RTW89_ACPI_SAR_6GHZ_SUBBAND_5_H; +	case 6435 ... 6515: +		return RTW89_ACPI_SAR_6GHZ_SUBBAND_6; +	case 6535 ... 6695: +		return RTW89_ACPI_SAR_6GHZ_SUBBAND_7_L; +	case 6715 ... 6855: +		return RTW89_ACPI_SAR_6GHZ_SUBBAND_7_H; + +	/* freq 6875 (ch 185, 20MHz) spans RTW89_ACPI_SAR_6GHZ_SUBBAND_7_H +	 * and RTW89_ACPI_SAR_6GHZ_SUBBAND_8, so directly describe it with +	 * struct rtw89_6ghz_span. +	 */ + +	case 6895 ... 7115: +		return RTW89_ACPI_SAR_6GHZ_SUBBAND_8; +	} +} + +enum rtw89_band rtw89_acpi_sar_subband_to_band(struct rtw89_dev *rtwdev, +					       enum rtw89_acpi_sar_subband subband) +{ +	switch (subband) { +	default: +		rtw89_debug(rtwdev, RTW89_DBG_ACPI, +			    "ACPI SAR subband %u to band is unhandled\n", subband); +		fallthrough; +	case RTW89_ACPI_SAR_2GHZ_SUBBAND: +		return RTW89_BAND_2G; +	case RTW89_ACPI_SAR_5GHZ_SUBBAND_1: +		return RTW89_BAND_5G; +	case RTW89_ACPI_SAR_5GHZ_SUBBAND_2: +		return RTW89_BAND_5G; +	case RTW89_ACPI_SAR_5GHZ_SUBBAND_2E: +		return RTW89_BAND_5G; +	case RTW89_ACPI_SAR_5GHZ_SUBBAND_3_4: +		return RTW89_BAND_5G; +	case RTW89_ACPI_SAR_6GHZ_SUBBAND_5_L: +		return RTW89_BAND_6G; +	case RTW89_ACPI_SAR_6GHZ_SUBBAND_5_H: +		return RTW89_BAND_6G; +	case RTW89_ACPI_SAR_6GHZ_SUBBAND_6: +		return RTW89_BAND_6G; +	case RTW89_ACPI_SAR_6GHZ_SUBBAND_7_L: +		return RTW89_BAND_6G; +	case RTW89_ACPI_SAR_6GHZ_SUBBAND_7_H: +		return RTW89_BAND_6G; +	case RTW89_ACPI_SAR_6GHZ_SUBBAND_8: +		return RTW89_BAND_6G; +	} +} + +static u8 rtw89_acpi_sar_rfpath_to_hp_antidx(enum rtw89_rf_path rfpath) +{ +	switch (rfpath) { +	default: +	case RF_PATH_B: +		return 0; +	case RF_PATH_A: +		return 1; +	} +} + +static u8 rtw89_acpi_sar_rfpath_to_rt_antidx(enum rtw89_rf_path rfpath) +{ +	switch (rfpath) { +	default: +	case RF_PATH_A: +		return 0; +	case RF_PATH_B: +		return 1; +	} +} + +static s16 rtw89_acpi_sar_normalize_hp_val(u8 v) +{ +	static const u8 bias = 10; +	static const u8 fct = 1; +	u16 res; + +	BUILD_BUG_ON(fct > TXPWR_FACTOR_OF_RTW89_ACPI_SAR); + +	res = (bias << TXPWR_FACTOR_OF_RTW89_ACPI_SAR) + +	      (v << (TXPWR_FACTOR_OF_RTW89_ACPI_SAR - fct)); + +	return min_t(s32, res, MAX_VAL_OF_RTW89_ACPI_SAR); +} + +static s16 rtw89_acpi_sar_normalize_rt_val(u8 v) +{ +	static const u8 fct = 3; +	u16 res; + +	BUILD_BUG_ON(fct > TXPWR_FACTOR_OF_RTW89_ACPI_SAR); + +	res = v << (TXPWR_FACTOR_OF_RTW89_ACPI_SAR - fct); + +	return min_t(s32, res, MAX_VAL_OF_RTW89_ACPI_SAR); +} + +static +void rtw89_acpi_sar_load_std_legacy(struct rtw89_dev *rtwdev, +				    const struct rtw89_acpi_sar_recognition *rec, +				    const void *content, +				    struct rtw89_sar_entry_from_acpi *ent) +{ +	const struct rtw89_acpi_sar_std_legacy *ptr = content; +	enum rtw89_acpi_sar_subband subband; +	enum rtw89_rf_path path; + +	for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) { +		for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) { +			u8 antidx = rec->rfpath_to_antidx(path); + +			if (subband < RTW89_ACPI_SAR_SUBBAND_NR_LEGACY) +				ent->v[subband][path] = +					rec->normalize(ptr->v[antidx][subband]); +			else +				ent->v[subband][path] = MAX_VAL_OF_RTW89_ACPI_SAR; +		} +	} +} + +static +void rtw89_acpi_sar_load_std_has_6ghz(struct rtw89_dev *rtwdev, +				      const struct rtw89_acpi_sar_recognition *rec, +				      const void *content, +				      struct rtw89_sar_entry_from_acpi *ent) +{ +	const struct rtw89_acpi_sar_std_has_6ghz *ptr = content; +	enum rtw89_acpi_sar_subband subband; +	enum rtw89_rf_path path; + +	BUILD_BUG_ON(RTW89_ACPI_SAR_SUBBAND_NR_HAS_6GHZ != NUM_OF_RTW89_ACPI_SAR_SUBBAND); + +	for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) { +		for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) { +			u8 antidx = rec->rfpath_to_antidx(path); + +			ent->v[subband][path] = rec->normalize(ptr->v[antidx][subband]); +		} +	} +} + +static +void rtw89_acpi_sar_load_sml_legacy(struct rtw89_dev *rtwdev, +				    const struct rtw89_acpi_sar_recognition *rec, +				    const void *content, +				    struct rtw89_sar_entry_from_acpi *ent) +{ +	const struct rtw89_acpi_sar_sml_legacy *ptr = content; +	enum rtw89_acpi_sar_subband subband; +	enum rtw89_rf_path path; + +	for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) { +		for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) { +			u8 antidx = rec->rfpath_to_antidx(path); + +			if (subband < RTW89_ACPI_SAR_SUBBAND_NR_LEGACY) +				ent->v[subband][path] = +					rec->normalize(ptr->v[antidx][subband]); +			else +				ent->v[subband][path] = MAX_VAL_OF_RTW89_ACPI_SAR; +		} +	} +} + +static +void rtw89_acpi_sar_load_sml_has_6ghz(struct rtw89_dev *rtwdev, +				      const struct rtw89_acpi_sar_recognition *rec, +				      const void *content, +				      struct rtw89_sar_entry_from_acpi *ent) +{ +	const struct rtw89_acpi_sar_sml_has_6ghz *ptr = content; +	enum rtw89_acpi_sar_subband subband; +	enum rtw89_rf_path path; + +	BUILD_BUG_ON(RTW89_ACPI_SAR_SUBBAND_NR_HAS_6GHZ != NUM_OF_RTW89_ACPI_SAR_SUBBAND); + +	for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) { +		for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) { +			u8 antidx = rec->rfpath_to_antidx(path); + +			ent->v[subband][path] = rec->normalize(ptr->v[antidx][subband]); +		} +	} +} + +static s16 rtw89_acpi_geo_sar_normalize_delta(s8 delta) +{ +	static const u8 fct = 1; + +	BUILD_BUG_ON(fct > TXPWR_FACTOR_OF_RTW89_ACPI_SAR); + +	return delta << (TXPWR_FACTOR_OF_RTW89_ACPI_SAR - fct); +} + +static enum rtw89_acpi_geo_sar_regd_hp +rtw89_acpi_geo_sar_regd_convert_hp_idx(enum rtw89_regulation_type regd) +{ +	switch (regd) { +	case RTW89_FCC: +	case RTW89_IC: +	case RTW89_NCC: +	case RTW89_CHILE: +	case RTW89_MEXICO: +		return RTW89_ACPI_GEO_SAR_REGD_HP_FCC; +	case RTW89_ETSI: +	case RTW89_MKK: +	case RTW89_ACMA: +		return RTW89_ACPI_GEO_SAR_REGD_HP_ETSI; +	default: +	case RTW89_WW: +	case RTW89_NA: +	case RTW89_KCC: +		return RTW89_ACPI_GEO_SAR_REGD_HP_WW; +	} +} + +static enum rtw89_acpi_geo_sar_regd_rt +rtw89_acpi_geo_sar_regd_convert_rt_idx(enum rtw89_regulation_type regd) +{ +	switch (regd) { +	case RTW89_FCC: +	case RTW89_NCC: +	case RTW89_CHILE: +	case RTW89_MEXICO: +		return RTW89_ACPI_GEO_SAR_REGD_RT_FCC; +	case RTW89_ETSI: +	case RTW89_ACMA: +		return RTW89_ACPI_GEO_SAR_REGD_RT_ETSI; +	case RTW89_MKK: +		return RTW89_ACPI_GEO_SAR_REGD_RT_MKK; +	case RTW89_IC: +		return RTW89_ACPI_GEO_SAR_REGD_RT_IC; +	case RTW89_KCC: +		return RTW89_ACPI_GEO_SAR_REGD_RT_KCC; +	default: +	case RTW89_WW: +	case RTW89_NA: +		return RTW89_ACPI_GEO_SAR_REGD_RT_WW; +	} +} + +static +void rtw89_acpi_geo_sar_load_by_hp(struct rtw89_dev *rtwdev, +				   const struct rtw89_acpi_geo_sar_hp_val *ptr, +				   enum rtw89_rf_path path, s16 *val) +{ +	u8 antidx = rtw89_acpi_sar_rfpath_to_hp_antidx(path); +	s16 delta = rtw89_acpi_geo_sar_normalize_delta(ptr->delta[antidx]); +	s16 max = rtw89_acpi_sar_normalize_hp_val(ptr->max); + +	*val = clamp_t(s32, (*val) + delta, MIN_VAL_OF_RTW89_ACPI_SAR, max); +} + +static +void rtw89_acpi_geo_sar_load_by_rt(struct rtw89_dev *rtwdev, +				   const struct rtw89_acpi_geo_sar_rt_val *ptr, +				   s16 *val) +{ +	s16 delta = rtw89_acpi_geo_sar_normalize_delta(ptr->delta); +	s16 max = rtw89_acpi_sar_normalize_rt_val(ptr->max); + +	*val = clamp_t(s32, (*val) + delta, MIN_VAL_OF_RTW89_ACPI_SAR, max); +} + +static +void rtw89_acpi_geo_sar_load_hp_legacy(struct rtw89_dev *rtwdev, +				       const void *content, +				       enum rtw89_regulation_type regd, +				       struct rtw89_sar_entry_from_acpi *ent) +{ +	const struct rtw89_acpi_geo_sar_hp_legacy *ptr = content; +	const struct rtw89_acpi_geo_sar_hp_legacy_entry *ptr_ent; +	const struct rtw89_acpi_geo_sar_hp_val *ptr_ent_val; +	enum rtw89_acpi_geo_sar_regd_hp geo_idx = +		rtw89_acpi_geo_sar_regd_convert_hp_idx(regd); +	enum rtw89_acpi_sar_subband subband; +	enum rtw89_rf_path path; +	enum rtw89_band band; + +	ptr_ent = &ptr->entries[geo_idx]; + +	for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) { +		band = rtw89_acpi_sar_subband_to_band(rtwdev, subband); +		switch (band) { +		case RTW89_BAND_2G: +			ptr_ent_val = &ptr_ent->val_2ghz; +			break; +		case RTW89_BAND_5G: +			ptr_ent_val = &ptr_ent->val_5ghz; +			break; +		default: +		case RTW89_BAND_6G: +			ptr_ent_val = NULL; +			break; +		} + +		if (!ptr_ent_val) +			continue; + +		for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) +			rtw89_acpi_geo_sar_load_by_hp(rtwdev, ptr_ent_val, path, +						      &ent->v[subband][path]); +	} +} + +static +void rtw89_acpi_geo_sar_load_hp_has_6ghz(struct rtw89_dev *rtwdev, +					 const void *content, +					 enum rtw89_regulation_type regd, +					 struct rtw89_sar_entry_from_acpi *ent) +{ +	const struct rtw89_acpi_geo_sar_hp_has_6ghz *ptr = content; +	const struct rtw89_acpi_geo_sar_hp_has_6ghz_entry *ptr_ent; +	const struct rtw89_acpi_geo_sar_hp_val *ptr_ent_val; +	enum rtw89_acpi_geo_sar_regd_hp geo_idx = +		rtw89_acpi_geo_sar_regd_convert_hp_idx(regd); +	enum rtw89_acpi_sar_subband subband; +	enum rtw89_rf_path path; +	enum rtw89_band band; + +	ptr_ent = &ptr->entries[geo_idx]; + +	for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) { +		band = rtw89_acpi_sar_subband_to_band(rtwdev, subband); +		switch (band) { +		case RTW89_BAND_2G: +			ptr_ent_val = &ptr_ent->val_2ghz; +			break; +		case RTW89_BAND_5G: +			ptr_ent_val = &ptr_ent->val_5ghz; +			break; +		case RTW89_BAND_6G: +			ptr_ent_val = &ptr_ent->val_6ghz; +			break; +		default: +			ptr_ent_val = NULL; +			break; +		} + +		if (!ptr_ent_val) +			continue; + +		for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) +			rtw89_acpi_geo_sar_load_by_hp(rtwdev, ptr_ent_val, path, +						      &ent->v[subband][path]); +	} +} + +static +void rtw89_acpi_geo_sar_load_rt_legacy(struct rtw89_dev *rtwdev, +				       const void *content, +				       enum rtw89_regulation_type regd, +				       struct rtw89_sar_entry_from_acpi *ent) +{ +	const struct rtw89_acpi_geo_sar_rt_legacy *ptr = content; +	const struct rtw89_acpi_geo_sar_rt_legacy_entry *ptr_ent; +	const struct rtw89_acpi_geo_sar_rt_val *ptr_ent_val; +	enum rtw89_acpi_geo_sar_regd_rt geo_idx = +		rtw89_acpi_geo_sar_regd_convert_rt_idx(regd); +	enum rtw89_acpi_sar_subband subband; +	enum rtw89_rf_path path; +	enum rtw89_band band; + +	ptr_ent = &ptr->entries[geo_idx]; + +	for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) { +		band = rtw89_acpi_sar_subband_to_band(rtwdev, subband); +		switch (band) { +		case RTW89_BAND_2G: +			ptr_ent_val = &ptr_ent->val_2ghz; +			break; +		case RTW89_BAND_5G: +			ptr_ent_val = &ptr_ent->val_5ghz; +			break; +		default: +		case RTW89_BAND_6G: +			ptr_ent_val = NULL; +			break; +		} + +		if (!ptr_ent_val) +			continue; + +		for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) +			rtw89_acpi_geo_sar_load_by_rt(rtwdev, ptr_ent_val, +						      &ent->v[subband][path]); +	} +} + +static +void rtw89_acpi_geo_sar_load_rt_has_6ghz(struct rtw89_dev *rtwdev, +					 const void *content, +					 enum rtw89_regulation_type regd, +					 struct rtw89_sar_entry_from_acpi *ent) +{ +	const struct rtw89_acpi_geo_sar_rt_has_6ghz *ptr = content; +	const struct rtw89_acpi_geo_sar_rt_has_6ghz_entry *ptr_ent; +	const struct rtw89_acpi_geo_sar_rt_val *ptr_ent_val; +	enum rtw89_acpi_geo_sar_regd_rt geo_idx = +		rtw89_acpi_geo_sar_regd_convert_rt_idx(regd); +	enum rtw89_acpi_sar_subband subband; +	enum rtw89_rf_path path; +	enum rtw89_band band; + +	ptr_ent = &ptr->entries[geo_idx]; + +	for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) { +		band = rtw89_acpi_sar_subband_to_band(rtwdev, subband); +		switch (band) { +		case RTW89_BAND_2G: +			ptr_ent_val = &ptr_ent->val_2ghz; +			break; +		case RTW89_BAND_5G: +			ptr_ent_val = &ptr_ent->val_5ghz; +			break; +		case RTW89_BAND_6G: +			ptr_ent_val = &ptr_ent->val_6ghz; +			break; +		default: +			ptr_ent_val = NULL; +			break; +		} + +		if (!ptr_ent_val) +			continue; + +		for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) +			rtw89_acpi_geo_sar_load_by_rt(rtwdev, ptr_ent_val, +						      &ent->v[subband][path]); +	} +} + +#define RTW89_ACPI_GEO_SAR_DECL_HANDLER(type) \ +static const struct rtw89_acpi_geo_sar_handler \ +rtw89_acpi_geo_sar_handler_ ## type = { \ +	.data_size = RTW89_ACPI_GEO_SAR_SIZE_OF(type), \ +	.load = rtw89_acpi_geo_sar_load_ ## type, \ +} + +RTW89_ACPI_GEO_SAR_DECL_HANDLER(hp_legacy); +RTW89_ACPI_GEO_SAR_DECL_HANDLER(hp_has_6ghz); +RTW89_ACPI_GEO_SAR_DECL_HANDLER(rt_legacy); +RTW89_ACPI_GEO_SAR_DECL_HANDLER(rt_has_6ghz); + +static const struct rtw89_acpi_sar_recognition rtw89_acpi_sar_recs[] = { +	{ +		.id = { +			.cid = RTW89_ACPI_SAR_CID_HP, +			.rev = RTW89_ACPI_SAR_REV_LEGACY, +			.size = RTW89_ACPI_SAR_SIZE_OF(std_legacy), +		}, +		.geo = &rtw89_acpi_geo_sar_handler_hp_legacy, + +		.rfpath_to_antidx = rtw89_acpi_sar_rfpath_to_hp_antidx, +		.normalize = rtw89_acpi_sar_normalize_hp_val, +		.load = rtw89_acpi_sar_load_std_legacy, +	}, +	{ +		.id = { +			.cid = RTW89_ACPI_SAR_CID_HP, +			.rev = RTW89_ACPI_SAR_REV_HAS_6GHZ, +			.size = RTW89_ACPI_SAR_SIZE_OF(std_has_6ghz), +		}, +		.geo = &rtw89_acpi_geo_sar_handler_hp_has_6ghz, + +		.rfpath_to_antidx = rtw89_acpi_sar_rfpath_to_hp_antidx, +		.normalize = rtw89_acpi_sar_normalize_hp_val, +		.load = rtw89_acpi_sar_load_std_has_6ghz, +	}, +	{ +		.id = { +			.cid = RTW89_ACPI_SAR_CID_RT, +			.rev = RTW89_ACPI_SAR_REV_LEGACY, +			.size = RTW89_ACPI_SAR_SIZE_OF(std_legacy), +		}, +		.geo = &rtw89_acpi_geo_sar_handler_rt_legacy, + +		.rfpath_to_antidx = rtw89_acpi_sar_rfpath_to_rt_antidx, +		.normalize = rtw89_acpi_sar_normalize_rt_val, +		.load = rtw89_acpi_sar_load_std_legacy, +	}, +	{ +		.id = { +			.cid = RTW89_ACPI_SAR_CID_RT, +			.rev = RTW89_ACPI_SAR_REV_HAS_6GHZ, +			.size = RTW89_ACPI_SAR_SIZE_OF(std_has_6ghz), +		}, +		.geo = &rtw89_acpi_geo_sar_handler_rt_has_6ghz, + +		.rfpath_to_antidx = rtw89_acpi_sar_rfpath_to_rt_antidx, +		.normalize = rtw89_acpi_sar_normalize_rt_val, +		.load = rtw89_acpi_sar_load_std_has_6ghz, +	}, +	{ +		.id = { +			.cid = RTW89_ACPI_SAR_CID_RT, +			.rev = RTW89_ACPI_SAR_REV_LEGACY, +			.size = RTW89_ACPI_SAR_SIZE_OF(sml_legacy), +		}, +		.geo = &rtw89_acpi_geo_sar_handler_rt_legacy, + +		.rfpath_to_antidx = rtw89_acpi_sar_rfpath_to_rt_antidx, +		.normalize = rtw89_acpi_sar_normalize_rt_val, +		.load = rtw89_acpi_sar_load_sml_legacy, +	}, +	{ +		.id = { +			.cid = RTW89_ACPI_SAR_CID_RT, +			.rev = RTW89_ACPI_SAR_REV_HAS_6GHZ, +			.size = RTW89_ACPI_SAR_SIZE_OF(sml_has_6ghz), +		}, +		.geo = &rtw89_acpi_geo_sar_handler_rt_has_6ghz, + +		.rfpath_to_antidx = rtw89_acpi_sar_rfpath_to_rt_antidx, +		.normalize = rtw89_acpi_sar_normalize_rt_val, +		.load = rtw89_acpi_sar_load_sml_has_6ghz, +	}, +}; + +struct rtw89_acpi_sar_rec_parm { +	u32 pld_len; +	u8 tbl_cnt; +	u16 cid; +	u8 rev; +}; + +static const struct rtw89_acpi_sar_recognition * +rtw89_acpi_sar_recognize(struct rtw89_dev *rtwdev, +			 const struct rtw89_acpi_sar_rec_parm *parm) +{ +	const u32 tbl_len = parm->pld_len / parm->tbl_cnt; +	const struct rtw89_acpi_sar_recognition *rec; +	struct rtw89_acpi_sar_identifier id = {}; + +	rtw89_debug(rtwdev, RTW89_DBG_ACPI, +		    "%s: cid %u, rev %u, tbl len %u, tbl cnt %u\n", +		    __func__, parm->cid, parm->rev, tbl_len, parm->tbl_cnt); + +	if (unlikely(parm->pld_len % parm->tbl_cnt)) { +		rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid pld len %u\n", +			    parm->pld_len); +		return NULL; +	} + +	if (unlikely(tbl_len > RTW89_ACPI_SAR_SIZE_MAX)) { +		rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid tbl len %u\n", +			    tbl_len); +		return NULL; +	} + +	if (unlikely(parm->tbl_cnt > MAX_NUM_OF_RTW89_ACPI_SAR_TBL)) { +		rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid tbl cnt %u\n", +			    parm->tbl_cnt); +		return NULL; +	} + +	switch (parm->cid) { +	case RTW89_ACPI_SAR_CID_HP: +	case RTW89_ACPI_SAR_CID_RT: +		id.cid = parm->cid; +		break; +	default: +		rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid cid 0x%x\n", +			    parm->cid); +		return NULL; +	} + +	switch (parm->rev) { +	case RTW89_ACPI_SAR_REV_LEGACY: +	case RTW89_ACPI_SAR_REV_HAS_6GHZ: +		id.rev = parm->rev; +		break; +	default: +		rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid rev %u\n", +			    parm->rev); +		return NULL; +	} + +	id.size = tbl_len; +	for (unsigned int i = 0; i < ARRAY_SIZE(rtw89_acpi_sar_recs); i++) { +		rec = &rtw89_acpi_sar_recs[i]; +		if (memcmp(&rec->id, &id, sizeof(rec->id)) == 0) +			return rec; +	} + +	rtw89_debug(rtwdev, RTW89_DBG_ACPI, "failed to recognize\n"); +	return NULL; +} + +static const struct rtw89_acpi_sar_recognition * +rtw89_acpi_evaluate_static_sar(struct rtw89_dev *rtwdev, +			       struct rtw89_sar_cfg_acpi *cfg) +{ +	const struct rtw89_acpi_sar_recognition *rec = NULL; +	const struct rtw89_acpi_static_sar_hdr *hdr; +	struct rtw89_sar_entry_from_acpi tmp = {}; +	struct rtw89_acpi_sar_rec_parm parm = {}; +	struct rtw89_sar_table_from_acpi *tbl; +	const struct rtw89_acpi_data *data; +	u32 len; + +	data = rtw89_acpi_evaluate_method(rtwdev, RTW89_ACPI_METHOD_STATIC_SAR); +	if (!data) +		return NULL; + +	rtw89_debug(rtwdev, RTW89_DBG_ACPI, "acpi load static sar\n"); + +	len = data->len; +	if (len <= sizeof(*hdr)) { +		rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid buf len %u\n", len);  		goto out;  	} -	buf_len = obj->buffer.length; -	if (buf_len != sizeof(*res)) { -		rtw89_debug(rtwdev, RTW89_DBG_ACPI, "%s: invalid buffer length: %u\n", -			    __func__, buf_len); +	hdr = (typeof(hdr))data->buf; + +	parm.cid = le16_to_cpu(hdr->cid); +	parm.rev = hdr->rev; +	parm.tbl_cnt = 1; +	parm.pld_len = len - sizeof(*hdr); + +	rec = rtw89_acpi_sar_recognize(rtwdev, &parm); +	if (!rec) +		goto out; + +	rec->load(rtwdev, rec, hdr->content, &tmp); + +	tbl = &cfg->tables[0]; +	for (u8 regd = 0; regd < RTW89_REGD_NUM; regd++) +		tbl->entries[regd] = tmp; + +	cfg->valid_num = 1; + +out: +	kfree(data); +	return rec; +} + +static const struct rtw89_acpi_sar_recognition * +rtw89_acpi_evaluate_dynamic_sar(struct rtw89_dev *rtwdev, +				struct rtw89_sar_cfg_acpi *cfg) +{ +	const struct rtw89_acpi_sar_recognition *rec = NULL; +	const struct rtw89_acpi_dynamic_sar_hdr *hdr; +	struct rtw89_acpi_sar_rec_parm parm = {}; +	struct rtw89_sar_table_from_acpi *tbl; +	const struct rtw89_acpi_data *data; +	u32 len; + +	data = rtw89_acpi_evaluate_method(rtwdev, RTW89_ACPI_METHOD_DYNAMIC_SAR); +	if (!data) +		return NULL; + +	rtw89_debug(rtwdev, RTW89_DBG_ACPI, "acpi load dynamic sar\n"); + +	len = data->len; +	if (len <= sizeof(*hdr)) { +		rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid buf len %u\n", len); +		goto out; +	} + +	hdr = (typeof(hdr))data->buf; + +	parm.cid = le16_to_cpu(hdr->cid); +	parm.rev = hdr->rev; +	parm.tbl_cnt = hdr->cnt; +	parm.pld_len = len - sizeof(*hdr); + +	rec = rtw89_acpi_sar_recognize(rtwdev, &parm); +	if (!rec) +		goto out; + +	for (unsigned int i = 0; i < hdr->cnt; i++) { +		const u8 *content = hdr->content + rec->id.size * i; +		struct rtw89_sar_entry_from_acpi tmp = {}; + +		rec->load(rtwdev, rec, content, &tmp); + +		tbl = &cfg->tables[i]; +		for (u8 regd = 0; regd < RTW89_REGD_NUM; regd++) +			tbl->entries[regd] = tmp; +	} + +	cfg->valid_num = hdr->cnt; + +out: +	kfree(data); +	return rec; +} + +int rtw89_acpi_evaluate_dynamic_sar_indicator(struct rtw89_dev *rtwdev, +					      struct rtw89_sar_cfg_acpi *cfg, +					      bool *poll_changed) +{ +	struct rtw89_sar_indicator_from_acpi *ind = &cfg->indicator; +	struct rtw89_sar_indicator_from_acpi tmp = *ind; +	const struct rtw89_acpi_data *data; +	const u8 *tbl_base1_by_ant; +	enum rtw89_rf_path path; +	int ret = 0; +	u32 len; + +	data = rtw89_acpi_evaluate_method(rtwdev, RTW89_ACPI_METHOD_DYNAMIC_SAR_INDICATOR); +	if (!data) +		return -EFAULT; + +	if (!poll_changed) +		rtw89_debug(rtwdev, RTW89_DBG_ACPI, "acpi load dynamic sar indicator\n"); + +	len = data->len; +	if (len != ind->fields) { +		rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid buf len %u\n", len);  		ret = -EINVAL;  		goto out;  	} -	*res = *(struct rtw89_acpi_rtag_result *)obj->buffer.pointer; +	tbl_base1_by_ant = data->buf; -	rtw89_hex_dump(rtwdev, RTW89_DBG_ACPI, "antenna_gain: ", res, sizeof(*res)); +	for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) { +		u8 antidx = ind->rfpath_to_antidx(path); +		u8 sel; + +		if (antidx >= ind->fields) +			antidx = 0; + +		/* convert the table index from 1-based to 0-based */ +		sel = tbl_base1_by_ant[antidx] - 1; +		if (sel >= cfg->valid_num) +			sel = 0; + +		tmp.tblsel[path] = sel; +	} + +	if (memcmp(ind, &tmp, sizeof(*ind)) == 0) { +		if (poll_changed) +			*poll_changed = false; +	} else { +		if (poll_changed) +			*poll_changed = true; + +		*ind = tmp; +	}  out: -	ACPI_FREE(obj); +	kfree(data);  	return ret;  } + +static +void rtw89_acpi_evaluate_geo_sar(struct rtw89_dev *rtwdev, +				 const struct rtw89_acpi_geo_sar_handler *hdl, +				 struct rtw89_sar_cfg_acpi *cfg) +{ +	const struct rtw89_acpi_data *data; +	u32 len; + +	data = rtw89_acpi_evaluate_method(rtwdev, RTW89_ACPI_METHOD_GEO_SAR); +	if (!data) +		return; + +	rtw89_debug(rtwdev, RTW89_DBG_ACPI, "acpi load geo sar\n"); + +	len = data->len; +	if (len != hdl->data_size) { +		rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid buf len %u (expected %u)\n", +			    len, hdl->data_size); +		goto out; +	} + +	for (unsigned int i = 0; i < cfg->valid_num; i++) +		for (u8 regd = 0; regd < RTW89_REGD_NUM; regd++) +			hdl->load(rtwdev, data->buf, regd, &cfg->tables[i].entries[regd]); + +out: +	kfree(data); +} + +int rtw89_acpi_evaluate_sar(struct rtw89_dev *rtwdev, +			    struct rtw89_sar_cfg_acpi *cfg) +{ +	struct rtw89_sar_indicator_from_acpi *ind = &cfg->indicator; +	const struct rtw89_acpi_sar_recognition *rec; +	bool fetch_indicator = false; +	int ret; + +	rec = rtw89_acpi_evaluate_static_sar(rtwdev, cfg); +	if (rec) +		goto recognized; + +	rec = rtw89_acpi_evaluate_dynamic_sar(rtwdev, cfg); +	if (!rec) +		return -ENOENT; + +	fetch_indicator = true; + +recognized: +	rtw89_acpi_evaluate_geo_sar(rtwdev, rec->geo, cfg); + +	switch (rec->id.cid) { +	case RTW89_ACPI_SAR_CID_HP: +		cfg->downgrade_2tx = 3 << TXPWR_FACTOR_OF_RTW89_ACPI_SAR; +		ind->fields = RTW89_ACPI_SAR_ANT_NR_STD; +		break; +	case RTW89_ACPI_SAR_CID_RT: +		cfg->downgrade_2tx = 0; +		ind->fields = 1; +		break; +	default: +		return -EFAULT; +	} + +	if (fetch_indicator) { +		ind->rfpath_to_antidx = rec->rfpath_to_antidx; +		ret = rtw89_acpi_evaluate_dynamic_sar_indicator(rtwdev, cfg, NULL); +		if (ret) +			fetch_indicator = false; +	} + +	if (!fetch_indicator) +		memset(ind->tblsel, 0, sizeof(ind->tblsel)); + +	ind->enable_sync = fetch_indicator; +	return 0; +} diff --git a/sys/contrib/dev/rtw89/acpi.h b/sys/contrib/dev/rtw89/acpi.h index b43ab106e44d..48a46f2005b1 100644 --- a/sys/contrib/dev/rtw89/acpi.h +++ b/sys/contrib/dev/rtw89/acpi.h @@ -7,6 +7,11 @@  #include "core.h" +struct rtw89_acpi_data { +	u32 len; +	u8 buf[] __counted_by(len); +}; +  enum rtw89_acpi_dsm_func {  	RTW89_ACPI_DSM_FUNC_IDN_BAND_SUP = 2,  	RTW89_ACPI_DSM_FUNC_6G_DIS = 3, @@ -14,11 +19,13 @@ enum rtw89_acpi_dsm_func {  	RTW89_ACPI_DSM_FUNC_TAS_EN = 5,  	RTW89_ACPI_DSM_FUNC_UNII4_SUP = 6,  	RTW89_ACPI_DSM_FUNC_6GHZ_SP_SUP = 7, +	RTW89_ACPI_DSM_FUNC_REG_RULES_EN = 10, +	RTW89_ACPI_DSM_FUNC_6GHZ_VLP_SUP = 11,  };  enum rtw89_acpi_conf_unii4 { -	RTW89_ACPI_CONF_UNII4_FCC = BIT(0), -	RTW89_ACPI_CONF_UNII4_IC = BIT(1), +	RTW89_ACPI_CONF_UNII4_US = BIT(0), +	RTW89_ACPI_CONF_UNII4_CA = BIT(1),  };  enum rtw89_acpi_policy_mode { @@ -26,6 +33,13 @@ enum rtw89_acpi_policy_mode {  	RTW89_ACPI_POLICY_ALLOW = 1,  }; +enum rtw89_acpi_conf_tas { +	RTW89_ACPI_CONF_TAS_US = BIT(0), +	RTW89_ACPI_CONF_TAS_CA = BIT(1), +	RTW89_ACPI_CONF_TAS_KR = BIT(2), +	RTW89_ACPI_CONF_TAS_OTHERS = BIT(7), +}; +  struct rtw89_acpi_country_code {  	/* below are allowed:  	 * * ISO alpha2 country code @@ -44,6 +58,7 @@ struct rtw89_acpi_policy_6ghz {  enum rtw89_acpi_conf_6ghz_sp {  	RTW89_ACPI_CONF_6GHZ_SP_US = BIT(0), +	RTW89_ACPI_CONF_6GHZ_SP_CA = BIT(1),  };  struct rtw89_acpi_policy_6ghz_sp { @@ -54,12 +69,47 @@ struct rtw89_acpi_policy_6ghz_sp {  	u8 rsvd;  } __packed; +enum rtw89_acpi_conf_6ghz_vlp { +	RTW89_ACPI_CONF_6GHZ_VLP_US = BIT(0), +	RTW89_ACPI_CONF_6GHZ_VLP_CA = BIT(1), +}; + +struct rtw89_acpi_policy_6ghz_vlp { +	u8 signature[4]; +	u8 revision; +	u8 override; +	u8 conf; +	u8 rsvd; +} __packed; + +struct rtw89_acpi_policy_tas { +	u8 signature[4]; +	u8 revision; +	u8 enable; +	u8 enabled_countries; +	u8 rsvd[3]; +} __packed; + +enum rtw89_acpi_conf_reg_rules { +	RTW89_ACPI_CONF_REG_RULE_REGD_UK = BIT(0), +}; + +struct rtw89_acpi_policy_reg_rules { +	u8 signature[4]; +	u8 revision; +	u8 conf; +	u8 rsvd[3]; +} __packed; +  struct rtw89_acpi_dsm_result {  	union {  		u8 value;  		/* caller needs to free it after using */  		struct rtw89_acpi_policy_6ghz *policy_6ghz;  		struct rtw89_acpi_policy_6ghz_sp *policy_6ghz_sp; +		struct rtw89_acpi_policy_6ghz_vlp *policy_6ghz_vlp; +		struct rtw89_acpi_policy_tas *policy_tas; +		struct rtw89_acpi_policy_reg_rules *policy_reg_rules;  	} u;  }; @@ -70,10 +120,179 @@ struct rtw89_acpi_rtag_result {  	u8 ant_gain_table[RTW89_ANT_GAIN_CHAIN_NUM][RTW89_ANT_GAIN_SUBBAND_NR];  } __packed; +enum rtw89_acpi_sar_cid { +	RTW89_ACPI_SAR_CID_HP = 0x5048, +	RTW89_ACPI_SAR_CID_RT = 0x5452, +}; + +enum rtw89_acpi_sar_rev { +	RTW89_ACPI_SAR_REV_LEGACY = 1, +	RTW89_ACPI_SAR_REV_HAS_6GHZ = 2, +}; + +#define RTW89_ACPI_SAR_ANT_NR_STD 4 +#define RTW89_ACPI_SAR_ANT_NR_SML 2 + +#define RTW89_ACPI_METHOD_STATIC_SAR "WRDS" +#define RTW89_ACPI_METHOD_DYNAMIC_SAR "RWRD" +#define RTW89_ACPI_METHOD_DYNAMIC_SAR_INDICATOR "RWSI" +#define RTW89_ACPI_METHOD_GEO_SAR "RWGS" + +struct rtw89_acpi_sar_std_legacy { +	u8 v[RTW89_ACPI_SAR_ANT_NR_STD][RTW89_ACPI_SAR_SUBBAND_NR_LEGACY]; +} __packed; + +struct rtw89_acpi_sar_std_has_6ghz { +	u8 v[RTW89_ACPI_SAR_ANT_NR_STD][RTW89_ACPI_SAR_SUBBAND_NR_HAS_6GHZ]; +} __packed; + +struct rtw89_acpi_sar_sml_legacy { +	u8 v[RTW89_ACPI_SAR_ANT_NR_SML][RTW89_ACPI_SAR_SUBBAND_NR_LEGACY]; +} __packed; + +struct rtw89_acpi_sar_sml_has_6ghz { +	u8 v[RTW89_ACPI_SAR_ANT_NR_SML][RTW89_ACPI_SAR_SUBBAND_NR_HAS_6GHZ]; +} __packed; + +struct rtw89_acpi_static_sar_hdr { +	__le16 cid; +	u8 rev; +	u8 content[]; +} __packed; + +struct rtw89_acpi_dynamic_sar_hdr { +	__le16 cid; +	u8 rev; +	u8 cnt; +	u8 content[]; +} __packed; + +struct rtw89_acpi_sar_identifier { +	enum rtw89_acpi_sar_cid cid; +	enum rtw89_acpi_sar_rev rev; +	u8 size; +}; + +/* for rtw89_acpi_sar_identifier::size */ +#define RTW89_ACPI_SAR_SIZE_MAX U8_MAX +#define RTW89_ACPI_SAR_SIZE_OF(type) \ +	(BUILD_BUG_ON_ZERO(sizeof(struct rtw89_acpi_sar_ ## type) > \ +			   RTW89_ACPI_SAR_SIZE_MAX) + \ +	 sizeof(struct rtw89_acpi_sar_ ## type)) + +struct rtw89_acpi_sar_recognition { +	struct rtw89_acpi_sar_identifier id; +	const struct rtw89_acpi_geo_sar_handler *geo; + +	u8 (*rfpath_to_antidx)(enum rtw89_rf_path rfpath); +	s16 (*normalize)(u8 v); +	void (*load)(struct rtw89_dev *rtwdev, +		     const struct rtw89_acpi_sar_recognition *rec, +		     const void *content, +		     struct rtw89_sar_entry_from_acpi *ent); +}; + +struct rtw89_acpi_geo_sar_hp_val { +	u8 max; +	s8 delta[RTW89_ACPI_SAR_ANT_NR_STD]; +} __packed; + +struct rtw89_acpi_geo_sar_hp_legacy_entry { +	struct rtw89_acpi_geo_sar_hp_val val_2ghz; +	struct rtw89_acpi_geo_sar_hp_val val_5ghz; +} __packed; + +struct rtw89_acpi_geo_sar_hp_has_6ghz_entry { +	struct rtw89_acpi_geo_sar_hp_val val_2ghz; +	struct rtw89_acpi_geo_sar_hp_val val_5ghz; +	struct rtw89_acpi_geo_sar_hp_val val_6ghz; +} __packed; + +enum rtw89_acpi_geo_sar_regd_hp { +	RTW89_ACPI_GEO_SAR_REGD_HP_FCC = 0, +	RTW89_ACPI_GEO_SAR_REGD_HP_ETSI = 1, +	RTW89_ACPI_GEO_SAR_REGD_HP_WW = 2, + +	RTW89_ACPI_GEO_SAR_REGD_NR_HP, +}; + +struct rtw89_acpi_geo_sar_hp_legacy { +	struct rtw89_acpi_geo_sar_hp_legacy_entry +		entries[RTW89_ACPI_GEO_SAR_REGD_NR_HP]; +} __packed; + +struct rtw89_acpi_geo_sar_hp_has_6ghz { +	struct rtw89_acpi_geo_sar_hp_has_6ghz_entry +		entries[RTW89_ACPI_GEO_SAR_REGD_NR_HP]; +} __packed; + +struct rtw89_acpi_geo_sar_rt_val { +	u8 max; +	s8 delta; +} __packed; + +struct rtw89_acpi_geo_sar_rt_legacy_entry { +	struct rtw89_acpi_geo_sar_rt_val val_2ghz; +	struct rtw89_acpi_geo_sar_rt_val val_5ghz; +} __packed; + +struct rtw89_acpi_geo_sar_rt_has_6ghz_entry { +	struct rtw89_acpi_geo_sar_rt_val val_2ghz; +	struct rtw89_acpi_geo_sar_rt_val val_5ghz; +	struct rtw89_acpi_geo_sar_rt_val val_6ghz; +} __packed; + +enum rtw89_acpi_geo_sar_regd_rt { +	RTW89_ACPI_GEO_SAR_REGD_RT_FCC = 0, +	RTW89_ACPI_GEO_SAR_REGD_RT_ETSI = 1, +	RTW89_ACPI_GEO_SAR_REGD_RT_MKK = 2, +	RTW89_ACPI_GEO_SAR_REGD_RT_IC = 3, +	RTW89_ACPI_GEO_SAR_REGD_RT_KCC = 4, +	RTW89_ACPI_GEO_SAR_REGD_RT_WW = 5, + +	RTW89_ACPI_GEO_SAR_REGD_NR_RT, +}; + +struct rtw89_acpi_geo_sar_rt_legacy { +	struct rtw89_acpi_geo_sar_rt_legacy_entry +		entries[RTW89_ACPI_GEO_SAR_REGD_NR_RT]; +} __packed; + +struct rtw89_acpi_geo_sar_rt_has_6ghz { +	struct rtw89_acpi_geo_sar_rt_has_6ghz_entry +		entries[RTW89_ACPI_GEO_SAR_REGD_NR_RT]; +} __packed; + +struct rtw89_acpi_geo_sar_handler { +	u8 data_size; + +	void (*load)(struct rtw89_dev *rtwdev, +		     const void *content, +		     enum rtw89_regulation_type regd, +		     struct rtw89_sar_entry_from_acpi *ent); +}; + +/* for rtw89_acpi_geo_sar_handler::data_size */ +#define RTW89_ACPI_GEO_SAR_SIZE_MAX U8_MAX +#define RTW89_ACPI_GEO_SAR_SIZE_OF(type) \ +	(BUILD_BUG_ON_ZERO(sizeof(struct rtw89_acpi_geo_sar_ ## type) > \ +			   RTW89_ACPI_GEO_SAR_SIZE_MAX) + \ +	 sizeof(struct rtw89_acpi_geo_sar_ ## type)) + +enum rtw89_acpi_sar_subband rtw89_acpi_sar_get_subband(struct rtw89_dev *rtwdev, +						       u32 center_freq); +enum rtw89_band rtw89_acpi_sar_subband_to_band(struct rtw89_dev *rtwdev, +					       enum rtw89_acpi_sar_subband subband); +  int rtw89_acpi_evaluate_dsm(struct rtw89_dev *rtwdev,  			    enum rtw89_acpi_dsm_func func,  			    struct rtw89_acpi_dsm_result *res);  int rtw89_acpi_evaluate_rtag(struct rtw89_dev *rtwdev,  			     struct rtw89_acpi_rtag_result *res); +int rtw89_acpi_evaluate_sar(struct rtw89_dev *rtwdev, +			    struct rtw89_sar_cfg_acpi *cfg); +int rtw89_acpi_evaluate_dynamic_sar_indicator(struct rtw89_dev *rtwdev, +					      struct rtw89_sar_cfg_acpi *cfg, +					      bool *changed);  #endif diff --git a/sys/contrib/dev/rtw89/cam.c b/sys/contrib/dev/rtw89/cam.c index 8fa1e6c1ce13..385a238fe5cc 100644 --- a/sys/contrib/dev/rtw89/cam.c +++ b/sys/contrib/dev/rtw89/cam.c @@ -6,6 +6,7 @@  #include "debug.h"  #include "fw.h"  #include "mac.h" +#include "ps.h"  static struct sk_buff *  rtw89_cam_get_sec_key_cmd(struct rtw89_dev *rtwdev, @@ -469,13 +470,25 @@ int rtw89_cam_sec_key_add(struct rtw89_dev *rtwdev,  	bool ext_key = false;  	int ret; +	if (ieee80211_vif_is_mld(vif) && !chip->hw_mlo_bmc_crypto && +	    !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) +		return -EOPNOTSUPP; +  	switch (key->cipher) {  	case WLAN_CIPHER_SUITE_WEP40: +		rtw89_leave_ips_by_hwflags(rtwdev);  		hw_key_type = RTW89_SEC_KEY_TYPE_WEP40;  		break;  	case WLAN_CIPHER_SUITE_WEP104: +		rtw89_leave_ips_by_hwflags(rtwdev);  		hw_key_type = RTW89_SEC_KEY_TYPE_WEP104;  		break; +	case WLAN_CIPHER_SUITE_TKIP: +		if (!chip->hw_tkip_crypto) +			return -EOPNOTSUPP; +		hw_key_type = RTW89_SEC_KEY_TYPE_TKIP; +		key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; +		break;  	case WLAN_CIPHER_SUITE_CCMP:  		hw_key_type = RTW89_SEC_KEY_TYPE_CCMP128;  		if (!chip->hw_mgmt_tx_encrypt) diff --git a/sys/contrib/dev/rtw89/chan.c b/sys/contrib/dev/rtw89/chan.c index 257331c2de2e..bbdae184a0df 100644 --- a/sys/contrib/dev/rtw89/chan.c +++ b/sys/contrib/dev/rtw89/chan.c @@ -7,7 +7,9 @@  #include "debug.h"  #include "fw.h"  #include "mac.h" +#include "phy.h"  #include "ps.h" +#include "sar.h"  #include "util.h"  static void rtw89_swap_chanctx(struct rtw89_dev *rtwdev, @@ -127,6 +129,48 @@ void rtw89_chan_create(struct rtw89_chan *chan, u8 center_chan, u8 primary_chan,  						    bandwidth);  } +static void _rtw89_chan_update_punctured(struct rtw89_dev *rtwdev, +					 struct rtw89_vif_link *rtwvif_link, +					 const struct cfg80211_chan_def *chandef) +{ +	struct ieee80211_bss_conf *bss_conf; + +	if (rtwvif_link->wifi_role != RTW89_WIFI_ROLE_STATION && +	    rtwvif_link->wifi_role != RTW89_WIFI_ROLE_P2P_CLIENT) +		return; + +	rcu_read_lock(); + +	bss_conf = rtw89_vif_rcu_dereference_link(rtwvif_link, true); +	if (!bss_conf->eht_support) { +		rcu_read_unlock(); +		return; +	} + +	rcu_read_unlock(); + +	rtw89_chip_h2c_punctured_cmac_tbl(rtwdev, rtwvif_link, chandef->punctured); +} + +static void rtw89_chan_update_punctured(struct rtw89_dev *rtwdev, +					enum rtw89_chanctx_idx idx, +					const struct cfg80211_chan_def *chandef) +{ +	struct rtw89_vif_link *rtwvif_link; +	struct rtw89_vif *rtwvif; +	unsigned int link_id; + +	rtw89_for_each_rtwvif(rtwdev, rtwvif) { +		rtw89_vif_for_each_link(rtwvif, rtwvif_link, link_id) { +			if (!rtwvif_link->chanctx_assigned || +			    rtwvif_link->chanctx_idx != idx) +				continue; + +			_rtw89_chan_update_punctured(rtwdev, rtwvif_link, chandef); +		} +	} +} +  bool rtw89_assign_entity_chan(struct rtw89_dev *rtwdev,  			      enum rtw89_chanctx_idx idx,  			      const struct rtw89_chan *new) @@ -155,7 +199,7 @@ int rtw89_iterate_entity_chan(struct rtw89_dev *rtwdev,  	int ret;  	u8 idx; -	lockdep_assert_held(&rtwdev->mutex); +	lockdep_assert_wiphy(rtwdev->hw->wiphy);  	for_each_set_bit(idx,  hal->entity_map, NUM_OF_RTW89_CHANCTX) {  		chan = rtw89_chan_get(rtwdev, idx); @@ -169,28 +213,33 @@ int rtw89_iterate_entity_chan(struct rtw89_dev *rtwdev,  static void __rtw89_config_entity_chandef(struct rtw89_dev *rtwdev,  					  enum rtw89_chanctx_idx idx, -					  const struct cfg80211_chan_def *chandef, -					  bool from_stack) +					  const struct cfg80211_chan_def *chandef)  {  	struct rtw89_hal *hal = &rtwdev->hal;  	hal->chanctx[idx].chandef = *chandef; - -	if (from_stack) -		set_bit(idx, hal->entity_map);  }  void rtw89_config_entity_chandef(struct rtw89_dev *rtwdev,  				 enum rtw89_chanctx_idx idx,  				 const struct cfg80211_chan_def *chandef)  { -	__rtw89_config_entity_chandef(rtwdev, idx, chandef, true); +	struct rtw89_hal *hal = &rtwdev->hal; + +	if (!chandef) { +		clear_bit(idx, hal->entity_map); +		return; +	} + +	__rtw89_config_entity_chandef(rtwdev, idx, chandef); +	set_bit(idx, hal->entity_map);  }  void rtw89_config_roc_chandef(struct rtw89_dev *rtwdev, -			      enum rtw89_chanctx_idx idx, +			      struct rtw89_vif_link *rtwvif_link,  			      const struct cfg80211_chan_def *chandef)  { +	enum rtw89_chanctx_idx idx = rtwvif_link->chanctx_idx;  	struct rtw89_hal *hal = &rtwdev->hal;  	enum rtw89_chanctx_idx cur; @@ -204,6 +253,7 @@ void rtw89_config_roc_chandef(struct rtw89_dev *rtwdev,  		}  		hal->roc_chandef = *chandef; +		hal->roc_link_index = rtw89_vif_link_inst_get_index(rtwvif_link);  	} else {  		cur = atomic_cmpxchg(&hal->roc_chanctx_idx, idx,  				     RTW89_CHANCTX_IDLE); @@ -224,7 +274,7 @@ static void rtw89_config_default_chandef(struct rtw89_dev *rtwdev)  	struct cfg80211_chan_def chandef = {0};  	rtw89_get_default_chandef(&chandef); -	__rtw89_config_entity_chandef(rtwdev, RTW89_CHANCTX_0, &chandef, false); +	__rtw89_config_entity_chandef(rtwdev, RTW89_CHANCTX_0, &chandef);  }  void rtw89_entity_init(struct rtw89_dev *rtwdev) @@ -262,6 +312,8 @@ static void rtw89_entity_calculate_weight(struct rtw89_dev *rtwdev,  	struct rtw89_vif *rtwvif;  	int idx; +	w->registered_chanctxs = bitmap_weight(hal->entity_map, NUM_OF_RTW89_CHANCTX); +  	for_each_set_bit(idx, hal->entity_map, NUM_OF_RTW89_CHANCTX) {  		cfg = hal->chanctx[idx].cfg;  		if (!cfg) { @@ -310,7 +362,7 @@ const struct rtw89_chan *__rtw89_mgnt_chan_get(struct rtw89_dev *rtwdev,  	enum rtw89_entity_mode mode;  	u8 role_index; -	lockdep_assert_held(&rtwdev->mutex); +	lockdep_assert_wiphy(rtwdev->hw->wiphy);  	if (unlikely(link_index >= __RTW89_MLD_MAX_LINK_NUM)) {  		WARN(1, "link index %u is invalid (max link inst num: %d)\n", @@ -338,11 +390,10 @@ const struct rtw89_chan *__rtw89_mgnt_chan_get(struct rtw89_dev *rtwdev,  	roc_idx = atomic_read(&hal->roc_chanctx_idx);  	if (roc_idx != RTW89_CHANCTX_IDLE) { -		/* ROC is ongoing (given ROC runs on RTW89_ROC_BY_LINK_INDEX). -		 * If @link_index is the same as RTW89_ROC_BY_LINK_INDEX, get -		 * the ongoing ROC chanctx. +		/* ROC is ongoing (given ROC runs on @hal->roc_link_index). +		 * If @link_index is the same, get the ongoing ROC chanctx.  		 */ -		if (link_index == RTW89_ROC_BY_LINK_INDEX) +		if (link_index == hal->roc_link_index)  			chanctx_idx = roc_idx;  	} @@ -357,16 +408,45 @@ dflt:  }  EXPORT_SYMBOL(__rtw89_mgnt_chan_get); +static enum rtw89_mlo_dbcc_mode +rtw89_entity_sel_mlo_dbcc_mode(struct rtw89_dev *rtwdev, u8 active_hws) +{ +	if (rtwdev->chip->chip_gen != RTW89_CHIP_BE) +		return MLO_DBCC_NOT_SUPPORT; + +	switch (active_hws) { +	case BIT(0): +		return MLO_2_PLUS_0_1RF; +	case BIT(1): +		return MLO_0_PLUS_2_1RF; +	case BIT(0) | BIT(1): +	default: +		return MLO_1_PLUS_1_1RF; +	} +} + +static +void rtw89_entity_recalc_mlo_dbcc_mode(struct rtw89_dev *rtwdev, u8 active_hws) +{ +	enum rtw89_mlo_dbcc_mode mode; + +	mode = rtw89_entity_sel_mlo_dbcc_mode(rtwdev, active_hws); +	rtwdev->mlo_dbcc_mode = mode; + +	rtw89_debug(rtwdev, RTW89_DBG_STATE, "recalc mlo dbcc mode to %d\n", mode); +} +  static void rtw89_entity_recalc_mgnt_roles(struct rtw89_dev *rtwdev)  {  	struct rtw89_hal *hal = &rtwdev->hal;  	struct rtw89_entity_mgnt *mgnt = &hal->entity_mgnt;  	struct rtw89_vif_link *link;  	struct rtw89_vif *role; +	u8 active_hws = 0;  	u8 pos = 0;  	int i, j; -	lockdep_assert_held(&rtwdev->mutex); +	lockdep_assert_wiphy(rtwdev->hw->wiphy);  	for (i = 0; i < RTW89_MAX_INTERFACE_NUM; i++)  		mgnt->active_roles[i] = NULL; @@ -411,10 +491,13 @@ fill:  				continue;  			mgnt->chanctx_tbl[pos][i] = link->chanctx_idx; +			active_hws |= BIT(i);  		}  		mgnt->active_roles[pos++] = role;  	} + +	rtw89_entity_recalc_mlo_dbcc_mode(rtwdev, active_hws);  }  enum rtw89_entity_mode rtw89_entity_recalc(struct rtw89_dev *rtwdev) @@ -427,7 +510,7 @@ enum rtw89_entity_mode rtw89_entity_recalc(struct rtw89_dev *rtwdev)  	struct rtw89_chan chan;  	u8 idx; -	lockdep_assert_held(&rtwdev->mutex); +	lockdep_assert_wiphy(rtwdev->hw->wiphy);  	bitmap_copy(recalc_map, hal->entity_map, NUM_OF_RTW89_CHANCTX); @@ -439,7 +522,8 @@ enum rtw89_entity_mode rtw89_entity_recalc(struct rtw89_dev *rtwdev)  		bitmap_zero(recalc_map, NUM_OF_RTW89_CHANCTX);  		fallthrough;  	case 0: -		rtw89_config_default_chandef(rtwdev); +		if (!w.registered_chanctxs) +			rtw89_config_default_chandef(rtwdev);  		set_bit(RTW89_CHANCTX_0, recalc_map);  		fallthrough;  	case 1: @@ -556,7 +640,9 @@ static u32 rtw89_mcc_get_tbtt_ofst(struct rtw89_dev *rtwdev,  	u64 sync_tsf = READ_ONCE(rtwvif_link->sync_bcn_tsf);  	u32 remainder; -	if (tsf < sync_tsf) { +	if (role->is_go) { +		sync_tsf = 0; +	} else if (tsf < sync_tsf) {  		rtw89_debug(rtwdev, RTW89_DBG_CHAN,  			    "MCC get tbtt ofst: tsf might not update yet\n");  		sync_tsf = 0; @@ -690,19 +776,13 @@ static void rtw89_mcc_role_macid_sta_iter(void *data, struct ieee80211_sta *sta)  	struct rtw89_vif *target = mcc_role->rtwvif_link->rtwvif;  	struct rtw89_sta *rtwsta = sta_to_rtwsta(sta);  	struct rtw89_vif *rtwvif = rtwsta->rtwvif; -	struct rtw89_dev *rtwdev = rtwsta->rtwdev; -	struct rtw89_sta_link *rtwsta_link; +	u8 macid;  	if (rtwvif != target)  		return; -	rtwsta_link = rtw89_sta_get_link_inst(rtwsta, 0); -	if (unlikely(!rtwsta_link)) { -		rtw89_err(rtwdev, "mcc sta macid: find no link on HW-0\n"); -		return; -	} - -	rtw89_mcc_role_fw_macid_bitmap_set_bit(mcc_role, rtwsta_link->mac_id); +	macid = rtw89_sta_get_main_macid(rtwsta); +	rtw89_mcc_role_fw_macid_bitmap_set_bit(mcc_role, macid);  }  static void rtw89_mcc_fill_role_macid_bitmap(struct rtw89_dev *rtwdev, @@ -746,9 +826,11 @@ static void rtw89_mcc_fill_role_limit(struct rtw89_dev *rtwdev,  	int ret;  	int i; -	if (!mcc_role->is_go && !mcc_role->is_gc) +	if (!mcc_role->is_gc)  		return; +	rtw89_p2p_noa_once_recalc(rtwvif_link); +  	rcu_read_lock();  	bss_conf = rtw89_vif_rcu_dereference_link(rtwvif_link, true); @@ -784,6 +866,9 @@ fill:  	}  	tsf_lmt = (tsf & GENMASK_ULL(63, 32)) | start_time; +	if (tsf_lmt < tsf) +		tsf_lmt += roundup_u64(tsf - tsf_lmt, interval); +  	max_toa_us = rtw89_mcc_get_tbtt_ofst(rtwdev, mcc_role, tsf_lmt);  	max_dur_us = interval - duration;  	max_tob_us = max_dur_us - max_toa_us; @@ -918,6 +1003,7 @@ static int rtw89_mcc_fill_all_roles(struct rtw89_dev *rtwdev)  		}  		sel.bind_vif[i] = rtwvif_link; +		rtw89_p2p_disable_all_noa(rtwdev, rtwvif_link, NULL);  	}  	ret = rtw89_iterate_mcc_roles(rtwdev, rtw89_mcc_fill_role_iterator, &sel); @@ -928,6 +1014,15 @@ static int rtw89_mcc_fill_all_roles(struct rtw89_dev *rtwdev)  	return 0;  } +static bool rtw89_mcc_can_courtesy(const struct rtw89_mcc_role *provider, +				   const struct rtw89_mcc_role *receiver) +{ +	if (provider->is_go || receiver->is_gc) +		return false; + +	return true; +} +  static void rtw89_mcc_assign_pattern(struct rtw89_dev *rtwdev,  				     const struct rtw89_mcc_pattern *new)  { @@ -936,37 +1031,54 @@ static void rtw89_mcc_assign_pattern(struct rtw89_dev *rtwdev,  	struct rtw89_mcc_role *aux = &mcc->role_aux;  	struct rtw89_mcc_config *config = &mcc->config;  	struct rtw89_mcc_pattern *pattern = &config->pattern; +	struct rtw89_mcc_courtesy_cfg *crtz;  	rtw89_debug(rtwdev, RTW89_DBG_CHAN,  		    "MCC assign pattern: ref {%d | %d}, aux {%d | %d}\n",  		    new->tob_ref, new->toa_ref, new->tob_aux, new->toa_aux); +	rtw89_debug(rtwdev, RTW89_DBG_CHAN, "MCC pattern plan: %d\n", new->plan); +  	*pattern = *new;  	memset(&pattern->courtesy, 0, sizeof(pattern->courtesy)); -	if (pattern->tob_aux <= 0 || pattern->toa_aux <= 0) { -		pattern->courtesy.macid_tgt = aux->rtwvif_link->mac_id; -		pattern->courtesy.macid_src = ref->rtwvif_link->mac_id; -		pattern->courtesy.slot_num = RTW89_MCC_DFLT_COURTESY_SLOT; -		pattern->courtesy.enable = true; -	} else if (pattern->tob_ref <= 0 || pattern->toa_ref <= 0) { -		pattern->courtesy.macid_tgt = ref->rtwvif_link->mac_id; -		pattern->courtesy.macid_src = aux->rtwvif_link->mac_id; -		pattern->courtesy.slot_num = RTW89_MCC_DFLT_COURTESY_SLOT; -		pattern->courtesy.enable = true; +	if (RTW89_MCC_REQ_COURTESY(pattern, aux) && aux->is_gc) +		aux->ignore_bcn = true; +	else +		aux->ignore_bcn = false; + +	if (RTW89_MCC_REQ_COURTESY(pattern, aux) && rtw89_mcc_can_courtesy(ref, aux)) { +		crtz = &pattern->courtesy.ref; +		ref->crtz = crtz; + +		crtz->macid_tgt = aux->rtwvif_link->mac_id; +		crtz->slot_num = RTW89_MCC_DFLT_COURTESY_SLOT; + +		rtw89_debug(rtwdev, RTW89_DBG_CHAN, +			    "MCC courtesy ref: tgt %d, slot %d\n", +			    crtz->macid_tgt, crtz->slot_num); +	} else { +		ref->crtz = NULL;  	} -	rtw89_debug(rtwdev, RTW89_DBG_CHAN, -		    "MCC pattern flags: plan %d, courtesy_en %d\n", -		    pattern->plan, pattern->courtesy.enable); +	if (RTW89_MCC_REQ_COURTESY(pattern, ref) && ref->is_gc) +		ref->ignore_bcn = true; +	else +		ref->ignore_bcn = false; -	if (!pattern->courtesy.enable) -		return; +	if (RTW89_MCC_REQ_COURTESY(pattern, ref) && rtw89_mcc_can_courtesy(aux, ref)) { +		crtz = &pattern->courtesy.aux; +		aux->crtz = crtz; -	rtw89_debug(rtwdev, RTW89_DBG_CHAN, -		    "MCC pattern courtesy: tgt %d, src %d, slot %d\n", -		    pattern->courtesy.macid_tgt, pattern->courtesy.macid_src, -		    pattern->courtesy.slot_num); +		crtz->macid_tgt = ref->rtwvif_link->mac_id; +		crtz->slot_num = RTW89_MCC_DFLT_COURTESY_SLOT; + +		rtw89_debug(rtwdev, RTW89_DBG_CHAN, +			    "MCC courtesy aux: tgt %d, slot %d\n", +			    crtz->macid_tgt, crtz->slot_num); +	} else { +		aux->crtz = NULL; +	}  }  /* The follow-up roughly shows the relationship between the parameters @@ -991,6 +1103,7 @@ static void __rtw89_mcc_calc_pattern_loose(struct rtw89_dev *rtwdev,  	struct rtw89_mcc_role *ref = &mcc->role_ref;  	struct rtw89_mcc_role *aux = &mcc->role_aux;  	struct rtw89_mcc_config *config = &mcc->config; +	u16 mcc_intvl = config->mcc_interval;  	u16 bcn_ofst = config->beacon_offset;  	u16 bt_dur_in_mid = 0;  	u16 max_bcn_ofst; @@ -1024,7 +1137,7 @@ calc:  	res = bcn_ofst - bt_dur_in_mid;  	upper = min_t(s16, ref->duration, res); -	lower = 0; +	lower = max_t(s16, 0, ref->duration - (mcc_intvl - bcn_ofst));  	if (ref->limit.enable) {  		upper = min_t(s16, upper, ref->limit.max_toa); @@ -1055,7 +1168,7 @@ static int __rtw89_mcc_calc_pattern_strict(struct rtw89_dev *rtwdev,  	struct rtw89_mcc_role *ref = &mcc->role_ref;  	struct rtw89_mcc_role *aux = &mcc->role_aux;  	struct rtw89_mcc_config *config = &mcc->config; -	u16 min_tob = RTW89_MCC_EARLY_RX_BCN_TIME; +	u16 min_tob = RTW89_MCC_EARLY_RX_BCN_TIME + RTW89_MCC_SWITCH_CH_TIME;  	u16 min_toa = RTW89_MCC_MIN_RX_BCN_TIME;  	u16 bcn_ofst = config->beacon_offset;  	s16 upper_toa_ref, lower_toa_ref; @@ -1135,6 +1248,109 @@ static int __rtw89_mcc_calc_pattern_strict(struct rtw89_dev *rtwdev,  	return 0;  } +static void __rtw89_mcc_fill_ptrn_anchor_ref(struct rtw89_dev *rtwdev, +					     struct rtw89_mcc_pattern *ptrn, +					     bool small_bcn_ofst) +{ +	struct rtw89_mcc_info *mcc = &rtwdev->mcc; +	struct rtw89_mcc_role *ref = &mcc->role_ref; +	struct rtw89_mcc_role *aux = &mcc->role_aux; +	struct rtw89_mcc_config *config = &mcc->config; +	u16 bcn_ofst = config->beacon_offset; +	u16 ref_tob; +	u16 ref_toa; + +	if (ref->limit.enable) { +		ref_tob = ref->limit.max_tob; +		ref_toa = ref->limit.max_toa; +	} else { +		ref_tob = ref->duration / 2; +		ref_toa = ref->duration / 2; +	} + +	if (small_bcn_ofst) { +		ptrn->toa_ref = ref_toa; +		ptrn->tob_ref = ref->duration - ptrn->toa_ref; +	} else { +		ptrn->tob_ref = ref_tob; +		ptrn->toa_ref = ref->duration - ptrn->tob_ref; +	} + +	ptrn->tob_aux = bcn_ofst - ptrn->toa_ref; +	ptrn->toa_aux = aux->duration - ptrn->tob_aux; +} + +static void __rtw89_mcc_fill_ptrn_anchor_aux(struct rtw89_dev *rtwdev, +					     struct rtw89_mcc_pattern *ptrn, +					     bool small_bcn_ofst) +{ +	struct rtw89_mcc_info *mcc = &rtwdev->mcc; +	struct rtw89_mcc_role *ref = &mcc->role_ref; +	struct rtw89_mcc_role *aux = &mcc->role_aux; +	struct rtw89_mcc_config *config = &mcc->config; +	u16 bcn_ofst = config->beacon_offset; +	u16 aux_tob; +	u16 aux_toa; + +	if (aux->limit.enable) { +		aux_tob = aux->limit.max_tob; +		aux_toa = aux->limit.max_toa; +	} else { +		aux_tob = aux->duration / 2; +		aux_toa = aux->duration / 2; +	} + +	if (small_bcn_ofst) { +		ptrn->tob_aux = aux_tob; +		ptrn->toa_aux = aux->duration - ptrn->tob_aux; +	} else { +		ptrn->toa_aux = aux_toa; +		ptrn->tob_aux = aux->duration - ptrn->toa_aux; +	} + +	ptrn->toa_ref = bcn_ofst - ptrn->tob_aux; +	ptrn->tob_ref = ref->duration - ptrn->toa_ref; +} + +static int __rtw89_mcc_calc_pattern_anchor(struct rtw89_dev *rtwdev, +					   struct rtw89_mcc_pattern *ptrn, +					   bool hdl_bt) +{ +	struct rtw89_mcc_info *mcc = &rtwdev->mcc; +	struct rtw89_mcc_role *ref = &mcc->role_ref; +	struct rtw89_mcc_role *aux = &mcc->role_aux; +	struct rtw89_mcc_config *config = &mcc->config; +	u16 mcc_intvl = config->mcc_interval; +	u16 bcn_ofst = config->beacon_offset; +	bool small_bcn_ofst; + +	if (bcn_ofst < RTW89_MCC_MIN_RX_BCN_WITH_SWITCH_CH_TIME) +		small_bcn_ofst = true; +	else if (bcn_ofst < aux->duration - aux->limit.max_toa) +		small_bcn_ofst = true; +	else if (mcc_intvl - bcn_ofst < RTW89_MCC_MIN_RX_BCN_WITH_SWITCH_CH_TIME) +		small_bcn_ofst = false; +	else +		return -EPERM; + +	*ptrn = (typeof(*ptrn)){ +		.plan = hdl_bt ? RTW89_MCC_PLAN_TAIL_BT : RTW89_MCC_PLAN_NO_BT, +	}; + +	rtw89_debug(rtwdev, RTW89_DBG_CHAN, +		    "MCC calc ptrn_ac: plan %d, bcn_ofst %d\n", +		    ptrn->plan, bcn_ofst); + +	if (ref->is_go || ref->is_gc) +		__rtw89_mcc_fill_ptrn_anchor_ref(rtwdev, ptrn, small_bcn_ofst); +	else if (aux->is_go || aux->is_gc) +		__rtw89_mcc_fill_ptrn_anchor_aux(rtwdev, ptrn, small_bcn_ofst); +	else +		__rtw89_mcc_fill_ptrn_anchor_ref(rtwdev, ptrn, small_bcn_ofst); + +	return 0; +} +  static int rtw89_mcc_calc_pattern(struct rtw89_dev *rtwdev, bool hdl_bt)  {  	struct rtw89_mcc_info *mcc = &rtwdev->mcc; @@ -1188,6 +1404,10 @@ static int rtw89_mcc_calc_pattern(struct rtw89_dev *rtwdev, bool hdl_bt)  			goto done;  	} +	ret = __rtw89_mcc_calc_pattern_anchor(rtwdev, &ptrn, hdl_bt); +	if (!ret) +		goto done; +  	__rtw89_mcc_calc_pattern_loose(rtwdev, &ptrn, hdl_bt);  done: @@ -1438,88 +1658,72 @@ static bool rtw89_mcc_duration_decision_on_bt(struct rtw89_dev *rtwdev)  	return false;  } -static void rtw89_mcc_sync_tbtt(struct rtw89_dev *rtwdev, -				struct rtw89_mcc_role *tgt, -				struct rtw89_mcc_role *src, -				bool ref_is_src) +void rtw89_mcc_prepare_done_work(struct wiphy *wiphy, struct wiphy_work *work)  { -	struct rtw89_mcc_info *mcc = &rtwdev->mcc; -	struct rtw89_mcc_config *config = &mcc->config; -	u16 beacon_offset_us = ieee80211_tu_to_usec(config->beacon_offset); -	u32 bcn_intvl_src_us = ieee80211_tu_to_usec(src->beacon_interval); -	u32 cur_tbtt_ofst_src; -	u32 tsf_ofst_tgt; -	u32 remainder; -	u64 tbtt_tgt; -	u64 tsf_src; -	int ret; - -	ret = rtw89_mac_port_get_tsf(rtwdev, src->rtwvif_link, &tsf_src); -	if (ret) { -		rtw89_warn(rtwdev, "MCC failed to get port tsf: %d\n", ret); -		return; -	} - -	cur_tbtt_ofst_src = rtw89_mcc_get_tbtt_ofst(rtwdev, src, tsf_src); +	struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, +						mcc_prepare_done_work.work); -	if (ref_is_src) -		tbtt_tgt = tsf_src - cur_tbtt_ofst_src + beacon_offset_us; -	else -		tbtt_tgt = tsf_src - cur_tbtt_ofst_src + -			   (bcn_intvl_src_us - beacon_offset_us); +	lockdep_assert_wiphy(wiphy); -	div_u64_rem(tbtt_tgt, bcn_intvl_src_us, &remainder); -	tsf_ofst_tgt = bcn_intvl_src_us - remainder; +	ieee80211_wake_queues(rtwdev->hw); +} -	config->sync.macid_tgt = tgt->rtwvif_link->mac_id; -	config->sync.band_tgt = tgt->rtwvif_link->mac_idx; -	config->sync.port_tgt = tgt->rtwvif_link->port; -	config->sync.macid_src = src->rtwvif_link->mac_id; -	config->sync.band_src = src->rtwvif_link->mac_idx; -	config->sync.port_src = src->rtwvif_link->port; -	config->sync.offset = tsf_ofst_tgt / 1024; -	config->sync.enable = true; +static void rtw89_mcc_prepare(struct rtw89_dev *rtwdev, bool start) +{ +	struct rtw89_mcc_info *mcc = &rtwdev->mcc; +	struct rtw89_mcc_config *config = &mcc->config; -	rtw89_debug(rtwdev, RTW89_DBG_CHAN, -		    "MCC sync tbtt: tgt %d, src %d, offset %d\n", -		    config->sync.macid_tgt, config->sync.macid_src, -		    config->sync.offset); +	if (start) { +		ieee80211_stop_queues(rtwdev->hw); -	rtw89_mac_port_tsf_sync(rtwdev, tgt->rtwvif_link, src->rtwvif_link, -				config->sync.offset); +		wiphy_delayed_work_queue(rtwdev->hw->wiphy, +					 &rtwdev->mcc_prepare_done_work, +					 usecs_to_jiffies(config->prepare_delay)); +	} else { +		wiphy_delayed_work_queue(rtwdev->hw->wiphy, +					 &rtwdev->mcc_prepare_done_work, 0); +		wiphy_delayed_work_flush(rtwdev->hw->wiphy, +					 &rtwdev->mcc_prepare_done_work); +	}  }  static int rtw89_mcc_fill_start_tsf(struct rtw89_dev *rtwdev)  {  	struct rtw89_mcc_info *mcc = &rtwdev->mcc;  	struct rtw89_mcc_role *ref = &mcc->role_ref; +	struct rtw89_mcc_role *aux = &mcc->role_aux;  	struct rtw89_mcc_config *config = &mcc->config;  	u32 bcn_intvl_ref_us = ieee80211_tu_to_usec(ref->beacon_interval); -	u32 tob_ref_us = ieee80211_tu_to_usec(config->pattern.tob_ref); -	struct rtw89_vif_link *rtwvif_link = ref->rtwvif_link; +	s32 tob_ref_us = ieee80211_tu_to_usec(config->pattern.tob_ref);  	u64 tsf, start_tsf;  	u32 cur_tbtt_ofst;  	u64 min_time; +	u64 tsf_aux;  	int ret; -	ret = rtw89_mac_port_get_tsf(rtwdev, rtwvif_link, &tsf); -	if (ret) { -		rtw89_warn(rtwdev, "MCC failed to get port tsf: %d\n", ret); +	if (rtw89_concurrent_via_mrc(rtwdev)) +		ret = __mrc_fw_req_tsf(rtwdev, &tsf, &tsf_aux); +	else +		ret = __mcc_fw_req_tsf(rtwdev, &tsf, &tsf_aux); + +	if (ret)  		return ret; -	}  	min_time = tsf; -	if (ref->is_go) +	if (ref->is_go || aux->is_go)  		min_time += ieee80211_tu_to_usec(RTW89_MCC_SHORT_TRIGGER_TIME);  	else  		min_time += ieee80211_tu_to_usec(RTW89_MCC_LONG_TRIGGER_TIME);  	cur_tbtt_ofst = rtw89_mcc_get_tbtt_ofst(rtwdev, ref, tsf);  	start_tsf = tsf - cur_tbtt_ofst + bcn_intvl_ref_us - tob_ref_us; -	while (start_tsf < min_time) -		start_tsf += bcn_intvl_ref_us; +	if (start_tsf < min_time) +		start_tsf += roundup_u64(min_time - start_tsf, bcn_intvl_ref_us);  	config->start_tsf = start_tsf; +	config->start_tsf_in_aux_domain = tsf_aux + start_tsf - tsf; +	config->prepare_delay = start_tsf - tsf; +  	return 0;  } @@ -1536,13 +1740,11 @@ static int rtw89_mcc_fill_config(struct rtw89_dev *rtwdev)  	switch (mcc->mode) {  	case RTW89_MCC_MODE_GO_STA: -		config->beacon_offset = RTW89_MCC_DFLT_BCN_OFST_TIME; +		config->beacon_offset = rtw89_mcc_get_bcn_ofst(rtwdev);  		if (ref->is_go) { -			rtw89_mcc_sync_tbtt(rtwdev, ref, aux, false);  			config->mcc_interval = ref->beacon_interval;  			rtw89_mcc_set_duration_go_sta(rtwdev, ref, aux);  		} else { -			rtw89_mcc_sync_tbtt(rtwdev, aux, ref, true);  			config->mcc_interval = aux->beacon_interval;  			rtw89_mcc_set_duration_go_sta(rtwdev, aux, ref);  		} @@ -1572,10 +1774,8 @@ bottom:  static int __mcc_fw_add_role(struct rtw89_dev *rtwdev, struct rtw89_mcc_role *role)  { +	const struct rtw89_mcc_courtesy_cfg *crtz = role->crtz;  	struct rtw89_mcc_info *mcc = &rtwdev->mcc; -	struct rtw89_mcc_config *config = &mcc->config; -	struct rtw89_mcc_pattern *pattern = &config->pattern; -	struct rtw89_mcc_courtesy *courtesy = &pattern->courtesy;  	struct rtw89_mcc_policy *policy = &role->policy;  	struct rtw89_fw_mcc_add_req req = {};  	const struct rtw89_chan *chan; @@ -1598,9 +1798,9 @@ static int __mcc_fw_add_role(struct rtw89_dev *rtwdev, struct rtw89_mcc_role *ro  	req.duration = role->duration;  	req.btc_in_2g = false; -	if (courtesy->enable && courtesy->macid_src == req.macid) { -		req.courtesy_target = courtesy->macid_tgt; -		req.courtesy_num = courtesy->slot_num; +	if (crtz) { +		req.courtesy_target = crtz->macid_tgt; +		req.courtesy_num = crtz->slot_num;  		req.courtesy_en = true;  	} @@ -1780,26 +1980,23 @@ static void __mrc_fw_add_courtesy(struct rtw89_dev *rtwdev,  	struct rtw89_mcc_info *mcc = &rtwdev->mcc;  	struct rtw89_mcc_role *ref = &mcc->role_ref;  	struct rtw89_mcc_role *aux = &mcc->role_aux; -	struct rtw89_mcc_config *config = &mcc->config; -	struct rtw89_mcc_pattern *pattern = &config->pattern; -	struct rtw89_mcc_courtesy *courtesy = &pattern->courtesy;  	struct rtw89_fw_mrc_add_slot_arg *slot_arg_src; -	u8 slot_idx_tgt; - -	if (!courtesy->enable) -		return; -	if (courtesy->macid_src == ref->rtwvif_link->mac_id) { +	if (ref->crtz) {  		slot_arg_src = &arg->slots[ref->slot_idx]; -		slot_idx_tgt = aux->slot_idx; -	} else { -		slot_arg_src = &arg->slots[aux->slot_idx]; -		slot_idx_tgt = ref->slot_idx; + +		slot_arg_src->courtesy_target = aux->slot_idx; +		slot_arg_src->courtesy_period = ref->crtz->slot_num; +		slot_arg_src->courtesy_en = true;  	} -	slot_arg_src->courtesy_target = slot_idx_tgt; -	slot_arg_src->courtesy_period = courtesy->slot_num; -	slot_arg_src->courtesy_en = true; +	if (aux->crtz) { +		slot_arg_src = &arg->slots[aux->slot_idx]; + +		slot_arg_src->courtesy_target = ref->slot_idx; +		slot_arg_src->courtesy_period = aux->crtz->slot_num; +		slot_arg_src->courtesy_en = true; +	}  }  static int __mrc_fw_start(struct rtw89_dev *rtwdev, bool replace) @@ -1999,30 +2196,24 @@ static void rtw89_mcc_handle_beacon_noa(struct rtw89_dev *rtwdev, bool enable)  	struct rtw89_mcc_role *ref = &mcc->role_ref;  	struct rtw89_mcc_role *aux = &mcc->role_aux;  	struct rtw89_mcc_config *config = &mcc->config; -	struct rtw89_mcc_pattern *pattern = &config->pattern; -	struct rtw89_mcc_sync *sync = &config->sync;  	struct ieee80211_p2p_noa_desc noa_desc = {}; -	u64 start_time = config->start_tsf;  	u32 interval = config->mcc_interval;  	struct rtw89_vif_link *rtwvif_go; +	u64 start_time;  	u32 duration;  	if (mcc->mode != RTW89_MCC_MODE_GO_STA)  		return;  	if (ref->is_go) { +		start_time = config->start_tsf;  		rtwvif_go = ref->rtwvif_link;  		start_time += ieee80211_tu_to_usec(ref->duration);  		duration = config->mcc_interval - ref->duration;  	} else if (aux->is_go) { +		start_time = config->start_tsf_in_aux_domain;  		rtwvif_go = aux->rtwvif_link; -		start_time += ieee80211_tu_to_usec(pattern->tob_ref) + -			      ieee80211_tu_to_usec(config->beacon_offset) + -			      ieee80211_tu_to_usec(pattern->toa_aux);  		duration = config->mcc_interval - aux->duration; - -		/* convert time domain from sta(ref) to GO(aux) */ -		start_time += ieee80211_tu_to_usec(sync->offset);  	} else {  		rtw89_debug(rtwdev, RTW89_DBG_CHAN,  			    "MCC find no GO: skip updating beacon NoA\n"); @@ -2032,6 +2223,7 @@ static void rtw89_mcc_handle_beacon_noa(struct rtw89_dev *rtwdev, bool enable)  	rtw89_p2p_noa_renew(rtwvif_go);  	if (enable) { +		duration += RTW89_MCC_SWITCH_CH_TIME;  		noa_desc.start_time = cpu_to_le32(start_time);  		noa_desc.interval = cpu_to_le32(ieee80211_tu_to_usec(interval));  		noa_desc.duration = cpu_to_le32(ieee80211_tu_to_usec(duration)); @@ -2080,6 +2272,18 @@ static void rtw89_mcc_stop_beacon_noa(struct rtw89_dev *rtwdev)  	rtw89_mcc_handle_beacon_noa(rtwdev, false);  } +static bool rtw89_mcc_ignore_bcn(struct rtw89_dev *rtwdev, struct rtw89_mcc_role *role) +{ +	enum rtw89_chip_gen chip_gen = rtwdev->chip->chip_gen; + +	if (role->is_go) +		return true; +	else if (chip_gen == RTW89_CHIP_BE && role->is_gc) +		return true; +	else +		return false; +} +  static int rtw89_mcc_start(struct rtw89_dev *rtwdev)  {  	struct rtw89_mcc_info *mcc = &rtwdev->mcc; @@ -2111,6 +2315,15 @@ static int rtw89_mcc_start(struct rtw89_dev *rtwdev)  	if (ret)  		return ret; +	if (rtw89_mcc_ignore_bcn(rtwdev, ref) || aux->ignore_bcn) { +		rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, aux->rtwvif_link, false); +	} else if (rtw89_mcc_ignore_bcn(rtwdev, aux) || ref->ignore_bcn) { +		rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, ref->rtwvif_link, false); +	} else { +		rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, ref->rtwvif_link, true); +		rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, aux->rtwvif_link, true); +	} +  	if (rtw89_concurrent_via_mrc(rtwdev))  		ret = __mrc_fw_start(rtwdev, false);  	else @@ -2122,10 +2335,19 @@ static int rtw89_mcc_start(struct rtw89_dev *rtwdev)  	rtw89_chanctx_notify(rtwdev, RTW89_CHANCTX_STATE_MCC_START);  	rtw89_mcc_start_beacon_noa(rtwdev); +	rtw89_phy_dig_suspend(rtwdev); + +	rtw89_mcc_prepare(rtwdev, true);  	return 0;  }  struct rtw89_mcc_stop_sel { +	struct { +		const struct rtw89_vif_link *target; +	} hint; + +	/* selection content */ +	bool filled;  	u8 mac_id;  	u8 slot_idx;  }; @@ -2135,6 +2357,7 @@ static void rtw89_mcc_stop_sel_fill(struct rtw89_mcc_stop_sel *sel,  {  	sel->mac_id = mcc_role->rtwvif_link->mac_id;  	sel->slot_idx = mcc_role->slot_idx; +	sel->filled = true;  }  static int rtw89_mcc_stop_sel_iterator(struct rtw89_dev *rtwdev, @@ -2144,23 +2367,49 @@ static int rtw89_mcc_stop_sel_iterator(struct rtw89_dev *rtwdev,  {  	struct rtw89_mcc_stop_sel *sel = data; +	if (mcc_role->rtwvif_link == sel->hint.target) { +		rtw89_mcc_stop_sel_fill(sel, mcc_role); +		return 1; /* break iteration */ +	} + +	if (sel->filled) +		return 0; +  	if (!mcc_role->rtwvif_link->chanctx_assigned)  		return 0;  	rtw89_mcc_stop_sel_fill(sel, mcc_role); -	return 1; /* break iteration */ +	return 0;  } -static void rtw89_mcc_stop(struct rtw89_dev *rtwdev) +static void rtw89_mcc_stop(struct rtw89_dev *rtwdev, +			   const struct rtw89_chanctx_pause_parm *pause)  { +	struct rtw89_hal *hal = &rtwdev->hal;  	struct rtw89_mcc_info *mcc = &rtwdev->mcc;  	struct rtw89_mcc_role *ref = &mcc->role_ref; -	struct rtw89_mcc_stop_sel sel; +	struct rtw89_mcc_role *aux = &mcc->role_aux; +	struct rtw89_mcc_stop_sel sel = { +		.hint.target = pause ? pause->trigger : NULL, +	}; +	bool rsn_scan;  	int ret; +	if (!pause) { +		wiphy_delayed_work_cancel(rtwdev->hw->wiphy, &rtwdev->chanctx_work); +		bitmap_zero(hal->changes, NUM_OF_RTW89_CHANCTX_CHANGES); +	} + +	rsn_scan = pause && pause->rsn == RTW89_CHANCTX_PAUSE_REASON_HW_SCAN; +	if (rsn_scan && ref->is_go) +		sel.hint.target = ref->rtwvif_link; +	else if (rsn_scan && aux->is_go) +		sel.hint.target = aux->rtwvif_link; +  	/* by default, stop at ref */ -	rtw89_mcc_stop_sel_fill(&sel, ref);  	rtw89_iterate_mcc_roles(rtwdev, rtw89_mcc_stop_sel_iterator, &sel); +	if (!sel.filled) +		rtw89_mcc_stop_sel_fill(&sel, ref);  	rtw89_debug(rtwdev, RTW89_DBG_CHAN, "MCC stop at <macid %d>\n", sel.mac_id); @@ -2185,13 +2434,22 @@ static void rtw89_mcc_stop(struct rtw89_dev *rtwdev)  	rtw89_chanctx_notify(rtwdev, RTW89_CHANCTX_STATE_MCC_STOP);  	rtw89_mcc_stop_beacon_noa(rtwdev); +	rtw89_fw_h2c_mcc_dig(rtwdev, RTW89_CHANCTX_0, 0, 0, false); +	rtw89_phy_dig_resume(rtwdev, true); + +	rtw89_mcc_prepare(rtwdev, false);  }  static int rtw89_mcc_update(struct rtw89_dev *rtwdev)  {  	struct rtw89_mcc_info *mcc = &rtwdev->mcc; +	bool old_ref_ignore_bcn = mcc->role_ref.ignore_bcn; +	bool old_aux_ignore_bcn = mcc->role_aux.ignore_bcn;  	struct rtw89_mcc_config *config = &mcc->config; +	struct rtw89_mcc_role *ref = &mcc->role_ref; +	struct rtw89_mcc_role *aux = &mcc->role_aux;  	struct rtw89_mcc_config old_cfg = *config; +	bool courtesy_changed;  	bool sync_changed;  	int ret; @@ -2204,8 +2462,20 @@ static int rtw89_mcc_update(struct rtw89_dev *rtwdev)  	if (ret)  		return ret; +	if (old_ref_ignore_bcn != ref->ignore_bcn) +		rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, ref->rtwvif_link, !ref->ignore_bcn); +	else if (old_aux_ignore_bcn != aux->ignore_bcn) +		rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, aux->rtwvif_link, !aux->ignore_bcn); + +	if (memcmp(&old_cfg.pattern.courtesy, &config->pattern.courtesy, +		   sizeof(old_cfg.pattern.courtesy)) == 0) +		courtesy_changed = false; +	else +		courtesy_changed = true; +  	if (old_cfg.pattern.plan != RTW89_MCC_PLAN_NO_BT || -	    config->pattern.plan != RTW89_MCC_PLAN_NO_BT) { +	    config->pattern.plan != RTW89_MCC_PLAN_NO_BT || +	    courtesy_changed) {  		if (rtw89_concurrent_via_mrc(rtwdev))  			ret = __mrc_fw_start(rtwdev, true);  		else @@ -2232,31 +2502,167 @@ static int rtw89_mcc_update(struct rtw89_dev *rtwdev)  	return 0;  } +static int rtw89_mcc_search_gc_iterator(struct rtw89_dev *rtwdev, +					struct rtw89_mcc_role *mcc_role, +					unsigned int ordered_idx, +					void *data) +{ +	struct rtw89_mcc_role **role = data; + +	if (mcc_role->is_gc) +		*role = mcc_role; + +	return 0; +} + +static struct rtw89_mcc_role *rtw89_mcc_get_gc_role(struct rtw89_dev *rtwdev) +{ +	struct rtw89_mcc_info *mcc = &rtwdev->mcc; +	struct rtw89_mcc_role *role = NULL; + +	if (mcc->mode != RTW89_MCC_MODE_GC_STA) +		return NULL; + +	rtw89_iterate_mcc_roles(rtwdev, rtw89_mcc_search_gc_iterator, &role); + +	return role; +} + +void rtw89_mcc_gc_detect_beacon_work(struct wiphy *wiphy, struct wiphy_work *work) +{ +	struct rtw89_vif_link *rtwvif_link = container_of(work, struct rtw89_vif_link, +							  mcc_gc_detect_beacon_work.work); +	struct ieee80211_vif *vif = rtwvif_link_to_vif(rtwvif_link); +	enum rtw89_entity_mode mode; +	struct rtw89_dev *rtwdev; + +	lockdep_assert_wiphy(wiphy); + +	rtwdev = rtwvif_link->rtwvif->rtwdev; + +	mode = rtw89_get_entity_mode(rtwdev); +	if (mode != RTW89_ENTITY_MODE_MCC) +		return; + +	if (READ_ONCE(rtwvif_link->sync_bcn_tsf) > rtwvif_link->last_sync_bcn_tsf) +		rtwvif_link->detect_bcn_count = 0; +	else +		rtwvif_link->detect_bcn_count++; + +	if (rtwvif_link->detect_bcn_count < RTW89_MCC_DETECT_BCN_MAX_TRIES) +		rtw89_chanctx_proceed(rtwdev, NULL); +	else +		ieee80211_connection_loss(vif); +} + +bool rtw89_mcc_detect_go_bcn(struct rtw89_dev *rtwdev, +			     struct rtw89_vif_link *rtwvif_link) +{ +	enum rtw89_entity_mode mode = rtw89_get_entity_mode(rtwdev); +	struct rtw89_chanctx_pause_parm pause_parm = { +		.rsn = RTW89_CHANCTX_PAUSE_REASON_GC_BCN_LOSS, +		.trigger = rtwvif_link, +	}; +	struct ieee80211_bss_conf *bss_conf; +	struct rtw89_mcc_role *role; +	u16 bcn_int; + +	if (mode != RTW89_ENTITY_MODE_MCC) +		return false; + +	role = rtw89_mcc_get_gc_role(rtwdev); +	if (!role) +		return false; + +	if (role->rtwvif_link != rtwvif_link) +		return false; + +	rtw89_debug(rtwdev, RTW89_DBG_CHAN, +		    "MCC GC beacon loss, pause MCC to detect GO beacon\n"); + +	rcu_read_lock(); + +	bss_conf = rtw89_vif_rcu_dereference_link(rtwvif_link, true); +	bcn_int = bss_conf->beacon_int; + +	rcu_read_unlock(); + +	rtw89_chanctx_pause(rtwdev, &pause_parm); +	rtwvif_link->last_sync_bcn_tsf = READ_ONCE(rtwvif_link->sync_bcn_tsf); +	wiphy_delayed_work_queue(rtwdev->hw->wiphy, +				 &rtwvif_link->mcc_gc_detect_beacon_work, +				 usecs_to_jiffies(ieee80211_tu_to_usec(bcn_int))); + +	return true; +} + +static void rtw89_mcc_detect_connection(struct rtw89_dev *rtwdev, +					struct rtw89_mcc_role *role) +{ +	struct ieee80211_vif *vif; +	bool start_detect; +	int ret; + +	ret = rtw89_core_send_nullfunc(rtwdev, role->rtwvif_link, true, false, +				       RTW89_MCC_PROBE_TIMEOUT); +	if (ret) +		role->probe_count++; +	else +		role->probe_count = 0; + +	if (role->probe_count < RTW89_MCC_PROBE_MAX_TRIES) +		return; + +	rtw89_debug(rtwdev, RTW89_DBG_CHAN, +		    "MCC <macid %d> can not detect AP/GO\n", role->rtwvif_link->mac_id); + +	start_detect = rtw89_mcc_detect_go_bcn(rtwdev, role->rtwvif_link); +	if (start_detect) +		return; + +	vif = rtwvif_link_to_vif(role->rtwvif_link); +	ieee80211_connection_loss(vif); +} +  static void rtw89_mcc_track(struct rtw89_dev *rtwdev)  {  	struct rtw89_mcc_info *mcc = &rtwdev->mcc;  	struct rtw89_mcc_config *config = &mcc->config;  	struct rtw89_mcc_pattern *pattern = &config->pattern; -	s16 tolerance; +	struct rtw89_mcc_role *ref = &mcc->role_ref; +	struct rtw89_mcc_role *aux = &mcc->role_aux; +	u16 tolerance;  	u16 bcn_ofst;  	u16 diff; +	if (rtw89_mcc_ignore_bcn(rtwdev, ref) || aux->ignore_bcn) +		rtw89_mcc_detect_connection(rtwdev, aux); +	else if (rtw89_mcc_ignore_bcn(rtwdev, aux) || ref->ignore_bcn) +		rtw89_mcc_detect_connection(rtwdev, ref); +  	if (mcc->mode != RTW89_MCC_MODE_GC_STA)  		return;  	bcn_ofst = rtw89_mcc_get_bcn_ofst(rtwdev); +	if (bcn_ofst == config->beacon_offset) +		return; +  	if (bcn_ofst > config->beacon_offset) {  		diff = bcn_ofst - config->beacon_offset;  		if (pattern->tob_aux < 0)  			tolerance = -pattern->tob_aux; -		else +		else if (pattern->toa_aux > 0)  			tolerance = pattern->toa_aux; +		else +			return; /* no chance to improve */  	} else {  		diff = config->beacon_offset - bcn_ofst;  		if (pattern->toa_aux < 0)  			tolerance = -pattern->toa_aux; -		else +		else if (pattern->tob_aux > 0)  			tolerance = pattern->tob_aux; +		else +			return; /* no chance to improve */  	}  	if (diff <= tolerance) @@ -2390,7 +2796,31 @@ static void rtw89_mcc_update_limit(struct rtw89_dev *rtwdev)  	rtw89_iterate_mcc_roles(rtwdev, rtw89_mcc_upd_lmt_iterator, NULL);  } -void rtw89_chanctx_work(struct work_struct *work) +static int rtw89_mcc_get_links_iterator(struct rtw89_dev *rtwdev, +					struct rtw89_mcc_role *mcc_role, +					unsigned int ordered_idx, +					void *data) +{ +	struct rtw89_mcc_links_info *info = data; + +	info->links[ordered_idx] = mcc_role->rtwvif_link; +	return 0; +} + +void rtw89_mcc_get_links(struct rtw89_dev *rtwdev, struct rtw89_mcc_links_info *info) +{ +	enum rtw89_entity_mode mode; + +	memset(info, 0, sizeof(*info)); + +	mode = rtw89_get_entity_mode(rtwdev); +	if (unlikely(mode != RTW89_ENTITY_MODE_MCC)) +		return; + +	rtw89_iterate_mcc_roles(rtwdev, rtw89_mcc_get_links_iterator, info); +} + +void rtw89_chanctx_work(struct wiphy *wiphy, struct wiphy_work *work)  {  	struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,  						chanctx_work.work); @@ -2401,12 +2831,10 @@ void rtw89_chanctx_work(struct work_struct *work)  	int ret;  	int i; -	mutex_lock(&rtwdev->mutex); +	lockdep_assert_wiphy(wiphy); -	if (hal->entity_pause) { -		mutex_unlock(&rtwdev->mutex); +	if (hal->entity_pause)  		return; -	}  	for (i = 0; i < NUM_OF_RTW89_CHANCTX_CHANGES; i++) {  		if (test_and_clear_bit(i, hal->changes)) @@ -2445,8 +2873,6 @@ void rtw89_chanctx_work(struct work_struct *work)  	default:  		break;  	} - -	mutex_unlock(&rtwdev->mutex);  }  void rtw89_queue_chanctx_change(struct rtw89_dev *rtwdev, @@ -2462,6 +2888,7 @@ void rtw89_queue_chanctx_change(struct rtw89_dev *rtwdev,  		return;  	case RTW89_ENTITY_MODE_MCC_PREPARE:  		delay = ieee80211_tu_to_usec(RTW89_CHANCTX_TIME_MCC_PREPARE); +		rtw89_phy_dig_suspend(rtwdev);  		break;  	case RTW89_ENTITY_MODE_MCC:  		delay = ieee80211_tu_to_usec(RTW89_CHANCTX_TIME_MCC); @@ -2477,8 +2904,8 @@ void rtw89_queue_chanctx_change(struct rtw89_dev *rtwdev,  	rtw89_debug(rtwdev, RTW89_DBG_CHAN,  		    "queue chanctx work for mode %d with delay %d us\n",  		    mode, delay); -	ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->chanctx_work, -				     usecs_to_jiffies(delay)); +	wiphy_delayed_work_queue(rtwdev->hw->wiphy, &rtwdev->chanctx_work, +				 usecs_to_jiffies(delay));  }  void rtw89_queue_chanctx_work(struct rtw89_dev *rtwdev) @@ -2486,12 +2913,207 @@ void rtw89_queue_chanctx_work(struct rtw89_dev *rtwdev)  	rtw89_queue_chanctx_change(rtwdev, RTW89_CHANCTX_CHANGE_DFLT);  } +static enum rtw89_mr_wtype __rtw89_query_mr_wtype(struct rtw89_dev *rtwdev) +{ +	struct rtw89_entity_mgnt *mgnt = &rtwdev->hal.entity_mgnt; +	enum rtw89_chanctx_idx chanctx_idx; +	struct ieee80211_vif *vif; +	struct rtw89_vif *rtwvif; +	unsigned int num_mld = 0; +	unsigned int num_ml = 0; +	unsigned int cnt = 0; +	u8 role_idx; +	u8 idx; + +	for (role_idx = 0; role_idx < RTW89_MAX_INTERFACE_NUM; role_idx++) { +		rtwvif = mgnt->active_roles[role_idx]; +		if (!rtwvif) +			continue; + +		cnt++; + +		vif = rtwvif_to_vif(rtwvif); +		if (!ieee80211_vif_is_mld(vif)) +			continue; + +		num_mld++; + +		for (idx = 0; idx < __RTW89_MLD_MAX_LINK_NUM; idx++) { +			chanctx_idx = mgnt->chanctx_tbl[role_idx][idx]; +			if (chanctx_idx != RTW89_CHANCTX_IDLE) +				num_ml++; +		} +	} + +	if (num_mld > 1) +		goto err; + +	switch (cnt) { +	case 0: +		return RTW89_MR_WTYPE_NONE; +	case 1: +		if (!num_mld) +			return RTW89_MR_WTYPE_NONMLD; +		switch (num_ml) { +		case 1: +			return RTW89_MR_WTYPE_MLD1L1R; +		case 2: +			return RTW89_MR_WTYPE_MLD2L1R; +		default: +			break; +		} +		break; +	case 2: +		if (!num_mld) +			return RTW89_MR_WTYPE_NONMLD_NONMLD; +		switch (num_ml) { +		case 1: +			return RTW89_MR_WTYPE_MLD1L1R_NONMLD; +		case 2: +			return RTW89_MR_WTYPE_MLD2L1R_NONMLD; +		default: +			break; +		} +		break; +	default: +		break; +	} + +err: +	rtw89_warn(rtwdev, "%s: unhandled cnt %u mld %u ml %u\n", __func__, +		   cnt, num_mld, num_ml); +	return RTW89_MR_WTYPE_UNKNOWN; +} + +static enum rtw89_mr_wmode __rtw89_query_mr_wmode(struct rtw89_dev *rtwdev, +						  u8 inst_idx) +{ +	struct rtw89_entity_mgnt *mgnt = &rtwdev->hal.entity_mgnt; +	unsigned int num[NUM_NL80211_IFTYPES] = {}; +	enum rtw89_chanctx_idx chanctx_idx; +	struct ieee80211_vif *vif; +	struct rtw89_vif *rtwvif; +	unsigned int cnt = 0; +	u8 role_idx; + +	if (unlikely(inst_idx >= __RTW89_MLD_MAX_LINK_NUM)) +		return RTW89_MR_WMODE_UNKNOWN; + +	for (role_idx = 0; role_idx < RTW89_MAX_INTERFACE_NUM; role_idx++) { +		chanctx_idx = mgnt->chanctx_tbl[role_idx][inst_idx]; +		if (chanctx_idx == RTW89_CHANCTX_IDLE) +			continue; + +		rtwvif = mgnt->active_roles[role_idx]; +		if (unlikely(!rtwvif)) +			continue; + +		vif = rtwvif_to_vif(rtwvif); +		num[vif->type]++; +		cnt++; +	} + +	switch (cnt) { +	case 0: +		return RTW89_MR_WMODE_NONE; +	case 1: +		if (num[NL80211_IFTYPE_STATION]) +			return RTW89_MR_WMODE_1CLIENT; +		if (num[NL80211_IFTYPE_AP]) +			return RTW89_MR_WMODE_1AP; +		break; +	case 2: +		if (num[NL80211_IFTYPE_STATION] == 2) +			return RTW89_MR_WMODE_2CLIENTS; +		if (num[NL80211_IFTYPE_AP] == 2) +			return RTW89_MR_WMODE_2APS; +		if (num[NL80211_IFTYPE_STATION] && num[NL80211_IFTYPE_AP]) +			return RTW89_MR_WMODE_1AP_1CLIENT; +		break; +	default: +		break; +	} + +	rtw89_warn(rtwdev, "%s: unhandled cnt %u\n", __func__, cnt); +	return RTW89_MR_WMODE_UNKNOWN; +} + +static enum rtw89_mr_ctxtype __rtw89_query_mr_ctxtype(struct rtw89_dev *rtwdev, +						      u8 inst_idx) +{ +	struct rtw89_entity_mgnt *mgnt = &rtwdev->hal.entity_mgnt; +	DECLARE_BITMAP(map, NUM_OF_RTW89_CHANCTX) = {}; +	unsigned int num[RTW89_BAND_NUM] = {}; +	enum rtw89_chanctx_idx chanctx_idx; +	const struct rtw89_chan *chan; +	unsigned int cnt = 0; +	u8 role_idx; + +	if (unlikely(inst_idx >= __RTW89_MLD_MAX_LINK_NUM)) +		return RTW89_MR_CTX_UNKNOWN; + +	for (role_idx = 0; role_idx < RTW89_MAX_INTERFACE_NUM; role_idx++) { +		chanctx_idx = mgnt->chanctx_tbl[role_idx][inst_idx]; +		if (chanctx_idx == RTW89_CHANCTX_IDLE) +			continue; + +		if (__test_and_set_bit(chanctx_idx, map)) +			continue; + +		chan = rtw89_chan_get(rtwdev, chanctx_idx); +		num[chan->band_type]++; +		cnt++; +	} + +	switch (cnt) { +	case 0: +		return RTW89_MR_CTX_NONE; +	case 1: +		if (num[RTW89_BAND_2G]) +			return RTW89_MR_CTX1_2GHZ; +		if (num[RTW89_BAND_5G]) +			return RTW89_MR_CTX1_5GHZ; +		if (num[RTW89_BAND_6G]) +			return RTW89_MR_CTX1_6GHZ; +		break; +	case 2: +		if (num[RTW89_BAND_2G] == 2) +			return RTW89_MR_CTX2_2GHZ; +		if (num[RTW89_BAND_5G] == 2) +			return RTW89_MR_CTX2_5GHZ; +		if (num[RTW89_BAND_6G] == 2) +			return RTW89_MR_CTX2_6GHZ; +		if (num[RTW89_BAND_2G] && num[RTW89_BAND_5G]) +			return RTW89_MR_CTX2_2GHZ_5GHZ; +		if (num[RTW89_BAND_2G] && num[RTW89_BAND_6G]) +			return RTW89_MR_CTX2_2GHZ_6GHZ; +		if (num[RTW89_BAND_5G] && num[RTW89_BAND_6G]) +			return RTW89_MR_CTX2_5GHZ_6GHZ; +		break; +	default: +		break; +	} + +	rtw89_warn(rtwdev, "%s: unhandled cnt %u\n", __func__, cnt); +	return RTW89_MR_CTX_UNKNOWN; +} + +void rtw89_query_mr_chanctx_info(struct rtw89_dev *rtwdev, u8 inst_idx, +				 struct rtw89_mr_chanctx_info *info) +{ +	lockdep_assert_wiphy(rtwdev->hw->wiphy); + +	info->wtype = __rtw89_query_mr_wtype(rtwdev); +	info->wmode = __rtw89_query_mr_wmode(rtwdev, inst_idx); +	info->ctxtype = __rtw89_query_mr_ctxtype(rtwdev, inst_idx); +} +  void rtw89_chanctx_track(struct rtw89_dev *rtwdev)  {  	struct rtw89_hal *hal = &rtwdev->hal;  	enum rtw89_entity_mode mode; -	lockdep_assert_held(&rtwdev->mutex); +	lockdep_assert_wiphy(rtwdev->hw->wiphy);  	if (hal->entity_pause)  		return; @@ -2507,22 +3129,22 @@ void rtw89_chanctx_track(struct rtw89_dev *rtwdev)  }  void rtw89_chanctx_pause(struct rtw89_dev *rtwdev, -			 enum rtw89_chanctx_pause_reasons rsn) +			 const struct rtw89_chanctx_pause_parm *pause_parm)  {  	struct rtw89_hal *hal = &rtwdev->hal;  	enum rtw89_entity_mode mode; -	lockdep_assert_held(&rtwdev->mutex); +	lockdep_assert_wiphy(rtwdev->hw->wiphy);  	if (hal->entity_pause)  		return; -	rtw89_debug(rtwdev, RTW89_DBG_CHAN, "chanctx pause (rsn: %d)\n", rsn); +	rtw89_debug(rtwdev, RTW89_DBG_CHAN, "chanctx pause (rsn: %d)\n", pause_parm->rsn);  	mode = rtw89_get_entity_mode(rtwdev);  	switch (mode) {  	case RTW89_ENTITY_MODE_MCC: -		rtw89_mcc_stop(rtwdev); +		rtw89_mcc_stop(rtwdev, pause_parm);  		break;  	default:  		break; @@ -2555,7 +3177,7 @@ void rtw89_chanctx_proceed(struct rtw89_dev *rtwdev,  	enum rtw89_entity_mode mode;  	int ret; -	lockdep_assert_held(&rtwdev->mutex); +	lockdep_assert_wiphy(rtwdev->hw->wiphy);  	if (unlikely(!hal->entity_pause)) {  		rtw89_chanctx_proceed_cb(rtwdev, cb_parm); @@ -2670,10 +3292,9 @@ int rtw89_chanctx_ops_add(struct rtw89_dev *rtwdev,  void rtw89_chanctx_ops_remove(struct rtw89_dev *rtwdev,  			      struct ieee80211_chanctx_conf *ctx)  { -	struct rtw89_hal *hal = &rtwdev->hal;  	struct rtw89_chanctx_cfg *cfg = (struct rtw89_chanctx_cfg *)ctx->drv_priv; -	clear_bit(cfg->idx, hal->entity_map); +	rtw89_config_entity_chandef(rtwdev, cfg->idx, NULL);  }  void rtw89_chanctx_ops_change(struct rtw89_dev *rtwdev, @@ -2687,6 +3308,9 @@ void rtw89_chanctx_ops_change(struct rtw89_dev *rtwdev,  		rtw89_config_entity_chandef(rtwdev, idx, &ctx->def);  		rtw89_set_channel(rtwdev);  	} + +	if (changed & IEEE80211_CHANCTX_CHANGE_PUNCTURING) +		rtw89_chan_update_punctured(rtwdev, idx, &ctx->def);  }  int rtw89_chanctx_ops_assign_vif(struct rtw89_dev *rtwdev, @@ -2698,11 +3322,15 @@ int rtw89_chanctx_ops_assign_vif(struct rtw89_dev *rtwdev,  	struct rtw89_hal *hal = &rtwdev->hal;  	struct rtw89_entity_mgnt *mgnt = &hal->entity_mgnt;  	struct rtw89_entity_weight w = {}; +	int ret;  	rtwvif_link->chanctx_idx = cfg->idx;  	rtwvif_link->chanctx_assigned = true;  	cfg->ref_count++; +	if (rtwdev->scanning) +		rtw89_hw_scan_abort(rtwdev, rtwdev->scan_info.scanning_vif); +  	if (list_empty(&rtwvif->mgnt_entry))  		list_add_tail(&rtwvif->mgnt_entry, &mgnt->active_list); @@ -2717,7 +3345,13 @@ int rtw89_chanctx_ops_assign_vif(struct rtw89_dev *rtwdev,  	rtw89_swap_chanctx(rtwdev, cfg->idx, RTW89_CHANCTX_0);  out: -	return rtw89_set_channel(rtwdev); +	ret = rtw89_set_channel(rtwdev); +	if (ret) +		return ret; + +	rtw89_tas_reset(rtwdev, true); + +	return 0;  }  void rtw89_chanctx_ops_unassign_vif(struct rtw89_dev *rtwdev, @@ -2736,6 +3370,9 @@ void rtw89_chanctx_ops_unassign_vif(struct rtw89_dev *rtwdev,  	rtwvif_link->chanctx_assigned = false;  	cfg->ref_count--; +	if (rtwdev->scanning) +		rtw89_hw_scan_abort(rtwdev, rtwdev->scan_info.scanning_vif); +  	if (!rtw89_vif_is_active_role(rtwvif))  		list_del_init(&rtwvif->mgnt_entry); @@ -2761,7 +3398,7 @@ out:  		cur = rtw89_get_entity_mode(rtwdev);  		switch (cur) {  		case RTW89_ENTITY_MODE_MCC: -			rtw89_mcc_stop(rtwdev); +			rtw89_mcc_stop(rtwdev, NULL);  			break;  		default:  			break; @@ -2787,3 +3424,37 @@ out:  		break;  	}  } + +int rtw89_chanctx_ops_reassign_vif(struct rtw89_dev *rtwdev, +				   struct rtw89_vif_link *rtwvif_link, +				   struct ieee80211_chanctx_conf *old_ctx, +				   struct ieee80211_chanctx_conf *new_ctx, +				   bool replace) +{ +	int ret; + +	rtw89_chanctx_ops_unassign_vif(rtwdev, rtwvif_link, old_ctx); + +	if (!replace) +		goto assign; + +	rtw89_chanctx_ops_remove(rtwdev, old_ctx); +	ret = rtw89_chanctx_ops_add(rtwdev, new_ctx); +	if (ret) { +		rtw89_err(rtwdev, "%s: failed to add chanctx: %d\n", +			  __func__, ret); +		return ret; +	} + +assign: +	ret = rtw89_chanctx_ops_assign_vif(rtwdev, rtwvif_link, new_ctx); +	if (ret) { +		rtw89_err(rtwdev, "%s: failed to assign chanctx: %d\n", +			  __func__, ret); +		return ret; +	} + +	_rtw89_chan_update_punctured(rtwdev, rtwvif_link, &new_ctx->def); + +	return 0; +} diff --git a/sys/contrib/dev/rtw89/chan.h b/sys/contrib/dev/rtw89/chan.h index 092a6f676894..b1175419f92b 100644 --- a/sys/contrib/dev/rtw89/chan.h +++ b/sys/contrib/dev/rtw89/chan.h @@ -18,6 +18,12 @@  #define RTW89_MCC_EARLY_RX_BCN_TIME 5  #define RTW89_MCC_MIN_RX_BCN_TIME 10  #define RTW89_MCC_DFLT_BCN_OFST_TIME 40 +#define RTW89_MCC_SWITCH_CH_TIME 3 + +#define RTW89_MCC_PROBE_TIMEOUT 100 +#define RTW89_MCC_PROBE_MAX_TRIES 3 + +#define RTW89_MCC_DETECT_BCN_MAX_TRIES 2  #define RTW89_MCC_MIN_GO_DURATION \  	(RTW89_MCC_EARLY_TX_BCN_TIME + RTW89_MCC_MIN_RX_BCN_TIME) @@ -25,17 +31,77 @@  #define RTW89_MCC_MIN_STA_DURATION \  	(RTW89_MCC_EARLY_RX_BCN_TIME + RTW89_MCC_MIN_RX_BCN_TIME) +#define RTW89_MCC_MIN_RX_BCN_WITH_SWITCH_CH_TIME \ +	(RTW89_MCC_MIN_RX_BCN_TIME + RTW89_MCC_SWITCH_CH_TIME) +  #define RTW89_MCC_DFLT_GROUP 0  #define RTW89_MCC_NEXT_GROUP(cur) (((cur) + 1) % 4) -#define RTW89_MCC_DFLT_TX_NULL_EARLY 3 +#define RTW89_MCC_DFLT_TX_NULL_EARLY 7  #define RTW89_MCC_DFLT_COURTESY_SLOT 3 +#define RTW89_MCC_REQ_COURTESY_TIME 5 +#define RTW89_MCC_REQ_COURTESY(pattern, role)			\ +({								\ +	const struct rtw89_mcc_pattern *p = pattern;		\ +	p->tob_ ## role <= RTW89_MCC_REQ_COURTESY_TIME ||	\ +	p->toa_ ## role <= RTW89_MCC_REQ_COURTESY_TIME;		\ +}) +  #define NUM_OF_RTW89_MCC_ROLES 2 +enum rtw89_mr_wtype { +	RTW89_MR_WTYPE_NONE, +	RTW89_MR_WTYPE_NONMLD, +	RTW89_MR_WTYPE_MLD1L1R, +	RTW89_MR_WTYPE_MLD2L1R, +	RTW89_MR_WTYPE_MLD2L2R, +	RTW89_MR_WTYPE_NONMLD_NONMLD, +	RTW89_MR_WTYPE_MLD1L1R_NONMLD, +	RTW89_MR_WTYPE_MLD2L1R_NONMLD, +	RTW89_MR_WTYPE_MLD2L2R_NONMLD, +	RTW89_MR_WTYPE_UNKNOWN, +}; + +enum rtw89_mr_wmode { +	RTW89_MR_WMODE_NONE, +	RTW89_MR_WMODE_1CLIENT, +	RTW89_MR_WMODE_1AP, +	RTW89_MR_WMODE_1AP_1CLIENT, +	RTW89_MR_WMODE_2CLIENTS, +	RTW89_MR_WMODE_2APS, +	RTW89_MR_WMODE_UNKNOWN, +}; + +enum rtw89_mr_ctxtype { +	RTW89_MR_CTX_NONE, +	RTW89_MR_CTX1_2GHZ, +	RTW89_MR_CTX1_5GHZ, +	RTW89_MR_CTX1_6GHZ, +	RTW89_MR_CTX2_2GHZ, +	RTW89_MR_CTX2_5GHZ, +	RTW89_MR_CTX2_6GHZ, +	RTW89_MR_CTX2_2GHZ_5GHZ, +	RTW89_MR_CTX2_2GHZ_6GHZ, +	RTW89_MR_CTX2_5GHZ_6GHZ, +	RTW89_MR_CTX_UNKNOWN, +}; + +struct rtw89_mr_chanctx_info { +	enum rtw89_mr_wtype wtype; +	enum rtw89_mr_wmode wmode; +	enum rtw89_mr_ctxtype ctxtype; +}; +  enum rtw89_chanctx_pause_reasons {  	RTW89_CHANCTX_PAUSE_REASON_HW_SCAN,  	RTW89_CHANCTX_PAUSE_REASON_ROC, +	RTW89_CHANCTX_PAUSE_REASON_GC_BCN_LOSS, +}; + +struct rtw89_chanctx_pause_parm { +	const struct rtw89_vif_link *trigger; +	enum rtw89_chanctx_pause_reasons rsn;  };  struct rtw89_chanctx_cb_parm { @@ -45,6 +111,7 @@ struct rtw89_chanctx_cb_parm {  };  struct rtw89_entity_weight { +	unsigned int registered_chanctxs;  	unsigned int active_chanctxs;  	unsigned int active_roles;  }; @@ -95,17 +162,19 @@ void rtw89_config_entity_chandef(struct rtw89_dev *rtwdev,  				 enum rtw89_chanctx_idx idx,  				 const struct cfg80211_chan_def *chandef);  void rtw89_config_roc_chandef(struct rtw89_dev *rtwdev, -			      enum rtw89_chanctx_idx idx, +			      struct rtw89_vif_link *rtwvif_link,  			      const struct cfg80211_chan_def *chandef);  void rtw89_entity_init(struct rtw89_dev *rtwdev);  enum rtw89_entity_mode rtw89_entity_recalc(struct rtw89_dev *rtwdev); -void rtw89_chanctx_work(struct work_struct *work); +void rtw89_chanctx_work(struct wiphy *wiphy, struct wiphy_work *work);  void rtw89_queue_chanctx_work(struct rtw89_dev *rtwdev);  void rtw89_queue_chanctx_change(struct rtw89_dev *rtwdev,  				enum rtw89_chanctx_changes change); +void rtw89_query_mr_chanctx_info(struct rtw89_dev *rtwdev, u8 inst_idx, +				 struct rtw89_mr_chanctx_info *info);  void rtw89_chanctx_track(struct rtw89_dev *rtwdev);  void rtw89_chanctx_pause(struct rtw89_dev *rtwdev, -			 enum rtw89_chanctx_pause_reasons rsn); +			 const struct rtw89_chanctx_pause_parm *parm);  void rtw89_chanctx_proceed(struct rtw89_dev *rtwdev,  			   const struct rtw89_chanctx_cb_parm *cb_parm); @@ -116,6 +185,16 @@ const struct rtw89_chan *__rtw89_mgnt_chan_get(struct rtw89_dev *rtwdev,  #define rtw89_mgnt_chan_get(rtwdev, link_index) \  	__rtw89_mgnt_chan_get(rtwdev, __func__, link_index) +struct rtw89_mcc_links_info { +	struct rtw89_vif_link *links[NUM_OF_RTW89_MCC_ROLES]; +}; + +void rtw89_mcc_get_links(struct rtw89_dev *rtwdev, struct rtw89_mcc_links_info *info); +void rtw89_mcc_prepare_done_work(struct wiphy *wiphy, struct wiphy_work *work); +void rtw89_mcc_gc_detect_beacon_work(struct wiphy *wiphy, struct wiphy_work *work); +bool rtw89_mcc_detect_go_bcn(struct rtw89_dev *rtwdev, +			     struct rtw89_vif_link *rtwvif_link); +  int rtw89_chanctx_ops_add(struct rtw89_dev *rtwdev,  			  struct ieee80211_chanctx_conf *ctx);  void rtw89_chanctx_ops_remove(struct rtw89_dev *rtwdev, @@ -129,5 +208,10 @@ int rtw89_chanctx_ops_assign_vif(struct rtw89_dev *rtwdev,  void rtw89_chanctx_ops_unassign_vif(struct rtw89_dev *rtwdev,  				    struct rtw89_vif_link *rtwvif_link,  				    struct ieee80211_chanctx_conf *ctx); +int rtw89_chanctx_ops_reassign_vif(struct rtw89_dev *rtwdev, +				   struct rtw89_vif_link *rtwvif_link, +				   struct ieee80211_chanctx_conf *old_ctx, +				   struct ieee80211_chanctx_conf *new_ctx, +				   bool replace);  #endif diff --git a/sys/contrib/dev/rtw89/coex.c b/sys/contrib/dev/rtw89/coex.c index 68316d44b204..e4e6daf51a1b 100644 --- a/sys/contrib/dev/rtw89/coex.c +++ b/sys/contrib/dev/rtw89/coex.c @@ -2,6 +2,7 @@  /* Copyright(c) 2019-2020  Realtek Corporation   */ +#include "chan.h"  #include "coex.h"  #include "debug.h"  #include "fw.h" @@ -10,7 +11,7 @@  #include "ps.h"  #include "reg.h" -#define RTW89_COEX_VERSION 0x07000113 +#define RTW89_COEX_VERSION 0x09000013  #define FCXDEF_STEP 50 /* MUST <= FCXMAX_STEP and match with wl fw*/  #define BTC_E2G_LIMIT_DEF 80 @@ -89,10 +90,10 @@ static const struct rtw89_btc_fbtc_slot s_def[] = {  	[CXST_B4]	= __DEF_FBTC_SLOT(50,  0xe5555555, SLOT_MIX),  	[CXST_LK]	= __DEF_FBTC_SLOT(20,  0xea5a5a5a, SLOT_ISO),  	[CXST_BLK]	= __DEF_FBTC_SLOT(500, 0x55555555, SLOT_MIX), -	[CXST_E2G]	= __DEF_FBTC_SLOT(0,   0xea5a5a5a, SLOT_MIX), -	[CXST_E5G]	= __DEF_FBTC_SLOT(0,   0xffffffff, SLOT_ISO), +	[CXST_E2G]	= __DEF_FBTC_SLOT(5,   0xea5a5a5a, SLOT_MIX), +	[CXST_E5G]	= __DEF_FBTC_SLOT(5,   0xffffffff, SLOT_ISO),  	[CXST_EBT]	= __DEF_FBTC_SLOT(5,   0xe5555555, SLOT_MIX), -	[CXST_ENULL]	= __DEF_FBTC_SLOT(0,   0xaaaaaaaa, SLOT_ISO), +	[CXST_ENULL]	= __DEF_FBTC_SLOT(5,   0xaaaaaaaa, SLOT_ISO),  	[CXST_WLK]	= __DEF_FBTC_SLOT(250, 0xea5a5a5a, SLOT_MIX),  	[CXST_W1FDD]	= __DEF_FBTC_SLOT(50,  0xffffffff, SLOT_ISO),  	[CXST_B1FDD]	= __DEF_FBTC_SLOT(50,  0xffffdfff, SLOT_ISO), @@ -132,13 +133,37 @@ static const u32 cxtbl[] = {  static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = {  	/* firmware version must be in decreasing order for each chip */ +	{RTL8852BT, RTW89_FW_VER_CODE(0, 29, 122, 0), +	 .fcxbtcrpt = 8, .fcxtdma = 7,    .fcxslots = 7, .fcxcysta = 7, +	 .fcxstep = 7,   .fcxnullsta = 7, .fcxmreg = 7,  .fcxgpiodbg = 7, +	 .fcxbtver = 7,  .fcxbtscan = 7,  .fcxbtafh = 7, .fcxbtdevinfo = 7, +	 .fwlrole = 7,   .frptmap = 3,    .fcxctrl = 7,  .fcxinit = 7, +	 .fwevntrptl = 1, .fwc2hfunc = 2, .drvinfo_type = 1, .info_buf = 1800, +	 .max_role_num = 6, .fcxosi = 0,  .fcxmlo = 0,   .bt_desired = 8, +	},  	{RTL8852BT, RTW89_FW_VER_CODE(0, 29, 90, 0),  	 .fcxbtcrpt = 7, .fcxtdma = 7,    .fcxslots = 7, .fcxcysta = 7,  	 .fcxstep = 7,   .fcxnullsta = 7, .fcxmreg = 7,  .fcxgpiodbg = 7,  	 .fcxbtver = 7,  .fcxbtscan = 7,  .fcxbtafh = 7, .fcxbtdevinfo = 7,  	 .fwlrole = 7,   .frptmap = 3,    .fcxctrl = 7,  .fcxinit = 7,  	 .fwevntrptl = 1, .fwc2hfunc = 2, .drvinfo_type = 1, .info_buf = 1800, -	 .max_role_num = 6, +	 .max_role_num = 6, .fcxosi = 0,  .fcxmlo = 0,   .bt_desired = 8, +	}, +	{RTL8922A, RTW89_FW_VER_CODE(0, 35, 71, 0), +	 .fcxbtcrpt = 8, .fcxtdma = 7,    .fcxslots = 7, .fcxcysta = 7, +	 .fcxstep = 7,   .fcxnullsta = 7, .fcxmreg = 7,  .fcxgpiodbg = 7, +	 .fcxbtver = 7,  .fcxbtscan = 7,  .fcxbtafh = 7, .fcxbtdevinfo = 7, +	 .fwlrole = 8,   .frptmap = 4,    .fcxctrl = 7,  .fcxinit = 7, +	 .fwevntrptl = 1, .fwc2hfunc = 3, .drvinfo_type = 2, .info_buf = 1800, +	 .max_role_num = 6, .fcxosi = 1,  .fcxmlo = 1,   .bt_desired = 9, +	}, +	{RTL8922A, RTW89_FW_VER_CODE(0, 35, 63, 0), +	 .fcxbtcrpt = 8, .fcxtdma = 7,    .fcxslots = 7, .fcxcysta = 7, +	 .fcxstep = 7,   .fcxnullsta = 7, .fcxmreg = 7,  .fcxgpiodbg = 7, +	 .fcxbtver = 7,  .fcxbtscan = 7,  .fcxbtafh = 7, .fcxbtdevinfo = 7, +	 .fwlrole = 8,   .frptmap = 4,    .fcxctrl = 7,  .fcxinit = 7, +	 .fwevntrptl = 1, .fwc2hfunc = 3, .drvinfo_type = 2, .info_buf = 1800, +	 .max_role_num = 6, .fcxosi = 1,  .fcxmlo = 1,   .bt_desired = 9,  	},  	{RTL8922A, RTW89_FW_VER_CODE(0, 35, 8, 0),  	 .fcxbtcrpt = 8, .fcxtdma = 7,    .fcxslots = 7, .fcxcysta = 7, @@ -146,7 +171,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = {  	 .fcxbtver = 7,  .fcxbtscan = 7,  .fcxbtafh = 7, .fcxbtdevinfo = 7,  	 .fwlrole = 8,   .frptmap = 3,    .fcxctrl = 7,  .fcxinit = 7,  	 .fwevntrptl = 1, .fwc2hfunc = 1, .drvinfo_type = 1, .info_buf = 1800, -	 .max_role_num = 6, +	 .max_role_num = 6, .fcxosi = 0,  .fcxmlo = 0,   .bt_desired = 7,  	},  	{RTL8851B, RTW89_FW_VER_CODE(0, 29, 29, 0),  	 .fcxbtcrpt = 105, .fcxtdma = 3,    .fcxslots = 1, .fcxcysta = 5, @@ -154,7 +179,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = {  	 .fcxbtver = 1,  .fcxbtscan = 2,  .fcxbtafh = 2, .fcxbtdevinfo = 1,  	 .fwlrole = 2,   .frptmap = 3,    .fcxctrl = 1,  .fcxinit = 0,  	 .fwevntrptl = 0, .fwc2hfunc = 1, .drvinfo_type = 0, .info_buf = 1800, -	 .max_role_num = 6, +	 .max_role_num = 6, .fcxosi = 0,  .fcxmlo = 0,   .bt_desired = 7,  	},  	{RTL8852C, RTW89_FW_VER_CODE(0, 27, 57, 0),  	 .fcxbtcrpt = 4, .fcxtdma = 3,    .fcxslots = 1, .fcxcysta = 3, @@ -162,7 +187,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = {  	 .fcxbtver = 1,  .fcxbtscan = 1,  .fcxbtafh = 2, .fcxbtdevinfo = 1,  	 .fwlrole = 1,   .frptmap = 3,    .fcxctrl = 1,  .fcxinit = 0,  	 .fwevntrptl = 0, .fwc2hfunc = 1, .drvinfo_type = 0, .info_buf = 1280, -	 .max_role_num = 5, +	 .max_role_num = 5, .fcxosi = 0,  .fcxmlo = 0,   .bt_desired = 7,  	},  	{RTL8852C, RTW89_FW_VER_CODE(0, 27, 42, 0),  	 .fcxbtcrpt = 4, .fcxtdma = 3,    .fcxslots = 1, .fcxcysta = 3, @@ -170,7 +195,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = {  	 .fcxbtver = 1,  .fcxbtscan = 1,  .fcxbtafh = 2, .fcxbtdevinfo = 1,  	 .fwlrole = 1,   .frptmap = 2,    .fcxctrl = 1,  .fcxinit = 0,  	 .fwevntrptl = 0, .fwc2hfunc = 1, .drvinfo_type = 0, .info_buf = 1280, -	 .max_role_num = 5, +	 .max_role_num = 5, .fcxosi = 0,  .fcxmlo = 0,   .bt_desired = 7,  	},  	{RTL8852C, RTW89_FW_VER_CODE(0, 27, 0, 0),  	 .fcxbtcrpt = 4, .fcxtdma = 3,    .fcxslots = 1, .fcxcysta = 3, @@ -178,7 +203,15 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = {  	 .fcxbtver = 1,  .fcxbtscan = 1,  .fcxbtafh = 1, .fcxbtdevinfo = 1,  	 .fwlrole = 1,   .frptmap = 2,    .fcxctrl = 1,  .fcxinit = 0,  	 .fwevntrptl = 0, .fwc2hfunc = 1, .drvinfo_type = 0, .info_buf = 1280, -	 .max_role_num = 5, +	 .max_role_num = 5, .fcxosi = 0,  .fcxmlo = 0,   .bt_desired = 7, +	}, +	{RTL8852B, RTW89_FW_VER_CODE(0, 29, 122, 0), +	 .fcxbtcrpt = 8, .fcxtdma = 7,    .fcxslots = 7, .fcxcysta = 7, +	 .fcxstep = 7,   .fcxnullsta = 7, .fcxmreg = 7,  .fcxgpiodbg = 7, +	 .fcxbtver = 7,  .fcxbtscan = 7,  .fcxbtafh = 7, .fcxbtdevinfo = 7, +	 .fwlrole = 7,   .frptmap = 3,    .fcxctrl = 7,  .fcxinit = 7, +	 .fwevntrptl = 1, .fwc2hfunc = 2, .drvinfo_type = 1, .info_buf = 1800, +	 .max_role_num = 6, .fcxosi = 0,  .fcxmlo = 0,   .bt_desired = 8,  	},  	{RTL8852B, RTW89_FW_VER_CODE(0, 29, 29, 0),  	 .fcxbtcrpt = 105, .fcxtdma = 3,  .fcxslots = 1, .fcxcysta = 5, @@ -186,7 +219,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = {  	 .fcxbtver = 1,  .fcxbtscan = 2,  .fcxbtafh = 2, .fcxbtdevinfo = 1,  	 .fwlrole = 2,   .frptmap = 3,    .fcxctrl = 1,  .fcxinit = 0,  	 .fwevntrptl = 0, .fwc2hfunc = 1, .drvinfo_type = 0, .info_buf = 1800, -	 .max_role_num = 6, +	 .max_role_num = 6, .fcxosi = 0,  .fcxmlo = 0,   .bt_desired = 7,  	},  	{RTL8852B, RTW89_FW_VER_CODE(0, 29, 14, 0),  	 .fcxbtcrpt = 5, .fcxtdma = 3,    .fcxslots = 1, .fcxcysta = 4, @@ -194,7 +227,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = {  	 .fcxbtver = 1,  .fcxbtscan = 1,  .fcxbtafh = 2, .fcxbtdevinfo = 1,  	 .fwlrole = 1,   .frptmap = 3,    .fcxctrl = 1,  .fcxinit = 0,  	 .fwevntrptl = 0, .fwc2hfunc = 1, .drvinfo_type = 0, .info_buf = 1800, -	 .max_role_num = 6, +	 .max_role_num = 6, .fcxosi = 0,  .fcxmlo = 0,   .bt_desired = 7,  	},  	{RTL8852B, RTW89_FW_VER_CODE(0, 27, 0, 0),  	 .fcxbtcrpt = 4, .fcxtdma = 3,    .fcxslots = 1, .fcxcysta = 3, @@ -202,7 +235,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = {  	 .fcxbtver = 1,  .fcxbtscan = 1,  .fcxbtafh = 1, .fcxbtdevinfo = 1,  	 .fwlrole = 1,   .frptmap = 1,    .fcxctrl = 1,  .fcxinit = 0,  	 .fwevntrptl = 0, .fwc2hfunc = 1, .drvinfo_type = 0, .info_buf = 1280, -	 .max_role_num = 5, +	 .max_role_num = 5, .fcxosi = 0,  .fcxmlo = 0,   .bt_desired = 7,  	},  	{RTL8852A, RTW89_FW_VER_CODE(0, 13, 37, 0),  	 .fcxbtcrpt = 4, .fcxtdma = 3,    .fcxslots = 1, .fcxcysta = 3, @@ -210,7 +243,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = {  	 .fcxbtver = 1,  .fcxbtscan = 1,  .fcxbtafh = 2, .fcxbtdevinfo = 1,  	 .fwlrole = 1,   .frptmap = 3,    .fcxctrl = 1,  .fcxinit = 0,  	 .fwevntrptl = 0, .fwc2hfunc = 0, .drvinfo_type = 0, .info_buf = 1280, -	 .max_role_num = 5, +	 .max_role_num = 5, .fcxosi = 0,  .fcxmlo = 0,   .bt_desired = 7,  	},  	{RTL8852A, RTW89_FW_VER_CODE(0, 13, 0, 0),  	 .fcxbtcrpt = 1, .fcxtdma = 1,    .fcxslots = 1, .fcxcysta = 2, @@ -218,7 +251,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = {  	 .fcxbtver = 1,  .fcxbtscan = 1,  .fcxbtafh = 1, .fcxbtdevinfo = 1,  	 .fwlrole = 0,   .frptmap = 0,    .fcxctrl = 0,  .fcxinit = 0,  	 .fwevntrptl = 0, .fwc2hfunc = 0, .drvinfo_type = 0, .info_buf = 1024, -	 .max_role_num = 5, +	 .max_role_num = 5, .fcxosi = 0,  .fcxmlo = 0,   .bt_desired = 7,  	},  	/* keep it to be the last as default entry */ @@ -228,7 +261,7 @@ static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = {  	 .fcxbtver = 1,  .fcxbtscan = 1,  .fcxbtafh = 1, .fcxbtdevinfo = 1,  	 .fwlrole = 0,   .frptmap = 0,    .fcxctrl = 0,  .fcxinit = 0,  	 .fwevntrptl = 0, .fwc2hfunc = 1, .drvinfo_type = 0, .info_buf = 1024, -	 .max_role_num = 5, +	 .max_role_num = 5, .fcxosi = 0,  .fcxmlo = 0,   .bt_desired = 7,  	},  }; @@ -261,6 +294,39 @@ static u32 chip_id_to_bt_rom_code_id(u32 id)  	}  } +#define CASE_BTC_MLME_STATE(e) case MLME_##e: return #e + +static const char *id_to_mlme_state(u32 id) +{ +	switch (id) { +	CASE_BTC_MLME_STATE(NO_LINK); +	CASE_BTC_MLME_STATE(LINKING); +	CASE_BTC_MLME_STATE(LINKED); +	default: +		return "unknown"; +	} +} + +static char *chip_id_str(u32 id) +{ +	switch (id) { +	case RTL8852A: +		return "RTL8852A"; +	case RTL8852B: +		return "RTL8852B"; +	case RTL8852C: +		return "RTL8852C"; +	case RTL8852BT: +		return "RTL8852BT"; +	case RTL8851B: +		return "RTL8851B"; +	case RTL8922A: +		return "RTL8922A"; +	default: +		return "UNKNOWN"; +	} +} +  struct rtw89_btc_btf_tlv {  	u8 type;  	u8 len; @@ -283,6 +349,7 @@ enum btc_btf_set_report_en {  	RPT_EN_BT_DEVICE_INFO,  	RPT_EN_BT_AFH_MAP,  	RPT_EN_BT_AFH_MAP_LE, +	RPT_EN_BT_TX_PWR_LVL,  	RPT_EN_FW_STEP_INFO,  	RPT_EN_TEST,  	RPT_EN_WL_ALL, @@ -660,6 +727,27 @@ enum btc_wl_link_mode {  	BTC_WLINK_MAX  }; +#define CASE_BTC_WL_LINK_MODE(e) case BTC_WLINK_## e: return #e + +static const char *id_to_linkmode(u8 id) +{ +	switch (id) { +	CASE_BTC_WL_LINK_MODE(NOLINK); +	CASE_BTC_WL_LINK_MODE(2G_STA); +	CASE_BTC_WL_LINK_MODE(2G_AP); +	CASE_BTC_WL_LINK_MODE(2G_GO); +	CASE_BTC_WL_LINK_MODE(2G_GC); +	CASE_BTC_WL_LINK_MODE(2G_SCC); +	CASE_BTC_WL_LINK_MODE(2G_MCC); +	CASE_BTC_WL_LINK_MODE(25G_MCC); +	CASE_BTC_WL_LINK_MODE(25G_DBCC); +	CASE_BTC_WL_LINK_MODE(5G); +	CASE_BTC_WL_LINK_MODE(OTHER); +	default: +		return "unknown"; +	} +} +  enum btc_wl_mrole_type {  	BTC_WLMROLE_NONE = 0x0,  	BTC_WLMROLE_STA_GC, @@ -825,6 +913,9 @@ static int _send_fw_cmd(struct rtw89_dev *rtwdev, u8 h2c_class, u8 h2c_func,  	return ret;  } +#define BTC_BT_DEF_BR_TX_PWR 4 +#define BTC_BT_DEF_LE_TX_PWR 4 +  static void _reset_btc_var(struct rtw89_dev *rtwdev, u8 type)  {  	struct rtw89_btc *btc = &rtwdev->btc; @@ -893,6 +984,9 @@ static void _reset_btc_var(struct rtw89_dev *rtwdev, u8 type)  	if (type & BTC_RESET_MDINFO)  		memset(&btc->mdinfo, 0, sizeof(btc->mdinfo)); + +	bt->link_info.bt_txpwr_desc.br_dbm = BTC_BT_DEF_BR_TX_PWR; +	bt->link_info.bt_txpwr_desc.le_dbm = BTC_BT_DEF_LE_TX_PWR;  }  static u8 _search_reg_index(struct rtw89_dev *rtwdev, u8 mreg_num, u16 reg_type, u32 target) @@ -1316,6 +1410,7 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev,  			   u8 *prptbuf, u32 index)  {  	struct rtw89_btc *btc = &rtwdev->btc; +	struct rtw89_btc_ver *fwsubver = &btc->fwinfo.fw_subver;  	const struct rtw89_btc_ver *ver = btc->ver;  	struct rtw89_btc_dm *dm = &btc->dm;  	struct rtw89_btc_rpt_cmn_info *pcinfo = NULL; @@ -1358,25 +1453,29 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev,  		if (ver->fcxbtcrpt == 1) {  			pfinfo = &pfwinfo->rpt_ctrl.finfo.v1;  			pcinfo->req_len = sizeof(pfwinfo->rpt_ctrl.finfo.v1); +			fwsubver->fcxbtcrpt = pfwinfo->rpt_ctrl.finfo.v1.fver;  		} else if (ver->fcxbtcrpt == 4) {  			pfinfo = &pfwinfo->rpt_ctrl.finfo.v4;  			pcinfo->req_len = sizeof(pfwinfo->rpt_ctrl.finfo.v4); +			fwsubver->fcxbtcrpt = pfwinfo->rpt_ctrl.finfo.v4.fver;  		} else if (ver->fcxbtcrpt == 5) {  			pfinfo = &pfwinfo->rpt_ctrl.finfo.v5;  			pcinfo->req_len = sizeof(pfwinfo->rpt_ctrl.finfo.v5); +			fwsubver->fcxbtcrpt = pfwinfo->rpt_ctrl.finfo.v5.fver;  		} else if (ver->fcxbtcrpt == 105) {  			pfinfo = &pfwinfo->rpt_ctrl.finfo.v105;  			pcinfo->req_len = sizeof(pfwinfo->rpt_ctrl.finfo.v105); +			fwsubver->fcxbtcrpt = pfwinfo->rpt_ctrl.finfo.v105.fver;  			pcinfo->req_fver = 5;  			break;  		} else if (ver->fcxbtcrpt == 8) {  			pfinfo = &pfwinfo->rpt_ctrl.finfo.v8;  			pcinfo->req_len = sizeof(pfwinfo->rpt_ctrl.finfo.v8); -			break; +			fwsubver->fcxbtcrpt = pfwinfo->rpt_ctrl.finfo.v8.fver;  		} else if (ver->fcxbtcrpt == 7) {  			pfinfo = &pfwinfo->rpt_ctrl.finfo.v7;  			pcinfo->req_len = sizeof(pfwinfo->rpt_ctrl.finfo.v7); -			break; +			fwsubver->fcxbtcrpt = pfwinfo->rpt_ctrl.finfo.v7.fver;  		} else {  			goto err;  		} @@ -1387,9 +1486,11 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev,  		if (ver->fcxtdma == 1) {  			pfinfo = &pfwinfo->rpt_fbtc_tdma.finfo.v1;  			pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_tdma.finfo.v1); +			fwsubver->fcxtdma = 0;  		} else if (ver->fcxtdma == 3 || ver->fcxtdma == 7) {  			pfinfo = &pfwinfo->rpt_fbtc_tdma.finfo.v3;  			pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_tdma.finfo.v3); +			fwsubver->fcxtdma = pfwinfo->rpt_fbtc_tdma.finfo.v3.fver;  		} else {  			goto err;  		} @@ -1400,9 +1501,11 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev,  		if (ver->fcxslots == 1) {  			pfinfo = &pfwinfo->rpt_fbtc_slots.finfo.v1;  			pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_slots.finfo.v1); +			fwsubver->fcxslots = pfwinfo->rpt_fbtc_slots.finfo.v1.fver;  		} else if (ver->fcxslots == 7) {  			pfinfo = &pfwinfo->rpt_fbtc_slots.finfo.v7;  			pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_slots.finfo.v7); +			fwsubver->fcxslots = pfwinfo->rpt_fbtc_slots.finfo.v7.fver;  		} else {  			goto err;  		} @@ -1415,22 +1518,27 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev,  			pfinfo = &pfwinfo->rpt_fbtc_cysta.finfo.v2;  			pcysta->v2 = pfwinfo->rpt_fbtc_cysta.finfo.v2;  			pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_cysta.finfo.v2); +			fwsubver->fcxcysta = pfwinfo->rpt_fbtc_cysta.finfo.v2.fver;  		} else if (ver->fcxcysta == 3) {  			pfinfo = &pfwinfo->rpt_fbtc_cysta.finfo.v3;  			pcysta->v3 = pfwinfo->rpt_fbtc_cysta.finfo.v3;  			pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_cysta.finfo.v3); +			fwsubver->fcxcysta = pfwinfo->rpt_fbtc_cysta.finfo.v3.fver;  		} else if (ver->fcxcysta == 4) {  			pfinfo = &pfwinfo->rpt_fbtc_cysta.finfo.v4;  			pcysta->v4 = pfwinfo->rpt_fbtc_cysta.finfo.v4;  			pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_cysta.finfo.v4); +			fwsubver->fcxcysta = pfwinfo->rpt_fbtc_cysta.finfo.v4.fver;  		} else if (ver->fcxcysta == 5) {  			pfinfo = &pfwinfo->rpt_fbtc_cysta.finfo.v5;  			pcysta->v5 = pfwinfo->rpt_fbtc_cysta.finfo.v5;  			pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_cysta.finfo.v5); +			fwsubver->fcxcysta = pfwinfo->rpt_fbtc_cysta.finfo.v5.fver;  		} else if (ver->fcxcysta == 7) {  			pfinfo = &pfwinfo->rpt_fbtc_cysta.finfo.v7;  			pcysta->v7 = pfwinfo->rpt_fbtc_cysta.finfo.v7;  			pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_cysta.finfo.v7); +			fwsubver->fcxcysta = pfwinfo->rpt_fbtc_cysta.finfo.v7.fver;  		} else {  			goto err;  		} @@ -1446,11 +1554,13 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev,  			pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_step.finfo.v2.step[0]) *  					  trace_step +  					  offsetof(struct rtw89_btc_fbtc_steps_v2, step); +			fwsubver->fcxstep = pfwinfo->rpt_fbtc_step.finfo.v2.fver;  		} else if (ver->fcxstep == 3) {  			pfinfo = &pfwinfo->rpt_fbtc_step.finfo.v3;  			pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_step.finfo.v3.step[0]) *  					  trace_step +  					  offsetof(struct rtw89_btc_fbtc_steps_v3, step); +			fwsubver->fcxstep = pfwinfo->rpt_fbtc_step.finfo.v3.fver;  		} else {  			goto err;  		} @@ -1461,12 +1571,15 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev,  		if (ver->fcxnullsta == 1) {  			pfinfo = &pfwinfo->rpt_fbtc_nullsta.finfo.v1;  			pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_nullsta.finfo.v1); +			fwsubver->fcxnullsta = pfwinfo->rpt_fbtc_nullsta.finfo.v1.fver;  		} else if (ver->fcxnullsta == 2) {  			pfinfo = &pfwinfo->rpt_fbtc_nullsta.finfo.v2;  			pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_nullsta.finfo.v2); +			fwsubver->fcxnullsta = pfwinfo->rpt_fbtc_nullsta.finfo.v2.fver;  		} else if (ver->fcxnullsta == 7) {  			pfinfo = &pfwinfo->rpt_fbtc_nullsta.finfo.v7;  			pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_nullsta.finfo.v7); +			fwsubver->fcxnullsta = pfwinfo->rpt_fbtc_nullsta.finfo.v7.fver;  		} else {  			goto err;  		} @@ -1477,12 +1590,15 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev,  		if (ver->fcxmreg == 1) {  			pfinfo = &pfwinfo->rpt_fbtc_mregval.finfo.v1;  			pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_mregval.finfo.v1); +			fwsubver->fcxmreg = pfwinfo->rpt_fbtc_mregval.finfo.v1.fver;  		} else if (ver->fcxmreg == 2) {  			pfinfo = &pfwinfo->rpt_fbtc_mregval.finfo.v2;  			pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_mregval.finfo.v2); +			fwsubver->fcxmreg = pfwinfo->rpt_fbtc_mregval.finfo.v2.fver;  		} else if (ver->fcxmreg == 7) {  			pfinfo = &pfwinfo->rpt_fbtc_mregval.finfo.v7;  			pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_mregval.finfo.v7); +			fwsubver->fcxmreg = pfwinfo->rpt_fbtc_mregval.finfo.v7.fver;  		} else {  			goto err;  		} @@ -1493,9 +1609,11 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev,  		if (ver->fcxgpiodbg == 7) {  			pfinfo = &pfwinfo->rpt_fbtc_gpio_dbg.finfo.v7;  			pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_gpio_dbg.finfo.v7); +			fwsubver->fcxgpiodbg = pfwinfo->rpt_fbtc_gpio_dbg.finfo.v7.fver;  		} else {  			pfinfo = &pfwinfo->rpt_fbtc_gpio_dbg.finfo.v1;  			pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_gpio_dbg.finfo.v1); +			fwsubver->fcxgpiodbg = pfwinfo->rpt_fbtc_gpio_dbg.finfo.v1.fver;  		}  		pcinfo->req_fver = ver->fcxgpiodbg;  		break; @@ -1504,9 +1622,11 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev,  		if (ver->fcxbtver == 1) {  			pfinfo = &pfwinfo->rpt_fbtc_btver.finfo.v1;  			pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_btver.finfo.v1); +			fwsubver->fcxbtver = pfwinfo->rpt_fbtc_btver.finfo.v1.fver;  		} else if (ver->fcxbtver == 7) {  			pfinfo = &pfwinfo->rpt_fbtc_btver.finfo.v7;  			pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_btver.finfo.v7); +			fwsubver->fcxbtver = pfwinfo->rpt_fbtc_btver.finfo.v7.fver;  		}  		pcinfo->req_fver = ver->fcxbtver;  		break; @@ -1515,12 +1635,15 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev,  		if (ver->fcxbtscan == 1) {  			pfinfo = &pfwinfo->rpt_fbtc_btscan.finfo.v1;  			pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_btscan.finfo.v1); +			fwsubver->fcxbtscan = pfwinfo->rpt_fbtc_btscan.finfo.v1.fver;  		} else if (ver->fcxbtscan == 2) {  			pfinfo = &pfwinfo->rpt_fbtc_btscan.finfo.v2;  			pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_btscan.finfo.v2); +			fwsubver->fcxbtscan = pfwinfo->rpt_fbtc_btscan.finfo.v2.fver;  		} else if (ver->fcxbtscan == 7) {  			pfinfo = &pfwinfo->rpt_fbtc_btscan.finfo.v7;  			pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_btscan.finfo.v7); +			fwsubver->fcxbtscan = pfwinfo->rpt_fbtc_btscan.finfo.v7.fver;  		} else {  			goto err;  		} @@ -1531,9 +1654,15 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev,  		if (ver->fcxbtafh == 1) {  			pfinfo = &pfwinfo->rpt_fbtc_btafh.finfo.v1;  			pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_btafh.finfo.v1); +			fwsubver->fcxbtafh = pfwinfo->rpt_fbtc_btafh.finfo.v1.fver;  		} else if (ver->fcxbtafh == 2) {  			pfinfo = &pfwinfo->rpt_fbtc_btafh.finfo.v2;  			pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_btafh.finfo.v2); +			fwsubver->fcxbtafh = pfwinfo->rpt_fbtc_btafh.finfo.v2.fver; +		} else if (ver->fcxbtafh == 7) { +			pfinfo = &pfwinfo->rpt_fbtc_btafh.finfo.v7; +			pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_btafh.finfo.v7); +			fwsubver->fcxbtafh = pfwinfo->rpt_fbtc_btafh.finfo.v7.fver;  		} else {  			goto err;  		} @@ -1543,6 +1672,7 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev,  		pcinfo = &pfwinfo->rpt_fbtc_btdev.cinfo;  		pfinfo = &pfwinfo->rpt_fbtc_btdev.finfo;  		pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_btdev.finfo); +		fwsubver->fcxbtdevinfo = pfwinfo->rpt_fbtc_btdev.finfo.fver;  		pcinfo->req_fver = ver->fcxbtdevinfo;  		break;  	default: @@ -1602,7 +1732,7 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev,  			wl->ver_info.fw = le32_to_cpu(prpt->v4.wl_fw_info.fw_ver);  			dm->wl_fw_cx_offload = !!le32_to_cpu(prpt->v4.wl_fw_info.cx_offload); -			for (i = RTW89_PHY_0; i < RTW89_PHY_MAX; i++) +			for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++)  				memcpy(&dm->gnt.band[i], &prpt->v4.gnt_val[i],  				       sizeof(dm->gnt.band[i])); @@ -1634,7 +1764,7 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev,  			wl->ver_info.fw = le32_to_cpu(prpt->v5.rpt_info.fw_ver);  			dm->wl_fw_cx_offload = 0; -			for (i = RTW89_PHY_0; i < RTW89_PHY_MAX; i++) +			for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++)  				memcpy(&dm->gnt.band[i], &prpt->v5.gnt_val[i][0],  				       sizeof(dm->gnt.band[i])); @@ -1661,7 +1791,7 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev,  			wl->ver_info.fw = le32_to_cpu(prpt->v105.rpt_info.fw_ver);  			dm->wl_fw_cx_offload = 0; -			for (i = RTW89_PHY_0; i < RTW89_PHY_MAX; i++) +			for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++)  				memcpy(&dm->gnt.band[i], &prpt->v105.gnt_val[i][0],  				       sizeof(dm->gnt.band[i])); @@ -1687,7 +1817,7 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev,  			wl->ver_info.fw_coex = le32_to_cpu(prpt->v7.rpt_info.cx_ver);  			wl->ver_info.fw = le32_to_cpu(prpt->v7.rpt_info.fw_ver); -			for (i = RTW89_PHY_0; i < RTW89_PHY_MAX; i++) +			for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++)  				memcpy(&dm->gnt.band[i], &prpt->v7.gnt_val[i][0],  				       sizeof(dm->gnt.band[i])); @@ -1719,7 +1849,7 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev,  			wl->ver_info.fw_coex = le32_to_cpu(prpt->v8.rpt_info.cx_ver);  			wl->ver_info.fw = le32_to_cpu(prpt->v8.rpt_info.fw_ver); -			for (i = RTW89_PHY_0; i < RTW89_PHY_MAX; i++) +			for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++)  				memcpy(&dm->gnt.band[i], &prpt->v8.gnt_val[i][0],  				       sizeof(dm->gnt.band[i])); @@ -2274,6 +2404,7 @@ static u32 rtw89_btc_fw_rpt_ver(struct rtw89_dev *rtwdev, u32 rpt_map)  			bit_map = BIT(6);  			break;  		case 3: +		case 4:  			bit_map = BIT(5);  			break;  		default: @@ -2288,6 +2419,7 @@ static u32 rtw89_btc_fw_rpt_ver(struct rtw89_dev *rtwdev, u32 rpt_map)  			bit_map = BIT(5);  			break;  		case 3: +		case 4:  			bit_map = BIT(6);  			break;  		default: @@ -2300,12 +2432,27 @@ static u32 rtw89_btc_fw_rpt_ver(struct rtw89_dev *rtwdev, u32 rpt_map)  			bit_map = BIT(8);  			break;  		case 3: +		case 4:  			bit_map = BIT(7);  			break;  		default:  			break;  		}  		break; +	case RPT_EN_BT_TX_PWR_LVL: +		switch (ver->frptmap) { +		case 0: +		case 1: +		case 2: +		case 3: +			break; +		case 4: +			bit_map = BIT(8); +			break; +		default: +			break; +		} +		break;  	case RPT_EN_FW_STEP_INFO:  		switch (ver->frptmap) {  		case 1: @@ -2315,6 +2462,9 @@ static u32 rtw89_btc_fw_rpt_ver(struct rtw89_dev *rtwdev, u32 rpt_map)  		case 3:  			bit_map = BIT(8);  			break; +		case 4: +			bit_map = BIT(9); +			break;  		default:  			break;  		} @@ -2332,6 +2482,9 @@ static u32 rtw89_btc_fw_rpt_ver(struct rtw89_dev *rtwdev, u32 rpt_map)  		case 3:  			bit_map = GENMASK(2, 0) | BIT(8);  			break; +		case 4: +			bit_map = GENMASK(2, 0) | BIT(9); +			break;  		default:  			break;  		} @@ -2348,6 +2501,9 @@ static u32 rtw89_btc_fw_rpt_ver(struct rtw89_dev *rtwdev, u32 rpt_map)  		case 3:  			bit_map = GENMASK(7, 3);  			break; +		case 4: +			bit_map = GENMASK(8, 3); +			break;  		default:  			break;  		} @@ -2364,6 +2520,9 @@ static u32 rtw89_btc_fw_rpt_ver(struct rtw89_dev *rtwdev, u32 rpt_map)  		case 3:  			bit_map = GENMASK(8, 0);  			break; +		case 4: +			bit_map = GENMASK(9, 0); +			break;  		default:  			break;  		} @@ -2380,6 +2539,9 @@ static u32 rtw89_btc_fw_rpt_ver(struct rtw89_dev *rtwdev, u32 rpt_map)  		case 3:  			bit_map = GENMASK(8, 2);  			break; +		case 4: +			bit_map = GENMASK(9, 2); +			break;  		default:  			break;  		} @@ -2669,6 +2831,16 @@ static void _fw_set_drv_info(struct rtw89_dev *rtwdev, u8 type)  	case CXDRVINFO_FDDT:  	case CXDRVINFO_MLO:  	case CXDRVINFO_OSI: +		if (!ver->fcxosi) +			return; + +		if (ver->drvinfo_type == 2) +			type = 7; +		else +			return; + +		rtw89_fw_h2c_cxdrv_osi_info(rtwdev, type); +		break;  	default:  		break;  	} @@ -2706,7 +2878,7 @@ static void _set_gnt(struct rtw89_dev *rtwdev, u8 phy_map, u8 wl_state, u8 bt_st  	if (phy_map > BTC_PHY_ALL)  		return; -	for (i = 0; i < RTW89_PHY_MAX; i++) { +	for (i = 0; i < RTW89_PHY_NUM; i++) {  		if (!(phy_map & BIT(i)))  			continue; @@ -2749,13 +2921,15 @@ static void _set_gnt_v1(struct rtw89_dev *rtwdev, u8 phy_map,  {  	struct rtw89_btc *btc = &rtwdev->btc;  	struct rtw89_btc_dm *dm = &btc->dm; +	struct rtw89_btc_fbtc_outsrc_set_info *osi = &dm->ost_info; +	struct rtw89_mac_ax_wl_act *b = dm->gnt.bt;  	struct rtw89_mac_ax_gnt *g = dm->gnt.band;  	u8 i, bt_idx = dm->bt_select + 1;  	if (phy_map > BTC_PHY_ALL)  		return; -	for (i = 0; i < RTW89_PHY_MAX; i++) { +	for (i = 0; i < RTW89_PHY_NUM; i++) {  		if (!(phy_map & BIT(i)))  			continue; @@ -2797,21 +2971,35 @@ static void _set_gnt_v1(struct rtw89_dev *rtwdev, u8 phy_map,  			switch (wlact_state) {  			case BTC_WLACT_HW: -				dm->gnt.bt[i].wlan_act_en = 0; -				dm->gnt.bt[i].wlan_act = 0; +				b[i].wlan_act_en = 0; +				b[i].wlan_act = 0;  				break;  			case BTC_WLACT_SW_LO: -				dm->gnt.bt[i].wlan_act_en = 1; -				dm->gnt.bt[i].wlan_act = 0; +				b[i].wlan_act_en = 1; +				b[i].wlan_act = 0;  				break;  			case BTC_WLACT_SW_HI: -				dm->gnt.bt[i].wlan_act_en = 1; -				dm->gnt.bt[i].wlan_act = 1; +				b[i].wlan_act_en = 1; +				b[i].wlan_act = 1;  				break;  			}  		}  	} -	rtw89_mac_cfg_gnt_v2(rtwdev, &dm->gnt); + +	if (!btc->ver->fcxosi) { +		rtw89_mac_cfg_gnt_v2(rtwdev, &dm->gnt); +		return; +	} + +	memcpy(osi->gnt_set, dm->gnt.band, sizeof(osi->gnt_set)); +	memcpy(osi->wlact_set, dm->gnt.bt, sizeof(osi->wlact_set)); + +	/* GBT source should be GBT_S1 in 1+1 (HWB0:5G + HWB1:2G) case */ +	if (osi->rf_band[BTC_RF_S0] == 1 && +	    osi->rf_band[BTC_RF_S1] == 0) +		osi->rf_gbt_source = BTC_RF_S1; +	else +		osi->rf_gbt_source = BTC_RF_S0;  }  #define BTC_TDMA_WLROLE_MAX 3 @@ -2955,7 +3143,7 @@ static void _set_rf_trx_para(struct rtw89_dev *rtwdev)  	if (ver->fwlrole == 0) {  		link_mode = wl->role_info.link_mode; -		for (i = 0; i < RTW89_PHY_MAX; i++) { +		for (i = 0; i < RTW89_PHY_NUM; i++) {  			if (wl->dbcc_info.real_band[i] == RTW89_BAND_2G)  				dbcc_2g_phy = i;  		} @@ -3053,7 +3241,7 @@ static void _update_btc_state_map(struct rtw89_dev *rtwdev)  	}  } -static void _set_bt_afh_info(struct rtw89_dev *rtwdev) +static void _set_bt_afh_info_v0(struct rtw89_dev *rtwdev)  {  	const struct rtw89_chip_info *chip = rtwdev->chip;  	struct rtw89_btc *btc = &rtwdev->btc; @@ -3222,6 +3410,115 @@ static void _set_bt_afh_info(struct rtw89_dev *rtwdev)  	btc->cx.cnt_wl[BTC_WCNT_CH_UPDATE]++;  } +static void _set_bt_afh_info_v1(struct rtw89_dev *rtwdev) +{ +	const struct rtw89_chip_info *chip = rtwdev->chip; +	struct rtw89_btc *btc = &rtwdev->btc; +	struct rtw89_btc_wl_info *wl = &btc->cx.wl; +	struct rtw89_btc_wl_role_info_v8 *wl_rinfo = &wl->role_info_v8; +	struct rtw89_btc_wl_afh_info *wl_afh = &wl->afh_info; +	struct rtw89_btc_bt_info *bt = &btc->cx.bt; +	struct rtw89_btc_wl_rlink *rlink; +	u8 en = 0, ch = 0, bw = 0, buf[3] = {}; +	u8 i, j, link_mode; + +	if (btc->manual_ctrl || wl->status.map.scan) +		return; + +	link_mode = wl_rinfo->link_mode; + +	for (i = 0; i < btc->ver->max_role_num; i++) { +		for (j = RTW89_MAC_0; j < RTW89_MAC_NUM; j++) { +			if (wl->status.map.rf_off || bt->whql_test || +			    link_mode == BTC_WLINK_NOLINK || +			    link_mode == BTC_WLINK_5G) +				break; + +			rlink = &wl_rinfo->rlink[i][j]; + +			/* Don't care no-connected/non-2G-band role */ +			if (!rlink->connected || !rlink->active || +			    rlink->rf_band != RTW89_BAND_2G) +				continue; + +			en = 1; +			ch = rlink->ch; +			bw = rlink->bw; + +			if (link_mode == BTC_WLINK_2G_MCC && +			    (rlink->role == RTW89_WIFI_ROLE_AP || +			     rlink->role == RTW89_WIFI_ROLE_P2P_GO || +			     rlink->role == RTW89_WIFI_ROLE_P2P_CLIENT)) { +				/* for 2.4G MCC, take role = ap/go/gc */ +				break; +			} else if (link_mode != BTC_WLINK_2G_SCC || +				   rlink->bw == RTW89_CHANNEL_WIDTH_40) { +				/* for 2.4G scc, take bw = 40M */ +				break; +			} +		} +	} + +	/* default AFH channel sapn = center-ch +- 6MHz */ +	switch (bw) { +	case RTW89_CHANNEL_WIDTH_20: +		if (btc->dm.freerun || btc->dm.fddt_train) +			bw = 48; +		else +			bw = 20 + chip->afh_guard_ch * 2; +		break; +	case RTW89_CHANNEL_WIDTH_40: +		if (btc->dm.freerun) +			bw = 40 + chip->afh_guard_ch * 2; +		else +			bw = 40; +		break; +	case RTW89_CHANNEL_WIDTH_5: +		bw = 5 + chip->afh_guard_ch * 2; +		break; +	case RTW89_CHANNEL_WIDTH_10: +		bw = 10 + chip->afh_guard_ch * 2; +		break; +	default: +		en = false; /* turn off AFH info if invalid BW */ +		bw = 0; +		ch = 0; +		break; +	} + +	if (!en || ch > 14 || ch == 0) { +		en = false; +		bw = 0; +		ch = 0; +	} + +	if (wl_afh->en == en && +	    wl_afh->ch == ch && +	    wl_afh->bw == bw && +	    (!bt->enable.now || bt->enable.last)) +		return; + +	wl_afh->en = buf[0]; +	wl_afh->ch = buf[1]; +	wl_afh->bw = buf[2]; + +	if (_send_fw_cmd(rtwdev, BTFC_SET, SET_BT_WL_CH_INFO, &wl->afh_info, 3)) { +		rtw89_debug(rtwdev, RTW89_DBG_BTC, +			    "[BTC], %s(): en=%d, ch=%d, bw=%d\n", +			    __func__, en, ch, bw); + +		btc->cx.cnt_wl[BTC_WCNT_CH_UPDATE]++; +	} +} + +static void _set_bt_afh_info(struct rtw89_dev *rtwdev) +{ +	if (rtwdev->chip->chip_id == RTL8922A) +		_set_bt_afh_info_v1(rtwdev); +	else +		_set_bt_afh_info_v0(rtwdev); +} +  static bool _check_freerun(struct rtw89_dev *rtwdev)  {  	struct rtw89_btc *btc = &rtwdev->btc; @@ -3707,6 +4004,15 @@ void rtw89_btc_set_policy_v1(struct rtw89_dev *rtwdev, u16 policy_type)  	u32 tbl_w1, tbl_b1, tbl_b4;  	u16 dur_2; +	if (wl->status.map.lps) { +		_slot_set_le(btc, CXST_E2G, s_def[CXST_E2G].dur, +			     s_def[CXST_E2G].cxtbl, s_def[CXST_E2G].cxtype); +		_slot_set_le(btc, CXST_E5G, s_def[CXST_E5G].dur, +			     s_def[CXST_E5G].cxtbl, s_def[CXST_E5G].cxtype); +		_slot_set_le(btc, CXST_EBT, s_def[CXST_EBT].dur, +			     s_def[CXST_EBT].cxtbl, s_def[CXST_EBT].cxtype); +	} +  	type = FIELD_GET(BTC_CXP_MASK, policy_type);  	if (btc->ant_type == BTC_ANT_SHARED) { @@ -3827,13 +4133,13 @@ void rtw89_btc_set_policy_v1(struct rtw89_dev *rtwdev, u16 policy_type)  		switch (policy_type) {  		case BTC_CXP_OFFE_2GBWISOB: /* for normal-case */ -			_slot_set(btc, CXST_E2G, 0, tbl_w1, SLOT_ISO); +			_slot_set(btc, CXST_E2G, 5, tbl_w1, SLOT_ISO);  			_slot_set_le(btc, CXST_EBT, s_def[CXST_EBT].dur,  				     s_def[CXST_EBT].cxtbl, s_def[CXST_EBT].cxtype);  			_slot_set_dur(btc, CXST_EBT, dur_2);  			break;  		case BTC_CXP_OFFE_2GISOB: /* for bt no-link */ -			_slot_set(btc, CXST_E2G, 0, cxtbl[1], SLOT_ISO); +			_slot_set(btc, CXST_E2G, 5, cxtbl[1], SLOT_ISO);  			_slot_set_le(btc, CXST_EBT, s_def[CXST_EBT].dur,  				     s_def[CXST_EBT].cxtbl, s_def[CXST_EBT].cxtype);  			_slot_set_dur(btc, CXST_EBT, dur_2); @@ -3859,15 +4165,15 @@ void rtw89_btc_set_policy_v1(struct rtw89_dev *rtwdev, u16 policy_type)  			break;  		case BTC_CXP_OFFE_2GBWMIXB:  			if (a2dp->exist) -				_slot_set(btc, CXST_E2G, 0, cxtbl[2], SLOT_MIX); +				_slot_set(btc, CXST_E2G, 5, cxtbl[2], SLOT_MIX);  			else -				_slot_set(btc, CXST_E2G, 0, tbl_w1, SLOT_MIX); -			_slot_set_le(btc, CXST_EBT, s_def[CXST_EBT].dur, +				_slot_set(btc, CXST_E2G, 5, tbl_w1, SLOT_MIX); +			_slot_set_le(btc, CXST_EBT, cpu_to_le16(40),  				     s_def[CXST_EBT].cxtbl, s_def[CXST_EBT].cxtype);  			break;  		case BTC_CXP_OFFE_WL: /* for 4-way */ -			_slot_set(btc, CXST_E2G, 0, cxtbl[1], SLOT_MIX); -			_slot_set(btc, CXST_EBT, 0, cxtbl[1], SLOT_MIX); +			_slot_set(btc, CXST_E2G, 5, cxtbl[1], SLOT_MIX); +			_slot_set(btc, CXST_EBT, 5, cxtbl[1], SLOT_MIX);  			break;  		default:  			break; @@ -4240,7 +4546,7 @@ static void _set_ant_v0(struct rtw89_dev *rtwdev, bool force_exec,  	case BTC_ANT_W2G:  		rtw89_chip_cfg_ctrl_path(rtwdev, BTC_CTRL_BY_WL);  		if (rtwdev->dbcc_en) { -			for (i = 0; i < RTW89_PHY_MAX; i++) { +			for (i = 0; i < RTW89_PHY_NUM; i++) {  				b2g = (wl_dinfo->real_band[i] == RTW89_BAND_2G);  				gnt_wl_ctrl = b2g ? BTC_GNT_HW : BTC_GNT_SW_HI; @@ -4583,23 +4889,16 @@ static void _action_bt_hid(struct rtw89_dev *rtwdev)  static void _action_bt_a2dp(struct rtw89_dev *rtwdev)  {  	struct rtw89_btc *btc = &rtwdev->btc; -	struct rtw89_btc_bt_link_info *bt_linfo = &btc->cx.bt.link_info; -	struct rtw89_btc_bt_a2dp_desc a2dp = bt_linfo->a2dp_desc;  	struct rtw89_btc_dm *dm = &btc->dm;  	_set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G); +	dm->slot_dur[CXST_W1] = 20; +	dm->slot_dur[CXST_B1] = BTC_B1_MAX; +  	switch (btc->cx.state_map) {  	case BTC_WBUSY_BNOSCAN: /* wl-busy + bt-A2DP */ -		if (a2dp.vendor_id == 0x4c || dm->leak_ap) { -			dm->slot_dur[CXST_W1] = 40; -			dm->slot_dur[CXST_B1] = 200; -			_set_policy(rtwdev, -				    BTC_CXP_PAUTO_TDW1B1, BTC_ACT_BT_A2DP); -		} else { -			_set_policy(rtwdev, -				    BTC_CXP_PAUTO_TD50B1, BTC_ACT_BT_A2DP); -		} +		_set_policy(rtwdev, BTC_CXP_PAUTO_TDW1B1, BTC_ACT_BT_A2DP);  		break;  	case BTC_WBUSY_BSCAN: /* wl-busy + bt-inq + bt-A2DP */  		_set_policy(rtwdev, BTC_CXP_PAUTO2_TD3050, BTC_ACT_BT_A2DP); @@ -4609,15 +4908,10 @@ static void _action_bt_a2dp(struct rtw89_dev *rtwdev)  		break;  	case BTC_WSCAN_BNOSCAN: /* wl-scan + bt-A2DP */  	case BTC_WLINKING: /* wl-connecting + bt-A2DP */ -		if (a2dp.vendor_id == 0x4c || dm->leak_ap) { -			dm->slot_dur[CXST_W1] = 40; -			dm->slot_dur[CXST_B1] = 200; -			_set_policy(rtwdev, BTC_CXP_AUTO_TDW1B1, -				    BTC_ACT_BT_A2DP); -		} else { -			_set_policy(rtwdev, BTC_CXP_AUTO_TD50B1, -				    BTC_ACT_BT_A2DP); -		} +		if (btc->cx.wl.rfk_info.con_rfk) +			_set_policy(rtwdev, BTC_CXP_OFF_BT, BTC_ACT_BT_A2DP); +		else +			_set_policy(rtwdev, BTC_CXP_AUTO_TDW1B1, BTC_ACT_BT_A2DP);  		break;  	case BTC_WIDLE:  /* wl-idle + bt-A2DP */  		_set_policy(rtwdev, BTC_CXP_AUTO_TD20B1, BTC_ACT_BT_A2DP); @@ -4645,7 +4939,10 @@ static void _action_bt_a2dpsink(struct rtw89_dev *rtwdev)  		_set_policy(rtwdev, BTC_CXP_FIX_TD2060, BTC_ACT_BT_A2DPSINK);  		break;  	case BTC_WLINKING: /* wl-connecting + bt-A2dp_Sink */ -		_set_policy(rtwdev, BTC_CXP_FIX_TD3030, BTC_ACT_BT_A2DPSINK); +		if (btc->cx.wl.rfk_info.con_rfk) +			_set_policy(rtwdev, BTC_CXP_OFF_BT, BTC_ACT_BT_A2DPSINK); +		else +			_set_policy(rtwdev, BTC_CXP_FIX_TD3030, BTC_ACT_BT_A2DPSINK);  		break;  	case BTC_WIDLE: /* wl-idle + bt-A2dp_Sink */  		_set_policy(rtwdev, BTC_CXP_FIX_TD2080, BTC_ACT_BT_A2DPSINK); @@ -4693,27 +4990,20 @@ static void _action_bt_pan(struct rtw89_dev *rtwdev)  static void _action_bt_a2dp_hid(struct rtw89_dev *rtwdev)  {  	struct rtw89_btc *btc = &rtwdev->btc; -	struct rtw89_btc_bt_link_info *bt_linfo = &btc->cx.bt.link_info; -	struct rtw89_btc_bt_a2dp_desc a2dp = bt_linfo->a2dp_desc;  	struct rtw89_btc_dm *dm = &btc->dm;  	_set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G); +	dm->slot_dur[CXST_W1] = 20; +	dm->slot_dur[CXST_B1] = BTC_B1_MAX; +  	switch (btc->cx.state_map) {  	case BTC_WBUSY_BNOSCAN: /* wl-busy + bt-A2DP+HID */  	case BTC_WIDLE:  /* wl-idle + bt-A2DP */ -		if (a2dp.vendor_id == 0x4c || dm->leak_ap) { -			dm->slot_dur[CXST_W1] = 40; -			dm->slot_dur[CXST_B1] = 200; -			_set_policy(rtwdev, -				    BTC_CXP_PAUTO_TDW1B1, BTC_ACT_BT_A2DP_HID); -		} else { -			_set_policy(rtwdev, -				    BTC_CXP_PAUTO_TD50B1, BTC_ACT_BT_A2DP_HID); -		} +		_set_policy(rtwdev, BTC_CXP_PAUTO_TDW1B1, BTC_ACT_BT_A2DP_HID);  		break;  	case BTC_WBUSY_BSCAN: /* wl-busy + bt-inq + bt-A2DP+HID */ -		_set_policy(rtwdev, BTC_CXP_PAUTO2_TD3050, BTC_ACT_BT_A2DP_HID); +		_set_policy(rtwdev, BTC_CXP_PAUTO2_TD3070, BTC_ACT_BT_A2DP_HID);  		break;  	case BTC_WSCAN_BSCAN: /* wl-scan + bt-inq + bt-A2DP+HID */ @@ -4721,15 +5011,10 @@ static void _action_bt_a2dp_hid(struct rtw89_dev *rtwdev)  		break;  	case BTC_WSCAN_BNOSCAN: /* wl-scan + bt-A2DP+HID */  	case BTC_WLINKING: /* wl-connecting + bt-A2DP+HID */ -		if (a2dp.vendor_id == 0x4c || dm->leak_ap) { -			dm->slot_dur[CXST_W1] = 40; -			dm->slot_dur[CXST_B1] = 200; -			_set_policy(rtwdev, BTC_CXP_AUTO_TDW1B1, -				    BTC_ACT_BT_A2DP_HID); -		} else { -			_set_policy(rtwdev, BTC_CXP_AUTO_TD50B1, -				    BTC_ACT_BT_A2DP_HID); -		} +		if (btc->cx.wl.rfk_info.con_rfk) +			_set_policy(rtwdev, BTC_CXP_OFF_BT, BTC_ACT_BT_A2DP_HID); +		else +			_set_policy(rtwdev, BTC_CXP_AUTO_TDW1B1, BTC_ACT_BT_A2DP_HID);  		break;  	}  } @@ -4876,16 +5161,14 @@ static void _set_btg_ctrl(struct rtw89_dev *rtwdev)  	struct rtw89_btc_wl_role_info_v2 *wl_rinfo_v2 = &wl->role_info_v2;  	struct rtw89_btc_wl_role_info_v7 *wl_rinfo_v7 = &wl->role_info_v7;  	struct rtw89_btc_wl_role_info_v8 *wl_rinfo_v8 = &wl->role_info_v8; +	struct rtw89_btc_fbtc_outsrc_set_info *o_info = &btc->dm.ost_info;  	struct rtw89_btc_wl_role_info *wl_rinfo_v0 = &wl->role_info; -	struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info;  	const struct rtw89_chip_info *chip = rtwdev->chip;  	const struct rtw89_btc_ver *ver = btc->ver;  	struct rtw89_btc_bt_info *bt = &btc->cx.bt;  	struct rtw89_btc_dm *dm = &btc->dm;  	struct _wl_rinfo_now wl_rinfo; -	u32 run_reason = btc->dm.run_reason; -	u32 is_btg; -	u8 i, val; +	u32 is_btg = BTC_BTGCTRL_DISABLE;  	if (btc->manual_ctrl)  		return; @@ -4903,63 +5186,62 @@ static void _set_btg_ctrl(struct rtw89_dev *rtwdev)  	else  		return; -	if (rtwdev->dbcc_en) { -		if (ver->fwlrole == 0) { -			wl_rinfo.dbcc_2g_phy = RTW89_PHY_MAX; +	/* notify halbb ignore GNT_BT or not for WL BB Rx-AGC control */ +	if (btc->ant_type == BTC_ANT_SHARED) { +		if (!(bt->run_patch_code && bt->enable.now)) +			is_btg = BTC_BTGCTRL_DISABLE; +		else if (wl_rinfo.link_mode != BTC_WLINK_5G) +			is_btg = BTC_BTGCTRL_ENABLE; +		else +			is_btg = BTC_BTGCTRL_DISABLE; -			for (i = 0; i < RTW89_PHY_MAX; i++) { -				if (wl_dinfo->real_band[i] == RTW89_BAND_2G) -					wl_rinfo.dbcc_2g_phy = i; -			} -		} else if (ver->fwlrole == 1) { -			wl_rinfo.dbcc_2g_phy = wl_rinfo_v1->dbcc_2g_phy; -		} else if (ver->fwlrole == 2) { -			wl_rinfo.dbcc_2g_phy = wl_rinfo_v2->dbcc_2g_phy; -		} else if (ver->fwlrole == 7) { -			wl_rinfo.dbcc_2g_phy = wl_rinfo_v7->dbcc_2g_phy; -		} else if (ver->fwlrole == 8) { -			wl_rinfo.dbcc_2g_phy = wl_rinfo_v8->dbcc_2g_phy; -		} else { -			return; -		} +		/* bb call ctrl_btg() in WL FW by slot */ +		if (!ver->fcxosi && +		    wl_rinfo.link_mode == BTC_WLINK_25G_MCC) +			is_btg = BTC_BTGCTRL_BB_GNT_FWCTRL;  	} -	if (wl_rinfo.link_mode == BTC_WLINK_25G_MCC) -		is_btg = BTC_BTGCTRL_BB_GNT_FWCTRL; -	else if (!(bt->run_patch_code && bt->enable.now)) -		is_btg = BTC_BTGCTRL_DISABLE; -	else if (wl_rinfo.link_mode == BTC_WLINK_5G) -		is_btg = BTC_BTGCTRL_DISABLE; -	else if (dm->freerun) -		is_btg = BTC_BTGCTRL_DISABLE; -	else if (rtwdev->dbcc_en && wl_rinfo.dbcc_2g_phy != RTW89_PHY_1) -		is_btg = BTC_BTGCTRL_DISABLE; +	if (is_btg == dm->wl_btg_rx) +		return;  	else -		is_btg = BTC_BTGCTRL_ENABLE; +		dm->wl_btg_rx = is_btg; -	if (dm->wl_btg_rx_rb != dm->wl_btg_rx && -	    dm->wl_btg_rx_rb != BTC_BTGCTRL_BB_GNT_NOTFOUND) { -		_get_reg_status(rtwdev, BTC_CSTATUS_BB_GNT_MUX, &val); -		dm->wl_btg_rx_rb = val; -	} +	/* skip setup if btg_ctrl set by wl fw */ +	if (!ver->fcxosi && is_btg > BTC_BTGCTRL_ENABLE) +		return; -	if (run_reason == BTC_RSN_NTFY_INIT || -	    run_reason == BTC_RSN_NTFY_SWBAND || -	    dm->wl_btg_rx_rb != dm->wl_btg_rx || -	    is_btg != dm->wl_btg_rx) { +	/* Below flow is for BTC_FEAT_NEW_BBAPI_FLOW = 1 */ +	if (o_info->rf_band[BTC_RF_S0] != o_info->rf_band[BTC_RF_S1]) {/* 1+1 */ +		if (o_info->rf_band[BTC_RF_S0]) /* Non-2G */ +			o_info->btg_rx[BTC_RF_S0] = BTC_BTGCTRL_DISABLE; +		else +			o_info->btg_rx[BTC_RF_S0] = is_btg; -		dm->wl_btg_rx = is_btg; +		if (o_info->rf_band[BTC_RF_S1]) /* Non-2G */ +			o_info->btg_rx[BTC_RF_S1] = BTC_BTGCTRL_DISABLE; +		else +			o_info->btg_rx[BTC_RF_S1] = is_btg; +	} else { /* 2+0 or 0+2 */ +		o_info->btg_rx[BTC_RF_S0] = is_btg; +		o_info->btg_rx[BTC_RF_S1] = is_btg; +	} -		if (is_btg > BTC_BTGCTRL_ENABLE) -			return; +	if (ver->fcxosi) +		return; -		chip->ops->ctrl_btg_bt_rx(rtwdev, is_btg, RTW89_PHY_0); -	} +	chip->ops->ctrl_btg_bt_rx(rtwdev, o_info->btg_rx[BTC_RF_S0], +				  RTW89_PHY_0); +	if (chip->chip_id != RTL8922A) +		return; + +	chip->ops->ctrl_btg_bt_rx(rtwdev, o_info->btg_rx[BTC_RF_S1], +				  RTW89_PHY_1);  }  static void _set_wl_preagc_ctrl(struct rtw89_dev *rtwdev)  {  	struct rtw89_btc *btc = &rtwdev->btc; +	struct rtw89_btc_fbtc_outsrc_set_info *o_info = &btc->dm.ost_info;  	struct rtw89_btc_bt_link_info *bt_linfo = &btc->cx.bt.link_info;  	struct rtw89_btc_wl_info *wl = &btc->cx.wl;  	struct rtw89_btc_wl_role_info_v2 *rinfo_v2 = &wl->role_info_v2; @@ -4991,9 +5273,7 @@ static void _set_wl_preagc_ctrl(struct rtw89_dev *rtwdev)  		return;  	} -	if (link_mode == BTC_WLINK_25G_MCC) { -		is_preagc = BTC_PREAGC_BB_FWCTRL; -	} else if (!(bt->run_patch_code && bt->enable.now)) { +	if (!(bt->run_patch_code && bt->enable.now)) {  		is_preagc = BTC_PREAGC_DISABLE;  	} else if (link_mode == BTC_WLINK_5G) {  		is_preagc = BTC_PREAGC_DISABLE; @@ -5013,6 +5293,9 @@ static void _set_wl_preagc_ctrl(struct rtw89_dev *rtwdev)  		is_preagc = BTC_PREAGC_ENABLE;  	} +	if (!btc->ver->fcxosi && link_mode == BTC_WLINK_25G_MCC) +		is_preagc = BTC_PREAGC_BB_FWCTRL; +  	if (dm->wl_pre_agc_rb != dm->wl_pre_agc &&  	    dm->wl_pre_agc_rb != BTC_PREAGC_NOTFOUND) {  		_get_reg_status(rtwdev, BTC_CSTATUS_BB_PRE_AGC, &val); @@ -5026,9 +5309,34 @@ static void _set_wl_preagc_ctrl(struct rtw89_dev *rtwdev)  	    is_preagc != dm->wl_pre_agc) {  		dm->wl_pre_agc = is_preagc; -		if (is_preagc > BTC_PREAGC_ENABLE) +		if (!btc->ver->fcxosi && is_preagc > BTC_PREAGC_ENABLE)  			return; -		chip->ops->ctrl_nbtg_bt_tx(rtwdev, dm->wl_pre_agc, RTW89_PHY_0); + +		if (o_info->rf_band[BTC_RF_S0] != o_info->rf_band[BTC_RF_S1]) {/* 1+1 */ +			if (o_info->rf_band[BTC_RF_S0]) /* Non-2G */ +				o_info->nbtg_tx[BTC_RF_S0] = BTC_PREAGC_DISABLE; +			else +				o_info->nbtg_tx[BTC_RF_S0] = is_preagc; + +			if (o_info->rf_band[BTC_RF_S1]) /* Non-2G */ +				o_info->nbtg_tx[BTC_RF_S1] = BTC_PREAGC_DISABLE; +			else +				o_info->nbtg_tx[BTC_RF_S1] = is_preagc; + +		} else { /* 2+0 or 0+2 */ +			o_info->nbtg_tx[BTC_RF_S0] = is_preagc; +			o_info->nbtg_tx[BTC_RF_S1] = is_preagc; +		} + +		if (btc->ver->fcxosi) +			return; + +		chip->ops->ctrl_nbtg_bt_tx(rtwdev, o_info->nbtg_tx[BTC_RF_S0], +					   RTW89_PHY_0); +		if (chip->chip_id != RTL8922A) +			return; +		chip->ops->ctrl_nbtg_bt_tx(rtwdev, o_info->nbtg_tx[BTC_RF_S1], +					   RTW89_PHY_1);  	}  } @@ -5241,15 +5549,47 @@ static void _set_bt_rx_scan_pri(struct rtw89_dev *rtwdev)  	_write_scbd(rtwdev, BTC_WSCB_RXSCAN_PRI, (bool)(!!bt->scan_rx_low_pri));  } +static void _wl_req_mac(struct rtw89_dev *rtwdev, u8 mac) +{ +	struct rtw89_btc *btc = &rtwdev->btc; +	struct rtw89_btc_wl_info *wl = &btc->cx.wl; +	struct rtw89_btc_dm *dm = &btc->dm; +	u32 add; + +	if (mac == wl->pta_req_mac) +		return; + +	dm->ost_info.pta_req_hw_band = mac; +	wl->pta_req_mac = mac; +	wl->pta_reg_mac_chg = true; + +	if (btc->ver->fcxosi) +		return; + +	if (rtwdev->chip->chip_gen == RTW89_CHIP_BE) +		add = R_BE_BTC_CFG; +	else +		add = R_AX_BTC_CFG; + +	if (mac == RTW89_MAC_0) +		rtw89_write32_clr(rtwdev, add, B_AX_WL_SRC); +	else +		rtw89_write32_set(rtwdev, add, B_AX_WL_SRC); +} +  static void _action_common(struct rtw89_dev *rtwdev)  {  	struct rtw89_btc *btc = &rtwdev->btc;  	struct rtw89_btc_wl_info *wl = &btc->cx.wl; +	struct rtw89_btc_wl_role_info_v8 *rinfo_v8 = &wl->role_info_v8;  	struct rtw89_btc_wl_smap *wl_smap = &wl->status.map;  	struct rtw89_btc_bt_info *bt = &btc->cx.bt;  	struct rtw89_btc_dm *dm = &btc->dm;  	u32 bt_rom_code_id, bt_fw_ver; +	if (btc->ver->fwlrole == 8) +		_wl_req_mac(rtwdev, rinfo_v8->pta_req_band); +  	_set_btg_ctrl(rtwdev);  	_set_wl_preagc_ctrl(rtwdev);  	_set_wl_tx_limit(rtwdev); @@ -5285,7 +5625,18 @@ static void _action_common(struct rtw89_dev *rtwdev)  		wl->scbd_change = false;  		btc->cx.cnt_wl[BTC_WCNT_SCBDUPDATE]++;  	} + +	if (btc->ver->fcxosi) { +		if (memcmp(&dm->ost_info_last, &dm->ost_info, +			   sizeof(dm->ost_info_last)) || +		    dm->run_reason == BTC_RSN_NTFY_INIT || +		    dm->run_reason == BTC_RSN_NTFY_RADIO_STATE) { +			dm->ost_info_last = dm->ost_info; +			_fw_set_drv_info(rtwdev, CXDRVINFO_OSI); +		} +	}  	btc->dm.tdma_instant_excute = 0; +	wl->pta_reg_mac_chg = false;  }  static void _action_by_bt(struct rtw89_dev *rtwdev) @@ -5408,7 +5759,8 @@ static void _action_wl_scan(struct rtw89_dev *rtwdev)  	struct rtw89_btc_wl_info *wl = &btc->cx.wl;  	struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info; -	if (RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD, &rtwdev->fw)) { +	if (btc->cx.state_map != BTC_WLINKING && +	    RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD, &rtwdev->fw)) {  		_action_wl_25g_mcc(rtwdev);  		rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], Scan offload!\n");  	} else if (rtwdev->dbcc_en) { @@ -5747,14 +6099,6 @@ _update_rssi_state(struct rtw89_dev *rtwdev, u8 pre_state, u8 rssi, u8 thresh)  	return next_state;  } -static void _wl_req_mac(struct rtw89_dev *rtwdev, u8 mac) -{ -	if (mac == RTW89_MAC_0) -		rtw89_write32_clr(rtwdev, R_AX_BTC_CFG, B_AX_WL_SRC); -	else -		rtw89_write32_set(rtwdev, R_AX_BTC_CFG, B_AX_WL_SRC); -} -  static  void _update_dbcc_band(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx)  { @@ -5798,7 +6142,7 @@ static void _update_wl_info(struct rtw89_dev *rtwdev)  		phy = wl_linfo[i].phy;  		/* check dbcc role */ -		if (rtwdev->dbcc_en && phy < RTW89_PHY_MAX) { +		if (rtwdev->dbcc_en && phy < RTW89_PHY_NUM) {  			wl_dinfo->role[phy] = wl_linfo[i].role;  			wl_dinfo->op_band[phy] = wl_linfo[i].band;  			_update_dbcc_band(rtwdev, phy); @@ -5948,7 +6292,7 @@ static void _update_wl_info_v1(struct rtw89_dev *rtwdev)  		phy = wl_linfo[i].phy; -		if (rtwdev->dbcc_en && phy < RTW89_PHY_MAX) { +		if (rtwdev->dbcc_en && phy < RTW89_PHY_NUM) {  			wl_dinfo->role[phy] = wl_linfo[i].role;  			wl_dinfo->op_band[phy] = wl_linfo[i].band;  			_update_dbcc_band(rtwdev, phy); @@ -6098,7 +6442,7 @@ static void _update_wl_info_v2(struct rtw89_dev *rtwdev)  		phy = wl_linfo[i].phy; -		if (rtwdev->dbcc_en && phy < RTW89_PHY_MAX) { +		if (rtwdev->dbcc_en && phy < RTW89_PHY_NUM) {  			wl_dinfo->role[phy] = wl_linfo[i].role;  			wl_dinfo->op_band[phy] = wl_linfo[i].band;  			_update_dbcc_band(rtwdev, phy); @@ -6250,23 +6594,16 @@ static bool _chk_role_ch_group(const struct rtw89_btc_chdef *r1,  }  static u8 _chk_dbcc(struct rtw89_dev *rtwdev, struct rtw89_btc_chdef *ch, -		    u8 *phy, u8 *role, u8 *dbcc_2g_phy) +		    u8 *phy, u8 *role, u8 link_cnt)  {  	struct rtw89_btc_wl_info *wl = &rtwdev->btc.cx.wl;  	struct rtw89_btc_wl_role_info_v7 *rinfo_v7 = &wl->role_info_v7;  	struct rtw89_btc_wl_role_info_v8 *rinfo_v8 = &wl->role_info_v8;  	bool is_2g_ch_exist = false, is_multi_role_in_2g_phy = false; -	u8 j, k, dbcc_2g_cid, dbcc_2g_cid2, connect_cnt; - -	if (rtwdev->btc.ver->fwlrole == 7) -		connect_cnt = rinfo_v7->connect_cnt; -	else if (rtwdev->btc.ver->fwlrole == 8) -		connect_cnt = rinfo_v8->connect_cnt; -	else -		return BTC_WLINK_NOLINK; +	u8 j, k, dbcc_2g_cid, dbcc_2g_cid2, dbcc_2g_phy, pta_req_band;  	/* find out the 2G-PHY by connect-id ->ch  */ -	for (j = 0; j < connect_cnt; j++) { +	for (j = 0; j < link_cnt; j++) {  		if (ch[j].center_ch <= 14) {  			is_2g_ch_exist = true;  			break; @@ -6275,21 +6612,33 @@ static u8 _chk_dbcc(struct rtw89_dev *rtwdev, struct rtw89_btc_chdef *ch,  	/* If no any 2G-port exist, it's impossible because 5G-exclude */  	if (!is_2g_ch_exist) -		return BTC_WLINK_OTHER; +		return BTC_WLINK_5G;  	dbcc_2g_cid = j; -	*dbcc_2g_phy = phy[dbcc_2g_cid]; +	dbcc_2g_phy = phy[dbcc_2g_cid]; + +	if (dbcc_2g_phy == RTW89_PHY_1) +		pta_req_band = RTW89_PHY_1; +	else +		pta_req_band = RTW89_PHY_0; + +	if (rtwdev->btc.ver->fwlrole == 7) { +		rinfo_v7->dbcc_2g_phy = dbcc_2g_phy; +	} else if (rtwdev->btc.ver->fwlrole == 8) { +		rinfo_v8->dbcc_2g_phy = dbcc_2g_phy; +		rinfo_v8->pta_req_band = pta_req_band; +	}  	/* connect_cnt <= 2 */ -	if (connect_cnt < BTC_TDMA_WLROLE_MAX) +	if (link_cnt < BTC_TDMA_WLROLE_MAX)  		return (_get_role_link_mode((role[dbcc_2g_cid])));  	/* find the other-port in the 2G-PHY, ex: PHY-0:6G, PHY1: mcc/scc */ -	for (k = 0; k < connect_cnt; k++) { +	for (k = 0; k < link_cnt; k++) {  		if (k == dbcc_2g_cid)  			continue; -		if (phy[k] == *dbcc_2g_phy) { +		if (phy[k] == dbcc_2g_phy) {  			is_multi_role_in_2g_phy = true;  			dbcc_2g_cid2 = k;  			break; @@ -6389,7 +6738,7 @@ static void _update_wl_info_v7(struct rtw89_dev *rtwdev, u8 rid)  	struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info;  	struct rtw89_btc_wl_link_info *wl_linfo = wl->link_info;  	struct rtw89_btc_wl_active_role_v7 *act_role = NULL; -	u8 i, mode, cnt = 0, cnt_2g = 0, cnt_5g = 0, phy_now = RTW89_PHY_MAX, phy_dbcc; +	u8 i, mode, cnt = 0, cnt_2g = 0, cnt_5g = 0, phy_now = RTW89_PHY_NUM, phy_dbcc;  	bool b2g = false, b5g = false, client_joined = false, client_inc_2g = false;  	u8 client_cnt_last[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER] = {};  	u8 cid_role[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER] = {}; @@ -6400,7 +6749,7 @@ static void _update_wl_info_v7(struct rtw89_dev *rtwdev, u8 rid)  	memset(wl_rinfo, 0, sizeof(*wl_rinfo));  	for (i = 0; i < RTW89_PORT_NUM; i++) { -		if (!wl_linfo[i].active || wl_linfo[i].phy >= RTW89_PHY_MAX) +		if (!wl_linfo[i].active || wl_linfo[i].phy >= RTW89_PHY_NUM)  			continue;  		act_role = &wl_rinfo->active_role[i]; @@ -6491,10 +6840,10 @@ static void _update_wl_info_v7(struct rtw89_dev *rtwdev, u8 rid)  	} else if (cnt > BTC_TDMA_WLROLE_MAX) {  		mode = BTC_WLINK_OTHER;  	} else if (rtwdev->dbcc_en) { -		mode = _chk_dbcc(rtwdev, cid_ch, cid_phy, cid_role, &dbcc_2g_phy); +		mode = _chk_dbcc(rtwdev, cid_ch, cid_phy, cid_role, cnt);  		/* correct 2G-located PHY band for gnt ctrl */ -		if (dbcc_2g_phy < RTW89_PHY_MAX) +		if (dbcc_2g_phy < RTW89_PHY_NUM)  			wl_dinfo->op_band[dbcc_2g_phy] = RTW89_BAND_2G;  	} else if (b2g && b5g && cnt == 2) {  		mode = BTC_WLINK_25G_MCC; @@ -6536,26 +6885,336 @@ static void _update_wl_info_v7(struct rtw89_dev *rtwdev, u8 rid)  	_fw_set_drv_info(rtwdev, CXDRVINFO_ROLE);  } +static u8 _update_wl_link_mode(struct rtw89_dev *rtwdev, u8 hw_band, u8 type) +{ +	struct rtw89_btc_wl_info *wl = &rtwdev->btc.cx.wl; +	struct rtw89_btc_wl_mlo_info *mlo_info = &wl->mlo_info; +	u8 mode = BTC_WLINK_NOLINK; + +	switch (type) { +	case RTW89_MR_WTYPE_NONE: /* no-link */ +		mode = BTC_WLINK_NOLINK; +		break; +	case RTW89_MR_WTYPE_NONMLD:  /* Non_MLO 1-role 2+0/0+2 */ +	case RTW89_MR_WTYPE_MLD1L1R: /* MLO only-1 link 2+0/0+2 */ +		if (mlo_info->hwb_rf_band[hw_band] != RTW89_BAND_2G) { +			mode = BTC_WLINK_5G; +		} else if (mlo_info->wmode[hw_band] == RTW89_MR_WMODE_1AP) { +			mode = BTC_WLINK_2G_GO; +		} else if (mlo_info->wmode[hw_band] == RTW89_MR_WMODE_1CLIENT) { +			if (wl->role_info_v8.p2p_2g) +				mode = BTC_WLINK_2G_GC; +			else +				mode = BTC_WLINK_2G_STA; +		} +		break; +	case RTW89_MR_WTYPE_NONMLD_NONMLD: /* Non_MLO 2-role 2+0/0+2 */ +	case RTW89_MR_WTYPE_MLD1L1R_NONMLD: /* MLO only-1 link + P2P 2+0/0+2 */ +		if (mlo_info->hwb_rf_band[hw_band] != RTW89_BAND_2G) { +			mode = BTC_WLINK_5G; +		} else if (mlo_info->ch_type[hw_band] == RTW89_MR_CTX2_2GHZ_5GHZ || +			   mlo_info->ch_type[hw_band] == RTW89_MR_CTX2_2GHZ_6GHZ) { +			mode = BTC_WLINK_25G_MCC; +		} else if (mlo_info->ch_type[hw_band] == RTW89_MR_CTX2_2GHZ) { +			mode = BTC_WLINK_2G_MCC; +		} else if (mlo_info->ch_type[hw_band] == RTW89_MR_CTX1_2GHZ) { +			mode = BTC_WLINK_2G_SCC; +		} +		break; +	case RTW89_MR_WTYPE_MLD2L1R: /* MLO_MLSR 2+0/0+2 */ +		if (mlo_info->hwb_rf_band[hw_band] != RTW89_BAND_2G) +			mode = BTC_WLINK_5G; +		else if (wl->role_info_v8.p2p_2g) +			mode = BTC_WLINK_2G_GC; +		else +			mode = BTC_WLINK_2G_STA; +		break; +	case RTW89_MR_WTYPE_MLD2L1R_NONMLD: /* MLO_MLSR + P2P 2+0/0+2 */ +	case RTW89_MR_WTYPE_MLD2L2R_NONMLD: /* MLO_MLMR + P2P 1+1/2+2 */ +		/* driver may doze 1-link to +		 * 2G+5G   -> TDMA slot switch by E2G/E5G +		 * 5G only -> TDMA slot switch by E5G +		 */ +		mode = BTC_WLINK_25G_MCC; +		break; +	case RTW89_MR_WTYPE_MLD2L2R: /* MLO_MLMR  1+1/2+2 */ +		if (mlo_info->hwb_rf_band[hw_band] != RTW89_BAND_2G) { +			mode = BTC_WLINK_5G; +		} else if (mlo_info->wmode[hw_band] == RTW89_MR_WMODE_1AP) { +			mode = BTC_WLINK_2G_GO; +		} else if (mlo_info->wmode[hw_band] == RTW89_MR_WMODE_1CLIENT) { +			if (wl->role_info_v8.p2p_2g) +				mode = BTC_WLINK_2G_GC; +			else +				mode = BTC_WLINK_2G_STA; +		} +		break; +	} +	return mode; +} + +static void _update_wl_mlo_info(struct rtw89_dev *rtwdev) +{ +	struct rtw89_btc_wl_info *wl = &rtwdev->btc.cx.wl; +	struct rtw89_btc_wl_role_info_v8 *wl_rinfo = &wl->role_info_v8; +	struct rtw89_btc_wl_mlo_info *mlo_info = &wl->mlo_info; +	struct rtw89_mr_chanctx_info qinfo; +	u8 track_band = RTW89_PHY_0; +	u8 rf_band = RTW89_BAND_2G; +	u8 i, type; + +	/* parse MLO info form PHL API for each HW-band */ +	for (i = RTW89_MAC_0; i <= RTW89_MAC_1; i++) { +		memset(&qinfo, 0, sizeof(qinfo)); + +		rtw89_query_mr_chanctx_info(rtwdev, i, &qinfo); +		mlo_info->wmode[i] = qinfo.wmode; +		mlo_info->ch_type[i] = qinfo.ctxtype; +		mlo_info->wtype = qinfo.wtype; + +		if (mlo_info->ch_type[i] == RTW89_MR_CTX1_5GHZ || +		    mlo_info->ch_type[i] == RTW89_MR_CTX2_5GHZ || +		    mlo_info->ch_type[i] == RTW89_MR_CTX2_5GHZ_6GHZ) +			mlo_info->hwb_rf_band[i] = RTW89_BAND_5G; +		else if (mlo_info->ch_type[i] == RTW89_MR_CTX1_6GHZ || +			 mlo_info->ch_type[i] == RTW89_MR_CTX2_6GHZ) +			mlo_info->hwb_rf_band[i] = RTW89_BAND_6G; +		else /* check if "2G-included" or unknown in each HW-band */ +			mlo_info->hwb_rf_band[i] = RTW89_BAND_2G; +	} + +	mlo_info->link_status = rtwdev->mlo_dbcc_mode; +	type = mlo_info->wtype; + +	if (mlo_info->wtype == RTW89_MR_WTYPE_MLD1L1R || +	    mlo_info->wtype == RTW89_MR_WTYPE_MLD2L1R || +	    mlo_info->wtype == RTW89_MR_WTYPE_MLD2L2R || +	    mlo_info->wtype == RTW89_MR_WTYPE_MLD1L1R_NONMLD || +	    mlo_info->wtype == RTW89_MR_WTYPE_MLD2L1R_NONMLD || +	    mlo_info->wtype == RTW89_MR_WTYPE_MLD2L2R_NONMLD) +		mlo_info->mlo_en = 1; +	else +		mlo_info->mlo_en = 0; + +	if (mlo_info->ch_type[RTW89_MAC_0] != RTW89_MR_CTX_NONE && +	    mlo_info->ch_type[RTW89_MAC_0] != RTW89_MR_CTX_UNKNOWN && +	    mlo_info->ch_type[RTW89_MAC_1] != RTW89_MR_CTX_NONE && +	    mlo_info->ch_type[RTW89_MAC_1] != RTW89_MR_CTX_UNKNOWN) +		mlo_info->dual_hw_band_en = 1; /* two HW-hand link exist */ +	else +		mlo_info->dual_hw_band_en = 0; + +	if (mlo_info->link_status == MLO_2_PLUS_0_2RF || +	    mlo_info->link_status == MLO_0_PLUS_2_2RF || +	    mlo_info->link_status == MLO_2_PLUS_2_2RF) +		mlo_info->mlo_adie = 2; +	else +		mlo_info->mlo_adie = 1; + +	switch (mlo_info->link_status) { +	default: +	case MLO_2_PLUS_0_1RF: /* 2+0 */ +	case MLO_2_PLUS_0_2RF: +		mlo_info->rf_combination = BTC_MLO_RF_2_PLUS_0; +		track_band = RTW89_MAC_0; +		rf_band = mlo_info->hwb_rf_band[RTW89_MAC_0]; +		mlo_info->path_rf_band[BTC_RF_S0] = rf_band; +		mlo_info->path_rf_band[BTC_RF_S1] = rf_band; + +		wl_rinfo->pta_req_band = RTW89_MAC_0; +		wl_rinfo->dbcc_2g_phy = RTW89_PHY_0; +		wl_rinfo->dbcc_en = 0; +		break; +	case MLO_0_PLUS_2_1RF: /* 0+2 */ +	case MLO_0_PLUS_2_2RF: +		mlo_info->rf_combination = BTC_MLO_RF_0_PLUS_2; +		track_band = RTW89_MAC_1; +		rf_band = mlo_info->hwb_rf_band[RTW89_MAC_1]; +		mlo_info->path_rf_band[BTC_RF_S0] = rf_band; +		mlo_info->path_rf_band[BTC_RF_S1] = rf_band; + +		wl_rinfo->pta_req_band = RTW89_MAC_1; +		wl_rinfo->dbcc_2g_phy = RTW89_PHY_1; +		wl_rinfo->dbcc_en = 0; +		break; +	case MLO_1_PLUS_1_1RF: /* 1+1 */ +	case MLO_1_PLUS_1_2RF: /* 1+1 */ +	case MLO_2_PLUS_2_2RF: /* 2+2 */ +	case DBCC_LEGACY: /* DBCC 1+1 */ +		if (mlo_info->link_status == MLO_2_PLUS_2_2RF) +			mlo_info->rf_combination = BTC_MLO_RF_2_PLUS_2; +		else +			mlo_info->rf_combination = BTC_MLO_RF_1_PLUS_1; + +		if (mlo_info->hwb_rf_band[RTW89_MAC_0] == RTW89_BAND_2G) +			track_band = RTW89_MAC_0; +		else +			track_band = RTW89_MAC_1; + +		mlo_info->path_rf_band[BTC_RF_S0] = +					mlo_info->hwb_rf_band[RTW89_MAC_0]; +		mlo_info->path_rf_band[BTC_RF_S1] = +					mlo_info->hwb_rf_band[RTW89_MAC_1]; + +		/* Check ch count from ch_type @ 2.4G HW-band, and modify type */ +		if (mlo_info->ch_type[track_band] == RTW89_MR_CTX1_2GHZ) +			type = RTW89_MR_WTYPE_NONMLD; /* only 1-role at 2G */ +		else +			type = RTW89_MR_WTYPE_NONMLD_NONMLD; + +		if (mlo_info->hwb_rf_band[RTW89_MAC_0] == RTW89_BAND_2G) { +			wl_rinfo->pta_req_band = RTW89_MAC_0; +			wl_rinfo->dbcc_2g_phy = RTW89_PHY_0; +		} else { +			wl_rinfo->pta_req_band = RTW89_MAC_1; +			wl_rinfo->dbcc_2g_phy = RTW89_PHY_1; +		} + +		if (mlo_info->wmode[RTW89_MAC_0] == RTW89_MR_WMODE_NONE && +		    mlo_info->wmode[RTW89_MAC_1] == RTW89_MR_WMODE_NONE) +			wl_rinfo->dbcc_en = 0; +		else +			wl_rinfo->dbcc_en = 1; +		break; +	} + +	wl_rinfo->link_mode = _update_wl_link_mode(rtwdev, track_band, type); + +	rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(), mode=%s, pta_band=%d", +		    __func__, id_to_linkmode(wl_rinfo->link_mode), +		    wl_rinfo->pta_req_band); +} + +static void _update_wl_non_mlo_info(struct rtw89_dev *rtwdev) +{ +	struct rtw89_btc_wl_info *wl = &rtwdev->btc.cx.wl; +	struct rtw89_btc_wl_rlink *rlink = NULL; +	struct rtw89_btc_wl_role_info_v8 *wl_rinfo = &wl->role_info_v8; +	struct rtw89_btc_chdef cid_ch[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER] = {}; +	u8 cid_role[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER] = {}; +	u8 cid_phy[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER] = {}; +	bool b2g = false, b5g = false, outloop = false; +	u8 mode = BTC_WLINK_NOLINK; +	u8 cnt_2g = 0, cnt_5g = 0; +	u8 i, j, cnt = 0; + +	for (j = RTW89_PHY_0; j < RTW89_PHY_NUM; j++) { +		for (i = 0; i < RTW89_BE_BTC_WL_MAX_ROLE_NUMBER; i++) { +			rlink = &wl_rinfo->rlink[i][j]; + +			if (!rlink->active || !rlink->connected) +				continue; + +			if (cnt >= RTW89_BE_BTC_WL_MAX_ROLE_NUMBER) { +				outloop = true; +				break; +			} + +			cid_ch[cnt] = wl->rlink_info[i][j].chdef; +			cid_phy[cnt] = rlink->phy; +			cid_role[cnt] = rlink->role; +			cnt++; + +			if (rlink->rf_band != RTW89_BAND_2G) { +				cnt_5g++; +				b5g = true; +			} else { +				cnt_2g++; +				b2g = true; +			} +		} +		if (outloop) +			break; +	} + +	rtw89_debug(rtwdev, RTW89_DBG_BTC, +		    "[BTC], %s(): cnt_2g=%d, cnt_5g=%d\n", __func__, cnt_2g, cnt_5g); + +	wl_rinfo->dbcc_en = rtwdev->dbcc_en; +	/* Be careful to change the following sequence!! */ +	if (cnt == 0) { +		mode = BTC_WLINK_NOLINK; +	} else if (!b2g && b5g) { +		mode = BTC_WLINK_5G; +	} else if (wl_rinfo->dbcc_en) { +		mode = _chk_dbcc(rtwdev, cid_ch, cid_phy, cid_role, cnt); +	} else if (b2g && b5g) { +		mode = BTC_WLINK_25G_MCC; +	} else if (!b5g && cnt >= 2) { +		if (_chk_role_ch_group(&cid_ch[0], &cid_ch[1])) +			mode = BTC_WLINK_2G_SCC; +		else +			mode = BTC_WLINK_2G_MCC; +	} else if (!b5g) { /* cnt_connect = 1 */ +		mode = _get_role_link_mode(cid_role[0]); +	} + +	wl_rinfo->link_mode = mode; +} + +static void _modify_role_link_mode(struct rtw89_dev *rtwdev) +{ +	struct rtw89_btc_wl_info *wl = &rtwdev->btc.cx.wl; +	struct rtw89_btc_wl_role_info_v8 *wl_rinfo = &wl->role_info_v8; +	u8 go_cleint_exist = wl->go_client_exist; +	u8 link_mode = wl_rinfo->link_mode; +	u32 role_map = wl_rinfo->role_map; +	u8 noa_exist = wl->noa_exist; +	u32 mrole = BTC_WLMROLE_NONE; + +	/* if no client_joined, don't care P2P-GO/AP role */ +	if (((role_map & BIT(RTW89_WIFI_ROLE_P2P_GO)) || +	     (role_map & BIT(RTW89_WIFI_ROLE_AP))) && !go_cleint_exist) { +		if (link_mode == BTC_WLINK_2G_SCC) { +			wl_rinfo->link_mode = BTC_WLINK_2G_STA; +		} else if (link_mode == BTC_WLINK_2G_GO || +			   link_mode == BTC_WLINK_2G_AP) { +			wl_rinfo->link_mode = BTC_WLINK_NOLINK; +		} +	} + +	/* Identify 2-Role type */ +	if  (link_mode == BTC_WLINK_2G_SCC || +	     link_mode == BTC_WLINK_2G_MCC || +	     link_mode == BTC_WLINK_25G_MCC || +	     link_mode == BTC_WLINK_5G) { +		if ((role_map & BIT(RTW89_WIFI_ROLE_P2P_GO)) || +		    (role_map & BIT(RTW89_WIFI_ROLE_AP))) { +			if (noa_exist) +				mrole = BTC_WLMROLE_STA_GO_NOA; +			else +				mrole = BTC_WLMROLE_STA_GO; +		} else if (role_map & BIT(RTW89_WIFI_ROLE_P2P_CLIENT)) { +			if (noa_exist) +				mrole = BTC_WLMROLE_STA_GC_NOA; +			else +				mrole = BTC_WLMROLE_STA_GC; +		} else { +			mrole = BTC_WLMROLE_STA_STA; +		} +	} + +	wl_rinfo->mrole_type = mrole; + +	rtw89_debug(rtwdev, RTW89_DBG_BTC, +		    "[BTC], %s(): link_mode=%s, mrole_type=%d\n", __func__, +		    id_to_linkmode(wl_rinfo->link_mode), wl_rinfo->mrole_type); +} +  static void _update_wl_info_v8(struct rtw89_dev *rtwdev, u8 role_id, u8 rlink_id,  			       enum btc_role_state state)  { +	struct rtw89_btc_wl_rlink *rlink = NULL; +	struct rtw89_btc_wl_link_info *wl_linfo;  	struct rtw89_btc *btc = &rtwdev->btc;  	struct rtw89_btc_wl_info *wl = &btc->cx.wl; -	struct rtw89_btc_chdef cid_ch[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER];  	struct rtw89_btc_wl_role_info_v8 *wl_rinfo = &wl->role_info_v8; -	struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info; -	bool client_joined = false, b2g = false, b5g = false; -	u8 cid_role[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER] = {}; -	u8 cid_phy[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER] = {}; -	u8 dbcc_en = 0, pta_req_band = RTW89_MAC_0; -	u8 i, j, cnt = 0, cnt_2g = 0, cnt_5g = 0; -	struct rtw89_btc_wl_link_info *wl_linfo; -	struct rtw89_btc_wl_rlink *rlink = NULL; -	u8 dbcc_2g_phy = RTW89_PHY_0; -	u8 mode = BTC_WLINK_NOLINK; -	u32 noa_dur = 0; +	bool client_joined = false, noa_exist = false, p2p_exist = false; +	bool is_5g_hi_channel = false, bg_mode = false, dbcc_en_ori; +	u8 i, j, link_mode_ori; +	u32 role_map = 0; -	if (role_id >= RTW89_BE_BTC_WL_MAX_ROLE_NUMBER || rlink_id > RTW89_MAC_1) +	if (role_id >= RTW89_BE_BTC_WL_MAX_ROLE_NUMBER || rlink_id >= RTW89_MAC_NUM)  		return;  	/* Extract wl->link_info[role_id][rlink_id] to wl->role_info @@ -6565,10 +7224,8 @@ static void _update_wl_info_v8(struct rtw89_dev *rtwdev, u8 role_id, u8 rlink_id  	 */  	wl_linfo = &wl->rlink_info[role_id][rlink_id]; -	if (wl_linfo->connected == MLME_LINKING) -		return; -  	rlink = &wl_rinfo->rlink[role_id][rlink_id]; +  	rlink->role = wl_linfo->role;  	rlink->active = wl_linfo->active; /* Doze or not */  	rlink->pid = wl_linfo->pid; @@ -6584,8 +7241,6 @@ static void _update_wl_info_v8(struct rtw89_dev *rtwdev, u8 role_id, u8 rlink_id  	switch (wl_linfo->connected) {  	case MLME_NO_LINK:  		rlink->connected = 0; -		if (rlink->role == RTW89_WIFI_ROLE_STATION) -			btc->dm.leak_ap = 0;  		break;  	case MLME_LINKED:  		rlink->connected = 1; @@ -6594,133 +7249,75 @@ static void _update_wl_info_v8(struct rtw89_dev *rtwdev, u8 role_id, u8 rlink_id  		return;  	} -	wl->is_5g_hi_channel = false; -	wl->bg_mode = false; -	wl_rinfo->role_map = 0; -	wl_rinfo->p2p_2g = 0; -	memset(cid_ch, 0, sizeof(cid_ch)); - -	for (i = 0; i < RTW89_BE_BTC_WL_MAX_ROLE_NUMBER; i++) { -		for (j = RTW89_MAC_0; j <= RTW89_MAC_1; j++) { +	for (j = RTW89_MAC_0; j <= RTW89_MAC_1; j++) { +		for (i = 0; i < RTW89_BE_BTC_WL_MAX_ROLE_NUMBER; i++) {  			rlink = &wl_rinfo->rlink[i][j];  			if (!rlink->active || !rlink->connected)  				continue; -			cnt++; -			wl_rinfo->role_map |= BIT(rlink->role); - -			/* only if client connect for p2p-Go/AP */ -			if ((rlink->role == RTW89_WIFI_ROLE_P2P_GO || -			     rlink->role == RTW89_WIFI_ROLE_AP) && -			     rlink->client_cnt > 1) -				client_joined = true; - -			/* Identufy if P2P-Go (GO/GC/AP) exist at 2G band*/ -			if (rlink->rf_band == RTW89_BAND_2G && -			    (client_joined || rlink->role == RTW89_WIFI_ROLE_P2P_CLIENT)) -				wl_rinfo->p2p_2g = 1; +			role_map |= BIT(rlink->role);  			/* only one noa-role exist */  			if (rlink->noa && rlink->noa_dur > 0) -				noa_dur = rlink->noa_dur; +				noa_exist = true;  			/* for WL 5G-Rx interfered with BT issue */ -			if (rlink->rf_band == RTW89_BAND_5G && rlink->ch >= 100) -				wl->is_5g_hi_channel = 1; +			if (rlink->rf_band == RTW89_BAND_5G) { +				if (rlink->ch >= 100) +					is_5g_hi_channel = true; -			if ((rlink->mode & BIT(BTC_WL_MODE_11B)) || -			    (rlink->mode & BIT(BTC_WL_MODE_11G))) -				wl->bg_mode = 1; - -			if (rtwdev->chip->para_ver & BTC_FEAT_MLO_SUPPORT)  				continue; +			} -			cid_ch[cnt - 1] = wl_linfo->chdef; -			cid_phy[cnt - 1] = rlink->phy; -			cid_role[cnt - 1] = rlink->role; - -			if (rlink->rf_band != RTW89_BAND_2G) { -				cnt_5g++; -				b5g = true; -			} else { -				cnt_2g++; -				b2g = true; +			/* only if client connect for p2p-Go/AP */ +			if ((rlink->role == RTW89_WIFI_ROLE_P2P_GO || +			     rlink->role == RTW89_WIFI_ROLE_AP) && +			     rlink->client_cnt > 1) { +				p2p_exist = true; +				client_joined = true;  			} -		} -	} -	if (rtwdev->chip->para_ver & BTC_FEAT_MLO_SUPPORT) { -		rtw89_debug(rtwdev, RTW89_DBG_BTC, -			    "[BTC] rlink cnt_2g=%d cnt_5g=%d\n", cnt_2g, cnt_5g); -		rtw89_warn(rtwdev, "not support MLO feature yet"); -	} else { -		dbcc_en = rtwdev->dbcc_en; +			/* Identify if P2P-Go (GO/GC/AP) exist at 2G band */ +			if (rlink->role == RTW89_WIFI_ROLE_P2P_CLIENT) +				p2p_exist = true; -		/* Be careful to change the following sequence!! */ -		if (cnt == 0) { -			mode = BTC_WLINK_NOLINK; -		} else if (!b2g && b5g) { -			mode = BTC_WLINK_5G; -		} else if (wl_rinfo->role_map & BIT(RTW89_WIFI_ROLE_NAN)) { -			mode = BTC_WLINK_2G_NAN; -		} else if (cnt > BTC_TDMA_WLROLE_MAX) { -			mode = BTC_WLINK_OTHER; -		} else if (dbcc_en) { -			mode = _chk_dbcc(rtwdev, cid_ch, cid_phy, cid_role, -					 &dbcc_2g_phy); -		} else if (b2g && b5g && cnt == 2) { -			mode = BTC_WLINK_25G_MCC; -		} else if (!b5g && cnt == 2) { /* cnt_connect = 2 */ -			if (_chk_role_ch_group(&cid_ch[0], &cid_ch[cnt - 1])) -				mode = BTC_WLINK_2G_SCC; -			else -				mode = BTC_WLINK_2G_MCC; -		} else if (!b5g && cnt == 1) { /* cnt_connect = 1 */ -			mode = _get_role_link_mode(cid_role[0]); +			if ((rlink->mode & BIT(BTC_WL_MODE_11B)) || +			    (rlink->mode & BIT(BTC_WL_MODE_11G))) +				bg_mode = true;  		}  	} -	wl_rinfo->link_mode = mode; -	wl_rinfo->connect_cnt = cnt; -	if (wl_rinfo->connect_cnt == 0) -		wl_rinfo->role_map = BIT(RTW89_WIFI_ROLE_NONE); -	_update_role_link_mode(rtwdev, client_joined, noa_dur); +	link_mode_ori = wl_rinfo->link_mode; +	wl->is_5g_hi_channel = is_5g_hi_channel; +	wl->bg_mode = bg_mode; +	wl->go_client_exist = client_joined; +	wl->noa_exist = noa_exist; +	wl_rinfo->p2p_2g = p2p_exist; +	wl_rinfo->role_map = role_map; -	wl_rinfo->dbcc_2g_phy = dbcc_2g_phy; -	if (wl_rinfo->dbcc_en != dbcc_en) { -		wl_rinfo->dbcc_en = dbcc_en; -		wl_rinfo->dbcc_chg = 1; -		btc->cx.cnt_wl[BTC_WCNT_DBCC_CHG]++; +	dbcc_en_ori = wl_rinfo->dbcc_en; + +	if (rtwdev->chip->para_ver & BTC_FEAT_MLO_SUPPORT) { +		/* for MLO-supported, link-mode from driver directly */ +		_update_wl_mlo_info(rtwdev);  	} else { -		wl_rinfo->dbcc_chg = 0; +		/* for non-MLO-supported, link-mode by BTC */ +		_update_wl_non_mlo_info(rtwdev);  	} -	if (wl_rinfo->dbcc_en) { -		memset(wl_dinfo, 0, sizeof(struct rtw89_btc_wl_dbcc_info)); +	_modify_role_link_mode(rtwdev); -		if (mode == BTC_WLINK_5G) { -			pta_req_band = RTW89_PHY_0; -			wl_dinfo->op_band[RTW89_PHY_0] = RTW89_BAND_5G; -			wl_dinfo->op_band[RTW89_PHY_1] = RTW89_BAND_2G; -		} else if (wl_rinfo->dbcc_2g_phy == RTW89_PHY_1) { -			pta_req_band = RTW89_PHY_1; -			wl_dinfo->op_band[RTW89_PHY_0] = RTW89_BAND_5G; -			wl_dinfo->op_band[RTW89_PHY_1] = RTW89_BAND_2G; -		} else { -			pta_req_band = RTW89_PHY_0; -			wl_dinfo->op_band[RTW89_PHY_0] = RTW89_BAND_2G; -			wl_dinfo->op_band[RTW89_PHY_1] = RTW89_BAND_5G; -		} -		_update_dbcc_band(rtwdev, RTW89_PHY_0); -		_update_dbcc_band(rtwdev, RTW89_PHY_1); -	} +	if (link_mode_ori != wl_rinfo->link_mode) +		wl->link_mode_chg = true; -	wl_rinfo->pta_req_band = pta_req_band; -	_fw_set_drv_info(rtwdev, CXDRVINFO_ROLE); +	if (wl_rinfo->dbcc_en != dbcc_en_ori) { +		wl->dbcc_chg = true; +		btc->cx.cnt_wl[BTC_WCNT_DBCC_CHG]++; +	}  } -void rtw89_coex_act1_work(struct work_struct *work) +void rtw89_coex_act1_work(struct wiphy *wiphy, struct wiphy_work *work)  {  	struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,  						coex_act1_work.work); @@ -6729,7 +7326,8 @@ void rtw89_coex_act1_work(struct work_struct *work)  	struct rtw89_btc_cx *cx = &btc->cx;  	struct rtw89_btc_wl_info *wl = &cx->wl; -	mutex_lock(&rtwdev->mutex); +	lockdep_assert_wiphy(wiphy); +  	rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): enter\n", __func__);  	dm->cnt_notify[BTC_NCNT_TIMER]++;  	if (wl->status.map._4way) @@ -6738,10 +7336,9 @@ void rtw89_coex_act1_work(struct work_struct *work)  		wl->status.map.connecting = false;  	_run_coex(rtwdev, BTC_RSN_ACT1_WORK); -	mutex_unlock(&rtwdev->mutex);  } -void rtw89_coex_bt_devinfo_work(struct work_struct *work) +void rtw89_coex_bt_devinfo_work(struct wiphy *wiphy, struct wiphy_work *work)  {  	struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,  						coex_bt_devinfo_work.work); @@ -6749,15 +7346,15 @@ void rtw89_coex_bt_devinfo_work(struct work_struct *work)  	struct rtw89_btc_dm *dm = &rtwdev->btc.dm;  	struct rtw89_btc_bt_a2dp_desc *a2dp = &btc->cx.bt.link_info.a2dp_desc; -	mutex_lock(&rtwdev->mutex); +	lockdep_assert_wiphy(wiphy); +  	rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): enter\n", __func__);  	dm->cnt_notify[BTC_NCNT_TIMER]++;  	a2dp->play_latency = 0;  	_run_coex(rtwdev, BTC_RSN_BT_DEVINFO_WORK); -	mutex_unlock(&rtwdev->mutex);  } -void rtw89_coex_rfk_chk_work(struct work_struct *work) +void rtw89_coex_rfk_chk_work(struct wiphy *wiphy, struct wiphy_work *work)  {  	struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,  						coex_rfk_chk_work.work); @@ -6766,7 +7363,8 @@ void rtw89_coex_rfk_chk_work(struct work_struct *work)  	struct rtw89_btc_cx *cx = &btc->cx;  	struct rtw89_btc_wl_info *wl = &cx->wl; -	mutex_lock(&rtwdev->mutex); +	lockdep_assert_wiphy(wiphy); +  	rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): enter\n", __func__);  	dm->cnt_notify[BTC_NCNT_TIMER]++;  	if (wl->rfk_info.state != BTC_WRFK_STOP) { @@ -6778,7 +7376,6 @@ void rtw89_coex_rfk_chk_work(struct work_struct *work)  		_write_scbd(rtwdev, BTC_WSCB_WLRFK, false);  		_run_coex(rtwdev, BTC_RSN_RFK_CHK_WORK);  	} -	mutex_unlock(&rtwdev->mutex);  }  static void _update_bt_scbd(struct rtw89_dev *rtwdev, bool only_update) @@ -6840,12 +7437,33 @@ static void _update_bt_scbd(struct rtw89_dev *rtwdev, bool only_update)  	bt->rfk_info.map.req = !!(val & BTC_BSCB_RFK_REQ);  	bt->hi_lna_rx = !!(val & BTC_BSCB_BT_HILNA);  	bt->link_info.status.map.connect = !!(val & BTC_BSCB_BT_CONNECT); +	if (bt->run_patch_code != !!(val & BTC_BSCB_PATCH_CODE)) +		status_change = true;  	bt->run_patch_code = !!(val & BTC_BSCB_PATCH_CODE);  	if (!only_update && status_change)  		_run_coex(rtwdev, BTC_RSN_UPDATE_BT_SCBD);  } +#define BTC_BTINFO_PWR_LEN 5 +static void _update_bt_txpwr_info(struct rtw89_dev *rtwdev, u8 *buf, u32 len) +{ +	struct rtw89_btc_bt_info *bt = &rtwdev->btc.cx.bt; +	struct rtw89_btc_bt_link_info *b = &bt->link_info; + +	if (len != BTC_BTINFO_PWR_LEN) +		return; + +	if (!memcmp(bt->txpwr_info, buf, sizeof(bt->txpwr_info))) { +		rtw89_debug(rtwdev, RTW89_DBG_BTC, +			    "[BTC], %s return by info duplicate!\n", __func__); +		return; +	} + +	memcpy(bt->txpwr_info, buf, BTC_BTINFO_MAX); +	memcpy(&b->bt_txpwr_desc, &buf[2], sizeof(b->bt_txpwr_desc)); +} +  static bool _chk_wl_rfk_request(struct rtw89_dev *rtwdev)  {  	struct rtw89_btc *btc = &rtwdev->btc; @@ -6882,7 +7500,7 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason)  	struct rtw89_btc_wl_role_info_v8 *wl_rinfo_v8 = &wl->role_info_v8;  	u8 mode, igno_bt, always_freerun; -	lockdep_assert_held(&rtwdev->mutex); +	lockdep_assert_wiphy(rtwdev->hw->wiphy);  	dm->run_reason = reason;  	_update_dm_step(rtwdev, reason); @@ -7002,7 +7620,7 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason)  		goto exit;  	} -	if (wl->status.val & btc_scanning_map.val) { +	if (wl->status.val & btc_scanning_map.val && !wl->rfk_info.con_rfk) {  		_action_wl_scan(rtwdev);  		bt->scan_rx_low_pri = true;  		goto exit; @@ -7187,7 +7805,7 @@ void rtw89_btc_ntfy_scan_start(struct rtw89_dev *rtwdev, u8 phy_idx, u8 band)  		    "[BTC], %s(): phy_idx=%d, band=%d\n",  		    __func__, phy_idx, band); -	if (phy_idx >= RTW89_PHY_MAX) +	if (phy_idx >= RTW89_PHY_NUM)  		return;  	btc->dm.cnt_notify[BTC_NCNT_SCAN_START]++; @@ -7223,6 +7841,8 @@ void rtw89_btc_ntfy_scan_finish(struct rtw89_dev *rtwdev, u8 phy_idx)  		_fw_set_drv_info(rtwdev, CXDRVINFO_DBCC);  	} +	btc->dm.tdma_instant_excute = 1; +  	_run_coex(rtwdev, BTC_RSN_NTFY_SCAN_FINISH);  } @@ -7235,7 +7855,7 @@ void rtw89_btc_ntfy_switch_band(struct rtw89_dev *rtwdev, u8 phy_idx, u8 band)  		    "[BTC], %s(): phy_idx=%d, band=%d\n",  		    __func__, phy_idx, band); -	if (phy_idx >= RTW89_PHY_MAX) +	if (phy_idx >= RTW89_PHY_NUM)  		return;  	btc->dm.cnt_notify[BTC_NCNT_SWITCH_BAND]++; @@ -7284,7 +7904,7 @@ void rtw89_btc_ntfy_specific_packet(struct rtw89_dev *rtwdev,  			    "[BTC], %s(): EAPOL_End cnt=%d\n",  			    __func__, cnt);  		wl->status.map._4way = false; -		cancel_delayed_work(&rtwdev->coex_act1_work); +		wiphy_delayed_work_cancel(rtwdev->hw->wiphy, &rtwdev->coex_act1_work);  		break;  	case PACKET_ARP:  		cnt = ++cx->cnt_wl[BTC_WCNT_ARP]; @@ -7303,56 +7923,56 @@ void rtw89_btc_ntfy_specific_packet(struct rtw89_dev *rtwdev,  	}  	if (delay_work) { -		cancel_delayed_work(&rtwdev->coex_act1_work); -		ieee80211_queue_delayed_work(rtwdev->hw, -					     &rtwdev->coex_act1_work, delay); +		wiphy_delayed_work_cancel(rtwdev->hw->wiphy, &rtwdev->coex_act1_work); +		wiphy_delayed_work_queue(rtwdev->hw->wiphy, +					 &rtwdev->coex_act1_work, delay);  	}  	btc->dm.cnt_notify[BTC_NCNT_SPECIAL_PACKET]++;  	_run_coex(rtwdev, BTC_RSN_NTFY_SPECIFIC_PACKET);  } -void rtw89_btc_ntfy_eapol_packet_work(struct work_struct *work) +void rtw89_btc_ntfy_eapol_packet_work(struct wiphy *wiphy, struct wiphy_work *work)  {  	struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,  						btc.eapol_notify_work); -	mutex_lock(&rtwdev->mutex); +	lockdep_assert_wiphy(wiphy); +  	rtw89_leave_ps_mode(rtwdev);  	rtw89_btc_ntfy_specific_packet(rtwdev, PACKET_EAPOL); -	mutex_unlock(&rtwdev->mutex);  } -void rtw89_btc_ntfy_arp_packet_work(struct work_struct *work) +void rtw89_btc_ntfy_arp_packet_work(struct wiphy *wiphy, struct wiphy_work *work)  {  	struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,  						btc.arp_notify_work); -	mutex_lock(&rtwdev->mutex); +	lockdep_assert_wiphy(wiphy); +  	rtw89_btc_ntfy_specific_packet(rtwdev, PACKET_ARP); -	mutex_unlock(&rtwdev->mutex);  } -void rtw89_btc_ntfy_dhcp_packet_work(struct work_struct *work) +void rtw89_btc_ntfy_dhcp_packet_work(struct wiphy *wiphy, struct wiphy_work *work)  {  	struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,  						btc.dhcp_notify_work); -	mutex_lock(&rtwdev->mutex); +	lockdep_assert_wiphy(wiphy); +  	rtw89_leave_ps_mode(rtwdev);  	rtw89_btc_ntfy_specific_packet(rtwdev, PACKET_DHCP); -	mutex_unlock(&rtwdev->mutex);  } -void rtw89_btc_ntfy_icmp_packet_work(struct work_struct *work) +void rtw89_btc_ntfy_icmp_packet_work(struct wiphy *wiphy, struct wiphy_work *work)  {  	struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,  						btc.icmp_notify_work); -	mutex_lock(&rtwdev->mutex); +	lockdep_assert_wiphy(wiphy); +  	rtw89_leave_ps_mode(rtwdev);  	rtw89_btc_ntfy_specific_packet(rtwdev, PACKET_ICMP); -	mutex_unlock(&rtwdev->mutex);  }  static u8 _update_bt_rssi_level(struct rtw89_dev *rtwdev, u8 rssi) @@ -7531,9 +8151,9 @@ static void _update_bt_info(struct rtw89_dev *rtwdev, u8 *buf, u32 len)  		a2dp->vendor_id = 0;  		a2dp->flush_time = 0;  		a2dp->play_latency = 1; -		ieee80211_queue_delayed_work(rtwdev->hw, -					     &rtwdev->coex_bt_devinfo_work, -					     RTW89_COEX_BT_DEVINFO_WORK_PERIOD); +		wiphy_delayed_work_queue(rtwdev->hw->wiphy, +					 &rtwdev->coex_bt_devinfo_work, +					 RTW89_COEX_BT_DEVINFO_WORK_PERIOD);  	}  	_run_coex(rtwdev, BTC_RSN_UPDATE_BT_INFO); @@ -7627,7 +8247,6 @@ void rtw89_btc_ntfy_role_info(struct rtw89_dev *rtwdev,  	wlinfo = &wl->link_info[r.pid]; -	rlink_id = 0; /* to do */  	if (ver->fwlrole == 0) {  		*wlinfo = r;  		_update_wl_info(rtwdev); @@ -7641,6 +8260,7 @@ void rtw89_btc_ntfy_role_info(struct rtw89_dev *rtwdev,  		*wlinfo = r;  		_update_wl_info_v7(rtwdev, r.pid);  	} else if (ver->fwlrole == 8) { +		rlink_id = rtwvif_link->mac_idx;  		wlinfo = &wl->rlink_info[r.pid][rlink_id];  		*wlinfo = r;  		link_mode_ori = wl->role_info_v8.link_mode; @@ -7671,7 +8291,8 @@ void rtw89_btc_ntfy_role_info(struct rtw89_dev *rtwdev,  	else  		wl->status.map.connecting = 0; -	if (state == BTC_ROLE_MSTS_STA_DIS_CONN) +	if (state == BTC_ROLE_MSTS_STA_DIS_CONN || +	    state == BTC_ROLE_MSTS_STA_CONN_END)  		wl->status.map._4way = false;  	_run_coex(rtwdev, BTC_RSN_NTFY_ROLE_INFO); @@ -7781,7 +8402,7 @@ static bool _ntfy_wl_rfk(struct rtw89_dev *rtwdev, u8 phy_path,  		wl->rfk_info.state = BTC_WRFK_STOP;  		_write_scbd(rtwdev, BTC_WSCB_WLRFK, false); -		cancel_delayed_work(&rtwdev->coex_rfk_chk_work); +		wiphy_delayed_work_cancel(rtwdev->hw->wiphy, &rtwdev->coex_rfk_chk_work);  		break;  	default:  		rtw89_debug(rtwdev, RTW89_DBG_BTC, @@ -7795,9 +8416,9 @@ static bool _ntfy_wl_rfk(struct rtw89_dev *rtwdev, u8 phy_path,  			_run_coex(rtwdev, BTC_RSN_NTFY_WL_RFK);  		if (wl->rfk_info.state == BTC_WRFK_START) -			ieee80211_queue_delayed_work(rtwdev->hw, -						     &rtwdev->coex_rfk_chk_work, -						     RTW89_COEX_RFK_CHK_WORK_PERIOD); +			wiphy_delayed_work_queue(rtwdev->hw->wiphy, +						 &rtwdev->coex_rfk_chk_work, +						 RTW89_COEX_RFK_CHK_WORK_PERIOD);  	}  	rtw89_debug(rtwdev, RTW89_DBG_BTC, @@ -7815,6 +8436,8 @@ void rtw89_btc_ntfy_wl_rfk(struct rtw89_dev *rtwdev, u8 phy_map,  	bool allow;  	int ret; +	lockdep_assert_wiphy(rtwdev->hw->wiphy); +  	band = FIELD_GET(BTC_RFK_BAND_MAP, phy_map);  	rtw89_debug(rtwdev, RTW89_DBG_RFK, @@ -7882,7 +8505,11 @@ void __rtw89_btc_ntfy_wl_sta_iter(struct rtw89_vif_link *rtwvif_link,  	rssi = ewma_rssi_read(&rtwsta_link->avg_rssi) >> RSSI_FACTOR;  	rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], rssi=%d\n", rssi); -	link_info = &wl->link_info[port]; +	if (btc->ver->fwlrole != 8) +		link_info = &wl->link_info[port]; +	else +		link_info = &wl->rlink_info[port][rtwvif_link->mac_idx]; +  	link_info->stat.traffic = *stats;  	link_info_t = &link_info->stat.traffic; @@ -7963,13 +8590,12 @@ void __rtw89_btc_ntfy_wl_sta_iter(struct rtw89_vif_link *rtwvif_link,  		r1->active_role_v1[port].rx_lvl = stats->rx_tfc_lv;  		r1->active_role_v1[port].tx_rate = rtwsta_link->ra_report.hw_rate;  		r1->active_role_v1[port].rx_rate = rtwsta_link->rx_hw_rate; -	} else if (ver->fwlrole == 2) { -		dm->trx_info.tx_lvl = stats->tx_tfc_lv; -		dm->trx_info.rx_lvl = stats->rx_tfc_lv; -		dm->trx_info.tx_rate = rtwsta_link->ra_report.hw_rate; -		dm->trx_info.rx_rate = rtwsta_link->rx_hw_rate;  	} +	dm->trx_info.tx_lvl = stats->tx_tfc_lv; +	dm->trx_info.rx_lvl = stats->rx_tfc_lv; +	dm->trx_info.tx_rate = rtwsta_link->ra_report.hw_rate; +	dm->trx_info.rx_rate = rtwsta_link->rx_hw_rate;  	dm->trx_info.tx_tp = link_info_t->tx_throughput;  	dm->trx_info.rx_tp = link_info_t->rx_throughput; @@ -8076,6 +8702,8 @@ static u8 rtw89_btc_c2h_get_index_by_ver(struct rtw89_dev *rtwdev, u8 func)  			return BTF_EVNT_BUF_OVERFLOW;  		else if (ver->fwc2hfunc == 2)  			return func; +		else if (ver->fwc2hfunc == 3) +			return BTF_EVNT_BUF_OVERFLOW;  		else  			return BTF_EVNT_MAX;  	case BTF_EVNT_BUF_OVERFLOW: @@ -8085,11 +8713,20 @@ static u8 rtw89_btc_c2h_get_index_by_ver(struct rtw89_dev *rtwdev, u8 func)  			return BTF_EVNT_C2H_LOOPBACK;  		else if (ver->fwc2hfunc == 2)  			return func; +		else if (ver->fwc2hfunc == 3) +			return BTF_EVNT_C2H_LOOPBACK;  		else  			return BTF_EVNT_MAX;  	case BTF_EVNT_C2H_LOOPBACK:  		if (ver->fwc2hfunc == 2)  			return func; +		else if (ver->fwc2hfunc == 3) +			return BTF_EVNT_BT_LEAUDIO_INFO; +		else +			return BTF_EVNT_MAX; +	case BTF_EVNT_BT_QUERY_TXPWR: +		if (ver->fwc2hfunc == 3) +			return func;  		else  			return BTF_EVNT_MAX;  	case BTF_EVNT_MAX: @@ -8115,6 +8752,7 @@ void rtw89_btc_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb,  		return;  	func = rtw89_btc_c2h_get_index_by_ver(rtwdev, func); +	pfwinfo->cnt_c2h++;  	switch (func) {  	case BTF_EVNT_BUF_OVERFLOW: @@ -8151,12 +8789,15 @@ void rtw89_btc_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb,  	case BTF_EVNT_CX_RUNINFO:  		btc->dm.cnt_dm[BTC_DCNT_CX_RUNINFO]++;  		break; +	case BTF_EVNT_BT_QUERY_TXPWR: +		btc->cx.cnt_bt[BTC_BCNT_BTTXPWR_UPDATE]++; +		_update_bt_txpwr_info(rtwdev, buf, len);  	}  }  #define BTC_CX_FW_OFFLOAD 0 -static void _show_cx_info(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_cx_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)  {  	union rtw89_btc_module_info *md = &rtwdev->btc.mdinfo;  	const struct rtw89_chip_info *chip = rtwdev->chip; @@ -8168,40 +8809,41 @@ static void _show_cx_info(struct rtw89_dev *rtwdev, struct seq_file *m)  	struct rtw89_btc_wl_info *wl = &btc->cx.wl;  	u32 ver_main = 0, ver_sub = 0, ver_hotfix = 0, id_branch = 0;  	u8 cv, rfe, iso, ant_num, ant_single_pos; +	char *p = buf, *end = buf + bufsz;  	if (!(dm->coex_info_map & BTC_COEX_INFO_CX)) -		return; - -	dm->cnt_notify[BTC_NCNT_SHOW_COEX_INFO]++; +		return 0; -	seq_printf(m, "========== [BTC COEX INFO (%d)] ==========\n", -		   chip->chip_id); +	p += scnprintf(p, end - p, +		       "\n========== [BTC COEX INFO (%s)] ==========\n", +		       chip_id_str(chip->chip_id));  	ver_main = FIELD_GET(GENMASK(31, 24), RTW89_COEX_VERSION);  	ver_sub = FIELD_GET(GENMASK(23, 16), RTW89_COEX_VERSION);  	ver_hotfix = FIELD_GET(GENMASK(15, 8), RTW89_COEX_VERSION);  	id_branch = FIELD_GET(GENMASK(7, 0), RTW89_COEX_VERSION); -	seq_printf(m, " %-15s : Coex:%d.%d.%d(branch:%d), ", -		   "[coex_version]", ver_main, ver_sub, ver_hotfix, id_branch); +	p += scnprintf(p, end - p, " %-15s : Coex:%d.%d.%d(branch:%d), ", +		       "[coex_version]", ver_main, ver_sub, ver_hotfix, +		       id_branch);  	ver_main = FIELD_GET(GENMASK(31, 24), wl->ver_info.fw_coex);  	ver_sub = FIELD_GET(GENMASK(23, 16), wl->ver_info.fw_coex);  	ver_hotfix = FIELD_GET(GENMASK(15, 8), wl->ver_info.fw_coex);  	id_branch = FIELD_GET(GENMASK(7, 0), wl->ver_info.fw_coex); -	seq_printf(m, "WL_FW_coex:%d.%d.%d(branch:%d)", -		   ver_main, ver_sub, ver_hotfix, id_branch); +	p += scnprintf(p, end - p, "WL_FW_coex:%d.%d.%d(branch:%d)", +		       ver_main, ver_sub, ver_hotfix, id_branch);  	ver_main = FIELD_GET(GENMASK(31, 24), chip->wlcx_desired);  	ver_sub = FIELD_GET(GENMASK(23, 16), chip->wlcx_desired);  	ver_hotfix = FIELD_GET(GENMASK(15, 8), chip->wlcx_desired); -	seq_printf(m, "(%s, desired:%d.%d.%d), ", -		   (wl->ver_info.fw_coex >= chip->wlcx_desired ? -		   "Match" : "Mismatch"), ver_main, ver_sub, ver_hotfix); +	p += scnprintf(p, end - p, "(%s, desired:%d.%d.%d), ", +		       (wl->ver_info.fw_coex >= chip->wlcx_desired ? +			"Match" : "Mismatch"), ver_main, ver_sub, ver_hotfix); -	seq_printf(m, "BT_FW_coex:%d(%s, desired:%d)\n", -		   bt->ver_info.fw_coex, -		   (bt->ver_info.fw_coex >= chip->btcx_desired ? -		   "Match" : "Mismatch"), chip->btcx_desired); +	p += scnprintf(p, end - p, "BT_FW_coex:%d(%s, desired:%d)\n", +		       bt->ver_info.fw_coex, +		       (bt->ver_info.fw_coex >= ver->bt_desired ? +			"Match" : "Mismatch"), ver->bt_desired);  	if (bt->enable.now && bt->ver_info.fw == 0)  		rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_VER_INFO, true); @@ -8212,10 +8854,11 @@ static void _show_cx_info(struct rtw89_dev *rtwdev, struct seq_file *m)  	ver_sub = FIELD_GET(GENMASK(23, 16), wl->ver_info.fw);  	ver_hotfix = FIELD_GET(GENMASK(15, 8), wl->ver_info.fw);  	id_branch = FIELD_GET(GENMASK(7, 0), wl->ver_info.fw); -	seq_printf(m, " %-15s : WL_FW:%d.%d.%d.%d, BT_FW:0x%x(%s)\n", -		   "[sub_module]", -		   ver_main, ver_sub, ver_hotfix, id_branch, -		   bt->ver_info.fw, bt->run_patch_code ? "patch" : "ROM"); +	p += scnprintf(p, end - p, +		       " %-15s : WL_FW:%d.%d.%d.%d, BT_FW:0x%x(%s)\n", +		       "[sub_module]", +		       ver_main, ver_sub, ver_hotfix, id_branch, +		       bt->ver_info.fw, bt->run_patch_code ? "patch" : "ROM");  	if (ver->fcxinit == 7) {  		cv = md->md_v7.kt_ver; @@ -8231,79 +8874,74 @@ static void _show_cx_info(struct rtw89_dev *rtwdev, struct seq_file *m)  		ant_single_pos = md->md.ant.single_pos;  	} -	seq_printf(m, " %-15s : cv:%x, rfe_type:0x%x, ant_iso:%d, ant_pg:%d, %s", -		   "[hw_info]", cv, rfe, iso, ant_num, -		   ant_num > 1 ? "" : -		   ant_single_pos ? "1Ant_Pos:S1, " : "1Ant_Pos:S0, "); +	p += scnprintf(p, end - p, +		       " %-15s : cv:%x, rfe_type:0x%x, ant_iso:%d, ant_pg:%d, %s", +		       "[hw_info]", cv, rfe, iso, ant_num, +		       ant_num > 1 ? "" : +		       ant_single_pos ? "1Ant_Pos:S1, " : "1Ant_Pos:S0, "); + +	p += scnprintf(p, end - p, +		       "3rd_coex:%d, dbcc:%d, tx_num:%d, rx_num:%d\n", +		       btc->cx.other.type, rtwdev->dbcc_en, hal->tx_nss, +		       hal->rx_nss); -	seq_printf(m, "3rd_coex:%d, dbcc:%d, tx_num:%d, rx_num:%d\n", -		   btc->cx.other.type, rtwdev->dbcc_en, hal->tx_nss, -		   hal->rx_nss); +	return p - buf;  } -static void _show_wl_role_info(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_wl_role_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)  {  	struct rtw89_btc *btc = &rtwdev->btc;  	struct rtw89_btc_wl_link_info *plink = NULL; -	struct rtw89_btc_wl_info *wl = &btc->cx.wl; -	struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info;  	struct rtw89_traffic_stats *t; -	u8 i; +	char *p = buf, *end = buf + bufsz; +	u8 i, j; -	if (rtwdev->dbcc_en) { -		seq_printf(m, -			   " %-15s : PHY0_band(op:%d/scan:%d/real:%d), ", -			   "[dbcc_info]", wl_dinfo->op_band[RTW89_PHY_0], -			   wl_dinfo->scan_band[RTW89_PHY_0], -			   wl_dinfo->real_band[RTW89_PHY_0]); -		seq_printf(m, -			   "PHY1_band(op:%d/scan:%d/real:%d)\n", -			   wl_dinfo->op_band[RTW89_PHY_1], -			   wl_dinfo->scan_band[RTW89_PHY_1], -			   wl_dinfo->real_band[RTW89_PHY_1]); -	} - -	for (i = 0; i < RTW89_PORT_NUM; i++) { -		if (btc->ver->fwlrole == 8) -			plink = &btc->cx.wl.rlink_info[i][0]; -		else -			plink = &btc->cx.wl.link_info[i]; +	for (i = 0; i < btc->ver->max_role_num; i++) { +		for (j = 0; j < RTW89_MAC_NUM; j++) { +			if (btc->ver->fwlrole == 8) +				plink = &btc->cx.wl.rlink_info[i][j]; +			else +				plink = &btc->cx.wl.link_info[i]; -		if (!plink->active) -			continue; +			if (!plink->active) +				continue; -		seq_printf(m, -			   " [port_%d]        : role=%d(phy-%d), connect=%d(client_cnt=%d), mode=%d, center_ch=%d, bw=%d", -			   plink->pid, (u32)plink->role, plink->phy, -			   (u32)plink->connected, plink->client_cnt - 1, -			   (u32)plink->mode, plink->ch, (u32)plink->bw); +			p += scnprintf(p, end - p, +				       " [port_%d]        : role=%d(phy-%d), connect=%s(client_cnt=%d), mode=%d, center_ch=%d, bw=%d", +				       plink->pid, plink->role, plink->phy, +				       id_to_mlme_state(plink->connected), +				       plink->client_cnt - 1, plink->mode, +				       plink->ch, plink->bw); -		if (plink->connected == MLME_NO_LINK) -			continue; +			if (plink->connected == MLME_NO_LINK) +				continue; -		seq_printf(m, -			   ", mac_id=%d, max_tx_time=%dus, max_tx_retry=%d\n", -			   plink->mac_id, plink->tx_time, plink->tx_retry); +			p += scnprintf(p, end - p, +				       ", mac_id=%d, max_tx_time=%dus, max_tx_retry=%d\n", +				       plink->mac_id, plink->tx_time, plink->tx_retry); -		seq_printf(m, -			   " [port_%d]        : rssi=-%ddBm(%d), busy=%d, dir=%s, ", -			   plink->pid, 110 - plink->stat.rssi, -			   plink->stat.rssi, plink->busy, -			   plink->dir == RTW89_TFC_UL ? "UL" : "DL"); +			p += scnprintf(p, end - p, +				       " [port_%d]        : rssi=-%ddBm(%d), busy=%d, dir=%s, ", +				       plink->pid, 110 - plink->stat.rssi, +				       plink->stat.rssi, plink->busy, +				       plink->dir == RTW89_TFC_UL ? "UL" : "DL"); -		t = &plink->stat.traffic; +			t = &plink->stat.traffic; -		seq_printf(m, -			   "tx[rate:%d/busy_level:%d], ", -			   (u32)t->tx_rate, t->tx_tfc_lv); +			p += scnprintf(p, end - p, +				       "tx[rate:%d/busy_level:%d], ", +				       t->tx_rate, t->tx_tfc_lv); -		seq_printf(m, "rx[rate:%d/busy_level:%d/drop:%d]\n", -			   (u32)t->rx_rate, -			   t->rx_tfc_lv, plink->rx_rate_drop_cnt); +			p += scnprintf(p, end - p, +				       "rx[rate:%d/busy_level:%d/drop:%d]\n", +				       t->rx_rate, +				       t->rx_tfc_lv, plink->rx_rate_drop_cnt); +		}  	} +	return p - buf;  } -static void _show_wl_info(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_wl_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)  {  	struct rtw89_btc *btc = &rtwdev->btc;  	const struct rtw89_btc_ver *ver = btc->ver; @@ -8314,12 +8952,13 @@ static void _show_wl_info(struct rtw89_dev *rtwdev, struct seq_file *m)  	struct rtw89_btc_wl_role_info_v2 *wl_rinfo_v2 = &wl->role_info_v2;  	struct rtw89_btc_wl_role_info_v7 *wl_rinfo_v7 = &wl->role_info_v7;  	struct rtw89_btc_wl_role_info_v8 *wl_rinfo_v8 = &wl->role_info_v8; +	char *p = buf, *end = buf + bufsz;  	u8 mode;  	if (!(btc->dm.coex_info_map & BTC_COEX_INFO_WL)) -		return; +		return 0; -	seq_puts(m, "========== [WL Status] ==========\n"); +	p += scnprintf(p, end - p, "========== [WL Status] ==========\n");  	if (ver->fwlrole == 0)  		mode = wl_rinfo->link_mode; @@ -8332,24 +8971,28 @@ static void _show_wl_info(struct rtw89_dev *rtwdev, struct seq_file *m)  	else if (ver->fwlrole == 8)  		mode = wl_rinfo_v8->link_mode;  	else -		return; +		goto out; + +	p += scnprintf(p, end - p, " %-15s : link_mode:%s, ", "[status]", +		       id_to_linkmode(mode)); -	seq_printf(m, " %-15s : link_mode:%d, ", "[status]", mode); +	p += scnprintf(p, end - p, +		       "rf_off:%d, power_save:%d, scan:%s(band:%d/phy_map:0x%x), ", +		       wl->status.map.rf_off, wl->status.map.lps, +		       wl->status.map.scan ? "Y" : "N", +		       wl->scan_info.band[RTW89_PHY_0], wl->scan_info.phy_map); -	seq_printf(m, -		   "rf_off:%d, power_save:%d, scan:%s(band:%d/phy_map:0x%x), ", -		   wl->status.map.rf_off, wl->status.map.lps, -		   wl->status.map.scan ? "Y" : "N", -		   wl->scan_info.band[RTW89_PHY_0], wl->scan_info.phy_map); +	p += scnprintf(p, end - p, +		       "connecting:%s, roam:%s, 4way:%s, init_ok:%s\n", +		       wl->status.map.connecting ? "Y" : "N", +		       wl->status.map.roaming ?  "Y" : "N", +		       wl->status.map._4way ? "Y" : "N", +		       wl->status.map.init_ok ? "Y" : "N"); -	seq_printf(m, -		   "connecting:%s, roam:%s, 4way:%s, init_ok:%s\n", -		   wl->status.map.connecting ? "Y" : "N", -		   wl->status.map.roaming ?  "Y" : "N", -		   wl->status.map._4way ? "Y" : "N", -		   wl->status.map.init_ok ? "Y" : "N"); +	p += _show_wl_role_info(rtwdev, p, end - p); -	_show_wl_role_info(rtwdev, m); +out: +	return p - buf;  }  enum btc_bt_a2dp_type { @@ -8358,7 +9001,7 @@ enum btc_bt_a2dp_type {  	BTC_A2DP_TWS_RELAY = 2,  }; -static void _show_bt_profile_info(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_bt_profile_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)  {  	struct rtw89_btc *btc = &rtwdev->btc;  	struct rtw89_btc_bt_link_info *bt_linfo = &btc->cx.bt.link_info; @@ -8366,182 +9009,219 @@ static void _show_bt_profile_info(struct rtw89_dev *rtwdev, struct seq_file *m)  	struct rtw89_btc_bt_hid_desc hid = bt_linfo->hid_desc;  	struct rtw89_btc_bt_a2dp_desc a2dp = bt_linfo->a2dp_desc;  	struct rtw89_btc_bt_pan_desc pan = bt_linfo->pan_desc; +	char *p = buf, *end = buf + bufsz;  	if (hfp.exist) { -		seq_printf(m, " %-15s : type:%s, sut_pwr:%d, golden-rx:%d", -			   "[HFP]", (hfp.type == 0 ? "SCO" : "eSCO"), -			   bt_linfo->sut_pwr_level[0], -			   bt_linfo->golden_rx_shift[0]); +		p += scnprintf(p, end - p, +			       " %-15s : type:%s, sut_pwr:%d, golden-rx:%d", +			       "[HFP]", (hfp.type == 0 ? "SCO" : "eSCO"), +			       bt_linfo->sut_pwr_level[0], +			       bt_linfo->golden_rx_shift[0]);  	}  	if (hid.exist) { -		seq_printf(m, -			   "\n\r %-15s : type:%s%s%s%s%s pair-cnt:%d, sut_pwr:%d, golden-rx:%d\n", -			   "[HID]", -			   hid.type & BTC_HID_218 ? "2/18," : "", -			   hid.type & BTC_HID_418 ? "4/18," : "", -			   hid.type & BTC_HID_BLE ? "BLE," : "", -			   hid.type & BTC_HID_RCU ? "RCU," : "", -			   hid.type & BTC_HID_RCU_VOICE ? "RCU-Voice," : "", -			   hid.pair_cnt, bt_linfo->sut_pwr_level[1], -			   bt_linfo->golden_rx_shift[1]); +		p += scnprintf(p, end - p, +			       "\n\r %-15s : type:%s%s%s%s%s pair-cnt:%d, sut_pwr:%d, golden-rx:%d\n", +			       "[HID]", +			       hid.type & BTC_HID_218 ? "2/18," : "", +			       hid.type & BTC_HID_418 ? "4/18," : "", +			       hid.type & BTC_HID_BLE ? "BLE," : "", +			       hid.type & BTC_HID_RCU ? "RCU," : "", +			       hid.type & BTC_HID_RCU_VOICE ? "RCU-Voice," : "", +			       hid.pair_cnt, bt_linfo->sut_pwr_level[1], +			       bt_linfo->golden_rx_shift[1]);  	}  	if (a2dp.exist) { -		seq_printf(m, -			   " %-15s : type:%s, bit-pool:%d, flush-time:%d, ", -			   "[A2DP]", -			   a2dp.type == BTC_A2DP_LEGACY ? "Legacy" : "TWS", -			    a2dp.bitpool, a2dp.flush_time); +		p += scnprintf(p, end - p, +			       " %-15s : type:%s, bit-pool:%d, flush-time:%d, ", +			       "[A2DP]", +			       a2dp.type == BTC_A2DP_LEGACY ? "Legacy" : "TWS", +			       a2dp.bitpool, a2dp.flush_time); -		seq_printf(m, -			   "vid:0x%x, Dev-name:0x%x, sut_pwr:%d, golden-rx:%d\n", -			   a2dp.vendor_id, a2dp.device_name, -			   bt_linfo->sut_pwr_level[2], -			   bt_linfo->golden_rx_shift[2]); +		p += scnprintf(p, end - p, +			       "vid:0x%x, Dev-name:0x%x, sut_pwr:%d, golden-rx:%d\n", +			       a2dp.vendor_id, a2dp.device_name, +			       bt_linfo->sut_pwr_level[2], +			       bt_linfo->golden_rx_shift[2]);  	}  	if (pan.exist) { -		seq_printf(m, " %-15s : sut_pwr:%d, golden-rx:%d\n", -			   "[PAN]", -			   bt_linfo->sut_pwr_level[3], -			   bt_linfo->golden_rx_shift[3]); +		p += scnprintf(p, end - p, +			       " %-15s : sut_pwr:%d, golden-rx:%d\n", +			       "[PAN]", +			       bt_linfo->sut_pwr_level[3], +			       bt_linfo->golden_rx_shift[3]);  	} + +	return p - buf;  } -static void _show_bt_info(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_bt_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)  {  	struct rtw89_btc *btc = &rtwdev->btc;  	const struct rtw89_btc_ver *ver = btc->ver;  	struct rtw89_btc_cx *cx = &btc->cx;  	struct rtw89_btc_bt_info *bt = &cx->bt;  	struct rtw89_btc_wl_info *wl = &cx->wl; +	u32 ver_main = FIELD_GET(GENMASK(31, 24), wl->ver_info.fw_coex);  	struct rtw89_btc_bt_link_info *bt_linfo = &bt->link_info;  	union rtw89_btc_module_info *md = &btc->mdinfo; +	s8 br_dbm = bt->link_info.bt_txpwr_desc.br_dbm; +	s8 le_dbm = bt->link_info.bt_txpwr_desc.le_dbm; +	char *p = buf, *end = buf + bufsz;  	u8 *afh = bt_linfo->afh_map;  	u8 *afh_le = bt_linfo->afh_map_le;  	u8 bt_pos;  	if (!(btc->dm.coex_info_map & BTC_COEX_INFO_BT)) -		return; +		return 0;  	if (ver->fcxinit == 7)  		bt_pos = md->md_v7.bt_pos;  	else  		bt_pos = md->md.bt_pos; -	seq_puts(m, "========== [BT Status] ==========\n"); - -	seq_printf(m, " %-15s : enable:%s, btg:%s%s, connect:%s, ", -		   "[status]", bt->enable.now ? "Y" : "N", -		   bt->btg_type ? "Y" : "N", -		   (bt->enable.now && (bt->btg_type != bt_pos) ? -		   "(efuse-mismatch!!)" : ""), -		   (bt_linfo->status.map.connect ? "Y" : "N")); - -	seq_printf(m, "igno_wl:%s, mailbox_avl:%s, rfk_state:0x%x\n", -		   bt->igno_wl ? "Y" : "N", -		   bt->mbx_avl ? "Y" : "N", bt->rfk_info.val); - -	seq_printf(m, " %-15s : profile:%s%s%s%s%s ", -		   "[profile]", -		   (bt_linfo->profile_cnt.now == 0) ? "None," : "", -		   bt_linfo->hfp_desc.exist ? "HFP," : "", -		   bt_linfo->hid_desc.exist ? "HID," : "", -		   bt_linfo->a2dp_desc.exist ? -		   (bt_linfo->a2dp_desc.sink ? "A2DP_sink," : "A2DP,") : "", -		   bt_linfo->pan_desc.exist ? "PAN," : ""); - -	seq_printf(m, -		   "multi-link:%s, role:%s, ble-connect:%s, CQDDR:%s, A2DP_active:%s, PAN_active:%s\n", -		   bt_linfo->multi_link.now ? "Y" : "N", -		   bt_linfo->slave_role ? "Slave" : "Master", -		   bt_linfo->status.map.ble_connect ? "Y" : "N", -		   bt_linfo->cqddr ? "Y" : "N", -		   bt_linfo->a2dp_desc.active ? "Y" : "N", -		   bt_linfo->pan_desc.active ? "Y" : "N"); - -	seq_printf(m, -		   " %-15s : rssi:%ddBm(lvl:%d), tx_rate:%dM, %s%s%s", -		   "[link]", bt_linfo->rssi - 100, -		   bt->rssi_level, -		   bt_linfo->tx_3m ? 3 : 2, -		   bt_linfo->status.map.inq_pag ? " inq-page!!" : "", -		   bt_linfo->status.map.acl_busy ? " acl_busy!!" : "", -		   bt_linfo->status.map.mesh_busy ? " mesh_busy!!" : ""); - -	seq_printf(m, -		   "%s afh_map[%02x%02x_%02x%02x_%02x%02x_%02x%02x_%02x%02x], ", -		   bt_linfo->relink.now ? " ReLink!!" : "", -		   afh[0], afh[1], afh[2], afh[3], afh[4], -		   afh[5], afh[6], afh[7], afh[8], afh[9]); +	p += scnprintf(p, end - p, "========== [BT Status] ==========\n"); + +	p += scnprintf(p, end - p, +		       " %-15s : enable:%s, btg:%s%s, connect:%s, ", +		       "[status]", bt->enable.now ? "Y" : "N", +		       bt->btg_type ? "Y" : "N", +		       (bt->enable.now && (bt->btg_type != bt_pos) ? +			"(efuse-mismatch!!)" : ""), +		       (bt_linfo->status.map.connect ? "Y" : "N")); + +	p += scnprintf(p, end - p, +		       "igno_wl:%s, mailbox_avl:%s, rfk_state:0x%x\n", +		       bt->igno_wl ? "Y" : "N", +		       bt->mbx_avl ? "Y" : "N", bt->rfk_info.val); + +	p += scnprintf(p, end - p, " %-15s : profile:%s%s%s%s%s ", +		       "[profile]", +		       (bt_linfo->profile_cnt.now == 0) ? "None," : "", +		       bt_linfo->hfp_desc.exist ? "HFP," : "", +		       bt_linfo->hid_desc.exist ? "HID," : "", +		       bt_linfo->a2dp_desc.exist ? +		       (bt_linfo->a2dp_desc.sink ? "A2DP_sink," : "A2DP,") : "", +		       bt_linfo->pan_desc.exist ? "PAN," : ""); + +	p += scnprintf(p, end - p, +		       "multi-link:%s, role:%s, ble-connect:%s, CQDDR:%s, A2DP_active:%s, PAN_active:%s\n", +		       bt_linfo->multi_link.now ? "Y" : "N", +		       bt_linfo->slave_role ? "Slave" : "Master", +		       bt_linfo->status.map.ble_connect ? "Y" : "N", +		       bt_linfo->cqddr ? "Y" : "N", +		       bt_linfo->a2dp_desc.active ? "Y" : "N", +		       bt_linfo->pan_desc.active ? "Y" : "N"); + +	p += scnprintf(p, end - p, +		       " %-15s : rssi:%ddBm(lvl:%d), tx_rate:%dM, %s%s%s", +		       "[link]", bt_linfo->rssi - 100, +		       bt->rssi_level, +		       bt_linfo->tx_3m ? 3 : 2, +		       bt_linfo->status.map.inq_pag ? " inq-page!!" : "", +		       bt_linfo->status.map.acl_busy ? " acl_busy!!" : "", +		       bt_linfo->status.map.mesh_busy ? " mesh_busy!!" : ""); + +	p += scnprintf(p, end - p, +		       "%s afh_map[%02x%02x_%02x%02x_%02x%02x_%02x%02x_%02x%02x], ", +		       bt_linfo->relink.now ? " ReLink!!" : "", +		       afh[0], afh[1], afh[2], afh[3], afh[4], +		       afh[5], afh[6], afh[7], afh[8], afh[9]);  	if (ver->fcxbtafh == 2 && bt_linfo->status.map.ble_connect) -		seq_printf(m, -			   "LE[%02x%02x_%02x_%02x%02x]", -			   afh_le[0], afh_le[1], afh_le[2], -			   afh_le[3], afh_le[4]); - -	seq_printf(m, "wl_ch_map[en:%d/ch:%d/bw:%d]\n", -		   wl->afh_info.en, wl->afh_info.ch, wl->afh_info.bw); - -	seq_printf(m, -		   " %-15s : retry:%d, relink:%d, rate_chg:%d, reinit:%d, reenable:%d, ", -		   "[stat_cnt]", cx->cnt_bt[BTC_BCNT_RETRY], -		   cx->cnt_bt[BTC_BCNT_RELINK], cx->cnt_bt[BTC_BCNT_RATECHG], -		   cx->cnt_bt[BTC_BCNT_REINIT], cx->cnt_bt[BTC_BCNT_REENABLE]); - -	seq_printf(m, -		   "role-switch:%d, afh:%d, inq_page:%d(inq:%d/page:%d), igno_wl:%d\n", -		   cx->cnt_bt[BTC_BCNT_ROLESW], cx->cnt_bt[BTC_BCNT_AFH], -		   cx->cnt_bt[BTC_BCNT_INQPAG], cx->cnt_bt[BTC_BCNT_INQ], -		   cx->cnt_bt[BTC_BCNT_PAGE], cx->cnt_bt[BTC_BCNT_IGNOWL]); - -	_show_bt_profile_info(rtwdev, m); - -	seq_printf(m, -		   " %-15s : raw_data[%02x %02x %02x %02x %02x %02x] (type:%s/cnt:%d/same:%d)\n", -		   "[bt_info]", bt->raw_info[2], bt->raw_info[3], -		   bt->raw_info[4], bt->raw_info[5], bt->raw_info[6], -		   bt->raw_info[7], -		   bt->raw_info[0] == BTC_BTINFO_AUTO ? "auto" : "reply", -		   cx->cnt_bt[BTC_BCNT_INFOUPDATE], -		   cx->cnt_bt[BTC_BCNT_INFOSAME]); - -	seq_printf(m, -		   " %-15s : Hi-rx = %d, Hi-tx = %d, Lo-rx = %d, Lo-tx = %d (bt_polut_wl_tx = %d)", -		   "[trx_req_cnt]", cx->cnt_bt[BTC_BCNT_HIPRI_RX], -		   cx->cnt_bt[BTC_BCNT_HIPRI_TX], cx->cnt_bt[BTC_BCNT_LOPRI_RX], -		   cx->cnt_bt[BTC_BCNT_LOPRI_TX], cx->cnt_bt[BTC_BCNT_POLUT]); +		p += scnprintf(p, end - p, +			       "LE[%02x%02x_%02x_%02x%02x]", +			       afh_le[0], afh_le[1], afh_le[2], +			       afh_le[3], afh_le[4]); + +	p += scnprintf(p, end - p, "wl_ch_map[en:%d/ch:%d/bw:%d]\n", +		       wl->afh_info.en, wl->afh_info.ch, wl->afh_info.bw); + +	p += scnprintf(p, end - p, +		       " %-15s : retry:%d, relink:%d, rate_chg:%d, reinit:%d, reenable:%d, ", +		       "[stat_cnt]", cx->cnt_bt[BTC_BCNT_RETRY], +		       cx->cnt_bt[BTC_BCNT_RELINK], +		       cx->cnt_bt[BTC_BCNT_RATECHG], +		       cx->cnt_bt[BTC_BCNT_REINIT], +		       cx->cnt_bt[BTC_BCNT_REENABLE]); + +	p += scnprintf(p, end - p, +		       "role-switch:%d, afh:%d, inq_page:%d(inq:%d/page:%d), igno_wl:%d\n", +		       cx->cnt_bt[BTC_BCNT_ROLESW], cx->cnt_bt[BTC_BCNT_AFH], +		       cx->cnt_bt[BTC_BCNT_INQPAG], cx->cnt_bt[BTC_BCNT_INQ], +		       cx->cnt_bt[BTC_BCNT_PAGE], cx->cnt_bt[BTC_BCNT_IGNOWL]); + +	p += _show_bt_profile_info(rtwdev, p, end - p); + +	p += scnprintf(p, end - p, +		       " %-15s : raw_data[%02x %02x %02x %02x %02x %02x] (type:%s/cnt:%d/same:%d)\n", +		       "[bt_info]", bt->raw_info[2], bt->raw_info[3], +		       bt->raw_info[4], bt->raw_info[5], bt->raw_info[6], +		       bt->raw_info[7], +		       bt->raw_info[0] == BTC_BTINFO_AUTO ? "auto" : "reply", +		       cx->cnt_bt[BTC_BCNT_INFOUPDATE], +		       cx->cnt_bt[BTC_BCNT_INFOSAME]); + +	p += scnprintf(p, end - p, +		       " %-15s : Hi-rx = %d, Hi-tx = %d, Lo-rx = %d, Lo-tx = %d (bt_polut_wl_tx = %d)", +		       "[trx_req_cnt]", cx->cnt_bt[BTC_BCNT_HIPRI_RX], +		       cx->cnt_bt[BTC_BCNT_HIPRI_TX], +		       cx->cnt_bt[BTC_BCNT_LOPRI_RX], +		       cx->cnt_bt[BTC_BCNT_LOPRI_TX], +		       cx->cnt_bt[BTC_BCNT_POLUT]);  	if (!bt->scan_info_update) {  		rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_SCAN_INFO, true); -		seq_puts(m, "\n"); +		p += scnprintf(p, end - p, "\n");  	} else {  		rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_SCAN_INFO, false);  		if (ver->fcxbtscan == 1) { -			seq_printf(m, -				   "(INQ:%d-%d/PAGE:%d-%d/LE:%d-%d/INIT:%d-%d)", -				   le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INQ].win), -				   le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INQ].intvl), -				   le16_to_cpu(bt->scan_info_v1[BTC_SCAN_PAGE].win), -				   le16_to_cpu(bt->scan_info_v1[BTC_SCAN_PAGE].intvl), -				   le16_to_cpu(bt->scan_info_v1[BTC_SCAN_BLE].win), -				   le16_to_cpu(bt->scan_info_v1[BTC_SCAN_BLE].intvl), -				   le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INIT].win), -				   le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INIT].intvl)); +			p += scnprintf(p, end - p, +				       "(INQ:%d-%d/PAGE:%d-%d/LE:%d-%d/INIT:%d-%d)", +				       le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INQ].win), +				       le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INQ].intvl), +				       le16_to_cpu(bt->scan_info_v1[BTC_SCAN_PAGE].win), +				       le16_to_cpu(bt->scan_info_v1[BTC_SCAN_PAGE].intvl), +				       le16_to_cpu(bt->scan_info_v1[BTC_SCAN_BLE].win), +				       le16_to_cpu(bt->scan_info_v1[BTC_SCAN_BLE].intvl), +				       le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INIT].win), +				       le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INIT].intvl));  		} else if (ver->fcxbtscan == 2) { -			seq_printf(m, -				   "(BG:%d-%d/INIT:%d-%d/LE:%d-%d)", -				   le16_to_cpu(bt->scan_info_v2[CXSCAN_BG].win), -				   le16_to_cpu(bt->scan_info_v2[CXSCAN_BG].intvl), -				   le16_to_cpu(bt->scan_info_v2[CXSCAN_INIT].win), -				   le16_to_cpu(bt->scan_info_v2[CXSCAN_INIT].intvl), -				   le16_to_cpu(bt->scan_info_v2[CXSCAN_LE].win), -				   le16_to_cpu(bt->scan_info_v2[CXSCAN_LE].intvl)); +			p += scnprintf(p, end - p, +				       "(BG:%d-%d/INIT:%d-%d/LE:%d-%d)", +				       le16_to_cpu(bt->scan_info_v2[CXSCAN_BG].win), +				       le16_to_cpu(bt->scan_info_v2[CXSCAN_BG].intvl), +				       le16_to_cpu(bt->scan_info_v2[CXSCAN_INIT].win), +				       le16_to_cpu(bt->scan_info_v2[CXSCAN_INIT].intvl), +				       le16_to_cpu(bt->scan_info_v2[CXSCAN_LE].win), +				       le16_to_cpu(bt->scan_info_v2[CXSCAN_LE].intvl));  		} -		seq_puts(m, "\n"); +		p += scnprintf(p, end - p, "\n"); +	} + +	if (ver_main >= 9 && bt_linfo->profile_cnt.now) +		rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_TX_PWR_LVL, true); +	else +		rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_TX_PWR_LVL, false); + +	if (cx->cnt_bt[BTC_BCNT_BTTXPWR_UPDATE]) { +		p += scnprintf(p, end - p, +			       " %-15s : br_index:0x%x, le_index:0x%x", +			       "[bt_txpwr_lvl]", +			       bt->link_info.bt_txpwr_desc.br_gain_index, +			       bt->link_info.bt_txpwr_desc.le_gain_index); +		p += scnprintf(p, end - p, ", br_dbm:%d dBm", br_dbm); +		p += scnprintf(p, end - p, ", le_dbm:%d dBm", le_dbm); +	} else { +		p += scnprintf(p, end - p, +			       " %-15s : br_index:NA, le_index:NA, br_dbm:%d dBm[def], le_dbm:%d dBm[def]", +			       "[bt_txpwr_lvl]", +			       bt->link_info.bt_txpwr_desc.br_dbm, +			       bt->link_info.bt_txpwr_desc.le_dbm);  	} +	p += scnprintf(p, end - p, "\n");  	if (bt_linfo->profile_cnt.now || bt_linfo->status.map.ble_connect)  		rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_AFH_MAP, true); @@ -8560,6 +9240,8 @@ static void _show_bt_info(struct rtw89_dev *rtwdev, struct seq_file *m)  		rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_DEVICE_INFO, true);  	else  		rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_DEVICE_INFO, false); + +	return p - buf;  }  #define CASE_BTC_RSN_STR(e) case BTC_RSN_ ## e: return #e @@ -8853,114 +9535,132 @@ static const char *id_to_ant(u32 id)  }  static -void seq_print_segment(struct seq_file *m, const char *prefix, u16 *data, -		       u8 len, u8 seg_len, u8 start_idx, u8 ring_len) +int scnprintf_segment(char *buf, size_t bufsz, const char *prefix, const u16 *data, +		      u8 len, u8 seg_len, u8 start_idx, u8 ring_len)  { -	u8 i; +	char *p = buf, *end = buf + bufsz;  	u8 cur_index; +	u8 i;  	for (i = 0; i < len ; i++) {  		if ((i % seg_len) == 0) -			seq_printf(m, " %-15s : ", prefix); +			p += scnprintf(p, end - p, " %-15s : ", prefix);  		cur_index = (start_idx + i) % ring_len;  		if (i % 3 == 0) -			seq_printf(m, "-> %-20s", -				   steps_to_str(*(data + cur_index))); +			p += scnprintf(p, end - p, "-> %-20s", +				       steps_to_str(*(data + cur_index)));  		else if (i % 3 == 1) -			seq_printf(m, "-> %-15s", -				   steps_to_str(*(data + cur_index))); +			p += scnprintf(p, end - p, "-> %-15s", +				       steps_to_str(*(data + cur_index)));  		else -			seq_printf(m, "-> %-13s", -				   steps_to_str(*(data + cur_index))); +			p += scnprintf(p, end - p, "-> %-13s", +				       steps_to_str(*(data + cur_index)));  		if (i == (len - 1) || (i % seg_len) == (seg_len - 1)) -			seq_puts(m, "\n"); +			p += scnprintf(p, end - p, "\n");  	} + +	return p - buf;  } -static void _show_dm_step(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_dm_step(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)  {  	struct rtw89_btc *btc = &rtwdev->btc;  	struct rtw89_btc_dm *dm = &btc->dm; +	char *p = buf, *end = buf + bufsz;  	u8 start_idx;  	u8 len;  	len = dm->dm_step.step_ov ? RTW89_BTC_DM_MAXSTEP : dm->dm_step.step_pos;  	start_idx = dm->dm_step.step_ov ? dm->dm_step.step_pos : 0; -	seq_print_segment(m, "[dm_steps]", dm->dm_step.step, len, 6, start_idx, -			  ARRAY_SIZE(dm->dm_step.step)); +	p += scnprintf_segment(p, end - p, "[dm_steps]", dm->dm_step.step, len, +			       6, start_idx, ARRAY_SIZE(dm->dm_step.step)); + +	return p - buf;  } -static void _show_dm_info(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_dm_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)  {  	struct rtw89_btc *btc = &rtwdev->btc;  	const struct rtw89_btc_ver *ver = btc->ver;  	struct rtw89_btc_dm *dm = &btc->dm;  	struct rtw89_btc_wl_info *wl = &btc->cx.wl;  	struct rtw89_btc_bt_info *bt = &btc->cx.bt; +	char *p = buf, *end = buf + bufsz;  	u8 igno_bt;  	if (!(dm->coex_info_map & BTC_COEX_INFO_DM)) -		return; +		return 0; -	seq_printf(m, "========== [Mechanism Status %s] ==========\n", -		   (btc->manual_ctrl ? "(Manual)" : "(Auto)")); +	p += scnprintf(p, end - p, +		       "========== [Mechanism Status %s] ==========\n", +		       (btc->manual_ctrl ? "(Manual)" : "(Auto)")); -	seq_printf(m, -		   " %-15s : type:%s, reason:%s(), action:%s(), ant_path:%s, init_mode:%s, run_cnt:%d\n", -		   "[status]", -		   btc->ant_type == BTC_ANT_SHARED ? "shared" : "dedicated", -		   steps_to_str(dm->run_reason), -		   steps_to_str(dm->run_action | BTC_ACT_EXT_BIT), -		   id_to_ant(FIELD_GET(GENMASK(7, 0), dm->set_ant_path)), -		   id_to_mode(wl->coex_mode), -		   dm->cnt_dm[BTC_DCNT_RUN]); +	p += scnprintf(p, end - p, +		       " %-15s : type:%s, reason:%s(), action:%s(), ant_path:%s, init_mode:%s, run_cnt:%d\n", +		       "[status]", +		       btc->ant_type == BTC_ANT_SHARED ? "shared" : "dedicated", +		       steps_to_str(dm->run_reason), +		       steps_to_str(dm->run_action | BTC_ACT_EXT_BIT), +		       id_to_ant(FIELD_GET(GENMASK(7, 0), dm->set_ant_path)), +		       id_to_mode(wl->coex_mode), +		       dm->cnt_dm[BTC_DCNT_RUN]); -	_show_dm_step(rtwdev, m); +	p += _show_dm_step(rtwdev, p, end - p);  	if (ver->fcxctrl == 7)  		igno_bt = btc->ctrl.ctrl_v7.igno_bt;  	else  		igno_bt = btc->ctrl.ctrl.igno_bt; -	seq_printf(m, " %-15s : wl_only:%d, bt_only:%d, igno_bt:%d, free_run:%d, wl_ps_ctrl:%d, wl_mimo_ps:%d, ", -		   "[dm_flag]", dm->wl_only, dm->bt_only, igno_bt, -		   dm->freerun, btc->lps, dm->wl_mimo_ps); +	p += scnprintf(p, end - p, +		       " %-15s : wl_only:%d, bt_only:%d, igno_bt:%d, free_run:%d, wl_ps_ctrl:%d, wl_mimo_ps:%d, ", +		       "[dm_flag]", dm->wl_only, dm->bt_only, igno_bt, +		       dm->freerun, btc->lps, dm->wl_mimo_ps); -	seq_printf(m, "leak_ap:%d, fw_offload:%s%s\n", dm->leak_ap, -		   (BTC_CX_FW_OFFLOAD ? "Y" : "N"), -		   (dm->wl_fw_cx_offload == BTC_CX_FW_OFFLOAD ? -		    "" : "(Mismatch!!)")); +	p += scnprintf(p, end - p, "leak_ap:%d, fw_offload:%s%s\n", +		       dm->leak_ap, +		       (BTC_CX_FW_OFFLOAD ? "Y" : "N"), +		       (dm->wl_fw_cx_offload == BTC_CX_FW_OFFLOAD ? +			"" : "(Mismatch!!)"));  	if (dm->rf_trx_para.wl_tx_power == 0xff) -		seq_printf(m, -			   " %-15s : wl_rssi_lvl:%d, para_lvl:%d, wl_tx_pwr:orig, ", -			   "[trx_ctrl]", wl->rssi_level, dm->trx_para_level); +		p += scnprintf(p, end - p, +			       " %-15s : wl_rssi_lvl:%d, para_lvl:%d, wl_tx_pwr:orig, ", +			       "[trx_ctrl]", wl->rssi_level, +			       dm->trx_para_level);  	else -		seq_printf(m, -			   " %-15s : wl_rssi_lvl:%d, para_lvl:%d, wl_tx_pwr:%d, ", -			   "[trx_ctrl]", wl->rssi_level, dm->trx_para_level, -			   dm->rf_trx_para.wl_tx_power); +		p += scnprintf(p, end - p, +			       " %-15s : wl_rssi_lvl:%d, para_lvl:%d, wl_tx_pwr:%d, ", +			       "[trx_ctrl]", wl->rssi_level, +			       dm->trx_para_level, +			       dm->rf_trx_para.wl_tx_power); + +	p += scnprintf(p, end - p, +		       "wl_rx_lvl:%d, bt_tx_pwr_dec:%d, bt_rx_lna:%d(%s-tbl), wl_btg_rx:%d\n", +		       dm->rf_trx_para.wl_rx_gain, +		       dm->rf_trx_para.bt_tx_power, +		       dm->rf_trx_para.bt_rx_gain, +		       (bt->hi_lna_rx ? "Hi" : "Ori"), dm->wl_btg_rx); -	seq_printf(m, -		   "wl_rx_lvl:%d, bt_tx_pwr_dec:%d, bt_rx_lna:%d(%s-tbl), wl_btg_rx:%d\n", -		   dm->rf_trx_para.wl_rx_gain, dm->rf_trx_para.bt_tx_power, -		   dm->rf_trx_para.bt_rx_gain, -		   (bt->hi_lna_rx ? "Hi" : "Ori"), dm->wl_btg_rx); +	p += scnprintf(p, end - p, +		       " %-15s : wl_tx_limit[en:%d/max_t:%dus/max_retry:%d], bt_slot_reg:%d-TU, bt_scan_rx_low_pri:%d\n", +		       "[dm_ctrl]", dm->wl_tx_limit.enable, +		       dm->wl_tx_limit.tx_time, +		       dm->wl_tx_limit.tx_retry, btc->bt_req_len, +		       bt->scan_rx_low_pri); -	seq_printf(m, -		   " %-15s : wl_tx_limit[en:%d/max_t:%dus/max_retry:%d], bt_slot_reg:%d-TU, bt_scan_rx_low_pri:%d\n", -		   "[dm_ctrl]", dm->wl_tx_limit.enable, dm->wl_tx_limit.tx_time, -		   dm->wl_tx_limit.tx_retry, btc->bt_req_len, bt->scan_rx_low_pri); +	return p - buf;  } -static void _show_error(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_error(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)  {  	struct rtw89_btc *btc = &rtwdev->btc;  	const struct rtw89_btc_ver *ver = btc->ver;  	struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo;  	union rtw89_btc_fbtc_cysta_info *pcysta; +	char *p = buf, *end = buf + bufsz;  	u32 except_cnt, exception_map;  	pcysta = &pfwinfo->rpt_fbtc_cysta.finfo; @@ -8985,81 +9685,87 @@ static void _show_error(struct rtw89_dev *rtwdev, struct seq_file *m)  		except_cnt = pcysta->v7.except_cnt;  		exception_map = le32_to_cpu(pcysta->v7.except_map);  	} else { -		return; +		return 0;  	}  	if (pfwinfo->event[BTF_EVNT_BUF_OVERFLOW] == 0 && except_cnt == 0 &&  	    !pfwinfo->len_mismch && !pfwinfo->fver_mismch) -		return; +		return 0; -	seq_printf(m, " %-15s : ", "[error]"); +	p += scnprintf(p, end - p, " %-15s : ", "[error]");  	if (pfwinfo->event[BTF_EVNT_BUF_OVERFLOW]) { -		seq_printf(m, -			   "overflow-cnt: %d, ", -			   pfwinfo->event[BTF_EVNT_BUF_OVERFLOW]); +		p += scnprintf(p, end - p, +			       "overflow-cnt: %d, ", +			       pfwinfo->event[BTF_EVNT_BUF_OVERFLOW]);  	}  	if (pfwinfo->len_mismch) { -		seq_printf(m, -			   "len-mismatch: 0x%x, ", -			   pfwinfo->len_mismch); +		p += scnprintf(p, end - p, +			       "len-mismatch: 0x%x, ", +			       pfwinfo->len_mismch);  	}  	if (pfwinfo->fver_mismch) { -		seq_printf(m, -			   "fver-mismatch: 0x%x, ", -			   pfwinfo->fver_mismch); +		p += scnprintf(p, end - p, +			       "fver-mismatch: 0x%x, ", +			       pfwinfo->fver_mismch);  	}  	/* cycle statistics exceptions */  	if (exception_map || except_cnt) { -		seq_printf(m, -			   "exception-type: 0x%x, exception-cnt = %d", -			   exception_map, except_cnt); +		p += scnprintf(p, end - p, +			       "exception-type: 0x%x, exception-cnt = %d", +			       exception_map, except_cnt);  	} -	seq_puts(m, "\n"); +	p += scnprintf(p, end - p, "\n"); + +	return p - buf;  } -static void _show_fbtc_tdma(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_fbtc_tdma(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)  {  	struct rtw89_btc *btc = &rtwdev->btc;  	const struct rtw89_btc_ver *ver = btc->ver;  	struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo;  	struct rtw89_btc_rpt_cmn_info *pcinfo = NULL;  	struct rtw89_btc_fbtc_tdma *t = NULL; +	char *p = buf, *end = buf + bufsz;  	pcinfo = &pfwinfo->rpt_fbtc_tdma.cinfo;  	if (!pcinfo->valid) -		return; +		return 0;  	if (ver->fcxtdma == 1)  		t = &pfwinfo->rpt_fbtc_tdma.finfo.v1;  	else  		t = &pfwinfo->rpt_fbtc_tdma.finfo.v3.tdma; -	seq_printf(m, -		   " %-15s : ", "[tdma_policy]"); -	seq_printf(m, -		   "type:%d, rx_flow_ctrl:%d, tx_pause:%d, ", -		   (u32)t->type, -		   t->rxflctrl, t->txpause); +	p += scnprintf(p, end - p, +		       " %-15s : ", "[tdma_policy]"); +	p += scnprintf(p, end - p, +		       "type:%d, rx_flow_ctrl:%d, tx_pause:%d, ", +		       (u32)t->type, +		       t->rxflctrl, t->txpause); -	seq_printf(m, -		   "wl_toggle_n:%d, leak_n:%d, ext_ctrl:%d, ", -		   t->wtgle_n, t->leak_n, t->ext_ctrl); +	p += scnprintf(p, end - p, +		       "wl_toggle_n:%d, leak_n:%d, ext_ctrl:%d, ", +		       t->wtgle_n, t->leak_n, t->ext_ctrl); -	seq_printf(m, -		   "policy_type:%d", -		   (u32)btc->policy_type); +	p += scnprintf(p, end - p, +		       "policy_type:%d", +		       (u32)btc->policy_type); -	seq_puts(m, "\n"); +	p += scnprintf(p, end - p, "\n"); + +	return p - buf;  } -static void _show_fbtc_slots(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_fbtc_slots(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)  {  	struct rtw89_btc *btc = &rtwdev->btc;  	struct rtw89_btc_dm *dm = &btc->dm; +	char *p = buf, *end = buf + bufsz;  	u16 dur, cxtype;  	u32 tbl;  	u8 i = 0; @@ -9074,28 +9780,29 @@ static void _show_fbtc_slots(struct rtw89_dev *rtwdev, struct seq_file *m)  			tbl = le32_to_cpu(dm->slot_now.v7[i].cxtbl);  			cxtype = le16_to_cpu(dm->slot_now.v7[i].cxtype);  		} else { -			return; +			return 0;  		}  		if (i % 5 == 0) -			seq_printf(m, -				   " %-15s : %5s[%03d/0x%x/%d]", -				   "[slot_list]", -				   id_to_slot((u32)i), -				   dur, tbl, cxtype); +			p += scnprintf(p, end - p, +				       " %-15s : %5s[%03d/0x%x/%d]", +				       "[slot_list]", +				       id_to_slot((u32)i), +				       dur, tbl, cxtype);  		else -			seq_printf(m, -				   ", %5s[%03d/0x%x/%d]", -				   id_to_slot((u32)i), -				   dur, tbl, cxtype); +			p += scnprintf(p, end - p, +				       ", %5s[%03d/0x%x/%d]", +				       id_to_slot((u32)i), +				       dur, tbl, cxtype);  		if (i % 5 == 4) -			seq_puts(m, "\n"); +			p += scnprintf(p, end - p, "\n");  	} -	seq_puts(m, "\n"); + +	return p - buf;  } -static void _show_fbtc_cysta_v2(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_fbtc_cysta_v2(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)  {  	struct rtw89_btc *btc = &rtwdev->btc;  	struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; @@ -9104,63 +9811,64 @@ static void _show_fbtc_cysta_v2(struct rtw89_dev *rtwdev, struct seq_file *m)  	struct rtw89_btc_rpt_cmn_info *pcinfo = NULL;  	struct rtw89_btc_fbtc_cysta_v2 *pcysta_le32 = NULL;  	union rtw89_btc_fbtc_rxflct r; -	u8 i, cnt = 0, slot_pair;  	u16 cycle, c_begin, c_end, store_index; +	char *p = buf, *end = buf + bufsz; +	u8 i, cnt = 0, slot_pair;  	pcinfo = &pfwinfo->rpt_fbtc_cysta.cinfo;  	if (!pcinfo->valid) -		return; +		return 0;  	pcysta_le32 = &pfwinfo->rpt_fbtc_cysta.finfo.v2; -	seq_printf(m, -		   " %-15s : cycle:%d, bcn[all:%d/all_ok:%d/bt:%d/bt_ok:%d]", -		   "[cycle_cnt]", -		   le16_to_cpu(pcysta_le32->cycles), -		   le32_to_cpu(pcysta_le32->bcn_cnt[CXBCN_ALL]), -		   le32_to_cpu(pcysta_le32->bcn_cnt[CXBCN_ALL_OK]), -		   le32_to_cpu(pcysta_le32->bcn_cnt[CXBCN_BT_SLOT]), -		   le32_to_cpu(pcysta_le32->bcn_cnt[CXBCN_BT_OK])); +	p += scnprintf(p, end - p, +		       " %-15s : cycle:%d, bcn[all:%d/all_ok:%d/bt:%d/bt_ok:%d]", +		       "[cycle_cnt]", +		       le16_to_cpu(pcysta_le32->cycles), +		       le32_to_cpu(pcysta_le32->bcn_cnt[CXBCN_ALL]), +		       le32_to_cpu(pcysta_le32->bcn_cnt[CXBCN_ALL_OK]), +		       le32_to_cpu(pcysta_le32->bcn_cnt[CXBCN_BT_SLOT]), +		       le32_to_cpu(pcysta_le32->bcn_cnt[CXBCN_BT_OK]));  	for (i = 0; i < CXST_MAX; i++) {  		if (!le32_to_cpu(pcysta_le32->slot_cnt[i]))  			continue; -		seq_printf(m, ", %s:%d", id_to_slot((u32)i), -			   le32_to_cpu(pcysta_le32->slot_cnt[i])); +		p += scnprintf(p, end - p, ", %s:%d", id_to_slot((u32)i), +			       le32_to_cpu(pcysta_le32->slot_cnt[i]));  	}  	if (dm->tdma_now.rxflctrl) { -		seq_printf(m, ", leak_rx:%d", -			   le32_to_cpu(pcysta_le32->leakrx_cnt)); +		p += scnprintf(p, end - p, ", leak_rx:%d", +			       le32_to_cpu(pcysta_le32->leakrx_cnt));  	}  	if (le32_to_cpu(pcysta_le32->collision_cnt)) { -		seq_printf(m, ", collision:%d", -			   le32_to_cpu(pcysta_le32->collision_cnt)); +		p += scnprintf(p, end - p, ", collision:%d", +			       le32_to_cpu(pcysta_le32->collision_cnt));  	}  	if (le32_to_cpu(pcysta_le32->skip_cnt)) { -		seq_printf(m, ", skip:%d", -			   le32_to_cpu(pcysta_le32->skip_cnt)); -	} -	seq_puts(m, "\n"); - -	seq_printf(m, " %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]", -		   "[cycle_time]", -		   le16_to_cpu(pcysta_le32->tavg_cycle[CXT_WL]), -		   le16_to_cpu(pcysta_le32->tavg_cycle[CXT_BT]), -		   le16_to_cpu(pcysta_le32->tavg_lk) / 1000, -		   le16_to_cpu(pcysta_le32->tavg_lk) % 1000); -	seq_printf(m, ", max_t[wl:%d/bt:%d/lk:%d.%03d]", -		   le16_to_cpu(pcysta_le32->tmax_cycle[CXT_WL]), -		   le16_to_cpu(pcysta_le32->tmax_cycle[CXT_BT]), -		   le16_to_cpu(pcysta_le32->tmax_lk) / 1000, -		   le16_to_cpu(pcysta_le32->tmax_lk) % 1000); -	seq_printf(m, ", maxdiff_t[wl:%d/bt:%d]\n", -		   le16_to_cpu(pcysta_le32->tmaxdiff_cycle[CXT_WL]), -		   le16_to_cpu(pcysta_le32->tmaxdiff_cycle[CXT_BT])); +		p += scnprintf(p, end - p, ", skip:%d", +			       le32_to_cpu(pcysta_le32->skip_cnt)); +	} +	p += scnprintf(p, end - p, "\n"); + +	p += scnprintf(p, end - p, " %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]", +		       "[cycle_time]", +		       le16_to_cpu(pcysta_le32->tavg_cycle[CXT_WL]), +		       le16_to_cpu(pcysta_le32->tavg_cycle[CXT_BT]), +		       le16_to_cpu(pcysta_le32->tavg_lk) / 1000, +		       le16_to_cpu(pcysta_le32->tavg_lk) % 1000); +	p += scnprintf(p, end - p, ", max_t[wl:%d/bt:%d/lk:%d.%03d]", +		       le16_to_cpu(pcysta_le32->tmax_cycle[CXT_WL]), +		       le16_to_cpu(pcysta_le32->tmax_cycle[CXT_BT]), +		       le16_to_cpu(pcysta_le32->tmax_lk) / 1000, +		       le16_to_cpu(pcysta_le32->tmax_lk) % 1000); +	p += scnprintf(p, end - p, ", maxdiff_t[wl:%d/bt:%d]\n", +		       le16_to_cpu(pcysta_le32->tmaxdiff_cycle[CXT_WL]), +		       le16_to_cpu(pcysta_le32->tmaxdiff_cycle[CXT_BT]));  	if (le16_to_cpu(pcysta_le32->cycles) <= 1) -		return; +		goto out;  	/* 1 cycle record 1 wl-slot and 1 bt-slot */  	slot_pair = BTC_CYCLE_SLOT_MAX / 2; @@ -9177,53 +9885,57 @@ static void _show_fbtc_cysta_v2(struct rtw89_dev *rtwdev, struct seq_file *m)  		store_index = ((cycle - 1) % slot_pair) * 2;  		if (cnt % (BTC_CYCLE_SLOT_MAX / 4) == 1) -			seq_printf(m, -				   " %-15s : ->b%02d->w%02d", "[cycle_step]", -				   le16_to_cpu(pcysta_le32->tslot_cycle[store_index]), -				   le16_to_cpu(pcysta_le32->tslot_cycle[store_index + 1])); +			p += scnprintf(p, end - p, +				       " %-15s : ->b%02d->w%02d", +				       "[cycle_step]", +				       le16_to_cpu(pcysta_le32->tslot_cycle[store_index]), +				       le16_to_cpu(pcysta_le32->tslot_cycle[store_index + 1]));  		else -			seq_printf(m, -				   "->b%02d->w%02d", -				   le16_to_cpu(pcysta_le32->tslot_cycle[store_index]), -				   le16_to_cpu(pcysta_le32->tslot_cycle[store_index + 1])); +			p += scnprintf(p, end - p, +				       "->b%02d->w%02d", +				       le16_to_cpu(pcysta_le32->tslot_cycle[store_index]), +				       le16_to_cpu(pcysta_le32->tslot_cycle[store_index + 1]));  		if (cnt % (BTC_CYCLE_SLOT_MAX / 4) == 0 || cnt == c_end) -			seq_puts(m, "\n"); +			p += scnprintf(p, end - p, "\n");  	}  	if (a2dp->exist) { -		seq_printf(m, -			   " %-15s : a2dp_ept:%d, a2dp_late:%d", -			   "[a2dp_t_sta]", -			   le16_to_cpu(pcysta_le32->a2dpept), -			   le16_to_cpu(pcysta_le32->a2dpeptto)); - -		seq_printf(m, -			   ", avg_t:%d, max_t:%d", -			   le16_to_cpu(pcysta_le32->tavg_a2dpept), -			   le16_to_cpu(pcysta_le32->tmax_a2dpept)); +		p += scnprintf(p, end - p, +			       " %-15s : a2dp_ept:%d, a2dp_late:%d", +			       "[a2dp_t_sta]", +			       le16_to_cpu(pcysta_le32->a2dpept), +			       le16_to_cpu(pcysta_le32->a2dpeptto)); + +		p += scnprintf(p, end - p, +			       ", avg_t:%d, max_t:%d", +			       le16_to_cpu(pcysta_le32->tavg_a2dpept), +			       le16_to_cpu(pcysta_le32->tmax_a2dpept));  		r.val = dm->tdma_now.rxflctrl;  		if (r.type && r.tgln_n) { -			seq_printf(m, -				   ", cycle[PSTDMA:%d/TDMA:%d], ", -				   le16_to_cpu(pcysta_le32->cycles_a2dp[CXT_FLCTRL_ON]), -				   le16_to_cpu(pcysta_le32->cycles_a2dp[CXT_FLCTRL_OFF])); - -			seq_printf(m, -				   "avg_t[PSTDMA:%d/TDMA:%d], ", -				   le16_to_cpu(pcysta_le32->tavg_a2dp[CXT_FLCTRL_ON]), -				   le16_to_cpu(pcysta_le32->tavg_a2dp[CXT_FLCTRL_OFF])); - -			seq_printf(m, -				   "max_t[PSTDMA:%d/TDMA:%d]", -				   le16_to_cpu(pcysta_le32->tmax_a2dp[CXT_FLCTRL_ON]), -				   le16_to_cpu(pcysta_le32->tmax_a2dp[CXT_FLCTRL_OFF])); +			p += scnprintf(p, end - p, +				       ", cycle[PSTDMA:%d/TDMA:%d], ", +				       le16_to_cpu(pcysta_le32->cycles_a2dp[CXT_FLCTRL_ON]), +				       le16_to_cpu(pcysta_le32->cycles_a2dp[CXT_FLCTRL_OFF])); + +			p += scnprintf(p, end - p, +				       "avg_t[PSTDMA:%d/TDMA:%d], ", +				       le16_to_cpu(pcysta_le32->tavg_a2dp[CXT_FLCTRL_ON]), +				       le16_to_cpu(pcysta_le32->tavg_a2dp[CXT_FLCTRL_OFF])); + +			p += scnprintf(p, end - p, +				       "max_t[PSTDMA:%d/TDMA:%d]", +				       le16_to_cpu(pcysta_le32->tmax_a2dp[CXT_FLCTRL_ON]), +				       le16_to_cpu(pcysta_le32->tmax_a2dp[CXT_FLCTRL_OFF]));  		} -		seq_puts(m, "\n"); +		p += scnprintf(p, end - p, "\n");  	} + +out: +	return p - buf;  } -static void _show_fbtc_cysta_v3(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_fbtc_cysta_v3(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)  {  	struct rtw89_btc *btc = &rtwdev->btc;  	struct rtw89_btc_bt_a2dp_desc *a2dp = &btc->cx.bt.link_info.a2dp_desc; @@ -9234,60 +9946,64 @@ static void _show_fbtc_cysta_v3(struct rtw89_dev *rtwdev, struct seq_file *m)  	struct rtw89_btc_rpt_cmn_info *pcinfo;  	u8 i, cnt = 0, slot_pair, divide_cnt;  	u16 cycle, c_begin, c_end, store_index; +	char *p = buf, *end = buf + bufsz;  	pcinfo = &pfwinfo->rpt_fbtc_cysta.cinfo;  	if (!pcinfo->valid) -		return; +		return 0;  	pcysta = &pfwinfo->rpt_fbtc_cysta.finfo.v3; -	seq_printf(m, -		   " %-15s : cycle:%d, bcn[all:%d/all_ok:%d/bt:%d/bt_ok:%d]", -		   "[cycle_cnt]", -		   le16_to_cpu(pcysta->cycles), -		   le32_to_cpu(pcysta->bcn_cnt[CXBCN_ALL]), -		   le32_to_cpu(pcysta->bcn_cnt[CXBCN_ALL_OK]), -		   le32_to_cpu(pcysta->bcn_cnt[CXBCN_BT_SLOT]), -		   le32_to_cpu(pcysta->bcn_cnt[CXBCN_BT_OK])); +	p += scnprintf(p, end - p, +		       " %-15s : cycle:%d, bcn[all:%d/all_ok:%d/bt:%d/bt_ok:%d]", +		       "[cycle_cnt]", +		       le16_to_cpu(pcysta->cycles), +		       le32_to_cpu(pcysta->bcn_cnt[CXBCN_ALL]), +		       le32_to_cpu(pcysta->bcn_cnt[CXBCN_ALL_OK]), +		       le32_to_cpu(pcysta->bcn_cnt[CXBCN_BT_SLOT]), +		       le32_to_cpu(pcysta->bcn_cnt[CXBCN_BT_OK]));  	for (i = 0; i < CXST_MAX; i++) {  		if (!le32_to_cpu(pcysta->slot_cnt[i]))  			continue; -		seq_printf(m, ", %s:%d", id_to_slot(i), -			   le32_to_cpu(pcysta->slot_cnt[i])); +		p += scnprintf(p, end - p, ", %s:%d", id_to_slot(i), +			       le32_to_cpu(pcysta->slot_cnt[i]));  	}  	if (dm->tdma_now.rxflctrl) -		seq_printf(m, ", leak_rx:%d", le32_to_cpu(pcysta->leak_slot.cnt_rximr)); +		p += scnprintf(p, end - p, ", leak_rx:%d", +			       le32_to_cpu(pcysta->leak_slot.cnt_rximr));  	if (le32_to_cpu(pcysta->collision_cnt)) -		seq_printf(m, ", collision:%d", le32_to_cpu(pcysta->collision_cnt)); +		p += scnprintf(p, end - p, ", collision:%d", +			       le32_to_cpu(pcysta->collision_cnt));  	if (le32_to_cpu(pcysta->skip_cnt)) -		seq_printf(m, ", skip:%d", le32_to_cpu(pcysta->skip_cnt)); - -	seq_puts(m, "\n"); - -	seq_printf(m, " %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]", -		   "[cycle_time]", -		   le16_to_cpu(pcysta->cycle_time.tavg[CXT_WL]), -		   le16_to_cpu(pcysta->cycle_time.tavg[CXT_BT]), -		   le16_to_cpu(pcysta->leak_slot.tavg) / 1000, -		   le16_to_cpu(pcysta->leak_slot.tavg) % 1000); -	seq_printf(m, -		   ", max_t[wl:%d/bt:%d/lk:%d.%03d]", -		   le16_to_cpu(pcysta->cycle_time.tmax[CXT_WL]), -		   le16_to_cpu(pcysta->cycle_time.tmax[CXT_BT]), -		   le16_to_cpu(pcysta->leak_slot.tmax) / 1000, -		   le16_to_cpu(pcysta->leak_slot.tmax) % 1000); -	seq_printf(m, -		   ", maxdiff_t[wl:%d/bt:%d]\n", -		   le16_to_cpu(pcysta->cycle_time.tmaxdiff[CXT_WL]), -		   le16_to_cpu(pcysta->cycle_time.tmaxdiff[CXT_BT])); +		p += scnprintf(p, end - p, ", skip:%d", +			       le32_to_cpu(pcysta->skip_cnt)); + +	p += scnprintf(p, end - p, "\n"); + +	p += scnprintf(p, end - p, " %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]", +		       "[cycle_time]", +		       le16_to_cpu(pcysta->cycle_time.tavg[CXT_WL]), +		       le16_to_cpu(pcysta->cycle_time.tavg[CXT_BT]), +		       le16_to_cpu(pcysta->leak_slot.tavg) / 1000, +		       le16_to_cpu(pcysta->leak_slot.tavg) % 1000); +	p += scnprintf(p, end - p, +		       ", max_t[wl:%d/bt:%d/lk:%d.%03d]", +		       le16_to_cpu(pcysta->cycle_time.tmax[CXT_WL]), +		       le16_to_cpu(pcysta->cycle_time.tmax[CXT_BT]), +		       le16_to_cpu(pcysta->leak_slot.tmax) / 1000, +		       le16_to_cpu(pcysta->leak_slot.tmax) % 1000); +	p += scnprintf(p, end - p, +		       ", maxdiff_t[wl:%d/bt:%d]\n", +		       le16_to_cpu(pcysta->cycle_time.tmaxdiff[CXT_WL]), +		       le16_to_cpu(pcysta->cycle_time.tmaxdiff[CXT_BT]));  	cycle = le16_to_cpu(pcysta->cycles);  	if (cycle <= 1) -		return; +		goto out;  	/* 1 cycle record 1 wl-slot and 1 bt-slot */  	slot_pair = BTC_CYCLE_SLOT_MAX / 2; @@ -9309,51 +10025,56 @@ static void _show_fbtc_cysta_v3(struct rtw89_dev *rtwdev, struct seq_file *m)  		store_index = ((cycle - 1) % slot_pair) * 2;  		if (cnt % divide_cnt == 1) -			seq_printf(m, " %-15s : ", "[cycle_step]"); +			p += scnprintf(p, end - p, " %-15s : ", +				       "[cycle_step]"); -		seq_printf(m, "->b%02d", -			   le16_to_cpu(pcysta->slot_step_time[store_index])); +		p += scnprintf(p, end - p, "->b%02d", +			       le16_to_cpu(pcysta->slot_step_time[store_index]));  		if (a2dp->exist) {  			a2dp_trx = &pcysta->a2dp_trx[store_index]; -			seq_printf(m, "(%d/%d/%dM/%d/%d/%d)", -				   a2dp_trx->empty_cnt, -				   a2dp_trx->retry_cnt, -				   a2dp_trx->tx_rate ? 3 : 2, -				   a2dp_trx->tx_cnt, -				   a2dp_trx->ack_cnt, -				   a2dp_trx->nack_cnt); -		} -		seq_printf(m, "->w%02d", -			   le16_to_cpu(pcysta->slot_step_time[store_index + 1])); +			p += scnprintf(p, end - p, "(%d/%d/%dM/%d/%d/%d)", +				       a2dp_trx->empty_cnt, +				       a2dp_trx->retry_cnt, +				       a2dp_trx->tx_rate ? 3 : 2, +				       a2dp_trx->tx_cnt, +				       a2dp_trx->ack_cnt, +				       a2dp_trx->nack_cnt); +		} +		p += scnprintf(p, end - p, "->w%02d", +			       le16_to_cpu(pcysta->slot_step_time[store_index + 1]));  		if (a2dp->exist) {  			a2dp_trx = &pcysta->a2dp_trx[store_index + 1]; -			seq_printf(m, "(%d/%d/%dM/%d/%d/%d)", -				   a2dp_trx->empty_cnt, -				   a2dp_trx->retry_cnt, -				   a2dp_trx->tx_rate ? 3 : 2, -				   a2dp_trx->tx_cnt, -				   a2dp_trx->ack_cnt, -				   a2dp_trx->nack_cnt); +			p += scnprintf(p, end - p, "(%d/%d/%dM/%d/%d/%d)", +				       a2dp_trx->empty_cnt, +				       a2dp_trx->retry_cnt, +				       a2dp_trx->tx_rate ? 3 : 2, +				       a2dp_trx->tx_cnt, +				       a2dp_trx->ack_cnt, +				       a2dp_trx->nack_cnt);  		}  		if (cnt % divide_cnt == 0 || cnt == c_end) -			seq_puts(m, "\n"); +			p += scnprintf(p, end - p, "\n");  	}  	if (a2dp->exist) { -		seq_printf(m, " %-15s : a2dp_ept:%d, a2dp_late:%d", -			   "[a2dp_t_sta]", -			   le16_to_cpu(pcysta->a2dp_ept.cnt), -			   le16_to_cpu(pcysta->a2dp_ept.cnt_timeout)); +		p += scnprintf(p, end - p, +			       " %-15s : a2dp_ept:%d, a2dp_late:%d", +			       "[a2dp_t_sta]", +			       le16_to_cpu(pcysta->a2dp_ept.cnt), +			       le16_to_cpu(pcysta->a2dp_ept.cnt_timeout)); -		seq_printf(m, ", avg_t:%d, max_t:%d", -			   le16_to_cpu(pcysta->a2dp_ept.tavg), -			   le16_to_cpu(pcysta->a2dp_ept.tmax)); +		p += scnprintf(p, end - p, ", avg_t:%d, max_t:%d", +			       le16_to_cpu(pcysta->a2dp_ept.tavg), +			       le16_to_cpu(pcysta->a2dp_ept.tmax)); -		seq_puts(m, "\n"); +		p += scnprintf(p, end - p, "\n");  	} + +out: +	return p - buf;  } -static void _show_fbtc_cysta_v4(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_fbtc_cysta_v4(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)  {  	struct rtw89_btc *btc = &rtwdev->btc;  	struct rtw89_btc_bt_a2dp_desc *a2dp = &btc->cx.bt.link_info.a2dp_desc; @@ -9364,62 +10085,64 @@ static void _show_fbtc_cysta_v4(struct rtw89_dev *rtwdev, struct seq_file *m)  	struct rtw89_btc_rpt_cmn_info *pcinfo;  	u8 i, cnt = 0, slot_pair, divide_cnt;  	u16 cycle, c_begin, c_end, store_index; +	char *p = buf, *end = buf + bufsz;  	pcinfo = &pfwinfo->rpt_fbtc_cysta.cinfo;  	if (!pcinfo->valid) -		return; +		return 0;  	pcysta = &pfwinfo->rpt_fbtc_cysta.finfo.v4; -	seq_printf(m, -		   " %-15s : cycle:%d, bcn[all:%d/all_ok:%d/bt:%d/bt_ok:%d]", -		   "[cycle_cnt]", -		   le16_to_cpu(pcysta->cycles), -		   le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL]), -		   le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL_OK]), -		   le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_SLOT]), -		   le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_OK])); +	p += scnprintf(p, end - p, +		       " %-15s : cycle:%d, bcn[all:%d/all_ok:%d/bt:%d/bt_ok:%d]", +		       "[cycle_cnt]", +		       le16_to_cpu(pcysta->cycles), +		       le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL]), +		       le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL_OK]), +		       le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_SLOT]), +		       le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_OK]));  	for (i = 0; i < CXST_MAX; i++) {  		if (!le16_to_cpu(pcysta->slot_cnt[i]))  			continue; -		seq_printf(m, ", %s:%d", id_to_slot(i), -			   le16_to_cpu(pcysta->slot_cnt[i])); +		p += scnprintf(p, end - p, ", %s:%d", id_to_slot(i), +			       le16_to_cpu(pcysta->slot_cnt[i]));  	}  	if (dm->tdma_now.rxflctrl) -		seq_printf(m, ", leak_rx:%d", -			   le32_to_cpu(pcysta->leak_slot.cnt_rximr)); +		p += scnprintf(p, end - p, ", leak_rx:%d", +			       le32_to_cpu(pcysta->leak_slot.cnt_rximr));  	if (pcysta->collision_cnt) -		seq_printf(m, ", collision:%d", pcysta->collision_cnt); +		p += scnprintf(p, end - p, ", collision:%d", +			       pcysta->collision_cnt);  	if (le16_to_cpu(pcysta->skip_cnt)) -		seq_printf(m, ", skip:%d", -			   le16_to_cpu(pcysta->skip_cnt)); - -	seq_puts(m, "\n"); - -	seq_printf(m, " %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]", -		   "[cycle_time]", -		   le16_to_cpu(pcysta->cycle_time.tavg[CXT_WL]), -		   le16_to_cpu(pcysta->cycle_time.tavg[CXT_BT]), -		   le16_to_cpu(pcysta->leak_slot.tavg) / 1000, -		   le16_to_cpu(pcysta->leak_slot.tavg) % 1000); -	seq_printf(m, -		   ", max_t[wl:%d/bt:%d/lk:%d.%03d]", -		   le16_to_cpu(pcysta->cycle_time.tmax[CXT_WL]), -		   le16_to_cpu(pcysta->cycle_time.tmax[CXT_BT]), -		   le16_to_cpu(pcysta->leak_slot.tmax) / 1000, -		   le16_to_cpu(pcysta->leak_slot.tmax) % 1000); -	seq_printf(m, -		   ", maxdiff_t[wl:%d/bt:%d]\n", -		   le16_to_cpu(pcysta->cycle_time.tmaxdiff[CXT_WL]), -		   le16_to_cpu(pcysta->cycle_time.tmaxdiff[CXT_BT])); +		p += scnprintf(p, end - p, ", skip:%d", +			       le16_to_cpu(pcysta->skip_cnt)); + +	p += scnprintf(p, end - p, "\n"); + +	p += scnprintf(p, end - p, " %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]", +		       "[cycle_time]", +		       le16_to_cpu(pcysta->cycle_time.tavg[CXT_WL]), +		       le16_to_cpu(pcysta->cycle_time.tavg[CXT_BT]), +		       le16_to_cpu(pcysta->leak_slot.tavg) / 1000, +		       le16_to_cpu(pcysta->leak_slot.tavg) % 1000); +	p += scnprintf(p, end - p, +		       ", max_t[wl:%d/bt:%d/lk:%d.%03d]", +		       le16_to_cpu(pcysta->cycle_time.tmax[CXT_WL]), +		       le16_to_cpu(pcysta->cycle_time.tmax[CXT_BT]), +		       le16_to_cpu(pcysta->leak_slot.tmax) / 1000, +		       le16_to_cpu(pcysta->leak_slot.tmax) % 1000); +	p += scnprintf(p, end - p, +		       ", maxdiff_t[wl:%d/bt:%d]\n", +		       le16_to_cpu(pcysta->cycle_time.tmaxdiff[CXT_WL]), +		       le16_to_cpu(pcysta->cycle_time.tmaxdiff[CXT_BT]));  	cycle = le16_to_cpu(pcysta->cycles);  	if (cycle <= 1) -		return; +		goto out;  	/* 1 cycle record 1 wl-slot and 1 bt-slot */  	slot_pair = BTC_CYCLE_SLOT_MAX / 2; @@ -9441,51 +10164,56 @@ static void _show_fbtc_cysta_v4(struct rtw89_dev *rtwdev, struct seq_file *m)  		store_index = ((cycle - 1) % slot_pair) * 2;  		if (cnt % divide_cnt == 1) -			seq_printf(m, " %-15s : ", "[cycle_step]"); +			p += scnprintf(p, end - p, " %-15s : ", +				       "[cycle_step]"); -		seq_printf(m, "->b%02d", -			   le16_to_cpu(pcysta->slot_step_time[store_index])); +		p += scnprintf(p, end - p, "->b%02d", +			       le16_to_cpu(pcysta->slot_step_time[store_index]));  		if (a2dp->exist) {  			a2dp_trx = &pcysta->a2dp_trx[store_index]; -			seq_printf(m, "(%d/%d/%dM/%d/%d/%d)", -				   a2dp_trx->empty_cnt, -				   a2dp_trx->retry_cnt, -				   a2dp_trx->tx_rate ? 3 : 2, -				   a2dp_trx->tx_cnt, -				   a2dp_trx->ack_cnt, -				   a2dp_trx->nack_cnt); -		} -		seq_printf(m, "->w%02d", -			   le16_to_cpu(pcysta->slot_step_time[store_index + 1])); +			p += scnprintf(p, end - p, "(%d/%d/%dM/%d/%d/%d)", +				       a2dp_trx->empty_cnt, +				       a2dp_trx->retry_cnt, +				       a2dp_trx->tx_rate ? 3 : 2, +				       a2dp_trx->tx_cnt, +				       a2dp_trx->ack_cnt, +				       a2dp_trx->nack_cnt); +		} +		p += scnprintf(p, end - p, "->w%02d", +			       le16_to_cpu(pcysta->slot_step_time[store_index + 1]));  		if (a2dp->exist) {  			a2dp_trx = &pcysta->a2dp_trx[store_index + 1]; -			seq_printf(m, "(%d/%d/%dM/%d/%d/%d)", -				   a2dp_trx->empty_cnt, -				   a2dp_trx->retry_cnt, -				   a2dp_trx->tx_rate ? 3 : 2, -				   a2dp_trx->tx_cnt, -				   a2dp_trx->ack_cnt, -				   a2dp_trx->nack_cnt); +			p += scnprintf(p, end - p, "(%d/%d/%dM/%d/%d/%d)", +				       a2dp_trx->empty_cnt, +				       a2dp_trx->retry_cnt, +				       a2dp_trx->tx_rate ? 3 : 2, +				       a2dp_trx->tx_cnt, +				       a2dp_trx->ack_cnt, +				       a2dp_trx->nack_cnt);  		}  		if (cnt % divide_cnt == 0 || cnt == c_end) -			seq_puts(m, "\n"); +			p += scnprintf(p, end - p, "\n");  	}  	if (a2dp->exist) { -		seq_printf(m, " %-15s : a2dp_ept:%d, a2dp_late:%d", -			   "[a2dp_t_sta]", -			   le16_to_cpu(pcysta->a2dp_ept.cnt), -			   le16_to_cpu(pcysta->a2dp_ept.cnt_timeout)); +		p += scnprintf(p, end - p, +			       " %-15s : a2dp_ept:%d, a2dp_late:%d", +			       "[a2dp_t_sta]", +			       le16_to_cpu(pcysta->a2dp_ept.cnt), +			       le16_to_cpu(pcysta->a2dp_ept.cnt_timeout)); -		seq_printf(m, ", avg_t:%d, max_t:%d", -			   le16_to_cpu(pcysta->a2dp_ept.tavg), -			   le16_to_cpu(pcysta->a2dp_ept.tmax)); +		p += scnprintf(p, end - p, ", avg_t:%d, max_t:%d", +			       le16_to_cpu(pcysta->a2dp_ept.tavg), +			       le16_to_cpu(pcysta->a2dp_ept.tmax)); -		seq_puts(m, "\n"); +		p += scnprintf(p, end - p, "\n");  	} + +out: +	return p - buf;  } -static void _show_fbtc_cysta_v5(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_fbtc_cysta_v5(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)  {  	struct rtw89_btc *btc = &rtwdev->btc;  	struct rtw89_btc_bt_a2dp_desc *a2dp = &btc->cx.bt.link_info.a2dp_desc; @@ -9496,58 +10224,60 @@ static void _show_fbtc_cysta_v5(struct rtw89_dev *rtwdev, struct seq_file *m)  	struct rtw89_btc_rpt_cmn_info *pcinfo;  	u8 i, cnt = 0, slot_pair, divide_cnt;  	u16 cycle, c_begin, c_end, store_index; +	char *p = buf, *end = buf + bufsz;  	pcinfo = &pfwinfo->rpt_fbtc_cysta.cinfo;  	if (!pcinfo->valid) -		return; +		return 0;  	pcysta = &pfwinfo->rpt_fbtc_cysta.finfo.v5; -	seq_printf(m, -		   " %-15s : cycle:%d, bcn[all:%d/all_ok:%d/bt:%d/bt_ok:%d]", -		   "[cycle_cnt]", -		   le16_to_cpu(pcysta->cycles), -		   le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL]), -		   le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL_OK]), -		   le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_SLOT]), -		   le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_OK])); +	p += scnprintf(p, end - p, +		       " %-15s : cycle:%d, bcn[all:%d/all_ok:%d/bt:%d/bt_ok:%d]", +		       "[cycle_cnt]", +		       le16_to_cpu(pcysta->cycles), +		       le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL]), +		       le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL_OK]), +		       le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_SLOT]), +		       le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_OK]));  	for (i = 0; i < CXST_MAX; i++) {  		if (!le16_to_cpu(pcysta->slot_cnt[i]))  			continue; -		seq_printf(m, ", %s:%d", id_to_slot(i), -			   le16_to_cpu(pcysta->slot_cnt[i])); +		p += scnprintf(p, end - p, ", %s:%d", id_to_slot(i), +			       le16_to_cpu(pcysta->slot_cnt[i]));  	}  	if (dm->tdma_now.rxflctrl) -		seq_printf(m, ", leak_rx:%d", -			   le32_to_cpu(pcysta->leak_slot.cnt_rximr)); +		p += scnprintf(p, end - p, ", leak_rx:%d", +			       le32_to_cpu(pcysta->leak_slot.cnt_rximr));  	if (pcysta->collision_cnt) -		seq_printf(m, ", collision:%d", pcysta->collision_cnt); +		p += scnprintf(p, end - p, ", collision:%d", +			       pcysta->collision_cnt);  	if (le16_to_cpu(pcysta->skip_cnt)) -		seq_printf(m, ", skip:%d", -			   le16_to_cpu(pcysta->skip_cnt)); - -	seq_puts(m, "\n"); - -	seq_printf(m, " %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]", -		   "[cycle_time]", -		   le16_to_cpu(pcysta->cycle_time.tavg[CXT_WL]), -		   le16_to_cpu(pcysta->cycle_time.tavg[CXT_BT]), -		   le16_to_cpu(pcysta->leak_slot.tavg) / 1000, -		   le16_to_cpu(pcysta->leak_slot.tavg) % 1000); -	seq_printf(m, -		   ", max_t[wl:%d/bt:%d/lk:%d.%03d]\n", -		   le16_to_cpu(pcysta->cycle_time.tmax[CXT_WL]), -		   le16_to_cpu(pcysta->cycle_time.tmax[CXT_BT]), -		   le16_to_cpu(pcysta->leak_slot.tmax) / 1000, -		   le16_to_cpu(pcysta->leak_slot.tmax) % 1000); +		p += scnprintf(p, end - p, ", skip:%d", +			       le16_to_cpu(pcysta->skip_cnt)); + +	p += scnprintf(p, end - p, "\n"); + +	p += scnprintf(p, end - p, " %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]", +		       "[cycle_time]", +		       le16_to_cpu(pcysta->cycle_time.tavg[CXT_WL]), +		       le16_to_cpu(pcysta->cycle_time.tavg[CXT_BT]), +		       le16_to_cpu(pcysta->leak_slot.tavg) / 1000, +		       le16_to_cpu(pcysta->leak_slot.tavg) % 1000); +	p += scnprintf(p, end - p, +		       ", max_t[wl:%d/bt:%d/lk:%d.%03d]\n", +		       le16_to_cpu(pcysta->cycle_time.tmax[CXT_WL]), +		       le16_to_cpu(pcysta->cycle_time.tmax[CXT_BT]), +		       le16_to_cpu(pcysta->leak_slot.tmax) / 1000, +		       le16_to_cpu(pcysta->leak_slot.tmax) % 1000);  	cycle = le16_to_cpu(pcysta->cycles);  	if (cycle <= 1) -		return; +		goto out;  	/* 1 cycle record 1 wl-slot and 1 bt-slot */  	slot_pair = BTC_CYCLE_SLOT_MAX / 2; @@ -9565,58 +10295,63 @@ static void _show_fbtc_cysta_v5(struct rtw89_dev *rtwdev, struct seq_file *m)  		divide_cnt = BTC_CYCLE_SLOT_MAX / 4;  	if (c_begin > c_end) -		return; +		goto out;  	for (cycle = c_begin; cycle <= c_end; cycle++) {  		cnt++;  		store_index = ((cycle - 1) % slot_pair) * 2;  		if (cnt % divide_cnt == 1) -			seq_printf(m, " %-15s : ", "[cycle_step]"); +			p += scnprintf(p, end - p, " %-15s : ", +				       "[cycle_step]"); -		seq_printf(m, "->b%02d", -			   le16_to_cpu(pcysta->slot_step_time[store_index])); +		p += scnprintf(p, end - p, "->b%02d", +			       le16_to_cpu(pcysta->slot_step_time[store_index]));  		if (a2dp->exist) {  			a2dp_trx = &pcysta->a2dp_trx[store_index]; -			seq_printf(m, "(%d/%d/%dM/%d/%d/%d)", -				   a2dp_trx->empty_cnt, -				   a2dp_trx->retry_cnt, -				   a2dp_trx->tx_rate ? 3 : 2, -				   a2dp_trx->tx_cnt, -				   a2dp_trx->ack_cnt, -				   a2dp_trx->nack_cnt); -		} -		seq_printf(m, "->w%02d", -			   le16_to_cpu(pcysta->slot_step_time[store_index + 1])); +			p += scnprintf(p, end - p, "(%d/%d/%dM/%d/%d/%d)", +				       a2dp_trx->empty_cnt, +				       a2dp_trx->retry_cnt, +				       a2dp_trx->tx_rate ? 3 : 2, +				       a2dp_trx->tx_cnt, +				       a2dp_trx->ack_cnt, +				       a2dp_trx->nack_cnt); +		} +		p += scnprintf(p, end - p, "->w%02d", +			       le16_to_cpu(pcysta->slot_step_time[store_index + 1]));  		if (a2dp->exist) {  			a2dp_trx = &pcysta->a2dp_trx[store_index + 1]; -			seq_printf(m, "(%d/%d/%dM/%d/%d/%d)", -				   a2dp_trx->empty_cnt, -				   a2dp_trx->retry_cnt, -				   a2dp_trx->tx_rate ? 3 : 2, -				   a2dp_trx->tx_cnt, -				   a2dp_trx->ack_cnt, -				   a2dp_trx->nack_cnt); +			p += scnprintf(p, end - p, "(%d/%d/%dM/%d/%d/%d)", +				       a2dp_trx->empty_cnt, +				       a2dp_trx->retry_cnt, +				       a2dp_trx->tx_rate ? 3 : 2, +				       a2dp_trx->tx_cnt, +				       a2dp_trx->ack_cnt, +				       a2dp_trx->nack_cnt);  		}  		if (cnt % divide_cnt == 0 || cnt == c_end) -			seq_puts(m, "\n"); +			p += scnprintf(p, end - p, "\n");  	}  	if (a2dp->exist) { -		seq_printf(m, " %-15s : a2dp_ept:%d, a2dp_late:%d", -			   "[a2dp_t_sta]", -			   le16_to_cpu(pcysta->a2dp_ept.cnt), -			   le16_to_cpu(pcysta->a2dp_ept.cnt_timeout)); +		p += scnprintf(p, end - p, +			       " %-15s : a2dp_ept:%d, a2dp_late:%d", +			       "[a2dp_t_sta]", +			       le16_to_cpu(pcysta->a2dp_ept.cnt), +			       le16_to_cpu(pcysta->a2dp_ept.cnt_timeout)); -		seq_printf(m, ", avg_t:%d, max_t:%d", -			   le16_to_cpu(pcysta->a2dp_ept.tavg), -			   le16_to_cpu(pcysta->a2dp_ept.tmax)); +		p += scnprintf(p, end - p, ", avg_t:%d, max_t:%d", +			       le16_to_cpu(pcysta->a2dp_ept.tavg), +			       le16_to_cpu(pcysta->a2dp_ept.tmax)); -		seq_puts(m, "\n"); +		p += scnprintf(p, end - p, "\n");  	} + +out: +	return p - buf;  } -static void _show_fbtc_cysta_v7(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_fbtc_cysta_v7(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)  {  	struct rtw89_btc_bt_info *bt = &rtwdev->btc.cx.bt;  	struct rtw89_btc_bt_a2dp_desc *a2dp = &bt->link_info.a2dp_desc; @@ -9624,68 +10359,75 @@ static void _show_fbtc_cysta_v7(struct rtw89_dev *rtwdev, struct seq_file *m)  	struct rtw89_btc_fbtc_cysta_v7 *pcysta = NULL;  	struct rtw89_btc_dm *dm = &rtwdev->btc.dm;  	struct rtw89_btc_rpt_cmn_info *pcinfo; +	char *p = buf, *end = buf + bufsz;  	u16 cycle, c_begin, c_end, s_id;  	u8 i, cnt = 0, divide_cnt;  	u8 slot_pair;  	pcinfo = &pfwinfo->rpt_fbtc_cysta.cinfo;  	if (!pcinfo->valid) -		return; +		return 0;  	pcysta = &pfwinfo->rpt_fbtc_cysta.finfo.v7; -	seq_printf(m, "\n\r %-15s : cycle:%d", "[slot_stat]", -		   le16_to_cpu(pcysta->cycles)); +	p += scnprintf(p, end - p, "\n %-15s : cycle:%d", "[slot_stat]", +		       le16_to_cpu(pcysta->cycles));  	for (i = 0; i < CXST_MAX; i++) {  		if (!le16_to_cpu(pcysta->slot_cnt[i]))  			continue; -		seq_printf(m, ", %s:%d", -			   id_to_slot(i), le16_to_cpu(pcysta->slot_cnt[i])); +		p += scnprintf(p, end - p, ", %s:%d", +			       id_to_slot(i), +			       le16_to_cpu(pcysta->slot_cnt[i]));  	}  	if (dm->tdma_now.rxflctrl) -		seq_printf(m, ", leak_rx:%d", -			   le32_to_cpu(pcysta->leak_slot.cnt_rximr)); +		p += scnprintf(p, end - p, ", leak_rx:%d", +			       le32_to_cpu(pcysta->leak_slot.cnt_rximr));  	if (pcysta->collision_cnt) -		seq_printf(m, ", collision:%d", pcysta->collision_cnt); +		p += scnprintf(p, end - p, ", collision:%d", +			       pcysta->collision_cnt);  	if (pcysta->skip_cnt) -		seq_printf(m, ", skip:%d", le16_to_cpu(pcysta->skip_cnt)); - -	seq_printf(m, "\n\r %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]", -		   "[cycle_stat]", -		   le16_to_cpu(pcysta->cycle_time.tavg[CXT_WL]), -		   le16_to_cpu(pcysta->cycle_time.tavg[CXT_BT]), -		   le16_to_cpu(pcysta->leak_slot.tavg) / 1000, -		   le16_to_cpu(pcysta->leak_slot.tavg) % 1000); -	seq_printf(m, ", max_t[wl:%d/bt:%d(>%dms:%d)/lk:%d.%03d]", -		   le16_to_cpu(pcysta->cycle_time.tmax[CXT_WL]), -		   le16_to_cpu(pcysta->cycle_time.tmax[CXT_BT]), -		   dm->bt_slot_flood, dm->cnt_dm[BTC_DCNT_BT_SLOT_FLOOD], -		   le16_to_cpu(pcysta->leak_slot.tamx) / 1000, -		   le16_to_cpu(pcysta->leak_slot.tamx) % 1000); -	seq_printf(m, ", bcn[all:%d/ok:%d/in_bt:%d/in_bt_ok:%d]", -		   le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL]), -		   le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL_OK]), -		   le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_SLOT]), -		   le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_OK])); +		p += scnprintf(p, end - p, ", skip:%d", +			       le16_to_cpu(pcysta->skip_cnt)); + +	p += scnprintf(p, end - p, +		       "\n\r %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]", +		       "[cycle_stat]", +		       le16_to_cpu(pcysta->cycle_time.tavg[CXT_WL]), +		       le16_to_cpu(pcysta->cycle_time.tavg[CXT_BT]), +		       le16_to_cpu(pcysta->leak_slot.tavg) / 1000, +		       le16_to_cpu(pcysta->leak_slot.tavg) % 1000); +	p += scnprintf(p, end - p, +		       ", max_t[wl:%d/bt:%d(>%dms:%d)/lk:%d.%03d]", +		       le16_to_cpu(pcysta->cycle_time.tmax[CXT_WL]), +		       le16_to_cpu(pcysta->cycle_time.tmax[CXT_BT]), +		       dm->bt_slot_flood, dm->cnt_dm[BTC_DCNT_BT_SLOT_FLOOD], +		       le16_to_cpu(pcysta->leak_slot.tamx) / 1000, +		       le16_to_cpu(pcysta->leak_slot.tamx) % 1000); +	p += scnprintf(p, end - p, ", bcn[all:%d/ok:%d/in_bt:%d/in_bt_ok:%d]", +		       le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL]), +		       le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL_OK]), +		       le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_SLOT]), +		       le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_OK]));  	if (a2dp->exist) { -		seq_printf(m, -			   "\n\r %-15s : a2dp_ept:%d, a2dp_late:%d(streak 2S:%d/max:%d)", -			   "[a2dp_stat]", -			   le16_to_cpu(pcysta->a2dp_ept.cnt), -			   le16_to_cpu(pcysta->a2dp_ept.cnt_timeout), -			   a2dp->no_empty_streak_2s, a2dp->no_empty_streak_max); +		p += scnprintf(p, end - p, +			       "\n\r %-15s : a2dp_ept:%d, a2dp_late:%d(streak 2S:%d/max:%d)", +			       "[a2dp_stat]", +			       le16_to_cpu(pcysta->a2dp_ept.cnt), +			       le16_to_cpu(pcysta->a2dp_ept.cnt_timeout), +			       a2dp->no_empty_streak_2s, +			       a2dp->no_empty_streak_max); -		seq_printf(m, ", avg_t:%d, max_t:%d", -			   le16_to_cpu(pcysta->a2dp_ept.tavg), -			   le16_to_cpu(pcysta->a2dp_ept.tmax)); +		p += scnprintf(p, end - p, ", avg_t:%d, max_t:%d", +			       le16_to_cpu(pcysta->a2dp_ept.tavg), +			       le16_to_cpu(pcysta->a2dp_ept.tmax));  	}  	if (le16_to_cpu(pcysta->cycles) <= 1) -		return; +		goto out;  	/* 1 cycle = 1 wl-slot + 1 bt-slot */  	slot_pair = BTC_CYCLE_SLOT_MAX / 2; @@ -9703,7 +10445,7 @@ static void _show_fbtc_cysta_v7(struct rtw89_dev *rtwdev, struct seq_file *m)  		divide_cnt = 6;  	if (c_begin > c_end) -		return; +		goto out;  	for (cycle = c_begin; cycle <= c_end; cycle++) {  		cnt++; @@ -9711,129 +10453,142 @@ static void _show_fbtc_cysta_v7(struct rtw89_dev *rtwdev, struct seq_file *m)  		if (cnt % divide_cnt == 1) {  			if (a2dp->exist) -				seq_printf(m, "\n\r %-15s : ", "[slotT_wermtan]"); +				p += scnprintf(p, end - p, "\n\r %-15s : ", +					       "[slotT_wermtan]");  			else -				seq_printf(m, "\n\r %-15s : ", "[slotT_rxerr]"); +				p += scnprintf(p, end - p, "\n\r %-15s : ", +					       "[slotT_rxerr]");  		} -		seq_printf(m, "->b%d", le16_to_cpu(pcysta->slot_step_time[s_id])); +		p += scnprintf(p, end - p, "->b%d", +			       le16_to_cpu(pcysta->slot_step_time[s_id]));  		if (a2dp->exist) -			seq_printf(m, "(%d/%d/%d/%dM/%d/%d/%d)", -				   pcysta->wl_rx_err_ratio[s_id], -				   pcysta->a2dp_trx[s_id].empty_cnt, -				   pcysta->a2dp_trx[s_id].retry_cnt, -				   (pcysta->a2dp_trx[s_id].tx_rate ? 3 : 2), -				   pcysta->a2dp_trx[s_id].tx_cnt, -				   pcysta->a2dp_trx[s_id].ack_cnt, -				   pcysta->a2dp_trx[s_id].nack_cnt); +			p += scnprintf(p, end - p, "(%d/%d/%d/%dM/%d/%d/%d)", +				       pcysta->wl_rx_err_ratio[s_id], +				       pcysta->a2dp_trx[s_id].empty_cnt, +				       pcysta->a2dp_trx[s_id].retry_cnt, +				       (pcysta->a2dp_trx[s_id].tx_rate ? 3 : 2), +				       pcysta->a2dp_trx[s_id].tx_cnt, +				       pcysta->a2dp_trx[s_id].ack_cnt, +				       pcysta->a2dp_trx[s_id].nack_cnt);  		else -			seq_printf(m, "(%d)", pcysta->wl_rx_err_ratio[s_id]); +			p += scnprintf(p, end - p, "(%d)", +				       pcysta->wl_rx_err_ratio[s_id]); -		seq_printf(m, "->w%d", le16_to_cpu(pcysta->slot_step_time[s_id + 1])); +		p += scnprintf(p, end - p, "->w%d", +			       le16_to_cpu(pcysta->slot_step_time[s_id + 1]));  		if (a2dp->exist) -			seq_printf(m, "(%d/%d/%d/%dM/%d/%d/%d)", -				   pcysta->wl_rx_err_ratio[s_id + 1], -				   pcysta->a2dp_trx[s_id + 1].empty_cnt, -				   pcysta->a2dp_trx[s_id + 1].retry_cnt, -				   (pcysta->a2dp_trx[s_id + 1].tx_rate ? 3 : 2), -				   pcysta->a2dp_trx[s_id + 1].tx_cnt, -				   pcysta->a2dp_trx[s_id + 1].ack_cnt, -				   pcysta->a2dp_trx[s_id + 1].nack_cnt); +			p += scnprintf(p, end - p, "(%d/%d/%d/%dM/%d/%d/%d)", +				       pcysta->wl_rx_err_ratio[s_id + 1], +				       pcysta->a2dp_trx[s_id + 1].empty_cnt, +				       pcysta->a2dp_trx[s_id + 1].retry_cnt, +				       (pcysta->a2dp_trx[s_id + 1].tx_rate ? 3 : 2), +				       pcysta->a2dp_trx[s_id + 1].tx_cnt, +				       pcysta->a2dp_trx[s_id + 1].ack_cnt, +				       pcysta->a2dp_trx[s_id + 1].nack_cnt);  		else -			seq_printf(m, "(%d)", pcysta->wl_rx_err_ratio[s_id + 1]); +			p += scnprintf(p, end - p, "(%d)", +				       pcysta->wl_rx_err_ratio[s_id + 1]);  	} + +out: +	return p - buf;  } -static void _show_fbtc_nullsta(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_fbtc_nullsta(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)  {  	struct rtw89_btc *btc = &rtwdev->btc;  	const struct rtw89_btc_ver *ver = btc->ver;  	struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo;  	struct rtw89_btc_rpt_cmn_info *pcinfo;  	union rtw89_btc_fbtc_cynullsta_info *ns; +	char *p = buf, *end = buf + bufsz;  	u8 i = 0;  	if (!btc->dm.tdma_now.rxflctrl) -		return; +		return 0;  	pcinfo = &pfwinfo->rpt_fbtc_nullsta.cinfo;  	if (!pcinfo->valid) -		return; +		return 0;  	ns = &pfwinfo->rpt_fbtc_nullsta.finfo;  	if (ver->fcxnullsta == 1) {  		for (i = 0; i < 2; i++) { -			seq_printf(m, " %-15s : ", "[NULL-STA]"); -			seq_printf(m, "null-%d", i); -			seq_printf(m, "[ok:%d/", -				   le32_to_cpu(ns->v1.result[i][1])); -			seq_printf(m, "fail:%d/", -				   le32_to_cpu(ns->v1.result[i][0])); -			seq_printf(m, "on_time:%d/", -				   le32_to_cpu(ns->v1.result[i][2])); -			seq_printf(m, "retry:%d/", -				   le32_to_cpu(ns->v1.result[i][3])); -			seq_printf(m, "avg_t:%d.%03d/", -				   le32_to_cpu(ns->v1.avg_t[i]) / 1000, -				   le32_to_cpu(ns->v1.avg_t[i]) % 1000); -			seq_printf(m, "max_t:%d.%03d]\n", -				   le32_to_cpu(ns->v1.max_t[i]) / 1000, -				   le32_to_cpu(ns->v1.max_t[i]) % 1000); +			p += scnprintf(p, end - p, " %-15s : ", "\n[NULL-STA]"); +			p += scnprintf(p, end - p, "null-%d", i); +			p += scnprintf(p, end - p, "[ok:%d/", +				       le32_to_cpu(ns->v1.result[i][1])); +			p += scnprintf(p, end - p, "fail:%d/", +				       le32_to_cpu(ns->v1.result[i][0])); +			p += scnprintf(p, end - p, "on_time:%d/", +				       le32_to_cpu(ns->v1.result[i][2])); +			p += scnprintf(p, end - p, "retry:%d/", +				       le32_to_cpu(ns->v1.result[i][3])); +			p += scnprintf(p, end - p, "avg_t:%d.%03d/", +				       le32_to_cpu(ns->v1.avg_t[i]) / 1000, +				       le32_to_cpu(ns->v1.avg_t[i]) % 1000); +			p += scnprintf(p, end - p, "max_t:%d.%03d]", +				       le32_to_cpu(ns->v1.max_t[i]) / 1000, +				       le32_to_cpu(ns->v1.max_t[i]) % 1000);  		}  	} else if (ver->fcxnullsta == 7) {  		for (i = 0; i < 2; i++) { -			seq_printf(m, " %-15s : ", "[NULL-STA]"); -			seq_printf(m, "null-%d", i); -			seq_printf(m, "[Tx:%d/", -				   le32_to_cpu(ns->v7.result[i][4])); -			seq_printf(m, "[ok:%d/", -				   le32_to_cpu(ns->v7.result[i][1])); -			seq_printf(m, "fail:%d/", -				   le32_to_cpu(ns->v7.result[i][0])); -			seq_printf(m, "on_time:%d/", -				   le32_to_cpu(ns->v7.result[i][2])); -			seq_printf(m, "retry:%d/", -				   le32_to_cpu(ns->v7.result[i][3])); -			seq_printf(m, "avg_t:%d.%03d/", -				   le32_to_cpu(ns->v7.tavg[i]) / 1000, -				   le32_to_cpu(ns->v7.tavg[i]) % 1000); -			seq_printf(m, "max_t:%d.%03d]\n", -				   le32_to_cpu(ns->v7.tmax[i]) / 1000, -				   le32_to_cpu(ns->v7.tmax[i]) % 1000); +			p += scnprintf(p, end - p, " %-15s : ", "\n[NULL-STA]"); +			p += scnprintf(p, end - p, "null-%d", i); +			p += scnprintf(p, end - p, "[Tx:%d/", +				       le32_to_cpu(ns->v7.result[i][4])); +			p += scnprintf(p, end - p, "[ok:%d/", +				       le32_to_cpu(ns->v7.result[i][1])); +			p += scnprintf(p, end - p, "fail:%d/", +				       le32_to_cpu(ns->v7.result[i][0])); +			p += scnprintf(p, end - p, "on_time:%d/", +				       le32_to_cpu(ns->v7.result[i][2])); +			p += scnprintf(p, end - p, "retry:%d/", +				       le32_to_cpu(ns->v7.result[i][3])); +			p += scnprintf(p, end - p, "avg_t:%d.%03d/", +				       le32_to_cpu(ns->v7.tavg[i]) / 1000, +				       le32_to_cpu(ns->v7.tavg[i]) % 1000); +			p += scnprintf(p, end - p, "max_t:%d.%03d]", +				       le32_to_cpu(ns->v7.tmax[i]) / 1000, +				       le32_to_cpu(ns->v7.tmax[i]) % 1000);  		}  	} else {  		for (i = 0; i < 2; i++) { -			seq_printf(m, " %-15s : ", "[NULL-STA]"); -			seq_printf(m, "null-%d", i); -			seq_printf(m, "[Tx:%d/", -				   le32_to_cpu(ns->v2.result[i][4])); -			seq_printf(m, "[ok:%d/", -				   le32_to_cpu(ns->v2.result[i][1])); -			seq_printf(m, "fail:%d/", -				   le32_to_cpu(ns->v2.result[i][0])); -			seq_printf(m, "on_time:%d/", -				   le32_to_cpu(ns->v2.result[i][2])); -			seq_printf(m, "retry:%d/", -				   le32_to_cpu(ns->v2.result[i][3])); -			seq_printf(m, "avg_t:%d.%03d/", -				   le32_to_cpu(ns->v2.avg_t[i]) / 1000, -				   le32_to_cpu(ns->v2.avg_t[i]) % 1000); -			seq_printf(m, "max_t:%d.%03d]\n", -				   le32_to_cpu(ns->v2.max_t[i]) / 1000, -				   le32_to_cpu(ns->v2.max_t[i]) % 1000); -		} -	} -} - -static void _show_fbtc_step_v2(struct rtw89_dev *rtwdev, struct seq_file *m) +			p += scnprintf(p, end - p, " %-15s : ", "\n[NULL-STA]"); +			p += scnprintf(p, end - p, "null-%d", i); +			p += scnprintf(p, end - p, "[Tx:%d/", +				       le32_to_cpu(ns->v2.result[i][4])); +			p += scnprintf(p, end - p, "[ok:%d/", +				       le32_to_cpu(ns->v2.result[i][1])); +			p += scnprintf(p, end - p, "fail:%d/", +				       le32_to_cpu(ns->v2.result[i][0])); +			p += scnprintf(p, end - p, "on_time:%d/", +				       le32_to_cpu(ns->v2.result[i][2])); +			p += scnprintf(p, end - p, "retry:%d/", +				       le32_to_cpu(ns->v2.result[i][3])); +			p += scnprintf(p, end - p, "avg_t:%d.%03d/", +				       le32_to_cpu(ns->v2.avg_t[i]) / 1000, +				       le32_to_cpu(ns->v2.avg_t[i]) % 1000); +			p += scnprintf(p, end - p, "max_t:%d.%03d]", +				       le32_to_cpu(ns->v2.max_t[i]) / 1000, +				       le32_to_cpu(ns->v2.max_t[i]) % 1000); +		} +	} + +	return p - buf; +} + +static int _show_fbtc_step_v2(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)  {  	struct rtw89_btc *btc = &rtwdev->btc;  	struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo;  	struct rtw89_btc_rpt_cmn_info *pcinfo = NULL;  	struct rtw89_btc_fbtc_steps_v2 *pstep = NULL;  	const struct rtw89_btc_ver *ver = btc->ver; +	char *p = buf, *end = buf + bufsz;  	u8 type, val, cnt = 0, state = 0;  	bool outloop = false;  	u16 i, diff_t, n_start = 0, n_stop = 0; @@ -9841,14 +10596,14 @@ static void _show_fbtc_step_v2(struct rtw89_dev *rtwdev, struct seq_file *m)  	pcinfo = &pfwinfo->rpt_fbtc_step.cinfo;  	if (!pcinfo->valid) -		return; +		return 0;  	pstep = &pfwinfo->rpt_fbtc_step.finfo.v2;  	pos_old = le16_to_cpu(pstep->pos_old);  	pos_new = le16_to_cpu(pstep->pos_new);  	if (pcinfo->req_fver != pstep->fver) -		return; +		return 0;  	/* store step info by using ring instead of FIFO*/  	do { @@ -9877,13 +10632,15 @@ static void _show_fbtc_step_v2(struct rtw89_dev *rtwdev, struct seq_file *m)  					continue;  				if (cnt % 10 == 0) -					seq_printf(m, " %-15s : ", "[steps]"); +					p += scnprintf(p, end - p, +						       " %-15s : ", "[steps]"); -				seq_printf(m, "-> %s(%02d)(%02d)", -					   (type == CXSTEP_SLOT ? "SLT" : -					    "EVT"), (u32)val, diff_t); +				p += scnprintf(p, end - p, +					       "-> %s(%02d)(%02d)", +					       (type == CXSTEP_SLOT ? "SLT" : +						"EVT"), (u32)val, diff_t);  				if (cnt % 10 == 9) -					seq_puts(m, "\n"); +					p += scnprintf(p, end - p, "\n");  				cnt++;  			} @@ -9900,29 +10657,32 @@ static void _show_fbtc_step_v2(struct rtw89_dev *rtwdev, struct seq_file *m)  			break;  		}  	} while (!outloop); + +	return p - buf;  } -static void _show_fbtc_step_v3(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_fbtc_step_v3(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)  {  	struct rtw89_btc *btc = &rtwdev->btc;  	struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo;  	struct rtw89_btc_rpt_cmn_info *pcinfo;  	struct rtw89_btc_fbtc_steps_v3 *pstep;  	u32 i, n_begin, n_end, array_idx, cnt = 0; +	char *p = buf, *end = buf + bufsz;  	u8 type, val;  	u16 diff_t;  	if ((pfwinfo->rpt_en_map &  	     rtw89_btc_fw_rpt_ver(rtwdev, RPT_EN_FW_STEP_INFO)) == 0) -		return; +		return 0;  	pcinfo = &pfwinfo->rpt_fbtc_step.cinfo;  	if (!pcinfo->valid) -		return; +		return 0;  	pstep = &pfwinfo->rpt_fbtc_step.finfo.v3;  	if (pcinfo->req_fver != pstep->fver) -		return; +		return 0;  	if (le32_to_cpu(pstep->cnt) <= FCXDEF_STEP)  		n_begin = 1; @@ -9932,7 +10692,7 @@ static void _show_fbtc_step_v3(struct rtw89_dev *rtwdev, struct seq_file *m)  	n_end = le32_to_cpu(pstep->cnt);  	if (n_begin > n_end) -		return; +		return 0;  	/* restore step info by using ring instead of FIFO */  	for (i = n_begin; i <= n_end; i++) { @@ -9945,50 +10705,55 @@ static void _show_fbtc_step_v3(struct rtw89_dev *rtwdev, struct seq_file *m)  			continue;  		if (cnt % 10 == 0) -			seq_printf(m, " %-15s : ", "[steps]"); +			p += scnprintf(p, end - p, " %-15s : ", "[steps]"); -		seq_printf(m, "-> %s(%02d)", -			   (type == CXSTEP_SLOT ? -			    id_to_slot((u32)val) : -			    id_to_evt((u32)val)), diff_t); +		p += scnprintf(p, end - p, "-> %s(%02d)", +			       (type == CXSTEP_SLOT ? +				id_to_slot((u32)val) : +				id_to_evt((u32)val)), diff_t);  		if (cnt % 10 == 9) -			seq_puts(m, "\n"); +			p += scnprintf(p, end - p, "\n");  		cnt++;  	} + +	return p - buf;  } -static void _show_fw_dm_msg(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_fw_dm_msg(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)  {  	struct rtw89_btc *btc = &rtwdev->btc;  	const struct rtw89_btc_ver *ver = btc->ver; +	char *p = buf, *end = buf + bufsz;  	if (!(btc->dm.coex_info_map & BTC_COEX_INFO_DM)) -		return; +		goto out; -	_show_error(rtwdev, m); -	_show_fbtc_tdma(rtwdev, m); -	_show_fbtc_slots(rtwdev, m); +	p += _show_error(rtwdev, p, end - p); +	p += _show_fbtc_tdma(rtwdev, p, end - p); +	p += _show_fbtc_slots(rtwdev, p, end - p);  	if (ver->fcxcysta == 2) -		_show_fbtc_cysta_v2(rtwdev, m); +		p += _show_fbtc_cysta_v2(rtwdev, p, end - p);  	else if (ver->fcxcysta == 3) -		_show_fbtc_cysta_v3(rtwdev, m); +		p += _show_fbtc_cysta_v3(rtwdev, p, end - p);  	else if (ver->fcxcysta == 4) -		_show_fbtc_cysta_v4(rtwdev, m); +		p += _show_fbtc_cysta_v4(rtwdev, p, end - p);  	else if (ver->fcxcysta == 5) -		_show_fbtc_cysta_v5(rtwdev, m); +		p += _show_fbtc_cysta_v5(rtwdev, p, end - p);  	else if (ver->fcxcysta == 7) -		_show_fbtc_cysta_v7(rtwdev, m); +		p += _show_fbtc_cysta_v7(rtwdev, p, end - p); -	_show_fbtc_nullsta(rtwdev, m); +	p += _show_fbtc_nullsta(rtwdev, p, end - p);  	if (ver->fcxstep == 2) -		_show_fbtc_step_v2(rtwdev, m); +		p += _show_fbtc_step_v2(rtwdev, p, end - p);  	else if (ver->fcxstep == 3) -		_show_fbtc_step_v3(rtwdev, m); +		p += _show_fbtc_step_v3(rtwdev, p, end - p); +out: +	return p - buf;  }  static void _get_gnt(struct rtw89_dev *rtwdev, struct rtw89_mac_ax_coex_gnt *gnt_cfg) @@ -10033,12 +10798,13 @@ static void _get_gnt(struct rtw89_dev *rtwdev, struct rtw89_mac_ax_coex_gnt *gnt  	}  } -static void _show_gpio_dbg(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_gpio_dbg(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)  {  	struct rtw89_btc_btf_fwinfo *pfwinfo = &rtwdev->btc.fwinfo;  	const struct rtw89_btc_ver *ver = rtwdev->btc.ver;  	struct rtw89_btc_rpt_cmn_info *pcinfo = NULL;  	union rtw89_btc_fbtc_gpio_dbg *gdbg = NULL; +	char *p = buf, *end = buf + bufsz;  	u8 *gpio_map, i;  	u32 en_map; @@ -10048,8 +10814,7 @@ static void _show_gpio_dbg(struct rtw89_dev *rtwdev, struct seq_file *m)  		rtw89_debug(rtwdev, RTW89_DBG_BTC,  			    "[BTC], %s(): stop due rpt_fbtc_gpio_dbg.cinfo\n",  			    __func__); -		seq_puts(m, "\n"); -		return; +		goto out;  	}  	if (ver->fcxgpiodbg == 7) { @@ -10061,20 +10826,24 @@ static void _show_gpio_dbg(struct rtw89_dev *rtwdev, struct seq_file *m)  	}  	if (!en_map) -		return; +		goto out; -	seq_printf(m, " %-15s : enable_map:0x%08x", -		   "[gpio_dbg]", en_map); +	p += scnprintf(p, end - p, " %-15s : enable_map:0x%08x", +		       "[gpio_dbg]", en_map);  	for (i = 0; i < BTC_DBG_MAX1; i++) {  		if (!(en_map & BIT(i)))  			continue; -		seq_printf(m, ", %s->GPIO%d", id_to_gdbg(i), gpio_map[i]); +		p += scnprintf(p, end - p, ", %s->GPIO%d", id_to_gdbg(i), +			       gpio_map[i]);  	} -	seq_puts(m, "\n"); +	p += scnprintf(p, end - p, "\n"); + +out: +	return p - buf;  } -static void _show_mreg_v1(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_mreg_v1(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)  {  	const struct rtw89_chip_info *chip = rtwdev->chip;  	struct rtw89_btc *btc = &rtwdev->btc; @@ -10086,45 +10855,47 @@ static void _show_mreg_v1(struct rtw89_dev *rtwdev, struct seq_file *m)  	struct rtw89_btc_bt_info *bt = &btc->cx.bt;  	struct rtw89_mac_ax_coex_gnt gnt_cfg = {};  	struct rtw89_mac_ax_gnt gnt; +	char *p = buf, *end = buf + bufsz;  	u8 i = 0, type = 0, cnt = 0;  	u32 val, offset;  	if (!(btc->dm.coex_info_map & BTC_COEX_INFO_MREG)) -		return; +		return 0; -	seq_puts(m, "========== [HW Status] ==========\n"); +	p += scnprintf(p, end - p, "========== [HW Status] ==========\n"); -	seq_printf(m, -		   " %-15s : WL->BT:0x%08x(cnt:%d), BT->WL:0x%08x(total:%d, bt_update:%d)\n", -		   "[scoreboard]", wl->scbd, cx->cnt_wl[BTC_WCNT_SCBDUPDATE], -		   bt->scbd, cx->cnt_bt[BTC_BCNT_SCBDREAD], -		   cx->cnt_bt[BTC_BCNT_SCBDUPDATE]); +	p += scnprintf(p, end - p, +		       " %-15s : WL->BT:0x%08x(cnt:%d), BT->WL:0x%08x(total:%d, bt_update:%d)\n", +		       "[scoreboard]", wl->scbd, +		       cx->cnt_wl[BTC_WCNT_SCBDUPDATE], +		       bt->scbd, cx->cnt_bt[BTC_BCNT_SCBDREAD], +		       cx->cnt_bt[BTC_BCNT_SCBDUPDATE]);  	btc->dm.pta_owner = rtw89_mac_get_ctrl_path(rtwdev);  	_get_gnt(rtwdev, &gnt_cfg);  	gnt = gnt_cfg.band[0]; -	seq_printf(m, -		   " %-15s : pta_owner:%s, phy-0[gnt_wl:%s-%d/gnt_bt:%s-%d], ", -		   "[gnt_status]", -		   chip->chip_id == RTL8852C ? "HW" : -		   btc->dm.pta_owner == BTC_CTRL_BY_WL ? "WL" : "BT", -		   gnt.gnt_wl_sw_en ? "SW" : "HW", gnt.gnt_wl, -		   gnt.gnt_bt_sw_en ? "SW" : "HW", gnt.gnt_bt); +	p += scnprintf(p, end - p, +		       " %-15s : pta_owner:%s, phy-0[gnt_wl:%s-%d/gnt_bt:%s-%d], ", +		       "[gnt_status]", +		       chip->chip_id == RTL8852C ? "HW" : +		       btc->dm.pta_owner == BTC_CTRL_BY_WL ? "WL" : "BT", +		       gnt.gnt_wl_sw_en ? "SW" : "HW", gnt.gnt_wl, +		       gnt.gnt_bt_sw_en ? "SW" : "HW", gnt.gnt_bt);  	gnt = gnt_cfg.band[1]; -	seq_printf(m, "phy-1[gnt_wl:%s-%d/gnt_bt:%s-%d]\n", -		   gnt.gnt_wl_sw_en ? "SW" : "HW", -		   gnt.gnt_wl, -		   gnt.gnt_bt_sw_en ? "SW" : "HW", -		   gnt.gnt_bt); +	p += scnprintf(p, end - p, "phy-1[gnt_wl:%s-%d/gnt_bt:%s-%d]\n", +		       gnt.gnt_wl_sw_en ? "SW" : "HW", +		       gnt.gnt_wl, +		       gnt.gnt_bt_sw_en ? "SW" : "HW", +		       gnt.gnt_bt);  	pcinfo = &pfwinfo->rpt_fbtc_mregval.cinfo;  	if (!pcinfo->valid) {  		rtw89_debug(rtwdev, RTW89_DBG_BTC,  			    "[BTC], %s(): stop due rpt_fbtc_mregval.cinfo\n",  			    __func__); -		return; +		goto out;  	}  	pmreg = &pfwinfo->rpt_fbtc_mregval.finfo.v1; @@ -10138,21 +10909,26 @@ static void _show_mreg_v1(struct rtw89_dev *rtwdev, struct seq_file *m)  		val = le32_to_cpu(pmreg->mreg_val[i]);  		if (cnt % 6 == 0) -			seq_printf(m, " %-15s : %d_0x%04x=0x%08x", -				   "[reg]", (u32)type, offset, val); +			p += scnprintf(p, end - p, +				       " %-15s : %d_0x%04x=0x%08x", +				       "[reg]", (u32)type, offset, val);  		else -			seq_printf(m, ", %d_0x%04x=0x%08x", (u32)type, -				   offset, val); +			p += scnprintf(p, end - p, ", %d_0x%04x=0x%08x", +				       (u32)type, +				       offset, val);  		if (cnt % 6 == 5) -			seq_puts(m, "\n"); +			p += scnprintf(p, end - p, "\n");  		cnt++;  		if (i >= pmreg->reg_num) -			seq_puts(m, "\n"); +			p += scnprintf(p, end - p, "\n");  	} + +out: +	return p - buf;  } -static void _show_mreg_v2(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_mreg_v2(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)  {  	const struct rtw89_chip_info *chip = rtwdev->chip;  	struct rtw89_btc *btc = &rtwdev->btc; @@ -10164,46 +10940,48 @@ static void _show_mreg_v2(struct rtw89_dev *rtwdev, struct seq_file *m)  	struct rtw89_btc_bt_info *bt = &btc->cx.bt;  	struct rtw89_mac_ax_coex_gnt gnt_cfg = {};  	struct rtw89_mac_ax_gnt gnt; +	char *p = buf, *end = buf + bufsz;  	u8 i = 0, type = 0, cnt = 0;  	u32 val, offset;  	if (!(btc->dm.coex_info_map & BTC_COEX_INFO_MREG)) -		return; +		return 0; -	seq_puts(m, "========== [HW Status] ==========\n"); +	p += scnprintf(p, end - p, "========== [HW Status] ==========\n"); -	seq_printf(m, -		   " %-15s : WL->BT:0x%08x(cnt:%d), BT->WL:0x%08x(total:%d, bt_update:%d)\n", -		   "[scoreboard]", wl->scbd, cx->cnt_wl[BTC_WCNT_SCBDUPDATE], -		   bt->scbd, cx->cnt_bt[BTC_BCNT_SCBDREAD], -		   cx->cnt_bt[BTC_BCNT_SCBDUPDATE]); +	p += scnprintf(p, end - p, +		       " %-15s : WL->BT:0x%08x(cnt:%d), BT->WL:0x%08x(total:%d, bt_update:%d)\n", +		       "[scoreboard]", wl->scbd, +		       cx->cnt_wl[BTC_WCNT_SCBDUPDATE], +		       bt->scbd, cx->cnt_bt[BTC_BCNT_SCBDREAD], +		       cx->cnt_bt[BTC_BCNT_SCBDUPDATE]);  	btc->dm.pta_owner = rtw89_mac_get_ctrl_path(rtwdev);  	_get_gnt(rtwdev, &gnt_cfg);  	gnt = gnt_cfg.band[0]; -	seq_printf(m, -		   " %-15s : pta_owner:%s, phy-0[gnt_wl:%s-%d/gnt_bt:%s-%d], polut_type:%s", -		   "[gnt_status]", -		   chip->chip_id == RTL8852C ? "HW" : -		   btc->dm.pta_owner == BTC_CTRL_BY_WL ? "WL" : "BT", -		   gnt.gnt_wl_sw_en ? "SW" : "HW", gnt.gnt_wl, -		   gnt.gnt_bt_sw_en ? "SW" : "HW", gnt.gnt_bt, -		   id_to_polut(wl->bt_polut_type[wl->pta_req_mac])); +	p += scnprintf(p, end - p, +		       " %-15s : pta_owner:%s, phy-0[gnt_wl:%s-%d/gnt_bt:%s-%d], polut_type:%s", +		       "[gnt_status]", +		       chip->chip_id == RTL8852C ? "HW" : +		       btc->dm.pta_owner == BTC_CTRL_BY_WL ? "WL" : "BT", +		       gnt.gnt_wl_sw_en ? "SW" : "HW", gnt.gnt_wl, +		       gnt.gnt_bt_sw_en ? "SW" : "HW", gnt.gnt_bt, +		       id_to_polut(wl->bt_polut_type[wl->pta_req_mac]));  	gnt = gnt_cfg.band[1]; -	seq_printf(m, "phy-1[gnt_wl:%s-%d/gnt_bt:%s-%d]\n", -		   gnt.gnt_wl_sw_en ? "SW" : "HW", -		   gnt.gnt_wl, -		   gnt.gnt_bt_sw_en ? "SW" : "HW", -		   gnt.gnt_bt); +	p += scnprintf(p, end - p, "phy-1[gnt_wl:%s-%d/gnt_bt:%s-%d]\n", +		       gnt.gnt_wl_sw_en ? "SW" : "HW", +		       gnt.gnt_wl, +		       gnt.gnt_bt_sw_en ? "SW" : "HW", +		       gnt.gnt_bt);  	pcinfo = &pfwinfo->rpt_fbtc_mregval.cinfo;  	if (!pcinfo->valid) {  		rtw89_debug(rtwdev, RTW89_DBG_BTC,  			    "[BTC], %s(): stop due rpt_fbtc_mregval.cinfo\n",  			    __func__); -		return; +		goto out;  	}  	pmreg = &pfwinfo->rpt_fbtc_mregval.finfo.v2; @@ -10217,21 +10995,26 @@ static void _show_mreg_v2(struct rtw89_dev *rtwdev, struct seq_file *m)  		val = le32_to_cpu(pmreg->mreg_val[i]);  		if (cnt % 6 == 0) -			seq_printf(m, " %-15s : %d_0x%04x=0x%08x", -				   "[reg]", (u32)type, offset, val); +			p += scnprintf(p, end - p, +				       " %-15s : %d_0x%04x=0x%08x", +				       "[reg]", (u32)type, offset, val);  		else -			seq_printf(m, ", %d_0x%04x=0x%08x", (u32)type, -				   offset, val); +			p += scnprintf(p, end - p, ", %d_0x%04x=0x%08x", +				       (u32)type, +				       offset, val);  		if (cnt % 6 == 5) -			seq_puts(m, "\n"); +			p += scnprintf(p, end - p, "\n");  		cnt++;  		if (i >= pmreg->reg_num) -			seq_puts(m, "\n"); +			p += scnprintf(p, end - p, "\n");  	} + +out: +	return p - buf;  } -static void _show_mreg_v7(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_mreg_v7(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)  {  	struct rtw89_btc *btc = &rtwdev->btc;  	struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; @@ -10242,46 +11025,50 @@ static void _show_mreg_v7(struct rtw89_dev *rtwdev, struct seq_file *m)  	struct rtw89_btc_bt_info *bt = &cx->bt;  	struct rtw89_mac_ax_gnt *gnt = NULL;  	struct rtw89_btc_dm *dm = &btc->dm; +	char *p = buf, *end = buf + bufsz;  	u8 i, type, cnt = 0;  	u32 val, offset;  	if (!(dm->coex_info_map & BTC_COEX_INFO_MREG)) -		return; +		return 0; -	seq_puts(m, "\n\r========== [HW Status] =========="); +	p += scnprintf(p, end - p, "\n\r========== [HW Status] =========="); -	seq_printf(m, -		   "\n\r %-15s : WL->BT:0x%08x(cnt:%d), BT->WL:0x%08x(total:%d, bt_update:%d)", -		   "[scoreboard]", wl->scbd, cx->cnt_wl[BTC_WCNT_SCBDUPDATE], -		   bt->scbd, cx->cnt_bt[BTC_BCNT_SCBDREAD], -		   cx->cnt_bt[BTC_BCNT_SCBDUPDATE]); +	p += scnprintf(p, end - p, +		       "\n\r %-15s : WL->BT:0x%08x(cnt:%d), BT->WL:0x%08x(total:%d, bt_update:%d)", +		       "[scoreboard]", wl->scbd, +		       cx->cnt_wl[BTC_WCNT_SCBDUPDATE], +		       bt->scbd, cx->cnt_bt[BTC_BCNT_SCBDREAD], +		       cx->cnt_bt[BTC_BCNT_SCBDUPDATE]);  	/* To avoid I/O if WL LPS or power-off  */  	dm->pta_owner = rtw89_mac_get_ctrl_path(rtwdev); -	seq_printf(m, -		   "\n\r %-15s : pta_owner:%s, pta_req_mac:MAC%d, rf_gnt_source: polut_type:%s", -		   "[gnt_status]", -		   rtwdev->chip->para_ver & BTC_FEAT_PTA_ONOFF_CTRL ? "HW" : -		   dm->pta_owner == BTC_CTRL_BY_WL ? "WL" : "BT", -		   wl->pta_req_mac, id_to_polut(wl->bt_polut_type[wl->pta_req_mac])); +	p += scnprintf(p, end - p, +		       "\n\r %-15s : pta_owner:%s, pta_req_mac:MAC%d, rf_gnt_source: polut_type:%s", +		       "[gnt_status]", +		       rtwdev->chip->para_ver & BTC_FEAT_PTA_ONOFF_CTRL ? "HW" : +		       dm->pta_owner == BTC_CTRL_BY_WL ? "WL" : "BT", +		       wl->pta_req_mac, +		       id_to_polut(wl->bt_polut_type[wl->pta_req_mac]));  	gnt = &dm->gnt.band[RTW89_PHY_0]; -	seq_printf(m, ", phy-0[gnt_wl:%s-%d/gnt_bt:%s-%d]", -		   gnt->gnt_wl_sw_en ? "SW" : "HW", gnt->gnt_wl, -		   gnt->gnt_bt_sw_en ? "SW" : "HW", gnt->gnt_bt); +	p += scnprintf(p, end - p, ", phy-0[gnt_wl:%s-%d/gnt_bt:%s-%d]", +		       gnt->gnt_wl_sw_en ? "SW" : "HW", gnt->gnt_wl, +		       gnt->gnt_bt_sw_en ? "SW" : "HW", gnt->gnt_bt);  	if (rtwdev->dbcc_en) {  		gnt = &dm->gnt.band[RTW89_PHY_1]; -		seq_printf(m, ", phy-1[gnt_wl:%s-%d/gnt_bt:%s-%d]", -			   gnt->gnt_wl_sw_en ? "SW" : "HW", gnt->gnt_wl, -			   gnt->gnt_bt_sw_en ? "SW" : "HW", gnt->gnt_bt); +		p += scnprintf(p, end - p, +			       ", phy-1[gnt_wl:%s-%d/gnt_bt:%s-%d]", +			       gnt->gnt_wl_sw_en ? "SW" : "HW", gnt->gnt_wl, +			       gnt->gnt_bt_sw_en ? "SW" : "HW", gnt->gnt_bt);  	}  	pcinfo = &pfwinfo->rpt_fbtc_mregval.cinfo;  	if (!pcinfo->valid) -		return; +		goto out;  	pmreg = &pfwinfo->rpt_fbtc_mregval.finfo.v7; @@ -10291,17 +11078,20 @@ static void _show_mreg_v7(struct rtw89_dev *rtwdev, struct seq_file *m)  		val = le32_to_cpu(pmreg->mreg_val[i]);  		if (cnt % 6 == 0) -			seq_printf(m, "\n\r %-15s : %s_0x%x=0x%x", "[reg]", -				   id_to_regtype(type), offset, val); +			p += scnprintf(p, end - p, +				       "\n\r %-15s : %s_0x%x=0x%x", "[reg]", +				       id_to_regtype(type), offset, val);  		else -			seq_printf(m, ", %s_0x%x=0x%x", -				   id_to_regtype(type), offset, val); +			p += scnprintf(p, end - p, ", %s_0x%x=0x%x", +				       id_to_regtype(type), offset, val);  		cnt++;  	} -	seq_puts(m, "\n"); + +out: +	return p - buf;  } -static void _show_summary_v1(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_summary_v1(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)  {  	struct rtw89_btc *btc = &rtwdev->btc;  	struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; @@ -10312,56 +11102,59 @@ static void _show_summary_v1(struct rtw89_dev *rtwdev, struct seq_file *m)  	struct rtw89_btc_wl_info *wl = &cx->wl;  	struct rtw89_btc_bt_info *bt = &cx->bt;  	u32 cnt_sum = 0, *cnt = btc->dm.cnt_notify; +	char *p = buf, *end = buf + bufsz;  	u8 i;  	if (!(dm->coex_info_map & BTC_COEX_INFO_SUMMARY)) -		return; +		return 0; -	seq_puts(m, "========== [Statistics] ==========\n"); +	p += scnprintf(p, end - p, "========== [Statistics] ==========\n");  	pcinfo = &pfwinfo->rpt_ctrl.cinfo;  	if (pcinfo->valid && !wl->status.map.lps && !wl->status.map.rf_off) {  		prptctrl = &pfwinfo->rpt_ctrl.finfo.v1; -		seq_printf(m, -			   " %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d), ", -			   "[summary]", pfwinfo->cnt_h2c, -			   pfwinfo->cnt_h2c_fail, prptctrl->h2c_cnt, -			   pfwinfo->cnt_c2h, prptctrl->c2h_cnt); +		p += scnprintf(p, end - p, +			       " %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d), ", +			       "[summary]", pfwinfo->cnt_h2c, +			       pfwinfo->cnt_h2c_fail, prptctrl->h2c_cnt, +			       pfwinfo->cnt_c2h, prptctrl->c2h_cnt); -		seq_printf(m, -			   "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x, dm_error_map:0x%x", -			   pfwinfo->event[BTF_EVNT_RPT], prptctrl->rpt_cnt, -			   prptctrl->rpt_enable, dm->error.val); +		p += scnprintf(p, end - p, +			       "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x, dm_error_map:0x%x", +			       pfwinfo->event[BTF_EVNT_RPT], +			       prptctrl->rpt_cnt, +			       prptctrl->rpt_enable, dm->error.val);  		if (dm->error.map.wl_fw_hang) -			seq_puts(m, " (WL FW Hang!!)"); -		seq_puts(m, "\n"); -		seq_printf(m, -			   " %-15s : send_ok:%d, send_fail:%d, recv:%d", -			   "[mailbox]", prptctrl->mb_send_ok_cnt, -			   prptctrl->mb_send_fail_cnt, prptctrl->mb_recv_cnt); - -		seq_printf(m, -			   "(A2DP_empty:%d, A2DP_flowstop:%d, A2DP_full:%d)\n", -			   prptctrl->mb_a2dp_empty_cnt, -			   prptctrl->mb_a2dp_flct_cnt, -			   prptctrl->mb_a2dp_full_cnt); - -		seq_printf(m, -			   " %-15s : wl_rfk[req:%d/go:%d/reject:%d/timeout:%d]", -			   "[RFK]", cx->cnt_wl[BTC_WCNT_RFK_REQ], -			   cx->cnt_wl[BTC_WCNT_RFK_GO], -			   cx->cnt_wl[BTC_WCNT_RFK_REJECT], -			   cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]); - -		seq_printf(m, -			   ", bt_rfk[req:%d/go:%d/reject:%d/timeout:%d/fail:%d]\n", -			   prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_REQ], -			   prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_GO], -			   prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_REJECT], -			   prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_TIMEOUT], -			   prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_FAIL]); +			p += scnprintf(p, end - p, " (WL FW Hang!!)"); +		p += scnprintf(p, end - p, "\n"); +		p += scnprintf(p, end - p, +			       " %-15s : send_ok:%d, send_fail:%d, recv:%d", +			       "[mailbox]", prptctrl->mb_send_ok_cnt, +			       prptctrl->mb_send_fail_cnt, +			       prptctrl->mb_recv_cnt); + +		p += scnprintf(p, end - p, +			       "(A2DP_empty:%d, A2DP_flowstop:%d, A2DP_full:%d)\n", +			       prptctrl->mb_a2dp_empty_cnt, +			       prptctrl->mb_a2dp_flct_cnt, +			       prptctrl->mb_a2dp_full_cnt); + +		p += scnprintf(p, end - p, +			       " %-15s : wl_rfk[req:%d/go:%d/reject:%d/timeout:%d]", +			       "[RFK]", cx->cnt_wl[BTC_WCNT_RFK_REQ], +			       cx->cnt_wl[BTC_WCNT_RFK_GO], +			       cx->cnt_wl[BTC_WCNT_RFK_REJECT], +			       cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]); + +		p += scnprintf(p, end - p, +			       ", bt_rfk[req:%d/go:%d/reject:%d/timeout:%d/fail:%d]\n", +			       prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_REQ], +			       prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_GO], +			       prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_REJECT], +			       prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_TIMEOUT], +			       prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_FAIL]);  		if (prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_TIMEOUT] > 0)  			bt->rfk_info.map.timeout = 1; @@ -10370,42 +11163,44 @@ static void _show_summary_v1(struct rtw89_dev *rtwdev, struct seq_file *m)  		dm->error.map.wl_rfk_timeout = bt->rfk_info.map.timeout;  	} else { -		seq_printf(m, -			   " %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d, rpt_cnt=%d, rpt_map=0x%x", -			   "[summary]", pfwinfo->cnt_h2c, -			   pfwinfo->cnt_h2c_fail, pfwinfo->cnt_c2h, -			   pfwinfo->event[BTF_EVNT_RPT], -			   btc->fwinfo.rpt_en_map); -		seq_puts(m, " (WL FW report invalid!!)\n"); +		p += scnprintf(p, end - p, +			       " %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d, rpt_cnt=%d, rpt_map=0x%x", +			       "[summary]", pfwinfo->cnt_h2c, +			       pfwinfo->cnt_h2c_fail, pfwinfo->cnt_c2h, +			       pfwinfo->event[BTF_EVNT_RPT], +			       btc->fwinfo.rpt_en_map); +		p += scnprintf(p, end - p, " (WL FW report invalid!!)\n");  	}  	for (i = 0; i < BTC_NCNT_NUM; i++)  		cnt_sum += dm->cnt_notify[i]; -	seq_printf(m, -		   " %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", -		   "[notify_cnt]", cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], -		   cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); +	p += scnprintf(p, end - p, +		       " %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", +		       "[notify_cnt]", cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], +		       cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); + +	p += scnprintf(p, end - p, +		       "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d\n", +		       cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], +		       cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], +		       cnt[BTC_NCNT_WL_STA]); -	seq_printf(m, -		   "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d\n", -		   cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], -		   cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], -		   cnt[BTC_NCNT_WL_STA]); +	p += scnprintf(p, end - p, +		       " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, special_pkt=%d, ", +		       "[notify_cnt]", cnt[BTC_NCNT_SCAN_START], +		       cnt[BTC_NCNT_SCAN_FINISH], cnt[BTC_NCNT_SWITCH_BAND], +		       cnt[BTC_NCNT_SPECIAL_PACKET]); -	seq_printf(m, -		   " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, special_pkt=%d, ", -		   "[notify_cnt]", cnt[BTC_NCNT_SCAN_START], -		   cnt[BTC_NCNT_SCAN_FINISH], cnt[BTC_NCNT_SWITCH_BAND], -		   cnt[BTC_NCNT_SPECIAL_PACKET]); +	p += scnprintf(p, end - p, +		       "timer=%d, control=%d, customerize=%d\n", +		       cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CONTROL], +		       cnt[BTC_NCNT_CUSTOMERIZE]); -	seq_printf(m, -		   "timer=%d, control=%d, customerize=%d\n", -		   cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CONTROL], -		   cnt[BTC_NCNT_CUSTOMERIZE]); +	return p - buf;  } -static void _show_summary_v4(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_summary_v4(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)  {  	struct rtw89_btc *btc = &rtwdev->btc;  	struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; @@ -10416,64 +11211,65 @@ static void _show_summary_v4(struct rtw89_dev *rtwdev, struct seq_file *m)  	struct rtw89_btc_wl_info *wl = &cx->wl;  	struct rtw89_btc_bt_info *bt = &cx->bt;  	u32 cnt_sum = 0, *cnt = btc->dm.cnt_notify; +	char *p = buf, *end = buf + bufsz;  	u8 i;  	if (!(dm->coex_info_map & BTC_COEX_INFO_SUMMARY)) -		return; +		return 0; -	seq_puts(m, "========== [Statistics] ==========\n"); +	p += scnprintf(p, end - p, "========== [Statistics] ==========\n");  	pcinfo = &pfwinfo->rpt_ctrl.cinfo;  	if (pcinfo->valid && !wl->status.map.lps && !wl->status.map.rf_off) {  		prptctrl = &pfwinfo->rpt_ctrl.finfo.v4; -		seq_printf(m, -			   " %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d), ", -			   "[summary]", pfwinfo->cnt_h2c, -			   pfwinfo->cnt_h2c_fail, -			   le32_to_cpu(prptctrl->rpt_info.cnt_h2c), -			   pfwinfo->cnt_c2h, -			   le32_to_cpu(prptctrl->rpt_info.cnt_c2h)); - -		seq_printf(m, -			   "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x, dm_error_map:0x%x", -			   pfwinfo->event[BTF_EVNT_RPT], -			   le32_to_cpu(prptctrl->rpt_info.cnt), -			   le32_to_cpu(prptctrl->rpt_info.en), -			   dm->error.val); +		p += scnprintf(p, end - p, +			       " %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d), ", +			       "[summary]", pfwinfo->cnt_h2c, +			       pfwinfo->cnt_h2c_fail, +			       le32_to_cpu(prptctrl->rpt_info.cnt_h2c), +			       pfwinfo->cnt_c2h, +			       le32_to_cpu(prptctrl->rpt_info.cnt_c2h)); + +		p += scnprintf(p, end - p, +			       "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x, dm_error_map:0x%x", +			       pfwinfo->event[BTF_EVNT_RPT], +			       le32_to_cpu(prptctrl->rpt_info.cnt), +			       le32_to_cpu(prptctrl->rpt_info.en), +			       dm->error.val);  		if (dm->error.map.wl_fw_hang) -			seq_puts(m, " (WL FW Hang!!)"); -		seq_puts(m, "\n"); -		seq_printf(m, -			   " %-15s : send_ok:%d, send_fail:%d, recv:%d, ", -			   "[mailbox]", -			   le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok), -			   le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail), -			   le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv)); - -		seq_printf(m, -			   "A2DP_empty:%d(stop:%d, tx:%d, ack:%d, nack:%d)\n", -			   le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty), -			   le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl), -			   le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx), -			   le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack), -			   le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack)); - -		seq_printf(m, -			   " %-15s : wl_rfk[req:%d/go:%d/reject:%d/timeout:%d]", -			   "[RFK]", cx->cnt_wl[BTC_WCNT_RFK_REQ], -			   cx->cnt_wl[BTC_WCNT_RFK_GO], -			   cx->cnt_wl[BTC_WCNT_RFK_REJECT], -			   cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]); - -		seq_printf(m, -			   ", bt_rfk[req:%d/go:%d/reject:%d/timeout:%d/fail:%d]\n", -			   le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ]), -			   le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_GO]), -			   le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REJECT]), -			   le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_TIMEOUT]), -			   le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_FAIL])); +			p += scnprintf(p, end - p, " (WL FW Hang!!)"); +		p += scnprintf(p, end - p, "\n"); +		p += scnprintf(p, end - p, +			       " %-15s : send_ok:%d, send_fail:%d, recv:%d, ", +			       "[mailbox]", +			       le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok), +			       le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail), +			       le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv)); + +		p += scnprintf(p, end - p, +			       "A2DP_empty:%d(stop:%d, tx:%d, ack:%d, nack:%d)\n", +			       le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty), +			       le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl), +			       le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx), +			       le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack), +			       le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack)); + +		p += scnprintf(p, end - p, +			       " %-15s : wl_rfk[req:%d/go:%d/reject:%d/timeout:%d]", +			       "[RFK]", cx->cnt_wl[BTC_WCNT_RFK_REQ], +			       cx->cnt_wl[BTC_WCNT_RFK_GO], +			       cx->cnt_wl[BTC_WCNT_RFK_REJECT], +			       cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]); + +		p += scnprintf(p, end - p, +			       ", bt_rfk[req:%d/go:%d/reject:%d/timeout:%d/fail:%d]\n", +			       le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ]), +			       le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_GO]), +			       le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REJECT]), +			       le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_TIMEOUT]), +			       le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_FAIL]));  		if (le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_TIMEOUT]) > 0)  			bt->rfk_info.map.timeout = 1; @@ -10482,42 +11278,44 @@ static void _show_summary_v4(struct rtw89_dev *rtwdev, struct seq_file *m)  		dm->error.map.wl_rfk_timeout = bt->rfk_info.map.timeout;  	} else { -		seq_printf(m, -			   " %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d, rpt_cnt=%d, rpt_map=0x%x", -			   "[summary]", pfwinfo->cnt_h2c, -			   pfwinfo->cnt_h2c_fail, pfwinfo->cnt_c2h, -			   pfwinfo->event[BTF_EVNT_RPT], -			   btc->fwinfo.rpt_en_map); -		seq_puts(m, " (WL FW report invalid!!)\n"); +		p += scnprintf(p, end - p, +			       " %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d, rpt_cnt=%d, rpt_map=0x%x", +			       "[summary]", pfwinfo->cnt_h2c, +			       pfwinfo->cnt_h2c_fail, pfwinfo->cnt_c2h, +			       pfwinfo->event[BTF_EVNT_RPT], +			       btc->fwinfo.rpt_en_map); +		p += scnprintf(p, end - p, " (WL FW report invalid!!)\n");  	}  	for (i = 0; i < BTC_NCNT_NUM; i++)  		cnt_sum += dm->cnt_notify[i]; -	seq_printf(m, -		   " %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", -		   "[notify_cnt]", cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], -		   cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); +	p += scnprintf(p, end - p, +		       " %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", +		       "[notify_cnt]", cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], +		       cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); + +	p += scnprintf(p, end - p, +		       "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d\n", +		       cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], +		       cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], +		       cnt[BTC_NCNT_WL_STA]); -	seq_printf(m, -		   "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d\n", -		   cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], -		   cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], -		   cnt[BTC_NCNT_WL_STA]); +	p += scnprintf(p, end - p, +		       " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, special_pkt=%d, ", +		       "[notify_cnt]", cnt[BTC_NCNT_SCAN_START], +		       cnt[BTC_NCNT_SCAN_FINISH], cnt[BTC_NCNT_SWITCH_BAND], +		       cnt[BTC_NCNT_SPECIAL_PACKET]); -	seq_printf(m, -		   " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, special_pkt=%d, ", -		   "[notify_cnt]", cnt[BTC_NCNT_SCAN_START], -		   cnt[BTC_NCNT_SCAN_FINISH], cnt[BTC_NCNT_SWITCH_BAND], -		   cnt[BTC_NCNT_SPECIAL_PACKET]); +	p += scnprintf(p, end - p, +		       "timer=%d, control=%d, customerize=%d\n", +		       cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CONTROL], +		       cnt[BTC_NCNT_CUSTOMERIZE]); -	seq_printf(m, -		   "timer=%d, control=%d, customerize=%d\n", -		   cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CONTROL], -		   cnt[BTC_NCNT_CUSTOMERIZE]); +	return p - buf;  } -static void _show_summary_v5(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_summary_v5(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)  {  	struct rtw89_btc *btc = &rtwdev->btc;  	struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; @@ -10527,112 +11325,118 @@ static void _show_summary_v5(struct rtw89_dev *rtwdev, struct seq_file *m)  	struct rtw89_btc_dm *dm = &btc->dm;  	struct rtw89_btc_wl_info *wl = &cx->wl;  	u32 cnt_sum = 0, *cnt = btc->dm.cnt_notify; +	char *p = buf, *end = buf + bufsz;  	u8 i;  	if (!(dm->coex_info_map & BTC_COEX_INFO_SUMMARY)) -		return; +		return 0; -	seq_puts(m, "========== [Statistics] ==========\n"); +	p += scnprintf(p, end - p, "========== [Statistics] ==========\n");  	pcinfo = &pfwinfo->rpt_ctrl.cinfo;  	if (pcinfo->valid && !wl->status.map.lps && !wl->status.map.rf_off) {  		prptctrl = &pfwinfo->rpt_ctrl.finfo.v5; -		seq_printf(m, -			   " %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d, len:%d), ", -			   "[summary]", pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail, -			   le16_to_cpu(prptctrl->rpt_info.cnt_h2c), -			   pfwinfo->cnt_c2h, -			   le16_to_cpu(prptctrl->rpt_info.cnt_c2h), -			   le16_to_cpu(prptctrl->rpt_info.len_c2h)); - -		seq_printf(m, -			   "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x", -			   pfwinfo->event[BTF_EVNT_RPT], -			   le16_to_cpu(prptctrl->rpt_info.cnt), -			   le32_to_cpu(prptctrl->rpt_info.en)); +		p += scnprintf(p, end - p, +			       " %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d, len:%d), ", +			       "[summary]", pfwinfo->cnt_h2c, +			       pfwinfo->cnt_h2c_fail, +			       le16_to_cpu(prptctrl->rpt_info.cnt_h2c), +			       pfwinfo->cnt_c2h, +			       le16_to_cpu(prptctrl->rpt_info.cnt_c2h), +			       le16_to_cpu(prptctrl->rpt_info.len_c2h)); + +		p += scnprintf(p, end - p, +			       "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x", +			       pfwinfo->event[BTF_EVNT_RPT], +			       le16_to_cpu(prptctrl->rpt_info.cnt), +			       le32_to_cpu(prptctrl->rpt_info.en));  		if (dm->error.map.wl_fw_hang) -			seq_puts(m, " (WL FW Hang!!)"); -		seq_puts(m, "\n"); -		seq_printf(m, -			   " %-15s : send_ok:%d, send_fail:%d, recv:%d, ", -			   "[mailbox]", -			   le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok), -			   le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail), -			   le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv)); - -		seq_printf(m, -			   "A2DP_empty:%d(stop:%d, tx:%d, ack:%d, nack:%d)\n", -			   le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty), -			   le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl), -			   le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx), -			   le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack), -			   le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack)); - -		seq_printf(m, -			   " %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d]", -			   "[RFK/LPS]", cx->cnt_wl[BTC_WCNT_RFK_REQ], -			   cx->cnt_wl[BTC_WCNT_RFK_GO], -			   cx->cnt_wl[BTC_WCNT_RFK_REJECT], -			   cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]); - -		seq_printf(m, -			   ", bt_rfk[req:%d]", -			   le16_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ])); - -		seq_printf(m, -			   ", AOAC[RF_on:%d/RF_off:%d]", -			   le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on), -			   le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off)); +			p += scnprintf(p, end - p, " (WL FW Hang!!)"); +		p += scnprintf(p, end - p, "\n"); +		p += scnprintf(p, end - p, +			       " %-15s : send_ok:%d, send_fail:%d, recv:%d, ", +			       "[mailbox]", +			       le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok), +			       le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail), +			       le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv)); + +		p += scnprintf(p, end - p, +			       "A2DP_empty:%d(stop:%d, tx:%d, ack:%d, nack:%d)\n", +			       le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty), +			       le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl), +			       le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx), +			       le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack), +			       le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack)); + +		p += scnprintf(p, end - p, +			       " %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d]", +			       "[RFK/LPS]", cx->cnt_wl[BTC_WCNT_RFK_REQ], +			       cx->cnt_wl[BTC_WCNT_RFK_GO], +			       cx->cnt_wl[BTC_WCNT_RFK_REJECT], +			       cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]); + +		p += scnprintf(p, end - p, +			       ", bt_rfk[req:%d]", +			       le16_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ])); + +		p += scnprintf(p, end - p, +			       ", AOAC[RF_on:%d/RF_off:%d]", +			       le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on), +			       le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off));  	} else { -		seq_printf(m, -			   " %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d", -			   "[summary]", pfwinfo->cnt_h2c, -			   pfwinfo->cnt_h2c_fail, pfwinfo->cnt_c2h); +		p += scnprintf(p, end - p, +			       " %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d", +			       "[summary]", pfwinfo->cnt_h2c, +			       pfwinfo->cnt_h2c_fail, pfwinfo->cnt_c2h);  	}  	if (!pcinfo->valid || pfwinfo->len_mismch || pfwinfo->fver_mismch ||  	    pfwinfo->err[BTFRE_EXCEPTION]) { -		seq_puts(m, "\n"); -		seq_printf(m, -			   " %-15s : WL FW rpt error!![rpt_ctrl_valid:%d/len:" -			   "0x%x/ver:0x%x/ex:%d/lps=%d/rf_off=%d]", -			   "[ERROR]", pcinfo->valid, pfwinfo->len_mismch, -			   pfwinfo->fver_mismch, pfwinfo->err[BTFRE_EXCEPTION], -			   wl->status.map.lps, wl->status.map.rf_off); +		p += scnprintf(p, end - p, "\n"); +		p += scnprintf(p, end - p, +			       " %-15s : WL FW rpt error!![rpt_ctrl_valid:%d/len:" +			       "0x%x/ver:0x%x/ex:%d/lps=%d/rf_off=%d]", +			       "[ERROR]", pcinfo->valid, pfwinfo->len_mismch, +			       pfwinfo->fver_mismch, +			       pfwinfo->err[BTFRE_EXCEPTION], +			       wl->status.map.lps, wl->status.map.rf_off);  	}  	for (i = 0; i < BTC_NCNT_NUM; i++)  		cnt_sum += dm->cnt_notify[i]; -	seq_puts(m, "\n"); -	seq_printf(m, -		   " %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", -		   "[notify_cnt]", -		   cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], -		   cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); +	p += scnprintf(p, end - p, "\n"); +	p += scnprintf(p, end - p, +		       " %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", +		       "[notify_cnt]", +		       cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], +		       cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); + +	p += scnprintf(p, end - p, +		       "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d", +		       cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], +		       cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], +		       cnt[BTC_NCNT_WL_STA]); -	seq_printf(m, -		   "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d", -		   cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], -		   cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], -		   cnt[BTC_NCNT_WL_STA]); +	p += scnprintf(p, end - p, "\n"); +	p += scnprintf(p, end - p, +		       " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, special_pkt=%d, ", +		       "[notify_cnt]", +		       cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH], +		       cnt[BTC_NCNT_SWITCH_BAND], +		       cnt[BTC_NCNT_SPECIAL_PACKET]); -	seq_puts(m, "\n"); -	seq_printf(m, -		   " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, special_pkt=%d, ", -		   "[notify_cnt]", -		   cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH], -		   cnt[BTC_NCNT_SWITCH_BAND], cnt[BTC_NCNT_SPECIAL_PACKET]); +	p += scnprintf(p, end - p, +		       "timer=%d, control=%d, customerize=%d", +		       cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CONTROL], +		       cnt[BTC_NCNT_CUSTOMERIZE]); -	seq_printf(m, -		   "timer=%d, control=%d, customerize=%d", -		   cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CONTROL], -		   cnt[BTC_NCNT_CUSTOMERIZE]); +	return p - buf;  } -static void _show_summary_v105(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_summary_v105(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)  {  	struct rtw89_btc *btc = &rtwdev->btc;  	struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; @@ -10642,112 +11446,118 @@ static void _show_summary_v105(struct rtw89_dev *rtwdev, struct seq_file *m)  	struct rtw89_btc_dm *dm = &btc->dm;  	struct rtw89_btc_wl_info *wl = &cx->wl;  	u32 cnt_sum = 0, *cnt = btc->dm.cnt_notify; +	char *p = buf, *end = buf + bufsz;  	u8 i;  	if (!(dm->coex_info_map & BTC_COEX_INFO_SUMMARY)) -		return; +		return 0; -	seq_puts(m, "========== [Statistics] ==========\n"); +	p += scnprintf(p, end - p, "========== [Statistics] ==========\n");  	pcinfo = &pfwinfo->rpt_ctrl.cinfo;  	if (pcinfo->valid && !wl->status.map.lps && !wl->status.map.rf_off) {  		prptctrl = &pfwinfo->rpt_ctrl.finfo.v105; -		seq_printf(m, -			   " %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d, len:%d), ", -			   "[summary]", pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail, -			   le16_to_cpu(prptctrl->rpt_info.cnt_h2c), -			   pfwinfo->cnt_c2h, -			   le16_to_cpu(prptctrl->rpt_info.cnt_c2h), -			   le16_to_cpu(prptctrl->rpt_info.len_c2h)); - -		seq_printf(m, -			   "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x", -			   pfwinfo->event[BTF_EVNT_RPT], -			   le16_to_cpu(prptctrl->rpt_info.cnt), -			   le32_to_cpu(prptctrl->rpt_info.en)); +		p += scnprintf(p, end - p, +			       " %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d, len:%d), ", +			       "[summary]", pfwinfo->cnt_h2c, +			       pfwinfo->cnt_h2c_fail, +			       le16_to_cpu(prptctrl->rpt_info.cnt_h2c), +			       pfwinfo->cnt_c2h, +			       le16_to_cpu(prptctrl->rpt_info.cnt_c2h), +			       le16_to_cpu(prptctrl->rpt_info.len_c2h)); + +		p += scnprintf(p, end - p, +			       "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x", +			       pfwinfo->event[BTF_EVNT_RPT], +			       le16_to_cpu(prptctrl->rpt_info.cnt), +			       le32_to_cpu(prptctrl->rpt_info.en));  		if (dm->error.map.wl_fw_hang) -			seq_puts(m, " (WL FW Hang!!)"); -		seq_puts(m, "\n"); -		seq_printf(m, -			   " %-15s : send_ok:%d, send_fail:%d, recv:%d, ", -			   "[mailbox]", -			   le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok), -			   le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail), -			   le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv)); - -		seq_printf(m, -			   "A2DP_empty:%d(stop:%d, tx:%d, ack:%d, nack:%d)\n", -			   le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty), -			   le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl), -			   le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx), -			   le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack), -			   le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack)); - -		seq_printf(m, -			   " %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d]", -			   "[RFK/LPS]", cx->cnt_wl[BTC_WCNT_RFK_REQ], -			   cx->cnt_wl[BTC_WCNT_RFK_GO], -			   cx->cnt_wl[BTC_WCNT_RFK_REJECT], -			   cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]); - -		seq_printf(m, -			   ", bt_rfk[req:%d]", -			   le16_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ])); - -		seq_printf(m, -			   ", AOAC[RF_on:%d/RF_off:%d]", -			   le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on), -			   le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off)); +			p += scnprintf(p, end - p, " (WL FW Hang!!)"); +		p += scnprintf(p, end - p, "\n"); +		p += scnprintf(p, end - p, +			       " %-15s : send_ok:%d, send_fail:%d, recv:%d, ", +			       "[mailbox]", +			       le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok), +			       le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail), +			       le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv)); + +		p += scnprintf(p, end - p, +			       "A2DP_empty:%d(stop:%d, tx:%d, ack:%d, nack:%d)\n", +			       le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty), +			       le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl), +			       le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx), +			       le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack), +			       le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack)); + +		p += scnprintf(p, end - p, +			       " %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d]", +			       "[RFK/LPS]", cx->cnt_wl[BTC_WCNT_RFK_REQ], +			       cx->cnt_wl[BTC_WCNT_RFK_GO], +			       cx->cnt_wl[BTC_WCNT_RFK_REJECT], +			       cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]); + +		p += scnprintf(p, end - p, +			       ", bt_rfk[req:%d]", +			       le16_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ])); + +		p += scnprintf(p, end - p, +			       ", AOAC[RF_on:%d/RF_off:%d]", +			       le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on), +			       le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off));  	} else { -		seq_printf(m, -			   " %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d", -			   "[summary]", pfwinfo->cnt_h2c, -			   pfwinfo->cnt_h2c_fail, pfwinfo->cnt_c2h); +		p += scnprintf(p, end - p, +			       " %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d", +			       "[summary]", pfwinfo->cnt_h2c, +			       pfwinfo->cnt_h2c_fail, pfwinfo->cnt_c2h);  	}  	if (!pcinfo->valid || pfwinfo->len_mismch || pfwinfo->fver_mismch ||  	    pfwinfo->err[BTFRE_EXCEPTION]) { -		seq_puts(m, "\n"); -		seq_printf(m, -			   " %-15s : WL FW rpt error!![rpt_ctrl_valid:%d/len:" -			   "0x%x/ver:0x%x/ex:%d/lps=%d/rf_off=%d]", -			   "[ERROR]", pcinfo->valid, pfwinfo->len_mismch, -			   pfwinfo->fver_mismch, pfwinfo->err[BTFRE_EXCEPTION], -			   wl->status.map.lps, wl->status.map.rf_off); +		p += scnprintf(p, end - p, "\n"); +		p += scnprintf(p, end - p, +			       " %-15s : WL FW rpt error!![rpt_ctrl_valid:%d/len:" +			       "0x%x/ver:0x%x/ex:%d/lps=%d/rf_off=%d]", +			       "[ERROR]", pcinfo->valid, pfwinfo->len_mismch, +			       pfwinfo->fver_mismch, +			       pfwinfo->err[BTFRE_EXCEPTION], +			       wl->status.map.lps, wl->status.map.rf_off);  	}  	for (i = 0; i < BTC_NCNT_NUM; i++)  		cnt_sum += dm->cnt_notify[i]; -	seq_puts(m, "\n"); -	seq_printf(m, -		   " %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", -		   "[notify_cnt]", -		   cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], -		   cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); +	p += scnprintf(p, end - p, "\n"); +	p += scnprintf(p, end - p, +		       " %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", +		       "[notify_cnt]", +		       cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], +		       cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); -	seq_printf(m, -		   "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d", -		   cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], -		   cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], -		   cnt[BTC_NCNT_WL_STA]); +	p += scnprintf(p, end - p, +		       "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d", +		       cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], +		       cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], +		       cnt[BTC_NCNT_WL_STA]); -	seq_puts(m, "\n"); -	seq_printf(m, -		   " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, special_pkt=%d, ", -		   "[notify_cnt]", -		   cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH], -		   cnt[BTC_NCNT_SWITCH_BAND], cnt[BTC_NCNT_SPECIAL_PACKET]); +	p += scnprintf(p, end - p, "\n"); +	p += scnprintf(p, end - p, +		       " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, special_pkt=%d, ", +		       "[notify_cnt]", +		       cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH], +		       cnt[BTC_NCNT_SWITCH_BAND], +		       cnt[BTC_NCNT_SPECIAL_PACKET]); -	seq_printf(m, -		   "timer=%d, control=%d, customerize=%d", -		   cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CONTROL], -		   cnt[BTC_NCNT_CUSTOMERIZE]); +	p += scnprintf(p, end - p, +		       "timer=%d, control=%d, customerize=%d", +		       cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CONTROL], +		       cnt[BTC_NCNT_CUSTOMERIZE]); + +	return p - buf;  } -static void _show_summary_v7(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_summary_v7(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)  {  	struct rtw89_btc_btf_fwinfo *pfwinfo = &rtwdev->btc.fwinfo;  	struct rtw89_btc_fbtc_rpt_ctrl_v7 *prptctrl = NULL; @@ -10756,100 +11566,111 @@ static void _show_summary_v7(struct rtw89_dev *rtwdev, struct seq_file *m)  	struct rtw89_btc_dm *dm = &rtwdev->btc.dm;  	struct rtw89_btc_wl_info *wl = &cx->wl;  	u32 *cnt = rtwdev->btc.dm.cnt_notify; +	char *p = buf, *end = buf + bufsz;  	u32 cnt_sum = 0;  	u8 i;  	if (!(dm->coex_info_map & BTC_COEX_INFO_SUMMARY)) -		return; +		return 0; -	seq_printf(m, "%s", "\n\r========== [Statistics] =========="); +	p += scnprintf(p, end - p, "%s", +		       "\n\r========== [Statistics] ==========");  	pcinfo = &pfwinfo->rpt_ctrl.cinfo;  	if (pcinfo->valid && wl->status.map.lps != BTC_LPS_RF_OFF &&  	    !wl->status.map.rf_off) {  		prptctrl = &pfwinfo->rpt_ctrl.finfo.v7; -		seq_printf(m, -			   "\n\r %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d)," -			   "c2h_cnt=%d(fw_send:%d, len:%d, max:%d), ", -			   "[summary]", pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail, -			   le16_to_cpu(prptctrl->rpt_info.cnt_h2c), pfwinfo->cnt_c2h, -			   le16_to_cpu(prptctrl->rpt_info.cnt_c2h), -			   le16_to_cpu(prptctrl->rpt_info.len_c2h), -			   rtwdev->btc.ver->info_buf); - -		seq_printf(m, "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x", -			   pfwinfo->event[BTF_EVNT_RPT], -			   le16_to_cpu(prptctrl->rpt_info.cnt), -			   le32_to_cpu(prptctrl->rpt_info.en)); +		p += scnprintf(p, end - p, +			       "\n\r %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d)," +			       "c2h_cnt=%d(fw_send:%d, len:%d, max:%d), ", +			       "[summary]", pfwinfo->cnt_h2c, +			       pfwinfo->cnt_h2c_fail, +			       le16_to_cpu(prptctrl->rpt_info.cnt_h2c), +			       pfwinfo->cnt_c2h, +			       le16_to_cpu(prptctrl->rpt_info.cnt_c2h), +			       le16_to_cpu(prptctrl->rpt_info.len_c2h), +			       rtwdev->btc.ver->info_buf); + +		p += scnprintf(p, end - p, +			       "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x", +			       pfwinfo->event[BTF_EVNT_RPT], +			       le16_to_cpu(prptctrl->rpt_info.cnt), +			       le32_to_cpu(prptctrl->rpt_info.en));  		if (dm->error.map.wl_fw_hang) -			seq_puts(m, " (WL FW Hang!!)"); - -		seq_printf(m, "\n\r %-15s : send_ok:%d, send_fail:%d, recv:%d, ", -			   "[mailbox]", le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok), -			   le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail), -			   le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv)); - -		seq_printf(m, "A2DP_empty:%d(stop:%d/tx:%d/ack:%d/nack:%d)", -			   le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty), -			   le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl), -			   le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx), -			   le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack), -			   le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack)); - -		seq_printf(m, -			   "\n\r %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d/time:%dms]", -			   "[RFK/LPS]", cx->cnt_wl[BTC_WCNT_RFK_REQ], -			   cx->cnt_wl[BTC_WCNT_RFK_GO], -			   cx->cnt_wl[BTC_WCNT_RFK_REJECT], -			   cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT], -			   wl->rfk_info.proc_time); - -		seq_printf(m, ", bt_rfk[req:%d]", -			   le16_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ])); - -		seq_printf(m, ", AOAC[RF_on:%d/RF_off:%d]", -			   le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on), -			   le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off)); +			p += scnprintf(p, end - p, " (WL FW Hang!!)"); + +		p += scnprintf(p, end - p, +			       "\n\r %-15s : send_ok:%d, send_fail:%d, recv:%d, ", +			       "[mailbox]", +			       le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok), +			       le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail), +			       le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv)); + +		p += scnprintf(p, end - p, +			       "A2DP_empty:%d(stop:%d/tx:%d/ack:%d/nack:%d)", +			       le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty), +			       le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl), +			       le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx), +			       le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack), +			       le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack)); + +		p += scnprintf(p, end - p, +			       "\n\r %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d/time:%dms]", +			       "[RFK/LPS]", cx->cnt_wl[BTC_WCNT_RFK_REQ], +			       cx->cnt_wl[BTC_WCNT_RFK_GO], +			       cx->cnt_wl[BTC_WCNT_RFK_REJECT], +			       cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT], +			       wl->rfk_info.proc_time); + +		p += scnprintf(p, end - p, ", bt_rfk[req:%d]", +			       le16_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ])); + +		p += scnprintf(p, end - p, ", AOAC[RF_on:%d/RF_off:%d]", +			       le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on), +			       le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off));  	} else { -		seq_printf(m, -			   "\n\r %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d (lps=%d/rf_off=%d)", -			   "[summary]", -			   pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail, -			   pfwinfo->cnt_c2h, -			   wl->status.map.lps, wl->status.map.rf_off); +		p += scnprintf(p, end - p, +			       "\n\r %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d (lps=%d/rf_off=%d)", +			       "[summary]", +			       pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail, +			       pfwinfo->cnt_c2h, +			       wl->status.map.lps, wl->status.map.rf_off);  	}  	for (i = 0; i < BTC_NCNT_NUM; i++)  		cnt_sum += dm->cnt_notify[i]; -	seq_printf(m, -		   "\n\r %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", -		   "[notify_cnt]", -		   cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], -		   cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); +	p += scnprintf(p, end - p, +		       "\n\r %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", +		       "[notify_cnt]", +		       cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], +		       cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); + +	p += scnprintf(p, end - p, +		       "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d", +		       cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], +		       cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], +		       cnt[BTC_NCNT_WL_STA]); -	seq_printf(m, -		   "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d", -		   cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], -		   cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], -		   cnt[BTC_NCNT_WL_STA]); +	p += scnprintf(p, end - p, +		       "\n\r %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, switch_chbw=%d, special_pkt=%d, ", +		       "[notify_cnt]", +		       cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH], +		       cnt[BTC_NCNT_SWITCH_BAND], cnt[BTC_NCNT_SWITCH_CHBW], +		       cnt[BTC_NCNT_SPECIAL_PACKET]); -	seq_printf(m, -		   "\n\r %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, switch_chbw=%d, special_pkt=%d, ", -		   "[notify_cnt]", -		   cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH], -		   cnt[BTC_NCNT_SWITCH_BAND], cnt[BTC_NCNT_SWITCH_CHBW], -		   cnt[BTC_NCNT_SPECIAL_PACKET]); +	p += scnprintf(p, end - p, +		       "timer=%d, customerize=%d, hub_msg=%d, chg_fw=%d, send_cc=%d", +		       cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CUSTOMERIZE], +		       rtwdev->btc.hubmsg_cnt, cnt[BTC_NCNT_RESUME_DL_FW], +		       cnt[BTC_NCNT_COUNTRYCODE]); -	seq_printf(m, "timer=%d, customerize=%d, hub_msg=%d, chg_fw=%d, send_cc=%d", -		   cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CUSTOMERIZE], -		   rtwdev->btc.hubmsg_cnt, cnt[BTC_NCNT_RESUME_DL_FW], -		   cnt[BTC_NCNT_COUNTRYCODE]); +	return p - buf;  } -static void _show_summary_v8(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_summary_v8(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)  {  	struct rtw89_btc_btf_fwinfo *pfwinfo = &rtwdev->btc.fwinfo;  	struct rtw89_btc_rpt_cmn_info *pcinfo = NULL; @@ -10858,153 +11679,175 @@ static void _show_summary_v8(struct rtw89_dev *rtwdev, struct seq_file *m)  	struct rtw89_btc_dm *dm = &rtwdev->btc.dm;  	struct rtw89_btc_wl_info *wl = &cx->wl;  	u32 *cnt = rtwdev->btc.dm.cnt_notify; +	char *p = buf, *end = buf + bufsz;  	u32 cnt_sum = 0;  	u8 i;  	if (!(dm->coex_info_map & BTC_COEX_INFO_SUMMARY)) -		return; +		return 0; -	seq_printf(m, "%s", "\n\r========== [Statistics] =========="); +	p += scnprintf(p, end - p, "%s", +		       "\n\r========== [Statistics] ==========");  	pcinfo = &pfwinfo->rpt_ctrl.cinfo;  	if (pcinfo->valid && wl->status.map.lps != BTC_LPS_RF_OFF &&  	    !wl->status.map.rf_off) {  		prptctrl = &pfwinfo->rpt_ctrl.finfo.v8; -		seq_printf(m, -			   "\n\r %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d, len:%d, max:fw-%d/drv-%d), ", -			   "[summary]", pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail, -			   le16_to_cpu(prptctrl->rpt_info.cnt_h2c), pfwinfo->cnt_c2h, -			   le16_to_cpu(prptctrl->rpt_info.cnt_c2h), -			   le16_to_cpu(prptctrl->rpt_info.len_c2h), -			   (prptctrl->rpt_len_max_h << 8) + prptctrl->rpt_len_max_l, -			   rtwdev->btc.ver->info_buf); - -		seq_printf(m, "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x", -			   pfwinfo->event[BTF_EVNT_RPT], -			   le16_to_cpu(prptctrl->rpt_info.cnt), -			   le32_to_cpu(prptctrl->rpt_info.en)); +		p += scnprintf(p, end - p, +			       "\n\r %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d, len:%d, max:fw-%d/drv-%d), ", +			       "[summary]", pfwinfo->cnt_h2c, +			       pfwinfo->cnt_h2c_fail, +			       le16_to_cpu(prptctrl->rpt_info.cnt_h2c), +			       pfwinfo->cnt_c2h, +			       le16_to_cpu(prptctrl->rpt_info.cnt_c2h), +			       le16_to_cpu(prptctrl->rpt_info.len_c2h), +			       (prptctrl->rpt_len_max_h << 8) + prptctrl->rpt_len_max_l, +			       rtwdev->btc.ver->info_buf); + +		p += scnprintf(p, end - p, +			       "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x", +			       pfwinfo->event[BTF_EVNT_RPT], +			       le16_to_cpu(prptctrl->rpt_info.cnt), +			       le32_to_cpu(prptctrl->rpt_info.en));  		if (dm->error.map.wl_fw_hang) -			seq_puts(m, " (WL FW Hang!!)"); - -		seq_printf(m, "\n\r %-15s : send_ok:%d, send_fail:%d, recv:%d, ", -			   "[mailbox]", le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok), -			   le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail), -			   le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv)); - -		seq_printf(m, "A2DP_empty:%d(stop:%d/tx:%d/ack:%d/nack:%d)", -			   le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty), -			   le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl), -			   le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx), -			   le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack), -			   le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack)); - -		seq_printf(m, -			   "\n\r %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d/time:%dms]", -			   "[RFK/LPS]", cx->cnt_wl[BTC_WCNT_RFK_REQ], -			   cx->cnt_wl[BTC_WCNT_RFK_GO], -			   cx->cnt_wl[BTC_WCNT_RFK_REJECT], -			   cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT], -			   wl->rfk_info.proc_time); - -		seq_printf(m, ", bt_rfk[req:%d]", -			   le16_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ])); - -		seq_printf(m, ", AOAC[RF_on:%d/RF_off:%d]", -			   le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on), -			   le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off)); +			p += scnprintf(p, end - p, " (WL FW Hang!!)"); + +		p += scnprintf(p, end - p, +			       "\n\r %-15s : send_ok:%d, send_fail:%d, recv:%d, ", +			       "[mailbox]", +			       le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok), +			       le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail), +			       le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv)); + +		p += scnprintf(p, end - p, +			       "A2DP_empty:%d(stop:%d/tx:%d/ack:%d/nack:%d)", +			       le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty), +			       le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl), +			       le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx), +			       le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack), +			       le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack)); + +		p += scnprintf(p, end - p, +			       "\n\r %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d/time:%dms]", +			       "[RFK/LPS]", cx->cnt_wl[BTC_WCNT_RFK_REQ], +			       cx->cnt_wl[BTC_WCNT_RFK_GO], +			       cx->cnt_wl[BTC_WCNT_RFK_REJECT], +			       cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT], +			       wl->rfk_info.proc_time); + +		p += scnprintf(p, end - p, ", bt_rfk[req:%d]", +			       le16_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ])); + +		p += scnprintf(p, end - p, ", AOAC[RF_on:%d/RF_off:%d]", +			       le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on), +			       le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off));  	} else { -		seq_printf(m, -			   "\n\r %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d (lps=%d/rf_off=%d)", -			   "[summary]", -			   pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail, -			   pfwinfo->cnt_c2h, -			   wl->status.map.lps, wl->status.map.rf_off); +		p += scnprintf(p, end - p, +			       "\n\r %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d (lps=%d/rf_off=%d)", +			       "[summary]", +			       pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail, +			       pfwinfo->cnt_c2h, +			       wl->status.map.lps, wl->status.map.rf_off);  	}  	for (i = 0; i < BTC_NCNT_NUM; i++)  		cnt_sum += dm->cnt_notify[i]; -	seq_printf(m, -		   "\n\r %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", -		   "[notify_cnt]", -		   cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], -		   cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); +	p += scnprintf(p, end - p, +		       "\n\r %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", +		       "[notify_cnt]", +		       cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], +		       cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); -	seq_printf(m, -		   "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d", -		   cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], -		   cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], -		   cnt[BTC_NCNT_WL_STA]); +	p += scnprintf(p, end - p, +		       "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d", +		       cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], +		       cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], +		       cnt[BTC_NCNT_WL_STA]); -	seq_printf(m, -		   "\n\r %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, switch_chbw=%d, special_pkt=%d, ", -		   "[notify_cnt]", -		   cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH], -		   cnt[BTC_NCNT_SWITCH_BAND], cnt[BTC_NCNT_SWITCH_CHBW], -		   cnt[BTC_NCNT_SPECIAL_PACKET]); +	p += scnprintf(p, end - p, +		       "\n\r %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, switch_chbw=%d, special_pkt=%d, ", +		       "[notify_cnt]", +		       cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH], +		       cnt[BTC_NCNT_SWITCH_BAND], cnt[BTC_NCNT_SWITCH_CHBW], +		       cnt[BTC_NCNT_SPECIAL_PACKET]); -	seq_printf(m, "timer=%d, customerize=%d, hub_msg=%d, chg_fw=%d, send_cc=%d", -		   cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CUSTOMERIZE], -		   rtwdev->btc.hubmsg_cnt, cnt[BTC_NCNT_RESUME_DL_FW], -		   cnt[BTC_NCNT_COUNTRYCODE]); +	p += scnprintf(p, end - p, +		       "timer=%d, customerize=%d, hub_msg=%d, chg_fw=%d, send_cc=%d", +		       cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CUSTOMERIZE], +		       rtwdev->btc.hubmsg_cnt, cnt[BTC_NCNT_RESUME_DL_FW], +		       cnt[BTC_NCNT_COUNTRYCODE]); + +	return p - buf;  } -void rtw89_btc_dump_info(struct rtw89_dev *rtwdev, struct seq_file *m) +ssize_t rtw89_btc_dump_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)  { -	struct rtw89_fw_suit *fw_suit = &rtwdev->fw.normal;  	struct rtw89_btc *btc = &rtwdev->btc; +	struct rtw89_btc_ver *fwsubver = &btc->fwinfo.fw_subver;  	const struct rtw89_btc_ver *ver = btc->ver; -	struct rtw89_btc_cx *cx = &btc->cx; -	struct rtw89_btc_bt_info *bt = &cx->bt; - -	seq_puts(m, "=========================================\n"); -	seq_printf(m, "WL FW / BT FW		%d.%d.%d.%d / NA\n", -		   fw_suit->major_ver, fw_suit->minor_ver, -		   fw_suit->sub_ver, fw_suit->sub_idex); -	seq_printf(m, "manual			%d\n", btc->manual_ctrl); - -	seq_puts(m, "=========================================\n"); - -	seq_printf(m, "\n\r %-15s : raw_data[%02x %02x %02x %02x %02x %02x] (type:%s/cnt:%d/same:%d)", -		   "[bt_info]", -		   bt->raw_info[2], bt->raw_info[3], -		   bt->raw_info[4], bt->raw_info[5], -		   bt->raw_info[6], bt->raw_info[7], -		   bt->raw_info[0] == BTC_BTINFO_AUTO ? "auto" : "reply", -		   cx->cnt_bt[BTC_BCNT_INFOUPDATE], -		   cx->cnt_bt[BTC_BCNT_INFOSAME]); +	struct rtw89_btc_dm *dm = &btc->dm; +	char *p = buf, *end = buf + bufsz; -	seq_puts(m, "\n=========================================\n"); +	dm->cnt_notify[BTC_NCNT_SHOW_COEX_INFO]++; -	_show_cx_info(rtwdev, m); -	_show_wl_info(rtwdev, m); -	_show_bt_info(rtwdev, m); -	_show_dm_info(rtwdev, m); -	_show_fw_dm_msg(rtwdev, m); +	p += scnprintf(p, end - p, +		       "\n\n\n** Page:%3d/RunCNT:%3d **", +		       dm->cnt_notify[BTC_NCNT_SHOW_COEX_INFO], +		       dm->cnt_dm[BTC_DCNT_RUN]); +	p += scnprintf(p, end - p, +		       "\n========== [BTC FEATURE SUB VER] =========="); +	p += scnprintf(p, end - p, +		       "\n %-15s : fcxbtcrpt[%d/%d], fcxtdma[%d/%d], fcxslots[%d/%d], fcxcysta[%d/%d]", +		       "[FW/DRV]", fwsubver->fcxbtcrpt, ver->fcxbtcrpt, +		       fwsubver->fcxtdma, ver->fcxtdma, fwsubver->fcxslots, +		       ver->fcxslots, fwsubver->fcxcysta, ver->fcxcysta); +	p += scnprintf(p, end - p, +		       "\n %-15s : fcxstep[%d/%d], fcxnullsta[%d/%d], fcxmreg[%d/%d], fcxgpiodbg[%d/%d]", +		       "[FW/DRV]", fwsubver->fcxstep, ver->fcxstep, +		       fwsubver->fcxnullsta, ver->fcxnullsta, fwsubver->fcxmreg, +		       ver->fcxmreg, fwsubver->fcxgpiodbg, ver->fcxgpiodbg); +	p += scnprintf(p, end - p, +		       "\n %-15s : fcxbtver[%d/%d], fcxbtscan[%d/%d], fcxbtafh[%d/%d], fcxbtdevinfo[%d/%d]", +		       "[FW/DRV]", fwsubver->fcxbtver, ver->fcxbtver, +		       fwsubver->fcxbtscan, ver->fcxbtscan, fwsubver->fcxbtafh, +		       ver->fcxbtafh, fwsubver->fcxbtdevinfo, ver->fcxbtdevinfo); +	p += scnprintf(p, end - p, +		       "\n %-15s : fcxosi[%d/%d], fcxmlo[%d/%d],", +		       "[FW/DRV]", fwsubver->fcxosi, ver->fcxosi, +		       fwsubver->fcxmlo, ver->fcxmlo); + +	p += _show_cx_info(rtwdev, p, end - p); +	p += _show_wl_info(rtwdev, p, end - p); +	p += _show_bt_info(rtwdev, p, end - p); +	p += _show_dm_info(rtwdev, p, end - p); +	p += _show_fw_dm_msg(rtwdev, p, end - p);  	if (ver->fcxmreg == 1) -		_show_mreg_v1(rtwdev, m); +		p += _show_mreg_v1(rtwdev, p, end - p);  	else if (ver->fcxmreg == 2) -		_show_mreg_v2(rtwdev, m); +		p += _show_mreg_v2(rtwdev, p, end - p);  	else if (ver->fcxmreg == 7) -		_show_mreg_v7(rtwdev, m); +		p += _show_mreg_v7(rtwdev, p, end - p); -	_show_gpio_dbg(rtwdev, m); +	p += _show_gpio_dbg(rtwdev, p, end - p);  	if (ver->fcxbtcrpt == 1) -		_show_summary_v1(rtwdev, m); +		p += _show_summary_v1(rtwdev, p, end - p);  	else if (ver->fcxbtcrpt == 4) -		_show_summary_v4(rtwdev, m); +		p += _show_summary_v4(rtwdev, p, end - p);  	else if (ver->fcxbtcrpt == 5) -		_show_summary_v5(rtwdev, m); +		p += _show_summary_v5(rtwdev, p, end - p);  	else if (ver->fcxbtcrpt == 105) -		_show_summary_v105(rtwdev, m); +		p += _show_summary_v105(rtwdev, p, end - p);  	else if (ver->fcxbtcrpt == 7) -		_show_summary_v7(rtwdev, m); +		p += _show_summary_v7(rtwdev, p, end - p);  	else if (ver->fcxbtcrpt == 8) -		_show_summary_v8(rtwdev, m); +		p += _show_summary_v8(rtwdev, p, end - p); + +	return p - buf;  }  void rtw89_coex_recognize_ver(struct rtw89_dev *rtwdev) @@ -11037,3 +11880,24 @@ out:  	rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC] use version def[%d] = 0x%08x\n",  		    (int)(btc->ver - rtw89_btc_ver_defs), btc->ver->fw_ver_code);  } + +void rtw89_btc_ntfy_preserve_bt_time(struct rtw89_dev *rtwdev, u32 ms) +{ +	struct rtw89_btc_bt_link_info *bt_linfo = &rtwdev->btc.cx.bt.link_info; +	struct rtw89_btc_bt_a2dp_desc a2dp = bt_linfo->a2dp_desc; + +	if (test_bit(RTW89_FLAG_SER_HANDLING, rtwdev->flags)) +		return; + +	if (!a2dp.exist) +		return; + +	fsleep(ms * 1000); +} +EXPORT_SYMBOL(rtw89_btc_ntfy_preserve_bt_time); + +void rtw89_btc_ntfy_conn_rfk(struct rtw89_dev *rtwdev, bool state) +{ +	rtwdev->btc.cx.wl.rfk_info.con_rfk = state; +} +EXPORT_SYMBOL(rtw89_btc_ntfy_conn_rfk); diff --git a/sys/contrib/dev/rtw89/coex.h b/sys/contrib/dev/rtw89/coex.h index dbdb56e063ef..ea2c1e5d70f5 100644 --- a/sys/contrib/dev/rtw89/coex.h +++ b/sys/contrib/dev/rtw89/coex.h @@ -224,6 +224,13 @@ enum btc_wl_mode {  	BTC_WL_MODE_NUM,  }; +enum btc_mlo_rf_combin { +	BTC_MLO_RF_2_PLUS_0 = 0, +	BTC_MLO_RF_0_PLUS_2 = 1, +	BTC_MLO_RF_1_PLUS_1 = 2, +	BTC_MLO_RF_2_PLUS_2 = 3, +}; +  enum btc_wl_gpio_debug {  	BTC_DBG_GNT_BT = 0,  	BTC_DBG_GNT_WL = 1, @@ -267,10 +274,10 @@ void rtw89_btc_ntfy_scan_finish(struct rtw89_dev *rtwdev, u8 phy_idx);  void rtw89_btc_ntfy_switch_band(struct rtw89_dev *rtwdev, u8 phy_idx, u8 band);  void rtw89_btc_ntfy_specific_packet(struct rtw89_dev *rtwdev,  				    enum btc_pkt_type pkt_type); -void rtw89_btc_ntfy_eapol_packet_work(struct work_struct *work); -void rtw89_btc_ntfy_arp_packet_work(struct work_struct *work); -void rtw89_btc_ntfy_dhcp_packet_work(struct work_struct *work); -void rtw89_btc_ntfy_icmp_packet_work(struct work_struct *work); +void rtw89_btc_ntfy_eapol_packet_work(struct wiphy *wiphy, struct wiphy_work *work); +void rtw89_btc_ntfy_arp_packet_work(struct wiphy *wiphy, struct wiphy_work *work); +void rtw89_btc_ntfy_dhcp_packet_work(struct wiphy *wiphy, struct wiphy_work *work); +void rtw89_btc_ntfy_icmp_packet_work(struct wiphy *wiphy, struct wiphy_work *work);  void rtw89_btc_ntfy_role_info(struct rtw89_dev *rtwdev,  			      struct rtw89_vif_link *rtwvif_link,  			      struct rtw89_sta_link *rtwsta_link, @@ -282,14 +289,16 @@ void rtw89_btc_ntfy_wl_rfk(struct rtw89_dev *rtwdev, u8 phy_map,  void rtw89_btc_ntfy_wl_sta(struct rtw89_dev *rtwdev);  void rtw89_btc_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb,  			  u32 len, u8 class, u8 func); -void rtw89_btc_dump_info(struct rtw89_dev *rtwdev, struct seq_file *m); -void rtw89_coex_act1_work(struct work_struct *work); -void rtw89_coex_bt_devinfo_work(struct work_struct *work); -void rtw89_coex_rfk_chk_work(struct work_struct *work); +ssize_t rtw89_btc_dump_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz); +void rtw89_coex_act1_work(struct wiphy *wiphy, struct wiphy_work *work); +void rtw89_coex_bt_devinfo_work(struct wiphy *wiphy, struct wiphy_work *work); +void rtw89_coex_rfk_chk_work(struct wiphy *wiphy, struct wiphy_work *work);  void rtw89_coex_power_on(struct rtw89_dev *rtwdev);  void rtw89_btc_set_policy(struct rtw89_dev *rtwdev, u16 policy_type);  void rtw89_btc_set_policy_v1(struct rtw89_dev *rtwdev, u16 policy_type);  void rtw89_coex_recognize_ver(struct rtw89_dev *rtwdev); +void rtw89_btc_ntfy_preserve_bt_time(struct rtw89_dev *rtwdev, u32 ms); +void rtw89_btc_ntfy_conn_rfk(struct rtw89_dev *rtwdev, bool state);  static inline u8 rtw89_btc_phymap(struct rtw89_dev *rtwdev,  				  enum rtw89_phy_idx phy_idx, diff --git a/sys/contrib/dev/rtw89/core.c b/sys/contrib/dev/rtw89/core.c index e002af84f1d1..1220378d08cf 100644 --- a/sys/contrib/dev/rtw89/core.c +++ b/sys/contrib/dev/rtw89/core.c @@ -223,6 +223,24 @@ static const struct ieee80211_iface_combination rtw89_iface_combs[] = {  	},  }; +static const u8 rtw89_ext_capa_sta[] = { +	[0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING, +	[2] = WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT, +	[7] = WLAN_EXT_CAPA8_OPMODE_NOTIF, +}; + +static const struct wiphy_iftype_ext_capab rtw89_iftypes_ext_capa[] = { +	{ +		.iftype = NL80211_IFTYPE_STATION, +		.extended_capabilities = rtw89_ext_capa_sta, +		.extended_capabilities_mask = rtw89_ext_capa_sta, +		.extended_capabilities_len = sizeof(rtw89_ext_capa_sta), +		/* relevant only if EHT is supported */ +		.eml_capabilities = 0, +		.mld_capa_and_ops = 0, +	}, +}; +  #define RTW89_6GHZ_SPAN_HEAD 6145  #define RTW89_6GHZ_SPAN_IDX(center_freq) \  	((((int)(center_freq) - RTW89_6GHZ_SPAN_HEAD) / 5) / 2) @@ -231,6 +249,8 @@ static const struct ieee80211_iface_combination rtw89_iface_combs[] = {  	[RTW89_6GHZ_SPAN_IDX(center_freq)] = { \  		.sar_subband_low = RTW89_SAR_6GHZ_ ## subband_l, \  		.sar_subband_high = RTW89_SAR_6GHZ_ ## subband_h, \ +		.acpi_sar_subband_low = RTW89_ACPI_SAR_6GHZ_ ## subband_l, \ +		.acpi_sar_subband_high = RTW89_ACPI_SAR_6GHZ_ ## subband_h, \  		.ant_gain_subband_low = RTW89_ANT_GAIN_6GHZ_ ## subband_l, \  		.ant_gain_subband_high = RTW89_ANT_GAIN_6GHZ_ ## subband_h, \  	} @@ -319,15 +339,25 @@ static const struct ieee80211_supported_band rtw89_sband_6ghz = {  	.n_bitrates	= ARRAY_SIZE(rtw89_bitrates) - 4,  }; +static void __rtw89_traffic_stats_accu(struct rtw89_traffic_stats *stats, +				       struct sk_buff *skb, bool tx) +{ +	if (tx) { +		stats->tx_cnt++; +		stats->tx_unicast += skb->len; +	} else { +		stats->rx_cnt++; +		stats->rx_unicast += skb->len; +	} +} +  static void rtw89_traffic_stats_accu(struct rtw89_dev *rtwdev, -				     struct rtw89_traffic_stats *stats, -				     struct sk_buff *skb, bool tx) +				     struct rtw89_vif *rtwvif, +				     struct sk_buff *skb, +				     bool accu_dev, bool tx)  {  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; -	if (tx && ieee80211_is_assoc_req(hdr->frame_control)) -		rtw89_wow_parse_akm(rtwdev, skb); -  	if (!ieee80211_is_data(hdr->frame_control))  		return; @@ -335,12 +365,12 @@ static void rtw89_traffic_stats_accu(struct rtw89_dev *rtwdev,  	    is_multicast_ether_addr(hdr->addr1))  		return; -	if (tx) { -		stats->tx_cnt++; -		stats->tx_unicast += skb->len; -	} else { -		stats->rx_cnt++; -		stats->rx_unicast += skb->len; +	if (accu_dev) +		__rtw89_traffic_stats_accu(&rtwdev->stats, skb, tx); + +	if (rtwvif) { +		__rtw89_traffic_stats_accu(&rtwvif->stats, skb, tx); +		__rtw89_traffic_stats_accu(&rtwvif->stats_ps, skb, tx);  	}  } @@ -659,9 +689,17 @@ out:  static u8 rtw89_core_tx_get_mac_id(struct rtw89_dev *rtwdev,  				   struct rtw89_core_tx_request *tx_req)  { +	struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info;  	struct rtw89_vif_link *rtwvif_link = tx_req->rtwvif_link;  	struct rtw89_sta_link *rtwsta_link = tx_req->rtwsta_link; +	if (desc_info->mlo && !desc_info->sw_mld) { +		if (rtwsta_link) +			return rtw89_sta_get_main_macid(rtwsta_link->rtwsta); +		else +			return rtw89_vif_get_main_macid(rtwvif_link->rtwvif); +	} +  	if (!rtwsta_link)  		return rtwvif_link->mac_id; @@ -691,7 +729,7 @@ rtw89_core_tx_update_mgmt_info(struct rtw89_dev *rtwdev,  	struct sk_buff *skb = tx_req->skb;  	u8 qsel, ch_dma; -	qsel = desc_info->hiq ? RTW89_TX_QSEL_B0_HI : RTW89_TX_QSEL_B0_MGMT; +	qsel = rtw89_core_get_qsel_mgmt(rtwdev, tx_req);  	ch_dma = rtw89_core_get_ch_dma(rtwdev, qsel);  	desc_info->qsel = qsel; @@ -945,16 +983,17 @@ static enum btc_pkt_type  rtw89_core_tx_btc_spec_pkt_notify(struct rtw89_dev *rtwdev,  				  struct rtw89_core_tx_request *tx_req)  { +	struct wiphy *wiphy = rtwdev->hw->wiphy;  	struct sk_buff *skb = tx_req->skb;  	struct udphdr *udphdr;  	if (IEEE80211_SKB_CB(skb)->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO) { -		ieee80211_queue_work(rtwdev->hw, &rtwdev->btc.eapol_notify_work); +		wiphy_work_queue(wiphy, &rtwdev->btc.eapol_notify_work);  		return PACKET_EAPOL;  	}  	if (skb->protocol == htons(ETH_P_ARP)) { -		ieee80211_queue_work(rtwdev->hw, &rtwdev->btc.arp_notify_work); +		wiphy_work_queue(wiphy, &rtwdev->btc.arp_notify_work);  		return PACKET_ARP;  	} @@ -964,14 +1003,14 @@ rtw89_core_tx_btc_spec_pkt_notify(struct rtw89_dev *rtwdev,  		if (((udphdr->source == htons(67) && udphdr->dest == htons(68)) ||  		     (udphdr->source == htons(68) && udphdr->dest == htons(67))) &&  		    skb->len > 282) { -			ieee80211_queue_work(rtwdev->hw, &rtwdev->btc.dhcp_notify_work); +			wiphy_work_queue(wiphy, &rtwdev->btc.dhcp_notify_work);  			return PACKET_DHCP;  		}  	}  	if (skb->protocol == htons(ETH_P_IP) &&  	    ip_hdr(skb)->protocol == IPPROTO_ICMP) { -		ieee80211_queue_work(rtwdev->hw, &rtwdev->btc.icmp_notify_work); +		wiphy_work_queue(wiphy, &rtwdev->btc.icmp_notify_work);  		return PACKET_ICMP;  	} @@ -987,13 +1026,25 @@ rtw89_core_tx_wake(struct rtw89_dev *rtwdev,  	if (!RTW89_CHK_FW_FEATURE(TX_WAKE, &rtwdev->fw))  		return; -	if (!test_bit(RTW89_FLAG_LOW_POWER_MODE, rtwdev->flags)) -		return; +	switch (chip->chip_id) { +	case RTL8852BT: +		if (test_bit(RTW89_FLAG_LEISURE_PS, rtwdev->flags)) +			goto notify; +		break; +	case RTL8852C: +		if (test_bit(RTW89_FLAG_LOW_POWER_MODE, rtwdev->flags)) +			goto notify; +		break; +	default: +		if (test_bit(RTW89_FLAG_LOW_POWER_MODE, rtwdev->flags) && +		    tx_req->tx_type == RTW89_CORE_TX_TYPE_MGMT) +			goto notify; +		break; +	} -	if (chip->chip_id != RTL8852C && -	    tx_req->tx_type != RTW89_CORE_TX_TYPE_MGMT) -		return; +	return; +notify:  	rtw89_mac_notify_wake(rtwdev);  } @@ -1135,42 +1186,26 @@ int rtw89_h2c_tx(struct rtw89_dev *rtwdev,  	return 0;  } -int rtw89_core_tx_write(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif, -			struct ieee80211_sta *sta, struct sk_buff *skb, int *qsel) +static int rtw89_core_tx_write_link(struct rtw89_dev *rtwdev, +				    struct rtw89_vif_link *rtwvif_link, +				    struct rtw89_sta_link *rtwsta_link, +				    struct sk_buff *skb, int *qsel, bool sw_mld)  { -	struct rtw89_sta *rtwsta = sta_to_rtwsta_safe(sta); -	struct rtw89_vif *rtwvif = vif_to_rtwvif(vif); -	struct rtw89_core_tx_request tx_req = {0}; -	struct rtw89_sta_link *rtwsta_link = NULL; -	struct rtw89_vif_link *rtwvif_link; +	struct ieee80211_sta *sta = rtwsta_link_to_sta_safe(rtwsta_link); +	struct ieee80211_vif *vif = rtwvif_link_to_vif(rtwvif_link); +	struct rtw89_vif *rtwvif = rtwvif_link->rtwvif; +	struct rtw89_core_tx_request tx_req = {};  	int ret; -	/* By default, driver writes tx via the link on HW-0. And then, -	 * according to links' status, HW can change tx to another link. -	 */ - -	if (rtwsta) { -		rtwsta_link = rtw89_sta_get_link_inst(rtwsta, 0); -		if (unlikely(!rtwsta_link)) { -			rtw89_err(rtwdev, "tx: find no sta link on HW-0\n"); -			return -ENOLINK; -		} -	} - -	rtwvif_link = rtw89_vif_get_link_inst(rtwvif, 0); -	if (unlikely(!rtwvif_link)) { -		rtw89_err(rtwdev, "tx: find no vif link on HW-0\n"); -		return -ENOLINK; -	} -  	tx_req.skb = skb;  	tx_req.vif = vif;  	tx_req.sta = sta;  	tx_req.rtwvif_link = rtwvif_link;  	tx_req.rtwsta_link = rtwsta_link; +	tx_req.desc_info.sw_mld = sw_mld; -	rtw89_traffic_stats_accu(rtwdev, &rtwdev->stats, skb, true); -	rtw89_traffic_stats_accu(rtwdev, &rtwvif->stats, skb, true); +	rtw89_traffic_stats_accu(rtwdev, rtwvif, skb, true, true); +	rtw89_wow_parse_akm(rtwdev, skb);  	rtw89_core_tx_update_desc_info(rtwdev, &tx_req);  	rtw89_core_tx_wake(rtwdev, &tx_req); @@ -1186,6 +1221,33 @@ int rtw89_core_tx_write(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif,  	return 0;  } +int rtw89_core_tx_write(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif, +			struct ieee80211_sta *sta, struct sk_buff *skb, int *qsel) +{ +	struct rtw89_sta *rtwsta = sta_to_rtwsta_safe(sta); +	struct rtw89_vif *rtwvif = vif_to_rtwvif(vif); +	struct rtw89_sta_link *rtwsta_link = NULL; +	struct rtw89_vif_link *rtwvif_link; + +	if (rtwsta) { +		rtwsta_link = rtw89_get_designated_link(rtwsta); +		if (unlikely(!rtwsta_link)) { +			rtw89_err(rtwdev, "tx: find no sta designated link\n"); +			return -ENOLINK; +		} + +		rtwvif_link = rtwsta_link->rtwvif_link; +	} else { +		rtwvif_link = rtw89_get_designated_link(rtwvif); +		if (unlikely(!rtwvif_link)) { +			rtw89_err(rtwdev, "tx: find no vif designated link\n"); +			return -ENOLINK; +		} +	} + +	return rtw89_core_tx_write_link(rtwdev, rtwvif_link, rtwsta_link, skb, qsel, false); +} +  static __le32 rtw89_build_txwd_body0(struct rtw89_tx_desc_info *desc_info)  {  	u32 dword = FIELD_PREP(RTW89_TXWD_BODY0_WP_OFFSET, desc_info->wp_offset) | @@ -1413,7 +1475,9 @@ static __le32 rtw89_build_txwd_body2_v2(struct rtw89_tx_desc_info *desc_info)  static __le32 rtw89_build_txwd_body3_v2(struct rtw89_tx_desc_info *desc_info)  { -	u32 dword = FIELD_PREP(BE_TXD_BODY3_WIFI_SEQ, desc_info->seq); +	u32 dword = FIELD_PREP(BE_TXD_BODY3_WIFI_SEQ, desc_info->seq) | +		    FIELD_PREP(BE_TXD_BODY3_MLO_FLAG, desc_info->mlo) | +		    FIELD_PREP(BE_TXD_BODY3_IS_MLD_SW_EN, desc_info->sw_mld);  	return cpu_to_le32(dword);  } @@ -1666,10 +1730,7 @@ static void rtw89_core_rx_process_phy_ppdu_iter(void *data,  	u8 evm_pos = 0;  	int i; -	/* FIXME: For single link, taking link on HW-0 here is okay. But, when -	 * enabling multiple active links, we should determine the right link. -	 */ -	rtwsta_link = rtw89_sta_get_link_inst(rtwsta, 0); +	rtwsta_link = rtw89_sta_get_link_inst(rtwsta, phy_ppdu->phy_idx);  	if (unlikely(!rtwsta_link))  		return; @@ -1716,7 +1777,7 @@ static u16 rtw89_core_get_phy_status_ie_len(struct rtw89_dev *rtwdev,  		},  		[RTW89_CHIP_BE] = {  			32, 40, 24, 24, 8, 8, 8, 8, VAR_LEN, 8, VAR_LEN, 176, VAR_LEN, -			VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, 16, 24, VAR_LEN, +			VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, VAR_LEN, 88, 56, VAR_LEN,  			VAR_LEN, VAR_LEN, 0, 24, 24, 24, 24, 32, 32, 32, 32  		},  	}; @@ -1920,6 +1981,8 @@ static int rtw89_core_rx_parse_phy_sts(struct rtw89_dev *rtwdev,  #if defined(__linux__)  	pos = phy_ppdu->buf + PHY_STS_HDR_LEN; +	if (phy_ppdu->hdr_2_en) +		pos += PHY_STS_HDR_LEN;  	end = phy_ppdu->buf + phy_ppdu->len;  #elif defined(__FreeBSD__)  	pos = (u8 *)phy_ppdu->buf + PHY_STS_HDR_LEN; @@ -2106,10 +2169,21 @@ static void rtw89_stats_trigger_frame(struct rtw89_dev *rtwdev,  			break;  		if (aid == vif->cfg.aid) { -			enum nl80211_he_ru_alloc rua = rtw89_he_rua_to_ru_alloc(tf_rua >> 1); +			enum nl80211_he_ru_alloc rua;  			rtwvif->stats.rx_tf_acc++;  			rtwdev->stats.rx_tf_acc++; + +			/* The following only required for HE trigger frame, but we +			 * cannot use UL HE-SIG-A2 reserved subfield to identify it +			 * since some 11ax APs will fill it with all 0s, which will +			 * be misunderstood as EHT trigger frame. +			 */ +			if (bss_conf->eht_support) +				break; + +			rua = rtw89_he_rua_to_ru_alloc(tf_rua >> 1); +  			if (tf_bw == IEEE80211_TRIGGER_ULBW_160_80P80MHZ &&  			    rua <= NL80211_RATE_INFO_HE_RU_ALLOC_106)  				rtwvif_link->pwr_diff_en = true; @@ -2120,17 +2194,17 @@ static void rtw89_stats_trigger_frame(struct rtw89_dev *rtwdev,  	}  } -static void rtw89_cancel_6ghz_probe_work(struct work_struct *work) +static void rtw89_cancel_6ghz_probe_work(struct wiphy *wiphy, struct wiphy_work *work)  {  	struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,  						cancel_6ghz_probe_work);  	struct list_head *pkt_list = rtwdev->scan_info.pkt_list;  	struct rtw89_pktofld_info *info; -	mutex_lock(&rtwdev->mutex); +	lockdep_assert_wiphy(wiphy);  	if (!rtwdev->scanning) -		goto out; +		return;  	list_for_each_entry(info, &pkt_list[NL80211_BAND_6GHZ], list) {  		if (!info->cancel || !test_bit(info->id, rtwdev->pkt_offload)) @@ -2143,9 +2217,6 @@ static void rtw89_cancel_6ghz_probe_work(struct work_struct *work)  		 * since if during scanning, pkt_list is accessed in bottom half.  		 */  	} - -out: -	mutex_unlock(&rtwdev->mutex);  }  static void rtw89_core_cancel_6ghz_probe_tx(struct rtw89_dev *rtwdev, @@ -2161,6 +2232,11 @@ static void rtw89_core_cancel_6ghz_probe_tx(struct rtw89_dev *rtwdev,  	if (rx_status->band != NL80211_BAND_6GHZ)  		return; +	if (unlikely(!(rtwdev->chip->support_bands & BIT(NL80211_BAND_6GHZ)))) { +		rtw89_debug(rtwdev, RTW89_DBG_UNEXP, "invalid rx on unsupported 6 GHz\n"); +		return; +	} +  	ssid_ie = cfg80211_find_ie(WLAN_EID_SSID, ies, skb->len);  	list_for_each_entry(info, &pkt_list[NL80211_BAND_6GHZ], list) { @@ -2180,7 +2256,7 @@ static void rtw89_core_cancel_6ghz_probe_tx(struct rtw89_dev *rtwdev,  	}  	if (queue_work) -		ieee80211_queue_work(rtwdev->hw, &rtwdev->cancel_6ghz_probe_work); +		wiphy_work_queue(rtwdev->hw->wiphy, &rtwdev->cancel_6ghz_probe_work);  }  static void rtw89_vif_sync_bcn_tsf(struct rtw89_vif_link *rtwvif_link, @@ -2203,8 +2279,10 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac,  	struct rtw89_pkt_stat *pkt_stat = &rtwdev->phystat.cur_pkt_stat;  	struct rtw89_rx_desc_info *desc_info = iter_data->desc_info;  	struct sk_buff *skb = iter_data->skb; +	struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;  	struct rtw89_rx_phy_ppdu *phy_ppdu = iter_data->phy_ppdu; +	bool is_mld = ieee80211_vif_is_mld(vif);  	struct ieee80211_bss_conf *bss_conf;  	struct rtw89_vif_link *rtwvif_link;  	const u8 *bssid = iter_data->bssid; @@ -2216,10 +2294,7 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac,  	rcu_read_lock(); -	/* FIXME: For single link, taking link on HW-0 here is okay. But, when -	 * enabling multiple active links, we should determine the right link. -	 */ -	rtwvif_link = rtw89_vif_get_link_inst(rtwvif, 0); +	rtwvif_link = rtw89_vif_get_link_inst(rtwvif, desc_info->bb_sel);  	if (unlikely(!rtwvif_link))  		goto out; @@ -2235,6 +2310,11 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac,  	if (!ether_addr_equal(bss_conf->bssid, bssid))  		goto out; +	if (is_mld) { +		rx_status->link_valid = true; +		rx_status->link_id = rtwvif_link->link_id; +	} +  	if (ieee80211_is_beacon(hdr->frame_control)) {  		if (vif->type == NL80211_IFTYPE_STATION &&  		    !test_bit(RTW89_FLAG_WOWLAN, rtwdev->flags)) { @@ -2243,8 +2323,11 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac,  		}  		pkt_stat->beacon_nr++; -		if (phy_ppdu) +		if (phy_ppdu) {  			ewma_rssi_add(&rtwdev->phystat.bcn_rssi, phy_ppdu->rssi_avg); +			if (!test_bit(RTW89_FLAG_LOW_POWER_MODE, rtwdev->flags)) +				rtwvif_link->bcn_bw_idx = phy_ppdu->bw_idx; +		}  		pkt_stat->beacon_rate = desc_info->data_rate;  	} @@ -2255,7 +2338,7 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac,  	if (desc_info->data_rate < RTW89_HW_RATE_NR)  		pkt_stat->rx_rate_cnt[desc_info->data_rate]++; -	rtw89_traffic_stats_accu(rtwdev, &rtwvif->stats, skb, false); +	rtw89_traffic_stats_accu(rtwdev, rtwvif, skb, false, false);  out:  	rcu_read_unlock(); @@ -2268,7 +2351,7 @@ static void rtw89_core_rx_stats(struct rtw89_dev *rtwdev,  {  	struct rtw89_vif_rx_stats_iter_data iter_data; -	rtw89_traffic_stats_accu(rtwdev, &rtwdev->stats, skb, false); +	rtw89_traffic_stats_accu(rtwdev, NULL, skb, true, false);  	iter_data.rtwdev = rtwdev;  	iter_data.phy_ppdu = phy_ppdu; @@ -2434,6 +2517,84 @@ static void rtw89_core_validate_rx_signal(struct ieee80211_rx_status *rx_status)  		rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL;  } +static void rtw89_core_update_rx_freq_from_ie(struct rtw89_dev *rtwdev, +					      struct sk_buff *skb, +					      struct ieee80211_rx_status *rx_status) +{ +	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; +	size_t hdr_len, ielen; +	u8 *variable; +	int chan; + +	if (!rtwdev->chip->rx_freq_frome_ie) +		return; + +	if (!rtwdev->scanning) +		return; + +	if (ieee80211_is_beacon(mgmt->frame_control)) { +		variable = mgmt->u.beacon.variable; +		hdr_len = offsetof(struct ieee80211_mgmt, +				   u.beacon.variable); +	} else if (ieee80211_is_probe_resp(mgmt->frame_control)) { +		variable = mgmt->u.probe_resp.variable; +		hdr_len = offsetof(struct ieee80211_mgmt, +				   u.probe_resp.variable); +	} else { +		return; +	} + +	if (skb->len > hdr_len) +		ielen = skb->len - hdr_len; +	else +		return; + +	/* The parsing code for both 2GHz and 5GHz bands is the same in this +	 * function. +	 */ +	chan = cfg80211_get_ies_channel_number(variable, ielen, NL80211_BAND_2GHZ); +	if (chan == -1) +		return; + +	rx_status->band = chan > 14 ? RTW89_BAND_5G : RTW89_BAND_2G; +	rx_status->freq = ieee80211_channel_to_frequency(chan, rx_status->band); +} + +static void rtw89_core_correct_mcc_chan(struct rtw89_dev *rtwdev, +					struct rtw89_rx_desc_info *desc_info, +					struct ieee80211_rx_status *rx_status, +					struct rtw89_rx_phy_ppdu *phy_ppdu) +{ +	enum rtw89_chip_gen chip_gen = rtwdev->chip->chip_gen; +	struct rtw89_vif_link *rtwvif_link; +	struct rtw89_sta_link *rtwsta_link; +	const struct rtw89_chan *chan; +	u8 mac_id = desc_info->mac_id; +	enum rtw89_entity_mode mode; +	enum nl80211_band band; + +	mode = rtw89_get_entity_mode(rtwdev); +	if (likely(mode != RTW89_ENTITY_MODE_MCC)) +		return; + +	if (chip_gen == RTW89_CHIP_BE && phy_ppdu) +		mac_id = phy_ppdu->mac_id; + +	rcu_read_lock(); + +	rtwsta_link = rtw89_assoc_link_rcu_dereference(rtwdev, mac_id); +	if (!rtwsta_link) +		goto out; + +	rtwvif_link = rtwsta_link->rtwvif_link; +	chan = rtw89_chan_get(rtwdev, rtwvif_link->chanctx_idx); +	band = rtw89_hw_to_nl80211_band(chan->band_type); +	rx_status->freq = ieee80211_channel_to_frequency(chan->primary_channel, band); + +out: +	rcu_read_unlock(); +} +  static void rtw89_core_rx_to_mac80211(struct rtw89_dev *rtwdev,  				      struct rtw89_rx_phy_ppdu *phy_ppdu,  				      struct rtw89_rx_desc_info *desc_info, @@ -2451,6 +2612,8 @@ static void rtw89_core_rx_to_mac80211(struct rtw89_dev *rtwdev,  	rtw89_core_update_rx_status_by_ppdu(rtwdev, rx_status, phy_ppdu);  	rtw89_core_update_radiotap(rtwdev, skb_ppdu, rx_status);  	rtw89_core_validate_rx_signal(rx_status); +	rtw89_core_update_rx_freq_from_ie(rtwdev, skb_ppdu, rx_status); +	rtw89_core_correct_mcc_chan(rtwdev, desc_info, rx_status, phy_ppdu);  	/* In low power mode, it does RX in thread context. */  	local_bh_disable(); @@ -2490,7 +2653,8 @@ static void rtw89_core_rx_process_ppdu_sts(struct rtw89_dev *rtwdev,  					     .len = skb->len,  					     .to_self = desc_info->addr1_match,  					     .rate = desc_info->data_rate, -					     .mac_id = desc_info->mac_id}; +					     .mac_id = desc_info->mac_id, +					     .phy_idx = desc_info->bb_sel};  	int ret;  	if (desc_info->mac_info_valid) { @@ -2601,6 +2765,7 @@ void rtw89_core_query_rxdesc_v2(struct rtw89_dev *rtwdev,  	desc_info->shift = le32_get_bits(rxd_s->dword0, BE_RXD_SHIFT_MASK);  	desc_info->long_rxdesc = le32_get_bits(rxd_s->dword0, BE_RXD_LONG_RXD);  	desc_info->pkt_type = le32_get_bits(rxd_s->dword0, BE_RXD_RPKT_TYPE_MASK); +	desc_info->bb_sel = le32_get_bits(rxd_s->dword0, BE_RXD_BB_SEL);  	if (desc_info->pkt_type == RTW89_CORE_RX_TYPE_PPDU_STAT)  		desc_info->mac_info_valid = true; @@ -2673,10 +2838,7 @@ void rtw89_core_stats_sta_rx_status_iter(void *data, struct ieee80211_sta *sta)  	struct rtw89_sta_link *rtwsta_link;  	u8 mac_id = iter_data->mac_id; -	/* FIXME: For single link, taking link on HW-0 here is okay. But, when -	 * enabling multiple active links, we should determine the right link. -	 */ -	rtwsta_link = rtw89_sta_get_link_inst(rtwsta, 0); +	rtwsta_link = rtw89_sta_get_link_inst(rtwsta, desc_info->bb_sel);  	if (unlikely(!rtwsta_link))  		return; @@ -2709,9 +2871,11 @@ static void rtw89_core_stats_sta_rx_status(struct rtw89_dev *rtwdev,  }  static void rtw89_core_update_rx_status(struct rtw89_dev *rtwdev, +					struct sk_buff *skb,  					struct rtw89_rx_desc_info *desc_info,  					struct ieee80211_rx_status *rx_status)  { +	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;  	const struct cfg80211_chan_def *chandef =  		rtw89_chandef_get(rtwdev, RTW89_CHANCTX_0);  	u16 data_rate; @@ -2723,6 +2887,10 @@ static void rtw89_core_update_rx_status(struct rtw89_dev *rtwdev,  	rx_status->freq = chandef->chan->center_freq;  	rx_status->band = chandef->chan->band; +	if (ieee80211_is_beacon(hdr->frame_control) || +	    ieee80211_is_probe_resp(hdr->frame_control)) +		rx_status->boottime_ns = ktime_get_boottime_ns(); +  	if (rtwdev->scanning &&  	    RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD, &rtwdev->fw)) {  		const struct rtw89_chan *cur = rtw89_scan_chan_get(rtwdev); @@ -2791,6 +2959,9 @@ static enum rtw89_ps_mode rtw89_update_ps_mode(struct rtw89_dev *rtwdev)  {  	const struct rtw89_chip_info *chip = rtwdev->chip; +	if (rtwdev->hci.type != RTW89_HCI_TYPE_PCIE) +		return RTW89_PS_MODE_NONE; +  	if (rtw89_disable_ps_mode || !chip->ps_mode_supported ||  	    RTW89_CHK_FW_FEATURE(NO_DEEP_PS, &rtwdev->fw))  		return RTW89_PS_MODE_NONE; @@ -2879,7 +3050,7 @@ void rtw89_core_rx(struct rtw89_dev *rtwdev,  	rx_status = IEEE80211_SKB_RXCB(skb);  	memset(rx_status, 0, sizeof(*rx_status)); -	rtw89_core_update_rx_status(rtwdev, desc_info, rx_status); +	rtw89_core_update_rx_status(rtwdev, skb, desc_info, rx_status);  	rtw89_core_rx_pkt_hdl(rtwdev, skb, desc_info);  	if (desc_info->long_rxdesc &&  	    BIT(desc_info->frame_type) & PPDU_FILTER_BITMAP) @@ -3125,9 +3296,9 @@ static bool rtw89_core_txq_agg_wait(struct rtw89_dev *rtwdev,  	if (!rtwsta)  		return false; -	rtwsta_link = rtw89_sta_get_link_inst(rtwsta, 0); +	rtwsta_link = rtw89_get_designated_link(rtwsta);  	if (unlikely(!rtwsta_link)) { -		rtw89_err(rtwdev, "agg wait: find no link on HW-0\n"); +		rtw89_err(rtwdev, "agg wait: find no designated link\n");  		return false;  	} @@ -3196,13 +3367,14 @@ static void rtw89_core_txq_schedule(struct rtw89_dev *rtwdev, u8 ac, bool *reinv  	ieee80211_txq_schedule_end(hw, ac);  } -static void rtw89_ips_work(struct work_struct *work) +static void rtw89_ips_work(struct wiphy *wiphy, struct wiphy_work *work)  {  	struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,  						ips_work); -	mutex_lock(&rtwdev->mutex); + +	lockdep_assert_wiphy(wiphy); +  	rtw89_enter_ips_by_hwflags(rtwdev); -	mutex_unlock(&rtwdev->mutex);  }  static void rtw89_core_txq_work(struct work_struct *w) @@ -3286,13 +3458,15 @@ static void rtw89_core_handle_sta_pending_tx(struct rtw89_dev *rtwdev,  					  rtwvif_link);  } -static int rtw89_core_send_nullfunc(struct rtw89_dev *rtwdev, -				    struct rtw89_vif_link *rtwvif_link, bool qos, bool ps) +int rtw89_core_send_nullfunc(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, +			     bool qos, bool ps, int timeout)  {  	struct ieee80211_vif *vif = rtwvif_link_to_vif(rtwvif_link);  	int link_id = ieee80211_vif_is_mld(vif) ? rtwvif_link->link_id : -1; +	struct rtw89_sta_link *rtwsta_link;  	struct ieee80211_sta *sta;  	struct ieee80211_hdr *hdr; +	struct rtw89_sta *rtwsta;  	struct sk_buff *skb;  	int ret, qsel; @@ -3305,6 +3479,7 @@ static int rtw89_core_send_nullfunc(struct rtw89_dev *rtwdev,  		ret = -EINVAL;  		goto out;  	} +	rtwsta = sta_to_rtwsta(sta);  	skb = ieee80211_nullfunc_get(rtwdev->hw, vif, link_id, qos);  	if (!skb) { @@ -3316,7 +3491,13 @@ static int rtw89_core_send_nullfunc(struct rtw89_dev *rtwdev,  	if (ps)  		hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); -	ret = rtw89_core_tx_write(rtwdev, vif, sta, skb, &qsel); +	rtwsta_link = rtwsta->links[rtwvif_link->link_id]; +	if (unlikely(!rtwsta_link)) { +		ret = -ENOLINK; +		goto out; +	} + +	ret = rtw89_core_tx_write_link(rtwdev, rtwvif_link, rtwsta_link, skb, &qsel, true);  	if (ret) {  		rtw89_warn(rtwdev, "nullfunc transmit failed: %d\n", ret);  		dev_kfree_skb_any(skb); @@ -3326,7 +3507,7 @@ static int rtw89_core_send_nullfunc(struct rtw89_dev *rtwdev,  	rcu_read_unlock();  	return rtw89_core_tx_kick_off_and_wait(rtwdev, skb, qsel, -					       RTW89_ROC_TX_TIMEOUT); +					       timeout);  out:  	rcu_read_unlock(); @@ -3336,6 +3517,9 @@ out:  void rtw89_roc_start(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)  {  	const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; +	struct rtw89_chanctx_pause_parm pause_parm = { +		.rsn = RTW89_CHANCTX_PAUSE_REASON_ROC, +	};  	struct ieee80211_hw *hw = rtwdev->hw;  	struct rtw89_roc *roc = &rtwvif->roc;  	struct rtw89_vif_link *rtwvif_link; @@ -3344,21 +3528,24 @@ void rtw89_roc_start(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)  	u32 reg;  	int ret; -	lockdep_assert_held(&rtwdev->mutex); +	lockdep_assert_wiphy(hw->wiphy);  	rtw89_leave_ips_by_hwflags(rtwdev);  	rtw89_leave_lps(rtwdev); -	rtwvif_link = rtw89_vif_get_link_inst(rtwvif, RTW89_ROC_BY_LINK_INDEX); +	rtwvif_link = rtw89_get_designated_link(rtwvif);  	if (unlikely(!rtwvif_link)) { -		rtw89_err(rtwdev, "roc start: find no link on HW-%u\n", -			  RTW89_ROC_BY_LINK_INDEX); +		rtw89_err(rtwdev, "roc start: find no designated link\n");  		return;  	} -	rtw89_chanctx_pause(rtwdev, RTW89_CHANCTX_PAUSE_REASON_ROC); +	roc->link_id = rtwvif_link->link_id; + +	pause_parm.trigger = rtwvif_link; +	rtw89_chanctx_pause(rtwdev, &pause_parm); -	ret = rtw89_core_send_nullfunc(rtwdev, rtwvif_link, true, true); +	ret = rtw89_core_send_nullfunc(rtwdev, rtwvif_link, true, true, +				       RTW89_ROC_TX_TIMEOUT);  	if (ret)  		rtw89_debug(rtwdev, RTW89_DBG_TXRX,  			    "roc send null-1 failed: %d\n", ret); @@ -3376,16 +3563,16 @@ void rtw89_roc_start(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)  	}  	cfg80211_chandef_create(&roc_chan, &roc->chan, NL80211_CHAN_NO_HT); -	rtw89_config_roc_chandef(rtwdev, rtwvif_link->chanctx_idx, &roc_chan); +	rtw89_config_roc_chandef(rtwdev, rtwvif_link, &roc_chan);  	rtw89_set_channel(rtwdev);  	reg = rtw89_mac_reg_by_idx(rtwdev, mac->rx_fltr, rtwvif_link->mac_idx);  	rtw89_write32_clr(rtwdev, reg, B_AX_A_UC_CAM_MATCH | B_AX_A_BC_CAM_MATCH);  	ieee80211_ready_on_channel(hw); -	cancel_delayed_work(&rtwvif->roc.roc_work); -	ieee80211_queue_delayed_work(hw, &rtwvif->roc.roc_work, -				     msecs_to_jiffies(rtwvif->roc.duration)); +	wiphy_delayed_work_cancel(hw->wiphy, &rtwvif->roc.roc_work); +	wiphy_delayed_work_queue(hw->wiphy, &rtwvif->roc.roc_work, +				 msecs_to_jiffies(rtwvif->roc.duration));  }  void rtw89_roc_end(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) @@ -3398,17 +3585,17 @@ void rtw89_roc_end(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)  	u32 reg;  	int ret; -	lockdep_assert_held(&rtwdev->mutex); +	lockdep_assert_wiphy(hw->wiphy);  	ieee80211_remain_on_channel_expired(hw);  	rtw89_leave_ips_by_hwflags(rtwdev);  	rtw89_leave_lps(rtwdev); -	rtwvif_link = rtw89_vif_get_link_inst(rtwvif, RTW89_ROC_BY_LINK_INDEX); +	rtwvif_link = rtwvif->links[roc->link_id];  	if (unlikely(!rtwvif_link)) { -		rtw89_err(rtwdev, "roc end: find no link on HW-%u\n", -			  RTW89_ROC_BY_LINK_INDEX); +		rtw89_err(rtwdev, "roc end: find no link (link id %u)\n", +			  roc->link_id);  		return;  	} @@ -3416,9 +3603,10 @@ void rtw89_roc_end(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)  	rtw89_write32_mask(rtwdev, reg, B_AX_RX_FLTR_CFG_MASK, rtwdev->hal.rx_fltr);  	roc->state = RTW89_ROC_IDLE; -	rtw89_config_roc_chandef(rtwdev, rtwvif_link->chanctx_idx, NULL); +	rtw89_config_roc_chandef(rtwdev, rtwvif_link, NULL);  	rtw89_chanctx_proceed(rtwdev, NULL); -	ret = rtw89_core_send_nullfunc(rtwdev, rtwvif_link, true, false); +	ret = rtw89_core_send_nullfunc(rtwdev, rtwvif_link, true, false, +				       RTW89_ROC_TX_TIMEOUT);  	if (ret)  		rtw89_debug(rtwdev, RTW89_DBG_TXRX,  			    "roc send null-0 failed: %d\n", ret); @@ -3430,18 +3618,18 @@ void rtw89_roc_end(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)  	queue_work(rtwdev->txq_wq, &rtwdev->txq_work);  	if (hw->conf.flags & IEEE80211_CONF_IDLE) -		ieee80211_queue_delayed_work(hw, &roc->roc_work, -					     msecs_to_jiffies(RTW89_ROC_IDLE_TIMEOUT)); +		wiphy_delayed_work_queue(hw->wiphy, &roc->roc_work, +					 msecs_to_jiffies(RTW89_ROC_IDLE_TIMEOUT));  } -void rtw89_roc_work(struct work_struct *work) +void rtw89_roc_work(struct wiphy *wiphy, struct wiphy_work *work)  {  	struct rtw89_vif *rtwvif = container_of(work, struct rtw89_vif,  						roc.roc_work.work);  	struct rtw89_dev *rtwdev = rtwvif->rtwdev;  	struct rtw89_roc *roc = &rtwvif->roc; -	mutex_lock(&rtwdev->mutex); +	lockdep_assert_wiphy(wiphy);  	switch (roc->state) {  	case RTW89_ROC_IDLE: @@ -3454,14 +3642,25 @@ void rtw89_roc_work(struct work_struct *work)  	default:  		break;  	} - -	mutex_unlock(&rtwdev->mutex);  }  static enum rtw89_tfc_lv rtw89_get_traffic_level(struct rtw89_dev *rtwdev, -						 u32 throughput, u64 cnt) +						 u32 throughput, u64 cnt, +						 enum rtw89_tfc_interval interval)  { -	if (cnt < 100) +	u64 cnt_level; + +	switch (interval) { +	default: +	case RTW89_TFC_INTERVAL_100MS: +		cnt_level = 5; +		break; +	case RTW89_TFC_INTERVAL_2SEC: +		cnt_level = 100; +		break; +	} + +	if (cnt < cnt_level)  		return RTW89_TFC_IDLE;  	if (throughput > 50)  		return RTW89_TFC_HIGH; @@ -3473,13 +3672,14 @@ static enum rtw89_tfc_lv rtw89_get_traffic_level(struct rtw89_dev *rtwdev,  }  static bool rtw89_traffic_stats_calc(struct rtw89_dev *rtwdev, -				     struct rtw89_traffic_stats *stats) +				     struct rtw89_traffic_stats *stats, +				     enum rtw89_tfc_interval interval)  {  	enum rtw89_tfc_lv tx_tfc_lv = stats->tx_tfc_lv;  	enum rtw89_tfc_lv rx_tfc_lv = stats->rx_tfc_lv; -	stats->tx_throughput_raw = (u32)(stats->tx_unicast >> RTW89_TP_SHIFT); -	stats->rx_throughput_raw = (u32)(stats->rx_unicast >> RTW89_TP_SHIFT); +	stats->tx_throughput_raw = rtw89_bytes_to_mbps(stats->tx_unicast, interval); +	stats->rx_throughput_raw = rtw89_bytes_to_mbps(stats->rx_unicast, interval);  	ewma_tp_add(&stats->tx_ewma_tp, stats->tx_throughput_raw);  	ewma_tp_add(&stats->rx_ewma_tp, stats->rx_throughput_raw); @@ -3487,9 +3687,9 @@ static bool rtw89_traffic_stats_calc(struct rtw89_dev *rtwdev,  	stats->tx_throughput = ewma_tp_read(&stats->tx_ewma_tp);  	stats->rx_throughput = ewma_tp_read(&stats->rx_ewma_tp);  	stats->tx_tfc_lv = rtw89_get_traffic_level(rtwdev, stats->tx_throughput, -						   stats->tx_cnt); +						   stats->tx_cnt, interval);  	stats->rx_tfc_lv = rtw89_get_traffic_level(rtwdev, stats->rx_throughput, -						   stats->rx_cnt); +						   stats->rx_cnt, interval);  	stats->tx_avg_len = stats->tx_cnt ?  			    DIV_ROUND_DOWN_ULL(stats->tx_unicast, stats->tx_cnt) : 0;  	stats->rx_avg_len = stats->rx_cnt ? @@ -3515,10 +3715,12 @@ static bool rtw89_traffic_stats_track(struct rtw89_dev *rtwdev)  	unsigned int link_id;  	bool tfc_changed; -	tfc_changed = rtw89_traffic_stats_calc(rtwdev, &rtwdev->stats); +	tfc_changed = rtw89_traffic_stats_calc(rtwdev, &rtwdev->stats, +					       RTW89_TFC_INTERVAL_2SEC);  	rtw89_for_each_rtwvif(rtwdev, rtwvif) { -		rtw89_traffic_stats_calc(rtwdev, &rtwvif->stats); +		rtw89_traffic_stats_calc(rtwdev, &rtwvif->stats, +					 RTW89_TFC_INTERVAL_2SEC);  		rtw89_vif_for_each_link(rtwvif, rtwvif_link, link_id)  			rtw89_fw_h2c_tp_offload(rtwdev, rtwvif_link); @@ -3538,8 +3740,8 @@ static void rtw89_enter_lps_track(struct rtw89_dev *rtwdev)  		if (rtwvif->offchan)  			continue; -		if (rtwvif->stats.tx_tfc_lv != RTW89_TFC_IDLE || -		    rtwvif->stats.rx_tfc_lv != RTW89_TFC_IDLE) +		if (rtwvif->stats_ps.tx_tfc_lv >= RTW89_TFC_MID || +		    rtwvif->stats_ps.rx_tfc_lv >= RTW89_TFC_MID)  			continue;  		vif = rtwvif_to_vif(rtwvif); @@ -3586,26 +3788,146 @@ void rtw89_traffic_stats_init(struct rtw89_dev *rtwdev,  	ewma_tp_init(&stats->rx_ewma_tp);  } -static void rtw89_track_work(struct work_struct *work) +#define RTW89_MLSR_GOTO_2GHZ_THRESHOLD -53 +#define RTW89_MLSR_EXIT_2GHZ_THRESHOLD -38 +static void rtw89_core_mlsr_link_decision(struct rtw89_dev *rtwdev, +					  struct rtw89_vif *rtwvif) +{ +	unsigned int sel_link_id = IEEE80211_MLD_MAX_NUM_LINKS; +	struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif); +	struct rtw89_vif_link *rtwvif_link; +	const struct rtw89_chan *chan; +	unsigned long usable_links; +	unsigned int link_id; +	u8 decided_bands; +	u8 rssi; + +	rssi = ewma_rssi_read(&rtwdev->phystat.bcn_rssi); +	if (unlikely(!rssi)) +		return; + +	if (RTW89_RSSI_RAW_TO_DBM(rssi) >= RTW89_MLSR_EXIT_2GHZ_THRESHOLD) +		decided_bands = BIT(RTW89_BAND_5G) | BIT(RTW89_BAND_6G); +	else if (RTW89_RSSI_RAW_TO_DBM(rssi) <= RTW89_MLSR_GOTO_2GHZ_THRESHOLD) +		decided_bands = BIT(RTW89_BAND_2G); +	else +		return; + +	usable_links = ieee80211_vif_usable_links(vif); + +	rtwvif_link = rtw89_get_designated_link(rtwvif); +	if (unlikely(!rtwvif_link)) +		goto select; + +	chan = rtw89_chan_get(rtwdev, rtwvif_link->chanctx_idx); +	if (decided_bands & BIT(chan->band_type)) +		return; + +	usable_links &= ~BIT(rtwvif_link->link_id); + +select: +	rcu_read_lock(); + +	for_each_set_bit(link_id, &usable_links, IEEE80211_MLD_MAX_NUM_LINKS) { +		struct ieee80211_bss_conf *link_conf; +		struct ieee80211_channel *channel; +		enum rtw89_band band; + +		link_conf = rcu_dereference(vif->link_conf[link_id]); +		if (unlikely(!link_conf)) +			continue; + +		channel = link_conf->chanreq.oper.chan; +		if (unlikely(!channel)) +			continue; + +		band = rtw89_nl80211_to_hw_band(channel->band); +		if (decided_bands & BIT(band)) { +			sel_link_id = link_id; +			break; +		} +	} + +	rcu_read_unlock(); + +	if (sel_link_id == IEEE80211_MLD_MAX_NUM_LINKS) +		return; + +	rtw89_core_mlsr_switch(rtwdev, rtwvif, sel_link_id); +} + +static void rtw89_core_mlo_track(struct rtw89_dev *rtwdev) +{ +	struct rtw89_hal *hal = &rtwdev->hal; +	struct ieee80211_vif *vif; +	struct rtw89_vif *rtwvif; + +	if (hal->disabled_dm_bitmap & BIT(RTW89_DM_MLO)) +		return; + +	rtw89_for_each_rtwvif(rtwdev, rtwvif) { +		vif = rtwvif_to_vif(rtwvif); +		if (!vif->cfg.assoc || !ieee80211_vif_is_mld(vif)) +			continue; + +		switch (rtwvif->mlo_mode) { +		case RTW89_MLO_MODE_MLSR: +			rtw89_core_mlsr_link_decision(rtwdev, rtwvif); +			break; +		default: +			break; +		} +	} +} + +static void rtw89_track_ps_work(struct wiphy *wiphy, struct wiphy_work *work) +{ +	struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, +						track_ps_work.work); +	struct rtw89_vif *rtwvif; + +	lockdep_assert_wiphy(wiphy); + +	if (test_bit(RTW89_FLAG_FORBIDDEN_TRACK_WORK, rtwdev->flags)) +		return; + +	if (!test_bit(RTW89_FLAG_RUNNING, rtwdev->flags)) +		return; + +	wiphy_delayed_work_queue(wiphy, &rtwdev->track_ps_work, +				 RTW89_TRACK_PS_WORK_PERIOD); + +	rtw89_for_each_rtwvif(rtwdev, rtwvif) +		rtw89_traffic_stats_calc(rtwdev, &rtwvif->stats_ps, +					 RTW89_TFC_INTERVAL_100MS); + +	if (rtwdev->scanning) +		return; + +	if (rtwdev->lps_enabled && !rtwdev->btc.lps) +		rtw89_enter_lps_track(rtwdev); +} + +static void rtw89_track_work(struct wiphy *wiphy, struct wiphy_work *work)  {  	struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,  						track_work.work);  	bool tfc_changed; -	if (test_bit(RTW89_FLAG_FORBIDDEN_TRACK_WROK, rtwdev->flags)) -		return; +	lockdep_assert_wiphy(wiphy); -	mutex_lock(&rtwdev->mutex); +	if (test_bit(RTW89_FLAG_FORBIDDEN_TRACK_WORK, rtwdev->flags)) +		return;  	if (!test_bit(RTW89_FLAG_RUNNING, rtwdev->flags)) -		goto out; +		return; -	ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->track_work, -				     RTW89_TRACK_WORK_PERIOD); +	wiphy_delayed_work_queue(wiphy, &rtwdev->track_work, +				 RTW89_TRACK_WORK_PERIOD);  	tfc_changed = rtw89_traffic_stats_track(rtwdev);  	if (rtwdev->scanning) -		goto out; +		return;  	rtw89_leave_lps(rtwdev); @@ -3624,15 +3946,13 @@ static void rtw89_track_work(struct work_struct *work)  	rtw89_phy_antdiv_track(rtwdev);  	rtw89_phy_ul_tb_ctrl_track(rtwdev);  	rtw89_phy_edcca_track(rtwdev); -	rtw89_tas_track(rtwdev); +	rtw89_sar_track(rtwdev);  	rtw89_chanctx_track(rtwdev);  	rtw89_core_rfkill_poll(rtwdev, false); +	rtw89_core_mlo_track(rtwdev);  	if (rtwdev->lps_enabled && !rtwdev->btc.lps)  		rtw89_enter_lps_track(rtwdev); - -out: -	mutex_unlock(&rtwdev->mutex);  }  u8 rtw89_core_acquire_bit_map(unsigned long *addr, unsigned long size) @@ -3666,7 +3986,7 @@ int rtw89_core_acquire_sta_ba_entry(struct rtw89_dev *rtwdev,  	u8 idx;  	int i; -	lockdep_assert_held(&rtwdev->mutex); +	lockdep_assert_wiphy(rtwdev->hw->wiphy);  	idx = rtw89_core_acquire_bit_map(cam_info->ba_cam_map, chip->bacam_num);  	if (idx == chip->bacam_num) { @@ -3710,7 +4030,7 @@ int rtw89_core_release_sta_ba_entry(struct rtw89_dev *rtwdev,  	struct rtw89_ba_cam_entry *entry = NULL, *tmp;  	u8 idx; -	lockdep_assert_held(&rtwdev->mutex); +	lockdep_assert_wiphy(rtwdev->hw->wiphy);  	list_for_each_entry_safe(entry, tmp, &rtwsta_link->ba_cam_list, list) {  		if (entry->tid != tid) @@ -3821,6 +4141,13 @@ int rtw89_core_sta_link_add(struct rtw89_dev *rtwdev,  		rtw89_btc_ntfy_role_info(rtwdev, rtwvif_link, rtwsta_link,  					 BTC_ROLE_MSTS_STA_CONN_START);  		rtw89_chip_rfk_channel(rtwdev, rtwvif_link); + +		if (vif->p2p) { +			rtw89_mac_get_tx_retry_limit(rtwdev, rtwsta_link, +						     &rtwsta_link->tx_retry); +			rtw89_mac_set_tx_retry_limit(rtwdev, rtwsta_link, false, 60); +		} +		rtw89_phy_dig_suspend(rtwdev);  	} else if (vif->type == NL80211_IFTYPE_AP || sta->tdls) {  		ret = rtw89_mac_set_macid_pause(rtwdev, rtwsta_link->mac_id, false);  		if (ret) { @@ -3858,6 +4185,9 @@ int rtw89_core_sta_link_disassoc(struct rtw89_dev *rtwdev,  	if (vif->type == NL80211_IFTYPE_STATION)  		rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, rtwvif_link, false); +	if (rtwvif_link->wifi_role == RTW89_WIFI_ROLE_P2P_CLIENT) +		rtw89_p2p_noa_once_deinit(rtwvif_link); +  	return 0;  } @@ -4002,6 +4332,11 @@ int rtw89_core_sta_link_assoc(struct rtw89_dev *rtwdev,  		}  		rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, rtwvif_link, true); + +		if (vif->p2p) +			rtw89_mac_set_tx_retry_limit(rtwdev, rtwsta_link, false, +						     rtwsta_link->tx_retry); +		rtw89_phy_dig_resume(rtwdev, false);  	}  	rtw89_assoc_link_set(rtwsta_link); @@ -4020,6 +4355,10 @@ int rtw89_core_sta_link_remove(struct rtw89_dev *rtwdev,  		rtw89_reg_6ghz_recalc(rtwdev, rtwvif_link, false);  		rtw89_btc_ntfy_role_info(rtwdev, rtwvif_link, rtwsta_link,  					 BTC_ROLE_MSTS_STA_DIS_CONN); + +		if (vif->p2p) +			rtw89_mac_set_tx_retry_limit(rtwdev, rtwsta_link, false, +						     rtwsta_link->tx_retry);  	} else if (vif->type == NL80211_IFTYPE_AP || sta->tdls) {  		ret = rtw89_fw_h2c_role_maintain(rtwdev, rtwvif_link, rtwsta_link,  						 RTW89_ROLE_REMOVE); @@ -4373,17 +4712,18 @@ static void rtw89_init_eht_cap(struct rtw89_dev *rtwdev,  #define RTW89_SBAND_IFTYPES_NR 2 -static void rtw89_init_he_eht_cap(struct rtw89_dev *rtwdev, -				  enum nl80211_band band, -				  struct ieee80211_supported_band *sband) +static int rtw89_init_he_eht_cap(struct rtw89_dev *rtwdev, +				 enum nl80211_band band, +				 struct ieee80211_supported_band *sband)  {  	struct ieee80211_sband_iftype_data *iftype_data;  	enum nl80211_iftype iftype;  	int idx = 0; -	iftype_data = kcalloc(RTW89_SBAND_IFTYPES_NR, sizeof(*iftype_data), GFP_KERNEL); +	iftype_data = devm_kcalloc(rtwdev->dev, RTW89_SBAND_IFTYPES_NR, +				   sizeof(*iftype_data), GFP_KERNEL);  	if (!iftype_data) -		return; +		return -ENOMEM;  	for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) {  		switch (iftype) { @@ -4408,118 +4748,162 @@ static void rtw89_init_he_eht_cap(struct rtw89_dev *rtwdev,  	}  	_ieee80211_set_sband_iftype_data(sband, iftype_data, idx); +	return 0; +} + +static struct ieee80211_supported_band * +rtw89_core_sband_dup(struct rtw89_dev *rtwdev, +		     const struct ieee80211_supported_band *sband) +{ +	struct ieee80211_supported_band *dup; + +	dup = devm_kmemdup(rtwdev->dev, sband, sizeof(*sband), GFP_KERNEL); +	if (!dup) +		return NULL; + +	dup->channels = devm_kmemdup(rtwdev->dev, sband->channels, +				     sizeof(*sband->channels) * sband->n_channels, +				     GFP_KERNEL); +	if (!dup->channels) +		return NULL; + +	dup->bitrates = devm_kmemdup(rtwdev->dev, sband->bitrates, +				     sizeof(*sband->bitrates) * sband->n_bitrates, +				     GFP_KERNEL); +	if (!dup->bitrates) +		return NULL; + +	return dup;  }  static int rtw89_core_set_supported_band(struct rtw89_dev *rtwdev)  {  	struct ieee80211_hw *hw = rtwdev->hw; -	struct ieee80211_supported_band *sband_2ghz = NULL, *sband_5ghz = NULL; -	struct ieee80211_supported_band *sband_6ghz = NULL; -	u32 size = sizeof(struct ieee80211_supported_band); +	struct ieee80211_supported_band *sband;  	u8 support_bands = rtwdev->chip->support_bands; +	int ret;  	if (support_bands & BIT(NL80211_BAND_2GHZ)) { -		sband_2ghz = kmemdup(&rtw89_sband_2ghz, size, GFP_KERNEL); -		if (!sband_2ghz) -			goto err; +		sband = rtw89_core_sband_dup(rtwdev, &rtw89_sband_2ghz); +		if (!sband) +			return -ENOMEM;  #if defined(__FreeBSD__)  		if (rtw_ht_support)  #endif -		rtw89_init_ht_cap(rtwdev, &sband_2ghz->ht_cap); +		rtw89_init_ht_cap(rtwdev, &sband->ht_cap); +#if defined(__FreeBSD__) +		if (rtw_eht_support) { +#endif +		ret = rtw89_init_he_eht_cap(rtwdev, NL80211_BAND_2GHZ, sband); +		if (ret) +			return ret;  #if defined(__FreeBSD__) -		if (rtw_eht_support) +		}  #endif -		rtw89_init_he_eht_cap(rtwdev, NL80211_BAND_2GHZ, sband_2ghz); -		hw->wiphy->bands[NL80211_BAND_2GHZ] = sband_2ghz; +		hw->wiphy->bands[NL80211_BAND_2GHZ] = sband;  	}  	if (support_bands & BIT(NL80211_BAND_5GHZ)) { -		sband_5ghz = kmemdup(&rtw89_sband_5ghz, size, GFP_KERNEL); -		if (!sband_5ghz) -			goto err; +		sband = rtw89_core_sband_dup(rtwdev, &rtw89_sband_5ghz); +		if (!sband) +			return -ENOMEM;  #if defined(__FreeBSD__)  		if (rtw_ht_support)  #endif -		rtw89_init_ht_cap(rtwdev, &sband_5ghz->ht_cap); +		rtw89_init_ht_cap(rtwdev, &sband->ht_cap);  #if defined(__FreeBSD__)  		if (rtw_vht_support)  #endif -		rtw89_init_vht_cap(rtwdev, &sband_5ghz->vht_cap); +		rtw89_init_vht_cap(rtwdev, &sband->vht_cap);  #if defined(__FreeBSD__) -		if (rtw_eht_support) +		if (rtw_eht_support) {  #endif -		rtw89_init_he_eht_cap(rtwdev, NL80211_BAND_5GHZ, sband_5ghz); -		hw->wiphy->bands[NL80211_BAND_5GHZ] = sband_5ghz; +		ret = rtw89_init_he_eht_cap(rtwdev, NL80211_BAND_5GHZ, sband); +		if (ret) +			return ret; +#if defined(__FreeBSD__) +		} +#endif +		hw->wiphy->bands[NL80211_BAND_5GHZ] = sband;  	} +#if defined(__FreeBSD__) +	if (rtw_eht_support) +#endif  	if (support_bands & BIT(NL80211_BAND_6GHZ)) { -		sband_6ghz = kmemdup(&rtw89_sband_6ghz, size, GFP_KERNEL); -		if (!sband_6ghz) -			goto err; -		rtw89_init_he_eht_cap(rtwdev, NL80211_BAND_6GHZ, sband_6ghz); -		hw->wiphy->bands[NL80211_BAND_6GHZ] = sband_6ghz; +		sband = rtw89_core_sband_dup(rtwdev, &rtw89_sband_6ghz); +		if (!sband) +			return -ENOMEM; +		ret = rtw89_init_he_eht_cap(rtwdev, NL80211_BAND_6GHZ, sband); +		if (ret) +			return ret; +		hw->wiphy->bands[NL80211_BAND_6GHZ] = sband;  	}  	return 0; - -err: -	hw->wiphy->bands[NL80211_BAND_2GHZ] = NULL; -	hw->wiphy->bands[NL80211_BAND_5GHZ] = NULL; -	hw->wiphy->bands[NL80211_BAND_6GHZ] = NULL; -	if (sband_2ghz) -		kfree((__force void *)sband_2ghz->iftype_data); -	if (sband_5ghz) -		kfree((__force void *)sband_5ghz->iftype_data); -	if (sband_6ghz) -		kfree((__force void *)sband_6ghz->iftype_data); -	kfree(sband_2ghz); -	kfree(sband_5ghz); -	kfree(sband_6ghz); -	return -ENOMEM; -} - -static void rtw89_core_clr_supported_band(struct rtw89_dev *rtwdev) -{ -	struct ieee80211_hw *hw = rtwdev->hw; - -	if (hw->wiphy->bands[NL80211_BAND_2GHZ]) -		kfree((__force void *)hw->wiphy->bands[NL80211_BAND_2GHZ]->iftype_data); -	if (hw->wiphy->bands[NL80211_BAND_5GHZ]) -		kfree((__force void *)hw->wiphy->bands[NL80211_BAND_5GHZ]->iftype_data); -	if (hw->wiphy->bands[NL80211_BAND_6GHZ]) -		kfree((__force void *)hw->wiphy->bands[NL80211_BAND_6GHZ]->iftype_data); -	kfree(hw->wiphy->bands[NL80211_BAND_2GHZ]); -	kfree(hw->wiphy->bands[NL80211_BAND_5GHZ]); -	kfree(hw->wiphy->bands[NL80211_BAND_6GHZ]); -	hw->wiphy->bands[NL80211_BAND_2GHZ] = NULL; -	hw->wiphy->bands[NL80211_BAND_5GHZ] = NULL; -	hw->wiphy->bands[NL80211_BAND_6GHZ] = NULL;  }  static void rtw89_core_ppdu_sts_init(struct rtw89_dev *rtwdev)  {  	int i; -	for (i = 0; i < RTW89_PHY_MAX; i++) +	for (i = 0; i < RTW89_PHY_NUM; i++)  		skb_queue_head_init(&rtwdev->ppdu_sts.rx_queue[i]); -	for (i = 0; i < RTW89_PHY_MAX; i++) +	for (i = 0; i < RTW89_PHY_NUM; i++)  		rtwdev->ppdu_sts.curr_rx_ppdu_cnt[i] = U8_MAX;  } -void rtw89_core_update_beacon_work(struct work_struct *work) +void rtw89_core_update_beacon_work(struct wiphy *wiphy, struct wiphy_work *work)  {  	struct rtw89_dev *rtwdev;  	struct rtw89_vif_link *rtwvif_link = container_of(work, struct rtw89_vif_link,  							  update_beacon_work); +	lockdep_assert_wiphy(wiphy); +  	if (rtwvif_link->net_type != RTW89_NET_TYPE_AP_MODE)  		return;  	rtwdev = rtwvif_link->rtwvif->rtwdev; -	mutex_lock(&rtwdev->mutex);  	rtw89_chip_h2c_update_beacon(rtwdev, rtwvif_link); -	mutex_unlock(&rtwdev->mutex); +} + +void rtw89_core_csa_beacon_work(struct wiphy *wiphy, struct wiphy_work *work) +{ +	struct rtw89_vif_link *rtwvif_link = +		container_of(work, struct rtw89_vif_link, csa_beacon_work.work); +	struct rtw89_vif *rtwvif = rtwvif_link->rtwvif; +	struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif); +	struct rtw89_dev *rtwdev = rtwvif->rtwdev; +	struct ieee80211_bss_conf *bss_conf; +	unsigned int delay; + +	lockdep_assert_wiphy(wiphy); + +	if (rtwvif_link->net_type != RTW89_NET_TYPE_AP_MODE) +		return; + +	rcu_read_lock(); + +	bss_conf = rtw89_vif_rcu_dereference_link(rtwvif_link, true); +	if (!bss_conf->csa_active) { +		rcu_read_unlock(); +		return; +	} + +	delay = ieee80211_tu_to_usec(bss_conf->beacon_int); + +	rcu_read_unlock(); + +	if (!ieee80211_beacon_cntdwn_is_complete(vif, rtwvif_link->link_id)) { +		rtw89_chip_h2c_update_beacon(rtwdev, rtwvif_link); + +		wiphy_delayed_work_queue(wiphy, &rtwvif_link->csa_beacon_work, +					 usecs_to_jiffies(delay)); +	} else { +		ieee80211_csa_finish(vif, rtwvif_link->link_id); +	}  }  int rtw89_wait_for_cond(struct rtw89_wait_info *wait, unsigned int cond) @@ -4628,16 +5012,16 @@ int rtw89_core_start(struct rtw89_dev *rtwdev)  	rtw89_mac_cfg_phy_rpt_bands(rtwdev, true);  	rtw89_mac_update_rts_threshold(rtwdev); -	rtw89_tas_reset(rtwdev); -  	ret = rtw89_hci_start(rtwdev);  	if (ret) {  		rtw89_err(rtwdev, "failed to start hci\n");  		return ret;  	} -	ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->track_work, -				     RTW89_TRACK_WORK_PERIOD); +	wiphy_delayed_work_queue(rtwdev->hw->wiphy, &rtwdev->track_work, +				 RTW89_TRACK_WORK_PERIOD); +	wiphy_delayed_work_queue(rtwdev->hw->wiphy, &rtwdev->track_ps_work, +				 RTW89_TRACK_PS_WORK_PERIOD);  	set_bit(RTW89_FLAG_RUNNING, rtwdev->flags); @@ -4651,8 +5035,11 @@ int rtw89_core_start(struct rtw89_dev *rtwdev)  void rtw89_core_stop(struct rtw89_dev *rtwdev)  { +	struct wiphy *wiphy = rtwdev->hw->wiphy;  	struct rtw89_btc *btc = &rtwdev->btc; +	lockdep_assert_wiphy(wiphy); +  	/* Prvent to stop twice; enter_ips and ops_stop */  	if (!test_bit(RTW89_FLAG_RUNNING, rtwdev->flags))  		return; @@ -4661,25 +5048,23 @@ void rtw89_core_stop(struct rtw89_dev *rtwdev)  	clear_bit(RTW89_FLAG_RUNNING, rtwdev->flags); -	mutex_unlock(&rtwdev->mutex); - -	cancel_work_sync(&rtwdev->c2h_work); -	cancel_work_sync(&rtwdev->cancel_6ghz_probe_work); -	cancel_work_sync(&btc->eapol_notify_work); -	cancel_work_sync(&btc->arp_notify_work); -	cancel_work_sync(&btc->dhcp_notify_work); -	cancel_work_sync(&btc->icmp_notify_work); +	wiphy_work_cancel(wiphy, &rtwdev->c2h_work); +	wiphy_work_cancel(wiphy, &rtwdev->cancel_6ghz_probe_work); +	wiphy_work_cancel(wiphy, &btc->eapol_notify_work); +	wiphy_work_cancel(wiphy, &btc->arp_notify_work); +	wiphy_work_cancel(wiphy, &btc->dhcp_notify_work); +	wiphy_work_cancel(wiphy, &btc->icmp_notify_work);  	cancel_delayed_work_sync(&rtwdev->txq_reinvoke_work); -	cancel_delayed_work_sync(&rtwdev->track_work); -	cancel_delayed_work_sync(&rtwdev->chanctx_work); -	cancel_delayed_work_sync(&rtwdev->coex_act1_work); -	cancel_delayed_work_sync(&rtwdev->coex_bt_devinfo_work); -	cancel_delayed_work_sync(&rtwdev->coex_rfk_chk_work); -	cancel_delayed_work_sync(&rtwdev->cfo_track_work); +	wiphy_delayed_work_cancel(wiphy, &rtwdev->track_work); +	wiphy_delayed_work_cancel(wiphy, &rtwdev->track_ps_work); +	wiphy_delayed_work_cancel(wiphy, &rtwdev->chanctx_work); +	wiphy_delayed_work_cancel(wiphy, &rtwdev->coex_act1_work); +	wiphy_delayed_work_cancel(wiphy, &rtwdev->coex_bt_devinfo_work); +	wiphy_delayed_work_cancel(wiphy, &rtwdev->coex_rfk_chk_work); +	wiphy_delayed_work_cancel(wiphy, &rtwdev->cfo_track_work); +	wiphy_delayed_work_cancel(wiphy, &rtwdev->mcc_prepare_done_work);  	cancel_delayed_work_sync(&rtwdev->forbid_ba_work); -	cancel_delayed_work_sync(&rtwdev->antdiv_work); - -	mutex_lock(&rtwdev->mutex); +	wiphy_delayed_work_cancel(wiphy, &rtwdev->antdiv_work);  	rtw89_btc_ntfy_poweroff(rtwdev);  	rtw89_hci_flush_queues(rtwdev, BIT(rtwdev->hw->queues) - 1, true); @@ -4804,6 +5189,7 @@ struct rtw89_vif_link *rtw89_vif_set_link(struct rtw89_vif *rtwvif,  	set_bit(index, rtwvif->links_inst_map);  	rtwvif->links[link_id] = rtwvif_link; +	list_add_tail(&rtwvif_link->dlink_schd, &rtwvif->dlink_pool);  	return rtwvif_link;  err: @@ -4824,6 +5210,7 @@ void rtw89_vif_unset_link(struct rtw89_vif *rtwvif, unsigned int link_id)  	index = rtw89_vif_link_inst_get_index(link);  	clear_bit(index, rtwvif->links_inst_map);  	*container = NULL; +	list_del(&link->dlink_schd);  }  struct rtw89_sta_link *rtw89_sta_set_link(struct rtw89_sta *rtwsta, @@ -4854,6 +5241,7 @@ struct rtw89_sta_link *rtw89_sta_set_link(struct rtw89_sta *rtwsta,  	set_bit(index, rtwsta->links_inst_map);  	rtwsta->links[link_id] = rtwsta_link; +	list_add_tail(&rtwsta_link->dlink_schd, &rtwsta->dlink_pool);  	return rtwsta_link;  err: @@ -4874,6 +5262,7 @@ void rtw89_sta_unset_link(struct rtw89_sta *rtwsta, unsigned int link_id)  	index = rtw89_sta_link_inst_get_index(link);  	clear_bit(index, rtwsta->links_inst_map);  	*container = NULL; +	list_del(&link->dlink_schd);  }  int rtw89_core_init(struct rtw89_dev *rtwdev) @@ -4890,35 +5279,38 @@ int rtw89_core_init(struct rtw89_dev *rtwdev)  			continue;  		INIT_LIST_HEAD(&rtwdev->scan_info.pkt_list[band]);  	} +	INIT_LIST_HEAD(&rtwdev->scan_info.chan_list);  	INIT_WORK(&rtwdev->ba_work, rtw89_core_ba_work);  	INIT_WORK(&rtwdev->txq_work, rtw89_core_txq_work);  	INIT_DELAYED_WORK(&rtwdev->txq_reinvoke_work, rtw89_core_txq_reinvoke_work); -	INIT_DELAYED_WORK(&rtwdev->track_work, rtw89_track_work); -	INIT_DELAYED_WORK(&rtwdev->chanctx_work, rtw89_chanctx_work); -	INIT_DELAYED_WORK(&rtwdev->coex_act1_work, rtw89_coex_act1_work); -	INIT_DELAYED_WORK(&rtwdev->coex_bt_devinfo_work, rtw89_coex_bt_devinfo_work); -	INIT_DELAYED_WORK(&rtwdev->coex_rfk_chk_work, rtw89_coex_rfk_chk_work); -	INIT_DELAYED_WORK(&rtwdev->cfo_track_work, rtw89_phy_cfo_track_work); +	wiphy_delayed_work_init(&rtwdev->track_work, rtw89_track_work); +	wiphy_delayed_work_init(&rtwdev->track_ps_work, rtw89_track_ps_work); +	wiphy_delayed_work_init(&rtwdev->chanctx_work, rtw89_chanctx_work); +	wiphy_delayed_work_init(&rtwdev->coex_act1_work, rtw89_coex_act1_work); +	wiphy_delayed_work_init(&rtwdev->coex_bt_devinfo_work, rtw89_coex_bt_devinfo_work); +	wiphy_delayed_work_init(&rtwdev->coex_rfk_chk_work, rtw89_coex_rfk_chk_work); +	wiphy_delayed_work_init(&rtwdev->cfo_track_work, rtw89_phy_cfo_track_work); +	wiphy_delayed_work_init(&rtwdev->mcc_prepare_done_work, rtw89_mcc_prepare_done_work);  	INIT_DELAYED_WORK(&rtwdev->forbid_ba_work, rtw89_forbid_ba_work); -	INIT_DELAYED_WORK(&rtwdev->antdiv_work, rtw89_phy_antdiv_work); +	wiphy_delayed_work_init(&rtwdev->antdiv_work, rtw89_phy_antdiv_work);  	rtwdev->txq_wq = alloc_workqueue("rtw89_tx_wq", WQ_UNBOUND | WQ_HIGHPRI, 0);  	if (!rtwdev->txq_wq)  		return -ENOMEM;  	spin_lock_init(&rtwdev->ba_lock);  	spin_lock_init(&rtwdev->rpwm_lock); -	mutex_init(&rtwdev->mutex);  	mutex_init(&rtwdev->rf_mutex);  	rtwdev->total_sta_assoc = 0;  	rtw89_init_wait(&rtwdev->mcc.wait); +	rtw89_init_wait(&rtwdev->mlo.wait);  	rtw89_init_wait(&rtwdev->mac.fw_ofld_wait);  	rtw89_init_wait(&rtwdev->wow.wait);  	rtw89_init_wait(&rtwdev->mac.ps_wait); -	INIT_WORK(&rtwdev->c2h_work, rtw89_fw_c2h_work); -	INIT_WORK(&rtwdev->ips_work, rtw89_ips_work); +	wiphy_work_init(&rtwdev->c2h_work, rtw89_fw_c2h_work); +	wiphy_work_init(&rtwdev->ips_work, rtw89_ips_work); +	wiphy_work_init(&rtwdev->cancel_6ghz_probe_work, rtw89_cancel_6ghz_probe_work);  	INIT_WORK(&rtwdev->load_firmware_work, rtw89_load_firmware_work); -	INIT_WORK(&rtwdev->cancel_6ghz_probe_work, rtw89_cancel_6ghz_probe_work);  	skb_queue_head_init(&rtwdev->c2h_queue);  	rtw89_core_ppdu_sts_init(rtwdev); @@ -4932,13 +5324,16 @@ int rtw89_core_init(struct rtw89_dev *rtwdev)  	if (rtwdev->chip->chip_gen == RTW89_CHIP_BE) {  		rtwdev->dbcc_en = true;  		rtwdev->mac.qta_mode = RTW89_QTA_DBCC; -		rtwdev->mlo_dbcc_mode = MLO_2_PLUS_0_1RF; +		rtwdev->mlo_dbcc_mode = MLO_1_PLUS_1_1RF;  	} -	INIT_WORK(&btc->eapol_notify_work, rtw89_btc_ntfy_eapol_packet_work); -	INIT_WORK(&btc->arp_notify_work, rtw89_btc_ntfy_arp_packet_work); -	INIT_WORK(&btc->dhcp_notify_work, rtw89_btc_ntfy_dhcp_packet_work); -	INIT_WORK(&btc->icmp_notify_work, rtw89_btc_ntfy_icmp_packet_work); +	rtwdev->bbs[RTW89_PHY_0].phy_idx = RTW89_PHY_0; +	rtwdev->bbs[RTW89_PHY_1].phy_idx = RTW89_PHY_1; + +	wiphy_work_init(&btc->eapol_notify_work, rtw89_btc_ntfy_eapol_packet_work); +	wiphy_work_init(&btc->arp_notify_work, rtw89_btc_ntfy_arp_packet_work); +	wiphy_work_init(&btc->dhcp_notify_work, rtw89_btc_ntfy_dhcp_packet_work); +	wiphy_work_init(&btc->icmp_notify_work, rtw89_btc_ntfy_icmp_packet_work);  	init_completion(&rtwdev->fw.req.completion);  	init_completion(&rtwdev->rfk_wait.completion); @@ -4947,7 +5342,7 @@ int rtw89_core_init(struct rtw89_dev *rtwdev)  	rtw89_ser_init(rtwdev);  	rtw89_entity_init(rtwdev); -	rtw89_tas_init(rtwdev); +	rtw89_sar_init(rtwdev);  	rtw89_phy_ant_gain_init(rtwdev);  	return 0; @@ -4958,11 +5353,10 @@ void rtw89_core_deinit(struct rtw89_dev *rtwdev)  {  	rtw89_ser_deinit(rtwdev);  	rtw89_unload_firmware(rtwdev); -	rtw89_fw_free_all_early_h2c(rtwdev); +	__rtw89_fw_free_all_early_h2c(rtwdev);  	destroy_workqueue(rtwdev->txq_wq);  	mutex_destroy(&rtwdev->rf_mutex); -	mutex_destroy(&rtwdev->mutex);  }  EXPORT_SYMBOL(rtw89_core_deinit); @@ -4971,17 +5365,16 @@ void rtw89_core_scan_start(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwv  {  	const struct rtw89_chan *chan = rtw89_chan_get(rtwdev,  						       rtwvif_link->chanctx_idx); +	struct rtw89_bb_ctx *bb = rtw89_get_bb_ctx(rtwdev, rtwvif_link->phy_idx);  	rtwdev->scanning = true; -	rtw89_leave_lps(rtwdev); -	if (hw_scan) -		rtw89_leave_ips_by_hwflags(rtwdev);  	ether_addr_copy(rtwvif_link->mac_addr, mac_addr);  	rtw89_btc_ntfy_scan_start(rtwdev, rtwvif_link->phy_idx, chan->band_type);  	rtw89_chip_rfk_scan(rtwdev, rtwvif_link, true);  	rtw89_hci_recalc_int_mit(rtwdev); -	rtw89_phy_config_edcca(rtwdev, true); +	rtw89_phy_config_edcca(rtwdev, bb, true); +	rtw89_tas_scan(rtwdev, true);  	rtw89_fw_h2c_cam(rtwdev, rtwvif_link, NULL, mac_addr);  } @@ -4990,6 +5383,8 @@ void rtw89_core_scan_complete(struct rtw89_dev *rtwdev,  			      struct rtw89_vif_link *rtwvif_link, bool hw_scan)  {  	struct ieee80211_bss_conf *bss_conf; +	struct rtw89_bb_ctx *bb; +	int ret;  	if (!rtwvif_link)  		return; @@ -5005,12 +5400,23 @@ void rtw89_core_scan_complete(struct rtw89_dev *rtwdev,  	rtw89_chip_rfk_scan(rtwdev, rtwvif_link, false);  	rtw89_btc_ntfy_scan_finish(rtwdev, rtwvif_link->phy_idx); -	rtw89_phy_config_edcca(rtwdev, false); +	bb = rtw89_get_bb_ctx(rtwdev, rtwvif_link->phy_idx); +	rtw89_phy_config_edcca(rtwdev, bb, false); +	rtw89_tas_scan(rtwdev, false); + +	if (hw_scan) { +		ret = rtw89_core_send_nullfunc(rtwdev, rtwvif_link, false, false, +					       RTW89_SCAN_NULL_TIMEOUT); +		if (ret) +			rtw89_debug(rtwdev, RTW89_DBG_TXRX, +				    "scan send null-0 failed: %d\n", ret); +	}  	rtwdev->scanning = false; -	rtwdev->dig.bypass_dig = true; +	rtw89_for_each_active_bb(rtwdev, bb) +		bb->dig.bypass_dig = true;  	if (hw_scan && (rtwdev->hw->conf.flags & IEEE80211_CONF_IDLE)) -		ieee80211_queue_work(rtwdev->hw, &rtwdev->ips_work); +		wiphy_work_queue(rtwdev->hw->wiphy, &rtwdev->ips_work);  }  static void rtw89_read_chip_ver(struct rtw89_dev *rtwdev) @@ -5085,6 +5491,77 @@ out:  	rtw89_load_txpwr_table(rtwdev, rtwdev->rfe_parms->byr_tbl);  } +int rtw89_core_mlsr_switch(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, +			   unsigned int link_id) +{ +	struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif); +	u16 usable_links = ieee80211_vif_usable_links(vif); +	u16 active_links = vif->active_links; +	struct rtw89_vif_link *target, *cur; +	int ret; + +	lockdep_assert_wiphy(rtwdev->hw->wiphy); + +	if (unlikely(!ieee80211_vif_is_mld(vif))) +		return -EOPNOTSUPP; + +	if (unlikely(link_id >= IEEE80211_MLD_MAX_NUM_LINKS || +		     !(usable_links & BIT(link_id)))) { +		rtw89_warn(rtwdev, "%s: link id %u is not usable\n", __func__, +			   link_id); +		return -ENOLINK; +	} + +	if (active_links == BIT(link_id)) +		return 0; + +	rtw89_debug(rtwdev, RTW89_DBG_STATE, "%s: switch to link id %u MLSR\n", +		    __func__, link_id); + +	rtw89_leave_lps(rtwdev); + +	ieee80211_stop_queues(rtwdev->hw); +	flush_work(&rtwdev->txq_work); + +	cur = rtw89_get_designated_link(rtwvif); + +	ret = ieee80211_set_active_links(vif, active_links | BIT(link_id)); +	if (ret) { +		rtw89_err(rtwdev, "%s: failed to activate link id %u\n", +			  __func__, link_id); +		goto wake_queue; +	} + +	target = rtwvif->links[link_id]; +	if (unlikely(!target)) { +		rtw89_err(rtwdev, "%s: failed to confirm link id %u\n", +			  __func__, link_id); + +		ieee80211_set_active_links(vif, active_links); +		ret = -EFAULT; +		goto wake_queue; +	} + +	if (likely(cur)) +		rtw89_fw_h2c_mlo_link_cfg(rtwdev, cur, false); + +	rtw89_fw_h2c_mlo_link_cfg(rtwdev, target, true); + +	ret = ieee80211_set_active_links(vif, BIT(link_id)); +	if (ret) +		rtw89_err(rtwdev, "%s: failed to inactivate links 0x%x\n", +			  __func__, active_links); + +	rtw89_chip_rfk_channel(rtwdev, target); + +	rtwvif->mlo_mode = RTW89_MLO_MODE_MLSR; + +wake_queue: +	ieee80211_wake_queues(rtwdev->hw); + +	return ret; +} +  static int rtw89_chip_efuse_info_setup(struct rtw89_dev *rtwdev)  {  	const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; @@ -5110,8 +5587,6 @@ static int rtw89_chip_efuse_info_setup(struct rtw89_dev *rtwdev)  	rtw89_hci_mac_pre_deinit(rtwdev); -	rtw89_mac_pwr_off(rtwdev); -  	return 0;  } @@ -5192,36 +5667,45 @@ int rtw89_chip_info_setup(struct rtw89_dev *rtwdev)  	rtw89_read_chip_ver(rtwdev); +	ret = rtw89_mac_pwr_on(rtwdev); +	if (ret) { +		rtw89_err(rtwdev, "failed to power on\n"); +		return ret; +	} +  	ret = rtw89_wait_firmware_completion(rtwdev);  	if (ret) {  		rtw89_err(rtwdev, "failed to wait firmware completion\n"); -		return ret; +		goto out;  	}  	ret = rtw89_fw_recognize(rtwdev);  	if (ret) {  		rtw89_err(rtwdev, "failed to recognize firmware\n"); -		return ret; +		goto out;  	}  	ret = rtw89_chip_efuse_info_setup(rtwdev);  	if (ret) -		return ret; +		goto out;  	ret = rtw89_fw_recognize_elements(rtwdev);  	if (ret) {  		rtw89_err(rtwdev, "failed to recognize firmware elements\n"); -		return ret; +		goto out;  	}  	ret = rtw89_chip_board_info_setup(rtwdev);  	if (ret) -		return ret; +		goto out;  	rtw89_core_setup_rfe_parms(rtwdev);  	rtwdev->ps_mode = rtw89_update_ps_mode(rtwdev); -	return 0; +out: +	rtw89_mac_pwr_off(rtwdev); + +	return ret;  }  EXPORT_SYMBOL(rtw89_chip_info_setup); @@ -5256,6 +5740,9 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev)  	int ret;  	int tx_headroom = IEEE80211_HT_CTL_LEN; +	if (rtwdev->hci.type == RTW89_HCI_TYPE_USB) +		tx_headroom += chip->txwd_body_size + chip->txwd_info_size; +  	hw->vif_data_size = struct_size_t(struct rtw89_vif, links_inst, n);  	hw->sta_data_size = struct_size_t(struct rtw89_sta, links_inst, n);  	hw->txq_data_size = sizeof(struct rtw89_txq); @@ -5287,6 +5774,7 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev)  	ieee80211_hw_set(hw, SINGLE_SCAN_ON_ALL_BANDS);  	ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID);  	ieee80211_hw_set(hw, WANT_MONITOR_VIF); +	ieee80211_hw_set(hw, CHANCTX_STA_CSA);  	if (chip->support_bandwidths & BIT(NL80211_CHAN_WIDTH_160))  		ieee80211_hw_set(hw, SUPPORTS_VHT_EXT_NSS_BW); @@ -5313,6 +5801,7 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev)  	hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS |  			    WIPHY_FLAG_TDLS_EXTERNAL_SETUP |  			    WIPHY_FLAG_AP_UAPSD | +			    WIPHY_FLAG_HAS_CHANNEL_SWITCH |  			    WIPHY_FLAG_SUPPORTS_EXT_KEK_KCK;  	if (!chip->support_rnr) @@ -5321,8 +5810,11 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev)  	if (chip->chip_gen == RTW89_CHIP_BE)  		hw->wiphy->flags |= WIPHY_FLAG_DISABLE_WEXT; -	if (rtwdev->support_mlo) +	if (rtwdev->support_mlo) {  		hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_MLO; +		hw->wiphy->iftype_ext_capab = rtw89_iftypes_ext_capa; +		hw->wiphy->num_iftype_ext_capab = ARRAY_SIZE(rtw89_iftypes_ext_capa); +	}  	hw->wiphy->features |= NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR; @@ -5353,7 +5845,7 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev)  	ret = rtw89_regd_setup(rtwdev);  	if (ret) {  		rtw89_err(rtwdev, "failed to set up regd\n"); -		goto err_free_supported_band; +		return ret;  	}  	hw->wiphy->sar_capa = &rtw89_sar_capa; @@ -5361,10 +5853,10 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev)  	ret = ieee80211_register_hw(hw);  	if (ret) {  		rtw89_err(rtwdev, "failed to register hw\n"); -		goto err_free_supported_band; +		return ret;  	} -	ret = rtw89_regd_init(rtwdev, rtw89_regd_notifier); +	ret = rtw89_regd_init_hint(rtwdev);  	if (ret) {  		rtw89_err(rtwdev, "failed to init regd\n");  		goto err_unregister_hw; @@ -5376,8 +5868,6 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev)  err_unregister_hw:  	ieee80211_unregister_hw(hw); -err_free_supported_band: -	rtw89_core_clr_supported_band(rtwdev);  	return ret;  } @@ -5388,7 +5878,6 @@ static void rtw89_core_unregister_hw(struct rtw89_dev *rtwdev)  	rtw89_rfkill_polling_deinit(rtwdev);  	ieee80211_unregister_hw(hw); -	rtw89_core_clr_supported_band(rtwdev);  }  int rtw89_core_register(struct rtw89_dev *rtwdev) @@ -5456,13 +5945,13 @@ struct rtw89_dev *rtw89_alloc_ieee80211_hw(struct device *device,  	if (!hw)  		goto err; -	/* TODO: When driver MLO arch. is done, determine whether to support MLO -	 * according to the following conditions. -	 * 1. run with chanctx_ops -	 * 2. chip->support_link_num != 0 -	 * 3. FW feature supports AP_LINK_PS +	/* Currently, our AP_LINK_PS handling only works for non-MLD softap +	 * or MLD-single-link softap. If RTW89_MLD_NON_STA_LINK_NUM enlarges, +	 * please tweak entire AP_LINKS_PS handling before supporting MLO.  	 */ -	support_mlo = false; +	support_mlo = !no_chanctx && chip->support_link_num && +		      RTW89_CHK_FW_FEATURE(NOTIFY_AP_INFO, &early_fw) && +		      RTW89_MLD_NON_STA_LINK_NUM == 1;  	hw->wiphy->iface_combinations = rtw89_iface_combs; diff --git a/sys/contrib/dev/rtw89/core.h b/sys/contrib/dev/rtw89/core.h index 1f1113fbfc75..1aead91341d1 100644 --- a/sys/contrib/dev/rtw89/core.h +++ b/sys/contrib/dev/rtw89/core.h @@ -23,11 +23,13 @@ struct rtw89_dev;  struct rtw89_pci_info;  struct rtw89_mac_gen_def;  struct rtw89_phy_gen_def; +struct rtw89_fw_blacklist;  struct rtw89_efuse_block_cfg;  struct rtw89_h2c_rf_tssi;  struct rtw89_fw_txpwr_track_cfg;  struct rtw89_phy_rfk_log_fmt;  struct rtw89_debugfs; +struct rtw89_regd_data;  extern const struct ieee80211_ops rtw89_ops; @@ -44,6 +46,7 @@ extern const struct ieee80211_ops rtw89_ops;  #define BYPASS_CR_DATA 0xbabecafe  #define RTW89_TRACK_WORK_PERIOD	round_jiffies_relative(HZ * 2) +#define RTW89_TRACK_PS_WORK_PERIOD msecs_to_jiffies(100)  #define RTW89_FORBID_BA_TIMER round_jiffies_relative(HZ * 4)  #define CFO_TRACK_MAX_USER 64  #define MAX_RSSI 110 @@ -134,6 +137,17 @@ enum rtw89_hci_type {  	RTW89_HCI_TYPE_PCIE,  	RTW89_HCI_TYPE_USB,  	RTW89_HCI_TYPE_SDIO, + +	RTW89_HCI_TYPE_NUM, +}; + +enum rtw89_hci_dle_type { +	RTW89_HCI_DLE_TYPE_PCIE, +	RTW89_HCI_DLE_TYPE_USB2, +	RTW89_HCI_DLE_TYPE_USB3, +	RTW89_HCI_DLE_TYPE_SDIO, + +	RTW89_HCI_DLE_TYPE_NUM,  };  enum rtw89_core_chip_id { @@ -724,6 +738,7 @@ enum rtw89_ofdma_type {  	RTW89_OFDMA_NUM,  }; +/* neither insert new in the middle, nor change any given definition */  enum rtw89_regulation_type {  	RTW89_WW	= 0,  	RTW89_ETSI	= 1, @@ -801,6 +816,7 @@ struct rtw89_rx_phy_ppdu {  	u8 rssi[RF_PATH_MAX];  	u8 mac_id;  	u8 chan_idx; +	u8 phy_idx;  	u8 ie;  	u16 rate;  	u8 rpl_avg; @@ -832,7 +848,7 @@ enum rtw89_mac_idx {  enum rtw89_phy_idx {  	RTW89_PHY_0 = 0,  	RTW89_PHY_1 = 1, -	RTW89_PHY_MAX +	RTW89_PHY_NUM,  };  #define __RTW89_MLD_MAX_LINK_NUM 2 @@ -1177,6 +1193,7 @@ struct rtw89_tx_desc_info {  	bool ldpc;  	bool upd_wlan_hdr;  	bool mlo; +	bool sw_mld;  };  struct rtw89_core_tx_request { @@ -1206,7 +1223,7 @@ struct rtw89_mac_ax_gnt {  struct rtw89_mac_ax_wl_act {  	u8 wlan_act_en;  	u8 wlan_act; -}; +} __packed;  #define RTW89_MAC_AX_COEX_GNT_NR 2  struct rtw89_mac_ax_coex_gnt { @@ -1323,6 +1340,7 @@ enum rtw89_btc_bt_state_cnt {  	BTC_BCNT_POLUT_NOW,  	BTC_BCNT_POLUT_DIFF,  	BTC_BCNT_RATECHG, +	BTC_BCNT_BTTXPWR_UPDATE,  	BTC_BCNT_NUM,  }; @@ -1381,6 +1399,11 @@ struct rtw89_btc_wl_smap {  	u32 emlsr: 1;  }; +enum rtw89_tfc_interval { +	RTW89_TFC_INTERVAL_100MS, +	RTW89_TFC_INTERVAL_2SEC, +}; +  enum rtw89_tfc_lv {  	RTW89_TFC_IDLE,  	RTW89_TFC_ULTRA_LOW, @@ -1389,7 +1412,6 @@ enum rtw89_tfc_lv {  	RTW89_TFC_HIGH,  }; -#define RTW89_TP_SHIFT 18 /* bytes/2s --> Mbps */  DECLARE_EWMA(tp, 10, 2);  struct rtw89_traffic_stats { @@ -1546,16 +1568,35 @@ struct rtw89_btc_u8_sta_chg {  };  struct rtw89_btc_wl_scan_info { -	u8 band[RTW89_PHY_MAX]; +	u8 band[RTW89_PHY_NUM];  	u8 phy_map;  	u8 rsvd;  };  struct rtw89_btc_wl_dbcc_info { -	u8 op_band[RTW89_PHY_MAX]; /* op band in each phy */ -	u8 scan_band[RTW89_PHY_MAX]; /* scan band in  each phy */ -	u8 real_band[RTW89_PHY_MAX]; -	u8 role[RTW89_PHY_MAX]; /* role in each phy */ +	u8 op_band[RTW89_PHY_NUM]; /* op band in each phy */ +	u8 scan_band[RTW89_PHY_NUM]; /* scan band in  each phy */ +	u8 real_band[RTW89_PHY_NUM]; +	u8 role[RTW89_PHY_NUM]; /* role in each phy */ +}; + +struct rtw89_btc_wl_mlo_info { +	u8 wmode[RTW89_PHY_NUM]; /* enum phl_mr_wmode */ +	u8 ch_type[RTW89_PHY_NUM]; /* enum phl_mr_ch_type */ +	u8 hwb_rf_band[RTW89_PHY_NUM]; /* enum band_type, RF-band for HW-band */ +	u8 path_rf_band[RTW89_PHY_NUM]; /* enum band_type, RF-band for PHY0/1 */ + +	u8 wtype; /* enum phl_mr_wtype */ +	u8 mrcx_mode; +	u8 mrcx_act_hwb_map; +	u8 mrcx_bt_slot_rsp; + +	u8 rf_combination; /* enum btc_mlo_rf_combin 0:2+0, 1:0+2, 2:1+1,3:2+2 */ +	u8 mlo_en; /* MLO enable */ +	u8 mlo_adie; /* a-die count */ +	u8 dual_hw_band_en; /* both 2 HW-band link exist */ + +	u32 link_status; /* enum mlo_dbcc_mode_type */  };  struct rtw89_btc_wl_active_role { @@ -1767,7 +1808,8 @@ struct rtw89_btc_wl_rfk_info {  	u32 phy_map: 2;  	u32 band: 2;  	u32 type: 8; -	u32 rsvd: 14; +	u32 con_rfk: 1; +	u32 rsvd: 13;  	u32 start_time;  	u32 proc_time; @@ -1791,6 +1833,13 @@ union rtw89_btc_bt_state_map {  #define BTC_BT_AFH_GROUP 12  #define BTC_BT_AFH_LE_GROUP 5 +struct rtw89_btc_bt_txpwr_desc { +	s8 br_dbm; +	s8 le_dbm; +	u8 br_gain_index; +	u8 le_gain_index; +}; +  struct rtw89_btc_bt_link_info {  	struct rtw89_btc_u8_sta_chg profile_cnt;  	struct rtw89_btc_bool_sta_chg multi_link; @@ -1800,6 +1849,7 @@ struct rtw89_btc_bt_link_info {  	struct rtw89_btc_bt_a2dp_desc a2dp_desc;  	struct rtw89_btc_bt_pan_desc pan_desc;  	union rtw89_btc_bt_state_map status; +	struct rtw89_btc_bt_txpwr_desc bt_txpwr_desc;  	u8 sut_pwr_level[BTC_PROFILE_MAX];  	u8 golden_rx_shift[BTC_PROFILE_MAX]; @@ -1895,6 +1945,7 @@ struct rtw89_btc_wl_info {  	struct rtw89_btc_wl_role_info_v8 role_info_v8;  	struct rtw89_btc_wl_scan_info scan_info;  	struct rtw89_btc_wl_dbcc_info dbcc_info; +	struct rtw89_btc_wl_mlo_info mlo_info;  	struct rtw89_btc_rf_para rf_para;  	struct rtw89_btc_wl_nhm nhm;  	union rtw89_btc_wl_state_map status; @@ -1904,15 +1955,19 @@ struct rtw89_btc_wl_info {  	u8 cn_report;  	u8 coex_mode;  	u8 pta_req_mac; -	u8 bt_polut_type[RTW89_PHY_MAX]; /* BT polluted WL-Tx type for phy0/1  */ +	u8 bt_polut_type[RTW89_PHY_NUM]; /* BT polluted WL-Tx type for phy0/1  */  	bool is_5g_hi_channel; +	bool go_client_exist; +	bool noa_exist;  	bool pta_reg_mac_chg;  	bool bg_mode;  	bool he_mode;  	bool scbd_change;  	bool fw_ver_mismatch;  	bool client_cnt_inc_2g; +	bool link_mode_chg; +	bool dbcc_chg;  	u32 scbd;  }; @@ -2065,6 +2120,7 @@ struct rtw89_btc_bt_info {  	union rtw89_btc_bt_rfk_info_map rfk_info;  	u8 raw_info[BTC_BTINFO_MAX]; /* raw bt info from mailbox */ +	u8 txpwr_info[BTC_BTINFO_MAX];  	u8 rssi_level;  	u32 scbd; @@ -2236,7 +2292,7 @@ struct rtw89_btc_fbtc_rpt_ctrl_v4 {  	struct rtw89_btc_fbtc_rpt_ctrl_wl_fw_info wl_fw_info;  	struct rtw89_btc_fbtc_rpt_ctrl_bt_mailbox bt_mbx_info;  	__le32 bt_cnt[BTC_BCNT_STA_MAX]; -	struct rtw89_mac_ax_gnt gnt_val[RTW89_PHY_MAX]; +	struct rtw89_mac_ax_gnt gnt_val[RTW89_PHY_NUM];  } __packed;  struct rtw89_btc_fbtc_rpt_ctrl_v5 { @@ -2244,7 +2300,7 @@ struct rtw89_btc_fbtc_rpt_ctrl_v5 {  	u8 rsvd;  	__le16 rsvd1; -	u8 gnt_val[RTW89_PHY_MAX][4]; +	u8 gnt_val[RTW89_PHY_NUM][4];  	__le16 bt_cnt[BTC_BCNT_STA_MAX];  	struct rtw89_btc_fbtc_rpt_ctrl_info_v5 rpt_info; @@ -2256,7 +2312,7 @@ struct rtw89_btc_fbtc_rpt_ctrl_v105 {  	u8 rsvd;  	__le16 rsvd1; -	u8 gnt_val[RTW89_PHY_MAX][4]; +	u8 gnt_val[RTW89_PHY_NUM][4];  	__le16 bt_cnt[BTC_BCNT_STA_MAX_V105];  	struct rtw89_btc_fbtc_rpt_ctrl_info_v5 rpt_info; @@ -2269,7 +2325,7 @@ struct rtw89_btc_fbtc_rpt_ctrl_v7 {  	u8 rsvd1;  	u8 rsvd2; -	u8 gnt_val[RTW89_PHY_MAX][4]; +	u8 gnt_val[RTW89_PHY_NUM][4];  	__le16 bt_cnt[BTC_BCNT_STA_MAX_V105];  	struct rtw89_btc_fbtc_rpt_ctrl_info_v8 rpt_info; @@ -2282,7 +2338,7 @@ struct rtw89_btc_fbtc_rpt_ctrl_v8 {  	u8 rpt_len_max_l; /* BTC_RPT_MAX bit0~7 */  	u8 rpt_len_max_h; /* BTC_RPT_MAX bit8~15 */ -	u8 gnt_val[RTW89_PHY_MAX][4]; +	u8 gnt_val[RTW89_PHY_NUM][4];  	__le16 bt_cnt[BTC_BCNT_STA_MAX_V105];  	struct rtw89_btc_fbtc_rpt_ctrl_info_v8 rpt_info; @@ -2903,12 +2959,32 @@ struct rtw89_btc_trx_info {  	u32 rx_err_ratio;  }; +enum btc_rf_path { +	BTC_RF_S0 = 0, +	BTC_RF_S1 = 1, +	BTC_RF_NUM, +}; + +struct rtw89_btc_fbtc_outsrc_set_info { +	u8 rf_band[BTC_RF_NUM]; /* 0:2G, 1:non-2G */ +	u8 btg_rx[BTC_RF_NUM]; +	u8 nbtg_tx[BTC_RF_NUM]; + +	struct rtw89_mac_ax_gnt gnt_set[BTC_RF_NUM]; /* refer to btc_gnt_ctrl */ +	struct rtw89_mac_ax_wl_act wlact_set[BTC_RF_NUM]; /* BT0/BT1 */ + +	u8 pta_req_hw_band; +	u8 rf_gbt_source; +} __packed; +  union rtw89_btc_fbtc_slot_u {  	struct rtw89_btc_fbtc_slot v1[CXST_MAX];  	struct rtw89_btc_fbtc_slot_v7 v7[CXST_MAX];  };  struct rtw89_btc_dm { +	struct rtw89_btc_fbtc_outsrc_set_info ost_info_last; /* outsrc API setup info */ +	struct rtw89_btc_fbtc_outsrc_set_info ost_info; /* outsrc API setup info */  	union rtw89_btc_fbtc_slot_u slot;  	union rtw89_btc_fbtc_slot_u slot_now;  	struct rtw89_btc_fbtc_tdma tdma; @@ -2998,6 +3074,7 @@ enum rtw89_btc_btf_fw_event {  	BTF_EVNT_BT_LEAUDIO_INFO = 7, /* fwc2hfunc > 1 */  	BTF_EVNT_BUF_OVERFLOW,  	BTF_EVNT_C2H_LOOPBACK, +	BTF_EVNT_BT_QUERY_TXPWR, /* fwc2hfunc > 3 */  	BTF_EVNT_MAX,  }; @@ -3046,6 +3123,7 @@ struct rtw89_btc_rpt_cmn_info {  union rtw89_btc_fbtc_btafh_info {  	struct rtw89_btc_fbtc_btafh v1;  	struct rtw89_btc_fbtc_btafh_v2 v2; +	struct rtw89_btc_fbtc_btafh_v7 v7;  };  struct rtw89_btc_report_ctrl_state { @@ -3115,31 +3193,6 @@ enum rtw89_btc_btfre_type {  	BTFRE_MAX,  }; -struct rtw89_btc_btf_fwinfo { -	u32 cnt_c2h; -	u32 cnt_h2c; -	u32 cnt_h2c_fail; -	u32 event[BTF_EVNT_MAX]; - -	u32 err[BTFRE_MAX]; -	u32 len_mismch; -	u32 fver_mismch; -	u32 rpt_en_map; - -	struct rtw89_btc_report_ctrl_state rpt_ctrl; -	struct rtw89_btc_rpt_fbtc_tdma rpt_fbtc_tdma; -	struct rtw89_btc_rpt_fbtc_slots rpt_fbtc_slots; -	struct rtw89_btc_rpt_fbtc_cysta rpt_fbtc_cysta; -	struct rtw89_btc_rpt_fbtc_step rpt_fbtc_step; -	struct rtw89_btc_rpt_fbtc_nullsta rpt_fbtc_nullsta; -	struct rtw89_btc_rpt_fbtc_mreg rpt_fbtc_mregval; -	struct rtw89_btc_rpt_fbtc_gpio_dbg rpt_fbtc_gpio_dbg; -	struct rtw89_btc_rpt_fbtc_btver rpt_fbtc_btver; -	struct rtw89_btc_rpt_fbtc_btscan rpt_fbtc_btscan; -	struct rtw89_btc_rpt_fbtc_btafh rpt_fbtc_btafh; -	struct rtw89_btc_rpt_fbtc_btdev rpt_fbtc_btdev; -}; -  struct rtw89_btc_ver {  	enum rtw89_core_chip_id chip_id;  	u32 fw_ver_code; @@ -3166,6 +3219,35 @@ struct rtw89_btc_ver {  	u8 drvinfo_type;  	u16 info_buf;  	u8 max_role_num; +	u8 fcxosi; +	u8 fcxmlo; +	u8 bt_desired; +}; + +struct rtw89_btc_btf_fwinfo { +	u32 cnt_c2h; +	u32 cnt_h2c; +	u32 cnt_h2c_fail; +	u32 event[BTF_EVNT_MAX]; + +	u32 err[BTFRE_MAX]; +	u32 len_mismch; +	u32 fver_mismch; +	u32 rpt_en_map; + +	struct rtw89_btc_ver fw_subver; +	struct rtw89_btc_report_ctrl_state rpt_ctrl; +	struct rtw89_btc_rpt_fbtc_tdma rpt_fbtc_tdma; +	struct rtw89_btc_rpt_fbtc_slots rpt_fbtc_slots; +	struct rtw89_btc_rpt_fbtc_cysta rpt_fbtc_cysta; +	struct rtw89_btc_rpt_fbtc_step rpt_fbtc_step; +	struct rtw89_btc_rpt_fbtc_nullsta rpt_fbtc_nullsta; +	struct rtw89_btc_rpt_fbtc_mreg rpt_fbtc_mregval; +	struct rtw89_btc_rpt_fbtc_gpio_dbg rpt_fbtc_gpio_dbg; +	struct rtw89_btc_rpt_fbtc_btver rpt_fbtc_btver; +	struct rtw89_btc_rpt_fbtc_btscan rpt_fbtc_btscan; +	struct rtw89_btc_rpt_fbtc_btafh rpt_fbtc_btafh; +	struct rtw89_btc_rpt_fbtc_btdev rpt_fbtc_btdev;  };  #define RTW89_BTC_POLICY_MAXLEN 512 @@ -3180,10 +3262,10 @@ struct rtw89_btc {  	struct rtw89_btc_btf_fwinfo fwinfo;  	struct rtw89_btc_dbg dbg; -	struct work_struct eapol_notify_work; -	struct work_struct arp_notify_work; -	struct work_struct dhcp_notify_work; -	struct work_struct icmp_notify_work; +	struct wiphy_work eapol_notify_work; +	struct wiphy_work arp_notify_work; +	struct wiphy_work dhcp_notify_work; +	struct wiphy_work icmp_notify_work;  	u32 bt_req_len; @@ -3380,9 +3462,11 @@ struct rtw89_sec_cam_entry {  struct rtw89_sta_link {  	struct rtw89_sta *rtwsta; +	struct list_head dlink_schd;  	unsigned int link_id;  	u8 mac_id; +	u8 tx_retry;  	bool er_cap;  	struct rtw89_vif_link *rtwvif_link;  	struct rtw89_ra_info ra; @@ -3418,6 +3502,7 @@ struct rtw89_efuse {  	u8 addr[ETH_ALEN];  	u8 rfe_type;  	char country_code[2]; +	u8 adc_td;  };  struct rtw89_phy_rate_pattern { @@ -3438,6 +3523,8 @@ struct rtw89_tx_skb_data {  	u8 hci_priv[];  }; +#define RTW89_SCAN_NULL_TIMEOUT 30 +  #define RTW89_ROC_IDLE_TIMEOUT 500  #define RTW89_ROC_TX_TIMEOUT 30  enum rtw89_roc_state { @@ -3446,14 +3533,13 @@ enum rtw89_roc_state {  	RTW89_ROC_MGMT,  }; -#define RTW89_ROC_BY_LINK_INDEX 0 -  struct rtw89_roc {  	struct ieee80211_channel chan; -	struct delayed_work roc_work; +	struct wiphy_delayed_work roc_work;  	enum ieee80211_roc_type type;  	enum rtw89_roc_state state;  	int duration; +	unsigned int link_id;  };  #define RTW89_P2P_MAX_NOA_NUM 2 @@ -3484,8 +3570,17 @@ struct rtw89_p2p_noa_setter {  	u8 noa_index;  }; +struct rtw89_ps_noa_once_handler { +	bool in_duration; +	u64 tsf_begin; +	u64 tsf_end; +	struct wiphy_delayed_work set_work; +	struct wiphy_delayed_work clr_work; +}; +  struct rtw89_vif_link {  	struct rtw89_vif *rtwvif; +	struct list_head dlink_schd;  	unsigned int link_id;  	bool chanctx_assigned; /* only valid when running with chanctx_ops */ @@ -3504,9 +3599,12 @@ struct rtw89_vif_link {  	u8 self_role;  	u8 wmm;  	u8 bcn_hit_cond; +	u8 bcn_bw_idx;  	u8 hit_rule;  	u8 last_noa_nr;  	u64 sync_bcn_tsf; +	u64 last_sync_bcn_tsf; +	bool rand_tsf_done;  	bool trigger;  	bool lsig_txop;  	u8 tgt_ind; @@ -3520,13 +3618,17 @@ struct rtw89_vif_link {  	bool pre_pwr_diff_en;  	bool pwr_diff_en;  	u8 def_tri_idx; -	struct work_struct update_beacon_work; +	struct wiphy_work update_beacon_work; +	struct wiphy_delayed_work csa_beacon_work;  	struct rtw89_addr_cam_entry addr_cam;  	struct rtw89_bssid_cam_entry bssid_cam;  	struct ieee80211_tx_queue_params tx_params[IEEE80211_NUM_ACS];  	struct rtw89_phy_rate_pattern rate_pattern;  	struct list_head general_pkt_list;  	struct rtw89_p2p_noa_setter p2p_noa; +	struct rtw89_ps_noa_once_handler noa_once; +	struct wiphy_delayed_work mcc_gc_detect_beacon_work; +	u8 detect_bcn_count;  };  enum rtw89_lv1_rcvy_step { @@ -3583,6 +3685,7 @@ struct rtw89_hci_ops {  struct rtw89_hci_info {  	const struct rtw89_hci_ops *ops;  	enum rtw89_hci_type type; +	enum rtw89_hci_dle_type dle_type;  	u32 rpwm_addr;  	u32 cpwm_addr;  	bool paused; @@ -3632,6 +3735,8 @@ struct rtw89_chip_ops {  			       enum rtw89_phy_idx phy_idx);  	int (*init_txpwr_unit)(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx);  	u8 (*get_thermal)(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path); +	u32 (*chan_to_rf18_val)(struct rtw89_dev *rtwdev, +				const struct rtw89_chan *chan);  	void (*ctrl_btg_bt_rx)(struct rtw89_dev *rtwdev, bool en,  			       enum rtw89_phy_idx phy_idx);  	void (*query_ppdu)(struct rtw89_dev *rtwdev, @@ -3678,6 +3783,11 @@ struct rtw89_chip_ops {  	int (*h2c_ampdu_cmac_tbl)(struct rtw89_dev *rtwdev,  				  struct rtw89_vif_link *rtwvif_link,  				  struct rtw89_sta_link *rtwsta_link); +	int (*h2c_txtime_cmac_tbl)(struct rtw89_dev *rtwdev, +				   struct rtw89_sta_link *rtwsta_link); +	int (*h2c_punctured_cmac_tbl)(struct rtw89_dev *rtwdev, +				      struct rtw89_vif_link *rtwvif_link, +				      u16 punctured);  	int (*h2c_default_dmac_tbl)(struct rtw89_dev *rtwdev,  				    struct rtw89_vif_link *rtwvif_link,  				    struct rtw89_sta_link *rtwsta_link); @@ -3762,7 +3872,7 @@ struct rtw89_scan_option {  	u16 slow_pd;  	u16 norm_cy;  	u8 opch_end; -	u16 delay; +	u16 delay; /* in unit of ms */  	u64 prohib_chan;  	enum rtw89_phy_idx band;  	enum rtw89_scan_be_operation operation; @@ -3984,7 +4094,11 @@ struct rtw89_rfe_parms {  	struct rtw89_txpwr_rule_2ghz rule_2ghz;  	struct rtw89_txpwr_rule_5ghz rule_5ghz;  	struct rtw89_txpwr_rule_6ghz rule_6ghz; +	struct rtw89_txpwr_rule_2ghz rule_da_2ghz; +	struct rtw89_txpwr_rule_5ghz rule_da_5ghz; +	struct rtw89_txpwr_rule_6ghz rule_da_6ghz;  	struct rtw89_tx_shape tx_shape; +	bool has_da;  };  struct rtw89_rfe_parms_conf { @@ -4089,9 +4203,15 @@ struct rtw89_rfe_data {  	struct rtw89_txpwr_lmt_2ghz_data lmt_2ghz;  	struct rtw89_txpwr_lmt_5ghz_data lmt_5ghz;  	struct rtw89_txpwr_lmt_6ghz_data lmt_6ghz; +	struct rtw89_txpwr_lmt_2ghz_data da_lmt_2ghz; +	struct rtw89_txpwr_lmt_5ghz_data da_lmt_5ghz; +	struct rtw89_txpwr_lmt_6ghz_data da_lmt_6ghz;  	struct rtw89_txpwr_lmt_ru_2ghz_data lmt_ru_2ghz;  	struct rtw89_txpwr_lmt_ru_5ghz_data lmt_ru_5ghz;  	struct rtw89_txpwr_lmt_ru_6ghz_data lmt_ru_6ghz; +	struct rtw89_txpwr_lmt_ru_2ghz_data da_lmt_ru_2ghz; +	struct rtw89_txpwr_lmt_ru_5ghz_data da_lmt_ru_5ghz; +	struct rtw89_txpwr_lmt_ru_6ghz_data da_lmt_ru_6ghz;  	struct rtw89_tx_shape_lmt_data tx_shape_lmt;  	struct rtw89_tx_shape_lmt_ru_data tx_shape_lmt_ru;  	struct rtw89_rfe_parms rfe_parms; @@ -4205,10 +4325,12 @@ struct rtw89_edcca_regs {  	u32 edcca_p_mask;  	u32 ppdu_level;  	u32 ppdu_mask; -	u32 rpt_a; -	u32 rpt_b; -	u32 rpt_sel; -	u32 rpt_sel_mask; +	struct rtw89_edcca_p_regs { +		u32 rpt_a; +		u32 rpt_b; +		u32 rpt_sel; +		u32 rpt_sel_mask; +	} p[RTW89_PHY_NUM];  	u32 rpt_sel_be;  	u32 rpt_sel_be_mask;  	u32 tx_collision_t2r_st; @@ -4247,6 +4369,7 @@ enum rtw89_chanctx_state {  enum rtw89_chanctx_callbacks {  	RTW89_CHANCTX_CALLBACK_PLACEHOLDER,  	RTW89_CHANCTX_CALLBACK_RFK, +	RTW89_CHANCTX_CALLBACK_TAS,  	NUM_OF_RTW89_CHANCTX_CALLBACKS,  }; @@ -4267,14 +4390,15 @@ struct rtw89_chip_info {  	bool try_ce_fw;  	u8 bbmcu_nr;  	u32 needed_fw_elms; +	const struct rtw89_fw_blacklist *fw_blacklist;  	u32 fifo_size;  	bool small_fifo_size;  	u32 dle_scc_rsvd_size;  	u16 max_amsdu_limit;  	bool dis_2g_40m_ul_ofdma;  	u32 rsvd_ple_ofst; -	const struct rtw89_hfc_param_ini *hfc_param_ini; -	const struct rtw89_dle_mem *dle_mem; +	const struct rtw89_hfc_param_ini *hfc_param_ini[RTW89_HCI_TYPE_NUM]; +	const struct rtw89_dle_mem *dle_mem[RTW89_HCI_DLE_TYPE_NUM];  	u8 wde_qempty_acq_grpnum;  	u8 wde_qempty_mgq_grpsel;  	u32 rf_base_addr[2]; @@ -4287,10 +4411,15 @@ struct rtw89_chip_info {  	bool support_unii4;  	bool support_rnr;  	bool support_ant_gain; +	bool support_tas; +	bool support_sar_by_ant;  	bool ul_tb_waveform_ctrl;  	bool ul_tb_pwr_diff; +	bool rx_freq_frome_ie;  	bool hw_sec_hdr;  	bool hw_mgmt_tx_encrypt; +	bool hw_tkip_crypto; +	bool hw_mlo_bmc_crypto;  	u8 rf_path_num;  	u8 tx_nss;  	u8 rx_nss; @@ -4334,7 +4463,6 @@ struct rtw89_chip_info {  	u32 para_ver;  	u32 wlcx_desired; -	u8 btcx_desired;  	u8 scbd;  	u8 mailbox; @@ -4474,11 +4602,21 @@ enum rtw89_fw_type {  	RTW89_FW_LOGFMT = 255,  }; +#define RTW89_FW_FEATURE_GROUP(_grp, _features...) \ +	RTW89_FW_FEATURE_##_grp##_MIN, \ +	__RTW89_FW_FEATURE_##_grp##_S = RTW89_FW_FEATURE_##_grp##_MIN - 1, \ +	_features \ +	__RTW89_FW_FEATURE_##_grp##_E, \ +	RTW89_FW_FEATURE_##_grp##_MAX = __RTW89_FW_FEATURE_##_grp##_E - 1 +  enum rtw89_fw_feature {  	RTW89_FW_FEATURE_OLD_HT_RA_FORMAT,  	RTW89_FW_FEATURE_SCAN_OFFLOAD,  	RTW89_FW_FEATURE_TX_WAKE, -	RTW89_FW_FEATURE_CRASH_TRIGGER, +	RTW89_FW_FEATURE_GROUP(CRASH_TRIGGER, +			       RTW89_FW_FEATURE_CRASH_TRIGGER_TYPE_0, +			       RTW89_FW_FEATURE_CRASH_TRIGGER_TYPE_1, +	),  	RTW89_FW_FEATURE_NO_PACKET_DROP,  	RTW89_FW_FEATURE_NO_DEEP_PS,  	RTW89_FW_FEATURE_NO_LPS_PG, @@ -4489,11 +4627,17 @@ enum rtw89_fw_feature {  	RTW89_FW_FEATURE_RFK_PRE_NOTIFY_V0,  	RTW89_FW_FEATURE_RFK_PRE_NOTIFY_V1,  	RTW89_FW_FEATURE_RFK_RXDCK_V0, +	RTW89_FW_FEATURE_RFK_IQK_V0,  	RTW89_FW_FEATURE_NO_WOW_CPU_IO_RX,  	RTW89_FW_FEATURE_NOTIFY_AP_INFO,  	RTW89_FW_FEATURE_CH_INFO_BE_V0,  	RTW89_FW_FEATURE_LPS_CH_INFO,  	RTW89_FW_FEATURE_NO_PHYCAP_P1, +	RTW89_FW_FEATURE_NO_POWER_DIFFERENCE, +	RTW89_FW_FEATURE_BEACON_LOSS_COUNT_V1, +	RTW89_FW_FEATURE_SCAN_OFFLOAD_EXTRA_OP, +	RTW89_FW_FEATURE_RFK_NTFY_MCC_V0, +	RTW89_FW_FEATURE_LPS_DACK_BY_C2H_REG,  };  struct rtw89_fw_suit { @@ -4552,6 +4696,7 @@ struct rtw89_fw_elm_info {  	struct rtw89_phy_table *rf_nctl;  	struct rtw89_fw_txpwr_track_cfg *txpwr_trk;  	struct rtw89_phy_rfk_log_fmt *rfk_log_fmt; +	const struct rtw89_regd_data *regd;  };  enum rtw89_fw_mss_dev_type { @@ -4590,6 +4735,10 @@ struct rtw89_fw_info {  #define RTW89_CHK_FW_FEATURE(_feat, _fw) \  	(!!((_fw)->feature_map & BIT(RTW89_FW_FEATURE_ ## _feat))) +#define RTW89_CHK_FW_FEATURE_GROUP(_grp, _fw) \ +	(!!((_fw)->feature_map & GENMASK(RTW89_FW_FEATURE_ ## _grp ## _MAX, \ +					 RTW89_FW_FEATURE_ ## _grp ## _MIN))) +  #define RTW89_SET_FW_FEATURE(_fw_feature, _fw) \  	((_fw)->feature_map |= BIT(_fw_feature)) @@ -4605,6 +4754,7 @@ struct rtw89_cam_info {  enum rtw89_sar_sources {  	RTW89_SAR_SOURCE_NONE,  	RTW89_SAR_SOURCE_COMMON, +	RTW89_SAR_SOURCE_ACPI,  	RTW89_SAR_SOURCE_NR,  }; @@ -4629,8 +4779,62 @@ struct rtw89_sar_cfg_common {  	s32 cfg[RTW89_SAR_SUBBAND_NR];  }; +enum rtw89_acpi_sar_subband { +	RTW89_ACPI_SAR_2GHZ_SUBBAND, +	RTW89_ACPI_SAR_5GHZ_SUBBAND_1,   /* U-NII-1 */ +	RTW89_ACPI_SAR_5GHZ_SUBBAND_2,   /* U-NII-2 */ +	RTW89_ACPI_SAR_5GHZ_SUBBAND_2E,  /* U-NII-2-Extended */ +	RTW89_ACPI_SAR_5GHZ_SUBBAND_3_4, /* U-NII-3 and U-NII-4 */ +	RTW89_ACPI_SAR_6GHZ_SUBBAND_5_L, /* U-NII-5 lower part */ +	RTW89_ACPI_SAR_6GHZ_SUBBAND_5_H, /* U-NII-5 higher part */ +	RTW89_ACPI_SAR_6GHZ_SUBBAND_6,   /* U-NII-6 */ +	RTW89_ACPI_SAR_6GHZ_SUBBAND_7_L, /* U-NII-7 lower part */ +	RTW89_ACPI_SAR_6GHZ_SUBBAND_7_H, /* U-NII-7 higher part */ +	RTW89_ACPI_SAR_6GHZ_SUBBAND_8,   /* U-NII-8 */ + +	NUM_OF_RTW89_ACPI_SAR_SUBBAND, +	RTW89_ACPI_SAR_SUBBAND_NR_LEGACY = RTW89_ACPI_SAR_5GHZ_SUBBAND_3_4 + 1, +	RTW89_ACPI_SAR_SUBBAND_NR_HAS_6GHZ = RTW89_ACPI_SAR_6GHZ_SUBBAND_8 + 1, +}; + +#define TXPWR_FACTOR_OF_RTW89_ACPI_SAR 3 /* unit: 0.125 dBm */ +#define MAX_VAL_OF_RTW89_ACPI_SAR S16_MAX +#define MIN_VAL_OF_RTW89_ACPI_SAR S16_MIN +#define MAX_NUM_OF_RTW89_ACPI_SAR_TBL 6 +#define NUM_OF_RTW89_ACPI_SAR_RF_PATH (RF_PATH_B + 1) + +struct rtw89_sar_entry_from_acpi { +	s16 v[NUM_OF_RTW89_ACPI_SAR_SUBBAND][NUM_OF_RTW89_ACPI_SAR_RF_PATH]; +}; + +struct rtw89_sar_table_from_acpi { +	/* If this table is active, must fill all fields according to either +	 * configuration in BIOS or some default values for SAR to work well. +	 */ +	struct rtw89_sar_entry_from_acpi entries[RTW89_REGD_NUM]; +}; + +struct rtw89_sar_indicator_from_acpi { +	bool enable_sync; +	unsigned int fields; +	u8 (*rfpath_to_antidx)(enum rtw89_rf_path rfpath); + +	/* Select among @tables of container, rtw89_sar_cfg_acpi, by path. +	 * Not design with pointers since addresses will be invalid after +	 * sync content with local container instance. +	 */ +	u8 tblsel[NUM_OF_RTW89_ACPI_SAR_RF_PATH]; +}; + +struct rtw89_sar_cfg_acpi { +	u8 downgrade_2tx; +	unsigned int valid_num; +	struct rtw89_sar_table_from_acpi tables[MAX_NUM_OF_RTW89_ACPI_SAR_TBL]; +	struct rtw89_sar_indicator_from_acpi indicator; +}; +  struct rtw89_sar_info { -	/* used to decide how to acces SAR cfg union */ +	/* used to decide how to access SAR cfg union */  	enum rtw89_sar_sources src;  	/* reserved for different knids of SAR cfg struct. @@ -4638,6 +4842,7 @@ struct rtw89_sar_info {  	 */  	union {  		struct rtw89_sar_cfg_common cfg_common; +		struct rtw89_sar_cfg_acpi cfg_acpi;  	};  }; @@ -4667,33 +4872,49 @@ enum rtw89_ant_gain_domain_type {  struct rtw89_ant_gain_info {  	s8 offset[RTW89_ANT_GAIN_CHAIN_NUM][RTW89_ANT_GAIN_SUBBAND_NR];  	u32 regd_enabled; +	bool block_country;  };  struct rtw89_6ghz_span {  	enum rtw89_sar_subband sar_subband_low;  	enum rtw89_sar_subband sar_subband_high; +	enum rtw89_acpi_sar_subband acpi_sar_subband_low; +	enum rtw89_acpi_sar_subband acpi_sar_subband_high;  	enum rtw89_ant_gain_subband ant_gain_subband_low;  	enum rtw89_ant_gain_subband ant_gain_subband_high;  };  #define RTW89_SAR_SPAN_VALID(span) ((span)->sar_subband_high) +#define RTW89_ACPI_SAR_SPAN_VALID(span) ((span)->acpi_sar_subband_high)  #define RTW89_ANT_GAIN_SPAN_VALID(span) ((span)->ant_gain_subband_high)  enum rtw89_tas_state {  	RTW89_TAS_STATE_DPR_OFF,  	RTW89_TAS_STATE_DPR_ON, -	RTW89_TAS_STATE_DPR_FORBID, +	RTW89_TAS_STATE_STATIC_SAR,  }; -#define RTW89_TAS_MAX_WINDOW 50 +#define RTW89_TAS_TX_RATIO_WINDOW 6 +#define RTW89_TAS_TXPWR_WINDOW 180  struct rtw89_tas_info { -	s16 txpwr_history[RTW89_TAS_MAX_WINDOW]; -	s32 total_txpwr; -	u8 cur_idx; -	s8 dpr_gap; -	s8 delta; +	u16 tx_ratio_history[RTW89_TAS_TX_RATIO_WINDOW]; +	u64 txpwr_history[RTW89_TAS_TXPWR_WINDOW]; +	u8 enabled_countries; +	u8 txpwr_head_idx; +	u8 txpwr_tail_idx; +	u8 tx_ratio_idx; +	u16 total_tx_ratio; +	u64 total_txpwr; +	u64 instant_txpwr; +	u32 window_size; +	s8 dpr_on_threshold; +	s8 dpr_off_threshold; +	enum rtw89_tas_state backup_state;  	enum rtw89_tas_state state; +	bool keep_history; +	bool block_regd;  	bool enable; +	bool pause;  };  struct rtw89_chanctx_cfg { @@ -4751,6 +4972,8 @@ struct rtw89_edcca_bak {  enum rtw89_dm_type {  	RTW89_DM_DYNAMIC_EDCCA,  	RTW89_DM_THERMAL_PROTECT, +	RTW89_DM_TAS, +	RTW89_DM_MLO,  };  #define RTW89_THERMAL_PROT_LV_MAX 5 @@ -4772,18 +4995,18 @@ struct rtw89_hal {  	bool no_mcs_12_13;  	atomic_t roc_chanctx_idx; +	u8 roc_link_index;  	DECLARE_BITMAP(changes, NUM_OF_RTW89_CHANCTX_CHANGES);  	DECLARE_BITMAP(entity_map, NUM_OF_RTW89_CHANCTX);  	struct rtw89_chanctx chanctx[NUM_OF_RTW89_CHANCTX];  	struct cfg80211_chan_def roc_chandef; -	bool entity_active[RTW89_PHY_MAX]; +	bool entity_active[RTW89_PHY_NUM];  	bool entity_pause;  	enum rtw89_entity_mode entity_mode;  	struct rtw89_entity_mgnt entity_mgnt; -	struct rtw89_edcca_bak edcca_bak;  	u32 disabled_dm_bitmap; /* bitmap of enum rtw89_dm_type */  	u8 thermal_prot_th; @@ -4811,9 +5034,10 @@ enum rtw89_flags {  	RTW89_FLAG_CRASH_SIMULATING,  	RTW89_FLAG_SER_HANDLING,  	RTW89_FLAG_WOWLAN, -	RTW89_FLAG_FORBIDDEN_TRACK_WROK, +	RTW89_FLAG_FORBIDDEN_TRACK_WORK,  	RTW89_FLAG_CHANGING_INTERFACE,  	RTW89_FLAG_HW_RFKILL_STATE, +	RTW89_FLAG_UNPLUGGED,  	NUM_OF_RTW89_FLAGS,  }; @@ -4978,7 +5202,7 @@ struct rtw89_dpk_bkup_para {  	enum rtw89_band band;  	enum rtw89_bandwidth bw;  	u8 ch; -	bool path_ok; +	u8 path_ok;  	u8 mdpd_en;  	u8 txagc_dpk;  	u8 ther_dpk; @@ -4989,7 +5213,7 @@ struct rtw89_dpk_bkup_para {  struct rtw89_dpk_info {  	bool is_dpk_enable;  	bool is_dpk_reload_en; -	u8 dpk_gs[RTW89_PHY_MAX]; +	u8 dpk_gs[RTW89_PHY_NUM];  	u16 dc_i[RTW89_DPK_RF_PATH][RTW89_DPK_BKUP_NUM];  	u16 dc_q[RTW89_DPK_RF_PATH][RTW89_DPK_BKUP_NUM];  	u8 corr_val[RTW89_DPK_RF_PATH][RTW89_DPK_BKUP_NUM]; @@ -5056,8 +5280,10 @@ struct rtw89_dig_info {  	s8 tia_gain_a[TIA_GAIN_NUM];  	s8 tia_gain_g[TIA_GAIN_NUM];  	s8 *tia_gain; +	u32 bak_dig;  	bool is_linked_pre;  	bool bypass_dig; +	bool pause_dig;  };  enum rtw89_multi_cfo_mode { @@ -5151,7 +5377,7 @@ struct rtw89_tssi_info {  	u32 alignment_backup_by_ch[RF_PATH_MAX][TSSI_MAX_CH_NUM][TSSI_ALIMK_VALUE_NUM];  	u32 alignment_value[RF_PATH_MAX][TSSI_ALIMK_MAX][TSSI_ALIMK_VALUE_NUM];  	bool alignment_done[RF_PATH_MAX][TSSI_ALIMK_MAX]; -	u32 tssi_alimk_time; +	u64 tssi_alimk_time;  };  struct rtw89_power_trim_info { @@ -5162,9 +5388,27 @@ struct rtw89_power_trim_info {  	u8 pad_bias_trim[RF_PATH_MAX];  }; +enum rtw89_regd_func { +	RTW89_REGD_FUNC_TAS = 0, /* TAS (Time Average SAR) */ +	RTW89_REGD_FUNC_DAG = 1, /* DAG (Dynamic Antenna Gain) */ + +	NUM_OF_RTW89_REGD_FUNC, +}; +  struct rtw89_regd {  	char alpha2[3];  	u8 txpwr_regd[RTW89_BAND_NUM]; +	DECLARE_BITMAP(func_bitmap, NUM_OF_RTW89_REGD_FUNC); +}; + +struct rtw89_regd_data { +	unsigned int nr; +	struct rtw89_regd map[] __counted_by(nr); +}; + +struct rtw89_regd_ctrl { +	unsigned int nr; +	const struct rtw89_regd *map;  };  #define RTW89_REGD_MAX_COUNTRY_NUM U8_MAX @@ -5172,12 +5416,16 @@ struct rtw89_regd {  #define RTW89_5GHZ_UNII4_START_INDEX 25  struct rtw89_regulatory_info { +	struct rtw89_regd_ctrl ctrl;  	const struct rtw89_regd *regd;  	enum rtw89_reg_6ghz_power reg_6ghz_power;  	struct rtw89_reg_6ghz_tpe reg_6ghz_tpe; +	bool txpwr_uk_follow_etsi; +  	DECLARE_BITMAP(block_unii4, RTW89_REGD_MAX_COUNTRY_NUM);  	DECLARE_BITMAP(block_6ghz, RTW89_REGD_MAX_COUNTRY_NUM);  	DECLARE_BITMAP(block_6ghz_sp, RTW89_REGD_MAX_COUNTRY_NUM); +	DECLARE_BITMAP(block_6ghz_vlp, RTW89_REGD_MAX_COUNTRY_NUM);  };  enum rtw89_ifs_clm_application { @@ -5315,8 +5563,8 @@ struct rtw89_lps_parm {  };  struct rtw89_ppdu_sts_info { -	struct sk_buff_head rx_queue[RTW89_PHY_MAX]; -	u8 curr_rx_ppdu_cnt[RTW89_PHY_MAX]; +	struct sk_buff_head rx_queue[RTW89_PHY_NUM]; +	u8 curr_rx_ppdu_cnt[RTW89_PHY_NUM];  };  struct rtw89_early_h2c { @@ -5325,12 +5573,24 @@ struct rtw89_early_h2c {  	u16 h2c_len;  }; +struct rtw89_hw_scan_extra_op { +	bool set; +	u8 macid; +	u8 port; +	struct rtw89_chan chan; +	struct rtw89_vif_link *rtwvif_link; +}; +  struct rtw89_hw_scan_info {  	struct rtw89_vif_link *scanning_vif;  	struct list_head pkt_list[NUM_NL80211_BANDS]; +	struct list_head chan_list;  	struct rtw89_chan op_chan; +	struct rtw89_hw_scan_extra_op extra_op; +	bool connected;  	bool abort; -	u32 last_chan_idx; +	u16 delay; /* in unit of ms */ +	u8 seq: 2;  };  enum rtw89_phy_bb_gain_band { @@ -5435,8 +5695,8 @@ struct rtw89_phy_efuse_gain {  	bool offset_valid;  	bool comp_valid;  	s8 offset[RF_PATH_MAX][RTW89_GAIN_OFFSET_NR]; /* S(8, 0) */ -	s8 offset_base[RTW89_PHY_MAX]; /* S(8, 4) */ -	s8 rssi_base[RTW89_PHY_MAX]; /* S(8, 4) */ +	s8 offset_base[RTW89_PHY_NUM]; /* S(8, 4) */ +	s8 rssi_base[RTW89_PHY_NUM]; /* S(8, 4) */  	s8 comp[RF_PATH_MAX][RTW89_SUBBAND_NR]; /* S(8, 0) */  }; @@ -5542,30 +5802,37 @@ struct rtw89_mcc_role {  	struct rtw89_mcc_policy policy;  	struct rtw89_mcc_limit limit; +	const struct rtw89_mcc_courtesy_cfg *crtz; +  	/* only valid when running with FW MRC mechanism */  	u8 slot_idx;  	/* byte-array in LE order for FW */  	u8 macid_bitmap[BITS_TO_BYTES(RTW89_MAX_MAC_ID_NUM)]; +	u8 probe_count;  	u16 duration; /* TU */  	u16 beacon_interval; /* TU */  	bool is_2ghz;  	bool is_go;  	bool is_gc; +	bool ignore_bcn;  };  struct rtw89_mcc_bt_role {  	u16 duration; /* TU */  }; -struct rtw89_mcc_courtesy { -	bool enable; +struct rtw89_mcc_courtesy_cfg {  	u8 slot_num; -	u8 macid_src;  	u8 macid_tgt;  }; +struct rtw89_mcc_courtesy { +	struct rtw89_mcc_courtesy_cfg ref; +	struct rtw89_mcc_courtesy_cfg aux; +}; +  enum rtw89_mcc_plan {  	RTW89_MCC_PLAN_TAIL_BT,  	RTW89_MCC_PLAN_MID_BT, @@ -5599,6 +5866,8 @@ struct rtw89_mcc_config {  	struct rtw89_mcc_pattern pattern;  	struct rtw89_mcc_sync sync;  	u64 start_tsf; +	u64 start_tsf_in_aux_domain; +	u64 prepare_delay;  	u16 mcc_interval; /* TU */  	u16 beacon_offset; /* TU */  }; @@ -5619,6 +5888,16 @@ struct rtw89_mcc_info {  	struct rtw89_mcc_config config;  }; +enum rtw89_mlo_mode { +	RTW89_MLO_MODE_MLSR = 0, + +	NUM_OF_RTW89_MLO_MODE, +}; + +struct rtw89_mlo_info { +	struct rtw89_wait_info wait; +}; +  struct rtw89_dev {  	struct ieee80211_hw *hw;  	struct device *dev; @@ -5634,6 +5913,7 @@ struct rtw89_dev {  	const struct rtw89_rfe_parms *rfe_parms;  	struct rtw89_hal hal;  	struct rtw89_mcc_info mcc; +	struct rtw89_mlo_info mlo;  	struct rtw89_mac_info mac;  	struct rtw89_fw_info fw;  	struct rtw89_hci_info hci; @@ -5645,8 +5925,6 @@ struct rtw89_dev {  	struct rtw89_sta_link __rcu *assoc_link_on_macid[RTW89_MAX_MAC_ID_NUM];  	refcount_t refcount_ap_info; -	/* ensures exclusive access from mac80211 callbacks */ -	struct mutex mutex;  	struct list_head rtwvifs_list;  	/* used to protect rf read write */  	struct mutex rf_mutex; @@ -5666,10 +5944,10 @@ struct rtw89_dev {  	struct rtw89_cam_info cam_info;  	struct sk_buff_head c2h_queue; -	struct work_struct c2h_work; -	struct work_struct ips_work; +	struct wiphy_work c2h_work; +	struct wiphy_work ips_work; +	struct wiphy_work cancel_6ghz_probe_work;  	struct work_struct load_firmware_work; -	struct work_struct cancel_6ghz_probe_work;  	struct list_head early_h2c_list; @@ -5698,9 +5976,6 @@ struct rtw89_dev {  	struct rtw89_power_trim_info pwr_trim;  	struct rtw89_cfo_tracking_info cfo_tracking; -	struct rtw89_env_monitor_info env_monitor; -	struct rtw89_dig_info dig; -	struct rtw89_phy_ch_info ch_info;  	union {  		struct rtw89_phy_bb_gain_info ax;  		struct rtw89_phy_bb_gain_info_be be; @@ -5709,15 +5984,24 @@ struct rtw89_dev {  	struct rtw89_phy_ul_tb_info ul_tb_info;  	struct rtw89_antdiv_info antdiv; -	struct delayed_work track_work; -	struct delayed_work chanctx_work; -	struct delayed_work coex_act1_work; -	struct delayed_work coex_bt_devinfo_work; -	struct delayed_work coex_rfk_chk_work; -	struct delayed_work cfo_track_work; +	struct rtw89_bb_ctx { +		enum rtw89_phy_idx phy_idx; +		struct rtw89_env_monitor_info env_monitor; +		struct rtw89_dig_info dig; +		struct rtw89_phy_ch_info ch_info; +		struct rtw89_edcca_bak edcca_bak; +	} bbs[RTW89_PHY_NUM]; + +	struct wiphy_delayed_work track_work; +	struct wiphy_delayed_work track_ps_work; +	struct wiphy_delayed_work chanctx_work; +	struct wiphy_delayed_work coex_act1_work; +	struct wiphy_delayed_work coex_bt_devinfo_work; +	struct wiphy_delayed_work coex_rfk_chk_work; +	struct wiphy_delayed_work cfo_track_work; +	struct wiphy_delayed_work mcc_prepare_done_work;  	struct delayed_work forbid_ba_work; -	struct delayed_work roc_work; -	struct delayed_work antdiv_work; +	struct wiphy_delayed_work antdiv_work;  	struct rtw89_ppdu_sts_info ppdu_sts;  	u8 total_sta_assoc;  	bool scanning; @@ -5760,6 +6044,7 @@ struct rtw89_vif {  	__be32 ip_addr;  	struct rtw89_traffic_stats stats; +	struct rtw89_traffic_stats stats_ps;  	u32 tdls_peer;  	struct ieee80211_scan_ies *scan_ies; @@ -5768,6 +6053,9 @@ struct rtw89_vif {  	struct rtw89_roc roc;  	bool offchan; +	enum rtw89_mlo_mode mlo_mode; + +	struct list_head dlink_pool;  	u8 links_inst_valid_num;  	DECLARE_BITMAP(links_inst_map, __RTW89_MLD_MAX_LINK_NUM);  	struct rtw89_vif_link *links[IEEE80211_MLD_MAX_NUM_LINKS]; @@ -5807,6 +6095,7 @@ struct rtw89_sta {  	DECLARE_BITMAP(pairwise_sec_cam_map, RTW89_MAX_SEC_CAM_NUM); +	struct list_head dlink_pool;  	u8 links_inst_valid_num;  	DECLARE_BITMAP(links_inst_map, __RTW89_MLD_MAX_LINK_NUM);  	struct rtw89_sta_link *links[IEEE80211_MLD_MAX_NUM_LINKS]; @@ -5902,6 +6191,12 @@ rtw89_assoc_link_rcu_dereference(struct rtw89_dev *rtwdev, u8 macid)  	return rcu_dereference(rtwdev->assoc_link_on_macid[macid]);  } +#define rtw89_get_designated_link(links_holder) \ +({ \ +	typeof(links_holder) p = links_holder; \ +	list_first_entry_or_null(&p->dlink_pool, typeof(*p->links_inst), dlink_schd); \ +}) +  static inline int rtw89_hci_tx_write(struct rtw89_dev *rtwdev,  				     struct rtw89_core_tx_request *tx_req)  { @@ -6728,6 +7023,17 @@ static inline u8 rtw89_chip_get_thermal(struct rtw89_dev *rtwdev,  	return chip->ops->get_thermal(rtwdev, rf_path);  } +static inline u32 rtw89_chip_chan_to_rf18_val(struct rtw89_dev *rtwdev, +					      const struct rtw89_chan *chan) +{ +	const struct rtw89_chip_info *chip = rtwdev->chip; + +	if (!chip->ops->chan_to_rf18_val) +		return 0; + +	return chip->ops->chan_to_rf18_val(rtwdev, chan); +} +  static inline void rtw89_chip_query_ppdu(struct rtw89_dev *rtwdev,  					 struct rtw89_rx_phy_ppdu *phy_ppdu,  					 struct ieee80211_rx_status *status) @@ -6791,9 +7097,14 @@ static inline void rtw89_load_txpwr_table(struct rtw89_dev *rtwdev,  static inline u8 rtw89_regd_get(struct rtw89_dev *rtwdev, u8 band)  { -	const struct rtw89_regd *regd = rtwdev->regulatory.regd; +	const struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; +	const struct rtw89_regd *regd = regulatory->regd; +	u8 txpwr_regd = regd->txpwr_regd[band]; + +	if (regulatory->txpwr_uk_follow_etsi && txpwr_regd == RTW89_UK) +		return RTW89_ETSI; -	return regd->txpwr_regd[band]; +	return txpwr_regd;  }  static inline void rtw89_ctrl_btg_bt_rx(struct rtw89_dev *rtwdev, bool en, @@ -6998,6 +7309,48 @@ static inline bool rtw89_is_mlo_1_1(struct rtw89_dev *rtwdev)  	}  } +static inline u8 rtw89_get_active_phy_bitmap(struct rtw89_dev *rtwdev) +{ +	if (!rtwdev->dbcc_en) +		return BIT(RTW89_PHY_0); + +	switch (rtwdev->mlo_dbcc_mode) { +	case MLO_0_PLUS_2_1RF: +	case MLO_0_PLUS_2_2RF: +		return BIT(RTW89_PHY_1); +	case MLO_1_PLUS_1_1RF: +	case MLO_1_PLUS_1_2RF: +	case MLO_2_PLUS_2_2RF: +	case DBCC_LEGACY: +		return BIT(RTW89_PHY_0) | BIT(RTW89_PHY_1); +	case MLO_2_PLUS_0_1RF: +	case MLO_2_PLUS_0_2RF: +	default: +		return BIT(RTW89_PHY_0); +	} +} + +#define rtw89_for_each_active_bb(rtwdev, bb) \ +	for (u8 __active_bb_bitmap = rtw89_get_active_phy_bitmap(rtwdev), \ +	     __phy_idx = 0; __phy_idx < RTW89_PHY_NUM; __phy_idx++) \ +		if (__active_bb_bitmap & BIT(__phy_idx) && \ +		    (bb = &rtwdev->bbs[__phy_idx])) + +#define rtw89_for_each_capab_bb(rtwdev, bb) \ +	for (u8 __phy_idx_max = rtwdev->dbcc_en ? RTW89_PHY_1 : RTW89_PHY_0, \ +	     __phy_idx = 0; __phy_idx <= __phy_idx_max; __phy_idx++) \ +		if ((bb = &rtwdev->bbs[__phy_idx])) + +static inline +struct rtw89_bb_ctx *rtw89_get_bb_ctx(struct rtw89_dev *rtwdev, +				      enum rtw89_phy_idx phy_idx) +{ +	if (phy_idx >= RTW89_PHY_NUM) +		return &rtwdev->bbs[RTW89_PHY_0]; + +	return &rtwdev->bbs[phy_idx]; +} +  static inline bool rtw89_is_rtl885xb(struct rtw89_dev *rtwdev)  {  	enum rtw89_core_chip_id chip_id = rtwdev->chip->chip_id; @@ -7008,6 +7361,17 @@ static inline bool rtw89_is_rtl885xb(struct rtw89_dev *rtwdev)  	return false;  } +static inline u32 rtw89_bytes_to_mbps(u64 bytes, enum rtw89_tfc_interval interval) +{ +	switch (interval) { +	default: +	case RTW89_TFC_INTERVAL_2SEC: +		return bytes >> 18; /* bytes/2s --> Mbps */; +	case RTW89_TFC_INTERVAL_100MS: +		return (bytes * 10) >> 17; /* bytes/100ms --> Mbps */ +	} +} +  int rtw89_core_tx_write(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif,  			struct ieee80211_sta *sta, struct sk_buff *skb, int *qsel);  int rtw89_h2c_tx(struct rtw89_dev *rtwdev, @@ -7112,9 +7476,8 @@ void rtw89_chip_cfg_txpwr_ul_tb_offset(struct rtw89_dev *rtwdev,  				       struct rtw89_vif_link *rtwvif_link);  bool rtw89_ra_report_to_bitrate(struct rtw89_dev *rtwdev, u8 rpt_rate, u16 *bitrate);  int rtw89_regd_setup(struct rtw89_dev *rtwdev); -int rtw89_regd_init(struct rtw89_dev *rtwdev, -		    void (*reg_notifier)(struct wiphy *wiphy, struct regulatory_request *request)); -void rtw89_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request); +int rtw89_regd_init_hint(struct rtw89_dev *rtwdev); +const char *rtw89_regd_get_string(enum rtw89_regulation_type regd);  void rtw89_traffic_stats_init(struct rtw89_dev *rtwdev,  			      struct rtw89_traffic_stats *stats);  int rtw89_wait_for_cond(struct rtw89_wait_info *wait, unsigned int cond); @@ -7122,8 +7485,11 @@ void rtw89_complete_cond(struct rtw89_wait_info *wait, unsigned int cond,  			 const struct rtw89_completion_data *data);  int rtw89_core_start(struct rtw89_dev *rtwdev);  void rtw89_core_stop(struct rtw89_dev *rtwdev); -void rtw89_core_update_beacon_work(struct work_struct *work); -void rtw89_roc_work(struct work_struct *work); +void rtw89_core_update_beacon_work(struct wiphy *wiphy, struct wiphy_work *work); +void rtw89_core_csa_beacon_work(struct wiphy *wiphy, struct wiphy_work *work); +int rtw89_core_send_nullfunc(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, +			     bool qos, bool ps, int timeout); +void rtw89_roc_work(struct wiphy *wiphy, struct wiphy_work *work);  void rtw89_roc_start(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif);  void rtw89_roc_end(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif);  void rtw89_core_scan_start(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, @@ -7136,6 +7502,8 @@ void rtw89_core_update_p2p_ps(struct rtw89_dev *rtwdev,  			      struct rtw89_vif_link *rtwvif_link,  			      struct ieee80211_bss_conf *bss_conf);  void rtw89_core_ntfy_btc_event(struct rtw89_dev *rtwdev, enum rtw89_btc_hmsg event); +int rtw89_core_mlsr_switch(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, +			   unsigned int link_id);  #if defined(__linux__)  #define	rtw89_static_assert(_x)		static_assert(_x) diff --git a/sys/contrib/dev/rtw89/debug.c b/sys/contrib/dev/rtw89/debug.c index f7e451b0a032..d6624c2ed379 100644 --- a/sys/contrib/dev/rtw89/debug.c +++ b/sys/contrib/dev/rtw89/debug.c @@ -17,6 +17,7 @@  #include "ps.h"  #include "reg.h"  #include "sar.h" +#include "util.h"  #if defined(__FreeBSD__)  #ifdef CONFIG_RTW89_DEBUGFS  #include <linux/debugfs.h> @@ -31,11 +32,21 @@ MODULE_PARM_DESC(debug_mask, "Debugging mask");  #endif  #ifdef CONFIG_RTW89_DEBUGFS +struct rtw89_debugfs_priv_opt { +	bool rlock:1; +	bool wlock:1; +	size_t rsize; +}; +  struct rtw89_debugfs_priv {  	struct rtw89_dev *rtwdev; -	int (*cb_read)(struct seq_file *m, void *v); -	ssize_t (*cb_write)(struct file *filp, const char __user *buffer, -			    size_t count, loff_t *loff); +	ssize_t (*cb_read)(struct rtw89_dev *rtwdev, +			   struct rtw89_debugfs_priv *debugfs_priv, +			   char *buf, size_t bufsz); +	ssize_t (*cb_write)(struct rtw89_dev *rtwdev, +			    struct rtw89_debugfs_priv *debugfs_priv, +			    const char *buf, size_t count); +	struct rtw89_debugfs_priv_opt opt;  	union {  		u32 cb_data;  		struct { @@ -60,6 +71,8 @@ struct rtw89_debugfs_priv {  			u8 sel;  		} mac_mem;  	}; +	ssize_t rused; +	char *rbuf;  };  struct rtw89_debugfs { @@ -81,8 +94,31 @@ struct rtw89_debugfs {  	struct rtw89_debugfs_priv phy_info;  	struct rtw89_debugfs_priv stations;  	struct rtw89_debugfs_priv disable_dm; +	struct rtw89_debugfs_priv mlo_mode; +}; + +struct rtw89_debugfs_iter_data { +	char *buf; +	size_t bufsz; +	int written_sz;  }; +static void rtw89_debugfs_iter_data_setup(struct rtw89_debugfs_iter_data *iter_data, +					  char *buf, size_t bufsz) +{ +	iter_data->buf = buf; +	iter_data->bufsz = bufsz; +	iter_data->written_sz = 0; +} + +static void rtw89_debugfs_iter_data_next(struct rtw89_debugfs_iter_data *iter_data, +					 char *buf, size_t bufsz, int written_sz) +{ +	iter_data->buf = buf; +	iter_data->bufsz = bufsz; +	iter_data->written_sz += written_sz; +} +  static const u16 rtw89_rate_info_bw_to_mhz_map[] = {  	[RATE_INFO_BW_20] = 20,  	[RATE_INFO_BW_40] = 40, @@ -99,84 +135,122 @@ static u16 rtw89_rate_info_bw_to_mhz(enum rate_info_bw bw)  	return 0;  } -static int rtw89_debugfs_single_show(struct seq_file *m, void *v) +static ssize_t rtw89_debugfs_file_read_helper(struct wiphy *wiphy, struct file *file, +					      char *buf, size_t bufsz, void *data)  { -	struct rtw89_debugfs_priv *debugfs_priv = m->private; +	struct rtw89_debugfs_priv *debugfs_priv = data; +	struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; +	ssize_t n; -	return debugfs_priv->cb_read(m, v); +	n = debugfs_priv->cb_read(rtwdev, debugfs_priv, buf, bufsz); +	rtw89_might_trailing_ellipsis(buf, bufsz, n); + +	return n;  } -static ssize_t rtw89_debugfs_single_write(struct file *filp, -					  const char __user *buffer, -					  size_t count, loff_t *loff) +static ssize_t rtw89_debugfs_file_read(struct file *file, char __user *userbuf, +				       size_t count, loff_t *ppos)  { -	struct rtw89_debugfs_priv *debugfs_priv = filp->private_data; +	struct rtw89_debugfs_priv *debugfs_priv = file->private_data; +	struct rtw89_debugfs_priv_opt *opt = &debugfs_priv->opt; +	struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; +	size_t bufsz = opt->rsize ? opt->rsize : PAGE_SIZE; +	char *buf; +	ssize_t n; -	return debugfs_priv->cb_write(filp, buffer, count, loff); -} +	if (!debugfs_priv->rbuf) +		debugfs_priv->rbuf = devm_kzalloc(rtwdev->dev, bufsz, GFP_KERNEL); -static ssize_t rtw89_debugfs_seq_file_write(struct file *filp, -					    const char __user *buffer, -					    size_t count, loff_t *loff) -{ -	struct seq_file *seqpriv = (struct seq_file *)filp->private_data; -	struct rtw89_debugfs_priv *debugfs_priv = seqpriv->private; +	buf = debugfs_priv->rbuf; +	if (!buf) +		return -ENOMEM; + +	if (*ppos) { +		n = debugfs_priv->rused; +		goto out; +	} -	return debugfs_priv->cb_write(filp, buffer, count, loff); +	if (opt->rlock) { +		n = wiphy_locked_debugfs_read(rtwdev->hw->wiphy, file, buf, bufsz, +					      userbuf, count, ppos, +					      rtw89_debugfs_file_read_helper, +					      debugfs_priv); +		debugfs_priv->rused = n; + +		return n; +	} + +	n = rtw89_debugfs_file_read_helper(rtwdev->hw->wiphy, file, buf, bufsz, +					   debugfs_priv); +	debugfs_priv->rused = n; + +out: +	return simple_read_from_buffer(userbuf, count, ppos, buf, n);  } -static int rtw89_debugfs_single_open(struct inode *inode, struct file *filp) +static ssize_t rtw89_debugfs_file_write_helper(struct wiphy *wiphy, struct file *file, +					       char *buf, size_t count, void *data)  { -	return single_open(filp, rtw89_debugfs_single_show, inode->i_private); +	struct rtw89_debugfs_priv *debugfs_priv = data; +	struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; + +	return debugfs_priv->cb_write(rtwdev, debugfs_priv, buf, count);  } -static int rtw89_debugfs_close(struct inode *inode, struct file *filp) +static ssize_t rtw89_debugfs_file_write(struct file *file, +					const char __user *userbuf, +					size_t count, loff_t *loff)  { -	return 0; +	struct rtw89_debugfs_priv *debugfs_priv = file->private_data; +	struct rtw89_debugfs_priv_opt *opt = &debugfs_priv->opt; +	struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; +	char *buf __free(kfree) = kmalloc(count + 1, GFP_KERNEL); +	ssize_t n; + +	if (!buf) +		return -ENOMEM; + +	if (opt->wlock) { +		n = wiphy_locked_debugfs_write(rtwdev->hw->wiphy, +					       file, buf, count + 1, +					       userbuf, count, +					       rtw89_debugfs_file_write_helper, +					       debugfs_priv); +		return n; +	} + +	if (copy_from_user(buf, userbuf, count)) +		return -EFAULT; + +	buf[count] = '\0'; + +	return debugfs_priv->cb_write(rtwdev, debugfs_priv, buf, count);  } -static const struct file_operations file_ops_single_r = { -	.owner = THIS_MODULE, -	.open = rtw89_debugfs_single_open, -	.read = seq_read, -	.llseek = seq_lseek, -	.release = single_release, +static const struct debugfs_short_fops file_ops_single_r = { +	.read = rtw89_debugfs_file_read, +	.llseek = generic_file_llseek,  }; -static const struct file_operations file_ops_common_rw = { -	.owner = THIS_MODULE, -	.open = rtw89_debugfs_single_open, -	.release = single_release, -	.read = seq_read, -	.llseek = seq_lseek, -	.write = rtw89_debugfs_seq_file_write, +static const struct debugfs_short_fops file_ops_common_rw = { +	.read = rtw89_debugfs_file_read, +	.write = rtw89_debugfs_file_write, +	.llseek = generic_file_llseek,  }; -static const struct file_operations file_ops_single_w = { -	.owner = THIS_MODULE, -	.write = rtw89_debugfs_single_write, -	.open = simple_open, -	.release = rtw89_debugfs_close, +static const struct debugfs_short_fops file_ops_single_w = { +	.write = rtw89_debugfs_file_write, +	.llseek = generic_file_llseek,  };  static ssize_t -rtw89_debug_priv_read_reg_select(struct file *filp, -				 const char __user *user_buf, -				 size_t count, loff_t *loff) +rtw89_debug_priv_read_reg_select(struct rtw89_dev *rtwdev, +				 struct rtw89_debugfs_priv *debugfs_priv, +				 const char *buf, size_t count)  { -	struct seq_file *m = (struct seq_file *)filp->private_data; -	struct rtw89_debugfs_priv *debugfs_priv = m->private; -	struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; -	char buf[32]; -	size_t buf_size;  	u32 addr, len;  	int num; -	buf_size = min(count, sizeof(buf) - 1); -	if (copy_from_user(buf, user_buf, buf_size)) -		return -EFAULT; - -	buf[buf_size] = '\0';  	num = sscanf(buf, "%x %x", &addr, &len);  	if (num != 2) {  		rtw89_info(rtwdev, "invalid format: <addr> <len>\n"); @@ -191,11 +265,13 @@ rtw89_debug_priv_read_reg_select(struct file *filp,  	return count;  } -static int rtw89_debug_priv_read_reg_get(struct seq_file *m, void *v) +static +ssize_t rtw89_debug_priv_read_reg_get(struct rtw89_dev *rtwdev, +				      struct rtw89_debugfs_priv *debugfs_priv, +				      char *buf, size_t bufsz)  { -	struct rtw89_debugfs_priv *debugfs_priv = m->private; -	struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; -	u32 addr, end, data, k; +	char *p = buf, *end = buf + bufsz; +	u32 addr, addr_end, data, k;  	u32 len;  	len = debugfs_priv->read_reg.len; @@ -219,41 +295,34 @@ static int rtw89_debug_priv_read_reg_get(struct seq_file *m, void *v)  		return -EINVAL;  	} -	seq_printf(m, "get %d bytes at 0x%08x=0x%08x\n", len, addr, data); +	p += scnprintf(p, end - p, "get %d bytes at 0x%08x=0x%08x\n", len, +		       addr, data); -	return 0; +	return p - buf;  ndata: -	end = addr + len; +	addr_end = addr + len; -	for (; addr < end; addr += 16) { -		seq_printf(m, "%08xh : ", 0x18600000 + addr); +	for (; addr < addr_end; addr += 16) { +		p += scnprintf(p, end - p, "%08xh : ", 0x18600000 + addr);  		for (k = 0; k < 16; k += 4) {  			data = rtw89_read32(rtwdev, addr + k); -			seq_printf(m, "%08x ", data); +			p += scnprintf(p, end - p, "%08x ", data);  		} -		seq_puts(m, "\n"); +		p += scnprintf(p, end - p, "\n");  	} -	return 0; +	return p - buf;  } -static ssize_t rtw89_debug_priv_write_reg_set(struct file *filp, -					      const char __user *user_buf, -					      size_t count, loff_t *loff) +static +ssize_t rtw89_debug_priv_write_reg_set(struct rtw89_dev *rtwdev, +				       struct rtw89_debugfs_priv *debugfs_priv, +				       const char *buf, size_t count)  { -	struct rtw89_debugfs_priv *debugfs_priv = filp->private_data; -	struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; -	char buf[32]; -	size_t buf_size;  	u32 addr, val, len;  	int num; -	buf_size = min(count, sizeof(buf) - 1); -	if (copy_from_user(buf, user_buf, buf_size)) -		return -EFAULT; - -	buf[buf_size] = '\0';  	num = sscanf(buf, "%x %x %x", &addr, &val, &len);  	if (num !=  3) {  		rtw89_info(rtwdev, "invalid format: <addr> <val> <len>\n"); @@ -282,24 +351,14 @@ static ssize_t rtw89_debug_priv_write_reg_set(struct file *filp,  }  static ssize_t -rtw89_debug_priv_read_rf_select(struct file *filp, -				const char __user *user_buf, -				size_t count, loff_t *loff) +rtw89_debug_priv_read_rf_select(struct rtw89_dev *rtwdev, +				struct rtw89_debugfs_priv *debugfs_priv, +				const char *buf, size_t count)  { -	struct seq_file *m = (struct seq_file *)filp->private_data; -	struct rtw89_debugfs_priv *debugfs_priv = m->private; -	struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; -	char buf[32]; -	size_t buf_size;  	u32 addr, mask;  	u8 path;  	int num; -	buf_size = min(count, sizeof(buf) - 1); -	if (copy_from_user(buf, user_buf, buf_size)) -		return -EFAULT; - -	buf[buf_size] = '\0';  	num = sscanf(buf, "%hhd %x %x", &path, &addr, &mask);  	if (num != 3) {  		rtw89_info(rtwdev, "invalid format: <path> <addr> <mask>\n"); @@ -319,10 +378,12 @@ rtw89_debug_priv_read_rf_select(struct file *filp,  	return count;  } -static int rtw89_debug_priv_read_rf_get(struct seq_file *m, void *v) +static +ssize_t rtw89_debug_priv_read_rf_get(struct rtw89_dev *rtwdev, +				     struct rtw89_debugfs_priv *debugfs_priv, +				     char *buf, size_t bufsz)  { -	struct rtw89_debugfs_priv *debugfs_priv = m->private; -	struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; +	char *p = buf, *end = buf + bufsz;  	u32 addr, data, mask;  	u8 path; @@ -332,28 +393,21 @@ static int rtw89_debug_priv_read_rf_get(struct seq_file *m, void *v)  	data = rtw89_read_rf(rtwdev, path, addr, mask); -	seq_printf(m, "path %d, rf register 0x%08x=0x%08x\n", path, addr, data); +	p += scnprintf(p, end - p, "path %d, rf register 0x%08x=0x%08x\n", +		       path, addr, data); -	return 0; +	return p - buf;  } -static ssize_t rtw89_debug_priv_write_rf_set(struct file *filp, -					     const char __user *user_buf, -					     size_t count, loff_t *loff) +static +ssize_t rtw89_debug_priv_write_rf_set(struct rtw89_dev *rtwdev, +				      struct rtw89_debugfs_priv *debugfs_priv, +				      const char *buf, size_t count)  { -	struct rtw89_debugfs_priv *debugfs_priv = filp->private_data; -	struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; -	char buf[32]; -	size_t buf_size;  	u32 addr, val, mask;  	u8 path;  	int num; -	buf_size = min(count, sizeof(buf) - 1); -	if (copy_from_user(buf, user_buf, buf_size)) -		return -EFAULT; - -	buf[buf_size] = '\0';  	num = sscanf(buf, "%hhd %x %x %x", &path, &addr, &mask, &val);  	if (num != 4) {  		rtw89_info(rtwdev, "invalid format: <path> <addr> <mask> <val>\n"); @@ -372,29 +426,31 @@ static ssize_t rtw89_debug_priv_write_rf_set(struct file *filp,  	return count;  } -static int rtw89_debug_priv_rf_reg_dump_get(struct seq_file *m, void *v) +static +ssize_t rtw89_debug_priv_rf_reg_dump_get(struct rtw89_dev *rtwdev, +					 struct rtw89_debugfs_priv *debugfs_priv, +					 char *buf, size_t bufsz)  { -	struct rtw89_debugfs_priv *debugfs_priv = m->private; -	struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;  	const struct rtw89_chip_info *chip = rtwdev->chip; +	char *p = buf, *end = buf + bufsz;  	u32 addr, offset, data;  	u8 path;  	for (path = 0; path < chip->rf_path_num; path++) { -		seq_printf(m, "RF path %d:\n\n", path); +		p += scnprintf(p, end - p, "RF path %d:\n\n", path);  		for (addr = 0; addr < 0x100; addr += 4) { -			seq_printf(m, "0x%08x: ", addr); +			p += scnprintf(p, end - p, "0x%08x: ", addr);  			for (offset = 0; offset < 4; offset++) {  				data = rtw89_read_rf(rtwdev, path,  						     addr + offset, RFREG_MASK); -				seq_printf(m, "0x%05x  ", data); +				p += scnprintf(p, end - p, "0x%05x  ", data);  			} -			seq_puts(m, "\n"); +			p += scnprintf(p, end - p, "\n");  		} -		seq_puts(m, "\n"); +		p += scnprintf(p, end - p, "\n");  	} -	return 0; +	return p - buf;  }  struct txpwr_ent { @@ -725,56 +781,71 @@ static const struct txpwr_map __txpwr_map_lmt_ru_be = {  };  static unsigned int -__print_txpwr_ent(struct seq_file *m, const struct txpwr_ent *ent, -		  const s8 *buf, const unsigned int cur) +__print_txpwr_ent(char *buf, size_t bufsz, const struct txpwr_ent *ent, +		  const s8 *bufp, const unsigned int cur, unsigned int *ate)  { +	char *p = buf, *end = buf + bufsz;  	unsigned int cnt, i; +	unsigned int eaten;  	char *fmt;  	if (ent->nested) { -		for (cnt = 0, i = 0; i < ent->len; i++) -			cnt += __print_txpwr_ent(m, ent->ptr + i, buf, -						 cur + cnt); -		return cnt; +		for (cnt = 0, i = 0; i < ent->len; i++, cnt += eaten) +			p += __print_txpwr_ent(p, end - p, ent->ptr + i, bufp, +					       cur + cnt, &eaten); +		*ate = cnt; +		goto out;  	}  	switch (ent->len) {  	case 0: -		seq_printf(m, "\t<< %s >>\n", ent->txt); -		return 0; +		p += scnprintf(p, end - p, "\t<< %s >>\n", ent->txt); +		*ate = 0; +		goto out;  	case 2:  		fmt = "%s\t| %3d, %3d,\t\tdBm\n"; -		seq_printf(m, fmt, ent->txt, buf[cur], buf[cur + 1]); -		return 2; +		p += scnprintf(p, end - p, fmt, ent->txt, bufp[cur], +			       bufp[cur + 1]); +		*ate = 2; +		goto out;  	case 4:  		fmt = "%s\t| %3d, %3d, %3d, %3d,\tdBm\n"; -		seq_printf(m, fmt, ent->txt, buf[cur], buf[cur + 1], -			   buf[cur + 2], buf[cur + 3]); -		return 4; +		p += scnprintf(p, end - p, fmt, ent->txt, bufp[cur], +			       bufp[cur + 1], +			       bufp[cur + 2], bufp[cur + 3]); +		*ate = 4; +		goto out;  	case 8:  		fmt = "%s\t| %3d, %3d, %3d, %3d, %3d, %3d, %3d, %3d,\tdBm\n"; -		seq_printf(m, fmt, ent->txt, buf[cur], buf[cur + 1], -			   buf[cur + 2], buf[cur + 3], buf[cur + 4], -			   buf[cur + 5], buf[cur + 6], buf[cur + 7]); -		return 8; +		p += scnprintf(p, end - p, fmt, ent->txt, bufp[cur], +			       bufp[cur + 1], +			       bufp[cur + 2], bufp[cur + 3], bufp[cur + 4], +			       bufp[cur + 5], bufp[cur + 6], bufp[cur + 7]); +		*ate = 8; +		goto out;  	default:  		return 0;  	} + +out: +	return p - buf;  } -static int __print_txpwr_map(struct seq_file *m, struct rtw89_dev *rtwdev, -			     const struct txpwr_map *map) +static ssize_t __print_txpwr_map(struct rtw89_dev *rtwdev, char *buf, size_t bufsz, +				 const struct txpwr_map *map)  {  	u8 fct = rtwdev->chip->txpwr_factor_mac;  	u8 path_num = rtwdev->chip->rf_path_num; +	char *p = buf, *end = buf + bufsz;  	unsigned int cur, i; +	unsigned int eaten;  	u32 max_valid_addr;  	u32 val, addr; -	s8 *buf, tmp; +	s8 *bufp, tmp;  	int ret; -	buf = vzalloc(map->addr_to - map->addr_from + 4); -	if (!buf) +	bufp = vzalloc(map->addr_to - map->addr_from + 4); +	if (!bufp)  		return -ENOMEM;  	if (path_num == 1) @@ -794,52 +865,31 @@ static int __print_txpwr_map(struct seq_file *m, struct rtw89_dev *rtwdev,  		for (i = 0; i < 4; i++, val >>= 8) {  			/* signed 7 bits, and reserved BIT(7) */  			tmp = sign_extend32(val, 6); -			buf[cur + i] = tmp >> fct; +			bufp[cur + i] = tmp >> fct;  		}  	} -	for (cur = 0, i = 0; i < map->size; i++) -		cur += __print_txpwr_ent(m, &map->ent[i], buf, cur); +	for (cur = 0, i = 0; i < map->size; i++, cur += eaten) +		p += __print_txpwr_ent(p, end - p, &map->ent[i], bufp, cur, &eaten); -	vfree(buf); -	return 0; +	vfree(bufp); +	return p - buf;  } -#define case_REGD(_regd) \ -	case RTW89_ ## _regd: \ -		seq_puts(m, #_regd "\n"); \ -		break - -static void __print_regd(struct seq_file *m, struct rtw89_dev *rtwdev, -			 const struct rtw89_chan *chan) +static int __print_regd(struct rtw89_dev *rtwdev, char *buf, size_t bufsz, +			const struct rtw89_chan *chan)  { +	const struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; +	char *p = buf, *end = buf + bufsz;  	u8 band = chan->band_type;  	u8 regd = rtw89_regd_get(rtwdev, band); -	switch (regd) { -	default: -		seq_printf(m, "UNKNOWN: %d\n", regd); -		break; -	case_REGD(WW); -	case_REGD(ETSI); -	case_REGD(FCC); -	case_REGD(MKK); -	case_REGD(NA); -	case_REGD(IC); -	case_REGD(KCC); -	case_REGD(NCC); -	case_REGD(CHILE); -	case_REGD(ACMA); -	case_REGD(MEXICO); -	case_REGD(UKRAINE); -	case_REGD(CN); -	case_REGD(QATAR); -	case_REGD(UK); -	case_REGD(THAILAND); -	} -} +	p += scnprintf(p, end - p, "%s\n", rtw89_regd_get_string(regd)); +	p += scnprintf(p, end - p, "\t(txpwr UK follow ETSI: %s)\n", +		       str_yes_no(regulatory->txpwr_uk_follow_etsi)); -#undef case_REGD +	return p - buf; +}  struct dbgfs_txpwr_table {  	const struct txpwr_map *byr; @@ -865,96 +915,95 @@ static const struct dbgfs_txpwr_table *dbgfs_txpwr_tables[RTW89_CHIP_GEN_NUM] =  };  static -void rtw89_debug_priv_txpwr_table_get_regd(struct seq_file *m, -					   struct rtw89_dev *rtwdev, -					   const struct rtw89_chan *chan) +int rtw89_debug_priv_txpwr_table_get_regd(struct rtw89_dev *rtwdev, +					  char *buf, size_t bufsz, +					  const struct rtw89_chan *chan)  {  	const struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory;  	const struct rtw89_reg_6ghz_tpe *tpe6 = ®ulatory->reg_6ghz_tpe; +	char *p = buf, *end = buf + bufsz; -	seq_printf(m, "[Chanctx] band %u, ch %u, bw %u\n", -		   chan->band_type, chan->channel, chan->band_width); +	p += scnprintf(p, end - p, "[Chanctx] band %u, ch %u, bw %u\n", +		       chan->band_type, chan->channel, chan->band_width); -	seq_puts(m, "[Regulatory] "); -	__print_regd(m, rtwdev, chan); +	p += scnprintf(p, end - p, "[Regulatory] "); +	p += __print_regd(rtwdev, p, end - p, chan);  	if (chan->band_type == RTW89_BAND_6G) { -		seq_printf(m, "[reg6_pwr_type] %u\n", regulatory->reg_6ghz_power); +		p += scnprintf(p, end - p, "[reg6_pwr_type] %u\n", +			       regulatory->reg_6ghz_power);  		if (tpe6->valid) -			seq_printf(m, "[TPE] %d dBm\n", tpe6->constraint); +			p += scnprintf(p, end - p, "[TPE] %d dBm\n", +				       tpe6->constraint);  	} + +	return p - buf;  } -static int rtw89_debug_priv_txpwr_table_get(struct seq_file *m, void *v) +static +ssize_t rtw89_debug_priv_txpwr_table_get(struct rtw89_dev *rtwdev, +					 struct rtw89_debugfs_priv *debugfs_priv, +					 char *buf, size_t bufsz)  { -	struct rtw89_debugfs_priv *debugfs_priv = m->private; -	struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;  	enum rtw89_chip_gen chip_gen = rtwdev->chip->chip_gen; +	struct rtw89_sar_parm sar_parm = {};  	const struct dbgfs_txpwr_table *tbl;  	const struct rtw89_chan *chan; -	int ret = 0; +	char *p = buf, *end = buf + bufsz; +	ssize_t n; + +	lockdep_assert_wiphy(rtwdev->hw->wiphy); -	mutex_lock(&rtwdev->mutex);  	rtw89_leave_ps_mode(rtwdev);  	chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0); +	sar_parm.center_freq = chan->freq; -	rtw89_debug_priv_txpwr_table_get_regd(m, rtwdev, chan); +	p += rtw89_debug_priv_txpwr_table_get_regd(rtwdev, p, end - p, chan); -	seq_puts(m, "[SAR]\n"); -	rtw89_print_sar(m, rtwdev, chan->freq); +	p += scnprintf(p, end - p, "[SAR]\n"); +	p += rtw89_print_sar(rtwdev, p, end - p, &sar_parm); -	seq_puts(m, "[TAS]\n"); -	rtw89_print_tas(m, rtwdev); +	p += scnprintf(p, end - p, "[TAS]\n"); +	p += rtw89_print_tas(rtwdev, p, end - p); -	seq_puts(m, "[DAG]\n"); -	rtw89_print_ant_gain(m, rtwdev, chan); +	p += scnprintf(p, end - p, "[DAG]\n"); +	p += rtw89_print_ant_gain(rtwdev, p, end - p, chan);  	tbl = dbgfs_txpwr_tables[chip_gen]; -	if (!tbl) { -		ret = -EOPNOTSUPP; -		goto err; -	} - -	seq_puts(m, "\n[TX power byrate]\n"); -	ret = __print_txpwr_map(m, rtwdev, tbl->byr); -	if (ret) -		goto err; - -	seq_puts(m, "\n[TX power limit]\n"); -	ret = __print_txpwr_map(m, rtwdev, tbl->lmt); -	if (ret) -		goto err; - -	seq_puts(m, "\n[TX power limit_ru]\n"); -	ret = __print_txpwr_map(m, rtwdev, tbl->lmt_ru); -	if (ret) -		goto err; +	if (!tbl) +		return -EOPNOTSUPP; -err: -	mutex_unlock(&rtwdev->mutex); -	return ret; +	p += scnprintf(p, end - p, "\n[TX power byrate]\n"); +	n = __print_txpwr_map(rtwdev, p, end - p, tbl->byr); +	if (n < 0) +		return n; +	p += n; + +	p += scnprintf(p, end - p, "\n[TX power limit]\n"); +	n = __print_txpwr_map(rtwdev, p, end - p, tbl->lmt); +	if (n < 0) +		return n; +	p += n; + +	p += scnprintf(p, end - p, "\n[TX power limit_ru]\n"); +	n = __print_txpwr_map(rtwdev, p, end - p, tbl->lmt_ru); +	if (n < 0) +		return n; +	p += n; + +	return p - buf;  }  static ssize_t -rtw89_debug_priv_mac_reg_dump_select(struct file *filp, -				     const char __user *user_buf, -				     size_t count, loff_t *loff) +rtw89_debug_priv_mac_reg_dump_select(struct rtw89_dev *rtwdev, +				     struct rtw89_debugfs_priv *debugfs_priv, +				     const char *buf, size_t count)  { -	struct seq_file *m = (struct seq_file *)filp->private_data; -	struct rtw89_debugfs_priv *debugfs_priv = m->private; -	struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;  	const struct rtw89_chip_info *chip = rtwdev->chip; -	char buf[32]; -	size_t buf_size;  	int sel;  	int ret; -	buf_size = min(count, sizeof(buf) - 1); -	if (copy_from_user(buf, user_buf, buf_size)) -		return -EFAULT; - -	buf[buf_size] = '\0';  	ret = kstrtoint(buf, 0, &sel);  	if (ret)  		return ret; @@ -978,99 +1027,91 @@ rtw89_debug_priv_mac_reg_dump_select(struct file *filp,  #define RTW89_MAC_PAGE_SIZE		0x100 -static int rtw89_debug_priv_mac_reg_dump_get(struct seq_file *m, void *v) +static +ssize_t rtw89_debug_priv_mac_reg_dump_get(struct rtw89_dev *rtwdev, +					  struct rtw89_debugfs_priv *debugfs_priv, +					  char *buf, size_t bufsz)  { -	struct rtw89_debugfs_priv *debugfs_priv = m->private; -	struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;  	enum rtw89_debug_mac_reg_sel reg_sel = debugfs_priv->cb_data; -	u32 start, end; +	char *p = buf, *end = buf + bufsz; +	u32 start, end_addr;  	u32 i, j, k, page;  	u32 val;  	switch (reg_sel) {  	case RTW89_DBG_SEL_MAC_00: -		seq_puts(m, "Debug selected MAC page 0x00\n"); +		p += scnprintf(p, end - p, "Debug selected MAC page 0x00\n");  		start = 0x000; -		end = 0x014; +		end_addr = 0x014;  		break;  	case RTW89_DBG_SEL_MAC_30: -		seq_puts(m, "Debug selected MAC page 0x30\n"); +		p += scnprintf(p, end - p, "Debug selected MAC page 0x30\n");  		start = 0x030; -		end = 0x033; +		end_addr = 0x033;  		break;  	case RTW89_DBG_SEL_MAC_40: -		seq_puts(m, "Debug selected MAC page 0x40\n"); +		p += scnprintf(p, end - p, "Debug selected MAC page 0x40\n");  		start = 0x040; -		end = 0x07f; +		end_addr = 0x07f;  		break;  	case RTW89_DBG_SEL_MAC_80: -		seq_puts(m, "Debug selected MAC page 0x80\n"); +		p += scnprintf(p, end - p, "Debug selected MAC page 0x80\n");  		start = 0x080; -		end = 0x09f; +		end_addr = 0x09f;  		break;  	case RTW89_DBG_SEL_MAC_C0: -		seq_puts(m, "Debug selected MAC page 0xc0\n"); +		p += scnprintf(p, end - p, "Debug selected MAC page 0xc0\n");  		start = 0x0c0; -		end = 0x0df; +		end_addr = 0x0df;  		break;  	case RTW89_DBG_SEL_MAC_E0: -		seq_puts(m, "Debug selected MAC page 0xe0\n"); +		p += scnprintf(p, end - p, "Debug selected MAC page 0xe0\n");  		start = 0x0e0; -		end = 0x0ff; +		end_addr = 0x0ff;  		break;  	case RTW89_DBG_SEL_BB: -		seq_puts(m, "Debug selected BB register\n"); +		p += scnprintf(p, end - p, "Debug selected BB register\n");  		start = 0x100; -		end = 0x17f; +		end_addr = 0x17f;  		break;  	case RTW89_DBG_SEL_IQK: -		seq_puts(m, "Debug selected IQK register\n"); +		p += scnprintf(p, end - p, "Debug selected IQK register\n");  		start = 0x180; -		end = 0x1bf; +		end_addr = 0x1bf;  		break;  	case RTW89_DBG_SEL_RFC: -		seq_puts(m, "Debug selected RFC register\n"); +		p += scnprintf(p, end - p, "Debug selected RFC register\n");  		start = 0x1c0; -		end = 0x1ff; +		end_addr = 0x1ff;  		break;  	default: -		seq_puts(m, "Selected invalid register page\n"); +		p += scnprintf(p, end - p, "Selected invalid register page\n");  		return -EINVAL;  	} -	for (i = start; i <= end; i++) { +	for (i = start; i <= end_addr; i++) {  		page = i << 8;  		for (j = page; j < page + RTW89_MAC_PAGE_SIZE; j += 16) { -			seq_printf(m, "%08xh : ", 0x18600000 + j); +			p += scnprintf(p, end - p, "%08xh : ", 0x18600000 + j);  			for (k = 0; k < 4; k++) {  				val = rtw89_read32(rtwdev, j + (k << 2)); -				seq_printf(m, "%08x ", val); +				p += scnprintf(p, end - p, "%08x ", val);  			} -			seq_puts(m, "\n"); +			p += scnprintf(p, end - p, "\n");  		}  	} -	return 0; +	return p - buf;  }  static ssize_t -rtw89_debug_priv_mac_mem_dump_select(struct file *filp, -				     const char __user *user_buf, -				     size_t count, loff_t *loff) +rtw89_debug_priv_mac_mem_dump_select(struct rtw89_dev *rtwdev, +				     struct rtw89_debugfs_priv *debugfs_priv, +				     const char *buf, size_t count)  { -	struct seq_file *m = (struct seq_file *)filp->private_data; -	struct rtw89_debugfs_priv *debugfs_priv = m->private; -	struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; -	char buf[32]; -	size_t buf_size;  	u32 sel, start_addr, len;  	int num; -	buf_size = min(count, sizeof(buf) - 1); -	if (copy_from_user(buf, user_buf, buf_size)) -		return -EFAULT; - -	buf[buf_size] = '\0';  	num = sscanf(buf, "%x %x %x", &sel, &start_addr, &len);  	if (num != 3) {  		rtw89_info(rtwdev, "invalid format: <sel> <start> <len>\n"); @@ -1087,51 +1128,58 @@ rtw89_debug_priv_mac_mem_dump_select(struct file *filp,  	return count;  } -static void rtw89_debug_dump_mac_mem(struct seq_file *m, -				     struct rtw89_dev *rtwdev, -				     u8 sel, u32 start_addr, u32 len) +static int rtw89_debug_dump_mac_mem(struct rtw89_dev *rtwdev, +				    char *buf, size_t bufsz, +				    u8 sel, u32 start_addr, u32 len)  {  	const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;  	u32 filter_model_addr = mac->filter_model_addr;  	u32 indir_access_addr = mac->indir_access_addr; +	u32 mem_page_size = mac->mem_page_size;  	u32 base_addr, start_page, residue; -	u32 i, j, p, pages; +	char *p = buf, *end = buf + bufsz; +	u32 i, j, pp, pages;  	u32 dump_len, remain;  	u32 val;  	remain = len; -	pages = len / MAC_MEM_DUMP_PAGE_SIZE + 1; -	start_page = start_addr / MAC_MEM_DUMP_PAGE_SIZE; -	residue = start_addr % MAC_MEM_DUMP_PAGE_SIZE; +	pages = len / mem_page_size + 1; +	start_page = start_addr / mem_page_size; +	residue = start_addr % mem_page_size;  	base_addr = mac->mem_base_addrs[sel]; -	base_addr += start_page * MAC_MEM_DUMP_PAGE_SIZE; +	base_addr += start_page * mem_page_size; -	for (p = 0; p < pages; p++) { -		dump_len = min_t(u32, remain, MAC_MEM_DUMP_PAGE_SIZE); +	for (pp = 0; pp < pages; pp++) { +		dump_len = min_t(u32, remain, mem_page_size);  		rtw89_write32(rtwdev, filter_model_addr, base_addr);  		for (i = indir_access_addr + residue;  		     i < indir_access_addr + dump_len;) { -			seq_printf(m, "%08xh:", i); +			p += scnprintf(p, end - p, "%08xh:", i);  			for (j = 0;  			     j < 4 && i < indir_access_addr + dump_len;  			     j++, i += 4) {  				val = rtw89_read32(rtwdev, i); -				seq_printf(m, "  %08x", val); +				p += scnprintf(p, end - p, "  %08x", val);  				remain -= 4;  			} -			seq_puts(m, "\n"); +			p += scnprintf(p, end - p, "\n");  		} -		base_addr += MAC_MEM_DUMP_PAGE_SIZE; +		base_addr += mem_page_size;  	} + +	return p - buf;  } -static int -rtw89_debug_priv_mac_mem_dump_get(struct seq_file *m, void *v) +static ssize_t +rtw89_debug_priv_mac_mem_dump_get(struct rtw89_dev *rtwdev, +				  struct rtw89_debugfs_priv *debugfs_priv, +				  char *buf, size_t bufsz)  { -	struct rtw89_debugfs_priv *debugfs_priv = m->private; -	struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; +	char *p = buf, *end = buf + bufsz;  	bool grant_read = false; +	lockdep_assert_wiphy(rtwdev->hw->wiphy); +  	if (debugfs_priv->mac_mem.sel >= RTW89_MAC_MEM_NUM)  		return -ENOENT; @@ -1148,40 +1196,28 @@ rtw89_debug_priv_mac_mem_dump_get(struct seq_file *m, void *v)  		}  	} -	mutex_lock(&rtwdev->mutex);  	rtw89_leave_ps_mode(rtwdev);  	if (grant_read)  		rtw89_write32_set(rtwdev, R_AX_TCR1, B_AX_TCR_FORCE_READ_TXDFIFO); -	rtw89_debug_dump_mac_mem(m, rtwdev, -				 debugfs_priv->mac_mem.sel, -				 debugfs_priv->mac_mem.start, -				 debugfs_priv->mac_mem.len); +	p += rtw89_debug_dump_mac_mem(rtwdev, p, end - p, +				      debugfs_priv->mac_mem.sel, +				      debugfs_priv->mac_mem.start, +				      debugfs_priv->mac_mem.len);  	if (grant_read)  		rtw89_write32_clr(rtwdev, R_AX_TCR1, B_AX_TCR_FORCE_READ_TXDFIFO); -	mutex_unlock(&rtwdev->mutex); -	return 0; +	return p - buf;  }  static ssize_t -rtw89_debug_priv_mac_dbg_port_dump_select(struct file *filp, -					  const char __user *user_buf, -					  size_t count, loff_t *loff) +rtw89_debug_priv_mac_dbg_port_dump_select(struct rtw89_dev *rtwdev, +					  struct rtw89_debugfs_priv *debugfs_priv, +					  const char *buf, size_t count)  { -	struct seq_file *m = (struct seq_file *)filp->private_data; -	struct rtw89_debugfs_priv *debugfs_priv = m->private; -	struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; -	char buf[32]; -	size_t buf_size;  	int sel, set;  	int num;  	bool enable; -	buf_size = min(count, sizeof(buf) - 1); -	if (copy_from_user(buf, user_buf, buf_size)) -		return -EFAULT; - -	buf[buf_size] = '\0';  	num = sscanf(buf, "%d %d", &sel, &set);  	if (num != 2) {  		rtw89_info(rtwdev, "invalid format: <sel> <set>\n"); @@ -1217,13 +1253,13 @@ rtw89_debug_priv_mac_dbg_port_dump_select(struct file *filp,  }  static int rtw89_debug_mac_dump_ss_dbg(struct rtw89_dev *rtwdev, -				       struct seq_file *m) +				       char *buf, size_t bufsz)  {  	return 0;  }  static int rtw89_debug_mac_dump_dle_dbg(struct rtw89_dev *rtwdev, -					struct seq_file *m) +				       char *buf, size_t bufsz)  {  #define DLE_DFI_DUMP(__type, __target, __sel)				\  ({									\ @@ -1252,7 +1288,7 @@ static int rtw89_debug_mac_dump_dle_dbg(struct rtw89_dev *rtwdev,  	__data;								\  }) -#define DLE_DFI_FREE_PAGE_DUMP(__m, __type)				\ +#define DLE_DFI_FREE_PAGE_DUMP(__p, __end, __type)			\  ({									\  	u32 __freepg, __pubpg;						\  	u32 __freepg_head, __freepg_tail, __pubpg_num;			\ @@ -1262,24 +1298,25 @@ static int rtw89_debug_mac_dump_dle_dbg(struct rtw89_dev *rtwdev,  	__freepg_head = FIELD_GET(B_AX_DLE_FREE_HEADPG, __freepg);	\  	__freepg_tail = FIELD_GET(B_AX_DLE_FREE_TAILPG, __freepg);	\  	__pubpg_num = FIELD_GET(B_AX_DLE_PUB_PGNUM, __pubpg);		\ -	seq_printf(__m, "[%s] freepg head: %d\n",			\ -		   #__type, __freepg_head);				\ -	seq_printf(__m, "[%s] freepg tail: %d\n",			\ -		   #__type, __freepg_tail);				\ -	seq_printf(__m, "[%s] pubpg num  : %d\n",			\ -		  #__type, __pubpg_num);				\ +	__p += scnprintf(__p, __end - __p, "[%s] freepg head: %d\n",	\ +			 #__type, __freepg_head);			\ +	__p += scnprintf(__p, __end - __p, "[%s] freepg tail: %d\n",	\ +			 #__type, __freepg_tail);			\ +	__p += scnprintf(__p, __end - __p, "[%s] pubpg num  : %d\n",	\ +			 #__type, __pubpg_num);				\  }) -#define case_QUOTA(__m, __type, __id)					\ +#define case_QUOTA(__p, __end, __type, __id)				\  	case __type##_QTAID_##__id:					\ -		val32 = DLE_DFI_DUMP(__type, QUOTA, __type##_QTAID_##__id);	\ +		val32 = DLE_DFI_DUMP(__type, QUOTA, __type##_QTAID_##__id); \  		rsv_pgnum = FIELD_GET(B_AX_DLE_RSV_PGNUM, val32);	\  		use_pgnum = FIELD_GET(B_AX_DLE_USE_PGNUM, val32);	\ -		seq_printf(__m, "[%s][%s] rsv_pgnum: %d\n",		\ -			   #__type, #__id, rsv_pgnum);			\ -		seq_printf(__m, "[%s][%s] use_pgnum: %d\n",		\ -			   #__type, #__id, use_pgnum);			\ +		__p += scnprintf(__p, __end - __p, "[%s][%s] rsv_pgnum: %d\n", \ +				 #__type, #__id, rsv_pgnum);		\ +		__p += scnprintf(__p, __end - __p, "[%s][%s] use_pgnum: %d\n", \ +				 #__type, #__id, use_pgnum);		\  		break +	char *p = buf, *end = buf + bufsz;  	u32 quota_id;  	u32 val32;  	u16 rsv_pgnum, use_pgnum; @@ -1287,38 +1324,39 @@ static int rtw89_debug_mac_dump_dle_dbg(struct rtw89_dev *rtwdev,  	ret = rtw89_mac_check_mac_en(rtwdev, 0, RTW89_DMAC_SEL);  	if (ret) { -		seq_puts(m, "[DLE]  : DMAC not enabled\n"); -		return ret; +		p += scnprintf(p, end - p, "[DLE]  : DMAC not enabled\n"); +		goto out;  	} -	DLE_DFI_FREE_PAGE_DUMP(m, WDE); -	DLE_DFI_FREE_PAGE_DUMP(m, PLE); +	DLE_DFI_FREE_PAGE_DUMP(p, end, WDE); +	DLE_DFI_FREE_PAGE_DUMP(p, end, PLE);  	for (quota_id = 0; quota_id <= WDE_QTAID_CPUIO; quota_id++) {  		switch (quota_id) { -		case_QUOTA(m, WDE, HOST_IF); -		case_QUOTA(m, WDE, WLAN_CPU); -		case_QUOTA(m, WDE, DATA_CPU); -		case_QUOTA(m, WDE, PKTIN); -		case_QUOTA(m, WDE, CPUIO); +		case_QUOTA(p, end, WDE, HOST_IF); +		case_QUOTA(p, end, WDE, WLAN_CPU); +		case_QUOTA(p, end, WDE, DATA_CPU); +		case_QUOTA(p, end, WDE, PKTIN); +		case_QUOTA(p, end, WDE, CPUIO);  		}  	}  	for (quota_id = 0; quota_id <= PLE_QTAID_CPUIO; quota_id++) {  		switch (quota_id) { -		case_QUOTA(m, PLE, B0_TXPL); -		case_QUOTA(m, PLE, B1_TXPL); -		case_QUOTA(m, PLE, C2H); -		case_QUOTA(m, PLE, H2C); -		case_QUOTA(m, PLE, WLAN_CPU); -		case_QUOTA(m, PLE, MPDU); -		case_QUOTA(m, PLE, CMAC0_RX); -		case_QUOTA(m, PLE, CMAC1_RX); -		case_QUOTA(m, PLE, CMAC1_BBRPT); -		case_QUOTA(m, PLE, WDRLS); -		case_QUOTA(m, PLE, CPUIO); +		case_QUOTA(p, end, PLE, B0_TXPL); +		case_QUOTA(p, end, PLE, B1_TXPL); +		case_QUOTA(p, end, PLE, C2H); +		case_QUOTA(p, end, PLE, H2C); +		case_QUOTA(p, end, PLE, WLAN_CPU); +		case_QUOTA(p, end, PLE, MPDU); +		case_QUOTA(p, end, PLE, CMAC0_RX); +		case_QUOTA(p, end, PLE, CMAC1_RX); +		case_QUOTA(p, end, PLE, CMAC1_BBRPT); +		case_QUOTA(p, end, PLE, WDRLS); +		case_QUOTA(p, end, PLE, CPUIO);  		}  	} -	return 0; +out: +	return p - buf;  #undef case_QUOTA  #undef DLE_DFI_DUMP @@ -1326,73 +1364,88 @@ static int rtw89_debug_mac_dump_dle_dbg(struct rtw89_dev *rtwdev,  }  static int rtw89_debug_mac_dump_dmac_dbg(struct rtw89_dev *rtwdev, -					 struct seq_file *m) +					 char *buf, size_t bufsz)  {  	const struct rtw89_chip_info *chip = rtwdev->chip; +	char *p = buf, *end = buf + bufsz;  	u32 dmac_err;  	int i, ret;  	ret = rtw89_mac_check_mac_en(rtwdev, 0, RTW89_DMAC_SEL);  	if (ret) { -		seq_puts(m, "[DMAC] : DMAC not enabled\n"); -		return ret; +		p += scnprintf(p, end - p, "[DMAC] : DMAC not enabled\n"); +		goto out;  	}  	dmac_err = rtw89_read32(rtwdev, R_AX_DMAC_ERR_ISR); -	seq_printf(m, "R_AX_DMAC_ERR_ISR=0x%08x\n", dmac_err); -	seq_printf(m, "R_AX_DMAC_ERR_IMR=0x%08x\n", -		   rtw89_read32(rtwdev, R_AX_DMAC_ERR_IMR)); +	p += scnprintf(p, end - p, "R_AX_DMAC_ERR_ISR=0x%08x\n", dmac_err); +	p += scnprintf(p, end - p, "R_AX_DMAC_ERR_IMR=0x%08x\n", +		       rtw89_read32(rtwdev, R_AX_DMAC_ERR_IMR));  	if (dmac_err) { -		seq_printf(m, "R_AX_WDE_ERR_FLAG_CFG=0x%08x\n", -			   rtw89_read32(rtwdev, R_AX_WDE_ERR_FLAG_CFG_NUM1)); -		seq_printf(m, "R_AX_PLE_ERR_FLAG_CFG=0x%08x\n", -			   rtw89_read32(rtwdev, R_AX_PLE_ERR_FLAG_CFG_NUM1)); +		p += scnprintf(p, end - p, "R_AX_WDE_ERR_FLAG_CFG=0x%08x\n", +			       rtw89_read32(rtwdev, R_AX_WDE_ERR_FLAG_CFG_NUM1)); +		p += scnprintf(p, end - p, "R_AX_PLE_ERR_FLAG_CFG=0x%08x\n", +			       rtw89_read32(rtwdev, R_AX_PLE_ERR_FLAG_CFG_NUM1));  		if (chip->chip_id == RTL8852C) { -			seq_printf(m, "R_AX_PLE_ERRFLAG_MSG=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_PLE_ERRFLAG_MSG)); -			seq_printf(m, "R_AX_WDE_ERRFLAG_MSG=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_WDE_ERRFLAG_MSG)); -			seq_printf(m, "R_AX_PLE_DBGERR_LOCKEN=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_PLE_DBGERR_LOCKEN)); -			seq_printf(m, "R_AX_PLE_DBGERR_STS=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_PLE_DBGERR_STS)); +			p += scnprintf(p, end - p, +				       "R_AX_PLE_ERRFLAG_MSG=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_PLE_ERRFLAG_MSG)); +			p += scnprintf(p, end - p, +				       "R_AX_WDE_ERRFLAG_MSG=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_WDE_ERRFLAG_MSG)); +			p += scnprintf(p, end - p, +				       "R_AX_PLE_DBGERR_LOCKEN=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_PLE_DBGERR_LOCKEN)); +			p += scnprintf(p, end - p, +				       "R_AX_PLE_DBGERR_STS=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_PLE_DBGERR_STS));  		}  	}  	if (dmac_err & B_AX_WDRLS_ERR_FLAG) { -		seq_printf(m, "R_AX_WDRLS_ERR_IMR=0x%08x\n", -			   rtw89_read32(rtwdev, R_AX_WDRLS_ERR_IMR)); -		seq_printf(m, "R_AX_WDRLS_ERR_ISR=0x%08x\n", -			   rtw89_read32(rtwdev, R_AX_WDRLS_ERR_ISR)); +		p += scnprintf(p, end - p, "R_AX_WDRLS_ERR_IMR=0x%08x\n", +			       rtw89_read32(rtwdev, R_AX_WDRLS_ERR_IMR)); +		p += scnprintf(p, end - p, "R_AX_WDRLS_ERR_ISR=0x%08x\n", +			       rtw89_read32(rtwdev, R_AX_WDRLS_ERR_ISR));  		if (chip->chip_id == RTL8852C) -			seq_printf(m, "R_AX_RPQ_RXBD_IDX=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_RPQ_RXBD_IDX_V1)); +			p += scnprintf(p, end - p, +				       "R_AX_RPQ_RXBD_IDX=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_RPQ_RXBD_IDX_V1));  		else -			seq_printf(m, "R_AX_RPQ_RXBD_IDX=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_RPQ_RXBD_IDX)); +			p += scnprintf(p, end - p, +				       "R_AX_RPQ_RXBD_IDX=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_RPQ_RXBD_IDX));  	}  	if (dmac_err & B_AX_WSEC_ERR_FLAG) {  		if (chip->chip_id == RTL8852C) { -			seq_printf(m, "R_AX_SEC_ERR_IMR=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_SEC_ERROR_FLAG_IMR)); -			seq_printf(m, "R_AX_SEC_ERR_ISR=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_SEC_ERROR_FLAG)); -			seq_printf(m, "R_AX_SEC_ENG_CTRL=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_SEC_ENG_CTRL)); -			seq_printf(m, "R_AX_SEC_MPDU_PROC=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_SEC_MPDU_PROC)); -			seq_printf(m, "R_AX_SEC_CAM_ACCESS=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_SEC_CAM_ACCESS)); -			seq_printf(m, "R_AX_SEC_CAM_RDATA=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_SEC_CAM_RDATA)); -			seq_printf(m, "R_AX_SEC_DEBUG1=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_SEC_DEBUG1)); -			seq_printf(m, "R_AX_SEC_TX_DEBUG=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_SEC_TX_DEBUG)); -			seq_printf(m, "R_AX_SEC_RX_DEBUG=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_SEC_RX_DEBUG)); +			p += scnprintf(p, end - p, +				       "R_AX_SEC_ERR_IMR=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_SEC_ERROR_FLAG_IMR)); +			p += scnprintf(p, end - p, +				       "R_AX_SEC_ERR_ISR=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_SEC_ERROR_FLAG)); +			p += scnprintf(p, end - p, +				       "R_AX_SEC_ENG_CTRL=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_SEC_ENG_CTRL)); +			p += scnprintf(p, end - p, +				       "R_AX_SEC_MPDU_PROC=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_SEC_MPDU_PROC)); +			p += scnprintf(p, end - p, +				       "R_AX_SEC_CAM_ACCESS=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_SEC_CAM_ACCESS)); +			p += scnprintf(p, end - p, +				       "R_AX_SEC_CAM_RDATA=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_SEC_CAM_RDATA)); +			p += scnprintf(p, end - p, "R_AX_SEC_DEBUG1=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_SEC_DEBUG1)); +			p += scnprintf(p, end - p, +				       "R_AX_SEC_TX_DEBUG=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_SEC_TX_DEBUG)); +			p += scnprintf(p, end - p, +				       "R_AX_SEC_RX_DEBUG=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_SEC_RX_DEBUG));  			rtw89_write32_mask(rtwdev, R_AX_DBG_CTRL,  					   B_AX_DBG_SEL0, 0x8B); @@ -1403,187 +1456,229 @@ static int rtw89_debug_mac_dump_dmac_dbg(struct rtw89_dev *rtwdev,  			for (i = 0; i < 0x10; i++) {  				rtw89_write32_mask(rtwdev, R_AX_SEC_ENG_CTRL,  						   B_AX_SEC_DBG_PORT_FIELD_MASK, i); -				seq_printf(m, "sel=%x,R_AX_SEC_DEBUG2=0x%08x\n", -					   i, rtw89_read32(rtwdev, R_AX_SEC_DEBUG2)); +				p += scnprintf(p, end - p, +					       "sel=%x,R_AX_SEC_DEBUG2=0x%08x\n", +					       i, +					       rtw89_read32(rtwdev, R_AX_SEC_DEBUG2));  			}  		} else { -			seq_printf(m, "R_AX_SEC_ERR_IMR_ISR=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_SEC_DEBUG)); -			seq_printf(m, "R_AX_SEC_ENG_CTRL=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_SEC_ENG_CTRL)); -			seq_printf(m, "R_AX_SEC_MPDU_PROC=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_SEC_MPDU_PROC)); -			seq_printf(m, "R_AX_SEC_CAM_ACCESS=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_SEC_CAM_ACCESS)); -			seq_printf(m, "R_AX_SEC_CAM_RDATA=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_SEC_CAM_RDATA)); -			seq_printf(m, "R_AX_SEC_CAM_WDATA=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_SEC_CAM_WDATA)); -			seq_printf(m, "R_AX_SEC_TX_DEBUG=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_SEC_TX_DEBUG)); -			seq_printf(m, "R_AX_SEC_RX_DEBUG=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_SEC_RX_DEBUG)); -			seq_printf(m, "R_AX_SEC_TRX_PKT_CNT=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_SEC_TRX_PKT_CNT)); -			seq_printf(m, "R_AX_SEC_TRX_BLK_CNT=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_SEC_TRX_BLK_CNT)); +			p += scnprintf(p, end - p, +				       "R_AX_SEC_ERR_IMR_ISR=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_SEC_DEBUG)); +			p += scnprintf(p, end - p, +				       "R_AX_SEC_ENG_CTRL=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_SEC_ENG_CTRL)); +			p += scnprintf(p, end - p, +				       "R_AX_SEC_MPDU_PROC=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_SEC_MPDU_PROC)); +			p += scnprintf(p, end - p, +				       "R_AX_SEC_CAM_ACCESS=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_SEC_CAM_ACCESS)); +			p += scnprintf(p, end - p, +				       "R_AX_SEC_CAM_RDATA=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_SEC_CAM_RDATA)); +			p += scnprintf(p, end - p, +				       "R_AX_SEC_CAM_WDATA=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_SEC_CAM_WDATA)); +			p += scnprintf(p, end - p, +				       "R_AX_SEC_TX_DEBUG=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_SEC_TX_DEBUG)); +			p += scnprintf(p, end - p, +				       "R_AX_SEC_RX_DEBUG=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_SEC_RX_DEBUG)); +			p += scnprintf(p, end - p, +				       "R_AX_SEC_TRX_PKT_CNT=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_SEC_TRX_PKT_CNT)); +			p += scnprintf(p, end - p, +				       "R_AX_SEC_TRX_BLK_CNT=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_SEC_TRX_BLK_CNT));  		}  	}  	if (dmac_err & B_AX_MPDU_ERR_FLAG) { -		seq_printf(m, "R_AX_MPDU_TX_ERR_IMR=0x%08x\n", -			   rtw89_read32(rtwdev, R_AX_MPDU_TX_ERR_IMR)); -		seq_printf(m, "R_AX_MPDU_TX_ERR_ISR=0x%08x\n", -			   rtw89_read32(rtwdev, R_AX_MPDU_TX_ERR_ISR)); -		seq_printf(m, "R_AX_MPDU_RX_ERR_IMR=0x%08x\n", -			   rtw89_read32(rtwdev, R_AX_MPDU_RX_ERR_IMR)); -		seq_printf(m, "R_AX_MPDU_RX_ERR_ISR=0x%08x\n", -			   rtw89_read32(rtwdev, R_AX_MPDU_RX_ERR_ISR)); +		p += scnprintf(p, end - p, "R_AX_MPDU_TX_ERR_IMR=0x%08x\n", +			       rtw89_read32(rtwdev, R_AX_MPDU_TX_ERR_IMR)); +		p += scnprintf(p, end - p, "R_AX_MPDU_TX_ERR_ISR=0x%08x\n", +			       rtw89_read32(rtwdev, R_AX_MPDU_TX_ERR_ISR)); +		p += scnprintf(p, end - p, "R_AX_MPDU_RX_ERR_IMR=0x%08x\n", +			       rtw89_read32(rtwdev, R_AX_MPDU_RX_ERR_IMR)); +		p += scnprintf(p, end - p, "R_AX_MPDU_RX_ERR_ISR=0x%08x\n", +			       rtw89_read32(rtwdev, R_AX_MPDU_RX_ERR_ISR));  	}  	if (dmac_err & B_AX_STA_SCHEDULER_ERR_FLAG) { -		seq_printf(m, "R_AX_STA_SCHEDULER_ERR_IMR=0x%08x\n", -			   rtw89_read32(rtwdev, R_AX_STA_SCHEDULER_ERR_IMR)); -		seq_printf(m, "R_AX_STA_SCHEDULER_ERR_ISR=0x%08x\n", -			   rtw89_read32(rtwdev, R_AX_STA_SCHEDULER_ERR_ISR)); +		p += scnprintf(p, end - p, +			       "R_AX_STA_SCHEDULER_ERR_IMR=0x%08x\n", +			       rtw89_read32(rtwdev, R_AX_STA_SCHEDULER_ERR_IMR)); +		p += scnprintf(p, end - p, +			       "R_AX_STA_SCHEDULER_ERR_ISR=0x%08x\n", +			       rtw89_read32(rtwdev, R_AX_STA_SCHEDULER_ERR_ISR));  	}  	if (dmac_err & B_AX_WDE_DLE_ERR_FLAG) { -		seq_printf(m, "R_AX_WDE_ERR_IMR=0x%08x\n", -			   rtw89_read32(rtwdev, R_AX_WDE_ERR_IMR)); -		seq_printf(m, "R_AX_WDE_ERR_ISR=0x%08x\n", -			   rtw89_read32(rtwdev, R_AX_WDE_ERR_ISR)); -		seq_printf(m, "R_AX_PLE_ERR_IMR=0x%08x\n", -			   rtw89_read32(rtwdev, R_AX_PLE_ERR_IMR)); -		seq_printf(m, "R_AX_PLE_ERR_FLAG_ISR=0x%08x\n", -			   rtw89_read32(rtwdev, R_AX_PLE_ERR_FLAG_ISR)); +		p += scnprintf(p, end - p, "R_AX_WDE_ERR_IMR=0x%08x\n", +			       rtw89_read32(rtwdev, R_AX_WDE_ERR_IMR)); +		p += scnprintf(p, end - p, "R_AX_WDE_ERR_ISR=0x%08x\n", +			       rtw89_read32(rtwdev, R_AX_WDE_ERR_ISR)); +		p += scnprintf(p, end - p, "R_AX_PLE_ERR_IMR=0x%08x\n", +			       rtw89_read32(rtwdev, R_AX_PLE_ERR_IMR)); +		p += scnprintf(p, end - p, "R_AX_PLE_ERR_FLAG_ISR=0x%08x\n", +			       rtw89_read32(rtwdev, R_AX_PLE_ERR_FLAG_ISR));  	}  	if (dmac_err & B_AX_TXPKTCTRL_ERR_FLAG) {  		if (chip->chip_id == RTL8852C) { -			seq_printf(m, "R_AX_TXPKTCTL_B0_ERRFLAG_IMR=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_TXPKTCTL_B0_ERRFLAG_IMR)); -			seq_printf(m, "R_AX_TXPKTCTL_B0_ERRFLAG_ISR=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_TXPKTCTL_B0_ERRFLAG_ISR)); -			seq_printf(m, "R_AX_TXPKTCTL_B1_ERRFLAG_IMR=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_TXPKTCTL_B1_ERRFLAG_IMR)); -			seq_printf(m, "R_AX_TXPKTCTL_B1_ERRFLAG_ISR=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_TXPKTCTL_B1_ERRFLAG_ISR)); +			p += scnprintf(p, end - p, +				       "R_AX_TXPKTCTL_B0_ERRFLAG_IMR=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_TXPKTCTL_B0_ERRFLAG_IMR)); +			p += scnprintf(p, end - p, +				       "R_AX_TXPKTCTL_B0_ERRFLAG_ISR=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_TXPKTCTL_B0_ERRFLAG_ISR)); +			p += scnprintf(p, end - p, +				       "R_AX_TXPKTCTL_B1_ERRFLAG_IMR=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_TXPKTCTL_B1_ERRFLAG_IMR)); +			p += scnprintf(p, end - p, +				       "R_AX_TXPKTCTL_B1_ERRFLAG_ISR=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_TXPKTCTL_B1_ERRFLAG_ISR));  		} else { -			seq_printf(m, "R_AX_TXPKTCTL_ERR_IMR_ISR=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_TXPKTCTL_ERR_IMR_ISR)); -			seq_printf(m, "R_AX_TXPKTCTL_ERR_IMR_ISR_B1=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_TXPKTCTL_ERR_IMR_ISR_B1)); +			p += scnprintf(p, end - p, +				       "R_AX_TXPKTCTL_ERR_IMR_ISR=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_TXPKTCTL_ERR_IMR_ISR)); +			p += scnprintf(p, end - p, +				       "R_AX_TXPKTCTL_ERR_IMR_ISR_B1=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_TXPKTCTL_ERR_IMR_ISR_B1));  		}  	}  	if (dmac_err & B_AX_PLE_DLE_ERR_FLAG) { -		seq_printf(m, "R_AX_WDE_ERR_IMR=0x%08x\n", -			   rtw89_read32(rtwdev, R_AX_WDE_ERR_IMR)); -		seq_printf(m, "R_AX_WDE_ERR_ISR=0x%08x\n", -			   rtw89_read32(rtwdev, R_AX_WDE_ERR_ISR)); -		seq_printf(m, "R_AX_PLE_ERR_IMR=0x%08x\n", -			   rtw89_read32(rtwdev, R_AX_PLE_ERR_IMR)); -		seq_printf(m, "R_AX_PLE_ERR_FLAG_ISR=0x%08x\n", -			   rtw89_read32(rtwdev, R_AX_PLE_ERR_FLAG_ISR)); -		seq_printf(m, "R_AX_WD_CPUQ_OP_0=0x%08x\n", -			   rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_0)); -		seq_printf(m, "R_AX_WD_CPUQ_OP_1=0x%08x\n", -			   rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_1)); -		seq_printf(m, "R_AX_WD_CPUQ_OP_2=0x%08x\n", -			   rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_2)); -		seq_printf(m, "R_AX_WD_CPUQ_OP_STATUS=0x%08x\n", -			   rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_STATUS)); -		seq_printf(m, "R_AX_PL_CPUQ_OP_0=0x%08x\n", -			   rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_0)); -		seq_printf(m, "R_AX_PL_CPUQ_OP_1=0x%08x\n", -			   rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_1)); -		seq_printf(m, "R_AX_PL_CPUQ_OP_2=0x%08x\n", -			   rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_2)); -		seq_printf(m, "R_AX_PL_CPUQ_OP_STATUS=0x%08x\n", -			   rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_STATUS)); +		p += scnprintf(p, end - p, "R_AX_WDE_ERR_IMR=0x%08x\n", +			       rtw89_read32(rtwdev, R_AX_WDE_ERR_IMR)); +		p += scnprintf(p, end - p, "R_AX_WDE_ERR_ISR=0x%08x\n", +			       rtw89_read32(rtwdev, R_AX_WDE_ERR_ISR)); +		p += scnprintf(p, end - p, "R_AX_PLE_ERR_IMR=0x%08x\n", +			       rtw89_read32(rtwdev, R_AX_PLE_ERR_IMR)); +		p += scnprintf(p, end - p, "R_AX_PLE_ERR_FLAG_ISR=0x%08x\n", +			       rtw89_read32(rtwdev, R_AX_PLE_ERR_FLAG_ISR)); +		p += scnprintf(p, end - p, "R_AX_WD_CPUQ_OP_0=0x%08x\n", +			       rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_0)); +		p += scnprintf(p, end - p, "R_AX_WD_CPUQ_OP_1=0x%08x\n", +			       rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_1)); +		p += scnprintf(p, end - p, "R_AX_WD_CPUQ_OP_2=0x%08x\n", +			       rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_2)); +		p += scnprintf(p, end - p, "R_AX_WD_CPUQ_OP_STATUS=0x%08x\n", +			       rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_STATUS)); +		p += scnprintf(p, end - p, "R_AX_PL_CPUQ_OP_0=0x%08x\n", +			       rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_0)); +		p += scnprintf(p, end - p, "R_AX_PL_CPUQ_OP_1=0x%08x\n", +			       rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_1)); +		p += scnprintf(p, end - p, "R_AX_PL_CPUQ_OP_2=0x%08x\n", +			       rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_2)); +		p += scnprintf(p, end - p, "R_AX_PL_CPUQ_OP_STATUS=0x%08x\n", +			       rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_STATUS));  		if (chip->chip_id == RTL8852C) { -			seq_printf(m, "R_AX_RX_CTRL0=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_RX_CTRL0)); -			seq_printf(m, "R_AX_RX_CTRL1=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_RX_CTRL1)); -			seq_printf(m, "R_AX_RX_CTRL2=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_RX_CTRL2)); +			p += scnprintf(p, end - p, "R_AX_RX_CTRL0=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_RX_CTRL0)); +			p += scnprintf(p, end - p, "R_AX_RX_CTRL1=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_RX_CTRL1)); +			p += scnprintf(p, end - p, "R_AX_RX_CTRL2=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_RX_CTRL2));  		} else { -			seq_printf(m, "R_AX_RXDMA_PKT_INFO_0=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_RXDMA_PKT_INFO_0)); -			seq_printf(m, "R_AX_RXDMA_PKT_INFO_1=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_RXDMA_PKT_INFO_1)); -			seq_printf(m, "R_AX_RXDMA_PKT_INFO_2=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_RXDMA_PKT_INFO_2)); +			p += scnprintf(p, end - p, +				       "R_AX_RXDMA_PKT_INFO_0=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_RXDMA_PKT_INFO_0)); +			p += scnprintf(p, end - p, +				       "R_AX_RXDMA_PKT_INFO_1=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_RXDMA_PKT_INFO_1)); +			p += scnprintf(p, end - p, +				       "R_AX_RXDMA_PKT_INFO_2=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_RXDMA_PKT_INFO_2));  		}  	}  	if (dmac_err & B_AX_PKTIN_ERR_FLAG) { -		seq_printf(m, "R_AX_PKTIN_ERR_IMR=0x%08x\n", -			   rtw89_read32(rtwdev, R_AX_PKTIN_ERR_IMR)); -		seq_printf(m, "R_AX_PKTIN_ERR_ISR=0x%08x\n", -			   rtw89_read32(rtwdev, R_AX_PKTIN_ERR_ISR)); +		p += scnprintf(p, end - p, "R_AX_PKTIN_ERR_IMR=0x%08x\n", +			       rtw89_read32(rtwdev, R_AX_PKTIN_ERR_IMR)); +		p += scnprintf(p, end - p, "R_AX_PKTIN_ERR_ISR=0x%08x\n", +			       rtw89_read32(rtwdev, R_AX_PKTIN_ERR_ISR));  	}  	if (dmac_err & B_AX_DISPATCH_ERR_FLAG) { -		seq_printf(m, "R_AX_HOST_DISPATCHER_ERR_IMR=0x%08x\n", -			   rtw89_read32(rtwdev, R_AX_HOST_DISPATCHER_ERR_IMR)); -		seq_printf(m, "R_AX_HOST_DISPATCHER_ERR_ISR=0x%08x\n", -			   rtw89_read32(rtwdev, R_AX_HOST_DISPATCHER_ERR_ISR)); -		seq_printf(m, "R_AX_CPU_DISPATCHER_ERR_IMR=0x%08x\n", -			   rtw89_read32(rtwdev, R_AX_CPU_DISPATCHER_ERR_IMR)); -		seq_printf(m, "R_AX_CPU_DISPATCHER_ERR_ISR=0x%08x\n", -			   rtw89_read32(rtwdev, R_AX_CPU_DISPATCHER_ERR_ISR)); -		seq_printf(m, "R_AX_OTHER_DISPATCHER_ERR_IMR=0x%08x\n", -			   rtw89_read32(rtwdev, R_AX_OTHER_DISPATCHER_ERR_IMR)); -		seq_printf(m, "R_AX_OTHER_DISPATCHER_ERR_ISR=0x%08x\n", -			   rtw89_read32(rtwdev, R_AX_OTHER_DISPATCHER_ERR_ISR)); +		p += scnprintf(p, end - p, +			       "R_AX_HOST_DISPATCHER_ERR_IMR=0x%08x\n", +			       rtw89_read32(rtwdev, R_AX_HOST_DISPATCHER_ERR_IMR)); +		p += scnprintf(p, end - p, +			       "R_AX_HOST_DISPATCHER_ERR_ISR=0x%08x\n", +			       rtw89_read32(rtwdev, R_AX_HOST_DISPATCHER_ERR_ISR)); +		p += scnprintf(p, end - p, +			       "R_AX_CPU_DISPATCHER_ERR_IMR=0x%08x\n", +			       rtw89_read32(rtwdev, R_AX_CPU_DISPATCHER_ERR_IMR)); +		p += scnprintf(p, end - p, +			       "R_AX_CPU_DISPATCHER_ERR_ISR=0x%08x\n", +			       rtw89_read32(rtwdev, R_AX_CPU_DISPATCHER_ERR_ISR)); +		p += scnprintf(p, end - p, +			       "R_AX_OTHER_DISPATCHER_ERR_IMR=0x%08x\n", +			       rtw89_read32(rtwdev, R_AX_OTHER_DISPATCHER_ERR_IMR)); +		p += scnprintf(p, end - p, +			       "R_AX_OTHER_DISPATCHER_ERR_ISR=0x%08x\n", +			       rtw89_read32(rtwdev, R_AX_OTHER_DISPATCHER_ERR_ISR));  	}  	if (dmac_err & B_AX_BBRPT_ERR_FLAG) {  		if (chip->chip_id == RTL8852C) { -			seq_printf(m, "R_AX_BBRPT_COM_ERR_IMR=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_BBRPT_COM_ERR_IMR)); -			seq_printf(m, "R_AX_BBRPT_COM_ERR_ISR=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_BBRPT_COM_ERR_ISR)); -			seq_printf(m, "R_AX_BBRPT_CHINFO_ERR_ISR=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_ISR)); -			seq_printf(m, "R_AX_BBRPT_CHINFO_ERR_IMR=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_IMR)); -			seq_printf(m, "R_AX_BBRPT_DFS_ERR_IMR=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_IMR)); -			seq_printf(m, "R_AX_BBRPT_DFS_ERR_ISR=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_ISR)); +			p += scnprintf(p, end - p, +				       "R_AX_BBRPT_COM_ERR_IMR=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_BBRPT_COM_ERR_IMR)); +			p += scnprintf(p, end - p, +				       "R_AX_BBRPT_COM_ERR_ISR=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_BBRPT_COM_ERR_ISR)); +			p += scnprintf(p, end - p, +				       "R_AX_BBRPT_CHINFO_ERR_ISR=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_ISR)); +			p += scnprintf(p, end - p, +				       "R_AX_BBRPT_CHINFO_ERR_IMR=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_IMR)); +			p += scnprintf(p, end - p, +				       "R_AX_BBRPT_DFS_ERR_IMR=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_IMR)); +			p += scnprintf(p, end - p, +				       "R_AX_BBRPT_DFS_ERR_ISR=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_ISR));  		} else { -			seq_printf(m, "R_AX_BBRPT_COM_ERR_IMR_ISR=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_BBRPT_COM_ERR_IMR_ISR)); -			seq_printf(m, "R_AX_BBRPT_CHINFO_ERR_ISR=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_ISR)); -			seq_printf(m, "R_AX_BBRPT_CHINFO_ERR_IMR=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_IMR)); -			seq_printf(m, "R_AX_BBRPT_DFS_ERR_IMR=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_IMR)); -			seq_printf(m, "R_AX_BBRPT_DFS_ERR_ISR=0x%08x\n", -				   rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_ISR)); +			p += scnprintf(p, end - p, +				       "R_AX_BBRPT_COM_ERR_IMR_ISR=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_BBRPT_COM_ERR_IMR_ISR)); +			p += scnprintf(p, end - p, +				       "R_AX_BBRPT_CHINFO_ERR_ISR=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_ISR)); +			p += scnprintf(p, end - p, +				       "R_AX_BBRPT_CHINFO_ERR_IMR=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_IMR)); +			p += scnprintf(p, end - p, +				       "R_AX_BBRPT_DFS_ERR_IMR=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_IMR)); +			p += scnprintf(p, end - p, +				       "R_AX_BBRPT_DFS_ERR_ISR=0x%08x\n", +				       rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_ISR));  		}  	}  	if (dmac_err & B_AX_HAXIDMA_ERR_FLAG && chip->chip_id == RTL8852C) { -		seq_printf(m, "R_AX_HAXIDMA_ERR_IMR=0x%08x\n", -			   rtw89_read32(rtwdev, R_AX_HAXI_IDCT_MSK)); -		seq_printf(m, "R_AX_HAXIDMA_ERR_ISR=0x%08x\n", -			   rtw89_read32(rtwdev, R_AX_HAXI_IDCT)); +		p += scnprintf(p, end - p, "R_AX_HAXIDMA_ERR_IMR=0x%08x\n", +			       rtw89_read32(rtwdev, R_AX_HAXI_IDCT_MSK)); +		p += scnprintf(p, end - p, "R_AX_HAXIDMA_ERR_ISR=0x%08x\n", +			       rtw89_read32(rtwdev, R_AX_HAXI_IDCT));  	} -	return 0; +out: +	return p - buf;  }  static int rtw89_debug_mac_dump_cmac_err(struct rtw89_dev *rtwdev, -					 struct seq_file *m, +					 char *buf, size_t bufsz,  					 enum rtw89_mac_idx band)  {  	const struct rtw89_chip_info *chip = rtwdev->chip; +	char *p = buf, *end = buf + bufsz;  	u32 offset = 0;  	u32 cmac_err;  	int ret; @@ -1591,96 +1686,127 @@ static int rtw89_debug_mac_dump_cmac_err(struct rtw89_dev *rtwdev,  	ret = rtw89_mac_check_mac_en(rtwdev, band, RTW89_CMAC_SEL);  	if (ret) {  		if (band) -			seq_puts(m, "[CMAC] : CMAC1 not enabled\n"); +			p += scnprintf(p, end - p, +				       "[CMAC] : CMAC1 not enabled\n");  		else -			seq_puts(m, "[CMAC] : CMAC0 not enabled\n"); -		return ret; +			p += scnprintf(p, end - p, +				       "[CMAC] : CMAC0 not enabled\n"); +		goto out;  	}  	if (band)  		offset = RTW89_MAC_AX_BAND_REG_OFFSET;  	cmac_err = rtw89_read32(rtwdev, R_AX_CMAC_ERR_ISR + offset); -	seq_printf(m, "R_AX_CMAC_ERR_ISR [%d]=0x%08x\n", band, -		   rtw89_read32(rtwdev, R_AX_CMAC_ERR_ISR + offset)); -	seq_printf(m, "R_AX_CMAC_FUNC_EN [%d]=0x%08x\n", band, -		   rtw89_read32(rtwdev, R_AX_CMAC_FUNC_EN + offset)); -	seq_printf(m, "R_AX_CK_EN [%d]=0x%08x\n", band, -		   rtw89_read32(rtwdev, R_AX_CK_EN + offset)); +	p += scnprintf(p, end - p, "R_AX_CMAC_ERR_ISR [%d]=0x%08x\n", band, +		       rtw89_read32(rtwdev, R_AX_CMAC_ERR_ISR + offset)); +	p += scnprintf(p, end - p, "R_AX_CMAC_FUNC_EN [%d]=0x%08x\n", band, +		       rtw89_read32(rtwdev, R_AX_CMAC_FUNC_EN + offset)); +	p += scnprintf(p, end - p, "R_AX_CK_EN [%d]=0x%08x\n", band, +		       rtw89_read32(rtwdev, R_AX_CK_EN + offset));  	if (cmac_err & B_AX_SCHEDULE_TOP_ERR_IND) { -		seq_printf(m, "R_AX_SCHEDULE_ERR_IMR [%d]=0x%08x\n", band, -			   rtw89_read32(rtwdev, R_AX_SCHEDULE_ERR_IMR + offset)); -		seq_printf(m, "R_AX_SCHEDULE_ERR_ISR [%d]=0x%08x\n", band, -			   rtw89_read32(rtwdev, R_AX_SCHEDULE_ERR_ISR + offset)); +		p += scnprintf(p, end - p, +			       "R_AX_SCHEDULE_ERR_IMR [%d]=0x%08x\n", band, +			       rtw89_read32(rtwdev, R_AX_SCHEDULE_ERR_IMR + offset)); +		p += scnprintf(p, end - p, +			       "R_AX_SCHEDULE_ERR_ISR [%d]=0x%08x\n", band, +			       rtw89_read32(rtwdev, R_AX_SCHEDULE_ERR_ISR + offset));  	}  	if (cmac_err & B_AX_PTCL_TOP_ERR_IND) { -		seq_printf(m, "R_AX_PTCL_IMR0 [%d]=0x%08x\n", band, -			   rtw89_read32(rtwdev, R_AX_PTCL_IMR0 + offset)); -		seq_printf(m, "R_AX_PTCL_ISR0 [%d]=0x%08x\n", band, -			   rtw89_read32(rtwdev, R_AX_PTCL_ISR0 + offset)); +		p += scnprintf(p, end - p, "R_AX_PTCL_IMR0 [%d]=0x%08x\n", +			       band, +			       rtw89_read32(rtwdev, R_AX_PTCL_IMR0 + offset)); +		p += scnprintf(p, end - p, "R_AX_PTCL_ISR0 [%d]=0x%08x\n", +			       band, +			       rtw89_read32(rtwdev, R_AX_PTCL_ISR0 + offset));  	}  	if (cmac_err & B_AX_DMA_TOP_ERR_IND) {  		if (chip->chip_id == RTL8852C) { -			seq_printf(m, "R_AX_RX_ERR_FLAG [%d]=0x%08x\n", band, -				   rtw89_read32(rtwdev, R_AX_RX_ERR_FLAG + offset)); -			seq_printf(m, "R_AX_RX_ERR_FLAG_IMR [%d]=0x%08x\n", band, -				   rtw89_read32(rtwdev, R_AX_RX_ERR_FLAG_IMR + offset)); +			p += scnprintf(p, end - p, +				       "R_AX_RX_ERR_FLAG [%d]=0x%08x\n", band, +				       rtw89_read32(rtwdev, R_AX_RX_ERR_FLAG + offset)); +			p += scnprintf(p, end - p, +				       "R_AX_RX_ERR_FLAG_IMR [%d]=0x%08x\n", +				       band, +				       rtw89_read32(rtwdev, R_AX_RX_ERR_FLAG_IMR + offset));  		} else { -			seq_printf(m, "R_AX_DLE_CTRL [%d]=0x%08x\n", band, -				   rtw89_read32(rtwdev, R_AX_DLE_CTRL + offset)); +			p += scnprintf(p, end - p, +				       "R_AX_DLE_CTRL [%d]=0x%08x\n", band, +				       rtw89_read32(rtwdev, R_AX_DLE_CTRL + offset));  		}  	}  	if (cmac_err & B_AX_DMA_TOP_ERR_IND || cmac_err & B_AX_WMAC_RX_ERR_IND) {  		if (chip->chip_id == RTL8852C) { -			seq_printf(m, "R_AX_PHYINFO_ERR_ISR [%d]=0x%08x\n", band, -				   rtw89_read32(rtwdev, R_AX_PHYINFO_ERR_ISR + offset)); -			seq_printf(m, "R_AX_PHYINFO_ERR_IMR [%d]=0x%08x\n", band, -				   rtw89_read32(rtwdev, R_AX_PHYINFO_ERR_IMR + offset)); +			p += scnprintf(p, end - p, +				       "R_AX_PHYINFO_ERR_ISR [%d]=0x%08x\n", +				       band, +				       rtw89_read32(rtwdev, R_AX_PHYINFO_ERR_ISR + offset)); +			p += scnprintf(p, end - p, +				       "R_AX_PHYINFO_ERR_IMR [%d]=0x%08x\n", +				       band, +				       rtw89_read32(rtwdev, R_AX_PHYINFO_ERR_IMR + offset));  		} else { -			seq_printf(m, "R_AX_PHYINFO_ERR_IMR [%d]=0x%08x\n", band, -				   rtw89_read32(rtwdev, R_AX_PHYINFO_ERR_IMR + offset)); +			p += scnprintf(p, end - p, +				       "R_AX_PHYINFO_ERR_IMR [%d]=0x%08x\n", +				       band, +				       rtw89_read32(rtwdev, R_AX_PHYINFO_ERR_IMR + offset));  		}  	}  	if (cmac_err & B_AX_TXPWR_CTRL_ERR_IND) { -		seq_printf(m, "R_AX_TXPWR_IMR [%d]=0x%08x\n", band, -			   rtw89_read32(rtwdev, R_AX_TXPWR_IMR + offset)); -		seq_printf(m, "R_AX_TXPWR_ISR [%d]=0x%08x\n", band, -			   rtw89_read32(rtwdev, R_AX_TXPWR_ISR + offset)); +		p += scnprintf(p, end - p, "R_AX_TXPWR_IMR [%d]=0x%08x\n", +			       band, +			       rtw89_read32(rtwdev, R_AX_TXPWR_IMR + offset)); +		p += scnprintf(p, end - p, "R_AX_TXPWR_ISR [%d]=0x%08x\n", +			       band, +			       rtw89_read32(rtwdev, R_AX_TXPWR_ISR + offset));  	}  	if (cmac_err & B_AX_WMAC_TX_ERR_IND) {  		if (chip->chip_id == RTL8852C) { -			seq_printf(m, "R_AX_TRXPTCL_ERROR_INDICA [%d]=0x%08x\n", band, -				   rtw89_read32(rtwdev, R_AX_TRXPTCL_ERROR_INDICA + offset)); -			seq_printf(m, "R_AX_TRXPTCL_ERROR_INDICA_MASK [%d]=0x%08x\n", band, -				   rtw89_read32(rtwdev, R_AX_TRXPTCL_ERROR_INDICA_MASK + offset)); +			p += scnprintf(p, end - p, +				       "R_AX_TRXPTCL_ERROR_INDICA [%d]=0x%08x\n", +				       band, +				       rtw89_read32(rtwdev, +						    R_AX_TRXPTCL_ERROR_INDICA + offset)); +			p += scnprintf(p, end - p, +				       "R_AX_TRXPTCL_ERROR_INDICA_MASK [%d]=0x%08x\n", +				       band, +				       rtw89_read32(rtwdev, +						    R_AX_TRXPTCL_ERROR_INDICA_MASK + offset));  		} else { -			seq_printf(m, "R_AX_TMAC_ERR_IMR_ISR [%d]=0x%08x\n", band, -				   rtw89_read32(rtwdev, R_AX_TMAC_ERR_IMR_ISR + offset)); +			p += scnprintf(p, end - p, +				       "R_AX_TMAC_ERR_IMR_ISR [%d]=0x%08x\n", +				       band, +				       rtw89_read32(rtwdev, +						    R_AX_TMAC_ERR_IMR_ISR + offset));  		} -		seq_printf(m, "R_AX_DBGSEL_TRXPTCL [%d]=0x%08x\n", band, -			   rtw89_read32(rtwdev, R_AX_DBGSEL_TRXPTCL + offset)); +		p += scnprintf(p, end - p, +			       "R_AX_DBGSEL_TRXPTCL [%d]=0x%08x\n", band, +			       rtw89_read32(rtwdev, R_AX_DBGSEL_TRXPTCL + offset));  	} -	seq_printf(m, "R_AX_CMAC_ERR_IMR [%d]=0x%08x\n", band, -		   rtw89_read32(rtwdev, R_AX_CMAC_ERR_IMR + offset)); +	p += scnprintf(p, end - p, "R_AX_CMAC_ERR_IMR [%d]=0x%08x\n", band, +		       rtw89_read32(rtwdev, R_AX_CMAC_ERR_IMR + offset)); -	return 0; +out: +	return p - buf;  }  static int rtw89_debug_mac_dump_cmac_dbg(struct rtw89_dev *rtwdev, -					 struct seq_file *m) +					 char *buf, size_t bufsz)  { -	rtw89_debug_mac_dump_cmac_err(rtwdev, m, RTW89_MAC_0); +	char *p = buf, *end = buf + bufsz; + +	p += rtw89_debug_mac_dump_cmac_err(rtwdev, p, end - p, RTW89_MAC_0);  	if (rtwdev->dbcc_en) -		rtw89_debug_mac_dump_cmac_err(rtwdev, m, RTW89_MAC_1); +		p += rtw89_debug_mac_dump_cmac_err(rtwdev, p, end - p, RTW89_MAC_1); -	return 0; +	return p - buf;  }  static const struct rtw89_mac_dbg_port_info dbg_port_ptcl_c0 = { @@ -2486,11 +2612,12 @@ static const struct rtw89_mac_dbg_port_info dbg_port_pcie_misc2 = {  	.rd_msk = B_AX_DEBUG_ST_MASK  }; -static const struct rtw89_mac_dbg_port_info * -rtw89_debug_mac_dbg_port_sel(struct seq_file *m, -			     struct rtw89_dev *rtwdev, u32 sel) +static int +rtw89_debug_mac_dbg_port_sel(struct rtw89_dev *rtwdev, char *buf, size_t bufsz, +			     u32 sel, const struct rtw89_mac_dbg_port_info **ppinfo)  { -	const struct rtw89_mac_dbg_port_info *info; +	const struct rtw89_mac_dbg_port_info *info = NULL; +	char *p = buf, *end = buf + bufsz;  	u32 index;  	u32 val32;  	u16 val16; @@ -2502,28 +2629,28 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,  		val16 = rtw89_read16(rtwdev, R_AX_PTCL_DBG);  		val16 |= B_AX_PTCL_DBG_EN;  		rtw89_write16(rtwdev, R_AX_PTCL_DBG, val16); -		seq_puts(m, "Enable PTCL C0 dbgport.\n"); +		p += scnprintf(p, end - p, "Enable PTCL C0 dbgport.\n");  		break;  	case RTW89_DBG_PORT_SEL_PTCL_C1:  		info = &dbg_port_ptcl_c1;  		val16 = rtw89_read16(rtwdev, R_AX_PTCL_DBG_C1);  		val16 |= B_AX_PTCL_DBG_EN;  		rtw89_write16(rtwdev, R_AX_PTCL_DBG_C1, val16); -		seq_puts(m, "Enable PTCL C1 dbgport.\n"); +		p += scnprintf(p, end - p, "Enable PTCL C1 dbgport.\n");  		break;  	case RTW89_DBG_PORT_SEL_SCH_C0:  		info = &dbg_port_sch_c0;  		val32 = rtw89_read32(rtwdev, R_AX_SCH_DBG_SEL);  		val32 |= B_AX_SCH_DBG_EN;  		rtw89_write32(rtwdev, R_AX_SCH_DBG_SEL, val32); -		seq_puts(m, "Enable SCH C0 dbgport.\n"); +		p += scnprintf(p, end - p, "Enable SCH C0 dbgport.\n");  		break;  	case RTW89_DBG_PORT_SEL_SCH_C1:  		info = &dbg_port_sch_c1;  		val32 = rtw89_read32(rtwdev, R_AX_SCH_DBG_SEL_C1);  		val32 |= B_AX_SCH_DBG_EN;  		rtw89_write32(rtwdev, R_AX_SCH_DBG_SEL_C1, val32); -		seq_puts(m, "Enable SCH C1 dbgport.\n"); +		p += scnprintf(p, end - p, "Enable SCH C1 dbgport.\n");  		break;  	case RTW89_DBG_PORT_SEL_TMAC_C0:  		info = &dbg_port_tmac_c0; @@ -2540,7 +2667,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,  		val32 = rtw89_read32(rtwdev, R_AX_SYS_STATUS1);  		val32 = u32_replace_bits(val32, MAC_DBG_SEL, B_AX_SEL_0XC0_MASK);  		rtw89_write32(rtwdev, R_AX_SYS_STATUS1, val32); -		seq_puts(m, "Enable TMAC C0 dbgport.\n"); +		p += scnprintf(p, end - p, "Enable TMAC C0 dbgport.\n");  		break;  	case RTW89_DBG_PORT_SEL_TMAC_C1:  		info = &dbg_port_tmac_c1; @@ -2557,7 +2684,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,  		val32 = rtw89_read32(rtwdev, R_AX_SYS_STATUS1);  		val32 = u32_replace_bits(val32, MAC_DBG_SEL, B_AX_SEL_0XC0_MASK);  		rtw89_write32(rtwdev, R_AX_SYS_STATUS1, val32); -		seq_puts(m, "Enable TMAC C1 dbgport.\n"); +		p += scnprintf(p, end - p, "Enable TMAC C1 dbgport.\n");  		break;  	case RTW89_DBG_PORT_SEL_RMAC_C0:  		info = &dbg_port_rmac_c0; @@ -2579,7 +2706,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,  		val8 = u8_replace_bits(val8, RMAC_CMAC_DBG_SEL,  				       B_AX_DBGSEL_TRXPTCL_MASK);  		rtw89_write8(rtwdev, R_AX_DBGSEL_TRXPTCL, val8); -		seq_puts(m, "Enable RMAC C0 dbgport.\n"); +		p += scnprintf(p, end - p, "Enable RMAC C0 dbgport.\n");  		break;  	case RTW89_DBG_PORT_SEL_RMAC_C1:  		info = &dbg_port_rmac_c1; @@ -2601,23 +2728,23 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,  		val8 = u8_replace_bits(val8, RMAC_CMAC_DBG_SEL,  				       B_AX_DBGSEL_TRXPTCL_MASK);  		rtw89_write8(rtwdev, R_AX_DBGSEL_TRXPTCL_C1, val8); -		seq_puts(m, "Enable RMAC C1 dbgport.\n"); +		p += scnprintf(p, end - p, "Enable RMAC C1 dbgport.\n");  		break;  	case RTW89_DBG_PORT_SEL_RMACST_C0:  		info = &dbg_port_rmacst_c0; -		seq_puts(m, "Enable RMAC state C0 dbgport.\n"); +		p += scnprintf(p, end - p, "Enable RMAC state C0 dbgport.\n");  		break;  	case RTW89_DBG_PORT_SEL_RMACST_C1:  		info = &dbg_port_rmacst_c1; -		seq_puts(m, "Enable RMAC state C1 dbgport.\n"); +		p += scnprintf(p, end - p, "Enable RMAC state C1 dbgport.\n");  		break;  	case RTW89_DBG_PORT_SEL_RMAC_PLCP_C0:  		info = &dbg_port_rmac_plcp_c0; -		seq_puts(m, "Enable RMAC PLCP C0 dbgport.\n"); +		p += scnprintf(p, end - p, "Enable RMAC PLCP C0 dbgport.\n");  		break;  	case RTW89_DBG_PORT_SEL_RMAC_PLCP_C1:  		info = &dbg_port_rmac_plcp_c1; -		seq_puts(m, "Enable RMAC PLCP C1 dbgport.\n"); +		p += scnprintf(p, end - p, "Enable RMAC PLCP C1 dbgport.\n");  		break;  	case RTW89_DBG_PORT_SEL_TRXPTCL_C0:  		info = &dbg_port_trxptcl_c0; @@ -2629,7 +2756,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,  		val32 = rtw89_read32(rtwdev, R_AX_SYS_STATUS1);  		val32 = u32_replace_bits(val32, MAC_DBG_SEL, B_AX_SEL_0XC0_MASK);  		rtw89_write32(rtwdev, R_AX_SYS_STATUS1, val32); -		seq_puts(m, "Enable TRXPTCL C0 dbgport.\n"); +		p += scnprintf(p, end - p, "Enable TRXPTCL C0 dbgport.\n");  		break;  	case RTW89_DBG_PORT_SEL_TRXPTCL_C1:  		info = &dbg_port_trxptcl_c1; @@ -2641,131 +2768,137 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,  		val32 = rtw89_read32(rtwdev, R_AX_SYS_STATUS1);  		val32 = u32_replace_bits(val32, MAC_DBG_SEL, B_AX_SEL_0XC0_MASK);  		rtw89_write32(rtwdev, R_AX_SYS_STATUS1, val32); -		seq_puts(m, "Enable TRXPTCL C1 dbgport.\n"); +		p += scnprintf(p, end - p, "Enable TRXPTCL C1 dbgport.\n");  		break;  	case RTW89_DBG_PORT_SEL_TX_INFOL_C0:  		info = &dbg_port_tx_infol_c0;  		val32 = rtw89_read32(rtwdev, R_AX_TCR1);  		val32 |= B_AX_TCR_FORCE_READ_TXDFIFO;  		rtw89_write32(rtwdev, R_AX_TCR1, val32); -		seq_puts(m, "Enable tx infol dump.\n"); +		p += scnprintf(p, end - p, "Enable tx infol dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_TX_INFOH_C0:  		info = &dbg_port_tx_infoh_c0;  		val32 = rtw89_read32(rtwdev, R_AX_TCR1);  		val32 |= B_AX_TCR_FORCE_READ_TXDFIFO;  		rtw89_write32(rtwdev, R_AX_TCR1, val32); -		seq_puts(m, "Enable tx infoh dump.\n"); +		p += scnprintf(p, end - p, "Enable tx infoh dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_TX_INFOL_C1:  		info = &dbg_port_tx_infol_c1;  		val32 = rtw89_read32(rtwdev, R_AX_TCR1_C1);  		val32 |= B_AX_TCR_FORCE_READ_TXDFIFO;  		rtw89_write32(rtwdev, R_AX_TCR1_C1, val32); -		seq_puts(m, "Enable tx infol dump.\n"); +		p += scnprintf(p, end - p, "Enable tx infol dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_TX_INFOH_C1:  		info = &dbg_port_tx_infoh_c1;  		val32 = rtw89_read32(rtwdev, R_AX_TCR1_C1);  		val32 |= B_AX_TCR_FORCE_READ_TXDFIFO;  		rtw89_write32(rtwdev, R_AX_TCR1_C1, val32); -		seq_puts(m, "Enable tx infoh dump.\n"); +		p += scnprintf(p, end - p, "Enable tx infoh dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_TXTF_INFOL_C0:  		info = &dbg_port_txtf_infol_c0;  		val32 = rtw89_read32(rtwdev, R_AX_TCR1);  		val32 |= B_AX_TCR_FORCE_READ_TXDFIFO;  		rtw89_write32(rtwdev, R_AX_TCR1, val32); -		seq_puts(m, "Enable tx tf infol dump.\n"); +		p += scnprintf(p, end - p, "Enable tx tf infol dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_TXTF_INFOH_C0:  		info = &dbg_port_txtf_infoh_c0;  		val32 = rtw89_read32(rtwdev, R_AX_TCR1);  		val32 |= B_AX_TCR_FORCE_READ_TXDFIFO;  		rtw89_write32(rtwdev, R_AX_TCR1, val32); -		seq_puts(m, "Enable tx tf infoh dump.\n"); +		p += scnprintf(p, end - p, "Enable tx tf infoh dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_TXTF_INFOL_C1:  		info = &dbg_port_txtf_infol_c1;  		val32 = rtw89_read32(rtwdev, R_AX_TCR1_C1);  		val32 |= B_AX_TCR_FORCE_READ_TXDFIFO;  		rtw89_write32(rtwdev, R_AX_TCR1_C1, val32); -		seq_puts(m, "Enable tx tf infol dump.\n"); +		p += scnprintf(p, end - p, "Enable tx tf infol dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_TXTF_INFOH_C1:  		info = &dbg_port_txtf_infoh_c1;  		val32 = rtw89_read32(rtwdev, R_AX_TCR1_C1);  		val32 |= B_AX_TCR_FORCE_READ_TXDFIFO;  		rtw89_write32(rtwdev, R_AX_TCR1_C1, val32); -		seq_puts(m, "Enable tx tf infoh dump.\n"); +		p += scnprintf(p, end - p, "Enable tx tf infoh dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_WDE_BUFMGN_FREEPG:  		info = &dbg_port_wde_bufmgn_freepg; -		seq_puts(m, "Enable wde bufmgn freepg dump.\n"); +		p += scnprintf(p, end - p, "Enable wde bufmgn freepg dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_WDE_BUFMGN_QUOTA:  		info = &dbg_port_wde_bufmgn_quota; -		seq_puts(m, "Enable wde bufmgn quota dump.\n"); +		p += scnprintf(p, end - p, "Enable wde bufmgn quota dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_WDE_BUFMGN_PAGELLT:  		info = &dbg_port_wde_bufmgn_pagellt; -		seq_puts(m, "Enable wde bufmgn pagellt dump.\n"); +		p += scnprintf(p, end - p, +			       "Enable wde bufmgn pagellt dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_WDE_BUFMGN_PKTINFO:  		info = &dbg_port_wde_bufmgn_pktinfo; -		seq_puts(m, "Enable wde bufmgn pktinfo dump.\n"); +		p += scnprintf(p, end - p, +			       "Enable wde bufmgn pktinfo dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_WDE_QUEMGN_PREPKT:  		info = &dbg_port_wde_quemgn_prepkt; -		seq_puts(m, "Enable wde quemgn prepkt dump.\n"); +		p += scnprintf(p, end - p, "Enable wde quemgn prepkt dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_WDE_QUEMGN_NXTPKT:  		info = &dbg_port_wde_quemgn_nxtpkt; -		seq_puts(m, "Enable wde quemgn nxtpkt dump.\n"); +		p += scnprintf(p, end - p, "Enable wde quemgn nxtpkt dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_WDE_QUEMGN_QLNKTBL:  		info = &dbg_port_wde_quemgn_qlnktbl; -		seq_puts(m, "Enable wde quemgn qlnktbl dump.\n"); +		p += scnprintf(p, end - p, +			       "Enable wde quemgn qlnktbl dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_WDE_QUEMGN_QEMPTY:  		info = &dbg_port_wde_quemgn_qempty; -		seq_puts(m, "Enable wde quemgn qempty dump.\n"); +		p += scnprintf(p, end - p, "Enable wde quemgn qempty dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_PLE_BUFMGN_FREEPG:  		info = &dbg_port_ple_bufmgn_freepg; -		seq_puts(m, "Enable ple bufmgn freepg dump.\n"); +		p += scnprintf(p, end - p, "Enable ple bufmgn freepg dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_PLE_BUFMGN_QUOTA:  		info = &dbg_port_ple_bufmgn_quota; -		seq_puts(m, "Enable ple bufmgn quota dump.\n"); +		p += scnprintf(p, end - p, "Enable ple bufmgn quota dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_PLE_BUFMGN_PAGELLT:  		info = &dbg_port_ple_bufmgn_pagellt; -		seq_puts(m, "Enable ple bufmgn pagellt dump.\n"); +		p += scnprintf(p, end - p, +			       "Enable ple bufmgn pagellt dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_PLE_BUFMGN_PKTINFO:  		info = &dbg_port_ple_bufmgn_pktinfo; -		seq_puts(m, "Enable ple bufmgn pktinfo dump.\n"); +		p += scnprintf(p, end - p, +			       "Enable ple bufmgn pktinfo dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_PLE_QUEMGN_PREPKT:  		info = &dbg_port_ple_quemgn_prepkt; -		seq_puts(m, "Enable ple quemgn prepkt dump.\n"); +		p += scnprintf(p, end - p, "Enable ple quemgn prepkt dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_PLE_QUEMGN_NXTPKT:  		info = &dbg_port_ple_quemgn_nxtpkt; -		seq_puts(m, "Enable ple quemgn nxtpkt dump.\n"); +		p += scnprintf(p, end - p, "Enable ple quemgn nxtpkt dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_PLE_QUEMGN_QLNKTBL:  		info = &dbg_port_ple_quemgn_qlnktbl; -		seq_puts(m, "Enable ple quemgn qlnktbl dump.\n"); +		p += scnprintf(p, end - p, +			       "Enable ple quemgn qlnktbl dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_PLE_QUEMGN_QEMPTY:  		info = &dbg_port_ple_quemgn_qempty; -		seq_puts(m, "Enable ple quemgn qempty dump.\n"); +		p += scnprintf(p, end - p, "Enable ple quemgn qempty dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_PKTINFO:  		info = &dbg_port_pktinfo; -		seq_puts(m, "Enable pktinfo dump.\n"); +		p += scnprintf(p, end - p, "Enable pktinfo dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_DSPT_HDT_TX0:  		rtw89_write32_mask(rtwdev, R_AX_DBG_CTRL, @@ -2784,7 +2917,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,  				   B_AX_DISPATCHER_INTN_SEL_MASK, 0);  		rtw89_write16_mask(rtwdev, info->sel_addr,  				   B_AX_DISPATCHER_CH_SEL_MASK, index); -		seq_printf(m, "Enable Dispatcher hdt tx%x dump.\n", index); +		p += scnprintf(p, end - p, +			       "Enable Dispatcher hdt tx%x dump.\n", index);  		break;  	case RTW89_DBG_PORT_SEL_DSPT_HDT_TX6:  		info = &dbg_port_dspt_hdt_tx6; @@ -2792,7 +2926,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,  				   B_AX_DISPATCHER_INTN_SEL_MASK, 0);  		rtw89_write16_mask(rtwdev, info->sel_addr,  				   B_AX_DISPATCHER_CH_SEL_MASK, 6); -		seq_puts(m, "Enable Dispatcher hdt tx6 dump.\n"); +		p += scnprintf(p, end - p, +			       "Enable Dispatcher hdt tx6 dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_DSPT_HDT_TX7:  		info = &dbg_port_dspt_hdt_tx7; @@ -2800,7 +2935,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,  				   B_AX_DISPATCHER_INTN_SEL_MASK, 0);  		rtw89_write16_mask(rtwdev, info->sel_addr,  				   B_AX_DISPATCHER_CH_SEL_MASK, 7); -		seq_puts(m, "Enable Dispatcher hdt tx7 dump.\n"); +		p += scnprintf(p, end - p, +			       "Enable Dispatcher hdt tx7 dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_DSPT_HDT_TX8:  		info = &dbg_port_dspt_hdt_tx8; @@ -2808,7 +2944,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,  				   B_AX_DISPATCHER_INTN_SEL_MASK, 0);  		rtw89_write16_mask(rtwdev, info->sel_addr,  				   B_AX_DISPATCHER_CH_SEL_MASK, 8); -		seq_puts(m, "Enable Dispatcher hdt tx8 dump.\n"); +		p += scnprintf(p, end - p, +			       "Enable Dispatcher hdt tx8 dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_DSPT_HDT_TX9:  	case RTW89_DBG_PORT_SEL_DSPT_HDT_TXA: @@ -2820,7 +2957,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,  				   B_AX_DISPATCHER_INTN_SEL_MASK, 0);  		rtw89_write16_mask(rtwdev, info->sel_addr,  				   B_AX_DISPATCHER_CH_SEL_MASK, index); -		seq_printf(m, "Enable Dispatcher hdt tx%x dump.\n", index); +		p += scnprintf(p, end - p, +			       "Enable Dispatcher hdt tx%x dump.\n", index);  		break;  	case RTW89_DBG_PORT_SEL_DSPT_HDT_TXD:  		info = &dbg_port_dspt_hdt_txD; @@ -2828,7 +2966,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,  				   B_AX_DISPATCHER_INTN_SEL_MASK, 0);  		rtw89_write16_mask(rtwdev, info->sel_addr,  				   B_AX_DISPATCHER_CH_SEL_MASK, 0xD); -		seq_puts(m, "Enable Dispatcher hdt txD dump.\n"); +		p += scnprintf(p, end - p, +			       "Enable Dispatcher hdt txD dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_DSPT_CDT_TX0:  		info = &dbg_port_dspt_cdt_tx0; @@ -2836,7 +2975,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,  				   B_AX_DISPATCHER_INTN_SEL_MASK, 1);  		rtw89_write16_mask(rtwdev, info->sel_addr,  				   B_AX_DISPATCHER_CH_SEL_MASK, 0); -		seq_puts(m, "Enable Dispatcher cdt tx0 dump.\n"); +		p += scnprintf(p, end - p, +			       "Enable Dispatcher cdt tx0 dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_DSPT_CDT_TX1:  		info = &dbg_port_dspt_cdt_tx1; @@ -2844,7 +2984,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,  				   B_AX_DISPATCHER_INTN_SEL_MASK, 1);  		rtw89_write16_mask(rtwdev, info->sel_addr,  				   B_AX_DISPATCHER_CH_SEL_MASK, 1); -		seq_puts(m, "Enable Dispatcher cdt tx1 dump.\n"); +		p += scnprintf(p, end - p, +			       "Enable Dispatcher cdt tx1 dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_DSPT_CDT_TX3:  		info = &dbg_port_dspt_cdt_tx3; @@ -2852,7 +2993,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,  				   B_AX_DISPATCHER_INTN_SEL_MASK, 1);  		rtw89_write16_mask(rtwdev, info->sel_addr,  				   B_AX_DISPATCHER_CH_SEL_MASK, 3); -		seq_puts(m, "Enable Dispatcher cdt tx3 dump.\n"); +		p += scnprintf(p, end - p, +			       "Enable Dispatcher cdt tx3 dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_DSPT_CDT_TX4:  		info = &dbg_port_dspt_cdt_tx4; @@ -2860,7 +3002,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,  				   B_AX_DISPATCHER_INTN_SEL_MASK, 1);  		rtw89_write16_mask(rtwdev, info->sel_addr,  				   B_AX_DISPATCHER_CH_SEL_MASK, 4); -		seq_puts(m, "Enable Dispatcher cdt tx4 dump.\n"); +		p += scnprintf(p, end - p, +			       "Enable Dispatcher cdt tx4 dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_DSPT_CDT_TX5:  	case RTW89_DBG_PORT_SEL_DSPT_CDT_TX6: @@ -2872,7 +3015,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,  				   B_AX_DISPATCHER_INTN_SEL_MASK, 1);  		rtw89_write16_mask(rtwdev, info->sel_addr,  				   B_AX_DISPATCHER_CH_SEL_MASK, index); -		seq_printf(m, "Enable Dispatcher cdt tx%x dump.\n", index); +		p += scnprintf(p, end - p, +			       "Enable Dispatcher cdt tx%x dump.\n", index);  		break;  	case RTW89_DBG_PORT_SEL_DSPT_CDT_TX9:  		info = &dbg_port_dspt_cdt_tx9; @@ -2880,7 +3024,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,  				   B_AX_DISPATCHER_INTN_SEL_MASK, 1);  		rtw89_write16_mask(rtwdev, info->sel_addr,  				   B_AX_DISPATCHER_CH_SEL_MASK, 9); -		seq_puts(m, "Enable Dispatcher cdt tx9 dump.\n"); +		p += scnprintf(p, end - p, +			       "Enable Dispatcher cdt tx9 dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_DSPT_CDT_TXA:  	case RTW89_DBG_PORT_SEL_DSPT_CDT_TXB: @@ -2891,7 +3036,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,  				   B_AX_DISPATCHER_INTN_SEL_MASK, 1);  		rtw89_write16_mask(rtwdev, info->sel_addr,  				   B_AX_DISPATCHER_CH_SEL_MASK, index); -		seq_printf(m, "Enable Dispatcher cdt tx%x dump.\n", index); +		p += scnprintf(p, end - p, +			       "Enable Dispatcher cdt tx%x dump.\n", index);  		break;  	case RTW89_DBG_PORT_SEL_DSPT_HDT_RX0:  		info = &dbg_port_dspt_hdt_rx0; @@ -2899,7 +3045,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,  				   B_AX_DISPATCHER_INTN_SEL_MASK, 2);  		rtw89_write16_mask(rtwdev, info->sel_addr,  				   B_AX_DISPATCHER_CH_SEL_MASK, 0); -		seq_puts(m, "Enable Dispatcher hdt rx0 dump.\n"); +		p += scnprintf(p, end - p, +			       "Enable Dispatcher hdt rx0 dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_DSPT_HDT_RX1:  	case RTW89_DBG_PORT_SEL_DSPT_HDT_RX2: @@ -2909,7 +3056,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,  				   B_AX_DISPATCHER_INTN_SEL_MASK, 2);  		rtw89_write16_mask(rtwdev, info->sel_addr,  				   B_AX_DISPATCHER_CH_SEL_MASK, index); -		seq_printf(m, "Enable Dispatcher hdt rx%x dump.\n", index); +		p += scnprintf(p, end - p, +			       "Enable Dispatcher hdt rx%x dump.\n", index);  		break;  	case RTW89_DBG_PORT_SEL_DSPT_HDT_RX3:  		info = &dbg_port_dspt_hdt_rx3; @@ -2917,7 +3065,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,  				   B_AX_DISPATCHER_INTN_SEL_MASK, 2);  		rtw89_write16_mask(rtwdev, info->sel_addr,  				   B_AX_DISPATCHER_CH_SEL_MASK, 3); -		seq_puts(m, "Enable Dispatcher hdt rx3 dump.\n"); +		p += scnprintf(p, end - p, +			       "Enable Dispatcher hdt rx3 dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_DSPT_HDT_RX4:  		info = &dbg_port_dspt_hdt_rx4; @@ -2925,7 +3074,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,  				   B_AX_DISPATCHER_INTN_SEL_MASK, 2);  		rtw89_write16_mask(rtwdev, info->sel_addr,  				   B_AX_DISPATCHER_CH_SEL_MASK, 4); -		seq_puts(m, "Enable Dispatcher hdt rx4 dump.\n"); +		p += scnprintf(p, end - p, +			       "Enable Dispatcher hdt rx4 dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_DSPT_HDT_RX5:  		info = &dbg_port_dspt_hdt_rx5; @@ -2933,7 +3083,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,  				   B_AX_DISPATCHER_INTN_SEL_MASK, 2);  		rtw89_write16_mask(rtwdev, info->sel_addr,  				   B_AX_DISPATCHER_CH_SEL_MASK, 5); -		seq_puts(m, "Enable Dispatcher hdt rx5 dump.\n"); +		p += scnprintf(p, end - p, +			       "Enable Dispatcher hdt rx5 dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_DSPT_CDT_RX_P0_0:  		info = &dbg_port_dspt_cdt_rx_p0_0; @@ -2941,7 +3092,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,  				   B_AX_DISPATCHER_INTN_SEL_MASK, 3);  		rtw89_write16_mask(rtwdev, info->sel_addr,  				   B_AX_DISPATCHER_CH_SEL_MASK, 0); -		seq_puts(m, "Enable Dispatcher cdt rx part0 0 dump.\n"); +		p += scnprintf(p, end - p, +			       "Enable Dispatcher cdt rx part0 0 dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_DSPT_CDT_RX_P0:  	case RTW89_DBG_PORT_SEL_DSPT_CDT_RX_P0_1: @@ -2950,7 +3102,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,  				   B_AX_DISPATCHER_INTN_SEL_MASK, 3);  		rtw89_write16_mask(rtwdev, info->sel_addr,  				   B_AX_DISPATCHER_CH_SEL_MASK, 1); -		seq_puts(m, "Enable Dispatcher cdt rx part0 1 dump.\n"); +		p += scnprintf(p, end - p, +			       "Enable Dispatcher cdt rx part0 1 dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_DSPT_CDT_RX_P0_2:  		info = &dbg_port_dspt_cdt_rx_p0_2; @@ -2958,43 +3111,50 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,  				   B_AX_DISPATCHER_INTN_SEL_MASK, 3);  		rtw89_write16_mask(rtwdev, info->sel_addr,  				   B_AX_DISPATCHER_CH_SEL_MASK, 2); -		seq_puts(m, "Enable Dispatcher cdt rx part0 2 dump.\n"); +		p += scnprintf(p, end - p, +			       "Enable Dispatcher cdt rx part0 2 dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_DSPT_CDT_RX_P1:  		info = &dbg_port_dspt_cdt_rx_p1;  		rtw89_write8_mask(rtwdev, info->sel_addr,  				  B_AX_DISPATCHER_INTN_SEL_MASK, 3); -		seq_puts(m, "Enable Dispatcher cdt rx part1 dump.\n"); +		p += scnprintf(p, end - p, +			       "Enable Dispatcher cdt rx part1 dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_DSPT_STF_CTRL:  		info = &dbg_port_dspt_stf_ctrl;  		rtw89_write8_mask(rtwdev, info->sel_addr,  				  B_AX_DISPATCHER_INTN_SEL_MASK, 4); -		seq_puts(m, "Enable Dispatcher stf control dump.\n"); +		p += scnprintf(p, end - p, +			       "Enable Dispatcher stf control dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_DSPT_ADDR_CTRL:  		info = &dbg_port_dspt_addr_ctrl;  		rtw89_write8_mask(rtwdev, info->sel_addr,  				  B_AX_DISPATCHER_INTN_SEL_MASK, 5); -		seq_puts(m, "Enable Dispatcher addr control dump.\n"); +		p += scnprintf(p, end - p, +			       "Enable Dispatcher addr control dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_DSPT_WDE_INTF:  		info = &dbg_port_dspt_wde_intf;  		rtw89_write8_mask(rtwdev, info->sel_addr,  				  B_AX_DISPATCHER_INTN_SEL_MASK, 6); -		seq_puts(m, "Enable Dispatcher wde interface dump.\n"); +		p += scnprintf(p, end - p, +			       "Enable Dispatcher wde interface dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_DSPT_PLE_INTF:  		info = &dbg_port_dspt_ple_intf;  		rtw89_write8_mask(rtwdev, info->sel_addr,  				  B_AX_DISPATCHER_INTN_SEL_MASK, 7); -		seq_puts(m, "Enable Dispatcher ple interface dump.\n"); +		p += scnprintf(p, end - p, +			       "Enable Dispatcher ple interface dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_DSPT_FLOW_CTRL:  		info = &dbg_port_dspt_flow_ctrl;  		rtw89_write8_mask(rtwdev, info->sel_addr,  				  B_AX_DISPATCHER_INTN_SEL_MASK, 8); -		seq_puts(m, "Enable Dispatcher flow control dump.\n"); +		p += scnprintf(p, end - p, +			       "Enable Dispatcher flow control dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_PCIE_TXDMA:  		info = &dbg_port_pcie_txdma; @@ -3002,7 +3162,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,  		val32 = u32_replace_bits(val32, PCIE_TXDMA_DBG_SEL, B_AX_DBG_SEL0);  		val32 = u32_replace_bits(val32, PCIE_TXDMA_DBG_SEL, B_AX_DBG_SEL1);  		rtw89_write32(rtwdev, R_AX_DBG_CTRL, val32); -		seq_puts(m, "Enable pcie txdma dump.\n"); +		p += scnprintf(p, end - p, "Enable pcie txdma dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_PCIE_RXDMA:  		info = &dbg_port_pcie_rxdma; @@ -3010,7 +3170,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,  		val32 = u32_replace_bits(val32, PCIE_RXDMA_DBG_SEL, B_AX_DBG_SEL0);  		val32 = u32_replace_bits(val32, PCIE_RXDMA_DBG_SEL, B_AX_DBG_SEL1);  		rtw89_write32(rtwdev, R_AX_DBG_CTRL, val32); -		seq_puts(m, "Enable pcie rxdma dump.\n"); +		p += scnprintf(p, end - p, "Enable pcie rxdma dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_PCIE_CVT:  		info = &dbg_port_pcie_cvt; @@ -3018,7 +3178,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,  		val32 = u32_replace_bits(val32, PCIE_CVT_DBG_SEL, B_AX_DBG_SEL0);  		val32 = u32_replace_bits(val32, PCIE_CVT_DBG_SEL, B_AX_DBG_SEL1);  		rtw89_write32(rtwdev, R_AX_DBG_CTRL, val32); -		seq_puts(m, "Enable pcie cvt dump.\n"); +		p += scnprintf(p, end - p, "Enable pcie cvt dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_PCIE_CXPL:  		info = &dbg_port_pcie_cxpl; @@ -3026,7 +3186,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,  		val32 = u32_replace_bits(val32, PCIE_CXPL_DBG_SEL, B_AX_DBG_SEL0);  		val32 = u32_replace_bits(val32, PCIE_CXPL_DBG_SEL, B_AX_DBG_SEL1);  		rtw89_write32(rtwdev, R_AX_DBG_CTRL, val32); -		seq_puts(m, "Enable pcie cxpl dump.\n"); +		p += scnprintf(p, end - p, "Enable pcie cxpl dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_PCIE_IO:  		info = &dbg_port_pcie_io; @@ -3034,7 +3194,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,  		val32 = u32_replace_bits(val32, PCIE_IO_DBG_SEL, B_AX_DBG_SEL0);  		val32 = u32_replace_bits(val32, PCIE_IO_DBG_SEL, B_AX_DBG_SEL1);  		rtw89_write32(rtwdev, R_AX_DBG_CTRL, val32); -		seq_puts(m, "Enable pcie io dump.\n"); +		p += scnprintf(p, end - p, "Enable pcie io dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_PCIE_MISC:  		info = &dbg_port_pcie_misc; @@ -3042,7 +3202,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,  		val32 = u32_replace_bits(val32, PCIE_MISC_DBG_SEL, B_AX_DBG_SEL0);  		val32 = u32_replace_bits(val32, PCIE_MISC_DBG_SEL, B_AX_DBG_SEL1);  		rtw89_write32(rtwdev, R_AX_DBG_CTRL, val32); -		seq_puts(m, "Enable pcie misc dump.\n"); +		p += scnprintf(p, end - p, "Enable pcie misc dump.\n");  		break;  	case RTW89_DBG_PORT_SEL_PCIE_MISC2:  		info = &dbg_port_pcie_misc2; @@ -3050,14 +3210,16 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m,  		val16 = u16_replace_bits(val16, PCIE_MISC2_DBG_SEL,  					 B_AX_PCIE_DBG_SEL_MASK);  		rtw89_write16(rtwdev, R_AX_PCIE_DBG_CTRL, val16); -		seq_puts(m, "Enable pcie misc2 dump.\n"); +		p += scnprintf(p, end - p, "Enable pcie misc2 dump.\n");  		break;  	default: -		seq_puts(m, "Dbg port select err\n"); -		return NULL; +		p += scnprintf(p, end - p, "Dbg port select err\n"); +		break;  	} -	return info; +	*ppinfo = info; + +	return p - buf;  }  static bool is_dbg_port_valid(struct rtw89_dev *rtwdev, u32 sel) @@ -3091,23 +3253,25 @@ static bool is_dbg_port_valid(struct rtw89_dev *rtwdev, u32 sel)  }  static int rtw89_debug_mac_dbg_port_dump(struct rtw89_dev *rtwdev, -					 struct seq_file *m, u32 sel) +					 char *buf, size_t bufsz, u32 sel)  { -	const struct rtw89_mac_dbg_port_info *info; -	u8 val8; -	u16 val16; +	const struct rtw89_mac_dbg_port_info *info = NULL; +	char *p = buf, *end = buf + bufsz;  	u32 val32; +	u16 val16; +	u8 val8;  	u32 i; -	info = rtw89_debug_mac_dbg_port_sel(m, rtwdev, sel); +	p += rtw89_debug_mac_dbg_port_sel(rtwdev, p, end - p, sel, &info); +  	if (!info) {  		rtw89_err(rtwdev, "failed to select debug port %d\n", sel); -		return -EINVAL; +		goto out;  	}  #define case_DBG_SEL(__sel) \  	case RTW89_DBG_PORT_SEL_##__sel: \ -		seq_puts(m, "Dump debug port " #__sel ":\n"); \ +		p += scnprintf(p, end - p, "Dump debug port " #__sel ":\n"); \  		break  	switch (sel) { @@ -3203,8 +3367,8 @@ static int rtw89_debug_mac_dbg_port_dump(struct rtw89_dev *rtwdev,  #undef case_DBG_SEL -	seq_printf(m, "Sel addr = 0x%X\n", info->sel_addr); -	seq_printf(m, "Read addr = 0x%X\n", info->rd_addr); +	p += scnprintf(p, end - p, "Sel addr = 0x%X\n", info->sel_addr); +	p += scnprintf(p, end - p, "Read addr = 0x%X\n", info->rd_addr);  	for (i = info->srt; i <= info->end; i++) {  		switch (info->sel_byte) { @@ -3212,17 +3376,17 @@ static int rtw89_debug_mac_dbg_port_dump(struct rtw89_dev *rtwdev,  		default:  			rtw89_write8_mask(rtwdev, info->sel_addr,  					  info->sel_msk, i); -			seq_printf(m, "0x%02X: ", i); +			p += scnprintf(p, end - p, "0x%02X: ", i);  			break;  		case 2:  			rtw89_write16_mask(rtwdev, info->sel_addr,  					   info->sel_msk, i); -			seq_printf(m, "0x%04X: ", i); +			p += scnprintf(p, end - p, "0x%04X: ", i);  			break;  		case 4:  			rtw89_write32_mask(rtwdev, info->sel_addr,  					   info->sel_msk, i); -			seq_printf(m, "0x%04X: ", i); +			p += scnprintf(p, end - p, "0x%04X: ", i);  			break;  		} @@ -3233,77 +3397,75 @@ static int rtw89_debug_mac_dbg_port_dump(struct rtw89_dev *rtwdev,  		default:  			val8 = rtw89_read8_mask(rtwdev,  						info->rd_addr, info->rd_msk); -			seq_printf(m, "0x%02X\n", val8); +			p += scnprintf(p, end - p, "0x%02X\n", val8);  			break;  		case 2:  			val16 = rtw89_read16_mask(rtwdev,  						  info->rd_addr, info->rd_msk); -			seq_printf(m, "0x%04X\n", val16); +			p += scnprintf(p, end - p, "0x%04X\n", val16);  			break;  		case 4:  			val32 = rtw89_read32_mask(rtwdev,  						  info->rd_addr, info->rd_msk); -			seq_printf(m, "0x%08X\n", val32); +			p += scnprintf(p, end - p, "0x%08X\n", val32);  			break;  		}  	} -	return 0; +out: +	return p - buf;  }  static int rtw89_debug_mac_dump_dbg_port(struct rtw89_dev *rtwdev, -					 struct seq_file *m) +					 char *buf, size_t bufsz)  { +	char *p = buf, *end = buf + bufsz; +	ssize_t n;  	u32 sel; -	int ret = 0;  	for (sel = RTW89_DBG_PORT_SEL_PTCL_C0;  	     sel < RTW89_DBG_PORT_SEL_LAST; sel++) {  		if (!is_dbg_port_valid(rtwdev, sel))  			continue; -		ret = rtw89_debug_mac_dbg_port_dump(rtwdev, m, sel); -		if (ret) { +		n = rtw89_debug_mac_dbg_port_dump(rtwdev, p, end - p, sel); +		if (n < 0) {  			rtw89_err(rtwdev,  				  "failed to dump debug port %d\n", sel);  			break;  		} +		p += n;  	} -	return ret; +	return p - buf;  } -static int -rtw89_debug_priv_mac_dbg_port_dump_get(struct seq_file *m, void *v) +static ssize_t +rtw89_debug_priv_mac_dbg_port_dump_get(struct rtw89_dev *rtwdev, +				       struct rtw89_debugfs_priv *debugfs_priv, +				       char *buf, size_t bufsz)  { -	struct rtw89_debugfs_priv *debugfs_priv = m->private; -	struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; +	char *p = buf, *end = buf + bufsz;  	if (debugfs_priv->dbgpkg_en.ss_dbg) -		rtw89_debug_mac_dump_ss_dbg(rtwdev, m); +		p += rtw89_debug_mac_dump_ss_dbg(rtwdev, p, end - p);  	if (debugfs_priv->dbgpkg_en.dle_dbg) -		rtw89_debug_mac_dump_dle_dbg(rtwdev, m); +		p += rtw89_debug_mac_dump_dle_dbg(rtwdev, p, end - p);  	if (debugfs_priv->dbgpkg_en.dmac_dbg) -		rtw89_debug_mac_dump_dmac_dbg(rtwdev, m); +		p += rtw89_debug_mac_dump_dmac_dbg(rtwdev, p, end - p);  	if (debugfs_priv->dbgpkg_en.cmac_dbg) -		rtw89_debug_mac_dump_cmac_dbg(rtwdev, m); +		p += rtw89_debug_mac_dump_cmac_dbg(rtwdev, p, end - p);  	if (debugfs_priv->dbgpkg_en.dbg_port) -		rtw89_debug_mac_dump_dbg_port(rtwdev, m); +		p += rtw89_debug_mac_dump_dbg_port(rtwdev, p, end - p); -	return 0; +	return p - buf;  }; -static u8 *rtw89_hex2bin_user(struct rtw89_dev *rtwdev, -			      const char __user *user_buf, size_t count) +static u8 *rtw89_hex2bin(struct rtw89_dev *rtwdev, const char *buf, size_t count)  { -	char *buf;  	u8 *bin;  	int num;  	int err = 0; -	buf = memdup_user(user_buf, count); -	if (IS_ERR(buf)) -		return buf; -  	num = count / 2;  	bin = kmalloc(num, GFP_KERNEL);  	if (!bin) { @@ -3318,22 +3480,18 @@ static u8 *rtw89_hex2bin_user(struct rtw89_dev *rtwdev,  	}  out: -	kfree(buf); -  	return err ? ERR_PTR(err) : bin;  } -static ssize_t rtw89_debug_priv_send_h2c_set(struct file *filp, -					     const char __user *user_buf, -					     size_t count, loff_t *loff) +static ssize_t rtw89_debug_priv_send_h2c_set(struct rtw89_dev *rtwdev, +					     struct rtw89_debugfs_priv *debugfs_priv, +					     const char *buf, size_t count)  { -	struct rtw89_debugfs_priv *debugfs_priv = filp->private_data; -	struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;  	u8 *h2c;  	int ret;  	u16 h2c_len = count / 2; -	h2c = rtw89_hex2bin_user(rtwdev, user_buf, count); +	h2c = rtw89_hex2bin(rtwdev, buf, count);  	if (IS_ERR(h2c))  		return -EFAULT; @@ -3344,34 +3502,36 @@ static ssize_t rtw89_debug_priv_send_h2c_set(struct file *filp,  	return ret ? ret : count;  } -static int -rtw89_debug_priv_early_h2c_get(struct seq_file *m, void *v) +static ssize_t +rtw89_debug_priv_early_h2c_get(struct rtw89_dev *rtwdev, +			       struct rtw89_debugfs_priv *debugfs_priv, +			       char *buf, size_t bufsz)  { -	struct rtw89_debugfs_priv *debugfs_priv = m->private; -	struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;  	struct rtw89_early_h2c *early_h2c; +	char *p = buf, *end = buf + bufsz;  	int seq = 0; -	mutex_lock(&rtwdev->mutex); +	lockdep_assert_wiphy(rtwdev->hw->wiphy); +  	list_for_each_entry(early_h2c, &rtwdev->early_h2c_list, list) -		seq_printf(m, "%d: %*ph\n", ++seq, early_h2c->h2c_len, early_h2c->h2c); -	mutex_unlock(&rtwdev->mutex); +		p += scnprintf(p, end - p, "%d: %*ph\n", ++seq, +			       early_h2c->h2c_len, early_h2c->h2c); -	return 0; +	return p - buf;  }  static ssize_t -rtw89_debug_priv_early_h2c_set(struct file *filp, const char __user *user_buf, -			       size_t count, loff_t *loff) +rtw89_debug_priv_early_h2c_set(struct rtw89_dev *rtwdev, +			       struct rtw89_debugfs_priv *debugfs_priv, +			       const char *buf, size_t count)  { -	struct seq_file *m = (struct seq_file *)filp->private_data; -	struct rtw89_debugfs_priv *debugfs_priv = m->private; -	struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;  	struct rtw89_early_h2c *early_h2c;  	u8 *h2c;  	u16 h2c_len = count / 2; -	h2c = rtw89_hex2bin_user(rtwdev, user_buf, count); +	lockdep_assert_wiphy(rtwdev->hw->wiphy); + +	h2c = rtw89_hex2bin(rtwdev, buf, count);  	if (IS_ERR(h2c))  		return -EFAULT; @@ -3390,9 +3550,7 @@ rtw89_debug_priv_early_h2c_set(struct file *filp, const char __user *user_buf,  	early_h2c->h2c = h2c;  	early_h2c->h2c_len = h2c_len; -	mutex_lock(&rtwdev->mutex);  	list_add_tail(&early_h2c->list, &rtwdev->early_h2c_list); -	mutex_unlock(&rtwdev->mutex);  out:  	return count; @@ -3425,15 +3583,16 @@ static int rtw89_dbg_trigger_ctrl_error(struct rtw89_dev *rtwdev)  	return 0;  } -static int -rtw89_debug_priv_fw_crash_get(struct seq_file *m, void *v) +static ssize_t +rtw89_debug_priv_fw_crash_get(struct rtw89_dev *rtwdev, +			      struct rtw89_debugfs_priv *debugfs_priv, +			      char *buf, size_t bufsz)  { -	struct rtw89_debugfs_priv *debugfs_priv = m->private; -	struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; +	char *p = buf, *end = buf + bufsz; -	seq_printf(m, "%d\n", -		   test_bit(RTW89_FLAG_CRASH_SIMULATING, rtwdev->flags)); -	return 0; +	p += scnprintf(p, end - p, "%d\n", +		       test_bit(RTW89_FLAG_CRASH_SIMULATING, rtwdev->flags)); +	return p - buf;  }  enum rtw89_dbg_crash_simulation_type { @@ -3442,23 +3601,23 @@ enum rtw89_dbg_crash_simulation_type {  };  static ssize_t -rtw89_debug_priv_fw_crash_set(struct file *filp, const char __user *user_buf, -			      size_t count, loff_t *loff) +rtw89_debug_priv_fw_crash_set(struct rtw89_dev *rtwdev, +			      struct rtw89_debugfs_priv *debugfs_priv, +			      const char *buf, size_t count)  { -	struct seq_file *m = (struct seq_file *)filp->private_data; -	struct rtw89_debugfs_priv *debugfs_priv = m->private; -	struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;  	int (*sim)(struct rtw89_dev *rtwdev);  	u8 crash_type;  	int ret; -	ret = kstrtou8_from_user(user_buf, count, 0, &crash_type); +	lockdep_assert_wiphy(rtwdev->hw->wiphy); + +	ret = kstrtou8(buf, 0, &crash_type);  	if (ret)  		return -EINVAL;  	switch (crash_type) {  	case RTW89_DBG_SIM_CPU_EXCEPTION: -		if (!RTW89_CHK_FW_FEATURE(CRASH_TRIGGER, &rtwdev->fw)) +		if (!RTW89_CHK_FW_FEATURE_GROUP(CRASH_TRIGGER, &rtwdev->fw))  			return -EOPNOTSUPP;  		sim = rtw89_fw_h2c_trigger_cpu_exception;  		break; @@ -3469,10 +3628,8 @@ rtw89_debug_priv_fw_crash_set(struct file *filp, const char __user *user_buf,  		return -EINVAL;  	} -	mutex_lock(&rtwdev->mutex);  	set_bit(RTW89_FLAG_CRASH_SIMULATING, rtwdev->flags);  	ret = sim(rtwdev); -	mutex_unlock(&rtwdev->mutex);  	if (ret)  		return ret; @@ -3480,27 +3637,22 @@ rtw89_debug_priv_fw_crash_set(struct file *filp, const char __user *user_buf,  	return count;  } -static int rtw89_debug_priv_btc_info_get(struct seq_file *m, void *v) +static ssize_t rtw89_debug_priv_btc_info_get(struct rtw89_dev *rtwdev, +					     struct rtw89_debugfs_priv *debugfs_priv, +					     char *buf, size_t bufsz)  { -	struct rtw89_debugfs_priv *debugfs_priv = m->private; -	struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; - -	rtw89_btc_dump_info(rtwdev, m); - -	return 0; +	return rtw89_btc_dump_info(rtwdev, buf, bufsz);  } -static ssize_t rtw89_debug_priv_btc_manual_set(struct file *filp, -					       const char __user *user_buf, -					       size_t count, loff_t *loff) +static ssize_t rtw89_debug_priv_btc_manual_set(struct rtw89_dev *rtwdev, +					       struct rtw89_debugfs_priv *debugfs_priv, +					       const char *buf, size_t count)  { -	struct rtw89_debugfs_priv *debugfs_priv = filp->private_data; -	struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;  	struct rtw89_btc *btc = &rtwdev->btc;  	const struct rtw89_btc_ver *ver = btc->ver;  	int ret; -	ret = kstrtobool_from_user(user_buf, count, &btc->manual_ctrl); +	ret = kstrtobool(buf, &btc->manual_ctrl);  	if (ret)  		return ret; @@ -3512,31 +3664,29 @@ static ssize_t rtw89_debug_priv_btc_manual_set(struct file *filp,  	return count;  } -static ssize_t rtw89_debug_priv_fw_log_manual_set(struct file *filp, -						  const char __user *user_buf, -						  size_t count, loff_t *loff) +static ssize_t rtw89_debug_priv_fw_log_manual_set(struct rtw89_dev *rtwdev, +						  struct rtw89_debugfs_priv *debugfs_priv, +						  const char *buf, size_t count)  { -	struct rtw89_debugfs_priv *debugfs_priv = filp->private_data; -	struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;  	struct rtw89_fw_log *log = &rtwdev->fw.log;  	bool fw_log_manual; -	if (kstrtobool_from_user(user_buf, count, &fw_log_manual)) +	lockdep_assert_wiphy(rtwdev->hw->wiphy); + +	if (kstrtobool(buf, &fw_log_manual))  		goto out; -	mutex_lock(&rtwdev->mutex);  	log->enable = fw_log_manual;  	if (log->enable)  		rtw89_fw_log_prepare(rtwdev);  	rtw89_fw_h2c_fw_log(rtwdev, fw_log_manual); -	mutex_unlock(&rtwdev->mutex);  out:  	return count;  } -static void rtw89_sta_link_info_get_iter(struct seq_file *m, -					 struct rtw89_dev *rtwdev, -					 struct rtw89_sta_link *rtwsta_link) +static int rtw89_sta_link_info_get_iter(struct rtw89_dev *rtwdev, +					char *buf, size_t bufsz, +					struct rtw89_sta_link *rtwsta_link)  {  	static const char * const he_gi_str[] = {  		[NL80211_RATE_INFO_HE_GI_0_8] = "0.8", @@ -3554,6 +3704,7 @@ static void rtw89_sta_link_info_get_iter(struct seq_file *m,  	u8 ant_num = hal->ant_diversity ? 2 : rtwdev->chip->rf_path_num;  	bool ant_asterisk = hal->tx_path_diversity || hal->ant_diversity;  	struct ieee80211_link_sta *link_sta; +	char *p = buf, *end = buf + bufsz;  	u8 evm_min, evm_max, evm_1ss;  	u16 max_rc_amsdu_len;  	u8 rssi; @@ -3567,107 +3718,136 @@ static void rtw89_sta_link_info_get_iter(struct seq_file *m,  	rcu_read_unlock(); -	seq_printf(m, "TX rate [%u, %u]: ", rtwsta_link->mac_id, rtwsta_link->link_id); +	p += scnprintf(p, end - p, "TX rate [%u, %u]: ", rtwsta_link->mac_id, +		       rtwsta_link->link_id);  	if (rate->flags & RATE_INFO_FLAGS_MCS) -		seq_printf(m, "HT MCS-%d%s", rate->mcs, -			   rate->flags & RATE_INFO_FLAGS_SHORT_GI ? " SGI" : ""); +		p += scnprintf(p, end - p, "HT MCS-%d%s", rate->mcs, +			       rate->flags & RATE_INFO_FLAGS_SHORT_GI ? " SGI" : "");  	else if (rate->flags & RATE_INFO_FLAGS_VHT_MCS) -		seq_printf(m, "VHT %dSS MCS-%d%s", rate->nss, rate->mcs, -			   rate->flags & RATE_INFO_FLAGS_SHORT_GI ? " SGI" : ""); +		p += scnprintf(p, end - p, "VHT %dSS MCS-%d%s", rate->nss, +			       rate->mcs, +			       rate->flags & RATE_INFO_FLAGS_SHORT_GI ? " SGI" : "");  	else if (rate->flags & RATE_INFO_FLAGS_HE_MCS) -		seq_printf(m, "HE %dSS MCS-%d GI:%s", rate->nss, rate->mcs, -			   rate->he_gi <= NL80211_RATE_INFO_HE_GI_3_2 ? -			   he_gi_str[rate->he_gi] : "N/A"); +		p += scnprintf(p, end - p, "HE %dSS MCS-%d GI:%s", rate->nss, +			       rate->mcs, +			       rate->he_gi <= NL80211_RATE_INFO_HE_GI_3_2 ? +			       he_gi_str[rate->he_gi] : "N/A");  	else if (rate->flags & RATE_INFO_FLAGS_EHT_MCS) -		seq_printf(m, "EHT %dSS MCS-%d GI:%s", rate->nss, rate->mcs, -			   rate->eht_gi < ARRAY_SIZE(eht_gi_str) ? -			   eht_gi_str[rate->eht_gi] : "N/A"); +		p += scnprintf(p, end - p, "EHT %dSS MCS-%d GI:%s", rate->nss, +			       rate->mcs, +			       rate->eht_gi < ARRAY_SIZE(eht_gi_str) ? +			       eht_gi_str[rate->eht_gi] : "N/A");  	else -		seq_printf(m, "Legacy %d", rate->legacy); -	seq_printf(m, "%s", rtwsta_link->ra_report.might_fallback_legacy ? " FB_G" : ""); -	seq_printf(m, " BW:%u", rtw89_rate_info_bw_to_mhz(rate->bw)); -	seq_printf(m, " (hw_rate=0x%x)", rtwsta_link->ra_report.hw_rate); -	seq_printf(m, " ==> agg_wait=%d (%d)\n", rtwsta_link->max_agg_wait, -		   max_rc_amsdu_len); - -	seq_printf(m, "RX rate [%u, %u]: ", rtwsta_link->mac_id, rtwsta_link->link_id); +		p += scnprintf(p, end - p, "Legacy %d", rate->legacy); +	p += scnprintf(p, end - p, "%s", +		       rtwsta_link->ra_report.might_fallback_legacy ? " FB_G" : ""); +	p += scnprintf(p, end - p, " BW:%u", +		       rtw89_rate_info_bw_to_mhz(rate->bw)); +	p += scnprintf(p, end - p, " (hw_rate=0x%x)", +		       rtwsta_link->ra_report.hw_rate); +	p += scnprintf(p, end - p, " ==> agg_wait=%d (%d)\n", +		       rtwsta_link->max_agg_wait, +		       max_rc_amsdu_len); + +	p += scnprintf(p, end - p, "RX rate [%u, %u]: ", rtwsta_link->mac_id, +		       rtwsta_link->link_id);  	switch (status->encoding) {  	case RX_ENC_LEGACY: -		seq_printf(m, "Legacy %d", status->rate_idx + -			   (status->band != NL80211_BAND_2GHZ ? 4 : 0)); +		p += scnprintf(p, end - p, "Legacy %d", status->rate_idx + +			       (status->band != NL80211_BAND_2GHZ ? 4 : 0));  		break;  	case RX_ENC_HT: -		seq_printf(m, "HT MCS-%d%s", status->rate_idx, -			   status->enc_flags & RX_ENC_FLAG_SHORT_GI ? " SGI" : ""); +		p += scnprintf(p, end - p, "HT MCS-%d%s", status->rate_idx, +			       status->enc_flags & RX_ENC_FLAG_SHORT_GI ? " SGI" : "");  		break;  	case RX_ENC_VHT: -		seq_printf(m, "VHT %dSS MCS-%d%s", status->nss, status->rate_idx, -			   status->enc_flags & RX_ENC_FLAG_SHORT_GI ? " SGI" : ""); +		p += scnprintf(p, end - p, "VHT %dSS MCS-%d%s", status->nss, +			       status->rate_idx, +			       status->enc_flags & RX_ENC_FLAG_SHORT_GI ? " SGI" : "");  		break;  	case RX_ENC_HE: -		seq_printf(m, "HE %dSS MCS-%d GI:%s", status->nss, status->rate_idx, -			   status->he_gi <= NL80211_RATE_INFO_HE_GI_3_2 ? -			   he_gi_str[status->he_gi] : "N/A"); +		p += scnprintf(p, end - p, "HE %dSS MCS-%d GI:%s", +			       status->nss, status->rate_idx, +			       status->he_gi <= NL80211_RATE_INFO_HE_GI_3_2 ? +			       he_gi_str[status->he_gi] : "N/A");  		break;  	case RX_ENC_EHT: -		seq_printf(m, "EHT %dSS MCS-%d GI:%s", status->nss, status->rate_idx, -			   status->eht.gi < ARRAY_SIZE(eht_gi_str) ? -			   eht_gi_str[status->eht.gi] : "N/A"); +		p += scnprintf(p, end - p, "EHT %dSS MCS-%d GI:%s", +			       status->nss, status->rate_idx, +			       status->eht.gi < ARRAY_SIZE(eht_gi_str) ? +			       eht_gi_str[status->eht.gi] : "N/A");  		break;  	} -	seq_printf(m, " BW:%u", rtw89_rate_info_bw_to_mhz(status->bw)); -	seq_printf(m, " (hw_rate=0x%x)\n", rtwsta_link->rx_hw_rate); +	p += scnprintf(p, end - p, " BW:%u", +		       rtw89_rate_info_bw_to_mhz(status->bw)); +	p += scnprintf(p, end - p, " (hw_rate=0x%x)\n", +		       rtwsta_link->rx_hw_rate);  	rssi = ewma_rssi_read(&rtwsta_link->avg_rssi); -	seq_printf(m, "RSSI: %d dBm (raw=%d, prev=%d) [", -		   RTW89_RSSI_RAW_TO_DBM(rssi), rssi, rtwsta_link->prev_rssi); +	p += scnprintf(p, end - p, "RSSI: %d dBm (raw=%d, prev=%d) [", +		       RTW89_RSSI_RAW_TO_DBM(rssi), rssi, +		       rtwsta_link->prev_rssi);  	for (i = 0; i < ant_num; i++) {  		rssi = ewma_rssi_read(&rtwsta_link->rssi[i]); -		seq_printf(m, "%d%s%s", RTW89_RSSI_RAW_TO_DBM(rssi), -			   ant_asterisk && (hal->antenna_tx & BIT(i)) ? "*" : "", -			   i + 1 == ant_num ? "" : ", "); +		p += scnprintf(p, end - p, "%d%s%s", +			       RTW89_RSSI_RAW_TO_DBM(rssi), +			       ant_asterisk && (hal->antenna_tx & BIT(i)) ? "*" : "", +			       i + 1 == ant_num ? "" : ", ");  	} -	seq_puts(m, "]\n"); +	p += scnprintf(p, end - p, "]\n");  	evm_1ss = ewma_evm_read(&rtwsta_link->evm_1ss); -	seq_printf(m, "EVM: [%2u.%02u, ", evm_1ss >> 2, (evm_1ss & 0x3) * 25); +	p += scnprintf(p, end - p, "EVM: [%2u.%02u, ", evm_1ss >> 2, +		       (evm_1ss & 0x3) * 25);  	for (i = 0; i < (hal->ant_diversity ? 2 : 1); i++) {  		evm_min = ewma_evm_read(&rtwsta_link->evm_min[i]);  		evm_max = ewma_evm_read(&rtwsta_link->evm_max[i]); -		seq_printf(m, "%s(%2u.%02u, %2u.%02u)", i == 0 ? "" : " ", -			   evm_min >> 2, (evm_min & 0x3) * 25, -			   evm_max >> 2, (evm_max & 0x3) * 25); +		p += scnprintf(p, end - p, "%s(%2u.%02u, %2u.%02u)", +			       i == 0 ? "" : " ", +			       evm_min >> 2, (evm_min & 0x3) * 25, +			       evm_max >> 2, (evm_max & 0x3) * 25);  	} -	seq_puts(m, "]\t"); +	p += scnprintf(p, end - p, "]\t");  	snr = ewma_snr_read(&rtwsta_link->avg_snr); -	seq_printf(m, "SNR: %u\n", snr); +	p += scnprintf(p, end - p, "SNR: %u\n", snr); + +	return p - buf;  }  static void rtw89_sta_info_get_iter(void *data, struct ieee80211_sta *sta)  { -	struct seq_file *m = (struct seq_file *)data; +	struct rtw89_debugfs_iter_data *iter_data = +		(struct rtw89_debugfs_iter_data *)data;  	struct rtw89_sta *rtwsta = sta_to_rtwsta(sta);  	struct rtw89_dev *rtwdev = rtwsta->rtwdev;  	struct rtw89_sta_link *rtwsta_link; +	size_t bufsz = iter_data->bufsz; +	char *buf = iter_data->buf; +	char *p = buf, *end = buf + bufsz;  	unsigned int link_id;  	rtw89_sta_for_each_link(rtwsta, rtwsta_link, link_id) -		rtw89_sta_link_info_get_iter(m, rtwdev, rtwsta_link); +		p += rtw89_sta_link_info_get_iter(rtwdev, p, end - p, rtwsta_link); + +	rtw89_debugfs_iter_data_next(iter_data, p, end - p, p - buf);  } -static void -rtw89_debug_append_rx_rate(struct seq_file *m, struct rtw89_pkt_stat *pkt_stat, +static int +rtw89_debug_append_rx_rate(char *buf, size_t bufsz, struct rtw89_pkt_stat *pkt_stat,  			   enum rtw89_hw_rate first_rate, int len)  { +	char *p = buf, *end = buf + bufsz;  	int i;  	for (i = 0; i < len; i++) -		seq_printf(m, "%s%u", i == 0 ? "" : ", ", -			   pkt_stat->rx_rate_cnt[first_rate + i]); +		p += scnprintf(p, end - p, "%s%u", i == 0 ? "" : ", ", +			       pkt_stat->rx_rate_cnt[first_rate + i]); + +	return p - buf;  }  #define FIRST_RATE_SAME(rate) {RTW89_HW_RATE_ ## rate, RTW89_HW_RATE_ ## rate} @@ -3692,34 +3872,40 @@ static const struct rtw89_rx_rate_cnt_info {  	{FIRST_RATE_GEV1(EHT_NSS2_MCS0), 14, 0, "EHT 2SS:"},  }; -static int rtw89_debug_priv_phy_info_get(struct seq_file *m, void *v) +static ssize_t rtw89_debug_priv_phy_info_get(struct rtw89_dev *rtwdev, +					     struct rtw89_debugfs_priv *debugfs_priv, +					     char *buf, size_t bufsz)  { -	struct rtw89_debugfs_priv *debugfs_priv = m->private; -	struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;  	struct rtw89_traffic_stats *stats = &rtwdev->stats;  	struct rtw89_pkt_stat *pkt_stat = &rtwdev->phystat.last_pkt_stat;  	const struct rtw89_chip_info *chip = rtwdev->chip; +	struct rtw89_debugfs_iter_data iter_data;  	const struct rtw89_rx_rate_cnt_info *info;  	struct rtw89_hal *hal = &rtwdev->hal; +	char *p = buf, *end = buf + bufsz;  	enum rtw89_hw_rate first_rate;  	u8 rssi;  	int i;  	rssi = ewma_rssi_read(&rtwdev->phystat.bcn_rssi); -	seq_printf(m, "TP TX: %u [%u] Mbps (lv: %d", -		   stats->tx_throughput, stats->tx_throughput_raw, stats->tx_tfc_lv); +	p += scnprintf(p, end - p, "TP TX: %u [%u] Mbps (lv: %d", +		       stats->tx_throughput, stats->tx_throughput_raw, +		       stats->tx_tfc_lv);  	if (hal->thermal_prot_lv) -		seq_printf(m, ", duty: %d%%", -			   100 - hal->thermal_prot_lv * RTW89_THERMAL_PROT_STEP); -	seq_printf(m, "), RX: %u [%u] Mbps (lv: %d)\n", -		   stats->rx_throughput, stats->rx_throughput_raw, stats->rx_tfc_lv); -	seq_printf(m, "Beacon: %u (%d dBm), TF: %u\n", pkt_stat->beacon_nr, -		   RTW89_RSSI_RAW_TO_DBM(rssi), stats->rx_tf_periodic); -	seq_printf(m, "Avg packet length: TX=%u, RX=%u\n", stats->tx_avg_len, -		   stats->rx_avg_len); - -	seq_puts(m, "RX count:\n"); +		p += scnprintf(p, end - p, ", duty: %d%%", +			       100 - hal->thermal_prot_lv * RTW89_THERMAL_PROT_STEP); +	p += scnprintf(p, end - p, "), RX: %u [%u] Mbps (lv: %d)\n", +		       stats->rx_throughput, stats->rx_throughput_raw, +		       stats->rx_tfc_lv); +	p += scnprintf(p, end - p, "Beacon: %u (%d dBm), TF: %u\n", +		       pkt_stat->beacon_nr, +		       RTW89_RSSI_RAW_TO_DBM(rssi), stats->rx_tf_periodic); +	p += scnprintf(p, end - p, "Avg packet length: TX=%u, RX=%u\n", +		       stats->tx_avg_len, +		       stats->rx_avg_len); + +	p += scnprintf(p, end - p, "RX count:\n");  	for (i = 0; i < ARRAY_SIZE(rtw89_rx_rate_cnt_infos); i++) {  		info = &rtw89_rx_rate_cnt_infos[i]; @@ -3727,189 +3913,279 @@ static int rtw89_debug_priv_phy_info_get(struct seq_file *m, void *v)  		if (first_rate >= RTW89_HW_RATE_NR)  			continue; -		seq_printf(m, "%10s [", info->rate_mode); -		rtw89_debug_append_rx_rate(m, pkt_stat, -					   first_rate, info->len); +		p += scnprintf(p, end - p, "%10s [", info->rate_mode); +		p += rtw89_debug_append_rx_rate(p, end - p, pkt_stat, +						first_rate, info->len);  		if (info->ext) { -			seq_puts(m, "]["); -			rtw89_debug_append_rx_rate(m, pkt_stat, -						   first_rate + info->len, info->ext); +			p += scnprintf(p, end - p, "]["); +			p += rtw89_debug_append_rx_rate(p, end - p, pkt_stat, +							first_rate + info->len, info->ext);  		} -		seq_puts(m, "]\n"); +		p += scnprintf(p, end - p, "]\n");  	} -	ieee80211_iterate_stations_atomic(rtwdev->hw, rtw89_sta_info_get_iter, m); +	rtw89_debugfs_iter_data_setup(&iter_data, p, end - p); +	ieee80211_iterate_stations_atomic(rtwdev->hw, rtw89_sta_info_get_iter, &iter_data); +	p += iter_data.written_sz; -	return 0; +	return p - buf;  } -static void rtw89_dump_addr_cam(struct seq_file *m, -				struct rtw89_dev *rtwdev, -				struct rtw89_addr_cam_entry *addr_cam) +static int rtw89_dump_addr_cam(struct rtw89_dev *rtwdev, +			       char *buf, size_t bufsz, +			       struct rtw89_addr_cam_entry *addr_cam)  {  	struct rtw89_cam_info *cam_info = &rtwdev->cam_info;  	const struct rtw89_sec_cam_entry *sec_entry; +	char *p = buf, *end = buf + bufsz;  	u8 sec_cam_idx;  	int i; -	seq_printf(m, "\taddr_cam_idx=%u\n", addr_cam->addr_cam_idx); -	seq_printf(m, "\t-> bssid_cam_idx=%u\n", addr_cam->bssid_cam_idx); -	seq_printf(m, "\tsec_cam_bitmap=%*ph\n", (int)sizeof(addr_cam->sec_cam_map), -		   addr_cam->sec_cam_map); +	p += scnprintf(p, end - p, "\taddr_cam_idx=%u\n", +		       addr_cam->addr_cam_idx); +	p += scnprintf(p, end - p, "\t-> bssid_cam_idx=%u\n", +		       addr_cam->bssid_cam_idx); +	p += scnprintf(p, end - p, "\tsec_cam_bitmap=%*ph\n", +		       (int)sizeof(addr_cam->sec_cam_map), +		       addr_cam->sec_cam_map);  	for_each_set_bit(i, addr_cam->sec_cam_map, RTW89_SEC_CAM_IN_ADDR_CAM) {  		sec_cam_idx = addr_cam->sec_ent[i];  		sec_entry = cam_info->sec_entries[sec_cam_idx];  		if (!sec_entry)  			continue; -		seq_printf(m, "\tsec[%d]: sec_cam_idx %u", i, sec_entry->sec_cam_idx); +		p += scnprintf(p, end - p, "\tsec[%d]: sec_cam_idx %u", i, +			       sec_entry->sec_cam_idx);  		if (sec_entry->ext_key) -			seq_printf(m, ", %u", sec_entry->sec_cam_idx + 1); -		seq_puts(m, "\n"); +			p += scnprintf(p, end - p, ", %u", +				       sec_entry->sec_cam_idx + 1); +		p += scnprintf(p, end - p, "\n");  	} + +	return p - buf;  } -__printf(3, 4) -static void rtw89_dump_pkt_offload(struct seq_file *m, struct list_head *pkt_list, -				   const char *fmt, ...) +__printf(4, 5) +static int rtw89_dump_pkt_offload(char *buf, size_t bufsz, struct list_head *pkt_list, +				  const char *fmt, ...)  { +	char *p = buf, *end = buf + bufsz;  	struct rtw89_pktofld_info *info;  	struct va_format vaf;  	va_list args;  	if (list_empty(pkt_list)) -		return; +		return 0;  	va_start(args, fmt);  	vaf.va = &args;  	vaf.fmt = fmt; -	seq_printf(m, "%pV", &vaf); +	p += scnprintf(p, end - p, "%pV", &vaf);  	va_end(args);  	list_for_each_entry(info, pkt_list, list) -		seq_printf(m, "%d ", info->id); +		p += scnprintf(p, end - p, "%d ", info->id); + +	p += scnprintf(p, end - p, "\n"); -	seq_puts(m, "\n"); +	return p - buf;  } -static void rtw89_vif_link_ids_get(struct seq_file *m, u8 *mac, -				   struct rtw89_dev *rtwdev, -				   struct rtw89_vif_link *rtwvif_link) +static int rtw89_vif_link_ids_get(struct rtw89_dev *rtwdev, +				  char *buf, size_t bufsz, u8 *mac, +				  struct rtw89_vif_link *rtwvif_link, +				  bool designated)  {  	struct rtw89_bssid_cam_entry *bssid_cam = &rtwvif_link->bssid_cam; - -	seq_printf(m, "    [%u] %pM\n", rtwvif_link->mac_id, rtwvif_link->mac_addr); -	seq_printf(m, "\tlink_id=%u\n", rtwvif_link->link_id); -	seq_printf(m, "\tbssid_cam_idx=%u\n", bssid_cam->bssid_cam_idx); -	rtw89_dump_addr_cam(m, rtwdev, &rtwvif_link->addr_cam); -	rtw89_dump_pkt_offload(m, &rtwvif_link->general_pkt_list, -			       "\tpkt_ofld[GENERAL]: "); +	char *p = buf, *end = buf + bufsz; + +	p += scnprintf(p, end - p, "    [%u] %pM\n", rtwvif_link->mac_id, +		       rtwvif_link->mac_addr); +	p += scnprintf(p, end - p, "\tlink_id=%u%s\n", rtwvif_link->link_id, +		       designated ? " (*)" : ""); +	p += scnprintf(p, end - p, "\tbssid_cam_idx=%u\n", +		       bssid_cam->bssid_cam_idx); +	p += rtw89_dump_addr_cam(rtwdev, p, end - p, &rtwvif_link->addr_cam); +	p += rtw89_dump_pkt_offload(p, end - p, &rtwvif_link->general_pkt_list, +				    "\tpkt_ofld[GENERAL]: "); + +	return p - buf;  }  static  void rtw89_vif_ids_get_iter(void *data, u8 *mac, struct ieee80211_vif *vif)  { -	struct seq_file *m = (struct seq_file *)data; +	struct rtw89_debugfs_iter_data *iter_data = +		(struct rtw89_debugfs_iter_data *)data;  	struct rtw89_vif *rtwvif = vif_to_rtwvif(vif);  	struct rtw89_dev *rtwdev = rtwvif->rtwdev; +	struct rtw89_vif_link *designated_link;  	struct rtw89_vif_link *rtwvif_link; +	size_t bufsz = iter_data->bufsz; +	char *buf = iter_data->buf; +	char *p = buf, *end = buf + bufsz;  	unsigned int link_id; -	seq_printf(m, "VIF %pM\n", rtwvif->mac_addr); +	designated_link = rtw89_get_designated_link(rtwvif); + +	p += scnprintf(p, end - p, "VIF %pM\n", rtwvif->mac_addr);  	rtw89_vif_for_each_link(rtwvif, rtwvif_link, link_id) -		rtw89_vif_link_ids_get(m, mac, rtwdev, rtwvif_link); +		p += rtw89_vif_link_ids_get(rtwdev, p, end - p, mac, rtwvif_link, +					    rtwvif_link == designated_link); + +	rtw89_debugfs_iter_data_next(iter_data, p, end - p, p - buf);  } -static void rtw89_dump_ba_cam(struct seq_file *m, struct rtw89_dev *rtwdev, -			      struct rtw89_sta_link *rtwsta_link) +static int rtw89_dump_ba_cam(struct rtw89_dev *rtwdev, +			     char *buf, size_t bufsz, +			     struct rtw89_sta_link *rtwsta_link)  {  	struct rtw89_ba_cam_entry *entry; +	char *p = buf, *end = buf + bufsz;  	bool first = true;  	list_for_each_entry(entry, &rtwsta_link->ba_cam_list, list) {  		if (first) { -			seq_puts(m, "\tba_cam "); +			p += scnprintf(p, end - p, "\tba_cam ");  			first = false;  		} else { -			seq_puts(m, ", "); +			p += scnprintf(p, end - p, ", ");  		} -		seq_printf(m, "tid[%u]=%d", entry->tid, -			   (int)(entry - rtwdev->cam_info.ba_cam_entry)); +		p += scnprintf(p, end - p, "tid[%u]=%d", entry->tid, +			       (int)(entry - rtwdev->cam_info.ba_cam_entry));  	} -	seq_puts(m, "\n"); +	p += scnprintf(p, end - p, "\n"); + +	return p - buf;  } -static void rtw89_sta_link_ids_get(struct seq_file *m, -				   struct rtw89_dev *rtwdev, -				   struct rtw89_sta_link *rtwsta_link) +static int rtw89_sta_link_ids_get(struct rtw89_dev *rtwdev, +				  char *buf, size_t bufsz, +				  struct rtw89_sta_link *rtwsta_link, +				  bool designated)  {  	struct ieee80211_link_sta *link_sta; +	char *p = buf, *end = buf + bufsz;  	rcu_read_lock();  	link_sta = rtw89_sta_rcu_dereference_link(rtwsta_link, true); -	seq_printf(m, "    [%u] %pM\n", rtwsta_link->mac_id, link_sta->addr); +	p += scnprintf(p, end - p, "    [%u] %pM\n", rtwsta_link->mac_id, +		       link_sta->addr);  	rcu_read_unlock(); -	seq_printf(m, "\tlink_id=%u\n", rtwsta_link->link_id); -	rtw89_dump_addr_cam(m, rtwdev, &rtwsta_link->addr_cam); -	rtw89_dump_ba_cam(m, rtwdev, rtwsta_link); +	p += scnprintf(p, end - p, "\tlink_id=%u%s\n", rtwsta_link->link_id, +		       designated ? " (*)" : ""); +	p += rtw89_dump_addr_cam(rtwdev, p, end - p, &rtwsta_link->addr_cam); +	p += rtw89_dump_ba_cam(rtwdev, p, end - p, rtwsta_link); + +	return p - buf;  }  static void rtw89_sta_ids_get_iter(void *data, struct ieee80211_sta *sta)  { -	struct seq_file *m = (struct seq_file *)data; +	struct rtw89_debugfs_iter_data *iter_data = +		(struct rtw89_debugfs_iter_data *)data;  	struct rtw89_sta *rtwsta = sta_to_rtwsta(sta);  	struct rtw89_dev *rtwdev = rtwsta->rtwdev; +	struct rtw89_sta_link *designated_link;  	struct rtw89_sta_link *rtwsta_link; +	size_t bufsz = iter_data->bufsz; +	char *buf = iter_data->buf; +	char *p = buf, *end = buf + bufsz;  	unsigned int link_id; -	seq_printf(m, "STA %pM %s\n", sta->addr, sta->tdls ? "(TDLS)" : ""); +	designated_link = rtw89_get_designated_link(rtwsta); + +	p += scnprintf(p, end - p, "STA %pM %s\n", sta->addr, +		       sta->tdls ? "(TDLS)" : "");  	rtw89_sta_for_each_link(rtwsta, rtwsta_link, link_id) -		rtw89_sta_link_ids_get(m, rtwdev, rtwsta_link); +		p += rtw89_sta_link_ids_get(rtwdev, p, end - p, rtwsta_link, +					    rtwsta_link == designated_link); + +	rtw89_debugfs_iter_data_next(iter_data, p, end - p, p - buf);  } -static int rtw89_debug_priv_stations_get(struct seq_file *m, void *v) +static ssize_t rtw89_debug_priv_stations_get(struct rtw89_dev *rtwdev, +					     struct rtw89_debugfs_priv *debugfs_priv, +					     char *buf, size_t bufsz)  { -	struct rtw89_debugfs_priv *debugfs_priv = m->private; -	struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;  	struct rtw89_cam_info *cam_info = &rtwdev->cam_info; +	struct rtw89_debugfs_iter_data iter_data; +	char *p = buf, *end = buf + bufsz;  	u8 idx; -	mutex_lock(&rtwdev->mutex); - -	seq_puts(m, "map:\n"); -	seq_printf(m, "\tmac_id:    %*ph\n", (int)sizeof(rtwdev->mac_id_map), -		   rtwdev->mac_id_map); -	seq_printf(m, "\taddr_cam:  %*ph\n", (int)sizeof(cam_info->addr_cam_map), -		   cam_info->addr_cam_map); -	seq_printf(m, "\tbssid_cam: %*ph\n", (int)sizeof(cam_info->bssid_cam_map), -		   cam_info->bssid_cam_map); -	seq_printf(m, "\tsec_cam:   %*ph\n", (int)sizeof(cam_info->sec_cam_map), -		   cam_info->sec_cam_map); -	seq_printf(m, "\tba_cam:    %*ph\n", (int)sizeof(cam_info->ba_cam_map), -		   cam_info->ba_cam_map); -	seq_printf(m, "\tpkt_ofld:  %*ph\n", (int)sizeof(rtwdev->pkt_offload), -		   rtwdev->pkt_offload); +	lockdep_assert_wiphy(rtwdev->hw->wiphy); + +	p += scnprintf(p, end - p, "map:\n"); +	p += scnprintf(p, end - p, "\tmac_id:    %*ph\n", +		       (int)sizeof(rtwdev->mac_id_map), +		       rtwdev->mac_id_map); +	p += scnprintf(p, end - p, "\taddr_cam:  %*ph\n", +		       (int)sizeof(cam_info->addr_cam_map), +		       cam_info->addr_cam_map); +	p += scnprintf(p, end - p, "\tbssid_cam: %*ph\n", +		       (int)sizeof(cam_info->bssid_cam_map), +		       cam_info->bssid_cam_map); +	p += scnprintf(p, end - p, "\tsec_cam:   %*ph\n", +		       (int)sizeof(cam_info->sec_cam_map), +		       cam_info->sec_cam_map); +	p += scnprintf(p, end - p, "\tba_cam:    %*ph\n", +		       (int)sizeof(cam_info->ba_cam_map), +		       cam_info->ba_cam_map); +	p += scnprintf(p, end - p, "\tpkt_ofld:  %*ph\n", +		       (int)sizeof(rtwdev->pkt_offload), +		       rtwdev->pkt_offload);  	for (idx = NL80211_BAND_2GHZ; idx < NUM_NL80211_BANDS; idx++) {  		if (!(rtwdev->chip->support_bands & BIT(idx)))  			continue; -		rtw89_dump_pkt_offload(m, &rtwdev->scan_info.pkt_list[idx], -				       "\t\t[SCAN %u]: ", idx); +		p += rtw89_dump_pkt_offload(p, end - p, &rtwdev->scan_info.pkt_list[idx], +					    "\t\t[SCAN %u]: ", idx);  	} +	rtw89_debugfs_iter_data_setup(&iter_data, p, end - p);  	ieee80211_iterate_active_interfaces_atomic(rtwdev->hw, -		IEEE80211_IFACE_ITER_NORMAL, rtw89_vif_ids_get_iter, m); +		IEEE80211_IFACE_ITER_NORMAL, rtw89_vif_ids_get_iter, &iter_data); +	p += iter_data.written_sz; + +	rtw89_debugfs_iter_data_setup(&iter_data, p, end - p); +	ieee80211_iterate_stations_atomic(rtwdev->hw, rtw89_sta_ids_get_iter, &iter_data); +	p += iter_data.written_sz; -	ieee80211_iterate_stations_atomic(rtwdev->hw, rtw89_sta_ids_get_iter, m); +	return p - buf; +} -	mutex_unlock(&rtwdev->mutex); +static void rtw89_debug_disable_dm_cfg_bmap(struct rtw89_dev *rtwdev, u32 new) +{ +	struct rtw89_hal *hal = &rtwdev->hal; +	u32 old = hal->disabled_dm_bitmap; -	return 0; +	if (new == old) +		return; + +	hal->disabled_dm_bitmap = new; + +	rtw89_debug(rtwdev, RTW89_DBG_STATE, "Disable DM: 0x%x -> 0x%x\n", old, new); +} + +static void rtw89_debug_disable_dm_set_flag(struct rtw89_dev *rtwdev, u8 flag) +{ +	struct rtw89_hal *hal = &rtwdev->hal; +	u32 cur = hal->disabled_dm_bitmap; + +	rtw89_debug_disable_dm_cfg_bmap(rtwdev, cur | BIT(flag)); +} + +static void rtw89_debug_disable_dm_clr_flag(struct rtw89_dev *rtwdev, u8 flag) +{ +	struct rtw89_hal *hal = &rtwdev->hal; +	u32 cur = hal->disabled_dm_bitmap; + +	rtw89_debug_disable_dm_cfg_bmap(rtwdev, cur & ~BIT(flag));  }  #define DM_INFO(type) {RTW89_DM_ ## type, #type} @@ -3920,92 +4196,187 @@ static const struct rtw89_disabled_dm_info {  } rtw89_disabled_dm_infos[] = {  	DM_INFO(DYNAMIC_EDCCA),  	DM_INFO(THERMAL_PROTECT), +	DM_INFO(TAS), +	DM_INFO(MLO),  }; -static int -rtw89_debug_priv_disable_dm_get(struct seq_file *m, void *v) +static ssize_t +rtw89_debug_priv_disable_dm_get(struct rtw89_dev *rtwdev, +				struct rtw89_debugfs_priv *debugfs_priv, +				char *buf, size_t bufsz)  { -	struct rtw89_debugfs_priv *debugfs_priv = m->private; -	struct rtw89_dev *rtwdev = debugfs_priv->rtwdev;  	const struct rtw89_disabled_dm_info *info;  	struct rtw89_hal *hal = &rtwdev->hal; +	char *p = buf, *end = buf + bufsz;  	u32 disabled;  	int i; -	seq_printf(m, "Disabled DM: 0x%x\n", hal->disabled_dm_bitmap); +	p += scnprintf(p, end - p, "Disabled DM: 0x%x\n", +		       hal->disabled_dm_bitmap);  	for (i = 0; i < ARRAY_SIZE(rtw89_disabled_dm_infos); i++) {  		info = &rtw89_disabled_dm_infos[i];  		disabled = BIT(info->type) & hal->disabled_dm_bitmap; -		seq_printf(m, "[%d] %s: %c\n", info->type, info->name, -			   disabled ? 'X' : 'O'); +		p += scnprintf(p, end - p, "[%d] %s: %c\n", info->type, +			       info->name, +			       disabled ? 'X' : 'O');  	} -	return 0; +	return p - buf;  }  static ssize_t -rtw89_debug_priv_disable_dm_set(struct file *filp, const char __user *user_buf, -				size_t count, loff_t *loff) +rtw89_debug_priv_disable_dm_set(struct rtw89_dev *rtwdev, +				struct rtw89_debugfs_priv *debugfs_priv, +				const char *buf, size_t count)  { -	struct seq_file *m = (struct seq_file *)filp->private_data; -	struct rtw89_debugfs_priv *debugfs_priv = m->private; -	struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; -	struct rtw89_hal *hal = &rtwdev->hal;  	u32 conf;  	int ret; -	ret = kstrtou32_from_user(user_buf, count, 0, &conf); +	ret = kstrtou32(buf, 0, &conf);  	if (ret)  		return -EINVAL; -	hal->disabled_dm_bitmap = conf; +	rtw89_debug_disable_dm_cfg_bmap(rtwdev, conf); + +	return count; +} + +static void rtw89_debug_mlo_mode_set_mlsr(struct rtw89_dev *rtwdev, +					  unsigned int link_id) +{ +	struct ieee80211_vif *vif; +	struct rtw89_vif *rtwvif; + +	rtw89_for_each_rtwvif(rtwdev, rtwvif) { +		vif = rtwvif_to_vif(rtwvif); +		if (!ieee80211_vif_is_mld(vif)) +			continue; + +		rtw89_core_mlsr_switch(rtwdev, rtwvif, link_id); +	} +} + +static ssize_t +rtw89_debug_priv_mlo_mode_get(struct rtw89_dev *rtwdev, +			      struct rtw89_debugfs_priv *debugfs_priv, +			      char *buf, size_t bufsz) +{ +	bool mlo_dm_dis = rtwdev->hal.disabled_dm_bitmap & BIT(RTW89_DM_MLO); +	char *p = buf, *end = buf + bufsz; +	struct ieee80211_vif *vif; +	struct rtw89_vif *rtwvif; +	int count = 0; + +	p += scnprintf(p, end - p, "MLD(s) status: (MLO DM: %s)\n", +		       str_disable_enable(mlo_dm_dis)); + +	rtw89_for_each_rtwvif(rtwdev, rtwvif) { +		vif = rtwvif_to_vif(rtwvif); +		if (!ieee80211_vif_is_mld(vif)) +			continue; + +		p += scnprintf(p, end - p, +			       "\t#%u: MLO mode %x, valid 0x%x, active 0x%x\n", +			       count++, rtwvif->mlo_mode, vif->valid_links, +			       vif->active_links); +	} + +	if (count == 0) +		p += scnprintf(p, end - p, "\t(None)\n"); + +	return p - buf; +} + +static ssize_t +rtw89_debug_priv_mlo_mode_set(struct rtw89_dev *rtwdev, +			      struct rtw89_debugfs_priv *debugfs_priv, +			      const char *buf, size_t count) +{ +	u8 num, mlo_mode; +	u32 argv; + +	num = sscanf(buf, "%hhx %u", &mlo_mode, &argv); +	if (num != 2) +		return -EINVAL; + +	rtw89_debug_disable_dm_set_flag(rtwdev, RTW89_DM_MLO); + +	rtw89_debug(rtwdev, RTW89_DBG_STATE, "Set MLO mode to %x\n", mlo_mode); + +	switch (mlo_mode) { +	case RTW89_MLO_MODE_MLSR: +		rtw89_debug_mlo_mode_set_mlsr(rtwdev, argv); +		break; +	default: +		rtw89_debug(rtwdev, RTW89_DBG_STATE, "Unsupported MLO mode\n"); +		rtw89_debug_disable_dm_clr_flag(rtwdev, RTW89_DM_MLO); + +		return -EOPNOTSUPP; +	}  	return count;  } -#define rtw89_debug_priv_get(name)				\ +#define rtw89_debug_priv_get(name, opts...)			\  {								\  	.cb_read = rtw89_debug_priv_ ##name## _get,		\ +	.opt = { opts },					\  } -#define rtw89_debug_priv_set(name)				\ +#define rtw89_debug_priv_set(name, opts...)			\  {								\  	.cb_write = rtw89_debug_priv_ ##name## _set,		\ +	.opt = { opts },					\  } -#define rtw89_debug_priv_select_and_get(name)			\ +#define rtw89_debug_priv_select_and_get(name, opts...)		\  {								\  	.cb_write = rtw89_debug_priv_ ##name## _select,		\  	.cb_read = rtw89_debug_priv_ ##name## _get,		\ +	.opt = { opts },					\  } -#define rtw89_debug_priv_set_and_get(name)			\ +#define rtw89_debug_priv_set_and_get(name, opts...)		\  {								\  	.cb_write = rtw89_debug_priv_ ##name## _set,		\  	.cb_read = rtw89_debug_priv_ ##name## _get,		\ +	.opt = { opts },					\  } +#define RSIZE_8K .rsize = 0x2000 +#define RSIZE_12K .rsize = 0x3000 +#define RSIZE_16K .rsize = 0x4000 +#define RSIZE_20K .rsize = 0x5000 +#define RSIZE_32K .rsize = 0x8000 +#define RSIZE_64K .rsize = 0x10000 +#define RSIZE_128K .rsize = 0x20000 +#define RSIZE_1M .rsize = 0x100000 +#define RLOCK .rlock = 1 +#define WLOCK .wlock = 1 +#define RWLOCK RLOCK, WLOCK +  static const struct rtw89_debugfs rtw89_debugfs_templ = {  	.read_reg = rtw89_debug_priv_select_and_get(read_reg),  	.write_reg = rtw89_debug_priv_set(write_reg),  	.read_rf = rtw89_debug_priv_select_and_get(read_rf),  	.write_rf = rtw89_debug_priv_set(write_rf), -	.rf_reg_dump = rtw89_debug_priv_get(rf_reg_dump), -	.txpwr_table = rtw89_debug_priv_get(txpwr_table), -	.mac_reg_dump = rtw89_debug_priv_select_and_get(mac_reg_dump), -	.mac_mem_dump = rtw89_debug_priv_select_and_get(mac_mem_dump), -	.mac_dbg_port_dump = rtw89_debug_priv_select_and_get(mac_dbg_port_dump), +	.rf_reg_dump = rtw89_debug_priv_get(rf_reg_dump, RSIZE_8K), +	.txpwr_table = rtw89_debug_priv_get(txpwr_table, RSIZE_20K, RLOCK), +	.mac_reg_dump = rtw89_debug_priv_select_and_get(mac_reg_dump, RSIZE_128K), +	.mac_mem_dump = rtw89_debug_priv_select_and_get(mac_mem_dump, RSIZE_16K, RLOCK), +	.mac_dbg_port_dump = rtw89_debug_priv_select_and_get(mac_dbg_port_dump, RSIZE_1M),  	.send_h2c = rtw89_debug_priv_set(send_h2c), -	.early_h2c = rtw89_debug_priv_set_and_get(early_h2c), -	.fw_crash = rtw89_debug_priv_set_and_get(fw_crash), -	.btc_info = rtw89_debug_priv_get(btc_info), +	.early_h2c = rtw89_debug_priv_set_and_get(early_h2c, RWLOCK), +	.fw_crash = rtw89_debug_priv_set_and_get(fw_crash, WLOCK), +	.btc_info = rtw89_debug_priv_get(btc_info, RSIZE_12K),  	.btc_manual = rtw89_debug_priv_set(btc_manual), -	.fw_log_manual = rtw89_debug_priv_set(fw_log_manual), +	.fw_log_manual = rtw89_debug_priv_set(fw_log_manual, WLOCK),  	.phy_info = rtw89_debug_priv_get(phy_info), -	.stations = rtw89_debug_priv_get(stations), -	.disable_dm = rtw89_debug_priv_set_and_get(disable_dm), +	.stations = rtw89_debug_priv_get(stations, RLOCK), +	.disable_dm = rtw89_debug_priv_set_and_get(disable_dm, RWLOCK), +	.mlo_mode = rtw89_debug_priv_set_and_get(mlo_mode, RWLOCK),  };  #define rtw89_debugfs_add(name, mode, fopname, parent)				\ @@ -4050,6 +4421,7 @@ void rtw89_debugfs_add_sec1(struct rtw89_dev *rtwdev, struct dentry *debugfs_top  	rtw89_debugfs_add_r(phy_info);  	rtw89_debugfs_add_r(stations);  	rtw89_debugfs_add_rw(disable_dm); +	rtw89_debugfs_add_rw(mlo_mode);  }  void rtw89_debugfs_init(struct rtw89_dev *rtwdev) diff --git a/sys/contrib/dev/rtw89/fw.c b/sys/contrib/dev/rtw89/fw.c index b4c0f864bc75..1685084b0140 100644 --- a/sys/contrib/dev/rtw89/fw.c +++ b/sys/contrib/dev/rtw89/fw.c @@ -15,6 +15,8 @@  #include "util.h"  #include "wow.h" +static bool rtw89_is_any_vif_connected_or_connecting(struct rtw89_dev *rtwdev); +  struct rtw89_eapol_2_of_2 {  	u8 gtkbody[14];  	u8 key_des_ver; @@ -38,6 +40,16 @@ struct rtw89_arp_rsp {  static const u8 mss_signature[] = {0x4D, 0x53, 0x53, 0x4B, 0x50, 0x4F, 0x4F, 0x4C}; +const struct rtw89_fw_blacklist rtw89_fw_blacklist_default = { +	.ver = 0x00, +	.list = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, +		 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, +		 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, +		 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, +	}, +}; +EXPORT_SYMBOL(rtw89_fw_blacklist_default); +  union rtw89_fw_element_arg {  	size_t offset;  	enum rtw89_rf_path rf_path; @@ -327,7 +339,7 @@ static int __parse_formatted_mssc(struct rtw89_dev *rtwdev,  	if (!sec->secure_boot)  		goto out; -	sb_sel_ver = le32_to_cpu(section_content->sb_sel_ver.v); +	sb_sel_ver = get_unaligned_le32(§ion_content->sb_sel_ver.v);  	if (sb_sel_ver && sb_sel_ver != sec->sb_sel_mgn)  		goto ignore; @@ -357,6 +369,46 @@ ignore:  	return 0;  } +static int __check_secure_blacklist(struct rtw89_dev *rtwdev, +				    struct rtw89_fw_bin_info *info, +				    struct rtw89_fw_hdr_section_info *section_info, +				    const void *content) +{ +	const struct rtw89_fw_blacklist *chip_blacklist = rtwdev->chip->fw_blacklist; +	const union rtw89_fw_section_mssc_content *section_content = content; +	struct rtw89_fw_secure *sec = &rtwdev->fw.sec; +	u8 byte_idx; +	u8 bit_mask; + +	if (!sec->secure_boot) +		return 0; + +	if (!info->secure_section_exist || section_info->ignore) +		return 0; + +	if (!chip_blacklist) { +		rtw89_warn(rtwdev, "chip no blacklist for secure firmware\n"); +		return -ENOENT; +	} + +	byte_idx = section_content->blacklist.bit_in_chip_list >> 3; +	bit_mask = BIT(section_content->blacklist.bit_in_chip_list & 0x7); + +	if (section_content->blacklist.ver > chip_blacklist->ver) { +		rtw89_warn(rtwdev, "chip blacklist out of date (%u, %u)\n", +			   section_content->blacklist.ver, chip_blacklist->ver); +		return -EINVAL; +	} + +	if (chip_blacklist->list[byte_idx] & bit_mask) { +		rtw89_warn(rtwdev, "firmware %u in chip blacklist\n", +			   section_content->blacklist.ver); +		return -EPERM; +	} + +	return 0; +} +  static int __parse_security_section(struct rtw89_dev *rtwdev,  				    struct rtw89_fw_bin_info *info,  				    struct rtw89_fw_hdr_section_info *section_info, @@ -381,8 +433,11 @@ static int __parse_security_section(struct rtw89_dev *rtwdev,  			*mssc_len += section_info->mssc * FWDL_SECURITY_CHKSUM_LEN;  		if (sec->secure_boot) { -			if (sec->mss_idx >= section_info->mssc) +			if (sec->mss_idx >= section_info->mssc) { +				rtw89_err(rtwdev, "unexpected MSS %d >= %d\n", +					  sec->mss_idx, section_info->mssc);  				return -EFAULT; +			}  			section_info->key_addr = content + section_info->len +  						 sec->mss_idx * FWDL_SECURITY_SIGLEN;  			section_info->key_len = FWDL_SECURITY_SIGLEN; @@ -391,6 +446,9 @@ static int __parse_security_section(struct rtw89_dev *rtwdev,  		info->secure_section_exist = true;  	} +	ret = __check_secure_blacklist(rtwdev, info, section_info, content); +	WARN_ONCE(ret, "Current firmware in blacklist. Please update firmware.\n"); +  	return 0;  } @@ -507,18 +565,69 @@ static int rtw89_fw_hdr_parser(struct rtw89_dev *rtwdev,  }  static +const struct rtw89_mfw_hdr *rtw89_mfw_get_hdr_ptr(struct rtw89_dev *rtwdev, +						  const struct firmware *firmware) +{ +	const struct rtw89_mfw_hdr *mfw_hdr; + +	if (sizeof(*mfw_hdr) > firmware->size) +		return NULL; + +	mfw_hdr = (const struct rtw89_mfw_hdr *)&firmware->data[0]; + +	if (mfw_hdr->sig != RTW89_MFW_SIG) +		return NULL; + +	return mfw_hdr; +} + +static int rtw89_mfw_validate_hdr(struct rtw89_dev *rtwdev, +				  const struct firmware *firmware, +				  const struct rtw89_mfw_hdr *mfw_hdr) +{ +#if defined(__linux__) +	const void *mfw = firmware->data; +#elif defined(__FreeBSD__) +	const u8 *mfw = firmware->data; +#endif +	u32 mfw_len = firmware->size; +	u8 fw_nr = mfw_hdr->fw_nr; +	const void *ptr; + +	if (fw_nr == 0) { +		rtw89_err(rtwdev, "mfw header has no fw entry\n"); +		return -ENOENT; +	} + +	ptr = &mfw_hdr->info[fw_nr]; + +#if defined(__linux__) +	if (ptr > mfw + mfw_len) { +#elif defined(__FreeBSD__) +	if ((const u8 *)ptr > mfw + mfw_len) { +#endif +		rtw89_err(rtwdev, "mfw header out of address\n"); +		return -EFAULT; +	} + +	return 0; +} + +static  int rtw89_mfw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,  			struct rtw89_fw_suit *fw_suit, bool nowarn)  {  	struct rtw89_fw_info *fw_info = &rtwdev->fw;  	const struct firmware *firmware = fw_info->req.firmware; +	const struct rtw89_mfw_info *mfw_info = NULL, *tmp; +	const struct rtw89_mfw_hdr *mfw_hdr;  	const u8 *mfw = firmware->data;  	u32 mfw_len = firmware->size; -	const struct rtw89_mfw_hdr *mfw_hdr = (const struct rtw89_mfw_hdr *)mfw; -	const struct rtw89_mfw_info *mfw_info = NULL, *tmp; +	int ret;  	int i; -	if (mfw_hdr->sig != RTW89_MFW_SIG) { +	mfw_hdr = rtw89_mfw_get_hdr_ptr(rtwdev, firmware); +	if (!mfw_hdr) {  		rtw89_debug(rtwdev, RTW89_DBG_FW, "use legacy firmware\n");  		/* legacy firmware support normal type only */  		if (type != RTW89_FW_NORMAL) @@ -528,6 +637,10 @@ int rtw89_mfw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,  		return 0;  	} +	ret = rtw89_mfw_validate_hdr(rtwdev, firmware, mfw_hdr); +	if (ret) +		return ret; +  	for (i = 0; i < mfw_hdr->fw_nr; i++) {  		tmp = &mfw_hdr->info[i];  		if (tmp->type != type) @@ -557,6 +670,12 @@ int rtw89_mfw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,  found:  	fw_suit->data = mfw + le32_to_cpu(mfw_info->shift);  	fw_suit->size = le32_to_cpu(mfw_info->size); + +	if (fw_suit->data + fw_suit->size > mfw + mfw_len) { +		rtw89_err(rtwdev, "fw_suit %d out of address\n", type); +		return -EFAULT; +	} +  	return 0;  } @@ -564,16 +683,21 @@ static u32 rtw89_mfw_get_size(struct rtw89_dev *rtwdev)  {  	struct rtw89_fw_info *fw_info = &rtwdev->fw;  	const struct firmware *firmware = fw_info->req.firmware; -	const struct rtw89_mfw_hdr *mfw_hdr = -		(const struct rtw89_mfw_hdr *)firmware->data;  	const struct rtw89_mfw_info *mfw_info; +	const struct rtw89_mfw_hdr *mfw_hdr;  	u32 size; +	int ret; -	if (mfw_hdr->sig != RTW89_MFW_SIG) { +	mfw_hdr = rtw89_mfw_get_hdr_ptr(rtwdev, firmware); +	if (!mfw_hdr) {  		rtw89_warn(rtwdev, "not mfw format\n");  		return 0;  	} +	ret = rtw89_mfw_validate_hdr(rtwdev, firmware, mfw_hdr); +	if (ret) +		return ret; +  	mfw_info = &mfw_hdr->info[mfw_hdr->fw_nr - 1];  	size = le32_to_cpu(mfw_info->shift) + le32_to_cpu(mfw_info->size); @@ -719,36 +843,45 @@ struct __fw_feat_cfg {  static const struct __fw_feat_cfg fw_feat_tbl[] = {  	__CFG_FW_FEAT(RTL8851B, ge, 0, 29, 37, 1, TX_WAKE),  	__CFG_FW_FEAT(RTL8851B, ge, 0, 29, 37, 1, SCAN_OFFLOAD), -	__CFG_FW_FEAT(RTL8851B, ge, 0, 29, 41, 0, CRASH_TRIGGER), +	__CFG_FW_FEAT(RTL8851B, ge, 0, 29, 41, 0, CRASH_TRIGGER_TYPE_0),  	__CFG_FW_FEAT(RTL8852A, le, 0, 13, 29, 0, OLD_HT_RA_FORMAT),  	__CFG_FW_FEAT(RTL8852A, ge, 0, 13, 35, 0, SCAN_OFFLOAD),  	__CFG_FW_FEAT(RTL8852A, ge, 0, 13, 35, 0, TX_WAKE), -	__CFG_FW_FEAT(RTL8852A, ge, 0, 13, 36, 0, CRASH_TRIGGER), +	__CFG_FW_FEAT(RTL8852A, ge, 0, 13, 36, 0, CRASH_TRIGGER_TYPE_0),  	__CFG_FW_FEAT(RTL8852A, lt, 0, 13, 37, 0, NO_WOW_CPU_IO_RX),  	__CFG_FW_FEAT(RTL8852A, lt, 0, 13, 38, 0, NO_PACKET_DROP),  	__CFG_FW_FEAT(RTL8852B, ge, 0, 29, 26, 0, NO_LPS_PG),  	__CFG_FW_FEAT(RTL8852B, ge, 0, 29, 26, 0, TX_WAKE), -	__CFG_FW_FEAT(RTL8852B, ge, 0, 29, 29, 0, CRASH_TRIGGER), +	__CFG_FW_FEAT(RTL8852B, ge, 0, 29, 29, 0, CRASH_TRIGGER_TYPE_0),  	__CFG_FW_FEAT(RTL8852B, ge, 0, 29, 29, 0, SCAN_OFFLOAD),  	__CFG_FW_FEAT(RTL8852B, ge, 0, 29, 29, 7, BEACON_FILTER),  	__CFG_FW_FEAT(RTL8852B, lt, 0, 29, 30, 0, NO_WOW_CPU_IO_RX), +	__CFG_FW_FEAT(RTL8852B, ge, 0, 29, 127, 0, LPS_DACK_BY_C2H_REG), +	__CFG_FW_FEAT(RTL8852B, ge, 0, 29, 128, 0, CRASH_TRIGGER_TYPE_1), +	__CFG_FW_FEAT(RTL8852B, ge, 0, 29, 128, 0, SCAN_OFFLOAD_EXTRA_OP),  	__CFG_FW_FEAT(RTL8852BT, ge, 0, 29, 74, 0, NO_LPS_PG),  	__CFG_FW_FEAT(RTL8852BT, ge, 0, 29, 74, 0, TX_WAKE), -	__CFG_FW_FEAT(RTL8852BT, ge, 0, 29, 90, 0, CRASH_TRIGGER), +	__CFG_FW_FEAT(RTL8852BT, ge, 0, 29, 90, 0, CRASH_TRIGGER_TYPE_0),  	__CFG_FW_FEAT(RTL8852BT, ge, 0, 29, 91, 0, SCAN_OFFLOAD),  	__CFG_FW_FEAT(RTL8852BT, ge, 0, 29, 110, 0, BEACON_FILTER), +	__CFG_FW_FEAT(RTL8852BT, ge, 0, 29, 127, 0, SCAN_OFFLOAD_EXTRA_OP), +	__CFG_FW_FEAT(RTL8852BT, ge, 0, 29, 127, 0, LPS_DACK_BY_C2H_REG), +	__CFG_FW_FEAT(RTL8852BT, ge, 0, 29, 127, 0, CRASH_TRIGGER_TYPE_1),  	__CFG_FW_FEAT(RTL8852C, le, 0, 27, 33, 0, NO_DEEP_PS), +	__CFG_FW_FEAT(RTL8852C, ge, 0, 0, 0, 0, RFK_NTFY_MCC_V0),  	__CFG_FW_FEAT(RTL8852C, ge, 0, 27, 34, 0, TX_WAKE),  	__CFG_FW_FEAT(RTL8852C, ge, 0, 27, 36, 0, SCAN_OFFLOAD), -	__CFG_FW_FEAT(RTL8852C, ge, 0, 27, 40, 0, CRASH_TRIGGER), +	__CFG_FW_FEAT(RTL8852C, ge, 0, 27, 40, 0, CRASH_TRIGGER_TYPE_0),  	__CFG_FW_FEAT(RTL8852C, ge, 0, 27, 56, 10, BEACON_FILTER),  	__CFG_FW_FEAT(RTL8852C, ge, 0, 27, 80, 0, WOW_REASON_V1), -	__CFG_FW_FEAT(RTL8922A, ge, 0, 34, 30, 0, CRASH_TRIGGER), +	__CFG_FW_FEAT(RTL8852C, ge, 0, 27, 128, 0, BEACON_LOSS_COUNT_V1), +	__CFG_FW_FEAT(RTL8922A, ge, 0, 34, 30, 0, CRASH_TRIGGER_TYPE_0),  	__CFG_FW_FEAT(RTL8922A, ge, 0, 34, 11, 0, MACID_PAUSE_SLEEP),  	__CFG_FW_FEAT(RTL8922A, ge, 0, 34, 35, 0, SCAN_OFFLOAD),  	__CFG_FW_FEAT(RTL8922A, lt, 0, 35, 21, 0, SCAN_OFFLOAD_BE_V0),  	__CFG_FW_FEAT(RTL8922A, ge, 0, 35, 12, 0, BEACON_FILTER),  	__CFG_FW_FEAT(RTL8922A, ge, 0, 35, 22, 0, WOW_REASON_V1), +	__CFG_FW_FEAT(RTL8922A, lt, 0, 35, 28, 0, RFK_IQK_V0),  	__CFG_FW_FEAT(RTL8922A, lt, 0, 35, 31, 0, RFK_PRE_NOTIFY_V0),  	__CFG_FW_FEAT(RTL8922A, lt, 0, 35, 31, 0, LPS_CH_INFO),  	__CFG_FW_FEAT(RTL8922A, lt, 0, 35, 42, 0, RFK_RXDCK_V0), @@ -756,6 +889,10 @@ static const struct __fw_feat_cfg fw_feat_tbl[] = {  	__CFG_FW_FEAT(RTL8922A, lt, 0, 35, 47, 0, CH_INFO_BE_V0),  	__CFG_FW_FEAT(RTL8922A, lt, 0, 35, 49, 0, RFK_PRE_NOTIFY_V1),  	__CFG_FW_FEAT(RTL8922A, lt, 0, 35, 51, 0, NO_PHYCAP_P1), +	__CFG_FW_FEAT(RTL8922A, lt, 0, 35, 64, 0, NO_POWER_DIFFERENCE), +	__CFG_FW_FEAT(RTL8922A, ge, 0, 35, 71, 0, BEACON_LOSS_COUNT_V1), +	__CFG_FW_FEAT(RTL8922A, ge, 0, 35, 76, 0, LPS_DACK_BY_C2H_REG), +	__CFG_FW_FEAT(RTL8922A, ge, 0, 35, 79, 0, CRASH_TRIGGER_TYPE_1),  };  static void rtw89_fw_iterate_feature_cfg(struct rtw89_fw_info *fw, @@ -924,7 +1061,7 @@ int rtw89_build_phy_tbl_from_elm(struct rtw89_dev *rtwdev,  	}  	n_regs = le32_to_cpu(elm->size) / sizeof(tbl->regs[0]); -	regs = kcalloc(n_regs, sizeof(tbl->regs[0]), GFP_KERNEL); +	regs = kcalloc(n_regs, sizeof(*regs), GFP_KERNEL);  	if (!regs)  		goto out; @@ -1013,7 +1150,7 @@ int rtw89_build_txpwr_trk_tbl_from_elm(struct rtw89_dev *rtwdev,  	bitmap = le32_to_cpu(elm->u.txpwr_trk.bitmap);  	if ((bitmap & needed_bitmap) != needed_bitmap) { -		rtw89_warn(rtwdev, "needed txpwr trk bitmap %08x but %0x8x\n", +		rtw89_warn(rtwdev, "needed txpwr trk bitmap %08x but %08x\n",  			   needed_bitmap, bitmap);  		return -ENOENT;  	} @@ -1081,6 +1218,110 @@ allocated:  	return 0;  } +static bool rtw89_regd_entcpy(struct rtw89_regd *regd, const void *cursor, +			      u8 cursor_size) +{ +	/* fill default values if needed for backward compatibility */ +	struct rtw89_fw_regd_entry entry = { +		.rule_2ghz = RTW89_NA, +		.rule_5ghz = RTW89_NA, +		.rule_6ghz = RTW89_NA, +		.fmap = cpu_to_le32(0x0), +	}; +	u8 valid_size = min_t(u8, sizeof(entry), cursor_size); +	unsigned int i; +	u32 fmap; + +	memcpy(&entry, cursor, valid_size); +	memset(regd, 0, sizeof(*regd)); + +	regd->alpha2[0] = entry.alpha2_0; +	regd->alpha2[1] = entry.alpha2_1; +	regd->alpha2[2] = '\0'; + +	/* also need to consider forward compatibility */ +	regd->txpwr_regd[RTW89_BAND_2G] = entry.rule_2ghz < RTW89_REGD_NUM ? +					  entry.rule_2ghz : RTW89_NA; +	regd->txpwr_regd[RTW89_BAND_5G] = entry.rule_5ghz < RTW89_REGD_NUM ? +					  entry.rule_5ghz : RTW89_NA; +	regd->txpwr_regd[RTW89_BAND_6G] = entry.rule_6ghz < RTW89_REGD_NUM ? +					  entry.rule_6ghz : RTW89_NA; + +	BUILD_BUG_ON(sizeof(fmap) != sizeof(entry.fmap)); +	BUILD_BUG_ON(sizeof(fmap) * 8 < NUM_OF_RTW89_REGD_FUNC); + +	fmap = le32_to_cpu(entry.fmap); +	for (i = 0; i < NUM_OF_RTW89_REGD_FUNC; i++) { +		if (fmap & BIT(i)) +			set_bit(i, regd->func_bitmap); +	} + +	return true; +} + +#if defined(__linux__) +#define rtw89_for_each_in_regd_element(regd, element) \ +	for (const void *cursor = (element)->content, \ +	     *end = (element)->content + \ +		    le32_to_cpu((element)->num_ents) * (element)->ent_sz; \ +	     cursor < end; cursor += (element)->ent_sz) \ +		if (rtw89_regd_entcpy(regd, cursor, (element)->ent_sz)) +#elif defined(__FreeBSD__) +#define rtw89_for_each_in_regd_element(regd, element) \ +	for (const u8 *cursor = (element)->content, \ +	     *end = (element)->content + \ +		    le32_to_cpu((element)->num_ents) * (element)->ent_sz; \ +	     cursor < end; cursor += (element)->ent_sz) \ +		if (rtw89_regd_entcpy(regd, cursor, (element)->ent_sz)) +#endif + +static +int rtw89_recognize_regd_from_elm(struct rtw89_dev *rtwdev, +				  const struct rtw89_fw_element_hdr *elm, +				  const union rtw89_fw_element_arg arg) +{ +	const struct __rtw89_fw_regd_element *regd_elm = &elm->u.regd; +	struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info; +	u32 num_ents = le32_to_cpu(regd_elm->num_ents); +	struct rtw89_regd_data *p; +	struct rtw89_regd regd; +	u32 i = 0; + +	if (num_ents > RTW89_REGD_MAX_COUNTRY_NUM) { +		rtw89_warn(rtwdev, +			   "regd element ents (%d) are over max num (%d)\n", +			   num_ents, RTW89_REGD_MAX_COUNTRY_NUM); +		rtw89_warn(rtwdev, +			   "regd element ignore and take another/common\n"); +		return 1; +	} + +	if (elm_info->regd) { +		rtw89_debug(rtwdev, RTW89_DBG_REGD, +			    "regd element take the latter\n"); +		devm_kfree(rtwdev->dev, elm_info->regd); +		elm_info->regd = NULL; +	} + +	p = devm_kzalloc(rtwdev->dev, struct_size(p, map, num_ents), GFP_KERNEL); +	if (!p) +		return -ENOMEM; + +	p->nr = num_ents; +	rtw89_for_each_in_regd_element(®d, regd_elm) +		p->map[i++] = regd; + +	if (i != num_ents) { +		rtw89_err(rtwdev, "regd element has %d invalid ents\n", +			  num_ents - i); +		devm_kfree(rtwdev->dev, p); +		return -EINVAL; +	} + +	elm_info->regd = p; +	return 0; +} +  static const struct rtw89_fw_element_handler __fw_element_handlers[] = {  	[RTW89_FW_ELEMENT_ID_BBMCU0] = {__rtw89_fw_recognize_from_elm,  					{ .fw_type = RTW89_FW_BBMCU0 }, NULL}, @@ -1113,6 +1354,18 @@ static const struct rtw89_fw_element_handler __fw_element_handlers[] = {  		rtw89_fw_recognize_txpwr_from_elm,  		{ .offset = offsetof(struct rtw89_rfe_data, lmt_6ghz.conf) }, NULL,  	}, +	[RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_2GHZ] = { +		rtw89_fw_recognize_txpwr_from_elm, +		{ .offset = offsetof(struct rtw89_rfe_data, da_lmt_2ghz.conf) }, NULL, +	}, +	[RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_5GHZ] = { +		rtw89_fw_recognize_txpwr_from_elm, +		{ .offset = offsetof(struct rtw89_rfe_data, da_lmt_5ghz.conf) }, NULL, +	}, +	[RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_6GHZ] = { +		rtw89_fw_recognize_txpwr_from_elm, +		{ .offset = offsetof(struct rtw89_rfe_data, da_lmt_6ghz.conf) }, NULL, +	},  	[RTW89_FW_ELEMENT_ID_TXPWR_LMT_RU_2GHZ] = {  		rtw89_fw_recognize_txpwr_from_elm,  		{ .offset = offsetof(struct rtw89_rfe_data, lmt_ru_2ghz.conf) }, NULL, @@ -1125,6 +1378,18 @@ static const struct rtw89_fw_element_handler __fw_element_handlers[] = {  		rtw89_fw_recognize_txpwr_from_elm,  		{ .offset = offsetof(struct rtw89_rfe_data, lmt_ru_6ghz.conf) }, NULL,  	}, +	[RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_RU_2GHZ] = { +		rtw89_fw_recognize_txpwr_from_elm, +		{ .offset = offsetof(struct rtw89_rfe_data, da_lmt_ru_2ghz.conf) }, NULL, +	}, +	[RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_RU_5GHZ] = { +		rtw89_fw_recognize_txpwr_from_elm, +		{ .offset = offsetof(struct rtw89_rfe_data, da_lmt_ru_5ghz.conf) }, NULL, +	}, +	[RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_RU_6GHZ] = { +		rtw89_fw_recognize_txpwr_from_elm, +		{ .offset = offsetof(struct rtw89_rfe_data, da_lmt_ru_6ghz.conf) }, NULL, +	},  	[RTW89_FW_ELEMENT_ID_TX_SHAPE_LMT] = {  		rtw89_fw_recognize_txpwr_from_elm,  		{ .offset = offsetof(struct rtw89_rfe_data, tx_shape_lmt.conf) }, NULL, @@ -1139,6 +1404,9 @@ static const struct rtw89_fw_element_handler __fw_element_handlers[] = {  	[RTW89_FW_ELEMENT_ID_RFKLOG_FMT] = {  		rtw89_build_rfk_log_fmt_from_elm, {}, NULL,  	}, +	[RTW89_FW_ELEMENT_ID_REGD] = { +		rtw89_recognize_regd_from_elm, {}, "REGD", +	},  };  int rtw89_fw_recognize_elements(struct rtw89_dev *rtwdev) @@ -1347,7 +1615,6 @@ static int __rtw89_fw_download_hdr(struct rtw89_dev *rtwdev,  	ret = rtw89_h2c_tx(rtwdev, skb, false);  	if (ret) {  		rtw89_err(rtwdev, "failed to send h2c\n"); -		ret = -1;  		goto fail;  	} @@ -1434,7 +1701,6 @@ static int __rtw89_fw_download_main(struct rtw89_dev *rtwdev,  		ret = rtw89_h2c_tx(rtwdev, skb, true);  		if (ret) {  			rtw89_err(rtwdev, "failed to send h2c\n"); -			ret = -1;  			goto fail;  		} @@ -2310,7 +2576,7 @@ int rtw89_fw_h2c_fw_log(struct rtw89_dev *rtwdev, bool enable)  	if (enable)  		comp = BIT(RTW89_FW_LOG_COMP_INIT) | BIT(RTW89_FW_LOG_COMP_TASK) |  		       BIT(RTW89_FW_LOG_COMP_PS) | BIT(RTW89_FW_LOG_COMP_ERROR) | -		       BIT(RTW89_FW_LOG_COMP_SCAN); +		       BIT(RTW89_FW_LOG_COMP_MLO) | BIT(RTW89_FW_LOG_COMP_SCAN);  	skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_LOG_CFG_LEN);  	if (!skb) { @@ -2621,8 +2887,14 @@ int rtw89_fw_h2c_lps_parm(struct rtw89_dev *rtwdev,  			  struct rtw89_lps_parm *lps_param)  {  	struct sk_buff *skb; +	bool done_ack;  	int ret; +	if (RTW89_CHK_FW_FEATURE(LPS_DACK_BY_C2H_REG, &rtwdev->fw)) +		done_ack = false; +	else +		done_ack = !lps_param->psmode; +  	skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_LPS_PARM_LEN);  	if (!skb) {  		rtw89_err(rtwdev, "failed to alloc skb for fw dl\n"); @@ -2644,7 +2916,7 @@ int rtw89_fw_h2c_lps_parm(struct rtw89_dev *rtwdev,  	rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,  			      H2C_CAT_MAC,  			      H2C_CL_MAC_PS, -			      H2C_FUNC_MAC_LPS_PARM, 0, !lps_param->psmode, +			      H2C_FUNC_MAC_LPS_PARM, 0, done_ack,  			      H2C_LPS_PARM_LEN);  	ret = rtw89_h2c_tx(rtwdev, skb, false); @@ -2735,7 +3007,9 @@ int rtw89_fw_h2c_lps_ml_cmn_info(struct rtw89_dev *rtwdev,  {  	const struct rtw89_phy_bb_gain_info_be *gain = &rtwdev->bb_gain.be;  	struct rtw89_pkt_stat *pkt_stat = &rtwdev->phystat.cur_pkt_stat; +	static const u8 bcn_bw_ofst[] = {0, 0, 0, 3, 6, 9, 0, 12};  	const struct rtw89_chip_info *chip = rtwdev->chip; +	struct rtw89_efuse *efuse = &rtwdev->efuse;  	struct rtw89_h2c_lps_ml_cmn_info *h2c;  	struct rtw89_vif_link *rtwvif_link;  	const struct rtw89_chan *chan; @@ -2743,6 +3017,7 @@ int rtw89_fw_h2c_lps_ml_cmn_info(struct rtw89_dev *rtwdev,  	u32 len = sizeof(*h2c);  	unsigned int link_id;  	struct sk_buff *skb; +	u8 beacon_bw_ofst;  	u8 gain_band;  	u32 done;  	u8 path; @@ -2760,9 +3035,10 @@ int rtw89_fw_h2c_lps_ml_cmn_info(struct rtw89_dev *rtwdev,  	skb_put(skb, len);  	h2c = (struct rtw89_h2c_lps_ml_cmn_info *)skb->data; -	h2c->fmt_id = 0x1; +	h2c->fmt_id = 0x3;  	h2c->mlo_dbcc_mode = cpu_to_le32(rtwdev->mlo_dbcc_mode); +	h2c->rfe_type = efuse->rfe_type;  	rtw89_vif_for_each_link(rtwvif, rtwvif_link, link_id) {  		path = rtwvif_link->phy_idx == RTW89_PHY_1 ? RF_PATH_B : RF_PATH_A; @@ -2783,9 +3059,21 @@ int rtw89_fw_h2c_lps_ml_cmn_info(struct rtw89_dev *rtwdev,  			h2c->tia_gain[rtwvif_link->phy_idx][i] =  				cpu_to_le16(gain->tia_gain[gain_band][bw_idx][path][i]);  		} + +		if (rtwvif_link->bcn_bw_idx < ARRAY_SIZE(bcn_bw_ofst)) { +			beacon_bw_ofst = bcn_bw_ofst[rtwvif_link->bcn_bw_idx]; +			h2c->dup_bcn_ofst[rtwvif_link->phy_idx] = beacon_bw_ofst; +		} +  		memcpy(h2c->lna_gain[rtwvif_link->phy_idx],  		       gain->lna_gain[gain_band][bw_idx][path],  		       LNA_GAIN_NUM); +		memcpy(h2c->tia_lna_op1db[rtwvif_link->phy_idx], +		       gain->tia_lna_op1db[gain_band][bw_idx][path], +		       LNA_GAIN_NUM + 1); +		memcpy(h2c->lna_op1db[rtwvif_link->phy_idx], +		       gain->lna_op1db[gain_band][bw_idx][path], +		       LNA_GAIN_NUM);  	}  	rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, @@ -2814,12 +3102,10 @@ fail:  #define H2C_P2P_ACT_LEN 20  int rtw89_fw_h2c_p2p_act(struct rtw89_dev *rtwdev,  			 struct rtw89_vif_link *rtwvif_link, -			 struct ieee80211_bss_conf *bss_conf,  			 struct ieee80211_p2p_noa_desc *desc, -			 u8 act, u8 noa_id) +			 u8 act, u8 noa_id, u8 ctwindow_oppps)  {  	bool p2p_type_gc = rtwvif_link->wifi_role == RTW89_WIFI_ROLE_P2P_CLIENT; -	u8 ctwindow_oppps = bss_conf->p2p_noa_attr.oppps_ctwindow;  	struct sk_buff *skb;  	u8 *cmd;  	int ret; @@ -2876,8 +3162,8 @@ static void __rtw89_fw_h2c_set_tx_path(struct rtw89_dev *rtwdev,  		ntx_path = RF_A;  		map_b = 0;  	} else { -		ntx_path = hal->antenna_tx ? hal->antenna_tx : RF_B; -		map_b = hal->antenna_tx == RF_AB ? 1 : 0; +		ntx_path = hal->antenna_tx ? hal->antenna_tx : RF_AB; +		map_b = ntx_path == RF_AB ? 1 : 0;  	}  	SET_CMC_TBL_NTX_PATH_EN(skb->data, ntx_path); @@ -3319,9 +3605,10 @@ int rtw89_fw_h2c_assoc_cmac_tbl_g7(struct rtw89_dev *rtwdev,  			      CCTLINFO_G7_W5_NOMINAL_PKT_PADDING3 |  			      CCTLINFO_G7_W5_NOMINAL_PKT_PADDING4); -	h2c->w6 = le32_encode_bits(vif->type == NL80211_IFTYPE_STATION ? 1 : 0, +	h2c->w6 = le32_encode_bits(vif->cfg.aid, CCTLINFO_G7_W6_AID12_PAID) | +		  le32_encode_bits(vif->type == NL80211_IFTYPE_STATION ? 1 : 0,  				   CCTLINFO_G7_W6_ULDL); -	h2c->m6 = cpu_to_le32(CCTLINFO_G7_W6_ULDL); +	h2c->m6 = cpu_to_le32(CCTLINFO_G7_W6_AID12_PAID | CCTLINFO_G7_W6_ULDL);  	if (rtwsta_link) {  		h2c->w8 = le32_encode_bits(link_sta->he_cap.has_he, @@ -3457,6 +3744,104 @@ fail:  	return ret;  } +EXPORT_SYMBOL(rtw89_fw_h2c_txtime_cmac_tbl); + +int rtw89_fw_h2c_txtime_cmac_tbl_g7(struct rtw89_dev *rtwdev, +				    struct rtw89_sta_link *rtwsta_link) +{ +	struct rtw89_h2c_cctlinfo_ud_g7 *h2c; +	u32 len = sizeof(*h2c); +	struct sk_buff *skb; +	int ret; + +	skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); +	if (!skb) { +		rtw89_err(rtwdev, "failed to alloc skb for txtime_cmac_g7\n"); +		return -ENOMEM; +	} +	skb_put(skb, len); +	h2c = (struct rtw89_h2c_cctlinfo_ud_g7 *)skb->data; + +	h2c->c0 = le32_encode_bits(rtwsta_link->mac_id, CCTLINFO_G7_C0_MACID) | +		  le32_encode_bits(1, CCTLINFO_G7_C0_OP); + +	if (rtwsta_link->cctl_tx_time) { +		h2c->w3 |= le32_encode_bits(1, CCTLINFO_G7_W3_AMPDU_TIME_SEL); +		h2c->m3 |= cpu_to_le32(CCTLINFO_G7_W3_AMPDU_TIME_SEL); + +		h2c->w2 |= le32_encode_bits(rtwsta_link->ampdu_max_time, +					   CCTLINFO_G7_W2_AMPDU_MAX_TIME); +		h2c->m2 |= cpu_to_le32(CCTLINFO_G7_W2_AMPDU_MAX_TIME); +	} +	if (rtwsta_link->cctl_tx_retry_limit) { +		h2c->w2 |= le32_encode_bits(1, CCTLINFO_G7_W2_DATA_TXCNT_LMT_SEL) | +			   le32_encode_bits(rtwsta_link->data_tx_cnt_lmt, +					    CCTLINFO_G7_W2_DATA_TX_CNT_LMT); +		h2c->m2 |= cpu_to_le32(CCTLINFO_G7_W2_DATA_TXCNT_LMT_SEL | +				       CCTLINFO_G7_W2_DATA_TX_CNT_LMT); +	} + +	rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, +			      H2C_CAT_MAC, H2C_CL_MAC_FR_EXCHG, +			      H2C_FUNC_MAC_CCTLINFO_UD_G7, 0, 1, +			      len); + +	ret = rtw89_h2c_tx(rtwdev, skb, false); +	if (ret) { +		rtw89_err(rtwdev, "failed to send h2c\n"); +		goto fail; +	} + +	return 0; +fail: +	dev_kfree_skb_any(skb); + +	return ret; +} +EXPORT_SYMBOL(rtw89_fw_h2c_txtime_cmac_tbl_g7); + +int rtw89_fw_h2c_punctured_cmac_tbl_g7(struct rtw89_dev *rtwdev, +				       struct rtw89_vif_link *rtwvif_link, +				       u16 punctured) +{ +	struct rtw89_h2c_cctlinfo_ud_g7 *h2c; +	u32 len = sizeof(*h2c); +	struct sk_buff *skb; +	int ret; + +	skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); +	if (!skb) { +		rtw89_err(rtwdev, "failed to alloc skb for punctured cmac g7\n"); +		return -ENOMEM; +	} + +	skb_put(skb, len); +	h2c = (struct rtw89_h2c_cctlinfo_ud_g7 *)skb->data; + +	h2c->c0 = le32_encode_bits(rtwvif_link->mac_id, CCTLINFO_G7_C0_MACID) | +		  le32_encode_bits(1, CCTLINFO_G7_C0_OP); + +	h2c->w4 = le32_encode_bits(~punctured, CCTLINFO_G7_W4_ACT_SUBCH_CBW); +	h2c->m4 = cpu_to_le32(CCTLINFO_G7_W4_ACT_SUBCH_CBW); + +	rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, +			      H2C_CAT_MAC, H2C_CL_MAC_FR_EXCHG, +			      H2C_FUNC_MAC_CCTLINFO_UD_G7, 0, 1, +			      len); + +	ret = rtw89_h2c_tx(rtwdev, skb, false); +	if (ret) { +		rtw89_err(rtwdev, "failed to send h2c\n"); +		goto fail; +	} + +	return 0; +fail: +	dev_kfree_skb_any(skb); + +	return ret; +} +EXPORT_SYMBOL(rtw89_fw_h2c_punctured_cmac_tbl_g7);  int rtw89_fw_h2c_txpath_cmac_tbl(struct rtw89_dev *rtwdev,  				 struct rtw89_sta_link *rtwsta_link) @@ -3660,14 +4045,15 @@ fail:  }  EXPORT_SYMBOL(rtw89_fw_h2c_update_beacon_be); -#define H2C_ROLE_MAINTAIN_LEN 4  int rtw89_fw_h2c_role_maintain(struct rtw89_dev *rtwdev,  			       struct rtw89_vif_link *rtwvif_link,  			       struct rtw89_sta_link *rtwsta_link,  			       enum rtw89_upd_mode upd_mode)  { -	struct sk_buff *skb;  	u8 mac_id = rtwsta_link ? rtwsta_link->mac_id : rtwvif_link->mac_id; +	struct rtw89_h2c_role_maintain *h2c; +	u32 len = sizeof(*h2c); +	struct sk_buff *skb;  	u8 self_role;  	int ret; @@ -3680,21 +4066,27 @@ int rtw89_fw_h2c_role_maintain(struct rtw89_dev *rtwdev,  		self_role = rtwvif_link->self_role;  	} -	skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_ROLE_MAINTAIN_LEN); +	skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len);  	if (!skb) {  		rtw89_err(rtwdev, "failed to alloc skb for h2c join\n");  		return -ENOMEM;  	} -	skb_put(skb, H2C_ROLE_MAINTAIN_LEN); -	SET_FWROLE_MAINTAIN_MACID(skb->data, mac_id); -	SET_FWROLE_MAINTAIN_SELF_ROLE(skb->data, self_role); -	SET_FWROLE_MAINTAIN_UPD_MODE(skb->data, upd_mode); -	SET_FWROLE_MAINTAIN_WIFI_ROLE(skb->data, rtwvif_link->wifi_role); +	skb_put(skb, len); +	h2c = (struct rtw89_h2c_role_maintain *)skb->data; + +	h2c->w0 = le32_encode_bits(mac_id, RTW89_H2C_ROLE_MAINTAIN_W0_MACID) | +		  le32_encode_bits(self_role, RTW89_H2C_ROLE_MAINTAIN_W0_SELF_ROLE) | +		  le32_encode_bits(upd_mode, RTW89_H2C_ROLE_MAINTAIN_W0_UPD_MODE) | +		  le32_encode_bits(rtwvif_link->wifi_role, +				   RTW89_H2C_ROLE_MAINTAIN_W0_WIFI_ROLE) | +		  le32_encode_bits(rtwvif_link->mac_idx, +				   RTW89_H2C_ROLE_MAINTAIN_W0_BAND) | +		  le32_encode_bits(rtwvif_link->port, RTW89_H2C_ROLE_MAINTAIN_W0_PORT);  	rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,  			      H2C_CAT_MAC, H2C_CL_MAC_MEDIA_RPT,  			      H2C_FUNC_MAC_FWROLE_MAINTAIN, 0, 1, -			      H2C_ROLE_MAINTAIN_LEN); +			      len);  	ret = rtw89_h2c_tx(rtwdev, skb, false);  	if (ret) { @@ -3752,8 +4144,9 @@ out:  int rtw89_fw_h2c_join_info(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link,  			   struct rtw89_sta_link *rtwsta_link, bool dis_conn)  { -	struct sk_buff *skb;  	u8 mac_id = rtwsta_link ? rtwsta_link->mac_id : rtwvif_link->mac_id; +	struct ieee80211_vif *vif = rtwvif_link_to_vif(rtwvif_link); +	bool is_mld = ieee80211_vif_is_mld(vif);  	u8 self_role = rtwvif_link->self_role;  	enum rtw89_fw_sta_type sta_type;  	u8 net_type = rtwvif_link->net_type; @@ -3761,6 +4154,9 @@ int rtw89_fw_h2c_join_info(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwv  	struct rtw89_h2c_join *h2c;  	u32 len = sizeof(*h2c);  	bool format_v1 = false; +	struct sk_buff *skb; +	u8 main_mac_id; +	bool init_ps;  	int ret;  	if (rtwdev->chip->chip_gen == RTW89_CHIP_BE) { @@ -3802,8 +4198,28 @@ int rtw89_fw_h2c_join_info(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwv  	h2c_v1 = (struct rtw89_h2c_join_v1 *)skb->data;  	sta_type = rtw89_fw_get_sta_type(rtwdev, rtwvif_link, rtwsta_link); +	init_ps = rtwvif_link != rtw89_get_designated_link(rtwvif_link->rtwvif); + +	if (rtwsta_link) +		main_mac_id = rtw89_sta_get_main_macid(rtwsta_link->rtwsta); +	else +		main_mac_id = rtw89_vif_get_main_macid(rtwvif_link->rtwvif); + +	h2c_v1->w1 = le32_encode_bits(sta_type, RTW89_H2C_JOININFO_W1_STA_TYPE) | +		     le32_encode_bits(is_mld, RTW89_H2C_JOININFO_W1_IS_MLD) | +		     le32_encode_bits(main_mac_id, RTW89_H2C_JOININFO_W1_MAIN_MACID) | +		     le32_encode_bits(RTW89_H2C_JOININFO_MLO_MODE_MLSR, +				      RTW89_H2C_JOININFO_W1_MLO_MODE) | +		     le32_encode_bits(0, RTW89_H2C_JOININFO_W1_EMLSR_CAB) | +		     le32_encode_bits(0, RTW89_H2C_JOININFO_W1_NSTR_EN) | +		     le32_encode_bits(init_ps, RTW89_H2C_JOININFO_W1_INIT_PWR_STATE) | +		     le32_encode_bits(IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_256US, +				      RTW89_H2C_JOININFO_W1_EMLSR_PADDING) | +		     le32_encode_bits(IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_256US, +				      RTW89_H2C_JOININFO_W1_EMLSR_TRANS_DELAY) | +		     le32_encode_bits(0, RTW89_H2C_JOININFO_W2_MACID_EXT) | +		     le32_encode_bits(0, RTW89_H2C_JOININFO_W2_MAIN_MACID_EXT); -	h2c_v1->w1 = le32_encode_bits(sta_type, RTW89_H2C_JOININFO_W1_STA_TYPE);  	h2c_v1->w2 = 0;  done: @@ -4087,6 +4503,7 @@ int rtw89_fw_h2c_set_bcn_fltr_cfg(struct rtw89_dev *rtwdev,  	struct rtw89_h2c_bcnfltr *h2c;  	u32 len = sizeof(*h2c);  	struct sk_buff *skb; +	u8 max_cnt, cnt;  	int ret;  	if (!RTW89_CHK_FW_FEATURE(BEACON_FILTER, &rtwdev->fw)) @@ -4115,12 +4532,20 @@ int rtw89_fw_h2c_set_bcn_fltr_cfg(struct rtw89_dev *rtwdev,  	skb_put(skb, len);  	h2c = (struct rtw89_h2c_bcnfltr *)skb->data; +	if (RTW89_CHK_FW_FEATURE(BEACON_LOSS_COUNT_V1, &rtwdev->fw)) +		max_cnt = BIT(7) - 1; +	else +		max_cnt = BIT(4) - 1; + +	cnt = min(RTW89_BCN_LOSS_CNT, max_cnt); +  	h2c->w0 = le32_encode_bits(connect, RTW89_H2C_BCNFLTR_W0_MON_RSSI) |  		  le32_encode_bits(connect, RTW89_H2C_BCNFLTR_W0_MON_BCN) |  		  le32_encode_bits(connect, RTW89_H2C_BCNFLTR_W0_MON_EN) |  		  le32_encode_bits(RTW89_BCN_FLTR_OFFLOAD_MODE_DEFAULT,  				   RTW89_H2C_BCNFLTR_W0_MODE) | -		  le32_encode_bits(RTW89_BCN_LOSS_CNT, RTW89_H2C_BCNFLTR_W0_BCN_LOSS_CNT) | +		  le32_encode_bits(cnt >> 4, RTW89_H2C_BCNFLTR_W0_BCN_LOSS_CNT_H3) | +		  le32_encode_bits(cnt & 0xf, RTW89_H2C_BCNFLTR_W0_BCN_LOSS_CNT_L4) |  		  le32_encode_bits(hyst, RTW89_H2C_BCNFLTR_W0_RSSI_HYST) |  		  le32_encode_bits(thold + MAX_RSSI,  				   RTW89_H2C_BCNFLTR_W0_RSSI_THRESHOLD) | @@ -4775,6 +5200,46 @@ fail:  	return ret;  } +int rtw89_fw_h2c_cxdrv_osi_info(struct rtw89_dev *rtwdev, u8 type) +{ +	struct rtw89_btc *btc = &rtwdev->btc; +	struct rtw89_btc_fbtc_outsrc_set_info *osi = &btc->dm.ost_info; +	struct rtw89_h2c_cxosi *h2c; +	u32 len = sizeof(*h2c); +	struct sk_buff *skb; +	int ret; + +	skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); +	if (!skb) { +		rtw89_err(rtwdev, "failed to alloc skb for h2c cxdrv_osi\n"); +		return -ENOMEM; +	} +	skb_put(skb, len); +	h2c = (struct rtw89_h2c_cxosi *)skb->data; + +	h2c->hdr.type = type; +	h2c->hdr.ver = btc->ver->fcxosi; +	h2c->hdr.len = len - H2C_LEN_CXDRVHDR_V7; +	h2c->osi = *osi; + +	rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, +			      H2C_CAT_OUTSRC, BTFC_SET, +			      SET_DRV_INFO, 0, 0, +			      len); + +	ret = rtw89_h2c_tx(rtwdev, skb, false); +	if (ret) { +		rtw89_err(rtwdev, "failed to send h2c\n"); +		goto fail; +	} + +	return 0; +fail: +	dev_kfree_skb_any(skb); + +	return ret; +} +  #define H2C_LEN_CXDRVINFO_CTRL (4 + H2C_LEN_CXDRVHDR)  int rtw89_fw_h2c_cxdrv_ctrl(struct rtw89_dev *rtwdev, u8 type)  { @@ -5050,12 +5515,13 @@ int rtw89_fw_h2c_add_pkt_offload(struct rtw89_dev *rtwdev, u8 *id,  }  static -int rtw89_fw_h2c_scan_list_offload(struct rtw89_dev *rtwdev, int ch_num, -				   struct list_head *chan_list) +int rtw89_fw_h2c_scan_list_offload_ax(struct rtw89_dev *rtwdev, int ch_num, +				      struct list_head *chan_list)  { +	struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info;  	struct rtw89_wait_info *wait = &rtwdev->mac.fw_ofld_wait;  	struct rtw89_h2c_chinfo_elem *elem; -	struct rtw89_mac_chinfo *ch_info; +	struct rtw89_mac_chinfo_ax *ch_info;  	struct rtw89_h2c_chinfo *h2c;  	struct sk_buff *skb;  	unsigned int cond; @@ -5095,6 +5561,10 @@ int rtw89_fw_h2c_scan_list_offload(struct rtw89_dev *rtwdev, int ch_num,  			   le32_encode_bits(ch_info->tx_null, RTW89_H2C_CHINFO_W1_TX_NULL) |  			   le32_encode_bits(ch_info->rand_seq_num, RTW89_H2C_CHINFO_W1_RANDOM); +		if (scan_info->extra_op.set) +			elem->w1 |= le32_encode_bits(ch_info->macid_tx, +						     RTW89_H2C_CHINFO_W1_MACID_TX); +  		elem->w2 = le32_encode_bits(ch_info->pkt_id[0], RTW89_H2C_CHINFO_W2_PKT0) |  			   le32_encode_bits(ch_info->pkt_id[1], RTW89_H2C_CHINFO_W2_PKT1) |  			   le32_encode_bits(ch_info->pkt_id[2], RTW89_H2C_CHINFO_W2_PKT2) | @@ -5229,12 +5699,12 @@ int rtw89_fw_h2c_scan_list_offload_be(struct rtw89_dev *rtwdev, int ch_num,  	return 0;  } -#define RTW89_SCAN_DELAY_TSF_UNIT 104800  int rtw89_fw_h2c_scan_offload_ax(struct rtw89_dev *rtwdev,  				 struct rtw89_scan_option *option,  				 struct rtw89_vif_link *rtwvif_link,  				 bool wowlan)  { +	struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info;  	struct rtw89_wait_info *wait = &rtwdev->mac.fw_ofld_wait;  	struct rtw89_chan *op = &rtwdev->scan_info.op_chan;  	enum rtw89_scan_mode scan_mode = RTW89_SCAN_IMMEDIATE; @@ -5260,7 +5730,7 @@ int rtw89_fw_h2c_scan_offload_ax(struct rtw89_dev *rtwdev,  			scan_mode = RTW89_SCAN_IMMEDIATE;  		} else {  			scan_mode = RTW89_SCAN_DELAY; -			tsf += (u64)option->delay * RTW89_SCAN_DELAY_TSF_UNIT; +			tsf += (u64)option->delay * 1000;  		}  	} @@ -5294,6 +5764,10 @@ int rtw89_fw_h2c_scan_offload_ax(struct rtw89_dev *rtwdev,  	h2c->tsf_low = le32_encode_bits(lower_32_bits(tsf),  					RTW89_H2C_SCANOFLD_W4_TSF_LOW); +	if (scan_info->extra_op.set) +		h2c->w6 = le32_encode_bits(scan_info->extra_op.macid, +					   RTW89_H2C_SCANOFLD_W6_SECOND_MACID); +  	rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,  			      H2C_CAT_MAC, H2C_CL_MAC_FW_OFLD,  			      H2C_FUNC_SCANOFLD, 1, 1, @@ -5342,31 +5816,58 @@ int rtw89_fw_h2c_scan_offload_be(struct rtw89_dev *rtwdev,  {  	struct rtw89_vif *rtwvif = rtwvif_link->rtwvif;  	struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; +	const struct rtw89_hw_scan_extra_op *ext = &scan_info->extra_op;  	struct rtw89_wait_info *wait = &rtwdev->mac.fw_ofld_wait;  	struct cfg80211_scan_request *req = rtwvif->scan_req;  	struct rtw89_h2c_scanofld_be_macc_role *macc_role; +	struct rtw89_hw_scan_extra_op scan_op[2] = {};  	struct rtw89_chan *op = &scan_info->op_chan;  	struct rtw89_h2c_scanofld_be_opch *opch;  	struct rtw89_pktofld_info *pkt_info;  	struct rtw89_h2c_scanofld_be *h2c; +	struct ieee80211_vif *vif;  	struct sk_buff *skb;  	u8 macc_role_size = sizeof(*macc_role) * option->num_macc_role;  	u8 opch_size = sizeof(*opch) * option->num_opch; +	enum rtw89_scan_be_opmode opmode;  	u8 probe_id[NUM_NL80211_BANDS]; +	u8 scan_offload_ver = U8_MAX;  	u8 cfg_len = sizeof(*h2c);  	unsigned int cond; +	u8 ap_idx = U8_MAX;  	u8 ver = U8_MAX; +	u8 policy_val;  #if defined(__linux__)  	void *ptr;  #elif defined(__FreeBSD__)  	u8 *ptr;  #endif +	u8 txbcn;  	int ret;  	u32 len;  	u8 i; +	scan_op[0].macid = rtwvif_link->mac_id; +	scan_op[0].port = rtwvif_link->port; +	scan_op[0].chan = *op; +	vif = rtwvif_to_vif(rtwvif_link->rtwvif); +	if (vif->type == NL80211_IFTYPE_AP) +		ap_idx = 0; + +	if (ext->set) { +		scan_op[1] = *ext; +		vif = rtwvif_to_vif(ext->rtwvif_link->rtwvif); +		if (vif->type == NL80211_IFTYPE_AP) +			ap_idx = 1; +	} +  	rtw89_scan_get_6g_disabled_chan(rtwdev, option); +	if (RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD_BE_V0, &rtwdev->fw)) { +		cfg_len = offsetofend(typeof(*h2c), w8); +		scan_offload_ver = 0; +	} +  	len = cfg_len + macc_role_size + opch_size;  	skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len);  	if (!skb) { @@ -5420,7 +5921,7 @@ int rtw89_fw_h2c_scan_offload_be(struct rtw89_dev *rtwdev,  				   RTW89_H2C_SCANOFLD_BE_W4_PROBE_5G) |  		  le32_encode_bits(probe_id[NL80211_BAND_6GHZ],  				   RTW89_H2C_SCANOFLD_BE_W4_PROBE_6G) | -		  le32_encode_bits(option->delay, RTW89_H2C_SCANOFLD_BE_W4_DELAY_START); +		  le32_encode_bits(option->delay / 1000, RTW89_H2C_SCANOFLD_BE_W4_DELAY_START);  	h2c->w5 = le32_encode_bits(option->mlo_mode, RTW89_H2C_SCANOFLD_BE_W5_MLO_MODE); @@ -5438,10 +5939,8 @@ int rtw89_fw_h2c_scan_offload_be(struct rtw89_dev *rtwdev,  					   RTW89_H2C_SCANOFLD_BE_W8_PROBE_RATE_6GHZ);  	} -	if (RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD_BE_V0, &rtwdev->fw)) { -		cfg_len = offsetofend(typeof(*h2c), w8); +	if (scan_offload_ver == 0)  		goto flex_member; -	}  	h2c->w9 = le32_encode_bits(sizeof(*h2c) / sizeof(h2c->w0),  				   RTW89_H2C_SCANOFLD_BE_W9_SIZE_CFG) | @@ -5468,41 +5967,49 @@ flex_member:  	}  	for (i = 0; i < option->num_opch; i++) { +		bool is_ap_idx = i == ap_idx; + +		opmode = is_ap_idx ? RTW89_SCAN_OPMODE_TBTT : RTW89_SCAN_OPMODE_INTV; +		policy_val = is_ap_idx ? 2 : RTW89_OFF_CHAN_TIME / 10; +		txbcn = is_ap_idx ? 1 : 0; +  #if defined(__linux__)  		opch = ptr;  #elif defined(__FreeBSD__)  		opch = (void *)ptr;  #endif -		opch->w0 = le32_encode_bits(rtwvif_link->mac_id, +		opch->w0 = le32_encode_bits(scan_op[i].macid,  					    RTW89_H2C_SCANOFLD_BE_OPCH_W0_MACID) |  			   le32_encode_bits(option->band,  					    RTW89_H2C_SCANOFLD_BE_OPCH_W0_BAND) | -			   le32_encode_bits(rtwvif_link->port, +			   le32_encode_bits(scan_op[i].port,  					    RTW89_H2C_SCANOFLD_BE_OPCH_W0_PORT) | -			   le32_encode_bits(RTW89_SCAN_OPMODE_INTV, +			   le32_encode_bits(opmode,  					    RTW89_H2C_SCANOFLD_BE_OPCH_W0_POLICY) |  			   le32_encode_bits(true,  					    RTW89_H2C_SCANOFLD_BE_OPCH_W0_TXNULL) | -			   le32_encode_bits(RTW89_OFF_CHAN_TIME / 10, +			   le32_encode_bits(policy_val,  					    RTW89_H2C_SCANOFLD_BE_OPCH_W0_POLICY_VAL); -		opch->w1 = le32_encode_bits(op->band_type, +		opch->w1 = le32_encode_bits(scan_op[i].chan.band_type,  					    RTW89_H2C_SCANOFLD_BE_OPCH_W1_CH_BAND) | -			   le32_encode_bits(op->band_width, +			   le32_encode_bits(scan_op[i].chan.band_width,  					    RTW89_H2C_SCANOFLD_BE_OPCH_W1_BW) |  			   le32_encode_bits(0x3,  					    RTW89_H2C_SCANOFLD_BE_OPCH_W1_NOTIFY) | -			   le32_encode_bits(op->primary_channel, +			   le32_encode_bits(scan_op[i].chan.primary_channel,  					    RTW89_H2C_SCANOFLD_BE_OPCH_W1_PRI_CH) | -			   le32_encode_bits(op->channel, +			   le32_encode_bits(scan_op[i].chan.channel,  					    RTW89_H2C_SCANOFLD_BE_OPCH_W1_CENTRAL_CH);  		opch->w2 = le32_encode_bits(0,  					    RTW89_H2C_SCANOFLD_BE_OPCH_W2_PKTS_CTRL) |  			   le32_encode_bits(0,  					    RTW89_H2C_SCANOFLD_BE_OPCH_W2_SW_DEF) | -			   le32_encode_bits(2, -					    RTW89_H2C_SCANOFLD_BE_OPCH_W2_SS); +			   le32_encode_bits(rtw89_is_mlo_1_1(rtwdev) ? 1 : 2, +					    RTW89_H2C_SCANOFLD_BE_OPCH_W2_SS) | +			   le32_encode_bits(txbcn, +					    RTW89_H2C_SCANOFLD_BE_OPCH_W2_TXBCN);  		opch->w3 = le32_encode_bits(RTW89_SCANOFLD_PKT_NONE,  					    RTW89_H2C_SCANOFLD_BE_OPCH_W3_PKT0) | @@ -5577,31 +6084,48 @@ fail:  int rtw89_fw_h2c_rf_ntfy_mcc(struct rtw89_dev *rtwdev)  {  	struct rtw89_rfk_mcc_info_data *rfk_mcc = rtwdev->rfk_mcc.data; +	struct rtw89_fw_h2c_rf_get_mccch_v0 *mccch_v0;  	struct rtw89_fw_h2c_rf_get_mccch *mccch; +	u32 len = sizeof(*mccch);  	struct sk_buff *skb; +	u8 ver = U8_MAX;  	int ret;  	u8 idx; -	skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, sizeof(*mccch)); +	if (RTW89_CHK_FW_FEATURE(RFK_NTFY_MCC_V0, &rtwdev->fw)) { +		len = sizeof(*mccch_v0); +		ver = 0; +	} + +	skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len);  	if (!skb) {  		rtw89_err(rtwdev, "failed to alloc skb for h2c cxdrv_ctrl\n");  		return -ENOMEM;  	} -	skb_put(skb, sizeof(*mccch)); -	mccch = (struct rtw89_fw_h2c_rf_get_mccch *)skb->data; +	skb_put(skb, len);  	idx = rfk_mcc->table_idx; -	mccch->ch_0 = cpu_to_le32(rfk_mcc->ch[0]); -	mccch->ch_1 = cpu_to_le32(rfk_mcc->ch[1]); -	mccch->band_0 = cpu_to_le32(rfk_mcc->band[0]); -	mccch->band_1 = cpu_to_le32(rfk_mcc->band[1]); -	mccch->current_channel = cpu_to_le32(rfk_mcc->ch[idx]); -	mccch->current_band_type = cpu_to_le32(rfk_mcc->band[idx]); +	if (ver == 0) { +		mccch_v0 = (struct rtw89_fw_h2c_rf_get_mccch_v0 *)skb->data; +		mccch_v0->ch_0 = cpu_to_le32(rfk_mcc->ch[0]); +		mccch_v0->ch_1 = cpu_to_le32(rfk_mcc->ch[1]); +		mccch_v0->band_0 = cpu_to_le32(rfk_mcc->band[0]); +		mccch_v0->band_1 = cpu_to_le32(rfk_mcc->band[1]); +		mccch_v0->current_band_type = cpu_to_le32(rfk_mcc->band[idx]); +		mccch_v0->current_channel = cpu_to_le32(rfk_mcc->ch[idx]); +	} else { +		mccch = (struct rtw89_fw_h2c_rf_get_mccch *)skb->data; +		mccch->ch_0_0 = cpu_to_le32(rfk_mcc->ch[0]); +		mccch->ch_0_1 = cpu_to_le32(rfk_mcc->ch[0]); +		mccch->ch_1_0 = cpu_to_le32(rfk_mcc->ch[1]); +		mccch->ch_1_1 = cpu_to_le32(rfk_mcc->ch[1]); +		mccch->current_channel = cpu_to_le32(rfk_mcc->ch[idx]); +	}  	rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,  			      H2C_CAT_OUTSRC, H2C_CL_OUTSRC_RF_FW_NOTIFY,  			      H2C_FUNC_OUTSRC_RF_GET_MCCCH, 0, 0, -			      sizeof(*mccch)); +			      len);  	ret = rtw89_h2c_tx(rtwdev, skb, false);  	if (ret) { @@ -5617,6 +6141,118 @@ fail:  }  EXPORT_SYMBOL(rtw89_fw_h2c_rf_ntfy_mcc); +int rtw89_fw_h2c_mcc_dig(struct rtw89_dev *rtwdev, +			 enum rtw89_chanctx_idx chanctx_idx, +			 u8 mcc_role_idx, u8 pd_val, bool en) +{ +	const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, chanctx_idx); +	const struct rtw89_dig_regs *dig_regs = rtwdev->chip->dig_regs; +	struct rtw89_h2c_mcc_dig *h2c; +	u32 len = sizeof(*h2c); +	struct sk_buff *skb; +	int ret; + +	skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); +	if (!skb) { +		rtw89_err(rtwdev, "failed to alloc skb for h2c mcc_dig\n"); +		return -ENOMEM; +	} +	skb_put(skb, len); +	h2c = (struct rtw89_h2c_mcc_dig *)skb->data; + +	h2c->w0 = le32_encode_bits(1, RTW89_H2C_MCC_DIG_W0_REG_CNT) | +		  le32_encode_bits(en, RTW89_H2C_MCC_DIG_W0_DM_EN) | +		  le32_encode_bits(mcc_role_idx, RTW89_H2C_MCC_DIG_W0_IDX) | +		  le32_encode_bits(1, RTW89_H2C_MCC_DIG_W0_SET) | +		  le32_encode_bits(1, RTW89_H2C_MCC_DIG_W0_PHY0_EN) | +		  le32_encode_bits(chan->channel, RTW89_H2C_MCC_DIG_W0_CENTER_CH) | +		  le32_encode_bits(chan->band_type, RTW89_H2C_MCC_DIG_W0_BAND_TYPE); +	h2c->w1 = le32_encode_bits(dig_regs->seg0_pd_reg, +				   RTW89_H2C_MCC_DIG_W1_ADDR_LSB) | +		  le32_encode_bits(dig_regs->seg0_pd_reg >> 8, +				   RTW89_H2C_MCC_DIG_W1_ADDR_MSB) | +		  le32_encode_bits(dig_regs->pd_lower_bound_mask, +				   RTW89_H2C_MCC_DIG_W1_BMASK_LSB) | +		  le32_encode_bits(dig_regs->pd_lower_bound_mask >> 8, +				   RTW89_H2C_MCC_DIG_W1_BMASK_MSB); +	h2c->w2 = le32_encode_bits(pd_val, RTW89_H2C_MCC_DIG_W2_VAL_LSB); + +	rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, +			      H2C_CAT_OUTSRC, H2C_CL_OUTSRC_DM, +			      H2C_FUNC_FW_MCC_DIG, 0, 0, len); + +	ret = rtw89_h2c_tx(rtwdev, skb, false); +	if (ret) { +		rtw89_err(rtwdev, "failed to send h2c\n"); +		goto fail; +	} + +	return 0; +fail: +	dev_kfree_skb_any(skb); + +	return ret; +} + +int rtw89_fw_h2c_rf_ps_info(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) +{ +	const struct rtw89_chip_info *chip = rtwdev->chip; +	struct rtw89_vif_link *rtwvif_link; +	struct rtw89_h2c_rf_ps_info *h2c; +	const struct rtw89_chan *chan; +	u32 len = sizeof(*h2c); +	unsigned int link_id; +	struct sk_buff *skb; +	int ret; +	u8 path; +	u32 val; + +	if (chip->chip_gen != RTW89_CHIP_BE) +		return 0; + +	skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); +	if (!skb) { +		rtw89_err(rtwdev, "failed to alloc skb for h2c rf ps info\n"); +		return -ENOMEM; +	} +	skb_put(skb, len); +	h2c = (struct rtw89_h2c_rf_ps_info *)skb->data; +	h2c->mlo_mode = cpu_to_le32(rtwdev->mlo_dbcc_mode); + +	rtw89_vif_for_each_link(rtwvif, rtwvif_link, link_id) { +		chan = rtw89_chan_get(rtwdev, rtwvif_link->chanctx_idx); +		path = rtw89_phy_get_syn_sel(rtwdev, rtwvif_link->phy_idx); +		val = rtw89_chip_chan_to_rf18_val(rtwdev, chan); + +		if (path >= chip->rf_path_num || path >= NUM_OF_RTW89_FW_RFK_PATH) { +			rtw89_err(rtwdev, "unsupported rf path (%d)\n", path); +			ret = -ENOENT; +			goto fail; +		} + +		h2c->rf18[path] = cpu_to_le32(val); +		h2c->pri_ch[path] = chan->primary_channel; +	} + +	rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, +			      H2C_CAT_OUTSRC, H2C_CL_OUTSRC_RF_FW_NOTIFY, +			      H2C_FUNC_OUTSRC_RF_PS_INFO, 0, 0, +			      sizeof(*h2c)); + +	ret = rtw89_h2c_tx(rtwdev, skb, false); +	if (ret) { +		rtw89_err(rtwdev, "failed to send h2c\n"); +		goto fail; +	} + +	return 0; +fail: +	dev_kfree_skb_any(skb); + +	return ret; +} +EXPORT_SYMBOL(rtw89_fw_h2c_rf_ps_info); +  int rtw89_fw_h2c_rf_pre_ntfy(struct rtw89_dev *rtwdev,  			     enum rtw89_phy_idx phy_idx)  { @@ -5731,6 +6367,7 @@ fail:  int rtw89_fw_h2c_rf_tssi(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx,  			 const struct rtw89_chan *chan, enum rtw89_tssi_mode tssi_mode)  { +	struct rtw89_efuse *efuse = &rtwdev->efuse;  	struct rtw89_hal *hal = &rtwdev->hal;  	struct rtw89_h2c_rf_tssi *h2c;  	u32 len = sizeof(*h2c); @@ -5753,6 +6390,7 @@ int rtw89_fw_h2c_rf_tssi(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx,  	h2c->hwtx_en = true;  	h2c->cv = hal->cv;  	h2c->tssi_mode = tssi_mode; +	h2c->rfe_type = efuse->rfe_type;  	rtw89_phy_rfk_tssi_fill_fwcmd_efuse_to_de(rtwdev, phy_idx, chan, h2c);  	rtw89_phy_rfk_tssi_fill_fwcmd_tmeter_tbl(rtwdev, phy_idx, chan, h2c); @@ -5777,22 +6415,47 @@ fail:  int rtw89_fw_h2c_rf_iqk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx,  			const struct rtw89_chan *chan)  { +	struct rtw89_hal *hal = &rtwdev->hal; +	struct rtw89_h2c_rf_iqk_v0 *h2c_v0;  	struct rtw89_h2c_rf_iqk *h2c;  	u32 len = sizeof(*h2c);  	struct sk_buff *skb; +	u8 ver = U8_MAX;  	int ret; +	if (RTW89_CHK_FW_FEATURE(RFK_IQK_V0, &rtwdev->fw)) { +		len = sizeof(*h2c_v0); +		ver = 0; +	} +  	skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len);  	if (!skb) {  		rtw89_err(rtwdev, "failed to alloc skb for h2c RF IQK\n");  		return -ENOMEM;  	}  	skb_put(skb, len); + +	if (ver == 0) { +		h2c_v0 = (struct rtw89_h2c_rf_iqk_v0 *)skb->data; + +		h2c_v0->phy_idx = cpu_to_le32(phy_idx); +		h2c_v0->dbcc = cpu_to_le32(rtwdev->dbcc_en); + +		goto done; +	} +  	h2c = (struct rtw89_h2c_rf_iqk *)skb->data; -	h2c->phy_idx = cpu_to_le32(phy_idx); -	h2c->dbcc = cpu_to_le32(rtwdev->dbcc_en); +	h2c->len = sizeof(*h2c); +	h2c->ktype = 0; +	h2c->phy = phy_idx; +	h2c->kpath = rtw89_phy_get_kpath(rtwdev, phy_idx); +	h2c->band = chan->band_type; +	h2c->bw = chan->band_width; +	h2c->ch = chan->channel; +	h2c->cv = hal->cv; +done:  	rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,  			      H2C_CAT_OUTSRC, H2C_CL_OUTSRC_RF_FW_RFK,  			      H2C_FUNC_RFK_IQK_OFFLOAD, 0, 0, len); @@ -6048,24 +6711,29 @@ void rtw89_fw_send_all_early_h2c(struct rtw89_dev *rtwdev)  {  	struct rtw89_early_h2c *early_h2c; -	lockdep_assert_held(&rtwdev->mutex); +	lockdep_assert_wiphy(rtwdev->hw->wiphy);  	list_for_each_entry(early_h2c, &rtwdev->early_h2c_list, list) {  		rtw89_fw_h2c_raw(rtwdev, early_h2c->h2c, early_h2c->h2c_len);  	}  } -void rtw89_fw_free_all_early_h2c(struct rtw89_dev *rtwdev) +void __rtw89_fw_free_all_early_h2c(struct rtw89_dev *rtwdev)  {  	struct rtw89_early_h2c *early_h2c, *tmp; -	mutex_lock(&rtwdev->mutex);  	list_for_each_entry_safe(early_h2c, tmp, &rtwdev->early_h2c_list, list) {  		list_del(&early_h2c->list);  		kfree(early_h2c->h2c);  		kfree(early_h2c);  	} -	mutex_unlock(&rtwdev->mutex); +} + +void rtw89_fw_free_all_early_h2c(struct rtw89_dev *rtwdev) +{ +	lockdep_assert_wiphy(rtwdev->hw->wiphy); + +	__rtw89_fw_free_all_early_h2c(rtwdev);  }  static void rtw89_fw_c2h_parse_attr(struct sk_buff *c2h) @@ -6109,7 +6777,7 @@ void rtw89_fw_c2h_irqsafe(struct rtw89_dev *rtwdev, struct sk_buff *c2h)  enqueue:  	skb_queue_tail(&rtwdev->c2h_queue, c2h); -	ieee80211_queue_work(rtwdev->hw, &rtwdev->c2h_work); +	wiphy_work_queue(rtwdev->hw->wiphy, &rtwdev->c2h_work);  }  static void rtw89_fw_c2h_cmd_handle(struct rtw89_dev *rtwdev, @@ -6147,17 +6815,45 @@ static void rtw89_fw_c2h_cmd_handle(struct rtw89_dev *rtwdev,  		rtw89_hex_dump(rtwdev, RTW89_DBG_FW, "C2H: ", skb->data, skb->len);  } -void rtw89_fw_c2h_work(struct work_struct *work) +void rtw89_fw_c2h_work(struct wiphy *wiphy, struct wiphy_work *work)  {  	struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,  						c2h_work);  	struct sk_buff *skb, *tmp; +	lockdep_assert_wiphy(rtwdev->hw->wiphy); +  	skb_queue_walk_safe(&rtwdev->c2h_queue, skb, tmp) {  		skb_unlink(skb, &rtwdev->c2h_queue); -		mutex_lock(&rtwdev->mutex);  		rtw89_fw_c2h_cmd_handle(rtwdev, skb); -		mutex_unlock(&rtwdev->mutex); +		dev_kfree_skb_any(skb); +	} +} + +void rtw89_fw_c2h_purge_obsoleted_scan_events(struct rtw89_dev *rtwdev) +{ +	struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; +	struct sk_buff *skb, *tmp; +	int limit; + +	lockdep_assert_wiphy(rtwdev->hw->wiphy); + +	limit = skb_queue_len(&rtwdev->c2h_queue); + +	skb_queue_walk_safe(&rtwdev->c2h_queue, skb, tmp) { +		struct rtw89_fw_c2h_attr *attr = RTW89_SKB_C2H_CB(skb); + +		if (--limit < 0) +			return; + +		if (!attr->is_scan_event || attr->scan_seq == scan_info->seq) +			continue; + +		rtw89_debug(rtwdev, RTW89_DBG_HW_SCAN, +			    "purge obsoleted scan event with seq=%d (cur=%d)\n", +			    attr->scan_seq, scan_info->seq); + +		skb_unlink(skb, &rtwdev->c2h_queue);  		dev_kfree_skb_any(skb);  	}  } @@ -6201,13 +6897,18 @@ static int rtw89_fw_read_c2h_reg(struct rtw89_dev *rtwdev,  	const struct rtw89_chip_info *chip = rtwdev->chip;  	struct rtw89_fw_info *fw_info = &rtwdev->fw;  	const u32 *c2h_reg = chip->c2h_regs; -	u32 ret; +	u32 ret, timeout;  	u8 i, val;  	info->id = RTW89_FWCMD_C2HREG_FUNC_NULL; +	if (rtwdev->hci.type == RTW89_HCI_TYPE_USB) +		timeout = RTW89_C2H_TIMEOUT_USB; +	else +		timeout = RTW89_C2H_TIMEOUT; +  	ret = read_poll_timeout_atomic(rtw89_read8, val, val, 1, -				       RTW89_C2H_TIMEOUT, false, rtwdev, +				       timeout, false, rtwdev,  				       chip->c2h_ctrl_reg);  	if (ret) {  		rtw89_warn(rtwdev, "c2h reg timeout\n"); @@ -6238,7 +6939,7 @@ int rtw89_fw_msg_reg(struct rtw89_dev *rtwdev,  	u32 ret;  	if (h2c_info && h2c_info->id != RTW89_FWCMD_H2CREG_FUNC_GET_FEATURE) -		lockdep_assert_held(&rtwdev->mutex); +		lockdep_assert_wiphy(rtwdev->hw->wiphy);  	if (!h2c_info && !c2h_info)  		return -EINVAL; @@ -6280,7 +6981,7 @@ void rtw89_fw_st_dbg_dump(struct rtw89_dev *rtwdev)  	rtw89_fw_prog_cnt_dump(rtwdev);  } -static void rtw89_release_pkt_list(struct rtw89_dev *rtwdev) +static void rtw89_hw_scan_release_pkt_list(struct rtw89_dev *rtwdev)  {  	struct list_head *pkt_list = rtwdev->scan_info.pkt_list;  	struct rtw89_pktofld_info *info, *tmp; @@ -6299,6 +7000,24 @@ static void rtw89_release_pkt_list(struct rtw89_dev *rtwdev)  	}  } +static void rtw89_hw_scan_cleanup(struct rtw89_dev *rtwdev, +				  struct rtw89_vif_link *rtwvif_link) +{ +	const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; +	struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; +	struct rtw89_vif *rtwvif = rtwvif_link->rtwvif; + +	mac->free_chan_list(rtwdev); +	rtw89_hw_scan_release_pkt_list(rtwdev); + +	rtwvif->scan_req = NULL; +	rtwvif->scan_ies = NULL; +	scan_info->scanning_vif = NULL; +	scan_info->abort = false; +	scan_info->connected = false; +	scan_info->delay = 0; +} +  static bool rtw89_is_6ghz_wildcard_probe_req(struct rtw89_dev *rtwdev,  					     struct cfg80211_scan_request *req,  					     struct rtw89_pktofld_info *info, @@ -6367,7 +7086,8 @@ out:  }  static int rtw89_hw_scan_update_probe_req(struct rtw89_dev *rtwdev, -					  struct rtw89_vif_link *rtwvif_link) +					  struct rtw89_vif_link *rtwvif_link, +					  const u8 *mac_addr)  {  	struct rtw89_vif *rtwvif = rtwvif_link->rtwvif;  	struct cfg80211_scan_request *req = rtwvif->scan_req; @@ -6376,7 +7096,7 @@ static int rtw89_hw_scan_update_probe_req(struct rtw89_dev *rtwdev,  	int ret;  	for (i = 0; i < num; i++) { -		skb = ieee80211_probereq_get(rtwdev->hw, rtwvif_link->mac_addr, +		skb = ieee80211_probereq_get(rtwdev->hw, mac_addr,  					     req->ssids[i].ssid,  					     req->ssids[i].ssid_len,  					     req->ie_len); @@ -6393,10 +7113,10 @@ static int rtw89_hw_scan_update_probe_req(struct rtw89_dev *rtwdev,  	return 0;  } -static int rtw89_update_6ghz_rnr_chan(struct rtw89_dev *rtwdev, -				      struct ieee80211_scan_ies *ies, -				      struct cfg80211_scan_request *req, -				      struct rtw89_mac_chinfo *ch_info) +static int rtw89_update_6ghz_rnr_chan_ax(struct rtw89_dev *rtwdev, +					 struct ieee80211_scan_ies *ies, +					 struct cfg80211_scan_request *req, +					 struct rtw89_mac_chinfo_ax *ch_info)  {  	struct rtw89_vif_link *rtwvif_link = rtwdev->scan_info.scanning_vif;  	struct list_head *pkt_list = rtwdev->scan_info.pkt_list; @@ -6468,7 +7188,7 @@ out:  static void rtw89_pno_scan_add_chan_ax(struct rtw89_dev *rtwdev,  				       int chan_type, int ssid_num, -				       struct rtw89_mac_chinfo *ch_info) +				       struct rtw89_mac_chinfo_ax *ch_info)  {  	struct rtw89_wow_param *rtw_wow = &rtwdev->wow;  	struct rtw89_pktofld_info *info; @@ -6516,12 +7236,13 @@ static void rtw89_pno_scan_add_chan_ax(struct rtw89_dev *rtwdev,  	}  } -static void rtw89_hw_scan_add_chan(struct rtw89_dev *rtwdev, int chan_type, -				   int ssid_num, -				   struct rtw89_mac_chinfo *ch_info) +static void rtw89_hw_scan_add_chan_ax(struct rtw89_dev *rtwdev, int chan_type, +				      int ssid_num, +				      struct rtw89_mac_chinfo_ax *ch_info)  {  	struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info;  	struct rtw89_vif_link *rtwvif_link = rtwdev->scan_info.scanning_vif; +	const struct rtw89_hw_scan_extra_op *ext = &scan_info->extra_op;  	struct rtw89_vif *rtwvif = rtwvif_link->rtwvif;  	struct ieee80211_scan_ies *ies = rtwvif->scan_ies;  	struct cfg80211_scan_request *req = rtwvif->scan_req; @@ -6549,7 +7270,7 @@ static void rtw89_hw_scan_add_chan(struct rtw89_dev *rtwdev, int chan_type,  		}  	} -	ret = rtw89_update_6ghz_rnr_chan(rtwdev, ies, req, ch_info); +	ret = rtw89_update_6ghz_rnr_chan_ax(rtwdev, ies, req, ch_info);  	if (ret)  		rtw89_warn(rtwdev, "RNR fails: %d\n", ret); @@ -6592,6 +7313,15 @@ static void rtw89_hw_scan_add_chan(struct rtw89_dev *rtwdev, int chan_type,  	case RTW89_CHAN_ACTIVE:  		ch_info->pause_data = true;  		break; +	case RTW89_CHAN_EXTRA_OP: +		ch_info->central_ch = ext->chan.channel; +		ch_info->pri_ch = ext->chan.primary_channel; +		ch_info->ch_band = ext->chan.band_type; +		ch_info->bw = ext->chan.band_width; +		ch_info->tx_null = true; +		ch_info->num_pkt = 0; +		ch_info->macid_tx = true; +		break;  	default:  		rtw89_err(rtwdev, "Channel type out of bound\n");  	} @@ -6705,7 +7435,7 @@ int rtw89_pno_scan_add_chan_list_ax(struct rtw89_dev *rtwdev,  {  	struct rtw89_wow_param *rtw_wow = &rtwdev->wow;  	struct cfg80211_sched_scan_request *nd_config = rtw_wow->nd_config; -	struct rtw89_mac_chinfo	*ch_info, *tmp; +	struct rtw89_mac_chinfo_ax *ch_info, *tmp;  	struct ieee80211_channel *channel;  	struct list_head chan_list;  	int list_len; @@ -6739,7 +7469,7 @@ int rtw89_pno_scan_add_chan_list_ax(struct rtw89_dev *rtwdev,  		rtw89_pno_scan_add_chan_ax(rtwdev, type, nd_config->n_match_sets, ch_info);  		list_add_tail(&ch_info->list, &chan_list);  	} -	ret = rtw89_fw_h2c_scan_list_offload(rtwdev, list_len, &chan_list); +	ret = rtw89_fw_h2c_scan_list_offload_ax(rtwdev, list_len, &chan_list);  out:  	list_for_each_entry_safe(ch_info, tmp, &chan_list, list) { @@ -6750,24 +7480,59 @@ out:  	return ret;  } -int rtw89_hw_scan_add_chan_list_ax(struct rtw89_dev *rtwdev, -				   struct rtw89_vif_link *rtwvif_link, bool connected) +static int rtw89_hw_scan_add_op_types_ax(struct rtw89_dev *rtwdev, +					 enum rtw89_chan_type type, +					 struct list_head *chan_list, +					 struct cfg80211_scan_request *req, +					 int *off_chan_time)  { +	struct rtw89_mac_chinfo_ax *tmp; + +	tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); +	if (!tmp) +		return -ENOMEM; + +	switch (type) { +	case RTW89_CHAN_OPERATE: +		tmp->period = req->duration_mandatory ? +			      req->duration : RTW89_CHANNEL_TIME; +		*off_chan_time = 0; +		break; +	case RTW89_CHAN_EXTRA_OP: +		tmp->period = RTW89_CHANNEL_TIME_EXTRA_OP; +		/* still calc @off_chan_time for scan op */ +		*off_chan_time += tmp->period; +		break; +	default: +		kfree(tmp); +		return -EINVAL; +	} + +	rtw89_hw_scan_add_chan_ax(rtwdev, type, 0, tmp); +	list_add_tail(&tmp->list, chan_list); + +	return 0; +} + +int rtw89_hw_scan_prep_chan_list_ax(struct rtw89_dev *rtwdev, +				    struct rtw89_vif_link *rtwvif_link) +{ +	struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; +	const struct rtw89_hw_scan_extra_op *ext = &scan_info->extra_op;  	struct rtw89_vif *rtwvif = rtwvif_link->rtwvif;  	struct cfg80211_scan_request *req = rtwvif->scan_req; -	struct rtw89_mac_chinfo	*ch_info, *tmp; +	struct rtw89_mac_chinfo_ax *ch_info, *tmp;  	struct ieee80211_channel *channel;  	struct list_head chan_list;  	bool random_seq = req->flags & NL80211_SCAN_FLAG_RANDOM_SN; -	int list_len, off_chan_time = 0;  	enum rtw89_chan_type type; -	int ret = 0; +	int off_chan_time = 0; +	int ret;  	u32 idx;  	INIT_LIST_HEAD(&chan_list); -	for (idx = rtwdev->scan_info.last_chan_idx, list_len = 0; -	     idx < req->n_channels && list_len < RTW89_SCAN_LIST_LIMIT_AX; -	     idx++, list_len++) { + +	for (idx = 0; idx < req->n_channels; idx++) {  		channel = req->channels[idx];  		ch_info = kzalloc(sizeof(*ch_info), GFP_KERNEL);  		if (!ch_info) { @@ -6780,6 +7545,8 @@ int rtw89_hw_scan_add_chan_list_ax(struct rtw89_dev *rtwdev,  		else if (channel->band == NL80211_BAND_6GHZ)  			ch_info->period = RTW89_CHANNEL_TIME_6G +  					  RTW89_DWELL_TIME_6G; +		else if (rtwvif_link->wifi_role == RTW89_WIFI_ROLE_P2P_CLIENT) +			ch_info->period = RTW89_P2P_CHAN_TIME;  		else  			ch_info->period = RTW89_CHANNEL_TIME; @@ -6794,30 +7561,36 @@ int rtw89_hw_scan_add_chan_list_ax(struct rtw89_dev *rtwdev,  			type = RTW89_CHAN_DFS;  		else  			type = RTW89_CHAN_ACTIVE; -		rtw89_hw_scan_add_chan(rtwdev, type, req->n_ssids, ch_info); - -		if (connected && -		    off_chan_time + ch_info->period > RTW89_OFF_CHAN_TIME) { -			tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); -			if (!tmp) { -				ret = -ENOMEM; -				kfree(ch_info); -				goto out; -			} +		rtw89_hw_scan_add_chan_ax(rtwdev, type, req->n_ssids, ch_info); + +		if (!(scan_info->connected && +		      off_chan_time + ch_info->period > RTW89_OFF_CHAN_TIME)) +			goto next; + +		ret = rtw89_hw_scan_add_op_types_ax(rtwdev, RTW89_CHAN_OPERATE, +						    &chan_list, req, &off_chan_time); +		if (ret) { +			kfree(ch_info); +			goto out; +		} + +		if (!ext->set) +			goto next; -			type = RTW89_CHAN_OPERATE; -			tmp->period = req->duration_mandatory ? -				      req->duration : RTW89_CHANNEL_TIME; -			rtw89_hw_scan_add_chan(rtwdev, type, 0, tmp); -			list_add_tail(&tmp->list, &chan_list); -			off_chan_time = 0; -			list_len++; +		ret = rtw89_hw_scan_add_op_types_ax(rtwdev, RTW89_CHAN_EXTRA_OP, +						    &chan_list, req, &off_chan_time); +		if (ret) { +			kfree(ch_info); +			goto out;  		} + +next:  		list_add_tail(&ch_info->list, &chan_list);  		off_chan_time += ch_info->period;  	} -	rtwdev->scan_info.last_chan_idx = idx; -	ret = rtw89_fw_h2c_scan_list_offload(rtwdev, list_len, &chan_list); + +	list_splice_tail(&chan_list, &scan_info->chan_list); +	return 0;  out:  	list_for_each_entry_safe(ch_info, tmp, &chan_list, list) { @@ -6828,6 +7601,46 @@ out:  	return ret;  } +void rtw89_hw_scan_free_chan_list_ax(struct rtw89_dev *rtwdev) +{ +	struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; +	struct rtw89_mac_chinfo_ax *ch_info, *tmp; + +	list_for_each_entry_safe(ch_info, tmp, &scan_info->chan_list, list) { +		list_del(&ch_info->list); +		kfree(ch_info); +	} +} + +int rtw89_hw_scan_add_chan_list_ax(struct rtw89_dev *rtwdev, +				   struct rtw89_vif_link *rtwvif_link) +{ +	struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; +	struct rtw89_mac_chinfo_ax *ch_info, *tmp; +	unsigned int list_len = 0; +	struct list_head list; +	int ret; + +	INIT_LIST_HEAD(&list); + +	list_for_each_entry_safe(ch_info, tmp, &scan_info->chan_list, list) { +		list_move_tail(&ch_info->list, &list); + +		list_len++; +		if (list_len == RTW89_SCAN_LIST_LIMIT_AX) +			break; +	} + +	ret = rtw89_fw_h2c_scan_list_offload_ax(rtwdev, list_len, &list); + +	list_for_each_entry_safe(ch_info, tmp, &list, list) { +		list_del(&ch_info->list); +		kfree(ch_info); +	} + +	return ret; +} +  int rtw89_pno_scan_add_chan_list_be(struct rtw89_dev *rtwdev,  				    struct rtw89_vif_link *rtwvif_link)  { @@ -6881,25 +7694,24 @@ out:  	return ret;  } -int rtw89_hw_scan_add_chan_list_be(struct rtw89_dev *rtwdev, -				   struct rtw89_vif_link *rtwvif_link, bool connected) +int rtw89_hw_scan_prep_chan_list_be(struct rtw89_dev *rtwdev, +				    struct rtw89_vif_link *rtwvif_link)  { +	struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info;  	struct rtw89_vif *rtwvif = rtwvif_link->rtwvif;  	struct cfg80211_scan_request *req = rtwvif->scan_req;  	struct rtw89_mac_chinfo_be *ch_info, *tmp;  	struct ieee80211_channel *channel;  	struct list_head chan_list;  	enum rtw89_chan_type type; -	int list_len, ret;  	bool random_seq; +	int ret;  	u32 idx;  	random_seq = !!(req->flags & NL80211_SCAN_FLAG_RANDOM_SN);  	INIT_LIST_HEAD(&chan_list); -	for (idx = rtwdev->scan_info.last_chan_idx, list_len = 0; -	     idx < req->n_channels && list_len < RTW89_SCAN_LIST_LIMIT_BE; -	     idx++, list_len++) { +	for (idx = 0; idx < req->n_channels; idx++) {  		channel = req->channels[idx];  		ch_info = kzalloc(sizeof(*ch_info), GFP_KERNEL);  		if (!ch_info) { @@ -6911,6 +7723,8 @@ int rtw89_hw_scan_add_chan_list_be(struct rtw89_dev *rtwdev,  			ch_info->period = req->duration;  		else if (channel->band == NL80211_BAND_6GHZ)  			ch_info->period = RTW89_CHANNEL_TIME_6G + RTW89_DWELL_TIME_6G; +		else if (rtwvif_link->wifi_role == RTW89_WIFI_ROLE_P2P_CLIENT) +			ch_info->period = RTW89_P2P_CHAN_TIME;  		else  			ch_info->period = RTW89_CHANNEL_TIME; @@ -6929,9 +7743,8 @@ int rtw89_hw_scan_add_chan_list_be(struct rtw89_dev *rtwdev,  		list_add_tail(&ch_info->list, &chan_list);  	} -	rtwdev->scan_info.last_chan_idx = idx; -	ret = rtw89_fw_h2c_scan_list_offload_be(rtwdev, list_len, &chan_list, -						rtwvif_link); +	list_splice_tail(&chan_list, &scan_info->chan_list); +	return 0;  out:  	list_for_each_entry_safe(ch_info, tmp, &chan_list, list) { @@ -6942,13 +7755,55 @@ out:  	return ret;  } +void rtw89_hw_scan_free_chan_list_be(struct rtw89_dev *rtwdev) +{ +	struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; +	struct rtw89_mac_chinfo_be *ch_info, *tmp; + +	list_for_each_entry_safe(ch_info, tmp, &scan_info->chan_list, list) { +		list_del(&ch_info->list); +		kfree(ch_info); +	} +} + +int rtw89_hw_scan_add_chan_list_be(struct rtw89_dev *rtwdev, +				   struct rtw89_vif_link *rtwvif_link) +{ +	struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; +	struct rtw89_mac_chinfo_be *ch_info, *tmp; +	unsigned int list_len = 0; +	struct list_head list; +	int ret; + +	INIT_LIST_HEAD(&list); + +	list_for_each_entry_safe(ch_info, tmp, &scan_info->chan_list, list) { +		list_move_tail(&ch_info->list, &list); + +		list_len++; +		if (list_len == RTW89_SCAN_LIST_LIMIT_BE) +			break; +	} + +	ret = rtw89_fw_h2c_scan_list_offload_be(rtwdev, list_len, &list, +						rtwvif_link); + +	list_for_each_entry_safe(ch_info, tmp, &list, list) { +		list_del(&ch_info->list); +		kfree(ch_info); +	} + +	return ret; +} +  static int rtw89_hw_scan_prehandle(struct rtw89_dev *rtwdev, -				   struct rtw89_vif_link *rtwvif_link, bool connected) +				   struct rtw89_vif_link *rtwvif_link, +				   const u8 *mac_addr)  {  	const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;  	int ret; -	ret = rtw89_hw_scan_update_probe_req(rtwdev, rtwvif_link); +	ret = rtw89_hw_scan_update_probe_req(rtwdev, rtwvif_link, mac_addr);  	if (ret) {  #if defined(__linux__)  		rtw89_err(rtwdev, "Update probe request failed\n"); @@ -6957,40 +7812,204 @@ static int rtw89_hw_scan_prehandle(struct rtw89_dev *rtwdev,  #endif  		goto out;  	} -	ret = mac->add_chan_list(rtwdev, rtwvif_link, connected); +	ret = mac->prep_chan_list(rtwdev, rtwvif_link);  out:  	return ret;  } -void rtw89_hw_scan_start(struct rtw89_dev *rtwdev, -			 struct rtw89_vif_link *rtwvif_link, -			 struct ieee80211_scan_request *scan_req) +static void rtw89_hw_scan_update_link_beacon_noa(struct rtw89_dev *rtwdev, +						 struct rtw89_vif_link *rtwvif_link, +						 u16 tu, bool scan) +{ +	struct ieee80211_p2p_noa_desc noa_desc = {}; +	struct ieee80211_bss_conf *bss_conf; +	u16 beacon_int; +	u64 tsf; +	int ret; + +	rcu_read_lock(); + +	bss_conf = rtw89_vif_rcu_dereference_link(rtwvif_link, true); +	beacon_int = bss_conf->beacon_int; + +	rcu_read_unlock(); + +	tu += beacon_int * 3; +	if (rtwdev->chip->chip_gen == RTW89_CHIP_AX) +		rtwdev->scan_info.delay = ieee80211_tu_to_usec(beacon_int * 3) / 1000; + +	ret = rtw89_mac_port_get_tsf(rtwdev, rtwvif_link, &tsf); +	if (ret) { +		rtw89_warn(rtwdev, "%s: failed to get tsf\n", __func__); +		return; +	} + +	noa_desc.start_time = cpu_to_le32(tsf); +	if (rtwdev->chip->chip_gen == RTW89_CHIP_AX) { +		noa_desc.interval = cpu_to_le32(ieee80211_tu_to_usec(tu)); +		noa_desc.duration = cpu_to_le32(ieee80211_tu_to_usec(tu)); +		noa_desc.count = 1; +	} else { +		noa_desc.duration = cpu_to_le32(ieee80211_tu_to_usec(20000)); +		noa_desc.interval = cpu_to_le32(ieee80211_tu_to_usec(20000)); +		noa_desc.count = 255; +	} + +	rtw89_p2p_noa_renew(rtwvif_link); +	if (scan) +		rtw89_p2p_noa_append(rtwvif_link, &noa_desc); + +	rtw89_chip_h2c_update_beacon(rtwdev, rtwvif_link); +} + +static void rtw89_hw_scan_update_beacon_noa(struct rtw89_dev *rtwdev, bool scan) +{ +	const struct rtw89_entity_mgnt *mgnt = &rtwdev->hal.entity_mgnt; +	const struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; +	const struct rtw89_chip_info *chip = rtwdev->chip; +	struct rtw89_mac_chinfo_ax *chinfo_ax; +	struct rtw89_mac_chinfo_be *chinfo_be; +	struct rtw89_vif_link *rtwvif_link; +	struct list_head *pos, *tmp; +	struct ieee80211_vif *vif; +	struct rtw89_vif *rtwvif; +	u16 tu = 0; + +	lockdep_assert_wiphy(rtwdev->hw->wiphy); + +	if (!scan) +		goto update; + +	list_for_each_safe(pos, tmp, &scan_info->chan_list) { +		switch (chip->chip_gen) { +		case RTW89_CHIP_AX: +			chinfo_ax = list_entry(pos, typeof(*chinfo_ax), list); +			tu += chinfo_ax->period; +			break; +		case RTW89_CHIP_BE: +			chinfo_be = list_entry(pos, typeof(*chinfo_be), list); +			tu += chinfo_be->period; +			break; +		default: +			rtw89_warn(rtwdev, "%s: invalid chip gen %d\n", +				   __func__, chip->chip_gen); +			return; +		} +	} + +	if (unlikely(tu == 0)) { +		rtw89_debug(rtwdev, RTW89_DBG_HW_SCAN, +			    "%s: cannot estimate needed TU\n", __func__); +		return; +	} + +update: +	list_for_each_entry(rtwvif, &mgnt->active_list, mgnt_entry) { +		unsigned int link_id; + +		vif = rtwvif_to_vif(rtwvif); +		if (vif->type != NL80211_IFTYPE_AP || !vif->p2p) +			continue; + +		rtw89_vif_for_each_link(rtwvif, rtwvif_link, link_id) +			rtw89_hw_scan_update_link_beacon_noa(rtwdev, rtwvif_link, +							     tu, scan); +	} +} + +static void rtw89_hw_scan_set_extra_op_info(struct rtw89_dev *rtwdev, +					    struct rtw89_vif *scan_rtwvif, +					    const struct rtw89_chan *scan_op) +{ +	struct rtw89_entity_mgnt *mgnt = &rtwdev->hal.entity_mgnt; +	struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; +	struct rtw89_hw_scan_extra_op *ext = &scan_info->extra_op; +	struct rtw89_vif *tmp; + +	ext->set = false; +	if (!RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD_EXTRA_OP, &rtwdev->fw)) +		return; + +	list_for_each_entry(tmp, &mgnt->active_list, mgnt_entry) { +		const struct rtw89_chan *tmp_chan; +		struct rtw89_vif_link *tmp_link; + +		if (tmp == scan_rtwvif) +			continue; + +		tmp_link = rtw89_vif_get_link_inst(tmp, 0); +		if (unlikely(!tmp_link)) { +			rtw89_debug(rtwdev, RTW89_DBG_HW_SCAN, +				    "hw scan: no HW-0 link for extra op\n"); +			continue; +		} + +		tmp_chan = rtw89_chan_get(rtwdev, tmp_link->chanctx_idx); +		*ext = (struct rtw89_hw_scan_extra_op){ +			.set = true, +			.macid = tmp_link->mac_id, +			.port = tmp_link->port, +			.chan = *tmp_chan, +			.rtwvif_link = tmp_link, +		}; + +		rtw89_debug(rtwdev, RTW89_DBG_HW_SCAN, +			    "hw scan: extra op: center %d primary %d\n", +			    ext->chan.channel, ext->chan.primary_channel); +		break; +	} +} + +int rtw89_hw_scan_start(struct rtw89_dev *rtwdev, +			struct rtw89_vif_link *rtwvif_link, +			struct ieee80211_scan_request *scan_req)  {  	const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; +	enum rtw89_entity_mode mode = rtw89_get_entity_mode(rtwdev);  	struct cfg80211_scan_request *req = &scan_req->req;  	const struct rtw89_chan *chan = rtw89_chan_get(rtwdev,  						       rtwvif_link->chanctx_idx);  	struct rtw89_vif *rtwvif = rtwvif_link->rtwvif; +	struct rtw89_chanctx_pause_parm pause_parm = { +		.rsn = RTW89_CHANCTX_PAUSE_REASON_HW_SCAN, +		.trigger = rtwvif_link, +	};  	u32 rx_fltr = rtwdev->hal.rx_fltr;  	u8 mac_addr[ETH_ALEN];  	u32 reg; +	int ret;  	/* clone op and keep it during scan */  	rtwdev->scan_info.op_chan = *chan; +	rtw89_debug(rtwdev, RTW89_DBG_HW_SCAN, +		    "hw scan: op: center %d primary %d\n", +		    chan->channel, chan->primary_channel); + +	rtw89_hw_scan_set_extra_op_info(rtwdev, rtwvif, chan); + +	rtwdev->scan_info.connected = rtw89_is_any_vif_connected_or_connecting(rtwdev);  	rtwdev->scan_info.scanning_vif = rtwvif_link; -	rtwdev->scan_info.last_chan_idx = 0;  	rtwdev->scan_info.abort = false; +	rtwdev->scan_info.delay = 0;  	rtwvif->scan_ies = &scan_req->ies;  	rtwvif->scan_req = req; -	ieee80211_stop_queues(rtwdev->hw); -	rtw89_mac_port_cfg_rx_sync(rtwdev, rtwvif_link, false);  	if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)  		get_random_mask_addr(mac_addr, req->mac_addr,  				     req->mac_addr_mask);  	else  		ether_addr_copy(mac_addr, rtwvif_link->mac_addr); + +	ret = rtw89_hw_scan_prehandle(rtwdev, rtwvif_link, mac_addr); +	if (ret) { +		rtw89_hw_scan_cleanup(rtwdev, rtwvif_link); +		return ret; +	} + +	ieee80211_stop_queues(rtwdev->hw); +	rtw89_mac_port_cfg_rx_sync(rtwdev, rtwvif_link, false); +  	rtw89_core_scan_start(rtwdev, rtwvif_link, mac_addr, true);  	rx_fltr &= ~B_AX_A_BCN_CHK_EN; @@ -7000,7 +8019,13 @@ void rtw89_hw_scan_start(struct rtw89_dev *rtwdev,  	reg = rtw89_mac_reg_by_idx(rtwdev, mac->rx_fltr, rtwvif_link->mac_idx);  	rtw89_write32_mask(rtwdev, reg, B_AX_RX_FLTR_CFG_MASK, rx_fltr); -	rtw89_chanctx_pause(rtwdev, RTW89_CHANCTX_PAUSE_REASON_HW_SCAN); +	rtw89_chanctx_pause(rtwdev, &pause_parm); +	rtw89_phy_dig_suspend(rtwdev); + +	if (mode == RTW89_ENTITY_MODE_MCC) +		rtw89_hw_scan_update_beacon_noa(rtwdev, true); + +	return 0;  }  struct rtw89_hw_scan_complete_cb_data { @@ -7011,20 +8036,17 @@ struct rtw89_hw_scan_complete_cb_data {  static int rtw89_hw_scan_complete_cb(struct rtw89_dev *rtwdev, void *data)  {  	const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; -	struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; +	enum rtw89_entity_mode mode = rtw89_get_entity_mode(rtwdev);  	struct rtw89_hw_scan_complete_cb_data *cb_data = data;  	struct rtw89_vif_link *rtwvif_link = cb_data->rtwvif_link;  	struct cfg80211_scan_info info = {  		.aborted = cb_data->aborted,  	}; -	struct rtw89_vif *rtwvif;  	u32 reg;  	if (!rtwvif_link)  		return -EINVAL; -	rtwvif = rtwvif_link->rtwvif; -  	reg = rtw89_mac_reg_by_idx(rtwdev, mac->rx_fltr, rtwvif_link->mac_idx);  	rtw89_write32_mask(rtwdev, reg, B_AX_RX_FLTR_CFG_MASK, rtwdev->hal.rx_fltr); @@ -7033,13 +8055,12 @@ static int rtw89_hw_scan_complete_cb(struct rtw89_dev *rtwdev, void *data)  	ieee80211_wake_queues(rtwdev->hw);  	rtw89_mac_port_cfg_rx_sync(rtwdev, rtwvif_link, true);  	rtw89_mac_enable_beacon_for_ap_vifs(rtwdev, true); +	rtw89_phy_dig_resume(rtwdev, true); -	rtw89_release_pkt_list(rtwdev); -	rtwvif->scan_req = NULL; -	rtwvif->scan_ies = NULL; -	scan_info->last_chan_idx = 0; -	scan_info->scanning_vif = NULL; -	scan_info->abort = false; +	rtw89_hw_scan_cleanup(rtwdev, rtwvif_link); + +	if (mode == RTW89_ENTITY_MODE_MCC) +		rtw89_hw_scan_update_beacon_noa(rtwdev, false);  	return 0;  } @@ -7107,6 +8128,8 @@ int rtw89_hw_scan_offload(struct rtw89_dev *rtwdev,  			  bool enable)  {  	const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; +	struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; +	const struct rtw89_hw_scan_extra_op *ext = &scan_info->extra_op;  	struct rtw89_scan_option opt = {0};  	bool connected;  	int ret = 0; @@ -7114,11 +8137,12 @@ int rtw89_hw_scan_offload(struct rtw89_dev *rtwdev,  	if (!rtwvif_link)  		return -EINVAL; -	connected = rtw89_is_any_vif_connected_or_connecting(rtwdev); +	connected = rtwdev->scan_info.connected;  	opt.enable = enable;  	opt.target_ch_mode = connected; +	opt.delay = rtwdev->scan_info.delay;  	if (enable) { -		ret = rtw89_hw_scan_prehandle(rtwdev, rtwvif_link, connected); +		ret = mac->add_chan_list(rtwdev, rtwvif_link);  		if (ret)  			goto out;  	} @@ -7130,49 +8154,62 @@ int rtw89_hw_scan_offload(struct rtw89_dev *rtwdev,  		opt.num_macc_role = 0;  		opt.mlo_mode = rtwdev->mlo_dbcc_mode;  		opt.num_opch = connected ? 1 : 0; +		if (connected && ext->set) +			opt.num_opch++; +  		opt.opch_end = connected ? 0 : RTW89_CHAN_INVALID;  	} -	ret = mac->scan_offload(rtwdev, &opt, rtwvif_link, false); +	ret = rtw89_mac_scan_offload(rtwdev, &opt, rtwvif_link, false); +  out:  	return ret;  } -#define H2C_FW_CPU_EXCEPTION_LEN 4 -#define H2C_FW_CPU_EXCEPTION_TYPE_DEF 0x5566 +#define H2C_FW_CPU_EXCEPTION_TYPE_0 0x5566 +#define H2C_FW_CPU_EXCEPTION_TYPE_1 0x0  int rtw89_fw_h2c_trigger_cpu_exception(struct rtw89_dev *rtwdev)  { +	struct rtw89_h2c_trig_cpu_except *h2c; +	u32 cpu_exception_type_def; +	u32 len = sizeof(*h2c);  	struct sk_buff *skb;  	int ret; -	skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_FW_CPU_EXCEPTION_LEN); +	if (RTW89_CHK_FW_FEATURE(CRASH_TRIGGER_TYPE_1, &rtwdev->fw)) +		cpu_exception_type_def = H2C_FW_CPU_EXCEPTION_TYPE_1; +	else if (RTW89_CHK_FW_FEATURE(CRASH_TRIGGER_TYPE_0, &rtwdev->fw)) +		cpu_exception_type_def = H2C_FW_CPU_EXCEPTION_TYPE_0; +	else +		return -EOPNOTSUPP; + +	skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len);  	if (!skb) {  		rtw89_err(rtwdev,  			  "failed to alloc skb for fw cpu exception\n");  		return -ENOMEM;  	} -	skb_put(skb, H2C_FW_CPU_EXCEPTION_LEN); -	RTW89_SET_FWCMD_CPU_EXCEPTION_TYPE(skb->data, -					   H2C_FW_CPU_EXCEPTION_TYPE_DEF); +	skb_put(skb, len); +	h2c = (struct rtw89_h2c_trig_cpu_except *)skb->data; + +	h2c->w0 = le32_encode_bits(cpu_exception_type_def, +				   RTW89_H2C_CPU_EXCEPTION_TYPE);  	rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,  			      H2C_CAT_TEST,  			      H2C_CL_FW_STATUS_TEST,  			      H2C_FUNC_CPU_EXCEPTION, 0, 0, -			      H2C_FW_CPU_EXCEPTION_LEN); +			      len);  	ret = rtw89_h2c_tx(rtwdev, skb, false);  	if (ret) {  		rtw89_err(rtwdev, "failed to send h2c\n"); -		goto fail; +		dev_kfree_skb_any(skb); +		return ret;  	}  	return 0; - -fail: -	dev_kfree_skb_any(skb); -	return ret;  }  #define H2C_PKT_DROP_LEN 24 @@ -8490,6 +9527,47 @@ int rtw89_fw_h2c_ap_info_refcount(struct rtw89_dev *rtwdev, bool en)  	return 0;  } +int rtw89_fw_h2c_mlo_link_cfg(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, +			      bool enable) +{ +	struct rtw89_wait_info *wait = &rtwdev->mlo.wait; +	struct rtw89_h2c_mlo_link_cfg *h2c; +	u8 mac_id = rtwvif_link->mac_id; +	u32 len = sizeof(*h2c); +	struct sk_buff *skb; +	unsigned int cond; +	int ret; + +	skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); +	if (!skb) { +		rtw89_err(rtwdev, "failed to alloc skb for mlo link cfg\n"); +		return -ENOMEM; +	} + +	skb_put(skb, len); +	h2c = (struct rtw89_h2c_mlo_link_cfg *)skb->data; + +	h2c->w0 = le32_encode_bits(mac_id, RTW89_H2C_MLO_LINK_CFG_W0_MACID) | +		  le32_encode_bits(enable, RTW89_H2C_MLO_LINK_CFG_W0_OPTION); + +	rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, +			      H2C_CAT_MAC, +			      H2C_CL_MLO, +			      H2C_FUNC_MLO_LINK_CFG, 0, 0, +			      len); + +	cond = RTW89_MLO_WAIT_COND(mac_id, H2C_FUNC_MLO_LINK_CFG); + +	ret = rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); +	if (ret) { +		rtw89_err(rtwdev, "mlo link cfg (%s link id %u) failed: %d\n", +			  str_enable_disable(enable), rtwvif_link->link_id, ret); +		return ret; +	} + +	return 0; +} +  static bool __fw_txpwr_entry_zero_ext(const void *ext_ptr, u8 ext_len)  {  	static const u8 zeros[U8_MAX] = {}; @@ -8927,6 +10005,26 @@ void rtw89_fw_load_tx_shape_lmt_ru(struct rtw89_tx_shape_lmt_ru_data *data)  	}  } +static bool rtw89_fw_has_da_txpwr_table(struct rtw89_dev *rtwdev, +					const struct rtw89_rfe_parms *parms) +{ +	const struct rtw89_chip_info *chip = rtwdev->chip; + +	if (chip->support_bands & BIT(NL80211_BAND_2GHZ) && +	    !(parms->rule_da_2ghz.lmt && parms->rule_da_2ghz.lmt_ru)) +		return false; + +	if (chip->support_bands & BIT(NL80211_BAND_5GHZ) && +	    !(parms->rule_da_5ghz.lmt && parms->rule_da_5ghz.lmt_ru)) +		return false; + +	if (chip->support_bands & BIT(NL80211_BAND_6GHZ) && +	    !(parms->rule_da_6ghz.lmt && parms->rule_da_6ghz.lmt_ru)) +		return false; + +	return true; +} +  const struct rtw89_rfe_parms *  rtw89_load_rfe_data_from_fw(struct rtw89_dev *rtwdev,  			    const struct rtw89_rfe_parms *init) @@ -8963,6 +10061,21 @@ rtw89_load_rfe_data_from_fw(struct rtw89_dev *rtwdev,  		parms->rule_6ghz.lmt = &rfe_data->lmt_6ghz.v;  	} +	if (rtw89_txpwr_conf_valid(&rfe_data->da_lmt_2ghz.conf)) { +		rtw89_fw_load_txpwr_lmt_2ghz(&rfe_data->da_lmt_2ghz); +		parms->rule_da_2ghz.lmt = &rfe_data->da_lmt_2ghz.v; +	} + +	if (rtw89_txpwr_conf_valid(&rfe_data->da_lmt_5ghz.conf)) { +		rtw89_fw_load_txpwr_lmt_5ghz(&rfe_data->da_lmt_5ghz); +		parms->rule_da_5ghz.lmt = &rfe_data->da_lmt_5ghz.v; +	} + +	if (rtw89_txpwr_conf_valid(&rfe_data->da_lmt_6ghz.conf)) { +		rtw89_fw_load_txpwr_lmt_6ghz(&rfe_data->da_lmt_6ghz); +		parms->rule_da_6ghz.lmt = &rfe_data->da_lmt_6ghz.v; +	} +  	if (rtw89_txpwr_conf_valid(&rfe_data->lmt_ru_2ghz.conf)) {  		rtw89_fw_load_txpwr_lmt_ru_2ghz(&rfe_data->lmt_ru_2ghz);  		parms->rule_2ghz.lmt_ru = &rfe_data->lmt_ru_2ghz.v; @@ -8978,6 +10091,21 @@ rtw89_load_rfe_data_from_fw(struct rtw89_dev *rtwdev,  		parms->rule_6ghz.lmt_ru = &rfe_data->lmt_ru_6ghz.v;  	} +	if (rtw89_txpwr_conf_valid(&rfe_data->da_lmt_ru_2ghz.conf)) { +		rtw89_fw_load_txpwr_lmt_ru_2ghz(&rfe_data->da_lmt_ru_2ghz); +		parms->rule_da_2ghz.lmt_ru = &rfe_data->da_lmt_ru_2ghz.v; +	} + +	if (rtw89_txpwr_conf_valid(&rfe_data->da_lmt_ru_5ghz.conf)) { +		rtw89_fw_load_txpwr_lmt_ru_5ghz(&rfe_data->da_lmt_ru_5ghz); +		parms->rule_da_5ghz.lmt_ru = &rfe_data->da_lmt_ru_5ghz.v; +	} + +	if (rtw89_txpwr_conf_valid(&rfe_data->da_lmt_ru_6ghz.conf)) { +		rtw89_fw_load_txpwr_lmt_ru_6ghz(&rfe_data->da_lmt_ru_6ghz); +		parms->rule_da_6ghz.lmt_ru = &rfe_data->da_lmt_ru_6ghz.v; +	} +  	if (rtw89_txpwr_conf_valid(&rfe_data->tx_shape_lmt.conf)) {  		rtw89_fw_load_tx_shape_lmt(&rfe_data->tx_shape_lmt);  		parms->tx_shape.lmt = &rfe_data->tx_shape_lmt.v; @@ -8988,5 +10116,7 @@ rtw89_load_rfe_data_from_fw(struct rtw89_dev *rtwdev,  		parms->tx_shape.lmt_ru = &rfe_data->tx_shape_lmt_ru.v;  	} +	parms->has_da = rtw89_fw_has_da_txpwr_table(rtwdev, parms); +  	return parms;  } diff --git a/sys/contrib/dev/rtw89/fw.h b/sys/contrib/dev/rtw89/fw.h index de0330bd6f59..4bd7d1e94e6a 100644 --- a/sys/contrib/dev/rtw89/fw.h +++ b/sys/contrib/dev/rtw89/fw.h @@ -87,6 +87,9 @@ struct rtw89_c2hreg_phycap {  #define RTW89_C2HREG_AOAC_RPT_2_W3_IGTK_IPN_IV_6 GENMASK(7, 0)  #define RTW89_C2HREG_AOAC_RPT_2_W3_IGTK_IPN_IV_7 GENMASK(15, 8) +#define RTW89_C2HREG_PS_LEAVE_ACK_RET GENMASK(7, 0) +#define RTW89_C2HREG_PS_LEAVE_ACK_MACID GENMASK(31, 16) +  struct rtw89_h2creg_hdr {  	u32 w0;  }; @@ -112,6 +115,8 @@ struct rtw89_h2creg_sch_tx_en {  #define RTW89_C2HREG_HDR_LEN 2  #define RTW89_H2CREG_HDR_LEN 2  #define RTW89_C2H_TIMEOUT 1000000 +#define RTW89_C2H_TIMEOUT_USB 4000 +  struct rtw89_mac_c2h_info {  	u8 id;  	u8 content_len; @@ -154,6 +159,7 @@ enum rtw89_mac_c2h_type {  	RTW89_FWCMD_C2HREG_FUNC_TX_PAUSE_RPT,  	RTW89_FWCMD_C2HREG_FUNC_WOW_CPUIO_RX_ACK = 0xA,  	RTW89_FWCMD_C2HREG_FUNC_PHY_CAP_PART1 = 0xC, +	RTW89_FWCMD_C2HREG_FUNC_PS_LEAVE_ACK = 0xD,  	RTW89_FWCMD_C2HREG_FUNC_NULL = 0xFF,  }; @@ -199,6 +205,7 @@ enum rtw89_fw_log_comp {  	RTW89_FW_LOG_COMP_TWT,  	RTW89_FW_LOG_COMP_RF,  	RTW89_FW_LOG_COMP_MCC = 20, +	RTW89_FW_LOG_COMP_MLO = 26,  	RTW89_FW_LOG_COMP_SCAN = 28,  }; @@ -236,6 +243,7 @@ enum rtw89_chan_type {  	RTW89_CHAN_OPERATE = 0,  	RTW89_CHAN_ACTIVE,  	RTW89_CHAN_DFS, +	RTW89_CHAN_EXTRA_OP,  };  enum rtw89_p2pps_action { @@ -315,8 +323,10 @@ struct rtw89_fw_macid_pause_sleep_grp {  #define RTW89_H2C_MAX_SIZE 2048  #define RTW89_CHANNEL_TIME 45  #define RTW89_CHANNEL_TIME_6G 20 +#define RTW89_CHANNEL_TIME_EXTRA_OP 30  #define RTW89_DFS_CHAN_TIME 105  #define RTW89_OFF_CHAN_TIME 100 +#define RTW89_P2P_CHAN_TIME 105  #define RTW89_DWELL_TIME 20  #define RTW89_DWELL_TIME_6G 10  #define RTW89_SCAN_WIDTH 0 @@ -333,9 +343,9 @@ struct rtw89_fw_macid_pause_sleep_grp {  #define RTW89_SCAN_LIST_LIMIT_AX RTW89_SCAN_LIST_LIMIT(RTW89_MAC_CHINFO_SIZE)  #define RTW89_SCAN_LIST_LIMIT_BE RTW89_SCAN_LIST_LIMIT(RTW89_MAC_CHINFO_SIZE_BE) -#define RTW89_BCN_LOSS_CNT 10 +#define RTW89_BCN_LOSS_CNT 60 -struct rtw89_mac_chinfo { +struct rtw89_mac_chinfo_ax {  	u8 period;  	u8 dwell_time;  	u8 central_ch; @@ -351,7 +361,8 @@ struct rtw89_mac_chinfo {  	u8 tx_null:1;  	u8 rand_seq_num:1;  	u8 cfg_tx_pwr:1; -	u8 rsvd0: 4; +	u8 macid_tx: 1; +	u8 rsvd0: 3;  	u8 pkt_id[RTW89_SCANOFLD_MAX_SSID];  	u16 tx_pwr_idx;  	u8 rsvd1; @@ -664,6 +675,11 @@ struct rtw89_fw_mss_pool_hdr {  union rtw89_fw_section_mssc_content {  	struct { +		u8 pad[0x20]; +		u8 bit_in_chip_list; +		u8 ver; +	} __packed blacklist; +	struct {  		u8 pad[58];  		__le32 v;  	} __packed sb_sel_ver; @@ -673,6 +689,13 @@ union rtw89_fw_section_mssc_content {  	} __packed key_sign_len;  } __packed; +struct rtw89_fw_blacklist { +	u8 ver; +	u8 list[32]; +}; + +extern const struct rtw89_fw_blacklist rtw89_fw_blacklist_default; +  static inline void SET_CTRL_INFO_MACID(void *table, u32 val)  {  	le32p_replace_bits((__le32 *)(table) + 0, val, GENMASK(6, 0)); @@ -1579,25 +1602,17 @@ struct rtw89_h2c_bcn_upd_be {  #define RTW89_H2C_BCN_UPD_BE_W7_ECSA_OFST GENMASK(30, 16)  #define RTW89_H2C_BCN_UPD_BE_W7_PROTECTION_KEY_ID BIT(31) -static inline void SET_FWROLE_MAINTAIN_MACID(void *h2c, u32 val) -{ -	le32p_replace_bits((__le32 *)h2c, val, GENMASK(7, 0)); -} - -static inline void SET_FWROLE_MAINTAIN_SELF_ROLE(void *h2c, u32 val) -{ -	le32p_replace_bits((__le32 *)h2c, val, GENMASK(9, 8)); -} - -static inline void SET_FWROLE_MAINTAIN_UPD_MODE(void *h2c, u32 val) -{ -	le32p_replace_bits((__le32 *)h2c, val, GENMASK(12, 10)); -} +struct rtw89_h2c_role_maintain { +	__le32 w0; +}; -static inline void SET_FWROLE_MAINTAIN_WIFI_ROLE(void *h2c, u32 val) -{ -	le32p_replace_bits((__le32 *)h2c, val, GENMASK(16, 13)); -} +#define RTW89_H2C_ROLE_MAINTAIN_W0_MACID GENMASK(7, 0) +#define RTW89_H2C_ROLE_MAINTAIN_W0_SELF_ROLE GENMASK(9, 8) +#define RTW89_H2C_ROLE_MAINTAIN_W0_UPD_MODE GENMASK(12, 10) +#define RTW89_H2C_ROLE_MAINTAIN_W0_WIFI_ROLE GENMASK(16, 13) +#define RTW89_H2C_ROLE_MAINTAIN_W0_BAND GENMASK(18, 17) +#define RTW89_H2C_ROLE_MAINTAIN_W0_PORT GENMASK(21, 19) +#define RTW89_H2C_ROLE_MAINTAIN_W0_MACID_EXT GENMASK(31, 24)  enum rtw89_fw_sta_type { /* value of RTW89_H2C_JOININFO_W1_STA_TYPE */  	RTW89_FW_N_AC_STA = 0, @@ -1632,6 +1647,8 @@ struct rtw89_h2c_join_v1 {  #define RTW89_H2C_JOININFO_W1_IS_MLD BIT(3)  #define RTW89_H2C_JOININFO_W1_MAIN_MACID GENMASK(11, 4)  #define RTW89_H2C_JOININFO_W1_MLO_MODE BIT(12) +#define RTW89_H2C_JOININFO_MLO_MODE_MLMR 0 +#define RTW89_H2C_JOININFO_MLO_MODE_MLSR 1  #define RTW89_H2C_JOININFO_W1_EMLSR_CAB BIT(13)  #define RTW89_H2C_JOININFO_W1_NSTR_EN BIT(14)  #define RTW89_H2C_JOININFO_W1_INIT_PWR_STATE BIT(15) @@ -1801,23 +1818,28 @@ struct rtw89_h2c_lps_ch_info {  struct rtw89_h2c_lps_ml_cmn_info {  	u8 fmt_id; -	u8 rsvd0[3]; +	u8 rfe_type; +	u8 rsvd0[2];  	__le32 mlo_dbcc_mode; -	u8 central_ch[RTW89_PHY_MAX]; -	u8 pri_ch[RTW89_PHY_MAX]; -	u8 bw[RTW89_PHY_MAX]; -	u8 band[RTW89_PHY_MAX]; -	u8 bcn_rate_type[RTW89_PHY_MAX]; +	u8 central_ch[RTW89_PHY_NUM]; +	u8 pri_ch[RTW89_PHY_NUM]; +	u8 bw[RTW89_PHY_NUM]; +	u8 band[RTW89_PHY_NUM]; +	u8 bcn_rate_type[RTW89_PHY_NUM];  	u8 rsvd1[2]; -	__le16 tia_gain[RTW89_PHY_MAX][TIA_GAIN_NUM]; -	u8 lna_gain[RTW89_PHY_MAX][LNA_GAIN_NUM]; +	__le16 tia_gain[RTW89_PHY_NUM][TIA_GAIN_NUM]; +	u8 lna_gain[RTW89_PHY_NUM][LNA_GAIN_NUM];  	u8 rsvd2[2]; +	u8 tia_lna_op1db[RTW89_PHY_NUM][LNA_GAIN_NUM + 1]; +	u8 lna_op1db[RTW89_PHY_NUM][LNA_GAIN_NUM]; +	u8 dup_bcn_ofst[RTW89_PHY_NUM];  } __packed; -static inline void RTW89_SET_FWCMD_CPU_EXCEPTION_TYPE(void *cmd, u32 val) -{ -	le32p_replace_bits((__le32 *)cmd, val, GENMASK(31, 0)); -} +struct rtw89_h2c_trig_cpu_except { +	__le32 w0; +} __packed; + +#define RTW89_H2C_CPU_EXCEPTION_TYPE GENMASK(31, 0)  static inline void RTW89_SET_FWCMD_PKT_DROP_SEL(void *cmd, u32 val)  { @@ -2236,6 +2258,11 @@ struct rtw89_h2c_cxrole_v8 {  	struct rtw89_btc_wl_role_info_v8_u32 _u32;  } __packed; +struct rtw89_h2c_cxosi { +	struct rtw89_h2c_cxhdr_v7 hdr; +	struct rtw89_btc_fbtc_outsrc_set_info osi; +} __packed; +  struct rtw89_h2c_cxinit {  	struct rtw89_h2c_cxhdr hdr;  	u8 ant_type; @@ -2663,6 +2690,7 @@ struct rtw89_h2c_chinfo_elem {  #define RTW89_H2C_CHINFO_W1_TX_NULL BIT(25)  #define RTW89_H2C_CHINFO_W1_RANDOM BIT(26)  #define RTW89_H2C_CHINFO_W1_CFG_TX BIT(27) +#define RTW89_H2C_CHINFO_W1_MACID_TX BIT(29)  #define RTW89_H2C_CHINFO_W2_PKT0 GENMASK(7, 0)  #define RTW89_H2C_CHINFO_W2_PKT1 GENMASK(15, 8)  #define RTW89_H2C_CHINFO_W2_PKT2 GENMASK(23, 16) @@ -2762,6 +2790,7 @@ struct rtw89_h2c_scanofld {  #define RTW89_H2C_SCANOFLD_W2_SLOW_PD GENMASK(23, 16)  #define RTW89_H2C_SCANOFLD_W3_TSF_HIGH GENMASK(31, 0)  #define RTW89_H2C_SCANOFLD_W4_TSF_LOW GENMASK(31, 0) +#define RTW89_H2C_SCANOFLD_W6_SECOND_MACID GENMASK(31, 24)  struct rtw89_h2c_scanofld_be_macc_role {  	__le32 w0; @@ -2795,6 +2824,7 @@ struct rtw89_h2c_scanofld_be_opch {  #define RTW89_H2C_SCANOFLD_BE_OPCH_W2_PKTS_CTRL GENMASK(7, 0)  #define RTW89_H2C_SCANOFLD_BE_OPCH_W2_SW_DEF GENMASK(15, 8)  #define RTW89_H2C_SCANOFLD_BE_OPCH_W2_SS GENMASK(18, 16) +#define RTW89_H2C_SCANOFLD_BE_OPCH_W2_TXBCN BIT(19)  #define RTW89_H2C_SCANOFLD_BE_OPCH_W3_PKT0 GENMASK(7, 0)  #define RTW89_H2C_SCANOFLD_BE_OPCH_W3_PKT1 GENMASK(15, 8)  #define RTW89_H2C_SCANOFLD_BE_OPCH_W3_PKT2 GENMASK(23, 16) @@ -2855,6 +2885,13 @@ struct rtw89_h2c_fwips {  #define RTW89_H2C_FW_IPS_W0_MACID GENMASK(7, 0)  #define RTW89_H2C_FW_IPS_W0_ENABLE BIT(8) +struct rtw89_h2c_mlo_link_cfg { +	__le32 w0; +}; + +#define RTW89_H2C_MLO_LINK_CFG_W0_MACID GENMASK(15, 0) +#define RTW89_H2C_MLO_LINK_CFG_W0_OPTION GENMASK(19, 16) +  static inline void RTW89_SET_FWCMD_P2P_MACID(void *cmd, u32 val)  {  	le32p_replace_bits((__le32 *)cmd, val, GENMASK(7, 0)); @@ -3535,6 +3572,8 @@ struct rtw89_fw_c2h_attr {  	u8 class;  	u8 func;  	u16 len; +	u8 is_scan_event: 1; +	u8 scan_seq: 2;  };  static inline struct rtw89_fw_c2h_attr *RTW89_SKB_C2H_CB(struct sk_buff *skb) @@ -3558,6 +3597,7 @@ struct rtw89_c2h_done_ack {  #define RTW89_C2H_DONE_ACK_W2_CLASS GENMASK(7, 2)  #define RTW89_C2H_DONE_ACK_W2_FUNC GENMASK(15, 8)  #define RTW89_C2H_DONE_ACK_W2_H2C_RETURN GENMASK(23, 16) +#define	RTW89_C2H_SCAN_DONE_ACK_RETURN GENMASK(5, 0)  #define RTW89_C2H_DONE_ACK_W2_H2C_SEQ GENMASK(31, 24)  #define RTW89_GET_MAC_C2H_REV_ACK_CAT(c2h) \ @@ -3616,6 +3656,19 @@ struct rtw89_c2h_ra_rpt {  #define RTW89_C2H_RA_RPT_W3_MD_SEL_B2 BIT(15)  #define RTW89_C2H_RA_RPT_W3_BW_B2 BIT(16) +struct rtw89_c2h_fw_scan_rpt { +	struct rtw89_c2h_hdr hdr; +	u8 phy_idx; +	u8 band; +	u8 center_ch; +	u8 ofdm_pd_idx; /* in unit of 2 dBm */ +#define PD_LOWER_BOUND_BASE 102 +	s8 cck_pd_idx; +	u8 rsvd0; +	u8 rsvd1; +	u8 rsvd2; +} __packed; +  /* For WiFi 6 chips:   *   VHT, HE, HT-old: [6:4]: NSS, [3:0]: MCS   *   HT-new: [6:5]: NA, [4:0]: MCS @@ -3717,6 +3770,25 @@ rtw89_static_assert(sizeof(struct rtw89_mac_mcc_tsf_rpt) <= RTW89_COMPLETION_BUF  #define RTW89_GET_MAC_C2H_MCC_STATUS_RPT_TSF_HIGH(c2h) \  	le32_get_bits(*((const __le32 *)(c2h) + 4), GENMASK(31, 0)) +struct rtw89_c2h_mlo_link_cfg_rpt { +	struct rtw89_c2h_hdr hdr; +	__le32 w2; +} __packed; + +#define RTW89_C2H_MLO_LINK_CFG_RPT_W2_MACID GENMASK(15, 0) +#define RTW89_C2H_MLO_LINK_CFG_RPT_W2_STATUS GENMASK(19, 16) + +enum rtw89_c2h_mlo_link_status { +	RTW89_C2H_MLO_LINK_CFG_IDLE = 0, +	RTW89_C2H_MLO_LINK_CFG_DONE = 1, +	RTW89_C2H_MLO_LINK_CFG_ISSUE_NULL_FAIL = 2, +	RTW89_C2H_MLO_LINK_CFG_TX_NULL_FAIL = 3, +	RTW89_C2H_MLO_LINK_CFG_ROLE_NOT_EXIST = 4, +	RTW89_C2H_MLO_LINK_CFG_NULL_1_TIMEOUT = 5, +	RTW89_C2H_MLO_LINK_CFG_NULL_0_TIMEOUT = 6, +	RTW89_C2H_MLO_LINK_CFG_RUNNING = 0xff, +}; +  struct rtw89_mac_mrc_tsf_rpt {  	unsigned int num;  	u64 tsfs[RTW89_MAC_MRC_MAX_REQ_TSF_NUM]; @@ -3813,7 +3885,8 @@ struct rtw89_h2c_bcnfltr {  #define RTW89_H2C_BCNFLTR_W0_MON_BCN BIT(1)  #define RTW89_H2C_BCNFLTR_W0_MON_EN BIT(2)  #define RTW89_H2C_BCNFLTR_W0_MODE GENMASK(4, 3) -#define RTW89_H2C_BCNFLTR_W0_BCN_LOSS_CNT GENMASK(11, 8) +#define RTW89_H2C_BCNFLTR_W0_BCN_LOSS_CNT_H3 GENMASK(7, 5) +#define RTW89_H2C_BCNFLTR_W0_BCN_LOSS_CNT_L4 GENMASK(11, 8)  #define RTW89_H2C_BCNFLTR_W0_RSSI_HYST GENMASK(15, 12)  #define RTW89_H2C_BCNFLTR_W0_RSSI_THRESHOLD GENMASK(23, 16)  #define RTW89_H2C_BCNFLTR_W0_MAC_ID GENMASK(31, 24) @@ -3890,6 +3963,13 @@ enum rtw89_fw_element_id {  	RTW89_FW_ELEMENT_ID_TX_SHAPE_LMT_RU = 17,  	RTW89_FW_ELEMENT_ID_TXPWR_TRK = 18,  	RTW89_FW_ELEMENT_ID_RFKLOG_FMT = 19, +	RTW89_FW_ELEMENT_ID_REGD = 20, +	RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_2GHZ = 21, +	RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_5GHZ = 22, +	RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_6GHZ = 23, +	RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_RU_2GHZ = 24, +	RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_RU_5GHZ = 25, +	RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_RU_6GHZ = 26,  	RTW89_FW_ELEMENT_ID_NUM,  }; @@ -3933,6 +4013,15 @@ struct __rtw89_fw_txpwr_element {  	u8 content[];  } __packed; +struct __rtw89_fw_regd_element { +	u8 rsvd0; +	u8 rsvd1; +	u8 rsvd2; +	u8 ent_sz; +	__le32 num_ents; +	u8 content[]; +} __packed; +  enum rtw89_fw_txpwr_trk_type {  	__RTW89_FW_TXPWR_TRK_TYPE_6GHZ_START = 0,  	RTW89_FW_TXPWR_TRK_TYPE_6GB_N = 0, @@ -4024,6 +4113,7 @@ struct rtw89_fw_element_hdr {  			__le16 offset[];  		} __packed rfk_log_fmt;  		struct __rtw89_fw_txpwr_element txpwr; +		struct __rtw89_fw_regd_element regd;  	} __packed u;  } __packed; @@ -4218,6 +4308,26 @@ enum rtw89_mcc_h2c_func {  #define RTW89_MCC_WAIT_COND(group, func) \  	((group) * NUM_OF_RTW89_MCC_H2C_FUNC + (func)) +/* CLASS 20 - MLO */ +#define H2C_CL_MLO                     0x14 +enum rtw89_mlo_h2c_func { +	H2C_FUNC_MLO_TBL_CFG		= 0x0, +	H2C_FUNC_MLO_STA_CFG		= 0x1, +	H2C_FUNC_MLO_TTLM		= 0x2, +	H2C_FUNC_MLO_DM_CFG		= 0x3, +	H2C_FUNC_MLO_EMLSR_STA_CFG	= 0x4, +	H2C_FUNC_MLO_MCMLO_RELINK_DROP	= 0x5, +	H2C_FUNC_MLO_MCMLO_SN_SYNC	= 0x6, +	H2C_FUNC_MLO_RELINK		= 0x7, +	H2C_FUNC_MLO_LINK_CFG		= 0x8, +	H2C_FUNC_MLO_DM_DBG		= 0x9, + +	NUM_OF_RTW89_MLO_H2C_FUNC, +}; + +#define RTW89_MLO_WAIT_COND(macid, func) \ +	((macid) * NUM_OF_RTW89_MLO_H2C_FUNC + (func)) +  /* CLASS 24 - MRC */  #define H2C_CL_MRC			0x18  enum rtw89_mrc_h2c_func { @@ -4249,6 +4359,7 @@ enum rtw89_mrc_h2c_func {  #define H2C_FUNC_OUTSRC_RA_MACIDCFG	0x0  #define H2C_CL_OUTSRC_DM		0x2 +#define H2C_FUNC_FW_MCC_DIG		0x6  #define H2C_FUNC_FW_LPS_CH_INFO		0xb  #define H2C_FUNC_FW_LPS_ML_CMN_INFO	0xe @@ -4256,6 +4367,7 @@ enum rtw89_mrc_h2c_func {  #define H2C_CL_OUTSRC_RF_REG_B		0x9  #define H2C_CL_OUTSRC_RF_FW_NOTIFY	0xa  #define H2C_FUNC_OUTSRC_RF_GET_MCCCH	0x2 +#define H2C_FUNC_OUTSRC_RF_PS_INFO	0x10  #define H2C_CL_OUTSRC_RF_FW_RFK		0xb  enum rtw89_rfk_offload_h2c_func { @@ -4269,6 +4381,14 @@ enum rtw89_rfk_offload_h2c_func {  };  struct rtw89_fw_h2c_rf_get_mccch { +	__le32 ch_0_0; +	__le32 ch_0_1; +	__le32 ch_1_0; +	__le32 ch_1_1; +	__le32 current_channel; +} __packed; + +struct rtw89_fw_h2c_rf_get_mccch_v0 {  	__le32 ch_0;  	__le32 ch_1;  	__le32 band_0; @@ -4277,9 +4397,36 @@ struct rtw89_fw_h2c_rf_get_mccch {  	__le32 current_band_type;  } __packed; +struct rtw89_h2c_mcc_dig { +	__le32 w0; +	__le32 w1; +	__le32 w2; +} __packed; + +#define RTW89_H2C_MCC_DIG_W0_REG_CNT GENMASK(7, 0) +#define RTW89_H2C_MCC_DIG_W0_DM_EN BIT(8) +#define RTW89_H2C_MCC_DIG_W0_IDX GENMASK(10, 9) +#define RTW89_H2C_MCC_DIG_W0_SET BIT(11) +#define RTW89_H2C_MCC_DIG_W0_PHY0_EN BIT(12) +#define RTW89_H2C_MCC_DIG_W0_PHY1_EN BIT(13) +#define RTW89_H2C_MCC_DIG_W0_CENTER_CH GENMASK(23, 16) +#define RTW89_H2C_MCC_DIG_W0_BAND_TYPE GENMASK(31, 24) +#define RTW89_H2C_MCC_DIG_W1_ADDR_LSB GENMASK(7, 0) +#define RTW89_H2C_MCC_DIG_W1_ADDR_MSB GENMASK(15, 8) +#define RTW89_H2C_MCC_DIG_W1_BMASK_LSB GENMASK(23, 16) +#define RTW89_H2C_MCC_DIG_W1_BMASK_MSB GENMASK(31, 24) +#define RTW89_H2C_MCC_DIG_W2_VAL_LSB GENMASK(7, 0) +#define RTW89_H2C_MCC_DIG_W2_VAL_MSB GENMASK(15, 8) +  #define NUM_OF_RTW89_FW_RFK_PATH 2  #define NUM_OF_RTW89_FW_RFK_TBL 3 +struct rtw89_h2c_rf_ps_info { +	__le32 rf18[NUM_OF_RTW89_FW_RFK_PATH]; +	__le32 mlo_mode; +	u8 pri_ch[NUM_OF_RTW89_FW_RFK_PATH]; +} __packed; +  struct rtw89_fw_h2c_rfk_pre_info_common {  	struct {  		__le32 ch[NUM_OF_RTW89_FW_RFK_PATH][NUM_OF_RTW89_FW_RFK_TBL]; @@ -4354,13 +4501,25 @@ struct rtw89_h2c_rf_tssi {  	u8 pg_thermal[2];  	u8 ftable[2][128];  	u8 tssi_mode; +	u8 rfe_type;  } __packed; -struct rtw89_h2c_rf_iqk { +struct rtw89_h2c_rf_iqk_v0 {  	__le32 phy_idx;  	__le32 dbcc;  } __packed; +struct rtw89_h2c_rf_iqk { +	u8 len; +	u8 ktype; +	u8 phy; +	u8 kpath; +	u8 band; +	u8 bw; +	u8 ch; +	u8 cv; +} __packed; +  struct rtw89_h2c_rf_dpk {  	u8 len;  	u8 phy; @@ -4532,6 +4691,12 @@ struct rtw89_c2h_rfk_report {  	u8 version;  } __packed; +struct rtw89_c2h_rf_tas_info { +	struct rtw89_c2h_hdr hdr; +	__le32 cur_idx; +	__le16 txpwr_history[20]; +} __packed; +  #define RTW89_FW_RSVD_PLE_SIZE 0x800  #define RTW89_FW_BACKTRACE_INFO_SIZE 8 @@ -4542,6 +4707,7 @@ struct rtw89_c2h_rfk_report {  #define RTW89_FW_BACKTRACE_KEY 0xBACEBACE  #define FWDL_WAIT_CNT 400000 +#define FWDL_WAIT_CNT_USB 3200  int rtw89_fw_check_rdy(struct rtw89_dev *rtwdev, enum rtw89_fwdl_check_type type);  int rtw89_fw_recognize(struct rtw89_dev *rtwdev); @@ -4581,6 +4747,11 @@ int rtw89_fw_h2c_ampdu_cmac_tbl_g7(struct rtw89_dev *rtwdev,  				   struct rtw89_sta_link *rtwsta_link);  int rtw89_fw_h2c_txtime_cmac_tbl(struct rtw89_dev *rtwdev,  				 struct rtw89_sta_link *rtwsta_link); +int rtw89_fw_h2c_txtime_cmac_tbl_g7(struct rtw89_dev *rtwdev, +				    struct rtw89_sta_link *rtwsta_link); +int rtw89_fw_h2c_punctured_cmac_tbl_g7(struct rtw89_dev *rtwdev, +				       struct rtw89_vif_link *rtwvif_link, +				       u16 punctured);  int rtw89_fw_h2c_txpath_cmac_tbl(struct rtw89_dev *rtwdev,  				 struct rtw89_sta_link *rtwsta_link);  int rtw89_fw_h2c_update_beacon(struct rtw89_dev *rtwdev, @@ -4596,7 +4767,8 @@ int rtw89_fw_h2c_dctl_sec_cam_v2(struct rtw89_dev *rtwdev,  				 struct rtw89_vif_link *rtwvif_link,  				 struct rtw89_sta_link *rtwsta_link);  void rtw89_fw_c2h_irqsafe(struct rtw89_dev *rtwdev, struct sk_buff *c2h); -void rtw89_fw_c2h_work(struct work_struct *work); +void rtw89_fw_c2h_work(struct wiphy *wiphy, struct wiphy_work *work); +void rtw89_fw_c2h_purge_obsoleted_scan_events(struct rtw89_dev *rtwdev);  int rtw89_fw_h2c_role_maintain(struct rtw89_dev *rtwdev,  			       struct rtw89_vif_link *rtwvif_link,  			       struct rtw89_sta_link *rtwsta_link, @@ -4624,6 +4796,7 @@ int rtw89_fw_h2c_cxdrv_role_v1(struct rtw89_dev *rtwdev, u8 type);  int rtw89_fw_h2c_cxdrv_role_v2(struct rtw89_dev *rtwdev, u8 type);  int rtw89_fw_h2c_cxdrv_role_v7(struct rtw89_dev *rtwdev, u8 type);  int rtw89_fw_h2c_cxdrv_role_v8(struct rtw89_dev *rtwdev, u8 type); +int rtw89_fw_h2c_cxdrv_osi_info(struct rtw89_dev *rtwdev, u8 type);  int rtw89_fw_h2c_cxdrv_ctrl(struct rtw89_dev *rtwdev, u8 type);  int rtw89_fw_h2c_cxdrv_ctrl_v7(struct rtw89_dev *rtwdev, u8 type);  int rtw89_fw_h2c_cxdrv_trx(struct rtw89_dev *rtwdev, u8 type); @@ -4643,8 +4816,12 @@ int rtw89_fw_h2c_rf_reg(struct rtw89_dev *rtwdev,  			struct rtw89_fw_h2c_rf_reg_info *info,  			u16 len, u8 page);  int rtw89_fw_h2c_rf_ntfy_mcc(struct rtw89_dev *rtwdev); +int rtw89_fw_h2c_rf_ps_info(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif);  int rtw89_fw_h2c_rf_pre_ntfy(struct rtw89_dev *rtwdev,  			     enum rtw89_phy_idx phy_idx); +int rtw89_fw_h2c_mcc_dig(struct rtw89_dev *rtwdev, +			 enum rtw89_chanctx_idx chanctx_idx, +			 u8 mcc_role_idx, u8 pd_val, bool en);  int rtw89_fw_h2c_rf_tssi(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx,  			 const struct rtw89_chan *chan, enum rtw89_tssi_mode tssi_mode);  int rtw89_fw_h2c_rf_iqk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, @@ -4662,6 +4839,7 @@ int rtw89_fw_h2c_raw_with_hdr(struct rtw89_dev *rtwdev,  			      bool rack, bool dack);  int rtw89_fw_h2c_raw(struct rtw89_dev *rtwdev, const u8 *buf, u16 len);  void rtw89_fw_send_all_early_h2c(struct rtw89_dev *rtwdev); +void __rtw89_fw_free_all_early_h2c(struct rtw89_dev *rtwdev);  void rtw89_fw_free_all_early_h2c(struct rtw89_dev *rtwdev);  int rtw89_fw_h2c_general_pkt(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link,  			     u8 macid); @@ -4695,9 +4873,9 @@ int rtw89_fw_msg_reg(struct rtw89_dev *rtwdev,  		     struct rtw89_mac_c2h_info *c2h_info);  int rtw89_fw_h2c_fw_log(struct rtw89_dev *rtwdev, bool enable);  void rtw89_fw_st_dbg_dump(struct rtw89_dev *rtwdev); -void rtw89_hw_scan_start(struct rtw89_dev *rtwdev, -			 struct rtw89_vif_link *rtwvif_link, -			 struct ieee80211_scan_request *scan_req); +int rtw89_hw_scan_start(struct rtw89_dev *rtwdev, +			struct rtw89_vif_link *rtwvif_link, +			struct ieee80211_scan_request *scan_req);  void rtw89_hw_scan_complete(struct rtw89_dev *rtwdev,  			    struct rtw89_vif_link *rtwvif_link,  			    bool aborted); @@ -4706,12 +4884,18 @@ int rtw89_hw_scan_offload(struct rtw89_dev *rtwdev,  			  bool enable);  void rtw89_hw_scan_abort(struct rtw89_dev *rtwdev,  			 struct rtw89_vif_link *rtwvif_link); +int rtw89_hw_scan_prep_chan_list_ax(struct rtw89_dev *rtwdev, +				    struct rtw89_vif_link *rtwvif_link); +void rtw89_hw_scan_free_chan_list_ax(struct rtw89_dev *rtwdev);  int rtw89_hw_scan_add_chan_list_ax(struct rtw89_dev *rtwdev, -				   struct rtw89_vif_link *rtwvif_link, bool connected); +				   struct rtw89_vif_link *rtwvif_link);  int rtw89_pno_scan_add_chan_list_ax(struct rtw89_dev *rtwdev,  				    struct rtw89_vif_link *rtwvif_link); +int rtw89_hw_scan_prep_chan_list_be(struct rtw89_dev *rtwdev, +				    struct rtw89_vif_link *rtwvif_link); +void rtw89_hw_scan_free_chan_list_be(struct rtw89_dev *rtwdev);  int rtw89_hw_scan_add_chan_list_be(struct rtw89_dev *rtwdev, -				   struct rtw89_vif_link *rtwvif_link, bool connected); +				   struct rtw89_vif_link *rtwvif_link);  int rtw89_pno_scan_add_chan_list_be(struct rtw89_dev *rtwdev,  				    struct rtw89_vif_link *rtwvif_link);  int rtw89_fw_h2c_trigger_cpu_exception(struct rtw89_dev *rtwdev); @@ -4719,9 +4903,8 @@ int rtw89_fw_h2c_pkt_drop(struct rtw89_dev *rtwdev,  			  const struct rtw89_pkt_drop_params *params);  int rtw89_fw_h2c_p2p_act(struct rtw89_dev *rtwdev,  			 struct rtw89_vif_link *rtwvif_link, -			 struct ieee80211_bss_conf *bss_conf,  			 struct ieee80211_p2p_noa_desc *desc, -			 u8 act, u8 noa_id); +			 u8 act, u8 noa_id, u8 ctwindow_oppps);  int rtw89_fw_h2c_tsf32_toggle(struct rtw89_dev *rtwdev,  			      struct rtw89_vif_link *rtwvif_link,  			      bool en); @@ -4780,6 +4963,8 @@ int rtw89_fw_h2c_mrc_sync(struct rtw89_dev *rtwdev,  int rtw89_fw_h2c_mrc_upd_duration(struct rtw89_dev *rtwdev,  				  const struct rtw89_fw_mrc_upd_duration_arg *arg);  int rtw89_fw_h2c_ap_info_refcount(struct rtw89_dev *rtwdev, bool en); +int rtw89_fw_h2c_mlo_link_cfg(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, +			      bool enable);  static inline void rtw89_fw_h2c_init_ba_cam(struct rtw89_dev *rtwdev)  { @@ -4862,6 +5047,28 @@ static inline int rtw89_chip_h2c_ampdu_cmac_tbl(struct rtw89_dev *rtwdev,  }  static inline +int rtw89_chip_h2c_txtime_cmac_tbl(struct rtw89_dev *rtwdev, +				   struct rtw89_sta_link *rtwsta_link) +{ +	const struct rtw89_chip_info *chip = rtwdev->chip; + +	return chip->ops->h2c_txtime_cmac_tbl(rtwdev, rtwsta_link); +} + +static inline +int rtw89_chip_h2c_punctured_cmac_tbl(struct rtw89_dev *rtwdev, +				      struct rtw89_vif_link *rtwvif_link, +				      u16 punctured) +{ +	const struct rtw89_chip_info *chip = rtwdev->chip; + +	if (!chip->ops->h2c_punctured_cmac_tbl) +		return 0; + +	return chip->ops->h2c_punctured_cmac_tbl(rtwdev, rtwvif_link, punctured); +} + +static inline  int rtw89_chip_h2c_ba_cam(struct rtw89_dev *rtwdev, struct rtw89_sta *rtwsta,  			  bool valid, struct ieee80211_ampdu_params *params)  { @@ -4882,6 +5089,18 @@ int rtw89_chip_h2c_ba_cam(struct rtw89_dev *rtwdev, struct rtw89_sta *rtwsta,  	return 0;  } +/* Must consider compatibility; don't insert new in the mid. + * Fill each field's default value in rtw89_regd_entcpy(). + */ +struct rtw89_fw_regd_entry { +	u8 alpha2_0; +	u8 alpha2_1; +	u8 rule_2ghz; +	u8 rule_5ghz; +	u8 rule_6ghz; +	__le32 fmap; +} __packed; +  /* must consider compatibility; don't insert new in the mid */  struct rtw89_fw_txpwr_byrate_entry {  	u8 band; diff --git a/sys/contrib/dev/rtw89/mac.c b/sys/contrib/dev/rtw89/mac.c index 0f025e785834..1d8eeb6e9997 100644 --- a/sys/contrib/dev/rtw89/mac.c +++ b/sys/contrib/dev/rtw89/mac.c @@ -88,7 +88,7 @@ int rtw89_mac_write_lte(struct rtw89_dev *rtwdev, const u32 offset, u32 val)  	ret = read_poll_timeout(rtw89_read8, lte_ctrl, (lte_ctrl & BIT(5)) != 0,  				50, 50000, false, rtwdev, R_AX_LTE_CTRL + 3); -	if (ret) +	if (ret && !test_bit(RTW89_FLAG_UNPLUGGED, rtwdev->flags))  		rtw89_err(rtwdev, "[ERR]lte not ready(W)\n");  	rtw89_write32(rtwdev, R_AX_LTE_WDATA, val); @@ -104,7 +104,7 @@ int rtw89_mac_read_lte(struct rtw89_dev *rtwdev, const u32 offset, u32 *val)  	ret = read_poll_timeout(rtw89_read8, lte_ctrl, (lte_ctrl & BIT(5)) != 0,  				50, 50000, false, rtwdev, R_AX_LTE_CTRL + 3); -	if (ret) +	if (ret && !test_bit(RTW89_FLAG_UNPLUGGED, rtwdev->flags))  		rtw89_err(rtwdev, "[ERR]lte not ready(W)\n");  	rtw89_write32(rtwdev, R_AX_LTE_CTRL, 0x800F0000 | offset); @@ -875,31 +875,30 @@ EXPORT_SYMBOL(rtw89_mac_set_err_status);  static int hfc_reset_param(struct rtw89_dev *rtwdev)  { +	const struct rtw89_hfc_param_ini *param_ini, *param_inis;  	struct rtw89_hfc_param *param = &rtwdev->mac.hfc_param; -	struct rtw89_hfc_param_ini param_ini = {NULL};  	u8 qta_mode = rtwdev->mac.dle_info.qta_mode; -	switch (rtwdev->hci.type) { -	case RTW89_HCI_TYPE_PCIE: -		param_ini = rtwdev->chip->hfc_param_ini[qta_mode]; -		param->en = 0; -		break; -	default: +	param_inis = rtwdev->chip->hfc_param_ini[rtwdev->hci.type]; +	if (!param_inis)  		return -EINVAL; -	} -	if (param_ini.pub_cfg) -		param->pub_cfg = *param_ini.pub_cfg; +	param_ini = ¶m_inis[qta_mode]; + +	param->en = 0; + +	if (param_ini->pub_cfg) +		param->pub_cfg = *param_ini->pub_cfg; -	if (param_ini.prec_cfg) -		param->prec_cfg = *param_ini.prec_cfg; +	if (param_ini->prec_cfg) +		param->prec_cfg = *param_ini->prec_cfg; -	if (param_ini.ch_cfg) -		param->ch_cfg = param_ini.ch_cfg; +	if (param_ini->ch_cfg) +		param->ch_cfg = param_ini->ch_cfg;  	memset(¶m->ch_info, 0, sizeof(param->ch_info));  	memset(¶m->pub_info, 0, sizeof(param->pub_info)); -	param->mode = param_ini.mode; +	param->mode = param_ini->mode;  	return 0;  } @@ -1441,6 +1440,23 @@ void rtw89_mac_notify_wake(struct rtw89_dev *rtwdev)  	rtw89_mac_send_rpwm(rtwdev, state, true);  } +static void rtw89_mac_power_switch_boot_mode(struct rtw89_dev *rtwdev) +{ +	u32 boot_mode; + +	if (rtwdev->hci.type != RTW89_HCI_TYPE_USB) +		return; + +	boot_mode = rtw89_read32_mask(rtwdev, R_AX_GPIO_MUXCFG, B_AX_BOOT_MODE); +	if (!boot_mode) +		return; + +	rtw89_write32_clr(rtwdev, R_AX_SYS_PW_CTRL, B_AX_APFN_ONMAC); +	rtw89_write32_clr(rtwdev, R_AX_SYS_STATUS1, B_AX_AUTO_WLPON); +	rtw89_write32_clr(rtwdev, R_AX_GPIO_MUXCFG, B_AX_BOOT_MODE); +	rtw89_write32_clr(rtwdev, R_AX_RSV_CTRL, B_AX_R_DIS_PRST); +} +  static int rtw89_mac_power_switch(struct rtw89_dev *rtwdev, bool on)  {  #define PWR_ACT 1 @@ -1451,6 +1467,8 @@ static int rtw89_mac_power_switch(struct rtw89_dev *rtwdev, bool on)  	int ret;  	u8 val; +	rtw89_mac_power_switch_boot_mode(rtwdev); +  	if (on) {  		cfg_seq = chip->pwr_on_seq;  		cfg_func = chip->ops->pwr_on_func; @@ -1495,6 +1513,21 @@ static int rtw89_mac_power_switch(struct rtw89_dev *rtwdev, bool on)  #undef PWR_ACT  } +int rtw89_mac_pwr_on(struct rtw89_dev *rtwdev) +{ +	int ret; + +	ret = rtw89_mac_power_switch(rtwdev, true); +	if (ret) { +		rtw89_mac_power_switch(rtwdev, false); +		ret = rtw89_mac_power_switch(rtwdev, true); +		if (ret) +			return ret; +	} + +	return 0; +} +  void rtw89_mac_pwr_off(struct rtw89_dev *rtwdev)  {  	rtw89_mac_power_switch(rtwdev, false); @@ -1631,6 +1664,8 @@ const struct rtw89_mac_size_set rtw89_mac_size = {  	/* 8852C PCIE SCC */  	.wde_size19 = {RTW89_WDE_PG_64, 3328, 0,},  	.wde_size23 = {RTW89_WDE_PG_64, 1022, 2,}, +	/* 8852B USB2.0/USB3.0 SCC */ +	.wde_size25 = {RTW89_WDE_PG_64, 162, 94,},  	/* PCIE */  	.ple_size0 = {RTW89_PLE_PG_128, 1520, 16,},  	.ple_size0_v1 = {RTW89_PLE_PG_128, 2688, 240, 212992,}, @@ -1646,6 +1681,10 @@ const struct rtw89_mac_size_set rtw89_mac_size = {  	.ple_size18 = {RTW89_PLE_PG_128, 2544, 16,},  	/* 8852C PCIE SCC */  	.ple_size19 = {RTW89_PLE_PG_128, 1904, 16,}, +	/* 8852B USB2.0 SCC */ +	.ple_size32 = {RTW89_PLE_PG_128, 620, 20,}, +	/* 8852B USB3.0 SCC */ +	.ple_size33 = {RTW89_PLE_PG_128, 632, 8,},  	/* PCIE 64 */  	.wde_qt0 = {3792, 196, 0, 107,},  	.wde_qt0_v1 = {3302, 6, 0, 20,}, @@ -1660,6 +1699,8 @@ const struct rtw89_mac_size_set rtw89_mac_size = {  	/* 8852C PCIE SCC */  	.wde_qt18 = {3228, 60, 0, 40,},  	.wde_qt23 = {958, 48, 0, 16,}, +	/* 8852B USB2.0/USB3.0 SCC */ +	.wde_qt25 = {152, 2, 0, 8,},  	.ple_qt0 = {320, 320, 32, 16, 13, 13, 292, 292, 64, 18, 1, 4, 0,},  	.ple_qt1 = {320, 320, 32, 16, 1316, 1316, 1595, 1595, 1367, 1321, 1, 1307, 0,},  	/* PCIE SCC */ @@ -1683,6 +1724,13 @@ const struct rtw89_mac_size_set rtw89_mac_size = {  	/* PCIE 64 */  	.ple_qt58 = {147, 0, 16, 20, 157, 13, 229, 0, 172, 14, 24, 0,},  	.ple_qt59 = {147, 0, 32, 20, 1860, 13, 2025, 0, 1879, 14, 24, 0,}, +	/* USB2.0 52B SCC */ +	.ple_qt72 = {130, 0, 16, 48, 4, 13, 322, 0, 32, 14, 8, 0, 0,}, +	/* USB2.0 52B 92K */ +	.ple_qt73 = {130, 0, 32, 48, 37, 13, 355, 0, 65, 14, 24, 0, 0,}, +	/* USB3.0 52B 92K */ +	.ple_qt74 = {286, 0, 16, 48, 4, 13, 178, 0, 32, 14, 8, 0, 0,}, +	.ple_qt75 = {286, 0, 32, 48, 37, 13, 211, 0, 65, 14, 24, 0, 0,},  	/* 8852A PCIE WOW */  	.ple_qt_52a_wow = {264, 0, 32, 20, 64, 13, 1005, 0, 64, 128, 120,},  	/* 8852B PCIE WOW */ @@ -1702,12 +1750,13 @@ static const struct rtw89_dle_mem *get_dle_mem_cfg(struct rtw89_dev *rtwdev,  						   enum rtw89_qta_mode mode)  {  	struct rtw89_mac_info *mac = &rtwdev->mac; -	const struct rtw89_dle_mem *cfg; +	const struct rtw89_dle_mem *cfg, *cfgs; -	cfg = &rtwdev->chip->dle_mem[mode]; -	if (!cfg) +	cfgs = rtwdev->chip->dle_mem[rtwdev->hci.dle_type]; +	if (!cfgs)  		return NULL; +	cfg = &cfgs[mode];  	if (cfg->mode != mode) {  		rtw89_warn(rtwdev, "qta mode unmatch!\n");  		return NULL; @@ -3996,14 +4045,6 @@ int rtw89_mac_partial_init(struct rtw89_dev *rtwdev, bool include_bb)  {  	int ret; -	ret = rtw89_mac_power_switch(rtwdev, true); -	if (ret) { -		rtw89_mac_power_switch(rtwdev, false); -		ret = rtw89_mac_power_switch(rtwdev, true); -		if (ret) -			return ret; -	} -  	rtw89_mac_ctrl_hci_dma_trx(rtwdev, true);  	if (include_bb) { @@ -4036,6 +4077,10 @@ int rtw89_mac_init(struct rtw89_dev *rtwdev)  	bool include_bb = !!chip->bbmcu_nr;  	int ret; +	ret = rtw89_mac_pwr_on(rtwdev); +	if (ret) +		return ret; +  	ret = rtw89_mac_partial_init(rtwdev, include_bb);  	if (ret)  		goto fail; @@ -4067,7 +4112,7 @@ int rtw89_mac_init(struct rtw89_dev *rtwdev)  	return ret;  fail: -	rtw89_mac_power_switch(rtwdev, false); +	rtw89_mac_pwr_off(rtwdev);  	return ret;  } @@ -4377,7 +4422,33 @@ static void rtw89_mac_port_cfg_tx_sw_by_nettype(struct rtw89_dev *rtwdev,  	rtw89_mac_port_cfg_tx_sw(rtwdev, rtwvif_link, en);  } -void rtw89_mac_enable_beacon_for_ap_vifs(struct rtw89_dev *rtwdev, bool en) +static void rtw89_mac_enable_ap_bcn_by_chan(struct rtw89_dev *rtwdev, +					    struct rtw89_vif_link *rtwvif_link, +					    const struct rtw89_chan *to_match, +					    bool en) +{ +	const struct rtw89_chan *chan; + +	if (rtwvif_link->net_type != RTW89_NET_TYPE_AP_MODE) +		return; + +	if (!to_match) +		goto doit; + +	/* @to_match may not be in the same domain as return of calling +	 * rtw89_chan_get(). So, cannot compare their addresses directly. +	 */ +	chan = rtw89_chan_get(rtwdev, rtwvif_link->chanctx_idx); +	if (chan->channel != to_match->channel) +		return; + +doit: +	rtw89_mac_port_cfg_tx_sw(rtwdev, rtwvif_link, en); +} + +static void rtw89_mac_enable_aps_bcn_by_chan(struct rtw89_dev *rtwdev, +					     const struct rtw89_chan *to_match, +					     bool en)  {  	struct rtw89_vif_link *rtwvif_link;  	struct rtw89_vif *rtwvif; @@ -4385,8 +4456,13 @@ void rtw89_mac_enable_beacon_for_ap_vifs(struct rtw89_dev *rtwdev, bool en)  	rtw89_for_each_rtwvif(rtwdev, rtwvif)  		rtw89_vif_for_each_link(rtwvif, rtwvif_link, link_id) -			if (rtwvif_link->net_type == RTW89_NET_TYPE_AP_MODE) -				rtw89_mac_port_cfg_tx_sw(rtwdev, rtwvif_link, en); +			rtw89_mac_enable_ap_bcn_by_chan(rtwdev, rtwvif_link, +							to_match, en); +} + +void rtw89_mac_enable_beacon_for_ap_vifs(struct rtw89_dev *rtwdev, bool en) +{ +	rtw89_mac_enable_aps_bcn_by_chan(rtwdev, NULL, en);  }  static void rtw89_mac_port_cfg_bcn_intv(struct rtw89_dev *rtwdev, @@ -4620,11 +4696,17 @@ static void rtw89_mac_port_tsf_sync_rand(struct rtw89_dev *rtwdev,  	if (rtwvif_link->net_type != RTW89_NET_TYPE_AP_MODE || rtwvif_link == rtwvif_src)  		return; +	if (rtwvif_link->rand_tsf_done) +		goto out; +  	/* adjust offset randomly to avoid beacon conflict */  	offset = offset - offset / 4 + get_random_u32() % (offset / 2);  	rtw89_mac_port_tsf_sync(rtwdev, rtwvif_link, rtwvif_src,  				(*n_offset) * offset); +	rtwvif_link->rand_tsf_done = true; + +out:  	(*n_offset)++;  } @@ -4826,9 +4908,37 @@ void rtw89_mac_set_he_obss_narrow_bw_ru(struct rtw89_dev *rtwdev,  		rtw89_write32_set(rtwdev, reg, mac->narrow_bw_ru_dis.mask);  } +void rtw89_mac_set_he_tb(struct rtw89_dev *rtwdev, +			 struct rtw89_vif_link *rtwvif_link) +{ +	struct ieee80211_bss_conf *bss_conf; +	bool set; +	u32 reg; + +	if (rtwdev->chip->chip_gen != RTW89_CHIP_BE) +		return; + +	rcu_read_lock(); + +	bss_conf = rtw89_vif_rcu_dereference_link(rtwvif_link, true); +	set = bss_conf->he_support && !bss_conf->eht_support; + +	rcu_read_unlock(); + +	reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_CLIENT_OM_CTRL, +				   rtwvif_link->mac_idx); + +	if (set) +		rtw89_write32_set(rtwdev, reg, B_BE_TRIG_DIS_EHTTB); +	else +		rtw89_write32_clr(rtwdev, reg, B_BE_TRIG_DIS_EHTTB); +} +  void rtw89_mac_stop_ap(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link)  {  	rtw89_mac_port_cfg_func_sw(rtwdev, rtwvif_link); + +	rtwvif_link->rand_tsf_done = false;  }  int rtw89_mac_add_vif(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link) @@ -4846,11 +4956,22 @@ rtw89_mac_c2h_macid_pause(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len  {  } -static bool rtw89_is_op_chan(struct rtw89_dev *rtwdev, u8 band, u8 channel) +static const struct rtw89_chan * +rtw89_hw_scan_search_op_chan(struct rtw89_dev *rtwdev, u8 band, u8 channel)  { +	struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info;  	const struct rtw89_chan *op = &rtwdev->scan_info.op_chan; -	return band == op->band_type && channel == op->primary_channel; +	if (band == op->band_type && channel == op->primary_channel) +		return op; + +	if (scan_info->extra_op.set) { +		op = &scan_info->extra_op.chan; +		if (band == op->band_type && channel == op->primary_channel) +			return op; +	} + +	return NULL;  }  static void @@ -4860,13 +4981,14 @@ rtw89_mac_c2h_scanofld_rsp(struct rtw89_dev *rtwdev, struct sk_buff *skb,  	const struct rtw89_c2h_scanofld *c2h =  		(const struct rtw89_c2h_scanofld *)skb->data;  	struct rtw89_vif_link *rtwvif_link = rtwdev->scan_info.scanning_vif; +	const struct rtw89_chan *op_chan;  	struct rtw89_vif *rtwvif;  	struct rtw89_chan new; -	u32 last_chan = rtwdev->scan_info.last_chan_idx, report_tsf;  	u16 actual_period, expect_period;  	u8 reason, status, tx_fail, band;  	u8 mac_idx, sw_def, fw_def;  	u8 ver = U8_MAX; +	u32 report_tsf;  	u16 chan;  	int ret; @@ -4915,8 +5037,9 @@ rtw89_mac_c2h_scanofld_rsp(struct rtw89_dev *rtwdev, struct sk_buff *skb,  	switch (reason) {  	case RTW89_SCAN_LEAVE_OP_NOTIFY:  	case RTW89_SCAN_LEAVE_CH_NOTIFY: -		if (rtw89_is_op_chan(rtwdev, band, chan)) { -			rtw89_mac_enable_beacon_for_ap_vifs(rtwdev, false); +		op_chan = rtw89_hw_scan_search_op_chan(rtwdev, band, chan); +		if (op_chan) { +			rtw89_mac_enable_aps_bcn_by_chan(rtwdev, op_chan, false);  			ieee80211_stop_queues(rtwdev->hw);  		}  		return; @@ -4925,7 +5048,8 @@ rtw89_mac_c2h_scanofld_rsp(struct rtw89_dev *rtwdev, struct sk_buff *skb,  			return;  		if (rtwvif_link && rtwvif->scan_req && -		    last_chan < rtwvif->scan_req->n_channels) { +		    !list_empty(&rtwdev->scan_info.chan_list)) { +			rtwdev->scan_info.delay = 0;  			ret = rtw89_hw_scan_offload(rtwdev, rtwvif_link, true);  			if (ret) {  				rtw89_hw_scan_abort(rtwdev, rtwvif_link); @@ -4937,10 +5061,10 @@ rtw89_mac_c2h_scanofld_rsp(struct rtw89_dev *rtwdev, struct sk_buff *skb,  		break;  	case RTW89_SCAN_ENTER_OP_NOTIFY:  	case RTW89_SCAN_ENTER_CH_NOTIFY: -		if (rtw89_is_op_chan(rtwdev, band, chan)) { -			rtw89_assign_entity_chan(rtwdev, rtwvif_link->chanctx_idx, -						 &rtwdev->scan_info.op_chan); -			rtw89_mac_enable_beacon_for_ap_vifs(rtwdev, true); +		op_chan = rtw89_hw_scan_search_op_chan(rtwdev, band, chan); +		if (op_chan) { +			rtw89_assign_entity_chan(rtwdev, rtwvif_link->chanctx_idx, op_chan); +			rtw89_mac_enable_aps_bcn_by_chan(rtwdev, op_chan, true);  			ieee80211_wake_queues(rtwdev->hw);  		} else {  			rtw89_chan_create(&new, chan, chan, band, @@ -4964,6 +5088,7 @@ rtw89_mac_bcn_fltr_rpt(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_l  	const struct rtw89_c2h_mac_bcnfltr_rpt *c2h =  		(const struct rtw89_c2h_mac_bcnfltr_rpt *)skb->data;  	u8 type, event, mac_id; +	bool start_detect;  	s8 sig;  	type = le32_get_bits(c2h->w2, RTW89_C2H_MAC_BCNFLTR_RPT_W2_TYPE); @@ -4980,10 +5105,16 @@ rtw89_mac_bcn_fltr_rpt(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_l  	switch (type) {  	case RTW89_BCN_FLTR_BEACON_LOSS: -		if (!rtwdev->scanning && !rtwvif->offchan) +		if (!rtwdev->scanning && !rtwvif->offchan && +		    !rtwvif_link->noa_once.in_duration) { +			start_detect = rtw89_mcc_detect_go_bcn(rtwdev, rtwvif_link); +			if (start_detect) +				return; +  			ieee80211_connection_loss(vif); -		else +		} else {  			rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, rtwvif_link, true); +		}  		return;  	case RTW89_BCN_FLTR_NOTIFY:  		nl_event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH; @@ -5034,6 +5165,7 @@ rtw89_mac_c2h_done_ack(struct rtw89_dev *rtwdev, struct sk_buff *skb_c2h, u32 le  {  	/* N.B. This will run in interrupt context. */  	struct rtw89_wait_info *fw_ofld_wait = &rtwdev->mac.fw_ofld_wait; +	struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info;  	struct rtw89_wait_info *ps_wait = &rtwdev->mac.ps_wait;  	const struct rtw89_c2h_done_ack *c2h =  		(const struct rtw89_c2h_done_ack *)skb_c2h->data; @@ -5073,12 +5205,16 @@ rtw89_mac_c2h_done_ack(struct rtw89_dev *rtwdev, struct sk_buff *skb_c2h, u32 le  			return;  		case H2C_FUNC_ADD_SCANOFLD_CH:  			cond = RTW89_SCANOFLD_WAIT_COND_ADD_CH; +			h2c_return &= RTW89_C2H_SCAN_DONE_ACK_RETURN;  			break;  		case H2C_FUNC_SCANOFLD: +			scan_info->seq++;  			cond = RTW89_SCANOFLD_WAIT_COND_START;  			break;  		case H2C_FUNC_SCANOFLD_BE: +			scan_info->seq++;  			cond = RTW89_SCANOFLD_BE_WAIT_COND_START; +			h2c_return &= RTW89_C2H_SCAN_DONE_ACK_RETURN;  			break;  		} @@ -5378,6 +5514,27 @@ rtw89_mac_c2h_wow_aoac_rpt(struct rtw89_dev *rtwdev, struct sk_buff *skb, u32 le  }  static void +rtw89_mac_c2h_mlo_link_cfg_stat(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) +{ +	const struct rtw89_c2h_mlo_link_cfg_rpt *c2h_rpt; +	struct rtw89_wait_info *wait = &rtwdev->mlo.wait; +	struct rtw89_completion_data data = {}; +	unsigned int cond; +	u16 mac_id; +	u8 status; + +	c2h_rpt = (const struct rtw89_c2h_mlo_link_cfg_rpt *)c2h->data; + +	mac_id = le32_get_bits(c2h_rpt->w2, RTW89_C2H_MLO_LINK_CFG_RPT_W2_MACID); +	status = le32_get_bits(c2h_rpt->w2, RTW89_C2H_MLO_LINK_CFG_RPT_W2_STATUS); + +	data.err = status == RTW89_C2H_MLO_LINK_CFG_ROLE_NOT_EXIST || +		   status == RTW89_C2H_MLO_LINK_CFG_RUNNING; +	cond = RTW89_MLO_WAIT_COND(mac_id, H2C_FUNC_MLO_LINK_CFG); +	rtw89_complete_cond(wait, cond, &data); +} + +static void  rtw89_mac_c2h_mrc_status_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len)  {  	struct rtw89_wait_info *wait = &rtwdev->mcc.wait; @@ -5537,6 +5694,18 @@ void (* const rtw89_mac_c2h_mcc_handler[])(struct rtw89_dev *rtwdev,  };  static +void (* const rtw89_mac_c2h_mlo_handler[])(struct rtw89_dev *rtwdev, +					   struct sk_buff *c2h, u32 len) = { +	[RTW89_MAC_C2H_FUNC_MLO_GET_TBL] = NULL, +	[RTW89_MAC_C2H_FUNC_MLO_EMLSR_TRANS_DONE] = NULL, +	[RTW89_MAC_C2H_FUNC_MLO_EMLSR_STA_CFG_DONE] = NULL, +	[RTW89_MAC_C2H_FUNC_MCMLO_RELINK_RPT] = NULL, +	[RTW89_MAC_C2H_FUNC_MCMLO_SN_SYNC_RPT] = NULL, +	[RTW89_MAC_C2H_FUNC_MLO_LINK_CFG_STAT] = rtw89_mac_c2h_mlo_link_cfg_stat, +	[RTW89_MAC_C2H_FUNC_MLO_DM_DBG_DUMP] = NULL, +}; + +static  void (* const rtw89_mac_c2h_mrc_handler[])(struct rtw89_dev *rtwdev,  					   struct sk_buff *c2h, u32 len) = {  	[RTW89_MAC_C2H_FUNC_MRC_TSF_RPT] = rtw89_mac_c2h_mrc_tsf_rpt, @@ -5561,10 +5730,15 @@ static void rtw89_mac_c2h_scanofld_rsp_atomic(struct rtw89_dev *rtwdev,  	const struct rtw89_c2h_scanofld *c2h =  		(const struct rtw89_c2h_scanofld *)skb->data;  	struct rtw89_wait_info *fw_ofld_wait = &rtwdev->mac.fw_ofld_wait; +	struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; +	struct rtw89_fw_c2h_attr *attr = RTW89_SKB_C2H_CB(skb);  	struct rtw89_completion_data data = {};  	unsigned int cond;  	u8 status, reason; +	attr->is_scan_event = 1; +	attr->scan_seq = scan_info->seq; +  	status = le32_get_bits(c2h->w2, RTW89_C2H_SCANOFLD_W2_STATUS);  	reason = le32_get_bits(c2h->w2, RTW89_C2H_SCANOFLD_W2_RSN);  	data.err = status != RTW89_SCAN_STATUS_SUCCESS; @@ -5605,6 +5779,8 @@ bool rtw89_mac_c2h_chk_atomic(struct rtw89_dev *rtwdev, struct sk_buff *c2h,  		}  	case RTW89_MAC_C2H_CLASS_MCC:  		return true; +	case RTW89_MAC_C2H_CLASS_MLO: +		return true;  	case RTW89_MAC_C2H_CLASS_MRC:  		return true;  	case RTW89_MAC_C2H_CLASS_WOW: @@ -5638,6 +5814,10 @@ void rtw89_mac_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb,  		if (func < NUM_OF_RTW89_MAC_C2H_FUNC_MCC)  			handler = rtw89_mac_c2h_mcc_handler[func];  		break; +	case RTW89_MAC_C2H_CLASS_MLO: +		if (func < NUM_OF_RTW89_MAC_C2H_FUNC_MLO) +			handler = rtw89_mac_c2h_mlo_handler[func]; +		break;  	case RTW89_MAC_C2H_CLASS_MRC:  		if (func < NUM_OF_RTW89_MAC_C2H_FUNC_MRC)  			handler = rtw89_mac_c2h_mrc_handler[func]; @@ -5651,6 +5831,7 @@ void rtw89_mac_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb,  			handler = rtw89_mac_c2h_ap_handler[func];  		break;  	case RTW89_MAC_C2H_CLASS_FWDBG: +	case RTW89_MAC_C2H_CLASS_ROLE:  		return;  	default:  		rtw89_info(rtwdev, "MAC c2h class %d not support\n", class); @@ -5713,7 +5894,7 @@ int rtw89_mac_cfg_ppdu_status_ax(struct rtw89_dev *rtwdev, u8 mac_idx, bool enab  	rtw89_write32(rtwdev, reg, B_AX_PPDU_STAT_RPT_EN |  				   B_AX_APP_MAC_INFO_RPT | -				   B_AX_APP_RX_CNT_RPT | B_AX_APP_PLCP_HDR_RPT | +				   B_AX_APP_PLCP_HDR_RPT |  				   B_AX_PPDU_STAT_RPT_CRC32);  	rtw89_write32_mask(rtwdev, R_AX_HW_RPT_FWD, B_AX_FWD_PPDU_STAT_MASK,  			   RTW89_PRPT_DEST_HOST); @@ -5796,13 +5977,15 @@ int rtw89_mac_coex_init(struct rtw89_dev *rtwdev, const struct rtw89_mac_ax_coex  	ret = rtw89_mac_read_lte(rtwdev, R_AX_LTE_SW_CFG_2, &val32);  	if (ret) { -		rtw89_err(rtwdev, "Read R_AX_LTE_SW_CFG_2 fail!\n"); +		if (!test_bit(RTW89_FLAG_UNPLUGGED, rtwdev->flags)) +			rtw89_err(rtwdev, "Read R_AX_LTE_SW_CFG_2 fail!\n");  		return ret;  	}  	val32 = val32 & B_AX_WL_RX_CTRL;  	ret = rtw89_mac_write_lte(rtwdev, R_AX_LTE_SW_CFG_2, val32);  	if (ret) { -		rtw89_err(rtwdev, "Write R_AX_LTE_SW_CFG_2 fail!\n"); +		if (!test_bit(RTW89_FLAG_UNPLUGGED, rtwdev->flags)) +			rtw89_err(rtwdev, "Write R_AX_LTE_SW_CFG_2 fail!\n");  		return ret;  	} @@ -5926,7 +6109,8 @@ int rtw89_mac_cfg_gnt(struct rtw89_dev *rtwdev,  	ret = rtw89_mac_write_lte(rtwdev, R_AX_LTE_SW_CFG_1, val);  	if (ret) { -		rtw89_err(rtwdev, "Write LTE fail!\n"); +		if (!test_bit(RTW89_FLAG_UNPLUGGED, rtwdev->flags)) +			rtw89_err(rtwdev, "Write LTE fail!\n");  		return ret;  	} @@ -6052,7 +6236,7 @@ int rtw89_mac_cfg_ctrl_path_v1(struct rtw89_dev *rtwdev, bool wl)  	if (wl)  		return 0; -	for (i = 0; i < RTW89_PHY_MAX; i++) { +	for (i = 0; i < RTW89_PHY_NUM; i++) {  		g[i].gnt_bt_sw_en = 1;  		g[i].gnt_bt = 1;  		g[i].gnt_wl_sw_en = 1; @@ -6443,6 +6627,7 @@ __rtw89_mac_set_tx_time(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwsta_  			u32 tx_time)  {  #define MAC_AX_DFLT_TX_TIME 5280 +	const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;  	u8 mac_idx = rtwsta_link->rtwvif_link->mac_idx;  	u32 max_tx_time = tx_time == 0 ? MAC_AX_DFLT_TX_TIME : tx_time;  	u32 reg; @@ -6450,7 +6635,7 @@ __rtw89_mac_set_tx_time(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwsta_  	if (rtwsta_link->cctl_tx_time) {  		rtwsta_link->ampdu_max_time = (max_tx_time - 512) >> 9; -		ret = rtw89_fw_h2c_txtime_cmac_tbl(rtwdev, rtwsta_link); +		ret = rtw89_chip_h2c_txtime_cmac_tbl(rtwdev, rtwsta_link);  	} else {  		ret = rtw89_mac_check_mac_en(rtwdev, mac_idx, RTW89_CMAC_SEL);  		if (ret) { @@ -6458,8 +6643,8 @@ __rtw89_mac_set_tx_time(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwsta_  			return ret;  		} -		reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_AMPDU_AGG_LIMIT, mac_idx); -		rtw89_write32_mask(rtwdev, reg, B_AX_AMPDU_MAX_TIME_MASK, +		reg = rtw89_mac_reg_by_idx(rtwdev, mac->agg_limit.addr, mac_idx); +		rtw89_write32_mask(rtwdev, reg, mac->agg_limit.mask,  				   max_tx_time >> 5);  	} @@ -6485,6 +6670,7 @@ int rtw89_mac_set_tx_time(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwst  int rtw89_mac_get_tx_time(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwsta_link,  			  u32 *tx_time)  { +	const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;  	u8 mac_idx = rtwsta_link->rtwvif_link->mac_idx;  	u32 reg;  	int ret = 0; @@ -6498,8 +6684,8 @@ int rtw89_mac_get_tx_time(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwst  			return ret;  		} -		reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_AMPDU_AGG_LIMIT, mac_idx); -		*tx_time = rtw89_read32_mask(rtwdev, reg, B_AX_AMPDU_MAX_TIME_MASK) << 5; +		reg = rtw89_mac_reg_by_idx(rtwdev, mac->agg_limit.addr, mac_idx); +		*tx_time = rtw89_read32_mask(rtwdev, reg, mac->agg_limit.mask) << 5;  	}  	return ret; @@ -6515,9 +6701,9 @@ int rtw89_mac_set_tx_retry_limit(struct rtw89_dev *rtwdev,  	if (!resume) {  		rtwsta_link->cctl_tx_retry_limit = true; -		ret = rtw89_fw_h2c_txtime_cmac_tbl(rtwdev, rtwsta_link); +		ret = rtw89_chip_h2c_txtime_cmac_tbl(rtwdev, rtwsta_link);  	} else { -		ret = rtw89_fw_h2c_txtime_cmac_tbl(rtwdev, rtwsta_link); +		ret = rtw89_chip_h2c_txtime_cmac_tbl(rtwdev, rtwsta_link);  		rtwsta_link->cctl_tx_retry_limit = false;  	} @@ -6527,6 +6713,7 @@ int rtw89_mac_set_tx_retry_limit(struct rtw89_dev *rtwdev,  int rtw89_mac_get_tx_retry_limit(struct rtw89_dev *rtwdev,  				 struct rtw89_sta_link *rtwsta_link, u8 *tx_retry)  { +	const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;  	u8 mac_idx = rtwsta_link->rtwvif_link->mac_idx;  	u32 reg;  	int ret = 0; @@ -6540,8 +6727,8 @@ int rtw89_mac_get_tx_retry_limit(struct rtw89_dev *rtwdev,  			return ret;  		} -		reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_TXCNT, mac_idx); -		*tx_retry = rtw89_read32_mask(rtwdev, reg, B_AX_L_TXCNT_LMT_MASK); +		reg = rtw89_mac_reg_by_idx(rtwdev, mac->txcnt_limit.addr, mac_idx); +		*tx_retry = rtw89_read32_mask(rtwdev, reg, mac->txcnt_limit.mask);  	}  	return ret; @@ -6774,10 +6961,16 @@ int rtw89_fwdl_check_path_ready_ax(struct rtw89_dev *rtwdev,  				   bool h2c_or_fwdl)  {  	u8 check = h2c_or_fwdl ? B_AX_H2C_PATH_RDY : B_AX_FWDL_PATH_RDY; +	u32 timeout;  	u8 val; +	if (rtwdev->hci.type == RTW89_HCI_TYPE_USB) +		timeout = FWDL_WAIT_CNT_USB; +	else +		timeout = FWDL_WAIT_CNT; +  	return read_poll_timeout_atomic(rtw89_read8, val, val & check, -					1, FWDL_WAIT_CNT, false, +					1, timeout, false,  					rtwdev, R_AX_WCPU_FW_CTRL);  } @@ -6800,6 +6993,7 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_ax = {  	.filter_model_addr = R_AX_FILTER_MODEL_ADDR,  	.indir_access_addr = R_AX_INDIR_ACCESS_ENTRY,  	.mem_base_addrs = rtw89_mac_mem_base_addrs_ax, +	.mem_page_size = MAC_MEM_DUMP_PAGE_SIZE_AX,  	.rx_fltr = R_AX_RX_FLTR_OPT,  	.port_base = &rtw89_port_base_ax,  	.agg_len_ht = R_AX_AGG_LEN_HT_0, @@ -6819,6 +7013,8 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_ax = {  		.mask = B_AX_RXTRIG_RU26_DIS,  	},  	.wow_ctrl = {.addr = R_AX_WOW_CTRL, .mask = B_AX_WOW_WOWEN,}, +	.agg_limit = {.addr = R_AX_AMPDU_AGG_LIMIT, .mask = B_AX_AMPDU_MAX_TIME_MASK,}, +	.txcnt_limit = {.addr = R_AX_TXCNT, .mask = B_AX_L_TXCNT_LMT_MASK,},  	.check_mac_en = rtw89_mac_check_mac_en_ax,  	.sys_init = sys_init_ax, @@ -6868,6 +7064,8 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_ax = {  	.is_txq_empty = mac_is_txq_empty_ax, +	.prep_chan_list = rtw89_hw_scan_prep_chan_list_ax, +	.free_chan_list = rtw89_hw_scan_free_chan_list_ax,  	.add_chan_list = rtw89_hw_scan_add_chan_list_ax,  	.add_chan_list_pno = rtw89_pno_scan_add_chan_list_ax,  	.scan_offload = rtw89_fw_h2c_scan_offload_ax, diff --git a/sys/contrib/dev/rtw89/mac.h b/sys/contrib/dev/rtw89/mac.h index 8edea96d037f..241e89983c4a 100644 --- a/sys/contrib/dev/rtw89/mac.h +++ b/sys/contrib/dev/rtw89/mac.h @@ -6,9 +6,12 @@  #define __RTW89_MAC_H__  #include "core.h" +#include "fw.h"  #include "reg.h" -#define MAC_MEM_DUMP_PAGE_SIZE 0x40000 +#define MAC_MEM_DUMP_PAGE_SIZE_AX 0x40000 +#define MAC_MEM_DUMP_PAGE_SIZE_BE 0x80000 +  #define ADDR_CAM_ENT_SIZE  0x40  #define ADDR_CAM_ENT_SHORT_SIZE 0x20  #define BSSID_CAM_ENT_SIZE 0x08 @@ -370,6 +373,7 @@ enum rtw89_mac_mem_sel {  	RTW89_MAC_MEM_TXD_FIFO_0_V1,  	RTW89_MAC_MEM_TXD_FIFO_1_V1,  	RTW89_MAC_MEM_WD_PAGE, +	RTW89_MAC_MEM_MLD_TBL,  	/* keep last */  	RTW89_MAC_MEM_NUM, @@ -427,6 +431,18 @@ enum rtw89_mac_c2h_mcc_func {  	NUM_OF_RTW89_MAC_C2H_FUNC_MCC,  }; +enum rtw89_mac_c2h_mlo_func { +	RTW89_MAC_C2H_FUNC_MLO_GET_TBL			= 0x0, +	RTW89_MAC_C2H_FUNC_MLO_EMLSR_TRANS_DONE		= 0x1, +	RTW89_MAC_C2H_FUNC_MLO_EMLSR_STA_CFG_DONE	= 0x2, +	RTW89_MAC_C2H_FUNC_MCMLO_RELINK_RPT		= 0x3, +	RTW89_MAC_C2H_FUNC_MCMLO_SN_SYNC_RPT		= 0x4, +	RTW89_MAC_C2H_FUNC_MLO_LINK_CFG_STAT		= 0x5, +	RTW89_MAC_C2H_FUNC_MLO_DM_DBG_DUMP		= 0x6, + +	NUM_OF_RTW89_MAC_C2H_FUNC_MLO, +}; +  enum rtw89_mac_c2h_mrc_func {  	RTW89_MAC_C2H_FUNC_MRC_TSF_RPT = 0,  	RTW89_MAC_C2H_FUNC_MRC_STATUS_RPT = 1, @@ -453,8 +469,10 @@ enum rtw89_mac_c2h_class {  	RTW89_MAC_C2H_CLASS_WOW = 0x3,  	RTW89_MAC_C2H_CLASS_MCC = 0x4,  	RTW89_MAC_C2H_CLASS_FWDBG = 0x5, +	RTW89_MAC_C2H_CLASS_MLO = 0xc,  	RTW89_MAC_C2H_CLASS_MRC = 0xe,  	RTW89_MAC_C2H_CLASS_AP = 0x18, +	RTW89_MAC_C2H_CLASS_ROLE = 0x1b,  	RTW89_MAC_C2H_CLASS_MAX,  }; @@ -907,6 +925,7 @@ struct rtw89_mac_size_set {  	const struct rtw89_dle_size wde_size18;  	const struct rtw89_dle_size wde_size19;  	const struct rtw89_dle_size wde_size23; +	const struct rtw89_dle_size wde_size25;  	const struct rtw89_dle_size ple_size0;  	const struct rtw89_dle_size ple_size0_v1;  	const struct rtw89_dle_size ple_size3_v1; @@ -916,6 +935,8 @@ struct rtw89_mac_size_set {  	const struct rtw89_dle_size ple_size9;  	const struct rtw89_dle_size ple_size18;  	const struct rtw89_dle_size ple_size19; +	const struct rtw89_dle_size ple_size32; +	const struct rtw89_dle_size ple_size33;  	const struct rtw89_wde_quota wde_qt0;  	const struct rtw89_wde_quota wde_qt0_v1;  	const struct rtw89_wde_quota wde_qt4; @@ -924,6 +945,7 @@ struct rtw89_mac_size_set {  	const struct rtw89_wde_quota wde_qt17;  	const struct rtw89_wde_quota wde_qt18;  	const struct rtw89_wde_quota wde_qt23; +	const struct rtw89_wde_quota wde_qt25;  	const struct rtw89_ple_quota ple_qt0;  	const struct rtw89_ple_quota ple_qt1;  	const struct rtw89_ple_quota ple_qt4; @@ -938,6 +960,10 @@ struct rtw89_mac_size_set {  	const struct rtw89_ple_quota ple_qt57;  	const struct rtw89_ple_quota ple_qt58;  	const struct rtw89_ple_quota ple_qt59; +	const struct rtw89_ple_quota ple_qt72; +	const struct rtw89_ple_quota ple_qt73; +	const struct rtw89_ple_quota ple_qt74; +	const struct rtw89_ple_quota ple_qt75;  	const struct rtw89_ple_quota ple_qt_52a_wow;  	const struct rtw89_ple_quota ple_qt_52b_wow;  	const struct rtw89_ple_quota ple_qt_52bt_wow; @@ -955,6 +981,7 @@ struct rtw89_mac_gen_def {  	u32 filter_model_addr;  	u32 indir_access_addr;  	const u32 *mem_base_addrs; +	u32 mem_page_size;  	u32 rx_fltr;  	const struct rtw89_port_reg *port_base;  	u32 agg_len_ht; @@ -964,6 +991,8 @@ struct rtw89_mac_gen_def {  	struct rtw89_reg_def bfee_ctrl;  	struct rtw89_reg_def narrow_bw_ru_dis;  	struct rtw89_reg_def wow_ctrl; +	struct rtw89_reg_def agg_limit; +	struct rtw89_reg_def txcnt_limit;  	int (*check_mac_en)(struct rtw89_dev *rtwdev, u8 band,  			    enum rtw89_mac_hwmod_sel sel); @@ -1029,8 +1058,11 @@ struct rtw89_mac_gen_def {  	bool (*is_txq_empty)(struct rtw89_dev *rtwdev); +	int (*prep_chan_list)(struct rtw89_dev *rtwdev, +			      struct rtw89_vif_link *rtwvif_link); +	void (*free_chan_list)(struct rtw89_dev *rtwdev);  	int (*add_chan_list)(struct rtw89_dev *rtwdev, -			     struct rtw89_vif_link *rtwvif_link, bool connected); +			     struct rtw89_vif_link *rtwvif_link);  	int (*add_chan_list_pno)(struct rtw89_dev *rtwdev,  				 struct rtw89_vif_link *rtwvif_link);  	int (*scan_offload)(struct rtw89_dev *rtwdev, @@ -1145,6 +1177,7 @@ rtw89_write32_port_set(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_l  	rtw89_write32_set(rtwdev, reg, bit);  } +int rtw89_mac_pwr_on(struct rtw89_dev *rtwdev);  void rtw89_mac_pwr_off(struct rtw89_dev *rtwdev);  int rtw89_mac_partial_init(struct rtw89_dev *rtwdev, bool include_bb);  int rtw89_mac_init(struct rtw89_dev *rtwdev); @@ -1185,6 +1218,8 @@ void rtw89_mac_port_cfg_rx_sync(struct rtw89_dev *rtwdev,  				struct rtw89_vif_link *rtwvif_link, bool en);  void rtw89_mac_set_he_obss_narrow_bw_ru(struct rtw89_dev *rtwdev,  					struct rtw89_vif_link *rtwvif_link); +void rtw89_mac_set_he_tb(struct rtw89_dev *rtwdev, +			 struct rtw89_vif_link *rtwvif_link);  void rtw89_mac_stop_ap(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link);  void rtw89_mac_enable_beacon_for_ap_vifs(struct rtw89_dev *rtwdev, bool en);  int rtw89_mac_remove_vif(struct rtw89_dev *rtwdev, struct rtw89_vif_link *vif); @@ -1549,4 +1584,28 @@ void rtw89_fwdl_secure_idmem_share_mode(struct rtw89_dev *rtwdev, u8 mode)  	return mac->fwdl_secure_idmem_share_mode(rtwdev, mode);  } + +static inline +int rtw89_mac_scan_offload(struct rtw89_dev *rtwdev, +			   struct rtw89_scan_option *option, +			   struct rtw89_vif_link *rtwvif_link, +			   bool wowlan) +{ +	const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; +	int ret; + +	ret = mac->scan_offload(rtwdev, option, rtwvif_link, wowlan); + +	if (option->enable) { +		/* +		 * At this point, new scan request is acknowledged by firmware, +		 * so scan events of previous scan request become obsoleted. +		 * Purge the queued scan events to prevent interference to +		 * current new request. +		 */ +		rtw89_fw_c2h_purge_obsoleted_scan_events(rtwdev); +	} + +	return ret; +}  #endif diff --git a/sys/contrib/dev/rtw89/mac80211.c b/sys/contrib/dev/rtw89/mac80211.c index 3eb074c591cf..c1ca6d741b32 100644 --- a/sys/contrib/dev/rtw89/mac80211.c +++ b/sys/contrib/dev/rtw89/mac80211.c @@ -16,10 +16,6 @@  #include "util.h"  #include "wow.h" -#if defined(__FreeBSD__) -DEFINE_GUARD(mutex, struct mutex *, mutex_lock(_T), mutex_unlock(_T)) -#endif -  static void rtw89_ops_tx(struct ieee80211_hw *hw,  			 struct ieee80211_tx_control *control,  			 struct sk_buff *skb) @@ -61,32 +57,30 @@ static void rtw89_ops_wake_tx_queue(struct ieee80211_hw *hw,  static int rtw89_ops_start(struct ieee80211_hw *hw)  {  	struct rtw89_dev *rtwdev = hw->priv; -	int ret; -	mutex_lock(&rtwdev->mutex); -	ret = rtw89_core_start(rtwdev); -	mutex_unlock(&rtwdev->mutex); +	lockdep_assert_wiphy(hw->wiphy); -	return ret; +	return rtw89_core_start(rtwdev);  }  static void rtw89_ops_stop(struct ieee80211_hw *hw, bool suspend)  {  	struct rtw89_dev *rtwdev = hw->priv; -	mutex_lock(&rtwdev->mutex); +	lockdep_assert_wiphy(hw->wiphy); +  	rtw89_core_stop(rtwdev); -	mutex_unlock(&rtwdev->mutex);  } -static int rtw89_ops_config(struct ieee80211_hw *hw, u32 changed) +static int rtw89_ops_config(struct ieee80211_hw *hw, int radio_idx, u32 changed)  {  	struct rtw89_dev *rtwdev = hw->priv; +	lockdep_assert_wiphy(hw->wiphy); +  	/* let previous ips work finish to ensure we don't leave ips twice */ -	cancel_work_sync(&rtwdev->ips_work); +	wiphy_work_cancel(hw->wiphy, &rtwdev->ips_work); -	mutex_lock(&rtwdev->mutex);  	rtw89_leave_ps_mode(rtwdev);  	if ((changed & IEEE80211_CONF_CHANGE_IDLE) && @@ -104,8 +98,6 @@ static int rtw89_ops_config(struct ieee80211_hw *hw, u32 changed)  	    !rtwdev->scanning)  		rtw89_enter_ips(rtwdev); -	mutex_unlock(&rtwdev->mutex); -  	return 0;  } @@ -119,14 +111,22 @@ static int __rtw89_ops_add_iface_link(struct rtw89_dev *rtwdev,  	rtw89_vif_type_mapping(rtwvif_link, false); -	INIT_WORK(&rtwvif_link->update_beacon_work, rtw89_core_update_beacon_work); +	wiphy_work_init(&rtwvif_link->update_beacon_work, rtw89_core_update_beacon_work); +	wiphy_delayed_work_init(&rtwvif_link->csa_beacon_work, rtw89_core_csa_beacon_work); +	wiphy_delayed_work_init(&rtwvif_link->mcc_gc_detect_beacon_work, +				rtw89_mcc_gc_detect_beacon_work); +  	INIT_LIST_HEAD(&rtwvif_link->general_pkt_list); +	rtw89_p2p_noa_once_init(rtwvif_link); +  	rtwvif_link->hit_rule = 0;  	rtwvif_link->bcn_hit_cond = 0;  	rtwvif_link->chanctx_assigned = false;  	rtwvif_link->chanctx_idx = RTW89_CHANCTX_0;  	rtwvif_link->reg_6ghz_power = RTW89_REG_6GHZ_POWER_DFLT; +	rtwvif_link->rand_tsf_done = false; +	rtwvif_link->detect_bcn_count = 0;  	rcu_read_lock(); @@ -146,9 +146,14 @@ static int __rtw89_ops_add_iface_link(struct rtw89_dev *rtwdev,  static void __rtw89_ops_remove_iface_link(struct rtw89_dev *rtwdev,  					  struct rtw89_vif_link *rtwvif_link)  { -	mutex_unlock(&rtwdev->mutex); -	cancel_work_sync(&rtwvif_link->update_beacon_work); -	mutex_lock(&rtwdev->mutex); +	lockdep_assert_wiphy(rtwdev->hw->wiphy); + +	wiphy_work_cancel(rtwdev->hw->wiphy, &rtwvif_link->update_beacon_work); +	wiphy_delayed_work_cancel(rtwdev->hw->wiphy, &rtwvif_link->csa_beacon_work); +	wiphy_delayed_work_cancel(rtwdev->hw->wiphy, +				  &rtwvif_link->mcc_gc_detect_beacon_work); + +	rtw89_p2p_noa_once_deinit(rtwvif_link);  	rtw89_leave_ps_mode(rtwdev); @@ -166,11 +171,11 @@ static int rtw89_ops_add_interface(struct ieee80211_hw *hw,  	u8 mac_id, port;  	int ret = 0; +	lockdep_assert_wiphy(hw->wiphy); +  	rtw89_debug(rtwdev, RTW89_DBG_STATE, "add vif %pM type %d, p2p %d\n",  		    vif->addr, vif->type, vif->p2p); -	mutex_lock(&rtwdev->mutex); -  	rtw89_leave_ips_by_hwflags(rtwdev);  	if (RTW89_CHK_FW_FEATURE(BEACON_FILTER, &rtwdev->fw)) @@ -178,10 +183,8 @@ static int rtw89_ops_add_interface(struct ieee80211_hw *hw,  				     IEEE80211_VIF_SUPPORTS_CQM_RSSI;  	mac_id = rtw89_acquire_mac_id(rtwdev); -	if (mac_id == RTW89_MAX_MAC_ID_NUM) { -		ret = -ENOSPC; -		goto err; -	} +	if (mac_id == RTW89_MAX_MAC_ID_NUM) +		return -ENOSPC;  	port = rtw89_core_acquire_bit_map(rtwdev->hw_port, RTW89_PORT_NUM);  	if (port == RTW89_PORT_NUM) { @@ -196,13 +199,14 @@ static int rtw89_ops_add_interface(struct ieee80211_hw *hw,  	if (!rtw89_rtwvif_in_list(rtwdev, rtwvif)) {  		list_add_tail(&rtwvif->list, &rtwdev->rtwvifs_list);  		INIT_LIST_HEAD(&rtwvif->mgnt_entry); +		INIT_LIST_HEAD(&rtwvif->dlink_pool);  	}  	ether_addr_copy(rtwvif->mac_addr, vif->addr);  	rtwvif->offchan = false;  	rtwvif->roc.state = RTW89_ROC_IDLE; -	INIT_DELAYED_WORK(&rtwvif->roc.roc_work, rtw89_roc_work); +	wiphy_delayed_work_init(&rtwvif->roc.roc_work, rtw89_roc_work);  	rtw89_traffic_stats_init(rtwdev, &rtwvif->stats); @@ -217,8 +221,6 @@ static int rtw89_ops_add_interface(struct ieee80211_hw *hw,  		goto unset_link;  	rtw89_recalc_lps(rtwdev); - -	mutex_unlock(&rtwdev->mutex);  	return 0;  unset_link: @@ -228,8 +230,6 @@ release_port:  	rtw89_core_release_bit_map(rtwdev->hw_port, port);  release_macid:  	rtw89_release_mac_id(rtwdev, mac_id); -err: -	mutex_unlock(&rtwdev->mutex);  	return ret;  } @@ -243,12 +243,12 @@ static void rtw89_ops_remove_interface(struct ieee80211_hw *hw,  	u8 port = rtw89_vif_get_main_port(rtwvif);  	struct rtw89_vif_link *rtwvif_link; +	lockdep_assert_wiphy(hw->wiphy); +  	rtw89_debug(rtwdev, RTW89_DBG_STATE, "remove vif %pM type %d p2p %d\n",  		    vif->addr, vif->type, vif->p2p); -	cancel_delayed_work_sync(&rtwvif->roc.roc_work); - -	mutex_lock(&rtwdev->mutex); +	wiphy_delayed_work_cancel(hw->wiphy, &rtwvif->roc.roc_work);  	rtwvif_link = rtwvif->links[RTW89_VIF_IDLE_LINK_ID];  	if (unlikely(!rtwvif_link)) { @@ -269,8 +269,6 @@ bottom:  	rtw89_recalc_lps(rtwdev);  	rtw89_enter_ips_by_hwflags(rtwdev); - -	mutex_unlock(&rtwdev->mutex);  }  static int rtw89_ops_change_interface(struct ieee80211_hw *hw, @@ -308,7 +306,8 @@ static void rtw89_ops_configure_filter(struct ieee80211_hw *hw,  	const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;  	u32 rx_fltr; -	mutex_lock(&rtwdev->mutex); +	lockdep_assert_wiphy(hw->wiphy); +  	rtw89_leave_ps_mode(rtwdev);  	*new_flags &= FIF_ALLMULTI | FIF_OTHER_BSS | FIF_FCSFAIL | @@ -371,14 +370,11 @@ static void rtw89_ops_configure_filter(struct ieee80211_hw *hw,  			   B_AX_RX_FLTR_CFG_MASK,  			   rx_fltr);  	if (!rtwdev->dbcc_en) -		goto out; +		return;  	rtw89_write32_mask(rtwdev,  			   rtw89_mac_reg_by_idx(rtwdev, mac->rx_fltr, RTW89_MAC_1),  			   B_AX_RX_FLTR_CFG_MASK,  			   rx_fltr); - -out: -	mutex_unlock(&rtwdev->mutex);  }  static const u8 ac_to_fw_idx[IEEE80211_NUM_ACS] = { @@ -497,6 +493,8 @@ static int __rtw89_ops_sta_add(struct rtw89_dev *rtwdev,  	int i;  	if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls) { +		rtwvif->mlo_mode = RTW89_MLO_MODE_MLSR; +  		/* for station mode, assign the mac_id from itself */  		macid = rtw89_vif_get_main_macid(rtwvif);  	} else { @@ -512,6 +510,8 @@ static int __rtw89_ops_sta_add(struct rtw89_dev *rtwdev,  	for (i = 0; i < ARRAY_SIZE(sta->txq); i++)  		rtw89_core_txq_init(rtwdev, sta->txq[i]); +	INIT_LIST_HEAD(&rtwsta->dlink_pool); +  	skb_queue_head_init(&rtwsta->roc_queue);  	bitmap_zero(rtwsta->pairwise_sec_cam_map, RTW89_MAX_SEC_CAM_NUM); @@ -674,6 +674,7 @@ static void __rtw89_ops_bss_link_assoc(struct rtw89_dev *rtwdev,  	rtw89_chip_cfg_txpwr_ul_tb_offset(rtwdev, rtwvif_link);  	rtw89_mac_port_update(rtwdev, rtwvif_link);  	rtw89_mac_set_he_obss_narrow_bw_ru(rtwdev, rtwvif_link); +	rtw89_mac_set_he_tb(rtwdev, rtwvif_link);  }  static void __rtw89_ops_bss_assoc(struct rtw89_dev *rtwdev, @@ -693,7 +694,8 @@ static void rtw89_ops_vif_cfg_changed(struct ieee80211_hw *hw,  	struct rtw89_dev *rtwdev = hw->priv;  	struct rtw89_vif *rtwvif = vif_to_rtwvif(vif); -	mutex_lock(&rtwdev->mutex); +	lockdep_assert_wiphy(hw->wiphy); +  	rtw89_leave_ps_mode(rtwdev);  	if (changed & BSS_CHANGED_ASSOC) { @@ -716,8 +718,6 @@ static void rtw89_ops_vif_cfg_changed(struct ieee80211_hw *hw,  	if (changed & BSS_CHANGED_ARP_FILTER)  		rtwvif->ip_addr = vif->cfg.arp_addr_list[0]; - -	mutex_unlock(&rtwdev->mutex);  }  static void rtw89_ops_link_info_changed(struct ieee80211_hw *hw, @@ -729,7 +729,8 @@ static void rtw89_ops_link_info_changed(struct ieee80211_hw *hw,  	struct rtw89_vif *rtwvif = vif_to_rtwvif(vif);  	struct rtw89_vif_link *rtwvif_link; -	mutex_lock(&rtwdev->mutex); +	lockdep_assert_wiphy(hw->wiphy); +  	rtw89_leave_ps_mode(rtwdev);  	rtwvif_link = rtwvif->links[conf->link_id]; @@ -737,7 +738,7 @@ static void rtw89_ops_link_info_changed(struct ieee80211_hw *hw,  		rtw89_err(rtwdev,  			  "%s: rtwvif link (link_id %u) is not active\n",  			  __func__, conf->link_id); -		goto out; +		return;  	}  	if (changed & BSS_CHANGED_BSSID) { @@ -767,9 +768,6 @@ static void rtw89_ops_link_info_changed(struct ieee80211_hw *hw,  	if (changed & BSS_CHANGED_TPE)  		rtw89_reg_6ghz_recalc(rtwdev, rtwvif_link, true); - -out: -	mutex_unlock(&rtwdev->mutex);  }  static int rtw89_ops_start_ap(struct ieee80211_hw *hw, @@ -782,22 +780,19 @@ static int rtw89_ops_start_ap(struct ieee80211_hw *hw,  	const struct rtw89_chan *chan;  	int ret = 0; -	mutex_lock(&rtwdev->mutex); +	lockdep_assert_wiphy(hw->wiphy);  	rtwvif_link = rtwvif->links[link_conf->link_id];  	if (unlikely(!rtwvif_link)) {  		rtw89_err(rtwdev,  			  "%s: rtwvif link (link_id %u) is not active\n",  			  __func__, link_conf->link_id); -		ret = -ENOLINK; -		goto out; +		return -ENOLINK;  	}  	chan = rtw89_chan_get(rtwdev, rtwvif_link->chanctx_idx); -	if (chan->band_type == RTW89_BAND_6G) { -		mutex_unlock(&rtwdev->mutex); +	if (chan->band_type == RTW89_BAND_6G)  		return -EOPNOTSUPP; -	}  	if (rtwdev->scanning)  		rtw89_hw_scan_abort(rtwdev, rtwdev->scan_info.scanning_vif); @@ -814,15 +809,12 @@ static int rtw89_ops_start_ap(struct ieee80211_hw *hw,  	if (RTW89_CHK_FW_FEATURE(NOTIFY_AP_INFO, &rtwdev->fw)) {  		ret = rtw89_fw_h2c_ap_info_refcount(rtwdev, true);  		if (ret) -			goto out; +			return ret;  	}  	rtw89_queue_chanctx_work(rtwdev); -out: -	mutex_unlock(&rtwdev->mutex); - -	return ret; +	return 0;  }  static @@ -833,14 +825,14 @@ void rtw89_ops_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif,  	struct rtw89_vif *rtwvif = vif_to_rtwvif(vif);  	struct rtw89_vif_link *rtwvif_link; -	mutex_lock(&rtwdev->mutex); +	lockdep_assert_wiphy(hw->wiphy);  	rtwvif_link = rtwvif->links[link_conf->link_id];  	if (unlikely(!rtwvif_link)) {  		rtw89_err(rtwdev,  			  "%s: rtwvif link (link_id %u) is not active\n",  			  __func__, link_conf->link_id); -		goto out; +		return;  	}  	if (RTW89_CHK_FW_FEATURE(NOTIFY_AP_INFO, &rtwdev->fw)) @@ -849,22 +841,18 @@ void rtw89_ops_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif,  	rtw89_mac_stop_ap(rtwdev, rtwvif_link);  	rtw89_chip_h2c_assoc_cmac_tbl(rtwdev, rtwvif_link, NULL);  	rtw89_fw_h2c_join_info(rtwdev, rtwvif_link, NULL, true); - -out: -	mutex_unlock(&rtwdev->mutex);  }  static int rtw89_ops_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta,  			     bool set)  { -	struct rtw89_dev *rtwdev = hw->priv;  	struct rtw89_sta *rtwsta = sta_to_rtwsta(sta);  	struct rtw89_vif *rtwvif = rtwsta->rtwvif;  	struct rtw89_vif_link *rtwvif_link;  	unsigned int link_id;  	rtw89_vif_for_each_link(rtwvif, rtwvif_link, link_id) -		ieee80211_queue_work(rtwdev->hw, &rtwvif_link->update_beacon_work); +		wiphy_work_queue(hw->wiphy, &rtwvif_link->update_beacon_work);  	return 0;  } @@ -877,9 +865,9 @@ static int rtw89_ops_conf_tx(struct ieee80211_hw *hw,  	struct rtw89_dev *rtwdev = hw->priv;  	struct rtw89_vif *rtwvif = vif_to_rtwvif(vif);  	struct rtw89_vif_link *rtwvif_link; -	int ret = 0; -	mutex_lock(&rtwdev->mutex); +	lockdep_assert_wiphy(hw->wiphy); +  	rtw89_leave_ps_mode(rtwdev);  	rtwvif_link = rtwvif->links[link_id]; @@ -887,17 +875,13 @@ static int rtw89_ops_conf_tx(struct ieee80211_hw *hw,  		rtw89_err(rtwdev,  			  "%s: rtwvif link (link_id %u) is not active\n",  			  __func__, link_id); -		ret = -ENOLINK; -		goto out; +		return -ENOLINK;  	}  	rtwvif_link->tx_params[ac] = *params;  	__rtw89_conf_tx(rtwdev, rtwvif_link, ac); -out: -	mutex_unlock(&rtwdev->mutex); - -	return ret; +	return 0;  }  static int __rtw89_ops_sta_state(struct ieee80211_hw *hw, @@ -941,14 +925,11 @@ static int rtw89_ops_sta_state(struct ieee80211_hw *hw,  			       enum ieee80211_sta_state new_state)  {  	struct rtw89_dev *rtwdev = hw->priv; -	int ret; -	mutex_lock(&rtwdev->mutex); -	rtw89_leave_ps_mode(rtwdev); -	ret = __rtw89_ops_sta_state(hw, vif, sta, old_state, new_state); -	mutex_unlock(&rtwdev->mutex); +	lockdep_assert_wiphy(hw->wiphy); -	return ret; +	rtw89_leave_ps_mode(rtwdev); +	return __rtw89_ops_sta_state(hw, vif, sta, old_state, new_state);  }  static int rtw89_ops_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, @@ -957,9 +938,10 @@ static int rtw89_ops_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,  			     struct ieee80211_key_conf *key)  {  	struct rtw89_dev *rtwdev = hw->priv; -	int ret = 0; +	int ret; + +	lockdep_assert_wiphy(hw->wiphy); -	mutex_lock(&rtwdev->mutex);  	rtw89_leave_ps_mode(rtwdev);  	switch (cmd) { @@ -968,7 +950,7 @@ static int rtw89_ops_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,  		ret = rtw89_cam_sec_key_add(rtwdev, vif, sta, key);  		if (ret && ret != -EOPNOTSUPP) {  			rtw89_err(rtwdev, "failed to add key to sec cam\n"); -			goto out; +			return ret;  		}  		break;  	case DISABLE_KEY: @@ -978,14 +960,11 @@ static int rtw89_ops_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,  		ret = rtw89_cam_sec_key_del(rtwdev, vif, sta, key, true);  		if (ret) {  			rtw89_err(rtwdev, "failed to remove key from sec cam\n"); -			goto out; +			return ret;  		}  		break;  	} -out: -	mutex_unlock(&rtwdev->mutex); -  	return ret;  } @@ -1001,38 +980,32 @@ static int rtw89_ops_ampdu_action(struct ieee80211_hw *hw,  	struct ieee80211_txq *txq = sta->txq[tid];  	struct rtw89_txq *rtwtxq = (struct rtw89_txq *)txq->drv_priv; +	lockdep_assert_wiphy(rtwdev->hw->wiphy); +  	switch (params->action) {  	case IEEE80211_AMPDU_TX_START:  		return IEEE80211_AMPDU_TX_START_IMMEDIATE;  	case IEEE80211_AMPDU_TX_STOP_CONT:  	case IEEE80211_AMPDU_TX_STOP_FLUSH:  	case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: -		mutex_lock(&rtwdev->mutex);  		clear_bit(RTW89_TXQ_F_AMPDU, &rtwtxq->flags);  		clear_bit(tid, rtwsta->ampdu_map);  		rtw89_chip_h2c_ampdu_cmac_tbl(rtwdev, rtwvif, rtwsta); -		mutex_unlock(&rtwdev->mutex);  		ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);  		break;  	case IEEE80211_AMPDU_TX_OPERATIONAL: -		mutex_lock(&rtwdev->mutex);  		set_bit(RTW89_TXQ_F_AMPDU, &rtwtxq->flags);  		rtwsta->ampdu_params[tid].agg_num = params->buf_size;  		rtwsta->ampdu_params[tid].amsdu = params->amsdu;  		set_bit(tid, rtwsta->ampdu_map);  		rtw89_leave_ps_mode(rtwdev);  		rtw89_chip_h2c_ampdu_cmac_tbl(rtwdev, rtwvif, rtwsta); -		mutex_unlock(&rtwdev->mutex);  		break;  	case IEEE80211_AMPDU_RX_START: -		mutex_lock(&rtwdev->mutex);  		rtw89_chip_h2c_ba_cam(rtwdev, rtwsta, true, params); -		mutex_unlock(&rtwdev->mutex);  		break;  	case IEEE80211_AMPDU_RX_STOP: -		mutex_lock(&rtwdev->mutex);  		rtw89_chip_h2c_ba_cam(rtwdev, rtwsta, false, params); -		mutex_unlock(&rtwdev->mutex);  		break;  	default:  		WARN_ON(1); @@ -1042,15 +1015,16 @@ static int rtw89_ops_ampdu_action(struct ieee80211_hw *hw,  	return 0;  } -static int rtw89_ops_set_rts_threshold(struct ieee80211_hw *hw, u32 value) +static int rtw89_ops_set_rts_threshold(struct ieee80211_hw *hw, int radio_idx, +				       u32 value)  {  	struct rtw89_dev *rtwdev = hw->priv; -	mutex_lock(&rtwdev->mutex); +	lockdep_assert_wiphy(hw->wiphy); +  	rtw89_leave_ps_mode(rtwdev);  	if (test_bit(RTW89_FLAG_POWERON, rtwdev->flags))  		rtw89_mac_update_rts_threshold(rtwdev); -	mutex_unlock(&rtwdev->mutex);  	return 0;  } @@ -1063,7 +1037,7 @@ static void rtw89_ops_sta_statistics(struct ieee80211_hw *hw,  	struct rtw89_sta *rtwsta = sta_to_rtwsta(sta);  	struct rtw89_sta_link *rtwsta_link; -	rtwsta_link = rtw89_sta_get_link_inst(rtwsta, 0); +	rtwsta_link = rtw89_get_designated_link(rtwsta);  	if (unlikely(!rtwsta_link))  		return; @@ -1090,7 +1064,8 @@ static void rtw89_ops_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,  {  	struct rtw89_dev *rtwdev = hw->priv; -	mutex_lock(&rtwdev->mutex); +	lockdep_assert_wiphy(hw->wiphy); +  	rtw89_leave_lps(rtwdev);  	rtw89_hci_flush_queues(rtwdev, queues, drop); @@ -1098,8 +1073,6 @@ static void rtw89_ops_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,  		__rtw89_drop_packets(rtwdev, vif);  	else  		rtw89_mac_flush_txq(rtwdev, queues, drop); - -	mutex_unlock(&rtwdev->mutex);  }  struct rtw89_iter_bitrate_mask_data { @@ -1146,20 +1119,22 @@ static int rtw89_ops_set_bitrate_mask(struct ieee80211_hw *hw,  {  	struct rtw89_dev *rtwdev = hw->priv; -	mutex_lock(&rtwdev->mutex); +	lockdep_assert_wiphy(hw->wiphy); +  	rtw89_phy_rate_pattern_vif(rtwdev, vif, mask);  	rtw89_ra_mask_info_update(rtwdev, vif, mask); -	mutex_unlock(&rtwdev->mutex);  	return 0;  }  static -int rtw89_ops_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) +int rtw89_ops_set_antenna(struct ieee80211_hw *hw, int radio_idx, u32 tx_ant, u32 rx_ant)  {  	struct rtw89_dev *rtwdev = hw->priv;  	struct rtw89_hal *hal = &rtwdev->hal; +	lockdep_assert_wiphy(hw->wiphy); +  	if (hal->ant_diversity) {  		if (tx_ant != rx_ant || hweight32(tx_ant) != 1)  			return -EINVAL; @@ -1167,18 +1142,17 @@ int rtw89_ops_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)  		return -EINVAL;  	} -	mutex_lock(&rtwdev->mutex);  	hal->antenna_tx = tx_ant;  	hal->antenna_rx = rx_ant;  	hal->tx_path_diversity = false;  	hal->ant_diversity_fixed = true; -	mutex_unlock(&rtwdev->mutex);  	return 0;  }  static -int rtw89_ops_get_antenna(struct ieee80211_hw *hw,  u32 *tx_ant, u32 *rx_ant) +int rtw89_ops_get_antenna(struct ieee80211_hw *hw, int radio_idx, u32 *tx_ant, +			  u32 *rx_ant)  {  	struct rtw89_dev *rtwdev = hw->priv;  	struct rtw89_hal *hal = &rtwdev->hal; @@ -1197,18 +1171,17 @@ static void rtw89_ops_sw_scan_start(struct ieee80211_hw *hw,  	struct rtw89_vif *rtwvif = vif_to_rtwvif(vif);  	struct rtw89_vif_link *rtwvif_link; -	mutex_lock(&rtwdev->mutex); +	lockdep_assert_wiphy(hw->wiphy); -	rtwvif_link = rtw89_vif_get_link_inst(rtwvif, 0); +	rtwvif_link = rtw89_get_designated_link(rtwvif);  	if (unlikely(!rtwvif_link)) { -		rtw89_err(rtwdev, "sw scan start: find no link on HW-0\n"); -		goto out; +		rtw89_err(rtwdev, "sw scan start: find no designated link\n"); +		return;  	} -	rtw89_core_scan_start(rtwdev, rtwvif_link, mac_addr, false); +	rtw89_leave_lps(rtwdev); -out: -	mutex_unlock(&rtwdev->mutex); +	rtw89_core_scan_start(rtwdev, rtwvif_link, mac_addr, false);  }  static void rtw89_ops_sw_scan_complete(struct ieee80211_hw *hw, @@ -1218,18 +1191,15 @@ static void rtw89_ops_sw_scan_complete(struct ieee80211_hw *hw,  	struct rtw89_vif *rtwvif = vif_to_rtwvif(vif);  	struct rtw89_vif_link *rtwvif_link; -	mutex_lock(&rtwdev->mutex); +	lockdep_assert_wiphy(hw->wiphy); -	rtwvif_link = rtw89_vif_get_link_inst(rtwvif, 0); +	rtwvif_link = rtw89_get_designated_link(rtwvif);  	if (unlikely(!rtwvif_link)) { -		rtw89_err(rtwdev, "sw scan complete: find no link on HW-0\n"); -		goto out; +		rtw89_err(rtwdev, "sw scan complete: find no designated link\n"); +		return;  	}  	rtw89_core_scan_complete(rtwdev, rtwvif_link, false); - -out: -	mutex_unlock(&rtwdev->mutex);  }  static void rtw89_ops_reconfig_complete(struct ieee80211_hw *hw, @@ -1249,33 +1219,33 @@ static int rtw89_ops_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,  	struct rtw89_vif_link *rtwvif_link;  	int ret; +	lockdep_assert_wiphy(hw->wiphy); +  	if (!RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD, &rtwdev->fw))  		return 1; -	mutex_lock(&rtwdev->mutex); - -	if (rtwdev->scanning || rtwvif->offchan) { -		ret = -EBUSY; -		goto out; -	} +	if (rtwdev->scanning || rtwvif->offchan) +		return -EBUSY; -	rtwvif_link = rtw89_vif_get_link_inst(rtwvif, 0); +	rtwvif_link = rtw89_get_designated_link(rtwvif);  	if (unlikely(!rtwvif_link)) { -		rtw89_err(rtwdev, "hw scan: find no link on HW-0\n"); -		ret = -ENOLINK; -		goto out; +		rtw89_err(rtwdev, "hw scan: find no designated link\n"); +		return -ENOLINK;  	} -	rtw89_hw_scan_start(rtwdev, rtwvif_link, req); +	rtw89_leave_lps(rtwdev); +	rtw89_leave_ips_by_hwflags(rtwdev); + +	ret = rtw89_hw_scan_start(rtwdev, rtwvif_link, req); +	if (ret) +		return ret; +  	ret = rtw89_hw_scan_offload(rtwdev, rtwvif_link, true);  	if (ret) {  		rtw89_hw_scan_abort(rtwdev, rtwvif_link);  		rtw89_err(rtwdev, "HW scan failed with status: %d\n", ret);  	} -out: -	mutex_unlock(&rtwdev->mutex); -  	return ret;  } @@ -1286,24 +1256,21 @@ static void rtw89_ops_cancel_hw_scan(struct ieee80211_hw *hw,  	struct rtw89_vif *rtwvif = vif_to_rtwvif(vif);  	struct rtw89_vif_link *rtwvif_link; +	lockdep_assert_wiphy(hw->wiphy); +  	if (!RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD, &rtwdev->fw))  		return; -	mutex_lock(&rtwdev->mutex); -  	if (!rtwdev->scanning) -		goto out; +		return; -	rtwvif_link = rtw89_vif_get_link_inst(rtwvif, 0); +	rtwvif_link = rtw89_get_designated_link(rtwvif);  	if (unlikely(!rtwvif_link)) { -		rtw89_err(rtwdev, "cancel hw scan: find no link on HW-0\n"); -		goto out; +		rtw89_err(rtwdev, "cancel hw scan: find no designated link\n"); +		return;  	}  	rtw89_hw_scan_abort(rtwdev, rtwvif_link); - -out: -	mutex_unlock(&rtwdev->mutex);  }  static void rtw89_ops_sta_rc_update(struct ieee80211_hw *hw, @@ -1326,13 +1293,10 @@ static int rtw89_ops_add_chanctx(struct ieee80211_hw *hw,  				 struct ieee80211_chanctx_conf *ctx)  {  	struct rtw89_dev *rtwdev = hw->priv; -	int ret; -	mutex_lock(&rtwdev->mutex); -	ret = rtw89_chanctx_ops_add(rtwdev, ctx); -	mutex_unlock(&rtwdev->mutex); +	lockdep_assert_wiphy(hw->wiphy); -	return ret; +	return rtw89_chanctx_ops_add(rtwdev, ctx);  }  static void rtw89_ops_remove_chanctx(struct ieee80211_hw *hw, @@ -1340,9 +1304,9 @@ static void rtw89_ops_remove_chanctx(struct ieee80211_hw *hw,  {  	struct rtw89_dev *rtwdev = hw->priv; -	mutex_lock(&rtwdev->mutex); +	lockdep_assert_wiphy(hw->wiphy); +  	rtw89_chanctx_ops_remove(rtwdev, ctx); -	mutex_unlock(&rtwdev->mutex);  }  static void rtw89_ops_change_chanctx(struct ieee80211_hw *hw, @@ -1351,9 +1315,9 @@ static void rtw89_ops_change_chanctx(struct ieee80211_hw *hw,  {  	struct rtw89_dev *rtwdev = hw->priv; -	mutex_lock(&rtwdev->mutex); +	lockdep_assert_wiphy(hw->wiphy); +  	rtw89_chanctx_ops_change(rtwdev, ctx, changed); -	mutex_unlock(&rtwdev->mutex);  }  static int rtw89_ops_assign_vif_chanctx(struct ieee80211_hw *hw, @@ -1364,25 +1328,18 @@ static int rtw89_ops_assign_vif_chanctx(struct ieee80211_hw *hw,  	struct rtw89_dev *rtwdev = hw->priv;  	struct rtw89_vif *rtwvif = vif_to_rtwvif(vif);  	struct rtw89_vif_link *rtwvif_link; -	int ret; -	mutex_lock(&rtwdev->mutex); +	lockdep_assert_wiphy(hw->wiphy);  	rtwvif_link = rtwvif->links[link_conf->link_id];  	if (unlikely(!rtwvif_link)) {  		rtw89_err(rtwdev,  			  "%s: rtwvif link (link_id %u) is not active\n",  			  __func__, link_conf->link_id); -		ret = -ENOLINK; -		goto out; +		return -ENOLINK;  	} -	ret = rtw89_chanctx_ops_assign_vif(rtwdev, rtwvif_link, ctx); - -out: -	mutex_unlock(&rtwdev->mutex); - -	return ret; +	return rtw89_chanctx_ops_assign_vif(rtwdev, rtwvif_link, ctx);  }  static void rtw89_ops_unassign_vif_chanctx(struct ieee80211_hw *hw, @@ -1394,11 +1351,10 @@ static void rtw89_ops_unassign_vif_chanctx(struct ieee80211_hw *hw,  	struct rtw89_vif *rtwvif = vif_to_rtwvif(vif);  	struct rtw89_vif_link *rtwvif_link; -	mutex_lock(&rtwdev->mutex); +	lockdep_assert_wiphy(hw->wiphy);  	rtwvif_link = rtwvif->links[link_conf->link_id];  	if (unlikely(!rtwvif_link)) { -		mutex_unlock(&rtwdev->mutex);  		rtw89_err(rtwdev,  			  "%s: rtwvif link (link_id %u) is not active\n",  			  __func__, link_conf->link_id); @@ -1406,7 +1362,73 @@ static void rtw89_ops_unassign_vif_chanctx(struct ieee80211_hw *hw,  	}  	rtw89_chanctx_ops_unassign_vif(rtwdev, rtwvif_link, ctx); -	mutex_unlock(&rtwdev->mutex); +} + +static +int rtw89_ops_switch_vif_chanctx(struct ieee80211_hw *hw, +				 struct ieee80211_vif_chanctx_switch *vifs, +				 int n_vifs, +				 enum ieee80211_chanctx_switch_mode mode) +{ +	struct rtw89_dev *rtwdev = hw->priv; +	bool replace; +	int ret; +	int i; + +	lockdep_assert_wiphy(hw->wiphy); + +	switch (mode) { +	case CHANCTX_SWMODE_REASSIGN_VIF: +		replace = false; +		break; +	case CHANCTX_SWMODE_SWAP_CONTEXTS: +		replace = true; +		break; +	default: +		return -EOPNOTSUPP; +	} + +	for (i = 0; i < n_vifs; i++) { +		struct ieee80211_vif_chanctx_switch *p = &vifs[i]; +		struct ieee80211_bss_conf *link_conf = p->link_conf; +		struct rtw89_vif *rtwvif = vif_to_rtwvif(p->vif); +		struct rtw89_vif_link *rtwvif_link; + +		rtwvif_link = rtwvif->links[link_conf->link_id]; +		if (unlikely(!rtwvif_link)) { +			rtw89_err(rtwdev, +				  "%s: rtwvif link (link_id %u) is not active\n", +				  __func__, link_conf->link_id); +			return -ENOLINK; +		} + +		ret = rtw89_chanctx_ops_reassign_vif(rtwdev, rtwvif_link, +						     p->old_ctx, p->new_ctx, +						     replace); +		if (ret) +			return ret; +	} + +	return 0; +} + +static void rtw89_ops_channel_switch_beacon(struct ieee80211_hw *hw, +					    struct ieee80211_vif *vif, +					    struct cfg80211_chan_def *chandef) +{ +	struct rtw89_vif *rtwvif = vif_to_rtwvif(vif); +	struct rtw89_dev *rtwdev = hw->priv; +	struct rtw89_vif_link *rtwvif_link; + +	BUILD_BUG_ON(RTW89_MLD_NON_STA_LINK_NUM != 1); + +	rtwvif_link = rtw89_vif_get_link_inst(rtwvif, 0); +	if (unlikely(!rtwvif_link)) { +		rtw89_err(rtwdev, "chsw bcn: find no link on HW-0\n"); +		return; +	} + +	wiphy_delayed_work_queue(hw->wiphy, &rtwvif_link->csa_beacon_work, 0);  }  static int rtw89_ops_remain_on_channel(struct ieee80211_hw *hw, @@ -1419,13 +1441,12 @@ static int rtw89_ops_remain_on_channel(struct ieee80211_hw *hw,  	struct rtw89_vif *rtwvif = vif_to_rtwvif_safe(vif);  	struct rtw89_roc *roc = &rtwvif->roc; +	lockdep_assert_wiphy(hw->wiphy); +  	if (!rtwvif)  		return -EINVAL; -	mutex_lock(&rtwdev->mutex); -  	if (roc->state != RTW89_ROC_IDLE) { -		mutex_unlock(&rtwdev->mutex);  		return -EBUSY;  	} @@ -1443,8 +1464,6 @@ static int rtw89_ops_remain_on_channel(struct ieee80211_hw *hw,  	rtw89_roc_start(rtwdev, rtwvif); -	mutex_unlock(&rtwdev->mutex); -  	return 0;  } @@ -1454,14 +1473,14 @@ static int rtw89_ops_cancel_remain_on_channel(struct ieee80211_hw *hw,  	struct rtw89_dev *rtwdev = hw->priv;  	struct rtw89_vif *rtwvif = vif_to_rtwvif_safe(vif); +	lockdep_assert_wiphy(hw->wiphy); +  	if (!rtwvif)  		return -EINVAL; -	cancel_delayed_work_sync(&rtwvif->roc.roc_work); +	wiphy_delayed_work_cancel(hw->wiphy, &rtwvif->roc.roc_work); -	mutex_lock(&rtwdev->mutex);  	rtw89_roc_end(rtwdev, rtwvif); -	mutex_unlock(&rtwdev->mutex);  	return 0;  } @@ -1482,14 +1501,14 @@ static int rtw89_ops_set_tid_config(struct ieee80211_hw *hw,  {  	struct rtw89_dev *rtwdev = hw->priv; -	mutex_lock(&rtwdev->mutex); +	lockdep_assert_wiphy(hw->wiphy); +  	if (sta)  		rtw89_core_set_tid_config(rtwdev, sta, tid_config);  	else  		ieee80211_iterate_stations_atomic(rtwdev->hw,  						  rtw89_set_tid_config_iter,  						  tid_config); -	mutex_unlock(&rtwdev->mutex);  	return 0;  } @@ -1513,7 +1532,7 @@ static bool rtw89_ops_can_activate_links(struct ieee80211_hw *hw,  {  	struct rtw89_dev *rtwdev = hw->priv; -	guard(mutex)(&rtwdev->mutex); +	lockdep_assert_wiphy(hw->wiphy);  	return rtw89_can_work_on_links(rtwdev, vif, active_links);  } @@ -1575,7 +1594,7 @@ int rtw89_ops_change_vif_links(struct ieee80211_hw *hw,  	int ret = 0;  	int i; -	guard(mutex)(&rtwdev->mutex); +	lockdep_assert_wiphy(hw->wiphy);  	rtw89_debug(rtwdev, RTW89_DBG_STATE,  		    "%s: old_links (0x%08x) -> new_links (0x%08x)\n", @@ -1724,7 +1743,7 @@ int rtw89_ops_change_sta_links(struct ieee80211_hw *hw,  	unsigned long set_links = new_links & ~old_links;  	int ret = 0; -	guard(mutex)(&rtwdev->mutex); +	lockdep_assert_wiphy(hw->wiphy);  	rtw89_debug(rtwdev, RTW89_DBG_STATE,  		    "%s: old_links (0x%08x) -> new_links (0x%08x)\n", @@ -1754,16 +1773,16 @@ static int rtw89_ops_suspend(struct ieee80211_hw *hw,  	struct rtw89_dev *rtwdev = hw->priv;  	int ret; -	set_bit(RTW89_FLAG_FORBIDDEN_TRACK_WROK, rtwdev->flags); -	cancel_delayed_work_sync(&rtwdev->track_work); +	lockdep_assert_wiphy(hw->wiphy); -	mutex_lock(&rtwdev->mutex); -	ret = rtw89_wow_suspend(rtwdev, wowlan); -	mutex_unlock(&rtwdev->mutex); +	set_bit(RTW89_FLAG_FORBIDDEN_TRACK_WORK, rtwdev->flags); +	wiphy_delayed_work_cancel(hw->wiphy, &rtwdev->track_work); +	wiphy_delayed_work_cancel(hw->wiphy, &rtwdev->track_ps_work); +	ret = rtw89_wow_suspend(rtwdev, wowlan);  	if (ret) {  		rtw89_warn(rtwdev, "failed to suspend for wow %d\n", ret); -		clear_bit(RTW89_FLAG_FORBIDDEN_TRACK_WROK, rtwdev->flags); +		clear_bit(RTW89_FLAG_FORBIDDEN_TRACK_WORK, rtwdev->flags);  		return 1;  	} @@ -1775,15 +1794,17 @@ static int rtw89_ops_resume(struct ieee80211_hw *hw)  	struct rtw89_dev *rtwdev = hw->priv;  	int ret; -	mutex_lock(&rtwdev->mutex); +	lockdep_assert_wiphy(hw->wiphy); +  	ret = rtw89_wow_resume(rtwdev);  	if (ret)  		rtw89_warn(rtwdev, "failed to resume for wow %d\n", ret); -	mutex_unlock(&rtwdev->mutex); -	clear_bit(RTW89_FLAG_FORBIDDEN_TRACK_WROK, rtwdev->flags); -	ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->track_work, -				     RTW89_TRACK_WORK_PERIOD); +	clear_bit(RTW89_FLAG_FORBIDDEN_TRACK_WORK, rtwdev->flags); +	wiphy_delayed_work_queue(hw->wiphy, &rtwdev->track_work, +				 RTW89_TRACK_WORK_PERIOD); +	wiphy_delayed_work_queue(hw->wiphy, &rtwdev->track_ps_work, +				 RTW89_TRACK_PS_WORK_PERIOD);  	return ret ? 1 : 0;  } @@ -1803,18 +1824,16 @@ static void rtw89_set_rekey_data(struct ieee80211_hw *hw,  	struct rtw89_wow_param *rtw_wow = &rtwdev->wow;  	struct rtw89_wow_gtk_info *gtk_info = &rtw_wow->gtk_info; +	lockdep_assert_wiphy(hw->wiphy); +  	if (data->kek_len > sizeof(gtk_info->kek) ||  	    data->kck_len > sizeof(gtk_info->kck)) {  		rtw89_warn(rtwdev, "kek or kck length over fw limit\n");  		return;  	} -	mutex_lock(&rtwdev->mutex); -  	memcpy(gtk_info->kek, data->kek, data->kek_len);  	memcpy(gtk_info->kck, data->kck, data->kck_len); - -	mutex_unlock(&rtwdev->mutex);  }  #endif @@ -1822,16 +1841,13 @@ static void rtw89_ops_rfkill_poll(struct ieee80211_hw *hw)  {  	struct rtw89_dev *rtwdev = hw->priv; -	mutex_lock(&rtwdev->mutex); +	lockdep_assert_wiphy(hw->wiphy);  	/* wl_disable GPIO get floating when entering LPS */  	if (test_bit(RTW89_FLAG_RUNNING, rtwdev->flags)) -		goto out; +		return;  	rtw89_core_rfkill_poll(rtwdev, false); - -out: -	mutex_unlock(&rtwdev->mutex);  }  const struct ieee80211_ops rtw89_ops = { @@ -1869,6 +1885,8 @@ const struct ieee80211_ops rtw89_ops = {  	.change_chanctx		= rtw89_ops_change_chanctx,  	.assign_vif_chanctx	= rtw89_ops_assign_vif_chanctx,  	.unassign_vif_chanctx	= rtw89_ops_unassign_vif_chanctx, +	.switch_vif_chanctx	= rtw89_ops_switch_vif_chanctx, +	.channel_switch_beacon	= rtw89_ops_channel_switch_beacon,  	.remain_on_channel		= rtw89_ops_remain_on_channel,  	.cancel_remain_on_channel	= rtw89_ops_cancel_remain_on_channel,  	.set_sar_specs		= rtw89_ops_set_sar_specs, diff --git a/sys/contrib/dev/rtw89/mac_be.c b/sys/contrib/dev/rtw89/mac_be.c index 2dbdeae904ad..0078080b3999 100644 --- a/sys/contrib/dev/rtw89/mac_be.c +++ b/sys/contrib/dev/rtw89/mac_be.c @@ -29,6 +29,7 @@ static const u32 rtw89_mac_mem_base_addrs_be[RTW89_MAC_MEM_NUM] = {  	[RTW89_MAC_MEM_CPU_LOCAL]	= CPU_LOCAL_BASE_ADDR_BE,  	[RTW89_MAC_MEM_BSSID_CAM]	= BSSID_CAM_BASE_ADDR_BE,  	[RTW89_MAC_MEM_WD_PAGE]		= WD_PAGE_BASE_ADDR_BE, +	[RTW89_MAC_MEM_MLD_TBL]		= MLD_TBL_BASE_ADDR_BE,  };  static const struct rtw89_port_reg rtw89_port_base_be = { @@ -708,8 +709,8 @@ static int sec_eng_init_be(struct rtw89_dev *rtwdev)  	val32 |= B_BE_CLK_EN_CGCMP | B_BE_CLK_EN_WAPI | B_BE_CLK_EN_WEP_TKIP |  		 B_BE_SEC_TX_ENC | B_BE_SEC_RX_DEC |  		 B_BE_MC_DEC | B_BE_BC_DEC | -		 B_BE_BMC_MGNT_DEC | B_BE_UC_MGNT_DEC; -	val32 &= ~B_BE_SEC_PRE_ENQUE_TX; +		 B_BE_BMC_MGNT_DEC | B_BE_UC_MGNT_DEC | +		 B_BE_SEC_PRE_ENQUE_TX;  	rtw89_write32(rtwdev, R_BE_SEC_ENG_CTRL, val32);  	rtw89_write32_set(rtwdev, R_BE_SEC_MPDU_PROC, B_BE_APPEND_ICV | B_BE_APPEND_MIC); @@ -1865,7 +1866,7 @@ int rtw89_mac_cfg_ctrl_path_v2(struct rtw89_dev *rtwdev, bool wl)  	if (wl)  		return 0; -	for (i = 0; i < RTW89_PHY_MAX; i++) { +	for (i = 0; i < RTW89_PHY_NUM; i++) {  		g[i].gnt_bt_sw_en = 1;  		g[i].gnt_bt = 1;  		g[i].gnt_wl_sw_en = 1; @@ -2566,6 +2567,7 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_be = {  	.filter_model_addr = R_BE_FILTER_MODEL_ADDR,  	.indir_access_addr = R_BE_INDIR_ACCESS_ENTRY,  	.mem_base_addrs = rtw89_mac_mem_base_addrs_be, +	.mem_page_size = MAC_MEM_DUMP_PAGE_SIZE_BE,  	.rx_fltr = R_BE_RX_FLTR_OPT,  	.port_base = &rtw89_port_base_be,  	.agg_len_ht = R_BE_AGG_LEN_HT_0, @@ -2585,6 +2587,8 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_be = {  		.mask = B_BE_RXTRIG_RU26_DIS,  	},  	.wow_ctrl = {.addr = R_BE_WOW_CTRL, .mask = B_BE_WOW_WOWEN,}, +	.agg_limit = {.addr = R_BE_AMPDU_AGG_LIMIT, .mask = B_BE_AMPDU_MAX_TIME_MASK,}, +	.txcnt_limit = {.addr = R_BE_TXCNT, .mask = B_BE_L_TXCNT_LMT_MASK,},  	.check_mac_en = rtw89_mac_check_mac_en_be,  	.sys_init = sys_init_be, @@ -2634,6 +2638,8 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_be = {  	.is_txq_empty = mac_is_txq_empty_be, +	.prep_chan_list = rtw89_hw_scan_prep_chan_list_be, +	.free_chan_list = rtw89_hw_scan_free_chan_list_be,  	.add_chan_list = rtw89_hw_scan_add_chan_list_be,  	.add_chan_list_pno = rtw89_pno_scan_add_chan_list_be,  	.scan_offload = rtw89_fw_h2c_scan_offload_be, diff --git a/sys/contrib/dev/rtw89/pci.c b/sys/contrib/dev/rtw89/pci.c index 2b2f1c7f09b4..fd61ddf3841c 100644 --- a/sys/contrib/dev/rtw89/pci.c +++ b/sys/contrib/dev/rtw89/pci.c @@ -235,7 +235,7 @@ int rtw89_pci_sync_skb_for_device_and_validate_rx_info(struct rtw89_dev *rtwdev,  						       struct sk_buff *skb)  {  	struct rtw89_pci_rx_info *rx_info = RTW89_PCI_RX_SKB_CB(skb); -	int rx_tag_retry = 100; +	int rx_tag_retry = 1000;  	int ret;  	do { @@ -2710,6 +2710,10 @@ static void rtw89_pci_set_dbg(struct rtw89_dev *rtwdev)  	rtw89_write32_set(rtwdev, R_AX_PCIE_DBG_CTRL,  			  B_AX_ASFF_FULL_NO_STK | B_AX_EN_STUCK_DBG); +	rtw89_write32_mask(rtwdev, R_AX_PCIE_EXP_CTRL, +			   B_AX_EN_STUCK_DBG | B_AX_ASFF_FULL_NO_STK, +			   B_AX_EN_STUCK_DBG); +  	if (rtwdev->chip->chip_id == RTL8852A)  		rtw89_write32_set(rtwdev, R_AX_PCIE_EXP_CTRL,  				  B_AX_EN_CHKDSC_NO_RX_STUCK); @@ -3177,17 +3181,26 @@ static bool rtw89_pci_is_dac_compatible_bridge(struct rtw89_dev *rtwdev)  	return false;  } -static void rtw89_pci_cfg_dac(struct rtw89_dev *rtwdev) +static int rtw89_pci_cfg_dac(struct rtw89_dev *rtwdev, bool force)  {  	struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv; +	struct pci_dev *pdev = rtwpci->pdev; +	int ret; +	u8 val; -	if (!rtwpci->enable_dac) -		return; +	if (!rtwpci->enable_dac && !force) +		return 0;  	if (!rtw89_pci_chip_is_manual_dac(rtwdev)) -		return; +		return 0; + +	/* Configure DAC only via PCI config API, not DBI interfaces */ +	ret = pci_read_config_byte(pdev, RTW89_PCIE_L1_CTRL, &val); +	if (ret) +		return ret; -	rtw89_pci_config_byte_set(rtwdev, RTW89_PCIE_L1_CTRL, RTW89_PCIE_BIT_EN_64BITS); +	val |= RTW89_PCIE_BIT_EN_64BITS; +	return pci_write_config_byte(pdev, RTW89_PCIE_L1_CTRL, val);  }  static int rtw89_pci_setup_mapping(struct rtw89_dev *rtwdev, @@ -3205,13 +3218,16 @@ static int rtw89_pci_setup_mapping(struct rtw89_dev *rtwdev,  	}  	if (!rtw89_pci_is_dac_compatible_bridge(rtwdev)) -		goto no_dac; +		goto try_dac_done;  	ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(36));  	if (!ret) { -		rtwpci->enable_dac = true; -		rtw89_pci_cfg_dac(rtwdev); -	} else { +		ret = rtw89_pci_cfg_dac(rtwdev, true); +		if (!ret) { +			rtwpci->enable_dac = true; +			goto try_dac_done; +		} +  		ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));  		if (ret) {  			rtw89_err(rtwdev, @@ -3219,7 +3235,7 @@ static int rtw89_pci_setup_mapping(struct rtw89_dev *rtwdev,  			goto err_release_regions;  		}  	} -no_dac: +try_dac_done:  #if defined(__FreeBSD__)  	linuxkpi_pcim_want_to_use_bus_functions(pdev); @@ -4385,7 +4401,7 @@ static void rtw89_pci_l2_hci_ldo(struct rtw89_dev *rtwdev)  void rtw89_pci_basic_cfg(struct rtw89_dev *rtwdev, bool resume)  {  	if (resume) -		rtw89_pci_cfg_dac(rtwdev); +		rtw89_pci_cfg_dac(rtwdev, false);  	rtw89_pci_disable_eq(rtwdev);  	rtw89_pci_filter_out(rtwdev); @@ -4424,6 +4440,43 @@ static int __maybe_unused rtw89_pci_resume(struct device *dev)  SIMPLE_DEV_PM_OPS(rtw89_pm_ops, rtw89_pci_suspend, rtw89_pci_resume);  EXPORT_SYMBOL(rtw89_pm_ops); +static pci_ers_result_t rtw89_pci_io_error_detected(struct pci_dev *pdev, +						    pci_channel_state_t state) +{ +	struct net_device *netdev = pci_get_drvdata(pdev); + +	netif_device_detach(netdev); + +	return PCI_ERS_RESULT_NEED_RESET; +} + +static pci_ers_result_t rtw89_pci_io_slot_reset(struct pci_dev *pdev) +{ +	struct ieee80211_hw *hw = pci_get_drvdata(pdev); +	struct rtw89_dev *rtwdev = hw->priv; + +	rtw89_ser_notify(rtwdev, MAC_AX_ERR_ASSERTION); + +	return PCI_ERS_RESULT_RECOVERED; +} + +static void rtw89_pci_io_resume(struct pci_dev *pdev) +{ +	struct net_device *netdev = pci_get_drvdata(pdev); + +	/* ack any pending wake events, disable PME */ +	pci_enable_wake(pdev, PCI_D0, 0); + +	netif_device_attach(netdev); +} + +const struct pci_error_handlers rtw89_pci_err_handler = { +	.error_detected = rtw89_pci_io_error_detected, +	.slot_reset = rtw89_pci_io_slot_reset, +	.resume = rtw89_pci_io_resume, +}; +EXPORT_SYMBOL(rtw89_pci_err_handler); +  const struct rtw89_pci_gen_def rtw89_pci_gen_ax = {  	.isr_rdu = B_AX_RDU_INT,  	.isr_halt_c2h = B_AX_HALT_C2H_INT_EN, @@ -4520,6 +4573,7 @@ int rtw89_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)  	rtwdev->pci_info = info->bus.pci;  	rtwdev->hci.ops = &rtw89_pci_ops;  	rtwdev->hci.type = RTW89_HCI_TYPE_PCIE; +	rtwdev->hci.dle_type = RTW89_HCI_DLE_TYPE_PCIE;  	rtwdev->hci.rpwm_addr = pci_info->rpwm_addr;  	rtwdev->hci.cpwm_addr = pci_info->cpwm_addr; diff --git a/sys/contrib/dev/rtw89/pci.h b/sys/contrib/dev/rtw89/pci.h index 4d11c3dd60a5..52f527069da6 100644 --- a/sys/contrib/dev/rtw89/pci.h +++ b/sys/contrib/dev/rtw89/pci.h @@ -455,34 +455,36 @@  #define B_BE_RX0DMA_INT_EN BIT(0)  #define R_BE_HAXI_HISR00 0xB0B4 -#define B_BE_RDU_CH6_INT BIT(28) -#define B_BE_RDU_CH5_INT BIT(27) -#define B_BE_RDU_CH4_INT BIT(26) -#define B_BE_RDU_CH2_INT BIT(25) -#define B_BE_RDU_CH1_INT BIT(24) -#define B_BE_RDU_CH0_INT BIT(23) -#define B_BE_RXDMA_STUCK_INT BIT(22) -#define B_BE_TXDMA_STUCK_INT BIT(21) -#define B_BE_TXDMA_CH14_INT BIT(20) -#define B_BE_TXDMA_CH13_INT BIT(19) -#define B_BE_TXDMA_CH12_INT BIT(18) -#define B_BE_TXDMA_CH11_INT BIT(17) -#define B_BE_TXDMA_CH10_INT BIT(16) -#define B_BE_TXDMA_CH9_INT BIT(15) -#define B_BE_TXDMA_CH8_INT BIT(14) -#define B_BE_TXDMA_CH7_INT BIT(13) -#define B_BE_TXDMA_CH6_INT BIT(12) -#define B_BE_TXDMA_CH5_INT BIT(11) -#define B_BE_TXDMA_CH4_INT BIT(10) -#define B_BE_TXDMA_CH3_INT BIT(9) -#define B_BE_TXDMA_CH2_INT BIT(8) -#define B_BE_TXDMA_CH1_INT BIT(7) -#define B_BE_TXDMA_CH0_INT BIT(6) -#define B_BE_RPQ1DMA_INT BIT(5) -#define B_BE_RX1P1DMA_INT BIT(4) +#define B_BE_RDU_CH5_INT_V1 BIT(30) +#define B_BE_RDU_CH4_INT_V1 BIT(29) +#define B_BE_RDU_CH3_INT_V1 BIT(28) +#define B_BE_RDU_CH2_INT_V1 BIT(27) +#define B_BE_RDU_CH1_INT_V1 BIT(26) +#define B_BE_RDU_CH0_INT_V1 BIT(25) +#define B_BE_RXDMA_STUCK_INT_V1 BIT(24) +#define B_BE_TXDMA_STUCK_INT_V1 BIT(23) +#define B_BE_TXDMA_CH14_INT_V1 BIT(22) +#define B_BE_TXDMA_CH13_INT_V1 BIT(21) +#define B_BE_TXDMA_CH12_INT_V1 BIT(20) +#define B_BE_TXDMA_CH11_INT_V1 BIT(19) +#define B_BE_TXDMA_CH10_INT_V1 BIT(18) +#define B_BE_TXDMA_CH9_INT_V1 BIT(17) +#define B_BE_TXDMA_CH8_INT_V1 BIT(16) +#define B_BE_TXDMA_CH7_INT_V1 BIT(15) +#define B_BE_TXDMA_CH6_INT_V1 BIT(14) +#define B_BE_TXDMA_CH5_INT_V1 BIT(13) +#define B_BE_TXDMA_CH4_INT_V1 BIT(12) +#define B_BE_TXDMA_CH3_INT_V1 BIT(11) +#define B_BE_TXDMA_CH2_INT_V1 BIT(10) +#define B_BE_TXDMA_CH1_INT_V1 BIT(9) +#define B_BE_TXDMA_CH0_INT_V1 BIT(8) +#define B_BE_RX1P1DMA_INT_V1 BIT(7) +#define B_BE_RX0P1DMA_INT_V1 BIT(6) +#define B_BE_RO1DMA_INT BIT(5) +#define B_BE_RP1DMA_INT BIT(4)  #define B_BE_RX1DMA_INT BIT(3) -#define B_BE_RPQ0DMA_INT BIT(2) -#define B_BE_RX0P1DMA_INT BIT(1) +#define B_BE_RO0DMA_INT BIT(2) +#define B_BE_RP0DMA_INT BIT(1)  #define B_BE_RX0DMA_INT BIT(0)  /* TX/RX */ @@ -1620,6 +1622,7 @@ static inline bool rtw89_pci_ltr_is_err_reg_val(u32 val)  extern const struct dev_pm_ops rtw89_pm_ops;  extern const struct dev_pm_ops rtw89_pm_ops_be; +extern const struct pci_error_handlers rtw89_pci_err_handler;  extern const struct rtw89_pci_ch_dma_addr_set rtw89_pci_ch_dma_addr_set;  extern const struct rtw89_pci_ch_dma_addr_set rtw89_pci_ch_dma_addr_set_v1;  extern const struct rtw89_pci_ch_dma_addr_set rtw89_pci_ch_dma_addr_set_be; diff --git a/sys/contrib/dev/rtw89/pci_be.c b/sys/contrib/dev/rtw89/pci_be.c index cd39eebe8186..12e6a0cbb889 100644 --- a/sys/contrib/dev/rtw89/pci_be.c +++ b/sys/contrib/dev/rtw89/pci_be.c @@ -666,7 +666,7 @@ SIMPLE_DEV_PM_OPS(rtw89_pm_ops_be, rtw89_pci_suspend_be, rtw89_pci_resume_be);  EXPORT_SYMBOL(rtw89_pm_ops_be);  const struct rtw89_pci_gen_def rtw89_pci_gen_be = { -	.isr_rdu = B_BE_RDU_CH1_INT | B_BE_RDU_CH0_INT, +	.isr_rdu = B_BE_RDU_CH1_INT_V1 | B_BE_RDU_CH0_INT_V1,  	.isr_halt_c2h = B_BE_HALT_C2H_INT,  	.isr_wdt_timeout = B_BE_WDT_TIMEOUT_INT,  	.isr_clear_rpq = {R_BE_PCIE_DMA_ISR, B_BE_PCIE_RX_RPQ0_ISR_V1}, diff --git a/sys/contrib/dev/rtw89/phy.c b/sys/contrib/dev/rtw89/phy.c index 3aea05eaa73f..5b48051e62d1 100644 --- a/sys/contrib/dev/rtw89/phy.c +++ b/sys/contrib/dev/rtw89/phy.c @@ -119,10 +119,12 @@ static u64 get_eht_mcs_ra_mask(u8 *max_nss, u8 start_mcs, u8 n_nss)  	return mask;  } -static u64 get_eht_ra_mask(struct ieee80211_link_sta *link_sta) +static u64 get_eht_ra_mask(struct rtw89_vif_link *rtwvif_link, +			   struct ieee80211_link_sta *link_sta)  { -	struct ieee80211_sta_eht_cap *eht_cap = &link_sta->eht_cap; +	struct ieee80211_vif *vif = rtwvif_link_to_vif(rtwvif_link);  	struct ieee80211_eht_mcs_nss_supp_20mhz_only *mcs_nss_20mhz; +	struct ieee80211_sta_eht_cap *eht_cap = &link_sta->eht_cap;  	struct ieee80211_eht_mcs_nss_supp_bw *mcs_nss;  	u8 *he_phy_cap = link_sta->he_cap.he_cap_elem.phy_cap_info; @@ -136,8 +138,8 @@ static u64 get_eht_ra_mask(struct ieee80211_link_sta *link_sta)  		/* MCS 9, 11, 13 */  		return get_eht_mcs_ra_mask(mcs_nss->rx_tx_max_nss, 9, 3);  	case IEEE80211_STA_RX_BW_20: -		if (!(he_phy_cap[0] & -		      IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) { +		if (vif->type == NL80211_IFTYPE_AP && +		    !(he_phy_cap[0] & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) {  			mcs_nss_20mhz = &eht_cap->eht_mcs_nss_supp.only_20mhz;  			/* MCS 7, 9, 11, 13 */  			return get_eht_mcs_ra_mask(mcs_nss_20mhz->rx_tx_max_nss, 7, 4); @@ -332,7 +334,7 @@ static void rtw89_phy_ra_sta_update(struct rtw89_dev *rtwdev,  	/* Set the ra mask from sta's capability */  	if (link_sta->eht_cap.has_eht) {  		mode |= RTW89_RA_MODE_EHT; -		ra_mask |= get_eht_ra_mask(link_sta); +		ra_mask |= get_eht_ra_mask(rtwvif_link, link_sta);  		if (rtwdev->hal.no_mcs_12_13)  			high_rate_masks = rtw89_ra_mask_eht_mcs0_11; @@ -902,7 +904,8 @@ static u32 rtw89_phy_read_rf_a(struct rtw89_dev *rtwdev,  				       30, false, rtwdev, R_SWSI_V1,  				       B_SWSI_R_DATA_DONE_V1);  	if (ret) { -		rtw89_err(rtwdev, "read swsi busy\n"); +		if (!test_bit(RTW89_FLAG_UNPLUGGED, rtwdev->flags)) +			rtw89_err(rtwdev, "read swsi busy\n");  		return INV_RF_DATA;  	} @@ -2045,33 +2048,47 @@ static s8 rtw89_phy_ant_gain_query(struct rtw89_dev *rtwdev,  		   ant_gain->offset[path][subband_h]);  } -static s8 rtw89_phy_ant_gain_offset(struct rtw89_dev *rtwdev, u8 band, u32 center_freq) +static s8 rtw89_phy_ant_gain_offset(struct rtw89_dev *rtwdev, u32 center_freq) +{ +	s8 offset_patha, offset_pathb; + +	offset_patha = rtw89_phy_ant_gain_query(rtwdev, RF_PATH_A, center_freq); +	offset_pathb = rtw89_phy_ant_gain_query(rtwdev, RF_PATH_B, center_freq); + +	if (RTW89_CHK_FW_FEATURE(NO_POWER_DIFFERENCE, &rtwdev->fw)) +		return min(offset_patha, offset_pathb); + +	return max(offset_patha, offset_pathb); +} + +static bool rtw89_can_apply_ant_gain(struct rtw89_dev *rtwdev, u8 band)  { +	const struct rtw89_rfe_parms *rfe_parms = rtwdev->rfe_parms;  	struct rtw89_ant_gain_info *ant_gain = &rtwdev->ant_gain;  	const struct rtw89_chip_info *chip = rtwdev->chip;  	u8 regd = rtw89_regd_get(rtwdev, band); -	s8 offset_patha, offset_pathb;  	if (!chip->support_ant_gain) -		return 0; +		return false; -	if (!(ant_gain->regd_enabled & BIT(regd))) -		return 0; +	if (ant_gain->block_country || !(ant_gain->regd_enabled & BIT(regd))) +		return false; -	offset_patha = rtw89_phy_ant_gain_query(rtwdev, RF_PATH_A, center_freq); -	offset_pathb = rtw89_phy_ant_gain_query(rtwdev, RF_PATH_B, center_freq); +	if (!rfe_parms->has_da) +		return false; -	return max(offset_patha, offset_pathb); +	return true;  }  s16 rtw89_phy_ant_gain_pwr_offset(struct rtw89_dev *rtwdev,  				  const struct rtw89_chan *chan)  { -	struct rtw89_ant_gain_info *ant_gain = &rtwdev->ant_gain; -	u8 regd = rtw89_regd_get(rtwdev, chan->band_type);  	s8 offset_patha, offset_pathb; -	if (!(ant_gain->regd_enabled & BIT(regd))) +	if (!rtw89_can_apply_ant_gain(rtwdev, chan->band_type)) +		return 0; + +	if (RTW89_CHK_FW_FEATURE(NO_POWER_DIFFERENCE, &rtwdev->fw))  		return 0;  	offset_patha = rtw89_phy_ant_gain_query(rtwdev, RF_PATH_A, chan->freq); @@ -2081,24 +2098,25 @@ s16 rtw89_phy_ant_gain_pwr_offset(struct rtw89_dev *rtwdev,  }  EXPORT_SYMBOL(rtw89_phy_ant_gain_pwr_offset); -void rtw89_print_ant_gain(struct seq_file *m, struct rtw89_dev *rtwdev, -			  const struct rtw89_chan *chan) +int rtw89_print_ant_gain(struct rtw89_dev *rtwdev, char *buf, size_t bufsz, +			 const struct rtw89_chan *chan)  { -	struct rtw89_ant_gain_info *ant_gain = &rtwdev->ant_gain; -	const struct rtw89_chip_info *chip = rtwdev->chip; -	u8 regd = rtw89_regd_get(rtwdev, chan->band_type); +	char *p = buf, *end = buf + bufsz;  	s8 offset_patha, offset_pathb; -	if (!chip->support_ant_gain || !(ant_gain->regd_enabled & BIT(regd))) { -		seq_puts(m, "no DAG is applied\n"); -		return; +	if (!rtw89_can_apply_ant_gain(rtwdev, chan->band_type)) { +		p += scnprintf(p, end - p, "no DAG is applied\n"); +		goto out;  	}  	offset_patha = rtw89_phy_ant_gain_query(rtwdev, RF_PATH_A, chan->freq);  	offset_pathb = rtw89_phy_ant_gain_query(rtwdev, RF_PATH_B, chan->freq); -	seq_printf(m, "ChainA offset: %d dBm\n", offset_patha); -	seq_printf(m, "ChainB offset: %d dBm\n", offset_pathb); +	p += scnprintf(p, end - p, "ChainA offset: %d dBm\n", offset_patha); +	p += scnprintf(p, end - p, "ChainB offset: %d dBm\n", offset_pathb); + +out: +	return p - buf;  }  static const u8 rtw89_rs_idx_num_ax[] = { @@ -2251,20 +2269,31 @@ s8 rtw89_phy_read_txpwr_limit(struct rtw89_dev *rtwdev, u8 band,  			      u8 bw, u8 ntx, u8 rs, u8 bf, u8 ch)  {  	const struct rtw89_rfe_parms *rfe_parms = rtwdev->rfe_parms; +	const struct rtw89_txpwr_rule_2ghz *rule_da_2ghz = &rfe_parms->rule_da_2ghz; +	const struct rtw89_txpwr_rule_5ghz *rule_da_5ghz = &rfe_parms->rule_da_5ghz; +	const struct rtw89_txpwr_rule_6ghz *rule_da_6ghz = &rfe_parms->rule_da_6ghz;  	const struct rtw89_txpwr_rule_2ghz *rule_2ghz = &rfe_parms->rule_2ghz;  	const struct rtw89_txpwr_rule_5ghz *rule_5ghz = &rfe_parms->rule_5ghz;  	const struct rtw89_txpwr_rule_6ghz *rule_6ghz = &rfe_parms->rule_6ghz;  	struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory;  	enum nl80211_band nl_band = rtw89_hw_to_nl80211_band(band); +	bool has_ant_gain = rtw89_can_apply_ant_gain(rtwdev, band);  	u32 freq = ieee80211_channel_to_frequency(ch, nl_band);  	u8 ch_idx = rtw89_channel_to_idx(rtwdev, band, ch); +	s8 lmt = 0, da_lmt = S8_MAX, sar, offset = 0;  	u8 regd = rtw89_regd_get(rtwdev, band);  	u8 reg6 = regulatory->reg_6ghz_power; -	s8 lmt = 0, sar, offset; +	struct rtw89_sar_parm sar_parm = { +		.center_freq = freq, +		.ntx = ntx, +	};  	s8 cstr;  	switch (band) {  	case RTW89_BAND_2G: +		if (has_ant_gain) +			da_lmt = (*rule_da_2ghz->lmt)[bw][ntx][rs][bf][regd][ch_idx]; +  		lmt = (*rule_2ghz->lmt)[bw][ntx][rs][bf][regd][ch_idx];  		if (lmt)  			break; @@ -2272,6 +2301,9 @@ s8 rtw89_phy_read_txpwr_limit(struct rtw89_dev *rtwdev, u8 band,  		lmt = (*rule_2ghz->lmt)[bw][ntx][rs][bf][RTW89_WW][ch_idx];  		break;  	case RTW89_BAND_5G: +		if (has_ant_gain) +			da_lmt = (*rule_da_5ghz->lmt)[bw][ntx][rs][bf][regd][ch_idx]; +  		lmt = (*rule_5ghz->lmt)[bw][ntx][rs][bf][regd][ch_idx];  		if (lmt)  			break; @@ -2279,6 +2311,9 @@ s8 rtw89_phy_read_txpwr_limit(struct rtw89_dev *rtwdev, u8 band,  		lmt = (*rule_5ghz->lmt)[bw][ntx][rs][bf][RTW89_WW][ch_idx];  		break;  	case RTW89_BAND_6G: +		if (has_ant_gain) +			da_lmt = (*rule_da_6ghz->lmt)[bw][ntx][rs][bf][regd][reg6][ch_idx]; +  		lmt = (*rule_6ghz->lmt)[bw][ntx][rs][bf][regd][reg6][ch_idx];  		if (lmt)  			break; @@ -2292,9 +2327,12 @@ s8 rtw89_phy_read_txpwr_limit(struct rtw89_dev *rtwdev, u8 band,  		return 0;  	} -	offset = rtw89_phy_ant_gain_offset(rtwdev, band, freq); -	lmt = rtw89_phy_txpwr_rf_to_mac(rtwdev, lmt + offset); -	sar = rtw89_query_sar(rtwdev, freq); +	da_lmt = da_lmt ?: S8_MAX; +	if (da_lmt != S8_MAX) +		offset = rtw89_phy_ant_gain_offset(rtwdev, freq); + +	lmt = rtw89_phy_txpwr_rf_to_mac(rtwdev, min(lmt + offset, da_lmt)); +	sar = rtw89_query_sar(rtwdev, &sar_parm);  	cstr = rtw89_phy_get_tpe_constraint(rtwdev, band);  	return min3(lmt, sar, cstr); @@ -2511,20 +2549,31 @@ s8 rtw89_phy_read_txpwr_limit_ru(struct rtw89_dev *rtwdev, u8 band,  				 u8 ru, u8 ntx, u8 ch)  {  	const struct rtw89_rfe_parms *rfe_parms = rtwdev->rfe_parms; +	const struct rtw89_txpwr_rule_2ghz *rule_da_2ghz = &rfe_parms->rule_da_2ghz; +	const struct rtw89_txpwr_rule_5ghz *rule_da_5ghz = &rfe_parms->rule_da_5ghz; +	const struct rtw89_txpwr_rule_6ghz *rule_da_6ghz = &rfe_parms->rule_da_6ghz;  	const struct rtw89_txpwr_rule_2ghz *rule_2ghz = &rfe_parms->rule_2ghz;  	const struct rtw89_txpwr_rule_5ghz *rule_5ghz = &rfe_parms->rule_5ghz;  	const struct rtw89_txpwr_rule_6ghz *rule_6ghz = &rfe_parms->rule_6ghz;  	struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory;  	enum nl80211_band nl_band = rtw89_hw_to_nl80211_band(band); +	bool has_ant_gain = rtw89_can_apply_ant_gain(rtwdev, band);  	u32 freq = ieee80211_channel_to_frequency(ch, nl_band);  	u8 ch_idx = rtw89_channel_to_idx(rtwdev, band, ch); +	s8 lmt_ru = 0, da_lmt_ru = S8_MAX, sar, offset = 0;  	u8 regd = rtw89_regd_get(rtwdev, band);  	u8 reg6 = regulatory->reg_6ghz_power; -	s8 lmt_ru = 0, sar, offset; +	struct rtw89_sar_parm sar_parm = { +		.center_freq = freq, +		.ntx = ntx, +	};  	s8 cstr;  	switch (band) {  	case RTW89_BAND_2G: +		if (has_ant_gain) +			da_lmt_ru = (*rule_da_2ghz->lmt_ru)[ru][ntx][regd][ch_idx]; +  		lmt_ru = (*rule_2ghz->lmt_ru)[ru][ntx][regd][ch_idx];  		if (lmt_ru)  			break; @@ -2532,6 +2581,9 @@ s8 rtw89_phy_read_txpwr_limit_ru(struct rtw89_dev *rtwdev, u8 band,  		lmt_ru = (*rule_2ghz->lmt_ru)[ru][ntx][RTW89_WW][ch_idx];  		break;  	case RTW89_BAND_5G: +		if (has_ant_gain) +			da_lmt_ru = (*rule_da_5ghz->lmt_ru)[ru][ntx][regd][ch_idx]; +  		lmt_ru = (*rule_5ghz->lmt_ru)[ru][ntx][regd][ch_idx];  		if (lmt_ru)  			break; @@ -2539,6 +2591,9 @@ s8 rtw89_phy_read_txpwr_limit_ru(struct rtw89_dev *rtwdev, u8 band,  		lmt_ru = (*rule_5ghz->lmt_ru)[ru][ntx][RTW89_WW][ch_idx];  		break;  	case RTW89_BAND_6G: +		if (has_ant_gain) +			da_lmt_ru = (*rule_da_6ghz->lmt_ru)[ru][ntx][regd][reg6][ch_idx]; +  		lmt_ru = (*rule_6ghz->lmt_ru)[ru][ntx][regd][reg6][ch_idx];  		if (lmt_ru)  			break; @@ -2552,9 +2607,12 @@ s8 rtw89_phy_read_txpwr_limit_ru(struct rtw89_dev *rtwdev, u8 band,  		return 0;  	} -	offset = rtw89_phy_ant_gain_offset(rtwdev, band, freq); -	lmt_ru = rtw89_phy_txpwr_rf_to_mac(rtwdev, lmt_ru + offset); -	sar = rtw89_query_sar(rtwdev, freq); +	da_lmt_ru = da_lmt_ru ?: S8_MAX; +	if (da_lmt_ru != S8_MAX) +		offset = rtw89_phy_ant_gain_offset(rtwdev, freq); + +	lmt_ru = rtw89_phy_txpwr_rf_to_mac(rtwdev, min(lmt_ru + offset, da_lmt_ru)); +	sar = rtw89_query_sar(rtwdev, &sar_parm);  	cstr = rtw89_phy_get_tpe_constraint(rtwdev, band);  	return min3(lmt_ru, sar, cstr); @@ -3015,6 +3073,35 @@ void (* const rtw89_phy_c2h_ra_handler[])(struct rtw89_dev *rtwdev,  	[RTW89_PHY_C2H_FUNC_TXSTS] = NULL,  }; +static void +rtw89_phy_c2h_lowrt_rty(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) +{ +} + +static void +rtw89_phy_c2h_fw_scan_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) +{ +	const struct rtw89_c2h_fw_scan_rpt *c2h_rpt = +		(const struct rtw89_c2h_fw_scan_rpt *)c2h->data; + +	rtw89_debug(rtwdev, RTW89_DBG_DIG, +		    "%s: band: %u, op_chan: %u, PD_low_bd(ofdm, cck): (-%d, %d), phy_idx: %u\n", +		    __func__, c2h_rpt->band, c2h_rpt->center_ch, +		    PD_LOWER_BOUND_BASE - (c2h_rpt->ofdm_pd_idx << 1), +		    c2h_rpt->cck_pd_idx, c2h_rpt->phy_idx); +} + +static +void (* const rtw89_phy_c2h_dm_handler[])(struct rtw89_dev *rtwdev, +					  struct sk_buff *c2h, u32 len) = { +	[RTW89_PHY_C2H_DM_FUNC_FW_TEST] = NULL, +	[RTW89_PHY_C2H_DM_FUNC_FW_TRIG_TX_RPT] = NULL, +	[RTW89_PHY_C2H_DM_FUNC_SIGB] = NULL, +	[RTW89_PHY_C2H_DM_FUNC_LOWRT_RTY] = rtw89_phy_c2h_lowrt_rty, +	[RTW89_PHY_C2H_DM_FUNC_MCC_DIG] = NULL, +	[RTW89_PHY_C2H_DM_FUNC_FW_SCAN] = rtw89_phy_c2h_fw_scan_rpt, +}; +  static void rtw89_phy_c2h_rfk_rpt_log(struct rtw89_dev *rtwdev,  				      enum rtw89_phy_c2h_rfk_log_func func,  				      void *content, u16 len) @@ -3483,6 +3570,30 @@ rtw89_phy_c2h_rfk_report_state(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u3  static void  rtw89_phy_c2h_rfk_log_tas_pwr(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len)  { +	const struct rtw89_c2h_rf_tas_info *rf_tas = +		(const struct rtw89_c2h_rf_tas_info *)c2h->data; +	const enum rtw89_sar_sources src = rtwdev->sar.src; +	struct rtw89_tas_info *tas = &rtwdev->tas; +	u64 linear = 0; +	u32 i, cur_idx; +	s16 txpwr; + +	if (!tas->enable || src == RTW89_SAR_SOURCE_NONE) +		return; + +	cur_idx = le32_to_cpu(rf_tas->cur_idx); +	for (i = 0; i < cur_idx; i++) { +		txpwr = (s16)le16_to_cpu(rf_tas->txpwr_history[i]); +		linear += rtw89_db_quarter_to_linear(txpwr); + +		rtw89_debug(rtwdev, RTW89_DBG_SAR, +			    "tas: index: %u, txpwr: %d\n", i, txpwr); +	} + +	if (cur_idx == 0) +		tas->instant_txpwr = rtw89_db_to_linear(0); +	else +		tas->instant_txpwr = DIV_ROUND_DOWN_ULL(linear, cur_idx);  }  static @@ -3539,9 +3650,9 @@ void rtw89_phy_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb,  			handler = rtw89_phy_c2h_rfk_report_handler[func];  		break;  	case RTW89_PHY_C2H_CLASS_DM: -		if (func == RTW89_PHY_C2H_DM_FUNC_LOWRT_RTY) -			return; -		fallthrough; +		if (func < ARRAY_SIZE(rtw89_phy_c2h_dm_handler)) +			handler = rtw89_phy_c2h_dm_handler[func]; +		break;  	default:  		rtw89_info(rtwdev, "PHY c2h class %d not support\n", class);  		return; @@ -4628,7 +4739,7 @@ static void rtw89_phy_cfo_dm(struct rtw89_dev *rtwdev)  	cfo->dcfo_avg = 0;  	rtw89_debug(rtwdev, RTW89_DBG_CFO, "CFO:total_sta_assoc=%d\n",  		    rtwdev->total_sta_assoc); -	if (rtwdev->total_sta_assoc == 0) { +	if (rtwdev->total_sta_assoc == 0 || rtw89_is_mlo_1_1(rtwdev)) {  		rtw89_phy_cfo_reset(rtwdev);  		return;  	} @@ -4679,29 +4790,28 @@ static void rtw89_phy_cfo_dm(struct rtw89_dev *rtwdev)  	rtw89_phy_cfo_statistics_reset(rtwdev);  } -void rtw89_phy_cfo_track_work(struct work_struct *work) +void rtw89_phy_cfo_track_work(struct wiphy *wiphy, struct wiphy_work *work)  {  	struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,  						cfo_track_work.work);  	struct rtw89_cfo_tracking_info *cfo = &rtwdev->cfo_tracking; -	mutex_lock(&rtwdev->mutex); +	lockdep_assert_wiphy(wiphy); +  	if (!cfo->cfo_trig_by_timer_en) -		goto out; +		return;  	rtw89_leave_ps_mode(rtwdev);  	rtw89_phy_cfo_dm(rtwdev); -	ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->cfo_track_work, -				     msecs_to_jiffies(cfo->cfo_timer_ms)); -out: -	mutex_unlock(&rtwdev->mutex); +	wiphy_delayed_work_queue(wiphy, &rtwdev->cfo_track_work, +				 msecs_to_jiffies(cfo->cfo_timer_ms));  }  static void rtw89_phy_cfo_start_work(struct rtw89_dev *rtwdev)  {  	struct rtw89_cfo_tracking_info *cfo = &rtwdev->cfo_tracking; -	ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->cfo_track_work, -				     msecs_to_jiffies(cfo->cfo_timer_ms)); +	wiphy_delayed_work_queue(rtwdev->hw->wiphy, &rtwdev->cfo_track_work, +				 msecs_to_jiffies(cfo->cfo_timer_ms));  }  void rtw89_phy_cfo_track(struct rtw89_dev *rtwdev) @@ -5143,7 +5253,6 @@ static void rtw89_phy_stat_thermal_update(struct rtw89_dev *rtwdev)  struct rtw89_phy_iter_rssi_data {  	struct rtw89_dev *rtwdev; -	struct rtw89_phy_ch_info *ch_info;  	bool rssi_changed;  }; @@ -5151,10 +5260,15 @@ static  void __rtw89_phy_stat_rssi_update_iter(struct rtw89_sta_link *rtwsta_link,  				       struct rtw89_phy_iter_rssi_data *rssi_data)  { -	struct rtw89_phy_ch_info *ch_info = rssi_data->ch_info; +	struct rtw89_vif_link *rtwvif_link = rtwsta_link->rtwvif_link; +	struct rtw89_dev *rtwdev = rssi_data->rtwdev; +	struct rtw89_phy_ch_info *ch_info; +	struct rtw89_bb_ctx *bb;  	unsigned long rssi_curr;  	rssi_curr = ewma_rssi_read(&rtwsta_link->avg_rssi); +	bb = rtw89_get_bb_ctx(rtwdev, rtwvif_link->phy_idx); +	ch_info = &bb->ch_info;  	if (rssi_curr < ch_info->rssi_min) {  		ch_info->rssi_min = rssi_curr; @@ -5185,11 +5299,13 @@ static void rtw89_phy_stat_rssi_update_iter(void *data,  static void rtw89_phy_stat_rssi_update(struct rtw89_dev *rtwdev)  { -	struct rtw89_phy_iter_rssi_data rssi_data = {0}; +	struct rtw89_phy_iter_rssi_data rssi_data = {}; +	struct rtw89_bb_ctx *bb;  	rssi_data.rtwdev = rtwdev; -	rssi_data.ch_info = &rtwdev->ch_info; -	rssi_data.ch_info->rssi_min = U8_MAX; +	rtw89_for_each_active_bb(rtwdev, bb) +		bb->ch_info.rssi_min = U8_MAX; +  	ieee80211_iterate_stations_atomic(rtwdev->hw,  					  rtw89_phy_stat_rssi_update_iter,  					  &rssi_data); @@ -5227,24 +5343,27 @@ void rtw89_phy_stat_track(struct rtw89_dev *rtwdev)  	memset(&phystat->cur_pkt_stat, 0, sizeof(phystat->cur_pkt_stat));  } -static u16 rtw89_phy_ccx_us_to_idx(struct rtw89_dev *rtwdev, u32 time_us) +static u16 rtw89_phy_ccx_us_to_idx(struct rtw89_dev *rtwdev, +				   struct rtw89_bb_ctx *bb, u32 time_us)  { -	struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; +	struct rtw89_env_monitor_info *env = &bb->env_monitor;  	return time_us >> (ilog2(CCX_US_BASE_RATIO) + env->ccx_unit_idx);  } -static u32 rtw89_phy_ccx_idx_to_us(struct rtw89_dev *rtwdev, u16 idx) +static u32 rtw89_phy_ccx_idx_to_us(struct rtw89_dev *rtwdev, +				   struct rtw89_bb_ctx *bb, u16 idx)  { -	struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; +	struct rtw89_env_monitor_info *env = &bb->env_monitor;  	return idx << (ilog2(CCX_US_BASE_RATIO) + env->ccx_unit_idx);  } -static void rtw89_phy_ccx_top_setting_init(struct rtw89_dev *rtwdev) +static void rtw89_phy_ccx_top_setting_init(struct rtw89_dev *rtwdev, +					   struct rtw89_bb_ctx *bb)  {  	const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; -	struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; +	struct rtw89_env_monitor_info *env = &bb->env_monitor;  	const struct rtw89_ccx_regs *ccx = phy->ccx;  	env->ccx_manual_ctrl = false; @@ -5253,17 +5372,20 @@ static void rtw89_phy_ccx_top_setting_init(struct rtw89_dev *rtwdev)  	env->ccx_period = 0;  	env->ccx_unit_idx = RTW89_CCX_32_US; -	rtw89_phy_set_phy_regs(rtwdev, ccx->setting_addr, ccx->en_mask, 1); -	rtw89_phy_set_phy_regs(rtwdev, ccx->setting_addr, ccx->trig_opt_mask, 1); -	rtw89_phy_set_phy_regs(rtwdev, ccx->setting_addr, ccx->measurement_trig_mask, 1); -	rtw89_phy_set_phy_regs(rtwdev, ccx->setting_addr, ccx->edcca_opt_mask, -			       RTW89_CCX_EDCCA_BW20_0); +	rtw89_phy_write32_idx(rtwdev, ccx->setting_addr, ccx->en_mask, 1, bb->phy_idx); +	rtw89_phy_write32_idx(rtwdev, ccx->setting_addr, ccx->trig_opt_mask, 1, +			      bb->phy_idx); +	rtw89_phy_write32_idx(rtwdev, ccx->setting_addr, ccx->measurement_trig_mask, 1, +			      bb->phy_idx); +	rtw89_phy_write32_idx(rtwdev, ccx->setting_addr, ccx->edcca_opt_mask, +			      RTW89_CCX_EDCCA_BW20_0, bb->phy_idx);  } -static u16 rtw89_phy_ccx_get_report(struct rtw89_dev *rtwdev, u16 report, -				    u16 score) +static u16 rtw89_phy_ccx_get_report(struct rtw89_dev *rtwdev, +				    struct rtw89_bb_ctx *bb, +				    u16 report, u16 score)  { -	struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; +	struct rtw89_env_monitor_info *env = &bb->env_monitor;  	u32 numer = 0;  	u16 ret = 0; @@ -5303,9 +5425,10 @@ static void rtw89_phy_ccx_ms_to_period_unit(struct rtw89_dev *rtwdev,  		    *period, *unit_idx);  } -static void rtw89_phy_ccx_racing_release(struct rtw89_dev *rtwdev) +static void rtw89_phy_ccx_racing_release(struct rtw89_dev *rtwdev, +					 struct rtw89_bb_ctx *bb)  { -	struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; +	struct rtw89_env_monitor_info *env = &bb->env_monitor;  	rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK,  		    "lv:(%d)->(0)\n", env->ccx_rac_lv); @@ -5316,9 +5439,10 @@ static void rtw89_phy_ccx_racing_release(struct rtw89_dev *rtwdev)  }  static bool rtw89_phy_ifs_clm_th_update_check(struct rtw89_dev *rtwdev, +					      struct rtw89_bb_ctx *bb,  					      struct rtw89_ccx_para_info *para)  { -	struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; +	struct rtw89_env_monitor_info *env = &bb->env_monitor;  	bool is_update = env->ifs_clm_app != para->ifs_clm_app;  	u8 i = 0;  	u16 *ifs_th_l = env->ifs_clm_th_l; @@ -5353,12 +5477,12 @@ static bool rtw89_phy_ifs_clm_th_update_check(struct rtw89_dev *rtwdev,  	 */  	ifs_th_l[IFS_CLM_TH_START_IDX] = 0;  	ifs_th_h_us[IFS_CLM_TH_START_IDX] = ifs_th0_us; -	ifs_th_h[IFS_CLM_TH_START_IDX] = rtw89_phy_ccx_us_to_idx(rtwdev, +	ifs_th_h[IFS_CLM_TH_START_IDX] = rtw89_phy_ccx_us_to_idx(rtwdev, bb,  								 ifs_th0_us);  	for (i = 1; i < RTW89_IFS_CLM_NUM; i++) {  		ifs_th_l[i] = ifs_th_h[i - 1] + 1;  		ifs_th_h_us[i] = ifs_th_h_us[i - 1] * ifs_th_times; -		ifs_th_h[i] = rtw89_phy_ccx_us_to_idx(rtwdev, ifs_th_h_us[i]); +		ifs_th_h[i] = rtw89_phy_ccx_us_to_idx(rtwdev, bb, ifs_th_h_us[i]);  	}  ifs_update_finished: @@ -5369,30 +5493,31 @@ ifs_update_finished:  	return is_update;  } -static void rtw89_phy_ifs_clm_set_th_reg(struct rtw89_dev *rtwdev) +static void rtw89_phy_ifs_clm_set_th_reg(struct rtw89_dev *rtwdev, +					 struct rtw89_bb_ctx *bb)  {  	const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; -	struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; +	struct rtw89_env_monitor_info *env = &bb->env_monitor;  	const struct rtw89_ccx_regs *ccx = phy->ccx;  	u8 i = 0; -	rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t1_addr, ccx->ifs_t1_th_l_mask, -			       env->ifs_clm_th_l[0]); -	rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t2_addr, ccx->ifs_t2_th_l_mask, -			       env->ifs_clm_th_l[1]); -	rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t3_addr, ccx->ifs_t3_th_l_mask, -			       env->ifs_clm_th_l[2]); -	rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t4_addr, ccx->ifs_t4_th_l_mask, -			       env->ifs_clm_th_l[3]); - -	rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t1_addr, ccx->ifs_t1_th_h_mask, -			       env->ifs_clm_th_h[0]); -	rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t2_addr, ccx->ifs_t2_th_h_mask, -			       env->ifs_clm_th_h[1]); -	rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t3_addr, ccx->ifs_t3_th_h_mask, -			       env->ifs_clm_th_h[2]); -	rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t4_addr, ccx->ifs_t4_th_h_mask, -			       env->ifs_clm_th_h[3]); +	rtw89_phy_write32_idx(rtwdev, ccx->ifs_t1_addr, ccx->ifs_t1_th_l_mask, +			      env->ifs_clm_th_l[0], bb->phy_idx); +	rtw89_phy_write32_idx(rtwdev, ccx->ifs_t2_addr, ccx->ifs_t2_th_l_mask, +			      env->ifs_clm_th_l[1], bb->phy_idx); +	rtw89_phy_write32_idx(rtwdev, ccx->ifs_t3_addr, ccx->ifs_t3_th_l_mask, +			      env->ifs_clm_th_l[2], bb->phy_idx); +	rtw89_phy_write32_idx(rtwdev, ccx->ifs_t4_addr, ccx->ifs_t4_th_l_mask, +			      env->ifs_clm_th_l[3], bb->phy_idx); + +	rtw89_phy_write32_idx(rtwdev, ccx->ifs_t1_addr, ccx->ifs_t1_th_h_mask, +			      env->ifs_clm_th_h[0], bb->phy_idx); +	rtw89_phy_write32_idx(rtwdev, ccx->ifs_t2_addr, ccx->ifs_t2_th_h_mask, +			      env->ifs_clm_th_h[1], bb->phy_idx); +	rtw89_phy_write32_idx(rtwdev, ccx->ifs_t3_addr, ccx->ifs_t3_th_h_mask, +			      env->ifs_clm_th_h[2], bb->phy_idx); +	rtw89_phy_write32_idx(rtwdev, ccx->ifs_t4_addr, ccx->ifs_t4_th_h_mask, +			      env->ifs_clm_th_h[3], bb->phy_idx);  	for (i = 0; i < RTW89_IFS_CLM_NUM; i++)  		rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK, @@ -5400,31 +5525,38 @@ static void rtw89_phy_ifs_clm_set_th_reg(struct rtw89_dev *rtwdev)  			    i + 1, env->ifs_clm_th_l[i], env->ifs_clm_th_h[i]);  } -static void rtw89_phy_ifs_clm_setting_init(struct rtw89_dev *rtwdev) +static void rtw89_phy_ifs_clm_setting_init(struct rtw89_dev *rtwdev, +					   struct rtw89_bb_ctx *bb)  {  	const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; -	struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; +	struct rtw89_env_monitor_info *env = &bb->env_monitor;  	const struct rtw89_ccx_regs *ccx = phy->ccx; -	struct rtw89_ccx_para_info para = {0}; +	struct rtw89_ccx_para_info para = {};  	env->ifs_clm_app = RTW89_IFS_CLM_BACKGROUND;  	env->ifs_clm_mntr_time = 0;  	para.ifs_clm_app = RTW89_IFS_CLM_INIT; -	if (rtw89_phy_ifs_clm_th_update_check(rtwdev, ¶)) -		rtw89_phy_ifs_clm_set_th_reg(rtwdev); +	if (rtw89_phy_ifs_clm_th_update_check(rtwdev, bb, ¶)) +		rtw89_phy_ifs_clm_set_th_reg(rtwdev, bb); -	rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_cnt_addr, ccx->ifs_collect_en_mask, true); -	rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t1_addr, ccx->ifs_t1_en_mask, true); -	rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t2_addr, ccx->ifs_t2_en_mask, true); -	rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t3_addr, ccx->ifs_t3_en_mask, true); -	rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t4_addr, ccx->ifs_t4_en_mask, true); +	rtw89_phy_write32_idx(rtwdev, ccx->ifs_cnt_addr, ccx->ifs_collect_en_mask, true, +			      bb->phy_idx); +	rtw89_phy_write32_idx(rtwdev, ccx->ifs_t1_addr, ccx->ifs_t1_en_mask, true, +			      bb->phy_idx); +	rtw89_phy_write32_idx(rtwdev, ccx->ifs_t2_addr, ccx->ifs_t2_en_mask, true, +			      bb->phy_idx); +	rtw89_phy_write32_idx(rtwdev, ccx->ifs_t3_addr, ccx->ifs_t3_en_mask, true, +			      bb->phy_idx); +	rtw89_phy_write32_idx(rtwdev, ccx->ifs_t4_addr, ccx->ifs_t4_en_mask, true, +			      bb->phy_idx);  }  static int rtw89_phy_ccx_racing_ctrl(struct rtw89_dev *rtwdev, +				     struct rtw89_bb_ctx *bb,  				     enum rtw89_env_racing_lv level)  { -	struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; +	struct rtw89_env_monitor_info *env = &bb->env_monitor;  	int ret = 0;  	if (level >= RTW89_RAC_MAX_NUM) { @@ -5453,56 +5585,62 @@ static int rtw89_phy_ccx_racing_ctrl(struct rtw89_dev *rtwdev,  	return ret;  } -static void rtw89_phy_ccx_trigger(struct rtw89_dev *rtwdev) +static void rtw89_phy_ccx_trigger(struct rtw89_dev *rtwdev, +				  struct rtw89_bb_ctx *bb)  {  	const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; -	struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; +	struct rtw89_env_monitor_info *env = &bb->env_monitor;  	const struct rtw89_ccx_regs *ccx = phy->ccx; -	rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_cnt_addr, ccx->ifs_clm_cnt_clear_mask, 0); -	rtw89_phy_set_phy_regs(rtwdev, ccx->setting_addr, ccx->measurement_trig_mask, 0); -	rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_cnt_addr, ccx->ifs_clm_cnt_clear_mask, 1); -	rtw89_phy_set_phy_regs(rtwdev, ccx->setting_addr, ccx->measurement_trig_mask, 1); +	rtw89_phy_write32_idx(rtwdev, ccx->ifs_cnt_addr, ccx->ifs_clm_cnt_clear_mask, 0, +			      bb->phy_idx); +	rtw89_phy_write32_idx(rtwdev, ccx->setting_addr, ccx->measurement_trig_mask, 0, +			      bb->phy_idx); +	rtw89_phy_write32_idx(rtwdev, ccx->ifs_cnt_addr, ccx->ifs_clm_cnt_clear_mask, 1, +			      bb->phy_idx); +	rtw89_phy_write32_idx(rtwdev, ccx->setting_addr, ccx->measurement_trig_mask, 1, +			      bb->phy_idx);  	env->ccx_ongoing = true;  } -static void rtw89_phy_ifs_clm_get_utility(struct rtw89_dev *rtwdev) +static void rtw89_phy_ifs_clm_get_utility(struct rtw89_dev *rtwdev, +					  struct rtw89_bb_ctx *bb)  { -	struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; +	struct rtw89_env_monitor_info *env = &bb->env_monitor;  	u8 i = 0;  	u32 res = 0;  	env->ifs_clm_tx_ratio = -		rtw89_phy_ccx_get_report(rtwdev, env->ifs_clm_tx, PERCENT); +		rtw89_phy_ccx_get_report(rtwdev, bb, env->ifs_clm_tx, PERCENT);  	env->ifs_clm_edcca_excl_cca_ratio = -		rtw89_phy_ccx_get_report(rtwdev, env->ifs_clm_edcca_excl_cca, +		rtw89_phy_ccx_get_report(rtwdev, bb, env->ifs_clm_edcca_excl_cca,  					 PERCENT);  	env->ifs_clm_cck_fa_ratio = -		rtw89_phy_ccx_get_report(rtwdev, env->ifs_clm_cckfa, PERCENT); +		rtw89_phy_ccx_get_report(rtwdev, bb, env->ifs_clm_cckfa, PERCENT);  	env->ifs_clm_ofdm_fa_ratio = -		rtw89_phy_ccx_get_report(rtwdev, env->ifs_clm_ofdmfa, PERCENT); +		rtw89_phy_ccx_get_report(rtwdev, bb, env->ifs_clm_ofdmfa, PERCENT);  	env->ifs_clm_cck_cca_excl_fa_ratio = -		rtw89_phy_ccx_get_report(rtwdev, env->ifs_clm_cckcca_excl_fa, +		rtw89_phy_ccx_get_report(rtwdev, bb, env->ifs_clm_cckcca_excl_fa,  					 PERCENT);  	env->ifs_clm_ofdm_cca_excl_fa_ratio = -		rtw89_phy_ccx_get_report(rtwdev, env->ifs_clm_ofdmcca_excl_fa, +		rtw89_phy_ccx_get_report(rtwdev, bb, env->ifs_clm_ofdmcca_excl_fa,  					 PERCENT);  	env->ifs_clm_cck_fa_permil = -		rtw89_phy_ccx_get_report(rtwdev, env->ifs_clm_cckfa, PERMIL); +		rtw89_phy_ccx_get_report(rtwdev, bb, env->ifs_clm_cckfa, PERMIL);  	env->ifs_clm_ofdm_fa_permil = -		rtw89_phy_ccx_get_report(rtwdev, env->ifs_clm_ofdmfa, PERMIL); +		rtw89_phy_ccx_get_report(rtwdev, bb, env->ifs_clm_ofdmfa, PERMIL);  	for (i = 0; i < RTW89_IFS_CLM_NUM; i++) {  		if (env->ifs_clm_his[i] > ENV_MNTR_IFSCLM_HIS_MAX) {  			env->ifs_clm_ifs_avg[i] = ENV_MNTR_FAIL_DWORD;  		} else {  			env->ifs_clm_ifs_avg[i] = -				rtw89_phy_ccx_idx_to_us(rtwdev, +				rtw89_phy_ccx_idx_to_us(rtwdev, bb,  							env->ifs_clm_avg[i]);  		} -		res = rtw89_phy_ccx_idx_to_us(rtwdev, env->ifs_clm_cca[i]); +		res = rtw89_phy_ccx_idx_to_us(rtwdev, bb, env->ifs_clm_cca[i]);  		res += env->ifs_clm_his[i] >> 1;  		if (env->ifs_clm_his[i])  			res /= env->ifs_clm_his[i]; @@ -5532,81 +5670,82 @@ static void rtw89_phy_ifs_clm_get_utility(struct rtw89_dev *rtwdev)  			    env->ifs_clm_cca_avg[i]);  } -static bool rtw89_phy_ifs_clm_get_result(struct rtw89_dev *rtwdev) +static bool rtw89_phy_ifs_clm_get_result(struct rtw89_dev *rtwdev, +					 struct rtw89_bb_ctx *bb)  {  	const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; -	struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; +	struct rtw89_env_monitor_info *env = &bb->env_monitor;  	const struct rtw89_ccx_regs *ccx = phy->ccx;  	u8 i = 0; -	if (rtw89_phy_read32_mask(rtwdev, ccx->ifs_total_addr, -				  ccx->ifs_cnt_done_mask) == 0) { +	if (rtw89_phy_read32_idx(rtwdev, ccx->ifs_total_addr, +				 ccx->ifs_cnt_done_mask, bb->phy_idx) == 0) {  		rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK,  			    "Get IFS_CLM report Fail\n");  		return false;  	}  	env->ifs_clm_tx = -		rtw89_phy_read32_mask(rtwdev, ccx->ifs_clm_tx_cnt_addr, -				      ccx->ifs_clm_tx_cnt_msk); +		rtw89_phy_read32_idx(rtwdev, ccx->ifs_clm_tx_cnt_addr, +				     ccx->ifs_clm_tx_cnt_msk, bb->phy_idx);  	env->ifs_clm_edcca_excl_cca = -		rtw89_phy_read32_mask(rtwdev, ccx->ifs_clm_tx_cnt_addr, -				      ccx->ifs_clm_edcca_excl_cca_fa_mask); +		rtw89_phy_read32_idx(rtwdev, ccx->ifs_clm_tx_cnt_addr, +				     ccx->ifs_clm_edcca_excl_cca_fa_mask, bb->phy_idx);  	env->ifs_clm_cckcca_excl_fa = -		rtw89_phy_read32_mask(rtwdev, ccx->ifs_clm_cca_addr, -				      ccx->ifs_clm_cckcca_excl_fa_mask); +		rtw89_phy_read32_idx(rtwdev, ccx->ifs_clm_cca_addr, +				     ccx->ifs_clm_cckcca_excl_fa_mask, bb->phy_idx);  	env->ifs_clm_ofdmcca_excl_fa = -		rtw89_phy_read32_mask(rtwdev, ccx->ifs_clm_cca_addr, -				      ccx->ifs_clm_ofdmcca_excl_fa_mask); +		rtw89_phy_read32_idx(rtwdev, ccx->ifs_clm_cca_addr, +				     ccx->ifs_clm_ofdmcca_excl_fa_mask, bb->phy_idx);  	env->ifs_clm_cckfa = -		rtw89_phy_read32_mask(rtwdev, ccx->ifs_clm_fa_addr, -				      ccx->ifs_clm_cck_fa_mask); +		rtw89_phy_read32_idx(rtwdev, ccx->ifs_clm_fa_addr, +				     ccx->ifs_clm_cck_fa_mask, bb->phy_idx);  	env->ifs_clm_ofdmfa = -		rtw89_phy_read32_mask(rtwdev, ccx->ifs_clm_fa_addr, -				      ccx->ifs_clm_ofdm_fa_mask); +		rtw89_phy_read32_idx(rtwdev, ccx->ifs_clm_fa_addr, +				     ccx->ifs_clm_ofdm_fa_mask, bb->phy_idx);  	env->ifs_clm_his[0] = -		rtw89_phy_read32_mask(rtwdev, ccx->ifs_his_addr, -				      ccx->ifs_t1_his_mask); +		rtw89_phy_read32_idx(rtwdev, ccx->ifs_his_addr, +				     ccx->ifs_t1_his_mask, bb->phy_idx);  	env->ifs_clm_his[1] = -		rtw89_phy_read32_mask(rtwdev, ccx->ifs_his_addr, -				      ccx->ifs_t2_his_mask); +		rtw89_phy_read32_idx(rtwdev, ccx->ifs_his_addr, +				     ccx->ifs_t2_his_mask, bb->phy_idx);  	env->ifs_clm_his[2] = -		rtw89_phy_read32_mask(rtwdev, ccx->ifs_his_addr, -				      ccx->ifs_t3_his_mask); +		rtw89_phy_read32_idx(rtwdev, ccx->ifs_his_addr, +				     ccx->ifs_t3_his_mask, bb->phy_idx);  	env->ifs_clm_his[3] = -		rtw89_phy_read32_mask(rtwdev, ccx->ifs_his_addr, -				      ccx->ifs_t4_his_mask); +		rtw89_phy_read32_idx(rtwdev, ccx->ifs_his_addr, +				     ccx->ifs_t4_his_mask, bb->phy_idx);  	env->ifs_clm_avg[0] = -		rtw89_phy_read32_mask(rtwdev, ccx->ifs_avg_l_addr, -				      ccx->ifs_t1_avg_mask); +		rtw89_phy_read32_idx(rtwdev, ccx->ifs_avg_l_addr, +				     ccx->ifs_t1_avg_mask, bb->phy_idx);  	env->ifs_clm_avg[1] = -		rtw89_phy_read32_mask(rtwdev, ccx->ifs_avg_l_addr, -				      ccx->ifs_t2_avg_mask); +		rtw89_phy_read32_idx(rtwdev, ccx->ifs_avg_l_addr, +				     ccx->ifs_t2_avg_mask, bb->phy_idx);  	env->ifs_clm_avg[2] = -		rtw89_phy_read32_mask(rtwdev, ccx->ifs_avg_h_addr, -				      ccx->ifs_t3_avg_mask); +		rtw89_phy_read32_idx(rtwdev, ccx->ifs_avg_h_addr, +				     ccx->ifs_t3_avg_mask, bb->phy_idx);  	env->ifs_clm_avg[3] = -		rtw89_phy_read32_mask(rtwdev, ccx->ifs_avg_h_addr, -				      ccx->ifs_t4_avg_mask); +		rtw89_phy_read32_idx(rtwdev, ccx->ifs_avg_h_addr, +				     ccx->ifs_t4_avg_mask, bb->phy_idx);  	env->ifs_clm_cca[0] = -		rtw89_phy_read32_mask(rtwdev, ccx->ifs_cca_l_addr, -				      ccx->ifs_t1_cca_mask); +		rtw89_phy_read32_idx(rtwdev, ccx->ifs_cca_l_addr, +				     ccx->ifs_t1_cca_mask, bb->phy_idx);  	env->ifs_clm_cca[1] = -		rtw89_phy_read32_mask(rtwdev, ccx->ifs_cca_l_addr, -				      ccx->ifs_t2_cca_mask); +		rtw89_phy_read32_idx(rtwdev, ccx->ifs_cca_l_addr, +				     ccx->ifs_t2_cca_mask, bb->phy_idx);  	env->ifs_clm_cca[2] = -		rtw89_phy_read32_mask(rtwdev, ccx->ifs_cca_h_addr, -				      ccx->ifs_t3_cca_mask); +		rtw89_phy_read32_idx(rtwdev, ccx->ifs_cca_h_addr, +				     ccx->ifs_t3_cca_mask, bb->phy_idx);  	env->ifs_clm_cca[3] = -		rtw89_phy_read32_mask(rtwdev, ccx->ifs_cca_h_addr, -				      ccx->ifs_t4_cca_mask); +		rtw89_phy_read32_idx(rtwdev, ccx->ifs_cca_h_addr, +				     ccx->ifs_t4_cca_mask, bb->phy_idx);  	env->ifs_clm_total_ifs = -		rtw89_phy_read32_mask(rtwdev, ccx->ifs_total_addr, -				      ccx->ifs_total_mask); +		rtw89_phy_read32_idx(rtwdev, ccx->ifs_total_addr, +				     ccx->ifs_total_mask, bb->phy_idx);  	rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK, "IFS-CLM total_ifs = %d\n",  		    env->ifs_clm_total_ifs); @@ -5626,16 +5765,17 @@ static bool rtw89_phy_ifs_clm_get_result(struct rtw89_dev *rtwdev)  			    "T%d:[%d, %d, %d]\n", i + 1, env->ifs_clm_his[i],  			    env->ifs_clm_avg[i], env->ifs_clm_cca[i]); -	rtw89_phy_ifs_clm_get_utility(rtwdev); +	rtw89_phy_ifs_clm_get_utility(rtwdev, bb);  	return true;  }  static int rtw89_phy_ifs_clm_set(struct rtw89_dev *rtwdev, +				 struct rtw89_bb_ctx *bb,  				 struct rtw89_ccx_para_info *para)  {  	const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; -	struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; +	struct rtw89_env_monitor_info *env = &bb->env_monitor;  	const struct rtw89_ccx_regs *ccx = phy->ccx;  	u32 period = 0;  	u32 unit_idx = 0; @@ -5646,17 +5786,17 @@ static int rtw89_phy_ifs_clm_set(struct rtw89_dev *rtwdev,  		return -EINVAL;  	} -	if (rtw89_phy_ccx_racing_ctrl(rtwdev, para->rac_lv)) +	if (rtw89_phy_ccx_racing_ctrl(rtwdev, bb, para->rac_lv))  		return -EINVAL;  	if (para->mntr_time != env->ifs_clm_mntr_time) {  		rtw89_phy_ccx_ms_to_period_unit(rtwdev, para->mntr_time,  						&period, &unit_idx); -		rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_cnt_addr, -				       ccx->ifs_clm_period_mask, period); -		rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_cnt_addr, -				       ccx->ifs_clm_cnt_unit_mask, -				       unit_idx); +		rtw89_phy_write32_idx(rtwdev, ccx->ifs_cnt_addr, +				      ccx->ifs_clm_period_mask, period, bb->phy_idx); +		rtw89_phy_write32_idx(rtwdev, ccx->ifs_cnt_addr, +				      ccx->ifs_clm_cnt_unit_mask, +				      unit_idx, bb->phy_idx);  		rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK,  			    "Update IFS-CLM time ((%d)) -> ((%d))\n", @@ -5667,18 +5807,19 @@ static int rtw89_phy_ifs_clm_set(struct rtw89_dev *rtwdev,  		env->ccx_unit_idx = (u8)unit_idx;  	} -	if (rtw89_phy_ifs_clm_th_update_check(rtwdev, para)) { +	if (rtw89_phy_ifs_clm_th_update_check(rtwdev, bb, para)) {  		env->ifs_clm_app = para->ifs_clm_app; -		rtw89_phy_ifs_clm_set_th_reg(rtwdev); +		rtw89_phy_ifs_clm_set_th_reg(rtwdev, bb);  	}  	return 0;  } -void rtw89_phy_env_monitor_track(struct rtw89_dev *rtwdev) +static void __rtw89_phy_env_monitor_track(struct rtw89_dev *rtwdev, +					  struct rtw89_bb_ctx *bb)  { -	struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; -	struct rtw89_ccx_para_info para = {0}; +	struct rtw89_env_monitor_info *env = &bb->env_monitor; +	struct rtw89_ccx_para_info para = {};  	u8 chk_result = RTW89_PHY_ENV_MON_CCX_FAIL;  	env->ccx_watchdog_result = RTW89_PHY_ENV_MON_CCX_FAIL; @@ -5688,33 +5829,50 @@ void rtw89_phy_env_monitor_track(struct rtw89_dev *rtwdev)  		return;  	} +	rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK, +		    "BB-%d env_monitor track\n", bb->phy_idx); +  	/* only ifs_clm for now */ -	if (rtw89_phy_ifs_clm_get_result(rtwdev)) +	if (rtw89_phy_ifs_clm_get_result(rtwdev, bb))  		env->ccx_watchdog_result |= RTW89_PHY_ENV_MON_IFS_CLM; -	rtw89_phy_ccx_racing_release(rtwdev); +	rtw89_phy_ccx_racing_release(rtwdev, bb);  	para.mntr_time = 1900;  	para.rac_lv = RTW89_RAC_LV_1;  	para.ifs_clm_app = RTW89_IFS_CLM_BACKGROUND; -	if (rtw89_phy_ifs_clm_set(rtwdev, ¶) == 0) +	if (rtw89_phy_ifs_clm_set(rtwdev, bb, ¶) == 0)  		chk_result |= RTW89_PHY_ENV_MON_IFS_CLM;  	if (chk_result) -		rtw89_phy_ccx_trigger(rtwdev); +		rtw89_phy_ccx_trigger(rtwdev, bb);  	rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK,  		    "get_result=0x%x, chk_result:0x%x\n",  		    env->ccx_watchdog_result, chk_result);  } -static bool rtw89_physts_ie_page_valid(enum rtw89_phy_status_bitmap *ie_page) +void rtw89_phy_env_monitor_track(struct rtw89_dev *rtwdev) +{ +	struct rtw89_bb_ctx *bb; + +	rtw89_for_each_active_bb(rtwdev, bb) +		__rtw89_phy_env_monitor_track(rtwdev, bb); +} + +static bool rtw89_physts_ie_page_valid(struct rtw89_dev *rtwdev, +				       enum rtw89_phy_status_bitmap *ie_page)  { +	const struct rtw89_chip_info *chip = rtwdev->chip; +  	if (*ie_page >= RTW89_PHYSTS_BITMAP_NUM ||  	    *ie_page == RTW89_RSVD_9)  		return false; -	else if (*ie_page > RTW89_RSVD_9) +	else if (*ie_page > RTW89_RSVD_9 && *ie_page < RTW89_EHT_PKT)  		*ie_page -= 1; +	if (*ie_page == RTW89_EHT_PKT && chip->chip_gen == RTW89_CHIP_AX) +		return false; +  	return true;  } @@ -5722,6 +5880,9 @@ static u32 rtw89_phy_get_ie_bitmap_addr(enum rtw89_phy_status_bitmap ie_page)  {  	static const u8 ie_page_shift = 2; +	if (ie_page == RTW89_EHT_PKT) +		return R_PHY_STS_BITMAP_EHT; +  	return R_PHY_STS_BITMAP_ADDR_START + (ie_page << ie_page_shift);  } @@ -5731,7 +5892,7 @@ static u32 rtw89_physts_get_ie_bitmap(struct rtw89_dev *rtwdev,  {  	u32 addr; -	if (!rtw89_physts_ie_page_valid(&ie_page)) +	if (!rtw89_physts_ie_page_valid(rtwdev, &ie_page))  		return 0;  	addr = rtw89_phy_get_ie_bitmap_addr(ie_page); @@ -5746,7 +5907,7 @@ static void rtw89_physts_set_ie_bitmap(struct rtw89_dev *rtwdev,  	const struct rtw89_chip_info *chip = rtwdev->chip;  	u32 addr; -	if (!rtw89_physts_ie_page_valid(&ie_page)) +	if (!rtw89_physts_ie_page_valid(rtwdev, &ie_page))  		return;  	if (chip->chip_id == RTL8852A) @@ -5756,21 +5917,6 @@ static void rtw89_physts_set_ie_bitmap(struct rtw89_dev *rtwdev,  	rtw89_phy_write32_idx(rtwdev, addr, MASKDWORD, val, phy_idx);  } -static void rtw89_physts_enable_ie_bitmap(struct rtw89_dev *rtwdev, -					  enum rtw89_phy_status_bitmap bitmap, -					  enum rtw89_phy_status_ie_type ie, -					  bool enable, enum rtw89_phy_idx phy_idx) -{ -	u32 val = rtw89_physts_get_ie_bitmap(rtwdev, bitmap, phy_idx); - -	if (enable) -		val |= BIT(ie); -	else -		val &= ~BIT(ie); - -	rtw89_physts_set_ie_bitmap(rtwdev, bitmap, val, phy_idx); -} -  static void rtw89_physts_enable_fail_report(struct rtw89_dev *rtwdev,  					    bool enable,  					    enum rtw89_phy_idx phy_idx) @@ -5794,30 +5940,37 @@ static void rtw89_physts_enable_fail_report(struct rtw89_dev *rtwdev,  static void __rtw89_physts_parsing_init(struct rtw89_dev *rtwdev,  					enum rtw89_phy_idx phy_idx)  { +	const struct rtw89_chip_info *chip = rtwdev->chip; +	u32 val;  	u8 i;  	rtw89_physts_enable_fail_report(rtwdev, false, phy_idx);  	for (i = 0; i < RTW89_PHYSTS_BITMAP_NUM; i++) { -		if (i >= RTW89_CCK_PKT) -			rtw89_physts_enable_ie_bitmap(rtwdev, i, -						      RTW89_PHYSTS_IE09_FTR_0, -						      true, phy_idx); -		if ((i >= RTW89_CCK_BRK && i <= RTW89_VHT_MU) || -		    (i >= RTW89_RSVD_9 && i <= RTW89_CCK_PKT)) +		if (i == RTW89_RSVD_9 || +		    (i == RTW89_EHT_PKT && chip->chip_gen == RTW89_CHIP_AX))  			continue; -		rtw89_physts_enable_ie_bitmap(rtwdev, i, -					      RTW89_PHYSTS_IE24_OFDM_TD_PATH_A, -					      true, phy_idx); -	} -	rtw89_physts_enable_ie_bitmap(rtwdev, RTW89_VHT_PKT, -				      RTW89_PHYSTS_IE13_DL_MU_DEF, true, phy_idx); -	rtw89_physts_enable_ie_bitmap(rtwdev, RTW89_HE_PKT, -				      RTW89_PHYSTS_IE13_DL_MU_DEF, true, phy_idx); -	/* force IE01 for channel index, only channel field is valid */ -	rtw89_physts_enable_ie_bitmap(rtwdev, RTW89_CCK_PKT, -				      RTW89_PHYSTS_IE01_CMN_OFDM, true, phy_idx); +		val = rtw89_physts_get_ie_bitmap(rtwdev, i, phy_idx); +		if (i == RTW89_HE_MU || i == RTW89_VHT_MU) { +			val |= BIT(RTW89_PHYSTS_IE13_DL_MU_DEF); +		} else if (i == RTW89_TRIG_BASE_PPDU) { +			val |= BIT(RTW89_PHYSTS_IE13_DL_MU_DEF) | +			       BIT(RTW89_PHYSTS_IE01_CMN_OFDM); +		} else if (i >= RTW89_CCK_PKT) { +			val |= BIT(RTW89_PHYSTS_IE09_FTR_0); + +			val &= ~(GENMASK(RTW89_PHYSTS_IE07_CMN_EXT_PATH_D, +					 RTW89_PHYSTS_IE04_CMN_EXT_PATH_A)); + +			if (i == RTW89_CCK_PKT) +				val |= BIT(RTW89_PHYSTS_IE01_CMN_OFDM); +			else if (i >= RTW89_HT_PKT) +				val |= BIT(RTW89_PHYSTS_IE20_DBG_OFDM_FD_USER_SEG_0); +		} + +		rtw89_physts_set_ie_bitmap(rtwdev, i, val, phy_idx); +	}  }  static void rtw89_physts_parsing_init(struct rtw89_dev *rtwdev) @@ -5827,11 +5980,12 @@ static void rtw89_physts_parsing_init(struct rtw89_dev *rtwdev)  		__rtw89_physts_parsing_init(rtwdev, RTW89_PHY_1);  } -static void rtw89_phy_dig_read_gain_table(struct rtw89_dev *rtwdev, int type) +static void rtw89_phy_dig_read_gain_table(struct rtw89_dev *rtwdev, +					  struct rtw89_bb_ctx *bb, int type)  {  	const struct rtw89_chip_info *chip = rtwdev->chip; -	struct rtw89_dig_info *dig = &rtwdev->dig;  	const struct rtw89_phy_dig_gain_cfg *cfg; +	struct rtw89_dig_info *dig = &bb->dig;  	const char *msg;  	u8 i;  	s8 gain_base; @@ -5868,8 +6022,8 @@ static void rtw89_phy_dig_read_gain_table(struct rtw89_dev *rtwdev, int type)  	}  	for (i = 0; i < cfg->size; i++) { -		tmp = rtw89_phy_read32_mask(rtwdev, cfg->table[i].addr, -					    cfg->table[i].mask); +		tmp = rtw89_phy_read32_idx(rtwdev, cfg->table[i].addr, +					   cfg->table[i].mask, bb->phy_idx);  		tmp >>= DIG_GAIN_SHIFT;  		gain_arr[i] = sign_extend32(tmp, U4_MAX_BIT) + gain_base;  		gain_base += DIG_GAIN; @@ -5879,25 +6033,26 @@ static void rtw89_phy_dig_read_gain_table(struct rtw89_dev *rtwdev, int type)  	}  } -static void rtw89_phy_dig_update_gain_para(struct rtw89_dev *rtwdev) +static void rtw89_phy_dig_update_gain_para(struct rtw89_dev *rtwdev, +					   struct rtw89_bb_ctx *bb)  { -	struct rtw89_dig_info *dig = &rtwdev->dig; +	struct rtw89_dig_info *dig = &bb->dig;  	u32 tmp;  	u8 i;  	if (!rtwdev->hal.support_igi)  		return; -	tmp = rtw89_phy_read32_mask(rtwdev, R_PATH0_IB_PKPW, -				    B_PATH0_IB_PKPW_MSK); +	tmp = rtw89_phy_read32_idx(rtwdev, R_PATH0_IB_PKPW, +				   B_PATH0_IB_PKPW_MSK, bb->phy_idx);  	dig->ib_pkpwr = sign_extend32(tmp >> DIG_GAIN_SHIFT, U8_MAX_BIT); -	dig->ib_pbk = rtw89_phy_read32_mask(rtwdev, R_PATH0_IB_PBK, -					    B_PATH0_IB_PBK_MSK); +	dig->ib_pbk = rtw89_phy_read32_idx(rtwdev, R_PATH0_IB_PBK, +					   B_PATH0_IB_PBK_MSK, bb->phy_idx);  	rtw89_debug(rtwdev, RTW89_DBG_DIG, "ib_pkpwr=%d, ib_pbk=%d\n",  		    dig->ib_pkpwr, dig->ib_pbk);  	for (i = RTW89_DIG_GAIN_LNA_G; i < RTW89_DIG_GAIN_MAX; i++) -		rtw89_phy_dig_read_gain_table(rtwdev, i); +		rtw89_phy_dig_read_gain_table(rtwdev, bb, i);  }  static const u8 rssi_nolink = 22; @@ -5906,10 +6061,11 @@ static const u16 fa_th_2g[FA_TH_NUM] = {22, 44, 66, 88};  static const u16 fa_th_5g[FA_TH_NUM] = {4, 8, 12, 16};  static const u16 fa_th_nolink[FA_TH_NUM] = {196, 352, 440, 528}; -static void rtw89_phy_dig_update_rssi_info(struct rtw89_dev *rtwdev) +static void rtw89_phy_dig_update_rssi_info(struct rtw89_dev *rtwdev, +					   struct rtw89_bb_ctx *bb)  { -	struct rtw89_phy_ch_info *ch_info = &rtwdev->ch_info; -	struct rtw89_dig_info *dig = &rtwdev->dig; +	struct rtw89_phy_ch_info *ch_info = &bb->ch_info; +	struct rtw89_dig_info *dig = &bb->dig;  	bool is_linked = rtwdev->total_sta_assoc > 0;  	if (is_linked) { @@ -5920,10 +6076,11 @@ static void rtw89_phy_dig_update_rssi_info(struct rtw89_dev *rtwdev)  	}  } -static void rtw89_phy_dig_update_para(struct rtw89_dev *rtwdev) +static void rtw89_phy_dig_update_para(struct rtw89_dev *rtwdev, +				      struct rtw89_bb_ctx *bb)  { -	struct rtw89_dig_info *dig = &rtwdev->dig; -	const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0); +	const struct rtw89_chan *chan = rtw89_mgnt_chan_get(rtwdev, bb->phy_idx); +	struct rtw89_dig_info *dig = &bb->dig;  	bool is_linked = rtwdev->total_sta_assoc > 0;  	const u16 *fa_th_src = NULL; @@ -5952,9 +6109,10 @@ static const u8 pd_low_th_offset = 16, dynamic_igi_min = 0x20;  static const u8 igi_max_performance_mode = 0x5a;  static const u8 dynamic_pd_threshold_max; -static void rtw89_phy_dig_para_reset(struct rtw89_dev *rtwdev) +static void rtw89_phy_dig_para_reset(struct rtw89_dev *rtwdev, +				     struct rtw89_bb_ctx *bb)  { -	struct rtw89_dig_info *dig = &rtwdev->dig; +	struct rtw89_dig_info *dig = &bb->dig;  	dig->cur_gaincode.lna_idx = LNA_IDX_MAX;  	dig->cur_gaincode.tia_idx = TIA_IDX_MAX; @@ -5970,15 +6128,27 @@ static void rtw89_phy_dig_para_reset(struct rtw89_dev *rtwdev)  	dig->is_linked_pre = false;  } +static void __rtw89_phy_dig_init(struct rtw89_dev *rtwdev, +				 struct rtw89_bb_ctx *bb) +{ +	rtw89_debug(rtwdev, RTW89_DBG_DIG, "BB-%d dig_init\n", bb->phy_idx); + +	rtw89_phy_dig_update_gain_para(rtwdev, bb); +	rtw89_phy_dig_reset(rtwdev, bb); +} +  static void rtw89_phy_dig_init(struct rtw89_dev *rtwdev)  { -	rtw89_phy_dig_update_gain_para(rtwdev); -	rtw89_phy_dig_reset(rtwdev); +	struct rtw89_bb_ctx *bb; + +	rtw89_for_each_capab_bb(rtwdev, bb) +		__rtw89_phy_dig_init(rtwdev, bb);  } -static u8 rtw89_phy_dig_lna_idx_by_rssi(struct rtw89_dev *rtwdev, u8 rssi) +static u8 rtw89_phy_dig_lna_idx_by_rssi(struct rtw89_dev *rtwdev, +					struct rtw89_bb_ctx *bb, u8 rssi)  { -	struct rtw89_dig_info *dig = &rtwdev->dig; +	struct rtw89_dig_info *dig = &bb->dig;  	u8 lna_idx;  	if (rssi < dig->igi_rssi_th[0]) @@ -5997,9 +6167,10 @@ static u8 rtw89_phy_dig_lna_idx_by_rssi(struct rtw89_dev *rtwdev, u8 rssi)  	return lna_idx;  } -static u8 rtw89_phy_dig_tia_idx_by_rssi(struct rtw89_dev *rtwdev, u8 rssi) +static u8 rtw89_phy_dig_tia_idx_by_rssi(struct rtw89_dev *rtwdev, +					struct rtw89_bb_ctx *bb, u8 rssi)  { -	struct rtw89_dig_info *dig = &rtwdev->dig; +	struct rtw89_dig_info *dig = &bb->dig;  	u8 tia_idx;  	if (rssi < dig->igi_rssi_th[0]) @@ -6012,10 +6183,11 @@ static u8 rtw89_phy_dig_tia_idx_by_rssi(struct rtw89_dev *rtwdev, u8 rssi)  #define IB_PBK_BASE 110  #define WB_RSSI_BASE 10 -static u8 rtw89_phy_dig_rxb_idx_by_rssi(struct rtw89_dev *rtwdev, u8 rssi, +static u8 rtw89_phy_dig_rxb_idx_by_rssi(struct rtw89_dev *rtwdev, +					struct rtw89_bb_ctx *bb, u8 rssi,  					struct rtw89_agc_gaincode_set *set)  { -	struct rtw89_dig_info *dig = &rtwdev->dig; +	struct rtw89_dig_info *dig = &bb->dig;  	s8 lna_gain = dig->lna_gain[set->lna_idx];  	s8 tia_gain = dig->tia_gain[set->tia_idx];  	s32 wb_rssi = rssi + lna_gain + tia_gain; @@ -6031,12 +6203,13 @@ static u8 rtw89_phy_dig_rxb_idx_by_rssi(struct rtw89_dev *rtwdev, u8 rssi,  	return rxb_idx;  } -static void rtw89_phy_dig_gaincode_by_rssi(struct rtw89_dev *rtwdev, u8 rssi, +static void rtw89_phy_dig_gaincode_by_rssi(struct rtw89_dev *rtwdev, +					   struct rtw89_bb_ctx *bb, u8 rssi,  					   struct rtw89_agc_gaincode_set *set)  { -	set->lna_idx = rtw89_phy_dig_lna_idx_by_rssi(rtwdev, rssi); -	set->tia_idx = rtw89_phy_dig_tia_idx_by_rssi(rtwdev, rssi); -	set->rxb_idx = rtw89_phy_dig_rxb_idx_by_rssi(rtwdev, rssi, set); +	set->lna_idx = rtw89_phy_dig_lna_idx_by_rssi(rtwdev, bb, rssi); +	set->tia_idx = rtw89_phy_dig_tia_idx_by_rssi(rtwdev, bb, rssi); +	set->rxb_idx = rtw89_phy_dig_rxb_idx_by_rssi(rtwdev, bb, rssi, set);  	rtw89_debug(rtwdev, RTW89_DBG_DIG,  		    "final_rssi=%03d, (lna,tia,rab)=(%d,%d,%02d)\n", @@ -6045,10 +6218,11 @@ static void rtw89_phy_dig_gaincode_by_rssi(struct rtw89_dev *rtwdev, u8 rssi,  #define IGI_OFFSET_MAX 25  #define IGI_OFFSET_MUL 2 -static void rtw89_phy_dig_igi_offset_by_env(struct rtw89_dev *rtwdev) +static void rtw89_phy_dig_igi_offset_by_env(struct rtw89_dev *rtwdev, +					    struct rtw89_bb_ctx *bb)  { -	struct rtw89_dig_info *dig = &rtwdev->dig; -	struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; +	struct rtw89_dig_info *dig = &bb->dig; +	struct rtw89_env_monitor_info *env = &bb->env_monitor;  	enum rtw89_dig_noisy_level noisy_lv;  	u8 igi_offset = dig->fa_rssi_ofst;  	u16 fa_ratio = 0; @@ -6085,96 +6259,98 @@ static void rtw89_phy_dig_igi_offset_by_env(struct rtw89_dev *rtwdev)  		    noisy_lv, igi_offset);  } -static void rtw89_phy_dig_set_lna_idx(struct rtw89_dev *rtwdev, u8 lna_idx) +static void rtw89_phy_dig_set_lna_idx(struct rtw89_dev *rtwdev, +				      struct rtw89_bb_ctx *bb, u8 lna_idx)  {  	const struct rtw89_dig_regs *dig_regs = rtwdev->chip->dig_regs; -	rtw89_phy_write32_mask(rtwdev, dig_regs->p0_lna_init.addr, -			       dig_regs->p0_lna_init.mask, lna_idx); -	rtw89_phy_write32_mask(rtwdev, dig_regs->p1_lna_init.addr, -			       dig_regs->p1_lna_init.mask, lna_idx); +	rtw89_phy_write32_idx(rtwdev, dig_regs->p0_lna_init.addr, +			      dig_regs->p0_lna_init.mask, lna_idx, bb->phy_idx); +	rtw89_phy_write32_idx(rtwdev, dig_regs->p1_lna_init.addr, +			      dig_regs->p1_lna_init.mask, lna_idx, bb->phy_idx);  } -static void rtw89_phy_dig_set_tia_idx(struct rtw89_dev *rtwdev, u8 tia_idx) +static void rtw89_phy_dig_set_tia_idx(struct rtw89_dev *rtwdev, +				      struct rtw89_bb_ctx *bb, u8 tia_idx)  {  	const struct rtw89_dig_regs *dig_regs = rtwdev->chip->dig_regs; -	rtw89_phy_write32_mask(rtwdev, dig_regs->p0_tia_init.addr, -			       dig_regs->p0_tia_init.mask, tia_idx); -	rtw89_phy_write32_mask(rtwdev, dig_regs->p1_tia_init.addr, -			       dig_regs->p1_tia_init.mask, tia_idx); +	rtw89_phy_write32_idx(rtwdev, dig_regs->p0_tia_init.addr, +			      dig_regs->p0_tia_init.mask, tia_idx, bb->phy_idx); +	rtw89_phy_write32_idx(rtwdev, dig_regs->p1_tia_init.addr, +			      dig_regs->p1_tia_init.mask, tia_idx, bb->phy_idx);  } -static void rtw89_phy_dig_set_rxb_idx(struct rtw89_dev *rtwdev, u8 rxb_idx) +static void rtw89_phy_dig_set_rxb_idx(struct rtw89_dev *rtwdev, +				      struct rtw89_bb_ctx *bb, u8 rxb_idx)  {  	const struct rtw89_dig_regs *dig_regs = rtwdev->chip->dig_regs; -	rtw89_phy_write32_mask(rtwdev, dig_regs->p0_rxb_init.addr, -			       dig_regs->p0_rxb_init.mask, rxb_idx); -	rtw89_phy_write32_mask(rtwdev, dig_regs->p1_rxb_init.addr, -			       dig_regs->p1_rxb_init.mask, rxb_idx); +	rtw89_phy_write32_idx(rtwdev, dig_regs->p0_rxb_init.addr, +			      dig_regs->p0_rxb_init.mask, rxb_idx, bb->phy_idx); +	rtw89_phy_write32_idx(rtwdev, dig_regs->p1_rxb_init.addr, +			      dig_regs->p1_rxb_init.mask, rxb_idx, bb->phy_idx);  }  static void rtw89_phy_dig_set_igi_cr(struct rtw89_dev *rtwdev, +				     struct rtw89_bb_ctx *bb,  				     const struct rtw89_agc_gaincode_set set)  {  	if (!rtwdev->hal.support_igi)  		return; -	rtw89_phy_dig_set_lna_idx(rtwdev, set.lna_idx); -	rtw89_phy_dig_set_tia_idx(rtwdev, set.tia_idx); -	rtw89_phy_dig_set_rxb_idx(rtwdev, set.rxb_idx); +	rtw89_phy_dig_set_lna_idx(rtwdev, bb, set.lna_idx); +	rtw89_phy_dig_set_tia_idx(rtwdev, bb, set.tia_idx); +	rtw89_phy_dig_set_rxb_idx(rtwdev, bb, set.rxb_idx);  	rtw89_debug(rtwdev, RTW89_DBG_DIG, "Set (lna,tia,rxb)=((%d,%d,%02d))\n",  		    set.lna_idx, set.tia_idx, set.rxb_idx);  }  static void rtw89_phy_dig_sdagc_follow_pagc_config(struct rtw89_dev *rtwdev, +						   struct rtw89_bb_ctx *bb,  						   bool enable)  {  	const struct rtw89_dig_regs *dig_regs = rtwdev->chip->dig_regs; -	rtw89_phy_write32_mask(rtwdev, dig_regs->p0_p20_pagcugc_en.addr, -			       dig_regs->p0_p20_pagcugc_en.mask, enable); -	rtw89_phy_write32_mask(rtwdev, dig_regs->p0_s20_pagcugc_en.addr, -			       dig_regs->p0_s20_pagcugc_en.mask, enable); -	rtw89_phy_write32_mask(rtwdev, dig_regs->p1_p20_pagcugc_en.addr, -			       dig_regs->p1_p20_pagcugc_en.mask, enable); -	rtw89_phy_write32_mask(rtwdev, dig_regs->p1_s20_pagcugc_en.addr, -			       dig_regs->p1_s20_pagcugc_en.mask, enable); +	rtw89_phy_write32_idx(rtwdev, dig_regs->p0_p20_pagcugc_en.addr, +			      dig_regs->p0_p20_pagcugc_en.mask, enable, bb->phy_idx); +	rtw89_phy_write32_idx(rtwdev, dig_regs->p0_s20_pagcugc_en.addr, +			      dig_regs->p0_s20_pagcugc_en.mask, enable, bb->phy_idx); +	rtw89_phy_write32_idx(rtwdev, dig_regs->p1_p20_pagcugc_en.addr, +			      dig_regs->p1_p20_pagcugc_en.mask, enable, bb->phy_idx); +	rtw89_phy_write32_idx(rtwdev, dig_regs->p1_s20_pagcugc_en.addr, +			      dig_regs->p1_s20_pagcugc_en.mask, enable, bb->phy_idx);  	rtw89_debug(rtwdev, RTW89_DBG_DIG, "sdagc_follow_pagc=%d\n", enable);  } -static void rtw89_phy_dig_config_igi(struct rtw89_dev *rtwdev) +static void rtw89_phy_dig_config_igi(struct rtw89_dev *rtwdev, +				     struct rtw89_bb_ctx *bb)  { -	struct rtw89_dig_info *dig = &rtwdev->dig; +	struct rtw89_dig_info *dig = &bb->dig;  	if (!rtwdev->hal.support_igi)  		return;  	if (dig->force_gaincode_idx_en) { -		rtw89_phy_dig_set_igi_cr(rtwdev, dig->force_gaincode); +		rtw89_phy_dig_set_igi_cr(rtwdev, bb, dig->force_gaincode);  		rtw89_debug(rtwdev, RTW89_DBG_DIG,  			    "Force gaincode index enabled.\n");  	} else { -		rtw89_phy_dig_gaincode_by_rssi(rtwdev, dig->igi_fa_rssi, +		rtw89_phy_dig_gaincode_by_rssi(rtwdev, bb, dig->igi_fa_rssi,  					       &dig->cur_gaincode); -		rtw89_phy_dig_set_igi_cr(rtwdev, dig->cur_gaincode); +		rtw89_phy_dig_set_igi_cr(rtwdev, bb, dig->cur_gaincode);  	}  } -static void rtw89_phy_dig_dyn_pd_th(struct rtw89_dev *rtwdev, u8 rssi, -				    bool enable) +static u8 rtw89_phy_dig_cal_under_region(struct rtw89_dev *rtwdev, +					 struct rtw89_bb_ctx *bb, +					 const struct rtw89_chan *chan)  { -	const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0); -	const struct rtw89_dig_regs *dig_regs = rtwdev->chip->dig_regs;  	enum rtw89_bandwidth cbw = chan->band_width; -	struct rtw89_dig_info *dig = &rtwdev->dig; -	u8 final_rssi = 0, under_region = dig->pd_low_th_ofst; -	u8 ofdm_cca_th; -	s8 cck_cca_th; -	u32 pd_val = 0; +	struct rtw89_dig_info *dig = &bb->dig; +	u8 under_region = dig->pd_low_th_ofst;  	if (rtwdev->chip->chip_gen == RTW89_CHIP_AX)  		under_region += PD_TH_SB_FLTR_CMP_VAL; @@ -6196,6 +6372,20 @@ static void rtw89_phy_dig_dyn_pd_th(struct rtw89_dev *rtwdev, u8 rssi,  		break;  	} +	return under_region; +} + +static u32 __rtw89_phy_dig_dyn_pd_th(struct rtw89_dev *rtwdev, +				     struct rtw89_bb_ctx *bb, +				     u8 rssi, bool enable, +				     const struct rtw89_chan *chan) +{ +	struct rtw89_dig_info *dig = &bb->dig; +	u8 ofdm_cca_th, under_region; +	u8 final_rssi; +	u32 pd_val; + +	under_region = rtw89_phy_dig_cal_under_region(rtwdev, bb, chan);  	dig->dyn_pd_th_max = dig->igi_rssi;  	final_rssi = min_t(u8, rssi, dig->igi_rssi); @@ -6208,18 +6398,38 @@ static void rtw89_phy_dig_dyn_pd_th(struct rtw89_dev *rtwdev, u8 rssi,  			    "igi=%d, ofdm_ccaTH=%d, backoff=%d, PD_low=%d\n",  			    final_rssi, ofdm_cca_th, under_region, pd_val);  	} else { +		pd_val = 0;  		rtw89_debug(rtwdev, RTW89_DBG_DIG,  			    "Dynamic PD th disabled, Set PD_low_bd=0\n");  	} -	rtw89_phy_write32_mask(rtwdev, dig_regs->seg0_pd_reg, -			       dig_regs->pd_lower_bound_mask, pd_val); -	rtw89_phy_write32_mask(rtwdev, dig_regs->seg0_pd_reg, -			       dig_regs->pd_spatial_reuse_en, enable); +	return pd_val; +} + +static void rtw89_phy_dig_dyn_pd_th(struct rtw89_dev *rtwdev, +				    struct rtw89_bb_ctx *bb, +				    u8 rssi, bool enable) +{ +	const struct rtw89_chan *chan = rtw89_mgnt_chan_get(rtwdev, bb->phy_idx); +	const struct rtw89_dig_regs *dig_regs = rtwdev->chip->dig_regs; +	struct rtw89_dig_info *dig = &bb->dig; +	u8 final_rssi, under_region = dig->pd_low_th_ofst; +	s8 cck_cca_th; +	u32 pd_val; + +	pd_val = __rtw89_phy_dig_dyn_pd_th(rtwdev, bb, rssi, enable, chan); +	dig->bak_dig = pd_val; + +	rtw89_phy_write32_idx(rtwdev, dig_regs->seg0_pd_reg, +			      dig_regs->pd_lower_bound_mask, pd_val, bb->phy_idx); +	rtw89_phy_write32_idx(rtwdev, dig_regs->seg0_pd_reg, +			      dig_regs->pd_spatial_reuse_en, enable, bb->phy_idx);  	if (!rtwdev->hal.support_cckpd)  		return; +	final_rssi = min_t(u8, rssi, dig->igi_rssi); +	under_region = rtw89_phy_dig_cal_under_region(rtwdev, bb, chan);  	cck_cca_th = max_t(s8, final_rssi - under_region, CCKPD_TH_MIN_RSSI);  	pd_val = (u32)(cck_cca_th - IGI_RSSI_MAX); @@ -6227,77 +6437,234 @@ static void rtw89_phy_dig_dyn_pd_th(struct rtw89_dev *rtwdev, u8 rssi,  		    "igi=%d, cck_ccaTH=%d, backoff=%d, cck_PD_low=((%d))dB\n",  		    final_rssi, cck_cca_th, under_region, pd_val); -	rtw89_phy_write32_mask(rtwdev, dig_regs->bmode_pd_reg, -			       dig_regs->bmode_cca_rssi_limit_en, enable); -	rtw89_phy_write32_mask(rtwdev, dig_regs->bmode_pd_lower_bound_reg, -			       dig_regs->bmode_rssi_nocca_low_th_mask, pd_val); +	rtw89_phy_write32_idx(rtwdev, dig_regs->bmode_pd_reg, +			      dig_regs->bmode_cca_rssi_limit_en, enable, bb->phy_idx); +	rtw89_phy_write32_idx(rtwdev, dig_regs->bmode_pd_lower_bound_reg, +			      dig_regs->bmode_rssi_nocca_low_th_mask, pd_val, bb->phy_idx);  } -void rtw89_phy_dig_reset(struct rtw89_dev *rtwdev) +void rtw89_phy_dig_reset(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb)  { -	struct rtw89_dig_info *dig = &rtwdev->dig; +	struct rtw89_dig_info *dig = &bb->dig;  	dig->bypass_dig = false; -	rtw89_phy_dig_para_reset(rtwdev); -	rtw89_phy_dig_set_igi_cr(rtwdev, dig->force_gaincode); -	rtw89_phy_dig_dyn_pd_th(rtwdev, rssi_nolink, false); -	rtw89_phy_dig_sdagc_follow_pagc_config(rtwdev, false); -	rtw89_phy_dig_update_para(rtwdev); +	rtw89_phy_dig_para_reset(rtwdev, bb); +	rtw89_phy_dig_set_igi_cr(rtwdev, bb, dig->force_gaincode); +	rtw89_phy_dig_dyn_pd_th(rtwdev, bb, rssi_nolink, false); +	rtw89_phy_dig_sdagc_follow_pagc_config(rtwdev, bb, false); +	rtw89_phy_dig_update_para(rtwdev, bb);  }  #define IGI_RSSI_MIN 10  #define ABS_IGI_MIN 0xc -void rtw89_phy_dig(struct rtw89_dev *rtwdev) +static +void rtw89_phy_cal_igi_fa_rssi(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb)  { -	struct rtw89_dig_info *dig = &rtwdev->dig; -	bool is_linked = rtwdev->total_sta_assoc > 0; +	struct rtw89_dig_info *dig = &bb->dig;  	u8 igi_min; +	rtw89_phy_dig_igi_offset_by_env(rtwdev, bb); + +	igi_min = max_t(int, dig->igi_rssi - IGI_RSSI_MIN, 0); +	dig->dyn_igi_max = min(igi_min + IGI_OFFSET_MAX, igi_max_performance_mode); +	dig->dyn_igi_min = max(igi_min, ABS_IGI_MIN); + +	if (dig->dyn_igi_max >= dig->dyn_igi_min) { +		dig->igi_fa_rssi += dig->fa_rssi_ofst; +		dig->igi_fa_rssi = clamp(dig->igi_fa_rssi, dig->dyn_igi_min, +					 dig->dyn_igi_max); +	} else { +		dig->igi_fa_rssi = dig->dyn_igi_max; +	} +} + +struct rtw89_phy_iter_mcc_dig { +	struct rtw89_vif_link *rtwvif_link; +	bool has_sta; +	u8 rssi_min; +}; + +static void rtw89_phy_set_mcc_dig(struct rtw89_dev *rtwdev, +				  struct rtw89_vif_link *rtwvif_link, +				  struct rtw89_bb_ctx *bb, +				  u8 rssi_min, u8 mcc_role_idx, +				  bool is_linked) +{ +	struct rtw89_dig_info *dig = &bb->dig; +	const struct rtw89_chan *chan; +	u8 pd_val; + +	if (is_linked) { +		dig->igi_rssi = rssi_min >> 1; +		dig->igi_fa_rssi = dig->igi_rssi; +	} else { +		rtw89_debug(rtwdev, RTW89_DBG_DIG, "RSSI update : NO Link\n"); +		dig->igi_rssi = rssi_nolink; +		dig->igi_fa_rssi = dig->igi_rssi; +	} + +	chan = rtw89_chan_get(rtwdev, rtwvif_link->chanctx_idx); +	rtw89_phy_cal_igi_fa_rssi(rtwdev, bb); +	pd_val = __rtw89_phy_dig_dyn_pd_th(rtwdev, bb, dig->igi_fa_rssi, +					   is_linked, chan); +	rtw89_fw_h2c_mcc_dig(rtwdev, rtwvif_link->chanctx_idx, +			     mcc_role_idx, pd_val, true); + +	rtw89_debug(rtwdev, RTW89_DBG_DIG, +		    "MCC chanctx_idx %d chan %d rssi %d pd_val %d", +		    rtwvif_link->chanctx_idx, chan->primary_channel, +		    dig->igi_rssi, pd_val); +} + +static void rtw89_phy_set_mcc_dig_iter(void *data, struct ieee80211_sta *sta) +{ +	struct rtw89_phy_iter_mcc_dig *mcc_dig = (struct rtw89_phy_iter_mcc_dig *)data; +	unsigned int link_id = mcc_dig->rtwvif_link->link_id; +	struct rtw89_sta *rtwsta = sta_to_rtwsta(sta); +	struct rtw89_sta_link *rtwsta_link; + +	if (rtwsta->rtwvif != mcc_dig->rtwvif_link->rtwvif) +		return; + +	rtwsta_link = rtwsta->links[link_id]; +	if (!rtwsta_link) +		return; + +	mcc_dig->has_sta = true; +	if (ewma_rssi_read(&rtwsta_link->avg_rssi) < mcc_dig->rssi_min) +		mcc_dig->rssi_min = ewma_rssi_read(&rtwsta_link->avg_rssi); +} + +static void rtw89_phy_dig_mcc(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) +{ +	struct rtw89_phy_iter_mcc_dig mcc_dig; +	struct rtw89_vif_link *rtwvif_link; +	struct rtw89_mcc_links_info info; +	int i; + +	rtw89_mcc_get_links(rtwdev, &info); +	for (i = 0; i < ARRAY_SIZE(info.links); i++) { +		rtwvif_link = info.links[i]; +		if (!rtwvif_link) +			continue; + +		memset(&mcc_dig, 0, sizeof(mcc_dig)); +		mcc_dig.rtwvif_link = rtwvif_link; +		mcc_dig.has_sta = false; +		mcc_dig.rssi_min = U8_MAX; +		ieee80211_iterate_stations_atomic(rtwdev->hw, +						  rtw89_phy_set_mcc_dig_iter, +						  &mcc_dig); + +		rtw89_phy_set_mcc_dig(rtwdev, rtwvif_link, bb, +				      mcc_dig.rssi_min, i, mcc_dig.has_sta); +	} +} + +static void rtw89_phy_dig_ctrl(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb, +			       bool pause_dig, bool restore) +{ +	const struct rtw89_dig_regs *dig_regs = rtwdev->chip->dig_regs; +	struct rtw89_dig_info *dig = &bb->dig; +	bool en_dig; +	u32 pd_val; + +	if (dig->pause_dig == pause_dig) +		return; + +	if (pause_dig) { +		en_dig = false; +		pd_val = 0; +	} else { +		en_dig = rtwdev->total_sta_assoc > 0; +		pd_val = restore ? dig->bak_dig : 0; +	} + +	rtw89_debug(rtwdev, RTW89_DBG_DIG, "%s <%s> PD_low=%d", __func__, +		    pause_dig ? "suspend" : "resume", pd_val); + +	rtw89_phy_write32_idx(rtwdev, dig_regs->seg0_pd_reg, +			      dig_regs->pd_lower_bound_mask, pd_val, bb->phy_idx); +	rtw89_phy_write32_idx(rtwdev, dig_regs->seg0_pd_reg, +			      dig_regs->pd_spatial_reuse_en, en_dig, bb->phy_idx); + +	dig->pause_dig = pause_dig; +} + +void rtw89_phy_dig_suspend(struct rtw89_dev *rtwdev) +{ +	struct rtw89_bb_ctx *bb; + +	rtw89_for_each_active_bb(rtwdev, bb) +		rtw89_phy_dig_ctrl(rtwdev, bb, true, false); +} + +void rtw89_phy_dig_resume(struct rtw89_dev *rtwdev, bool restore) +{ +	struct rtw89_bb_ctx *bb; + +	rtw89_for_each_active_bb(rtwdev, bb) +		rtw89_phy_dig_ctrl(rtwdev, bb, false, restore); +} + +static void __rtw89_phy_dig(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) +{ +	struct rtw89_dig_info *dig = &bb->dig; +	bool is_linked = rtwdev->total_sta_assoc > 0; +	enum rtw89_entity_mode mode; +  	if (unlikely(dig->bypass_dig)) {  		dig->bypass_dig = false;  		return;  	} -	rtw89_phy_dig_update_rssi_info(rtwdev); +	rtw89_debug(rtwdev, RTW89_DBG_DIG, "BB-%d dig track\n", bb->phy_idx); + +	rtw89_phy_dig_update_rssi_info(rtwdev, bb); + +	mode = rtw89_get_entity_mode(rtwdev); +	if (mode == RTW89_ENTITY_MODE_MCC) { +		rtw89_phy_dig_mcc(rtwdev, bb); +		return; +	} + +	if (unlikely(dig->pause_dig)) +		return;  	if (!dig->is_linked_pre && is_linked) {  		rtw89_debug(rtwdev, RTW89_DBG_DIG, "First connected\n"); -		rtw89_phy_dig_update_para(rtwdev); +		rtw89_phy_dig_update_para(rtwdev, bb);  		dig->igi_fa_rssi = dig->igi_rssi;  	} else if (dig->is_linked_pre && !is_linked) {  		rtw89_debug(rtwdev, RTW89_DBG_DIG, "First disconnected\n"); -		rtw89_phy_dig_update_para(rtwdev); +		rtw89_phy_dig_update_para(rtwdev, bb);  		dig->igi_fa_rssi = dig->igi_rssi;  	}  	dig->is_linked_pre = is_linked; -	rtw89_phy_dig_igi_offset_by_env(rtwdev); - -	igi_min = max_t(int, dig->igi_rssi - IGI_RSSI_MIN, 0); -	dig->dyn_igi_max = min(igi_min + IGI_OFFSET_MAX, igi_max_performance_mode); -	dig->dyn_igi_min = max(igi_min, ABS_IGI_MIN); - -	if (dig->dyn_igi_max >= dig->dyn_igi_min) { -		dig->igi_fa_rssi += dig->fa_rssi_ofst; -		dig->igi_fa_rssi = clamp(dig->igi_fa_rssi, dig->dyn_igi_min, -					 dig->dyn_igi_max); -	} else { -		dig->igi_fa_rssi = dig->dyn_igi_max; -	} +	rtw89_phy_cal_igi_fa_rssi(rtwdev, bb);  	rtw89_debug(rtwdev, RTW89_DBG_DIG,  		    "rssi=%03d, dyn_joint(max,min)=(%d,%d), final_rssi=%d\n",  		    dig->igi_rssi, dig->dyn_igi_max, dig->dyn_igi_min,  		    dig->igi_fa_rssi); -	rtw89_phy_dig_config_igi(rtwdev); +	rtw89_phy_dig_config_igi(rtwdev, bb); -	rtw89_phy_dig_dyn_pd_th(rtwdev, dig->igi_fa_rssi, dig->dyn_pd_th_en); +	rtw89_phy_dig_dyn_pd_th(rtwdev, bb, dig->igi_fa_rssi, dig->dyn_pd_th_en);  	if (dig->dyn_pd_th_en && dig->igi_fa_rssi > dig->dyn_pd_th_max) -		rtw89_phy_dig_sdagc_follow_pagc_config(rtwdev, true); +		rtw89_phy_dig_sdagc_follow_pagc_config(rtwdev, bb, true);  	else -		rtw89_phy_dig_sdagc_follow_pagc_config(rtwdev, false); +		rtw89_phy_dig_sdagc_follow_pagc_config(rtwdev, bb, false); +} + +void rtw89_phy_dig(struct rtw89_dev *rtwdev) +{ +	struct rtw89_bb_ctx *bb; + +	rtw89_for_each_active_bb(rtwdev, bb) +		__rtw89_phy_dig(rtwdev, bb);  }  static void __rtw89_phy_tx_path_div_sta_iter(struct rtw89_dev *rtwdev, @@ -6471,17 +6838,17 @@ static void rtw89_phy_antdiv_training_state(struct rtw89_dev *rtwdev)  	}  	antdiv->training_count++; -	ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->antdiv_work, -				     state_period); +	wiphy_delayed_work_queue(rtwdev->hw->wiphy, &rtwdev->antdiv_work, +				 state_period);  } -void rtw89_phy_antdiv_work(struct work_struct *work) +void rtw89_phy_antdiv_work(struct wiphy *wiphy, struct wiphy_work *work)  {  	struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,  						antdiv_work.work);  	struct rtw89_antdiv_info *antdiv = &rtwdev->antdiv; -	mutex_lock(&rtwdev->mutex); +	lockdep_assert_wiphy(wiphy);  	if (antdiv->training_count <= ANTDIV_TRAINNING_CNT) {  		rtw89_phy_antdiv_training_state(rtwdev); @@ -6489,8 +6856,6 @@ void rtw89_phy_antdiv_work(struct work_struct *work)  		rtw89_phy_antdiv_decision_state(rtwdev);  		rtw89_phy_antdiv_set_ant(rtwdev);  	} - -	mutex_unlock(&rtwdev->mutex);  }  void rtw89_phy_antdiv_track(struct rtw89_dev *rtwdev) @@ -6511,19 +6876,34 @@ void rtw89_phy_antdiv_track(struct rtw89_dev *rtwdev)  		return;  	antdiv->training_count = 0; -	ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->antdiv_work, 0); +	wiphy_delayed_work_queue(rtwdev->hw->wiphy, &rtwdev->antdiv_work, 0); +} + +static void __rtw89_phy_env_monitor_init(struct rtw89_dev *rtwdev, +					 struct rtw89_bb_ctx *bb) +{ +	rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK, +		    "BB-%d env_monitor init\n", bb->phy_idx); + +	rtw89_phy_ccx_top_setting_init(rtwdev, bb); +	rtw89_phy_ifs_clm_setting_init(rtwdev, bb);  }  static void rtw89_phy_env_monitor_init(struct rtw89_dev *rtwdev)  { -	rtw89_phy_ccx_top_setting_init(rtwdev); -	rtw89_phy_ifs_clm_setting_init(rtwdev); +	struct rtw89_bb_ctx *bb; + +	rtw89_for_each_capab_bb(rtwdev, bb) +		__rtw89_phy_env_monitor_init(rtwdev, bb);  } -static void rtw89_phy_edcca_init(struct rtw89_dev *rtwdev) +static void __rtw89_phy_edcca_init(struct rtw89_dev *rtwdev, +				   struct rtw89_bb_ctx *bb)  {  	const struct rtw89_edcca_regs *edcca_regs = rtwdev->chip->edcca_regs; -	struct rtw89_edcca_bak *edcca_bak = &rtwdev->hal.edcca_bak; +	struct rtw89_edcca_bak *edcca_bak = &bb->edcca_bak; + +	rtw89_debug(rtwdev, RTW89_DBG_EDCCA, "BB-%d edcca init\n", bb->phy_idx);  	memset(edcca_bak, 0, sizeof(*edcca_bak)); @@ -6539,8 +6919,16 @@ static void rtw89_phy_edcca_init(struct rtw89_dev *rtwdev)  		rtw89_phy_set_phy_regs(rtwdev, R_DFS_FFT_CG, B_DFS_FFT_EN, 1);  	} -	rtw89_phy_write32_mask(rtwdev, edcca_regs->tx_collision_t2r_st, -			       edcca_regs->tx_collision_t2r_st_mask, 0x29); +	rtw89_phy_write32_idx(rtwdev, edcca_regs->tx_collision_t2r_st, +			      edcca_regs->tx_collision_t2r_st_mask, 0x29, bb->phy_idx); +} + +static void rtw89_phy_edcca_init(struct rtw89_dev *rtwdev) +{ +	struct rtw89_bb_ctx *bb; + +	rtw89_for_each_capab_bb(rtwdev, bb) +		__rtw89_phy_edcca_init(rtwdev, bb);  }  void rtw89_phy_dm_init(struct rtw89_dev *rtwdev) @@ -6907,60 +7295,67 @@ void rtw89_decode_chan_idx(struct rtw89_dev *rtwdev, u8 chan_idx,  }  EXPORT_SYMBOL(rtw89_decode_chan_idx); -void rtw89_phy_config_edcca(struct rtw89_dev *rtwdev, bool scan) +void rtw89_phy_config_edcca(struct rtw89_dev *rtwdev, +			    struct rtw89_bb_ctx *bb, bool scan)  {  	const struct rtw89_edcca_regs *edcca_regs = rtwdev->chip->edcca_regs; -	struct rtw89_edcca_bak *edcca_bak = &rtwdev->hal.edcca_bak; +	struct rtw89_edcca_bak *edcca_bak = &bb->edcca_bak;  	if (scan) {  		edcca_bak->a = -			rtw89_phy_read32_mask(rtwdev, edcca_regs->edcca_level, -					      edcca_regs->edcca_mask); +			rtw89_phy_read32_idx(rtwdev, edcca_regs->edcca_level, +					     edcca_regs->edcca_mask, bb->phy_idx);  		edcca_bak->p = -			rtw89_phy_read32_mask(rtwdev, edcca_regs->edcca_level, -					      edcca_regs->edcca_p_mask); +			rtw89_phy_read32_idx(rtwdev, edcca_regs->edcca_level, +					     edcca_regs->edcca_p_mask, bb->phy_idx);  		edcca_bak->ppdu = -			rtw89_phy_read32_mask(rtwdev, edcca_regs->ppdu_level, -					      edcca_regs->ppdu_mask); - -		rtw89_phy_write32_mask(rtwdev, edcca_regs->edcca_level, -				       edcca_regs->edcca_mask, EDCCA_MAX); -		rtw89_phy_write32_mask(rtwdev, edcca_regs->edcca_level, -				       edcca_regs->edcca_p_mask, EDCCA_MAX); -		rtw89_phy_write32_mask(rtwdev, edcca_regs->ppdu_level, -				       edcca_regs->ppdu_mask, EDCCA_MAX); +			rtw89_phy_read32_idx(rtwdev, edcca_regs->ppdu_level, +					     edcca_regs->ppdu_mask, bb->phy_idx); + +		rtw89_phy_write32_idx(rtwdev, edcca_regs->edcca_level, +				      edcca_regs->edcca_mask, EDCCA_MAX, bb->phy_idx); +		rtw89_phy_write32_idx(rtwdev, edcca_regs->edcca_level, +				      edcca_regs->edcca_p_mask, EDCCA_MAX, bb->phy_idx); +		rtw89_phy_write32_idx(rtwdev, edcca_regs->ppdu_level, +				      edcca_regs->ppdu_mask, EDCCA_MAX, bb->phy_idx);  	} else { -		rtw89_phy_write32_mask(rtwdev, edcca_regs->edcca_level, -				       edcca_regs->edcca_mask, -				       edcca_bak->a); -		rtw89_phy_write32_mask(rtwdev, edcca_regs->edcca_level, -				       edcca_regs->edcca_p_mask, -				       edcca_bak->p); -		rtw89_phy_write32_mask(rtwdev, edcca_regs->ppdu_level, -				       edcca_regs->ppdu_mask, -				       edcca_bak->ppdu); +		rtw89_phy_write32_idx(rtwdev, edcca_regs->edcca_level, +				      edcca_regs->edcca_mask, +				      edcca_bak->a, bb->phy_idx); +		rtw89_phy_write32_idx(rtwdev, edcca_regs->edcca_level, +				      edcca_regs->edcca_p_mask, +				      edcca_bak->p, bb->phy_idx); +		rtw89_phy_write32_idx(rtwdev, edcca_regs->ppdu_level, +				      edcca_regs->ppdu_mask, +				      edcca_bak->ppdu, bb->phy_idx);  	}  } -static void rtw89_phy_edcca_log(struct rtw89_dev *rtwdev) +static void rtw89_phy_edcca_log(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb)  {  	const struct rtw89_edcca_regs *edcca_regs = rtwdev->chip->edcca_regs; +	const struct rtw89_edcca_p_regs *edcca_p_regs;  	bool flag_fb, flag_p20, flag_s20, flag_s40, flag_s80;  	s8 pwdb_fb, pwdb_p20, pwdb_s20, pwdb_s40, pwdb_s80; -	u8 path, per20_bitmap; +	u8 path, per20_bitmap = 0;  	u8 pwdb[8];  	u32 tmp;  	if (!rtw89_debug_is_enabled(rtwdev, RTW89_DBG_EDCCA))  		return; +	if (bb->phy_idx == RTW89_PHY_1) +		edcca_p_regs = &edcca_regs->p[RTW89_PHY_1]; +	else +		edcca_p_regs = &edcca_regs->p[RTW89_PHY_0]; +  	if (rtwdev->chip->chip_id == RTL8922A)  		rtw89_phy_write32_mask(rtwdev, edcca_regs->rpt_sel_be,  				       edcca_regs->rpt_sel_be_mask, 0); -	rtw89_phy_write32_mask(rtwdev, edcca_regs->rpt_sel, -			       edcca_regs->rpt_sel_mask, 0); -	tmp = rtw89_phy_read32(rtwdev, edcca_regs->rpt_b); +	rtw89_phy_write32_mask(rtwdev, edcca_p_regs->rpt_sel, +			       edcca_p_regs->rpt_sel_mask, 0); +	tmp = rtw89_phy_read32(rtwdev, edcca_p_regs->rpt_b);  	path = u32_get_bits(tmp, B_EDCCA_RPT_B_PATH_MASK);  	flag_s80 = u32_get_bits(tmp, B_EDCCA_RPT_B_S80);  	flag_s40 = u32_get_bits(tmp, B_EDCCA_RPT_B_S40); @@ -6971,53 +7366,52 @@ static void rtw89_phy_edcca_log(struct rtw89_dev *rtwdev)  	pwdb_p20 = u32_get_bits(tmp, MASKBYTE2);  	pwdb_fb = u32_get_bits(tmp, MASKBYTE3); -	rtw89_phy_write32_mask(rtwdev, edcca_regs->rpt_sel, -			       edcca_regs->rpt_sel_mask, 4); -	tmp = rtw89_phy_read32(rtwdev, edcca_regs->rpt_b); +	rtw89_phy_write32_mask(rtwdev, edcca_p_regs->rpt_sel, +			       edcca_p_regs->rpt_sel_mask, 5); +	tmp = rtw89_phy_read32(rtwdev, edcca_p_regs->rpt_b);  	pwdb_s80 = u32_get_bits(tmp, MASKBYTE1);  	pwdb_s40 = u32_get_bits(tmp, MASKBYTE2); -	per20_bitmap = rtw89_phy_read32_mask(rtwdev, edcca_regs->rpt_a, -					     MASKBYTE0); -  	if (rtwdev->chip->chip_id == RTL8922A) {  		rtw89_phy_write32_mask(rtwdev, edcca_regs->rpt_sel_be,  				       edcca_regs->rpt_sel_be_mask, 4); -		tmp = rtw89_phy_read32(rtwdev, edcca_regs->rpt_b); +		tmp = rtw89_phy_read32(rtwdev, edcca_p_regs->rpt_b);  		pwdb[0] = u32_get_bits(tmp, MASKBYTE3);  		pwdb[1] = u32_get_bits(tmp, MASKBYTE2);  		pwdb[2] = u32_get_bits(tmp, MASKBYTE1);  		pwdb[3] = u32_get_bits(tmp, MASKBYTE0); +		per20_bitmap = rtw89_phy_read32_mask(rtwdev, edcca_p_regs->rpt_a, +						     MASKBYTE0);  		rtw89_phy_write32_mask(rtwdev, edcca_regs->rpt_sel_be,  				       edcca_regs->rpt_sel_be_mask, 5); -		tmp = rtw89_phy_read32(rtwdev, edcca_regs->rpt_b); +		tmp = rtw89_phy_read32(rtwdev, edcca_p_regs->rpt_b);  		pwdb[4] = u32_get_bits(tmp, MASKBYTE3);  		pwdb[5] = u32_get_bits(tmp, MASKBYTE2);  		pwdb[6] = u32_get_bits(tmp, MASKBYTE1);  		pwdb[7] = u32_get_bits(tmp, MASKBYTE0);  	} else { -		rtw89_phy_write32_mask(rtwdev, edcca_regs->rpt_sel, -				       edcca_regs->rpt_sel_mask, 0); -		tmp = rtw89_phy_read32(rtwdev, edcca_regs->rpt_a); +		rtw89_phy_write32_mask(rtwdev, edcca_p_regs->rpt_sel, +				       edcca_p_regs->rpt_sel_mask, 0); +		tmp = rtw89_phy_read32(rtwdev, edcca_p_regs->rpt_a);  		pwdb[0] = u32_get_bits(tmp, MASKBYTE3);  		pwdb[1] = u32_get_bits(tmp, MASKBYTE2); -		rtw89_phy_write32_mask(rtwdev, edcca_regs->rpt_sel, -				       edcca_regs->rpt_sel_mask, 1); -		tmp = rtw89_phy_read32(rtwdev, edcca_regs->rpt_a); +		rtw89_phy_write32_mask(rtwdev, edcca_p_regs->rpt_sel, +				       edcca_p_regs->rpt_sel_mask, 5); +		tmp = rtw89_phy_read32(rtwdev, edcca_p_regs->rpt_a);  		pwdb[2] = u32_get_bits(tmp, MASKBYTE3);  		pwdb[3] = u32_get_bits(tmp, MASKBYTE2); -		rtw89_phy_write32_mask(rtwdev, edcca_regs->rpt_sel, -				       edcca_regs->rpt_sel_mask, 2); -		tmp = rtw89_phy_read32(rtwdev, edcca_regs->rpt_a); +		rtw89_phy_write32_mask(rtwdev, edcca_p_regs->rpt_sel, +				       edcca_p_regs->rpt_sel_mask, 2); +		tmp = rtw89_phy_read32(rtwdev, edcca_p_regs->rpt_a);  		pwdb[4] = u32_get_bits(tmp, MASKBYTE3);  		pwdb[5] = u32_get_bits(tmp, MASKBYTE2); -		rtw89_phy_write32_mask(rtwdev, edcca_regs->rpt_sel, -				       edcca_regs->rpt_sel_mask, 3); -		tmp = rtw89_phy_read32(rtwdev, edcca_regs->rpt_a); +		rtw89_phy_write32_mask(rtwdev, edcca_p_regs->rpt_sel, +				       edcca_p_regs->rpt_sel_mask, 3); +		tmp = rtw89_phy_read32(rtwdev, edcca_p_regs->rpt_a);  		pwdb[6] = u32_get_bits(tmp, MASKBYTE3);  		pwdb[7] = u32_get_bits(tmp, MASKBYTE2);  	} @@ -7039,9 +7433,10 @@ static void rtw89_phy_edcca_log(struct rtw89_dev *rtwdev)  		    pwdb_fb, pwdb_p20, pwdb_s20, pwdb_s40, pwdb_s80);  } -static u8 rtw89_phy_edcca_get_thre_by_rssi(struct rtw89_dev *rtwdev) +static u8 rtw89_phy_edcca_get_thre_by_rssi(struct rtw89_dev *rtwdev, +					   struct rtw89_bb_ctx *bb)  { -	struct rtw89_phy_ch_info *ch_info = &rtwdev->ch_info; +	struct rtw89_phy_ch_info *ch_info = &bb->ch_info;  	bool is_linked = rtwdev->total_sta_assoc > 0;  	u8 rssi_min = ch_info->rssi_min >> 1;  	u8 edcca_thre; @@ -7057,13 +7452,13 @@ static u8 rtw89_phy_edcca_get_thre_by_rssi(struct rtw89_dev *rtwdev)  	return edcca_thre;  } -void rtw89_phy_edcca_thre_calc(struct rtw89_dev *rtwdev) +void rtw89_phy_edcca_thre_calc(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb)  {  	const struct rtw89_edcca_regs *edcca_regs = rtwdev->chip->edcca_regs; -	struct rtw89_edcca_bak *edcca_bak = &rtwdev->hal.edcca_bak; +	struct rtw89_edcca_bak *edcca_bak = &bb->edcca_bak;  	u8 th; -	th = rtw89_phy_edcca_get_thre_by_rssi(rtwdev); +	th = rtw89_phy_edcca_get_thre_by_rssi(rtwdev, bb);  	if (th == edcca_bak->th_old)  		return; @@ -7072,23 +7467,33 @@ void rtw89_phy_edcca_thre_calc(struct rtw89_dev *rtwdev)  	rtw89_debug(rtwdev, RTW89_DBG_EDCCA,  		    "[EDCCA]: Normal Mode, EDCCA_th = %d\n", th); -	rtw89_phy_write32_mask(rtwdev, edcca_regs->edcca_level, -			       edcca_regs->edcca_mask, th); -	rtw89_phy_write32_mask(rtwdev, edcca_regs->edcca_level, -			       edcca_regs->edcca_p_mask, th); -	rtw89_phy_write32_mask(rtwdev, edcca_regs->ppdu_level, -			       edcca_regs->ppdu_mask, th); +	rtw89_phy_write32_idx(rtwdev, edcca_regs->edcca_level, +			      edcca_regs->edcca_mask, th, bb->phy_idx); +	rtw89_phy_write32_idx(rtwdev, edcca_regs->edcca_level, +			      edcca_regs->edcca_p_mask, th, bb->phy_idx); +	rtw89_phy_write32_idx(rtwdev, edcca_regs->ppdu_level, +			      edcca_regs->ppdu_mask, th, bb->phy_idx); +} + +static +void __rtw89_phy_edcca_track(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) +{ +	rtw89_debug(rtwdev, RTW89_DBG_EDCCA, "BB-%d edcca track\n", bb->phy_idx); + +	rtw89_phy_edcca_thre_calc(rtwdev, bb); +	rtw89_phy_edcca_log(rtwdev, bb);  }  void rtw89_phy_edcca_track(struct rtw89_dev *rtwdev)  {  	struct rtw89_hal *hal = &rtwdev->hal; +	struct rtw89_bb_ctx *bb;  	if (hal->disabled_dm_bitmap & BIT(RTW89_DM_DYNAMIC_EDCCA))  		return; -	rtw89_phy_edcca_thre_calc(rtwdev); -	rtw89_phy_edcca_log(rtwdev); +	rtw89_for_each_active_bb(rtwdev, bb) +		__rtw89_phy_edcca_track(rtwdev, bb);  }  enum rtw89_rf_path_bit rtw89_phy_get_kpath(struct rtw89_dev *rtwdev, diff --git a/sys/contrib/dev/rtw89/phy.h b/sys/contrib/dev/rtw89/phy.h index 08b635c93ac3..dc156376d951 100644 --- a/sys/contrib/dev/rtw89/phy.h +++ b/sys/contrib/dev/rtw89/phy.h @@ -164,6 +164,7 @@ enum rtw89_phy_c2h_dm_func {  	RTW89_PHY_C2H_DM_FUNC_SIGB,  	RTW89_PHY_C2H_DM_FUNC_LOWRT_RTY,  	RTW89_PHY_C2H_DM_FUNC_MCC_DIG, +	RTW89_PHY_C2H_DM_FUNC_FW_SCAN = 0xc,  	RTW89_PHY_C2H_DM_FUNC_NUM,  }; @@ -251,6 +252,7 @@ enum rtw89_phy_status_bitmap {  	RTW89_HT_PKT          = 13,  	RTW89_VHT_PKT         = 14,  	RTW89_HE_PKT          = 15, +	RTW89_EHT_PKT         = 16,  	RTW89_PHYSTS_BITMAP_NUM  }; @@ -835,8 +837,8 @@ s8 rtw89_phy_read_txpwr_byrate(struct rtw89_dev *rtwdev, u8 band, u8 bw,  void rtw89_phy_ant_gain_init(struct rtw89_dev *rtwdev);  s16 rtw89_phy_ant_gain_pwr_offset(struct rtw89_dev *rtwdev,  				  const struct rtw89_chan *chan); -void rtw89_print_ant_gain(struct seq_file *m, struct rtw89_dev *rtwdev, -			  const struct rtw89_chan *chan); +int rtw89_print_ant_gain(struct rtw89_dev *rtwdev, char *buf, size_t bufsz, +			 const struct rtw89_chan *chan);  void rtw89_phy_load_txpwr_byrate(struct rtw89_dev *rtwdev,  				 const struct rtw89_txpwr_table *tbl);  s8 rtw89_phy_read_txpwr_limit(struct rtw89_dev *rtwdev, u8 band, @@ -914,6 +916,13 @@ static inline s8 rtw89_phy_txpwr_rf_to_bb(struct rtw89_dev *rtwdev, s8 txpwr_rf)  	return txpwr_rf << (chip->txpwr_factor_bb - chip->txpwr_factor_rf);  } +static inline s8 rtw89_phy_txpwr_bb_to_rf(struct rtw89_dev *rtwdev, s8 txpwr_bb) +{ +	const struct rtw89_chip_info *chip = rtwdev->chip; + +	return txpwr_bb >> (chip->txpwr_factor_bb - chip->txpwr_factor_rf); +} +  static inline s8 rtw89_phy_txpwr_rf_to_mac(struct rtw89_dev *rtwdev, s8 txpwr_rf)  {  	const struct rtw89_chip_info *chip = rtwdev->chip; @@ -928,6 +937,20 @@ static inline s8 rtw89_phy_txpwr_dbm_to_mac(struct rtw89_dev *rtwdev, s8 dbm)  	return clamp_t(s16, dbm << chip->txpwr_factor_mac, -64, 63);  } +static inline s16 rtw89_phy_txpwr_mac_to_rf(struct rtw89_dev *rtwdev, s8 txpwr_mac) +{ +	const struct rtw89_chip_info *chip = rtwdev->chip; + +	return txpwr_mac << (chip->txpwr_factor_rf - chip->txpwr_factor_mac); +} + +static inline s16 rtw89_phy_txpwr_mac_to_bb(struct rtw89_dev *rtwdev, s8 txpwr_mac) +{ +	const struct rtw89_chip_info *chip = rtwdev->chip; + +	return txpwr_mac << (chip->txpwr_factor_bb - chip->txpwr_factor_mac); +} +  void rtw89_phy_ra_assoc(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwsta_link);  void rtw89_phy_ra_update(struct rtw89_dev *rtwdev);  void rtw89_phy_ra_update_sta(struct rtw89_dev *rtwdev, struct ieee80211_sta *sta, @@ -978,20 +1001,22 @@ void rtw89_phy_rfk_tssi_fill_fwcmd_tmeter_tbl(struct rtw89_dev *rtwdev,  					      const struct rtw89_chan *chan,  					      struct rtw89_h2c_rf_tssi *h2c);  void rtw89_phy_cfo_track(struct rtw89_dev *rtwdev); -void rtw89_phy_cfo_track_work(struct work_struct *work); +void rtw89_phy_cfo_track_work(struct wiphy *wiphy, struct wiphy_work *work);  void rtw89_phy_cfo_parse(struct rtw89_dev *rtwdev, s16 cfo_val,  			 struct rtw89_rx_phy_ppdu *phy_ppdu);  void rtw89_phy_stat_track(struct rtw89_dev *rtwdev);  void rtw89_phy_env_monitor_track(struct rtw89_dev *rtwdev);  void rtw89_phy_set_phy_regs(struct rtw89_dev *rtwdev, u32 addr, u32 mask,  			    u32 val); -void rtw89_phy_dig_reset(struct rtw89_dev *rtwdev); +void rtw89_phy_dig_reset(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb);  void rtw89_phy_dig(struct rtw89_dev *rtwdev); +void rtw89_phy_dig_suspend(struct rtw89_dev *rtwdev); +void rtw89_phy_dig_resume(struct rtw89_dev *rtwdev, bool restore);  void rtw89_phy_tx_path_div_track(struct rtw89_dev *rtwdev);  void rtw89_phy_antdiv_parse(struct rtw89_dev *rtwdev,  			    struct rtw89_rx_phy_ppdu *phy_ppdu);  void rtw89_phy_antdiv_track(struct rtw89_dev *rtwdev); -void rtw89_phy_antdiv_work(struct work_struct *work); +void rtw89_phy_antdiv_work(struct wiphy *wiphy, struct wiphy_work *work);  void rtw89_phy_set_bss_color(struct rtw89_dev *rtwdev,  			     struct rtw89_vif_link *rtwvif_link);  void rtw89_phy_tssi_ctrl_set_bandedge_cfg(struct rtw89_dev *rtwdev, @@ -1002,9 +1027,10 @@ void rtw89_phy_ul_tb_ctrl_track(struct rtw89_dev *rtwdev);  u8 rtw89_encode_chan_idx(struct rtw89_dev *rtwdev, u8 central_ch, u8 band);  void rtw89_decode_chan_idx(struct rtw89_dev *rtwdev, u8 chan_idx,  			   u8 *ch, enum nl80211_band *band); -void rtw89_phy_config_edcca(struct rtw89_dev *rtwdev, bool scan); +void rtw89_phy_config_edcca(struct rtw89_dev *rtwdev, +			    struct rtw89_bb_ctx *bb, bool scan);  void rtw89_phy_edcca_track(struct rtw89_dev *rtwdev); -void rtw89_phy_edcca_thre_calc(struct rtw89_dev *rtwdev); +void rtw89_phy_edcca_thre_calc(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb);  enum rtw89_rf_path_bit rtw89_phy_get_kpath(struct rtw89_dev *rtwdev,  					   enum rtw89_phy_idx phy_idx);  enum rtw89_rf_path rtw89_phy_get_syn_sel(struct rtw89_dev *rtwdev, diff --git a/sys/contrib/dev/rtw89/phy_be.c b/sys/contrib/dev/rtw89/phy_be.c index 37d8f254ae32..d321cf1fc485 100644 --- a/sys/contrib/dev/rtw89/phy_be.c +++ b/sys/contrib/dev/rtw89/phy_be.c @@ -362,7 +362,7 @@ static void rtw89_phy_bb_wrap_force_cr_init(struct rtw89_dev *rtwdev,  	rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_RU_ENON, 0);  	rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_RU_ON, 0);  	addr = rtw89_mac_reg_by_idx(rtwdev, R_BE_PWR_FORCE_MACID, mac_idx); -	rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_MACID_ON, 0); +	rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_MACID_ALL, 0);  	addr = rtw89_mac_reg_by_idx(rtwdev, R_BE_PWR_COEX_CTRL, mac_idx);  	rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_COEX_ON, 0);  	addr = rtw89_mac_reg_by_idx(rtwdev, R_BE_PWR_RATE_CTRL, mac_idx); diff --git a/sys/contrib/dev/rtw89/ps.c b/sys/contrib/dev/rtw89/ps.c index a47c6e7145d3..098726f91db7 100644 --- a/sys/contrib/dev/rtw89/ps.c +++ b/sys/contrib/dev/rtw89/ps.c @@ -13,6 +13,31 @@  #include "reg.h"  #include "util.h" +static int rtw89_fw_receive_lps_h2c_check(struct rtw89_dev *rtwdev, u8 macid) +{ +	struct rtw89_mac_c2h_info c2h_info = {}; +	u16 c2hreg_macid; +	u32 c2hreg_ret; +	int ret; + +	if (!RTW89_CHK_FW_FEATURE(LPS_DACK_BY_C2H_REG, &rtwdev->fw)) +		return 0; + +	c2h_info.id = RTW89_FWCMD_C2HREG_FUNC_PS_LEAVE_ACK; +	ret = rtw89_fw_msg_reg(rtwdev, NULL, &c2h_info); +	if (ret) +		return ret; + +	c2hreg_macid = u32_get_bits(c2h_info.u.c2hreg[0], +				    RTW89_C2HREG_PS_LEAVE_ACK_MACID); +	c2hreg_ret = u32_get_bits(c2h_info.u.c2hreg[1], RTW89_C2HREG_PS_LEAVE_ACK_RET); + +	if (macid != c2hreg_macid || c2hreg_ret) +		rtw89_warn(rtwdev, "rtw89: check lps h2c received by firmware fail\n"); + +	return 0; +} +  static int rtw89_fw_leave_lps_check(struct rtw89_dev *rtwdev, u8 macid)  {  	const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; @@ -106,14 +131,15 @@ static void __rtw89_leave_lps(struct rtw89_dev *rtwdev,  	};  	rtw89_fw_h2c_lps_parm(rtwdev, &lps_param); -	rtw89_fw_leave_lps_check(rtwdev, 0); +	rtw89_fw_receive_lps_h2c_check(rtwdev, rtwvif_link->mac_id); +	rtw89_fw_leave_lps_check(rtwdev, rtwvif_link->mac_id);  	rtw89_btc_ntfy_radio_state(rtwdev, BTC_RFCTRL_WL_ON);  	rtw89_chip_digital_pwr_comp(rtwdev, rtwvif_link->phy_idx);  }  void rtw89_leave_ps_mode(struct rtw89_dev *rtwdev)  { -	lockdep_assert_held(&rtwdev->mutex); +	lockdep_assert_wiphy(rtwdev->hw->wiphy);  	__rtw89_leave_ps_mode(rtwdev);  } @@ -125,7 +151,7 @@ void rtw89_enter_lps(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif,  	bool can_ps_mode = true;  	unsigned int link_id; -	lockdep_assert_held(&rtwdev->mutex); +	lockdep_assert_wiphy(rtwdev->hw->wiphy);  	if (test_and_set_bit(RTW89_FLAG_LEISURE_PS, rtwdev->flags))  		return; @@ -137,6 +163,8 @@ void rtw89_enter_lps(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif,  			can_ps_mode = false;  	} +	rtw89_fw_h2c_rf_ps_info(rtwdev, rtwvif); +  	if (RTW89_CHK_FW_FEATURE(LPS_CH_INFO, &rtwdev->fw))  		rtw89_fw_h2c_lps_ch_info(rtwdev, rtwvif);  	else @@ -162,7 +190,7 @@ void rtw89_leave_lps(struct rtw89_dev *rtwdev)  	struct rtw89_vif *rtwvif;  	unsigned int link_id; -	lockdep_assert_held(&rtwdev->mutex); +	lockdep_assert_wiphy(rtwdev->hw->wiphy);  	if (!test_and_clear_bit(RTW89_FLAG_LEISURE_PS, rtwdev->flags))  		return; @@ -236,13 +264,23 @@ static void rtw89_tsf32_toggle(struct rtw89_dev *rtwdev,  		rtw89_fw_h2c_tsf32_toggle(rtwdev, rtwvif_link, false);  } -static void rtw89_p2p_disable_all_noa(struct rtw89_dev *rtwdev, -				      struct rtw89_vif_link *rtwvif_link, -				      struct ieee80211_bss_conf *bss_conf) +void rtw89_p2p_disable_all_noa(struct rtw89_dev *rtwdev, +			       struct rtw89_vif_link *rtwvif_link, +			       struct ieee80211_bss_conf *bss_conf)  {  	enum rtw89_p2pps_action act; +	u8 oppps_ctwindow;  	u8 noa_id; +	rcu_read_lock(); + +	if (!bss_conf) +		bss_conf = rtw89_vif_rcu_dereference_link(rtwvif_link, true); + +	oppps_ctwindow = bss_conf->p2p_noa_attr.oppps_ctwindow; + +	rcu_read_unlock(); +  	if (rtwvif_link->last_noa_nr == 0)  		return; @@ -252,8 +290,8 @@ static void rtw89_p2p_disable_all_noa(struct rtw89_dev *rtwdev,  		else  			act = RTW89_P2P_ACT_REMOVE;  		rtw89_tsf32_toggle(rtwdev, rtwvif_link, act); -		rtw89_fw_h2c_p2p_act(rtwdev, rtwvif_link, bss_conf, -				     NULL, act, noa_id); +		rtw89_fw_h2c_p2p_act(rtwdev, rtwvif_link, NULL, +				     act, noa_id, oppps_ctwindow);  	}  } @@ -275,8 +313,8 @@ static void rtw89_p2p_update_noa(struct rtw89_dev *rtwdev,  		else  			act = RTW89_P2P_ACT_UPDATE;  		rtw89_tsf32_toggle(rtwdev, rtwvif_link, act); -		rtw89_fw_h2c_p2p_act(rtwdev, rtwvif_link, bss_conf, -				     desc, act, noa_id); +		rtw89_fw_h2c_p2p_act(rtwdev, rtwvif_link, desc, act, noa_id, +				     bss_conf->p2p_noa_attr.oppps_ctwindow);  	}  	rtwvif_link->last_noa_nr = noa_id;  } @@ -391,3 +429,150 @@ u8 rtw89_p2p_noa_fetch(struct rtw89_vif_link *rtwvif_link, void **data)  	return tail - (u8 *)*data;  #endif  } + +static void rtw89_ps_noa_once_set_work(struct wiphy *wiphy, struct wiphy_work *work) +{ +	struct rtw89_ps_noa_once_handler *noa_once = +		container_of(work, struct rtw89_ps_noa_once_handler, set_work.work); + +	lockdep_assert_wiphy(wiphy); + +	noa_once->in_duration = true; +} + +static void rtw89_ps_noa_once_clr_work(struct wiphy *wiphy, struct wiphy_work *work) +{ +	struct rtw89_ps_noa_once_handler *noa_once = +		container_of(work, struct rtw89_ps_noa_once_handler, clr_work.work); +	struct rtw89_vif_link *rtwvif_link = +		container_of(noa_once, struct rtw89_vif_link, noa_once); +	struct rtw89_dev *rtwdev = rtwvif_link->rtwvif->rtwdev; + +	lockdep_assert_wiphy(wiphy); + +	rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, rtwvif_link, true); +	noa_once->in_duration = false; +} + +void rtw89_p2p_noa_once_init(struct rtw89_vif_link *rtwvif_link) +{ +	struct rtw89_ps_noa_once_handler *noa_once = &rtwvif_link->noa_once; + +	noa_once->in_duration = false; +	noa_once->tsf_begin = 0; +	noa_once->tsf_end = 0; + +	wiphy_delayed_work_init(&noa_once->set_work, rtw89_ps_noa_once_set_work); +	wiphy_delayed_work_init(&noa_once->clr_work, rtw89_ps_noa_once_clr_work); +} + +static void rtw89_p2p_noa_once_cancel(struct rtw89_vif_link *rtwvif_link) +{ +	struct rtw89_ps_noa_once_handler *noa_once = &rtwvif_link->noa_once; +	struct rtw89_dev *rtwdev = rtwvif_link->rtwvif->rtwdev; +	struct wiphy *wiphy = rtwdev->hw->wiphy; + +	wiphy_delayed_work_cancel(wiphy, &noa_once->set_work); +	wiphy_delayed_work_cancel(wiphy, &noa_once->clr_work); +} + +void rtw89_p2p_noa_once_deinit(struct rtw89_vif_link *rtwvif_link) +{ +	rtw89_p2p_noa_once_cancel(rtwvif_link); +	rtw89_p2p_noa_once_init(rtwvif_link); +} + +void rtw89_p2p_noa_once_recalc(struct rtw89_vif_link *rtwvif_link) +{ +	struct rtw89_ps_noa_once_handler *noa_once = &rtwvif_link->noa_once; +	struct rtw89_dev *rtwdev = rtwvif_link->rtwvif->rtwdev; +	const struct ieee80211_p2p_noa_desc *noa_desc; +	struct wiphy *wiphy = rtwdev->hw->wiphy; +	struct ieee80211_bss_conf *bss_conf; +	u64 tsf_begin = U64_MAX, tsf_end; +	u64 set_delay_us = 0; +	u64 clr_delay_us = 0; +	u32 start_time; +	u32 interval; +	u32 duration; +	u64 tsf; +	int ret; +	int i; + +	lockdep_assert_wiphy(wiphy); + +	ret = rtw89_mac_port_get_tsf(rtwdev, rtwvif_link, &tsf); +	if (ret) { +		rtw89_warn(rtwdev, "%s: failed to get tsf\n", __func__); +		return; +	} + +	rcu_read_lock(); + +	bss_conf = rtw89_vif_rcu_dereference_link(rtwvif_link, true); + +	for (i = 0; i < ARRAY_SIZE(bss_conf->p2p_noa_attr.desc); i++) { +		bool first = tsf_begin == U64_MAX; +		u64 tmp; + +		noa_desc = &bss_conf->p2p_noa_attr.desc[i]; +		if (noa_desc->count == 0 || noa_desc->count == 255) +			continue; + +		start_time = le32_to_cpu(noa_desc->start_time); +		interval = le32_to_cpu(noa_desc->interval); +		duration = le32_to_cpu(noa_desc->duration); + +		if (unlikely(duration == 0 || +			     (noa_desc->count > 1 && interval == 0))) +			continue; + +		tmp = start_time + interval * (noa_desc->count - 1) + duration; +		tmp = (tsf & GENMASK_ULL(63, 32)) + tmp; +		if (unlikely(tmp <= tsf)) +			continue; +		tsf_end = first ? tmp : max(tsf_end, tmp); + +		tmp = (tsf & GENMASK_ULL(63, 32)) | start_time; +		tsf_begin = first ? tmp : min(tsf_begin, tmp); +	} + +	rcu_read_unlock(); + +	if (tsf_begin == U64_MAX) +		return; + +	rtw89_p2p_noa_once_cancel(rtwvif_link); + +	if (noa_once->tsf_end > tsf) { +		tsf_begin = min(tsf_begin, noa_once->tsf_begin); +		tsf_end = max(tsf_end, noa_once->tsf_end); +	} + +	clr_delay_us = min_t(u64, tsf_end - tsf, UINT_MAX); + +	if (tsf_begin <= tsf) { +		noa_once->in_duration = true; +		goto out; +	} + +	set_delay_us = tsf_begin - tsf; +	if (unlikely(set_delay_us > UINT_MAX)) { +		rtw89_warn(rtwdev, "%s: unhandled begin\n", __func__); +		set_delay_us = 0; +		clr_delay_us = 0; +		rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, rtwvif_link, true); +		noa_once->in_duration = false; +	} + +out: +	if (set_delay_us) +		wiphy_delayed_work_queue(wiphy, &noa_once->set_work, +					 usecs_to_jiffies(set_delay_us)); +	if (clr_delay_us) +		wiphy_delayed_work_queue(wiphy, &noa_once->clr_work, +					 usecs_to_jiffies(clr_delay_us)); + +	noa_once->tsf_begin = tsf_begin; +	noa_once->tsf_end = tsf_end; +} diff --git a/sys/contrib/dev/rtw89/ps.h b/sys/contrib/dev/rtw89/ps.h index 2b88f254a32d..729477153de6 100644 --- a/sys/contrib/dev/rtw89/ps.h +++ b/sys/contrib/dev/rtw89/ps.h @@ -22,6 +22,12 @@ void rtw89_p2p_noa_renew(struct rtw89_vif_link *rtwvif_link);  void rtw89_p2p_noa_append(struct rtw89_vif_link *rtwvif_link,  			  const struct ieee80211_p2p_noa_desc *desc);  u8 rtw89_p2p_noa_fetch(struct rtw89_vif_link *rtwvif_link, void **data); +void rtw89_p2p_noa_once_init(struct rtw89_vif_link *rtwvif_link); +void rtw89_p2p_noa_once_deinit(struct rtw89_vif_link *rtwvif_link); +void rtw89_p2p_noa_once_recalc(struct rtw89_vif_link *rtwvif_link); +void rtw89_p2p_disable_all_noa(struct rtw89_dev *rtwdev, +			       struct rtw89_vif_link *rtwvif_link, +			       struct ieee80211_bss_conf *bss_conf);  static inline void rtw89_leave_ips_by_hwflags(struct rtw89_dev *rtwdev)  { diff --git a/sys/contrib/dev/rtw89/reg.h b/sys/contrib/dev/rtw89/reg.h index 10d0efa7a58e..de81103a072f 100644 --- a/sys/contrib/dev/rtw89/reg.h +++ b/sys/contrib/dev/rtw89/reg.h @@ -21,6 +21,7 @@  #define R_AX_SYS_PW_CTRL 0x0004  #define B_AX_SOP_ASWRM BIT(31)  #define B_AX_SOP_PWMM_DSWR BIT(29) +#define B_AX_SOP_EDSWR BIT(28)  #define B_AX_XTAL_OFF_A_DIE BIT(22)  #define B_AX_DIS_WLBT_PDNSUSEN_SOPC BIT(18)  #define B_AX_RDY_SYSPWR BIT(17) @@ -182,6 +183,7 @@  #define R_AX_SYS_STATUS1 0x00F4  #define B_AX_SEL_0XC0_MASK GENMASK(17, 16) +#define B_AX_AUTO_WLPON BIT(10)  #define B_AX_PAD_HCI_SEL_V2_MASK GENMASK(5, 3)  #define MAC_AX_HCI_SEL_SDIO_UART 0  #define MAC_AX_HCI_SEL_MULTI_USB 1 @@ -380,6 +382,18 @@  #define B_AX_ACH1_BUSY BIT(9)  #define B_AX_ACH0_BUSY BIT(8) +#define R_AX_USB_ENDPOINT_0 0x1060 +#define B_AX_EP_IDX GENMASK(3, 0) +#define R_AX_USB_ENDPOINT_2 0x1068 +#define NUMP 0x1 +#define R_AX_USB_HOST_REQUEST_2 0x1078 +#define B_AX_R_USBIO_MODE BIT(4) +#define R_AX_USB3_MAC_NPI_CONFIG_INTF_0 0x1114 +#define B_AX_SSPHY_LFPS_FILTER BIT(31) +#define R_AX_USB_WLAN0_1 0x1174 +#define B_AX_USBRX_RST BIT(9) +#define B_AX_USBTX_RST BIT(8) +  #define R_AX_PCIE_DBG_CTRL 0x11C0  #define B_AX_DBG_DUMMY_MASK GENMASK(23, 16)  #define B_AX_PCIE_DBG_SEL_MASK GENMASK(15, 13) @@ -459,6 +473,17 @@  #define R_AX_WP_PAGE_CTRL2_V1 0x17A4  #define R_AX_WP_PAGE_INFO1_V1 0x17A8 +#define R_AX_USB_ENDPOINT_0_V1 0x5060 +#define B_AX_EP_IDX_V1 GENMASK(3, 0) +#define R_AX_USB_ENDPOINT_2_V1 0x5068 +#define R_AX_USB_HOST_REQUEST_2_V1 0x5078 +#define B_AX_R_USBIO_MODE_V1 BIT(4) +#define R_AX_USB3_MAC_NPI_CONFIG_INTF_0_V1 0x5114 +#define B_AX_SSPHY_LFPS_FILTER_V1 BIT(31) +#define R_AX_USB_WLAN0_1_V1 0x5174 +#define B_AX_USBRX_RST_V1 BIT(9) +#define B_AX_USBTX_RST_V1 BIT(8) +  #define R_AX_H2CREG_DATA0_V1 0x7140  #define R_AX_H2CREG_DATA1_V1 0x7144  #define R_AX_H2CREG_DATA2_V1 0x7148 @@ -1025,6 +1050,12 @@  #define B_AX_DISPATCHER_INTN_SEL_MASK GENMASK(7, 4)  #define B_AX_DISPATCHER_CH_SEL_MASK GENMASK(3, 0) +#define R_AX_RXDMA_SETTING 0x8908 +#define B_AX_BULK_SIZE GENMASK(1, 0) +#define USB11_BULKSIZE 0x2 +#define USB2_BULKSIZE 0x1 +#define USB3_BULKSIZE 0x0 +  #define R_AX_RX_FUNCTION_STOP 0x8920  #define B_AX_HDR_RX_STOP BIT(0) @@ -6070,6 +6101,7 @@  #define B_BE_MACID_ACQ_GRP0_CLR_P BIT(2)  #define B_BE_R_MACID_ACQ_CHK_EN BIT(0) +#define R_BE_BTC_CFG 0x0E300  #define R_BE_BT_BREAK_TABLE 0x0E344  #define R_BE_GNT_SW_CTRL 0x0E348 @@ -6618,6 +6650,13 @@  #define B_BE_RTS_LIMIT_IN_OFDM6 BIT(1)  #define B_BE_CHECK_CCK_EN BIT(0) +#define R_BE_TXCNT 0x1082C +#define R_BE_TXCNT_C1 0x1482C +#define B_BE_ADD_TXCNT_BY BIT(31) +#define B_BE_TOTAL_TC_OPT BIT(30) +#define B_BE_S_TXCNT_LMT_MASK GENMASK(29, 24) +#define B_BE_L_TXCNT_LMT_MASK GENMASK(21, 16) +  #define R_BE_MBSSID_DROP_0 0x1083C  #define R_BE_MBSSID_DROP_0_C1 0x1483C  #define B_BE_GI_LTF_FB_SEL BIT(30) @@ -7095,6 +7134,10 @@  #define B_BE_MACLBK_RDY_NUM_MASK GENMASK(7, 3)  #define B_BE_MACLBK_EN BIT(0) +#define R_BE_CLIENT_OM_CTRL 0x11040 +#define R_BE_CLIENT_OM_CTRL_C1 0x15040 +#define B_BE_TRIG_DIS_EHTTB BIT(24) +  #define R_BE_WMAC_NAV_CTL 0x11080  #define R_BE_WMAC_NAV_CTL_C1 0x15080  #define B_BE_WMAC_NAV_UPPER_EN BIT(26) @@ -7590,7 +7633,15 @@  #define B_BE_PWR_FORCE_RU_ON BIT(18)  #define B_BE_PWR_FORCE_RU_ENON BIT(28)  #define R_BE_PWR_FORCE_MACID 0x11A48 -#define B_BE_PWR_FORCE_MACID_ON BIT(9) +#define B_BE_PWR_FORCE_MACID_DBM_ON BIT(9) +#define B_BE_PWR_FORCE_MACID_DBM_VAL GENMASK(17, 10) +#define B_BE_PWR_FORCE_MACID_EN_VAL BIT(18) +#define B_BE_PWR_FORCE_MACID_EN_ON BIT(19) +#define B_BE_PWR_FORCE_MACID_ALL \ +	(B_BE_PWR_FORCE_MACID_DBM_ON | \ +	 B_BE_PWR_FORCE_MACID_DBM_VAL | \ +	 B_BE_PWR_FORCE_MACID_EN_VAL | \ +	 B_BE_PWR_FORCE_MACID_EN_ON)  #define R_BE_PWR_REG_CTRL 0x11A50  #define B_BE_PWR_BT_EN BIT(23) @@ -8005,6 +8056,7 @@  #define R_PHY_STS_BITMAP_HT 0x076C  #define R_PHY_STS_BITMAP_VHT 0x0770  #define R_PHY_STS_BITMAP_HE 0x0774 +#define R_PHY_STS_BITMAP_EHT 0x0788  #define R_EDCCA_RPTREG_SEL_BE 0x078C  #define B_EDCCA_RPTREG_SEL_BE_MSK GENMASK(22, 20)  #define R_PMAC_GNT 0x0980 @@ -8157,6 +8209,8 @@  #define B_EDCCA_RPT_B_S40 BIT(4)  #define B_EDCCA_RPT_B_S80 BIT(3)  #define B_EDCCA_RPT_B_PATH_MASK GENMASK(2, 1) +#define R_EDCCA_RPT_P1_A 0x1740 +#define R_EDCCA_RPT_P1_B 0x1744  #define R_SWSI_V1 0x174C  #define B_SWSI_W_BUSY_V1 BIT(24)  #define B_SWSI_R_BUSY_V1 BIT(25) @@ -8222,6 +8276,7 @@  #define B_TXCKEN_FORCE_ALL GENMASK(24, 0)  #define R_EDCCA_RPT_SEL 0x20CC  #define B_EDCCA_RPT_SEL_MSK GENMASK(2, 0) +#define B_EDCCA_RPT_SEL_P1_MSK GENMASK(5, 3)  #define R_ADC_FIFO 0x20fc  #define B_ADC_FIFO_RST GENMASK(31, 24)  #define B_ADC_FIFO_RXK GENMASK(31, 16) @@ -8291,6 +8346,8 @@  #define B_P1_EN_SOUND_WO_NDP BIT(1)  #define R_EDCCA_RPT_A_BE 0x2E38  #define R_EDCCA_RPT_B_BE 0x2E3C +#define R_EDCCA_RPT_P1_A_BE 0x2E40 +#define R_EDCCA_RPT_P1_B_BE 0x2E44  #define R_S1_HW_SI_DIS 0x3200  #define B_S1_HW_SI_DIS_W_R_TRIG GENMASK(30, 28)  #define R_P1_RXCK 0x32A0 @@ -8721,8 +8778,10 @@  #define B_DPD_GDIS BIT(13)  #define B_IQK_RFC_ON BIT(1)  #define R_TXPWRB 0x56CC +#define R_P1_TXPWRB 0x76CC  #define B_TXPWRB_ON BIT(28)  #define B_TXPWRB_VAL GENMASK(27, 19) +#define B_TXPWRB_MAX GENMASK(8, 0)  #define R_DPD_OFT_EN 0x5800  #define B_DPD_OFT_EN BIT(28)  #define B_DPD_TSSI_CW GENMASK(26, 18) @@ -8747,6 +8806,8 @@  #define B_P0_TSSI_RFC GENMASK(28, 27)  #define B_P0_TSSI_OFT_EN BIT(28)  #define B_P0_TSSI_OFT GENMASK(7, 0) +#define R_P0_TSSI_SLOPE_CAL 0x581c +#define B_P0_TSSI_SLOPE_CAL_EN BIT(20)  #define R_P0_TSSI_AVG 0x5820  #define B_P0_TSSI_EN BIT(31)  #define B_P0_TSSI_AVG GENMASK(15, 12) @@ -9169,6 +9230,16 @@  #define B_IQKINF2_FCNT GENMASK(23, 16)  #define B_IQKINF2_KCNT GENMASK(15, 8)  #define B_IQKINF2_NCTLV GENMASK(7, 0) +#define R_TXAGC_REF_DBM_RF1_P0 0xBC04 +#define B_TXAGC_OFDM_REF_DBM_RF1_P0 GENMASK(10, 2) +#define B_TXAGC_CCK_REF_DBM_RF1_P0 GENMASK(19, 11) +#define R_TSSI_K_RF1_P0 0xBC28 +#define B_TSSI_K_OFDM_RF1_P0 GENMASK(9, 0) +#define R_TXAGC_REF_DBM_RF1_P1 0xBD04 +#define B_TXAGC_OFDM_REF_DBM_RF1_P1 GENMASK(10, 2) +#define B_TXAGC_CCK_REF_DBM_RF1_P1 GENMASK(19, 11) +#define R_TSSI_K_RF1_P1 0xBD28 +#define B_TSSI_K_OFDM_RF1_P1 GENMASK(9, 0)  #define R_RFK_ST 0xBFF8  #define R_DCOF0 0xC000  #define B_DCOF0_RST BIT(17) @@ -9230,6 +9301,7 @@  #define B_WDADC_SEL GENMASK(5, 4)  #define R_ADCMOD 0xC0E8  #define B_ADCMOD_LP GENMASK(31, 16) +#define B_ADCMOD_AUTO_RST BIT(6)  #define R_DCIM 0xC0EC  #define B_DCIM_RC GENMASK(23, 16)  #define B_DCIM_FR GENMASK(14, 13) @@ -9334,20 +9406,25 @@  #define R_TSSI_PWR_P0 0xE610  #define R_TSSI_PWR_P1 0xE710  #define B_TSSI_CONT_EN BIT(3) +#define R_P0_TXPWRB_BE 0xE61C +#define R_P1_TXPWRB_BE 0xE71C +#define B_TXPWRB_MAX_BE GENMASK(20, 12)  #define R_TSSI_MAP_OFST_P0 0xE620  #define R_TSSI_MAP_OFST_P1 0xE720  #define B_TSSI_MAP_OFST_OFDM GENMASK(17, 9)  #define B_TSSI_MAP_OFST_CCK GENMASK(26, 18) -#define R_TXAGC_REF0_P0 0xE628 -#define R_TXAGC_REF0_P1 0xE728 -#define B_TXAGC_REF0_OFDM_DBM GENMASK(8, 0) -#define B_TXAGC_REF0_CCK_DBM GENMASK(17, 9) -#define B_TXAGC_REF0_OFDM_CW GENMASK(26, 18) -#define R_TXAGC_REF1_P0 0xE62C -#define R_TXAGC_REF1_P1 0xE72C -#define B_TXAGC_REF1_CCK_CW GENMASK(8, 0) +#define R_TXAGC_REF_DBM_P0 0xE628 +#define B_TXAGC_OFDM_REF_DBM_P0 GENMASK(8, 0) +#define B_TXAGC_CCK_REF_DBM_P0 GENMASK(17, 9) +#define R_TSSI_K_P0 0xE6A0 +#define B_TSSI_K_OFDM_P0 GENMASK(29, 20)  #define R_TXPWR_RSTB 0xE70C  #define B_TXPWR_RSTB BIT(16) +#define R_TXAGC_REF_DBM_P1 0xE728 +#define B_TXAGC_OFDM_REF_DBM_P1 GENMASK(8, 0) +#define B_TXAGC_CCK_REF_DBM_P1 GENMASK(17, 9) +#define R_TSSI_K_P1 0xE7A0 +#define B_TSSI_K_OFDM_P1 GENMASK(29, 20)  /* WiFi CPU local domain */  #define R_AX_WDT_CTRL 0x0040 diff --git a/sys/contrib/dev/rtw89/regd.c b/sys/contrib/dev/rtw89/regd.c index 4b71d5a68c45..e0a79612ebfd 100644 --- a/sys/contrib/dev/rtw89/regd.c +++ b/sys/contrib/dev/rtw89/regd.c @@ -7,257 +7,266 @@  #include "ps.h"  #include "util.h" -#define COUNTRY_REGD(_alpha2, _txpwr_regd...) \ -	{.alpha2 = (_alpha2), \ -	 .txpwr_regd = {_txpwr_regd}, \ +static +void rtw89_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request); + +#define COUNTRY_REGD(_alpha2, _rule_2ghz, _rule_5ghz, _rule_6ghz, _fmap) \ +	{							\ +		.alpha2 = _alpha2,				\ +		.txpwr_regd[RTW89_BAND_2G] = _rule_2ghz,	\ +		.txpwr_regd[RTW89_BAND_5G] = _rule_5ghz,	\ +		.txpwr_regd[RTW89_BAND_6G] = _rule_6ghz,	\ +		.func_bitmap = { _fmap, },	\  	} +static_assert(BITS_PER_TYPE(unsigned long) >= NUM_OF_RTW89_REGD_FUNC); +  static const struct rtw89_regd rtw89_ww_regd = -	COUNTRY_REGD("00", RTW89_WW, RTW89_WW, RTW89_WW); +	COUNTRY_REGD("00", RTW89_WW, RTW89_WW, RTW89_WW, 0x0);  static const struct rtw89_regd rtw89_regd_map[] = { -	COUNTRY_REGD("AR", RTW89_MEXICO, RTW89_MEXICO, RTW89_FCC), -	COUNTRY_REGD("BO", RTW89_FCC, RTW89_FCC, RTW89_NA), -	COUNTRY_REGD("BR", RTW89_FCC, RTW89_FCC, RTW89_FCC), -	COUNTRY_REGD("CL", RTW89_CHILE, RTW89_CHILE, RTW89_CHILE), -	COUNTRY_REGD("CO", RTW89_FCC, RTW89_FCC, RTW89_FCC), -	COUNTRY_REGD("CR", RTW89_FCC, RTW89_FCC, RTW89_FCC), -	COUNTRY_REGD("EC", RTW89_FCC, RTW89_FCC, RTW89_NA), -	COUNTRY_REGD("SV", RTW89_FCC, RTW89_FCC, RTW89_FCC), -	COUNTRY_REGD("GT", RTW89_FCC, RTW89_FCC, RTW89_FCC), -	COUNTRY_REGD("HN", RTW89_FCC, RTW89_FCC, RTW89_FCC), -	COUNTRY_REGD("MX", RTW89_MEXICO, RTW89_MEXICO, RTW89_FCC), -	COUNTRY_REGD("NI", RTW89_FCC, RTW89_FCC, RTW89_NA), -	COUNTRY_REGD("PA", RTW89_FCC, RTW89_FCC, RTW89_NA), -	COUNTRY_REGD("PY", RTW89_FCC, RTW89_FCC, RTW89_NA), -	COUNTRY_REGD("PE", RTW89_FCC, RTW89_FCC, RTW89_FCC), -	COUNTRY_REGD("US", RTW89_FCC, RTW89_FCC, RTW89_FCC), -	COUNTRY_REGD("UY", RTW89_FCC, RTW89_FCC, RTW89_NA), -	COUNTRY_REGD("VE", RTW89_FCC, RTW89_FCC, RTW89_NA), -	COUNTRY_REGD("PR", RTW89_FCC, RTW89_FCC, RTW89_NA), -	COUNTRY_REGD("DO", RTW89_FCC, RTW89_FCC, RTW89_FCC), -	COUNTRY_REGD("AT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("BE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("CY", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("CZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("DK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("EE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("FI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("FR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("DE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("GR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("HU", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("IS", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("IE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("IT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("LV", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("LI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("LT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("LU", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("MT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("MC", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("NL", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("NO", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("PL", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("PT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("SK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("SI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("ES", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("SE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("CH", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("GB", RTW89_UK, RTW89_UK, RTW89_UK), -	COUNTRY_REGD("AL", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("AZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("BH", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("BA", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("BG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("HR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("EG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("GH", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("IQ", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("IL", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("JO", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("KZ", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("KE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("KW", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("KG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("LB", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("LS", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("MK", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("MA", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("MZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("NA", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("NG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("OM", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("QA", RTW89_QATAR, RTW89_QATAR, RTW89_QATAR), -	COUNTRY_REGD("RO", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("RU", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("SA", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("SN", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("RS", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("ME", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("ZA", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("TR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("UA", RTW89_UKRAINE, RTW89_UKRAINE, RTW89_UKRAINE), -	COUNTRY_REGD("AE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("YE", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("ZW", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("BD", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("KH", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("CN", RTW89_CN, RTW89_CN, RTW89_CN), -	COUNTRY_REGD("HK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("IN", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("ID", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("KR", RTW89_KCC, RTW89_KCC, RTW89_KCC), -	COUNTRY_REGD("MY", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("PK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("PH", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("SG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("LK", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("TW", RTW89_FCC, RTW89_FCC, RTW89_ETSI), -	COUNTRY_REGD("TH", RTW89_THAILAND, RTW89_THAILAND, RTW89_THAILAND), -	COUNTRY_REGD("VN", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("AU", RTW89_ACMA, RTW89_ACMA, RTW89_ACMA), -	COUNTRY_REGD("NZ", RTW89_ACMA, RTW89_ACMA, RTW89_ACMA), -	COUNTRY_REGD("PG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("CA", RTW89_IC, RTW89_IC, RTW89_IC), -	COUNTRY_REGD("JP", RTW89_MKK, RTW89_MKK, RTW89_MKK), -	COUNTRY_REGD("JM", RTW89_FCC, RTW89_FCC, RTW89_FCC), -	COUNTRY_REGD("AN", RTW89_FCC, RTW89_FCC, RTW89_FCC), -	COUNTRY_REGD("TT", RTW89_FCC, RTW89_FCC, RTW89_NA), -	COUNTRY_REGD("TN", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("AF", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("DZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("AS", RTW89_FCC, RTW89_FCC, RTW89_NA), -	COUNTRY_REGD("AD", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("AO", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("AI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("AQ", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("AG", RTW89_FCC, RTW89_FCC, RTW89_FCC), -	COUNTRY_REGD("AM", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("AW", RTW89_FCC, RTW89_FCC, RTW89_FCC), -	COUNTRY_REGD("BS", RTW89_FCC, RTW89_FCC, RTW89_FCC), -	COUNTRY_REGD("BB", RTW89_FCC, RTW89_FCC, RTW89_FCC), -	COUNTRY_REGD("BY", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("BZ", RTW89_FCC, RTW89_FCC, RTW89_NA), -	COUNTRY_REGD("BJ", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("BM", RTW89_FCC, RTW89_FCC, RTW89_FCC), -	COUNTRY_REGD("BT", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("BW", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("BV", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("IO", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("VG", RTW89_FCC, RTW89_FCC, RTW89_FCC), -	COUNTRY_REGD("BN", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("BF", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("MM", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("BI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("CM", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("CV", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("KY", RTW89_FCC, RTW89_FCC, RTW89_FCC), -	COUNTRY_REGD("CF", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("TD", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("CX", RTW89_ACMA, RTW89_ACMA, RTW89_NA), -	COUNTRY_REGD("CC", RTW89_ACMA, RTW89_ACMA, RTW89_NA), -	COUNTRY_REGD("KM", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("CG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("CD", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("CK", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("CI", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("DJ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("DM", RTW89_FCC, RTW89_FCC, RTW89_NA), -	COUNTRY_REGD("GQ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("ER", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("ET", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("FK", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("FO", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("FJ", RTW89_FCC, RTW89_FCC, RTW89_NA), -	COUNTRY_REGD("GF", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("PF", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("TF", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("GA", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("GM", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("GE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("GI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("GL", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("GD", RTW89_FCC, RTW89_FCC, RTW89_FCC), -	COUNTRY_REGD("GP", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("GU", RTW89_FCC, RTW89_FCC, RTW89_FCC), -	COUNTRY_REGD("GG", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("GN", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("GW", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("GY", RTW89_FCC, RTW89_FCC, RTW89_NA), -	COUNTRY_REGD("HT", RTW89_FCC, RTW89_FCC, RTW89_FCC), -	COUNTRY_REGD("HM", RTW89_ACMA, RTW89_ACMA, RTW89_NA), -	COUNTRY_REGD("VA", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("IM", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("JE", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("KI", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("XK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("LA", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("LR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("LY", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("MO", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("MG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("MW", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("MV", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("ML", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("MH", RTW89_FCC, RTW89_FCC, RTW89_FCC), -	COUNTRY_REGD("MQ", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("MR", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("MU", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("YT", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("FM", RTW89_FCC, RTW89_FCC, RTW89_FCC), -	COUNTRY_REGD("MD", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("MN", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("MS", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("NR", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("NP", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("NC", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("NE", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("NU", RTW89_ACMA, RTW89_ACMA, RTW89_NA), -	COUNTRY_REGD("NF", RTW89_ACMA, RTW89_ACMA, RTW89_NA), -	COUNTRY_REGD("MP", RTW89_FCC, RTW89_FCC, RTW89_FCC), -	COUNTRY_REGD("PW", RTW89_FCC, RTW89_FCC, RTW89_FCC), -	COUNTRY_REGD("RE", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("RW", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("SH", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("KN", RTW89_FCC, RTW89_FCC, RTW89_FCC), -	COUNTRY_REGD("LC", RTW89_FCC, RTW89_FCC, RTW89_FCC), -	COUNTRY_REGD("MF", RTW89_FCC, RTW89_FCC, RTW89_FCC), -	COUNTRY_REGD("SX", RTW89_FCC, RTW89_FCC, RTW89_FCC), -	COUNTRY_REGD("PM", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("VC", RTW89_FCC, RTW89_FCC, RTW89_NA), -	COUNTRY_REGD("WS", RTW89_FCC, RTW89_FCC, RTW89_NA), -	COUNTRY_REGD("SM", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("ST", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("SC", RTW89_FCC, RTW89_FCC, RTW89_NA), -	COUNTRY_REGD("SL", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("SB", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("SO", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("GS", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("SR", RTW89_FCC, RTW89_FCC, RTW89_FCC), -	COUNTRY_REGD("SJ", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("SZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("TJ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("TZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("TG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("TK", RTW89_ACMA, RTW89_ACMA, RTW89_NA), -	COUNTRY_REGD("TO", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("TM", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("TC", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("TV", RTW89_ETSI, RTW89_NA, RTW89_NA), -	COUNTRY_REGD("UG", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("VI", RTW89_FCC, RTW89_FCC, RTW89_FCC), -	COUNTRY_REGD("UZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), -	COUNTRY_REGD("VU", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("WF", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("EH", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("ZM", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("CU", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("IR", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("SY", RTW89_ETSI, RTW89_NA, RTW89_NA), -	COUNTRY_REGD("SD", RTW89_ETSI, RTW89_ETSI, RTW89_NA), -	COUNTRY_REGD("PS", RTW89_ETSI, RTW89_ETSI, RTW89_NA), +	COUNTRY_REGD("AR", RTW89_MEXICO, RTW89_MEXICO, RTW89_FCC, 0x0), +	COUNTRY_REGD("BO", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), +	COUNTRY_REGD("BR", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), +	COUNTRY_REGD("CL", RTW89_CHILE, RTW89_CHILE, RTW89_CHILE, 0x0), +	COUNTRY_REGD("CO", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), +	COUNTRY_REGD("CR", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), +	COUNTRY_REGD("EC", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), +	COUNTRY_REGD("SV", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), +	COUNTRY_REGD("GT", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), +	COUNTRY_REGD("HN", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), +	COUNTRY_REGD("MX", RTW89_MEXICO, RTW89_MEXICO, RTW89_FCC, 0x0), +	COUNTRY_REGD("NI", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), +	COUNTRY_REGD("PA", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), +	COUNTRY_REGD("PY", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), +	COUNTRY_REGD("PE", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), +	COUNTRY_REGD("US", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x1), +	COUNTRY_REGD("UY", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), +	COUNTRY_REGD("VE", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), +	COUNTRY_REGD("PR", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), +	COUNTRY_REGD("DO", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), +	COUNTRY_REGD("AT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), +	COUNTRY_REGD("BE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), +	COUNTRY_REGD("CY", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), +	COUNTRY_REGD("CZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), +	COUNTRY_REGD("DK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), +	COUNTRY_REGD("EE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), +	COUNTRY_REGD("FI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), +	COUNTRY_REGD("FR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), +	COUNTRY_REGD("DE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), +	COUNTRY_REGD("GR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), +	COUNTRY_REGD("HU", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), +	COUNTRY_REGD("IS", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), +	COUNTRY_REGD("IE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), +	COUNTRY_REGD("IT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), +	COUNTRY_REGD("LV", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), +	COUNTRY_REGD("LI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), +	COUNTRY_REGD("LT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), +	COUNTRY_REGD("LU", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), +	COUNTRY_REGD("MT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), +	COUNTRY_REGD("MC", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), +	COUNTRY_REGD("NL", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), +	COUNTRY_REGD("NO", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), +	COUNTRY_REGD("PL", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), +	COUNTRY_REGD("PT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), +	COUNTRY_REGD("SK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), +	COUNTRY_REGD("SI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), +	COUNTRY_REGD("ES", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), +	COUNTRY_REGD("SE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), +	COUNTRY_REGD("CH", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), +	COUNTRY_REGD("GB", RTW89_UK, RTW89_UK, RTW89_UK, 0x0), +	COUNTRY_REGD("AL", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("AZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("BH", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("BA", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("BG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), +	COUNTRY_REGD("HR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), +	COUNTRY_REGD("EG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("GH", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("IQ", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("IL", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("JO", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("KZ", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("KE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("KW", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("KG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("LB", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("LS", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("MK", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("MA", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("MZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("NA", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("NG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("OM", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("QA", RTW89_QATAR, RTW89_QATAR, RTW89_QATAR, 0x0), +	COUNTRY_REGD("RO", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), +	COUNTRY_REGD("RU", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("SA", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("SN", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("RS", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("ME", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("ZA", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("TR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("UA", RTW89_UKRAINE, RTW89_UKRAINE, RTW89_UKRAINE, 0x0), +	COUNTRY_REGD("AE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("YE", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("ZW", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("BD", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("KH", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("CN", RTW89_CN, RTW89_CN, RTW89_CN, 0x0), +	COUNTRY_REGD("HK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("IN", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("ID", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("KR", RTW89_KCC, RTW89_KCC, RTW89_KCC, 0x1), +	COUNTRY_REGD("MY", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("PK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("PH", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("SG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("LK", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("TW", RTW89_FCC, RTW89_FCC, RTW89_ETSI, 0x0), +	COUNTRY_REGD("TH", RTW89_THAILAND, RTW89_THAILAND, RTW89_THAILAND, 0x0), +	COUNTRY_REGD("VN", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("AU", RTW89_ACMA, RTW89_ACMA, RTW89_ACMA, 0x0), +	COUNTRY_REGD("NZ", RTW89_ACMA, RTW89_ACMA, RTW89_ACMA, 0x0), +	COUNTRY_REGD("PG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("CA", RTW89_IC, RTW89_IC, RTW89_IC, 0x1), +	COUNTRY_REGD("JP", RTW89_MKK, RTW89_MKK, RTW89_MKK, 0x0), +	COUNTRY_REGD("JM", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), +	COUNTRY_REGD("AN", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), +	COUNTRY_REGD("TT", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), +	COUNTRY_REGD("TN", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("AF", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("DZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("AS", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), +	COUNTRY_REGD("AD", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("AO", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("AI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("AQ", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("AG", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), +	COUNTRY_REGD("AM", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("AW", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), +	COUNTRY_REGD("BS", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), +	COUNTRY_REGD("BB", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), +	COUNTRY_REGD("BY", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("BZ", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), +	COUNTRY_REGD("BJ", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("BM", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), +	COUNTRY_REGD("BT", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("BW", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("BV", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("IO", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("VG", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), +	COUNTRY_REGD("BN", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("BF", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("MM", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("BI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("CM", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("CV", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("KY", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), +	COUNTRY_REGD("CF", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("TD", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("CX", RTW89_ACMA, RTW89_ACMA, RTW89_NA, 0x0), +	COUNTRY_REGD("CC", RTW89_ACMA, RTW89_ACMA, RTW89_NA, 0x0), +	COUNTRY_REGD("KM", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("CG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("CD", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("CK", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("CI", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("DJ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("DM", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), +	COUNTRY_REGD("GQ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("ER", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("ET", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("FK", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("FO", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("FJ", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), +	COUNTRY_REGD("GF", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("PF", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("TF", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("GA", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("GM", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("GE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("GI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("GL", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("GD", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), +	COUNTRY_REGD("GP", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("GU", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), +	COUNTRY_REGD("GG", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("GN", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("GW", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("GY", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), +	COUNTRY_REGD("HT", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), +	COUNTRY_REGD("HM", RTW89_ACMA, RTW89_ACMA, RTW89_NA, 0x0), +	COUNTRY_REGD("VA", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("IM", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("JE", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("KI", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("XK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("LA", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("LR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("LY", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("MO", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("MG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("MW", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("MV", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("ML", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("MH", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), +	COUNTRY_REGD("MQ", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("MR", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("MU", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("YT", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("FM", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), +	COUNTRY_REGD("MD", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("MN", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("MS", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("NR", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("NP", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("NC", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("NE", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("NU", RTW89_ACMA, RTW89_ACMA, RTW89_NA, 0x0), +	COUNTRY_REGD("NF", RTW89_ACMA, RTW89_ACMA, RTW89_NA, 0x0), +	COUNTRY_REGD("MP", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), +	COUNTRY_REGD("PW", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), +	COUNTRY_REGD("RE", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("RW", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("SH", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("KN", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), +	COUNTRY_REGD("LC", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), +	COUNTRY_REGD("MF", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), +	COUNTRY_REGD("SX", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), +	COUNTRY_REGD("PM", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("VC", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), +	COUNTRY_REGD("WS", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), +	COUNTRY_REGD("SM", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("ST", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("SC", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), +	COUNTRY_REGD("SL", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("SB", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("SO", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("GS", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("SR", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), +	COUNTRY_REGD("SJ", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("SZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("TJ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("TZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("TG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("TK", RTW89_ACMA, RTW89_ACMA, RTW89_NA, 0x0), +	COUNTRY_REGD("TO", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("TM", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("TC", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("TV", RTW89_ETSI, RTW89_NA, RTW89_NA, 0x0), +	COUNTRY_REGD("UG", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("VI", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), +	COUNTRY_REGD("UZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), +	COUNTRY_REGD("VU", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("WF", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("EH", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("ZM", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("CU", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("IR", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("SY", RTW89_ETSI, RTW89_NA, RTW89_NA, 0x0), +	COUNTRY_REGD("SD", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), +	COUNTRY_REGD("PS", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0),  };  static const char rtw89_alpha2_list_eu[][3] = { @@ -295,13 +304,16 @@ static const char rtw89_alpha2_list_eu[][3] = {  	"RO",  }; -static const struct rtw89_regd *rtw89_regd_find_reg_by_name(const char *alpha2) +static const struct rtw89_regd *rtw89_regd_find_reg_by_name(struct rtw89_dev *rtwdev, +							    const char *alpha2)  { +	struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; +	const struct rtw89_regd_ctrl *regd_ctrl = ®ulatory->ctrl;  	u32 i; -	for (i = 0; i < ARRAY_SIZE(rtw89_regd_map); i++) { -		if (!memcmp(rtw89_regd_map[i].alpha2, alpha2, 2)) -			return &rtw89_regd_map[i]; +	for (i = 0; i < regd_ctrl->nr; i++) { +		if (!memcmp(regd_ctrl->map[i].alpha2, alpha2, 2)) +			return ®d_ctrl->map[i];  	}  	return &rtw89_ww_regd; @@ -312,22 +324,25 @@ static bool rtw89_regd_is_ww(const struct rtw89_regd *regd)  	return regd == &rtw89_ww_regd;  } -static u8 rtw89_regd_get_index(const struct rtw89_regd *regd) +static u8 rtw89_regd_get_index(struct rtw89_dev *rtwdev, const struct rtw89_regd *regd)  { +	struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; +	const struct rtw89_regd_ctrl *regd_ctrl = ®ulatory->ctrl; +  	BUILD_BUG_ON(ARRAY_SIZE(rtw89_regd_map) > RTW89_REGD_MAX_COUNTRY_NUM);  	if (rtw89_regd_is_ww(regd))  		return RTW89_REGD_MAX_COUNTRY_NUM; -	return regd - rtw89_regd_map; +	return regd - regd_ctrl->map;  } -static u8 rtw89_regd_get_index_by_name(const char *alpha2) +static u8 rtw89_regd_get_index_by_name(struct rtw89_dev *rtwdev, const char *alpha2)  {  	const struct rtw89_regd *regd; -	regd = rtw89_regd_find_reg_by_name(alpha2); -	return rtw89_regd_get_index(regd); +	regd = rtw89_regd_find_reg_by_name(rtwdev, alpha2); +	return rtw89_regd_get_index(rtwdev, regd);  }  #define rtw89_debug_regd(_dev, _regd, _desc, _argv...) \ @@ -348,11 +363,10 @@ static void rtw89_regd_setup_unii4(struct rtw89_dev *rtwdev,  	const struct rtw89_chip_info *chip = rtwdev->chip;  	struct ieee80211_supported_band *sband;  	struct rtw89_acpi_dsm_result res = {}; -	bool enable_by_fcc; -	bool enable_by_ic; +	bool enable; +	u8 index;  	int ret;  	u8 val; -	int i;  	sband = wiphy->bands[NL80211_BAND_5GHZ];  	if (!sband) @@ -369,35 +383,25 @@ static void rtw89_regd_setup_unii4(struct rtw89_dev *rtwdev,  	if (ret) {  		rtw89_debug(rtwdev, RTW89_DBG_REGD,  			    "acpi: cannot eval unii 4: %d\n", ret); -		enable_by_fcc = true; -		enable_by_ic = false; +		val = u8_encode_bits(1, RTW89_ACPI_CONF_UNII4_US);  		goto bottom;  	}  	val = res.u.value; -	enable_by_fcc = u8_get_bits(val, RTW89_ACPI_CONF_UNII4_FCC); -	enable_by_ic = u8_get_bits(val, RTW89_ACPI_CONF_UNII4_IC);  	rtw89_debug(rtwdev, RTW89_DBG_REGD,  		    "acpi: eval if allow unii-4: 0x%x\n", val);  bottom: -	for (i = 0; i < ARRAY_SIZE(rtw89_regd_map); i++) { -		const struct rtw89_regd *regd = &rtw89_regd_map[i]; - -		switch (regd->txpwr_regd[RTW89_BAND_5G]) { -		case RTW89_FCC: -			if (enable_by_fcc) -				clear_bit(i, regulatory->block_unii4); -			break; -		case RTW89_IC: -			if (enable_by_ic) -				clear_bit(i, regulatory->block_unii4); -			break; -		default: -			break; -		} -	} +	index = rtw89_regd_get_index_by_name(rtwdev, "US"); +	enable = u8_get_bits(val, RTW89_ACPI_CONF_UNII4_US); +	if (enable && index != RTW89_REGD_MAX_COUNTRY_NUM) +		clear_bit(index, regulatory->block_unii4); + +	index = rtw89_regd_get_index_by_name(rtwdev, "CA"); +	enable = u8_get_bits(val, RTW89_ACPI_CONF_UNII4_CA); +	if (enable && index != RTW89_REGD_MAX_COUNTRY_NUM) +		clear_bit(index, regulatory->block_unii4);  }  static void __rtw89_regd_setup_policy_6ghz(struct rtw89_dev *rtwdev, bool block, @@ -406,7 +410,7 @@ static void __rtw89_regd_setup_policy_6ghz(struct rtw89_dev *rtwdev, bool block,  	struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory;  	u8 index; -	index = rtw89_regd_get_index_by_name(alpha2); +	index = rtw89_regd_get_index_by_name(rtwdev, alpha2);  	if (index == RTW89_REGD_MAX_COUNTRY_NUM) {  		rtw89_debug(rtwdev, RTW89_DBG_REGD, "%s: unknown alpha2 %c%c\n",  			    __func__, alpha2[0], alpha2[1]); @@ -476,9 +480,9 @@ static void rtw89_regd_setup_policy_6ghz_sp(struct rtw89_dev *rtwdev)  	struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory;  	const struct rtw89_acpi_policy_6ghz_sp *ptr;  	struct rtw89_acpi_dsm_result res = {}; -	bool enable_by_us; +	bool enable; +	u8 index;  	int ret; -	int i;  	ret = rtw89_acpi_evaluate_dsm(rtwdev, RTW89_ACPI_DSM_FUNC_6GHZ_SP_SUP, &res);  	if (ret) { @@ -503,16 +507,66 @@ static void rtw89_regd_setup_policy_6ghz_sp(struct rtw89_dev *rtwdev)  	bitmap_fill(regulatory->block_6ghz_sp, RTW89_REGD_MAX_COUNTRY_NUM); -	enable_by_us = u8_get_bits(ptr->conf, RTW89_ACPI_CONF_6GHZ_SP_US); +	index = rtw89_regd_get_index_by_name(rtwdev, "US"); +	enable = u8_get_bits(ptr->conf, RTW89_ACPI_CONF_6GHZ_SP_US); +	if (enable && index != RTW89_REGD_MAX_COUNTRY_NUM) +		clear_bit(index, regulatory->block_6ghz_sp); + +	index = rtw89_regd_get_index_by_name(rtwdev, "CA"); +	enable = u8_get_bits(ptr->conf, RTW89_ACPI_CONF_6GHZ_SP_CA); +	if (enable && index != RTW89_REGD_MAX_COUNTRY_NUM) +		clear_bit(index, regulatory->block_6ghz_sp); + +out: +	kfree(ptr); +} + +static void rtw89_regd_setup_policy_6ghz_vlp(struct rtw89_dev *rtwdev) +{ +	struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; +	const struct rtw89_acpi_policy_6ghz_vlp *ptr = NULL; +	struct rtw89_acpi_dsm_result res = {}; +	bool enable; +	u8 index; +	int ret; +	u8 val; -	for (i = 0; i < ARRAY_SIZE(rtw89_regd_map); i++) { -		const struct rtw89_regd *tmp = &rtw89_regd_map[i]; +	/* By default, allow 6 GHz VLP on all countries except US and CA. */ +	val = ~(RTW89_ACPI_CONF_6GHZ_VLP_US | RTW89_ACPI_CONF_6GHZ_VLP_CA); -		if (enable_by_us && memcmp(tmp->alpha2, "US", 2) == 0) -			clear_bit(i, regulatory->block_6ghz_sp); +	ret = rtw89_acpi_evaluate_dsm(rtwdev, RTW89_ACPI_DSM_FUNC_6GHZ_VLP_SUP, &res); +	if (ret) { +		rtw89_debug(rtwdev, RTW89_DBG_REGD, +			    "acpi: cannot eval policy 6ghz-vlp: %d\n", ret); +		goto bottom;  	} -out: +	ptr = res.u.policy_6ghz_vlp; + +	switch (ptr->override) { +	default: +		rtw89_debug(rtwdev, RTW89_DBG_REGD, +			    "%s: unknown override case: %d\n", __func__, +			    ptr->override); +		fallthrough; +	case 0: +		break; +	case 1: +		val = ptr->conf; +		break; +	} + +bottom: +	index = rtw89_regd_get_index_by_name(rtwdev, "US"); +	enable = u8_get_bits(val, RTW89_ACPI_CONF_6GHZ_VLP_US); +	if (!enable && index != RTW89_REGD_MAX_COUNTRY_NUM) +		set_bit(index, regulatory->block_6ghz_vlp); + +	index = rtw89_regd_get_index_by_name(rtwdev, "CA"); +	enable = u8_get_bits(val, RTW89_ACPI_CONF_6GHZ_VLP_CA); +	if (!enable && index != RTW89_REGD_MAX_COUNTRY_NUM) +		set_bit(index, regulatory->block_6ghz_vlp); +  	kfree(ptr);  } @@ -559,6 +613,7 @@ bottom:  	if (regd_allow_6ghz) {  		rtw89_regd_setup_policy_6ghz(rtwdev);  		rtw89_regd_setup_policy_6ghz_sp(rtwdev); +		rtw89_regd_setup_policy_6ghz_vlp(rtwdev);  		return;  	} @@ -571,10 +626,81 @@ bottom:  	kfree(sband);  } +#define RTW89_DEF_REGD_STR(regd) \ +	[RTW89_ ## regd] = #regd + +static const char * const rtw89_regd_string[] = { +	RTW89_DEF_REGD_STR(WW), +	RTW89_DEF_REGD_STR(ETSI), +	RTW89_DEF_REGD_STR(FCC), +	RTW89_DEF_REGD_STR(MKK), +	RTW89_DEF_REGD_STR(NA), +	RTW89_DEF_REGD_STR(IC), +	RTW89_DEF_REGD_STR(KCC), +	RTW89_DEF_REGD_STR(ACMA), +	RTW89_DEF_REGD_STR(NCC), +	RTW89_DEF_REGD_STR(MEXICO), +	RTW89_DEF_REGD_STR(CHILE), +	RTW89_DEF_REGD_STR(UKRAINE), +	RTW89_DEF_REGD_STR(CN), +	RTW89_DEF_REGD_STR(QATAR), +	RTW89_DEF_REGD_STR(UK), +	RTW89_DEF_REGD_STR(THAILAND), +}; + +static_assert(ARRAY_SIZE(rtw89_regd_string) == RTW89_REGD_NUM); + +const char *rtw89_regd_get_string(enum rtw89_regulation_type regd) +{ +	if (regd < 0 || regd >= RTW89_REGD_NUM) +		return "(unknown)"; + +	return rtw89_regd_string[regd]; +} + +static void rtw89_regd_setup_reg_rules(struct rtw89_dev *rtwdev) +{ +	struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; +	const struct rtw89_acpi_policy_reg_rules *ptr; +	struct rtw89_acpi_dsm_result res = {}; +	int ret; + +	regulatory->txpwr_uk_follow_etsi = true; + +	ret = rtw89_acpi_evaluate_dsm(rtwdev, RTW89_ACPI_DSM_FUNC_REG_RULES_EN, &res); +	if (ret) { +		rtw89_debug(rtwdev, RTW89_DBG_REGD, +			    "acpi: cannot eval policy reg-rules: %d\n", ret); +		return; +	} + +	ptr = res.u.policy_reg_rules; + +	regulatory->txpwr_uk_follow_etsi = +		!u8_get_bits(ptr->conf, RTW89_ACPI_CONF_REG_RULE_REGD_UK); + +	kfree(ptr); +} +  int rtw89_regd_setup(struct rtw89_dev *rtwdev)  { +	struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; +	struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info; +	const struct rtw89_regd_data *regd_data = elm_info->regd;  	struct wiphy *wiphy = rtwdev->hw->wiphy; +	if (regd_data) { +		regulatory->ctrl.nr = regd_data->nr; +		regulatory->ctrl.map = regd_data->map; +	} else { +		regulatory->ctrl.nr = ARRAY_SIZE(rtw89_regd_map); +		regulatory->ctrl.map = rtw89_regd_map; +	} + +	regulatory->reg_6ghz_power = RTW89_REG_6GHZ_POWER_DFLT; + +	rtw89_regd_setup_reg_rules(rtwdev); +  	if (!wiphy)  		return -EINVAL; @@ -585,21 +711,16 @@ int rtw89_regd_setup(struct rtw89_dev *rtwdev)  	return 0;  } -int rtw89_regd_init(struct rtw89_dev *rtwdev, -		    void (*reg_notifier)(struct wiphy *wiphy, -					 struct regulatory_request *request)) +int rtw89_regd_init_hint(struct rtw89_dev *rtwdev)  { -	struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory;  	const struct rtw89_regd *chip_regd;  	struct wiphy *wiphy = rtwdev->hw->wiphy;  	int ret; -	regulatory->reg_6ghz_power = RTW89_REG_6GHZ_POWER_DFLT; -  	if (!wiphy)  		return -EINVAL; -	chip_regd = rtw89_regd_find_reg_by_name(rtwdev->efuse.country_code); +	chip_regd = rtw89_regd_find_reg_by_name(rtwdev, rtwdev->efuse.country_code);  #if defined(__FreeBSD__)  	rtwdev->regulatory.regd = chip_regd;  #endif @@ -642,7 +763,7 @@ static void rtw89_regd_apply_policy_unii4(struct rtw89_dev *rtwdev,  	if (!chip->support_unii4)  		return; -	index = rtw89_regd_get_index(regd); +	index = rtw89_regd_get_index(rtwdev, regd);  	if (index != RTW89_REGD_MAX_COUNTRY_NUM &&  	    !test_bit(index, regulatory->block_unii4))  		return; @@ -660,7 +781,7 @@ static bool regd_is_6ghz_blocked(struct rtw89_dev *rtwdev)  	const struct rtw89_regd *regd = regulatory->regd;  	u8 index; -	index = rtw89_regd_get_index(regd); +	index = rtw89_regd_get_index(rtwdev, regd);  	if (index != RTW89_REGD_MAX_COUNTRY_NUM &&  	    !test_bit(index, regulatory->block_6ghz))  		return false; @@ -701,11 +822,47 @@ static void rtw89_regd_apply_policy_6ghz(struct rtw89_dev *rtwdev,  		sband->channels[i].flags |= IEEE80211_CHAN_DISABLED;  } +static void rtw89_regd_apply_policy_tas(struct rtw89_dev *rtwdev) +{ +	struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; +	const struct rtw89_regd *regd = regulatory->regd; +	struct rtw89_tas_info *tas = &rtwdev->tas; +	u8 tas_country; + +	if (!tas->enable) +		return; + +	if (memcmp("US", regd->alpha2, 2) == 0) +		tas_country = RTW89_ACPI_CONF_TAS_US; +	else if (memcmp("CA", regd->alpha2, 2) == 0) +		tas_country = RTW89_ACPI_CONF_TAS_CA; +	else if (memcmp("KR", regd->alpha2, 2) == 0) +		tas_country = RTW89_ACPI_CONF_TAS_KR; +	else +		tas_country = RTW89_ACPI_CONF_TAS_OTHERS; + +	tas->block_regd = !(tas->enabled_countries & tas_country && +			    test_bit(RTW89_REGD_FUNC_TAS, regd->func_bitmap)); +} + +static void rtw89_regd_apply_policy_ant_gain(struct rtw89_dev *rtwdev) +{ +	struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; +	struct rtw89_ant_gain_info *ant_gain = &rtwdev->ant_gain; +	const struct rtw89_chip_info *chip = rtwdev->chip; +	const struct rtw89_regd *regd = regulatory->regd; + +	if (!chip->support_ant_gain) +		return; + +	ant_gain->block_country = !test_bit(RTW89_REGD_FUNC_DAG, regd->func_bitmap); +} +  static void rtw89_regd_notifier_apply(struct rtw89_dev *rtwdev,  				      struct wiphy *wiphy,  				      struct regulatory_request *request)  { -	rtwdev->regulatory.regd = rtw89_regd_find_reg_by_name(request->alpha2); +	rtwdev->regulatory.regd = rtw89_regd_find_reg_by_name(rtwdev, request->alpha2);  	/* This notification might be set from the system of distros,  	 * and it does not expect the regulatory will be modified by  	 * connecting to an AP (i.e. country ie). @@ -718,14 +875,17 @@ static void rtw89_regd_notifier_apply(struct rtw89_dev *rtwdev,  	rtw89_regd_apply_policy_unii4(rtwdev, wiphy);  	rtw89_regd_apply_policy_6ghz(rtwdev, wiphy); +	rtw89_regd_apply_policy_tas(rtwdev); +	rtw89_regd_apply_policy_ant_gain(rtwdev);  } +static  void rtw89_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request)  {  	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);  	struct rtw89_dev *rtwdev = hw->priv; -	mutex_lock(&rtwdev->mutex); +	wiphy_lock(wiphy);  	rtw89_leave_ps_mode(rtwdev);  	if (wiphy->regd) { @@ -741,7 +901,7 @@ void rtw89_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request  	rtw89_core_set_chip_txpwr(rtwdev);  exit: -	mutex_unlock(&rtwdev->mutex); +	wiphy_unlock(wiphy);  }  /* Maximum Transmit Power field (@raw) can be EIRP or PSD. @@ -930,7 +1090,7 @@ static bool __rtw89_reg_6ghz_power_recalc(struct rtw89_dev *rtwdev)  		sel = RTW89_REG_6GHZ_POWER_DFLT;  	if (sel == RTW89_REG_6GHZ_POWER_STD) { -		index = rtw89_regd_get_index(regd); +		index = rtw89_regd_get_index(rtwdev, regd);  		if (index == RTW89_REGD_MAX_COUNTRY_NUM ||  		    test_bit(index, regulatory->block_6ghz_sp)) {  			rtw89_debug(rtwdev, RTW89_DBG_REGD, @@ -954,7 +1114,16 @@ static int rtw89_reg_6ghz_power_recalc(struct rtw89_dev *rtwdev,  				       struct rtw89_vif_link *rtwvif_link, bool active,  				       unsigned int *changed)  { +	struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; +	const struct rtw89_regd *regd = regulatory->regd; +	bool blocked[NUM_OF_RTW89_REG_6GHZ_POWER] = {}; +	u8 index = rtw89_regd_get_index(rtwdev, regd);  	struct ieee80211_bss_conf *bss_conf; +	bool dflt = false; + +	if (index == RTW89_REGD_MAX_COUNTRY_NUM || +	    test_bit(index, regulatory->block_6ghz_vlp)) +		blocked[RTW89_REG_6GHZ_POWER_VLP] = true;  	rcu_read_lock(); @@ -973,6 +1142,7 @@ static int rtw89_reg_6ghz_power_recalc(struct rtw89_dev *rtwdev,  			break;  		default:  			rtwvif_link->reg_6ghz_power = RTW89_REG_6GHZ_POWER_DFLT; +			dflt = true;  			break;  		}  	} else { @@ -981,6 +1151,14 @@ static int rtw89_reg_6ghz_power_recalc(struct rtw89_dev *rtwdev,  	rcu_read_unlock(); +	if (!dflt && blocked[rtwvif_link->reg_6ghz_power]) { +		rtw89_debug(rtwdev, RTW89_DBG_REGD, +			    "%c%c 6 GHz power type-%u is blocked by policy\n", +			    regd->alpha2[0], regd->alpha2[1], +			    rtwvif_link->reg_6ghz_power); +		return -EINVAL; +	} +  	*changed += __rtw89_reg_6ghz_power_recalc(rtwdev);  	return 0;  } @@ -991,7 +1169,7 @@ int rtw89_reg_6ghz_recalc(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvi  	unsigned int changed = 0;  	int ret; -	lockdep_assert_held(&rtwdev->mutex); +	lockdep_assert_wiphy(rtwdev->hw->wiphy);  	/* The result of reg_6ghz_tpe may depend on reg_6ghz_power type,  	 * so must do reg_6ghz_tpe_recalc() after reg_6ghz_power_recalc(). diff --git a/sys/contrib/dev/rtw89/rtw8851b.c b/sys/contrib/dev/rtw89/rtw8851b.c index 6f7a068b30a5..b2d75de83602 100644 --- a/sys/contrib/dev/rtw89/rtw8851b.c +++ b/sys/contrib/dev/rtw89/rtw8851b.c @@ -51,6 +51,48 @@ static const struct rtw89_hfc_param_ini rtw8851b_hfc_param_ini_pcie[] = {  	[RTW89_QTA_INVALID] = {NULL},  }; +static const struct rtw89_hfc_ch_cfg rtw8851b_hfc_chcfg_usb[] = { +	{18, 152, grp_0}, /* ACH 0 */ +	{18, 152, grp_0}, /* ACH 1 */ +	{18, 152, grp_0}, /* ACH 2 */ +	{18, 152, grp_0}, /* ACH 3 */ +	{0, 0, grp_0}, /* ACH 4 */ +	{0, 0, grp_0}, /* ACH 5 */ +	{0, 0, grp_0}, /* ACH 6 */ +	{0, 0, grp_0}, /* ACH 7 */ +	{18, 152, grp_0}, /* B0MGQ */ +	{18, 152, grp_0}, /* B0HIQ */ +	{0, 0, grp_0}, /* B1MGQ */ +	{0, 0, grp_0}, /* B1HIQ */ +	{0, 0, 0} /* FWCMDQ */ +}; + +static const struct rtw89_hfc_pub_cfg rtw8851b_hfc_pubcfg_usb = { +	152, /* Group 0 */ +	0, /* Group 1 */ +	152, /* Public Max */ +	0 /* WP threshold */ +}; + +static const struct rtw89_hfc_prec_cfg rtw8851b_hfc_preccfg_usb = { +	9, /* CH 0-11 pre-cost */ +	32, /* H2C pre-cost */ +	64, /* WP CH 0-7 pre-cost */ +	24, /* WP CH 8-11 pre-cost */ +	1, /* CH 0-11 full condition */ +	1, /* H2C full condition */ +	1, /* WP CH 0-7 full condition */ +	1, /* WP CH 8-11 full condition */ +}; + +static const struct rtw89_hfc_param_ini rtw8851b_hfc_param_ini_usb[] = { +	[RTW89_QTA_SCC] = {rtw8851b_hfc_chcfg_usb, &rtw8851b_hfc_pubcfg_usb, +			   &rtw8851b_hfc_preccfg_usb, RTW89_HCIFC_STF}, +	[RTW89_QTA_DLFW] = {NULL, NULL, +			   &rtw8851b_hfc_preccfg_usb, RTW89_HCIFC_STF}, +	[RTW89_QTA_INVALID] = {NULL}, +}; +  static const struct rtw89_dle_mem rtw8851b_dle_mem_pcie[] = {  	[RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size6,  			   &rtw89_mac_size.ple_size6, &rtw89_mac_size.wde_qt6, @@ -68,6 +110,32 @@ static const struct rtw89_dle_mem rtw8851b_dle_mem_pcie[] = {  			       NULL},  }; +static const struct rtw89_dle_mem rtw8851b_dle_mem_usb2[] = { +	[RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size25, +			   &rtw89_mac_size.ple_size32, &rtw89_mac_size.wde_qt25, +			   &rtw89_mac_size.wde_qt25, &rtw89_mac_size.ple_qt72, +			   &rtw89_mac_size.ple_qt73}, +	[RTW89_QTA_DLFW] = {RTW89_QTA_DLFW, &rtw89_mac_size.wde_size9, +			    &rtw89_mac_size.ple_size8, &rtw89_mac_size.wde_qt4, +			    &rtw89_mac_size.wde_qt4, &rtw89_mac_size.ple_qt13, +			    &rtw89_mac_size.ple_qt13}, +	[RTW89_QTA_INVALID] = {RTW89_QTA_INVALID, NULL, NULL, NULL, NULL, NULL, +			       NULL}, +}; + +static const struct rtw89_dle_mem rtw8851b_dle_mem_usb3[] = { +	[RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size25, +			   &rtw89_mac_size.ple_size33, &rtw89_mac_size.wde_qt25, +			   &rtw89_mac_size.wde_qt25, &rtw89_mac_size.ple_qt74, +			   &rtw89_mac_size.ple_qt75}, +	[RTW89_QTA_DLFW] = {RTW89_QTA_DLFW, &rtw89_mac_size.wde_size9, +			    &rtw89_mac_size.ple_size8, &rtw89_mac_size.wde_qt4, +			    &rtw89_mac_size.wde_qt4, &rtw89_mac_size.ple_qt13, +			    &rtw89_mac_size.ple_qt13}, +	[RTW89_QTA_INVALID] = {RTW89_QTA_INVALID, NULL, NULL, NULL, NULL, NULL, +			       NULL}, +}; +  static const struct rtw89_reg3_def rtw8851b_btc_preagc_en_defs[] = {  	{0x46D0, GENMASK(1, 0), 0x3},  	{0x4AD4, GENMASK(31, 0), 0xf}, @@ -224,10 +292,17 @@ static const struct rtw89_edcca_regs rtw8851b_edcca_regs = {  	.edcca_p_mask			= B_EDCCA_LVL_MSK1,  	.ppdu_level			= R_SEG0R_EDCCA_LVL_V1,  	.ppdu_mask			= B_EDCCA_LVL_MSK3, -	.rpt_a				= R_EDCCA_RPT_A, -	.rpt_b				= R_EDCCA_RPT_B, -	.rpt_sel			= R_EDCCA_RPT_SEL, -	.rpt_sel_mask			= B_EDCCA_RPT_SEL_MSK, +	.p = {{ +		.rpt_a			= R_EDCCA_RPT_A, +		.rpt_b			= R_EDCCA_RPT_B, +		.rpt_sel		= R_EDCCA_RPT_SEL, +		.rpt_sel_mask		= B_EDCCA_RPT_SEL_MSK, +	}, { +		.rpt_a			= R_EDCCA_RPT_P1_A, +		.rpt_b			= R_EDCCA_RPT_P1_B, +		.rpt_sel		= R_EDCCA_RPT_SEL, +		.rpt_sel_mask		= B_EDCCA_RPT_SEL_P1_MSK, +	}},  	.tx_collision_t2r_st		= R_TX_COLLISION_T2R_ST,  	.tx_collision_t2r_st_mask	= B_TX_COLLISION_T2R_ST_M,  }; @@ -310,7 +385,8 @@ static int rtw8851b_pwr_on_func(struct rtw89_dev *rtwdev)  	rtw89_write8_clr(rtwdev, R_AX_PLATFORM_ENABLE, B_AX_PLATFORM_EN);  	rtw89_write8_set(rtwdev, R_AX_PLATFORM_ENABLE, B_AX_PLATFORM_EN); -	rtw89_write32_clr(rtwdev, R_AX_SYS_SDIO_CTRL, B_AX_PCIE_CALIB_EN_V1); +	if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) +		rtw89_write32_clr(rtwdev, R_AX_SYS_SDIO_CTRL, B_AX_PCIE_CALIB_EN_V1);  	ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_ANAPAR_WL, XTAL_SI_OFF_WEI,  				      XTAL_SI_OFF_WEI); @@ -355,8 +431,9 @@ static int rtw8851b_pwr_on_func(struct rtw89_dev *rtwdev)  	rtw89_write32_clr(rtwdev, R_AX_SYS_ISO_CTRL, B_AX_PWC_EV2EF_B14);  	rtw89_write32_clr(rtwdev, R_AX_PMC_DBG_CTRL2, B_AX_SYSON_DIS_PMCR_AX_WRMSK); -	rtw89_write32_set(rtwdev, R_AX_GPIO0_16_EECS_EESK_LED1_PULL_LOW_EN, -			  B_AX_GPIO10_PULL_LOW_EN | B_AX_GPIO16_PULL_LOW_EN_V1); +	if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) +		rtw89_write32_set(rtwdev, R_AX_GPIO0_16_EECS_EESK_LED1_PULL_LOW_EN, +				  B_AX_GPIO10_PULL_LOW_EN | B_AX_GPIO16_PULL_LOW_EN_V1);  	if (rtwdev->hal.cv == CHIP_CAV) {  		ret = rtw89_read_efuse_ver(rtwdev, &val8); @@ -440,7 +517,10 @@ static int rtw8851b_pwr_off_func(struct rtw89_dev *rtwdev)  	if (ret)  		return ret; -	rtw89_write32(rtwdev, R_AX_WLLPS_CTRL, SW_LPS_OPTION); +	if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) +		rtw89_write32(rtwdev, R_AX_WLLPS_CTRL, SW_LPS_OPTION); +	else if (rtwdev->hci.type == RTW89_HCI_TYPE_USB) +		rtw89_write32_clr(rtwdev, R_AX_SYS_PW_CTRL, B_AX_SOP_EDSWR);  	if (rtwdev->hal.cv == CHIP_CAV) {  		rtw8851b_patch_swr_pfm2pwm(rtwdev); @@ -449,19 +529,18 @@ static int rtw8851b_pwr_off_func(struct rtw89_dev *rtwdev)  		rtw89_write32_set(rtwdev, R_AX_SPSANA_ON_CTRL1, B_AX_FPWMDELAY);  	} -	rtw89_write32_set(rtwdev, R_AX_SYS_PW_CTRL, B_AX_APFM_SWLPS); +	if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) { +		rtw89_write32_set(rtwdev, R_AX_SYS_PW_CTRL, B_AX_APFM_SWLPS); +	} else if (rtwdev->hci.type == RTW89_HCI_TYPE_USB) { +		val32 = rtw89_read32(rtwdev, R_AX_SYS_PW_CTRL); +		val32 &= ~B_AX_AFSM_PCIE_SUS_EN; +		val32 |= B_AX_AFSM_WLSUS_EN; +		rtw89_write32(rtwdev, R_AX_SYS_PW_CTRL, val32); +	}  	return 0;  } -static void rtw8851b_efuse_parsing(struct rtw89_efuse *efuse, -				   struct rtw8851b_efuse *map) -{ -	ether_addr_copy(efuse->addr, map->e.mac_addr); -	efuse->rfe_type = map->rfe_type; -	efuse->xtal_cap = map->xtal_k; -} -  static void rtw8851b_efuse_parsing_tssi(struct rtw89_dev *rtwdev,  					struct rtw8851b_efuse *map)  { @@ -542,12 +621,18 @@ static int rtw8851b_read_efuse(struct rtw89_dev *rtwdev, u8 *log_map,  	switch (rtwdev->hci.type) {  	case RTW89_HCI_TYPE_PCIE: -		rtw8851b_efuse_parsing(efuse, map); +		ether_addr_copy(efuse->addr, map->e.mac_addr); +		break; +	case RTW89_HCI_TYPE_USB: +		ether_addr_copy(efuse->addr, map->u.mac_addr);  		break;  	default:  		return -EOPNOTSUPP;  	} +	efuse->rfe_type = map->rfe_type; +	efuse->xtal_cap = map->xtal_k; +  	rtw89_info(rtwdev, "chip rfe_type is %d\n", efuse->rfe_type);  	return 0; @@ -705,12 +790,22 @@ static void rtw8851b_phycap_parsing_gain_comp(struct rtw89_dev *rtwdev, u8 *phyc  	gain->comp_valid = valid;  } +static void rtw8851b_phycap_parsing_adc_td(struct rtw89_dev *rtwdev, u8 *phycap_map) +{ +	u32 phycap_addr = rtwdev->chip->phycap_addr; +	struct rtw89_efuse *efuse = &rtwdev->efuse; +	const u32 addr_adc_td = 0x5AF; + +	efuse->adc_td = phycap_map[addr_adc_td - phycap_addr] & GENMASK(4, 0); +} +  static int rtw8851b_read_phycap(struct rtw89_dev *rtwdev, u8 *phycap_map)  {  	rtw8851b_phycap_parsing_tssi(rtwdev, phycap_map);  	rtw8851b_phycap_parsing_thermal_trim(rtwdev, phycap_map);  	rtw8851b_phycap_parsing_pa_bias_trim(rtwdev, phycap_map);  	rtw8851b_phycap_parsing_gain_comp(rtwdev, phycap_map); +	rtw8851b_phycap_parsing_adc_td(rtwdev, phycap_map);  	return 0;  } @@ -1076,39 +1171,72 @@ static void rtw8851b_ctrl_ch(struct rtw89_dev *rtwdev,  static void rtw8851b_bw_setting(struct rtw89_dev *rtwdev, u8 bw)  { -	rtw89_phy_write32_mask(rtwdev, R_P0_CFCH_BW0, B_P0_CFCH_CTL, 0x8); -	rtw89_phy_write32_mask(rtwdev, R_P0_CFCH_BW0, B_P0_CFCH_EN, 0x2); -	rtw89_phy_write32_mask(rtwdev, R_P0_CFCH_BW0, B_P0_CFCH_BW0, 0x2); -	rtw89_phy_write32_mask(rtwdev, R_P0_CFCH_BW1, B_P0_CFCH_BW1, 0x4); +	struct rtw89_efuse *efuse = &rtwdev->efuse; +	u8 adc_bw_sel; + +	switch (efuse->adc_td) { +	default: +	case 0x19: +		adc_bw_sel = 0x4; +		break; +	case 0x11: +		adc_bw_sel = 0x5; +		break; +	case 0x9: +		adc_bw_sel = 0x3; +		break; +	} + +	rtw89_phy_write32_mask(rtwdev, R_P0_CFCH_BW1, B_P0_CFCH_BW1, adc_bw_sel);  	rtw89_phy_write32_mask(rtwdev, R_DRCK, B_DRCK_MUL, 0xf);  	rtw89_phy_write32_mask(rtwdev, R_ADCMOD, B_ADCMOD_LP, 0xa); -	rtw89_phy_write32_mask(rtwdev, R_P0_RXCK, B_P0_RXCK_ADJ, 0x92); +	rtw89_phy_write32_mask(rtwdev, R_DCIM, B_DCIM_RC, 0x3);  	switch (bw) {  	case RTW89_CHANNEL_WIDTH_5: +		rtw89_phy_write32_mask(rtwdev, R_P0_CFCH_BW0, B_P0_CFCH_CTL, 0x8); +		rtw89_phy_write32_mask(rtwdev, R_P0_CFCH_BW0, B_P0_CFCH_EN, 0x2); +		rtw89_phy_write32_mask(rtwdev, R_P0_CFCH_BW0, B_P0_CFCH_BW0, 0x2);  		rtw89_phy_write32_mask(rtwdev, R_DCIM, B_DCIM_FR, 0x1);  		rtw89_phy_write32_mask(rtwdev, R_WDADC, B_WDADC_SEL, 0x0);  		rtw89_phy_write32_mask(rtwdev, R_ADDCK0D, B_ADDCK_DS, 0x1); +		rtw89_phy_write32_mask(rtwdev, R_P0_RXCK, B_P0_RXCK_ADJ, 0x92);  		break;  	case RTW89_CHANNEL_WIDTH_10: +		rtw89_phy_write32_mask(rtwdev, R_P0_CFCH_BW0, B_P0_CFCH_CTL, 0x8); +		rtw89_phy_write32_mask(rtwdev, R_P0_CFCH_BW0, B_P0_CFCH_EN, 0x2); +		rtw89_phy_write32_mask(rtwdev, R_P0_CFCH_BW0, B_P0_CFCH_BW0, 0x2);  		rtw89_phy_write32_mask(rtwdev, R_DCIM, B_DCIM_FR, 0x1);  		rtw89_phy_write32_mask(rtwdev, R_WDADC, B_WDADC_SEL, 0x1);  		rtw89_phy_write32_mask(rtwdev, R_ADDCK0D, B_ADDCK_DS, 0x0); +		rtw89_phy_write32_mask(rtwdev, R_P0_RXCK, B_P0_RXCK_ADJ, 0x92);  		break;  	case RTW89_CHANNEL_WIDTH_20: +		rtw89_phy_write32_mask(rtwdev, R_P0_CFCH_BW0, B_P0_CFCH_CTL, 0x8); +		rtw89_phy_write32_mask(rtwdev, R_P0_CFCH_BW0, B_P0_CFCH_EN, 0x2); +		rtw89_phy_write32_mask(rtwdev, R_P0_CFCH_BW0, B_P0_CFCH_BW0, 0x2);  		rtw89_phy_write32_mask(rtwdev, R_DCIM, B_DCIM_FR, 0x2);  		rtw89_phy_write32_mask(rtwdev, R_WDADC, B_WDADC_SEL, 0x2);  		rtw89_phy_write32_mask(rtwdev, R_ADDCK0D, B_ADDCK_DS, 0x0); +		rtw89_phy_write32_mask(rtwdev, R_P0_RXCK, B_P0_RXCK_ADJ, 0x92);  		break;  	case RTW89_CHANNEL_WIDTH_40: +		rtw89_phy_write32_mask(rtwdev, R_P0_CFCH_BW0, B_P0_CFCH_CTL, 0x8); +		rtw89_phy_write32_mask(rtwdev, R_P0_CFCH_BW0, B_P0_CFCH_EN, 0x2); +		rtw89_phy_write32_mask(rtwdev, R_P0_CFCH_BW0, B_P0_CFCH_BW0, 0x2);  		rtw89_phy_write32_mask(rtwdev, R_DCIM, B_DCIM_FR, 0x2);  		rtw89_phy_write32_mask(rtwdev, R_WDADC, B_WDADC_SEL, 0x2);  		rtw89_phy_write32_mask(rtwdev, R_ADDCK0D, B_ADDCK_DS, 0x0); +		rtw89_phy_write32_mask(rtwdev, R_P0_RXCK, B_P0_RXCK_ADJ, 0x92);  		break;  	case RTW89_CHANNEL_WIDTH_80: +		rtw89_phy_write32_mask(rtwdev, R_P0_CFCH_BW0, B_P0_CFCH_CTL, 0x8); +		rtw89_phy_write32_mask(rtwdev, R_P0_CFCH_BW0, B_P0_CFCH_EN, 0x2); +		rtw89_phy_write32_mask(rtwdev, R_P0_CFCH_BW0, B_P0_CFCH_BW0, 0x2);  		rtw89_phy_write32_mask(rtwdev, R_DCIM, B_DCIM_FR, 0x0);  		rtw89_phy_write32_mask(rtwdev, R_WDADC, B_WDADC_SEL, 0x2);  		rtw89_phy_write32_mask(rtwdev, R_ADDCK0D, B_ADDCK_DS, 0x0); +		rtw89_phy_write32_mask(rtwdev, R_P0_RXCK, B_P0_RXCK_ADJ, 0x92);  		break;  	default:  		rtw89_warn(rtwdev, "Fail to set ADC\n"); @@ -1596,10 +1724,16 @@ static void rtw8851b_rfk_channel(struct rtw89_dev *rtwdev,  	enum rtw89_chanctx_idx chanctx_idx = rtwvif_link->chanctx_idx;  	enum rtw89_phy_idx phy_idx = rtwvif_link->phy_idx; +	rtw89_btc_ntfy_conn_rfk(rtwdev, true); +  	rtw8851b_rx_dck(rtwdev, phy_idx, chanctx_idx);  	rtw8851b_iqk(rtwdev, phy_idx, chanctx_idx); +	rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30);  	rtw8851b_tssi(rtwdev, phy_idx, true, chanctx_idx); +	rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30);  	rtw8851b_dpk(rtwdev, phy_idx, chanctx_idx); + +	rtw89_btc_ntfy_conn_rfk(rtwdev, false);  }  static void rtw8851b_rfk_band_changed(struct rtw89_dev *rtwdev, @@ -2395,6 +2529,7 @@ static const struct rtw89_chip_ops rtw8851b_chip_ops = {  	.set_txpwr_ctrl		= rtw8851b_set_txpwr_ctrl,  	.init_txpwr_unit	= rtw8851b_init_txpwr_unit,  	.get_thermal		= rtw8851b_get_thermal, +	.chan_to_rf18_val	= NULL,  	.ctrl_btg_bt_rx		= rtw8851b_ctrl_btg_bt_rx,  	.query_ppdu		= rtw8851b_query_ppdu,  	.convert_rpl_to_rssi	= NULL, @@ -2416,6 +2551,8 @@ static const struct rtw89_chip_ops rtw8851b_chip_ops = {  	.h2c_default_cmac_tbl	= rtw89_fw_h2c_default_cmac_tbl,  	.h2c_assoc_cmac_tbl	= rtw89_fw_h2c_assoc_cmac_tbl,  	.h2c_ampdu_cmac_tbl	= NULL, +	.h2c_txtime_cmac_tbl	= rtw89_fw_h2c_txtime_cmac_tbl, +	.h2c_punctured_cmac_tbl	= NULL,  	.h2c_default_dmac_tbl	= NULL,  	.h2c_update_beacon	= rtw89_fw_h2c_update_beacon,  	.h2c_ba_cam		= rtw89_fw_h2c_ba_cam, @@ -2451,14 +2588,20 @@ const struct rtw89_chip_info rtw8851b_chip_info = {  	.try_ce_fw		= true,  	.bbmcu_nr		= 0,  	.needed_fw_elms		= 0, +	.fw_blacklist		= NULL,  	.fifo_size		= 196608,  	.small_fifo_size	= true,  	.dle_scc_rsvd_size	= 98304,  	.max_amsdu_limit	= 3500,  	.dis_2g_40m_ul_ofdma	= true,  	.rsvd_ple_ofst		= 0x2f800, -	.hfc_param_ini		= rtw8851b_hfc_param_ini_pcie, -	.dle_mem		= rtw8851b_dle_mem_pcie, +	.hfc_param_ini		= {rtw8851b_hfc_param_ini_pcie, +				   rtw8851b_hfc_param_ini_usb, +				   NULL}, +	.dle_mem		= {rtw8851b_dle_mem_pcie, +				   rtw8851b_dle_mem_usb2, +				   rtw8851b_dle_mem_usb3, +				   NULL},  	.wde_qempty_acq_grpnum	= 4,  	.wde_qempty_mgq_grpsel	= 4,  	.rf_base_addr		= {0xe000}, @@ -2489,10 +2632,15 @@ const struct rtw89_chip_info rtw8851b_chip_info = {  				  BIT(NL80211_CHAN_WIDTH_80),  	.support_unii4		= true,  	.support_ant_gain	= false, +	.support_tas		= false, +	.support_sar_by_ant	= false,  	.ul_tb_waveform_ctrl	= true,  	.ul_tb_pwr_diff		= false, +	.rx_freq_frome_ie	= true,  	.hw_sec_hdr		= false,  	.hw_mgmt_tx_encrypt	= false, +	.hw_tkip_crypto		= false, +	.hw_mlo_bmc_crypto	= false,  	.rf_path_num		= 1,  	.tx_nss			= 1,  	.rx_nss			= 1, @@ -2514,7 +2662,6 @@ const struct rtw89_chip_info rtw8851b_chip_info = {  	.phycap_size		= 128,  	.para_ver		= 0,  	.wlcx_desired		= 0x06000000, -	.btcx_desired		= 0x7,  	.scbd			= 0x1,  	.mailbox		= 0x1, diff --git a/sys/contrib/dev/rtw89/rtw8851b_rfk.c b/sys/contrib/dev/rtw89/rtw8851b_rfk.c index f72b3ac6f149..7a319a6c838a 100644 --- a/sys/contrib/dev/rtw89/rtw8851b_rfk.c +++ b/sys/contrib/dev/rtw89/rtw8851b_rfk.c @@ -12,14 +12,14 @@  #include "rtw8851b_rfk_table.h"  #include "rtw8851b_table.h" -#define DPK_VER_8851B 0x5 -#define DPK_KIP_REG_NUM_8851B 7 +#define DPK_VER_8851B 0x11 +#define DPK_KIP_REG_NUM_8851B 8  #define DPK_RF_REG_NUM_8851B 4  #define DPK_KSET_NUM 4  #define RTW8851B_RXK_GROUP_NR 4  #define RTW8851B_RXK_GROUP_IDX_NR 2  #define RTW8851B_TXK_GROUP_NR 1 -#define RTW8851B_IQK_VER 0x2a +#define RTW8851B_IQK_VER 0x14  #define RTW8851B_IQK_SS 1  #define RTW8851B_LOK_GRAM 10  #define RTW8851B_TSSI_PATH_NR 1 @@ -85,6 +85,24 @@ enum rf_mode {  	RF_RXK2 = 0x7,  }; +enum adc_ck { +	ADC_NA = 0, +	ADC_480M = 1, +	ADC_960M = 2, +	ADC_1920M = 3, +}; + +enum dac_ck { +	DAC_40M = 0, +	DAC_80M = 1, +	DAC_120M = 2, +	DAC_160M = 3, +	DAC_240M = 4, +	DAC_320M = 5, +	DAC_480M = 6, +	DAC_960M = 7, +}; +  static const u32 _tssi_de_cck_long[RF_PATH_NUM_8851B] = {0x5858};  static const u32 _tssi_de_cck_short[RF_PATH_NUM_8851B] = {0x5860};  static const u32 _tssi_de_mcs_20m[RF_PATH_NUM_8851B] = {0x5838}; @@ -116,7 +134,7 @@ static const u32 rtw8851b_backup_rf_regs[] = {  #define BACKUP_RF_REGS_NR ARRAY_SIZE(rtw8851b_backup_rf_regs)  static const u32 dpk_kip_reg[DPK_KIP_REG_NUM_8851B] = { -	0x813c, 0x8124, 0xc0ec, 0xc0e8, 0xc0c4, 0xc0d4, 0xc0d8}; +	0x813c, 0x8124, 0xc0ec, 0xc0e8, 0xc0c4, 0xc0d4, 0xc0d8, 0x12a0};  static const u32 dpk_rf_reg[DPK_RF_REG_NUM_8851B] = {0xde, 0x8f, 0x5, 0x10005};  static void _set_ch(struct rtw89_dev *rtwdev, u32 val); @@ -163,6 +181,51 @@ static void _rfk_drf_direct_cntrl(struct rtw89_dev *rtwdev,  		rtw89_write_rf(rtwdev, path, RR_BBDC, RR_BBDC_SEL, 0x0);  } +static void _txck_force(struct rtw89_dev *rtwdev, enum rtw89_rf_path path, +			bool force, enum dac_ck ck) +{ +	rtw89_phy_write32_mask(rtwdev, R_P0_RXCK | (path << 13), B_P0_TXCK_ON, 0x0); + +	if (!force) +		return; + +	rtw89_phy_write32_mask(rtwdev, R_P0_RXCK | (path << 13), B_P0_TXCK_VAL, ck); +	rtw89_phy_write32_mask(rtwdev, R_P0_RXCK | (path << 13), B_P0_TXCK_ON, 0x1); +} + +static void _rxck_force(struct rtw89_dev *rtwdev, enum rtw89_rf_path path, +			bool force, enum adc_ck ck) +{ +	static const u32 ck960_8851b[] = {0x8, 0x2, 0x2, 0x4, 0xf, 0xa, 0x93}; +	static const u32 ck1920_8851b[] = {0x9, 0x0, 0x0, 0x3, 0xf, 0xa, 0x49}; +	const u32 *data; + +	rtw89_phy_write32_mask(rtwdev, R_P0_RXCK | (path << 13), B_P0_RXCK_ON, 0x0); +	if (!force) +		return; + +	rtw89_phy_write32_mask(rtwdev, R_P0_RXCK | (path << 13), B_P0_RXCK_VAL, ck); +	rtw89_phy_write32_mask(rtwdev, R_P0_RXCK | (path << 13), B_P0_RXCK_ON, 0x1); + +	switch (ck) { +	case ADC_960M: +		data = ck960_8851b; +		break; +	case ADC_1920M: +	default: +		data = ck1920_8851b; +		break; +	} + +	rtw89_phy_write32_mask(rtwdev, R_P0_CFCH_BW0 | (path << 8), B_P0_CFCH_CTL, data[0]); +	rtw89_phy_write32_mask(rtwdev, R_P0_CFCH_BW0 | (path << 8), B_P0_CFCH_EN, data[1]); +	rtw89_phy_write32_mask(rtwdev, R_P0_CFCH_BW0 | (path << 8), B_P0_CFCH_BW0, data[2]); +	rtw89_phy_write32_mask(rtwdev, R_P0_CFCH_BW1 | (path << 8), B_P0_CFCH_BW1, data[3]); +	rtw89_phy_write32_mask(rtwdev, R_DRCK | (path << 8), B_DRCK_MUL, data[4]); +	rtw89_phy_write32_mask(rtwdev, R_ADCMOD | (path << 8), B_ADCMOD_LP, data[5]); +	rtw89_phy_write32_mask(rtwdev, R_P0_RXCK | (path << 8), B_P0_RXCK_ADJ, data[6]); +} +  static void _wait_rx_mode(struct rtw89_dev *rtwdev, u8 kpath)  {  	u32 rf_mode; @@ -1044,10 +1107,43 @@ static void _iqk_rxclk_setting(struct rtw89_dev *rtwdev, u8 path)  	rtw89_write_rf(rtwdev, path, RR_RXBB2, RR_RXBB2_CKT, 0x1); -	if (iqk_info->iqk_bw[path] == RTW89_CHANNEL_WIDTH_80) +	if (iqk_info->iqk_bw[path] == RTW89_CHANNEL_WIDTH_80) { +		rtw89_phy_write32_mask(rtwdev, R_ADC_FIFO, B_ADC_FIFO_RXK, 0x0101); +		rtw89_phy_write32_mask(rtwdev, R_UPD_CLK, B_DPD_GDIS, 0x1); + +		_rxck_force(rtwdev, path, true, ADC_960M); +  		rtw89_rfk_parser(rtwdev, &rtw8851b_iqk_rxclk_80_defs_tbl); -	else +	} else { +		rtw89_phy_write32_mask(rtwdev, R_ADC_FIFO, B_ADC_FIFO_RXK, 0x0101); +		rtw89_phy_write32_mask(rtwdev, R_UPD_CLK, B_DPD_GDIS, 0x1); + +		_rxck_force(rtwdev, path, true, ADC_960M); +  		rtw89_rfk_parser(rtwdev, &rtw8851b_iqk_rxclk_others_defs_tbl); +	} + +	rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]S%x, (2)before RXK IQK\n", path); +	rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]S%x, 0x%x[07:10] = 0x%x\n", path, +		    0xc0d4, rtw89_phy_read32_mask(rtwdev, 0xc0d4, GENMASK(10, 7))); +	rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]S%x, 0x%x[11:14] = 0x%x\n", path, +		    0xc0d4, rtw89_phy_read32_mask(rtwdev, 0xc0d4, GENMASK(14, 11))); +	rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]S%x, 0x%x[26:27] = 0x%x\n", path, +		    0xc0d4, rtw89_phy_read32_mask(rtwdev, 0xc0d4, GENMASK(27, 26))); +	rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]S%x, 0x%x[05:08] = 0x%x\n", path, +		    0xc0d8, rtw89_phy_read32_mask(rtwdev, 0xc0d8, GENMASK(8, 5))); +	rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]S%x, 0x%x[17:21] = 0x%x\n", path, +		    0xc0c4, rtw89_phy_read32_mask(rtwdev, 0xc0c4, GENMASK(21, 17))); +	rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]S%x, 0x%x[16:31] = 0x%x\n", path, +		    0xc0e8, rtw89_phy_read32_mask(rtwdev, 0xc0e8, GENMASK(31, 16))); +	rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]S%x, 0x%x[04:05] = 0x%x\n", path, +		    0xc0e4, rtw89_phy_read32_mask(rtwdev, 0xc0e4, GENMASK(5, 4))); +	rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]S%x, 0x%x[23:31]  = 0x%x\n", path, +		    0x12a0, rtw89_phy_read32_mask(rtwdev, 0x12a0, GENMASK(31, 23))); +	rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]S%x, 0x%x[13:14] = 0x%x\n", path, +		    0xc0ec, rtw89_phy_read32_mask(rtwdev, 0xc0ec, GENMASK(14, 13))); +	rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]S%x, 0x%x[16:23] = 0x%x\n", path, +		    0xc0ec, rtw89_phy_read32_mask(rtwdev, 0xc0ec, GENMASK(23, 16)));  }  static bool _txk_5g_group_sel(struct rtw89_dev *rtwdev, @@ -1553,6 +1649,14 @@ static void _iqk_macbb_setting(struct rtw89_dev *rtwdev,  	rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]===>%s\n", __func__);  	rtw89_rfk_parser(rtwdev, &rtw8851b_iqk_macbb_defs_tbl); + +	_txck_force(rtwdev, path, true, DAC_960M); + +	rtw89_phy_write32_mask(rtwdev, R_UPD_CLK, B_DPD_GDIS, 0x1); + +	_rxck_force(rtwdev, path, true, ADC_1920M); + +	rtw89_rfk_parser(rtwdev, &rtw8851b_iqk_macbb_bh_defs_tbl);  }  static void _iqk_init(struct rtw89_dev *rtwdev) @@ -1794,7 +1898,21 @@ static void _dpk_bb_afe_setting(struct rtw89_dev *rtwdev, enum rtw89_rf_path pat  	rtw89_phy_write32_mask(rtwdev, R_ADC_FIFO, BIT(28 + path), 0x0);  	rtw89_phy_write32_mask(rtwdev, R_UPD_CLK + (path << 13), MASKDWORD, 0xd801dffd); -	rtw89_rfk_parser(rtwdev, &rtw8851b_iqk_bb_afe_defs_tbl); +	_txck_force(rtwdev, path, true, DAC_960M); +	_rxck_force(rtwdev, path, true, ADC_1920M); + +	rtw89_phy_write32_mask(rtwdev, R_DCIM, B_DCIM_FR, 0x0); +	rtw89_phy_write32_mask(rtwdev, R_ADCMOD, B_ADCMOD_AUTO_RST, 0x1); +	rtw89_phy_write32_mask(rtwdev, R_P0_NRBW, B_P0_NRBW_DBG, 0x1); +	udelay(1); +	rtw89_phy_write32_mask(rtwdev, R_ANAPAR_PW15, B_ANAPAR_PW15, 0x1f); +	udelay(10); +	rtw89_phy_write32_mask(rtwdev, R_ANAPAR_PW15, B_ANAPAR_PW15, 0x13); +	udelay(2); +	rtw89_phy_write32_mask(rtwdev, R_ANAPAR, B_ANAPAR_15, 0x0001); +	udelay(2); +	rtw89_phy_write32_mask(rtwdev, R_ANAPAR, B_ANAPAR_15, 0x0041); +	udelay(10);  	rtw89_phy_write32_mask(rtwdev, R_ADC_FIFO, BIT(20 + path), 0x1);  	rtw89_phy_write32_mask(rtwdev, R_ADC_FIFO, BIT(28 + path), 0x1); @@ -1827,6 +1945,17 @@ static void _dpk_tssi_pause(struct rtw89_dev *rtwdev, enum rtw89_rf_path path,  		    is_pause ? "pause" : "resume");  } +static +void _dpk_tssi_slope_k_onoff(struct rtw89_dev *rtwdev, enum rtw89_rf_path path, +			     bool is_on) +{ +	rtw89_phy_write32_mask(rtwdev, R_P0_TSSI_SLOPE_CAL + (path << 13), +			       B_P0_TSSI_SLOPE_CAL_EN, is_on); + +	rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DPK] S%d TSSI slpoe_k %s\n", path, +		    str_on_off(is_on)); +} +  static void _dpk_tpg_sel(struct rtw89_dev *rtwdev, enum rtw89_rf_path path, u8 kidx)  {  	struct rtw89_dpk_info *dpk = &rtwdev->dpk; @@ -1874,9 +2003,6 @@ static void _dpk_kip_control_rfc(struct rtw89_dev *rtwdev,  {  	rtw89_phy_write32_mask(rtwdev, R_UPD_CLK + (path << 13),  			       B_IQK_RFC_ON, ctrl_by_kip); - -	rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DPK] RFC is controlled by %s\n", -		    ctrl_by_kip ? "KIP" : "BB");  }  static void _dpk_kip_preset(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, @@ -2279,7 +2405,7 @@ static void _dpk_set_mdpd_para(struct rtw89_dev *rtwdev, u8 order)  	case 0: /* (5,3,1) */  		rtw89_phy_write32_mask(rtwdev, R_LDL_NORM, B_LDL_NORM_OP, 0x0);  		rtw89_phy_write32_mask(rtwdev, R_DPK_IDL, B_DPK_IDL_SEL, 0x2); -		rtw89_phy_write32_mask(rtwdev, R_LDL_NORM, B_LDL_NORM_PN, 0x4); +		rtw89_phy_write32_mask(rtwdev, R_LDL_NORM, B_LDL_NORM_PN, 0x3);  		rtw89_phy_write32_mask(rtwdev, R_MDPK_SYNC, B_MDPK_SYNC_DMAN, 0x1);  		break;  	case 1: /* (5,3,0) */ @@ -2315,8 +2441,6 @@ static void _dpk_set_mdpd_para(struct rtw89_dev *rtwdev, u8 order)  static void _dpk_idl_mpa(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy,  			 enum rtw89_rf_path path, u8 kidx)  { -	rtw89_phy_write32_mask(rtwdev, R_LDL_NORM, B_LDL_NORM_MA, 0x1); -  	if (rtw89_phy_read32_mask(rtwdev, R_IDL_MPA, B_IDL_MD500) == 0x1)  		_dpk_set_mdpd_para(rtwdev, 0x2);  	else if (rtw89_phy_read32_mask(rtwdev, R_IDL_MPA, B_IDL_MD530) == 0x1) @@ -2419,9 +2543,6 @@ static bool _dpk_main(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy,  	u8 init_xdbm = 17;  	bool is_fail; -	if (dpk->bp[path][kidx].band != RTW89_BAND_2G) -		init_xdbm = 15; -  	_dpk_kip_control_rfc(rtwdev, path, false);  	_rfk_rf_direct_cntrl(rtwdev, path, false);  	rtw89_write_rf(rtwdev, path, RR_BBDC, RFREG_MASK, 0x03ffd); @@ -2485,6 +2606,7 @@ static void _dpk_cal_select(struct rtw89_dev *rtwdev, bool force,  			    "[DPK] ========= S%d[%d] DPK Start =========\n",  			    path, dpk->cur_idx[path]); +		_dpk_tssi_slope_k_onoff(rtwdev, path, false);  		_dpk_rxagc_onoff(rtwdev, path, false);  		_rfk_drf_direct_cntrl(rtwdev, path, false);  		_dpk_bb_afe_setting(rtwdev, path); @@ -2502,7 +2624,7 @@ static void _dpk_cal_select(struct rtw89_dev *rtwdev, bool force,  		_dpk_reload_rf(rtwdev, dpk_rf_reg, rf_bkup, path);  		_dpk_bb_afe_restore(rtwdev, path);  		_dpk_rxagc_onoff(rtwdev, path, true); - +		_dpk_tssi_slope_k_onoff(rtwdev, path, true);  		if (rtwdev->is_tssi_mode[path])  			_dpk_tssi_pause(rtwdev, path, false);  	} diff --git a/sys/contrib/dev/rtw89/rtw8851b_rfk_table.c b/sys/contrib/dev/rtw89/rtw8851b_rfk_table.c index 0abf7978ccab..c5f70c045692 100644 --- a/sys/contrib/dev/rtw89/rtw8851b_rfk_table.c +++ b/sys/contrib/dev/rtw89/rtw8851b_rfk_table.c @@ -63,16 +63,7 @@ static const struct rtw89_reg5_def rtw8851b_dack_manual_off_defs[] = {  RTW89_DECLARE_RFK_TBL(rtw8851b_dack_manual_off_defs);  static const struct rtw89_reg5_def rtw8851b_iqk_rxclk_80_defs[] = { -	RTW89_DECL_RFK_WM(0x20fc, 0xffff0000, 0x0101), -	RTW89_DECL_RFK_WM(0x5670, 0x00002000, 0x1), -	RTW89_DECL_RFK_WM(0x12a0, 0x00080000, 0x1), -	RTW89_DECL_RFK_WM(0x12a0, 0x00070000, 0x2),  	RTW89_DECL_RFK_WM(0x5670, 0x60000000, 0x1), -	RTW89_DECL_RFK_WM(0xc0d4, 0x00000780, 0x8), -	RTW89_DECL_RFK_WM(0xc0d4, 0x00007800, 0x2), -	RTW89_DECL_RFK_WM(0xc0d4, 0x0c000000, 0x2), -	RTW89_DECL_RFK_WM(0xc0d8, 0x000001e0, 0x5), -	RTW89_DECL_RFK_WM(0xc0c4, 0x003e0000, 0xf),  	RTW89_DECL_RFK_WM(0xc0ec, 0x00006000, 0x0),  	RTW89_DECL_RFK_WM(0x12b8, 0x40000000, 0x1),  	RTW89_DECL_RFK_WM(0x030c, 0xff000000, 0x0f), @@ -85,16 +76,7 @@ static const struct rtw89_reg5_def rtw8851b_iqk_rxclk_80_defs[] = {  RTW89_DECLARE_RFK_TBL(rtw8851b_iqk_rxclk_80_defs);  static const struct rtw89_reg5_def rtw8851b_iqk_rxclk_others_defs[] = { -	RTW89_DECL_RFK_WM(0x20fc, 0xffff0000, 0x0101), -	RTW89_DECL_RFK_WM(0x5670, 0x00002000, 0x1), -	RTW89_DECL_RFK_WM(0x12a0, 0x00080000, 0x1), -	RTW89_DECL_RFK_WM(0x12a0, 0x00070000, 0x2),  	RTW89_DECL_RFK_WM(0x5670, 0x60000000, 0x0), -	RTW89_DECL_RFK_WM(0xc0d4, 0x00000780, 0x8), -	RTW89_DECL_RFK_WM(0xc0d4, 0x00007800, 0x2), -	RTW89_DECL_RFK_WM(0xc0d4, 0x0c000000, 0x2), -	RTW89_DECL_RFK_WM(0xc0d8, 0x000001e0, 0x5), -	RTW89_DECL_RFK_WM(0xc0c4, 0x003e0000, 0xf),  	RTW89_DECL_RFK_WM(0xc0ec, 0x00006000, 0x2),  	RTW89_DECL_RFK_WM(0x12b8, 0x40000000, 0x1),  	RTW89_DECL_RFK_WM(0x030c, 0xff000000, 0x0f), @@ -157,55 +139,38 @@ static const struct rtw89_reg5_def rtw8851b_iqk_macbb_defs[] = {  	RTW89_DECL_RFK_WM(0x20fc, 0x10000000, 0x0),  	RTW89_DECL_RFK_WM(0x5670, MASKDWORD, 0xf801fffd),  	RTW89_DECL_RFK_WM(0x5670, 0x00004000, 0x1), -	RTW89_DECL_RFK_WM(0x12a0, 0x00008000, 0x1),  	RTW89_DECL_RFK_WM(0x5670, 0x80000000, 0x1), -	RTW89_DECL_RFK_WM(0x12a0, 0x00007000, 0x7), -	RTW89_DECL_RFK_WM(0x5670, 0x00002000, 0x1), -	RTW89_DECL_RFK_WM(0x12a0, 0x00080000, 0x1), -	RTW89_DECL_RFK_WM(0x12a0, 0x00070000, 0x3), +}; + +RTW89_DECLARE_RFK_TBL(rtw8851b_iqk_macbb_defs); + +static const struct rtw89_reg5_def rtw8851b_iqk_macbb_bh_defs[] = {  	RTW89_DECL_RFK_WM(0x5670, 0x60000000, 0x2), -	RTW89_DECL_RFK_WM(0xc0d4, 0x00000780, 0x9), -	RTW89_DECL_RFK_WM(0xc0d4, 0x00007800, 0x1), -	RTW89_DECL_RFK_WM(0xc0d4, 0x0c000000, 0x0), -	RTW89_DECL_RFK_WM(0xc0d8, 0x000001e0, 0x3), -	RTW89_DECL_RFK_WM(0xc0c4, 0x003e0000, 0xa), -	RTW89_DECL_RFK_WM(0xc0ec, 0x00006000, 0x0), -	RTW89_DECL_RFK_WM(0xc0e8, 0x00000040, 0x1),  	RTW89_DECL_RFK_WM(0x12b8, 0x40000000, 0x1), +	RTW89_DECL_RFK_DELAY(2), +	RTW89_DECL_RFK_WM(0x030c, 0xff000000, 0x1f), +	RTW89_DECL_RFK_DELAY(10), +	RTW89_DECL_RFK_WM(0x030c, 0xff000000, 0x13), +	RTW89_DECL_RFK_DELAY(2), +	RTW89_DECL_RFK_WM(0x032c, 0xffff0000, 0x0001), +	RTW89_DECL_RFK_DELAY(2), +	RTW89_DECL_RFK_WM(0x032c, 0xffff0000, 0x0041), +	RTW89_DECL_RFK_DELAY(10), +	RTW89_DECL_RFK_WM(0x12b8, 0x40000000, 0x1), +	RTW89_DECL_RFK_DELAY(2),  	RTW89_DECL_RFK_WM(0x030c, 0xff000000, 0x1f), +	RTW89_DECL_RFK_DELAY(10),  	RTW89_DECL_RFK_WM(0x030c, 0xff000000, 0x13), +	RTW89_DECL_RFK_DELAY(2),  	RTW89_DECL_RFK_WM(0x032c, 0xffff0000, 0x0001), +	RTW89_DECL_RFK_DELAY(2),  	RTW89_DECL_RFK_WM(0x032c, 0xffff0000, 0x0041), +	RTW89_DECL_RFK_DELAY(10),  	RTW89_DECL_RFK_WM(0x20fc, 0x00100000, 0x1),  	RTW89_DECL_RFK_WM(0x20fc, 0x10000000, 0x1),  }; -RTW89_DECLARE_RFK_TBL(rtw8851b_iqk_macbb_defs); - -static const struct rtw89_reg5_def rtw8851b_iqk_bb_afe_defs[] = { -	RTW89_DECL_RFK_WM(0x5670, 0x00004000, 0x1), -	RTW89_DECL_RFK_WM(0x12a0, 0x00008000, 0x1), -	RTW89_DECL_RFK_WM(0x5670, 0x80000000, 0x1), -	RTW89_DECL_RFK_WM(0x12a0, 0x00007000, 0x7), -	RTW89_DECL_RFK_WM(0x5670, 0x00002000, 0x1), -	RTW89_DECL_RFK_WM(0x12a0, 0x00080000, 0x1), -	RTW89_DECL_RFK_WM(0x12a0, 0x00070000, 0x3), -	RTW89_DECL_RFK_WM(0x5670, 0x60000000, 0x2), -	RTW89_DECL_RFK_WM(0xc0d4, 0x00000780, 0x9), -	RTW89_DECL_RFK_WM(0xc0d4, 0x00007800, 0x1), -	RTW89_DECL_RFK_WM(0xc0d4, 0x0c000000, 0x0), -	RTW89_DECL_RFK_WM(0xc0d8, 0x000001e0, 0x3), -	RTW89_DECL_RFK_WM(0xc0c4, 0x003e0000, 0xa), -	RTW89_DECL_RFK_WM(0xc0ec, 0x00006000, 0x0), -	RTW89_DECL_RFK_WM(0xc0e8, 0x00000040, 0x1), -	RTW89_DECL_RFK_WM(0x12b8, 0x40000000, 0x1), -	RTW89_DECL_RFK_WM(0x030c, MASKBYTE3, 0x1f), -	RTW89_DECL_RFK_WM(0x030c, MASKBYTE3, 0x13), -	RTW89_DECL_RFK_WM(0x032c, MASKHWORD, 0x0001), -	RTW89_DECL_RFK_WM(0x032c, MASKHWORD, 0x0041), -}; - -RTW89_DECLARE_RFK_TBL(rtw8851b_iqk_bb_afe_defs); +RTW89_DECLARE_RFK_TBL(rtw8851b_iqk_macbb_bh_defs);  static const struct rtw89_reg5_def rtw8851b_tssi_sys_defs[] = {  	RTW89_DECL_RFK_WM(0x12bc, 0x000ffff0, 0xb5b5), diff --git a/sys/contrib/dev/rtw89/rtw8851b_rfk_table.h b/sys/contrib/dev/rtw89/rtw8851b_rfk_table.h index febfbecb691c..3f1547f57505 100644 --- a/sys/contrib/dev/rtw89/rtw8851b_rfk_table.h +++ b/sys/contrib/dev/rtw89/rtw8851b_rfk_table.h @@ -17,8 +17,8 @@ extern const struct rtw89_rfk_tbl rtw8851b_iqk_rxclk_others_defs_tbl;  extern const struct rtw89_rfk_tbl rtw8851b_iqk_txk_2ghz_defs_tbl;  extern const struct rtw89_rfk_tbl rtw8851b_iqk_txk_5ghz_defs_tbl;  extern const struct rtw89_rfk_tbl rtw8851b_iqk_afebb_restore_defs_tbl; -extern const struct rtw89_rfk_tbl rtw8851b_iqk_bb_afe_defs_tbl;  extern const struct rtw89_rfk_tbl rtw8851b_iqk_macbb_defs_tbl; +extern const struct rtw89_rfk_tbl rtw8851b_iqk_macbb_bh_defs_tbl;  extern const struct rtw89_rfk_tbl rtw8851b_tssi_sys_defs_tbl;  extern const struct rtw89_rfk_tbl rtw8851b_tssi_sys_a_defs_2g_tbl;  extern const struct rtw89_rfk_tbl rtw8851b_tssi_sys_a_defs_5g_tbl; diff --git a/sys/contrib/dev/rtw89/rtw8851b_table.c b/sys/contrib/dev/rtw89/rtw8851b_table.c index 522883c8dfb9..a9c309c245c3 100644 --- a/sys/contrib/dev/rtw89/rtw8851b_table.c +++ b/sys/contrib/dev/rtw89/rtw8851b_table.c @@ -2320,9 +2320,9 @@ static const struct rtw89_reg2_def rtw89_8851b_phy_nctl_regs[] = {  	{0x813c, 0x40000000},  	{0x8140, 0x00000000},  	{0x8144, 0x0b040b03}, -	{0x8148, 0x07020b04}, -	{0x814c, 0x07020b04}, -	{0x8150, 0xa0a00000}, +	{0x8148, 0x07020a04}, +	{0x814c, 0x07020a04}, +	{0x8150, 0xe4e40000},  	{0x8158, 0xffffffff},  	{0x815c, 0xffffffff},  	{0x8160, 0xffffffff}, @@ -2577,14 +2577,14 @@ static const struct rtw89_reg2_def rtw89_8851b_phy_nctl_regs[] = {  	{0x8534, 0x400042fe},  	{0x8538, 0x50554200},  	{0x853c, 0xb4183000}, -	{0x8540, 0xe537a50f}, +	{0x8540, 0xe535a50f},  	{0x8544, 0xf12bf02b},  	{0x8548, 0xf32bf22b},  	{0x854c, 0xf62bf42b},  	{0x8550, 0xf82bf72b},  	{0x8554, 0xfa2bf92b},  	{0x8558, 0xfd2bfc2b}, -	{0x855c, 0xe537fe2b}, +	{0x855c, 0xe535fe2b},  	{0x8560, 0xf12af02a},  	{0x8564, 0xf32af22a},  	{0x8568, 0xf52af42a}, @@ -2653,7 +2653,7 @@ static const struct rtw89_reg2_def rtw89_8851b_phy_nctl_regs[] = {  	{0x8664, 0x9700140f},  	{0x8668, 0x00017430},  	{0x866c, 0xe39ce35e}, -	{0x8670, 0xe52a0bbd}, +	{0x8670, 0xe5280bbd},  	{0x8674, 0xe36a0001},  	{0x8678, 0x0001e3c4},  	{0x867c, 0x55005b30}, @@ -2664,93 +2664,93 @@ static const struct rtw89_reg2_def rtw89_8851b_phy_nctl_regs[] = {  	{0x8690, 0x46100005},  	{0x8694, 0x00010004},  	{0x8698, 0x30f8e35e}, -	{0x869c, 0xe52a0023}, +	{0x869c, 0xe5280023},  	{0x86a0, 0x54ed0002},  	{0x86a4, 0x00230baa}, -	{0x86a8, 0x0002e52a}, +	{0x86a8, 0x0002e528},  	{0x86ac, 0xe356e3e4},  	{0x86b0, 0xe35e0001},  	{0x86b4, 0x002230f3}, -	{0x86b8, 0x0002e52a}, +	{0x86b8, 0x0002e528},  	{0x86bc, 0x0baa54ec}, -	{0x86c0, 0xe52a0022}, +	{0x86c0, 0xe5280022},  	{0x86c4, 0xe3e40002},  	{0x86c8, 0x0001e356},  	{0x86cc, 0x0baae35e},  	{0x86d0, 0xe3e430ec},  	{0x86d4, 0x0001e356},  	{0x86d8, 0x6d0f6c67}, -	{0x86dc, 0xe52ae39c}, +	{0x86dc, 0xe528e39c},  	{0x86e0, 0xe39c6c8b}, -	{0x86e4, 0x0bace52a}, +	{0x86e4, 0x0bace528},  	{0x86e8, 0x6d0f6cb3}, -	{0x86ec, 0xe52ae39c}, +	{0x86ec, 0xe528e39c},  	{0x86f0, 0x6cdb0bad},  	{0x86f4, 0xe39c6d0f}, -	{0x86f8, 0x6cf5e52a}, +	{0x86f8, 0x6cf5e528},  	{0x86fc, 0xe39c6d0f}, -	{0x8700, 0x6c0be52a}, +	{0x8700, 0x6c0be528},  	{0x8704, 0xe39c6d00}, -	{0x8708, 0x6c25e52a}, -	{0x870c, 0xe52ae39c}, +	{0x8708, 0x6c25e528}, +	{0x870c, 0xe528e39c},  	{0x8710, 0x6c4df8c6}, -	{0x8714, 0xe52ae39c}, +	{0x8714, 0xe528e39c},  	{0x8718, 0x6c75f9cf}, -	{0x871c, 0xe52ae39c}, +	{0x871c, 0xe528e39c},  	{0x8720, 0xe39c6c99}, -	{0x8724, 0xfad6e52a}, +	{0x8724, 0xfad6e528},  	{0x8728, 0x21e87410},  	{0x872c, 0x6e670009},  	{0x8730, 0xe3c46f0f}, -	{0x8734, 0x7410e52f}, +	{0x8734, 0x7410e52d},  	{0x8738, 0x000b21e8},  	{0x873c, 0xe3c46e8b}, -	{0x8740, 0x7410e52f}, +	{0x8740, 0x7410e52d},  	{0x8744, 0x000d21e8},  	{0x8748, 0x6f0f6eb3}, -	{0x874c, 0xe52fe3c4}, +	{0x874c, 0xe52de3c4},  	{0x8750, 0xfe07ff08},  	{0x8754, 0x21e87410},  	{0x8758, 0x6ec7000e}, -	{0x875c, 0xe52fe3c4}, +	{0x875c, 0xe52de3c4},  	{0x8760, 0x21e87410},  	{0x8764, 0x6edb000f},  	{0x8768, 0xe3c46f0f}, -	{0x876c, 0x7410e52f}, +	{0x876c, 0x7410e52d},  	{0x8770, 0x001021e8},  	{0x8774, 0xe3c46eef}, -	{0x8778, 0xff03e52f}, -	{0x877c, 0xe52ffe02}, +	{0x8778, 0xff03e52d}, +	{0x877c, 0xe52dfe02},  	{0x8780, 0x21e87410},  	{0x8784, 0x6e110013},  	{0x8788, 0xe3c46f00}, -	{0x878c, 0xff03e52f}, -	{0x8790, 0xe52ffe02}, +	{0x878c, 0xff03e52d}, +	{0x8790, 0xe52dfe02},  	{0x8794, 0x21e87410},  	{0x8798, 0x6e250014}, -	{0x879c, 0xe52fe3c4}, +	{0x879c, 0xe52de3c4},  	{0x87a0, 0xff08fc24},  	{0x87a4, 0x7410fe07},  	{0x87a8, 0x001521e8},  	{0x87ac, 0xe3c46e39}, -	{0x87b0, 0x7410e52f}, +	{0x87b0, 0x7410e52d},  	{0x87b4, 0x001621e8},  	{0x87b8, 0xe3c46e4d}, -	{0x87bc, 0xfd27e52f}, +	{0x87bc, 0xfd27e52d},  	{0x87c0, 0x21e87410},  	{0x87c4, 0x6e750018}, -	{0x87c8, 0xe52fe3c4}, +	{0x87c8, 0xe52de3c4},  	{0x87cc, 0x21e87410},  	{0x87d0, 0x6e99001a}, -	{0x87d4, 0xe52fe3c4}, +	{0x87d4, 0xe52de3c4},  	{0x87d8, 0xe36afe24},  	{0x87dc, 0x63404380},  	{0x87e0, 0x43006880},  	{0x87e4, 0x31300bac}, -	{0x87e8, 0xe52f0022}, +	{0x87e8, 0xe52d0022},  	{0x87ec, 0x54ec0002},  	{0x87f0, 0x00220baa}, -	{0x87f4, 0x0002e52f}, +	{0x87f4, 0x0002e52d},  	{0x87f8, 0xe362e3e4},  	{0x87fc, 0xe36a0001},  	{0x8800, 0x63404380}, @@ -2770,7 +2770,7 @@ static const struct rtw89_reg2_def rtw89_8851b_phy_nctl_regs[] = {  	{0x8838, 0x55010004},  	{0x883c, 0x66055b40},  	{0x8840, 0x62000007}, -	{0x8844, 0xe40e6300}, +	{0x8844, 0xe40c6300},  	{0x8848, 0x09000004},  	{0x884c, 0x0b400a01},  	{0x8850, 0x0e010d00}, @@ -2782,13 +2782,13 @@ static const struct rtw89_reg2_def rtw89_8851b_phy_nctl_regs[] = {  	{0x8868, 0x00044d01},  	{0x886c, 0x00074300},  	{0x8870, 0x05a30562}, -	{0x8874, 0xe40e961f}, +	{0x8874, 0xe40c961f},  	{0x8878, 0xe37e0004},  	{0x887c, 0x06a20007}, -	{0x8880, 0xe40e07a3}, +	{0x8880, 0xe40c07a3},  	{0x8884, 0xe37e0004}, -	{0x8888, 0x0002e3fe}, -	{0x888c, 0x4380e406}, +	{0x8888, 0x0002e3fc}, +	{0x888c, 0x4380e404},  	{0x8890, 0x4d000007},  	{0x8894, 0x43000004},  	{0x8898, 0x000742fe}, @@ -2815,13 +2815,13 @@ static const struct rtw89_reg2_def rtw89_8851b_phy_nctl_regs[] = {  	{0x88ec, 0x42000004},  	{0x88f0, 0x00070001},  	{0x88f4, 0x62006220}, -	{0x88f8, 0x0001e406}, +	{0x88f8, 0x0001e404},  	{0x88fc, 0x63000007},  	{0x8900, 0x09000004},  	{0x8904, 0x0e010a00},  	{0x8908, 0x00070032}, -	{0x890c, 0xe40e06a2}, -	{0x8910, 0x0002e41a}, +	{0x890c, 0xe40c06a2}, +	{0x8910, 0x0002e418},  	{0x8914, 0x000742fe},  	{0x8918, 0x00044d00},  	{0x891c, 0x00014200}, @@ -2839,50 +2839,50 @@ static const struct rtw89_reg2_def rtw89_8851b_phy_nctl_regs[] = {  	{0x894c, 0x00014300},  	{0x8950, 0x6c060005},  	{0x8954, 0xe2aae298}, -	{0x8958, 0xe42ae285}, -	{0x895c, 0xe432e2f3}, +	{0x8958, 0xe428e285}, +	{0x895c, 0xe430e2f3},  	{0x8960, 0x0001e30c},  	{0x8964, 0x0005e285},  	{0x8968, 0xe2986c06}, -	{0x896c, 0xe42ae4a9}, -	{0x8970, 0xe432e2f3}, +	{0x896c, 0xe428e4a7}, +	{0x8970, 0xe430e2f3},  	{0x8974, 0x0001e30c},  	{0x8978, 0x6c000005},  	{0x897c, 0xe2aae298}, -	{0x8980, 0xe445e285}, -	{0x8984, 0xe44de2f3}, +	{0x8980, 0xe443e285}, +	{0x8984, 0xe44be2f3},  	{0x8988, 0x0001e30c},  	{0x898c, 0x0005e285},  	{0x8990, 0xe2986c00}, -	{0x8994, 0xe445e4a9}, -	{0x8998, 0xe44de2f3}, +	{0x8994, 0xe443e4a7}, +	{0x8998, 0xe44be2f3},  	{0x899c, 0x0001e30c},  	{0x89a0, 0x6c040005},  	{0x89a4, 0xe2aae298}, -	{0x89a8, 0xe460e285}, -	{0x89ac, 0xe468e2f3}, +	{0x89a8, 0xe45ee285}, +	{0x89ac, 0xe466e2f3},  	{0x89b0, 0x0001e30c},  	{0x89b4, 0x0005e285},  	{0x89b8, 0xe2986c04}, -	{0x89bc, 0xe460e4a9}, -	{0x89c0, 0xe468e2f3}, +	{0x89bc, 0xe45ee4a7}, +	{0x89c0, 0xe466e2f3},  	{0x89c4, 0x0001e30c},  	{0x89c8, 0x6c020005},  	{0x89cc, 0xe2aae298}, -	{0x89d0, 0xe47be285}, -	{0x89d4, 0xe483e2f3}, +	{0x89d0, 0xe479e285}, +	{0x89d4, 0xe481e2f3},  	{0x89d8, 0x0001e30c},  	{0x89dc, 0x0005e285},  	{0x89e0, 0xe2986c02}, -	{0x89e4, 0xe47be4a9}, -	{0x89e8, 0xe483e2f3}, +	{0x89e4, 0xe479e4a7}, +	{0x89e8, 0xe481e2f3},  	{0x89ec, 0x0001e30c},  	{0x89f0, 0x43800004},  	{0x89f4, 0x610a6008},  	{0x89f8, 0x63ce6200},  	{0x89fc, 0x60800006},  	{0x8a00, 0x00047f00}, -	{0x8a04, 0xe4e04300}, +	{0x8a04, 0xe4de4300},  	{0x8a08, 0x00070001},  	{0x8a0c, 0x4d015500},  	{0x8a10, 0x74200004}, @@ -2895,22 +2895,22 @@ static const struct rtw89_reg2_def rtw89_8851b_phy_nctl_regs[] = {  	{0x8a2c, 0x00014300},  	{0x8a30, 0x74200004},  	{0x8a34, 0x77000005}, -	{0x8a38, 0x73887e07}, +	{0x8a38, 0x73887e06},  	{0x8a3c, 0x8f007380},  	{0x8a40, 0x0004140f},  	{0x8a44, 0x00057430},  	{0x8a48, 0x00017300}, -	{0x8a4c, 0x0005e496}, +	{0x8a4c, 0x0005e494},  	{0x8a50, 0x00017300},  	{0x8a54, 0x43800004},  	{0x8a58, 0x0006b103},  	{0x8a5c, 0x91037cdb},  	{0x8a60, 0x40db0007},  	{0x8a64, 0x43000004}, -	{0x8a68, 0x0005e496}, +	{0x8a68, 0x0005e494},  	{0x8a6c, 0x00067380},  	{0x8a70, 0x60025d01}, -	{0x8a74, 0xe4ba6200}, +	{0x8a74, 0xe4b86200},  	{0x8a78, 0x73000005},  	{0x8a7c, 0x76080007},  	{0x8a80, 0x00047578}, @@ -2943,8 +2943,8 @@ static const struct rtw89_reg2_def rtw89_8851b_phy_nctl_regs[] = {  	{0x8aec, 0x7cdb0006},  	{0x8af0, 0x00079103},  	{0x8af4, 0x000440db}, -	{0x8af8, 0xe4964300}, -	{0x8afc, 0xe4ba7e03}, +	{0x8af8, 0xe4944300}, +	{0x8afc, 0xe4b87e03},  	{0x8b00, 0x43800004},  	{0x8b04, 0x0006b103},  	{0x8b08, 0x91037c5b}, @@ -2976,14 +2976,14 @@ static const struct rtw89_reg2_def rtw89_8851b_phy_nctl_regs[] = {  	{0x8b70, 0x43000004},  	{0x8b74, 0x73000005},  	{0x8b78, 0x76000007}, -	{0x8b7c, 0xe4c30001}, +	{0x8b7c, 0xe4c10001},  	{0x8b80, 0x00040001},  	{0x8b84, 0x60004380},  	{0x8b88, 0x62016100},  	{0x8b8c, 0x00066310},  	{0x8b90, 0x00046000},  	{0x8b94, 0x00014300}, -	{0x8b98, 0x0001e4e0}, +	{0x8b98, 0x0001e4de},  	{0x8b9c, 0x4e004f02},  	{0x8ba0, 0x52015302},  	{0x8ba4, 0x140f0001}, @@ -3030,7 +3030,7 @@ static const struct rtw89_reg2_def rtw89_8851b_phy_nctl_regs[] = {  	{0x8c48, 0xbf1ae3ac},  	{0x8c4c, 0xe36e300b},  	{0x8c50, 0xe390e377}, -	{0x8c54, 0x0001e523}, +	{0x8c54, 0x0001e521},  	{0x8c58, 0x54c054bf},  	{0x8c5c, 0x54c154a3},  	{0x8c60, 0x4c1854a4}, @@ -3039,7 +3039,7 @@ static const struct rtw89_reg2_def rtw89_8851b_phy_nctl_regs[] = {  	{0x8c6c, 0xbf051402},  	{0x8c70, 0x54a354c1},  	{0x8c74, 0xbf011402}, -	{0x8c78, 0x54dfe534}, +	{0x8c78, 0x54dfe532},  	{0x8c7c, 0x54bf0001},  	{0x8c80, 0x050a54e5},  	{0x8c84, 0x000154df}, @@ -3060,186 +3060,185 @@ static const struct rtw89_reg2_def rtw89_8851b_phy_nctl_regs[] = {  	{0x8cc0, 0x56005610},  	{0x8cc4, 0x00018c00},  	{0x8cc8, 0x57005704}, -	{0x8ccc, 0xa7038e00}, -	{0x8cd0, 0x33f0aff7}, -	{0x8cd4, 0xaf034019}, -	{0x8cd8, 0x33f0402b}, -	{0x8cdc, 0x33df402b}, -	{0x8ce0, 0x57005708}, -	{0x8ce4, 0x57818e00}, -	{0x8ce8, 0x8e005780}, -	{0x8cec, 0x00074380}, -	{0x8cf0, 0x5c005c01}, -	{0x8cf4, 0x00041403}, -	{0x8cf8, 0x00014300}, -	{0x8cfc, 0x0007427f}, -	{0x8d00, 0x62006280}, -	{0x8d04, 0x00049200}, -	{0x8d08, 0x00014200}, -	{0x8d0c, 0x0007427f}, -	{0x8d10, 0x63146394}, -	{0x8d14, 0x00049200}, -	{0x8d18, 0x00014200}, -	{0x8d1c, 0x42fe0004}, -	{0x8d20, 0x4d010007}, -	{0x8d24, 0x42000004}, -	{0x8d28, 0x140f7420}, -	{0x8d2c, 0x57005710}, -	{0x8d30, 0x0001141f}, -	{0x8d34, 0x42fe0004}, -	{0x8d38, 0x4d010007}, -	{0x8d3c, 0x42000004}, -	{0x8d40, 0x140f7420}, -	{0x8d44, 0x000742bf}, -	{0x8d48, 0x62006240}, -	{0x8d4c, 0x0004141f}, -	{0x8d50, 0x00014200}, -	{0x8d54, 0x5d060006}, -	{0x8d58, 0x61046003}, -	{0x8d5c, 0x00056201}, -	{0x8d60, 0x00017310}, -	{0x8d64, 0x43800004}, -	{0x8d68, 0x5e010007}, -	{0x8d6c, 0x140a5e00}, -	{0x8d70, 0x0006b103}, -	{0x8d74, 0x91037f07}, -	{0x8d78, 0x43070007}, -	{0x8d7c, 0x5c000006}, -	{0x8d80, 0x5e035d02}, -	{0x8d84, 0x43000004}, -	{0x8d88, 0x00060001}, -	{0x8d8c, 0x60005d04}, -	{0x8d90, 0x62016104}, -	{0x8d94, 0x73100005}, -	{0x8d98, 0x00040001}, -	{0x8d9c, 0x00074380}, -	{0x8da0, 0x5e005e01}, -	{0x8da4, 0xb103140a}, -	{0x8da8, 0x7fc60006}, -	{0x8dac, 0x00079103}, -	{0x8db0, 0x000643c6}, -	{0x8db4, 0x5d025c00}, -	{0x8db8, 0x00045e03}, -	{0x8dbc, 0x00014300}, -	{0x8dc0, 0x5d040006}, -	{0x8dc4, 0x61046000}, -	{0x8dc8, 0x00056201}, -	{0x8dcc, 0x00017310}, -	{0x8dd0, 0x43800004}, -	{0x8dd4, 0x5e010007}, -	{0x8dd8, 0x140a5e00}, -	{0x8ddc, 0x0006b103}, -	{0x8de0, 0x91037fc6}, -	{0x8de4, 0x43c60007}, -	{0x8de8, 0x5c000006}, -	{0x8dec, 0x5e035d02}, -	{0x8df0, 0x43000004}, -	{0x8df4, 0x00060001}, -	{0x8df8, 0x60025d00}, -	{0x8dfc, 0x62016100}, -	{0x8e00, 0x73000005}, -	{0x8e04, 0x00040001}, -	{0x8e08, 0x00074380}, -	{0x8e0c, 0x5e005e01}, -	{0x8e10, 0xb103140a}, -	{0x8e14, 0x7fc00006}, -	{0x8e18, 0x00079103}, -	{0x8e1c, 0x000643c0}, -	{0x8e20, 0x5d025c00}, -	{0x8e24, 0x00045e03}, -	{0x8e28, 0x00014300}, -	{0x8e2c, 0x7e020005}, -	{0x8e30, 0x42f70004}, -	{0x8e34, 0x6c080005}, -	{0x8e38, 0x42700004}, -	{0x8e3c, 0x73810005}, -	{0x8e40, 0x93007380}, -	{0x8e44, 0x42f70004}, -	{0x8e48, 0x6c000005}, -	{0x8e4c, 0x42000004}, -	{0x8e50, 0x00040001}, -	{0x8e54, 0x00074380}, -	{0x8e58, 0x73007304}, -	{0x8e5c, 0x72401405}, -	{0x8e60, 0x43000004}, -	{0x8e64, 0x74040006}, -	{0x8e68, 0x40010007}, -	{0x8e6c, 0xab004000}, -	{0x8e70, 0x0001140f}, -	{0x8e74, 0x140ae517}, -	{0x8e78, 0x140ae4c3}, -	{0x8e7c, 0x0001e51e}, -	{0x8e80, 0xe4c3e517}, -	{0x8e84, 0x00040001}, -	{0x8e88, 0x00047410}, -	{0x8e8c, 0x42f04380}, -	{0x8e90, 0x62080007}, -	{0x8e94, 0x24206301}, -	{0x8e98, 0x14c80000}, -	{0x8e9c, 0x00002428}, -	{0x8ea0, 0x1a4215f4}, -	{0x8ea4, 0x6300000b}, -	{0x8ea8, 0x42000004}, -	{0x8eac, 0x74304300}, -	{0x8eb0, 0x4380140f}, -	{0x8eb4, 0x73080007}, -	{0x8eb8, 0x00047300}, -	{0x8ebc, 0x00014300}, -	{0x8ec0, 0x4bf00007}, -	{0x8ec4, 0x490b4a8f}, -	{0x8ec8, 0x4a8e48f1}, -	{0x8ecc, 0x48a5490a}, -	{0x8ed0, 0x49094a8d}, -	{0x8ed4, 0x4a8c487d}, -	{0x8ed8, 0x48754908}, -	{0x8edc, 0x49074a8b}, -	{0x8ee0, 0x4a8a4889}, -	{0x8ee4, 0x48b74906}, -	{0x8ee8, 0x49054a89}, -	{0x8eec, 0x4a8848fc}, -	{0x8ef0, 0x48564905}, -	{0x8ef4, 0x49044a87}, -	{0x8ef8, 0x4a8648c1}, -	{0x8efc, 0x483d4904}, -	{0x8f00, 0x49034a85}, -	{0x8f04, 0x4a8448c7}, -	{0x8f08, 0x485e4903}, -	{0x8f0c, 0x49024a83}, -	{0x8f10, 0x4a8248ac}, -	{0x8f14, 0x48624902}, -	{0x8f18, 0x49024a81}, -	{0x8f1c, 0x4a804820}, -	{0x8f20, 0x48004900}, -	{0x8f24, 0x49014a90}, -	{0x8f28, 0x4a10481f}, -	{0x8f2c, 0x00060001}, -	{0x8f30, 0x5f005f80}, -	{0x8f34, 0x00059900}, -	{0x8f38, 0x00017300}, -	{0x8f3c, 0x63800006}, -	{0x8f40, 0x98006300}, -	{0x8f44, 0x549f0001}, -	{0x8f48, 0x5c015400}, -	{0x8f4c, 0x540054df}, -	{0x8f50, 0x00015c02}, -	{0x8f54, 0x07145c01}, -	{0x8f58, 0x5c025400}, -	{0x8f5c, 0x5c020001}, -	{0x8f60, 0x54000714}, -	{0x8f64, 0x00015c01}, -	{0x8f68, 0x4c184c98}, -	{0x8f6c, 0x00080001}, -	{0x8f70, 0x5c020004}, -	{0x8f74, 0x09017430}, -	{0x8f78, 0x0ba60c01}, -	{0x8f7c, 0x77800005}, -	{0x8f80, 0x52200007}, -	{0x8f84, 0x43800004}, -	{0x8f88, 0x610a6008}, -	{0x8f8c, 0x63c26200}, -	{0x8f90, 0x5c000007}, -	{0x8f94, 0x43000004}, -	{0x8f98, 0x00000001}, +	{0x8ccc, 0x33ee8e00}, +	{0x8cd0, 0xaf034019}, +	{0x8cd4, 0x33ee402b}, +	{0x8cd8, 0x33df402b}, +	{0x8cdc, 0x57005708}, +	{0x8ce0, 0x57818e00}, +	{0x8ce4, 0x8e005780}, +	{0x8ce8, 0x00074380}, +	{0x8cec, 0x5c005c01}, +	{0x8cf0, 0x00041403}, +	{0x8cf4, 0x00014300}, +	{0x8cf8, 0x0007427f}, +	{0x8cfc, 0x62006280}, +	{0x8d00, 0x00049200}, +	{0x8d04, 0x00014200}, +	{0x8d08, 0x0007427f}, +	{0x8d0c, 0x63146394}, +	{0x8d10, 0x00049200}, +	{0x8d14, 0x00014200}, +	{0x8d18, 0x42fe0004}, +	{0x8d1c, 0x4d010007}, +	{0x8d20, 0x42000004}, +	{0x8d24, 0x140f7420}, +	{0x8d28, 0x57005710}, +	{0x8d2c, 0x0001141f}, +	{0x8d30, 0x42fe0004}, +	{0x8d34, 0x4d010007}, +	{0x8d38, 0x42000004}, +	{0x8d3c, 0x140f7420}, +	{0x8d40, 0x000742bf}, +	{0x8d44, 0x62006240}, +	{0x8d48, 0x0004141f}, +	{0x8d4c, 0x00014200}, +	{0x8d50, 0x5d060006}, +	{0x8d54, 0x61046003}, +	{0x8d58, 0x00056200}, +	{0x8d5c, 0x00017310}, +	{0x8d60, 0x43800004}, +	{0x8d64, 0x5e010007}, +	{0x8d68, 0x140a5e00}, +	{0x8d6c, 0x0006b103}, +	{0x8d70, 0x91037f07}, +	{0x8d74, 0x43070007}, +	{0x8d78, 0x5c000006}, +	{0x8d7c, 0x5e035d02}, +	{0x8d80, 0x43000004}, +	{0x8d84, 0x00060001}, +	{0x8d88, 0x60005d04}, +	{0x8d8c, 0x62006104}, +	{0x8d90, 0x73100005}, +	{0x8d94, 0x00040001}, +	{0x8d98, 0x00074380}, +	{0x8d9c, 0x5e005e01}, +	{0x8da0, 0xb103140a}, +	{0x8da4, 0x7fc60006}, +	{0x8da8, 0x00079103}, +	{0x8dac, 0x000643c6}, +	{0x8db0, 0x5d025c00}, +	{0x8db4, 0x00045e03}, +	{0x8db8, 0x00014300}, +	{0x8dbc, 0x5d040006}, +	{0x8dc0, 0x61046000}, +	{0x8dc4, 0x00056200}, +	{0x8dc8, 0x00017310}, +	{0x8dcc, 0x43800004}, +	{0x8dd0, 0x5e010007}, +	{0x8dd4, 0x140a5e00}, +	{0x8dd8, 0x0006b103}, +	{0x8ddc, 0x91037fc6}, +	{0x8de0, 0x43c60007}, +	{0x8de4, 0x5c000006}, +	{0x8de8, 0x5e035d02}, +	{0x8dec, 0x43000004}, +	{0x8df0, 0x00060001}, +	{0x8df4, 0x60025d00}, +	{0x8df8, 0x62006100}, +	{0x8dfc, 0x73000005}, +	{0x8e00, 0x00040001}, +	{0x8e04, 0x00074380}, +	{0x8e08, 0x5e005e01}, +	{0x8e0c, 0xb103140a}, +	{0x8e10, 0x7fc00006}, +	{0x8e14, 0x00079103}, +	{0x8e18, 0x000643c0}, +	{0x8e1c, 0x5d025c00}, +	{0x8e20, 0x00045e03}, +	{0x8e24, 0x00014300}, +	{0x8e28, 0x7e020005}, +	{0x8e2c, 0x42f70004}, +	{0x8e30, 0x6c080005}, +	{0x8e34, 0x42700004}, +	{0x8e38, 0x73810005}, +	{0x8e3c, 0x93007380}, +	{0x8e40, 0x42f70004}, +	{0x8e44, 0x6c000005}, +	{0x8e48, 0x42000004}, +	{0x8e4c, 0x00040001}, +	{0x8e50, 0x00074380}, +	{0x8e54, 0x73007304}, +	{0x8e58, 0x72401405}, +	{0x8e5c, 0x43000004}, +	{0x8e60, 0x74040006}, +	{0x8e64, 0x40010007}, +	{0x8e68, 0xab004000}, +	{0x8e6c, 0x0001140f}, +	{0x8e70, 0x140ae515}, +	{0x8e74, 0x140ae4c1}, +	{0x8e78, 0x0001e51c}, +	{0x8e7c, 0xe4c1e515}, +	{0x8e80, 0x00040001}, +	{0x8e84, 0x00047410}, +	{0x8e88, 0x42f04380}, +	{0x8e8c, 0x62080007}, +	{0x8e90, 0x24206301}, +	{0x8e94, 0x14c80000}, +	{0x8e98, 0x00002428}, +	{0x8e9c, 0x1a4215f4}, +	{0x8ea0, 0x6300000b}, +	{0x8ea4, 0x42000004}, +	{0x8ea8, 0x74304300}, +	{0x8eac, 0x4380140f}, +	{0x8eb0, 0x73080007}, +	{0x8eb4, 0x00047300}, +	{0x8eb8, 0x00014300}, +	{0x8ebc, 0x4bf00007}, +	{0x8ec0, 0x490b4a8f}, +	{0x8ec4, 0x4a8e48f1}, +	{0x8ec8, 0x48a5490a}, +	{0x8ecc, 0x49094a8d}, +	{0x8ed0, 0x4a8c487d}, +	{0x8ed4, 0x48754908}, +	{0x8ed8, 0x49074a8b}, +	{0x8edc, 0x4a8a4889}, +	{0x8ee0, 0x48b74906}, +	{0x8ee4, 0x49054a89}, +	{0x8ee8, 0x4a8848fc}, +	{0x8eec, 0x48564905}, +	{0x8ef0, 0x49044a87}, +	{0x8ef4, 0x4a8648c1}, +	{0x8ef8, 0x483d4904}, +	{0x8efc, 0x49034a85}, +	{0x8f00, 0x4a8448c7}, +	{0x8f04, 0x485e4903}, +	{0x8f08, 0x49024a83}, +	{0x8f0c, 0x4a8248ac}, +	{0x8f10, 0x48624902}, +	{0x8f14, 0x49024a81}, +	{0x8f18, 0x4a804820}, +	{0x8f1c, 0x48004900}, +	{0x8f20, 0x49014a90}, +	{0x8f24, 0x4a10481f}, +	{0x8f28, 0x00060001}, +	{0x8f2c, 0x5f005f80}, +	{0x8f30, 0x00059900}, +	{0x8f34, 0x00017300}, +	{0x8f38, 0x63800006}, +	{0x8f3c, 0x98006300}, +	{0x8f40, 0x549f0001}, +	{0x8f44, 0x5c015400}, +	{0x8f48, 0x540054df}, +	{0x8f4c, 0x00015c02}, +	{0x8f50, 0x07145c01}, +	{0x8f54, 0x5c025400}, +	{0x8f58, 0x5c020001}, +	{0x8f5c, 0x54000714}, +	{0x8f60, 0x00015c01}, +	{0x8f64, 0x4c184c98}, +	{0x8f68, 0x00080001}, +	{0x8f6c, 0x5c020004}, +	{0x8f70, 0x09017430}, +	{0x8f74, 0x0ba60c01}, +	{0x8f78, 0x77800005}, +	{0x8f7c, 0x52200007}, +	{0x8f80, 0x43800004}, +	{0x8f84, 0x610a6008}, +	{0x8f88, 0x63c26200}, +	{0x8f8c, 0x5c000007}, +	{0x8f90, 0x43000004}, +	{0x8f94, 0x00000001},  	{0x8080, 0x00000004},  	{0x8080, 0x00000000},  	{0x8088, 0x00000000}, diff --git a/sys/contrib/dev/rtw89/rtw8851be.c b/sys/contrib/dev/rtw89/rtw8851be.c index cbf200f0068e..b0ad4cb4953b 100644 --- a/sys/contrib/dev/rtw89/rtw8851be.c +++ b/sys/contrib/dev/rtw89/rtw8851be.c @@ -89,6 +89,7 @@ static struct pci_driver rtw89_8851be_driver = {  	.probe		= rtw89_pci_probe,  	.remove		= rtw89_pci_remove,  	.driver.pm	= &rtw89_pm_ops, +	.err_handler    = &rtw89_pci_err_handler,  #if defined(__FreeBSD__)  	.bsddriver.name	= KBUILD_MODNAME,  #endif diff --git a/sys/contrib/dev/rtw89/rtw8851bu.c b/sys/contrib/dev/rtw89/rtw8851bu.c new file mode 100644 index 000000000000..c3722547c6b0 --- /dev/null +++ b/sys/contrib/dev/rtw89/rtw8851bu.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2025  Realtek Corporation + */ + +#include <linux/module.h> +#include <linux/usb.h> +#include "rtw8851b.h" +#include "usb.h" + +static const struct rtw89_driver_info rtw89_8851bu_info = { +	.chip = &rtw8851b_chip_info, +	.variant = NULL, +	.quirks = NULL, +}; + +static const struct usb_device_id rtw_8851bu_id_table[] = { +	{ USB_DEVICE_AND_INTERFACE_INFO(0x0bda, 0xb851, 0xff, 0xff, 0xff), +	  .driver_info = (kernel_ulong_t)&rtw89_8851bu_info }, +	/* TP-Link Archer TX10UB Nano */ +	{ USB_DEVICE_AND_INTERFACE_INFO(0x3625, 0x010b, 0xff, 0xff, 0xff), +	  .driver_info = (kernel_ulong_t)&rtw89_8851bu_info }, +	/* Edimax EW-7611UXB */ +	{ USB_DEVICE_AND_INTERFACE_INFO(0x7392, 0xe611, 0xff, 0xff, 0xff), +	  .driver_info = (kernel_ulong_t)&rtw89_8851bu_info }, +	{}, +}; +MODULE_DEVICE_TABLE(usb, rtw_8851bu_id_table); + +static struct usb_driver rtw_8851bu_driver = { +	.name = KBUILD_MODNAME, +	.id_table = rtw_8851bu_id_table, +	.probe = rtw89_usb_probe, +	.disconnect = rtw89_usb_disconnect, +}; +module_usb_driver(rtw_8851bu_driver); + +MODULE_AUTHOR("Bitterblue Smith <rtl8821cerfe2@gmail.com>"); +MODULE_DESCRIPTION("Realtek 802.11ax wireless 8851BU driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sys/contrib/dev/rtw89/rtw8852a.c b/sys/contrib/dev/rtw89/rtw8852a.c index 9bd2842c27d5..3bbe2a808844 100644 --- a/sys/contrib/dev/rtw89/rtw8852a.c +++ b/sys/contrib/dev/rtw89/rtw8852a.c @@ -522,10 +522,17 @@ static const struct rtw89_edcca_regs rtw8852a_edcca_regs = {  	.edcca_p_mask			= B_EDCCA_LVL_MSK1,  	.ppdu_level			= R_SEG0R_EDCCA_LVL,  	.ppdu_mask			= B_EDCCA_LVL_MSK3, -	.rpt_a				= R_EDCCA_RPT_A, -	.rpt_b				= R_EDCCA_RPT_B, -	.rpt_sel			= R_EDCCA_RPT_SEL, -	.rpt_sel_mask			= B_EDCCA_RPT_SEL_MSK, +	.p = {{ +		.rpt_a			= R_EDCCA_RPT_A, +		.rpt_b			= R_EDCCA_RPT_B, +		.rpt_sel		= R_EDCCA_RPT_SEL, +		.rpt_sel_mask		= B_EDCCA_RPT_SEL_MSK, +	}, { +		.rpt_a			= R_EDCCA_RPT_P1_A, +		.rpt_b			= R_EDCCA_RPT_P1_B, +		.rpt_sel		= R_EDCCA_RPT_SEL, +		.rpt_sel_mask		= B_EDCCA_RPT_SEL_P1_MSK, +	}},  	.tx_collision_t2r_st		= R_TX_COLLISION_T2R_ST,  	.tx_collision_t2r_st_mask	= B_TX_COLLISION_T2R_ST_M,  }; @@ -1356,10 +1363,16 @@ static void rtw8852a_rfk_channel(struct rtw89_dev *rtwdev,  	enum rtw89_chanctx_idx chanctx_idx = rtwvif_link->chanctx_idx;  	enum rtw89_phy_idx phy_idx = rtwvif_link->phy_idx; +	rtw89_btc_ntfy_conn_rfk(rtwdev, true); +  	rtw8852a_rx_dck(rtwdev, phy_idx, true, chanctx_idx);  	rtw8852a_iqk(rtwdev, phy_idx, chanctx_idx); +	rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30);  	rtw8852a_tssi(rtwdev, phy_idx, chanctx_idx); +	rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30);  	rtw8852a_dpk(rtwdev, phy_idx, chanctx_idx); + +	rtw89_btc_ntfy_conn_rfk(rtwdev, false);  }  static void rtw8852a_rfk_band_changed(struct rtw89_dev *rtwdev, @@ -2115,6 +2128,7 @@ static const struct rtw89_chip_ops rtw8852a_chip_ops = {  	.set_txpwr_ctrl		= rtw8852a_set_txpwr_ctrl,  	.init_txpwr_unit	= rtw8852a_init_txpwr_unit,  	.get_thermal		= rtw8852a_get_thermal, +	.chan_to_rf18_val	= NULL,  	.ctrl_btg_bt_rx		= rtw8852a_ctrl_btg_bt_rx,  	.query_ppdu		= rtw8852a_query_ppdu,  	.convert_rpl_to_rssi	= NULL, @@ -2136,6 +2150,8 @@ static const struct rtw89_chip_ops rtw8852a_chip_ops = {  	.h2c_default_cmac_tbl	= rtw89_fw_h2c_default_cmac_tbl,  	.h2c_assoc_cmac_tbl	= rtw89_fw_h2c_assoc_cmac_tbl,  	.h2c_ampdu_cmac_tbl	= NULL, +	.h2c_txtime_cmac_tbl	= rtw89_fw_h2c_txtime_cmac_tbl, +	.h2c_punctured_cmac_tbl	= NULL,  	.h2c_default_dmac_tbl	= NULL,  	.h2c_update_beacon	= rtw89_fw_h2c_update_beacon,  	.h2c_ba_cam		= rtw89_fw_h2c_ba_cam, @@ -2162,14 +2178,15 @@ const struct rtw89_chip_info rtw8852a_chip_info = {  	.try_ce_fw		= false,  	.bbmcu_nr		= 0,  	.needed_fw_elms		= 0, +	.fw_blacklist		= NULL,  	.fifo_size		= 458752,  	.small_fifo_size	= false,  	.dle_scc_rsvd_size	= 0,  	.max_amsdu_limit	= 3500,  	.dis_2g_40m_ul_ofdma	= true,  	.rsvd_ple_ofst		= 0x6f800, -	.hfc_param_ini		= rtw8852a_hfc_param_ini_pcie, -	.dle_mem		= rtw8852a_dle_mem_pcie, +	.hfc_param_ini		= {rtw8852a_hfc_param_ini_pcie, NULL, NULL}, +	.dle_mem		= {rtw8852a_dle_mem_pcie, NULL, NULL, NULL},  	.wde_qempty_acq_grpnum	= 16,  	.wde_qempty_mgq_grpsel	= 16,  	.rf_base_addr		= {0xc000, 0xd000}, @@ -2201,10 +2218,15 @@ const struct rtw89_chip_info rtw8852a_chip_info = {  				  BIT(NL80211_CHAN_WIDTH_80),  	.support_unii4		= false,  	.support_ant_gain	= false, +	.support_tas		= false, +	.support_sar_by_ant	= false,  	.ul_tb_waveform_ctrl	= false,  	.ul_tb_pwr_diff		= false, +	.rx_freq_frome_ie	= true,  	.hw_sec_hdr		= false,  	.hw_mgmt_tx_encrypt	= false, +	.hw_tkip_crypto		= false, +	.hw_mlo_bmc_crypto	= false,  	.rf_path_num		= 2,  	.tx_nss			= 2,  	.rx_nss			= 2, @@ -2226,7 +2248,6 @@ const struct rtw89_chip_info rtw8852a_chip_info = {  	.phycap_size		= 128,  	.para_ver		= 0x0,  	.wlcx_desired		= 0x06000000, -	.btcx_desired		= 0x7,  	.scbd			= 0x1,  	.mailbox		= 0x1, diff --git a/sys/contrib/dev/rtw89/rtw8852ae.c b/sys/contrib/dev/rtw89/rtw8852ae.c index 79b9450432e7..cc6ae04e7bab 100644 --- a/sys/contrib/dev/rtw89/rtw8852ae.c +++ b/sys/contrib/dev/rtw89/rtw8852ae.c @@ -91,6 +91,7 @@ static struct pci_driver rtw89_8852ae_driver = {  	.probe		= rtw89_pci_probe,  	.remove		= rtw89_pci_remove,  	.driver.pm	= &rtw89_pm_ops, +	.err_handler    = &rtw89_pci_err_handler,  #if defined(__FreeBSD__)  	.bsddriver.name	= KBUILD_MODNAME,  #endif diff --git a/sys/contrib/dev/rtw89/rtw8852b.c b/sys/contrib/dev/rtw89/rtw8852b.c index dfb2bf61b0b8..7ede07f7b1eb 100644 --- a/sys/contrib/dev/rtw89/rtw8852b.c +++ b/sys/contrib/dev/rtw89/rtw8852b.c @@ -49,6 +49,48 @@ static const struct rtw89_hfc_param_ini rtw8852b_hfc_param_ini_pcie[] = {  	[RTW89_QTA_INVALID] = {NULL},  }; +static const struct rtw89_hfc_ch_cfg rtw8852b_hfc_chcfg_usb[] = { +	{18, 152, grp_0}, /* ACH 0 */ +	{18, 152, grp_0}, /* ACH 1 */ +	{18, 152, grp_0}, /* ACH 2 */ +	{18, 152, grp_0}, /* ACH 3 */ +	{0, 0, grp_0}, /* ACH 4 */ +	{0, 0, grp_0}, /* ACH 5 */ +	{0, 0, grp_0}, /* ACH 6 */ +	{0, 0, grp_0}, /* ACH 7 */ +	{18, 152, grp_0}, /* B0MGQ */ +	{18, 152, grp_0}, /* B0HIQ */ +	{0, 0, grp_0}, /* B1MGQ */ +	{0, 0, grp_0}, /* B1HIQ */ +	{0, 0, 0} /* FWCMDQ */ +}; + +static const struct rtw89_hfc_pub_cfg rtw8852b_hfc_pubcfg_usb = { +	152, /* Group 0 */ +	0, /* Group 1 */ +	152, /* Public Max */ +	0 /* WP threshold */ +}; + +static const struct rtw89_hfc_prec_cfg rtw8852b_hfc_preccfg_usb = { +	9, /* CH 0-11 pre-cost */ +	32, /* H2C pre-cost */ +	64, /* WP CH 0-7 pre-cost */ +	24, /* WP CH 8-11 pre-cost */ +	1, /* CH 0-11 full condition */ +	1, /* H2C full condition */ +	1, /* WP CH 0-7 full condition */ +	1, /* WP CH 8-11 full condition */ +}; + +static const struct rtw89_hfc_param_ini rtw8852b_hfc_param_ini_usb[] = { +	[RTW89_QTA_SCC] = {rtw8852b_hfc_chcfg_usb, &rtw8852b_hfc_pubcfg_usb, +			   &rtw8852b_hfc_preccfg_usb, RTW89_HCIFC_STF}, +	[RTW89_QTA_DLFW] = {NULL, NULL, +			    &rtw8852b_hfc_preccfg_usb, RTW89_HCIFC_STF}, +	[RTW89_QTA_INVALID] = {NULL}, +}; +  static const struct rtw89_dle_mem rtw8852b_dle_mem_pcie[] = {  	[RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size7,  			   &rtw89_mac_size.ple_size6, &rtw89_mac_size.wde_qt7, @@ -66,6 +108,19 @@ static const struct rtw89_dle_mem rtw8852b_dle_mem_pcie[] = {  			       NULL},  }; +static const struct rtw89_dle_mem rtw8852b_dle_mem_usb3[] = { +	[RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size25, +			   &rtw89_mac_size.ple_size33, &rtw89_mac_size.wde_qt25, +			   &rtw89_mac_size.wde_qt25, &rtw89_mac_size.ple_qt74, +			   &rtw89_mac_size.ple_qt75}, +	[RTW89_QTA_DLFW] = {RTW89_QTA_DLFW, &rtw89_mac_size.wde_size9, +			    &rtw89_mac_size.ple_size8, &rtw89_mac_size.wde_qt4, +			    &rtw89_mac_size.wde_qt4, &rtw89_mac_size.ple_qt13, +			    &rtw89_mac_size.ple_qt13}, +	[RTW89_QTA_INVALID] = {RTW89_QTA_INVALID, NULL, NULL, NULL, NULL, NULL, +			       NULL}, +}; +  static const u32 rtw8852b_h2c_regs[RTW89_H2CREG_MAX] = {  	R_AX_H2CREG_DATA0, R_AX_H2CREG_DATA1,  R_AX_H2CREG_DATA2,  	R_AX_H2CREG_DATA3 @@ -189,10 +244,17 @@ static const struct rtw89_edcca_regs rtw8852b_edcca_regs = {  	.edcca_p_mask			= B_EDCCA_LVL_MSK1,  	.ppdu_level			= R_SEG0R_EDCCA_LVL_V1,  	.ppdu_mask			= B_EDCCA_LVL_MSK3, -	.rpt_a				= R_EDCCA_RPT_A, -	.rpt_b				= R_EDCCA_RPT_B, -	.rpt_sel			= R_EDCCA_RPT_SEL, -	.rpt_sel_mask			= B_EDCCA_RPT_SEL_MSK, +	.p = {{ +		.rpt_a			= R_EDCCA_RPT_A, +		.rpt_b			= R_EDCCA_RPT_B, +		.rpt_sel		= R_EDCCA_RPT_SEL, +		.rpt_sel_mask		= B_EDCCA_RPT_SEL_MSK, +	}, { +		.rpt_a			= R_EDCCA_RPT_P1_A, +		.rpt_b			= R_EDCCA_RPT_P1_B, +		.rpt_sel		= R_EDCCA_RPT_SEL, +		.rpt_sel_mask		= B_EDCCA_RPT_SEL_P1_MSK, +	}},  	.tx_collision_t2r_st		= R_TX_COLLISION_T2R_ST,  	.tx_collision_t2r_st_mask	= B_TX_COLLISION_T2R_ST_M,  }; @@ -292,7 +354,8 @@ static int rtw8852b_pwr_on_func(struct rtw89_dev *rtwdev)  	rtw89_write8_clr(rtwdev, R_AX_PLATFORM_ENABLE, B_AX_PLATFORM_EN);  	rtw89_write8_set(rtwdev, R_AX_PLATFORM_ENABLE, B_AX_PLATFORM_EN); -	rtw89_write32_clr(rtwdev, R_AX_SYS_SDIO_CTRL, B_AX_PCIE_CALIB_EN_V1); +	if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) +		rtw89_write32_clr(rtwdev, R_AX_SYS_SDIO_CTRL, B_AX_PCIE_CALIB_EN_V1);  	rtw89_write32_set(rtwdev, R_AX_SYS_ADIE_PAD_PWR_CTRL, B_AX_SYM_PADPDN_WL_PTA_1P3); @@ -354,7 +417,7 @@ static int rtw8852b_pwr_on_func(struct rtw89_dev *rtwdev)  	rtw89_write32_mask(rtwdev, R_AX_SPS_DIG_ON_CTRL0, B_AX_VOL_L1_MASK, 0x9);  	rtw89_write32_mask(rtwdev, R_AX_SPS_DIG_ON_CTRL0, B_AX_VREFPFM_L_MASK, 0xA); -	if (rtwdev->hal.cv == CHIP_CBV) { +	if (rtwdev->hal.cv == CHIP_CBV && rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) {  		rtw89_write32_set(rtwdev, R_AX_PMC_DBG_CTRL2, B_AX_SYSON_DIS_PMCR_AX_WRMSK);  		rtw89_write16_mask(rtwdev, R_AX_HCI_LDO_CTRL, B_AX_R_AX_VADJ_MASK, 0xA);  		rtw89_write32_clr(rtwdev, R_AX_PMC_DBG_CTRL2, B_AX_SYSON_DIS_PMCR_AX_WRMSK); @@ -436,10 +499,22 @@ static int rtw8852b_pwr_off_func(struct rtw89_dev *rtwdev)  	if (ret)  		return ret; -	rtw89_write32(rtwdev, R_AX_WLLPS_CTRL, SW_LPS_OPTION); +	if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) +		rtw89_write32(rtwdev, R_AX_WLLPS_CTRL, SW_LPS_OPTION); +	else if (rtwdev->hci.type == RTW89_HCI_TYPE_USB) +		rtw89_write32_clr(rtwdev, R_AX_SYS_PW_CTRL, B_AX_SOP_EDSWR); +  	rtw89_write32_set(rtwdev, R_AX_SYS_SWR_CTRL1, B_AX_SYM_CTRL_SPS_PWMFREQ);  	rtw89_write32_mask(rtwdev, R_AX_SPS_DIG_ON_CTRL0, B_AX_REG_ZCDC_H_MASK, 0x3); -	rtw89_write32_set(rtwdev, R_AX_SYS_PW_CTRL, B_AX_APFM_SWLPS); + +	if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) { +		rtw89_write32_set(rtwdev, R_AX_SYS_PW_CTRL, B_AX_APFM_SWLPS); +	} else if (rtwdev->hci.type == RTW89_HCI_TYPE_USB) { +		val32 = rtw89_read32(rtwdev, R_AX_SYS_PW_CTRL); +		val32 &= ~B_AX_AFSM_PCIE_SUS_EN; +		val32 |= B_AX_AFSM_WLSUS_EN; +		rtw89_write32(rtwdev, R_AX_SYS_PW_CTRL, val32); +	}  	return 0;  } @@ -553,8 +628,11 @@ static void rtw8852b_set_channel_help(struct rtw89_dev *rtwdev, bool enter,  static void rtw8852b_rfk_init(struct rtw89_dev *rtwdev)  { +	struct rtw89_rfk_mcc_info *rfk_mcc = &rtwdev->rfk_mcc; +  	rtwdev->is_tssi_mode[RF_PATH_A] = false;  	rtwdev->is_tssi_mode[RF_PATH_B] = false; +	memset(rfk_mcc, 0, sizeof(*rfk_mcc));  	rtw8852b_dpk_init(rtwdev);  	rtw8852b_rck(rtwdev); @@ -568,10 +646,18 @@ static void rtw8852b_rfk_channel(struct rtw89_dev *rtwdev,  	enum rtw89_chanctx_idx chanctx_idx = rtwvif_link->chanctx_idx;  	enum rtw89_phy_idx phy_idx = rtwvif_link->phy_idx; +	rtw8852b_mcc_get_ch_info(rtwdev, phy_idx); +	rtw89_btc_ntfy_conn_rfk(rtwdev, true); +  	rtw8852b_rx_dck(rtwdev, phy_idx, chanctx_idx);  	rtw8852b_iqk(rtwdev, phy_idx, chanctx_idx); +	rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30);  	rtw8852b_tssi(rtwdev, phy_idx, true, chanctx_idx); +	rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30);  	rtw8852b_dpk(rtwdev, phy_idx, chanctx_idx); + +	rtw89_btc_ntfy_conn_rfk(rtwdev, false); +	rtw89_fw_h2c_rf_ntfy_mcc(rtwdev);  }  static void rtw8852b_rfk_band_changed(struct rtw89_dev *rtwdev, @@ -742,6 +828,7 @@ static const struct rtw89_chip_ops rtw8852b_chip_ops = {  	.set_txpwr_ctrl		= rtw8852bx_set_txpwr_ctrl,  	.init_txpwr_unit	= rtw8852bx_init_txpwr_unit,  	.get_thermal		= rtw8852bx_get_thermal, +	.chan_to_rf18_val	= NULL,  	.ctrl_btg_bt_rx		= rtw8852bx_ctrl_btg_bt_rx,  	.query_ppdu		= rtw8852bx_query_ppdu,  	.convert_rpl_to_rssi	= rtw8852bx_convert_rpl_to_rssi, @@ -763,6 +850,8 @@ static const struct rtw89_chip_ops rtw8852b_chip_ops = {  	.h2c_default_cmac_tbl	= rtw89_fw_h2c_default_cmac_tbl,  	.h2c_assoc_cmac_tbl	= rtw89_fw_h2c_assoc_cmac_tbl,  	.h2c_ampdu_cmac_tbl	= NULL, +	.h2c_txtime_cmac_tbl	= rtw89_fw_h2c_txtime_cmac_tbl, +	.h2c_punctured_cmac_tbl	= NULL,  	.h2c_default_dmac_tbl	= NULL,  	.h2c_update_beacon	= rtw89_fw_h2c_update_beacon,  	.h2c_ba_cam		= rtw89_fw_h2c_ba_cam, @@ -778,6 +867,10 @@ static const struct rtw89_chip_ops rtw8852b_chip_ops = {  	.btc_set_policy		= rtw89_btc_set_policy_v1,  }; +static const struct rtw89_chanctx_listener rtw8852b_chanctx_listener = { +	.callbacks[RTW89_CHANCTX_CALLBACK_RFK] = rtw8852b_rfk_chanctx_cb, +}; +  #ifdef CONFIG_PM  static const struct wiphy_wowlan_support rtw_wowlan_stub_8852b = {  	.flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, @@ -798,14 +891,20 @@ const struct rtw89_chip_info rtw8852b_chip_info = {  	.try_ce_fw		= true,  	.bbmcu_nr		= 0,  	.needed_fw_elms		= 0, +	.fw_blacklist		= &rtw89_fw_blacklist_default,  	.fifo_size		= 196608,  	.small_fifo_size	= true,  	.dle_scc_rsvd_size	= 98304,  	.max_amsdu_limit	= 5000,  	.dis_2g_40m_ul_ofdma	= true,  	.rsvd_ple_ofst		= 0x2f800, -	.hfc_param_ini		= rtw8852b_hfc_param_ini_pcie, -	.dle_mem		= rtw8852b_dle_mem_pcie, +	.hfc_param_ini		= {rtw8852b_hfc_param_ini_pcie, +				   rtw8852b_hfc_param_ini_usb, +				   NULL}, +	.dle_mem		= {rtw8852b_dle_mem_pcie, +				   rtw8852b_dle_mem_usb3, +				   rtw8852b_dle_mem_usb3, +				   NULL},  	.wde_qempty_acq_grpnum	= 4,  	.wde_qempty_mgq_grpsel	= 4,  	.rf_base_addr		= {0xe000, 0xf000}, @@ -820,6 +919,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = {  	.nctl_post_table	= NULL,  	.dflt_parms		= &rtw89_8852b_dflt_parms,  	.rfe_parms_conf		= NULL, +	.chanctx_listener	= &rtw8852b_chanctx_listener,  	.txpwr_factor_bb	= 3,  	.txpwr_factor_rf	= 2,  	.txpwr_factor_mac	= 1, @@ -828,7 +928,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = {  	.tssi_dbw_table		= NULL,  	.support_macid_num	= RTW89_MAX_MAC_ID_NUM,  	.support_link_num	= 0, -	.support_chanctx_num	= 0, +	.support_chanctx_num	= 2,  	.support_rnr		= false,  	.support_bands		= BIT(NL80211_BAND_2GHZ) |  				  BIT(NL80211_BAND_5GHZ), @@ -837,10 +937,15 @@ const struct rtw89_chip_info rtw8852b_chip_info = {  				  BIT(NL80211_CHAN_WIDTH_80),  	.support_unii4		= true,  	.support_ant_gain	= true, +	.support_tas		= false, +	.support_sar_by_ant	= true,  	.ul_tb_waveform_ctrl	= true,  	.ul_tb_pwr_diff		= false, +	.rx_freq_frome_ie	= true,  	.hw_sec_hdr		= false,  	.hw_mgmt_tx_encrypt	= false, +	.hw_tkip_crypto		= false, +	.hw_mlo_bmc_crypto	= false,  	.rf_path_num		= 2,  	.tx_nss			= 2,  	.rx_nss			= 2, @@ -862,7 +967,6 @@ const struct rtw89_chip_info rtw8852b_chip_info = {  	.phycap_size		= 128,  	.para_ver		= 0,  	.wlcx_desired		= 0x05050000, -	.btcx_desired		= 0x5,  	.scbd			= 0x1,  	.mailbox		= 0x1, diff --git a/sys/contrib/dev/rtw89/rtw8852b_common.c b/sys/contrib/dev/rtw89/rtw8852b_common.c index 0e094ce9c9b0..3fb2972ae6f6 100644 --- a/sys/contrib/dev/rtw89/rtw8852b_common.c +++ b/sys/contrib/dev/rtw89/rtw8852b_common.c @@ -8,6 +8,7 @@  #include "phy.h"  #include "reg.h"  #include "rtw8852b_common.h" +#include "sar.h"  #include "util.h"  static const struct rtw89_reg3_def rtw8852bx_pmac_ht20_mcs7_tbl[] = { @@ -171,14 +172,6 @@ static const struct rtw89_reg3_def rtw8852bx_btc_preagc_dis_defs[] = {  static DECLARE_PHY_REG3_TBL(rtw8852bx_btc_preagc_dis_defs); -static void rtw8852be_efuse_parsing(struct rtw89_efuse *efuse, -				    struct rtw8852bx_efuse *map) -{ -	ether_addr_copy(efuse->addr, map->e.mac_addr); -	efuse->rfe_type = map->rfe_type; -	efuse->xtal_cap = map->xtal_k; -} -  static void rtw8852bx_efuse_parsing_tssi(struct rtw89_dev *rtwdev,  					 struct rtw8852bx_efuse *map)  { @@ -260,12 +253,18 @@ static int __rtw8852bx_read_efuse(struct rtw89_dev *rtwdev, u8 *log_map,  	switch (rtwdev->hci.type) {  	case RTW89_HCI_TYPE_PCIE: -		rtw8852be_efuse_parsing(efuse, map); +		ether_addr_copy(efuse->addr, map->e.mac_addr); +		break; +	case RTW89_HCI_TYPE_USB: +		ether_addr_copy(efuse->addr, map->u.mac_addr);  		break;  	default:  		return -EOPNOTSUPP;  	} +	efuse->rfe_type = map->rfe_type; +	efuse->xtal_cap = map->xtal_k; +  	rtw89_info(rtwdev, "chip rfe_type is %d\n", efuse->rfe_type);  	return 0; @@ -621,9 +620,9 @@ static void rtw8852bt_ext_loss_avg_update(struct rtw89_dev *rtwdev,  	if (ext_loss_a == ext_loss_b) {  		ext_loss_avg = ext_loss_a;  	} else { -		linear = rtw89_db_2_linear(abs(ext_loss_a - ext_loss_b)) + 1; -		linear = DIV_ROUND_CLOSEST_ULL(linear / 2, 1 << RTW89_LINEAR_FRAC_BITS); -		ext_loss_avg = rtw89_linear_2_db(linear); +		linear = rtw89_db_to_linear(abs(ext_loss_a - ext_loss_b)) + 1; +		linear /= 2; +		ext_loss_avg = rtw89_linear_to_db(linear);  		ext_loss_avg += min(ext_loss_a, ext_loss_b);  	} @@ -1234,6 +1233,7 @@ static u32 rtw8852bx_bb_cal_txpwr_ref(struct rtw89_dev *rtwdev,  	       u32_encode_bits(ref, B_DPD_REF);  } +/* @pwr_ofst (unit: 1/8 dBm): power of path A minus power of path B */  static void rtw8852bx_set_txpwr_ref(struct rtw89_dev *rtwdev,  				    enum rtw89_phy_idx phy_idx, s16 pwr_ofst)  { @@ -1336,6 +1336,27 @@ static void rtw8852bx_set_tx_shape(struct rtw89_dev *rtwdev,  			       tx_shape_ofdm);  } +static s16 rtw8852bx_get_txpwr_sar_diff(struct rtw89_dev *rtwdev, +					const struct rtw89_chan *chan) +{ +	struct rtw89_sar_parm sar_parm = { +		.center_freq = chan->freq, +		.force_path = true, +	}; +	s16 sar_bb_a, sar_bb_b; +	s8 sar_mac; + +	sar_parm.path = RF_PATH_A; +	sar_mac = rtw89_query_sar(rtwdev, &sar_parm); +	sar_bb_a = rtw89_phy_txpwr_mac_to_bb(rtwdev, sar_mac); + +	sar_parm.path = RF_PATH_B; +	sar_mac = rtw89_query_sar(rtwdev, &sar_parm); +	sar_bb_b = rtw89_phy_txpwr_mac_to_bb(rtwdev, sar_mac); + +	return sar_bb_a - sar_bb_b; +} +  static void rtw8852bx_set_txpwr_diff(struct rtw89_dev *rtwdev,  				     const struct rtw89_chan *chan,  				     enum rtw89_phy_idx phy_idx) @@ -1343,6 +1364,7 @@ static void rtw8852bx_set_txpwr_diff(struct rtw89_dev *rtwdev,  	s16 pwr_ofst;  	pwr_ofst = rtw89_phy_ant_gain_pwr_offset(rtwdev, chan); +	pwr_ofst += rtw8852bx_get_txpwr_sar_diff(rtwdev, chan);  	rtw8852bx_set_txpwr_ref(rtwdev, phy_idx, pwr_ofst);  } diff --git a/sys/contrib/dev/rtw89/rtw8852b_rfk.c b/sys/contrib/dev/rtw89/rtw8852b_rfk.c index ef47a5facc83..3e39a04c91e4 100644 --- a/sys/contrib/dev/rtw89/rtw8852b_rfk.c +++ b/sys/contrib/dev/rtw89/rtw8852b_rfk.c @@ -2,6 +2,7 @@  /* Copyright(c) 2019-2022  Realtek Corporation   */ +#include "chan.h"  #include "coex.h"  #include "debug.h"  #include "mac.h" @@ -1145,19 +1146,19 @@ static void _lok_res_table(struct rtw89_dev *rtwdev, u8 path, u8 ibias)  static bool _lok_finetune_check(struct rtw89_dev *rtwdev, u8 path)  { +	struct rtw89_rfk_mcc_info_data *rfk_mcc = rtwdev->rfk_mcc.data;  	struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; +	u8 ch = rfk_mcc->table_idx;  	bool is_fail1, is_fail2;  	u32 vbuff_i;  	u32 vbuff_q;  	u32 core_i;  	u32 core_q;  	u32 tmp; -	u8 ch;  	tmp = rtw89_read_rf(rtwdev, path, RR_TXMO, RFREG_MASK);  	core_i = FIELD_GET(RR_TXMO_COI, tmp);  	core_q = FIELD_GET(RR_TXMO_COQ, tmp); -	ch = (iqk_info->iqk_times / 2) % RTW89_IQK_CHS_NR;  	if (core_i < 0x2 || core_i > 0x1d || core_q < 0x2 || core_q > 0x1d)  		is_fail1 = true; @@ -1386,26 +1387,11 @@ static void _iqk_get_ch_info(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, u  			     enum rtw89_chanctx_idx chanctx_idx)  {  	const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, chanctx_idx); +	struct rtw89_rfk_mcc_info_data *rfk_mcc = rtwdev->rfk_mcc.data;  	struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; +	u8 idx = rfk_mcc->table_idx;  	u32 reg_rf18;  	u32 reg_35c; -	u8 idx; -	u8 get_empty_table = false; - -	for (idx = 0; idx < RTW89_IQK_CHS_NR; idx++) { -		if (iqk_info->iqk_mcc_ch[idx][path] == 0) { -			get_empty_table = true; -			break; -		} -	} -	rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK] (1)idx = %x\n", idx); - -	if (!get_empty_table) { -		idx = iqk_info->iqk_table_idx[path] + 1; -		if (idx > 1) -			idx = 0; -	} -	rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK] (2)idx = %x\n", idx);  	reg_rf18 = rtw89_read_rf(rtwdev, path, RR_CFGCH, RFREG_MASK);  	reg_35c = rtw89_phy_read32_mask(rtwdev, R_CIRST, B_CIRST_SYN); @@ -1506,11 +1492,10 @@ static void _iqk_afebb_restore(struct rtw89_dev *rtwdev,  static void _iqk_preset(struct rtw89_dev *rtwdev, u8 path)  { -	struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; -	u8 idx; +	struct rtw89_rfk_mcc_info_data *rfk_mcc = rtwdev->rfk_mcc.data; +	u8 idx = rfk_mcc->table_idx; -	idx = iqk_info->iqk_table_idx[path]; -	rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK] (3)idx = %x\n", idx); +	rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK] idx = %x\n", idx);  	rtw89_phy_write32_mask(rtwdev, R_COEF_SEL + (path << 8), B_COEF_SEL_IQC, idx);  	rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT + (path << 8), B_CFIR_LUT_G3, idx); @@ -3585,9 +3570,10 @@ static void _tssi_alimentk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy,  	u8 ch_idx = _tssi_ch_to_idx(rtwdev, channel);  	struct rtw8852bx_bb_tssi_bak tssi_bak;  	s32 aliment_diff, tssi_cw_default; -	u32 start_time, finish_time;  	u32 bb_reg_backup[8] = {0}; +	ktime_t start_time;  	const s16 *power; +	s64 this_time;  	u8 band;  	bool ok;  	u32 tmp; @@ -3613,7 +3599,7 @@ static void _tssi_alimentk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy,  		return;  	} -	start_time = ktime_get_ns(); +	start_time = ktime_get();  	if (chan->band_type == RTW89_BAND_2G)  		power = power_2g; @@ -3738,12 +3724,17 @@ out:  	rtw8852bx_bb_restore_tssi(rtwdev, phy, &tssi_bak);  	rtw8852bx_bb_tx_mode_switch(rtwdev, phy, 0); -	finish_time = ktime_get_ns(); -	tssi_info->tssi_alimk_time += finish_time - start_time; +	this_time = ktime_us_delta(ktime_get(), start_time); +	tssi_info->tssi_alimk_time += this_time;  	rtw89_debug(rtwdev, RTW89_DBG_RFK, -		    "[TSSI PA K] %s processing time = %d ms\n", __func__, -		    tssi_info->tssi_alimk_time); +#if defined(__linux__) +		    "[TSSI PA K] %s processing time = %lld us (acc = %llu us)\n", +		    __func__, this_time, tssi_info->tssi_alimk_time); +#elif defined(__FreeBSD__) +		    "[TSSI PA K] %s processing time = %jd us (acc = %ju us)\n", +		    __func__, (intmax_t)this_time, (uintmax_t)tssi_info->tssi_alimk_time); +#endif  }  void rtw8852b_dpk_init(struct rtw89_dev *rtwdev) @@ -4178,3 +4169,49 @@ void rtw8852b_set_channel_rf(struct rtw89_dev *rtwdev,  	rtw8852b_ctrl_bw_ch(rtwdev, phy_idx, chan->channel, chan->band_type,  			    chan->band_width);  } + +void rtw8852b_mcc_get_ch_info(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) +{ +	const struct rtw89_chan *chan = rtw89_mgnt_chan_get(rtwdev, 0); +	struct rtw89_rfk_mcc_info_data *rfk_mcc = rtwdev->rfk_mcc.data; +	struct rtw89_rfk_chan_desc desc[__RTW89_RFK_CHS_NR_V0] = {}; +	u8 idx; + +	for (idx = 0; idx < ARRAY_SIZE(desc); idx++) { +		struct rtw89_rfk_chan_desc *p = &desc[idx]; + +		p->ch = rfk_mcc->ch[idx]; + +		p->has_band = true; +		p->band = rfk_mcc->band[idx]; +	} + +	idx = rtw89_rfk_chan_lookup(rtwdev, desc, ARRAY_SIZE(desc), chan); + +	rfk_mcc->ch[idx] = chan->channel; +	rfk_mcc->band[idx] = chan->band_type; +	rfk_mcc->table_idx = idx; +} + +void rtw8852b_rfk_chanctx_cb(struct rtw89_dev *rtwdev, +			     enum rtw89_chanctx_state state) +{ +	struct rtw89_dpk_info *dpk = &rtwdev->dpk; +	u8 path; + +	switch (state) { +	case RTW89_CHANCTX_STATE_MCC_START: +		dpk->is_dpk_enable = false; +		for (path = 0; path < RTW8852B_DPK_RF_PATH; path++) +			_dpk_onoff(rtwdev, path, false); +		break; +	case RTW89_CHANCTX_STATE_MCC_STOP: +		dpk->is_dpk_enable = true; +		for (path = 0; path < RTW8852B_DPK_RF_PATH; path++) +			_dpk_onoff(rtwdev, path, false); +		rtw8852b_dpk(rtwdev, RTW89_PHY_0, RTW89_CHANCTX_0); +		break; +	default: +		break; +	} +} diff --git a/sys/contrib/dev/rtw89/rtw8852b_rfk.h b/sys/contrib/dev/rtw89/rtw8852b_rfk.h index c31ba446e6e0..5fae980d5e2c 100644 --- a/sys/contrib/dev/rtw89/rtw8852b_rfk.h +++ b/sys/contrib/dev/rtw89/rtw8852b_rfk.h @@ -27,5 +27,8 @@ void rtw8852b_wifi_scan_notify(struct rtw89_dev *rtwdev, bool scan_start,  void rtw8852b_set_channel_rf(struct rtw89_dev *rtwdev,  			     const struct rtw89_chan *chan,  			     enum rtw89_phy_idx phy_idx); +void rtw8852b_mcc_get_ch_info(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); +void rtw8852b_rfk_chanctx_cb(struct rtw89_dev *rtwdev, +			     enum rtw89_chanctx_state state);  #endif diff --git a/sys/contrib/dev/rtw89/rtw8852be.c b/sys/contrib/dev/rtw89/rtw8852be.c index b3b5b9b94f1a..832c0ee008a2 100644 --- a/sys/contrib/dev/rtw89/rtw8852be.c +++ b/sys/contrib/dev/rtw89/rtw8852be.c @@ -93,6 +93,7 @@ static struct pci_driver rtw89_8852be_driver = {  	.probe		= rtw89_pci_probe,  	.remove		= rtw89_pci_remove,  	.driver.pm	= &rtw89_pm_ops, +	.err_handler    = &rtw89_pci_err_handler,  #if defined(__FreeBSD__)  	.bsddriver.name	= KBUILD_MODNAME,  #endif diff --git a/sys/contrib/dev/rtw89/rtw8852bt.c b/sys/contrib/dev/rtw89/rtw8852bt.c index bde3e1fb7ca6..9427823aca2f 100644 --- a/sys/contrib/dev/rtw89/rtw8852bt.c +++ b/sys/contrib/dev/rtw89/rtw8852bt.c @@ -187,10 +187,17 @@ static const struct rtw89_edcca_regs rtw8852bt_edcca_regs = {  	.edcca_p_mask			= B_EDCCA_LVL_MSK1,  	.ppdu_level			= R_SEG0R_EDCCA_LVL_V1,  	.ppdu_mask			= B_EDCCA_LVL_MSK3, -	.rpt_a				= R_EDCCA_RPT_A, -	.rpt_b				= R_EDCCA_RPT_B, -	.rpt_sel			= R_EDCCA_RPT_SEL, -	.rpt_sel_mask			= B_EDCCA_RPT_SEL_MSK, +	.p = {{ +		.rpt_a			= R_EDCCA_RPT_A, +		.rpt_b			= R_EDCCA_RPT_B, +		.rpt_sel		= R_EDCCA_RPT_SEL, +		.rpt_sel_mask		= B_EDCCA_RPT_SEL_MSK, +	}, { +		.rpt_a			= R_EDCCA_RPT_P1_A, +		.rpt_b			= R_EDCCA_RPT_P1_B, +		.rpt_sel		= R_EDCCA_RPT_SEL, +		.rpt_sel_mask		= B_EDCCA_RPT_SEL_P1_MSK, +	}},  	.tx_collision_t2r_st		= R_TX_COLLISION_T2R_ST,  	.tx_collision_t2r_st_mask	= B_TX_COLLISION_T2R_ST_M,  }; @@ -526,8 +533,11 @@ static void rtw8852bt_set_channel_help(struct rtw89_dev *rtwdev, bool enter,  static void rtw8852bt_rfk_init(struct rtw89_dev *rtwdev)  { +	struct rtw89_rfk_mcc_info *rfk_mcc = &rtwdev->rfk_mcc; +  	rtwdev->is_tssi_mode[RF_PATH_A] = false;  	rtwdev->is_tssi_mode[RF_PATH_B] = false; +	memset(rfk_mcc, 0, sizeof(*rfk_mcc));  	rtw8852bt_dpk_init(rtwdev);  	rtw8852bt_rck(rtwdev); @@ -541,10 +551,18 @@ static void rtw8852bt_rfk_channel(struct rtw89_dev *rtwdev,  	enum rtw89_chanctx_idx chanctx_idx = rtwvif_link->chanctx_idx;  	enum rtw89_phy_idx phy_idx = rtwvif_link->phy_idx; +	rtw8852bt_mcc_get_ch_info(rtwdev, phy_idx); +	rtw89_btc_ntfy_conn_rfk(rtwdev, true); +  	rtw8852bt_rx_dck(rtwdev, phy_idx, chanctx_idx);  	rtw8852bt_iqk(rtwdev, phy_idx, chanctx_idx); +	rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30);  	rtw8852bt_tssi(rtwdev, phy_idx, true, chanctx_idx); +	rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30);  	rtw8852bt_dpk(rtwdev, phy_idx, chanctx_idx); + +	rtw89_btc_ntfy_conn_rfk(rtwdev, false); +	rtw89_fw_h2c_rf_ntfy_mcc(rtwdev);  }  static void rtw8852bt_rfk_band_changed(struct rtw89_dev *rtwdev, @@ -676,6 +694,7 @@ static const struct rtw89_chip_ops rtw8852bt_chip_ops = {  	.set_txpwr_ctrl		= rtw8852bx_set_txpwr_ctrl,  	.init_txpwr_unit	= rtw8852bx_init_txpwr_unit,  	.get_thermal		= rtw8852bx_get_thermal, +	.chan_to_rf18_val	= NULL,  	.ctrl_btg_bt_rx		= rtw8852bx_ctrl_btg_bt_rx,  	.query_ppdu		= rtw8852bx_query_ppdu,  	.convert_rpl_to_rssi	= rtw8852bx_convert_rpl_to_rssi, @@ -697,6 +716,8 @@ static const struct rtw89_chip_ops rtw8852bt_chip_ops = {  	.h2c_default_cmac_tbl	= rtw89_fw_h2c_default_cmac_tbl,  	.h2c_assoc_cmac_tbl	= rtw89_fw_h2c_assoc_cmac_tbl,  	.h2c_ampdu_cmac_tbl	= NULL, +	.h2c_txtime_cmac_tbl	= rtw89_fw_h2c_txtime_cmac_tbl, +	.h2c_punctured_cmac_tbl	= NULL,  	.h2c_default_dmac_tbl	= NULL,  	.h2c_update_beacon	= rtw89_fw_h2c_update_beacon,  	.h2c_ba_cam		= rtw89_fw_h2c_ba_cam, @@ -712,6 +733,10 @@ static const struct rtw89_chip_ops rtw8852bt_chip_ops = {  	.btc_set_policy		= rtw89_btc_set_policy_v1,  }; +static const struct rtw89_chanctx_listener rtw8852bt_chanctx_listener = { +	.callbacks[RTW89_CHANCTX_CALLBACK_RFK] = rtw8852bt_rfk_chanctx_cb, +}; +  #ifdef CONFIG_PM  static const struct wiphy_wowlan_support rtw_wowlan_stub_8852bt = {  	.flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, @@ -732,14 +757,15 @@ const struct rtw89_chip_info rtw8852bt_chip_info = {  	.try_ce_fw		= true,  	.bbmcu_nr		= 0,  	.needed_fw_elms		= RTW89_AX_GEN_DEF_NEEDED_FW_ELEMENTS_NO_6GHZ, +	.fw_blacklist		= &rtw89_fw_blacklist_default,  	.fifo_size		= 458752,  	.small_fifo_size	= true,  	.dle_scc_rsvd_size	= 98304,  	.max_amsdu_limit	= 5000,  	.dis_2g_40m_ul_ofdma	= true,  	.rsvd_ple_ofst		= 0x6f800, -	.hfc_param_ini		= rtw8852bt_hfc_param_ini_pcie, -	.dle_mem		= rtw8852bt_dle_mem_pcie, +	.hfc_param_ini		= {rtw8852bt_hfc_param_ini_pcie, NULL, NULL}, +	.dle_mem		= {rtw8852bt_dle_mem_pcie, NULL, NULL, NULL},  	.wde_qempty_acq_grpnum	= 4,  	.wde_qempty_mgq_grpsel	= 4,  	.rf_base_addr		= {0xe000, 0xf000}, @@ -753,6 +779,7 @@ const struct rtw89_chip_info rtw8852bt_chip_info = {  	.nctl_post_table	= NULL,  	.dflt_parms		= NULL,  	.rfe_parms_conf		= NULL, +	.chanctx_listener	= &rtw8852bt_chanctx_listener,  	.txpwr_factor_bb	= 3,  	.txpwr_factor_rf	= 2,  	.txpwr_factor_mac	= 1, @@ -761,7 +788,7 @@ const struct rtw89_chip_info rtw8852bt_chip_info = {  	.tssi_dbw_table		= NULL,  	.support_macid_num	= RTW89_MAX_MAC_ID_NUM,  	.support_link_num       = 0, -	.support_chanctx_num	= 1, +	.support_chanctx_num	= 2,  	.support_rnr		= false,  	.support_bands		= BIT(NL80211_BAND_2GHZ) |  				  BIT(NL80211_BAND_5GHZ), @@ -770,10 +797,15 @@ const struct rtw89_chip_info rtw8852bt_chip_info = {  				  BIT(NL80211_CHAN_WIDTH_80),  	.support_unii4		= true,  	.support_ant_gain	= true, +	.support_tas		= false, +	.support_sar_by_ant	= true,  	.ul_tb_waveform_ctrl	= true,  	.ul_tb_pwr_diff		= false, +	.rx_freq_frome_ie	= true,  	.hw_sec_hdr		= false,  	.hw_mgmt_tx_encrypt     = false, +	.hw_tkip_crypto		= true, +	.hw_mlo_bmc_crypto	= false,  	.rf_path_num		= 2,  	.tx_nss			= 2,  	.rx_nss			= 2, @@ -795,7 +827,6 @@ const struct rtw89_chip_info rtw8852bt_chip_info = {  	.phycap_size		= 128,  	.para_ver		= 0,  	.wlcx_desired		= 0x070e0000, -	.btcx_desired		= 0x7,  	.scbd			= 0x1,  	.mailbox		= 0x1, diff --git a/sys/contrib/dev/rtw89/rtw8852bt_rfk.c b/sys/contrib/dev/rtw89/rtw8852bt_rfk.c index 336a83e1d46b..fffa1dbb45b1 100644 --- a/sys/contrib/dev/rtw89/rtw8852bt_rfk.c +++ b/sys/contrib/dev/rtw89/rtw8852bt_rfk.c @@ -2,6 +2,7 @@  /* Copyright(c) 2024 Realtek Corporation   */ +#include "chan.h"  #include "coex.h"  #include "debug.h"  #include "fw.h" @@ -1529,26 +1530,11 @@ static void _iqk_get_ch_info(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, u  			     enum rtw89_chanctx_idx chanctx_idx)  {  	const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, chanctx_idx); +	struct rtw89_rfk_mcc_info_data *rfk_mcc = rtwdev->rfk_mcc.data;  	struct rtw89_iqk_info *iqk_info = &rtwdev->iqk; -	u8 get_empty_table = false; +	u8 idx = rfk_mcc->table_idx;  	u32 reg_rf18;  	u32 reg_35c; -	u8 idx; - -	for (idx = 0; idx < RTW89_IQK_CHS_NR; idx++) { -		if (iqk_info->iqk_mcc_ch[idx][path] == 0) { -			get_empty_table = true; -			break; -		} -	} -	rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK] (1)idx = %x\n", idx); - -	if (!get_empty_table) { -		idx = iqk_info->iqk_table_idx[path] + 1; -		if (idx > 1) -			idx = 0; -	} -	rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK] (2)idx = %x\n", idx);  	reg_rf18 = rtw89_read_rf(rtwdev, path, RR_CFGCH, RFREG_MASK);  	reg_35c = rtw89_phy_read32_mask(rtwdev, R_CIRST, B_CIRST_SYN); @@ -1640,7 +1626,8 @@ static void _iqk_afebb_restore(struct rtw89_dev *rtwdev,  static void _iqk_preset(struct rtw89_dev *rtwdev, u8 path)  { -	u8 idx = 0; +	struct rtw89_rfk_mcc_info_data *rfk_mcc = rtwdev->rfk_mcc.data; +	u8 idx = rfk_mcc->table_idx;  	rtw89_phy_write32_mask(rtwdev, R_COEF_SEL + (path << 8), 0x00000001, idx);  	rtw89_phy_write32_mask(rtwdev, R_CFIR_LUT + (path << 8), 0x00000008, idx); @@ -3663,9 +3650,10 @@ static void _tssi_alimentk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy,  	u8 ch_idx = _tssi_ch_to_idx(rtwdev, channel);  	struct rtw8852bx_bb_tssi_bak tssi_bak;  	s32 aliment_diff, tssi_cw_default; -	u32 start_time, finish_time;  	u32 bb_reg_backup[8] = {}; +	ktime_t start_time;  	const s16 *power; +	s64 this_time;  	u8 band;  	bool ok;  	u32 tmp; @@ -3675,7 +3663,7 @@ static void _tssi_alimentk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy,  		    "======> %s   channel=%d   path=%d\n", __func__, channel,  		    path); -	start_time = ktime_get_ns(); +	start_time = ktime_get();  	if (chan->band_type == RTW89_BAND_2G)  		power = power_2g; @@ -3802,12 +3790,17 @@ out:  	rtw8852bx_bb_restore_tssi(rtwdev, phy, &tssi_bak);  	rtw8852bx_bb_tx_mode_switch(rtwdev, phy, 0); -	finish_time = ktime_get_ns(); -	tssi_info->tssi_alimk_time += finish_time - start_time; +	this_time = ktime_us_delta(ktime_get(), start_time); +	tssi_info->tssi_alimk_time += this_time;  	rtw89_debug(rtwdev, RTW89_DBG_RFK, -		    "[TSSI PA K] %s processing time = %d ms\n", __func__, -		    tssi_info->tssi_alimk_time); +#if defined(__linux__) +		    "[TSSI PA K] %s processing time = %lld us (acc = %llu us)\n", +		    __func__, this_time, tssi_info->tssi_alimk_time); +#elif defined(__FreeBSD__) +		    "[TSSI PA K] %s processing time = %jd us (acc = %ju us)\n", +		    __func__, (intmax_t)this_time, (uintmax_t)tssi_info->tssi_alimk_time); +#endif  }  void rtw8852bt_dpk_init(struct rtw89_dev *rtwdev) @@ -4251,3 +4244,49 @@ void rtw8852bt_set_channel_rf(struct rtw89_dev *rtwdev,  	rtw8852bt_ctrl_bw_ch(rtwdev, phy_idx, chan->channel, chan->band_type,  			     chan->band_width);  } + +void rtw8852bt_mcc_get_ch_info(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx) +{ +	const struct rtw89_chan *chan = rtw89_mgnt_chan_get(rtwdev, 0); +	struct rtw89_rfk_mcc_info_data *rfk_mcc = rtwdev->rfk_mcc.data; +	struct rtw89_rfk_chan_desc desc[__RTW89_RFK_CHS_NR_V0] = {}; +	u8 idx; + +	for (idx = 0; idx < ARRAY_SIZE(desc); idx++) { +		struct rtw89_rfk_chan_desc *p = &desc[idx]; + +		p->ch = rfk_mcc->ch[idx]; + +		p->has_band = true; +		p->band = rfk_mcc->band[idx]; +	} + +	idx = rtw89_rfk_chan_lookup(rtwdev, desc, ARRAY_SIZE(desc), chan); + +	rfk_mcc->ch[idx] = chan->channel; +	rfk_mcc->band[idx] = chan->band_type; +	rfk_mcc->table_idx = idx; +} + +void rtw8852bt_rfk_chanctx_cb(struct rtw89_dev *rtwdev, +			      enum rtw89_chanctx_state state) +{ +	struct rtw89_dpk_info *dpk = &rtwdev->dpk; +	u8 path; + +	switch (state) { +	case RTW89_CHANCTX_STATE_MCC_START: +		dpk->is_dpk_enable = false; +		for (path = 0; path < RTW8852BT_SS; path++) +			_dpk_onoff(rtwdev, path, false); +		break; +	case RTW89_CHANCTX_STATE_MCC_STOP: +		dpk->is_dpk_enable = true; +		for (path = 0; path < RTW8852BT_SS; path++) +			_dpk_onoff(rtwdev, path, false); +		rtw8852bt_dpk(rtwdev, RTW89_PHY_0, RTW89_CHANCTX_0); +		break; +	default: +		break; +	} +} diff --git a/sys/contrib/dev/rtw89/rtw8852bt_rfk.h b/sys/contrib/dev/rtw89/rtw8852bt_rfk.h index e34560b4905f..a663bbda4075 100644 --- a/sys/contrib/dev/rtw89/rtw8852bt_rfk.h +++ b/sys/contrib/dev/rtw89/rtw8852bt_rfk.h @@ -27,5 +27,8 @@ void rtw8852bt_wifi_scan_notify(struct rtw89_dev *rtwdev, bool scan_start,  void rtw8852bt_set_channel_rf(struct rtw89_dev *rtwdev,  			      const struct rtw89_chan *chan,  			      enum rtw89_phy_idx phy_idx); +void rtw8852bt_mcc_get_ch_info(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); +void rtw8852bt_rfk_chanctx_cb(struct rtw89_dev *rtwdev, +			      enum rtw89_chanctx_state state);  #endif diff --git a/sys/contrib/dev/rtw89/rtw8852bte.c b/sys/contrib/dev/rtw89/rtw8852bte.c index b69fa17beb33..a584c75b801d 100644 --- a/sys/contrib/dev/rtw89/rtw8852bte.c +++ b/sys/contrib/dev/rtw89/rtw8852bte.c @@ -95,6 +95,7 @@ static struct pci_driver rtw89_8852bte_driver = {  	.probe		= rtw89_pci_probe,  	.remove		= rtw89_pci_remove,  	.driver.pm	= &rtw89_pm_ops, +	.err_handler    = &rtw89_pci_err_handler,  };  module_pci_driver(rtw89_8852bte_driver); diff --git a/sys/contrib/dev/rtw89/rtw8852bu.c b/sys/contrib/dev/rtw89/rtw8852bu.c new file mode 100644 index 000000000000..b315cb997758 --- /dev/null +++ b/sys/contrib/dev/rtw89/rtw8852bu.c @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2025  Realtek Corporation + */ + +#include <linux/module.h> +#include <linux/usb.h> +#include "rtw8852b.h" +#include "usb.h" + +static const struct rtw89_driver_info rtw89_8852bu_info = { +	.chip = &rtw8852b_chip_info, +	.variant = NULL, +	.quirks = NULL, +}; + +static const struct usb_device_id rtw_8852bu_id_table[] = { +	{ USB_DEVICE_AND_INTERFACE_INFO(0x0bda, 0xb832, 0xff, 0xff, 0xff), +	  .driver_info = (kernel_ulong_t)&rtw89_8852bu_info }, +	{ USB_DEVICE_AND_INTERFACE_INFO(0x0bda, 0xb83a, 0xff, 0xff, 0xff), +	  .driver_info = (kernel_ulong_t)&rtw89_8852bu_info }, +	{ USB_DEVICE_AND_INTERFACE_INFO(0x0bda, 0xb852, 0xff, 0xff, 0xff), +	  .driver_info = (kernel_ulong_t)&rtw89_8852bu_info }, +	{ USB_DEVICE_AND_INTERFACE_INFO(0x0bda, 0xb85a, 0xff, 0xff, 0xff), +	  .driver_info = (kernel_ulong_t)&rtw89_8852bu_info }, +	{ USB_DEVICE_AND_INTERFACE_INFO(0x0bda, 0xa85b, 0xff, 0xff, 0xff), +	  .driver_info = (kernel_ulong_t)&rtw89_8852bu_info }, +	{ USB_DEVICE_AND_INTERFACE_INFO(0x0586, 0x3428, 0xff, 0xff, 0xff), +	  .driver_info = (kernel_ulong_t)&rtw89_8852bu_info }, +	{ USB_DEVICE_AND_INTERFACE_INFO(0x0b05, 0x1a62, 0xff, 0xff, 0xff), +	  .driver_info = (kernel_ulong_t)&rtw89_8852bu_info }, +	{ USB_DEVICE_AND_INTERFACE_INFO(0x0db0, 0x6931, 0xff, 0xff, 0xff), +	  .driver_info = (kernel_ulong_t)&rtw89_8852bu_info }, +	{ USB_DEVICE_AND_INTERFACE_INFO(0x3574, 0x6121, 0xff, 0xff, 0xff), +	  .driver_info = (kernel_ulong_t)&rtw89_8852bu_info }, +	{ USB_DEVICE_AND_INTERFACE_INFO(0x35bc, 0x0100, 0xff, 0xff, 0xff), +	  .driver_info = (kernel_ulong_t)&rtw89_8852bu_info }, +	{ USB_DEVICE_AND_INTERFACE_INFO(0x35bc, 0x0108, 0xff, 0xff, 0xff), +	  .driver_info = (kernel_ulong_t)&rtw89_8852bu_info }, +	{ USB_DEVICE_AND_INTERFACE_INFO(0x7392, 0x6822, 0xff, 0xff, 0xff), +	  .driver_info = (kernel_ulong_t)&rtw89_8852bu_info }, +	{}, +}; +MODULE_DEVICE_TABLE(usb, rtw_8852bu_id_table); + +static struct usb_driver rtw_8852bu_driver = { +	.name = KBUILD_MODNAME, +	.id_table = rtw_8852bu_id_table, +	.probe = rtw89_usb_probe, +	.disconnect = rtw89_usb_disconnect, +}; +module_usb_driver(rtw_8852bu_driver); + +MODULE_AUTHOR("Bitterblue Smith <rtl8821cerfe2@gmail.com>"); +MODULE_DESCRIPTION("Realtek 802.11ax wireless 8852BU driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sys/contrib/dev/rtw89/rtw8852c.c b/sys/contrib/dev/rtw89/rtw8852c.c index ef80d80dee2e..99f02c7702f9 100644 --- a/sys/contrib/dev/rtw89/rtw8852c.c +++ b/sys/contrib/dev/rtw89/rtw8852c.c @@ -12,9 +12,10 @@  #include "rtw8852c.h"  #include "rtw8852c_rfk.h"  #include "rtw8852c_table.h" +#include "sar.h"  #include "util.h" -#define RTW8852C_FW_FORMAT_MAX 1 +#define RTW8852C_FW_FORMAT_MAX 2  #define RTW8852C_FW_BASENAME "rtw89/rtw8852c_fw"  #define RTW8852C_MODULE_FIRMWARE \  	RTW8852C_FW_BASENAME "-" __stringify(RTW8852C_FW_FORMAT_MAX) ".bin" @@ -186,10 +187,17 @@ static const struct rtw89_edcca_regs rtw8852c_edcca_regs = {  	.edcca_p_mask			= B_EDCCA_LVL_MSK1,  	.ppdu_level			= R_SEG0R_EDCCA_LVL,  	.ppdu_mask			= B_EDCCA_LVL_MSK3, -	.rpt_a				= R_EDCCA_RPT_A, -	.rpt_b				= R_EDCCA_RPT_B, -	.rpt_sel			= R_EDCCA_RPT_SEL, -	.rpt_sel_mask			= B_EDCCA_RPT_SEL_MSK, +	.p = {{ +		.rpt_a			= R_EDCCA_RPT_A, +		.rpt_b			= R_EDCCA_RPT_B, +		.rpt_sel		= R_EDCCA_RPT_SEL, +		.rpt_sel_mask		= B_EDCCA_RPT_SEL_MSK, +	}, { +		.rpt_a			= R_EDCCA_RPT_P1_A, +		.rpt_b			= R_EDCCA_RPT_P1_B, +		.rpt_sel		= R_EDCCA_RPT_SEL, +		.rpt_sel_mask		= B_EDCCA_RPT_SEL_P1_MSK, +	}},  	.tx_collision_t2r_st		= R_TX_COLLISION_T2R_ST,  	.tx_collision_t2r_st_mask	= B_TX_COLLISION_T2R_ST_M,  }; @@ -1853,10 +1861,16 @@ static void rtw8852c_rfk_channel(struct rtw89_dev *rtwdev,  	enum rtw89_phy_idx phy_idx = rtwvif_link->phy_idx;  	rtw8852c_mcc_get_ch_info(rtwdev, phy_idx); +	rtw89_btc_ntfy_conn_rfk(rtwdev, true); +  	rtw8852c_rx_dck(rtwdev, phy_idx, false);  	rtw8852c_iqk(rtwdev, phy_idx, chanctx_idx); +	rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30);  	rtw8852c_tssi(rtwdev, phy_idx, chanctx_idx); +	rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30);  	rtw8852c_dpk(rtwdev, phy_idx, chanctx_idx); + +	rtw89_btc_ntfy_conn_rfk(rtwdev, false);  	rtw89_fw_h2c_rf_ntfy_mcc(rtwdev);  } @@ -2071,6 +2085,31 @@ static void rtw8852c_set_txpwr_diff(struct rtw89_dev *rtwdev,  	rtw8852c_set_txpwr_ref(rtwdev, phy_idx, pwr_ofst);  } +static void rtw8852c_set_txpwr_sar_diff(struct rtw89_dev *rtwdev, +					const struct rtw89_chan *chan, +					enum rtw89_phy_idx phy_idx) +{ +	struct rtw89_sar_parm sar_parm = { +		.center_freq = chan->freq, +		.force_path = true, +	}; +	s16 sar_rf; +	s8 sar_mac; + +	if (phy_idx != RTW89_PHY_0) +		return; + +	sar_parm.path = RF_PATH_A; +	sar_mac = rtw89_query_sar(rtwdev, &sar_parm); +	sar_rf = rtw89_phy_txpwr_mac_to_rf(rtwdev, sar_mac); +	rtw89_phy_write32_mask(rtwdev, R_TXPWRB, B_TXPWRB_MAX, sar_rf); + +	sar_parm.path = RF_PATH_B; +	sar_mac = rtw89_query_sar(rtwdev, &sar_parm); +	sar_rf = rtw89_phy_txpwr_mac_to_rf(rtwdev, sar_mac); +	rtw89_phy_write32_mask(rtwdev, R_P1_TXPWRB, B_TXPWRB_MAX, sar_rf); +} +  static void rtw8852c_set_txpwr(struct rtw89_dev *rtwdev,  			       const struct rtw89_chan *chan,  			       enum rtw89_phy_idx phy_idx) @@ -2081,6 +2120,7 @@ static void rtw8852c_set_txpwr(struct rtw89_dev *rtwdev,  	rtw89_phy_set_txpwr_limit(rtwdev, chan, phy_idx);  	rtw89_phy_set_txpwr_limit_ru(rtwdev, chan, phy_idx);  	rtw8852c_set_txpwr_diff(rtwdev, chan, phy_idx); +	rtw8852c_set_txpwr_sar_diff(rtwdev, chan, phy_idx);  }  static void rtw8852c_set_txpwr_ctrl(struct rtw89_dev *rtwdev, @@ -2873,6 +2913,7 @@ static int rtw8852c_mac_disable_bb_rf(struct rtw89_dev *rtwdev)  static const struct rtw89_chanctx_listener rtw8852c_chanctx_listener = {  	.callbacks[RTW89_CHANCTX_CALLBACK_RFK] = rtw8852c_rfk_chanctx_cb, +	.callbacks[RTW89_CHANCTX_CALLBACK_TAS] = rtw89_tas_chanctx_cb,  };  #ifdef CONFIG_PM @@ -2913,6 +2954,7 @@ static const struct rtw89_chip_ops rtw8852c_chip_ops = {  	.set_txpwr_ctrl		= rtw8852c_set_txpwr_ctrl,  	.init_txpwr_unit	= rtw8852c_init_txpwr_unit,  	.get_thermal		= rtw8852c_get_thermal, +	.chan_to_rf18_val	= NULL,  	.ctrl_btg_bt_rx		= rtw8852c_ctrl_btg_bt_rx,  	.query_ppdu		= rtw8852c_query_ppdu,  	.convert_rpl_to_rssi	= NULL, @@ -2934,6 +2976,8 @@ static const struct rtw89_chip_ops rtw8852c_chip_ops = {  	.h2c_default_cmac_tbl	= rtw89_fw_h2c_default_cmac_tbl,  	.h2c_assoc_cmac_tbl	= rtw89_fw_h2c_assoc_cmac_tbl,  	.h2c_ampdu_cmac_tbl	= NULL, +	.h2c_txtime_cmac_tbl	= rtw89_fw_h2c_txtime_cmac_tbl, +	.h2c_punctured_cmac_tbl	= NULL,  	.h2c_default_dmac_tbl	= NULL,  	.h2c_update_beacon	= rtw89_fw_h2c_update_beacon,  	.h2c_ba_cam		= rtw89_fw_h2c_ba_cam, @@ -2960,14 +3004,15 @@ const struct rtw89_chip_info rtw8852c_chip_info = {  	.try_ce_fw		= false,  	.bbmcu_nr		= 0,  	.needed_fw_elms		= 0, +	.fw_blacklist		= &rtw89_fw_blacklist_default,  	.fifo_size		= 458752,  	.small_fifo_size	= false,  	.dle_scc_rsvd_size	= 0,  	.max_amsdu_limit	= 8000,  	.dis_2g_40m_ul_ofdma	= false,  	.rsvd_ple_ofst		= 0x6f800, -	.hfc_param_ini		= rtw8852c_hfc_param_ini_pcie, -	.dle_mem		= rtw8852c_dle_mem_pcie, +	.hfc_param_ini		= {rtw8852c_hfc_param_ini_pcie, NULL, NULL}, +	.dle_mem		= {rtw8852c_dle_mem_pcie, NULL, NULL, NULL},  	.wde_qempty_acq_grpnum	= 16,  	.wde_qempty_mgq_grpsel	= 16,  	.rf_base_addr		= {0xe000, 0xf000}, @@ -3002,10 +3047,15 @@ const struct rtw89_chip_info rtw8852c_chip_info = {  				  BIT(NL80211_CHAN_WIDTH_160),  	.support_unii4		= true,  	.support_ant_gain	= true, +	.support_tas		= true, +	.support_sar_by_ant	= true,  	.ul_tb_waveform_ctrl	= false,  	.ul_tb_pwr_diff		= true, +	.rx_freq_frome_ie	= false,  	.hw_sec_hdr		= true,  	.hw_mgmt_tx_encrypt	= true, +	.hw_tkip_crypto		= true, +	.hw_mlo_bmc_crypto	= false,  	.rf_path_num		= 2,  	.tx_nss			= 2,  	.rx_nss			= 2, @@ -3027,7 +3077,6 @@ const struct rtw89_chip_info rtw8852c_chip_info = {  	.phycap_size		= 0x60,  	.para_ver		= 0x1,  	.wlcx_desired		= 0x06000000, -	.btcx_desired		= 0x7,  	.scbd			= 0x1,  	.mailbox		= 0x1, diff --git a/sys/contrib/dev/rtw89/rtw8852ce.c b/sys/contrib/dev/rtw89/rtw8852ce.c index 0785ed14461e..a7447daaf750 100644 --- a/sys/contrib/dev/rtw89/rtw8852ce.c +++ b/sys/contrib/dev/rtw89/rtw8852ce.c @@ -118,6 +118,7 @@ static struct pci_driver rtw89_8852ce_driver = {  	.probe		= rtw89_pci_probe,  	.remove		= rtw89_pci_remove,  	.driver.pm	= &rtw89_pm_ops, +	.err_handler    = &rtw89_pci_err_handler,  #if defined(__FreeBSD__)  	.bsddriver.name	= KBUILD_MODNAME,  #endif diff --git a/sys/contrib/dev/rtw89/rtw8922a.c b/sys/contrib/dev/rtw89/rtw8922a.c index 11d66bfceb15..36c641e3bc13 100644 --- a/sys/contrib/dev/rtw89/rtw8922a.c +++ b/sys/contrib/dev/rtw89/rtw8922a.c @@ -12,9 +12,10 @@  #include "reg.h"  #include "rtw8922a.h"  #include "rtw8922a_rfk.h" +#include "sar.h"  #include "util.h" -#define RTW8922A_FW_FORMAT_MAX 3 +#define RTW8922A_FW_FORMAT_MAX 4  #define RTW8922A_FW_BASENAME "rtw89/rtw8922a_fw"  #define RTW8922A_MODULE_FIRMWARE \  	RTW8922A_FW_BASENAME "-" __stringify(RTW8922A_FW_FORMAT_MAX) ".bin" @@ -205,10 +206,17 @@ static const struct rtw89_edcca_regs rtw8922a_edcca_regs = {  	.edcca_p_mask			= B_EDCCA_LVL_MSK1,  	.ppdu_level			= R_SEG0R_PPDU_LVL_BE,  	.ppdu_mask			= B_EDCCA_LVL_MSK1, -	.rpt_a				= R_EDCCA_RPT_A_BE, -	.rpt_b				= R_EDCCA_RPT_B_BE, -	.rpt_sel			= R_EDCCA_RPT_SEL_BE, -	.rpt_sel_mask			= B_EDCCA_RPT_SEL_MSK, +	.p = {{ +		.rpt_a			= R_EDCCA_RPT_A_BE, +		.rpt_b			= R_EDCCA_RPT_B_BE, +		.rpt_sel		= R_EDCCA_RPT_SEL_BE, +		.rpt_sel_mask		= B_EDCCA_RPT_SEL_MSK, +	}, { +		.rpt_a			= R_EDCCA_RPT_P1_A_BE, +		.rpt_b			= R_EDCCA_RPT_P1_B_BE, +		.rpt_sel		= R_EDCCA_RPT_SEL_BE, +		.rpt_sel_mask		= B_EDCCA_RPT_SEL_P1_MSK, +	}},  	.rpt_sel_be			= R_EDCCA_RPTREG_SEL_BE,  	.rpt_sel_be_mask		= B_EDCCA_RPTREG_SEL_BE_MSK,  	.tx_collision_t2r_st		= R_TX_COLLISION_T2R_ST_BE, @@ -2063,7 +2071,8 @@ static void __rtw8922a_rfk_init_late(struct rtw89_dev *rtwdev,  	rtw89_phy_rfk_pre_ntfy_and_wait(rtwdev, phy_idx, 5);  	rtw89_phy_rfk_dack_and_wait(rtwdev, phy_idx, chan, 58); -	rtw89_phy_rfk_rxdck_and_wait(rtwdev, phy_idx, chan, false, 32); +	if (!test_bit(RTW89_FLAG_SER_HANDLING, rtwdev->flags)) +		rtw89_phy_rfk_rxdck_and_wait(rtwdev, phy_idx, chan, false, 128);  }  static void rtw8922a_rfk_init_late(struct rtw89_dev *rtwdev) @@ -2149,6 +2158,56 @@ static void rtw8922a_set_txpwr_ref(struct rtw89_dev *rtwdev,  				     B_BE_PWR_REF_CTRL_CCK, ref_cck);  } +static const struct rtw89_reg_def rtw8922a_txpwr_ref[][3] = { +	{{ .addr = R_TXAGC_REF_DBM_P0, .mask = B_TXAGC_OFDM_REF_DBM_P0}, +	 { .addr = R_TXAGC_REF_DBM_P0, .mask = B_TXAGC_CCK_REF_DBM_P0}, +	 { .addr = R_TSSI_K_P0, .mask = B_TSSI_K_OFDM_P0} +	}, +	{{ .addr = R_TXAGC_REF_DBM_RF1_P0, .mask = B_TXAGC_OFDM_REF_DBM_RF1_P0}, +	 { .addr = R_TXAGC_REF_DBM_RF1_P0, .mask = B_TXAGC_CCK_REF_DBM_RF1_P0}, +	 { .addr = R_TSSI_K_RF1_P0, .mask = B_TSSI_K_OFDM_RF1_P0} +	}, +}; + +static void rtw8922a_set_txpwr_diff(struct rtw89_dev *rtwdev, +				    const struct rtw89_chan *chan, +				    enum rtw89_phy_idx phy_idx) +{ +	s16 pwr_ofst = rtw89_phy_ant_gain_pwr_offset(rtwdev, chan); +	const struct rtw89_chip_info *chip = rtwdev->chip; +	static const u32 path_ofst[] = {0x0, 0x100}; +	const struct rtw89_reg_def *txpwr_ref; +	static const s16 tssi_k_base = 0x12; +	s16 tssi_k_ofst = abs(pwr_ofst) + tssi_k_base; +	s16 ofst_dec[RF_PATH_NUM_8922A]; +	s16 tssi_k[RF_PATH_NUM_8922A]; +	s16 pwr_ref_ofst; +	s16 pwr_ref = 0; +	u8 i; + +	if (rtwdev->hal.cv == CHIP_CAV) +		pwr_ref = 16; + +	pwr_ref <<= chip->txpwr_factor_rf; +	pwr_ref_ofst = pwr_ref - rtw89_phy_txpwr_bb_to_rf(rtwdev, abs(pwr_ofst)); + +	ofst_dec[RF_PATH_A] = pwr_ofst > 0 ? pwr_ref : pwr_ref_ofst; +	ofst_dec[RF_PATH_B] = pwr_ofst > 0 ? pwr_ref_ofst : pwr_ref; +	tssi_k[RF_PATH_A] = pwr_ofst > 0 ? tssi_k_base : tssi_k_ofst; +	tssi_k[RF_PATH_B] = pwr_ofst > 0 ? tssi_k_ofst : tssi_k_base; + +	for (i = 0; i < RF_PATH_NUM_8922A; i++) { +		txpwr_ref = rtw8922a_txpwr_ref[phy_idx]; + +		rtw89_phy_write32_mask(rtwdev, txpwr_ref[0].addr + path_ofst[i], +				       txpwr_ref[0].mask, ofst_dec[i]); +		rtw89_phy_write32_mask(rtwdev, txpwr_ref[1].addr + path_ofst[i], +				       txpwr_ref[1].mask, ofst_dec[i]); +		rtw89_phy_write32_mask(rtwdev, txpwr_ref[2].addr + path_ofst[i], +				       txpwr_ref[2].mask, tssi_k[i]); +	} +} +  static void rtw8922a_bb_tx_triangular(struct rtw89_dev *rtwdev, bool en,  				      enum rtw89_phy_idx phy_idx)  { @@ -2176,6 +2235,31 @@ static void rtw8922a_set_tx_shape(struct rtw89_dev *rtwdev,  		rtw8922a_bb_tx_triangular(rtwdev, true, phy_idx);  } +static void rtw8922a_set_txpwr_sar_diff(struct rtw89_dev *rtwdev, +					const struct rtw89_chan *chan, +					enum rtw89_phy_idx phy_idx) +{ +	struct rtw89_sar_parm sar_parm = { +		.center_freq = chan->freq, +		.force_path = true, +	}; +	s16 sar_rf; +	s8 sar_mac; + +	if (phy_idx != RTW89_PHY_0) +		return; + +	sar_parm.path = RF_PATH_A; +	sar_mac = rtw89_query_sar(rtwdev, &sar_parm); +	sar_rf = rtw89_phy_txpwr_mac_to_rf(rtwdev, sar_mac); +	rtw89_phy_write32_mask(rtwdev, R_P0_TXPWRB_BE, B_TXPWRB_MAX_BE, sar_rf); + +	sar_parm.path = RF_PATH_B; +	sar_mac = rtw89_query_sar(rtwdev, &sar_parm); +	sar_rf = rtw89_phy_txpwr_mac_to_rf(rtwdev, sar_mac); +	rtw89_phy_write32_mask(rtwdev, R_P1_TXPWRB_BE, B_TXPWRB_MAX_BE, sar_rf); +} +  static void rtw8922a_set_txpwr(struct rtw89_dev *rtwdev,  			       const struct rtw89_chan *chan,  			       enum rtw89_phy_idx phy_idx) @@ -2185,6 +2269,9 @@ static void rtw8922a_set_txpwr(struct rtw89_dev *rtwdev,  	rtw8922a_set_tx_shape(rtwdev, chan, phy_idx);  	rtw89_phy_set_txpwr_limit(rtwdev, chan, phy_idx);  	rtw89_phy_set_txpwr_limit_ru(rtwdev, chan, phy_idx); +	rtw8922a_set_txpwr_diff(rtwdev, chan, phy_idx); +	rtw8922a_set_txpwr_ref(rtwdev, phy_idx); +	rtw8922a_set_txpwr_sar_diff(rtwdev, chan, phy_idx);  }  static void rtw8922a_set_txpwr_ctrl(struct rtw89_dev *rtwdev, @@ -2303,6 +2390,48 @@ static u8 rtw8922a_get_thermal(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_p  	return clamp_t(int, th, 0, U8_MAX);  } +static u32 rtw8922a_chan_to_rf18_val(struct rtw89_dev *rtwdev, +				     const struct rtw89_chan *chan) +{ +	u32 val = u32_encode_bits(chan->channel, RR_CFGCH_CH); + +	switch (chan->band_type) { +	case RTW89_BAND_2G: +	default: +		break; +	case RTW89_BAND_5G: +		val |= u32_encode_bits(CFGCH_BAND1_5G, RR_CFGCH_BAND1) | +		       u32_encode_bits(CFGCH_BAND0_5G, RR_CFGCH_BAND0); +		break; +	case RTW89_BAND_6G: +		val |= u32_encode_bits(CFGCH_BAND1_6G, RR_CFGCH_BAND1) | +		       u32_encode_bits(CFGCH_BAND0_6G, RR_CFGCH_BAND0); +		break; +	} + +	switch (chan->band_width) { +	case RTW89_CHANNEL_WIDTH_5: +	case RTW89_CHANNEL_WIDTH_10: +	case RTW89_CHANNEL_WIDTH_20: +	default: +		break; +	case RTW89_CHANNEL_WIDTH_40: +		val |= u32_encode_bits(CFGCH_BW_V2_40M, RR_CFGCH_BW_V2); +		break; +	case RTW89_CHANNEL_WIDTH_80: +		val |= u32_encode_bits(CFGCH_BW_V2_80M, RR_CFGCH_BW_V2); +		break; +	case RTW89_CHANNEL_WIDTH_160: +		val |= u32_encode_bits(CFGCH_BW_V2_160M, RR_CFGCH_BW_V2); +		break; +	case RTW89_CHANNEL_WIDTH_320: +		val |= u32_encode_bits(CFGCH_BW_V2_320M, RR_CFGCH_BW_V2); +		break; +	} + +	return val; +} +  static void rtw8922a_btc_set_rfe(struct rtw89_dev *rtwdev)  {  	union rtw89_btc_module_info *md = &rtwdev->btc.mdinfo; @@ -2674,6 +2803,7 @@ static const struct rtw89_chip_ops rtw8922a_chip_ops = {  	.set_txpwr_ctrl		= rtw8922a_set_txpwr_ctrl,  	.init_txpwr_unit	= NULL,  	.get_thermal		= rtw8922a_get_thermal, +	.chan_to_rf18_val	= rtw8922a_chan_to_rf18_val,  	.ctrl_btg_bt_rx		= rtw8922a_ctrl_btg_bt_rx,  	.query_ppdu		= rtw8922a_query_ppdu,  	.convert_rpl_to_rssi	= rtw8922a_convert_rpl_to_rssi, @@ -2695,6 +2825,8 @@ static const struct rtw89_chip_ops rtw8922a_chip_ops = {  	.h2c_default_cmac_tbl	= rtw89_fw_h2c_default_cmac_tbl_g7,  	.h2c_assoc_cmac_tbl	= rtw89_fw_h2c_assoc_cmac_tbl_g7,  	.h2c_ampdu_cmac_tbl	= rtw89_fw_h2c_ampdu_cmac_tbl_g7, +	.h2c_txtime_cmac_tbl	= rtw89_fw_h2c_txtime_cmac_tbl_g7, +	.h2c_punctured_cmac_tbl	= rtw89_fw_h2c_punctured_cmac_tbl_g7,  	.h2c_default_dmac_tbl	= rtw89_fw_h2c_default_dmac_tbl_v2,  	.h2c_update_beacon	= rtw89_fw_h2c_update_beacon_be,  	.h2c_ba_cam		= rtw89_fw_h2c_ba_cam_v1, @@ -2721,14 +2853,15 @@ const struct rtw89_chip_info rtw8922a_chip_info = {  	.try_ce_fw		= false,  	.bbmcu_nr		= 1,  	.needed_fw_elms		= RTW89_BE_GEN_DEF_NEEDED_FW_ELEMENTS, +	.fw_blacklist		= &rtw89_fw_blacklist_default,  	.fifo_size		= 589824,  	.small_fifo_size	= false,  	.dle_scc_rsvd_size	= 0,  	.max_amsdu_limit	= 8000,  	.dis_2g_40m_ul_ofdma	= false,  	.rsvd_ple_ofst		= 0x8f800, -	.hfc_param_ini		= rtw8922a_hfc_param_ini_pcie, -	.dle_mem		= rtw8922a_dle_mem_pcie, +	.hfc_param_ini		= {rtw8922a_hfc_param_ini_pcie, NULL, NULL}, +	.dle_mem		= {rtw8922a_dle_mem_pcie, NULL, NULL, NULL},  	.wde_qempty_acq_grpnum	= 4,  	.wde_qempty_mgq_grpsel	= 4,  	.rf_base_addr		= {0xe000, 0xf000}, @@ -2760,11 +2893,16 @@ const struct rtw89_chip_info rtw8922a_chip_info = {  				  BIT(NL80211_CHAN_WIDTH_80) |  				  BIT(NL80211_CHAN_WIDTH_160),  	.support_unii4		= true, -	.support_ant_gain	= false, +	.support_ant_gain	= true, +	.support_tas		= false, +	.support_sar_by_ant	= true,  	.ul_tb_waveform_ctrl	= false,  	.ul_tb_pwr_diff		= false, +	.rx_freq_frome_ie	= false,  	.hw_sec_hdr		= true,  	.hw_mgmt_tx_encrypt	= true, +	.hw_tkip_crypto		= true, +	.hw_mlo_bmc_crypto	= false,  	.rf_path_num		= 2,  	.tx_nss			= 2,  	.rx_nss			= 2, @@ -2786,7 +2924,6 @@ const struct rtw89_chip_info rtw8922a_chip_info = {  	.phycap_size		= 0x38,  	.para_ver		= 0xf,  	.wlcx_desired		= 0x07110000, -	.btcx_desired		= 0x7,  	.scbd			= 0x1,  	.mailbox		= 0x1, diff --git a/sys/contrib/dev/rtw89/rtw8922a_rfk.c b/sys/contrib/dev/rtw89/rtw8922a_rfk.c index c4c93f836a2f..fce094c7ce93 100644 --- a/sys/contrib/dev/rtw89/rtw8922a_rfk.c +++ b/sys/contrib/dev/rtw89/rtw8922a_rfk.c @@ -36,8 +36,7 @@ void rtw8922a_tssi_cont_en_phyidx(struct rtw89_dev *rtwdev, bool en, u8 phy_idx)  static  void rtw8922a_ctl_band_ch_bw(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, -			     u8 central_ch, enum rtw89_band band, -			     enum rtw89_bandwidth bw) +			     const struct rtw89_chan *chan)  {  	const u32 rf_addr[2] = {RR_CFGCH, RR_CFGCH_V1};  	struct rtw89_hal *hal = &rtwdev->hal; @@ -73,54 +72,9 @@ void rtw8922a_ctl_band_ch_bw(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy,  				return;  			} -			rf_reg[path][i] &= ~(RR_CFGCH_BAND1 | RR_CFGCH_BW | +			rf_reg[path][i] &= ~(RR_CFGCH_BAND1 | RR_CFGCH_BW_V2 |  					     RR_CFGCH_BAND0 | RR_CFGCH_CH); -			rf_reg[path][i] |= u32_encode_bits(central_ch, RR_CFGCH_CH); - -			if (band == RTW89_BAND_2G) -				rtw89_write_rf(rtwdev, path, RR_SMD, RR_VCO2, 0x0); -			else -				rtw89_write_rf(rtwdev, path, RR_SMD, RR_VCO2, 0x1); - -			switch (band) { -			case RTW89_BAND_2G: -			default: -				break; -			case RTW89_BAND_5G: -				rf_reg[path][i] |= -					u32_encode_bits(CFGCH_BAND1_5G, RR_CFGCH_BAND1) | -					u32_encode_bits(CFGCH_BAND0_5G, RR_CFGCH_BAND0); -				break; -			case RTW89_BAND_6G: -				rf_reg[path][i] |= -					u32_encode_bits(CFGCH_BAND1_6G, RR_CFGCH_BAND1) | -					u32_encode_bits(CFGCH_BAND0_6G, RR_CFGCH_BAND0); -				break; -			} - -			switch (bw) { -			case RTW89_CHANNEL_WIDTH_5: -			case RTW89_CHANNEL_WIDTH_10: -			case RTW89_CHANNEL_WIDTH_20: -			default: -				break; -			case RTW89_CHANNEL_WIDTH_40: -				rf_reg[path][i] |= -					u32_encode_bits(CFGCH_BW_V2_40M, RR_CFGCH_BW_V2); -				break; -			case RTW89_CHANNEL_WIDTH_80: -				rf_reg[path][i] |= -					u32_encode_bits(CFGCH_BW_V2_80M, RR_CFGCH_BW_V2); -				break; -			case RTW89_CHANNEL_WIDTH_160: -				rf_reg[path][i] |= -					u32_encode_bits(CFGCH_BW_V2_160M, RR_CFGCH_BW_V2); -				break; -			case RTW89_CHANNEL_WIDTH_320: -				rf_reg[path][i] |= -					u32_encode_bits(CFGCH_BW_V2_320M, RR_CFGCH_BW_V2); -				break; -			} +			rf_reg[path][i] |= rtw89_chip_chan_to_rf18_val(rtwdev, chan);  			rtw89_write_rf(rtwdev, path, rf_addr[i],  				       RFREG_MASK, rf_reg[path][i]); @@ -131,7 +85,7 @@ void rtw8922a_ctl_band_ch_bw(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy,  	if (hal->cv != CHIP_CAV)  		return; -	if (band == RTW89_BAND_2G) { +	if (chan->band_type == RTW89_BAND_2G) {  		rtw89_write_rf(rtwdev, RF_PATH_A, RR_LUTWE, RFREG_MASK, 0x80000);  		rtw89_write_rf(rtwdev, RF_PATH_A, RR_LUTWA, RFREG_MASK, 0x00003);  		rtw89_write_rf(rtwdev, RF_PATH_A, RR_LUTWD1, RFREG_MASK, 0x0c990); @@ -150,8 +104,7 @@ void rtw8922a_set_channel_rf(struct rtw89_dev *rtwdev,  			     const struct rtw89_chan *chan,  			     enum rtw89_phy_idx phy_idx)  { -	rtw8922a_ctl_band_ch_bw(rtwdev, phy_idx, chan->channel, chan->band_type, -				chan->band_width); +	rtw8922a_ctl_band_ch_bw(rtwdev, phy_idx, chan);  }  enum _rf_syn_pow { diff --git a/sys/contrib/dev/rtw89/rtw8922ae.c b/sys/contrib/dev/rtw89/rtw8922ae.c index e8ccc5b4b759..f1eeb0dc61b4 100644 --- a/sys/contrib/dev/rtw89/rtw8922ae.c +++ b/sys/contrib/dev/rtw89/rtw8922ae.c @@ -106,6 +106,7 @@ static struct pci_driver rtw89_8922ae_driver = {  	.probe		= rtw89_pci_probe,  	.remove		= rtw89_pci_remove,  	.driver.pm	= &rtw89_pm_ops_be, +	.err_handler    = &rtw89_pci_err_handler,  #if defined(__FreeBSD__)  	.bsddriver.name	= KBUILD_MODNAME,  #endif diff --git a/sys/contrib/dev/rtw89/sar.c b/sys/contrib/dev/rtw89/sar.c index 8b8c075780d4..5b16b98aa5b3 100644 --- a/sys/contrib/dev/rtw89/sar.c +++ b/sys/contrib/dev/rtw89/sar.c @@ -7,10 +7,16 @@  #include "phy.h"  #include "reg.h"  #include "sar.h" +#include "util.h"  #define RTW89_TAS_FACTOR 2 /* unit: 0.25 dBm */ +#define RTW89_TAS_SAR_GAP (1 << RTW89_TAS_FACTOR)  #define RTW89_TAS_DPR_GAP (1 << RTW89_TAS_FACTOR)  #define RTW89_TAS_DELTA (2 << RTW89_TAS_FACTOR) +#define RTW89_TAS_TX_RATIO_THRESHOLD 70 +#define RTW89_TAS_DFLT_TX_RATIO 80 +#define RTW89_TAS_DPR_ON_OFFSET (RTW89_TAS_DELTA + RTW89_TAS_SAR_GAP) +#define RTW89_TAS_DPR_OFF_OFFSET (4 << RTW89_TAS_FACTOR)  static enum rtw89_sar_subband rtw89_sar_get_subband(struct rtw89_dev *rtwdev,  						    u32 center_freq) @@ -51,10 +57,12 @@ static enum rtw89_sar_subband rtw89_sar_get_subband(struct rtw89_dev *rtwdev,  }  static int rtw89_query_sar_config_common(struct rtw89_dev *rtwdev, -					 u32 center_freq, s32 *cfg) +					 const struct rtw89_sar_parm *sar_parm, +					 s32 *cfg)  {  	struct rtw89_sar_cfg_common *rtwsar = &rtwdev->sar.cfg_common;  	enum rtw89_sar_subband subband_l, subband_h; +	u32 center_freq = sar_parm->center_freq;  	const struct rtw89_6ghz_span *span;  	span = rtw89_get_6ghz_span(rtwdev, center_freq); @@ -84,6 +92,93 @@ static int rtw89_query_sar_config_common(struct rtw89_dev *rtwdev,  	return 0;  } +static const struct rtw89_sar_entry_from_acpi * +rtw89_sar_cfg_acpi_get_ent(const struct rtw89_sar_cfg_acpi *rtwsar, +			   enum rtw89_rf_path path, +			   enum rtw89_regulation_type regd) +{ +	const struct rtw89_sar_indicator_from_acpi *ind = &rtwsar->indicator; +	const struct rtw89_sar_table_from_acpi *tbl; +	u8 sel; + +	sel = ind->tblsel[path]; +	tbl = &rtwsar->tables[sel]; + +	return &tbl->entries[regd]; +} + +static +s32 rtw89_sar_cfg_acpi_get_min(const struct rtw89_sar_entry_from_acpi *ent, +			       enum rtw89_rf_path path, +			       enum rtw89_acpi_sar_subband subband_low, +			       enum rtw89_acpi_sar_subband subband_high) +{ +	return min(ent->v[subband_low][path], ent->v[subband_high][path]); +} + +static int rtw89_query_sar_config_acpi(struct rtw89_dev *rtwdev, +				       const struct rtw89_sar_parm *sar_parm, +				       s32 *cfg) +{ +	const struct rtw89_chip_info *chip = rtwdev->chip; +	const struct rtw89_sar_cfg_acpi *rtwsar = &rtwdev->sar.cfg_acpi; +	const struct rtw89_sar_entry_from_acpi *ent_a, *ent_b; +	enum rtw89_acpi_sar_subband subband_l, subband_h; +	u32 center_freq = sar_parm->center_freq; +	const struct rtw89_6ghz_span *span; +	enum rtw89_regulation_type regd; +	enum rtw89_band band; +	s32 cfg_a, cfg_b; + +	span = rtw89_get_6ghz_span(rtwdev, center_freq); + +	if (span && RTW89_ACPI_SAR_SPAN_VALID(span)) { +		subband_l = span->acpi_sar_subband_low; +		subband_h = span->acpi_sar_subband_high; +	} else { +		subband_l = rtw89_acpi_sar_get_subband(rtwdev, center_freq); +		subband_h = subband_l; +	} + +	band = rtw89_acpi_sar_subband_to_band(rtwdev, subband_l); +	regd = rtw89_regd_get(rtwdev, band); + +	ent_a = rtw89_sar_cfg_acpi_get_ent(rtwsar, RF_PATH_A, regd); +	ent_b = rtw89_sar_cfg_acpi_get_ent(rtwsar, RF_PATH_B, regd); + +	cfg_a = rtw89_sar_cfg_acpi_get_min(ent_a, RF_PATH_A, subband_l, subband_h); +	cfg_b = rtw89_sar_cfg_acpi_get_min(ent_b, RF_PATH_B, subband_l, subband_h); + +	if (chip->support_sar_by_ant) { +		/* With declaration of support_sar_by_ant, relax the general +		 * SAR querying to return the maximum between paths. However, +		 * expect chip has dealt with the corresponding SAR settings +		 * by path. (To get SAR for a given path, chip can then query +		 * with force_path.) +		 */ +		if (sar_parm->force_path) { +			switch (sar_parm->path) { +			default: +			case RF_PATH_A: +				*cfg = cfg_a; +				break; +			case RF_PATH_B: +				*cfg = cfg_b; +				break; +			} +		} else { +			*cfg = max(cfg_a, cfg_b); +		} +	} else { +		*cfg = min(cfg_a, cfg_b); +	} + +	if (sar_parm->ntx == RTW89_2TX) +		*cfg -= rtwsar->downgrade_2tx; + +	return 0; +} +  static const  struct rtw89_sar_handler rtw89_sar_handlers[RTW89_SAR_SOURCE_NR] = {  	[RTW89_SAR_SOURCE_COMMON] = { @@ -91,6 +186,11 @@ struct rtw89_sar_handler rtw89_sar_handlers[RTW89_SAR_SOURCE_NR] = {  		.txpwr_factor_sar = 2,  		.query_sar_config = rtw89_query_sar_config_common,  	}, +	[RTW89_SAR_SOURCE_ACPI] = { +		.descr_sar_source = "RTW89_SAR_SOURCE_ACPI", +		.txpwr_factor_sar = TXPWR_FACTOR_OF_RTW89_ACPI_SAR, +		.query_sar_config = rtw89_query_sar_config_acpi, +	},  };  #define rtw89_sar_set_src(_dev, _src, _cfg_name, _cfg_data)		\ @@ -99,7 +199,8 @@ struct rtw89_sar_handler rtw89_sar_handlers[RTW89_SAR_SOURCE_NR] = {  		typeof(_dev) _d = (_dev);				\  		BUILD_BUG_ON(!rtw89_sar_handlers[_s].descr_sar_source);	\  		BUILD_BUG_ON(!rtw89_sar_handlers[_s].query_sar_config);	\ -		lockdep_assert_held(&_d->mutex);			\ +		if (test_bit(RTW89_FLAG_PROBE_DONE, _d->flags))		\ +			lockdep_assert_wiphy(_d->hw->wiphy);		\  		_d->sar._cfg_name = *(_cfg_data);			\  		_d->sar.src = _s;					\  	} while (0) @@ -117,8 +218,8 @@ static s8 rtw89_txpwr_sar_to_mac(struct rtw89_dev *rtwdev, u8 fct, s32 cfg)  			   RTW89_SAR_TXPWR_MAC_MAX);  } -static s8 rtw89_txpwr_tas_to_sar(const struct rtw89_sar_handler *sar_hdl, -				 s8 cfg) +static s32 rtw89_txpwr_tas_to_sar(const struct rtw89_sar_handler *sar_hdl, +				  s32 cfg)  {  	const u8 fct = sar_hdl->txpwr_factor_sar; @@ -128,8 +229,8 @@ static s8 rtw89_txpwr_tas_to_sar(const struct rtw89_sar_handler *sar_hdl,  		return cfg >> (RTW89_TAS_FACTOR - fct);  } -static s8 rtw89_txpwr_sar_to_tas(const struct rtw89_sar_handler *sar_hdl, -				 s8 cfg) +static s32 rtw89_txpwr_sar_to_tas(const struct rtw89_sar_handler *sar_hdl, +				  s32 cfg)  {  	const u8 fct = sar_hdl->txpwr_factor_sar; @@ -139,35 +240,67 @@ static s8 rtw89_txpwr_sar_to_tas(const struct rtw89_sar_handler *sar_hdl,  		return cfg << (RTW89_TAS_FACTOR - fct);  } -s8 rtw89_query_sar(struct rtw89_dev *rtwdev, u32 center_freq) +static bool rtw89_tas_is_active(struct rtw89_dev *rtwdev) +{ +	struct rtw89_tas_info *tas = &rtwdev->tas; +	struct rtw89_vif *rtwvif; + +	if (!tas->enable) +		return false; + +	rtw89_for_each_rtwvif(rtwdev, rtwvif) { +		if (ieee80211_vif_is_mld(rtwvif_to_vif(rtwvif))) +			return false; +	} + +	return true; +} + +static const char *rtw89_tas_state_str(enum rtw89_tas_state state) +{ +	switch (state) { +	case RTW89_TAS_STATE_DPR_OFF: +		return "DPR OFF"; +	case RTW89_TAS_STATE_DPR_ON: +		return "DPR ON"; +	case RTW89_TAS_STATE_STATIC_SAR: +		return "STATIC SAR"; +	default: +		return NULL; +	} +} + +s8 rtw89_query_sar(struct rtw89_dev *rtwdev, const struct rtw89_sar_parm *sar_parm)  {  	const enum rtw89_sar_sources src = rtwdev->sar.src;  	/* its members are protected by rtw89_sar_set_src() */  	const struct rtw89_sar_handler *sar_hdl = &rtw89_sar_handlers[src];  	struct rtw89_tas_info *tas = &rtwdev->tas; -	s8 delta; +	s32 offset;  	int ret;  	s32 cfg;  	u8 fct; -	lockdep_assert_held(&rtwdev->mutex); +	lockdep_assert_wiphy(rtwdev->hw->wiphy);  	if (src == RTW89_SAR_SOURCE_NONE)  		return RTW89_SAR_TXPWR_MAC_MAX; -	ret = sar_hdl->query_sar_config(rtwdev, center_freq, &cfg); +	ret = sar_hdl->query_sar_config(rtwdev, sar_parm, &cfg);  	if (ret)  		return RTW89_SAR_TXPWR_MAC_MAX; -	if (tas->enable) { +	if (rtw89_tas_is_active(rtwdev)) {  		switch (tas->state) {  		case RTW89_TAS_STATE_DPR_OFF: -			return RTW89_SAR_TXPWR_MAC_MAX; +			offset = rtw89_txpwr_tas_to_sar(sar_hdl, RTW89_TAS_DPR_OFF_OFFSET); +			cfg += offset; +			break;  		case RTW89_TAS_STATE_DPR_ON: -			delta = rtw89_txpwr_tas_to_sar(sar_hdl, tas->delta); -			cfg -= delta; +			offset = rtw89_txpwr_tas_to_sar(sar_hdl, RTW89_TAS_DPR_ON_OFFSET); +			cfg -= offset;  			break; -		case RTW89_TAS_STATE_DPR_FORBID: +		case RTW89_TAS_STATE_STATIC_SAR:  		default:  			break;  		} @@ -177,73 +310,86 @@ s8 rtw89_query_sar(struct rtw89_dev *rtwdev, u32 center_freq)  	return rtw89_txpwr_sar_to_mac(rtwdev, fct, cfg);  } +EXPORT_SYMBOL(rtw89_query_sar); -void rtw89_print_sar(struct seq_file *m, struct rtw89_dev *rtwdev, u32 center_freq) +int rtw89_print_sar(struct rtw89_dev *rtwdev, char *buf, size_t bufsz, +		    const struct rtw89_sar_parm *sar_parm)  {  	const enum rtw89_sar_sources src = rtwdev->sar.src;  	/* its members are protected by rtw89_sar_set_src() */  	const struct rtw89_sar_handler *sar_hdl = &rtw89_sar_handlers[src];  	const u8 fct_mac = rtwdev->chip->txpwr_factor_mac; +	char *p = buf, *end = buf + bufsz;  	int ret;  	s32 cfg;  	u8 fct; -	lockdep_assert_held(&rtwdev->mutex); +	lockdep_assert_wiphy(rtwdev->hw->wiphy);  	if (src == RTW89_SAR_SOURCE_NONE) { -		seq_puts(m, "no SAR is applied\n"); -		return; +		p += scnprintf(p, end - p, "no SAR is applied\n"); +		goto out;  	} -	seq_printf(m, "source: %d (%s)\n", src, sar_hdl->descr_sar_source); +	p += scnprintf(p, end - p, "source: %d (%s)\n", src, +		       sar_hdl->descr_sar_source); -	ret = sar_hdl->query_sar_config(rtwdev, center_freq, &cfg); +	ret = sar_hdl->query_sar_config(rtwdev, sar_parm, &cfg);  	if (ret) { -		seq_printf(m, "config: return code: %d\n", ret); -		seq_printf(m, "assign: max setting: %d (unit: 1/%lu dBm)\n", -			   RTW89_SAR_TXPWR_MAC_MAX, BIT(fct_mac)); -		return; +		p += scnprintf(p, end - p, "config: return code: %d\n", ret); +		p += scnprintf(p, end - p, +			       "assign: max setting: %d (unit: 1/%lu dBm)\n", +			       RTW89_SAR_TXPWR_MAC_MAX, BIT(fct_mac)); +		goto out;  	}  	fct = sar_hdl->txpwr_factor_sar; -	seq_printf(m, "config: %d (unit: 1/%lu dBm)\n", cfg, BIT(fct)); +	p += scnprintf(p, end - p, "config: %d (unit: 1/%lu dBm)\n", cfg, +		       BIT(fct)); + +	p += scnprintf(p, end - p, "support different configs by antenna: %s\n", +		       str_yes_no(rtwdev->chip->support_sar_by_ant)); +out: +	return p - buf;  } -void rtw89_print_tas(struct seq_file *m, struct rtw89_dev *rtwdev) +int rtw89_print_tas(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)  {  	struct rtw89_tas_info *tas = &rtwdev->tas; +	char *p = buf, *end = buf + bufsz; -	if (!tas->enable) { -		seq_puts(m, "no TAS is applied\n"); -		return; +	if (!rtw89_tas_is_active(rtwdev)) { +		p += scnprintf(p, end - p, "no TAS is applied\n"); +		goto out;  	} -	seq_printf(m, "DPR gap: %d\n", tas->dpr_gap); -	seq_printf(m, "TAS delta: %d\n", tas->delta); +	p += scnprintf(p, end - p, "State: %s\n", +		       rtw89_tas_state_str(tas->state)); +	p += scnprintf(p, end - p, "Average time: %d\n", +		       tas->window_size * 2); +	p += scnprintf(p, end - p, "SAR gap: %d dBm\n", +		       RTW89_TAS_SAR_GAP >> RTW89_TAS_FACTOR); +	p += scnprintf(p, end - p, "DPR gap: %d dBm\n", +		       RTW89_TAS_DPR_GAP >> RTW89_TAS_FACTOR); +	p += scnprintf(p, end - p, "DPR ON offset: %d dBm\n", +		       RTW89_TAS_DPR_ON_OFFSET >> RTW89_TAS_FACTOR); +	p += scnprintf(p, end - p, "DPR OFF offset: %d dBm\n", +		       RTW89_TAS_DPR_OFF_OFFSET >> RTW89_TAS_FACTOR); + +out: +	return p - buf;  }  static int rtw89_apply_sar_common(struct rtw89_dev *rtwdev,  				  const struct rtw89_sar_cfg_common *sar)  { -	enum rtw89_sar_sources src; -	int ret = 0; - -	mutex_lock(&rtwdev->mutex); - -	src = rtwdev->sar.src; -	if (src != RTW89_SAR_SOURCE_NONE && src != RTW89_SAR_SOURCE_COMMON) { -		rtw89_warn(rtwdev, "SAR source: %d is in use", src); -		ret = -EBUSY; -		goto exit; -	} - +	/* let common SAR have the highest priority; always apply it */  	rtw89_sar_set_src(rtwdev, RTW89_SAR_SOURCE_COMMON, cfg_common, sar);  	rtw89_core_set_chip_txpwr(rtwdev); +	rtw89_tas_reset(rtwdev, false); -exit: -	mutex_unlock(&rtwdev->mutex); -	return ret; +	return 0;  }  static const struct cfg80211_sar_freq_ranges rtw89_common_sar_freq_ranges[] = { @@ -283,6 +429,8 @@ int rtw89_ops_set_sar_specs(struct ieee80211_hw *hw,  	s32 power;  	u32 i, idx; +	lockdep_assert_wiphy(rtwdev->hw->wiphy); +  	if (sar->type != NL80211_SAR_TYPE_POWER)  		return -EINVAL; @@ -308,64 +456,254 @@ int rtw89_ops_set_sar_specs(struct ieee80211_hw *hw,  	return rtw89_apply_sar_common(rtwdev, &sar_common);  } -static void rtw89_tas_state_update(struct rtw89_dev *rtwdev) +static void rtw89_apply_sar_acpi(struct rtw89_dev *rtwdev, +				 const struct rtw89_sar_cfg_acpi *sar) +{ +	const struct rtw89_sar_table_from_acpi *tbl; +	const struct rtw89_sar_entry_from_acpi *ent; +	enum rtw89_sar_sources src; +	unsigned int i, j, k; + +	src = rtwdev->sar.src; +	if (src != RTW89_SAR_SOURCE_NONE) { +		rtw89_warn(rtwdev, "SAR source: %d is in use", src); +		return; +	} + +	rtw89_debug(rtwdev, RTW89_DBG_SAR, +		    "SAR-ACPI downgrade 2TX: %u (unit: 1/%lu dBm)\n", +		    sar->downgrade_2tx, BIT(TXPWR_FACTOR_OF_RTW89_ACPI_SAR)); + +	for (i = 0; i < sar->valid_num; i++) { +		tbl = &sar->tables[i]; + +		for (j = 0; j < RTW89_REGD_NUM; j++) { +			ent = &tbl->entries[j]; + +			rtw89_debug(rtwdev, RTW89_DBG_SAR, +				    "SAR-ACPI-[%u] REGD-%s (unit: 1/%lu dBm)\n", +				    i, rtw89_regd_get_string(j), +				    BIT(TXPWR_FACTOR_OF_RTW89_ACPI_SAR)); + +			for (k = 0; k < NUM_OF_RTW89_ACPI_SAR_SUBBAND; k++) +				rtw89_debug(rtwdev, RTW89_DBG_SAR, +					    "On subband %u, { %d, %d }\n", k, +					    ent->v[k][RF_PATH_A], ent->v[k][RF_PATH_B]); +		} +	} + +	rtw89_sar_set_src(rtwdev, RTW89_SAR_SOURCE_ACPI, cfg_acpi, sar); + +	/* SAR via ACPI is only configured in the early initial phase, so +	 * it does not seem necessary to reset txpwr related things here. +	 */ +} + +static void rtw89_set_sar_from_acpi(struct rtw89_dev *rtwdev) +{ +	struct rtw89_sar_cfg_acpi *cfg; +	int ret; + +	cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); +	if (!cfg) +		return; + +	ret = rtw89_acpi_evaluate_sar(rtwdev, cfg); +	if (ret) { +		rtw89_debug(rtwdev, RTW89_DBG_SAR, +			    "evaluating ACPI SAR returns %d\n", ret); +		goto out; +	} + +	if (unlikely(!cfg->valid_num)) { +		rtw89_debug(rtwdev, RTW89_DBG_SAR, "no valid SAR table from ACPI\n"); +		goto out; +	} + +	rtw89_apply_sar_acpi(rtwdev, cfg); + +out: +	kfree(cfg); +} + +static bool rtw89_tas_query_sar_config(struct rtw89_dev *rtwdev, s32 *cfg)  { +	const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0);  	const enum rtw89_sar_sources src = rtwdev->sar.src;  	/* its members are protected by rtw89_sar_set_src() */  	const struct rtw89_sar_handler *sar_hdl = &rtw89_sar_handlers[src]; -	struct rtw89_tas_info *tas = &rtwdev->tas; -	s32 txpwr_avg = tas->total_txpwr / RTW89_TAS_MAX_WINDOW / PERCENT; -	s32 dpr_on_threshold, dpr_off_threshold, cfg; -	enum rtw89_tas_state state = tas->state; -	const struct rtw89_chan *chan; +	struct rtw89_sar_parm sar_parm = {};  	int ret; -	lockdep_assert_held(&rtwdev->mutex); -  	if (src == RTW89_SAR_SOURCE_NONE) -		return; +		return false; -	chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0); -	ret = sar_hdl->query_sar_config(rtwdev, chan->freq, &cfg); +	sar_parm.center_freq = chan->freq; +	ret = sar_hdl->query_sar_config(rtwdev, &sar_parm, cfg);  	if (ret) +		return false; + +	*cfg = rtw89_txpwr_sar_to_tas(sar_hdl, *cfg); + +	return true; +} + +static bool __rtw89_tas_state_update(struct rtw89_dev *rtwdev, +				     enum rtw89_tas_state state) +{ +	struct rtw89_tas_info *tas = &rtwdev->tas; + +	if (tas->state == state) +		return false; + +	rtw89_debug(rtwdev, RTW89_DBG_SAR, "tas: switch state: %s -> %s\n", +		    rtw89_tas_state_str(tas->state), rtw89_tas_state_str(state)); + +	tas->state = state; +	return true; +} + +static void rtw89_tas_state_update(struct rtw89_dev *rtwdev, +				   enum rtw89_tas_state state) +{ +	if (!__rtw89_tas_state_update(rtwdev, state))  		return; -	cfg = rtw89_txpwr_sar_to_tas(sar_hdl, cfg); +	rtw89_core_set_chip_txpwr(rtwdev); +} + +static u32 rtw89_tas_get_window_size(struct rtw89_dev *rtwdev) +{ +	const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0); +	u8 band = chan->band_type; +	u8 regd = rtw89_regd_get(rtwdev, band); -	if (tas->delta >= cfg) { +	switch (regd) { +	default:  		rtw89_debug(rtwdev, RTW89_DBG_SAR, -			    "TAS delta exceed SAR limit\n"); -		state = RTW89_TAS_STATE_DPR_FORBID; -		goto out; +			    "tas: regd: %u is unhandled\n", regd); +		fallthrough; +	case RTW89_IC: +	case RTW89_KCC: +		return 180; +	case RTW89_FCC: +		switch (band) { +		case RTW89_BAND_2G: +			return 50; +		case RTW89_BAND_5G: +			return 30; +		case RTW89_BAND_6G: +		default: +			return 15; +		} +		break; +	} +} + +static void rtw89_tas_window_update(struct rtw89_dev *rtwdev) +{ +	u32 window_size = rtw89_tas_get_window_size(rtwdev); +	struct rtw89_tas_info *tas = &rtwdev->tas; +	u64 total_txpwr = 0; +	u8 head_idx; +	u32 i, j; + +	WARN_ON_ONCE(tas->window_size > RTW89_TAS_TXPWR_WINDOW); + +	if (tas->window_size == window_size) +		return; + +	rtw89_debug(rtwdev, RTW89_DBG_SAR, "tas: window update: %u -> %u\n", +		    tas->window_size, window_size); + +	head_idx = (tas->txpwr_tail_idx - window_size + 1 + RTW89_TAS_TXPWR_WINDOW) % +		   RTW89_TAS_TXPWR_WINDOW; +	for (i = 0; i < window_size; i++) { +		j = (head_idx + i) % RTW89_TAS_TXPWR_WINDOW; +		total_txpwr += tas->txpwr_history[j]; +	} + +	tas->window_size = window_size; +	tas->total_txpwr = total_txpwr; +	tas->txpwr_head_idx = head_idx; +} + +static void rtw89_tas_history_update(struct rtw89_dev *rtwdev) +{ +	struct rtw89_bb_ctx *bb = rtw89_get_bb_ctx(rtwdev, RTW89_PHY_0); +	struct rtw89_env_monitor_info *env = &bb->env_monitor; +	struct rtw89_tas_info *tas = &rtwdev->tas; +	u8 tx_ratio = env->ifs_clm_tx_ratio; +	u64 instant_txpwr, txpwr; + +	/* txpwr in unit of linear(mW) multiply by percentage */ +	if (tx_ratio == 0) { +		/* special case: idle tx power +		 * use -40 dBm * 100 tx ratio +		 */ +		instant_txpwr = rtw89_db_to_linear(-40); +		txpwr = instant_txpwr * 100; +	} else { +		instant_txpwr = tas->instant_txpwr; +		txpwr = instant_txpwr * tx_ratio;  	} -	dpr_on_threshold = cfg; -	dpr_off_threshold = cfg - tas->dpr_gap; +	tas->total_txpwr += txpwr - tas->txpwr_history[tas->txpwr_head_idx]; +	tas->total_tx_ratio += tx_ratio - tas->tx_ratio_history[tas->tx_ratio_idx]; +	tas->tx_ratio_history[tas->tx_ratio_idx] = tx_ratio; + +	tas->txpwr_head_idx = (tas->txpwr_head_idx + 1) % RTW89_TAS_TXPWR_WINDOW; +	tas->txpwr_tail_idx = (tas->txpwr_tail_idx + 1) % RTW89_TAS_TXPWR_WINDOW; +	tas->tx_ratio_idx = (tas->tx_ratio_idx + 1) % RTW89_TAS_TX_RATIO_WINDOW; +	tas->txpwr_history[tas->txpwr_tail_idx] = txpwr; + +	rtw89_debug(rtwdev, RTW89_DBG_SAR, +		    "tas: instant_txpwr: %d, tx_ratio: %u, txpwr: %d\n", +		    rtw89_linear_to_db_quarter(instant_txpwr), tx_ratio, +		    rtw89_linear_to_db_quarter(div_u64(txpwr, PERCENT))); +} + +static bool rtw89_tas_rolling_average(struct rtw89_dev *rtwdev) +{ +	struct rtw89_tas_info *tas = &rtwdev->tas; +	s32 dpr_on_threshold, dpr_off_threshold; +	enum rtw89_tas_state state; +	u16 tx_ratio_avg; +	s32 txpwr_avg; +	u64 linear; + +	linear = DIV_ROUND_DOWN_ULL(tas->total_txpwr, tas->window_size * PERCENT); +	txpwr_avg = rtw89_linear_to_db_quarter(linear); +	tx_ratio_avg = tas->total_tx_ratio / RTW89_TAS_TX_RATIO_WINDOW; +	dpr_on_threshold = tas->dpr_on_threshold; +	dpr_off_threshold = tas->dpr_off_threshold; +  	rtw89_debug(rtwdev, RTW89_DBG_SAR, -		    "DPR_ON thold: %d, DPR_OFF thold: %d, txpwr_avg: %d\n", -		    dpr_on_threshold, dpr_off_threshold, txpwr_avg); +		    "tas: DPR_ON: %d, DPR_OFF: %d, txpwr_avg: %d, tx_ratio_avg: %u\n", +		    dpr_on_threshold, dpr_off_threshold, txpwr_avg, tx_ratio_avg); -	if (txpwr_avg >= dpr_on_threshold) +	if (tx_ratio_avg >= RTW89_TAS_TX_RATIO_THRESHOLD) +		state = RTW89_TAS_STATE_STATIC_SAR; +	else if (txpwr_avg >= dpr_on_threshold)  		state = RTW89_TAS_STATE_DPR_ON;  	else if (txpwr_avg < dpr_off_threshold)  		state = RTW89_TAS_STATE_DPR_OFF; +	else +		return false; -out: -	if (tas->state == state) -		return; - -	rtw89_debug(rtwdev, RTW89_DBG_SAR, -		    "TAS old state: %d, new state: %d\n", tas->state, state); -	tas->state = state; -	rtw89_core_set_chip_txpwr(rtwdev); +	return __rtw89_tas_state_update(rtwdev, state);  } -void rtw89_tas_init(struct rtw89_dev *rtwdev) +static void rtw89_tas_init(struct rtw89_dev *rtwdev)  { +	const struct rtw89_chip_info *chip = rtwdev->chip;  	struct rtw89_tas_info *tas = &rtwdev->tas; +	const struct rtw89_acpi_policy_tas *ptr;  	struct rtw89_acpi_dsm_result res = {};  	int ret; -	u8 val; + +	if (!chip->support_tas) +		return;  	ret = rtw89_acpi_evaluate_dsm(rtwdev, RTW89_ACPI_DSM_FUNC_TAS_EN, &res);  	if (ret) { @@ -374,8 +712,9 @@ void rtw89_tas_init(struct rtw89_dev *rtwdev)  		return;  	} -	val = res.u.value; -	switch (val) { +	ptr = res.u.policy_tas; + +	switch (ptr->enable) {  	case 0:  		tas->enable = false;  		break; @@ -388,66 +727,170 @@ void rtw89_tas_init(struct rtw89_dev *rtwdev)  	if (!tas->enable) {  		rtw89_debug(rtwdev, RTW89_DBG_SAR, "TAS not enable\n"); -		return; +		goto out;  	} -	tas->dpr_gap = RTW89_TAS_DPR_GAP; -	tas->delta = RTW89_TAS_DELTA; +	tas->enabled_countries = ptr->enabled_countries; + +out: +	kfree(ptr);  } -void rtw89_tas_reset(struct rtw89_dev *rtwdev) +void rtw89_tas_reset(struct rtw89_dev *rtwdev, bool force)  { +	const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0);  	struct rtw89_tas_info *tas = &rtwdev->tas; +	u64 linear; +	s32 cfg; +	int i; -	if (!tas->enable) +	if (!rtw89_tas_is_active(rtwdev)) +		return; + +	if (!rtw89_tas_query_sar_config(rtwdev, &cfg))  		return; -	memset(&tas->txpwr_history, 0, sizeof(tas->txpwr_history)); -	tas->total_txpwr = 0; -	tas->cur_idx = 0; +	tas->dpr_on_threshold = cfg - RTW89_TAS_SAR_GAP; +	tas->dpr_off_threshold = cfg - RTW89_TAS_SAR_GAP - RTW89_TAS_DPR_GAP; + +	/* avoid history reset after new SAR apply */ +	if (!force && tas->keep_history) +		return; + +	linear = rtw89_db_quarter_to_linear(cfg) * RTW89_TAS_DFLT_TX_RATIO; +	for (i = 0; i < RTW89_TAS_TXPWR_WINDOW; i++) +		tas->txpwr_history[i] = linear; + +	for (i = 0; i < RTW89_TAS_TX_RATIO_WINDOW; i++) +		tas->tx_ratio_history[i] = RTW89_TAS_DFLT_TX_RATIO; + +	tas->total_tx_ratio = RTW89_TAS_DFLT_TX_RATIO * RTW89_TAS_TX_RATIO_WINDOW; +	tas->total_txpwr = linear * RTW89_TAS_TXPWR_WINDOW; +	tas->window_size = RTW89_TAS_TXPWR_WINDOW; +	tas->txpwr_head_idx = 0; +	tas->txpwr_tail_idx = RTW89_TAS_TXPWR_WINDOW - 1; +	tas->tx_ratio_idx = 0;  	tas->state = RTW89_TAS_STATE_DPR_OFF; +	tas->backup_state = RTW89_TAS_STATE_DPR_OFF; +	tas->keep_history = true; + +	rtw89_debug(rtwdev, RTW89_DBG_SAR, +		    "tas: band: %u, freq: %u\n", chan->band_type, chan->freq);  } -static const struct rtw89_reg_def txpwr_regs[] = { -	{R_PATH0_TXPWR, B_PATH0_TXPWR}, -	{R_PATH1_TXPWR, B_PATH1_TXPWR}, -}; +static bool rtw89_tas_track(struct rtw89_dev *rtwdev) +{ +	struct rtw89_tas_info *tas = &rtwdev->tas; +	struct rtw89_hal *hal = &rtwdev->hal; +	s32 cfg; + +	if (hal->disabled_dm_bitmap & BIT(RTW89_DM_TAS)) +		return false; + +	if (!rtw89_tas_is_active(rtwdev)) +		return false; + +	if (!rtw89_tas_query_sar_config(rtwdev, &cfg) || tas->block_regd) +		return __rtw89_tas_state_update(rtwdev, RTW89_TAS_STATE_STATIC_SAR); -void rtw89_tas_track(struct rtw89_dev *rtwdev) +	if (tas->pause) +		return false; + +	rtw89_tas_window_update(rtwdev); +	rtw89_tas_history_update(rtwdev); + +	return rtw89_tas_rolling_average(rtwdev); +} + +void rtw89_tas_scan(struct rtw89_dev *rtwdev, bool start)  { -	struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; -	const enum rtw89_sar_sources src = rtwdev->sar.src; -	u8 max_nss_num = rtwdev->chip->rf_path_num;  	struct rtw89_tas_info *tas = &rtwdev->tas; -	s16 tmp, txpwr, instant_txpwr = 0; -	u32 val; -	int i; +	s32 cfg; + +	if (!rtw89_tas_is_active(rtwdev)) +		return; + +	if (!rtw89_tas_query_sar_config(rtwdev, &cfg)) +		return; + +	if (start) { +		tas->backup_state = tas->state; +		rtw89_tas_state_update(rtwdev, RTW89_TAS_STATE_STATIC_SAR); +	} else { +		rtw89_tas_state_update(rtwdev, tas->backup_state); +	} +} -	if (!tas->enable || src == RTW89_SAR_SOURCE_NONE) +void rtw89_tas_chanctx_cb(struct rtw89_dev *rtwdev, +			  enum rtw89_chanctx_state state) +{ +	struct rtw89_tas_info *tas = &rtwdev->tas; +	s32 cfg; + +	if (!rtw89_tas_is_active(rtwdev))  		return; -	if (env->ccx_watchdog_result != RTW89_PHY_ENV_MON_IFS_CLM) +	if (!rtw89_tas_query_sar_config(rtwdev, &cfg))  		return; -	for (i = 0; i < max_nss_num; i++) { -		val = rtw89_phy_read32_mask(rtwdev, txpwr_regs[i].addr, -					    txpwr_regs[i].mask); -		tmp = sign_extend32(val, 8); -		if (tmp <= 0) -			return; -		instant_txpwr += tmp; +	switch (state) { +	case RTW89_CHANCTX_STATE_MCC_START: +		tas->pause = true; +		rtw89_tas_state_update(rtwdev, RTW89_TAS_STATE_STATIC_SAR); +		break; +	case RTW89_CHANCTX_STATE_MCC_STOP: +		tas->pause = false; +		break; +	default: +		break;  	} +} +EXPORT_SYMBOL(rtw89_tas_chanctx_cb); + +void rtw89_sar_init(struct rtw89_dev *rtwdev) +{ +	rtw89_set_sar_from_acpi(rtwdev); +	rtw89_tas_init(rtwdev); +} + +static bool rtw89_sar_track_acpi(struct rtw89_dev *rtwdev) +{ +	struct rtw89_sar_cfg_acpi *cfg = &rtwdev->sar.cfg_acpi; +	struct rtw89_sar_indicator_from_acpi *ind = &cfg->indicator; +	const enum rtw89_sar_sources src = rtwdev->sar.src; +	bool changed; +	int ret; + +	lockdep_assert_wiphy(rtwdev->hw->wiphy); + +	if (src != RTW89_SAR_SOURCE_ACPI) +		return false; + +	if (!ind->enable_sync) +		return false; + +	ret = rtw89_acpi_evaluate_dynamic_sar_indicator(rtwdev, cfg, &changed); +	if (likely(!ret)) +		return changed; -	instant_txpwr /= max_nss_num; -	/* in unit of 0.25 dBm multiply by percentage */ -	txpwr = instant_txpwr * env->ifs_clm_tx_ratio; -	tas->total_txpwr += txpwr - tas->txpwr_history[tas->cur_idx]; -	tas->txpwr_history[tas->cur_idx] = txpwr;  	rtw89_debug(rtwdev, RTW89_DBG_SAR, -		    "instant_txpwr: %d, tx_ratio: %d, txpwr: %d\n", -		    instant_txpwr, env->ifs_clm_tx_ratio, txpwr); +		    "%s: failed to track indicator: %d; reset and disable\n", +		    __func__, ret); -	tas->cur_idx = (tas->cur_idx + 1) % RTW89_TAS_MAX_WINDOW; +	memset(ind->tblsel, 0, sizeof(ind->tblsel)); +	ind->enable_sync = false; +	return true; +} + +void rtw89_sar_track(struct rtw89_dev *rtwdev) +{ +	unsigned int changes = 0; + +	changes += rtw89_sar_track_acpi(rtwdev); +	changes += rtw89_tas_track(rtwdev); -	rtw89_tas_state_update(rtwdev); +	if (!changes) +		return; + +	rtw89_core_set_chip_txpwr(rtwdev);  } diff --git a/sys/contrib/dev/rtw89/sar.h b/sys/contrib/dev/rtw89/sar.h index 4ae081d2d3b4..4b7f3d44f57b 100644 --- a/sys/contrib/dev/rtw89/sar.h +++ b/sys/contrib/dev/rtw89/sar.h @@ -10,21 +10,34 @@  #define RTW89_SAR_TXPWR_MAC_MAX 63  #define RTW89_SAR_TXPWR_MAC_MIN -64 +struct rtw89_sar_parm { +	u32 center_freq; +	enum rtw89_ntx ntx; + +	bool force_path; +	enum rtw89_rf_path path; +}; +  struct rtw89_sar_handler {  	const char *descr_sar_source;  	u8 txpwr_factor_sar; -	int (*query_sar_config)(struct rtw89_dev *rtwdev, u32 center_freq, s32 *cfg); +	int (*query_sar_config)(struct rtw89_dev *rtwdev, +				const struct rtw89_sar_parm *sar_parm, s32 *cfg);  };  extern const struct cfg80211_sar_capa rtw89_sar_capa; -s8 rtw89_query_sar(struct rtw89_dev *rtwdev, u32 center_freq); -void rtw89_print_sar(struct seq_file *m, struct rtw89_dev *rtwdev, u32 center_freq); -void rtw89_print_tas(struct seq_file *m, struct rtw89_dev *rtwdev); +s8 rtw89_query_sar(struct rtw89_dev *rtwdev, const struct rtw89_sar_parm *sar_parm); +int rtw89_print_sar(struct rtw89_dev *rtwdev, char *buf, size_t bufsz, +		    const struct rtw89_sar_parm *sar_parm); +int rtw89_print_tas(struct rtw89_dev *rtwdev, char *buf, size_t bufsz);  int rtw89_ops_set_sar_specs(struct ieee80211_hw *hw,  			    const struct cfg80211_sar_specs *sar); -void rtw89_tas_init(struct rtw89_dev *rtwdev); -void rtw89_tas_reset(struct rtw89_dev *rtwdev); -void rtw89_tas_track(struct rtw89_dev *rtwdev); +void rtw89_tas_reset(struct rtw89_dev *rtwdev, bool force); +void rtw89_tas_scan(struct rtw89_dev *rtwdev, bool start); +void rtw89_tas_chanctx_cb(struct rtw89_dev *rtwdev, +			  enum rtw89_chanctx_state state); +void rtw89_sar_init(struct rtw89_dev *rtwdev); +void rtw89_sar_track(struct rtw89_dev *rtwdev);  #endif diff --git a/sys/contrib/dev/rtw89/ser.c b/sys/contrib/dev/rtw89/ser.c index 6014e765c319..0459a7a73647 100644 --- a/sys/contrib/dev/rtw89/ser.c +++ b/sys/contrib/dev/rtw89/ser.c @@ -156,9 +156,9 @@ static void ser_state_run(struct rtw89_ser *ser, u8 evt)  	rtw89_debug(rtwdev, RTW89_DBG_SER, "ser: %s receive %s\n",  		    ser_st_name(ser), ser_ev_name(ser, evt)); -	mutex_lock(&rtwdev->mutex); +	wiphy_lock(rtwdev->hw->wiphy);  	rtw89_leave_lps(rtwdev); -	mutex_unlock(&rtwdev->mutex); +	wiphy_unlock(rtwdev->hw->wiphy);  	ser->st_tbl[ser->state].st_func(ser, evt);  } @@ -309,6 +309,9 @@ static void ser_reset_vif(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)  		rtw89_core_release_bit_map(rtwdev->hw_port, rtwvif_link->port);  		rtwvif_link->net_type = RTW89_NET_TYPE_NO_LINK;  		rtwvif_link->trigger = false; +		rtwvif_link->rand_tsf_done = false; + +		rtw89_p2p_noa_once_deinit(rtwvif_link);  	}  } @@ -483,10 +486,14 @@ static void ser_l1_reset_pre_st_hdl(struct rtw89_ser *ser, u8 evt)  static void ser_reset_trx_st_hdl(struct rtw89_ser *ser, u8 evt)  {  	struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser); +	struct wiphy *wiphy = rtwdev->hw->wiphy;  	switch (evt) {  	case SER_EV_STATE_IN: -		cancel_delayed_work_sync(&rtwdev->track_work); +		wiphy_lock(wiphy); +		wiphy_delayed_work_cancel(wiphy, &rtwdev->track_work); +		wiphy_delayed_work_cancel(wiphy, &rtwdev->track_ps_work); +		wiphy_unlock(wiphy);  		drv_stop_tx(ser);  		if (hal_stop_dma(ser)) { @@ -517,8 +524,10 @@ static void ser_reset_trx_st_hdl(struct rtw89_ser *ser, u8 evt)  		hal_enable_dma(ser);  		drv_resume_rx(ser);  		drv_resume_tx(ser); -		ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->track_work, -					     RTW89_TRACK_WORK_PERIOD); +		wiphy_delayed_work_queue(wiphy, &rtwdev->track_work, +					 RTW89_TRACK_WORK_PERIOD); +		wiphy_delayed_work_queue(wiphy, &rtwdev->track_ps_work, +					 RTW89_TRACK_PS_WORK_PERIOD);  		break;  	default: @@ -560,21 +569,22 @@ static void ser_mac_mem_dump(struct rtw89_dev *rtwdev, u8 *buf,  	const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;  	u32 filter_model_addr = mac->filter_model_addr;  	u32 indir_access_addr = mac->indir_access_addr; +	u32 mem_page_size = mac->mem_page_size;  	u32 *ptr = (u32 *)buf;  	u32 base_addr, start_page, residue;  	u32 cnt = 0;  	u32 i; -	start_page = start_addr / MAC_MEM_DUMP_PAGE_SIZE; -	residue = start_addr % MAC_MEM_DUMP_PAGE_SIZE; +	start_page = start_addr / mem_page_size; +	residue = start_addr % mem_page_size;  	base_addr = mac->mem_base_addrs[sel]; -	base_addr += start_page * MAC_MEM_DUMP_PAGE_SIZE; +	base_addr += start_page * mem_page_size;  	while (cnt < len) {  		rtw89_write32(rtwdev, filter_model_addr, base_addr);  		for (i = indir_access_addr + residue; -		     i < indir_access_addr + MAC_MEM_DUMP_PAGE_SIZE; +		     i < indir_access_addr + mem_page_size;  		     i += 4, ptr++) {  			*ptr = rtw89_read32(rtwdev, i);  			cnt += 4; @@ -583,7 +593,7 @@ static void ser_mac_mem_dump(struct rtw89_dev *rtwdev, u8 *buf,  		}  		residue = 0; -		base_addr += MAC_MEM_DUMP_PAGE_SIZE; +		base_addr += mem_page_size;  	}  } @@ -712,9 +722,9 @@ static void ser_l2_reset_st_hdl(struct rtw89_ser *ser, u8 evt)  	switch (evt) {  	case SER_EV_STATE_IN: -		mutex_lock(&rtwdev->mutex); +		wiphy_lock(rtwdev->hw->wiphy);  		ser_l2_reset_st_pre_hdl(ser); -		mutex_unlock(&rtwdev->mutex); +		wiphy_unlock(rtwdev->hw->wiphy);  		ieee80211_restart_hw(rtwdev->hw);  		ser_set_alarm(ser, SER_RECFG_TIMEOUT, SER_EV_L2_RECFG_TIMEOUT); diff --git a/sys/contrib/dev/rtw89/txrx.h b/sys/contrib/dev/rtw89/txrx.h index 70fe7cebc9d5..ec01bfc363da 100644 --- a/sys/contrib/dev/rtw89/txrx.h +++ b/sys/contrib/dev/rtw89/txrx.h @@ -73,6 +73,7 @@ static inline u8 rtw89_get_data_nss(struct rtw89_dev *rtwdev, u16 hw_rate)  #define RTW89_TXWD_BODY0_FW_DL BIT(20)  #define RTW89_TXWD_BODY0_CHANNEL_DMA GENMASK(19, 16)  #define RTW89_TXWD_BODY0_HDR_LLC_LEN GENMASK(15, 11) +#define RTW89_TXWD_BODY0_STF_MODE BIT(10)  #define RTW89_TXWD_BODY0_WD_PAGE BIT(7)  #define RTW89_TXWD_BODY0_HW_AMSDU BIT(5)  #define RTW89_TXWD_BODY0_HW_SSN_SEL GENMASK(3, 2) @@ -712,6 +713,25 @@ static inline u8 rtw89_core_get_qsel(struct rtw89_dev *rtwdev, u8 tid)  	}  } +static inline u8 +rtw89_core_get_qsel_mgmt(struct rtw89_dev *rtwdev, struct rtw89_core_tx_request *tx_req) +{ +	struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info; +	struct rtw89_vif_link *rtwvif_link = tx_req->rtwvif_link; + +	if (desc_info->hiq) { +		if (rtwvif_link->mac_idx == RTW89_MAC_1) +			return RTW89_TX_QSEL_B1_HI; +		else +			return RTW89_TX_QSEL_B0_HI; +	} + +	if (rtwvif_link->mac_idx == RTW89_MAC_1) +		return RTW89_TX_QSEL_B1_MGMT; +	else +		return RTW89_TX_QSEL_B0_MGMT; +} +  static inline u8 rtw89_core_get_ch_dma(struct rtw89_dev *rtwdev, u8 qsel)  {  	switch (qsel) { @@ -719,12 +739,24 @@ static inline u8 rtw89_core_get_ch_dma(struct rtw89_dev *rtwdev, u8 qsel)  		rtw89_warn(rtwdev, "Cannot map qsel to dma: %d\n", qsel);  		fallthrough;  	case RTW89_TX_QSEL_BE_0: +	case RTW89_TX_QSEL_BE_1: +	case RTW89_TX_QSEL_BE_2: +	case RTW89_TX_QSEL_BE_3:  		return RTW89_TXCH_ACH0;  	case RTW89_TX_QSEL_BK_0: +	case RTW89_TX_QSEL_BK_1: +	case RTW89_TX_QSEL_BK_2: +	case RTW89_TX_QSEL_BK_3:  		return RTW89_TXCH_ACH1;  	case RTW89_TX_QSEL_VI_0: +	case RTW89_TX_QSEL_VI_1: +	case RTW89_TX_QSEL_VI_2: +	case RTW89_TX_QSEL_VI_3:  		return RTW89_TXCH_ACH2;  	case RTW89_TX_QSEL_VO_0: +	case RTW89_TX_QSEL_VO_1: +	case RTW89_TX_QSEL_VO_2: +	case RTW89_TX_QSEL_VO_3:  		return RTW89_TXCH_ACH3;  	case RTW89_TX_QSEL_B0_MGMT:  		return RTW89_TXCH_CH8; diff --git a/sys/contrib/dev/rtw89/usb.c b/sys/contrib/dev/rtw89/usb.c new file mode 100644 index 000000000000..6cf89aee252e --- /dev/null +++ b/sys/contrib/dev/rtw89/usb.c @@ -0,0 +1,1042 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2025  Realtek Corporation + */ + +#include <linux/usb.h> +#include "debug.h" +#include "mac.h" +#include "reg.h" +#include "txrx.h" +#include "usb.h" + +static void rtw89_usb_read_port_complete(struct urb *urb); + +static void rtw89_usb_vendorreq(struct rtw89_dev *rtwdev, u32 addr, +				void *data, u16 len, u8 reqtype) +{ +	struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); +	struct usb_device *udev = rtwusb->udev; +	unsigned int pipe; +	u16 value, index; +	int attempt, ret; + +	if (test_bit(RTW89_FLAG_UNPLUGGED, rtwdev->flags)) +		return; + +	value = u32_get_bits(addr, GENMASK(15, 0)); +	index = u32_get_bits(addr, GENMASK(23, 16)); + +	for (attempt = 0; attempt < 10; attempt++) { +		*rtwusb->vendor_req_buf = 0; + +		if (reqtype == RTW89_USB_VENQT_READ) { +			pipe = usb_rcvctrlpipe(udev, 0); +		} else { /* RTW89_USB_VENQT_WRITE */ +			pipe = usb_sndctrlpipe(udev, 0); + +			memcpy(rtwusb->vendor_req_buf, data, len); +		} + +		ret = usb_control_msg(udev, pipe, RTW89_USB_VENQT, reqtype, +				      value, index, rtwusb->vendor_req_buf, +				      len, 500); + +		if (ret == len) { /* Success */ +			atomic_set(&rtwusb->continual_io_error, 0); + +			if (reqtype == RTW89_USB_VENQT_READ) +				memcpy(data, rtwusb->vendor_req_buf, len); + +			break; +		} + +		if (ret == -ESHUTDOWN || ret == -ENODEV) +			set_bit(RTW89_FLAG_UNPLUGGED, rtwdev->flags); +		else if (ret < 0) +			rtw89_warn(rtwdev, +				   "usb %s%u 0x%x fail ret=%d value=0x%x attempt=%d\n", +				   reqtype == RTW89_USB_VENQT_READ ? "read" : "write", +				   len * 8, addr, ret, +				   le32_to_cpup(rtwusb->vendor_req_buf), +				   attempt); +		else if (ret > 0 && reqtype == RTW89_USB_VENQT_READ) +			memcpy(data, rtwusb->vendor_req_buf, len); + +		if (atomic_inc_return(&rtwusb->continual_io_error) > 4) { +			set_bit(RTW89_FLAG_UNPLUGGED, rtwdev->flags); +			break; +		} +	} +} + +static u32 rtw89_usb_read_cmac(struct rtw89_dev *rtwdev, u32 addr) +{ +	u32 addr32, val32, shift; +	__le32 data = 0; +	int count; + +	addr32 = addr & ~0x3; +	shift = (addr & 0x3) * 8; + +	for (count = 0; ; count++) { +		rtw89_usb_vendorreq(rtwdev, addr32, &data, 4, +				    RTW89_USB_VENQT_READ); + +		val32 = le32_to_cpu(data); +		if (val32 != RTW89_R32_DEAD) +			break; + +		if (count >= MAC_REG_POOL_COUNT) { +			rtw89_warn(rtwdev, "%s: addr %#x = %#x\n", +				   __func__, addr32, val32); +			val32 = RTW89_R32_DEAD; +			break; +		} + +		rtw89_write32(rtwdev, R_AX_CK_EN, B_AX_CMAC_ALLCKEN); +	} + +	return val32 >> shift; +} + +static u8 rtw89_usb_ops_read8(struct rtw89_dev *rtwdev, u32 addr) +{ +	u8 data = 0; + +	if (ACCESS_CMAC(addr)) +		return rtw89_usb_read_cmac(rtwdev, addr); + +	rtw89_usb_vendorreq(rtwdev, addr, &data, 1, RTW89_USB_VENQT_READ); + +	return data; +} + +static u16 rtw89_usb_ops_read16(struct rtw89_dev *rtwdev, u32 addr) +{ +	__le16 data = 0; + +	if (ACCESS_CMAC(addr)) +		return rtw89_usb_read_cmac(rtwdev, addr); + +	rtw89_usb_vendorreq(rtwdev, addr, &data, 2, RTW89_USB_VENQT_READ); + +	return le16_to_cpu(data); +} + +static u32 rtw89_usb_ops_read32(struct rtw89_dev *rtwdev, u32 addr) +{ +	__le32 data = 0; + +	if (ACCESS_CMAC(addr)) +		return rtw89_usb_read_cmac(rtwdev, addr); + +	rtw89_usb_vendorreq(rtwdev, addr, &data, 4, +			    RTW89_USB_VENQT_READ); + +	return le32_to_cpu(data); +} + +static void rtw89_usb_ops_write8(struct rtw89_dev *rtwdev, u32 addr, u8 val) +{ +	u8 data = val; + +	rtw89_usb_vendorreq(rtwdev, addr, &data, 1, RTW89_USB_VENQT_WRITE); +} + +static void rtw89_usb_ops_write16(struct rtw89_dev *rtwdev, u32 addr, u16 val) +{ +	__le16 data = cpu_to_le16(val); + +	rtw89_usb_vendorreq(rtwdev, addr, &data, 2, RTW89_USB_VENQT_WRITE); +} + +static void rtw89_usb_ops_write32(struct rtw89_dev *rtwdev, u32 addr, u32 val) +{ +	__le32 data = cpu_to_le32(val); + +	rtw89_usb_vendorreq(rtwdev, addr, &data, 4, RTW89_USB_VENQT_WRITE); +} + +static u32 +rtw89_usb_ops_check_and_reclaim_tx_resource(struct rtw89_dev *rtwdev, +					    u8 txch) +{ +	if (txch == RTW89_TXCH_CH12) +		return 1; + +	return 42; /* TODO some kind of calculation? */ +} + +static u8 rtw89_usb_get_bulkout_id(u8 ch_dma) +{ +	switch (ch_dma) { +	case RTW89_DMA_ACH0: +		return 3; +	case RTW89_DMA_ACH1: +		return 4; +	case RTW89_DMA_ACH2: +		return 5; +	case RTW89_DMA_ACH3: +		return 6; +	default: +	case RTW89_DMA_B0MG: +		return 0; +	case RTW89_DMA_B0HI: +		return 1; +	case RTW89_DMA_H2C: +		return 2; +	} +} + +static void rtw89_usb_write_port_complete(struct urb *urb) +{ +	struct rtw89_usb_tx_ctrl_block *txcb = urb->context; +	struct rtw89_dev *rtwdev = txcb->rtwdev; +	struct ieee80211_tx_info *info; +	struct rtw89_txwd_body *txdesc; +	struct sk_buff *skb; +	u32 txdesc_size; + +	while (true) { +		skb = skb_dequeue(&txcb->tx_ack_queue); +		if (!skb) +			break; + +		if (txcb->txch == RTW89_TXCH_CH12) { +			dev_kfree_skb_any(skb); +			continue; +		} + +		txdesc = (struct rtw89_txwd_body *)skb->data; + +		txdesc_size = rtwdev->chip->txwd_body_size; +		if (le32_get_bits(txdesc->dword0, RTW89_TXWD_BODY0_WD_INFO_EN)) +			txdesc_size += rtwdev->chip->txwd_info_size; + +		skb_pull(skb, txdesc_size); + +		info = IEEE80211_SKB_CB(skb); +		ieee80211_tx_info_clear_status(info); + +		if (urb->status == 0) { +			if (info->flags & IEEE80211_TX_CTL_NO_ACK) +				info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED; +			else +				info->flags |= IEEE80211_TX_STAT_ACK; +		} + +		ieee80211_tx_status_irqsafe(rtwdev->hw, skb); +	} + +	switch (urb->status) { +	case 0: +	case -EPIPE: +	case -EPROTO: +	case -EINPROGRESS: +	case -ENOENT: +	case -ECONNRESET: +		break; +	default: +		set_bit(RTW89_FLAG_UNPLUGGED, rtwdev->flags); +		break; +	} + +	kfree(txcb); +	usb_free_urb(urb); +} + +static int rtw89_usb_write_port(struct rtw89_dev *rtwdev, u8 ch_dma, +				void *data, int len, void *context) +{ +	struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); +	struct usb_device *usbd = rtwusb->udev; +	struct urb *urb; +	u8 bulkout_id = rtw89_usb_get_bulkout_id(ch_dma); +	unsigned int pipe; +	int ret; + +	if (test_bit(RTW89_FLAG_UNPLUGGED, rtwdev->flags)) +		return 0; + +	urb = usb_alloc_urb(0, GFP_ATOMIC); +	if (!urb) +		return -ENOMEM; + +	pipe = usb_sndbulkpipe(usbd, rtwusb->out_pipe[bulkout_id]); + +	usb_fill_bulk_urb(urb, usbd, pipe, data, len, +			  rtw89_usb_write_port_complete, context); +	urb->transfer_flags |= URB_ZERO_PACKET; +	ret = usb_submit_urb(urb, GFP_ATOMIC); + +	if (ret) +		usb_free_urb(urb); + +	if (ret == -ENODEV) +		set_bit(RTW89_FLAG_UNPLUGGED, rtwdev->flags); + +	return ret; +} + +static void rtw89_usb_ops_tx_kick_off(struct rtw89_dev *rtwdev, u8 txch) +{ +	struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); +	struct rtw89_usb_tx_ctrl_block *txcb; +	struct sk_buff *skb; +	int ret; + +	while (true) { +		skb = skb_dequeue(&rtwusb->tx_queue[txch]); +		if (!skb) +			break; + +		txcb = kmalloc(sizeof(*txcb), GFP_ATOMIC); +		if (!txcb) { +			dev_kfree_skb_any(skb); +			continue; +		} + +		txcb->rtwdev = rtwdev; +		txcb->txch = txch; +		skb_queue_head_init(&txcb->tx_ack_queue); + +		skb_queue_tail(&txcb->tx_ack_queue, skb); + +		ret = rtw89_usb_write_port(rtwdev, txch, skb->data, skb->len, +					   txcb); +		if (ret) { +			rtw89_err(rtwdev, "write port txch %d failed: %d\n", +				  txch, ret); + +			skb_dequeue(&txcb->tx_ack_queue); +			kfree(txcb); +			dev_kfree_skb_any(skb); +		} +	} +} + +static int rtw89_usb_tx_write_fwcmd(struct rtw89_dev *rtwdev, +				    struct rtw89_core_tx_request *tx_req) +{ +	struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info; +	struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); +	struct sk_buff *skb = tx_req->skb; +	struct sk_buff *skb512; +	u32 txdesc_size = rtwdev->chip->h2c_desc_size; +	void *txdesc; + +	if (((desc_info->pkt_size + txdesc_size) % 512) == 0) { +		rtw89_debug(rtwdev, RTW89_DBG_HCI, "avoiding multiple of 512\n"); + +		skb512 = dev_alloc_skb(txdesc_size + desc_info->pkt_size + +				       RTW89_USB_MOD512_PADDING); +		if (!skb512) { +			rtw89_err(rtwdev, "%s: failed to allocate skb\n", +				  __func__); + +			return -ENOMEM; +		} + +		skb_pull(skb512, txdesc_size); +		skb_put_data(skb512, skb->data, skb->len); +		skb_put_zero(skb512, RTW89_USB_MOD512_PADDING); + +		dev_kfree_skb_any(skb); +		skb = skb512; +		tx_req->skb = skb512; + +		desc_info->pkt_size += RTW89_USB_MOD512_PADDING; +	} + +	txdesc = skb_push(skb, txdesc_size); +	memset(txdesc, 0, txdesc_size); +	rtw89_chip_fill_txdesc_fwcmd(rtwdev, desc_info, txdesc); + +	skb_queue_tail(&rtwusb->tx_queue[desc_info->ch_dma], skb); + +	return 0; +} + +static int rtw89_usb_ops_tx_write(struct rtw89_dev *rtwdev, +				  struct rtw89_core_tx_request *tx_req) +{ +	struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info; +	struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); +	struct sk_buff *skb = tx_req->skb; +	struct rtw89_txwd_body *txdesc; +	u32 txdesc_size; + +	if ((desc_info->ch_dma == RTW89_TXCH_CH12 || +	     tx_req->tx_type == RTW89_CORE_TX_TYPE_FWCMD) && +	    (desc_info->ch_dma != RTW89_TXCH_CH12 || +	     tx_req->tx_type != RTW89_CORE_TX_TYPE_FWCMD)) { +		rtw89_err(rtwdev, "dma channel %d/TX type %d mismatch\n", +			  desc_info->ch_dma, tx_req->tx_type); +		return -EINVAL; +	} + +	if (desc_info->ch_dma == RTW89_TXCH_CH12) +		return rtw89_usb_tx_write_fwcmd(rtwdev, tx_req); + +	txdesc_size = rtwdev->chip->txwd_body_size; +	if (desc_info->en_wd_info) +		txdesc_size += rtwdev->chip->txwd_info_size; + +	txdesc = skb_push(skb, txdesc_size); +	memset(txdesc, 0, txdesc_size); +	rtw89_chip_fill_txdesc(rtwdev, desc_info, txdesc); + +	le32p_replace_bits(&txdesc->dword0, 1, RTW89_TXWD_BODY0_STF_MODE); + +	skb_queue_tail(&rtwusb->tx_queue[desc_info->ch_dma], skb); + +	return 0; +} + +static void rtw89_usb_rx_handler(struct work_struct *work) +{ +	struct rtw89_usb *rtwusb = container_of(work, struct rtw89_usb, rx_work); +	struct rtw89_dev *rtwdev = rtwusb->rtwdev; +	struct rtw89_rx_desc_info desc_info; +	struct sk_buff *rx_skb; +	struct sk_buff *skb; +	u32 pkt_offset; +	int limit; + +	for (limit = 0; limit < 200; limit++) { +		rx_skb = skb_dequeue(&rtwusb->rx_queue); +		if (!rx_skb) +			break; + +		if (skb_queue_len(&rtwusb->rx_queue) >= RTW89_USB_MAX_RXQ_LEN) { +			rtw89_warn(rtwdev, "rx_queue overflow\n"); +			dev_kfree_skb_any(rx_skb); +			continue; +		} + +		memset(&desc_info, 0, sizeof(desc_info)); +		rtw89_chip_query_rxdesc(rtwdev, &desc_info, rx_skb->data, 0); + +		skb = rtw89_alloc_skb_for_rx(rtwdev, desc_info.pkt_size); +		if (!skb) { +			rtw89_debug(rtwdev, RTW89_DBG_HCI, +				    "failed to allocate RX skb of size %u\n", +				    desc_info.pkt_size); +			continue; +		} + +		pkt_offset = desc_info.offset + desc_info.rxd_len; + +		skb_put_data(skb, rx_skb->data + pkt_offset, +			     desc_info.pkt_size); + +		rtw89_core_rx(rtwdev, &desc_info, skb); + +		if (skb_queue_len(&rtwusb->rx_free_queue) >= RTW89_USB_RX_SKB_NUM) +			dev_kfree_skb_any(rx_skb); +		else +			skb_queue_tail(&rtwusb->rx_free_queue, rx_skb); +	} + +	if (limit == 200) { +		rtw89_debug(rtwdev, RTW89_DBG_HCI, +			    "left %d rx skbs in the queue for later\n", +			    skb_queue_len(&rtwusb->rx_queue)); +		queue_work(rtwusb->rxwq, &rtwusb->rx_work); +	} +} + +static void rtw89_usb_rx_resubmit(struct rtw89_usb *rtwusb, +				  struct rtw89_usb_rx_ctrl_block *rxcb, +				  gfp_t gfp) +{ +	struct rtw89_dev *rtwdev = rtwusb->rtwdev; +	struct sk_buff *rx_skb; +	int ret; + +	rx_skb = skb_dequeue(&rtwusb->rx_free_queue); +	if (!rx_skb) +		rx_skb = alloc_skb(RTW89_USB_RECVBUF_SZ, gfp); + +	if (!rx_skb) +		goto try_later; + +	skb_reset_tail_pointer(rx_skb); +	rx_skb->len = 0; + +	rxcb->rx_skb = rx_skb; + +	usb_fill_bulk_urb(rxcb->rx_urb, rtwusb->udev, +			  usb_rcvbulkpipe(rtwusb->udev, rtwusb->in_pipe), +			  rxcb->rx_skb->data, RTW89_USB_RECVBUF_SZ, +			  rtw89_usb_read_port_complete, rxcb); + +	ret = usb_submit_urb(rxcb->rx_urb, gfp); +	if (ret) { +		skb_queue_tail(&rtwusb->rx_free_queue, rxcb->rx_skb); + +		if (ret == -ENODEV) +			set_bit(RTW89_FLAG_UNPLUGGED, rtwdev->flags); +		else +			rtw89_err(rtwdev, "Err sending rx data urb %d\n", ret); + +		if (ret == -ENOMEM) +			goto try_later; +	} + +	return; + +try_later: +	rxcb->rx_skb = NULL; +	queue_work(rtwusb->rxwq, &rtwusb->rx_urb_work); +} + +static void rtw89_usb_rx_resubmit_work(struct work_struct *work) +{ +	struct rtw89_usb *rtwusb = container_of(work, struct rtw89_usb, rx_urb_work); +	struct rtw89_usb_rx_ctrl_block *rxcb; +	int i; + +	for (i = 0; i < RTW89_USB_RXCB_NUM; i++) { +		rxcb = &rtwusb->rx_cb[i]; + +		if (!rxcb->rx_skb) +			rtw89_usb_rx_resubmit(rtwusb, rxcb, GFP_ATOMIC); +	} +} + +static void rtw89_usb_read_port_complete(struct urb *urb) +{ +	struct rtw89_usb_rx_ctrl_block *rxcb = urb->context; +	struct rtw89_dev *rtwdev = rxcb->rtwdev; +	struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); +	struct sk_buff *skb = rxcb->rx_skb; + +	if (urb->status == 0) { +		if (urb->actual_length > urb->transfer_buffer_length || +		    urb->actual_length < sizeof(struct rtw89_rxdesc_short)) { +			rtw89_err(rtwdev, "failed to get urb length: %d\n", +				  urb->actual_length); +			skb_queue_tail(&rtwusb->rx_free_queue, skb); +		} else { +			skb_put(skb, urb->actual_length); +			skb_queue_tail(&rtwusb->rx_queue, skb); +			queue_work(rtwusb->rxwq, &rtwusb->rx_work); +		} + +		rtw89_usb_rx_resubmit(rtwusb, rxcb, GFP_ATOMIC); +	} else { +		skb_queue_tail(&rtwusb->rx_free_queue, skb); + +		if (atomic_inc_return(&rtwusb->continual_io_error) > 4) +			set_bit(RTW89_FLAG_UNPLUGGED, rtwdev->flags); + +		switch (urb->status) { +		case -EINVAL: +		case -EPIPE: +		case -ENODEV: +		case -ESHUTDOWN: +			set_bit(RTW89_FLAG_UNPLUGGED, rtwdev->flags); +			break; +		case -EPROTO: +		case -EILSEQ: +		case -ETIME: +		case -ECOMM: +		case -EOVERFLOW: +		case -ENOENT: +			break; +		case -EINPROGRESS: +			rtw89_info(rtwdev, "URB is in progress\n"); +			break; +		default: +			rtw89_err(rtwdev, "%s status %d\n", +				  __func__, urb->status); +			break; +		} +	} +} + +static void rtw89_usb_cancel_rx_bufs(struct rtw89_usb *rtwusb) +{ +	struct rtw89_usb_rx_ctrl_block *rxcb; +	int i; + +	for (i = 0; i < RTW89_USB_RXCB_NUM; i++) { +		rxcb = &rtwusb->rx_cb[i]; +		usb_kill_urb(rxcb->rx_urb); +	} +} + +static void rtw89_usb_free_rx_bufs(struct rtw89_usb *rtwusb) +{ +	struct rtw89_usb_rx_ctrl_block *rxcb; +	int i; + +	for (i = 0; i < RTW89_USB_RXCB_NUM; i++) { +		rxcb = &rtwusb->rx_cb[i]; +		usb_free_urb(rxcb->rx_urb); +	} +} + +static int rtw89_usb_alloc_rx_bufs(struct rtw89_usb *rtwusb) +{ +	struct rtw89_usb_rx_ctrl_block *rxcb; +	int i; + +	for (i = 0; i < RTW89_USB_RXCB_NUM; i++) { +		rxcb = &rtwusb->rx_cb[i]; + +		rxcb->rtwdev = rtwusb->rtwdev; +		rxcb->rx_urb = usb_alloc_urb(0, GFP_KERNEL); +		if (!rxcb->rx_urb) { +			rtw89_usb_free_rx_bufs(rtwusb); +			return -ENOMEM; +		} +	} + +	return 0; +} + +static int rtw89_usb_init_rx(struct rtw89_dev *rtwdev) +{ +	struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); +	struct sk_buff *rx_skb; +	int i; + +	rtwusb->rxwq = alloc_workqueue("rtw89_usb: rx wq", WQ_BH, 0); +	if (!rtwusb->rxwq) { +		rtw89_err(rtwdev, "failed to create RX work queue\n"); +		return -ENOMEM; +	} + +	skb_queue_head_init(&rtwusb->rx_queue); +	skb_queue_head_init(&rtwusb->rx_free_queue); + +	INIT_WORK(&rtwusb->rx_work, rtw89_usb_rx_handler); +	INIT_WORK(&rtwusb->rx_urb_work, rtw89_usb_rx_resubmit_work); + +	for (i = 0; i < RTW89_USB_RX_SKB_NUM; i++) { +		rx_skb = alloc_skb(RTW89_USB_RECVBUF_SZ, GFP_KERNEL); +		if (rx_skb) +			skb_queue_tail(&rtwusb->rx_free_queue, rx_skb); +	} + +	return 0; +} + +static void rtw89_usb_deinit_rx(struct rtw89_dev *rtwdev) +{ +	struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); + +	skb_queue_purge(&rtwusb->rx_queue); + +	destroy_workqueue(rtwusb->rxwq); + +	skb_queue_purge(&rtwusb->rx_free_queue); +} + +static void rtw89_usb_start_rx(struct rtw89_dev *rtwdev) +{ +	struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); +	int i; + +	for (i = 0; i < RTW89_USB_RXCB_NUM; i++) +		rtw89_usb_rx_resubmit(rtwusb, &rtwusb->rx_cb[i], GFP_KERNEL); +} + +static void rtw89_usb_init_tx(struct rtw89_dev *rtwdev) +{ +	struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); +	int i; + +	for (i = 0; i < ARRAY_SIZE(rtwusb->tx_queue); i++) +		skb_queue_head_init(&rtwusb->tx_queue[i]); +} + +static void rtw89_usb_deinit_tx(struct rtw89_dev *rtwdev) +{ +	struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); +	int i; + +	for (i = 0; i < ARRAY_SIZE(rtwusb->tx_queue); i++) { +		if (i == RTW89_TXCH_CH12) +			skb_queue_purge(&rtwusb->tx_queue[i]); +		else +			ieee80211_purge_tx_queue(rtwdev->hw, &rtwusb->tx_queue[i]); +	} +} + +static void rtw89_usb_ops_reset(struct rtw89_dev *rtwdev) +{ +	/* TODO: anything to do here? */ +} + +static int rtw89_usb_ops_start(struct rtw89_dev *rtwdev) +{ +	return 0; /* Nothing to do. */ +} + +static void rtw89_usb_ops_stop(struct rtw89_dev *rtwdev) +{ +	/* Nothing to do. */ +} + +static void rtw89_usb_ops_pause(struct rtw89_dev *rtwdev, bool pause) +{ +	/* Nothing to do? */ +} + +static void rtw89_usb_ops_switch_mode(struct rtw89_dev *rtwdev, bool low_power) +{ +	/* Nothing to do. */ +} + +static int rtw89_usb_ops_deinit(struct rtw89_dev *rtwdev) +{ +	return 0; /* Nothing to do. */ +} + +static int rtw89_usb_ops_mac_pre_init(struct rtw89_dev *rtwdev) +{ +	u32 val32; + +	rtw89_write32_set(rtwdev, R_AX_USB_HOST_REQUEST_2, B_AX_R_USBIO_MODE); + +	/* fix USB IO hang suggest by chihhanli@realtek.com */ +	rtw89_write32_clr(rtwdev, R_AX_USB_WLAN0_1, +			  B_AX_USBRX_RST | B_AX_USBTX_RST); + +	val32 = rtw89_read32(rtwdev, R_AX_HCI_FUNC_EN); +	val32 &= ~(B_AX_HCI_RXDMA_EN | B_AX_HCI_TXDMA_EN); +	rtw89_write32(rtwdev, R_AX_HCI_FUNC_EN, val32); + +	val32 |= B_AX_HCI_RXDMA_EN | B_AX_HCI_TXDMA_EN; +	rtw89_write32(rtwdev, R_AX_HCI_FUNC_EN, val32); +	/* fix USB TRX hang suggest by chihhanli@realtek.com */ + +	return 0; +} + +static int rtw89_usb_ops_mac_pre_deinit(struct rtw89_dev *rtwdev) +{ +	return 0; /* Nothing to do. */ +} + +static int rtw89_usb_ops_mac_post_init(struct rtw89_dev *rtwdev) +{ +	struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); +	enum usb_device_speed speed; +	u32 ep; + +	rtw89_write32_clr(rtwdev, R_AX_USB3_MAC_NPI_CONFIG_INTF_0, +			  B_AX_SSPHY_LFPS_FILTER); + +	speed = rtwusb->udev->speed; + +	if (speed == USB_SPEED_SUPER) +		rtw89_write8(rtwdev, R_AX_RXDMA_SETTING, USB3_BULKSIZE); +	else if (speed == USB_SPEED_HIGH) +		rtw89_write8(rtwdev, R_AX_RXDMA_SETTING, USB2_BULKSIZE); +	else +		rtw89_write8(rtwdev, R_AX_RXDMA_SETTING, USB11_BULKSIZE); + +	for (ep = 5; ep <= 12; ep++) { +		if (ep == 8) +			continue; + +		rtw89_write8_mask(rtwdev, R_AX_USB_ENDPOINT_0, +				  B_AX_EP_IDX, ep); +		rtw89_write8(rtwdev, R_AX_USB_ENDPOINT_2 + 1, NUMP); +	} + +	return 0; +} + +static void rtw89_usb_ops_recalc_int_mit(struct rtw89_dev *rtwdev) +{ +	/* Nothing to do. */ +} + +static int rtw89_usb_ops_mac_lv1_rcvy(struct rtw89_dev *rtwdev, +				      enum rtw89_lv1_rcvy_step step) +{ +	u32 reg, mask; + +	switch (rtwdev->chip->chip_id) { +	case RTL8851B: +	case RTL8852A: +	case RTL8852B: +		reg = R_AX_USB_WLAN0_1; +		mask = B_AX_USBRX_RST | B_AX_USBTX_RST; +		break; +	case RTL8852C: +		reg = R_AX_USB_WLAN0_1_V1; +		mask = B_AX_USBRX_RST_V1 | B_AX_USBTX_RST_V1; +		break; +	default: +		rtw89_err(rtwdev, "%s: fix me\n", __func__); +		return -EOPNOTSUPP; +	} + +	switch (step) { +	case RTW89_LV1_RCVY_STEP_1: +		rtw89_write32_set(rtwdev, reg, mask); + +		msleep(30); +		break; +	case RTW89_LV1_RCVY_STEP_2: +		rtw89_write32_clr(rtwdev, reg, mask); +		break; +	default: +		return -EINVAL; +	} + +	return 0; +} + +static void rtw89_usb_ops_dump_err_status(struct rtw89_dev *rtwdev) +{ +	rtw89_warn(rtwdev, "%s TODO\n", __func__); +} + +static const struct rtw89_hci_ops rtw89_usb_ops = { +	.tx_write	= rtw89_usb_ops_tx_write, +	.tx_kick_off	= rtw89_usb_ops_tx_kick_off, +	.flush_queues	= NULL, /* Not needed? */ +	.reset		= rtw89_usb_ops_reset, +	.start		= rtw89_usb_ops_start, +	.stop		= rtw89_usb_ops_stop, +	.pause		= rtw89_usb_ops_pause, +	.switch_mode	= rtw89_usb_ops_switch_mode, +	.recalc_int_mit = rtw89_usb_ops_recalc_int_mit, + +	.read8		= rtw89_usb_ops_read8, +	.read16		= rtw89_usb_ops_read16, +	.read32		= rtw89_usb_ops_read32, +	.write8		= rtw89_usb_ops_write8, +	.write16	= rtw89_usb_ops_write16, +	.write32	= rtw89_usb_ops_write32, + +	.mac_pre_init	= rtw89_usb_ops_mac_pre_init, +	.mac_pre_deinit	= rtw89_usb_ops_mac_pre_deinit, +	.mac_post_init	= rtw89_usb_ops_mac_post_init, +	.deinit		= rtw89_usb_ops_deinit, + +	.check_and_reclaim_tx_resource = rtw89_usb_ops_check_and_reclaim_tx_resource, +	.mac_lv1_rcvy	= rtw89_usb_ops_mac_lv1_rcvy, +	.dump_err_status = rtw89_usb_ops_dump_err_status, +	.napi_poll	= NULL, + +	.recovery_start = NULL, +	.recovery_complete = NULL, + +	.ctrl_txdma_ch	= NULL, +	.ctrl_txdma_fw_ch = NULL, +	.ctrl_trxhci	= NULL, +	.poll_txdma_ch_idle = NULL, + +	.clr_idx_all	= NULL, +	.clear		= NULL, +	.disable_intr	= NULL, +	.enable_intr	= NULL, +	.rst_bdram	= NULL, +}; + +static int rtw89_usb_parse(struct rtw89_dev *rtwdev, +			   struct usb_interface *intf) +{ +	struct usb_host_interface *host_interface = &intf->altsetting[0]; +	struct usb_interface_descriptor *intf_desc = &host_interface->desc; +	struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); +	struct usb_endpoint_descriptor *endpoint; +	int num_out_pipes = 0; +	u8 num; +	int i; + +	if (intf_desc->bNumEndpoints > RTW89_MAX_ENDPOINT_NUM) { +		rtw89_err(rtwdev, "found %d endpoints, expected %d max\n", +			  intf_desc->bNumEndpoints, RTW89_MAX_ENDPOINT_NUM); +		return -EINVAL; +	} + +	for (i = 0; i < intf_desc->bNumEndpoints; i++) { +		endpoint = &host_interface->endpoint[i].desc; +		num = usb_endpoint_num(endpoint); + +		if (usb_endpoint_dir_in(endpoint) && +		    usb_endpoint_xfer_bulk(endpoint)) { +			if (rtwusb->in_pipe) { +				rtw89_err(rtwdev, +					  "found more than 1 bulk in endpoint\n"); +				return -EINVAL; +			} + +			rtwusb->in_pipe = num; +		} + +		if (usb_endpoint_dir_out(endpoint) && +		    usb_endpoint_xfer_bulk(endpoint)) { +			if (num_out_pipes >= RTW89_MAX_BULKOUT_NUM) { +				rtw89_err(rtwdev, +					  "found more than %d bulk out endpoints\n", +					  RTW89_MAX_BULKOUT_NUM); +				return -EINVAL; +			} + +			rtwusb->out_pipe[num_out_pipes++] = num; +		} +	} + +	if (num_out_pipes < 1) { +		rtw89_err(rtwdev, "no bulk out endpoints found\n"); +		return -EINVAL; +	} + +	return 0; +} + +static int rtw89_usb_intf_init(struct rtw89_dev *rtwdev, +			       struct usb_interface *intf) +{ +	struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); +	int ret; + +	ret = rtw89_usb_parse(rtwdev, intf); +	if (ret) +		return ret; + +	rtwusb->vendor_req_buf = kmalloc(sizeof(*rtwusb->vendor_req_buf), +					 GFP_KERNEL); +	if (!rtwusb->vendor_req_buf) +		return -ENOMEM; + +	rtwusb->udev = usb_get_dev(interface_to_usbdev(intf)); + +	usb_set_intfdata(intf, rtwdev->hw); + +	SET_IEEE80211_DEV(rtwdev->hw, &intf->dev); + +	return 0; +} + +static void rtw89_usb_intf_deinit(struct rtw89_dev *rtwdev, +				  struct usb_interface *intf) +{ +	struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev); + +	usb_put_dev(rtwusb->udev); +	kfree(rtwusb->vendor_req_buf); +	usb_set_intfdata(intf, NULL); +} + +int rtw89_usb_probe(struct usb_interface *intf, +		    const struct usb_device_id *id) +{ +	const struct rtw89_driver_info *info; +	struct rtw89_dev *rtwdev; +	struct rtw89_usb *rtwusb; +	int ret; + +	info = (const struct rtw89_driver_info *)id->driver_info; + +	rtwdev = rtw89_alloc_ieee80211_hw(&intf->dev, +					  sizeof(struct rtw89_usb), +					  info->chip, info->variant); +	if (!rtwdev) { +		dev_err(&intf->dev, "failed to allocate hw\n"); +		return -ENOMEM; +	} + +	rtwusb = rtw89_usb_priv(rtwdev); +	rtwusb->rtwdev = rtwdev; + +	rtwdev->hci.ops = &rtw89_usb_ops; +	rtwdev->hci.type = RTW89_HCI_TYPE_USB; + +	ret = rtw89_usb_intf_init(rtwdev, intf); +	if (ret) { +		rtw89_err(rtwdev, "failed to initialise intf: %d\n", ret); +		goto err_free_hw; +	} + +	if (rtwusb->udev->speed == USB_SPEED_SUPER) +		rtwdev->hci.dle_type = RTW89_HCI_DLE_TYPE_USB3; +	else +		rtwdev->hci.dle_type = RTW89_HCI_DLE_TYPE_USB2; + +	rtw89_usb_init_tx(rtwdev); + +	ret = rtw89_usb_alloc_rx_bufs(rtwusb); +	if (ret) +		goto err_intf_deinit; + +	ret = rtw89_usb_init_rx(rtwdev); +	if (ret) +		goto err_free_rx_bufs; + +	ret = rtw89_core_init(rtwdev); +	if (ret) { +		rtw89_err(rtwdev, "failed to initialise core: %d\n", ret); +		goto err_deinit_rx; +	} + +	ret = rtw89_chip_info_setup(rtwdev); +	if (ret) { +		rtw89_err(rtwdev, "failed to setup chip information\n"); +		goto err_core_deinit; +	} + +	ret = rtw89_core_register(rtwdev); +	if (ret) { +		rtw89_err(rtwdev, "failed to register core\n"); +		goto err_core_deinit; +	} + +	rtw89_usb_start_rx(rtwdev); + +	set_bit(RTW89_FLAG_PROBE_DONE, rtwdev->flags); + +	return 0; + +err_core_deinit: +	rtw89_core_deinit(rtwdev); +err_deinit_rx: +	rtw89_usb_deinit_rx(rtwdev); +err_free_rx_bufs: +	rtw89_usb_free_rx_bufs(rtwusb); +err_intf_deinit: +	rtw89_usb_intf_deinit(rtwdev, intf); +err_free_hw: +	rtw89_free_ieee80211_hw(rtwdev); + +	return ret; +} +EXPORT_SYMBOL(rtw89_usb_probe); + +void rtw89_usb_disconnect(struct usb_interface *intf) +{ +	struct ieee80211_hw *hw = usb_get_intfdata(intf); +	struct rtw89_dev *rtwdev; +	struct rtw89_usb *rtwusb; + +	if (!hw) +		return; + +	rtwdev = hw->priv; +	rtwusb = rtw89_usb_priv(rtwdev); + +	rtw89_usb_cancel_rx_bufs(rtwusb); + +	rtw89_core_unregister(rtwdev); +	rtw89_core_deinit(rtwdev); +	rtw89_usb_deinit_rx(rtwdev); +	rtw89_usb_free_rx_bufs(rtwusb); +	rtw89_usb_deinit_tx(rtwdev); +	rtw89_usb_intf_deinit(rtwdev, intf); +	rtw89_free_ieee80211_hw(rtwdev); +} +EXPORT_SYMBOL(rtw89_usb_disconnect); + +MODULE_AUTHOR("Bitterblue Smith <rtl8821cerfe2@gmail.com>"); +MODULE_DESCRIPTION("Realtek USB 802.11ax wireless driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sys/contrib/dev/rtw89/usb.h b/sys/contrib/dev/rtw89/usb.h new file mode 100644 index 000000000000..c1b4bfa20979 --- /dev/null +++ b/sys/contrib/dev/rtw89/usb.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* Copyright(c) 2025  Realtek Corporation + */ + +#ifndef __RTW89_USB_H__ +#define __RTW89_USB_H__ + +#include "txrx.h" + +#define RTW89_USB_VENQT			0x05 +#define RTW89_USB_VENQT_READ		0xc0 +#define RTW89_USB_VENQT_WRITE		0x40 + +#define RTW89_USB_RECVBUF_SZ		20480 +#define RTW89_USB_RXCB_NUM		8 +#define RTW89_USB_RX_SKB_NUM		16 +#define RTW89_USB_MAX_RXQ_LEN		512 +#define RTW89_USB_MOD512_PADDING	4 + +#define RTW89_MAX_ENDPOINT_NUM		9 +#define RTW89_MAX_BULKOUT_NUM		7 + +struct rtw89_usb_rx_ctrl_block { +	struct rtw89_dev *rtwdev; +	struct urb *rx_urb; +	struct sk_buff *rx_skb; +}; + +struct rtw89_usb_tx_ctrl_block { +	struct rtw89_dev *rtwdev; +	u8 txch; +	struct sk_buff_head tx_ack_queue; +}; + +struct rtw89_usb { +	struct rtw89_dev *rtwdev; +	struct usb_device *udev; + +	__le32 *vendor_req_buf; + +	atomic_t continual_io_error; + +	u8 in_pipe; +	u8 out_pipe[RTW89_MAX_BULKOUT_NUM]; + +	struct workqueue_struct *rxwq; +	struct rtw89_usb_rx_ctrl_block rx_cb[RTW89_USB_RXCB_NUM]; +	struct sk_buff_head rx_queue; +	struct sk_buff_head rx_free_queue; +	struct work_struct rx_work; +	struct work_struct rx_urb_work; + +	struct sk_buff_head tx_queue[RTW89_TXCH_NUM]; +}; + +static inline struct rtw89_usb *rtw89_usb_priv(struct rtw89_dev *rtwdev) +{ +	return (struct rtw89_usb *)rtwdev->priv; +} + +int rtw89_usb_probe(struct usb_interface *intf, +		    const struct usb_device_id *id); +void rtw89_usb_disconnect(struct usb_interface *intf); + +#endif diff --git a/sys/contrib/dev/rtw89/util.c b/sys/contrib/dev/rtw89/util.c index e71956ce9853..073714db26f2 100644 --- a/sys/contrib/dev/rtw89/util.c +++ b/sys/contrib/dev/rtw89/util.c @@ -4,103 +4,159 @@  #include "util.h" -#define FRAC_ROWS 3 -#define FRAC_ROW_MAX (FRAC_ROWS - 1) -#define NORM_ROW_MIN FRAC_ROWS - -static const u32 db_invert_table[12][8] = { -	/* rows 0~2 in unit of U(32,3) */ -	{10, 13, 16, 20, 25, 32, 40, 50}, -	{64, 80, 101, 128, 160, 201, 256, 318}, -	{401, 505, 635, 800, 1007, 1268, 1596, 2010}, -	/* rows 3~11 in unit of U(32,0) */ -	{316, 398, 501, 631, 794, 1000, 1259, 1585}, -	{1995, 2512, 3162, 3981, 5012, 6310, 7943, 10000}, -	{12589, 15849, 19953, 25119, 31623, 39811, 50119, 63098}, -	{79433, 100000, 125893, 158489, 199526, 251189, 316228, 398107}, -	{501187, 630957, 794328, 1000000, 1258925, 1584893, 1995262, 2511886}, -	{3162278, 3981072, 5011872, 6309573, 7943282, 1000000, 12589254, -	 15848932}, -	{19952623, 25118864, 31622777, 39810717, 50118723, 63095734, 79432823, -	 100000000}, -	{125892541, 158489319, 199526232, 251188643, 316227766, 398107171, -	 501187234, 630957345}, -	{794328235, 1000000000, 1258925412, 1584893192, 1995262315, 2511886432U, -	 3162277660U, 3981071706U}, +#define RTW89_DBM_QUARTER_FACTOR 2 +#define RTW89_MIN_DBM (-41.25 * (1 << RTW89_DBM_QUARTER_FACTOR)) +#define RTW89_MAX_DBM (96 * (1 << RTW89_DBM_QUARTER_FACTOR)) +#define RTW89_DB_INVERT_TABLE_OFFSET (-RTW89_MIN_DBM) + +static const u64 db_invert_table[] = { +	/* in unit of 0.000001 */ +	75, 79, 84, 89, 94, 100, 106, 112, 119, 126, 133, 141, 150, 158, 168, 178, 188, +	200, 211, 224, 237, 251, 266, 282, 299, 316, 335, 355, 376, 398, 422, 447, 473, +	501, 531, 562, 596, 631, 668, 708, 750, 794, 841, 891, 944, 1000, 1059, 1122, 1189, +	1259, 1334, 1413, 1496, 1585, 1679, 1778, 1884, 1995, 2113, 2239, 2371, 2512, 2661, +	2818, 2985, 3162, 3350, 3548, 3758, 3981, 4217, 4467, 4732, 5012, 5309, 5623, 5957, +	6310, 6683, 7079, 7499, 7943, 8414, 8913, 9441, 10000, 10593, 11220, 11885, 12589, +	13335, 14125, 14962, 15849, 16788, 17783, 18836, 19953, 21135, 22387, 23714, 25119, +	26607, 28184, 29854, 31623, 33497, 35481, 37584, 39811, 42170, 44668, 47315, 50119, +	53088, 56234, 59566, 63096, 66834, 70795, 74989, 79433, 84140, 89125, 94406, 100000, +	105925, 112202, 118850, 125893, 133352, 141254, 149624, 158489, 167880, 177828, +	188365, 199526, 211349, 223872, 237137, 251189, 266073, 281838, 298538, 316228, +	334965, 354813, 375837, 398107, 421697, 446684, 473151, 501187, 530884, 562341, +	595662, 630957, 668344, 707946, 749894, 794328, 841395, 891251, 944061, 1000000, +	1059254, 1122018, 1188502, 1258925, 1333521, 1412538, 1496236, 1584893, 1678804, +	1778279, 1883649, 1995262, 2113489, 2238721, 2371374, 2511886, 2660725, 2818383, +	2985383, 3162278, 3349654, 3548134, 3758374, 3981072, 4216965, 4466836, 4731513, +	5011872, 5308844, 5623413, 5956621, 6309573, 6683439, 7079458, 7498942, 7943282, +	8413951, 8912509, 9440609, 10000000, 10592537, 11220185, 11885022, 12589254, +	13335214, 14125375, 14962357, 15848932, 16788040, 17782794, 18836491, 19952623, +	21134890, 22387211, 23713737, 25118864, 26607251, 28183829, 29853826, 31622777, +	33496544, 35481339, 37583740, 39810717, 42169650, 44668359, 47315126, 50118723, +	53088444, 56234133, 59566214, 63095734, 66834392, 70794578, 74989421, 79432823, +	84139514, 89125094, 94406088, 100000000, 105925373, 112201845, 118850223, 125892541, +	133352143, 141253754, 149623566, 158489319, 167880402, 177827941, 188364909, 199526231, +	211348904, 223872114, 237137371, 251188643, 266072506, 281838293, 298538262, 316227766, +	334965439, 354813389, 375837404, 398107171, 421696503, 446683592, 473151259, 501187234, +	530884444, 562341325, 595662144, 630957344, 668343918, 707945784, 749894209, 794328235, +	841395142, 891250938, 944060876, 1000000000, 1059253725, 1122018454, 1188502227, +	1258925412, 1333521432, 1412537545, 1496235656, 1584893192, 1678804018, 1778279410, +	1883649089, 1995262315, 2113489040, 2238721139, 2371373706, 2511886432, 2660725060, +	2818382931, 2985382619, 3162277660, 3349654392, 3548133892, 3758374043, 3981071706, +	4216965034, 4466835922ULL, 4731512590ULL, 5011872336ULL, 5308844442ULL, 5623413252ULL, +	5956621435ULL, 6309573445ULL, 6683439176ULL, 7079457844ULL, 7498942093ULL, +	7943282347ULL, 8413951416ULL, 8912509381ULL, 9440608763ULL, 10000000000ULL, +	10592537252ULL, 11220184543ULL, 11885022274ULL, 12589254118ULL, 13335214322ULL, +	14125375446ULL, 14962356561ULL, 15848931925ULL, 16788040181ULL, 17782794100ULL, +	18836490895ULL, 19952623150ULL, 21134890398ULL, 22387211386ULL, 23713737057ULL, +	25118864315ULL, 26607250598ULL, 28183829313ULL, 29853826189ULL, 31622776602ULL, +	33496543916ULL, 35481338923ULL, 37583740429ULL, 39810717055ULL, 42169650343ULL, +	44668359215ULL, 47315125896ULL, 50118723363ULL, 53088444423ULL, 56234132519ULL, +	59566214353ULL, 63095734448ULL, 66834391757ULL, 70794578438ULL, 74989420933ULL, +	79432823472ULL, 84139514165ULL, 89125093813ULL, 94406087629ULL, 100000000000ULL, +	105925372518ULL, 112201845430ULL, 118850222744ULL, 125892541179ULL, 133352143216ULL, +	141253754462ULL, 149623565609ULL, 158489319246ULL, 167880401812ULL, 177827941004ULL, +	188364908949ULL, 199526231497ULL, 211348903984ULL, 223872113857ULL, 237137370566ULL, +	251188643151ULL, 266072505980ULL, 281838293126ULL, 298538261892ULL, 316227766017ULL, +	334965439158ULL, 354813389234ULL, 375837404288ULL, 398107170553ULL, 421696503429ULL, +	446683592151ULL, 473151258961ULL, 501187233627ULL, 530884444231ULL, 562341325190ULL, +	595662143529ULL, 630957344480ULL, 668343917569ULL, 707945784384ULL, 749894209332ULL, +	794328234724ULL, 841395141645ULL, 891250938134ULL, 944060876286ULL, 1000000000000ULL, +	1059253725177ULL, 1122018454302ULL, 1188502227437ULL, 1258925411794ULL, +	1333521432163ULL, 1412537544623ULL, 1496235656094ULL, 1584893192461ULL, +	1678804018123ULL, 1778279410039ULL, 1883649089490ULL, 1995262314969ULL, +	2113489039837ULL, 2238721138568ULL, 2371373705662ULL, 2511886431510ULL, +	2660725059799ULL, 2818382931264ULL, 2985382618918ULL, 3162277660168ULL, +	3349654391578ULL, 3548133892336ULL, 3758374042884ULL, 3981071705535ULL, +	4216965034286ULL, 4466835921510ULL, 4731512589615ULL, 5011872336273ULL, +	5308844442310ULL, 5623413251904ULL, 5956621435290ULL, 6309573444802ULL, +	6683439175686ULL, 7079457843841ULL, 7498942093325ULL, 7943282347243ULL, +	8413951416452ULL, 8912509381337ULL, 9440608762859ULL, 10000000000000ULL, +	10592537251773ULL, 11220184543020ULL, 11885022274370ULL, 12589254117942ULL, +	13335214321633ULL, 14125375446228ULL, 14962356560944ULL, 15848931924611ULL, +	16788040181226ULL, 17782794100389ULL, 18836490894898ULL, 19952623149689ULL, +	21134890398367ULL, 22387211385683ULL, 23713737056617ULL, 25118864315096ULL, +	26607250597988ULL, 28183829312645ULL, 29853826189180ULL, 31622776601684ULL, +	33496543915783ULL, 35481338923358ULL, 37583740428845ULL, 39810717055350ULL, +	42169650342858ULL, 44668359215096ULL, 47315125896148ULL, 50118723362727ULL, +	53088444423099ULL, 56234132519035ULL, 59566214352901ULL, 63095734448019ULL, +	66834391756862ULL, 70794578438414ULL, 74989420933246ULL, 79432823472428ULL, +	84139514164520ULL, 89125093813375ULL, 94406087628593ULL, 100000000000000ULL, +	105925372517729ULL, 112201845430197ULL, 118850222743702ULL, 125892541179417ULL, +	133352143216332ULL, 141253754462276ULL, 149623565609444ULL, 158489319246111ULL, +	167880401812256ULL, 177827941003893ULL, 188364908948981ULL, 199526231496888ULL, +	211348903983664ULL, 223872113856834ULL, 237137370566166ULL, 251188643150958ULL, +	266072505979882ULL, 281838293126446ULL, 298538261891796ULL, 316227766016838ULL, +	334965439157829ULL, 354813389233577ULL, 375837404288444ULL, 398107170553497ULL, +	421696503428583ULL, 446683592150964ULL, 473151258961482ULL, 501187233627272ULL, +	530884444230989ULL, 562341325190350ULL, 595662143529011ULL, 630957344480196ULL, +	668343917568615ULL, 707945784384138ULL, 749894209332456ULL, 794328234724284ULL, +	841395141645198ULL, 891250938133745ULL, 944060876285923ULL, 1000000000000000ULL, +	1059253725177290ULL, 1122018454301970ULL, 1188502227437020ULL, 1258925411794170ULL, +	1333521432163330ULL, 1412537544622760ULL, 1496235656094440ULL, 1584893192461110ULL, +	1678804018122560ULL, 1778279410038920ULL, 1883649089489810ULL, 1995262314968890ULL, +	2113489039836650ULL, 2238721138568340ULL, 2371373705661660ULL, 2511886431509590ULL, +	2660725059798820ULL, 2818382931264460ULL, 2985382618917960ULL, 3162277660168380ULL, +	3349654391578280ULL, 3548133892335770ULL, 3758374042884440ULL, 3981071705534970ULL  }; -u32 rtw89_linear_2_db(u64 val) +s32 rtw89_linear_to_db_quarter(u64 val)  { -	u8 i, j; -	u32 dB; - -	for (i = 0; i < 12; i++) { -		for (j = 0; j < 8; j++) { -			if (i <= FRAC_ROW_MAX && -			    (val << RTW89_LINEAR_FRAC_BITS) <= db_invert_table[i][j]) -				goto cnt; -			else if (i > FRAC_ROW_MAX && val <= db_invert_table[i][j]) -				goto cnt; -		} -	} +	int r = ARRAY_SIZE(db_invert_table) - 1; +	int l = 0; +	int m; -	return 96; /* maximum 96 dB */ +	while (l <= r) { +		m = l + (r - l) / 2; -cnt: -	/* special cases */ -	if (j == 0 && i == 0) -		goto end; +		if (db_invert_table[m] == val) +			return m - (s32)RTW89_DB_INVERT_TABLE_OFFSET; -	if (i == NORM_ROW_MIN && j == 0) { -		if (db_invert_table[NORM_ROW_MIN][0] - val > -		    val - (db_invert_table[FRAC_ROW_MAX][7] >> RTW89_LINEAR_FRAC_BITS)) { -			i = FRAC_ROW_MAX; -			j = 7; -		} -		goto end; +		if (db_invert_table[m] > val) +			r = m - 1; +		else +			l = m + 1;  	} -	if (i <= FRAC_ROW_MAX) -		val <<= RTW89_LINEAR_FRAC_BITS; - -	/* compare difference to get precise dB */ -	if (j == 0) { -		if (db_invert_table[i][j] - val > -		    val - db_invert_table[i - 1][7]) { -			i--; -			j = 7; -		} -	} else { -		if (db_invert_table[i][j] - val > -		    val - db_invert_table[i][j - 1]) { -			j--; -		} -	} -end: -	dB = (i << 3) + j + 1; +	if (l >= ARRAY_SIZE(db_invert_table)) +		return RTW89_MAX_DBM; +	else if (r < 0) +		return RTW89_MIN_DBM; +	else if (val - db_invert_table[r] <= db_invert_table[l] - val) +		return r - (s32)RTW89_DB_INVERT_TABLE_OFFSET; +	else +		return l - (s32)RTW89_DB_INVERT_TABLE_OFFSET; +} +EXPORT_SYMBOL(rtw89_linear_to_db_quarter); -	return dB; +s32 rtw89_linear_to_db(u64 val) +{ +	return rtw89_linear_to_db_quarter(val) >> RTW89_DBM_QUARTER_FACTOR;  } -EXPORT_SYMBOL(rtw89_linear_2_db); +EXPORT_SYMBOL(rtw89_linear_to_db); -u64 rtw89_db_2_linear(u32 db) +u64 rtw89_db_quarter_to_linear(s32 db)  { -	u64 linear; -	u8 i, j; +	/* supported range -41.25 to 96 dBm, in unit of 0.25 dBm */ +	db = clamp_t(s32, db, RTW89_MIN_DBM, RTW89_MAX_DBM); +	db += (s32)RTW89_DB_INVERT_TABLE_OFFSET; -	if (db > 96) -		db = 96; -	else if (db < 1) -		return 1; +	return db_invert_table[db]; +} +EXPORT_SYMBOL(rtw89_db_quarter_to_linear); -	i = (db - 1) >> 3; -	j = (db - 1) & 0x7; +u64 rtw89_db_to_linear(s32 db) +{ +	return rtw89_db_quarter_to_linear(db << RTW89_DBM_QUARTER_FACTOR); +} +EXPORT_SYMBOL(rtw89_db_to_linear); -	linear = db_invert_table[i][j]; +void rtw89_might_trailing_ellipsis(char *buf, size_t size, ssize_t used) +{ +	static const char ellipsis[] = "..."; -	if (i >= NORM_ROW_MIN) -		linear = linear << RTW89_LINEAR_FRAC_BITS; +	/* length of null terminiator isn't included in 'used' */ +	if (used + 1 < size || size < sizeof(ellipsis)) +		return; -	return linear; +	memcpy(buf + size - sizeof(ellipsis), ellipsis, sizeof(ellipsis));  } -EXPORT_SYMBOL(rtw89_db_2_linear); diff --git a/sys/contrib/dev/rtw89/util.h b/sys/contrib/dev/rtw89/util.h index e669544cafd3..bd08495301e4 100644 --- a/sys/contrib/dev/rtw89/util.h +++ b/sys/contrib/dev/rtw89/util.h @@ -6,13 +6,11 @@  #include "core.h" -#define RTW89_LINEAR_FRAC_BITS 3 -  #define rtw89_iterate_vifs_bh(rtwdev, iterator, data)                          \  	ieee80211_iterate_active_interfaces_atomic((rtwdev)->hw,               \  			IEEE80211_IFACE_ITER_NORMAL, iterator, data) -/* call this function with rtwdev->mutex is held */ +/* call this function with wiphy mutex is held */  #define rtw89_for_each_rtwvif(rtwdev, rtwvif)				       \  	list_for_each_entry(rtwvif, &(rtwdev)->rtwvifs_list, list) @@ -25,7 +23,7 @@ static inline bool rtw89_rtwvif_in_list(struct rtw89_dev *rtwdev,  {  	struct rtw89_vif *rtwvif; -	lockdep_assert_held(&rtwdev->mutex); +	lockdep_assert_wiphy(rtwdev->hw->wiphy);  	rtw89_for_each_rtwvif(rtwdev, rtwvif)  		if (rtwvif == new) @@ -75,7 +73,10 @@ static inline void ether_addr_copy_mask(u8 *dst, const u8 *src, u8 mask)  	}  } -u32 rtw89_linear_2_db(u64 linear); -u64 rtw89_db_2_linear(u32 db); +s32 rtw89_linear_to_db_quarter(u64 val); +s32 rtw89_linear_to_db(u64 val); +u64 rtw89_db_quarter_to_linear(s32 db); +u64 rtw89_db_to_linear(s32 db); +void rtw89_might_trailing_ellipsis(char *buf, size_t size, ssize_t used);  #endif diff --git a/sys/contrib/dev/rtw89/wow.c b/sys/contrib/dev/rtw89/wow.c index 311bf82b355e..695884e6cb22 100644 --- a/sys/contrib/dev/rtw89/wow.c +++ b/sys/contrib/dev/rtw89/wow.c @@ -12,7 +12,7 @@  #include "util.h"  #include "wow.h" -void rtw89_wow_parse_akm(struct rtw89_dev *rtwdev, struct sk_buff *skb) +void __rtw89_wow_parse_akm(struct rtw89_dev *rtwdev, struct sk_buff *skb)  {  	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;  	struct rtw89_wow_param *rtw_wow = &rtwdev->wow; @@ -639,6 +639,8 @@ static struct ieee80211_key_conf *rtw89_wow_gtk_rekey(struct rtw89_dev *rtwdev,  	struct ieee80211_key_conf *key;  	u8 sz; +	lockdep_assert_wiphy(rtwdev->hw->wiphy); +  	cipher_info = rtw89_cipher_alg_recognize(cipher);  	sz = struct_size(rekey_conf, key, cipher_info->len);  	rekey_conf = kmalloc(sz, GFP_KERNEL); @@ -651,15 +653,13 @@ static struct ieee80211_key_conf *rtw89_wow_gtk_rekey(struct rtw89_dev *rtwdev,  	memcpy(rekey_conf->key, gtk,  	       flex_array_size(rekey_conf, key, cipher_info->len)); -	/* ieee80211_gtk_rekey_add() will call set_key(), therefore we -	 * need to unlock mutex -	 */ -	mutex_unlock(&rtwdev->mutex);  	if (ieee80211_vif_is_mld(wow_vif)) -		key = ieee80211_gtk_rekey_add(wow_vif, rekey_conf, rtwvif_link->link_id); +		key = ieee80211_gtk_rekey_add(wow_vif, keyidx, gtk, +					      cipher_info->len, +					      rtwvif_link->link_id);  	else -		key = ieee80211_gtk_rekey_add(wow_vif, rekey_conf, -1); -	mutex_lock(&rtwdev->mutex); +		key = ieee80211_gtk_rekey_add(wow_vif, keyidx, gtk, +					      cipher_info->len, -1);  	kfree(rekey_conf);  	if (IS_ERR(key)) { @@ -1124,8 +1124,7 @@ static int rtw89_wow_set_wakeups(struct rtw89_dev *rtwdev,  		rtw89_wow_init_pno(rtwdev, wowlan->nd_config);  	rtw89_for_each_rtwvif(rtwdev, rtwvif) { -		/* use the link on HW-0 to do wow flow */ -		rtwvif_link = rtw89_vif_get_link_inst(rtwvif, 0); +		rtwvif_link = rtw89_get_designated_link(rtwvif);  		if (!rtwvif_link)  			continue; @@ -1451,6 +1450,8 @@ static void rtw89_fw_release_pno_pkt_list(struct rtw89_dev *rtwdev,  static int rtw89_pno_scan_update_probe_req(struct rtw89_dev *rtwdev,  					   struct rtw89_vif_link *rtwvif_link)  { +	static const u8 basic_rate_ie[] = {WLAN_EID_SUPP_RATES, 0x08, +		 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c};  	struct rtw89_wow_param *rtw_wow = &rtwdev->wow;  	struct cfg80211_sched_scan_request *nd_config = rtw_wow->nd_config;  	u8 num = nd_config->n_match_sets, i; @@ -1462,10 +1463,11 @@ static int rtw89_pno_scan_update_probe_req(struct rtw89_dev *rtwdev,  		skb = ieee80211_probereq_get(rtwdev->hw, rtwvif_link->mac_addr,  					     nd_config->match_sets[i].ssid.ssid,  					     nd_config->match_sets[i].ssid.ssid_len, -					     nd_config->ie_len); +					     nd_config->ie_len + sizeof(basic_rate_ie));  		if (!skb)  			return -ENOMEM; +		skb_put_data(skb, basic_rate_ie, sizeof(basic_rate_ie));  		skb_put_data(skb, nd_config->ie, nd_config->ie_len);  		info = kzalloc(sizeof(*info), GFP_KERNEL); @@ -1516,7 +1518,7 @@ static int rtw89_pno_scan_offload(struct rtw89_dev *rtwdev, bool enable)  	opt.enable = enable;  	opt.repeat = RTW89_SCAN_NORMAL;  	opt.norm_pd = max(interval, 1) * 10; /* in unit of 100ms */ -	opt.delay = max(rtw_wow->nd_config->delay, 1); +	opt.delay = max(rtw_wow->nd_config->delay, 1) * 1000;  	if (rtwdev->chip->chip_gen == RTW89_CHIP_BE) {  		opt.operation = enable ? RTW89_SCAN_OP_START : RTW89_SCAN_OP_STOP; @@ -1528,7 +1530,7 @@ static int rtw89_pno_scan_offload(struct rtw89_dev *rtwdev, bool enable)  		opt.opch_end = RTW89_CHAN_INVALID;  	} -	mac->scan_offload(rtwdev, &opt, rtwvif_link, true); +	rtw89_mac_scan_offload(rtwdev, &opt, rtwvif_link, true);  	return 0;  } diff --git a/sys/contrib/dev/rtw89/wow.h b/sys/contrib/dev/rtw89/wow.h index f91991e8f2e3..6606528d31c7 100644 --- a/sys/contrib/dev/rtw89/wow.h +++ b/sys/contrib/dev/rtw89/wow.h @@ -116,9 +116,21 @@ static inline bool rtw_wow_has_mgd_features(struct rtw89_dev *rtwdev)  	return !bitmap_empty(rtw_wow->flags, RTW89_WOW_FLAG_NUM);  } +void __rtw89_wow_parse_akm(struct rtw89_dev *rtwdev, struct sk_buff *skb); + +static inline +void rtw89_wow_parse_akm(struct rtw89_dev *rtwdev, struct sk_buff *skb) +{ +	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + +	if (likely(!ieee80211_is_assoc_req(hdr->frame_control))) +		return; + +	__rtw89_wow_parse_akm(rtwdev, skb); +} +  int rtw89_wow_suspend(struct rtw89_dev *rtwdev, struct cfg80211_wowlan *wowlan);  int rtw89_wow_resume(struct rtw89_dev *rtwdev); -void rtw89_wow_parse_akm(struct rtw89_dev *rtwdev, struct sk_buff *skb);  #else  static inline  void rtw89_wow_parse_akm(struct rtw89_dev *rtwdev, struct sk_buff *skb)  | 
