--- /dev/null
+From 85e2038891989e41bc62f6a4625fd5865da8a1a2 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Wed, 24 Jan 2024 19:17:02 +0100
+Subject: [PATCH 1/3] dt-bindings: net: ipq4019-mdio: document now supported
+ clock-frequency
+
+Document support for clock-frequency and add details on why this
+property is needed and what values are supported.
+
+From internal documentation, while other values are supported, the
+correct function of the MDIO bus is not assured hence add only the
+suggested supported values to the property enum.
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+---
+ .../bindings/net/qcom,ipq4019-mdio.yaml | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+--- a/Documentation/devicetree/bindings/net/qcom,ipq4019-mdio.yaml
++++ b/Documentation/devicetree/bindings/net/qcom,ipq4019-mdio.yaml
+@@ -38,6 +38,21 @@ properties:
+ MDIO clock source frequency fixed to 100MHZ, this clock should be specified
+ by the platform IPQ807x, IPQ60xx and IPQ50xx.
+
++ clock-frequency:
++ description:
++ The MDIO bus clock that must be output by the MDIO bus hardware, if
++ absent, the default hardware values are used.
++
++ MDC rate is feed by an external clock (fixed 100MHz) and is divider
++ internally. The default divider is /256 resulting in the default rate
++ applied of 390KHz.
++
++ To follow 802.3 standard that instruct up to 2.5MHz by default, if
++ this property is not declared and the divider is set to /256, by
++ default 1.5625Mhz is select.
++ enum: [ 390625, 781250, 1562500, 3125000, 6250000, 12500000 ]
++ default: 1562500
++
+ required:
+ - compatible
+ - reg
--- /dev/null
+From eacf1d2505dfecd3599d558cdade1a2da47fe06d Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Wed, 24 Jan 2024 18:52:33 +0100
+Subject: [PATCH 2/3] net: mdio: ipq4019: add support for clock-frequency
+ property
+
+The IPQ4019 MDIO internally divide the clock feed by AHB based on the
+MDIO_MODE reg. On reset or power up, the default value for the
+divider is 0xff that reflect the divider set to /256.
+
+This makes the MDC run at a very low rate, that is, considering AHB is
+always fixed to 100Mhz, a value of 390KHz.
+
+This hasn't have been a problem as MDIO wasn't used for time sensitive
+operation, it is now that on IPQ807x is usually mounted with PHY that
+requires MDIO to load their firmware (example Aquantia PHY).
+
+To handle this problem and permit to set the correct designed MDC
+frequency for the SoC add support for the standard "clock-frequency"
+property for the MDIO node.
+
+The divider supports value from /1 to /256 and the common value are to
+set it to /16 to reflect 6.25Mhz or to /8 on newer platform to reflect
+12.5Mhz.
+
+To scan if the requested rate is supported by the divider, loop with
+each supported divider and stop when the requested rate match the final
+rate with the current divider. An error is returned if the rate doesn't
+match any value.
+
+On MDIO reset, the divider is restored to the requested value to prevent
+any kind of downclocking caused by the divider reverting to a default
+value.
+
+To follow 802.3 spec of 2.5MHz of default value, if divider is set at
+/256 and "clock-frequency" is not set in DT, assume nobody set the
+divider and try to find the closest MDC rate to 2.5MHz. (in the case of
+AHB set to 100MHz, it's 1.5625MHz)
+
+While at is also document other bits of the MDIO_MODE reg to have a
+clear idea of what is actually applied there.
+
+Documentation of some BITs is skipped as they are marked as reserved and
+their usage is not clear (RES 11:9 GENPHY 16:13 RES1 19:17)
+
+Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
+---
+ drivers/net/mdio/mdio-ipq4019.c | 109 ++++++++++++++++++++++++++++++--
+ 1 file changed, 103 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/mdio/mdio-ipq4019.c
++++ b/drivers/net/mdio/mdio-ipq4019.c
+@@ -14,6 +14,20 @@
+ #include <linux/clk.h>
+
+ #define MDIO_MODE_REG 0x40
++#define MDIO_MODE_MDC_MODE BIT(12)
++/* 0 = Clause 22, 1 = Clause 45 */
++#define MDIO_MODE_C45 BIT(8)
++#define MDIO_MODE_DIV_MASK GENMASK(7, 0)
++#define MDIO_MODE_DIV(x) FIELD_PREP(MDIO_MODE_DIV_MASK, (x) - 1)
++#define MDIO_MODE_DIV_1 0x0
++#define MDIO_MODE_DIV_2 0x1
++#define MDIO_MODE_DIV_4 0x3
++#define MDIO_MODE_DIV_8 0x7
++#define MDIO_MODE_DIV_16 0xf
++#define MDIO_MODE_DIV_32 0x1f
++#define MDIO_MODE_DIV_64 0x3f
++#define MDIO_MODE_DIV_128 0x7f
++#define MDIO_MODE_DIV_256 0xff
+ #define MDIO_ADDR_REG 0x44
+ #define MDIO_DATA_WRITE_REG 0x48
+ #define MDIO_DATA_READ_REG 0x4c
+@@ -26,9 +40,6 @@
+ #define MDIO_CMD_ACCESS_CODE_C45_WRITE 1
+ #define MDIO_CMD_ACCESS_CODE_C45_READ 2
+
+-/* 0 = Clause 22, 1 = Clause 45 */
+-#define MDIO_MODE_C45 BIT(8)
+-
+ #define IPQ4019_MDIO_TIMEOUT 10000
+ #define IPQ4019_MDIO_SLEEP 10
+
+@@ -41,6 +52,7 @@ struct ipq4019_mdio_data {
+ void __iomem *membase;
+ void __iomem *eth_ldo_rdy;
+ struct clk *mdio_clk;
++ unsigned int mdc_rate;
+ };
+
+ static int ipq4019_mdio_wait_busy(struct mii_bus *bus)
+@@ -179,6 +191,38 @@ static int ipq4019_mdio_write(struct mii
+ return 0;
+ }
+
++static int ipq4019_mdio_set_div(struct ipq4019_mdio_data *priv)
++{
++ unsigned long ahb_rate;
++ int div;
++ u32 val;
++
++ /* If we don't have a clock for AHB use the fixed value */
++ ahb_rate = IPQ_MDIO_CLK_RATE;
++ if (priv->mdio_clk)
++ ahb_rate = clk_get_rate(priv->mdio_clk);
++
++ /* MDC rate is ahb_rate/(MDIO_MODE_DIV + 1)
++ * While supported, internal documentation doesn't
++ * assure correct functionality of the MDIO bus
++ * with divider of 1, 2 or 4.
++ */
++ for (div = 8; div <= 256; div *= 2) {
++ /* The requested rate is supported by the div */
++ if (priv->mdc_rate == DIV_ROUND_UP(ahb_rate, div)) {
++ val = readl(priv->membase + MDIO_MODE_REG);
++ val &= ~MDIO_MODE_DIV_MASK;
++ val |= MDIO_MODE_DIV(div);
++ writel(val, priv->membase + MDIO_MODE_REG);
++
++ return 0;
++ }
++ }
++
++ /* The requested rate is not supported */
++ return -EINVAL;
++}
++
+ static int ipq_mdio_reset(struct mii_bus *bus)
+ {
+ struct ipq4019_mdio_data *priv = bus->priv;
+@@ -201,10 +245,58 @@ static int ipq_mdio_reset(struct mii_bus
+ return ret;
+
+ ret = clk_prepare_enable(priv->mdio_clk);
+- if (ret == 0)
+- mdelay(10);
++ if (ret)
++ return ret;
++
++ mdelay(10);
+
+- return ret;
++ /* Restore MDC rate */
++ return ipq4019_mdio_set_div(priv);
++}
++
++static void ipq4019_mdio_select_mdc_rate(struct platform_device *pdev,
++ struct ipq4019_mdio_data *priv)
++{
++ unsigned long ahb_rate;
++ int div;
++ u32 val;
++
++ /* MDC rate defined in DT, we don't have to decide a default value */
++ if (!of_property_read_u32(pdev->dev.of_node, "clock-frequency",
++ &priv->mdc_rate))
++ return;
++
++ /* If we don't have a clock for AHB use the fixed value */
++ ahb_rate = IPQ_MDIO_CLK_RATE;
++ if (priv->mdio_clk)
++ ahb_rate = clk_get_rate(priv->mdio_clk);
++
++ /* Check what is the current div set */
++ val = readl(priv->membase + MDIO_MODE_REG);
++ div = FIELD_GET(MDIO_MODE_DIV_MASK, val);
++
++ /* div is not set to the default value of /256
++ * Probably someone changed that (bootloader, other drivers)
++ * Keep this and doesn't overwrite it.
++ */
++ if (div != MDIO_MODE_DIV_256) {
++ priv->mdc_rate = DIV_ROUND_UP(ahb_rate, div + 1);
++ return;
++ }
++
++ /* If div is /256 assume nobody have set this value and
++ * try to find one MDC rate that is close the 802.3 spec of
++ * 2.5MHz
++ */
++ for (div = 256; div >= 8; div /= 2) {
++ /* Stop as soon as we found a divider that
++ * reached the closest value to 2.5MHz
++ */
++ if (DIV_ROUND_UP(ahb_rate, div) > 2500000)
++ break;
++
++ priv->mdc_rate = DIV_ROUND_UP(ahb_rate, div);
++ }
+ }
+
+ static int ipq4019_mdio_probe(struct platform_device *pdev)
+@@ -228,6 +320,11 @@ static int ipq4019_mdio_probe(struct pla
+ if (IS_ERR(priv->mdio_clk))
+ return PTR_ERR(priv->mdio_clk);
+
++ ipq4019_mdio_select_mdc_rate(pdev, priv);
++ ret = ipq4019_mdio_set_div(priv);
++ if (ret)
++ return ret;
++
+ /* The platform resource is provided on the chipset IPQ5018 */
+ /* This resource is optional */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);