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