xref: /openbmc/linux/drivers/net/phy/at803x.c (revision 5c5f626b)
1a2443fd1SAndrew Lunn // SPDX-License-Identifier: GPL-2.0+
20ca7111aSMatus Ujhelyi /*
30ca7111aSMatus Ujhelyi  * drivers/net/phy/at803x.c
40ca7111aSMatus Ujhelyi  *
50ca7111aSMatus Ujhelyi  * Driver for Atheros 803x PHY
60ca7111aSMatus Ujhelyi  *
70ca7111aSMatus Ujhelyi  * Author: Matus Ujhelyi <ujhelyi.m@gmail.com>
80ca7111aSMatus Ujhelyi  */
90ca7111aSMatus Ujhelyi 
100ca7111aSMatus Ujhelyi #include <linux/phy.h>
110ca7111aSMatus Ujhelyi #include <linux/module.h>
120ca7111aSMatus Ujhelyi #include <linux/string.h>
130ca7111aSMatus Ujhelyi #include <linux/netdevice.h>
140ca7111aSMatus Ujhelyi #include <linux/etherdevice.h>
1513a56b44SDaniel Mack #include <linux/of_gpio.h>
1613a56b44SDaniel Mack #include <linux/gpio/consumer.h>
170ca7111aSMatus Ujhelyi 
180ca7111aSMatus Ujhelyi #define AT803X_INTR_ENABLE			0x12
19e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_AUTONEG_ERR		BIT(15)
20e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_SPEED_CHANGED	BIT(14)
21e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_DUPLEX_CHANGED	BIT(13)
22e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_PAGE_RECEIVED	BIT(12)
23e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_LINK_FAIL		BIT(11)
24e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_LINK_SUCCESS		BIT(10)
25e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_WIRESPEED_DOWNGRADE	BIT(5)
26e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_POLARITY_CHANGED	BIT(1)
27e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_WOL			BIT(0)
28e6e4a556SMartin Blumenstingl 
290ca7111aSMatus Ujhelyi #define AT803X_INTR_STATUS			0x13
30a46bd63bSMartin Blumenstingl 
3113a56b44SDaniel Mack #define AT803X_SMART_SPEED			0x14
3213a56b44SDaniel Mack #define AT803X_LED_CONTROL			0x18
33a46bd63bSMartin Blumenstingl 
340ca7111aSMatus Ujhelyi #define AT803X_DEVICE_ADDR			0x03
350ca7111aSMatus Ujhelyi #define AT803X_LOC_MAC_ADDR_0_15_OFFSET		0x804C
360ca7111aSMatus Ujhelyi #define AT803X_LOC_MAC_ADDR_16_31_OFFSET	0x804B
370ca7111aSMatus Ujhelyi #define AT803X_LOC_MAC_ADDR_32_47_OFFSET	0x804A
38f62265b5SZefir Kurtisi #define AT803X_REG_CHIP_CONFIG			0x1f
39f62265b5SZefir Kurtisi #define AT803X_BT_BX_REG_SEL			0x8000
40a46bd63bSMartin Blumenstingl 
411ca6d1b1SMugunthan V N #define AT803X_DEBUG_ADDR			0x1D
421ca6d1b1SMugunthan V N #define AT803X_DEBUG_DATA			0x1E
43a46bd63bSMartin Blumenstingl 
44f62265b5SZefir Kurtisi #define AT803X_MODE_CFG_MASK			0x0F
45f62265b5SZefir Kurtisi #define AT803X_MODE_CFG_SGMII			0x01
46f62265b5SZefir Kurtisi 
47f62265b5SZefir Kurtisi #define AT803X_PSSR			0x11	/*PHY-Specific Status Register*/
48f62265b5SZefir Kurtisi #define AT803X_PSSR_MR_AN_COMPLETE	0x0200
49f62265b5SZefir Kurtisi 
502e5f9f28SMartin Blumenstingl #define AT803X_DEBUG_REG_0			0x00
512e5f9f28SMartin Blumenstingl #define AT803X_DEBUG_RX_CLK_DLY_EN		BIT(15)
52a46bd63bSMartin Blumenstingl 
532e5f9f28SMartin Blumenstingl #define AT803X_DEBUG_REG_5			0x05
542e5f9f28SMartin Blumenstingl #define AT803X_DEBUG_TX_CLK_DLY_EN		BIT(8)
550ca7111aSMatus Ujhelyi 
56bd8ca17fSDaniel Mack #define ATH8030_PHY_ID 0x004dd076
57bd8ca17fSDaniel Mack #define ATH8031_PHY_ID 0x004dd074
58bd8ca17fSDaniel Mack #define ATH8035_PHY_ID 0x004dd072
5958effd71SFabio Estevam #define AT803X_PHY_ID_MASK			0xffffffef
60bd8ca17fSDaniel Mack 
610ca7111aSMatus Ujhelyi MODULE_DESCRIPTION("Atheros 803x PHY driver");
620ca7111aSMatus Ujhelyi MODULE_AUTHOR("Matus Ujhelyi");
630ca7111aSMatus Ujhelyi MODULE_LICENSE("GPL");
640ca7111aSMatus Ujhelyi 
6513a56b44SDaniel Mack struct at803x_priv {
6613a56b44SDaniel Mack 	bool phy_reset:1;
6713a56b44SDaniel Mack };
6813a56b44SDaniel Mack 
6913a56b44SDaniel Mack struct at803x_context {
7013a56b44SDaniel Mack 	u16 bmcr;
7113a56b44SDaniel Mack 	u16 advertise;
7213a56b44SDaniel Mack 	u16 control1000;
7313a56b44SDaniel Mack 	u16 int_enable;
7413a56b44SDaniel Mack 	u16 smart_speed;
7513a56b44SDaniel Mack 	u16 led_control;
7613a56b44SDaniel Mack };
7713a56b44SDaniel Mack 
782e5f9f28SMartin Blumenstingl static int at803x_debug_reg_read(struct phy_device *phydev, u16 reg)
792e5f9f28SMartin Blumenstingl {
802e5f9f28SMartin Blumenstingl 	int ret;
812e5f9f28SMartin Blumenstingl 
822e5f9f28SMartin Blumenstingl 	ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg);
832e5f9f28SMartin Blumenstingl 	if (ret < 0)
842e5f9f28SMartin Blumenstingl 		return ret;
852e5f9f28SMartin Blumenstingl 
862e5f9f28SMartin Blumenstingl 	return phy_read(phydev, AT803X_DEBUG_DATA);
872e5f9f28SMartin Blumenstingl }
882e5f9f28SMartin Blumenstingl 
892e5f9f28SMartin Blumenstingl static int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg,
902e5f9f28SMartin Blumenstingl 				 u16 clear, u16 set)
912e5f9f28SMartin Blumenstingl {
922e5f9f28SMartin Blumenstingl 	u16 val;
932e5f9f28SMartin Blumenstingl 	int ret;
942e5f9f28SMartin Blumenstingl 
952e5f9f28SMartin Blumenstingl 	ret = at803x_debug_reg_read(phydev, reg);
962e5f9f28SMartin Blumenstingl 	if (ret < 0)
972e5f9f28SMartin Blumenstingl 		return ret;
982e5f9f28SMartin Blumenstingl 
992e5f9f28SMartin Blumenstingl 	val = ret & 0xffff;
1002e5f9f28SMartin Blumenstingl 	val &= ~clear;
1012e5f9f28SMartin Blumenstingl 	val |= set;
1022e5f9f28SMartin Blumenstingl 
1032e5f9f28SMartin Blumenstingl 	return phy_write(phydev, AT803X_DEBUG_DATA, val);
1042e5f9f28SMartin Blumenstingl }
1052e5f9f28SMartin Blumenstingl 
1066d4cd041SVinod Koul static int at803x_enable_rx_delay(struct phy_device *phydev)
1076d4cd041SVinod Koul {
1086d4cd041SVinod Koul 	return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0, 0,
1096d4cd041SVinod Koul 				     AT803X_DEBUG_RX_CLK_DLY_EN);
1106d4cd041SVinod Koul }
1116d4cd041SVinod Koul 
1126d4cd041SVinod Koul static int at803x_enable_tx_delay(struct phy_device *phydev)
1136d4cd041SVinod Koul {
1146d4cd041SVinod Koul 	return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_5, 0,
1156d4cd041SVinod Koul 				     AT803X_DEBUG_TX_CLK_DLY_EN);
1166d4cd041SVinod Koul }
1176d4cd041SVinod Koul 
11843f2ebd5SVinod Koul static int at803x_disable_rx_delay(struct phy_device *phydev)
1192e5f9f28SMartin Blumenstingl {
120cd28d1d6SVinod Koul 	return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0,
121cd28d1d6SVinod Koul 				     AT803X_DEBUG_RX_CLK_DLY_EN, 0);
1222e5f9f28SMartin Blumenstingl }
1232e5f9f28SMartin Blumenstingl 
12443f2ebd5SVinod Koul static int at803x_disable_tx_delay(struct phy_device *phydev)
1252e5f9f28SMartin Blumenstingl {
126cd28d1d6SVinod Koul 	return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_5,
127cd28d1d6SVinod Koul 				     AT803X_DEBUG_TX_CLK_DLY_EN, 0);
1282e5f9f28SMartin Blumenstingl }
1292e5f9f28SMartin Blumenstingl 
13013a56b44SDaniel Mack /* save relevant PHY registers to private copy */
13113a56b44SDaniel Mack static void at803x_context_save(struct phy_device *phydev,
13213a56b44SDaniel Mack 				struct at803x_context *context)
13313a56b44SDaniel Mack {
13413a56b44SDaniel Mack 	context->bmcr = phy_read(phydev, MII_BMCR);
13513a56b44SDaniel Mack 	context->advertise = phy_read(phydev, MII_ADVERTISE);
13613a56b44SDaniel Mack 	context->control1000 = phy_read(phydev, MII_CTRL1000);
13713a56b44SDaniel Mack 	context->int_enable = phy_read(phydev, AT803X_INTR_ENABLE);
13813a56b44SDaniel Mack 	context->smart_speed = phy_read(phydev, AT803X_SMART_SPEED);
13913a56b44SDaniel Mack 	context->led_control = phy_read(phydev, AT803X_LED_CONTROL);
14013a56b44SDaniel Mack }
14113a56b44SDaniel Mack 
14213a56b44SDaniel Mack /* restore relevant PHY registers from private copy */
14313a56b44SDaniel Mack static void at803x_context_restore(struct phy_device *phydev,
14413a56b44SDaniel Mack 				   const struct at803x_context *context)
14513a56b44SDaniel Mack {
14613a56b44SDaniel Mack 	phy_write(phydev, MII_BMCR, context->bmcr);
14713a56b44SDaniel Mack 	phy_write(phydev, MII_ADVERTISE, context->advertise);
14813a56b44SDaniel Mack 	phy_write(phydev, MII_CTRL1000, context->control1000);
14913a56b44SDaniel Mack 	phy_write(phydev, AT803X_INTR_ENABLE, context->int_enable);
15013a56b44SDaniel Mack 	phy_write(phydev, AT803X_SMART_SPEED, context->smart_speed);
15113a56b44SDaniel Mack 	phy_write(phydev, AT803X_LED_CONTROL, context->led_control);
15213a56b44SDaniel Mack }
15313a56b44SDaniel Mack 
154ea13c9eeSMugunthan V N static int at803x_set_wol(struct phy_device *phydev,
155ea13c9eeSMugunthan V N 			  struct ethtool_wolinfo *wol)
1560ca7111aSMatus Ujhelyi {
1570ca7111aSMatus Ujhelyi 	struct net_device *ndev = phydev->attached_dev;
1580ca7111aSMatus Ujhelyi 	const u8 *mac;
159ea13c9eeSMugunthan V N 	int ret;
160ea13c9eeSMugunthan V N 	u32 value;
1610ca7111aSMatus Ujhelyi 	unsigned int i, offsets[] = {
1620ca7111aSMatus Ujhelyi 		AT803X_LOC_MAC_ADDR_32_47_OFFSET,
1630ca7111aSMatus Ujhelyi 		AT803X_LOC_MAC_ADDR_16_31_OFFSET,
1640ca7111aSMatus Ujhelyi 		AT803X_LOC_MAC_ADDR_0_15_OFFSET,
1650ca7111aSMatus Ujhelyi 	};
1660ca7111aSMatus Ujhelyi 
1670ca7111aSMatus Ujhelyi 	if (!ndev)
168ea13c9eeSMugunthan V N 		return -ENODEV;
1690ca7111aSMatus Ujhelyi 
170ea13c9eeSMugunthan V N 	if (wol->wolopts & WAKE_MAGIC) {
1710ca7111aSMatus Ujhelyi 		mac = (const u8 *) ndev->dev_addr;
1720ca7111aSMatus Ujhelyi 
1730ca7111aSMatus Ujhelyi 		if (!is_valid_ether_addr(mac))
174fc755687SDan Murphy 			return -EINVAL;
1750ca7111aSMatus Ujhelyi 
1760e021396SCarlo Caione 		for (i = 0; i < 3; i++)
1770e021396SCarlo Caione 			phy_write_mmd(phydev, AT803X_DEVICE_ADDR, offsets[i],
1780ca7111aSMatus Ujhelyi 				      mac[(i * 2) + 1] | (mac[(i * 2)] << 8));
179ea13c9eeSMugunthan V N 
180ea13c9eeSMugunthan V N 		value = phy_read(phydev, AT803X_INTR_ENABLE);
181e6e4a556SMartin Blumenstingl 		value |= AT803X_INTR_ENABLE_WOL;
182ea13c9eeSMugunthan V N 		ret = phy_write(phydev, AT803X_INTR_ENABLE, value);
183ea13c9eeSMugunthan V N 		if (ret)
184ea13c9eeSMugunthan V N 			return ret;
185ea13c9eeSMugunthan V N 		value = phy_read(phydev, AT803X_INTR_STATUS);
186ea13c9eeSMugunthan V N 	} else {
187ea13c9eeSMugunthan V N 		value = phy_read(phydev, AT803X_INTR_ENABLE);
188e6e4a556SMartin Blumenstingl 		value &= (~AT803X_INTR_ENABLE_WOL);
189ea13c9eeSMugunthan V N 		ret = phy_write(phydev, AT803X_INTR_ENABLE, value);
190ea13c9eeSMugunthan V N 		if (ret)
191ea13c9eeSMugunthan V N 			return ret;
192ea13c9eeSMugunthan V N 		value = phy_read(phydev, AT803X_INTR_STATUS);
193ea13c9eeSMugunthan V N 	}
194ea13c9eeSMugunthan V N 
195ea13c9eeSMugunthan V N 	return ret;
196ea13c9eeSMugunthan V N }
197ea13c9eeSMugunthan V N 
198ea13c9eeSMugunthan V N static void at803x_get_wol(struct phy_device *phydev,
199ea13c9eeSMugunthan V N 			   struct ethtool_wolinfo *wol)
200ea13c9eeSMugunthan V N {
201ea13c9eeSMugunthan V N 	u32 value;
202ea13c9eeSMugunthan V N 
203ea13c9eeSMugunthan V N 	wol->supported = WAKE_MAGIC;
204ea13c9eeSMugunthan V N 	wol->wolopts = 0;
205ea13c9eeSMugunthan V N 
206ea13c9eeSMugunthan V N 	value = phy_read(phydev, AT803X_INTR_ENABLE);
207e6e4a556SMartin Blumenstingl 	if (value & AT803X_INTR_ENABLE_WOL)
208ea13c9eeSMugunthan V N 		wol->wolopts |= WAKE_MAGIC;
2090ca7111aSMatus Ujhelyi }
2100ca7111aSMatus Ujhelyi 
2116229ed1fSDaniel Mack static int at803x_suspend(struct phy_device *phydev)
2126229ed1fSDaniel Mack {
2136229ed1fSDaniel Mack 	int value;
2146229ed1fSDaniel Mack 	int wol_enabled;
2156229ed1fSDaniel Mack 
2166229ed1fSDaniel Mack 	value = phy_read(phydev, AT803X_INTR_ENABLE);
217e6e4a556SMartin Blumenstingl 	wol_enabled = value & AT803X_INTR_ENABLE_WOL;
2186229ed1fSDaniel Mack 
2196229ed1fSDaniel Mack 	if (wol_enabled)
220fea23fb5SRussell King 		value = BMCR_ISOLATE;
2216229ed1fSDaniel Mack 	else
222fea23fb5SRussell King 		value = BMCR_PDOWN;
2236229ed1fSDaniel Mack 
224fea23fb5SRussell King 	phy_modify(phydev, MII_BMCR, 0, value);
2256229ed1fSDaniel Mack 
2266229ed1fSDaniel Mack 	return 0;
2276229ed1fSDaniel Mack }
2286229ed1fSDaniel Mack 
2296229ed1fSDaniel Mack static int at803x_resume(struct phy_device *phydev)
2306229ed1fSDaniel Mack {
231f102852fSRussell King 	return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0);
2326229ed1fSDaniel Mack }
2336229ed1fSDaniel Mack 
23413a56b44SDaniel Mack static int at803x_probe(struct phy_device *phydev)
23513a56b44SDaniel Mack {
236e5a03bfdSAndrew Lunn 	struct device *dev = &phydev->mdio.dev;
23713a56b44SDaniel Mack 	struct at803x_priv *priv;
23813a56b44SDaniel Mack 
2398f2877caSFengguang Wu 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
24013a56b44SDaniel Mack 	if (!priv)
24113a56b44SDaniel Mack 		return -ENOMEM;
24213a56b44SDaniel Mack 
24313a56b44SDaniel Mack 	phydev->priv = priv;
24413a56b44SDaniel Mack 
24513a56b44SDaniel Mack 	return 0;
24613a56b44SDaniel Mack }
24713a56b44SDaniel Mack 
2480ca7111aSMatus Ujhelyi static int at803x_config_init(struct phy_device *phydev)
2490ca7111aSMatus Ujhelyi {
2501ca6d1b1SMugunthan V N 	int ret;
2510ca7111aSMatus Ujhelyi 
2526ff01dbbSDaniel Mack 	ret = genphy_config_init(phydev);
2536ff01dbbSDaniel Mack 	if (ret < 0)
2546ff01dbbSDaniel Mack 		return ret;
2550ca7111aSMatus Ujhelyi 
2566d4cd041SVinod Koul 	/* The RX and TX delay default is:
2576d4cd041SVinod Koul 	 *   after HW reset: RX delay enabled and TX delay disabled
2586d4cd041SVinod Koul 	 *   after SW reset: RX delay enabled, while TX delay retains the
2596d4cd041SVinod Koul 	 *   value before reset.
2606d4cd041SVinod Koul 	 *
2616d4cd041SVinod Koul 	 * So let's first disable the RX and TX delays in PHY and enable
2626d4cd041SVinod Koul 	 * them based on the mode selected (this also takes care of RGMII
2636d4cd041SVinod Koul 	 * mode where we expect delays to be disabled)
2646d4cd041SVinod Koul 	 */
2656d4cd041SVinod Koul 
266cd28d1d6SVinod Koul 	ret = at803x_disable_rx_delay(phydev);
2672e5f9f28SMartin Blumenstingl 	if (ret < 0)
2681ca6d1b1SMugunthan V N 		return ret;
269cd28d1d6SVinod Koul 	ret = at803x_disable_tx_delay(phydev);
2702e5f9f28SMartin Blumenstingl 	if (ret < 0)
2711ca6d1b1SMugunthan V N 		return ret;
2726d4cd041SVinod Koul 
2736d4cd041SVinod Koul 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
2746d4cd041SVinod Koul 	    phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
2756d4cd041SVinod Koul 		/* If RGMII_ID or RGMII_RXID are specified enable RX delay,
2766d4cd041SVinod Koul 		 * otherwise keep it disabled
2776d4cd041SVinod Koul 		 */
2786d4cd041SVinod Koul 		ret = at803x_enable_rx_delay(phydev);
2796d4cd041SVinod Koul 		if (ret < 0)
2806d4cd041SVinod Koul 			return ret;
2811ca6d1b1SMugunthan V N 	}
2821ca6d1b1SMugunthan V N 
2836d4cd041SVinod Koul 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
2846d4cd041SVinod Koul 	    phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
2856d4cd041SVinod Koul 		/* If RGMII_ID or RGMII_TXID are specified enable TX delay,
2866d4cd041SVinod Koul 		 * otherwise keep it disabled
2876d4cd041SVinod Koul 		 */
2886d4cd041SVinod Koul 		ret = at803x_enable_tx_delay(phydev);
2896d4cd041SVinod Koul 	}
2906d4cd041SVinod Koul 
2916d4cd041SVinod Koul 	return ret;
2920ca7111aSMatus Ujhelyi }
2930ca7111aSMatus Ujhelyi 
29477a99394SZhao Qiang static int at803x_ack_interrupt(struct phy_device *phydev)
29577a99394SZhao Qiang {
29677a99394SZhao Qiang 	int err;
29777a99394SZhao Qiang 
298a46bd63bSMartin Blumenstingl 	err = phy_read(phydev, AT803X_INTR_STATUS);
29977a99394SZhao Qiang 
30077a99394SZhao Qiang 	return (err < 0) ? err : 0;
30177a99394SZhao Qiang }
30277a99394SZhao Qiang 
30377a99394SZhao Qiang static int at803x_config_intr(struct phy_device *phydev)
30477a99394SZhao Qiang {
30577a99394SZhao Qiang 	int err;
30677a99394SZhao Qiang 	int value;
30777a99394SZhao Qiang 
308a46bd63bSMartin Blumenstingl 	value = phy_read(phydev, AT803X_INTR_ENABLE);
30977a99394SZhao Qiang 
310e6e4a556SMartin Blumenstingl 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
311e6e4a556SMartin Blumenstingl 		value |= AT803X_INTR_ENABLE_AUTONEG_ERR;
312e6e4a556SMartin Blumenstingl 		value |= AT803X_INTR_ENABLE_SPEED_CHANGED;
313e6e4a556SMartin Blumenstingl 		value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED;
314e6e4a556SMartin Blumenstingl 		value |= AT803X_INTR_ENABLE_LINK_FAIL;
315e6e4a556SMartin Blumenstingl 		value |= AT803X_INTR_ENABLE_LINK_SUCCESS;
316e6e4a556SMartin Blumenstingl 
317e6e4a556SMartin Blumenstingl 		err = phy_write(phydev, AT803X_INTR_ENABLE, value);
318e6e4a556SMartin Blumenstingl 	}
31977a99394SZhao Qiang 	else
320a46bd63bSMartin Blumenstingl 		err = phy_write(phydev, AT803X_INTR_ENABLE, 0);
32177a99394SZhao Qiang 
32277a99394SZhao Qiang 	return err;
32377a99394SZhao Qiang }
32477a99394SZhao Qiang 
32513a56b44SDaniel Mack static void at803x_link_change_notify(struct phy_device *phydev)
32613a56b44SDaniel Mack {
32713a56b44SDaniel Mack 	/*
32813a56b44SDaniel Mack 	 * Conduct a hardware reset for AT8030 every time a link loss is
32913a56b44SDaniel Mack 	 * signalled. This is necessary to circumvent a hardware bug that
33013a56b44SDaniel Mack 	 * occurs when the cable is unplugged while TX packets are pending
33113a56b44SDaniel Mack 	 * in the FIFO. In such cases, the FIFO enters an error mode it
33213a56b44SDaniel Mack 	 * cannot recover from by software.
33313a56b44SDaniel Mack 	 */
3345c5f626bSHeiner Kallweit 	if (phydev->state == PHY_NOLINK && phydev->mdio.reset) {
33513a56b44SDaniel Mack 		struct at803x_context context;
33613a56b44SDaniel Mack 
33713a56b44SDaniel Mack 		at803x_context_save(phydev, &context);
33813a56b44SDaniel Mack 
339bafbdd52SSergei Shtylyov 		phy_device_reset(phydev, 1);
34013a56b44SDaniel Mack 		msleep(1);
341bafbdd52SSergei Shtylyov 		phy_device_reset(phydev, 0);
342d57019d1SSergei Shtylyov 		msleep(1);
34313a56b44SDaniel Mack 
34413a56b44SDaniel Mack 		at803x_context_restore(phydev, &context);
34513a56b44SDaniel Mack 
3465c5f626bSHeiner Kallweit 		phydev_dbg(phydev, "%s(): phy was reset\n", __func__);
34713a56b44SDaniel Mack 	}
34813a56b44SDaniel Mack }
34913a56b44SDaniel Mack 
350f62265b5SZefir Kurtisi static int at803x_aneg_done(struct phy_device *phydev)
351f62265b5SZefir Kurtisi {
352f62265b5SZefir Kurtisi 	int ccr;
353f62265b5SZefir Kurtisi 
354f62265b5SZefir Kurtisi 	int aneg_done = genphy_aneg_done(phydev);
355f62265b5SZefir Kurtisi 	if (aneg_done != BMSR_ANEGCOMPLETE)
356f62265b5SZefir Kurtisi 		return aneg_done;
357f62265b5SZefir Kurtisi 
358f62265b5SZefir Kurtisi 	/*
359f62265b5SZefir Kurtisi 	 * in SGMII mode, if copper side autoneg is successful,
360f62265b5SZefir Kurtisi 	 * also check SGMII side autoneg result
361f62265b5SZefir Kurtisi 	 */
362f62265b5SZefir Kurtisi 	ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG);
363f62265b5SZefir Kurtisi 	if ((ccr & AT803X_MODE_CFG_MASK) != AT803X_MODE_CFG_SGMII)
364f62265b5SZefir Kurtisi 		return aneg_done;
365f62265b5SZefir Kurtisi 
366f62265b5SZefir Kurtisi 	/* switch to SGMII/fiber page */
367f62265b5SZefir Kurtisi 	phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr & ~AT803X_BT_BX_REG_SEL);
368f62265b5SZefir Kurtisi 
369f62265b5SZefir Kurtisi 	/* check if the SGMII link is OK. */
370f62265b5SZefir Kurtisi 	if (!(phy_read(phydev, AT803X_PSSR) & AT803X_PSSR_MR_AN_COMPLETE)) {
371ab2a605fSAndrew Lunn 		phydev_warn(phydev, "803x_aneg_done: SGMII link is not ok\n");
372f62265b5SZefir Kurtisi 		aneg_done = 0;
373f62265b5SZefir Kurtisi 	}
374f62265b5SZefir Kurtisi 	/* switch back to copper page */
375f62265b5SZefir Kurtisi 	phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr | AT803X_BT_BX_REG_SEL);
376f62265b5SZefir Kurtisi 
377f62265b5SZefir Kurtisi 	return aneg_done;
378f62265b5SZefir Kurtisi }
379f62265b5SZefir Kurtisi 
380317420abSMugunthan V N static struct phy_driver at803x_driver[] = {
381317420abSMugunthan V N {
3820ca7111aSMatus Ujhelyi 	/* ATHEROS 8035 */
383bd8ca17fSDaniel Mack 	.phy_id			= ATH8035_PHY_ID,
3840ca7111aSMatus Ujhelyi 	.name			= "Atheros 8035 ethernet",
38558effd71SFabio Estevam 	.phy_id_mask		= AT803X_PHY_ID_MASK,
38613a56b44SDaniel Mack 	.probe			= at803x_probe,
3870ca7111aSMatus Ujhelyi 	.config_init		= at803x_config_init,
388ea13c9eeSMugunthan V N 	.set_wol		= at803x_set_wol,
389ea13c9eeSMugunthan V N 	.get_wol		= at803x_get_wol,
3906229ed1fSDaniel Mack 	.suspend		= at803x_suspend,
3916229ed1fSDaniel Mack 	.resume			= at803x_resume,
3920ca7111aSMatus Ujhelyi 	.features		= PHY_GBIT_FEATURES,
3930eae5982SMåns Rullgård 	.ack_interrupt		= at803x_ack_interrupt,
3940eae5982SMåns Rullgård 	.config_intr		= at803x_config_intr,
395317420abSMugunthan V N }, {
3960ca7111aSMatus Ujhelyi 	/* ATHEROS 8030 */
397bd8ca17fSDaniel Mack 	.phy_id			= ATH8030_PHY_ID,
3980ca7111aSMatus Ujhelyi 	.name			= "Atheros 8030 ethernet",
39958effd71SFabio Estevam 	.phy_id_mask		= AT803X_PHY_ID_MASK,
40013a56b44SDaniel Mack 	.probe			= at803x_probe,
4010ca7111aSMatus Ujhelyi 	.config_init		= at803x_config_init,
40213a56b44SDaniel Mack 	.link_change_notify	= at803x_link_change_notify,
403ea13c9eeSMugunthan V N 	.set_wol		= at803x_set_wol,
404ea13c9eeSMugunthan V N 	.get_wol		= at803x_get_wol,
4056229ed1fSDaniel Mack 	.suspend		= at803x_suspend,
4066229ed1fSDaniel Mack 	.resume			= at803x_resume,
407e15bb4c6SMartin Blumenstingl 	.features		= PHY_BASIC_FEATURES,
4080eae5982SMåns Rullgård 	.ack_interrupt		= at803x_ack_interrupt,
4090eae5982SMåns Rullgård 	.config_intr		= at803x_config_intr,
41005d7cce8SMugunthan V N }, {
41105d7cce8SMugunthan V N 	/* ATHEROS 8031 */
412bd8ca17fSDaniel Mack 	.phy_id			= ATH8031_PHY_ID,
41305d7cce8SMugunthan V N 	.name			= "Atheros 8031 ethernet",
41458effd71SFabio Estevam 	.phy_id_mask		= AT803X_PHY_ID_MASK,
41513a56b44SDaniel Mack 	.probe			= at803x_probe,
41605d7cce8SMugunthan V N 	.config_init		= at803x_config_init,
41705d7cce8SMugunthan V N 	.set_wol		= at803x_set_wol,
41805d7cce8SMugunthan V N 	.get_wol		= at803x_get_wol,
4196229ed1fSDaniel Mack 	.suspend		= at803x_suspend,
4206229ed1fSDaniel Mack 	.resume			= at803x_resume,
42105d7cce8SMugunthan V N 	.features		= PHY_GBIT_FEATURES,
422f62265b5SZefir Kurtisi 	.aneg_done		= at803x_aneg_done,
42377a99394SZhao Qiang 	.ack_interrupt		= &at803x_ack_interrupt,
42477a99394SZhao Qiang 	.config_intr		= &at803x_config_intr,
425317420abSMugunthan V N } };
4260ca7111aSMatus Ujhelyi 
42750fd7150SJohan Hovold module_phy_driver(at803x_driver);
4280ca7111aSMatus Ujhelyi 
4290ca7111aSMatus Ujhelyi static struct mdio_device_id __maybe_unused atheros_tbl[] = {
43058effd71SFabio Estevam 	{ ATH8030_PHY_ID, AT803X_PHY_ID_MASK },
43158effd71SFabio Estevam 	{ ATH8031_PHY_ID, AT803X_PHY_ID_MASK },
43258effd71SFabio Estevam 	{ ATH8035_PHY_ID, AT803X_PHY_ID_MASK },
4330ca7111aSMatus Ujhelyi 	{ }
4340ca7111aSMatus Ujhelyi };
4350ca7111aSMatus Ujhelyi 
4360ca7111aSMatus Ujhelyi MODULE_DEVICE_TABLE(mdio, atheros_tbl);
437