xref: /openbmc/linux/drivers/net/phy/dp83867.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
15f857575SAndrew Lunn // SPDX-License-Identifier: GPL-2.0
2753c66efSDan Murphy /* Driver for the Texas Instruments DP83867 PHY
32a10154aSDan Murphy  *
42a10154aSDan Murphy  * Copyright (C) 2015 Texas Instruments Inc.
52a10154aSDan Murphy  */
62a10154aSDan Murphy 
72a10154aSDan Murphy #include <linux/ethtool.h>
82a10154aSDan Murphy #include <linux/kernel.h>
92a10154aSDan Murphy #include <linux/mii.h>
102a10154aSDan Murphy #include <linux/module.h>
112a10154aSDan Murphy #include <linux/of.h>
122a10154aSDan Murphy #include <linux/phy.h>
1372a7d452SMax Uvarov #include <linux/delay.h>
14caabee5bSThomas Haemmerle #include <linux/netdevice.h>
15caabee5bSThomas Haemmerle #include <linux/etherdevice.h>
16cd26d72dSDan Murphy #include <linux/bitfield.h>
175c2d0a6aSRasmus Villemoes #include <linux/nvmem-consumer.h>
182a10154aSDan Murphy 
192a10154aSDan Murphy #include <dt-bindings/net/ti-dp83867.h>
202a10154aSDan Murphy 
212a10154aSDan Murphy #define DP83867_PHY_ID		0x2000a231
222a10154aSDan Murphy #define DP83867_DEVADDR		0x1f
232a10154aSDan Murphy 
242a10154aSDan Murphy #define MII_DP83867_PHYCTRL	0x10
25cd26d72dSDan Murphy #define MII_DP83867_PHYSTS	0x11
262a10154aSDan Murphy #define MII_DP83867_MICR	0x12
272a10154aSDan Murphy #define MII_DP83867_ISR		0x13
28caabee5bSThomas Haemmerle #define DP83867_CFG2		0x14
29938f65adSAlexander Stein #define DP83867_LEDCR1		0x18
30938f65adSAlexander Stein #define DP83867_LEDCR2		0x19
315ca7d1caSGrygorii Strashko #define DP83867_CFG3		0x1e
32caabee5bSThomas Haemmerle #define DP83867_CTRL		0x1f
332a10154aSDan Murphy 
342a10154aSDan Murphy /* Extended Registers */
35749f6f68SGrygorii Strashko #define DP83867_FLD_THR_CFG	0x002e
36fc6d39c3SLukasz Majewski #define DP83867_CFG4		0x0031
371a97a477SMax Uvarov #define DP83867_CFG4_SGMII_ANEG_MASK (BIT(5) | BIT(6))
381a97a477SMax Uvarov #define DP83867_CFG4_SGMII_ANEG_TIMER_11MS   (3 << 5)
391a97a477SMax Uvarov #define DP83867_CFG4_SGMII_ANEG_TIMER_800US  (2 << 5)
401a97a477SMax Uvarov #define DP83867_CFG4_SGMII_ANEG_TIMER_2US    (1 << 5)
411a97a477SMax Uvarov #define DP83867_CFG4_SGMII_ANEG_TIMER_16MS   (0 << 5)
421a97a477SMax Uvarov 
432a10154aSDan Murphy #define DP83867_RGMIICTL	0x0032
44ac6e058bSLukasz Majewski #define DP83867_STRAP_STS1	0x006E
45c11669a2STrent Piepho #define DP83867_STRAP_STS2	0x006f
462a10154aSDan Murphy #define DP83867_RGMIIDCTL	0x0086
470b01db27SGrygorii Strashko #define DP83867_DSP_FFE_CFG	0x012c
48caabee5bSThomas Haemmerle #define DP83867_RXFCFG		0x0134
49caabee5bSThomas Haemmerle #define DP83867_RXFPMD1	0x0136
50caabee5bSThomas Haemmerle #define DP83867_RXFPMD2	0x0137
51caabee5bSThomas Haemmerle #define DP83867_RXFPMD3	0x0138
52caabee5bSThomas Haemmerle #define DP83867_RXFSOP1	0x0139
53caabee5bSThomas Haemmerle #define DP83867_RXFSOP2	0x013A
54caabee5bSThomas Haemmerle #define DP83867_RXFSOP3	0x013B
55ed838fe9SMugunthan V N #define DP83867_IO_MUX_CFG	0x0170
56507ddd5cSVitaly Gaiduk #define DP83867_SGMIICTL	0x00D3
57333061b9SMax Uvarov #define DP83867_10M_SGMII_CFG   0x016F
58333061b9SMax Uvarov #define DP83867_10M_SGMII_RATE_ADAPT_MASK BIT(7)
592a10154aSDan Murphy 
602a10154aSDan Murphy #define DP83867_SW_RESET	BIT(15)
612a10154aSDan Murphy #define DP83867_SW_RESTART	BIT(14)
622a10154aSDan Murphy 
632a10154aSDan Murphy /* MICR Interrupt bits */
642a10154aSDan Murphy #define MII_DP83867_MICR_AN_ERR_INT_EN		BIT(15)
652a10154aSDan Murphy #define MII_DP83867_MICR_SPEED_CHNG_INT_EN	BIT(14)
662a10154aSDan Murphy #define MII_DP83867_MICR_DUP_MODE_CHNG_INT_EN	BIT(13)
672a10154aSDan Murphy #define MII_DP83867_MICR_PAGE_RXD_INT_EN	BIT(12)
682a10154aSDan Murphy #define MII_DP83867_MICR_AUTONEG_COMP_INT_EN	BIT(11)
692a10154aSDan Murphy #define MII_DP83867_MICR_LINK_STS_CHNG_INT_EN	BIT(10)
702a10154aSDan Murphy #define MII_DP83867_MICR_FALSE_CARRIER_INT_EN	BIT(8)
712a10154aSDan Murphy #define MII_DP83867_MICR_SLEEP_MODE_CHNG_INT_EN	BIT(4)
722a10154aSDan Murphy #define MII_DP83867_MICR_WOL_INT_EN		BIT(3)
732a10154aSDan Murphy #define MII_DP83867_MICR_XGMII_ERR_INT_EN	BIT(2)
742a10154aSDan Murphy #define MII_DP83867_MICR_POL_CHNG_INT_EN	BIT(1)
752a10154aSDan Murphy #define MII_DP83867_MICR_JABBER_INT_EN		BIT(0)
762a10154aSDan Murphy 
772a10154aSDan Murphy /* RGMIICTL bits */
782a10154aSDan Murphy #define DP83867_RGMII_TX_CLK_DELAY_EN		BIT(1)
792a10154aSDan Murphy #define DP83867_RGMII_RX_CLK_DELAY_EN		BIT(0)
802a10154aSDan Murphy 
81507ddd5cSVitaly Gaiduk /* SGMIICTL bits */
82507ddd5cSVitaly Gaiduk #define DP83867_SGMII_TYPE		BIT(14)
83507ddd5cSVitaly Gaiduk 
84caabee5bSThomas Haemmerle /* RXFCFG bits*/
85caabee5bSThomas Haemmerle #define DP83867_WOL_MAGIC_EN		BIT(0)
86caabee5bSThomas Haemmerle #define DP83867_WOL_BCAST_EN		BIT(2)
87caabee5bSThomas Haemmerle #define DP83867_WOL_UCAST_EN		BIT(4)
88caabee5bSThomas Haemmerle #define DP83867_WOL_SEC_EN		BIT(5)
89caabee5bSThomas Haemmerle #define DP83867_WOL_ENH_MAC		BIT(7)
90caabee5bSThomas Haemmerle 
91ac6e058bSLukasz Majewski /* STRAP_STS1 bits */
92ac6e058bSLukasz Majewski #define DP83867_STRAP_STS1_RESERVED		BIT(11)
93ac6e058bSLukasz Majewski 
94c11669a2STrent Piepho /* STRAP_STS2 bits */
95c11669a2STrent Piepho #define DP83867_STRAP_STS2_CLK_SKEW_TX_MASK	GENMASK(6, 4)
96c11669a2STrent Piepho #define DP83867_STRAP_STS2_CLK_SKEW_TX_SHIFT	4
97c11669a2STrent Piepho #define DP83867_STRAP_STS2_CLK_SKEW_RX_MASK	GENMASK(2, 0)
98c11669a2STrent Piepho #define DP83867_STRAP_STS2_CLK_SKEW_RX_SHIFT	0
99c11669a2STrent Piepho #define DP83867_STRAP_STS2_CLK_SKEW_NONE	BIT(2)
100749f6f68SGrygorii Strashko #define DP83867_STRAP_STS2_STRAP_FLD		BIT(10)
101c11669a2STrent Piepho 
1022a10154aSDan Murphy /* PHY CTRL bits */
103e02d1816SDan Murphy #define DP83867_PHYCR_TX_FIFO_DEPTH_SHIFT	14
104e02d1816SDan Murphy #define DP83867_PHYCR_RX_FIFO_DEPTH_SHIFT	12
105f8bbf417STrent Piepho #define DP83867_PHYCR_FIFO_DEPTH_MAX		0x03
106e02d1816SDan Murphy #define DP83867_PHYCR_TX_FIFO_DEPTH_MASK	GENMASK(15, 14)
107e02d1816SDan Murphy #define DP83867_PHYCR_RX_FIFO_DEPTH_MASK	GENMASK(13, 12)
108ac6e058bSLukasz Majewski #define DP83867_PHYCR_RESERVED_MASK		BIT(11)
10986ffe920SMichael Grzeschik #define DP83867_PHYCR_FORCE_LINK_GOOD		BIT(10)
1102a10154aSDan Murphy 
1112a10154aSDan Murphy /* RGMIIDCTL bits */
112c11669a2STrent Piepho #define DP83867_RGMII_TX_CLK_DELAY_MAX		0xf
1132a10154aSDan Murphy #define DP83867_RGMII_TX_CLK_DELAY_SHIFT	4
114fafc5db2SGrygorii Strashko #define DP83867_RGMII_TX_CLK_DELAY_INV	(DP83867_RGMII_TX_CLK_DELAY_MAX + 1)
115c11669a2STrent Piepho #define DP83867_RGMII_RX_CLK_DELAY_MAX		0xf
116c11669a2STrent Piepho #define DP83867_RGMII_RX_CLK_DELAY_SHIFT	0
117fafc5db2SGrygorii Strashko #define DP83867_RGMII_RX_CLK_DELAY_INV	(DP83867_RGMII_RX_CLK_DELAY_MAX + 1)
118fafc5db2SGrygorii Strashko 
119ed838fe9SMugunthan V N /* IO_MUX_CFG bits */
12027708eb5STrent Piepho #define DP83867_IO_MUX_CFG_IO_IMPEDANCE_MASK	0x1f
121ed838fe9SMugunthan V N #define DP83867_IO_MUX_CFG_IO_IMPEDANCE_MAX	0x0
122ed838fe9SMugunthan V N #define DP83867_IO_MUX_CFG_IO_IMPEDANCE_MIN	0x1f
12313c83cf8STrent Piepho #define DP83867_IO_MUX_CFG_CLK_O_DISABLE	BIT(6)
1249708fb63SWadim Egorov #define DP83867_IO_MUX_CFG_CLK_O_SEL_MASK	(0x1f << 8)
1259708fb63SWadim Egorov #define DP83867_IO_MUX_CFG_CLK_O_SEL_SHIFT	8
126ed838fe9SMugunthan V N 
127cd26d72dSDan Murphy /* PHY STS bits */
128cd26d72dSDan Murphy #define DP83867_PHYSTS_1000			BIT(15)
129cd26d72dSDan Murphy #define DP83867_PHYSTS_100			BIT(14)
130cd26d72dSDan Murphy #define DP83867_PHYSTS_DUPLEX			BIT(13)
131cd26d72dSDan Murphy #define DP83867_PHYSTS_LINK			BIT(10)
132cd26d72dSDan Murphy 
133cd26d72dSDan Murphy /* CFG2 bits */
134cd26d72dSDan Murphy #define DP83867_DOWNSHIFT_EN		(BIT(8) | BIT(9))
135cd26d72dSDan Murphy #define DP83867_DOWNSHIFT_ATTEMPT_MASK	(BIT(10) | BIT(11))
136cd26d72dSDan Murphy #define DP83867_DOWNSHIFT_1_COUNT_VAL	0
137cd26d72dSDan Murphy #define DP83867_DOWNSHIFT_2_COUNT_VAL	1
138cd26d72dSDan Murphy #define DP83867_DOWNSHIFT_4_COUNT_VAL	2
139cd26d72dSDan Murphy #define DP83867_DOWNSHIFT_8_COUNT_VAL	3
140cd26d72dSDan Murphy #define DP83867_DOWNSHIFT_1_COUNT	1
141cd26d72dSDan Murphy #define DP83867_DOWNSHIFT_2_COUNT	2
142cd26d72dSDan Murphy #define DP83867_DOWNSHIFT_4_COUNT	4
143cd26d72dSDan Murphy #define DP83867_DOWNSHIFT_8_COUNT	8
144c76acfb7STan Tee Min #define DP83867_SGMII_AUTONEG_EN	BIT(7)
145cd26d72dSDan Murphy 
1465a7f08c2SGrygorii Strashko /* CFG3 bits */
1475a7f08c2SGrygorii Strashko #define DP83867_CFG3_INT_OE			BIT(7)
1485a7f08c2SGrygorii Strashko #define DP83867_CFG3_ROBUST_AUTO_MDIX		BIT(9)
1495a7f08c2SGrygorii Strashko 
150fc6d39c3SLukasz Majewski /* CFG4 bits */
151fc6d39c3SLukasz Majewski #define DP83867_CFG4_PORT_MIRROR_EN              BIT(0)
152fc6d39c3SLukasz Majewski 
153749f6f68SGrygorii Strashko /* FLD_THR_CFG */
154749f6f68SGrygorii Strashko #define DP83867_FLD_THR_CFG_ENERGY_LOST_THR_MASK	0x7
155749f6f68SGrygorii Strashko 
156938f65adSAlexander Stein #define DP83867_LED_COUNT	4
157938f65adSAlexander Stein 
158938f65adSAlexander Stein /* LED_DRV bits */
159938f65adSAlexander Stein #define DP83867_LED_DRV_EN(x)	BIT((x) * 4)
160938f65adSAlexander Stein #define DP83867_LED_DRV_VAL(x)	BIT((x) * 4 + 1)
161938f65adSAlexander Stein 
162fc6d39c3SLukasz Majewski enum {
163fc6d39c3SLukasz Majewski 	DP83867_PORT_MIRROING_KEEP,
164fc6d39c3SLukasz Majewski 	DP83867_PORT_MIRROING_EN,
165fc6d39c3SLukasz Majewski 	DP83867_PORT_MIRROING_DIS,
166fc6d39c3SLukasz Majewski };
167fc6d39c3SLukasz Majewski 
1682a10154aSDan Murphy struct dp83867_private {
1691b9b2954STrent Piepho 	u32 rx_id_delay;
1701b9b2954STrent Piepho 	u32 tx_id_delay;
171e02d1816SDan Murphy 	u32 tx_fifo_depth;
172e02d1816SDan Murphy 	u32 rx_fifo_depth;
173ed838fe9SMugunthan V N 	int io_impedance;
174fc6d39c3SLukasz Majewski 	int port_mirroring;
17537144476SMurali Karicheri 	bool rxctrl_strap_quirk;
17613c83cf8STrent Piepho 	bool set_clk_output;
17713c83cf8STrent Piepho 	u32 clk_output_sel;
178507ddd5cSVitaly Gaiduk 	bool sgmii_ref_clk_en;
1792a10154aSDan Murphy };
1802a10154aSDan Murphy 
dp83867_ack_interrupt(struct phy_device * phydev)1812a10154aSDan Murphy static int dp83867_ack_interrupt(struct phy_device *phydev)
1822a10154aSDan Murphy {
1832a10154aSDan Murphy 	int err = phy_read(phydev, MII_DP83867_ISR);
1842a10154aSDan Murphy 
1852a10154aSDan Murphy 	if (err < 0)
1862a10154aSDan Murphy 		return err;
1872a10154aSDan Murphy 
1882a10154aSDan Murphy 	return 0;
1892a10154aSDan Murphy }
1902a10154aSDan Murphy 
dp83867_set_wol(struct phy_device * phydev,struct ethtool_wolinfo * wol)191caabee5bSThomas Haemmerle static int dp83867_set_wol(struct phy_device *phydev,
192caabee5bSThomas Haemmerle 			   struct ethtool_wolinfo *wol)
193caabee5bSThomas Haemmerle {
194caabee5bSThomas Haemmerle 	struct net_device *ndev = phydev->attached_dev;
195caabee5bSThomas Haemmerle 	u16 val_rxcfg, val_micr;
19686466cbeSJakub Kicinski 	const u8 *mac;
197caabee5bSThomas Haemmerle 
198caabee5bSThomas Haemmerle 	val_rxcfg = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_RXFCFG);
199caabee5bSThomas Haemmerle 	val_micr = phy_read(phydev, MII_DP83867_MICR);
200caabee5bSThomas Haemmerle 
201caabee5bSThomas Haemmerle 	if (wol->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_UCAST |
202caabee5bSThomas Haemmerle 			    WAKE_BCAST)) {
203caabee5bSThomas Haemmerle 		val_rxcfg |= DP83867_WOL_ENH_MAC;
204caabee5bSThomas Haemmerle 		val_micr |= MII_DP83867_MICR_WOL_INT_EN;
205caabee5bSThomas Haemmerle 
206caabee5bSThomas Haemmerle 		if (wol->wolopts & WAKE_MAGIC) {
20786466cbeSJakub Kicinski 			mac = (const u8 *)ndev->dev_addr;
208caabee5bSThomas Haemmerle 
209caabee5bSThomas Haemmerle 			if (!is_valid_ether_addr(mac))
210caabee5bSThomas Haemmerle 				return -EINVAL;
211caabee5bSThomas Haemmerle 
212caabee5bSThomas Haemmerle 			phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RXFPMD1,
213caabee5bSThomas Haemmerle 				      (mac[1] << 8 | mac[0]));
214caabee5bSThomas Haemmerle 			phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RXFPMD2,
215caabee5bSThomas Haemmerle 				      (mac[3] << 8 | mac[2]));
216caabee5bSThomas Haemmerle 			phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RXFPMD3,
217caabee5bSThomas Haemmerle 				      (mac[5] << 8 | mac[4]));
218caabee5bSThomas Haemmerle 
219caabee5bSThomas Haemmerle 			val_rxcfg |= DP83867_WOL_MAGIC_EN;
220caabee5bSThomas Haemmerle 		} else {
221caabee5bSThomas Haemmerle 			val_rxcfg &= ~DP83867_WOL_MAGIC_EN;
222caabee5bSThomas Haemmerle 		}
223caabee5bSThomas Haemmerle 
224caabee5bSThomas Haemmerle 		if (wol->wolopts & WAKE_MAGICSECURE) {
225caabee5bSThomas Haemmerle 			phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RXFSOP1,
226caabee5bSThomas Haemmerle 				      (wol->sopass[1] << 8) | wol->sopass[0]);
2278b4a11c6SDan Murphy 			phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RXFSOP2,
228caabee5bSThomas Haemmerle 				      (wol->sopass[3] << 8) | wol->sopass[2]);
2298b4a11c6SDan Murphy 			phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RXFSOP3,
230caabee5bSThomas Haemmerle 				      (wol->sopass[5] << 8) | wol->sopass[4]);
231caabee5bSThomas Haemmerle 
232caabee5bSThomas Haemmerle 			val_rxcfg |= DP83867_WOL_SEC_EN;
233caabee5bSThomas Haemmerle 		} else {
234caabee5bSThomas Haemmerle 			val_rxcfg &= ~DP83867_WOL_SEC_EN;
235caabee5bSThomas Haemmerle 		}
236caabee5bSThomas Haemmerle 
237caabee5bSThomas Haemmerle 		if (wol->wolopts & WAKE_UCAST)
238caabee5bSThomas Haemmerle 			val_rxcfg |= DP83867_WOL_UCAST_EN;
239caabee5bSThomas Haemmerle 		else
240caabee5bSThomas Haemmerle 			val_rxcfg &= ~DP83867_WOL_UCAST_EN;
241caabee5bSThomas Haemmerle 
242caabee5bSThomas Haemmerle 		if (wol->wolopts & WAKE_BCAST)
243caabee5bSThomas Haemmerle 			val_rxcfg |= DP83867_WOL_BCAST_EN;
244caabee5bSThomas Haemmerle 		else
245caabee5bSThomas Haemmerle 			val_rxcfg &= ~DP83867_WOL_BCAST_EN;
246caabee5bSThomas Haemmerle 	} else {
247caabee5bSThomas Haemmerle 		val_rxcfg &= ~DP83867_WOL_ENH_MAC;
248caabee5bSThomas Haemmerle 		val_micr &= ~MII_DP83867_MICR_WOL_INT_EN;
249caabee5bSThomas Haemmerle 	}
250caabee5bSThomas Haemmerle 
251caabee5bSThomas Haemmerle 	phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RXFCFG, val_rxcfg);
252caabee5bSThomas Haemmerle 	phy_write(phydev, MII_DP83867_MICR, val_micr);
253caabee5bSThomas Haemmerle 
254caabee5bSThomas Haemmerle 	return 0;
255caabee5bSThomas Haemmerle }
256caabee5bSThomas Haemmerle 
dp83867_get_wol(struct phy_device * phydev,struct ethtool_wolinfo * wol)257caabee5bSThomas Haemmerle static void dp83867_get_wol(struct phy_device *phydev,
258caabee5bSThomas Haemmerle 			    struct ethtool_wolinfo *wol)
259caabee5bSThomas Haemmerle {
260caabee5bSThomas Haemmerle 	u16 value, sopass_val;
261caabee5bSThomas Haemmerle 
262caabee5bSThomas Haemmerle 	wol->supported = (WAKE_UCAST | WAKE_BCAST | WAKE_MAGIC |
263caabee5bSThomas Haemmerle 			WAKE_MAGICSECURE);
264caabee5bSThomas Haemmerle 	wol->wolopts = 0;
265caabee5bSThomas Haemmerle 
266caabee5bSThomas Haemmerle 	value = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_RXFCFG);
267caabee5bSThomas Haemmerle 
268caabee5bSThomas Haemmerle 	if (value & DP83867_WOL_UCAST_EN)
269caabee5bSThomas Haemmerle 		wol->wolopts |= WAKE_UCAST;
270caabee5bSThomas Haemmerle 
271caabee5bSThomas Haemmerle 	if (value & DP83867_WOL_BCAST_EN)
272caabee5bSThomas Haemmerle 		wol->wolopts |= WAKE_BCAST;
273caabee5bSThomas Haemmerle 
274caabee5bSThomas Haemmerle 	if (value & DP83867_WOL_MAGIC_EN)
275caabee5bSThomas Haemmerle 		wol->wolopts |= WAKE_MAGIC;
276caabee5bSThomas Haemmerle 
277caabee5bSThomas Haemmerle 	if (value & DP83867_WOL_SEC_EN) {
278caabee5bSThomas Haemmerle 		sopass_val = phy_read_mmd(phydev, DP83867_DEVADDR,
279caabee5bSThomas Haemmerle 					  DP83867_RXFSOP1);
280caabee5bSThomas Haemmerle 		wol->sopass[0] = (sopass_val & 0xff);
281caabee5bSThomas Haemmerle 		wol->sopass[1] = (sopass_val >> 8);
282caabee5bSThomas Haemmerle 
283caabee5bSThomas Haemmerle 		sopass_val = phy_read_mmd(phydev, DP83867_DEVADDR,
284caabee5bSThomas Haemmerle 					  DP83867_RXFSOP2);
285caabee5bSThomas Haemmerle 		wol->sopass[2] = (sopass_val & 0xff);
286caabee5bSThomas Haemmerle 		wol->sopass[3] = (sopass_val >> 8);
287caabee5bSThomas Haemmerle 
288caabee5bSThomas Haemmerle 		sopass_val = phy_read_mmd(phydev, DP83867_DEVADDR,
289caabee5bSThomas Haemmerle 					  DP83867_RXFSOP3);
290caabee5bSThomas Haemmerle 		wol->sopass[4] = (sopass_val & 0xff);
291caabee5bSThomas Haemmerle 		wol->sopass[5] = (sopass_val >> 8);
292caabee5bSThomas Haemmerle 
293caabee5bSThomas Haemmerle 		wol->wolopts |= WAKE_MAGICSECURE;
294caabee5bSThomas Haemmerle 	}
295caabee5bSThomas Haemmerle 
296caabee5bSThomas Haemmerle 	if (!(value & DP83867_WOL_ENH_MAC))
297caabee5bSThomas Haemmerle 		wol->wolopts = 0;
298caabee5bSThomas Haemmerle }
299caabee5bSThomas Haemmerle 
dp83867_config_intr(struct phy_device * phydev)3002a10154aSDan Murphy static int dp83867_config_intr(struct phy_device *phydev)
3012a10154aSDan Murphy {
302aa2d603aSIoana Ciornei 	int micr_status, err;
3032a10154aSDan Murphy 
3042a10154aSDan Murphy 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
305aa2d603aSIoana Ciornei 		err = dp83867_ack_interrupt(phydev);
306aa2d603aSIoana Ciornei 		if (err)
307aa2d603aSIoana Ciornei 			return err;
308aa2d603aSIoana Ciornei 
3092a10154aSDan Murphy 		micr_status = phy_read(phydev, MII_DP83867_MICR);
3102a10154aSDan Murphy 		if (micr_status < 0)
3112a10154aSDan Murphy 			return micr_status;
3122a10154aSDan Murphy 
3132a10154aSDan Murphy 		micr_status |=
3142a10154aSDan Murphy 			(MII_DP83867_MICR_AN_ERR_INT_EN |
3152a10154aSDan Murphy 			MII_DP83867_MICR_SPEED_CHNG_INT_EN |
3165ca7d1caSGrygorii Strashko 			MII_DP83867_MICR_AUTONEG_COMP_INT_EN |
3175ca7d1caSGrygorii Strashko 			MII_DP83867_MICR_LINK_STS_CHNG_INT_EN |
3182a10154aSDan Murphy 			MII_DP83867_MICR_DUP_MODE_CHNG_INT_EN |
3192a10154aSDan Murphy 			MII_DP83867_MICR_SLEEP_MODE_CHNG_INT_EN);
3202a10154aSDan Murphy 
321aa2d603aSIoana Ciornei 		err = phy_write(phydev, MII_DP83867_MICR, micr_status);
322aa2d603aSIoana Ciornei 	} else {
323aa2d603aSIoana Ciornei 		micr_status = 0x0;
324aa2d603aSIoana Ciornei 		err = phy_write(phydev, MII_DP83867_MICR, micr_status);
325aa2d603aSIoana Ciornei 		if (err)
326aa2d603aSIoana Ciornei 			return err;
327aa2d603aSIoana Ciornei 
328aa2d603aSIoana Ciornei 		err = dp83867_ack_interrupt(phydev);
3292a10154aSDan Murphy 	}
3302a10154aSDan Murphy 
331aa2d603aSIoana Ciornei 	return err;
3322a10154aSDan Murphy }
3332a10154aSDan Murphy 
dp83867_handle_interrupt(struct phy_device * phydev)3341d1ae3c6SIoana Ciornei static irqreturn_t dp83867_handle_interrupt(struct phy_device *phydev)
3351d1ae3c6SIoana Ciornei {
3361d1ae3c6SIoana Ciornei 	int irq_status, irq_enabled;
3371d1ae3c6SIoana Ciornei 
3381d1ae3c6SIoana Ciornei 	irq_status = phy_read(phydev, MII_DP83867_ISR);
3391d1ae3c6SIoana Ciornei 	if (irq_status < 0) {
3401d1ae3c6SIoana Ciornei 		phy_error(phydev);
3411d1ae3c6SIoana Ciornei 		return IRQ_NONE;
3421d1ae3c6SIoana Ciornei 	}
3431d1ae3c6SIoana Ciornei 
3441d1ae3c6SIoana Ciornei 	irq_enabled = phy_read(phydev, MII_DP83867_MICR);
3451d1ae3c6SIoana Ciornei 	if (irq_enabled < 0) {
3461d1ae3c6SIoana Ciornei 		phy_error(phydev);
3471d1ae3c6SIoana Ciornei 		return IRQ_NONE;
3481d1ae3c6SIoana Ciornei 	}
3491d1ae3c6SIoana Ciornei 
3501d1ae3c6SIoana Ciornei 	if (!(irq_status & irq_enabled))
3511d1ae3c6SIoana Ciornei 		return IRQ_NONE;
3521d1ae3c6SIoana Ciornei 
3531d1ae3c6SIoana Ciornei 	phy_trigger_machine(phydev);
3541d1ae3c6SIoana Ciornei 
3551d1ae3c6SIoana Ciornei 	return IRQ_HANDLED;
3561d1ae3c6SIoana Ciornei }
3571d1ae3c6SIoana Ciornei 
dp83867_read_status(struct phy_device * phydev)358cd26d72dSDan Murphy static int dp83867_read_status(struct phy_device *phydev)
359cd26d72dSDan Murphy {
360cd26d72dSDan Murphy 	int status = phy_read(phydev, MII_DP83867_PHYSTS);
361cd26d72dSDan Murphy 	int ret;
362cd26d72dSDan Murphy 
363cd26d72dSDan Murphy 	ret = genphy_read_status(phydev);
364cd26d72dSDan Murphy 	if (ret)
365cd26d72dSDan Murphy 		return ret;
366cd26d72dSDan Murphy 
367cd26d72dSDan Murphy 	if (status < 0)
368cd26d72dSDan Murphy 		return status;
369cd26d72dSDan Murphy 
370cd26d72dSDan Murphy 	if (status & DP83867_PHYSTS_DUPLEX)
371cd26d72dSDan Murphy 		phydev->duplex = DUPLEX_FULL;
372cd26d72dSDan Murphy 	else
373cd26d72dSDan Murphy 		phydev->duplex = DUPLEX_HALF;
374cd26d72dSDan Murphy 
375cd26d72dSDan Murphy 	if (status & DP83867_PHYSTS_1000)
376cd26d72dSDan Murphy 		phydev->speed = SPEED_1000;
377cd26d72dSDan Murphy 	else if (status & DP83867_PHYSTS_100)
378cd26d72dSDan Murphy 		phydev->speed = SPEED_100;
379cd26d72dSDan Murphy 	else
380cd26d72dSDan Murphy 		phydev->speed = SPEED_10;
381cd26d72dSDan Murphy 
382cd26d72dSDan Murphy 	return 0;
383cd26d72dSDan Murphy }
384cd26d72dSDan Murphy 
dp83867_get_downshift(struct phy_device * phydev,u8 * data)385cd26d72dSDan Murphy static int dp83867_get_downshift(struct phy_device *phydev, u8 *data)
386cd26d72dSDan Murphy {
387cd26d72dSDan Murphy 	int val, cnt, enable, count;
388cd26d72dSDan Murphy 
389cd26d72dSDan Murphy 	val = phy_read(phydev, DP83867_CFG2);
390cd26d72dSDan Murphy 	if (val < 0)
391cd26d72dSDan Murphy 		return val;
392cd26d72dSDan Murphy 
393cd26d72dSDan Murphy 	enable = FIELD_GET(DP83867_DOWNSHIFT_EN, val);
394cd26d72dSDan Murphy 	cnt = FIELD_GET(DP83867_DOWNSHIFT_ATTEMPT_MASK, val);
395cd26d72dSDan Murphy 
396cd26d72dSDan Murphy 	switch (cnt) {
397cd26d72dSDan Murphy 	case DP83867_DOWNSHIFT_1_COUNT_VAL:
398cd26d72dSDan Murphy 		count = DP83867_DOWNSHIFT_1_COUNT;
399cd26d72dSDan Murphy 		break;
400cd26d72dSDan Murphy 	case DP83867_DOWNSHIFT_2_COUNT_VAL:
401cd26d72dSDan Murphy 		count = DP83867_DOWNSHIFT_2_COUNT;
402cd26d72dSDan Murphy 		break;
403cd26d72dSDan Murphy 	case DP83867_DOWNSHIFT_4_COUNT_VAL:
404cd26d72dSDan Murphy 		count = DP83867_DOWNSHIFT_4_COUNT;
405cd26d72dSDan Murphy 		break;
406cd26d72dSDan Murphy 	case DP83867_DOWNSHIFT_8_COUNT_VAL:
407cd26d72dSDan Murphy 		count = DP83867_DOWNSHIFT_8_COUNT;
408cd26d72dSDan Murphy 		break;
409cd26d72dSDan Murphy 	default:
410cd26d72dSDan Murphy 		return -EINVAL;
411dce38b74SZheng Bin 	}
412cd26d72dSDan Murphy 
413cd26d72dSDan Murphy 	*data = enable ? count : DOWNSHIFT_DEV_DISABLE;
414cd26d72dSDan Murphy 
415cd26d72dSDan Murphy 	return 0;
416cd26d72dSDan Murphy }
417cd26d72dSDan Murphy 
dp83867_set_downshift(struct phy_device * phydev,u8 cnt)418cd26d72dSDan Murphy static int dp83867_set_downshift(struct phy_device *phydev, u8 cnt)
419cd26d72dSDan Murphy {
420cd26d72dSDan Murphy 	int val, count;
421cd26d72dSDan Murphy 
422cd26d72dSDan Murphy 	if (cnt > DP83867_DOWNSHIFT_8_COUNT)
423cd26d72dSDan Murphy 		return -E2BIG;
424cd26d72dSDan Murphy 
425cd26d72dSDan Murphy 	if (!cnt)
426cd26d72dSDan Murphy 		return phy_clear_bits(phydev, DP83867_CFG2,
427cd26d72dSDan Murphy 				      DP83867_DOWNSHIFT_EN);
428cd26d72dSDan Murphy 
429cd26d72dSDan Murphy 	switch (cnt) {
430cd26d72dSDan Murphy 	case DP83867_DOWNSHIFT_1_COUNT:
431cd26d72dSDan Murphy 		count = DP83867_DOWNSHIFT_1_COUNT_VAL;
432cd26d72dSDan Murphy 		break;
433cd26d72dSDan Murphy 	case DP83867_DOWNSHIFT_2_COUNT:
434cd26d72dSDan Murphy 		count = DP83867_DOWNSHIFT_2_COUNT_VAL;
435cd26d72dSDan Murphy 		break;
436cd26d72dSDan Murphy 	case DP83867_DOWNSHIFT_4_COUNT:
437cd26d72dSDan Murphy 		count = DP83867_DOWNSHIFT_4_COUNT_VAL;
438cd26d72dSDan Murphy 		break;
439cd26d72dSDan Murphy 	case DP83867_DOWNSHIFT_8_COUNT:
440cd26d72dSDan Murphy 		count = DP83867_DOWNSHIFT_8_COUNT_VAL;
441cd26d72dSDan Murphy 		break;
442cd26d72dSDan Murphy 	default:
443cd26d72dSDan Murphy 		phydev_err(phydev,
444cd26d72dSDan Murphy 			   "Downshift count must be 1, 2, 4 or 8\n");
445cd26d72dSDan Murphy 		return -EINVAL;
446dce38b74SZheng Bin 	}
447cd26d72dSDan Murphy 
448cd26d72dSDan Murphy 	val = DP83867_DOWNSHIFT_EN;
449cd26d72dSDan Murphy 	val |= FIELD_PREP(DP83867_DOWNSHIFT_ATTEMPT_MASK, count);
450cd26d72dSDan Murphy 
451cd26d72dSDan Murphy 	return phy_modify(phydev, DP83867_CFG2,
452cd26d72dSDan Murphy 			  DP83867_DOWNSHIFT_EN | DP83867_DOWNSHIFT_ATTEMPT_MASK,
453cd26d72dSDan Murphy 			  val);
454cd26d72dSDan Murphy }
455cd26d72dSDan Murphy 
dp83867_get_tunable(struct phy_device * phydev,struct ethtool_tunable * tuna,void * data)456cd26d72dSDan Murphy static int dp83867_get_tunable(struct phy_device *phydev,
457cd26d72dSDan Murphy 			       struct ethtool_tunable *tuna, void *data)
458cd26d72dSDan Murphy {
459cd26d72dSDan Murphy 	switch (tuna->id) {
460cd26d72dSDan Murphy 	case ETHTOOL_PHY_DOWNSHIFT:
461cd26d72dSDan Murphy 		return dp83867_get_downshift(phydev, data);
462cd26d72dSDan Murphy 	default:
463cd26d72dSDan Murphy 		return -EOPNOTSUPP;
464cd26d72dSDan Murphy 	}
465cd26d72dSDan Murphy }
466cd26d72dSDan Murphy 
dp83867_set_tunable(struct phy_device * phydev,struct ethtool_tunable * tuna,const void * data)467cd26d72dSDan Murphy static int dp83867_set_tunable(struct phy_device *phydev,
468cd26d72dSDan Murphy 			       struct ethtool_tunable *tuna, const void *data)
469cd26d72dSDan Murphy {
470cd26d72dSDan Murphy 	switch (tuna->id) {
471cd26d72dSDan Murphy 	case ETHTOOL_PHY_DOWNSHIFT:
472cd26d72dSDan Murphy 		return dp83867_set_downshift(phydev, *(const u8 *)data);
473cd26d72dSDan Murphy 	default:
474cd26d72dSDan Murphy 		return -EOPNOTSUPP;
475cd26d72dSDan Murphy 	}
476cd26d72dSDan Murphy }
477cd26d72dSDan Murphy 
dp83867_config_port_mirroring(struct phy_device * phydev)478fc6d39c3SLukasz Majewski static int dp83867_config_port_mirroring(struct phy_device *phydev)
479fc6d39c3SLukasz Majewski {
48086c2b51fSwuych 	struct dp83867_private *dp83867 = phydev->priv;
481fc6d39c3SLukasz Majewski 
482fc6d39c3SLukasz Majewski 	if (dp83867->port_mirroring == DP83867_PORT_MIRROING_EN)
483b52c018dSHeiner Kallweit 		phy_set_bits_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4,
484b52c018dSHeiner Kallweit 				 DP83867_CFG4_PORT_MIRROR_EN);
485fc6d39c3SLukasz Majewski 	else
486b52c018dSHeiner Kallweit 		phy_clear_bits_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4,
487b52c018dSHeiner Kallweit 				   DP83867_CFG4_PORT_MIRROR_EN);
488fc6d39c3SLukasz Majewski 	return 0;
489fc6d39c3SLukasz Majewski }
490fc6d39c3SLukasz Majewski 
dp83867_verify_rgmii_cfg(struct phy_device * phydev)491fafc5db2SGrygorii Strashko static int dp83867_verify_rgmii_cfg(struct phy_device *phydev)
492fafc5db2SGrygorii Strashko {
493fafc5db2SGrygorii Strashko 	struct dp83867_private *dp83867 = phydev->priv;
494fafc5db2SGrygorii Strashko 
495fafc5db2SGrygorii Strashko 	/* Existing behavior was to use default pin strapping delay in rgmii
496fafc5db2SGrygorii Strashko 	 * mode, but rgmii should have meant no delay.  Warn existing users.
497fafc5db2SGrygorii Strashko 	 */
498fafc5db2SGrygorii Strashko 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII) {
499fafc5db2SGrygorii Strashko 		const u16 val = phy_read_mmd(phydev, DP83867_DEVADDR,
500fafc5db2SGrygorii Strashko 					     DP83867_STRAP_STS2);
501fafc5db2SGrygorii Strashko 		const u16 txskew = (val & DP83867_STRAP_STS2_CLK_SKEW_TX_MASK) >>
502fafc5db2SGrygorii Strashko 				   DP83867_STRAP_STS2_CLK_SKEW_TX_SHIFT;
503fafc5db2SGrygorii Strashko 		const u16 rxskew = (val & DP83867_STRAP_STS2_CLK_SKEW_RX_MASK) >>
504fafc5db2SGrygorii Strashko 				   DP83867_STRAP_STS2_CLK_SKEW_RX_SHIFT;
505fafc5db2SGrygorii Strashko 
506fafc5db2SGrygorii Strashko 		if (txskew != DP83867_STRAP_STS2_CLK_SKEW_NONE ||
507fafc5db2SGrygorii Strashko 		    rxskew != DP83867_STRAP_STS2_CLK_SKEW_NONE)
508fafc5db2SGrygorii Strashko 			phydev_warn(phydev,
509fafc5db2SGrygorii Strashko 				    "PHY has delays via pin strapping, but phy-mode = 'rgmii'\n"
510fafc5db2SGrygorii Strashko 				    "Should be 'rgmii-id' to use internal delays txskew:%x rxskew:%x\n",
511fafc5db2SGrygorii Strashko 				    txskew, rxskew);
512fafc5db2SGrygorii Strashko 	}
513fafc5db2SGrygorii Strashko 
514fafc5db2SGrygorii Strashko 	/* RX delay *must* be specified if internal delay of RX is used. */
515fafc5db2SGrygorii Strashko 	if ((phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
516fafc5db2SGrygorii Strashko 	     phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) &&
517fafc5db2SGrygorii Strashko 	     dp83867->rx_id_delay == DP83867_RGMII_RX_CLK_DELAY_INV) {
518fafc5db2SGrygorii Strashko 		phydev_err(phydev, "ti,rx-internal-delay must be specified\n");
519fafc5db2SGrygorii Strashko 		return -EINVAL;
520fafc5db2SGrygorii Strashko 	}
521fafc5db2SGrygorii Strashko 
522fafc5db2SGrygorii Strashko 	/* TX delay *must* be specified if internal delay of TX is used. */
523fafc5db2SGrygorii Strashko 	if ((phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
524fafc5db2SGrygorii Strashko 	     phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) &&
525fafc5db2SGrygorii Strashko 	     dp83867->tx_id_delay == DP83867_RGMII_TX_CLK_DELAY_INV) {
526fafc5db2SGrygorii Strashko 		phydev_err(phydev, "ti,tx-internal-delay must be specified\n");
527fafc5db2SGrygorii Strashko 		return -EINVAL;
528fafc5db2SGrygorii Strashko 	}
529fafc5db2SGrygorii Strashko 
530fafc5db2SGrygorii Strashko 	return 0;
531fafc5db2SGrygorii Strashko }
532fafc5db2SGrygorii Strashko 
533506de006SDan Murphy #if IS_ENABLED(CONFIG_OF_MDIO)
dp83867_of_init_io_impedance(struct phy_device * phydev)5345c2d0a6aSRasmus Villemoes static int dp83867_of_init_io_impedance(struct phy_device *phydev)
5355c2d0a6aSRasmus Villemoes {
5365c2d0a6aSRasmus Villemoes 	struct dp83867_private *dp83867 = phydev->priv;
5375c2d0a6aSRasmus Villemoes 	struct device *dev = &phydev->mdio.dev;
5385c2d0a6aSRasmus Villemoes 	struct device_node *of_node = dev->of_node;
5395c2d0a6aSRasmus Villemoes 	struct nvmem_cell *cell;
5405c2d0a6aSRasmus Villemoes 	u8 *buf, val;
5415c2d0a6aSRasmus Villemoes 	int ret;
5425c2d0a6aSRasmus Villemoes 
5435c2d0a6aSRasmus Villemoes 	cell = of_nvmem_cell_get(of_node, "io_impedance_ctrl");
5445c2d0a6aSRasmus Villemoes 	if (IS_ERR(cell)) {
5455c2d0a6aSRasmus Villemoes 		ret = PTR_ERR(cell);
546546b9d3fSNikita Shubin 		if (ret != -ENOENT && ret != -EOPNOTSUPP)
5475c2d0a6aSRasmus Villemoes 			return phydev_err_probe(phydev, ret,
5485c2d0a6aSRasmus Villemoes 						"failed to get nvmem cell io_impedance_ctrl\n");
5495c2d0a6aSRasmus Villemoes 
5505c2d0a6aSRasmus Villemoes 		/* If no nvmem cell, check for the boolean properties. */
5515c2d0a6aSRasmus Villemoes 		if (of_property_read_bool(of_node, "ti,max-output-impedance"))
5525c2d0a6aSRasmus Villemoes 			dp83867->io_impedance = DP83867_IO_MUX_CFG_IO_IMPEDANCE_MAX;
5535c2d0a6aSRasmus Villemoes 		else if (of_property_read_bool(of_node, "ti,min-output-impedance"))
5545c2d0a6aSRasmus Villemoes 			dp83867->io_impedance = DP83867_IO_MUX_CFG_IO_IMPEDANCE_MIN;
5555c2d0a6aSRasmus Villemoes 		else
5565c2d0a6aSRasmus Villemoes 			dp83867->io_impedance = -1; /* leave at default */
5575c2d0a6aSRasmus Villemoes 
5585c2d0a6aSRasmus Villemoes 		return 0;
5595c2d0a6aSRasmus Villemoes 	}
5605c2d0a6aSRasmus Villemoes 
5615c2d0a6aSRasmus Villemoes 	buf = nvmem_cell_read(cell, NULL);
5625c2d0a6aSRasmus Villemoes 	nvmem_cell_put(cell);
5635c2d0a6aSRasmus Villemoes 
5645c2d0a6aSRasmus Villemoes 	if (IS_ERR(buf))
5655c2d0a6aSRasmus Villemoes 		return PTR_ERR(buf);
5665c2d0a6aSRasmus Villemoes 
5675c2d0a6aSRasmus Villemoes 	val = *buf;
5685c2d0a6aSRasmus Villemoes 	kfree(buf);
5695c2d0a6aSRasmus Villemoes 
5705c2d0a6aSRasmus Villemoes 	if ((val & DP83867_IO_MUX_CFG_IO_IMPEDANCE_MASK) != val) {
5715c2d0a6aSRasmus Villemoes 		phydev_err(phydev, "nvmem cell 'io_impedance_ctrl' contents out of range\n");
5725c2d0a6aSRasmus Villemoes 		return -ERANGE;
5735c2d0a6aSRasmus Villemoes 	}
5745c2d0a6aSRasmus Villemoes 	dp83867->io_impedance = val;
5755c2d0a6aSRasmus Villemoes 
5765c2d0a6aSRasmus Villemoes 	return 0;
5775c2d0a6aSRasmus Villemoes }
5785c2d0a6aSRasmus Villemoes 
dp83867_of_init(struct phy_device * phydev)5792a10154aSDan Murphy static int dp83867_of_init(struct phy_device *phydev)
5802a10154aSDan Murphy {
5812a10154aSDan Murphy 	struct dp83867_private *dp83867 = phydev->priv;
582e5a03bfdSAndrew Lunn 	struct device *dev = &phydev->mdio.dev;
5832a10154aSDan Murphy 	struct device_node *of_node = dev->of_node;
5842a10154aSDan Murphy 	int ret;
5852a10154aSDan Murphy 
5867bf9ae01SAndrew Lunn 	if (!of_node)
5872a10154aSDan Murphy 		return -ENODEV;
5882a10154aSDan Murphy 
589ed838fe9SMugunthan V N 	/* Optional configuration */
5909708fb63SWadim Egorov 	ret = of_property_read_u32(of_node, "ti,clk-output-sel",
5919708fb63SWadim Egorov 				   &dp83867->clk_output_sel);
59213c83cf8STrent Piepho 	/* If not set, keep default */
59313c83cf8STrent Piepho 	if (!ret) {
59413c83cf8STrent Piepho 		dp83867->set_clk_output = true;
59513c83cf8STrent Piepho 		/* Valid values are 0 to DP83867_CLK_O_SEL_REF_CLK or
59613c83cf8STrent Piepho 		 * DP83867_CLK_O_SEL_OFF.
5979708fb63SWadim Egorov 		 */
59813c83cf8STrent Piepho 		if (dp83867->clk_output_sel > DP83867_CLK_O_SEL_REF_CLK &&
59913c83cf8STrent Piepho 		    dp83867->clk_output_sel != DP83867_CLK_O_SEL_OFF) {
60013c83cf8STrent Piepho 			phydev_err(phydev, "ti,clk-output-sel value %u out of range\n",
60113c83cf8STrent Piepho 				   dp83867->clk_output_sel);
60213c83cf8STrent Piepho 			return -EINVAL;
60313c83cf8STrent Piepho 		}
60413c83cf8STrent Piepho 	}
6059708fb63SWadim Egorov 
6065c2d0a6aSRasmus Villemoes 	ret = dp83867_of_init_io_impedance(phydev);
6075c2d0a6aSRasmus Villemoes 	if (ret)
6085c2d0a6aSRasmus Villemoes 		return ret;
609ed838fe9SMugunthan V N 
61037144476SMurali Karicheri 	dp83867->rxctrl_strap_quirk = of_property_read_bool(of_node,
61137144476SMurali Karicheri 							    "ti,dp83867-rxctrl-strap-quirk");
61237144476SMurali Karicheri 
613507ddd5cSVitaly Gaiduk 	dp83867->sgmii_ref_clk_en = of_property_read_bool(of_node,
614507ddd5cSVitaly Gaiduk 							  "ti,sgmii-ref-clock-output-enable");
615507ddd5cSVitaly Gaiduk 
616fafc5db2SGrygorii Strashko 	dp83867->rx_id_delay = DP83867_RGMII_RX_CLK_DELAY_INV;
617ac7ba51cSDan Murphy 	ret = of_property_read_u32(of_node, "ti,rx-internal-delay",
6182a10154aSDan Murphy 				   &dp83867->rx_id_delay);
619fafc5db2SGrygorii Strashko 	if (!ret && dp83867->rx_id_delay > DP83867_RGMII_RX_CLK_DELAY_MAX) {
620c11669a2STrent Piepho 		phydev_err(phydev,
621c11669a2STrent Piepho 			   "ti,rx-internal-delay value of %u out of range\n",
622c11669a2STrent Piepho 			   dp83867->rx_id_delay);
623c11669a2STrent Piepho 		return -EINVAL;
624c11669a2STrent Piepho 	}
6252a10154aSDan Murphy 
626fafc5db2SGrygorii Strashko 	dp83867->tx_id_delay = DP83867_RGMII_TX_CLK_DELAY_INV;
627ac7ba51cSDan Murphy 	ret = of_property_read_u32(of_node, "ti,tx-internal-delay",
6282a10154aSDan Murphy 				   &dp83867->tx_id_delay);
629fafc5db2SGrygorii Strashko 	if (!ret && dp83867->tx_id_delay > DP83867_RGMII_TX_CLK_DELAY_MAX) {
630c11669a2STrent Piepho 		phydev_err(phydev,
631c11669a2STrent Piepho 			   "ti,tx-internal-delay value of %u out of range\n",
632c11669a2STrent Piepho 			   dp83867->tx_id_delay);
633c11669a2STrent Piepho 		return -EINVAL;
634c11669a2STrent Piepho 	}
6352a10154aSDan Murphy 
636fc6d39c3SLukasz Majewski 	if (of_property_read_bool(of_node, "enet-phy-lane-swap"))
637fc6d39c3SLukasz Majewski 		dp83867->port_mirroring = DP83867_PORT_MIRROING_EN;
638fc6d39c3SLukasz Majewski 
639fc6d39c3SLukasz Majewski 	if (of_property_read_bool(of_node, "enet-phy-lane-no-swap"))
640fc6d39c3SLukasz Majewski 		dp83867->port_mirroring = DP83867_PORT_MIRROING_DIS;
641fc6d39c3SLukasz Majewski 
642f8bbf417STrent Piepho 	ret = of_property_read_u32(of_node, "ti,fifo-depth",
643e02d1816SDan Murphy 				   &dp83867->tx_fifo_depth);
644f8bbf417STrent Piepho 	if (ret) {
645e02d1816SDan Murphy 		ret = of_property_read_u32(of_node, "tx-fifo-depth",
646e02d1816SDan Murphy 					   &dp83867->tx_fifo_depth);
647e02d1816SDan Murphy 		if (ret)
648e02d1816SDan Murphy 			dp83867->tx_fifo_depth =
649e02d1816SDan Murphy 					DP83867_PHYCR_FIFO_DEPTH_4_B_NIB;
650f8bbf417STrent Piepho 	}
651e02d1816SDan Murphy 
652e02d1816SDan Murphy 	if (dp83867->tx_fifo_depth > DP83867_PHYCR_FIFO_DEPTH_MAX) {
653e02d1816SDan Murphy 		phydev_err(phydev, "tx-fifo-depth value %u out of range\n",
654e02d1816SDan Murphy 			   dp83867->tx_fifo_depth);
655f8bbf417STrent Piepho 		return -EINVAL;
656f8bbf417STrent Piepho 	}
657e02d1816SDan Murphy 
658e02d1816SDan Murphy 	ret = of_property_read_u32(of_node, "rx-fifo-depth",
659e02d1816SDan Murphy 				   &dp83867->rx_fifo_depth);
660e02d1816SDan Murphy 	if (ret)
661e02d1816SDan Murphy 		dp83867->rx_fifo_depth = DP83867_PHYCR_FIFO_DEPTH_4_B_NIB;
662e02d1816SDan Murphy 
663e02d1816SDan Murphy 	if (dp83867->rx_fifo_depth > DP83867_PHYCR_FIFO_DEPTH_MAX) {
664e02d1816SDan Murphy 		phydev_err(phydev, "rx-fifo-depth value %u out of range\n",
665e02d1816SDan Murphy 			   dp83867->rx_fifo_depth);
666e02d1816SDan Murphy 		return -EINVAL;
667e02d1816SDan Murphy 	}
668e02d1816SDan Murphy 
669f8bbf417STrent Piepho 	return 0;
6702a10154aSDan Murphy }
6712a10154aSDan Murphy #else
dp83867_of_init(struct phy_device * phydev)6722a10154aSDan Murphy static int dp83867_of_init(struct phy_device *phydev)
6732a10154aSDan Murphy {
6744dc08dccSLay, Kuan Loon 	struct dp83867_private *dp83867 = phydev->priv;
6754dc08dccSLay, Kuan Loon 	u16 delay;
6764dc08dccSLay, Kuan Loon 
6774dc08dccSLay, Kuan Loon 	/* For non-OF device, the RX and TX ID values are either strapped
6784dc08dccSLay, Kuan Loon 	 * or take from default value. So, we init RX & TX ID values here
6794dc08dccSLay, Kuan Loon 	 * so that the RGMIIDCTL is configured correctly later in
6804dc08dccSLay, Kuan Loon 	 * dp83867_config_init();
6814dc08dccSLay, Kuan Loon 	 */
6824dc08dccSLay, Kuan Loon 	delay = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_RGMIIDCTL);
6834dc08dccSLay, Kuan Loon 	dp83867->rx_id_delay = delay & DP83867_RGMII_RX_CLK_DELAY_MAX;
6844dc08dccSLay, Kuan Loon 	dp83867->tx_id_delay = (delay >> DP83867_RGMII_TX_CLK_DELAY_SHIFT) &
6854dc08dccSLay, Kuan Loon 			       DP83867_RGMII_TX_CLK_DELAY_MAX;
6864dc08dccSLay, Kuan Loon 
6874dc08dccSLay, Kuan Loon 	/* Per datasheet, IO impedance is default to 50-ohm, so we set the
6884dc08dccSLay, Kuan Loon 	 * same here or else the default '0' means highest IO impedance
6894dc08dccSLay, Kuan Loon 	 * which is wrong.
6904dc08dccSLay, Kuan Loon 	 */
6914dc08dccSLay, Kuan Loon 	dp83867->io_impedance = DP83867_IO_MUX_CFG_IO_IMPEDANCE_MIN / 2;
6924dc08dccSLay, Kuan Loon 
693e2a54350SMichael Sit Wei Hong 	/* For non-OF device, the RX and TX FIFO depths are taken from
694e2a54350SMichael Sit Wei Hong 	 * default value. So, we init RX & TX FIFO depths here
695e2a54350SMichael Sit Wei Hong 	 * so that it is configured correctly later in dp83867_config_init();
696e2a54350SMichael Sit Wei Hong 	 */
697e2a54350SMichael Sit Wei Hong 	dp83867->tx_fifo_depth = DP83867_PHYCR_FIFO_DEPTH_4_B_NIB;
698e2a54350SMichael Sit Wei Hong 	dp83867->rx_fifo_depth = DP83867_PHYCR_FIFO_DEPTH_4_B_NIB;
699e2a54350SMichael Sit Wei Hong 
7002a10154aSDan Murphy 	return 0;
7012a10154aSDan Murphy }
7022a10154aSDan Murphy #endif /* CONFIG_OF_MDIO */
7032a10154aSDan Murphy 
dp83867_suspend(struct phy_device * phydev)704c5a8027dSAlexander Stein static int dp83867_suspend(struct phy_device *phydev)
705c5a8027dSAlexander Stein {
706c5a8027dSAlexander Stein 	/* Disable PHY Interrupts */
707c5a8027dSAlexander Stein 	if (phy_interrupt_is_valid(phydev)) {
708c5a8027dSAlexander Stein 		phydev->interrupts = PHY_INTERRUPT_DISABLED;
709c5a8027dSAlexander Stein 		dp83867_config_intr(phydev);
710c5a8027dSAlexander Stein 	}
711c5a8027dSAlexander Stein 
712c5a8027dSAlexander Stein 	return genphy_suspend(phydev);
713c5a8027dSAlexander Stein }
714c5a8027dSAlexander Stein 
dp83867_resume(struct phy_device * phydev)715c5a8027dSAlexander Stein static int dp83867_resume(struct phy_device *phydev)
716c5a8027dSAlexander Stein {
717c5a8027dSAlexander Stein 	/* Enable PHY Interrupts */
718c5a8027dSAlexander Stein 	if (phy_interrupt_is_valid(phydev)) {
719c5a8027dSAlexander Stein 		phydev->interrupts = PHY_INTERRUPT_ENABLED;
720c5a8027dSAlexander Stein 		dp83867_config_intr(phydev);
721c5a8027dSAlexander Stein 	}
722c5a8027dSAlexander Stein 
723c5a8027dSAlexander Stein 	genphy_resume(phydev);
724c5a8027dSAlexander Stein 
725c5a8027dSAlexander Stein 	return 0;
726c5a8027dSAlexander Stein }
727c5a8027dSAlexander Stein 
dp83867_probe(struct phy_device * phydev)728565d9d22STrent Piepho static int dp83867_probe(struct phy_device *phydev)
7292a10154aSDan Murphy {
7302a10154aSDan Murphy 	struct dp83867_private *dp83867;
7312a10154aSDan Murphy 
732e5a03bfdSAndrew Lunn 	dp83867 = devm_kzalloc(&phydev->mdio.dev, sizeof(*dp83867),
7332a10154aSDan Murphy 			       GFP_KERNEL);
7342a10154aSDan Murphy 	if (!dp83867)
7352a10154aSDan Murphy 		return -ENOMEM;
7362a10154aSDan Murphy 
7372a10154aSDan Murphy 	phydev->priv = dp83867;
738565d9d22STrent Piepho 
739ef87f7daSGrygorii Strashko 	return dp83867_of_init(phydev);
740565d9d22STrent Piepho }
741565d9d22STrent Piepho 
dp83867_config_init(struct phy_device * phydev)742565d9d22STrent Piepho static int dp83867_config_init(struct phy_device *phydev)
743565d9d22STrent Piepho {
744565d9d22STrent Piepho 	struct dp83867_private *dp83867 = phydev->priv;
745565d9d22STrent Piepho 	int ret, val, bs;
746565d9d22STrent Piepho 	u16 delay;
747565d9d22STrent Piepho 
748cd26d72dSDan Murphy 	/* Force speed optimization for the PHY even if it strapped */
749cd26d72dSDan Murphy 	ret = phy_modify(phydev, DP83867_CFG2, DP83867_DOWNSHIFT_EN,
750cd26d72dSDan Murphy 			 DP83867_DOWNSHIFT_EN);
751cd26d72dSDan Murphy 	if (ret)
752cd26d72dSDan Murphy 		return ret;
753cd26d72dSDan Murphy 
754fafc5db2SGrygorii Strashko 	ret = dp83867_verify_rgmii_cfg(phydev);
755fafc5db2SGrygorii Strashko 	if (ret)
756fafc5db2SGrygorii Strashko 		return ret;
757fafc5db2SGrygorii Strashko 
75837144476SMurali Karicheri 	/* RX_DV/RX_CTRL strapped in mode 1 or mode 2 workaround */
759b52c018dSHeiner Kallweit 	if (dp83867->rxctrl_strap_quirk)
760b52c018dSHeiner Kallweit 		phy_clear_bits_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4,
761b52c018dSHeiner Kallweit 				   BIT(7));
76237144476SMurali Karicheri 
763749f6f68SGrygorii Strashko 	bs = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_STRAP_STS2);
764749f6f68SGrygorii Strashko 	if (bs & DP83867_STRAP_STS2_STRAP_FLD) {
765749f6f68SGrygorii Strashko 		/* When using strap to enable FLD, the ENERGY_LOST_FLD_THR will
766749f6f68SGrygorii Strashko 		 * be set to 0x2. This may causes the PHY link to be unstable -
767749f6f68SGrygorii Strashko 		 * the default value 0x1 need to be restored.
768749f6f68SGrygorii Strashko 		 */
769749f6f68SGrygorii Strashko 		ret = phy_modify_mmd(phydev, DP83867_DEVADDR,
770749f6f68SGrygorii Strashko 				     DP83867_FLD_THR_CFG,
771749f6f68SGrygorii Strashko 				     DP83867_FLD_THR_CFG_ENERGY_LOST_THR_MASK,
772749f6f68SGrygorii Strashko 				     0x1);
773749f6f68SGrygorii Strashko 		if (ret)
774749f6f68SGrygorii Strashko 			return ret;
775749f6f68SGrygorii Strashko 	}
776749f6f68SGrygorii Strashko 
777e02d1816SDan Murphy 	if (phy_interface_is_rgmii(phydev) ||
778e02d1816SDan Murphy 	    phydev->interface == PHY_INTERFACE_MODE_SGMII) {
779e02d1816SDan Murphy 		val = phy_read(phydev, MII_DP83867_PHYCTRL);
780e02d1816SDan Murphy 		if (val < 0)
781e02d1816SDan Murphy 			return val;
782e02d1816SDan Murphy 
783e02d1816SDan Murphy 		val &= ~DP83867_PHYCR_TX_FIFO_DEPTH_MASK;
784e02d1816SDan Murphy 		val |= (dp83867->tx_fifo_depth <<
785e02d1816SDan Murphy 			DP83867_PHYCR_TX_FIFO_DEPTH_SHIFT);
786e02d1816SDan Murphy 
787e02d1816SDan Murphy 		if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
788e02d1816SDan Murphy 			val &= ~DP83867_PHYCR_RX_FIFO_DEPTH_MASK;
789e02d1816SDan Murphy 			val |= (dp83867->rx_fifo_depth <<
790e02d1816SDan Murphy 				DP83867_PHYCR_RX_FIFO_DEPTH_SHIFT);
791e02d1816SDan Murphy 		}
792e02d1816SDan Murphy 
793e02d1816SDan Murphy 		ret = phy_write(phydev, MII_DP83867_PHYCTRL, val);
794e02d1816SDan Murphy 		if (ret)
795e02d1816SDan Murphy 			return ret;
796e02d1816SDan Murphy 	}
797e02d1816SDan Murphy 
7982a10154aSDan Murphy 	if (phy_interface_is_rgmii(phydev)) {
799b291c418SStefan Hauser 		val = phy_read(phydev, MII_DP83867_PHYCTRL);
800b291c418SStefan Hauser 		if (val < 0)
801b291c418SStefan Hauser 			return val;
802ac6e058bSLukasz Majewski 
803ac6e058bSLukasz Majewski 		/* The code below checks if "port mirroring" N/A MODE4 has been
804ac6e058bSLukasz Majewski 		 * enabled during power on bootstrap.
805ac6e058bSLukasz Majewski 		 *
806ac6e058bSLukasz Majewski 		 * Such N/A mode enabled by mistake can put PHY IC in some
807ac6e058bSLukasz Majewski 		 * internal testing mode and disable RGMII transmission.
808ac6e058bSLukasz Majewski 		 *
809ac6e058bSLukasz Majewski 		 * In this particular case one needs to check STRAP_STS1
810ac6e058bSLukasz Majewski 		 * register's bit 11 (marked as RESERVED).
811ac6e058bSLukasz Majewski 		 */
812ac6e058bSLukasz Majewski 
813a6d99fcdSRussell King 		bs = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_STRAP_STS1);
814ac6e058bSLukasz Majewski 		if (bs & DP83867_STRAP_STS1_RESERVED)
815ac6e058bSLukasz Majewski 			val &= ~DP83867_PHYCR_RESERVED_MASK;
816ac6e058bSLukasz Majewski 
817b291c418SStefan Hauser 		ret = phy_write(phydev, MII_DP83867_PHYCTRL, val);
8182a10154aSDan Murphy 		if (ret)
8192a10154aSDan Murphy 			return ret;
8202a10154aSDan Murphy 
821c11669a2STrent Piepho 		/* If rgmii mode with no internal delay is selected, we do NOT use
822c11669a2STrent Piepho 		 * aligned mode as one might expect.  Instead we use the PHY's default
823c11669a2STrent Piepho 		 * based on pin strapping.  And the "mode 0" default is to *use*
824c11669a2STrent Piepho 		 * internal delay with a value of 7 (2.00 ns).
825b4b12b0dSDavid S. Miller 		 *
826b4b12b0dSDavid S. Miller 		 * Set up RGMII delays
827c11669a2STrent Piepho 		 */
828a6d99fcdSRussell King 		val = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_RGMIICTL);
8292a10154aSDan Murphy 
830c11669a2STrent Piepho 		val &= ~(DP83867_RGMII_TX_CLK_DELAY_EN | DP83867_RGMII_RX_CLK_DELAY_EN);
8312a10154aSDan Murphy 		if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
8322a10154aSDan Murphy 			val |= (DP83867_RGMII_TX_CLK_DELAY_EN | DP83867_RGMII_RX_CLK_DELAY_EN);
8332a10154aSDan Murphy 
8342a10154aSDan Murphy 		if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
8352a10154aSDan Murphy 			val |= DP83867_RGMII_TX_CLK_DELAY_EN;
8362a10154aSDan Murphy 
8372a10154aSDan Murphy 		if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
8382a10154aSDan Murphy 			val |= DP83867_RGMII_RX_CLK_DELAY_EN;
8392a10154aSDan Murphy 
840a6d99fcdSRussell King 		phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RGMIICTL, val);
8412a10154aSDan Murphy 
842fafc5db2SGrygorii Strashko 		delay = 0;
843fafc5db2SGrygorii Strashko 		if (dp83867->rx_id_delay != DP83867_RGMII_RX_CLK_DELAY_INV)
844fafc5db2SGrygorii Strashko 			delay |= dp83867->rx_id_delay;
845fafc5db2SGrygorii Strashko 		if (dp83867->tx_id_delay != DP83867_RGMII_TX_CLK_DELAY_INV)
846fafc5db2SGrygorii Strashko 			delay |= dp83867->tx_id_delay <<
847fafc5db2SGrygorii Strashko 				 DP83867_RGMII_TX_CLK_DELAY_SHIFT;
8482a10154aSDan Murphy 
849a6d99fcdSRussell King 		phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RGMIIDCTL,
850a6d99fcdSRussell King 			      delay);
85127708eb5STrent Piepho 	}
852ed838fe9SMugunthan V N 
85327708eb5STrent Piepho 	/* If specified, set io impedance */
854b52c018dSHeiner Kallweit 	if (dp83867->io_impedance >= 0)
855b52c018dSHeiner Kallweit 		phy_modify_mmd(phydev, DP83867_DEVADDR, DP83867_IO_MUX_CFG,
85627708eb5STrent Piepho 			       DP83867_IO_MUX_CFG_IO_IMPEDANCE_MASK,
85727708eb5STrent Piepho 			       dp83867->io_impedance);
8582a10154aSDan Murphy 
859333061b9SMax Uvarov 	if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
860333061b9SMax Uvarov 		/* For support SPEED_10 in SGMII mode
861333061b9SMax Uvarov 		 * DP83867_10M_SGMII_RATE_ADAPT bit
862333061b9SMax Uvarov 		 * has to be cleared by software. That
863333061b9SMax Uvarov 		 * does not affect SPEED_100 and
864333061b9SMax Uvarov 		 * SPEED_1000.
865333061b9SMax Uvarov 		 */
866333061b9SMax Uvarov 		ret = phy_modify_mmd(phydev, DP83867_DEVADDR,
867333061b9SMax Uvarov 				     DP83867_10M_SGMII_CFG,
868333061b9SMax Uvarov 				     DP83867_10M_SGMII_RATE_ADAPT_MASK,
869333061b9SMax Uvarov 				     0);
870333061b9SMax Uvarov 		if (ret)
871333061b9SMax Uvarov 			return ret;
8721a97a477SMax Uvarov 
8731a97a477SMax Uvarov 		/* After reset SGMII Autoneg timer is set to 2us (bits 6 and 5
8741a97a477SMax Uvarov 		 * are 01). That is not enough to finalize autoneg on some
8751a97a477SMax Uvarov 		 * devices. Increase this timer duration to maximum 16ms.
8761a97a477SMax Uvarov 		 */
8771a97a477SMax Uvarov 		ret = phy_modify_mmd(phydev, DP83867_DEVADDR,
8781a97a477SMax Uvarov 				     DP83867_CFG4,
8791a97a477SMax Uvarov 				     DP83867_CFG4_SGMII_ANEG_MASK,
8801a97a477SMax Uvarov 				     DP83867_CFG4_SGMII_ANEG_TIMER_16MS);
8811a97a477SMax Uvarov 
8821a97a477SMax Uvarov 		if (ret)
8831a97a477SMax Uvarov 			return ret;
884507ddd5cSVitaly Gaiduk 
885507ddd5cSVitaly Gaiduk 		val = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_SGMIICTL);
886507ddd5cSVitaly Gaiduk 		/* SGMII type is set to 4-wire mode by default.
887507ddd5cSVitaly Gaiduk 		 * If we place appropriate property in dts (see above)
888507ddd5cSVitaly Gaiduk 		 * switch on 6-wire mode.
889507ddd5cSVitaly Gaiduk 		 */
890507ddd5cSVitaly Gaiduk 		if (dp83867->sgmii_ref_clk_en)
891507ddd5cSVitaly Gaiduk 			val |= DP83867_SGMII_TYPE;
892507ddd5cSVitaly Gaiduk 		else
893507ddd5cSVitaly Gaiduk 			val &= ~DP83867_SGMII_TYPE;
894507ddd5cSVitaly Gaiduk 		phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_SGMIICTL, val);
8950c9efbd5SHarini Katakam 
8960c9efbd5SHarini Katakam 		/* This is a SW workaround for link instability if RX_CTRL is
8970c9efbd5SHarini Katakam 		 * not strapped to mode 3 or 4 in HW. This is required for SGMII
8980c9efbd5SHarini Katakam 		 * in addition to clearing bit 7, handled above.
8990c9efbd5SHarini Katakam 		 */
9000c9efbd5SHarini Katakam 		if (dp83867->rxctrl_strap_quirk)
9010c9efbd5SHarini Katakam 			phy_set_bits_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4,
9020c9efbd5SHarini Katakam 					 BIT(8));
903333061b9SMax Uvarov 	}
904333061b9SMax Uvarov 
9055ca7d1caSGrygorii Strashko 	val = phy_read(phydev, DP83867_CFG3);
9065a7f08c2SGrygorii Strashko 	/* Enable Interrupt output INT_OE in CFG3 register */
9075a7f08c2SGrygorii Strashko 	if (phy_interrupt_is_valid(phydev))
9085a7f08c2SGrygorii Strashko 		val |= DP83867_CFG3_INT_OE;
9095a7f08c2SGrygorii Strashko 
9105a7f08c2SGrygorii Strashko 	val |= DP83867_CFG3_ROBUST_AUTO_MDIX;
9115ca7d1caSGrygorii Strashko 	phy_write(phydev, DP83867_CFG3, val);
9125ca7d1caSGrygorii Strashko 
913fc6d39c3SLukasz Majewski 	if (dp83867->port_mirroring != DP83867_PORT_MIRROING_KEEP)
914fc6d39c3SLukasz Majewski 		dp83867_config_port_mirroring(phydev);
915fc6d39c3SLukasz Majewski 
9169708fb63SWadim Egorov 	/* Clock output selection if muxing property is set */
91713c83cf8STrent Piepho 	if (dp83867->set_clk_output) {
91813c83cf8STrent Piepho 		u16 mask = DP83867_IO_MUX_CFG_CLK_O_DISABLE;
91913c83cf8STrent Piepho 
92013c83cf8STrent Piepho 		if (dp83867->clk_output_sel == DP83867_CLK_O_SEL_OFF) {
92113c83cf8STrent Piepho 			val = DP83867_IO_MUX_CFG_CLK_O_DISABLE;
92213c83cf8STrent Piepho 		} else {
92313c83cf8STrent Piepho 			mask |= DP83867_IO_MUX_CFG_CLK_O_SEL_MASK;
92413c83cf8STrent Piepho 			val = dp83867->clk_output_sel <<
92513c83cf8STrent Piepho 			      DP83867_IO_MUX_CFG_CLK_O_SEL_SHIFT;
92613c83cf8STrent Piepho 		}
92713c83cf8STrent Piepho 
928b52c018dSHeiner Kallweit 		phy_modify_mmd(phydev, DP83867_DEVADDR, DP83867_IO_MUX_CFG,
92913c83cf8STrent Piepho 			       mask, val);
93013c83cf8STrent Piepho 	}
9319708fb63SWadim Egorov 
9322a10154aSDan Murphy 	return 0;
9332a10154aSDan Murphy }
9342a10154aSDan Murphy 
dp83867_phy_reset(struct phy_device * phydev)9352a10154aSDan Murphy static int dp83867_phy_reset(struct phy_device *phydev)
9362a10154aSDan Murphy {
9372a10154aSDan Murphy 	int err;
9382a10154aSDan Murphy 
939*a129b41fSFrancesco Dolcini 	err = phy_write(phydev, DP83867_CTRL, DP83867_SW_RESET);
9402a10154aSDan Murphy 	if (err < 0)
9412a10154aSDan Murphy 		return err;
9422a10154aSDan Murphy 
94372a7d452SMax Uvarov 	usleep_range(10, 20);
94472a7d452SMax Uvarov 
9450b01db27SGrygorii Strashko 	err = phy_modify(phydev, MII_DP83867_PHYCTRL,
94686ffe920SMichael Grzeschik 			 DP83867_PHYCR_FORCE_LINK_GOOD, 0);
9470b01db27SGrygorii Strashko 	if (err < 0)
9480b01db27SGrygorii Strashko 		return err;
9490b01db27SGrygorii Strashko 
9500b01db27SGrygorii Strashko 	/* Configure the DSP Feedforward Equalizer Configuration register to
9510b01db27SGrygorii Strashko 	 * improve short cable (< 1 meter) performance. This will not affect
9520b01db27SGrygorii Strashko 	 * long cable performance.
9530b01db27SGrygorii Strashko 	 */
9540b01db27SGrygorii Strashko 	err = phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_DSP_FFE_CFG,
9550b01db27SGrygorii Strashko 			    0x0e81);
9560b01db27SGrygorii Strashko 	if (err < 0)
9570b01db27SGrygorii Strashko 		return err;
9580b01db27SGrygorii Strashko 
9590b01db27SGrygorii Strashko 	err = phy_write(phydev, DP83867_CTRL, DP83867_SW_RESTART);
9600b01db27SGrygorii Strashko 	if (err < 0)
9610b01db27SGrygorii Strashko 		return err;
9620b01db27SGrygorii Strashko 
9630b01db27SGrygorii Strashko 	usleep_range(10, 20);
9640b01db27SGrygorii Strashko 
9650b01db27SGrygorii Strashko 	return 0;
9662a10154aSDan Murphy }
9672a10154aSDan Murphy 
dp83867_link_change_notify(struct phy_device * phydev)968c76acfb7STan Tee Min static void dp83867_link_change_notify(struct phy_device *phydev)
969c76acfb7STan Tee Min {
970c76acfb7STan Tee Min 	/* There is a limitation in DP83867 PHY device where SGMII AN is
971c76acfb7STan Tee Min 	 * only triggered once after the device is booted up. Even after the
972c76acfb7STan Tee Min 	 * PHY TPI is down and up again, SGMII AN is not triggered and
973c76acfb7STan Tee Min 	 * hence no new in-band message from PHY to MAC side SGMII.
974c76acfb7STan Tee Min 	 * This could cause an issue during power up, when PHY is up prior
975c76acfb7STan Tee Min 	 * to MAC. At this condition, once MAC side SGMII is up, MAC side
976c76acfb7STan Tee Min 	 * SGMII wouldn`t receive new in-band message from TI PHY with
977c76acfb7STan Tee Min 	 * correct link status, speed and duplex info.
978c76acfb7STan Tee Min 	 * Thus, implemented a SW solution here to retrigger SGMII Auto-Neg
979c76acfb7STan Tee Min 	 * whenever there is a link change.
980c76acfb7STan Tee Min 	 */
981c76acfb7STan Tee Min 	if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
982c76acfb7STan Tee Min 		int val = 0;
983c76acfb7STan Tee Min 
984c76acfb7STan Tee Min 		val = phy_clear_bits(phydev, DP83867_CFG2,
985c76acfb7STan Tee Min 				     DP83867_SGMII_AUTONEG_EN);
986c76acfb7STan Tee Min 		if (val < 0)
987c76acfb7STan Tee Min 			return;
988c76acfb7STan Tee Min 
989c76acfb7STan Tee Min 		phy_set_bits(phydev, DP83867_CFG2,
990c76acfb7STan Tee Min 			     DP83867_SGMII_AUTONEG_EN);
991c76acfb7STan Tee Min 	}
992c76acfb7STan Tee Min }
993c76acfb7STan Tee Min 
dp83867_loopback(struct phy_device * phydev,bool enable)99413bd8558STan Tee Min static int dp83867_loopback(struct phy_device *phydev, bool enable)
99513bd8558STan Tee Min {
99613bd8558STan Tee Min 	return phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK,
99713bd8558STan Tee Min 			  enable ? BMCR_LOOPBACK : 0);
99813bd8558STan Tee Min }
99913bd8558STan Tee Min 
1000938f65adSAlexander Stein static int
dp83867_led_brightness_set(struct phy_device * phydev,u8 index,enum led_brightness brightness)1001938f65adSAlexander Stein dp83867_led_brightness_set(struct phy_device *phydev,
1002938f65adSAlexander Stein 			   u8 index, enum led_brightness brightness)
1003938f65adSAlexander Stein {
1004938f65adSAlexander Stein 	u32 val;
1005938f65adSAlexander Stein 
1006938f65adSAlexander Stein 	if (index >= DP83867_LED_COUNT)
1007938f65adSAlexander Stein 		return -EINVAL;
1008938f65adSAlexander Stein 
1009938f65adSAlexander Stein 	/* DRV_EN==1: output is DRV_VAL */
1010938f65adSAlexander Stein 	val = DP83867_LED_DRV_EN(index);
1011938f65adSAlexander Stein 
1012938f65adSAlexander Stein 	if (brightness)
1013938f65adSAlexander Stein 		val |= DP83867_LED_DRV_VAL(index);
1014938f65adSAlexander Stein 
1015938f65adSAlexander Stein 	return phy_modify(phydev, DP83867_LEDCR2,
1016938f65adSAlexander Stein 			  DP83867_LED_DRV_VAL(index) |
1017938f65adSAlexander Stein 			  DP83867_LED_DRV_EN(index),
1018938f65adSAlexander Stein 			  val);
1019938f65adSAlexander Stein }
1020938f65adSAlexander Stein 
10212a10154aSDan Murphy static struct phy_driver dp83867_driver[] = {
10222a10154aSDan Murphy 	{
10232a10154aSDan Murphy 		.phy_id		= DP83867_PHY_ID,
10242a10154aSDan Murphy 		.phy_id_mask	= 0xfffffff0,
10252a10154aSDan Murphy 		.name		= "TI DP83867",
1026dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
10272a10154aSDan Murphy 
1028565d9d22STrent Piepho 		.probe          = dp83867_probe,
10292a10154aSDan Murphy 		.config_init	= dp83867_config_init,
10302a10154aSDan Murphy 		.soft_reset	= dp83867_phy_reset,
10312a10154aSDan Murphy 
1032cd26d72dSDan Murphy 		.read_status	= dp83867_read_status,
1033cd26d72dSDan Murphy 		.get_tunable	= dp83867_get_tunable,
1034cd26d72dSDan Murphy 		.set_tunable	= dp83867_set_tunable,
1035cd26d72dSDan Murphy 
1036caabee5bSThomas Haemmerle 		.get_wol	= dp83867_get_wol,
1037caabee5bSThomas Haemmerle 		.set_wol	= dp83867_set_wol,
1038caabee5bSThomas Haemmerle 
10392a10154aSDan Murphy 		/* IRQ related */
10402a10154aSDan Murphy 		.config_intr	= dp83867_config_intr,
10411d1ae3c6SIoana Ciornei 		.handle_interrupt = dp83867_handle_interrupt,
10422a10154aSDan Murphy 
1043c5a8027dSAlexander Stein 		.suspend	= dp83867_suspend,
1044c5a8027dSAlexander Stein 		.resume		= dp83867_resume,
1045c76acfb7STan Tee Min 
1046c76acfb7STan Tee Min 		.link_change_notify = dp83867_link_change_notify,
104713bd8558STan Tee Min 		.set_loopback	= dp83867_loopback,
1048938f65adSAlexander Stein 
1049938f65adSAlexander Stein 		.led_brightness_set = dp83867_led_brightness_set,
10502a10154aSDan Murphy 	},
10512a10154aSDan Murphy };
10522a10154aSDan Murphy module_phy_driver(dp83867_driver);
10532a10154aSDan Murphy 
10542a10154aSDan Murphy static struct mdio_device_id __maybe_unused dp83867_tbl[] = {
10552a10154aSDan Murphy 	{ DP83867_PHY_ID, 0xfffffff0 },
10562a10154aSDan Murphy 	{ }
10572a10154aSDan Murphy };
10582a10154aSDan Murphy 
10592a10154aSDan Murphy MODULE_DEVICE_TABLE(mdio, dp83867_tbl);
10602a10154aSDan Murphy 
10612a10154aSDan Murphy MODULE_DESCRIPTION("Texas Instruments DP83867 PHY driver");
10622a10154aSDan Murphy MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com");
10635f857575SAndrew Lunn MODULE_LICENSE("GPL v2");
1064