提交 ebc7a496 编写于 作者: K Kalle Valo

Merge ath-next from git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git

ath.git patches for v5.19. Major changes:

ath11k

* Wake-on-WLAN support for QCA6390 and WCN6855

* device recovery (firmware restart) support for QCA6390 and WCN6855

wcn36xx

* support for transmit rate reporting to user space
...@@ -728,20 +728,17 @@ static int ath10k_ahb_probe(struct platform_device *pdev) ...@@ -728,20 +728,17 @@ static int ath10k_ahb_probe(struct platform_device *pdev)
struct ath10k *ar; struct ath10k *ar;
struct ath10k_ahb *ar_ahb; struct ath10k_ahb *ar_ahb;
struct ath10k_pci *ar_pci; struct ath10k_pci *ar_pci;
const struct of_device_id *of_id;
enum ath10k_hw_rev hw_rev; enum ath10k_hw_rev hw_rev;
size_t size; size_t size;
int ret; int ret;
struct ath10k_bus_params bus_params = {}; struct ath10k_bus_params bus_params = {};
of_id = of_match_device(ath10k_ahb_of_match, &pdev->dev); hw_rev = (enum ath10k_hw_rev)of_device_get_match_data(&pdev->dev);
if (!of_id) { if (!hw_rev) {
dev_err(&pdev->dev, "failed to find matching device tree id\n"); dev_err(&pdev->dev, "OF data missing\n");
return -EINVAL; return -EINVAL;
} }
hw_rev = (enum ath10k_hw_rev)of_id->data;
size = sizeof(*ar_pci) + sizeof(*ar_ahb); size = sizeof(*ar_pci) + sizeof(*ar_ahb);
ar = ath10k_core_create(size, &pdev->dev, ATH10K_BUS_AHB, ar = ath10k_core_create(size, &pdev->dev, ATH10K_BUS_AHB,
hw_rev, &ath10k_ahb_hif_ops); hw_rev, &ath10k_ahb_hif_ops);
......
...@@ -94,6 +94,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { ...@@ -94,6 +94,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.credit_size_workaround = false, .credit_size_workaround = false,
.tx_stats_over_pktlog = true, .tx_stats_over_pktlog = true,
.dynamic_sar_support = false, .dynamic_sar_support = false,
.hw_restart_disconnect = false,
}, },
{ {
.id = QCA988X_HW_2_0_VERSION, .id = QCA988X_HW_2_0_VERSION,
...@@ -131,6 +132,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { ...@@ -131,6 +132,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.credit_size_workaround = false, .credit_size_workaround = false,
.tx_stats_over_pktlog = true, .tx_stats_over_pktlog = true,
.dynamic_sar_support = false, .dynamic_sar_support = false,
.hw_restart_disconnect = false,
}, },
{ {
.id = QCA9887_HW_1_0_VERSION, .id = QCA9887_HW_1_0_VERSION,
...@@ -169,6 +171,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { ...@@ -169,6 +171,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.credit_size_workaround = false, .credit_size_workaround = false,
.tx_stats_over_pktlog = false, .tx_stats_over_pktlog = false,
.dynamic_sar_support = false, .dynamic_sar_support = false,
.hw_restart_disconnect = false,
}, },
{ {
.id = QCA6174_HW_3_2_VERSION, .id = QCA6174_HW_3_2_VERSION,
...@@ -202,6 +205,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { ...@@ -202,6 +205,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.bmi_large_size_download = true, .bmi_large_size_download = true,
.supports_peer_stats_info = true, .supports_peer_stats_info = true,
.dynamic_sar_support = true, .dynamic_sar_support = true,
.hw_restart_disconnect = false,
}, },
{ {
.id = QCA6174_HW_2_1_VERSION, .id = QCA6174_HW_2_1_VERSION,
...@@ -239,6 +243,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { ...@@ -239,6 +243,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.credit_size_workaround = false, .credit_size_workaround = false,
.tx_stats_over_pktlog = false, .tx_stats_over_pktlog = false,
.dynamic_sar_support = false, .dynamic_sar_support = false,
.hw_restart_disconnect = false,
}, },
{ {
.id = QCA6174_HW_2_1_VERSION, .id = QCA6174_HW_2_1_VERSION,
...@@ -276,6 +281,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { ...@@ -276,6 +281,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.credit_size_workaround = false, .credit_size_workaround = false,
.tx_stats_over_pktlog = false, .tx_stats_over_pktlog = false,
.dynamic_sar_support = false, .dynamic_sar_support = false,
.hw_restart_disconnect = false,
}, },
{ {
.id = QCA6174_HW_3_0_VERSION, .id = QCA6174_HW_3_0_VERSION,
...@@ -313,6 +319,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { ...@@ -313,6 +319,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.credit_size_workaround = false, .credit_size_workaround = false,
.tx_stats_over_pktlog = false, .tx_stats_over_pktlog = false,
.dynamic_sar_support = false, .dynamic_sar_support = false,
.hw_restart_disconnect = false,
}, },
{ {
.id = QCA6174_HW_3_2_VERSION, .id = QCA6174_HW_3_2_VERSION,
...@@ -354,6 +361,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { ...@@ -354,6 +361,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.tx_stats_over_pktlog = false, .tx_stats_over_pktlog = false,
.supports_peer_stats_info = true, .supports_peer_stats_info = true,
.dynamic_sar_support = true, .dynamic_sar_support = true,
.hw_restart_disconnect = false,
}, },
{ {
.id = QCA99X0_HW_2_0_DEV_VERSION, .id = QCA99X0_HW_2_0_DEV_VERSION,
...@@ -397,6 +405,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { ...@@ -397,6 +405,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.credit_size_workaround = false, .credit_size_workaround = false,
.tx_stats_over_pktlog = false, .tx_stats_over_pktlog = false,
.dynamic_sar_support = false, .dynamic_sar_support = false,
.hw_restart_disconnect = false,
}, },
{ {
.id = QCA9984_HW_1_0_DEV_VERSION, .id = QCA9984_HW_1_0_DEV_VERSION,
...@@ -447,6 +456,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { ...@@ -447,6 +456,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.credit_size_workaround = false, .credit_size_workaround = false,
.tx_stats_over_pktlog = false, .tx_stats_over_pktlog = false,
.dynamic_sar_support = false, .dynamic_sar_support = false,
.hw_restart_disconnect = false,
}, },
{ {
.id = QCA9888_HW_2_0_DEV_VERSION, .id = QCA9888_HW_2_0_DEV_VERSION,
...@@ -494,6 +504,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { ...@@ -494,6 +504,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.credit_size_workaround = false, .credit_size_workaround = false,
.tx_stats_over_pktlog = false, .tx_stats_over_pktlog = false,
.dynamic_sar_support = false, .dynamic_sar_support = false,
.hw_restart_disconnect = false,
}, },
{ {
.id = QCA9377_HW_1_0_DEV_VERSION, .id = QCA9377_HW_1_0_DEV_VERSION,
...@@ -531,6 +542,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { ...@@ -531,6 +542,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.credit_size_workaround = false, .credit_size_workaround = false,
.tx_stats_over_pktlog = false, .tx_stats_over_pktlog = false,
.dynamic_sar_support = false, .dynamic_sar_support = false,
.hw_restart_disconnect = false,
}, },
{ {
.id = QCA9377_HW_1_1_DEV_VERSION, .id = QCA9377_HW_1_1_DEV_VERSION,
...@@ -570,6 +582,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { ...@@ -570,6 +582,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.credit_size_workaround = false, .credit_size_workaround = false,
.tx_stats_over_pktlog = false, .tx_stats_over_pktlog = false,
.dynamic_sar_support = false, .dynamic_sar_support = false,
.hw_restart_disconnect = false,
}, },
{ {
.id = QCA9377_HW_1_1_DEV_VERSION, .id = QCA9377_HW_1_1_DEV_VERSION,
...@@ -600,6 +613,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { ...@@ -600,6 +613,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.uart_pin_workaround = true, .uart_pin_workaround = true,
.credit_size_workaround = true, .credit_size_workaround = true,
.dynamic_sar_support = false, .dynamic_sar_support = false,
.hw_restart_disconnect = false,
}, },
{ {
.id = QCA4019_HW_1_0_DEV_VERSION, .id = QCA4019_HW_1_0_DEV_VERSION,
...@@ -644,6 +658,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { ...@@ -644,6 +658,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.credit_size_workaround = false, .credit_size_workaround = false,
.tx_stats_over_pktlog = false, .tx_stats_over_pktlog = false,
.dynamic_sar_support = false, .dynamic_sar_support = false,
.hw_restart_disconnect = false,
}, },
{ {
.id = WCN3990_HW_1_0_DEV_VERSION, .id = WCN3990_HW_1_0_DEV_VERSION,
...@@ -674,6 +689,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { ...@@ -674,6 +689,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.credit_size_workaround = false, .credit_size_workaround = false,
.tx_stats_over_pktlog = false, .tx_stats_over_pktlog = false,
.dynamic_sar_support = true, .dynamic_sar_support = true,
.hw_restart_disconnect = true,
}, },
}; };
...@@ -2442,6 +2458,7 @@ EXPORT_SYMBOL(ath10k_core_napi_sync_disable); ...@@ -2442,6 +2458,7 @@ EXPORT_SYMBOL(ath10k_core_napi_sync_disable);
static void ath10k_core_restart(struct work_struct *work) static void ath10k_core_restart(struct work_struct *work)
{ {
struct ath10k *ar = container_of(work, struct ath10k, restart_work); struct ath10k *ar = container_of(work, struct ath10k, restart_work);
struct ath10k_vif *arvif;
int ret; int ret;
set_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags); set_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags);
...@@ -2480,6 +2497,14 @@ static void ath10k_core_restart(struct work_struct *work) ...@@ -2480,6 +2497,14 @@ static void ath10k_core_restart(struct work_struct *work)
ar->state = ATH10K_STATE_RESTARTING; ar->state = ATH10K_STATE_RESTARTING;
ath10k_halt(ar); ath10k_halt(ar);
ath10k_scan_finish(ar); ath10k_scan_finish(ar);
if (ar->hw_params.hw_restart_disconnect) {
list_for_each_entry(arvif, &ar->arvifs, list) {
if (arvif->is_up &&
arvif->vdev_type == WMI_VDEV_TYPE_STA)
ieee80211_hw_restart_disconnect(arvif->vif);
}
}
ieee80211_restart_hw(ar->hw); ieee80211_restart_hw(ar->hw);
break; break;
case ATH10K_STATE_OFF: case ATH10K_STATE_OFF:
......
...@@ -633,6 +633,8 @@ struct ath10k_hw_params { ...@@ -633,6 +633,8 @@ struct ath10k_hw_params {
bool supports_peer_stats_info; bool supports_peer_stats_info;
bool dynamic_sar_support; bool dynamic_sar_support;
bool hw_restart_disconnect;
}; };
struct htt_resp; struct htt_resp;
......
...@@ -345,6 +345,12 @@ static void ath10k_usb_rx_complete(struct ath10k *ar, struct sk_buff *skb) ...@@ -345,6 +345,12 @@ static void ath10k_usb_rx_complete(struct ath10k *ar, struct sk_buff *skb)
ep->ep_ops.ep_rx_complete(ar, skb); ep->ep_ops.ep_rx_complete(ar, skb);
/* The RX complete handler now owns the skb... */ /* The RX complete handler now owns the skb... */
if (test_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags)) {
local_bh_disable();
napi_schedule(&ar->napi);
local_bh_enable();
}
return; return;
out_free_skb: out_free_skb:
...@@ -387,6 +393,7 @@ static int ath10k_usb_hif_start(struct ath10k *ar) ...@@ -387,6 +393,7 @@ static int ath10k_usb_hif_start(struct ath10k *ar)
int i; int i;
struct ath10k_usb *ar_usb = ath10k_usb_priv(ar); struct ath10k_usb *ar_usb = ath10k_usb_priv(ar);
ath10k_core_napi_enable(ar);
ath10k_usb_start_recv_pipes(ar); ath10k_usb_start_recv_pipes(ar);
/* set the TX resource avail threshold for each TX pipe */ /* set the TX resource avail threshold for each TX pipe */
...@@ -462,6 +469,7 @@ static int ath10k_usb_hif_tx_sg(struct ath10k *ar, u8 pipe_id, ...@@ -462,6 +469,7 @@ static int ath10k_usb_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
static void ath10k_usb_hif_stop(struct ath10k *ar) static void ath10k_usb_hif_stop(struct ath10k *ar)
{ {
ath10k_usb_flush_all(ar); ath10k_usb_flush_all(ar);
ath10k_core_napi_sync_disable(ar);
} }
static u16 ath10k_usb_hif_get_free_queue_number(struct ath10k *ar, u8 pipe_id) static u16 ath10k_usb_hif_get_free_queue_number(struct ath10k *ar, u8 pipe_id)
...@@ -966,6 +974,20 @@ static int ath10k_usb_create(struct ath10k *ar, ...@@ -966,6 +974,20 @@ static int ath10k_usb_create(struct ath10k *ar,
return ret; return ret;
} }
static int ath10k_usb_napi_poll(struct napi_struct *ctx, int budget)
{
struct ath10k *ar = container_of(ctx, struct ath10k, napi);
int done;
done = ath10k_htt_rx_hl_indication(ar, budget);
ath10k_dbg(ar, ATH10K_DBG_USB, "napi poll: done: %d, budget:%d\n", done, budget);
if (done < budget)
napi_complete_done(ctx, done);
return done;
}
/* ath10k usb driver registered functions */ /* ath10k usb driver registered functions */
static int ath10k_usb_probe(struct usb_interface *interface, static int ath10k_usb_probe(struct usb_interface *interface,
const struct usb_device_id *id) const struct usb_device_id *id)
...@@ -992,6 +1014,9 @@ static int ath10k_usb_probe(struct usb_interface *interface, ...@@ -992,6 +1014,9 @@ static int ath10k_usb_probe(struct usb_interface *interface,
return -ENOMEM; return -ENOMEM;
} }
netif_napi_add(&ar->napi_dev, &ar->napi, ath10k_usb_napi_poll,
ATH10K_NAPI_BUDGET);
usb_get_dev(dev); usb_get_dev(dev);
vendor_id = le16_to_cpu(dev->descriptor.idVendor); vendor_id = le16_to_cpu(dev->descriptor.idVendor);
product_id = le16_to_cpu(dev->descriptor.idProduct); product_id = le16_to_cpu(dev->descriptor.idProduct);
...@@ -1013,6 +1038,7 @@ static int ath10k_usb_probe(struct usb_interface *interface, ...@@ -1013,6 +1038,7 @@ static int ath10k_usb_probe(struct usb_interface *interface,
bus_params.dev_type = ATH10K_DEV_TYPE_HL; bus_params.dev_type = ATH10K_DEV_TYPE_HL;
/* TODO: don't know yet how to get chip_id with USB */ /* TODO: don't know yet how to get chip_id with USB */
bus_params.chip_id = 0; bus_params.chip_id = 0;
bus_params.hl_msdu_ids = true;
ret = ath10k_core_register(ar, &bus_params); ret = ath10k_core_register(ar, &bus_params);
if (ret) { if (ret) {
ath10k_warn(ar, "failed to register driver core: %d\n", ret); ath10k_warn(ar, "failed to register driver core: %d\n", ret);
...@@ -1044,6 +1070,7 @@ static void ath10k_usb_remove(struct usb_interface *interface) ...@@ -1044,6 +1070,7 @@ static void ath10k_usb_remove(struct usb_interface *interface)
return; return;
ath10k_core_unregister(ar_usb->ar); ath10k_core_unregister(ar_usb->ar);
netif_napi_del(&ar_usb->ar->napi);
ath10k_usb_destroy(ar_usb->ar); ath10k_usb_destroy(ar_usb->ar);
usb_put_dev(interface_to_usbdev(interface)); usb_put_dev(interface_to_usbdev(interface));
ath10k_core_destroy(ar_usb->ar); ath10k_core_destroy(ar_usb->ar);
......
...@@ -16,20 +16,20 @@ ath11k-y += core.o \ ...@@ -16,20 +16,20 @@ ath11k-y += core.o \
ce.o \ ce.o \
peer.o \ peer.o \
dbring.o \ dbring.o \
hw.o \ hw.o
wow.o
ath11k-$(CONFIG_ATH11K_DEBUGFS) += debugfs.o debugfs_htt_stats.o debugfs_sta.o ath11k-$(CONFIG_ATH11K_DEBUGFS) += debugfs.o debugfs_htt_stats.o debugfs_sta.o
ath11k-$(CONFIG_NL80211_TESTMODE) += testmode.o ath11k-$(CONFIG_NL80211_TESTMODE) += testmode.o
ath11k-$(CONFIG_ATH11K_TRACING) += trace.o ath11k-$(CONFIG_ATH11K_TRACING) += trace.o
ath11k-$(CONFIG_THERMAL) += thermal.o ath11k-$(CONFIG_THERMAL) += thermal.o
ath11k-$(CONFIG_ATH11K_SPECTRAL) += spectral.o ath11k-$(CONFIG_ATH11K_SPECTRAL) += spectral.o
ath11k-$(CONFIG_PM) += wow.o
obj-$(CONFIG_ATH11K_AHB) += ath11k_ahb.o obj-$(CONFIG_ATH11K_AHB) += ath11k_ahb.o
ath11k_ahb-y += ahb.o ath11k_ahb-y += ahb.o
obj-$(CONFIG_ATH11K_PCI) += ath11k_pci.o obj-$(CONFIG_ATH11K_PCI) += ath11k_pci.o
ath11k_pci-y += mhi.o pci.o ath11k_pci-y += mhi.o pci.o pcic.o
# for tracing framework to find trace.h # for tracing framework to find trace.h
CFLAGS_trace.o := -I$(src) CFLAGS_trace.o := -I$(src)
/* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* /*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/ */
#ifndef ATH11K_CORE_H #ifndef ATH11K_CORE_H
...@@ -10,6 +11,9 @@ ...@@ -10,6 +11,9 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/bitfield.h> #include <linux/bitfield.h>
#include <linux/dmi.h>
#include <linux/ctype.h>
#include <linux/rhashtable.h>
#include "qmi.h" #include "qmi.h"
#include "htc.h" #include "htc.h"
#include "wmi.h" #include "wmi.h"
...@@ -23,6 +27,7 @@ ...@@ -23,6 +27,7 @@
#include "thermal.h" #include "thermal.h"
#include "dbring.h" #include "dbring.h"
#include "spectral.h" #include "spectral.h"
#include "wow.h"
#define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK) #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK)
...@@ -36,9 +41,26 @@ ...@@ -36,9 +41,26 @@
#define ATH11K_INVALID_HW_MAC_ID 0xFF #define ATH11K_INVALID_HW_MAC_ID 0xFF
#define ATH11K_CONNECTION_LOSS_HZ (3 * HZ) #define ATH11K_CONNECTION_LOSS_HZ (3 * HZ)
/* SMBIOS type containing Board Data File Name Extension */
#define ATH11K_SMBIOS_BDF_EXT_TYPE 0xF8
/* SMBIOS type structure length (excluding strings-set) */
#define ATH11K_SMBIOS_BDF_EXT_LENGTH 0x9
/* The magic used by QCA spec */
#define ATH11K_SMBIOS_BDF_EXT_MAGIC "BDF_"
extern unsigned int ath11k_frame_mode; extern unsigned int ath11k_frame_mode;
#define ATH11K_SCAN_TIMEOUT_HZ (20 * HZ)
#define ATH11K_MON_TIMER_INTERVAL 10 #define ATH11K_MON_TIMER_INTERVAL 10
#define ATH11K_RESET_TIMEOUT_HZ (20 * HZ)
#define ATH11K_RESET_MAX_FAIL_COUNT_FIRST 3
#define ATH11K_RESET_MAX_FAIL_COUNT_FINAL 5
#define ATH11K_RESET_FAIL_TIMEOUT_HZ (20 * HZ)
#define ATH11K_RECONFIGURE_TIMEOUT_HZ (10 * HZ)
#define ATH11K_RECOVER_START_TIMEOUT_HZ (20 * HZ)
enum ath11k_supported_bw { enum ath11k_supported_bw {
ATH11K_BW_20 = 0, ATH11K_BW_20 = 0,
...@@ -147,6 +169,13 @@ struct ath11k_ext_irq_grp { ...@@ -147,6 +169,13 @@ struct ath11k_ext_irq_grp {
struct net_device napi_ndev; struct net_device napi_ndev;
}; };
struct ath11k_smbios_bdf {
struct dmi_header hdr;
u32 padding;
u8 bdf_enabled;
u8 bdf_ext[];
};
#define HEHANDLE_CAP_PHYINFO_SIZE 3 #define HEHANDLE_CAP_PHYINFO_SIZE 3
#define HECAP_PHYINFO_SIZE 9 #define HECAP_PHYINFO_SIZE 9
#define HECAP_MACINFO_SIZE 5 #define HECAP_MACINFO_SIZE 5
...@@ -189,6 +218,12 @@ enum ath11k_scan_state { ...@@ -189,6 +218,12 @@ enum ath11k_scan_state {
ATH11K_SCAN_ABORTING, ATH11K_SCAN_ABORTING,
}; };
enum ath11k_11d_state {
ATH11K_11D_IDLE,
ATH11K_11D_PREPARING,
ATH11K_11D_RUNNING,
};
enum ath11k_dev_flags { enum ath11k_dev_flags {
ATH11K_CAC_RUNNING, ATH11K_CAC_RUNNING,
ATH11K_FLAG_CORE_REGISTERED, ATH11K_FLAG_CORE_REGISTERED,
...@@ -204,6 +239,8 @@ enum ath11k_dev_flags { ...@@ -204,6 +239,8 @@ enum ath11k_dev_flags {
ATH11K_FLAG_CE_IRQ_ENABLED, ATH11K_FLAG_CE_IRQ_ENABLED,
ATH11K_FLAG_EXT_IRQ_ENABLED, ATH11K_FLAG_EXT_IRQ_ENABLED,
ATH11K_FLAG_FIXED_MEM_RGN, ATH11K_FLAG_FIXED_MEM_RGN,
ATH11K_FLAG_DEVICE_INIT_DONE,
ATH11K_FLAG_MULTI_MSI_VECTORS,
}; };
enum ath11k_monitor_flags { enum ath11k_monitor_flags {
...@@ -212,6 +249,30 @@ enum ath11k_monitor_flags { ...@@ -212,6 +249,30 @@ enum ath11k_monitor_flags {
ATH11K_FLAG_MONITOR_VDEV_CREATED, ATH11K_FLAG_MONITOR_VDEV_CREATED,
}; };
#define ATH11K_IPV6_UC_TYPE 0
#define ATH11K_IPV6_AC_TYPE 1
#define ATH11K_IPV6_MAX_COUNT 16
#define ATH11K_IPV4_MAX_COUNT 2
struct ath11k_arp_ns_offload {
u8 ipv4_addr[ATH11K_IPV4_MAX_COUNT][4];
u32 ipv4_count;
u32 ipv6_count;
u8 ipv6_addr[ATH11K_IPV6_MAX_COUNT][16];
u8 self_ipv6_addr[ATH11K_IPV6_MAX_COUNT][16];
u8 ipv6_type[ATH11K_IPV6_MAX_COUNT];
bool ipv6_valid[ATH11K_IPV6_MAX_COUNT];
u8 mac_addr[ETH_ALEN];
};
struct ath11k_rekey_data {
u8 kck[NL80211_KCK_LEN];
u8 kek[NL80211_KCK_LEN];
u64 replay_ctr;
bool enable_offload;
};
struct ath11k_vif { struct ath11k_vif {
u32 vdev_id; u32 vdev_id;
enum wmi_vdev_type vdev_type; enum wmi_vdev_type vdev_type;
...@@ -263,6 +324,9 @@ struct ath11k_vif { ...@@ -263,6 +324,9 @@ struct ath11k_vif {
bool bcca_zero_sent; bool bcca_zero_sent;
bool do_not_send_tmpl; bool do_not_send_tmpl;
struct ieee80211_chanctx_conf chanctx; struct ieee80211_chanctx_conf chanctx;
struct ath11k_arp_ns_offload arp_ns_offload;
struct ath11k_rekey_data rekey_data;
#ifdef CONFIG_ATH11K_DEBUGFS #ifdef CONFIG_ATH11K_DEBUGFS
struct dentry *debugfs_twt; struct dentry *debugfs_twt;
#endif /* CONFIG_ATH11K_DEBUGFS */ #endif /* CONFIG_ATH11K_DEBUGFS */
...@@ -590,6 +654,9 @@ struct ath11k { ...@@ -590,6 +654,9 @@ struct ath11k {
struct work_struct wmi_mgmt_tx_work; struct work_struct wmi_mgmt_tx_work;
struct sk_buff_head wmi_mgmt_tx_queue; struct sk_buff_head wmi_mgmt_tx_queue;
struct ath11k_wow wow;
struct completion target_suspend;
bool target_suspend_ack;
struct ath11k_per_peer_tx_stats peer_tx_stats; struct ath11k_per_peer_tx_stats peer_tx_stats;
struct list_head ppdu_stats_info; struct list_head ppdu_stats_info;
u32 ppdu_stat_list_depth; u32 ppdu_stat_list_depth;
...@@ -607,12 +674,13 @@ struct ath11k { ...@@ -607,12 +674,13 @@ struct ath11k {
bool dfs_block_radar_events; bool dfs_block_radar_events;
struct ath11k_thermal thermal; struct ath11k_thermal thermal;
u32 vdev_id_11d_scan; u32 vdev_id_11d_scan;
struct completion finish_11d_scan; struct completion completed_11d_scan;
struct completion finish_11d_ch_list; enum ath11k_11d_state state_11d;
bool pending_11d;
bool regdom_set_by_user; bool regdom_set_by_user;
int hw_rate_code; int hw_rate_code;
u8 twt_enabled; u8 twt_enabled;
bool nlo_enabled;
u8 alpha2[REG_ALPHA2_LEN + 1];
}; };
struct ath11k_band_cap { struct ath11k_band_cap {
...@@ -662,6 +730,14 @@ struct ath11k_bus_params { ...@@ -662,6 +730,14 @@ struct ath11k_bus_params {
bool static_window_map; bool static_window_map;
}; };
struct ath11k_pci_ops {
int (*wakeup)(struct ath11k_base *ab);
void (*release)(struct ath11k_base *ab);
int (*get_msi_irq)(struct ath11k_base *ab, unsigned int vector);
void (*window_write32)(struct ath11k_base *ab, u32 offset, u32 value);
u32 (*window_read32)(struct ath11k_base *ab, u32 offset);
};
/* IPQ8074 HW channel counters frequency value in hertz */ /* IPQ8074 HW channel counters frequency value in hertz */
#define IPQ8074_CC_FREQ_HERTZ 320000 #define IPQ8074_CC_FREQ_HERTZ 320000
...@@ -703,6 +779,19 @@ struct ath11k_soc_dp_stats { ...@@ -703,6 +779,19 @@ struct ath11k_soc_dp_stats {
struct ath11k_dp_ring_bp_stats bp_stats; struct ath11k_dp_ring_bp_stats bp_stats;
}; };
struct ath11k_msi_user {
char *name;
int num_vectors;
u32 base_vector;
};
struct ath11k_msi_config {
int total_vectors;
int total_users;
struct ath11k_msi_user *users;
u16 hw_rev;
};
/* Master structure to hold the hw data which may be used in core module */ /* Master structure to hold the hw data which may be used in core module */
struct ath11k_base { struct ath11k_base {
enum ath11k_hw_rev hw_rev; enum ath11k_hw_rev hw_rev;
...@@ -747,6 +836,18 @@ struct ath11k_base { ...@@ -747,6 +836,18 @@ struct ath11k_base {
struct ath11k_pdev __rcu *pdevs_active[MAX_RADIOS]; struct ath11k_pdev __rcu *pdevs_active[MAX_RADIOS];
struct ath11k_hal_reg_capabilities_ext hal_reg_cap[MAX_RADIOS]; struct ath11k_hal_reg_capabilities_ext hal_reg_cap[MAX_RADIOS];
unsigned long long free_vdev_map; unsigned long long free_vdev_map;
/* To synchronize rhash tbl write operation */
struct mutex tbl_mtx_lock;
/* The rhashtable containing struct ath11k_peer keyed by mac addr */
struct rhashtable *rhead_peer_addr;
struct rhashtable_params rhash_peer_addr_param;
/* The rhashtable containing struct ath11k_peer keyed by id */
struct rhashtable *rhead_peer_id;
struct rhashtable_params rhash_peer_id_param;
struct list_head peers; struct list_head peers;
wait_queue_head_t peer_mapping_wq; wait_queue_head_t peer_mapping_wq;
u8 mac_addr[ETH_ALEN]; u8 mac_addr[ETH_ALEN];
...@@ -788,6 +889,18 @@ struct ath11k_base { ...@@ -788,6 +889,18 @@ struct ath11k_base {
struct work_struct restart_work; struct work_struct restart_work;
struct work_struct update_11d_work; struct work_struct update_11d_work;
u8 new_alpha2[3]; u8 new_alpha2[3];
struct workqueue_struct *workqueue_aux;
struct work_struct reset_work;
atomic_t reset_count;
atomic_t recovery_count;
atomic_t recovery_start_count;
bool is_reset;
struct completion reset_complete;
struct completion reconfigure_complete;
struct completion recovery_start;
/* continuous recovery fail count */
atomic_t fail_cont_count;
unsigned long reset_fail_timeout;
struct { struct {
/* protected by data_lock */ /* protected by data_lock */
u32 fw_crash_counter; u32 fw_crash_counter;
...@@ -815,6 +928,17 @@ struct ath11k_base { ...@@ -815,6 +928,17 @@ struct ath11k_base {
u32 subsystem_device; u32 subsystem_device;
} id; } id;
struct {
struct {
const struct ath11k_msi_config *config;
u32 ep_base_data;
u32 addr_lo;
u32 addr_hi;
} msi;
const struct ath11k_pci_ops *ops;
} pci;
/* must be last */ /* must be last */
u8 drv_priv[] __aligned(sizeof(void *)); u8 drv_priv[] __aligned(sizeof(void *));
}; };
...@@ -996,7 +1120,7 @@ int ath11k_core_fetch_board_data_api_1(struct ath11k_base *ab, ...@@ -996,7 +1120,7 @@ int ath11k_core_fetch_board_data_api_1(struct ath11k_base *ab,
const char *name); const char *name);
void ath11k_core_free_bdf(struct ath11k_base *ab, struct ath11k_board_data *bd); void ath11k_core_free_bdf(struct ath11k_base *ab, struct ath11k_board_data *bd);
int ath11k_core_check_dt(struct ath11k_base *ath11k); int ath11k_core_check_dt(struct ath11k_base *ath11k);
int ath11k_core_check_smbios(struct ath11k_base *ab);
void ath11k_core_halt(struct ath11k *ar); void ath11k_core_halt(struct ath11k *ar);
int ath11k_core_resume(struct ath11k_base *ab); int ath11k_core_resume(struct ath11k_base *ab);
int ath11k_core_suspend(struct ath11k_base *ab); int ath11k_core_suspend(struct ath11k_base *ab);
......
...@@ -596,6 +596,10 @@ static ssize_t ath11k_write_simulate_fw_crash(struct file *file, ...@@ -596,6 +596,10 @@ static ssize_t ath11k_write_simulate_fw_crash(struct file *file,
ret = ath11k_wmi_force_fw_hang_cmd(ar, ret = ath11k_wmi_force_fw_hang_cmd(ar,
ATH11K_WMI_FW_HANG_ASSERT_TYPE, ATH11K_WMI_FW_HANG_ASSERT_TYPE,
ATH11K_WMI_FW_HANG_DELAY); ATH11K_WMI_FW_HANG_DELAY);
} else if (!strcmp(buf, "hw-restart")) {
ath11k_info(ab, "user requested hw restart\n");
queue_work(ab->workqueue_aux, &ab->reset_work);
ret = 0;
} else { } else {
ret = -EINVAL; ret = -EINVAL;
goto exit; goto exit;
......
...@@ -272,6 +272,11 @@ void ath11k_htc_tx_completion_handler(struct ath11k_base *ab, ...@@ -272,6 +272,11 @@ void ath11k_htc_tx_completion_handler(struct ath11k_base *ab,
ep_tx_complete(htc->ab, skb); ep_tx_complete(htc->ab, skb);
} }
static void ath11k_htc_wakeup_from_suspend(struct ath11k_base *ab)
{
ath11k_dbg(ab, ATH11K_DBG_BOOT, "boot wakeup from suspend is received\n");
}
void ath11k_htc_rx_completion_handler(struct ath11k_base *ab, void ath11k_htc_rx_completion_handler(struct ath11k_base *ab,
struct sk_buff *skb) struct sk_buff *skb)
{ {
...@@ -376,6 +381,7 @@ void ath11k_htc_rx_completion_handler(struct ath11k_base *ab, ...@@ -376,6 +381,7 @@ void ath11k_htc_rx_completion_handler(struct ath11k_base *ab,
ath11k_htc_suspend_complete(ab, false); ath11k_htc_suspend_complete(ab, false);
break; break;
case ATH11K_HTC_MSG_WAKEUP_FROM_SUSPEND_ID: case ATH11K_HTC_MSG_WAKEUP_FROM_SUSPEND_ID:
ath11k_htc_wakeup_from_suspend(ab);
break; break;
default: default:
ath11k_warn(ab, "ignoring unsolicited htc ep0 event %ld\n", ath11k_warn(ab, "ignoring unsolicited htc ep0 event %ld\n",
......
/* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* /*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved.
*/ */
#ifndef ATH11K_HW_H #ifndef ATH11K_HW_H
...@@ -189,11 +190,11 @@ struct ath11k_hw_params { ...@@ -189,11 +190,11 @@ struct ath11k_hw_params {
const struct ath11k_hw_hal_params *hal_params; const struct ath11k_hw_hal_params *hal_params;
bool supports_dynamic_smps_6ghz; bool supports_dynamic_smps_6ghz;
bool alloc_cacheable_memory; bool alloc_cacheable_memory;
bool wakeup_mhi;
bool supports_rssi_stats; bool supports_rssi_stats;
bool fw_wmi_diag_event; bool fw_wmi_diag_event;
bool current_cc_support; bool current_cc_support;
bool dbr_debug_support; bool dbr_debug_support;
bool global_reset;
}; };
struct ath11k_hw_ops { struct ath11k_hw_ops {
...@@ -290,10 +291,16 @@ enum ath11k_bd_ie_board_type { ...@@ -290,10 +291,16 @@ enum ath11k_bd_ie_board_type {
ATH11K_BD_IE_BOARD_DATA = 1, ATH11K_BD_IE_BOARD_DATA = 1,
}; };
enum ath11k_bd_ie_regdb_type {
ATH11K_BD_IE_REGDB_NAME = 0,
ATH11K_BD_IE_REGDB_DATA = 1,
};
enum ath11k_bd_ie_type { enum ath11k_bd_ie_type {
/* contains sub IEs of enum ath11k_bd_ie_board_type */ /* contains sub IEs of enum ath11k_bd_ie_board_type */
ATH11K_BD_IE_BOARD = 0, ATH11K_BD_IE_BOARD = 0,
ATH11K_BD_IE_BOARD_EXT = 1, /* contains sub IEs of enum ath11k_bd_ie_regdb_type */
ATH11K_BD_IE_REGDB = 1,
}; };
struct ath11k_hw_regs { struct ath11k_hw_regs {
...@@ -361,4 +368,16 @@ extern const struct ath11k_hw_regs qca6390_regs; ...@@ -361,4 +368,16 @@ extern const struct ath11k_hw_regs qca6390_regs;
extern const struct ath11k_hw_regs qcn9074_regs; extern const struct ath11k_hw_regs qcn9074_regs;
extern const struct ath11k_hw_regs wcn6855_regs; extern const struct ath11k_hw_regs wcn6855_regs;
static inline const char *ath11k_bd_ie_type_str(enum ath11k_bd_ie_type type)
{
switch (type) {
case ATH11K_BD_IE_BOARD:
return "board data";
case ATH11K_BD_IE_REGDB:
return "regdb data";
}
return "unknown";
}
#endif #endif
...@@ -130,7 +130,7 @@ extern const struct htt_rx_ring_tlv_filter ath11k_mac_mon_status_filter_default; ...@@ -130,7 +130,7 @@ extern const struct htt_rx_ring_tlv_filter ath11k_mac_mon_status_filter_default;
#define ATH11K_SCAN_11D_INTERVAL 600000 #define ATH11K_SCAN_11D_INTERVAL 600000
#define ATH11K_11D_INVALID_VDEV_ID 0xFFFF #define ATH11K_11D_INVALID_VDEV_ID 0xFFFF
void ath11k_mac_11d_scan_start(struct ath11k *ar, u32 vdev_id, bool wait); void ath11k_mac_11d_scan_start(struct ath11k *ar, u32 vdev_id);
void ath11k_mac_11d_scan_stop(struct ath11k *ar); void ath11k_mac_11d_scan_stop(struct ath11k *ar);
void ath11k_mac_11d_scan_stop_all(struct ath11k_base *ab); void ath11k_mac_11d_scan_stop_all(struct ath11k_base *ab);
...@@ -172,4 +172,5 @@ enum hal_encrypt_type ath11k_dp_tx_get_encrypt_type(u32 cipher); ...@@ -172,4 +172,5 @@ enum hal_encrypt_type ath11k_dp_tx_get_encrypt_type(u32 cipher);
void ath11k_mac_handle_beacon(struct ath11k *ar, struct sk_buff *skb); void ath11k_mac_handle_beacon(struct ath11k *ar, struct sk_buff *skb);
void ath11k_mac_handle_beacon_miss(struct ath11k *ar, u32 vdev_id); void ath11k_mac_handle_beacon_miss(struct ath11k *ar, u32 vdev_id);
void ath11k_mac_bcn_tx_event(struct ath11k_vif *arvif); void ath11k_mac_bcn_tx_event(struct ath11k_vif *arvif);
int ath11k_mac_wait_tx_complete(struct ath11k *ar);
#endif #endif
// SPDX-License-Identifier: BSD-3-Clause-Clear // SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (c) 2020 The Linux Foundation. All rights reserved. */ /*
* Copyright (c) 2020 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/msi.h> #include <linux/msi.h>
#include <linux/pci.h> #include <linux/pci.h>
...@@ -11,6 +14,7 @@ ...@@ -11,6 +14,7 @@
#include "debug.h" #include "debug.h"
#include "mhi.h" #include "mhi.h"
#include "pci.h" #include "pci.h"
#include "pcic.h"
#define MHI_TIMEOUT_DEFAULT_MS 90000 #define MHI_TIMEOUT_DEFAULT_MS 90000
#define RDDM_DUMP_SIZE 0x420000 #define RDDM_DUMP_SIZE 0x420000
...@@ -205,7 +209,7 @@ void ath11k_mhi_set_mhictrl_reset(struct ath11k_base *ab) ...@@ -205,7 +209,7 @@ void ath11k_mhi_set_mhictrl_reset(struct ath11k_base *ab)
{ {
u32 val; u32 val;
val = ath11k_pci_read32(ab, MHISTATUS); val = ath11k_pcic_read32(ab, MHISTATUS);
ath11k_dbg(ab, ATH11K_DBG_PCI, "MHISTATUS 0x%x\n", val); ath11k_dbg(ab, ATH11K_DBG_PCI, "MHISTATUS 0x%x\n", val);
...@@ -213,29 +217,29 @@ void ath11k_mhi_set_mhictrl_reset(struct ath11k_base *ab) ...@@ -213,29 +217,29 @@ void ath11k_mhi_set_mhictrl_reset(struct ath11k_base *ab)
* has SYSERR bit set and thus need to set MHICTRL_RESET * has SYSERR bit set and thus need to set MHICTRL_RESET
* to clear SYSERR. * to clear SYSERR.
*/ */
ath11k_pci_write32(ab, MHICTRL, MHICTRL_RESET_MASK); ath11k_pcic_write32(ab, MHICTRL, MHICTRL_RESET_MASK);
mdelay(10); mdelay(10);
} }
static void ath11k_mhi_reset_txvecdb(struct ath11k_base *ab) static void ath11k_mhi_reset_txvecdb(struct ath11k_base *ab)
{ {
ath11k_pci_write32(ab, PCIE_TXVECDB, 0); ath11k_pcic_write32(ab, PCIE_TXVECDB, 0);
} }
static void ath11k_mhi_reset_txvecstatus(struct ath11k_base *ab) static void ath11k_mhi_reset_txvecstatus(struct ath11k_base *ab)
{ {
ath11k_pci_write32(ab, PCIE_TXVECSTATUS, 0); ath11k_pcic_write32(ab, PCIE_TXVECSTATUS, 0);
} }
static void ath11k_mhi_reset_rxvecdb(struct ath11k_base *ab) static void ath11k_mhi_reset_rxvecdb(struct ath11k_base *ab)
{ {
ath11k_pci_write32(ab, PCIE_RXVECDB, 0); ath11k_pcic_write32(ab, PCIE_RXVECDB, 0);
} }
static void ath11k_mhi_reset_rxvecstatus(struct ath11k_base *ab) static void ath11k_mhi_reset_rxvecstatus(struct ath11k_base *ab)
{ {
ath11k_pci_write32(ab, PCIE_RXVECSTATUS, 0); ath11k_pcic_write32(ab, PCIE_RXVECSTATUS, 0);
} }
void ath11k_mhi_clear_vector(struct ath11k_base *ab) void ath11k_mhi_clear_vector(struct ath11k_base *ab)
...@@ -254,9 +258,8 @@ static int ath11k_mhi_get_msi(struct ath11k_pci *ab_pci) ...@@ -254,9 +258,8 @@ static int ath11k_mhi_get_msi(struct ath11k_pci *ab_pci)
int *irq; int *irq;
unsigned int msi_data; unsigned int msi_data;
ret = ath11k_pci_get_user_msi_assignment(ab_pci, ret = ath11k_pcic_get_user_msi_assignment(ab, "MHI", &num_vectors,
"MHI", &num_vectors, &user_base_data, &base_vector);
&user_base_data, &base_vector);
if (ret) if (ret)
return ret; return ret;
...@@ -270,11 +273,10 @@ static int ath11k_mhi_get_msi(struct ath11k_pci *ab_pci) ...@@ -270,11 +273,10 @@ static int ath11k_mhi_get_msi(struct ath11k_pci *ab_pci)
for (i = 0; i < num_vectors; i++) { for (i = 0; i < num_vectors; i++) {
msi_data = base_vector; msi_data = base_vector;
if (test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags)) if (test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))
msi_data += i; msi_data += i;
irq[i] = ath11k_pci_get_msi_irq(ab->dev, irq[i] = ath11k_pci_get_msi_irq(ab, msi_data);
msi_data);
} }
ab_pci->mhi_ctrl->irq = irq; ab_pci->mhi_ctrl->irq = irq;
...@@ -292,15 +294,48 @@ static void ath11k_mhi_op_runtime_put(struct mhi_controller *mhi_cntrl) ...@@ -292,15 +294,48 @@ static void ath11k_mhi_op_runtime_put(struct mhi_controller *mhi_cntrl)
{ {
} }
static char *ath11k_mhi_op_callback_to_str(enum mhi_callback reason)
{
switch (reason) {
case MHI_CB_IDLE:
return "MHI_CB_IDLE";
case MHI_CB_PENDING_DATA:
return "MHI_CB_PENDING_DATA";
case MHI_CB_LPM_ENTER:
return "MHI_CB_LPM_ENTER";
case MHI_CB_LPM_EXIT:
return "MHI_CB_LPM_EXIT";
case MHI_CB_EE_RDDM:
return "MHI_CB_EE_RDDM";
case MHI_CB_EE_MISSION_MODE:
return "MHI_CB_EE_MISSION_MODE";
case MHI_CB_SYS_ERROR:
return "MHI_CB_SYS_ERROR";
case MHI_CB_FATAL_ERROR:
return "MHI_CB_FATAL_ERROR";
case MHI_CB_BW_REQ:
return "MHI_CB_BW_REQ";
default:
return "UNKNOWN";
}
};
static void ath11k_mhi_op_status_cb(struct mhi_controller *mhi_cntrl, static void ath11k_mhi_op_status_cb(struct mhi_controller *mhi_cntrl,
enum mhi_callback cb) enum mhi_callback cb)
{ {
struct ath11k_base *ab = dev_get_drvdata(mhi_cntrl->cntrl_dev); struct ath11k_base *ab = dev_get_drvdata(mhi_cntrl->cntrl_dev);
ath11k_dbg(ab, ATH11K_DBG_BOOT, "mhi notify status reason %s\n",
ath11k_mhi_op_callback_to_str(cb));
switch (cb) { switch (cb) {
case MHI_CB_SYS_ERROR: case MHI_CB_SYS_ERROR:
ath11k_warn(ab, "firmware crashed: MHI_CB_SYS_ERROR\n"); ath11k_warn(ab, "firmware crashed: MHI_CB_SYS_ERROR\n");
break; break;
case MHI_CB_EE_RDDM:
if (!(test_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags)))
queue_work(ab->workqueue_aux, &ab->reset_work);
break;
default: default:
break; break;
} }
...@@ -371,7 +406,7 @@ int ath11k_mhi_register(struct ath11k_pci *ab_pci) ...@@ -371,7 +406,7 @@ int ath11k_mhi_register(struct ath11k_pci *ab_pci)
return ret; return ret;
} }
if (!test_bit(ATH11K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags)) if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))
mhi_ctrl->irq_flags = IRQF_SHARED | IRQF_NOBALANCING; mhi_ctrl->irq_flags = IRQF_SHARED | IRQF_NOBALANCING;
if (test_bit(ATH11K_FLAG_FIXED_MEM_RGN, &ab->dev_flags)) { if (test_bit(ATH11K_FLAG_FIXED_MEM_RGN, &ab->dev_flags)) {
...@@ -428,216 +463,62 @@ void ath11k_mhi_unregister(struct ath11k_pci *ab_pci) ...@@ -428,216 +463,62 @@ void ath11k_mhi_unregister(struct ath11k_pci *ab_pci)
mhi_free_controller(mhi_ctrl); mhi_free_controller(mhi_ctrl);
} }
static char *ath11k_mhi_state_to_str(enum ath11k_mhi_state mhi_state) int ath11k_mhi_start(struct ath11k_pci *ab_pci)
{
switch (mhi_state) {
case ATH11K_MHI_INIT:
return "INIT";
case ATH11K_MHI_DEINIT:
return "DEINIT";
case ATH11K_MHI_POWER_ON:
return "POWER_ON";
case ATH11K_MHI_POWER_OFF:
return "POWER_OFF";
case ATH11K_MHI_FORCE_POWER_OFF:
return "FORCE_POWER_OFF";
case ATH11K_MHI_SUSPEND:
return "SUSPEND";
case ATH11K_MHI_RESUME:
return "RESUME";
case ATH11K_MHI_TRIGGER_RDDM:
return "TRIGGER_RDDM";
case ATH11K_MHI_RDDM_DONE:
return "RDDM_DONE";
default:
return "UNKNOWN";
}
};
static void ath11k_mhi_set_state_bit(struct ath11k_pci *ab_pci,
enum ath11k_mhi_state mhi_state)
{ {
struct ath11k_base *ab = ab_pci->ab; struct ath11k_base *ab = ab_pci->ab;
int ret;
switch (mhi_state) { ab_pci->mhi_ctrl->timeout_ms = MHI_TIMEOUT_DEFAULT_MS;
case ATH11K_MHI_INIT:
set_bit(ATH11K_MHI_INIT, &ab_pci->mhi_state);
break;
case ATH11K_MHI_DEINIT:
clear_bit(ATH11K_MHI_INIT, &ab_pci->mhi_state);
break;
case ATH11K_MHI_POWER_ON:
set_bit(ATH11K_MHI_POWER_ON, &ab_pci->mhi_state);
break;
case ATH11K_MHI_POWER_OFF:
case ATH11K_MHI_FORCE_POWER_OFF:
clear_bit(ATH11K_MHI_POWER_ON, &ab_pci->mhi_state);
clear_bit(ATH11K_MHI_TRIGGER_RDDM, &ab_pci->mhi_state);
clear_bit(ATH11K_MHI_RDDM_DONE, &ab_pci->mhi_state);
break;
case ATH11K_MHI_SUSPEND:
set_bit(ATH11K_MHI_SUSPEND, &ab_pci->mhi_state);
break;
case ATH11K_MHI_RESUME:
clear_bit(ATH11K_MHI_SUSPEND, &ab_pci->mhi_state);
break;
case ATH11K_MHI_TRIGGER_RDDM:
set_bit(ATH11K_MHI_TRIGGER_RDDM, &ab_pci->mhi_state);
break;
case ATH11K_MHI_RDDM_DONE:
set_bit(ATH11K_MHI_RDDM_DONE, &ab_pci->mhi_state);
break;
default:
ath11k_err(ab, "unhandled mhi state (%d)\n", mhi_state);
}
}
static int ath11k_mhi_check_state_bit(struct ath11k_pci *ab_pci, ret = mhi_prepare_for_power_up(ab_pci->mhi_ctrl);
enum ath11k_mhi_state mhi_state) if (ret) {
{ ath11k_warn(ab, "failed to prepare mhi: %d", ret);
struct ath11k_base *ab = ab_pci->ab; return ret;
}
switch (mhi_state) { ret = mhi_sync_power_up(ab_pci->mhi_ctrl);
case ATH11K_MHI_INIT: if (ret) {
if (!test_bit(ATH11K_MHI_INIT, &ab_pci->mhi_state)) ath11k_warn(ab, "failed to power up mhi: %d", ret);
return 0; return ret;
break;
case ATH11K_MHI_DEINIT:
case ATH11K_MHI_POWER_ON:
if (test_bit(ATH11K_MHI_INIT, &ab_pci->mhi_state) &&
!test_bit(ATH11K_MHI_POWER_ON, &ab_pci->mhi_state))
return 0;
break;
case ATH11K_MHI_FORCE_POWER_OFF:
if (test_bit(ATH11K_MHI_POWER_ON, &ab_pci->mhi_state))
return 0;
break;
case ATH11K_MHI_POWER_OFF:
case ATH11K_MHI_SUSPEND:
if (test_bit(ATH11K_MHI_POWER_ON, &ab_pci->mhi_state) &&
!test_bit(ATH11K_MHI_SUSPEND, &ab_pci->mhi_state))
return 0;
break;
case ATH11K_MHI_RESUME:
if (test_bit(ATH11K_MHI_SUSPEND, &ab_pci->mhi_state))
return 0;
break;
case ATH11K_MHI_TRIGGER_RDDM:
if (test_bit(ATH11K_MHI_POWER_ON, &ab_pci->mhi_state) &&
!test_bit(ATH11K_MHI_TRIGGER_RDDM, &ab_pci->mhi_state))
return 0;
break;
case ATH11K_MHI_RDDM_DONE:
return 0;
default:
ath11k_err(ab, "unhandled mhi state: %s(%d)\n",
ath11k_mhi_state_to_str(mhi_state), mhi_state);
} }
ath11k_err(ab, "failed to set mhi state %s(%d) in current mhi state (0x%lx)\n", return 0;
ath11k_mhi_state_to_str(mhi_state), mhi_state, }
ab_pci->mhi_state);
return -EINVAL; void ath11k_mhi_stop(struct ath11k_pci *ab_pci)
{
mhi_power_down(ab_pci->mhi_ctrl, true);
mhi_unprepare_after_power_down(ab_pci->mhi_ctrl);
} }
static int ath11k_mhi_set_state(struct ath11k_pci *ab_pci, int ath11k_mhi_suspend(struct ath11k_pci *ab_pci)
enum ath11k_mhi_state mhi_state)
{ {
struct ath11k_base *ab = ab_pci->ab; struct ath11k_base *ab = ab_pci->ab;
int ret; int ret;
ret = ath11k_mhi_check_state_bit(ab_pci, mhi_state); ret = mhi_pm_suspend(ab_pci->mhi_ctrl);
if (ret) if (ret) {
goto out; ath11k_warn(ab, "failed to suspend mhi: %d", ret);
return ret;
ath11k_dbg(ab, ATH11K_DBG_PCI, "setting mhi state: %s(%d)\n",
ath11k_mhi_state_to_str(mhi_state), mhi_state);
switch (mhi_state) {
case ATH11K_MHI_INIT:
ret = mhi_prepare_for_power_up(ab_pci->mhi_ctrl);
break;
case ATH11K_MHI_DEINIT:
mhi_unprepare_after_power_down(ab_pci->mhi_ctrl);
ret = 0;
break;
case ATH11K_MHI_POWER_ON:
ret = mhi_sync_power_up(ab_pci->mhi_ctrl);
break;
case ATH11K_MHI_POWER_OFF:
mhi_power_down(ab_pci->mhi_ctrl, true);
ret = 0;
break;
case ATH11K_MHI_FORCE_POWER_OFF:
mhi_power_down(ab_pci->mhi_ctrl, false);
ret = 0;
break;
case ATH11K_MHI_SUSPEND:
ret = mhi_pm_suspend(ab_pci->mhi_ctrl);
break;
case ATH11K_MHI_RESUME:
/* Do force MHI resume as some devices like QCA6390, WCN6855
* are not in M3 state but they are functional. So just ignore
* the MHI state while resuming.
*/
ret = mhi_pm_resume_force(ab_pci->mhi_ctrl);
break;
case ATH11K_MHI_TRIGGER_RDDM:
ret = mhi_force_rddm_mode(ab_pci->mhi_ctrl);
break;
case ATH11K_MHI_RDDM_DONE:
break;
default:
ath11k_err(ab, "unhandled MHI state (%d)\n", mhi_state);
ret = -EINVAL;
} }
if (ret)
goto out;
ath11k_mhi_set_state_bit(ab_pci, mhi_state);
return 0; return 0;
out:
ath11k_err(ab, "failed to set mhi state: %s(%d)\n",
ath11k_mhi_state_to_str(mhi_state), mhi_state);
return ret;
} }
int ath11k_mhi_start(struct ath11k_pci *ab_pci) int ath11k_mhi_resume(struct ath11k_pci *ab_pci)
{ {
struct ath11k_base *ab = ab_pci->ab;
int ret; int ret;
ab_pci->mhi_ctrl->timeout_ms = MHI_TIMEOUT_DEFAULT_MS; /* Do force MHI resume as some devices like QCA6390, WCN6855
* are not in M3 state but they are functional. So just ignore
ret = ath11k_mhi_set_state(ab_pci, ATH11K_MHI_INIT); * the MHI state while resuming.
if (ret) */
goto out; ret = mhi_pm_resume_force(ab_pci->mhi_ctrl);
if (ret) {
ret = ath11k_mhi_set_state(ab_pci, ATH11K_MHI_POWER_ON); ath11k_warn(ab, "failed to resume mhi: %d", ret);
if (ret) return ret;
goto out; }
return 0; return 0;
out:
return ret;
}
void ath11k_mhi_stop(struct ath11k_pci *ab_pci)
{
ath11k_mhi_set_state(ab_pci, ATH11K_MHI_POWER_OFF);
ath11k_mhi_set_state(ab_pci, ATH11K_MHI_DEINIT);
}
void ath11k_mhi_suspend(struct ath11k_pci *ab_pci)
{
ath11k_mhi_set_state(ab_pci, ATH11K_MHI_SUSPEND);
}
void ath11k_mhi_resume(struct ath11k_pci *ab_pci)
{
ath11k_mhi_set_state(ab_pci, ATH11K_MHI_RESUME);
} }
...@@ -16,19 +16,6 @@ ...@@ -16,19 +16,6 @@
#define MHICTRL 0x38 #define MHICTRL 0x38
#define MHICTRL_RESET_MASK 0x2 #define MHICTRL_RESET_MASK 0x2
enum ath11k_mhi_state {
ATH11K_MHI_INIT,
ATH11K_MHI_DEINIT,
ATH11K_MHI_POWER_ON,
ATH11K_MHI_POWER_OFF,
ATH11K_MHI_FORCE_POWER_OFF,
ATH11K_MHI_SUSPEND,
ATH11K_MHI_RESUME,
ATH11K_MHI_TRIGGER_RDDM,
ATH11K_MHI_RDDM,
ATH11K_MHI_RDDM_DONE,
};
int ath11k_mhi_start(struct ath11k_pci *ar_pci); int ath11k_mhi_start(struct ath11k_pci *ar_pci);
void ath11k_mhi_stop(struct ath11k_pci *ar_pci); void ath11k_mhi_stop(struct ath11k_pci *ar_pci);
int ath11k_mhi_register(struct ath11k_pci *ar_pci); int ath11k_mhi_register(struct ath11k_pci *ar_pci);
...@@ -36,7 +23,7 @@ void ath11k_mhi_unregister(struct ath11k_pci *ar_pci); ...@@ -36,7 +23,7 @@ void ath11k_mhi_unregister(struct ath11k_pci *ar_pci);
void ath11k_mhi_set_mhictrl_reset(struct ath11k_base *ab); void ath11k_mhi_set_mhictrl_reset(struct ath11k_base *ab);
void ath11k_mhi_clear_vector(struct ath11k_base *ab); void ath11k_mhi_clear_vector(struct ath11k_base *ab);
void ath11k_mhi_suspend(struct ath11k_pci *ar_pci); int ath11k_mhi_suspend(struct ath11k_pci *ar_pci);
void ath11k_mhi_resume(struct ath11k_pci *ar_pci); int ath11k_mhi_resume(struct ath11k_pci *ar_pci);
#endif #endif
/* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* /*
* Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved.
*/ */
#ifndef _ATH11K_PCI_H #ifndef _ATH11K_PCI_H
#define _ATH11K_PCI_H #define _ATH11K_PCI_H
...@@ -52,23 +53,8 @@ ...@@ -52,23 +53,8 @@
#define WLAON_QFPROM_PWR_CTRL_REG 0x01f8031c #define WLAON_QFPROM_PWR_CTRL_REG 0x01f8031c
#define QFPROM_PWR_CTRL_VDD4BLOW_MASK 0x4 #define QFPROM_PWR_CTRL_VDD4BLOW_MASK 0x4
struct ath11k_msi_user {
char *name;
int num_vectors;
u32 base_vector;
};
struct ath11k_msi_config {
int total_vectors;
int total_users;
struct ath11k_msi_user *users;
};
enum ath11k_pci_flags { enum ath11k_pci_flags {
ATH11K_PCI_FLAG_INIT_DONE,
ATH11K_PCI_FLAG_IS_MSI_64,
ATH11K_PCI_ASPM_RESTORE, ATH11K_PCI_ASPM_RESTORE,
ATH11K_PCI_FLAG_MULTI_MSI_VECTORS,
}; };
struct ath11k_pci { struct ath11k_pci {
...@@ -76,10 +62,8 @@ struct ath11k_pci { ...@@ -76,10 +62,8 @@ struct ath11k_pci {
struct ath11k_base *ab; struct ath11k_base *ab;
u16 dev_id; u16 dev_id;
char amss_path[100]; char amss_path[100];
u32 msi_ep_base_data;
struct mhi_controller *mhi_ctrl; struct mhi_controller *mhi_ctrl;
const struct ath11k_msi_config *msi_config; const struct ath11k_msi_config *msi_config;
unsigned long mhi_state;
u32 register_window; u32 register_window;
/* protects register_window above */ /* protects register_window above */
...@@ -88,8 +72,6 @@ struct ath11k_pci { ...@@ -88,8 +72,6 @@ struct ath11k_pci {
/* enum ath11k_pci_flags */ /* enum ath11k_pci_flags */
unsigned long flags; unsigned long flags;
u16 link_ctl; u16 link_ctl;
unsigned long irq_flags;
}; };
static inline struct ath11k_pci *ath11k_pci_priv(struct ath11k_base *ab) static inline struct ath11k_pci *ath11k_pci_priv(struct ath11k_base *ab)
...@@ -97,11 +79,5 @@ static inline struct ath11k_pci *ath11k_pci_priv(struct ath11k_base *ab) ...@@ -97,11 +79,5 @@ static inline struct ath11k_pci *ath11k_pci_priv(struct ath11k_base *ab)
return (struct ath11k_pci *)ab->drv_priv; return (struct ath11k_pci *)ab->drv_priv;
} }
int ath11k_pci_get_user_msi_assignment(struct ath11k_pci *ar_pci, char *user_name, int ath11k_pci_get_msi_irq(struct ath11k_base *ab, unsigned int vector);
int *num_vectors, u32 *user_base_data,
u32 *base_vector);
int ath11k_pci_get_msi_irq(struct device *dev, unsigned int vector);
void ath11k_pci_write32(struct ath11k_base *ab, u32 offset, u32 value);
u32 ath11k_pci_read32(struct ath11k_base *ab, u32 offset);
#endif #endif
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2019-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include "core.h"
#include "pcic.h"
#include "debug.h"
static const char *irq_name[ATH11K_IRQ_NUM_MAX] = {
"bhi",
"mhi-er0",
"mhi-er1",
"ce0",
"ce1",
"ce2",
"ce3",
"ce4",
"ce5",
"ce6",
"ce7",
"ce8",
"ce9",
"ce10",
"ce11",
"host2wbm-desc-feed",
"host2reo-re-injection",
"host2reo-command",
"host2rxdma-monitor-ring3",
"host2rxdma-monitor-ring2",
"host2rxdma-monitor-ring1",
"reo2ost-exception",
"wbm2host-rx-release",
"reo2host-status",
"reo2host-destination-ring4",
"reo2host-destination-ring3",
"reo2host-destination-ring2",
"reo2host-destination-ring1",
"rxdma2host-monitor-destination-mac3",
"rxdma2host-monitor-destination-mac2",
"rxdma2host-monitor-destination-mac1",
"ppdu-end-interrupts-mac3",
"ppdu-end-interrupts-mac2",
"ppdu-end-interrupts-mac1",
"rxdma2host-monitor-status-ring-mac3",
"rxdma2host-monitor-status-ring-mac2",
"rxdma2host-monitor-status-ring-mac1",
"host2rxdma-host-buf-ring-mac3",
"host2rxdma-host-buf-ring-mac2",
"host2rxdma-host-buf-ring-mac1",
"rxdma2host-destination-ring-mac3",
"rxdma2host-destination-ring-mac2",
"rxdma2host-destination-ring-mac1",
"host2tcl-input-ring4",
"host2tcl-input-ring3",
"host2tcl-input-ring2",
"host2tcl-input-ring1",
"wbm2host-tx-completions-ring3",
"wbm2host-tx-completions-ring2",
"wbm2host-tx-completions-ring1",
"tcl2host-status-ring",
};
static const struct ath11k_msi_config ath11k_msi_config[] = {
{
.total_vectors = 32,
.total_users = 4,
.users = (struct ath11k_msi_user[]) {
{ .name = "MHI", .num_vectors = 3, .base_vector = 0 },
{ .name = "CE", .num_vectors = 10, .base_vector = 3 },
{ .name = "WAKE", .num_vectors = 1, .base_vector = 13 },
{ .name = "DP", .num_vectors = 18, .base_vector = 14 },
},
.hw_rev = ATH11K_HW_QCA6390_HW20,
},
{
.total_vectors = 16,
.total_users = 3,
.users = (struct ath11k_msi_user[]) {
{ .name = "MHI", .num_vectors = 3, .base_vector = 0 },
{ .name = "CE", .num_vectors = 5, .base_vector = 3 },
{ .name = "DP", .num_vectors = 8, .base_vector = 8 },
},
.hw_rev = ATH11K_HW_QCN9074_HW10,
},
{
.total_vectors = 32,
.total_users = 4,
.users = (struct ath11k_msi_user[]) {
{ .name = "MHI", .num_vectors = 3, .base_vector = 0 },
{ .name = "CE", .num_vectors = 10, .base_vector = 3 },
{ .name = "WAKE", .num_vectors = 1, .base_vector = 13 },
{ .name = "DP", .num_vectors = 18, .base_vector = 14 },
},
.hw_rev = ATH11K_HW_WCN6855_HW20,
},
{
.total_vectors = 32,
.total_users = 4,
.users = (struct ath11k_msi_user[]) {
{ .name = "MHI", .num_vectors = 3, .base_vector = 0 },
{ .name = "CE", .num_vectors = 10, .base_vector = 3 },
{ .name = "WAKE", .num_vectors = 1, .base_vector = 13 },
{ .name = "DP", .num_vectors = 18, .base_vector = 14 },
},
.hw_rev = ATH11K_HW_WCN6855_HW21,
},
};
int ath11k_pcic_init_msi_config(struct ath11k_base *ab)
{
const struct ath11k_msi_config *msi_config;
int i;
for (i = 0; i < ARRAY_SIZE(ath11k_msi_config); i++) {
msi_config = &ath11k_msi_config[i];
if (msi_config->hw_rev == ab->hw_rev)
break;
}
if (i == ARRAY_SIZE(ath11k_msi_config)) {
ath11k_err(ab, "failed to fetch msi config, unsupported hw version: 0x%x\n",
ab->hw_rev);
return -EINVAL;
}
ab->pci.msi.config = msi_config;
return 0;
}
EXPORT_SYMBOL(ath11k_pcic_init_msi_config);
static inline u32 ath11k_pcic_get_window_start(struct ath11k_base *ab,
u32 offset)
{
u32 window_start;
/* If offset lies within DP register range, use 3rd window */
if ((offset ^ HAL_SEQ_WCSS_UMAC_OFFSET) < ATH11K_PCI_WINDOW_RANGE_MASK)
window_start = 3 * ATH11K_PCI_WINDOW_START;
/* If offset lies within CE register range, use 2nd window */
else if ((offset ^ HAL_CE_WFSS_CE_REG_BASE) < ATH11K_PCI_WINDOW_RANGE_MASK)
window_start = 2 * ATH11K_PCI_WINDOW_START;
else
window_start = ATH11K_PCI_WINDOW_START;
return window_start;
}
void ath11k_pcic_write32(struct ath11k_base *ab, u32 offset, u32 value)
{
u32 window_start;
int ret = 0;
/* for offset beyond BAR + 4K - 32, may
* need to wakeup the device to access.
*/
if (test_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags) &&
offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF && ab->pci.ops->wakeup)
ret = ab->pci.ops->wakeup(ab);
if (offset < ATH11K_PCI_WINDOW_START) {
iowrite32(value, ab->mem + offset);
} else {
if (ab->bus_params.static_window_map)
window_start = ath11k_pcic_get_window_start(ab, offset);
else
window_start = ATH11K_PCI_WINDOW_START;
if (window_start == ATH11K_PCI_WINDOW_START &&
ab->pci.ops->window_write32) {
ab->pci.ops->window_write32(ab, offset, value);
} else {
iowrite32(value, ab->mem + window_start +
(offset & ATH11K_PCI_WINDOW_RANGE_MASK));
}
}
if (test_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags) &&
offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF && ab->pci.ops->release &&
!ret)
ab->pci.ops->release(ab);
}
u32 ath11k_pcic_read32(struct ath11k_base *ab, u32 offset)
{
u32 val, window_start;
int ret = 0;
/* for offset beyond BAR + 4K - 32, may
* need to wakeup the device to access.
*/
if (test_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags) &&
offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF && ab->pci.ops->wakeup)
ret = ab->pci.ops->wakeup(ab);
if (offset < ATH11K_PCI_WINDOW_START) {
val = ioread32(ab->mem + offset);
} else {
if (ab->bus_params.static_window_map)
window_start = ath11k_pcic_get_window_start(ab, offset);
else
window_start = ATH11K_PCI_WINDOW_START;
if (window_start == ATH11K_PCI_WINDOW_START &&
ab->pci.ops->window_read32) {
val = ab->pci.ops->window_read32(ab, offset);
} else {
val = ioread32(ab->mem + window_start +
(offset & ATH11K_PCI_WINDOW_RANGE_MASK));
}
}
if (test_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags) &&
offset >= ATH11K_PCI_ACCESS_ALWAYS_OFF && ab->pci.ops->release &&
!ret)
ab->pci.ops->release(ab);
return val;
}
void ath11k_pcic_get_msi_address(struct ath11k_base *ab, u32 *msi_addr_lo,
u32 *msi_addr_hi)
{
*msi_addr_lo = ab->pci.msi.addr_lo;
*msi_addr_hi = ab->pci.msi.addr_hi;
}
int ath11k_pcic_get_user_msi_assignment(struct ath11k_base *ab, char *user_name,
int *num_vectors, u32 *user_base_data,
u32 *base_vector)
{
const struct ath11k_msi_config *msi_config = ab->pci.msi.config;
int idx;
for (idx = 0; idx < msi_config->total_users; idx++) {
if (strcmp(user_name, msi_config->users[idx].name) == 0) {
*num_vectors = msi_config->users[idx].num_vectors;
*base_vector = msi_config->users[idx].base_vector;
*user_base_data = *base_vector + ab->pci.msi.ep_base_data;
ath11k_dbg(ab, ATH11K_DBG_PCI,
"Assign MSI to user: %s, num_vectors: %d, user_base_data: %u, base_vector: %u\n",
user_name, *num_vectors, *user_base_data,
*base_vector);
return 0;
}
}
ath11k_err(ab, "Failed to find MSI assignment for %s!\n", user_name);
return -EINVAL;
}
void ath11k_pcic_get_ce_msi_idx(struct ath11k_base *ab, u32 ce_id, u32 *msi_idx)
{
u32 i, msi_data_idx;
for (i = 0, msi_data_idx = 0; i < ab->hw_params.ce_count; i++) {
if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
continue;
if (ce_id == i)
break;
msi_data_idx++;
}
*msi_idx = msi_data_idx;
}
static void ath11k_pcic_free_ext_irq(struct ath11k_base *ab)
{
int i, j;
for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];
for (j = 0; j < irq_grp->num_irq; j++)
free_irq(ab->irq_num[irq_grp->irqs[j]], irq_grp);
netif_napi_del(&irq_grp->napi);
}
}
void ath11k_pcic_free_irq(struct ath11k_base *ab)
{
int i, irq_idx;
for (i = 0; i < ab->hw_params.ce_count; i++) {
if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
continue;
irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i;
free_irq(ab->irq_num[irq_idx], &ab->ce.ce_pipe[i]);
}
ath11k_pcic_free_ext_irq(ab);
}
static void ath11k_pcic_ce_irq_enable(struct ath11k_base *ab, u16 ce_id)
{
u32 irq_idx;
/* In case of one MSI vector, we handle irq enable/disable in a
* uniform way since we only have one irq
*/
if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))
return;
irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_id;
enable_irq(ab->irq_num[irq_idx]);
}
static void ath11k_pcic_ce_irq_disable(struct ath11k_base *ab, u16 ce_id)
{
u32 irq_idx;
/* In case of one MSI vector, we handle irq enable/disable in a
* uniform way since we only have one irq
*/
if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))
return;
irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_id;
disable_irq_nosync(ab->irq_num[irq_idx]);
}
static void ath11k_pcic_ce_irqs_disable(struct ath11k_base *ab)
{
int i;
clear_bit(ATH11K_FLAG_CE_IRQ_ENABLED, &ab->dev_flags);
for (i = 0; i < ab->hw_params.ce_count; i++) {
if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
continue;
ath11k_pcic_ce_irq_disable(ab, i);
}
}
static void ath11k_pcic_sync_ce_irqs(struct ath11k_base *ab)
{
int i;
int irq_idx;
for (i = 0; i < ab->hw_params.ce_count; i++) {
if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
continue;
irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i;
synchronize_irq(ab->irq_num[irq_idx]);
}
}
static void ath11k_pcic_ce_tasklet(struct tasklet_struct *t)
{
struct ath11k_ce_pipe *ce_pipe = from_tasklet(ce_pipe, t, intr_tq);
int irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_pipe->pipe_num;
ath11k_ce_per_engine_service(ce_pipe->ab, ce_pipe->pipe_num);
enable_irq(ce_pipe->ab->irq_num[irq_idx]);
}
static irqreturn_t ath11k_pcic_ce_interrupt_handler(int irq, void *arg)
{
struct ath11k_ce_pipe *ce_pipe = arg;
struct ath11k_base *ab = ce_pipe->ab;
int irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_pipe->pipe_num;
if (!test_bit(ATH11K_FLAG_CE_IRQ_ENABLED, &ab->dev_flags))
return IRQ_HANDLED;
/* last interrupt received for this CE */
ce_pipe->timestamp = jiffies;
disable_irq_nosync(ab->irq_num[irq_idx]);
tasklet_schedule(&ce_pipe->intr_tq);
return IRQ_HANDLED;
}
static void ath11k_pcic_ext_grp_disable(struct ath11k_ext_irq_grp *irq_grp)
{
struct ath11k_base *ab = irq_grp->ab;
int i;
/* In case of one MSI vector, we handle irq enable/disable
* in a uniform way since we only have one irq
*/
if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))
return;
for (i = 0; i < irq_grp->num_irq; i++)
disable_irq_nosync(irq_grp->ab->irq_num[irq_grp->irqs[i]]);
}
static void __ath11k_pcic_ext_irq_disable(struct ath11k_base *sc)
{
int i;
clear_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &sc->dev_flags);
for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
struct ath11k_ext_irq_grp *irq_grp = &sc->ext_irq_grp[i];
ath11k_pcic_ext_grp_disable(irq_grp);
if (irq_grp->napi_enabled) {
napi_synchronize(&irq_grp->napi);
napi_disable(&irq_grp->napi);
irq_grp->napi_enabled = false;
}
}
}
static void ath11k_pcic_ext_grp_enable(struct ath11k_ext_irq_grp *irq_grp)
{
struct ath11k_base *ab = irq_grp->ab;
int i;
/* In case of one MSI vector, we handle irq enable/disable in a
* uniform way since we only have one irq
*/
if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))
return;
for (i = 0; i < irq_grp->num_irq; i++)
enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]);
}
void ath11k_pcic_ext_irq_enable(struct ath11k_base *ab)
{
int i;
set_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags);
for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];
if (!irq_grp->napi_enabled) {
napi_enable(&irq_grp->napi);
irq_grp->napi_enabled = true;
}
ath11k_pcic_ext_grp_enable(irq_grp);
}
}
static void ath11k_pcic_sync_ext_irqs(struct ath11k_base *ab)
{
int i, j, irq_idx;
for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];
for (j = 0; j < irq_grp->num_irq; j++) {
irq_idx = irq_grp->irqs[j];
synchronize_irq(ab->irq_num[irq_idx]);
}
}
}
void ath11k_pcic_ext_irq_disable(struct ath11k_base *ab)
{
__ath11k_pcic_ext_irq_disable(ab);
ath11k_pcic_sync_ext_irqs(ab);
}
static int ath11k_pcic_ext_grp_napi_poll(struct napi_struct *napi, int budget)
{
struct ath11k_ext_irq_grp *irq_grp = container_of(napi,
struct ath11k_ext_irq_grp,
napi);
struct ath11k_base *ab = irq_grp->ab;
int work_done;
int i;
work_done = ath11k_dp_service_srng(ab, irq_grp, budget);
if (work_done < budget) {
napi_complete_done(napi, work_done);
for (i = 0; i < irq_grp->num_irq; i++)
enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]);
}
if (work_done > budget)
work_done = budget;
return work_done;
}
static irqreturn_t ath11k_pcic_ext_interrupt_handler(int irq, void *arg)
{
struct ath11k_ext_irq_grp *irq_grp = arg;
struct ath11k_base *ab = irq_grp->ab;
int i;
if (!test_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags))
return IRQ_HANDLED;
ath11k_dbg(irq_grp->ab, ATH11K_DBG_PCI, "ext irq:%d\n", irq);
/* last interrupt received for this group */
irq_grp->timestamp = jiffies;
for (i = 0; i < irq_grp->num_irq; i++)
disable_irq_nosync(irq_grp->ab->irq_num[irq_grp->irqs[i]]);
napi_schedule(&irq_grp->napi);
return IRQ_HANDLED;
}
static int
ath11k_pcic_get_msi_irq(struct ath11k_base *ab, unsigned int vector)
{
if (!ab->pci.ops->get_msi_irq) {
WARN_ONCE(1, "get_msi_irq pci op not defined");
return -EOPNOTSUPP;
}
return ab->pci.ops->get_msi_irq(ab, vector);
}
static int ath11k_pcic_ext_irq_config(struct ath11k_base *ab)
{
int i, j, ret, num_vectors = 0;
u32 user_base_data = 0, base_vector = 0;
unsigned long irq_flags;
ret = ath11k_pcic_get_user_msi_assignment(ab, "DP", &num_vectors,
&user_base_data,
&base_vector);
if (ret < 0)
return ret;
irq_flags = IRQF_SHARED;
if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))
irq_flags |= IRQF_NOBALANCING;
for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) {
struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i];
u32 num_irq = 0;
irq_grp->ab = ab;
irq_grp->grp_id = i;
init_dummy_netdev(&irq_grp->napi_ndev);
netif_napi_add(&irq_grp->napi_ndev, &irq_grp->napi,
ath11k_pcic_ext_grp_napi_poll, NAPI_POLL_WEIGHT);
if (ab->hw_params.ring_mask->tx[i] ||
ab->hw_params.ring_mask->rx[i] ||
ab->hw_params.ring_mask->rx_err[i] ||
ab->hw_params.ring_mask->rx_wbm_rel[i] ||
ab->hw_params.ring_mask->reo_status[i] ||
ab->hw_params.ring_mask->rxdma2host[i] ||
ab->hw_params.ring_mask->host2rxdma[i] ||
ab->hw_params.ring_mask->rx_mon_status[i]) {
num_irq = 1;
}
irq_grp->num_irq = num_irq;
irq_grp->irqs[0] = ATH11K_PCI_IRQ_DP_OFFSET + i;
for (j = 0; j < irq_grp->num_irq; j++) {
int irq_idx = irq_grp->irqs[j];
int vector = (i % num_vectors) + base_vector;
int irq = ath11k_pcic_get_msi_irq(ab, vector);
if (irq < 0)
return irq;
ab->irq_num[irq_idx] = irq;
ath11k_dbg(ab, ATH11K_DBG_PCI,
"irq:%d group:%d\n", irq, i);
irq_set_status_flags(irq, IRQ_DISABLE_UNLAZY);
ret = request_irq(irq, ath11k_pcic_ext_interrupt_handler,
irq_flags, "DP_EXT_IRQ", irq_grp);
if (ret) {
ath11k_err(ab, "failed request irq %d: %d\n",
vector, ret);
return ret;
}
}
ath11k_pcic_ext_grp_disable(irq_grp);
}
return 0;
}
int ath11k_pcic_config_irq(struct ath11k_base *ab)
{
struct ath11k_ce_pipe *ce_pipe;
u32 msi_data_start;
u32 msi_data_count, msi_data_idx;
u32 msi_irq_start;
unsigned int msi_data;
int irq, i, ret, irq_idx;
unsigned long irq_flags;
ret = ath11k_pcic_get_user_msi_assignment(ab, "CE", &msi_data_count,
&msi_data_start, &msi_irq_start);
if (ret)
return ret;
irq_flags = IRQF_SHARED;
if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags))
irq_flags |= IRQF_NOBALANCING;
/* Configure CE irqs */
for (i = 0, msi_data_idx = 0; i < ab->hw_params.ce_count; i++) {
if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
continue;
msi_data = (msi_data_idx % msi_data_count) + msi_irq_start;
irq = ath11k_pcic_get_msi_irq(ab, msi_data);
if (irq < 0)
return irq;
ce_pipe = &ab->ce.ce_pipe[i];
irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i;
tasklet_setup(&ce_pipe->intr_tq, ath11k_pcic_ce_tasklet);
ret = request_irq(irq, ath11k_pcic_ce_interrupt_handler,
irq_flags, irq_name[irq_idx], ce_pipe);
if (ret) {
ath11k_err(ab, "failed to request irq %d: %d\n",
irq_idx, ret);
return ret;
}
ab->irq_num[irq_idx] = irq;
msi_data_idx++;
ath11k_pcic_ce_irq_disable(ab, i);
}
ret = ath11k_pcic_ext_irq_config(ab);
if (ret)
return ret;
return 0;
}
void ath11k_pcic_ce_irqs_enable(struct ath11k_base *ab)
{
int i;
set_bit(ATH11K_FLAG_CE_IRQ_ENABLED, &ab->dev_flags);
for (i = 0; i < ab->hw_params.ce_count; i++) {
if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
continue;
ath11k_pcic_ce_irq_enable(ab, i);
}
}
static void ath11k_pcic_kill_tasklets(struct ath11k_base *ab)
{
int i;
for (i = 0; i < ab->hw_params.ce_count; i++) {
struct ath11k_ce_pipe *ce_pipe = &ab->ce.ce_pipe[i];
if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR)
continue;
tasklet_kill(&ce_pipe->intr_tq);
}
}
void ath11k_pcic_ce_irq_disable_sync(struct ath11k_base *ab)
{
ath11k_pcic_ce_irqs_disable(ab);
ath11k_pcic_sync_ce_irqs(ab);
ath11k_pcic_kill_tasklets(ab);
}
void ath11k_pcic_stop(struct ath11k_base *ab)
{
ath11k_pcic_ce_irq_disable_sync(ab);
ath11k_ce_cleanup_pipes(ab);
}
int ath11k_pcic_start(struct ath11k_base *ab)
{
set_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags);
ath11k_pcic_ce_irqs_enable(ab);
ath11k_ce_rx_post_buf(ab);
return 0;
}
int ath11k_pcic_map_service_to_pipe(struct ath11k_base *ab, u16 service_id,
u8 *ul_pipe, u8 *dl_pipe)
{
const struct service_to_pipe *entry;
bool ul_set = false, dl_set = false;
int i;
for (i = 0; i < ab->hw_params.svc_to_ce_map_len; i++) {
entry = &ab->hw_params.svc_to_ce_map[i];
if (__le32_to_cpu(entry->service_id) != service_id)
continue;
switch (__le32_to_cpu(entry->pipedir)) {
case PIPEDIR_NONE:
break;
case PIPEDIR_IN:
WARN_ON(dl_set);
*dl_pipe = __le32_to_cpu(entry->pipenum);
dl_set = true;
break;
case PIPEDIR_OUT:
WARN_ON(ul_set);
*ul_pipe = __le32_to_cpu(entry->pipenum);
ul_set = true;
break;
case PIPEDIR_INOUT:
WARN_ON(dl_set);
WARN_ON(ul_set);
*dl_pipe = __le32_to_cpu(entry->pipenum);
*ul_pipe = __le32_to_cpu(entry->pipenum);
dl_set = true;
ul_set = true;
break;
}
}
if (WARN_ON(!ul_set || !dl_set))
return -ENOENT;
return 0;
}
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2019-2021 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef _ATH11K_PCI_CMN_H
#define _ATH11K_PCI_CMN_H
#include "core.h"
#define ATH11K_PCI_IRQ_CE0_OFFSET 3
#define ATH11K_PCI_IRQ_DP_OFFSET 14
#define ATH11K_PCI_WINDOW_ENABLE_BIT 0x40000000
#define ATH11K_PCI_WINDOW_REG_ADDRESS 0x310c
#define ATH11K_PCI_WINDOW_VALUE_MASK GENMASK(24, 19)
#define ATH11K_PCI_WINDOW_START 0x80000
#define ATH11K_PCI_WINDOW_RANGE_MASK GENMASK(18, 0)
/* BAR0 + 4k is always accessible, and no
* need to force wakeup.
* 4K - 32 = 0xFE0
*/
#define ATH11K_PCI_ACCESS_ALWAYS_OFF 0xFE0
int ath11k_pcic_get_user_msi_assignment(struct ath11k_base *ab, char *user_name,
int *num_vectors, u32 *user_base_data,
u32 *base_vector);
void ath11k_pcic_write32(struct ath11k_base *ab, u32 offset, u32 value);
u32 ath11k_pcic_read32(struct ath11k_base *ab, u32 offset);
void ath11k_pcic_get_msi_address(struct ath11k_base *ab, u32 *msi_addr_lo,
u32 *msi_addr_hi);
void ath11k_pcic_get_ce_msi_idx(struct ath11k_base *ab, u32 ce_id, u32 *msi_idx);
void ath11k_pcic_free_irq(struct ath11k_base *ab);
int ath11k_pcic_config_irq(struct ath11k_base *ab);
void ath11k_pcic_ext_irq_enable(struct ath11k_base *ab);
void ath11k_pcic_ext_irq_disable(struct ath11k_base *ab);
void ath11k_pcic_stop(struct ath11k_base *ab);
int ath11k_pcic_start(struct ath11k_base *ab);
int ath11k_pcic_map_service_to_pipe(struct ath11k_base *ab, u16 service_id,
u8 *ul_pipe, u8 *dl_pipe);
void ath11k_pcic_ce_irqs_enable(struct ath11k_base *ab);
void ath11k_pcic_ce_irq_disable_sync(struct ath11k_base *ab);
int ath11k_pcic_init_msi_config(struct ath11k_base *ab);
#endif
// SPDX-License-Identifier: BSD-3-Clause-Clear // SPDX-License-Identifier: BSD-3-Clause-Clear
/* /*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/ */
#include "core.h" #include "core.h"
#include "peer.h" #include "peer.h"
#include "debug.h" #include "debug.h"
struct ath11k_peer *ath11k_peer_find(struct ath11k_base *ab, int vdev_id, static struct ath11k_peer *ath11k_peer_find_list_by_id(struct ath11k_base *ab,
const u8 *addr) int peer_id)
{ {
struct ath11k_peer *peer; struct ath11k_peer *peer;
lockdep_assert_held(&ab->base_lock); lockdep_assert_held(&ab->base_lock);
list_for_each_entry(peer, &ab->peers, list) { list_for_each_entry(peer, &ab->peers, list) {
if (peer->vdev_id != vdev_id) if (peer->peer_id != peer_id)
continue;
if (!ether_addr_equal(peer->addr, addr))
continue; continue;
return peer; return peer;
...@@ -26,15 +25,15 @@ struct ath11k_peer *ath11k_peer_find(struct ath11k_base *ab, int vdev_id, ...@@ -26,15 +25,15 @@ struct ath11k_peer *ath11k_peer_find(struct ath11k_base *ab, int vdev_id,
return NULL; return NULL;
} }
static struct ath11k_peer *ath11k_peer_find_by_pdev_idx(struct ath11k_base *ab, struct ath11k_peer *ath11k_peer_find(struct ath11k_base *ab, int vdev_id,
u8 pdev_idx, const u8 *addr) const u8 *addr)
{ {
struct ath11k_peer *peer; struct ath11k_peer *peer;
lockdep_assert_held(&ab->base_lock); lockdep_assert_held(&ab->base_lock);
list_for_each_entry(peer, &ab->peers, list) { list_for_each_entry(peer, &ab->peers, list) {
if (peer->pdev_idx != pdev_idx) if (peer->vdev_id != vdev_id)
continue; continue;
if (!ether_addr_equal(peer->addr, addr)) if (!ether_addr_equal(peer->addr, addr))
continue; continue;
...@@ -52,14 +51,13 @@ struct ath11k_peer *ath11k_peer_find_by_addr(struct ath11k_base *ab, ...@@ -52,14 +51,13 @@ struct ath11k_peer *ath11k_peer_find_by_addr(struct ath11k_base *ab,
lockdep_assert_held(&ab->base_lock); lockdep_assert_held(&ab->base_lock);
list_for_each_entry(peer, &ab->peers, list) { if (!ab->rhead_peer_addr)
if (!ether_addr_equal(peer->addr, addr)) return NULL;
continue;
return peer; peer = rhashtable_lookup_fast(ab->rhead_peer_addr, addr,
} ab->rhash_peer_addr_param);
return NULL; return peer;
} }
struct ath11k_peer *ath11k_peer_find_by_id(struct ath11k_base *ab, struct ath11k_peer *ath11k_peer_find_by_id(struct ath11k_base *ab,
...@@ -69,11 +67,13 @@ struct ath11k_peer *ath11k_peer_find_by_id(struct ath11k_base *ab, ...@@ -69,11 +67,13 @@ struct ath11k_peer *ath11k_peer_find_by_id(struct ath11k_base *ab,
lockdep_assert_held(&ab->base_lock); lockdep_assert_held(&ab->base_lock);
list_for_each_entry(peer, &ab->peers, list) if (!ab->rhead_peer_id)
if (peer_id == peer->peer_id) return NULL;
return peer;
return NULL; peer = rhashtable_lookup_fast(ab->rhead_peer_id, &peer_id,
ab->rhash_peer_id_param);
return peer;
} }
struct ath11k_peer *ath11k_peer_find_by_vdev_id(struct ath11k_base *ab, struct ath11k_peer *ath11k_peer_find_by_vdev_id(struct ath11k_base *ab,
...@@ -99,7 +99,7 @@ void ath11k_peer_unmap_event(struct ath11k_base *ab, u16 peer_id) ...@@ -99,7 +99,7 @@ void ath11k_peer_unmap_event(struct ath11k_base *ab, u16 peer_id)
spin_lock_bh(&ab->base_lock); spin_lock_bh(&ab->base_lock);
peer = ath11k_peer_find_by_id(ab, peer_id); peer = ath11k_peer_find_list_by_id(ab, peer_id);
if (!peer) { if (!peer) {
ath11k_warn(ab, "peer-unmap-event: unknown peer id %d\n", ath11k_warn(ab, "peer-unmap-event: unknown peer id %d\n",
peer_id); peer_id);
...@@ -167,6 +167,76 @@ static int ath11k_wait_for_peer_common(struct ath11k_base *ab, int vdev_id, ...@@ -167,6 +167,76 @@ static int ath11k_wait_for_peer_common(struct ath11k_base *ab, int vdev_id,
return 0; return 0;
} }
static inline int ath11k_peer_rhash_insert(struct ath11k_base *ab,
struct rhashtable *rtbl,
struct rhash_head *rhead,
struct rhashtable_params *params,
void *key)
{
struct ath11k_peer *tmp;
lockdep_assert_held(&ab->tbl_mtx_lock);
tmp = rhashtable_lookup_get_insert_fast(rtbl, rhead, *params);
if (!tmp)
return 0;
else if (IS_ERR(tmp))
return PTR_ERR(tmp);
else
return -EEXIST;
}
static inline int ath11k_peer_rhash_remove(struct ath11k_base *ab,
struct rhashtable *rtbl,
struct rhash_head *rhead,
struct rhashtable_params *params)
{
int ret;
lockdep_assert_held(&ab->tbl_mtx_lock);
ret = rhashtable_remove_fast(rtbl, rhead, *params);
if (ret && ret != -ENOENT)
return ret;
return 0;
}
static int ath11k_peer_rhash_add(struct ath11k_base *ab, struct ath11k_peer *peer)
{
int ret;
lockdep_assert_held(&ab->base_lock);
lockdep_assert_held(&ab->tbl_mtx_lock);
if (!ab->rhead_peer_id || !ab->rhead_peer_addr)
return -EPERM;
ret = ath11k_peer_rhash_insert(ab, ab->rhead_peer_id, &peer->rhash_id,
&ab->rhash_peer_id_param, &peer->peer_id);
if (ret) {
ath11k_warn(ab, "failed to add peer %pM with id %d in rhash_id ret %d\n",
peer->addr, peer->peer_id, ret);
return ret;
}
ret = ath11k_peer_rhash_insert(ab, ab->rhead_peer_addr, &peer->rhash_addr,
&ab->rhash_peer_addr_param, &peer->addr);
if (ret) {
ath11k_warn(ab, "failed to add peer %pM with id %d in rhash_addr ret %d\n",
peer->addr, peer->peer_id, ret);
goto err_clean;
}
return 0;
err_clean:
ath11k_peer_rhash_remove(ab, ab->rhead_peer_id, &peer->rhash_id,
&ab->rhash_peer_id_param);
return ret;
}
void ath11k_peer_cleanup(struct ath11k *ar, u32 vdev_id) void ath11k_peer_cleanup(struct ath11k *ar, u32 vdev_id)
{ {
struct ath11k_peer *peer, *tmp; struct ath11k_peer *peer, *tmp;
...@@ -174,6 +244,7 @@ void ath11k_peer_cleanup(struct ath11k *ar, u32 vdev_id) ...@@ -174,6 +244,7 @@ void ath11k_peer_cleanup(struct ath11k *ar, u32 vdev_id)
lockdep_assert_held(&ar->conf_mutex); lockdep_assert_held(&ar->conf_mutex);
mutex_lock(&ab->tbl_mtx_lock);
spin_lock_bh(&ab->base_lock); spin_lock_bh(&ab->base_lock);
list_for_each_entry_safe(peer, tmp, &ab->peers, list) { list_for_each_entry_safe(peer, tmp, &ab->peers, list) {
if (peer->vdev_id != vdev_id) if (peer->vdev_id != vdev_id)
...@@ -182,12 +253,14 @@ void ath11k_peer_cleanup(struct ath11k *ar, u32 vdev_id) ...@@ -182,12 +253,14 @@ void ath11k_peer_cleanup(struct ath11k *ar, u32 vdev_id)
ath11k_warn(ab, "removing stale peer %pM from vdev_id %d\n", ath11k_warn(ab, "removing stale peer %pM from vdev_id %d\n",
peer->addr, vdev_id); peer->addr, vdev_id);
ath11k_peer_rhash_delete(ab, peer);
list_del(&peer->list); list_del(&peer->list);
kfree(peer); kfree(peer);
ar->num_peers--; ar->num_peers--;
} }
spin_unlock_bh(&ab->base_lock); spin_unlock_bh(&ab->base_lock);
mutex_unlock(&ab->tbl_mtx_lock);
} }
static int ath11k_wait_for_peer_deleted(struct ath11k *ar, int vdev_id, const u8 *addr) static int ath11k_wait_for_peer_deleted(struct ath11k *ar, int vdev_id, const u8 *addr)
...@@ -217,17 +290,38 @@ int ath11k_wait_for_peer_delete_done(struct ath11k *ar, u32 vdev_id, ...@@ -217,17 +290,38 @@ int ath11k_wait_for_peer_delete_done(struct ath11k *ar, u32 vdev_id,
return 0; return 0;
} }
int ath11k_peer_delete(struct ath11k *ar, u32 vdev_id, u8 *addr) static int __ath11k_peer_delete(struct ath11k *ar, u32 vdev_id, const u8 *addr)
{ {
int ret; int ret;
struct ath11k_peer *peer;
struct ath11k_base *ab = ar->ab;
lockdep_assert_held(&ar->conf_mutex); lockdep_assert_held(&ar->conf_mutex);
mutex_lock(&ab->tbl_mtx_lock);
spin_lock_bh(&ab->base_lock);
peer = ath11k_peer_find_by_addr(ab, addr);
if (!peer) {
spin_unlock_bh(&ab->base_lock);
mutex_unlock(&ab->tbl_mtx_lock);
ath11k_warn(ab,
"failed to find peer vdev_id %d addr %pM in delete\n",
vdev_id, addr);
return -EINVAL;
}
ath11k_peer_rhash_delete(ab, peer);
spin_unlock_bh(&ab->base_lock);
mutex_unlock(&ab->tbl_mtx_lock);
reinit_completion(&ar->peer_delete_done); reinit_completion(&ar->peer_delete_done);
ret = ath11k_wmi_send_peer_delete_cmd(ar, addr, vdev_id); ret = ath11k_wmi_send_peer_delete_cmd(ar, addr, vdev_id);
if (ret) { if (ret) {
ath11k_warn(ar->ab, ath11k_warn(ab,
"failed to delete peer vdev_id %d addr %pM ret %d\n", "failed to delete peer vdev_id %d addr %pM ret %d\n",
vdev_id, addr, ret); vdev_id, addr, ret);
return ret; return ret;
...@@ -237,6 +331,19 @@ int ath11k_peer_delete(struct ath11k *ar, u32 vdev_id, u8 *addr) ...@@ -237,6 +331,19 @@ int ath11k_peer_delete(struct ath11k *ar, u32 vdev_id, u8 *addr)
if (ret) if (ret)
return ret; return ret;
return 0;
}
int ath11k_peer_delete(struct ath11k *ar, u32 vdev_id, u8 *addr)
{
int ret;
lockdep_assert_held(&ar->conf_mutex);
ret = __ath11k_peer_delete(ar, vdev_id, addr);
if (ret)
return ret;
ar->num_peers--; ar->num_peers--;
return 0; return 0;
...@@ -263,7 +370,7 @@ int ath11k_peer_create(struct ath11k *ar, struct ath11k_vif *arvif, ...@@ -263,7 +370,7 @@ int ath11k_peer_create(struct ath11k *ar, struct ath11k_vif *arvif,
} }
spin_lock_bh(&ar->ab->base_lock); spin_lock_bh(&ar->ab->base_lock);
peer = ath11k_peer_find_by_pdev_idx(ar->ab, ar->pdev_idx, param->peer_addr); peer = ath11k_peer_find_by_addr(ar->ab, param->peer_addr);
if (peer) { if (peer) {
spin_unlock_bh(&ar->ab->base_lock); spin_unlock_bh(&ar->ab->base_lock);
return -EINVAL; return -EINVAL;
...@@ -283,11 +390,13 @@ int ath11k_peer_create(struct ath11k *ar, struct ath11k_vif *arvif, ...@@ -283,11 +390,13 @@ int ath11k_peer_create(struct ath11k *ar, struct ath11k_vif *arvif,
if (ret) if (ret)
return ret; return ret;
mutex_lock(&ar->ab->tbl_mtx_lock);
spin_lock_bh(&ar->ab->base_lock); spin_lock_bh(&ar->ab->base_lock);
peer = ath11k_peer_find(ar->ab, param->vdev_id, param->peer_addr); peer = ath11k_peer_find(ar->ab, param->vdev_id, param->peer_addr);
if (!peer) { if (!peer) {
spin_unlock_bh(&ar->ab->base_lock); spin_unlock_bh(&ar->ab->base_lock);
mutex_unlock(&ar->ab->tbl_mtx_lock);
ath11k_warn(ar->ab, "failed to find peer %pM on vdev %i after creation\n", ath11k_warn(ar->ab, "failed to find peer %pM on vdev %i after creation\n",
param->peer_addr, param->vdev_id); param->peer_addr, param->vdev_id);
...@@ -295,6 +404,13 @@ int ath11k_peer_create(struct ath11k *ar, struct ath11k_vif *arvif, ...@@ -295,6 +404,13 @@ int ath11k_peer_create(struct ath11k *ar, struct ath11k_vif *arvif,
goto cleanup; goto cleanup;
} }
ret = ath11k_peer_rhash_add(ar->ab, peer);
if (ret) {
spin_unlock_bh(&ar->ab->base_lock);
mutex_unlock(&ar->ab->tbl_mtx_lock);
goto cleanup;
}
peer->pdev_idx = ar->pdev_idx; peer->pdev_idx = ar->pdev_idx;
peer->sta = sta; peer->sta = sta;
...@@ -319,26 +435,213 @@ int ath11k_peer_create(struct ath11k *ar, struct ath11k_vif *arvif, ...@@ -319,26 +435,213 @@ int ath11k_peer_create(struct ath11k *ar, struct ath11k_vif *arvif,
ar->num_peers++; ar->num_peers++;
spin_unlock_bh(&ar->ab->base_lock); spin_unlock_bh(&ar->ab->base_lock);
mutex_unlock(&ar->ab->tbl_mtx_lock);
return 0; return 0;
cleanup: cleanup:
reinit_completion(&ar->peer_delete_done); fbret = __ath11k_peer_delete(ar, param->vdev_id, param->peer_addr);
if (fbret)
ath11k_warn(ar->ab, "failed peer %pM delete vdev_id %d fallback ret %d\n",
param->peer_addr, param->vdev_id, fbret);
fbret = ath11k_wmi_send_peer_delete_cmd(ar, param->peer_addr, return ret;
param->vdev_id); }
if (fbret) {
ath11k_warn(ar->ab, "failed to delete peer vdev_id %d addr %pM\n", int ath11k_peer_rhash_delete(struct ath11k_base *ab, struct ath11k_peer *peer)
param->vdev_id, param->peer_addr); {
goto exit; int ret;
lockdep_assert_held(&ab->base_lock);
lockdep_assert_held(&ab->tbl_mtx_lock);
if (!ab->rhead_peer_id || !ab->rhead_peer_addr)
return -EPERM;
ret = ath11k_peer_rhash_remove(ab, ab->rhead_peer_addr, &peer->rhash_addr,
&ab->rhash_peer_addr_param);
if (ret) {
ath11k_warn(ab, "failed to remove peer %pM id %d in rhash_addr ret %d\n",
peer->addr, peer->peer_id, ret);
return ret;
} }
fbret = ath11k_wait_for_peer_delete_done(ar, param->vdev_id, ret = ath11k_peer_rhash_remove(ab, ab->rhead_peer_id, &peer->rhash_id,
param->peer_addr); &ab->rhash_peer_id_param);
if (fbret) if (ret) {
ath11k_warn(ar->ab, "failed wait for peer %pM delete done id %d fallback ret %d\n", ath11k_warn(ab, "failed to remove peer %pM id %d in rhash_id ret %d\n",
param->peer_addr, param->vdev_id, fbret); peer->addr, peer->peer_id, ret);
return ret;
}
return 0;
}
static int ath11k_peer_rhash_id_tbl_init(struct ath11k_base *ab)
{
struct rhashtable_params *param;
struct rhashtable *rhash_id_tbl;
int ret;
size_t size;
lockdep_assert_held(&ab->tbl_mtx_lock);
if (ab->rhead_peer_id)
return 0;
size = sizeof(*ab->rhead_peer_id);
rhash_id_tbl = kzalloc(size, GFP_KERNEL);
if (!rhash_id_tbl) {
ath11k_warn(ab, "failed to init rhash id table due to no mem (size %zu)\n",
size);
return -ENOMEM;
}
param = &ab->rhash_peer_id_param;
param->key_offset = offsetof(struct ath11k_peer, peer_id);
param->head_offset = offsetof(struct ath11k_peer, rhash_id);
param->key_len = sizeof_field(struct ath11k_peer, peer_id);
param->automatic_shrinking = true;
param->nelem_hint = ab->num_radios * TARGET_NUM_PEERS_PDEV(ab);
ret = rhashtable_init(rhash_id_tbl, param);
if (ret) {
ath11k_warn(ab, "failed to init peer id rhash table %d\n", ret);
goto err_free;
}
spin_lock_bh(&ab->base_lock);
if (!ab->rhead_peer_id) {
ab->rhead_peer_id = rhash_id_tbl;
} else {
spin_unlock_bh(&ab->base_lock);
goto cleanup_tbl;
}
spin_unlock_bh(&ab->base_lock);
return 0;
cleanup_tbl:
rhashtable_destroy(rhash_id_tbl);
err_free:
kfree(rhash_id_tbl);
exit:
return ret; return ret;
} }
static int ath11k_peer_rhash_addr_tbl_init(struct ath11k_base *ab)
{
struct rhashtable_params *param;
struct rhashtable *rhash_addr_tbl;
int ret;
size_t size;
lockdep_assert_held(&ab->tbl_mtx_lock);
if (ab->rhead_peer_addr)
return 0;
size = sizeof(*ab->rhead_peer_addr);
rhash_addr_tbl = kzalloc(size, GFP_KERNEL);
if (!rhash_addr_tbl) {
ath11k_warn(ab, "failed to init rhash addr table due to no mem (size %zu)\n",
size);
return -ENOMEM;
}
param = &ab->rhash_peer_addr_param;
param->key_offset = offsetof(struct ath11k_peer, addr);
param->head_offset = offsetof(struct ath11k_peer, rhash_addr);
param->key_len = sizeof_field(struct ath11k_peer, addr);
param->automatic_shrinking = true;
param->nelem_hint = ab->num_radios * TARGET_NUM_PEERS_PDEV(ab);
ret = rhashtable_init(rhash_addr_tbl, param);
if (ret) {
ath11k_warn(ab, "failed to init peer addr rhash table %d\n", ret);
goto err_free;
}
spin_lock_bh(&ab->base_lock);
if (!ab->rhead_peer_addr) {
ab->rhead_peer_addr = rhash_addr_tbl;
} else {
spin_unlock_bh(&ab->base_lock);
goto cleanup_tbl;
}
spin_unlock_bh(&ab->base_lock);
return 0;
cleanup_tbl:
rhashtable_destroy(rhash_addr_tbl);
err_free:
kfree(rhash_addr_tbl);
return ret;
}
static inline void ath11k_peer_rhash_id_tbl_destroy(struct ath11k_base *ab)
{
lockdep_assert_held(&ab->tbl_mtx_lock);
if (!ab->rhead_peer_id)
return;
rhashtable_destroy(ab->rhead_peer_id);
kfree(ab->rhead_peer_id);
ab->rhead_peer_id = NULL;
}
static inline void ath11k_peer_rhash_addr_tbl_destroy(struct ath11k_base *ab)
{
lockdep_assert_held(&ab->tbl_mtx_lock);
if (!ab->rhead_peer_addr)
return;
rhashtable_destroy(ab->rhead_peer_addr);
kfree(ab->rhead_peer_addr);
ab->rhead_peer_addr = NULL;
}
int ath11k_peer_rhash_tbl_init(struct ath11k_base *ab)
{
int ret;
mutex_lock(&ab->tbl_mtx_lock);
ret = ath11k_peer_rhash_id_tbl_init(ab);
if (ret)
goto out;
ret = ath11k_peer_rhash_addr_tbl_init(ab);
if (ret)
goto cleanup_tbl;
mutex_unlock(&ab->tbl_mtx_lock);
return 0;
cleanup_tbl:
ath11k_peer_rhash_id_tbl_destroy(ab);
out:
mutex_unlock(&ab->tbl_mtx_lock);
return ret;
}
void ath11k_peer_rhash_tbl_destroy(struct ath11k_base *ab)
{
mutex_lock(&ab->tbl_mtx_lock);
ath11k_peer_rhash_addr_tbl_destroy(ab);
ath11k_peer_rhash_id_tbl_destroy(ab);
mutex_unlock(&ab->tbl_mtx_lock);
}
/* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* /*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/ */
#ifndef ATH11K_PEER_H #ifndef ATH11K_PEER_H
...@@ -20,6 +21,11 @@ struct ath11k_peer { ...@@ -20,6 +21,11 @@ struct ath11k_peer {
struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1]; struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1];
struct dp_rx_tid rx_tid[IEEE80211_NUM_TIDS + 1]; struct dp_rx_tid rx_tid[IEEE80211_NUM_TIDS + 1];
/* peer id based rhashtable list pointer */
struct rhash_head rhash_id;
/* peer addr based rhashtable list pointer */
struct rhash_head rhash_addr;
/* Info used in MMIC verification of /* Info used in MMIC verification of
* RX fragments * RX fragments
*/ */
...@@ -47,5 +53,7 @@ int ath11k_wait_for_peer_delete_done(struct ath11k *ar, u32 vdev_id, ...@@ -47,5 +53,7 @@ int ath11k_wait_for_peer_delete_done(struct ath11k *ar, u32 vdev_id,
const u8 *addr); const u8 *addr);
struct ath11k_peer *ath11k_peer_find_by_vdev_id(struct ath11k_base *ab, struct ath11k_peer *ath11k_peer_find_by_vdev_id(struct ath11k_base *ab,
int vdev_id); int vdev_id);
int ath11k_peer_rhash_tbl_init(struct ath11k_base *ab);
void ath11k_peer_rhash_tbl_destroy(struct ath11k_base *ab);
int ath11k_peer_rhash_delete(struct ath11k_base *ab, struct ath11k_peer *peer);
#endif /* _PEER_H_ */ #endif /* _PEER_H_ */
...@@ -32,5 +32,5 @@ struct ieee80211_regdomain * ...@@ -32,5 +32,5 @@ struct ieee80211_regdomain *
ath11k_reg_build_regd(struct ath11k_base *ab, ath11k_reg_build_regd(struct ath11k_base *ab,
struct cur_regulatory_info *reg_info, bool intersect); struct cur_regulatory_info *reg_info, bool intersect);
int ath11k_regd_update(struct ath11k *ar); int ath11k_regd_update(struct ath11k *ar);
int ath11k_reg_update_chan_list(struct ath11k *ar); int ath11k_reg_update_chan_list(struct ath11k *ar, bool wait);
#endif #endif
...@@ -1538,7 +1538,7 @@ static int ath6kl_htc_rx_alloc(struct htc_target *target, ...@@ -1538,7 +1538,7 @@ static int ath6kl_htc_rx_alloc(struct htc_target *target,
queue, n_msg); queue, n_msg);
/* /*
* This is due to unavailabilty of buffers to rx entire data. * This is due to unavailability of buffers to rx entire data.
* Return no error so that free buffers from queue can be used * Return no error so that free buffers from queue can be used
* to receive partial data. * to receive partial data.
*/ */
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册