Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f8023da8 authored by Janne Huttunen's avatar Janne Huttunen Committed by Ralf Baechle
Browse files

STAGING: Octeon: Support CN68XX style WQE



CN68XX has a bit different WQE structure. This patch provides the new
definitions and converts the code to use the proper variant based on
the actual model.

Signed-off-by: default avatarJanne Huttunen <janne.huttunen@nokia.com>
Signed-off-by: default avatarAaro Koskinen <aaro.koskinen@nokia.com>
Acked-by: default avatarDavid Daney <david.daney@cavium.com>
Cc: David Daney <ddaney.cavm@gmail.com>
Cc: linux-mips@linux-mips.org
Cc: Janne Huttunen <janne.huttunen@nokia.com>
Cc: Aaro Koskinen <aaro.koskinen@nokia.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: devel@driverdev.osuosl.org
Patchwork: https://patchwork.linux-mips.org/patch/10973/


Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>
parent d5f9bc73
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -95,9 +95,9 @@ int cvmx_helper_dump_packet(cvmx_wqe_t *work)
	uint8_t *data_address;
	uint8_t *end_of_data;

	cvmx_dprintf("Packet Length:   %u\n", work->len);
	cvmx_dprintf("	  Input Port:  %u\n", work->ipprt);
	cvmx_dprintf("	  QoS:	       %u\n", work->qos);
	cvmx_dprintf("Packet Length:   %u\n", work->word1.len);
	cvmx_dprintf("	  Input Port:  %u\n", cvmx_wqe_get_port(work));
	cvmx_dprintf("	  QoS:	       %u\n", cvmx_wqe_get_qos(work));
	cvmx_dprintf("	  Buffers:     %u\n", work->word2.s.bufs);

	if (work->word2.s.bufs == 0) {
@@ -127,7 +127,7 @@ int cvmx_helper_dump_packet(cvmx_wqe_t *work)
		}
	} else
		buffer_ptr = work->packet_ptr;
	remaining_bytes = work->len;
	remaining_bytes = work->word1.len;

	while (remaining_bytes) {
		start_of_buffer =
+5 −4
Original line number Diff line number Diff line
@@ -1810,10 +1810,11 @@ static inline void cvmx_pow_work_submit(cvmx_wqe_t *wqp, uint32_t tag,
	cvmx_addr_t ptr;
	cvmx_pow_tag_req_t tag_req;

	wqp->qos = qos;
	wqp->tag = tag;
	wqp->tag_type = tag_type;
	wqp->grp = grp;
	wqp->word1.tag = tag;
	wqp->word1.tag_type = tag_type;

	cvmx_wqe_set_qos(wqp, qos);
	cvmx_wqe_set_grp(wqp, grp);

	tag_req.u64 = 0;
	tag_req.s.op = CVMX_POW_TAG_OP_ADDWQ;
+249 −59
Original line number Diff line number Diff line
@@ -193,6 +193,53 @@ typedef union {
	        uint64_t bufs:8;
#endif
	} s;
	struct {
#ifdef __BIG_ENDIAN_BITFIELD
		uint64_t bufs:8;
		uint64_t ip_offset:8;
		uint64_t vlan_valid:1;
		uint64_t vlan_stacked:1;
		uint64_t unassigned:1;
		uint64_t vlan_cfi:1;
		uint64_t vlan_id:12;
		uint64_t port:12;		/* MAC/PIP port number. */
		uint64_t dec_ipcomp:1;
		uint64_t tcp_or_udp:1;
		uint64_t dec_ipsec:1;
		uint64_t is_v6:1;
		uint64_t software:1;
		uint64_t L4_error:1;
		uint64_t is_frag:1;
		uint64_t IP_exc:1;
		uint64_t is_bcast:1;
		uint64_t is_mcast:1;
		uint64_t not_IP:1;
		uint64_t rcv_error:1;
		uint64_t err_code:8;
#else
		uint64_t err_code:8;
		uint64_t rcv_error:1;
		uint64_t not_IP:1;
		uint64_t is_mcast:1;
		uint64_t is_bcast:1;
		uint64_t IP_exc:1;
		uint64_t is_frag:1;
		uint64_t L4_error:1;
		uint64_t software:1;
		uint64_t is_v6:1;
		uint64_t dec_ipsec:1;
		uint64_t tcp_or_udp:1;
		uint64_t dec_ipcomp:1;
		uint64_t port:12;
		uint64_t vlan_id:12;
		uint64_t vlan_cfi:1;
		uint64_t unassigned:1;
		uint64_t vlan_stacked:1;
		uint64_t vlan_valid:1;
		uint64_t ip_offset:8;
		uint64_t bufs:8;
#endif
	} s_cn68xx;

	/* use this to get at the 16 vlan bits */
	struct {
@@ -355,18 +402,8 @@ typedef union {

} cvmx_pip_wqe_word2;

/**
 * Work queue entry format
 *
 * must be 8-byte aligned
 */
typedef struct {

    /*****************************************************************
     * WORD 0
     *	HW WRITE: the following 64 bits are filled by HW when a packet arrives
     */

union cvmx_pip_wqe_word0 {
	struct {
#ifdef __BIG_ENDIAN_BITFIELD
		/**
		 * raw chksum result generated by the HW
@@ -379,8 +416,8 @@ typedef struct {
		/**
		 * Next pointer used by hardware for list maintenance.
		 * May be written/read by HW before the work queue
     *		 entry is scheduled to a PP
     * (Only 36 bits used in Octeon 1)
		 * entry is scheduled to a PP (Only 36 bits used in
		 * Octeon 1)
		 */
		uint64_t next_ptr:40;
#else
@@ -388,12 +425,83 @@ typedef struct {
		uint8_t unused;
		uint16_t hw_chksum;
#endif
	} cn38xx;
	struct {
#ifdef __BIG_ENDIAN_BITFIELD
		uint64_t l4ptr:8;       /* 56..63 */
		uint64_t unused0:8;     /* 48..55 */
		uint64_t l3ptr:8;       /* 40..47 */
		uint64_t l2ptr:8;       /* 32..39 */
		uint64_t unused1:18;    /* 14..31 */
		uint64_t bpid:6;        /* 8..13 */
		uint64_t unused2:2;     /* 6..7 */
		uint64_t pknd:6;        /* 0..5 */
#else
		uint64_t pknd:6;        /* 0..5 */
		uint64_t unused2:2;     /* 6..7 */
		uint64_t bpid:6;        /* 8..13 */
		uint64_t unused1:18;    /* 14..31 */
		uint64_t l2ptr:8;       /* 32..39 */
		uint64_t l3ptr:8;       /* 40..47 */
		uint64_t unused0:8;     /* 48..55 */
		uint64_t l4ptr:8;       /* 56..63 */
#endif
	} cn68xx;
};

    /*****************************************************************
     * WORD 1
     *	HW WRITE: the following 64 bits are filled by HW when a packet arrives
union cvmx_wqe_word0 {
	uint64_t u64;
	union cvmx_pip_wqe_word0 pip;
};

union cvmx_wqe_word1 {
	uint64_t u64;
	struct {
#ifdef __BIG_ENDIAN_BITFIELD
		uint64_t len:16;
		uint64_t varies:14;
		/**
		 * the type of the tag (ORDERED, ATOMIC, NULL)
		 */
		uint64_t tag_type:2;
		uint64_t tag:32;
#else
		uint64_t tag:32;
		uint64_t tag_type:2;
		uint64_t varies:14;
		uint64_t len:16;
#endif
	};
	struct {
#ifdef __BIG_ENDIAN_BITFIELD
		uint64_t len:16;
		uint64_t zero_0:1;
		/**
		 * HW sets this to what it thought the priority of
		 * the input packet was
		 */
		uint64_t qos:3;

		uint64_t zero_1:1;
		/**
		 * the group that the work queue entry will be scheduled to
		 */
		uint64_t grp:6;
		uint64_t zero_2:3;
		uint64_t tag_type:2;
		uint64_t tag:32;
#else
		uint64_t tag:32;
		uint64_t tag_type:2;
		uint64_t zero_2:3;
		uint64_t grp:6;
		uint64_t zero_1:1;
		uint64_t qos:3;
		uint64_t zero_0:1;
		uint64_t len:16;
#endif
	} cn68xx;
	struct {
#ifdef __BIG_ENDIAN_BITFIELD
		/**
		 * HW sets to the total number of bytes in the packet
@@ -405,7 +513,8 @@ typedef struct {
		uint64_t ipprt:6;

		/**
     * HW sets this to what it thought the priority of the input packet was
		 * HW sets this to what it thought the priority of
		 * the input packet was
		 */
		uint64_t qos:3;

@@ -430,6 +539,27 @@ typedef struct {
		uint64_t ipprt:6;
		uint64_t len:16;
#endif
	} cn38xx;
};

/**
 * Work queue entry format
 *
 * must be 8-byte aligned
 */
typedef struct {

    /*****************************************************************
     * WORD 0
     *	HW WRITE: the following 64 bits are filled by HW when a packet arrives
     */
	union cvmx_wqe_word0 word0;

    /*****************************************************************
     * WORD 1
     *	HW WRITE: the following 64 bits are filled by HW when a packet arrives
     */
	union cvmx_wqe_word1 word1;

    /**
     * WORD 2 HW WRITE: the following 64-bits are filled in by
@@ -465,4 +595,64 @@ typedef struct {

} CVMX_CACHE_LINE_ALIGNED cvmx_wqe_t;

static inline int cvmx_wqe_get_port(cvmx_wqe_t *work)
{
	int port;

	if (octeon_has_feature(OCTEON_FEATURE_CN68XX_WQE))
		port = work->word2.s_cn68xx.port;
	else
		port = work->word1.cn38xx.ipprt;

	return port;
}

static inline void cvmx_wqe_set_port(cvmx_wqe_t *work, int port)
{
	if (octeon_has_feature(OCTEON_FEATURE_CN68XX_WQE))
		work->word2.s_cn68xx.port = port;
	else
		work->word1.cn38xx.ipprt = port;
}

static inline int cvmx_wqe_get_grp(cvmx_wqe_t *work)
{
	int grp;

	if (octeon_has_feature(OCTEON_FEATURE_CN68XX_WQE))
		grp = work->word1.cn68xx.grp;
	else
		grp = work->word1.cn38xx.grp;

	return grp;
}

static inline void cvmx_wqe_set_grp(cvmx_wqe_t *work, int grp)
{
	if (octeon_has_feature(OCTEON_FEATURE_CN68XX_WQE))
		work->word1.cn68xx.grp = grp;
	else
		work->word1.cn38xx.grp = grp;
}

static inline int cvmx_wqe_get_qos(cvmx_wqe_t *work)
{
	int qos;

	if (octeon_has_feature(OCTEON_FEATURE_CN68XX_WQE))
		qos = work->word1.cn68xx.qos;
	else
		qos = work->word1.cn38xx.qos;

	return qos;
}

static inline void cvmx_wqe_set_qos(cvmx_wqe_t *work, int qos)
{
	if (octeon_has_feature(OCTEON_FEATURE_CN68XX_WQE))
		work->word1.cn68xx.qos = qos;
	else
		work->word1.cn38xx.qos = qos;
}

#endif /* __CVMX_WQE_H__ */
+36 −22
Original line number Diff line number Diff line
@@ -70,7 +70,14 @@ 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
@@ -87,8 +94,8 @@ 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 =
@@ -99,7 +106,7 @@ static inline int cvm_oct_check_rcv_error(cvmx_wqe_t *work)
			    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++;
@@ -109,18 +116,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);
@@ -128,7 +135,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);
				 */
@@ -138,7 +145,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;
	}
@@ -193,6 +200,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);
@@ -234,7 +242,13 @@ static int cvm_oct_napi_poll(struct napi_struct *napi, int budget)
			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)) {
@@ -251,7 +265,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 {
@@ -259,7 +273,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;
@@ -282,13 +296,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 =
@@ -324,10 +339,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);

			/*
@@ -347,7 +361,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);
@@ -382,7 +396,7 @@ 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);
		}
		/*
+10 −9
Original line number Diff line number Diff line
@@ -589,13 +589,14 @@ int cvm_oct_xmit_pow(struct sk_buff *skb, struct net_device *dev)
	 * Fill in some of the work queue fields. We may need to add
	 * more if the software at the other end needs them.
	 */
	work->hw_chksum = skb->csum;
	work->len = skb->len;
	work->ipprt = priv->port;
	work->qos = priv->port & 0x7;
	work->grp = pow_send_group;
	work->tag_type = CVMX_HELPER_INPUT_TAG_TYPE;
	work->tag = pow_send_group;	/* FIXME */
	if (!OCTEON_IS_MODEL(OCTEON_CN68XX))
		work->word0.pip.cn38xx.hw_chksum = skb->csum;
	work->word1.len = skb->len;
	cvmx_wqe_set_port(work, priv->port);
	cvmx_wqe_set_qos(work, priv->port & 0x7);
	cvmx_wqe_set_grp(work, pow_send_group);
	work->word1.tag_type = CVMX_HELPER_INPUT_TAG_TYPE;
	work->word1.tag = pow_send_group;	/* FIXME */
	/* Default to zero. Sets of zero later are commented out */
	work->word2.u64 = 0;
	work->word2.s.bufs = 1;
@@ -675,8 +676,8 @@ int cvm_oct_xmit_pow(struct sk_buff *skb, struct net_device *dev)
	}

	/* Submit the packet to the POW */
	cvmx_pow_work_submit(work, work->tag, work->tag_type, work->qos,
			     work->grp);
	cvmx_pow_work_submit(work, work->word1.tag, work->word1.tag_type,
			     cvmx_wqe_get_qos(work), cvmx_wqe_get_grp(work));
	priv->stats.tx_packets++;
	priv->stats.tx_bytes += skb->len;
	dev_consume_skb_any(skb);