xref: /openbmc/linux/drivers/net/phy/at803x.c (revision 428061f7)
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>
162f664823SMichael Walle #include <linux/bitfield.h>
1713a56b44SDaniel Mack #include <linux/gpio/consumer.h>
182f664823SMichael Walle #include <linux/regulator/of_regulator.h>
192f664823SMichael Walle #include <linux/regulator/driver.h>
202f664823SMichael Walle #include <linux/regulator/consumer.h>
212f664823SMichael Walle #include <dt-bindings/net/qca-ar803x.h>
220ca7111aSMatus Ujhelyi 
2306d5f344SRussell King #define AT803X_SPECIFIC_STATUS			0x11
2406d5f344SRussell King #define AT803X_SS_SPEED_MASK			(3 << 14)
2506d5f344SRussell King #define AT803X_SS_SPEED_1000			(2 << 14)
2606d5f344SRussell King #define AT803X_SS_SPEED_100			(1 << 14)
2706d5f344SRussell King #define AT803X_SS_SPEED_10			(0 << 14)
2806d5f344SRussell King #define AT803X_SS_DUPLEX			BIT(13)
2906d5f344SRussell King #define AT803X_SS_SPEED_DUPLEX_RESOLVED		BIT(11)
3006d5f344SRussell King #define AT803X_SS_MDIX				BIT(6)
3106d5f344SRussell King 
320ca7111aSMatus Ujhelyi #define AT803X_INTR_ENABLE			0x12
33e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_AUTONEG_ERR		BIT(15)
34e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_SPEED_CHANGED	BIT(14)
35e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_DUPLEX_CHANGED	BIT(13)
36e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_PAGE_RECEIVED	BIT(12)
37e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_LINK_FAIL		BIT(11)
38e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_LINK_SUCCESS		BIT(10)
39e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_WIRESPEED_DOWNGRADE	BIT(5)
40e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_POLARITY_CHANGED	BIT(1)
41e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_WOL			BIT(0)
42e6e4a556SMartin Blumenstingl 
430ca7111aSMatus Ujhelyi #define AT803X_INTR_STATUS			0x13
44a46bd63bSMartin Blumenstingl 
4513a56b44SDaniel Mack #define AT803X_SMART_SPEED			0x14
4613a56b44SDaniel Mack #define AT803X_LED_CONTROL			0x18
47a46bd63bSMartin Blumenstingl 
480ca7111aSMatus Ujhelyi #define AT803X_DEVICE_ADDR			0x03
490ca7111aSMatus Ujhelyi #define AT803X_LOC_MAC_ADDR_0_15_OFFSET		0x804C
500ca7111aSMatus Ujhelyi #define AT803X_LOC_MAC_ADDR_16_31_OFFSET	0x804B
510ca7111aSMatus Ujhelyi #define AT803X_LOC_MAC_ADDR_32_47_OFFSET	0x804A
52f62265b5SZefir Kurtisi #define AT803X_REG_CHIP_CONFIG			0x1f
53f62265b5SZefir Kurtisi #define AT803X_BT_BX_REG_SEL			0x8000
54a46bd63bSMartin Blumenstingl 
551ca6d1b1SMugunthan V N #define AT803X_DEBUG_ADDR			0x1D
561ca6d1b1SMugunthan V N #define AT803X_DEBUG_DATA			0x1E
57a46bd63bSMartin Blumenstingl 
58f62265b5SZefir Kurtisi #define AT803X_MODE_CFG_MASK			0x0F
59f62265b5SZefir Kurtisi #define AT803X_MODE_CFG_SGMII			0x01
60f62265b5SZefir Kurtisi 
61f62265b5SZefir Kurtisi #define AT803X_PSSR			0x11	/*PHY-Specific Status Register*/
62f62265b5SZefir Kurtisi #define AT803X_PSSR_MR_AN_COMPLETE	0x0200
63f62265b5SZefir Kurtisi 
642e5f9f28SMartin Blumenstingl #define AT803X_DEBUG_REG_0			0x00
652e5f9f28SMartin Blumenstingl #define AT803X_DEBUG_RX_CLK_DLY_EN		BIT(15)
66a46bd63bSMartin Blumenstingl 
672e5f9f28SMartin Blumenstingl #define AT803X_DEBUG_REG_5			0x05
682e5f9f28SMartin Blumenstingl #define AT803X_DEBUG_TX_CLK_DLY_EN		BIT(8)
690ca7111aSMatus Ujhelyi 
702f664823SMichael Walle #define AT803X_DEBUG_REG_1F			0x1F
712f664823SMichael Walle #define AT803X_DEBUG_PLL_ON			BIT(2)
722f664823SMichael Walle #define AT803X_DEBUG_RGMII_1V8			BIT(3)
732f664823SMichael Walle 
742f664823SMichael Walle /* AT803x supports either the XTAL input pad, an internal PLL or the
752f664823SMichael Walle  * DSP as clock reference for the clock output pad. The XTAL reference
762f664823SMichael Walle  * is only used for 25 MHz output, all other frequencies need the PLL.
772f664823SMichael Walle  * The DSP as a clock reference is used in synchronous ethernet
782f664823SMichael Walle  * applications.
792f664823SMichael Walle  *
802f664823SMichael Walle  * By default the PLL is only enabled if there is a link. Otherwise
812f664823SMichael Walle  * the PHY will go into low power state and disabled the PLL. You can
822f664823SMichael Walle  * set the PLL_ON bit (see debug register 0x1f) to keep the PLL always
832f664823SMichael Walle  * enabled.
842f664823SMichael Walle  */
852f664823SMichael Walle #define AT803X_MMD7_CLK25M			0x8016
862f664823SMichael Walle #define AT803X_CLK_OUT_MASK			GENMASK(4, 2)
872f664823SMichael Walle #define AT803X_CLK_OUT_25MHZ_XTAL		0
882f664823SMichael Walle #define AT803X_CLK_OUT_25MHZ_DSP		1
892f664823SMichael Walle #define AT803X_CLK_OUT_50MHZ_PLL		2
902f664823SMichael Walle #define AT803X_CLK_OUT_50MHZ_DSP		3
912f664823SMichael Walle #define AT803X_CLK_OUT_62_5MHZ_PLL		4
922f664823SMichael Walle #define AT803X_CLK_OUT_62_5MHZ_DSP		5
932f664823SMichael Walle #define AT803X_CLK_OUT_125MHZ_PLL		6
942f664823SMichael Walle #define AT803X_CLK_OUT_125MHZ_DSP		7
952f664823SMichael Walle 
96428061f7SMichael Walle /* The AR8035 has another mask which is compatible with the AR8031/AR8033 mask
97428061f7SMichael Walle  * but doesn't support choosing between XTAL/PLL and DSP.
982f664823SMichael Walle  */
992f664823SMichael Walle #define AT8035_CLK_OUT_MASK			GENMASK(4, 3)
1002f664823SMichael Walle 
1012f664823SMichael Walle #define AT803X_CLK_OUT_STRENGTH_MASK		GENMASK(8, 7)
1022f664823SMichael Walle #define AT803X_CLK_OUT_STRENGTH_FULL		0
1032f664823SMichael Walle #define AT803X_CLK_OUT_STRENGTH_HALF		1
1042f664823SMichael Walle #define AT803X_CLK_OUT_STRENGTH_QUARTER		2
1052f664823SMichael Walle 
1067908d2ceSOleksij Rempel #define ATH9331_PHY_ID 0x004dd041
107bd8ca17fSDaniel Mack #define ATH8030_PHY_ID 0x004dd076
108bd8ca17fSDaniel Mack #define ATH8031_PHY_ID 0x004dd074
109bd8ca17fSDaniel Mack #define ATH8035_PHY_ID 0x004dd072
11058effd71SFabio Estevam #define AT803X_PHY_ID_MASK			0xffffffef
111bd8ca17fSDaniel Mack 
1120ca7111aSMatus Ujhelyi MODULE_DESCRIPTION("Atheros 803x PHY driver");
1130ca7111aSMatus Ujhelyi MODULE_AUTHOR("Matus Ujhelyi");
1140ca7111aSMatus Ujhelyi MODULE_LICENSE("GPL");
1150ca7111aSMatus Ujhelyi 
1162f664823SMichael Walle struct at803x_priv {
1172f664823SMichael Walle 	int flags;
1182f664823SMichael Walle #define AT803X_KEEP_PLL_ENABLED	BIT(0)	/* don't turn off internal PLL */
1192f664823SMichael Walle 	u16 clk_25m_reg;
1202f664823SMichael Walle 	u16 clk_25m_mask;
1212f664823SMichael Walle 	struct regulator_dev *vddio_rdev;
1222f664823SMichael Walle 	struct regulator_dev *vddh_rdev;
1232f664823SMichael Walle 	struct regulator *vddio;
1242f664823SMichael Walle };
1252f664823SMichael Walle 
12613a56b44SDaniel Mack struct at803x_context {
12713a56b44SDaniel Mack 	u16 bmcr;
12813a56b44SDaniel Mack 	u16 advertise;
12913a56b44SDaniel Mack 	u16 control1000;
13013a56b44SDaniel Mack 	u16 int_enable;
13113a56b44SDaniel Mack 	u16 smart_speed;
13213a56b44SDaniel Mack 	u16 led_control;
13313a56b44SDaniel Mack };
13413a56b44SDaniel Mack 
1352e5f9f28SMartin Blumenstingl static int at803x_debug_reg_read(struct phy_device *phydev, u16 reg)
1362e5f9f28SMartin Blumenstingl {
1372e5f9f28SMartin Blumenstingl 	int ret;
1382e5f9f28SMartin Blumenstingl 
1392e5f9f28SMartin Blumenstingl 	ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg);
1402e5f9f28SMartin Blumenstingl 	if (ret < 0)
1412e5f9f28SMartin Blumenstingl 		return ret;
1422e5f9f28SMartin Blumenstingl 
1432e5f9f28SMartin Blumenstingl 	return phy_read(phydev, AT803X_DEBUG_DATA);
1442e5f9f28SMartin Blumenstingl }
1452e5f9f28SMartin Blumenstingl 
1462e5f9f28SMartin Blumenstingl static int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg,
1472e5f9f28SMartin Blumenstingl 				 u16 clear, u16 set)
1482e5f9f28SMartin Blumenstingl {
1492e5f9f28SMartin Blumenstingl 	u16 val;
1502e5f9f28SMartin Blumenstingl 	int ret;
1512e5f9f28SMartin Blumenstingl 
1522e5f9f28SMartin Blumenstingl 	ret = at803x_debug_reg_read(phydev, reg);
1532e5f9f28SMartin Blumenstingl 	if (ret < 0)
1542e5f9f28SMartin Blumenstingl 		return ret;
1552e5f9f28SMartin Blumenstingl 
1562e5f9f28SMartin Blumenstingl 	val = ret & 0xffff;
1572e5f9f28SMartin Blumenstingl 	val &= ~clear;
1582e5f9f28SMartin Blumenstingl 	val |= set;
1592e5f9f28SMartin Blumenstingl 
1602e5f9f28SMartin Blumenstingl 	return phy_write(phydev, AT803X_DEBUG_DATA, val);
1612e5f9f28SMartin Blumenstingl }
1622e5f9f28SMartin Blumenstingl 
1636d4cd041SVinod Koul static int at803x_enable_rx_delay(struct phy_device *phydev)
1646d4cd041SVinod Koul {
1656d4cd041SVinod Koul 	return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0, 0,
1666d4cd041SVinod Koul 				     AT803X_DEBUG_RX_CLK_DLY_EN);
1676d4cd041SVinod Koul }
1686d4cd041SVinod Koul 
1696d4cd041SVinod Koul static int at803x_enable_tx_delay(struct phy_device *phydev)
1706d4cd041SVinod Koul {
1716d4cd041SVinod Koul 	return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_5, 0,
1726d4cd041SVinod Koul 				     AT803X_DEBUG_TX_CLK_DLY_EN);
1736d4cd041SVinod Koul }
1746d4cd041SVinod Koul 
17543f2ebd5SVinod Koul static int at803x_disable_rx_delay(struct phy_device *phydev)
1762e5f9f28SMartin Blumenstingl {
177cd28d1d6SVinod Koul 	return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0,
178cd28d1d6SVinod Koul 				     AT803X_DEBUG_RX_CLK_DLY_EN, 0);
1792e5f9f28SMartin Blumenstingl }
1802e5f9f28SMartin Blumenstingl 
18143f2ebd5SVinod Koul static int at803x_disable_tx_delay(struct phy_device *phydev)
1822e5f9f28SMartin Blumenstingl {
183cd28d1d6SVinod Koul 	return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_5,
184cd28d1d6SVinod Koul 				     AT803X_DEBUG_TX_CLK_DLY_EN, 0);
1852e5f9f28SMartin Blumenstingl }
1862e5f9f28SMartin Blumenstingl 
18713a56b44SDaniel Mack /* save relevant PHY registers to private copy */
18813a56b44SDaniel Mack static void at803x_context_save(struct phy_device *phydev,
18913a56b44SDaniel Mack 				struct at803x_context *context)
19013a56b44SDaniel Mack {
19113a56b44SDaniel Mack 	context->bmcr = phy_read(phydev, MII_BMCR);
19213a56b44SDaniel Mack 	context->advertise = phy_read(phydev, MII_ADVERTISE);
19313a56b44SDaniel Mack 	context->control1000 = phy_read(phydev, MII_CTRL1000);
19413a56b44SDaniel Mack 	context->int_enable = phy_read(phydev, AT803X_INTR_ENABLE);
19513a56b44SDaniel Mack 	context->smart_speed = phy_read(phydev, AT803X_SMART_SPEED);
19613a56b44SDaniel Mack 	context->led_control = phy_read(phydev, AT803X_LED_CONTROL);
19713a56b44SDaniel Mack }
19813a56b44SDaniel Mack 
19913a56b44SDaniel Mack /* restore relevant PHY registers from private copy */
20013a56b44SDaniel Mack static void at803x_context_restore(struct phy_device *phydev,
20113a56b44SDaniel Mack 				   const struct at803x_context *context)
20213a56b44SDaniel Mack {
20313a56b44SDaniel Mack 	phy_write(phydev, MII_BMCR, context->bmcr);
20413a56b44SDaniel Mack 	phy_write(phydev, MII_ADVERTISE, context->advertise);
20513a56b44SDaniel Mack 	phy_write(phydev, MII_CTRL1000, context->control1000);
20613a56b44SDaniel Mack 	phy_write(phydev, AT803X_INTR_ENABLE, context->int_enable);
20713a56b44SDaniel Mack 	phy_write(phydev, AT803X_SMART_SPEED, context->smart_speed);
20813a56b44SDaniel Mack 	phy_write(phydev, AT803X_LED_CONTROL, context->led_control);
20913a56b44SDaniel Mack }
21013a56b44SDaniel Mack 
211ea13c9eeSMugunthan V N static int at803x_set_wol(struct phy_device *phydev,
212ea13c9eeSMugunthan V N 			  struct ethtool_wolinfo *wol)
2130ca7111aSMatus Ujhelyi {
2140ca7111aSMatus Ujhelyi 	struct net_device *ndev = phydev->attached_dev;
2150ca7111aSMatus Ujhelyi 	const u8 *mac;
216ea13c9eeSMugunthan V N 	int ret;
217ea13c9eeSMugunthan V N 	u32 value;
2180ca7111aSMatus Ujhelyi 	unsigned int i, offsets[] = {
2190ca7111aSMatus Ujhelyi 		AT803X_LOC_MAC_ADDR_32_47_OFFSET,
2200ca7111aSMatus Ujhelyi 		AT803X_LOC_MAC_ADDR_16_31_OFFSET,
2210ca7111aSMatus Ujhelyi 		AT803X_LOC_MAC_ADDR_0_15_OFFSET,
2220ca7111aSMatus Ujhelyi 	};
2230ca7111aSMatus Ujhelyi 
2240ca7111aSMatus Ujhelyi 	if (!ndev)
225ea13c9eeSMugunthan V N 		return -ENODEV;
2260ca7111aSMatus Ujhelyi 
227ea13c9eeSMugunthan V N 	if (wol->wolopts & WAKE_MAGIC) {
2280ca7111aSMatus Ujhelyi 		mac = (const u8 *) ndev->dev_addr;
2290ca7111aSMatus Ujhelyi 
2300ca7111aSMatus Ujhelyi 		if (!is_valid_ether_addr(mac))
231fc755687SDan Murphy 			return -EINVAL;
2320ca7111aSMatus Ujhelyi 
2330e021396SCarlo Caione 		for (i = 0; i < 3; i++)
2340e021396SCarlo Caione 			phy_write_mmd(phydev, AT803X_DEVICE_ADDR, offsets[i],
2350ca7111aSMatus Ujhelyi 				      mac[(i * 2) + 1] | (mac[(i * 2)] << 8));
236ea13c9eeSMugunthan V N 
237ea13c9eeSMugunthan V N 		value = phy_read(phydev, AT803X_INTR_ENABLE);
238e6e4a556SMartin Blumenstingl 		value |= AT803X_INTR_ENABLE_WOL;
239ea13c9eeSMugunthan V N 		ret = phy_write(phydev, AT803X_INTR_ENABLE, value);
240ea13c9eeSMugunthan V N 		if (ret)
241ea13c9eeSMugunthan V N 			return ret;
242ea13c9eeSMugunthan V N 		value = phy_read(phydev, AT803X_INTR_STATUS);
243ea13c9eeSMugunthan V N 	} else {
244ea13c9eeSMugunthan V N 		value = phy_read(phydev, AT803X_INTR_ENABLE);
245e6e4a556SMartin Blumenstingl 		value &= (~AT803X_INTR_ENABLE_WOL);
246ea13c9eeSMugunthan V N 		ret = phy_write(phydev, AT803X_INTR_ENABLE, value);
247ea13c9eeSMugunthan V N 		if (ret)
248ea13c9eeSMugunthan V N 			return ret;
249ea13c9eeSMugunthan V N 		value = phy_read(phydev, AT803X_INTR_STATUS);
250ea13c9eeSMugunthan V N 	}
251ea13c9eeSMugunthan V N 
252ea13c9eeSMugunthan V N 	return ret;
253ea13c9eeSMugunthan V N }
254ea13c9eeSMugunthan V N 
255ea13c9eeSMugunthan V N static void at803x_get_wol(struct phy_device *phydev,
256ea13c9eeSMugunthan V N 			   struct ethtool_wolinfo *wol)
257ea13c9eeSMugunthan V N {
258ea13c9eeSMugunthan V N 	u32 value;
259ea13c9eeSMugunthan V N 
260ea13c9eeSMugunthan V N 	wol->supported = WAKE_MAGIC;
261ea13c9eeSMugunthan V N 	wol->wolopts = 0;
262ea13c9eeSMugunthan V N 
263ea13c9eeSMugunthan V N 	value = phy_read(phydev, AT803X_INTR_ENABLE);
264e6e4a556SMartin Blumenstingl 	if (value & AT803X_INTR_ENABLE_WOL)
265ea13c9eeSMugunthan V N 		wol->wolopts |= WAKE_MAGIC;
2660ca7111aSMatus Ujhelyi }
2670ca7111aSMatus Ujhelyi 
2686229ed1fSDaniel Mack static int at803x_suspend(struct phy_device *phydev)
2696229ed1fSDaniel Mack {
2706229ed1fSDaniel Mack 	int value;
2716229ed1fSDaniel Mack 	int wol_enabled;
2726229ed1fSDaniel Mack 
2736229ed1fSDaniel Mack 	value = phy_read(phydev, AT803X_INTR_ENABLE);
274e6e4a556SMartin Blumenstingl 	wol_enabled = value & AT803X_INTR_ENABLE_WOL;
2756229ed1fSDaniel Mack 
2766229ed1fSDaniel Mack 	if (wol_enabled)
277fea23fb5SRussell King 		value = BMCR_ISOLATE;
2786229ed1fSDaniel Mack 	else
279fea23fb5SRussell King 		value = BMCR_PDOWN;
2806229ed1fSDaniel Mack 
281fea23fb5SRussell King 	phy_modify(phydev, MII_BMCR, 0, value);
2826229ed1fSDaniel Mack 
2836229ed1fSDaniel Mack 	return 0;
2846229ed1fSDaniel Mack }
2856229ed1fSDaniel Mack 
2866229ed1fSDaniel Mack static int at803x_resume(struct phy_device *phydev)
2876229ed1fSDaniel Mack {
288f102852fSRussell King 	return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0);
2896229ed1fSDaniel Mack }
2906229ed1fSDaniel Mack 
2912f664823SMichael Walle static int at803x_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev,
2922f664823SMichael Walle 					    unsigned int selector)
2932f664823SMichael Walle {
2942f664823SMichael Walle 	struct phy_device *phydev = rdev_get_drvdata(rdev);
2952f664823SMichael Walle 
2962f664823SMichael Walle 	if (selector)
2972f664823SMichael Walle 		return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
2982f664823SMichael Walle 					     0, AT803X_DEBUG_RGMII_1V8);
2992f664823SMichael Walle 	else
3002f664823SMichael Walle 		return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
3012f664823SMichael Walle 					     AT803X_DEBUG_RGMII_1V8, 0);
3022f664823SMichael Walle }
3032f664823SMichael Walle 
3042f664823SMichael Walle static int at803x_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev)
3052f664823SMichael Walle {
3062f664823SMichael Walle 	struct phy_device *phydev = rdev_get_drvdata(rdev);
3072f664823SMichael Walle 	int val;
3082f664823SMichael Walle 
3092f664823SMichael Walle 	val = at803x_debug_reg_read(phydev, AT803X_DEBUG_REG_1F);
3102f664823SMichael Walle 	if (val < 0)
3112f664823SMichael Walle 		return val;
3122f664823SMichael Walle 
3132f664823SMichael Walle 	return (val & AT803X_DEBUG_RGMII_1V8) ? 1 : 0;
3142f664823SMichael Walle }
3152f664823SMichael Walle 
3162f664823SMichael Walle static struct regulator_ops vddio_regulator_ops = {
3172f664823SMichael Walle 	.list_voltage = regulator_list_voltage_table,
3182f664823SMichael Walle 	.set_voltage_sel = at803x_rgmii_reg_set_voltage_sel,
3192f664823SMichael Walle 	.get_voltage_sel = at803x_rgmii_reg_get_voltage_sel,
3202f664823SMichael Walle };
3212f664823SMichael Walle 
3222f664823SMichael Walle static const unsigned int vddio_voltage_table[] = {
3232f664823SMichael Walle 	1500000,
3242f664823SMichael Walle 	1800000,
3252f664823SMichael Walle };
3262f664823SMichael Walle 
3272f664823SMichael Walle static const struct regulator_desc vddio_desc = {
3282f664823SMichael Walle 	.name = "vddio",
3292f664823SMichael Walle 	.of_match = of_match_ptr("vddio-regulator"),
3302f664823SMichael Walle 	.n_voltages = ARRAY_SIZE(vddio_voltage_table),
3312f664823SMichael Walle 	.volt_table = vddio_voltage_table,
3322f664823SMichael Walle 	.ops = &vddio_regulator_ops,
3332f664823SMichael Walle 	.type = REGULATOR_VOLTAGE,
3342f664823SMichael Walle 	.owner = THIS_MODULE,
3352f664823SMichael Walle };
3362f664823SMichael Walle 
3372f664823SMichael Walle static struct regulator_ops vddh_regulator_ops = {
3382f664823SMichael Walle };
3392f664823SMichael Walle 
3402f664823SMichael Walle static const struct regulator_desc vddh_desc = {
3412f664823SMichael Walle 	.name = "vddh",
3422f664823SMichael Walle 	.of_match = of_match_ptr("vddh-regulator"),
3432f664823SMichael Walle 	.n_voltages = 1,
3442f664823SMichael Walle 	.fixed_uV = 2500000,
3452f664823SMichael Walle 	.ops = &vddh_regulator_ops,
3462f664823SMichael Walle 	.type = REGULATOR_VOLTAGE,
3472f664823SMichael Walle 	.owner = THIS_MODULE,
3482f664823SMichael Walle };
3492f664823SMichael Walle 
3502f664823SMichael Walle static int at8031_register_regulators(struct phy_device *phydev)
3512f664823SMichael Walle {
3522f664823SMichael Walle 	struct at803x_priv *priv = phydev->priv;
3532f664823SMichael Walle 	struct device *dev = &phydev->mdio.dev;
3542f664823SMichael Walle 	struct regulator_config config = { };
3552f664823SMichael Walle 
3562f664823SMichael Walle 	config.dev = dev;
3572f664823SMichael Walle 	config.driver_data = phydev;
3582f664823SMichael Walle 
3592f664823SMichael Walle 	priv->vddio_rdev = devm_regulator_register(dev, &vddio_desc, &config);
3602f664823SMichael Walle 	if (IS_ERR(priv->vddio_rdev)) {
3612f664823SMichael Walle 		phydev_err(phydev, "failed to register VDDIO regulator\n");
3622f664823SMichael Walle 		return PTR_ERR(priv->vddio_rdev);
3632f664823SMichael Walle 	}
3642f664823SMichael Walle 
3652f664823SMichael Walle 	priv->vddh_rdev = devm_regulator_register(dev, &vddh_desc, &config);
3662f664823SMichael Walle 	if (IS_ERR(priv->vddh_rdev)) {
3672f664823SMichael Walle 		phydev_err(phydev, "failed to register VDDH regulator\n");
3682f664823SMichael Walle 		return PTR_ERR(priv->vddh_rdev);
3692f664823SMichael Walle 	}
3702f664823SMichael Walle 
3712f664823SMichael Walle 	return 0;
3722f664823SMichael Walle }
3732f664823SMichael Walle 
3742f664823SMichael Walle static bool at803x_match_phy_id(struct phy_device *phydev, u32 phy_id)
3752f664823SMichael Walle {
3762f664823SMichael Walle 	return (phydev->phy_id & phydev->drv->phy_id_mask)
3772f664823SMichael Walle 		== (phy_id & phydev->drv->phy_id_mask);
3782f664823SMichael Walle }
3792f664823SMichael Walle 
3802f664823SMichael Walle static int at803x_parse_dt(struct phy_device *phydev)
3812f664823SMichael Walle {
3822f664823SMichael Walle 	struct device_node *node = phydev->mdio.dev.of_node;
3832f664823SMichael Walle 	struct at803x_priv *priv = phydev->priv;
3842f664823SMichael Walle 	unsigned int sel, mask;
3852f664823SMichael Walle 	u32 freq, strength;
3862f664823SMichael Walle 	int ret;
3872f664823SMichael Walle 
3882f664823SMichael Walle 	if (!IS_ENABLED(CONFIG_OF_MDIO))
3892f664823SMichael Walle 		return 0;
3902f664823SMichael Walle 
3912f664823SMichael Walle 	ret = of_property_read_u32(node, "qca,clk-out-frequency", &freq);
3922f664823SMichael Walle 	if (!ret) {
3932f664823SMichael Walle 		mask = AT803X_CLK_OUT_MASK;
3942f664823SMichael Walle 		switch (freq) {
3952f664823SMichael Walle 		case 25000000:
3962f664823SMichael Walle 			sel = AT803X_CLK_OUT_25MHZ_XTAL;
3972f664823SMichael Walle 			break;
3982f664823SMichael Walle 		case 50000000:
3992f664823SMichael Walle 			sel = AT803X_CLK_OUT_50MHZ_PLL;
4002f664823SMichael Walle 			break;
4012f664823SMichael Walle 		case 62500000:
4022f664823SMichael Walle 			sel = AT803X_CLK_OUT_62_5MHZ_PLL;
4032f664823SMichael Walle 			break;
4042f664823SMichael Walle 		case 125000000:
4052f664823SMichael Walle 			sel = AT803X_CLK_OUT_125MHZ_PLL;
4062f664823SMichael Walle 			break;
4072f664823SMichael Walle 		default:
4082f664823SMichael Walle 			phydev_err(phydev, "invalid qca,clk-out-frequency\n");
4092f664823SMichael Walle 			return -EINVAL;
4102f664823SMichael Walle 		}
4112f664823SMichael Walle 
4122f664823SMichael Walle 		priv->clk_25m_reg |= FIELD_PREP(mask, sel);
4132f664823SMichael Walle 		priv->clk_25m_mask |= mask;
4142f664823SMichael Walle 
4152f664823SMichael Walle 		/* Fixup for the AR8030/AR8035. This chip has another mask and
4162f664823SMichael Walle 		 * doesn't support the DSP reference. Eg. the lowest bit of the
4172f664823SMichael Walle 		 * mask. The upper two bits select the same frequencies. Mask
4182f664823SMichael Walle 		 * the lowest bit here.
4192f664823SMichael Walle 		 *
4202f664823SMichael Walle 		 * Warning:
4212f664823SMichael Walle 		 *   There was no datasheet for the AR8030 available so this is
4222f664823SMichael Walle 		 *   just a guess. But the AR8035 is listed as pin compatible
4232f664823SMichael Walle 		 *   to the AR8030 so there might be a good chance it works on
4242f664823SMichael Walle 		 *   the AR8030 too.
4252f664823SMichael Walle 		 */
4262f664823SMichael Walle 		if (at803x_match_phy_id(phydev, ATH8030_PHY_ID) ||
4272f664823SMichael Walle 		    at803x_match_phy_id(phydev, ATH8035_PHY_ID)) {
4282f664823SMichael Walle 			priv->clk_25m_reg &= ~AT8035_CLK_OUT_MASK;
4292f664823SMichael Walle 			priv->clk_25m_mask &= ~AT8035_CLK_OUT_MASK;
4302f664823SMichael Walle 		}
4312f664823SMichael Walle 	}
4322f664823SMichael Walle 
4332f664823SMichael Walle 	ret = of_property_read_u32(node, "qca,clk-out-strength", &strength);
4342f664823SMichael Walle 	if (!ret) {
4352f664823SMichael Walle 		priv->clk_25m_mask |= AT803X_CLK_OUT_STRENGTH_MASK;
4362f664823SMichael Walle 		switch (strength) {
4372f664823SMichael Walle 		case AR803X_STRENGTH_FULL:
4382f664823SMichael Walle 			priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_FULL;
4392f664823SMichael Walle 			break;
4402f664823SMichael Walle 		case AR803X_STRENGTH_HALF:
4412f664823SMichael Walle 			priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_HALF;
4422f664823SMichael Walle 			break;
4432f664823SMichael Walle 		case AR803X_STRENGTH_QUARTER:
4442f664823SMichael Walle 			priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_QUARTER;
4452f664823SMichael Walle 			break;
4462f664823SMichael Walle 		default:
4472f664823SMichael Walle 			phydev_err(phydev, "invalid qca,clk-out-strength\n");
4482f664823SMichael Walle 			return -EINVAL;
4492f664823SMichael Walle 		}
4502f664823SMichael Walle 	}
4512f664823SMichael Walle 
452428061f7SMichael Walle 	/* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping
453428061f7SMichael Walle 	 * options.
454428061f7SMichael Walle 	 */
4552f664823SMichael Walle 	if (at803x_match_phy_id(phydev, ATH8031_PHY_ID)) {
4562f664823SMichael Walle 		if (of_property_read_bool(node, "qca,keep-pll-enabled"))
4572f664823SMichael Walle 			priv->flags |= AT803X_KEEP_PLL_ENABLED;
4582f664823SMichael Walle 
4592f664823SMichael Walle 		ret = at8031_register_regulators(phydev);
4602f664823SMichael Walle 		if (ret < 0)
4612f664823SMichael Walle 			return ret;
4622f664823SMichael Walle 
4632f664823SMichael Walle 		priv->vddio = devm_regulator_get_optional(&phydev->mdio.dev,
4642f664823SMichael Walle 							  "vddio");
4652f664823SMichael Walle 		if (IS_ERR(priv->vddio)) {
4662f664823SMichael Walle 			phydev_err(phydev, "failed to get VDDIO regulator\n");
4672f664823SMichael Walle 			return PTR_ERR(priv->vddio);
4682f664823SMichael Walle 		}
4692f664823SMichael Walle 
4702f664823SMichael Walle 		ret = regulator_enable(priv->vddio);
4712f664823SMichael Walle 		if (ret < 0)
4722f664823SMichael Walle 			return ret;
4732f664823SMichael Walle 	}
4742f664823SMichael Walle 
4752f664823SMichael Walle 	return 0;
4762f664823SMichael Walle }
4772f664823SMichael Walle 
4782f664823SMichael Walle static int at803x_probe(struct phy_device *phydev)
4792f664823SMichael Walle {
4802f664823SMichael Walle 	struct device *dev = &phydev->mdio.dev;
4812f664823SMichael Walle 	struct at803x_priv *priv;
4822f664823SMichael Walle 
4832f664823SMichael Walle 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
4842f664823SMichael Walle 	if (!priv)
4852f664823SMichael Walle 		return -ENOMEM;
4862f664823SMichael Walle 
4872f664823SMichael Walle 	phydev->priv = priv;
4882f664823SMichael Walle 
4892f664823SMichael Walle 	return at803x_parse_dt(phydev);
4902f664823SMichael Walle }
4912f664823SMichael Walle 
4922f664823SMichael Walle static int at803x_clk_out_config(struct phy_device *phydev)
4932f664823SMichael Walle {
4942f664823SMichael Walle 	struct at803x_priv *priv = phydev->priv;
4952f664823SMichael Walle 	int val;
4962f664823SMichael Walle 
4972f664823SMichael Walle 	if (!priv->clk_25m_mask)
4982f664823SMichael Walle 		return 0;
4992f664823SMichael Walle 
5002f664823SMichael Walle 	val = phy_read_mmd(phydev, MDIO_MMD_AN, AT803X_MMD7_CLK25M);
5012f664823SMichael Walle 	if (val < 0)
5022f664823SMichael Walle 		return val;
5032f664823SMichael Walle 
5042f664823SMichael Walle 	val &= ~priv->clk_25m_mask;
5052f664823SMichael Walle 	val |= priv->clk_25m_reg;
5062f664823SMichael Walle 
5072f664823SMichael Walle 	return phy_write_mmd(phydev, MDIO_MMD_AN, AT803X_MMD7_CLK25M, val);
5082f664823SMichael Walle }
5092f664823SMichael Walle 
5102f664823SMichael Walle static int at8031_pll_config(struct phy_device *phydev)
5112f664823SMichael Walle {
5122f664823SMichael Walle 	struct at803x_priv *priv = phydev->priv;
5132f664823SMichael Walle 
5142f664823SMichael Walle 	/* The default after hardware reset is PLL OFF. After a soft reset, the
5152f664823SMichael Walle 	 * values are retained.
5162f664823SMichael Walle 	 */
5172f664823SMichael Walle 	if (priv->flags & AT803X_KEEP_PLL_ENABLED)
5182f664823SMichael Walle 		return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
5192f664823SMichael Walle 					     0, AT803X_DEBUG_PLL_ON);
5202f664823SMichael Walle 	else
5212f664823SMichael Walle 		return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
5222f664823SMichael Walle 					     AT803X_DEBUG_PLL_ON, 0);
5232f664823SMichael Walle }
5242f664823SMichael Walle 
5250ca7111aSMatus Ujhelyi static int at803x_config_init(struct phy_device *phydev)
5260ca7111aSMatus Ujhelyi {
5271ca6d1b1SMugunthan V N 	int ret;
5280ca7111aSMatus Ujhelyi 
5296d4cd041SVinod Koul 	/* The RX and TX delay default is:
5306d4cd041SVinod Koul 	 *   after HW reset: RX delay enabled and TX delay disabled
5316d4cd041SVinod Koul 	 *   after SW reset: RX delay enabled, while TX delay retains the
5326d4cd041SVinod Koul 	 *   value before reset.
5336d4cd041SVinod Koul 	 */
534bb0ce4c1SAndré Draszik 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
535bb0ce4c1SAndré Draszik 	    phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
536bb0ce4c1SAndré Draszik 		ret = at803x_enable_rx_delay(phydev);
537bb0ce4c1SAndré Draszik 	else
538cd28d1d6SVinod Koul 		ret = at803x_disable_rx_delay(phydev);
5392e5f9f28SMartin Blumenstingl 	if (ret < 0)
5401ca6d1b1SMugunthan V N 		return ret;
5416d4cd041SVinod Koul 
5426d4cd041SVinod Koul 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
543bb0ce4c1SAndré Draszik 	    phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
5446d4cd041SVinod Koul 		ret = at803x_enable_tx_delay(phydev);
545bb0ce4c1SAndré Draszik 	else
546bb0ce4c1SAndré Draszik 		ret = at803x_disable_tx_delay(phydev);
5472f664823SMichael Walle 	if (ret < 0)
5486d4cd041SVinod Koul 		return ret;
5492f664823SMichael Walle 
5502f664823SMichael Walle 	ret = at803x_clk_out_config(phydev);
5512f664823SMichael Walle 	if (ret < 0)
5522f664823SMichael Walle 		return ret;
5532f664823SMichael Walle 
5542f664823SMichael Walle 	if (at803x_match_phy_id(phydev, ATH8031_PHY_ID)) {
5552f664823SMichael Walle 		ret = at8031_pll_config(phydev);
5562f664823SMichael Walle 		if (ret < 0)
5572f664823SMichael Walle 			return ret;
5582f664823SMichael Walle 	}
5592f664823SMichael Walle 
5602f664823SMichael Walle 	return 0;
5610ca7111aSMatus Ujhelyi }
5620ca7111aSMatus Ujhelyi 
56377a99394SZhao Qiang static int at803x_ack_interrupt(struct phy_device *phydev)
56477a99394SZhao Qiang {
56577a99394SZhao Qiang 	int err;
56677a99394SZhao Qiang 
567a46bd63bSMartin Blumenstingl 	err = phy_read(phydev, AT803X_INTR_STATUS);
56877a99394SZhao Qiang 
56977a99394SZhao Qiang 	return (err < 0) ? err : 0;
57077a99394SZhao Qiang }
57177a99394SZhao Qiang 
57277a99394SZhao Qiang static int at803x_config_intr(struct phy_device *phydev)
57377a99394SZhao Qiang {
57477a99394SZhao Qiang 	int err;
57577a99394SZhao Qiang 	int value;
57677a99394SZhao Qiang 
577a46bd63bSMartin Blumenstingl 	value = phy_read(phydev, AT803X_INTR_ENABLE);
57877a99394SZhao Qiang 
579e6e4a556SMartin Blumenstingl 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
580e6e4a556SMartin Blumenstingl 		value |= AT803X_INTR_ENABLE_AUTONEG_ERR;
581e6e4a556SMartin Blumenstingl 		value |= AT803X_INTR_ENABLE_SPEED_CHANGED;
582e6e4a556SMartin Blumenstingl 		value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED;
583e6e4a556SMartin Blumenstingl 		value |= AT803X_INTR_ENABLE_LINK_FAIL;
584e6e4a556SMartin Blumenstingl 		value |= AT803X_INTR_ENABLE_LINK_SUCCESS;
585e6e4a556SMartin Blumenstingl 
586e6e4a556SMartin Blumenstingl 		err = phy_write(phydev, AT803X_INTR_ENABLE, value);
587e6e4a556SMartin Blumenstingl 	}
58877a99394SZhao Qiang 	else
589a46bd63bSMartin Blumenstingl 		err = phy_write(phydev, AT803X_INTR_ENABLE, 0);
59077a99394SZhao Qiang 
59177a99394SZhao Qiang 	return err;
59277a99394SZhao Qiang }
59377a99394SZhao Qiang 
59413a56b44SDaniel Mack static void at803x_link_change_notify(struct phy_device *phydev)
59513a56b44SDaniel Mack {
59613a56b44SDaniel Mack 	/*
59713a56b44SDaniel Mack 	 * Conduct a hardware reset for AT8030 every time a link loss is
59813a56b44SDaniel Mack 	 * signalled. This is necessary to circumvent a hardware bug that
59913a56b44SDaniel Mack 	 * occurs when the cable is unplugged while TX packets are pending
60013a56b44SDaniel Mack 	 * in the FIFO. In such cases, the FIFO enters an error mode it
60113a56b44SDaniel Mack 	 * cannot recover from by software.
60213a56b44SDaniel Mack 	 */
6036110ed2dSDavid Bauer 	if (phydev->state == PHY_NOLINK && phydev->mdio.reset_gpio) {
60413a56b44SDaniel Mack 		struct at803x_context context;
60513a56b44SDaniel Mack 
60613a56b44SDaniel Mack 		at803x_context_save(phydev, &context);
60713a56b44SDaniel Mack 
608bafbdd52SSergei Shtylyov 		phy_device_reset(phydev, 1);
60913a56b44SDaniel Mack 		msleep(1);
610bafbdd52SSergei Shtylyov 		phy_device_reset(phydev, 0);
611d57019d1SSergei Shtylyov 		msleep(1);
61213a56b44SDaniel Mack 
61313a56b44SDaniel Mack 		at803x_context_restore(phydev, &context);
61413a56b44SDaniel Mack 
6155c5f626bSHeiner Kallweit 		phydev_dbg(phydev, "%s(): phy was reset\n", __func__);
61613a56b44SDaniel Mack 	}
61713a56b44SDaniel Mack }
61813a56b44SDaniel Mack 
619f62265b5SZefir Kurtisi static int at803x_aneg_done(struct phy_device *phydev)
620f62265b5SZefir Kurtisi {
621f62265b5SZefir Kurtisi 	int ccr;
622f62265b5SZefir Kurtisi 
623f62265b5SZefir Kurtisi 	int aneg_done = genphy_aneg_done(phydev);
624f62265b5SZefir Kurtisi 	if (aneg_done != BMSR_ANEGCOMPLETE)
625f62265b5SZefir Kurtisi 		return aneg_done;
626f62265b5SZefir Kurtisi 
627f62265b5SZefir Kurtisi 	/*
628f62265b5SZefir Kurtisi 	 * in SGMII mode, if copper side autoneg is successful,
629f62265b5SZefir Kurtisi 	 * also check SGMII side autoneg result
630f62265b5SZefir Kurtisi 	 */
631f62265b5SZefir Kurtisi 	ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG);
632f62265b5SZefir Kurtisi 	if ((ccr & AT803X_MODE_CFG_MASK) != AT803X_MODE_CFG_SGMII)
633f62265b5SZefir Kurtisi 		return aneg_done;
634f62265b5SZefir Kurtisi 
635f62265b5SZefir Kurtisi 	/* switch to SGMII/fiber page */
636f62265b5SZefir Kurtisi 	phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr & ~AT803X_BT_BX_REG_SEL);
637f62265b5SZefir Kurtisi 
638f62265b5SZefir Kurtisi 	/* check if the SGMII link is OK. */
639f62265b5SZefir Kurtisi 	if (!(phy_read(phydev, AT803X_PSSR) & AT803X_PSSR_MR_AN_COMPLETE)) {
640ab2a605fSAndrew Lunn 		phydev_warn(phydev, "803x_aneg_done: SGMII link is not ok\n");
641f62265b5SZefir Kurtisi 		aneg_done = 0;
642f62265b5SZefir Kurtisi 	}
643f62265b5SZefir Kurtisi 	/* switch back to copper page */
644f62265b5SZefir Kurtisi 	phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr | AT803X_BT_BX_REG_SEL);
645f62265b5SZefir Kurtisi 
646f62265b5SZefir Kurtisi 	return aneg_done;
647f62265b5SZefir Kurtisi }
648f62265b5SZefir Kurtisi 
64906d5f344SRussell King static int at803x_read_status(struct phy_device *phydev)
65006d5f344SRussell King {
65106d5f344SRussell King 	int ss, err, old_link = phydev->link;
65206d5f344SRussell King 
65306d5f344SRussell King 	/* Update the link, but return if there was an error */
65406d5f344SRussell King 	err = genphy_update_link(phydev);
65506d5f344SRussell King 	if (err)
65606d5f344SRussell King 		return err;
65706d5f344SRussell King 
65806d5f344SRussell King 	/* why bother the PHY if nothing can have changed */
65906d5f344SRussell King 	if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)
66006d5f344SRussell King 		return 0;
66106d5f344SRussell King 
66206d5f344SRussell King 	phydev->speed = SPEED_UNKNOWN;
66306d5f344SRussell King 	phydev->duplex = DUPLEX_UNKNOWN;
66406d5f344SRussell King 	phydev->pause = 0;
66506d5f344SRussell King 	phydev->asym_pause = 0;
66606d5f344SRussell King 
66706d5f344SRussell King 	err = genphy_read_lpa(phydev);
66806d5f344SRussell King 	if (err < 0)
66906d5f344SRussell King 		return err;
67006d5f344SRussell King 
67106d5f344SRussell King 	/* Read the AT8035 PHY-Specific Status register, which indicates the
67206d5f344SRussell King 	 * speed and duplex that the PHY is actually using, irrespective of
67306d5f344SRussell King 	 * whether we are in autoneg mode or not.
67406d5f344SRussell King 	 */
67506d5f344SRussell King 	ss = phy_read(phydev, AT803X_SPECIFIC_STATUS);
67606d5f344SRussell King 	if (ss < 0)
67706d5f344SRussell King 		return ss;
67806d5f344SRussell King 
67906d5f344SRussell King 	if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) {
68006d5f344SRussell King 		switch (ss & AT803X_SS_SPEED_MASK) {
68106d5f344SRussell King 		case AT803X_SS_SPEED_10:
68206d5f344SRussell King 			phydev->speed = SPEED_10;
68306d5f344SRussell King 			break;
68406d5f344SRussell King 		case AT803X_SS_SPEED_100:
68506d5f344SRussell King 			phydev->speed = SPEED_100;
68606d5f344SRussell King 			break;
68706d5f344SRussell King 		case AT803X_SS_SPEED_1000:
68806d5f344SRussell King 			phydev->speed = SPEED_1000;
68906d5f344SRussell King 			break;
69006d5f344SRussell King 		}
69106d5f344SRussell King 		if (ss & AT803X_SS_DUPLEX)
69206d5f344SRussell King 			phydev->duplex = DUPLEX_FULL;
69306d5f344SRussell King 		else
69406d5f344SRussell King 			phydev->duplex = DUPLEX_HALF;
69506d5f344SRussell King 		if (ss & AT803X_SS_MDIX)
69606d5f344SRussell King 			phydev->mdix = ETH_TP_MDI_X;
69706d5f344SRussell King 		else
69806d5f344SRussell King 			phydev->mdix = ETH_TP_MDI;
69906d5f344SRussell King 	}
70006d5f344SRussell King 
70106d5f344SRussell King 	if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete)
70206d5f344SRussell King 		phy_resolve_aneg_pause(phydev);
70306d5f344SRussell King 
70406d5f344SRussell King 	return 0;
70506d5f344SRussell King }
70606d5f344SRussell King 
707317420abSMugunthan V N static struct phy_driver at803x_driver[] = {
708317420abSMugunthan V N {
7090ca7111aSMatus Ujhelyi 	/* ATHEROS 8035 */
710bd8ca17fSDaniel Mack 	.phy_id			= ATH8035_PHY_ID,
7110ca7111aSMatus Ujhelyi 	.name			= "Atheros 8035 ethernet",
71258effd71SFabio Estevam 	.phy_id_mask		= AT803X_PHY_ID_MASK,
7132f664823SMichael Walle 	.probe			= at803x_probe,
7140ca7111aSMatus Ujhelyi 	.config_init		= at803x_config_init,
715ea13c9eeSMugunthan V N 	.set_wol		= at803x_set_wol,
716ea13c9eeSMugunthan V N 	.get_wol		= at803x_get_wol,
7176229ed1fSDaniel Mack 	.suspend		= at803x_suspend,
7186229ed1fSDaniel Mack 	.resume			= at803x_resume,
719dcdecdcfSHeiner Kallweit 	/* PHY_GBIT_FEATURES */
72006d5f344SRussell King 	.read_status		= at803x_read_status,
7210eae5982SMåns Rullgård 	.ack_interrupt		= at803x_ack_interrupt,
7220eae5982SMåns Rullgård 	.config_intr		= at803x_config_intr,
723317420abSMugunthan V N }, {
7240ca7111aSMatus Ujhelyi 	/* ATHEROS 8030 */
725bd8ca17fSDaniel Mack 	.phy_id			= ATH8030_PHY_ID,
7260ca7111aSMatus Ujhelyi 	.name			= "Atheros 8030 ethernet",
72758effd71SFabio Estevam 	.phy_id_mask		= AT803X_PHY_ID_MASK,
7282f664823SMichael Walle 	.probe			= at803x_probe,
7290ca7111aSMatus Ujhelyi 	.config_init		= at803x_config_init,
73013a56b44SDaniel Mack 	.link_change_notify	= at803x_link_change_notify,
731ea13c9eeSMugunthan V N 	.set_wol		= at803x_set_wol,
732ea13c9eeSMugunthan V N 	.get_wol		= at803x_get_wol,
7336229ed1fSDaniel Mack 	.suspend		= at803x_suspend,
7346229ed1fSDaniel Mack 	.resume			= at803x_resume,
735dcdecdcfSHeiner Kallweit 	/* PHY_BASIC_FEATURES */
7360eae5982SMåns Rullgård 	.ack_interrupt		= at803x_ack_interrupt,
7370eae5982SMåns Rullgård 	.config_intr		= at803x_config_intr,
73805d7cce8SMugunthan V N }, {
739428061f7SMichael Walle 	/* ATHEROS 8031/8033 */
740bd8ca17fSDaniel Mack 	.phy_id			= ATH8031_PHY_ID,
741428061f7SMichael Walle 	.name			= "Atheros 8031/8033 ethernet",
74258effd71SFabio Estevam 	.phy_id_mask		= AT803X_PHY_ID_MASK,
7432f664823SMichael Walle 	.probe			= at803x_probe,
74405d7cce8SMugunthan V N 	.config_init		= at803x_config_init,
74505d7cce8SMugunthan V N 	.set_wol		= at803x_set_wol,
74605d7cce8SMugunthan V N 	.get_wol		= at803x_get_wol,
7476229ed1fSDaniel Mack 	.suspend		= at803x_suspend,
7486229ed1fSDaniel Mack 	.resume			= at803x_resume,
749dcdecdcfSHeiner Kallweit 	/* PHY_GBIT_FEATURES */
75006d5f344SRussell King 	.read_status		= at803x_read_status,
751f62265b5SZefir Kurtisi 	.aneg_done		= at803x_aneg_done,
75277a99394SZhao Qiang 	.ack_interrupt		= &at803x_ack_interrupt,
75377a99394SZhao Qiang 	.config_intr		= &at803x_config_intr,
7547908d2ceSOleksij Rempel }, {
7557908d2ceSOleksij Rempel 	/* ATHEROS AR9331 */
7567908d2ceSOleksij Rempel 	PHY_ID_MATCH_EXACT(ATH9331_PHY_ID),
7577908d2ceSOleksij Rempel 	.name			= "Atheros AR9331 built-in PHY",
7582f664823SMichael Walle 	.probe			= at803x_probe,
7597908d2ceSOleksij Rempel 	.config_init		= at803x_config_init,
7607908d2ceSOleksij Rempel 	.suspend		= at803x_suspend,
7617908d2ceSOleksij Rempel 	.resume			= at803x_resume,
7627908d2ceSOleksij Rempel 	/* PHY_BASIC_FEATURES */
7637908d2ceSOleksij Rempel 	.ack_interrupt		= &at803x_ack_interrupt,
7647908d2ceSOleksij Rempel 	.config_intr		= &at803x_config_intr,
765317420abSMugunthan V N } };
7660ca7111aSMatus Ujhelyi 
76750fd7150SJohan Hovold module_phy_driver(at803x_driver);
7680ca7111aSMatus Ujhelyi 
7690ca7111aSMatus Ujhelyi static struct mdio_device_id __maybe_unused atheros_tbl[] = {
77058effd71SFabio Estevam 	{ ATH8030_PHY_ID, AT803X_PHY_ID_MASK },
77158effd71SFabio Estevam 	{ ATH8031_PHY_ID, AT803X_PHY_ID_MASK },
77258effd71SFabio Estevam 	{ ATH8035_PHY_ID, AT803X_PHY_ID_MASK },
7737908d2ceSOleksij Rempel 	{ PHY_ID_MATCH_EXACT(ATH9331_PHY_ID) },
7740ca7111aSMatus Ujhelyi 	{ }
7750ca7111aSMatus Ujhelyi };
7760ca7111aSMatus Ujhelyi 
7770ca7111aSMatus Ujhelyi MODULE_DEVICE_TABLE(mdio, atheros_tbl);
778