Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * MDIO bus driver for the Xilinx Axi Ethernet device
0004  *
0005  * Copyright (c) 2009 Secret Lab Technologies, Ltd.
0006  * Copyright (c) 2010 - 2011 Michal Simek <monstr@monstr.eu>
0007  * Copyright (c) 2010 - 2011 PetaLogix
0008  * Copyright (c) 2019 SED Systems, a division of Calian Ltd.
0009  * Copyright (c) 2010 - 2012 Xilinx, Inc. All rights reserved.
0010  */
0011 
0012 #include <linux/clk.h>
0013 #include <linux/of_address.h>
0014 #include <linux/of_mdio.h>
0015 #include <linux/jiffies.h>
0016 #include <linux/iopoll.h>
0017 
0018 #include "xilinx_axienet.h"
0019 
0020 #define MAX_MDIO_FREQ       2500000 /* 2.5 MHz */
0021 #define DEFAULT_HOST_CLOCK  150000000 /* 150 MHz */
0022 
0023 /* Wait till MDIO interface is ready to accept a new transaction.*/
0024 static int axienet_mdio_wait_until_ready(struct axienet_local *lp)
0025 {
0026     u32 val;
0027 
0028     return readx_poll_timeout(axinet_ior_read_mcr, lp,
0029                   val, val & XAE_MDIO_MCR_READY_MASK,
0030                   1, 20000);
0031 }
0032 
0033 /* Enable the MDIO MDC. Called prior to a read/write operation */
0034 static void axienet_mdio_mdc_enable(struct axienet_local *lp)
0035 {
0036     axienet_iow(lp, XAE_MDIO_MC_OFFSET,
0037             ((u32)lp->mii_clk_div | XAE_MDIO_MC_MDIOEN_MASK));
0038 }
0039 
0040 /* Disable the MDIO MDC. Called after a read/write operation*/
0041 static void axienet_mdio_mdc_disable(struct axienet_local *lp)
0042 {
0043     u32 mc_reg;
0044 
0045     mc_reg = axienet_ior(lp, XAE_MDIO_MC_OFFSET);
0046     axienet_iow(lp, XAE_MDIO_MC_OFFSET,
0047             (mc_reg & ~XAE_MDIO_MC_MDIOEN_MASK));
0048 }
0049 
0050 /**
0051  * axienet_mdio_read - MDIO interface read function
0052  * @bus:    Pointer to mii bus structure
0053  * @phy_id: Address of the PHY device
0054  * @reg:    PHY register to read
0055  *
0056  * Return:  The register contents on success, -ETIMEDOUT on a timeout
0057  *
0058  * Reads the contents of the requested register from the requested PHY
0059  * address by first writing the details into MCR register. After a while
0060  * the register MRD is read to obtain the PHY register content.
0061  */
0062 static int axienet_mdio_read(struct mii_bus *bus, int phy_id, int reg)
0063 {
0064     u32 rc;
0065     int ret;
0066     struct axienet_local *lp = bus->priv;
0067 
0068     axienet_mdio_mdc_enable(lp);
0069 
0070     ret = axienet_mdio_wait_until_ready(lp);
0071     if (ret < 0) {
0072         axienet_mdio_mdc_disable(lp);
0073         return ret;
0074     }
0075 
0076     axienet_iow(lp, XAE_MDIO_MCR_OFFSET,
0077             (((phy_id << XAE_MDIO_MCR_PHYAD_SHIFT) &
0078               XAE_MDIO_MCR_PHYAD_MASK) |
0079              ((reg << XAE_MDIO_MCR_REGAD_SHIFT) &
0080               XAE_MDIO_MCR_REGAD_MASK) |
0081              XAE_MDIO_MCR_INITIATE_MASK |
0082              XAE_MDIO_MCR_OP_READ_MASK));
0083 
0084     ret = axienet_mdio_wait_until_ready(lp);
0085     if (ret < 0) {
0086         axienet_mdio_mdc_disable(lp);
0087         return ret;
0088     }
0089 
0090     rc = axienet_ior(lp, XAE_MDIO_MRD_OFFSET) & 0x0000FFFF;
0091 
0092     dev_dbg(lp->dev, "axienet_mdio_read(phy_id=%i, reg=%x) == %x\n",
0093         phy_id, reg, rc);
0094 
0095     axienet_mdio_mdc_disable(lp);
0096     return rc;
0097 }
0098 
0099 /**
0100  * axienet_mdio_write - MDIO interface write function
0101  * @bus:    Pointer to mii bus structure
0102  * @phy_id: Address of the PHY device
0103  * @reg:    PHY register to write to
0104  * @val:    Value to be written into the register
0105  *
0106  * Return:  0 on success, -ETIMEDOUT on a timeout
0107  *
0108  * Writes the value to the requested register by first writing the value
0109  * into MWD register. The MCR register is then appropriately setup
0110  * to finish the write operation.
0111  */
0112 static int axienet_mdio_write(struct mii_bus *bus, int phy_id, int reg,
0113                   u16 val)
0114 {
0115     int ret;
0116     struct axienet_local *lp = bus->priv;
0117 
0118     dev_dbg(lp->dev, "axienet_mdio_write(phy_id=%i, reg=%x, val=%x)\n",
0119         phy_id, reg, val);
0120 
0121     axienet_mdio_mdc_enable(lp);
0122 
0123     ret = axienet_mdio_wait_until_ready(lp);
0124     if (ret < 0) {
0125         axienet_mdio_mdc_disable(lp);
0126         return ret;
0127     }
0128 
0129     axienet_iow(lp, XAE_MDIO_MWD_OFFSET, (u32) val);
0130     axienet_iow(lp, XAE_MDIO_MCR_OFFSET,
0131             (((phy_id << XAE_MDIO_MCR_PHYAD_SHIFT) &
0132               XAE_MDIO_MCR_PHYAD_MASK) |
0133              ((reg << XAE_MDIO_MCR_REGAD_SHIFT) &
0134               XAE_MDIO_MCR_REGAD_MASK) |
0135              XAE_MDIO_MCR_INITIATE_MASK |
0136              XAE_MDIO_MCR_OP_WRITE_MASK));
0137 
0138     ret = axienet_mdio_wait_until_ready(lp);
0139     if (ret < 0) {
0140         axienet_mdio_mdc_disable(lp);
0141         return ret;
0142     }
0143     axienet_mdio_mdc_disable(lp);
0144     return 0;
0145 }
0146 
0147 /**
0148  * axienet_mdio_enable - MDIO hardware setup function
0149  * @lp:     Pointer to axienet local data structure.
0150  *
0151  * Return:  0 on success, -ETIMEDOUT on a timeout.
0152  *
0153  * Sets up the MDIO interface by initializing the MDIO clock and enabling the
0154  * MDIO interface in hardware.
0155  **/
0156 int axienet_mdio_enable(struct axienet_local *lp)
0157 {
0158     u32 host_clock;
0159 
0160     lp->mii_clk_div = 0;
0161 
0162     if (lp->axi_clk) {
0163         host_clock = clk_get_rate(lp->axi_clk);
0164     } else {
0165         struct device_node *np1;
0166 
0167         /* Legacy fallback: detect CPU clock frequency and use as AXI
0168          * bus clock frequency. This only works on certain platforms.
0169          */
0170         np1 = of_find_node_by_name(NULL, "cpu");
0171         if (!np1) {
0172             netdev_warn(lp->ndev, "Could not find CPU device node.\n");
0173             host_clock = DEFAULT_HOST_CLOCK;
0174         } else {
0175             int ret = of_property_read_u32(np1, "clock-frequency",
0176                                &host_clock);
0177             if (ret) {
0178                 netdev_warn(lp->ndev, "CPU clock-frequency property not found.\n");
0179                 host_clock = DEFAULT_HOST_CLOCK;
0180             }
0181             of_node_put(np1);
0182         }
0183         netdev_info(lp->ndev, "Setting assumed host clock to %u\n",
0184                 host_clock);
0185     }
0186 
0187     /* clk_div can be calculated by deriving it from the equation:
0188      * fMDIO = fHOST / ((1 + clk_div) * 2)
0189      *
0190      * Where fMDIO <= 2500000, so we get:
0191      * fHOST / ((1 + clk_div) * 2) <= 2500000
0192      *
0193      * Then we get:
0194      * 1 / ((1 + clk_div) * 2) <= (2500000 / fHOST)
0195      *
0196      * Then we get:
0197      * 1 / (1 + clk_div) <= ((2500000 * 2) / fHOST)
0198      *
0199      * Then we get:
0200      * 1 / (1 + clk_div) <= (5000000 / fHOST)
0201      *
0202      * So:
0203      * (1 + clk_div) >= (fHOST / 5000000)
0204      *
0205      * And finally:
0206      * clk_div >= (fHOST / 5000000) - 1
0207      *
0208      * fHOST can be read from the flattened device tree as property
0209      * "clock-frequency" from the CPU
0210      */
0211 
0212     lp->mii_clk_div = (host_clock / (MAX_MDIO_FREQ * 2)) - 1;
0213     /* If there is any remainder from the division of
0214      * fHOST / (MAX_MDIO_FREQ * 2), then we need to add
0215      * 1 to the clock divisor or we will surely be above 2.5 MHz
0216      */
0217     if (host_clock % (MAX_MDIO_FREQ * 2))
0218         lp->mii_clk_div++;
0219 
0220     netdev_dbg(lp->ndev,
0221            "Setting MDIO clock divisor to %u/%u Hz host clock.\n",
0222            lp->mii_clk_div, host_clock);
0223 
0224     axienet_iow(lp, XAE_MDIO_MC_OFFSET, lp->mii_clk_div | XAE_MDIO_MC_MDIOEN_MASK);
0225 
0226     return axienet_mdio_wait_until_ready(lp);
0227 }
0228 
0229 /**
0230  * axienet_mdio_disable - MDIO hardware disable function
0231  * @lp:     Pointer to axienet local data structure.
0232  *
0233  * Disable the MDIO interface in hardware.
0234  **/
0235 void axienet_mdio_disable(struct axienet_local *lp)
0236 {
0237     axienet_iow(lp, XAE_MDIO_MC_OFFSET, 0);
0238 }
0239 
0240 /**
0241  * axienet_mdio_setup - MDIO setup function
0242  * @lp:     Pointer to axienet local data structure.
0243  *
0244  * Return:  0 on success, -ETIMEDOUT on a timeout, -ENOMEM when
0245  *      mdiobus_alloc (to allocate memory for mii bus structure) fails.
0246  *
0247  * Sets up the MDIO interface by initializing the MDIO clock.
0248  * Register the MDIO interface.
0249  **/
0250 int axienet_mdio_setup(struct axienet_local *lp)
0251 {
0252     struct device_node *mdio_node;
0253     struct mii_bus *bus;
0254     int ret;
0255 
0256     ret = axienet_mdio_enable(lp);
0257     if (ret < 0)
0258         return ret;
0259 
0260     bus = mdiobus_alloc();
0261     if (!bus)
0262         return -ENOMEM;
0263 
0264     snprintf(bus->id, MII_BUS_ID_SIZE, "axienet-%.8llx",
0265          (unsigned long long)lp->regs_start);
0266 
0267     bus->priv = lp;
0268     bus->name = "Xilinx Axi Ethernet MDIO";
0269     bus->read = axienet_mdio_read;
0270     bus->write = axienet_mdio_write;
0271     bus->parent = lp->dev;
0272     lp->mii_bus = bus;
0273 
0274     mdio_node = of_get_child_by_name(lp->dev->of_node, "mdio");
0275     ret = of_mdiobus_register(bus, mdio_node);
0276     of_node_put(mdio_node);
0277     if (ret) {
0278         mdiobus_free(bus);
0279         lp->mii_bus = NULL;
0280         return ret;
0281     }
0282     axienet_mdio_mdc_disable(lp);
0283     return 0;
0284 }
0285 
0286 /**
0287  * axienet_mdio_teardown - MDIO remove function
0288  * @lp:     Pointer to axienet local data structure.
0289  *
0290  * Unregisters the MDIO and frees any associate memory for mii bus.
0291  */
0292 void axienet_mdio_teardown(struct axienet_local *lp)
0293 {
0294     mdiobus_unregister(lp->mii_bus);
0295     mdiobus_free(lp->mii_bus);
0296     lp->mii_bus = NULL;
0297 }