OpenWrt – Blame information for rev 2
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* |
2 | * B53 register access through MII registers |
||
3 | * |
||
4 | * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org> |
||
5 | * |
||
6 | * Permission to use, copy, modify, and/or distribute this software for any |
||
7 | * purpose with or without fee is hereby granted, provided that the above |
||
8 | * copyright notice and this permission notice appear in all copies. |
||
9 | * |
||
10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
||
11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
||
12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
||
13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
||
14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
||
15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
||
16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
||
17 | */ |
||
18 | |||
19 | #include <linux/kernel.h> |
||
20 | #include <linux/phy.h> |
||
21 | #include <linux/module.h> |
||
22 | |||
23 | #include "b53_priv.h" |
||
24 | |||
25 | #define B53_PSEUDO_PHY 0x1e /* Register Access Pseudo PHY */ |
||
26 | |||
27 | /* MII registers */ |
||
28 | #define REG_MII_PAGE 0x10 /* MII Page register */ |
||
29 | #define REG_MII_ADDR 0x11 /* MII Address register */ |
||
30 | #define REG_MII_DATA0 0x18 /* MII Data register 0 */ |
||
31 | #define REG_MII_DATA1 0x19 /* MII Data register 1 */ |
||
32 | #define REG_MII_DATA2 0x1a /* MII Data register 2 */ |
||
33 | #define REG_MII_DATA3 0x1b /* MII Data register 3 */ |
||
34 | |||
35 | #define REG_MII_PAGE_ENABLE BIT(0) |
||
36 | #define REG_MII_ADDR_WRITE BIT(0) |
||
37 | #define REG_MII_ADDR_READ BIT(1) |
||
38 | |||
39 | static int b53_mdio_op(struct b53_device *dev, u8 page, u8 reg, u16 op) |
||
40 | { |
||
41 | int i; |
||
42 | u16 v; |
||
43 | int ret; |
||
44 | struct mii_bus *bus = dev->priv; |
||
45 | |||
46 | if (dev->current_page != page) { |
||
47 | /* set page number */ |
||
48 | v = (page << 8) | REG_MII_PAGE_ENABLE; |
||
49 | ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_PAGE, v); |
||
50 | if (ret) |
||
51 | return ret; |
||
52 | dev->current_page = page; |
||
53 | } |
||
54 | |||
55 | /* set register address */ |
||
56 | v = (reg << 8) | op; |
||
57 | ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_ADDR, v); |
||
58 | if (ret) |
||
59 | return ret; |
||
60 | |||
61 | /* check if operation completed */ |
||
62 | for (i = 0; i < 5; ++i) { |
||
63 | v = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_ADDR); |
||
64 | if (!(v & (REG_MII_ADDR_WRITE | REG_MII_ADDR_READ))) |
||
65 | break; |
||
66 | usleep_range(10, 100); |
||
67 | } |
||
68 | |||
69 | if (WARN_ON(i == 5)) |
||
70 | return -EIO; |
||
71 | |||
72 | return 0; |
||
73 | } |
||
74 | |||
75 | static int b53_mdio_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val) |
||
76 | { |
||
77 | struct mii_bus *bus = dev->priv; |
||
78 | int ret; |
||
79 | |||
80 | ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ); |
||
81 | if (ret) |
||
82 | return ret; |
||
83 | |||
84 | *val = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0) & 0xff; |
||
85 | |||
86 | return 0; |
||
87 | } |
||
88 | |||
89 | static int b53_mdio_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val) |
||
90 | { |
||
91 | struct mii_bus *bus = dev->priv; |
||
92 | int ret; |
||
93 | |||
94 | ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ); |
||
95 | if (ret) |
||
96 | return ret; |
||
97 | |||
98 | *val = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0); |
||
99 | |||
100 | return 0; |
||
101 | } |
||
102 | |||
103 | static int b53_mdio_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val) |
||
104 | { |
||
105 | struct mii_bus *bus = dev->priv; |
||
106 | int ret; |
||
107 | |||
108 | ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ); |
||
109 | if (ret) |
||
110 | return ret; |
||
111 | |||
112 | *val = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0); |
||
113 | *val |= mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA1) << 16; |
||
114 | |||
115 | return 0; |
||
116 | } |
||
117 | |||
118 | static int b53_mdio_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val) |
||
119 | { |
||
120 | struct mii_bus *bus = dev->priv; |
||
121 | u64 temp = 0; |
||
122 | int i; |
||
123 | int ret; |
||
124 | |||
125 | ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ); |
||
126 | if (ret) |
||
127 | return ret; |
||
128 | |||
129 | for (i = 2; i >= 0; i--) { |
||
130 | temp <<= 16; |
||
131 | temp |= mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i); |
||
132 | } |
||
133 | |||
134 | *val = temp; |
||
135 | |||
136 | return 0; |
||
137 | } |
||
138 | |||
139 | static int b53_mdio_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val) |
||
140 | { |
||
141 | struct mii_bus *bus = dev->priv; |
||
142 | u64 temp = 0; |
||
143 | int i; |
||
144 | int ret; |
||
145 | |||
146 | ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ); |
||
147 | if (ret) |
||
148 | return ret; |
||
149 | |||
150 | for (i = 3; i >= 0; i--) { |
||
151 | temp <<= 16; |
||
152 | temp |= mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i); |
||
153 | } |
||
154 | |||
155 | *val = temp; |
||
156 | |||
157 | return 0; |
||
158 | } |
||
159 | |||
160 | static int b53_mdio_write8(struct b53_device *dev, u8 page, u8 reg, u8 value) |
||
161 | { |
||
162 | struct mii_bus *bus = dev->priv; |
||
163 | int ret; |
||
164 | |||
165 | ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0, value); |
||
166 | if (ret) |
||
167 | return ret; |
||
168 | |||
169 | return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE); |
||
170 | } |
||
171 | |||
172 | static int b53_mdio_write16(struct b53_device *dev, u8 page, u8 reg, |
||
173 | u16 value) |
||
174 | { |
||
175 | struct mii_bus *bus = dev->priv; |
||
176 | int ret; |
||
177 | |||
178 | ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0, value); |
||
179 | if (ret) |
||
180 | return ret; |
||
181 | |||
182 | return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE); |
||
183 | } |
||
184 | |||
185 | static int b53_mdio_write32(struct b53_device *dev, u8 page, u8 reg, |
||
186 | u32 value) |
||
187 | { |
||
188 | struct mii_bus *bus = dev->priv; |
||
189 | unsigned int i; |
||
190 | u32 temp = value; |
||
191 | |||
192 | for (i = 0; i < 2; i++) { |
||
193 | int ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i, |
||
194 | temp & 0xffff); |
||
195 | if (ret) |
||
196 | return ret; |
||
197 | temp >>= 16; |
||
198 | } |
||
199 | |||
200 | return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE); |
||
201 | |||
202 | } |
||
203 | |||
204 | static int b53_mdio_write48(struct b53_device *dev, u8 page, u8 reg, |
||
205 | u64 value) |
||
206 | { |
||
207 | struct mii_bus *bus = dev->priv; |
||
208 | unsigned i; |
||
209 | u64 temp = value; |
||
210 | |||
211 | for (i = 0; i < 3; i++) { |
||
212 | int ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i, |
||
213 | temp & 0xffff); |
||
214 | if (ret) |
||
215 | return ret; |
||
216 | temp >>= 16; |
||
217 | } |
||
218 | |||
219 | return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE); |
||
220 | |||
221 | } |
||
222 | |||
223 | static int b53_mdio_write64(struct b53_device *dev, u8 page, u8 reg, |
||
224 | u64 value) |
||
225 | { |
||
226 | struct mii_bus *bus = dev->priv; |
||
227 | unsigned i; |
||
228 | u64 temp = value; |
||
229 | |||
230 | for (i = 0; i < 4; i++) { |
||
231 | int ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i, |
||
232 | temp & 0xffff); |
||
233 | if (ret) |
||
234 | return ret; |
||
235 | temp >>= 16; |
||
236 | } |
||
237 | |||
238 | return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE); |
||
239 | } |
||
240 | |||
241 | static int b53_mdio_phy_read16(struct b53_device *dev, int addr, u8 reg, |
||
242 | u16 *value) |
||
243 | { |
||
244 | struct mii_bus *bus = dev->priv; |
||
245 | |||
246 | *value = mdiobus_read(bus, addr, reg); |
||
247 | |||
248 | return 0; |
||
249 | } |
||
250 | |||
251 | static int b53_mdio_phy_write16(struct b53_device *dev, int addr, u8 reg, |
||
252 | u16 value) |
||
253 | { |
||
254 | struct mii_bus *bus = dev->priv; |
||
255 | |||
256 | return mdiobus_write(bus, addr, reg, value); |
||
257 | } |
||
258 | |||
259 | static struct b53_io_ops b53_mdio_ops = { |
||
260 | .read8 = b53_mdio_read8, |
||
261 | .read16 = b53_mdio_read16, |
||
262 | .read32 = b53_mdio_read32, |
||
263 | .read48 = b53_mdio_read48, |
||
264 | .read64 = b53_mdio_read64, |
||
265 | .write8 = b53_mdio_write8, |
||
266 | .write16 = b53_mdio_write16, |
||
267 | .write32 = b53_mdio_write32, |
||
268 | .write48 = b53_mdio_write48, |
||
269 | .write64 = b53_mdio_write64, |
||
270 | .phy_read16 = b53_mdio_phy_read16, |
||
271 | .phy_write16 = b53_mdio_phy_write16, |
||
272 | }; |
||
273 | |||
274 | static int b53_phy_probe(struct phy_device *phydev) |
||
275 | { |
||
276 | struct b53_device *dev; |
||
277 | int ret; |
||
278 | |||
279 | /* allow the generic phy driver to take over */ |
||
280 | if (phydev->mdio.addr != B53_PSEUDO_PHY && phydev->mdio.addr != 0) |
||
281 | return -ENODEV; |
||
282 | |||
283 | dev = b53_switch_alloc(&phydev->mdio.dev, &b53_mdio_ops, phydev->mdio.bus); |
||
284 | if (!dev) |
||
285 | return -ENOMEM; |
||
286 | |||
287 | dev->current_page = 0xff; |
||
288 | dev->priv = phydev->mdio.bus; |
||
289 | dev->ops = &b53_mdio_ops; |
||
290 | dev->pdata = NULL; |
||
291 | mutex_init(&dev->reg_mutex); |
||
292 | |||
293 | ret = b53_switch_detect(dev); |
||
294 | if (ret) |
||
295 | return ret; |
||
296 | |||
297 | if (is5325(dev) || is5365(dev)) |
||
298 | phydev->supported = SUPPORTED_100baseT_Full; |
||
299 | else |
||
300 | phydev->supported = SUPPORTED_1000baseT_Full; |
||
301 | |||
302 | phydev->advertising = phydev->supported; |
||
303 | |||
304 | ret = b53_switch_register(dev); |
||
305 | if (ret) { |
||
306 | dev_err(dev->dev, "failed to register switch: %i\n", ret); |
||
307 | return ret; |
||
308 | } |
||
309 | |||
310 | phydev->priv = dev; |
||
311 | |||
312 | return 0; |
||
313 | } |
||
314 | |||
315 | static int b53_phy_config_init(struct phy_device *phydev) |
||
316 | { |
||
317 | struct b53_device *dev = phydev->priv; |
||
318 | |||
319 | /* we don't use page 0xff, so force a page set */ |
||
320 | dev->current_page = 0xff; |
||
321 | /* force the ethX as alias */ |
||
322 | dev->sw_dev.alias = phydev->attached_dev->name; |
||
323 | |||
324 | return 0; |
||
325 | } |
||
326 | |||
327 | static void b53_phy_remove(struct phy_device *phydev) |
||
328 | { |
||
329 | struct b53_device *priv = phydev->priv; |
||
330 | |||
331 | if (!priv) |
||
332 | return; |
||
333 | |||
334 | b53_switch_remove(priv); |
||
335 | |||
336 | phydev->priv = NULL; |
||
337 | } |
||
338 | |||
339 | static int b53_phy_config_aneg(struct phy_device *phydev) |
||
340 | { |
||
341 | return 0; |
||
342 | } |
||
343 | |||
344 | static int b53_phy_read_status(struct phy_device *phydev) |
||
345 | { |
||
346 | struct b53_device *priv = phydev->priv; |
||
347 | |||
348 | if (is5325(priv) || is5365(priv)) |
||
349 | phydev->speed = 100; |
||
350 | else |
||
351 | phydev->speed = 1000; |
||
352 | |||
353 | phydev->duplex = DUPLEX_FULL; |
||
354 | phydev->link = 1; |
||
355 | phydev->state = PHY_RUNNING; |
||
356 | |||
357 | netif_carrier_on(phydev->attached_dev); |
||
358 | phydev->adjust_link(phydev->attached_dev); |
||
359 | |||
360 | return 0; |
||
361 | } |
||
362 | |||
363 | /* BCM5325, BCM539x */ |
||
364 | static struct phy_driver b53_phy_driver_id1 = { |
||
365 | .phy_id = 0x0143bc00, |
||
366 | .name = "Broadcom B53 (1)", |
||
367 | .phy_id_mask = 0x1ffffc00, |
||
368 | .features = 0, |
||
369 | .probe = b53_phy_probe, |
||
370 | .remove = b53_phy_remove, |
||
371 | .config_aneg = b53_phy_config_aneg, |
||
372 | .config_init = b53_phy_config_init, |
||
373 | .read_status = b53_phy_read_status, |
||
374 | }; |
||
375 | |||
376 | /* BCM53125, BCM53128 */ |
||
377 | static struct phy_driver b53_phy_driver_id2 = { |
||
378 | .phy_id = 0x03625c00, |
||
379 | .name = "Broadcom B53 (2)", |
||
380 | .phy_id_mask = 0x1ffffc00, |
||
381 | .features = 0, |
||
382 | .probe = b53_phy_probe, |
||
383 | .remove = b53_phy_remove, |
||
384 | .config_aneg = b53_phy_config_aneg, |
||
385 | .config_init = b53_phy_config_init, |
||
386 | .read_status = b53_phy_read_status, |
||
387 | }; |
||
388 | |||
389 | /* BCM5365 */ |
||
390 | static struct phy_driver b53_phy_driver_id3 = { |
||
391 | .phy_id = 0x00406000, |
||
392 | .name = "Broadcom B53 (3)", |
||
393 | .phy_id_mask = 0x1ffffc00, |
||
394 | .features = 0, |
||
395 | .probe = b53_phy_probe, |
||
396 | .remove = b53_phy_remove, |
||
397 | .config_aneg = b53_phy_config_aneg, |
||
398 | .config_init = b53_phy_config_init, |
||
399 | .read_status = b53_phy_read_status, |
||
400 | }; |
||
401 | |||
402 | int __init b53_phy_driver_register(void) |
||
403 | { |
||
404 | int ret; |
||
405 | |||
406 | ret = phy_driver_register(&b53_phy_driver_id1, THIS_MODULE); |
||
407 | if (ret) |
||
408 | return ret; |
||
409 | |||
410 | ret = phy_driver_register(&b53_phy_driver_id2, THIS_MODULE); |
||
411 | if (ret) |
||
412 | goto err1; |
||
413 | |||
414 | ret = phy_driver_register(&b53_phy_driver_id3, THIS_MODULE); |
||
415 | if (!ret) |
||
416 | return 0; |
||
417 | |||
418 | phy_driver_unregister(&b53_phy_driver_id2); |
||
419 | err1: |
||
420 | phy_driver_unregister(&b53_phy_driver_id1); |
||
421 | return ret; |
||
422 | } |
||
423 | |||
424 | void __exit b53_phy_driver_unregister(void) |
||
425 | { |
||
426 | phy_driver_unregister(&b53_phy_driver_id3); |
||
427 | phy_driver_unregister(&b53_phy_driver_id2); |
||
428 | phy_driver_unregister(&b53_phy_driver_id1); |
||
429 | } |
||
430 | |||
431 | module_init(b53_phy_driver_register); |
||
432 | module_exit(b53_phy_driver_unregister); |
||
433 | |||
434 | MODULE_DESCRIPTION("B53 MDIO access driver"); |
||
435 | MODULE_LICENSE("Dual BSD/GPL"); |