OSDN Git Service

ath11k: add support for hardware rfkill for QCA6390
authorWen Gong <quic_wgong@quicinc.com>
Mon, 20 Dec 2021 16:11:09 +0000 (18:11 +0200)
committerKalle Valo <quic_kvalo@quicinc.com>
Mon, 20 Dec 2021 18:29:22 +0000 (20:29 +0200)
When hardware rfkill is enabled in the firmware it will report the
capability via using WMI_SYS_CAP_INFO_RFKILL bit in the WMI_SERVICE_READY
event to the host. ath11k will check the capability, and if it is enabled then
ath11k will set the GPIO information to firmware using WMI_PDEV_SET_PARAM. When
the firmware detects hardware rfkill is enabled by the user, it will report it
via WMI_RFKILL_STATE_CHANGE_EVENTID. Once ath11k receives the event it will
send wmi command WMI_PDEV_SET_PARAM to the firmware and also notifies cfg80211.

This only enable rfkill feature for QCA6390, rfkill_pin is all initialized to 0
for other chips in ath11k_hw_params.

Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1

Signed-off-by: Wen Gong <quic_wgong@quicinc.com>
Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
Link: https://lore.kernel.org/r/20211217102334.14907-1-quic_wgong@quicinc.com
drivers/net/wireless/ath/ath11k/core.c
drivers/net/wireless/ath/ath11k/core.h
drivers/net/wireless/ath/ath11k/hw.h
drivers/net/wireless/ath/ath11k/mac.c
drivers/net/wireless/ath/ath11k/mac.h
drivers/net/wireless/ath/ath11k/wmi.c
drivers/net/wireless/ath/ath11k/wmi.h

index 9e74542..bfda297 100644 (file)
@@ -52,6 +52,9 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
                .target_ce_count = 11,
                .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_ipq8074,
                .svc_to_ce_map_len = 21,
+               .rfkill_pin = 0,
+               .rfkill_cfg = 0,
+               .rfkill_on_level = 0,
                .single_pdev_only = false,
                .rxdma1_enable = true,
                .num_rxmda_per_pdev = 1,
@@ -114,6 +117,9 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
                .target_ce_count = 11,
                .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_ipq6018,
                .svc_to_ce_map_len = 19,
+               .rfkill_pin = 0,
+               .rfkill_cfg = 0,
+               .rfkill_on_level = 0,
                .single_pdev_only = false,
                .rxdma1_enable = true,
                .num_rxmda_per_pdev = 1,
@@ -173,6 +179,9 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
                .target_ce_count = 9,
                .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_qca6390,
                .svc_to_ce_map_len = 14,
+               .rfkill_pin = 48,
+               .rfkill_cfg = 0,
+               .rfkill_on_level = 1,
                .single_pdev_only = true,
                .rxdma1_enable = false,
                .num_rxmda_per_pdev = 2,
@@ -231,6 +240,9 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
                .target_ce_count = 9,
                .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_qcn9074,
                .svc_to_ce_map_len = 18,
+               .rfkill_pin = 0,
+               .rfkill_cfg = 0,
+               .rfkill_on_level = 0,
                .rxdma1_enable = true,
                .num_rxmda_per_pdev = 1,
                .rx_mac_buf_ring = false,
@@ -289,6 +301,9 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
                .target_ce_count = 9,
                .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_qca6390,
                .svc_to_ce_map_len = 14,
+               .rfkill_pin = 0,
+               .rfkill_cfg = 0,
+               .rfkill_on_level = 0,
                .single_pdev_only = true,
                .rxdma1_enable = false,
                .num_rxmda_per_pdev = 2,
@@ -347,6 +362,9 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
                .target_ce_count = 9,
                .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_qca6390,
                .svc_to_ce_map_len = 14,
+               .rfkill_pin = 0,
+               .rfkill_cfg = 0,
+               .rfkill_on_level = 0,
                .single_pdev_only = true,
                .rxdma1_enable = false,
                .num_rxmda_per_pdev = 2,
@@ -1015,6 +1033,27 @@ err_firmware_stop:
        return ret;
 }
 
