diff options
Diffstat (limited to 'drivers/net/ethernet/xilinx')
-rw-r--r-- | drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c | 70 |
1 files changed, 49 insertions, 21 deletions
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c index e1f51a071888..2f07fde361aa 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c @@ -17,7 +17,7 @@ #include "xilinx_axienet.h" -#define MAX_MDIO_FREQ 2500000 /* 2.5 MHz */ +#define DEFAULT_MDIO_FREQ 2500000 /* 2.5 MHz */ #define DEFAULT_HOST_CLOCK 150000000 /* 150 MHz */ /* Wait till MDIO interface is ready to accept a new transaction.*/ @@ -147,15 +147,20 @@ 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. + * @np: Pointer to mdio device tree node. * - * Return: 0 on success, -ETIMEDOUT on a timeout. + * Return: 0 on success, -ETIMEDOUT on a timeout, -EOVERFLOW on a clock + * divisor overflow. * * Sets up the MDIO interface by initializing the MDIO clock and enabling the * MDIO interface in hardware. **/ -static int axienet_mdio_enable(struct axienet_local *lp) +static int axienet_mdio_enable(struct axienet_local *lp, struct device_node *np) { + u32 mdio_freq = DEFAULT_MDIO_FREQ; u32 host_clock; + u32 clk_div; + int ret; lp->mii_clk_div = 0; @@ -184,6 +189,12 @@ static int axienet_mdio_enable(struct axienet_local *lp) host_clock); } + if (np) + of_property_read_u32(np, "clock-frequency", &mdio_freq); + if (mdio_freq != DEFAULT_MDIO_FREQ) + netdev_info(lp->ndev, "Setting non-standard mdio bus frequency to %u Hz\n", + mdio_freq); + /* clk_div can be calculated by deriving it from the equation: * fMDIO = fHOST / ((1 + clk_div) * 2) * @@ -209,29 +220,42 @@ static int axienet_mdio_enable(struct axienet_local *lp) * "clock-frequency" from the CPU */ - lp->mii_clk_div = (host_clock / (MAX_MDIO_FREQ * 2)) - 1; + clk_div = (host_clock / (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 + * fHOST / (mdio_freq * 2), then we need to add + * 1 to the clock divisor or we will surely be + * above the requested frequency */ - if (host_clock % (MAX_MDIO_FREQ * 2)) - lp->mii_clk_div++; + if (host_clock % (mdio_freq * 2)) + clk_div++; + + /* Check for overflow of mii_clk_div */ + if (clk_div & ~XAE_MDIO_MC_CLOCK_DIVIDE_MAX) { + netdev_warn(lp->ndev, "MDIO clock divisor overflow\n"); + return -EOVERFLOW; + } + lp->mii_clk_div = (u8)clk_div; netdev_dbg(lp->ndev, "Setting MDIO clock divisor to %u/%u Hz host clock.\n", lp->mii_clk_div, host_clock); - axienet_iow(lp, XAE_MDIO_MC_OFFSET, lp->mii_clk_div | XAE_MDIO_MC_MDIOEN_MASK); + axienet_mdio_mdc_enable(lp); - return axienet_mdio_wait_until_ready(lp); + ret = axienet_mdio_wait_until_ready(lp); + if (ret) + axienet_mdio_mdc_disable(lp); + + return ret; } /** * axienet_mdio_setup - MDIO setup function * @lp: Pointer to axienet local data structure. * - * Return: 0 on success, -ETIMEDOUT on a timeout, -ENOMEM when - * mdiobus_alloc (to allocate memory for mii bus structure) fails. + * Return: 0 on success, -ETIMEDOUT on a timeout, -EOVERFLOW on a clock + * divisor overflow, -ENOMEM when mdiobus_alloc (to allocate + * memory for mii bus structure) fails. * * Sets up the MDIO interface by initializing the MDIO clock. * Register the MDIO interface. @@ -242,10 +266,6 @@ int axienet_mdio_setup(struct axienet_local *lp) struct mii_bus *bus; int ret; - ret = axienet_mdio_enable(lp); - if (ret < 0) - return ret; - bus = mdiobus_alloc(); if (!bus) return -ENOMEM; @@ -261,15 +281,23 @@ int axienet_mdio_setup(struct axienet_local *lp) lp->mii_bus = bus; mdio_node = of_get_child_by_name(lp->dev->of_node, "mdio"); + ret = axienet_mdio_enable(lp, mdio_node); + if (ret < 0) + goto unregister; ret = of_mdiobus_register(bus, mdio_node); + if (ret) + goto unregister_mdio_enabled; of_node_put(mdio_node); - if (ret) { - mdiobus_free(bus); - lp->mii_bus = NULL; - return ret; - } axienet_mdio_mdc_disable(lp); return 0; + +unregister_mdio_enabled: + axienet_mdio_mdc_disable(lp); +unregister: + of_node_put(mdio_node); + mdiobus_free(bus); + lp->mii_bus = NULL; + return ret; } /** |