-Allow specifying the MDIO clock divisor explicitly in the device tree,
rather than always detecting it from the CPU clock which only works on
the MicroBlaze platform.

-Centralize all MDIO handling in xilinx_axienet_mdio.c

-Ensure that MDIO clock divisor is always re-set after resetting the
device, since it will be cleared.

-Fixed ordering of MDIO teardown vs. netdev teardown

Signed-off-by: Robert Hancock <hanc...@sedsystems.ca>
---
 .../devicetree/bindings/net/xilinx_axienet.txt     |   4 +
 drivers/net/ethernet/xilinx/xilinx_axienet.h       |   8 +-
 drivers/net/ethernet/xilinx/xilinx_axienet_main.c  |  36 ++---
 drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c  | 160 ++++++++++++---------
 4 files changed, 117 insertions(+), 91 deletions(-)

diff --git a/Documentation/devicetree/bindings/net/xilinx_axienet.txt 
b/Documentation/devicetree/bindings/net/xilinx_axienet.txt
index 38f9ec0..708722e 100644
--- a/Documentation/devicetree/bindings/net/xilinx_axienet.txt
+++ b/Documentation/devicetree/bindings/net/xilinx_axienet.txt
@@ -31,6 +31,10 @@ Optional properties:
                  1 to enable partial TX checksum offload,
                  2 to enable full TX checksum offload
 - xlnx,rxcsum  : Same values as xlnx,txcsum but for RX checksum offload
