Commit 652f69ed authored by Baochen Qiang's avatar Baochen Qiang Committed by Kalle Valo
Browse files

ath11k: Add support for SAR



Add ath11k_mac_op_set_bios_sar_specs() to ath11k_ops, this function is called
when user space application calls NL80211_CMD_SET_SAR_SPECS. ath11k also
registers SAR type and frequency ranges to wiphy so user space can query SAR
capabilities.

This feature is currently enabled for WCN6855.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-02431-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1

Signed-off-by: default avatarBaochen Qiang <quic_bqiang@quicinc.com>
Signed-off-by: default avatarKalle Valo <quic_kvalo@quicinc.com>
Link: https://lore.kernel.org/r/20220401120948.1312956-3-quic_bqiang@quicinc.com
parent e2e23a79
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -101,6 +101,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
		.current_cc_support = false,
		.dbr_debug_support = true,
		.global_reset = false,
		.bios_sar_capa = NULL,
	},
	{
		.hw_rev = ATH11K_HW_IPQ6018_HW10,
@@ -167,6 +168,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
		.current_cc_support = false,
		.dbr_debug_support = true,
		.global_reset = false,
		.bios_sar_capa = NULL,
	},
	{
		.name = "qca6390 hw2.0",
@@ -232,6 +234,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
		.current_cc_support = true,
		.dbr_debug_support = false,
		.global_reset = true,
		.bios_sar_capa = NULL,
	},
	{
		.name = "qcn9074 hw1.0",
@@ -297,6 +300,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
		.current_cc_support = false,
		.dbr_debug_support = true,
		.global_reset = false,
		.bios_sar_capa = NULL,
	},
	{
		.name = "wcn6855 hw2.0",
@@ -362,6 +366,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
		.current_cc_support = true,
		.dbr_debug_support = false,
		.global_reset = true,
		.bios_sar_capa = &ath11k_hw_sar_capa_wcn6855,
	},
	{
		.name = "wcn6855 hw2.1",
@@ -426,6 +431,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
		.current_cc_support = true,
		.dbr_debug_support = false,
		.global_reset = true,
		.bios_sar_capa = &ath11k_hw_sar_capa_wcn6855,
	},
};

