diff options
| author | Yevgeny Petrilin <yevgenyp@mellanox.co.il> | 2011-03-22 18:37:59 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2011-03-23 15:24:19 -0400 |
| commit | 14c07b1358ede1664652bb9b28d9ace5fe6f7f92 (patch) | |
| tree | 05fa72b9680e85923d938edd8e4963bbac0b629b | |
| parent | 1fb9876e9bf895ea4127ff17180f1b2ab37771b6 (diff) | |
mlx4: Wake on LAN support
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>
| -rw-r--r-- | drivers/net/mlx4/en_ethtool.c | 62 | ||||
| -rw-r--r-- | drivers/net/mlx4/fw.c | 20 | ||||
| -rw-r--r-- | drivers/net/mlx4/fw.h | 1 | ||||
| -rw-r--r-- | drivers/net/mlx4/main.c | 1 | ||||
| -rw-r--r-- | drivers/net/mlx4/mlx4_en.h | 7 | ||||
| -rw-r--r-- | include/linux/mlx4/device.h | 4 |
6 files changed, 93 insertions, 2 deletions
diff --git a/drivers/net/mlx4/en_ethtool.c b/drivers/net/mlx4/en_ethtool.c index 8cfe8586ed2d..c1f351f6ae57 100644 --- a/drivers/net/mlx4/en_ethtool.c +++ b/drivers/net/mlx4/en_ethtool.c | |||
| @@ -131,8 +131,65 @@ static void mlx4_en_set_msglevel(struct net_device *dev, u32 val) | |||
| 131 | static void mlx4_en_get_wol(struct net_device *netdev, | 131 | static void mlx4_en_get_wol(struct net_device *netdev, |
| 132 | struct ethtool_wolinfo *wol) | 132 | struct ethtool_wolinfo *wol) |
| 133 | { | 133 | { |
| 134 | wol->supported = 0; | 134 | struct mlx4_en_priv *priv = netdev_priv(netdev); |
| 135 | wol->wolopts = 0; | 135 | int err = 0; |
| 136 | u64 config = 0; | ||
| 137 | |||
| 138 | if (!priv->mdev->dev->caps.wol) { | ||
| 139 | wol->supported = 0; | ||
| 140 | wol->wolopts = 0; | ||
| 141 | return; | ||
| 142 | } | ||
| 143 | |||
| 144 | err = mlx4_wol_read(priv->mdev->dev, &config, priv->port); | ||
| 145 | if (err) { | ||
| 146 | en_err(priv, "Failed to get WoL information\n"); | ||
| 147 | return; | ||
| 148 | } | ||
| 149 | |||
| 150 | if (config & MLX4_EN_WOL_MAGIC) | ||
| 151 | wol->supported = WAKE_MAGIC; | ||
| 152 | else | ||
| 153 | wol->supported = 0; | ||
| 154 | |||
| 155 | if (config & MLX4_EN_WOL_ENABLED) | ||
| 156 | wol->wolopts = WAKE_MAGIC; | ||
| 157 | else | ||
| 158 | wol->wolopts = 0; | ||
| 159 | } | ||
| 160 | |||
| 161 | static int mlx4_en_set_wol(struct net_device *netdev, | ||
| 162 | struct ethtool_wolinfo *wol) | ||
| 163 | { | ||
| 164 | struct mlx4_en_priv *priv = netdev_priv(netdev); | ||
| 165 | u64 config = 0; | ||
| 166 | int err = 0; | ||
| 167 | |||
| 168 | if (!priv->mdev->dev->caps.wol) | ||
| 169 | return -EOPNOTSUPP; | ||
| 170 | |||
| 171 | if (wol->supported & ~WAKE_MAGIC) | ||
| 172 | return -EINVAL; | ||
| 173 | |||
| 174 | err = mlx4_wol_read(priv->mdev->dev, &config, priv->port); | ||
| 175 | if (err) { | ||
| 176 | en_err(priv, "Failed to get WoL info, unable to modify\n"); | ||
| 177 | return err; | ||
| 178 | } | ||
| 179 | |||
| 180 | if (wol->wolopts & WAKE_MAGIC) { | ||
| 181 | config |= MLX4_EN_WOL_DO_MODIFY | MLX4_EN_WOL_ENABLED | | ||
| 182 | MLX4_EN_WOL_MAGIC; | ||
| 183 | } else { | ||
| 184 | config &= ~(MLX4_EN_WOL_ENABLED | MLX4_EN_WOL_MAGIC); | ||
| 185 | config |= MLX4_EN_WOL_DO_MODIFY; | ||
| 186 | } | ||
| 187 | |||
| 188 | err = mlx4_wol_write(priv->mdev->dev, config, priv->port); | ||
| 189 | if (err) | ||
| 190 | en_err(priv, "Failed to set WoL information\n"); | ||
| 191 | |||
| 192 | return err; | ||
| 136 | } | 193 | } |
| 137 | 194 | ||
| 138 | static int mlx4_en_get_sset_count(struct net_device *dev, int sset) | 195 | 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 = { | |||
| 442 | .get_ethtool_stats = mlx4_en_get_ethtool_stats, | 499 | .get_ethtool_stats = mlx4_en_get_ethtool_stats, |
| 443 | .self_test = mlx4_en_self_test, | 500 | .self_test = mlx4_en_self_test, |
| 444 | .get_wol = mlx4_en_get_wol, | 501 | .get_wol = mlx4_en_get_wol, |
| 502 | .set_wol = mlx4_en_set_wol, | ||
| 445 | .get_msglevel = mlx4_en_get_msglevel, | 503 | .get_msglevel = mlx4_en_get_msglevel, |
| 446 | .set_msglevel = mlx4_en_set_msglevel, | 504 | .set_msglevel = mlx4_en_set_msglevel, |
| 447 | .get_coalesce = mlx4_en_get_coalesce, | 505 | .get_coalesce = mlx4_en_get_coalesce, |
diff --git a/drivers/net/mlx4/fw.c b/drivers/net/mlx4/fw.c index 5de1db897835..fd1c51b6197a 100644 --- a/drivers/net/mlx4/fw.c +++ b/drivers/net/mlx4/fw.c | |||
| @@ -276,6 +276,7 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) | |||
| 276 | dev_cap->udp_rss = field & 0x1; | 276 | dev_cap->udp_rss = field & 0x1; |
| 277 | MLX4_GET(field, outbox, QUERY_DEV_CAP_ETH_UC_LOOPBACK_OFFSET); | 277 | MLX4_GET(field, outbox, QUERY_DEV_CAP_ETH_UC_LOOPBACK_OFFSET); |
| 278 | dev_cap->loopback_support = field & 0x1; | 278 | dev_cap->loopback_support = field & 0x1; |
| 279 | dev_cap->wol = field & 0x40; | ||
| 279 | MLX4_GET(dev_cap->flags, outbox, QUERY_DEV_CAP_FLAGS_OFFSET); | 280 | MLX4_GET(dev_cap->flags, outbox, QUERY_DEV_CAP_FLAGS_OFFSET); |
| 280 | MLX4_GET(field, outbox, QUERY_DEV_CAP_RSVD_UAR_OFFSET); | 281 | MLX4_GET(field, outbox, QUERY_DEV_CAP_RSVD_UAR_OFFSET); |
| 281 | dev_cap->reserved_uars = field >> 4; | 282 | dev_cap->reserved_uars = field >> 4; |
| @@ -908,3 +909,22 @@ int mlx4_NOP(struct mlx4_dev *dev) | |||
| 908 | /* Input modifier of 0x1f means "finish as soon as possible." */ | 909 | /* Input modifier of 0x1f means "finish as soon as possible." */ |
| 909 | return mlx4_cmd(dev, 0, 0x1f, 0, MLX4_CMD_NOP, 100); | 910 | return mlx4_cmd(dev, 0, 0x1f, 0, MLX4_CMD_NOP, 100); |
| 910 | } | 911 | } |
| 912 | |||
| 913 | #define MLX4_WOL_SETUP_MODE (5 << 28) | ||
| 914 | int mlx4_wol_read(struct mlx4_dev *dev, u64 *config, int port) | ||
| 915 | { | ||
| 916 | u32 in_mod = MLX4_WOL_SETUP_MODE | port << 8; | ||
| 917 | |||
| 918 | return mlx4_cmd_imm(dev, 0, config, in_mod, 0x3, | ||
| 919 | MLX4_CMD_MOD_STAT_CFG, MLX4_CMD_TIME_CLASS_A); | ||
| 920 | } | ||
| 921 | EXPORT_SYMBOL_GPL(mlx4_wol_read); | ||
| 922 | |||
| 923 | int mlx4_wol_write(struct mlx4_dev *dev, u64 config, int port) | ||
| 924 | { | ||
| 925 | u32 in_mod = MLX4_WOL_SETUP_MODE | port << 8; | ||
| 926 | |||
| 927 | return mlx4_cmd(dev, config, in_mod, 0x1, MLX4_CMD_MOD_STAT_CFG, | ||
| 928 | MLX4_CMD_TIME_CLASS_A); | ||
| 929 | } | ||
| 930 | EXPORT_SYMBOL_GPL(mlx4_wol_write); | ||
diff --git a/drivers/net/mlx4/fw.h b/drivers/net/mlx4/fw.h index 65cc72eb899d..f7b9cc2d1b2a 100644 --- a/drivers/net/mlx4/fw.h +++ b/drivers/net/mlx4/fw.h | |||
| @@ -80,6 +80,7 @@ struct mlx4_dev_cap { | |||
| 80 | u16 stat_rate_support; | 80 | u16 stat_rate_support; |
| 81 | int udp_rss; | 81 | int udp_rss; |
| 82 | int loopback_support; | 82 | int loopback_support; |
| 83 | int wol; | ||
| 83 | u32 flags; | 84 | u32 flags; |
| 84 | int reserved_uars; | 85 | int reserved_uars; |
| 85 | int uar_size; | 86 | int uar_size; |
diff --git a/drivers/net/mlx4/main.c b/drivers/net/mlx4/main.c index 517ca34f5b37..42d4fb477870 100644 --- a/drivers/net/mlx4/main.c +++ b/drivers/net/mlx4/main.c | |||
| @@ -227,6 +227,7 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) | |||
| 227 | dev->caps.stat_rate_support = dev_cap->stat_rate_support; | 227 | dev->caps.stat_rate_support = dev_cap->stat_rate_support; |
| 228 | dev->caps.udp_rss = dev_cap->udp_rss; | 228 | dev->caps.udp_rss = dev_cap->udp_rss; |
| 229 | dev->caps.loopback_support = dev_cap->loopback_support; | 229 | dev->caps.loopback_support = dev_cap->loopback_support; |
| 230 | dev->caps.wol = dev_cap->wol; | ||
| 230 | dev->caps.max_gso_sz = dev_cap->max_gso_sz; | 231 | dev->caps.max_gso_sz = dev_cap->max_gso_sz; |
| 231 | 232 | ||
| 232 | dev->caps.log_num_macs = log_num_mac; | 233 | dev->caps.log_num_macs = log_num_mac; |
diff --git a/drivers/net/mlx4/mlx4_en.h b/drivers/net/mlx4/mlx4_en.h index 2db245fcd84b..07aea8d0beeb 100644 --- a/drivers/net/mlx4/mlx4_en.h +++ b/drivers/net/mlx4/mlx4_en.h | |||
| @@ -479,6 +479,13 @@ struct mlx4_en_priv { | |||
| 479 | int mc_addrs_cnt; | 479 | int mc_addrs_cnt; |
| 480 | struct mlx4_en_stat_out_mbox hw_stats; | 480 | struct mlx4_en_stat_out_mbox hw_stats; |
| 481 | int vids[128]; | 481 | int vids[128]; |
| 482 | bool wol; | ||
| 483 | }; | ||
| 484 | |||
| 485 | enum mlx4_en_wol { | ||
| 486 | MLX4_EN_WOL_MAGIC = (1ULL << 61), | ||
| 487 | MLX4_EN_WOL_ENABLED = (1ULL << 62), | ||
| 488 | MLX4_EN_WOL_DO_MODIFY = (1ULL << 63), | ||
| 482 | }; | 489 | }; |
| 483 | 490 | ||
| 484 | 491 | ||
diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index 78380823d827..2460356d2c72 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h | |||
| @@ -251,6 +251,7 @@ struct mlx4_caps { | |||
| 251 | u16 stat_rate_support; | 251 | u16 stat_rate_support; |
| 252 | int udp_rss; | 252 | int udp_rss; |
| 253 | int loopback_support; | 253 | int loopback_support; |
| 254 | int wol; | ||
| 254 | u8 port_width_cap[MLX4_MAX_PORTS + 1]; | 255 | u8 port_width_cap[MLX4_MAX_PORTS + 1]; |
| 255 | int max_gso_sz; | 256 | int max_gso_sz; |
| 256 | int reserved_qps_cnt[MLX4_NUM_QP_REGION]; | 257 | int reserved_qps_cnt[MLX4_NUM_QP_REGION]; |
| @@ -535,4 +536,7 @@ int mlx4_test_interrupts(struct mlx4_dev *dev); | |||
| 535 | int mlx4_assign_eq(struct mlx4_dev *dev, char* name , int* vector); | 536 | int mlx4_assign_eq(struct mlx4_dev *dev, char* name , int* vector); |
| 536 | void mlx4_release_eq(struct mlx4_dev *dev, int vec); | 537 | void mlx4_release_eq(struct mlx4_dev *dev, int vec); |
| 537 | 538 | ||
| 539 | int mlx4_wol_read(struct mlx4_dev *dev, u64 *config, int port); | ||
| 540 | int mlx4_wol_write(struct mlx4_dev *dev, u64 config, int port); | ||
| 541 | |||
| 538 | #endif /* MLX4_DEVICE_H */ | 542 | #endif /* MLX4_DEVICE_H */ |
