]>
Commit | Line | Data |
---|---|---|
affae2bf | 1 | /*-----------------------------------------------------------------------------+ |
31773496 JB |
2 | | This source code is dual-licensed. You may use it under the terms of the |
3 | | GNU General Public License version 2, or under the license below. | |
affae2bf | 4 | | |
d6c61aab SR |
5 | | This source code has been made available to you by IBM on an AS-IS |
6 | | basis. Anyone receiving this source is licensed under IBM | |
7 | | copyrights to use it in any way he or she deems fit, including | |
8 | | copying it, modifying it, compiling it, and redistributing it either | |
9 | | with or without modifications. No license under IBM patents or | |
10 | | patent applications is to be implied by the copyright license. | |
affae2bf | 11 | | |
d6c61aab SR |
12 | | Any user of this software should understand that IBM cannot provide |
13 | | technical support for this software and will not be responsible for | |
14 | | any consequences resulting from the use of this software. | |
affae2bf | 15 | | |
d6c61aab SR |
16 | | Any person who transfers this source code or any derivative work |
17 | | must include the IBM copyright notice, this paragraph, and the | |
18 | | preceding two paragraphs in the transferred software. | |
affae2bf | 19 | | |
d6c61aab SR |
20 | | COPYRIGHT I B M CORPORATION 1995 |
21 | | LICENSED MATERIAL - PROGRAM PROPERTY OF I B M | |
affae2bf WD |
22 | +-----------------------------------------------------------------------------*/ |
23 | /*-----------------------------------------------------------------------------+ | |
24 | | | |
d6c61aab | 25 | | File Name: miiphy.c |
affae2bf | 26 | | |
d6c61aab | 27 | | Function: This module has utilities for accessing the MII PHY through |
affae2bf WD |
28 | | the EMAC3 macro. |
29 | | | |
d6c61aab | 30 | | Author: Mark Wisner |
affae2bf | 31 | | |
affae2bf WD |
32 | +-----------------------------------------------------------------------------*/ |
33 | ||
c3307fa1 SR |
34 | /* define DEBUG for debugging output (obviously ;-)) */ |
35 | #if 0 | |
36 | #define DEBUG | |
37 | #endif | |
38 | ||
affae2bf WD |
39 | #include <common.h> |
40 | #include <asm/processor.h> | |
2d83476a | 41 | #include <asm/io.h> |
affae2bf WD |
42 | #include <ppc_asm.tmpl> |
43 | #include <commproc.h> | |
d6c61aab | 44 | #include <ppc4xx_enet.h> |
affae2bf WD |
45 | #include <405_mal.h> |
46 | #include <miiphy.h> | |
47 | ||
c3307fa1 SR |
48 | #if !defined(CONFIG_PHY_CLK_FREQ) |
49 | #define CONFIG_PHY_CLK_FREQ 0 | |
50 | #endif | |
51 | ||
affae2bf | 52 | /***********************************************************/ |
d6c61aab | 53 | /* Dump out to the screen PHY regs */ |
affae2bf WD |
54 | /***********************************************************/ |
55 | ||
63ff004c | 56 | void miiphy_dump (char *devname, unsigned char addr) |
affae2bf WD |
57 | { |
58 | unsigned long i; | |
59 | unsigned short data; | |
60 | ||
affae2bf | 61 | for (i = 0; i < 0x1A; i++) { |
63ff004c | 62 | if (miiphy_read (devname, addr, i, &data)) { |
affae2bf WD |
63 | printf ("read error for reg %lx\n", i); |
64 | return; | |
65 | } | |
66 | printf ("Phy reg %lx ==> %4x\n", i, data); | |
67 | ||
68 | /* jump to the next set of regs */ | |
69 | if (i == 0x07) | |
70 | i = 0x0f; | |
71 | ||
d6c61aab SR |
72 | } /* end for loop */ |
73 | } /* end dump */ | |
affae2bf | 74 | |
affae2bf | 75 | /***********************************************************/ |
d6c61aab | 76 | /* (Re)start autonegotiation */ |
affae2bf | 77 | /***********************************************************/ |
63ff004c | 78 | int phy_setup_aneg (char *devname, unsigned char addr) |
d6c61aab | 79 | { |
c348578b LJ |
80 | u16 bmcr; |
81 | ||
82 | #if defined(CONFIG_PHY_DYNAMIC_ANEG) | |
83 | /* | |
84 | * Set up advertisement based on capablilities reported by the PHY. | |
85 | * This should work for both copper and fiber. | |
86 | */ | |
87 | u16 bmsr; | |
88 | #if defined(CONFIG_PHY_GIGE) | |
89 | u16 exsr = 0x0000; | |
90 | #endif | |
91 | ||
92 | miiphy_read (devname, addr, PHY_BMSR, &bmsr); | |
93 | ||
94 | #if defined(CONFIG_PHY_GIGE) | |
95 | if (bmsr & PHY_BMSR_EXT_STAT) | |
96 | miiphy_read (devname, addr, PHY_EXSR, &exsr); | |
97 | ||
98 | if (exsr & (PHY_EXSR_1000XF | PHY_EXSR_1000XH)) { | |
99 | /* 1000BASE-X */ | |
100 | u16 anar = 0x0000; | |
101 | ||
102 | if (exsr & PHY_EXSR_1000XF) | |
103 | anar |= PHY_X_ANLPAR_FD; | |
104 | ||
105 | if (exsr & PHY_EXSR_1000XH) | |
106 | anar |= PHY_X_ANLPAR_HD; | |
107 | ||
108 | miiphy_write (devname, addr, PHY_ANAR, anar); | |
109 | } else | |
110 | #endif | |
111 | { | |
112 | u16 anar, btcr; | |
113 | ||
114 | miiphy_read (devname, addr, PHY_ANAR, &anar); | |
115 | anar &= ~(0x5000 | PHY_ANLPAR_T4 | PHY_ANLPAR_TXFD | | |
116 | PHY_ANLPAR_TX | PHY_ANLPAR_10FD | PHY_ANLPAR_10); | |
117 | ||
118 | miiphy_read (devname, addr, PHY_1000BTCR, &btcr); | |
119 | btcr &= ~(0x00FF | PHY_1000BTCR_1000FD | PHY_1000BTCR_1000HD); | |
120 | ||
121 | if (bmsr & PHY_BMSR_100T4) | |
122 | anar |= PHY_ANLPAR_T4; | |
123 | ||
124 | if (bmsr & PHY_BMSR_100TXF) | |
125 | anar |= PHY_ANLPAR_TXFD; | |
126 | ||
127 | if (bmsr & PHY_BMSR_100TXH) | |
128 | anar |= PHY_ANLPAR_TX; | |
129 | ||
130 | if (bmsr & PHY_BMSR_10TF) | |
131 | anar |= PHY_ANLPAR_10FD; | |
132 | ||
133 | if (bmsr & PHY_BMSR_10TH) | |
134 | anar |= PHY_ANLPAR_10; | |
135 | ||
136 | miiphy_write (devname, addr, PHY_ANAR, anar); | |
137 | ||
138 | #if defined(CONFIG_PHY_GIGE) | |
139 | if (exsr & PHY_EXSR_1000TF) | |
140 | btcr |= PHY_1000BTCR_1000FD; | |
141 | ||
142 | if (exsr & PHY_EXSR_1000TH) | |
143 | btcr |= PHY_1000BTCR_1000HD; | |
144 | ||
145 | miiphy_write (devname, addr, PHY_1000BTCR, btcr); | |
146 | #endif | |
147 | } | |
148 | ||
149 | #else /* defined(CONFIG_PHY_DYNAMIC_ANEG) */ | |
150 | /* | |
151 | * Set up standard advertisement | |
152 | */ | |
153 | u16 adv; | |
d6c61aab | 154 | |
63ff004c | 155 | miiphy_read (devname, addr, PHY_ANAR, &adv); |
74eb0222 MN |
156 | adv |= (PHY_ANLPAR_ACK | PHY_ANLPAR_TXFD | PHY_ANLPAR_TX | |
157 | PHY_ANLPAR_10FD | PHY_ANLPAR_10); | |
63ff004c | 158 | miiphy_write (devname, addr, PHY_ANAR, adv); |
affae2bf | 159 | |
6c5879f3 MB |
160 | miiphy_read (devname, addr, PHY_1000BTCR, &adv); |
161 | adv |= (0x0300); | |
162 | miiphy_write (devname, addr, PHY_1000BTCR, adv); | |
163 | ||
c348578b LJ |
164 | #endif /* defined(CONFIG_PHY_DYNAMIC_ANEG) */ |
165 | ||
d6c61aab | 166 | /* Start/Restart aneg */ |
c348578b LJ |
167 | miiphy_read (devname, addr, PHY_BMCR, &bmcr); |
168 | bmcr |= (PHY_BMCR_AUTON | PHY_BMCR_RST_NEG); | |
169 | miiphy_write (devname, addr, PHY_BMCR, bmcr); | |
d6c61aab SR |
170 | |
171 | return 0; | |
172 | } | |
173 | ||
d6c61aab SR |
174 | /***********************************************************/ |
175 | /* read a phy reg and return the value with a rc */ | |
176 | /***********************************************************/ | |
c3307fa1 SR |
177 | /* AMCC_TODO: |
178 | * Find out of the choice for the emac for MDIO is from the bridges, | |
179 | * i.e. ZMII or RGMII as approporiate. If the bridges are not used | |
180 | * to determine the emac for MDIO, then is the SDR0_ETH_CFG[MDIO_SEL] | |
181 | * used? If so, then this routine below does not apply to the 460EX/GT. | |
182 | * | |
183 | * sr: Currently on 460EX only EMAC0 works with MDIO, so we always | |
184 | * return EMAC0 offset here | |
78d78236 VG |
185 | * vg: For 460EX/460GT if internal GPCS PHY address is specified |
186 | * return appropriate EMAC offset | |
c3307fa1 | 187 | */ |
78d78236 | 188 | unsigned int miiphy_getemac_offset(u8 addr) |
affae2bf | 189 | { |
c3307fa1 SR |
190 | #if (defined(CONFIG_440) && \ |
191 | !defined(CONFIG_440SP) && !defined(CONFIG_440SPE) && \ | |
192 | !defined(CONFIG_460EX) && !defined(CONFIG_460GT)) && \ | |
193 | defined(CONFIG_NET_MULTI) | |
d6c61aab SR |
194 | unsigned long zmii; |
195 | unsigned long eoffset; | |
196 | ||
197 | /* Need to find out which mdi port we're using */ | |
ddc922ff | 198 | zmii = in_be32((void *)ZMII0_FER); |
d6c61aab | 199 | |
c348578b | 200 | if (zmii & (ZMII_FER_MDI << ZMII_FER_V (0))) |
d6c61aab SR |
201 | /* using port 0 */ |
202 | eoffset = 0; | |
c348578b LJ |
203 | |
204 | else if (zmii & (ZMII_FER_MDI << ZMII_FER_V (1))) | |
d6c61aab SR |
205 | /* using port 1 */ |
206 | eoffset = 0x100; | |
c348578b LJ |
207 | |
208 | else if (zmii & (ZMII_FER_MDI << ZMII_FER_V (2))) | |
d6c61aab SR |
209 | /* using port 2 */ |
210 | eoffset = 0x400; | |
c348578b LJ |
211 | |
212 | else if (zmii & (ZMII_FER_MDI << ZMII_FER_V (3))) | |
d6c61aab SR |
213 | /* using port 3 */ |
214 | eoffset = 0x600; | |
c348578b LJ |
215 | |
216 | else { | |
d6c61aab SR |
217 | /* None of the mdi ports are enabled! */ |
218 | /* enable port 0 */ | |
219 | zmii |= ZMII_FER_MDI << ZMII_FER_V (0); | |
ddc922ff | 220 | out_be32((void *)ZMII0_FER, zmii); |
d6c61aab SR |
221 | eoffset = 0; |
222 | /* need to soft reset port 0 */ | |
ddc922ff NG |
223 | zmii = in_be32((void *)EMAC0_MR0); |
224 | zmii |= EMAC_MR0_SRST; | |
225 | out_be32((void *)EMAC0_MR0, zmii); | |
d6c61aab SR |
226 | } |
227 | ||
228 | return (eoffset); | |
229 | #else | |
dbbd1257 SR |
230 | |
231 | #if defined(CONFIG_NET_MULTI) && defined(CONFIG_405EX) | |
232 | unsigned long rgmii; | |
233 | int devnum = 1; | |
234 | ||
2d83476a | 235 | rgmii = in_be32((void *)RGMII_FER); |
dbbd1257 SR |
236 | if (rgmii & (1 << (19 - devnum))) |
237 | return 0x100; | |
238 | #endif | |
239 | ||
78d78236 | 240 | #if defined(CONFIG_460EX) || defined(CONFIG_460GT) |
78d78236 VG |
241 | u32 eoffset = 0; |
242 | ||
243 | switch (addr) { | |
244 | #if defined(CONFIG_HAS_ETH1) && defined(CONFIG_GPCS_PHY1_ADDR) | |
245 | case CONFIG_GPCS_PHY1_ADDR: | |
ddc922ff | 246 | if (addr == EMAC_MR1_IPPA_GET(in_be32((void *)EMAC0_MR1 + 0x100))) |
78d78236 VG |
247 | eoffset = 0x100; |
248 | break; | |
249 | #endif | |
250 | #if defined(CONFIG_HAS_ETH2) && defined(CONFIG_GPCS_PHY2_ADDR) | |
251 | case CONFIG_GPCS_PHY2_ADDR: | |
ddc922ff | 252 | if (addr == EMAC_MR1_IPPA_GET(in_be32((void *)EMAC0_MR1 + 0x300))) |
78d78236 VG |
253 | eoffset = 0x300; |
254 | break; | |
255 | #endif | |
256 | #if defined(CONFIG_HAS_ETH3) && defined(CONFIG_GPCS_PHY3_ADDR) | |
257 | case CONFIG_GPCS_PHY3_ADDR: | |
ddc922ff | 258 | if (addr == EMAC_MR1_IPPA_GET(in_be32((void *)EMAC0_MR1 + 0x400))) |
78d78236 VG |
259 | eoffset = 0x400; |
260 | break; | |
261 | #endif | |
262 | default: | |
263 | eoffset = 0; | |
264 | break; | |
265 | } | |
266 | return eoffset; | |
267 | #endif | |
268 | ||
d6c61aab SR |
269 | return 0; |
270 | #endif | |
271 | } | |
272 | ||
c3307fa1 | 273 | static int emac_miiphy_wait(u32 emac_reg) |
d6c61aab | 274 | { |
c3307fa1 SR |
275 | u32 sta_reg; |
276 | int i; | |
d6c61aab | 277 | |
c3307fa1 | 278 | /* wait for completion */ |
affae2bf | 279 | i = 0; |
c3307fa1 | 280 | do { |
ddc922ff | 281 | sta_reg = in_be32((void *)EMAC0_STACR + emac_reg); |
c3307fa1 | 282 | if (i++ > 5) { |
ddc922ff | 283 | debug("%s [%d]: Timeout! EMAC0_STACR=0x%0x\n", __func__, |
c3307fa1 | 284 | __LINE__, sta_reg); |
affae2bf WD |
285 | return -1; |
286 | } | |
c3307fa1 SR |
287 | udelay(10); |
288 | } while ((sta_reg & EMAC_STACR_OC) == EMAC_STACR_OC_MASK); | |
289 | ||
290 | return 0; | |
291 | } | |
292 | ||
293 | static int emac_miiphy_command(u8 addr, u8 reg, int cmd, u16 value) | |
294 | { | |
295 | u32 emac_reg; | |
296 | u32 sta_reg; | |
297 | ||
78d78236 | 298 | emac_reg = miiphy_getemac_offset(addr); |
c3307fa1 SR |
299 | |
300 | /* wait for completion */ | |
301 | if (emac_miiphy_wait(emac_reg) != 0) | |
302 | return -1; | |
303 | ||
d6c61aab | 304 | sta_reg = reg; /* reg address */ |
c3307fa1 | 305 | |
8ed44d91 | 306 | /* set clock (50MHz) and read flags */ |
887e2ec9 | 307 | #if defined(CONFIG_440GX) || defined(CONFIG_440SPE) || \ |
dbbd1257 | 308 | defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ |
c3307fa1 | 309 | defined(CONFIG_460EX) || defined(CONFIG_460GT) || \ |
dbbd1257 | 310 | defined(CONFIG_405EX) |
c348578b | 311 | #if defined(CONFIG_IBM_EMAC4_V4) /* EMAC4 V4 changed bit setting */ |
c3307fa1 | 312 | sta_reg = (sta_reg & ~EMAC_STACR_OP_MASK) | cmd; |
6c5879f3 | 313 | #else |
c3307fa1 | 314 | sta_reg |= cmd; |
6c5879f3 | 315 | #endif |
d6c61aab | 316 | #else |
c3307fa1 | 317 | sta_reg = (sta_reg | cmd) & ~EMAC_STACR_CLK_100MHZ; |
d6c61aab SR |
318 | #endif |
319 | ||
c3307fa1 | 320 | /* Some boards (mainly 405EP based) define the PHY clock freqency fixed */ |
12f34241 | 321 | sta_reg = sta_reg | CONFIG_PHY_CLK_FREQ; |
c3307fa1 | 322 | sta_reg = sta_reg | ((u32)addr << 5); /* Phy address */ |
6c5879f3 | 323 | sta_reg = sta_reg | EMAC_STACR_OC_MASK; /* new IBM emac v4 */ |
c3307fa1 SR |
324 | if (cmd == EMAC_STACR_WRITE) |
325 | memcpy(&sta_reg, &value, 2); /* put in data */ | |
326 | ||
ddc922ff | 327 | out_be32((void *)EMAC0_STACR + emac_reg, sta_reg); |
c3307fa1 | 328 | debug("%s [%d]: sta_reg=%08x\n", __func__, __LINE__, sta_reg); |
affae2bf | 329 | |
c3307fa1 SR |
330 | /* wait for completion */ |
331 | if (emac_miiphy_wait(emac_reg) != 0) | |
332 | return -1; | |
c348578b | 333 | |
c3307fa1 | 334 | debug("%s [%d]: sta_reg=%08x\n", __func__, __LINE__, sta_reg); |
c348578b | 335 | if ((sta_reg & EMAC_STACR_PHYE) != 0) |
affae2bf | 336 | return -1; |
affae2bf | 337 | |
affae2bf | 338 | return 0; |
c3307fa1 | 339 | } |
affae2bf | 340 | |
c3307fa1 SR |
341 | int emac4xx_miiphy_read (char *devname, unsigned char addr, unsigned char reg, |
342 | unsigned short *value) | |
affae2bf | 343 | { |
c3307fa1 | 344 | unsigned long sta_reg; |
d6c61aab | 345 | unsigned long emac_reg; |
affae2bf | 346 | |
78d78236 | 347 | emac_reg = miiphy_getemac_offset(addr); |
affae2bf | 348 | |
c3307fa1 SR |
349 | if (emac_miiphy_command(addr, reg, EMAC_STACR_READ, 0) != 0) |
350 | return -1; | |
affae2bf | 351 | |
ddc922ff | 352 | sta_reg = in_be32((void *)EMAC0_STACR + emac_reg); |
0b34dbbd | 353 | *value = sta_reg >> 16; |
c348578b | 354 | |
affae2bf | 355 | return 0; |
c3307fa1 | 356 | } |
affae2bf | 357 | |
c3307fa1 SR |
358 | /***********************************************************/ |
359 | /* write a phy reg and return the value with a rc */ | |
360 | /***********************************************************/ | |
361 | ||
362 | int emac4xx_miiphy_write (char *devname, unsigned char addr, unsigned char reg, | |
363 | unsigned short value) | |
364 | { | |
365 | return emac_miiphy_command(addr, reg, EMAC_STACR_WRITE, value); | |
366 | } |