+20 −0
Original line number Diff line number Diff line
@@ -2154,3 +2154,23 @@ const struct ath11k_hw_hal_params ath11k_hw_hal_params_ipq8074 = {
const struct ath11k_hw_hal_params ath11k_hw_hal_params_qca6390 = {
	.rx_buf_rbm = HAL_RX_BUF_RBM_SW1_BM,
};

static const struct cfg80211_sar_freq_ranges ath11k_hw_sar_freq_ranges_wcn6855[] = {
	{.start_freq = 2402, .end_freq = 2482 },  /* 2G ch1~ch13 */
	{.start_freq = 5150, .end_freq = 5250 },  /* 5G UNII-1 ch32~ch48 */
	{.start_freq = 5250, .end_freq = 5725 },  /* 5G UNII-2 ch50~ch144 */
	{.start_freq = 5725, .end_freq = 5810 },  /* 5G UNII-3 ch149~ch161 */
	{.start_freq = 5815, .end_freq = 5895 },  /* 5G UNII-4 ch163~ch177 */
	{.start_freq = 5925, .end_freq = 6165 },  /* 6G UNII-5 Ch1, Ch2 ~ Ch41 */
	{.start_freq = 6165, .end_freq = 6425 },  /* 6G UNII-5 ch45~ch93 */
	{.start_freq = 6425, .end_freq = 6525 },  /* 6G UNII-6 ch97~ch113 */
	{.start_freq = 6525, .end_freq = 6705 },  /* 6G UNII-7 ch117~ch149 */
	{.start_freq = 6705, .end_freq = 6875 },  /* 6G UNII-7 ch153~ch185 */
	{.start_freq = 6875, .end_freq = 7125 },  /* 6G UNII-8 ch189~ch233 */
};

const struct cfg80211_sar_capa ath11k_hw_sar_capa_wcn6855 = {
	.type = NL80211_SAR_TYPE_POWER,
	.num_freq_ranges = (ARRAY_SIZE(ath11k_hw_sar_freq_ranges_wcn6855)),
	.freq_ranges = ath11k_hw_sar_freq_ranges_wcn6855,
};
+2 −0
Original line number Diff line number Diff line
@@ -195,6 +195,7 @@ struct ath11k_hw_params {
	bool current_cc_support;
	bool dbr_debug_support;
	bool global_reset;
	const struct cfg80211_sar_capa *bios_sar_capa;
};

struct ath11k_hw_ops {
@@ -380,4 +381,5 @@ static inline const char *ath11k_bd_ie_type_str(enum ath11k_bd_ie_type type)
	return "unknown";
}

extern const struct cfg80211_sar_capa ath11k_hw_sar_capa_wcn6855;
#endif
+67 −0
Original line number Diff line number Diff line
@@ -8273,6 +8273,68 @@ static void ath11k_mac_op_set_rekey_data(struct ieee80211_hw *hw,
	mutex_unlock(&ar->conf_mutex);
}

static int ath11k_mac_op_set_bios_sar_specs(struct ieee80211_hw *hw,
					    const struct cfg80211_sar_specs *sar)
{
	struct ath11k *ar = hw->priv;
	const struct cfg80211_sar_sub_specs *sspec = sar->sub_specs;
	int ret, index;
	u8 *sar_tbl;
	u32 i;

	mutex_lock(&ar->conf_mutex);

	if (!test_bit(WMI_TLV_SERVICE_BIOS_SAR_SUPPORT, ar->ab->wmi_ab.svc_map) ||
	    !ar->ab->hw_params.bios_sar_capa) {
		ret = -EOPNOTSUPP;
		goto exit;
	}

	if (!sar || sar->type != NL80211_SAR_TYPE_POWER ||
	    sar->num_sub_specs == 0) {
		ret = -EINVAL;
		goto exit;
	}

	ret = ath11k_wmi_pdev_set_bios_geo_table_param(ar);
	if (ret) {
		ath11k_warn(ar->ab, "failed to set geo table: %d\n", ret);
		goto exit;
	}

	sar_tbl = kzalloc(BIOS_SAR_TABLE_LEN, GFP_KERNEL);
	if (!sar_tbl) {
		ret = -ENOMEM;
		goto exit;
	}

	for (i = 0; i < sar->num_sub_specs; i++) {
		if (sspec->freq_range_index >= (BIOS_SAR_TABLE_LEN >> 1)) {
			ath11k_warn(ar->ab, "Ignore bad frequency index %u, max allowed %u\n",
				    sspec->freq_range_index, BIOS_SAR_TABLE_LEN >> 1);
			continue;
		}

		/* chain0 and chain1 share same power setting */
		sar_tbl[sspec->freq_range_index] = sspec->power;
		index = sspec->freq_range_index + (BIOS_SAR_TABLE_LEN >> 1);
		sar_tbl[index] = sspec->power;
		ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "sar tbl[%d] = %d\n",
			   sspec->freq_range_index, sar_tbl[sspec->freq_range_index]);
		sspec++;
	}

	ret = ath11k_wmi_pdev_set_bios_sar_table_param(ar, sar_tbl);
	if (ret)
		ath11k_warn(ar->ab, "failed to set sar power: %d", ret);

	kfree(sar_tbl);
exit:
	mutex_unlock(&ar->conf_mutex);

	return ret;
}

