2 * B53 register access through MII registers
4 * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
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.
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.
19 #include <linux/kernel.h>
20 #include <linux/phy.h>
21 #include <linux/module.h>
22 #include <linux/delay.h>
23 #include <linux/brcmphy.h>
24 #include <linux/rtnetlink.h>
30 #define REG_MII_PAGE 0x10 /* MII Page register */
31 #define REG_MII_ADDR 0x11 /* MII Address register */
32 #define REG_MII_DATA0 0x18 /* MII Data register 0 */
33 #define REG_MII_DATA1 0x19 /* MII Data register 1 */
34 #define REG_MII_DATA2 0x1a /* MII Data register 2 */
35 #define REG_MII_DATA3 0x1b /* MII Data register 3 */
37 #define REG_MII_PAGE_ENABLE BIT(0)
38 #define REG_MII_ADDR_WRITE BIT(0)
39 #define REG_MII_ADDR_READ BIT(1)
41 static int b53_mdio_op(struct b53_device
*dev
, u8 page
, u8 reg
, u16 op
)
46 struct mii_bus
*bus
= dev
->priv
;
48 if (dev
->current_page
!= page
) {
50 v
= (page
<< 8) | REG_MII_PAGE_ENABLE
;
51 ret
= mdiobus_write_nested(bus
, BRCM_PSEUDO_PHY_ADDR
,
55 dev
->current_page
= page
;
58 /* set register address */
60 ret
= mdiobus_write_nested(bus
, BRCM_PSEUDO_PHY_ADDR
, REG_MII_ADDR
, v
);
64 /* check if operation completed */
65 for (i
= 0; i
< 5; ++i
) {
66 v
= mdiobus_read_nested(bus
, BRCM_PSEUDO_PHY_ADDR
,
68 if (!(v
& (REG_MII_ADDR_WRITE
| REG_MII_ADDR_READ
)))
70 usleep_range(10, 100);
79 static int b53_mdio_read8(struct b53_device
*dev
, u8 page
, u8 reg
, u8
*val
)
81 struct mii_bus
*bus
= dev
->priv
;
84 ret
= b53_mdio_op(dev
, page
, reg
, REG_MII_ADDR_READ
);
88 *val
= mdiobus_read_nested(bus
, BRCM_PSEUDO_PHY_ADDR
,
89 REG_MII_DATA0
) & 0xff;
94 static int b53_mdio_read16(struct b53_device
*dev
, u8 page
, u8 reg
, u16
*val
)
96 struct mii_bus
*bus
= dev
->priv
;
99 ret
= b53_mdio_op(dev
, page
, reg
, REG_MII_ADDR_READ
);
103 *val
= mdiobus_read_nested(bus
, BRCM_PSEUDO_PHY_ADDR
, REG_MII_DATA0
);
108 static int b53_mdio_read32(struct b53_device
*dev
, u8 page
, u8 reg
, u32
*val
)
110 struct mii_bus
*bus
= dev
->priv
;
113 ret
= b53_mdio_op(dev
, page
, reg
, REG_MII_ADDR_READ
);
117 *val
= mdiobus_read_nested(bus
, BRCM_PSEUDO_PHY_ADDR
, REG_MII_DATA0
);
118 *val
|= mdiobus_read_nested(bus
, BRCM_PSEUDO_PHY_ADDR
,
119 REG_MII_DATA1
) << 16;
124 static int b53_mdio_read48(struct b53_device
*dev
, u8 page
, u8 reg
, u64
*val
)
126 struct mii_bus
*bus
= dev
->priv
;
131 ret
= b53_mdio_op(dev
, page
, reg
, REG_MII_ADDR_READ
);
135 for (i
= 2; i
>= 0; i
--) {
137 temp
|= mdiobus_read_nested(bus
, BRCM_PSEUDO_PHY_ADDR
,
146 static int b53_mdio_read64(struct b53_device
*dev
, u8 page
, u8 reg
, u64
*val
)
148 struct mii_bus
*bus
= dev
->priv
;
153 ret
= b53_mdio_op(dev
, page
, reg
, REG_MII_ADDR_READ
);
157 for (i
= 3; i
>= 0; i
--) {
159 temp
|= mdiobus_read_nested(bus
, BRCM_PSEUDO_PHY_ADDR
,
168 static int b53_mdio_write8(struct b53_device
*dev
, u8 page
, u8 reg
, u8 value
)
170 struct mii_bus
*bus
= dev
->priv
;
173 ret
= mdiobus_write_nested(bus
, BRCM_PSEUDO_PHY_ADDR
,
174 REG_MII_DATA0
, value
);
178 return b53_mdio_op(dev
, page
, reg
, REG_MII_ADDR_WRITE
);
181 static int b53_mdio_write16(struct b53_device
*dev
, u8 page
, u8 reg
,
184 struct mii_bus
*bus
= dev
->priv
;
187 ret
= mdiobus_write_nested(bus
, BRCM_PSEUDO_PHY_ADDR
,
188 REG_MII_DATA0
, value
);
192 return b53_mdio_op(dev
, page
, reg
, REG_MII_ADDR_WRITE
);
195 static int b53_mdio_write32(struct b53_device
*dev
, u8 page
, u8 reg
,
198 struct mii_bus
*bus
= dev
->priv
;
202 for (i
= 0; i
< 2; i
++) {
203 int ret
= mdiobus_write_nested(bus
, BRCM_PSEUDO_PHY_ADDR
,
211 return b53_mdio_op(dev
, page
, reg
, REG_MII_ADDR_WRITE
);
214 static int b53_mdio_write48(struct b53_device
*dev
, u8 page
, u8 reg
,
217 struct mii_bus
*bus
= dev
->priv
;
221 for (i
= 0; i
< 3; i
++) {
222 int ret
= mdiobus_write_nested(bus
, BRCM_PSEUDO_PHY_ADDR
,
230 return b53_mdio_op(dev
, page
, reg
, REG_MII_ADDR_WRITE
);
233 static int b53_mdio_write64(struct b53_device
*dev
, u8 page
, u8 reg
,
236 struct mii_bus
*bus
= dev
->priv
;
240 for (i
= 0; i
< 4; i
++) {
241 int ret
= mdiobus_write_nested(bus
, BRCM_PSEUDO_PHY_ADDR
,
249 return b53_mdio_op(dev
, page
, reg
, REG_MII_ADDR_WRITE
);
252 static int b53_mdio_phy_read16(struct b53_device
*dev
, int addr
, int reg
,
255 struct mii_bus
*bus
= dev
->priv
;
257 *value
= mdiobus_read_nested(bus
, addr
, reg
);
262 static int b53_mdio_phy_write16(struct b53_device
*dev
, int addr
, int reg
,
265 struct mii_bus
*bus
= dev
->bus
;
267 return mdiobus_write_nested(bus
, addr
, reg
, value
);
270 static const struct b53_io_ops b53_mdio_ops
= {
271 .read8
= b53_mdio_read8
,
272 .read16
= b53_mdio_read16
,
273 .read32
= b53_mdio_read32
,
274 .read48
= b53_mdio_read48
,
275 .read64
= b53_mdio_read64
,
276 .write8
= b53_mdio_write8
,
277 .write16
= b53_mdio_write16
,
278 .write32
= b53_mdio_write32
,
279 .write48
= b53_mdio_write48
,
280 .write64
= b53_mdio_write64
,
281 .phy_read16
= b53_mdio_phy_read16
,
282 .phy_write16
= b53_mdio_phy_write16
,
285 #define B53_BRCM_OUI_1 0x0143bc00
286 #define B53_BRCM_OUI_2 0x03625c00
287 #define B53_BRCM_OUI_3 0x00406000
289 static int b53_mdio_probe(struct mdio_device
*mdiodev
)
291 struct b53_device
*dev
;
295 /* allow the generic PHY driver to take over the non-management MDIO
298 if (mdiodev
->addr
!= BRCM_PSEUDO_PHY_ADDR
&& mdiodev
->addr
!= 0) {
299 dev_err(&mdiodev
->dev
, "leaving address %d to PHY\n",
304 /* read the first port's id */
305 phy_id
= mdiobus_read(mdiodev
->bus
, 0, 2) << 16;
306 phy_id
|= mdiobus_read(mdiodev
->bus
, 0, 3);
308 /* BCM5325, BCM539x (OUI_1)
309 * BCM53125, BCM53128 (OUI_2)
312 if ((phy_id
& 0xfffffc00) != B53_BRCM_OUI_1
&&
313 (phy_id
& 0xfffffc00) != B53_BRCM_OUI_2
&&
314 (phy_id
& 0xfffffc00) != B53_BRCM_OUI_3
) {
315 dev_err(&mdiodev
->dev
, "Unsupported device: 0x%08x\n", phy_id
);
319 /* First probe will come from SWITCH_MDIO controller on the 7445D0
320 * switch, which will conflict with the 7445 integrated switch
321 * pseudo-phy (we end-up programming both). In that case, we return
322 * -EPROBE_DEFER for the first time we get here, and wait until we come
323 * back with the slave MDIO bus which has the correct indirection
326 if (of_machine_is_compatible("brcm,bcm7445d0") &&
327 strcmp(mdiodev
->bus
->name
, "sf2 slave mii"))
328 return -EPROBE_DEFER
;
330 dev
= b53_switch_alloc(&mdiodev
->dev
, &b53_mdio_ops
, mdiodev
->bus
);
334 /* we don't use page 0xff, so force a page set */
335 dev
->current_page
= 0xff;
336 dev
->bus
= mdiodev
->bus
;
338 dev_set_drvdata(&mdiodev
->dev
, dev
);
340 ret
= b53_switch_register(dev
);
342 dev_err(&mdiodev
->dev
, "failed to register switch: %i\n", ret
);
349 static void b53_mdio_remove(struct mdio_device
*mdiodev
)
351 struct b53_device
*dev
= dev_get_drvdata(&mdiodev
->dev
);
352 struct dsa_switch
*ds
= dev
->ds
;
354 dsa_unregister_switch(ds
);
357 static const struct of_device_id b53_of_match
[] = {
358 { .compatible
= "brcm,bcm5325" },
359 { .compatible
= "brcm,bcm53115" },
360 { .compatible
= "brcm,bcm53125" },
361 { .compatible
= "brcm,bcm53128" },
362 { .compatible
= "brcm,bcm5365" },
363 { .compatible
= "brcm,bcm5395" },
364 { .compatible
= "brcm,bcm5397" },
365 { .compatible
= "brcm,bcm5398" },
368 MODULE_DEVICE_TABLE(of
, b53_of_match
);
370 static struct mdio_driver b53_mdio_driver
= {
371 .probe
= b53_mdio_probe
,
372 .remove
= b53_mdio_remove
,
375 .of_match_table
= b53_of_match
,
378 mdio_module_driver(b53_mdio_driver
);
380 MODULE_DESCRIPTION("B53 MDIO access driver");
381 MODULE_LICENSE("Dual BSD/GPL");