diff --git a/drivers/gpu/drm/nouveau/include/nvkm/core/firmware.h b/drivers/gpu/drm/nouveau/include/nvkm/core/firmware.h
index ff0fa38aee72c03b07e60c24bf9167eea7efda8b..54da9c6bc8d57172a0d9ec8287e032dd4cfdb016 100644
--- a/drivers/gpu/drm/nouveau/include/nvkm/core/firmware.h
+++ b/drivers/gpu/drm/nouveau/include/nvkm/core/firmware.h
@@ -1,12 +1,12 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 #ifndef __NVKM_FIRMWARE_H__
 #define __NVKM_FIRMWARE_H__
-
-#include <core/device.h>
-
-int nvkm_firmware_get(struct nvkm_device *device, const char *fwname,
-		      const struct firmware **fw);
-
-void nvkm_firmware_put(const struct firmware *fw);
-
+#include <core/subdev.h>
+
+int nvkm_firmware_get_version(const struct nvkm_subdev *, const char *fwname,
+			      int min_version, int max_version,
+			      const struct firmware **);
+int nvkm_firmware_get(const struct nvkm_subdev *, const char *fwname,
+		      const struct firmware **);
+void nvkm_firmware_put(const struct firmware *);
 #endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/core/firmware.c b/drivers/gpu/drm/nouveau/nvkm/core/firmware.c
index 058ff46b5f16676ee56ab9323252109724a31595..092acdec2c39f77af23abfbb3481ac771be92374 100644
--- a/drivers/gpu/drm/nouveau/nvkm/core/firmware.c
+++ b/drivers/gpu/drm/nouveau/nvkm/core/firmware.c
@@ -24,7 +24,7 @@
 
 /**
  * nvkm_firmware_get - load firmware from the official nvidia/chip/ directory
- * @device	device that will use that firmware
+ * @subdev	subdevice that will use that firmware
  * @fwname	name of firmware file to load
  * @fw		firmware structure to load to
  *
@@ -32,9 +32,11 @@
  * Firmware files released by NVIDIA will always follow this format.
  */
 int
