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: Janne Huttunen <janne.huttunen@nokia.com>
Signed-off-by: Aaro Koskinen <aaro.koskinen@nokia.com>
Acked-by: David 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: Ralf Baechle <ralf@linux-mips.org>
This commit is contained in:
Janne Huttunen 2015-08-13 16:21:42 +03:00 committed by Ralf Baechle
parent d5f9bc7360
commit f8023da8ae
5 changed files with 304 additions and 98 deletions

View file

@ -95,9 +95,9 @@ int cvmx_helper_dump_packet(cvmx_wqe_t *work)
uint8_t *data_address; uint8_t *data_address;
uint8_t *end_of_data; uint8_t *end_of_data;
cvmx_dprintf("Packet Length: %u\n", work->len); cvmx_dprintf("Packet Length: %u\n", work->word1.len);
cvmx_dprintf(" Input Port: %u\n", work->ipprt); cvmx_dprintf(" Input Port: %u\n", cvmx_wqe_get_port(work));
cvmx_dprintf(" QoS: %u\n", work->qos); cvmx_dprintf(" QoS: %u\n", cvmx_wqe_get_qos(work));
cvmx_dprintf(" Buffers: %u\n", work->word2.s.bufs); cvmx_dprintf(" Buffers: %u\n", work->word2.s.bufs);
if (work->word2.s.bufs == 0) { if (work->word2.s.bufs == 0) {
@ -127,7 +127,7 @@ int cvmx_helper_dump_packet(cvmx_wqe_t *work)
} }
} else } else
buffer_ptr = work->packet_ptr; buffer_ptr = work->packet_ptr;
remaining_bytes = work->len; remaining_bytes = work->word1.len;
while (remaining_bytes) { while (remaining_bytes) {
start_of_buffer = start_of_buffer =

View file

@ -1810,10 +1810,11 @@ static inline void cvmx_pow_work_submit(cvmx_wqe_t *wqp, uint32_t tag,
cvmx_addr_t ptr; cvmx_addr_t ptr;
cvmx_pow_tag_req_t tag_req; cvmx_pow_tag_req_t tag_req;
wqp->qos = qos; wqp->word1.tag = tag;
wqp->tag = tag; wqp->word1.tag_type = tag_type;
wqp->tag_type = tag_type;
wqp->grp = grp; cvmx_wqe_set_qos(wqp, qos);
cvmx_wqe_set_grp(wqp, grp);
tag_req.u64 = 0; tag_req.u64 = 0;
tag_req.s.op = CVMX_POW_TAG_OP_ADDWQ; tag_req.s.op = CVMX_POW_TAG_OP_ADDWQ;

View file

@ -193,6 +193,53 @@ typedef union {
uint64_t bufs:8; uint64_t bufs:8;
#endif #endif
} s; } 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 */ /* use this to get at the 16 vlan bits */
struct { struct {
@ -355,6 +402,146 @@ typedef union {
} cvmx_pip_wqe_word2; } cvmx_pip_wqe_word2;
union cvmx_pip_wqe_word0 {
struct {
#ifdef __BIG_ENDIAN_BITFIELD
/**
* raw chksum result generated by the HW
*/
uint16_t hw_chksum;
/**
* Field unused by hardware - available for software
*/
uint8_t unused;
/**
* 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)
*/
uint64_t next_ptr:40;
#else
uint64_t next_ptr:40;
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;
};
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
*/
uint64_t len:16;
/**
* HW sets this to input physical port
*/
uint64_t ipprt:6;
/**
* HW sets this to what it thought the priority of
* the input packet was
*/
uint64_t qos:3;
/**
* the group that the work queue entry will be scheduled to
*/
uint64_t grp:4;
/**
* the type of the tag (ORDERED, ATOMIC, NULL)
*/
uint64_t tag_type:3;
/**
* the synchronization/ordering tag
*/
uint64_t tag:32;
#else
uint64_t tag:32;
uint64_t tag_type:2;
uint64_t zero_2:1;
uint64_t grp:4;
uint64_t qos:3;
uint64_t ipprt:6;
uint64_t len:16;
#endif
} cn38xx;
};
/** /**
* Work queue entry format * Work queue entry format
* *
@ -366,70 +553,13 @@ typedef struct {
* WORD 0 * WORD 0
* HW WRITE: the following 64 bits are filled by HW when a packet arrives * HW WRITE: the following 64 bits are filled by HW when a packet arrives
*/ */
union cvmx_wqe_word0 word0;
#ifdef __BIG_ENDIAN_BITFIELD
/**
* raw chksum result generated by the HW
*/
uint16_t hw_chksum;
/**
* Field unused by hardware - available for software
*/
uint8_t unused;
/**
* 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)
*/
uint64_t next_ptr:40;
#else
uint64_t next_ptr:40;
uint8_t unused;
uint16_t hw_chksum;
#endif
/***************************************************************** /*****************************************************************
* WORD 1 * WORD 1
* HW WRITE: the following 64 bits are filled by HW when a packet arrives * HW WRITE: the following 64 bits are filled by HW when a packet arrives
*/ */
union cvmx_wqe_word1 word1;
#ifdef __BIG_ENDIAN_BITFIELD
/**
* HW sets to the total number of bytes in the packet
*/
uint64_t len:16;
/**
* HW sets this to input physical port
*/
uint64_t ipprt:6;
/**
* HW sets this to what it thought the priority of the input packet was
*/
uint64_t qos:3;
/**
* the group that the work queue entry will be scheduled to
*/
uint64_t grp:4;
/**
* the type of the tag (ORDERED, ATOMIC, NULL)
*/
uint64_t tag_type:3;
/**
* the synchronization/ordering tag
*/
uint64_t tag:32;
#else
uint64_t tag:32;
uint64_t tag_type:2;
uint64_t zero_2:1;
uint64_t grp:4;
uint64_t qos:3;
uint64_t ipprt:6;
uint64_t len:16;
#endif
/** /**
* WORD 2 HW WRITE: the following 64-bits are filled in by * 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; } 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__ */ #endif /* __CVMX_WQE_H__ */

View file

@ -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) 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 * Ignore length errors on min size packets. Some
* equipment incorrectly pads packets to 64+4FCS * 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 * packet to determine if we can remove a non spec
* preamble and generate a correct packet. * preamble and generate a correct packet.
*/ */
int interface = cvmx_helper_get_interface_num(work->ipprt); int interface = cvmx_helper_get_interface_num(port);
int index = cvmx_helper_get_interface_index_num(work->ipprt); int index = cvmx_helper_get_interface_index_num(port);
union cvmx_gmxx_rxx_frm_ctl gmxx_rxx_frm_ctl; union cvmx_gmxx_rxx_frm_ctl gmxx_rxx_frm_ctl;
gmxx_rxx_frm_ctl.u64 = 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); cvmx_phys_to_ptr(work->packet_ptr.s.addr);
int i = 0; int i = 0;
while (i < work->len - 1) { while (i < work->word1.len - 1) {
if (*ptr != 0x55) if (*ptr != 0x55)
break; break;
ptr++; ptr++;
@ -109,18 +116,18 @@ static inline int cvm_oct_check_rcv_error(cvmx_wqe_t *work)
if (*ptr == 0xd5) { if (*ptr == 0xd5) {
/* /*
printk_ratelimited("Port %d received 0xd5 preamble\n", printk_ratelimited("Port %d received 0xd5 preamble\n",
work->ipprt); port);
*/ */
work->packet_ptr.s.addr += i + 1; work->packet_ptr.s.addr += i + 1;
work->len -= i + 5; work->word1.len -= i + 5;
} else if ((*ptr & 0xf) == 0xd) { } else if ((*ptr & 0xf) == 0xd) {
/* /*
printk_ratelimited("Port %d received 0x?d preamble\n", printk_ratelimited("Port %d received 0x?d preamble\n",
work->ipprt); port);
*/ */
work->packet_ptr.s.addr += i; work->packet_ptr.s.addr += i;
work->len -= i + 4; work->word1.len -= i + 4;
for (i = 0; i < work->len; i++) { for (i = 0; i < work->word1.len; i++) {
*ptr = *ptr =
((*ptr & 0xf0) >> 4) | ((*ptr & 0xf0) >> 4) |
((*(ptr + 1) & 0xf) << 4); ((*(ptr + 1) & 0xf) << 4);
@ -128,7 +135,7 @@ static inline int cvm_oct_check_rcv_error(cvmx_wqe_t *work)
} }
} else { } else {
printk_ratelimited("Port %d unknown preamble, packet dropped\n", printk_ratelimited("Port %d unknown preamble, packet dropped\n",
work->ipprt); port);
/* /*
cvmx_helper_dump_packet(work); cvmx_helper_dump_packet(work);
*/ */
@ -138,7 +145,7 @@ static inline int cvm_oct_check_rcv_error(cvmx_wqe_t *work)
} }
} else { } else {
printk_ratelimited("Port %d receive error code %d, packet dropped\n", 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); cvm_oct_free_work(work);
return 1; return 1;
} }
@ -193,6 +200,7 @@ static int cvm_oct_napi_poll(struct napi_struct *napi, int budget)
struct sk_buff **pskb = NULL; struct sk_buff **pskb = NULL;
int skb_in_hw; int skb_in_hw;
cvmx_wqe_t *work; cvmx_wqe_t *work;
int port;
if (USE_ASYNC_IOBDMA && did_work_request) if (USE_ASYNC_IOBDMA && did_work_request)
work = cvmx_pow_work_response_async(CVMX_SCR_SCRATCH); 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->head);
prefetch(&skb->len); 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 */ /* Immediately throw away all packets with receive errors */
if (unlikely(work->word2.snoip.rcv_error)) { 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 - skb->data = skb->head + work->packet_ptr.s.addr -
cvmx_ptr_to_phys(skb->head); cvmx_ptr_to_phys(skb->head);
prefetch(skb->data); prefetch(skb->data);
skb->len = work->len; skb->len = work->word1.len;
skb_set_tail_pointer(skb, skb->len); skb_set_tail_pointer(skb, skb->len);
packet_not_copied = 1; packet_not_copied = 1;
} else { } 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 * We have to copy the packet. First allocate
* an skbuff for it. * an skbuff for it.
*/ */
skb = dev_alloc_skb(work->len); skb = dev_alloc_skb(work->word1.len);
if (!skb) { if (!skb) {
cvm_oct_free_work(work); cvm_oct_free_work(work);
continue; continue;
@ -282,13 +296,14 @@ static int cvm_oct_napi_poll(struct napi_struct *napi, int budget)
else else
ptr += 6; 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 */ /* No packet buffers to free */
} else { } else {
int segments = work->word2.s.bufs; int segments = work->word2.s.bufs;
union cvmx_buf_ptr segment_ptr = union cvmx_buf_ptr segment_ptr =
work->packet_ptr; work->packet_ptr;
int len = work->len; int len = work->word1.len;
while (segments--) { while (segments--) {
union cvmx_buf_ptr next_ptr = 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; packet_not_copied = 0;
} }
if (likely((port < TOTAL_NUMBER_OF_PORTS) &&
if (likely((work->ipprt < TOTAL_NUMBER_OF_PORTS) && cvm_oct_device[port])) {
cvm_oct_device[work->ipprt])) { struct net_device *dev = cvm_oct_device[port];
struct net_device *dev = cvm_oct_device[work->ipprt];
struct octeon_ethernet *priv = netdev_priv(dev); 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; skb->ip_summed = CHECKSUM_UNNECESSARY;
/* Increment RX stats for virtual ports */ /* Increment RX stats for virtual ports */
if (work->ipprt >= CVMX_PIP_NUM_INPUT_PORTS) { if (port >= CVMX_PIP_NUM_INPUT_PORTS) {
#ifdef CONFIG_64BIT #ifdef CONFIG_64BIT
atomic64_add(1, atomic64_add(1,
(atomic64_t *)&priv->stats.rx_packets); (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. * doesn't exist.
*/ */
printk_ratelimited("Port %d not controlled by Linux, packet dropped\n", printk_ratelimited("Port %d not controlled by Linux, packet dropped\n",
work->ipprt); port);
dev_kfree_skb_irq(skb); dev_kfree_skb_irq(skb);
} }
/* /*

View file

@ -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 * Fill in some of the work queue fields. We may need to add
* more if the software at the other end needs them. * more if the software at the other end needs them.
*/ */
work->hw_chksum = skb->csum; if (!OCTEON_IS_MODEL(OCTEON_CN68XX))
work->len = skb->len; work->word0.pip.cn38xx.hw_chksum = skb->csum;
work->ipprt = priv->port; work->word1.len = skb->len;
work->qos = priv->port & 0x7; cvmx_wqe_set_port(work, priv->port);
work->grp = pow_send_group; cvmx_wqe_set_qos(work, priv->port & 0x7);
work->tag_type = CVMX_HELPER_INPUT_TAG_TYPE; cvmx_wqe_set_grp(work, pow_send_group);
work->tag = pow_send_group; /* FIXME */ 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 */ /* Default to zero. Sets of zero later are commented out */
work->word2.u64 = 0; work->word2.u64 = 0;
work->word2.s.bufs = 1; 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 */ /* Submit the packet to the POW */
cvmx_pow_work_submit(work, work->tag, work->tag_type, work->qos, cvmx_pow_work_submit(work, work->word1.tag, work->word1.tag_type,
work->grp); cvmx_wqe_get_qos(work), cvmx_wqe_get_grp(work));
priv->stats.tx_packets++; priv->stats.tx_packets++;
priv->stats.tx_bytes += skb->len; priv->stats.tx_bytes += skb->len;
dev_consume_skb_any(skb); dev_consume_skb_any(skb);