diff --git a/drivers/staging/wfx/bh.c b/drivers/staging/wfx/bh.c
index c94c9c401a69..d321fd312d55 100644
--- a/drivers/staging/wfx/bh.c
+++ b/drivers/staging/wfx/bh.c
@@ -12,6 +12,7 @@
 #include "wfx.h"
 #include "hwio.h"
 #include "traces.h"
+#include "secure_link.h"
 #include "hif_rx.h"
 #include "hif_api_cmd.h"
 
@@ -74,7 +75,18 @@ static int rx_helper(struct wfx_dev *wdev, size_t read_len, int *is_cnf)
 	hif = (struct hif_msg *) skb->data;
 	WARN(hif->encrypted & 0x1, "unsupported encryption type");
 	if (hif->encrypted == 0x2) {
-		BUG(); // Not yet implemented
+		if (wfx_sl_decode(wdev, (void *) hif)) {
+			dev_kfree_skb(skb);
+			// If frame was a confirmation, expect trouble in next
+			// exchange. However, it is harmless to fail to decode
+			// an indication frame, so try to continue. Anyway,
+			// piggyback is probably correct.
+			return piggyback;
+		}
+		le16_to_cpus(hif->len);
+		computed_len = round_up(hif->len - sizeof(hif->len), 16)
+			       + sizeof(struct hif_sl_msg)
+			       + sizeof(struct hif_sl_tag);
 	} else {
 		le16_to_cpus(hif->len);
 		computed_len = round_up(hif->len, 2);
@@ -166,7 +178,22 @@ static void tx_helper(struct wfx_dev *wdev, struct hif_msg *hif)
 	hif->seqnum = wdev->hif.tx_seqnum;
 	wdev->hif.tx_seqnum = (wdev->hif.tx_seqnum + 1) % (HIF_COUNTER_MAX + 1);
 
-	data = hif;
+	if (wfx_is_secure_command(wdev, hif->id)) {
+		len = round_up(len - sizeof(hif->len), 16) + sizeof(hif->len)
+		      + sizeof(struct hif_sl_msg_hdr) + sizeof(struct hif_sl_tag);
+		// AES support encryption in-place. However, mac80211 access to
+		// 802.11 header after frame was sent (to get MAC addresses).
+		// So, keep origin buffer clear.
+		data = kmalloc(len, GFP_KERNEL);
+		if (!data)
+			goto end;
+		is_encrypted = true;
+		ret = wfx_sl_encode(wdev, hif, data);
+		if (ret)
+			goto end;
+	} else {
+		data = hif;
+	}
 	WARN(len > wdev->hw_caps.size_inp_ch_buf,
 	     "%s: request exceed WFx capability: %zu > %d\n", __func__,
 	     len, wdev->hw_caps.size_inp_ch_buf);
diff --git a/drivers/staging/wfx/debug.c b/drivers/staging/wfx/debug.c
index 0a328c96eaa0..f79693a4be7f 100644
--- a/drivers/staging/wfx/debug.c
+++ b/drivers/staging/wfx/debug.c
@@ -6,6 +6,7 @@
  * Copyright (c) 2010, ST-Ericsson
  */
 #include <linux/debugfs.h>
+#include <linux/crc32.h>
 
 #include "debug.h"
 #include "wfx.h"
@@ -53,6 +54,21 @@ const char *get_reg_name(unsigned long id)
 	return get_symbol(id, wfx_reg_print_map);
 }
 