+- xlnx,mdio-clock-divisor: Explicitly set clock divisor from AXI bus clock
+                           to MDIO bus. If not specified, it is auto-detected
+                           from the CPU clock (but only on platforms where this
+                           is possible).
 
 Example:
        axi_ethernet_eth: ethernet@40c00000 {
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet.h 
b/drivers/net/ethernet/xilinx/xilinx_axienet.h
index 0d89ebc..dfe0e4c 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet.h
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet.h
@@ -418,6 +418,9 @@ struct axienet_local {
        /* Connection to PHY device */
        struct device_node *phy_node;
 
+       /* MDIO clock divisor (0=detected from CPU clock) */
+       u32 mdio_clock_divisor;
+
        /* MDIO bus data */
        struct mii_bus *mii_bus;        /* MII bus reference */
 
@@ -510,8 +513,9 @@ static inline void axienet_iow(struct axienet_local *lp, 
off_t offset,
 }
 
 /* Function prototypes visible in xilinx_axienet_mdio.c for other files */
-int axienet_mdio_setup(struct axienet_local *lp, struct device_node *np);
-int axienet_mdio_wait_until_ready(struct axienet_local *lp);
+int axienet_mdio_enable(struct axienet_local *lp);
+void axienet_mdio_disable(struct axienet_local *lp);
+int axienet_mdio_setup(struct axienet_local *lp);
 void axienet_mdio_teardown(struct axienet_local *lp);
 
 #endif /* XILINX_AXI_ENET_H */
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c 
b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
index c0a8861..2e69755 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
@@ -921,27 +921,20 @@ static irqreturn_t axienet_rx_irq(int irq, void *_ndev)
  */
 static int axienet_open(struct net_device *ndev)
 {
-       int ret, mdio_mcreg;
+       int ret;
        struct axienet_local *lp = netdev_priv(ndev);
        struct phy_device *phydev = NULL;
 
        dev_dbg(&ndev->dev, "axienet_open()\n");
 
-       mdio_mcreg = axienet_ior(lp, XAE_MDIO_MC_OFFSET);
-       ret = axienet_mdio_wait_until_ready(lp);
-       if (ret < 0)
-               return ret;
        /* Disable the MDIO interface till Axi Ethernet Reset is completed.
         * When we do an Axi Ethernet reset, it resets the complete core
         * including the MDIO. If MDIO is not disabled when the reset
         * process is started, MDIO will be broken afterwards.
         */
-       axienet_iow(lp, XAE_MDIO_MC_OFFSET,
-                   (mdio_mcreg & (~XAE_MDIO_MC_MDIOEN_MASK)));
+       axienet_mdio_disable(lp);
        axienet_device_reset(ndev);
-       /* Enable the MDIO */
-       axienet_iow(lp, XAE_MDIO_MC_OFFSET, mdio_mcreg);
-       ret = axienet_mdio_wait_until_ready(lp);
+       ret = axienet_mdio_enable(lp);
        if (ret < 0)
                return ret;
 
@@ -1323,28 +1316,21 @@ static void axienet_dma_err_handler(unsigned long data)
 {
        u32 axienet_status;
        u32 cr, i;
-       int mdio_mcreg;
        struct axienet_local *lp = (struct axienet_local *) data;
        struct net_device *ndev = lp->ndev;
        struct axidma_bd *cur_p;
 
        axienet_setoptions(ndev, lp->options &
                           ~(XAE_OPTION_TXEN | XAE_OPTION_RXEN));
-       mdio_mcreg = axienet_ior(lp, XAE_MDIO_MC_OFFSET);
-       axienet_mdio_wait_until_ready(lp);
        /* Disable the MDIO interface till Axi Ethernet Reset is completed.
         * When we do an Axi Ethernet reset, it resets the complete core
         * including the MDIO. So if MDIO is not disabled when the reset
         * process is started, MDIO will be broken afterwards.
         */
-       axienet_iow(lp, XAE_MDIO_MC_OFFSET, (mdio_mcreg &
-                   ~XAE_MDIO_MC_MDIOEN_MASK));
-
+       axienet_mdio_disable(lp);
        __axienet_device_reset(lp, XAXIDMA_TX_CR_OFFSET);
        __axienet_device_reset(lp, XAXIDMA_RX_CR_OFFSET);
-
-       axienet_iow(lp, XAE_MDIO_MC_OFFSET, mdio_mcreg);
-       axienet_mdio_wait_until_ready(lp);
+       axienet_mdio_enable(lp);
 
        for (i = 0; i < TX_BD_NUM; i++) {
                cur_p = &lp->tx_bd_v[i];
@@ -1619,9 +1605,15 @@ static int axienet_probe(struct platform_device *pdev)
 
        lp->phy_node = of_parse_phandle(pdev->dev.of_node, "phy-handle", 0);
        if (lp->phy_node) {
-               ret = axienet_mdio_setup(lp, pdev->dev.of_node);
+               /* Defaults to 0 if not present */
+               of_property_read_u32(pdev->dev.of_node,
+                                    "xlnx,mdio-clock-divisor",
+                                    &lp->mdio_clock_divisor);
+
+               ret = axienet_mdio_setup(lp);
                if (ret)
-                       dev_warn(&pdev->dev, "error registering MDIO bus\n");
+                       dev_warn(&pdev->dev,
+                                "error registering MDIO bus: %d\n", ret);
        }
 
        ret = register_netdev(lp->ndev);
@@ -1643,8 +1635,8 @@ static int axienet_remove(struct platform_device *pdev)
        struct net_device *ndev = platform_get_drvdata(pdev);
        struct axienet_local *lp = netdev_priv(ndev);
 
-       axienet_mdio_teardown(lp);
        unregister_netdev(ndev);
+       axienet_mdio_teardown(lp);
 
        of_node_put(lp->phy_node);
        lp->phy_node = NULL;
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c 
b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c
index 665ae1d..662e005 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c
@@ -5,6 +5,7 @@
  * Copyright (c) 2009 Secret Lab Technologies, Ltd.
  * Copyright (c) 2010 - 2011 Michal Simek <mon...@monstr.eu>
  * Copyright (c) 2010 - 2011 PetaLogix
+ * Copyright (c) 2019 SED Systems, a division of Calian Ltd.
  * Copyright (c) 2010 - 2012 Xilinx, Inc. All rights reserved.
  */
 
@@ -19,7 +20,7 @@
 #define DEFAULT_CLOCK_DIVISOR  XAE_MDIO_DIV_DFT
 
 /* Wait till MDIO interface is ready to accept a new transaction.*/
-int axienet_mdio_wait_until_ready(struct axienet_local *lp)
+static int axienet_mdio_wait_until_ready(struct axienet_local *lp)
 {
        u32 val;
 
@@ -112,9 +113,97 @@ static int axienet_mdio_write(struct mii_bus *bus, int 
phy_id, int reg,
 }
 
 /**
+ * axienet_mdio_enable - MDIO hardware setup function
+ * @lp:                Pointer to axienet local data structure.
+ *
+ * Return:     0 on success, -ETIMEDOUT on a timeout.
+ *
+ * Sets up the MDIO interface by initializing the MDIO clock and enabling the
+ * MDIO interface in hardware.
+ **/
+int axienet_mdio_enable(struct axienet_local *lp)
+{
+       u32 clk_div = lp->mdio_clock_divisor, host_clock;
+
+       if (!clk_div) {
+               /* clk_div can be calculated by deriving it from the equation:
+                * fMDIO = fHOST / ((1 + clk_div) * 2)
+                *
+                * Where fMDIO <= 2500000, so we get:
+                * fHOST / ((1 + clk_div) * 2) <= 2500000
+                *
+                * Then we get:
+                * 1 / ((1 + clk_div) * 2) <= (2500000 / fHOST)
+                *
+                * Then we get:
+                * 1 / (1 + clk_div) <= ((2500000 * 2) / fHOST)
+                *
+                * Then we get:
+                * 1 / (1 + clk_div) <= (5000000 / fHOST)
+                *
+                * So:
+                * (1 + clk_div) >= (fHOST / 5000000)
+                *
+                * And finally:
+                * clk_div >= (fHOST / 5000000) - 1
+                *
+                * fHOST can be read from the flattened device tree as property
+                * "clock-frequency" from the CPU
+                */
+               struct device_node *np1 = of_find_node_by_name(NULL, "cpu");
+
+               if (!np1) {
+                       netdev_warn(lp->ndev, "Could not find CPU device 
node.\n");
+                       netdev_warn(lp->ndev,
+                                   "Setting MDIO clock divisor to default 
%d\n",
+                                   DEFAULT_CLOCK_DIVISOR);
+                       clk_div = DEFAULT_CLOCK_DIVISOR;
+                       goto issue;
+               }
+               if (of_property_read_u32(np1, "clock-frequency", &host_clock)) {
+                       netdev_warn(lp->ndev, "clock-frequency property not 
found.\n");
+                       netdev_warn(lp->ndev,
+                                   "Setting MDIO clock divisor to default 
%d\n",
+                                   DEFAULT_CLOCK_DIVISOR);
+                       clk_div = DEFAULT_CLOCK_DIVISOR;
+                       of_node_put(np1);
+                       goto issue;
+               }
+
+               clk_div = (host_clock / (MAX_MDIO_FREQ * 2)) - 1;
+               /* If there is any remainder from the division of
+                * fHOST / (MAX_MDIO_FREQ * 2), then we need to add
+                * 1 to the clock divisor or we will surely be above 2.5 MHz
+                */
+               if (host_clock % (MAX_MDIO_FREQ * 2))
+                       clk_div++;
+
+               netdev_dbg(lp->ndev,
+                          "Setting MDIO clock divisor to %u/%u Hz host 
clock.\n",
+                          clk_div, host_clock);
+
+               of_node_put(np1);
+       }
+issue:
+       axienet_iow(lp, XAE_MDIO_MC_OFFSET, clk_div | XAE_MDIO_MC_MDIOEN_MASK);
+
+       return axienet_mdio_wait_until_ready(lp);
+}
+
+/**
+ * axienet_mdio_disable - MDIO hardware disable function
+ * @lp:                Pointer to axienet local data structure.
+ *
+ * Disable the MDIO interface in hardware.
+ **/
+void axienet_mdio_disable(struct axienet_local *lp)
+{
+       axienet_iow(lp, XAE_MDIO_MC_OFFSET, 0);
+}
+
+/**
  * axienet_mdio_setup - MDIO setup function
  * @lp:                Pointer to axienet local data structure.
- * @np:                Pointer to device node
  *
  * Return:     0 on success, -ETIMEDOUT on a timeout, -ENOMEM when
  *             mdiobus_alloc (to allocate memory for mii bus structure) fails.
@@ -122,76 +211,13 @@ static int axienet_mdio_write(struct mii_bus *bus, int 
phy_id, int reg,
  * Sets up the MDIO interface by initializing the MDIO clock and enabling the
  * MDIO interface in hardware. Register the MDIO interface.
  **/
-int axienet_mdio_setup(struct axienet_local *lp, struct device_node *np)
+int axienet_mdio_setup(struct axienet_local *lp)
 {
        int ret;
-       u32 clk_div, host_clock;
        struct mii_bus *bus;
        struct device_node *mdio_node;
-       struct device_node *np1;
-
-       /* clk_div can be calculated by deriving it from the equation:
-        * fMDIO = fHOST / ((1 + clk_div) * 2)
-        *
-        * Where fMDIO <= 2500000, so we get:
-        * fHOST / ((1 + clk_div) * 2) <= 2500000
-        *
-        * Then we get:
-        * 1 / ((1 + clk_div) * 2) <= (2500000 / fHOST)
-        *
-        * Then we get:
-        * 1 / (1 + clk_div) <= ((2500000 * 2) / fHOST)
-        *
-        * Then we get:
-        * 1 / (1 + clk_div) <= (5000000 / fHOST)
-        *
-        * So:
-        * (1 + clk_div) >= (fHOST / 5000000)
-        *
-        * And finally:
-        * clk_div >= (fHOST / 5000000) - 1
-        *
-        * fHOST can be read from the flattened device tree as property
-        * "clock-frequency" from the CPU
-        */
-
-       np1 = of_find_node_by_name(NULL, "cpu");
-       if (!np1) {
-               netdev_warn(lp->ndev, "Could not find CPU device node.\n");
-               netdev_warn(lp->ndev,
-                           "Setting MDIO clock divisor to default %d\n",
-                           DEFAULT_CLOCK_DIVISOR);
-               clk_div = DEFAULT_CLOCK_DIVISOR;
-               goto issue;
-       }
-       if (of_property_read_u32(np1, "clock-frequency", &host_clock)) {
-               netdev_warn(lp->ndev, "clock-frequency property not found.\n");
-               netdev_warn(lp->ndev,
-                           "Setting MDIO clock divisor to default %d\n",
-                           DEFAULT_CLOCK_DIVISOR);
-               clk_div = DEFAULT_CLOCK_DIVISOR;
-               of_node_put(np1);
-               goto issue;
-       }
-
-       clk_div = (host_clock / (MAX_MDIO_FREQ * 2)) - 1;
-       /* If there is any remainder from the division of
-        * fHOST / (MAX_MDIO_FREQ * 2), then we need to add
-        * 1 to the clock divisor or we will surely be above 2.5 MHz
-        */
-       if (host_clock % (MAX_MDIO_FREQ * 2))
-               clk_div++;
 
-       netdev_dbg(lp->ndev,
-                  "Setting MDIO clock divisor to %u/%u Hz host clock.\n",
-                  clk_div, host_clock);
-
-       of_node_put(np1);
-issue:
-       axienet_iow(lp, XAE_MDIO_MC_OFFSET,
-                   (((u32) clk_div) | XAE_MDIO_MC_MDIOEN_MASK));
-
-       ret = axienet_mdio_wait_until_ready(lp);
+       ret = axienet_mdio_enable(lp);
        if (ret < 0)
                return ret;
 
-- 
1.8.3.1

Reply via email to