diff options
Diffstat (limited to 'drivers/staging/octeon/ethernet-rx.c')
-rw-r--r-- | drivers/staging/octeon/ethernet-rx.c | 538 |
1 files changed, 0 insertions, 538 deletions
diff --git a/drivers/staging/octeon/ethernet-rx.c b/drivers/staging/octeon/ethernet-rx.c deleted file mode 100644 index 2c16230f993c..000000000000 --- a/drivers/staging/octeon/ethernet-rx.c +++ /dev/null @@ -1,538 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * This file is based on code from OCTEON SDK by Cavium Networks. - * - * Copyright (c) 2003-2010 Cavium Networks - */ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/cache.h> -#include <linux/cpumask.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/ip.h> -#include <linux/string.h> -#include <linux/prefetch.h> -#include <linux/ratelimit.h> -#include <linux/smp.h> -#include <linux/interrupt.h> -#include <net/dst.h> -#ifdef CONFIG_XFRM -#include <linux/xfrm.h> -#include <net/xfrm.h> -#endif /* CONFIG_XFRM */ - -#include "octeon-ethernet.h" -#include "ethernet-defines.h" -#include "ethernet-mem.h" -#include "ethernet-rx.h" -#include "ethernet-util.h" - -static atomic_t oct_rx_ready = ATOMIC_INIT(0); - -static struct oct_rx_group { - int irq; - int group; - struct napi_struct napi; -} oct_rx_group[16]; - -/** - * cvm_oct_do_interrupt - interrupt handler. - * @irq: Interrupt number. - * @napi_id: Cookie to identify the NAPI instance. - * - * The interrupt occurs whenever the POW has packets in our group. - * - */ -static irqreturn_t cvm_oct_do_interrupt(int irq, void *napi_id) -{ - /* Disable the IRQ and start napi_poll. */ - disable_irq_nosync(irq); - napi_schedule(napi_id); - - return IRQ_HANDLED; -} - -/** - * cvm_oct_check_rcv_error - process receive errors - * @work: Work queue entry pointing to the packet. - * - * Returns Non-zero if the packet can be dropped, zero otherwise. - */ -static inline int cvm_oct_check_rcv_error(struct cvmx_wqe *work) -{ - 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 (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 - * running 10Mbps with GMXX_RXX_FRM_CTL[PRE_CHK] - * off. If this is the case we need to parse the - * packet to determine if we can remove a non spec - * preamble and generate a correct packet. - */ - 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) { - u8 *ptr = - cvmx_phys_to_ptr(work->packet_ptr.s.addr); - int i = 0; - - while (i < work->word1.len - 1) { - if (*ptr != 0x55) - break; - ptr++; - i++; - } - - if (*ptr == 0xd5) { - /* Port received 0xd5 preamble */ - work->packet_ptr.s.addr += i + 1; - work->word1.len -= i + 5; - } else if ((*ptr & 0xf) == 0xd) { - /* Port received 0xd preamble */ - work->packet_ptr.s.addr += i; - work->word1.len -= i + 4; - for (i = 0; i < work->word1.len; i++) { - *ptr = - ((*ptr & 0xf0) >> 4) | - ((*(ptr + 1) & 0xf) << 4); - ptr++; - } - } else { - printk_ratelimited("Port %d unknown preamble, packet dropped\n", - port); - cvm_oct_free_work(work); - return 1; - } - } - } else { - printk_ratelimited("Port %d receive error code %d, packet dropped\n", - port, work->word2.snoip.err_code); - cvm_oct_free_work(work); - return 1; - } - - return 0; -} - -static void copy_segments_to_skb(struct cvmx_wqe *work, struct sk_buff *skb) -{ - int segments = work->word2.s.bufs; - union cvmx_buf_ptr segment_ptr = work->packet_ptr; - int len = work->word1.len; - int segment_size; - - while (segments--) { - union cvmx_buf_ptr next_ptr; - - next_ptr = *(union cvmx_buf_ptr *) - cvmx_phys_to_ptr(segment_ptr.s.addr - 8); - - /* - * Octeon Errata PKI-100: The segment size is wrong. - * - * Until it is fixed, calculate the segment size based on - * the packet pool buffer size. - * When it is fixed, the following line should be replaced - * with this one: - * int segment_size = segment_ptr.s.size; - */ - segment_size = - CVMX_FPA_PACKET_POOL_SIZE - - (segment_ptr.s.addr - - (((segment_ptr.s.addr >> 7) - - segment_ptr.s.back) << 7)); - - /* Don't copy more than what is left in the packet */ - if (segment_size > len) - segment_size = len; - - /* Copy the data into the packet */ - skb_put_data(skb, cvmx_phys_to_ptr(segment_ptr.s.addr), - segment_size); - len -= segment_size; - segment_ptr = next_ptr; - } -} - -static int cvm_oct_poll(struct oct_rx_group *rx_group, int budget) -{ - const int coreid = cvmx_get_core_num(); - u64 old_group_mask; - u64 old_scratch; - int rx_count = 0; - int did_work_request = 0; - int packet_not_copied; - - /* Prefetch cvm_oct_device since we know we need it soon */ - prefetch(cvm_oct_device); - - if (USE_ASYNC_IOBDMA) { - /* Save scratch in case userspace is using it */ - CVMX_SYNCIOBDMA; - old_scratch = cvmx_scratch_read64(CVMX_SCR_SCRATCH); - } - - /* Only allow work for our group (and preserve priorities) */ - 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), - BIT(rx_group->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) | - BIT(rx_group->group)); - } - - if (USE_ASYNC_IOBDMA) { - cvmx_pow_work_request_async(CVMX_SCR_SCRATCH, CVMX_POW_NO_WAIT); - did_work_request = 1; - } - - while (rx_count < budget) { - struct sk_buff *skb = NULL; - struct sk_buff **pskb = NULL; - int skb_in_hw; - struct cvmx_wqe *work; - int port; - - if (USE_ASYNC_IOBDMA && did_work_request) - work = cvmx_pow_work_response_async(CVMX_SCR_SCRATCH); - else - work = cvmx_pow_work_request_sync(CVMX_POW_NO_WAIT); - - prefetch(work); - did_work_request = 0; - if (!work) { - if (OCTEON_IS_MODEL(OCTEON_CN68XX)) { - cvmx_write_csr(CVMX_SSO_WQ_IQ_DIS, - BIT(rx_group->group)); - cvmx_write_csr(CVMX_SSO_WQ_INT, - BIT(rx_group->group)); - } else { - union cvmx_pow_wq_int wq_int; - - wq_int.u64 = 0; - wq_int.s.iq_dis = BIT(rx_group->group); - wq_int.s.wq_int = BIT(rx_group->group); - cvmx_write_csr(CVMX_POW_WQ_INT, wq_int.u64); - } - break; - } - pskb = (struct sk_buff **) - (cvm_oct_get_buffer_ptr(work->packet_ptr) - - sizeof(void *)); - prefetch(pskb); - - if (USE_ASYNC_IOBDMA && rx_count < (budget - 1)) { - cvmx_pow_work_request_async_nocheck(CVMX_SCR_SCRATCH, - CVMX_POW_NO_WAIT); - did_work_request = 1; - } - rx_count++; - - skb_in_hw = work->word2.s.bufs == 1; - if (likely(skb_in_hw)) { - skb = *pskb; - prefetch(&skb->head); - prefetch(&skb->len); - } - - 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)) { - if (cvm_oct_check_rcv_error(work)) - continue; - } - - /* - * We can only use the zero copy path if skbuffs are - * in the FPA pool and the packet fits in a single - * buffer. - */ - if (likely(skb_in_hw)) { - skb->data = skb->head + work->packet_ptr.s.addr - - cvmx_ptr_to_phys(skb->head); - prefetch(skb->data); - skb->len = work->word1.len; - skb_set_tail_pointer(skb, skb->len); - packet_not_copied = 1; - } else { - /* - * We have to copy the packet. First allocate - * an skbuff for it. - */ - skb = dev_alloc_skb(work->word1.len); - if (!skb) { - cvm_oct_free_work(work); - continue; - } - - /* - * Check if we've received a packet that was - * entirely stored in the work entry. - */ - if (unlikely(work->word2.s.bufs == 0)) { - u8 *ptr = work->packet_data; - - if (likely(!work->word2.s.not_IP)) { - /* - * The beginning of the packet - * moves for IP packets. - */ - if (work->word2.s.is_v6) - ptr += 2; - else - ptr += 6; - } - skb_put_data(skb, ptr, work->word1.len); - /* No packet buffers to free */ - } else { - copy_segments_to_skb(work, skb); - } - packet_not_copied = 0; - } - if (likely((port < TOTAL_NUMBER_OF_PORTS) && - cvm_oct_device[port])) { - struct net_device *dev = cvm_oct_device[port]; - - /* - * Only accept packets for devices that are - * currently up. - */ - if (likely(dev->flags & IFF_UP)) { - skb->protocol = eth_type_trans(skb, dev); - skb->dev = dev; - - if (unlikely(work->word2.s.not_IP || - work->word2.s.IP_exc || - work->word2.s.L4_error || - !work->word2.s.tcp_or_udp)) - skb->ip_summed = CHECKSUM_NONE; - else - skb->ip_summed = CHECKSUM_UNNECESSARY; - - /* Increment RX stats for virtual ports */ - if (port >= CVMX_PIP_NUM_INPUT_PORTS) { - dev->stats.rx_packets++; - dev->stats.rx_bytes += skb->len; - } - netif_receive_skb(skb); - } else { - /* - * Drop any packet received for a device that - * isn't up. - */ - dev->stats.rx_dropped++; - dev_kfree_skb_irq(skb); - } - } else { - /* - * Drop any packet received for a device that - * doesn't exist. - */ - printk_ratelimited("Port %d not controlled by Linux, packet dropped\n", - port); - dev_kfree_skb_irq(skb); - } - /* - * Check to see if the skbuff and work share the same - * packet buffer. - */ - if (likely(packet_not_copied)) { - /* - * This buffer needs to be replaced, increment - * the number of buffers we need to free by - * one. - */ - cvmx_fau_atomic_add32(FAU_NUM_PACKET_BUFFERS_TO_FREE, - 1); - - cvmx_fpa_free(work, CVMX_FPA_WQE_POOL, 1); - } else { - cvm_oct_free_work(work); - } - } - /* Restore the original POW 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); - } - cvm_oct_rx_refill_pool(0); - - return rx_count; -} - -/** - * cvm_oct_napi_poll - the NAPI poll function. - * @napi: The NAPI instance. - * @budget: Maximum number of packets to receive. - * - * Returns the number of packets processed. - */ -static int cvm_oct_napi_poll(struct napi_struct *napi, int budget) -{ - struct oct_rx_group *rx_group = container_of(napi, struct oct_rx_group, - napi); - int rx_count; - - rx_count = cvm_oct_poll(rx_group, budget); - - if (rx_count < budget) { - /* No more work */ - napi_complete_done(napi, rx_count); - enable_irq(rx_group->irq); - } - return rx_count; -} - -#ifdef CONFIG_NET_POLL_CONTROLLER -/** - * cvm_oct_poll_controller - poll for receive packets - * device. - * - * @dev: Device to poll. Unused - */ -void cvm_oct_poll_controller(struct net_device *dev) -{ - int i; - - if (!atomic_read(&oct_rx_ready)) - return; - - for (i = 0; i < ARRAY_SIZE(oct_rx_group); i++) { - if (!(pow_receive_groups & BIT(i))) - continue; - - cvm_oct_poll(&oct_rx_group[i], 16); - } -} -#endif - -void cvm_oct_rx_initialize(void) -{ - int i; - struct net_device *dev_for_napi = NULL; - - for (i = 0; i < TOTAL_NUMBER_OF_PORTS; i++) { - if (cvm_oct_device[i]) { - dev_for_napi = cvm_oct_device[i]; - break; - } - } - - if (!dev_for_napi) - panic("No net_devices were allocated."); - - for (i = 0; i < ARRAY_SIZE(oct_rx_group); i++) { - int ret; - - if (!(pow_receive_groups & BIT(i))) - continue; - - netif_napi_add(dev_for_napi, &oct_rx_group[i].napi, - cvm_oct_napi_poll, rx_napi_weight); - napi_enable(&oct_rx_group[i].napi); - - oct_rx_group[i].irq = OCTEON_IRQ_WORKQ0 + i; - oct_rx_group[i].group = i; - - /* Register an IRQ handler to receive POW interrupts */ - ret = request_irq(oct_rx_group[i].irq, cvm_oct_do_interrupt, 0, - "Ethernet", &oct_rx_group[i].napi); - if (ret) - panic("Could not acquire Ethernet IRQ %d\n", - oct_rx_group[i].irq); - - disable_irq_nosync(oct_rx_group[i].irq); - - /* Enable POW interrupt when our port has at least one packet */ - 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(i), 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(i), 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(&oct_rx_group[i].napi); - } - atomic_inc(&oct_rx_ready); -} - -void cvm_oct_rx_shutdown(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(oct_rx_group); i++) { - if (!(pow_receive_groups & BIT(i))) - continue; - - /* Disable POW interrupt */ - if (OCTEON_IS_MODEL(OCTEON_CN68XX)) - cvmx_write_csr(CVMX_SSO_WQ_INT_THRX(i), 0); - else - cvmx_write_csr(CVMX_POW_WQ_INT_THRX(i), 0); - - /* Free the interrupt handler */ - free_irq(oct_rx_group[i].irq, cvm_oct_device); - - netif_napi_del(&oct_rx_group[i].napi); - } -} |