summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/ath/ath12k/core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/ath/ath12k/core.c')
-rw-r--r--drivers/net/wireless/ath/ath12k/core.c329
1 files changed, 297 insertions, 32 deletions
diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c
index 0b2dec081c6e..31d851d8e688 100644
--- a/drivers/net/wireless/ath/ath12k/core.c
+++ b/drivers/net/wireless/ath/ath12k/core.c
@@ -10,15 +10,18 @@
#include <linux/firmware.h>
#include <linux/of.h>
#include <linux/of_graph.h>
+#include "ahb.h"
#include "core.h"
#include "dp_tx.h"
#include "dp_rx.h"
#include "debug.h"
-#include "hif.h"
-#include "fw.h"
#include "debugfs.h"
+#include "fw.h"
+#include "hif.h"
+#include "pci.h"
#include "wow.h"
+static int ahb_err, pci_err;
unsigned int ath12k_debug_mask;
module_param_named(debug_mask, ath12k_debug_mask, uint, 0644);
MODULE_PARM_DESC(debug_mask, "Debugging mask");
@@ -612,9 +615,74 @@ u32 ath12k_core_get_max_num_tids(struct ath12k_base *ab)
return TARGET_NUM_TIDS(SINGLE);
}
+struct reserved_mem *ath12k_core_get_reserved_mem(struct ath12k_base *ab,
+ int index)
+{
+ struct device *dev = ab->dev;
+ struct reserved_mem *rmem;
+ struct device_node *node;
+
+ node = of_parse_phandle(dev->of_node, "memory-region", index);
+ if (!node) {
+ ath12k_dbg(ab, ATH12K_DBG_BOOT,
+ "failed to parse memory-region for index %d\n", index);
+ return NULL;
+ }
+
+ rmem = of_reserved_mem_lookup(node);
+ of_node_put(node);
+ if (!rmem) {
+ ath12k_dbg(ab, ATH12K_DBG_BOOT,
+ "unable to get memory-region for index %d\n", index);
+ return NULL;
+ }
+
+ return rmem;
+}
+
+static inline
+void ath12k_core_to_group_ref_get(struct ath12k_base *ab)
+{
+ struct ath12k_hw_group *ag = ab->ag;
+
+ lockdep_assert_held(&ag->mutex);
+
+ if (ab->hw_group_ref) {
+ ath12k_dbg(ab, ATH12K_DBG_BOOT, "core already attached to group %d\n",
+ ag->id);
+ return;
+ }
+
+ ab->hw_group_ref = true;
+ ag->num_started++;
+
+ ath12k_dbg(ab, ATH12K_DBG_BOOT, "core attached to group %d, num_started %d\n",
+ ag->id, ag->num_started);
+}
+
+static inline
+void ath12k_core_to_group_ref_put(struct ath12k_base *ab)
+{
+ struct ath12k_hw_group *ag = ab->ag;
+
+ lockdep_assert_held(&ag->mutex);
+
+ if (!ab->hw_group_ref) {
+ ath12k_dbg(ab, ATH12K_DBG_BOOT, "core already de-attached from group %d\n",
+ ag->id);
+ return;
+ }
+
+ ab->hw_group_ref = false;
+ ag->num_started--;
+
+ ath12k_dbg(ab, ATH12K_DBG_BOOT, "core de-attached from group %d, num_started %d\n",
+ ag->id, ag->num_started);
+}
+
static void ath12k_core_stop(struct ath12k_base *ab)
{
- ath12k_core_stopped(ab);
+ ath12k_core_to_group_ref_put(ab);
if (!test_bit(ATH12K_FLAG_CRASH_FLUSH, &ab->dev_flags))
ath12k_qmi_firmware_stop(ab);
@@ -629,7 +697,7 @@ static void ath12k_core_stop(struct ath12k_base *ab)
/* De-Init of components as needed */
}
-static void ath12k_core_check_bdfext(const struct dmi_header *hdr, void *data)
+static void ath12k_core_check_cc_code_bdfext(const struct dmi_header *hdr, void *data)
{
struct ath12k_base *ab = data;
const char *magic = ATH12K_SMBIOS_BDF_EXT_MAGIC;
@@ -651,6 +719,28 @@ static void ath12k_core_check_bdfext(const struct dmi_header *hdr, void *data)
return;
}
+ spin_lock_bh(&ab->base_lock);
+
+ switch (smbios->country_code_flag) {
+ case ATH12K_SMBIOS_CC_ISO:
+ ab->new_alpha2[0] = u16_get_bits(smbios->cc_code >> 8, 0xff);
+ ab->new_alpha2[1] = u16_get_bits(smbios->cc_code, 0xff);
+ ath12k_dbg(ab, ATH12K_DBG_BOOT, "boot smbios cc_code %c%c\n",
+ ab->new_alpha2[0], ab->new_alpha2[1]);
+ break;
+ case ATH12K_SMBIOS_CC_WW:
+ ab->new_alpha2[0] = '0';
+ ab->new_alpha2[1] = '0';
+ ath12k_dbg(ab, ATH12K_DBG_BOOT, "boot smbios worldwide regdomain\n");
+ break;
+ default:
+ ath12k_dbg(ab, ATH12K_DBG_BOOT, "boot ignore smbios country code setting %d\n",
+ smbios->country_code_flag);
+ break;
+ }
+
+ spin_unlock_bh(&ab->base_lock);
+
if (!smbios->bdf_enabled) {
ath12k_dbg(ab, ATH12K_DBG_BOOT, "bdf variant name not found.\n");
return;
@@ -690,7 +780,7 @@ static void ath12k_core_check_bdfext(const struct dmi_header *hdr, void *data)
int ath12k_core_check_smbios(struct ath12k_base *ab)
{
ab->qmi.target.bdf_ext[0] = '\0';
- dmi_walk(ath12k_core_check_bdfext, ab);
+ dmi_walk(ath12k_core_check_cc_code_bdfext, ab);
if (ab->qmi.target.bdf_ext[0] == '\0')
return -ENODATA;
@@ -721,6 +811,8 @@ static int ath12k_core_soc_create(struct ath12k_base *ab)
goto err_qmi_deinit;
}
+ ath12k_debugfs_pdev_create(ab);
+
return 0;
err_qmi_deinit:
@@ -851,9 +943,8 @@ static int ath12k_core_start(struct ath12k_base *ab)
ath12k_acpi_set_dsm_func(ab);
- if (!test_bit(ATH12K_FLAG_RECOVERY, &ab->dev_flags))
- /* Indicate the core start in the appropriate group */
- ath12k_core_started(ab);
+ /* Indicate the core start in the appropriate group */
+ ath12k_core_to_group_ref_get(ab);
return 0;
@@ -891,6 +982,9 @@ static void ath12k_core_hw_group_stop(struct ath12k_hw_group *ag)
ab = ag->ab[i];
if (!ab)
continue;
+
+ clear_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags);
+
ath12k_core_device_cleanup(ab);
}
@@ -1026,6 +1120,8 @@ core_pdev_create:
mutex_lock(&ab->core_lock);
+ set_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags);
+
ret = ath12k_core_pdev_create(ab);
if (ret) {
ath12k_err(ab, "failed to create pdev core %d\n", ret);
@@ -1084,6 +1180,59 @@ bool ath12k_core_hw_group_start_ready(struct ath12k_hw_group *ag)
return (ag->num_started == ag->num_devices);
}
+static void ath12k_fw_stats_pdevs_free(struct list_head *head)
+{
+ struct ath12k_fw_stats_pdev *i, *tmp;
+
+ list_for_each_entry_safe(i, tmp, head, list) {
+ list_del(&i->list);
+ kfree(i);
+ }
+}
+
+void ath12k_fw_stats_bcn_free(struct list_head *head)
+{
+ struct ath12k_fw_stats_bcn *i, *tmp;
+
+ list_for_each_entry_safe(i, tmp, head, list) {
+ list_del(&i->list);
+ kfree(i);
+ }
+}
+
+static void ath12k_fw_stats_vdevs_free(struct list_head *head)
+{
+ struct ath12k_fw_stats_vdev *i, *tmp;
+
+ list_for_each_entry_safe(i, tmp, head, list) {
+ list_del(&i->list);
+ kfree(i);
+ }
+}
+
+void ath12k_fw_stats_init(struct ath12k *ar)
+{
+ INIT_LIST_HEAD(&ar->fw_stats.vdevs);
+ INIT_LIST_HEAD(&ar->fw_stats.pdevs);
+ INIT_LIST_HEAD(&ar->fw_stats.bcn);
+ init_completion(&ar->fw_stats_complete);
+}
+
+void ath12k_fw_stats_free(struct ath12k_fw_stats *stats)
+{
+ ath12k_fw_stats_pdevs_free(&stats->pdevs);
+ ath12k_fw_stats_vdevs_free(&stats->vdevs);
+ ath12k_fw_stats_bcn_free(&stats->bcn);
+}
+
+void ath12k_fw_stats_reset(struct ath12k *ar)
+{
+ spin_lock_bh(&ar->data_lock);
+ ar->fw_stats.fw_stats_done = false;
+ ath12k_fw_stats_free(&ar->fw_stats);
+ spin_unlock_bh(&ar->data_lock);
+}
+
static void ath12k_core_trigger_partner(struct ath12k_base *ab)
{
struct ath12k_hw_group *ag = ab->ag;
@@ -1246,6 +1395,7 @@ static void ath12k_rfkill_work(struct work_struct *work)
void ath12k_core_halt(struct ath12k *ar)
{
+ struct list_head *pos, *n;
struct ath12k_base *ab = ar->ab;
lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
@@ -1258,10 +1408,16 @@ void ath12k_core_halt(struct ath12k *ar)
cancel_delayed_work_sync(&ar->scan.timeout);
cancel_work_sync(&ar->regd_update_work);
cancel_work_sync(&ab->rfkill_work);
+ cancel_work_sync(&ab->update_11d_work);
rcu_assign_pointer(ab->pdevs_active[ar->pdev_idx], NULL);
synchronize_rcu();
- INIT_LIST_HEAD(&ar->arvifs);
+
+ spin_lock_bh(&ar->data_lock);
+ list_for_each_safe(pos, n, &ar->arvifs)
+ list_del_init(pos);
+ spin_unlock_bh(&ar->data_lock);
+
idr_init(&ar->txmgmt_idr);
}
@@ -1285,14 +1441,28 @@ static void ath12k_core_pre_reconfigure_recovery(struct ath12k_base *ab)
ah->state == ATH12K_HW_STATE_TM)
continue;
+ wiphy_lock(ah->hw->wiphy);
+
+ /* If queue 0 is stopped, it is safe to assume that all
+ * other queues are stopped by driver via
+ * ieee80211_stop_queues() below. This means, there is
+ * no need to stop it again and hence continue
+ */
+ if (ieee80211_queue_stopped(ah->hw, 0)) {
+ wiphy_unlock(ah->hw->wiphy);
+ continue;
+ }
+
ieee80211_stop_queues(ah->hw);
for (j = 0; j < ah->num_radio; j++) {
ar = &ah->radio[j];
ath12k_mac_drain_tx(ar);
+ ar->state_11d = ATH12K_11D_IDLE;
+ complete(&ar->completed_11d_scan);
complete(&ar->scan.started);
- complete(&ar->scan.completed);
+ complete_all(&ar->scan.completed);
complete(&ar->scan.on_channel);
complete(&ar->peer_assoc_done);
complete(&ar->peer_delete_done);
@@ -1306,13 +1476,47 @@ static void ath12k_core_pre_reconfigure_recovery(struct ath12k_base *ab)
ath12k_mac_tx_mgmt_pending_free, ar);
idr_destroy(&ar->txmgmt_idr);
wake_up(&ar->txmgmt_empty_waitq);
+
+ ar->monitor_vdev_id = -1;
+ ar->monitor_vdev_created = false;
+ ar->monitor_started = false;
}
+
+ wiphy_unlock(ah->hw->wiphy);
}
wake_up(&ab->wmi_ab.tx_credits_wq);
wake_up(&ab->peer_mapping_wq);
}
+static void ath12k_update_11d(struct work_struct *work)
+{
+ struct ath12k_base *ab = container_of(work, struct ath12k_base, update_11d_work);
+ struct ath12k *ar;
+ struct ath12k_pdev *pdev;
+ struct wmi_set_current_country_arg arg = {};
+ int ret, i;
+
+ spin_lock_bh(&ab->base_lock);
+ memcpy(&arg.alpha2, &ab->new_alpha2, 2);
+ spin_unlock_bh(&ab->base_lock);
+
+ ath12k_dbg(ab, ATH12K_DBG_WMI, "update 11d new cc %c%c\n",
+ arg.alpha2[0], arg.alpha2[1]);
+
+ for (i = 0; i < ab->num_radios; i++) {
+ pdev = &ab->pdevs[i];
+ ar = pdev->ar;
+
+ memcpy(&ar->alpha2, &arg.alpha2, 2);
+ ret = ath12k_wmi_send_set_current_country_cmd(ar, &arg);
+ if (ret)
+ ath12k_warn(ar->ab,
+ "pdev id %d failed set current country code: %d\n",
+ i, ret);
+ }
+}
+
static void ath12k_core_post_reconfigure_recovery(struct ath12k_base *ab)
{
struct ath12k_hw_group *ag = ab->ag;
@@ -1386,19 +1590,30 @@ static void ath12k_core_restart(struct work_struct *work)
ath12k_dbg(ab, ATH12K_DBG_BOOT, "reset success\n");
}
+ mutex_lock(&ag->mutex);
+
+ if (!ath12k_core_hw_group_start_ready(ag)) {
+ mutex_unlock(&ag->mutex);
+ goto exit_restart;
+ }
+
for (i = 0; i < ag->num_hw; i++) {
- ah = ath12k_ag_to_ah(ab->ag, i);
+ ah = ath12k_ag_to_ah(ag, i);
ieee80211_restart_hw(ah->hw);
}
+
+ mutex_unlock(&ag->mutex);
}
+exit_restart:
complete(&ab->restart_completed);
}
static void ath12k_core_reset(struct work_struct *work)
{
struct ath12k_base *ab = container_of(work, struct ath12k_base, reset_work);
- int reset_count, fail_cont_count;
+ struct ath12k_hw_group *ag = ab->ag;
+ int reset_count, fail_cont_count, i;
long time_left;
if (!(test_bit(ATH12K_FLAG_QMI_FW_READY_COMPLETE, &ab->dev_flags))) {
@@ -1457,9 +1672,34 @@ static void ath12k_core_reset(struct work_struct *work)
ath12k_hif_ce_irq_disable(ab);
ath12k_hif_power_down(ab, false);
- ath12k_hif_power_up(ab);
- ath12k_dbg(ab, ATH12K_DBG_BOOT, "reset started\n");
+ /* prepare for power up */
+ ab->qmi.num_radios = U8_MAX;
+
+ mutex_lock(&ag->mutex);
+ ath12k_core_to_group_ref_put(ab);
+
+ if (ag->num_started > 0) {
+ ath12k_dbg(ab, ATH12K_DBG_BOOT,
+ "waiting for %d partner device(s) to reset\n",
+ ag->num_started);
+ mutex_unlock(&ag->mutex);
+ return;
+ }
+
+ /* Prepare MLO global memory region for power up */
+ ath12k_qmi_reset_mlo_mem(ag);
+
+ for (i = 0; i < ag->num_devices; i++) {
+ ab = ag->ab[i];
+ if (!ab)
+ continue;
+
+ ath12k_hif_power_up(ab);
+ ath12k_dbg(ab, ATH12K_DBG_BOOT, "reset started\n");
+ }
+
+ mutex_unlock(&ag->mutex);
}
int ath12k_core_pre_init(struct ath12k_base *ab)
@@ -1596,9 +1836,9 @@ static int ath12k_core_get_wsi_info(struct ath12k_hw_group *ag,
of_node_put(next_rx_endpoint);
device_count++;
- if (device_count > ATH12K_MAX_SOCS) {
+ if (device_count > ATH12K_MAX_DEVICES) {
ath12k_warn(ab, "device count in DT %d is more than limit %d\n",
- device_count, ATH12K_MAX_SOCS);
+ device_count, ATH12K_MAX_DEVICES);
of_node_put(next_wsi_dev);
return -EINVAL;
}
@@ -1774,7 +2014,7 @@ static void ath12k_core_hw_group_destroy(struct ath12k_hw_group *ag)
}
}
-static void ath12k_core_hw_group_cleanup(struct ath12k_hw_group *ag)
+void ath12k_core_hw_group_cleanup(struct ath12k_hw_group *ag)
{
struct ath12k_base *ab;
int i;
@@ -1843,20 +2083,18 @@ void ath12k_core_hw_group_set_mlo_capable(struct ath12k_hw_group *ag)
lockdep_assert_held(&ag->mutex);
- /* If more than one devices are grouped, then inter MLO
- * functionality can work still independent of whether internally
- * each device supports single_chip_mlo or not.
- * Only when there is one device, then disable for WCN chipsets
- * till the required driver implementation is in place.
- */
if (ag->num_devices == 1) {
ab = ag->ab[0];
-
- /* WCN chipsets does not advertise in firmware features
- * hence skip checking
- */
- if (ab->hw_params->def_num_link)
+ /* QCN9274 firmware uses firmware IE for MLO advertisement */
+ if (ab->fw.fw_features_valid) {
+ ag->mlo_capable =
+ ath12k_fw_feature_supported(ab, ATH12K_FW_FEATURE_MLO);
return;
+ }
+
+ /* while WCN7850 firmware uses QMI single_chip_mlo_support bit */
+ ag->mlo_capable = ab->single_chip_mlo_support;
+ return;
}
ag->mlo_capable = true;
@@ -1869,7 +2107,7 @@ void ath12k_core_hw_group_set_mlo_capable(struct ath12k_hw_group *ag)
/* even if 1 device's firmware feature indicates MLO
* unsupported, make MLO unsupported for the whole group
*/
- if (!test_bit(ATH12K_FW_FEATURE_MLO, ab->fw.fw_features)) {
+ if (!ath12k_fw_feature_supported(ab, ATH12K_FW_FEATURE_MLO)) {
ag->mlo_capable = false;
return;
}
@@ -1922,10 +2160,9 @@ err:
void ath12k_core_deinit(struct ath12k_base *ab)
{
- ath12k_core_panic_notifier_unregister(ab);
- ath12k_core_hw_group_cleanup(ab->ag);
ath12k_core_hw_group_destroy(ab->ag);
ath12k_core_hw_group_unassign(ab);
+ ath12k_core_panic_notifier_unregister(ab);
}
void ath12k_core_free(struct ath12k_base *ab)
@@ -1966,6 +2203,7 @@ struct ath12k_base *ath12k_core_alloc(struct device *dev, size_t priv_size,
INIT_WORK(&ab->reset_work, ath12k_core_reset);
INIT_WORK(&ab->rfkill_work, ath12k_rfkill_work);
INIT_WORK(&ab->dump_work, ath12k_coredump_upload);
+ INIT_WORK(&ab->update_11d_work, ath12k_update_11d);
timer_setup(&ab->rx_replenish_retry, ath12k_ce_rx_replenish_retry, 0);
init_completion(&ab->htc_suspend);
@@ -1975,6 +2213,7 @@ struct ath12k_base *ath12k_core_alloc(struct device *dev, size_t priv_size,
ab->dev = dev;
ab->hif.bus = bus;
ab->qmi.num_radios = U8_MAX;
+ ab->single_chip_mlo_support = false;
/* Device index used to identify the devices in a group.
*
@@ -1995,5 +2234,31 @@ err_sc_free:
return NULL;
}
-MODULE_DESCRIPTION("Core module for Qualcomm Atheros 802.11be wireless LAN cards.");
+static int ath12k_init(void)
+{
+ ahb_err = ath12k_ahb_init();
+ if (ahb_err)
+ pr_warn("Failed to initialize ath12k AHB device: %d\n", ahb_err);
+
+ pci_err = ath12k_pci_init();
+ if (pci_err)
+ pr_warn("Failed to initialize ath12k PCI device: %d\n", pci_err);
+
+ /* If both failed, return one of the failures (arbitrary) */
+ return ahb_err && pci_err ? ahb_err : 0;
+}
+
+static void ath12k_exit(void)
+{
+ if (!pci_err)
+ ath12k_pci_exit();
+
+ if (!ahb_err)
+ ath12k_ahb_exit();
+}
+
+module_init(ath12k_init);
+module_exit(ath12k_exit);
+
+MODULE_DESCRIPTION("Driver support for Qualcomm Technologies 802.11be WLAN devices");
MODULE_LICENSE("Dual BSD/GPL");