linux/drivers/net/ethernet/xilinx/ll_temac_mdio.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * MDIO bus driver for the Xilinx TEMAC device
   4 *
   5 * Copyright (c) 2009 Secret Lab Technologies, Ltd.
   6 */
   7
   8#include <linux/io.h>
   9#include <linux/netdevice.h>
  10#include <linux/mutex.h>
  11#include <linux/phy.h>
  12#include <linux/of.h>
  13#include <linux/of_device.h>
  14#include <linux/of_address.h>
  15#include <linux/slab.h>
  16#include <linux/of_mdio.h>
  17#include <linux/platform_data/xilinx-ll-temac.h>
  18
  19#include "ll_temac.h"
  20
  21/* ---------------------------------------------------------------------
  22 * MDIO Bus functions
  23 */
  24static int temac_mdio_read(struct mii_bus *bus, int phy_id, int reg)
  25{
  26        struct temac_local *lp = bus->priv;
  27        u32 rc;
  28        unsigned long flags;
  29
  30        /* Write the PHY address to the MIIM Access Initiator register.
  31         * When the transfer completes, the PHY register value will appear
  32         * in the LSW0 register */
  33        spin_lock_irqsave(lp->indirect_lock, flags);
  34        temac_iow(lp, XTE_LSW0_OFFSET, (phy_id << 5) | reg);
  35        rc = temac_indirect_in32_locked(lp, XTE_MIIMAI_OFFSET);
  36        spin_unlock_irqrestore(lp->indirect_lock, flags);
  37
  38        dev_dbg(lp->dev, "temac_mdio_read(phy_id=%i, reg=%x) == %x\n",
  39                phy_id, reg, rc);
  40
  41        return rc;
  42}
  43
  44static int temac_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 val)
  45{
  46        struct temac_local *lp = bus->priv;
  47        unsigned long flags;
  48
  49        dev_dbg(lp->dev, "temac_mdio_write(phy_id=%i, reg=%x, val=%x)\n",
  50                phy_id, reg, val);
  51
  52        /* First write the desired value into the write data register
  53         * and then write the address into the access initiator register
  54         */
  55        spin_lock_irqsave(lp->indirect_lock, flags);
  56        temac_indirect_out32_locked(lp, XTE_MGTDR_OFFSET, val);
  57        temac_indirect_out32_locked(lp, XTE_MIIMAI_OFFSET, (phy_id << 5) | reg);
  58        spin_unlock_irqrestore(lp->indirect_lock, flags);
  59
  60        return 0;
  61}
  62
  63int temac_mdio_setup(struct temac_local *lp, struct platform_device *pdev)
  64{
  65        struct ll_temac_platform_data *pdata = dev_get_platdata(&pdev->dev);
  66        struct device_node *np = dev_of_node(&pdev->dev);
  67        struct mii_bus *bus;
  68        u32 bus_hz;
  69        int clk_div;
  70        int rc;
  71        struct resource res;
  72
  73        /* Get MDIO bus frequency (if specified) */
  74        bus_hz = 0;
  75        if (np)
  76                of_property_read_u32(np, "clock-frequency", &bus_hz);
  77        else if (pdata)
  78                bus_hz = pdata->mdio_clk_freq;
  79
  80        /* Calculate a reasonable divisor for the clock rate */
  81        clk_div = 0x3f; /* worst-case default setting */
  82        if (bus_hz != 0) {
  83                clk_div = bus_hz / (2500 * 1000 * 2) - 1;
  84                if (clk_div < 1)
  85                        clk_div = 1;
  86                if (clk_div > 0x3f)
  87                        clk_div = 0x3f;
  88        }
  89
  90        /* Enable the MDIO bus by asserting the enable bit and writing
  91         * in the clock config */
  92        temac_indirect_out32(lp, XTE_MC_OFFSET, 1 << 6 | clk_div);
  93
  94        bus = devm_mdiobus_alloc(&pdev->dev);
  95        if (!bus)
  96                return -ENOMEM;
  97
  98        if (np) {
  99                of_address_to_resource(np, 0, &res);
 100                snprintf(bus->id, MII_BUS_ID_SIZE, "%.8llx",
 101                         (unsigned long long)res.start);
 102        } else if (pdata) {
 103                snprintf(bus->id, MII_BUS_ID_SIZE, "%.8llx",
 104                         pdata->mdio_bus_id);
 105        }
 106
 107        bus->priv = lp;
 108        bus->name = "Xilinx TEMAC MDIO";
 109        bus->read = temac_mdio_read;
 110        bus->write = temac_mdio_write;
 111        bus->parent = lp->dev;
 112
 113        lp->mii_bus = bus;
 114
 115        rc = of_mdiobus_register(bus, np);
 116        if (rc)
 117                return rc;
 118
 119        dev_dbg(lp->dev, "MDIO bus registered;  MC:%x\n",
 120                temac_indirect_in32(lp, XTE_MC_OFFSET));
 121        return 0;
 122}
 123
 124void temac_mdio_teardown(struct temac_local *lp)
 125{
 126        mdiobus_unregister(lp->mii_bus);
 127}
 128