+static int ath11k_core_rfkill_config(struct ath11k_base *ab)
+{
+       struct ath11k *ar;
+       int ret = 0, i;
+
+       if (!(ab->target_caps.sys_cap_info & WMI_SYS_CAP_INFO_RFKILL))
+               return 0;
+
+       for (i = 0; i < ab->num_radios; i++) {
+               ar = ab->pdevs[i].ar;
+
+               ret = ath11k_mac_rfkill_config(ar);
+               if (ret && ret != -EOPNOTSUPP) {
+                       ath11k_warn(ab, "failed to configure rfkill: %d", ret);
+                       return ret;
+               }
+       }
+
+       return ret;
+}
+
 int ath11k_core_qmi_firmware_ready(struct ath11k_base *ab)
 {
        int ret;
@@ -1061,6 +1100,13 @@ int ath11k_core_qmi_firmware_ready(struct ath11k_base *ab)
                goto err_core_stop;
        }
        ath11k_hif_irq_enable(ab);
+
+       ret = ath11k_core_rfkill_config(ab);
+       if (ret && ret != -EOPNOTSUPP) {
+               ath11k_err(ab, "failed to config rfkill: %d\n", ret);
+               goto err_core_stop;
+       }
+
        mutex_unlock(&ab->core_lock);
 
        return 0;
@@ -1126,6 +1172,7 @@ void ath11k_core_halt(struct ath11k *ar)
        cancel_delayed_work_sync(&ar->scan.timeout);
        cancel_work_sync(&ar->regd_update_work);
        cancel_work_sync(&ab->update_11d_work);
+       cancel_work_sync(&ab->rfkill_work);
 
        rcu_assign_pointer(ab->pdevs_active[ar->pdev_idx], NULL);
        synchronize_rcu();
@@ -1133,6 +1180,28 @@ void ath11k_core_halt(struct ath11k *ar)
        idr_init(&ar->txmgmt_idr);
 }
 
