diff --git a/cyw43/src/control.rs b/cyw43/src/control.rs
index a3808f56f..cd890c982 100644
--- a/cyw43/src/control.rs
+++ b/cyw43/src/control.rs
@@ -228,8 +228,8 @@ impl<'a> Control<'a> {
         self.wait_for_join(i).await
     }
 
-    /// Join an protected network with the provided ssid and passphrase.
-    pub async fn join_wpa2(&mut self, ssid: &str, passphrase: &str) -> Result<(), Error> {
+    /// Join a protected network with the provided ssid and [`PassphraseInfo`].
+    async fn join_wpa2_passphrase_info(&mut self, ssid: &str, passphrase_info: &PassphraseInfo) -> Result<(), Error> {
         self.set_iovar_u32("ampdu_ba_wsize", 8).await;
 
         self.ioctl_set_u32(134, 0, 4).await; // wsec = wpa2
@@ -239,14 +239,13 @@ impl<'a> Control<'a> {
 
         Timer::after_millis(100).await;
 
-        let mut pfi = PassphraseInfo {
-            len: passphrase.len() as _,
-            flags: 1,
-            passphrase: [0; 64],
-        };
-        pfi.passphrase[..passphrase.len()].copy_from_slice(passphrase.as_bytes());
-        self.ioctl(IoctlType::Set, IOCTL_CMD_SET_PASSPHRASE, 0, &mut pfi.to_bytes())
-            .await; // WLC_SET_WSEC_PMK
+        self.ioctl(
+            IoctlType::Set,
+            IOCTL_CMD_SET_PASSPHRASE,
+            0,
+            &mut passphrase_info.to_bytes(),
+        )
+        .await; // WLC_SET_WSEC_PMK
 
         self.ioctl_set_u32(20, 0, 1).await; // set_infra = 1
         self.ioctl_set_u32(22, 0, 0).await; // set_auth = 0 (open)
@@ -261,6 +260,28 @@ impl<'a> Control<'a> {
         self.wait_for_join(i).await
     }
 
+    /// Join a protected network with the provided ssid and passphrase.
+    pub async fn join_wpa2(&mut self, ssid: &str, passphrase: &str) -> Result<(), Error> {
+        let mut pfi = PassphraseInfo {
+            len: passphrase.len() as _,
+            flags: 1,
+            passphrase: [0; 64],
+        };
+        pfi.passphrase[..passphrase.len()].copy_from_slice(passphrase.as_bytes());
+        self.join_wpa2_passphrase_info(ssid, &pfi).await
+    }
+
+    /// Join a protected network with the provided ssid and precomputed PSK.
+    pub async fn join_wpa2_psk(&mut self, ssid: &str, psk: &[u8; 32]) -> Result<(), Error> {
+        let mut pfi = PassphraseInfo {
+            len: psk.len() as _,
+            flags: 0,
+            passphrase: [0; 64],
+        };
+        pfi.passphrase[..psk.len()].copy_from_slice(psk);
+        self.join_wpa2_passphrase_info(ssid, &pfi).await
+    }
+
     async fn wait_for_join(&mut self, i: SsidInfo) -> Result<(), Error> {
         self.events.mask.enable(&[Event::SET_SSID, Event::AUTH]);
         let mut subscriber = self.events.queue.subscriber().unwrap();