]> git.sur5r.net Git - u-boot/blob - drivers/net/phy/broadcom.c
mxc_ipuv3: fix memory alignment of framebuffer
[u-boot] / drivers / net / phy / broadcom.c
1 /*
2  * Broadcom PHY drivers
3  *
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.
8  *
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.
13  *
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,
17  * MA 02111-1307 USA
18  *
19  * Copyright 2010-2011 Freescale Semiconductor, Inc.
20  * author Andy Fleming
21  *
22  */
23 #include <config.h>
24 #include <common.h>
25 #include <phy.h>
26
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
33
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))
41
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 */
46
47 /* Broadcom BCM5461S */
48 static int bcm5461_config(struct phy_device *phydev)
49 {
50         genphy_config_aneg(phydev);
51
52         phy_reset(phydev);
53
54         return 0;
55 }
56
57 static int bcm54xx_parse_status(struct phy_device *phydev)
58 {
59         unsigned int mii_reg;
60
61         mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXSTATUS);
62
63         switch ((mii_reg & MIIM_BCM54xx_AUXSTATUS_LINKMODE_MASK) >>
64                         MIIM_BCM54xx_AUXSTATUS_LINKMODE_SHIFT) {
65         case 1:
66                 phydev->duplex = DUPLEX_HALF;
67                 phydev->speed = SPEED_10;
68                 break;
69         case 2:
70                 phydev->duplex = DUPLEX_FULL;
71                 phydev->speed = SPEED_10;
72                 break;
73         case 3:
74                 phydev->duplex = DUPLEX_HALF;
75                 phydev->speed = SPEED_100;
76                 break;
77         case 5:
78                 phydev->duplex = DUPLEX_FULL;
79                 phydev->speed = SPEED_100;
80                 break;
81         case 6:
82                 phydev->duplex = DUPLEX_HALF;
83                 phydev->speed = SPEED_1000;
84                 break;
85         case 7:
86                 phydev->duplex = DUPLEX_FULL;
87                 phydev->speed = SPEED_1000;
88                 break;
89         default:
90                 printf("Auto-neg error, defaulting to 10BT/HD\n");
91                 phydev->duplex = DUPLEX_HALF;
92                 phydev->speed = SPEED_10;
93                 break;
94         }
95
96         return 0;
97 }
98
99 static int bcm54xx_startup(struct phy_device *phydev)
100 {
101         /* Read the Status (2x to make sure link is right) */
102         genphy_update_link(phydev);
103         bcm54xx_parse_status(phydev);
104
105         return 0;
106 }
107
108 /* Broadcom BCM5482S */
109 /*
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
114  * can be achieved.
115  */
116 static u32 bcm5482_read_wirespeed(struct phy_device *phydev, u32 reg)
117 {
118         return (phy_read(phydev, MDIO_DEVAD_NONE, reg) & 0x8FFF) | 0x8010;
119 }
120
121 static int bcm5482_config(struct phy_device *phydev)
122 {
123         unsigned int reg;
124
125         /* reset the PHY */
126         reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
127         reg |= BMCR_RESET;
128         phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, reg);
129
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);
136
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,
144                         BMCR_ANRESTART);
145         /* Enable copper/fiber auto-detect */
146         phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_SHD,
147                         MIIM_BCM54XX_SHD_WR_ENCODE(0x1e, 0x201));
148
149         genphy_config_aneg(phydev);
150
151         return 0;
152 }
153
154 /*
155  * Find out if PHY is in copper or serdes mode by looking at Expansion Reg
156  * 0x42 - "Operating Mode Status Register"
157  */
158 static int bcm5482_is_serdes(struct phy_device *phydev)
159 {
160         u16 val;
161         int serdes = 0;
162
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);
166
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 */
174                 serdes = 1;
175                 break;
176         case 0x6:       /* RGMII-to-Copper */
177         case 0x14:      /* SGMII-to-Copper */
178         case 0x17:      /* SerDes-to-Copper */
179                 break;
180         default:
181                 printf("ERROR, invalid PHY mode (0x%x\n)", val);
182                 break;
183         }
184
185         return serdes;
186 }
187
188 /*
189  * Determine SerDes link speed and duplex from Expansion reg 0x42 "Operating
190  * Mode Status Register"
191  */
192 static u32 bcm5482_parse_serdes_sr(struct phy_device *phydev)
193 {
194         u16 val;
195         int i = 0;
196
197         /* Wait 1s for link - Clause 37 autonegotiation happens very fast */
198         while (1) {
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);
202
203                 if (val & 0x8000)
204                         break;
205
206                 if (i++ > 1000) {
207                         phydev->link = 0;
208                         return 1;
209                 }
210
211                 udelay(1000);   /* 1 ms */
212         }
213
214         phydev->link = 1;
215         switch ((val >> 13) & 0x3) {
216         case (0x00):
217                 phydev->speed = 10;
218                 break;
219         case (0x01):
220                 phydev->speed = 100;
221                 break;
222         case (0x02):
223                 phydev->speed = 1000;
224                 break;
225         }
226
227         phydev->duplex = (val & 0x1000) == 0x1000;
228
229         return 0;
230 }
231
232 /*
233  * Figure out if BCM5482 is in serdes or copper mode and determine link
234  * configuration accordingly
235  */
236 static int bcm5482_startup(struct phy_device *phydev)
237 {
238         if (bcm5482_is_serdes(phydev)) {
239                 bcm5482_parse_serdes_sr(phydev);
240                 phydev->port = PORT_FIBRE;
241         } else {
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);
246         }
247
248         return 0;
249 }
250
251 static struct phy_driver BCM5461S_driver = {
252         .name = "Broadcom BCM5461S",
253         .uid = 0x2060c0,
254         .mask = 0xfffff0,
255         .features = PHY_GBIT_FEATURES,
256         .config = &bcm5461_config,
257         .startup = &bcm54xx_startup,
258         .shutdown = &genphy_shutdown,
259 };
260
261 static struct phy_driver BCM5464S_driver = {
262         .name = "Broadcom BCM5464S",
263         .uid = 0x2060b0,
264         .mask = 0xfffff0,
265         .features = PHY_GBIT_FEATURES,
266         .config = &bcm5461_config,
267         .startup = &bcm54xx_startup,
268         .shutdown = &genphy_shutdown,
269 };
270
271 static struct phy_driver BCM5482S_driver = {
272         .name = "Broadcom BCM5482S",
273         .uid = 0x143bcb0,
274         .mask = 0xffffff0,
275         .features = PHY_GBIT_FEATURES,
276         .config = &bcm5482_config,
277         .startup = &bcm5482_startup,
278         .shutdown = &genphy_shutdown,
279 };
280
281 int phy_broadcom_init(void)
282 {
283         phy_register(&BCM5482S_driver);
284         phy_register(&BCM5464S_driver);
285         phy_register(&BCM5461S_driver);
286
287         return 0;
288 }