qlge: Add ethtool wake on LAN function.
authorRon Mercer <ron.mercer@qlogic.com>
Wed, 21 Oct 2009 11:07:40 +0000 (11:07 +0000)
committerDavid S. Miller <davem@davemloft.net>
Thu, 22 Oct 2009 04:45:40 +0000 (21:45 -0700)
Signed-off-by: Ron Mercer <ron.mercer@qlogic.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/qlge/qlge.h
drivers/net/qlge/qlge_ethtool.c
drivers/net/qlge/qlge_main.c
drivers/net/qlge/qlge_mpi.c

index a5bb3a5..ba29776 100644 (file)
@@ -796,6 +796,7 @@ enum {
        MB_WOL_BCAST = (1 << 5),
        MB_WOL_LINK_UP = (1 << 6),
        MB_WOL_LINK_DOWN = (1 << 7),
+       MB_WOL_MODE_ON = (1 << 16),             /* Wake on Lan Mode on */
        MB_CMD_SET_WOL_FLTR = 0x00000111,       /* Wake On Lan Filter */
        MB_CMD_CLEAR_WOL_FLTR = 0x00000112, /* Wake On Lan Filter */
        MB_CMD_SET_WOL_MAGIC = 0x00000113,      /* Wake On Lan Magic Packet */
@@ -1646,6 +1647,9 @@ int ql_mb_get_fw_state(struct ql_adapter *qdev);
 int ql_cam_route_initialize(struct ql_adapter *qdev);
 int ql_read_mpi_reg(struct ql_adapter *qdev, u32 reg, u32 *data);
 int ql_mb_about_fw(struct ql_adapter *qdev);
+int ql_wol(struct ql_adapter *qdev);
+int ql_mb_wol_set_magic(struct ql_adapter *qdev, u32 enable_wol);
+int ql_mb_wol_mode(struct ql_adapter *qdev, u32 wol);
 int ql_mb_set_led_cfg(struct ql_adapter *qdev, u32 led_config);
 int ql_mb_get_led_cfg(struct ql_adapter *qdev);
 void ql_link_on(struct ql_adapter *qdev);
index 0c0549b..019f35f 100644 (file)
@@ -371,6 +371,36 @@ static void ql_get_drvinfo(struct net_device *ndev,
        drvinfo->eedump_len = 0;
 }
 
+static void ql_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol)
+{
+       struct ql_adapter *qdev = netdev_priv(ndev);
+       /* What we support. */
+       wol->supported = WAKE_MAGIC;
+       /* What we've currently got set. */
+       wol->wolopts = qdev->wol;
+}
+
+static int ql_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol)
+{
+       struct ql_adapter *qdev = netdev_priv(ndev);
+       int status;
+
+       if (wol->wolopts & ~WAKE_MAGIC)
+               return -EINVAL;
+       qdev->wol = wol->wolopts;
+
+       QPRINTK(qdev, DRV, INFO, "Set wol option 0x%x on %s\n",
+                        qdev->wol, ndev->name);
+       if (!qdev->wol) {
+               u32 wol = 0;
+               status = ql_mb_wol_mode(qdev, wol);
+               QPRINTK(qdev, DRV, ERR, "WOL %s (wol code 0x%x) on %s\n",
+                       (status == 0) ? "cleared sucessfully" : "clear failed",
+                       wol, qdev->ndev->name);
+       }
+
+       return 0;
+}
 
 static int ql_phys_id(struct net_device *ndev, u32 data)
 {
@@ -523,6 +553,8 @@ static void ql_set_msglevel(struct net_device *ndev, u32 value)
 const struct ethtool_ops qlge_ethtool_ops = {
        .get_settings = ql_get_settings,
        .get_drvinfo = ql_get_drvinfo,
+       .get_wol = ql_get_wol,
+       .set_wol = ql_set_wol,
        .get_msglevel = ql_get_msglevel,
        .set_msglevel = ql_set_msglevel,
        .get_link = ethtool_op_get_link,
index 34242fb..dd0ea02 100644 (file)
@@ -3335,6 +3335,22 @@ static int ql_adapter_initialize(struct ql_adapter *qdev)
         * the same MAC address.
         */
        ql_write32(qdev, RST_FO, RST_FO_RR_MASK | RST_FO_RR_RCV_FUNC_CQ);
+       /* Reroute all packets to our Interface.
+        * They may have been routed to MPI firmware
+        * due to WOL.
+        */
+       value = ql_read32(qdev, MGMT_RCV_CFG);
+       value &= ~MGMT_RCV_CFG_RM;
+       mask = 0xffff0000;
+
+       /* Sticky reg needs clearing due to WOL. */
+       ql_write32(qdev, MGMT_RCV_CFG, mask);
+       ql_write32(qdev, MGMT_RCV_CFG, mask | value);
+
+       /* Default WOL is enable on Mezz cards */
+       if (qdev->pdev->subsystem_device == 0x0068 ||
+                       qdev->pdev->subsystem_device == 0x0180)
+               qdev->wol = WAKE_MAGIC;
 
        /* Start up the rx queues. */
        for (i = 0; i < qdev->rx_ring_count; i++) {
@@ -3449,6 +3465,55 @@ static void ql_display_dev_info(struct net_device *ndev)
        QPRINTK(qdev, PROBE, INFO, "MAC address %pM\n", ndev->dev_addr);
 }
 
+int ql_wol(struct ql_adapter *qdev)
+{
+       int status = 0;
+       u32 wol = MB_WOL_DISABLE;
+
+       /* The CAM is still intact after a reset, but if we
+        * are doing WOL, then we may need to program the
+        * routing regs. We would also need to issue the mailbox
+        * commands to instruct the MPI what to do per the ethtool
+        * settings.
+        */
+
+       if (qdev->wol & (WAKE_ARP | WAKE_MAGICSECURE | WAKE_PHY | WAKE_UCAST |
+                       WAKE_MCAST | WAKE_BCAST)) {
+               QPRINTK(qdev, IFDOWN, ERR,
+                       "Unsupported WOL paramter. qdev->wol = 0x%x.\n",
+                       qdev->wol);
+               return -EINVAL;
+       }
+
+       if (qdev->wol & WAKE_MAGIC) {
+               status = ql_mb_wol_set_magic(qdev, 1);
+               if (status) {
+                       QPRINTK(qdev, IFDOWN, ERR,
+                               "Failed to set magic packet on %s.\n",
+                               qdev->ndev->name);
+                       return status;
+               } else
+                       QPRINTK(qdev, DRV, INFO,
+                               "Enabled magic packet successfully on %s.\n",
+                               qdev->ndev->name);
+
+               wol |= MB_WOL_MAGIC_PKT;
+       }
+
+       if (qdev->wol) {
+               /* Reroute all packets to Management Interface */
+               ql_write32(qdev, MGMT_RCV_CFG, (MGMT_RCV_CFG_RM |
+                       (MGMT_RCV_CFG_RM << 16)));
+               wol |= MB_WOL_MODE_ON;
+               status = ql_mb_wol_mode(qdev, wol);
+               QPRINTK(qdev, DRV, ERR, "WOL %s (wol code 0x%x) on %s\n",
+                       (status == 0) ? "Sucessfully set" : "Failed", wol,
+                       qdev->ndev->name);
+       }
+
+       return status;
+}
+
 static int ql_adapter_down(struct ql_adapter *qdev)
 {
        int i, status = 0;
@@ -4285,6 +4350,7 @@ static int qlge_suspend(struct pci_dev *pdev, pm_message_t state)
                        return err;
        }
 
+       ql_wol(qdev);
        err = pci_save_state(pdev);
        if (err)
                return err;
index 14d76f1..80b6853 100644 (file)
@@ -702,6 +702,76 @@ int ql_mb_get_port_cfg(struct ql_adapter *qdev)
        return status;
 }
 
+int ql_mb_wol_mode(struct ql_adapter *qdev, u32 wol)
+{
+       struct mbox_params mbc;
+       struct mbox_params *mbcp = &mbc;
+       int status;
+
+       memset(mbcp, 0, sizeof(struct mbox_params));
+
+       mbcp->in_count = 2;
+       mbcp->out_count = 1;
+
+       mbcp->mbox_in[0] = MB_CMD_SET_WOL_MODE;
+       mbcp->mbox_in[1] = wol;
+
+
+       status = ql_mailbox_command(qdev, mbcp);
+       if (status)
+               return status;
+
+       if (mbcp->mbox_out[0] != MB_CMD_STS_GOOD) {
+               QPRINTK(qdev, DRV, ERR,
+                       "Failed to set WOL mode.\n");
+               status = -EIO;
+       }
+       return status;
+}
+
+int ql_mb_wol_set_magic(struct ql_adapter *qdev, u32 enable_wol)
+{
+       struct mbox_params mbc;
+       struct mbox_params *mbcp = &mbc;
+       int status;
+       u8 *addr = qdev->ndev->dev_addr;
+
+       memset(mbcp, 0, sizeof(struct mbox_params));
+
+       mbcp->in_count = 8;
+       mbcp->out_count = 1;
+
+       mbcp->mbox_in[0] = MB_CMD_SET_WOL_MAGIC;
+       if (enable_wol) {
+               mbcp->mbox_in[1] = (u32)addr[0];
+               mbcp->mbox_in[2] = (u32)addr[1];
+               mbcp->mbox_in[3] = (u32)addr[2];
+               mbcp->mbox_in[4] = (u32)addr[3];
+               mbcp->mbox_in[5] = (u32)addr[4];
+               mbcp->mbox_in[6] = (u32)addr[5];
+               mbcp->mbox_in[7] = 0;
+       } else {
+               mbcp->mbox_in[1] = 0;
+               mbcp->mbox_in[2] = 1;
+               mbcp->mbox_in[3] = 1;
+               mbcp->mbox_in[4] = 1;
+               mbcp->mbox_in[5] = 1;
+               mbcp->mbox_in[6] = 1;
+               mbcp->mbox_in[7] = 0;
+       }
+
+       status = ql_mailbox_command(qdev, mbcp);
+       if (status)
+               return status;
+
+       if (mbcp->mbox_out[0] != MB_CMD_STS_GOOD) {
+               QPRINTK(qdev, DRV, ERR,
+                       "Failed to set WOL mode.\n");
+               status = -EIO;
+       }
+       return status;
+}
+
 /* IDC - Inter Device Communication...
  * Some firmware commands require consent of adjacent FCOE
  * function.  This function waits for the OK, or a