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