xref: /openbmc/linux/drivers/net/phy/dp83869.c (revision 9dfe2937)
101db923eSDan Murphy // SPDX-License-Identifier: GPL-2.0
201db923eSDan Murphy /* Driver for the Texas Instruments DP83869 PHY
301db923eSDan Murphy  * Copyright (C) 2019 Texas Instruments Inc.
401db923eSDan Murphy  */
501db923eSDan Murphy 
601db923eSDan Murphy #include <linux/ethtool.h>
749fc2301SDan Murphy #include <linux/etherdevice.h>
801db923eSDan Murphy #include <linux/kernel.h>
901db923eSDan Murphy #include <linux/mii.h>
1001db923eSDan Murphy #include <linux/module.h>
1101db923eSDan Murphy #include <linux/of.h>
1201db923eSDan Murphy #include <linux/phy.h>
1301db923eSDan Murphy #include <linux/delay.h>
14811ac400SDan Murphy #include <linux/bitfield.h>
1501db923eSDan Murphy 
1601db923eSDan Murphy #include <dt-bindings/net/ti-dp83869.h>
1701db923eSDan Murphy 
1801db923eSDan Murphy #define DP83869_PHY_ID		0x2000a0f1
191388d4adSHari Nagalla #define DP83561_PHY_ID		0x2000a1a4
2001db923eSDan Murphy #define DP83869_DEVADDR		0x1f
2101db923eSDan Murphy 
2201db923eSDan Murphy #define MII_DP83869_PHYCTRL	0x10
2301db923eSDan Murphy #define MII_DP83869_MICR	0x12
2401db923eSDan Murphy #define MII_DP83869_ISR		0x13
25811ac400SDan Murphy #define DP83869_CFG2		0x14
2601db923eSDan Murphy #define DP83869_CTRL		0x1f
2701db923eSDan Murphy #define DP83869_CFG4		0x1e
2801db923eSDan Murphy 
2901db923eSDan Murphy /* Extended Registers */
3001db923eSDan Murphy #define DP83869_GEN_CFG3        0x0031
3101db923eSDan Murphy #define DP83869_RGMIICTL	0x0032
3201db923eSDan Murphy #define DP83869_STRAP_STS1	0x006e
3301db923eSDan Murphy #define DP83869_RGMIIDCTL	0x0086
3449fc2301SDan Murphy #define DP83869_RXFCFG		0x0134
3549fc2301SDan Murphy #define DP83869_RXFPMD1		0x0136
3649fc2301SDan Murphy #define DP83869_RXFPMD2		0x0137
3749fc2301SDan Murphy #define DP83869_RXFPMD3		0x0138
3849fc2301SDan Murphy #define DP83869_RXFSOP1		0x0139
3949fc2301SDan Murphy #define DP83869_RXFSOP2		0x013A
4049fc2301SDan Murphy #define DP83869_RXFSOP3		0x013B
4101db923eSDan Murphy #define DP83869_IO_MUX_CFG	0x0170
4201db923eSDan Murphy #define DP83869_OP_MODE		0x01df
4301db923eSDan Murphy #define DP83869_FX_CTRL		0x0c00
4401db923eSDan Murphy 
4501db923eSDan Murphy #define DP83869_SW_RESET	BIT(15)
4601db923eSDan Murphy #define DP83869_SW_RESTART	BIT(14)
4701db923eSDan Murphy 
4801db923eSDan Murphy /* MICR Interrupt bits */
4901db923eSDan Murphy #define MII_DP83869_MICR_AN_ERR_INT_EN		BIT(15)
5001db923eSDan Murphy #define MII_DP83869_MICR_SPEED_CHNG_INT_EN	BIT(14)
5101db923eSDan Murphy #define MII_DP83869_MICR_DUP_MODE_CHNG_INT_EN	BIT(13)
5201db923eSDan Murphy #define MII_DP83869_MICR_PAGE_RXD_INT_EN	BIT(12)
5301db923eSDan Murphy #define MII_DP83869_MICR_AUTONEG_COMP_INT_EN	BIT(11)
5401db923eSDan Murphy #define MII_DP83869_MICR_LINK_STS_CHNG_INT_EN	BIT(10)
5501db923eSDan Murphy #define MII_DP83869_MICR_FALSE_CARRIER_INT_EN	BIT(8)
5601db923eSDan Murphy #define MII_DP83869_MICR_SLEEP_MODE_CHNG_INT_EN	BIT(4)
5701db923eSDan Murphy #define MII_DP83869_MICR_WOL_INT_EN		BIT(3)
5801db923eSDan Murphy #define MII_DP83869_MICR_XGMII_ERR_INT_EN	BIT(2)
5901db923eSDan Murphy #define MII_DP83869_MICR_POL_CHNG_INT_EN	BIT(1)
6001db923eSDan Murphy #define MII_DP83869_MICR_JABBER_INT_EN		BIT(0)
6101db923eSDan Murphy 
6201db923eSDan Murphy #define MII_DP83869_BMCR_DEFAULT	(BMCR_ANENABLE | \
6301db923eSDan Murphy 					 BMCR_FULLDPLX | \
6401db923eSDan Murphy 					 BMCR_SPEED1000)
6501db923eSDan Murphy 
66a29de52bSDan Murphy #define MII_DP83869_FIBER_ADVERTISE    (ADVERTISED_FIBRE | \
67a29de52bSDan Murphy 					ADVERTISED_Pause | \
68a29de52bSDan Murphy 					ADVERTISED_Asym_Pause)
69a29de52bSDan Murphy 
7001db923eSDan Murphy /* This is the same bit mask as the BMCR so re-use the BMCR default */
7101db923eSDan Murphy #define DP83869_FX_CTRL_DEFAULT	MII_DP83869_BMCR_DEFAULT
7201db923eSDan Murphy 
7301db923eSDan Murphy /* CFG1 bits */
7401db923eSDan Murphy #define DP83869_CFG1_DEFAULT	(ADVERTISE_1000HALF | \
7501db923eSDan Murphy 				 ADVERTISE_1000FULL | \
7601db923eSDan Murphy 				 CTL1000_AS_MASTER)
7701db923eSDan Murphy 
7801db923eSDan Murphy /* RGMIICTL bits */
7901db923eSDan Murphy #define DP83869_RGMII_TX_CLK_DELAY_EN		BIT(1)
8001db923eSDan Murphy #define DP83869_RGMII_RX_CLK_DELAY_EN		BIT(0)
8101db923eSDan Murphy 
82736b25afSDan Murphy /* RGMIIDCTL */
83736b25afSDan Murphy #define DP83869_RGMII_CLK_DELAY_SHIFT		4
84736b25afSDan Murphy #define DP83869_CLK_DELAY_DEF			7
85736b25afSDan Murphy 
8601db923eSDan Murphy /* STRAP_STS1 bits */
870eaf8ccfSDan Murphy #define DP83869_STRAP_OP_MODE_MASK		GENMASK(2, 0)
8801db923eSDan Murphy #define DP83869_STRAP_STS1_RESERVED		BIT(11)
89c4566aecSDan Murphy #define DP83869_STRAP_MIRROR_ENABLED           BIT(12)
9001db923eSDan Murphy 
9101db923eSDan Murphy /* PHYCTRL bits */
9201db923eSDan Murphy #define DP83869_RX_FIFO_SHIFT	12
9301db923eSDan Murphy #define DP83869_TX_FIFO_SHIFT	14
9401db923eSDan Murphy 
9501db923eSDan Murphy /* PHY_CTRL lower bytes 0x48 are declared as reserved */
9601db923eSDan Murphy #define DP83869_PHY_CTRL_DEFAULT	0x48
9701db923eSDan Murphy #define DP83869_PHYCR_FIFO_DEPTH_MASK	GENMASK(15, 12)
9801db923eSDan Murphy #define DP83869_PHYCR_RESERVED_MASK	BIT(11)
9901db923eSDan Murphy 
10001db923eSDan Murphy /* IO_MUX_CFG bits */
10101db923eSDan Murphy #define DP83869_IO_MUX_CFG_IO_IMPEDANCE_CTRL	0x1f
10201db923eSDan Murphy 
10301db923eSDan Murphy #define DP83869_IO_MUX_CFG_IO_IMPEDANCE_MAX	0x0
10401db923eSDan Murphy #define DP83869_IO_MUX_CFG_IO_IMPEDANCE_MIN	0x1f
10501db923eSDan Murphy #define DP83869_IO_MUX_CFG_CLK_O_SEL_MASK	(0x1f << 8)
10601db923eSDan Murphy #define DP83869_IO_MUX_CFG_CLK_O_SEL_SHIFT	8
10701db923eSDan Murphy 
10801db923eSDan Murphy /* CFG3 bits */
10901db923eSDan Murphy #define DP83869_CFG3_PORT_MIRROR_EN              BIT(0)
11001db923eSDan Murphy 
11101db923eSDan Murphy /* CFG4 bits */
11201db923eSDan Murphy #define DP83869_INT_OE	BIT(7)
11301db923eSDan Murphy 
11401db923eSDan Murphy /* OP MODE */
11501db923eSDan Murphy #define DP83869_OP_MODE_MII			BIT(5)
11601db923eSDan Murphy #define DP83869_SGMII_RGMII_BRIDGE		BIT(6)
11701db923eSDan Murphy 
11849fc2301SDan Murphy /* RXFCFG bits*/
11949fc2301SDan Murphy #define DP83869_WOL_MAGIC_EN		BIT(0)
12049fc2301SDan Murphy #define DP83869_WOL_PATTERN_EN		BIT(1)
12149fc2301SDan Murphy #define DP83869_WOL_BCAST_EN		BIT(2)
12249fc2301SDan Murphy #define DP83869_WOL_UCAST_EN		BIT(4)
12349fc2301SDan Murphy #define DP83869_WOL_SEC_EN		BIT(5)
12449fc2301SDan Murphy #define DP83869_WOL_ENH_MAC		BIT(7)
12549fc2301SDan Murphy 
126811ac400SDan Murphy /* CFG2 bits */
127811ac400SDan Murphy #define DP83869_DOWNSHIFT_EN		(BIT(8) | BIT(9))
128811ac400SDan Murphy #define DP83869_DOWNSHIFT_ATTEMPT_MASK	(BIT(10) | BIT(11))
129811ac400SDan Murphy #define DP83869_DOWNSHIFT_1_COUNT_VAL	0
130811ac400SDan Murphy #define DP83869_DOWNSHIFT_2_COUNT_VAL	1
131811ac400SDan Murphy #define DP83869_DOWNSHIFT_4_COUNT_VAL	2
132811ac400SDan Murphy #define DP83869_DOWNSHIFT_8_COUNT_VAL	3
133811ac400SDan Murphy #define DP83869_DOWNSHIFT_1_COUNT	1
134811ac400SDan Murphy #define DP83869_DOWNSHIFT_2_COUNT	2
135811ac400SDan Murphy #define DP83869_DOWNSHIFT_4_COUNT	4
136811ac400SDan Murphy #define DP83869_DOWNSHIFT_8_COUNT	8
137811ac400SDan Murphy 
13801db923eSDan Murphy enum {
13901db923eSDan Murphy 	DP83869_PORT_MIRRORING_KEEP,
14001db923eSDan Murphy 	DP83869_PORT_MIRRORING_EN,
14101db923eSDan Murphy 	DP83869_PORT_MIRRORING_DIS,
14201db923eSDan Murphy };
14301db923eSDan Murphy 
14401db923eSDan Murphy struct dp83869_private {
14501db923eSDan Murphy 	int tx_fifo_depth;
14601db923eSDan Murphy 	int rx_fifo_depth;
147736b25afSDan Murphy 	s32 rx_int_delay;
148736b25afSDan Murphy 	s32 tx_int_delay;
14901db923eSDan Murphy 	int io_impedance;
15001db923eSDan Murphy 	int port_mirroring;
15101db923eSDan Murphy 	bool rxctrl_strap_quirk;
15201db923eSDan Murphy 	int clk_output_sel;
15301db923eSDan Murphy 	int mode;
15401db923eSDan Murphy };
15501db923eSDan Murphy 
dp83869_read_status(struct phy_device * phydev)156a29de52bSDan Murphy static int dp83869_read_status(struct phy_device *phydev)
157a29de52bSDan Murphy {
158a29de52bSDan Murphy 	struct dp83869_private *dp83869 = phydev->priv;
159a29de52bSDan Murphy 	int ret;
160a29de52bSDan Murphy 
161a29de52bSDan Murphy 	ret = genphy_read_status(phydev);
162a29de52bSDan Murphy 	if (ret)
163a29de52bSDan Murphy 		return ret;
164a29de52bSDan Murphy 
165a29de52bSDan Murphy 	if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported)) {
166a29de52bSDan Murphy 		if (phydev->link) {
167a29de52bSDan Murphy 			if (dp83869->mode == DP83869_RGMII_100_BASE)
168a29de52bSDan Murphy 				phydev->speed = SPEED_100;
169a29de52bSDan Murphy 		} else {
170a29de52bSDan Murphy 			phydev->speed = SPEED_UNKNOWN;
171a29de52bSDan Murphy 			phydev->duplex = DUPLEX_UNKNOWN;
172a29de52bSDan Murphy 		}
173a29de52bSDan Murphy 	}
174a29de52bSDan Murphy 
175a29de52bSDan Murphy 	return 0;
176a29de52bSDan Murphy }
177a29de52bSDan Murphy 
dp83869_ack_interrupt(struct phy_device * phydev)17801db923eSDan Murphy static int dp83869_ack_interrupt(struct phy_device *phydev)
17901db923eSDan Murphy {
18001db923eSDan Murphy 	int err = phy_read(phydev, MII_DP83869_ISR);
18101db923eSDan Murphy 
18201db923eSDan Murphy 	if (err < 0)
18301db923eSDan Murphy 		return err;
18401db923eSDan Murphy 
18501db923eSDan Murphy 	return 0;
18601db923eSDan Murphy }
18701db923eSDan Murphy 
dp83869_config_intr(struct phy_device * phydev)18801db923eSDan Murphy static int dp83869_config_intr(struct phy_device *phydev)
18901db923eSDan Murphy {
190aa2d603aSIoana Ciornei 	int micr_status = 0, err;
19101db923eSDan Murphy 
19201db923eSDan Murphy 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
193aa2d603aSIoana Ciornei 		err = dp83869_ack_interrupt(phydev);
194aa2d603aSIoana Ciornei 		if (err)
195aa2d603aSIoana Ciornei 			return err;
196aa2d603aSIoana Ciornei 
19701db923eSDan Murphy 		micr_status = phy_read(phydev, MII_DP83869_MICR);
19801db923eSDan Murphy 		if (micr_status < 0)
19901db923eSDan Murphy 			return micr_status;
20001db923eSDan Murphy 
20101db923eSDan Murphy 		micr_status |=
20201db923eSDan Murphy 			(MII_DP83869_MICR_AN_ERR_INT_EN |
20301db923eSDan Murphy 			MII_DP83869_MICR_SPEED_CHNG_INT_EN |
20401db923eSDan Murphy 			MII_DP83869_MICR_AUTONEG_COMP_INT_EN |
20501db923eSDan Murphy 			MII_DP83869_MICR_LINK_STS_CHNG_INT_EN |
20601db923eSDan Murphy 			MII_DP83869_MICR_DUP_MODE_CHNG_INT_EN |
20701db923eSDan Murphy 			MII_DP83869_MICR_SLEEP_MODE_CHNG_INT_EN);
20801db923eSDan Murphy 
209aa2d603aSIoana Ciornei 		err = phy_write(phydev, MII_DP83869_MICR, micr_status);
210aa2d603aSIoana Ciornei 	} else {
211aa2d603aSIoana Ciornei 		err = phy_write(phydev, MII_DP83869_MICR, micr_status);
212aa2d603aSIoana Ciornei 		if (err)
213aa2d603aSIoana Ciornei 			return err;
214aa2d603aSIoana Ciornei 
215aa2d603aSIoana Ciornei 		err = dp83869_ack_interrupt(phydev);
21601db923eSDan Murphy 	}
21701db923eSDan Murphy 
218aa2d603aSIoana Ciornei 	return err;
21901db923eSDan Murphy }
22001db923eSDan Murphy 
dp83869_handle_interrupt(struct phy_device * phydev)2211d1ae3c6SIoana Ciornei static irqreturn_t dp83869_handle_interrupt(struct phy_device *phydev)
2221d1ae3c6SIoana Ciornei {
2231d1ae3c6SIoana Ciornei 	int irq_status, irq_enabled;
2241d1ae3c6SIoana Ciornei 
2251d1ae3c6SIoana Ciornei 	irq_status = phy_read(phydev, MII_DP83869_ISR);
2261d1ae3c6SIoana Ciornei 	if (irq_status < 0) {
2271d1ae3c6SIoana Ciornei 		phy_error(phydev);
2281d1ae3c6SIoana Ciornei 		return IRQ_NONE;
2291d1ae3c6SIoana Ciornei 	}
2301d1ae3c6SIoana Ciornei 
2311d1ae3c6SIoana Ciornei 	irq_enabled = phy_read(phydev, MII_DP83869_MICR);
2321d1ae3c6SIoana Ciornei 	if (irq_enabled < 0) {
2331d1ae3c6SIoana Ciornei 		phy_error(phydev);
2341d1ae3c6SIoana Ciornei 		return IRQ_NONE;
2351d1ae3c6SIoana Ciornei 	}
2361d1ae3c6SIoana Ciornei 
2371d1ae3c6SIoana Ciornei 	if (!(irq_status & irq_enabled))
2381d1ae3c6SIoana Ciornei 		return IRQ_NONE;
2391d1ae3c6SIoana Ciornei 
2401d1ae3c6SIoana Ciornei 	phy_trigger_machine(phydev);
2411d1ae3c6SIoana Ciornei 
2421d1ae3c6SIoana Ciornei 	return IRQ_HANDLED;
2431d1ae3c6SIoana Ciornei }
2441d1ae3c6SIoana Ciornei 
dp83869_set_wol(struct phy_device * phydev,struct ethtool_wolinfo * wol)24549fc2301SDan Murphy static int dp83869_set_wol(struct phy_device *phydev,
24649fc2301SDan Murphy 			   struct ethtool_wolinfo *wol)
24749fc2301SDan Murphy {
24849fc2301SDan Murphy 	struct net_device *ndev = phydev->attached_dev;
24949fc2301SDan Murphy 	int val_rxcfg, val_micr;
25086466cbeSJakub Kicinski 	const u8 *mac;
25149fc2301SDan Murphy 	int ret;
25249fc2301SDan Murphy 
25349fc2301SDan Murphy 	val_rxcfg = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_RXFCFG);
25449fc2301SDan Murphy 	if (val_rxcfg < 0)
25549fc2301SDan Murphy 		return val_rxcfg;
25649fc2301SDan Murphy 
25749fc2301SDan Murphy 	val_micr = phy_read(phydev, MII_DP83869_MICR);
25849fc2301SDan Murphy 	if (val_micr < 0)
25949fc2301SDan Murphy 		return val_micr;
26049fc2301SDan Murphy 
26149fc2301SDan Murphy 	if (wol->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_UCAST |
26249fc2301SDan Murphy 			    WAKE_BCAST)) {
26349fc2301SDan Murphy 		val_rxcfg |= DP83869_WOL_ENH_MAC;
26449fc2301SDan Murphy 		val_micr |= MII_DP83869_MICR_WOL_INT_EN;
26549fc2301SDan Murphy 
26649fc2301SDan Murphy 		if (wol->wolopts & WAKE_MAGIC ||
26749fc2301SDan Murphy 		    wol->wolopts & WAKE_MAGICSECURE) {
26886466cbeSJakub Kicinski 			mac = (const u8 *)ndev->dev_addr;
26949fc2301SDan Murphy 
27049fc2301SDan Murphy 			if (!is_valid_ether_addr(mac))
27149fc2301SDan Murphy 				return -EINVAL;
27249fc2301SDan Murphy 
27349fc2301SDan Murphy 			ret = phy_write_mmd(phydev, DP83869_DEVADDR,
27449fc2301SDan Murphy 					    DP83869_RXFPMD1,
27549fc2301SDan Murphy 					    mac[1] << 8 | mac[0]);
27649fc2301SDan Murphy 			if (ret)
27749fc2301SDan Murphy 				return ret;
27849fc2301SDan Murphy 
27949fc2301SDan Murphy 			ret = phy_write_mmd(phydev, DP83869_DEVADDR,
28049fc2301SDan Murphy 					    DP83869_RXFPMD2,
28149fc2301SDan Murphy 					    mac[3] << 8 | mac[2]);
28249fc2301SDan Murphy 			if (ret)
28349fc2301SDan Murphy 				return ret;
28449fc2301SDan Murphy 
28549fc2301SDan Murphy 			ret = phy_write_mmd(phydev, DP83869_DEVADDR,
28649fc2301SDan Murphy 					    DP83869_RXFPMD3,
28749fc2301SDan Murphy 					    mac[5] << 8 | mac[4]);
28849fc2301SDan Murphy 			if (ret)
28949fc2301SDan Murphy 				return ret;
29049fc2301SDan Murphy 
29149fc2301SDan Murphy 			val_rxcfg |= DP83869_WOL_MAGIC_EN;
29249fc2301SDan Murphy 		} else {
29349fc2301SDan Murphy 			val_rxcfg &= ~DP83869_WOL_MAGIC_EN;
29449fc2301SDan Murphy 		}
29549fc2301SDan Murphy 
29649fc2301SDan Murphy 		if (wol->wolopts & WAKE_MAGICSECURE) {
29749fc2301SDan Murphy 			ret = phy_write_mmd(phydev, DP83869_DEVADDR,
29849fc2301SDan Murphy 					    DP83869_RXFSOP1,
29949fc2301SDan Murphy 					    (wol->sopass[1] << 8) | wol->sopass[0]);
30049fc2301SDan Murphy 			if (ret)
30149fc2301SDan Murphy 				return ret;
30249fc2301SDan Murphy 
30349fc2301SDan Murphy 			ret = phy_write_mmd(phydev, DP83869_DEVADDR,
30449fc2301SDan Murphy 					    DP83869_RXFSOP2,
30549fc2301SDan Murphy 					    (wol->sopass[3] << 8) | wol->sopass[2]);
30649fc2301SDan Murphy 			if (ret)
30749fc2301SDan Murphy 				return ret;
30849fc2301SDan Murphy 			ret = phy_write_mmd(phydev, DP83869_DEVADDR,
30949fc2301SDan Murphy 					    DP83869_RXFSOP3,
31049fc2301SDan Murphy 					    (wol->sopass[5] << 8) | wol->sopass[4]);
31149fc2301SDan Murphy 			if (ret)
31249fc2301SDan Murphy 				return ret;
31349fc2301SDan Murphy 
31449fc2301SDan Murphy 			val_rxcfg |= DP83869_WOL_SEC_EN;
31549fc2301SDan Murphy 		} else {
31649fc2301SDan Murphy 			val_rxcfg &= ~DP83869_WOL_SEC_EN;
31749fc2301SDan Murphy 		}
31849fc2301SDan Murphy 
31949fc2301SDan Murphy 		if (wol->wolopts & WAKE_UCAST)
32049fc2301SDan Murphy 			val_rxcfg |= DP83869_WOL_UCAST_EN;
32149fc2301SDan Murphy 		else
32249fc2301SDan Murphy 			val_rxcfg &= ~DP83869_WOL_UCAST_EN;
32349fc2301SDan Murphy 
32449fc2301SDan Murphy 		if (wol->wolopts & WAKE_BCAST)
32549fc2301SDan Murphy 			val_rxcfg |= DP83869_WOL_BCAST_EN;
32649fc2301SDan Murphy 		else
32749fc2301SDan Murphy 			val_rxcfg &= ~DP83869_WOL_BCAST_EN;
32849fc2301SDan Murphy 	} else {
32949fc2301SDan Murphy 		val_rxcfg &= ~DP83869_WOL_ENH_MAC;
33049fc2301SDan Murphy 		val_micr &= ~MII_DP83869_MICR_WOL_INT_EN;
33149fc2301SDan Murphy 	}
33249fc2301SDan Murphy 
33349fc2301SDan Murphy 	ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_RXFCFG, val_rxcfg);
33449fc2301SDan Murphy 	if (ret)
33549fc2301SDan Murphy 		return ret;
33649fc2301SDan Murphy 
33749fc2301SDan Murphy 	return phy_write(phydev, MII_DP83869_MICR, val_micr);
33849fc2301SDan Murphy }
33949fc2301SDan Murphy 
dp83869_get_wol(struct phy_device * phydev,struct ethtool_wolinfo * wol)34049fc2301SDan Murphy static void dp83869_get_wol(struct phy_device *phydev,
34149fc2301SDan Murphy 			    struct ethtool_wolinfo *wol)
34249fc2301SDan Murphy {
343e275d49aSColin Ian King 	int value, sopass_val;
34449fc2301SDan Murphy 
34549fc2301SDan Murphy 	wol->supported = (WAKE_UCAST | WAKE_BCAST | WAKE_MAGIC |
34649fc2301SDan Murphy 			WAKE_MAGICSECURE);
34749fc2301SDan Murphy 	wol->wolopts = 0;
34849fc2301SDan Murphy 
34949fc2301SDan Murphy 	value = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_RXFCFG);
35049fc2301SDan Murphy 	if (value < 0) {
35149fc2301SDan Murphy 		phydev_err(phydev, "Failed to read RX CFG\n");
35249fc2301SDan Murphy 		return;
35349fc2301SDan Murphy 	}
35449fc2301SDan Murphy 
35549fc2301SDan Murphy 	if (value & DP83869_WOL_UCAST_EN)
35649fc2301SDan Murphy 		wol->wolopts |= WAKE_UCAST;
35749fc2301SDan Murphy 
35849fc2301SDan Murphy 	if (value & DP83869_WOL_BCAST_EN)
35949fc2301SDan Murphy 		wol->wolopts |= WAKE_BCAST;
36049fc2301SDan Murphy 
36149fc2301SDan Murphy 	if (value & DP83869_WOL_MAGIC_EN)
36249fc2301SDan Murphy 		wol->wolopts |= WAKE_MAGIC;
36349fc2301SDan Murphy 
36449fc2301SDan Murphy 	if (value & DP83869_WOL_SEC_EN) {
36549fc2301SDan Murphy 		sopass_val = phy_read_mmd(phydev, DP83869_DEVADDR,
36649fc2301SDan Murphy 					  DP83869_RXFSOP1);
36749fc2301SDan Murphy 		if (sopass_val < 0) {
36849fc2301SDan Murphy 			phydev_err(phydev, "Failed to read RX SOP 1\n");
36949fc2301SDan Murphy 			return;
37049fc2301SDan Murphy 		}
37149fc2301SDan Murphy 
37249fc2301SDan Murphy 		wol->sopass[0] = (sopass_val & 0xff);
37349fc2301SDan Murphy 		wol->sopass[1] = (sopass_val >> 8);
37449fc2301SDan Murphy 
37549fc2301SDan Murphy 		sopass_val = phy_read_mmd(phydev, DP83869_DEVADDR,
37649fc2301SDan Murphy 					  DP83869_RXFSOP2);
37749fc2301SDan Murphy 		if (sopass_val < 0) {
37849fc2301SDan Murphy 			phydev_err(phydev, "Failed to read RX SOP 2\n");
37949fc2301SDan Murphy 			return;
38049fc2301SDan Murphy 		}
38149fc2301SDan Murphy 
38249fc2301SDan Murphy 		wol->sopass[2] = (sopass_val & 0xff);
38349fc2301SDan Murphy 		wol->sopass[3] = (sopass_val >> 8);
38449fc2301SDan Murphy 
38549fc2301SDan Murphy 		sopass_val = phy_read_mmd(phydev, DP83869_DEVADDR,
38649fc2301SDan Murphy 					  DP83869_RXFSOP3);
38749fc2301SDan Murphy 		if (sopass_val < 0) {
38849fc2301SDan Murphy 			phydev_err(phydev, "Failed to read RX SOP 3\n");
38949fc2301SDan Murphy 			return;
39049fc2301SDan Murphy 		}
39149fc2301SDan Murphy 
39249fc2301SDan Murphy 		wol->sopass[4] = (sopass_val & 0xff);
39349fc2301SDan Murphy 		wol->sopass[5] = (sopass_val >> 8);
39449fc2301SDan Murphy 
39549fc2301SDan Murphy 		wol->wolopts |= WAKE_MAGICSECURE;
39649fc2301SDan Murphy 	}
39749fc2301SDan Murphy 
39849fc2301SDan Murphy 	if (!(value & DP83869_WOL_ENH_MAC))
39949fc2301SDan Murphy 		wol->wolopts = 0;
40049fc2301SDan Murphy }
40149fc2301SDan Murphy 
dp83869_get_downshift(struct phy_device * phydev,u8 * data)402811ac400SDan Murphy static int dp83869_get_downshift(struct phy_device *phydev, u8 *data)
403811ac400SDan Murphy {
404811ac400SDan Murphy 	int val, cnt, enable, count;
405811ac400SDan Murphy 
406811ac400SDan Murphy 	val = phy_read(phydev, DP83869_CFG2);
407811ac400SDan Murphy 	if (val < 0)
408811ac400SDan Murphy 		return val;
409811ac400SDan Murphy 
410811ac400SDan Murphy 	enable = FIELD_GET(DP83869_DOWNSHIFT_EN, val);
411811ac400SDan Murphy 	cnt = FIELD_GET(DP83869_DOWNSHIFT_ATTEMPT_MASK, val);
412811ac400SDan Murphy 
413811ac400SDan Murphy 	switch (cnt) {
414811ac400SDan Murphy 	case DP83869_DOWNSHIFT_1_COUNT_VAL:
415811ac400SDan Murphy 		count = DP83869_DOWNSHIFT_1_COUNT;
416811ac400SDan Murphy 		break;
417811ac400SDan Murphy 	case DP83869_DOWNSHIFT_2_COUNT_VAL:
418811ac400SDan Murphy 		count = DP83869_DOWNSHIFT_2_COUNT;
419811ac400SDan Murphy 		break;
420811ac400SDan Murphy 	case DP83869_DOWNSHIFT_4_COUNT_VAL:
421811ac400SDan Murphy 		count = DP83869_DOWNSHIFT_4_COUNT;
422811ac400SDan Murphy 		break;
423811ac400SDan Murphy 	case DP83869_DOWNSHIFT_8_COUNT_VAL:
424811ac400SDan Murphy 		count = DP83869_DOWNSHIFT_8_COUNT;
425811ac400SDan Murphy 		break;
426811ac400SDan Murphy 	default:
427811ac400SDan Murphy 		return -EINVAL;
428811ac400SDan Murphy 	}
429811ac400SDan Murphy 
430811ac400SDan Murphy 	*data = enable ? count : DOWNSHIFT_DEV_DISABLE;
431811ac400SDan Murphy 
432811ac400SDan Murphy 	return 0;
433811ac400SDan Murphy }
434811ac400SDan Murphy 
dp83869_set_downshift(struct phy_device * phydev,u8 cnt)435811ac400SDan Murphy static int dp83869_set_downshift(struct phy_device *phydev, u8 cnt)
436811ac400SDan Murphy {
437811ac400SDan Murphy 	int val, count;
438811ac400SDan Murphy 
439811ac400SDan Murphy 	if (cnt > DP83869_DOWNSHIFT_8_COUNT)
440811ac400SDan Murphy 		return -EINVAL;
441811ac400SDan Murphy 
442811ac400SDan Murphy 	if (!cnt)
443811ac400SDan Murphy 		return phy_clear_bits(phydev, DP83869_CFG2,
444811ac400SDan Murphy 				      DP83869_DOWNSHIFT_EN);
445811ac400SDan Murphy 
446811ac400SDan Murphy 	switch (cnt) {
447811ac400SDan Murphy 	case DP83869_DOWNSHIFT_1_COUNT:
448811ac400SDan Murphy 		count = DP83869_DOWNSHIFT_1_COUNT_VAL;
449811ac400SDan Murphy 		break;
450811ac400SDan Murphy 	case DP83869_DOWNSHIFT_2_COUNT:
451811ac400SDan Murphy 		count = DP83869_DOWNSHIFT_2_COUNT_VAL;
452811ac400SDan Murphy 		break;
453811ac400SDan Murphy 	case DP83869_DOWNSHIFT_4_COUNT:
454811ac400SDan Murphy 		count = DP83869_DOWNSHIFT_4_COUNT_VAL;
455811ac400SDan Murphy 		break;
456811ac400SDan Murphy 	case DP83869_DOWNSHIFT_8_COUNT:
457811ac400SDan Murphy 		count = DP83869_DOWNSHIFT_8_COUNT_VAL;
458811ac400SDan Murphy 		break;
459811ac400SDan Murphy 	default:
460811ac400SDan Murphy 		phydev_err(phydev,
461811ac400SDan Murphy 			   "Downshift count must be 1, 2, 4 or 8\n");
462811ac400SDan Murphy 		return -EINVAL;
463811ac400SDan Murphy 	}
464811ac400SDan Murphy 
465811ac400SDan Murphy 	val = DP83869_DOWNSHIFT_EN;
466811ac400SDan Murphy 	val |= FIELD_PREP(DP83869_DOWNSHIFT_ATTEMPT_MASK, count);
467811ac400SDan Murphy 
468811ac400SDan Murphy 	return phy_modify(phydev, DP83869_CFG2,
469811ac400SDan Murphy 			  DP83869_DOWNSHIFT_EN | DP83869_DOWNSHIFT_ATTEMPT_MASK,
470811ac400SDan Murphy 			  val);
471811ac400SDan Murphy }
472811ac400SDan Murphy 
dp83869_get_tunable(struct phy_device * phydev,struct ethtool_tunable * tuna,void * data)473811ac400SDan Murphy static int dp83869_get_tunable(struct phy_device *phydev,
474811ac400SDan Murphy 			       struct ethtool_tunable *tuna, void *data)
475811ac400SDan Murphy {
476811ac400SDan Murphy 	switch (tuna->id) {
477811ac400SDan Murphy 	case ETHTOOL_PHY_DOWNSHIFT:
478811ac400SDan Murphy 		return dp83869_get_downshift(phydev, data);
479811ac400SDan Murphy 	default:
480811ac400SDan Murphy 		return -EOPNOTSUPP;
481811ac400SDan Murphy 	}
482811ac400SDan Murphy }
483811ac400SDan Murphy 
dp83869_set_tunable(struct phy_device * phydev,struct ethtool_tunable * tuna,const void * data)484811ac400SDan Murphy static int dp83869_set_tunable(struct phy_device *phydev,
485811ac400SDan Murphy 			       struct ethtool_tunable *tuna, const void *data)
486811ac400SDan Murphy {
487811ac400SDan Murphy 	switch (tuna->id) {
488811ac400SDan Murphy 	case ETHTOOL_PHY_DOWNSHIFT:
489811ac400SDan Murphy 		return dp83869_set_downshift(phydev, *(const u8 *)data);
490811ac400SDan Murphy 	default:
491811ac400SDan Murphy 		return -EOPNOTSUPP;
492811ac400SDan Murphy 	}
493811ac400SDan Murphy }
494811ac400SDan Murphy 
dp83869_config_port_mirroring(struct phy_device * phydev)49501db923eSDan Murphy static int dp83869_config_port_mirroring(struct phy_device *phydev)
49601db923eSDan Murphy {
49701db923eSDan Murphy 	struct dp83869_private *dp83869 = phydev->priv;
49801db923eSDan Murphy 
49901db923eSDan Murphy 	if (dp83869->port_mirroring == DP83869_PORT_MIRRORING_EN)
500786c4a53SDan Murphy 		return phy_set_bits_mmd(phydev, DP83869_DEVADDR,
501786c4a53SDan Murphy 					DP83869_GEN_CFG3,
50201db923eSDan Murphy 					DP83869_CFG3_PORT_MIRROR_EN);
50301db923eSDan Murphy 	else
504786c4a53SDan Murphy 		return phy_clear_bits_mmd(phydev, DP83869_DEVADDR,
505786c4a53SDan Murphy 					  DP83869_GEN_CFG3,
50601db923eSDan Murphy 					  DP83869_CFG3_PORT_MIRROR_EN);
50701db923eSDan Murphy }
50801db923eSDan Murphy 
dp83869_set_strapped_mode(struct phy_device * phydev)5090eaf8ccfSDan Murphy static int dp83869_set_strapped_mode(struct phy_device *phydev)
5100eaf8ccfSDan Murphy {
5110eaf8ccfSDan Murphy 	struct dp83869_private *dp83869 = phydev->priv;
5120eaf8ccfSDan Murphy 	int val;
5130eaf8ccfSDan Murphy 
5140eaf8ccfSDan Murphy 	val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_STRAP_STS1);
5150eaf8ccfSDan Murphy 	if (val < 0)
5160eaf8ccfSDan Murphy 		return val;
5170eaf8ccfSDan Murphy 
5180eaf8ccfSDan Murphy 	dp83869->mode = val & DP83869_STRAP_OP_MODE_MASK;
5190eaf8ccfSDan Murphy 
5200eaf8ccfSDan Murphy 	return 0;
5210eaf8ccfSDan Murphy }
5220eaf8ccfSDan Murphy 
523e9293c98SDan Murphy #if IS_ENABLED(CONFIG_OF_MDIO)
524736b25afSDan Murphy static const int dp83869_internal_delay[] = {250, 500, 750, 1000, 1250, 1500,
525736b25afSDan Murphy 					     1750, 2000, 2250, 2500, 2750, 3000,
526736b25afSDan Murphy 					     3250, 3500, 3750, 4000};
527736b25afSDan Murphy 
dp83869_of_init(struct phy_device * phydev)52801db923eSDan Murphy static int dp83869_of_init(struct phy_device *phydev)
52901db923eSDan Murphy {
53001db923eSDan Murphy 	struct dp83869_private *dp83869 = phydev->priv;
53101db923eSDan Murphy 	struct device *dev = &phydev->mdio.dev;
53201db923eSDan Murphy 	struct device_node *of_node = dev->of_node;
533736b25afSDan Murphy 	int delay_size = ARRAY_SIZE(dp83869_internal_delay);
53401db923eSDan Murphy 	int ret;
53501db923eSDan Murphy 
53601db923eSDan Murphy 	if (!of_node)
53701db923eSDan Murphy 		return -ENODEV;
53801db923eSDan Murphy 
53901db923eSDan Murphy 	dp83869->io_impedance = -EINVAL;
54001db923eSDan Murphy 
54101db923eSDan Murphy 	/* Optional configuration */
54201db923eSDan Murphy 	ret = of_property_read_u32(of_node, "ti,clk-output-sel",
54301db923eSDan Murphy 				   &dp83869->clk_output_sel);
54401db923eSDan Murphy 	if (ret || dp83869->clk_output_sel > DP83869_CLK_O_SEL_REF_CLK)
54501db923eSDan Murphy 		dp83869->clk_output_sel = DP83869_CLK_O_SEL_REF_CLK;
54601db923eSDan Murphy 
54701db923eSDan Murphy 	ret = of_property_read_u32(of_node, "ti,op-mode", &dp83869->mode);
54801db923eSDan Murphy 	if (ret == 0) {
54901db923eSDan Murphy 		if (dp83869->mode < DP83869_RGMII_COPPER_ETHERNET ||
55001db923eSDan Murphy 		    dp83869->mode > DP83869_SGMII_COPPER_ETHERNET)
55101db923eSDan Murphy 			return -EINVAL;
5520eaf8ccfSDan Murphy 	} else {
5530eaf8ccfSDan Murphy 		ret = dp83869_set_strapped_mode(phydev);
5540eaf8ccfSDan Murphy 		if (ret)
5550eaf8ccfSDan Murphy 			return ret;
55601db923eSDan Murphy 	}
55701db923eSDan Murphy 
55801db923eSDan Murphy 	if (of_property_read_bool(of_node, "ti,max-output-impedance"))
55901db923eSDan Murphy 		dp83869->io_impedance = DP83869_IO_MUX_CFG_IO_IMPEDANCE_MAX;
56001db923eSDan Murphy 	else if (of_property_read_bool(of_node, "ti,min-output-impedance"))
56101db923eSDan Murphy 		dp83869->io_impedance = DP83869_IO_MUX_CFG_IO_IMPEDANCE_MIN;
56201db923eSDan Murphy 
563c4566aecSDan Murphy 	if (of_property_read_bool(of_node, "enet-phy-lane-swap")) {
564c4566aecSDan Murphy 		dp83869->port_mirroring = DP83869_PORT_MIRRORING_EN;
565c4566aecSDan Murphy 	} else {
566c4566aecSDan Murphy 		/* If the lane swap is not in the DT then check the straps */
567c4566aecSDan Murphy 		ret = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_STRAP_STS1);
568c4566aecSDan Murphy 		if (ret < 0)
569c4566aecSDan Murphy 			return ret;
5704e2905adSDan Murphy 
571c4566aecSDan Murphy 		if (ret & DP83869_STRAP_MIRROR_ENABLED)
57201db923eSDan Murphy 			dp83869->port_mirroring = DP83869_PORT_MIRRORING_EN;
57301db923eSDan Murphy 		else
57401db923eSDan Murphy 			dp83869->port_mirroring = DP83869_PORT_MIRRORING_DIS;
5754e2905adSDan Murphy 
5764e2905adSDan Murphy 		ret = 0;
577c4566aecSDan Murphy 	}
57801db923eSDan Murphy 
57901db923eSDan Murphy 	if (of_property_read_u32(of_node, "rx-fifo-depth",
58001db923eSDan Murphy 				 &dp83869->rx_fifo_depth))
58101db923eSDan Murphy 		dp83869->rx_fifo_depth = DP83869_PHYCR_FIFO_DEPTH_4_B_NIB;
58201db923eSDan Murphy 
58301db923eSDan Murphy 	if (of_property_read_u32(of_node, "tx-fifo-depth",
58401db923eSDan Murphy 				 &dp83869->tx_fifo_depth))
58501db923eSDan Murphy 		dp83869->tx_fifo_depth = DP83869_PHYCR_FIFO_DEPTH_4_B_NIB;
58601db923eSDan Murphy 
587736b25afSDan Murphy 	dp83869->rx_int_delay = phy_get_internal_delay(phydev, dev,
588736b25afSDan Murphy 						       &dp83869_internal_delay[0],
589736b25afSDan Murphy 						       delay_size, true);
590736b25afSDan Murphy 	if (dp83869->rx_int_delay < 0)
59182e2c39fSJosua Mayer 		dp83869->rx_int_delay = DP83869_CLK_DELAY_DEF;
592736b25afSDan Murphy 
593736b25afSDan Murphy 	dp83869->tx_int_delay = phy_get_internal_delay(phydev, dev,
594736b25afSDan Murphy 						       &dp83869_internal_delay[0],
595736b25afSDan Murphy 						       delay_size, false);
596736b25afSDan Murphy 	if (dp83869->tx_int_delay < 0)
59782e2c39fSJosua Mayer 		dp83869->tx_int_delay = DP83869_CLK_DELAY_DEF;
598736b25afSDan Murphy 
599786c4a53SDan Murphy 	return ret;
60001db923eSDan Murphy }
60101db923eSDan Murphy #else
dp83869_of_init(struct phy_device * phydev)60201db923eSDan Murphy static int dp83869_of_init(struct phy_device *phydev)
60301db923eSDan Murphy {
6040eaf8ccfSDan Murphy 	return dp83869_set_strapped_mode(phydev);
60501db923eSDan Murphy }
60601db923eSDan Murphy #endif /* CONFIG_OF_MDIO */
60701db923eSDan Murphy 
dp83869_configure_rgmii(struct phy_device * phydev,struct dp83869_private * dp83869)60801db923eSDan Murphy static int dp83869_configure_rgmii(struct phy_device *phydev,
60901db923eSDan Murphy 				   struct dp83869_private *dp83869)
61001db923eSDan Murphy {
611786c4a53SDan Murphy 	int ret = 0, val;
61201db923eSDan Murphy 
61301db923eSDan Murphy 	if (phy_interface_is_rgmii(phydev)) {
61401db923eSDan Murphy 		val = phy_read(phydev, MII_DP83869_PHYCTRL);
61501db923eSDan Murphy 		if (val < 0)
61601db923eSDan Murphy 			return val;
61701db923eSDan Murphy 
61801db923eSDan Murphy 		val &= ~DP83869_PHYCR_FIFO_DEPTH_MASK;
61901db923eSDan Murphy 		val |= (dp83869->tx_fifo_depth << DP83869_TX_FIFO_SHIFT);
62001db923eSDan Murphy 		val |= (dp83869->rx_fifo_depth << DP83869_RX_FIFO_SHIFT);
62101db923eSDan Murphy 
62201db923eSDan Murphy 		ret = phy_write(phydev, MII_DP83869_PHYCTRL, val);
62301db923eSDan Murphy 		if (ret)
62401db923eSDan Murphy 			return ret;
62501db923eSDan Murphy 	}
62601db923eSDan Murphy 
62701db923eSDan Murphy 	if (dp83869->io_impedance >= 0)
628786c4a53SDan Murphy 		ret = phy_modify_mmd(phydev, DP83869_DEVADDR,
62901db923eSDan Murphy 				     DP83869_IO_MUX_CFG,
63001db923eSDan Murphy 				     DP83869_IO_MUX_CFG_IO_IMPEDANCE_CTRL,
63101db923eSDan Murphy 				     dp83869->io_impedance &
63201db923eSDan Murphy 				     DP83869_IO_MUX_CFG_IO_IMPEDANCE_CTRL);
63301db923eSDan Murphy 
634786c4a53SDan Murphy 	return ret;
63501db923eSDan Murphy }
63601db923eSDan Murphy 
dp83869_configure_fiber(struct phy_device * phydev,struct dp83869_private * dp83869)637a29de52bSDan Murphy static int dp83869_configure_fiber(struct phy_device *phydev,
638a29de52bSDan Murphy 				   struct dp83869_private *dp83869)
639a29de52bSDan Murphy {
640a29de52bSDan Murphy 	int bmcr;
641a29de52bSDan Murphy 	int ret;
642a29de52bSDan Murphy 
643a29de52bSDan Murphy 	/* Only allow advertising what this PHY supports */
644a29de52bSDan Murphy 	linkmode_and(phydev->advertising, phydev->advertising,
645a29de52bSDan Murphy 		     phydev->supported);
646a29de52bSDan Murphy 
647a29de52bSDan Murphy 	linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported);
648a29de52bSDan Murphy 	linkmode_set_bit(ADVERTISED_FIBRE, phydev->advertising);
649a29de52bSDan Murphy 
650a29de52bSDan Murphy 	if (dp83869->mode == DP83869_RGMII_1000_BASE) {
651a29de52bSDan Murphy 		linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
652a29de52bSDan Murphy 				 phydev->supported);
653a29de52bSDan Murphy 	} else {
654a29de52bSDan Murphy 		linkmode_set_bit(ETHTOOL_LINK_MODE_100baseFX_Full_BIT,
655a29de52bSDan Murphy 				 phydev->supported);
656a29de52bSDan Murphy 		linkmode_set_bit(ETHTOOL_LINK_MODE_100baseFX_Half_BIT,
657a29de52bSDan Murphy 				 phydev->supported);
658a29de52bSDan Murphy 
659a29de52bSDan Murphy 		/* Auto neg is not supported in 100base FX mode */
660a29de52bSDan Murphy 		bmcr = phy_read(phydev, MII_BMCR);
661a29de52bSDan Murphy 		if (bmcr < 0)
662a29de52bSDan Murphy 			return bmcr;
663a29de52bSDan Murphy 
664a29de52bSDan Murphy 		phydev->autoneg = AUTONEG_DISABLE;
665a29de52bSDan Murphy 		linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported);
666a29de52bSDan Murphy 		linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->advertising);
667a29de52bSDan Murphy 
668a29de52bSDan Murphy 		if (bmcr & BMCR_ANENABLE) {
669a29de52bSDan Murphy 			ret =  phy_modify(phydev, MII_BMCR, BMCR_ANENABLE, 0);
670a29de52bSDan Murphy 			if (ret < 0)
671a29de52bSDan Murphy 				return ret;
672a29de52bSDan Murphy 		}
673a29de52bSDan Murphy 	}
674a29de52bSDan Murphy 
675a29de52bSDan Murphy 	/* Update advertising from supported */
676a29de52bSDan Murphy 	linkmode_or(phydev->advertising, phydev->advertising,
677a29de52bSDan Murphy 		    phydev->supported);
678a29de52bSDan Murphy 
679a29de52bSDan Murphy 	return 0;
680a29de52bSDan Murphy }
681a29de52bSDan Murphy 
dp83869_configure_mode(struct phy_device * phydev,struct dp83869_private * dp83869)68201db923eSDan Murphy static int dp83869_configure_mode(struct phy_device *phydev,
68301db923eSDan Murphy 				  struct dp83869_private *dp83869)
68401db923eSDan Murphy {
68501db923eSDan Murphy 	int phy_ctrl_val;
68601db923eSDan Murphy 	int ret;
68701db923eSDan Murphy 
68801db923eSDan Murphy 	if (dp83869->mode < DP83869_RGMII_COPPER_ETHERNET ||
68901db923eSDan Murphy 	    dp83869->mode > DP83869_SGMII_COPPER_ETHERNET)
69001db923eSDan Murphy 		return -EINVAL;
69101db923eSDan Murphy 
69201db923eSDan Murphy 	/* Below init sequence for each operational mode is defined in
69301db923eSDan Murphy 	 * section 9.4.8 of the datasheet.
69401db923eSDan Murphy 	 */
69594e86ef1SGrygorii Strashko 	phy_ctrl_val = dp83869->mode;
69694e86ef1SGrygorii Strashko 	if (phydev->interface == PHY_INTERFACE_MODE_MII) {
69794e86ef1SGrygorii Strashko 		if (dp83869->mode == DP83869_100M_MEDIA_CONVERT ||
6989dfe2937SMD Danish Anwar 		    dp83869->mode == DP83869_RGMII_100_BASE ||
6999dfe2937SMD Danish Anwar 		    dp83869->mode == DP83869_RGMII_COPPER_ETHERNET) {
70094e86ef1SGrygorii Strashko 			phy_ctrl_val |= DP83869_OP_MODE_MII;
70194e86ef1SGrygorii Strashko 		} else {
70294e86ef1SGrygorii Strashko 			phydev_err(phydev, "selected op-mode is not valid with MII mode\n");
70394e86ef1SGrygorii Strashko 			return -EINVAL;
70494e86ef1SGrygorii Strashko 		}
70594e86ef1SGrygorii Strashko 	}
70694e86ef1SGrygorii Strashko 
70701db923eSDan Murphy 	ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_OP_MODE,
70894e86ef1SGrygorii Strashko 			    phy_ctrl_val);
70901db923eSDan Murphy 	if (ret)
71001db923eSDan Murphy 		return ret;
71101db923eSDan Murphy 
71201db923eSDan Murphy 	ret = phy_write(phydev, MII_BMCR, MII_DP83869_BMCR_DEFAULT);
71301db923eSDan Murphy 	if (ret)
71401db923eSDan Murphy 		return ret;
71501db923eSDan Murphy 
71601db923eSDan Murphy 	phy_ctrl_val = (dp83869->rx_fifo_depth << DP83869_RX_FIFO_SHIFT |
71701db923eSDan Murphy 			dp83869->tx_fifo_depth << DP83869_TX_FIFO_SHIFT |
71801db923eSDan Murphy 			DP83869_PHY_CTRL_DEFAULT);
71901db923eSDan Murphy 
72001db923eSDan Murphy 	switch (dp83869->mode) {
72101db923eSDan Murphy 	case DP83869_RGMII_COPPER_ETHERNET:
72201db923eSDan Murphy 		ret = phy_write(phydev, MII_DP83869_PHYCTRL,
72301db923eSDan Murphy 				phy_ctrl_val);
72401db923eSDan Murphy 		if (ret)
72501db923eSDan Murphy 			return ret;
72601db923eSDan Murphy 
72701db923eSDan Murphy 		ret = phy_write(phydev, MII_CTRL1000, DP83869_CFG1_DEFAULT);
72801db923eSDan Murphy 		if (ret)
72901db923eSDan Murphy 			return ret;
73001db923eSDan Murphy 
73101db923eSDan Murphy 		ret = dp83869_configure_rgmii(phydev, dp83869);
73201db923eSDan Murphy 		if (ret)
73301db923eSDan Murphy 			return ret;
73401db923eSDan Murphy 		break;
73501db923eSDan Murphy 	case DP83869_RGMII_SGMII_BRIDGE:
736786c4a53SDan Murphy 		ret = phy_modify_mmd(phydev, DP83869_DEVADDR, DP83869_OP_MODE,
73701db923eSDan Murphy 				     DP83869_SGMII_RGMII_BRIDGE,
73801db923eSDan Murphy 				     DP83869_SGMII_RGMII_BRIDGE);
739786c4a53SDan Murphy 		if (ret)
740786c4a53SDan Murphy 			return ret;
74101db923eSDan Murphy 
74201db923eSDan Murphy 		ret = phy_write_mmd(phydev, DP83869_DEVADDR,
74301db923eSDan Murphy 				    DP83869_FX_CTRL, DP83869_FX_CTRL_DEFAULT);
74401db923eSDan Murphy 		if (ret)
74501db923eSDan Murphy 			return ret;
74601db923eSDan Murphy 
74701db923eSDan Murphy 		break;
74801db923eSDan Murphy 	case DP83869_1000M_MEDIA_CONVERT:
74901db923eSDan Murphy 		ret = phy_write(phydev, MII_DP83869_PHYCTRL,
75001db923eSDan Murphy 				phy_ctrl_val);
75101db923eSDan Murphy 		if (ret)
75201db923eSDan Murphy 			return ret;
75301db923eSDan Murphy 
75401db923eSDan Murphy 		ret = phy_write_mmd(phydev, DP83869_DEVADDR,
75501db923eSDan Murphy 				    DP83869_FX_CTRL, DP83869_FX_CTRL_DEFAULT);
75601db923eSDan Murphy 		if (ret)
75701db923eSDan Murphy 			return ret;
75801db923eSDan Murphy 		break;
75901db923eSDan Murphy 	case DP83869_100M_MEDIA_CONVERT:
76001db923eSDan Murphy 		ret = phy_write(phydev, MII_DP83869_PHYCTRL,
76101db923eSDan Murphy 				phy_ctrl_val);
76201db923eSDan Murphy 		if (ret)
76301db923eSDan Murphy 			return ret;
76401db923eSDan Murphy 		break;
76501db923eSDan Murphy 	case DP83869_SGMII_COPPER_ETHERNET:
76601db923eSDan Murphy 		ret = phy_write(phydev, MII_DP83869_PHYCTRL,
76701db923eSDan Murphy 				phy_ctrl_val);
76801db923eSDan Murphy 		if (ret)
76901db923eSDan Murphy 			return ret;
77001db923eSDan Murphy 
77101db923eSDan Murphy 		ret = phy_write(phydev, MII_CTRL1000, DP83869_CFG1_DEFAULT);
77201db923eSDan Murphy 		if (ret)
77301db923eSDan Murphy 			return ret;
77401db923eSDan Murphy 
77501db923eSDan Murphy 		ret = phy_write_mmd(phydev, DP83869_DEVADDR,
77601db923eSDan Murphy 				    DP83869_FX_CTRL, DP83869_FX_CTRL_DEFAULT);
77701db923eSDan Murphy 		if (ret)
77801db923eSDan Murphy 			return ret;
77901db923eSDan Murphy 
78001db923eSDan Murphy 		break;
78101db923eSDan Murphy 	case DP83869_RGMII_1000_BASE:
78201db923eSDan Murphy 	case DP83869_RGMII_100_BASE:
783a29de52bSDan Murphy 		ret = dp83869_configure_fiber(phydev, dp83869);
78401db923eSDan Murphy 		break;
78501db923eSDan Murphy 	default:
78601db923eSDan Murphy 		return -EINVAL;
7873a5f494dSzhengbin 	}
78801db923eSDan Murphy 
789786c4a53SDan Murphy 	return ret;
79001db923eSDan Murphy }
79101db923eSDan Murphy 
dp83869_config_init(struct phy_device * phydev)79201db923eSDan Murphy static int dp83869_config_init(struct phy_device *phydev)
79301db923eSDan Murphy {
79401db923eSDan Murphy 	struct dp83869_private *dp83869 = phydev->priv;
79501db923eSDan Murphy 	int ret, val;
79601db923eSDan Murphy 
797811ac400SDan Murphy 	/* Force speed optimization for the PHY even if it strapped */
798811ac400SDan Murphy 	ret = phy_modify(phydev, DP83869_CFG2, DP83869_DOWNSHIFT_EN,
799811ac400SDan Murphy 			 DP83869_DOWNSHIFT_EN);
800811ac400SDan Murphy 	if (ret)
801811ac400SDan Murphy 		return ret;
802811ac400SDan Murphy 
80301db923eSDan Murphy 	ret = dp83869_configure_mode(phydev, dp83869);
80401db923eSDan Murphy 	if (ret)
80501db923eSDan Murphy 		return ret;
80601db923eSDan Murphy 
80701db923eSDan Murphy 	/* Enable Interrupt output INT_OE in CFG4 register */
80801db923eSDan Murphy 	if (phy_interrupt_is_valid(phydev)) {
80901db923eSDan Murphy 		val = phy_read(phydev, DP83869_CFG4);
81001db923eSDan Murphy 		val |= DP83869_INT_OE;
81101db923eSDan Murphy 		phy_write(phydev, DP83869_CFG4, val);
81201db923eSDan Murphy 	}
81301db923eSDan Murphy 
81401db923eSDan Murphy 	if (dp83869->port_mirroring != DP83869_PORT_MIRRORING_KEEP)
81501db923eSDan Murphy 		dp83869_config_port_mirroring(phydev);
81601db923eSDan Murphy 
81701db923eSDan Murphy 	/* Clock output selection if muxing property is set */
81801db923eSDan Murphy 	if (dp83869->clk_output_sel != DP83869_CLK_O_SEL_REF_CLK)
819786c4a53SDan Murphy 		ret = phy_modify_mmd(phydev,
820786c4a53SDan Murphy 				     DP83869_DEVADDR, DP83869_IO_MUX_CFG,
82101db923eSDan Murphy 				     DP83869_IO_MUX_CFG_CLK_O_SEL_MASK,
82201db923eSDan Murphy 				     dp83869->clk_output_sel <<
82301db923eSDan Murphy 				     DP83869_IO_MUX_CFG_CLK_O_SEL_SHIFT);
82401db923eSDan Murphy 
825736b25afSDan Murphy 	if (phy_interface_is_rgmii(phydev)) {
826736b25afSDan Murphy 		ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIIDCTL,
827736b25afSDan Murphy 				    dp83869->rx_int_delay |
828736b25afSDan Murphy 			dp83869->tx_int_delay << DP83869_RGMII_CLK_DELAY_SHIFT);
829736b25afSDan Murphy 		if (ret)
830736b25afSDan Murphy 			return ret;
831736b25afSDan Murphy 
832736b25afSDan Murphy 		val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIICTL);
833736b25afSDan Murphy 		val |= (DP83869_RGMII_TX_CLK_DELAY_EN |
834736b25afSDan Murphy 			DP83869_RGMII_RX_CLK_DELAY_EN);
835736b25afSDan Murphy 
8362e1ec861SDaniel Gorsulowski 		if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
8372e1ec861SDaniel Gorsulowski 			val &= ~(DP83869_RGMII_TX_CLK_DELAY_EN |
8382e1ec861SDaniel Gorsulowski 				 DP83869_RGMII_RX_CLK_DELAY_EN);
8392e1ec861SDaniel Gorsulowski 
840736b25afSDan Murphy 		if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
8412e1ec861SDaniel Gorsulowski 			val &= ~DP83869_RGMII_TX_CLK_DELAY_EN;
842736b25afSDan Murphy 
843736b25afSDan Murphy 		if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
8442e1ec861SDaniel Gorsulowski 			val &= ~DP83869_RGMII_RX_CLK_DELAY_EN;
845736b25afSDan Murphy 
846736b25afSDan Murphy 		ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIICTL,
847736b25afSDan Murphy 				    val);
848736b25afSDan Murphy 	}
849736b25afSDan Murphy 
850786c4a53SDan Murphy 	return ret;
85101db923eSDan Murphy }
85201db923eSDan Murphy 
dp83869_probe(struct phy_device * phydev)85301db923eSDan Murphy static int dp83869_probe(struct phy_device *phydev)
85401db923eSDan Murphy {
85501db923eSDan Murphy 	struct dp83869_private *dp83869;
85601db923eSDan Murphy 	int ret;
85701db923eSDan Murphy 
85801db923eSDan Murphy 	dp83869 = devm_kzalloc(&phydev->mdio.dev, sizeof(*dp83869),
85901db923eSDan Murphy 			       GFP_KERNEL);
86001db923eSDan Murphy 	if (!dp83869)
86101db923eSDan Murphy 		return -ENOMEM;
86201db923eSDan Murphy 
86301db923eSDan Murphy 	phydev->priv = dp83869;
86401db923eSDan Murphy 
86501db923eSDan Murphy 	ret = dp83869_of_init(phydev);
86601db923eSDan Murphy 	if (ret)
86701db923eSDan Murphy 		return ret;
86801db923eSDan Murphy 
8694217a64eSMichael Walle 	if (dp83869->mode == DP83869_RGMII_100_BASE ||
8704217a64eSMichael Walle 	    dp83869->mode == DP83869_RGMII_1000_BASE)
8714217a64eSMichael Walle 		phydev->port = PORT_FIBRE;
8724217a64eSMichael Walle 
87301db923eSDan Murphy 	return dp83869_config_init(phydev);
87401db923eSDan Murphy }
87501db923eSDan Murphy 
dp83869_phy_reset(struct phy_device * phydev)87601db923eSDan Murphy static int dp83869_phy_reset(struct phy_device *phydev)
87701db923eSDan Murphy {
87801db923eSDan Murphy 	int ret;
87901db923eSDan Murphy 
88001db923eSDan Murphy 	ret = phy_write(phydev, DP83869_CTRL, DP83869_SW_RESET);
88101db923eSDan Murphy 	if (ret < 0)
88201db923eSDan Murphy 		return ret;
88301db923eSDan Murphy 
88401db923eSDan Murphy 	usleep_range(10, 20);
88501db923eSDan Murphy 
88601db923eSDan Murphy 	/* Global sw reset sets all registers to default.
88701db923eSDan Murphy 	 * Need to set the registers in the PHY to the right config.
88801db923eSDan Murphy 	 */
88901db923eSDan Murphy 	return dp83869_config_init(phydev);
89001db923eSDan Murphy }
89101db923eSDan Murphy 
8921388d4adSHari Nagalla 
8931388d4adSHari Nagalla #define DP83869_PHY_DRIVER(_id, _name)				\
8941388d4adSHari Nagalla {								\
8951388d4adSHari Nagalla 	PHY_ID_MATCH_MODEL(_id),				\
8961388d4adSHari Nagalla 	.name		= (_name),				\
8971388d4adSHari Nagalla 	.probe          = dp83869_probe,			\
8981388d4adSHari Nagalla 	.config_init	= dp83869_config_init,			\
8991388d4adSHari Nagalla 	.soft_reset	= dp83869_phy_reset,			\
9001388d4adSHari Nagalla 	.config_intr	= dp83869_config_intr,			\
9011388d4adSHari Nagalla 	.handle_interrupt = dp83869_handle_interrupt,		\
9021388d4adSHari Nagalla 	.read_status	= dp83869_read_status,			\
9031388d4adSHari Nagalla 	.get_tunable	= dp83869_get_tunable,			\
9041388d4adSHari Nagalla 	.set_tunable	= dp83869_set_tunable,			\
9051388d4adSHari Nagalla 	.get_wol	= dp83869_get_wol,			\
9061388d4adSHari Nagalla 	.set_wol	= dp83869_set_wol,			\
9071388d4adSHari Nagalla 	.suspend	= genphy_suspend,			\
9081388d4adSHari Nagalla 	.resume		= genphy_resume,			\
9091388d4adSHari Nagalla }
9101388d4adSHari Nagalla 
91101db923eSDan Murphy static struct phy_driver dp83869_driver[] = {
9121388d4adSHari Nagalla 	DP83869_PHY_DRIVER(DP83869_PHY_ID, "TI DP83869"),
9131388d4adSHari Nagalla 	DP83869_PHY_DRIVER(DP83561_PHY_ID, "TI DP83561-SP"),
91401db923eSDan Murphy 
91501db923eSDan Murphy };
91601db923eSDan Murphy module_phy_driver(dp83869_driver);
91701db923eSDan Murphy 
91801db923eSDan Murphy static struct mdio_device_id __maybe_unused dp83869_tbl[] = {
91901db923eSDan Murphy 	{ PHY_ID_MATCH_MODEL(DP83869_PHY_ID) },
9201388d4adSHari Nagalla 	{ PHY_ID_MATCH_MODEL(DP83561_PHY_ID) },
92101db923eSDan Murphy 	{ }
92201db923eSDan Murphy };
92301db923eSDan Murphy MODULE_DEVICE_TABLE(mdio, dp83869_tbl);
92401db923eSDan Murphy 
92501db923eSDan Murphy MODULE_DESCRIPTION("Texas Instruments DP83869 PHY driver");
92601db923eSDan Murphy MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com");
92701db923eSDan Murphy MODULE_LICENSE("GPL v2");
928