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, val: 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, val: mdio_ctl);
79
80 /* write the value */
81 enetc_mdio_wr(mdio_priv, ENETC_MDIO_DATA, val: 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, val: 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, val: mdio_ctl);
110
111 /* set the register address */
112 enetc_mdio_wr(mdio_priv, ENETC_MDIO_ADDR, val: 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, val: 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, val: 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, val: mdio_ctl);
149
150 /* initiate the read */
151 enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, val: 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, val: 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, val: mdio_ctl);
191
192 /* set the register address */
193 enetc_mdio_wr(mdio_priv, ENETC_MDIO_ADDR, val: 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, val: 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, size: sizeof(*hw), GFP_KERNEL);
225 if (!hw)
226 return ERR_PTR(error: -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

source code of linux/drivers/net/ethernet/freescale/enetc/enetc_mdio.c