mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-29 07:34:06 +08:00
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:
parent
0e1a0dde80
commit
a0da96c08c
@ -285,6 +285,36 @@ static int aq_ethtool_set_coalesce(struct net_device *ndev,
|
||||
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)
|
||||
{
|
||||
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_strings = aq_ethtool_get_strings,
|
||||
.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,
|
||||
.get_ringparam = aq_get_ringparam,
|
||||
.set_ringparam = aq_set_ringparam,
|
||||
|
@ -204,7 +204,6 @@ struct aq_hw_ops {
|
||||
|
||||
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 {
|
||||
@ -228,6 +227,9 @@ struct aq_fw_ops {
|
||||
int (*update_stats)(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 */
|
||||
|
@ -889,11 +889,13 @@ void aq_nic_deinit(struct aq_nic_s *self)
|
||||
self->aq_vecs > i; ++i, aq_vec = self->aq_vec[i])
|
||||
aq_vec_deinit(aq_vec);
|
||||
|
||||
if (self->power_state == AQ_HW_POWER_STATE_D0) {
|
||||
(void)self->aq_fw_ops->deinit(self->aq_hw);
|
||||
} else {
|
||||
(void)self->aq_hw_ops->hw_set_power(self->aq_hw,
|
||||
self->power_state);
|
||||
self->aq_fw_ops->deinit(self->aq_hw);
|
||||
|
||||
if (self->power_state != AQ_HW_POWER_STATE_D0 ||
|
||||
self->aq_hw->aq_nic_cfg->wol) {
|
||||
self->aq_fw_ops->set_power(self->aq_hw,
|
||||
self->power_state,
|
||||
self->ndev->dev_addr);
|
||||
}
|
||||
|
||||
err_exit:;
|
||||
|
@ -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 = {
|
||||
.hw_set_mac_address = hw_atl_a0_hw_mac_addr_set,
|
||||
.hw_init = hw_atl_a0_hw_init,
|
||||
.hw_set_power = hw_atl_utils_hw_set_power,
|
||||
.hw_reset = hw_atl_a0_hw_reset,
|
||||
.hw_start = hw_atl_a0_hw_start,
|
||||
.hw_ring_tx_start = hw_atl_a0_hw_ring_tx_start,
|
||||
|
@ -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 = {
|
||||
.hw_set_mac_address = hw_atl_b0_hw_mac_addr_set,
|
||||
.hw_init = hw_atl_b0_hw_init,
|
||||
.hw_set_power = hw_atl_utils_hw_set_power,
|
||||
.hw_reset = hw_atl_b0_hw_reset,
|
||||
.hw_start = hw_atl_b0_hw_start,
|
||||
.hw_ring_tx_start = hw_atl_b0_hw_ring_tx_start,
|
||||
|
@ -744,14 +744,6 @@ static int hw_atl_fw1x_deinit(struct aq_hw_s *self)
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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 = {
|
||||
.init = hw_atl_utils_mpi_create,
|
||||
.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,
|
||||
.update_link_status = hw_atl_utils_mpi_get_link_status,
|
||||
.update_stats = hw_atl_utils_update_stats,
|
||||
.set_power = aq_fw1x_set_power,
|
||||
.set_flow_control = NULL,
|
||||
};
|
||||
|
@ -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_INJECT 0x3U
|
||||
#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_ENABLE_WAKEUP 0x6U
|
||||
#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,
|
||||
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_wait(struct aq_hw_s *self,
|
||||
|
@ -231,7 +231,7 @@ static int aq_fw2x_get_mac_permanent(struct aq_hw_s *self, u8 *mac)
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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,
|
||||
.update_link_status = aq_fw2x_update_link_status,
|
||||
.update_stats = aq_fw2x_update_stats,
|
||||
.set_power = aq_fw2x_set_power,
|
||||
.set_flow_control = aq_fw2x_set_flow_control,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user