summary refs log tree commit diff
path: root/drivers/net/phy/at803x.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/phy/at803x.c')
-rw-r--r--drivers/net/phy/at803x.c128
1 files changed, 88 insertions, 40 deletions
diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c
index 45cbc10de01c..1f7091b3c27c 100644
--- a/drivers/net/phy/at803x.c
+++ b/drivers/net/phy/at803x.c
@@ -27,15 +27,22 @@
 #define AT803X_MMD_ACCESS_CONTROL		0x0D
 #define AT803X_MMD_ACCESS_CONTROL_DATA		0x0E
 #define AT803X_FUNC_DATA			0x4003
+#define AT803X_DEBUG_ADDR			0x1D
+#define AT803X_DEBUG_DATA			0x1E
+#define AT803X_DEBUG_SYSTEM_MODE_CTRL		0x05
+#define AT803X_DEBUG_RGMII_TX_CLK_DLY		BIT(8)
 
 MODULE_DESCRIPTION("Atheros 803x PHY driver");
 MODULE_AUTHOR("Matus Ujhelyi");
 MODULE_LICENSE("GPL");
 
-static void at803x_set_wol_mac_addr(struct phy_device *phydev)
+static int at803x_set_wol(struct phy_device *phydev,
+			  struct ethtool_wolinfo *wol)
 {
 	struct net_device *ndev = phydev->attached_dev;
 	const u8 *mac;
+	int ret;
+	u32 value;
 	unsigned int i, offsets[] = {
 		AT803X_LOC_MAC_ADDR_32_47_OFFSET,
 		AT803X_LOC_MAC_ADDR_16_31_OFFSET,
@@ -43,30 +50,61 @@ static void at803x_set_wol_mac_addr(struct phy_device *phydev)
 	};
 
 	if (!ndev)
-		return;
+		return -ENODEV;
 
-	mac = (const u8 *) ndev->dev_addr;
+	if (wol->wolopts & WAKE_MAGIC) {
+		mac = (const u8 *) ndev->dev_addr;
 
-	if (!is_valid_ether_addr(mac))
-		return;
+		if (!is_valid_ether_addr(mac))
+			return -EFAULT;
 
-	for (i = 0; i < 3; i++) {
-		phy_write(phydev, AT803X_MMD_ACCESS_CONTROL,
+		for (i = 0; i < 3; i++) {
+			phy_write(phydev, AT803X_MMD_ACCESS_CONTROL,
 				  AT803X_DEVICE_ADDR);
-		phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA,
+			phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA,
 				  offsets[i]);
-		phy_write(phydev, AT803X_MMD_ACCESS_CONTROL,
+			phy_write(phydev, AT803X_MMD_ACCESS_CONTROL,
 				  AT803X_FUNC_DATA);
-		phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA,
+			phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA,
 				  mac[(i * 2) + 1] | (mac[(i * 2)] << 8));
+		}
+
+		value = phy_read(phydev, AT803X_INTR_ENABLE);
+		value |= AT803X_WOL_ENABLE;
+		ret = phy_write(phydev, AT803X_INTR_ENABLE, value);
+		if (ret)
+			return ret;
+		value = phy_read(phydev, AT803X_INTR_STATUS);
+	} else {
+		value = phy_read(phydev, AT803X_INTR_ENABLE);
+		value &= (~AT803X_WOL_ENABLE);
+		ret = phy_write(phydev, AT803X_INTR_ENABLE, value);
+		if (ret)
+			return ret;
+		value = phy_read(phydev, AT803X_INTR_STATUS);
 	}
+
+	return ret;
+}
+
+static void at803x_get_wol(struct phy_device *phydev,
+			   struct ethtool_wolinfo *wol)
+{
+	u32 value;
+
+	wol->supported = WAKE_MAGIC;
+	wol->wolopts = 0;
+
+	value = phy_read(phydev, AT803X_INTR_ENABLE);
+	if (value & AT803X_WOL_ENABLE)
+		wol->wolopts |= WAKE_MAGIC;
 }
 
 static int at803x_config_init(struct phy_device *phydev)
 {
 	int val;
+	int ret;
 	u32 features;
-	int status;
 
 	features = SUPPORTED_TP | SUPPORTED_MII | SUPPORTED_AUI |
 		   SUPPORTED_FIBRE | SUPPORTED_BNC;
@@ -100,20 +138,29 @@ static int at803x_config_init(struct phy_device *phydev)
 	phydev->supported = features;
 	phydev->advertising = features;
 
-	/* enable WOL */
-	at803x_set_wol_mac_addr(phydev);
-	status = phy_write(phydev, AT803X_INTR_ENABLE, AT803X_WOL_ENABLE);
-	status = phy_read(phydev, AT803X_INTR_STATUS);
+	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
+		ret = phy_write(phydev, AT803X_DEBUG_ADDR,
+				AT803X_DEBUG_SYSTEM_MODE_CTRL);
+		if (ret)
+			return ret;
+		ret = phy_write(phydev, AT803X_DEBUG_DATA,
+				AT803X_DEBUG_RGMII_TX_CLK_DLY);
+		if (ret)
+			return ret;
+	}
 
 	return 0;
 }
 
