return err;
 }
 
+static int brcmf_set_sae_password(struct brcmf_if *ifp, const u8 *pwd_data,
+                                 u16 pwd_len)
+{
+       struct brcmf_pub *drvr = ifp->drvr;
+       struct brcmf_wsec_sae_pwd_le sae_pwd;
+       int err;
+
+       if (pwd_len > BRCMF_WSEC_MAX_SAE_PASSWORD_LEN) {
+               bphy_err(drvr, "sae_password must be less than %d\n",
+                        BRCMF_WSEC_MAX_SAE_PASSWORD_LEN);
+               return -EINVAL;
+       }
+
+       sae_pwd.key_len = cpu_to_le16(pwd_len);
+       memcpy(sae_pwd.key, pwd_data, pwd_len);
+
+       err = brcmf_fil_iovar_data_set(ifp, "sae_password", &sae_pwd,
+                                      sizeof(sae_pwd));
+       if (err < 0)
+               bphy_err(drvr, "failed to set SAE password in firmware (len=%u)\n",
+                        pwd_len);
+
+       return err;
+}
+
 static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason)
 {
        struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy);
                val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
        else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)
                val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
+       else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_3)
+               val = WPA3_AUTH_SAE_PSK;
        else
                val = WPA_AUTH_DISABLED;
        brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val);
                val = 1;
                brcmf_dbg(CONN, "shared key\n");
                break;
+       case NL80211_AUTHTYPE_SAE:
+               val = 3;
+               brcmf_dbg(CONN, "SAE authentication\n");
+               break;
        default:
                val = 2;
                brcmf_dbg(CONN, "automatic, auth type (%d)\n", sme->auth_type);
                                 sme->crypto.cipher_group);
                        return -EINVAL;
                }
+       } else if (val & WPA3_AUTH_SAE_PSK) {
+               switch (sme->crypto.akm_suites[0]) {
+               case WLAN_AKM_SUITE_SAE:
+                       val = WPA3_AUTH_SAE_PSK;
+                       break;
+               default:
+                       bphy_err(drvr, "invalid cipher group (%d)\n",
+                                sme->crypto.cipher_group);
+                       return -EINVAL;
+               }
        }
 
        if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X)
        brcmf_dbg(CONN, "wpa_versions 0x%x cipher_pairwise 0x%x\n",
                  sec->wpa_versions, sec->cipher_pairwise);
 
-       if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2))
+       if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2 |
+                                NL80211_WPA_VERSION_3))
                return 0;
 
        if (!(sec->cipher_pairwise &
                goto done;
        }
 
-       if (sme->crypto.psk) {
+       if (sme->crypto.sae_pwd) {
+               brcmf_dbg(INFO, "using SAE offload\n");
+               profile->use_fwsup = BRCMF_PROFILE_FWSUP_SAE;
+       }
+
+       if (sme->crypto.psk &&
+           profile->use_fwsup != BRCMF_PROFILE_FWSUP_SAE) {
                if (WARN_ON(profile->use_fwsup != BRCMF_PROFILE_FWSUP_NONE)) {
                        err = -EINVAL;
                        goto done;
                }
        }
 
-       if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_PSK) {
+       if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_PSK)
                err = brcmf_set_pmk(ifp, sme->crypto.psk,
                                    BRCMF_WSEC_MAX_PSK_LEN);
-               if (err)
+       else if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_SAE) {
+               /* clean up user-space RSNE */
+               if (brcmf_fil_iovar_data_set(ifp, "wpaie", NULL, 0)) {
+                       bphy_err(drvr, "failed to clean up user-space RSNE\n");
                        goto done;
+               }
+               err = brcmf_set_sae_password(ifp, sme->crypto.sae_pwd,
+                                            sme->crypto.sae_pwd_len);
+               if (!err && sme->crypto.psk)
+                       err = brcmf_set_pmk(ifp, sme->crypto.psk,
+                                           BRCMF_WSEC_MAX_PSK_LEN);
        }
+       if (err)
+               goto done;
 
        /* Join with specific BSSID and cached SSID
         * If SSID is zero join based on BSSID only
        if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) {
                brcmf_dbg(CONN, "Processing set ssid\n");
                memcpy(vif->profile.bssid, e->addr, ETH_ALEN);
-               if (vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_PSK)
+               if (vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_PSK &&
+                   vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_SAE)
                        return true;
 
                set_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state);
                                      NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK);
                wiphy_ext_feature_set(wiphy,
                                      NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X);
+               if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SAE))
+                       wiphy_ext_feature_set(wiphy,
+                                             NL80211_EXT_FEATURE_SAE_OFFLOAD);
        }
        wiphy->mgmt_stypes = brcmf_txrx_stypes;
        wiphy->max_remain_on_channel_duration = 5000;