brintos

brintos / linux-shallow public Read only

0
0
Text · 8.6 KiB · 9ca2643 Raw
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