Merge tag 'drm-next-2019-05-16' of git://anongit.freedesktop.org/drm/drm
[linux-2.6-block.git] / drivers / gpu / drm / amd / amdgpu / amdgpu_psp.c
index 3091488cd8cca9c823ba707c7fa42f48b756b5ca..05897b05766b1bb6ff300d0203404b208d5a8807 100644 (file)
@@ -38,18 +38,10 @@ static void psp_set_funcs(struct amdgpu_device *adev);
 static int psp_early_init(void *handle)
 {
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+       struct psp_context *psp = &adev->psp;
 
        psp_set_funcs(adev);
 
-       return 0;
-}
-
-static int psp_sw_init(void *handle)
-{
-       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-       struct psp_context *psp = &adev->psp;
-       int ret;
-
        switch (adev->asic_type) {
        case CHIP_VEGA10:
        case CHIP_VEGA12:
@@ -67,6 +59,15 @@ static int psp_sw_init(void *handle)
 
        psp->adev = adev;
 
+       return 0;
+}
+
+static int psp_sw_init(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+       struct psp_context *psp = &adev->psp;
+       int ret;
+
        ret = psp_init_microcode(psp);
        if (ret) {
                DRM_ERROR("Failed to load psp firmware!\n");
@@ -120,6 +121,7 @@ psp_cmd_submit_buf(struct psp_context *psp,
 {
        int ret;
        int index;
+       int timeout = 2000;
 
        memset(psp->cmd_buf_mem, 0, PSP_CMD_BUFFER_SIZE);
 
@@ -133,8 +135,11 @@ psp_cmd_submit_buf(struct psp_context *psp,
                return ret;
        }
 
-       while (*((unsigned int *)psp->fence_buf) != index)
+       while (*((unsigned int *)psp->fence_buf) != index) {
+               if (--timeout == 0)
+                       break;
                msleep(1);
+       }
 
        /* In some cases, psp response status is not 0 even there is no
         * problem while the command is submitted. Some version of PSP FW
@@ -143,12 +148,14 @@ psp_cmd_submit_buf(struct psp_context *psp,
         * during psp initialization to avoid breaking hw_init and it doesn't
         * return -EINVAL.
         */
-       if (psp->cmd_buf_mem->resp.status) {
+       if (psp->cmd_buf_mem->resp.status || !timeout) {
                if (ucode)
                        DRM_WARN("failed to load ucode id (%d) ",
                                  ucode->ucode_id);
                DRM_WARN("psp command failed and response status is (%d)\n",
                          psp->cmd_buf_mem->resp.status);
+               if (!timeout)
+                       return -EINVAL;
        }
 
        /* get xGMI session id from response buffer */
@@ -181,13 +188,13 @@ static int psp_tmr_init(struct psp_context *psp)
        int ret;
 
        /*
-        * Allocate 3M memory aligned to 1M from Frame Buffer (local
-        * physical).
+        * According to HW engineer, they prefer the TMR address be "naturally
+        * aligned" , e.g. the start address be an integer divide of TMR size.
         *
         * Note: this memory need be reserved till the driver
         * uninitializes.
         */
-       ret = amdgpu_bo_create_kernel(psp->adev, PSP_TMR_SIZE, 0x100000,
+       ret = amdgpu_bo_create_kernel(psp->adev, PSP_TMR_SIZE, PSP_TMR_SIZE,
                                      AMDGPU_GEM_DOMAIN_VRAM,
                                      &psp->tmr_bo, &psp->tmr_mc_addr, &psp->tmr_buf);
 
@@ -466,6 +473,206 @@ static int psp_xgmi_initialize(struct psp_context *psp)
        return ret;
 }
 
+// ras begin
+static void psp_prep_ras_ta_load_cmd_buf(struct psp_gfx_cmd_resp *cmd,
+               uint64_t ras_ta_mc, uint64_t ras_mc_shared,
+               uint32_t ras_ta_size, uint32_t shared_size)
+{
+       cmd->cmd_id = GFX_CMD_ID_LOAD_TA;
+       cmd->cmd.cmd_load_ta.app_phy_addr_lo = lower_32_bits(ras_ta_mc);
+       cmd->cmd.cmd_load_ta.app_phy_addr_hi = upper_32_bits(ras_ta_mc);
+       cmd->cmd.cmd_load_ta.app_len = ras_ta_size;
+
+       cmd->cmd.cmd_load_ta.cmd_buf_phy_addr_lo = lower_32_bits(ras_mc_shared);
+       cmd->cmd.cmd_load_ta.cmd_buf_phy_addr_hi = upper_32_bits(ras_mc_shared);
+       cmd->cmd.cmd_load_ta.cmd_buf_len = shared_size;
+}
+
+static int psp_ras_init_shared_buf(struct psp_context *psp)
+{
+       int ret;
+
+       /*
+        * Allocate 16k memory aligned to 4k from Frame Buffer (local
+        * physical) for ras ta <-> Driver
+        */
+       ret = amdgpu_bo_create_kernel(psp->adev, PSP_RAS_SHARED_MEM_SIZE,
+                       PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM,
+                       &psp->ras.ras_shared_bo,
+                       &psp->ras.ras_shared_mc_addr,
+                       &psp->ras.ras_shared_buf);
+
+       return ret;
+}
+
+static int psp_ras_load(struct psp_context *psp)
+{
+       int ret;
+       struct psp_gfx_cmd_resp *cmd;
+
+       /*
+        * TODO: bypass the loading in sriov for now
+        */
+       if (amdgpu_sriov_vf(psp->adev))
+               return 0;
+
+       cmd = kzalloc(sizeof(struct psp_gfx_cmd_resp), GFP_KERNEL);
+       if (!cmd)
+               return -ENOMEM;
+
+       memset(psp->fw_pri_buf, 0, PSP_1_MEG);
+       memcpy(psp->fw_pri_buf, psp->ta_ras_start_addr, psp->ta_ras_ucode_size);
+
+       psp_prep_ras_ta_load_cmd_buf(cmd, psp->fw_pri_mc_addr,
+                       psp->ras.ras_shared_mc_addr,
+                       psp->ta_ras_ucode_size, PSP_RAS_SHARED_MEM_SIZE);
+
+       ret = psp_cmd_submit_buf(psp, NULL, cmd,
+                       psp->fence_buf_mc_addr);
+
+       if (!ret) {
+               psp->ras.ras_initialized = 1;
+               psp->ras.session_id = cmd->resp.session_id;
+       }
+
+       kfree(cmd);
+
+       return ret;
+}
+
+static void psp_prep_ras_ta_unload_cmd_buf(struct psp_gfx_cmd_resp *cmd,
+                                               uint32_t ras_session_id)
+{
+       cmd->cmd_id = GFX_CMD_ID_UNLOAD_TA;
+       cmd->cmd.cmd_unload_ta.session_id = ras_session_id;
+}
+
+static int psp_ras_unload(struct psp_context *psp)
+{
+       int ret;
+       struct psp_gfx_cmd_resp *cmd;
+
+       /*
+        * TODO: bypass the unloading in sriov for now
+        */
+       if (amdgpu_sriov_vf(psp->adev))
+               return 0;
+
+       cmd = kzalloc(sizeof(struct psp_gfx_cmd_resp), GFP_KERNEL);
+       if (!cmd)
+               return -ENOMEM;
+
+       psp_prep_ras_ta_unload_cmd_buf(cmd, psp->ras.session_id);
+
+       ret = psp_cmd_submit_buf(psp, NULL, cmd,
+                       psp->fence_buf_mc_addr);
+
+       kfree(cmd);
+
+       return ret;
+}
+
+static void psp_prep_ras_ta_invoke_cmd_buf(struct psp_gfx_cmd_resp *cmd,
+               uint32_t ta_cmd_id,
+               uint32_t ras_session_id)
+{
+       cmd->cmd_id = GFX_CMD_ID_INVOKE_CMD;
+       cmd->cmd.cmd_invoke_cmd.session_id = ras_session_id;
+       cmd->cmd.cmd_invoke_cmd.ta_cmd_id = ta_cmd_id;
+       /* Note: cmd_invoke_cmd.buf is not used for now */
+}
+
+int psp_ras_invoke(struct psp_context *psp, uint32_t ta_cmd_id)
+{
+       int ret;
+       struct psp_gfx_cmd_resp *cmd;
+
+       /*
+        * TODO: bypass the loading in sriov for now
+        */
+       if (amdgpu_sriov_vf(psp->adev))
+               return 0;
+
+       cmd = kzalloc(sizeof(struct psp_gfx_cmd_resp), GFP_KERNEL);
+       if (!cmd)
+               return -ENOMEM;
+
+       psp_prep_ras_ta_invoke_cmd_buf(cmd, ta_cmd_id,
+                       psp->ras.session_id);
+
+       ret = psp_cmd_submit_buf(psp, NULL, cmd,
+                       psp->fence_buf_mc_addr);
+
+       kfree(cmd);
+
+       return ret;
+}
+
+int psp_ras_enable_features(struct psp_context *psp,
+               union ta_ras_cmd_input *info, bool enable)
+{
+       struct ta_ras_shared_memory *ras_cmd;
+       int ret;
+
+       if (!psp->ras.ras_initialized)
+               return -EINVAL;
+
+       ras_cmd = (struct ta_ras_shared_memory *)psp->ras.ras_shared_buf;
+       memset(ras_cmd, 0, sizeof(struct ta_ras_shared_memory));
+
+       if (enable)
+               ras_cmd->cmd_id = TA_RAS_COMMAND__ENABLE_FEATURES;
+       else
+               ras_cmd->cmd_id = TA_RAS_COMMAND__DISABLE_FEATURES;
+
+       ras_cmd->ras_in_message = *info;
+
+       ret = psp_ras_invoke(psp, ras_cmd->cmd_id);
+       if (ret)
+               return -EINVAL;
+
+       return ras_cmd->ras_status;
+}
+
+static int psp_ras_terminate(struct psp_context *psp)
+{
+       int ret;
+
+       if (!psp->ras.ras_initialized)
+               return 0;
+
+       ret = psp_ras_unload(psp);
+       if (ret)
+               return ret;
+
+       psp->ras.ras_initialized = 0;
+
+       /* free ras shared memory */
+       amdgpu_bo_free_kernel(&psp->ras.ras_shared_bo,
+                       &psp->ras.ras_shared_mc_addr,
+                       &psp->ras.ras_shared_buf);
+
+       return 0;
+}
+
+static int psp_ras_initialize(struct psp_context *psp)
+{
+       int ret;
+
+       if (!psp->ras.ras_initialized) {
+               ret = psp_ras_init_shared_buf(psp);
+               if (ret)
+                       return ret;
+       }
+
+       ret = psp_ras_load(psp);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+// ras end
+
 static int psp_hw_start(struct psp_context *psp)
 {
        struct amdgpu_device *adev = psp->adev;
@@ -473,25 +680,35 @@ static int psp_hw_start(struct psp_context *psp)
 
        if (!amdgpu_sriov_vf(adev) || !adev->in_gpu_reset) {
                ret = psp_bootloader_load_sysdrv(psp);
-               if (ret)
+               if (ret) {
+                       DRM_ERROR("PSP load sysdrv failed!\n");
                        return ret;
+               }
 
                ret = psp_bootloader_load_sos(psp);
-               if (ret)
+               if (ret) {
+                       DRM_ERROR("PSP load sos failed!\n");
                        return ret;
+               }
        }
 
        ret = psp_ring_create(psp, PSP_RING_TYPE__KM);
-       if (ret)
+       if (ret) {
+               DRM_ERROR("PSP create ring failed!\n");
                return ret;
+       }
 
        ret = psp_tmr_load(psp);
-       if (ret)
+       if (ret) {
+               DRM_ERROR("PSP load tmr failed!\n");
                return ret;
+       }
 
        ret = psp_asd_load(psp);
-       if (ret)
+       if (ret) {
+               DRM_ERROR("PSP load asd failed!\n");
                return ret;
+       }
 
        if (adev->gmc.xgmi.num_physical_nodes > 1) {
                ret = psp_xgmi_initialize(psp);
@@ -502,6 +719,15 @@ static int psp_hw_start(struct psp_context *psp)
                        dev_err(psp->adev->dev,
                                "XGMI: Failed to initialize XGMI session\n");
        }
+
+
+       if (psp->adev->psp.ta_fw) {
+               ret = psp_ras_initialize(psp);
+               if (ret)
+                       dev_err(psp->adev->dev,
+                                       "RAS: Failed to initialize RAS\n");
+       }
+
        return 0;
 }
 
@@ -665,53 +891,52 @@ static int psp_load_fw(struct amdgpu_device *adev)
                                        &psp->fence_buf_mc_addr,
                                        &psp->fence_buf);
        if (ret)
-               goto failed_mem2;
+               goto failed;
 
        ret = amdgpu_bo_create_kernel(adev, PSP_CMD_BUFFER_SIZE, PAGE_SIZE,
                                      AMDGPU_GEM_DOMAIN_VRAM,
                                      &psp->cmd_buf_bo, &psp->cmd_buf_mc_addr,
                                      (void **)&psp->cmd_buf_mem);
        if (ret)
-               goto failed_mem1;
+               goto failed;
 
        memset(psp->fence_buf, 0, PSP_FENCE_BUFFER_SIZE);
 
        ret = psp_ring_init(psp, PSP_RING_TYPE__KM);
-       if (ret)
-               goto failed_mem;
+       if (ret) {
+               DRM_ERROR("PSP ring init failed!\n");
+               goto failed;
+       }
 
        ret = psp_tmr_init(psp);
-       if (ret)
-               goto failed_mem;
+       if (ret) {
+               DRM_ERROR("PSP tmr init failed!\n");
+               goto failed;
+       }
 
        ret = psp_asd_init(psp);
-       if (ret)
-               goto failed_mem;
+       if (ret) {
+               DRM_ERROR("PSP asd init failed!\n");
+               goto failed;
+       }
 
 skip_memalloc:
        ret = psp_hw_start(psp);
        if (ret)
-               goto failed_mem;
+               goto failed;
 
        ret = psp_np_fw_load(psp);
        if (ret)
-               goto failed_mem;
+               goto failed;
 
        return 0;
 
-failed_mem:
-       amdgpu_bo_free_kernel(&psp->cmd_buf_bo,
-                             &psp->cmd_buf_mc_addr,
-                             (void **)&psp->cmd_buf_mem);
-failed_mem1:
-       amdgpu_bo_free_kernel(&psp->fence_buf_bo,
-                             &psp->fence_buf_mc_addr, &psp->fence_buf);
-failed_mem2:
-       amdgpu_bo_free_kernel(&psp->fw_pri_bo,
-                             &psp->fw_pri_mc_addr, &psp->fw_pri_buf);
 failed:
-       kfree(psp->cmd);
-       psp->cmd = NULL;
+       /*
+        * all cleanup jobs (xgmi terminate, ras terminate,
+        * ring destroy, cmd/fence/fw buffers destory,
+        * psp->cmd destory) are delayed to psp_hw_fini
+        */
        return ret;
 }
 
@@ -753,6 +978,9 @@ static int psp_hw_fini(void *handle)
            psp->xgmi_context.initialized == 1)
                 psp_xgmi_terminate(psp);
 
+       if (psp->adev->psp.ta_fw)
+               psp_ras_terminate(psp);
+
        psp_ring_destroy(psp, PSP_RING_TYPE__KM);
 
        amdgpu_bo_free_kernel(&psp->tmr_bo, &psp->tmr_mc_addr, &psp->tmr_buf);
@@ -786,6 +1014,14 @@ static int psp_suspend(void *handle)
                }
        }
 
+       if (psp->adev->psp.ta_fw) {
+               ret = psp_ras_terminate(psp);
+               if (ret) {
+                       DRM_ERROR("Failed to terminate ras ta\n");
+                       return ret;
+               }
+       }
+
        ret = psp_ring_stop(psp, PSP_RING_TYPE__KM);
        if (ret) {
                DRM_ERROR("PSP ring stop failed\n");