]>
Commit | Line | Data |
---|---|---|
54bdcc9f TL |
1 | /* |
2 | * Copyright (C) 2004-2008 Freescale Semiconductor, Inc. | |
3 | * TsiChung Liew ([email protected]) | |
4 | * | |
1a459660 | 5 | * SPDX-License-Identifier: GPL-2.0+ |
54bdcc9f TL |
6 | */ |
7 | ||
8 | #include <common.h> | |
9 | #include <config.h> | |
10 | #include <net.h> | |
11 | #include <netdev.h> | |
12 | ||
13 | #ifdef CONFIG_MCF547x_8x | |
14 | #include <asm/fsl_mcdmafec.h> | |
15 | #else | |
16 | #include <asm/fec.h> | |
17 | #endif | |
18 | #include <asm/immap.h> | |
19 | ||
20 | DECLARE_GLOBAL_DATA_PTR; | |
21 | ||
e2a53458 | 22 | #if defined(CONFIG_CMD_NET) |
54bdcc9f TL |
23 | #undef MII_DEBUG |
24 | #undef ET_DEBUG | |
25 | ||
26 | /*extern int fecpin_setclear(struct eth_device *dev, int setclear);*/ | |
27 | ||
28 | #if defined(CONFIG_SYS_DISCOVER_PHY) || defined(CONFIG_CMD_MII) | |
29 | #include <miiphy.h> | |
30 | ||
31 | /* Make MII read/write commands for the FEC. */ | |
32 | #define mk_mii_read(ADDR, REG) (0x60020000 | ((ADDR << 23) | \ | |
33 | (REG & 0x1f) << 18)) | |
34 | #define mk_mii_write(ADDR, REG, VAL) (0x50020000 | ((ADDR << 23) | \ | |
35 | (REG & 0x1f) << 18) | (VAL & 0xffff)) | |
36 | ||
37 | #ifndef CONFIG_SYS_UNSPEC_PHYID | |
38 | # define CONFIG_SYS_UNSPEC_PHYID 0 | |
39 | #endif | |
40 | #ifndef CONFIG_SYS_UNSPEC_STRID | |
41 | # define CONFIG_SYS_UNSPEC_STRID 0 | |
42 | #endif | |
43 | ||
44 | #ifdef CONFIG_MCF547x_8x | |
45 | typedef struct fec_info_dma FEC_INFO_T; | |
46 | #define FEC_T fecdma_t | |
47 | #else | |
48 | typedef struct fec_info_s FEC_INFO_T; | |
49 | #define FEC_T fec_t | |
50 | #endif | |
51 | ||
52 | typedef struct phy_info_struct { | |
53 | u32 phyid; | |
54 | char *strid; | |
55 | } phy_info_t; | |
56 | ||
57 | phy_info_t phyinfo[] = { | |
58 | {0x0022561B, "AMD79C784VC"}, /* AMD 79C784VC */ | |
59 | {0x00406322, "BCM5222"}, /* Broadcom 5222 */ | |
60 | {0x02a80150, "Intel82555"}, /* Intel 82555 */ | |
61 | {0x0016f870, "LSI80225"}, /* LSI 80225 */ | |
62 | {0x0016f880, "LSI80225/B"}, /* LSI 80225/B */ | |
63 | {0x78100000, "LXT970"}, /* LXT970 */ | |
64 | {0x001378e0, "LXT971"}, /* LXT971 and 972 */ | |
65 | {0x00221619, "KS8721BL"}, /* Micrel KS8721BL/SL */ | |
66 | {0x00221512, "KSZ8041NL"}, /* Micrel KSZ8041NL */ | |
67 | {0x20005CE1, "N83640"}, /* National 83640 */ | |
68 | {0x20005C90, "N83848"}, /* National 83848 */ | |
69 | {0x20005CA2, "N83849"}, /* National 83849 */ | |
70 | {0x01814400, "QS6612"}, /* QS6612 */ | |
71 | #if defined(CONFIG_SYS_UNSPEC_PHYID) && defined(CONFIG_SYS_UNSPEC_STRID) | |
72 | {CONFIG_SYS_UNSPEC_PHYID, CONFIG_SYS_UNSPEC_STRID}, | |
73 | #endif | |
74 | {0, 0} | |
75 | }; | |
76 | ||
77 | /* | |
78 | * mii_init -- Initialize the MII for MII command without ethernet | |
79 | * This function is a subset of eth_init | |
80 | */ | |
81 | void mii_reset(FEC_INFO_T *info) | |
82 | { | |
83 | volatile FEC_T *fecp = (FEC_T *) (info->miibase); | |
84 | int i; | |
85 | ||
86 | fecp->ecr = FEC_ECR_RESET; | |
87 | ||
88 | for (i = 0; (fecp->ecr & FEC_ECR_RESET) && (i < FEC_RESET_DELAY); ++i) { | |
89 | udelay(1); | |
90 | } | |
91 | if (i == FEC_RESET_DELAY) | |
92 | printf("FEC_RESET_DELAY timeout\n"); | |
93 | } | |
94 | ||
95 | /* send command to phy using mii, wait for result */ | |
96 | uint mii_send(uint mii_cmd) | |
97 | { | |
98 | FEC_INFO_T *info; | |
99 | volatile FEC_T *ep; | |
100 | struct eth_device *dev; | |
101 | uint mii_reply; | |
102 | int j = 0; | |
103 | ||
104 | /* retrieve from register structure */ | |
105 | dev = eth_get_dev(); | |
106 | info = dev->priv; | |
107 | ||
108 | ep = (FEC_T *) info->miibase; | |
109 | ||
110 | ep->mmfr = mii_cmd; /* command to phy */ | |
111 | ||
112 | /* wait for mii complete */ | |
113 | while (!(ep->eir & FEC_EIR_MII) && (j < MCFFEC_TOUT_LOOP)) { | |
114 | udelay(1); | |
115 | j++; | |
116 | } | |
117 | if (j >= MCFFEC_TOUT_LOOP) { | |
118 | printf("MII not complete\n"); | |
119 | return -1; | |
120 | } | |
121 | ||
122 | mii_reply = ep->mmfr; /* result from phy */ | |
123 | ep->eir = FEC_EIR_MII; /* clear MII complete */ | |
124 | #ifdef ET_DEBUG | |
125 | printf("%s[%d] %s: sent=0x%8.8x, reply=0x%8.8x\n", | |
126 | __FILE__, __LINE__, __FUNCTION__, mii_cmd, mii_reply); | |
127 | #endif | |
128 | ||
129 | return (mii_reply & 0xffff); /* data read from phy */ | |
130 | } | |
131 | #endif /* CONFIG_SYS_DISCOVER_PHY || (CONFIG_MII) */ | |
132 | ||
133 | #if defined(CONFIG_SYS_DISCOVER_PHY) | |
134 | int mii_discover_phy(struct eth_device *dev) | |
135 | { | |
136 | #define MAX_PHY_PASSES 11 | |
137 | FEC_INFO_T *info = dev->priv; | |
138 | int phyaddr, pass; | |
139 | uint phyno, phytype; | |
140 | int i, found = 0; | |
141 | ||
142 | if (info->phyname_init) | |
143 | return info->phy_addr; | |
144 | ||
145 | phyaddr = -1; /* didn't find a PHY yet */ | |
146 | for (pass = 1; pass <= MAX_PHY_PASSES && phyaddr < 0; ++pass) { | |
147 | if (pass > 1) { | |
148 | /* PHY may need more time to recover from reset. | |
149 | * The LXT970 needs 50ms typical, no maximum is | |
150 | * specified, so wait 10ms before try again. | |
151 | * With 11 passes this gives it 100ms to wake up. | |
152 | */ | |
153 | udelay(10000); /* wait 10ms */ | |
154 | } | |
155 | ||
156 | for (phyno = 0; phyno < 32 && phyaddr < 0; ++phyno) { | |
157 | ||
8ef583a0 | 158 | phytype = mii_send(mk_mii_read(phyno, MII_PHYSID1)); |
54bdcc9f TL |
159 | #ifdef ET_DEBUG |
160 | printf("PHY type 0x%x pass %d type\n", phytype, pass); | |
161 | #endif | |
33f684d6 WW |
162 | if (phytype == 0xffff) |
163 | continue; | |
164 | phyaddr = phyno; | |
165 | phytype <<= 16; | |
166 | phytype |= | |
8ef583a0 | 167 | mii_send(mk_mii_read(phyno, MII_PHYSID2)); |
54bdcc9f TL |
168 | |
169 | #ifdef ET_DEBUG | |
33f684d6 | 170 | printf("PHY @ 0x%x pass %d\n", phyno, pass); |
54bdcc9f TL |
171 | #endif |
172 | ||
a62cd29c | 173 | for (i = 0; (i < ARRAY_SIZE(phyinfo)) |
33f684d6 WW |
174 | && (phyinfo[i].phyid != 0); i++) { |
175 | if (phyinfo[i].phyid == phytype) { | |
54bdcc9f | 176 | #ifdef ET_DEBUG |
33f684d6 WW |
177 | printf("phyid %x - %s\n", |
178 | phyinfo[i].phyid, | |
179 | phyinfo[i].strid); | |
54bdcc9f | 180 | #endif |
33f684d6 WW |
181 | strcpy(info->phy_name, phyinfo[i].strid); |
182 | info->phyname_init = 1; | |
183 | found = 1; | |
184 | break; | |
54bdcc9f | 185 | } |
33f684d6 | 186 | } |
54bdcc9f | 187 | |
33f684d6 | 188 | if (!found) { |
54bdcc9f | 189 | #ifdef ET_DEBUG |
33f684d6 | 190 | printf("0x%08x\n", phytype); |
54bdcc9f | 191 | #endif |
33f684d6 WW |
192 | strcpy(info->phy_name, "unknown"); |
193 | info->phyname_init = 1; | |
194 | break; | |
54bdcc9f TL |
195 | } |
196 | } | |
197 | } | |
198 | ||
199 | if (phyaddr < 0) | |
200 | printf("No PHY device found.\n"); | |
201 | ||
202 | return phyaddr; | |
203 | } | |
204 | #endif /* CONFIG_SYS_DISCOVER_PHY */ | |
205 | ||
206 | void mii_init(void) __attribute__((weak,alias("__mii_init"))); | |
207 | ||
208 | void __mii_init(void) | |
209 | { | |
210 | FEC_INFO_T *info; | |
211 | volatile FEC_T *fecp; | |
212 | struct eth_device *dev; | |
213 | int miispd = 0, i = 0; | |
c4ff77f5 RR |
214 | u16 status = 0; |
215 | u16 linkgood = 0; | |
54bdcc9f TL |
216 | |
217 | /* retrieve from register structure */ | |
218 | dev = eth_get_dev(); | |
219 | info = dev->priv; | |
220 | ||
221 | fecp = (FEC_T *) info->miibase; | |
222 | ||
223 | fecpin_setclear(dev, 1); | |
224 | ||
225 | mii_reset(info); | |
226 | ||
227 | /* We use strictly polling mode only */ | |
228 | fecp->eimr = 0; | |
229 | ||
230 | /* Clear any pending interrupt */ | |
231 | fecp->eir = 0xffffffff; | |
232 | ||
233 | /* Set MII speed */ | |
234 | miispd = (gd->bus_clk / 1000000) / 5; | |
235 | fecp->mscr = miispd << 1; | |
236 | ||
237 | info->phy_addr = mii_discover_phy(dev); | |
238 | ||
54bdcc9f | 239 | while (i < MCFFEC_TOUT_LOOP) { |
c4ff77f5 | 240 | status = 0; |
54bdcc9f | 241 | i++; |
c4ff77f5 | 242 | /* Read PHY control register */ |
8ef583a0 | 243 | miiphy_read(dev->name, info->phy_addr, MII_BMCR, &status); |
c4ff77f5 RR |
244 | |
245 | /* If phy set to autonegotiate, wait for autonegotiation done, | |
246 | * if phy is not autonegotiating, just wait for link up. | |
247 | */ | |
8ef583a0 MF |
248 | if ((status & BMCR_ANENABLE) == BMCR_ANENABLE) { |
249 | linkgood = (BMSR_ANEGCOMPLETE | BMSR_LSTATUS); | |
c4ff77f5 | 250 | } else { |
8ef583a0 | 251 | linkgood = BMSR_LSTATUS; |
c4ff77f5 RR |
252 | } |
253 | /* Read PHY status register */ | |
8ef583a0 | 254 | miiphy_read(dev->name, info->phy_addr, MII_BMSR, &status); |
c4ff77f5 | 255 | if ((status & linkgood) == linkgood) |
54bdcc9f TL |
256 | break; |
257 | ||
44578bea | 258 | udelay(1); |
54bdcc9f TL |
259 | } |
260 | if (i >= MCFFEC_TOUT_LOOP) { | |
c4ff77f5 | 261 | printf("Link UP timeout\n"); |
54bdcc9f TL |
262 | } |
263 | ||
c4ff77f5 | 264 | /* adapt to the duplex and speed settings of the phy */ |
54bdcc9f TL |
265 | info->dup_spd = miiphy_duplex(dev->name, info->phy_addr) << 16; |
266 | info->dup_spd |= miiphy_speed(dev->name, info->phy_addr); | |
267 | } | |
268 | ||
269 | /* | |
270 | * Read and write a MII PHY register, routines used by MII Utilities | |
271 | * | |
272 | * FIXME: These routines are expected to return 0 on success, but mii_send | |
273 | * does _not_ return an error code. Maybe 0xFFFF means error, i.e. | |
274 | * no PHY connected... | |
275 | * For now always return 0. | |
276 | * FIXME: These routines only work after calling eth_init() at least once! | |
277 | * Otherwise they hang in mii_send() !!! Sorry! | |
278 | */ | |
279 | ||
5700bb63 | 280 | int mcffec_miiphy_read(const char *devname, unsigned char addr, unsigned char reg, |
54bdcc9f TL |
281 | unsigned short *value) |
282 | { | |
283 | short rdreg; /* register working value */ | |
284 | ||
285 | #ifdef MII_DEBUG | |
286 | printf("miiphy_read(0x%x) @ 0x%x = ", reg, addr); | |
287 | #endif | |
288 | rdreg = mii_send(mk_mii_read(addr, reg)); | |
289 | ||
290 | *value = rdreg; | |
291 | ||
292 | #ifdef MII_DEBUG | |
293 | printf("0x%04x\n", *value); | |
294 | #endif | |
295 | ||
296 | return 0; | |
297 | } | |
298 | ||
5700bb63 | 299 | int mcffec_miiphy_write(const char *devname, unsigned char addr, unsigned char reg, |
54bdcc9f TL |
300 | unsigned short value) |
301 | { | |
54bdcc9f TL |
302 | #ifdef MII_DEBUG |
303 | printf("miiphy_write(0x%x) @ 0x%x = ", reg, addr); | |
304 | #endif | |
305 | ||
2b758cad | 306 | mii_send(mk_mii_write(addr, reg, value)); |
54bdcc9f TL |
307 | |
308 | #ifdef MII_DEBUG | |
309 | printf("0x%04x\n", value); | |
310 | #endif | |
311 | ||
312 | return 0; | |
313 | } | |
314 | ||
e2a53458 | 315 | #endif /* CONFIG_CMD_NET */ |