+static ssize_t wfx_burn_slk_key_write(struct file *file,
+				      const char __user *user_buf,
+				      size_t count, loff_t *ppos)
+{
+	struct wfx_dev *wdev = file->private_data;
+
+	dev_info(wdev->dev, "this driver does not support secure link\n");
+	return -EINVAL;
+}
+
+static const struct file_operations wfx_burn_slk_key_fops = {
+	.open = simple_open,
+	.write = wfx_burn_slk_key_write,
+};
+
 struct dbgfs_hif_msg {
 	struct wfx_dev *wdev;
 	struct completion complete;
@@ -146,6 +162,7 @@ int wfx_debug_init(struct wfx_dev *wdev)
 	struct dentry *d;
 
 	d = debugfs_create_dir("wfx", wdev->hw->wiphy->debugfsdir);
+	debugfs_create_file("burn_slk_key", 0200, d, wdev, &wfx_burn_slk_key_fops);
 	debugfs_create_file("send_hif_msg", 0600, d, wdev, &wfx_send_hif_msg_fops);
 
 	return 0;
diff --git a/drivers/staging/wfx/hif_rx.c b/drivers/staging/wfx/hif_rx.c
index ba8ea4f3c91b..dd5f1dea4e85 100644
--- a/drivers/staging/wfx/hif_rx.c
+++ b/drivers/staging/wfx/hif_rx.c
@@ -11,6 +11,7 @@
 
 #include "hif_rx.h"
 #include "wfx.h"
+#include "secure_link.h"
 #include "hif_api_cmd.h"
 
 static int hif_generic_confirm(struct wfx_dev *wdev, struct hif_msg *hif, void *buf)
@@ -46,6 +47,8 @@ static int hif_generic_confirm(struct wfx_dev *wdev, struct hif_msg *hif, void *
 	} else {
 		wdev->hif_cmd.buf_send = NULL;
 		mutex_unlock(&wdev->hif_cmd.lock);
+		if (cmd != HIF_REQ_ID_SL_EXCHANGE_PUB_KEYS)
+			mutex_unlock(&wdev->hif_cmd.key_renew_lock);
 	}
 	return status;
 }
@@ -68,11 +71,25 @@ static int hif_startup_indication(struct wfx_dev *wdev, struct hif_msg *hif, voi
 	return 0;
 }
 
+static int hif_keys_indication(struct wfx_dev *wdev, struct hif_msg *hif, void *buf)
+{
+	struct hif_ind_sl_exchange_pub_keys *body = buf;
+
+	// Compatibility with legacy secure link
+	if (body->status == SL_PUB_KEY_EXCHANGE_STATUS_SUCCESS)
+		body->status = 0;
+	if (body->status)
+		dev_warn(wdev->dev, "secure link negociation error\n");
+	wfx_sl_check_pubkey(wdev, body->ncp_pub_key, body->ncp_pub_key_mac);
+	return 0;
+}
+
 static const struct {
 	int msg_id;
 	int (*handler)(struct wfx_dev *wdev, struct hif_msg *hif, void *buf);
 } hif_handlers[] = {
 	{ HIF_IND_ID_STARTUP,              hif_startup_indication },
+	{ HIF_IND_ID_SL_EXCHANGE_PUB_KEYS, hif_keys_indication },
 };
 
 void wfx_handle_rx(struct wfx_dev *wdev, struct sk_buff *skb)
diff --git a/drivers/staging/wfx/hif_tx.c b/drivers/staging/wfx/hif_tx.c
index 781a6e28dbad..f8ab871aa188 100644
--- a/drivers/staging/wfx/hif_tx.c
+++ b/drivers/staging/wfx/hif_tx.c
@@ -20,6 +20,7 @@ void wfx_init_hif_cmd(struct wfx_hif_cmd *hif_cmd)
 	init_completion(&hif_cmd->ready);
 	init_completion(&hif_cmd->done);
 	mutex_init(&hif_cmd->lock);
+	mutex_init(&hif_cmd->key_renew_lock);
 }
 
 static void wfx_fill_header(struct hif_msg *hif, int if_id, unsigned int cmd, size_t size)
