brintos

brintos / linux-shallow public Read only

0
0
Text · 5.7 KiB · 998aaa3 Raw
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