mlx4: Wake on LAN support
authorYevgeny Petrilin <yevgenyp@mellanox.co.il>
Tue, 22 Mar 2011 22:37:59 +0000 (22:37 +0000)
committerDavid S. Miller <davem@davemloft.net>
Wed, 23 Mar 2011 19:24:19 +0000 (12:24 -0700)
The driver queries the FW for WOL support.
Ethtool get/set_wol is implemented accordingly.
Only magic packets are supported at the time.

Signed-off-by: Igor Yarovinsky <igory@mellanox.co.il>
Signed-off-by: Yevgeny Petrilin <yevgenyp@mellanox.co.il>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/mlx4/en_ethtool.c
drivers/net/mlx4/fw.c
drivers/net/mlx4/fw.h
drivers/net/mlx4/main.c
drivers/net/mlx4/mlx4_en.h
include/linux/mlx4/device.h

index 8cfe8586ed2ddbe1f90865ddb0c68ba35dc198b6..c1f351f6ae574349a5eb2044e1a8e835be04dad8 100644 (file)
@@ -131,8 +131,65 @@ static void mlx4_en_set_msglevel(struct net_device *dev, u32 val)
 static void mlx4_en_get_wol(struct net_device *netdev,
                            struct ethtool_wolinfo *wol)
 {
-       wol->supported = 0;
-       wol->wolopts = 0;
+       struct mlx4_en_priv *priv = netdev_priv(netdev);
+       int err = 0;
+       u64 config = 0;
+
+       if (!priv->mdev->dev->caps.wol) {
+               wol->supported = 0;
+               wol->wolopts = 0;
+               return;
+       }
+
+       err = mlx4_wol_read(priv->mdev->dev, &config, priv->port);
+       if (err) {
+               en_err(priv, "Failed to get WoL information\n");
+               return;
+       }
+
+       if (config & MLX4_EN_WOL_MAGIC)
+               wol->supported = WAKE_MAGIC;
+       else
+               wol->supported = 0;
+
+       if (config & MLX4_EN_WOL_ENABLED)
+               wol->wolopts = WAKE_MAGIC;
+       else
+               wol->wolopts = 0;
+}
+
+static int mlx4_en_set_wol(struct net_device *netdev,
+                           struct ethtool_wolinfo *wol)
+{
+       struct mlx4_en_priv *priv = netdev_priv(netdev);
+       u64 config = 0;
+       int err = 0;
+
+       if (!priv->mdev->dev->caps.wol)
+               return -EOPNOTSUPP;
+
+       if (wol->supported & ~WAKE_MAGIC)
+               return -EINVAL;
+
+       err = mlx4_wol_read(priv->mdev->dev, &config, priv->port);
+       if (err) {
+               en_err(priv, "Failed to get WoL info, unable to modify\n");
+               return err;
+       }
+
+       if (wol->wolopts & WAKE_MAGIC) {
+               config |= MLX4_EN_WOL_DO_MODIFY | MLX4_EN_WOL_ENABLED |
+                               MLX4_EN_WOL_MAGIC;
+       } else {
+               config &= ~(MLX4_EN_WOL_ENABLED | MLX4_EN_WOL_MAGIC);
+               config |= MLX4_EN_WOL_DO_MODIFY;
+       }
+
+       err = mlx4_wol_write(priv->mdev->dev, config, priv->port);
+       if (err)
+               en_err(priv, "Failed to set WoL information\n");
+
+       return err;
 }
 
 static int mlx4_en_get_sset_count(struct net_device *dev, int sset)
@@ -442,6 +499,7 @@ const struct ethtool_ops mlx4_en_ethtool_ops = {
        .get_ethtool_stats = mlx4_en_get_ethtool_stats,
        .self_test = mlx4_en_self_test,
        .get_wol = mlx4_en_get_wol,
+       .set_wol = mlx4_en_set_wol,
        .get_msglevel = mlx4_en_get_msglevel,
        .set_msglevel = mlx4_en_set_msglevel,
        .get_coalesce = mlx4_en_get_coalesce,
index 5de1db897835965d2eafddbfb0ba307cee2f5f37..fd1c51b6197a077ad4b2ae9370d8f3a0777f8047 100644 (file)
@@ -276,6 +276,7 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)
        dev_cap->udp_rss = field & 0x1;
        MLX4_GET(field, outbox, QUERY_DEV_CAP_ETH_UC_LOOPBACK_OFFSET);
        dev_cap->loopback_support = field & 0x1;
