Commit 837c6378 authored by Nikita Danilov's avatar Nikita Danilov Committed by David S. Miller

net: atlantic: implement wake_phy feature

Wake on PHY allows to configure device to wakeup host
as soon as PHY link status is changed to active.
Signed-off-by: default avatarNikita Danilov <ndanilov@marvell.com>
Signed-off-by: default avatarIgor Russkikh <irusskikh@marvell.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent d993e14b
...@@ -78,6 +78,9 @@ ...@@ -78,6 +78,9 @@
#define AQ_CFG_FC_MODE AQ_NIC_FC_FULL #define AQ_CFG_FC_MODE AQ_NIC_FC_FULL
/* Default WOL modes used on initialization */
#define AQ_CFG_WOL_MODES WAKE_MAGIC
#define AQ_CFG_SPEED_MSK 0xFFFFU /* 0xFFFFU==auto_neg */ #define AQ_CFG_SPEED_MSK 0xFFFFU /* 0xFFFFU==auto_neg */
#define AQ_CFG_IS_AUTONEG_DEF 1U #define AQ_CFG_IS_AUTONEG_DEF 1U
......
...@@ -356,11 +356,8 @@ static void aq_ethtool_get_wol(struct net_device *ndev, ...@@ -356,11 +356,8 @@ static void aq_ethtool_get_wol(struct net_device *ndev,
struct aq_nic_s *aq_nic = netdev_priv(ndev); struct aq_nic_s *aq_nic = netdev_priv(ndev);
struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic); struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic);
wol->supported = WAKE_MAGIC; wol->supported = AQ_NIC_WOL_MODES;
wol->wolopts = 0; wol->wolopts = cfg->wol;
if (cfg->wol)
wol->wolopts |= WAKE_MAGIC;
} }
static int aq_ethtool_set_wol(struct net_device *ndev, static int aq_ethtool_set_wol(struct net_device *ndev,
...@@ -371,11 +368,12 @@ static int aq_ethtool_set_wol(struct net_device *ndev, ...@@ -371,11 +368,12 @@ static int aq_ethtool_set_wol(struct net_device *ndev,
struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic); struct aq_nic_cfg_s *cfg = aq_nic_get_cfg(aq_nic);
int err = 0; int err = 0;
if (wol->wolopts & WAKE_MAGIC) if (wol->wolopts & ~AQ_NIC_WOL_MODES)
cfg->wol |= AQ_NIC_WOL_ENABLED; return -EOPNOTSUPP;
else
cfg->wol &= ~AQ_NIC_WOL_ENABLED; cfg->wol = wol->wolopts;
err = device_set_wakeup_enable(&pdev->dev, wol->wolopts);
err = device_set_wakeup_enable(&pdev->dev, !!cfg->wol);
return err; return err;
} }
......
...@@ -74,7 +74,7 @@ static int aq_ndev_open(struct net_device *ndev) ...@@ -74,7 +74,7 @@ static int aq_ndev_open(struct net_device *ndev)
err_exit: err_exit:
if (err < 0) if (err < 0)
aq_nic_deinit(aq_nic); aq_nic_deinit(aq_nic, true);
return err; return err;
} }
...@@ -86,7 +86,7 @@ static int aq_ndev_close(struct net_device *ndev) ...@@ -86,7 +86,7 @@ static int aq_ndev_close(struct net_device *ndev)
err = aq_nic_stop(aq_nic); err = aq_nic_stop(aq_nic);
if (err < 0) if (err < 0)
goto err_exit; goto err_exit;
aq_nic_deinit(aq_nic); aq_nic_deinit(aq_nic, true);
err_exit: err_exit:
return err; return err;
......
...@@ -79,6 +79,7 @@ void aq_nic_cfg_start(struct aq_nic_s *self) ...@@ -79,6 +79,7 @@ void aq_nic_cfg_start(struct aq_nic_s *self)
cfg->num_rss_queues = AQ_CFG_NUM_RSS_QUEUES_DEF; cfg->num_rss_queues = AQ_CFG_NUM_RSS_QUEUES_DEF;
cfg->aq_rss.base_cpu_number = AQ_CFG_RSS_BASE_CPU_NUM_DEF; cfg->aq_rss.base_cpu_number = AQ_CFG_RSS_BASE_CPU_NUM_DEF;
cfg->flow_control = AQ_CFG_FC_MODE; cfg->flow_control = AQ_CFG_FC_MODE;
cfg->wol = AQ_CFG_WOL_MODES;
cfg->mtu = AQ_CFG_MTU_DEF; cfg->mtu = AQ_CFG_MTU_DEF;
cfg->link_speed_msk = AQ_CFG_SPEED_MSK; cfg->link_speed_msk = AQ_CFG_SPEED_MSK;
...@@ -1000,7 +1001,20 @@ int aq_nic_stop(struct aq_nic_s *self) ...@@ -1000,7 +1001,20 @@ int aq_nic_stop(struct aq_nic_s *self)
return self->aq_hw_ops->hw_stop(self->aq_hw); return self->aq_hw_ops->hw_stop(self->aq_hw);
} }
void aq_nic_deinit(struct aq_nic_s *self) void aq_nic_set_power(struct aq_nic_s *self)
{
if (self->power_state != AQ_HW_POWER_STATE_D0 ||
self->aq_hw->aq_nic_cfg->wol)
if (likely(self->aq_fw_ops->set_power)) {
mutex_lock(&self->fwreq_mutex);
self->aq_fw_ops->set_power(self->aq_hw,
self->power_state,
self->ndev->dev_addr);
mutex_unlock(&self->fwreq_mutex);
}
}
void aq_nic_deinit(struct aq_nic_s *self, bool link_down)
{ {
struct aq_vec_s *aq_vec = NULL; struct aq_vec_s *aq_vec = NULL;
unsigned int i = 0U; unsigned int i = 0U;
...@@ -1017,23 +1031,12 @@ void aq_nic_deinit(struct aq_nic_s *self) ...@@ -1017,23 +1031,12 @@ void aq_nic_deinit(struct aq_nic_s *self)
aq_ptp_ring_free(self); aq_ptp_ring_free(self);
aq_ptp_free(self); aq_ptp_free(self);
if (likely(self->aq_fw_ops->deinit)) { if (likely(self->aq_fw_ops->deinit) && link_down) {
mutex_lock(&self->fwreq_mutex); mutex_lock(&self->fwreq_mutex);
self->aq_fw_ops->deinit(self->aq_hw); self->aq_fw_ops->deinit(self->aq_hw);
mutex_unlock(&self->fwreq_mutex); mutex_unlock(&self->fwreq_mutex);
} }
if (self->power_state != AQ_HW_POWER_STATE_D0 ||
self->aq_hw->aq_nic_cfg->wol)
if (likely(self->aq_fw_ops->set_power)) {
mutex_lock(&self->fwreq_mutex);
self->aq_fw_ops->set_power(self->aq_hw,
self->power_state,
self->ndev->dev_addr);
mutex_unlock(&self->fwreq_mutex);
}
err_exit:; err_exit:;
} }
...@@ -1072,7 +1075,7 @@ int aq_nic_change_pm_state(struct aq_nic_s *self, pm_message_t *pm_msg) ...@@ -1072,7 +1075,7 @@ int aq_nic_change_pm_state(struct aq_nic_s *self, pm_message_t *pm_msg)
if (err < 0) if (err < 0)
goto err_exit; goto err_exit;
aq_nic_deinit(self); aq_nic_deinit(self, !self->aq_hw->aq_nic_cfg->wol);
} else { } else {
err = aq_nic_init(self); err = aq_nic_init(self);
if (err < 0) if (err < 0)
...@@ -1108,7 +1111,8 @@ void aq_nic_shutdown(struct aq_nic_s *self) ...@@ -1108,7 +1111,8 @@ void aq_nic_shutdown(struct aq_nic_s *self)
if (err < 0) if (err < 0)
goto err_exit; goto err_exit;
} }
aq_nic_deinit(self); aq_nic_deinit(self, !self->aq_hw->aq_nic_cfg->wol);
aq_nic_set_power(self);
err_exit: err_exit:
rtnl_unlock(); rtnl_unlock();
......
...@@ -60,7 +60,8 @@ struct aq_nic_cfg_s { ...@@ -60,7 +60,8 @@ struct aq_nic_cfg_s {
#define AQ_NIC_FLAG_ERR_UNPLUG 0x40000000U #define AQ_NIC_FLAG_ERR_UNPLUG 0x40000000U
#define AQ_NIC_FLAG_ERR_HW 0x80000000U #define AQ_NIC_FLAG_ERR_HW 0x80000000U
#define AQ_NIC_WOL_ENABLED BIT(0) #define AQ_NIC_WOL_MODES (WAKE_MAGIC |\
WAKE_PHY)
#define AQ_NIC_TCVEC2RING(_NIC_, _TC_, _VEC_) \ #define AQ_NIC_TCVEC2RING(_NIC_, _TC_, _VEC_) \
((_TC_) * AQ_CFG_TCS_MAX + (_VEC_)) ((_TC_) * AQ_CFG_TCS_MAX + (_VEC_))
...@@ -141,7 +142,8 @@ int aq_nic_get_regs(struct aq_nic_s *self, struct ethtool_regs *regs, void *p); ...@@ -141,7 +142,8 @@ int aq_nic_get_regs(struct aq_nic_s *self, struct ethtool_regs *regs, void *p);
int aq_nic_get_regs_count(struct aq_nic_s *self); int aq_nic_get_regs_count(struct aq_nic_s *self);
void aq_nic_get_stats(struct aq_nic_s *self, u64 *data); void aq_nic_get_stats(struct aq_nic_s *self, u64 *data);
int aq_nic_stop(struct aq_nic_s *self); int aq_nic_stop(struct aq_nic_s *self);
void aq_nic_deinit(struct aq_nic_s *self); void aq_nic_deinit(struct aq_nic_s *self, bool link_down);
void aq_nic_set_power(struct aq_nic_s *self);
void aq_nic_free_hot_resources(struct aq_nic_s *self); void aq_nic_free_hot_resources(struct aq_nic_s *self);
void aq_nic_free_vectors(struct aq_nic_s *self); void aq_nic_free_vectors(struct aq_nic_s *self);
int aq_nic_set_mtu(struct aq_nic_s *self, int new_mtu); int aq_nic_set_mtu(struct aq_nic_s *self, int new_mtu);
......
...@@ -845,7 +845,8 @@ int hw_atl_utils_get_fw_version(struct aq_hw_s *self, u32 *fw_version) ...@@ -845,7 +845,8 @@ int hw_atl_utils_get_fw_version(struct aq_hw_s *self, u32 *fw_version)
return 0; return 0;
} }
static int aq_fw1x_set_wol(struct aq_hw_s *self, bool wol_enabled, u8 *mac) static int aq_fw1x_set_wake_magic(struct aq_hw_s *self, bool wol_enabled,
u8 *mac)
{ {
struct hw_atl_utils_fw_rpc *prpc = NULL; struct hw_atl_utils_fw_rpc *prpc = NULL;
unsigned int rpc_size = 0U; unsigned int rpc_size = 0U;
...@@ -894,8 +895,8 @@ static int aq_fw1x_set_power(struct aq_hw_s *self, unsigned int power_state, ...@@ -894,8 +895,8 @@ static int aq_fw1x_set_power(struct aq_hw_s *self, unsigned int power_state,
unsigned int rpc_size = 0U; unsigned int rpc_size = 0U;
int err = 0; int err = 0;
if (self->aq_nic_cfg->wol & AQ_NIC_WOL_ENABLED) { if (self->aq_nic_cfg->wol & WAKE_MAGIC) {
err = aq_fw1x_set_wol(self, 1, mac); err = aq_fw1x_set_wake_magic(self, 1, mac);
if (err < 0) if (err < 0)
goto err_exit; goto err_exit;
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#define HW_ATL_FW2X_CAP_SLEEP_PROXY BIT(CAPS_HI_SLEEP_PROXY) #define HW_ATL_FW2X_CAP_SLEEP_PROXY BIT(CAPS_HI_SLEEP_PROXY)
#define HW_ATL_FW2X_CAP_WOL BIT(CAPS_HI_WOL) #define HW_ATL_FW2X_CAP_WOL BIT(CAPS_HI_WOL)
#define HW_ATL_FW2X_CTRL_WAKE_ON_LINK BIT(CTRL_WAKE_ON_LINK)
#define HW_ATL_FW2X_CTRL_SLEEP_PROXY BIT(CTRL_SLEEP_PROXY) #define HW_ATL_FW2X_CTRL_SLEEP_PROXY BIT(CTRL_SLEEP_PROXY)
#define HW_ATL_FW2X_CTRL_WOL BIT(CTRL_WOL) #define HW_ATL_FW2X_CTRL_WOL BIT(CTRL_WOL)
#define HW_ATL_FW2X_CTRL_LINK_DROP BIT(CTRL_LINK_DROP) #define HW_ATL_FW2X_CTRL_LINK_DROP BIT(CTRL_LINK_DROP)
...@@ -345,87 +346,46 @@ static int aq_fw2x_get_phy_temp(struct aq_hw_s *self, int *temp) ...@@ -345,87 +346,46 @@ static int aq_fw2x_get_phy_temp(struct aq_hw_s *self, int *temp)
return 0; return 0;
} }
static int aq_fw2x_set_sleep_proxy(struct aq_hw_s *self, u8 *mac) static int aq_fw2x_set_wol(struct aq_hw_s *self, u8 *mac)
{ {
struct hw_atl_utils_fw_rpc *rpc = NULL; struct hw_atl_utils_fw_rpc *rpc = NULL;
struct offload_info *cfg = NULL; struct offload_info *info = NULL;
unsigned int rpc_size = 0U; u32 wol_bits = 0;
u32 mpi_opts; u32 rpc_size;
int err = 0; int err = 0;
u32 val; u32 val;
rpc_size = sizeof(rpc->msg_id) + sizeof(*cfg); if (self->aq_nic_cfg->wol & WAKE_PHY) {
aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR,
err = hw_atl_utils_fw_rpc_wait(self, &rpc); HW_ATL_FW2X_CTRL_LINK_DROP);
if (err < 0) readx_poll_timeout_atomic(aq_fw2x_state2_get, self, val,
goto err_exit; (val &
HW_ATL_FW2X_CTRL_LINK_DROP) != 0,
memset(rpc, 0, rpc_size); 1000, 100000);
cfg = (struct offload_info *)(&rpc->msg_id + 1); wol_bits |= HW_ATL_FW2X_CTRL_WAKE_ON_LINK;
}
memcpy(cfg->mac_addr, mac, ETH_ALEN);
cfg->len = sizeof(*cfg);
/* Clear bit 0x36C.23 and 0x36C.22 */
mpi_opts = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR);
mpi_opts &= ~HW_ATL_FW2X_CTRL_SLEEP_PROXY;
mpi_opts &= ~HW_ATL_FW2X_CTRL_LINK_DROP;
aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts);
err = hw_atl_utils_fw_rpc_call(self, rpc_size);
if (err < 0)
goto err_exit;
/* Set bit 0x36C.23 */
mpi_opts |= HW_ATL_FW2X_CTRL_SLEEP_PROXY;
aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts);
err = readx_poll_timeout_atomic(aq_fw2x_state2_get,
self, val,
val & HW_ATL_FW2X_CTRL_SLEEP_PROXY,
1U, 100000U);
err_exit:
return err;
}
static int aq_fw2x_set_wol_params(struct aq_hw_s *self, u8 *mac)
{
struct hw_atl_utils_fw_rpc *rpc = NULL;
struct fw2x_msg_wol *msg = NULL;
u32 mpi_opts;
int err = 0;
u32 val;
err = hw_atl_utils_fw_rpc_wait(self, &rpc);
if (err < 0)
goto err_exit;
msg = (struct fw2x_msg_wol *)rpc;
memset(msg, 0, sizeof(*msg));
msg->msg_id = HAL_ATLANTIC_UTILS_FW2X_MSG_WOL; if (self->aq_nic_cfg->wol & WAKE_MAGIC) {
msg->magic_packet_enabled = true; wol_bits |= HW_ATL_FW2X_CTRL_SLEEP_PROXY |
memcpy(msg->hw_addr, mac, ETH_ALEN); HW_ATL_FW2X_CTRL_WOL;
mpi_opts = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR); err = hw_atl_utils_fw_rpc_wait(self, &rpc);
mpi_opts &= ~(HW_ATL_FW2X_CTRL_SLEEP_PROXY | HW_ATL_FW2X_CTRL_WOL); if (err < 0)
goto err_exit;
aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts); rpc_size = sizeof(*info) +
offsetof(struct hw_atl_utils_fw_rpc, fw2x_offloads);
memset(rpc, 0, rpc_size);
info = &rpc->fw2x_offloads;
memcpy(info->mac_addr, mac, ETH_ALEN);
info->len = sizeof(*info);
err = hw_atl_utils_fw_rpc_call(self, sizeof(*msg)); err = hw_atl_utils_fw_rpc_call(self, rpc_size);
if (err < 0) if (err < 0)
goto err_exit; goto err_exit;
}
/* Set bit 0x36C.24 */ aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, wol_bits);
mpi_opts |= HW_ATL_FW2X_CTRL_WOL;
aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts);
err = readx_poll_timeout_atomic(aq_fw2x_state2_get,
self, val, val & HW_ATL_FW2X_CTRL_WOL,
1U, 10000U);
err_exit: err_exit:
return err; return err;
...@@ -436,14 +396,9 @@ static int aq_fw2x_set_power(struct aq_hw_s *self, unsigned int power_state, ...@@ -436,14 +396,9 @@ static int aq_fw2x_set_power(struct aq_hw_s *self, unsigned int power_state,
{ {
int err = 0; int err = 0;
if (self->aq_nic_cfg->wol & AQ_NIC_WOL_ENABLED) { if (self->aq_nic_cfg->wol)
err = aq_fw2x_set_sleep_proxy(self, mac); err = aq_fw2x_set_wol(self, mac);
if (err < 0)
goto err_exit;
err = aq_fw2x_set_wol_params(self, mac);
}
err_exit:
return err; return err;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment