Commit 332ad43f authored by sjur.brandeland@stericsson.com's avatar sjur.brandeland@stericsson.com Committed by David S. Miller

caif-hsi: Add RX flip buffer

Implement RX flip buffer in the cfhsi_rx_done function,
piggy-backed frames is also supported.
This gives a significant performance gain for CAIF over HSI.
Signed-off-by: default avatarSjur Brændeland <sjur.brandeland@stericsson.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 576f3cc7
...@@ -426,6 +426,35 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi) ...@@ -426,6 +426,35 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
return xfer_sz; return xfer_sz;
} }
static int cfhsi_rx_desc_len(struct cfhsi_desc *desc)
{
int xfer_sz = 0;
int nfrms = 0;
u16 *plen;
if ((desc->header & ~CFHSI_PIGGY_DESC) ||
(desc->offset > CFHSI_MAX_EMB_FRM_SZ)) {
pr_err("Invalid descriptor. %x %x\n", desc->header,
desc->offset);
return -EPROTO;
}
/* Calculate transfer length. */
plen = desc->cffrm_len;
while (nfrms < CFHSI_MAX_PKTS && *plen) {
xfer_sz += *plen;
plen++;
nfrms++;
}
if (xfer_sz % 4) {
pr_err("Invalid payload len: %d, ignored.\n", xfer_sz);
return -EPROTO;
}
return xfer_sz;
}
static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi) static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
{ {
int rx_sz = 0; int rx_sz = 0;
...@@ -517,8 +546,10 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi) ...@@ -517,8 +546,10 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
static void cfhsi_rx_done(struct cfhsi *cfhsi) static void cfhsi_rx_done(struct cfhsi *cfhsi)
{ {
int res; int res;
int desc_pld_len = 0; int desc_pld_len = 0, rx_len, rx_state;
struct cfhsi_desc *desc = NULL; struct cfhsi_desc *desc = NULL;
u8 *rx_ptr, *rx_buf;
struct cfhsi_desc *piggy_desc = NULL;
desc = (struct cfhsi_desc *)cfhsi->rx_buf; desc = (struct cfhsi_desc *)cfhsi->rx_buf;
...@@ -534,65 +565,71 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi) ...@@ -534,65 +565,71 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi)
spin_unlock_bh(&cfhsi->lock); spin_unlock_bh(&cfhsi->lock);
if (cfhsi->rx_state.state == CFHSI_RX_STATE_DESC) { if (cfhsi->rx_state.state == CFHSI_RX_STATE_DESC) {
desc_pld_len = cfhsi_rx_desc(desc, cfhsi); desc_pld_len = cfhsi_rx_desc_len(desc);
if (desc_pld_len == -ENOMEM)
goto restart; if (desc_pld_len < 0)
if (desc_pld_len == -EPROTO)
goto out_of_sync; goto out_of_sync;
rx_buf = cfhsi->rx_buf;
rx_len = desc_pld_len;
if (desc_pld_len > 0 && (desc->header & CFHSI_PIGGY_DESC))
rx_len += CFHSI_DESC_SZ;
if (desc_pld_len == 0)
rx_buf = cfhsi->rx_flip_buf;
} else { } else {
int pld_len; rx_buf = cfhsi->rx_flip_buf;
if (!cfhsi->rx_state.piggy_desc) { rx_len = CFHSI_DESC_SZ;
pld_len = cfhsi_rx_pld(desc, cfhsi); if (cfhsi->rx_state.pld_len > 0 &&
if (pld_len == -ENOMEM) (desc->header & CFHSI_PIGGY_DESC)) {
goto restart;
if (pld_len == -EPROTO)
goto out_of_sync;
cfhsi->rx_state.pld_len = pld_len;
} else {
pld_len = cfhsi->rx_state.pld_len;
}
if ((pld_len > 0) && (desc->header & CFHSI_PIGGY_DESC)) {
struct cfhsi_desc *piggy_desc;
piggy_desc = (struct cfhsi_desc *) piggy_desc = (struct cfhsi_desc *)
(desc->emb_frm + CFHSI_MAX_EMB_FRM_SZ + (desc->emb_frm + CFHSI_MAX_EMB_FRM_SZ +
pld_len); cfhsi->rx_state.pld_len);
cfhsi->rx_state.piggy_desc = true; cfhsi->rx_state.piggy_desc = true;
/* Extract piggy-backed descriptor. */ /* Extract payload len from piggy-backed descriptor. */
desc_pld_len = cfhsi_rx_desc(piggy_desc, cfhsi); desc_pld_len = cfhsi_rx_desc_len(piggy_desc);
if (desc_pld_len == -ENOMEM) if (desc_pld_len < 0)
goto restart; goto out_of_sync;
if (desc_pld_len > 0)
rx_len = desc_pld_len;
if (desc_pld_len > 0 &&
(piggy_desc->header & CFHSI_PIGGY_DESC))
rx_len += CFHSI_DESC_SZ;
/* /*
* Copy needed information from the piggy-backed * Copy needed information from the piggy-backed
* descriptor to the descriptor in the start. * descriptor to the descriptor in the start.
*/ */
memcpy((u8 *)desc, (u8 *)piggy_desc, memcpy(rx_buf, (u8 *)piggy_desc,
CFHSI_DESC_SHORT_SZ); CFHSI_DESC_SHORT_SZ);
/* Mark no embedded frame here */
piggy_desc->offset = 0;
if (desc_pld_len == -EPROTO) if (desc_pld_len == -EPROTO)
goto out_of_sync; goto out_of_sync;
} }
} }
memset(&cfhsi->rx_state, 0, sizeof(cfhsi->rx_state));
if (desc_pld_len) { if (desc_pld_len) {
cfhsi->rx_state.state = CFHSI_RX_STATE_PAYLOAD; rx_state = CFHSI_RX_STATE_PAYLOAD;
cfhsi->rx_ptr = cfhsi->rx_buf + CFHSI_DESC_SZ; rx_ptr = rx_buf + CFHSI_DESC_SZ;
cfhsi->rx_len = desc_pld_len;
} else { } else {
cfhsi->rx_state.state = CFHSI_RX_STATE_DESC; rx_state = CFHSI_RX_STATE_DESC;
cfhsi->rx_ptr = cfhsi->rx_buf; rx_ptr = rx_buf;
cfhsi->rx_len = CFHSI_DESC_SZ; rx_len = CFHSI_DESC_SZ;
} }
/* Initiate next read */
if (test_bit(CFHSI_AWAKE, &cfhsi->bits)) { if (test_bit(CFHSI_AWAKE, &cfhsi->bits)) {
/* Set up new transfer. */ /* Set up new transfer. */
dev_dbg(&cfhsi->ndev->dev, "%s: Start RX.\n", dev_dbg(&cfhsi->ndev->dev, "%s: Start RX.\n",
__func__); __func__);
res = cfhsi->dev->cfhsi_rx(cfhsi->rx_ptr, cfhsi->rx_len,
res = cfhsi->dev->cfhsi_rx(rx_ptr, rx_len,
cfhsi->dev); cfhsi->dev);
if (WARN_ON(res < 0)) { if (WARN_ON(res < 0)) {
dev_err(&cfhsi->ndev->dev, "%s: RX error %d.\n", dev_err(&cfhsi->ndev->dev, "%s: RX error %d.\n",
...@@ -601,16 +638,32 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi) ...@@ -601,16 +638,32 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi)
cfhsi->ndev->stats.rx_dropped++; cfhsi->ndev->stats.rx_dropped++;
} }
} }
return;
restart: if (cfhsi->rx_state.state == CFHSI_RX_STATE_DESC) {
if (++cfhsi->rx_state.retries > CFHSI_MAX_RX_RETRIES) { /* Extract payload from descriptor */
dev_err(&cfhsi->ndev->dev, "%s: No memory available " if (cfhsi_rx_desc(desc, cfhsi) < 0)
"in %d iterations.\n", goto out_of_sync;
__func__, CFHSI_MAX_RX_RETRIES); } else {
BUG(); /* Extract payload */
if (cfhsi_rx_pld(desc, cfhsi) < 0)
goto out_of_sync;
if (piggy_desc) {
/* Extract any payload in piggyback descriptor. */
if (cfhsi_rx_desc(piggy_desc, cfhsi) < 0)
goto out_of_sync;
}
} }
mod_timer(&cfhsi->rx_slowpath_timer, jiffies + 1);
/* Update state info */
memset(&cfhsi->rx_state, 0, sizeof(cfhsi->rx_state));
cfhsi->rx_state.state = rx_state;
cfhsi->rx_ptr = rx_ptr;
cfhsi->rx_len = rx_len;
cfhsi->rx_state.pld_len = desc_pld_len;
cfhsi->rx_state.piggy_desc = desc->header & CFHSI_PIGGY_DESC;
if (rx_buf != cfhsi->rx_buf)
swap(cfhsi->rx_buf, cfhsi->rx_flip_buf);
return; return;
out_of_sync: out_of_sync:
...@@ -1040,6 +1093,12 @@ int cfhsi_probe(struct platform_device *pdev) ...@@ -1040,6 +1093,12 @@ int cfhsi_probe(struct platform_device *pdev)
goto err_alloc_rx; goto err_alloc_rx;
} }
cfhsi->rx_flip_buf = kzalloc(CFHSI_BUF_SZ_RX, GFP_KERNEL);
if (!cfhsi->rx_flip_buf) {
res = -ENODEV;
goto err_alloc_rx_flip;
}
/* Pre-calculate inactivity timeout. */ /* Pre-calculate inactivity timeout. */
if (inactivity_timeout != -1) { if (inactivity_timeout != -1) {
cfhsi->inactivity_timeout = cfhsi->inactivity_timeout =
...@@ -1138,6 +1197,8 @@ int cfhsi_probe(struct platform_device *pdev) ...@@ -1138,6 +1197,8 @@ int cfhsi_probe(struct platform_device *pdev)
err_activate: err_activate:
destroy_workqueue(cfhsi->wq); destroy_workqueue(cfhsi->wq);
err_create_wq: err_create_wq:
kfree(cfhsi->rx_flip_buf);
err_alloc_rx_flip:
kfree(cfhsi->rx_buf); kfree(cfhsi->rx_buf);
err_alloc_rx: err_alloc_rx:
kfree(cfhsi->tx_buf); kfree(cfhsi->tx_buf);
......
...@@ -138,6 +138,7 @@ struct cfhsi { ...@@ -138,6 +138,7 @@ struct cfhsi {
u8 *rx_ptr; u8 *rx_ptr;
u8 *tx_buf; u8 *tx_buf;
u8 *rx_buf; u8 *rx_buf;
u8 *rx_flip_buf;
spinlock_t lock; spinlock_t lock;
int flow_off_sent; int flow_off_sent;
u32 q_low_mark; u32 q_low_mark;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment