/*
* PVA mailbox code
*
* Copyright (c) 2016-2018, NVIDIA Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include "nvhost_acm.h"
#include "dev.h"
#include "pva.h"
#include "pva_mailbox.h"
static u32 pva_get_mb_reg_id(u32 i)
{
u32 mb_reg_id[VALID_MB_INPUT_REGS] = {
0,
1,
2,
3
};
return mb_reg_id[i];
}
static u32 pva_get_mb_reg_ex(u32 i)
{
u32 mb_reg[VALID_MB_INPUT_REGS_EX] = {
hsp_sm0_r(),
hsp_sm1_r(),
hsp_sm2_r(),
hsp_sm3_r(),
hsp_sm4_r(),
hsp_sm5_r(),
hsp_sm6_r(),
hsp_sm7_r()
};
return mb_reg[i];
}
u32 pva_read_mailbox(struct platform_device *pdev, u32 mbox_id)
{
u32 side_bits = 0;
u32 mbox_value = 0;
u32 side_channel_addr = pva_get_mb_reg_ex(PVA_MBOX_SIDE_CHANNEL_HOST_RD);
side_bits = host1x_readl(pdev, side_channel_addr);
mbox_value = host1x_readl(pdev, pva_get_mb_reg_ex(mbox_id));
side_bits = ((side_bits >> mbox_id) & 0x1) << PVA_SIDE_CHANNEL_MBOX_BIT;
mbox_value = (mbox_value & PVA_SIDE_CHANNEL_MBOX_BIT_MASK) | side_bits;
return mbox_value;
}
void pva_write_mailbox(struct platform_device *pdev, u32 mbox_id, u32 value)
{
u32 side_bits = 0;
u32 side_channel_addr = pva_get_mb_reg_ex(PVA_MBOX_SIDE_CHANNEL_HOST_WR);
side_bits = host1x_readl(pdev, side_channel_addr);
side_bits &= ~(1 << mbox_id);
side_bits |= ((value >> PVA_SIDE_CHANNEL_MBOX_BIT) & 0x1) << mbox_id;
value = (value & PVA_SIDE_CHANNEL_MBOX_BIT_MASK);
host1x_writel(pdev, side_channel_addr, side_bits);
host1x_writel(pdev, pva_get_mb_reg_ex(mbox_id), value);
}
static int pva_mailbox_send_cmd(struct pva *pva, struct pva_cmd *cmd,
u32 nregs)
{
struct platform_device *pdev = pva->pdev;
u32 reg, status;
int i;
if (nregs > VALID_MB_INPUT_REGS) {
pr_err("%s nregs %d more than expected\n", __func__, nregs);
return -EINVAL;
}
/* Make sure the state is what we expect it to be. */
status = pva_read_mailbox(pdev, PVA_MBOX_ISR);
WARN_ON((status & PVA_INT_PENDING));
WARN_ON((status & PVA_READY) == 0);
WARN_ON((status & PVA_BUSY));
/* Write all of the other command mailbox
* registers before writing mailbox 0.
*/
for (i = (nregs - 1); i >= 0; i--) {
reg = pva_get_mb_reg_id(i);
pva_write_mailbox(pdev, reg, cmd->mbox[i]);
}
return 0;
}
int pva_mailbox_wait_event(struct pva *pva, int wait_time)
{
int timeout = 1;
int err;
/* Wait for the event being triggered in ISR */
if (pva->timeout_enabled == true)
timeout = wait_event_timeout(pva->mailbox_waitqueue,
pva->mailbox_status == PVA_MBOX_STATUS_DONE ||
pva->mailbox_status == PVA_MBOX_STATUS_ABORTED,
msecs_to_jiffies(wait_time));
else
wait_event(pva->mailbox_waitqueue,
pva->mailbox_status == PVA_MBOX_STATUS_DONE ||
pva->mailbox_status == PVA_MBOX_STATUS_ABORTED);
if (timeout <= 0) {
err = -ETIMEDOUT;
pva_abort(pva);
} else if (pva->mailbox_status == PVA_MBOX_STATUS_ABORTED)
err = -EIO;
else
err = 0;
return err;
}
void pva_mailbox_isr(struct pva *pva)
{
struct platform_device *pdev = pva->pdev;
u32 int_status = pva_read_mailbox(pdev, PVA_MBOX_ISR);
if (pva->mailbox_status != PVA_MBOX_STATUS_WFI) {
nvhost_warn(&pdev->dev, "Unexpected PVA ISR (%x)", int_status);
return;
}
/* Save the current command and subcommand for later processing */
pva->mailbox_status_regs.cmd = pva_read_mailbox(pdev, PVA_MBOX_COMMAND);
/* Get all the valid status register data */
if (int_status & PVA_VALID_STATUS3) {
pva->mailbox_status_regs.status[PVA_CCQ_STATUS3_INDEX] =
host1x_readl(pdev, cfg_ccq_status3_r());
if (int_status & PVA_CMD_ERROR)
pva->mailbox_status_regs.error =
PVA_GET_ERROR_CODE(
pva->mailbox_status_regs.status[PVA_CCQ_STATUS3_INDEX]
);
}
if (int_status & PVA_VALID_STATUS4)
pva->mailbox_status_regs.status[PVA_CCQ_STATUS4_INDEX] =
host1x_readl(pdev, cfg_ccq_status4_r());
if (int_status & PVA_VALID_STATUS5)
pva->mailbox_status_regs.status[PVA_CCQ_STATUS5_INDEX] =
host1x_readl(pdev, cfg_ccq_status5_r());
if (int_status & PVA_VALID_STATUS6)
pva->mailbox_status_regs.status[PVA_CCQ_STATUS6_INDEX] =
host1x_readl(pdev, cfg_ccq_status6_r());
if (int_status & PVA_VALID_STATUS7)
pva->mailbox_status_regs.status[PVA_CCQ_STATUS7_INDEX] =
host1x_readl(pdev, cfg_ccq_status7_r());
/* Clear the mailbox interrupt status */
int_status = int_status & PVA_READY;
pva_write_mailbox(pdev, PVA_MBOX_ISR, int_status);
/* Wake up the waiters */
pva->mailbox_status = PVA_MBOX_STATUS_DONE;
wake_up(&pva->mailbox_waitqueue);
}
int pva_mailbox_send_cmd_sync_locked(struct pva *pva,
struct pva_cmd *cmd, u32 nregs,
struct pva_mailbox_status_regs *mailbox_status_regs)
{
int err = 0;
if (mailbox_status_regs == NULL) {
err = -EINVAL;
goto err_invalid_parameter;
}
/* Ensure that mailbox state is sane */
if (WARN_ON(pva->mailbox_status != PVA_MBOX_STATUS_INVALID)) {
err = -EIO;
goto err_check_status;
}
/* Mark that we are waiting for an interrupt */
pva->mailbox_status = PVA_MBOX_STATUS_WFI;
memset(&pva->mailbox_status_regs, 0, sizeof(pva->mailbox_status_regs));
/* Submit command to PVA */
err = pva_mailbox_send_cmd(pva, cmd, nregs);
if (err < 0)
goto err_send_command;
err = pva_mailbox_wait_event(pva, 100);
if (err < 0)
goto err_wait_response;
/* Return interrupt status back to caller */
memcpy(mailbox_status_regs, &pva->mailbox_status_regs,
sizeof(struct pva_mailbox_status_regs));
pva->mailbox_status = PVA_MBOX_STATUS_INVALID;
return err;
err_wait_response:
err_send_command:
pva->mailbox_status = PVA_MBOX_STATUS_INVALID;
err_check_status:
err_invalid_parameter:
return err;
}
int pva_mailbox_send_cmd_sync(struct pva *pva,
struct pva_cmd *cmd, u32 nregs,
struct pva_mailbox_status_regs *mailbox_status_regs)
{
int err = 0;
if (mailbox_status_regs == NULL) {
err = -EINVAL;
goto err_invalid_parameter;
}
mutex_lock(&pva->mailbox_mutex);
err = pva_mailbox_send_cmd_sync_locked(pva,
cmd,
nregs,
mailbox_status_regs);
mutex_unlock(&pva->mailbox_mutex);
return err;
err_invalid_parameter:
return err;
}
EXPORT_SYMBOL(pva_mailbox_send_cmd_sync);