Files
linux-nvgpu/drivers/gpu/nvgpu/hal/nvlink/minion_gv100.c
Vedashree Vidwans 54e179ddad gpu: nvgpu: fix MISRA 13.5 nvgpu.hal.nvlink
MISRA rule 13.5 doesn't allow right-hand operand of logical && or ||
operator to have persistent side effects. The reason is right-hand
operand is executed or checked depending on the left-hand operand value.
That means side effects in the right-hand operand may or may not occur,
contrary to programmer's expectation. Hence, this rule is implemented to
avoid unexpected behavior.

This patch divides if condition with logical && operator to nested if
conditions to resolve this violation.

Jira NVGPU-3277

Change-Id: I9f4387d71427821278db6bbda2eb53bd4d8ea543
Signed-off-by: Vedashree Vidwans <vvidwans@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/2119684
Reviewed-by: Philip Elcan <pelcan@nvidia.com>
Reviewed-by: svc-mobile-coverity <svc-mobile-coverity@nvidia.com>
Reviewed-by: svc-mobile-misra <svc-mobile-misra@nvidia.com>
GVS: Gerrit_Virtual_Submit
Reviewed-by: Vinod Gopalakrishnakurup <vinodg@nvidia.com>
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
2019-05-16 15:16:00 -07:00

412 lines
12 KiB
C

