Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[linux-flexiantxendom0-3.2.10.git] / drivers / net / caif / caif_hsi.c
index c998e1a..9a66e2a 100644 (file)
@@ -426,6 +426,35 @@ static int cfhsi_rx_desc(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
        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)
 {
        int rx_sz = 0;
@@ -517,8 +546,10 @@ static int cfhsi_rx_pld(struct cfhsi_desc *desc, struct cfhsi *cfhsi)
 static void cfhsi_rx_done(struct cfhsi *cfhsi)
 {
        int res;
-       int desc_pld_len = 0;
+       int desc_pld_len = 0, rx_len, rx_state;
        struct cfhsi_desc *desc = NULL;
+       u8 *rx_ptr, *rx_buf;
+       struct cfhsi_desc *piggy_desc = NULL;
 
        desc = (struct cfhsi_desc *)cfhsi->rx_buf;
 
@@ -534,65 +565,71 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi)
        spin_unlock_bh(&cfhsi->lock);
 
        if (cfhsi->rx_state.state == CFHSI_RX_STATE_DESC) {
-               desc_pld_len = cfhsi_rx_desc(desc, cfhsi);
-               if (desc_pld_len == -ENOMEM)
-                       goto restart;
-               if (desc_pld_len == -EPROTO)
+               desc_pld_len = cfhsi_rx_desc_len(desc);
+
+               if (desc_pld_len < 0)
                        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 {
-               int pld_len;
+               rx_buf = cfhsi->rx_flip_buf;
 
-               if (!cfhsi->rx_state.piggy_desc) {
-                       pld_len = cfhsi_rx_pld(desc, cfhsi);
-                       if (pld_len == -ENOMEM)
-                               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;
-               }
+               rx_len = CFHSI_DESC_SZ;
+               if (cfhsi->rx_state.pld_len > 0 &&
+                               (desc->header & CFHSI_PIGGY_DESC)) {
 
-               if ((pld_len > 0) && (desc->header & CFHSI_PIGGY_DESC)) {
-                       struct cfhsi_desc *piggy_desc;
                        piggy_desc = (struct cfhsi_desc *)
                                (desc->emb_frm + CFHSI_MAX_EMB_FRM_SZ +
-                                               pld_len);
+                                               cfhsi->rx_state.pld_len);
+
                        cfhsi->rx_state.piggy_desc = true;
 
-                       /* Extract piggy-backed descriptor. */
-                       desc_pld_len = cfhsi_rx_desc(piggy_desc, cfhsi);
-                       if (desc_pld_len == -ENOMEM)
-                               goto restart;
+                       /* Extract payload len from piggy-backed descriptor. */
+                       desc_pld_len = cfhsi_rx_desc_len(piggy_desc);
+                       if (desc_pld_len < 0)
+                               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
                         * descriptor to the descriptor in the start.
                         */
-                       memcpy((u8 *)desc, (u8 *)piggy_desc,
+                       memcpy(rx_buf, (u8 *)piggy_desc,
                                        CFHSI_DESC_SHORT_SZ);
-
+                       /* Mark no embedded frame here */
+                       piggy_desc->offset = 0;
                        if (desc_pld_len == -EPROTO)
                                goto out_of_sync;
                }
        }
 
-       memset(&cfhsi->rx_state, 0, sizeof(cfhsi->rx_state));
        if (desc_pld_len) {
-               cfhsi->rx_state.state = CFHSI_RX_STATE_PAYLOAD;
-               cfhsi->rx_ptr = cfhsi->rx_buf + CFHSI_DESC_SZ;
-               cfhsi->rx_len = desc_pld_len;
+               rx_state = CFHSI_RX_STATE_PAYLOAD;
+               rx_ptr = rx_buf + CFHSI_DESC_SZ;
        } else {
-               cfhsi->rx_state.state = CFHSI_RX_STATE_DESC;
-               cfhsi->rx_ptr = cfhsi->rx_buf;
-               cfhsi->rx_len = CFHSI_DESC_SZ;
+               rx_state = CFHSI_RX_STATE_DESC;
+               rx_ptr = rx_buf;
+               rx_len = CFHSI_DESC_SZ;
        }
 
+       /* Initiate next read */
        if (test_bit(CFHSI_AWAKE, &cfhsi->bits)) {
                /* Set up new transfer. */
                dev_dbg(&cfhsi->ndev->dev, "%s: Start RX.\n",
-                       __func__);
-               res = cfhsi->dev->cfhsi_rx(cfhsi->rx_ptr, cfhsi->rx_len,
+                               __func__);
+
+               res = cfhsi->dev->cfhsi_rx(rx_ptr, rx_len,
                                cfhsi->dev);
                if (WARN_ON(res < 0)) {
                        dev_err(&cfhsi->ndev->dev, "%s: RX error %d.\n",
@@ -601,16 +638,32 @@ static void cfhsi_rx_done(struct cfhsi *cfhsi)
                        cfhsi->ndev->stats.rx_dropped++;
                }
        }
-       return;
 
-restart:
-       if (++cfhsi->rx_state.retries > CFHSI_MAX_RX_RETRIES) {
-               dev_err(&cfhsi->ndev->dev, "%s: No memory available "
-                       "in %d iterations.\n",
-                       __func__, CFHSI_MAX_RX_RETRIES);
-               BUG();
+       if (cfhsi->rx_state.state == CFHSI_RX_STATE_DESC) {
+               /* Extract payload from descriptor */
+               if (cfhsi_rx_desc(desc, cfhsi) < 0)
+                       goto out_of_sync;
+       } else {
+               /* 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;
 
 out_of_sync:
@@ -1040,6 +1093,12 @@ int cfhsi_probe(struct platform_device *pdev)
                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. */
        if (inactivity_timeout != -1) {
                cfhsi->inactivity_timeout =
@@ -1138,6 +1197,8 @@ int cfhsi_probe(struct platform_device *pdev)
  err_activate:
        destroy_workqueue(cfhsi->wq);
  err_create_wq:
+       kfree(cfhsi->rx_flip_buf);
+ err_alloc_rx_flip:
        kfree(cfhsi->rx_buf);
  err_alloc_rx:
        kfree(cfhsi->tx_buf);