slim_ngd: Handle invalid/timeout TX errors gracefully

Invalid transactions can cause timeout or NACK TX errors.
Clear completion object reference for pending TX messages and print
TX BAM pipe descriptors and status registers for such transactions.
TX timeouts during connect/disconnect port error conditions should
clear completion object reference for acklowledgement receipt
to avoid referring to stale completion object later.

Change-Id: I81018df8cab59b43753c46c8a49a5066b794fda6
Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
This commit is contained in:
Sagar Dharia 2014-11-24 18:09:08 -07:00
parent ed198ed6c2
commit bc4c3bf0bf
3 changed files with 79 additions and 18 deletions

View file

@ -95,19 +95,22 @@ static irqreturn_t ngd_slim_interrupt(int irq, void *d)
(stat & NGD_INT_MSG_TX_INVAL) || (stat & NGD_INT_DEV_ERR) ||
(stat & NGD_INT_TX_NACKED_2)) {
writel_relaxed(stat, ngd + NGD_INT_CLR);
dev->err = -EIO;
if (stat & NGD_INT_MSG_TX_INVAL)
dev->err = -EINVAL;
else
dev->err = -EIO;
SLIM_WARN(dev, "NGD interrupt error:0x%x, err:%d\n", stat,
dev->err);
/* Guarantee that error interrupts are cleared */
mb();
msm_slim_manage_tx_msgq(dev, false, NULL);
msm_slim_manage_tx_msgq(dev, false, NULL, dev->err);
} else if (stat & NGD_INT_TX_MSG_SENT) {
writel_relaxed(NGD_INT_TX_MSG_SENT, ngd + NGD_INT_CLR);
/* Make sure interrupt is cleared */
mb();
msm_slim_manage_tx_msgq(dev, false, NULL);
msm_slim_manage_tx_msgq(dev, false, NULL, 0);
}
if (stat & NGD_INT_RX_MSG_RCVD) {
u32 rx_buf[10];
@ -475,6 +478,13 @@ static int ngd_xfer_msg(struct slim_controller *ctrl, struct slim_msg_txn *txn)
}
txn->rl--;
if (txn->len > SLIM_MSGQ_BUF_LEN || txn->rl > SLIM_MSGQ_BUF_LEN) {
SLIM_WARN(dev, "msg exeeds HW lim:%d, rl:%d, mc:0x%x, mt:0x%x",
txn->len, txn->rl, txn->mc, txn->mt);
ret = -EDQUOT;
goto ngd_xfer_err;
}
if (txn->mt == SLIM_MSG_MT_CORE && txn->comp &&
dev->use_tx_msgqs == MSM_MSGQ_ENABLED &&
(txn_mc != SLIM_MSG_MC_REQUEST_INFORMATION &&
@ -567,9 +577,26 @@ static int ngd_xfer_msg(struct slim_controller *ctrl, struct slim_msg_txn *txn)
ret = msm_send_msg_buf(dev, pbuf, txn->rl,
NGD_BASE(dev->ctrl.nr, dev->ver) + NGD_TX_MSG);
if (!ret && sync_wr) {
int i;
int timeout = wait_for_completion_timeout(&tx_sent, HZ);
if (!timeout) {
if (!timeout && dev->use_tx_msgqs == MSM_MSGQ_ENABLED) {
struct msm_slim_endp *endpoint = &dev->tx_msgq;
struct sps_mem_buffer *mem = &endpoint->buf;
u32 idx = (u32) (((u8 *)pbuf - (u8 *)mem->base) /
SLIM_MSGQ_BUF_LEN);
phys_addr_t addr = mem->phys_base +
(idx * SLIM_MSGQ_BUF_LEN);
ret = -ETIMEDOUT;
SLIM_WARN(dev, "timeout, BAM desc_idx:%d, phys:%llx",
idx, (u64)addr);
for (i = 0; i < (SLIM_MSGQ_BUF_LEN >> 2) ; i++)
SLIM_WARN(dev, "timeout:bam-desc[%d]:0x%x",
i, *(pbuf + i));
if (idx < MSM_TX_BUFS)
dev->wr_comp[idx] = NULL;
/* print BAM debug info for TX pipe */
sps_get_bam_debug_info(dev->bam.hdl, 93,
SPS_BAM_PIPE(4), 0, 2);
/*
* disconnect/recoonect pipe so that subsequent
* transactions don't timeout due to unavailable
@ -580,6 +607,12 @@ static int ngd_xfer_msg(struct slim_controller *ctrl, struct slim_msg_txn *txn)
&dev->use_tx_msgqs);
msm_slim_connect_endp(dev, &dev->tx_msgq, NULL);
}
} else if (!timeout) {
ret = -ETIMEDOUT;
SLIM_WARN(dev, "timeout non-BAM TX,len:%d", txn->rl);
for (i = 0; i < (SLIM_MSGQ_BUF_LEN >> 2) ; i++)
SLIM_WARN(dev, "timeout:txbuf[%d]:0x%x", i,
dev->tx_buf[i]);
} else {
ret = dev->err;
}
@ -599,22 +632,27 @@ static int ngd_xfer_msg(struct slim_controller *ctrl, struct slim_msg_txn *txn)
SLIM_WARN(dev, "conf:0x%x,stat:0x%x,rxmsgq:0x%x\n",
conf, stat, rx_msgq);
SLIM_WARN(dev, "int_stat:0x%x,int_en:0x%x,int_cll:0x%x\n",
SLIM_ERR(dev, "int_stat:0x%x,int_en:0x%x,int_cll:0x%x\n",
int_stat, int_en, int_clr);
} else if (txn_mt == SLIM_MSG_MT_DEST_REFERRED_USER &&
}
if (txn_mt == SLIM_MSG_MT_DEST_REFERRED_USER &&
(txn_mc == SLIM_USR_MC_CONNECT_SRC ||
txn_mc == SLIM_USR_MC_CONNECT_SINK ||
txn_mc == SLIM_USR_MC_DISCONNECT_PORT)) {
int timeout;
mutex_unlock(&dev->tx_lock);
msm_slim_put_ctrl(dev);
timeout = wait_for_completion_timeout(txn->comp, HZ);
if (!timeout)
ret = -ETIMEDOUT;
else
ret = txn->ec;
if (!ret) {
timeout = wait_for_completion_timeout(txn->comp, HZ);
/* remote side did not acknowledge */
if (!timeout)
ret = -EREMOTEIO;
else
ret = txn->ec;
}
if (ret) {
SLIM_INFO(dev,
SLIM_ERR(dev,
"connect/disconnect:0x%x,tid:%d err:%d\n",
txn->mc, txn->tid, ret);
mutex_lock(&ctrl->m_ctrl);

View file

@ -417,7 +417,7 @@ static int msm_slim_post_tx_msgq(struct msm_slim_ctrl *dev, u8 *buf, int len)
return ret;
}
void msm_slim_tx_msg_return(struct msm_slim_ctrl *dev)
void msm_slim_tx_msg_return(struct msm_slim_ctrl *dev, int err)
{
struct msm_slim_endp *endpoint = &dev->tx_msgq;
struct sps_mem_buffer *mem = &endpoint->buf;
@ -446,6 +446,25 @@ void msm_slim_tx_msg_return(struct msm_slim_ctrl *dev)
struct completion *comp = dev->wr_comp[idx];
dev->wr_comp[idx] = NULL;
complete(comp);
} else if (idx >= MSM_TX_BUFS) {
SLIM_ERR(dev, "BUF out of bounds:base:0x%llx, io:0x%x",
(u64)mem->phys_base, iovec.addr);
/* print BAM debug info for TX pipe */
sps_get_bam_debug_info(dev->bam.hdl, 93,
SPS_BAM_PIPE(4), 0, 2);
continue;
}
if (err) {
int i;
u32 *addr = (u32 *)mem->base +
(idx * (SLIM_MSGQ_BUF_LEN >> 2));
/* print the descriptor that resulted in error */
for (i = 0; i < (SLIM_MSGQ_BUF_LEN >> 2); i++)
SLIM_WARN(dev, "err desc[%d]:0x%x", i, addr[i]);
/* print BAM debug info for TX pipe for invalid TX */
if (err == -EINVAL)
sps_get_bam_debug_info(dev->bam.hdl, 93,
SPS_BAM_PIPE(4), 0, 2);
}
/* reclaim all packets that were delivered out of order */
if (idx != dev->tx_head)
@ -477,7 +496,7 @@ static u32 *msm_slim_modify_tx_buf(struct msm_slim_ctrl *dev,
return retbuf;
}
u32 *msm_slim_manage_tx_msgq(struct msm_slim_ctrl *dev, bool getbuf,
struct completion *comp)
struct completion *comp, int err)
{
int ret = 0;
int retries = 0;
@ -485,7 +504,7 @@ u32 *msm_slim_manage_tx_msgq(struct msm_slim_ctrl *dev, bool getbuf,
mutex_lock(&dev->tx_buf_lock);
if (!getbuf) {
msm_slim_tx_msg_return(dev);
msm_slim_tx_msg_return(dev, err);
mutex_unlock(&dev->tx_buf_lock);
return NULL;
}
@ -497,7 +516,7 @@ u32 *msm_slim_manage_tx_msgq(struct msm_slim_ctrl *dev, bool getbuf,
}
do {
msm_slim_tx_msg_return(dev);
msm_slim_tx_msg_return(dev, err);
retbuf = msm_slim_modify_tx_buf(dev, comp);
if (!retbuf)
ret = -EAGAIN;
@ -551,7 +570,7 @@ u32 *msm_get_msg_buf(struct msm_slim_ctrl *dev, int len,
return dev->tx_buf;
}
return msm_slim_manage_tx_msgq(dev, true, comp);
return msm_slim_manage_tx_msgq(dev, true, comp, 0);
}
static void
@ -701,8 +720,12 @@ int msm_slim_connect_endp(struct msm_slim_ctrl *dev,
}
dev->use_rx_msgqs = MSM_MSGQ_ENABLED;
} else {
mutex_lock(&dev->tx_buf_lock);
dev->tx_tail = 0;
dev->tx_head = 0;
for (i = 0; i < MSM_TX_BUFS; i++)
dev->wr_comp[i] = NULL;
mutex_unlock(&dev->tx_buf_lock);
dev->use_tx_msgqs = MSM_MSGQ_ENABLED;
}

View file

@ -379,7 +379,7 @@ int msm_send_msg_buf(struct msm_slim_ctrl *dev, u32 *buf, u8 len, u32 tx_reg);
u32 *msm_get_msg_buf(struct msm_slim_ctrl *dev, int len,
struct completion *comp);
u32 *msm_slim_manage_tx_msgq(struct msm_slim_ctrl *dev, bool getbuf,
struct completion *comp);
struct completion *comp, int err);
int msm_slim_rx_msgq_get(struct msm_slim_ctrl *dev, u32 *data, int offset);
int msm_slim_sps_init(struct msm_slim_ctrl *dev, struct resource *bam_mem,
u32 pipe_reg, bool remote);