mirror of https://gitee.com/openkylin/linux.git
ice: Add advanced power mgmt for WoL
Add callbacks needed to support advanced power management for Wake on LAN. Also make ice_pf_state_is_nominal function available for all configurations not just CONFIG_PCI_IOV. Signed-off-by: Akeem G Abodunrin <akeem.g.abodunrin@intel.com> Signed-off-by: Jesse Brandeburg <jesse.brandeburg@intel.com> Tested-by: Andrew Bowers <andrewx.bowers@intel.com> Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
This commit is contained in:
parent
81aed6475d
commit
769c500dcc
|
@ -423,6 +423,8 @@ struct ice_pf {
|
||||||
u16 empr_count; /* EMP reset count */
|
u16 empr_count; /* EMP reset count */
|
||||||
u16 pfr_count; /* PF reset count */
|
u16 pfr_count; /* PF reset count */
|
||||||
|
|
||||||
|
u8 wol_ena : 1; /* software state of WoL */
|
||||||
|
u32 wakeup_reason; /* last wakeup reason */
|
||||||
struct ice_hw_port_stats stats;
|
struct ice_hw_port_stats stats;
|
||||||
struct ice_hw_port_stats stats_prev;
|
struct ice_hw_port_stats stats_prev;
|
||||||
struct ice_hw hw;
|
struct ice_hw hw;
|
||||||
|
@ -568,6 +570,7 @@ int ice_schedule_reset(struct ice_pf *pf, enum ice_reset_req reset);
|
||||||
void ice_print_link_msg(struct ice_vsi *vsi, bool isup);
|
void ice_print_link_msg(struct ice_vsi *vsi, bool isup);
|
||||||
const char *ice_stat_str(enum ice_status stat_err);
|
const char *ice_stat_str(enum ice_status stat_err);
|
||||||
const char *ice_aq_str(enum ice_aq_err aq_err);
|
const char *ice_aq_str(enum ice_aq_err aq_err);
|
||||||
|
bool ice_is_wol_supported(struct ice_pf *pf);
|
||||||
int
|
int
|
||||||
ice_fdir_write_fltr(struct ice_pf *pf, struct ice_fdir_fltr *input, bool add,
|
ice_fdir_write_fltr(struct ice_pf *pf, struct ice_fdir_fltr *input, bool add,
|
||||||
bool is_tun);
|
bool is_tun);
|
||||||
|
|
|
@ -3322,6 +3322,58 @@ static int ice_set_channels(struct net_device *dev, struct ethtool_channels *ch)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ice_get_wol - get current Wake on LAN configuration
|
||||||
|
* @netdev: network interface device structure
|
||||||
|
* @wol: Ethtool structure to retrieve WoL settings
|
||||||
|
*/
|
||||||
|
static void ice_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
|
||||||
|
{
|
||||||
|
struct ice_netdev_priv *np = netdev_priv(netdev);
|
||||||
|
struct ice_pf *pf = np->vsi->back;
|
||||||
|
|
||||||
|
if (np->vsi->type != ICE_VSI_PF)
|
||||||
|
netdev_warn(netdev, "Wake on LAN is not supported on this interface!\n");
|
||||||
|
|
||||||
|
/* Get WoL settings based on the HW capability */
|
||||||
|
if (ice_is_wol_supported(pf)) {
|
||||||
|
wol->supported = WAKE_MAGIC;
|
||||||
|
wol->wolopts = pf->wol_ena ? WAKE_MAGIC : 0;
|
||||||
|
} else {
|
||||||
|
wol->supported = 0;
|
||||||
|
wol->wolopts = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ice_set_wol - set Wake on LAN on supported device
|
||||||
|
* @netdev: network interface device structure
|
||||||
|
* @wol: Ethtool structure to set WoL
|
||||||
|
*/
|
||||||
|
static int ice_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
|
||||||
|
{
|
||||||
|
struct ice_netdev_priv *np = netdev_priv(netdev);
|
||||||
|
struct ice_vsi *vsi = np->vsi;
|
||||||
|
struct ice_pf *pf = vsi->back;
|
||||||
|
|
||||||
|
if (vsi->type != ICE_VSI_PF || !ice_is_wol_supported(pf))
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
/* only magic packet is supported */
|
||||||
|
if (wol->wolopts && wol->wolopts != WAKE_MAGIC)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
/* Set WoL only if there is a new value */
|
||||||
|
if (pf->wol_ena != !!wol->wolopts) {
|
||||||
|
pf->wol_ena = !!wol->wolopts;
|
||||||
|
device_set_wakeup_enable(ice_pf_to_dev(pf), pf->wol_ena);
|
||||||
|
netdev_dbg(netdev, "WoL magic packet %sabled\n",
|
||||||
|
pf->wol_ena ? "en" : "dis");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
enum ice_container_type {
|
enum ice_container_type {
|
||||||
ICE_RX_CONTAINER,
|
ICE_RX_CONTAINER,
|
||||||
ICE_TX_CONTAINER,
|
ICE_TX_CONTAINER,
|
||||||
|
@ -3805,6 +3857,8 @@ static const struct ethtool_ops ice_ethtool_ops = {
|
||||||
.get_drvinfo = ice_get_drvinfo,
|
.get_drvinfo = ice_get_drvinfo,
|
||||||
.get_regs_len = ice_get_regs_len,
|
.get_regs_len = ice_get_regs_len,
|
||||||
.get_regs = ice_get_regs,
|
.get_regs = ice_get_regs,
|
||||||
|
.get_wol = ice_get_wol,
|
||||||
|
.set_wol = ice_set_wol,
|
||||||
.get_msglevel = ice_get_msglevel,
|
.get_msglevel = ice_get_msglevel,
|
||||||
.set_msglevel = ice_set_msglevel,
|
.set_msglevel = ice_set_msglevel,
|
||||||
.self_test = ice_self_test,
|
.self_test = ice_self_test,
|
||||||
|
@ -3847,6 +3901,8 @@ static const struct ethtool_ops ice_ethtool_safe_mode_ops = {
|
||||||
.get_drvinfo = ice_get_drvinfo,
|
.get_drvinfo = ice_get_drvinfo,
|
||||||
.get_regs_len = ice_get_regs_len,
|
.get_regs_len = ice_get_regs_len,
|
||||||
.get_regs = ice_get_regs,
|
.get_regs = ice_get_regs,
|
||||||
|
.get_wol = ice_get_wol,
|
||||||
|
.set_wol = ice_set_wol,
|
||||||
.get_msglevel = ice_get_msglevel,
|
.get_msglevel = ice_get_msglevel,
|
||||||
.set_msglevel = ice_set_msglevel,
|
.set_msglevel = ice_set_msglevel,
|
||||||
.get_link = ethtool_op_get_link,
|
.get_link = ethtool_op_get_link,
|
||||||
|
|
|
@ -367,6 +367,15 @@
|
||||||
#define VSIQF_FD_CNT_FD_GCNT_M ICE_M(0x3FFF, 0)
|
#define VSIQF_FD_CNT_FD_GCNT_M ICE_M(0x3FFF, 0)
|
||||||
#define VSIQF_HKEY_MAX_INDEX 12
|
#define VSIQF_HKEY_MAX_INDEX 12
|
||||||
#define VSIQF_HLUT_MAX_INDEX 15
|
#define VSIQF_HLUT_MAX_INDEX 15
|
||||||
|
#define PFPM_APM 0x000B8080
|
||||||
|
#define PFPM_APM_APME_M BIT(0)
|
||||||
|
#define PFPM_WUFC 0x0009DC00
|
||||||
|
#define PFPM_WUFC_MAG_M BIT(1)
|
||||||
|
#define PFPM_WUS 0x0009DB80
|
||||||
|
#define PFPM_WUS_LNKC_M BIT(0)
|
||||||
|
#define PFPM_WUS_MAG_M BIT(1)
|
||||||
|
#define PFPM_WUS_MNG_M BIT(3)
|
||||||
|
#define PFPM_WUS_FW_RST_WK_M BIT(31)
|
||||||
#define VFINT_DYN_CTLN(_i) (0x00003800 + ((_i) * 4))
|
#define VFINT_DYN_CTLN(_i) (0x00003800 + ((_i) * 4))
|
||||||
#define VFINT_DYN_CTLN_CLEARPBA_M BIT(1)
|
#define VFINT_DYN_CTLN_CLEARPBA_M BIT(1)
|
||||||
#define PRTRPB_RDPC 0x000AC260
|
#define PRTRPB_RDPC 0x000AC260
|
||||||
|
|
|
@ -1467,6 +1467,30 @@ static void ice_vsi_set_rss_flow_fld(struct ice_vsi *vsi)
|
||||||
vsi_num, ice_stat_str(status));
|
vsi_num, ice_stat_str(status));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ice_pf_state_is_nominal - checks the PF for nominal state
|
||||||
|
* @pf: pointer to PF to check
|
||||||
|
*
|
||||||
|
* Check the PF's state for a collection of bits that would indicate
|
||||||
|
* the PF is in a state that would inhibit normal operation for
|
||||||
|
* driver functionality.
|
||||||
|
*
|
||||||
|
* Returns true if PF is in a nominal state, false otherwise
|
||||||
|
*/
|
||||||
|
bool ice_pf_state_is_nominal(struct ice_pf *pf)
|
||||||
|
{
|
||||||
|
DECLARE_BITMAP(check_bits, __ICE_STATE_NBITS) = { 0 };
|
||||||
|
|
||||||
|
if (!pf)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bitmap_set(check_bits, 0, __ICE_STATE_NOMINAL_CHECK_BITS);
|
||||||
|
if (bitmap_intersects(pf->state, check_bits, __ICE_STATE_NBITS))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ice_update_eth_stats - Update VSI-specific ethernet statistics counters
|
* ice_update_eth_stats - Update VSI-specific ethernet statistics counters
|
||||||
* @vsi: the VSI to be updated
|
* @vsi: the VSI to be updated
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
|
|
||||||
const char *ice_vsi_type_str(enum ice_vsi_type vsi_type);
|
const char *ice_vsi_type_str(enum ice_vsi_type vsi_type);
|
||||||
|
|
||||||
|
bool ice_pf_state_is_nominal(struct ice_pf *pf);
|
||||||
|
|
||||||
void ice_update_eth_stats(struct ice_vsi *vsi);
|
void ice_update_eth_stats(struct ice_vsi *vsi);
|
||||||
|
|
||||||
int ice_vsi_cfg_rxqs(struct ice_vsi *vsi);
|
int ice_vsi_cfg_rxqs(struct ice_vsi *vsi);
|
||||||
|
|
|
@ -1129,10 +1129,15 @@ static void ice_service_task_complete(struct ice_pf *pf)
|
||||||
/**
|
/**
|
||||||
* ice_service_task_stop - stop service task and cancel works
|
* ice_service_task_stop - stop service task and cancel works
|
||||||
* @pf: board private structure
|
* @pf: board private structure
|
||||||
|
*
|
||||||
|
* Return 0 if the __ICE_SERVICE_DIS bit was not already set,
|
||||||
|
* 1 otherwise.
|
||||||
*/
|
*/
|
||||||
static void ice_service_task_stop(struct ice_pf *pf)
|
static int ice_service_task_stop(struct ice_pf *pf)
|
||||||
{
|
{
|
||||||
set_bit(__ICE_SERVICE_DIS, pf->state);
|
int ret;
|
||||||
|
|
||||||
|
ret = test_and_set_bit(__ICE_SERVICE_DIS, pf->state);
|
||||||
|
|
||||||
if (pf->serv_tmr.function)
|
if (pf->serv_tmr.function)
|
||||||
del_timer_sync(&pf->serv_tmr);
|
del_timer_sync(&pf->serv_tmr);
|
||||||
|
@ -1140,6 +1145,7 @@ static void ice_service_task_stop(struct ice_pf *pf)
|
||||||
cancel_work_sync(&pf->serv_task);
|
cancel_work_sync(&pf->serv_task);
|
||||||
|
|
||||||
clear_bit(__ICE_SERVICE_SCHED, pf->state);
|
clear_bit(__ICE_SERVICE_SCHED, pf->state);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2940,6 +2946,27 @@ static int ice_init_interrupt_scheme(struct ice_pf *pf)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ice_is_wol_supported - get NVM state of WoL
|
||||||
|
* @pf: board private structure
|
||||||
|
*
|
||||||
|
* Check if WoL is supported based on the HW configuration.
|
||||||
|
* Returns true if NVM supports and enables WoL for this port, false otherwise
|
||||||
|
*/
|
||||||
|
bool ice_is_wol_supported(struct ice_pf *pf)
|
||||||
|
{
|
||||||
|
struct ice_hw *hw = &pf->hw;
|
||||||
|
u16 wol_ctrl;
|
||||||
|
|
||||||
|
/* A bit set to 1 in the NVM Software Reserved Word 2 (WoL control
|
||||||
|
* word) indicates WoL is not supported on the corresponding PF ID.
|
||||||
|
*/
|
||||||
|
if (ice_read_sr_word(hw, ICE_SR_NVM_WOL_CFG, &wol_ctrl))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return !(BIT(hw->pf_id) & wol_ctrl);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ice_vsi_recfg_qs - Change the number of queues on a VSI
|
* ice_vsi_recfg_qs - Change the number of queues on a VSI
|
||||||
* @vsi: VSI being changed
|
* @vsi: VSI being changed
|
||||||
|
@ -3287,6 +3314,33 @@ static void ice_request_fw(struct ice_pf *pf)
|
||||||
release_firmware(firmware);
|
release_firmware(firmware);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ice_print_wake_reason - show the wake up cause in the log
|
||||||
|
* @pf: pointer to the PF struct
|
||||||
|
*/
|
||||||
|
static void ice_print_wake_reason(struct ice_pf *pf)
|
||||||
|
{
|
||||||
|
u32 wus = pf->wakeup_reason;
|
||||||
|
const char *wake_str;
|
||||||
|
|
||||||
|
/* if no wake event, nothing to print */
|
||||||
|
if (!wus)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (wus & PFPM_WUS_LNKC_M)
|
||||||
|
wake_str = "Link\n";
|
||||||
|
else if (wus & PFPM_WUS_MAG_M)
|
||||||
|
wake_str = "Magic Packet\n";
|
||||||
|
else if (wus & PFPM_WUS_MNG_M)
|
||||||
|
wake_str = "Management\n";
|
||||||
|
else if (wus & PFPM_WUS_FW_RST_WK_M)
|
||||||
|
wake_str = "Firmware Reset\n";
|
||||||
|
else
|
||||||
|
wake_str = "Unknown\n";
|
||||||
|
|
||||||
|
dev_info(ice_pf_to_dev(pf), "Wake reason: %s", wake_str);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ice_probe - Device initialization routine
|
* ice_probe - Device initialization routine
|
||||||
* @pdev: PCI device information struct
|
* @pdev: PCI device information struct
|
||||||
|
@ -3470,6 +3524,18 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent)
|
||||||
|
|
||||||
ice_verify_cacheline_size(pf);
|
ice_verify_cacheline_size(pf);
|
||||||
|
|
||||||
|
/* Save wakeup reason register for later use */
|
||||||
|
pf->wakeup_reason = rd32(hw, PFPM_WUS);
|
||||||
|
|
||||||
|
/* check for a power management event */
|
||||||
|
ice_print_wake_reason(pf);
|
||||||
|
|
||||||
|
/* clear wake status, all bits */
|
||||||
|
wr32(hw, PFPM_WUS, U32_MAX);
|
||||||
|
|
||||||
|
/* Disable WoL at init, wait for user to enable */
|
||||||
|
device_set_wakeup_enable(dev, false);
|
||||||
|
|
||||||
/* If no DDP driven features have to be setup, we are done with probe */
|
/* If no DDP driven features have to be setup, we are done with probe */
|
||||||
if (ice_is_safe_mode(pf))
|
if (ice_is_safe_mode(pf))
|
||||||
goto probe_done;
|
goto probe_done;
|
||||||
|
@ -3514,9 +3580,72 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent)
|
||||||
err_exit_unroll:
|
err_exit_unroll:
|
||||||
ice_devlink_unregister(pf);
|
ice_devlink_unregister(pf);
|
||||||
pci_disable_pcie_error_reporting(pdev);
|
pci_disable_pcie_error_reporting(pdev);
|
||||||
|
pci_disable_device(pdev);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ice_set_wake - enable or disable Wake on LAN
|
||||||
|
* @pf: pointer to the PF struct
|
||||||
|
*
|
||||||
|
* Simple helper for WoL control
|
||||||
|
*/
|
||||||
|
static void ice_set_wake(struct ice_pf *pf)
|
||||||
|
{
|
||||||
|
struct ice_hw *hw = &pf->hw;
|
||||||
|
bool wol = pf->wol_ena;
|
||||||
|
|
||||||
|
/* clear wake state, otherwise new wake events won't fire */
|
||||||
|
wr32(hw, PFPM_WUS, U32_MAX);
|
||||||
|
|
||||||
|
/* enable / disable APM wake up, no RMW needed */
|
||||||
|
wr32(hw, PFPM_APM, wol ? PFPM_APM_APME_M : 0);
|
||||||
|
|
||||||
|
/* set magic packet filter enabled */
|
||||||
|
wr32(hw, PFPM_WUFC, wol ? PFPM_WUFC_MAG_M : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ice_setup_magic_mc_wake - setup device to wake on multicast magic packet
|
||||||
|
* @pf: pointer to the PF struct
|
||||||
|
*
|
||||||
|
* Issue firmware command to enable multicast magic wake, making
|
||||||
|
* sure that any locally administered address (LAA) is used for
|
||||||
|
* wake, and that PF reset doesn't undo the LAA.
|
||||||
|
*/
|
||||||
|
static void ice_setup_mc_magic_wake(struct ice_pf *pf)
|
||||||
|
{
|
||||||
|
struct device *dev = ice_pf_to_dev(pf);
|
||||||
|
struct ice_hw *hw = &pf->hw;
|
||||||
|
enum ice_status status;
|
||||||
|
u8 mac_addr[ETH_ALEN];
|
||||||
|
struct ice_vsi *vsi;
|
||||||
|
u8 flags;
|
||||||
|
|
||||||
|
if (!pf->wol_ena)
|
||||||
|
return;
|
||||||
|
|
||||||
|
vsi = ice_get_main_vsi(pf);
|
||||||
|
if (!vsi)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Get current MAC address in case it's an LAA */
|
||||||
|
if (vsi->netdev)
|
||||||
|
ether_addr_copy(mac_addr, vsi->netdev->dev_addr);
|
||||||
|
else
|
||||||
|
ether_addr_copy(mac_addr, vsi->port_info->mac.perm_addr);
|
||||||
|
|
||||||
|
flags = ICE_AQC_MAN_MAC_WR_MC_MAG_EN |
|
||||||
|
ICE_AQC_MAN_MAC_UPDATE_LAA_WOL |
|
||||||
|
ICE_AQC_MAN_MAC_WR_WOL_LAA_PFR_KEEP;
|
||||||
|
|
||||||
|
status = ice_aq_manage_mac_write(hw, mac_addr, flags, NULL);
|
||||||
|
if (status)
|
||||||
|
dev_err(dev, "Failed to enable Multicast Magic Packet wake, err %s aq_err %s\n",
|
||||||
|
ice_stat_str(status),
|
||||||
|
ice_aq_str(hw->adminq.sq_last_status));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ice_remove - Device removal routine
|
* ice_remove - Device removal routine
|
||||||
* @pdev: PCI device information struct
|
* @pdev: PCI device information struct
|
||||||
|
@ -3546,8 +3675,10 @@ static void ice_remove(struct pci_dev *pdev)
|
||||||
mutex_destroy(&(&pf->hw)->fdir_fltr_lock);
|
mutex_destroy(&(&pf->hw)->fdir_fltr_lock);
|
||||||
if (!ice_is_safe_mode(pf))
|
if (!ice_is_safe_mode(pf))
|
||||||
ice_remove_arfs(pf);
|
ice_remove_arfs(pf);
|
||||||
|
ice_setup_mc_magic_wake(pf);
|
||||||
ice_devlink_destroy_port(pf);
|
ice_devlink_destroy_port(pf);
|
||||||
ice_vsi_release_all(pf);
|
ice_vsi_release_all(pf);
|
||||||
|
ice_set_wake(pf);
|
||||||
ice_free_irq_msix_misc(pf);
|
ice_free_irq_msix_misc(pf);
|
||||||
ice_for_each_vsi(pf, i) {
|
ice_for_each_vsi(pf, i) {
|
||||||
if (!pf->vsi[i])
|
if (!pf->vsi[i])
|
||||||
|
@ -3567,8 +3698,230 @@ static void ice_remove(struct pci_dev *pdev)
|
||||||
pci_wait_for_pending_transaction(pdev);
|
pci_wait_for_pending_transaction(pdev);
|
||||||
ice_clear_interrupt_scheme(pf);
|
ice_clear_interrupt_scheme(pf);
|
||||||
pci_disable_pcie_error_reporting(pdev);
|
pci_disable_pcie_error_reporting(pdev);
|
||||||
|
pci_disable_device(pdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ice_shutdown - PCI callback for shutting down device
|
||||||
|
* @pdev: PCI device information struct
|
||||||
|
*/
|
||||||
|
static void ice_shutdown(struct pci_dev *pdev)
|
||||||
|
{
|
||||||
|
struct ice_pf *pf = pci_get_drvdata(pdev);
|
||||||
|
|
||||||
|
ice_remove(pdev);
|
||||||
|
|
||||||
|
if (system_state == SYSTEM_POWER_OFF) {
|
||||||
|
pci_wake_from_d3(pdev, pf->wol_ena);
|
||||||
|
pci_set_power_state(pdev, PCI_D3hot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
/**
|
||||||
|
* ice_prepare_for_shutdown - prep for PCI shutdown
|
||||||
|
* @pf: board private structure
|
||||||
|
*
|
||||||
|
* Inform or close all dependent features in prep for PCI device shutdown
|
||||||
|
*/
|
||||||
|
static void ice_prepare_for_shutdown(struct ice_pf *pf)
|
||||||
|
{
|
||||||
|
struct ice_hw *hw = &pf->hw;
|
||||||
|
u32 v;
|
||||||
|
|
||||||
|
/* Notify VFs of impending reset */
|
||||||
|
if (ice_check_sq_alive(hw, &hw->mailboxq))
|
||||||
|
ice_vc_notify_reset(pf);
|
||||||
|
|
||||||
|
dev_dbg(ice_pf_to_dev(pf), "Tearing down internal switch for shutdown\n");
|
||||||
|
|
||||||
|
/* disable the VSIs and their queues that are not already DOWN */
|
||||||
|
ice_pf_dis_all_vsi(pf, false);
|
||||||
|
|
||||||
|
ice_for_each_vsi(pf, v)
|
||||||
|
if (pf->vsi[v])
|
||||||
|
pf->vsi[v]->vsi_num = 0;
|
||||||
|
|
||||||
|
ice_shutdown_all_ctrlq(hw);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ice_reinit_interrupt_scheme - Reinitialize interrupt scheme
|
||||||
|
* @pf: board private structure to reinitialize
|
||||||
|
*
|
||||||
|
* This routine reinitialize interrupt scheme that was cleared during
|
||||||
|
* power management suspend callback.
|
||||||
|
*
|
||||||
|
* This should be called during resume routine to re-allocate the q_vectors
|
||||||
|
* and reacquire interrupts.
|
||||||
|
*/
|
||||||
|
static int ice_reinit_interrupt_scheme(struct ice_pf *pf)
|
||||||
|
{
|
||||||
|
struct device *dev = ice_pf_to_dev(pf);
|
||||||
|
int ret, v;
|
||||||
|
|
||||||
|
/* Since we clear MSIX flag during suspend, we need to
|
||||||
|
* set it back during resume...
|
||||||
|
*/
|
||||||
|
|
||||||
|
ret = ice_init_interrupt_scheme(pf);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "Failed to re-initialize interrupt %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remap vectors and rings, after successful re-init interrupts */
|
||||||
|
ice_for_each_vsi(pf, v) {
|
||||||
|
if (!pf->vsi[v])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ret = ice_vsi_alloc_q_vectors(pf->vsi[v]);
|
||||||
|
if (ret)
|
||||||
|
goto err_reinit;
|
||||||
|
ice_vsi_map_rings_to_vectors(pf->vsi[v]);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = ice_req_irq_msix_misc(pf);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "Setting up misc vector failed after device suspend %d\n",
|
||||||
|
ret);
|
||||||
|
goto err_reinit;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_reinit:
|
||||||
|
while (v--)
|
||||||
|
if (pf->vsi[v])
|
||||||
|
ice_vsi_free_q_vectors(pf->vsi[v]);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ice_suspend
|
||||||
|
* @dev: generic device information structure
|
||||||
|
*
|
||||||
|
* Power Management callback to quiesce the device and prepare
|
||||||
|
* for D3 transition.
|
||||||
|
*/
|
||||||
|
static int ice_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct pci_dev *pdev = to_pci_dev(dev);
|
||||||
|
struct ice_pf *pf;
|
||||||
|
int disabled, v;
|
||||||
|
|
||||||
|
pf = pci_get_drvdata(pdev);
|
||||||
|
|
||||||
|
if (!ice_pf_state_is_nominal(pf)) {
|
||||||
|
dev_err(dev, "Device is not ready, no need to suspend it\n");
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stop watchdog tasks until resume completion.
|
||||||
|
* Even though it is most likely that the service task is
|
||||||
|
* disabled if the device is suspended or down, the service task's
|
||||||
|
* state is controlled by a different state bit, and we should
|
||||||
|
* store and honor whatever state that bit is in at this point.
|
||||||
|
*/
|
||||||
|
disabled = ice_service_task_stop(pf);
|
||||||
|
|
||||||
|
/* Already suspended?, then there is nothing to do */
|
||||||
|
if (test_and_set_bit(__ICE_SUSPENDED, pf->state)) {
|
||||||
|
if (!disabled)
|
||||||
|
ice_service_task_restart(pf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (test_bit(__ICE_DOWN, pf->state) ||
|
||||||
|
ice_is_reset_in_progress(pf->state)) {
|
||||||
|
dev_err(dev, "can't suspend device in reset or already down\n");
|
||||||
|
if (!disabled)
|
||||||
|
ice_service_task_restart(pf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ice_setup_mc_magic_wake(pf);
|
||||||
|
|
||||||
|
ice_prepare_for_shutdown(pf);
|
||||||
|
|
||||||
|
ice_set_wake(pf);
|
||||||
|
|
||||||
|
/* Free vectors, clear the interrupt scheme and release IRQs
|
||||||
|
* for proper hibernation, especially with large number of CPUs.
|
||||||
|
* Otherwise hibernation might fail when mapping all the vectors back
|
||||||
|
* to CPU0.
|
||||||
|
*/
|
||||||
|
ice_free_irq_msix_misc(pf);
|
||||||
|
ice_for_each_vsi(pf, v) {
|
||||||
|
if (!pf->vsi[v])
|
||||||
|
continue;
|
||||||
|
ice_vsi_free_q_vectors(pf->vsi[v]);
|
||||||
|
}
|
||||||
|
ice_clear_interrupt_scheme(pf);
|
||||||
|
|
||||||
|
pci_wake_from_d3(pdev, pf->wol_ena);
|
||||||
|
pci_set_power_state(pdev, PCI_D3hot);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ice_resume - PM callback for waking up from D3
|
||||||
|
* @dev: generic device information structure
|
||||||
|
*/
|
||||||
|
static int ice_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct pci_dev *pdev = to_pci_dev(dev);
|
||||||
|
enum ice_reset_req reset_type;
|
||||||
|
struct ice_pf *pf;
|
||||||
|
struct ice_hw *hw;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
pci_set_power_state(pdev, PCI_D0);
|
||||||
|
pci_restore_state(pdev);
|
||||||
|
pci_save_state(pdev);
|
||||||
|
|
||||||
|
if (!pci_device_is_present(pdev))
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
ret = pci_enable_device_mem(pdev);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "Cannot enable device after suspend\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
pf = pci_get_drvdata(pdev);
|
||||||
|
hw = &pf->hw;
|
||||||
|
|
||||||
|
pf->wakeup_reason = rd32(hw, PFPM_WUS);
|
||||||
|
ice_print_wake_reason(pf);
|
||||||
|
|
||||||
|
/* We cleared the interrupt scheme when we suspended, so we need to
|
||||||
|
* restore it now to resume device functionality.
|
||||||
|
*/
|
||||||
|
ret = ice_reinit_interrupt_scheme(pf);
|
||||||
|
if (ret)
|
||||||
|
dev_err(dev, "Cannot restore interrupt scheme: %d\n", ret);
|
||||||
|
|
||||||
|
clear_bit(__ICE_DOWN, pf->state);
|
||||||
|
/* Now perform PF reset and rebuild */
|
||||||
|
reset_type = ICE_RESET_PFR;
|
||||||
|
/* re-enable service task for reset, but allow reset to schedule it */
|
||||||
|
clear_bit(__ICE_SERVICE_DIS, pf->state);
|
||||||
|
|
||||||
|
if (ice_schedule_reset(pf, reset_type))
|
||||||
|
dev_err(dev, "Reset during resume failed.\n");
|
||||||
|
|
||||||
|
clear_bit(__ICE_SUSPENDED, pf->state);
|
||||||
|
ice_service_task_restart(pf);
|
||||||
|
|
||||||
|
/* Restart the service task */
|
||||||
|
mod_timer(&pf->serv_tmr, round_jiffies(jiffies + pf->serv_tmr_period));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_PM */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ice_pci_err_detected - warning that PCI error has been detected
|
* ice_pci_err_detected - warning that PCI error has been detected
|
||||||
* @pdev: PCI device information struct
|
* @pdev: PCI device information struct
|
||||||
|
@ -3734,6 +4087,8 @@ static const struct pci_device_id ice_pci_tbl[] = {
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(pci, ice_pci_tbl);
|
MODULE_DEVICE_TABLE(pci, ice_pci_tbl);
|
||||||
|
|
||||||
|
static __maybe_unused SIMPLE_DEV_PM_OPS(ice_pm_ops, ice_suspend, ice_resume);
|
||||||
|
|
||||||
static const struct pci_error_handlers ice_pci_err_handler = {
|
static const struct pci_error_handlers ice_pci_err_handler = {
|
||||||
.error_detected = ice_pci_err_detected,
|
.error_detected = ice_pci_err_detected,
|
||||||
.slot_reset = ice_pci_err_slot_reset,
|
.slot_reset = ice_pci_err_slot_reset,
|
||||||
|
@ -3747,6 +4102,10 @@ static struct pci_driver ice_driver = {
|
||||||
.id_table = ice_pci_tbl,
|
.id_table = ice_pci_tbl,
|
||||||
.probe = ice_probe,
|
.probe = ice_probe,
|
||||||
.remove = ice_remove,
|
.remove = ice_remove,
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
.driver.pm = &ice_pm_ops,
|
||||||
|
#endif /* CONFIG_PM */
|
||||||
|
.shutdown = ice_shutdown,
|
||||||
.sriov_configure = ice_sriov_configure,
|
.sriov_configure = ice_sriov_configure,
|
||||||
.err_handler = &ice_pci_err_handler
|
.err_handler = &ice_pci_err_handler
|
||||||
};
|
};
|
||||||
|
|
|
@ -172,8 +172,7 @@ void ice_release_nvm(struct ice_hw *hw)
|
||||||
*
|
*
|
||||||
* Reads one 16 bit word from the Shadow RAM using the ice_read_sr_word_aq.
|
* Reads one 16 bit word from the Shadow RAM using the ice_read_sr_word_aq.
|
||||||
*/
|
*/
|
||||||
static enum ice_status
|
enum ice_status ice_read_sr_word(struct ice_hw *hw, u16 offset, u16 *data)
|
||||||
ice_read_sr_word(struct ice_hw *hw, u16 offset, u16 *data)
|
|
||||||
{
|
{
|
||||||
enum ice_status status;
|
enum ice_status status;
|
||||||
|
|
||||||
|
|
|
@ -13,4 +13,5 @@ ice_read_flat_nvm(struct ice_hw *hw, u32 offset, u32 *length, u8 *data,
|
||||||
enum ice_status
|
enum ice_status
|
||||||
ice_read_pba_string(struct ice_hw *hw, u8 *pba_num, u32 pba_num_size);
|
ice_read_pba_string(struct ice_hw *hw, u8 *pba_num, u32 pba_num_size);
|
||||||
enum ice_status ice_init_nvm(struct ice_hw *hw);
|
enum ice_status ice_init_nvm(struct ice_hw *hw);
|
||||||
|
enum ice_status ice_read_sr_word(struct ice_hw *hw, u16 offset, u16 *data);
|
||||||
#endif /* _ICE_NVM_H_ */
|
#endif /* _ICE_NVM_H_ */
|
||||||
|
|
|
@ -709,6 +709,7 @@ struct ice_hw_port_stats {
|
||||||
|
|
||||||
/* Checksum and Shadow RAM pointers */
|
/* Checksum and Shadow RAM pointers */
|
||||||
#define ICE_SR_BOOT_CFG_PTR 0x132
|
#define ICE_SR_BOOT_CFG_PTR 0x132
|
||||||
|
#define ICE_SR_NVM_WOL_CFG 0x19
|
||||||
#define ICE_NVM_OROM_VER_OFF 0x02
|
#define ICE_NVM_OROM_VER_OFF 0x02
|
||||||
#define ICE_SR_PBA_BLOCK_PTR 0x16
|
#define ICE_SR_PBA_BLOCK_PTR 0x16
|
||||||
#define ICE_SR_NVM_DEV_STARTER_VER 0x18
|
#define ICE_SR_NVM_DEV_STARTER_VER 0x18
|
||||||
|
|
|
@ -1592,31 +1592,6 @@ static int ice_ena_vfs(struct ice_pf *pf, u16 num_vfs)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* ice_pf_state_is_nominal - checks the PF for nominal state
|
|
||||||
* @pf: pointer to PF to check
|
|
||||||
*
|
|
||||||
* Check the PF's state for a collection of bits that would indicate
|
|
||||||
* the PF is in a state that would inhibit normal operation for
|
|
||||||
* driver functionality.
|
|
||||||
*
|
|
||||||
* Returns true if PF is in a nominal state.
|
|
||||||
* Returns false otherwise
|
|
||||||
*/
|
|
||||||
static bool ice_pf_state_is_nominal(struct ice_pf *pf)
|
|
||||||
{
|
|
||||||
DECLARE_BITMAP(check_bits, __ICE_STATE_NBITS) = { 0 };
|
|
||||||
|
|
||||||
if (!pf)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
bitmap_set(check_bits, 0, __ICE_STATE_NOMINAL_CHECK_BITS);
|
|
||||||
if (bitmap_intersects(pf->state, check_bits, __ICE_STATE_NBITS))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ice_pci_sriov_ena - Enable or change number of VFs
|
* ice_pci_sriov_ena - Enable or change number of VFs
|
||||||
* @pf: pointer to the PF structure
|
* @pf: pointer to the PF structure
|
||||||
|
|
Loading…
Reference in New Issue