DP83869HM: Interrupt is not de asserting

Part Number: DP83869HM
Other Parts Discussed in Thread: DP83869

Tool/software:

Hi all,

SOC: Zynq MPSoC (Xilinx/AMD)
MAC interface: GEM1
Phy & mode: DP83869 (TI) & RGMII copper

The interrupt is configured as Level triggered as below

&gem1 {
phy-mode = "rgmii-id";
status = "okay";
phy-handle = <&phy1>;
phy1: phy@1 {
reg = <1>;
compatible = "ethernet-phy-ieee802.3-c22";
rx-internal-delay-ps = <2000>;
tx-internal-delay-ps = <2000>;
interrupt-parent = <&gpio>;
interrupts = <78 8>; //EMIO 0 is used as INT
};
};

It is confirmed interrupt is configured using "macb ff0c0000.ethernet eth0: PHY [ff0c0000.ethernet-ffffffff:01] driver [TI DP83869] (irq=52)" and in cat /proc/interrupts also entry is there.

By default when no cable is connected, the INT line probes high & reads high. When cable is connected link is up, counter in cat /proc/interrupt is increased and the INT line probes low. But after link up, the interrupt line remains in low state which causes the spurious interrupt state and make the interrupt is disabled with below crash.
root@zynqmp-iwg36o:~# [ 1040.510999] irq 52: nobody cared (try booting with the "irqpoll" option)
[ 1040.517716] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 6.6.40-xilinx-gf2d46953498a #1
[ 1040.525463] Hardware name: xlnx,zynqmp (DT)
[ 1040.529639] Call trace:
[ 1040.532077] dump_backtrace+0x90/0xe8
[ 1040.535749] show_stack+0x18/0x24
[ 1040.539073] dump_stack_lvl+0x48/0x60
[ 1040.542736] dump_stack+0x18/0x24
[ 1040.546051] __report_bad_irq+0x38/0x120
[ 1040.549975] note_interrupt+0x310/0x360
[ 1040.553811] handle_irq_event+0xd8/0xe8
[ 1040.557648] handle_fasteoi_irq+0xb0/0x284
[ 1040.561745] generic_handle_domain_irq+0x2c/0x44
[ 1040.566371] zynq_gpio_irqhandler+0xa0/0x16c
[ 1040.570642] generic_handle_domain_irq+0x2c/0x44
[ 1040.575268] gic_handle_irq+0x6c/0x9c
[ 1040.578931] call_on_irq_stack+0x24/0x4c
[ 1040.582855] do_interrupt_handler+0x80/0x84
[ 1040.587038] el1_interrupt+0x34/0x54
[ 1040.590614] el1h_64_irq_handler+0x18/0x24
[ 1040.594711] el1h_64_irq+0x64/0x68
[ 1040.598114] default_idle_call+0x28/0x3c
[ 1040.602037] do_idle+0xac/0x100
[ 1040.605180] cpu_startup_entry+0x34/0x3c
[ 1040.609103] kernel_init+0x0/0x1e0
[ 1040.612505] arch_post_acpi_subsys_init+0x0/0x8
[ 1040.617036] start_kernel+0x504/0x60c
[ 1040.620699] __primary_switched+0xbc/0xc4
[ 1040.624710] handlers:
[ 1040.626975] [<000000008cd40a2e>] irq_default_primary_handler threaded [<00000000054f5ea5>] phy_interrupt
[ 1040.636480] Disabling IRQ #52

How to confirm if the PHY interrupt handler is invoked and serviced the interrupt (By reading the ISR register right)?
When reading the ISR register manually or in interrupt handler function after sometime it reads 0x0.

