xref: /openbmc/linux/drivers/net/phy/at803x.c (revision f62265b5)
10ca7111aSMatus Ujhelyi /*
20ca7111aSMatus Ujhelyi  * drivers/net/phy/at803x.c
30ca7111aSMatus Ujhelyi  *
40ca7111aSMatus Ujhelyi  * Driver for Atheros 803x PHY
50ca7111aSMatus Ujhelyi  *
60ca7111aSMatus Ujhelyi  * Author: Matus Ujhelyi <ujhelyi.m@gmail.com>
70ca7111aSMatus Ujhelyi  *
80ca7111aSMatus Ujhelyi  * This program is free software; you can redistribute  it and/or modify it
90ca7111aSMatus Ujhelyi  * under  the terms of  the GNU General  Public License as published by the
100ca7111aSMatus Ujhelyi  * Free Software Foundation;  either version 2 of the  License, or (at your
110ca7111aSMatus Ujhelyi  * option) any later version.
120ca7111aSMatus Ujhelyi  */
130ca7111aSMatus Ujhelyi 
140ca7111aSMatus Ujhelyi #include <linux/phy.h>
150ca7111aSMatus Ujhelyi #include <linux/module.h>
160ca7111aSMatus Ujhelyi #include <linux/string.h>
170ca7111aSMatus Ujhelyi #include <linux/netdevice.h>
180ca7111aSMatus Ujhelyi #include <linux/etherdevice.h>
1913a56b44SDaniel Mack #include <linux/of_gpio.h>
2013a56b44SDaniel Mack #include <linux/gpio/consumer.h>
210ca7111aSMatus Ujhelyi 
220ca7111aSMatus Ujhelyi #define AT803X_INTR_ENABLE			0x12
23e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_AUTONEG_ERR		BIT(15)
24e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_SPEED_CHANGED	BIT(14)
25e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_DUPLEX_CHANGED	BIT(13)
26e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_PAGE_RECEIVED	BIT(12)
27e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_LINK_FAIL		BIT(11)
28e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_LINK_SUCCESS		BIT(10)
29e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_WIRESPEED_DOWNGRADE	BIT(5)
30e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_POLARITY_CHANGED	BIT(1)
31e6e4a556SMartin Blumenstingl #define AT803X_INTR_ENABLE_WOL			BIT(0)
32e6e4a556SMartin Blumenstingl 
330ca7111aSMatus Ujhelyi #define AT803X_INTR_STATUS			0x13
34a46bd63bSMartin Blumenstingl 
3513a56b44SDaniel Mack #define AT803X_SMART_SPEED			0x14
3613a56b44SDaniel Mack #define AT803X_LED_CONTROL			0x18
37a46bd63bSMartin Blumenstingl 
380ca7111aSMatus Ujhelyi #define AT803X_DEVICE_ADDR			0x03
390ca7111aSMatus Ujhelyi #define AT803X_LOC_MAC_ADDR_0_15_OFFSET		0x804C
400ca7111aSMatus Ujhelyi #define AT803X_LOC_MAC_ADDR_16_31_OFFSET	0x804B
410ca7111aSMatus Ujhelyi #define AT803X_LOC_MAC_ADDR_32_47_OFFSET	0x804A
420ca7111aSMatus Ujhelyi #define AT803X_MMD_ACCESS_CONTROL		0x0D
430ca7111aSMatus Ujhelyi #define AT803X_MMD_ACCESS_CONTROL_DATA		0x0E
440ca7111aSMatus Ujhelyi #define AT803X_FUNC_DATA			0x4003
45f62265b5SZefir Kurtisi #define AT803X_REG_CHIP_CONFIG			0x1f
46f62265b5SZefir Kurtisi #define AT803X_BT_BX_REG_SEL			0x8000
47a46bd63bSMartin Blumenstingl 
481ca6d1b1SMugunthan V N #define AT803X_DEBUG_ADDR			0x1D
491ca6d1b1SMugunthan V N #define AT803X_DEBUG_DATA			0x1E
50a46bd63bSMartin Blumenstingl 
51f62265b5SZefir Kurtisi #define AT803X_MODE_CFG_MASK			0x0F
52f62265b5SZefir Kurtisi #define AT803X_MODE_CFG_SGMII			0x01
53f62265b5SZefir Kurtisi 
54f62265b5SZefir Kurtisi #define AT803X_PSSR			0x11	/*PHY-Specific Status Register*/
55f62265b5SZefir Kurtisi #define AT803X_PSSR_MR_AN_COMPLETE	0x0200
56f62265b5SZefir Kurtisi 
572e5f9f28SMartin Blumenstingl #define AT803X_DEBUG_REG_0			0x00
582e5f9f28SMartin Blumenstingl #define AT803X_DEBUG_RX_CLK_DLY_EN		BIT(15)
59a46bd63bSMartin Blumenstingl 
602e5f9f28SMartin Blumenstingl #define AT803X_DEBUG_REG_5			0x05
612e5f9f28SMartin Blumenstingl #define AT803X_DEBUG_TX_CLK_DLY_EN		BIT(8)
620ca7111aSMatus Ujhelyi 
63bd8ca17fSDaniel Mack #define ATH8030_PHY_ID 0x004dd076
64bd8ca17fSDaniel Mack #define ATH8031_PHY_ID 0x004dd074
65bd8ca17fSDaniel Mack #define ATH8035_PHY_ID 0x004dd072
66bd8ca17fSDaniel Mack 
670ca7111aSMatus Ujhelyi MODULE_DESCRIPTION("Atheros 803x PHY driver");
680ca7111aSMatus Ujhelyi MODULE_AUTHOR("Matus Ujhelyi");
690ca7111aSMatus Ujhelyi MODULE_LICENSE("GPL");
700ca7111aSMatus Ujhelyi 
7113a56b44SDaniel Mack struct at803x_priv {
7213a56b44SDaniel Mack 	bool phy_reset:1;
7313a56b44SDaniel Mack 	struct gpio_desc *gpiod_reset;
7413a56b44SDaniel Mack };
7513a56b44SDaniel Mack 
7613a56b44SDaniel Mack struct at803x_context {
7713a56b44SDaniel Mack 	u16 bmcr;
7813a56b44SDaniel Mack 	u16 advertise;
7913a56b44SDaniel Mack 	u16 control1000;
8013a56b44SDaniel Mack 	u16 int_enable;
8113a56b44SDaniel Mack 	u16 smart_speed;
8213a56b44SDaniel Mack 	u16 led_control;
8313a56b44SDaniel Mack };
8413a56b44SDaniel Mack 
852e5f9f28SMartin Blumenstingl static int at803x_debug_reg_read(struct phy_device *phydev, u16 reg)
862e5f9f28SMartin Blumenstingl {
872e5f9f28SMartin Blumenstingl 	int ret;
882e5f9f28SMartin Blumenstingl 
892e5f9f28SMartin Blumenstingl 	ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg);
902e5f9f28SMartin Blumenstingl 	if (ret < 0)
912e5f9f28SMartin Blumenstingl 		return ret;
922e5f9f28SMartin Blumenstingl 
932e5f9f28SMartin Blumenstingl 	return phy_read(phydev, AT803X_DEBUG_DATA);
942e5f9f28SMartin Blumenstingl }
952e5f9f28SMartin Blumenstingl 
962e5f9f28SMartin Blumenstingl static int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg,
972e5f9f28SMartin Blumenstingl 				 u16 clear, u16 set)
982e5f9f28SMartin Blumenstingl {
992e5f9f28SMartin Blumenstingl 	u16 val;
1002e5f9f28SMartin Blumenstingl 	int ret;
1012e5f9f28SMartin Blumenstingl 
1022e5f9f28SMartin Blumenstingl 	ret = at803x_debug_reg_read(phydev, reg);
1032e5f9f28SMartin Blumenstingl 	if (ret < 0)
1042e5f9f28SMartin Blumenstingl 		return ret;
1052e5f9f28SMartin Blumenstingl 
1062e5f9f28SMartin Blumenstingl 	val = ret & 0xffff;
1072e5f9f28SMartin Blumenstingl 	val &= ~clear;
1082e5f9f28SMartin Blumenstingl 	val |= set;
1092e5f9f28SMartin Blumenstingl 
1102e5f9f28SMartin Blumenstingl 	return phy_write(phydev, AT803X_DEBUG_DATA, val);
1112e5f9f28SMartin Blumenstingl }
1122e5f9f28SMartin Blumenstingl 
1132e5f9f28SMartin Blumenstingl static inline int at803x_enable_rx_delay(struct phy_device *phydev)
1142e5f9f28SMartin Blumenstingl {
1152e5f9f28SMartin Blumenstingl 	return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0, 0,
1162e5f9f28SMartin Blumenstingl 					AT803X_DEBUG_RX_CLK_DLY_EN);
1172e5f9f28SMartin Blumenstingl }
1182e5f9f28SMartin Blumenstingl 
1192e5f9f28SMartin Blumenstingl static inline int at803x_enable_tx_delay(struct phy_device *phydev)
1202e5f9f28SMartin Blumenstingl {
1212e5f9f28SMartin Blumenstingl 	return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_5, 0,
1222e5f9f28SMartin Blumenstingl 					AT803X_DEBUG_TX_CLK_DLY_EN);
1232e5f9f28SMartin Blumenstingl }
1242e5f9f28SMartin Blumenstingl 
12513a56b44SDaniel Mack /* save relevant PHY registers to private copy */
12613a56b44SDaniel Mack static void at803x_context_save(struct phy_device *phydev,
12713a56b44SDaniel Mack 				struct at803x_context *context)
12813a56b44SDaniel Mack {
12913a56b44SDaniel Mack 	context->bmcr = phy_read(phydev, MII_BMCR);
13013a56b44SDaniel Mack 	context->advertise = phy_read(phydev, MII_ADVERTISE);
13113a56b44SDaniel Mack 	context->control1000 = phy_read(phydev, MII_CTRL1000);
13213a56b44SDaniel Mack 	context->int_enable = phy_read(phydev, AT803X_INTR_ENABLE);
13313a56b44SDaniel Mack 	context->smart_speed = phy_read(phydev, AT803X_SMART_SPEED);
13413a56b44SDaniel Mack 	context->led_control = phy_read(phydev, AT803X_LED_CONTROL);
13513a56b44SDaniel Mack }
13613a56b44SDaniel Mack 
13713a56b44SDaniel Mack /* restore relevant PHY registers from private copy */
13813a56b44SDaniel Mack static void at803x_context_restore(struct phy_device *phydev,
13913a56b44SDaniel Mack 				   const struct at803x_context *context)
14013a56b44SDaniel Mack {
14113a56b44SDaniel Mack 	phy_write(phydev, MII_BMCR, context->bmcr);
14213a56b44SDaniel Mack 	phy_write(phydev, MII_ADVERTISE, context->advertise);
14313a56b44SDaniel Mack 	phy_write(phydev, MII_CTRL1000, context->control1000);
14413a56b44SDaniel Mack 	phy_write(phydev, AT803X_INTR_ENABLE, context->int_enable);
14513a56b44SDaniel Mack 	phy_write(phydev, AT803X_SMART_SPEED, context->smart_speed);
14613a56b44SDaniel Mack 	phy_write(phydev, AT803X_LED_CONTROL, context->led_control);
14713a56b44SDaniel Mack }
14813a56b44SDaniel Mack 
149ea13c9eeSMugunthan V N static int at803x_set_wol(struct phy_device *phydev,
150ea13c9eeSMugunthan V N 			  struct ethtool_wolinfo *wol)
1510ca7111aSMatus Ujhelyi {
1520ca7111aSMatus Ujhelyi 	struct net_device *ndev = phydev->attached_dev;
1530ca7111aSMatus Ujhelyi 	const u8 *mac;
154ea13c9eeSMugunthan V N 	int ret;
155ea13c9eeSMugunthan V N 	u32 value;
1560ca7111aSMatus Ujhelyi 	unsigned int i, offsets[] = {
1570ca7111aSMatus Ujhelyi 		AT803X_LOC_MAC_ADDR_32_47_OFFSET,
1580ca7111aSMatus Ujhelyi 		AT803X_LOC_MAC_ADDR_16_31_OFFSET,
1590ca7111aSMatus Ujhelyi 		AT803X_LOC_MAC_ADDR_0_15_OFFSET,
1600ca7111aSMatus Ujhelyi 	};
1610ca7111aSMatus Ujhelyi 
1620ca7111aSMatus Ujhelyi 	if (!ndev)
163ea13c9eeSMugunthan V N 		return -ENODEV;
1640ca7111aSMatus Ujhelyi 
165ea13c9eeSMugunthan V N 	if (wol->wolopts & WAKE_MAGIC) {
1660ca7111aSMatus Ujhelyi 		mac = (const u8 *) ndev->dev_addr;
1670ca7111aSMatus Ujhelyi 
1680ca7111aSMatus Ujhelyi 		if (!is_valid_ether_addr(mac))
169ea13c9eeSMugunthan V N 			return -EFAULT;
1700ca7111aSMatus Ujhelyi 
1710ca7111aSMatus Ujhelyi 		for (i = 0; i < 3; i++) {
1720ca7111aSMatus Ujhelyi 			phy_write(phydev, AT803X_MMD_ACCESS_CONTROL,
1730ca7111aSMatus Ujhelyi 				  AT803X_DEVICE_ADDR);
1740ca7111aSMatus Ujhelyi 			phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA,
1750ca7111aSMatus Ujhelyi 				  offsets[i]);
1760ca7111aSMatus Ujhelyi 			phy_write(phydev, AT803X_MMD_ACCESS_CONTROL,
1770ca7111aSMatus Ujhelyi 				  AT803X_FUNC_DATA);
1780ca7111aSMatus Ujhelyi 			phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA,
1790ca7111aSMatus Ujhelyi 				  mac[(i * 2) + 1] | (mac[(i * 2)] << 8));
1800ca7111aSMatus Ujhelyi 		}
181ea13c9eeSMugunthan V N 
182ea13c9eeSMugunthan V N 		value = phy_read(phydev, AT803X_INTR_ENABLE);
183e6e4a556SMartin Blumenstingl 		value |= AT803X_INTR_ENABLE_WOL;
184ea13c9eeSMugunthan V N 		ret = phy_write(phydev, AT803X_INTR_ENABLE, value);
185ea13c9eeSMugunthan V N 		if (ret)
186ea13c9eeSMugunthan V N 			return ret;
187ea13c9eeSMugunthan V N 		value = phy_read(phydev, AT803X_INTR_STATUS);
188ea13c9eeSMugunthan V N 	} else {
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 	}
196ea13c9eeSMugunthan V N 
197ea13c9eeSMugunthan V N 	return ret;
198ea13c9eeSMugunthan V N }
199ea13c9eeSMugunthan V N 
200ea13c9eeSMugunthan V N static void at803x_get_wol(struct phy_device *phydev,
201ea13c9eeSMugunthan V N 			   struct ethtool_wolinfo *wol)
202ea13c9eeSMugunthan V N {
203ea13c9eeSMugunthan V N 	u32 value;
204ea13c9eeSMugunthan V N 
205ea13c9eeSMugunthan V N 	wol->supported = WAKE_MAGIC;
206ea13c9eeSMugunthan V N 	wol->wolopts = 0;
207ea13c9eeSMugunthan V N 
208ea13c9eeSMugunthan V N 	value = phy_read(phydev, AT803X_INTR_ENABLE);
209e6e4a556SMartin Blumenstingl 	if (value & AT803X_INTR_ENABLE_WOL)
210ea13c9eeSMugunthan V N 		wol->wolopts |= WAKE_MAGIC;
2110ca7111aSMatus Ujhelyi }
2120ca7111aSMatus Ujhelyi 
2136229ed1fSDaniel Mack static int at803x_suspend(struct phy_device *phydev)
2146229ed1fSDaniel Mack {
2156229ed1fSDaniel Mack 	int value;
2166229ed1fSDaniel Mack 	int wol_enabled;
2176229ed1fSDaniel Mack 
2186229ed1fSDaniel Mack 	mutex_lock(&phydev->lock);
2196229ed1fSDaniel Mack 
2206229ed1fSDaniel Mack 	value = phy_read(phydev, AT803X_INTR_ENABLE);
221e6e4a556SMartin Blumenstingl 	wol_enabled = value & AT803X_INTR_ENABLE_WOL;
2226229ed1fSDaniel Mack 
2236229ed1fSDaniel Mack 	value = phy_read(phydev, MII_BMCR);
2246229ed1fSDaniel Mack 
2256229ed1fSDaniel Mack 	if (wol_enabled)
2266229ed1fSDaniel Mack 		value |= BMCR_ISOLATE;
2276229ed1fSDaniel Mack 	else
2286229ed1fSDaniel Mack 		value |= BMCR_PDOWN;
2296229ed1fSDaniel Mack 
2306229ed1fSDaniel Mack 	phy_write(phydev, MII_BMCR, value);
2316229ed1fSDaniel Mack 
2326229ed1fSDaniel Mack 	mutex_unlock(&phydev->lock);
2336229ed1fSDaniel Mack 
2346229ed1fSDaniel Mack 	return 0;
2356229ed1fSDaniel Mack }
2366229ed1fSDaniel Mack 
2376229ed1fSDaniel Mack static int at803x_resume(struct phy_device *phydev)
2386229ed1fSDaniel Mack {
2396229ed1fSDaniel Mack 	int value;
2406229ed1fSDaniel Mack 
2416229ed1fSDaniel Mack 	mutex_lock(&phydev->lock);
2426229ed1fSDaniel Mack 
2436229ed1fSDaniel Mack 	value = phy_read(phydev, MII_BMCR);
2446229ed1fSDaniel Mack 	value &= ~(BMCR_PDOWN | BMCR_ISOLATE);
2456229ed1fSDaniel Mack 	phy_write(phydev, MII_BMCR, value);
2466229ed1fSDaniel Mack 
2476229ed1fSDaniel Mack 	mutex_unlock(&phydev->lock);
2486229ed1fSDaniel Mack 
2496229ed1fSDaniel Mack 	return 0;
2506229ed1fSDaniel Mack }
2516229ed1fSDaniel Mack 
25213a56b44SDaniel Mack static int at803x_probe(struct phy_device *phydev)
25313a56b44SDaniel Mack {
254e5a03bfdSAndrew Lunn 	struct device *dev = &phydev->mdio.dev;
25513a56b44SDaniel Mack 	struct at803x_priv *priv;
256687908c2SUwe Kleine-König 	struct gpio_desc *gpiod_reset;
25713a56b44SDaniel Mack 
2588f2877caSFengguang Wu 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
25913a56b44SDaniel Mack 	if (!priv)
26013a56b44SDaniel Mack 		return -ENOMEM;
26113a56b44SDaniel Mack 
2629eb13f65SSebastian Frias 	if (phydev->drv->phy_id != ATH8030_PHY_ID)
2639eb13f65SSebastian Frias 		goto does_not_require_reset_workaround;
2649eb13f65SSebastian Frias 
265d57019d1SSergei Shtylyov 	gpiod_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
266687908c2SUwe Kleine-König 	if (IS_ERR(gpiod_reset))
267687908c2SUwe Kleine-König 		return PTR_ERR(gpiod_reset);
268687908c2SUwe Kleine-König 
269687908c2SUwe Kleine-König 	priv->gpiod_reset = gpiod_reset;
27013a56b44SDaniel Mack 
2719eb13f65SSebastian Frias does_not_require_reset_workaround:
27213a56b44SDaniel Mack 	phydev->priv = priv;
27313a56b44SDaniel Mack 
27413a56b44SDaniel Mack 	return 0;
27513a56b44SDaniel Mack }
27613a56b44SDaniel Mack 
2770ca7111aSMatus Ujhelyi static int at803x_config_init(struct phy_device *phydev)
2780ca7111aSMatus Ujhelyi {
2791ca6d1b1SMugunthan V N 	int ret;
2800ca7111aSMatus Ujhelyi 
2816ff01dbbSDaniel Mack 	ret = genphy_config_init(phydev);
2826ff01dbbSDaniel Mack 	if (ret < 0)
2836ff01dbbSDaniel Mack 		return ret;
2840ca7111aSMatus Ujhelyi 
2852e5f9f28SMartin Blumenstingl 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID ||
2862e5f9f28SMartin Blumenstingl 			phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
2872e5f9f28SMartin Blumenstingl 		ret = at803x_enable_rx_delay(phydev);
2882e5f9f28SMartin Blumenstingl 		if (ret < 0)
2891ca6d1b1SMugunthan V N 			return ret;
2902e5f9f28SMartin Blumenstingl 	}
2912e5f9f28SMartin Blumenstingl 
2922e5f9f28SMartin Blumenstingl 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID ||
2932e5f9f28SMartin Blumenstingl 			phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
2942e5f9f28SMartin Blumenstingl 		ret = at803x_enable_tx_delay(phydev);
2952e5f9f28SMartin Blumenstingl 		if (ret < 0)
2961ca6d1b1SMugunthan V N 			return ret;
2971ca6d1b1SMugunthan V N 	}
2981ca6d1b1SMugunthan V N 
2990ca7111aSMatus Ujhelyi 	return 0;
3000ca7111aSMatus Ujhelyi }
3010ca7111aSMatus Ujhelyi 
30277a99394SZhao Qiang static int at803x_ack_interrupt(struct phy_device *phydev)
30377a99394SZhao Qiang {
30477a99394SZhao Qiang 	int err;
30577a99394SZhao Qiang 
306a46bd63bSMartin Blumenstingl 	err = phy_read(phydev, AT803X_INTR_STATUS);
30777a99394SZhao Qiang 
30877a99394SZhao Qiang 	return (err < 0) ? err : 0;
30977a99394SZhao Qiang }
31077a99394SZhao Qiang 
31177a99394SZhao Qiang static int at803x_config_intr(struct phy_device *phydev)
31277a99394SZhao Qiang {
31377a99394SZhao Qiang 	int err;
31477a99394SZhao Qiang 	int value;
31577a99394SZhao Qiang 
316a46bd63bSMartin Blumenstingl 	value = phy_read(phydev, AT803X_INTR_ENABLE);
31777a99394SZhao Qiang 
318e6e4a556SMartin Blumenstingl 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
319e6e4a556SMartin Blumenstingl 		value |= AT803X_INTR_ENABLE_AUTONEG_ERR;
320e6e4a556SMartin Blumenstingl 		value |= AT803X_INTR_ENABLE_SPEED_CHANGED;
321e6e4a556SMartin Blumenstingl 		value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED;
322e6e4a556SMartin Blumenstingl 		value |= AT803X_INTR_ENABLE_LINK_FAIL;
323e6e4a556SMartin Blumenstingl 		value |= AT803X_INTR_ENABLE_LINK_SUCCESS;
324e6e4a556SMartin Blumenstingl 
325e6e4a556SMartin Blumenstingl 		err = phy_write(phydev, AT803X_INTR_ENABLE, value);
326e6e4a556SMartin Blumenstingl 	}
32777a99394SZhao Qiang 	else
328a46bd63bSMartin Blumenstingl 		err = phy_write(phydev, AT803X_INTR_ENABLE, 0);
32977a99394SZhao Qiang 
33077a99394SZhao Qiang 	return err;
33177a99394SZhao Qiang }
33277a99394SZhao Qiang 
33313a56b44SDaniel Mack static void at803x_link_change_notify(struct phy_device *phydev)
33413a56b44SDaniel Mack {
33513a56b44SDaniel Mack 	struct at803x_priv *priv = phydev->priv;
33613a56b44SDaniel Mack 
33713a56b44SDaniel Mack 	/*
33813a56b44SDaniel Mack 	 * Conduct a hardware reset for AT8030 every time a link loss is
33913a56b44SDaniel Mack 	 * signalled. This is necessary to circumvent a hardware bug that
34013a56b44SDaniel Mack 	 * occurs when the cable is unplugged while TX packets are pending
34113a56b44SDaniel Mack 	 * in the FIFO. In such cases, the FIFO enters an error mode it
34213a56b44SDaniel Mack 	 * cannot recover from by software.
34313a56b44SDaniel Mack 	 */
34413a56b44SDaniel Mack 	if (phydev->state == PHY_NOLINK) {
34513a56b44SDaniel Mack 		if (priv->gpiod_reset && !priv->phy_reset) {
34613a56b44SDaniel Mack 			struct at803x_context context;
34713a56b44SDaniel Mack 
34813a56b44SDaniel Mack 			at803x_context_save(phydev, &context);
34913a56b44SDaniel Mack 
35013a56b44SDaniel Mack 			gpiod_set_value(priv->gpiod_reset, 1);
35113a56b44SDaniel Mack 			msleep(1);
352d57019d1SSergei Shtylyov 			gpiod_set_value(priv->gpiod_reset, 0);
353d57019d1SSergei Shtylyov 			msleep(1);
35413a56b44SDaniel Mack 
35513a56b44SDaniel Mack 			at803x_context_restore(phydev, &context);
35613a56b44SDaniel Mack 
35772ba48beSAndrew Lunn 			phydev_dbg(phydev, "%s(): phy was reset\n",
35813a56b44SDaniel Mack 				   __func__);
35913a56b44SDaniel Mack 			priv->phy_reset = true;
36013a56b44SDaniel Mack 		}
36113a56b44SDaniel Mack 	} else {
36213a56b44SDaniel Mack 		priv->phy_reset = false;
36313a56b44SDaniel Mack 	}
36413a56b44SDaniel Mack }
36513a56b44SDaniel Mack 
366f62265b5SZefir Kurtisi static int at803x_aneg_done(struct phy_device *phydev)
367f62265b5SZefir Kurtisi {
368f62265b5SZefir Kurtisi 	int ccr;
369f62265b5SZefir Kurtisi 
370f62265b5SZefir Kurtisi 	int aneg_done = genphy_aneg_done(phydev);
371f62265b5SZefir Kurtisi 	if (aneg_done != BMSR_ANEGCOMPLETE)
372f62265b5SZefir Kurtisi 		return aneg_done;
373f62265b5SZefir Kurtisi 
374f62265b5SZefir Kurtisi 	/*
375f62265b5SZefir Kurtisi 	 * in SGMII mode, if copper side autoneg is successful,
376f62265b5SZefir Kurtisi 	 * also check SGMII side autoneg result
377f62265b5SZefir Kurtisi 	 */
378f62265b5SZefir Kurtisi 	ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG);
379f62265b5SZefir Kurtisi 	if ((ccr & AT803X_MODE_CFG_MASK) != AT803X_MODE_CFG_SGMII)
380f62265b5SZefir Kurtisi 		return aneg_done;
381f62265b5SZefir Kurtisi 
382f62265b5SZefir Kurtisi 	/* switch to SGMII/fiber page */
383f62265b5SZefir Kurtisi 	phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr & ~AT803X_BT_BX_REG_SEL);
384f62265b5SZefir Kurtisi 
385f62265b5SZefir Kurtisi 	/* check if the SGMII link is OK. */
386f62265b5SZefir Kurtisi 	if (!(phy_read(phydev, AT803X_PSSR) & AT803X_PSSR_MR_AN_COMPLETE)) {
387f62265b5SZefir Kurtisi 		pr_warn("803x_aneg_done: SGMII link is not ok\n");
388f62265b5SZefir Kurtisi 		aneg_done = 0;
389f62265b5SZefir Kurtisi 	}
390f62265b5SZefir Kurtisi 	/* switch back to copper page */
391f62265b5SZefir Kurtisi 	phy_write(phydev, AT803X_REG_CHIP_CONFIG, ccr | AT803X_BT_BX_REG_SEL);
392f62265b5SZefir Kurtisi 
393f62265b5SZefir Kurtisi 	return aneg_done;
394f62265b5SZefir Kurtisi }
395f62265b5SZefir Kurtisi 
396317420abSMugunthan V N static struct phy_driver at803x_driver[] = {
397317420abSMugunthan V N {
3980ca7111aSMatus Ujhelyi 	/* ATHEROS 8035 */
399bd8ca17fSDaniel Mack 	.phy_id			= ATH8035_PHY_ID,
4000ca7111aSMatus Ujhelyi 	.name			= "Atheros 8035 ethernet",
4010ca7111aSMatus Ujhelyi 	.phy_id_mask		= 0xffffffef,
40213a56b44SDaniel Mack 	.probe			= at803x_probe,
4030ca7111aSMatus Ujhelyi 	.config_init		= at803x_config_init,
404ea13c9eeSMugunthan V N 	.set_wol		= at803x_set_wol,
405ea13c9eeSMugunthan V N 	.get_wol		= at803x_get_wol,
4066229ed1fSDaniel Mack 	.suspend		= at803x_suspend,
4076229ed1fSDaniel Mack 	.resume			= at803x_resume,
4080ca7111aSMatus Ujhelyi 	.features		= PHY_GBIT_FEATURES,
4090ca7111aSMatus Ujhelyi 	.flags			= PHY_HAS_INTERRUPT,
4100197ffedSDaniel Mack 	.config_aneg		= genphy_config_aneg,
4110197ffedSDaniel Mack 	.read_status		= genphy_read_status,
4120eae5982SMåns Rullgård 	.ack_interrupt		= at803x_ack_interrupt,
4130eae5982SMåns Rullgård 	.config_intr		= at803x_config_intr,
414317420abSMugunthan V N }, {
4150ca7111aSMatus Ujhelyi 	/* ATHEROS 8030 */
416bd8ca17fSDaniel Mack 	.phy_id			= ATH8030_PHY_ID,
4170ca7111aSMatus Ujhelyi 	.name			= "Atheros 8030 ethernet",
4180ca7111aSMatus Ujhelyi 	.phy_id_mask		= 0xffffffef,
41913a56b44SDaniel Mack 	.probe			= at803x_probe,
4200ca7111aSMatus Ujhelyi 	.config_init		= at803x_config_init,
42113a56b44SDaniel Mack 	.link_change_notify	= at803x_link_change_notify,
422ea13c9eeSMugunthan V N 	.set_wol		= at803x_set_wol,
423ea13c9eeSMugunthan V N 	.get_wol		= at803x_get_wol,
4246229ed1fSDaniel Mack 	.suspend		= at803x_suspend,
4256229ed1fSDaniel Mack 	.resume			= at803x_resume,
426e15bb4c6SMartin Blumenstingl 	.features		= PHY_BASIC_FEATURES,
4270ca7111aSMatus Ujhelyi 	.flags			= PHY_HAS_INTERRUPT,
4280197ffedSDaniel Mack 	.config_aneg		= genphy_config_aneg,
4290197ffedSDaniel Mack 	.read_status		= genphy_read_status,
4300eae5982SMåns Rullgård 	.ack_interrupt		= at803x_ack_interrupt,
4310eae5982SMåns Rullgård 	.config_intr		= at803x_config_intr,
43205d7cce8SMugunthan V N }, {
43305d7cce8SMugunthan V N 	/* ATHEROS 8031 */
434bd8ca17fSDaniel Mack 	.phy_id			= ATH8031_PHY_ID,
43505d7cce8SMugunthan V N 	.name			= "Atheros 8031 ethernet",
43605d7cce8SMugunthan V N 	.phy_id_mask		= 0xffffffef,
43713a56b44SDaniel Mack 	.probe			= at803x_probe,
43805d7cce8SMugunthan V N 	.config_init		= at803x_config_init,
43905d7cce8SMugunthan V N 	.set_wol		= at803x_set_wol,
44005d7cce8SMugunthan V N 	.get_wol		= at803x_get_wol,
4416229ed1fSDaniel Mack 	.suspend		= at803x_suspend,
4426229ed1fSDaniel Mack 	.resume			= at803x_resume,
44305d7cce8SMugunthan V N 	.features		= PHY_GBIT_FEATURES,
44405d7cce8SMugunthan V N 	.flags			= PHY_HAS_INTERRUPT,
4450197ffedSDaniel Mack 	.config_aneg		= genphy_config_aneg,
4460197ffedSDaniel Mack 	.read_status		= genphy_read_status,
447f62265b5SZefir Kurtisi 	.aneg_done		= at803x_aneg_done,
44877a99394SZhao Qiang 	.ack_interrupt		= &at803x_ack_interrupt,
44977a99394SZhao Qiang 	.config_intr		= &at803x_config_intr,
450317420abSMugunthan V N } };
4510ca7111aSMatus Ujhelyi 
45250fd7150SJohan Hovold module_phy_driver(at803x_driver);
4530ca7111aSMatus Ujhelyi 
4540ca7111aSMatus Ujhelyi static struct mdio_device_id __maybe_unused atheros_tbl[] = {
455bd8ca17fSDaniel Mack 	{ ATH8030_PHY_ID, 0xffffffef },
456bd8ca17fSDaniel Mack 	{ ATH8031_PHY_ID, 0xffffffef },
457bd8ca17fSDaniel Mack 	{ ATH8035_PHY_ID, 0xffffffef },
4580ca7111aSMatus Ujhelyi 	{ }
4590ca7111aSMatus Ujhelyi };
4600ca7111aSMatus Ujhelyi 
4610ca7111aSMatus Ujhelyi MODULE_DEVICE_TABLE(mdio, atheros_tbl);
462