static const struct ieee80211_ops ath11k_ops = {
	.tx				= ath11k_mac_op_tx,
	.start                          = ath11k_mac_op_start,
@@ -8324,6 +8386,7 @@ static const struct ieee80211_ops ath11k_ops = {
	.ipv6_addr_change = ath11k_mac_op_ipv6_changed,
#endif

	.set_sar_specs			= ath11k_mac_op_set_bios_sar_specs,
};

static void ath11k_mac_update_ch_list(struct ath11k *ar,
@@ -8746,6 +8809,10 @@ static int __ath11k_mac_register(struct ath11k *ar)
		ieee80211_hw_set(ar->hw, SUPPORT_FAST_XMIT);
	}

	if (test_bit(WMI_TLV_SERVICE_BIOS_SAR_SUPPORT, ar->ab->wmi_ab.svc_map) &&
	    ab->hw_params.bios_sar_capa)
		ar->hw->wiphy->sar_capa = ab->hw_params.bios_sar_capa;

	ret = ieee80211_register_hw(ar->hw);
	if (ret) {
		ath11k_err(ar->ab, "ieee80211 registration failed: %d\n", ret);
+70 −0
Original line number Diff line number Diff line
@@ -8883,3 +8883,73 @@ int ath11k_wmi_gtk_rekey_getinfo(struct ath11k *ar,
		   arvif->vdev_id);
	return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID);
}

int ath11k_wmi_pdev_set_bios_sar_table_param(struct ath11k *ar, const u8 *sar_val)
{	struct ath11k_pdev_wmi *wmi = ar->wmi;
	struct wmi_pdev_set_sar_table_cmd *cmd;
	struct wmi_tlv *tlv;
	struct sk_buff *skb;
	u8 *buf_ptr;
	u32 len, sar_len_aligned, rsvd_len_aligned;

	sar_len_aligned = roundup(BIOS_SAR_TABLE_LEN, sizeof(u32));
	rsvd_len_aligned = roundup(BIOS_SAR_RSVD1_LEN, sizeof(u32));
	len = sizeof(*cmd) +
	      TLV_HDR_SIZE + sar_len_aligned +
	      TLV_HDR_SIZE + rsvd_len_aligned;

	skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len);
	if (!skb)
		return -ENOMEM;

	cmd = (struct wmi_pdev_set_sar_table_cmd *)skb->data;
	cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PDEV_SET_BIOS_SAR_TABLE_CMD) |
			  FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);
	cmd->pdev_id = ar->pdev->pdev_id;
	cmd->sar_len = BIOS_SAR_TABLE_LEN;
	cmd->rsvd_len = BIOS_SAR_RSVD1_LEN;

	buf_ptr = skb->data + sizeof(*cmd);
	tlv = (struct wmi_tlv *)buf_ptr;
	tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) |
		      FIELD_PREP(WMI_TLV_LEN, sar_len_aligned);
	buf_ptr += TLV_HDR_SIZE;
	memcpy(buf_ptr, sar_val, BIOS_SAR_TABLE_LEN);

	buf_ptr += sar_len_aligned;
	tlv = (struct wmi_tlv *)buf_ptr;
	tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) |
		      FIELD_PREP(WMI_TLV_LEN, rsvd_len_aligned);

	return ath11k_wmi_cmd_send(wmi, skb, WMI_PDEV_SET_BIOS_SAR_TABLE_CMDID);
}

int ath11k_wmi_pdev_set_bios_geo_table_param(struct ath11k *ar)
{
	struct ath11k_pdev_wmi *wmi = ar->wmi;
	struct wmi_pdev_set_geo_table_cmd *cmd;
	struct wmi_tlv *tlv;
	struct sk_buff *skb;
	u8 *buf_ptr;
	u32 len, rsvd_len_aligned;

	rsvd_len_aligned = roundup(BIOS_SAR_RSVD2_LEN, sizeof(u32));
	len = sizeof(*cmd) + TLV_HDR_SIZE + rsvd_len_aligned;

	skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len);
	if (!skb)
		return -ENOMEM;

	cmd = (struct wmi_pdev_set_geo_table_cmd *)skb->data;
	cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PDEV_SET_BIOS_GEO_TABLE_CMD) |
			  FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);
	cmd->pdev_id = ar->pdev->pdev_id;
	cmd->rsvd_len = BIOS_SAR_RSVD2_LEN;

	buf_ptr = skb->data + sizeof(*cmd);
	tlv = (struct wmi_tlv *)buf_ptr;
	tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) |
		      FIELD_PREP(WMI_TLV_LEN, rsvd_len_aligned);

	return ath11k_wmi_cmd_send(wmi, skb, WMI_PDEV_SET_BIOS_GEO_TABLE_CMDID);
}
Loading