OSDN Git Service

Merge Official Source
[immortalwrt/immortalwrt.git] / target / linux / ipq40xx / files / drivers / net / mdio / ar40xx.c
index d5ef7af..360d956 100644 (file)
@@ -83,6 +83,8 @@ static const struct ar40xx_mib_desc ar40xx_mibs[] = {
        MIB_DESC(1, AR40XX_STATS_TXLATECOL, "TxLateCol"),
 };
 
+static const int ar40xx_mibs_rx_tx[] = { 15, 31 };
+
 static u32
 ar40xx_read(struct ar40xx_priv *priv, int reg)
 {
@@ -431,6 +433,36 @@ ar40xx_mib_fetch_port_stat(struct ar40xx_priv *priv, int port, bool flush)
        }
 }
 
+/* similar to ar40xx_mib_fetch_port_stat, but only fetch RXGOODBYTE and TXBYTE */
+static void
+ar40xx_mib_fetch_port_stat_rx_tx(struct ar40xx_priv *priv, int port)
+{
+       unsigned int base;
+       u64 *mib_stats;
+       int j;
+       u32 num_mibs = ARRAY_SIZE(ar40xx_mibs);
+
+       WARN_ON(port >= priv->dev.ports);
+
+       lockdep_assert_held(&priv->mib_lock);
+
+       base = AR40XX_REG_PORT_STATS_START +
+              AR40XX_REG_PORT_STATS_LEN * port;
+
+       mib_stats = &priv->mib_stats[port * num_mibs];
+       for (j = 0; j < ARRAY_SIZE(ar40xx_mibs_rx_tx); j++) {
+               const int i = ar40xx_mibs_rx_tx[j];
+               const struct ar40xx_mib_desc *mib;
+               u64 t;
+
+               mib = &ar40xx_mibs[i];
+               t = ar40xx_read(priv, base + mib->offset);
+               t |= ar40xx_read(priv, base + mib->offset + 4) << 32;
+
+               mib_stats[i] += t;
+       }
+}
+
 static int
 ar40xx_mib_capture(struct ar40xx_priv *priv)
 {
@@ -772,6 +804,36 @@ ar40xx_sw_get_port_link(struct switch_dev *dev, int port,
        return 0;
 }
 
+static int
+ar40xx_sw_get_port_stats(struct switch_dev *dev, int port,
+                       struct switch_port_stats *stats)
+{
+       struct ar40xx_priv *priv = swdev_to_ar40xx(dev);
+       u32 num_mibs = ARRAY_SIZE(ar40xx_mibs);
+       u64 *mib_stats;
+       int ret = 0;
+       static int last_port = AR40XX_NUM_PORTS - 1;
+
+       mutex_lock(&priv->mib_lock);
+
+       /* Avoid unnecessary capture */
+       if (last_port >= port) {
+               ret = ar40xx_mib_capture(priv);
+               if (ret)
+                       goto unlock;
+       }
+       last_port = port;
+
+       ar40xx_mib_fetch_port_stat_rx_tx(priv, port);
+
+       mib_stats = &priv->mib_stats[port * num_mibs];
+       stats->rx_bytes = mib_stats[ar40xx_mibs_rx_tx[0]];
+       stats->tx_bytes = mib_stats[ar40xx_mibs_rx_tx[1]];
+unlock:
+       mutex_unlock(&priv->mib_lock);
+       return ret;
+}
+
 static const struct switch_attr ar40xx_sw_attr_globals[] = {
        {
                .type = SWITCH_TYPE_INT,
@@ -1727,6 +1789,7 @@ static const struct switch_dev_ops ar40xx_sw_ops = {
        .apply_config = ar40xx_sw_hw_apply,
        .reset_switch = ar40xx_sw_reset_switch,
        .get_port_link = ar40xx_sw_get_port_link,
+       .get_port_stats = ar40xx_sw_get_port_stats,
 };
 
 /* Platform driver probe function */