-/* ATHEROS 8035 */
-static struct phy_driver at8035_driver = {
+static struct phy_driver at803x_driver[] = {
+{
+	/* ATHEROS 8035 */
 	.phy_id		= 0x004dd072,
 	.name		= "Atheros 8035 ethernet",
 	.phy_id_mask	= 0xffffffef,
 	.config_init	= at803x_config_init,
+	.set_wol	= at803x_set_wol,
+	.get_wol	= at803x_get_wol,
 	.features	= PHY_GBIT_FEATURES,
 	.flags		= PHY_HAS_INTERRUPT,
 	.config_aneg	= &genphy_config_aneg,
@@ -121,14 +168,14 @@ static struct phy_driver at8035_driver = {
 	.driver		= {
 		.owner = THIS_MODULE,
 	},
-};
-
-/* ATHEROS 8030 */
-static struct phy_driver at8030_driver = {
+}, {
+	/* ATHEROS 8030 */
 	.phy_id		= 0x004dd076,
 	.name		= "Atheros 8030 ethernet",
 	.phy_id_mask	= 0xffffffef,
 	.config_init	= at803x_config_init,
+	.set_wol	= at803x_set_wol,
+	.get_wol	= at803x_get_wol,
 	.features	= PHY_GBIT_FEATURES,
 	.flags		= PHY_HAS_INTERRUPT,
 	.config_aneg	= &genphy_config_aneg,
@@ -136,32 +183,33 @@ static struct phy_driver at8030_driver = {
 	.driver		= {
 		.owner = THIS_MODULE,
 	},
-};
+}, {
+	/* ATHEROS 8031 */
+	.phy_id		= 0x004dd074,
+	.name		= "Atheros 8031 ethernet",
+	.phy_id_mask	= 0xffffffef,
+	.config_init	= at803x_config_init,
+	.set_wol	= at803x_set_wol,
+	.get_wol	= at803x_get_wol,
+	.features	= PHY_GBIT_FEATURES,
+	.flags		= PHY_HAS_INTERRUPT,
+	.config_aneg	= &genphy_config_aneg,
+	.read_status	= &genphy_read_status,
+	.driver		= {
+		.owner = THIS_MODULE,
+	},
+} };
 
 static int __init atheros_init(void)
 {
-	int ret;
-
-	ret = phy_driver_register(&at8035_driver);
-	if (ret)
-		goto err1;
-
-	ret = phy_driver_register(&at8030_driver);
-	if (ret)
-		goto err2;
-
-	return 0;
-
-err2:
-	phy_driver_unregister(&at8035_driver);
-err1:
-	return ret;
+	return phy_drivers_register(at803x_driver,
+				    ARRAY_SIZE(at803x_driver));
 }
 
 static void __exit atheros_exit(void)
 {
-	phy_driver_unregister(&at8035_driver);
-	phy_driver_unregister(&at8030_driver);
+	return phy_drivers_unregister(at803x_driver,
+				      ARRAY_SIZE(at803x_driver));
 }
 
 module_init(atheros_init);