summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/spi/spi_qsd.txt4
-rw-r--r--arch/arm/boot/dts/qcom/msm8996.dtsi7
-rw-r--r--arch/arm/boot/dts/qcom/vplatform-lfv-msm8996.dtsi5
-rw-r--r--arch/arm64/configs/msm-auto-gvm-perf_defconfig3
-rw-r--r--arch/arm64/configs/msm-auto-gvm_defconfig4
-rw-r--r--drivers/bluetooth/ath3k.c18
-rw-r--r--drivers/bluetooth/btqca.c58
-rw-r--r--drivers/media/platform/msm/camera_v2/msm.c18
-rw-r--r--drivers/net/wireless/ath/ath10k/core.h3
-rw-r--r--drivers/net/wireless/ath/ath10k/mac.c4
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi-ops.h19
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi-tlv.c77
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi-tlv.h6
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi.h49
-rw-r--r--drivers/net/wireless/ath/ath10k/wow.c89
-rw-r--r--drivers/soc/qcom/msm_bus/msm_bus_dbg_voter.c15
-rw-r--r--drivers/spi/spi_qsd.c104
-rw-r--r--drivers/spi/spi_qsd.h29
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_tx.c3
-rw-r--r--include/linux/spi/qcom-spi.h2
-rw-r--r--include/uapi/linux/nl80211.h5
-rw-r--r--net/wireless/ap.c5
-rw-r--r--net/wireless/chan.c99
-rw-r--r--net/wireless/core.h10
-rw-r--r--net/wireless/db.txt6
-rw-r--r--net/wireless/ibss.c1
-rw-r--r--net/wireless/mesh.c1
-rw-r--r--net/wireless/mlme.c40
-rw-r--r--net/wireless/reg.c28
-rw-r--r--net/wireless/reg.h14
30 files changed, 678 insertions, 48 deletions
diff --git a/Documentation/devicetree/bindings/spi/spi_qsd.txt b/Documentation/devicetree/bindings/spi/spi_qsd.txt
index 1edf0820398a..b3007eb9ceea 100644
--- a/Documentation/devicetree/bindings/spi/spi_qsd.txt
+++ b/Documentation/devicetree/bindings/spi/spi_qsd.txt
@@ -1,7 +1,8 @@
Qualcomm Serial Peripheral Interface (SPI)
Required properties:
-- compatible : Should be "qcom,spi-qup-v2".
+- compatible : Should be "qcom,spi-qup-v2". Should "qcom,qup-v26" for
+ controllers that support spi slave mode.
- reg : Offset and length of the register regions for the device
- reg-names : Register region names referenced in reg above.
Required register resource entries are:
@@ -72,6 +73,7 @@ the following properties.
clock phase (CPHA) mode
- spi-cs-high : (optional) Empty property indicating device requires
chip select active high
+- qcom,slv-ctrl : Set this flag to configure QUP as SPI slave controller.
Example:
aliases {
diff --git a/arch/arm/boot/dts/qcom/msm8996.dtsi b/arch/arm/boot/dts/qcom/msm8996.dtsi
index 64c1e4550d14..9694490a39a2 100644
--- a/arch/arm/boot/dts/qcom/msm8996.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8996.dtsi
@@ -2289,6 +2289,13 @@
qcom,reset-ep-after-lpm-resume;
};
+ usb_audio_qmi_dev {
+ compatible = "qcom,usb-audio-qmi-dev";
+ iommus = <&lpass_q6_smmu 12>;
+ qcom,usb-audio-stream-id = <12>;
+ qcom,usb-audio-intr-num = <1>;
+ };
+
qcom,lpass@9300000 {
compatible = "qcom,pil-tz-generic";
reg = <0x9300000 0x00100>;
diff --git a/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996.dtsi b/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996.dtsi
index 08aa412f1ff4..7815399f23b1 100644
--- a/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996.dtsi
+++ b/arch/arm/boot/dts/qcom/vplatform-lfv-msm8996.dtsi
@@ -24,11 +24,8 @@
};
psci {
- compatible = "arm,psci";
+ compatible = "arm,psci-1.0";
method = "smc";
- cpu_suspend = <0xc4000001>;
- cpu_off = <0x84000002>;
- cpu_on = <0xc4000003>;
};
soc: soc { };
diff --git a/arch/arm64/configs/msm-auto-gvm-perf_defconfig b/arch/arm64/configs/msm-auto-gvm-perf_defconfig
index 1c4e19d9b859..70673d4959eb 100644
--- a/arch/arm64/configs/msm-auto-gvm-perf_defconfig
+++ b/arch/arm64/configs/msm-auto-gvm-perf_defconfig
@@ -44,6 +44,9 @@ CONFIG_ARMV8_DEPRECATED=y
CONFIG_SWP_EMULATION=y
CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y
CONFIG_COMPAT=y
+CONFIG_PM_AUTOSLEEP=y
+CONFIG_PM_WAKELOCKS=y
+CONFIG_PM_WAKELOCKS_LIMIT=0
CONFIG_NET=y
CONFIG_PACKET=y
CONFIG_UNIX=y
diff --git a/arch/arm64/configs/msm-auto-gvm_defconfig b/arch/arm64/configs/msm-auto-gvm_defconfig
index 05ade778b2ae..455c7581f51f 100644
--- a/arch/arm64/configs/msm-auto-gvm_defconfig
+++ b/arch/arm64/configs/msm-auto-gvm_defconfig
@@ -42,6 +42,10 @@ CONFIG_ARMV8_DEPRECATED=y
CONFIG_SWP_EMULATION=y
CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y
CONFIG_COMPAT=y
+CONFIG_PM_AUTOSLEEP=y
+CONFIG_PM_WAKELOCKS=y
+CONFIG_PM_WAKELOCKS_LIMIT=0
+CONFIG_PM_DEBUG=y
CONFIG_NET=y
CONFIG_PACKET=y
CONFIG_UNIX=y
diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c
index 5df8e1234505..071c94457d34 100644
--- a/drivers/bluetooth/ath3k.c
+++ b/drivers/bluetooth/ath3k.c
@@ -209,13 +209,27 @@ static int ath3k_load_firmware(struct usb_device *udev,
{
u8 *send_buf;
int err, pipe, len, size, sent = 0;
- int count = firmware->size;
+ int count;
BT_DBG("udev %p", udev);
+ if (!firmware || !firmware->data || firmware->size <= 0) {
+ err = -EINVAL;
+ BT_ERR("Not a valid FW file");
+ return err;
+ }
+
+ count = firmware->size;
+
+ if (count < FW_HDR_SIZE) {
+ err = -EINVAL;
+ BT_ERR("ath3k loading invalid size of file");
+ return err;
+ }
+
pipe = usb_sndctrlpipe(udev, 0);
- send_buf = kmalloc(BULK_SIZE, GFP_KERNEL);
+ send_buf = kzalloc(BULK_SIZE, GFP_KERNEL);
if (!send_buf) {
BT_ERR("Can't allocate memory chunk for firmware");
return -ENOMEM;
diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c
index 4a6208168850..0db4a00e7641 100644
--- a/drivers/bluetooth/btqca.c
+++ b/drivers/bluetooth/btqca.c
@@ -27,6 +27,9 @@
#define VERSION "0.1"
+#define MAX_PATCH_FILE_SIZE (100*1024)
+#define MAX_NVM_FILE_SIZE (10*1024)
+
static int rome_patch_ver_req(struct hci_dev *hdev, u32 *rome_version)
{
struct sk_buff *skb;
@@ -285,27 +288,59 @@ static int rome_download_firmware(struct hci_dev *hdev,
struct rome_config *config)
{
const struct firmware *fw;
+ u32 type_len, length;
+ struct tlv_type_hdr *tlv;
int ret;
- BT_INFO("%s: ROME Downloading %s", hdev->name, config->fwname);
-
+ BT_INFO("%s: ROME Downloading file: %s", hdev->name, config->fwname);
ret = request_firmware(&fw, config->fwname, &hdev->dev);
- if (ret) {
- BT_ERR("%s: Failed to request file: %s (%d)", hdev->name,
- config->fwname, ret);
+
+ if (ret || !fw || !fw->data || fw->size <= 0) {
+ BT_ERR("Failed to request file: err = (%d)", ret);
+ ret = ret ? ret : -EINVAL;
return ret;
}
+ if (config->type == TLV_TYPE_PATCH &&
+ (fw->size > MAX_PATCH_FILE_SIZE)) {
+ ret = -EINVAL;
+ BT_ERR("TLV_PATCH dload: wrong patch file sizes");
+ goto exit;
+ } else if (config->type == TLV_TYPE_NVM &&
+ (fw->size > MAX_NVM_FILE_SIZE)) {
+ ret = -EINVAL;
+ BT_ERR("TLV_NVM dload: wrong NVM file sizes");
+ goto exit;
+ } else {
+ ret = -EINVAL;
+ BT_ERR("TLV_NVM dload: wrong config type selected");
+ goto exit;
+ }
- rome_tlv_check_data(config, fw);
+ if (fw->size < sizeof(struct tlv_type_hdr)) {
+ ret = -EINVAL;
+ BT_ERR("Firware size smaller to fit minimum value");
+ goto exit;
+ }
+ tlv = (struct tlv_type_hdr *)fw->data;
+ type_len = le32_to_cpu(tlv->type_len);
+ length = (type_len >> 8) & 0x00ffffff;
+
+ if (fw->size - 4 != length) {
+ ret = -EINVAL;
+ BT_ERR("Requested size not matching size in header");
+ goto exit;
+ }
+
+ rome_tlv_check_data(config, fw);
ret = rome_tlv_download_request(hdev, fw);
+
if (ret) {
- BT_ERR("%s: Failed to download file: %s (%d)", hdev->name,
- config->fwname, ret);
+ BT_ERR("Failed to download FW: error = (%d)", ret);
}
+exit:
release_firmware(fw);
-
return ret;
}
@@ -316,8 +351,9 @@ int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr)
int err;
cmd[0] = EDL_NVM_ACCESS_SET_REQ_CMD;
- cmd[1] = 0x02; /* TAG ID */
- cmd[2] = sizeof(bdaddr_t); /* size */
+ /* Set the TAG ID of 0x02 for NVM set and size of tag */
+ cmd[1] = 0x02;
+ cmd[2] = sizeof(bdaddr_t);
memcpy(cmd + 3, bdaddr, sizeof(bdaddr_t));
skb = __hci_cmd_sync_ev(hdev, EDL_NVM_ACCESS_OPCODE, sizeof(cmd), cmd,
HCI_VENDOR_PKT, HCI_INIT_TIMEOUT);
diff --git a/drivers/media/platform/msm/camera_v2/msm.c b/drivers/media/platform/msm/camera_v2/msm.c
index 194a6583103e..6a969401e950 100644
--- a/drivers/media/platform/msm/camera_v2/msm.c
+++ b/drivers/media/platform/msm/camera_v2/msm.c
@@ -35,6 +35,7 @@
static struct v4l2_device *msm_v4l2_dev;
static struct list_head ordered_sd_list;
static struct mutex ordered_sd_mtx;
+static struct mutex v4l2_event_mtx;
static struct pm_qos_request msm_v4l2_pm_qos_request;
@@ -852,13 +853,25 @@ static long msm_private_ioctl(struct file *file, void *fh,
static int msm_unsubscribe_event(struct v4l2_fh *fh,
const struct v4l2_event_subscription *sub)
{
- return v4l2_event_unsubscribe(fh, sub);
+ int rc;
+
+ mutex_lock(&v4l2_event_mtx);
+ rc = v4l2_event_unsubscribe(fh, sub);
+ mutex_unlock(&v4l2_event_mtx);
+
+ return rc;
}
static int msm_subscribe_event(struct v4l2_fh *fh,
const struct v4l2_event_subscription *sub)
{
- return v4l2_event_subscribe(fh, sub, 5, NULL);
+ int rc;
+
+ mutex_lock(&v4l2_event_mtx);
+ rc = v4l2_event_subscribe(fh, sub, 5, NULL);
+ mutex_unlock(&v4l2_event_mtx);
+
+ return rc;
}
static const struct v4l2_ioctl_ops g_msm_ioctl_ops = {
@@ -1376,6 +1389,7 @@ static int msm_probe(struct platform_device *pdev)
spin_lock_init(&msm_eventq_lock);
spin_lock_init(&msm_pid_lock);
mutex_init(&ordered_sd_mtx);
+ mutex_init(&v4l2_event_mtx);
INIT_LIST_HEAD(&ordered_sd_list);
cam_debugfs_root = debugfs_create_dir(MSM_CAM_LOGSYNC_FILE_BASEDIR,
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index ffcf30756b9e..f194d434b97c 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -24,6 +24,7 @@
#include <linux/pci.h>
#include <linux/uuid.h>
#include <linux/time.h>
+#include <linux/inetdevice.h>
#include <soc/qcom/socinfo.h>
#include "htt.h"
@@ -430,6 +431,8 @@ struct ath10k_vif {
struct work_struct ap_csa_work;
struct delayed_work connection_loss_work;
struct cfg80211_bitrate_mask bitrate_mask;
+ struct wmi_ns_arp_offload_req arp_offload;
+ struct wmi_ns_arp_offload_req ns_offload;
};
struct ath10k_vif_iter {
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 1147dee4051e..9fcc2866d830 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -1387,6 +1387,10 @@ static int ath10k_vdev_start_restart(struct ath10k_vif *arvif,
lockdep_assert_held(&ar->conf_mutex);
+ /* Clear arp and ns offload cache */
+ memset(&arvif->arp_offload, 0, sizeof(arvif->arp_offload));
+ memset(&arvif->ns_offload, 0, sizeof(arvif->ns_offload));
+
reinit_completion(&ar->vdev_setup_done);
reinit_completion(&ar->vdev_delete_done);
diff --git a/drivers/net/wireless/ath/ath10k/wmi-ops.h b/drivers/net/wireless/ath/ath10k/wmi-ops.h
index 8bb01c1a35f7..dcb0da51530a 100644
--- a/drivers/net/wireless/ath/ath10k/wmi-ops.h
+++ b/drivers/net/wireless/ath/ath10k/wmi-ops.h
@@ -160,6 +160,8 @@ struct wmi_ops {
u32 num_ac);
struct sk_buff *(*gen_sta_keepalive)(struct ath10k *ar,
const struct wmi_sta_keepalive_arg *arg);
+ struct sk_buff *(*gen_set_arp_ns_offload)(struct ath10k *ar,
+ struct ath10k_vif *arvif);
struct sk_buff *(*gen_wow_enable)(struct ath10k *ar);
struct sk_buff *(*gen_wow_add_wakeup_event)(struct ath10k *ar, u32 vdev_id,
enum wmi_wow_wakeup_event event,
@@ -1187,6 +1189,23 @@ ath10k_wmi_sta_keepalive(struct ath10k *ar,
}
static inline int
+ath10k_wmi_set_arp_ns_offload(struct ath10k *ar, struct ath10k_vif *arvif)
+{
+ struct sk_buff *skb;
+ u32 cmd_id;
+
+ if (!ar->wmi.ops->gen_set_arp_ns_offload)
+ return -EOPNOTSUPP;
+
+ skb = ar->wmi.ops->gen_set_arp_ns_offload(ar, arvif);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+ cmd_id = ar->wmi.cmd->set_arp_ns_offload_cmdid;
+ return ath10k_wmi_cmd_send(ar, skb, cmd_id);
+}
+
+static inline int
ath10k_wmi_wow_enable(struct ath10k *ar)
{
struct sk_buff *skb;
diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c
index 95bd588e0d2c..b904a0d9d391 100644
--- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c
+++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c
@@ -3010,6 +3010,82 @@ ath10k_wmi_tlv_op_gen_tdls_peer_update(struct ath10k *ar,
}
static struct sk_buff *
+ath10k_wmi_tlv_op_gen_set_arp_ns_offload(struct ath10k *ar,
+ struct ath10k_vif *arvif)
+{
+ struct wmi_tlv_arp_ns_offload_cmd *cmd;
+ struct wmi_tlv *tlv;
+ struct sk_buff *skb;
+ size_t len;
+ void *ptr;
+ int i;
+ struct wmi_ns_arp_offload_req *arp = &arvif->arp_offload;
+ struct wmi_ns_offload *ns_tuple[WMI_MAX_NS_OFFLOADS];
+ struct wmi_arp_offload *arp_tuple[WMI_MAX_ARP_OFFLOADS];
+
+ len = sizeof(*cmd) + sizeof(*tlv) +
+ sizeof(*tlv) + WMI_MAX_NS_OFFLOADS *
+ (sizeof(struct wmi_ns_offload) + sizeof(*tlv)) +
+ sizeof(*tlv) + WMI_MAX_ARP_OFFLOADS *
+ (sizeof(struct wmi_arp_offload) + sizeof(*tlv));
+
+ skb = ath10k_wmi_alloc_skb(ar, len);
+ if (!skb)
+ return ERR_PTR(-ENOMEM);
+
+ ptr = (void *)skb->data;
+ tlv = ptr;
+ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_SET_ARP_NS_OFFLOAD_CMD);
+ tlv->len = __cpu_to_le16(sizeof(*cmd));
+ cmd = (struct wmi_tlv_arp_ns_offload_cmd *)tlv->value;
+ cmd->flags = __cpu_to_le32(0);
+ cmd->vdev_id = __cpu_to_le32(arvif->vdev_id);
+
+ ptr += (sizeof(*tlv) + sizeof(*cmd));
+ tlv = ptr;
+ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_STRUCT);
+ tlv->len = __cpu_to_le16(WMI_MAX_NS_OFFLOADS *
+ (sizeof(struct wmi_ns_offload) + sizeof(*tlv)));
+ ptr += sizeof(*tlv);
+ tlv = ptr;
+
+ for (i = 0; i < WMI_MAX_NS_OFFLOADS; i++) {
+ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_NS_OFFLOAD_TUPLE);
+ tlv->len = __cpu_to_le16(sizeof(struct wmi_ns_offload));
+ ns_tuple[i] = (struct wmi_ns_offload *)tlv->value;
+ ns_tuple[i]->flags |= __cpu_to_le32(WMI_ARP_NS_OFFLOAD_DISABLE);
+ ptr += (sizeof(*tlv) + sizeof(struct wmi_ns_offload));
+ tlv = ptr;
+ }
+
+ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_STRUCT);
+ tlv->len = __cpu_to_le16(WMI_MAX_ARP_OFFLOADS *
+ (sizeof(struct wmi_arp_offload) + sizeof(*tlv)));
+ ptr += sizeof(*tlv);
+ tlv = ptr;
+
+ for (i = 0; i < WMI_MAX_ARP_OFFLOADS; i++) {
+ tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_ARP_OFFLOAD_TUPLE);
+ tlv->len = __cpu_to_le16(sizeof(struct wmi_arp_offload));
+ arp_tuple[i] = (struct wmi_arp_offload *)tlv->value;
+ if (arp->enable_offload && (i == 0)) {
+ arp_tuple[i]->flags |=
+ __cpu_to_le32(WMI_ARPOFF_FLAGS_VALID);
+ memcpy(&arp_tuple[i]->target_ipaddr,
+ &arp->params.ipv4_addr, 4);
+ } else {
+ arp_tuple[i]->flags |=
+ __cpu_to_le32(WMI_ARP_NS_OFFLOAD_DISABLE);
+ }
+ ptr += (sizeof(*tlv) + sizeof(struct wmi_arp_offload));
+ tlv = ptr;
+ }
+
+ ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv set arp ns offload\n");
+ return skb;
+}
+
+static struct sk_buff *
ath10k_wmi_tlv_op_gen_wow_enable(struct ath10k *ar)
{
struct wmi_tlv_wow_enable_cmd *cmd;
@@ -3695,6 +3771,7 @@ static const struct wmi_ops wmi_tlv_ops = {
.gen_p2p_go_bcn_ie = ath10k_wmi_tlv_op_gen_p2p_go_bcn_ie,
.gen_vdev_sta_uapsd = ath10k_wmi_tlv_op_gen_vdev_sta_uapsd,
.gen_sta_keepalive = ath10k_wmi_tlv_op_gen_sta_keepalive,
+ .gen_set_arp_ns_offload = ath10k_wmi_tlv_op_gen_set_arp_ns_offload,
.gen_wow_enable = ath10k_wmi_tlv_op_gen_wow_enable,
.gen_wow_add_wakeup_event = ath10k_wmi_tlv_op_gen_wow_add_wakeup_event,
.gen_wow_host_wakeup_ind = ath10k_wmi_tlv_gen_wow_host_wakeup_ind,
diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.h b/drivers/net/wireless/ath/ath10k/wmi-tlv.h
index 7140e5e23307..1d4aed14fbb4 100644
--- a/drivers/net/wireless/ath/ath10k/wmi-tlv.h
+++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.h
@@ -1557,6 +1557,12 @@ struct wmi_tlv_wow_enable_cmd {
__le32 pause_iface_config;
} __packed;
+struct wmi_tlv_arp_ns_offload_cmd {
+ __le32 flags;
+ __le32 vdev_id;
+ __le32 num_ns_ext_tuples;
+} __packed;
+
struct wmi_tlv_wow_host_wakeup_ind {
__le32 reserved;
} __packed;
diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h
index f59e5f86708b..9b17ef150303 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.h
+++ b/drivers/net/wireless/ath/ath10k/wmi.h
@@ -20,6 +20,8 @@
#include <linux/types.h>
#include <net/mac80211.h>
+#include <linux/ipv6.h>
+#include <linux/in.h>
/*
* This file specifies the WMI interface for the Unified Software
@@ -2884,6 +2886,53 @@ struct wmi_start_scan_common {
__le32 scan_ctrl_flags;
} __packed;
+/* ARP-NS offload data structure */
+#define WMI_NSOFF_MAX_TARGET_IPS 2
+#define WMI_MAX_NS_OFFLOADS 2
+#define WMI_MAX_ARP_OFFLOADS 2
+#define WMI_ARPOFF_FLAGS_VALID BIT(0)
+#define WMI_IPV4_ARP_REPLY_OFFLOAD 0
+#define WMI_ARP_NS_OFFLOAD_DISABLE 0
+#define WMI_ARP_NS_OFFLOAD_ENABLE 1
+
+struct wmi_ns_offload_info {
+ struct in6_addr src_addr;
+ struct in6_addr self_addr[TARGET_NUM_STATIONS];
+ struct in6_addr target_addr[TARGET_NUM_STATIONS];
+ struct wmi_mac_addr self_macaddr;
+ u8 src_ipv6_addr_valid;
+ struct in6_addr target_addr_valid;
+ struct in6_addr target_addr_ac_type;
+ u8 slot_idx;
+} __packed;
+
+struct wmi_ns_arp_offload_req {
+ u8 offload_type;
+ u8 enable_offload;
+ __le32 num_ns_offload_count;
+ union {
+ struct in_addr ipv4_addr;
+ struct in6_addr ipv6_addr;
+ } params;
+ struct wmi_ns_offload_info offload_info;
+ struct wmi_mac_addr bssid;
+} __packed;
+
+struct wmi_ns_offload {
+ __le32 flags;
+ struct in6_addr target_ipaddr[WMI_NSOFF_MAX_TARGET_IPS];
+ struct in6_addr solicitation_ipaddr;
+ struct in6_addr remote_ipaddr;
+ struct wmi_mac_addr target_mac;
+} __packed;
+
+struct wmi_arp_offload {
+ __le32 flags;
+ struct in_addr target_ipaddr;
+ struct in_addr remote_ipaddr;
+ struct wmi_mac_addr target_mac;
+} __packed;
+
struct wmi_start_scan_tlvs {
/* TLV parameters. These includes channel list, ssid list, bssid list,
* extra ies.
diff --git a/drivers/net/wireless/ath/ath10k/wow.c b/drivers/net/wireless/ath/ath10k/wow.c
index de678fd3b801..1a43375c3264 100644
--- a/drivers/net/wireless/ath/ath10k/wow.c
+++ b/drivers/net/wireless/ath/ath10k/wow.c
@@ -224,6 +224,76 @@ static int ath10k_wow_wakeup(struct ath10k *ar)
return 0;
}
+static int
+ath10k_wow_fill_vdev_arp_offload_struct(struct ath10k_vif *arvif,
+ bool enable_offload)
+{
+ struct in_device *in_dev;
+ struct in_ifaddr *ifa;
+ bool offload_params_found = false;
+ struct wireless_dev *wdev = ieee80211_vif_to_wdev(arvif->vif);
+ struct wmi_ns_arp_offload_req *arp = &arvif->arp_offload;
+
+ if (!enable_offload) {
+ arp->offload_type = __cpu_to_le16(WMI_IPV4_ARP_REPLY_OFFLOAD);
+ arp->enable_offload = __cpu_to_le16(WMI_ARP_NS_OFFLOAD_DISABLE);
+ return 0;
+ }
+
+ if (!wdev)
+ return -ENODEV;
+ if (!wdev->netdev)
+ return -ENODEV;
+ in_dev = __in_dev_get_rtnl(wdev->netdev);
+ if (!in_dev)
+ return -ENODEV;
+
+ arp->offload_type = __cpu_to_le16(WMI_IPV4_ARP_REPLY_OFFLOAD);
+ arp->enable_offload = __cpu_to_le16(WMI_ARP_NS_OFFLOAD_ENABLE);
+ for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
+ if (!strcmp(ifa->ifa_label, wdev->netdev->name)) {
+ offload_params_found = true;
+ break;
+ }
+ }
+
+ if (!offload_params_found)
+ return -ENODEV;
+
+ memcpy(&arp->params.ipv4_addr, &ifa->ifa_local, 4);
+ return 0;
+}
+
+static int ath10k_wow_enable_ns_arp_offload(struct ath10k *ar, bool offload)
+{
+ struct ath10k_vif *arvif;
+ int ret;
+
+ list_for_each_entry(arvif, &ar->arvifs, list) {
+ if (arvif->vdev_type != WMI_VDEV_TYPE_STA)
+ continue;
+
+ if (!arvif->is_up)
+ continue;
+
+ ret = ath10k_wow_fill_vdev_arp_offload_struct(arvif, offload);
+ if (ret) {
+ ath10k_err(ar, "ARP-offload config failed, vdev: %d\n",
+ arvif->vdev_id);
+ return ret;
+ }
+
+ ret = ath10k_wmi_set_arp_ns_offload(ar, arvif);
+ if (ret) {
+ ath10k_err(ar, "failed to send offload cmd, vdev: %d\n",
+ arvif->vdev_id);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
static int ath10k_config_wow_listen_interval(struct ath10k *ar)
{
int ret;
@@ -263,11 +333,17 @@ int ath10k_wow_op_suspend(struct ieee80211_hw *hw,
goto exit;
}
+ ret = ath10k_wow_enable_ns_arp_offload(ar, true);
+ if (ret) {
+ ath10k_warn(ar, "failed to enable ARP-NS offload: %d\n", ret);
+ goto exit;
+ }
+
ret = ath10k_wow_cleanup(ar);
if (ret) {
ath10k_warn(ar, "failed to clear wow wakeup events: %d\n",
ret);
- goto exit;
+ goto disable_ns_arp_offload;
}
ret = ath10k_wow_set_wakeups(ar, wowlan);
@@ -304,6 +380,9 @@ wakeup:
cleanup:
ath10k_wow_cleanup(ar);
+disable_ns_arp_offload:
+ ath10k_wow_enable_ns_arp_offload(ar, false);
+
exit:
mutex_unlock(&ar->conf_mutex);
return ret ? 1 : 0;
@@ -341,8 +420,14 @@ int ath10k_wow_op_resume(struct ieee80211_hw *hw)
}
ret = ath10k_wow_wakeup(ar);
- if (ret)
+ if (ret) {
ath10k_warn(ar, "failed to wakeup from wow: %d\n", ret);
+ goto exit;
+ }
+
+ ret = ath10k_wow_enable_ns_arp_offload(ar, false);
+ if (ret)
+ ath10k_warn(ar, "failed to disable ARP-NS offload: %d\n", ret);
exit:
if (ret) {
diff --git a/drivers/soc/qcom/msm_bus/msm_bus_dbg_voter.c b/drivers/soc/qcom/msm_bus/msm_bus_dbg_voter.c
index a876484859eb..ba1adb8acea7 100644
--- a/drivers/soc/qcom/msm_bus/msm_bus_dbg_voter.c
+++ b/drivers/soc/qcom/msm_bus/msm_bus_dbg_voter.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is Mree software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -27,6 +27,7 @@ struct msm_bus_floor_client_type {
};
static struct class *bus_floor_class;
+static DEFINE_RT_MUTEX(msm_bus_floor_vote_lock);
#define MAX_VOTER_NAME (50)
#define DEFAULT_NODE_WIDTH (8)
#define DBG_NAME(s) (strnstr(s, "-", 7) + 1)
@@ -64,18 +65,22 @@ static ssize_t bus_floor_active_only_store(struct device *dev,
{
struct msm_bus_floor_client_type *cl;
+ rt_mutex_lock(&msm_bus_floor_vote_lock);
cl = dev_get_drvdata(dev);
if (!cl) {
pr_err("%s: Can't find cl", __func__);
+ rt_mutex_unlock(&msm_bus_floor_vote_lock);
return 0;
}
if (sscanf(buf, "%d", &cl->active_only) != 1) {
pr_err("%s:return error", __func__);
+ rt_mutex_unlock(&msm_bus_floor_vote_lock);
return -EINVAL;
}
+ rt_mutex_unlock(&msm_bus_floor_vote_lock);
return n;
}
@@ -100,20 +105,24 @@ static ssize_t bus_floor_vote_store(struct device *dev,
struct msm_bus_floor_client_type *cl;
int ret = 0;
+ rt_mutex_lock(&msm_bus_floor_vote_lock);
cl = dev_get_drvdata(dev);
if (!cl) {
pr_err("%s: Can't find cl", __func__);
+ rt_mutex_unlock(&msm_bus_floor_vote_lock);
return 0;
}
if (sscanf(buf, "%llu", &cl->cur_vote_hz) != 1) {
pr_err("%s:return error", __func__);
+ rt_mutex_unlock(&msm_bus_floor_vote_lock);
return -EINVAL;
}
ret = msm_bus_floor_vote_context(dev_name(dev), cl->cur_vote_hz,
cl->active_only);
+ rt_mutex_unlock(&msm_bus_floor_vote_lock);
return n;
}
@@ -126,15 +135,18 @@ static ssize_t bus_floor_vote_store_api(struct device *dev,
char name[10];
u64 vote_khz = 0;
+ rt_mutex_lock(&msm_bus_floor_vote_lock);
cl = dev_get_drvdata(dev);
if (!cl) {
pr_err("%s: Can't find cl", __func__);
+ rt_mutex_unlock(&msm_bus_floor_vote_lock);
return 0;
}
if (sscanf(buf, "%9s %llu", name, &vote_khz) != 2) {
pr_err("%s:return error", __func__);
+ rt_mutex_unlock(&msm_bus_floor_vote_lock);
return -EINVAL;
}
@@ -142,6 +154,7 @@ static ssize_t bus_floor_vote_store_api(struct device *dev,
__func__, name, vote_khz);
ret = msm_bus_floor_vote(name, vote_khz);
+ rt_mutex_unlock(&msm_bus_floor_vote_lock);
return n;
}
diff --git a/drivers/spi/spi_qsd.c b/drivers/spi/spi_qsd.c
index da58f19dd6e6..73396072a052 100644
--- a/drivers/spi/spi_qsd.c
+++ b/drivers/spi/spi_qsd.c
@@ -33,6 +33,7 @@
#include <linux/debugfs.h>
#include <linux/gpio.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/dma-mapping.h>
#include <linux/sched.h>
@@ -52,6 +53,7 @@ static int msm_spi_pm_suspend_runtime(struct device *device);
static inline void msm_spi_dma_unmap_buffers(struct msm_spi *dd);
static int get_local_resources(struct msm_spi *dd);
static void put_local_resources(struct msm_spi *dd);
+static void msm_spi_slv_setup(struct msm_spi *dd);
static inline int msm_spi_configure_gsbi(struct msm_spi *dd,
struct platform_device *pdev)
@@ -84,7 +86,10 @@ static inline int msm_spi_configure_gsbi(struct msm_spi *dd,
static inline void msm_spi_register_init(struct msm_spi *dd)
{
- writel_relaxed(0x00000001, dd->base + SPI_SW_RESET);
+ if (dd->pdata->is_slv_ctrl)
+ writel_relaxed(0x00000002, dd->base + SPI_SW_RESET);
+ else
+ writel_relaxed(0x00000001, dd->base + SPI_SW_RESET);
msm_spi_set_state(dd, SPI_OP_STATE_RESET);
writel_relaxed(0x00000000, dd->base + SPI_OPERATIONAL);
writel_relaxed(0x00000000, dd->base + SPI_CONFIG);
@@ -932,6 +937,7 @@ static inline void msm_spi_ack_transfer(struct msm_spi *dd)
static inline irqreturn_t msm_spi_qup_irq(int irq, void *dev_id)
{
u32 op, ret = IRQ_NONE;
+ u32 slv;
struct msm_spi *dd = dev_id;
if (pm_runtime_suspended(dd->dev)) {
@@ -945,7 +951,9 @@ static inline irqreturn_t msm_spi_qup_irq(int irq, void *dev_id)
}
op = readl_relaxed(dd->base + SPI_OPERATIONAL);
+ slv = readl_relaxed(dd->base + SPI_SLAVE_IRQ_STATUS);
writel_relaxed(op, dd->base + SPI_OPERATIONAL);
+ writel_relaxed(slv, dd->base + SPI_SLAVE_IRQ_STATUS);
/*
* Ensure service flag was cleared before further
* processing of interrupt.
@@ -1234,7 +1242,10 @@ msm_spi_use_dma(struct msm_spi *dd, struct spi_transfer *tr, u8 bpw)
static void
msm_spi_set_transfer_mode(struct msm_spi *dd, u8 bpw, u32 read_count)
{
- if (msm_spi_use_dma(dd, dd->cur_transfer, bpw)) {
+ if (dd->pdata->is_slv_ctrl) {
+ dd->tx_mode = SPI_BAM_MODE;
+ dd->rx_mode = SPI_BAM_MODE;
+ } else if (msm_spi_use_dma(dd, dd->cur_transfer, bpw)) {
dd->tx_mode = SPI_BAM_MODE;
dd->rx_mode = SPI_BAM_MODE;
} else {
@@ -1353,7 +1364,7 @@ static void get_transfer_length(struct msm_spi *dd)
static int msm_spi_process_transfer(struct msm_spi *dd)
{
u8 bpw;
- u32 max_speed;
+ u32 max_speed = 0;
u32 read_count;
u32 timeout;
u32 spi_ioc;
@@ -1392,7 +1403,7 @@ static int msm_spi_process_transfer(struct msm_spi *dd)
DIV_ROUND_UP(max_speed, MSEC_PER_SEC)));
read_count = DIV_ROUND_UP(dd->cur_msg_len, dd->bytes_per_word);
- if (dd->spi->mode & SPI_LOOP)
+ if (dd->spi->mode & SPI_LOOP && !dd->pdata->is_slv_ctrl)
int_loopback = 1;
if (msm_spi_set_state(dd, SPI_OP_STATE_RESET))
@@ -1414,8 +1425,11 @@ static int msm_spi_process_transfer(struct msm_spi *dd)
msm_spi_set_qup_io_modes(dd);
msm_spi_set_spi_config(dd, bpw);
msm_spi_set_qup_config(dd, bpw);
- spi_ioc = msm_spi_set_spi_io_control(dd);
+ if (!dd->pdata->is_slv_ctrl)
+ spi_ioc = msm_spi_set_spi_io_control(dd);
msm_spi_set_qup_op_mask(dd);
+ if (dd->pdata->is_slv_ctrl)
+ msm_spi_slv_setup(dd);
/* The output fifo interrupt handler will handle all writes after
the first. Restricting this to one write avoids contention
@@ -1482,12 +1496,20 @@ transfer_end:
dd->rx_mode = SPI_MODE_NONE;
msm_spi_set_state(dd, SPI_OP_STATE_RESET);
- if (!dd->cur_transfer->cs_change)
+ if (!dd->cur_transfer->cs_change && !dd->pdata->is_slv_ctrl)
writel_relaxed(spi_ioc & ~SPI_IO_C_MX_CS_MODE,
dd->base + SPI_IO_CONTROL);
return status;
}
+static int msm_spi_slv_abort(struct spi_master *spi)
+{
+ struct msm_spi *dd = spi_master_get_devdata(spi);
+
+ complete_all(&dd->tx_transfer_complete);
+ complete_all(&dd->rx_transfer_complete);
+ return 0;
+}
static inline void msm_spi_set_cs(struct spi_device *spi, bool set_flag)
{
@@ -1753,6 +1775,32 @@ static int msm_spi_unprepare_transfer_hardware(struct spi_master *master)
return 0;
}
+static void msm_spi_slv_setup(struct msm_spi *dd)
+{
+ u32 spi_config = readl_relaxed(dd->base + SPI_CONFIG);
+ u32 qup_config = readl_relaxed(dd->base + QUP_CONFIG);
+ u32 irq_en = GENMASK(6, 0);
+
+ qup_config &= ~QUP_CFG_MODE;
+ qup_config |= QUP_CONFIG_SPI_SLAVE;
+ qup_config |= (SPI_EN_EXT_OUT_FLAG | APP_CLK_ON_EN | CORE_CLK_ON_EN
+ | FIFO_CLK_ON_EN | CORE_EX_CLK_ON_EN);
+ spi_config |= SPI_CFG_SLAVE_OP;
+ writel_relaxed(qup_config, dd->base + QUP_CONFIG);
+ writel_relaxed(spi_config, dd->base + SPI_CONFIG);
+ writel_relaxed(irq_en, (dd->base + SPI_SLAVE_IRQ_EN));
+ if (dd->read_buf && !dd->write_buf) {
+ u32 slv_cfg =
+ readl_relaxed(dd->base + SPI_SLAVE_CONFIG);
+ slv_cfg |= (RX_UNBALANCED_MASK | SPI_S_CGC_EN);
+ writel_relaxed(slv_cfg, (dd->base + SPI_SLAVE_CONFIG));
+ }
+ /*
+ * Ensure Slave setup completes before returning.
+ */
+ mb();
+}
+
static int msm_spi_setup(struct spi_device *spi)
{
struct msm_spi *dd;
@@ -2248,6 +2296,8 @@ struct msm_spi_platform_data *msm_spi_dt_to_pdata(
&pdata->rt_priority, DT_OPT, DT_BOOL, 0},
{"qcom,shared",
&pdata->is_shared, DT_OPT, DT_BOOL, 0},
+ {"qcom,slv-ctrl",
+ &pdata->is_slv_ctrl, DT_OPT, DT_BOOL, 0},
{NULL, NULL, 0, 0, 0},
};
@@ -2451,6 +2501,12 @@ err_clk_get:
return rc;
}
+static const struct of_device_id msm_spi_dt_match[] = {
+ { .compatible = "qcom,spi-qup-v2", },
+ { .compatible = "qcom,qup-v26", },
+ {}
+};
+
static int msm_spi_probe(struct platform_device *pdev)
{
struct spi_master *master;
@@ -2481,6 +2537,9 @@ static int msm_spi_probe(struct platform_device *pdev)
dd = spi_master_get_devdata(master);
if (pdev->dev.of_node) {
+ const struct of_device_id *dev_id;
+ enum msm_spi_qup_version ver;
+
dd->qup_ver = SPI_QUP_VERSION_BFAM;
master->dev.of_node = pdev->dev.of_node;
pdata = msm_spi_dt_to_pdata(pdev, dd);
@@ -2495,6 +2554,17 @@ static int msm_spi_probe(struct platform_device *pdev)
"using default bus_num %d\n", pdev->id);
else
master->bus_num = pdev->id = rc;
+
+ dev_id = of_match_device(msm_spi_dt_match, &pdev->dev);
+ if (dev_id)
+ ver = SPI_QUP_VERSION_SPI_SLV;
+ else
+ ver = SPI_QUP_VERSION_BFAM;
+
+ if (ver >= SPI_QUP_VERSION_SPI_SLV)
+ dd->slv_support = true;
+ else
+ dd->slv_support = false;
} else {
pdata = pdev->dev.platform_data;
dd->qup_ver = SPI_QUP_VERSION_NONE;
@@ -2553,6 +2623,21 @@ static int msm_spi_probe(struct platform_device *pdev)
}
spi_dma_mask(&pdev->dev);
+
+ if (pdata && pdata->is_slv_ctrl) {
+ if (!dd->slv_support) {
+ rc = -ENXIO;
+ dev_err(&pdev->dev, "QUP ver %d, no slv support\n",
+ dd->qup_ver);
+ goto err_probe_res;
+ }
+
+ master->slave = true;
+ master->set_cs = NULL;
+ master->setup = NULL;
+ master->slave_abort = msm_spi_slv_abort;
+ }
+
skip_dma_resources:
spin_lock_init(&dd->queue_lock);
@@ -2753,13 +2838,6 @@ static int msm_spi_remove(struct platform_device *pdev)
return 0;
}
-static struct of_device_id msm_spi_dt_match[] = {
- {
- .compatible = "qcom,spi-qup-v2",
- },
- {}
-};
-
static const struct dev_pm_ops msm_spi_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(msm_spi_suspend, msm_spi_resume)
SET_RUNTIME_PM_OPS(msm_spi_pm_suspend_runtime,
diff --git a/drivers/spi/spi_qsd.h b/drivers/spi/spi_qsd.h
index 47d69965f18a..6632fe806e41 100644
--- a/drivers/spi/spi_qsd.h
+++ b/drivers/spi/spi_qsd.h
@@ -38,6 +38,7 @@
#define QUP_MX_WRITE_CNT_CURRENT 0x0154
#define QUP_CONFIG_SPI_MODE 0x0100
+#define QUP_CONFIG_SPI_SLAVE 0x0400
#endif
#define GSBI_CTRL_REG 0x0
@@ -72,17 +73,26 @@
#define SPI_OUTPUT_FIFO QSD_REG(0x0100) QUP_REG(0x0110)
#define SPI_INPUT_FIFO QSD_REG(0x0200) QUP_REG(0x0218)
#define SPI_STATE QSD_REG(SPI_OPERATIONAL) QUP_REG(0x0004)
+#define SPI_SLAVE_IRQ_STATUS (0x0330)
+#define SPI_SLAVE_IRQ_EN (0x0334)
+#define SPI_SLAVE_CONFIG (0x0338)
/* QUP_CONFIG fields */
#define SPI_CFG_N 0x0000001F
#define SPI_NO_INPUT 0x00000080
#define SPI_NO_OUTPUT 0x00000040
#define SPI_EN_EXT_OUT_FLAG 0x00010000
+#define QUP_CFG_MODE 0x00000F00
+#define APP_CLK_ON_EN BIT(12)
+#define CORE_CLK_ON_EN BIT(13)
+#define FIFO_CLK_ON_EN BIT(14)
+#define CORE_EX_CLK_ON_EN BIT(15)
/* SPI_CONFIG fields */
#define SPI_CFG_LOOPBACK 0x00000100
#define SPI_CFG_INPUT_FIRST 0x00000200
#define SPI_CFG_HS_MODE 0x00000400
+#define SPI_CFG_SLAVE_OP 0x00000020
/* SPI_IO_CONTROL fields */
#define SPI_IO_C_FORCE_CS 0x00000800
@@ -128,6 +138,23 @@
#define SPI_OP_STATE_CLEAR_BITS 0x2
+/* SPI SLAVE IRQ_STATUS/EN fields */
+#define CS_N_ASSERT BIT(0)
+#define CS_N_DEASSERT BIT(1)
+#define CS_N_ETXT BIT(2)
+#define TX_UNDERFLOW BIT(3)
+#define RX_OVERFLOW_WAIT_EOT BIT(4)
+#define RX_OVERFLOW_NO_EOT BIT(5)
+#define CS_N_ERXT BIT(6)
+
+/* SPI_SLAVE_CONFIG Fields */
+#define RX_N_SHIFT BIT(0)
+#define PAUSE_ON_ERR_DIS BIT(1)
+#define SPI_S_CGC_EN BIT(2)
+#define RX_UNBALANCED_MASK BIT(3)
+#define SLAVE_DIS_RESET_ST BIT(4)
+#define SLAVE_AUTO_PAUSE_EOT BIT(7)
+
#define SPI_PINCTRL_STATE_DEFAULT "spi_default"
#define SPI_PINCTRL_STATE_SLEEP "spi_sleep"
@@ -177,6 +204,7 @@ enum msm_spi_state {
enum msm_spi_qup_version {
SPI_QUP_VERSION_NONE = 0x0,
SPI_QUP_VERSION_BFAM = 0x2,
+ SPI_QUP_VERSION_SPI_SLV = 0x26,
};
enum msm_spi_pipe_direction {
@@ -376,6 +404,7 @@ struct msm_spi {
struct pinctrl_state *pins_sleep;
bool is_init_complete;
bool pack_words;
+ bool slv_support;
};
/* Forward declaration */
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.c b/drivers/video/fbdev/msm/mdss_hdmi_tx.c
index 5eb17ab27e4c..5cb436261115 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_tx.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.c
@@ -2223,6 +2223,9 @@ static int hdmi_tx_read_sink_info(struct hdmi_tx_ctrl *hdmi_ctrl)
DSS_REG_W_ND(io, HDMI_DDC_ARBITRATION,
DSS_REG_R(io, HDMI_DDC_ARBITRATION) & ~(BIT(4)));
+ /* Set/Reset HDMI max TMDS clock supported by source */
+ hdmi_edid_set_max_pclk_rate(data, hdmi_ctrl->max_pclk_khz);
+
if (!hdmi_ctrl->custom_edid && !hdmi_ctrl->sim_mode) {
hdmi_ddc_config(&hdmi_ctrl->ddc_ctrl);
diff --git a/include/linux/spi/qcom-spi.h b/include/linux/spi/qcom-spi.h
index 598481079383..67ef31c69641 100644
--- a/include/linux/spi/qcom-spi.h
+++ b/include/linux/spi/qcom-spi.h
@@ -48,9 +48,11 @@ struct msm_spi_platform_data {
u32 infinite_mode;
bool ver_reg_exists;
bool use_bam;
+ bool slv_test;
u32 bam_consumer_pipe_index;
u32 bam_producer_pipe_index;
bool rt_priority;
bool use_pinctrl;
bool is_shared;
+ bool is_slv_ctrl;
};
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 3dd0091f4af3..75050fac06fb 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -4904,12 +4904,17 @@ enum nl80211_smps_mode {
* change to the channel status.
* @NL80211_RADAR_NOP_FINISHED: The Non-Occupancy Period for this channel is
* over, channel becomes usable.
+ * @NL80211_RADAR_PRE_CAC_EXPIRED: Channel Availability Check done on this
+ * non-operating channel is expired and no longer valid. New CAC must
+ * be done on this channel before starting the operation. This is not
+ * applicable for ETSI dfs domain where pre-CAC is valid for ever.
*/
enum nl80211_radar_event {
NL80211_RADAR_DETECTED,
NL80211_RADAR_CAC_FINISHED,
NL80211_RADAR_CAC_ABORTED,
NL80211_RADAR_NOP_FINISHED,
+ NL80211_RADAR_PRE_CAC_EXPIRED,
};
/**
diff --git a/net/wireless/ap.c b/net/wireless/ap.c
index 91d02ac0f42f..547d38356583 100644
--- a/net/wireless/ap.c
+++ b/net/wireless/ap.c
@@ -32,6 +32,11 @@ int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
rdev_set_qos_map(rdev, dev, NULL);
if (notify)
nl80211_send_ap_stopped(wdev);
+
+ /* Should we apply the grace period during beaconing interface
+ * shutdown also?
+ */
+ cfg80211_sched_dfs_chan_update(rdev);
}
return err;
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index d5ccaeaa76e0..6b5467ea99db 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -456,6 +456,105 @@ bool cfg80211_chandef_dfs_usable(struct wiphy *wiphy,
return (r1 + r2 > 0);
}
+/*
+ * Checks if center frequency of chan falls with in the bandwidth
+ * range of chandef.
+ */
+bool cfg80211_is_sub_chan(struct cfg80211_chan_def *chandef,
+ struct ieee80211_channel *chan)
+{
+ int width;
+ u32 cf_offset, freq;
+
+ if (chandef->chan->center_freq == chan->center_freq)
+ return true;
+
+ width = cfg80211_chandef_get_width(chandef);
+ if (width <= 20)
+ return false;
+
+ cf_offset = width / 2 - 10;
+
+ for (freq = chandef->center_freq1 - width / 2 + 10;
+ freq <= chandef->center_freq1 + width / 2 - 10; freq += 20) {
+ if (chan->center_freq == freq)
+ return true;
+ }
+
+ if (!chandef->center_freq2)
+ return false;
+
+ for (freq = chandef->center_freq2 - width / 2 + 10;
+ freq <= chandef->center_freq2 + width / 2 - 10; freq += 20) {
+ if (chan->center_freq == freq)
+ return true;
+ }
+
+ return false;
+}
+
+bool cfg80211_beaconing_iface_active(struct wireless_dev *wdev)
+{
+ bool active = false;
+
+ ASSERT_WDEV_LOCK(wdev);
+
+ if (!wdev->chandef.chan)
+ return false;
+
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ active = wdev->beacon_interval != 0;
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ active = wdev->ssid_len != 0;
+ break;
+ case NL80211_IFTYPE_MESH_POINT:
+ active = wdev->mesh_id_len != 0;
+ break;
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_OCB:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_WDS:
+ case NL80211_IFTYPE_P2P_DEVICE:
+ break;
+ case NL80211_IFTYPE_UNSPECIFIED:
+ case NUM_NL80211_IFTYPES:
+ WARN_ON(1);
+ }
+
+ return active;
+}
+
+bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy,
+ struct ieee80211_channel *chan)
+{
+ struct wireless_dev *wdev;
+
+ ASSERT_RTNL();
+
+ if (!(chan->flags & IEEE80211_CHAN_RADAR))
+ return false;
+
+ list_for_each_entry(wdev, &wiphy->wdev_list, list) {
+ wdev_lock(wdev);
+ if (!cfg80211_beaconing_iface_active(wdev)) {
+ wdev_unlock(wdev);
+ continue;
+ }
+
+ if (cfg80211_is_sub_chan(&wdev->chandef, chan)) {
+ wdev_unlock(wdev);
+ return true;
+ }
+ wdev_unlock(wdev);
+ }
+
+ return false;
+}
static bool cfg80211_get_chans_dfs_available(struct wiphy *wiphy,
u32 center_freq,
diff --git a/net/wireless/core.h b/net/wireless/core.h
index be5ab8c13a39..55c64d08db31 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -438,6 +438,16 @@ unsigned int
cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy,
const struct cfg80211_chan_def *chandef);
+void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev);
+
+bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy,
+ struct ieee80211_channel *chan);
+
+bool cfg80211_beaconing_iface_active(struct wireless_dev *wdev);
+
+bool cfg80211_is_sub_chan(struct cfg80211_chan_def *chandef,
+ struct ieee80211_channel *chan);
+
static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
{
unsigned long end = jiffies;
diff --git a/net/wireless/db.txt b/net/wireless/db.txt
index 86005410a22f..499d98745d44 100644
--- a/net/wireless/db.txt
+++ b/net/wireless/db.txt
@@ -1478,9 +1478,9 @@ country VI: DFS-FCC
country VN: DFS-FCC
(2402 - 2482 @ 40), (20)
- (5170 - 5250 @ 80), (24), AUTO-BW
- (5250 - 5330 @ 80), (24), DFS, AUTO-BW
- (5490 - 5730 @ 160), (24), DFS
+ (5170 - 5250 @ 80), (24)
+ (5250 - 5330 @ 80), (24), DFS
+ (5490 - 5730 @ 80), (24), DFS
(5735 - 5835 @ 80), (30)
# 60 gHz band channels 1-4
(57240 - 65880 @ 2160), (40)
diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c
index 4c55fab9b4e4..7da0cd9c5e73 100644
--- a/net/wireless/ibss.c
+++ b/net/wireless/ibss.c
@@ -186,6 +186,7 @@ static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext)
if (!nowext)
wdev->wext.ibss.ssid_len = 0;
#endif
+ cfg80211_sched_dfs_chan_update(rdev);
}
void cfg80211_clear_ibss(struct net_device *dev, bool nowext)
diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c
index 092300b30c37..dda90a39be40 100644
--- a/net/wireless/mesh.c
+++ b/net/wireless/mesh.c
@@ -260,6 +260,7 @@ int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,
wdev->mesh_id_len = 0;
memset(&wdev->chandef, 0, sizeof(wdev->chandef));
rdev_set_qos_map(rdev, dev, NULL);
+ cfg80211_sched_dfs_chan_update(rdev);
}
return err;
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index 2bc6eaa766c7..465e0d31229d 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -743,6 +743,12 @@ bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm,
}
EXPORT_SYMBOL(cfg80211_rx_mgmt);
+void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev)
+{
+ cancel_delayed_work(&rdev->dfs_update_channels_wk);
+ queue_delayed_work(cfg80211_wq, &rdev->dfs_update_channels_wk, 0);
+}
+
void cfg80211_dfs_channels_update_work(struct work_struct *work)
{
struct delayed_work *delayed_work;
@@ -753,6 +759,8 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
struct wiphy *wiphy;
bool check_again = false;
unsigned long timeout, next_time = 0;
+ unsigned long time_dfs_update;
+ enum nl80211_radar_event radar_event;
int bandid, i;
delayed_work = container_of(work, struct delayed_work, work);
@@ -769,11 +777,27 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
for (i = 0; i < sband->n_channels; i++) {
c = &sband->channels[i];
- if (c->dfs_state != NL80211_DFS_UNAVAILABLE)
+ if (!(c->flags & IEEE80211_CHAN_RADAR))
+ continue;
+
+ if (c->dfs_state != NL80211_DFS_UNAVAILABLE &&
+ c->dfs_state != NL80211_DFS_AVAILABLE)
continue;
- timeout = c->dfs_state_entered + msecs_to_jiffies(
- IEEE80211_DFS_MIN_NOP_TIME_MS);
+ if (c->dfs_state == NL80211_DFS_UNAVAILABLE) {
+ time_dfs_update = IEEE80211_DFS_MIN_NOP_TIME_MS;
+ radar_event = NL80211_RADAR_NOP_FINISHED;
+ } else {
+ if (regulatory_pre_cac_allowed(wiphy) ||
+ cfg80211_any_wiphy_oper_chan(wiphy, c))
+ continue;
+
+ time_dfs_update = REG_PRE_CAC_EXPIRY_GRACE_MS;
+ radar_event = NL80211_RADAR_PRE_CAC_EXPIRED;
+ }
+
+ timeout = c->dfs_state_entered +
+ msecs_to_jiffies(time_dfs_update);
if (time_after_eq(jiffies, timeout)) {
c->dfs_state = NL80211_DFS_USABLE;
@@ -783,8 +807,8 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
NL80211_CHAN_NO_HT);
nl80211_radar_notify(rdev, &chandef,
- NL80211_RADAR_NOP_FINISHED,
- NULL, GFP_ATOMIC);
+ radar_event, NULL,
+ GFP_ATOMIC);
continue;
}
@@ -809,7 +833,6 @@ void cfg80211_radar_event(struct wiphy *wiphy,
gfp_t gfp)
{
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
- unsigned long timeout;
trace_cfg80211_radar_event(wiphy, chandef);
@@ -819,9 +842,7 @@ void cfg80211_radar_event(struct wiphy *wiphy,
*/
cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_UNAVAILABLE);
- timeout = msecs_to_jiffies(IEEE80211_DFS_MIN_NOP_TIME_MS);
- queue_delayed_work(cfg80211_wq, &rdev->dfs_update_channels_wk,
- timeout);
+ cfg80211_sched_dfs_chan_update(rdev);
nl80211_radar_notify(rdev, chandef, NL80211_RADAR_DETECTED, NULL, gfp);
}
@@ -850,6 +871,7 @@ void cfg80211_cac_event(struct net_device *netdev,
msecs_to_jiffies(wdev->cac_time_ms);
WARN_ON(!time_after_eq(jiffies, timeout));
cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE);
+ cfg80211_sched_dfs_chan_update(rdev);
break;
case NL80211_RADAR_CAC_ABORTED:
break;
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 050d7948dd68..26ac0a4808a0 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -3247,6 +3247,34 @@ bool regulatory_indoor_allowed(void)
return reg_is_indoor;
}
+bool regulatory_pre_cac_allowed(struct wiphy *wiphy)
+{
+ const struct ieee80211_regdomain *regd = NULL;
+ const struct ieee80211_regdomain *wiphy_regd = NULL;
+ bool pre_cac_allowed = false;
+
+ rcu_read_lock();
+
+ regd = rcu_dereference(cfg80211_regdomain);
+ wiphy_regd = rcu_dereference(wiphy->regd);
+ if (!wiphy_regd) {
+ if (regd->dfs_region == NL80211_DFS_ETSI)
+ pre_cac_allowed = true;
+
+ rcu_read_unlock();
+
+ return pre_cac_allowed;
+ }
+
+ if (regd->dfs_region == wiphy_regd->dfs_region &&
+ wiphy_regd->dfs_region == NL80211_DFS_ETSI)
+ pre_cac_allowed = true;
+
+ rcu_read_unlock();
+
+ return pre_cac_allowed;
+}
+
int __init regulatory_init(void)
{
int err = 0;
diff --git a/net/wireless/reg.h b/net/wireless/reg.h
index 9f495d76eca0..7bbe2d138d2a 100644
--- a/net/wireless/reg.h
+++ b/net/wireless/reg.h
@@ -143,4 +143,18 @@ int cfg80211_get_unii(int freq);
*/
bool regulatory_indoor_allowed(void);
+/*
+ * Grace period to timeout pre-CAC results on the dfs channels. This timeout
+ * value is used for Non-ETSI domain.
+ * TODO: May be make this timeout available through regdb?
+ */
+#define REG_PRE_CAC_EXPIRY_GRACE_MS 2000
+
+/**
+ * regulatory_pre_cac_allowed - if pre-CAC allowed in the current dfs domain
+ * @wiphy: wiphy for which pre-CAC capability is checked.
+
+ * Pre-CAC is allowed only in ETSI domain.
+ */
+bool regulatory_pre_cac_allowed(struct wiphy *wiphy);
#endif /* __NET_WIRELESS_REG_H */