+       dev_cap->wol = field & 0x40;
        MLX4_GET(dev_cap->flags, outbox, QUERY_DEV_CAP_FLAGS_OFFSET);
        MLX4_GET(field, outbox, QUERY_DEV_CAP_RSVD_UAR_OFFSET);
        dev_cap->reserved_uars = field >> 4;
@@ -908,3 +909,22 @@ int mlx4_NOP(struct mlx4_dev *dev)
        /* Input modifier of 0x1f means "finish as soon as possible." */
        return mlx4_cmd(dev, 0, 0x1f, 0, MLX4_CMD_NOP, 100);
 }
+
+#define MLX4_WOL_SETUP_MODE (5 << 28)
+int mlx4_wol_read(struct mlx4_dev *dev, u64 *config, int port)
+{
+       u32 in_mod = MLX4_WOL_SETUP_MODE | port << 8;
+
+       return mlx4_cmd_imm(dev, 0, config, in_mod, 0x3,
+                           MLX4_CMD_MOD_STAT_CFG, MLX4_CMD_TIME_CLASS_A);
+}
+EXPORT_SYMBOL_GPL(mlx4_wol_read);
+
+int mlx4_wol_write(struct mlx4_dev *dev, u64 config, int port)
+{
+       u32 in_mod = MLX4_WOL_SETUP_MODE | port << 8;
+
+       return mlx4_cmd(dev, config, in_mod, 0x1, MLX4_CMD_MOD_STAT_CFG,
+                                       MLX4_CMD_TIME_CLASS_A);
+}
+EXPORT_SYMBOL_GPL(mlx4_wol_write);
index 65cc72eb899d0491d53b844a0014d6106bee0c6c..f7b9cc2d1b2ac17403869919ed3e61e57c99f868 100644 (file)
@@ -80,6 +80,7 @@ struct mlx4_dev_cap {
        u16 stat_rate_support;
        int udp_rss;
        int loopback_support;
+       int wol;
        u32 flags;
        int reserved_uars;
        int uar_size;
index 517ca34f5b37229bcead8240a9c29d5f929c6bd6..42d4fb47787083cbcaf335357a00a9442db6f990 100644 (file)
@@ -227,6 +227,7 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)
        dev->caps.stat_rate_support  = dev_cap->stat_rate_support;
        dev->caps.udp_rss            = dev_cap->udp_rss;
        dev->caps.loopback_support   = dev_cap->loopback_support;
+       dev->caps.wol                = dev_cap->wol;
        dev->caps.max_gso_sz         = dev_cap->max_gso_sz;
 
        dev->caps.log_num_macs  = log_num_mac;
index 2db245fcd84b833abb42ee076beaff5a769f8156..07aea8d0beeb11e7a9b376bf07fdff38db8a9be9 100644 (file)
@@ -479,6 +479,13 @@ struct mlx4_en_priv {
        int mc_addrs_cnt;
        struct mlx4_en_stat_out_mbox hw_stats;
        int vids[128];
+       bool wol;
+};
+
+enum mlx4_en_wol {
+       MLX4_EN_WOL_MAGIC = (1ULL << 61),
+       MLX4_EN_WOL_ENABLED = (1ULL << 62),
+       MLX4_EN_WOL_DO_MODIFY = (1ULL << 63),
 };
 
 
index 78380823d82794305251f14e9cdeecfbc1d58f18..2460356d2c724b2de66c1f7114db342c4ca0b3ef 100644 (file)
@@ -251,6 +251,7 @@ struct mlx4_caps {
        u16                     stat_rate_support;
        int                     udp_rss;
        int                     loopback_support;
+       int                     wol;
        u8                      port_width_cap[MLX4_MAX_PORTS + 1];
        int                     max_gso_sz;
        int                     reserved_qps_cnt[MLX4_NUM_QP_REGION];
@@ -535,4 +536,7 @@ int mlx4_test_interrupts(struct mlx4_dev *dev);
 int mlx4_assign_eq(struct mlx4_dev *dev, char* name , int* vector);
 void mlx4_release_eq(struct mlx4_dev *dev, int vec);
 
+int mlx4_wol_read(struct mlx4_dev *dev, u64 *config, int port);
+int mlx4_wol_write(struct mlx4_dev *dev, u64 config, int port);
+
 #endif /* MLX4_DEVICE_H */