Skip to content

Commit

Permalink
net: atlantic: implement wake_phy feature
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Nikita Danilov authored and David S. Miller committed Nov 8, 2019
1 parent d993e14 commit 837c637
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 110 deletions.
3 changes: 3 additions & 0 deletions drivers/net/ethernet/aquantia/atlantic/aq_cfg.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 8 additions & 10 deletions drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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;
}
Expand Down
4 changes: 2 additions & 2 deletions drivers/net/ethernet/aquantia/atlantic/aq_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand All @@ -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;
Expand Down
34 changes: 19 additions & 15 deletions drivers/net/ethernet/aquantia/atlantic/aq_nic.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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:;
}

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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();
Expand Down
6 changes: 4 additions & 2 deletions drivers/net/ethernet/aquantia/atlantic/aq_nic.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_))
Expand Down Expand Up @@ -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);
Expand Down
7 changes: 4 additions & 3 deletions drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
111 changes: 33 additions & 78 deletions drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils_fw2x.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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);

err = hw_atl_utils_fw_rpc_wait(self, &rpc);
if (err < 0)
goto err_exit;

memset(rpc, 0, rpc_size);
cfg = (struct offload_info *)(&rpc->msg_id + 1);

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));
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;
}

msg->msg_id = HAL_ATLANTIC_UTILS_FW2X_MSG_WOL;
msg->magic_packet_enabled = true;
memcpy(msg->hw_addr, mac, ETH_ALEN);
if (self->aq_nic_cfg->wol & WAKE_MAGIC) {
wol_bits |= HW_ATL_FW2X_CTRL_SLEEP_PROXY |
HW_ATL_FW2X_CTRL_WOL;

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);
err = hw_atl_utils_fw_rpc_wait(self, &rpc);
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));
if (err < 0)
goto err_exit;
err = hw_atl_utils_fw_rpc_call(self, rpc_size);
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;
Expand All @@ -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;
}

Expand Down

0 comments on commit 837c637

Please sign in to comment.