Commit 14d39648 authored by Mintz, Yuval's avatar Mintz, Yuval Committed by David S. Miller

qed*: Add support for WoL

Signed-off-by: default avatarYuval Mintz <Yuval.Mintz@cavium.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 7a4b21b7
...@@ -195,6 +195,11 @@ enum qed_dev_cap { ...@@ -195,6 +195,11 @@ enum qed_dev_cap {
QED_DEV_CAP_ROCE, QED_DEV_CAP_ROCE,
}; };
enum qed_wol_support {
QED_WOL_SUPPORT_NONE,
QED_WOL_SUPPORT_PME,
};
struct qed_hw_info { struct qed_hw_info {
/* PCI personality */ /* PCI personality */
enum qed_pci_personality personality; enum qed_pci_personality personality;
...@@ -227,6 +232,8 @@ struct qed_hw_info { ...@@ -227,6 +232,8 @@ struct qed_hw_info {
u32 hw_mode; u32 hw_mode;
unsigned long device_capabilities; unsigned long device_capabilities;
u16 mtu; u16 mtu;
enum qed_wol_support b_wol_support;
}; };
struct qed_hw_cid_data { struct qed_hw_cid_data {
...@@ -539,7 +546,9 @@ struct qed_dev { ...@@ -539,7 +546,9 @@ struct qed_dev {
u8 mcp_rev; u8 mcp_rev;
u8 boot_mode; u8 boot_mode;
u8 wol; /* WoL related configurations */
u8 wol_config;
u8 wol_mac[ETH_ALEN];
u32 int_mode; u32 int_mode;
enum qed_coalescing_mode int_coalescing_mode; enum qed_coalescing_mode int_coalescing_mode;
......
...@@ -1364,8 +1364,24 @@ int qed_hw_reset(struct qed_dev *cdev) ...@@ -1364,8 +1364,24 @@ int qed_hw_reset(struct qed_dev *cdev)
{ {
int rc = 0; int rc = 0;
u32 unload_resp, unload_param; u32 unload_resp, unload_param;
u32 wol_param;
int i; int i;
switch (cdev->wol_config) {
case QED_OV_WOL_DISABLED:
wol_param = DRV_MB_PARAM_UNLOAD_WOL_DISABLED;
break;
case QED_OV_WOL_ENABLED:
wol_param = DRV_MB_PARAM_UNLOAD_WOL_ENABLED;
break;
default:
DP_NOTICE(cdev,
"Unknown WoL configuration %02x\n", cdev->wol_config);
/* Fallthrough */
case QED_OV_WOL_DEFAULT:
wol_param = DRV_MB_PARAM_UNLOAD_WOL_MCP;
}
for_each_hwfn(cdev, i) { for_each_hwfn(cdev, i) {
struct qed_hwfn *p_hwfn = &cdev->hwfns[i]; struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
...@@ -1394,8 +1410,7 @@ int qed_hw_reset(struct qed_dev *cdev) ...@@ -1394,8 +1410,7 @@ int qed_hw_reset(struct qed_dev *cdev)
/* Send unload command to MCP */ /* Send unload command to MCP */
rc = qed_mcp_cmd(p_hwfn, p_hwfn->p_main_ptt, rc = qed_mcp_cmd(p_hwfn, p_hwfn->p_main_ptt,
DRV_MSG_CODE_UNLOAD_REQ, DRV_MSG_CODE_UNLOAD_REQ, wol_param,
DRV_MB_PARAM_UNLOAD_WOL_MCP,
&unload_resp, &unload_param); &unload_resp, &unload_param);
if (rc) { if (rc) {
DP_NOTICE(p_hwfn, "qed_hw_reset: UNLOAD_REQ failed\n"); DP_NOTICE(p_hwfn, "qed_hw_reset: UNLOAD_REQ failed\n");
......
...@@ -8601,6 +8601,7 @@ struct public_drv_mb { ...@@ -8601,6 +8601,7 @@ struct public_drv_mb {
#define DRV_MSG_CODE_BIST_TEST 0x001e0000 #define DRV_MSG_CODE_BIST_TEST 0x001e0000
#define DRV_MSG_CODE_SET_LED_MODE 0x00200000 #define DRV_MSG_CODE_SET_LED_MODE 0x00200000
#define DRV_MSG_CODE_OS_WOL 0x002e0000
#define DRV_MSG_SEQ_NUMBER_MASK 0x0000ffff #define DRV_MSG_SEQ_NUMBER_MASK 0x0000ffff
...@@ -8697,6 +8698,9 @@ struct public_drv_mb { ...@@ -8697,6 +8698,9 @@ struct public_drv_mb {
#define FW_MSG_CODE_NVM_OK 0x00010000 #define FW_MSG_CODE_NVM_OK 0x00010000
#define FW_MSG_CODE_OK 0x00160000 #define FW_MSG_CODE_OK 0x00160000
#define FW_MSG_CODE_OS_WOL_SUPPORTED 0x00800000
#define FW_MSG_CODE_OS_WOL_NOT_SUPPORTED 0x00810000
#define FW_MSG_SEQ_NUMBER_MASK 0x0000ffff #define FW_MSG_SEQ_NUMBER_MASK 0x0000ffff
u32 fw_mb_param; u32 fw_mb_param;
......
...@@ -221,6 +221,10 @@ int qed_fill_dev_info(struct qed_dev *cdev, ...@@ -221,6 +221,10 @@ int qed_fill_dev_info(struct qed_dev *cdev,
dev_info->fw_eng = FW_ENGINEERING_VERSION; dev_info->fw_eng = FW_ENGINEERING_VERSION;
dev_info->mf_mode = cdev->mf_mode; dev_info->mf_mode = cdev->mf_mode;
dev_info->tx_switching = true; dev_info->tx_switching = true;
if (QED_LEADING_HWFN(cdev)->hw_info.b_wol_support ==
QED_WOL_SUPPORT_PME)
dev_info->wol_support = true;
} else { } else {
qed_vf_get_fw_version(&cdev->hwfns[0], &dev_info->fw_major, qed_vf_get_fw_version(&cdev->hwfns[0], &dev_info->fw_major,
&dev_info->fw_minor, &dev_info->fw_rev, &dev_info->fw_minor, &dev_info->fw_rev,
...@@ -1433,6 +1437,30 @@ static int qed_set_led(struct qed_dev *cdev, enum qed_led_mode mode) ...@@ -1433,6 +1437,30 @@ static int qed_set_led(struct qed_dev *cdev, enum qed_led_mode mode)
return status; return status;
} }
static int qed_update_wol(struct qed_dev *cdev, bool enabled)
{
struct qed_hwfn *hwfn = QED_LEADING_HWFN(cdev);
struct qed_ptt *ptt;
int rc = 0;
if (IS_VF(cdev))
return 0;
ptt = qed_ptt_acquire(hwfn);
if (!ptt)
return -EAGAIN;
rc = qed_mcp_ov_update_wol(hwfn, ptt, enabled ? QED_OV_WOL_ENABLED
: QED_OV_WOL_DISABLED);
if (rc)
goto out;
rc = qed_mcp_ov_update_current_config(hwfn, ptt, QED_OV_CLIENT_DRV);
out:
qed_ptt_release(hwfn, ptt);
return rc;
}
static int qed_update_drv_state(struct qed_dev *cdev, bool active) static int qed_update_drv_state(struct qed_dev *cdev, bool active)
{ {
struct qed_hwfn *hwfn = QED_LEADING_HWFN(cdev); struct qed_hwfn *hwfn = QED_LEADING_HWFN(cdev);
...@@ -1541,6 +1569,7 @@ const struct qed_common_ops qed_common_ops_pass = { ...@@ -1541,6 +1569,7 @@ const struct qed_common_ops qed_common_ops_pass = {
.update_drv_state = &qed_update_drv_state, .update_drv_state = &qed_update_drv_state,
.update_mac = &qed_update_mac, .update_mac = &qed_update_mac,
.update_mtu = &qed_update_mtu, .update_mtu = &qed_update_mtu,
.update_wol = &qed_update_wol,
}; };
void qed_get_protocol_stats(struct qed_dev *cdev, void qed_get_protocol_stats(struct qed_dev *cdev,
......
...@@ -330,6 +330,7 @@ static int qed_mcp_cmd_and_union(struct qed_hwfn *p_hwfn, ...@@ -330,6 +330,7 @@ static int qed_mcp_cmd_and_union(struct qed_hwfn *p_hwfn,
struct qed_mcp_mb_params *p_mb_params) struct qed_mcp_mb_params *p_mb_params)
{ {
u32 union_data_addr; u32 union_data_addr;
int rc; int rc;
/* MCP not initialized */ /* MCP not initialized */
...@@ -375,11 +376,32 @@ int qed_mcp_cmd(struct qed_hwfn *p_hwfn, ...@@ -375,11 +376,32 @@ int qed_mcp_cmd(struct qed_hwfn *p_hwfn,
u32 *o_mcp_param) u32 *o_mcp_param)
{ {
struct qed_mcp_mb_params mb_params; struct qed_mcp_mb_params mb_params;
union drv_union_data data_src;
int rc; int rc;
memset(&mb_params, 0, sizeof(mb_params)); memset(&mb_params, 0, sizeof(mb_params));
memset(&data_src, 0, sizeof(data_src));
mb_params.cmd = cmd; mb_params.cmd = cmd;
mb_params.param = param; mb_params.param = param;
/* In case of UNLOAD_DONE, set the primary MAC */
if ((cmd == DRV_MSG_CODE_UNLOAD_DONE) &&
(p_hwfn->cdev->wol_config == QED_OV_WOL_ENABLED)) {
u8 *p_mac = p_hwfn->cdev->wol_mac;
data_src.wol_mac.mac_upper = p_mac[0] << 8 | p_mac[1];
data_src.wol_mac.mac_lower = p_mac[2] << 24 | p_mac[3] << 16 |
p_mac[4] << 8 | p_mac[5];
DP_VERBOSE(p_hwfn,
(QED_MSG_SP | NETIF_MSG_IFDOWN),
"Setting WoL MAC: %pM --> [%08x,%08x]\n",
p_mac, data_src.wol_mac.mac_upper,
data_src.wol_mac.mac_lower);
mb_params.p_data_src = &data_src;
}
rc = qed_mcp_cmd_and_union(p_hwfn, p_ptt, &mb_params); rc = qed_mcp_cmd_and_union(p_hwfn, p_ptt, &mb_params);
if (rc) if (rc)
return rc; return rc;
...@@ -1058,6 +1080,9 @@ int qed_mcp_fill_shmem_func_info(struct qed_hwfn *p_hwfn, ...@@ -1058,6 +1080,9 @@ int qed_mcp_fill_shmem_func_info(struct qed_hwfn *p_hwfn,
info->mac[3] = (u8)(shmem_info.mac_lower >> 16); info->mac[3] = (u8)(shmem_info.mac_lower >> 16);
info->mac[4] = (u8)(shmem_info.mac_lower >> 8); info->mac[4] = (u8)(shmem_info.mac_lower >> 8);
info->mac[5] = (u8)(shmem_info.mac_lower); info->mac[5] = (u8)(shmem_info.mac_lower);
/* Store primary MAC for later possible WoL */
memcpy(&p_hwfn->cdev->wol_mac, info->mac, ETH_ALEN);
} else { } else {
DP_NOTICE(p_hwfn, "MAC is 0 in shmem\n"); DP_NOTICE(p_hwfn, "MAC is 0 in shmem\n");
} }
...@@ -1071,13 +1096,28 @@ int qed_mcp_fill_shmem_func_info(struct qed_hwfn *p_hwfn, ...@@ -1071,13 +1096,28 @@ int qed_mcp_fill_shmem_func_info(struct qed_hwfn *p_hwfn,
info->mtu = (u16)shmem_info.mtu_size; info->mtu = (u16)shmem_info.mtu_size;
p_hwfn->hw_info.b_wol_support = QED_WOL_SUPPORT_NONE;
p_hwfn->cdev->wol_config = (u8)QED_OV_WOL_DEFAULT;
if (qed_mcp_is_init(p_hwfn)) {
u32 resp = 0, param = 0;
int rc;
rc = qed_mcp_cmd(p_hwfn, p_ptt,
DRV_MSG_CODE_OS_WOL, 0, &resp, &param);
if (rc)
return rc;
if (resp == FW_MSG_CODE_OS_WOL_SUPPORTED)
p_hwfn->hw_info.b_wol_support = QED_WOL_SUPPORT_PME;
}
DP_VERBOSE(p_hwfn, (QED_MSG_SP | NETIF_MSG_IFUP), DP_VERBOSE(p_hwfn, (QED_MSG_SP | NETIF_MSG_IFUP),
"Read configuration from shmem: pause_on_host %02x protocol %02x BW [%02x - %02x] MAC %02x:%02x:%02x:%02x:%02x:%02x wwn port %llx node %llx ovlan %04x\n", "Read configuration from shmem: pause_on_host %02x protocol %02x BW [%02x - %02x] MAC %02x:%02x:%02x:%02x:%02x:%02x wwn port %llx node %llx ovlan %04x wol %02x\n",
info->pause_on_host, info->protocol, info->pause_on_host, info->protocol,
info->bandwidth_min, info->bandwidth_max, info->bandwidth_min, info->bandwidth_max,
info->mac[0], info->mac[1], info->mac[2], info->mac[0], info->mac[1], info->mac[2],
info->mac[3], info->mac[4], info->mac[5], info->mac[3], info->mac[4], info->mac[5],
info->wwn_port, info->wwn_node, info->ovlan); info->wwn_port, info->wwn_node,
info->ovlan, (u8)p_hwfn->hw_info.b_wol_support);
return 0; return 0;
} }
...@@ -1322,6 +1362,9 @@ int qed_mcp_ov_update_mac(struct qed_hwfn *p_hwfn, ...@@ -1322,6 +1362,9 @@ int qed_mcp_ov_update_mac(struct qed_hwfn *p_hwfn,
if (rc) if (rc)
DP_ERR(p_hwfn, "Failed to send mac address, rc = %d\n", rc); DP_ERR(p_hwfn, "Failed to send mac address, rc = %d\n", rc);
/* Store primary MAC for later possible WoL */
memcpy(p_hwfn->cdev->wol_mac, mac, ETH_ALEN);
return rc; return rc;
} }
...@@ -1332,6 +1375,12 @@ int qed_mcp_ov_update_wol(struct qed_hwfn *p_hwfn, ...@@ -1332,6 +1375,12 @@ int qed_mcp_ov_update_wol(struct qed_hwfn *p_hwfn,
u32 drv_mb_param; u32 drv_mb_param;
int rc; int rc;
if (p_hwfn->hw_info.b_wol_support == QED_WOL_SUPPORT_NONE) {
DP_VERBOSE(p_hwfn, QED_MSG_SP,
"Can't change WoL configuration when WoL isn't supported\n");
return -EINVAL;
}
switch (wol) { switch (wol) {
case QED_OV_WOL_DEFAULT: case QED_OV_WOL_DEFAULT:
drv_mb_param = DRV_MB_PARAM_WOL_DEFAULT; drv_mb_param = DRV_MB_PARAM_WOL_DEFAULT;
...@@ -1352,6 +1401,9 @@ int qed_mcp_ov_update_wol(struct qed_hwfn *p_hwfn, ...@@ -1352,6 +1401,9 @@ int qed_mcp_ov_update_wol(struct qed_hwfn *p_hwfn,
if (rc) if (rc)
DP_ERR(p_hwfn, "Failed to send wol mode, rc = %d\n", rc); DP_ERR(p_hwfn, "Failed to send wol mode, rc = %d\n", rc);
/* Store the WoL update for a future unload */
p_hwfn->cdev->wol_config = (u8)wol;
return rc; return rc;
} }
......
...@@ -193,6 +193,8 @@ struct qede_dev { ...@@ -193,6 +193,8 @@ struct qede_dev {
u16 vxlan_dst_port; u16 vxlan_dst_port;
u16 geneve_dst_port; u16 geneve_dst_port;
bool wol_enabled;
struct qede_rdma_dev rdma_info; struct qede_rdma_dev rdma_info;
}; };
......
...@@ -483,6 +483,45 @@ static void qede_get_drvinfo(struct net_device *ndev, ...@@ -483,6 +483,45 @@ static void qede_get_drvinfo(struct net_device *ndev,
strlcpy(info->bus_info, pci_name(edev->pdev), sizeof(info->bus_info)); strlcpy(info->bus_info, pci_name(edev->pdev), sizeof(info->bus_info));
} }
static void qede_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol)
{
struct qede_dev *edev = netdev_priv(ndev);
if (edev->dev_info.common.wol_support) {
wol->supported = WAKE_MAGIC;
wol->wolopts = edev->wol_enabled ? WAKE_MAGIC : 0;
}
}
static int qede_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol)
{
struct qede_dev *edev = netdev_priv(ndev);
bool wol_requested;
int rc;
if (wol->wolopts & ~WAKE_MAGIC) {
DP_INFO(edev,
"Can't support WoL options other than magic-packet\n");
return -EINVAL;
}
wol_requested = !!(wol->wolopts & WAKE_MAGIC);
if (wol_requested == edev->wol_enabled)
return 0;
/* Need to actually change configuration */
if (!edev->dev_info.common.wol_support) {
DP_INFO(edev, "Device doesn't support WoL\n");
return -EINVAL;
}
rc = edev->ops->common->update_wol(edev->cdev, wol_requested);
if (!rc)
edev->wol_enabled = wol_requested;
return rc;
}
static u32 qede_get_msglevel(struct net_device *ndev) static u32 qede_get_msglevel(struct net_device *ndev)
{ {
struct qede_dev *edev = netdev_priv(ndev); struct qede_dev *edev = netdev_priv(ndev);
...@@ -1449,6 +1488,8 @@ static const struct ethtool_ops qede_ethtool_ops = { ...@@ -1449,6 +1488,8 @@ static const struct ethtool_ops qede_ethtool_ops = {
.get_drvinfo = qede_get_drvinfo, .get_drvinfo = qede_get_drvinfo,
.get_regs_len = qede_get_regs_len, .get_regs_len = qede_get_regs_len,
.get_regs = qede_get_regs, .get_regs = qede_get_regs,
.get_wol = qede_get_wol,
.set_wol = qede_set_wol,
.get_msglevel = qede_get_msglevel, .get_msglevel = qede_get_msglevel,
.set_msglevel = qede_set_msglevel, .set_msglevel = qede_set_msglevel,
.nway_reset = qede_nway_reset, .nway_reset = qede_nway_reset,
......
...@@ -95,6 +95,7 @@ static int qede_probe(struct pci_dev *pdev, const struct pci_device_id *id); ...@@ -95,6 +95,7 @@ static int qede_probe(struct pci_dev *pdev, const struct pci_device_id *id);
#define TX_TIMEOUT (5 * HZ) #define TX_TIMEOUT (5 * HZ)
static void qede_remove(struct pci_dev *pdev); static void qede_remove(struct pci_dev *pdev);
static void qede_shutdown(struct pci_dev *pdev);
static int qede_alloc_rx_buffer(struct qede_dev *edev, static int qede_alloc_rx_buffer(struct qede_dev *edev,
struct qede_rx_queue *rxq); struct qede_rx_queue *rxq);
static void qede_link_update(void *dev, struct qed_link_output *link); static void qede_link_update(void *dev, struct qed_link_output *link);
...@@ -166,6 +167,7 @@ static struct pci_driver qede_pci_driver = { ...@@ -166,6 +167,7 @@ static struct pci_driver qede_pci_driver = {
.id_table = qede_pci_tbl, .id_table = qede_pci_tbl,
.probe = qede_probe, .probe = qede_probe,
.remove = qede_remove, .remove = qede_remove,
.shutdown = qede_shutdown,
#ifdef CONFIG_QED_SRIOV #ifdef CONFIG_QED_SRIOV
.sriov_configure = qede_sriov_configure, .sriov_configure = qede_sriov_configure,
#endif #endif
...@@ -2705,6 +2707,8 @@ static void __qede_remove(struct pci_dev *pdev, enum qede_remove_mode mode) ...@@ -2705,6 +2707,8 @@ static void __qede_remove(struct pci_dev *pdev, enum qede_remove_mode mode)
/* Use global ops since we've freed edev */ /* Use global ops since we've freed edev */
qed_ops->common->slowpath_stop(cdev); qed_ops->common->slowpath_stop(cdev);
if (system_state == SYSTEM_POWER_OFF)
return;
qed_ops->common->remove(cdev); qed_ops->common->remove(cdev);
dev_info(&pdev->dev, "Ending qede_remove successfully\n"); dev_info(&pdev->dev, "Ending qede_remove successfully\n");
...@@ -2715,6 +2719,11 @@ static void qede_remove(struct pci_dev *pdev) ...@@ -2715,6 +2719,11 @@ static void qede_remove(struct pci_dev *pdev)
__qede_remove(pdev, QEDE_REMOVE_NORMAL); __qede_remove(pdev, QEDE_REMOVE_NORMAL);
} }
static void qede_shutdown(struct pci_dev *pdev)
{
__qede_remove(pdev, QEDE_REMOVE_NORMAL);
}
/* ------------------------------------------------------------------------- /* -------------------------------------------------------------------------
* START OF LOAD / UNLOAD * START OF LOAD / UNLOAD
* ------------------------------------------------------------------------- * -------------------------------------------------------------------------
......
...@@ -268,6 +268,8 @@ struct qed_dev_info { ...@@ -268,6 +268,8 @@ struct qed_dev_info {
bool tx_switching; bool tx_switching;
bool rdma_supported; bool rdma_supported;
u16 mtu; u16 mtu;
bool wol_support;
}; };
enum qed_sb_type { enum qed_sb_type {
...@@ -591,6 +593,14 @@ struct qed_common_ops { ...@@ -591,6 +593,14 @@ struct qed_common_ops {
* *
*/ */
int (*update_mtu)(struct qed_dev *cdev, u16 mtu); int (*update_mtu)(struct qed_dev *cdev, u16 mtu);
/**
* @brief update_wol - update of changes in the WoL configuration
*
* @param cdev
* @param enabled - true iff WoL should be enabled.
*/
int (*update_wol) (struct qed_dev *cdev, bool enabled);
}; };
#define MASK_FIELD(_name, _value) \ #define MASK_FIELD(_name, _value) \
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment