]>
Commit | Line | Data |
---|---|---|
46263f2d | 1 | /* |
1b387ef5 | 2 | * SPDX-License-Identifier: GPL-2.0 IBM-pibs |
46263f2d | 3 | */ |
affae2bf WD |
4 | /*-----------------------------------------------------------------------------+ |
5 | | | |
d6c61aab | 6 | | File Name: miiphy.c |
affae2bf | 7 | | |
d6c61aab | 8 | | Function: This module has utilities for accessing the MII PHY through |
affae2bf WD |
9 | | the EMAC3 macro. |
10 | | | |
d6c61aab | 11 | | Author: Mark Wisner |
affae2bf | 12 | | |
affae2bf WD |
13 | +-----------------------------------------------------------------------------*/ |
14 | ||
c3307fa1 SR |
15 | /* define DEBUG for debugging output (obviously ;-)) */ |
16 | #if 0 | |
17 | #define DEBUG | |
18 | #endif | |
19 | ||
affae2bf WD |
20 | #include <common.h> |
21 | #include <asm/processor.h> | |
2d83476a | 22 | #include <asm/io.h> |
affae2bf | 23 | #include <ppc_asm.tmpl> |
85043159 | 24 | #include <asm/ppc4xx.h> |
b36df561 SR |
25 | #include <asm/ppc4xx-emac.h> |
26 | #include <asm/ppc4xx-mal.h> | |
affae2bf WD |
27 | #include <miiphy.h> |
28 | ||
c3307fa1 SR |
29 | #if !defined(CONFIG_PHY_CLK_FREQ) |
30 | #define CONFIG_PHY_CLK_FREQ 0 | |
31 | #endif | |
32 | ||
affae2bf | 33 | /***********************************************************/ |
d6c61aab | 34 | /* Dump out to the screen PHY regs */ |
affae2bf WD |
35 | /***********************************************************/ |
36 | ||
63ff004c | 37 | void miiphy_dump (char *devname, unsigned char addr) |
affae2bf WD |
38 | { |
39 | unsigned long i; | |
40 | unsigned short data; | |
41 | ||
affae2bf | 42 | for (i = 0; i < 0x1A; i++) { |
63ff004c | 43 | if (miiphy_read (devname, addr, i, &data)) { |
affae2bf WD |
44 | printf ("read error for reg %lx\n", i); |
45 | return; | |
46 | } | |
47 | printf ("Phy reg %lx ==> %4x\n", i, data); | |
48 | ||
49 | /* jump to the next set of regs */ | |
50 | if (i == 0x07) | |
51 | i = 0x0f; | |
52 | ||
d6c61aab SR |
53 | } /* end for loop */ |
54 | } /* end dump */ | |
affae2bf | 55 | |
affae2bf | 56 | /***********************************************************/ |
d6c61aab | 57 | /* (Re)start autonegotiation */ |
affae2bf | 58 | /***********************************************************/ |
63ff004c | 59 | int phy_setup_aneg (char *devname, unsigned char addr) |
d6c61aab | 60 | { |
c348578b LJ |
61 | u16 bmcr; |
62 | ||
63 | #if defined(CONFIG_PHY_DYNAMIC_ANEG) | |
64 | /* | |
65 | * Set up advertisement based on capablilities reported by the PHY. | |
66 | * This should work for both copper and fiber. | |
67 | */ | |
68 | u16 bmsr; | |
69 | #if defined(CONFIG_PHY_GIGE) | |
70 | u16 exsr = 0x0000; | |
71 | #endif | |
72 | ||
8ef583a0 | 73 | miiphy_read (devname, addr, MII_BMSR, &bmsr); |
c348578b LJ |
74 | |
75 | #if defined(CONFIG_PHY_GIGE) | |
8ef583a0 MF |
76 | if (bmsr & BMSR_ESTATEN) |
77 | miiphy_read (devname, addr, MII_ESTATUS, &exsr); | |
c348578b | 78 | |
8ef583a0 | 79 | if (exsr & (ESTATUS_1000XF | ESTATUS_1000XH)) { |
c348578b LJ |
80 | /* 1000BASE-X */ |
81 | u16 anar = 0x0000; | |
82 | ||
8ef583a0 | 83 | if (exsr & ESTATUS_1000XF) |
c722c708 | 84 | anar |= ADVERTISE_1000XFULL; |
c348578b | 85 | |
8ef583a0 MF |
86 | if (exsr & ESTATUS_1000XH) |
87 | anar |= ADVERTISE_1000XHALF; | |
c348578b | 88 | |
8ef583a0 | 89 | miiphy_write (devname, addr, MII_ADVERTISE, anar); |
c348578b LJ |
90 | } else |
91 | #endif | |
92 | { | |
93 | u16 anar, btcr; | |
94 | ||
8ef583a0 MF |
95 | miiphy_read (devname, addr, MII_ADVERTISE, &anar); |
96 | anar &= ~(0x5000 | LPA_100BASE4 | LPA_100FULL | | |
97 | LPA_100HALF | LPA_10FULL | LPA_10HALF); | |
c348578b | 98 | |
8ef583a0 | 99 | miiphy_read (devname, addr, MII_CTRL1000, &btcr); |
c348578b LJ |
100 | btcr &= ~(0x00FF | PHY_1000BTCR_1000FD | PHY_1000BTCR_1000HD); |
101 | ||
8ef583a0 MF |
102 | if (bmsr & BMSR_100BASE4) |
103 | anar |= LPA_100BASE4; | |
c348578b | 104 | |
8ef583a0 MF |
105 | if (bmsr & BMSR_100FULL) |
106 | anar |= LPA_100FULL; | |
c348578b | 107 | |
8ef583a0 MF |
108 | if (bmsr & BMSR_100HALF) |
109 | anar |= LPA_100HALF; | |
c348578b | 110 | |
8ef583a0 MF |
111 | if (bmsr & BMSR_10FULL) |
112 | anar |= LPA_10FULL; | |
c348578b | 113 | |
8ef583a0 MF |
114 | if (bmsr & BMSR_10HALF) |
115 | anar |= LPA_10HALF; | |
c348578b | 116 | |
8ef583a0 | 117 | miiphy_write (devname, addr, MII_ADVERTISE, anar); |
c348578b LJ |
118 | |
119 | #if defined(CONFIG_PHY_GIGE) | |
8ef583a0 | 120 | if (exsr & ESTATUS_1000_TFULL) |
c348578b LJ |
121 | btcr |= PHY_1000BTCR_1000FD; |
122 | ||
8ef583a0 | 123 | if (exsr & ESTATUS_1000_THALF) |
c348578b LJ |
124 | btcr |= PHY_1000BTCR_1000HD; |
125 | ||
8ef583a0 | 126 | miiphy_write (devname, addr, MII_CTRL1000, btcr); |
c348578b LJ |
127 | #endif |
128 | } | |
129 | ||
130 | #else /* defined(CONFIG_PHY_DYNAMIC_ANEG) */ | |
131 | /* | |
132 | * Set up standard advertisement | |
133 | */ | |
134 | u16 adv; | |
d6c61aab | 135 | |
8ef583a0 MF |
136 | miiphy_read (devname, addr, MII_ADVERTISE, &adv); |
137 | adv |= (LPA_LPACK | LPA_100FULL | LPA_100HALF | | |
138 | LPA_10FULL | LPA_10HALF); | |
139 | miiphy_write (devname, addr, MII_ADVERTISE, adv); | |
affae2bf | 140 | |
8ef583a0 | 141 | miiphy_read (devname, addr, MII_CTRL1000, &adv); |
6c5879f3 | 142 | adv |= (0x0300); |
8ef583a0 | 143 | miiphy_write (devname, addr, MII_CTRL1000, adv); |
6c5879f3 | 144 | |
c348578b LJ |
145 | #endif /* defined(CONFIG_PHY_DYNAMIC_ANEG) */ |
146 | ||
d6c61aab | 147 | /* Start/Restart aneg */ |
8ef583a0 MF |
148 | miiphy_read (devname, addr, MII_BMCR, &bmcr); |
149 | bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART); | |
150 | miiphy_write (devname, addr, MII_BMCR, bmcr); | |
d6c61aab SR |
151 | |
152 | return 0; | |
153 | } | |
154 | ||
d6c61aab SR |
155 | /***********************************************************/ |
156 | /* read a phy reg and return the value with a rc */ | |
157 | /***********************************************************/ | |
c3307fa1 SR |
158 | /* AMCC_TODO: |
159 | * Find out of the choice for the emac for MDIO is from the bridges, | |
160 | * i.e. ZMII or RGMII as approporiate. If the bridges are not used | |
161 | * to determine the emac for MDIO, then is the SDR0_ETH_CFG[MDIO_SEL] | |
162 | * used? If so, then this routine below does not apply to the 460EX/GT. | |
163 | * | |
164 | * sr: Currently on 460EX only EMAC0 works with MDIO, so we always | |
165 | * return EMAC0 offset here | |
78d78236 VG |
166 | * vg: For 460EX/460GT if internal GPCS PHY address is specified |
167 | * return appropriate EMAC offset | |
c3307fa1 | 168 | */ |
78d78236 | 169 | unsigned int miiphy_getemac_offset(u8 addr) |
affae2bf | 170 | { |
e2a53458 | 171 | #if defined(CONFIG_440) && \ |
c3307fa1 | 172 | !defined(CONFIG_440SP) && !defined(CONFIG_440SPE) && \ |
e2a53458 | 173 | !defined(CONFIG_460EX) && !defined(CONFIG_460GT) |
d6c61aab SR |
174 | unsigned long zmii; |
175 | unsigned long eoffset; | |
176 | ||
177 | /* Need to find out which mdi port we're using */ | |
ddc922ff | 178 | zmii = in_be32((void *)ZMII0_FER); |
d6c61aab | 179 | |
c348578b | 180 | if (zmii & (ZMII_FER_MDI << ZMII_FER_V (0))) |
d6c61aab SR |
181 | /* using port 0 */ |
182 | eoffset = 0; | |
c348578b LJ |
183 | |
184 | else if (zmii & (ZMII_FER_MDI << ZMII_FER_V (1))) | |
d6c61aab SR |
185 | /* using port 1 */ |
186 | eoffset = 0x100; | |
c348578b LJ |
187 | |
188 | else if (zmii & (ZMII_FER_MDI << ZMII_FER_V (2))) | |
d6c61aab SR |
189 | /* using port 2 */ |
190 | eoffset = 0x400; | |
c348578b LJ |
191 | |
192 | else if (zmii & (ZMII_FER_MDI << ZMII_FER_V (3))) | |
d6c61aab SR |
193 | /* using port 3 */ |
194 | eoffset = 0x600; | |
c348578b LJ |
195 | |
196 | else { | |
d6c61aab SR |
197 | /* None of the mdi ports are enabled! */ |
198 | /* enable port 0 */ | |
199 | zmii |= ZMII_FER_MDI << ZMII_FER_V (0); | |
ddc922ff | 200 | out_be32((void *)ZMII0_FER, zmii); |
d6c61aab SR |
201 | eoffset = 0; |
202 | /* need to soft reset port 0 */ | |
ddc922ff NG |
203 | zmii = in_be32((void *)EMAC0_MR0); |
204 | zmii |= EMAC_MR0_SRST; | |
205 | out_be32((void *)EMAC0_MR0, zmii); | |
d6c61aab SR |
206 | } |
207 | ||
208 | return (eoffset); | |
209 | #else | |
dbbd1257 | 210 | |
e2a53458 | 211 | #if defined(CONFIG_405EX) |
dbbd1257 SR |
212 | unsigned long rgmii; |
213 | int devnum = 1; | |
214 | ||
2d83476a | 215 | rgmii = in_be32((void *)RGMII_FER); |
dbbd1257 SR |
216 | if (rgmii & (1 << (19 - devnum))) |
217 | return 0x100; | |
218 | #endif | |
219 | ||
78d78236 | 220 | #if defined(CONFIG_460EX) || defined(CONFIG_460GT) |
78d78236 VG |
221 | u32 eoffset = 0; |
222 | ||
223 | switch (addr) { | |
224 | #if defined(CONFIG_HAS_ETH1) && defined(CONFIG_GPCS_PHY1_ADDR) | |
225 | case CONFIG_GPCS_PHY1_ADDR: | |
ddc922ff | 226 | if (addr == EMAC_MR1_IPPA_GET(in_be32((void *)EMAC0_MR1 + 0x100))) |
78d78236 VG |
227 | eoffset = 0x100; |
228 | break; | |
229 | #endif | |
230 | #if defined(CONFIG_HAS_ETH2) && defined(CONFIG_GPCS_PHY2_ADDR) | |
231 | case CONFIG_GPCS_PHY2_ADDR: | |
ddc922ff | 232 | if (addr == EMAC_MR1_IPPA_GET(in_be32((void *)EMAC0_MR1 + 0x300))) |
78d78236 VG |
233 | eoffset = 0x300; |
234 | break; | |
235 | #endif | |
236 | #if defined(CONFIG_HAS_ETH3) && defined(CONFIG_GPCS_PHY3_ADDR) | |
237 | case CONFIG_GPCS_PHY3_ADDR: | |
ddc922ff | 238 | if (addr == EMAC_MR1_IPPA_GET(in_be32((void *)EMAC0_MR1 + 0x400))) |
78d78236 VG |
239 | eoffset = 0x400; |
240 | break; | |
241 | #endif | |
242 | default: | |
243 | eoffset = 0; | |
244 | break; | |
245 | } | |
246 | return eoffset; | |
247 | #endif | |
248 | ||
d6c61aab SR |
249 | return 0; |
250 | #endif | |
251 | } | |
252 | ||
c3307fa1 | 253 | static int emac_miiphy_wait(u32 emac_reg) |
d6c61aab | 254 | { |
c3307fa1 SR |
255 | u32 sta_reg; |
256 | int i; | |
d6c61aab | 257 | |
c3307fa1 | 258 | /* wait for completion */ |
affae2bf | 259 | i = 0; |
c3307fa1 | 260 | do { |
ddc922ff | 261 | sta_reg = in_be32((void *)EMAC0_STACR + emac_reg); |
c3307fa1 | 262 | if (i++ > 5) { |
ddc922ff | 263 | debug("%s [%d]: Timeout! EMAC0_STACR=0x%0x\n", __func__, |
c3307fa1 | 264 | __LINE__, sta_reg); |
affae2bf WD |
265 | return -1; |
266 | } | |
c3307fa1 SR |
267 | udelay(10); |
268 | } while ((sta_reg & EMAC_STACR_OC) == EMAC_STACR_OC_MASK); | |
269 | ||
270 | return 0; | |
271 | } | |
272 | ||
273 | static int emac_miiphy_command(u8 addr, u8 reg, int cmd, u16 value) | |
274 | { | |
275 | u32 emac_reg; | |
276 | u32 sta_reg; | |
277 | ||
78d78236 | 278 | emac_reg = miiphy_getemac_offset(addr); |
c3307fa1 SR |
279 | |
280 | /* wait for completion */ | |
281 | if (emac_miiphy_wait(emac_reg) != 0) | |
282 | return -1; | |
283 | ||
d6c61aab | 284 | sta_reg = reg; /* reg address */ |
c3307fa1 | 285 | |
8ed44d91 | 286 | /* set clock (50MHz) and read flags */ |
887e2ec9 | 287 | #if defined(CONFIG_440GX) || defined(CONFIG_440SPE) || \ |
dbbd1257 | 288 | defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ |
c3307fa1 | 289 | defined(CONFIG_460EX) || defined(CONFIG_460GT) || \ |
dbbd1257 | 290 | defined(CONFIG_405EX) |
c348578b | 291 | #if defined(CONFIG_IBM_EMAC4_V4) /* EMAC4 V4 changed bit setting */ |
c3307fa1 | 292 | sta_reg = (sta_reg & ~EMAC_STACR_OP_MASK) | cmd; |
6c5879f3 | 293 | #else |
c3307fa1 | 294 | sta_reg |= cmd; |
6c5879f3 | 295 | #endif |
d6c61aab | 296 | #else |
c3307fa1 | 297 | sta_reg = (sta_reg | cmd) & ~EMAC_STACR_CLK_100MHZ; |
d6c61aab SR |
298 | #endif |
299 | ||
c3307fa1 | 300 | /* Some boards (mainly 405EP based) define the PHY clock freqency fixed */ |
12f34241 | 301 | sta_reg = sta_reg | CONFIG_PHY_CLK_FREQ; |
c3307fa1 | 302 | sta_reg = sta_reg | ((u32)addr << 5); /* Phy address */ |
6c5879f3 | 303 | sta_reg = sta_reg | EMAC_STACR_OC_MASK; /* new IBM emac v4 */ |
c3307fa1 SR |
304 | if (cmd == EMAC_STACR_WRITE) |
305 | memcpy(&sta_reg, &value, 2); /* put in data */ | |
306 | ||
ddc922ff | 307 | out_be32((void *)EMAC0_STACR + emac_reg, sta_reg); |
c3307fa1 | 308 | debug("%s [%d]: sta_reg=%08x\n", __func__, __LINE__, sta_reg); |
affae2bf | 309 | |
c3307fa1 SR |
310 | /* wait for completion */ |
311 | if (emac_miiphy_wait(emac_reg) != 0) | |
312 | return -1; | |
c348578b | 313 | |
c3307fa1 | 314 | debug("%s [%d]: sta_reg=%08x\n", __func__, __LINE__, sta_reg); |
c348578b | 315 | if ((sta_reg & EMAC_STACR_PHYE) != 0) |
affae2bf | 316 | return -1; |
affae2bf | 317 | |
affae2bf | 318 | return 0; |
c3307fa1 | 319 | } |
affae2bf | 320 | |
dfcc496e | 321 | int emac4xx_miiphy_read(struct mii_dev *bus, int addr, int devad, int reg) |
affae2bf | 322 | { |
c3307fa1 | 323 | unsigned long sta_reg; |
d6c61aab | 324 | unsigned long emac_reg; |
affae2bf | 325 | |
78d78236 | 326 | emac_reg = miiphy_getemac_offset(addr); |
affae2bf | 327 | |
c3307fa1 SR |
328 | if (emac_miiphy_command(addr, reg, EMAC_STACR_READ, 0) != 0) |
329 | return -1; | |
affae2bf | 330 | |
ddc922ff | 331 | sta_reg = in_be32((void *)EMAC0_STACR + emac_reg); |
dfcc496e | 332 | return sta_reg >> 16; |
c3307fa1 | 333 | } |
affae2bf | 334 | |
c3307fa1 SR |
335 | /***********************************************************/ |
336 | /* write a phy reg and return the value with a rc */ | |
337 | /***********************************************************/ | |
338 | ||
dfcc496e JH |
339 | int emac4xx_miiphy_write(struct mii_dev *bus, int addr, int devad, int reg, |
340 | u16 value) | |
c3307fa1 SR |
341 | { |
342 | return emac_miiphy_command(addr, reg, EMAC_STACR_WRITE, value); | |
343 | } |