From 748475df20bbe6843bdf4fbc02384dc5aa28866e Mon Sep 17 00:00:00 2001 From: Vijayakumar Date: Tue, 30 Sep 2014 20:19:44 +0530 Subject: gpu: nvgpu: gm20b: Support secure FECS recovery When falcons are secured use PMU commands to reload FECS firmware. Bug 200042729 Change-Id: I09f2472b16dac6a510dba067bce3950075973d5f Signed-off-by: Vijayakumar Reviewed-on: http://git-master/r/552544 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Terje Bergstrom --- drivers/gpu/nvgpu/gk20a/gk20a.h | 4 ++ drivers/gpu/nvgpu/gk20a/pmu_gk20a.c | 19 ++++++-- drivers/gpu/nvgpu/gk20a/pmu_gk20a.h | 90 +++++++++++++++++++++++++++++++++- drivers/gpu/nvgpu/gm20b/gr_gm20b.c | 69 +++++++++++++++++++++++--- drivers/gpu/nvgpu/gm20b/pmu_gm20b.c | 96 ++++++++++++++++++++++++++++++++++++- drivers/gpu/nvgpu/gm20b/pmu_gm20b.h | 1 + 6 files changed, 264 insertions(+), 15 deletions(-) (limited to 'drivers/gpu/nvgpu') diff --git a/drivers/gpu/nvgpu/gk20a/gk20a.h b/drivers/gpu/nvgpu/gk20a/gk20a.h index 20afd2bd..5669e1c5 100644 --- a/drivers/gpu/nvgpu/gk20a/gk20a.h +++ b/drivers/gpu/nvgpu/gk20a/gk20a.h @@ -307,6 +307,10 @@ struct gpu_ops { int (*prepare_ucode)(struct gk20a *g); int (*pmu_setup_hw_and_bootstrap)(struct gk20a *g); int (*pmu_setup_elpg)(struct gk20a *g); + int (*init_wpr_region)(struct gk20a *g); + bool lspmuwprinitdone; + bool fecsbootstrapdone; + u32 fecsrecoveryinprogress; } pmu; struct { int (*init_clk_support)(struct gk20a *g); diff --git a/drivers/gpu/nvgpu/gk20a/pmu_gk20a.c b/drivers/gpu/nvgpu/gk20a/pmu_gk20a.c index e60de70b..0580f19d 100644 --- a/drivers/gpu/nvgpu/gk20a/pmu_gk20a.c +++ b/drivers/gpu/nvgpu/gk20a/pmu_gk20a.c @@ -2262,6 +2262,7 @@ void gk20a_init_pmu_ops(struct gpu_ops *gops) gops->pmu.prepare_ucode = gk20a_prepare_ucode; gops->pmu.pmu_setup_hw_and_bootstrap = gk20a_init_pmu_setup_hw1; gops->pmu.pmu_setup_elpg = NULL; + gops->pmu.init_wpr_region = NULL; } int gk20a_init_pmu_support(struct gk20a *g) @@ -2749,7 +2750,7 @@ static int pmu_response_handle(struct pmu_gk20a *pmu, return 0; } -static int pmu_wait_message_cond(struct pmu_gk20a *pmu, u32 timeout, +int pmu_wait_message_cond(struct pmu_gk20a *pmu, u32 timeout, u32 *var, u32 val); static void pmu_handle_zbc_msg(struct gk20a *g, struct pmu_msg *msg, @@ -2902,10 +2903,14 @@ static int pmu_process_message(struct pmu_gk20a *pmu) { struct pmu_msg msg; int status; + struct gk20a *g = gk20a_from_pmu(pmu); if (unlikely(!pmu->pmu_ready)) { pmu_process_init_msg(pmu, &msg); + if (g->ops.pmu.init_wpr_region != NULL) + g->ops.pmu.init_wpr_region(g); pmu_init_perfmon(pmu); + return 0; } @@ -2930,7 +2935,7 @@ static int pmu_process_message(struct pmu_gk20a *pmu) return 0; } -static int pmu_wait_message_cond(struct pmu_gk20a *pmu, u32 timeout, +int pmu_wait_message_cond(struct pmu_gk20a *pmu, u32 timeout, u32 *var, u32 val) { struct gk20a *g = gk20a_from_pmu(pmu); @@ -3166,10 +3171,11 @@ void gk20a_pmu_isr(struct gk20a *g) mask = gk20a_readl(g, pwr_falcon_irqmask_r()) & gk20a_readl(g, pwr_falcon_irqdest_r()); - intr = gk20a_readl(g, pwr_falcon_irqstat_r()) & mask; + intr = gk20a_readl(g, pwr_falcon_irqstat_r()); gk20a_dbg_pmu("received falcon interrupt: 0x%08x", intr); + intr = gk20a_readl(g, pwr_falcon_irqstat_r()) & mask; if (!intr || pmu->pmu_state == PMU_STATE_OFF) { gk20a_writel(g, pwr_falcon_irqsclr_r(), intr); mutex_unlock(&pmu->isr_mutex); @@ -3631,6 +3637,10 @@ int gk20a_pmu_destroy(struct gk20a *g) pmu->pmu_ready = false; pmu->perfmon_ready = false; pmu->zbc_ready = false; + g->ops.pmu.lspmuwprinitdone = false; + g->ops.pmu.fecsbootstrapdone = false; + g->ops.pmu.fecsrecoveryinprogress = 0; + gk20a_dbg_fn("done"); return 0; @@ -3738,7 +3748,6 @@ int gk20a_pmu_ap_send_command(struct gk20a *g, gk20a_dbg_pmu("cmd post PMU_AP_CMD_ID_INIT"); cmd.cmd.pg.ap_cmd.init.pg_sampling_period_us = p_ap_cmd->init.pg_sampling_period_us; - p_callback = ap_callback_init_and_enable_ctrl; break; case PMU_AP_CMD_ID_INIT_AND_ENABLE_CTRL: @@ -3782,7 +3791,7 @@ int gk20a_pmu_ap_send_command(struct gk20a *g, status = gk20a_pmu_cmd_post(g, &cmd, NULL, NULL, PMU_COMMAND_QUEUE_HPQ, p_callback, pmu, &seq, ~0); - if (!status) { + if (status) { gk20a_dbg_pmu( "%s: Unable to submit Adaptive Power Command %d\n", __func__, p_ap_cmd->cmn.cmd_id); diff --git a/drivers/gpu/nvgpu/gk20a/pmu_gk20a.h b/drivers/gpu/nvgpu/gk20a/pmu_gk20a.h index 6dd1ad3b..823f5484 100644 --- a/drivers/gpu/nvgpu/gk20a/pmu_gk20a.h +++ b/drivers/gpu/nvgpu/gk20a/pmu_gk20a.h @@ -291,7 +291,6 @@ struct pmu_ap { struct ap_ctrl ap_ctrl[PMU_AP_CTRL_ID_MAX]; }; - enum { GK20A_PMU_DMAIDX_UCODE = 0, GK20A_PMU_DMAIDX_VIRT = 1, @@ -390,7 +389,7 @@ struct pmu_ucode_desc { #define PMU_UNIT_INIT (0x07) #define PMU_UNIT_FBBA (0x08) #define PMU_UNIT_DIDLE (0x09) -#define PMU_UNIT_AVAILABLE3 (0x0A) +#define PMU_UNIT_ACR (0x0A) #define PMU_UNIT_AVAILABLE4 (0x0B) #define PMU_UNIT_HDCP_MAIN (0x0C) #define PMU_UNIT_HDCP_V (0x0D) @@ -643,6 +642,89 @@ struct pmu_pg_cmd { }; }; +/* ACR Commands/Message structures */ + +enum { + PMU_ACR_CMD_ID_INIT_WPR_REGION = 0x0 , + PMU_ACR_CMD_ID_BOOTSTRAP_FALCON, +}; + +/* + * Initializes the WPR region details + */ +struct pmu_acr_cmd_init_wpr_details { + u8 cmd_type; + u32 regionid; + u32 wproffset; + +}; + +/* + * falcon ID to bootstrap + */ +struct pmu_acr_cmd_bootstrap_falcon { + u8 cmd_type; + u32 flags; + u32 falconid; +}; + +#define PMU_ACR_CMD_BOOTSTRAP_FALCON_FLAGS_RESET_NO 1 +#define PMU_ACR_CMD_BOOTSTRAP_FALCON_FLAGS_RESET_YES 0 + +struct pmu_acr_cmd { + union { + u8 cmd_type; + struct pmu_acr_cmd_bootstrap_falcon bootstrap_falcon; + struct pmu_acr_cmd_init_wpr_details init_wpr; + }; +}; + +/* acr messages */ + +/* + * returns the WPR region init information + */ +#define PMU_ACR_MSG_ID_INIT_WPR_REGION 0 + +/* + * Returns the Bootstrapped falcon ID to RM + */ +#define PMU_ACR_MSG_ID_BOOTSTRAP_FALCON 1 + +/* + * Returns the WPR init status + */ +#define PMU_ACR_SUCCESS 0 +#define PMU_ACR_ERROR 1 + +/* + * PMU notifies about bootstrap status of falcon + */ +struct pmu_acr_msg_bootstrap_falcon { + u8 msg_type; + union { + u32 errorcode; + u32 falconid; + }; +}; + +struct pmu_acr_msg { + union { + u8 msg_type; + struct pmu_acr_msg_bootstrap_falcon acrmsg; + }; +}; + +/***************************** ACR ERROR CODES ******************************/ +/*! + * Error codes used in PMU-ACR Task + * + * LSF_ACR_INVALID_TRANSCFG_SETUP : Indicates that TRANSCFG Setup is not valid + * MAILBOX1 returns the CTXDMA ID of invalid setup + * + */ +#define ACR_ERROR_INVALID_TRANSCFG_SETUP (0xAC120001) + /* PERFMON */ #define PMU_DOMAIN_GROUP_PSTATE 0 #define PMU_DOMAIN_GROUP_GPC2CLK 1 @@ -770,6 +852,7 @@ struct pmu_cmd { struct pmu_perfmon_cmd perfmon; struct pmu_pg_cmd pg; struct pmu_zbc_cmd zbc; + struct pmu_acr_cmd acr; } cmd; }; @@ -780,6 +863,7 @@ struct pmu_msg { struct pmu_perfmon_msg perfmon; struct pmu_pg_msg pg; struct pmu_rc_msg rc; + struct pmu_acr_msg acr; } msg; }; @@ -1145,4 +1229,6 @@ int gk20a_pmu_ap_send_command(struct gk20a *g, int gk20a_aelpg_init(struct gk20a *g); int gk20a_aelpg_init_and_enable(struct gk20a *g, u8 ctrl_id); void pmu_enable_irq(struct pmu_gk20a *pmu, bool enable); +int pmu_wait_message_cond(struct pmu_gk20a *pmu, u32 timeout, + u32 *var, u32 val); #endif /*__PMU_GK20A_H__*/ diff --git a/drivers/gpu/nvgpu/gm20b/gr_gm20b.c b/drivers/gpu/nvgpu/gm20b/gr_gm20b.c index 660ffa88..8a3de4e8 100644 --- a/drivers/gpu/nvgpu/gm20b/gr_gm20b.c +++ b/drivers/gpu/nvgpu/gm20b/gr_gm20b.c @@ -14,6 +14,7 @@ */ #include +#include /* for mdelay */ #include "gk20a/gk20a.h" #include "gk20a/gr_gk20a.h" @@ -24,6 +25,8 @@ #include "hw_proj_gm20b.h" #include "hw_ctxsw_prog_gm20b.h" #include "hw_fuse_gm20b.h" +#include "pmu_gm20b.h" +#include "acr_gm20b.h" static void gr_gm20b_init_gpc_mmu(struct gk20a *g) { @@ -625,8 +628,29 @@ static void gr_gm20b_load_gpccs_with_bootloader(struct gk20a *g) gr_fecs_falcon_hwcfg_r()); } +static int gr_gm20b_ctx_wait_lsf_ready(struct gk20a *g, u32 timeout, u32 val) +{ + unsigned long end_jiffies = jiffies + msecs_to_jiffies(timeout); + unsigned long delay = GR_IDLE_CHECK_DEFAULT; + u32 reg; + + gk20a_dbg_fn(""); + reg = gk20a_readl(g, gr_fecs_ctxsw_mailbox_r(0)); + do { + reg = gk20a_readl(g, gr_fecs_ctxsw_mailbox_r(0)); + if (reg == val) + return 0; + usleep_range(delay, delay * 2); + delay = min_t(u32, delay << 1, GR_IDLE_CHECK_MAX); + } while (time_before(jiffies, end_jiffies) || + !tegra_platform_is_silicon()); + + return -ETIMEDOUT; +} + static int gr_gm20b_load_ctxsw_ucode(struct gk20a *g) { + u32 err; gk20a_dbg_fn(""); if (tegra_platform_is_linsim()) { @@ -636,16 +660,49 @@ static int gr_gm20b_load_ctxsw_ucode(struct gk20a *g) gr_gpccs_ctxsw_mailbox_value_f(0xc0de7777)); } + gk20a_writel(g, gr_fecs_ctxsw_mailbox_clear_r(0), ~0x0); + gm20b_pmu_load_lsf(g, LSF_FALCON_ID_FECS); + gr_gm20b_load_gpccs_with_bootloader(g); - gk20a_writel(g, gr_fecs_ctxsw_mailbox_clear_r(0), 0x0); - gk20a_writel(g, gr_fecs_ctxsw_mailbox_r(1), 0x1); - gk20a_writel(g, gr_fecs_ctxsw_mailbox_clear_r(6), 0xffffffff); + if (g->ops.pmu.fecsrecoveryinprogress) { + unsigned long timeout = gk20a_get_gr_idle_timeout(g); + err = gr_gm20b_ctx_wait_lsf_ready(g, timeout, 0x55AA55AA); + if (err) { + gk20a_err(dev_from_gk20a(g), "Unable to recover FECS"); + return err; + } else { + g->ops.pmu.fecsrecoveryinprogress = 0; + gk20a_writel(g, gr_fecs_ctxsw_mailbox_clear_r(0), ~0x0); + gk20a_writel(g, gr_fecs_ctxsw_mailbox_r(1), 0x1); + gk20a_writel(g, gr_fecs_ctxsw_mailbox_clear_r(6), + 0xffffffff); + + gk20a_writel(g, gr_gpccs_dmactl_r(), + gr_gpccs_dmactl_require_ctx_f(0)); + gk20a_writel(g, gr_gpccs_cpuctl_r(), + gr_gpccs_cpuctl_startcpu_f(1)); + + gk20a_writel(g, gr_fecs_cpuctl_alias_r(), + gr_fecs_cpuctl_startcpu_f(1)); + } + } - gk20a_writel(g, gr_gpccs_dmactl_r(), gr_gpccs_dmactl_require_ctx_f(0)); - gk20a_writel(g, gr_gpccs_cpuctl_r(), gr_gpccs_cpuctl_startcpu_f(1)); - gk20a_writel(g, gr_fecs_cpuctl_alias_r(), gr_fecs_cpuctl_startcpu_f(1)); + if (!g->ops.pmu.fecsbootstrapdone) { + g->ops.pmu.fecsbootstrapdone = true; + gk20a_writel(g, gr_fecs_ctxsw_mailbox_clear_r(0), ~0x0); + gk20a_writel(g, gr_fecs_ctxsw_mailbox_r(1), 0x1); + gk20a_writel(g, gr_fecs_ctxsw_mailbox_clear_r(6), 0xffffffff); + + gk20a_writel(g, gr_gpccs_dmactl_r(), + gr_gpccs_dmactl_require_ctx_f(0)); + gk20a_writel(g, gr_gpccs_cpuctl_r(), + gr_gpccs_cpuctl_startcpu_f(1)); + + gk20a_writel(g, gr_fecs_cpuctl_alias_r(), + gr_fecs_cpuctl_startcpu_f(1)); + } gk20a_dbg_fn("done"); diff --git a/drivers/gpu/nvgpu/gm20b/pmu_gm20b.c b/drivers/gpu/nvgpu/gm20b/pmu_gm20b.c index 98dc6845..91927950 100644 --- a/drivers/gpu/nvgpu/gm20b/pmu_gm20b.c +++ b/drivers/gpu/nvgpu/gm20b/pmu_gm20b.c @@ -13,7 +13,9 @@ * more details. */ +#include /* for udelay */ #include "gk20a/gk20a.h" +#include "gk20a/pmu_gk20a.h" #include "acr_gm20b.h" #include "pmu_gm20b.h" @@ -26,6 +28,9 @@ struct pg_init_sequence_list { u32 writeval; }; +#define gm20b_dbg_pmu(fmt, arg...) \ + gk20a_dbg(gpu_dbg_pmu, fmt, ##arg) + /* PROD settings for ELPG sequencing registers*/ static struct pg_init_sequence_list _pginitseq_gm20b[] = { @@ -148,11 +153,98 @@ int gm20b_pmu_setup_elpg(struct gk20a *g) return ret; } +void pmu_handle_acr_init_wpr_msg(struct gk20a *g, struct pmu_msg *msg, + void *param, u32 handle, u32 status) +{ + gk20a_dbg_fn(""); + + gm20b_dbg_pmu("reply PMU_ACR_CMD_ID_INIT_WPR_REGION"); + + if (msg->msg.acr.acrmsg.errorcode == PMU_ACR_SUCCESS) + g->ops.pmu.lspmuwprinitdone = true; + gk20a_dbg_fn("done"); +} + + +int gm20b_pmu_init_acr(struct gk20a *g) +{ + struct pmu_gk20a *pmu = &g->pmu; + struct pmu_cmd cmd; + u32 seq; + + gk20a_dbg_fn(""); + + /* init ACR */ + memset(&cmd, 0, sizeof(struct pmu_cmd)); + cmd.hdr.unit_id = PMU_UNIT_ACR; + cmd.hdr.size = PMU_CMD_HDR_SIZE + + sizeof(struct pmu_acr_cmd_init_wpr_details); + cmd.cmd.acr.init_wpr.cmd_type = PMU_ACR_CMD_ID_INIT_WPR_REGION; + cmd.cmd.acr.init_wpr.regionid = 0x01; + cmd.cmd.acr.init_wpr.wproffset = 0x00; + gm20b_dbg_pmu("cmd post PMU_ACR_CMD_ID_INIT_WPR_REGION"); + gk20a_pmu_cmd_post(g, &cmd, NULL, NULL, PMU_COMMAND_QUEUE_HPQ, + pmu_handle_acr_init_wpr_msg, pmu, &seq, ~0); + + gk20a_dbg_fn("done"); + return 0; +} + +void pmu_handle_fecs_boot_acr_msg(struct gk20a *g, struct pmu_msg *msg, + void *param, u32 handle, u32 status) +{ + + gk20a_dbg_fn(""); + + + if (msg->msg.acr.acrmsg.falconid == LSF_FALCON_ID_FECS) + gm20b_dbg_pmu("reply PMU_ACR_CMD_ID_BOOTSTRAP_FALCON"); + + gm20b_dbg_pmu("response code = %x\n", msg->msg.acr.acrmsg.falconid); + gk20a_dbg_fn("done"); +} + +void gm20b_pmu_load_lsf(struct gk20a *g, u8 falcon_id) +{ + struct pmu_gk20a *pmu = &g->pmu; + struct pmu_cmd cmd; + u32 seq; + + gk20a_dbg_fn(""); + + gm20b_dbg_pmu("wprinit status = %x\n", g->ops.pmu.lspmuwprinitdone); + if (g->ops.pmu.lspmuwprinitdone && g->ops.pmu.fecsbootstrapdone) { + /* send message to load FECS falcon */ + memset(&cmd, 0, sizeof(struct pmu_cmd)); + cmd.hdr.unit_id = PMU_UNIT_ACR; + cmd.hdr.size = PMU_CMD_HDR_SIZE + + sizeof(struct pmu_acr_cmd_bootstrap_falcon); + cmd.cmd.acr.bootstrap_falcon.cmd_type = + PMU_ACR_CMD_ID_BOOTSTRAP_FALCON; + cmd.cmd.acr.bootstrap_falcon.flags = + PMU_ACR_CMD_BOOTSTRAP_FALCON_FLAGS_RESET_YES; + cmd.cmd.acr.bootstrap_falcon.falconid = falcon_id; + gm20b_dbg_pmu("cmd post PMU_ACR_CMD_ID_BOOTSTRAP_FALCON"); + g->ops.pmu.fecsrecoveryinprogress = 1; + gk20a_pmu_cmd_post(g, &cmd, NULL, NULL, PMU_COMMAND_QUEUE_HPQ, + pmu_handle_fecs_boot_acr_msg, pmu, &seq, ~0); + } + + gk20a_dbg_fn("done"); + return; +} + void gm20b_init_pmu_ops(struct gpu_ops *gops) { - if (gops->privsecurity) + if (gops->privsecurity) { gm20b_init_secure_pmu(gops); - else + gops->pmu.init_wpr_region = gm20b_pmu_init_acr; + } else { gk20a_init_pmu_ops(gops); + gops->pmu.init_wpr_region = NULL; + } gops->pmu.pmu_setup_elpg = gm20b_pmu_setup_elpg; + gops->pmu.lspmuwprinitdone = false; + gops->pmu.fecsbootstrapdone = false; + gops->pmu.fecsrecoveryinprogress = 0; } diff --git a/drivers/gpu/nvgpu/gm20b/pmu_gm20b.h b/drivers/gpu/nvgpu/gm20b/pmu_gm20b.h index fc2f7d60..f62a0786 100644 --- a/drivers/gpu/nvgpu/gm20b/pmu_gm20b.h +++ b/drivers/gpu/nvgpu/gm20b/pmu_gm20b.h @@ -17,5 +17,6 @@ #define __PMU_GM20B_H_ void gm20b_init_pmu_ops(struct gpu_ops *gops); +void gm20b_pmu_load_lsf(struct gk20a *g, u8 falcon_id); #endif /*__PMU_GM20B_H_*/ -- cgit v1.2.2