diff --git a/core/SConscript.prodtest b/core/SConscript.prodtest
index 8c5e45170f..42340ef025 100644
--- a/core/SConscript.prodtest
+++ b/core/SConscript.prodtest
@@ -107,6 +107,7 @@ SOURCE_PRODTEST = [
'embed/projects/prodtest/cmd/prodtest_get_cpuid.c',
'embed/projects/prodtest/cmd/prodtest_haptic.c',
'embed/projects/prodtest/cmd/prodtest_help.c',
+ 'embed/projects/prodtest/cmd/prodtest_nfc.c',
'embed/projects/prodtest/cmd/prodtest_optiga.c',
'embed/projects/prodtest/cmd/prodtest_otp_batch.c',
'embed/projects/prodtest/cmd/prodtest_otp_variant.c',
diff --git a/core/embed/io/nfc/st25r3916b/nfc.c b/core/embed/io/nfc/st25r3916b/nfc.c
index f59ef4aaf4..4ecdffa954 100644
--- a/core/embed/io/nfc/st25r3916b/nfc.c
+++ b/core/embed/io/nfc/st25r3916b/nfc.c
@@ -16,7 +16,6 @@
#include "../rfal/include/rfal_rf.h"
#include "../rfal/include/rfal_t2t.h"
#include "../rfal/include/rfal_utils.h"
-#include "prodtest_common.h"
#include "stm32u5xx_hal.h"
diff --git a/core/embed/models/T3W1/boards/trezor_t3w1_revB.h b/core/embed/models/T3W1/boards/trezor_t3w1_revB.h
index 1317a58954..3380c3a7e7 100644
--- a/core/embed/models/T3W1/boards/trezor_t3w1_revB.h
+++ b/core/embed/models/T3W1/boards/trezor_t3w1_revB.h
@@ -160,4 +160,29 @@
#define NRF_OUT_FW_RUNNING_PORT GPIOE
#define NRF_OUT_FW_RUNNING_CLK_ENA __HAL_RCC_GPIOE_CLK_ENABLE
+#define SPI_INSTANCE_3 SPI3
+#define SPI_INSTANCE_3_PIN_AF GPIO_AF6_SPI3
+#define SPI_INSTANCE_3_CLK_EN __HAL_RCC_SPI3_CLK_ENABLE
+#define SPI_INSTANCE_3_CLK_DIS __HAL_RCC_SPI3_CLK_DISABLE
+#define SPI_INSTANCE_3_MISO_PORT GPIOB
+#define SPI_INSTANCE_3_MISO_PIN GPIO_PIN_4
+#define SPI_INSTANCE_3_MISO_CLK_EN __HAL_RCC_GPIOB_CLK_ENABLE
+#define SPI_INSTANCE_3_MOSI_PORT GPIOB
+#define SPI_INSTANCE_3_MOSI_PIN GPIO_PIN_5
+#define SPI_INSTANCE_3_MOSI_CLK_EN __HAL_RCC_GPIOB_CLK_ENABLE
+#define SPI_INSTANCE_3_SCK_PORT GPIOG
+#define SPI_INSTANCE_3_SCK_PIN GPIO_PIN_9
+#define SPI_INSTANCE_3_SCK_CLK_EN __HAL_RCC_GPIOG_CLK_ENABLE
+#define SPI_INSTANCE_3_NSS_PORT GPIOG
+#define SPI_INSTANCE_3_NSS_PIN GPIO_PIN_12
+#define SPI_INSTANCE_3_NSS_CLK_EN __HAL_RCC_GPIOG_CLK_ENABLE
+
+#define NFC_INT_PIN GPIO_PIN_10
+#define NFC_INT_PORT GPIOG
+#define NFC_INT_PIN_CLK_ENA __HAL_RCC_GPIOG_CLK_ENABLE
+#define NFC_EXTI_INTERRUPT_GPIOSEL EXTI_GPIOG
+#define NFC_EXTI_INTERRUPT_LINE EXTI_LINE_10
+#define NFC_EXTI_INTERRUPT_NUM EXTI10_IRQn
+#define NFC_EXTI_INTERRUPT_HANDLER EXTI10_IRQHandler
+
#endif // TREZOR_T3W1_REVA_H_
diff --git a/core/embed/projects/prodtest/cmd/prodtest_nfc.c b/core/embed/projects/prodtest/cmd/prodtest_nfc.c
new file mode 100644
index 0000000000..05201d5dd8
--- /dev/null
+++ b/core/embed/projects/prodtest/cmd/prodtest_nfc.c
@@ -0,0 +1,237 @@
+/*
+ * This file is part of the Trezor project, https://trezor.io/
+ *
+ * Copyright (c) SatoshiLabs
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifdef USE_NFC
+
+#include
+
+#include
+#include
+#include
+
+static bool nfc_dev_activated = false;
+static nfc_dev_info_t dev_info = {0};
+
+static void nfc_activated_callback() {
+ nfc_dev_read_info(&dev_info);
+ nfc_dev_activated = true;
+}
+
+static void nfc_write_tag_callback() {
+ nfc_dev_info_t dev_info;
+ nfc_dev_read_info(&dev_info);
+
+ nfc_dev_activated = true;
+}
+
+static void prodtest_nfc_read_card(cli_t* cli) {
+ uint32_t timeout = 0;
+ memset(&dev_info, 0, sizeof(dev_info));
+
+ if (cli_has_arg(cli, "timeout") &&
+ !cli_arg_uint32(cli, "timeout", &timeout)) {
+ cli_error_arg(cli, "Expecting timeout argument.");
+ return;
+ }
+
+ if (cli_arg_count(cli) > 1) {
+ cli_error_arg_count(cli);
+ return;
+ }
+
+ nfc_dev_activated = false;
+
+ nfc_status_t ret = nfc_init();
+ if (ret != NFC_OK) {
+ cli_error(cli, CLI_ERROR_FATAL, "NFC init failed");
+ } else {
+ cli_trace(cli, "NFC activated in reader mode for %d seconds.", timeout);
+ }
+
+ nfc_register_tech(NFC_POLLER_TECH_A | NFC_POLLER_TECH_B | NFC_POLLER_TECH_F |
+ NFC_POLLER_TECH_V);
+ nfc_register_event_callback(NFC_STATE_ACTIVATED, nfc_activated_callback);
+ nfc_activate_stm();
+
+ uint32_t tick = systick_ms();
+
+ while (!nfc_dev_activated) { // Todo, while timeout or device found
+
+ if ((systick_ms() - tick) > (timeout * 1000)) {
+ nfc_deinit();
+ cli_error(cli, CLI_ERROR_TIMEOUT, "NFC timeout");
+ return;
+ }
+
+ nfc_feed_worker();
+ }
+
+ cli_trace(cli, "NFC card detected.");
+
+ switch (dev_info.type) {
+ case NFC_DEV_TYPE_A:
+ cli_trace(cli, "NFC Type A: UID: %s", dev_info.uid);
+ break;
+ case NFC_DEV_TYPE_B:
+ cli_trace(cli, "NFC Type B: UID: %s", dev_info.uid);
+ break;
+ case NFC_DEV_TYPE_F:
+ cli_trace(cli, "NFC Type F: UID: %s", dev_info.uid);
+ break;
+ case NFC_DEV_TYPE_V:
+ cli_trace(cli, "NFC Type V: UID: %s", dev_info.uid);
+ break;
+ case NFC_DEV_TYPE_ST25TB:
+ cli_trace(cli, "NFC Type ST25TB: UID: %s", dev_info.uid);
+ break;
+ case NFC_DEV_TYPE_AP2P:
+ cli_trace(cli, "NFC Type AP2P: UID: %s", dev_info.uid);
+ break;
+ case NFC_DEV_TYPE_UNKNOWN:
+ cli_error(cli, CLI_ERROR, "NFC Type UNKNOWN");
+ break;
+
+ default:
+ cli_error(cli, CLI_ERROR, "NFC Type UNKNOWN");
+ break;
+ }
+
+ nfc_deinit();
+
+ cli_ok(cli, "");
+}
+
+static void prodtest_nfc_emulate_card(cli_t* cli) {
+ uint32_t timeout = 0;
+
+ if (cli_has_arg(cli, "timeout") &&
+ !cli_arg_uint32(cli, "timeout", &timeout)) {
+ cli_error_arg(cli, "Expecting timeout argument.");
+ return;
+ }
+
+ if (cli_arg_count(cli) > 1) {
+ cli_error_arg_count(cli);
+ return;
+ }
+
+ nfc_status_t ret = nfc_init();
+
+ if (ret != NFC_OK) {
+ cli_error(cli, CLI_ERROR_FATAL, "NFC init failed");
+ } else {
+ cli_trace(cli, "Emulation started for %d seconds", timeout);
+ }
+
+ nfc_register_tech(NFC_CARD_EMU_TECH_A);
+ nfc_activate_stm();
+
+ uint32_t tick = systick_ms();
+
+ while ((systick_ms() - tick) < (timeout * 1000)) {
+ nfc_feed_worker();
+ }
+
+ cli_trace(cli, "Emulation over");
+
+ nfc_deinit();
+
+ cli_ok(cli, "");
+}
+
+static void prodtest_nfc_write_card(cli_t* cli) {
+ uint32_t timeout = 0;
+ memset(&dev_info, 0, sizeof(dev_info));
+
+ if (cli_has_arg(cli, "timeout") &&
+ !cli_arg_uint32(cli, "timeout", &timeout)) {
+ cli_error_arg(cli, "Expecting timeout argument.");
+ return;
+ }
+
+ if (cli_arg_count(cli) > 1) {
+ cli_error_arg_count(cli);
+ return;
+ }
+
+ nfc_dev_activated = false;
+
+ nfc_status_t ret = nfc_init();
+
+ if (ret != NFC_OK) {
+ cli_error(cli, CLI_ERROR_FATAL, "NFC init failed");
+ } else {
+ cli_trace(cli, "NFC reader on put the card on the reader (timeout %d s)",
+ timeout);
+ }
+
+ nfc_register_tech(NFC_POLLER_TECH_A);
+ nfc_register_event_callback(NFC_STATE_ACTIVATED, nfc_write_tag_callback);
+ nfc_activate_stm();
+
+ uint32_t tick = systick_ms();
+
+ while (!nfc_dev_activated) { // Todo, while timeout or device found
+
+ if ((systick_ms() - tick) > timeout * 1000) {
+ nfc_deinit();
+ cli_error(cli, CLI_ERROR_TIMEOUT, "NFC timeout");
+ return;
+ }
+
+ nfc_feed_worker();
+ }
+
+ if (dev_info.type != NFC_DEV_TYPE_A) {
+ cli_error(cli, CLI_ERROR, "Only NFC type A cards supported");
+ return;
+ }
+
+ cli_trace(cli, "Writting URI to NFC tag %s", dev_info.uid);
+ nfc_def_write_ndef_uri();
+
+ nfc_deinit();
+
+ cli_ok(cli, "");
+}
+
+// clang-format off
+
+PRODTEST_CLI_CMD(
+ .name = "nfc-read-card",
+ .func = prodtest_nfc_read_card,
+ .info = "Activate NFC in reader mode",
+ .args = ""
+);
+
+PRODTEST_CLI_CMD(
+ .name = "nfc-emulate-card",
+ .func = prodtest_nfc_emulate_card,
+ .info = "Activate NFC in card emulation (CE) mode",
+ .args = ""
+);
+
+PRODTEST_CLI_CMD(
+ .name = "nfc-write-card",
+ .func = prodtest_nfc_write_card,
+ .info = "Activate NFC in reader mode and write a URI to the attached card",
+ .args = ""
+);
+
+#endif // USE_NFC
diff --git a/core/site_scons/models/T3W1/trezor_t3w1_revB.py b/core/site_scons/models/T3W1/trezor_t3w1_revB.py
index 672352b94e..0d07c6a918 100644
--- a/core/site_scons/models/T3W1/trezor_t3w1_revB.py
+++ b/core/site_scons/models/T3W1/trezor_t3w1_revB.py
@@ -91,6 +91,37 @@ def configure(
"vendor/stm32u5xx_hal_driver/Src/stm32u5xx_hal_uart_ex.c",
]
+
+ if "nfc" in features_wanted:
+ sources += ["embed/io/nfc/st25r3916b/nfc.c"]
+ sources += ["embed/io/nfc/st25r3916b/ndef.c"]
+ sources += ["embed/io/nfc/st25r3916b/card_emulation.c"]
+ sources += ["embed/io/nfc/rfal/source/st25r3916/rfal_rfst25r3916.c"]
+ sources += ["embed/io/nfc/rfal/source/rfal_analogConfig.c"]
+ sources += ["embed/io/nfc/rfal/source/rfal_nfc.c"]
+ sources += ["embed/io/nfc/rfal/source/rfal_nfca.c"]
+ sources += ["embed/io/nfc/rfal/source/rfal_nfcb.c"]
+ sources += ["embed/io/nfc/rfal/source/rfal_nfcf.c"]
+ sources += ["embed/io/nfc/rfal/source/rfal_nfcv.c"]
+ sources += ["embed/io/nfc/rfal/source/rfal_isoDep.c"]
+ sources += ["embed/io/nfc/rfal/source/rfal_nfcDep.c"]
+ sources += ["embed/io/nfc/rfal/source/rfal_st25tb.c"]
+ sources += ["embed/io/nfc/rfal/source/rfal_t1t.c"]
+ sources += ["embed/io/nfc/rfal/source/rfal_t2t.c"]
+ sources += ["embed/io/nfc/rfal/source/rfal_iso15693_2.c"]
+ sources += ["embed/io/nfc/rfal/source/rfal_crc.c"]
+ sources += ["embed/io/nfc/rfal/source/st25r3916/st25r3916.c"]
+ sources += ["embed/io/nfc/rfal/source/st25r3916/st25r3916_com.c"]
+ sources += ["embed/io/nfc/rfal/source/st25r3916/st25r3916_led.c"]
+ sources += ["embed/io/nfc/rfal/source/st25r3916/st25r3916_irq.c"]
+ paths += ["embed/io/nfc/inc/"]
+ paths += ["embed/io/nfc/st25r3916b/"]
+ paths += ["embed/io/nfc/rfal/source"]
+ paths += ["embed/io/nfc/rfal/source/st25r3916"]
+ paths += ["embed/io/nfc/rfal/include/"]
+ features_available.append("nfc")
+ defines += [("USE_NFC", "1")]
+
if "optiga" in features_wanted:
sources += ["embed/sec/optiga/stm32/optiga_hal.c"]
sources += ["embed/sec/optiga/optiga.c"]