-nvkm_firmware_get(struct nvkm_device *device, const char *fwname,
-		  const struct firmware **fw)
+nvkm_firmware_get_version(const struct nvkm_subdev *subdev, const char *fwname,
+			  int min_version, int max_version,
+			  const struct firmware **fw)
 {
+	struct nvkm_device *device = subdev->device;
 	char f[64];
 	char cname[16];
 	int i;
@@ -48,8 +50,29 @@ nvkm_firmware_get(struct nvkm_device *device, const char *fwname,
 		cname[i] = tolower(cname[i]);
 	}
 
-	snprintf(f, sizeof(f), "nvidia/%s/%s.bin", cname, fwname);
-	return request_firmware(fw, f, device->dev);
+	for (i = max_version; i >= min_version; i--) {
+		if (i != 0)
+			snprintf(f, sizeof(f), "nvidia/%s/%s-%d.bin", cname, fwname, i);
+		else
+			snprintf(f, sizeof(f), "nvidia/%s/%s.bin", cname, fwname);
+
+		if (!firmware_request_nowarn(fw, f, device->dev)) {
+			nvkm_debug(subdev, "firmware \"%s\" loaded\n", f);
+			return i;
+		}
+
+		nvkm_debug(subdev, "firmware \"%s\" unavailable\n", f);
+	}
+
+	nvkm_error(subdev, "failed to load firmware \"%s\"", fwname);
+	return -ENOENT;
+}
+
+int
+nvkm_firmware_get(const struct nvkm_subdev *subdev, const char *fwname,
+		  const struct firmware **fw)
+{
+	return nvkm_firmware_get_version(subdev, fwname, 0, 0, fw);
 }
 
 /**
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c
index 81a13cf9a292fef896270d56e9d0eddcce127462..c578deb5867a8d0cd2495a5f9bf55d6330bfe86a 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c
@@ -2115,12 +2115,10 @@ int
 gf100_gr_ctor_fw(struct gf100_gr *gr, const char *fwname,
 		 struct gf100_gr_fuc *fuc)
 {
-	struct nvkm_subdev *subdev = &gr->base.engine.subdev;
-	struct nvkm_device *device = subdev->device;
 	const struct firmware *fw;
 	int ret;
 
-	ret = nvkm_firmware_get(device, fwname, &fw);
+	ret = nvkm_firmware_get(&gr->base.engine.subdev, fwname, &fw);
 	if (ret) {
 		ret = gf100_gr_ctor_fw_legacy(gr, fwname, fuc, ret);
 		if (ret)
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr.c b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr.c
index 75dc06557877670ad5b2529d556584514bf5ada4..dc80985cf0933a647855f470848cbe80bbe0a246 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr.c
@@ -36,7 +36,7 @@ nvkm_acr_load_firmware(const struct nvkm_subdev *subdev, const char *name,
 	void *blob;
 	int ret;
 
-	ret = nvkm_firmware_get(subdev->device, name, &fw);
+	ret = nvkm_firmware_get(subdev, name, &fw);
 	if (ret)
 		return ERR_PTR(ret);
 	if (fw->size < min_size) {
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r352.c b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r352.c
index 1df09ed6fe6d8025d16de873532e1741fa7a2498..4fd4cfe459b8ac5541eaf0eb8d1f70c3a2ab71e1 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r352.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r352.c
@@ -229,6 +229,8 @@ struct acr_r352_lsf_wpr_header {
 struct ls_ucode_img_r352 {
 	struct ls_ucode_img base;
 
+	const struct acr_r352_lsf_func *func;
+
 	struct acr_r352_lsf_wpr_header wpr_header;
 	struct acr_r352_lsf_lsb_header lsb_header;
 };
@@ -243,6 +245,7 @@ acr_r352_ls_ucode_img_load(const struct acr_r352 *acr,
 			   enum nvkm_secboot_falcon falcon_id)
 {
 	const struct nvkm_subdev *subdev = acr->base.subdev;
+	const struct acr_r352_ls_func *func = acr->func->ls_func[falcon_id];
 	struct ls_ucode_img_r352 *img;
 	int ret;
 
@@ -252,15 +255,16 @@ acr_r352_ls_ucode_img_load(const struct acr_r352 *acr,
 
 	img->base.falcon_id = falcon_id;
 
-	ret = acr->func->ls_func[falcon_id]->load(sb, &img->base);
-
-	if (ret) {
+	ret = func->load(sb, func->version_max, &img->base);
+	if (ret < 0) {
 		kfree(img->base.ucode_data);
 		kfree(img->base.sig);
 		kfree(img);
 		return ERR_PTR(ret);
 	}
 
+	img->func = func->version[ret];
+
 	/* Check that the signature size matches our expectations... */
 	if (img->base.sig_size != sizeof(img->lsb_header.signature)) {
 		nvkm_error(subdev, "invalid signature size for %s falcon!\n",
@@ -302,8 +306,7 @@ acr_r352_ls_img_fill_headers(struct acr_r352 *acr,
 	struct acr_r352_lsf_wpr_header *whdr = &img->wpr_header;
 	struct acr_r352_lsf_lsb_header *lhdr = &img->lsb_header;
 	struct ls_ucode_img_desc *desc = &_img->ucode_desc;
-	const struct acr_r352_ls_func *func =
-					    acr->func->ls_func[_img->falcon_id];
+	const struct acr_r352_lsf_func *func = img->func;
 
 	/* Fill WPR header */
 	whdr->falcon_id = _img->falcon_id;
@@ -419,8 +422,8 @@ acr_r352_ls_write_wpr(struct acr_r352 *acr, struct list_head *imgs,
 
 	/* Figure out how large we need gdesc to be. */
 	list_for_each_entry(_img, imgs, node) {
-		const struct acr_r352_ls_func *ls_func =
-					    acr->func->ls_func[_img->falcon_id];
+		struct ls_ucode_img_r352 *img = ls_ucode_img_r352(_img);
+		const struct acr_r352_lsf_func *ls_func = img->func;
 
 		max_desc_size = max(max_desc_size, ls_func->bl_desc_size);
 	}
@@ -433,8 +436,7 @@ acr_r352_ls_write_wpr(struct acr_r352 *acr, struct list_head *imgs,
 
 	list_for_each_entry(_img, imgs, node) {
 		struct ls_ucode_img_r352 *img = ls_ucode_img_r352(_img);
-		const struct acr_r352_ls_func *ls_func =
-					    acr->func->ls_func[_img->falcon_id];
+		const struct acr_r352_lsf_func *ls_func = img->func;
 
 		nvkm_gpuobj_memcpy_to(wpr_blob, pos, &img->wpr_header,
 				      sizeof(img->wpr_header));
@@ -1063,20 +1065,36 @@ acr_r352_dtor(struct nvkm_acr *_acr)
 	kfree(acr);
 }
 
+static const struct acr_r352_lsf_func
+acr_r352_ls_fecs_func_0 = {
+	.generate_bl_desc = acr_r352_generate_flcn_bl_desc,
+	.bl_desc_size = sizeof(struct acr_r352_flcn_bl_desc),
+};
+
 const struct acr_r352_ls_func
 acr_r352_ls_fecs_func = {
 	.load = acr_ls_ucode_load_fecs,
+	.version_max = 0,
+	.version = {
+		&acr_r352_ls_fecs_func_0,
+	}
+};
+
+static const struct acr_r352_lsf_func
+acr_r352_ls_gpccs_func_0 = {
 	.generate_bl_desc = acr_r352_generate_flcn_bl_desc,
 	.bl_desc_size = sizeof(struct acr_r352_flcn_bl_desc),
+	/* GPCCS will be loaded using PRI */
+	.lhdr_flags = LSF_FLAG_FORCE_PRIV_LOAD,
 };
 
 const struct acr_r352_ls_func
 acr_r352_ls_gpccs_func = {
 	.load = acr_ls_ucode_load_gpccs,
-	.generate_bl_desc = acr_r352_generate_flcn_bl_desc,
-	.bl_desc_size = sizeof(struct acr_r352_flcn_bl_desc),
-	/* GPCCS will be loaded using PRI */
-	.lhdr_flags = LSF_FLAG_FORCE_PRIV_LOAD,
+	.version_max = 0,
+	.version = {
+		&acr_r352_ls_gpccs_func_0,
+	}
 };
 
 
@@ -1150,12 +1168,20 @@ acr_r352_generate_pmu_bl_desc(const struct nvkm_acr *acr,
 	desc->argv = addr_args;
 }
 
+static const struct acr_r352_lsf_func
+acr_r352_ls_pmu_func_0 = {
+	.generate_bl_desc = acr_r352_generate_pmu_bl_desc,
+	.bl_desc_size = sizeof(struct acr_r352_pmu_bl_desc),
+};
+
 static const struct acr_r352_ls_func
 acr_r352_ls_pmu_func = {
 	.load = acr_ls_ucode_load_pmu,
-	.generate_bl_desc = acr_r352_generate_pmu_bl_desc,
-	.bl_desc_size = sizeof(struct acr_r352_pmu_bl_desc),
 	.post_run = acr_ls_pmu_post_run,
+	.version_max = 0,
+	.version = {
+		&acr_r352_ls_pmu_func_0,
+	}
 };
 
 const struct acr_r352_func
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r352.h b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r352.h
index 3d58ab87156325de7097c51d6820c01b77414ecb..e516cab849dd1915926d2117839d31c3ac9b7d57 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r352.h
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r352.h
@@ -47,24 +47,34 @@ hsf_load_header_app_size(const struct hsf_load_header *hdr, u32 app)
 }
 
 /**
- * struct acr_r352_ls_func - manages a single LS firmware
+ * struct acr_r352_lsf_func - manages a specific LS firmware version
  *
- * @load: load the external firmware into a ls_ucode_img
  * @generate_bl_desc: function called on a block of bl_desc_size to generate the
  *		      proper bootloader descriptor for this LS firmware
  * @bl_desc_size: size of the bootloader descriptor
- * @post_run: hook called right after the ACR is executed
  * @lhdr_flags: LS flags
  */
-struct acr_r352_ls_func {
-	int (*load)(const struct nvkm_secboot *, struct ls_ucode_img *);
+struct acr_r352_lsf_func {
 	void (*generate_bl_desc)(const struct nvkm_acr *,
 				 const struct ls_ucode_img *, u64, void *);
 	u32 bl_desc_size;
-	int (*post_run)(const struct nvkm_acr *, const struct nvkm_secboot *);
 	u32 lhdr_flags;
 };
 
+/**
+ * struct acr_r352_ls_func - manages a single LS falcon
+ *
+ * @load: load the external firmware into a ls_ucode_img
+ * @post_run: hook called right after the ACR is executed
+ */
+struct acr_r352_ls_func {
+	int (*load)(const struct nvkm_secboot *, int maxver,
+		    struct ls_ucode_img *);
+	int (*post_run)(const struct nvkm_acr *, const struct nvkm_secboot *);
+	int version_max;
+	const struct acr_r352_lsf_func *version[];
+};
+
 struct acr_r352;
 
 /**
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r361.c b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r361.c
index 14b36ef93628ea60a172f1079e3ec140ae81f72a..f6b2d20d7fc3dd3d098fcfbc7577149a0f7580ac 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r361.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r361.c
@@ -66,20 +66,36 @@ acr_r361_generate_hs_bl_desc(const struct hsf_load_header *hdr, void *_bl_desc,
 	bl_desc->data_size = hdr->data_size;
 }
 
+static const struct acr_r352_lsf_func
+acr_r361_ls_fecs_func_0 = {
+	.generate_bl_desc = acr_r361_generate_flcn_bl_desc,
+	.bl_desc_size = sizeof(struct acr_r361_flcn_bl_desc),
+};
+
 const struct acr_r352_ls_func
 acr_r361_ls_fecs_func = {
 	.load = acr_ls_ucode_load_fecs,
+	.version_max = 0,
+	.version = {
+		&acr_r361_ls_fecs_func_0,
+	}
+};
+
+static const struct acr_r352_lsf_func
+acr_r361_ls_gpccs_func_0 = {
 	.generate_bl_desc = acr_r361_generate_flcn_bl_desc,
 	.bl_desc_size = sizeof(struct acr_r361_flcn_bl_desc),
+	/* GPCCS will be loaded using PRI */
+	.lhdr_flags = LSF_FLAG_FORCE_PRIV_LOAD,
 };
 
 const struct acr_r352_ls_func
 acr_r361_ls_gpccs_func = {
 	.load = acr_ls_ucode_load_gpccs,
-	.generate_bl_desc = acr_r361_generate_flcn_bl_desc,
-	.bl_desc_size = sizeof(struct acr_r361_flcn_bl_desc),
-	/* GPCCS will be loaded using PRI */
-	.lhdr_flags = LSF_FLAG_FORCE_PRIV_LOAD,
+	.version_max = 0,
+	.version = {
+		&acr_r361_ls_gpccs_func_0,
+	}
 };
 
 struct acr_r361_pmu_bl_desc {
@@ -125,12 +141,20 @@ acr_r361_generate_pmu_bl_desc(const struct nvkm_acr *acr,
 	desc->argv = addr_args;
 }
 
+static const struct acr_r352_lsf_func
+acr_r361_ls_pmu_func_0 = {
+	.generate_bl_desc = acr_r361_generate_pmu_bl_desc,
+	.bl_desc_size = sizeof(struct acr_r361_pmu_bl_desc),
+};
+
 const struct acr_r352_ls_func
 acr_r361_ls_pmu_func = {
 	.load = acr_ls_ucode_load_pmu,
-	.generate_bl_desc = acr_r361_generate_pmu_bl_desc,
-	.bl_desc_size = sizeof(struct acr_r361_pmu_bl_desc),
 	.post_run = acr_ls_pmu_post_run,
+	.version_max = 0,
+	.version = {
+		&acr_r361_ls_pmu_func_0,
+	}
 };
 
 static void
@@ -164,12 +188,20 @@ acr_r361_generate_sec2_bl_desc(const struct nvkm_acr *acr,
 	desc->argv = 0x01000000;
 }
 
-const struct acr_r352_ls_func
-acr_r361_ls_sec2_func = {
-	.load = acr_ls_ucode_load_sec2,
+const struct acr_r352_lsf_func
+acr_r361_ls_sec2_func_0 = {
 	.generate_bl_desc = acr_r361_generate_sec2_bl_desc,
 	.bl_desc_size = sizeof(struct acr_r361_pmu_bl_desc),
+};
+
+static const struct acr_r352_ls_func
+acr_r361_ls_sec2_func = {
+	.load = acr_ls_ucode_load_sec2,
 	.post_run = acr_ls_sec2_post_run,
+	.version_max = 0,
+	.version = {
+		&acr_r361_ls_sec2_func_0,
+	}
 };
 
 
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r361.h b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r361.h
index f9f978daadb96270635457b89affe06b87455a9b..38dec93779c886ac5e5103bfdae46d37796f8aad 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r361.h
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r361.h
@@ -67,6 +67,5 @@ void acr_r361_generate_hs_bl_desc(const struct hsf_load_header *, void *, u64);
 extern const struct acr_r352_ls_func acr_r361_ls_fecs_func;
 extern const struct acr_r352_ls_func acr_r361_ls_gpccs_func;
 extern const struct acr_r352_ls_func acr_r361_ls_pmu_func;
-extern const struct acr_r352_ls_func acr_r361_ls_sec2_func;
-
+extern const struct acr_r352_lsf_func acr_r361_ls_sec2_func_0;
 #endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r367.c b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r367.c
index 978ad079036705cbb10b2b87fafa9507dc79317c..472ced29da7e5f01053d9e61b16a73bc9ebc1d84 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r367.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r367.c
@@ -22,6 +22,7 @@
 
 #include "acr_r367.h"
 #include "acr_r361.h"
+#include "acr_r370.h"
 
 #include <core/gpuobj.h>
 
@@ -100,6 +101,8 @@ struct acr_r367_lsf_wpr_header {
 struct ls_ucode_img_r367 {
 	struct ls_ucode_img base;
 
+	const struct acr_r352_lsf_func *func;
+
 	struct acr_r367_lsf_wpr_header wpr_header;
 	struct acr_r367_lsf_lsb_header lsb_header;
 };
@@ -111,6 +114,7 @@ acr_r367_ls_ucode_img_load(const struct acr_r352 *acr,
 			   enum nvkm_secboot_falcon falcon_id)
 {
 	const struct nvkm_subdev *subdev = acr->base.subdev;
+	const struct acr_r352_ls_func *func = acr->func->ls_func[falcon_id];
 	struct ls_ucode_img_r367 *img;
 	int ret;
 
@@ -120,14 +124,16 @@ acr_r367_ls_ucode_img_load(const struct acr_r352 *acr,
 
 	img->base.falcon_id = falcon_id;
 
-	ret = acr->func->ls_func[falcon_id]->load(sb, &img->base);
-	if (ret) {
+	ret = func->load(sb, func->version_max, &img->base);
+	if (ret < 0) {
 		kfree(img->base.ucode_data);
 		kfree(img->base.sig);
 		kfree(img);
 		return ERR_PTR(ret);
 	}
 
+	img->func = func->version[ret];
+
 	/* Check that the signature size matches our expectations... */
 	if (img->base.sig_size != sizeof(img->lsb_header.signature)) {
 		nvkm_error(subdev, "invalid signature size for %s falcon!\n",
@@ -158,8 +164,7 @@ acr_r367_ls_img_fill_headers(struct acr_r352 *acr,
 	struct acr_r367_lsf_wpr_header *whdr = &img->wpr_header;
 	struct acr_r367_lsf_lsb_header *lhdr = &img->lsb_header;
 	struct ls_ucode_img_desc *desc = &_img->ucode_desc;
-	const struct acr_r352_ls_func *func =
-					    acr->func->ls_func[_img->falcon_id];
+	const struct acr_r352_lsf_func *func = img->func;
 
 	/* Fill WPR header */
 	whdr->falcon_id = _img->falcon_id;
@@ -269,8 +274,8 @@ acr_r367_ls_write_wpr(struct acr_r352 *acr, struct list_head *imgs,
 	u8 *gdesc;
 
 	list_for_each_entry(_img, imgs, node) {
-		const struct acr_r352_ls_func *ls_func =
-					    acr->func->ls_func[_img->falcon_id];
+		struct ls_ucode_img_r367 *img = ls_ucode_img_r367(_img);
+		const struct acr_r352_lsf_func *ls_func = img->func;
 
 		max_desc_size = max(max_desc_size, ls_func->bl_desc_size);
 	}
@@ -283,8 +288,7 @@ acr_r367_ls_write_wpr(struct acr_r352 *acr, struct list_head *imgs,
 
 	list_for_each_entry(_img, imgs, node) {
 		struct ls_ucode_img_r367 *img = ls_ucode_img_r367(_img);
-		const struct acr_r352_ls_func *ls_func =
-					    acr->func->ls_func[_img->falcon_id];
+		const struct acr_r352_lsf_func *ls_func = img->func;
 
 		nvkm_gpuobj_memcpy_to(wpr_blob, pos, &img->wpr_header,
 				      sizeof(img->wpr_header));
@@ -378,6 +382,17 @@ acr_r367_fixup_hs_desc(struct acr_r352 *acr, struct nvkm_secboot *sb,
 	}
 }
 
+static const struct acr_r352_ls_func
+acr_r367_ls_sec2_func = {
+	.load = acr_ls_ucode_load_sec2,
+	.post_run = acr_ls_sec2_post_run,
+	.version_max = 1,
+	.version = {
+		&acr_r361_ls_sec2_func_0,
+		&acr_r370_ls_sec2_func_0,
+	}
+};
+
 const struct acr_r352_func
 acr_r367_func = {
 	.fixup_hs_desc = acr_r367_fixup_hs_desc,
@@ -391,7 +406,7 @@ acr_r367_func = {
 		[NVKM_SECBOOT_FALCON_FECS] = &acr_r361_ls_fecs_func,
 		[NVKM_SECBOOT_FALCON_GPCCS] = &acr_r361_ls_gpccs_func,
 		[NVKM_SECBOOT_FALCON_PMU] = &acr_r361_ls_pmu_func,
-		[NVKM_SECBOOT_FALCON_SEC2] = &acr_r361_ls_sec2_func,
+		[NVKM_SECBOOT_FALCON_SEC2] = &acr_r367_ls_sec2_func,
 	},
 };
 
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r370.c b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r370.c
index 2f890dfae7fc16cd7ad037013b42e48e2f3014dc..e821d0fd62171801047334b3344194744f3ac586 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r370.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r370.c
@@ -49,20 +49,36 @@ acr_r370_generate_flcn_bl_desc(const struct nvkm_acr *acr,
 	desc->data_size = pdesc->app_resident_data_size;
 }
 
+static const struct acr_r352_lsf_func
+acr_r370_ls_fecs_func_0 = {
+	.generate_bl_desc = acr_r370_generate_flcn_bl_desc,
+	.bl_desc_size = sizeof(struct acr_r370_flcn_bl_desc),
+};
+
 const struct acr_r352_ls_func
 acr_r370_ls_fecs_func = {
 	.load = acr_ls_ucode_load_fecs,
+	.version_max = 0,
+	.version = {
+		&acr_r370_ls_fecs_func_0,
+	}
+};
+
+static const struct acr_r352_lsf_func
+acr_r370_ls_gpccs_func_0 = {
 	.generate_bl_desc = acr_r370_generate_flcn_bl_desc,
 	.bl_desc_size = sizeof(struct acr_r370_flcn_bl_desc),
+	/* GPCCS will be loaded using PRI */
+	.lhdr_flags = LSF_FLAG_FORCE_PRIV_LOAD,
 };
 
 const struct acr_r352_ls_func
 acr_r370_ls_gpccs_func = {
 	.load = acr_ls_ucode_load_gpccs,
-	.generate_bl_desc = acr_r370_generate_flcn_bl_desc,
-	.bl_desc_size = sizeof(struct acr_r370_flcn_bl_desc),
-	/* GPCCS will be loaded using PRI */
-	.lhdr_flags = LSF_FLAG_FORCE_PRIV_LOAD,
+	.version_max = 0,
+	.version = {
+		&acr_r370_ls_gpccs_func_0,
+	}
 };
 
 static void
@@ -95,12 +111,20 @@ acr_r370_generate_sec2_bl_desc(const struct nvkm_acr *acr,
 	desc->argv = 0x01000000;
 }
 
+const struct acr_r352_lsf_func
+acr_r370_ls_sec2_func_0 = {
+	.generate_bl_desc = acr_r370_generate_sec2_bl_desc,
+	.bl_desc_size = sizeof(struct acr_r370_flcn_bl_desc),
+};
+
 const struct acr_r352_ls_func
 acr_r370_ls_sec2_func = {
 	.load = acr_ls_ucode_load_sec2,
-	.generate_bl_desc = acr_r370_generate_sec2_bl_desc,
-	.bl_desc_size = sizeof(struct acr_r370_flcn_bl_desc),
 	.post_run = acr_ls_sec2_post_run,
+	.version_max = 0,
+	.version = {
+		&acr_r370_ls_sec2_func_0,
+	}
 };
 
 void
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r370.h b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r370.h
index 3426f86a15e4766c52ed2de0514c0c91915bfe84..2efed6f995ad03fd64c895286f73985e52bda0a1 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r370.h
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r370.h
@@ -46,4 +46,5 @@ struct acr_r370_flcn_bl_desc {
 void acr_r370_generate_hs_bl_desc(const struct hsf_load_header *, void *, u64);
 extern const struct acr_r352_ls_func acr_r370_ls_fecs_func;
 extern const struct acr_r352_ls_func acr_r370_ls_gpccs_func;
+extern const struct acr_r352_lsf_func acr_r370_ls_sec2_func_0;
 #endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r375.c b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r375.c
index 7bdef93cb7aeacef382476d78b1d46812a275838..8f0647766038604635d90def01909cc5be75e378 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r375.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r375.c
@@ -54,12 +54,20 @@ acr_r375_generate_pmu_bl_desc(const struct nvkm_acr *acr,
 	desc->argv = addr_args;
 }
 
+static const struct acr_r352_lsf_func
+acr_r375_ls_pmu_func_0 = {
+	.generate_bl_desc = acr_r375_generate_pmu_bl_desc,
+	.bl_desc_size = sizeof(struct acr_r370_flcn_bl_desc),
+};
+
 const struct acr_r352_ls_func
 acr_r375_ls_pmu_func = {
 	.load = acr_ls_ucode_load_pmu,
-	.generate_bl_desc = acr_r375_generate_pmu_bl_desc,
-	.bl_desc_size = sizeof(struct acr_r370_flcn_bl_desc),
 	.post_run = acr_ls_pmu_post_run,
+	.version_max = 0,
+	.version = {
+		&acr_r375_ls_pmu_func_0,
+	}
 };
 
 const struct acr_r352_func
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/ls_ucode.h b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/ls_ucode.h
index 9b7c402594e80c16a9fe05e2f9a8147d5c2c1405..d43f906da3a7bbcb451a40f24aa27398e91134a4 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/ls_ucode.h
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/ls_ucode.h
@@ -147,11 +147,15 @@ struct fw_bl_desc {
 	u32 data_size;
 };
 
-int acr_ls_ucode_load_fecs(const struct nvkm_secboot *, struct ls_ucode_img *);
-int acr_ls_ucode_load_gpccs(const struct nvkm_secboot *, struct ls_ucode_img *);
-int acr_ls_ucode_load_pmu(const struct nvkm_secboot *, struct ls_ucode_img *);
+int acr_ls_ucode_load_fecs(const struct nvkm_secboot *, int,
+			   struct ls_ucode_img *);
+int acr_ls_ucode_load_gpccs(const struct nvkm_secboot *, int,
+			    struct ls_ucode_img *);
+int acr_ls_ucode_load_pmu(const struct nvkm_secboot *, int,
+			  struct ls_ucode_img *);
 int acr_ls_pmu_post_run(const struct nvkm_acr *, const struct nvkm_secboot *);
-int acr_ls_ucode_load_sec2(const struct nvkm_secboot *, struct ls_ucode_img *);
+int acr_ls_ucode_load_sec2(const struct nvkm_secboot *, int,
+			   struct ls_ucode_img *);
 int acr_ls_sec2_post_run(const struct nvkm_acr *, const struct nvkm_secboot *);
 
 #endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/ls_ucode_gr.c b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/ls_ucode_gr.c
index 1b0c793c0192b940103701142e5561ffb1b5263a..821d3b2bdb1fa7503785d4b74d511b96dac8c1d4 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/ls_ucode_gr.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/ls_ucode_gr.c
@@ -90,30 +90,30 @@ ls_ucode_img_build(const struct firmware *bl, const struct firmware *code,
  * blob. Also generate the corresponding ucode descriptor.
  */
 static int
-ls_ucode_img_load_gr(const struct nvkm_subdev *subdev, struct ls_ucode_img *img,
-		     const char *falcon_name)
+ls_ucode_img_load_gr(const struct nvkm_subdev *subdev, int maxver,
+		     struct ls_ucode_img *img, const char *falcon_name)
 {
 	const struct firmware *bl, *code, *data, *sig;
 	char f[64];
 	int ret;
 
 	snprintf(f, sizeof(f), "gr/%s_bl", falcon_name);
-	ret = nvkm_firmware_get(subdev->device, f, &bl);
+	ret = nvkm_firmware_get(subdev, f, &bl);
 	if (ret)
 		goto error;
 
 	snprintf(f, sizeof(f), "gr/%s_inst", falcon_name);
-	ret = nvkm_firmware_get(subdev->device, f, &code);
+	ret = nvkm_firmware_get(subdev, f, &code);
 	if (ret)
 		goto free_bl;
 
 	snprintf(f, sizeof(f), "gr/%s_data", falcon_name);
-	ret = nvkm_firmware_get(subdev->device, f, &data);
+	ret = nvkm_firmware_get(subdev, f, &data);
 	if (ret)
 		goto free_inst;
 
 	snprintf(f, sizeof(f), "gr/%s_sig", falcon_name);
-	ret = nvkm_firmware_get(subdev->device, f, &sig);
+	ret = nvkm_firmware_get(subdev, f, &sig);
 	if (ret)
 		goto free_data;
 
@@ -146,13 +146,15 @@ ls_ucode_img_load_gr(const struct nvkm_subdev *subdev, struct ls_ucode_img *img,
 }
 
 int
-acr_ls_ucode_load_fecs(const struct nvkm_secboot *sb, struct ls_ucode_img *img)
+acr_ls_ucode_load_fecs(const struct nvkm_secboot *sb, int maxver,
+		       struct ls_ucode_img *img)
 {
-	return ls_ucode_img_load_gr(&sb->subdev, img, "fecs");
+	return ls_ucode_img_load_gr(&sb->subdev, maxver, img, "fecs");
 }
 
 int
-acr_ls_ucode_load_gpccs(const struct nvkm_secboot *sb, struct ls_ucode_img *img)
+acr_ls_ucode_load_gpccs(const struct nvkm_secboot *sb, int maxver,
+			struct ls_ucode_img *img)
 {
-	return ls_ucode_img_load_gr(&sb->subdev, img, "gpccs");
+	return ls_ucode_img_load_gr(&sb->subdev, maxver, img, "gpccs");
 }
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/ls_ucode_msgqueue.c b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/ls_ucode_msgqueue.c
index 1e1f1c635cabc468f1f44cb142c929805b427952..77c13b096a67833b6b8c945554df370994d5a1d8 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/ls_ucode_msgqueue.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/secboot/ls_ucode_msgqueue.c
@@ -39,32 +39,32 @@
  */
 static int
 acr_ls_ucode_load_msgqueue(const struct nvkm_subdev *subdev, const char *name,
-			   struct ls_ucode_img *img)
+			   int maxver, struct ls_ucode_img *img)
 {
 	const struct firmware *image, *desc, *sig;
 	char f[64];
-	int ret;
+	int ver, ret;
 
 	snprintf(f, sizeof(f), "%s/image", name);
-	ret = nvkm_firmware_get(subdev->device, f, &image);
-	if (ret)
-		return ret;
+	ver = nvkm_firmware_get_version(subdev, f, 0, maxver, &image);
+	if (ver < 0)
+		return ver;
 	img->ucode_data = kmemdup(image->data, image->size, GFP_KERNEL);
 	nvkm_firmware_put(image);
 	if (!img->ucode_data)
 		return -ENOMEM;
 
 	snprintf(f, sizeof(f), "%s/desc", name);
-	ret = nvkm_firmware_get(subdev->device, f, &desc);
-	if (ret)
+	ret = nvkm_firmware_get_version(subdev, f, ver, ver, &desc);
+	if (ret < 0)
 		return ret;
 	memcpy(&img->ucode_desc, desc->data, sizeof(img->ucode_desc));
 	img->ucode_size = ALIGN(img->ucode_desc.app_start_offset + img->ucode_desc.app_size, 256);
 	nvkm_firmware_put(desc);
 
 	snprintf(f, sizeof(f), "%s/sig", name);
-	ret = nvkm_firmware_get(subdev->device, f, &sig);
-	if (ret)
+	ret = nvkm_firmware_get_version(subdev, f, ver, ver, &sig);
+	if (ret < 0)
 		return ret;
 	img->sig_size = sig->size;
 	img->sig = kmemdup(sig->data, sig->size, GFP_KERNEL);
@@ -72,7 +72,7 @@ acr_ls_ucode_load_msgqueue(const struct nvkm_subdev *subdev, const char *name,
 	if (!img->sig)
 		return -ENOMEM;
 
-	return 0;
+	return ver;
 }
 
 static int
@@ -99,12 +99,13 @@ acr_ls_msgqueue_post_run(struct nvkm_msgqueue *queue,
 }
 
 int
-acr_ls_ucode_load_pmu(const struct nvkm_secboot *sb, struct ls_ucode_img *img)
+acr_ls_ucode_load_pmu(const struct nvkm_secboot *sb, int maxver,
+		      struct ls_ucode_img *img)
 {
 	struct nvkm_pmu *pmu = sb->subdev.device->pmu;
 	int ret;
 
-	ret = acr_ls_ucode_load_msgqueue(&sb->subdev, "pmu", img);
+	ret = acr_ls_ucode_load_msgqueue(&sb->subdev, "pmu", maxver, img);
 	if (ret)
 		return ret;
 
@@ -136,14 +137,15 @@ acr_ls_pmu_post_run(const struct nvkm_acr *acr, const struct nvkm_secboot *sb)
 }
 
 int
-acr_ls_ucode_load_sec2(const struct nvkm_secboot *sb, struct ls_ucode_img *img)
+acr_ls_ucode_load_sec2(const struct nvkm_secboot *sb, int maxver,
+		       struct ls_ucode_img *img)
 {
 	struct nvkm_sec2 *sec = sb->subdev.device->sec2;
-	int ret;
+	int ver, ret;
 
-	ret = acr_ls_ucode_load_msgqueue(&sb->subdev, "sec2", img);
-	if (ret)
-		return ret;
+	ver = acr_ls_ucode_load_msgqueue(&sb->subdev, "sec2", maxver, img);
+	if (ver < 0)
+		return ver;
 
 	/* Allocate the PMU queue corresponding to the FW version */
 	ret = nvkm_msgqueue_new(img->ucode_desc.app_version, sec->falcon,
@@ -151,7 +153,7 @@ acr_ls_ucode_load_sec2(const struct nvkm_secboot *sb, struct ls_ucode_img *img)
 	if (ret)
 		return ret;
 
-	return 0;
+	return ver;
 }
 
 int