Commit 58b87d21 authored by David S. Miller's avatar David S. Miller

Merge branch 'stmmac-fixes'

Jose Abreu says:

====================
net: stmmac: Fixes for -net

Misc fixes for stmmac.

Patch 1/11 and 2/11, use the correct variable type for bitrev32() calls.

Patch 3/11, fixes the random failures the we were seing when running selftests.

Patch 4/11, prevents a crash that can occur when receiving AVB packets and with
SPH feature enabled on XGMAC.

Patch 5/11, fixes the correct settings for CBS on XGMAC.

Patch 6/11, corrects the interpretation of AVB feature on XGMAC.

Patch 7/11, disables Flow Control for AVB enabled queues on XGMAC.

Patch 8/11, disables MMC interrupts on XGMAC, preventing a storm of interrupts.

Patch 9/11, fixes the number of packets that were being taken into account in
the RX path cleaning function.

Patch 10/11, fixes an incorrect descriptor setting that could cause IP
misbehavior.

Patch 11/11, fixes the IOC generation mechanism when multiple descriptors
are used.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 53ba60af 7df4a3a7
......@@ -432,7 +432,7 @@ static void dwmac4_set_filter(struct mac_device_info *hw,
* bits used depends on the hardware configuration
* selected at core configuration time.
*/
int bit_nr = bitrev32(~crc32_le(~0, ha->addr,
u32 bit_nr = bitrev32(~crc32_le(~0, ha->addr,
ETH_ALEN)) >> (32 - mcbitslog2);
/* The most significant bit determines the register to
* use (H/L) while the other 5 bits determine the bit
......
......@@ -224,6 +224,7 @@ static void dwxgmac2_config_cbs(struct mac_device_info *hw,
writel(low_credit, ioaddr + XGMAC_MTL_TCx_LOCREDIT(queue));
value = readl(ioaddr + XGMAC_MTL_TCx_ETS_CONTROL(queue));
value &= ~XGMAC_TSA;
value |= XGMAC_CC | XGMAC_CBS;
writel(value, ioaddr + XGMAC_MTL_TCx_ETS_CONTROL(queue));
}
......@@ -463,7 +464,7 @@ static void dwxgmac2_set_filter(struct mac_device_info *hw,
value |= XGMAC_FILTER_HMC;
netdev_for_each_mc_addr(ha, dev) {
int nr = (bitrev32(~crc32_le(~0, ha->addr, 6)) >>
u32 nr = (bitrev32(~crc32_le(~0, ha->addr, 6)) >>
(32 - mcbitslog2));
mc_filter[nr >> 5] |= (1 << (nr & 0x1F));
}
......
......@@ -288,6 +288,7 @@ static int dwxgmac2_get_rx_hash(struct dma_desc *p, u32 *hash,
static int dwxgmac2_get_rx_header_len(struct dma_desc *p, unsigned int *len)
{
if (le32_to_cpu(p->des3) & XGMAC_RDES3_L34T)
*len = le32_to_cpu(p->des2) & XGMAC_RDES2_HL;
return 0;
}
......
......@@ -369,7 +369,7 @@ static void dwxgmac2_get_hw_feature(void __iomem *ioaddr,
dma_cap->eee = (hw_cap & XGMAC_HWFEAT_EEESEL) >> 13;
dma_cap->atime_stamp = (hw_cap & XGMAC_HWFEAT_TSSEL) >> 12;
dma_cap->av = (hw_cap & XGMAC_HWFEAT_AVSEL) >> 11;
dma_cap->av &= !(hw_cap & XGMAC_HWFEAT_RAVSEL) >> 10;
dma_cap->av &= !((hw_cap & XGMAC_HWFEAT_RAVSEL) >> 10);
dma_cap->arpoffsel = (hw_cap & XGMAC_HWFEAT_ARPOFFSEL) >> 9;
dma_cap->rmon = (hw_cap & XGMAC_HWFEAT_MMCSEL) >> 8;
dma_cap->pmt_magic_frame = (hw_cap & XGMAC_HWFEAT_MGKSEL) >> 7;
......@@ -470,6 +470,7 @@ static void dwxgmac2_enable_tso(void __iomem *ioaddr, bool en, u32 chan)
static void dwxgmac2_qmode(void __iomem *ioaddr, u32 channel, u8 qmode)
{
u32 value = readl(ioaddr + XGMAC_MTL_TXQ_OPMODE(channel));
u32 flow = readl(ioaddr + XGMAC_RX_FLOW_CTRL);
value &= ~XGMAC_TXQEN;
if (qmode != MTL_QUEUE_AVB) {
......@@ -477,6 +478,7 @@ static void dwxgmac2_qmode(void __iomem *ioaddr, u32 channel, u8 qmode)
writel(0, ioaddr + XGMAC_MTL_TCx_ETS_CONTROL(channel));
} else {
value |= 0x1 << XGMAC_TXQEN_SHIFT;
writel(flow & (~XGMAC_RFE), ioaddr + XGMAC_RX_FLOW_CTRL);
}
writel(value, ioaddr + XGMAC_MTL_TXQ_OPMODE(channel));
......
......@@ -176,6 +176,7 @@
#define MMC_XGMAC_RX_PKT_SMD_ERR 0x22c
#define MMC_XGMAC_RX_PKT_ASSEMBLY_OK 0x230
#define MMC_XGMAC_RX_FPE_FRAG 0x234
#define MMC_XGMAC_RX_IPC_INTR_MASK 0x25c
static void dwmac_mmc_ctrl(void __iomem *mmcaddr, unsigned int mode)
{
......@@ -333,8 +334,9 @@ static void dwxgmac_mmc_ctrl(void __iomem *mmcaddr, unsigned int mode)
static void dwxgmac_mmc_intr_all_mask(void __iomem *mmcaddr)
{
writel(MMC_DEFAULT_MASK, mmcaddr + MMC_RX_INTR_MASK);
writel(MMC_DEFAULT_MASK, mmcaddr + MMC_TX_INTR_MASK);
writel(0x0, mmcaddr + MMC_RX_INTR_MASK);
writel(0x0, mmcaddr + MMC_TX_INTR_MASK);
writel(MMC_DEFAULT_MASK, mmcaddr + MMC_XGMAC_RX_IPC_INTR_MASK);
}
static void dwxgmac_read_mmc_reg(void __iomem *addr, u32 reg, u32 *dest)
......
......@@ -2996,6 +2996,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
stmmac_set_desc_addr(priv, first, des);
tmp_pay_len = pay_len;
des += proto_hdr_len;
pay_len = 0;
}
stmmac_tso_allocator(priv, des, tmp_pay_len, (nfrags == 0), queue);
......@@ -3023,6 +3024,19 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
/* Only the last descriptor gets to point to the skb. */
tx_q->tx_skbuff[tx_q->cur_tx] = skb;
/* Manage tx mitigation */
tx_q->tx_count_frames += nfrags + 1;
if (likely(priv->tx_coal_frames > tx_q->tx_count_frames) &&
!((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
priv->hwts_tx_en)) {
stmmac_tx_timer_arm(priv, queue);
} else {
desc = &tx_q->dma_tx[tx_q->cur_tx];
tx_q->tx_count_frames = 0;
stmmac_set_tx_ic(priv, desc);
priv->xstats.tx_set_ic_bit++;
}
/* We've used all descriptors we need for this skb, however,
* advance cur_tx so that it references a fresh descriptor.
* ndo_start_xmit will fill this descriptor the next time it's
......@@ -3040,19 +3054,6 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
priv->xstats.tx_tso_frames++;
priv->xstats.tx_tso_nfrags += nfrags;
/* Manage tx mitigation */
tx_q->tx_count_frames += nfrags + 1;
if (likely(priv->tx_coal_frames > tx_q->tx_count_frames) &&
!(priv->synopsys_id >= DWMAC_CORE_4_00 &&
(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
priv->hwts_tx_en)) {
stmmac_tx_timer_arm(priv, queue);
} else {
tx_q->tx_count_frames = 0;
stmmac_set_tx_ic(priv, desc);
priv->xstats.tx_set_ic_bit++;
}
if (priv->sarc_type)
stmmac_set_desc_sarc(priv, first, priv->sarc_type);
......@@ -3224,6 +3225,27 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
/* Only the last descriptor gets to point to the skb. */
tx_q->tx_skbuff[entry] = skb;
/* According to the coalesce parameter the IC bit for the latest
* segment is reset and the timer re-started to clean the tx status.
* This approach takes care about the fragments: desc is the first
* element in case of no SG.
*/
tx_q->tx_count_frames += nfrags + 1;
if (likely(priv->tx_coal_frames > tx_q->tx_count_frames) &&
!((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
priv->hwts_tx_en)) {
stmmac_tx_timer_arm(priv, queue);
} else {
if (likely(priv->extend_desc))
desc = &tx_q->dma_etx[entry].basic;
else
desc = &tx_q->dma_tx[entry];
tx_q->tx_count_frames = 0;
stmmac_set_tx_ic(priv, desc);
priv->xstats.tx_set_ic_bit++;
}
/* We've used all descriptors we need for this skb, however,
* advance cur_tx so that it references a fresh descriptor.
* ndo_start_xmit will fill this descriptor the next time it's
......@@ -3259,23 +3281,6 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
dev->stats.tx_bytes += skb->len;
/* According to the coalesce parameter the IC bit for the latest
* segment is reset and the timer re-started to clean the tx status.
* This approach takes care about the fragments: desc is the first
* element in case of no SG.
*/
tx_q->tx_count_frames += nfrags + 1;
if (likely(priv->tx_coal_frames > tx_q->tx_count_frames) &&
!(priv->synopsys_id >= DWMAC_CORE_4_00 &&
(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
priv->hwts_tx_en)) {
stmmac_tx_timer_arm(priv, queue);
} else {
tx_q->tx_count_frames = 0;
stmmac_set_tx_ic(priv, desc);
priv->xstats.tx_set_ic_bit++;
}
if (priv->sarc_type)
stmmac_set_desc_sarc(priv, first, priv->sarc_type);
......@@ -3506,8 +3511,6 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
if (unlikely(status & dma_own))
break;
count++;
rx_q->cur_rx = STMMAC_GET_ENTRY(rx_q->cur_rx, DMA_RX_SIZE);
next_entry = rx_q->cur_rx;
......@@ -3534,6 +3537,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
goto read_again;
if (unlikely(error)) {
dev_kfree_skb(skb);
count++;
continue;
}
......@@ -3573,6 +3577,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
skb = napi_alloc_skb(&ch->rx_napi, len);
if (!skb) {
priv->dev->stats.rx_dropped++;
count++;
continue;
}
......@@ -3638,6 +3643,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
priv->dev->stats.rx_packets++;
priv->dev->stats.rx_bytes += len;
count++;
}
if (status & rx_not_ls) {
......
......@@ -6,7 +6,9 @@
* Author: Jose Abreu <joabreu@synopsys.com>
*/
#include <linux/bitrev.h>
#include <linux/completion.h>
#include <linux/crc32.h>
#include <linux/ethtool.h>
#include <linux/ip.h>
#include <linux/phy.h>
......@@ -485,12 +487,48 @@ static int stmmac_filter_check(struct stmmac_priv *priv)
return -EOPNOTSUPP;
}
static bool stmmac_hash_check(struct stmmac_priv *priv, unsigned char *addr)
{
int mc_offset = 32 - priv->hw->mcast_bits_log2;
struct netdev_hw_addr *ha;
u32 hash, hash_nr;
/* First compute the hash for desired addr */
hash = bitrev32(~crc32_le(~0, addr, 6)) >> mc_offset;
hash_nr = hash >> 5;
hash = 1 << (hash & 0x1f);
/* Now, check if it collides with any existing one */
netdev_for_each_mc_addr(ha, priv->dev) {
u32 nr = bitrev32(~crc32_le(~0, ha->addr, ETH_ALEN)) >> mc_offset;
if (((nr >> 5) == hash_nr) && ((1 << (nr & 0x1f)) == hash))
return false;
}
/* No collisions, address is good to go */
return true;
}
static bool stmmac_perfect_check(struct stmmac_priv *priv, unsigned char *addr)
{
struct netdev_hw_addr *ha;
/* Check if it collides with any existing one */
netdev_for_each_uc_addr(ha, priv->dev) {
if (!memcmp(ha->addr, addr, ETH_ALEN))
return false;
}
/* No collisions, address is good to go */
return true;
}
static int stmmac_test_hfilt(struct stmmac_priv *priv)
{
unsigned char gd_addr[ETH_ALEN] = {0x01, 0xee, 0xdd, 0xcc, 0xbb, 0xaa};
unsigned char bd_addr[ETH_ALEN] = {0x01, 0x01, 0x02, 0x03, 0x04, 0x05};
unsigned char gd_addr[ETH_ALEN] = {0xf1, 0xee, 0xdd, 0xcc, 0xbb, 0xaa};
unsigned char bd_addr[ETH_ALEN] = {0xf1, 0xff, 0xff, 0xff, 0xff, 0xff};
struct stmmac_packet_attrs attr = { };
int ret;
int ret, tries = 256;
ret = stmmac_filter_check(priv);
if (ret)
......@@ -499,6 +537,16 @@ static int stmmac_test_hfilt(struct stmmac_priv *priv)
if (netdev_mc_count(priv->dev) >= priv->hw->multicast_filter_bins)
return -EOPNOTSUPP;
while (--tries) {
/* We only need to check the bd_addr for collisions */
bd_addr[ETH_ALEN - 1] = tries;
if (stmmac_hash_check(priv, bd_addr))
break;
}
if (!tries)
return -EOPNOTSUPP;
ret = dev_mc_add(priv->dev, gd_addr);
if (ret)
return ret;
......@@ -523,13 +571,25 @@ static int stmmac_test_hfilt(struct stmmac_priv *priv)
static int stmmac_test_pfilt(struct stmmac_priv *priv)
{
unsigned char gd_addr[ETH_ALEN] = {0x00, 0x01, 0x44, 0x55, 0x66, 0x77};
unsigned char bd_addr[ETH_ALEN] = {0x08, 0x00, 0x22, 0x33, 0x44, 0x55};
unsigned char gd_addr[ETH_ALEN] = {0xf0, 0x01, 0x44, 0x55, 0x66, 0x77};
unsigned char bd_addr[ETH_ALEN] = {0xf0, 0xff, 0xff, 0xff, 0xff, 0xff};
struct stmmac_packet_attrs attr = { };
int ret;
int ret, tries = 256;
if (stmmac_filter_check(priv))
return -EOPNOTSUPP;
if (netdev_uc_count(priv->dev) >= priv->hw->unicast_filter_entries)
return -EOPNOTSUPP;
while (--tries) {
/* We only need to check the bd_addr for collisions */
bd_addr[ETH_ALEN - 1] = tries;
if (stmmac_perfect_check(priv, bd_addr))
break;
}
if (!tries)
return -EOPNOTSUPP;
ret = dev_uc_add(priv->dev, gd_addr);
if (ret)
......@@ -553,39 +613,31 @@ static int stmmac_test_pfilt(struct stmmac_priv *priv)
return ret;
}
static int stmmac_dummy_sync(struct net_device *netdev, const u8 *addr)
{
return 0;
}
static void stmmac_test_set_rx_mode(struct net_device *netdev)
{
/* As we are in test mode of ethtool we already own the rtnl lock
* so no address will change from user. We can just call the
* ndo_set_rx_mode() callback directly */
if (netdev->netdev_ops->ndo_set_rx_mode)
netdev->netdev_ops->ndo_set_rx_mode(netdev);
}
static int stmmac_test_mcfilt(struct stmmac_priv *priv)
{
unsigned char uc_addr[ETH_ALEN] = {0x00, 0x01, 0x44, 0x55, 0x66, 0x77};
unsigned char mc_addr[ETH_ALEN] = {0x01, 0x01, 0x44, 0x55, 0x66, 0x77};
unsigned char uc_addr[ETH_ALEN] = {0xf0, 0xff, 0xff, 0xff, 0xff, 0xff};
unsigned char mc_addr[ETH_ALEN] = {0xf1, 0xff, 0xff, 0xff, 0xff, 0xff};
struct stmmac_packet_attrs attr = { };
int ret;
int ret, tries = 256;
if (stmmac_filter_check(priv))
return -EOPNOTSUPP;
if (!priv->hw->multicast_filter_bins)
if (netdev_uc_count(priv->dev) >= priv->hw->unicast_filter_entries)
return -EOPNOTSUPP;
/* Remove all MC addresses */
__dev_mc_unsync(priv->dev, NULL);
stmmac_test_set_rx_mode(priv->dev);
while (--tries) {
/* We only need to check the mc_addr for collisions */
mc_addr[ETH_ALEN - 1] = tries;
if (stmmac_hash_check(priv, mc_addr))
break;
}
if (!tries)
return -EOPNOTSUPP;
ret = dev_uc_add(priv->dev, uc_addr);
if (ret)
goto cleanup;
return ret;
attr.dst = uc_addr;
......@@ -602,30 +654,34 @@ static int stmmac_test_mcfilt(struct stmmac_priv *priv)
cleanup:
dev_uc_del(priv->dev, uc_addr);
__dev_mc_sync(priv->dev, stmmac_dummy_sync, NULL);
stmmac_test_set_rx_mode(priv->dev);
return ret;
}
static int stmmac_test_ucfilt(struct stmmac_priv *priv)
{
unsigned char uc_addr[ETH_ALEN] = {0x00, 0x01, 0x44, 0x55, 0x66, 0x77};
unsigned char mc_addr[ETH_ALEN] = {0x01, 0x01, 0x44, 0x55, 0x66, 0x77};
unsigned char uc_addr[ETH_ALEN] = {0xf0, 0xff, 0xff, 0xff, 0xff, 0xff};
unsigned char mc_addr[ETH_ALEN] = {0xf1, 0xff, 0xff, 0xff, 0xff, 0xff};
struct stmmac_packet_attrs attr = { };
int ret;
int ret, tries = 256;
if (stmmac_filter_check(priv))
return -EOPNOTSUPP;
if (!priv->hw->multicast_filter_bins)
if (netdev_mc_count(priv->dev) >= priv->hw->multicast_filter_bins)
return -EOPNOTSUPP;
/* Remove all UC addresses */
__dev_uc_unsync(priv->dev, NULL);
stmmac_test_set_rx_mode(priv->dev);
while (--tries) {
/* We only need to check the uc_addr for collisions */
uc_addr[ETH_ALEN - 1] = tries;
if (stmmac_perfect_check(priv, uc_addr))
break;
}
if (!tries)
return -EOPNOTSUPP;
ret = dev_mc_add(priv->dev, mc_addr);
if (ret)
goto cleanup;
return ret;
attr.dst = mc_addr;
......@@ -642,8 +698,6 @@ static int stmmac_test_ucfilt(struct stmmac_priv *priv)
cleanup:
dev_mc_del(priv->dev, mc_addr);
__dev_uc_sync(priv->dev, stmmac_dummy_sync, NULL);
stmmac_test_set_rx_mode(priv->dev);
return ret;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment