These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / staging / octeon / ethernet-rx.c
index 22667db..6aed3cf 100644 (file)
@@ -1,29 +1,13 @@
-/**********************************************************************
- * Author: Cavium Networks
- *
- * Contact: support@caviumnetworks.com
- * This file is part of the OCTEON SDK
+/*
+ * This file is based on code from OCTEON SDK by Cavium Networks.
  *
  * Copyright (c) 2003-2010 Cavium Networks
  *
  * This file is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License, Version 2, as
  * published by the Free Software Foundation.
- *
- * This file is distributed in the hope that it will be useful, but
- * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
- * NONINFRINGEMENT.  See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this file; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- * or visit http://www.gnu.org/licenses/.
- *
- * This file may also be available under a different license from Cavium.
- * Contact Cavium Networks for more information
-**********************************************************************/
+ */
+
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/cache.h>
@@ -65,6 +49,8 @@ static struct napi_struct cvm_oct_napi;
 
 /**
  * cvm_oct_do_interrupt - interrupt handler.
+ * @cpl: Interrupt number. Unused
+ * @dev_id: Cookie to identify the device. Unused
  *
  * The interrupt occurs whenever the POW has packets in our group.
  *
@@ -86,18 +72,22 @@ static irqreturn_t cvm_oct_do_interrupt(int cpl, void *dev_id)
  */
 static inline int cvm_oct_check_rcv_error(cvmx_wqe_t *work)
 {
-       if ((work->word2.snoip.err_code == 10) && (work->len <= 64)) {
+       int port;
+
+       if (octeon_has_feature(OCTEON_FEATURE_PKND))
+               port = work->word0.pip.cn68xx.pknd;
+       else
+               port = work->word1.cn38xx.ipprt;
+
+       if ((work->word2.snoip.err_code == 10) && (work->word1.len <= 64)) {
                /*
                 * Ignore length errors on min size packets. Some
                 * equipment incorrectly pads packets to 64+4FCS
                 * instead of 60+4FCS.  Note these packets still get
                 * counted as frame errors.
                 */
-       } else
-           if (USE_10MBPS_PREAMBLE_WORKAROUND
-               && ((work->word2.snoip.err_code == 5)
-                   || (work->word2.snoip.err_code == 7))) {
-
+       } else if (work->word2.snoip.err_code == 5 ||
+                  work->word2.snoip.err_code == 7) {
                /*
                 * We received a packet with either an alignment error
                 * or a FCS error. This may be signalling that we are
@@ -106,19 +96,19 @@ static inline int cvm_oct_check_rcv_error(cvmx_wqe_t *work)
                 * packet to determine if we can remove a non spec
                 * preamble and generate a correct packet.
                 */
-               int interface = cvmx_helper_get_interface_num(work->ipprt);
-               int index = cvmx_helper_get_interface_index_num(work->ipprt);
+               int interface = cvmx_helper_get_interface_num(port);
+               int index = cvmx_helper_get_interface_index_num(port);
                union cvmx_gmxx_rxx_frm_ctl gmxx_rxx_frm_ctl;
 
                gmxx_rxx_frm_ctl.u64 =
                    cvmx_read_csr(CVMX_GMXX_RXX_FRM_CTL(index, interface));
                if (gmxx_rxx_frm_ctl.s.pre_chk == 0) {
 
-                       uint8_t *ptr =
+                       u8 *ptr =
                            cvmx_phys_to_ptr(work->packet_ptr.s.addr);
                        int i = 0;
 
-                       while (i < work->len - 1) {
+                       while (i < work->word1.len - 1) {
                                if (*ptr != 0x55)
                                        break;
                                ptr++;
@@ -128,18 +118,18 @@ static inline int cvm_oct_check_rcv_error(cvmx_wqe_t *work)
                        if (*ptr == 0xd5) {
                                /*
                                  printk_ratelimited("Port %d received 0xd5 preamble\n",
-                                         work->ipprt);
+                                         port);
                                 */
                                work->packet_ptr.s.addr += i + 1;
-                               work->len -= i + 5;
+                               work->word1.len -= i + 5;
                        } else if ((*ptr & 0xf) == 0xd) {
                                /*
                                  printk_ratelimited("Port %d received 0x?d preamble\n",
-                                         work->ipprt);
+                                         port);
                                 */
                                work->packet_ptr.s.addr += i;
-                               work->len -= i + 4;
-                               for (i = 0; i < work->len; i++) {
+                               work->word1.len -= i + 4;
+                               for (i = 0; i < work->word1.len; i++) {
                                        *ptr =
                                            ((*ptr & 0xf0) >> 4) |
                                            ((*(ptr + 1) & 0xf) << 4);
@@ -147,7 +137,7 @@ static inline int cvm_oct_check_rcv_error(cvmx_wqe_t *work)
                                }
                        } else {
                                printk_ratelimited("Port %d unknown preamble, packet dropped\n",
-                                                  work->ipprt);
+                                                  port);
                                /*
                                   cvmx_helper_dump_packet(work);
                                 */
@@ -157,7 +147,7 @@ static inline int cvm_oct_check_rcv_error(cvmx_wqe_t *work)
                }
        } else {
                printk_ratelimited("Port %d receive error code %d, packet dropped\n",
-                                  work->ipprt, work->word2.snoip.err_code);
+                                  port, work->word2.snoip.err_code);
                cvm_oct_free_work(work);
                return 1;
        }
@@ -175,8 +165,8 @@ static inline int cvm_oct_check_rcv_error(cvmx_wqe_t *work)
 static int cvm_oct_napi_poll(struct napi_struct *napi, int budget)
 {
        const int       coreid = cvmx_get_core_num();
-       uint64_t        old_group_mask;
-       uint64_t        old_scratch;
+       u64     old_group_mask;
+       u64     old_scratch;
        int             rx_count = 0;
        int             did_work_request = 0;
        int             packet_not_copied;
@@ -191,9 +181,16 @@ static int cvm_oct_napi_poll(struct napi_struct *napi, int budget)
        }
 
        /* Only allow work for our group (and preserve priorities) */
-       old_group_mask = cvmx_read_csr(CVMX_POW_PP_GRP_MSKX(coreid));
-       cvmx_write_csr(CVMX_POW_PP_GRP_MSKX(coreid),
-                      (old_group_mask & ~0xFFFFull) | 1 << pow_receive_group);
+       if (OCTEON_IS_MODEL(OCTEON_CN68XX)) {
+               old_group_mask = cvmx_read_csr(CVMX_SSO_PPX_GRP_MSK(coreid));
+               cvmx_write_csr(CVMX_SSO_PPX_GRP_MSK(coreid),
+                               1ull << pow_receive_group);
+               cvmx_read_csr(CVMX_SSO_PPX_GRP_MSK(coreid)); /* Flush */
+       } else {
+               old_group_mask = cvmx_read_csr(CVMX_POW_PP_GRP_MSKX(coreid));
+               cvmx_write_csr(CVMX_POW_PP_GRP_MSKX(coreid),
+                       (old_group_mask & ~0xFFFFull) | 1 << pow_receive_group);
+       }
 
        if (USE_ASYNC_IOBDMA) {
                cvmx_pow_work_request_async(CVMX_SCR_SCRATCH, CVMX_POW_NO_WAIT);
@@ -205,6 +202,7 @@ static int cvm_oct_napi_poll(struct napi_struct *napi, int budget)
                struct sk_buff **pskb = NULL;
                int skb_in_hw;
                cvmx_wqe_t *work;
+               int port;
 
                if (USE_ASYNC_IOBDMA && did_work_request)
                        work = cvmx_pow_work_response_async(CVMX_SCR_SCRATCH);
@@ -214,12 +212,19 @@ static int cvm_oct_napi_poll(struct napi_struct *napi, int budget)
                prefetch(work);
                did_work_request = 0;
                if (work == NULL) {
-                       union cvmx_pow_wq_int wq_int;
+                       if (OCTEON_IS_MODEL(OCTEON_CN68XX)) {
+                               cvmx_write_csr(CVMX_SSO_WQ_IQ_DIS,
+                                              1ull << pow_receive_group);
+                               cvmx_write_csr(CVMX_SSO_WQ_INT,
+                                              1ull << pow_receive_group);
+                       } else {
+                               union cvmx_pow_wq_int wq_int;
 
-                       wq_int.u64 = 0;
-                       wq_int.s.iq_dis = 1 << pow_receive_group;
-                       wq_int.s.wq_int = 1 << pow_receive_group;
-                       cvmx_write_csr(CVMX_POW_WQ_INT, wq_int.u64);
+                               wq_int.u64 = 0;
+                               wq_int.s.iq_dis = 1 << pow_receive_group;
+                               wq_int.s.wq_int = 1 << pow_receive_group;
+                               cvmx_write_csr(CVMX_POW_WQ_INT, wq_int.u64);
+                       }
                        break;
                }
                pskb = (struct sk_buff **)(cvm_oct_get_buffer_ptr(work->packet_ptr) -
@@ -233,13 +238,19 @@ static int cvm_oct_napi_poll(struct napi_struct *napi, int budget)
                }
                rx_count++;
 
-               skb_in_hw = USE_SKBUFFS_IN_HW && work->word2.s.bufs == 1;
+               skb_in_hw = work->word2.s.bufs == 1;
                if (likely(skb_in_hw)) {
                        skb = *pskb;
                        prefetch(&skb->head);
                        prefetch(&skb->len);
                }
-               prefetch(cvm_oct_device[work->ipprt]);
+
+               if (octeon_has_feature(OCTEON_FEATURE_PKND))
+                       port = work->word0.pip.cn68xx.pknd;
+               else
+                       port = work->word1.cn38xx.ipprt;
+
+               prefetch(cvm_oct_device[port]);
 
                /* Immediately throw away all packets with receive errors */
                if (unlikely(work->word2.snoip.rcv_error)) {
@@ -256,7 +267,7 @@ static int cvm_oct_napi_poll(struct napi_struct *napi, int budget)
                        skb->data = skb->head + work->packet_ptr.s.addr -
                                cvmx_ptr_to_phys(skb->head);
                        prefetch(skb->data);
-                       skb->len = work->len;
+                       skb->len = work->word1.len;
                        skb_set_tail_pointer(skb, skb->len);
                        packet_not_copied = 1;
                } else {
@@ -264,7 +275,7 @@ static int cvm_oct_napi_poll(struct napi_struct *napi, int budget)
                         * We have to copy the packet. First allocate
                         * an skbuff for it.
                         */
-                       skb = dev_alloc_skb(work->len);
+                       skb = dev_alloc_skb(work->word1.len);
                        if (!skb) {
                                cvm_oct_free_work(work);
                                continue;
@@ -275,7 +286,7 @@ static int cvm_oct_napi_poll(struct napi_struct *napi, int budget)
                         * entirely stored in the work entry.
                         */
                        if (unlikely(work->word2.s.bufs == 0)) {
-                               uint8_t *ptr = work->packet_data;
+                               u8 *ptr = work->packet_data;
 
                                if (likely(!work->word2.s.not_IP)) {
                                        /*
@@ -287,13 +298,14 @@ static int cvm_oct_napi_poll(struct napi_struct *napi, int budget)
                                        else
                                                ptr += 6;
                                }
-                               memcpy(skb_put(skb, work->len), ptr, work->len);
+                               memcpy(skb_put(skb, work->word1.len), ptr,
+                                      work->word1.len);
                                /* No packet buffers to free */
                        } else {
                                int segments = work->word2.s.bufs;
                                union cvmx_buf_ptr segment_ptr =
                                    work->packet_ptr;
-                               int len = work->len;
+                               int len = work->word1.len;
 
                                while (segments--) {
                                        union cvmx_buf_ptr next_ptr =
@@ -329,10 +341,9 @@ static int cvm_oct_napi_poll(struct napi_struct *napi, int budget)
                        }
                        packet_not_copied = 0;
                }
-
-               if (likely((work->ipprt < TOTAL_NUMBER_OF_PORTS) &&
-                          cvm_oct_device[work->ipprt])) {
-                       struct net_device *dev = cvm_oct_device[work->ipprt];
+               if (likely((port < TOTAL_NUMBER_OF_PORTS) &&
+                          cvm_oct_device[port])) {
+                       struct net_device *dev = cvm_oct_device[port];
                        struct octeon_ethernet *priv = netdev_priv(dev);
 
                        /*
@@ -352,7 +363,7 @@ static int cvm_oct_napi_poll(struct napi_struct *napi, int budget)
                                        skb->ip_summed = CHECKSUM_UNNECESSARY;
 
                                /* Increment RX stats for virtual ports */
-                               if (work->ipprt >= CVMX_PIP_NUM_INPUT_PORTS) {
+                               if (port >= CVMX_PIP_NUM_INPUT_PORTS) {
 #ifdef CONFIG_64BIT
                                        atomic64_add(1,
                                                     (atomic64_t *)&priv->stats.rx_packets);
@@ -387,14 +398,14 @@ static int cvm_oct_napi_poll(struct napi_struct *napi, int budget)
                         * doesn't exist.
                         */
                        printk_ratelimited("Port %d not controlled by Linux, packet dropped\n",
-                                  work->ipprt);
+                                  port);
                        dev_kfree_skb_irq(skb);
                }
                /*
                 * Check to see if the skbuff and work share the same
                 * packet buffer.
                 */
-               if (USE_SKBUFFS_IN_HW && likely(packet_not_copied)) {
+               if (likely(packet_not_copied)) {
                        /*
                         * This buffer needs to be replaced, increment
                         * the number of buffers we need to free by
@@ -403,14 +414,19 @@ static int cvm_oct_napi_poll(struct napi_struct *napi, int budget)
                        cvmx_fau_atomic_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE,
                                              1);
 
-                       cvmx_fpa_free(work, CVMX_FPA_WQE_POOL,
-                                     DONT_WRITEBACK(1));
+                       cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, 1);
                } else {
                        cvm_oct_free_work(work);
                }
        }
        /* Restore the original POW group mask */
-       cvmx_write_csr(CVMX_POW_PP_GRP_MSKX(coreid), old_group_mask);
+       if (OCTEON_IS_MODEL(OCTEON_CN68XX)) {
+               cvmx_write_csr(CVMX_SSO_PPX_GRP_MSK(coreid), old_group_mask);
+               cvmx_read_csr(CVMX_SSO_PPX_GRP_MSK(coreid)); /* Flush */
+       } else {
+               cvmx_write_csr(CVMX_POW_PP_GRP_MSKX(coreid), old_group_mask);
+       }
+
        if (USE_ASYNC_IOBDMA) {
                /* Restore the scratch area */
                cvmx_scratch_write64(CVMX_SCR_SCRATCH, old_scratch);
@@ -442,8 +458,6 @@ void cvm_oct_rx_initialize(void)
 {
        int i;
        struct net_device *dev_for_napi = NULL;
-       union cvmx_pow_wq_int_thrx int_thr;
-       union cvmx_pow_wq_int_pc int_pc;
 
        for (i = 0; i < TOTAL_NUMBER_OF_PORTS; i++) {
                if (cvm_oct_device[i]) {
@@ -469,15 +483,34 @@ void cvm_oct_rx_initialize(void)
 
        disable_irq_nosync(OCTEON_IRQ_WORKQ0 + pow_receive_group);
 
-       int_thr.u64 = 0;
-       int_thr.s.tc_en = 1;
-       int_thr.s.tc_thr = 1;
        /* Enable POW interrupt when our port has at least one packet */
-       cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group), int_thr.u64);
-
-       int_pc.u64 = 0;
-       int_pc.s.pc_thr = 5;
-       cvmx_write_csr(CVMX_POW_WQ_INT_PC, int_pc.u64);
+       if (OCTEON_IS_MODEL(OCTEON_CN68XX)) {
+               union cvmx_sso_wq_int_thrx int_thr;
+               union cvmx_pow_wq_int_pc int_pc;
+
+               int_thr.u64 = 0;
+               int_thr.s.tc_en = 1;
+               int_thr.s.tc_thr = 1;
+               cvmx_write_csr(CVMX_SSO_WQ_INT_THRX(pow_receive_group),
+                              int_thr.u64);
+
+               int_pc.u64 = 0;
+               int_pc.s.pc_thr = 5;
+               cvmx_write_csr(CVMX_SSO_WQ_INT_PC, int_pc.u64);
+       } else {
+               union cvmx_pow_wq_int_thrx int_thr;
+               union cvmx_pow_wq_int_pc int_pc;
+
+               int_thr.u64 = 0;
+               int_thr.s.tc_en = 1;
+               int_thr.s.tc_thr = 1;
+               cvmx_write_csr(CVMX_POW_WQ_INT_THRX(pow_receive_group),
+                              int_thr.u64);
+
+               int_pc.u64 = 0;
+               int_pc.s.pc_thr = 5;
+               cvmx_write_csr(CVMX_POW_WQ_INT_PC, int_pc.u64);
+       }
 
        /* Schedule NAPI now. This will indirectly enable the interrupt. */
        napi_schedule(&cvm_oct_napi);