162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * MDIO bus driver for the Xilinx TEMAC device 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2009 Secret Lab Technologies, Ltd. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/io.h> 962306a36Sopenharmony_ci#include <linux/netdevice.h> 1062306a36Sopenharmony_ci#include <linux/mutex.h> 1162306a36Sopenharmony_ci#include <linux/phy.h> 1262306a36Sopenharmony_ci#include <linux/of.h> 1362306a36Sopenharmony_ci#include <linux/of_address.h> 1462306a36Sopenharmony_ci#include <linux/platform_device.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/of_mdio.h> 1762306a36Sopenharmony_ci#include <linux/platform_data/xilinx-ll-temac.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "ll_temac.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* --------------------------------------------------------------------- 2262306a36Sopenharmony_ci * MDIO Bus functions 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_cistatic int temac_mdio_read(struct mii_bus *bus, int phy_id, int reg) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci struct temac_local *lp = bus->priv; 2762306a36Sopenharmony_ci u32 rc; 2862306a36Sopenharmony_ci unsigned long flags; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci /* Write the PHY address to the MIIM Access Initiator register. 3162306a36Sopenharmony_ci * When the transfer completes, the PHY register value will appear 3262306a36Sopenharmony_ci * in the LSW0 register 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_ci spin_lock_irqsave(lp->indirect_lock, flags); 3562306a36Sopenharmony_ci temac_iow(lp, XTE_LSW0_OFFSET, (phy_id << 5) | reg); 3662306a36Sopenharmony_ci rc = temac_indirect_in32_locked(lp, XTE_MIIMAI_OFFSET); 3762306a36Sopenharmony_ci spin_unlock_irqrestore(lp->indirect_lock, flags); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci dev_dbg(lp->dev, "temac_mdio_read(phy_id=%i, reg=%x) == %x\n", 4062306a36Sopenharmony_ci phy_id, reg, rc); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci return rc; 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic int temac_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 val) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci struct temac_local *lp = bus->priv; 4862306a36Sopenharmony_ci unsigned long flags; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci dev_dbg(lp->dev, "temac_mdio_write(phy_id=%i, reg=%x, val=%x)\n", 5162306a36Sopenharmony_ci phy_id, reg, val); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci /* First write the desired value into the write data register 5462306a36Sopenharmony_ci * and then write the address into the access initiator register 5562306a36Sopenharmony_ci */ 5662306a36Sopenharmony_ci spin_lock_irqsave(lp->indirect_lock, flags); 5762306a36Sopenharmony_ci temac_indirect_out32_locked(lp, XTE_MGTDR_OFFSET, val); 5862306a36Sopenharmony_ci temac_indirect_out32_locked(lp, XTE_MIIMAI_OFFSET, (phy_id << 5) | reg); 5962306a36Sopenharmony_ci spin_unlock_irqrestore(lp->indirect_lock, flags); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci return 0; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ciint temac_mdio_setup(struct temac_local *lp, struct platform_device *pdev) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci struct ll_temac_platform_data *pdata = dev_get_platdata(&pdev->dev); 6762306a36Sopenharmony_ci struct device_node *np = dev_of_node(&pdev->dev); 6862306a36Sopenharmony_ci struct mii_bus *bus; 6962306a36Sopenharmony_ci u32 bus_hz; 7062306a36Sopenharmony_ci int clk_div; 7162306a36Sopenharmony_ci int rc; 7262306a36Sopenharmony_ci struct resource res; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci /* Get MDIO bus frequency (if specified) */ 7562306a36Sopenharmony_ci bus_hz = 0; 7662306a36Sopenharmony_ci if (np) 7762306a36Sopenharmony_ci of_property_read_u32(np, "clock-frequency", &bus_hz); 7862306a36Sopenharmony_ci else if (pdata) 7962306a36Sopenharmony_ci bus_hz = pdata->mdio_clk_freq; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci /* Calculate a reasonable divisor for the clock rate */ 8262306a36Sopenharmony_ci clk_div = 0x3f; /* worst-case default setting */ 8362306a36Sopenharmony_ci if (bus_hz != 0) { 8462306a36Sopenharmony_ci clk_div = bus_hz / (2500 * 1000 * 2) - 1; 8562306a36Sopenharmony_ci if (clk_div < 1) 8662306a36Sopenharmony_ci clk_div = 1; 8762306a36Sopenharmony_ci if (clk_div > 0x3f) 8862306a36Sopenharmony_ci clk_div = 0x3f; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* Enable the MDIO bus by asserting the enable bit and writing 9262306a36Sopenharmony_ci * in the clock config 9362306a36Sopenharmony_ci */ 9462306a36Sopenharmony_ci temac_indirect_out32(lp, XTE_MC_OFFSET, 1 << 6 | clk_div); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci bus = devm_mdiobus_alloc(&pdev->dev); 9762306a36Sopenharmony_ci if (!bus) 9862306a36Sopenharmony_ci return -ENOMEM; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci if (np) { 10162306a36Sopenharmony_ci of_address_to_resource(np, 0, &res); 10262306a36Sopenharmony_ci snprintf(bus->id, MII_BUS_ID_SIZE, "%.8llx", 10362306a36Sopenharmony_ci (unsigned long long)res.start); 10462306a36Sopenharmony_ci } else if (pdata) { 10562306a36Sopenharmony_ci snprintf(bus->id, MII_BUS_ID_SIZE, "%.8llx", 10662306a36Sopenharmony_ci pdata->mdio_bus_id); 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci bus->priv = lp; 11062306a36Sopenharmony_ci bus->name = "Xilinx TEMAC MDIO"; 11162306a36Sopenharmony_ci bus->read = temac_mdio_read; 11262306a36Sopenharmony_ci bus->write = temac_mdio_write; 11362306a36Sopenharmony_ci bus->parent = lp->dev; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci lp->mii_bus = bus; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci rc = of_mdiobus_register(bus, np); 11862306a36Sopenharmony_ci if (rc) 11962306a36Sopenharmony_ci return rc; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci dev_dbg(lp->dev, "MDIO bus registered; MC:%x\n", 12262306a36Sopenharmony_ci temac_indirect_in32(lp, XTE_MC_OFFSET)); 12362306a36Sopenharmony_ci return 0; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_civoid temac_mdio_teardown(struct temac_local *lp) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci mdiobus_unregister(lp->mii_bus); 12962306a36Sopenharmony_ci} 130