1
2
3
4
5
6
7
8
9
10#include <linux/of_address.h>
11#include <linux/of_mdio.h>
12#include <linux/jiffies.h>
13
14#include "xilinx_axienet.h"
15
16#define MAX_MDIO_FREQ 2500000
17#define DEFAULT_CLOCK_DIVISOR XAE_MDIO_DIV_DFT
18
19
20int axienet_mdio_wait_until_ready(struct axienet_local *lp)
21{
22 unsigned long end = jiffies + 2;
23
24 while (!(axienet_ior(lp, XAE_MDIO_MCR_OFFSET) &
25 XAE_MDIO_MCR_READY_MASK)) {
26 if (time_before_eq(end, jiffies)) {
27 WARN_ON(1);
28 return -ETIMEDOUT;
29 }
30 udelay(1);
31 }
32 return 0;
33}
34
35
36
37
38
39
40
41
42
43
44
45
46
47static int axienet_mdio_read(struct mii_bus *bus, int phy_id, int reg)
48{
49 u32 rc;
50 int ret;
51 struct axienet_local *lp = bus->priv;
52
53 ret = axienet_mdio_wait_until_ready(lp);
54 if (ret < 0)
55 return ret;
56
57 axienet_iow(lp, XAE_MDIO_MCR_OFFSET,
58 (((phy_id << XAE_MDIO_MCR_PHYAD_SHIFT) &
59 XAE_MDIO_MCR_PHYAD_MASK) |
60 ((reg << XAE_MDIO_MCR_REGAD_SHIFT) &
61 XAE_MDIO_MCR_REGAD_MASK) |
62 XAE_MDIO_MCR_INITIATE_MASK |
63 XAE_MDIO_MCR_OP_READ_MASK));
64
65 ret = axienet_mdio_wait_until_ready(lp);
66 if (ret < 0)
67 return ret;
68
69 rc = axienet_ior(lp, XAE_MDIO_MRD_OFFSET) & 0x0000FFFF;
70
71 dev_dbg(lp->dev, "axienet_mdio_read(phy_id=%i, reg=%x) == %x\n",
72 phy_id, reg, rc);
73
74 return rc;
75}
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90static int axienet_mdio_write(struct mii_bus *bus, int phy_id, int reg,
91 u16 val)
92{
93 int ret;
94 struct axienet_local *lp = bus->priv;
95
96 dev_dbg(lp->dev, "axienet_mdio_write(phy_id=%i, reg=%x, val=%x)\n",
97 phy_id, reg, val);
98
99 ret = axienet_mdio_wait_until_ready(lp);
100 if (ret < 0)
101 return ret;
102
103 axienet_iow(lp, XAE_MDIO_MWD_OFFSET, (u32) val);
104 axienet_iow(lp, XAE_MDIO_MCR_OFFSET,
105 (((phy_id << XAE_MDIO_MCR_PHYAD_SHIFT) &
106 XAE_MDIO_MCR_PHYAD_MASK) |
107 ((reg << XAE_MDIO_MCR_REGAD_SHIFT) &
108 XAE_MDIO_MCR_REGAD_MASK) |
109 XAE_MDIO_MCR_INITIATE_MASK |
110 XAE_MDIO_MCR_OP_WRITE_MASK));
111
112 ret = axienet_mdio_wait_until_ready(lp);
113 if (ret < 0)
114 return ret;
115 return 0;
116}
117
118
119
120
121
122
123
124
125
126
127
128
129int axienet_mdio_setup(struct axienet_local *lp, struct device_node *np)
130{
131 int ret;
132 u32 clk_div, host_clock;
133
134 struct mii_bus *bus;
135 struct resource res;
136 struct device_node *np1;
137
138 struct device_node *npp = NULL;
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164 np1 = of_get_parent(lp->phy_node);
165 if (np1) {
166 npp = of_get_parent(np1);
167 of_node_put(np1);
168 }
169 if (!npp) {
170 dev_warn(lp->dev,
171 "Could not find ethernet controller device node.");
172 dev_warn(lp->dev, "Setting MDIO clock divisor to default %d\n",
173 DEFAULT_CLOCK_DIVISOR);
174 clk_div = DEFAULT_CLOCK_DIVISOR;
175 } else {
176 if (of_property_read_u32(npp, "clock-frequency", &host_clock)) {
177 netdev_warn(lp->ndev,
178 "clock-frequency property not found.\n");
179 netdev_warn(lp->ndev,
180 "Setting MDIO clock divisor to default %d\n",
181 DEFAULT_CLOCK_DIVISOR);
182 clk_div = DEFAULT_CLOCK_DIVISOR;
183 } else {
184 clk_div = (host_clock / (MAX_MDIO_FREQ * 2)) - 1;
185
186
187
188
189
190
191 if (host_clock % (MAX_MDIO_FREQ * 2))
192 clk_div++;
193 dev_dbg(lp->dev,
194 "Setting MDIO clock divisor to %u "
195 "based on %u Hz host clock.\n",
196 clk_div, host_clock);
197 }
198 of_node_put(npp);
199 }
200
201 axienet_iow(lp, XAE_MDIO_MC_OFFSET,
202 (((u32)clk_div) | XAE_MDIO_MC_MDIOEN_MASK));
203
204 ret = axienet_mdio_wait_until_ready(lp);
205 if (ret < 0)
206 return ret;
207
208 bus = mdiobus_alloc();
209 if (!bus)
210 return -ENOMEM;
211
212 of_address_to_resource(npp, 0, &res);
213 snprintf(bus->id, MII_BUS_ID_SIZE, "%.8llx",
214 (unsigned long long) res.start);
215
216 bus->priv = lp;
217 bus->name = "Xilinx Axi Ethernet MDIO";
218 bus->read = axienet_mdio_read;
219 bus->write = axienet_mdio_write;
220 bus->parent = lp->dev;
221 lp->mii_bus = bus;
222
223 ret = of_mdiobus_register(bus, np1);
224 if (ret) {
225 mdiobus_free(bus);
226 return ret;
227 }
228 return 0;
229}
230
231
232
233
234
235
236
237void axienet_mdio_teardown(struct axienet_local *lp)
238{
239 mdiobus_unregister(lp->mii_bus);
240 mdiobus_free(lp->mii_bus);
241 lp->mii_bus = NULL;
242}
243