net: phy: mdio_device: Reset device only when necessary
authorAndrew Halaney <ahalaney@redhat.com>
Mon, 27 Nov 2023 21:41:10 +0000 (15:41 -0600)
committerJakub Kicinski <kuba@kernel.org>
Fri, 1 Dec 2023 07:11:21 +0000 (23:11 -0800)
Currently the phy reset sequence is as shown below for a
devicetree described mdio phy on boot:

1. Assert the phy_device's reset as part of registering
2. Deassert the phy_device's reset as part of registering
3. Deassert the phy_device's reset as part of phy_probe
4. Deassert the phy_device's reset as part of phy_hw_init

The extra two deasserts include waiting the deassert delay afterwards,
which is adding unnecessary delay.

This applies to both possible types of resets (reset controller
reference and a reset gpio) that can be used.

Here's some snipped tracing output using the following command line
params "trace_event=gpio:* trace_options=stacktrace" illustrating
the reset handling and where its coming from:

    /* Assert */
       systemd-udevd-283     [002] .....     6.780434: gpio_value: 544 set 0
       systemd-udevd-283     [002] .....     6.783849: <stack trace>
     => gpiod_set_raw_value_commit
     => gpiod_set_value_nocheck
     => gpiod_set_value_cansleep
     => mdio_device_reset
     => mdiobus_register_device
     => phy_device_register
     => fwnode_mdiobus_phy_device_register
     => fwnode_mdiobus_register_phy
     => __of_mdiobus_register
     => stmmac_mdio_register
     => stmmac_dvr_probe
     => stmmac_pltfr_probe
     => devm_stmmac_pltfr_probe
     => qcom_ethqos_probe
     => platform_probe

    /* Deassert */
       systemd-udevd-283     [002] .....     6.802480: gpio_value: 544 set 1
       systemd-udevd-283     [002] .....     6.805886: <stack trace>
     => gpiod_set_raw_value_commit
     => gpiod_set_value_nocheck
     => gpiod_set_value_cansleep
     => mdio_device_reset
     => phy_device_register
     => fwnode_mdiobus_phy_device_register
     => fwnode_mdiobus_register_phy
     => __of_mdiobus_register
     => stmmac_mdio_register
     => stmmac_dvr_probe
     => stmmac_pltfr_probe
     => devm_stmmac_pltfr_probe
     => qcom_ethqos_probe
     => platform_probe

    /* Deassert */
       systemd-udevd-283     [002] .....     6.882601: gpio_value: 544 set 1
       systemd-udevd-283     [002] .....     6.886014: <stack trace>
     => gpiod_set_raw_value_commit
     => gpiod_set_value_nocheck
     => gpiod_set_value_cansleep
     => mdio_device_reset
     => phy_probe
     => really_probe
     => __driver_probe_device
     => driver_probe_device
     => __device_attach_driver
     => bus_for_each_drv
     => __device_attach
     => device_initial_probe
     => bus_probe_device
     => device_add
     => phy_device_register
     => fwnode_mdiobus_phy_device_register
     => fwnode_mdiobus_register_phy
     => __of_mdiobus_register
     => stmmac_mdio_register
     => stmmac_dvr_probe
     => stmmac_pltfr_probe
     => devm_stmmac_pltfr_probe
     => qcom_ethqos_probe
     => platform_probe

    /* Deassert */
      NetworkManager-477     [000] .....     7.023144: gpio_value: 544 set 1
      NetworkManager-477     [000] .....     7.026596: <stack trace>
     => gpiod_set_raw_value_commit
     => gpiod_set_value_nocheck
     => gpiod_set_value_cansleep
     => mdio_device_reset
     => phy_init_hw
     => phy_attach_direct
     => phylink_fwnode_phy_connect
     => __stmmac_open
     => stmmac_open

There's a lot of paths where the device is getting its reset
asserted and deasserted. Let's track the state and only actually
do the assert/deassert when it changes.

Reported-by: Sagar Cheluvegowda <quic_scheluve@quicinc.com>
Signed-off-by: Andrew Halaney <ahalaney@redhat.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Link: https://lore.kernel.org/r/20231127-net-phy-reset-once-v2-1-448e8658779e@redhat.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/phy/mdio_device.c
drivers/net/phy/phy_device.c
include/linux/mdio.h

index 044828d081d2251bf5084c87bc7f6a11d7591e59..73f6539b9e50617e1bfa04deeaf0e692a8b774fe 100644 (file)
@@ -62,6 +62,7 @@ struct mdio_device *mdio_device_create(struct mii_bus *bus, int addr)
        mdiodev->device_remove = mdio_device_remove;
        mdiodev->bus = bus;
        mdiodev->addr = addr;
+       mdiodev->reset_state = -1;
 
        dev_set_name(&mdiodev->dev, PHY_ID_FMT, bus->id, addr);
 
@@ -122,6 +123,9 @@ void mdio_device_reset(struct mdio_device *mdiodev, int value)
        if (!mdiodev->reset_gpio && !mdiodev->reset_ctrl)
                return;
 
+       if (mdiodev->reset_state == value)
+               return;
+
        if (mdiodev->reset_gpio)
                gpiod_set_value_cansleep(mdiodev->reset_gpio, value);
 
@@ -135,6 +139,8 @@ void mdio_device_reset(struct mdio_device *mdiodev, int value)
        d = value ? mdiodev->reset_assert_delay : mdiodev->reset_deassert_delay;
        if (d)
                fsleep(d);
+
+       mdiodev->reset_state = value;
 }
 EXPORT_SYMBOL(mdio_device_reset);
 
index 400fb09d9cd6b103900faf17ee90a228f5319cab..d8e9335d415ca5a28ee0098d006164e7b8b015ad 100644 (file)
@@ -654,6 +654,7 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id,
        mdiodev->flags = MDIO_DEVICE_FLAG_PHY;
        mdiodev->device_free = phy_mdio_device_free;
        mdiodev->device_remove = phy_mdio_device_remove;
+       mdiodev->reset_state = -1;
 
        dev->speed = SPEED_UNKNOWN;
        dev->duplex = DUPLEX_UNKNOWN;
index 007fd9c3e4b62cc93a9bf54a1c6502c95feab7b7..79ceee3c8673e8c64743d2983ce426140ad88aaa 100644 (file)
@@ -38,6 +38,7 @@ struct mdio_device {
        /* Bus address of the MDIO device (0-31) */
        int addr;
        int flags;
+       int reset_state;
        struct gpio_desc *reset_gpio;
        struct reset_control *reset_ctrl;
        unsigned int reset_assert_delay;