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