@@ -59,6 +60,9 @@ int wfx_cmd_send(struct wfx_dev *wdev, struct hif_msg *request, void *reply, siz
 	if (wdev->chip_frozen)
 		return -ETIMEDOUT;
 
+	if (cmd != HIF_REQ_ID_SL_EXCHANGE_PUB_KEYS)
+		mutex_lock(&wdev->hif_cmd.key_renew_lock);
+
 	mutex_lock(&wdev->hif_cmd.lock);
 	WARN(wdev->hif_cmd.buf_send, "data locking error");
 
@@ -107,6 +111,8 @@ int wfx_cmd_send(struct wfx_dev *wdev, struct hif_msg *request, void *reply, siz
 			 "WSM request %s%s%s (%#.2x) on vif %d returned status %d\n",
 			 get_hif_name(cmd), mib_sep, mib_name, cmd, vif, ret);
 
+	if (cmd != HIF_REQ_ID_SL_EXCHANGE_PUB_KEYS)
+		mutex_unlock(&wdev->hif_cmd.key_renew_lock);
 	return ret;
 }
 
diff --git a/drivers/staging/wfx/hif_tx.h b/drivers/staging/wfx/hif_tx.h
index 31f2a02c8466..6f2ea2f3a77d 100644
--- a/drivers/staging/wfx/hif_tx.h
+++ b/drivers/staging/wfx/hif_tx.h
@@ -23,6 +23,7 @@ struct wfx_scan_params {
 
 struct wfx_hif_cmd {
 	struct mutex      lock;
+	struct mutex      key_renew_lock;
 	struct completion ready;
 	struct completion done;
 	bool              async;
diff --git a/drivers/staging/wfx/main.c b/drivers/staging/wfx/main.c
index 8973eeb60eb8..0cfd6b2ec8d1 100644
--- a/drivers/staging/wfx/main.c
+++ b/drivers/staging/wfx/main.c
@@ -27,6 +27,7 @@
 #include "bh.h"
 #include "sta.h"
 #include "debug.h"
+#include "secure_link.h"
 #include "hif_api_cmd.h"
 #include "wfx_version.h"
 
@@ -39,6 +40,10 @@ static int gpio_wakeup = -2;
 module_param(gpio_wakeup, int, 0644);
 MODULE_PARM_DESC(gpio_wakeup, "gpio number for wakeup. -1 for none.");
 
+static char *slk_key;
+module_param(slk_key, charp, 0600);
+MODULE_PARM_DESC(slk_key, "secret key for secure link (expect 64 hexdecimal digits).");
+
 static const struct ieee80211_ops wfx_ops = {
 	.start			= wfx_start,
 	.stop			= wfx_stop,
@@ -84,6 +89,29 @@ struct gpio_desc *wfx_get_gpio(struct device *dev, int override, const char *lab
 	return ret;
 }
 
+static void wfx_fill_sl_key(struct device *dev, struct wfx_platform_data *pdata)
+{
+	const char *ascii_key = NULL;
+	int ret = 0;
+
+	if (slk_key)
+		ascii_key = slk_key;
+	if (!ascii_key)
+		ret = of_property_read_string(dev->of_node, "slk_key", &ascii_key);
+	if (ret == -EILSEQ || ret == -ENODATA)
+		dev_err(dev, "ignoring malformatted key from DT\n");
+	if (!ascii_key)
+		return;
+
+	ret = hex2bin(pdata->slk_key, ascii_key, sizeof(pdata->slk_key));
+	if (ret) {
+		dev_err(dev, "ignoring malformatted key: %s\n", ascii_key);
+		memset(pdata->slk_key, 0, sizeof(pdata->slk_key));
+		return;
+	}
+	dev_err(dev, "secure link is not supported by this driver, ignoring provided key\n");
+}
+
 struct wfx_dev *wfx_init_common(struct device *dev,
 				const struct wfx_platform_data *pdata,
 				const struct hwbus_ops *hwbus_ops,
@@ -113,6 +141,7 @@ struct wfx_dev *wfx_init_common(struct device *dev,
 	wdev->hwbus_ops = hwbus_ops;
 	wdev->hwbus_priv = hwbus_priv;
 	memcpy(&wdev->pdata, pdata, sizeof(*pdata));
+	wfx_fill_sl_key(dev, &wdev->pdata);
 
 	init_completion(&wdev->firmware_ready);
 	wfx_init_hif_cmd(&wdev->hif_cmd);
@@ -167,6 +196,12 @@ int wfx_probe(struct wfx_dev *wdev)
 		goto err1;
 	}
 
+	err = wfx_sl_init(wdev);
+	if (err && wdev->hw_caps.capabilities.link_mode == SEC_LINK_ENFORCED) {
+		dev_err(wdev->dev, "chip require secure_link, but can't negociate it\n");
+		goto err1;
+	}
+
 	for (i = 0; i < ARRAY_SIZE(wdev->addresses); i++) {
 		eth_zero_addr(wdev->addresses[i].addr);
 		macaddr = of_get_mac_address(wdev->dev->of_node);
@@ -198,6 +233,7 @@ int wfx_probe(struct wfx_dev *wdev)
 void wfx_release(struct wfx_dev *wdev)
 {
 	wfx_bh_unregister(wdev);
+	wfx_sl_deinit(wdev);
 }
 
 static int __init wfx_core_init(void)
diff --git a/drivers/staging/wfx/main.h b/drivers/staging/wfx/main.h
index f7c65999a493..2c9c215455ce 100644
--- a/drivers/staging/wfx/main.h
+++ b/drivers/staging/wfx/main.h
@@ -14,12 +14,14 @@
 #include <linux/gpio/consumer.h>
 
 #include "bus.h"
+#include "hif_api_general.h"
 
 struct wfx_dev;
 
 struct wfx_platform_data {
 	/* Keyset and ".sec" extention will appended to this string */
 	const char *file_fw;
+	unsigned char slk_key[API_KEY_VALUE_SIZE];
 	struct gpio_desc *gpio_wakeup;
 	/*
 	 * if true HIF D_out is sampled on the rising edge of the clock
diff --git a/drivers/staging/wfx/secure_link.h b/drivers/staging/wfx/secure_link.h
new file mode 100644
index 000000000000..e2da1c73c760
--- /dev/null
+++ b/drivers/staging/wfx/secure_link.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2019, Silicon Laboratories, Inc.
+ */
+#ifndef WFX_SECURE_LINK_H
+#define WFX_SECURE_LINK_H
+
+#include "hif_api_general.h"
+
+struct wfx_dev;
+
+
+struct sl_context {
+};
+
+static inline bool wfx_is_secure_command(struct wfx_dev *wdev, int cmd_id)
+{
+	return false;
+}
+
+static inline int wfx_sl_decode(struct wfx_dev *wdev, struct hif_sl_msg *m)
+{
+	return -EIO;
+}
+
+static inline int wfx_sl_encode(struct wfx_dev *wdev, struct hif_msg *input, struct hif_sl_msg *output)
+{
+	return -EIO;
+}
+
+static inline int wfx_sl_check_pubkey(struct wfx_dev *wdev, uint8_t *ncp_pubkey, uint8_t *ncp_pubmac)
+{
+	return -EIO;
+}
+
+static inline int wfx_sl_init(struct wfx_dev *wdev)
+{
+	return -EIO;
+}
+
+static inline void wfx_sl_deinit(struct wfx_dev *wdev)
+{
+}
+
+
+#endif
diff --git a/drivers/staging/wfx/wfx.h b/drivers/staging/wfx/wfx.h
index bf9de11f8896..7adb5bf67e90 100644
--- a/drivers/staging/wfx/wfx.h
+++ b/drivers/staging/wfx/wfx.h
@@ -15,6 +15,7 @@
 
 #include "bh.h"
 #include "main.h"
+#include "secure_link.h"
 #include "hif_tx.h"
 #include "hif_api_general.h"
 
@@ -33,6 +34,7 @@ struct wfx_dev {
 	struct completion	firmware_ready;
 	struct hif_ind_startup	hw_caps;
 	struct wfx_hif		hif;
+	struct sl_context	sl;
 	int			chip_frozen;
 
 	struct wfx_hif_cmd	hif_cmd;
