237 lines · c
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_c22(struct mii_bus *bus, int phy_id, int regnum,59 u16 value)60{61 struct enetc_mdio_priv *mdio_priv = bus->priv;62 u32 mdio_ctl, mdio_cfg;63 u16 dev_addr;64 int ret;65 66 mdio_cfg = ENETC_EMDIO_CFG;67 dev_addr = regnum & 0x1f;68 mdio_cfg &= ~MDIO_CFG_ENC45;69 70 enetc_mdio_wr(mdio_priv, ENETC_MDIO_CFG, mdio_cfg);71 72 ret = enetc_mdio_wait_complete(mdio_priv);73 if (ret)74 return ret;75 76 /* set port and dev addr */77 mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr);78 enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, mdio_ctl);79 80 /* write the value */81 enetc_mdio_wr(mdio_priv, ENETC_MDIO_DATA, value);82 83 ret = enetc_mdio_wait_complete(mdio_priv);84 if (ret)85 return ret;86 87 return 0;88}89EXPORT_SYMBOL_GPL(enetc_mdio_write_c22);90 91int enetc_mdio_write_c45(struct mii_bus *bus, int phy_id, int dev_addr,92 int regnum, u16 value)93{94 struct enetc_mdio_priv *mdio_priv = bus->priv;95 u32 mdio_ctl, mdio_cfg;96 int ret;97 98 mdio_cfg = ENETC_EMDIO_CFG;99 mdio_cfg |= MDIO_CFG_ENC45;100 101 enetc_mdio_wr(mdio_priv, ENETC_MDIO_CFG, mdio_cfg);102 103 ret = enetc_mdio_wait_complete(mdio_priv);104 if (ret)105 return ret;106 107 /* set port and dev addr */108 mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr);109 enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, mdio_ctl);110 111 /* set the register address */112 enetc_mdio_wr(mdio_priv, ENETC_MDIO_ADDR, regnum & 0xffff);113 114 ret = enetc_mdio_wait_complete(mdio_priv);115 if (ret)116 return ret;117 118 /* write the value */119 enetc_mdio_wr(mdio_priv, ENETC_MDIO_DATA, value);120 121 ret = enetc_mdio_wait_complete(mdio_priv);122 if (ret)123 return ret;124 125 return 0;126}127EXPORT_SYMBOL_GPL(enetc_mdio_write_c45);128 129int enetc_mdio_read_c22(struct mii_bus *bus, int phy_id, int regnum)130{131 struct enetc_mdio_priv *mdio_priv = bus->priv;132 u32 mdio_ctl, mdio_cfg;133 u16 dev_addr, value;134 int ret;135 136 mdio_cfg = ENETC_EMDIO_CFG;137 dev_addr = regnum & 0x1f;138 mdio_cfg &= ~MDIO_CFG_ENC45;139 140 enetc_mdio_wr(mdio_priv, ENETC_MDIO_CFG, mdio_cfg);141 142 ret = enetc_mdio_wait_complete(mdio_priv);143 if (ret)144 return ret;145 146 /* set port and device addr */147 mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr);148 enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, mdio_ctl);149 150 /* initiate the read */151 enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, mdio_ctl | MDIO_CTL_READ);152 153 ret = enetc_mdio_wait_complete(mdio_priv);154 if (ret)155 return ret;156 157 /* return all Fs if nothing was there */158 if (enetc_mdio_rd(mdio_priv, ENETC_MDIO_CFG) & MDIO_CFG_RD_ER) {159 dev_dbg(&bus->dev,160 "Error while reading PHY%d reg at %d.%d\n",161 phy_id, dev_addr, regnum);162 return 0xffff;163 }164 165 value = enetc_mdio_rd(mdio_priv, ENETC_MDIO_DATA) & 0xffff;166 167 return value;168}169EXPORT_SYMBOL_GPL(enetc_mdio_read_c22);170 171int enetc_mdio_read_c45(struct mii_bus *bus, int phy_id, int dev_addr,172 int regnum)173{174 struct enetc_mdio_priv *mdio_priv = bus->priv;175 u32 mdio_ctl, mdio_cfg;176 u16 value;177 int ret;178 179 mdio_cfg = ENETC_EMDIO_CFG;180 mdio_cfg |= MDIO_CFG_ENC45;181 182 enetc_mdio_wr(mdio_priv, ENETC_MDIO_CFG, mdio_cfg);183 184 ret = enetc_mdio_wait_complete(mdio_priv);185 if (ret)186 return ret;187 188 /* set port and device addr */189 mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr);190 enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, mdio_ctl);191 192 /* set the register address */193 enetc_mdio_wr(mdio_priv, ENETC_MDIO_ADDR, regnum & 0xffff);194 195 ret = enetc_mdio_wait_complete(mdio_priv);196 if (ret)197 return ret;198 199 /* initiate the read */200 enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, mdio_ctl | MDIO_CTL_READ);201 202 ret = enetc_mdio_wait_complete(mdio_priv);203 if (ret)204 return ret;205 206 /* return all Fs if nothing was there */207 if (enetc_mdio_rd(mdio_priv, ENETC_MDIO_CFG) & MDIO_CFG_RD_ER) {208 dev_dbg(&bus->dev,209 "Error while reading PHY%d reg at %d.%d\n",210 phy_id, dev_addr, regnum);211 return 0xffff;212 }213 214 value = enetc_mdio_rd(mdio_priv, ENETC_MDIO_DATA) & 0xffff;215 216 return value;217}218EXPORT_SYMBOL_GPL(enetc_mdio_read_c45);219 220struct enetc_hw *enetc_hw_alloc(struct device *dev, void __iomem *port_regs)221{222 struct enetc_hw *hw;223 224 hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);225 if (!hw)226 return ERR_PTR(-ENOMEM);227 228 hw->port = port_regs;229 230 return hw;231}232EXPORT_SYMBOL_GPL(enetc_hw_alloc);233 234/* Lock for MDIO access errata on LS1028A */235DEFINE_RWLOCK(enetc_mdio_lock);236EXPORT_SYMBOL_GPL(enetc_mdio_lock);237