diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 760ccc7..37b367c 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -1008,6 +1008,7 @@ static int cpsw_set_coalesce_mcast(struct net_device *ndev, { struct cpsw_priv *priv = netdev_priv(ndev); int port; + int ret = -EINVAL; priv->rx_max_mcast = coal->rx_max_mcast; @@ -1016,9 +1017,25 @@ static int cpsw_set_coalesce_mcast(struct net_device *ndev, else port = priv->data.active_slave; - cpsw_ale_control_set(priv->ale, port, ALE_PORT_MCAST_LIMIT, - coal->rx_max_mcast); - + ret = cpsw_ale_set_ratelimit(priv->ale, + priv->bus_freq_mhz * 1000000, + port, + coal->rx_max_bcast, + coal->rx_max_mcast, + 0); + if (ret) + dev_err(priv->dev, "CPSW_ALE set ratelimit failed"); + + ret = cpsw_ale_set_ratelimit(priv->ale, + priv->bus_freq_mhz * 1000000, + port, + coal->rx_max_bcast, + coal->rx_max_mcast, + 1); + if (ret) + dev_err(priv->dev, "CPSW_ALE set ratelimit failed"); + + dev_dbg(priv->dev, "rx_max_mcast set to %d\n", priv->rx_max_mcast); return 0; } @@ -1028,6 +1045,7 @@ static int cpsw_set_coalesce_bcast(struct net_device *ndev, { struct cpsw_priv *priv = netdev_priv(ndev); int port; + int ret = -EINVAL; priv->rx_max_bcast = coal->rx_max_bcast; @@ -1036,8 +1054,23 @@ static int cpsw_set_coalesce_bcast(struct net_device *ndev, else port = priv->data.active_slave + 1; - cpsw_ale_control_set(priv->ale, port, ALE_PORT_BCAST_LIMIT, - coal->rx_max_bcast); + ret = cpsw_ale_set_ratelimit(priv->ale, + priv->bus_freq_mhz * 1000000, + port, + coal->rx_max_bcast, + coal->rx_max_mcast, + 0); + if (ret) + dev_err(priv->dev, "CPSW_ALE set ratelimit failed"); + + ret = cpsw_ale_set_ratelimit(priv->ale, + priv->bus_freq_mhz * 1000000, + port, + coal->rx_max_bcast, + coal->rx_max_mcast, + 1); + if (ret) + dev_err(priv->dev, "CPSW_ALE set ratelimit failed"); dev_dbg(priv->dev, "rx_max_mcast set to %d\n", priv->rx_max_bcast); return 0; @@ -1911,26 +1944,15 @@ static int cpsw_switch_config_ioctl(struct net_device *ndev, break; } - ret = cpsw_ale_control_set(priv->ale, 0, ALE_RATE_LIMIT_TX, - !!config.direction); - if (ret) { - dev_err(priv->dev, "CPSW_ALE control set failed"); - break; - } - - ret = cpsw_ale_control_set(priv->ale, config.port, - ALE_PORT_BCAST_LIMIT, - config.bcast_rate_limit); - if (ret) { - dev_err(priv->dev, "CPSW_ALE control set failed"); - break; - } + ret = cpsw_ale_set_ratelimit(priv->ale, + priv->bus_freq_mhz * 1000000, + config.port, + config.bcast_rate_limit, + config.mcast_rate_limit, + !!config.direction); - ret = cpsw_ale_control_set(priv->ale, config.port, - ALE_PORT_MCAST_LIMIT, - config.mcast_rate_limit); if (ret) - dev_err(priv->dev, "CPSW_ALE control set failed"); + dev_err(priv->dev, "CPSW_ALE control set ratelimit"); break; } diff --git a/drivers/net/ethernet/ti/cpsw_ale.c b/drivers/net/ethernet/ti/cpsw_ale.c index 44fb14d..27fe2af 100644 --- a/drivers/net/ethernet/ti/cpsw_ale.c +++ b/drivers/net/ethernet/ti/cpsw_ale.c @@ -826,6 +826,52 @@ int cpsw_ale_control_get(struct cpsw_ale *ale, int port, int control) } EXPORT_SYMBOL_GPL(cpsw_ale_control_get); +int cpsw_ale_set_ratelimit(struct cpsw_ale *ale, unsigned long freq, int port, + unsigned int bcast_rate_limit, + unsigned int mcast_rate_limit, + bool direction) + +{ + unsigned int rate_limit; + unsigned long ale_prescale; + + if (!bcast_rate_limit && !mcast_rate_limit) { + /* disable rate limit */ + cpsw_ale_control_set(ale, 0, ALE_RATE_LIMIT, 0); + cpsw_ale_control_set(ale, port, ALE_PORT_BCAST_LIMIT, 0); + cpsw_ale_control_set(ale, port, ALE_PORT_MCAST_LIMIT, 0); + writel(0, ale->params.ale_regs + ALE_PRESCALE); + return 0; + } + + /* configure Broadcast and Multicast Rate Limit + * number_of_packets = (Fclk / ALE_PRESCALE) * port.BCASTMCAST/_LIMIT + * ALE_PRESCALE width is 19bit and min value 0x10 + * with Fclk = 125MHz and port.BCASTMCAST/_LIMIT = 1 + * + * max number_of_packets = (125MHz / 0x10) * 1 = 7812500 + * min number_of_packets = (125MHz / 0xFFFFF) * 1 = 119 + * + * above values are more than enough (with higher Fclk they will be + * just better), so port.BCASTMCAST/_LIMIT can be selected to be 1 + * while ALE_PRESCALE is calculated as: + * ALE_PRESCALE = Fclk / number_of_packets + */ + rate_limit = max_t(unsigned int, bcast_rate_limit, mcast_rate_limit); + ale_prescale = freq / rate_limit; + if (ale_prescale & (~0xfffff)) + return -EINVAL; + + cpsw_ale_control_set(ale, 0, ALE_RATE_LIMIT_TX, direction); + cpsw_ale_control_set(ale, port, ALE_PORT_BCAST_LIMIT, 1); + cpsw_ale_control_set(ale, port, ALE_PORT_MCAST_LIMIT, 1); + writel((u32)ale_prescale, ale->params.ale_regs + ALE_PRESCALE); + cpsw_ale_control_set(ale, 0, ALE_RATE_LIMIT, 1); + + return 0; +} +EXPORT_SYMBOL_GPL(cpsw_ale_set_ratelimit); + static int cpsw_ale_dump_mcast(struct cpsw_ale *ale, u32 *ale_entry, char *buf, int len) { diff --git a/drivers/net/ethernet/ti/cpsw_ale.h b/drivers/net/ethernet/ti/cpsw_ale.h index b165ba9..e6d08f1 100644 --- a/drivers/net/ethernet/ti/cpsw_ale.h +++ b/drivers/net/ethernet/ti/cpsw_ale.h @@ -130,6 +130,10 @@ int cpsw_ale_add_vlan(struct cpsw_ale *ale, u16 vid, int port, int untag, int reg_mcast, int unreg_mcast); int cpsw_ale_del_vlan(struct cpsw_ale *ale, u16 vid, int port); void cpsw_ale_set_allmulti(struct cpsw_ale *ale, int allmulti); +int cpsw_ale_set_ratelimit(struct cpsw_ale *ale, unsigned long freq, int port, + unsigned int bcast_rate_limit, + unsigned int mcast_rate_limit, + bool direction); int cpsw_ale_control_get(struct cpsw_ale *ale, int port, int control); int cpsw_ale_control_set(struct cpsw_ale *ale, int port,