xref: /openbmc/linux/drivers/net/phy/at803x.c (revision 06d5f344)
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 
1806d5f344SRussell King #define AT803X_SPECIFIC_STATUS			0x11
1906d5f344SRussell King #define AT803X_SS_SPEED_MASK			(3 << 14)
2006d5f344SRussell King #define AT803X_SS_SPEED_1000			(2 << 14)
2106d5f344SRussell King #define AT803X_SS_SPEED_100			(1 << 14)
2206d5f344SRussell King #define AT803X_SS_SPEED_10			(0 << 14)
2306d5f344SRussell King #define AT803X_SS_DUPLEX			BIT(13)
2406d5f344SRussell King #define AT803X_SS_SPEED_DUPLEX_RESOLVED		BIT(11)
2506d5f344SRussell King #define AT803X_SS_MDIX				BIT(6)
2606d5f344SRussell King 
270ca7111aSMatus Ujhelyi #define AT803X_INTR_ENABLE			0x12
28e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_AUTONEG_ERR		BIT(15)
29e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_SPEED_CHANGED	BIT(14)
30e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_DUPLEX_CHANGED	BIT(13)
31e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_PAGE_RECEIVED	BIT(12)
32e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_LINK_FAIL		BIT(11)
33e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_LINK_SUCCESS		BIT(10)
34e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_WIRESPEED_DOWNGRADE	BIT(5)
35e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_POLARITY_CHANGED	BIT(1)
36e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_WOL			BIT(0)
37e6e4a556SMartin Blumenstingl 
380ca7111aSMatus Ujhelyi #define AT803X_INTR_STATUS			0x13
39a46bd63bSMartin Blumenstingl 
4013a56b44SDaniel Mack #define AT803X_SMART_SPEED			0x14
4113a56b44SDaniel Mack #define AT803X_LED_CONTROL			0x18
42a46bd63bSMartin Blumenstingl 
430ca7111aSMatus Ujhelyi #define AT803X_DEVICE_ADDR			0x03
440ca7111aSMatus Ujhelyi #define AT803X_LOC_MAC_ADDR_0_15_OFFSET		0x804C
450ca7111aSMatus Ujhelyi #define AT803X_LOC_MAC_ADDR_16_31_OFFSET	0x804B
460ca7111aSMatus Ujhelyi #define AT803X_LOC_MAC_ADDR_32_47_OFFSET	0x804A
47f62265b5SZefir Kurtisi #define AT803X_REG_CHIP_CONFIG			0x1f
48f62265b5SZefir Kurtisi #define AT803X_BT_BX_REG_SEL			0x8000
49a46bd63bSMartin Blumenstingl 
501ca6d1b1SMugunthan V N #define AT803X_DEBUG_ADDR			0x1D
511ca6d1b1SMugunthan V N #define AT803X_DEBUG_DATA			0x1E
52a46bd63bSMartin Blumenstingl 
53f62265b5SZefir Kurtisi #define AT803X_MODE_CFG_MASK			0x0F
54f62265b5SZefir Kurtisi #define AT803X_MODE_CFG_SGMII			0x01
55f62265b5SZefir Kurtisi 
56f62265b5SZefir Kurtisi #define AT803X_PSSR			0x11	/*PHY-Specific Status Register*/
57f62265b5SZefir Kurtisi #define AT803X_PSSR_MR_AN_COMPLETE	0x0200
58f62265b5SZefir Kurtisi 
592e5f9f28SMartin Blumenstingl #define AT803X_DEBUG_REG_0			0x00
602e5f9f28SMartin Blumenstingl #define AT803X_DEBUG_RX_CLK_DLY_EN		BIT(15)
61a46bd63bSMartin Blumenstingl 
622e5f9f28SMartin Blumenstingl #define AT803X_DEBUG_REG_5			0x05
632e5f9f28SMartin Blumenstingl #define AT803X_DEBUG_TX_CLK_DLY_EN		BIT(8)
640ca7111aSMatus Ujhelyi 
65bd8ca17fSDaniel Mack #define ATH8030_PHY_ID 0x004dd076
66bd8ca17fSDaniel Mack #define ATH8031_PHY_ID 0x004dd074
67bd8ca17fSDaniel Mack #define ATH8035_PHY_ID 0x004dd072
6858effd71SFabio Estevam #define AT803X_PHY_ID_MASK			0xffffffef
69bd8ca17fSDaniel Mack 
700ca7111aSMatus Ujhelyi MODULE_DESCRIPTION("Atheros 803x PHY driver");
710ca7111aSMatus Ujhelyi MODULE_AUTHOR("Matus Ujhelyi");
720ca7111aSMatus Ujhelyi MODULE_LICENSE("GPL");
730ca7111aSMatus Ujhelyi 
7413a56b44SDaniel Mack struct at803x_priv {
7513a56b44SDaniel Mack 	bool phy_reset:1;
7613a56b44SDaniel Mack };
7713a56b44SDaniel Mack 
7813a56b44SDaniel Mack struct at803x_context {
7913a56b44SDaniel Mack 	u16 bmcr;
8013a56b44SDaniel Mack 	u16 advertise;
8113a56b44SDaniel Mack 	u16 control1000;
8213a56b44SDaniel Mack 	u16 int_enable;
8313a56b44SDaniel Mack 	u16 smart_speed;
8413a56b44SDaniel Mack 	u16 led_control;
8513a56b44SDaniel Mack };
8613a56b44SDaniel Mack 
872e5f9f28SMartin Blumenstingl static int at803x_debug_reg_read(struct phy_device *phydev, u16 reg)
882e5f9f28SMartin Blumenstingl {
892e5f9f28SMartin Blumenstingl 	int ret;
902e5f9f28SMartin Blumenstingl 
912e5f9f28SMartin Blumenstingl 	ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg);
922e5f9f28SMartin Blumenstingl 	if (ret < 0)
932e5f9f28SMartin Blumenstingl 		return ret;
942e5f9f28SMartin Blumenstingl 
952e5f9f28SMartin Blumenstingl 	return phy_read(phydev, AT803X_DEBUG_DATA);
962e5f9f28SMartin Blumenstingl }
972e5f9f28SMartin Blumenstingl 
982e5f9f28SMartin Blumenstingl static int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg,
992e5f9f28SMartin Blumenstingl 				 u16 clear, u16 set)
1002e5f9f28SMartin Blumenstingl {
1012e5f9f28SMartin Blumenstingl 	u16 val;
1022e5f9f28SMartin Blumenstingl 	int ret;
1032e5f9f28SMartin Blumenstingl 
1042e5f9f28SMartin Blumenstingl 	ret = at803x_debug_reg_read(phydev, reg);
1052e5f9f28SMartin Blumenstingl 	if (ret < 0)
1062e5f9f28SMartin Blumenstingl 		return ret;
1072e5f9f28SMartin Blumenstingl 
1082e5f9f28SMartin Blumenstingl 	val = ret & 0xffff;
1092e5f9f28SMartin Blumenstingl 	val &= ~clear;
1102e5f9f28SMartin Blumenstingl 	val |= set;
1112e5f9f28SMartin Blumenstingl 
1122e5f9f28SMartin Blumenstingl 	return phy_write(phydev, AT803X_DEBUG_DATA, val);
1132e5f9f28SMartin Blumenstingl }
1142e5f9f28SMartin Blumenstingl 
1156d4cd041SVinod Koul static int at803x_enable_rx_delay(struct phy_device *phydev)
1166d4cd041SVinod Koul {
1176d4cd041SVinod Koul 	return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0, 0,
1186d4cd041SVinod Koul 				     AT803X_DEBUG_RX_CLK_DLY_EN);
1196d4cd041SVinod Koul }
1206d4cd041SVinod Koul 
1216d4cd041SVinod Koul static int at803x_enable_tx_delay(struct phy_device *phydev)
1226d4cd041SVinod Koul {
1236d4cd041SVinod Koul 	return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_5, 0,
1246d4cd041SVinod Koul 				     AT803X_DEBUG_TX_CLK_DLY_EN);
1256d4cd041SVinod Koul }
1266d4cd041SVinod Koul 
12743f2ebd5SVinod Koul static int at803x_disable_rx_delay(struct phy_device *phydev)
1282e5f9f28SMartin Blumenstingl {
129cd28d1d6SVinod Koul 	return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0,
130cd28d1d6SVinod Koul 				     AT803X_DEBUG_RX_CLK_DLY_EN, 0);
1312e5f9f28SMartin Blumenstingl }
1322e5f9f28SMartin Blumenstingl 
13343f2ebd5SVinod Koul static int at803x_disable_tx_delay(struct phy_device *phydev)
1342e5f9f28SMartin Blumenstingl {
135cd28d1d6SVinod Koul 	return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_5,
136cd28d1d6SVinod Koul 				     AT803X_DEBUG_TX_CLK_DLY_EN, 0);
1372e5f9f28SMartin Blumenstingl }
1382e5f9f28SMartin Blumenstingl 
13913a56b44SDaniel Mack /* save relevant PHY registers to private copy */
14013a56b44SDaniel Mack static void at803x_context_save(struct phy_device *phydev,
14113a56b44SDaniel Mack 				struct at803x_context *context)
14213a56b44SDaniel Mack {
14313a56b44SDaniel Mack 	context->bmcr = phy_read(phydev, MII_BMCR);
14413a56b44SDaniel Mack 	context->advertise = phy_read(phydev, MII_ADVERTISE);
14513a56b44SDaniel Mack 	context->control1000 = phy_read(phydev, MII_CTRL1000);
14613a56b44SDaniel Mack 	context->int_enable = phy_read(phydev, AT803X_INTR_ENABLE);
14713a56b44SDaniel Mack 	context->smart_speed = phy_read(phydev, AT803X_SMART_SPEED);
14813a56b44SDaniel Mack 	context->led_control = phy_read(phydev, AT803X_LED_CONTROL);
14913a56b44SDaniel Mack }
15013a56b44SDaniel Mack 
15113a56b44SDaniel Mack /* restore relevant PHY registers from private copy */
15213a56b44SDaniel Mack static void at803x_context_restore(struct phy_device *phydev,
15313a56b44SDaniel Mack 				   const struct at803x_context *context)
15413a56b44SDaniel Mack {
15513a56b44SDaniel Mack 	phy_write(phydev, MII_BMCR, context->bmcr);
15613a56b44SDaniel Mack 	phy_write(phydev, MII_ADVERTISE, context->advertise);
15713a56b44SDaniel Mack 	phy_write(phydev, MII_CTRL1000, context->control1000);
15813a56b44SDaniel Mack 	phy_write(phydev, AT803X_INTR_ENABLE, context->int_enable);
15913a56b44SDaniel Mack 	phy_write(phydev, AT803X_SMART_SPEED, context->smart_speed);
16013a56b44SDaniel Mack 	phy_write(phydev, AT803X_LED_CONTROL, context->led_control);
16113a56b44SDaniel Mack }
16213a56b44SDaniel Mack 
163ea13c9eeSMugunthan V N static int at803x_set_wol(struct phy_device *phydev,
164ea13c9eeSMugunthan V N 			  struct ethtool_wolinfo *wol)
1650ca7111aSMatus Ujhelyi {
1660ca7111aSMatus Ujhelyi 	struct net_device *ndev = phydev->attached_dev;
1670ca7111aSMatus Ujhelyi 	const u8 *mac;
168ea13c9eeSMugunthan V N 	int ret;
169ea13c9eeSMugunthan V N 	u32 value;
1700ca7111aSMatus Ujhelyi 	unsigned int i, offsets[] = {
1710ca7111aSMatus Ujhelyi 		AT803X_LOC_MAC_ADDR_32_47_OFFSET,
1720ca7111aSMatus Ujhelyi 		AT803X_LOC_MAC_ADDR_16_31_OFFSET,
1730ca7111aSMatus Ujhelyi 		AT803X_LOC_MAC_ADDR_0_15_OFFSET,
1740ca7111aSMatus Ujhelyi 	};
1750ca7111aSMatus Ujhelyi 
1760ca7111aSMatus Ujhelyi 	if (!ndev)
177ea13c9eeSMugunthan V N 		return -ENODEV;
1780ca7111aSMatus Ujhelyi 
179ea13c9eeSMugunthan V N 	if (wol->wolopts & WAKE_MAGIC) {
1800ca7111aSMatus Ujhelyi 		mac = (const u8 *) ndev->dev_addr;
1810ca7111aSMatus Ujhelyi 
1820ca7111aSMatus Ujhelyi 		if (!is_valid_ether_addr(mac))
183fc755687SDan Murphy 			return -EINVAL;
1840ca7111aSMatus Ujhelyi 
1850e021396SCarlo Caione 		for (i = 0; i < 3; i++)
1860e021396SCarlo Caione 			phy_write_mmd(phydev, AT803X_DEVICE_ADDR, offsets[i],
1870ca7111aSMatus Ujhelyi 				      mac[(i * 2) + 1] | (mac[(i * 2)] << 8));
188ea13c9eeSMugunthan V N 
189ea13c9eeSMugunthan V N 		value = phy_read(phydev, AT803X_INTR_ENABLE);
190e6e4a556SMartin Blumenstingl 		value |= AT803X_INTR_ENABLE_WOL;
191ea13c9eeSMugunthan V N 		ret = phy_write(phydev, AT803X_INTR_ENABLE, value);
192ea13c9eeSMugunthan V N 		if (ret)
193ea13c9eeSMugunthan V N 			return ret;
194ea13c9eeSMugunthan V N 		value = phy_read(phydev, AT803X_INTR_STATUS);
195ea13c9eeSMugunthan V N 	} else {
196ea13c9eeSMugunthan V N 		value = phy_read(phydev, AT803X_INTR_ENABLE);
197e6e4a556SMartin Blumenstingl 		value &= (~AT803X_INTR_ENABLE_WOL);
198ea13c9eeSMugunthan V N 		ret = phy_write(phydev, AT803X_INTR_ENABLE, value);
199ea13c9eeSMugunthan V N 		if (ret)
200ea13c9eeSMugunthan V N 			return ret;
201ea13c9eeSMugunthan V N 		value = phy_read(phydev, AT803X_INTR_STATUS);
202ea13c9eeSMugunthan V N 	}
203ea13c9eeSMugunthan V N 
204ea13c9eeSMugunthan V N 	return ret;
205ea13c9eeSMugunthan V N }
206ea13c9eeSMugunthan V N 
207ea13c9eeSMugunthan V N static void at803x_get_wol(struct phy_device *phydev,
208ea13c9eeSMugunthan V N 			   struct ethtool_wolinfo *wol)
209ea13c9eeSMugunthan V N {
210ea13c9eeSMugunthan V N 	u32 value;
211ea13c9eeSMugunthan V N 
212ea13c9eeSMugunthan V N 	wol->supported = WAKE_MAGIC;
213ea13c9eeSMugunthan V N 	wol->wolopts = 0;
214ea13c9eeSMugunthan V N 
215ea13c9eeSMugunthan V N 	value = phy_read(phydev, AT803X_INTR_ENABLE);
216e6e4a556SMartin Blumenstingl 	if (value & AT803X_INTR_ENABLE_WOL)
217ea13c9eeSMugunthan V N 		wol->wolopts |= WAKE_MAGIC;
2180ca7111aSMatus Ujhelyi }
2190ca7111aSMatus Ujhelyi 
2206229ed1fSDaniel Mack static int at803x_suspend(struct phy_device *phydev)
2216229ed1fSDaniel Mack {
2226229ed1fSDaniel Mack 	int value;
2236229ed1fSDaniel Mack 	int wol_enabled;
2246229ed1fSDaniel Mack 
2256229ed1fSDaniel Mack 	value = phy_read(phydev, AT803X_INTR_ENABLE);
226e6e4a556SMartin Blumenstingl 	wol_enabled = value & AT803X_INTR_ENABLE_WOL;
2276229ed1fSDaniel Mack 
2286229ed1fSDaniel Mack 	if (wol_enabled)
229fea23fb5SRussell King 		value = BMCR_ISOLATE;
2306229ed1fSDaniel Mack 	else
231fea23fb5SRussell King 		value = BMCR_PDOWN;
2326229ed1fSDaniel Mack 
233fea23fb5SRussell King 	phy_modify(phydev, MII_BMCR, 0, value);
2346229ed1fSDaniel Mack 
2356229ed1fSDaniel Mack 	return 0;
2366229ed1fSDaniel Mack }
2376229ed1fSDaniel Mack 
2386229ed1fSDaniel Mack static int at803x_resume(struct phy_device *phydev)
2396229ed1fSDaniel Mack {
240f102852fSRussell King 	return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0);
2416229ed1fSDaniel Mack }
2426229ed1fSDaniel Mack 
24313a56b44SDaniel Mack static int at803x_probe(struct phy_device *phydev)
24413a56b44SDaniel Mack {
245e5a03bfdSAndrew Lunn 	struct device *dev = &phydev->mdio.dev;
24613a56b44SDaniel Mack 	struct at803x_priv *priv;
24713a56b44SDaniel Mack 
2488f2877caSFengguang Wu 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
24913a56b44SDaniel Mack 	if (!priv)
25013a56b44SDaniel Mack 		return -ENOMEM;
25113a56b44SDaniel Mack 
25213a56b44SDaniel Mack 	phydev->priv = priv;
25313a56b44SDaniel Mack 
25413a56b44SDaniel Mack 	return 0;
25513a56b44SDaniel Mack }
25613a56b44SDaniel Mack 
2570ca7111aSMatus Ujhelyi static int at803x_config_init(struct phy_device *phydev)
2580ca7111aSMatus Ujhelyi {
2591ca6d1b1SMugunthan V N 	int ret;
2600ca7111aSMatus Ujhelyi 
2616d4cd041SVinod Koul 	/* The RX and TX delay default is:
2626d4cd041SVinod Koul 	 *   after HW reset: RX delay enabled and TX delay disabled
2636d4cd041SVinod Koul 	 *   after SW reset: RX delay enabled, while TX delay retains the
2646d4cd041SVinod Koul 	 *   value before reset.
2656d4cd041SVinod Koul 	 */
266bb0ce4c1SAndré Draszik 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
267bb0ce4c1SAndré Draszik 	    phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
268bb0ce4c1SAndré Draszik 		ret = at803x_enable_rx_delay(phydev);
269bb0ce4c1SAndré Draszik 	else
270cd28d1d6SVinod Koul 		ret = at803x_disable_rx_delay(phydev);
2712e5f9f28SMartin Blumenstingl 	if (ret < 0)
2721ca6d1b1SMugunthan V N 		return ret;
2736d4cd041SVinod Koul 
2746d4cd041SVinod Koul 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
275bb0ce4c1SAndré Draszik 	    phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
2766d4cd041SVinod Koul 		ret = at803x_enable_tx_delay(phydev);
277bb0ce4c1SAndré Draszik 	else
278bb0ce4c1SAndré Draszik 		ret = at803x_disable_tx_delay(phydev);
2796d4cd041SVinod Koul 
2806d4cd041SVinod Koul 	return ret;
2810ca7111aSMatus Ujhelyi }
2820ca7111aSMatus Ujhelyi 
28377a99394SZhao Qiang static int at803x_ack_interrupt(struct phy_device *phydev)
28477a99394SZhao Qiang {
28577a99394SZhao Qiang 	int err;
28677a99394SZhao Qiang 
287a46bd63bSMartin Blumenstingl 	err = phy_read(phydev, AT803X_INTR_STATUS);
28877a99394SZhao Qiang 
28977a99394SZhao Qiang 	return (err < 0) ? err : 0;
29077a99394SZhao Qiang }
29177a99394SZhao Qiang 
29277a99394SZhao Qiang static int at803x_config_intr(struct phy_device *phydev)
29377a99394SZhao Qiang {
29477a99394SZhao Qiang 	int err;
29577a99394SZhao Qiang 	int value;
29677a99394SZhao Qiang 
297a46bd63bSMartin Blumenstingl 	value = phy_read(phydev, AT803X_INTR_ENABLE);
29877a99394SZhao Qiang 
299e6e4a556SMartin Blumenstingl 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
300e6e4a556SMartin Blumenstingl 		value |= AT803X_INTR_ENABLE_AUTONEG_ERR;
301e6e4a556SMartin Blumenstingl 		value |= AT803X_INTR_ENABLE_SPEED_CHANGED;
302e6e4a556SMartin Blumenstingl 		value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED;
303e6e4a556SMartin Blumenstingl 		value |= AT803X_INTR_ENABLE_LINK_FAIL;
304e6e4a556SMartin Blumenstingl 		value |= AT803X_INTR_ENABLE_LINK_SUCCESS;
305e6e4a556SMartin Blumenstingl 
306e6e4a556SMartin Blumenstingl 		err = phy_write(phydev, AT803X_INTR_ENABLE, value);
307e6e4a556SMartin Blumenstingl 	}
30877a99394SZhao Qiang 	else
309a46bd63bSMartin Blumenstingl 		err = phy_write(phydev, AT803X_INTR_ENABLE, 0);
31077a99394SZhao Qiang 
31177a99394SZhao Qiang 	return err;
31277a99394SZhao Qiang }
31377a99394SZhao Qiang 
31413a56b44SDaniel Mack static void at803x_link_change_notify(struct phy_device *phydev)
31513a56b44SDaniel Mack {
31613a56b44SDaniel Mack 	/*
31713a56b44SDaniel Mack 	 * Conduct a hardware reset for AT8030 every time a link loss is
31813a56b44SDaniel Mack 	 * signalled. This is necessary to circumvent a hardware bug that
31913a56b44SDaniel Mack 	 * occurs when the cable is unplugged while TX packets are pending
32013a56b44SDaniel Mack 	 * in the FIFO. In such cases, the FIFO enters an error mode it
32113a56b44SDaniel Mack 	 * cannot recover from by software.
32213a56b44SDaniel Mack 	 */
3236110ed2dSDavid Bauer 	if (phydev->state == PHY_NOLINK && phydev->mdio.reset_gpio) {
32413a56b44SDaniel Mack 		struct at803x_context context;
32513a56b44SDaniel Mack 
32613a56b44SDaniel Mack 		at803x_context_save(phydev, &context);
32713a56b44SDaniel Mack 
328bafbdd52SSergei Shtylyov 		phy_device_reset(phydev, 1);
32913a56b44SDaniel Mack 		msleep(1);
330bafbdd52SSergei Shtylyov 		phy_device_reset(phydev, 0);
331d57019d1SSergei Shtylyov 		msleep(1);
33213a56b44SDaniel Mack 
33313a56b44SDaniel Mack 		at803x_context_restore(phydev, &context);
33413a56b44SDaniel Mack 
3355c5f626bSHeiner Kallweit 		phydev_dbg(phydev, "%s(): phy was reset\n", __func__);
33613a56b44SDaniel Mack 	}
33713a56b44SDaniel Mack }
33813a56b44SDaniel Mack 
339f62265b5SZefir Kurtisi static int at803x_aneg_done(struct phy_device *phydev)
340f62265b5SZefir Kurtisi {
341f62265b5SZefir Kurtisi 	int ccr;
342f62265b5SZefir Kurtisi 
343f62265b5SZefir Kurtisi 	int aneg_done = genphy_aneg_done(phydev);
344f62265b5SZefir Kurtisi 	if (aneg_done != BMSR_ANEGCOMPLETE)
345f62265b5SZefir Kurtisi 		return aneg_done;
346f62265b5SZefir Kurtisi 
347f62265b5SZefir Kurtisi 	/*
348f62265b5SZefir Kurtisi 	 * in SGMII mode, if copper side autoneg is successful,
349f62265b5SZefir Kurtisi 	 * also check SGMII side autoneg result
350f62265b5SZefir Kurtisi 	 */
351f62265b5SZefir Kurtisi 	ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG);
352f62265b5SZefir Kurtisi 	if ((ccr & AT803X_MODE_CFG_MASK) != AT803X_MODE_CFG_SGMII)
353f62265b5SZefir Kurtisi 		return aneg_done;
354f62265b5SZefir Kurtisi 
355f62265b5SZefir Kurtisi 	/* switch to SGMII/fiber page */
356f62265b5SZefir Kurtisi 	phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr & ~AT803X_BT_BX_REG_SEL);
357f62265b5SZefir Kurtisi 
358f62265b5SZefir Kurtisi 	/* check if the SGMII link is OK. */
359f62265b5SZefir Kurtisi 	if (!(phy_read(phydev, AT803X_PSSR) & AT803X_PSSR_MR_AN_COMPLETE)) {
360ab2a605fSAndrew Lunn 		phydev_warn(phydev, "803x_aneg_done: SGMII link is not ok\n");
361f62265b5SZefir Kurtisi 		aneg_done = 0;
362f62265b5SZefir Kurtisi 	}
363f62265b5SZefir Kurtisi 	/* switch back to copper page */
364f62265b5SZefir Kurtisi 	phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr | AT803X_BT_BX_REG_SEL);
365f62265b5SZefir Kurtisi 
366f62265b5SZefir Kurtisi 	return aneg_done;
367f62265b5SZefir Kurtisi }
368f62265b5SZefir Kurtisi 
36906d5f344SRussell King static int at803x_read_status(struct phy_device *phydev)
37006d5f344SRussell King {
37106d5f344SRussell King 	int ss, err, old_link = phydev->link;
37206d5f344SRussell King 
37306d5f344SRussell King 	/* Update the link, but return if there was an error */
37406d5f344SRussell King 	err = genphy_update_link(phydev);
37506d5f344SRussell King 	if (err)
37606d5f344SRussell King 		return err;
37706d5f344SRussell King 
37806d5f344SRussell King 	/* why bother the PHY if nothing can have changed */
37906d5f344SRussell King 	if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)
38006d5f344SRussell King 		return 0;
38106d5f344SRussell King 
38206d5f344SRussell King 	phydev->speed = SPEED_UNKNOWN;
38306d5f344SRussell King 	phydev->duplex = DUPLEX_UNKNOWN;
38406d5f344SRussell King 	phydev->pause = 0;
38506d5f344SRussell King 	phydev->asym_pause = 0;
38606d5f344SRussell King 
38706d5f344SRussell King 	err = genphy_read_lpa(phydev);
38806d5f344SRussell King 	if (err < 0)
38906d5f344SRussell King 		return err;
39006d5f344SRussell King 
39106d5f344SRussell King 	/* Read the AT8035 PHY-Specific Status register, which indicates the
39206d5f344SRussell King 	 * speed and duplex that the PHY is actually using, irrespective of
39306d5f344SRussell King 	 * whether we are in autoneg mode or not.
39406d5f344SRussell King 	 */
39506d5f344SRussell King 	ss = phy_read(phydev, AT803X_SPECIFIC_STATUS);
39606d5f344SRussell King 	if (ss < 0)
39706d5f344SRussell King 		return ss;
39806d5f344SRussell King 
39906d5f344SRussell King 	if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) {
40006d5f344SRussell King 		switch (ss & AT803X_SS_SPEED_MASK) {
40106d5f344SRussell King 		case AT803X_SS_SPEED_10:
40206d5f344SRussell King 			phydev->speed = SPEED_10;
40306d5f344SRussell King 			break;
40406d5f344SRussell King 		case AT803X_SS_SPEED_100:
40506d5f344SRussell King 			phydev->speed = SPEED_100;
40606d5f344SRussell King 			break;
40706d5f344SRussell King 		case AT803X_SS_SPEED_1000:
40806d5f344SRussell King 			phydev->speed = SPEED_1000;
40906d5f344SRussell King 			break;
41006d5f344SRussell King 		}
41106d5f344SRussell King 		if (ss & AT803X_SS_DUPLEX)
41206d5f344SRussell King 			phydev->duplex = DUPLEX_FULL;
41306d5f344SRussell King 		else
41406d5f344SRussell King 			phydev->duplex = DUPLEX_HALF;
41506d5f344SRussell King 		if (ss & AT803X_SS_MDIX)
41606d5f344SRussell King 			phydev->mdix = ETH_TP_MDI_X;
41706d5f344SRussell King 		else
41806d5f344SRussell King 			phydev->mdix = ETH_TP_MDI;
41906d5f344SRussell King 	}
42006d5f344SRussell King 
42106d5f344SRussell King 	if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete)
42206d5f344SRussell King 		phy_resolve_aneg_pause(phydev);
42306d5f344SRussell King 
42406d5f344SRussell King 	return 0;
42506d5f344SRussell King }
42606d5f344SRussell King 
427317420abSMugunthan V N static struct phy_driver at803x_driver[] = {
428317420abSMugunthan V N {
4290ca7111aSMatus Ujhelyi 	/* ATHEROS 8035 */
430bd8ca17fSDaniel Mack 	.phy_id			= ATH8035_PHY_ID,
4310ca7111aSMatus Ujhelyi 	.name			= "Atheros 8035 ethernet",
43258effd71SFabio Estevam 	.phy_id_mask		= AT803X_PHY_ID_MASK,
43313a56b44SDaniel Mack 	.probe			= at803x_probe,
4340ca7111aSMatus Ujhelyi 	.config_init		= at803x_config_init,
435ea13c9eeSMugunthan V N 	.set_wol		= at803x_set_wol,
436ea13c9eeSMugunthan V N 	.get_wol		= at803x_get_wol,
4376229ed1fSDaniel Mack 	.suspend		= at803x_suspend,
4386229ed1fSDaniel Mack 	.resume			= at803x_resume,
439dcdecdcfSHeiner Kallweit 	/* PHY_GBIT_FEATURES */
44006d5f344SRussell King 	.read_status		= at803x_read_status,
4410eae5982SMåns Rullgård 	.ack_interrupt		= at803x_ack_interrupt,
4420eae5982SMåns Rullgård 	.config_intr		= at803x_config_intr,
443317420abSMugunthan V N }, {
4440ca7111aSMatus Ujhelyi 	/* ATHEROS 8030 */
445bd8ca17fSDaniel Mack 	.phy_id			= ATH8030_PHY_ID,
4460ca7111aSMatus Ujhelyi 	.name			= "Atheros 8030 ethernet",
44758effd71SFabio Estevam 	.phy_id_mask		= AT803X_PHY_ID_MASK,
44813a56b44SDaniel Mack 	.probe			= at803x_probe,
4490ca7111aSMatus Ujhelyi 	.config_init		= at803x_config_init,
45013a56b44SDaniel Mack 	.link_change_notify	= at803x_link_change_notify,
451ea13c9eeSMugunthan V N 	.set_wol		= at803x_set_wol,
452ea13c9eeSMugunthan V N 	.get_wol		= at803x_get_wol,
4536229ed1fSDaniel Mack 	.suspend		= at803x_suspend,
4546229ed1fSDaniel Mack 	.resume			= at803x_resume,
455dcdecdcfSHeiner Kallweit 	/* PHY_BASIC_FEATURES */
4560eae5982SMåns Rullgård 	.ack_interrupt		= at803x_ack_interrupt,
4570eae5982SMåns Rullgård 	.config_intr		= at803x_config_intr,
45805d7cce8SMugunthan V N }, {
45905d7cce8SMugunthan V N 	/* ATHEROS 8031 */
460bd8ca17fSDaniel Mack 	.phy_id			= ATH8031_PHY_ID,
46105d7cce8SMugunthan V N 	.name			= "Atheros 8031 ethernet",
46258effd71SFabio Estevam 	.phy_id_mask		= AT803X_PHY_ID_MASK,
46313a56b44SDaniel Mack 	.probe			= at803x_probe,
46405d7cce8SMugunthan V N 	.config_init		= at803x_config_init,
46505d7cce8SMugunthan V N 	.set_wol		= at803x_set_wol,
46605d7cce8SMugunthan V N 	.get_wol		= at803x_get_wol,
4676229ed1fSDaniel Mack 	.suspend		= at803x_suspend,
4686229ed1fSDaniel Mack 	.resume			= at803x_resume,
469dcdecdcfSHeiner Kallweit 	/* PHY_GBIT_FEATURES */
47006d5f344SRussell King 	.read_status		= at803x_read_status,
471f62265b5SZefir Kurtisi 	.aneg_done		= at803x_aneg_done,
47277a99394SZhao Qiang 	.ack_interrupt		= &at803x_ack_interrupt,
47377a99394SZhao Qiang 	.config_intr		= &at803x_config_intr,
474317420abSMugunthan V N } };
4750ca7111aSMatus Ujhelyi 
47650fd7150SJohan Hovold module_phy_driver(at803x_driver);
4770ca7111aSMatus Ujhelyi 
4780ca7111aSMatus Ujhelyi static struct mdio_device_id __maybe_unused atheros_tbl[] = {
47958effd71SFabio Estevam 	{ ATH8030_PHY_ID, AT803X_PHY_ID_MASK },
48058effd71SFabio Estevam 	{ ATH8031_PHY_ID, AT803X_PHY_ID_MASK },
48158effd71SFabio Estevam 	{ ATH8035_PHY_ID, AT803X_PHY_ID_MASK },
4820ca7111aSMatus Ujhelyi 	{ }
4830ca7111aSMatus Ujhelyi };
4840ca7111aSMatus Ujhelyi 
4850ca7111aSMatus Ujhelyi MODULE_DEVICE_TABLE(mdio, atheros_tbl);
486