qed*: Add support for WoL
authorMintz, Yuval <Yuval.Mintz@cavium.com>
Mon, 31 Oct 2016 05:14:23 +0000 (07:14 +0200)
committerDavid S. Miller <davem@davemloft.net>
Mon, 31 Oct 2016 19:52:35 +0000 (15:52 -0400)
Signed-off-by: Yuval Mintz <Yuval.Mintz@cavium.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/qlogic/qed/qed.h
drivers/net/ethernet/qlogic/qed/qed_dev.c
drivers/net/ethernet/qlogic/qed/qed_hsi.h
drivers/net/ethernet/qlogic/qed/qed_main.c
drivers/net/ethernet/qlogic/qed/qed_mcp.c
drivers/net/ethernet/qlogic/qede/qede.h
drivers/net/ethernet/qlogic/qede/qede_ethtool.c
drivers/net/ethernet/qlogic/qede/qede_main.c
include/linux/qed/qed_if.h

index f20243c1fb0b2ac59ff9ed84914314e47ef1fe5f..8828ffac4b231c1a4a6003e9b07a0e7b2c2c9b5c 100644 (file)
@@ -195,6 +195,11 @@ enum qed_dev_cap {
        QED_DEV_CAP_ROCE,
 };
 
+enum qed_wol_support {
+       QED_WOL_SUPPORT_NONE,
+       QED_WOL_SUPPORT_PME,
+};
+
 struct qed_hw_info {
        /* PCI personality */
        enum qed_pci_personality        personality;
@@ -227,6 +232,8 @@ struct qed_hw_info {
        u32                             hw_mode;
        unsigned long           device_capabilities;
        u16                             mtu;
+
+       enum qed_wol_support b_wol_support;
 };
 
 struct qed_hw_cid_data {
@@ -539,7 +546,9 @@ struct qed_dev {
        u8                              mcp_rev;
        u8                              boot_mode;
 
-       u8                              wol;
+       /* WoL related configurations */
+       u8 wol_config;
+       u8 wol_mac[ETH_ALEN];
 
        u32                             int_mode;
        enum qed_coalescing_mode        int_coalescing_mode;
index 33fd69e24baec6694a85f71ff152123096754104..127ed5f27d8d9e18da80e46f0a9dadf4f4a08ac2 100644 (file)
@@ -1364,8 +1364,24 @@ int qed_hw_reset(struct qed_dev *cdev)
 {
        int rc = 0;
        u32 unload_resp, unload_param;
+       u32 wol_param;
        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) {
                struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
 
@@ -1394,8 +1410,7 @@ int qed_hw_reset(struct qed_dev *cdev)
 
                /* Send unload command to MCP */
                rc = qed_mcp_cmd(p_hwfn, p_hwfn->p_main_ptt,
-                                DRV_MSG_CODE_UNLOAD_REQ,
-                                DRV_MB_PARAM_UNLOAD_WOL_MCP,
+                                DRV_MSG_CODE_UNLOAD_REQ, wol_param,
                                 &unload_resp, &unload_param);
                if (rc) {
                        DP_NOTICE(p_hwfn, "qed_hw_reset: UNLOAD_REQ failed\n");
index f7dfa2ec2d19158cd4c9e3f099e526e2b07dab99..fdb7a099955b8098a6424eef390de08a39846f5c 100644 (file)
@@ -8601,6 +8601,7 @@ struct public_drv_mb {
 
 #define DRV_MSG_CODE_BIST_TEST                 0x001e0000
 #define DRV_MSG_CODE_SET_LED_MODE              0x00200000
+#define DRV_MSG_CODE_OS_WOL                    0x002e0000
 
 #define DRV_MSG_SEQ_NUMBER_MASK                        0x0000ffff
 
@@ -8697,6 +8698,9 @@ struct public_drv_mb {
 #define FW_MSG_CODE_NVM_OK                     0x00010000
 #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
 
        u32 fw_mb_param;
index 31f8e420c8306878e6ab40a7623c96cd1b43ad9a..b71d73a41b10fd3edfb7b0f23156634191211ae5 100644 (file)
@@ -221,6 +221,10 @@ int qed_fill_dev_info(struct qed_dev *cdev,
                dev_info->fw_eng = FW_ENGINEERING_VERSION;
                dev_info->mf_mode = cdev->mf_mode;
                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 {
                qed_vf_get_fw_version(&cdev->hwfns[0], &dev_info->fw_major,
                                      &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)
        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)
 {
        struct qed_hwfn *hwfn = QED_LEADING_HWFN(cdev);
@@ -1541,6 +1569,7 @@ const struct qed_common_ops qed_common_ops_pass = {
        .update_drv_state = &qed_update_drv_state,
        .update_mac = &qed_update_mac,
        .update_mtu = &qed_update_mtu,
+       .update_wol = &qed_update_wol,
 };
 
 void qed_get_protocol_stats(struct qed_dev *cdev,
index 8be61570ce6b91bdcd1ed509a463fad9612ba419..768b35b1dca09bf37d3ccd7e457ab22b0b4be6da 100644 (file)
@@ -330,6 +330,7 @@ static int qed_mcp_cmd_and_union(struct qed_hwfn *p_hwfn,
                                 struct qed_mcp_mb_params *p_mb_params)
 {
        u32 union_data_addr;
+
        int rc;
 
        /* MCP not initialized */
@@ -375,11 +376,32 @@ int qed_mcp_cmd(struct qed_hwfn *p_hwfn,
                u32 *o_mcp_param)
 {
        struct qed_mcp_mb_params mb_params;
+       union drv_union_data data_src;
        int rc;
 
        memset(&mb_params, 0, sizeof(mb_params));
+       memset(&data_src, 0, sizeof(data_src));
        mb_params.cmd = cmd;
        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);
        if (rc)
                return rc;
@@ -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[4] = (u8)(shmem_info.mac_lower >> 8);
                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 {
                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,
 
        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),
-                  "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->bandwidth_min, info->bandwidth_max,
                info->mac[0], info->mac[1], info->mac[2],
                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;
 }
@@ -1322,6 +1362,9 @@ int qed_mcp_ov_update_mac(struct qed_hwfn *p_hwfn,
        if (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;
 }
 
@@ -1332,6 +1375,12 @@ int qed_mcp_ov_update_wol(struct qed_hwfn *p_hwfn,
        u32 drv_mb_param;
        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) {
        case QED_OV_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,
        if (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;
 }
 
index cf8d3547aecf8bba81fc441f260a376c346d635b..0cba21bf9d5f3f1c962f5bc34b2741f4950fef1d 100644 (file)
@@ -193,6 +193,8 @@ struct qede_dev {
        u16                             vxlan_dst_port;
        u16                             geneve_dst_port;
 
+       bool wol_enabled;
+
        struct qede_rdma_dev            rdma_info;
 };
 
index a8094088b9acb49916d5ad14d8c8a76d11a3bb35..327c614e76aab33a97aa70b4e709c7a75cd9dd6e 100644 (file)
@@ -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));
 }
 
+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)
 {
        struct qede_dev *edev = netdev_priv(ndev);
@@ -1449,6 +1488,8 @@ static const struct ethtool_ops qede_ethtool_ops = {
        .get_drvinfo = qede_get_drvinfo,
        .get_regs_len = qede_get_regs_len,
        .get_regs = qede_get_regs,
+       .get_wol = qede_get_wol,
+       .set_wol = qede_set_wol,
        .get_msglevel = qede_get_msglevel,
        .set_msglevel = qede_set_msglevel,
        .nway_reset = qede_nway_reset,
index df0bd0ce2b188a825b2ae1a6fdd8945f99b668db..873f2ebe249edc3a97e40e2d362e320097751ea5 100644 (file)
@@ -95,6 +95,7 @@ static int qede_probe(struct pci_dev *pdev, const struct pci_device_id *id);
 #define TX_TIMEOUT             (5 * HZ)
 
 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,
                                struct qede_rx_queue *rxq);
 static void qede_link_update(void *dev, struct qed_link_output *link);
@@ -166,6 +167,7 @@ static struct pci_driver qede_pci_driver = {
        .id_table = qede_pci_tbl,
        .probe = qede_probe,
        .remove = qede_remove,
+       .shutdown = qede_shutdown,
 #ifdef CONFIG_QED_SRIOV
        .sriov_configure = qede_sriov_configure,
 #endif
@@ -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 */
        qed_ops->common->slowpath_stop(cdev);
+       if (system_state == SYSTEM_POWER_OFF)
+               return;
        qed_ops->common->remove(cdev);
 
        dev_info(&pdev->dev, "Ending qede_remove successfully\n");
@@ -2715,6 +2719,11 @@ static void qede_remove(struct pci_dev *pdev)
        __qede_remove(pdev, QEDE_REMOVE_NORMAL);
 }
 
+static void qede_shutdown(struct pci_dev *pdev)
+{
+       __qede_remove(pdev, QEDE_REMOVE_NORMAL);
+}
+
 /* -------------------------------------------------------------------------
  * START OF LOAD / UNLOAD
  * -------------------------------------------------------------------------
index ffc2d2f5e88fb72931e5e55c2e9471be4d30da6a..ea095b4893aa45f4affdd4f8d49f07d2f5dec52d 100644 (file)
@@ -268,6 +268,8 @@ struct qed_dev_info {
        bool            tx_switching;
        bool            rdma_supported;
        u16             mtu;
+
+       bool wol_support;
 };
 
 enum qed_sb_type {
@@ -591,6 +593,14 @@ struct qed_common_ops {
  *
  */
        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) \