linux/drivers/net/ethernet/xilinx/xilinx_axienet_mdio.c
<<
>>
Prefs
   1/*
   2 * MDIO bus driver for the Xilinx Axi Ethernet device
   3 *
   4 * Copyright (c) 2009 Secret Lab Technologies, Ltd.
   5 * Copyright (c) 2010 - 2011 Michal Simek <monstr@monstr.eu>
   6 * Copyright (c) 2010 - 2011 PetaLogix
   7 * Copyright (c) 2010 - 2012 Xilinx, Inc. All rights reserved.
   8 */
   9
  10#include <linux/of_address.h>
  11#include <linux/of_mdio.h>
  12#include <linux/jiffies.h>
  13
  14#include "xilinx_axienet.h"
  15
  16#define MAX_MDIO_FREQ           2500000 /* 2.5 MHz */
  17#define DEFAULT_CLOCK_DIVISOR   XAE_MDIO_DIV_DFT
  18
  19/* Wait till MDIO interface is ready to accept a new transaction.*/
  20int axienet_mdio_wait_until_ready(struct axienet_local *lp)
  21{
  22        unsigned long end = jiffies + 2;
  23
  24        while (!(axienet_ior(lp, XAE_MDIO_MCR_OFFSET) &
  25                 XAE_MDIO_MCR_READY_MASK)) {
  26                if (time_before_eq(end, jiffies)) {
  27                        WARN_ON(1);
  28                        return -ETIMEDOUT;
  29                }
  30                udelay(1);
  31        }
  32        return 0;
  33}
  34
  35/**
  36 * axienet_mdio_read - MDIO interface read function
  37 * @bus:        Pointer to mii bus structure
  38 * @phy_id:     Address of the PHY device
  39 * @reg:        PHY register to read
  40 *
  41 * Return:      The register contents on success, -ETIMEDOUT on a timeout
  42 *
  43 * Reads the contents of the requested register from the requested PHY
  44 * address by first writing the details into MCR register. After a while
  45 * the register MRD is read to obtain the PHY register content.
  46 */
  47static int axienet_mdio_read(struct mii_bus *bus, int phy_id, int reg)
  48{
  49        u32 rc;
  50        int ret;
  51        struct axienet_local *lp = bus->priv;
  52
  53        ret = axienet_mdio_wait_until_ready(lp);
  54        if (ret < 0)
  55                return ret;
  56
  57        axienet_iow(lp, XAE_MDIO_MCR_OFFSET,
  58                    (((phy_id << XAE_MDIO_MCR_PHYAD_SHIFT) &
  59                      XAE_MDIO_MCR_PHYAD_MASK) |
  60                     ((reg << XAE_MDIO_MCR_REGAD_SHIFT) &
  61                      XAE_MDIO_MCR_REGAD_MASK) |
  62                     XAE_MDIO_MCR_INITIATE_MASK |
  63                     XAE_MDIO_MCR_OP_READ_MASK));
  64
  65        ret = axienet_mdio_wait_until_ready(lp);
  66        if (ret < 0)
  67                return ret;
  68
  69        rc = axienet_ior(lp, XAE_MDIO_MRD_OFFSET) & 0x0000FFFF;
  70
  71        dev_dbg(lp->dev, "axienet_mdio_read(phy_id=%i, reg=%x) == %x\n",
  72                phy_id, reg, rc);
  73
  74        return rc;
  75}
  76
  77/**
  78 * axienet_mdio_write - MDIO interface write function
  79 * @bus:        Pointer to mii bus structure
  80 * @phy_id:     Address of the PHY device
  81 * @reg:        PHY register to write to
  82 * @val:        Value to be written into the register
  83 *
  84 * Return:      0 on success, -ETIMEDOUT on a timeout
  85 *
  86 * Writes the value to the requested register by first writing the value
  87 * into MWD register. The the MCR register is then appropriately setup
  88 * to finish the write operation.
  89 */
  90static int axienet_mdio_write(struct mii_bus *bus, int phy_id, int reg,
  91                              u16 val)
  92{
  93        int ret;
  94        struct axienet_local *lp = bus->priv;
  95
  96        dev_dbg(lp->dev, "axienet_mdio_write(phy_id=%i, reg=%x, val=%x)\n",
  97                phy_id, reg, val);
  98
  99        ret = axienet_mdio_wait_until_ready(lp);
 100        if (ret < 0)
 101                return ret;
 102
 103        axienet_iow(lp, XAE_MDIO_MWD_OFFSET, (u32) val);
 104        axienet_iow(lp, XAE_MDIO_MCR_OFFSET,
 105                    (((phy_id << XAE_MDIO_MCR_PHYAD_SHIFT) &
 106                      XAE_MDIO_MCR_PHYAD_MASK) |
 107                     ((reg << XAE_MDIO_MCR_REGAD_SHIFT) &
 108                      XAE_MDIO_MCR_REGAD_MASK) |
 109                     XAE_MDIO_MCR_INITIATE_MASK |
 110                     XAE_MDIO_MCR_OP_WRITE_MASK));
 111
 112        ret = axienet_mdio_wait_until_ready(lp);
 113        if (ret < 0)
 114                return ret;
 115        return 0;
 116}
 117
 118/**
 119 * axienet_mdio_setup - MDIO setup function
 120 * @lp:         Pointer to axienet local data structure.
 121 * @np:         Pointer to device node
 122 *
 123 * Return:      0 on success, -ETIMEDOUT on a timeout, -ENOMEM when
 124 *              mdiobus_alloc (to allocate memory for mii bus structure) fails.
 125 *
 126 * Sets up the MDIO interface by initializing the MDIO clock and enabling the
 127 * MDIO interface in hardware. Register the MDIO interface.
 128 **/
 129int axienet_mdio_setup(struct axienet_local *lp, struct device_node *np)
 130{
 131        int ret;
 132        u32 clk_div, host_clock;
 133
 134        struct mii_bus *bus;
 135        struct resource res;
 136        struct device_node *np1;
 137        /* the ethernet controller device node */
 138        struct device_node *npp = NULL;
 139
 140        /* clk_div can be calculated by deriving it from the equation:
 141         * fMDIO = fHOST / ((1 + clk_div) * 2)
 142         *
 143         * Where fMDIO <= 2500000, so we get:
 144         * fHOST / ((1 + clk_div) * 2) <= 2500000
 145         *
 146         * Then we get:
 147         * 1 / ((1 + clk_div) * 2) <= (2500000 / fHOST)
 148         *
 149         * Then we get:
 150         * 1 / (1 + clk_div) <= ((2500000 * 2) / fHOST)
 151         *
 152         * Then we get:
 153         * 1 / (1 + clk_div) <= (5000000 / fHOST)
 154         *
 155         * So:
 156         * (1 + clk_div) >= (fHOST / 5000000)
 157         *
 158         * And finally:
 159         * clk_div >= (fHOST / 5000000) - 1
 160         *
 161         * fHOST can be read from the flattened device tree as property
 162         * "clock-frequency" from the CPU
 163         */
 164        np1 = of_get_parent(lp->phy_node);
 165        if (np1) {
 166                npp = of_get_parent(np1);
 167                of_node_put(np1);
 168        }
 169        if (!npp) {
 170                dev_warn(lp->dev,
 171                        "Could not find ethernet controller device node.");
 172                dev_warn(lp->dev, "Setting MDIO clock divisor to default %d\n",
 173                       DEFAULT_CLOCK_DIVISOR);
 174                clk_div = DEFAULT_CLOCK_DIVISOR;
 175        } else {
 176                if (of_property_read_u32(npp, "clock-frequency", &host_clock)) {
 177                        netdev_warn(lp->ndev,
 178                                    "clock-frequency property not found.\n");
 179                        netdev_warn(lp->ndev,
 180                                    "Setting MDIO clock divisor to default %d\n",
 181                                    DEFAULT_CLOCK_DIVISOR);
 182                        clk_div = DEFAULT_CLOCK_DIVISOR;
 183                } else {
 184                        clk_div = (host_clock / (MAX_MDIO_FREQ * 2)) - 1;
 185
 186                        /* If there is any remainder from the division of
 187                         * fHOST / (MAX_MDIO_FREQ * 2), then we need to add 1
 188                         * to the clock divisor or we will surely be
 189                         * above 2.5 MHz
 190                         */
 191                        if (host_clock % (MAX_MDIO_FREQ * 2))
 192                                clk_div++;
 193                        dev_dbg(lp->dev,
 194                                "Setting MDIO clock divisor to %u "
 195                                "based on %u Hz host clock.\n",
 196                                clk_div, host_clock);
 197                }
 198                of_node_put(npp);
 199        }
 200
 201        axienet_iow(lp, XAE_MDIO_MC_OFFSET,
 202                    (((u32)clk_div) | XAE_MDIO_MC_MDIOEN_MASK));
 203
 204        ret = axienet_mdio_wait_until_ready(lp);
 205        if (ret < 0)
 206                return ret;
 207
 208        bus = mdiobus_alloc();
 209        if (!bus)
 210                return -ENOMEM;
 211
 212        of_address_to_resource(npp, 0, &res);
 213        snprintf(bus->id, MII_BUS_ID_SIZE, "%.8llx",
 214                 (unsigned long long) res.start);
 215
 216        bus->priv = lp;
 217        bus->name = "Xilinx Axi Ethernet MDIO";
 218        bus->read = axienet_mdio_read;
 219        bus->write = axienet_mdio_write;
 220        bus->parent = lp->dev;
 221        lp->mii_bus = bus;
 222
 223        ret = of_mdiobus_register(bus, np1);
 224        if (ret) {
 225                mdiobus_free(bus);
 226                return ret;
 227        }
 228        return 0;
 229}
 230
 231/**
 232 * axienet_mdio_teardown - MDIO remove function
 233 * @lp:         Pointer to axienet local data structure.
 234 *
 235 * Unregisters the MDIO and frees any associate memory for mii bus.
 236 */
 237void axienet_mdio_teardown(struct axienet_local *lp)
 238{
 239        mdiobus_unregister(lp->mii_bus);
 240        mdiobus_free(lp->mii_bus);
 241        lp->mii_bus = NULL;
 242}
 243