4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of
7 * the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
19 * Copyright 2010-2011 Freescale Semiconductor, Inc.
27 /* Broadcom BCM54xx -- taken from linux sungem_phy */
28 #define MIIM_BCM54xx_AUXCNTL 0x18
29 #define MIIM_BCM54xx_AUXCNTL_ENCODE(val) (((val & 0x7) << 12)|(val & 0x7))
30 #define MIIM_BCM54xx_AUXSTATUS 0x19
31 #define MIIM_BCM54xx_AUXSTATUS_LINKMODE_MASK 0x0700
32 #define MIIM_BCM54xx_AUXSTATUS_LINKMODE_SHIFT 8
34 #define MIIM_BCM54XX_SHD 0x1c
35 #define MIIM_BCM54XX_SHD_WRITE 0x8000
36 #define MIIM_BCM54XX_SHD_VAL(x) ((x & 0x1f) << 10)
37 #define MIIM_BCM54XX_SHD_DATA(x) ((x & 0x3ff) << 0)
38 #define MIIM_BCM54XX_SHD_WR_ENCODE(val, data) \
39 (MIIM_BCM54XX_SHD_WRITE | MIIM_BCM54XX_SHD_VAL(val) | \
40 MIIM_BCM54XX_SHD_DATA(data))
42 #define MIIM_BCM54XX_EXP_DATA 0x15 /* Expansion register data */
43 #define MIIM_BCM54XX_EXP_SEL 0x17 /* Expansion register select */
44 #define MIIM_BCM54XX_EXP_SEL_SSD 0x0e00 /* Secondary SerDes select */
45 #define MIIM_BCM54XX_EXP_SEL_ER 0x0f00 /* Expansion register select */
47 /* Broadcom BCM5461S */
48 static int bcm5461_config(struct phy_device *phydev)
50 genphy_config_aneg(phydev);
57 static int bcm54xx_parse_status(struct phy_device *phydev)
61 mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXSTATUS);
63 switch ((mii_reg & MIIM_BCM54xx_AUXSTATUS_LINKMODE_MASK) >>
64 MIIM_BCM54xx_AUXSTATUS_LINKMODE_SHIFT) {
66 phydev->duplex = DUPLEX_HALF;
67 phydev->speed = SPEED_10;
70 phydev->duplex = DUPLEX_FULL;
71 phydev->speed = SPEED_10;
74 phydev->duplex = DUPLEX_HALF;
75 phydev->speed = SPEED_100;
78 phydev->duplex = DUPLEX_FULL;
79 phydev->speed = SPEED_100;
82 phydev->duplex = DUPLEX_HALF;
83 phydev->speed = SPEED_1000;
86 phydev->duplex = DUPLEX_FULL;
87 phydev->speed = SPEED_1000;
90 printf("Auto-neg error, defaulting to 10BT/HD\n");
91 phydev->duplex = DUPLEX_HALF;
92 phydev->speed = SPEED_10;
99 static int bcm54xx_startup(struct phy_device *phydev)
101 /* Read the Status (2x to make sure link is right) */
102 genphy_update_link(phydev);
103 bcm54xx_parse_status(phydev);
108 /* Broadcom BCM5482S */
110 * "Ethernet@Wirespeed" needs to be enabled to achieve link in certain
111 * circumstances. eg a gigabit TSEC connected to a gigabit switch with
112 * a 4-wire ethernet cable. Both ends advertise gigabit, but can't
113 * link. "Ethernet@Wirespeed" reduces advertised speed until link
116 static u32 bcm5482_read_wirespeed(struct phy_device *phydev, u32 reg)
118 return (phy_read(phydev, MDIO_DEVAD_NONE, reg) & 0x8FFF) | 0x8010;
121 static int bcm5482_config(struct phy_device *phydev)
126 reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
128 phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, reg);
130 /* Setup read from auxilary control shadow register 7 */
131 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL,
132 MIIM_BCM54xx_AUXCNTL_ENCODE(7));
133 /* Read Misc Control register and or in Ethernet@Wirespeed */
134 reg = bcm5482_read_wirespeed(phydev, MIIM_BCM54xx_AUXCNTL);
135 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL, reg);
137 /* Initial config/enable of secondary SerDes interface */
138 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_SHD,
139 MIIM_BCM54XX_SHD_WR_ENCODE(0x14, 0xf));
140 /* Write intial value to secondary SerDes Contol */
141 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_SEL,
142 MIIM_BCM54XX_EXP_SEL_SSD | 0);
143 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_DATA,
145 /* Enable copper/fiber auto-detect */
146 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_SHD,
147 MIIM_BCM54XX_SHD_WR_ENCODE(0x1e, 0x201));
149 genphy_config_aneg(phydev);
155 * Find out if PHY is in copper or serdes mode by looking at Expansion Reg
156 * 0x42 - "Operating Mode Status Register"
158 static int bcm5482_is_serdes(struct phy_device *phydev)
163 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_SEL,
164 MIIM_BCM54XX_EXP_SEL_ER | 0x42);
165 val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_DATA);
167 switch (val & 0x1f) {
168 case 0x0d: /* RGMII-to-100Base-FX */
169 case 0x0e: /* RGMII-to-SGMII */
170 case 0x0f: /* RGMII-to-SerDes */
171 case 0x12: /* SGMII-to-SerDes */
172 case 0x13: /* SGMII-to-100Base-FX */
173 case 0x16: /* SerDes-to-Serdes */
176 case 0x6: /* RGMII-to-Copper */
177 case 0x14: /* SGMII-to-Copper */
178 case 0x17: /* SerDes-to-Copper */
181 printf("ERROR, invalid PHY mode (0x%x\n)", val);
189 * Determine SerDes link speed and duplex from Expansion reg 0x42 "Operating
190 * Mode Status Register"
192 static u32 bcm5482_parse_serdes_sr(struct phy_device *phydev)
197 /* Wait 1s for link - Clause 37 autonegotiation happens very fast */
199 phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_SEL,
200 MIIM_BCM54XX_EXP_SEL_ER | 0x42);
201 val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_DATA);
211 udelay(1000); /* 1 ms */
215 switch ((val >> 13) & 0x3) {
223 phydev->speed = 1000;
227 phydev->duplex = (val & 0x1000) == 0x1000;
233 * Figure out if BCM5482 is in serdes or copper mode and determine link
234 * configuration accordingly
236 static int bcm5482_startup(struct phy_device *phydev)
238 if (bcm5482_is_serdes(phydev)) {
239 bcm5482_parse_serdes_sr(phydev);
240 phydev->port = PORT_FIBRE;
242 /* Wait for auto-negotiation to complete or fail */
243 genphy_update_link(phydev);
244 /* Parse BCM54xx copper aux status register */
245 bcm54xx_parse_status(phydev);
251 static struct phy_driver BCM5461S_driver = {
252 .name = "Broadcom BCM5461S",
255 .features = PHY_GBIT_FEATURES,
256 .config = &bcm5461_config,
257 .startup = &bcm54xx_startup,
258 .shutdown = &genphy_shutdown,
261 static struct phy_driver BCM5464S_driver = {
262 .name = "Broadcom BCM5464S",
265 .features = PHY_GBIT_FEATURES,
266 .config = &bcm5461_config,
267 .startup = &bcm54xx_startup,
268 .shutdown = &genphy_shutdown,
271 static struct phy_driver BCM5482S_driver = {
272 .name = "Broadcom BCM5482S",
275 .features = PHY_GBIT_FEATURES,
276 .config = &bcm5482_config,
277 .startup = &bcm5482_startup,
278 .shutdown = &genphy_shutdown,
281 int phy_broadcom_init(void)
283 phy_register(&BCM5482S_driver);
284 phy_register(&BCM5464S_driver);
285 phy_register(&BCM5461S_driver);