Переглянути джерело

Merge branch 'linux-4.12' of git://github.com/skeggsb/linux into drm-next

- Re-architecture of the code to handle proprietary fw, more abstracted
to support the multitude of differences that NVIDIA introduce
- Support in the said code for GP10x ACR and GR fw, giving acceleration
support \o/
- Fix for GTX 970 GPUs that are in an odd MMU configuration

* 'linux-4.12' of git://github.com/skeggsb/linux: (60 commits)
  drm/nouveau/fb/gf100-: rework ram detection
  drm/nouveau/fb/gm200: split ram implementation from gm107
  drm/nouveau/fb/gf108: split implementation from gf100
  drm/nouveau/fb/gf100-: modify constructors to allow more customisation
  drm/nouveau/kms/nv50: use drm core i2c-over-aux algorithm
  drm/nouveau/i2c/g94-: return REPLY_M value on reads
  drm/nouveau/i2c: modify aux interface to return length actually transferred
  drm/nouveau/gp10x: enable secboot and GR
  drm/nouveau/gr/gp102: initial support
  drm/nouveau/falcon: support for gp10x msgqueue
  drm/nouveau/secboot: add gp102/gp104/gp106/gp107 support
  drm/nouveau/secboot: put HS code loading code into own file
  drm/nouveau/secboot: support for r375 ACR
  drm/nouveau/secboot: support for r367 ACR
  drm/nouveau/secboot: support for r364 ACR
  drm/nouveau/secboot: workaround bug when starting SEC2 firmware
  drm/nouveau/secboot: support standard NVIDIA HS binaries
  drm/nouveau/secboot: support for unload blob bootloader
  drm/nouveau/secboot: let callers interpret return value of blobs
  drm/nouveau/secboot: support for different load and unload falcons
  ...
Dave Airlie 8 роки тому
батько
коміт
6796b129b0
80 змінених файлів з 4468 додано та 566 видалено
  1. 2 0
      drivers/gpu/drm/nouveau/include/nvif/class.h
  2. 5 2
      drivers/gpu/drm/nouveau/include/nvkm/core/device.h
  3. 47 0
      drivers/gpu/drm/nouveau/include/nvkm/core/msgqueue.h
  4. 9 0
      drivers/gpu/drm/nouveau/include/nvkm/engine/falcon.h
  5. 1 0
      drivers/gpu/drm/nouveau/include/nvkm/engine/gr.h
  6. 8 0
      drivers/gpu/drm/nouveau/include/nvkm/engine/nvdec.h
  7. 13 0
      drivers/gpu/drm/nouveau/include/nvkm/engine/sec2.h
  8. 7 0
      drivers/gpu/drm/nouveau/include/nvkm/subdev/fb.h
  9. 5 3
      drivers/gpu/drm/nouveau/include/nvkm/subdev/i2c.h
  10. 1 0
      drivers/gpu/drm/nouveau/include/nvkm/subdev/pmu.h
  11. 6 1
      drivers/gpu/drm/nouveau/include/nvkm/subdev/secboot.h
  12. 3 2
      drivers/gpu/drm/nouveau/nouveau_connector.c
  13. 3 2
      drivers/gpu/drm/nouveau/nv50_display.c
  14. 1 0
      drivers/gpu/drm/nouveau/nvkm/core/subdev.c
  15. 1 0
      drivers/gpu/drm/nouveau/nvkm/engine/Kbuild
  16. 19 2
      drivers/gpu/drm/nouveau/nvkm/engine/device/base.c
  17. 1 0
      drivers/gpu/drm/nouveau/nvkm/engine/device/priv.h
  18. 2 0
      drivers/gpu/drm/nouveau/nvkm/engine/gr/Kbuild
  19. 4 0
      drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf100.h
  20. 2 2
      drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgp100.c
  21. 98 0
      drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgp102.c
  22. 14 15
      drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c
  23. 4 0
      drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.h
  24. 4 2
      drivers/gpu/drm/nouveau/nvkm/engine/gr/gp100.c
  25. 66 0
      drivers/gpu/drm/nouveau/nvkm/engine/gr/gp102.c
  26. 2 1
      drivers/gpu/drm/nouveau/nvkm/engine/nvdec/Kbuild
  27. 59 0
      drivers/gpu/drm/nouveau/nvkm/engine/nvdec/base.c
  28. 30 0
      drivers/gpu/drm/nouveau/nvkm/engine/nvdec/gp102.c
  29. 6 0
      drivers/gpu/drm/nouveau/nvkm/engine/nvdec/priv.h
  30. 2 0
      drivers/gpu/drm/nouveau/nvkm/engine/sec2/Kbuild
  31. 101 0
      drivers/gpu/drm/nouveau/nvkm/engine/sec2/base.c
  32. 30 0
      drivers/gpu/drm/nouveau/nvkm/engine/sec2/gp102.c
  33. 9 0
      drivers/gpu/drm/nouveau/nvkm/engine/sec2/priv.h
  34. 3 0
      drivers/gpu/drm/nouveau/nvkm/falcon/Kbuild
  35. 38 2
      drivers/gpu/drm/nouveau/nvkm/falcon/base.c
  36. 553 0
      drivers/gpu/drm/nouveau/nvkm/falcon/msgqueue.c
  37. 207 0
      drivers/gpu/drm/nouveau/nvkm/falcon/msgqueue.h
  38. 323 0
      drivers/gpu/drm/nouveau/nvkm/falcon/msgqueue_0137c63d.c
  39. 263 0
      drivers/gpu/drm/nouveau/nvkm/falcon/msgqueue_0148cdec.c
  40. 104 20
      drivers/gpu/drm/nouveau/nvkm/falcon/v1.c
  41. 3 0
      drivers/gpu/drm/nouveau/nvkm/subdev/fb/Kbuild
  42. 42 0
      drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf108.c
  43. 1 1
      drivers/gpu/drm/nouveau/nvkm/subdev/fb/gm200.c
  44. 30 3
      drivers/gpu/drm/nouveau/nvkm/subdev/fb/ram.h
  45. 94 54
      drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgf100.c
  46. 62 0
      drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgf108.c
  47. 37 33
      drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgk104.c
  48. 24 1
      drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgm107.c
  49. 68 0
      drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgm200.c
  50. 14 54
      drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgp100.c
  51. 7 7
      drivers/gpu/drm/nouveau/nvkm/subdev/i2c/anx9805.c
  52. 2 2
      drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c
  53. 2 2
      drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.h
  54. 6 5
      drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxg94.c
  55. 6 5
      drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxgm200.c
  56. 3 3
      drivers/gpu/drm/nouveau/nvkm/subdev/ibus/gf100.c
  57. 3 3
      drivers/gpu/drm/nouveau/nvkm/subdev/ibus/gk104.c
  58. 13 2
      drivers/gpu/drm/nouveau/nvkm/subdev/pmu/base.c
  59. 17 2
      drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gm20b.c
  60. 6 0
      drivers/gpu/drm/nouveau/nvkm/subdev/secboot/Kbuild
  61. 6 5
      drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr.h
  62. 442 147
      drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r352.c
  63. 24 119
      drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r352.h
  64. 104 45
      drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r361.c
  65. 72 0
      drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r361.h
  66. 117 0
      drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r364.c
  67. 388 0
      drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r367.c
  68. 35 0
      drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r367.h
  69. 165 0
      drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r375.c
  70. 10 1
      drivers/gpu/drm/nouveau/nvkm/subdev/secboot/base.c
  71. 12 11
      drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gm200.c
  72. 2 1
      drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gm200.h
  73. 7 1
      drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gm20b.c
  74. 251 0
      drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gp102.c
  75. 97 0
      drivers/gpu/drm/nouveau/nvkm/subdev/secboot/hs_ucode.c
  76. 81 0
      drivers/gpu/drm/nouveau/nvkm/subdev/secboot/hs_ucode.h
  77. 7 1
      drivers/gpu/drm/nouveau/nvkm/subdev/secboot/ls_ucode.h
  78. 149 0
      drivers/gpu/drm/nouveau/nvkm/subdev/secboot/ls_ucode_msgqueue.c
  79. 2 3
      drivers/gpu/drm/nouveau/nvkm/subdev/secboot/priv.h
  80. 1 1
      drivers/gpu/drm/nouveau/nvkm/subdev/top/gk104.c

+ 2 - 0
drivers/gpu/drm/nouveau/include/nvif/class.h

@@ -125,6 +125,7 @@
 #define MAXWELL_B                                     /* cl9097.h */ 0x0000b197
 
 #define PASCAL_A                                      /* cl9097.h */ 0x0000c097
+#define PASCAL_B                                      /* cl9097.h */ 0x0000c197
 
 #define NV74_BSP                                                     0x000074b0
 
@@ -163,6 +164,7 @@
 #define MAXWELL_COMPUTE_A                                            0x0000b0c0
 #define MAXWELL_COMPUTE_B                                            0x0000b1c0
 #define PASCAL_COMPUTE_A                                             0x0000c0c0
+#define PASCAL_COMPUTE_B                                             0x0000c1c0
 
 #define NV74_CIPHER                                                  0x000074c1
 #endif

+ 5 - 2
drivers/gpu/drm/nouveau/include/nvkm/core/device.h

