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 *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 =

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_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;

View File

@ -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,6 +402,146 @@ typedef union {
} 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
*
@ -366,70 +553,13 @@ typedef struct {
* WORD 0
* HW WRITE: the following 64 bits are filled by HW when a packet arrives
*/
#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
union cvmx_wqe_word0 word0;
/*****************************************************************
* WORD 1
* HW WRITE: the following 64 bits are filled by HW when a packet arrives
*/
#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
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__ */

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)
{
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);
}
/*

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
* 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);