xref: /openbmc/linux/drivers/net/phy/dp83869.c (revision 4217a64e)
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
1901db923eSDan Murphy #define DP83869_DEVADDR		0x1f
2001db923eSDan Murphy 
2101db923eSDan Murphy #define MII_DP83869_PHYCTRL	0x10
2201db923eSDan Murphy #define MII_DP83869_MICR	0x12
2301db923eSDan Murphy #define MII_DP83869_ISR		0x13
24811ac400SDan Murphy #define DP83869_CFG2		0x14
2501db923eSDan Murphy #define DP83869_CTRL		0x1f
2601db923eSDan Murphy #define DP83869_CFG4		0x1e
2701db923eSDan Murphy 
2801db923eSDan Murphy /* Extended Registers */
2901db923eSDan Murphy #define DP83869_GEN_CFG3        0x0031
3001db923eSDan Murphy #define DP83869_RGMIICTL	0x0032
3101db923eSDan Murphy #define DP83869_STRAP_STS1	0x006e
3201db923eSDan Murphy #define DP83869_RGMIIDCTL	0x0086
3349fc2301SDan Murphy #define DP83869_RXFCFG		0x0134
3449fc2301SDan Murphy #define DP83869_RXFPMD1		0x0136
3549fc2301SDan Murphy #define DP83869_RXFPMD2		0x0137
3649fc2301SDan Murphy #define DP83869_RXFPMD3		0x0138
3749fc2301SDan Murphy #define DP83869_RXFSOP1		0x0139
3849fc2301SDan Murphy #define DP83869_RXFSOP2		0x013A
3949fc2301SDan Murphy #define DP83869_RXFSOP3		0x013B
4001db923eSDan Murphy #define DP83869_IO_MUX_CFG	0x0170
4101db923eSDan Murphy #define DP83869_OP_MODE		0x01df
4201db923eSDan Murphy #define DP83869_FX_CTRL		0x0c00
4301db923eSDan Murphy 
4401db923eSDan Murphy #define DP83869_SW_RESET	BIT(15)
4501db923eSDan Murphy #define DP83869_SW_RESTART	BIT(14)
4601db923eSDan Murphy 
4701db923eSDan Murphy /* MICR Interrupt bits */
4801db923eSDan Murphy #define MII_DP83869_MICR_AN_ERR_INT_EN		BIT(15)
4901db923eSDan Murphy #define MII_DP83869_MICR_SPEED_CHNG_INT_EN	BIT(14)
5001db923eSDan Murphy #define MII_DP83869_MICR_DUP_MODE_CHNG_INT_EN	BIT(13)
5101db923eSDan Murphy #define MII_DP83869_MICR_PAGE_RXD_INT_EN	BIT(12)
5201db923eSDan Murphy #define MII_DP83869_MICR_AUTONEG_COMP_INT_EN	BIT(11)
5301db923eSDan Murphy #define MII_DP83869_MICR_LINK_STS_CHNG_INT_EN	BIT(10)
5401db923eSDan Murphy #define MII_DP83869_MICR_FALSE_CARRIER_INT_EN	BIT(8)
5501db923eSDan Murphy #define MII_DP83869_MICR_SLEEP_MODE_CHNG_INT_EN	BIT(4)
5601db923eSDan Murphy #define MII_DP83869_MICR_WOL_INT_EN		BIT(3)
5701db923eSDan Murphy #define MII_DP83869_MICR_XGMII_ERR_INT_EN	BIT(2)
5801db923eSDan Murphy #define MII_DP83869_MICR_POL_CHNG_INT_EN	BIT(1)
5901db923eSDan Murphy #define MII_DP83869_MICR_JABBER_INT_EN		BIT(0)
6001db923eSDan Murphy 
6101db923eSDan Murphy #define MII_DP83869_BMCR_DEFAULT	(BMCR_ANENABLE | \
6201db923eSDan Murphy 					 BMCR_FULLDPLX | \
6301db923eSDan Murphy 					 BMCR_SPEED1000)
6401db923eSDan Murphy 
65a29de52bSDan Murphy #define MII_DP83869_FIBER_ADVERTISE    (ADVERTISED_FIBRE | \
66a29de52bSDan Murphy 					ADVERTISED_Pause | \
67a29de52bSDan Murphy 					ADVERTISED_Asym_Pause)
68a29de52bSDan Murphy 
6901db923eSDan Murphy /* This is the same bit mask as the BMCR so re-use the BMCR default */
7001db923eSDan Murphy #define DP83869_FX_CTRL_DEFAULT	MII_DP83869_BMCR_DEFAULT
7101db923eSDan Murphy 
7201db923eSDan Murphy /* CFG1 bits */
7301db923eSDan Murphy #define DP83869_CFG1_DEFAULT	(ADVERTISE_1000HALF | \
7401db923eSDan Murphy 				 ADVERTISE_1000FULL | \
7501db923eSDan Murphy 				 CTL1000_AS_MASTER)
7601db923eSDan Murphy 
7701db923eSDan Murphy /* RGMIICTL bits */
7801db923eSDan Murphy #define DP83869_RGMII_TX_CLK_DELAY_EN		BIT(1)
7901db923eSDan Murphy #define DP83869_RGMII_RX_CLK_DELAY_EN		BIT(0)
8001db923eSDan Murphy 
81736b25afSDan Murphy /* RGMIIDCTL */
82736b25afSDan Murphy #define DP83869_RGMII_CLK_DELAY_SHIFT		4
83736b25afSDan Murphy #define DP83869_CLK_DELAY_DEF			7
84736b25afSDan Murphy 
8501db923eSDan Murphy /* STRAP_STS1 bits */
860eaf8ccfSDan Murphy #define DP83869_STRAP_OP_MODE_MASK		GENMASK(2, 0)
8701db923eSDan Murphy #define DP83869_STRAP_STS1_RESERVED		BIT(11)
88c4566aecSDan Murphy #define DP83869_STRAP_MIRROR_ENABLED           BIT(12)
8901db923eSDan Murphy 
9001db923eSDan Murphy /* PHYCTRL bits */
9101db923eSDan Murphy #define DP83869_RX_FIFO_SHIFT	12
9201db923eSDan Murphy #define DP83869_TX_FIFO_SHIFT	14
9301db923eSDan Murphy 
9401db923eSDan Murphy /* PHY_CTRL lower bytes 0x48 are declared as reserved */
9501db923eSDan Murphy #define DP83869_PHY_CTRL_DEFAULT	0x48
9601db923eSDan Murphy #define DP83869_PHYCR_FIFO_DEPTH_MASK	GENMASK(15, 12)
9701db923eSDan Murphy #define DP83869_PHYCR_RESERVED_MASK	BIT(11)
9801db923eSDan Murphy 
9901db923eSDan Murphy /* IO_MUX_CFG bits */
10001db923eSDan Murphy #define DP83869_IO_MUX_CFG_IO_IMPEDANCE_CTRL	0x1f
10101db923eSDan Murphy 
10201db923eSDan Murphy #define DP83869_IO_MUX_CFG_IO_IMPEDANCE_MAX	0x0
10301db923eSDan Murphy #define DP83869_IO_MUX_CFG_IO_IMPEDANCE_MIN	0x1f
10401db923eSDan Murphy #define DP83869_IO_MUX_CFG_CLK_O_SEL_MASK	(0x1f << 8)
10501db923eSDan Murphy #define DP83869_IO_MUX_CFG_CLK_O_SEL_SHIFT	8
10601db923eSDan Murphy 
10701db923eSDan Murphy /* CFG3 bits */
10801db923eSDan Murphy #define DP83869_CFG3_PORT_MIRROR_EN              BIT(0)
10901db923eSDan Murphy 
11001db923eSDan Murphy /* CFG4 bits */
11101db923eSDan Murphy #define DP83869_INT_OE	BIT(7)
11201db923eSDan Murphy 
11301db923eSDan Murphy /* OP MODE */
11401db923eSDan Murphy #define DP83869_OP_MODE_MII			BIT(5)
11501db923eSDan Murphy #define DP83869_SGMII_RGMII_BRIDGE		BIT(6)
11601db923eSDan Murphy 
11749fc2301SDan Murphy /* RXFCFG bits*/
11849fc2301SDan Murphy #define DP83869_WOL_MAGIC_EN		BIT(0)
11949fc2301SDan Murphy #define DP83869_WOL_PATTERN_EN		BIT(1)
12049fc2301SDan Murphy #define DP83869_WOL_BCAST_EN		BIT(2)
12149fc2301SDan Murphy #define DP83869_WOL_UCAST_EN		BIT(4)
12249fc2301SDan Murphy #define DP83869_WOL_SEC_EN		BIT(5)
12349fc2301SDan Murphy #define DP83869_WOL_ENH_MAC		BIT(7)
12449fc2301SDan Murphy 
125811ac400SDan Murphy /* CFG2 bits */
126811ac400SDan Murphy #define DP83869_DOWNSHIFT_EN		(BIT(8) | BIT(9))
127811ac400SDan Murphy #define DP83869_DOWNSHIFT_ATTEMPT_MASK	(BIT(10) | BIT(11))
128811ac400SDan Murphy #define DP83869_DOWNSHIFT_1_COUNT_VAL	0
129811ac400SDan Murphy #define DP83869_DOWNSHIFT_2_COUNT_VAL	1
130811ac400SDan Murphy #define DP83869_DOWNSHIFT_4_COUNT_VAL	2
131811ac400SDan Murphy #define DP83869_DOWNSHIFT_8_COUNT_VAL	3
132811ac400SDan Murphy #define DP83869_DOWNSHIFT_1_COUNT	1
133811ac400SDan Murphy #define DP83869_DOWNSHIFT_2_COUNT	2
134811ac400SDan Murphy #define DP83869_DOWNSHIFT_4_COUNT	4
135811ac400SDan Murphy #define DP83869_DOWNSHIFT_8_COUNT	8
136811ac400SDan Murphy 
13701db923eSDan Murphy enum {
13801db923eSDan Murphy 	DP83869_PORT_MIRRORING_KEEP,
13901db923eSDan Murphy 	DP83869_PORT_MIRRORING_EN,
14001db923eSDan Murphy 	DP83869_PORT_MIRRORING_DIS,
14101db923eSDan Murphy };
14201db923eSDan Murphy 
14301db923eSDan Murphy struct dp83869_private {
14401db923eSDan Murphy 	int tx_fifo_depth;
14501db923eSDan Murphy 	int rx_fifo_depth;
146736b25afSDan Murphy 	s32 rx_int_delay;
147736b25afSDan Murphy 	s32 tx_int_delay;
14801db923eSDan Murphy 	int io_impedance;
14901db923eSDan Murphy 	int port_mirroring;
15001db923eSDan Murphy 	bool rxctrl_strap_quirk;
15101db923eSDan Murphy 	int clk_output_sel;
15201db923eSDan Murphy 	int mode;
15301db923eSDan Murphy };
15401db923eSDan Murphy 
155a29de52bSDan Murphy static int dp83869_read_status(struct phy_device *phydev)
156a29de52bSDan Murphy {
157a29de52bSDan Murphy 	struct dp83869_private *dp83869 = phydev->priv;
158a29de52bSDan Murphy 	int ret;
159a29de52bSDan Murphy 
160a29de52bSDan Murphy 	ret = genphy_read_status(phydev);
161a29de52bSDan Murphy 	if (ret)
162a29de52bSDan Murphy 		return ret;
163a29de52bSDan Murphy 
164a29de52bSDan Murphy 	if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported)) {
165a29de52bSDan Murphy 		if (phydev->link) {
166a29de52bSDan Murphy 			if (dp83869->mode == DP83869_RGMII_100_BASE)
167a29de52bSDan Murphy 				phydev->speed = SPEED_100;
168a29de52bSDan Murphy 		} else {
169a29de52bSDan Murphy 			phydev->speed = SPEED_UNKNOWN;
170a29de52bSDan Murphy 			phydev->duplex = DUPLEX_UNKNOWN;
171a29de52bSDan Murphy 		}
172a29de52bSDan Murphy 	}
173a29de52bSDan Murphy 
174a29de52bSDan Murphy 	return 0;
175a29de52bSDan Murphy }
176a29de52bSDan Murphy 
17701db923eSDan Murphy static int dp83869_ack_interrupt(struct phy_device *phydev)
17801db923eSDan Murphy {
17901db923eSDan Murphy 	int err = phy_read(phydev, MII_DP83869_ISR);
18001db923eSDan Murphy 
18101db923eSDan Murphy 	if (err < 0)
18201db923eSDan Murphy 		return err;
18301db923eSDan Murphy 
18401db923eSDan Murphy 	return 0;
18501db923eSDan Murphy }
18601db923eSDan Murphy 
18701db923eSDan Murphy static int dp83869_config_intr(struct phy_device *phydev)
18801db923eSDan Murphy {
189aa2d603aSIoana Ciornei 	int micr_status = 0, err;
19001db923eSDan Murphy 
19101db923eSDan Murphy 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
192aa2d603aSIoana Ciornei 		err = dp83869_ack_interrupt(phydev);
193aa2d603aSIoana Ciornei 		if (err)
194aa2d603aSIoana Ciornei 			return err;
195aa2d603aSIoana Ciornei 
19601db923eSDan Murphy 		micr_status = phy_read(phydev, MII_DP83869_MICR);
19701db923eSDan Murphy 		if (micr_status < 0)
19801db923eSDan Murphy 			return micr_status;
19901db923eSDan Murphy 
20001db923eSDan Murphy 		micr_status |=
20101db923eSDan Murphy 			(MII_DP83869_MICR_AN_ERR_INT_EN |
20201db923eSDan Murphy 			MII_DP83869_MICR_SPEED_CHNG_INT_EN |
20301db923eSDan Murphy 			MII_DP83869_MICR_AUTONEG_COMP_INT_EN |
20401db923eSDan Murphy 			MII_DP83869_MICR_LINK_STS_CHNG_INT_EN |
20501db923eSDan Murphy 			MII_DP83869_MICR_DUP_MODE_CHNG_INT_EN |
20601db923eSDan Murphy 			MII_DP83869_MICR_SLEEP_MODE_CHNG_INT_EN);
20701db923eSDan Murphy 
208aa2d603aSIoana Ciornei 		err = phy_write(phydev, MII_DP83869_MICR, micr_status);
209aa2d603aSIoana Ciornei 	} else {
210aa2d603aSIoana Ciornei 		err = phy_write(phydev, MII_DP83869_MICR, micr_status);
211aa2d603aSIoana Ciornei 		if (err)
212aa2d603aSIoana Ciornei 			return err;
213aa2d603aSIoana Ciornei 
214aa2d603aSIoana Ciornei 		err = dp83869_ack_interrupt(phydev);
21501db923eSDan Murphy 	}
21601db923eSDan Murphy 
217aa2d603aSIoana Ciornei 	return err;
21801db923eSDan Murphy }
21901db923eSDan Murphy 
2201d1ae3c6SIoana Ciornei static irqreturn_t dp83869_handle_interrupt(struct phy_device *phydev)
2211d1ae3c6SIoana Ciornei {
2221d1ae3c6SIoana Ciornei 	int irq_status, irq_enabled;
2231d1ae3c6SIoana Ciornei 
2241d1ae3c6SIoana Ciornei 	irq_status = phy_read(phydev, MII_DP83869_ISR);
2251d1ae3c6SIoana Ciornei 	if (irq_status < 0) {
2261d1ae3c6SIoana Ciornei 		phy_error(phydev);
2271d1ae3c6SIoana Ciornei 		return IRQ_NONE;
2281d1ae3c6SIoana Ciornei 	}
2291d1ae3c6SIoana Ciornei 
2301d1ae3c6SIoana Ciornei 	irq_enabled = phy_read(phydev, MII_DP83869_MICR);
2311d1ae3c6SIoana Ciornei 	if (irq_enabled < 0) {
2321d1ae3c6SIoana Ciornei 		phy_error(phydev);
2331d1ae3c6SIoana Ciornei 		return IRQ_NONE;
2341d1ae3c6SIoana Ciornei 	}
2351d1ae3c6SIoana Ciornei 
2361d1ae3c6SIoana Ciornei 	if (!(irq_status & irq_enabled))
2371d1ae3c6SIoana Ciornei 		return IRQ_NONE;
2381d1ae3c6SIoana Ciornei 
2391d1ae3c6SIoana Ciornei 	phy_trigger_machine(phydev);
2401d1ae3c6SIoana Ciornei 
2411d1ae3c6SIoana Ciornei 	return IRQ_HANDLED;
2421d1ae3c6SIoana Ciornei }
2431d1ae3c6SIoana Ciornei 
24449fc2301SDan Murphy static int dp83869_set_wol(struct phy_device *phydev,
24549fc2301SDan Murphy 			   struct ethtool_wolinfo *wol)
24649fc2301SDan Murphy {
24749fc2301SDan Murphy 	struct net_device *ndev = phydev->attached_dev;
24849fc2301SDan Murphy 	int val_rxcfg, val_micr;
24949fc2301SDan Murphy 	u8 *mac;
25049fc2301SDan Murphy 	int ret;
25149fc2301SDan Murphy 
25249fc2301SDan Murphy 	val_rxcfg = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_RXFCFG);
25349fc2301SDan Murphy 	if (val_rxcfg < 0)
25449fc2301SDan Murphy 		return val_rxcfg;
25549fc2301SDan Murphy 
25649fc2301SDan Murphy 	val_micr = phy_read(phydev, MII_DP83869_MICR);
25749fc2301SDan Murphy 	if (val_micr < 0)
25849fc2301SDan Murphy 		return val_micr;
25949fc2301SDan Murphy 
26049fc2301SDan Murphy 	if (wol->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_UCAST |
26149fc2301SDan Murphy 			    WAKE_BCAST)) {
26249fc2301SDan Murphy 		val_rxcfg |= DP83869_WOL_ENH_MAC;
26349fc2301SDan Murphy 		val_micr |= MII_DP83869_MICR_WOL_INT_EN;
26449fc2301SDan Murphy 
26549fc2301SDan Murphy 		if (wol->wolopts & WAKE_MAGIC ||
26649fc2301SDan Murphy 		    wol->wolopts & WAKE_MAGICSECURE) {
26749fc2301SDan Murphy 			mac = (u8 *)ndev->dev_addr;
26849fc2301SDan Murphy 
26949fc2301SDan Murphy 			if (!is_valid_ether_addr(mac))
27049fc2301SDan Murphy 				return -EINVAL;
27149fc2301SDan Murphy 
27249fc2301SDan Murphy 			ret = phy_write_mmd(phydev, DP83869_DEVADDR,
27349fc2301SDan Murphy 					    DP83869_RXFPMD1,
27449fc2301SDan Murphy 					    mac[1] << 8 | mac[0]);
27549fc2301SDan Murphy 			if (ret)
27649fc2301SDan Murphy 				return ret;
27749fc2301SDan Murphy 
27849fc2301SDan Murphy 			ret = phy_write_mmd(phydev, DP83869_DEVADDR,
27949fc2301SDan Murphy 					    DP83869_RXFPMD2,
28049fc2301SDan Murphy 					    mac[3] << 8 | mac[2]);
28149fc2301SDan Murphy 			if (ret)
28249fc2301SDan Murphy 				return ret;
28349fc2301SDan Murphy 
28449fc2301SDan Murphy 			ret = phy_write_mmd(phydev, DP83869_DEVADDR,
28549fc2301SDan Murphy 					    DP83869_RXFPMD3,
28649fc2301SDan Murphy 					    mac[5] << 8 | mac[4]);
28749fc2301SDan Murphy 			if (ret)
28849fc2301SDan Murphy 				return ret;
28949fc2301SDan Murphy 
29049fc2301SDan Murphy 			val_rxcfg |= DP83869_WOL_MAGIC_EN;
29149fc2301SDan Murphy 		} else {
29249fc2301SDan Murphy 			val_rxcfg &= ~DP83869_WOL_MAGIC_EN;
29349fc2301SDan Murphy 		}
29449fc2301SDan Murphy 
29549fc2301SDan Murphy 		if (wol->wolopts & WAKE_MAGICSECURE) {
29649fc2301SDan Murphy 			ret = phy_write_mmd(phydev, DP83869_DEVADDR,
29749fc2301SDan Murphy 					    DP83869_RXFSOP1,
29849fc2301SDan Murphy 					    (wol->sopass[1] << 8) | wol->sopass[0]);
29949fc2301SDan Murphy 			if (ret)
30049fc2301SDan Murphy 				return ret;
30149fc2301SDan Murphy 
30249fc2301SDan Murphy 			ret = phy_write_mmd(phydev, DP83869_DEVADDR,
30349fc2301SDan Murphy 					    DP83869_RXFSOP2,
30449fc2301SDan Murphy 					    (wol->sopass[3] << 8) | wol->sopass[2]);
30549fc2301SDan Murphy 			if (ret)
30649fc2301SDan Murphy 				return ret;
30749fc2301SDan Murphy 			ret = phy_write_mmd(phydev, DP83869_DEVADDR,
30849fc2301SDan Murphy 					    DP83869_RXFSOP3,
30949fc2301SDan Murphy 					    (wol->sopass[5] << 8) | wol->sopass[4]);
31049fc2301SDan Murphy 			if (ret)
31149fc2301SDan Murphy 				return ret;
31249fc2301SDan Murphy 
31349fc2301SDan Murphy 			val_rxcfg |= DP83869_WOL_SEC_EN;
31449fc2301SDan Murphy 		} else {
31549fc2301SDan Murphy 			val_rxcfg &= ~DP83869_WOL_SEC_EN;
31649fc2301SDan Murphy 		}
31749fc2301SDan Murphy 
31849fc2301SDan Murphy 		if (wol->wolopts & WAKE_UCAST)
31949fc2301SDan Murphy 			val_rxcfg |= DP83869_WOL_UCAST_EN;
32049fc2301SDan Murphy 		else
32149fc2301SDan Murphy 			val_rxcfg &= ~DP83869_WOL_UCAST_EN;
32249fc2301SDan Murphy 
32349fc2301SDan Murphy 		if (wol->wolopts & WAKE_BCAST)
32449fc2301SDan Murphy 			val_rxcfg |= DP83869_WOL_BCAST_EN;
32549fc2301SDan Murphy 		else
32649fc2301SDan Murphy 			val_rxcfg &= ~DP83869_WOL_BCAST_EN;
32749fc2301SDan Murphy 	} else {
32849fc2301SDan Murphy 		val_rxcfg &= ~DP83869_WOL_ENH_MAC;
32949fc2301SDan Murphy 		val_micr &= ~MII_DP83869_MICR_WOL_INT_EN;
33049fc2301SDan Murphy 	}
33149fc2301SDan Murphy 
33249fc2301SDan Murphy 	ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_RXFCFG, val_rxcfg);
33349fc2301SDan Murphy 	if (ret)
33449fc2301SDan Murphy 		return ret;
33549fc2301SDan Murphy 
33649fc2301SDan Murphy 	return phy_write(phydev, MII_DP83869_MICR, val_micr);
33749fc2301SDan Murphy }
33849fc2301SDan Murphy 
33949fc2301SDan Murphy static void dp83869_get_wol(struct phy_device *phydev,
34049fc2301SDan Murphy 			    struct ethtool_wolinfo *wol)
34149fc2301SDan Murphy {
342e275d49aSColin Ian King 	int value, sopass_val;
34349fc2301SDan Murphy 
34449fc2301SDan Murphy 	wol->supported = (WAKE_UCAST | WAKE_BCAST | WAKE_MAGIC |
34549fc2301SDan Murphy 			WAKE_MAGICSECURE);
34649fc2301SDan Murphy 	wol->wolopts = 0;
34749fc2301SDan Murphy 
34849fc2301SDan Murphy 	value = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_RXFCFG);
34949fc2301SDan Murphy 	if (value < 0) {
35049fc2301SDan Murphy 		phydev_err(phydev, "Failed to read RX CFG\n");
35149fc2301SDan Murphy 		return;
35249fc2301SDan Murphy 	}
35349fc2301SDan Murphy 
35449fc2301SDan Murphy 	if (value & DP83869_WOL_UCAST_EN)
35549fc2301SDan Murphy 		wol->wolopts |= WAKE_UCAST;
35649fc2301SDan Murphy 
35749fc2301SDan Murphy 	if (value & DP83869_WOL_BCAST_EN)
35849fc2301SDan Murphy 		wol->wolopts |= WAKE_BCAST;
35949fc2301SDan Murphy 
36049fc2301SDan Murphy 	if (value & DP83869_WOL_MAGIC_EN)
36149fc2301SDan Murphy 		wol->wolopts |= WAKE_MAGIC;
36249fc2301SDan Murphy 
36349fc2301SDan Murphy 	if (value & DP83869_WOL_SEC_EN) {
36449fc2301SDan Murphy 		sopass_val = phy_read_mmd(phydev, DP83869_DEVADDR,
36549fc2301SDan Murphy 					  DP83869_RXFSOP1);
36649fc2301SDan Murphy 		if (sopass_val < 0) {
36749fc2301SDan Murphy 			phydev_err(phydev, "Failed to read RX SOP 1\n");
36849fc2301SDan Murphy 			return;
36949fc2301SDan Murphy 		}
37049fc2301SDan Murphy 
37149fc2301SDan Murphy 		wol->sopass[0] = (sopass_val & 0xff);
37249fc2301SDan Murphy 		wol->sopass[1] = (sopass_val >> 8);
37349fc2301SDan Murphy 
37449fc2301SDan Murphy 		sopass_val = phy_read_mmd(phydev, DP83869_DEVADDR,
37549fc2301SDan Murphy 					  DP83869_RXFSOP2);
37649fc2301SDan Murphy 		if (sopass_val < 0) {
37749fc2301SDan Murphy 			phydev_err(phydev, "Failed to read RX SOP 2\n");
37849fc2301SDan Murphy 			return;
37949fc2301SDan Murphy 		}
38049fc2301SDan Murphy 
38149fc2301SDan Murphy 		wol->sopass[2] = (sopass_val & 0xff);
38249fc2301SDan Murphy 		wol->sopass[3] = (sopass_val >> 8);
38349fc2301SDan Murphy 
38449fc2301SDan Murphy 		sopass_val = phy_read_mmd(phydev, DP83869_DEVADDR,
38549fc2301SDan Murphy 					  DP83869_RXFSOP3);
38649fc2301SDan Murphy 		if (sopass_val < 0) {
38749fc2301SDan Murphy 			phydev_err(phydev, "Failed to read RX SOP 3\n");
38849fc2301SDan Murphy 			return;
38949fc2301SDan Murphy 		}
39049fc2301SDan Murphy 
39149fc2301SDan Murphy 		wol->sopass[4] = (sopass_val & 0xff);
39249fc2301SDan Murphy 		wol->sopass[5] = (sopass_val >> 8);
39349fc2301SDan Murphy 
39449fc2301SDan Murphy 		wol->wolopts |= WAKE_MAGICSECURE;
39549fc2301SDan Murphy 	}
39649fc2301SDan Murphy 
39749fc2301SDan Murphy 	if (!(value & DP83869_WOL_ENH_MAC))
39849fc2301SDan Murphy 		wol->wolopts = 0;
39949fc2301SDan Murphy }
40049fc2301SDan Murphy 
401811ac400SDan Murphy static int dp83869_get_downshift(struct phy_device *phydev, u8 *data)
402811ac400SDan Murphy {
403811ac400SDan Murphy 	int val, cnt, enable, count;
404811ac400SDan Murphy 
405811ac400SDan Murphy 	val = phy_read(phydev, DP83869_CFG2);
406811ac400SDan Murphy 	if (val < 0)
407811ac400SDan Murphy 		return val;
408811ac400SDan Murphy 
409811ac400SDan Murphy 	enable = FIELD_GET(DP83869_DOWNSHIFT_EN, val);
410811ac400SDan Murphy 	cnt = FIELD_GET(DP83869_DOWNSHIFT_ATTEMPT_MASK, val);
411811ac400SDan Murphy 
412811ac400SDan Murphy 	switch (cnt) {
413811ac400SDan Murphy 	case DP83869_DOWNSHIFT_1_COUNT_VAL:
414811ac400SDan Murphy 		count = DP83869_DOWNSHIFT_1_COUNT;
415811ac400SDan Murphy 		break;
416811ac400SDan Murphy 	case DP83869_DOWNSHIFT_2_COUNT_VAL:
417811ac400SDan Murphy 		count = DP83869_DOWNSHIFT_2_COUNT;
418811ac400SDan Murphy 		break;
419811ac400SDan Murphy 	case DP83869_DOWNSHIFT_4_COUNT_VAL:
420811ac400SDan Murphy 		count = DP83869_DOWNSHIFT_4_COUNT;
421811ac400SDan Murphy 		break;
422811ac400SDan Murphy 	case DP83869_DOWNSHIFT_8_COUNT_VAL:
423811ac400SDan Murphy 		count = DP83869_DOWNSHIFT_8_COUNT;
424811ac400SDan Murphy 		break;
425811ac400SDan Murphy 	default:
426811ac400SDan Murphy 		return -EINVAL;
427811ac400SDan Murphy 	}
428811ac400SDan Murphy 
429811ac400SDan Murphy 	*data = enable ? count : DOWNSHIFT_DEV_DISABLE;
430811ac400SDan Murphy 
431811ac400SDan Murphy 	return 0;
432811ac400SDan Murphy }
433811ac400SDan Murphy 
434811ac400SDan Murphy static int dp83869_set_downshift(struct phy_device *phydev, u8 cnt)
435811ac400SDan Murphy {
436811ac400SDan Murphy 	int val, count;
437811ac400SDan Murphy 
438811ac400SDan Murphy 	if (cnt > DP83869_DOWNSHIFT_8_COUNT)
439811ac400SDan Murphy 		return -EINVAL;
440811ac400SDan Murphy 
441811ac400SDan Murphy 	if (!cnt)
442811ac400SDan Murphy 		return phy_clear_bits(phydev, DP83869_CFG2,
443811ac400SDan Murphy 				      DP83869_DOWNSHIFT_EN);
444811ac400SDan Murphy 
445811ac400SDan Murphy 	switch (cnt) {
446811ac400SDan Murphy 	case DP83869_DOWNSHIFT_1_COUNT:
447811ac400SDan Murphy 		count = DP83869_DOWNSHIFT_1_COUNT_VAL;
448811ac400SDan Murphy 		break;
449811ac400SDan Murphy 	case DP83869_DOWNSHIFT_2_COUNT:
450811ac400SDan Murphy 		count = DP83869_DOWNSHIFT_2_COUNT_VAL;
451811ac400SDan Murphy 		break;
452811ac400SDan Murphy 	case DP83869_DOWNSHIFT_4_COUNT:
453811ac400SDan Murphy 		count = DP83869_DOWNSHIFT_4_COUNT_VAL;
454811ac400SDan Murphy 		break;
455811ac400SDan Murphy 	case DP83869_DOWNSHIFT_8_COUNT:
456811ac400SDan Murphy 		count = DP83869_DOWNSHIFT_8_COUNT_VAL;
457811ac400SDan Murphy 		break;
458811ac400SDan Murphy 	default:
459811ac400SDan Murphy 		phydev_err(phydev,
460811ac400SDan Murphy 			   "Downshift count must be 1, 2, 4 or 8\n");
461811ac400SDan Murphy 		return -EINVAL;
462811ac400SDan Murphy 	}
463811ac400SDan Murphy 
464811ac400SDan Murphy 	val = DP83869_DOWNSHIFT_EN;
465811ac400SDan Murphy 	val |= FIELD_PREP(DP83869_DOWNSHIFT_ATTEMPT_MASK, count);
466811ac400SDan Murphy 
467811ac400SDan Murphy 	return phy_modify(phydev, DP83869_CFG2,
468811ac400SDan Murphy 			  DP83869_DOWNSHIFT_EN | DP83869_DOWNSHIFT_ATTEMPT_MASK,
469811ac400SDan Murphy 			  val);
470811ac400SDan Murphy }
471811ac400SDan Murphy 
472811ac400SDan Murphy static int dp83869_get_tunable(struct phy_device *phydev,
473811ac400SDan Murphy 			       struct ethtool_tunable *tuna, void *data)
474811ac400SDan Murphy {
475811ac400SDan Murphy 	switch (tuna->id) {
476811ac400SDan Murphy 	case ETHTOOL_PHY_DOWNSHIFT:
477811ac400SDan Murphy 		return dp83869_get_downshift(phydev, data);
478811ac400SDan Murphy 	default:
479811ac400SDan Murphy 		return -EOPNOTSUPP;
480811ac400SDan Murphy 	}
481811ac400SDan Murphy }
482811ac400SDan Murphy 
483811ac400SDan Murphy static int dp83869_set_tunable(struct phy_device *phydev,
484811ac400SDan Murphy 			       struct ethtool_tunable *tuna, const void *data)
485811ac400SDan Murphy {
486811ac400SDan Murphy 	switch (tuna->id) {
487811ac400SDan Murphy 	case ETHTOOL_PHY_DOWNSHIFT:
488811ac400SDan Murphy 		return dp83869_set_downshift(phydev, *(const u8 *)data);
489811ac400SDan Murphy 	default:
490811ac400SDan Murphy 		return -EOPNOTSUPP;
491811ac400SDan Murphy 	}
492811ac400SDan Murphy }
493811ac400SDan Murphy 
49401db923eSDan Murphy static int dp83869_config_port_mirroring(struct phy_device *phydev)
49501db923eSDan Murphy {
49601db923eSDan Murphy 	struct dp83869_private *dp83869 = phydev->priv;
49701db923eSDan Murphy 
49801db923eSDan Murphy 	if (dp83869->port_mirroring == DP83869_PORT_MIRRORING_EN)
499786c4a53SDan Murphy 		return phy_set_bits_mmd(phydev, DP83869_DEVADDR,
500786c4a53SDan Murphy 					DP83869_GEN_CFG3,
50101db923eSDan Murphy 					DP83869_CFG3_PORT_MIRROR_EN);
50201db923eSDan Murphy 	else
503786c4a53SDan Murphy 		return phy_clear_bits_mmd(phydev, DP83869_DEVADDR,
504786c4a53SDan Murphy 					  DP83869_GEN_CFG3,
50501db923eSDan Murphy 					  DP83869_CFG3_PORT_MIRROR_EN);
50601db923eSDan Murphy }
50701db923eSDan Murphy 
5080eaf8ccfSDan Murphy static int dp83869_set_strapped_mode(struct phy_device *phydev)
5090eaf8ccfSDan Murphy {
5100eaf8ccfSDan Murphy 	struct dp83869_private *dp83869 = phydev->priv;
5110eaf8ccfSDan Murphy 	int val;
5120eaf8ccfSDan Murphy 
5130eaf8ccfSDan Murphy 	val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_STRAP_STS1);
5140eaf8ccfSDan Murphy 	if (val < 0)
5150eaf8ccfSDan Murphy 		return val;
5160eaf8ccfSDan Murphy 
5170eaf8ccfSDan Murphy 	dp83869->mode = val & DP83869_STRAP_OP_MODE_MASK;
5180eaf8ccfSDan Murphy 
5190eaf8ccfSDan Murphy 	return 0;
5200eaf8ccfSDan Murphy }
5210eaf8ccfSDan Murphy 
522e9293c98SDan Murphy #if IS_ENABLED(CONFIG_OF_MDIO)
523736b25afSDan Murphy static const int dp83869_internal_delay[] = {250, 500, 750, 1000, 1250, 1500,
524736b25afSDan Murphy 					     1750, 2000, 2250, 2500, 2750, 3000,
525736b25afSDan Murphy 					     3250, 3500, 3750, 4000};
526736b25afSDan Murphy 
52701db923eSDan Murphy static int dp83869_of_init(struct phy_device *phydev)
52801db923eSDan Murphy {
52901db923eSDan Murphy 	struct dp83869_private *dp83869 = phydev->priv;
53001db923eSDan Murphy 	struct device *dev = &phydev->mdio.dev;
53101db923eSDan Murphy 	struct device_node *of_node = dev->of_node;
532736b25afSDan Murphy 	int delay_size = ARRAY_SIZE(dp83869_internal_delay);
53301db923eSDan Murphy 	int ret;
53401db923eSDan Murphy 
53501db923eSDan Murphy 	if (!of_node)
53601db923eSDan Murphy 		return -ENODEV;
53701db923eSDan Murphy 
53801db923eSDan Murphy 	dp83869->io_impedance = -EINVAL;
53901db923eSDan Murphy 
54001db923eSDan Murphy 	/* Optional configuration */
54101db923eSDan Murphy 	ret = of_property_read_u32(of_node, "ti,clk-output-sel",
54201db923eSDan Murphy 				   &dp83869->clk_output_sel);
54301db923eSDan Murphy 	if (ret || dp83869->clk_output_sel > DP83869_CLK_O_SEL_REF_CLK)
54401db923eSDan Murphy 		dp83869->clk_output_sel = DP83869_CLK_O_SEL_REF_CLK;
54501db923eSDan Murphy 
54601db923eSDan Murphy 	ret = of_property_read_u32(of_node, "ti,op-mode", &dp83869->mode);
54701db923eSDan Murphy 	if (ret == 0) {
54801db923eSDan Murphy 		if (dp83869->mode < DP83869_RGMII_COPPER_ETHERNET ||
54901db923eSDan Murphy 		    dp83869->mode > DP83869_SGMII_COPPER_ETHERNET)
55001db923eSDan Murphy 			return -EINVAL;
5510eaf8ccfSDan Murphy 	} else {
5520eaf8ccfSDan Murphy 		ret = dp83869_set_strapped_mode(phydev);
5530eaf8ccfSDan Murphy 		if (ret)
5540eaf8ccfSDan Murphy 			return ret;
55501db923eSDan Murphy 	}
55601db923eSDan Murphy 
55701db923eSDan Murphy 	if (of_property_read_bool(of_node, "ti,max-output-impedance"))
55801db923eSDan Murphy 		dp83869->io_impedance = DP83869_IO_MUX_CFG_IO_IMPEDANCE_MAX;
55901db923eSDan Murphy 	else if (of_property_read_bool(of_node, "ti,min-output-impedance"))
56001db923eSDan Murphy 		dp83869->io_impedance = DP83869_IO_MUX_CFG_IO_IMPEDANCE_MIN;
56101db923eSDan Murphy 
562c4566aecSDan Murphy 	if (of_property_read_bool(of_node, "enet-phy-lane-swap")) {
563c4566aecSDan Murphy 		dp83869->port_mirroring = DP83869_PORT_MIRRORING_EN;
564c4566aecSDan Murphy 	} else {
565c4566aecSDan Murphy 		/* If the lane swap is not in the DT then check the straps */
566c4566aecSDan Murphy 		ret = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_STRAP_STS1);
567c4566aecSDan Murphy 		if (ret < 0)
568c4566aecSDan Murphy 			return ret;
5694e2905adSDan Murphy 
570c4566aecSDan Murphy 		if (ret & DP83869_STRAP_MIRROR_ENABLED)
57101db923eSDan Murphy 			dp83869->port_mirroring = DP83869_PORT_MIRRORING_EN;
57201db923eSDan Murphy 		else
57301db923eSDan Murphy 			dp83869->port_mirroring = DP83869_PORT_MIRRORING_DIS;
5744e2905adSDan Murphy 
5754e2905adSDan Murphy 		ret = 0;
576c4566aecSDan Murphy 	}
57701db923eSDan Murphy 
57801db923eSDan Murphy 	if (of_property_read_u32(of_node, "rx-fifo-depth",
57901db923eSDan Murphy 				 &dp83869->rx_fifo_depth))
58001db923eSDan Murphy 		dp83869->rx_fifo_depth = DP83869_PHYCR_FIFO_DEPTH_4_B_NIB;
58101db923eSDan Murphy 
58201db923eSDan Murphy 	if (of_property_read_u32(of_node, "tx-fifo-depth",
58301db923eSDan Murphy 				 &dp83869->tx_fifo_depth))
58401db923eSDan Murphy 		dp83869->tx_fifo_depth = DP83869_PHYCR_FIFO_DEPTH_4_B_NIB;
58501db923eSDan Murphy 
586736b25afSDan Murphy 	dp83869->rx_int_delay = phy_get_internal_delay(phydev, dev,
587736b25afSDan Murphy 						       &dp83869_internal_delay[0],
588736b25afSDan Murphy 						       delay_size, true);
589736b25afSDan Murphy 	if (dp83869->rx_int_delay < 0)
590736b25afSDan Murphy 		dp83869->rx_int_delay =
591736b25afSDan Murphy 				dp83869_internal_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)
597736b25afSDan Murphy 		dp83869->tx_int_delay =
598736b25afSDan Murphy 				dp83869_internal_delay[DP83869_CLK_DELAY_DEF];
599736b25afSDan Murphy 
600786c4a53SDan Murphy 	return ret;
60101db923eSDan Murphy }
60201db923eSDan Murphy #else
60301db923eSDan Murphy static int dp83869_of_init(struct phy_device *phydev)
60401db923eSDan Murphy {
6050eaf8ccfSDan Murphy 	return dp83869_set_strapped_mode(phydev);
60601db923eSDan Murphy }
60701db923eSDan Murphy #endif /* CONFIG_OF_MDIO */
60801db923eSDan Murphy 
60901db923eSDan Murphy static int dp83869_configure_rgmii(struct phy_device *phydev,
61001db923eSDan Murphy 				   struct dp83869_private *dp83869)
61101db923eSDan Murphy {
612786c4a53SDan Murphy 	int ret = 0, val;
61301db923eSDan Murphy 
61401db923eSDan Murphy 	if (phy_interface_is_rgmii(phydev)) {
61501db923eSDan Murphy 		val = phy_read(phydev, MII_DP83869_PHYCTRL);
61601db923eSDan Murphy 		if (val < 0)
61701db923eSDan Murphy 			return val;
61801db923eSDan Murphy 
61901db923eSDan Murphy 		val &= ~DP83869_PHYCR_FIFO_DEPTH_MASK;
62001db923eSDan Murphy 		val |= (dp83869->tx_fifo_depth << DP83869_TX_FIFO_SHIFT);
62101db923eSDan Murphy 		val |= (dp83869->rx_fifo_depth << DP83869_RX_FIFO_SHIFT);
62201db923eSDan Murphy 
62301db923eSDan Murphy 		ret = phy_write(phydev, MII_DP83869_PHYCTRL, val);
62401db923eSDan Murphy 		if (ret)
62501db923eSDan Murphy 			return ret;
62601db923eSDan Murphy 	}
62701db923eSDan Murphy 
62801db923eSDan Murphy 	if (dp83869->io_impedance >= 0)
629786c4a53SDan Murphy 		ret = phy_modify_mmd(phydev, DP83869_DEVADDR,
63001db923eSDan Murphy 				     DP83869_IO_MUX_CFG,
63101db923eSDan Murphy 				     DP83869_IO_MUX_CFG_IO_IMPEDANCE_CTRL,
63201db923eSDan Murphy 				     dp83869->io_impedance &
63301db923eSDan Murphy 				     DP83869_IO_MUX_CFG_IO_IMPEDANCE_CTRL);
63401db923eSDan Murphy 
635786c4a53SDan Murphy 	return ret;
63601db923eSDan Murphy }
63701db923eSDan Murphy 
638a29de52bSDan Murphy static int dp83869_configure_fiber(struct phy_device *phydev,
639a29de52bSDan Murphy 				   struct dp83869_private *dp83869)
640a29de52bSDan Murphy {
641a29de52bSDan Murphy 	int bmcr;
642a29de52bSDan Murphy 	int ret;
643a29de52bSDan Murphy 
644a29de52bSDan Murphy 	/* Only allow advertising what this PHY supports */
645a29de52bSDan Murphy 	linkmode_and(phydev->advertising, phydev->advertising,
646a29de52bSDan Murphy 		     phydev->supported);
647a29de52bSDan Murphy 
648a29de52bSDan Murphy 	linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported);
649a29de52bSDan Murphy 	linkmode_set_bit(ADVERTISED_FIBRE, phydev->advertising);
650a29de52bSDan Murphy 
651a29de52bSDan Murphy 	if (dp83869->mode == DP83869_RGMII_1000_BASE) {
652a29de52bSDan Murphy 		linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
653a29de52bSDan Murphy 				 phydev->supported);
654a29de52bSDan Murphy 	} else {
655a29de52bSDan Murphy 		linkmode_set_bit(ETHTOOL_LINK_MODE_100baseFX_Full_BIT,
656a29de52bSDan Murphy 				 phydev->supported);
657a29de52bSDan Murphy 		linkmode_set_bit(ETHTOOL_LINK_MODE_100baseFX_Half_BIT,
658a29de52bSDan Murphy 				 phydev->supported);
659a29de52bSDan Murphy 
660a29de52bSDan Murphy 		/* Auto neg is not supported in 100base FX mode */
661a29de52bSDan Murphy 		bmcr = phy_read(phydev, MII_BMCR);
662a29de52bSDan Murphy 		if (bmcr < 0)
663a29de52bSDan Murphy 			return bmcr;
664a29de52bSDan Murphy 
665a29de52bSDan Murphy 		phydev->autoneg = AUTONEG_DISABLE;
666a29de52bSDan Murphy 		linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported);
667a29de52bSDan Murphy 		linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->advertising);
668a29de52bSDan Murphy 
669a29de52bSDan Murphy 		if (bmcr & BMCR_ANENABLE) {
670a29de52bSDan Murphy 			ret =  phy_modify(phydev, MII_BMCR, BMCR_ANENABLE, 0);
671a29de52bSDan Murphy 			if (ret < 0)
672a29de52bSDan Murphy 				return ret;
673a29de52bSDan Murphy 		}
674a29de52bSDan Murphy 	}
675a29de52bSDan Murphy 
676a29de52bSDan Murphy 	/* Update advertising from supported */
677a29de52bSDan Murphy 	linkmode_or(phydev->advertising, phydev->advertising,
678a29de52bSDan Murphy 		    phydev->supported);
679a29de52bSDan Murphy 
680a29de52bSDan Murphy 	return 0;
681a29de52bSDan Murphy }
682a29de52bSDan Murphy 
68301db923eSDan Murphy static int dp83869_configure_mode(struct phy_device *phydev,
68401db923eSDan Murphy 				  struct dp83869_private *dp83869)
68501db923eSDan Murphy {
68601db923eSDan Murphy 	int phy_ctrl_val;
68701db923eSDan Murphy 	int ret;
68801db923eSDan Murphy 
68901db923eSDan Murphy 	if (dp83869->mode < DP83869_RGMII_COPPER_ETHERNET ||
69001db923eSDan Murphy 	    dp83869->mode > DP83869_SGMII_COPPER_ETHERNET)
69101db923eSDan Murphy 		return -EINVAL;
69201db923eSDan Murphy 
69301db923eSDan Murphy 	/* Below init sequence for each operational mode is defined in
69401db923eSDan Murphy 	 * section 9.4.8 of the datasheet.
69501db923eSDan Murphy 	 */
69601db923eSDan Murphy 	ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_OP_MODE,
69701db923eSDan Murphy 			    dp83869->mode);
69801db923eSDan Murphy 	if (ret)
69901db923eSDan Murphy 		return ret;
70001db923eSDan Murphy 
70101db923eSDan Murphy 	ret = phy_write(phydev, MII_BMCR, MII_DP83869_BMCR_DEFAULT);
70201db923eSDan Murphy 	if (ret)
70301db923eSDan Murphy 		return ret;
70401db923eSDan Murphy 
70501db923eSDan Murphy 	phy_ctrl_val = (dp83869->rx_fifo_depth << DP83869_RX_FIFO_SHIFT |
70601db923eSDan Murphy 			dp83869->tx_fifo_depth << DP83869_TX_FIFO_SHIFT |
70701db923eSDan Murphy 			DP83869_PHY_CTRL_DEFAULT);
70801db923eSDan Murphy 
70901db923eSDan Murphy 	switch (dp83869->mode) {
71001db923eSDan Murphy 	case DP83869_RGMII_COPPER_ETHERNET:
71101db923eSDan Murphy 		ret = phy_write(phydev, MII_DP83869_PHYCTRL,
71201db923eSDan Murphy 				phy_ctrl_val);
71301db923eSDan Murphy 		if (ret)
71401db923eSDan Murphy 			return ret;
71501db923eSDan Murphy 
71601db923eSDan Murphy 		ret = phy_write(phydev, MII_CTRL1000, DP83869_CFG1_DEFAULT);
71701db923eSDan Murphy 		if (ret)
71801db923eSDan Murphy 			return ret;
71901db923eSDan Murphy 
72001db923eSDan Murphy 		ret = dp83869_configure_rgmii(phydev, dp83869);
72101db923eSDan Murphy 		if (ret)
72201db923eSDan Murphy 			return ret;
72301db923eSDan Murphy 		break;
72401db923eSDan Murphy 	case DP83869_RGMII_SGMII_BRIDGE:
725786c4a53SDan Murphy 		ret = phy_modify_mmd(phydev, DP83869_DEVADDR, DP83869_OP_MODE,
72601db923eSDan Murphy 				     DP83869_SGMII_RGMII_BRIDGE,
72701db923eSDan Murphy 				     DP83869_SGMII_RGMII_BRIDGE);
728786c4a53SDan Murphy 		if (ret)
729786c4a53SDan Murphy 			return ret;
73001db923eSDan Murphy 
73101db923eSDan Murphy 		ret = phy_write_mmd(phydev, DP83869_DEVADDR,
73201db923eSDan Murphy 				    DP83869_FX_CTRL, DP83869_FX_CTRL_DEFAULT);
73301db923eSDan Murphy 		if (ret)
73401db923eSDan Murphy 			return ret;
73501db923eSDan Murphy 
73601db923eSDan Murphy 		break;
73701db923eSDan Murphy 	case DP83869_1000M_MEDIA_CONVERT:
73801db923eSDan Murphy 		ret = phy_write(phydev, MII_DP83869_PHYCTRL,
73901db923eSDan Murphy 				phy_ctrl_val);
74001db923eSDan Murphy 		if (ret)
74101db923eSDan Murphy 			return ret;
74201db923eSDan Murphy 
74301db923eSDan Murphy 		ret = phy_write_mmd(phydev, DP83869_DEVADDR,
74401db923eSDan Murphy 				    DP83869_FX_CTRL, DP83869_FX_CTRL_DEFAULT);
74501db923eSDan Murphy 		if (ret)
74601db923eSDan Murphy 			return ret;
74701db923eSDan Murphy 		break;
74801db923eSDan Murphy 	case DP83869_100M_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 		break;
75401db923eSDan Murphy 	case DP83869_SGMII_COPPER_ETHERNET:
75501db923eSDan Murphy 		ret = phy_write(phydev, MII_DP83869_PHYCTRL,
75601db923eSDan Murphy 				phy_ctrl_val);
75701db923eSDan Murphy 		if (ret)
75801db923eSDan Murphy 			return ret;
75901db923eSDan Murphy 
76001db923eSDan Murphy 		ret = phy_write(phydev, MII_CTRL1000, DP83869_CFG1_DEFAULT);
76101db923eSDan Murphy 		if (ret)
76201db923eSDan Murphy 			return ret;
76301db923eSDan Murphy 
76401db923eSDan Murphy 		ret = phy_write_mmd(phydev, DP83869_DEVADDR,
76501db923eSDan Murphy 				    DP83869_FX_CTRL, DP83869_FX_CTRL_DEFAULT);
76601db923eSDan Murphy 		if (ret)
76701db923eSDan Murphy 			return ret;
76801db923eSDan Murphy 
76901db923eSDan Murphy 		break;
77001db923eSDan Murphy 	case DP83869_RGMII_1000_BASE:
77101db923eSDan Murphy 	case DP83869_RGMII_100_BASE:
772a29de52bSDan Murphy 		ret = dp83869_configure_fiber(phydev, dp83869);
77301db923eSDan Murphy 		break;
77401db923eSDan Murphy 	default:
77501db923eSDan Murphy 		return -EINVAL;
7763a5f494dSzhengbin 	}
77701db923eSDan Murphy 
778786c4a53SDan Murphy 	return ret;
77901db923eSDan Murphy }
78001db923eSDan Murphy 
78101db923eSDan Murphy static int dp83869_config_init(struct phy_device *phydev)
78201db923eSDan Murphy {
78301db923eSDan Murphy 	struct dp83869_private *dp83869 = phydev->priv;
78401db923eSDan Murphy 	int ret, val;
78501db923eSDan Murphy 
786811ac400SDan Murphy 	/* Force speed optimization for the PHY even if it strapped */
787811ac400SDan Murphy 	ret = phy_modify(phydev, DP83869_CFG2, DP83869_DOWNSHIFT_EN,
788811ac400SDan Murphy 			 DP83869_DOWNSHIFT_EN);
789811ac400SDan Murphy 	if (ret)
790811ac400SDan Murphy 		return ret;
791811ac400SDan Murphy 
79201db923eSDan Murphy 	ret = dp83869_configure_mode(phydev, dp83869);
79301db923eSDan Murphy 	if (ret)
79401db923eSDan Murphy 		return ret;
79501db923eSDan Murphy 
79601db923eSDan Murphy 	/* Enable Interrupt output INT_OE in CFG4 register */
79701db923eSDan Murphy 	if (phy_interrupt_is_valid(phydev)) {
79801db923eSDan Murphy 		val = phy_read(phydev, DP83869_CFG4);
79901db923eSDan Murphy 		val |= DP83869_INT_OE;
80001db923eSDan Murphy 		phy_write(phydev, DP83869_CFG4, val);
80101db923eSDan Murphy 	}
80201db923eSDan Murphy 
80301db923eSDan Murphy 	if (dp83869->port_mirroring != DP83869_PORT_MIRRORING_KEEP)
80401db923eSDan Murphy 		dp83869_config_port_mirroring(phydev);
80501db923eSDan Murphy 
80601db923eSDan Murphy 	/* Clock output selection if muxing property is set */
80701db923eSDan Murphy 	if (dp83869->clk_output_sel != DP83869_CLK_O_SEL_REF_CLK)
808786c4a53SDan Murphy 		ret = phy_modify_mmd(phydev,
809786c4a53SDan Murphy 				     DP83869_DEVADDR, DP83869_IO_MUX_CFG,
81001db923eSDan Murphy 				     DP83869_IO_MUX_CFG_CLK_O_SEL_MASK,
81101db923eSDan Murphy 				     dp83869->clk_output_sel <<
81201db923eSDan Murphy 				     DP83869_IO_MUX_CFG_CLK_O_SEL_SHIFT);
81301db923eSDan Murphy 
814736b25afSDan Murphy 	if (phy_interface_is_rgmii(phydev)) {
815736b25afSDan Murphy 		ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIIDCTL,
816736b25afSDan Murphy 				    dp83869->rx_int_delay |
817736b25afSDan Murphy 			dp83869->tx_int_delay << DP83869_RGMII_CLK_DELAY_SHIFT);
818736b25afSDan Murphy 		if (ret)
819736b25afSDan Murphy 			return ret;
820736b25afSDan Murphy 
821736b25afSDan Murphy 		val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIICTL);
822736b25afSDan Murphy 		val |= (DP83869_RGMII_TX_CLK_DELAY_EN |
823736b25afSDan Murphy 			DP83869_RGMII_RX_CLK_DELAY_EN);
824736b25afSDan Murphy 
8252e1ec861SDaniel Gorsulowski 		if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
8262e1ec861SDaniel Gorsulowski 			val &= ~(DP83869_RGMII_TX_CLK_DELAY_EN |
8272e1ec861SDaniel Gorsulowski 				 DP83869_RGMII_RX_CLK_DELAY_EN);
8282e1ec861SDaniel Gorsulowski 
829736b25afSDan Murphy 		if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
8302e1ec861SDaniel Gorsulowski 			val &= ~DP83869_RGMII_TX_CLK_DELAY_EN;
831736b25afSDan Murphy 
832736b25afSDan Murphy 		if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
8332e1ec861SDaniel Gorsulowski 			val &= ~DP83869_RGMII_RX_CLK_DELAY_EN;
834736b25afSDan Murphy 
835736b25afSDan Murphy 		ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIICTL,
836736b25afSDan Murphy 				    val);
837736b25afSDan Murphy 	}
838736b25afSDan Murphy 
839786c4a53SDan Murphy 	return ret;
84001db923eSDan Murphy }
84101db923eSDan Murphy 
84201db923eSDan Murphy static int dp83869_probe(struct phy_device *phydev)
84301db923eSDan Murphy {
84401db923eSDan Murphy 	struct dp83869_private *dp83869;
84501db923eSDan Murphy 	int ret;
84601db923eSDan Murphy 
84701db923eSDan Murphy 	dp83869 = devm_kzalloc(&phydev->mdio.dev, sizeof(*dp83869),
84801db923eSDan Murphy 			       GFP_KERNEL);
84901db923eSDan Murphy 	if (!dp83869)
85001db923eSDan Murphy 		return -ENOMEM;
85101db923eSDan Murphy 
85201db923eSDan Murphy 	phydev->priv = dp83869;
85301db923eSDan Murphy 
85401db923eSDan Murphy 	ret = dp83869_of_init(phydev);
85501db923eSDan Murphy 	if (ret)
85601db923eSDan Murphy 		return ret;
85701db923eSDan Murphy 
858*4217a64eSMichael Walle 	if (dp83869->mode == DP83869_RGMII_100_BASE ||
859*4217a64eSMichael Walle 	    dp83869->mode == DP83869_RGMII_1000_BASE)
860*4217a64eSMichael Walle 		phydev->port = PORT_FIBRE;
861*4217a64eSMichael Walle 
86201db923eSDan Murphy 	return dp83869_config_init(phydev);
86301db923eSDan Murphy }
86401db923eSDan Murphy 
86501db923eSDan Murphy static int dp83869_phy_reset(struct phy_device *phydev)
86601db923eSDan Murphy {
86701db923eSDan Murphy 	int ret;
86801db923eSDan Murphy 
86901db923eSDan Murphy 	ret = phy_write(phydev, DP83869_CTRL, DP83869_SW_RESET);
87001db923eSDan Murphy 	if (ret < 0)
87101db923eSDan Murphy 		return ret;
87201db923eSDan Murphy 
87301db923eSDan Murphy 	usleep_range(10, 20);
87401db923eSDan Murphy 
87501db923eSDan Murphy 	/* Global sw reset sets all registers to default.
87601db923eSDan Murphy 	 * Need to set the registers in the PHY to the right config.
87701db923eSDan Murphy 	 */
87801db923eSDan Murphy 	return dp83869_config_init(phydev);
87901db923eSDan Murphy }
88001db923eSDan Murphy 
88101db923eSDan Murphy static struct phy_driver dp83869_driver[] = {
88201db923eSDan Murphy 	{
88301db923eSDan Murphy 		PHY_ID_MATCH_MODEL(DP83869_PHY_ID),
88401db923eSDan Murphy 		.name		= "TI DP83869",
88501db923eSDan Murphy 
88601db923eSDan Murphy 		.probe          = dp83869_probe,
88701db923eSDan Murphy 		.config_init	= dp83869_config_init,
88801db923eSDan Murphy 		.soft_reset	= dp83869_phy_reset,
88901db923eSDan Murphy 
89001db923eSDan Murphy 		/* IRQ related */
89101db923eSDan Murphy 		.config_intr	= dp83869_config_intr,
8921d1ae3c6SIoana Ciornei 		.handle_interrupt = dp83869_handle_interrupt,
893a29de52bSDan Murphy 		.read_status	= dp83869_read_status,
89401db923eSDan Murphy 
895811ac400SDan Murphy 		.get_tunable	= dp83869_get_tunable,
896811ac400SDan Murphy 		.set_tunable	= dp83869_set_tunable,
897811ac400SDan Murphy 
89849fc2301SDan Murphy 		.get_wol	= dp83869_get_wol,
89949fc2301SDan Murphy 		.set_wol	= dp83869_set_wol,
90049fc2301SDan Murphy 
90101db923eSDan Murphy 		.suspend	= genphy_suspend,
90201db923eSDan Murphy 		.resume		= genphy_resume,
90301db923eSDan Murphy 	},
90401db923eSDan Murphy };
90501db923eSDan Murphy module_phy_driver(dp83869_driver);
90601db923eSDan Murphy 
90701db923eSDan Murphy static struct mdio_device_id __maybe_unused dp83869_tbl[] = {
90801db923eSDan Murphy 	{ PHY_ID_MATCH_MODEL(DP83869_PHY_ID) },
90901db923eSDan Murphy 	{ }
91001db923eSDan Murphy };
91101db923eSDan Murphy MODULE_DEVICE_TABLE(mdio, dp83869_tbl);
91201db923eSDan Murphy 
91301db923eSDan Murphy MODULE_DESCRIPTION("Texas Instruments DP83869 PHY driver");
91401db923eSDan Murphy MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com");
91501db923eSDan Murphy MODULE_LICENSE("GPL v2");
916