@@ -59,6 +59,7 @@ enum nvkm_devidx {
 	NVKM_ENGINE_NVDEC,
 	NVKM_ENGINE_PM,
 	NVKM_ENGINE_SEC,
+	NVKM_ENGINE_SEC2,
 	NVKM_ENGINE_SW,
 	NVKM_ENGINE_VIC,
 	NVKM_ENGINE_VP,
@@ -155,9 +156,10 @@ struct nvkm_device {
 	struct nvkm_engine *msppp;
 	struct nvkm_engine *msvld;
 	struct nvkm_engine *nvenc[3];
-	struct nvkm_engine *nvdec;
+	struct nvkm_nvdec *nvdec;
 	struct nvkm_pm *pm;
 	struct nvkm_engine *sec;
+	struct nvkm_sec2 *sec2;
 	struct nvkm_sw *sw;
 	struct nvkm_engine *vic;
 	struct nvkm_engine *vp;
@@ -225,9 +227,10 @@ struct nvkm_device_chip {
 	int (*msppp   )(struct nvkm_device *, int idx, struct nvkm_engine **);
 	int (*msvld   )(struct nvkm_device *, int idx, struct nvkm_engine **);
 	int (*nvenc[3])(struct nvkm_device *, int idx, struct nvkm_engine **);
-	int (*nvdec   )(struct nvkm_device *, int idx, struct nvkm_engine **);
+	int (*nvdec   )(struct nvkm_device *, int idx, struct nvkm_nvdec **);
 	int (*pm      )(struct nvkm_device *, int idx, struct nvkm_pm **);
 	int (*sec     )(struct nvkm_device *, int idx, struct nvkm_engine **);
+	int (*sec2    )(struct nvkm_device *, int idx, struct nvkm_sec2 **);
 	int (*sw      )(struct nvkm_device *, int idx, struct nvkm_sw **);
 	int (*vic     )(struct nvkm_device *, int idx, struct nvkm_engine **);
 	int (*vp      )(struct nvkm_device *, int idx, struct nvkm_engine **);

+ 47 - 0
drivers/gpu/drm/nouveau/include/nvkm/core/msgqueue.h

@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef __NVKM_CORE_MSGQUEUE_H
+#define __NVKM_CORE_MSGQUEUE_H
+
+#include <core/os.h>
+
+struct nvkm_falcon;
+struct nvkm_msgqueue;
+enum nvkm_secboot_falcon;
+
+/* Hopefully we will never have firmware arguments larger than that... */
+#define NVKM_MSGQUEUE_CMDLINE_SIZE 0x100
+
+int nvkm_msgqueue_new(u32, struct nvkm_falcon *, struct nvkm_msgqueue **);
+void nvkm_msgqueue_del(struct nvkm_msgqueue **);
+void nvkm_msgqueue_recv(struct nvkm_msgqueue *);
+int nvkm_msgqueue_reinit(struct nvkm_msgqueue *);
+
+/* useful if we run a NVIDIA-signed firmware */
+void nvkm_msgqueue_write_cmdline(struct nvkm_msgqueue *, void *);
+
+/* interface to ACR unit running on falcon (NVIDIA signed firmware) */
+int nvkm_msgqueue_acr_boot_falcon(struct nvkm_msgqueue *,
+				  enum nvkm_secboot_falcon);
+
+#endif

+ 9 - 0
drivers/gpu/drm/nouveau/include/nvkm/engine/falcon.h

@@ -10,6 +10,7 @@ enum nvkm_falcon_dmaidx {
 	FALCON_DMAIDX_PHYS_VID		= 2,
 	FALCON_DMAIDX_PHYS_SYS_COH	= 3,
 	FALCON_DMAIDX_PHYS_SYS_NCOH	= 4,
+	FALCON_SEC2_DMAIDX_UCODE	= 6,
 };
 
 struct nvkm_falcon {
@@ -19,11 +20,13 @@ struct nvkm_falcon {
 	u32 addr;
 
 	struct mutex mutex;
+	struct mutex dmem_mutex;
 	const struct nvkm_subdev *user;
 
 	u8 version;
 	u8 secret;
 	bool debug;
+	bool has_emem;
 
 	struct nvkm_memory *core;
 	bool external;
@@ -45,8 +48,14 @@ struct nvkm_falcon {
 	struct nvkm_engine engine;
 };
 
+/* This constructor must be called from the owner's oneinit() hook and
+ * *not* its constructor.  This is to ensure that DEVINIT has been
+ * completed, and that the device is correctly enabled before we touch
+ * falcon registers.
+ */
 int nvkm_falcon_v1_new(struct nvkm_subdev *owner, const char *name, u32 addr,
 		       struct nvkm_falcon **);
+
 void nvkm_falcon_del(struct nvkm_falcon **);
 int nvkm_falcon_get(struct nvkm_falcon *, const struct nvkm_subdev *);
 void nvkm_falcon_put(struct nvkm_falcon *, const struct nvkm_subdev *);

+ 1 - 0
drivers/gpu/drm/nouveau/include/nvkm/engine/gr.h

@@ -43,4 +43,5 @@ int gm107_gr_new(struct nvkm_device *, int, struct nvkm_gr **);
 int gm200_gr_new(struct nvkm_device *, int, struct nvkm_gr **);
 int gm20b_gr_new(struct nvkm_device *, int, struct nvkm_gr **);
 int gp100_gr_new(struct nvkm_device *, int, struct nvkm_gr **);
+int gp102_gr_new(struct nvkm_device *, int, struct nvkm_gr **);
 #endif

+ 8 - 0
drivers/gpu/drm/nouveau/include/nvkm/engine/nvdec.h

@@ -1,4 +1,12 @@
 #ifndef __NVKM_NVDEC_H__
 #define __NVKM_NVDEC_H__
+#define nvkm_nvdec(p) container_of((p), struct nvkm_nvdec, engine)
 #include <core/engine.h>
+
+struct nvkm_nvdec {
+	struct nvkm_engine engine;
+	struct nvkm_falcon *falcon;
+};
+
+int gp102_nvdec_new(struct nvkm_device *, int, struct nvkm_nvdec **);
 #endif

+ 13 - 0
drivers/gpu/drm/nouveau/include/nvkm/engine/sec2.h

@@ -0,0 +1,13 @@
+#ifndef __NVKM_SEC2_H__
+#define __NVKM_SEC2_H__
+#include <core/engine.h>
+
+struct nvkm_sec2 {
+	struct nvkm_engine engine;
+	struct nvkm_falcon *falcon;
+	struct nvkm_msgqueue *queue;
+	struct work_struct work;
+};
+
+int gp102_sec2_new(struct nvkm_device *, int, struct nvkm_sec2 **);
+#endif

+ 7 - 0
drivers/gpu/drm/nouveau/include/nvkm/subdev/fb.h

@@ -89,6 +89,7 @@ int gt215_fb_new(struct nvkm_device *, int, struct nvkm_fb **);
 int mcp77_fb_new(struct nvkm_device *, int, struct nvkm_fb **);
 int mcp89_fb_new(struct nvkm_device *, int, struct nvkm_fb **);
 int gf100_fb_new(struct nvkm_device *, int, struct nvkm_fb **);
+int gf108_fb_new(struct nvkm_device *, int, struct nvkm_fb **);
 int gk104_fb_new(struct nvkm_device *, int, struct nvkm_fb **);
 int gk20a_fb_new(struct nvkm_device *, int, struct nvkm_fb **);
 int gm107_fb_new(struct nvkm_device *, int, struct nvkm_fb **);
@@ -146,6 +147,12 @@ struct nvkm_ram {
 };
 
 struct nvkm_ram_func {
+	u64 upper;
+	u32 (*probe_fbp)(const struct nvkm_ram_func *, struct nvkm_device *,
+			 int fbp, int *pltcs);
+	u32 (*probe_fbp_amount)(const struct nvkm_ram_func *, u32 fbpao,
+				struct nvkm_device *, int fbp, int *pltcs);
+	u32 (*probe_fbpa_amount)(struct nvkm_device *, int fbpa);
 	void *(*dtor)(struct nvkm_ram *);
 	int (*init)(struct nvkm_ram *);
 

+ 5 - 3
drivers/gpu/drm/nouveau/include/nvkm/subdev/i2c.h

@@ -64,7 +64,7 @@ void nvkm_i2c_aux_monitor(struct nvkm_i2c_aux *, bool monitor);
 int nvkm_i2c_aux_acquire(struct nvkm_i2c_aux *);
 void nvkm_i2c_aux_release(struct nvkm_i2c_aux *);
 int nvkm_i2c_aux_xfer(struct nvkm_i2c_aux *, bool retry, u8 type,
-		      u32 addr, u8 *data, u8 size);
+		      u32 addr, u8 *data, u8 *size);
 int nvkm_i2c_aux_lnk_ctl(struct nvkm_i2c_aux *, int link_nr, int link_bw,
 			 bool enhanced_framing);
 
@@ -162,9 +162,11 @@ nvkm_probe_i2c(struct i2c_adapter *adap, u8 addr)
 static inline int
 nvkm_rdaux(struct nvkm_i2c_aux *aux, u32 addr, u8 *data, u8 size)
 {
+	const u8 xfer = size;
 	int ret = nvkm_i2c_aux_acquire(aux);
 	if (ret == 0) {
-		ret = nvkm_i2c_aux_xfer(aux, true, 9, addr, data, size);
+		ret = nvkm_i2c_aux_xfer(aux, true, 9, addr, data, &size);
+		WARN_ON(!ret && size != xfer);
 		nvkm_i2c_aux_release(aux);
 	}
 	return ret;
@@ -175,7 +177,7 @@ nvkm_wraux(struct nvkm_i2c_aux *aux, u32 addr, u8 *data, u8 size)
 {
 	int ret = nvkm_i2c_aux_acquire(aux);
 	if (ret == 0) {
-		ret = nvkm_i2c_aux_xfer(aux, true, 8, addr, data, size);
+		ret = nvkm_i2c_aux_xfer(aux, true, 8, addr, data, &size);
 		nvkm_i2c_aux_release(aux);
 	}
 	return ret;

+ 1 - 0
drivers/gpu/drm/nouveau/include/nvkm/subdev/pmu.h

@@ -7,6 +7,7 @@ struct nvkm_pmu {
 	const struct nvkm_pmu_func *func;
 	struct nvkm_subdev subdev;
 	struct nvkm_falcon *falcon;
+	struct nvkm_msgqueue *queue;
 
 	struct {
 		u32 base;

+ 6 - 1
drivers/gpu/drm/nouveau/include/nvkm/subdev/secboot.h

@@ -30,10 +30,13 @@ enum nvkm_secboot_falcon {
 	NVKM_SECBOOT_FALCON_RESERVED = 1,
 	NVKM_SECBOOT_FALCON_FECS = 2,
 	NVKM_SECBOOT_FALCON_GPCCS = 3,
-	NVKM_SECBOOT_FALCON_END = 4,
+	NVKM_SECBOOT_FALCON_SEC2 = 7,
+	NVKM_SECBOOT_FALCON_END = 8,
 	NVKM_SECBOOT_FALCON_INVALID = 0xffffffff,
 };
 
+extern const char *nvkm_secboot_falcon_name[];
+
 /**
  * @wpr_set: whether the WPR region is currently set
 */
@@ -42,6 +45,7 @@ struct nvkm_secboot {
 	struct nvkm_acr *acr;
 	struct nvkm_subdev subdev;
 	struct nvkm_falcon *boot_falcon;
+	struct nvkm_falcon *halt_falcon;
 
 	u64 wpr_addr;
 	u32 wpr_size;
@@ -55,5 +59,6 @@ int nvkm_secboot_reset(struct nvkm_secboot *, enum nvkm_secboot_falcon);
 
 int gm200_secboot_new(struct nvkm_device *, int, struct nvkm_secboot **);
 int gm20b_secboot_new(struct nvkm_device *, int, struct nvkm_secboot **);
+int gp102_secboot_new(struct nvkm_device *, int, struct nvkm_secboot **);
 
 #endif

+ 3 - 2
drivers/gpu/drm/nouveau/nouveau_connector.c

@@ -1147,6 +1147,7 @@ nouveau_connector_aux_xfer(struct drm_dp_aux *obj, struct drm_dp_aux_msg *msg)
 		container_of(obj, typeof(*nv_connector), aux);
 	struct nouveau_encoder *nv_encoder;
 	struct nvkm_i2c_aux *aux;
+	u8 size = msg->size;
 	int ret;
 
 	nv_encoder = find_encoder(&nv_connector->base, DCB_OUTPUT_DP);
@@ -1162,11 +1163,11 @@ nouveau_connector_aux_xfer(struct drm_dp_aux *obj, struct drm_dp_aux_msg *msg)
 		return ret;
 
 	ret = nvkm_i2c_aux_xfer(aux, false, msg->request, msg->address,
-				msg->buffer, msg->size);
+				msg->buffer, &size);
 	nvkm_i2c_aux_release(aux);
 	if (ret >= 0) {
 		msg->reply = ret;
-		return msg->size;
+		return size;
 	}
 
 	return ret;

+ 3 - 2
drivers/gpu/drm/nouveau/nv50_display.c

@@ -3627,7 +3627,7 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe)
 		struct nvkm_i2c_aux *aux =
 			nvkm_i2c_aux_find(i2c, dcbe->i2c_index);
 		if (aux) {
-			nv_encoder->i2c = &aux->i2c;
+			nv_encoder->i2c = &nv_connector->aux.ddc;
 			nv_encoder->aux = aux;
 		}
 
@@ -3777,6 +3777,7 @@ nv50_pior_func = {
 static int
 nv50_pior_create(struct drm_connector *connector, struct dcb_output *dcbe)
 {
+	struct nouveau_connector *nv_connector = nouveau_connector(connector);
 	struct nouveau_drm *drm = nouveau_drm(connector->dev);
 	struct nvkm_i2c *i2c = nvxx_i2c(&drm->client.device);
 	struct nvkm_i2c_bus *bus = NULL;
@@ -3794,7 +3795,7 @@ nv50_pior_create(struct drm_connector *connector, struct dcb_output *dcbe)
 		break;
 	case DCB_OUTPUT_DP:
 		aux  = nvkm_i2c_aux_find(i2c, NVKM_I2C_AUX_EXT(dcbe->extdev));
-		ddc  = aux ? &aux->i2c : NULL;
+		ddc  = aux ? &nv_connector->aux.ddc : NULL;
 		type = DRM_MODE_ENCODER_TMDS;
 		break;
 	default:

+ 1 - 0
drivers/gpu/drm/nouveau/nvkm/core/subdev.c

@@ -78,6 +78,7 @@ nvkm_subdev_name[NVKM_SUBDEV_NR] = {
 	[NVKM_ENGINE_NVDEC   ] = "nvdec",
 	[NVKM_ENGINE_PM      ] = "pm",
 	[NVKM_ENGINE_SEC     ] = "sec",
+	[NVKM_ENGINE_SEC2    ] = "sec2",
 	[NVKM_ENGINE_SW      ] = "sw",
 	[NVKM_ENGINE_VIC     ] = "vic",
 	[NVKM_ENGINE_VP      ] = "vp",

+ 1 - 0
drivers/gpu/drm/nouveau/nvkm/engine/Kbuild

@@ -18,6 +18,7 @@ include $(src)/nvkm/engine/nvenc/Kbuild
 include $(src)/nvkm/engine/nvdec/Kbuild
 include $(src)/nvkm/engine/pm/Kbuild
 include $(src)/nvkm/engine/sec/Kbuild
+include $(src)/nvkm/engine/sec2/Kbuild
 include $(src)/nvkm/engine/sw/Kbuild
 include $(src)/nvkm/engine/vic/Kbuild
 include $(src)/nvkm/engine/vp/Kbuild

+ 19 - 2
drivers/gpu/drm/nouveau/nvkm/engine/device/base.c

@@ -1379,7 +1379,7 @@ nvc1_chipset = {
 	.bus = gf100_bus_new,
 	.clk = gf100_clk_new,
 	.devinit = gf100_devinit_new,
-	.fb = gf100_fb_new,
+	.fb = gf108_fb_new,
 	.fuse = gf100_fuse_new,
 	.gpio = g94_gpio_new,
 	.i2c = g94_i2c_new,
@@ -2200,6 +2200,9 @@ nv132_chipset = {
 	.ltc = gp100_ltc_new,
 	.mc = gp100_mc_new,
 	.mmu = gf100_mmu_new,
+	.secboot = gp102_secboot_new,
+	.sec2 = gp102_sec2_new,
+	.nvdec = gp102_nvdec_new,
 	.pci = gp100_pci_new,
 	.pmu = gp102_pmu_new,
 	.timer = gk20a_timer_new,
@@ -2211,6 +2214,8 @@ nv132_chipset = {
 	.disp = gp102_disp_new,
 	.dma = gf119_dma_new,
 	.fifo = gp100_fifo_new,
+	.gr = gp102_gr_new,
+	.sw = gf100_sw_new,
 };
 
 static const struct nvkm_device_chip
@@ -2229,6 +2234,9 @@ nv134_chipset = {
 	.ltc = gp100_ltc_new,
 	.mc = gp100_mc_new,
 	.mmu = gf100_mmu_new,
+	.secboot = gp102_secboot_new,
+	.sec2 = gp102_sec2_new,
+	.nvdec = gp102_nvdec_new,
 	.pci = gp100_pci_new,
 	.pmu = gp102_pmu_new,
 	.timer = gk20a_timer_new,
@@ -2240,6 +2248,8 @@ nv134_chipset = {
 	.disp = gp102_disp_new,
 	.dma = gf119_dma_new,
 	.fifo = gp100_fifo_new,
+	.gr = gp102_gr_new,
+	.sw = gf100_sw_new,
 };
 
 static const struct nvkm_device_chip
@@ -2258,6 +2268,9 @@ nv136_chipset = {
 	.ltc = gp100_ltc_new,
 	.mc = gp100_mc_new,
 	.mmu = gf100_mmu_new,
+	.secboot = gp102_secboot_new,
+	.sec2 = gp102_sec2_new,
+	.nvdec = gp102_nvdec_new,
 	.pci = gp100_pci_new,
 	.pmu = gp102_pmu_new,
 	.timer = gk20a_timer_new,
@@ -2269,6 +2282,8 @@ nv136_chipset = {
 	.disp = gp102_disp_new,
 	.dma = gf119_dma_new,
 	.fifo = gp100_fifo_new,
+	.gr = gp102_gr_new,
+	.sw = gf100_sw_new,
 };
 
 static int
@@ -2362,9 +2377,10 @@ nvkm_device_engine(struct nvkm_device *device, int index)
 	_(NVENC0 , device->nvenc[0],  device->nvenc[0]);
 	_(NVENC1 , device->nvenc[1],  device->nvenc[1]);
 	_(NVENC2 , device->nvenc[2],  device->nvenc[2]);
-	_(NVDEC  , device->nvdec   ,  device->nvdec);
+	_(NVDEC  , device->nvdec   , &device->nvdec->engine);
 	_(PM     , device->pm      , &device->pm->engine);
 	_(SEC    , device->sec     ,  device->sec);
+	_(SEC2   , device->sec2    , &device->sec2->engine);
 	_(SW     , device->sw      , &device->sw->engine);
 	_(VIC    , device->vic     ,  device->vic);
 	_(VP     , device->vp      ,  device->vp);
@@ -2812,6 +2828,7 @@ nvkm_device_ctor(const struct nvkm_device_func *func,
 		_(NVKM_ENGINE_NVDEC   ,    nvdec);
 		_(NVKM_ENGINE_PM      ,       pm);
 		_(NVKM_ENGINE_SEC     ,      sec);
+		_(NVKM_ENGINE_SEC2    ,     sec2);
 		_(NVKM_ENGINE_SW      ,       sw);
 		_(NVKM_ENGINE_VIC     ,      vic);
 		_(NVKM_ENGINE_VP      ,       vp);

+ 1 - 0
drivers/gpu/drm/nouveau/nvkm/engine/device/priv.h

@@ -41,6 +41,7 @@
 #include <engine/nvdec.h>
 #include <engine/pm.h>
 #include <engine/sec.h>
+#include <engine/sec2.h>
 #include <engine/sw.h>
 #include <engine/vic.h>
 #include <engine/vp.h>

+ 2 - 0
drivers/gpu/drm/nouveau/nvkm/engine/gr/Kbuild

@@ -32,6 +32,7 @@ nvkm-y += nvkm/engine/gr/gm107.o
 nvkm-y += nvkm/engine/gr/gm200.o
 nvkm-y += nvkm/engine/gr/gm20b.o
 nvkm-y += nvkm/engine/gr/gp100.o
+nvkm-y += nvkm/engine/gr/gp102.o
 
 nvkm-y += nvkm/engine/gr/ctxnv40.o
 nvkm-y += nvkm/engine/gr/ctxnv50.o
@@ -50,3 +51,4 @@ nvkm-y += nvkm/engine/gr/ctxgm107.o
 nvkm-y += nvkm/engine/gr/ctxgm200.o
 nvkm-y += nvkm/engine/gr/ctxgm20b.o
 nvkm-y += nvkm/engine/gr/ctxgp100.o
+nvkm-y += nvkm/engine/gr/ctxgp102.o

+ 4 - 0
drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgf100.h

@@ -102,6 +102,10 @@ void gm200_grctx_generate_405b60(struct gf100_gr *);
 extern const struct gf100_grctx_func gm20b_grctx;
 
 extern const struct gf100_grctx_func gp100_grctx;
+void gp100_grctx_generate_main(struct gf100_gr *, struct gf100_grctx *);
+void gp100_grctx_generate_pagepool(struct gf100_grctx *);
+
+extern const struct gf100_grctx_func gp102_grctx;
 
 /* context init value lists */
 

+ 2 - 2
drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgp100.c

@@ -29,7 +29,7 @@
  * PGRAPH context implementation
  ******************************************************************************/
 
-static void
+void
 gp100_grctx_generate_pagepool(struct gf100_grctx *info)
 {
 	const struct gf100_grctx_func *grctx = info->gr->func->grctx;
@@ -123,7 +123,7 @@ gp100_grctx_generate_405b60(struct gf100_gr *gr)
 		nvkm_wr32(device, 0x405ba0 + (i * 4), gpcs[i]);
 }
 
-static void
+void
 gp100_grctx_generate_main(struct gf100_gr *gr, struct gf100_grctx *info)
 {
 	struct nvkm_device *device = gr->base.engine.subdev.device;

+ 98 - 0
drivers/gpu/drm/nouveau/nvkm/engine/gr/ctxgp102.c

@@ -0,0 +1,98 @@
+/*
+ * Copyright 2016 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+#include "ctxgf100.h"
+
+#include <subdev/fb.h>
+
+/*******************************************************************************
+ * PGRAPH context implementation
+ ******************************************************************************/
+
+static void
+gp102_grctx_generate_attrib(struct gf100_grctx *info)
+{
+	struct gf100_gr *gr = info->gr;
+	const struct gf100_grctx_func *grctx = gr->func->grctx;
+	const u32  alpha = grctx->alpha_nr;
+	const u32 attrib = grctx->attrib_nr;
+	const u32 pertpc = 0x20 * (grctx->attrib_nr_max + grctx->alpha_nr_max);
+	const u32   size = roundup(gr->tpc_total * pertpc, 0x80);
+	const u32 access = NV_MEM_ACCESS_RW;
+	const int s = 12;
+	const int b = mmio_vram(info, size, (1 << s), access);
+	const int max_batches = 0xffff;
+	u32 ao = 0;
+	u32 bo = ao + grctx->alpha_nr_max * gr->tpc_total;
+	int gpc, ppc, n = 0;
+
+	mmio_refn(info, 0x418810, 0x80000000, s, b);
+	mmio_refn(info, 0x419848, 0x10000000, s, b);
+	mmio_refn(info, 0x419c2c, 0x10000000, s, b);
+	mmio_refn(info, 0x419b00, 0x00000000, s, b);
+	mmio_wr32(info, 0x419b04, 0x80000000 | size >> 7);
+	mmio_wr32(info, 0x405830, attrib);
+	mmio_wr32(info, 0x40585c, alpha);
+	mmio_wr32(info, 0x4064c4, ((alpha / 4) << 16) | max_batches);
+
+	for (gpc = 0; gpc < gr->gpc_nr; gpc++) {
+		for (ppc = 0; ppc < gr->ppc_nr[gpc]; ppc++, n++) {
+			const u32 as =  alpha * gr->ppc_tpc_nr[gpc][ppc];
+			const u32 bs = attrib * gr->ppc_tpc_nr[gpc][ppc];
+			const u32 u = 0x418ea0 + (n * 0x04);
+			const u32 o = PPC_UNIT(gpc, ppc, 0);
+			const u32 p = GPC_UNIT(gpc, 0xc44 + (ppc * 4));
+			if (!(gr->ppc_mask[gpc] & (1 << ppc)))
+				continue;
+			mmio_wr32(info, o + 0xc0, bs);
+			mmio_wr32(info, p, bs);
+			mmio_wr32(info, o + 0xf4, bo);
+			mmio_wr32(info, o + 0xf0, bs);
+			bo += grctx->attrib_nr_max * gr->ppc_tpc_nr[gpc][ppc];
+			mmio_wr32(info, o + 0xe4, as);
+			mmio_wr32(info, o + 0xf8, ao);
+			ao += grctx->alpha_nr_max * gr->ppc_tpc_nr[gpc][ppc];
+			mmio_wr32(info, u, bs);
+		}
+	}
+
+	mmio_wr32(info, 0x4181e4, 0x00000100);
+	mmio_wr32(info, 0x41befc, 0x00000100);
+}
+
+const struct gf100_grctx_func
+gp102_grctx = {
+	.main = gp100_grctx_generate_main,
+	.unkn = gk104_grctx_generate_unkn,
+	.bundle = gm107_grctx_generate_bundle,
+	.bundle_size = 0x3000,
+	.bundle_min_gpm_fifo_depth = 0x180,
+	.bundle_token_limit = 0x900,
+	.pagepool = gp100_grctx_generate_pagepool,
+	.pagepool_size = 0x20000,
+	.attrib = gp102_grctx_generate_attrib,
+	.attrib_nr_max = 0x5d4,
+	.attrib_nr = 0x320,
+	.alpha_nr_max = 0xc00,
+	.alpha_nr = 0x800,
+};

+ 14 - 15
drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c

@@ -1647,8 +1647,18 @@ static int
 gf100_gr_oneinit(struct nvkm_gr *base)
 {
 	struct gf100_gr *gr = gf100_gr(base);
-	struct nvkm_device *device = gr->base.engine.subdev.device;
+	struct nvkm_subdev *subdev = &gr->base.engine.subdev;
+	struct nvkm_device *device = subdev->device;
 	int i, j;
+	int ret;
+
+	ret = nvkm_falcon_v1_new(subdev, "FECS", 0x409000, &gr->fecs);
+	if (ret)
+		return ret;
+
+	ret = nvkm_falcon_v1_new(subdev, "GPCCS", 0x41a000, &gr->gpccs);
+	if (ret)
+		return ret;
 
 	nvkm_pmu_pgob(device->pmu, false);
 
@@ -1856,24 +1866,13 @@ int
 gf100_gr_ctor(const struct gf100_gr_func *func, struct nvkm_device *device,
 	      int index, struct gf100_gr *gr)
 {
-	struct nvkm_subdev *subdev = &gr->base.engine.subdev;
-	int ret;
-
 	gr->func = func;
 	gr->firmware = nvkm_boolopt(device->cfgopt, "NvGrUseFW",
 				    func->fecs.ucode == NULL);
 
-	ret = nvkm_gr_ctor(&gf100_gr_, device, index,
-			   gr->firmware || func->fecs.ucode != NULL,
-			   &gr->base);
-	if (ret)
-		return ret;
-
-	ret = nvkm_falcon_v1_new(subdev, "FECS", 0x409000, &gr->fecs);
-	if (ret)
-		return ret;
-
-	return nvkm_falcon_v1_new(subdev, "GPCCS", 0x41a000, &gr->gpccs);
+	return nvkm_gr_ctor(&gf100_gr_, device, index,
+			    gr->firmware || func->fecs.ucode != NULL,
+			    &gr->base);
 }
 
 int

+ 4 - 0
drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.h

@@ -124,6 +124,7 @@ struct gf100_gr_func {
 	void (*init_gpc_mmu)(struct gf100_gr *);
 	void (*init_rop_active_fbps)(struct gf100_gr *);
 	void (*init_ppc_exceptions)(struct gf100_gr *);
+	void (*init_swdx_pes_mask)(struct gf100_gr *);
 	void (*set_hww_esr_report_mask)(struct gf100_gr *);
 	const struct gf100_gr_pack *mmio;
 	struct {
@@ -150,6 +151,9 @@ int gk20a_gr_init(struct gf100_gr *);
 int gm200_gr_init(struct gf100_gr *);
 int gm200_gr_rops(struct gf100_gr *);
 
+int gp100_gr_init(struct gf100_gr *);
+void gp100_gr_init_rop_active_fbps(struct gf100_gr *);
+
 #define gf100_gr_chan(p) container_of((p), struct gf100_gr_chan, object)
 
 struct gf100_gr_chan {

+ 4 - 2
drivers/gpu/drm/nouveau/nvkm/engine/gr/gp100.c

@@ -30,7 +30,7 @@
  * PGRAPH engine/subdev functions
  ******************************************************************************/
 
-static void
+void
 gp100_gr_init_rop_active_fbps(struct gf100_gr *gr)
 {
 	struct nvkm_device *device = gr->base.engine.subdev.device;
@@ -40,7 +40,7 @@ gp100_gr_init_rop_active_fbps(struct gf100_gr *gr)
 	nvkm_mask(device, 0x408958, 0x0000000f, fbp_count); /* crop */
 }
 
-static int
+int
 gp100_gr_init(struct gf100_gr *gr)
 {
 	struct nvkm_device *device = gr->base.engine.subdev.device;
@@ -85,6 +85,8 @@ gp100_gr_init(struct gf100_gr *gr)
 	nvkm_wr32(device, GPC_BCAST(0x033c), nvkm_rd32(device, 0x100804));
 
 	gr->func->init_rop_active_fbps(gr);
+	if (gr->func->init_swdx_pes_mask)
+		gr->func->init_swdx_pes_mask(gr);
 
 	nvkm_wr32(device, 0x400500, 0x00010001);
 	nvkm_wr32(device, 0x400100, 0xffffffff);

+ 66 - 0
drivers/gpu/drm/nouveau/nvkm/engine/gr/gp102.c

@@ -0,0 +1,66 @@
+/*
+ * Copyright 2016 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+#include "gf100.h"
+#include "ctxgf100.h"
+
+#include <nvif/class.h>
+
+static void
+gp102_gr_init_swdx_pes_mask(struct gf100_gr *gr)
+{
+	struct nvkm_device *device = gr->base.engine.subdev.device;
+	u32 mask = 0, data, gpc;
+
+	for (gpc = 0; gpc < gr->gpc_nr; gpc++) {
+		data = nvkm_rd32(device, GPC_UNIT(gpc, 0x0c50)) & 0x0000000f;
+		mask |= data << (gpc * 4);
+	}
+
+	nvkm_wr32(device, 0x4181d0, mask);
+}
+
+static const struct gf100_gr_func
+gp102_gr = {
+	.init = gp100_gr_init,
+	.init_gpc_mmu = gm200_gr_init_gpc_mmu,
+	.init_rop_active_fbps = gp100_gr_init_rop_active_fbps,
+	.init_ppc_exceptions = gk104_gr_init_ppc_exceptions,
+	.init_swdx_pes_mask = gp102_gr_init_swdx_pes_mask,
+	.rops = gm200_gr_rops,
+	.ppc_nr = 3,
+	.grctx = &gp102_grctx,
+	.sclass = {
+		{ -1, -1, FERMI_TWOD_A },
+		{ -1, -1, KEPLER_INLINE_TO_MEMORY_B },
+		{ -1, -1, PASCAL_B, &gf100_fermi },
+		{ -1, -1, PASCAL_COMPUTE_B },
+		{}
+	}
+};
+
+int
+gp102_gr_new(struct nvkm_device *device, int index, struct nvkm_gr **pgr)
+{
+	return gm200_gr_new_(&gp102_gr, device, index, pgr);
+}

+ 2 - 1
drivers/gpu/drm/nouveau/nvkm/engine/nvdec/Kbuild

@@ -1 +1,2 @@
-#nvkm-y += nvkm/engine/nvdec/base.o
+nvkm-y += nvkm/engine/nvdec/base.o
+nvkm-y += nvkm/engine/nvdec/gp102.o

+ 59 - 0
drivers/gpu/drm/nouveau/nvkm/engine/nvdec/base.c

@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#include "priv.h"
+
+#include <engine/falcon.h>
+
+static int
+nvkm_nvdec_oneinit(struct nvkm_engine *engine)
+{
+	struct nvkm_nvdec *nvdec = nvkm_nvdec(engine);
+	return nvkm_falcon_v1_new(&nvdec->engine.subdev, "NVDEC", 0x84000,
+				  &nvdec->falcon);
+}
+
+static void *
+nvkm_nvdec_dtor(struct nvkm_engine *engine)
+{
+	struct nvkm_nvdec *nvdec = nvkm_nvdec(engine);
+	nvkm_falcon_del(&nvdec->falcon);
+	return nvdec;
+}
+
+static const struct nvkm_engine_func
+nvkm_nvdec = {
+	.dtor = nvkm_nvdec_dtor,
+	.oneinit = nvkm_nvdec_oneinit,
+};
+
+int
+nvkm_nvdec_new_(struct nvkm_device *device, int index,
+		struct nvkm_nvdec **pnvdec)
+{
+	struct nvkm_nvdec *nvdec;
+
+	if (!(nvdec = *pnvdec = kzalloc(sizeof(*nvdec), GFP_KERNEL)))
+		return -ENOMEM;
+
+	return nvkm_engine_ctor(&nvkm_nvdec, device, index, true,
+				&nvdec->engine);
+};

+ 30 - 0
drivers/gpu/drm/nouveau/nvkm/engine/nvdec/gp102.c

@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "priv.h"
+
+int
+gp102_nvdec_new(struct nvkm_device *device, int index,
+		struct nvkm_nvdec **pnvdec)
+{
+	return nvkm_nvdec_new_(device, index, pnvdec);
+}

+ 6 - 0
drivers/gpu/drm/nouveau/nvkm/engine/nvdec/priv.h

@@ -0,0 +1,6 @@
+#ifndef __NVKM_NVDEC_PRIV_H__
+#define __NVKM_NVDEC_PRIV_H__
+#include <engine/nvdec.h>
+
+int nvkm_nvdec_new_(struct nvkm_device *, int, struct nvkm_nvdec **);
+#endif

+ 2 - 0
drivers/gpu/drm/nouveau/nvkm/engine/sec2/Kbuild

@@ -0,0 +1,2 @@
+nvkm-y += nvkm/engine/sec2/base.o
+nvkm-y += nvkm/engine/sec2/gp102.o

+ 101 - 0
drivers/gpu/drm/nouveau/nvkm/engine/sec2/base.c

@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#include "priv.h"
+
+#include <core/msgqueue.h>
+#include <engine/falcon.h>
+
+static void *
+nvkm_sec2_dtor(struct nvkm_engine *engine)
+{
+	struct nvkm_sec2 *sec2 = nvkm_sec2(engine);
+	nvkm_msgqueue_del(&sec2->queue);
+	nvkm_falcon_del(&sec2->falcon);
+	return sec2;
+}
+
+static void
+nvkm_sec2_intr(struct nvkm_engine *engine)
+{
+	struct nvkm_sec2 *sec2 = nvkm_sec2(engine);
+	struct nvkm_subdev *subdev = &engine->subdev;
+	struct nvkm_device *device = subdev->device;
+	u32 disp = nvkm_rd32(device, 0x8701c);
+	u32 intr = nvkm_rd32(device, 0x87008) & disp & ~(disp >> 16);
+
+	if (intr & 0x00000040) {
+		schedule_work(&sec2->work);
+		nvkm_wr32(device, 0x87004, 0x00000040);
+		intr &= ~0x00000040;
+	}
+
+	if (intr) {
+		nvkm_error(subdev, "unhandled intr %08x\n", intr);
+		nvkm_wr32(device, 0x87004, intr);
+
+	}
+}
+
+static void
+nvkm_sec2_recv(struct work_struct *work)
+{
+	struct nvkm_sec2 *sec2 = container_of(work, typeof(*sec2), work);
+	nvkm_msgqueue_recv(sec2->queue);
+}
+
+
+static int
+nvkm_sec2_oneinit(struct nvkm_engine *engine)
+{
+	struct nvkm_sec2 *sec2 = nvkm_sec2(engine);
+	return nvkm_falcon_v1_new(&sec2->engine.subdev, "SEC2", 0x87000,
+				  &sec2->falcon);
+}
+
+static int
+nvkm_sec2_fini(struct nvkm_engine *engine, bool suspend)
+{
+	struct nvkm_sec2 *sec2 = nvkm_sec2(engine);
+	flush_work(&sec2->work);
+	return 0;
+}
+
+static const struct nvkm_engine_func
+nvkm_sec2 = {
+	.dtor = nvkm_sec2_dtor,
+	.oneinit = nvkm_sec2_oneinit,
+	.fini = nvkm_sec2_fini,
+	.intr = nvkm_sec2_intr,
+};
+
+int
+nvkm_sec2_new_(struct nvkm_device *device, int index,
+	       struct nvkm_sec2 **psec2)
+{
+	struct nvkm_sec2 *sec2;
+
+	if (!(sec2 = *psec2 = kzalloc(sizeof(*sec2), GFP_KERNEL)))
+		return -ENOMEM;
+	INIT_WORK(&sec2->work, nvkm_sec2_recv);
+
+	return nvkm_engine_ctor(&nvkm_sec2, device, index, true, &sec2->engine);
+};

+ 30 - 0
drivers/gpu/drm/nouveau/nvkm/engine/sec2/gp102.c

@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "priv.h"
+
+int
+gp102_sec2_new(struct nvkm_device *device, int index,
+	       struct nvkm_sec2 **psec2)
+{
+	return nvkm_sec2_new_(device, index, psec2);
+}

+ 9 - 0
drivers/gpu/drm/nouveau/nvkm/engine/sec2/priv.h

@@ -0,0 +1,9 @@
+#ifndef __NVKM_SEC2_PRIV_H__
+#define __NVKM_SEC2_PRIV_H__
+#include <engine/sec2.h>
+
+#define nvkm_sec2(p) container_of((p), struct nvkm_sec2, engine)
+
+int nvkm_sec2_new_(struct nvkm_device *, int, struct nvkm_sec2 **);
+
+#endif

+ 3 - 0
drivers/gpu/drm/nouveau/nvkm/falcon/Kbuild

@@ -1,2 +1,5 @@
 nvkm-y += nvkm/falcon/base.o
 nvkm-y += nvkm/falcon/v1.o
+nvkm-y += nvkm/falcon/msgqueue.o
+nvkm-y += nvkm/falcon/msgqueue_0137c63d.o
+nvkm-y += nvkm/falcon/msgqueue_0148cdec.o

+ 38 - 2
drivers/gpu/drm/nouveau/nvkm/falcon/base.c

@@ -41,14 +41,22 @@ void
 nvkm_falcon_load_dmem(struct nvkm_falcon *falcon, void *data, u32 start,
 		      u32 size, u8 port)
 {
+	mutex_lock(&falcon->dmem_mutex);
+
 	falcon->func->load_dmem(falcon, data, start, size, port);
+
+	mutex_unlock(&falcon->dmem_mutex);
 }
 
 void
 nvkm_falcon_read_dmem(struct nvkm_falcon *falcon, u32 start, u32 size, u8 port,
 		      void *data)
 {
+	mutex_lock(&falcon->dmem_mutex);
+
 	falcon->func->read_dmem(falcon, start, size, port, data);
+
+	mutex_unlock(&falcon->dmem_mutex);
 }
 
 void
@@ -129,6 +137,9 @@ nvkm_falcon_clear_interrupt(struct nvkm_falcon *falcon, u32 mask)
 void
 nvkm_falcon_put(struct nvkm_falcon *falcon, const struct nvkm_subdev *user)
 {
+	if (unlikely(!falcon))
+		return;
+
 	mutex_lock(&falcon->mutex);
 	if (falcon->user == user) {
 		nvkm_debug(falcon->user, "released %s falcon\n", falcon->name);
@@ -159,6 +170,7 @@ nvkm_falcon_ctor(const struct nvkm_falcon_func *func,
 		 struct nvkm_subdev *subdev, const char *name, u32 addr,
 		 struct nvkm_falcon *falcon)
 {
+	u32 debug_reg;
 	u32 reg;
 
 	falcon->func = func;
@@ -166,6 +178,7 @@ nvkm_falcon_ctor(const struct nvkm_falcon_func *func,
 	falcon->name = name;
 	falcon->addr = addr;
 	mutex_init(&falcon->mutex);
+	mutex_init(&falcon->dmem_mutex);
 
 	reg = nvkm_falcon_rd32(falcon, 0x12c);
 	falcon->version = reg & 0xf;
@@ -177,8 +190,31 @@ nvkm_falcon_ctor(const struct nvkm_falcon_func *func,
 	falcon->code.limit = (reg & 0x1ff) << 8;
 	falcon->data.limit = (reg & 0x3fe00) >> 1;
 
-	reg = nvkm_falcon_rd32(falcon, 0xc08);
-	falcon->debug = (reg >> 20) & 0x1;
+	switch (subdev->index) {
+	case NVKM_ENGINE_GR:
+		debug_reg = 0x0;
+		break;
+	case NVKM_SUBDEV_PMU:
+		debug_reg = 0xc08;
+		break;
+	case NVKM_ENGINE_NVDEC:
+		debug_reg = 0xd00;
+		break;
+	case NVKM_ENGINE_SEC2:
+		debug_reg = 0x408;
+		falcon->has_emem = true;
+		break;
+	default:
+		nvkm_warn(subdev, "unsupported falcon %s!\n",
+			  nvkm_subdev_name[subdev->index]);
+		debug_reg = 0;
+		break;
+	}
+
+	if (debug_reg) {
+		u32 val = nvkm_falcon_rd32(falcon, debug_reg);
+		falcon->debug = (val >> 20) & 0x1;
+	}
 }
 
 void

+ 553 - 0
drivers/gpu/drm/nouveau/nvkm/falcon/msgqueue.c

@@ -0,0 +1,553 @@
+/*
+ * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "msgqueue.h"
+#include <engine/falcon.h>
+
+#include <subdev/secboot.h>
+
+
+#define HDR_SIZE sizeof(struct nvkm_msgqueue_hdr)
+#define QUEUE_ALIGNMENT 4
+/* max size of the messages we can receive */
+#define MSG_BUF_SIZE 128
+
+static int
+msg_queue_open(struct nvkm_msgqueue *priv, struct nvkm_msgqueue_queue *queue)
+{
+	struct nvkm_falcon *falcon = priv->falcon;
+
+	mutex_lock(&queue->mutex);
+
+	queue->position = nvkm_falcon_rd32(falcon, queue->tail_reg);
+
+	return 0;
+}
+
+static void
+msg_queue_close(struct nvkm_msgqueue *priv, struct nvkm_msgqueue_queue *queue,
+		bool commit)
+{
+	struct nvkm_falcon *falcon = priv->falcon;
+
+	if (commit)
+		nvkm_falcon_wr32(falcon, queue->tail_reg, queue->position);
+
+	mutex_unlock(&queue->mutex);
+}
+
+static bool
+msg_queue_empty(struct nvkm_msgqueue *priv, struct nvkm_msgqueue_queue *queue)
+{
+	struct nvkm_falcon *falcon = priv->falcon;
+	u32 head, tail;
+
+	head = nvkm_falcon_rd32(falcon, queue->head_reg);
+	tail = nvkm_falcon_rd32(falcon, queue->tail_reg);
+
+	return head == tail;
+}
+
+static int
+msg_queue_pop(struct nvkm_msgqueue *priv, struct nvkm_msgqueue_queue *queue,
+	      void *data, u32 size)
+{
+	struct nvkm_falcon *falcon = priv->falcon;
+	const struct nvkm_subdev *subdev = priv->falcon->owner;
+	u32 head, tail, available;
+
+	head = nvkm_falcon_rd32(falcon, queue->head_reg);
+	/* has the buffer looped? */
+	if (head < queue->position)
+		queue->position = queue->offset;
+
+	tail = queue->position;
+
+	available = head - tail;
+
+	if (available == 0) {
+		nvkm_warn(subdev, "no message data available\n");
+		return 0;
+	}
+
+	if (size > available) {
+		nvkm_warn(subdev, "message data smaller than read request\n");
+		size = available;
+	}
+
+	nvkm_falcon_read_dmem(priv->falcon, tail, size, 0, data);
+	queue->position += ALIGN(size, QUEUE_ALIGNMENT);
+
+	return size;
+}
+
+static int
+msg_queue_read(struct nvkm_msgqueue *priv, struct nvkm_msgqueue_queue *queue,
+	       struct nvkm_msgqueue_hdr *hdr)
+{
+	const struct nvkm_subdev *subdev = priv->falcon->owner;
+	int err;
+
+	err = msg_queue_open(priv, queue);
+	if (err) {
+		nvkm_error(subdev, "fail to open queue %d\n", queue->index);
+		return err;
+	}
+
+	if (msg_queue_empty(priv, queue)) {
+		err = 0;
+		goto close;
+	}
+
+	err = msg_queue_pop(priv, queue, hdr, HDR_SIZE);
+	if (err >= 0 && err != HDR_SIZE)
+		err = -EINVAL;
+	if (err < 0) {
+		nvkm_error(subdev, "failed to read message header: %d\n", err);
+		goto close;
+	}
+
+	if (hdr->size > MSG_BUF_SIZE) {
+		nvkm_error(subdev, "message too big (%d bytes)\n", hdr->size);
+		err = -ENOSPC;
+		goto close;
+	}
+
+	if (hdr->size > HDR_SIZE) {
+		u32 read_size = hdr->size - HDR_SIZE;
+
+		err = msg_queue_pop(priv, queue, (hdr + 1), read_size);
+		if (err >= 0 && err != read_size)
+			err = -EINVAL;
+		if (err < 0) {
+			nvkm_error(subdev, "failed to read message: %d\n", err);
+			goto close;
+		}
+	}
+
+close:
+	msg_queue_close(priv, queue, (err >= 0));
+
+	return err;
+}
+
+static bool
+cmd_queue_has_room(struct nvkm_msgqueue *priv, struct nvkm_msgqueue_queue *queue,
+		   u32 size, bool *rewind)
+{
+	struct nvkm_falcon *falcon = priv->falcon;
+	u32 head, tail, free;
+
+	size = ALIGN(size, QUEUE_ALIGNMENT);
+
+	head = nvkm_falcon_rd32(falcon, queue->head_reg);
+	tail = nvkm_falcon_rd32(falcon, queue->tail_reg);
+
+	if (head >= tail) {
+		free = queue->offset + queue->size - head;
+		free -= HDR_SIZE;
+
+		if (size > free) {
+			*rewind = true;
+			head = queue->offset;
+		}
+	}
+
+	if (head < tail)
+		free = tail - head - 1;
+
+	return size <= free;
+}
+
+static int
+cmd_queue_push(struct nvkm_msgqueue *priv, struct nvkm_msgqueue_queue *queue,
+	       void *data, u32 size)
+{
+	nvkm_falcon_load_dmem(priv->falcon, data, queue->position, size, 0);
+	queue->position += ALIGN(size, QUEUE_ALIGNMENT);
+
+	return 0;
+}
+
+/* REWIND unit is always 0x00 */
+#define MSGQUEUE_UNIT_REWIND 0x00
+
+static void
+cmd_queue_rewind(struct nvkm_msgqueue *priv, struct nvkm_msgqueue_queue *queue)
+{
+	const struct nvkm_subdev *subdev = priv->falcon->owner;
+	struct nvkm_msgqueue_hdr cmd;
+	int err;
+
+	cmd.unit_id = MSGQUEUE_UNIT_REWIND;
+	cmd.size = sizeof(cmd);
+	err = cmd_queue_push(priv, queue, &cmd, cmd.size);
+	if (err)
+		nvkm_error(subdev, "queue %d rewind failed\n", queue->index);
+	else
+		nvkm_error(subdev, "queue %d rewinded\n", queue->index);
+
+	queue->position = queue->offset;
+}
+
+static int
+cmd_queue_open(struct nvkm_msgqueue *priv, struct nvkm_msgqueue_queue *queue,
+	       u32 size)
+{
+	struct nvkm_falcon *falcon = priv->falcon;
+	const struct nvkm_subdev *subdev = priv->falcon->owner;
+	bool rewind = false;
+
+	mutex_lock(&queue->mutex);
+
+	if (!cmd_queue_has_room(priv, queue, size, &rewind)) {
+		nvkm_error(subdev, "queue full\n");
+		mutex_unlock(&queue->mutex);
+		return -EAGAIN;
+	}
+
+	queue->position = nvkm_falcon_rd32(falcon, queue->head_reg);
+
+	if (rewind)
+		cmd_queue_rewind(priv, queue);
+
+	return 0;
+}
+
+static void
+cmd_queue_close(struct nvkm_msgqueue *priv, struct nvkm_msgqueue_queue *queue,
+		bool commit)
+{
+	struct nvkm_falcon *falcon = priv->falcon;
+
+	if (commit)
+		nvkm_falcon_wr32(falcon, queue->head_reg, queue->position);
+
+	mutex_unlock(&queue->mutex);
+}
+
+static int
+cmd_write(struct nvkm_msgqueue *priv, struct nvkm_msgqueue_hdr *cmd,
+	  struct nvkm_msgqueue_queue *queue)
+{
+	const struct nvkm_subdev *subdev = priv->falcon->owner;
+	static unsigned long timeout = ~0;
+	unsigned long end_jiffies = jiffies + msecs_to_jiffies(timeout);
+	int ret = -EAGAIN;
+	bool commit = true;
+
+	while (ret == -EAGAIN && time_before(jiffies, end_jiffies))
+		ret = cmd_queue_open(priv, queue, cmd->size);
+	if (ret) {
+		nvkm_error(subdev, "pmu_queue_open_write failed\n");
+		return ret;
+	}
+
+	ret = cmd_queue_push(priv, queue, cmd, cmd->size);
+	if (ret) {
+		nvkm_error(subdev, "pmu_queue_push failed\n");
+		commit = false;
+	}
+
+	   cmd_queue_close(priv, queue, commit);
+
+	return ret;
+}
+
+static struct nvkm_msgqueue_seq *
+msgqueue_seq_acquire(struct nvkm_msgqueue *priv)
+{
+	const struct nvkm_subdev *subdev = priv->falcon->owner;
+	struct nvkm_msgqueue_seq *seq;
+	u32 index;
+
+	mutex_lock(&priv->seq_lock);
+
+	index = find_first_zero_bit(priv->seq_tbl, NVKM_MSGQUEUE_NUM_SEQUENCES);
+
+	if (index >= NVKM_MSGQUEUE_NUM_SEQUENCES) {
+		nvkm_error(subdev, "no free sequence available\n");
+		mutex_unlock(&priv->seq_lock);
+		return ERR_PTR(-EAGAIN);
+	}
+
+	set_bit(index, priv->seq_tbl);
+
+	mutex_unlock(&priv->seq_lock);
+
+	seq = &priv->seq[index];
+	seq->state = SEQ_STATE_PENDING;
+
+	return seq;
+}
+
+static void
+msgqueue_seq_release(struct nvkm_msgqueue *priv, struct nvkm_msgqueue_seq *seq)
+{
+	/* no need to acquire seq_lock since clear_bit is atomic */
+	seq->state = SEQ_STATE_FREE;
+	seq->callback = NULL;
+	seq->completion = NULL;
+	clear_bit(seq->id, priv->seq_tbl);
+}
+
+/* specifies that we want to know the command status in the answer message */
+#define CMD_FLAGS_STATUS BIT(0)
+/* specifies that we want an interrupt when the answer message is queued */
+#define CMD_FLAGS_INTR BIT(1)
+
+int
+nvkm_msgqueue_post(struct nvkm_msgqueue *priv, enum msgqueue_msg_priority prio,
+		   struct nvkm_msgqueue_hdr *cmd, nvkm_msgqueue_callback cb,
+		   struct completion *completion, bool wait_init)
+{
+	struct nvkm_msgqueue_seq *seq;
+	struct nvkm_msgqueue_queue *queue;
+	int ret;
+
+	if (wait_init && !wait_for_completion_timeout(&priv->init_done,
+					 msecs_to_jiffies(1000)))
+		return -ETIMEDOUT;
+
+	queue = priv->func->cmd_queue(priv, prio);
+	if (IS_ERR(queue))
+		return PTR_ERR(queue);
+
+	seq = msgqueue_seq_acquire(priv);
+	if (IS_ERR(seq))
+		return PTR_ERR(seq);
+
+	cmd->seq_id = seq->id;
+	cmd->ctrl_flags = CMD_FLAGS_STATUS | CMD_FLAGS_INTR;
+
+	seq->callback = cb;
+	seq->state = SEQ_STATE_USED;
+	seq->completion = completion;
+
+	ret = cmd_write(priv, cmd, queue);
+	if (ret) {
+		seq->state = SEQ_STATE_PENDING;
+		      msgqueue_seq_release(priv, seq);
+	}
+
+	return ret;
+}
+
+static int
+msgqueue_msg_handle(struct nvkm_msgqueue *priv, struct nvkm_msgqueue_hdr *hdr)
+{
+	const struct nvkm_subdev *subdev = priv->falcon->owner;
+	struct nvkm_msgqueue_seq *seq;
+
+	seq = &priv->seq[hdr->seq_id];
+	if (seq->state != SEQ_STATE_USED && seq->state != SEQ_STATE_CANCELLED) {
+		nvkm_error(subdev, "msg for unknown sequence %d", seq->id);
+		return -EINVAL;
+	}
+
+	if (seq->state == SEQ_STATE_USED) {
+		if (seq->callback)
+			seq->callback(priv, hdr);
+	}
+
+	if (seq->completion)
+		complete(seq->completion);
+
+	   msgqueue_seq_release(priv, seq);
+
+	return 0;
+}
+
+static int
+msgqueue_handle_init_msg(struct nvkm_msgqueue *priv,
+			 struct nvkm_msgqueue_hdr *hdr)
+{
+	struct nvkm_falcon *falcon = priv->falcon;
+	const struct nvkm_subdev *subdev = falcon->owner;
+	u32 tail;
+	u32 tail_reg;
+	int ret;
+
+	/*
+	 * Of course the message queue registers vary depending on the falcon
+	 * used...
+	 */
+	switch (falcon->owner->index) {
+	case NVKM_SUBDEV_PMU:
+		tail_reg = 0x4cc;
+		break;
+	case NVKM_ENGINE_SEC2:
+		tail_reg = 0xa34;
+		break;
+	default:
+		nvkm_error(subdev, "falcon %s unsupported for msgqueue!\n",
+			   nvkm_subdev_name[falcon->owner->index]);
+		return -EINVAL;
+	}
+
+	/*
+	 * Read the message - queues are not initialized yet so we cannot rely
+	 * on msg_queue_read()
+	 */
+	tail = nvkm_falcon_rd32(falcon, tail_reg);
+	nvkm_falcon_read_dmem(falcon, tail, HDR_SIZE, 0, hdr);
+
+	if (hdr->size > MSG_BUF_SIZE) {
+		nvkm_error(subdev, "message too big (%d bytes)\n", hdr->size);
+		return -ENOSPC;
+	}
+
+	nvkm_falcon_read_dmem(falcon, tail + HDR_SIZE, hdr->size - HDR_SIZE, 0,
+			      (hdr + 1));
+
+	tail += ALIGN(hdr->size, QUEUE_ALIGNMENT);
+	nvkm_falcon_wr32(falcon, tail_reg, tail);
+
+	ret = priv->func->init_func->init_callback(priv, hdr);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+void
+nvkm_msgqueue_process_msgs(struct nvkm_msgqueue *priv,
+			   struct nvkm_msgqueue_queue *queue)
+{
+	/*
+	 * We are invoked from a worker thread, so normally we have plenty of
+	 * stack space to work with.
+	 */
+	u8 msg_buffer[MSG_BUF_SIZE];
+	struct nvkm_msgqueue_hdr *hdr = (void *)msg_buffer;
+	int ret;
+
+	/* the first message we receive must be the init message */
+	if ((!priv->init_msg_received)) {
+		ret = msgqueue_handle_init_msg(priv, hdr);
+		if (!ret)
+			priv->init_msg_received = true;
+	} else {
+		while (msg_queue_read(priv, queue, hdr) > 0)
+			msgqueue_msg_handle(priv, hdr);
+	}
+}
+
+void
+nvkm_msgqueue_write_cmdline(struct nvkm_msgqueue *queue, void *buf)
+{
+	if (!queue || !queue->func || !queue->func->init_func)
+		return;
+
+	queue->func->init_func->gen_cmdline(queue, buf);
+}
+
+int
+nvkm_msgqueue_acr_boot_falcon(struct nvkm_msgqueue *queue, enum nvkm_secboot_falcon falcon)
+{
+	if (!queue || !queue->func->acr_func || !queue->func->acr_func->boot_falcon)
+		return -ENODEV;
+
+	return queue->func->acr_func->boot_falcon(queue, falcon);
+}
+
+int
+nvkm_msgqueue_new(u32 version, struct nvkm_falcon *falcon, struct nvkm_msgqueue **queue)
+{
+	const struct nvkm_subdev *subdev = falcon->owner;
+	int ret = -EINVAL;
+
+	switch (version) {
+	case 0x0137c63d:
+		ret = msgqueue_0137c63d_new(falcon, queue);
+		break;
+	case 0x0148cdec:
+		ret = msgqueue_0148cdec_new(falcon, queue);
+		break;
+	default:
+		nvkm_error(subdev, "unhandled firmware version 0x%08x\n",
+			   version);
+		break;
+	}
+
+	if (ret == 0) {
+		nvkm_debug(subdev, "firmware version: 0x%08x\n", version);
+		(*queue)->fw_version = version;
+	}
+
+	return ret;
+}
+
+void
+nvkm_msgqueue_del(struct nvkm_msgqueue **queue)
+{
+	if (*queue) {
+		(*queue)->func->dtor(*queue);
+		*queue = NULL;
+	}
+}
+
+void
+nvkm_msgqueue_recv(struct nvkm_msgqueue *queue)
+{
+	if (!queue || !queue->func || !queue->func->recv) {
+		const struct nvkm_subdev *subdev = queue->falcon->owner;
+
+		nvkm_warn(subdev,
+		      "cmdqueue recv function called while no firmware set!\n");
+		return;
+	}
+
+	queue->func->recv(queue);
+}
+
+int
+nvkm_msgqueue_reinit(struct nvkm_msgqueue *queue)
+{
+	/* firmware not set yet... */
+	if (!queue)
+		return 0;
+
+	queue->init_msg_received = false;
+	reinit_completion(&queue->init_done);
+
+	return 0;
+}
+
+void
+nvkm_msgqueue_ctor(const struct nvkm_msgqueue_func *func,
+		   struct nvkm_falcon *falcon,
+		   struct nvkm_msgqueue *queue)
+{
+	int i;
+
+	queue->func = func;
+	queue->falcon = falcon;
+	mutex_init(&queue->seq_lock);
+	for (i = 0; i < NVKM_MSGQUEUE_NUM_SEQUENCES; i++)
+		queue->seq[i].id = i;
+
+	init_completion(&queue->init_done);
+
+
+}

+ 207 - 0
drivers/gpu/drm/nouveau/nvkm/falcon/msgqueue.h

@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef __NVKM_CORE_FALCON_MSGQUEUE_H
+#define __NVKM_CORE_FALCON_MSGQUEUE_H
+
+#include <core/msgqueue.h>
+
+/*
+ * The struct nvkm_msgqueue (named so for lack of better candidate) manages
+ * a firmware (typically, NVIDIA signed firmware) running under a given falcon.
+ *
+ * Such firmwares expect to receive commands (through one or several command
+ * queues) and will reply to such command by sending messages (using one
+ * message queue).
+ *
+ * Each firmware can support one or several units - ACR for managing secure
+ * falcons, PMU for power management, etc. A unit can be seen as a class to
+ * which command can be sent.
+ *
+ * One usage example would be to send a command to the SEC falcon to ask it to
+ * reset a secure falcon. The SEC falcon will receive the command, process it,
+ * and send a message to signal success or failure. Only when the corresponding
+ * message is received can the requester assume the request has been processed.
+ *
+ * Since we expect many variations between the firmwares NVIDIA will release
+ * across GPU generations, this library is built in a very modular way. Message
+ * formats and queues details (such as number of usage) are left to
+ * specializations of struct nvkm_msgqueue, while the functions in msgqueue.c
+ * take care of posting commands and processing messages in a fashion that is
+ * universal.
+ *
+ */
+
+enum msgqueue_msg_priority {
+	MSGQUEUE_MSG_PRIORITY_HIGH,
+	MSGQUEUE_MSG_PRIORITY_LOW,
+};
+
+/**
+ * struct nvkm_msgqueue_hdr - header for all commands/messages
+ * @unit_id:	id of firmware using receiving the command/sending the message
+ * @size:	total size of command/message
+ * @ctrl_flags:	type of command/message
+ * @seq_id:	used to match a message from its corresponding command
+ */
+struct nvkm_msgqueue_hdr {
+	u8 unit_id;
+	u8 size;
+	u8 ctrl_flags;
+	u8 seq_id;
+};
+
+/**
+ * struct nvkm_msgqueue_msg - base message.
+ *
+ * This is just a header and a message (or command) type. Useful when
+ * building command-specific structures.
+ */
+struct nvkm_msgqueue_msg {
+	struct nvkm_msgqueue_hdr hdr;
+	u8 msg_type;
+};
+
+struct nvkm_msgqueue;
+typedef void
+(*nvkm_msgqueue_callback)(struct nvkm_msgqueue *, struct nvkm_msgqueue_hdr *);
+
+/**
+ * struct nvkm_msgqueue_init_func - msgqueue functions related to initialization
+ *
+ * @gen_cmdline:	build the commandline into a pre-allocated buffer
+ * @init_callback:	called to process the init message
+ */
+struct nvkm_msgqueue_init_func {
+	void (*gen_cmdline)(struct nvkm_msgqueue *, void *);
+	int (*init_callback)(struct nvkm_msgqueue *, struct nvkm_msgqueue_hdr *);
+};
+
+/**
+ * struct nvkm_msgqueue_acr_func - msgqueue functions related to ACR
+ *
+ * @boot_falcon:	build and send the command to reset a given falcon
+ */
+struct nvkm_msgqueue_acr_func {
+	int (*boot_falcon)(struct nvkm_msgqueue *, enum nvkm_secboot_falcon);
+};
+
+struct nvkm_msgqueue_func {
+	const struct nvkm_msgqueue_init_func *init_func;
+	const struct nvkm_msgqueue_acr_func *acr_func;
+	void (*dtor)(struct nvkm_msgqueue *);
+	struct nvkm_msgqueue_queue *(*cmd_queue)(struct nvkm_msgqueue *,
+						 enum msgqueue_msg_priority);
+	void (*recv)(struct nvkm_msgqueue *queue);
+};
+
+/**
+ * struct nvkm_msgqueue_queue - information about a command or message queue
+ *
+ * The number of queues is firmware-dependent. All queues must have their
+ * information filled by the init message handler.
+ *
+ * @mutex_lock:	to be acquired when the queue is being used
+ * @index:	physical queue index
+ * @offset:	DMEM offset where this queue begins
+ * @size:	size allocated to this queue in DMEM (in bytes)
+ * @position:	current write position
+ * @head_reg:	address of the HEAD register for this queue
+ * @tail_reg:	address of the TAIL register for this queue
+ */
+struct nvkm_msgqueue_queue {
+	struct mutex mutex;
+	u32 index;
+	u32 offset;
+	u32 size;
+	u32 position;
+
+	u32 head_reg;
+	u32 tail_reg;
+};
+
+/**
+ * struct nvkm_msgqueue_seq - keep track of ongoing commands
+ *
+ * Every time a command is sent, a sequence is assigned to it so the
+ * corresponding message can be matched. Upon receiving the message, a callback
+ * can be called and/or a completion signaled.
+ *
+ * @id:		sequence ID
+ * @state:	current state
+ * @callback:	callback to call upon receiving matching message
+ * @completion:	completion to signal after callback is called
+ */
+struct nvkm_msgqueue_seq {
+	u16 id;
+	enum {
+		SEQ_STATE_FREE = 0,
+		SEQ_STATE_PENDING,
+		SEQ_STATE_USED,
+		SEQ_STATE_CANCELLED
+	} state;
+	nvkm_msgqueue_callback callback;
+	struct completion *completion;
+};
+
+/*
+ * We can have an arbitrary number of sequences, but realistically we will
+ * probably not use that much simultaneously.
+ */
+#define NVKM_MSGQUEUE_NUM_SEQUENCES 16
+
+/**
+ * struct nvkm_msgqueue - manage a command/message based FW on a falcon
+ *
+ * @falcon:	falcon to be managed
+ * @func:	implementation of the firmware to use
+ * @init_msg_received:	whether the init message has already been received
+ * @init_done:	whether all init is complete and commands can be processed
+ * @seq_lock:	protects seq and seq_tbl
+ * @seq:	sequences to match commands and messages
+ * @seq_tbl:	bitmap of sequences currently in use
+ */
+struct nvkm_msgqueue {
+	struct nvkm_falcon *falcon;
+	const struct nvkm_msgqueue_func *func;
+	u32 fw_version;
+	bool init_msg_received;
+	struct completion init_done;
+
+	struct mutex seq_lock;
+	struct nvkm_msgqueue_seq seq[NVKM_MSGQUEUE_NUM_SEQUENCES];
+	unsigned long seq_tbl[BITS_TO_LONGS(NVKM_MSGQUEUE_NUM_SEQUENCES)];
+};
+
+void nvkm_msgqueue_ctor(const struct nvkm_msgqueue_func *, struct nvkm_falcon *,
+			struct nvkm_msgqueue *);
+int nvkm_msgqueue_post(struct nvkm_msgqueue *, enum msgqueue_msg_priority,
+		       struct nvkm_msgqueue_hdr *, nvkm_msgqueue_callback,
+		       struct completion *, bool);
+void nvkm_msgqueue_process_msgs(struct nvkm_msgqueue *,
+				struct nvkm_msgqueue_queue *);
+
+int msgqueue_0137c63d_new(struct nvkm_falcon *, struct nvkm_msgqueue **);
+int msgqueue_0148cdec_new(struct nvkm_falcon *, struct nvkm_msgqueue **);
+
+#endif

+ 323 - 0
drivers/gpu/drm/nouveau/nvkm/falcon/msgqueue_0137c63d.c

@@ -0,0 +1,323 @@
+/*
+ * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include "msgqueue.h"
+#include <engine/falcon.h>
+#include <subdev/secboot.h>
+
+/* Queues identifiers */
+enum {
+	/* High Priority Command Queue for Host -> PMU communication */
+	MSGQUEUE_0137C63D_COMMAND_QUEUE_HPQ = 0,
+	/* Low Priority Command Queue for Host -> PMU communication */
+	MSGQUEUE_0137C63D_COMMAND_QUEUE_LPQ = 1,
+	/* Message queue for PMU -> Host communication */
+	MSGQUEUE_0137C63D_MESSAGE_QUEUE = 4,
+	MSGQUEUE_0137C63D_NUM_QUEUES = 5,
+};
+
+struct msgqueue_0137c63d {
+	struct nvkm_msgqueue base;
+
+	struct nvkm_msgqueue_queue queue[MSGQUEUE_0137C63D_NUM_QUEUES];
+};
+#define msgqueue_0137c63d(q) \
+	container_of(q, struct msgqueue_0137c63d, base)
+
+static struct nvkm_msgqueue_queue *
+msgqueue_0137c63d_cmd_queue(struct nvkm_msgqueue *queue,
+			    enum msgqueue_msg_priority priority)
+{
+	struct msgqueue_0137c63d *priv = msgqueue_0137c63d(queue);
+	const struct nvkm_subdev *subdev = priv->base.falcon->owner;
+
+	switch (priority) {
+	case MSGQUEUE_MSG_PRIORITY_HIGH:
+		return &priv->queue[MSGQUEUE_0137C63D_COMMAND_QUEUE_HPQ];
+	case MSGQUEUE_MSG_PRIORITY_LOW:
+		return &priv->queue[MSGQUEUE_0137C63D_COMMAND_QUEUE_LPQ];
+	default:
+		nvkm_error(subdev, "invalid command queue!\n");
+		return ERR_PTR(-EINVAL);
+	}
+}
+
+static void
+msgqueue_0137c63d_process_msgs(struct nvkm_msgqueue *queue)
+{
+	struct msgqueue_0137c63d *priv = msgqueue_0137c63d(queue);
+	struct nvkm_msgqueue_queue *q_queue =
+		&priv->queue[MSGQUEUE_0137C63D_MESSAGE_QUEUE];
+
+	nvkm_msgqueue_process_msgs(&priv->base, q_queue);
+}
+
+/* Init unit */
+#define MSGQUEUE_0137C63D_UNIT_INIT 0x07
+
+enum {
+	INIT_MSG_INIT = 0x0,
+};
+
+static void
+init_gen_cmdline(struct nvkm_msgqueue *queue, void *buf)
+{
+	struct {
+		u32 reserved;
+		u32 freq_hz;
+		u32 trace_size;
+		u32 trace_dma_base;
+		u16 trace_dma_base1;
+		u8 trace_dma_offset;
+		u32 trace_dma_idx;
+		bool secure_mode;
+		bool raise_priv_sec;
+		struct {
+			u32 dma_base;
+			u16 dma_base1;
+			u8 dma_offset;
+			u16 fb_size;
+			u8 dma_idx;
+		} gc6_ctx;
+		u8 pad;
+	} *args = buf;
+
+	args->secure_mode = 1;
+}
+
+/* forward declaration */
+static int acr_init_wpr(struct nvkm_msgqueue *queue);
+
+static int
+init_callback(struct nvkm_msgqueue *_queue, struct nvkm_msgqueue_hdr *hdr)
+{
+	struct msgqueue_0137c63d *priv = msgqueue_0137c63d(_queue);
+	struct {
+		struct nvkm_msgqueue_msg base;
+
+		u8 pad;
+		u16 os_debug_entry_point;
+
+		struct {
+			u16 size;
+			u16 offset;
+			u8 index;
+			u8 pad;
+		} queue_info[MSGQUEUE_0137C63D_NUM_QUEUES];
+
+		u16 sw_managed_area_offset;
+		u16 sw_managed_area_size;
+	} *init = (void *)hdr;
+	const struct nvkm_subdev *subdev = _queue->falcon->owner;
+	int i;
+
+	if (init->base.hdr.unit_id != MSGQUEUE_0137C63D_UNIT_INIT) {
+		nvkm_error(subdev, "expected message from init unit\n");
+		return -EINVAL;
+	}
+
+	if (init->base.msg_type != INIT_MSG_INIT) {
+		nvkm_error(subdev, "expected PMU init msg\n");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < MSGQUEUE_0137C63D_NUM_QUEUES; i++) {
+		struct nvkm_msgqueue_queue *queue = &priv->queue[i];
+
+		mutex_init(&queue->mutex);
+
+		queue->index = init->queue_info[i].index;
+		queue->offset = init->queue_info[i].offset;
+		queue->size = init->queue_info[i].size;
+
+		if (i != MSGQUEUE_0137C63D_MESSAGE_QUEUE) {
+			queue->head_reg = 0x4a0 + (queue->index * 4);
+			queue->tail_reg = 0x4b0 + (queue->index * 4);
+		} else {
+			queue->head_reg = 0x4c8;
+			queue->tail_reg = 0x4cc;
+		}
+
+		nvkm_debug(subdev,
+			   "queue %d: index %d, offset 0x%08x, size 0x%08x\n",
+			   i, queue->index, queue->offset, queue->size);
+	}
+
+	/* Complete initialization by initializing WPR region */
+	return acr_init_wpr(&priv->base);
+}
+
+static const struct nvkm_msgqueue_init_func
+msgqueue_0137c63d_init_func = {
+	.gen_cmdline = init_gen_cmdline,
+	.init_callback = init_callback,
+};
+
+
+
+/* ACR unit */
+#define MSGQUEUE_0137C63D_UNIT_ACR 0x0a
+
+enum {
+	ACR_CMD_INIT_WPR_REGION = 0x00,
+	ACR_CMD_BOOTSTRAP_FALCON = 0x01,
+};
+
+static void
+acr_init_wpr_callback(struct nvkm_msgqueue *queue,
+		      struct nvkm_msgqueue_hdr *hdr)
+{
+	struct {
+		struct nvkm_msgqueue_msg base;
+		u32 error_code;
+	} *msg = (void *)hdr;
+	const struct nvkm_subdev *subdev = queue->falcon->owner;
+
+	if (msg->error_code) {
+		nvkm_error(subdev, "ACR WPR init failure: %d\n",
+			   msg->error_code);
+		return;
+	}
+
+	nvkm_debug(subdev, "ACR WPR init complete\n");
+	complete_all(&queue->init_done);
+}
+
+static int
+acr_init_wpr(struct nvkm_msgqueue *queue)
+{
+	/*
+	 * region_id:	region ID in WPR region
+	 * wpr_offset:	offset in WPR region
+	 */
+	struct {
+		struct nvkm_msgqueue_hdr hdr;
+		u8 cmd_type;
+		u32 region_id;
+		u32 wpr_offset;
+	} cmd;
+	memset(&cmd, 0, sizeof(cmd));
+
+	cmd.hdr.unit_id = MSGQUEUE_0137C63D_UNIT_ACR;
+	cmd.hdr.size = sizeof(cmd);
+	cmd.cmd_type = ACR_CMD_INIT_WPR_REGION;
+	cmd.region_id = 0x01;
+	cmd.wpr_offset = 0x00;
+
+	nvkm_msgqueue_post(queue, MSGQUEUE_MSG_PRIORITY_HIGH, &cmd.hdr,
+			   acr_init_wpr_callback, NULL, false);
+
+	return 0;
+}
+
+
+static void
+acr_boot_falcon_callback(struct nvkm_msgqueue *priv,
+			 struct nvkm_msgqueue_hdr *hdr)
+{
+	struct acr_bootstrap_falcon_msg {
+		struct nvkm_msgqueue_msg base;
+
+		u32 falcon_id;
+	} *msg = (void *)hdr;
+	const struct nvkm_subdev *subdev = priv->falcon->owner;
+	u32 falcon_id = msg->falcon_id;
+
+	if (falcon_id >= NVKM_SECBOOT_FALCON_END) {
+		nvkm_error(subdev, "in bootstrap falcon callback:\n");
+		nvkm_error(subdev, "invalid falcon ID 0x%x\n", falcon_id);
+		return;
+	}
+	nvkm_debug(subdev, "%s booted\n", nvkm_secboot_falcon_name[falcon_id]);
+}
+
+enum {
+	ACR_CMD_BOOTSTRAP_FALCON_FLAGS_RESET_YES = 0,
+	ACR_CMD_BOOTSTRAP_FALCON_FLAGS_RESET_NO = 1,
+};
+
+static int
+acr_boot_falcon(struct nvkm_msgqueue *priv, enum nvkm_secboot_falcon falcon)
+{
+	DECLARE_COMPLETION_ONSTACK(completed);
+	/*
+	 * flags      - Flag specifying RESET or no RESET.
+	 * falcon id  - Falcon id specifying falcon to bootstrap.
+	 */
+	struct {
+		struct nvkm_msgqueue_hdr hdr;
+		u8 cmd_type;
+		u32 flags;
+		u32 falcon_id;
+	} cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	cmd.hdr.unit_id = MSGQUEUE_0137C63D_UNIT_ACR;
+	cmd.hdr.size = sizeof(cmd);
+	cmd.cmd_type = ACR_CMD_BOOTSTRAP_FALCON;
+	cmd.flags = ACR_CMD_BOOTSTRAP_FALCON_FLAGS_RESET_YES;
+	cmd.falcon_id = falcon;
+	nvkm_msgqueue_post(priv, MSGQUEUE_MSG_PRIORITY_HIGH, &cmd.hdr,
+			acr_boot_falcon_callback, &completed, true);
+
+	if (!wait_for_completion_timeout(&completed, msecs_to_jiffies(1000)))
+		return -ETIMEDOUT;
+
+	return 0;
+}
+
+static const struct nvkm_msgqueue_acr_func
+msgqueue_0137c63d_acr_func = {
+	.boot_falcon = acr_boot_falcon,
+};
+
+static void
+msgqueue_0137c63d_dtor(struct nvkm_msgqueue *queue)
+{
+	kfree(msgqueue_0137c63d(queue));
+}
+
+static const struct nvkm_msgqueue_func
+msgqueue_0137c63d_func = {
+	.init_func = &msgqueue_0137c63d_init_func,
+	.acr_func = &msgqueue_0137c63d_acr_func,
+	.cmd_queue = msgqueue_0137c63d_cmd_queue,
+	.recv = msgqueue_0137c63d_process_msgs,
+	.dtor = msgqueue_0137c63d_dtor,
+};
+
+int
+msgqueue_0137c63d_new(struct nvkm_falcon *falcon, struct nvkm_msgqueue **queue)
+{
+	struct msgqueue_0137c63d *ret;
+
+	ret = kzalloc(sizeof(*ret), GFP_KERNEL);
+	if (!ret)
+		return -ENOMEM;
+
+	*queue = &ret->base;
+
+	nvkm_msgqueue_ctor(&msgqueue_0137c63d_func, falcon, &ret->base);
+
+	return 0;
+}

+ 263 - 0
drivers/gpu/drm/nouveau/nvkm/falcon/msgqueue_0148cdec.c

@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "msgqueue.h"
+#include <engine/falcon.h>
+#include <subdev/secboot.h>
+
+/*
+ * This firmware runs on the SEC falcon. It only has one command and one
+ * message queue, and uses a different command line and init message.
+ */
+
+enum {
+	MSGQUEUE_0148CDEC_COMMAND_QUEUE = 0,
+	MSGQUEUE_0148CDEC_MESSAGE_QUEUE = 1,
+	MSGQUEUE_0148CDEC_NUM_QUEUES,
+};
+
+struct msgqueue_0148cdec {
+	struct nvkm_msgqueue base;
+
+	struct nvkm_msgqueue_queue queue[MSGQUEUE_0148CDEC_NUM_QUEUES];
+};
+#define msgqueue_0148cdec(q) \
+	container_of(q, struct msgqueue_0148cdec, base)
+
+static struct nvkm_msgqueue_queue *
+msgqueue_0148cdec_cmd_queue(struct nvkm_msgqueue *queue,
+			    enum msgqueue_msg_priority priority)
+{
+	struct msgqueue_0148cdec *priv = msgqueue_0148cdec(queue);
+
+	return &priv->queue[MSGQUEUE_0148CDEC_COMMAND_QUEUE];
+}
+
+static void
+msgqueue_0148cdec_process_msgs(struct nvkm_msgqueue *queue)
+{
+	struct msgqueue_0148cdec *priv = msgqueue_0148cdec(queue);
+	struct nvkm_msgqueue_queue *q_queue =
+		&priv->queue[MSGQUEUE_0148CDEC_MESSAGE_QUEUE];
+
+	nvkm_msgqueue_process_msgs(&priv->base, q_queue);
+}
+
+
+/* Init unit */
+#define MSGQUEUE_0148CDEC_UNIT_INIT 0x01
+
+enum {
+	INIT_MSG_INIT = 0x0,
+};
+
+static void
+init_gen_cmdline(struct nvkm_msgqueue *queue, void *buf)
+{
+	struct {
+		u32 freq_hz;
+		u32 falc_trace_size;
+		u32 falc_trace_dma_base;
+		u32 falc_trace_dma_idx;
+		bool secure_mode;
+	} *args = buf;
+
+	args->secure_mode = false;
+}
+
+static int
+init_callback(struct nvkm_msgqueue *_queue, struct nvkm_msgqueue_hdr *hdr)
+{
+	struct msgqueue_0148cdec *priv = msgqueue_0148cdec(_queue);
+	struct {
+		struct nvkm_msgqueue_msg base;
+
+		u8 num_queues;
+		u16 os_debug_entry_point;
+
+		struct {
+			u32 offset;
+			u16 size;
+			u8 index;
+			u8 id;
+		} queue_info[MSGQUEUE_0148CDEC_NUM_QUEUES];
+
+		u16 sw_managed_area_offset;
+		u16 sw_managed_area_size;
+	} *init = (void *)hdr;
+	const struct nvkm_subdev *subdev = _queue->falcon->owner;
+	int i;
+
+	if (init->base.hdr.unit_id != MSGQUEUE_0148CDEC_UNIT_INIT) {
+		nvkm_error(subdev, "expected message from init unit\n");
+		return -EINVAL;
+	}
+
+	if (init->base.msg_type != INIT_MSG_INIT) {
+		nvkm_error(subdev, "expected SEC init msg\n");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < MSGQUEUE_0148CDEC_NUM_QUEUES; i++) {
+		u8 id = init->queue_info[i].id;
+		struct nvkm_msgqueue_queue *queue = &priv->queue[id];
+
+		mutex_init(&queue->mutex);
+
+		queue->index = init->queue_info[i].index;
+		queue->offset = init->queue_info[i].offset;
+		queue->size = init->queue_info[i].size;
+
+		if (id == MSGQUEUE_0148CDEC_MESSAGE_QUEUE) {
+			queue->head_reg = 0xa30 + (queue->index * 8);
+			queue->tail_reg = 0xa34 + (queue->index * 8);
+		} else {
+			queue->head_reg = 0xa00 + (queue->index * 8);
+			queue->tail_reg = 0xa04 + (queue->index * 8);
+		}
+
+		nvkm_debug(subdev,
+			   "queue %d: index %d, offset 0x%08x, size 0x%08x\n",
+			   id, queue->index, queue->offset, queue->size);
+	}
+
+	complete_all(&_queue->init_done);
+
+	return 0;
+}
+
+static const struct nvkm_msgqueue_init_func
+msgqueue_0148cdec_init_func = {
+	.gen_cmdline = init_gen_cmdline,
+	.init_callback = init_callback,
+};
+
+
+
+/* ACR unit */
+#define MSGQUEUE_0148CDEC_UNIT_ACR 0x08
+
+enum {
+	ACR_CMD_BOOTSTRAP_FALCON = 0x00,
+};
+
+static void
+acr_boot_falcon_callback(struct nvkm_msgqueue *priv,
+			 struct nvkm_msgqueue_hdr *hdr)
+{
+	struct acr_bootstrap_falcon_msg {
+		struct nvkm_msgqueue_msg base;
+
+		u32 error_code;
+		u32 falcon_id;
+	} *msg = (void *)hdr;
+	const struct nvkm_subdev *subdev = priv->falcon->owner;
+	u32 falcon_id = msg->falcon_id;
+
+	if (msg->error_code) {
+		nvkm_error(subdev, "in bootstrap falcon callback:\n");
+		nvkm_error(subdev, "expected error code 0x%x\n",
+			   msg->error_code);
+		return;
+	}
+
+	if (falcon_id >= NVKM_SECBOOT_FALCON_END) {
+		nvkm_error(subdev, "in bootstrap falcon callback:\n");
+		nvkm_error(subdev, "invalid falcon ID 0x%x\n", falcon_id);
+		return;
+	}
+
+	nvkm_debug(subdev, "%s booted\n", nvkm_secboot_falcon_name[falcon_id]);
+}
+
+enum {
+	ACR_CMD_BOOTSTRAP_FALCON_FLAGS_RESET_YES = 0,
+	ACR_CMD_BOOTSTRAP_FALCON_FLAGS_RESET_NO = 1,
+};
+
+static int
+acr_boot_falcon(struct nvkm_msgqueue *priv, enum nvkm_secboot_falcon falcon)
+{
+	DECLARE_COMPLETION_ONSTACK(completed);
+	/*
+	 * flags      - Flag specifying RESET or no RESET.
+	 * falcon id  - Falcon id specifying falcon to bootstrap.
+	 */
+	struct {
+		struct nvkm_msgqueue_hdr hdr;
+		u8 cmd_type;
+		u32 flags;
+		u32 falcon_id;
+	} cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	cmd.hdr.unit_id = MSGQUEUE_0148CDEC_UNIT_ACR;
+	cmd.hdr.size = sizeof(cmd);
+	cmd.cmd_type = ACR_CMD_BOOTSTRAP_FALCON;
+	cmd.flags = ACR_CMD_BOOTSTRAP_FALCON_FLAGS_RESET_YES;
+	cmd.falcon_id = falcon;
+	nvkm_msgqueue_post(priv, MSGQUEUE_MSG_PRIORITY_HIGH, &cmd.hdr,
+			   acr_boot_falcon_callback, &completed, true);
+
+	if (!wait_for_completion_timeout(&completed, msecs_to_jiffies(1000)))
+		return -ETIMEDOUT;
+
+	return 0;
+}
+
+const struct nvkm_msgqueue_acr_func
+msgqueue_0148cdec_acr_func = {
+	.boot_falcon = acr_boot_falcon,
+};
+
+static void
+msgqueue_0148cdec_dtor(struct nvkm_msgqueue *queue)
+{
+	kfree(msgqueue_0148cdec(queue));
+}
+
+const struct nvkm_msgqueue_func
+msgqueue_0148cdec_func = {
+	.init_func = &msgqueue_0148cdec_init_func,
+	.acr_func = &msgqueue_0148cdec_acr_func,
+	.cmd_queue = msgqueue_0148cdec_cmd_queue,
+	.recv = msgqueue_0148cdec_process_msgs,
+	.dtor = msgqueue_0148cdec_dtor,
+};
+
+int
+msgqueue_0148cdec_new(struct nvkm_falcon *falcon, struct nvkm_msgqueue **queue)
+{
+	struct msgqueue_0148cdec *ret;
+
+	ret = kzalloc(sizeof(*ret), GFP_KERNEL);
+	if (!ret)
+		return -ENOMEM;
+
+	*queue = &ret->base;
+
+	nvkm_msgqueue_ctor(&msgqueue_0148cdec_func, falcon, &ret->base);
+
+	return 0;
+}

+ 104 - 20
drivers/gpu/drm/nouveau/nvkm/falcon/v1.c

@@ -40,8 +40,8 @@ nvkm_falcon_v1_load_imem(struct nvkm_falcon *falcon, void *data, u32 start,
 	for (i = 0; i < size / 4; i++) {
 		/* write new tag every 256B */
 		if ((i & 0x3f) == 0)
-			nvkm_falcon_wr32(falcon, 0x188, tag++);
-		nvkm_falcon_wr32(falcon, 0x184, ((u32 *)data)[i]);
+			nvkm_falcon_wr32(falcon, 0x188 + (port * 16), tag++);
+		nvkm_falcon_wr32(falcon, 0x184 + (port * 16), ((u32 *)data)[i]);
 	}
 
 	/*
@@ -53,16 +53,44 @@ nvkm_falcon_v1_load_imem(struct nvkm_falcon *falcon, void *data, u32 start,
 
 		/* write new tag every 256B */
 		if ((i & 0x3f) == 0)
-			nvkm_falcon_wr32(falcon, 0x188, tag++);
-		nvkm_falcon_wr32(falcon, 0x184, extra & (BIT(rem * 8) - 1));
+			nvkm_falcon_wr32(falcon, 0x188 + (port * 16), tag++);
+		nvkm_falcon_wr32(falcon, 0x184 + (port * 16),
+				 extra & (BIT(rem * 8) - 1));
 		++i;
 	}
 
 	/* code must be padded to 0x40 words */
 	for (; i & 0x3f; i++)
-		nvkm_falcon_wr32(falcon, 0x184, 0);
+		nvkm_falcon_wr32(falcon, 0x184 + (port * 16), 0);
 }
 
+static void
+nvkm_falcon_v1_load_emem(struct nvkm_falcon *falcon, void *data, u32 start,
+			 u32 size, u8 port)
+{
+	u8 rem = size % 4;
+	int i;
+
+	size -= rem;
+
+	nvkm_falcon_wr32(falcon, 0xac0 + (port * 8), start | (0x1 << 24));
+	for (i = 0; i < size / 4; i++)
+		nvkm_falcon_wr32(falcon, 0xac4 + (port * 8), ((u32 *)data)[i]);
+
+	/*
+	 * If size is not a multiple of 4, mask the last word to ensure garbage
+	 * does not get written
+	 */
+	if (rem) {
+		u32 extra = ((u32 *)data)[i];
+
+		nvkm_falcon_wr32(falcon, 0xac4 + (port * 8),
+				 extra & (BIT(rem * 8) - 1));
+	}
+}
+
+static const u32 EMEM_START_ADDR = 0x1000000;
+
 static void
 nvkm_falcon_v1_load_dmem(struct nvkm_falcon *falcon, void *data, u32 start,
 		      u32 size, u8 port)
@@ -70,20 +98,53 @@ nvkm_falcon_v1_load_dmem(struct nvkm_falcon *falcon, void *data, u32 start,
 	u8 rem = size % 4;
 	int i;
 
+	if (start >= EMEM_START_ADDR && falcon->has_emem)
+		return nvkm_falcon_v1_load_emem(falcon, data,
+						start - EMEM_START_ADDR, size,
+						port);
+
 	size -= rem;
 
-	nvkm_falcon_wr32(falcon, 0x1c0 + (port * 16), start | (0x1 << 24));
+	nvkm_falcon_wr32(falcon, 0x1c0 + (port * 8), start | (0x1 << 24));
 	for (i = 0; i < size / 4; i++)
-		nvkm_falcon_wr32(falcon, 0x1c4, ((u32 *)data)[i]);
+		nvkm_falcon_wr32(falcon, 0x1c4 + (port * 8), ((u32 *)data)[i]);
 
 	/*
-	 * If size is not a multiple of 4, mask the last work to ensure garbage
-	 * does not get read
+	 * If size is not a multiple of 4, mask the last word to ensure garbage
+	 * does not get written
 	 */
 	if (rem) {
 		u32 extra = ((u32 *)data)[i];
 
-		nvkm_falcon_wr32(falcon, 0x1c4, extra & (BIT(rem * 8) - 1));
+		nvkm_falcon_wr32(falcon, 0x1c4 + (port * 8),
+				 extra & (BIT(rem * 8) - 1));
+	}
+}
+
+static void
+nvkm_falcon_v1_read_emem(struct nvkm_falcon *falcon, u32 start, u32 size,
+			 u8 port, void *data)
+{
+	u8 rem = size % 4;
+	int i;
+
+	size -= rem;
+
+	nvkm_falcon_wr32(falcon, 0xac0 + (port * 8), start | (0x1 << 25));
+	for (i = 0; i < size / 4; i++)
+		((u32 *)data)[i] = nvkm_falcon_rd32(falcon, 0xac4 + (port * 8));
+
+	/*
+	 * If size is not a multiple of 4, mask the last word to ensure garbage
+	 * does not get read
+	 */
+	if (rem) {
+		u32 extra = nvkm_falcon_rd32(falcon, 0xac4 + (port * 8));
+
+		for (i = size; i < size + rem; i++) {
+			((u8 *)data)[i] = (u8)(extra & 0xff);
+			extra >>= 8;
+		}
 	}
 }
 
@@ -94,18 +155,22 @@ nvkm_falcon_v1_read_dmem(struct nvkm_falcon *falcon, u32 start, u32 size,
 	u8 rem = size % 4;
 	int i;
 
+	if (start >= EMEM_START_ADDR && falcon->has_emem)
+		return nvkm_falcon_v1_read_emem(falcon, start - EMEM_START_ADDR,
+						size, port, data);
+
 	size -= rem;
 
-	nvkm_falcon_wr32(falcon, 0x1c0 + (port * 16), start | (0x1 << 25));
+	nvkm_falcon_wr32(falcon, 0x1c0 + (port * 8), start | (0x1 << 25));
 	for (i = 0; i < size / 4; i++)
-		((u32 *)data)[i] = nvkm_falcon_rd32(falcon, 0x1c4);
+		((u32 *)data)[i] = nvkm_falcon_rd32(falcon, 0x1c4 + (port * 8));
 
 	/*
-	 * If size is not a multiple of 4, mask the last work to ensure garbage
+	 * If size is not a multiple of 4, mask the last word to ensure garbage
 	 * does not get read
 	 */
 	if (rem) {
-		u32 extra = nvkm_falcon_rd32(falcon, 0x1c4);
+		u32 extra = nvkm_falcon_rd32(falcon, 0x1c4 + (port * 8));
 
 		for (i = size; i < size + rem; i++) {
 			((u8 *)data)[i] = (u8)(extra & 0xff);
@@ -118,6 +183,7 @@ static void
 nvkm_falcon_v1_bind_context(struct nvkm_falcon *falcon, struct nvkm_gpuobj *ctx)
 {
 	u32 inst_loc;
+	u32 fbif;
 
 	/* disable instance block binding */
 	if (ctx == NULL) {
@@ -125,19 +191,34 @@ nvkm_falcon_v1_bind_context(struct nvkm_falcon *falcon, struct nvkm_gpuobj *ctx)
 		return;
 	}
 
+	switch (falcon->owner->index) {
+	case NVKM_ENGINE_NVENC0:
+	case NVKM_ENGINE_NVENC1:
+	case NVKM_ENGINE_NVENC2:
+		fbif = 0x800;
+		break;
+	case NVKM_SUBDEV_PMU:
+		fbif = 0xe00;
+		break;
+	default:
+		fbif = 0x600;
+		break;
+	}
+
 	nvkm_falcon_wr32(falcon, 0x10c, 0x1);
 
 	/* setup apertures - virtual */
-	nvkm_falcon_wr32(falcon, 0xe00 + 4 * FALCON_DMAIDX_UCODE, 0x4);
-	nvkm_falcon_wr32(falcon, 0xe00 + 4 * FALCON_DMAIDX_VIRT, 0x0);
+	nvkm_falcon_wr32(falcon, fbif + 4 * FALCON_DMAIDX_UCODE, 0x4);
+	nvkm_falcon_wr32(falcon, fbif + 4 * FALCON_DMAIDX_VIRT, 0x0);
 	/* setup apertures - physical */
-	nvkm_falcon_wr32(falcon, 0xe00 + 4 * FALCON_DMAIDX_PHYS_VID, 0x4);
-	nvkm_falcon_wr32(falcon, 0xe00 + 4 * FALCON_DMAIDX_PHYS_SYS_COH, 0x5);
-	nvkm_falcon_wr32(falcon, 0xe00 + 4 * FALCON_DMAIDX_PHYS_SYS_NCOH, 0x6);
+	nvkm_falcon_wr32(falcon, fbif + 4 * FALCON_DMAIDX_PHYS_VID, 0x4);
+	nvkm_falcon_wr32(falcon, fbif + 4 * FALCON_DMAIDX_PHYS_SYS_COH, 0x5);
+	nvkm_falcon_wr32(falcon, fbif + 4 * FALCON_DMAIDX_PHYS_SYS_NCOH, 0x6);
 
 	/* Set context */
 	switch (nvkm_memory_target(ctx->memory)) {
 	case NVKM_MEM_TARGET_VRAM: inst_loc = 0; break;
+	case NVKM_MEM_TARGET_HOST: inst_loc = 2; break;
 	case NVKM_MEM_TARGET_NCOH: inst_loc = 3; break;
 	default:
 		WARN_ON(1);
@@ -146,9 +227,12 @@ nvkm_falcon_v1_bind_context(struct nvkm_falcon *falcon, struct nvkm_gpuobj *ctx)
 
 	/* Enable context */
 	nvkm_falcon_mask(falcon, 0x048, 0x1, 0x1);
-	nvkm_falcon_wr32(falcon, 0x480,
+	nvkm_falcon_wr32(falcon, 0x054,
 			 ((ctx->addr >> 12) & 0xfffffff) |
 			 (inst_loc << 28) | (1 << 30));
+
+	nvkm_falcon_mask(falcon, 0x090, 0x10000, 0x10000);
+	nvkm_falcon_mask(falcon, 0x0a4, 0x8, 0x8);
 }
 
 static void

+ 3 - 0
drivers/gpu/drm/nouveau/nvkm/subdev/fb/Kbuild

@@ -20,6 +20,7 @@ nvkm-y += nvkm/subdev/fb/gt215.o
 nvkm-y += nvkm/subdev/fb/mcp77.o
 nvkm-y += nvkm/subdev/fb/mcp89.o
 nvkm-y += nvkm/subdev/fb/gf100.o
+nvkm-y += nvkm/subdev/fb/gf108.o
 nvkm-y += nvkm/subdev/fb/gk104.o
 nvkm-y += nvkm/subdev/fb/gk20a.o
 nvkm-y += nvkm/subdev/fb/gm107.o
@@ -42,8 +43,10 @@ nvkm-y += nvkm/subdev/fb/ramnv50.o
 nvkm-y += nvkm/subdev/fb/ramgt215.o
 nvkm-y += nvkm/subdev/fb/rammcp77.o
 nvkm-y += nvkm/subdev/fb/ramgf100.o
+nvkm-y += nvkm/subdev/fb/ramgf108.o
 nvkm-y += nvkm/subdev/fb/ramgk104.o
 nvkm-y += nvkm/subdev/fb/ramgm107.o
+nvkm-y += nvkm/subdev/fb/ramgm200.o
 nvkm-y += nvkm/subdev/fb/ramgp100.o
 nvkm-y += nvkm/subdev/fb/sddr2.o
 nvkm-y += nvkm/subdev/fb/sddr3.o

+ 42 - 0
drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf108.c

@@ -0,0 +1,42 @@
+/*
+ * Copyright 2017 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+#include "gf100.h"
+#include "ram.h"
+
+static const struct nvkm_fb_func
+gf108_fb = {
+	.dtor = gf100_fb_dtor,
+	.oneinit = gf100_fb_oneinit,
+	.init = gf100_fb_init,
+	.init_page = gf100_fb_init_page,
+	.intr = gf100_fb_intr,
+	.ram_new = gf108_ram_new,
+	.memtype_valid = gf100_fb_memtype_valid,
+};
+
+int
+gf108_fb_new(struct nvkm_device *device, int index, struct nvkm_fb **pfb)
+{
+	return gf100_fb_new_(&gf108_fb, device, index, pfb);
+}

+ 1 - 1
drivers/gpu/drm/nouveau/nvkm/subdev/fb/gm200.c

@@ -68,7 +68,7 @@ gm200_fb = {
 	.init = gm200_fb_init,
 	.init_page = gm200_fb_init_page,
 	.intr = gf100_fb_intr,
-	.ram_new = gm107_ram_new,
+	.ram_new = gm200_ram_new,
 	.memtype_valid = gf100_fb_memtype_valid,
 };
 

+ 30 - 3
drivers/gpu/drm/nouveau/nvkm/subdev/fb/ram.h

@@ -19,13 +19,38 @@ int  nv50_ram_get(struct nvkm_ram *, u64, u32, u32, u32, struct nvkm_mem **);
 void nv50_ram_put(struct nvkm_ram *, struct nvkm_mem **);
 void __nv50_ram_put(struct nvkm_ram *, struct nvkm_mem *);
 
+int gf100_ram_new_(const struct nvkm_ram_func *, struct nvkm_fb *,
+		   struct nvkm_ram **);
 int  gf100_ram_ctor(const struct nvkm_ram_func *, struct nvkm_fb *,
-		    u32, struct nvkm_ram *);
+		    struct nvkm_ram *);
+u32  gf100_ram_probe_fbp(const struct nvkm_ram_func *,
+			 struct nvkm_device *, int, int *);
+u32  gf100_ram_probe_fbp_amount(const struct nvkm_ram_func *, u32,
+				struct nvkm_device *, int, int *);
+u32  gf100_ram_probe_fbpa_amount(struct nvkm_device *, int);
 int  gf100_ram_get(struct nvkm_ram *, u64, u32, u32, u32, struct nvkm_mem **);
 void gf100_ram_put(struct nvkm_ram *, struct nvkm_mem **);
+int gf100_ram_init(struct nvkm_ram *);
+int gf100_ram_calc(struct nvkm_ram *, u32);
+int gf100_ram_prog(struct nvkm_ram *);
+void gf100_ram_tidy(struct nvkm_ram *);
+
+u32 gf108_ram_probe_fbp_amount(const struct nvkm_ram_func *, u32,
+			       struct nvkm_device *, int, int *);
+
+int gk104_ram_new_(const struct nvkm_ram_func *, struct nvkm_fb *,
+		   struct nvkm_ram **);
+void *gk104_ram_dtor(struct nvkm_ram *);
+int gk104_ram_init(struct nvkm_ram *);
+int gk104_ram_calc(struct nvkm_ram *, u32);
+int gk104_ram_prog(struct nvkm_ram *);
+void gk104_ram_tidy(struct nvkm_ram *);
+
+u32 gm107_ram_probe_fbp(const struct nvkm_ram_func *,
+			struct nvkm_device *, int, int *);
 
-int  gk104_ram_ctor(struct nvkm_fb *, struct nvkm_ram **, u32);
-int  gk104_ram_init(struct nvkm_ram *ram);
+u32 gm200_ram_probe_fbp_amount(const struct nvkm_ram_func *, u32,
+			       struct nvkm_device *, int, int *);
 
 /* RAM type-specific MR calculation routines */
 int nvkm_sddr2_calc(struct nvkm_ram *);
@@ -46,7 +71,9 @@ int nv50_ram_new(struct nvkm_fb *, struct nvkm_ram **);
 int gt215_ram_new(struct nvkm_fb *, struct nvkm_ram **);
 int mcp77_ram_new(struct nvkm_fb *, struct nvkm_ram **);
 int gf100_ram_new(struct nvkm_fb *, struct nvkm_ram **);
+int gf108_ram_new(struct nvkm_fb *, struct nvkm_ram **);
 int gk104_ram_new(struct nvkm_fb *, struct nvkm_ram **);
 int gm107_ram_new(struct nvkm_fb *, struct nvkm_ram **);
+int gm200_ram_new(struct nvkm_fb *, struct nvkm_ram **);
 int gp100_ram_new(struct nvkm_fb *, struct nvkm_ram **);
 #endif

+ 94 - 54
drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgf100.c

@@ -124,7 +124,7 @@ gf100_ram_train(struct gf100_ramfuc *fuc, u32 magic)
 	}
 }
 
-static int
+int
 gf100_ram_calc(struct nvkm_ram *base, u32 freq)
 {
 	struct gf100_ram *ram = gf100_ram(base);
@@ -404,7 +404,7 @@ gf100_ram_calc(struct nvkm_ram *base, u32 freq)
 	return 0;
 }
 
-static int
+int
 gf100_ram_prog(struct nvkm_ram *base)
 {
 	struct gf100_ram *ram = gf100_ram(base);
@@ -413,7 +413,7 @@ gf100_ram_prog(struct nvkm_ram *base)
 	return 0;
 }
 
-static void
+void
 gf100_ram_tidy(struct nvkm_ram *base)
 {
 	struct gf100_ram *ram = gf100_ram(base);
@@ -500,7 +500,7 @@ gf100_ram_get(struct nvkm_ram *ram, u64 size, u32 align, u32 ncmin,
 	return 0;
 }
 
-static int
+int
 gf100_ram_init(struct nvkm_ram *base)
 {
 	static const u8  train0[] = {
@@ -543,77 +543,96 @@ gf100_ram_init(struct nvkm_ram *base)
 	return 0;
 }
 
-static const struct nvkm_ram_func
-gf100_ram_func = {
-	.init = gf100_ram_init,
-	.get = gf100_ram_get,
-	.put = gf100_ram_put,
-	.calc = gf100_ram_calc,
-	.prog = gf100_ram_prog,
-	.tidy = gf100_ram_tidy,
-};
+u32
+gf100_ram_probe_fbpa_amount(struct nvkm_device *device, int fbpa)
+{
+	return nvkm_rd32(device, 0x11020c + (fbpa * 0x1000));
+}
+
+u32
+gf100_ram_probe_fbp_amount(const struct nvkm_ram_func *func, u32 fbpao,
+			   struct nvkm_device *device, int fbp, int *pltcs)
+{
+	if (!(fbpao & BIT(fbp))) {
+		*pltcs = 1;
+		return func->probe_fbpa_amount(device, fbp);
+	}
+	return 0;
+}
+
+u32
+gf100_ram_probe_fbp(const struct nvkm_ram_func *func,
+		    struct nvkm_device *device, int fbp, int *pltcs)
+{
+	u32 fbpao = nvkm_rd32(device, 0x022554);
+	return func->probe_fbp_amount(func, fbpao, device, fbp, pltcs);
+}
 
 int
 gf100_ram_ctor(const struct nvkm_ram_func *func, struct nvkm_fb *fb,
-	       u32 maskaddr, struct nvkm_ram *ram)
+	       struct nvkm_ram *ram)
 {
 	struct nvkm_subdev *subdev = &fb->subdev;
 	struct nvkm_device *device = subdev->device;
 	struct nvkm_bios *bios = device->bios;
 	const u32 rsvd_head = ( 256 * 1024); /* vga memory */
 	const u32 rsvd_tail = (1024 * 1024); /* vbios etc */
-	u32 parts = nvkm_rd32(device, 0x022438);
-	u32 pmask = nvkm_rd32(device, maskaddr);
-	u64 bsize = (u64)nvkm_rd32(device, 0x10f20c) << 20;
-	u64 psize, size = 0;
 	enum nvkm_ram_type type = nvkm_fb_bios_memtype(bios);
-	bool uniform = true;
-	int ret, i;
-
-	nvkm_debug(subdev, "100800: %08x\n", nvkm_rd32(device, 0x100800));
-	nvkm_debug(subdev, "parts %08x mask %08x\n", parts, pmask);
-
-	/* read amount of vram attached to each memory controller */
-	for (i = 0; i < parts; i++) {
-		if (pmask & (1 << i))
-			continue;
-
-		psize = (u64)nvkm_rd32(device, 0x11020c + (i * 0x1000)) << 20;
-		if (psize != bsize) {
-			if (psize < bsize)
-				bsize = psize;
-			uniform = false;
+	u32 fbps = nvkm_rd32(device, 0x022438);
+	u64 total = 0, lcomm = ~0, lower, ubase, usize;
+	int ret, fbp, ltcs, ltcn = 0;
+
+	nvkm_debug(subdev, "%d FBP(s)\n", fbps);
+	for (fbp = 0; fbp < fbps; fbp++) {
+		u32 size = func->probe_fbp(func, device, fbp, &ltcs);
+		if (size) {
+			nvkm_debug(subdev, "FBP %d: %4d MiB, %d LTC(s)\n",
+				   fbp, size, ltcs);
+			lcomm  = min(lcomm, (u64)(size / ltcs) << 20);
+			total += size << 20;
+			ltcn  += ltcs;
+		} else {
+			nvkm_debug(subdev, "FBP %d: disabled\n", fbp);
 		}
-
-		nvkm_debug(subdev, "%d: %d MiB\n", i, (u32)(psize >> 20));
-		size += psize;
 	}
 
-	ret = nvkm_ram_ctor(func, fb, type, size, 0, ram);
+	lower = lcomm * ltcn;
+	ubase = lcomm + func->upper;
+	usize = total - lower;
+
+	nvkm_debug(subdev, "Lower: %4lld MiB @ %010llx\n", lower >> 20, 0ULL);
+	nvkm_debug(subdev, "Upper: %4lld MiB @ %010llx\n", usize >> 20, ubase);
+	nvkm_debug(subdev, "Total: %4lld MiB\n", total >> 20);
+
+	ret = nvkm_ram_ctor(func, fb, type, total, 0, ram);
 	if (ret)
 		return ret;
 
 	nvkm_mm_fini(&ram->vram);
 
-	/* if all controllers have the same amount attached, there's no holes */
-	if (uniform) {
+	/* Some GPUs are in what's known as a "mixed memory" configuration.
+	 *
+	 * This is either where some FBPs have more memory than the others,
+	 * or where LTCs have been disabled on a FBP.
+	 */
+	if (lower != total) {
+		/* The common memory amount is addressed normally. */
 		ret = nvkm_mm_init(&ram->vram, rsvd_head >> NVKM_RAM_MM_SHIFT,
-				   (size - rsvd_head - rsvd_tail) >>
-				   NVKM_RAM_MM_SHIFT, 1);
+				   (lower - rsvd_head) >> NVKM_RAM_MM_SHIFT, 1);
 		if (ret)
 			return ret;
-	} else {
-		/* otherwise, address lowest common amount from 0GiB */
-		ret = nvkm_mm_init(&ram->vram, rsvd_head >> NVKM_RAM_MM_SHIFT,
-				   ((bsize * parts) - rsvd_head) >>
-				   NVKM_RAM_MM_SHIFT, 1);
+
+		/* And the rest is much higher in the physical address
+		 * space, and may not be usable for certain operations.
+		 */
+		ret = nvkm_mm_init(&ram->vram, ubase >> NVKM_RAM_MM_SHIFT,
+				   (usize - rsvd_tail) >> NVKM_RAM_MM_SHIFT, 1);
 		if (ret)
 			return ret;
-
-		/* and the rest starting from (8GiB + common_size) */
-		ret = nvkm_mm_init(&ram->vram, (0x0200000000ULL + bsize) >>
-				   NVKM_RAM_MM_SHIFT,
-				   (size - (bsize * parts) - rsvd_tail) >>
+	} else {
+		/* GPUs without mixed-memory are a lot nicer... */
+		ret = nvkm_mm_init(&ram->vram, rsvd_head >> NVKM_RAM_MM_SHIFT,
+				   (total - rsvd_head - rsvd_tail) >>
 				   NVKM_RAM_MM_SHIFT, 1);
 		if (ret)
 			return ret;
@@ -624,7 +643,8 @@ gf100_ram_ctor(const struct nvkm_ram_func *func, struct nvkm_fb *fb,
 }
 
 int
-gf100_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram)
+gf100_ram_new_(const struct nvkm_ram_func *func,
+	       struct nvkm_fb *fb, struct nvkm_ram **pram)
 {
 	struct nvkm_subdev *subdev = &fb->subdev;
 	struct nvkm_bios *bios = subdev->device->bios;
@@ -635,7 +655,7 @@ gf100_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram)
 		return -ENOMEM;
 	*pram = &ram->base;
 
-	ret = gf100_ram_ctor(&gf100_ram_func, fb, 0x022554, &ram->base);
+	ret = gf100_ram_ctor(func, fb, &ram->base);
 	if (ret)
 		return ret;
 
@@ -711,3 +731,23 @@ gf100_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram)
 	ram->fuc.r_0x13d8f4 = ramfuc_reg(0x13d8f4);
 	return 0;
 }
+
+static const struct nvkm_ram_func
+gf100_ram = {
+	.upper = 0x0200000000,
+	.probe_fbp = gf100_ram_probe_fbp,
+	.probe_fbp_amount = gf100_ram_probe_fbp_amount,
+	.probe_fbpa_amount = gf100_ram_probe_fbpa_amount,
+	.init = gf100_ram_init,
+	.get = gf100_ram_get,
+	.put = gf100_ram_put,
+	.calc = gf100_ram_calc,
+	.prog = gf100_ram_prog,
+	.tidy = gf100_ram_tidy,
+};
+
+int
+gf100_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram)
+{
+	return gf100_ram_new_(&gf100_ram, fb, pram);
+}

+ 62 - 0
drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgf108.c

@@ -0,0 +1,62 @@
+/*
+ * Copyright 2017 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+#include "ram.h"
+
+u32
+gf108_ram_probe_fbp_amount(const struct nvkm_ram_func *func, u32 fbpao,
+			   struct nvkm_device *device, int fbp, int *pltcs)
+{
+	u32 fbpt  = nvkm_rd32(device, 0x022438);
+	u32 fbpat = nvkm_rd32(device, 0x02243c);
+	u32 fbpas = fbpat / fbpt;
+	u32 fbpa  = fbp * fbpas;
+	u32 size  = 0;
+	while (fbpas--) {
+		if (!(fbpao & BIT(fbpa)))
+			size += func->probe_fbpa_amount(device, fbpa);
+		fbpa++;
+	}
+	*pltcs = 1;
+	return size;
+}
+
+static const struct nvkm_ram_func
+gf108_ram = {
+	.upper = 0x0200000000,
+	.probe_fbp = gf100_ram_probe_fbp,
+	.probe_fbp_amount = gf108_ram_probe_fbp_amount,
+	.probe_fbpa_amount = gf100_ram_probe_fbpa_amount,
+	.init = gf100_ram_init,
+	.get = gf100_ram_get,
+	.put = gf100_ram_put,
+	.calc = gf100_ram_calc,
+	.prog = gf100_ram_prog,
+	.tidy = gf100_ram_tidy,
+};
+
+int
+gf108_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram)
+{
+	return gf100_ram_new_(&gf108_ram, fb, pram);
+}

+ 37 - 33
drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgk104.c

@@ -1108,7 +1108,7 @@ gk104_ram_calc_xits(struct gk104_ram *ram, struct nvkm_ram_data *next)
 	return ret;
 }
 
-static int
+int
 gk104_ram_calc(struct nvkm_ram *base, u32 freq)
 {
 	struct gk104_ram *ram = gk104_ram(base);
@@ -1227,7 +1227,7 @@ gk104_ram_prog_0(struct gk104_ram *ram, u32 freq)
 	nvkm_mask(device, 0x10f444, mask, data);
 }
 
-static int
+int
 gk104_ram_prog(struct nvkm_ram *base)
 {
 	struct gk104_ram *ram = gk104_ram(base);
@@ -1247,7 +1247,7 @@ gk104_ram_prog(struct nvkm_ram *base)
 	return (ram->base.next == &ram->base.xition);
 }
 
-static void
+void
 gk104_ram_tidy(struct nvkm_ram *base)
 {
 	struct gk104_ram *ram = gk104_ram(base);
@@ -1509,7 +1509,7 @@ done:
 	return ret;
 }
 
-static void *
+void *
 gk104_ram_dtor(struct nvkm_ram *base)
 {
 	struct gk104_ram *ram = gk104_ram(base);
@@ -1522,31 +1522,14 @@ gk104_ram_dtor(struct nvkm_ram *base)
 	return ram;
 }
 
-static const struct nvkm_ram_func
-gk104_ram_func = {
-	.dtor = gk104_ram_dtor,
-	.init = gk104_ram_init,
-	.get = gf100_ram_get,
-	.put = gf100_ram_put,
-	.calc = gk104_ram_calc,
-	.prog = gk104_ram_prog,
-	.tidy = gk104_ram_tidy,
-};
-
 int
-gk104_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram)
-{
-	return gk104_ram_ctor(fb, pram, 0x022554);
-}
-
-int
-gk104_ram_ctor(struct nvkm_fb *fb, struct nvkm_ram **pram, u32 maskaddr)
+gk104_ram_new_(const struct nvkm_ram_func *func, struct nvkm_fb *fb,
+	       struct nvkm_ram **pram)
 {
 	struct nvkm_subdev *subdev = &fb->subdev;
 	struct nvkm_device *device = subdev->device;
 	struct nvkm_bios *bios = device->bios;
-	struct nvkm_gpio *gpio = device->gpio;
-	struct dcb_gpio_func func;
+	struct dcb_gpio_func gpio;
 	struct gk104_ram *ram;
 	int ret, i;
 	u8  ramcfg = nvbios_ramcfg_index(subdev);
@@ -1556,7 +1539,7 @@ gk104_ram_ctor(struct nvkm_fb *fb, struct nvkm_ram **pram, u32 maskaddr)
 		return -ENOMEM;
 	*pram = &ram->base;
 
-	ret = gf100_ram_ctor(&gk104_ram_func, fb, maskaddr, &ram->base);
+	ret = gf100_ram_ctor(func, fb, &ram->base);
 	if (ret)
 		return ret;
 
@@ -1614,18 +1597,18 @@ gk104_ram_ctor(struct nvkm_fb *fb, struct nvkm_ram **pram, u32 maskaddr)
 	}
 
 	/* lookup memory voltage gpios */
-	ret = nvkm_gpio_find(gpio, 0, 0x18, DCB_GPIO_UNUSED, &func);
+	ret = nvkm_gpio_find(device->gpio, 0, 0x18, DCB_GPIO_UNUSED, &gpio);
 	if (ret == 0) {
-		ram->fuc.r_gpioMV = ramfuc_reg(0x00d610 + (func.line * 0x04));
-		ram->fuc.r_funcMV[0] = (func.log[0] ^ 2) << 12;
-		ram->fuc.r_funcMV[1] = (func.log[1] ^ 2) << 12;
+		ram->fuc.r_gpioMV = ramfuc_reg(0x00d610 + (gpio.line * 0x04));
+		ram->fuc.r_funcMV[0] = (gpio.log[0] ^ 2) << 12;
+		ram->fuc.r_funcMV[1] = (gpio.log[1] ^ 2) << 12;
 	}
 
-	ret = nvkm_gpio_find(gpio, 0, 0x2e, DCB_GPIO_UNUSED, &func);
+	ret = nvkm_gpio_find(device->gpio, 0, 0x2e, DCB_GPIO_UNUSED, &gpio);
 	if (ret == 0) {
-		ram->fuc.r_gpio2E = ramfuc_reg(0x00d610 + (func.line * 0x04));
-		ram->fuc.r_func2E[0] = (func.log[0] ^ 2) << 12;
-		ram->fuc.r_func2E[1] = (func.log[1] ^ 2) << 12;
+		ram->fuc.r_gpio2E = ramfuc_reg(0x00d610 + (gpio.line * 0x04));
+		ram->fuc.r_func2E[0] = (gpio.log[0] ^ 2) << 12;
+		ram->fuc.r_func2E[1] = (gpio.log[1] ^ 2) << 12;
 	}
 
 	ram->fuc.r_gpiotrig = ramfuc_reg(0x00d604);
@@ -1717,3 +1700,24 @@ gk104_ram_ctor(struct nvkm_fb *fb, struct nvkm_ram **pram, u32 maskaddr)
 	ram->fuc.r_0x100750 = ramfuc_reg(0x100750);
 	return 0;
 }
+
+static const struct nvkm_ram_func
+gk104_ram = {
+	.upper = 0x0200000000,
+	.probe_fbp = gf100_ram_probe_fbp,
+	.probe_fbp_amount = gf108_ram_probe_fbp_amount,
+	.probe_fbpa_amount = gf100_ram_probe_fbpa_amount,
+	.dtor = gk104_ram_dtor,
+	.init = gk104_ram_init,
+	.get = gf100_ram_get,
+	.put = gf100_ram_put,
+	.calc = gk104_ram_calc,
+	.prog = gk104_ram_prog,
+	.tidy = gk104_ram_tidy,
+};
+
+int
+gk104_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram)
+{
+	return gk104_ram_new_(&gk104_ram, fb, pram);
+}

+ 24 - 1
drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgm107.c

@@ -23,8 +23,31 @@
  */
 #include "ram.h"
 
+u32
+gm107_ram_probe_fbp(const struct nvkm_ram_func *func,
+		    struct nvkm_device *device, int fbp, int *pltcs)
+{
+	u32 fbpao = nvkm_rd32(device, 0x021c14);
+	return func->probe_fbp_amount(func, fbpao, device, fbp, pltcs);
+}
+
+static const struct nvkm_ram_func
+gm107_ram = {
+	.upper = 0x1000000000,
+	.probe_fbp = gm107_ram_probe_fbp,
+	.probe_fbp_amount = gf108_ram_probe_fbp_amount,
+	.probe_fbpa_amount = gf100_ram_probe_fbpa_amount,
+	.dtor = gk104_ram_dtor,
+	.init = gk104_ram_init,
+	.get = gf100_ram_get,
+	.put = gf100_ram_put,
+	.calc = gk104_ram_calc,
+	.prog = gk104_ram_prog,
+	.tidy = gk104_ram_tidy,
+};
+
 int
 gm107_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram)
 {
-	return gk104_ram_ctor(fb, pram, 0x021c14);
+	return gk104_ram_new_(&gm107_ram, fb, pram);
 }

+ 68 - 0
drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgm200.c

@@ -0,0 +1,68 @@
+/*
+ * Copyright 2017 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs <bskeggs@redhat.com>
+ */
+#include "ram.h"
+
+u32
+gm200_ram_probe_fbp_amount(const struct nvkm_ram_func *func, u32 fbpao,
+			   struct nvkm_device *device, int fbp, int *pltcs)
+{
+	u32 ltcs  = nvkm_rd32(device, 0x022450);
+	u32 fbpas = nvkm_rd32(device, 0x022458);
+	u32 fbpa  = fbp * fbpas;
+	u32 size  = 0;
+	if (!(nvkm_rd32(device, 0x021d38) & BIT(fbp))) {
+		u32 ltco = nvkm_rd32(device, 0x021d70 + (fbp * 4));
+		u32 ltcm = ~ltco & ((1 << ltcs) - 1);
+
+		while (fbpas--) {
+			if (!(fbpao & (1 << fbpa)))
+				size += func->probe_fbpa_amount(device, fbpa);
+			fbpa++;
+		}
+
+		*pltcs = hweight32(ltcm);
+	}
+	return size;
+}
+
+static const struct nvkm_ram_func
+gm200_ram = {
+	.upper = 0x1000000000,
+	.probe_fbp = gm107_ram_probe_fbp,
+	.probe_fbp_amount = gm200_ram_probe_fbp_amount,
+	.probe_fbpa_amount = gf100_ram_probe_fbpa_amount,
+	.dtor = gk104_ram_dtor,
+	.init = gk104_ram_init,
+	.get = gf100_ram_get,
+	.put = gf100_ram_put,
+	.calc = gk104_ram_calc,
+	.prog = gk104_ram_prog,
+	.tidy = gk104_ram_tidy,
+};
+
+int
+gm200_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram)
+{
+	return gk104_ram_new_(&gm200_ram, fb, pram);
+}

+ 14 - 54
drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramgp100.c

@@ -76,8 +76,18 @@ gp100_ram_init(struct nvkm_ram *ram)
 	return 0;
 }
 
+static u32
+gp100_ram_probe_fbpa(struct nvkm_device *device, int fbpa)
+{
+	return nvkm_rd32(device, 0x90020c + (fbpa * 0x4000));
+}
+
 static const struct nvkm_ram_func
-gp100_ram_func = {
+gp100_ram = {
+	.upper = 0x1000000000,
+	.probe_fbp = gm107_ram_probe_fbp,
+	.probe_fbp_amount = gm200_ram_probe_fbp_amount,
+	.probe_fbpa_amount = gp100_ram_probe_fbpa,
 	.init = gp100_ram_init,
 	.get = gf100_ram_get,
 	.put = gf100_ram_put,
@@ -87,60 +97,10 @@ int
 gp100_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram)
 {
 	struct nvkm_ram *ram;
-	struct nvkm_subdev *subdev = &fb->subdev;
-	struct nvkm_device *device = subdev->device;
-	enum nvkm_ram_type type = nvkm_fb_bios_memtype(device->bios);
-	const u32 rsvd_head = ( 256 * 1024); /* vga memory */
-	const u32 rsvd_tail = (1024 * 1024); /* vbios etc */
-	u32 fbpa_num = nvkm_rd32(device, 0x02243c), fbpa;
-	u32 fbio_opt = nvkm_rd32(device, 0x021c14);
-	u64 part, size = 0, comm = ~0ULL;
-	bool mixed = false;
-	int ret;
-
-	nvkm_debug(subdev, "02243c: %08x\n", fbpa_num);
-	nvkm_debug(subdev, "021c14: %08x\n", fbio_opt);
-	for (fbpa = 0; fbpa < fbpa_num; fbpa++) {
-		if (!(fbio_opt & (1 << fbpa))) {
-			part = nvkm_rd32(device, 0x90020c + (fbpa * 0x4000));
-			nvkm_debug(subdev, "fbpa %02x: %lld MiB\n", fbpa, part);
-			part = part << 20;
-			if (part != comm) {
-				if (comm != ~0ULL)
-					mixed = true;
-				comm = min(comm, part);
-			}
-			size = size + part;
-		}
-	}
-
-	ret = nvkm_ram_new_(&gp100_ram_func, fb, type, size, 0, &ram);
-	*pram = ram;
-	if (ret)
-		return ret;
 
-	nvkm_mm_fini(&ram->vram);
+	if (!(ram = *pram = kzalloc(sizeof(*ram), GFP_KERNEL)))
+		return -ENOMEM;
 
-	if (mixed) {
-		ret = nvkm_mm_init(&ram->vram, rsvd_head >> NVKM_RAM_MM_SHIFT,
-				   ((comm * fbpa_num) - rsvd_head) >>
-				   NVKM_RAM_MM_SHIFT, 1);
-		if (ret)
-			return ret;
+	return gf100_ram_ctor(&gp100_ram, fb, ram);
 
-		ret = nvkm_mm_init(&ram->vram, (0x1000000000ULL + comm) >>
-				   NVKM_RAM_MM_SHIFT,
-				   (size - (comm * fbpa_num) - rsvd_tail) >>
-				   NVKM_RAM_MM_SHIFT, 1);
-		if (ret)
-			return ret;
-	} else {
-		ret = nvkm_mm_init(&ram->vram, rsvd_head >> NVKM_RAM_MM_SHIFT,
-				   (size - rsvd_head - rsvd_tail) >>
-				   NVKM_RAM_MM_SHIFT, 1);
-		if (ret)
-			return ret;
-	}
-
-	return 0;
 }

+ 7 - 7
drivers/gpu/drm/nouveau/nvkm/subdev/i2c/anx9805.c

@@ -134,7 +134,7 @@ struct anx9805_aux {
 
 static int
 anx9805_aux_xfer(struct nvkm_i2c_aux *base, bool retry,
-		 u8 type, u32 addr, u8 *data, u8 size)
+		 u8 type, u32 addr, u8 *data, u8 *size)
 {
 	struct anx9805_aux *aux = anx9805_aux(base);
 	struct anx9805_pad *pad = aux->pad;
@@ -143,7 +143,7 @@ anx9805_aux_xfer(struct nvkm_i2c_aux *base, bool retry,
 	u8 buf[16] = {};
 	u8 tmp;
 
-	AUX_DBG(&aux->base, "%02x %05x %d", type, addr, size);
+	AUX_DBG(&aux->base, "%02x %05x %d", type, addr, *size);
 
 	tmp = nvkm_rdi2cr(adap, pad->addr, 0x07) & ~0x04;
 	nvkm_wri2cr(adap, pad->addr, 0x07, tmp | 0x04);
@@ -152,12 +152,12 @@ anx9805_aux_xfer(struct nvkm_i2c_aux *base, bool retry,
 
 	nvkm_wri2cr(adap, aux->addr, 0xe4, 0x80);
 	if (!(type & 1)) {
-		memcpy(buf, data, size);
+		memcpy(buf, data, *size);
 		AUX_DBG(&aux->base, "%16ph", buf);
-		for (i = 0; i < size; i++)
+		for (i = 0; i < *size; i++)
 			nvkm_wri2cr(adap, aux->addr, 0xf0 + i, buf[i]);
 	}
-	nvkm_wri2cr(adap, aux->addr, 0xe5, ((size - 1) << 4) | type);
+	nvkm_wri2cr(adap, aux->addr, 0xe5, ((*size - 1) << 4) | type);
 	nvkm_wri2cr(adap, aux->addr, 0xe6, (addr & 0x000ff) >>  0);
 	nvkm_wri2cr(adap, aux->addr, 0xe7, (addr & 0x0ff00) >>  8);
 	nvkm_wri2cr(adap, aux->addr, 0xe8, (addr & 0xf0000) >> 16);
@@ -176,10 +176,10 @@ anx9805_aux_xfer(struct nvkm_i2c_aux *base, bool retry,
 	}
 
 	if (type & 1) {
-		for (i = 0; i < size; i++)
+		for (i = 0; i < *size; i++)
 			buf[i] = nvkm_rdi2cr(adap, aux->addr, 0xf0 + i);
 		AUX_DBG(&aux->base, "%16ph", buf);
-		memcpy(data, buf, size);
+		memcpy(data, buf, *size);
 	}
 
 	ret = 0;

+ 2 - 2
drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c

@@ -51,7 +51,7 @@ nvkm_i2c_aux_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
 			if (mcnt || remaining > 16)
 				cmd |= 4; /* MOT */
 
-			ret = aux->func->xfer(aux, true, cmd, msg->addr, ptr, cnt);
+			ret = aux->func->xfer(aux, true, cmd, msg->addr, ptr, &cnt);
 			if (ret < 0) {
 				nvkm_i2c_aux_release(aux);
 				return ret;
@@ -115,7 +115,7 @@ nvkm_i2c_aux_acquire(struct nvkm_i2c_aux *aux)
 
 int
 nvkm_i2c_aux_xfer(struct nvkm_i2c_aux *aux, bool retry, u8 type,
-		  u32 addr, u8 *data, u8 size)
+		  u32 addr, u8 *data, u8 *size)
 {
 	return aux->func->xfer(aux, retry, type, addr, data, size);
 }

+ 2 - 2
drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.h

@@ -4,7 +4,7 @@
 
 struct nvkm_i2c_aux_func {
 	int  (*xfer)(struct nvkm_i2c_aux *, bool retry, u8 type,
-		     u32 addr, u8 *data, u8 size);
+		     u32 addr, u8 *data, u8 *size);
 	int  (*lnk_ctl)(struct nvkm_i2c_aux *, int link_nr, int link_bw,
 			bool enhanced_framing);
 };
@@ -15,7 +15,7 @@ int nvkm_i2c_aux_new_(const struct nvkm_i2c_aux_func *, struct nvkm_i2c_pad *,
 		      int id, struct nvkm_i2c_aux **);
 void nvkm_i2c_aux_del(struct nvkm_i2c_aux **);
 int nvkm_i2c_aux_xfer(struct nvkm_i2c_aux *, bool retry, u8 type,
-		      u32 addr, u8 *data, u8 size);
+		      u32 addr, u8 *data, u8 *size);
 
 int g94_i2c_aux_new(struct nvkm_i2c_pad *, int, u8, struct nvkm_i2c_aux **);
 int gm200_i2c_aux_new(struct nvkm_i2c_pad *, int, u8, struct nvkm_i2c_aux **);

+ 6 - 5
drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxg94.c

@@ -74,7 +74,7 @@ g94_i2c_aux_init(struct g94_i2c_aux *aux)
 
 static int
 g94_i2c_aux_xfer(struct nvkm_i2c_aux *obj, bool retry,
-		 u8 type, u32 addr, u8 *data, u8 size)
+		 u8 type, u32 addr, u8 *data, u8 *size)
 {
 	struct g94_i2c_aux *aux = g94_i2c_aux(obj);
 	struct nvkm_device *device = aux->base.pad->i2c->subdev.device;
@@ -83,7 +83,7 @@ g94_i2c_aux_xfer(struct nvkm_i2c_aux *obj, bool retry,
 	u32 xbuf[4] = {};
 	int ret, i;
 
-	AUX_TRACE(&aux->base, "%d: %08x %d", type, addr, size);
+	AUX_TRACE(&aux->base, "%d: %08x %d", type, addr, *size);
 
 	ret = g94_i2c_aux_init(aux);
 	if (ret < 0)
@@ -97,7 +97,7 @@ g94_i2c_aux_xfer(struct nvkm_i2c_aux *obj, bool retry,
 	}
 
 	if (!(type & 1)) {
-		memcpy(xbuf, data, size);
+		memcpy(xbuf, data, *size);
 		for (i = 0; i < 16; i += 4) {
 			AUX_TRACE(&aux->base, "wr %08x", xbuf[i / 4]);
 			nvkm_wr32(device, 0x00e4c0 + base + i, xbuf[i / 4]);
@@ -107,7 +107,7 @@ g94_i2c_aux_xfer(struct nvkm_i2c_aux *obj, bool retry,
 	ctrl  = nvkm_rd32(device, 0x00e4e4 + base);
 	ctrl &= ~0x0001f0ff;
 	ctrl |= type << 12;
-	ctrl |= size - 1;
+	ctrl |= *size - 1;
 	nvkm_wr32(device, 0x00e4e0 + base, addr);
 
 	/* (maybe) retry transaction a number of times on failure... */
@@ -151,7 +151,8 @@ g94_i2c_aux_xfer(struct nvkm_i2c_aux *obj, bool retry,
 			xbuf[i / 4] = nvkm_rd32(device, 0x00e4d0 + base + i);
 			AUX_TRACE(&aux->base, "rd %08x", xbuf[i / 4]);
 		}
-		memcpy(data, xbuf, size);
+		memcpy(data, xbuf, *size);
+		*size = stat & 0x0000001f;
 	}
 
 out:

+ 6 - 5
drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxgm200.c

@@ -74,7 +74,7 @@ gm200_i2c_aux_init(struct gm200_i2c_aux *aux)
 
 static int
 gm200_i2c_aux_xfer(struct nvkm_i2c_aux *obj, bool retry,
-		   u8 type, u32 addr, u8 *data, u8 size)
+		   u8 type, u32 addr, u8 *data, u8 *size)
 {
 	struct gm200_i2c_aux *aux = gm200_i2c_aux(obj);
 	struct nvkm_device *device = aux->base.pad->i2c->subdev.device;
@@ -83,7 +83,7 @@ gm200_i2c_aux_xfer(struct nvkm_i2c_aux *obj, bool retry,
 	u32 xbuf[4] = {};
 	int ret, i;
 
-	AUX_TRACE(&aux->base, "%d: %08x %d", type, addr, size);
+	AUX_TRACE(&aux->base, "%d: %08x %d", type, addr, *size);
 
 	ret = gm200_i2c_aux_init(aux);
 	if (ret < 0)
@@ -97,7 +97,7 @@ gm200_i2c_aux_xfer(struct nvkm_i2c_aux *obj, bool retry,
 	}
 
 	if (!(type & 1)) {
-		memcpy(xbuf, data, size);
+		memcpy(xbuf, data, *size);
 		for (i = 0; i < 16; i += 4) {
 			AUX_TRACE(&aux->base, "wr %08x", xbuf[i / 4]);
 			nvkm_wr32(device, 0x00d930 + base + i, xbuf[i / 4]);
@@ -107,7 +107,7 @@ gm200_i2c_aux_xfer(struct nvkm_i2c_aux *obj, bool retry,
 	ctrl  = nvkm_rd32(device, 0x00d954 + base);
 	ctrl &= ~0x0001f0ff;
 	ctrl |= type << 12;
-	ctrl |= size - 1;
+	ctrl |= *size - 1;
 	nvkm_wr32(device, 0x00d950 + base, addr);
 
 	/* (maybe) retry transaction a number of times on failure... */
@@ -151,7 +151,8 @@ gm200_i2c_aux_xfer(struct nvkm_i2c_aux *obj, bool retry,
 			xbuf[i / 4] = nvkm_rd32(device, 0x00d940 + base + i);
 			AUX_TRACE(&aux->base, "rd %08x", xbuf[i / 4]);
 		}
-		memcpy(data, xbuf, size);
+		memcpy(data, xbuf, *size);
+		*size = stat & 0x0000001f;
 	}
 
 out:

+ 3 - 3
drivers/gpu/drm/nouveau/nvkm/subdev/ibus/gf100.c

@@ -30,7 +30,7 @@ gf100_ibus_intr_hub(struct nvkm_subdev *ibus, int i)
 	u32 addr = nvkm_rd32(device, 0x122120 + (i * 0x0400));
 	u32 data = nvkm_rd32(device, 0x122124 + (i * 0x0400));
 	u32 stat = nvkm_rd32(device, 0x122128 + (i * 0x0400));
-	nvkm_error(ibus, "HUB%d: %06x %08x (%08x)\n", i, addr, data, stat);
+	nvkm_debug(ibus, "HUB%d: %06x %08x (%08x)\n", i, addr, data, stat);
 	nvkm_mask(device, 0x122128 + (i * 0x0400), 0x00000200, 0x00000000);
 }
 
@@ -41,7 +41,7 @@ gf100_ibus_intr_rop(struct nvkm_subdev *ibus, int i)
 	u32 addr = nvkm_rd32(device, 0x124120 + (i * 0x0400));
 	u32 data = nvkm_rd32(device, 0x124124 + (i * 0x0400));
 	u32 stat = nvkm_rd32(device, 0x124128 + (i * 0x0400));
-	nvkm_error(ibus, "ROP%d: %06x %08x (%08x)\n", i, addr, data, stat);
+	nvkm_debug(ibus, "ROP%d: %06x %08x (%08x)\n", i, addr, data, stat);
 	nvkm_mask(device, 0x124128 + (i * 0x0400), 0x00000200, 0x00000000);
 }
 
@@ -52,7 +52,7 @@ gf100_ibus_intr_gpc(struct nvkm_subdev *ibus, int i)
 	u32 addr = nvkm_rd32(device, 0x128120 + (i * 0x0400));
 	u32 data = nvkm_rd32(device, 0x128124 + (i * 0x0400));
 	u32 stat = nvkm_rd32(device, 0x128128 + (i * 0x0400));
-	nvkm_error(ibus, "GPC%d: %06x %08x (%08x)\n", i, addr, data, stat);
+	nvkm_debug(ibus, "GPC%d: %06x %08x (%08x)\n", i, addr, data, stat);
 	nvkm_mask(device, 0x128128 + (i * 0x0400), 0x00000200, 0x00000000);
 }
 

+ 3 - 3
drivers/gpu/drm/nouveau/nvkm/subdev/ibus/gk104.c

@@ -30,7 +30,7 @@ gk104_ibus_intr_hub(struct nvkm_subdev *ibus, int i)
 	u32 addr = nvkm_rd32(device, 0x122120 + (i * 0x0800));
 	u32 data = nvkm_rd32(device, 0x122124 + (i * 0x0800));
 	u32 stat = nvkm_rd32(device, 0x122128 + (i * 0x0800));
-	nvkm_error(ibus, "HUB%d: %06x %08x (%08x)\n", i, addr, data, stat);
+	nvkm_debug(ibus, "HUB%d: %06x %08x (%08x)\n", i, addr, data, stat);
 	nvkm_mask(device, 0x122128 + (i * 0x0800), 0x00000200, 0x00000000);
 }
 
@@ -41,7 +41,7 @@ gk104_ibus_intr_rop(struct nvkm_subdev *ibus, int i)
 	u32 addr = nvkm_rd32(device, 0x124120 + (i * 0x0800));
 	u32 data = nvkm_rd32(device, 0x124124 + (i * 0x0800));
 	u32 stat = nvkm_rd32(device, 0x124128 + (i * 0x0800));
-	nvkm_error(ibus, "ROP%d: %06x %08x (%08x)\n", i, addr, data, stat);
+	nvkm_debug(ibus, "ROP%d: %06x %08x (%08x)\n", i, addr, data, stat);
 	nvkm_mask(device, 0x124128 + (i * 0x0800), 0x00000200, 0x00000000);
 }
 
@@ -52,7 +52,7 @@ gk104_ibus_intr_gpc(struct nvkm_subdev *ibus, int i)
 	u32 addr = nvkm_rd32(device, 0x128120 + (i * 0x0800));
 	u32 data = nvkm_rd32(device, 0x128124 + (i * 0x0800));
 	u32 stat = nvkm_rd32(device, 0x128128 + (i * 0x0800));
-	nvkm_error(ibus, "GPC%d: %06x %08x (%08x)\n", i, addr, data, stat);
+	nvkm_debug(ibus, "GPC%d: %06x %08x (%08x)\n", i, addr, data, stat);
 	nvkm_mask(device, 0x128128 + (i * 0x0800), 0x00000200, 0x00000000);
 }
 

+ 13 - 2
drivers/gpu/drm/nouveau/nvkm/subdev/pmu/base.c

@@ -23,6 +23,7 @@
  */
 #include "priv.h"
 
+#include <core/msgqueue.h>
 #include <subdev/timer.h>
 
 void
@@ -85,7 +86,8 @@ nvkm_pmu_reset(struct nvkm_pmu *pmu)
 	);
 
 	/* Reset. */
-	pmu->func->reset(pmu);
+	if (pmu->func->reset)
+		pmu->func->reset(pmu);
 
 	/* Wait for IMEM/DMEM scrubbing to be complete. */
 	nvkm_msec(device, 2000,
@@ -113,10 +115,18 @@ nvkm_pmu_init(struct nvkm_subdev *subdev)
 	return ret;
 }
 
+static int
+nvkm_pmu_oneinit(struct nvkm_subdev *subdev)
+{
+	struct nvkm_pmu *pmu = nvkm_pmu(subdev);
+	return nvkm_falcon_v1_new(&pmu->subdev, "PMU", 0x10a000, &pmu->falcon);
+}
+
 static void *
 nvkm_pmu_dtor(struct nvkm_subdev *subdev)
 {
 	struct nvkm_pmu *pmu = nvkm_pmu(subdev);
+	nvkm_msgqueue_del(&pmu->queue);
 	nvkm_falcon_del(&pmu->falcon);
 	return nvkm_pmu(subdev);
 }
@@ -125,6 +135,7 @@ static const struct nvkm_subdev_func
 nvkm_pmu = {
 	.dtor = nvkm_pmu_dtor,
 	.preinit = nvkm_pmu_preinit,
+	.oneinit = nvkm_pmu_oneinit,
 	.init = nvkm_pmu_init,
 	.fini = nvkm_pmu_fini,
 	.intr = nvkm_pmu_intr,
@@ -138,7 +149,7 @@ nvkm_pmu_ctor(const struct nvkm_pmu_func *func, struct nvkm_device *device,
 	pmu->func = func;
 	INIT_WORK(&pmu->recv.work, nvkm_pmu_recv);
 	init_waitqueue_head(&pmu->recv.wait);
-	return nvkm_falcon_v1_new(&pmu->subdev, "PMU", 0x10a000, &pmu->falcon);
+	return 0;
 }
 
 int

+ 17 - 2
drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gm20b.c

@@ -20,15 +20,30 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
+#include <engine/falcon.h>
+#include <core/msgqueue.h>
 #include "priv.h"
 
+static void
+gm20b_pmu_recv(struct nvkm_pmu *pmu)
+{
+	nvkm_msgqueue_recv(pmu->queue);
+}
+
 static const struct nvkm_pmu_func
 gm20b_pmu = {
-	.reset = gt215_pmu_reset,
+	.intr = gt215_pmu_intr,
+	.recv = gm20b_pmu_recv,
 };
 
 int
 gm20b_pmu_new(struct nvkm_device *device, int index, struct nvkm_pmu **ppmu)
 {
-	return nvkm_pmu_new_(&gm20b_pmu, device, index, ppmu);
+	int ret;
+
+	ret = nvkm_pmu_new_(&gm20b_pmu, device, index, ppmu);
+	if (ret)
+		return ret;
+
+	return 0;
 }

+ 6 - 0
drivers/gpu/drm/nouveau/nvkm/subdev/secboot/Kbuild

@@ -1,7 +1,13 @@
 nvkm-y += nvkm/subdev/secboot/base.o
+nvkm-y += nvkm/subdev/secboot/hs_ucode.o
 nvkm-y += nvkm/subdev/secboot/ls_ucode_gr.o
+nvkm-y += nvkm/subdev/secboot/ls_ucode_msgqueue.o
 nvkm-y += nvkm/subdev/secboot/acr.o
 nvkm-y += nvkm/subdev/secboot/acr_r352.o
 nvkm-y += nvkm/subdev/secboot/acr_r361.o
+nvkm-y += nvkm/subdev/secboot/acr_r364.o
+nvkm-y += nvkm/subdev/secboot/acr_r367.o
+nvkm-y += nvkm/subdev/secboot/acr_r375.o
 nvkm-y += nvkm/subdev/secboot/gm200.o
 nvkm-y += nvkm/subdev/secboot/gm20b.o
+nvkm-y += nvkm/subdev/secboot/gp102.o

+ 6 - 5
drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr.h

@@ -37,12 +37,10 @@ struct nvkm_acr_func {
 	void (*dtor)(struct nvkm_acr *);
 	int (*oneinit)(struct nvkm_acr *, struct nvkm_secboot *);
 	int (*fini)(struct nvkm_acr *, struct nvkm_secboot *, bool);
-	int (*load)(struct nvkm_acr *, struct nvkm_secboot *,
+	int (*load)(struct nvkm_acr *, struct nvkm_falcon *,
 		    struct nvkm_gpuobj *, u64);
 	int (*reset)(struct nvkm_acr *, struct nvkm_secboot *,
 		     enum nvkm_secboot_falcon);
-	int (*start)(struct nvkm_acr *, struct nvkm_secboot *,
-		     enum nvkm_secboot_falcon);
 };
 
 /**
@@ -50,7 +48,7 @@ struct nvkm_acr_func {
  *
  * @boot_falcon: ID of the falcon that will perform secure boot
  * @managed_falcons: bitfield of falcons managed by this ACR
- * @start_address: virtual start address of the HS bootloader
+ * @optional_falcons: bitfield of falcons we can live without
  */
 struct nvkm_acr {
 	const struct nvkm_acr_func *func;
@@ -58,12 +56,15 @@ struct nvkm_acr {
 
 	enum nvkm_secboot_falcon boot_falcon;
 	unsigned long managed_falcons;
-	u32 start_address;
+	unsigned long optional_falcons;
 };
 
 void *nvkm_acr_load_firmware(const struct nvkm_subdev *, const char *, size_t);
 
 struct nvkm_acr *acr_r352_new(unsigned long);
 struct nvkm_acr *acr_r361_new(unsigned long);
+struct nvkm_acr *acr_r364_new(unsigned long);
+struct nvkm_acr *acr_r367_new(enum nvkm_secboot_falcon, unsigned long);
+struct nvkm_acr *acr_r375_new(enum nvkm_secboot_falcon, unsigned long);
 
 #endif

+ 442 - 147
drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r352.c

@@ -21,35 +21,16 @@
  */
 
 #include "acr_r352.h"
+#include "hs_ucode.h"
 
 #include <core/gpuobj.h>
 #include <core/firmware.h>
 #include <engine/falcon.h>
-
-/**
- * struct hsf_fw_header - HS firmware descriptor
- * @sig_dbg_offset:	offset of the debug signature
- * @sig_dbg_size:	size of the debug signature
- * @sig_prod_offset:	offset of the production signature
- * @sig_prod_size:	size of the production signature
- * @patch_loc:		offset of the offset (sic) of where the signature is
- * @patch_sig:		offset of the offset (sic) to add to sig_*_offset
- * @hdr_offset:		offset of the load header (see struct hs_load_header)
- * @hdr_size:		size of above header
- *
- * This structure is embedded in the HS firmware image at
- * hs_bin_hdr.header_offset.
- */
-struct hsf_fw_header {
-	u32 sig_dbg_offset;
-	u32 sig_dbg_size;
-	u32 sig_prod_offset;
-	u32 sig_prod_size;
-	u32 patch_loc;
-	u32 patch_sig;
-	u32 hdr_offset;
-	u32 hdr_size;
-};
+#include <subdev/mc.h>
+#include <subdev/timer.h>
+#include <subdev/pmu.h>
+#include <core/msgqueue.h>
+#include <engine/sec2.h>
 
 /**
  * struct acr_r352_flcn_bl_desc - DMEM bootloader descriptor
@@ -95,15 +76,14 @@ struct acr_r352_flcn_bl_desc {
  */
 static void
 acr_r352_generate_flcn_bl_desc(const struct nvkm_acr *acr,
-			       const struct ls_ucode_img *_img, u64 wpr_addr,
+			       const struct ls_ucode_img *img, u64 wpr_addr,
 			       void *_desc)
 {
-	struct ls_ucode_img_r352 *img = ls_ucode_img_r352(_img);
 	struct acr_r352_flcn_bl_desc *desc = _desc;
-	const struct ls_ucode_img_desc *pdesc = &_img->ucode_desc;
+	const struct ls_ucode_img_desc *pdesc = &img->ucode_desc;
 	u64 base, addr_code, addr_data;
 
-	base = wpr_addr + img->lsb_header.ucode_off + pdesc->app_start_offset;
+	base = wpr_addr + img->ucode_off + pdesc->app_start_offset;
 	addr_code = (base + pdesc->app_resident_code_offset) >> 8;
 	addr_data = (base + pdesc->app_resident_data_offset) >> 8;
 
@@ -166,6 +146,96 @@ struct hsflcn_acr_desc {
  * Low-secure blob creation
  */
 
+/**
+ * struct acr_r352_lsf_lsb_header - LS firmware header
+ * @signature:		signature to verify the firmware against
+ * @ucode_off:		offset of the ucode blob in the WPR region. The ucode
+ *                      blob contains the bootloader, code and data of the
+ *                      LS falcon
+ * @ucode_size:		size of the ucode blob, including bootloader
+ * @data_size:		size of the ucode blob data
+ * @bl_code_size:	size of the bootloader code
+ * @bl_imem_off:	offset in imem of the bootloader
+ * @bl_data_off:	offset of the bootloader data in WPR region
+ * @bl_data_size:	size of the bootloader data
+ * @app_code_off:	offset of the app code relative to ucode_off
+ * @app_code_size:	size of the app code
+ * @app_data_off:	offset of the app data relative to ucode_off
+ * @app_data_size:	size of the app data
+ * @flags:		flags for the secure bootloader
+ *
+ * This structure is written into the WPR region for each managed falcon. Each
+ * instance is referenced by the lsb_offset member of the corresponding
+ * lsf_wpr_header.
+ */
+struct acr_r352_lsf_lsb_header {
+	/**
+	 * LS falcon signatures
+	 * @prd_keys:		signature to use in production mode
+	 * @dgb_keys:		signature to use in debug mode
+	 * @b_prd_present:	whether the production key is present
+	 * @b_dgb_present:	whether the debug key is present
+	 * @falcon_id:		ID of the falcon the ucode applies to
+	 */
+	struct {
+		u8 prd_keys[2][16];
+		u8 dbg_keys[2][16];
+		u32 b_prd_present;
+		u32 b_dbg_present;
+		u32 falcon_id;
+	} signature;
+	u32 ucode_off;
+	u32 ucode_size;
+	u32 data_size;
+	u32 bl_code_size;
+	u32 bl_imem_off;
+	u32 bl_data_off;
+	u32 bl_data_size;
+	u32 app_code_off;
+	u32 app_code_size;
+	u32 app_data_off;
+	u32 app_data_size;
+	u32 flags;
+};
+
+/**
+ * struct acr_r352_lsf_wpr_header - LS blob WPR Header
+ * @falcon_id:		LS falcon ID
+ * @lsb_offset:		offset of the lsb_lsf_header in the WPR region
+ * @bootstrap_owner:	secure falcon reponsible for bootstrapping the LS falcon
+ * @lazy_bootstrap:	skip bootstrapping by ACR
+ * @status:		bootstrapping status
+ *
+ * An array of these is written at the beginning of the WPR region, one for
+ * each managed falcon. The array is terminated by an instance which falcon_id
+ * is LSF_FALCON_ID_INVALID.
+ */
+struct acr_r352_lsf_wpr_header {
+	u32 falcon_id;
+	u32 lsb_offset;
+	u32 bootstrap_owner;
+	u32 lazy_bootstrap;
+	u32 status;
+#define LSF_IMAGE_STATUS_NONE				0
+#define LSF_IMAGE_STATUS_COPY				1
+#define LSF_IMAGE_STATUS_VALIDATION_CODE_FAILED		2
+#define LSF_IMAGE_STATUS_VALIDATION_DATA_FAILED		3
+#define LSF_IMAGE_STATUS_VALIDATION_DONE		4
+#define LSF_IMAGE_STATUS_VALIDATION_SKIPPED		5
+#define LSF_IMAGE_STATUS_BOOTSTRAP_READY		6
+};
+
+/**
+ * struct ls_ucode_img_r352 - ucode image augmented with r352 headers
+ */
+struct ls_ucode_img_r352 {
+	struct ls_ucode_img base;
+
+	struct acr_r352_lsf_wpr_header wpr_header;
+	struct acr_r352_lsf_lsb_header lsb_header;
+};
+#define ls_ucode_img_r352(i) container_of(i, struct ls_ucode_img_r352, base)
+
 /**
  * ls_ucode_img_load() - create a lsf_ucode_img and load it
  */
@@ -255,7 +325,7 @@ acr_r352_ls_img_fill_headers(struct acr_r352 *acr,
 	 * image size
 	 */
 	offset = ALIGN(offset, LSF_UCODE_DATA_ALIGN);
-	lhdr->ucode_off = offset;
+	_img->ucode_off = lhdr->ucode_off = offset;
 	offset += _img->ucode_size;
 
 	/*
@@ -341,7 +411,7 @@ acr_r352_ls_fill_headers(struct acr_r352 *acr, struct list_head *imgs)
  */
 int
 acr_r352_ls_write_wpr(struct acr_r352 *acr, struct list_head *imgs,
-		      struct nvkm_gpuobj *wpr_blob, u32 wpr_addr)
+		      struct nvkm_gpuobj *wpr_blob, u64 wpr_addr)
 {
 	struct ls_ucode_img *_img;
 	u32 pos = 0;
@@ -381,8 +451,8 @@ acr_r352_ls_write_wpr(struct acr_r352 *acr, struct list_head *imgs,
 	return 0;
 }
 
-/* Both size and address of WPR need to be 128K-aligned */
-#define WPR_ALIGNMENT	0x20000
+/* Both size and address of WPR need to be 256K-aligned */
+#define WPR_ALIGNMENT	0x40000
 /**
  * acr_r352_prepare_ls_blob() - prepare the LS blob
  *
@@ -399,7 +469,7 @@ acr_r352_prepare_ls_blob(struct acr_r352 *acr, u64 wpr_addr, u32 wpr_size)
 	struct ls_ucode_img *img, *t;
 	unsigned long managed_falcons = acr->base.managed_falcons;
 	int managed_count = 0;
-	u32 image_wpr_size;
+	u32 image_wpr_size, ls_blob_size;
 	int falcon_id;
 	int ret;
 
@@ -411,6 +481,12 @@ acr_r352_prepare_ls_blob(struct acr_r352 *acr, u64 wpr_addr, u32 wpr_size)
 
 		img = acr->func->ls_ucode_img_load(acr, falcon_id);
 		if (IS_ERR(img)) {
+			if (acr->base.optional_falcons & BIT(falcon_id)) {
+				managed_falcons &= ~BIT(falcon_id);
+				nvkm_info(subdev, "skipping %s falcon...\n",
+					  nvkm_secboot_falcon_name[falcon_id]);
+				continue;
+			}
 			ret = PTR_ERR(img);
 			goto cleanup;
 		}
@@ -419,6 +495,24 @@ acr_r352_prepare_ls_blob(struct acr_r352 *acr, u64 wpr_addr, u32 wpr_size)
 		managed_count++;
 	}
 
+	/* Commit the actual list of falcons we will manage from now on */
+	acr->base.managed_falcons = managed_falcons;
+
+	/*
+	 * If the boot falcon has a firmare, let it manage the bootstrap of other
+	 * falcons.
+	 */
+	if (acr->func->ls_func[acr->base.boot_falcon] &&
+	    (managed_falcons & BIT(acr->base.boot_falcon))) {
+		for_each_set_bit(falcon_id, &managed_falcons,
+				 NVKM_SECBOOT_FALCON_END) {
+			if (falcon_id == acr->base.boot_falcon)
+				continue;
+
+			acr->lazy_bootstrap |= BIT(falcon_id);
+		}
+	}
+
 	/*
 	 * Fill the WPR and LSF headers with the right offsets and compute
 	 * required WPR size
@@ -426,8 +520,17 @@ acr_r352_prepare_ls_blob(struct acr_r352 *acr, u64 wpr_addr, u32 wpr_size)
 	image_wpr_size = acr->func->ls_fill_headers(acr, &imgs);
 	image_wpr_size = ALIGN(image_wpr_size, WPR_ALIGNMENT);
 
+	ls_blob_size = image_wpr_size;
+
+	/*
+	 * If we need a shadow area, allocate twice the size and use the
+	 * upper half as WPR
+	 */
+	if (wpr_size == 0 && acr->func->shadow_blob)
+		ls_blob_size *= 2;
+
 	/* Allocate GPU object that will contain the WPR region */
-	ret = nvkm_gpuobj_new(subdev->device, image_wpr_size, WPR_ALIGNMENT,
+	ret = nvkm_gpuobj_new(subdev->device, ls_blob_size, WPR_ALIGNMENT,
 			      false, NULL, &acr->ls_blob);
 	if (ret)
 		goto cleanup;
@@ -438,6 +541,9 @@ acr_r352_prepare_ls_blob(struct acr_r352 *acr, u64 wpr_addr, u32 wpr_size)
 	/* If WPR address and size are not fixed, set them to fit the LS blob */
 	if (wpr_size == 0) {
 		wpr_addr = acr->ls_blob->addr;
+		if (acr->func->shadow_blob)
+			wpr_addr += acr->ls_blob->size / 2;
+
 		wpr_size = image_wpr_size;
 	/*
 	 * But if the WPR region is set by the bootloader, it is illegal for
@@ -469,41 +575,17 @@ cleanup:
 
 
 
-/**
- * acr_r352_hsf_patch_signature() - patch HS blob with correct signature
- */
-static void
-acr_r352_hsf_patch_signature(struct nvkm_secboot *sb, void *acr_image)
-{
-	struct fw_bin_header *hsbin_hdr = acr_image;
-	struct hsf_fw_header *fw_hdr = acr_image + hsbin_hdr->header_offset;
-	void *hs_data = acr_image + hsbin_hdr->data_offset;
-	void *sig;
-	u32 sig_size;
-
-	/* Falcon in debug or production mode? */
-	if (sb->boot_falcon->debug) {
-		sig = acr_image + fw_hdr->sig_dbg_offset;
-		sig_size = fw_hdr->sig_dbg_size;
-	} else {
-		sig = acr_image + fw_hdr->sig_prod_offset;
-		sig_size = fw_hdr->sig_prod_size;
-	}
-
-	/* Patch signature */
-	memcpy(hs_data + fw_hdr->patch_loc, sig + fw_hdr->patch_sig, sig_size);
-}
-
-static void
+void
 acr_r352_fixup_hs_desc(struct acr_r352 *acr, struct nvkm_secboot *sb,
-		       struct hsflcn_acr_desc *desc)
+		       void *_desc)
 {
+	struct hsflcn_acr_desc *desc = _desc;
 	struct nvkm_gpuobj *ls_blob = acr->ls_blob;
 
 	/* WPR region information if WPR is not fixed */
 	if (sb->wpr_size == 0) {
-		u32 wpr_start = ls_blob->addr;
-		u32 wpr_end = wpr_start + ls_blob->size;
+		u64 wpr_start = ls_blob->addr;
+		u64 wpr_end = wpr_start + ls_blob->size;
 
 		desc->wpr_region_id = 1;
 		desc->regions.no_regions = 2;
@@ -533,8 +615,8 @@ acr_r352_generate_hs_bl_desc(const struct hsf_load_header *hdr, void *_bl_desc,
 	bl_desc->code_dma_base = lower_32_bits(addr_code);
 	bl_desc->non_sec_code_off = hdr->non_sec_code_off;
 	bl_desc->non_sec_code_size = hdr->non_sec_code_size;
-	bl_desc->sec_code_off = hdr->app[0].sec_code_off;
-	bl_desc->sec_code_size = hdr->app[0].sec_code_size;
+	bl_desc->sec_code_off = hsf_load_header_app_off(hdr, 0);
+	bl_desc->sec_code_size = hsf_load_header_app_size(hdr, 0);
 	bl_desc->code_entry_point = 0;
 	bl_desc->data_dma_base = lower_32_bits(addr_data);
 	bl_desc->data_size = hdr->data_size;
@@ -562,7 +644,7 @@ acr_r352_prepare_hs_blob(struct acr_r352 *acr, struct nvkm_secboot *sb,
 	void *acr_data;
 	int ret;
 
-	acr_image = nvkm_acr_load_firmware(subdev, fw, 0);
+	acr_image = hs_ucode_load_blob(subdev, sb->boot_falcon, fw);
 	if (IS_ERR(acr_image))
 		return PTR_ERR(acr_image);
 
@@ -571,15 +653,12 @@ acr_r352_prepare_hs_blob(struct acr_r352 *acr, struct nvkm_secboot *sb,
 	load_hdr = acr_image + fw_hdr->hdr_offset;
 	acr_data = acr_image + hsbin_hdr->data_offset;
 
-	/* Patch signature */
-	acr_r352_hsf_patch_signature(sb, acr_image);
-
 	/* Patch descriptor with WPR information? */
 	if (patch) {
 		struct hsflcn_acr_desc *desc;
 
 		desc = acr_data + load_hdr->data_dma_base;
-		acr_r352_fixup_hs_desc(acr, sb, desc);
+		acr->func->fixup_hs_desc(acr, sb, desc);
 	}
 
 	if (load_hdr->num_apps > ACR_R352_MAX_APPS) {
@@ -589,7 +668,7 @@ acr_r352_prepare_hs_blob(struct acr_r352 *acr, struct nvkm_secboot *sb,
 		goto cleanup;
 	}
 	memcpy(load_header, load_hdr, sizeof(*load_header) +
-			       (sizeof(load_hdr->app[0]) * load_hdr->num_apps));
+			  (sizeof(load_hdr->apps[0]) * 2 * load_hdr->num_apps));
 
 	/* Create ACR blob and copy HS data to it */
 	ret = nvkm_gpuobj_new(subdev->device, ALIGN(hsbin_hdr->data_size, 256),
@@ -607,30 +686,6 @@ cleanup:
 	return ret;
 }
 
-static int
-acr_r352_prepare_hsbl_blob(struct acr_r352 *acr)
-{
-	const struct nvkm_subdev *subdev = acr->base.subdev;
-	struct fw_bin_header *hdr;
-	struct fw_bl_desc *hsbl_desc;
-
-	acr->hsbl_blob = nvkm_acr_load_firmware(subdev, "acr/bl", 0);
-	if (IS_ERR(acr->hsbl_blob)) {
-		int ret = PTR_ERR(acr->hsbl_blob);
-
-		acr->hsbl_blob = NULL;
-		return ret;
-	}
-
-	hdr = acr->hsbl_blob;
-	hsbl_desc = acr->hsbl_blob + hdr->header_offset;
-
-	/* virtual start address for boot vector */
-	acr->base.start_address = hsbl_desc->start_tag << 8;
-
-	return 0;
-}
-
 /**
  * acr_r352_load_blobs - load blobs common to all ACR V1 versions.
  *
@@ -641,6 +696,7 @@ acr_r352_prepare_hsbl_blob(struct acr_r352 *acr)
 int
 acr_r352_load_blobs(struct acr_r352 *acr, struct nvkm_secboot *sb)
 {
+	struct nvkm_subdev *subdev = &sb->subdev;
 	int ret;
 
 	/* Firmware already loaded? */
@@ -672,9 +728,24 @@ acr_r352_load_blobs(struct acr_r352 *acr, struct nvkm_secboot *sb)
 
 	/* Load the HS firmware bootloader */
 	if (!acr->hsbl_blob) {
-		ret = acr_r352_prepare_hsbl_blob(acr);
-		if (ret)
+		acr->hsbl_blob = nvkm_acr_load_firmware(subdev, "acr/bl", 0);
+		if (IS_ERR(acr->hsbl_blob)) {
+			ret = PTR_ERR(acr->hsbl_blob);
+			acr->hsbl_blob = NULL;
 			return ret;
+		}
+
+		if (acr->base.boot_falcon != NVKM_SECBOOT_FALCON_PMU) {
+			acr->hsbl_unload_blob = nvkm_acr_load_firmware(subdev,
+							    "acr/unload_bl", 0);
+			if (IS_ERR(acr->hsbl_unload_blob)) {
+				ret = PTR_ERR(acr->hsbl_unload_blob);
+				acr->hsbl_unload_blob = NULL;
+				return ret;
+			}
+		} else {
+			acr->hsbl_unload_blob = acr->hsbl_blob;
+		}
 	}
 
 	acr->firmware_ok = true;
@@ -684,35 +755,42 @@ acr_r352_load_blobs(struct acr_r352 *acr, struct nvkm_secboot *sb)
 }
 
 /**
- * acr_r352_load() - prepare HS falcon to run the specified blob, mapped
- * at GPU address offset.
+ * acr_r352_load() - prepare HS falcon to run the specified blob, mapped.
+ *
+ * Returns the start address to use, or a negative error value.
  */
 static int
-acr_r352_load(struct nvkm_acr *_acr, struct nvkm_secboot *sb,
+acr_r352_load(struct nvkm_acr *_acr, struct nvkm_falcon *falcon,
 	      struct nvkm_gpuobj *blob, u64 offset)
 {
 	struct acr_r352 *acr = acr_r352(_acr);
-	struct nvkm_falcon *falcon = sb->boot_falcon;
-	struct fw_bin_header *hdr = acr->hsbl_blob;
-	struct fw_bl_desc *hsbl_desc = acr->hsbl_blob + hdr->header_offset;
-	void *blob_data = acr->hsbl_blob + hdr->data_offset;
-	void *hsbl_code = blob_data + hsbl_desc->code_off;
-	void *hsbl_data = blob_data + hsbl_desc->data_off;
-	u32 code_size = ALIGN(hsbl_desc->code_size, 256);
-	const struct hsf_load_header *load_hdr;
 	const u32 bl_desc_size = acr->func->hs_bl_desc_size;
+	const struct hsf_load_header *load_hdr;
+	struct fw_bin_header *bl_hdr;
+	struct fw_bl_desc *hsbl_desc;
+	void *bl, *blob_data, *hsbl_code, *hsbl_data;
+	u32 code_size;
 	u8 bl_desc[bl_desc_size];
 
 	/* Find the bootloader descriptor for our blob and copy it */
 	if (blob == acr->load_blob) {
 		load_hdr = &acr->load_bl_header;
+		bl = acr->hsbl_blob;
 	} else if (blob == acr->unload_blob) {
 		load_hdr = &acr->unload_bl_header;
+		bl = acr->hsbl_unload_blob;
 	} else {
 		nvkm_error(_acr->subdev, "invalid secure boot blob!\n");
 		return -EINVAL;
 	}
 
+	bl_hdr = bl;
+	hsbl_desc = bl + bl_hdr->header_offset;
+	blob_data = bl + bl_hdr->data_offset;
+	hsbl_code = blob_data + hsbl_desc->code_off;
+	hsbl_data = blob_data + hsbl_desc->data_off;
+	code_size = ALIGN(hsbl_desc->code_size, 256);
+
 	/*
 	 * Copy HS bootloader data
 	 */
@@ -732,23 +810,32 @@ acr_r352_load(struct nvkm_acr *_acr, struct nvkm_secboot *sb,
 	nvkm_falcon_load_dmem(falcon, bl_desc, hsbl_desc->dmem_load_off,
 			      bl_desc_size, 0);
 
-	return 0;
+	return hsbl_desc->start_tag << 8;
 }
 
 static int
 acr_r352_shutdown(struct acr_r352 *acr, struct nvkm_secboot *sb)
 {
+	struct nvkm_subdev *subdev = &sb->subdev;
 	int i;
 
 	/* Run the unload blob to unprotect the WPR region */
 	if (acr->unload_blob && sb->wpr_set) {
 		int ret;
 
-		nvkm_debug(&sb->subdev, "running HS unload blob\n");
-		ret = sb->func->run_blob(sb, acr->unload_blob);
-		if (ret)
+		nvkm_debug(subdev, "running HS unload blob\n");
+		ret = sb->func->run_blob(sb, acr->unload_blob, sb->halt_falcon);
+		if (ret < 0)
 			return ret;
-		nvkm_debug(&sb->subdev, "HS unload blob completed\n");
+		/*
+		 * Unload blob will return this error code - it is not an error
+		 * and the expected behavior on RM as well
+		 */
+		if (ret && ret != 0x1d) {
+			nvkm_error(subdev, "HS unload failed, ret 0x%08x", ret);
+			return -EINVAL;
+		}
+		nvkm_debug(subdev, "HS unload blob completed\n");
 	}
 
 	for (i = 0; i < NVKM_SECBOOT_FALCON_END; i++)
@@ -759,9 +846,44 @@ acr_r352_shutdown(struct acr_r352 *acr, struct nvkm_secboot *sb)
 	return 0;
 }
 
+/**
+ * Check if the WPR region has been indeed set by the ACR firmware, and
+ * matches where it should be.
+ */
+static bool
+acr_r352_wpr_is_set(const struct acr_r352 *acr, const struct nvkm_secboot *sb)
+{
+	const struct nvkm_subdev *subdev = &sb->subdev;
+	const struct nvkm_device *device = subdev->device;
+	u64 wpr_lo, wpr_hi;
+	u64 wpr_range_lo, wpr_range_hi;
+
+	nvkm_wr32(device, 0x100cd4, 0x2);
+	wpr_lo = (nvkm_rd32(device, 0x100cd4) & ~0xff);
+	wpr_lo <<= 8;
+	nvkm_wr32(device, 0x100cd4, 0x3);
+	wpr_hi = (nvkm_rd32(device, 0x100cd4) & ~0xff);
+	wpr_hi <<= 8;
+
+	if (sb->wpr_size != 0) {
+		wpr_range_lo = sb->wpr_addr;
+		wpr_range_hi = wpr_range_lo + sb->wpr_size;
+	} else {
+		wpr_range_lo = acr->ls_blob->addr;
+		wpr_range_hi = wpr_range_lo + acr->ls_blob->size;
+	}
+
+	return (wpr_lo >= wpr_range_lo && wpr_lo < wpr_range_hi &&
+		wpr_hi > wpr_range_lo && wpr_hi <= wpr_range_hi);
+}
+
 static int
 acr_r352_bootstrap(struct acr_r352 *acr, struct nvkm_secboot *sb)
 {
+	const struct nvkm_subdev *subdev = &sb->subdev;
+	unsigned long managed_falcons = acr->base.managed_falcons;
+	u32 reg;
+	int falcon_id;
 	int ret;
 
 	if (sb->wpr_set)
@@ -772,40 +894,95 @@ acr_r352_bootstrap(struct acr_r352 *acr, struct nvkm_secboot *sb)
 	if (ret)
 		return ret;
 
-	nvkm_debug(&sb->subdev, "running HS load blob\n");
-	ret = sb->func->run_blob(sb, acr->load_blob);
+	nvkm_debug(subdev, "running HS load blob\n");
+	ret = sb->func->run_blob(sb, acr->load_blob, sb->boot_falcon);
 	/* clear halt interrupt */
 	nvkm_falcon_clear_interrupt(sb->boot_falcon, 0x10);
-	if (ret)
+	sb->wpr_set = acr_r352_wpr_is_set(acr, sb);
+	if (ret < 0) {
 		return ret;
-	nvkm_debug(&sb->subdev, "HS load blob completed\n");
+	} else if (ret > 0) {
+		nvkm_error(subdev, "HS load failed, ret 0x%08x", ret);
+		return -EINVAL;
+	}
+	nvkm_debug(subdev, "HS load blob completed\n");
+	/* WPR must be set at this point */
+	if (!sb->wpr_set) {
+		nvkm_error(subdev, "ACR blob completed but WPR not set!\n");
+		return -EINVAL;
+	}
+
+	/* Run LS firmwares post_run hooks */
+	for_each_set_bit(falcon_id, &managed_falcons, NVKM_SECBOOT_FALCON_END) {
+		const struct acr_r352_ls_func *func =
+						  acr->func->ls_func[falcon_id];
+
+		if (func->post_run)
+			func->post_run(&acr->base, sb);
+	}
+
+	/* Re-start ourselves if we are managed */
+	if (!nvkm_secboot_is_managed(sb, acr->base.boot_falcon))
+		return 0;
+
+	/* Enable interrupts */
+	nvkm_falcon_wr32(sb->boot_falcon, 0x10, 0xff);
+	nvkm_mc_intr_mask(subdev->device, sb->boot_falcon->owner->index, true);
+
+	/* Start LS firmware on boot falcon */
+	nvkm_falcon_start(sb->boot_falcon);
+
+	/*
+	 * There is a bug where the LS firmware sometimes require to be started
+	 * twice (this happens only on SEC). Detect and workaround that
+	 * condition.
+	 *
+	 * Once started, the falcon will end up in STOPPED condition (bit 5)
+	 * if successful, or in HALT condition (bit 4) if not.
+	 */
+	nvkm_msec(subdev->device, 1,
+		  if ((reg = nvkm_rd32(subdev->device,
+				       sb->boot_falcon->addr + 0x100)
+		       & 0x30) != 0)
+			  break;
+	);
+	if (reg & BIT(4)) {
+		nvkm_debug(subdev, "applying workaround for start bug...");
+		nvkm_falcon_start(sb->boot_falcon);
+		nvkm_msec(subdev->device, 1,
+			if ((reg = nvkm_rd32(subdev->device,
+					     sb->boot_falcon->addr + 0x100)
+			     & 0x30) != 0)
+				break;
+		);
+		if (reg & BIT(4)) {
+			nvkm_error(subdev, "%s failed to start\n",
+			       nvkm_secboot_falcon_name[acr->base.boot_falcon]);
+			return -EINVAL;
+		}
+	}
 
-	sb->wpr_set = true;
+	nvkm_debug(subdev, "%s started\n",
+		   nvkm_secboot_falcon_name[acr->base.boot_falcon]);
 
 	return 0;
 }
 
-/*
- * acr_r352_reset() - execute secure boot from the prepared state
+/**
+ * acr_r352_reset_nopmu - dummy reset method when no PMU firmware is loaded
  *
- * Load the HS bootloader and ask the falcon to run it. This will in turn
- * load the HS firmware and run it, so once the falcon stops all the managed
- * falcons should have their LS firmware loaded and be ready to run.
+ * Reset is done by re-executing secure boot from scratch, with lazy bootstrap
+ * disabled. This has the effect of making all managed falcons ready-to-run.
  */
 static int
-acr_r352_reset(struct nvkm_acr *_acr, struct nvkm_secboot *sb,
-	       enum nvkm_secboot_falcon falcon)
+acr_r352_reset_nopmu(struct acr_r352 *acr, struct nvkm_secboot *sb,
+		     enum nvkm_secboot_falcon falcon)
 {
-	struct acr_r352 *acr = acr_r352(_acr);
 	int ret;
 
 	/*
-	 * Dummy GM200 implementation: perform secure boot each time we are
-	 * called on FECS. Since only FECS and GPCCS are managed and started
-	 * together, this ought to be safe.
-	 *
-	 * Once we have proper PMU firmware and support, this will be changed
-	 * to a proper call to the PMU method.
+	 * Perform secure boot each time we are called on FECS. Since only FECS
+	 * and GPCCS are managed and started together, this ought to be safe.
 	 */
 	if (falcon != NVKM_SECBOOT_FALCON_FECS)
 		goto end;
@@ -814,7 +991,7 @@ acr_r352_reset(struct nvkm_acr *_acr, struct nvkm_secboot *sb,
 	if (ret)
 		return ret;
 
-	acr_r352_bootstrap(acr, sb);
+	ret = acr_r352_bootstrap(acr, sb);
 	if (ret)
 		return ret;
 
@@ -823,28 +1000,57 @@ end:
 	return 0;
 }
 
+/*
+ * acr_r352_reset() - execute secure boot from the prepared state
+ *
+ * Load the HS bootloader and ask the falcon to run it. This will in turn
+ * load the HS firmware and run it, so once the falcon stops all the managed
+ * falcons should have their LS firmware loaded and be ready to run.
+ */
 static int
-acr_r352_start(struct nvkm_acr *_acr, struct nvkm_secboot *sb,
-		    enum nvkm_secboot_falcon falcon)
+acr_r352_reset(struct nvkm_acr *_acr, struct nvkm_secboot *sb,
+	       enum nvkm_secboot_falcon falcon)
 {
 	struct acr_r352 *acr = acr_r352(_acr);
-	const struct nvkm_subdev *subdev = &sb->subdev;
-	int base;
+	struct nvkm_msgqueue *queue;
+	const char *fname = nvkm_secboot_falcon_name[falcon];
+	bool wpr_already_set = sb->wpr_set;
+	int ret;
 
-	switch (falcon) {
-	case NVKM_SECBOOT_FALCON_FECS:
-		base = 0x409000;
+	/* Make sure secure boot is performed */
+	ret = acr_r352_bootstrap(acr, sb);
+	if (ret)
+		return ret;
+
+	/* No PMU interface? */
+	if (!nvkm_secboot_is_managed(sb, _acr->boot_falcon)) {
+		/* Redo secure boot entirely if it was already done */
+		if (wpr_already_set)
+			return acr_r352_reset_nopmu(acr, sb, falcon);
+		/* Else return the result of the initial invokation */
+		else
+			return ret;
+	}
+
+	switch (_acr->boot_falcon) {
+	case NVKM_SECBOOT_FALCON_PMU:
+		queue = sb->subdev.device->pmu->queue;
 		break;
-	case NVKM_SECBOOT_FALCON_GPCCS:
-		base = 0x41a000;
+	case NVKM_SECBOOT_FALCON_SEC2:
+		queue = sb->subdev.device->sec2->queue;
 		break;
 	default:
-		nvkm_error(subdev, "cannot start unhandled falcon!\n");
 		return -EINVAL;
 	}
 
-	nvkm_wr32(subdev->device, base + 0x130, 0x00000002);
-	acr->falcon_state[falcon] = RUNNING;
+	/* Otherwise just ask the LS firmware to reset the falcon */
+	nvkm_debug(&sb->subdev, "resetting %s falcon\n", fname);
+	ret = nvkm_msgqueue_acr_boot_falcon(queue, falcon);
+	if (ret) {
+		nvkm_error(&sb->subdev, "cannot boot %s falcon\n", fname);
+		return ret;
+	}
+	nvkm_debug(&sb->subdev, "falcon %s reset\n", fname);
 
 	return 0;
 }
@@ -864,6 +1070,8 @@ acr_r352_dtor(struct nvkm_acr *_acr)
 
 	nvkm_gpuobj_del(&acr->unload_blob);
 
+	if (_acr->boot_falcon != NVKM_SECBOOT_FALCON_PMU)
+		kfree(acr->hsbl_unload_blob);
 	kfree(acr->hsbl_blob);
 	nvkm_gpuobj_del(&acr->load_blob);
 	nvkm_gpuobj_del(&acr->ls_blob);
@@ -887,8 +1095,88 @@ acr_r352_ls_gpccs_func = {
 	.lhdr_flags = LSF_FLAG_FORCE_PRIV_LOAD,
 };
 
+
+
+/**
+ * struct acr_r352_pmu_bl_desc - PMU DMEM bootloader descriptor
+ * @dma_idx:		DMA context to be used by BL while loading code/data
+ * @code_dma_base:	256B-aligned Physical FB Address where code is located
+ * @total_code_size:	total size of the code part in the ucode
+ * @code_size_to_load:	size of the code part to load in PMU IMEM.
+ * @code_entry_point:	entry point in the code.
+ * @data_dma_base:	Physical FB address where data part of ucode is located
+ * @data_size:		Total size of the data portion.
+ * @overlay_dma_base:	Physical Fb address for resident code present in ucode
+ * @argc:		Total number of args
+ * @argv:		offset where args are copied into PMU's DMEM.
+ *
+ * Structure used by the PMU bootloader to load the rest of the code
+ */
+struct acr_r352_pmu_bl_desc {
+	u32 dma_idx;
+	u32 code_dma_base;
+	u32 code_size_total;
+	u32 code_size_to_load;
+	u32 code_entry_point;
+	u32 data_dma_base;
+	u32 data_size;
+	u32 overlay_dma_base;
+	u32 argc;
+	u32 argv;
+	u16 code_dma_base1;
+	u16 data_dma_base1;
+	u16 overlay_dma_base1;
+};
+
+/**
+ * acr_r352_generate_pmu_bl_desc() - populate a DMEM BL descriptor for PMU LS image
+ *
+ */
+static void
+acr_r352_generate_pmu_bl_desc(const struct nvkm_acr *acr,
+			      const struct ls_ucode_img *img, u64 wpr_addr,
+			      void *_desc)
+{
+	const struct ls_ucode_img_desc *pdesc = &img->ucode_desc;
+	const struct nvkm_pmu *pmu = acr->subdev->device->pmu;
+	struct acr_r352_pmu_bl_desc *desc = _desc;
+	u64 base;
+	u64 addr_code;
+	u64 addr_data;
+	u32 addr_args;
+
+	base = wpr_addr + img->ucode_off + pdesc->app_start_offset;
+	addr_code = (base + pdesc->app_resident_code_offset) >> 8;
+	addr_data = (base + pdesc->app_resident_data_offset) >> 8;
+	addr_args = pmu->falcon->data.limit;
+	addr_args -= NVKM_MSGQUEUE_CMDLINE_SIZE;
+
+	desc->dma_idx = FALCON_DMAIDX_UCODE;
+	desc->code_dma_base = lower_32_bits(addr_code);
+	desc->code_dma_base1 = upper_32_bits(addr_code);
+	desc->code_size_total = pdesc->app_size;
+	desc->code_size_to_load = pdesc->app_resident_code_size;
+	desc->code_entry_point = pdesc->app_imem_entry;
+	desc->data_dma_base = lower_32_bits(addr_data);
+	desc->data_dma_base1 = upper_32_bits(addr_data);
+	desc->data_size = pdesc->app_resident_data_size;
+	desc->overlay_dma_base = lower_32_bits(addr_code);
+	desc->overlay_dma_base1 = upper_32_bits(addr_code);
+	desc->argc = 1;
+	desc->argv = addr_args;
+}
+
+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,
+};
+
 const struct acr_r352_func
 acr_r352_func = {
+	.fixup_hs_desc = acr_r352_fixup_hs_desc,
 	.generate_hs_bl_desc = acr_r352_generate_hs_bl_desc,
 	.hs_bl_desc_size = sizeof(struct acr_r352_flcn_bl_desc),
 	.ls_ucode_img_load = acr_r352_ls_ucode_img_load,
@@ -897,6 +1185,7 @@ acr_r352_func = {
 	.ls_func = {
 		[NVKM_SECBOOT_FALCON_FECS] = &acr_r352_ls_fecs_func,
 		[NVKM_SECBOOT_FALCON_GPCCS] = &acr_r352_ls_gpccs_func,
+		[NVKM_SECBOOT_FALCON_PMU] = &acr_r352_ls_pmu_func,
 	},
 };
 
@@ -906,7 +1195,6 @@ acr_r352_base_func = {
 	.fini = acr_r352_fini,
 	.load = acr_r352_load,
 	.reset = acr_r352_reset,
-	.start = acr_r352_start,
 };
 
 struct nvkm_acr *
@@ -915,6 +1203,13 @@ acr_r352_new_(const struct acr_r352_func *func,
 	      unsigned long managed_falcons)
 {
 	struct acr_r352 *acr;
+	int i;
+
+	/* Check that all requested falcons are supported */
+	for_each_set_bit(i, &managed_falcons, NVKM_SECBOOT_FALCON_END) {
+		if (!func->ls_func[i])
+			return ERR_PTR(-ENOTSUPP);
+	}
 
 	acr = kzalloc(sizeof(*acr), GFP_KERNEL);
 	if (!acr)

+ 24 - 119
drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r352.h

@@ -24,131 +24,27 @@
 
 #include "acr.h"
 #include "ls_ucode.h"
+#include "hs_ucode.h"
 
 struct ls_ucode_img;
 
 #define ACR_R352_MAX_APPS 8
 
-/*
- *
- * LS blob structures
- *
- */
-
-/**
- * struct acr_r352_lsf_lsb_header - LS firmware header
- * @signature:		signature to verify the firmware against
- * @ucode_off:		offset of the ucode blob in the WPR region. The ucode
- *                      blob contains the bootloader, code and data of the
- *                      LS falcon
- * @ucode_size:		size of the ucode blob, including bootloader
- * @data_size:		size of the ucode blob data
- * @bl_code_size:	size of the bootloader code
- * @bl_imem_off:	offset in imem of the bootloader
- * @bl_data_off:	offset of the bootloader data in WPR region
- * @bl_data_size:	size of the bootloader data
- * @app_code_off:	offset of the app code relative to ucode_off
- * @app_code_size:	size of the app code
- * @app_data_off:	offset of the app data relative to ucode_off
- * @app_data_size:	size of the app data
- * @flags:		flags for the secure bootloader
- *
- * This structure is written into the WPR region for each managed falcon. Each
- * instance is referenced by the lsb_offset member of the corresponding
- * lsf_wpr_header.
- */
-struct acr_r352_lsf_lsb_header {
-	/**
-	 * LS falcon signatures
-	 * @prd_keys:		signature to use in production mode
-	 * @dgb_keys:		signature to use in debug mode
-	 * @b_prd_present:	whether the production key is present
-	 * @b_dgb_present:	whether the debug key is present
-	 * @falcon_id:		ID of the falcon the ucode applies to
-	 */
-	struct {
-		u8 prd_keys[2][16];
-		u8 dbg_keys[2][16];
-		u32 b_prd_present;
-		u32 b_dbg_present;
-		u32 falcon_id;
-	} signature;
-	u32 ucode_off;
-	u32 ucode_size;
-	u32 data_size;
-	u32 bl_code_size;
-	u32 bl_imem_off;
-	u32 bl_data_off;
-	u32 bl_data_size;
-	u32 app_code_off;
-	u32 app_code_size;
-	u32 app_data_off;
-	u32 app_data_size;
-	u32 flags;
 #define LSF_FLAG_LOAD_CODE_AT_0		1
 #define LSF_FLAG_DMACTL_REQ_CTX		4
 #define LSF_FLAG_FORCE_PRIV_LOAD	8
-};
-
-/**
- * struct acr_r352_lsf_wpr_header - LS blob WPR Header
- * @falcon_id:		LS falcon ID
- * @lsb_offset:		offset of the lsb_lsf_header in the WPR region
- * @bootstrap_owner:	secure falcon reponsible for bootstrapping the LS falcon
- * @lazy_bootstrap:	skip bootstrapping by ACR
- * @status:		bootstrapping status
- *
- * An array of these is written at the beginning of the WPR region, one for
- * each managed falcon. The array is terminated by an instance which falcon_id
- * is LSF_FALCON_ID_INVALID.
- */
-struct acr_r352_lsf_wpr_header {
-	u32 falcon_id;
-	u32 lsb_offset;
-	u32 bootstrap_owner;
-	u32 lazy_bootstrap;
-	u32 status;
-#define LSF_IMAGE_STATUS_NONE				0
-#define LSF_IMAGE_STATUS_COPY				1
-#define LSF_IMAGE_STATUS_VALIDATION_CODE_FAILED		2
-#define LSF_IMAGE_STATUS_VALIDATION_DATA_FAILED		3
-#define LSF_IMAGE_STATUS_VALIDATION_DONE		4
-#define LSF_IMAGE_STATUS_VALIDATION_SKIPPED		5
-#define LSF_IMAGE_STATUS_BOOTSTRAP_READY		6
-};
-
-/**
- * struct ls_ucode_img_r352 - ucode image augmented with r352 headers
- */
-struct ls_ucode_img_r352 {
-	struct ls_ucode_img base;
-
-	struct acr_r352_lsf_wpr_header wpr_header;
-	struct acr_r352_lsf_lsb_header lsb_header;
-};
-#define ls_ucode_img_r352(i) container_of(i, struct ls_ucode_img_r352, base)
-
-
-/*
- * HS blob structures
- */
 
-struct hsf_load_header_app {
-	u32 sec_code_off;
-	u32 sec_code_size;
-};
+static inline u32
+hsf_load_header_app_off(const struct hsf_load_header *hdr, u32 app)
+{
+	return hdr->apps[app];
+}
 
-/**
- * struct hsf_load_header - HS firmware load header
- */
-struct hsf_load_header {
-	u32 non_sec_code_off;
-	u32 non_sec_code_size;
-	u32 data_dma_base;
-	u32 data_size;
-	u32 num_apps;
-	struct hsf_load_header_app app[0];
-};
+static inline u32
+hsf_load_header_app_size(const struct hsf_load_header *hdr, u32 app)
+{
+	return hdr->apps[hdr->num_apps + app];
+}
 
 /**
  * struct acr_r352_ls_func - manages a single LS firmware
@@ -157,6 +53,7 @@ struct hsf_load_header {
  * @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 {
@@ -164,6 +61,7 @@ struct acr_r352_ls_func {
 	void (*generate_bl_desc)(const struct nvkm_acr *,
 				 const struct ls_ucode_img *, u64, void *);
 	u32 bl_desc_size;
+	void (*post_run)(const struct nvkm_acr *, const struct nvkm_secboot *);
 	u32 lhdr_flags;
 };
 
@@ -179,13 +77,15 @@ struct acr_r352;
 struct acr_r352_func {
 	void (*generate_hs_bl_desc)(const struct hsf_load_header *, void *,
 				    u64);
+	void (*fixup_hs_desc)(struct acr_r352 *, struct nvkm_secboot *, void *);
 	u32 hs_bl_desc_size;
+	bool shadow_blob;
 
 	struct ls_ucode_img *(*ls_ucode_img_load)(const struct acr_r352 *,
 						  enum nvkm_secboot_falcon);
 	int (*ls_fill_headers)(struct acr_r352 *, struct list_head *);
 	int (*ls_write_wpr)(struct acr_r352 *, struct list_head *,
-			    struct nvkm_gpuobj *, u32);
+			    struct nvkm_gpuobj *, u64);
 
 	const struct acr_r352_ls_func *ls_func[NVKM_SECBOOT_FALCON_END];
 };
@@ -204,19 +104,22 @@ struct acr_r352 {
 	struct nvkm_gpuobj *load_blob;
 	struct {
 		struct hsf_load_header load_bl_header;
-		struct hsf_load_header_app __load_apps[ACR_R352_MAX_APPS];
+		u32 __load_apps[ACR_R352_MAX_APPS * 2];
 	};
 
 	/* HS FW - unlock WPR region (dGPU only) */
 	struct nvkm_gpuobj *unload_blob;
 	struct {
 		struct hsf_load_header unload_bl_header;
-		struct hsf_load_header_app __unload_apps[ACR_R352_MAX_APPS];
+		u32 __unload_apps[ACR_R352_MAX_APPS * 2];
 	};
 
 	/* HS bootloader */
 	void *hsbl_blob;
 
+	/* HS bootloader for unload blob, if using a different falcon */
+	void *hsbl_unload_blob;
+
 	/* LS FWs, to be loaded by the HS ACR */
 	struct nvkm_gpuobj *ls_blob;
 
@@ -245,6 +148,8 @@ struct ls_ucode_img *acr_r352_ls_ucode_img_load(const struct acr_r352 *,
 						enum nvkm_secboot_falcon);
 int acr_r352_ls_fill_headers(struct acr_r352 *, struct list_head *);
 int acr_r352_ls_write_wpr(struct acr_r352 *, struct list_head *,
-			  struct nvkm_gpuobj *, u32);
+			  struct nvkm_gpuobj *, u64);
+
+void acr_r352_fixup_hs_desc(struct acr_r352 *, struct nvkm_secboot *, void *);
 
 #endif

+ 104 - 45
drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r361.c

@@ -20,58 +20,23 @@
  * DEALINGS IN THE SOFTWARE.
  */
 
-#include "acr_r352.h"
+#include "acr_r361.h"
 
 #include <engine/falcon.h>
-
-/**
- * struct acr_r361_flcn_bl_desc - DMEM bootloader descriptor
- * @signature:		16B signature for secure code. 0s if no secure code
- * @ctx_dma:		DMA context to be used by BL while loading code/data
- * @code_dma_base:	256B-aligned Physical FB Address where code is located
- *			(falcon's $xcbase register)
- * @non_sec_code_off:	offset from code_dma_base where the non-secure code is
- *                      located. The offset must be multiple of 256 to help perf
- * @non_sec_code_size:	the size of the nonSecure code part.
- * @sec_code_off:	offset from code_dma_base where the secure code is
- *                      located. The offset must be multiple of 256 to help perf
- * @sec_code_size:	offset from code_dma_base where the secure code is
- *                      located. The offset must be multiple of 256 to help perf
- * @code_entry_point:	code entry point which will be invoked by BL after
- *                      code is loaded.
- * @data_dma_base:	256B aligned Physical FB Address where data is located.
- *			(falcon's $xdbase register)
- * @data_size:		size of data block. Should be multiple of 256B
- *
- * Structure used by the bootloader to load the rest of the code. This has
- * to be filled by host and copied into DMEM at offset provided in the
- * hsflcn_bl_desc.bl_desc_dmem_load_off.
- */
-struct acr_r361_flcn_bl_desc {
-	u32 reserved[4];
-	u32 signature[4];
-	u32 ctx_dma;
-	struct flcn_u64 code_dma_base;
-	u32 non_sec_code_off;
-	u32 non_sec_code_size;
-	u32 sec_code_off;
-	u32 sec_code_size;
-	u32 code_entry_point;
-	struct flcn_u64 data_dma_base;
-	u32 data_size;
-};
+#include <core/msgqueue.h>
+#include <subdev/pmu.h>
+#include <engine/sec2.h>
 
 static void
 acr_r361_generate_flcn_bl_desc(const struct nvkm_acr *acr,
-			       const struct ls_ucode_img *_img, u64 wpr_addr,
+			       const struct ls_ucode_img *img, u64 wpr_addr,
 			       void *_desc)
 {
-	struct ls_ucode_img_r352 *img = ls_ucode_img_r352(_img);
 	struct acr_r361_flcn_bl_desc *desc = _desc;
-	const struct ls_ucode_img_desc *pdesc = &img->base.ucode_desc;
+	const struct ls_ucode_img_desc *pdesc = &img->ucode_desc;
 	u64 base, addr_code, addr_data;
 
-	base = wpr_addr + img->lsb_header.ucode_off + pdesc->app_start_offset;
+	base = wpr_addr + img->ucode_off + pdesc->app_start_offset;
 	addr_code = base + pdesc->app_resident_code_offset;
 	addr_data = base + pdesc->app_resident_data_offset;
 
@@ -84,7 +49,7 @@ acr_r361_generate_flcn_bl_desc(const struct nvkm_acr *acr,
 	desc->data_size = pdesc->app_resident_data_size;
 }
 
-static void
+void
 acr_r361_generate_hs_bl_desc(const struct hsf_load_header *hdr, void *_bl_desc,
 			    u64 offset)
 {
@@ -94,8 +59,8 @@ acr_r361_generate_hs_bl_desc(const struct hsf_load_header *hdr, void *_bl_desc,
 	bl_desc->code_dma_base = u64_to_flcn64(offset);
 	bl_desc->non_sec_code_off = hdr->non_sec_code_off;
 	bl_desc->non_sec_code_size = hdr->non_sec_code_size;
-	bl_desc->sec_code_off = hdr->app[0].sec_code_off;
-	bl_desc->sec_code_size = hdr->app[0].sec_code_size;
+	bl_desc->sec_code_off = hsf_load_header_app_off(hdr, 0);
+	bl_desc->sec_code_size = hsf_load_header_app_size(hdr, 0);
 	bl_desc->code_entry_point = 0;
 	bl_desc->data_dma_base = u64_to_flcn64(offset + hdr->data_dma_base);
 	bl_desc->data_size = hdr->data_size;
@@ -117,8 +82,100 @@ acr_r361_ls_gpccs_func = {
 	.lhdr_flags = LSF_FLAG_FORCE_PRIV_LOAD,
 };
 
+struct acr_r361_pmu_bl_desc {
+	u32 reserved;
+	u32 dma_idx;
+	struct flcn_u64 code_dma_base;
+	u32 total_code_size;
+	u32 code_size_to_load;
+	u32 code_entry_point;
+	struct flcn_u64 data_dma_base;
+	u32 data_size;
+	struct flcn_u64 overlay_dma_base;
+	u32 argc;
+	u32 argv;
+};
+
+static void
+acr_r361_generate_pmu_bl_desc(const struct nvkm_acr *acr,
+			      const struct ls_ucode_img *img, u64 wpr_addr,
+			      void *_desc)
+{
+	const struct ls_ucode_img_desc *pdesc = &img->ucode_desc;
+	const struct nvkm_pmu *pmu = acr->subdev->device->pmu;
+	struct acr_r361_pmu_bl_desc *desc = _desc;
+	u64 base, addr_code, addr_data;
+	u32 addr_args;
+
+	base = wpr_addr + img->ucode_off + pdesc->app_start_offset;
+	addr_code = base + pdesc->app_resident_code_offset;
+	addr_data = base + pdesc->app_resident_data_offset;
+	addr_args = pmu->falcon->data.limit;
+	addr_args -= NVKM_MSGQUEUE_CMDLINE_SIZE;
+
+	desc->dma_idx = FALCON_DMAIDX_UCODE;
+	desc->code_dma_base = u64_to_flcn64(addr_code);
+	desc->total_code_size = pdesc->app_size;
+	desc->code_size_to_load = pdesc->app_resident_code_size;
+	desc->code_entry_point = pdesc->app_imem_entry;
+	desc->data_dma_base = u64_to_flcn64(addr_data);
+	desc->data_size = pdesc->app_resident_data_size;
+	desc->overlay_dma_base = u64_to_flcn64(addr_code);
+	desc->argc = 1;
+	desc->argv = addr_args;
+}
+
+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,
+};
+
+static void
+acr_r361_generate_sec2_bl_desc(const struct nvkm_acr *acr,
+			       const struct ls_ucode_img *img, u64 wpr_addr,
+			       void *_desc)
+{
+	const struct ls_ucode_img_desc *pdesc = &img->ucode_desc;
+	const struct nvkm_sec2 *sec = acr->subdev->device->sec2;
+	struct acr_r361_pmu_bl_desc *desc = _desc;
+	u64 base, addr_code, addr_data;
+	u32 addr_args;
+
+	base = wpr_addr + img->ucode_off + pdesc->app_start_offset;
+	/* For some reason we should not add app_resident_code_offset here */
+	addr_code = base;
+	addr_data = base + pdesc->app_resident_data_offset;
+	addr_args = sec->falcon->data.limit;
+	addr_args -= NVKM_MSGQUEUE_CMDLINE_SIZE;
+
+	desc->dma_idx = FALCON_SEC2_DMAIDX_UCODE;
+	desc->code_dma_base = u64_to_flcn64(addr_code);
+	desc->total_code_size = pdesc->app_size;
+	desc->code_size_to_load = pdesc->app_resident_code_size;
+	desc->code_entry_point = pdesc->app_imem_entry;
+	desc->data_dma_base = u64_to_flcn64(addr_data);
+	desc->data_size = pdesc->app_resident_data_size;
+	desc->overlay_dma_base = u64_to_flcn64(addr_code);
+	desc->argc = 1;
+	/* args are stored at the beginning of EMEM */
+	desc->argv = 0x01000000;
+}
+
+const struct acr_r352_ls_func
+acr_r361_ls_sec2_func = {
+	.load = acr_ls_ucode_load_sec2,
+	.generate_bl_desc = acr_r361_generate_sec2_bl_desc,
+	.bl_desc_size = sizeof(struct acr_r361_pmu_bl_desc),
+	.post_run = acr_ls_sec2_post_run,
+};
+
+
 const struct acr_r352_func
 acr_r361_func = {
+	.fixup_hs_desc = acr_r352_fixup_hs_desc,
 	.generate_hs_bl_desc = acr_r361_generate_hs_bl_desc,
 	.hs_bl_desc_size = sizeof(struct acr_r361_flcn_bl_desc),
 	.ls_ucode_img_load = acr_r352_ls_ucode_img_load,
@@ -127,6 +184,8 @@ acr_r361_func = {
 	.ls_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,
 	},
 };
 

+ 72 - 0
drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r361.h

@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef __NVKM_SECBOOT_ACR_R361_H__
+#define __NVKM_SECBOOT_ACR_R361_H__
+
+#include "acr_r352.h"
+
+/**
+ * struct acr_r361_flcn_bl_desc - DMEM bootloader descriptor
+ * @signature:		16B signature for secure code. 0s if no secure code
+ * @ctx_dma:		DMA context to be used by BL while loading code/data
+ * @code_dma_base:	256B-aligned Physical FB Address where code is located
+ *			(falcon's $xcbase register)
+ * @non_sec_code_off:	offset from code_dma_base where the non-secure code is
+ *                      located. The offset must be multiple of 256 to help perf
+ * @non_sec_code_size:	the size of the nonSecure code part.
+ * @sec_code_off:	offset from code_dma_base where the secure code is
+ *                      located. The offset must be multiple of 256 to help perf
+ * @sec_code_size:	offset from code_dma_base where the secure code is
+ *                      located. The offset must be multiple of 256 to help perf
+ * @code_entry_point:	code entry point which will be invoked by BL after
+ *                      code is loaded.
+ * @data_dma_base:	256B aligned Physical FB Address where data is located.
+ *			(falcon's $xdbase register)
+ * @data_size:		size of data block. Should be multiple of 256B
+ *
+ * Structure used by the bootloader to load the rest of the code. This has
+ * to be filled by host and copied into DMEM at offset provided in the
+ * hsflcn_bl_desc.bl_desc_dmem_load_off.
+ */
+struct acr_r361_flcn_bl_desc {
+	u32 reserved[4];
+	u32 signature[4];
+	u32 ctx_dma;
+	struct flcn_u64 code_dma_base;
+	u32 non_sec_code_off;
+	u32 non_sec_code_size;
+	u32 sec_code_off;
+	u32 sec_code_size;
+	u32 code_entry_point;
+	struct flcn_u64 data_dma_base;
+	u32 data_size;
+};
+
+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;
+
+#endif

+ 117 - 0
drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r364.c

@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "acr_r361.h"
+
+#include <core/gpuobj.h>
+
+/*
+ * r364 ACR: hsflcn_desc structure has changed to introduce the shadow_mem
+ * parameter.
+ */
+
+struct acr_r364_hsflcn_desc {
+	union {
+		u8 reserved_dmem[0x200];
+		u32 signatures[4];
+	} ucode_reserved_space;
+	u32 wpr_region_id;
+	u32 wpr_offset;
+	u32 mmu_memory_range;
+	struct {
+		u32 no_regions;
+		struct {
+			u32 start_addr;
+			u32 end_addr;
+			u32 region_id;
+			u32 read_mask;
+			u32 write_mask;
+			u32 client_mask;
+			u32 shadow_mem_start_addr;
+		} region_props[2];
+	} regions;
+	u32 ucode_blob_size;
+	u64 ucode_blob_base __aligned(8);
+	struct {
+		u32 vpr_enabled;
+		u32 vpr_start;
+		u32 vpr_end;
+		u32 hdcp_policies;
+	} vpr_desc;
+};
+
+static void
+acr_r364_fixup_hs_desc(struct acr_r352 *acr, struct nvkm_secboot *sb,
+		       void *_desc)
+{
+	struct acr_r364_hsflcn_desc *desc = _desc;
+	struct nvkm_gpuobj *ls_blob = acr->ls_blob;
+
+	/* WPR region information if WPR is not fixed */
+	if (sb->wpr_size == 0) {
+		u64 wpr_start = ls_blob->addr;
+		u64 wpr_end = ls_blob->addr + ls_blob->size;
+
+		if (acr->func->shadow_blob)
+			wpr_start += ls_blob->size / 2;
+
+		desc->wpr_region_id = 1;
+		desc->regions.no_regions = 2;
+		desc->regions.region_props[0].start_addr = wpr_start >> 8;
+		desc->regions.region_props[0].end_addr = wpr_end >> 8;
+		desc->regions.region_props[0].region_id = 1;
+		desc->regions.region_props[0].read_mask = 0xf;
+		desc->regions.region_props[0].write_mask = 0xc;
+		desc->regions.region_props[0].client_mask = 0x2;
+		if (acr->func->shadow_blob)
+			desc->regions.region_props[0].shadow_mem_start_addr =
+							     ls_blob->addr >> 8;
+		else
+			desc->regions.region_props[0].shadow_mem_start_addr = 0;
+	} else {
+		desc->ucode_blob_base = ls_blob->addr;
+		desc->ucode_blob_size = ls_blob->size;
+	}
+}
+
+const struct acr_r352_func
+acr_r364_func = {
+	.fixup_hs_desc = acr_r364_fixup_hs_desc,
+	.generate_hs_bl_desc = acr_r361_generate_hs_bl_desc,
+	.hs_bl_desc_size = sizeof(struct acr_r361_flcn_bl_desc),
+	.ls_ucode_img_load = acr_r352_ls_ucode_img_load,
+	.ls_fill_headers = acr_r352_ls_fill_headers,
+	.ls_write_wpr = acr_r352_ls_write_wpr,
+	.ls_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,
+	},
+};
+
+
+struct nvkm_acr *
+acr_r364_new(unsigned long managed_falcons)
+{
+	return acr_r352_new_(&acr_r364_func, NVKM_SECBOOT_FALCON_PMU,
+			     managed_falcons);
+}

+ 388 - 0
drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r367.c

@@ -0,0 +1,388 @@
+/*
+ * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "acr_r367.h"
+#include "acr_r361.h"
+
+#include <core/gpuobj.h>
+
+/*
+ * r367 ACR: new LS signature format requires a rewrite of LS firmware and
+ * blob creation functions. Also the hsflcn_desc layout has changed slightly.
+ */
+
+#define LSF_LSB_DEPMAP_SIZE 11
+
+/**
+ * struct acr_r367_lsf_lsb_header - LS firmware header
+ *
+ * See also struct acr_r352_lsf_lsb_header for documentation.
+ */
+struct acr_r367_lsf_lsb_header {
+	/**
+	 * LS falcon signatures
+	 * @prd_keys:		signature to use in production mode
+	 * @dgb_keys:		signature to use in debug mode
+	 * @b_prd_present:	whether the production key is present
+	 * @b_dgb_present:	whether the debug key is present
+	 * @falcon_id:		ID of the falcon the ucode applies to
+	 */
+	struct {
+		u8 prd_keys[2][16];
+		u8 dbg_keys[2][16];
+		u32 b_prd_present;
+		u32 b_dbg_present;
+		u32 falcon_id;
+		u32 supports_versioning;
+		u32 version;
+		u32 depmap_count;
+		u8 depmap[LSF_LSB_DEPMAP_SIZE * 2 * 4];
+		u8 kdf[16];
+	} signature;
+	u32 ucode_off;
+	u32 ucode_size;
+	u32 data_size;
+	u32 bl_code_size;
+	u32 bl_imem_off;
+	u32 bl_data_off;
+	u32 bl_data_size;
+	u32 app_code_off;
+	u32 app_code_size;
+	u32 app_data_off;
+	u32 app_data_size;
+	u32 flags;
+};
+
+/**
+ * struct acr_r367_lsf_wpr_header - LS blob WPR Header
+ *
+ * See also struct acr_r352_lsf_wpr_header for documentation.
+ */
+struct acr_r367_lsf_wpr_header {
+	u32 falcon_id;
+	u32 lsb_offset;
+	u32 bootstrap_owner;
+	u32 lazy_bootstrap;
+	u32 bin_version;
+	u32 status;
+#define LSF_IMAGE_STATUS_NONE				0
+#define LSF_IMAGE_STATUS_COPY				1
+#define LSF_IMAGE_STATUS_VALIDATION_CODE_FAILED		2
+#define LSF_IMAGE_STATUS_VALIDATION_DATA_FAILED		3
+#define LSF_IMAGE_STATUS_VALIDATION_DONE		4
+#define LSF_IMAGE_STATUS_VALIDATION_SKIPPED		5
+#define LSF_IMAGE_STATUS_BOOTSTRAP_READY		6
+#define LSF_IMAGE_STATUS_REVOCATION_CHECK_FAILED		7
+};
+
+/**
+ * struct ls_ucode_img_r367 - ucode image augmented with r367 headers
+ */
+struct ls_ucode_img_r367 {
+	struct ls_ucode_img base;
+
+	struct acr_r367_lsf_wpr_header wpr_header;
+	struct acr_r367_lsf_lsb_header lsb_header;
+};
+#define ls_ucode_img_r367(i) container_of(i, struct ls_ucode_img_r367, base)
+
+struct ls_ucode_img *
+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;
+	struct ls_ucode_img_r367 *img;
+	int ret;
+
+	img = kzalloc(sizeof(*img), GFP_KERNEL);
+	if (!img)
+		return ERR_PTR(-ENOMEM);
+
+	img->base.falcon_id = falcon_id;
+
+	ret = acr->func->ls_func[falcon_id]->load(subdev, &img->base);
+	if (ret) {
+		kfree(img->base.ucode_data);
+		kfree(img->base.sig);
+		kfree(img);
+		return ERR_PTR(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",
+			   nvkm_secboot_falcon_name[falcon_id]);
+		return ERR_PTR(-EINVAL);
+	}
+
+	/* Copy signature to the right place */
+	memcpy(&img->lsb_header.signature, img->base.sig, img->base.sig_size);
+
+	/* not needed? the signature should already have the right value */
+	img->lsb_header.signature.falcon_id = falcon_id;
+
+	return &img->base;
+}
+
+#define LSF_LSB_HEADER_ALIGN 256
+#define LSF_BL_DATA_ALIGN 256
+#define LSF_BL_DATA_SIZE_ALIGN 256
+#define LSF_BL_CODE_SIZE_ALIGN 256
+#define LSF_UCODE_DATA_ALIGN 4096
+
+static u32
+acr_r367_ls_img_fill_headers(struct acr_r352 *acr,
+			     struct ls_ucode_img_r367 *img, u32 offset)
+{
+	struct ls_ucode_img *_img = &img->base;
+	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];
+
+	/* Fill WPR header */
+	whdr->falcon_id = _img->falcon_id;
+	whdr->bootstrap_owner = acr->base.boot_falcon;
+	whdr->bin_version = lhdr->signature.version;
+	whdr->status = LSF_IMAGE_STATUS_COPY;
+
+	/* Skip bootstrapping falcons started by someone else than ACR */
+	if (acr->lazy_bootstrap & BIT(_img->falcon_id))
+		whdr->lazy_bootstrap = 1;
+
+	/* Align, save off, and include an LSB header size */
+	offset = ALIGN(offset, LSF_LSB_HEADER_ALIGN);
+	whdr->lsb_offset = offset;
+	offset += sizeof(*lhdr);
+
+	/*
+	 * Align, save off, and include the original (static) ucode
+	 * image size
+	 */
+	offset = ALIGN(offset, LSF_UCODE_DATA_ALIGN);
+	_img->ucode_off = lhdr->ucode_off = offset;
+	offset += _img->ucode_size;
+
+	/*
+	 * For falcons that use a boot loader (BL), we append a loader
+	 * desc structure on the end of the ucode image and consider
+	 * this the boot loader data. The host will then copy the loader
+	 * desc args to this space within the WPR region (before locking
+	 * down) and the HS bin will then copy them to DMEM 0 for the
+	 * loader.
+	 */
+	lhdr->bl_code_size = ALIGN(desc->bootloader_size,
+				   LSF_BL_CODE_SIZE_ALIGN);
+	lhdr->ucode_size = ALIGN(desc->app_resident_data_offset,
+				 LSF_BL_CODE_SIZE_ALIGN) + lhdr->bl_code_size;
+	lhdr->data_size = ALIGN(desc->app_size, LSF_BL_CODE_SIZE_ALIGN) +
+				lhdr->bl_code_size - lhdr->ucode_size;
+	/*
+	 * Though the BL is located at 0th offset of the image, the VA
+	 * is different to make sure that it doesn't collide the actual
+	 * OS VA range
+	 */
+	lhdr->bl_imem_off = desc->bootloader_imem_offset;
+	lhdr->app_code_off = desc->app_start_offset +
+			     desc->app_resident_code_offset;
+	lhdr->app_code_size = desc->app_resident_code_size;
+	lhdr->app_data_off = desc->app_start_offset +
+			     desc->app_resident_data_offset;
+	lhdr->app_data_size = desc->app_resident_data_size;
+
+	lhdr->flags = func->lhdr_flags;
+	if (_img->falcon_id == acr->base.boot_falcon)
+		lhdr->flags |= LSF_FLAG_DMACTL_REQ_CTX;
+
+	/* Align and save off BL descriptor size */
+	lhdr->bl_data_size = ALIGN(func->bl_desc_size, LSF_BL_DATA_SIZE_ALIGN);
+
+	/*
+	 * Align, save off, and include the additional BL data
+	 */
+	offset = ALIGN(offset, LSF_BL_DATA_ALIGN);
+	lhdr->bl_data_off = offset;
+	offset += lhdr->bl_data_size;
+
+	return offset;
+}
+
+int
+acr_r367_ls_fill_headers(struct acr_r352 *acr, struct list_head *imgs)
+{
+	struct ls_ucode_img_r367 *img;
+	struct list_head *l;
+	u32 count = 0;
+	u32 offset;
+
+	/* Count the number of images to manage */
+	list_for_each(l, imgs)
+		count++;
+
+	/*
+	 * Start with an array of WPR headers at the base of the WPR.
+	 * The expectation here is that the secure falcon will do a single DMA
+	 * read of this array and cache it internally so it's ok to pack these.
+	 * Also, we add 1 to the falcon count to indicate the end of the array.
+	 */
+	offset = sizeof(img->wpr_header) * (count + 1);
+
+	/*
+	 * Walk the managed falcons, accounting for the LSB structs
+	 * as well as the ucode images.
+	 */
+	list_for_each_entry(img, imgs, base.node) {
+		offset = acr_r367_ls_img_fill_headers(acr, img, offset);
+	}
+
+	return offset;
+}
+
+int
+acr_r367_ls_write_wpr(struct acr_r352 *acr, struct list_head *imgs,
+		      struct nvkm_gpuobj *wpr_blob, u64 wpr_addr)
+{
+	struct ls_ucode_img *_img;
+	u32 pos = 0;
+
+	nvkm_kmap(wpr_blob);
+
+	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];
+		u8 gdesc[ls_func->bl_desc_size];
+
+		nvkm_gpuobj_memcpy_to(wpr_blob, pos, &img->wpr_header,
+				      sizeof(img->wpr_header));
+
+		nvkm_gpuobj_memcpy_to(wpr_blob, img->wpr_header.lsb_offset,
+				     &img->lsb_header, sizeof(img->lsb_header));
+
+		/* Generate and write BL descriptor */
+		memset(gdesc, 0, ls_func->bl_desc_size);
+		ls_func->generate_bl_desc(&acr->base, _img, wpr_addr, gdesc);
+
+		nvkm_gpuobj_memcpy_to(wpr_blob, img->lsb_header.bl_data_off,
+				      gdesc, ls_func->bl_desc_size);
+
+		/* Copy ucode */
+		nvkm_gpuobj_memcpy_to(wpr_blob, img->lsb_header.ucode_off,
+				      _img->ucode_data, _img->ucode_size);
+
+		pos += sizeof(img->wpr_header);
+	}
+
+	nvkm_wo32(wpr_blob, pos, NVKM_SECBOOT_FALCON_INVALID);
+
+	nvkm_done(wpr_blob);
+
+	return 0;
+}
+
+struct acr_r367_hsflcn_desc {
+	u8 reserved_dmem[0x200];
+	u32 signatures[4];
+	u32 wpr_region_id;
+	u32 wpr_offset;
+	u32 mmu_memory_range;
+#define FLCN_ACR_MAX_REGIONS 2
+	struct {
+		u32 no_regions;
+		struct {
+			u32 start_addr;
+			u32 end_addr;
+			u32 region_id;
+			u32 read_mask;
+			u32 write_mask;
+			u32 client_mask;
+			u32 shadow_mem_start_addr;
+		} region_props[FLCN_ACR_MAX_REGIONS];
+	} regions;
+	u32 ucode_blob_size;
+	u64 ucode_blob_base __aligned(8);
+	struct {
+		u32 vpr_enabled;
+		u32 vpr_start;
+		u32 vpr_end;
+		u32 hdcp_policies;
+	} vpr_desc;
+};
+
+void
+acr_r367_fixup_hs_desc(struct acr_r352 *acr, struct nvkm_secboot *sb,
+		       void *_desc)
+{
+	struct acr_r367_hsflcn_desc *desc = _desc;
+	struct nvkm_gpuobj *ls_blob = acr->ls_blob;
+
+	/* WPR region information if WPR is not fixed */
+	if (sb->wpr_size == 0) {
+		u64 wpr_start = ls_blob->addr;
+		u64 wpr_end = ls_blob->addr + ls_blob->size;
+
+		if (acr->func->shadow_blob)
+			wpr_start += ls_blob->size / 2;
+
+		desc->wpr_region_id = 1;
+		desc->regions.no_regions = 2;
+		desc->regions.region_props[0].start_addr = wpr_start >> 8;
+		desc->regions.region_props[0].end_addr = wpr_end >> 8;
+		desc->regions.region_props[0].region_id = 1;
+		desc->regions.region_props[0].read_mask = 0xf;
+		desc->regions.region_props[0].write_mask = 0xc;
+		desc->regions.region_props[0].client_mask = 0x2;
+		if (acr->func->shadow_blob)
+			desc->regions.region_props[0].shadow_mem_start_addr =
+							     ls_blob->addr >> 8;
+		else
+			desc->regions.region_props[0].shadow_mem_start_addr = 0;
+	} else {
+		desc->ucode_blob_base = ls_blob->addr;
+		desc->ucode_blob_size = ls_blob->size;
+	}
+}
+
+const struct acr_r352_func
+acr_r367_func = {
+	.fixup_hs_desc = acr_r367_fixup_hs_desc,
+	.generate_hs_bl_desc = acr_r361_generate_hs_bl_desc,
+	.hs_bl_desc_size = sizeof(struct acr_r361_flcn_bl_desc),
+	.shadow_blob = true,
+	.ls_ucode_img_load = acr_r367_ls_ucode_img_load,
+	.ls_fill_headers = acr_r367_ls_fill_headers,
+	.ls_write_wpr = acr_r367_ls_write_wpr,
+	.ls_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,
+	},
+};
+
+struct nvkm_acr *
+acr_r367_new(enum nvkm_secboot_falcon boot_falcon,
+	     unsigned long managed_falcons)
+{
+	return acr_r352_new_(&acr_r367_func, boot_falcon, managed_falcons);
+}

+ 35 - 0
drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r367.h

@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef __NVKM_SECBOOT_ACR_R367_H__
+#define __NVKM_SECBOOT_ACR_R367_H__
+
+#include "acr_r352.h"
+
+void acr_r367_fixup_hs_desc(struct acr_r352 *, struct nvkm_secboot *, void *);
+
+struct ls_ucode_img *acr_r367_ls_ucode_img_load(const struct acr_r352 *,
+						enum nvkm_secboot_falcon);
+int acr_r367_ls_fill_headers(struct acr_r352 *, struct list_head *);
+int acr_r367_ls_write_wpr(struct acr_r352 *, struct list_head *,
+			  struct nvkm_gpuobj *, u64);
+#endif

+ 165 - 0
drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r375.c

@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "acr_r367.h"
+
+#include <engine/falcon.h>
+#include <core/msgqueue.h>
+#include <subdev/pmu.h>
+
+/*
+ * r375 ACR: similar to r367, but with a unified bootloader descriptor
+ * structure for GR and PMU falcons.
+ */
+
+/* Same as acr_r361_flcn_bl_desc, plus argc/argv */
+struct acr_r375_flcn_bl_desc {
+	u32 reserved[4];
+	u32 signature[4];
+	u32 ctx_dma;
+	struct flcn_u64 code_dma_base;
+	u32 non_sec_code_off;
+	u32 non_sec_code_size;
+	u32 sec_code_off;
+	u32 sec_code_size;
+	u32 code_entry_point;
+	struct flcn_u64 data_dma_base;
+	u32 data_size;
+	u32 argc;
+	u32 argv;
+};
+
+static void
+acr_r375_generate_flcn_bl_desc(const struct nvkm_acr *acr,
+			       const struct ls_ucode_img *img, u64 wpr_addr,
+			       void *_desc)
+{
+	struct acr_r375_flcn_bl_desc *desc = _desc;
+	const struct ls_ucode_img_desc *pdesc = &img->ucode_desc;
+	u64 base, addr_code, addr_data;
+
+	base = wpr_addr + img->ucode_off + pdesc->app_start_offset;
+	addr_code = base + pdesc->app_resident_code_offset;
+	addr_data = base + pdesc->app_resident_data_offset;
+
+	desc->ctx_dma = FALCON_DMAIDX_UCODE;
+	desc->code_dma_base = u64_to_flcn64(addr_code);
+	desc->non_sec_code_off = pdesc->app_resident_code_offset;
+	desc->non_sec_code_size = pdesc->app_resident_code_size;
+	desc->code_entry_point = pdesc->app_imem_entry;
+	desc->data_dma_base = u64_to_flcn64(addr_data);
+	desc->data_size = pdesc->app_resident_data_size;
+}
+
+static void
+acr_r375_generate_hs_bl_desc(const struct hsf_load_header *hdr, void *_bl_desc,
+			     u64 offset)
+{
+	struct acr_r375_flcn_bl_desc *bl_desc = _bl_desc;
+
+	bl_desc->ctx_dma = FALCON_DMAIDX_VIRT;
+	bl_desc->non_sec_code_off = hdr->non_sec_code_off;
+	bl_desc->non_sec_code_size = hdr->non_sec_code_size;
+	bl_desc->sec_code_off = hsf_load_header_app_off(hdr, 0);
+	bl_desc->sec_code_size = hsf_load_header_app_size(hdr, 0);
+	bl_desc->code_entry_point = 0;
+	bl_desc->code_dma_base = u64_to_flcn64(offset);
+	bl_desc->data_dma_base = u64_to_flcn64(offset + hdr->data_dma_base);
+	bl_desc->data_size = hdr->data_size;
+}
+
+const struct acr_r352_ls_func
+acr_r375_ls_fecs_func = {
+	.load = acr_ls_ucode_load_fecs,
+	.generate_bl_desc = acr_r375_generate_flcn_bl_desc,
+	.bl_desc_size = sizeof(struct acr_r375_flcn_bl_desc),
+};
+
+const struct acr_r352_ls_func
+acr_r375_ls_gpccs_func = {
+	.load = acr_ls_ucode_load_gpccs,
+	.generate_bl_desc = acr_r375_generate_flcn_bl_desc,
+	.bl_desc_size = sizeof(struct acr_r375_flcn_bl_desc),
+	/* GPCCS will be loaded using PRI */
+	.lhdr_flags = LSF_FLAG_FORCE_PRIV_LOAD,
+};
+
+
+static void
+acr_r375_generate_pmu_bl_desc(const struct nvkm_acr *acr,
+			      const struct ls_ucode_img *img, u64 wpr_addr,
+			      void *_desc)
+{
+	const struct ls_ucode_img_desc *pdesc = &img->ucode_desc;
+	const struct nvkm_pmu *pmu = acr->subdev->device->pmu;
+	struct acr_r375_flcn_bl_desc *desc = _desc;
+	u64 base, addr_code, addr_data;
+	u32 addr_args;
+
+	base = wpr_addr + img->ucode_off + pdesc->app_start_offset;
+	addr_code = base + pdesc->app_resident_code_offset;
+	addr_data = base + pdesc->app_resident_data_offset;
+	addr_args = pmu->falcon->data.limit;
+	addr_args -= NVKM_MSGQUEUE_CMDLINE_SIZE;
+
+	desc->ctx_dma = FALCON_DMAIDX_UCODE;
+	desc->code_dma_base = u64_to_flcn64(addr_code);
+	desc->non_sec_code_off = pdesc->app_resident_code_offset;
+	desc->non_sec_code_size = pdesc->app_resident_code_size;
+	desc->code_entry_point = pdesc->app_imem_entry;
+	desc->data_dma_base = u64_to_flcn64(addr_data);
+	desc->data_size = pdesc->app_resident_data_size;
+	desc->argc = 1;
+	desc->argv = addr_args;
+}
+
+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_r375_flcn_bl_desc),
+	.post_run = acr_ls_pmu_post_run,
+};
+
+
+const struct acr_r352_func
+acr_r375_func = {
+	.fixup_hs_desc = acr_r367_fixup_hs_desc,
+	.generate_hs_bl_desc = acr_r375_generate_hs_bl_desc,
+	.hs_bl_desc_size = sizeof(struct acr_r375_flcn_bl_desc),
+	.shadow_blob = true,
+	.ls_ucode_img_load = acr_r367_ls_ucode_img_load,
+	.ls_fill_headers = acr_r367_ls_fill_headers,
+	.ls_write_wpr = acr_r367_ls_write_wpr,
+	.ls_func = {
+		[NVKM_SECBOOT_FALCON_FECS] = &acr_r375_ls_fecs_func,
+		[NVKM_SECBOOT_FALCON_GPCCS] = &acr_r375_ls_gpccs_func,
+		[NVKM_SECBOOT_FALCON_PMU] = &acr_r375_ls_pmu_func,
+	},
+};
+
+struct nvkm_acr *
+acr_r375_new(enum nvkm_secboot_falcon boot_falcon,
+	     unsigned long managed_falcons)
+{
+	return acr_r352_new_(&acr_r375_func, boot_falcon, managed_falcons);
+}

+ 10 - 1
drivers/gpu/drm/nouveau/nvkm/subdev/secboot/base.c

@@ -87,6 +87,7 @@
 #include <subdev/mc.h>
 #include <subdev/timer.h>
 #include <subdev/pmu.h>
+#include <engine/sec2.h>
 
 const char *
 nvkm_secboot_falcon_name[] = {
@@ -94,6 +95,7 @@ nvkm_secboot_falcon_name[] = {
 	[NVKM_SECBOOT_FALCON_RESERVED] = "<reserved>",
 	[NVKM_SECBOOT_FALCON_FECS] = "FECS",
 	[NVKM_SECBOOT_FALCON_GPCCS] = "GPCCS",
+	[NVKM_SECBOOT_FALCON_SEC2] = "SEC2",
 	[NVKM_SECBOOT_FALCON_END] = "<invalid>",
 };
 /**
@@ -131,13 +133,20 @@ nvkm_secboot_oneinit(struct nvkm_subdev *subdev)
 
 	switch (sb->acr->boot_falcon) {
 	case NVKM_SECBOOT_FALCON_PMU:
-		sb->boot_falcon = subdev->device->pmu->falcon;
+		sb->halt_falcon = sb->boot_falcon = subdev->device->pmu->falcon;
+		break;
+	case NVKM_SECBOOT_FALCON_SEC2:
+		/* we must keep SEC2 alive forever since ACR will run on it */
+		nvkm_engine_ref(&subdev->device->sec2->engine);
+		sb->boot_falcon = subdev->device->sec2->falcon;
+		sb->halt_falcon = subdev->device->pmu->falcon;
 		break;
 	default:
 		nvkm_error(subdev, "Unmanaged boot falcon %s!\n",
 			                nvkm_secboot_falcon_name[sb->acr->boot_falcon]);
 		return -EINVAL;
 	}
+	nvkm_debug(subdev, "using %s falcon for ACR\n", sb->boot_falcon->name);
 
 	/* Call chip-specific init function */
 	if (sb->func->oneinit)

+ 12 - 11
drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gm200.c

@@ -34,12 +34,13 @@
  *
  */
 int
-gm200_secboot_run_blob(struct nvkm_secboot *sb, struct nvkm_gpuobj *blob)
+gm200_secboot_run_blob(struct nvkm_secboot *sb, struct nvkm_gpuobj *blob,
+		       struct nvkm_falcon *falcon)
 {
 	struct gm200_secboot *gsb = gm200_secboot(sb);
 	struct nvkm_subdev *subdev = &gsb->base.subdev;
-	struct nvkm_falcon *falcon = gsb->base.boot_falcon;
 	struct nvkm_vma vma;
+	u32 start_address;
 	int ret;
 
 	ret = nvkm_falcon_get(falcon, subdev);
@@ -60,10 +61,12 @@ gm200_secboot_run_blob(struct nvkm_secboot *sb, struct nvkm_gpuobj *blob)
 	nvkm_falcon_bind_context(falcon, gsb->inst);
 
 	/* Load the HS bootloader into the falcon's IMEM/DMEM */
-	ret = sb->acr->func->load(sb->acr, &gsb->base, blob, vma.offset);
-	if (ret)
+	ret = sb->acr->func->load(sb->acr, falcon, blob, vma.offset);
+	if (ret < 0)
 		goto end;
 
+	start_address = ret;
+
 	/* Disable interrupts as we will poll for the HALT bit */
 	nvkm_mc_intr_mask(sb->subdev.device, falcon->owner->index, false);
 
@@ -71,19 +74,17 @@ gm200_secboot_run_blob(struct nvkm_secboot *sb, struct nvkm_gpuobj *blob)
 	nvkm_falcon_wr32(falcon, 0x040, 0xdeada5a5);
 
 	/* Start the HS bootloader */
-	nvkm_falcon_set_start_addr(falcon, sb->acr->start_address);
+	nvkm_falcon_set_start_addr(falcon, start_address);
 	nvkm_falcon_start(falcon);
 	ret = nvkm_falcon_wait_for_halt(falcon, 100);
 	if (ret)
 		goto end;
 
-	/* If mailbox register contains an error code, then ACR has failed */
+	/*
+	 * The mailbox register contains the (positive) error code - return this
+	 * to the caller
+	 */
 	ret = nvkm_falcon_rd32(falcon, 0x040);
-	if (ret) {
-		nvkm_error(subdev, "ACR boot failed, ret 0x%08x", ret);
-		ret = -EINVAL;
-		goto end;
-	}
 
 end:
 	/* Reenable interrupts */

+ 2 - 1
drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gm200.h

@@ -38,6 +38,7 @@ struct gm200_secboot {
 int gm200_secboot_oneinit(struct nvkm_secboot *);
 int gm200_secboot_fini(struct nvkm_secboot *, bool);
 void *gm200_secboot_dtor(struct nvkm_secboot *);
-int gm200_secboot_run_blob(struct nvkm_secboot *, struct nvkm_gpuobj *);
+int gm200_secboot_run_blob(struct nvkm_secboot *, struct nvkm_gpuobj *,
+			   struct nvkm_falcon *);
 
 #endif

+ 7 - 1
drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gm20b.c

@@ -107,9 +107,12 @@ gm20b_secboot_new(struct nvkm_device *device, int index,
 	struct gm200_secboot *gsb;
 	struct nvkm_acr *acr;
 
-	acr = acr_r352_new(BIT(NVKM_SECBOOT_FALCON_FECS));
+	acr = acr_r352_new(BIT(NVKM_SECBOOT_FALCON_FECS) |
+			   BIT(NVKM_SECBOOT_FALCON_PMU));
 	if (IS_ERR(acr))
 		return PTR_ERR(acr);
+	/* Support the initial GM20B firmware release without PMU */
+	acr->optional_falcons = BIT(NVKM_SECBOOT_FALCON_PMU);
 
 	gsb = kzalloc(sizeof(*gsb), GFP_KERNEL);
 	if (!gsb) {
@@ -137,3 +140,6 @@ MODULE_FIRMWARE("nvidia/gm20b/gr/sw_ctx.bin");
 MODULE_FIRMWARE("nvidia/gm20b/gr/sw_nonctx.bin");
 MODULE_FIRMWARE("nvidia/gm20b/gr/sw_bundle_init.bin");
 MODULE_FIRMWARE("nvidia/gm20b/gr/sw_method_init.bin");
+MODULE_FIRMWARE("nvidia/gm20b/pmu/desc.bin");
+MODULE_FIRMWARE("nvidia/gm20b/pmu/image.bin");
+MODULE_FIRMWARE("nvidia/gm20b/pmu/sig.bin");

+ 251 - 0
drivers/gpu/drm/nouveau/nvkm/subdev/secboot/gp102.c

@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "acr.h"
+#include "gm200.h"
+
+#include "ls_ucode.h"
+#include "hs_ucode.h"
+#include <subdev/mc.h>
+#include <subdev/timer.h>
+#include <engine/falcon.h>
+#include <engine/nvdec.h>
+
+static bool
+gp102_secboot_scrub_required(struct nvkm_secboot *sb)
+{
+	struct nvkm_subdev *subdev = &sb->subdev;
+	struct nvkm_device *device = subdev->device;
+	u32 reg;
+
+	nvkm_wr32(device, 0x100cd0, 0x2);
+	reg = nvkm_rd32(device, 0x100cd0);
+
+	return (reg & BIT(4));
+}
+
+static int
+gp102_run_secure_scrub(struct nvkm_secboot *sb)
+{
+	struct nvkm_subdev *subdev = &sb->subdev;
+	struct nvkm_device *device = subdev->device;
+	struct nvkm_engine *engine;
+	struct nvkm_falcon *falcon;
+	void *scrub_image;
+	struct fw_bin_header *hsbin_hdr;
+	struct hsf_fw_header *fw_hdr;
+	struct hsf_load_header *lhdr;
+	void *scrub_data;
+	int ret;
+
+	nvkm_debug(subdev, "running VPR scrubber binary on NVDEC...\n");
+
+	if (!(engine = nvkm_engine_ref(&device->nvdec->engine)))
+		return PTR_ERR(engine);
+	falcon = device->nvdec->falcon;
+
+	nvkm_falcon_get(falcon, &sb->subdev);
+
+	scrub_image = hs_ucode_load_blob(subdev, falcon, "nvdec/scrubber");
+	if (IS_ERR(scrub_image))
+		return PTR_ERR(scrub_image);
+
+	nvkm_falcon_reset(falcon);
+	nvkm_falcon_bind_context(falcon, NULL);
+
+	hsbin_hdr = scrub_image;
+	fw_hdr = scrub_image + hsbin_hdr->header_offset;
+	lhdr = scrub_image + fw_hdr->hdr_offset;
+	scrub_data = scrub_image + hsbin_hdr->data_offset;
+
+	nvkm_falcon_load_imem(falcon, scrub_data, lhdr->non_sec_code_off,
+			      lhdr->non_sec_code_size,
+			      lhdr->non_sec_code_off >> 8, 0, false);
+	nvkm_falcon_load_imem(falcon, scrub_data + lhdr->apps[0],
+			      ALIGN(lhdr->apps[0], 0x100),
+			      lhdr->apps[1],
+			      lhdr->apps[0] >> 8, 0, true);
+	nvkm_falcon_load_dmem(falcon, scrub_data + lhdr->data_dma_base, 0,
+			      lhdr->data_size, 0);
+
+	kfree(scrub_image);
+
+	nvkm_falcon_set_start_addr(falcon, 0x0);
+	nvkm_falcon_start(falcon);
+
+	ret = nvkm_falcon_wait_for_halt(falcon, 500);
+	if (ret < 0) {
+		nvkm_error(subdev, "failed to run VPR scrubber binary!\n");
+		ret = -ETIMEDOUT;
+		goto end;
+	}
+
+	/* put nvdec in clean state - without reset it will remain in HS mode */
+	nvkm_falcon_reset(falcon);
+
+	if (gp102_secboot_scrub_required(sb)) {
+		nvkm_error(subdev, "VPR scrubber binary failed!\n");
+		ret = -EINVAL;
+		goto end;
+	}
+
+	nvkm_debug(subdev, "VPR scrub successfully completed\n");
+
+end:
+	nvkm_falcon_put(falcon, &sb->subdev);
+	nvkm_engine_unref(&engine);
+	return ret;
+}
+
+static int
+gp102_secboot_run_blob(struct nvkm_secboot *sb, struct nvkm_gpuobj *blob,
+		       struct nvkm_falcon *falcon)
+{
+	int ret;
+
+	/* make sure the VPR region is unlocked */
+	if (gp102_secboot_scrub_required(sb)) {
+		ret = gp102_run_secure_scrub(sb);
+		if (ret)
+			return ret;
+	}
+
+	return gm200_secboot_run_blob(sb, blob, falcon);
+}
+
+static const struct nvkm_secboot_func
+gp102_secboot = {
+	.dtor = gm200_secboot_dtor,
+	.oneinit = gm200_secboot_oneinit,
+	.fini = gm200_secboot_fini,
+	.run_blob = gp102_secboot_run_blob,
+};
+
+int
+gp102_secboot_new(struct nvkm_device *device, int index,
+		  struct nvkm_secboot **psb)
+{
+	int ret;
+	struct gm200_secboot *gsb;
+	struct nvkm_acr *acr;
+
+	acr = acr_r367_new(NVKM_SECBOOT_FALCON_SEC2,
+			   BIT(NVKM_SECBOOT_FALCON_FECS) |
+			   BIT(NVKM_SECBOOT_FALCON_GPCCS) |
+			   BIT(NVKM_SECBOOT_FALCON_SEC2));
+	if (IS_ERR(acr))
+		return PTR_ERR(acr);
+
+	gsb = kzalloc(sizeof(*gsb), GFP_KERNEL);
+	if (!gsb) {
+		psb = NULL;
+		return -ENOMEM;
+	}
+	*psb = &gsb->base;
+
+	ret = nvkm_secboot_ctor(&gp102_secboot, acr, device, index, &gsb->base);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+MODULE_FIRMWARE("nvidia/gp102/acr/bl.bin");
+MODULE_FIRMWARE("nvidia/gp102/acr/unload_bl.bin");
+MODULE_FIRMWARE("nvidia/gp102/acr/ucode_load.bin");
+MODULE_FIRMWARE("nvidia/gp102/acr/ucode_unload.bin");
+MODULE_FIRMWARE("nvidia/gp102/gr/fecs_bl.bin");
+MODULE_FIRMWARE("nvidia/gp102/gr/fecs_inst.bin");
+MODULE_FIRMWARE("nvidia/gp102/gr/fecs_data.bin");
+MODULE_FIRMWARE("nvidia/gp102/gr/fecs_sig.bin");
+MODULE_FIRMWARE("nvidia/gp102/gr/gpccs_bl.bin");
+MODULE_FIRMWARE("nvidia/gp102/gr/gpccs_inst.bin");
+MODULE_FIRMWARE("nvidia/gp102/gr/gpccs_data.bin");
+MODULE_FIRMWARE("nvidia/gp102/gr/gpccs_sig.bin");
+MODULE_FIRMWARE("nvidia/gp102/gr/sw_ctx.bin");
+MODULE_FIRMWARE("nvidia/gp102/gr/sw_nonctx.bin");
+MODULE_FIRMWARE("nvidia/gp102/gr/sw_bundle_init.bin");
+MODULE_FIRMWARE("nvidia/gp102/gr/sw_method_init.bin");
+MODULE_FIRMWARE("nvidia/gp102/nvdec/scrubber.bin");
+MODULE_FIRMWARE("nvidia/gp102/sec2/desc.bin");
+MODULE_FIRMWARE("nvidia/gp102/sec2/image.bin");
+MODULE_FIRMWARE("nvidia/gp102/sec2/sig.bin");
+MODULE_FIRMWARE("nvidia/gp104/acr/bl.bin");
+MODULE_FIRMWARE("nvidia/gp104/acr/unload_bl.bin");
+MODULE_FIRMWARE("nvidia/gp104/acr/ucode_load.bin");
+MODULE_FIRMWARE("nvidia/gp104/acr/ucode_unload.bin");
+MODULE_FIRMWARE("nvidia/gp104/gr/fecs_bl.bin");
+MODULE_FIRMWARE("nvidia/gp104/gr/fecs_inst.bin");
+MODULE_FIRMWARE("nvidia/gp104/gr/fecs_data.bin");
+MODULE_FIRMWARE("nvidia/gp104/gr/fecs_sig.bin");
+MODULE_FIRMWARE("nvidia/gp104/gr/gpccs_bl.bin");
+MODULE_FIRMWARE("nvidia/gp104/gr/gpccs_inst.bin");
+MODULE_FIRMWARE("nvidia/gp104/gr/gpccs_data.bin");
+MODULE_FIRMWARE("nvidia/gp104/gr/gpccs_sig.bin");
+MODULE_FIRMWARE("nvidia/gp104/gr/sw_ctx.bin");
+MODULE_FIRMWARE("nvidia/gp104/gr/sw_nonctx.bin");
+MODULE_FIRMWARE("nvidia/gp104/gr/sw_bundle_init.bin");
+MODULE_FIRMWARE("nvidia/gp104/gr/sw_method_init.bin");
+MODULE_FIRMWARE("nvidia/gp104/nvdec/scrubber.bin");
+MODULE_FIRMWARE("nvidia/gp104/sec2/desc.bin");
+MODULE_FIRMWARE("nvidia/gp104/sec2/image.bin");
+MODULE_FIRMWARE("nvidia/gp104/sec2/sig.bin");
+MODULE_FIRMWARE("nvidia/gp106/acr/bl.bin");
+MODULE_FIRMWARE("nvidia/gp106/acr/unload_bl.bin");
+MODULE_FIRMWARE("nvidia/gp106/acr/ucode_load.bin");
+MODULE_FIRMWARE("nvidia/gp106/acr/ucode_unload.bin");
+MODULE_FIRMWARE("nvidia/gp106/gr/fecs_bl.bin");
+MODULE_FIRMWARE("nvidia/gp106/gr/fecs_inst.bin");
+MODULE_FIRMWARE("nvidia/gp106/gr/fecs_data.bin");
+MODULE_FIRMWARE("nvidia/gp106/gr/fecs_sig.bin");
+MODULE_FIRMWARE("nvidia/gp106/gr/gpccs_bl.bin");
+MODULE_FIRMWARE("nvidia/gp106/gr/gpccs_inst.bin");
+MODULE_FIRMWARE("nvidia/gp106/gr/gpccs_data.bin");
+MODULE_FIRMWARE("nvidia/gp106/gr/gpccs_sig.bin");
+MODULE_FIRMWARE("nvidia/gp106/gr/sw_ctx.bin");
+MODULE_FIRMWARE("nvidia/gp106/gr/sw_nonctx.bin");
+MODULE_FIRMWARE("nvidia/gp106/gr/sw_bundle_init.bin");
+MODULE_FIRMWARE("nvidia/gp106/gr/sw_method_init.bin");
+MODULE_FIRMWARE("nvidia/gp106/nvdec/scrubber.bin");
+MODULE_FIRMWARE("nvidia/gp106/sec2/desc.bin");
+MODULE_FIRMWARE("nvidia/gp106/sec2/image.bin");
+MODULE_FIRMWARE("nvidia/gp106/sec2/sig.bin");
+MODULE_FIRMWARE("nvidia/gp107/acr/bl.bin");
+MODULE_FIRMWARE("nvidia/gp107/acr/unload_bl.bin");
+MODULE_FIRMWARE("nvidia/gp107/acr/ucode_load.bin");
+MODULE_FIRMWARE("nvidia/gp107/acr/ucode_unload.bin");
+MODULE_FIRMWARE("nvidia/gp107/gr/fecs_bl.bin");
+MODULE_FIRMWARE("nvidia/gp107/gr/fecs_inst.bin");
+MODULE_FIRMWARE("nvidia/gp107/gr/fecs_data.bin");
+MODULE_FIRMWARE("nvidia/gp107/gr/fecs_sig.bin");
+MODULE_FIRMWARE("nvidia/gp107/gr/gpccs_bl.bin");
+MODULE_FIRMWARE("nvidia/gp107/gr/gpccs_inst.bin");
+MODULE_FIRMWARE("nvidia/gp107/gr/gpccs_data.bin");
+MODULE_FIRMWARE("nvidia/gp107/gr/gpccs_sig.bin");
+MODULE_FIRMWARE("nvidia/gp107/gr/sw_ctx.bin");
+MODULE_FIRMWARE("nvidia/gp107/gr/sw_nonctx.bin");
+MODULE_FIRMWARE("nvidia/gp107/gr/sw_bundle_init.bin");
+MODULE_FIRMWARE("nvidia/gp107/gr/sw_method_init.bin");
+MODULE_FIRMWARE("nvidia/gp107/nvdec/scrubber.bin");
+MODULE_FIRMWARE("nvidia/gp107/sec2/desc.bin");
+MODULE_FIRMWARE("nvidia/gp107/sec2/image.bin");
+MODULE_FIRMWARE("nvidia/gp107/sec2/sig.bin");

+ 97 - 0
drivers/gpu/drm/nouveau/nvkm/subdev/secboot/hs_ucode.c

@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "hs_ucode.h"
+#include "ls_ucode.h"
+#include "acr.h"
+
+#include <engine/falcon.h>
+
+/**
+ * hs_ucode_patch_signature() - patch HS blob with correct signature for
+ * specified falcon.
+ */
+static void
+hs_ucode_patch_signature(const struct nvkm_falcon *falcon, void *acr_image,
+			 bool new_format)
+{
+	struct fw_bin_header *hsbin_hdr = acr_image;
+	struct hsf_fw_header *fw_hdr = acr_image + hsbin_hdr->header_offset;
+	void *hs_data = acr_image + hsbin_hdr->data_offset;
+	void *sig;
+	u32 sig_size;
+	u32 patch_loc, patch_sig;
+
+	/*
+	 * I had the brilliant idea to "improve" the binary format by
+	 * removing this useless indirection. However to make NVIDIA files
+	 * directly compatible, let's support both format.
+	 */
+	if (new_format) {
+		patch_loc = fw_hdr->patch_loc;
+		patch_sig = fw_hdr->patch_sig;
+	} else {
+		patch_loc = *(u32 *)(acr_image + fw_hdr->patch_loc);
+		patch_sig = *(u32 *)(acr_image + fw_hdr->patch_sig);
+	}
+
+	/* Falcon in debug or production mode? */
+	if (falcon->debug) {
+		sig = acr_image + fw_hdr->sig_dbg_offset;
+		sig_size = fw_hdr->sig_dbg_size;
+	} else {
+		sig = acr_image + fw_hdr->sig_prod_offset;
+		sig_size = fw_hdr->sig_prod_size;
+	}
+
+	/* Patch signature */
+	memcpy(hs_data + patch_loc, sig + patch_sig, sig_size);
+}
+
+void *
+hs_ucode_load_blob(struct nvkm_subdev *subdev, const struct nvkm_falcon *falcon,
+		   const char *fw)
+{
+	void *acr_image;
+	bool new_format;
+
+	acr_image = nvkm_acr_load_firmware(subdev, fw, 0);
+	if (IS_ERR(acr_image))
+		return acr_image;
+
+	/* detect the format to define how signature should be patched */
+	switch (((u32 *)acr_image)[0]) {
+	case 0x3b1d14f0:
+		new_format = true;
+		break;
+	case 0x000010de:
+		new_format = false;
+		break;
+	default:
+		nvkm_error(subdev, "unknown header for HS blob %s\n", fw);
+		return ERR_PTR(-EINVAL);
+	}
+
+	hs_ucode_patch_signature(falcon, acr_image, new_format);
+
+	return acr_image;
+}

+ 81 - 0
drivers/gpu/drm/nouveau/nvkm/subdev/secboot/hs_ucode.h

@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef __NVKM_SECBOOT_HS_UCODE_H__
+#define __NVKM_SECBOOT_HS_UCODE_H__
+
+#include <core/os.h>
+#include <core/subdev.h>
+
+struct nvkm_falcon;
+
+/**
+ * struct hsf_fw_header - HS firmware descriptor
+ * @sig_dbg_offset:	offset of the debug signature
+ * @sig_dbg_size:	size of the debug signature
+ * @sig_prod_offset:	offset of the production signature
+ * @sig_prod_size:	size of the production signature
+ * @patch_loc:		offset of the offset (sic) of where the signature is
+ * @patch_sig:		offset of the offset (sic) to add to sig_*_offset
+ * @hdr_offset:		offset of the load header (see struct hs_load_header)
+ * @hdr_size:		size of above header
+ *
+ * This structure is embedded in the HS firmware image at
+ * hs_bin_hdr.header_offset.
+ */
+struct hsf_fw_header {
+	u32 sig_dbg_offset;
+	u32 sig_dbg_size;
+	u32 sig_prod_offset;
+	u32 sig_prod_size;
+	u32 patch_loc;
+	u32 patch_sig;
+	u32 hdr_offset;
+	u32 hdr_size;
+};
+
+/**
+ * struct hsf_load_header - HS firmware load header
+ */
+struct hsf_load_header {
+	u32 non_sec_code_off;
+	u32 non_sec_code_size;
+	u32 data_dma_base;
+	u32 data_size;
+	u32 num_apps;
+	/*
+	 * Organized as follows:
+	 * - app0_code_off
+	 * - app1_code_off
+	 * - ...
+	 * - appn_code_off
+	 * - app0_code_size
+	 * - app1_code_size
+	 * - ...
+	 */
+	u32 apps[0];
+};
+
+void *hs_ucode_load_blob(struct nvkm_subdev *, const struct nvkm_falcon *,
+			 const char *);
+
+#endif

+ 7 - 1
drivers/gpu/drm/nouveau/nvkm/subdev/secboot/ls_ucode.h

@@ -27,6 +27,7 @@
 #include <core/subdev.h>
 #include <subdev/secboot.h>
 
+struct nvkm_acr;
 
 /**
  * struct ls_ucode_img_desc - descriptor of firmware image
@@ -83,6 +84,7 @@ struct ls_ucode_img_desc {
  * @ucode_desc:		loaded or generated map of ucode_data
  * @ucode_data:		firmware payload (code and data)
  * @ucode_size:		size in bytes of data in ucode_data
+ * @ucode_off:		offset of the ucode in ucode_data
  * @sig:		signature for this firmware
  * @sig:size:		size of the signature in bytes
  *
@@ -97,6 +99,7 @@ struct ls_ucode_img {
 	struct ls_ucode_img_desc ucode_desc;
 	u8 *ucode_data;
 	u32 ucode_size;
+	u32 ucode_off;
 
 	u8 *sig;
 	u32 sig_size;
@@ -146,6 +149,9 @@ struct fw_bl_desc {
 
 int acr_ls_ucode_load_fecs(const struct nvkm_subdev *, struct ls_ucode_img *);
 int acr_ls_ucode_load_gpccs(const struct nvkm_subdev *, struct ls_ucode_img *);
-
+int acr_ls_ucode_load_pmu(const struct nvkm_subdev *, struct ls_ucode_img *);
+void acr_ls_pmu_post_run(const struct nvkm_acr *, const struct nvkm_secboot *);
+int acr_ls_ucode_load_sec2(const struct nvkm_subdev *, struct ls_ucode_img *);
+void acr_ls_sec2_post_run(const struct nvkm_acr *, const struct nvkm_secboot *);
 
 #endif

+ 149 - 0
drivers/gpu/drm/nouveau/nvkm/subdev/secboot/ls_ucode_msgqueue.c

@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+
+#include "ls_ucode.h"
+#include "acr.h"
+
+#include <core/firmware.h>
+#include <core/msgqueue.h>
+#include <subdev/pmu.h>
+#include <engine/sec2.h>
+
+/**
+ * acr_ls_ucode_load_msgqueue - load and prepare a ucode img for a msgqueue fw
+ *
+ * Load the LS microcode, desc and signature and pack them into a single
+ * blob.
+ */
+static int
+acr_ls_ucode_load_msgqueue(const struct nvkm_subdev *subdev, const char *name,
+			   struct ls_ucode_img *img)
+{
+	const struct firmware *image, *desc, *sig;
+	char f[64];
+	int ret;
+
+	snprintf(f, sizeof(f), "%s/image", name);
+	ret = nvkm_firmware_get(subdev->device, f, &image);
+	if (ret)
+		return ret;
+	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)
+		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)
+		return ret;
+	img->sig_size = sig->size;
+	img->sig = kmemdup(sig->data, sig->size, GFP_KERNEL);
+	nvkm_firmware_put(sig);
+	if (!img->sig)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void
+acr_ls_msgqueue_post_run(struct nvkm_msgqueue *queue,
+			 struct nvkm_falcon *falcon, u32 addr_args)
+{
+	u32 cmdline_size = NVKM_MSGQUEUE_CMDLINE_SIZE;
+	u8 buf[cmdline_size];
+
+	memset(buf, 0, cmdline_size);
+	nvkm_msgqueue_write_cmdline(queue, buf);
+	nvkm_falcon_load_dmem(falcon, buf, addr_args, cmdline_size, 0);
+	/* rearm the queue so it will wait for the init message */
+	nvkm_msgqueue_reinit(queue);
+}
+
+int
+acr_ls_ucode_load_pmu(const struct nvkm_subdev *subdev,
+		      struct ls_ucode_img *img)
+{
+	struct nvkm_pmu *pmu = subdev->device->pmu;
+	int ret;
+
+	ret = acr_ls_ucode_load_msgqueue(subdev, "pmu", img);
+	if (ret)
+		return ret;
+
+	/* Allocate the PMU queue corresponding to the FW version */
+	ret = nvkm_msgqueue_new(img->ucode_desc.app_version, pmu->falcon,
+				&pmu->queue);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+void
+acr_ls_pmu_post_run(const struct nvkm_acr *acr, const struct nvkm_secboot *sb)
+{
+	struct nvkm_device *device = sb->subdev.device;
+	struct nvkm_pmu *pmu = device->pmu;
+	u32 addr_args = pmu->falcon->data.limit - NVKM_MSGQUEUE_CMDLINE_SIZE;
+
+	acr_ls_msgqueue_post_run(pmu->queue, pmu->falcon, addr_args);
+}
+
+int
+acr_ls_ucode_load_sec2(const struct nvkm_subdev *subdev,
+		       struct ls_ucode_img *img)
+{
+	struct nvkm_sec2 *sec = subdev->device->sec2;
+	int ret;
+
+	ret = acr_ls_ucode_load_msgqueue(subdev, "sec2", img);
+	if (ret)
+		return ret;
+
+	/* Allocate the PMU queue corresponding to the FW version */
+	ret = nvkm_msgqueue_new(img->ucode_desc.app_version, sec->falcon,
+				&sec->queue);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+void
+acr_ls_sec2_post_run(const struct nvkm_acr *acr, const struct nvkm_secboot *sb)
+{
+	struct nvkm_device *device = sb->subdev.device;
+	struct nvkm_sec2 *sec = device->sec2;
+	/* on SEC arguments are always at the beginning of EMEM */
+	u32 addr_args = 0x01000000;
+
+	acr_ls_msgqueue_post_run(sec->queue, sec->falcon, addr_args);
+}

+ 2 - 3
drivers/gpu/drm/nouveau/nvkm/subdev/secboot/priv.h

@@ -30,11 +30,10 @@ struct nvkm_secboot_func {
 	int (*oneinit)(struct nvkm_secboot *);
 	int (*fini)(struct nvkm_secboot *, bool suspend);
 	void *(*dtor)(struct nvkm_secboot *);
-	int (*run_blob)(struct nvkm_secboot *, struct nvkm_gpuobj *);
+	int (*run_blob)(struct nvkm_secboot *, struct nvkm_gpuobj *,
+			struct nvkm_falcon *);
 };
 
-extern const char *nvkm_secboot_falcon_name[];
-
 int nvkm_secboot_ctor(const struct nvkm_secboot_func *, struct nvkm_acr *,
 		      struct nvkm_device *, int, struct nvkm_secboot *);
 int nvkm_secboot_falcon_reset(struct nvkm_secboot *);

+ 1 - 1
drivers/gpu/drm/nouveau/nvkm/subdev/top/gk104.c

@@ -82,7 +82,7 @@ gk104_top_oneinit(struct nvkm_top *top)
 		case 0x0000000a: A_(MSVLD ); break;
 		case 0x0000000b: A_(MSENC ); break;
 		case 0x0000000c: A_(VIC   ); break;
-		case 0x0000000d: A_(SEC   ); break;
+		case 0x0000000d: A_(SEC2  ); break;
 		case 0x0000000e: B_(NVENC ); break;
 		case 0x0000000f: A_(NVENC1); break;
 		case 0x00000010: A_(NVDEC ); break;