--- /dev/null
+From bdce82e960d1205d118662f575cec39379984e34 Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+Date: Wed, 31 Jan 2024 03:26:04 +0100
+Subject: [PATCH] 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>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ 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 don'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);
+++ /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);