332 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * MDIO bus driver for the Xilinx Axi Ethernet device4 *5 * Copyright (c) 2009 Secret Lab Technologies, Ltd.6 * Copyright (c) 2010 - 2011 Michal Simek <monstr@monstr.eu>7 * Copyright (c) 2010 - 2011 PetaLogix8 * Copyright (c) 2019 SED Systems, a division of Calian Ltd.9 * Copyright (c) 2010 - 2012 Xilinx, Inc. All rights reserved.10 */11 12#include <linux/clk.h>13#include <linux/of_address.h>14#include <linux/of_mdio.h>15#include <linux/jiffies.h>16#include <linux/iopoll.h>17 18#include "xilinx_axienet.h"19 20#define DEFAULT_MDIO_FREQ 2500000 /* 2.5 MHz */21#define DEFAULT_HOST_CLOCK 150000000 /* 150 MHz */22 23/**24 * axienet_mdio_wait_until_ready - MDIO wait function25 * @lp: Pointer to axienet local data structure.26 *27 * Return : 0 on success, Negative value on errors28 *29 * Wait till MDIO interface is ready to accept a new transaction.30 */31static int axienet_mdio_wait_until_ready(struct axienet_local *lp)32{33 u32 val;34 35 return readx_poll_timeout(axinet_ior_read_mcr, lp,36 val, val & XAE_MDIO_MCR_READY_MASK,37 1, 20000);38}39 40/**41 * axienet_mdio_mdc_enable - MDIO MDC enable function42 * @lp: Pointer to axienet local data structure.43 *44 * Enable the MDIO MDC. Called prior to a read/write operation45 */46static void axienet_mdio_mdc_enable(struct axienet_local *lp)47{48 axienet_iow(lp, XAE_MDIO_MC_OFFSET,49 ((u32)lp->mii_clk_div | XAE_MDIO_MC_MDIOEN_MASK));50}51 52/**53 * axienet_mdio_mdc_disable - MDIO MDC disable function54 * @lp: Pointer to axienet local data structure.55 *56 * Disable the MDIO MDC. Called after a read/write operation57 */58static void axienet_mdio_mdc_disable(struct axienet_local *lp)59{60 u32 mc_reg;61 62 mc_reg = axienet_ior(lp, XAE_MDIO_MC_OFFSET);63 axienet_iow(lp, XAE_MDIO_MC_OFFSET,64 (mc_reg & ~XAE_MDIO_MC_MDIOEN_MASK));65}66 67/**68 * axienet_mdio_read - MDIO interface read function69 * @bus: Pointer to mii bus structure70 * @phy_id: Address of the PHY device71 * @reg: PHY register to read72 *73 * Return: The register contents on success, -ETIMEDOUT on a timeout74 *75 * Reads the contents of the requested register from the requested PHY76 * address by first writing the details into MCR register. After a while77 * the register MRD is read to obtain the PHY register content.78 */79static int axienet_mdio_read(struct mii_bus *bus, int phy_id, int reg)80{81 u32 rc;82 int ret;83 struct axienet_local *lp = bus->priv;84 85 axienet_mdio_mdc_enable(lp);86 87 ret = axienet_mdio_wait_until_ready(lp);88 if (ret < 0) {89 axienet_mdio_mdc_disable(lp);90 return ret;91 }92 93 axienet_iow(lp, XAE_MDIO_MCR_OFFSET,94 (((phy_id << XAE_MDIO_MCR_PHYAD_SHIFT) &95 XAE_MDIO_MCR_PHYAD_MASK) |96 ((reg << XAE_MDIO_MCR_REGAD_SHIFT) &97 XAE_MDIO_MCR_REGAD_MASK) |98 XAE_MDIO_MCR_INITIATE_MASK |99 XAE_MDIO_MCR_OP_READ_MASK));100 101 ret = axienet_mdio_wait_until_ready(lp);102 if (ret < 0) {103 axienet_mdio_mdc_disable(lp);104 return ret;105 }106 107 rc = axienet_ior(lp, XAE_MDIO_MRD_OFFSET) & 0x0000FFFF;108 109 dev_dbg(lp->dev, "axienet_mdio_read(phy_id=%i, reg=%x) == %x\n",110 phy_id, reg, rc);111 112 axienet_mdio_mdc_disable(lp);113 return rc;114}115 116/**117 * axienet_mdio_write - MDIO interface write function118 * @bus: Pointer to mii bus structure119 * @phy_id: Address of the PHY device120 * @reg: PHY register to write to121 * @val: Value to be written into the register122 *123 * Return: 0 on success, -ETIMEDOUT on a timeout124 *125 * Writes the value to the requested register by first writing the value126 * into MWD register. The MCR register is then appropriately setup127 * to finish the write operation.128 */129static int axienet_mdio_write(struct mii_bus *bus, int phy_id, int reg,130 u16 val)131{132 int ret;133 struct axienet_local *lp = bus->priv;134 135 dev_dbg(lp->dev, "axienet_mdio_write(phy_id=%i, reg=%x, val=%x)\n",136 phy_id, reg, val);137 138 axienet_mdio_mdc_enable(lp);139 140 ret = axienet_mdio_wait_until_ready(lp);141 if (ret < 0) {142 axienet_mdio_mdc_disable(lp);143 return ret;144 }145 146 axienet_iow(lp, XAE_MDIO_MWD_OFFSET, (u32)val);147 axienet_iow(lp, XAE_MDIO_MCR_OFFSET,148 (((phy_id << XAE_MDIO_MCR_PHYAD_SHIFT) &149 XAE_MDIO_MCR_PHYAD_MASK) |150 ((reg << XAE_MDIO_MCR_REGAD_SHIFT) &151 XAE_MDIO_MCR_REGAD_MASK) |152 XAE_MDIO_MCR_INITIATE_MASK |153 XAE_MDIO_MCR_OP_WRITE_MASK));154 155 ret = axienet_mdio_wait_until_ready(lp);156 if (ret < 0) {157 axienet_mdio_mdc_disable(lp);158 return ret;159 }160 axienet_mdio_mdc_disable(lp);161 return 0;162}163 164/**165 * axienet_mdio_enable - MDIO hardware setup function166 * @lp: Pointer to axienet local data structure.167 * @np: Pointer to mdio device tree node.168 *169 * Return: 0 on success, -ETIMEDOUT on a timeout, -EOVERFLOW on a clock170 * divisor overflow.171 *172 * Sets up the MDIO interface by initializing the MDIO clock and enabling the173 * MDIO interface in hardware.174 **/175static int axienet_mdio_enable(struct axienet_local *lp, struct device_node *np)176{177 u32 mdio_freq = DEFAULT_MDIO_FREQ;178 u32 host_clock;179 u32 clk_div;180 int ret;181 182 lp->mii_clk_div = 0;183 184 if (lp->axi_clk) {185 host_clock = clk_get_rate(lp->axi_clk);186 } else {187 struct device_node *np1;188 189 /* Legacy fallback: detect CPU clock frequency and use as AXI190 * bus clock frequency. This only works on certain platforms.191 */192 np1 = of_find_node_by_name(NULL, "cpu");193 if (!np1) {194 netdev_warn(lp->ndev, "Could not find CPU device node.\n");195 host_clock = DEFAULT_HOST_CLOCK;196 } else {197 int ret = of_property_read_u32(np1, "clock-frequency",198 &host_clock);199 if (ret) {200 netdev_warn(lp->ndev, "CPU clock-frequency property not found.\n");201 host_clock = DEFAULT_HOST_CLOCK;202 }203 of_node_put(np1);204 }205 netdev_info(lp->ndev, "Setting assumed host clock to %u\n",206 host_clock);207 }208 209 if (np)210 of_property_read_u32(np, "clock-frequency", &mdio_freq);211 if (mdio_freq != DEFAULT_MDIO_FREQ)212 netdev_info(lp->ndev, "Setting non-standard mdio bus frequency to %u Hz\n",213 mdio_freq);214 215 /* clk_div can be calculated by deriving it from the equation:216 * fMDIO = fHOST / ((1 + clk_div) * 2)217 *218 * Where fMDIO <= 2500000, so we get:219 * fHOST / ((1 + clk_div) * 2) <= 2500000220 *221 * Then we get:222 * 1 / ((1 + clk_div) * 2) <= (2500000 / fHOST)223 *224 * Then we get:225 * 1 / (1 + clk_div) <= ((2500000 * 2) / fHOST)226 *227 * Then we get:228 * 1 / (1 + clk_div) <= (5000000 / fHOST)229 *230 * So:231 * (1 + clk_div) >= (fHOST / 5000000)232 *233 * And finally:234 * clk_div >= (fHOST / 5000000) - 1235 *236 * fHOST can be read from the flattened device tree as property237 * "clock-frequency" from the CPU238 */239 240 clk_div = (host_clock / (mdio_freq * 2)) - 1;241 /* If there is any remainder from the division of242 * fHOST / (mdio_freq * 2), then we need to add243 * 1 to the clock divisor or we will surely be244 * above the requested frequency245 */246 if (host_clock % (mdio_freq * 2))247 clk_div++;248 249 /* Check for overflow of mii_clk_div */250 if (clk_div & ~XAE_MDIO_MC_CLOCK_DIVIDE_MAX) {251 netdev_warn(lp->ndev, "MDIO clock divisor overflow\n");252 return -EOVERFLOW;253 }254 lp->mii_clk_div = (u8)clk_div;255 256 netdev_dbg(lp->ndev,257 "Setting MDIO clock divisor to %u/%u Hz host clock.\n",258 lp->mii_clk_div, host_clock);259 260 axienet_mdio_mdc_enable(lp);261 262 ret = axienet_mdio_wait_until_ready(lp);263 if (ret)264 axienet_mdio_mdc_disable(lp);265 266 return ret;267}268 269/**270 * axienet_mdio_setup - MDIO setup function271 * @lp: Pointer to axienet local data structure.272 *273 * Return: 0 on success, -ETIMEDOUT on a timeout, -EOVERFLOW on a clock274 * divisor overflow, -ENOMEM when mdiobus_alloc (to allocate275 * memory for mii bus structure) fails.276 *277 * Sets up the MDIO interface by initializing the MDIO clock.278 * Register the MDIO interface.279 **/280int axienet_mdio_setup(struct axienet_local *lp)281{282 struct device_node *mdio_node;283 struct mii_bus *bus;284 int ret;285 286 bus = mdiobus_alloc();287 if (!bus)288 return -ENOMEM;289 290 snprintf(bus->id, MII_BUS_ID_SIZE, "axienet-%.8llx",291 (unsigned long long)lp->regs_start);292 293 bus->priv = lp;294 bus->name = "Xilinx Axi Ethernet MDIO";295 bus->read = axienet_mdio_read;296 bus->write = axienet_mdio_write;297 bus->parent = lp->dev;298 lp->mii_bus = bus;299 300 mdio_node = of_get_child_by_name(lp->dev->of_node, "mdio");301 ret = axienet_mdio_enable(lp, mdio_node);302 if (ret < 0)303 goto unregister;304 ret = of_mdiobus_register(bus, mdio_node);305 if (ret)306 goto unregister_mdio_enabled;307 of_node_put(mdio_node);308 axienet_mdio_mdc_disable(lp);309 return 0;310 311unregister_mdio_enabled:312 axienet_mdio_mdc_disable(lp);313unregister:314 of_node_put(mdio_node);315 mdiobus_free(bus);316 lp->mii_bus = NULL;317 return ret;318}319 320/**321 * axienet_mdio_teardown - MDIO remove function322 * @lp: Pointer to axienet local data structure.323 *324 * Unregisters the MDIO and frees any associate memory for mii bus.325 */326void axienet_mdio_teardown(struct axienet_local *lp)327{328 mdiobus_unregister(lp->mii_bus);329 mdiobus_free(lp->mii_bus);330 lp->mii_bus = NULL;331}332