drm/nouveau/secboot/gm20b: initialize pointer in gm20b_secboot_new()
[linux-2.6-microblaze.git] / drivers / gpu / drm / nouveau / nvkm / subdev / secboot / gm20b.c
index df8b919..32a584c 100644 (file)
 #include "acr.h"
 #include "gm200.h"
 
-#define TEGRA210_MC_BASE                       0x70019000
-
 #ifdef CONFIG_ARCH_TEGRA
-#define MC_SECURITY_CARVEOUT2_CFG0             0xc58
-#define MC_SECURITY_CARVEOUT2_BOM_0            0xc5c
-#define MC_SECURITY_CARVEOUT2_BOM_HI_0         0xc60
-#define MC_SECURITY_CARVEOUT2_SIZE_128K                0xc64
-#define TEGRA_MC_SECURITY_CARVEOUT_CFG_LOCKED  (1 << 1)
 /**
  * gm20b_secboot_tegra_read_wpr() - read the WPR registers on Tegra
  *
- * On dGPU, we can manage the WPR region ourselves, but on Tegra the WPR region
- * is reserved from system memory by the bootloader and irreversibly locked.
- * This function reads the address and size of the pre-configured WPR region.
+ * On dGPU, we can manage the WPR region ourselves, but on Tegra this region
+ * is allocated from system memory by the secure firmware. The region is then
+ * marked as a "secure carveout" and irreversibly locked. Furthermore, the WPR
+ * secure carveout is also configured to be sent to the GPU via a dedicated
+ * serial bus between the memory controller and the GPU. The GPU requests this
+ * information upon leaving reset and exposes it through a FIFO register at
+ * offset 0x100cd4.
+ *
+ * The FIFO register's lower 4 bits can be used to set the read index into the
+ * FIFO. After each read of the FIFO register, the read index is incremented.
+ *
+ * Indices 2 and 3 contain the lower and upper addresses of the WPR. These are
+ * stored in units of 256 B. The WPR is inclusive of both addresses.
+ *
+ * Unfortunately, for some reason the WPR info register doesn't contain the
+ * correct values for the secure carveout. It seems like the upper address is
+ * always too small by 128 KiB - 1. Given that the secure carvout size in the
+ * memory controller configuration is specified in units of 128 KiB, it's
+ * possible that the computation of the upper address of the WPR is wrong and
+ * causes this difference.
  */
 int
-gm20b_secboot_tegra_read_wpr(struct gm200_secboot *gsb, u32 mc_base)
+gm20b_secboot_tegra_read_wpr(struct gm200_secboot *gsb)
 {
+       struct nvkm_device *device = gsb->base.subdev.device;
        struct nvkm_secboot *sb = &gsb->base;
-       void __iomem *mc;
-       u32 cfg;
+       u64 base, limit;
+       u32 value;
 
-       mc = ioremap(mc_base, 0xd00);
-       if (!mc) {
-               nvkm_error(&sb->subdev, "Cannot map Tegra MC registers\n");
-               return -ENOMEM;
-       }
-       sb->wpr_addr = ioread32_native(mc + MC_SECURITY_CARVEOUT2_BOM_0) |
-             ((u64)ioread32_native(mc + MC_SECURITY_CARVEOUT2_BOM_HI_0) << 32);
-       sb->wpr_size = ioread32_native(mc + MC_SECURITY_CARVEOUT2_SIZE_128K)
-               << 17;
-       cfg = ioread32_native(mc + MC_SECURITY_CARVEOUT2_CFG0);
-       iounmap(mc);
+       /* set WPR info register to point at WPR base address register */
+       value = nvkm_rd32(device, 0x100cd4);
+       value &= ~0xf;
+       value |= 0x2;
+       nvkm_wr32(device, 0x100cd4, value);
+
+       /* read base address */
+       value = nvkm_rd32(device, 0x100cd4);
+       base = (u64)(value >> 4) << 12;
+
+       /* read limit */
+       value = nvkm_rd32(device, 0x100cd4);
+       limit = (u64)(value >> 4) << 12;
+
+       /*
+        * The upper address of the WPR seems to be computed wrongly and is
+        * actually SZ_128K - 1 bytes lower than it should be. Adjust the
+        * value accordingly.
+        */
+       limit += SZ_128K - 1;
+
+       sb->wpr_size = limit - base + 1;
+       sb->wpr_addr = base;
+
+       nvkm_info(&sb->subdev, "WPR: %016llx-%016llx\n", sb->wpr_addr,
+                 sb->wpr_addr + sb->wpr_size - 1);
 
        /* Check that WPR settings are valid */
        if (sb->wpr_size == 0) {
@@ -63,16 +89,11 @@ gm20b_secboot_tegra_read_wpr(struct gm200_secboot *gsb, u32 mc_base)
                return -EINVAL;
        }
 
-       if (!(cfg & TEGRA_MC_SECURITY_CARVEOUT_CFG_LOCKED)) {
-               nvkm_error(&sb->subdev, "WPR region not locked\n");
-               return -EINVAL;
-       }
-
        return 0;
 }
 #else
 int
-gm20b_secboot_tegra_read_wpr(struct gm200_secboot *gsb, u32 mc_base)
+gm20b_secboot_tegra_read_wpr(struct gm200_secboot *gsb)
 {
        nvkm_error(&gsb->base.subdev, "Tegra support not compiled in\n");
        return -EINVAL;
@@ -85,7 +106,7 @@ gm20b_secboot_oneinit(struct nvkm_secboot *sb)
        struct gm200_secboot *gsb = gm200_secboot(sb);
        int ret;
 
-       ret = gm20b_secboot_tegra_read_wpr(gsb, TEGRA210_MC_BASE);
+       ret = gm20b_secboot_tegra_read_wpr(gsb);
        if (ret)
                return ret;
 
@@ -108,6 +129,7 @@ gm20b_secboot_new(struct nvkm_device *device, int index,
        struct gm200_secboot *gsb;
        struct nvkm_acr *acr;
 
+       *psb = NULL;
        acr = acr_r352_new(BIT(NVKM_SECBOOT_FALCON_FECS) |
                           BIT(NVKM_SECBOOT_FALCON_PMU));
        if (IS_ERR(acr))
@@ -116,10 +138,8 @@ gm20b_secboot_new(struct nvkm_device *device, int index,
        acr->optional_falcons = BIT(NVKM_SECBOOT_FALCON_PMU);
 
        gsb = kzalloc(sizeof(*gsb), GFP_KERNEL);
-       if (!gsb) {
-               psb = NULL;
+       if (!gsb)
                return -ENOMEM;
-       }
        *psb = &gsb->base;
 
        ret = nvkm_secboot_ctor(&gm20b_secboot, acr, device, index, &gsb->base);