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>
23 #include <linux/delay.h>
24 #include <linux/brcmphy.h>
25 #include <linux/rtnetlink.h>
31 #define REG_MII_PAGE 0x10 /* MII Page register */
32 #define REG_MII_ADDR 0x11 /* MII Address register */
33 #define REG_MII_DATA0 0x18 /* MII Data register 0 */
34 #define REG_MII_DATA1 0x19 /* MII Data register 1 */
35 #define REG_MII_DATA2 0x1a /* MII Data register 2 */
36 #define REG_MII_DATA3 0x1b /* MII Data register 3 */
38 #define REG_MII_PAGE_ENABLE BIT(0)
39 #define REG_MII_ADDR_WRITE BIT(0)
40 #define REG_MII_ADDR_READ BIT(1)
42 static int b53_mdio_op(struct b53_device
*dev
, u8 page
, u8 reg
, u16 op
)
47 struct mii_bus
*bus
= dev
->priv
;
49 if (dev
->current_page
!= page
) {
51 v
= (page
<< 8) | REG_MII_PAGE_ENABLE
;
52 ret
= mdiobus_write_nested(bus
, BRCM_PSEUDO_PHY_ADDR
,
56 dev
->current_page
= page
;
59 /* set register address */
61 ret
= mdiobus_write_nested(bus
, BRCM_PSEUDO_PHY_ADDR
, REG_MII_ADDR
, v
);
65 /* check if operation completed */
66 for (i
= 0; i
< 5; ++i
) {
67 v
= mdiobus_read_nested(bus
, BRCM_PSEUDO_PHY_ADDR
,
69 if (!(v
& (REG_MII_ADDR_WRITE
| REG_MII_ADDR_READ
)))
71 usleep_range(10, 100);
80 static int b53_mdio_read8(struct b53_device
*dev
, u8 page
, u8 reg
, u8
*val
)
82 struct mii_bus
*bus
= dev
->priv
;
85 ret
= b53_mdio_op(dev
, page
, reg
, REG_MII_ADDR_READ
);
89 *val
= mdiobus_read_nested(bus
, BRCM_PSEUDO_PHY_ADDR
,
90 REG_MII_DATA0
) & 0xff;
95 static int b53_mdio_read16(struct b53_device
*dev
, u8 page
, u8 reg
, u16
*val
)
97 struct mii_bus
*bus
= dev
->priv
;
100 ret
= b53_mdio_op(dev
, page
, reg
, REG_MII_ADDR_READ
);
104 *val
= mdiobus_read_nested(bus
, BRCM_PSEUDO_PHY_ADDR
, REG_MII_DATA0
);
109 static int b53_mdio_read32(struct b53_device
*dev
, u8 page
, u8 reg
, u32
*val
)
111 struct mii_bus
*bus
= dev
->priv
;
114 ret
= b53_mdio_op(dev
, page
, reg
, REG_MII_ADDR_READ
);
118 *val
= mdiobus_read_nested(bus
, BRCM_PSEUDO_PHY_ADDR
, REG_MII_DATA0
);
119 *val
|= mdiobus_read_nested(bus
, BRCM_PSEUDO_PHY_ADDR
,
120 REG_MII_DATA1
) << 16;
125 static int b53_mdio_read48(struct b53_device
*dev
, u8 page
, u8 reg
, u64
*val
)
127 struct mii_bus
*bus
= dev
->priv
;
132 ret
= b53_mdio_op(dev
, page
, reg
, REG_MII_ADDR_READ
);
136 for (i
= 2; i
>= 0; i
--) {
138 temp
|= mdiobus_read_nested(bus
, BRCM_PSEUDO_PHY_ADDR
,
147 static int b53_mdio_read64(struct b53_device
*dev
, u8 page
, u8 reg
, u64
*val
)
149 struct mii_bus
*bus
= dev
->priv
;
154 ret
= b53_mdio_op(dev
, page
, reg
, REG_MII_ADDR_READ
);
158 for (i
= 3; i
>= 0; i
--) {
160 temp
|= mdiobus_read_nested(bus
, BRCM_PSEUDO_PHY_ADDR
,
169 static int b53_mdio_write8(struct b53_device
*dev
, u8 page
, u8 reg
, u8 value
)
171 struct mii_bus
*bus
= dev
->priv
;
174 ret
= mdiobus_write_nested(bus
, BRCM_PSEUDO_PHY_ADDR
,
175 REG_MII_DATA0
, value
);
179 return b53_mdio_op(dev
, page
, reg
, REG_MII_ADDR_WRITE
);
182 static int b53_mdio_write16(struct b53_device
*dev
, u8 page
, u8 reg
,
185 struct mii_bus
*bus
= dev
->priv
;
188 ret
= mdiobus_write_nested(bus
, BRCM_PSEUDO_PHY_ADDR
,
189 REG_MII_DATA0
, value
);
193 return b53_mdio_op(dev
, page
, reg
, REG_MII_ADDR_WRITE
);
196 static int b53_mdio_write32(struct b53_device
*dev
, u8 page
, u8 reg
,
199 struct mii_bus
*bus
= dev
->priv
;
203 for (i
= 0; i
< 2; i
++) {
204 int ret
= mdiobus_write_nested(bus
, BRCM_PSEUDO_PHY_ADDR
,
212 return b53_mdio_op(dev
, page
, reg
, REG_MII_ADDR_WRITE
);
215 static int b53_mdio_write48(struct b53_device
*dev
, u8 page
, u8 reg
,
218 struct mii_bus
*bus
= dev
->priv
;
222 for (i
= 0; i
< 3; i
++) {
223 int ret
= mdiobus_write_nested(bus
, BRCM_PSEUDO_PHY_ADDR
,
231 return b53_mdio_op(dev
, page
, reg
, REG_MII_ADDR_WRITE
);
234 static int b53_mdio_write64(struct b53_device
*dev
, u8 page
, u8 reg
,
237 struct mii_bus
*bus
= dev
->priv
;
241 for (i
= 0; i
< 4; i
++) {
242 int ret
= mdiobus_write_nested(bus
, BRCM_PSEUDO_PHY_ADDR
,
250 return b53_mdio_op(dev
, page
, reg
, REG_MII_ADDR_WRITE
);
253 static int b53_mdio_phy_read16(struct b53_device
*dev
, int addr
, int reg
,
256 struct mii_bus
*bus
= dev
->priv
;
258 *value
= mdiobus_read_nested(bus
, addr
, reg
);
263 static int b53_mdio_phy_write16(struct b53_device
*dev
, int addr
, int reg
,
266 struct mii_bus
*bus
= dev
->bus
;
268 return mdiobus_write_nested(bus
, addr
, reg
, value
);
271 static const struct b53_io_ops b53_mdio_ops
= {
272 .read8
= b53_mdio_read8
,
273 .read16
= b53_mdio_read16
,
274 .read32
= b53_mdio_read32
,
275 .read48
= b53_mdio_read48
,
276 .read64
= b53_mdio_read64
,
277 .write8
= b53_mdio_write8
,
278 .write16
= b53_mdio_write16
,
279 .write32
= b53_mdio_write32
,
280 .write48
= b53_mdio_write48
,
281 .write64
= b53_mdio_write64
,
282 .phy_read16
= b53_mdio_phy_read16
,
283 .phy_write16
= b53_mdio_phy_write16
,
286 #define B53_BRCM_OUI_1 0x0143bc00
287 #define B53_BRCM_OUI_2 0x03625c00
288 #define B53_BRCM_OUI_3 0x00406000
289 #define B53_BRCM_OUI_4 0x01410c00
290 #define B53_BRCM_OUI_5 0xae025000
292 static int b53_mdio_probe(struct mdio_device
*mdiodev
)
294 struct b53_device
*dev
;
298 /* allow the generic PHY driver to take over the non-management MDIO
301 if (mdiodev
->addr
!= BRCM_PSEUDO_PHY_ADDR
&& mdiodev
->addr
!= 0) {
302 dev_err(&mdiodev
->dev
, "leaving address %d to PHY\n",
307 /* read the first port's id */
308 phy_id
= mdiobus_read(mdiodev
->bus
, 0, 2) << 16;
309 phy_id
|= mdiobus_read(mdiodev
->bus
, 0, 3);
311 /* BCM5325, BCM539x (OUI_1)
312 * BCM53125, BCM53128 (OUI_2)
315 if ((phy_id
& 0xfffffc00) != B53_BRCM_OUI_1
&&
316 (phy_id
& 0xfffffc00) != B53_BRCM_OUI_2
&&
317 (phy_id
& 0xfffffc00) != B53_BRCM_OUI_3
&&
318 (phy_id
& 0xfffffc00) != B53_BRCM_OUI_4
&&
319 (phy_id
& 0xfffffc00) != B53_BRCM_OUI_5
) {
320 dev_err(&mdiodev
->dev
, "Unsupported device: 0x%08x\n", phy_id
);
324 /* First probe will come from SWITCH_MDIO controller on the 7445D0
325 * switch, which will conflict with the 7445 integrated switch
326 * pseudo-phy (we end-up programming both). In that case, we return
327 * -EPROBE_DEFER for the first time we get here, and wait until we come
328 * back with the slave MDIO bus which has the correct indirection
331 if (of_machine_is_compatible("brcm,bcm7445d0") &&
332 strcmp(mdiodev
->bus
->name
, "sf2 user mii"))
333 return -EPROBE_DEFER
;
335 dev
= b53_switch_alloc(&mdiodev
->dev
, &b53_mdio_ops
, mdiodev
->bus
);
339 /* we don't use page 0xff, so force a page set */
340 dev
->current_page
= 0xff;
341 dev
->bus
= mdiodev
->bus
;
343 dev_set_drvdata(&mdiodev
->dev
, dev
);
345 ret
= b53_switch_register(dev
);
347 return dev_err_probe(&mdiodev
->dev
, ret
,
348 "failed to register switch\n");
353 static void b53_mdio_remove(struct mdio_device
*mdiodev
)
355 struct b53_device
*dev
= dev_get_drvdata(&mdiodev
->dev
);
360 b53_switch_remove(dev
);
363 static void b53_mdio_shutdown(struct mdio_device
*mdiodev
)
365 struct b53_device
*dev
= dev_get_drvdata(&mdiodev
->dev
);
370 b53_switch_shutdown(dev
);
372 dev_set_drvdata(&mdiodev
->dev
, NULL
);
375 static const struct of_device_id b53_of_match
[] = {
376 { .compatible
= "brcm,bcm5325" },
377 { .compatible
= "brcm,bcm53115" },
378 { .compatible
= "brcm,bcm53125" },
379 { .compatible
= "brcm,bcm53128" },
380 { .compatible
= "brcm,bcm53134" },
381 { .compatible
= "brcm,bcm5365" },
382 { .compatible
= "brcm,bcm5389" },
383 { .compatible
= "brcm,bcm5395" },
384 { .compatible
= "brcm,bcm5397" },
385 { .compatible
= "brcm,bcm5398" },
388 MODULE_DEVICE_TABLE(of
, b53_of_match
);
390 static struct mdio_driver b53_mdio_driver
= {
391 .probe
= b53_mdio_probe
,
392 .remove
= b53_mdio_remove
,
393 .shutdown
= b53_mdio_shutdown
,
396 .of_match_table
= b53_of_match
,
399 mdio_module_driver(b53_mdio_driver
);
401 MODULE_DESCRIPTION("B53 MDIO access driver");
402 MODULE_LICENSE("Dual BSD/GPL");