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