[POWERPC] PS3: Gelic network driver Wake-on-LAN support

Add Wake-on-LAN support to the PS3 Gelic network driver.
Other OS WOL support was introduced in PS3 system firmware 2.20.

Signed-off-by: Masakazu Mokuno <mokuno@sm.sony.co.jp>
Signed-off-by: Geoff Levand <geoffrey.levand@am.sony.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
diff --git a/drivers/net/ps3_gelic_net.c b/drivers/net/ps3_gelic_net.c
index 7eb6e7e..e365efb 100644
--- a/drivers/net/ps3_gelic_net.c
+++ b/drivers/net/ps3_gelic_net.c
@@ -1266,6 +1266,85 @@
 	return 0;
 }
 
+static void gelic_net_get_wol(struct net_device *netdev,
+			      struct ethtool_wolinfo *wol)
+{
+	if (0 <= ps3_compare_firmware_version(2, 2, 0))
+		wol->supported = WAKE_MAGIC;
+	else
+		wol->supported = 0;
+
+	wol->wolopts = ps3_sys_manager_get_wol() ? wol->supported : 0;
+	memset(&wol->sopass, 0, sizeof(wol->sopass));
+}
+static int gelic_net_set_wol(struct net_device *netdev,
+			     struct ethtool_wolinfo *wol)
+{
+	int status;
+	struct gelic_card *card;
+	u64 v1, v2;
+
+	if (ps3_compare_firmware_version(2, 2, 0) < 0 ||
+	    !capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	if (wol->wolopts & ~WAKE_MAGIC)
+		return -EINVAL;
+
+	card = netdev_card(netdev);
+	if (wol->wolopts & WAKE_MAGIC) {
+		status = lv1_net_control(bus_id(card), dev_id(card),
+					 GELIC_LV1_SET_WOL,
+					 GELIC_LV1_WOL_MAGIC_PACKET,
+					 0, GELIC_LV1_WOL_MP_ENABLE,
+					 &v1, &v2);
+		if (status) {
+			pr_info("%s: enabling WOL failed %d\n", __func__,
+				status);
+			status = -EIO;
+			goto done;
+		}
+		status = lv1_net_control(bus_id(card), dev_id(card),
+					 GELIC_LV1_SET_WOL,
+					 GELIC_LV1_WOL_ADD_MATCH_ADDR,
+					 0, GELIC_LV1_WOL_MATCH_ALL,
+					 &v1, &v2);
+		if (!status)
+			ps3_sys_manager_set_wol(1);
+		else {
+			pr_info("%s: enabling WOL filter failed %d\n",
+				__func__, status);
+			status = -EIO;
+		}
+	} else {
+		status = lv1_net_control(bus_id(card), dev_id(card),
+					 GELIC_LV1_SET_WOL,
+					 GELIC_LV1_WOL_MAGIC_PACKET,
+					 0, GELIC_LV1_WOL_MP_DISABLE,
+					 &v1, &v2);
+		if (status) {
+			pr_info("%s: disabling WOL failed %d\n", __func__,
+				status);
+			status = -EIO;
+			goto done;
+		}
+		status = lv1_net_control(bus_id(card), dev_id(card),
+					 GELIC_LV1_SET_WOL,
+					 GELIC_LV1_WOL_DELETE_MATCH_ADDR,
+					 0, GELIC_LV1_WOL_MATCH_ALL,
+					 &v1, &v2);
+		if (!status)
+			ps3_sys_manager_set_wol(0);
+		else {
+			pr_info("%s: removing WOL filter failed %d\n",
+				__func__, status);
+			status = -EIO;
+		}
+	}
+done:
+	return status;
+}
+
 static struct ethtool_ops gelic_ether_ethtool_ops = {
 	.get_drvinfo	= gelic_net_get_drvinfo,
 	.get_settings	= gelic_ether_get_settings,
@@ -1274,6 +1353,8 @@
 	.set_tx_csum	= ethtool_op_set_tx_csum,
 	.get_rx_csum	= gelic_net_get_rx_csum,
 	.set_rx_csum	= gelic_net_set_rx_csum,
+	.get_wol	= gelic_net_get_wol,
+	.set_wol	= gelic_net_set_wol,
 };
 
 /**