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: Nikita Danilov <ndanilov@marvell.com>
Signed-off-by: Igor Russkikh <irusskikh@marvell.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Nikita Danilov 2019-11-07 22:41:50 +00:00 committed by David S. Miller
parent d993e14bd8
commit 837c637869
7 changed files with 73 additions and 110 deletions

View File

@ -78,6 +78,9 @@
#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_IS_AUTONEG_DEF 1U

View File

@ -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_cfg_s *cfg = aq_nic_get_cfg(aq_nic);
wol->supported = WAKE_MAGIC;
wol->wolopts = 0;
if (cfg->wol)
wol->wolopts |= WAKE_MAGIC;
wol->supported = AQ_NIC_WOL_MODES;
wol->wolopts = cfg->wol;
}
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);
int err = 0;
if (wol->wolopts & WAKE_MAGIC)
cfg->wol |= AQ_NIC_WOL_ENABLED;
else
cfg->wol &= ~AQ_NIC_WOL_ENABLED;
err = device_set_wakeup_enable(&pdev->dev, wol->wolopts);
if (wol->wolopts & ~AQ_NIC_WOL_MODES)
return -EOPNOTSUPP;
cfg->wol = wol->wolopts;
err = device_set_wakeup_enable(&pdev->dev, !!cfg->wol);
return err;
}

View File

@ -74,7 +74,7 @@ static int aq_ndev_open(struct net_device *ndev)
err_exit:
if (err < 0)
aq_nic_deinit(aq_nic);
aq_nic_deinit(aq_nic, true);
return err;
}
@ -86,7 +86,7 @@ static int aq_ndev_close(struct net_device *ndev)
err = aq_nic_stop(aq_nic);
if (err < 0)
goto err_exit;
aq_nic_deinit(aq_nic);
aq_nic_deinit(aq_nic, true);
err_exit:
return err;

View File

@ -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->aq_rss.base_cpu_number = AQ_CFG_RSS_BASE_CPU_NUM_DEF;
cfg->flow_control = AQ_CFG_FC_MODE;
cfg->wol = AQ_CFG_WOL_MODES;
cfg->mtu = AQ_CFG_MTU_DEF;
cfg->link_speed_msk = AQ_CFG_SPEED_MSK;
@ -1000,7 +1001,20 @@ int aq_nic_stop(struct aq_nic_s *self)
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;
unsigned int i = 0U;
@ -1017,23 +1031,12 @@ void aq_nic_deinit(struct aq_nic_s *self)
aq_ptp_ring_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);
self->aq_fw_ops->deinit(self->aq_hw);
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:;
}
@ -1072,7 +1075,7 @@ int aq_nic_change_pm_state(struct aq_nic_s *self, pm_message_t *pm_msg)
if (err < 0)
goto err_exit;
aq_nic_deinit(self);
aq_nic_deinit(self, !self->aq_hw->aq_nic_cfg->wol);
} else {
err = aq_nic_init(self);
if (err < 0)
@ -1108,7 +1111,8 @@ void aq_nic_shutdown(struct aq_nic_s *self)
if (err < 0)
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:
rtnl_unlock();

View File

@ -60,7 +60,8 @@ struct aq_nic_cfg_s {
#define AQ_NIC_FLAG_ERR_UNPLUG 0x40000000U
#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_) \
((_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);
int aq_nic_get_regs_count(struct aq_nic_s *self);
void aq_nic_get_stats(struct aq_nic_s *self, u64 *data);
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_vectors(struct aq_nic_s *self);
int aq_nic_set_mtu(struct aq_nic_s *self, int new_mtu);

View File

@ -845,7 +845,8 @@ int hw_atl_utils_get_fw_version(struct aq_hw_s *self, u32 *fw_version)
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;
unsigned int rpc_size = 0U;
@ -894,8 +895,8 @@ static int aq_fw1x_set_power(struct aq_hw_s *self, unsigned int power_state,
unsigned int rpc_size = 0U;
int err = 0;
if (self->aq_nic_cfg->wol & AQ_NIC_WOL_ENABLED) {
err = aq_fw1x_set_wol(self, 1, mac);
if (self->aq_nic_cfg->wol & WAKE_MAGIC) {
err = aq_fw1x_set_wake_magic(self, 1, mac);
if (err < 0)
goto err_exit;

View File

@ -34,6 +34,7 @@
#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_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_WOL BIT(CTRL_WOL)
#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)
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 offload_info *cfg = NULL;
unsigned int rpc_size = 0U;
u32 mpi_opts;
struct offload_info *info = NULL;
u32 wol_bits = 0;
u32 rpc_size;
int err = 0;
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,
HW_ATL_FW2X_CTRL_LINK_DROP);
readx_poll_timeout_atomic(aq_fw2x_state2_get, self, val,
(val &
HW_ATL_FW2X_CTRL_LINK_DROP) != 0,
1000, 100000);
wol_bits |= HW_ATL_FW2X_CTRL_WAKE_ON_LINK;
}
err = hw_atl_utils_fw_rpc_wait(self, &rpc);
if (err < 0)
goto err_exit;
if (self->aq_nic_cfg->wol & WAKE_MAGIC) {
wol_bits |= HW_ATL_FW2X_CTRL_SLEEP_PROXY |
HW_ATL_FW2X_CTRL_WOL;
memset(rpc, 0, rpc_size);
cfg = (struct offload_info *)(&rpc->msg_id + 1);
err = hw_atl_utils_fw_rpc_wait(self, &rpc);
if (err < 0)
goto err_exit;
memcpy(cfg->mac_addr, mac, ETH_ALEN);
cfg->len = sizeof(*cfg);
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);
/* 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;
err = hw_atl_utils_fw_rpc_call(self, rpc_size);
if (err < 0)
goto err_exit;
}
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;
msg->magic_packet_enabled = true;
memcpy(msg->hw_addr, mac, ETH_ALEN);
mpi_opts = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR);
mpi_opts &= ~(HW_ATL_FW2X_CTRL_SLEEP_PROXY | HW_ATL_FW2X_CTRL_WOL);
aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts);
err = hw_atl_utils_fw_rpc_call(self, sizeof(*msg));
if (err < 0)
goto err_exit;
/* Set bit 0x36C.24 */
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);
aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, wol_bits);
err_exit:
return err;
@ -436,14 +396,9 @@ static int aq_fw2x_set_power(struct aq_hw_s *self, unsigned int power_state,
{
int err = 0;
if (self->aq_nic_cfg->wol & AQ_NIC_WOL_ENABLED) {
err = aq_fw2x_set_sleep_proxy(self, mac);
if (err < 0)
goto err_exit;
err = aq_fw2x_set_wol_params(self, mac);
}
if (self->aq_nic_cfg->wol)
err = aq_fw2x_set_wol(self, mac);
err_exit:
return err;
}