Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: atlantic: add set_power to fw_ops for atl2 to fix wol

Aquantia AQC113(C) using ATL2FW doesn't properly prepare the NIC for
enabling wake-on-lan. The FW operation `set_power` was only implemented
for `hw_atl` and not `hw_atl2`. Implement the `set_power` functionality
for `hw_atl2`.

Tested with both AQC113 and AQC113C devices. Confirmed you can shutdown
the system and wake from S5 using magic packets. NIC was previously
powered off when entering S5. If the NIC was configured for WOL by the
Windows driver, loading the atlantic driver would disable WOL.

Partially cherry-picks changes from commit,
https://github.com/Aquantia/AQtion/commit/37bd5cc

Attributing original authors from Marvell for the referenced commit.

Closes: https://github.com/Aquantia/AQtion/issues/70
Co-developed-by: Igor Russkikh <irusskikh@marvell.com>
Co-developed-by: Mark Starovoitov <mstarovoitov@marvell.com>
Co-developed-by: Dmitry Bogdanov <dbogdanov@marvell.com>
Co-developed-by: Pavel Belous <pbelous@marvell.com>
Co-developed-by: Nikita Danilov <ndanilov@marvell.com>
Signed-off-by: Eric Work <work.eric@gmail.com>
Reviewed-by: Igor Russkikh <irusskikh@marvell.com>
Link: https://patch.msgid.link/20250629051535.5172-1-work.eric@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Eric Work and committed by
Jakub Kicinski
fad9cf21 fbe346ce

+41
+2
drivers/net/ethernet/aquantia/atlantic/aq_hw.h
··· 113 113 #define AQ_HW_POWER_STATE_D0 0U 114 114 #define AQ_HW_POWER_STATE_D3 3U 115 115 116 + #define AQ_FW_WAKE_ON_LINK_RTPM BIT(10) 117 + 116 118 #define AQ_HW_FLAG_STARTED 0x00000004U 117 119 #define AQ_HW_FLAG_STOPPING 0x00000008U 118 120 #define AQ_HW_FLAG_RESETTING 0x00000010U
+39
drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils_fw.c
··· 462 462 return aq_a2_fw_get_phy_temp(self, temp); 463 463 } 464 464 465 + static int aq_a2_fw_set_wol_params(struct aq_hw_s *self, const u8 *mac, u32 wol) 466 + { 467 + struct mac_address_aligned_s mac_address; 468 + struct link_control_s link_control; 469 + struct wake_on_lan_s wake_on_lan; 470 + 471 + memcpy(mac_address.aligned.mac_address, mac, ETH_ALEN); 472 + hw_atl2_shared_buffer_write(self, mac_address, mac_address); 473 + 474 + memset(&wake_on_lan, 0, sizeof(wake_on_lan)); 475 + 476 + if (wol & WAKE_MAGIC) 477 + wake_on_lan.wake_on_magic_packet = 1U; 478 + 479 + if (wol & (WAKE_PHY | AQ_FW_WAKE_ON_LINK_RTPM)) 480 + wake_on_lan.wake_on_link_up = 1U; 481 + 482 + hw_atl2_shared_buffer_write(self, sleep_proxy, wake_on_lan); 483 + 484 + hw_atl2_shared_buffer_get(self, link_control, link_control); 485 + link_control.mode = AQ_HOST_MODE_SLEEP_PROXY; 486 + hw_atl2_shared_buffer_write(self, link_control, link_control); 487 + 488 + return hw_atl2_shared_buffer_finish_ack(self); 489 + } 490 + 491 + static int aq_a2_fw_set_power(struct aq_hw_s *self, unsigned int power_state, 492 + const u8 *mac) 493 + { 494 + u32 wol = self->aq_nic_cfg->wol; 495 + int err = 0; 496 + 497 + if (wol) 498 + err = aq_a2_fw_set_wol_params(self, mac, wol); 499 + 500 + return err; 501 + } 502 + 465 503 static int aq_a2_fw_set_eee_rate(struct aq_hw_s *self, u32 speed) 466 504 { 467 505 struct link_options_s link_options; ··· 643 605 .set_state = aq_a2_fw_set_state, 644 606 .update_link_status = aq_a2_fw_update_link_status, 645 607 .update_stats = aq_a2_fw_update_stats, 608 + .set_power = aq_a2_fw_set_power, 646 609 .get_mac_temp = aq_a2_fw_get_mac_temp, 647 610 .get_phy_temp = aq_a2_fw_get_phy_temp, 648 611 .set_eee_rate = aq_a2_fw_set_eee_rate,