net: aquantia: implement WOL support

Add WOL support. Currently only magic packet
(ethtool -s <ethX> wol g) feature is implemented.

Remove hw_set_power and move that to FW_OPS set_power:
because WOL configuration behaves differently on 1x and 2x
firmwares

Signed-off-by: Yana Esina <yana.esina@aquantia.com>
Signed-off-by: Nikita Danilov <nikita.danilov@aquantia.com>
Tested-by: Nikita Danilov <nikita.danilov@aquantia.com>
Signed-off-by: Igor Russkikh <igor.russkikh@aquantia.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Yana Esina 2018-09-10 12:39:30 +03:00 committed by David S. Miller
parent 0e1a0dde80
commit a0da96c08c
8 changed files with 220 additions and 17 deletions

View File

@ -285,6 +285,36 @@ static int aq_ethtool_set_coalesce(struct net_device *ndev,
return aq_nic_update_interrupt_moderation_settings(aq_nic); return aq_nic_update_interrupt_moderation_settings(aq_nic);
} }
static void aq_ethtool_get_wol(struct net_device *ndev,
struct ethtool_wolinfo *wol)
{
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;
}
static int aq_ethtool_set_wol(struct net_device *ndev,
struct ethtool_wolinfo *wol)
{
struct pci_dev *pdev = to_pci_dev(ndev->dev.parent);
struct aq_nic_s *aq_nic = netdev_priv(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);
return err;
}
static int aq_ethtool_nway_reset(struct net_device *ndev) static int aq_ethtool_nway_reset(struct net_device *ndev)
{ {
struct aq_nic_s *aq_nic = netdev_priv(ndev); struct aq_nic_s *aq_nic = netdev_priv(ndev);
@ -403,6 +433,8 @@ const struct ethtool_ops aq_ethtool_ops = {
.get_drvinfo = aq_ethtool_get_drvinfo, .get_drvinfo = aq_ethtool_get_drvinfo,
.get_strings = aq_ethtool_get_strings, .get_strings = aq_ethtool_get_strings,
.get_rxfh_indir_size = aq_ethtool_get_rss_indir_size, .get_rxfh_indir_size = aq_ethtool_get_rss_indir_size,
.get_wol = aq_ethtool_get_wol,
.set_wol = aq_ethtool_set_wol,
.nway_reset = aq_ethtool_nway_reset, .nway_reset = aq_ethtool_nway_reset,
.get_ringparam = aq_get_ringparam, .get_ringparam = aq_get_ringparam,
.set_ringparam = aq_set_ringparam, .set_ringparam = aq_set_ringparam,

View File

@ -204,7 +204,6 @@ struct aq_hw_ops {
int (*hw_get_fw_version)(struct aq_hw_s *self, u32 *fw_version); int (*hw_get_fw_version)(struct aq_hw_s *self, u32 *fw_version);
int (*hw_set_power)(struct aq_hw_s *self, unsigned int power_state);
}; };
struct aq_fw_ops { struct aq_fw_ops {
@ -228,6 +227,9 @@ struct aq_fw_ops {
int (*update_stats)(struct aq_hw_s *self); int (*update_stats)(struct aq_hw_s *self);
int (*set_flow_control)(struct aq_hw_s *self); int (*set_flow_control)(struct aq_hw_s *self);
int (*set_power)(struct aq_hw_s *self, unsigned int power_state,
u8 *mac);
}; };
#endif /* AQ_HW_H */ #endif /* AQ_HW_H */

View File

@ -889,11 +889,13 @@ void aq_nic_deinit(struct aq_nic_s *self)
self->aq_vecs > i; ++i, aq_vec = self->aq_vec[i]) self->aq_vecs > i; ++i, aq_vec = self->aq_vec[i])
aq_vec_deinit(aq_vec); aq_vec_deinit(aq_vec);
if (self->power_state == AQ_HW_POWER_STATE_D0) { self->aq_fw_ops->deinit(self->aq_hw);
(void)self->aq_fw_ops->deinit(self->aq_hw);
} else { if (self->power_state != AQ_HW_POWER_STATE_D0 ||
(void)self->aq_hw_ops->hw_set_power(self->aq_hw, self->aq_hw->aq_nic_cfg->wol) {
self->power_state); self->aq_fw_ops->set_power(self->aq_hw,
self->power_state,
self->ndev->dev_addr);
} }
err_exit:; err_exit:;

View File

@ -877,7 +877,6 @@ static int hw_atl_a0_hw_ring_rx_stop(struct aq_hw_s *self,
const struct aq_hw_ops hw_atl_ops_a0 = { const struct aq_hw_ops hw_atl_ops_a0 = {
.hw_set_mac_address = hw_atl_a0_hw_mac_addr_set, .hw_set_mac_address = hw_atl_a0_hw_mac_addr_set,
.hw_init = hw_atl_a0_hw_init, .hw_init = hw_atl_a0_hw_init,
.hw_set_power = hw_atl_utils_hw_set_power,
.hw_reset = hw_atl_a0_hw_reset, .hw_reset = hw_atl_a0_hw_reset,
.hw_start = hw_atl_a0_hw_start, .hw_start = hw_atl_a0_hw_start,
.hw_ring_tx_start = hw_atl_a0_hw_ring_tx_start, .hw_ring_tx_start = hw_atl_a0_hw_ring_tx_start,

View File

@ -935,7 +935,6 @@ static int hw_atl_b0_hw_ring_rx_stop(struct aq_hw_s *self,
const struct aq_hw_ops hw_atl_ops_b0 = { const struct aq_hw_ops hw_atl_ops_b0 = {
.hw_set_mac_address = hw_atl_b0_hw_mac_addr_set, .hw_set_mac_address = hw_atl_b0_hw_mac_addr_set,
.hw_init = hw_atl_b0_hw_init, .hw_init = hw_atl_b0_hw_init,
.hw_set_power = hw_atl_utils_hw_set_power,
.hw_reset = hw_atl_b0_hw_reset, .hw_reset = hw_atl_b0_hw_reset,
.hw_start = hw_atl_b0_hw_start, .hw_start = hw_atl_b0_hw_start,
.hw_ring_tx_start = hw_atl_b0_hw_ring_tx_start, .hw_ring_tx_start = hw_atl_b0_hw_ring_tx_start,

View File

@ -744,14 +744,6 @@ static int hw_atl_fw1x_deinit(struct aq_hw_s *self)
return 0; return 0;
} }
int hw_atl_utils_hw_set_power(struct aq_hw_s *self,
unsigned int power_state)
{
hw_atl_utils_mpi_set_speed(self, 0);
hw_atl_utils_mpi_set_state(self, MPI_POWER);
return 0;
}
int hw_atl_utils_update_stats(struct aq_hw_s *self) int hw_atl_utils_update_stats(struct aq_hw_s *self)
{ {
struct hw_aq_atl_utils_mbox mbox; struct hw_aq_atl_utils_mbox mbox;
@ -839,6 +831,81 @@ 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)
{
struct hw_aq_atl_utils_fw_rpc *prpc = NULL;
unsigned int rpc_size = 0U;
int err = 0;
err = hw_atl_utils_fw_rpc_wait(self, &prpc);
if (err < 0)
goto err_exit;
memset(prpc, 0, sizeof(*prpc));
if (wol_enabled) {
rpc_size = sizeof(prpc->msg_id) + sizeof(prpc->msg_wol);
prpc->msg_id = HAL_ATLANTIC_UTILS_FW_MSG_WOL_ADD;
prpc->msg_wol.priority =
HAL_ATLANTIC_UTILS_FW_MSG_WOL_PRIOR;
prpc->msg_wol.pattern_id =
HAL_ATLANTIC_UTILS_FW_MSG_WOL_PATTERN;
prpc->msg_wol.wol_packet_type =
HAL_ATLANTIC_UTILS_FW_MSG_WOL_MAG_PKT;
ether_addr_copy((u8 *)&prpc->msg_wol.wol_pattern, mac);
} else {
rpc_size = sizeof(prpc->msg_id) + sizeof(prpc->msg_del_id);
prpc->msg_id = HAL_ATLANTIC_UTILS_FW_MSG_WOL_DEL;
prpc->msg_wol.pattern_id =
HAL_ATLANTIC_UTILS_FW_MSG_WOL_PATTERN;
}
err = hw_atl_utils_fw_rpc_call(self, rpc_size);
err_exit:
return err;
}
int aq_fw1x_set_power(struct aq_hw_s *self, unsigned int power_state,
u8 *mac)
{
struct hw_aq_atl_utils_fw_rpc *prpc = NULL;
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 (err < 0)
goto err_exit;
rpc_size = sizeof(prpc->msg_id) +
sizeof(prpc->msg_enable_wakeup);
err = hw_atl_utils_fw_rpc_wait(self, &prpc);
if (err < 0)
goto err_exit;
memset(prpc, 0, rpc_size);
prpc->msg_id = HAL_ATLANTIC_UTILS_FW_MSG_ENABLE_WAKEUP;
prpc->msg_enable_wakeup.pattern_mask = 0x00000002;
err = hw_atl_utils_fw_rpc_call(self, rpc_size);
if (err < 0)
goto err_exit;
}
hw_atl_utils_mpi_set_speed(self, 0);
hw_atl_utils_mpi_set_state(self, MPI_POWER);
err_exit:
return err;
}
const struct aq_fw_ops aq_fw_1x_ops = { const struct aq_fw_ops aq_fw_1x_ops = {
.init = hw_atl_utils_mpi_create, .init = hw_atl_utils_mpi_create,
.deinit = hw_atl_fw1x_deinit, .deinit = hw_atl_fw1x_deinit,
@ -848,5 +915,6 @@ const struct aq_fw_ops aq_fw_1x_ops = {
.set_state = hw_atl_utils_mpi_set_state, .set_state = hw_atl_utils_mpi_set_state,
.update_link_status = hw_atl_utils_mpi_get_link_status, .update_link_status = hw_atl_utils_mpi_get_link_status,
.update_stats = hw_atl_utils_update_stats, .update_stats = hw_atl_utils_update_stats,
.set_power = aq_fw1x_set_power,
.set_flow_control = NULL, .set_flow_control = NULL,
}; };

View File

@ -257,6 +257,9 @@ enum hal_atl_utils_fw_state_e {
#define HAL_ATLANTIC_UTILS_FW_MSG_ARP 0x2U #define HAL_ATLANTIC_UTILS_FW_MSG_ARP 0x2U
#define HAL_ATLANTIC_UTILS_FW_MSG_INJECT 0x3U #define HAL_ATLANTIC_UTILS_FW_MSG_INJECT 0x3U
#define HAL_ATLANTIC_UTILS_FW_MSG_WOL_ADD 0x4U #define HAL_ATLANTIC_UTILS_FW_MSG_WOL_ADD 0x4U
#define HAL_ATLANTIC_UTILS_FW_MSG_WOL_PRIOR 0x10000000U
#define HAL_ATLANTIC_UTILS_FW_MSG_WOL_PATTERN 0x1U
#define HAL_ATLANTIC_UTILS_FW_MSG_WOL_MAG_PKT 0x2U
#define HAL_ATLANTIC_UTILS_FW_MSG_WOL_DEL 0x5U #define HAL_ATLANTIC_UTILS_FW_MSG_WOL_DEL 0x5U
#define HAL_ATLANTIC_UTILS_FW_MSG_ENABLE_WAKEUP 0x6U #define HAL_ATLANTIC_UTILS_FW_MSG_ENABLE_WAKEUP 0x6U
#define HAL_ATLANTIC_UTILS_FW_MSG_MSM_PFC 0x7U #define HAL_ATLANTIC_UTILS_FW_MSG_MSM_PFC 0x7U
@ -403,6 +406,8 @@ struct aq_stats_s *hw_atl_utils_get_hw_stats(struct aq_hw_s *self);
int hw_atl_utils_fw_downld_dwords(struct aq_hw_s *self, u32 a, int hw_atl_utils_fw_downld_dwords(struct aq_hw_s *self, u32 a,
u32 *p, u32 cnt); u32 *p, u32 cnt);
int hw_atl_utils_fw_set_wol(struct aq_hw_s *self, bool wol_enabled, u8 *mac);
int hw_atl_utils_fw_rpc_call(struct aq_hw_s *self, unsigned int rpc_size); int hw_atl_utils_fw_rpc_call(struct aq_hw_s *self, unsigned int rpc_size);
int hw_atl_utils_fw_rpc_wait(struct aq_hw_s *self, int hw_atl_utils_fw_rpc_wait(struct aq_hw_s *self,

View File

@ -231,7 +231,7 @@ static int aq_fw2x_get_mac_permanent(struct aq_hw_s *self, u8 *mac)
return err; return err;
} }
static int aq_fw2x_update_stats(struct aq_hw_s *self) int aq_fw2x_update_stats(struct aq_hw_s *self)
{ {
int err = 0; int err = 0;
u32 mpi_opts = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR); u32 mpi_opts = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR);
@ -252,6 +252,101 @@ static int aq_fw2x_update_stats(struct aq_hw_s *self)
return hw_atl_utils_update_stats(self); return hw_atl_utils_update_stats(self);
} }
static int aq_fw2x_set_sleep_proxy(struct aq_hw_s *self, u8 *mac)
{
struct hw_aq_atl_utils_fw_rpc *rpc = NULL;
struct offload_info *cfg = NULL;
unsigned int rpc_size = 0U;
u32 mpi_opts;
int err = 0;
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);
AQ_HW_WAIT_FOR((aq_hw_read_reg(self, HW_ATL_FW2X_MPI_STATE2_ADDR) &
HW_ATL_FW2X_CTRL_SLEEP_PROXY), 1U, 10000U);
err_exit:
return err;
}
static int aq_fw2x_set_wol_params(struct aq_hw_s *self, u8 *mac)
{
struct hw_aq_atl_utils_fw_rpc *rpc = NULL;
struct fw2x_msg_wol *msg = NULL;
u32 mpi_opts;
int err = 0;
err = hw_atl_utils_fw_rpc_wait(self, &rpc);
if (err < 0)
goto err_exit;
msg = (struct fw2x_msg_wol *)rpc;
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);
AQ_HW_WAIT_FOR((aq_hw_read_reg(self, HW_ATL_FW2X_MPI_STATE2_ADDR) &
HW_ATL_FW2X_CTRL_WOL), 1U, 10000U);
err_exit:
return err;
}
static int aq_fw2x_set_power(struct aq_hw_s *self, unsigned int power_state,
u8 *mac)
{
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);
}
err_exit:
return err;
}
static int aq_fw2x_renegotiate(struct aq_hw_s *self) static int aq_fw2x_renegotiate(struct aq_hw_s *self)
{ {
u32 mpi_opts = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR); u32 mpi_opts = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR);
@ -284,5 +379,6 @@ const struct aq_fw_ops aq_fw_2x_ops = {
.set_state = aq_fw2x_set_state, .set_state = aq_fw2x_set_state,
.update_link_status = aq_fw2x_update_link_status, .update_link_status = aq_fw2x_update_link_status,
.update_stats = aq_fw2x_update_stats, .update_stats = aq_fw2x_update_stats,
.set_power = aq_fw2x_set_power,
.set_flow_control = aq_fw2x_set_flow_control, .set_flow_control = aq_fw2x_set_flow_control,
}; };