Please help in debugging why the interrupt remains low and not de-asserted.

  • Hi,

    PHY interrupt is serviced by reading the ISR register. This register will clear on read so you can only read it once and should deassert the interrupt once the register is read. 
    However, the interrupt can also self-clear if the register hasn't been read for 500ms in case the user doesn't have register access. This could be the reason why the interrupt handler function sometimes read 0x0. Lastly, once the interrupt is triggered, new interrupts are not routed until the current interrupt is cleared. This can be the reason why the interrupt is not being deasserted. If new interrupts are constantly being triggered, the interrupt will never seem to be clearing. 

    Have you tried enabling only one interrupt event and see if this event persists?

    Best,
    J

  • Hello J,

    Thanks for your reply.

    PHY interrupt is serviced by reading the ISR register. This register will clear on read so you can only read it once and should deassert the interrupt once the register is read.
    Bowya: OK. The register is read once in handler function as below. So it should be enough to clear the register.

    However, the interrupt can also self-clear if the register hasn't been read for 500ms in case the user doesn't have register access. This could be the reason why the interrupt handler function sometimes read 0x0.
    Bowya: How to know when the register access is not there? Even when register access is there, self-clear option also is applicable and valid? In datasheet, it says through pin option if register access is disabled then pin option, then 500ms self clearing.

    Lastly, once the interrupt is triggered, new interrupts are not routed until the current interrupt is cleared. This can be the reason why the interrupt is not being deasserted. If new interrupts are constantly being triggered, the interrupt will never seem to be clearing. Have you tried enabling only one interrupt event and see if this event persists?
    Bowya: Yes, I have tried with single interrupt enabled as well. The log & source attached. Please check and give your assistance.

    # ifconfig eth0 down
    [  426.619409] macb ff0c0000.ethernet eth0: Link is Down
    [  426.624754] 
    [  426.624754] Phy interrupt config_intr else condition in dp83869_config_intr: Micr_value:0
    [  426.627848] 
    [  426.627848] dp83869_handle_interrupt: IRQ handled, triggering PHY machine,irq_status:1024
    [  426.634518] 
    [  426.634518] Interrupt occured reading MII_DP83869_ISR : 0
    [  426.644125] 
    [  426.644125] dp83869_handle_interrupt: link=0 speed=1000 duplex=1
    [  426.663912] macb ff0c0000.ethernet: gem-ptp-timer ptp clock unregistered.
    # ifconfig eth0 up  
    [  455.596841] 
    [  455.596841] Enable Interrupt output INT_OE in CFG4 register in dp83869_config_init 
    [  455.606152] 
    [  455.606152] DP83869_CFG4 bit7:1
    [  455.611960] 
    [  455.611960] Enable Interrupt output INT_OE in CFG4 register in dp83869_config_init 
    [  455.621267] 
    [  455.621267] DP83869_CFG4 bit7:1
    [  455.626598] 
    [  455.626598] Phy interrupt config_intr else condition in dp83869_config_intr: Micr_value:0
    [  455.636366] 
    [  455.636366] Interrupt occured reading MII_DP83869_ISR : 0
    [  455.643332] macb ff0c0000.ethernet eth0: PHY [ff0c0000.ethernet-ffffffff:01] driver [TI DP83869] (irq=52)
    [  455.654415] 
    [  455.654415] Phy interrupt enabled if condition in dp83869_config_intr
    [  455.662426] 
    [  455.662426] Interrupt occured reading MII_DP83869_ISR : 0
    [  455.669375] 
    [  455.669375] MICR value default:0, dp83869_config_intr
    [  455.675986] 
    [  455.675986] MICR value:1024, dp83869_config_intr
    [  455.682108] macb ff0c0000.ethernet eth0: configuring for phy/rgmii-id link mode
    [  455.689881] pps pps0: new PPS source ptp0
    [  455.694583] macb ff0c0000.ethernet: gem-ptp-timer ptp clock registered.
    # cat /proc/interrupts | grep "zynq-gpio"
     52:     200004          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    # cat /proc/interrupts | grep "zynq-gpio"
     52:     200004          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    # [  458.463980] 
    [  458.463980] dp83869_handle_interrupt: IRQ handled, triggering PHY machine,irq_status:7232
    [  458.473673] 
    [  458.473673] dp83869_handle_interrupt: link=0 speed=-1 duplex=255
    
    # cat /proc/interrupts | grep "zynq-gpio"
     52:     238723          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    # cat /proc/interrupts | grep "zynq-gpio"
     52:     260231          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    # [  468.112202] irq 52: nobody cared (try booting with the "irqpoll" option)
    [  468.118915] CPU: 0 PID: 796 Comm: irq/52-ff0c0000 Not tainted 6.6.40-xilinx-ge8e854f06cab #1
    [  468.127356] Hardware name: xlnx,zynqmp (DT)
    [  468.131532] Call trace:
    [  468.133971]  dump_backtrace+0x90/0xe8
    [  468.137642]  show_stack+0x18/0x24
    [  468.140957]  dump_stack_lvl+0x48/0x60
    [  468.144620]  dump_stack+0x18/0x24
    [  468.147936]  __report_bad_irq+0x38/0x120
    [  468.151859]  note_interrupt+0x310/0x360
    [  468.155696]  handle_irq_event+0xd8/0xe8
    [  468.159533]  handle_fasteoi_irq+0xb0/0x284
    [  468.163629]  generic_handle_domain_irq+0x2c/0x44
    [  468.168256]  zynq_gpio_irqhandler+0xa0/0x16c
    [  468.172526]  generic_handle_domain_irq+0x2c/0x44
    [  468.177153]  gic_handle_irq+0x6c/0x9c
    [  468.180816]  call_on_irq_stack+0x24/0x4c
    [  468.184739]  do_interrupt_handler+0x80/0x84
    [  468.188923]  el1_interrupt+0x34/0x54
    [  468.192499]  el1h_64_irq_handler+0x18/0x24
    [  468.196596]  el1h_64_irq+0x64/0x68
    [  468.199999]  irq_finalize_oneshot.part.0+0x80/0x150
    [  468.204877]  irq_thread_fn+0x60/0xa8
    [  468.208453]  irq_thread+0x170/0x294
    [  468.211942]  kthread+0xe0/0xe4
    [  468.214998]  ret_from_fork+0x10/0x20
    [  468.218575] handlers:
    [  468.220839] [<00000000c7f40757>] irq_default_primary_handler threaded [<0000000078af64fc>] phy_interrupt
    [  468.230344] Disabling IRQ #52
    [  468.235635] macb ff0c0000.ethernet eth0: Link is Up - 1Gbps/Full - flow control off
    
    Broadcast message from systemd-journald@zynqmp (Tue 2024-02-27 09:33:40 PST):
    
    kernel[687]: [  468.230344] Disabling IRQ #52
    
    
    # #CABLE DISCONNECT
    # [  481.335816] 
    [  481.335816] dp83869_handle_interrupt: IRQ handled, triggering PHY machine,irq_status:1284
    [  481.345506] 
    [  481.345506] dp83869_handle_interrupt: link=1 speed=1000 duplex=1
    [  481.353370] macb ff0c0000.ethernet eth0: Link is Down
    
    # cat /proc/interrupts | grep "zynq-gpio"
     52:     300006          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    # cat /proc/interrupts | grep "zynq-gpio"
     52:     300006          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    # #CABLE CONNECT
    # [  498.079813] 
    [  498.079813] dp83869_handle_interrupt: IRQ handled, triggering PHY machine,irq_status:3072
    [  498.089501] 
    [  498.089501] dp83869_handle_interrupt: link=0 speed=1000 duplex=1
    [  498.097782] macb ff0c0000.ethernet eth0: Link is Up - 1Gbps/Full - flow control off
    
    # cat /proc/interrupts | grep "zynq-gpio"
     52:     300006          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    # cat /proc/interrupts | grep "zynq-gpio"
     52:     300006          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01 
    
    
    // SPDX-License-Identifier: GPL-2.0
    /* Driver for the Texas Instruments DP83869 PHY
     * Copyright (C) 2019 Texas Instruments Inc.
     */
    
    #include <linux/ethtool.h>
    #include <linux/etherdevice.h>
    #include <linux/kernel.h>
    #include <linux/mii.h>
    #include <linux/module.h>
    #include <linux/of.h>
    #include <linux/phy.h>
    #include <linux/delay.h>
    #include <linux/bitfield.h>
    
    #include <dt-bindings/net/ti-dp83869.h>
    
    #define DP83869_PHY_ID		0x2000a0f1
    #define DP83561_PHY_ID		0x2000a1a4
    #define DP83869_DEVADDR		0x1f
    
    #define MII_DP83869_PHYCTRL	0x10
    #define MII_DP83869_MICR	0x12
    #define MII_DP83869_ISR		0x13
    #define DP83869_CFG2		0x14
    #define DP83869_CTRL		0x1f
    #define DP83869_CFG4		0x1e
    #define DP83869_FX_INT_STS	0xC19
    
    /* Extended Registers */
    #define DP83869_GEN_CFG3        0x0031
    #define DP83869_RGMIICTL	0x0032
    #define DP83869_STRAP_STS1	0x006e
    #define DP83869_RGMIIDCTL	0x0086
    #define DP83869_RXFCFG		0x0134
    #define DP83869_RXFPMD1		0x0136
    #define DP83869_RXFPMD2		0x0137
    #define DP83869_RXFPMD3		0x0138
    #define DP83869_RXFSOP1		0x0139
    #define DP83869_RXFSOP2		0x013A
    #define DP83869_RXFSOP3		0x013B
    #define DP83869_IO_MUX_CFG	0x0170
    #define DP83869_OP_MODE		0x01df
    #define DP83869_FX_CTRL		0x0c00
    
    #define DP83869_SW_RESET	BIT(15)
    #define DP83869_SW_RESTART	BIT(14)
    
    /* MICR Interrupt bits */
    #define MII_DP83869_MICR_AN_ERR_INT_EN		BIT(15)
    #define MII_DP83869_MICR_SPEED_CHNG_INT_EN	BIT(14)
    #define MII_DP83869_MICR_DUP_MODE_CHNG_INT_EN	BIT(13)
    #define MII_DP83869_MICR_PAGE_RXD_INT_EN	BIT(12)
    #define MII_DP83869_MICR_AUTONEG_COMP_INT_EN	BIT(11)
    #define MII_DP83869_MICR_LINK_STS_CHNG_INT_EN	BIT(10)
    #define MII_DP83869_MICR_FALSE_CARRIER_INT_EN	BIT(8)
    #define MII_DP83869_MICR_SLEEP_MODE_CHNG_INT_EN	BIT(4)
    #define MII_DP83869_MICR_WOL_INT_EN		BIT(3)
    #define MII_DP83869_MICR_XGMII_ERR_INT_EN	BIT(2)
    #define MII_DP83869_MICR_POL_CHNG_INT_EN	BIT(1)
    #define MII_DP83869_MICR_JABBER_INT_EN		BIT(0)
    
    #define MII_DP83869_BMCR_DEFAULT	(BMCR_ANENABLE | \
    					 BMCR_FULLDPLX | \
    					 BMCR_SPEED1000)
    
    #define MII_DP83869_FIBER_ADVERTISE    (ADVERTISED_FIBRE | \
    					ADVERTISED_Pause | \
    					ADVERTISED_Asym_Pause)
    
    /* This is the same bit mask as the BMCR so re-use the BMCR default */
    #define DP83869_FX_CTRL_DEFAULT	MII_DP83869_BMCR_DEFAULT
    
    /* CFG1 bits */
    #define DP83869_CFG1_DEFAULT	(ADVERTISE_1000HALF | \
    				 ADVERTISE_1000FULL | \
    				 CTL1000_AS_MASTER)
    
    /* RGMIICTL bits */
    #define DP83869_RGMII_TX_CLK_DELAY_EN		BIT(1)
    #define DP83869_RGMII_RX_CLK_DELAY_EN		BIT(0)
    
    /* RGMIIDCTL */
    #define DP83869_RGMII_CLK_DELAY_SHIFT		4
    #define DP83869_CLK_DELAY_DEF			7
    
    /* STRAP_STS1 bits */
    #define DP83869_STRAP_OP_MODE_MASK		GENMASK(2, 0)
    #define DP83869_STRAP_STS1_RESERVED		BIT(11)
    #define DP83869_STRAP_MIRROR_ENABLED           BIT(12)
    
    /* PHYCTRL bits */
    #define DP83869_RX_FIFO_SHIFT	12
    #define DP83869_TX_FIFO_SHIFT	14
    
    /* PHY_CTRL lower bytes 0x48 are declared as reserved */
    #define DP83869_PHY_CTRL_DEFAULT	0x48
    #define DP83869_PHYCR_FIFO_DEPTH_MASK	GENMASK(15, 12)
    #define DP83869_PHYCR_RESERVED_MASK	BIT(11)
    
    /* IO_MUX_CFG bits */
    #define DP83869_IO_MUX_CFG_IO_IMPEDANCE_CTRL	0x1f
    
    #define DP83869_IO_MUX_CFG_IO_IMPEDANCE_MAX	0x0
    #define DP83869_IO_MUX_CFG_IO_IMPEDANCE_MIN	0x1f
    #define DP83869_IO_MUX_CFG_CLK_O_SEL_MASK	(0x1f << 8)
    #define DP83869_IO_MUX_CFG_CLK_O_SEL_SHIFT	8
    
    /* CFG3 bits */
    #define DP83869_CFG3_PORT_MIRROR_EN              BIT(0)
    
    /* CFG4 bits */
    #define DP83869_INT_OE	BIT(7)
    
    /* OP MODE */
    #define DP83869_OP_MODE_MII			BIT(5)
    #define DP83869_SGMII_RGMII_BRIDGE		BIT(6)
    
    /* RXFCFG bits*/
    #define DP83869_WOL_MAGIC_EN		BIT(0)
    #define DP83869_WOL_PATTERN_EN		BIT(1)
    #define DP83869_WOL_BCAST_EN		BIT(2)
    #define DP83869_WOL_UCAST_EN		BIT(4)
    #define DP83869_WOL_SEC_EN		BIT(5)
    #define DP83869_WOL_ENH_MAC		BIT(7)
    
    /* CFG2 bits */
    #define DP83869_DOWNSHIFT_EN		(BIT(8) | BIT(9))
    #define DP83869_DOWNSHIFT_ATTEMPT_MASK	(BIT(10) | BIT(11))
    #define DP83869_DOWNSHIFT_1_COUNT_VAL	0
    #define DP83869_DOWNSHIFT_2_COUNT_VAL	1
    #define DP83869_DOWNSHIFT_4_COUNT_VAL	2
    #define DP83869_DOWNSHIFT_8_COUNT_VAL	3
    #define DP83869_DOWNSHIFT_1_COUNT	1
    #define DP83869_DOWNSHIFT_2_COUNT	2
    #define DP83869_DOWNSHIFT_4_COUNT	4
    #define DP83869_DOWNSHIFT_8_COUNT	8
    
    enum {
    	DP83869_PORT_MIRRORING_KEEP,
    	DP83869_PORT_MIRRORING_EN,
    	DP83869_PORT_MIRRORING_DIS,
    };
    
    struct dp83869_private {
    	int tx_fifo_depth;
    	int rx_fifo_depth;
    	s32 rx_int_delay;
    	s32 tx_int_delay;
    	int io_impedance;
    	int port_mirroring;
    	bool rxctrl_strap_quirk;
    	int clk_output_sel;
    	int mode;
    };
    
    static int dp83869_read_status(struct phy_device *phydev)
    {
    	struct dp83869_private *dp83869 = phydev->priv;
    	int ret;
    
    	ret = genphy_read_status(phydev);
    	if (ret)
    		return ret;
    
    	if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported)) {
    		if (phydev->link) {
    			if (dp83869->mode == DP83869_RGMII_100_BASE)
    				phydev->speed = SPEED_100;
    		} else {
    			phydev->speed = SPEED_UNKNOWN;
    			phydev->duplex = DUPLEX_UNKNOWN;
    		}
    	}
    
    	return 0;
    }
    
    static int dp83869_ack_interrupt(struct phy_device *phydev)
    {
    	int err = phy_read(phydev, MII_DP83869_ISR);
    //	int dis_intr=0x0;
    	
    	printk ("\nInterrupt occured reading MII_DP83869_ISR : %d\n", err);
    	if (err < 0)
    		return err;
    
    //                phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_FX_INT_STS,dis_intr);
      //              err = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_FX_INT_STS);
                    if (err < 0)
                            return err;
    
    	return 0;
    }
    
    static int dp83869_config_intr(struct phy_device *phydev)
    {
    	int micr_status = 0, err;
    
    	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
    		printk ("\nPhy interrupt enabled if condition in %s\n",__func__);
    		err = dp83869_ack_interrupt(phydev);
    		if (err){
    			printk ("\nAlready in interrupt %s\n",__func__);
    			return err;
    		}
    
    		micr_status = phy_read(phydev, MII_DP83869_MICR);
    		if (micr_status < 0){
    			printk ("\nMICR Null or less than 0 %s\n",__func__);
    			return micr_status;
    		}
    
    			printk ("\nMICR value default:%d, %s\n",micr_status, __func__);
    #if 0
    		micr_status |=
    			(MII_DP83869_MICR_AN_ERR_INT_EN |
    			MII_DP83869_MICR_SPEED_CHNG_INT_EN |
    			MII_DP83869_MICR_AUTONEG_COMP_INT_EN |
    			MII_DP83869_MICR_LINK_STS_CHNG_INT_EN |
    			MII_DP83869_MICR_DUP_MODE_CHNG_INT_EN |
    			MII_DP83869_MICR_SLEEP_MODE_CHNG_INT_EN);
    #endif
    		micr_status |= 	MII_DP83869_MICR_LINK_STS_CHNG_INT_EN ;
    
    		err = phy_write(phydev, MII_DP83869_MICR, micr_status);
    			printk ("\nMICR value:%d, %s\n",micr_status, __func__);
    	} else {
    		printk ("\nPhy interrupt config_intr else condition in %s: Micr_value:%d\n",__func__,micr_status);
    		err = phy_write(phydev, MII_DP83869_MICR, micr_status);
    		if (err)
    			return err;
    
    		err = dp83869_ack_interrupt(phydev);
    	}
    
    	return err;
    }
    
    static irqreturn_t dp83869_handle_interrupt(struct phy_device *phydev)
    {
    	int irq_status, irq_enabled;
    
    	irq_status = phy_read(phydev, MII_DP83869_ISR);
    
    //	printk ("\n%s\tirq_status%d\n",__func__,irq_status);
    	if (irq_status < 0) {
    		phy_error(phydev);
    		return IRQ_NONE;
    	}
    
    	irq_enabled = phy_read(phydev, MII_DP83869_MICR);
    //	printk ("\n%s\tirq_enabled%d\n",__func__,irq_enabled);
    	if (irq_enabled < 0) {
    		phy_error(phydev);
    		return IRQ_NONE;
    	}
    
    	if (!(irq_status & irq_enabled))
    		return IRQ_NONE;
    	printk("\n%s: IRQ handled, triggering PHY machine,irq_status:%d\n",__func__,irq_status);
    	phy_trigger_machine(phydev);
    	printk("\n%s: link=%d speed=%d duplex=%d\n",__func__,
           phydev->link, phydev->speed, phydev->duplex);
    
    	return IRQ_HANDLED;
    }
    
    static int dp83869_set_wol(struct phy_device *phydev,
    			   struct ethtool_wolinfo *wol)
    {
    	struct net_device *ndev = phydev->attached_dev;
    	int val_rxcfg, val_micr;
    	const u8 *mac;
    	int ret;
    
    	val_rxcfg = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_RXFCFG);
    	if (val_rxcfg < 0)
    		return val_rxcfg;
    
    	val_micr = phy_read(phydev, MII_DP83869_MICR);
    	if (val_micr < 0)
    		return val_micr;
    
    	if (wol->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_UCAST |
    			    WAKE_BCAST)) {
    		val_rxcfg |= DP83869_WOL_ENH_MAC;
    		val_micr |= MII_DP83869_MICR_WOL_INT_EN;
    
    		if (wol->wolopts & WAKE_MAGIC ||
    		    wol->wolopts & WAKE_MAGICSECURE) {
    			mac = (const u8 *)ndev->dev_addr;
    
    			if (!is_valid_ether_addr(mac))
    				return -EINVAL;
    
    			ret = phy_write_mmd(phydev, DP83869_DEVADDR,
    					    DP83869_RXFPMD1,
    					    mac[1] << 8 | mac[0]);
    			if (ret)
    				return ret;
    
    			ret = phy_write_mmd(phydev, DP83869_DEVADDR,
    					    DP83869_RXFPMD2,
    					    mac[3] << 8 | mac[2]);
    			if (ret)
    				return ret;
    
    			ret = phy_write_mmd(phydev, DP83869_DEVADDR,
    					    DP83869_RXFPMD3,
    					    mac[5] << 8 | mac[4]);
    			if (ret)
    				return ret;
    
    			val_rxcfg |= DP83869_WOL_MAGIC_EN;
    		} else {
    			val_rxcfg &= ~DP83869_WOL_MAGIC_EN;
    		}
    
    		if (wol->wolopts & WAKE_MAGICSECURE) {
    			ret = phy_write_mmd(phydev, DP83869_DEVADDR,
    					    DP83869_RXFSOP1,
    					    (wol->sopass[1] << 8) | wol->sopass[0]);
    			if (ret)
    				return ret;
    
    			ret = phy_write_mmd(phydev, DP83869_DEVADDR,
    					    DP83869_RXFSOP2,
    					    (wol->sopass[3] << 8) | wol->sopass[2]);
    			if (ret)
    				return ret;
    			ret = phy_write_mmd(phydev, DP83869_DEVADDR,
    					    DP83869_RXFSOP3,
    					    (wol->sopass[5] << 8) | wol->sopass[4]);
    			if (ret)
    				return ret;
    
    			val_rxcfg |= DP83869_WOL_SEC_EN;
    		} else {
    			val_rxcfg &= ~DP83869_WOL_SEC_EN;
    		}
    
    		if (wol->wolopts & WAKE_UCAST)
    			val_rxcfg |= DP83869_WOL_UCAST_EN;
    		else
    			val_rxcfg &= ~DP83869_WOL_UCAST_EN;
    
    		if (wol->wolopts & WAKE_BCAST)
    			val_rxcfg |= DP83869_WOL_BCAST_EN;
    		else
    			val_rxcfg &= ~DP83869_WOL_BCAST_EN;
    	} else {
    		val_rxcfg &= ~DP83869_WOL_ENH_MAC;
    		val_micr &= ~MII_DP83869_MICR_WOL_INT_EN;
    	}
    
    	ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_RXFCFG, val_rxcfg);
    	if (ret)
    		return ret;
    
    	return phy_write(phydev, MII_DP83869_MICR, val_micr);
    }
    
    static void dp83869_get_wol(struct phy_device *phydev,
    			    struct ethtool_wolinfo *wol)
    {
    	int value, sopass_val;
    
    	wol->supported = (WAKE_UCAST | WAKE_BCAST | WAKE_MAGIC |
    			WAKE_MAGICSECURE);
    	wol->wolopts = 0;
    
    	value = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_RXFCFG);
    	if (value < 0) {
    		phydev_err(phydev, "Failed to read RX CFG\n");
    		return;
    	}
    
    	if (value & DP83869_WOL_UCAST_EN)
    		wol->wolopts |= WAKE_UCAST;
    
    	if (value & DP83869_WOL_BCAST_EN)
    		wol->wolopts |= WAKE_BCAST;
    
    	if (value & DP83869_WOL_MAGIC_EN)
    		wol->wolopts |= WAKE_MAGIC;
    
    	if (value & DP83869_WOL_SEC_EN) {
    		sopass_val = phy_read_mmd(phydev, DP83869_DEVADDR,
    					  DP83869_RXFSOP1);
    		if (sopass_val < 0) {
    			phydev_err(phydev, "Failed to read RX SOP 1\n");
    			return;
    		}
    
    		wol->sopass[0] = (sopass_val & 0xff);
    		wol->sopass[1] = (sopass_val >> 8);
    
    		sopass_val = phy_read_mmd(phydev, DP83869_DEVADDR,
    					  DP83869_RXFSOP2);
    		if (sopass_val < 0) {
    			phydev_err(phydev, "Failed to read RX SOP 2\n");
    			return;
    		}
    
    		wol->sopass[2] = (sopass_val & 0xff);
    		wol->sopass[3] = (sopass_val >> 8);
    
    		sopass_val = phy_read_mmd(phydev, DP83869_DEVADDR,
    					  DP83869_RXFSOP3);
    		if (sopass_val < 0) {
    			phydev_err(phydev, "Failed to read RX SOP 3\n");
    			return;
    		}
    
    		wol->sopass[4] = (sopass_val & 0xff);
    		wol->sopass[5] = (sopass_val >> 8);
    
    		wol->wolopts |= WAKE_MAGICSECURE;
    	}
    
    	if (!(value & DP83869_WOL_ENH_MAC))
    		wol->wolopts = 0;
    }
    
    static int dp83869_get_downshift(struct phy_device *phydev, u8 *data)
    {
    	int val, cnt, enable, count;
    
    	val = phy_read(phydev, DP83869_CFG2);
    	if (val < 0)
    		return val;
    
    	enable = FIELD_GET(DP83869_DOWNSHIFT_EN, val);
    	cnt = FIELD_GET(DP83869_DOWNSHIFT_ATTEMPT_MASK, val);
    
    	switch (cnt) {
    	case DP83869_DOWNSHIFT_1_COUNT_VAL:
    		count = DP83869_DOWNSHIFT_1_COUNT;
    		break;
    	case DP83869_DOWNSHIFT_2_COUNT_VAL:
    		count = DP83869_DOWNSHIFT_2_COUNT;
    		break;
    	case DP83869_DOWNSHIFT_4_COUNT_VAL:
    		count = DP83869_DOWNSHIFT_4_COUNT;
    		break;
    	case DP83869_DOWNSHIFT_8_COUNT_VAL:
    		count = DP83869_DOWNSHIFT_8_COUNT;
    		break;
    	default:
    		return -EINVAL;
    	}
    
    	*data = enable ? count : DOWNSHIFT_DEV_DISABLE;
    
    	return 0;
    }
    
    static int dp83869_set_downshift(struct phy_device *phydev, u8 cnt)
    {
    	int val, count;
    
    	if (cnt > DP83869_DOWNSHIFT_8_COUNT)
    		return -EINVAL;
    
    	if (!cnt)
    		return phy_clear_bits(phydev, DP83869_CFG2,
    				      DP83869_DOWNSHIFT_EN);
    
    	switch (cnt) {
    	case DP83869_DOWNSHIFT_1_COUNT:
    		count = DP83869_DOWNSHIFT_1_COUNT_VAL;
    		break;
    	case DP83869_DOWNSHIFT_2_COUNT:
    		count = DP83869_DOWNSHIFT_2_COUNT_VAL;
    		break;
    	case DP83869_DOWNSHIFT_4_COUNT:
    		count = DP83869_DOWNSHIFT_4_COUNT_VAL;
    		break;
    	case DP83869_DOWNSHIFT_8_COUNT:
    		count = DP83869_DOWNSHIFT_8_COUNT_VAL;
    		break;
    	default:
    		phydev_err(phydev,
    			   "Downshift count must be 1, 2, 4 or 8\n");
    		return -EINVAL;
    	}
    
    	val = DP83869_DOWNSHIFT_EN;
    	val |= FIELD_PREP(DP83869_DOWNSHIFT_ATTEMPT_MASK, count);
    
    	return phy_modify(phydev, DP83869_CFG2,
    			  DP83869_DOWNSHIFT_EN | DP83869_DOWNSHIFT_ATTEMPT_MASK,
    			  val);
    }
    
    static int dp83869_get_tunable(struct phy_device *phydev,
    			       struct ethtool_tunable *tuna, void *data)
    {
    	switch (tuna->id) {
    	case ETHTOOL_PHY_DOWNSHIFT:
    		return dp83869_get_downshift(phydev, data);
    	default:
    		return -EOPNOTSUPP;
    	}
    }
    
    static int dp83869_set_tunable(struct phy_device *phydev,
    			       struct ethtool_tunable *tuna, const void *data)
    {
    	switch (tuna->id) {
    	case ETHTOOL_PHY_DOWNSHIFT:
    		return dp83869_set_downshift(phydev, *(const u8 *)data);
    	default:
    		return -EOPNOTSUPP;
    	}
    }
    
    static int dp83869_config_port_mirroring(struct phy_device *phydev)
    {
    	struct dp83869_private *dp83869 = phydev->priv;
    
    	if (dp83869->port_mirroring == DP83869_PORT_MIRRORING_EN)
    		return phy_set_bits_mmd(phydev, DP83869_DEVADDR,
    					DP83869_GEN_CFG3,
    					DP83869_CFG3_PORT_MIRROR_EN);
    	else
    		return phy_clear_bits_mmd(phydev, DP83869_DEVADDR,
    					  DP83869_GEN_CFG3,
    					  DP83869_CFG3_PORT_MIRROR_EN);
    }
    
    static int dp83869_set_strapped_mode(struct phy_device *phydev)
    {
    	struct dp83869_private *dp83869 = phydev->priv;
    	int val;
    
    	val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_STRAP_STS1);
    	if (val < 0)
    		return val;
    
    	dp83869->mode = val & DP83869_STRAP_OP_MODE_MASK;
    
    	return 0;
    }
    
    #if IS_ENABLED(CONFIG_OF_MDIO)
    static const int dp83869_internal_delay[] = {250, 500, 750, 1000, 1250, 1500,
    					     1750, 2000, 2250, 2500, 2750, 3000,
    					     3250, 3500, 3750, 4000};
    
    static int dp83869_of_init(struct phy_device *phydev)
    {
    	struct dp83869_private *dp83869 = phydev->priv;
    	struct device *dev = &phydev->mdio.dev;
    	struct device_node *of_node = dev->of_node;
    	int delay_size = ARRAY_SIZE(dp83869_internal_delay);
    	int ret;
    
    	if (!of_node)
    		return -ENODEV;
    
    	dp83869->io_impedance = -EINVAL;
    
    	/* Optional configuration */
    	ret = of_property_read_u32(of_node, "ti,clk-output-sel",
    				   &dp83869->clk_output_sel);
    	if (ret || dp83869->clk_output_sel > DP83869_CLK_O_SEL_REF_CLK)
    		dp83869->clk_output_sel = DP83869_CLK_O_SEL_REF_CLK;
    
    	ret = of_property_read_u32(of_node, "ti,op-mode", &dp83869->mode);
    	if (ret == 0) {
    		if (dp83869->mode < DP83869_RGMII_COPPER_ETHERNET ||
    		    dp83869->mode > DP83869_SGMII_COPPER_ETHERNET)
    			return -EINVAL;
    	} else {
    		ret = dp83869_set_strapped_mode(phydev);
    		if (ret)
    			return ret;
    	}
    
    	if (of_property_read_bool(of_node, "ti,max-output-impedance"))
    		dp83869->io_impedance = DP83869_IO_MUX_CFG_IO_IMPEDANCE_MAX;
    	else if (of_property_read_bool(of_node, "ti,min-output-impedance"))
    		dp83869->io_impedance = DP83869_IO_MUX_CFG_IO_IMPEDANCE_MIN;
    
    	if (of_property_read_bool(of_node, "enet-phy-lane-swap")) {
    		dp83869->port_mirroring = DP83869_PORT_MIRRORING_EN;
    	} else {
    		/* If the lane swap is not in the DT then check the straps */
    		ret = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_STRAP_STS1);
    		if (ret < 0)
    			return ret;
    
    		if (ret & DP83869_STRAP_MIRROR_ENABLED)
    			dp83869->port_mirroring = DP83869_PORT_MIRRORING_EN;
    		else
    			dp83869->port_mirroring = DP83869_PORT_MIRRORING_DIS;
    
    		ret = 0;
    	}
    
    	if (of_property_read_u32(of_node, "rx-fifo-depth",
    				 &dp83869->rx_fifo_depth))
    		dp83869->rx_fifo_depth = DP83869_PHYCR_FIFO_DEPTH_4_B_NIB;
    
    	if (of_property_read_u32(of_node, "tx-fifo-depth",
    				 &dp83869->tx_fifo_depth))
    		dp83869->tx_fifo_depth = DP83869_PHYCR_FIFO_DEPTH_4_B_NIB;
    
    	dp83869->rx_int_delay = phy_get_internal_delay(phydev, dev,
    						       &dp83869_internal_delay[0],
    						       delay_size, true);
    	if (dp83869->rx_int_delay < 0)
    		dp83869->rx_int_delay = DP83869_CLK_DELAY_DEF;
    
    	dp83869->tx_int_delay = phy_get_internal_delay(phydev, dev,
    						       &dp83869_internal_delay[0],
    						       delay_size, false);
    	if (dp83869->tx_int_delay < 0)
    		dp83869->tx_int_delay = DP83869_CLK_DELAY_DEF;
    
    	return ret;
    }
    #else
    static int dp83869_of_init(struct phy_device *phydev)
    {
    	return dp83869_set_strapped_mode(phydev);
    }
    #endif /* CONFIG_OF_MDIO */
    
    static int dp83869_configure_rgmii(struct phy_device *phydev,
    				   struct dp83869_private *dp83869)
    {
    	int ret = 0, val;
    
    	if (phy_interface_is_rgmii(phydev)) {
    		val = phy_read(phydev, MII_DP83869_PHYCTRL);
    		if (val < 0)
    			return val;
    
    		val &= ~DP83869_PHYCR_FIFO_DEPTH_MASK;
    		val |= (dp83869->tx_fifo_depth << DP83869_TX_FIFO_SHIFT);
    		val |= (dp83869->rx_fifo_depth << DP83869_RX_FIFO_SHIFT);
    
    		ret = phy_write(phydev, MII_DP83869_PHYCTRL, val);
    		if (ret)
    			return ret;
    	}
    
    	if (dp83869->io_impedance >= 0)
    		ret = phy_modify_mmd(phydev, DP83869_DEVADDR,
    				     DP83869_IO_MUX_CFG,
    				     DP83869_IO_MUX_CFG_IO_IMPEDANCE_CTRL,
    				     dp83869->io_impedance &
    				     DP83869_IO_MUX_CFG_IO_IMPEDANCE_CTRL);
    
    	return ret;
    }
    
    static int dp83869_configure_fiber(struct phy_device *phydev,
    				   struct dp83869_private *dp83869)
    {
    	int bmcr;
    	int ret;
    
    	/* Only allow advertising what this PHY supports */
    	linkmode_and(phydev->advertising, phydev->advertising,
    		     phydev->supported);
    
    	linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported);
    	linkmode_set_bit(ADVERTISED_FIBRE, phydev->advertising);
    
    	if (dp83869->mode == DP83869_RGMII_1000_BASE) {
    		linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
    				 phydev->supported);
    	} else {
    		linkmode_set_bit(ETHTOOL_LINK_MODE_100baseFX_Full_BIT,
    				 phydev->supported);
    		linkmode_set_bit(ETHTOOL_LINK_MODE_100baseFX_Half_BIT,
    				 phydev->supported);
    
    		/* Auto neg is not supported in 100base FX mode */
    		bmcr = phy_read(phydev, MII_BMCR);
    		if (bmcr < 0)
    			return bmcr;
    
    		phydev->autoneg = AUTONEG_DISABLE;
    		linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported);
    		linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->advertising);
    
    		if (bmcr & BMCR_ANENABLE) {
    			ret =  phy_modify(phydev, MII_BMCR, BMCR_ANENABLE, 0);
    			if (ret < 0)
    				return ret;
    		}
    	}
    
    	/* Update advertising from supported */
    	linkmode_or(phydev->advertising, phydev->advertising,
    		    phydev->supported);
    
    	return 0;
    }
    
    static int dp83869_configure_mode(struct phy_device *phydev,
    				  struct dp83869_private *dp83869)
    {
    	int phy_ctrl_val;
    	int ret;
    
    	if (dp83869->mode < DP83869_RGMII_COPPER_ETHERNET ||
    	    dp83869->mode > DP83869_SGMII_COPPER_ETHERNET)
    		return -EINVAL;
    
    	/* Below init sequence for each operational mode is defined in
    	 * section 9.4.8 of the datasheet.
    	 */
    	phy_ctrl_val = dp83869->mode;
    	if (phydev->interface == PHY_INTERFACE_MODE_MII) {
    		if (dp83869->mode == DP83869_100M_MEDIA_CONVERT ||
    		    dp83869->mode == DP83869_RGMII_100_BASE ||
    		    dp83869->mode == DP83869_RGMII_COPPER_ETHERNET) {
    			phy_ctrl_val |= DP83869_OP_MODE_MII;
    		} else {
    			phydev_err(phydev, "selected op-mode is not valid with MII mode\n");
    			return -EINVAL;
    		}
    	}
    
    	ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_OP_MODE,
    			    phy_ctrl_val);
    	if (ret)
    		return ret;
    
    	ret = phy_write(phydev, MII_BMCR, MII_DP83869_BMCR_DEFAULT);
    	if (ret)
    		return ret;
    
    	phy_ctrl_val = (dp83869->rx_fifo_depth << DP83869_RX_FIFO_SHIFT |
    			dp83869->tx_fifo_depth << DP83869_TX_FIFO_SHIFT |
    			DP83869_PHY_CTRL_DEFAULT);
    
    	switch (dp83869->mode) {
    	case DP83869_RGMII_COPPER_ETHERNET:
    		ret = phy_write(phydev, MII_DP83869_PHYCTRL,
    				phy_ctrl_val);
    		if (ret)
    			return ret;
    
    		ret = phy_write(phydev, MII_CTRL1000, DP83869_CFG1_DEFAULT);
    		if (ret)
    			return ret;
    
    		ret = dp83869_configure_rgmii(phydev, dp83869);
    		if (ret)
    			return ret;
    		break;
    	case DP83869_RGMII_SGMII_BRIDGE:
    		ret = phy_modify_mmd(phydev, DP83869_DEVADDR, DP83869_OP_MODE,
    				     DP83869_SGMII_RGMII_BRIDGE,
    				     DP83869_SGMII_RGMII_BRIDGE);
    		if (ret)
    			return ret;
    
    		ret = phy_write_mmd(phydev, DP83869_DEVADDR,
    				    DP83869_FX_CTRL, DP83869_FX_CTRL_DEFAULT);
    		if (ret)
    			return ret;
    
    		break;
    	case DP83869_1000M_MEDIA_CONVERT:
    		ret = phy_write(phydev, MII_DP83869_PHYCTRL,
    				phy_ctrl_val);
    		if (ret)
    			return ret;
    
    		ret = phy_write_mmd(phydev, DP83869_DEVADDR,
    				    DP83869_FX_CTRL, DP83869_FX_CTRL_DEFAULT);
    		if (ret)
    			return ret;
    		break;
    	case DP83869_100M_MEDIA_CONVERT:
    		ret = phy_write(phydev, MII_DP83869_PHYCTRL,
    				phy_ctrl_val);
    		if (ret)
    			return ret;
    		break;
    	case DP83869_SGMII_COPPER_ETHERNET:
    		ret = phy_write(phydev, MII_DP83869_PHYCTRL,
    				phy_ctrl_val);
    		if (ret)
    			return ret;
    
    		ret = phy_write(phydev, MII_CTRL1000, DP83869_CFG1_DEFAULT);
    		if (ret)
    			return ret;
    
    		ret = phy_write_mmd(phydev, DP83869_DEVADDR,
    				    DP83869_FX_CTRL, DP83869_FX_CTRL_DEFAULT);
    		if (ret)
    			return ret;
    
    		break;
    	case DP83869_RGMII_1000_BASE:
    	case DP83869_RGMII_100_BASE:
    		ret = dp83869_configure_fiber(phydev, dp83869);
    		break;
    	default:
    		return -EINVAL;
    	}
    
    	return ret;
    }
    
    static int dp83869_config_init(struct phy_device *phydev)
    {
    	struct dp83869_private *dp83869 = phydev->priv;
    	int ret, val, bit7;
    
    	/* Force speed optimization for the PHY even if it strapped */
    	ret = phy_modify(phydev, DP83869_CFG2, DP83869_DOWNSHIFT_EN,
    			 DP83869_DOWNSHIFT_EN);
    	if (ret)
    		return ret;
    
    	ret = dp83869_configure_mode(phydev, dp83869);
    	if (ret)
    		return ret;
    
    	/* Enable Interrupt output INT_OE in CFG4 register */
    	if (phy_interrupt_is_valid(phydev)) {
    		printk ("\nEnable Interrupt output INT_OE in CFG4 register in %s \n",__func__);
    		val = phy_read(phydev, DP83869_CFG4);
    		val |= DP83869_INT_OE;
    		phy_write(phydev, DP83869_CFG4, val);
    		val = phy_read(phydev, DP83869_CFG4);
    		bit7 = (val >> 7) & 0x1;
    		printk ("\nDP83869_CFG4 bit7:%d\n",bit7);
    	}
    
    	if (dp83869->port_mirroring != DP83869_PORT_MIRRORING_KEEP)
    		dp83869_config_port_mirroring(phydev);
    
    	/* Clock output selection if muxing property is set */
    	if (dp83869->clk_output_sel != DP83869_CLK_O_SEL_REF_CLK)
    		ret = phy_modify_mmd(phydev,
    				     DP83869_DEVADDR, DP83869_IO_MUX_CFG,
    				     DP83869_IO_MUX_CFG_CLK_O_SEL_MASK,
    				     dp83869->clk_output_sel <<
    				     DP83869_IO_MUX_CFG_CLK_O_SEL_SHIFT);
    
    	if (phy_interface_is_rgmii(phydev)) {
    		ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIIDCTL,
    				    dp83869->rx_int_delay |
    			dp83869->tx_int_delay << DP83869_RGMII_CLK_DELAY_SHIFT);
    		if (ret)
    			return ret;
    
    		val = phy_read_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIICTL);
    		val |= (DP83869_RGMII_TX_CLK_DELAY_EN |
    			DP83869_RGMII_RX_CLK_DELAY_EN);
    
    		if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
    			val &= ~(DP83869_RGMII_TX_CLK_DELAY_EN |
    				 DP83869_RGMII_RX_CLK_DELAY_EN);
    
    		if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
    			val &= ~DP83869_RGMII_TX_CLK_DELAY_EN;
    
    		if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
    			val &= ~DP83869_RGMII_RX_CLK_DELAY_EN;
    
    		ret = phy_write_mmd(phydev, DP83869_DEVADDR, DP83869_RGMIICTL,
    				    val);
    	}
    
    	return ret;
    }
    
    static int dp83869_probe(struct phy_device *phydev)
    {
    	struct dp83869_private *dp83869;
    	int ret;
    
    	dp83869 = devm_kzalloc(&phydev->mdio.dev, sizeof(*dp83869),
    			       GFP_KERNEL);
    	if (!dp83869)
    		return -ENOMEM;
    
    	phydev->priv = dp83869;
    
    	ret = dp83869_of_init(phydev);
    	if (ret)
    		return ret;
    
    	if (dp83869->mode == DP83869_RGMII_100_BASE ||
    	    dp83869->mode == DP83869_RGMII_1000_BASE)
    		phydev->port = PORT_FIBRE;
    
    	return dp83869_config_init(phydev);
    }
    
    static int dp83869_phy_reset(struct phy_device *phydev)
    {
    	int ret;
    
    	ret = phy_write(phydev, DP83869_CTRL, DP83869_SW_RESET);
    	if (ret < 0)
    		return ret;
    
    	usleep_range(10, 20);
    
    	/* Global sw reset sets all registers to default.
    	 * Need to set the registers in the PHY to the right config.
    	 */
    	return dp83869_config_init(phydev);
    }
    
    
    #define DP83869_PHY_DRIVER(_id, _name)				\
    {								\
    	PHY_ID_MATCH_MODEL(_id),				\
    	.name		= (_name),				\
    	.probe          = dp83869_probe,			\
    	.config_init	= dp83869_config_init,			\
    	.soft_reset	= dp83869_phy_reset,			\
    	.config_intr	= dp83869_config_intr,			\
    	.handle_interrupt = dp83869_handle_interrupt,		\
    	.read_status	= dp83869_read_status,			\
    	.get_tunable	= dp83869_get_tunable,			\
    	.set_tunable	= dp83869_set_tunable,			\
    	.get_wol	= dp83869_get_wol,			\
    	.set_wol	= dp83869_set_wol,			\
    	.suspend	= genphy_suspend,			\
    	.resume		= genphy_resume,			\
    }
    
    static struct phy_driver dp83869_driver[] = {
    	DP83869_PHY_DRIVER(DP83869_PHY_ID, "TI DP83869"),
    	DP83869_PHY_DRIVER(DP83561_PHY_ID, "TI DP83561-SP"),
    
    };
    module_phy_driver(dp83869_driver);
    
    static struct mdio_device_id __maybe_unused dp83869_tbl[] = {
    	{ PHY_ID_MATCH_MODEL(DP83869_PHY_ID) },
    	{ PHY_ID_MATCH_MODEL(DP83561_PHY_ID) },
    	{ }
    };
    MODULE_DEVICE_TABLE(mdio, dp83869_tbl);
    
    MODULE_DESCRIPTION("Texas Instruments DP83869 PHY driver");
    MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com");
    MODULE_LICENSE("GPL v2");
    

    Best Regards,

    Bowya

  • Hi Bowya, 

    Bowya: How to know when the register access is not there? Even when register access is there, self-clear option also is applicable and valid? In datasheet, it says through pin option if register access is disabled then pin option, then 500ms self clearing.

    We discussed this internally and the description in the datasheet is misleading. As there is no pin option to disable register access, please understand this as if the interrupt register is not read for 500ms, the interrupt self-clears. 

    I looked at the log and the code and this is an interesting situation. 
    Have you tried enabling the interrupt pin, but do not enable any of the interrupt masks? I wonder if the interrupt pin is still being asserted. If so, I would like to check your board design to see if there's any hardware issue. 

    Best,
    J

  • Hi J,

    Without enabling any interrupts masks also, the interrupt pin is being asserted. The ISR register when manually reading 6th and 12th bit are set. Below is code for not enabling any interrupt source and log also attached.

    static int dp83869_config_intr(struct phy_device *phydev)
    {
    int micr_status = 0, err;

    if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
    printk ("\nPhy interrupt enabled if condition in %s\n",__func__);
    err = dp83869_ack_interrupt(phydev);
    if (err){
    printk ("\nAlready in interrupt %s\n",__func__);
    return err;
    }

    micr_status = phy_read(phydev, MII_DP83869_MICR);
    if (micr_status < 0){
    printk ("\nMICR Null or less than 0 %s\n",__func__);
    return micr_status;
    }

    printk ("\nMICR value default:%d, %s\n",micr_status, __func__);
    #if 0
    micr_status |=
    (MII_DP83869_MICR_AN_ERR_INT_EN |
    MII_DP83869_MICR_SPEED_CHNG_INT_EN |
    MII_DP83869_MICR_AUTONEG_COMP_INT_EN |
    MII_DP83869_MICR_LINK_STS_CHNG_INT_EN |
    MII_DP83869_MICR_DUP_MODE_CHNG_INT_EN |
    MII_DP83869_MICR_SLEEP_MODE_CHNG_INT_EN);
    #endif
    micr_status = 0;

    err = phy_write(phydev, MII_DP83869_MICR, micr_status);
    printk ("\nMICR value:%d, %s\n",micr_status, __func__);
    } else {
    printk ("\nPhy interrupt config_intr else condition in %s: Micr_value:%d\n",__func__,micr_status);
    err = phy_write(phydev, MII_DP83869_MICR, micr_status);
    if (err)
    return err;

    err = dp83869_ack_interrupt(phydev);
    }

    return err;
    }

    # ifconfig eth0 down
    [  523.555537] 
    [  523.555537] Phy interrupt config_intr else condition in dp83869_config_intr: Micr_value:0
    [  523.565314] 
    [  523.565314] Interrupt occured reading MII_DP83869_ISR : 1024
    [  523.573121] macb ff0c0000.ethernet: gem-ptp-timer ptp clock unregistered.
    # while true; do phytool read eth0/1/0x13; phytool read eth0/1/0x12; gpioget gpiochip8 78; cat /proc/interrupts | grep "zynq-gpio";sleep 0.5; done &
    [1] 1250
    # ifconfig eth0 uperror: phy_read (-22)
    error: phy_read (-22)
    0
     52:     300001          0          0          0  zynq-gpio  78 Level   
    error: phy_read (-22)
    error: phy_read (-22)
    0
     52:     300001          0          0          0  zynq-gpio  78 Level   
    error: phy_read (-22)
    error: phy_read (-22)
    0
     52:     300001          0          0          0  zynq-gpio  78 Level   
    error: phy_read (-22)
    error: phy_read (-22)
    0
     52:     300001          0          0          0  zynq-gpio  78 Level   
    error: phy_read (-22)
    error: phy_read (-22)
    0
     52:     300001          0          0          0  zynq-gpio  78 Level   
    error: phy_read (-22)
    error: phy_read (-22)
    0
     52:     300001          0          0          0  zynq-gpio  78 Level   
    
    [  526.332555] 
    [  526.332555] Enable Interrupt output INT_OE in CFG4 register in dp83869_config_init 
    [  526.341849] 
    [  526.341849] DP83869_CFG4 bit7:1
    [  526.347704] 
    [  526.347704] Enable Interrupt output INT_OE in CFG4 register in dp83869_config_init 
    [  526.356994] 
    [  526.356994] DP83869_CFG4 bit7:1
    [  526.362339] 
    [  526.362339] Phy interrupt config_intr else condition in dp83869_config_intr: Micr_value:0
    [  526.372103] 
    [  526.372103] Interrupt occured reading MII_DP83869_ISR : 0
    [  526.379075] macb ff0c0000.ethernet eth0: PHY [ff0c0000.ethernet-ffffffff:01] driver [TI DP83869] (irq=52)
    [  526.388881] 
    [  526.388881] Phy interrupt enabled if condition in dp83869_config_intr
    [  526.396910] 
    [  526.396910] Interrupt occured reading MII_DP83869_ISR : 0
    [  526.403856] 
    [  526.403856] MICR value default:0, dp83869_config_intr
    [  526.410452] 
    [  526.410452] MICR value:0, dp83869_config_intr
    [  526.416312] macb ff0c0000.ethernet eth0: configuring for phy/rgmii-id link mode
    [  526.424064] pps pps0: new PPS source ptp0
    [  526.428776] macb ff0c0000.ethernet: gem-ptp-timer ptp clock registered.
    # 0000
    0000
    1
     52:     300001          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0x0040
    0000
    1
     52:     300001          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0x0040
    0000
    1
     52:     300001          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0x1000
    0000
    1
     52:     300001          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0x1000
    0000
    1
     52:     300001          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0000
    0000
    0
     52:     302299          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0000
    0000
    0
     52:     307373          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0000
    0000
    0
     52:     312413          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0000
    0000
    0
     52:     317484          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0000
    0000
    0
     52:     322693          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0000
    0000
    0
     52:     327789          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0000
    0000
    0
     52:     332907          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0000
    0000
    0
     52:     337994          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0000
    0000
    0
     52:     343103          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0000
    0000
    0
     52:     348176          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0000
    0000
    0
     52:     353215          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0000
    0000
    0
     52:     358314          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0000
    0000
    0
     52:     363416          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0000
    0000
    0
     52:     368460          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0000
    0000
    0
     52:     373502          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    
    # 0000
    0000
    0
     52:     378605          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0000
    0000
    0
     52:     383702          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    f0000
    g0000
    0
     52:     388801          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0000
    0000
    0
     52:     393894          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0000
    0000
    0
     52:     399083          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    [  539.640440] irq 52: nobody cared (try booting with the "irqpoll" option)
    [  539.647157] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 6.6.40-xilinx-ga866e8177038 #1
    [  539.654903] Hardware name: xlnx,zynqmp (DT)
    [  539.659079] Call trace:
    [  539.661518]  dump_backtrace+0x90/0xe8
    [  539.665189]  show_stack+0x18/0x24
    [  539.668504]  dump_stack_lvl+0x48/0x60
    [  539.672168]  dump_stack+0x18/0x24
    [  539.675483]  __report_bad_irq+0x38/0x120
    [  539.679406]  note_interrupt+0x310/0x360
    [  539.683243]  handle_irq_event+0xd8/0xe8
    [  539.687079]  handle_fasteoi_irq+0xb0/0x284
    [  539.691176]  generic_handle_domain_irq+0x2c/0x44
    [  539.695803]  zynq_gpio_irqhandler+0xa0/0x16c
    [  539.700073]  generic_handle_domain_irq+0x2c/0x44
    [  539.704691]  gic_handle_irq+0x6c/0x9c
    [  539.708354]  call_on_irq_stack+0x24/0x4c
    [  539.712278]  do_interrupt_handler+0x80/0x84
    [  539.716461]  el1_interrupt+0x34/0x54
    [  539.720037]  el1h_64_irq_handler+0x18/0x24
    [  539.724135]  el1h_64_irq+0x64/0x68
    [  539.727537]  default_idle_call+0x28/0x3c
    [  539.731460]  do_idle+0xac/0x100
    [  539.734603]  cpu_startup_entry+0x38/0x3c
    [  539.738526]  kernel_init+0x0/0x1e0
    [  539.741929]  arch_post_acpi_subsys_init+0x0/0x8
    [  539.746459]  start_kernel+0x504/0x60c
    [  539.750122]  __primary_switched+0xbc/0xc4
    [  539.754133] handlers:
    [  539.756398] [<0000000082ba8a61>] irq_default_primary_handler threaded [<00000000ba4dc5d6>] phy_interrupt
    [  539.765903] Disabling IRQ #52
    
    Broadcast message from systemd-journald@zynqmp (Tue 2024-02-27 09:34:52 PST):
    
    kernel[689]: [  539.765903] Disabling IRQ #52
    
    0000
    0000
    0
     52:     400001          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0000
    0000
    0
     52:     400001          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    
    while true; do
        phytool read eth0/1/0x13; phytool read eth0/1/0x12; gpioget gpiochip8 78; cat /proc/interrupts | grep "zynq-gpio"; sleep 0.5;
    done
    ^C
    # ifconfig eth0 down
    tool read eth0/1/0x12; gpioget gpiochip8 78; cat /proc/i[ 1283.983502] 
    [ 1283.983502] Phy interrupt config_intr else condition in dp83869_config_intr: Micr_value:0
    nterrupts | grep "zynq-gpio";sleep 0.5; done &
    [ 1283.995582] 
    [ 1283.995582] Interrupt occured reading MII_DP83869_ISR : 1024
    ifconfig eth0 up
    [ 1284.011774] macb ff0c0000.ethernet: gem-ptp-timer ptp clock unregistered.
    # while true; do phytool read eth0/1/0x6;phytool read eth0/1/0x11;phytool read eth0/1/0x13; phytool read eth0/1/0x12; gpioget gpiochip8 78; cat /proc/interrupts | grep "zynq-gpio";sleep 0.5;d
    [1] 1467
    # ifconfig eth0 up
    error: phy_read (-22)
    [ 1284.081767] 
    [ 1284.081767] Enable Interrupt output INT_OE in CFG4 register in dp83869_config_init 
    [ 1284.091075] 
    [ 1284.091075] DP83869_CFG4 bit7:1
    [ 1284.098332] 
    [ 1284.098332] Enable Interrupt output INT_OE in CFG4 register in dp83869_config_init 
    [ 1284.107677] 
    [ 1284.107677] DP83869_CFG4 bit7:1
    [ 1284.113001] 
    [ 1284.113001] Phy interrupt config_intr else condition in dp83869_config_intr: Micr_value:0
    [ 1284.122767] 
    [ 1284.122767] Interrupt occured reading MII_DP83869_ISR : 0
    [ 1284.129737] macb ff0c0000.ethernet eth0: PHY [ff0c0000.ethernet-ffffffff:01] driver [TI DP83869] (irq=52)
    [ 1284.139532] 
    [ 1284.139532] Phy interrupt enabled if condition in dp83869_config_intr
    [ 1284.147525] 
    [ 1284.147525] Interrupt occured reading MII_DP83869_ISR : 64
    [ 1284.154558] 
    [ 1284.154558] MICR value default:0, dp83869_config_intr
    [ 1284.161155] 
    [ 1284.161155] MICR value:0, dp83869_config_intr
    [ 1284.167014] macb ff0c0000.ethernet eth0: configuring for phy/rgmii-id link mode
    [ 1284.174988] pps pps0: new PPS source ptp0
    [ 1284.179486] macb ff0c0000.ethernet: gem-ptp-timer ptp clock registered.
    # 0x0302
    0000
    0000
    1
     52:     400001          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0x0064
    0x0302
    0x0040
    0000
    1
     52:     400001          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0x0064
    0x0302
    0x0040
    0000
    1
     52:     400001          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0x0064
    0x0002
    0x0040
    0000
    1
     52:     400001          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0x006f
    0x1302
    0x1040
    0000
    1
     52:     400001          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    
    # 0x006f
    0x1302
    0x1000
    0000
    1
     52:     400001          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0x006f
    0x1302
    0x1000
    0000
    1
     52:     400001          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0x006f
    0xbf02
    0000
    0000
    0
     52:     403761          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0x006d
    0xaf02
    0000
    0000
    0
     52:     409197          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0x006d
    0xaf02
    0000
    0000
    0
     52:     414656          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0x006d
    0xaf02
    0000
    0000
    0
     52:     420471          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0x006d
    0xaf02
    0000
    0000
    0
     52:     425978          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0x006d
    0xaf02
    0000
    0000
    0
     52:     431406          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0x006d
    0xaf02
    0000
    0000
    0
     52:     436878          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0x006d
    0xaf02
    0000
    0000
    0
     52:     442345          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0x006d
    0xaf02
    0000
    0000
    0
     52:     447875          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0x006d
    0xaf02
    0000
    0000
    0
     52:     453477          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0x006d
    0xaf02
    0000
    0000
    0
     52:     458902          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0x006d
    0xaf02
    0000
    0000
    0
     52:     464413          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0x006d
    0xaf02
    0000
    0000
    0
     52:     469975          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0x006d
    0xaf02
    0000
    0000
    0
     52:     475432          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0x006d
    0xaf02
    0000
    0000
    0
     52:     480868          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0x006d
    0xaf02
    0000
    0000
    0
     52:     486479          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0x006d
    0xaf02
    0000
    0000
    0
     52:     491991          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0x006d
    0xaf02
    0000
    0000
    0
     52:     497470          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    [ 1297.416205] irq 52: nobody cared (try booting with the "irqpoll" option)
    [ 1297.422918] CPU: 0 PID: 1471 Comm: irq/52-ff0c0000 Not tainted 6.6.40-xilinx-ga866e8177038 #1
    [ 1297.431446] Hardware name: xlnx,zynqmp (DT)
    [ 1297.435622] Call trace:
    [ 1297.438061]  dump_backtrace+0x90/0xe8
    [ 1297.441731]  show_stack+0x18/0x24
    [ 1297.445047]  dump_stack_lvl+0x48/0x60
    [ 1297.448710]  dump_stack+0x18/0x24
    [ 1297.452026]  __report_bad_irq+0x38/0x120
    [ 1297.455949]  note_interrupt+0x310/0x360
    [ 1297.459786]  handle_irq_event+0xd8/0xe8
    [ 1297.463622]  handle_fasteoi_irq+0xb0/0x284
    [ 1297.467719]  generic_handle_domain_irq+0x2c/0x44
    [ 1297.472346]  zynq_gpio_irqhandler+0xa0/0x16c
    [ 1297.476616]  generic_handle_domain_irq+0x2c/0x44
    [ 1297.481243]  gic_handle_irq+0x6c/0x9c
    [ 1297.484905]  call_on_irq_stack+0x24/0x4c
    [ 1297.488829]  do_interrupt_handler+0x80/0x84
    [ 1297.493013]  el1_interrupt+0x34/0x54
    [ 1297.496589]  el1h_64_irq_handler+0x18/0x24
    [ 1297.500686]  el1h_64_irq+0x64/0x68
    [ 1297.504088]  irq_finalize_oneshot.part.0+0x80/0x150
    [ 1297.508967]  irq_thread_fn+0x60/0xa8
    [ 1297.512543]  irq_thread+0x170/0x294
    [ 1297.516032]  kthread+0xe0/0xe4
    [ 1297.519087]  ret_from_fork+0x10/0x20
    [ 1297.522664] handlers:
    [ 1297.524929] [<0000000082ba8a61>] irq_default_primary_handler threaded [<00000000ba4dc5d6>] phy_interrupt
    [ 1297.534434] Disabling IRQ #52
    
    Broadcast message from systemd-journald@zynqmp (Tue 2024-02-27 09:47:29 PST):
    
    kernel[689]: [ 1297.534434] Disabling IRQ #52
    
    0x006d
    0xaf02
    0000
    0000
    0
     52:     500001          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0x006d
    0xaf02
    0000
    0000
    0
     52:     500001          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0x006d
    0xaf02
    0000
    0000
    0
     52:     500001          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    
    # 0x006d
    0xaf02
    0000
    0000
    0
     52:     500001          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0x006d
    0xaf02
    0000
    0000
    0
     52:     500001          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    0x006d
    0xaf02
    0000
    0000
    0
     52:     500001          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
    fg
    while true; do
        phytool read eth0/1/0x6; phytool read eth0/1/0x11; phytool read eth0/1/0x13; phytool read eth0/1/0x12; gpioget gpiochip8 78; cat /proc/interrupts | grep "zynq-gpio"; sleep 0.5;
    done
    0x006d
    0xaf02
    0000
    0000
    0
     52:     500001          0          0          0  zynq-gpio  78 Level     ff0c0000.ethernet-ffffffff:01
                                                                                                                             
    
    
    

  • Hi Bowya,

    Interrupt flags on the register will still go up even if the mask is disabled. Only the interrupts with the mask enabled will route to the interrupt pin. 

    As the interrupt pin goes low even when there is no mask routed to the interrupt pin, I am suspecting if there is a hardware issue when the cable is connected. 

    Could you send me the schematic so we can look if there are any hardware issues? I sent you a friend request so feel free to message me the schematic. 
    In addition, could you check the polarity of the INT pin? The default should be active low, but the driver initialization may have set it to active high. 
    Please check register 14h bit 13. 

    Best,
    J