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