diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 49f72e1ffbef..de507c32036c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -381,7 +381,7 @@ struct stmmac_ops { int (*host_irq_status)(struct mac_device_info *hw, struct stmmac_extra_stats *x); /* Multicast filter setting */ - void (*set_filter)(struct net_device *dev); + void (*set_filter)(struct mac_device_info *hw, struct net_device *dev); /* Flow control setting */ void (*flow_ctrl)(struct mac_device_info *hw, unsigned int duplex, unsigned int fc, unsigned int pause_time); @@ -442,9 +442,13 @@ struct mac_device_info { struct mac_link link; unsigned int synopsys_uid; void __iomem *pcsr; /* vpointer to device CSRs */ + int multicast_filter_bins; + int unicast_filter_entries; + int mcast_bits_log2; }; -struct mac_device_info *dwmac1000_setup(void __iomem *ioaddr); +struct mac_device_info *dwmac1000_setup(void __iomem *ioaddr, int mcbins, + int perfect_uc_entries); struct mac_device_info *dwmac100_setup(void __iomem *ioaddr); void stmmac_set_mac_addr(void __iomem *ioaddr, u8 addr[6], diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h index 285e3056f362..71b5419256c1 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h @@ -261,6 +261,7 @@ enum rtc_control { #define GMAC_MMC_RX_INTR 0x104 #define GMAC_MMC_TX_INTR 0x108 #define GMAC_MMC_RX_CSUM_OFFLOAD 0x208 +#define GMAC_EXTHASH_BASE 0x500 extern const struct stmmac_dma_ops dwmac1000_dma_ops; #endif /* __DWMAC1000_H__ */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c index cdcbad1f1ac0..d8ef18786a1c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c @@ -97,12 +97,41 @@ static void dwmac1000_get_umac_addr(struct mac_device_info *hw, GMAC_ADDR_LOW(reg_n)); } -static void dwmac1000_set_filter(struct net_device *dev) +static void dwmac1000_set_mchash(void __iomem *ioaddr, u32 *mcfilterbits, + int mcbitslog2) +{ + int numhashregs, regs; + + switch (mcbitslog2) { + case 6: + writel(mcfilterbits[0], ioaddr + GMAC_HASH_LOW); + writel(mcfilterbits[1], ioaddr + GMAC_HASH_HIGH); + return; + break; + case 7: + numhashregs = 4; + break; + case 8: + numhashregs = 8; + break; + default: + pr_debug("STMMAC: err in setting mulitcast filter\n"); + return; + break; + } + for (regs = 0; regs < numhashregs; regs++) + writel(mcfilterbits[regs], + ioaddr + GMAC_EXTHASH_BASE + regs * 4); +} + +static void dwmac1000_set_filter(struct mac_device_info *hw, + struct net_device *dev) { void __iomem *ioaddr = (void __iomem *)dev->base_addr; unsigned int value = 0; - unsigned int perfect_addr_number; + unsigned int perfect_addr_number = hw->unicast_filter_entries; u32 mc_filter[2]; + int mcbitslog2 = hw->mcast_bits_log2; pr_debug("%s: # mcasts %d, # unicast %d\n", __func__, netdev_mc_count(dev), netdev_uc_count(dev)); @@ -120,10 +149,14 @@ static void dwmac1000_set_filter(struct net_device *dev) value = GMAC_FRAME_FILTER_HMC; netdev_for_each_mc_addr(ha, dev) { - /* The upper 6 bits of the calculated CRC are used to - * index the contens of the hash table + /* The upper n bits of the calculated CRC are used to + * index the contents of the hash table. The number of + * bits used depends on the hardware configuration + * selected at core configuration time. */ - int bit_nr = bitrev32(~crc32_le(~0, ha->addr, 6)) >> 26; + int 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 * within the register. @@ -132,15 +165,12 @@ static void dwmac1000_set_filter(struct net_device *dev) } } - writel(mc_filter[0], ioaddr + GMAC_HASH_LOW); - writel(mc_filter[1], ioaddr + GMAC_HASH_HIGH); - - perfect_addr_number = GMAC_MAX_PERFECT_ADDRESSES; + dwmac1000_set_mchash(ioaddr, mc_filter, mcbitslog2); /* Handle multiple unicast addresses (perfect filtering) */ if (netdev_uc_count(dev) > perfect_addr_number) - /* Switch to promiscuous mode if more than 16 addrs - * are required + /* Switch to promiscuous mode if more than unicast + * addresses are requested than supported by hardware. */ value |= GMAC_FRAME_FILTER_PR; else { @@ -160,10 +190,6 @@ static void dwmac1000_set_filter(struct net_device *dev) value |= GMAC_FRAME_FILTER_RA; #endif writel(value, ioaddr + GMAC_FRAME_FILTER); - - pr_debug("\tFilter: 0x%08x\n\tHash: HI 0x%08x, LO 0x%08x\n", - readl(ioaddr + GMAC_FRAME_FILTER), - readl(ioaddr + GMAC_HASH_HIGH), readl(ioaddr + GMAC_HASH_LOW)); } @@ -382,7 +408,8 @@ static const struct stmmac_ops dwmac1000_ops = { .get_adv = dwmac1000_get_adv, }; -struct mac_device_info *dwmac1000_setup(void __iomem *ioaddr) +struct mac_device_info *dwmac1000_setup(void __iomem *ioaddr, int mcbins, + int perfect_uc_entries) { struct mac_device_info *mac; u32 hwid = readl(ioaddr + GMAC_VERSION); @@ -392,6 +419,13 @@ struct mac_device_info *dwmac1000_setup(void __iomem *ioaddr) return NULL; mac->pcsr = ioaddr; + mac->multicast_filter_bins = mcbins; + mac->unicast_filter_entries = perfect_uc_entries; + mac->mcast_bits_log2 = 0; + + if (mac->multicast_filter_bins) + mac->mcast_bits_log2 = ilog2(mac->multicast_filter_bins); + mac->mac = &dwmac1000_ops; mac->dma = &dwmac1000_dma_ops; diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c index 3a2d63388f8d..f8dd773f246c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c @@ -95,7 +95,8 @@ static void dwmac100_get_umac_addr(struct mac_device_info *hw, stmmac_get_mac_addr(ioaddr, addr, MAC_ADDR_HIGH, MAC_ADDR_LOW); } -static void dwmac100_set_filter(struct net_device *dev) +static void dwmac100_set_filter(struct mac_device_info *hw, + struct net_device *dev) { void __iomem *ioaddr = (void __iomem *)dev->base_addr; u32 value = readl(ioaddr + MAC_CONTROL); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index cff2b69e62ee..08addd653728 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -2225,7 +2225,7 @@ static void stmmac_set_rx_mode(struct net_device *dev) struct stmmac_priv *priv = netdev_priv(dev); spin_lock(&priv->lock); - priv->hw->mac->set_filter(dev); + priv->hw->mac->set_filter(priv->hw, dev); spin_unlock(&priv->lock); } @@ -2598,7 +2598,9 @@ static int stmmac_hw_init(struct stmmac_priv *priv) /* Identify the MAC HW device */ if (priv->plat->has_gmac) { priv->dev->priv_flags |= IFF_UNICAST_FLT; - mac = dwmac1000_setup(priv->ioaddr); + mac = dwmac1000_setup(priv->ioaddr, + priv->plat->multicast_filter_bins, + priv->plat->unicast_filter_entries); } else { mac = dwmac100_setup(priv->ioaddr); } diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index ea7a65be1f9a..bb524a932be4 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -52,6 +52,59 @@ static const struct of_device_id stmmac_dt_ids[] = { MODULE_DEVICE_TABLE(of, stmmac_dt_ids); #ifdef CONFIG_OF + +/* This function validates the number of Multicast filtering bins specified + * by the configuration through the device tree. The Synopsys GMAC supports + * 64 bins, 128 bins, or 256 bins. "bins" refer to the division of CRC + * number space. 64 bins correspond to 6 bits of the CRC, 128 corresponds + * to 7 bits, and 256 refers to 8 bits of the CRC. Any other setting is + * invalid and will cause the filtering algorithm to use Multicast + * promiscuous mode. + */ +static int dwmac1000_validate_mcast_bins(int mcast_bins) +{ + int x = mcast_bins; + + switch (x) { + case HASH_TABLE_SIZE: + case 128: + case 256: + break; + default: + x = 0; + pr_info("Hash table entries set to unexpected value %d", + mcast_bins); + break; + } + return x; +} + +/* This function validates the number of Unicast address entries supported + * by a particular Synopsys 10/100/1000 controller. The Synopsys controller + * supports 1, 32, 64, or 128 Unicast filter entries for it's Unicast filter + * logic. This function validates a valid, supported configuration is + * selected, and defaults to 1 Unicast address if an unsupported + * configuration is selected. + */ +static int dwmac1000_validate_ucast_entries(int ucast_entries) +{ + int x = ucast_entries; + + switch (x) { + case 1: + case 32: + case 64: + case 128: + break; + default: + x = 1; + pr_info("Unicast table entries set to unexpected value %d\n", + ucast_entries); + break; + } + return x; +} + static int stmmac_probe_config_dt(struct platform_device *pdev, struct plat_stmmacenet_data *plat, const char **mac) @@ -115,6 +168,12 @@ static int stmmac_probe_config_dt(struct platform_device *pdev, */ plat->maxmtu = JUMBO_LEN; + /* Set default value for multicast hash bins */ + plat->multicast_filter_bins = HASH_TABLE_SIZE; + + /* Set default value for unicast filter entries */ + plat->unicast_filter_entries = 1; + /* * Currently only the properties needed on SPEAr600 * are provided. All other properties should be added @@ -131,6 +190,14 @@ static int stmmac_probe_config_dt(struct platform_device *pdev, * are clearly MTUs */ of_property_read_u32(np, "max-frame-size", &plat->maxmtu); + of_property_read_u32(np, "snps,multicast-filter-bins", + &plat->multicast_filter_bins); + of_property_read_u32(np, "snps,perfect-filter-entries", + &plat->unicast_filter_entries); + plat->unicast_filter_entries = dwmac1000_validate_ucast_entries( + plat->unicast_filter_entries); + plat->multicast_filter_bins = dwmac1000_validate_mcast_bins( + plat->multicast_filter_bins); plat->has_gmac = 1; plat->pmt = 1; } diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h index 6f27d4f957bd..cd63851b57f2 100644 --- a/include/linux/stmmac.h +++ b/include/linux/stmmac.h @@ -112,6 +112,8 @@ struct plat_stmmacenet_data { int riwt_off; int max_speed; int maxmtu; + int multicast_filter_bins; + int unicast_filter_entries; void (*fix_mac_speed)(void *priv, unsigned int speed); void (*bus_setup)(void __iomem *ioaddr); void *(*setup)(struct platform_device *pdev);