xref: /openbmc/linux/drivers/net/ethernet/freescale/enetc/enetc_mdio.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
1ebfcb23dSClaudiu Manoil // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
2ebfcb23dSClaudiu Manoil /* Copyright 2019 NXP */
3ebfcb23dSClaudiu Manoil 
46517798dSClaudiu Manoil #include <linux/fsl/enetc_mdio.h>
5ebfcb23dSClaudiu Manoil #include <linux/mdio.h>
6ebfcb23dSClaudiu Manoil #include <linux/of_mdio.h>
7ebfcb23dSClaudiu Manoil #include <linux/iopoll.h>
8ebfcb23dSClaudiu Manoil #include <linux/of.h>
9ebfcb23dSClaudiu Manoil 
106517798dSClaudiu Manoil #include "enetc_pf.h"
11ebfcb23dSClaudiu Manoil 
122152e7a2SClaudiu Manoil #define	ENETC_MDIO_CFG	0x0	/* MDIO configuration and status */
132152e7a2SClaudiu Manoil #define	ENETC_MDIO_CTL	0x4	/* MDIO control */
142152e7a2SClaudiu Manoil #define	ENETC_MDIO_DATA	0x8	/* MDIO data */
152152e7a2SClaudiu Manoil #define	ENETC_MDIO_ADDR	0xc	/* MDIO address */
162152e7a2SClaudiu Manoil 
17ebfcb23dSClaudiu Manoil #define MDIO_CFG_CLKDIV(x)	((((x) >> 1) & 0xff) << 8)
18ebfcb23dSClaudiu Manoil #define MDIO_CFG_BSY		BIT(0)
19ebfcb23dSClaudiu Manoil #define MDIO_CFG_RD_ER		BIT(1)
20d79d3032SVladimir Oltean #define MDIO_CFG_HOLD(x)	(((x) << 2) & GENMASK(4, 2))
21ebfcb23dSClaudiu Manoil #define MDIO_CFG_ENC45		BIT(6)
22ebfcb23dSClaudiu Manoil  /* external MDIO only - driven on neg MDC edge */
23ebfcb23dSClaudiu Manoil #define MDIO_CFG_NEG		BIT(23)
24ebfcb23dSClaudiu Manoil 
25d79d3032SVladimir Oltean #define ENETC_EMDIO_CFG \
26d79d3032SVladimir Oltean 	(MDIO_CFG_HOLD(2) | \
27d79d3032SVladimir Oltean 	 MDIO_CFG_CLKDIV(258) | \
28d79d3032SVladimir Oltean 	 MDIO_CFG_NEG)
29d79d3032SVladimir Oltean 
30ebfcb23dSClaudiu Manoil #define MDIO_CTL_DEV_ADDR(x)	((x) & 0x1f)
31ebfcb23dSClaudiu Manoil #define MDIO_CTL_PORT_ADDR(x)	(((x) & 0x1f) << 5)
32ebfcb23dSClaudiu Manoil #define MDIO_CTL_READ		BIT(15)
33ebfcb23dSClaudiu Manoil 
enetc_mdio_rd(struct enetc_mdio_priv * mdio_priv,int off)3476fa3ce9SMichael Walle static inline u32 enetc_mdio_rd(struct enetc_mdio_priv *mdio_priv, int off)
3576fa3ce9SMichael Walle {
3676fa3ce9SMichael Walle 	return enetc_port_rd_mdio(mdio_priv->hw, mdio_priv->mdio_base + off);
3776fa3ce9SMichael Walle }
3876fa3ce9SMichael Walle 
enetc_mdio_wr(struct enetc_mdio_priv * mdio_priv,int off,u32 val)3976fa3ce9SMichael Walle static inline void enetc_mdio_wr(struct enetc_mdio_priv *mdio_priv, int off,
4076fa3ce9SMichael Walle 				 u32 val)
4176fa3ce9SMichael Walle {
4276fa3ce9SMichael Walle 	enetc_port_wr_mdio(mdio_priv->hw, mdio_priv->mdio_base + off, val);
4376fa3ce9SMichael Walle }
4476fa3ce9SMichael Walle 
enetc_mdio_is_busy(struct enetc_mdio_priv * mdio_priv)453c7df82aSMichael Walle static bool enetc_mdio_is_busy(struct enetc_mdio_priv *mdio_priv)
463c7df82aSMichael Walle {
473c7df82aSMichael Walle 	return enetc_mdio_rd(mdio_priv, ENETC_MDIO_CFG) & MDIO_CFG_BSY;
483c7df82aSMichael Walle }
493c7df82aSMichael Walle 
enetc_mdio_wait_complete(struct enetc_mdio_priv * mdio_priv)506517798dSClaudiu Manoil static int enetc_mdio_wait_complete(struct enetc_mdio_priv *mdio_priv)
51ebfcb23dSClaudiu Manoil {
523c7df82aSMichael Walle 	bool is_busy;
53ebfcb23dSClaudiu Manoil 
543c7df82aSMichael Walle 	return readx_poll_timeout(enetc_mdio_is_busy, mdio_priv,
553c7df82aSMichael Walle 				  is_busy, !is_busy, 10, 10 * 1000);
56ebfcb23dSClaudiu Manoil }
57ebfcb23dSClaudiu Manoil 
enetc_mdio_write_c22(struct mii_bus * bus,int phy_id,int regnum,u16 value)58*80e87442SAndrew Lunn int enetc_mdio_write_c22(struct mii_bus *bus, int phy_id, int regnum,
59*80e87442SAndrew Lunn 			 u16 value)
60ebfcb23dSClaudiu Manoil {
612152e7a2SClaudiu Manoil 	struct enetc_mdio_priv *mdio_priv = bus->priv;
62ebfcb23dSClaudiu Manoil 	u32 mdio_ctl, mdio_cfg;
63ebfcb23dSClaudiu Manoil 	u16 dev_addr;
64ebfcb23dSClaudiu Manoil 	int ret;
65ebfcb23dSClaudiu Manoil 
66d79d3032SVladimir Oltean 	mdio_cfg = ENETC_EMDIO_CFG;
67ebfcb23dSClaudiu Manoil 	dev_addr = regnum & 0x1f;
68ebfcb23dSClaudiu Manoil 	mdio_cfg &= ~MDIO_CFG_ENC45;
69*80e87442SAndrew Lunn 
70*80e87442SAndrew Lunn 	enetc_mdio_wr(mdio_priv, ENETC_MDIO_CFG, mdio_cfg);
71*80e87442SAndrew Lunn 
72*80e87442SAndrew Lunn 	ret = enetc_mdio_wait_complete(mdio_priv);
73*80e87442SAndrew Lunn 	if (ret)
74*80e87442SAndrew Lunn 		return ret;
75*80e87442SAndrew Lunn 
76*80e87442SAndrew Lunn 	/* set port and dev addr */
77*80e87442SAndrew Lunn 	mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr);
78*80e87442SAndrew Lunn 	enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, mdio_ctl);
79*80e87442SAndrew Lunn 
80*80e87442SAndrew Lunn 	/* write the value */
81*80e87442SAndrew Lunn 	enetc_mdio_wr(mdio_priv, ENETC_MDIO_DATA, value);
82*80e87442SAndrew Lunn 
83*80e87442SAndrew Lunn 	ret = enetc_mdio_wait_complete(mdio_priv);
84*80e87442SAndrew Lunn 	if (ret)
85*80e87442SAndrew Lunn 		return ret;
86*80e87442SAndrew Lunn 
87*80e87442SAndrew Lunn 	return 0;
88ebfcb23dSClaudiu Manoil }
89*80e87442SAndrew Lunn EXPORT_SYMBOL_GPL(enetc_mdio_write_c22);
90*80e87442SAndrew Lunn 
enetc_mdio_write_c45(struct mii_bus * bus,int phy_id,int dev_addr,int regnum,u16 value)91*80e87442SAndrew Lunn int enetc_mdio_write_c45(struct mii_bus *bus, int phy_id, int dev_addr,
92*80e87442SAndrew Lunn 			 int regnum, u16 value)
93*80e87442SAndrew Lunn {
94*80e87442SAndrew Lunn 	struct enetc_mdio_priv *mdio_priv = bus->priv;
95*80e87442SAndrew Lunn 	u32 mdio_ctl, mdio_cfg;
96*80e87442SAndrew Lunn 	int ret;
97*80e87442SAndrew Lunn 
98*80e87442SAndrew Lunn 	mdio_cfg = ENETC_EMDIO_CFG;
99*80e87442SAndrew Lunn 	mdio_cfg |= MDIO_CFG_ENC45;
100ebfcb23dSClaudiu Manoil 
10138d26b24SMichael Walle 	enetc_mdio_wr(mdio_priv, ENETC_MDIO_CFG, mdio_cfg);
102ebfcb23dSClaudiu Manoil 
1036517798dSClaudiu Manoil 	ret = enetc_mdio_wait_complete(mdio_priv);
104ebfcb23dSClaudiu Manoil 	if (ret)
105ebfcb23dSClaudiu Manoil 		return ret;
106ebfcb23dSClaudiu Manoil 
107ebfcb23dSClaudiu Manoil 	/* set port and dev addr */
108ebfcb23dSClaudiu Manoil 	mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr);
10938d26b24SMichael Walle 	enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, mdio_ctl);
110ebfcb23dSClaudiu Manoil 
111ebfcb23dSClaudiu Manoil 	/* set the register address */
11238d26b24SMichael Walle 	enetc_mdio_wr(mdio_priv, ENETC_MDIO_ADDR, regnum & 0xffff);
113ebfcb23dSClaudiu Manoil 
1146517798dSClaudiu Manoil 	ret = enetc_mdio_wait_complete(mdio_priv);
115ebfcb23dSClaudiu Manoil 	if (ret)
116ebfcb23dSClaudiu Manoil 		return ret;
117ebfcb23dSClaudiu Manoil 
118ebfcb23dSClaudiu Manoil 	/* write the value */
119652b5dbaSMichael Walle 	enetc_mdio_wr(mdio_priv, ENETC_MDIO_DATA, value);
120ebfcb23dSClaudiu Manoil 
1216517798dSClaudiu Manoil 	ret = enetc_mdio_wait_complete(mdio_priv);
122ebfcb23dSClaudiu Manoil 	if (ret)
123ebfcb23dSClaudiu Manoil 		return ret;
124ebfcb23dSClaudiu Manoil 
125ebfcb23dSClaudiu Manoil 	return 0;
126ebfcb23dSClaudiu Manoil }
127*80e87442SAndrew Lunn EXPORT_SYMBOL_GPL(enetc_mdio_write_c45);
128ebfcb23dSClaudiu Manoil 
enetc_mdio_read_c22(struct mii_bus * bus,int phy_id,int regnum)129*80e87442SAndrew Lunn int enetc_mdio_read_c22(struct mii_bus *bus, int phy_id, int regnum)
130ebfcb23dSClaudiu Manoil {
1312152e7a2SClaudiu Manoil 	struct enetc_mdio_priv *mdio_priv = bus->priv;
132ebfcb23dSClaudiu Manoil 	u32 mdio_ctl, mdio_cfg;
133ebfcb23dSClaudiu Manoil 	u16 dev_addr, value;
134ebfcb23dSClaudiu Manoil 	int ret;
135ebfcb23dSClaudiu Manoil 
136d79d3032SVladimir Oltean 	mdio_cfg = ENETC_EMDIO_CFG;
137ebfcb23dSClaudiu Manoil 	dev_addr = regnum & 0x1f;
138ebfcb23dSClaudiu Manoil 	mdio_cfg &= ~MDIO_CFG_ENC45;
139ebfcb23dSClaudiu Manoil 
14038d26b24SMichael Walle 	enetc_mdio_wr(mdio_priv, ENETC_MDIO_CFG, mdio_cfg);
141ebfcb23dSClaudiu Manoil 
1426517798dSClaudiu Manoil 	ret = enetc_mdio_wait_complete(mdio_priv);
143ebfcb23dSClaudiu Manoil 	if (ret)
144ebfcb23dSClaudiu Manoil 		return ret;
145ebfcb23dSClaudiu Manoil 
146ebfcb23dSClaudiu Manoil 	/* set port and device addr */
147ebfcb23dSClaudiu Manoil 	mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr);
14838d26b24SMichael Walle 	enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, mdio_ctl);
149ebfcb23dSClaudiu Manoil 
150ebfcb23dSClaudiu Manoil 	/* initiate the read */
15138d26b24SMichael Walle 	enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, mdio_ctl | MDIO_CTL_READ);
152ebfcb23dSClaudiu Manoil 
1536517798dSClaudiu Manoil 	ret = enetc_mdio_wait_complete(mdio_priv);
154ebfcb23dSClaudiu Manoil 	if (ret)
155ebfcb23dSClaudiu Manoil 		return ret;
156ebfcb23dSClaudiu Manoil 
157ebfcb23dSClaudiu Manoil 	/* return all Fs if nothing was there */
15838d26b24SMichael Walle 	if (enetc_mdio_rd(mdio_priv, ENETC_MDIO_CFG) & MDIO_CFG_RD_ER) {
159ebfcb23dSClaudiu Manoil 		dev_dbg(&bus->dev,
160df4d35e1SBill Wendling 			"Error while reading PHY%d reg at %d.%d\n",
161ebfcb23dSClaudiu Manoil 			phy_id, dev_addr, regnum);
162ebfcb23dSClaudiu Manoil 		return 0xffff;
163ebfcb23dSClaudiu Manoil 	}
164ebfcb23dSClaudiu Manoil 
16538d26b24SMichael Walle 	value = enetc_mdio_rd(mdio_priv, ENETC_MDIO_DATA) & 0xffff;
166ebfcb23dSClaudiu Manoil 
167ebfcb23dSClaudiu Manoil 	return value;
168ebfcb23dSClaudiu Manoil }
169*80e87442SAndrew Lunn EXPORT_SYMBOL_GPL(enetc_mdio_read_c22);
170*80e87442SAndrew Lunn 
enetc_mdio_read_c45(struct mii_bus * bus,int phy_id,int dev_addr,int regnum)171*80e87442SAndrew Lunn int enetc_mdio_read_c45(struct mii_bus *bus, int phy_id, int dev_addr,
172*80e87442SAndrew Lunn 			int regnum)
173*80e87442SAndrew Lunn {
174*80e87442SAndrew Lunn 	struct enetc_mdio_priv *mdio_priv = bus->priv;
175*80e87442SAndrew Lunn 	u32 mdio_ctl, mdio_cfg;
176*80e87442SAndrew Lunn 	u16 value;
177*80e87442SAndrew Lunn 	int ret;
178*80e87442SAndrew Lunn 
179*80e87442SAndrew Lunn 	mdio_cfg = ENETC_EMDIO_CFG;
180*80e87442SAndrew Lunn 	mdio_cfg |= MDIO_CFG_ENC45;
181*80e87442SAndrew Lunn 
182*80e87442SAndrew Lunn 	enetc_mdio_wr(mdio_priv, ENETC_MDIO_CFG, mdio_cfg);
183*80e87442SAndrew Lunn 
184*80e87442SAndrew Lunn 	ret = enetc_mdio_wait_complete(mdio_priv);
185*80e87442SAndrew Lunn 	if (ret)
186*80e87442SAndrew Lunn 		return ret;
187*80e87442SAndrew Lunn 
188*80e87442SAndrew Lunn 	/* set port and device addr */
189*80e87442SAndrew Lunn 	mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr);
190*80e87442SAndrew Lunn 	enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, mdio_ctl);
191*80e87442SAndrew Lunn 
192*80e87442SAndrew Lunn 	/* set the register address */
193*80e87442SAndrew Lunn 	enetc_mdio_wr(mdio_priv, ENETC_MDIO_ADDR, regnum & 0xffff);
194*80e87442SAndrew Lunn 
195*80e87442SAndrew Lunn 	ret = enetc_mdio_wait_complete(mdio_priv);
196*80e87442SAndrew Lunn 	if (ret)
197*80e87442SAndrew Lunn 		return ret;
198*80e87442SAndrew Lunn 
199*80e87442SAndrew Lunn 	/* initiate the read */
200*80e87442SAndrew Lunn 	enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, mdio_ctl | MDIO_CTL_READ);
201*80e87442SAndrew Lunn 
202*80e87442SAndrew Lunn 	ret = enetc_mdio_wait_complete(mdio_priv);
203*80e87442SAndrew Lunn 	if (ret)
204*80e87442SAndrew Lunn 		return ret;
205*80e87442SAndrew Lunn 
206*80e87442SAndrew Lunn 	/* return all Fs if nothing was there */
207*80e87442SAndrew Lunn 	if (enetc_mdio_rd(mdio_priv, ENETC_MDIO_CFG) & MDIO_CFG_RD_ER) {
208*80e87442SAndrew Lunn 		dev_dbg(&bus->dev,
209*80e87442SAndrew Lunn 			"Error while reading PHY%d reg at %d.%d\n",
210*80e87442SAndrew Lunn 			phy_id, dev_addr, regnum);
211*80e87442SAndrew Lunn 		return 0xffff;
212*80e87442SAndrew Lunn 	}
213*80e87442SAndrew Lunn 
214*80e87442SAndrew Lunn 	value = enetc_mdio_rd(mdio_priv, ENETC_MDIO_DATA) & 0xffff;
215*80e87442SAndrew Lunn 
216*80e87442SAndrew Lunn 	return value;
217*80e87442SAndrew Lunn }
218*80e87442SAndrew Lunn EXPORT_SYMBOL_GPL(enetc_mdio_read_c45);
219ebfcb23dSClaudiu Manoil 
enetc_hw_alloc(struct device * dev,void __iomem * port_regs)2206517798dSClaudiu Manoil struct enetc_hw *enetc_hw_alloc(struct device *dev, void __iomem *port_regs)
221ebfcb23dSClaudiu Manoil {
2226517798dSClaudiu Manoil 	struct enetc_hw *hw;
223ebfcb23dSClaudiu Manoil 
2246517798dSClaudiu Manoil 	hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
2256517798dSClaudiu Manoil 	if (!hw)
2266517798dSClaudiu Manoil 		return ERR_PTR(-ENOMEM);
227ebfcb23dSClaudiu Manoil 
2286517798dSClaudiu Manoil 	hw->port = port_regs;
229ebfcb23dSClaudiu Manoil 
2306517798dSClaudiu Manoil 	return hw;
231ebfcb23dSClaudiu Manoil }
2326517798dSClaudiu Manoil EXPORT_SYMBOL_GPL(enetc_hw_alloc);
233fd5736bfSAlex Marginean 
234fd5736bfSAlex Marginean /* Lock for MDIO access errata on LS1028A */
235fd5736bfSAlex Marginean DEFINE_RWLOCK(enetc_mdio_lock);
236fd5736bfSAlex Marginean EXPORT_SYMBOL_GPL(enetc_mdio_lock);
237