+static void ath11k_rfkill_work(struct work_struct *work)
+{
+       struct ath11k_base *ab = container_of(work, struct ath11k_base, rfkill_work);
+       struct ath11k *ar;
+       bool rfkill_radio_on;
+       int i;
+
+       spin_lock_bh(&ab->base_lock);
+       rfkill_radio_on = ab->rfkill_radio_on;
+       spin_unlock_bh(&ab->base_lock);
+
+       for (i = 0; i < ab->num_radios; i++) {
+               ar = ab->pdevs[i].ar;
+               if (!ar)
+                       continue;
+
+               /* notify cfg80211 radio state change */
+               ath11k_mac_rfkill_enable_radio(ar, rfkill_radio_on);
+               wiphy_rfkill_set_hw_state(ar->hw->wiphy, !rfkill_radio_on);
+       }
+}
+
 static void ath11k_update_11d(struct work_struct *work)
 {
        struct ath11k_base *ab = container_of(work, struct ath11k_base, update_11d_work);
@@ -1339,6 +1408,7 @@ struct ath11k_base *ath11k_core_alloc(struct device *dev, size_t priv_size,
        init_waitqueue_head(&ab->qmi.cold_boot_waitq);
        INIT_WORK(&ab->restart_work, ath11k_core_restart);
        INIT_WORK(&ab->update_11d_work, ath11k_update_11d);
+       INIT_WORK(&ab->rfkill_work, ath11k_rfkill_work);
        timer_setup(&ab->rx_replenish_retry, ath11k_ce_rx_replenish_retry, 0);
        init_completion(&ab->htc_suspend);
        init_completion(&ab->wow.wakeup_completed);
index 5cb906c..45aae41 100644 (file)
@@ -786,6 +786,10 @@ struct ath11k_base {
 
        struct ath11k_dbring_cap *db_caps;
        u32 num_db_cap;
+       struct work_struct rfkill_work;
+
+       /* true means radio is on */
+       bool rfkill_radio_on;
 
        /* To synchronize 11d scan vdev id */
        struct mutex vdev_id_11d_lock;
index a8a5b76..e4685c7 100644 (file)
@@ -151,6 +151,9 @@ struct ath11k_hw_params {
        u32 svc_to_ce_map_len;
 
        bool single_pdev_only;
+       u32 rfkill_pin;
+       u32 rfkill_cfg;
+       u32 rfkill_on_level;
 
        bool rxdma1_enable;
        int num_rxmda_per_pdev;
index aebb4b7..07f499d 100644 (file)
@@ -5571,6 +5571,63 @@ static int ath11k_mac_mgmt_tx(struct ath11k *ar, struct sk_buff *skb,
        return 0;
 }
 
+int ath11k_mac_rfkill_config(struct ath11k *ar)
+{
+       struct ath11k_base *ab = ar->ab;
+       u32 param;
+       int ret;
+
+       if (ab->hw_params.rfkill_pin == 0)
+               return -EOPNOTSUPP;
+
+       ath11k_dbg(ab, ATH11K_DBG_MAC,
+                  "mac rfkill_pin %d rfkill_cfg %d rfkill_on_level %d",
+                  ab->hw_params.rfkill_pin, ab->hw_params.rfkill_cfg,
+                  ab->hw_params.rfkill_on_level);
+
+       param = FIELD_PREP(WMI_RFKILL_CFG_RADIO_LEVEL,
+                          ab->hw_params.rfkill_on_level) |
+               FIELD_PREP(WMI_RFKILL_CFG_GPIO_PIN_NUM,
+                          ab->hw_params.rfkill_pin) |
+               FIELD_PREP(WMI_RFKILL_CFG_PIN_AS_GPIO,
+                          ab->hw_params.rfkill_cfg);
+
+       ret = ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_HW_RFKILL_CONFIG,
+                                       param, ar->pdev->pdev_id);
+       if (ret) {
+               ath11k_warn(ab,
+                           "failed to set rfkill config 0x%x: %d\n",
+                           param, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+int ath11k_mac_rfkill_enable_radio(struct ath11k *ar, bool enable)
+{
+       enum wmi_rfkill_enable_radio param;
+       int ret;
+
+       if (enable)
+               param = WMI_RFKILL_ENABLE_RADIO_ON;
+       else
+               param = WMI_RFKILL_ENABLE_RADIO_OFF;
+
+       ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac %d rfkill enable %d",
+                  ar->pdev_idx, param);
+
+       ret = ath11k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_RFKILL_ENABLE,
+                                       param, ar->pdev->pdev_id);
+       if (ret) {
+               ath11k_warn(ar->ab, "failed to set rfkill enable param %d: %d\n",
+                           param, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
 static void ath11k_mac_op_tx(struct ieee80211_hw *hw,
                             struct ieee80211_tx_control *control,
                             struct sk_buff *skb)
@@ -5803,6 +5860,7 @@ static void ath11k_mac_op_stop(struct ieee80211_hw *hw)
        cancel_delayed_work_sync(&ar->scan.timeout);
        cancel_work_sync(&ar->regd_update_work);
        cancel_work_sync(&ar->ab->update_11d_work);
+       cancel_work_sync(&ar->ab->rfkill_work);
 
        spin_lock_bh(&ar->data_lock);
        list_for_each_entry_safe(ppdu_stats, tmp, &ar->ppdu_stats_info, list) {
index cab3356..0e6c870 100644 (file)
@@ -147,6 +147,8 @@ u8 ath11k_mac_hw_rate_to_idx(const struct ieee80211_supported_band *sband,
 
 void __ath11k_mac_scan_finish(struct ath11k *ar);
 void ath11k_mac_scan_finish(struct ath11k *ar);
+int ath11k_mac_rfkill_enable_radio(struct ath11k *ar, bool enable);
+int ath11k_mac_rfkill_config(struct ath11k *ar);
 
 struct ath11k_vif *ath11k_mac_get_arvif(struct ath11k *ar, u32 vdev_id);
 struct ath11k_vif *ath11k_mac_get_arvif_by_vdev_id(struct ath11k_base *ab,
index 79b0463..747e152 100644 (file)
@@ -128,6 +128,8 @@ static const struct wmi_tlv_policy wmi_tlv_policies[] = {
                = { .min_len = sizeof(struct wmi_peer_assoc_conf_event) },
        [WMI_TAG_STATS_EVENT]
                = { .min_len = sizeof(struct wmi_stats_event) },
+       [WMI_TAG_RFKILL_EVENT] = {
+               .min_len = sizeof(struct wmi_rfkill_state_change_ev) },
        [WMI_TAG_PDEV_CTL_FAILSAFE_CHECK_EVENT]
                = { .min_len = sizeof(struct wmi_pdev_ctl_failsafe_chk_event) },
        [WMI_TAG_HOST_SWFDA_EVENT] = {
@@ -524,6 +526,8 @@ static int ath11k_pull_service_ready_tlv(struct ath11k_base *ab,
        cap->default_dbs_hw_mode_index = ev->default_dbs_hw_mode_index;
        cap->num_msdu_desc = ev->num_msdu_desc;
 
+       ath11k_dbg(ab, ATH11K_DBG_WMI, "wmi sys cap info 0x%x\n", cap->sys_cap_info);
+
        return 0;
 }
 
@@ -7334,6 +7338,40 @@ exit:
        kfree(tb);
 }
 
+static void ath11k_rfkill_state_change_event(struct ath11k_base *ab,
+                                            struct sk_buff *skb)
+{
+       const struct wmi_rfkill_state_change_ev *ev;
+       const void **tb;
+       int ret;
+
+       tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
+       if (IS_ERR(tb)) {
+               ret = PTR_ERR(tb);
+               ath11k_warn(ab, "failed to parse tlv: %d\n", ret);
+               return;
+       }
+
+       ev = tb[WMI_TAG_RFKILL_EVENT];
+       if (!ev) {
+               kfree(tb);
+               return;
+       }
+
+       ath11k_dbg(ab, ATH11K_DBG_MAC,
+                  "wmi tlv rfkill state change gpio %d type %d radio_state %d\n",
+                  ev->gpio_pin_num,
+                  ev->int_type,
+                  ev->radio_state);
+
+       spin_lock_bh(&ab->base_lock);
+       ab->rfkill_radio_on = (ev->radio_state == WMI_RFKILL_RADIO_STATE_ON);
+       spin_unlock_bh(&ab->base_lock);
+
+       queue_work(ab->workqueue, &ab->rfkill_work);
+       kfree(tb);
+}
+
 static void
 ath11k_wmi_pdev_temperature_event(struct ath11k_base *ab,
                                  struct sk_buff *skb)
@@ -7606,6 +7644,9 @@ static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb)
        case WMI_11D_NEW_COUNTRY_EVENTID:
                ath11k_reg_11d_new_cc_event(ab, skb);
                break;
+       case WMI_RFKILL_STATE_CHANGE_EVENTID:
+               ath11k_rfkill_state_change_event(ab, skb);
+               break;
        /* TODO: Add remaining events */
        default:
                ath11k_dbg(ab, ATH11K_DBG_WMI, "Unknown eventid: 0x%x\n", id);
index 20aa723..2f26ec1 100644 (file)
@@ -5215,6 +5215,31 @@ struct target_resource_config {
        u32 twt_ap_sta_count;
 };
 
+enum wmi_sys_cap_info_flags {
+       WMI_SYS_CAP_INFO_RXTX_LED       = BIT(0),
+       WMI_SYS_CAP_INFO_RFKILL = BIT(1),
+};
+
+#define WMI_RFKILL_CFG_GPIO_PIN_NUM            GENMASK(5, 0)
+#define WMI_RFKILL_CFG_RADIO_LEVEL             BIT(6)
+#define WMI_RFKILL_CFG_PIN_AS_GPIO             GENMASK(10, 7)
+
+enum wmi_rfkill_enable_radio {
+       WMI_RFKILL_ENABLE_RADIO_ON      = 0,
+       WMI_RFKILL_ENABLE_RADIO_OFF     = 1,
+};
+
+enum wmi_rfkill_radio_state {
+       WMI_RFKILL_RADIO_STATE_OFF      = 1,
+       WMI_RFKILL_RADIO_STATE_ON       = 2,
+};
+
+struct wmi_rfkill_state_change_ev {
+       u32 gpio_pin_num;
+       u32 int_type;
+       u32 radio_state;
+} __packed;
+
 #define WMI_MAX_MEM_REQS 32
 
 #define MAX_RADIOS 3