1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * MDIO bus driver for the Xilinx Axi Ethernet device |
4 | * |
5 | * Copyright (c) 2009 Secret Lab Technologies, Ltd. |
6 | * Copyright (c) 2010 - 2011 Michal Simek <monstr@monstr.eu> |
7 | * Copyright (c) 2010 - 2011 PetaLogix |
8 | * Copyright (c) 2019 SED Systems, a division of Calian Ltd. |
9 | * Copyright (c) 2010 - 2012 Xilinx, Inc. All rights reserved. |
10 | */ |
11 | |
12 | #include <linux/clk.h> |
13 | #include <linux/of_address.h> |
14 | #include <linux/of_mdio.h> |
15 | #include <linux/jiffies.h> |
16 | #include <linux/iopoll.h> |
17 | |
18 | #include "xilinx_axienet.h" |
19 | |
20 | #define DEFAULT_MDIO_FREQ 2500000 /* 2.5 MHz */ |
21 | #define DEFAULT_HOST_CLOCK 150000000 /* 150 MHz */ |
22 | |
23 | /* Wait till MDIO interface is ready to accept a new transaction.*/ |
24 | static int axienet_mdio_wait_until_ready(struct axienet_local *lp) |
25 | { |
26 | u32 val; |
27 | |
28 | return readx_poll_timeout(axinet_ior_read_mcr, lp, |
29 | val, val & XAE_MDIO_MCR_READY_MASK, |
30 | 1, 20000); |
31 | } |
32 | |
33 | /* Enable the MDIO MDC. Called prior to a read/write operation */ |
34 | static void axienet_mdio_mdc_enable(struct axienet_local *lp) |
35 | { |
36 | axienet_iow(lp, XAE_MDIO_MC_OFFSET, |
37 | value: ((u32)lp->mii_clk_div | XAE_MDIO_MC_MDIOEN_MASK)); |
38 | } |
39 | |
40 | /* Disable the MDIO MDC. Called after a read/write operation*/ |
41 | static void axienet_mdio_mdc_disable(struct axienet_local *lp) |
42 | { |
43 | u32 mc_reg; |
44 | |
45 | mc_reg = axienet_ior(lp, XAE_MDIO_MC_OFFSET); |
46 | axienet_iow(lp, XAE_MDIO_MC_OFFSET, |
47 | value: (mc_reg & ~XAE_MDIO_MC_MDIOEN_MASK)); |
48 | } |
49 | |
50 | /** |
51 | * axienet_mdio_read - MDIO interface read function |
52 | * @bus: Pointer to mii bus structure |
53 | * @phy_id: Address of the PHY device |
54 | * @reg: PHY register to read |
55 | * |
56 | * Return: The register contents on success, -ETIMEDOUT on a timeout |
57 | * |
58 | * Reads the contents of the requested register from the requested PHY |
59 | * address by first writing the details into MCR register. After a while |
60 | * the register MRD is read to obtain the PHY register content. |
61 | */ |
62 | static int axienet_mdio_read(struct mii_bus *bus, int phy_id, int reg) |
63 | { |
64 | u32 rc; |
65 | int ret; |
66 | struct axienet_local *lp = bus->priv; |
67 | |
68 | axienet_mdio_mdc_enable(lp); |
69 | |
70 | ret = axienet_mdio_wait_until_ready(lp); |
71 | if (ret < 0) { |
72 | axienet_mdio_mdc_disable(lp); |
73 | return ret; |
74 | } |
75 | |
76 | axienet_iow(lp, XAE_MDIO_MCR_OFFSET, |
77 | value: (((phy_id << XAE_MDIO_MCR_PHYAD_SHIFT) & |
78 | XAE_MDIO_MCR_PHYAD_MASK) | |
79 | ((reg << XAE_MDIO_MCR_REGAD_SHIFT) & |
80 | XAE_MDIO_MCR_REGAD_MASK) | |
81 | XAE_MDIO_MCR_INITIATE_MASK | |
82 | XAE_MDIO_MCR_OP_READ_MASK)); |
83 | |
84 | ret = axienet_mdio_wait_until_ready(lp); |
85 | if (ret < 0) { |
86 | axienet_mdio_mdc_disable(lp); |
87 | return ret; |
88 | } |
89 | |
90 | rc = axienet_ior(lp, XAE_MDIO_MRD_OFFSET) & 0x0000FFFF; |
91 | |
92 | dev_dbg(lp->dev, "axienet_mdio_read(phy_id=%i, reg=%x) == %x\n" , |
93 | phy_id, reg, rc); |
94 | |
95 | axienet_mdio_mdc_disable(lp); |
96 | return rc; |
97 | } |
98 | |
99 | /** |
100 | * axienet_mdio_write - MDIO interface write function |
101 | * @bus: Pointer to mii bus structure |
102 | * @phy_id: Address of the PHY device |
103 | * @reg: PHY register to write to |
104 | * @val: Value to be written into the register |
105 | * |
106 | * Return: 0 on success, -ETIMEDOUT on a timeout |
107 | * |
108 | * Writes the value to the requested register by first writing the value |
109 | * into MWD register. The MCR register is then appropriately setup |
110 | * to finish the write operation. |
111 | */ |
112 | static int axienet_mdio_write(struct mii_bus *bus, int phy_id, int reg, |
113 | u16 val) |
114 | { |
115 | int ret; |
116 | struct axienet_local *lp = bus->priv; |
117 | |
118 | dev_dbg(lp->dev, "axienet_mdio_write(phy_id=%i, reg=%x, val=%x)\n" , |
119 | phy_id, reg, val); |
120 | |
121 | axienet_mdio_mdc_enable(lp); |
122 | |
123 | ret = axienet_mdio_wait_until_ready(lp); |
124 | if (ret < 0) { |
125 | axienet_mdio_mdc_disable(lp); |
126 | return ret; |
127 | } |
128 | |
129 | axienet_iow(lp, XAE_MDIO_MWD_OFFSET, value: (u32)val); |
130 | axienet_iow(lp, XAE_MDIO_MCR_OFFSET, |
131 | value: (((phy_id << XAE_MDIO_MCR_PHYAD_SHIFT) & |
132 | XAE_MDIO_MCR_PHYAD_MASK) | |
133 | ((reg << XAE_MDIO_MCR_REGAD_SHIFT) & |
134 | XAE_MDIO_MCR_REGAD_MASK) | |
135 | XAE_MDIO_MCR_INITIATE_MASK | |
136 | XAE_MDIO_MCR_OP_WRITE_MASK)); |
137 | |
138 | ret = axienet_mdio_wait_until_ready(lp); |
139 | if (ret < 0) { |
140 | axienet_mdio_mdc_disable(lp); |
141 | return ret; |
142 | } |
143 | axienet_mdio_mdc_disable(lp); |
144 | return 0; |
145 | } |
146 | |
147 | /** |
148 | * axienet_mdio_enable - MDIO hardware setup function |
149 | * @lp: Pointer to axienet local data structure. |
150 | * @np: Pointer to mdio device tree node. |
151 | * |
152 | * Return: 0 on success, -ETIMEDOUT on a timeout, -EOVERFLOW on a clock |
153 | * divisor overflow. |
154 | * |
155 | * Sets up the MDIO interface by initializing the MDIO clock and enabling the |
156 | * MDIO interface in hardware. |
157 | **/ |
158 | static int axienet_mdio_enable(struct axienet_local *lp, struct device_node *np) |
159 | { |
160 | u32 mdio_freq = DEFAULT_MDIO_FREQ; |
161 | u32 host_clock; |
162 | u32 clk_div; |
163 | int ret; |
164 | |
165 | lp->mii_clk_div = 0; |
166 | |
167 | if (lp->axi_clk) { |
168 | host_clock = clk_get_rate(clk: lp->axi_clk); |
169 | } else { |
170 | struct device_node *np1; |
171 | |
172 | /* Legacy fallback: detect CPU clock frequency and use as AXI |
173 | * bus clock frequency. This only works on certain platforms. |
174 | */ |
175 | np1 = of_find_node_by_name(NULL, name: "cpu" ); |
176 | if (!np1) { |
177 | netdev_warn(dev: lp->ndev, format: "Could not find CPU device node.\n" ); |
178 | host_clock = DEFAULT_HOST_CLOCK; |
179 | } else { |
180 | int ret = of_property_read_u32(np: np1, propname: "clock-frequency" , |
181 | out_value: &host_clock); |
182 | if (ret) { |
183 | netdev_warn(dev: lp->ndev, format: "CPU clock-frequency property not found.\n" ); |
184 | host_clock = DEFAULT_HOST_CLOCK; |
185 | } |
186 | of_node_put(node: np1); |
187 | } |
188 | netdev_info(dev: lp->ndev, format: "Setting assumed host clock to %u\n" , |
189 | host_clock); |
190 | } |
191 | |
192 | if (np) |
193 | of_property_read_u32(np, propname: "clock-frequency" , out_value: &mdio_freq); |
194 | if (mdio_freq != DEFAULT_MDIO_FREQ) |
195 | netdev_info(dev: lp->ndev, format: "Setting non-standard mdio bus frequency to %u Hz\n" , |
196 | mdio_freq); |
197 | |
198 | /* clk_div can be calculated by deriving it from the equation: |
199 | * fMDIO = fHOST / ((1 + clk_div) * 2) |
200 | * |
201 | * Where fMDIO <= 2500000, so we get: |
202 | * fHOST / ((1 + clk_div) * 2) <= 2500000 |
203 | * |
204 | * Then we get: |
205 | * 1 / ((1 + clk_div) * 2) <= (2500000 / fHOST) |
206 | * |
207 | * Then we get: |
208 | * 1 / (1 + clk_div) <= ((2500000 * 2) / fHOST) |
209 | * |
210 | * Then we get: |
211 | * 1 / (1 + clk_div) <= (5000000 / fHOST) |
212 | * |
213 | * So: |
214 | * (1 + clk_div) >= (fHOST / 5000000) |
215 | * |
216 | * And finally: |
217 | * clk_div >= (fHOST / 5000000) - 1 |
218 | * |
219 | * fHOST can be read from the flattened device tree as property |
220 | * "clock-frequency" from the CPU |
221 | */ |
222 | |
223 | clk_div = (host_clock / (mdio_freq * 2)) - 1; |
224 | /* If there is any remainder from the division of |
225 | * fHOST / (mdio_freq * 2), then we need to add |
226 | * 1 to the clock divisor or we will surely be |
227 | * above the requested frequency |
228 | */ |
229 | if (host_clock % (mdio_freq * 2)) |
230 | clk_div++; |
231 | |
232 | /* Check for overflow of mii_clk_div */ |
233 | if (clk_div & ~XAE_MDIO_MC_CLOCK_DIVIDE_MAX) { |
234 | netdev_warn(dev: lp->ndev, format: "MDIO clock divisor overflow\n" ); |
235 | return -EOVERFLOW; |
236 | } |
237 | lp->mii_clk_div = (u8)clk_div; |
238 | |
239 | netdev_dbg(lp->ndev, |
240 | "Setting MDIO clock divisor to %u/%u Hz host clock.\n" , |
241 | lp->mii_clk_div, host_clock); |
242 | |
243 | axienet_mdio_mdc_enable(lp); |
244 | |
245 | ret = axienet_mdio_wait_until_ready(lp); |
246 | if (ret) |
247 | axienet_mdio_mdc_disable(lp); |
248 | |
249 | return ret; |
250 | } |
251 | |
252 | /** |
253 | * axienet_mdio_setup - MDIO setup function |
254 | * @lp: Pointer to axienet local data structure. |
255 | * |
256 | * Return: 0 on success, -ETIMEDOUT on a timeout, -EOVERFLOW on a clock |
257 | * divisor overflow, -ENOMEM when mdiobus_alloc (to allocate |
258 | * memory for mii bus structure) fails. |
259 | * |
260 | * Sets up the MDIO interface by initializing the MDIO clock. |
261 | * Register the MDIO interface. |
262 | **/ |
263 | int axienet_mdio_setup(struct axienet_local *lp) |
264 | { |
265 | struct device_node *mdio_node; |
266 | struct mii_bus *bus; |
267 | int ret; |
268 | |
269 | bus = mdiobus_alloc(); |
270 | if (!bus) |
271 | return -ENOMEM; |
272 | |
273 | snprintf(buf: bus->id, MII_BUS_ID_SIZE, fmt: "axienet-%.8llx" , |
274 | (unsigned long long)lp->regs_start); |
275 | |
276 | bus->priv = lp; |
277 | bus->name = "Xilinx Axi Ethernet MDIO" ; |
278 | bus->read = axienet_mdio_read; |
279 | bus->write = axienet_mdio_write; |
280 | bus->parent = lp->dev; |
281 | lp->mii_bus = bus; |
282 | |
283 | mdio_node = of_get_child_by_name(node: lp->dev->of_node, name: "mdio" ); |
284 | ret = axienet_mdio_enable(lp, np: mdio_node); |
285 | if (ret < 0) |
286 | goto unregister; |
287 | ret = of_mdiobus_register(mdio: bus, np: mdio_node); |
288 | if (ret) |
289 | goto unregister_mdio_enabled; |
290 | of_node_put(node: mdio_node); |
291 | axienet_mdio_mdc_disable(lp); |
292 | return 0; |
293 | |
294 | unregister_mdio_enabled: |
295 | axienet_mdio_mdc_disable(lp); |
296 | unregister: |
297 | of_node_put(node: mdio_node); |
298 | mdiobus_free(bus); |
299 | lp->mii_bus = NULL; |
300 | return ret; |
301 | } |
302 | |
303 | /** |
304 | * axienet_mdio_teardown - MDIO remove function |
305 | * @lp: Pointer to axienet local data structure. |
306 | * |
307 | * Unregisters the MDIO and frees any associate memory for mii bus. |
308 | */ |
309 | void axienet_mdio_teardown(struct axienet_local *lp) |
310 | { |
311 | mdiobus_unregister(bus: lp->mii_bus); |
312 | mdiobus_free(bus: lp->mii_bus); |
313 | lp->mii_bus = NULL; |
314 | } |
315 | |