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
  17static inline u32 _enetc_mdio_rd(struct enetc_mdio_priv *mdio_priv, int off)
  18{
  19        return enetc_port_rd(mdio_priv->hw, mdio_priv->mdio_base + off);
  20}
  21
  22static inline void _enetc_mdio_wr(struct enetc_mdio_priv *mdio_priv, int off,
  23                                  u32 val)
  24{
  25        enetc_port_wr(mdio_priv->hw, mdio_priv->mdio_base + off, val);
  26}
  27
  28#define enetc_mdio_rd(mdio_priv, off) \
  29        _enetc_mdio_rd(mdio_priv, ENETC_##off)
  30#define enetc_mdio_wr(mdio_priv, off, val) \
  31        _enetc_mdio_wr(mdio_priv, ENETC_##off, val)
  32#define enetc_mdio_rd_reg(off)  enetc_mdio_rd(mdio_priv, off)
  33
  34#define MDIO_CFG_CLKDIV(x)      ((((x) >> 1) & 0xff) << 8)
  35#define MDIO_CFG_BSY            BIT(0)
  36#define MDIO_CFG_RD_ER          BIT(1)
  37#define MDIO_CFG_HOLD(x)        (((x) << 2) & GENMASK(4, 2))
  38#define MDIO_CFG_ENC45          BIT(6)
  39 /* external MDIO only - driven on neg MDC edge */
  40#define MDIO_CFG_NEG            BIT(23)
  41
  42#define ENETC_EMDIO_CFG \
  43        (MDIO_CFG_HOLD(2) | \
  44         MDIO_CFG_CLKDIV(258) | \
  45         MDIO_CFG_NEG)
  46
  47#define MDIO_CTL_DEV_ADDR(x)    ((x) & 0x1f)
  48#define MDIO_CTL_PORT_ADDR(x)   (((x) & 0x1f) << 5)
  49#define MDIO_CTL_READ           BIT(15)
  50#define MDIO_DATA(x)            ((x) & 0xffff)
  51
  52#define TIMEOUT 1000
  53static int enetc_mdio_wait_complete(struct enetc_mdio_priv *mdio_priv)
  54{
  55        u32 val;
  56
  57        return readx_poll_timeout(enetc_mdio_rd_reg, MDIO_CFG, val,
  58                                  !(val & MDIO_CFG_BSY), 10, 10 * TIMEOUT);
  59}
  60
  61int enetc_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 value)
  62{
  63        struct enetc_mdio_priv *mdio_priv = bus->priv;
  64        u32 mdio_ctl, mdio_cfg;
  65        u16 dev_addr;
  66        int ret;
  67
  68        mdio_cfg = ENETC_EMDIO_CFG;
  69        if (regnum & MII_ADDR_C45) {
  70                dev_addr = (regnum >> 16) & 0x1f;
  71                mdio_cfg |= MDIO_CFG_ENC45;
  72        } else {
  73                /* clause 22 (ie 1G) */
  74                dev_addr = regnum & 0x1f;
  75                mdio_cfg &= ~MDIO_CFG_ENC45;
  76        }
  77
  78        enetc_mdio_wr(mdio_priv, MDIO_CFG, mdio_cfg);
  79
  80        ret = enetc_mdio_wait_complete(mdio_priv);
  81        if (ret)
  82                return ret;
  83
  84        /* set port and dev addr */
  85        mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr);
  86        enetc_mdio_wr(mdio_priv, MDIO_CTL, mdio_ctl);
  87
  88        /* set the register address */
  89        if (regnum & MII_ADDR_C45) {
  90                enetc_mdio_wr(mdio_priv, MDIO_ADDR, regnum & 0xffff);
  91
  92                ret = enetc_mdio_wait_complete(mdio_priv);
  93                if (ret)
  94                        return ret;
  95        }
  96
  97        /* write the value */
  98        enetc_mdio_wr(mdio_priv, MDIO_DATA, MDIO_DATA(value));
  99
 100        ret = enetc_mdio_wait_complete(mdio_priv);
 101        if (ret)
 102                return ret;
 103
 104        return 0;
 105}
 106EXPORT_SYMBOL_GPL(enetc_mdio_write);
 107
 108int enetc_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
 109{
 110        struct enetc_mdio_priv *mdio_priv = bus->priv;
 111        u32 mdio_ctl, mdio_cfg;
 112        u16 dev_addr, value;
 113        int ret;
 114
 115        mdio_cfg = ENETC_EMDIO_CFG;
 116        if (regnum & MII_ADDR_C45) {
 117                dev_addr = (regnum >> 16) & 0x1f;
 118                mdio_cfg |= MDIO_CFG_ENC45;
 119        } else {
 120                dev_addr = regnum & 0x1f;
 121                mdio_cfg &= ~MDIO_CFG_ENC45;
 122        }
 123
 124        enetc_mdio_wr(mdio_priv, MDIO_CFG, mdio_cfg);
 125
 126        ret = enetc_mdio_wait_complete(mdio_priv);
 127        if (ret)
 128                return ret;
 129
 130        /* set port and device addr */
 131        mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr);
 132        enetc_mdio_wr(mdio_priv, MDIO_CTL, mdio_ctl);
 133
 134        /* set the register address */
 135        if (regnum & MII_ADDR_C45) {
 136                enetc_mdio_wr(mdio_priv, MDIO_ADDR, regnum & 0xffff);
 137
 138                ret = enetc_mdio_wait_complete(mdio_priv);
 139                if (ret)
 140                        return ret;
 141        }
 142
 143        /* initiate the read */
 144        enetc_mdio_wr(mdio_priv, MDIO_CTL, mdio_ctl | MDIO_CTL_READ);
 145
 146        ret = enetc_mdio_wait_complete(mdio_priv);
 147        if (ret)
 148                return ret;
 149
 150        /* return all Fs if nothing was there */
 151        if (enetc_mdio_rd(mdio_priv, MDIO_CFG) & MDIO_CFG_RD_ER) {
 152                dev_dbg(&bus->dev,
 153                        "Error while reading PHY%d reg at %d.%hhu\n",
 154                        phy_id, dev_addr, regnum);
 155                return 0xffff;
 156        }
 157
 158        value = enetc_mdio_rd(mdio_priv, MDIO_DATA) & 0xffff;
 159
 160        return value;
 161}
 162EXPORT_SYMBOL_GPL(enetc_mdio_read);
 163
 164struct enetc_hw *enetc_hw_alloc(struct device *dev, void __iomem *port_regs)
 165{
 166        struct enetc_hw *hw;
 167
 168        hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
 169        if (!hw)
 170                return ERR_PTR(-ENOMEM);
 171
 172        hw->port = port_regs;
 173
 174        return hw;
 175}
 176EXPORT_SYMBOL_GPL(enetc_hw_alloc);
 177