|
@@ -63,6 +63,7 @@
|
|
|
#include "fec.h"
|
|
|
|
|
|
static void set_multicast_list(struct net_device *ndev);
|
|
|
+static void fec_enet_itr_coal_init(struct net_device *ndev);
|
|
|
|
|
|
#define DRIVER_NAME "fec"
|
|
|
|
|
@@ -1095,6 +1096,10 @@ fec_restart(struct net_device *ndev)
|
|
|
|
|
|
/* Enable interrupts we wish to service */
|
|
|
writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
|
|
|
+
|
|
|
+ /* Init the interrupt coalescing */
|
|
|
+ fec_enet_itr_coal_init(ndev);
|
|
|
+
|
|
|
}
|
|
|
|
|
|
static void
|
|
@@ -2234,12 +2239,141 @@ static int fec_enet_nway_reset(struct net_device *dev)
|
|
|
return genphy_restart_aneg(phydev);
|
|
|
}
|
|
|
|
|
|
+/* ITR clock source is enet system clock (clk_ahb).
|
|
|
+ * TCTT unit is cycle_ns * 64 cycle
|
|
|
+ * So, the ICTT value = X us / (cycle_ns * 64)
|
|
|
+ */
|
|
|
+static int fec_enet_us_to_itr_clock(struct net_device *ndev, int us)
|
|
|
+{
|
|
|
+ struct fec_enet_private *fep = netdev_priv(ndev);
|
|
|
+
|
|
|
+ return us * (fep->itr_clk_rate / 64000) / 1000;
|
|
|
+}
|
|
|
+
|
|
|
+/* Set threshold for interrupt coalescing */
|
|
|
+static void fec_enet_itr_coal_set(struct net_device *ndev)
|
|
|
+{
|
|
|
+ struct fec_enet_private *fep = netdev_priv(ndev);
|
|
|
+ const struct platform_device_id *id_entry =
|
|
|
+ platform_get_device_id(fep->pdev);
|
|
|
+ int rx_itr, tx_itr;
|
|
|
+
|
|
|
+ if (!(id_entry->driver_data & FEC_QUIRK_HAS_AVB))
|
|
|
+ return;
|
|
|
+
|
|
|
+ /* Must be greater than zero to avoid unpredictable behavior */
|
|
|
+ if (!fep->rx_time_itr || !fep->rx_pkts_itr ||
|
|
|
+ !fep->tx_time_itr || !fep->tx_pkts_itr)
|
|
|
+ return;
|
|
|
+
|
|
|
+ /* Select enet system clock as Interrupt Coalescing
|
|
|
+ * timer Clock Source
|
|
|
+ */
|
|
|
+ rx_itr = FEC_ITR_CLK_SEL;
|
|
|
+ tx_itr = FEC_ITR_CLK_SEL;
|
|
|
+
|
|
|
+ /* set ICFT and ICTT */
|
|
|
+ rx_itr |= FEC_ITR_ICFT(fep->rx_pkts_itr);
|
|
|
+ rx_itr |= FEC_ITR_ICTT(fec_enet_us_to_itr_clock(ndev, fep->rx_time_itr));
|
|
|
+ tx_itr |= FEC_ITR_ICFT(fep->tx_pkts_itr);
|
|
|
+ tx_itr |= FEC_ITR_ICTT(fec_enet_us_to_itr_clock(ndev, fep->tx_time_itr));
|
|
|
+
|
|
|
+ rx_itr |= FEC_ITR_EN;
|
|
|
+ tx_itr |= FEC_ITR_EN;
|
|
|
+
|
|
|
+ writel(tx_itr, fep->hwp + FEC_TXIC0);
|
|
|
+ writel(rx_itr, fep->hwp + FEC_RXIC0);
|
|
|
+ writel(tx_itr, fep->hwp + FEC_TXIC1);
|
|
|
+ writel(rx_itr, fep->hwp + FEC_RXIC1);
|
|
|
+ writel(tx_itr, fep->hwp + FEC_TXIC2);
|
|
|
+ writel(rx_itr, fep->hwp + FEC_RXIC2);
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+fec_enet_get_coalesce(struct net_device *ndev, struct ethtool_coalesce *ec)
|
|
|
+{
|
|
|
+ struct fec_enet_private *fep = netdev_priv(ndev);
|
|
|
+ const struct platform_device_id *id_entry =
|
|
|
+ platform_get_device_id(fep->pdev);
|
|
|
+
|
|
|
+ if (!(id_entry->driver_data & FEC_QUIRK_HAS_AVB))
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+
|
|
|
+ ec->rx_coalesce_usecs = fep->rx_time_itr;
|
|
|
+ ec->rx_max_coalesced_frames = fep->rx_pkts_itr;
|
|
|
+
|
|
|
+ ec->tx_coalesce_usecs = fep->tx_time_itr;
|
|
|
+ ec->tx_max_coalesced_frames = fep->tx_pkts_itr;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+fec_enet_set_coalesce(struct net_device *ndev, struct ethtool_coalesce *ec)
|
|
|
+{
|
|
|
+ struct fec_enet_private *fep = netdev_priv(ndev);
|
|
|
+ const struct platform_device_id *id_entry =
|
|
|
+ platform_get_device_id(fep->pdev);
|
|
|
+
|
|
|
+ unsigned int cycle;
|
|
|
+
|
|
|
+ if (!(id_entry->driver_data & FEC_QUIRK_HAS_AVB))
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+
|
|
|
+ if (ec->rx_max_coalesced_frames > 255) {
|
|
|
+ pr_err("Rx coalesced frames exceed hardware limiation");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ec->tx_max_coalesced_frames > 255) {
|
|
|
+ pr_err("Tx coalesced frame exceed hardware limiation");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ cycle = fec_enet_us_to_itr_clock(ndev, fep->rx_time_itr);
|
|
|
+ if (cycle > 0xFFFF) {
|
|
|
+ pr_err("Rx coalesed usec exceeed hardware limiation");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ cycle = fec_enet_us_to_itr_clock(ndev, fep->tx_time_itr);
|
|
|
+ if (cycle > 0xFFFF) {
|
|
|
+ pr_err("Rx coalesed usec exceeed hardware limiation");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ fep->rx_time_itr = ec->rx_coalesce_usecs;
|
|
|
+ fep->rx_pkts_itr = ec->rx_max_coalesced_frames;
|
|
|
+
|
|
|
+ fep->tx_time_itr = ec->tx_coalesce_usecs;
|
|
|
+ fep->tx_pkts_itr = ec->tx_max_coalesced_frames;
|
|
|
+
|
|
|
+ fec_enet_itr_coal_set(ndev);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void fec_enet_itr_coal_init(struct net_device *ndev)
|
|
|
+{
|
|
|
+ struct ethtool_coalesce ec;
|
|
|
+
|
|
|
+ ec.rx_coalesce_usecs = FEC_ITR_ICTT_DEFAULT;
|
|
|
+ ec.rx_max_coalesced_frames = FEC_ITR_ICFT_DEFAULT;
|
|
|
+
|
|
|
+ ec.tx_coalesce_usecs = FEC_ITR_ICTT_DEFAULT;
|
|
|
+ ec.tx_max_coalesced_frames = FEC_ITR_ICFT_DEFAULT;
|
|
|
+
|
|
|
+ fec_enet_set_coalesce(ndev, &ec);
|
|
|
+}
|
|
|
+
|
|
|
static const struct ethtool_ops fec_enet_ethtool_ops = {
|
|
|
.get_settings = fec_enet_get_settings,
|
|
|
.set_settings = fec_enet_set_settings,
|
|
|
.get_drvinfo = fec_enet_get_drvinfo,
|
|
|
.nway_reset = fec_enet_nway_reset,
|
|
|
.get_link = ethtool_op_get_link,
|
|
|
+ .get_coalesce = fec_enet_get_coalesce,
|
|
|
+ .set_coalesce = fec_enet_set_coalesce,
|
|
|
#ifndef CONFIG_M5272
|
|
|
.get_pauseparam = fec_enet_get_pauseparam,
|
|
|
.set_pauseparam = fec_enet_set_pauseparam,
|
|
@@ -3006,6 +3140,8 @@ fec_probe(struct platform_device *pdev)
|
|
|
goto failed_clk;
|
|
|
}
|
|
|
|
|
|
+ fep->itr_clk_rate = clk_get_rate(fep->clk_ahb);
|
|
|
+
|
|
|
/* enet_out is optional, depends on board */
|
|
|
fep->clk_enet_out = devm_clk_get(&pdev->dev, "enet_out");
|
|
|
if (IS_ERR(fep->clk_enet_out))
|