/*
* Copyright (c) 2019, 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.
*/
#ifdef CONFIG_TEGRA_NVLINK
#include <nvgpu/io.h>
#include <nvgpu/timers.h>
#include <nvgpu/gk20a.h>
#include <nvgpu/nvlink_minion.h>
#include "minion_gv100.h"
#include <nvgpu/hw/gv100/hw_minion_gv100.h>
#define MINION_FALCON_INTR_MASK (minion_falcon_irqmset_wdtmr_set_f() | \
minion_falcon_irqmset_halt_set_f() | \
minion_falcon_irqmset_exterr_set_f()| \
minion_falcon_irqmset_swgen0_set_f()| \
minion_falcon_irqmset_swgen1_set_f())
#define MINION_FALCON_INTR_DEST ( \
minion_falcon_irqdest_host_wdtmr_host_f() | \
minion_falcon_irqdest_host_halt_host_f() | \
minion_falcon_irqdest_host_exterr_host_f() | \
minion_falcon_irqdest_host_swgen0_host_f() | \
minion_falcon_irqdest_host_swgen1_host_f() | \
minion_falcon_irqdest_target_wdtmr_host_normal_f() | \
minion_falcon_irqdest_target_halt_host_normal_f() | \
minion_falcon_irqdest_target_exterr_host_normal_f() | \
minion_falcon_irqdest_target_swgen0_host_normal_f() | \
minion_falcon_irqdest_target_swgen1_host_normal_f())
u32 gv100_nvlink_minion_base_addr(struct gk20a *g)
{
return g->nvlink.minion_base;
}
/*
* Check if minion is up
*/
bool gv100_nvlink_minion_is_running(struct gk20a *g)
{
/* if minion is booted and not halted, it is running */
if ((MINION_REG_RD32(g, minion_minion_status_r()) &
minion_minion_status_status_f(1)) != 0U) {
if (minion_falcon_irqstat_halt_v(
MINION_REG_RD32(g, minion_falcon_irqstat_r())) == 0U) {
return true;
}
}
return false;
}
/*
* Check if minion ucode boot is complete.
*/
int gv100_nvlink_minion_is_boot_complete(struct gk20a *g, bool *boot_cmplte)
{
u32 reg;
int err = 0;
reg = MINION_REG_RD32(g, minion_minion_status_r());
*boot_cmplte = false;
if (minion_minion_status_status_v(reg) != 0U) {
/* Minion sequence completed, check status */
if (minion_minion_status_status_v(reg) ==
minion_minion_status_status_boot_v()) {
*boot_cmplte = true;
} else {
nvgpu_err(g, "MINION init sequence failed: 0x%x",
minion_minion_status_status_v(reg));
err = -EINVAL;
}
}
return err;
}
/*
* Check if MINION command is complete
*/
static int gv100_nvlink_minion_command_complete(struct gk20a *g, u32 link_id)
{
u32 reg;
struct nvgpu_timeout timeout;
u32 delay = POLL_DELAY_MIN_US;
int err = 0;
err = nvgpu_timeout_init(g, &timeout, nvgpu_get_poll_timeout(g),
NVGPU_TIMER_CPU_TIMER);
if (err != 0) {
nvgpu_err(g, "Minion cmd complete timeout init failed");
return err;
}
do {
reg = MINION_REG_RD32(g, minion_nvlink_dl_cmd_r(link_id));
if (minion_nvlink_dl_cmd_ready_v(reg) == 1U) {
/* Command completed, check sucess */
if (minion_nvlink_dl_cmd_fault_v(reg) ==
minion_nvlink_dl_cmd_fault_fault_clear_v()) {
nvgpu_err(g, "minion cmd(%d) error: 0x%x",
link_id, reg);
reg = minion_nvlink_dl_cmd_fault_f(1);
MINION_REG_WR32(g,
minion_nvlink_dl_cmd_r(link_id), reg);
return -EINVAL;
}
/* Command success */
break;
}
nvgpu_usleep_range(delay, delay * 2U);
delay = min_t(unsigned int,
delay << 1, POLL_DELAY_MAX_US);
} while (nvgpu_timeout_expired_msg(&timeout,
"minion cmd timeout") == 0);
if (nvgpu_timeout_peek_expired(&timeout) != 0) {
return -ETIMEDOUT;
}
nvgpu_log(g, gpu_dbg_nvlink, "minion cmd Complete");
return err;
}
u32 gv100_nvlink_minion_get_dlcmd_ordinal(struct gk20a *g,
enum nvgpu_nvlink_minion_dlcmd dlcmd)
{
u32 dlcmd_ordinal;
switch (dlcmd) {
case NVGPU_NVLINK_MINION_DLCMD_INITPHY:
dlcmd_ordinal = minion_nvlink_dl_cmd_command_initphy_v();
break;
case NVGPU_NVLINK_MINION_DLCMD_INITLANEENABLE:
dlcmd_ordinal = minion_nvlink_dl_cmd_command_initlaneenable_v();
break;
case NVGPU_NVLINK_MINION_DLCMD_INITDLPL:
dlcmd_ordinal = minion_nvlink_dl_cmd_command_initdlpl_v();
break;
case NVGPU_NVLINK_MINION_DLCMD_LANEDISABLE:
dlcmd_ordinal = minion_nvlink_dl_cmd_command_lanedisable_v();
break;
case NVGPU_NVLINK_MINION_DLCMD_SETACMODE:
dlcmd_ordinal = minion_nvlink_dl_cmd_command_setacmode_v();
break;
case NVGPU_NVLINK_MINION_DLCMD_LANESHUTDOWN:
dlcmd_ordinal = minion_nvlink_dl_cmd_command_laneshutdown_v();
break;
case NVGPU_NVLINK_MINION_DLCMD_INITPLL_1:
dlcmd_ordinal = minion_nvlink_dl_cmd_command_initpll_1_v();
break;
case NVGPU_NVLINK_MINION_DLCMD_INITPLL_7:
dlcmd_ordinal = minion_nvlink_dl_cmd_command_initpll_7_v();
break;
default:
dlcmd_ordinal = U32_MAX;
break;
}
return dlcmd_ordinal;
}
/*
* Send Minion command (can be async)
*/
int gv100_nvlink_minion_send_dlcmd(struct gk20a *g, u32 link_id,
enum nvgpu_nvlink_minion_dlcmd dlcmd, bool sync)
{
int err = 0;
u32 dlcmd_ordinal;
dlcmd_ordinal = g->ops.nvlink.minion.get_dlcmd_ordinal(g, dlcmd);
if (dlcmd_ordinal == U32_MAX) {
nvgpu_err(g, "DLCMD not supported");
return -EPERM;
}
/* Check last command succeded */
err = gv100_nvlink_minion_command_complete(g, link_id);
if (err != 0) {
return -EINVAL;
}
nvgpu_log(g, gpu_dbg_nvlink, "sending MINION command 0x%x to link %d",
dlcmd_ordinal, link_id);
MINION_REG_WR32(g, minion_nvlink_dl_cmd_r(link_id),
minion_nvlink_dl_cmd_command_f(dlcmd_ordinal) |
minion_nvlink_dl_cmd_fault_f(1));
if (sync) {
err = gv100_nvlink_minion_command_complete(g, link_id);
}
return err;
}
/*
* Clear minion Interrupts
*/
void gv100_nvlink_minion_clear_intr(struct gk20a *g)
{
nvgpu_falcon_set_irq(&g->minion_flcn, true, MINION_FALCON_INTR_MASK,
MINION_FALCON_INTR_DEST);
}
/*
* Initialization of link specific interrupts
*/
void gv100_nvlink_minion_enable_link_intr(struct gk20a *g, u32 link_id,
bool enable)
{
u32 intr, links;
/* Only stall interrupts for now */
intr = MINION_REG_RD32(g, minion_minion_intr_stall_en_r());
links = minion_minion_intr_stall_en_link_v(intr);
if (enable) {
links |= BIT32(link_id);
} else {
links &= ~BIT32(link_id);
}
intr = set_field(intr, minion_minion_intr_stall_en_link_m(),
minion_minion_intr_stall_en_link_f(links));
MINION_REG_WR32(g, minion_minion_intr_stall_en_r(), intr);
}
/*
* Initialization of falcon interrupts
*/
static void gv100_nvlink_minion_falcon_intr_enable(struct gk20a *g, bool enable)
{
u32 reg;
reg = MINION_REG_RD32(g, minion_minion_intr_stall_en_r());
if (enable) {
reg = set_field(reg, minion_minion_intr_stall_en_fatal_m(),
minion_minion_intr_stall_en_fatal_enable_f());
reg = set_field(reg, minion_minion_intr_stall_en_nonfatal_m(),
minion_minion_intr_stall_en_nonfatal_enable_f());
reg = set_field(reg, minion_minion_intr_stall_en_falcon_stall_m(),
minion_minion_intr_stall_en_falcon_stall_enable_f());
reg = set_field(reg, minion_minion_intr_stall_en_falcon_nostall_m(),
minion_minion_intr_stall_en_falcon_nostall_enable_f());
} else {
reg = set_field(reg, minion_minion_intr_stall_en_fatal_m(),
minion_minion_intr_stall_en_fatal_disable_f());
reg = set_field(reg, minion_minion_intr_stall_en_nonfatal_m(),
minion_minion_intr_stall_en_nonfatal_disable_f());
reg = set_field(reg, minion_minion_intr_stall_en_falcon_stall_m(),
minion_minion_intr_stall_en_falcon_stall_disable_f());
reg = set_field(reg, minion_minion_intr_stall_en_falcon_nostall_m(),
minion_minion_intr_stall_en_falcon_nostall_disable_f());
}
MINION_REG_WR32(g, minion_minion_intr_stall_en_r(), reg);
}
/*
* Initialize minion IP interrupts
*/
void gv100_nvlink_minion_init_intr(struct gk20a *g)
{
/* Disable non-stall tree */
MINION_REG_WR32(g, minion_minion_intr_nonstall_en_r(), 0x0);
gv100_nvlink_minion_falcon_intr_enable(g, true);
}
/*
* Falcon specific ISR handling
*/
void gv100_nvlink_minion_falcon_isr(struct gk20a *g)
{
u32 intr;
intr = MINION_REG_RD32(g, minion_falcon_irqstat_r()) &
MINION_REG_RD32(g, minion_falcon_irqmask_r());
if (intr == 0u) {
return;
}
if ((intr & minion_falcon_irqstat_exterr_true_f()) != 0u) {
nvgpu_err(g, "falcon ext addr: 0x%x 0x%x 0x%x",
MINION_REG_RD32(g, minion_falcon_csberrstat_r()),
MINION_REG_RD32(g, minion_falcon_csberr_info_r()),
MINION_REG_RD32(g, minion_falcon_csberr_addr_r()));
}
MINION_REG_WR32(g, minion_falcon_irqsclr_r(), intr);
nvgpu_err(g, "fatal minion irq: 0x%08x", intr);
return;
}
/*
* link specific isr
*/
static void gv100_nvlink_minion_link_isr(struct gk20a *g, u32 link_id)
{
u32 intr, code;
bool fatal = false;
intr = MINION_REG_RD32(g, minion_nvlink_link_intr_r(link_id));
code = minion_nvlink_link_intr_code_v(intr);
if (code == minion_nvlink_link_intr_code_swreq_v()) {
nvgpu_err(g, " Intr SWREQ, link: %d subcode: %x",
link_id, minion_nvlink_link_intr_subcode_v(intr));
} else if (code == minion_nvlink_link_intr_code_pmdisabled_v()) {
nvgpu_err(g, " Fatal Intr PMDISABLED, link: %d subcode: %x",
link_id, minion_nvlink_link_intr_subcode_v(intr));
fatal = true;
} else if (code == minion_nvlink_link_intr_code_na_v()) {
nvgpu_err(g, " Fatal Intr NA, link: %d subcode: %x",
link_id, minion_nvlink_link_intr_subcode_v(intr));
fatal = true;
} else if (code == minion_nvlink_link_intr_code_dlreq_v()) {
nvgpu_err(g, " Fatal Intr DLREQ, link: %d subcode: %x",
link_id, minion_nvlink_link_intr_subcode_v(intr));
fatal = true;
} else {
nvgpu_err(g, " Fatal Intr UNKN:%x, link: %d subcode: %x", code,
link_id, minion_nvlink_link_intr_subcode_v(intr));
fatal = true;
}
if (fatal) {
g->ops.nvlink.minion.enable_link_intr(g, link_id, false);
}
intr = set_field(intr, minion_nvlink_link_intr_state_m(),
minion_nvlink_link_intr_state_f(1));
MINION_REG_WR32(g, minion_nvlink_link_intr_r(link_id), intr);
return;
}
/*
* Global minion routine to service interrupts
*/
void gv100_nvlink_minion_isr(struct gk20a *g) {
u32 intr, link_id;
unsigned long links;
unsigned long bit;
intr = MINION_REG_RD32(g, minion_minion_intr_r()) &
MINION_REG_RD32(g, minion_minion_intr_stall_en_r());
if ((minion_minion_intr_falcon_stall_v(intr) != 0U) ||
(minion_minion_intr_falcon_nostall_v(intr) != 0U)) {
gv100_nvlink_minion_falcon_isr(g);
}
if (minion_minion_intr_fatal_v(intr) != 0U) {
gv100_nvlink_minion_falcon_intr_enable(g, false);
MINION_REG_WR32(g, minion_minion_intr_r(),
minion_minion_intr_fatal_f(1));
}
if (minion_minion_intr_nonfatal_v(intr) != 0U) {
MINION_REG_WR32(g, minion_minion_intr_r(),
minion_minion_intr_nonfatal_f(1));
}
links = minion_minion_intr_link_v(intr) &
(unsigned long) g->nvlink.enabled_links;
if (links != 0UL) {
for_each_set_bit(bit, &links, NVLINK_MAX_LINKS_SW) {
link_id = (u32)bit;
gv100_nvlink_minion_link_isr(g, link_id);
}
}
return;
}
#endif /* CONFIG_TEGRA_NVLINK */