linux/drivers/net/ethernet/freescale/enetc/enetc_mdio.c
<<
>>
Prefs
   1// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
   2/* Copyright 2019 NXP */
   3
   4#include <linux/fsl/enetc_mdio.h>
   5#include <linux/mdio.h>
   6#include <linux/of_mdio.h>
   7#include <linux/iopoll.h>
   8#include <linux/of.h>
   9
  10#include "enetc_pf.h"
  11
  12#define ENETC_MDIO_CFG  0x0     /* MDIO configuration and status */
  13#define ENETC_MDIO_CTL  0x4     /* MDIO control */
  14#define ENETC_MDIO_DATA 0x8     /* MDIO data */
  15#define ENETC_MDIO_ADDR 0xc     /* MDIO address */
  16
  17#define MDIO_CFG_CLKDIV(x)      ((((x) >> 1) & 0xff) << 8)
  18#define MDIO_CFG_BSY            BIT(0)
  19#define MDIO_CFG_RD_ER          BIT(1)
  20#define MDIO_CFG_HOLD(x)        (((x) << 2) & GENMASK(4, 2))
  21#define MDIO_CFG_ENC45          BIT(6)
  22 /* external MDIO only - driven on neg MDC edge */
  23#define MDIO_CFG_NEG            BIT(23)
  24
  25#define ENETC_EMDIO_CFG \
  26        (MDIO_CFG_HOLD(2) | \
  27         MDIO_CFG_CLKDIV(258) | \
  28         MDIO_CFG_NEG)
  29
  30#define MDIO_CTL_DEV_ADDR(x)    ((x) & 0x1f)
  31#define MDIO_CTL_PORT_ADDR(x)   (((x) & 0x1f) << 5)
  32#define MDIO_CTL_READ           BIT(15)
  33
  34static inline u32 enetc_mdio_rd(struct enetc_mdio_priv *mdio_priv, int off)
  35{
  36        return enetc_port_rd_mdio(mdio_priv->hw, mdio_priv->mdio_base + off);
  37}
  38
  39static inline void enetc_mdio_wr(struct enetc_mdio_priv *mdio_priv, int off,
  40                                 u32 val)
  41{
  42        enetc_port_wr_mdio(mdio_priv->hw, mdio_priv->mdio_base + off, val);
  43}
  44
  45static bool enetc_mdio_is_busy(struct enetc_mdio_priv *mdio_priv)
  46{
  47        return enetc_mdio_rd(mdio_priv, ENETC_MDIO_CFG) & MDIO_CFG_BSY;
  48}
  49
  50static int enetc_mdio_wait_complete(struct enetc_mdio_priv *mdio_priv)
  51{
  52        bool is_busy;
  53
  54        return readx_poll_timeout(enetc_mdio_is_busy, mdio_priv,
  55                                  is_busy, !is_busy, 10, 10 * 1000);
  56}
  57
  58int enetc_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 value)
  59{
  60        struct enetc_mdio_priv *mdio_priv = bus->priv;
  61        u32 mdio_ctl, mdio_cfg;
  62        u16 dev_addr;
  63        int ret;
  64
  65        mdio_cfg = ENETC_EMDIO_CFG;
  66        if (regnum & MII_ADDR_C45) {
  67                dev_addr = (regnum >> 16) & 0x1f;
  68                mdio_cfg |= MDIO_CFG_ENC45;
  69        } else {
  70                /* clause 22 (ie 1G) */
  71                dev_addr = regnum & 0x1f;
  72                mdio_cfg &= ~MDIO_CFG_ENC45;
  73        }
  74
  75        enetc_mdio_wr(mdio_priv, ENETC_MDIO_CFG, mdio_cfg);
  76
  77        ret = enetc_mdio_wait_complete(mdio_priv);
  78        if (ret)
  79                return ret;
  80
  81        /* set port and dev addr */
  82        mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr);
  83        enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, mdio_ctl);
  84
  85        /* set the register address */
  86        if (regnum & MII_ADDR_C45) {
  87                enetc_mdio_wr(mdio_priv, ENETC_MDIO_ADDR, regnum & 0xffff);
  88
  89                ret = enetc_mdio_wait_complete(mdio_priv);
  90                if (ret)
  91                        return ret;
  92        }
  93
  94        /* write the value */
  95        enetc_mdio_wr(mdio_priv, ENETC_MDIO_DATA, value);
  96
  97        ret = enetc_mdio_wait_complete(mdio_priv);
  98        if (ret)
  99                return ret;
 100
 101        return 0;
 102}
 103EXPORT_SYMBOL_GPL(enetc_mdio_write);
 104
 105int enetc_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
 106{
 107        struct enetc_mdio_priv *mdio_priv = bus->priv;
 108        u32 mdio_ctl, mdio_cfg;
 109        u16 dev_addr, value;
 110        int ret;
 111
 112        mdio_cfg = ENETC_EMDIO_CFG;
 113        if (regnum & MII_ADDR_C45) {
 114                dev_addr = (regnum >> 16) & 0x1f;
 115                mdio_cfg |= MDIO_CFG_ENC45;
 116        } else {
 117                dev_addr = regnum & 0x1f;
 118                mdio_cfg &= ~MDIO_CFG_ENC45;
 119        }
 120
 121        enetc_mdio_wr(mdio_priv, ENETC_MDIO_CFG, mdio_cfg);
 122
 123        ret = enetc_mdio_wait_complete(mdio_priv);
 124        if (ret)
 125                return ret;
 126
 127        /* set port and device addr */
 128        mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr);
 129        enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, mdio_ctl);
 130
 131        /* set the register address */
 132        if (regnum & MII_ADDR_C45) {
 133                enetc_mdio_wr(mdio_priv, ENETC_MDIO_ADDR, regnum & 0xffff);
 134
 135                ret = enetc_mdio_wait_complete(mdio_priv);
 136                if (ret)
 137                        return ret;
 138        }
 139
 140        /* initiate the read */
 141        enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, mdio_ctl | MDIO_CTL_READ);
 142
 143        ret = enetc_mdio_wait_complete(mdio_priv);
 144        if (ret)
 145                return ret;
 146
 147        /* return all Fs if nothing was there */
 148        if (enetc_mdio_rd(mdio_priv, ENETC_MDIO_CFG) & MDIO_CFG_RD_ER) {
 149                dev_dbg(&bus->dev,
 150                        "Error while reading PHY%d reg at %d.%hhu\n",
 151                        phy_id, dev_addr, regnum);
 152                return 0xffff;
 153        }
 154
 155        value = enetc_mdio_rd(mdio_priv, ENETC_MDIO_DATA) & 0xffff;
 156
 157        return value;
 158}
 159EXPORT_SYMBOL_GPL(enetc_mdio_read);
 160
 161struct enetc_hw *enetc_hw_alloc(struct device *dev, void __iomem *port_regs)
 162{
 163        struct enetc_hw *hw;
 164
 165        hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
 166        if (!hw)
 167                return ERR_PTR(-ENOMEM);
 168
 169        hw->port = port_regs;
 170
 171        return hw;
 172}
 173EXPORT_SYMBOL_GPL(enetc_hw_alloc);
 174
 175/* Lock for MDIO access errata on LS1028A */
 176DEFINE_RWLOCK(enetc_mdio_lock);
 177EXPORT_SYMBOL_GPL(enetc_mdio_lock);
 178