]>
Commit | Line | Data |
---|---|---|
7737d5c6 DL |
1 | /* |
2 | * Copyright (C) 2005 Freescale Semiconductor, Inc. | |
3 | * | |
4 | * Author: Shlomi Gridish | |
5 | * | |
6 | * Description: UCC GETH Driver -- PHY handling | |
dd520bf3 WD |
7 | * Driver for UEC on QE |
8 | * Based on 8260_io/fcc_enet.c | |
7737d5c6 | 9 | * |
dd520bf3 WD |
10 | * This program is free software; you can redistribute it and/or modify it |
11 | * under the terms of the GNU General Public License as published by the | |
7737d5c6 DL |
12 | * Free Software Foundation; either version 2 of the License, or (at your |
13 | * option) any later version. | |
14 | * | |
15 | */ | |
16 | ||
17 | #include "common.h" | |
18 | #include "net.h" | |
19 | #include "malloc.h" | |
20 | #include "asm/errno.h" | |
21 | #include "asm/immap_qe.h" | |
22 | #include "asm/io.h" | |
23 | #include "qe.h" | |
24 | #include "uccf.h" | |
25 | #include "uec.h" | |
26 | #include "uec_phy.h" | |
27 | #include "miiphy.h" | |
28 | ||
29 | #if defined(CONFIG_QE) | |
30 | ||
7737d5c6 | 31 | #define ugphy_printk(format, arg...) \ |
dd520bf3 | 32 | printf(format "\n", ## arg) |
7737d5c6 | 33 | |
dd520bf3 WD |
34 | #define ugphy_dbg(format, arg...) \ |
35 | ugphy_printk(format , ## arg) | |
36 | #define ugphy_err(format, arg...) \ | |
37 | ugphy_printk(format , ## arg) | |
38 | #define ugphy_info(format, arg...) \ | |
39 | ugphy_printk(format , ## arg) | |
40 | #define ugphy_warn(format, arg...) \ | |
41 | ugphy_printk(format , ## arg) | |
7737d5c6 DL |
42 | |
43 | #ifdef UEC_VERBOSE_DEBUG | |
44 | #define ugphy_vdbg ugphy_dbg | |
45 | #else | |
46 | #define ugphy_vdbg(ugeth, fmt, args...) do { } while (0) | |
47 | #endif /* UEC_VERBOSE_DEBUG */ | |
48 | ||
dd520bf3 WD |
49 | static void config_genmii_advert (struct uec_mii_info *mii_info); |
50 | static void genmii_setup_forced (struct uec_mii_info *mii_info); | |
51 | static void genmii_restart_aneg (struct uec_mii_info *mii_info); | |
52 | static int gbit_config_aneg (struct uec_mii_info *mii_info); | |
53 | static int genmii_config_aneg (struct uec_mii_info *mii_info); | |
54 | static int genmii_update_link (struct uec_mii_info *mii_info); | |
55 | static int genmii_read_status (struct uec_mii_info *mii_info); | |
56 | u16 phy_read (struct uec_mii_info *mii_info, u16 regnum); | |
57 | void phy_write (struct uec_mii_info *mii_info, u16 regnum, u16 val); | |
7737d5c6 DL |
58 | |
59 | /* Write value to the PHY for this device to the register at regnum, */ | |
60 | /* waiting until the write is done before it returns. All PHY */ | |
61 | /* configuration has to be done through the TSEC1 MIIM regs */ | |
da9d4610 | 62 | void uec_write_phy_reg (struct eth_device *dev, int mii_id, int regnum, int value) |
7737d5c6 | 63 | { |
dd520bf3 | 64 | uec_private_t *ugeth = (uec_private_t *) dev->priv; |
da9d4610 | 65 | uec_mii_t *ug_regs; |
dd520bf3 WD |
66 | enet_tbi_mii_reg_e mii_reg = (enet_tbi_mii_reg_e) regnum; |
67 | u32 tmp_reg; | |
7737d5c6 | 68 | |
da9d4610 | 69 | ug_regs = ugeth->uec_mii_regs; |
7737d5c6 | 70 | |
dd520bf3 WD |
71 | /* Stop the MII management read cycle */ |
72 | out_be32 (&ug_regs->miimcom, 0); | |
73 | /* Setting up the MII Mangement Address Register */ | |
74 | tmp_reg = ((u32) mii_id << MIIMADD_PHY_ADDRESS_SHIFT) | mii_reg; | |
75 | out_be32 (&ug_regs->miimadd, tmp_reg); | |
7737d5c6 | 76 | |
dd520bf3 WD |
77 | /* Setting up the MII Mangement Control Register with the value */ |
78 | out_be32 (&ug_regs->miimcon, (u32) value); | |
ee62ed32 | 79 | sync(); |
7737d5c6 | 80 | |
dd520bf3 WD |
81 | /* Wait till MII management write is complete */ |
82 | while ((in_be32 (&ug_regs->miimind)) & MIIMIND_BUSY); | |
7737d5c6 DL |
83 | } |
84 | ||
85 | /* Reads from register regnum in the PHY for device dev, */ | |
86 | /* returning the value. Clears miimcom first. All PHY */ | |
87 | /* configuration has to be done through the TSEC1 MIIM regs */ | |
da9d4610 | 88 | int uec_read_phy_reg (struct eth_device *dev, int mii_id, int regnum) |
7737d5c6 | 89 | { |
dd520bf3 | 90 | uec_private_t *ugeth = (uec_private_t *) dev->priv; |
da9d4610 | 91 | uec_mii_t *ug_regs; |
dd520bf3 WD |
92 | enet_tbi_mii_reg_e mii_reg = (enet_tbi_mii_reg_e) regnum; |
93 | u32 tmp_reg; | |
94 | u16 value; | |
7737d5c6 | 95 | |
da9d4610 | 96 | ug_regs = ugeth->uec_mii_regs; |
7737d5c6 | 97 | |
dd520bf3 WD |
98 | /* Setting up the MII Mangement Address Register */ |
99 | tmp_reg = ((u32) mii_id << MIIMADD_PHY_ADDRESS_SHIFT) | mii_reg; | |
100 | out_be32 (&ug_regs->miimadd, tmp_reg); | |
7737d5c6 | 101 | |
ee62ed32 | 102 | /* clear MII management command cycle */ |
dd520bf3 | 103 | out_be32 (&ug_regs->miimcom, 0); |
ee62ed32 KP |
104 | sync(); |
105 | ||
106 | /* Perform an MII management read cycle */ | |
dd520bf3 | 107 | out_be32 (&ug_regs->miimcom, MIIMCOM_READ_CYCLE); |
7737d5c6 | 108 | |
dd520bf3 WD |
109 | /* Wait till MII management write is complete */ |
110 | while ((in_be32 (&ug_regs->miimind)) & | |
111 | (MIIMIND_NOT_VALID | MIIMIND_BUSY)); | |
7737d5c6 | 112 | |
dd520bf3 WD |
113 | /* Read MII management status */ |
114 | value = (u16) in_be32 (&ug_regs->miimstat); | |
115 | if (value == 0xffff) | |
84a3047b | 116 | ugphy_vdbg |
dd520bf3 WD |
117 | ("read wrong value : mii_id %d,mii_reg %d, base %08x", |
118 | mii_id, mii_reg, (u32) & (ug_regs->miimcfg)); | |
7737d5c6 | 119 | |
dd520bf3 | 120 | return (value); |
7737d5c6 DL |
121 | } |
122 | ||
dd520bf3 | 123 | void mii_clear_phy_interrupt (struct uec_mii_info *mii_info) |
7737d5c6 | 124 | { |
dd520bf3 WD |
125 | if (mii_info->phyinfo->ack_interrupt) |
126 | mii_info->phyinfo->ack_interrupt (mii_info); | |
7737d5c6 DL |
127 | } |
128 | ||
dd520bf3 WD |
129 | void mii_configure_phy_interrupt (struct uec_mii_info *mii_info, |
130 | u32 interrupts) | |
7737d5c6 | 131 | { |
dd520bf3 WD |
132 | mii_info->interrupts = interrupts; |
133 | if (mii_info->phyinfo->config_intr) | |
134 | mii_info->phyinfo->config_intr (mii_info); | |
7737d5c6 DL |
135 | } |
136 | ||
137 | /* Writes MII_ADVERTISE with the appropriate values, after | |
138 | * sanitizing advertise to make sure only supported features | |
139 | * are advertised | |
140 | */ | |
dd520bf3 | 141 | static void config_genmii_advert (struct uec_mii_info *mii_info) |
7737d5c6 | 142 | { |
dd520bf3 WD |
143 | u32 advertise; |
144 | u16 adv; | |
145 | ||
146 | /* Only allow advertising what this PHY supports */ | |
147 | mii_info->advertising &= mii_info->phyinfo->features; | |
148 | advertise = mii_info->advertising; | |
149 | ||
150 | /* Setup standard advertisement */ | |
151 | adv = phy_read (mii_info, PHY_ANAR); | |
152 | adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4); | |
153 | if (advertise & ADVERTISED_10baseT_Half) | |
154 | adv |= ADVERTISE_10HALF; | |
155 | if (advertise & ADVERTISED_10baseT_Full) | |
156 | adv |= ADVERTISE_10FULL; | |
157 | if (advertise & ADVERTISED_100baseT_Half) | |
158 | adv |= ADVERTISE_100HALF; | |
159 | if (advertise & ADVERTISED_100baseT_Full) | |
160 | adv |= ADVERTISE_100FULL; | |
161 | phy_write (mii_info, PHY_ANAR, adv); | |
7737d5c6 DL |
162 | } |
163 | ||
dd520bf3 | 164 | static void genmii_setup_forced (struct uec_mii_info *mii_info) |
7737d5c6 | 165 | { |
dd520bf3 WD |
166 | u16 ctrl; |
167 | u32 features = mii_info->phyinfo->features; | |
168 | ||
169 | ctrl = phy_read (mii_info, PHY_BMCR); | |
170 | ||
171 | ctrl &= ~(PHY_BMCR_DPLX | PHY_BMCR_100_MBPS | | |
172 | PHY_BMCR_1000_MBPS | PHY_BMCR_AUTON); | |
173 | ctrl |= PHY_BMCR_RESET; | |
174 | ||
175 | switch (mii_info->speed) { | |
176 | case SPEED_1000: | |
177 | if (features & (SUPPORTED_1000baseT_Half | |
178 | | SUPPORTED_1000baseT_Full)) { | |
179 | ctrl |= PHY_BMCR_1000_MBPS; | |
180 | break; | |
181 | } | |
182 | mii_info->speed = SPEED_100; | |
183 | case SPEED_100: | |
184 | if (features & (SUPPORTED_100baseT_Half | |
185 | | SUPPORTED_100baseT_Full)) { | |
186 | ctrl |= PHY_BMCR_100_MBPS; | |
187 | break; | |
188 | } | |
189 | mii_info->speed = SPEED_10; | |
190 | case SPEED_10: | |
191 | if (features & (SUPPORTED_10baseT_Half | |
192 | | SUPPORTED_10baseT_Full)) | |
193 | break; | |
194 | default: /* Unsupported speed! */ | |
195 | ugphy_err ("%s: Bad speed!", mii_info->dev->name); | |
196 | break; | |
197 | } | |
198 | ||
199 | phy_write (mii_info, PHY_BMCR, ctrl); | |
7737d5c6 DL |
200 | } |
201 | ||
202 | /* Enable and Restart Autonegotiation */ | |
dd520bf3 | 203 | static void genmii_restart_aneg (struct uec_mii_info *mii_info) |
7737d5c6 | 204 | { |
dd520bf3 | 205 | u16 ctl; |
7737d5c6 | 206 | |
dd520bf3 WD |
207 | ctl = phy_read (mii_info, PHY_BMCR); |
208 | ctl |= (PHY_BMCR_AUTON | PHY_BMCR_RST_NEG); | |
209 | phy_write (mii_info, PHY_BMCR, ctl); | |
7737d5c6 DL |
210 | } |
211 | ||
dd520bf3 | 212 | static int gbit_config_aneg (struct uec_mii_info *mii_info) |
7737d5c6 | 213 | { |
dd520bf3 WD |
214 | u16 adv; |
215 | u32 advertise; | |
216 | ||
217 | if (mii_info->autoneg) { | |
218 | /* Configure the ADVERTISE register */ | |
219 | config_genmii_advert (mii_info); | |
220 | advertise = mii_info->advertising; | |
221 | ||
222 | adv = phy_read (mii_info, MII_1000BASETCONTROL); | |
223 | adv &= ~(MII_1000BASETCONTROL_FULLDUPLEXCAP | | |
224 | MII_1000BASETCONTROL_HALFDUPLEXCAP); | |
225 | if (advertise & SUPPORTED_1000baseT_Half) | |
226 | adv |= MII_1000BASETCONTROL_HALFDUPLEXCAP; | |
227 | if (advertise & SUPPORTED_1000baseT_Full) | |
228 | adv |= MII_1000BASETCONTROL_FULLDUPLEXCAP; | |
229 | phy_write (mii_info, MII_1000BASETCONTROL, adv); | |
230 | ||
231 | /* Start/Restart aneg */ | |
232 | genmii_restart_aneg (mii_info); | |
233 | } else | |
234 | genmii_setup_forced (mii_info); | |
235 | ||
236 | return 0; | |
7737d5c6 DL |
237 | } |
238 | ||
dd520bf3 | 239 | static int marvell_config_aneg (struct uec_mii_info *mii_info) |
7737d5c6 | 240 | { |
dd520bf3 WD |
241 | /* The Marvell PHY has an errata which requires |
242 | * that certain registers get written in order | |
243 | * to restart autonegotiation */ | |
244 | phy_write (mii_info, PHY_BMCR, PHY_BMCR_RESET); | |
7737d5c6 | 245 | |
dd520bf3 WD |
246 | phy_write (mii_info, 0x1d, 0x1f); |
247 | phy_write (mii_info, 0x1e, 0x200c); | |
248 | phy_write (mii_info, 0x1d, 0x5); | |
249 | phy_write (mii_info, 0x1e, 0); | |
250 | phy_write (mii_info, 0x1e, 0x100); | |
7737d5c6 | 251 | |
dd520bf3 | 252 | gbit_config_aneg (mii_info); |
7737d5c6 | 253 | |
dd520bf3 | 254 | return 0; |
7737d5c6 DL |
255 | } |
256 | ||
dd520bf3 | 257 | static int genmii_config_aneg (struct uec_mii_info *mii_info) |
7737d5c6 | 258 | { |
dd520bf3 WD |
259 | if (mii_info->autoneg) { |
260 | config_genmii_advert (mii_info); | |
261 | genmii_restart_aneg (mii_info); | |
262 | } else | |
263 | genmii_setup_forced (mii_info); | |
7737d5c6 | 264 | |
dd520bf3 | 265 | return 0; |
7737d5c6 DL |
266 | } |
267 | ||
dd520bf3 | 268 | static int genmii_update_link (struct uec_mii_info *mii_info) |
7737d5c6 | 269 | { |
dd520bf3 | 270 | u16 status; |
7737d5c6 | 271 | |
ee62ed32 | 272 | /* Status is read once to clear old link state */ |
dd520bf3 | 273 | phy_read (mii_info, PHY_BMSR); |
7737d5c6 | 274 | |
ee62ed32 KP |
275 | /* |
276 | * Wait if the link is up, and autonegotiation is in progress | |
277 | * (ie - we're capable and it's not done) | |
278 | */ | |
279 | status = phy_read(mii_info, PHY_BMSR); | |
280 | if ((status & PHY_BMSR_LS) && (status & PHY_BMSR_AUTN_ABLE) | |
281 | && !(status & PHY_BMSR_AUTN_COMP)) { | |
282 | int i = 0; | |
283 | ||
284 | while (!(status & PHY_BMSR_AUTN_COMP)) { | |
285 | /* | |
286 | * Timeout reached ? | |
287 | */ | |
288 | if (i > UGETH_AN_TIMEOUT) { | |
289 | mii_info->link = 0; | |
290 | return 0; | |
291 | } | |
292 | ||
f30b6154 | 293 | i++; |
ee62ed32 KP |
294 | udelay(1000); /* 1 ms */ |
295 | status = phy_read(mii_info, PHY_BMSR); | |
296 | } | |
dd520bf3 | 297 | mii_info->link = 1; |
ee62ed32 KP |
298 | udelay(500000); /* another 500 ms (results in faster booting) */ |
299 | } else { | |
300 | if (status & PHY_BMSR_LS) | |
301 | mii_info->link = 1; | |
302 | else | |
303 | mii_info->link = 0; | |
304 | } | |
7737d5c6 | 305 | |
dd520bf3 | 306 | return 0; |
7737d5c6 DL |
307 | } |
308 | ||
dd520bf3 | 309 | static int genmii_read_status (struct uec_mii_info *mii_info) |
7737d5c6 | 310 | { |
dd520bf3 WD |
311 | u16 status; |
312 | int err; | |
313 | ||
314 | /* Update the link, but return if there | |
315 | * was an error */ | |
316 | err = genmii_update_link (mii_info); | |
317 | if (err) | |
318 | return err; | |
319 | ||
320 | if (mii_info->autoneg) { | |
91cdaa3a AV |
321 | status = phy_read(mii_info, MII_1000BASETSTATUS); |
322 | ||
323 | if (status & (LPA_1000FULL | LPA_1000HALF)) { | |
324 | mii_info->speed = SPEED_1000; | |
325 | if (status & LPA_1000FULL) | |
326 | mii_info->duplex = DUPLEX_FULL; | |
327 | else | |
328 | mii_info->duplex = DUPLEX_HALF; | |
329 | } else { | |
330 | status = phy_read(mii_info, PHY_ANLPAR); | |
331 | ||
332 | if (status & (PHY_ANLPAR_10FD | PHY_ANLPAR_TXFD)) | |
333 | mii_info->duplex = DUPLEX_FULL; | |
334 | else | |
335 | mii_info->duplex = DUPLEX_HALF; | |
336 | if (status & (PHY_ANLPAR_TXFD | PHY_ANLPAR_TX)) | |
337 | mii_info->speed = SPEED_100; | |
338 | else | |
339 | mii_info->speed = SPEED_10; | |
340 | } | |
dd520bf3 WD |
341 | mii_info->pause = 0; |
342 | } | |
343 | /* On non-aneg, we assume what we put in BMCR is the speed, | |
344 | * though magic-aneg shouldn't prevent this case from occurring | |
345 | */ | |
346 | ||
347 | return 0; | |
7737d5c6 DL |
348 | } |
349 | ||
dd520bf3 | 350 | static int marvell_read_status (struct uec_mii_info *mii_info) |
7737d5c6 | 351 | { |
dd520bf3 WD |
352 | u16 status; |
353 | int err; | |
354 | ||
355 | /* Update the link, but return if there | |
356 | * was an error */ | |
357 | err = genmii_update_link (mii_info); | |
358 | if (err) | |
359 | return err; | |
360 | ||
361 | /* If the link is up, read the speed and duplex */ | |
362 | /* If we aren't autonegotiating, assume speeds | |
363 | * are as set */ | |
364 | if (mii_info->autoneg && mii_info->link) { | |
365 | int speed; | |
366 | ||
367 | status = phy_read (mii_info, MII_M1011_PHY_SPEC_STATUS); | |
368 | ||
369 | /* Get the duplexity */ | |
370 | if (status & MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX) | |
371 | mii_info->duplex = DUPLEX_FULL; | |
372 | else | |
373 | mii_info->duplex = DUPLEX_HALF; | |
374 | ||
375 | /* Get the speed */ | |
376 | speed = status & MII_M1011_PHY_SPEC_STATUS_SPD_MASK; | |
377 | switch (speed) { | |
378 | case MII_M1011_PHY_SPEC_STATUS_1000: | |
379 | mii_info->speed = SPEED_1000; | |
380 | break; | |
381 | case MII_M1011_PHY_SPEC_STATUS_100: | |
382 | mii_info->speed = SPEED_100; | |
383 | break; | |
384 | default: | |
385 | mii_info->speed = SPEED_10; | |
386 | break; | |
387 | } | |
388 | mii_info->pause = 0; | |
389 | } | |
390 | ||
391 | return 0; | |
7737d5c6 DL |
392 | } |
393 | ||
dd520bf3 | 394 | static int marvell_ack_interrupt (struct uec_mii_info *mii_info) |
7737d5c6 | 395 | { |
dd520bf3 WD |
396 | /* Clear the interrupts by reading the reg */ |
397 | phy_read (mii_info, MII_M1011_IEVENT); | |
7737d5c6 | 398 | |
dd520bf3 | 399 | return 0; |
7737d5c6 DL |
400 | } |
401 | ||
dd520bf3 | 402 | static int marvell_config_intr (struct uec_mii_info *mii_info) |
7737d5c6 | 403 | { |
dd520bf3 WD |
404 | if (mii_info->interrupts == MII_INTERRUPT_ENABLED) |
405 | phy_write (mii_info, MII_M1011_IMASK, MII_M1011_IMASK_INIT); | |
406 | else | |
407 | phy_write (mii_info, MII_M1011_IMASK, MII_M1011_IMASK_CLEAR); | |
7737d5c6 | 408 | |
dd520bf3 | 409 | return 0; |
7737d5c6 DL |
410 | } |
411 | ||
dd520bf3 | 412 | static int dm9161_init (struct uec_mii_info *mii_info) |
7737d5c6 | 413 | { |
dd520bf3 WD |
414 | /* Reset the PHY */ |
415 | phy_write (mii_info, PHY_BMCR, phy_read (mii_info, PHY_BMCR) | | |
416 | PHY_BMCR_RESET); | |
417 | /* PHY and MAC connect */ | |
418 | phy_write (mii_info, PHY_BMCR, phy_read (mii_info, PHY_BMCR) & | |
419 | ~PHY_BMCR_ISO); | |
ee62ed32 | 420 | |
dd520bf3 | 421 | phy_write (mii_info, MII_DM9161_SCR, MII_DM9161_SCR_INIT); |
ee62ed32 | 422 | |
dd520bf3 WD |
423 | config_genmii_advert (mii_info); |
424 | /* Start/restart aneg */ | |
425 | genmii_config_aneg (mii_info); | |
7737d5c6 | 426 | |
dd520bf3 | 427 | return 0; |
7737d5c6 DL |
428 | } |
429 | ||
dd520bf3 | 430 | static int dm9161_config_aneg (struct uec_mii_info *mii_info) |
7737d5c6 | 431 | { |
dd520bf3 | 432 | return 0; |
7737d5c6 DL |
433 | } |
434 | ||
dd520bf3 | 435 | static int dm9161_read_status (struct uec_mii_info *mii_info) |
7737d5c6 | 436 | { |
dd520bf3 WD |
437 | u16 status; |
438 | int err; | |
439 | ||
440 | /* Update the link, but return if there was an error */ | |
441 | err = genmii_update_link (mii_info); | |
442 | if (err) | |
443 | return err; | |
444 | /* If the link is up, read the speed and duplex | |
445 | If we aren't autonegotiating assume speeds are as set */ | |
446 | if (mii_info->autoneg && mii_info->link) { | |
447 | status = phy_read (mii_info, MII_DM9161_SCSR); | |
448 | if (status & (MII_DM9161_SCSR_100F | MII_DM9161_SCSR_100H)) | |
449 | mii_info->speed = SPEED_100; | |
450 | else | |
451 | mii_info->speed = SPEED_10; | |
452 | ||
453 | if (status & (MII_DM9161_SCSR_100F | MII_DM9161_SCSR_10F)) | |
454 | mii_info->duplex = DUPLEX_FULL; | |
455 | else | |
456 | mii_info->duplex = DUPLEX_HALF; | |
457 | } | |
458 | ||
459 | return 0; | |
7737d5c6 DL |
460 | } |
461 | ||
dd520bf3 | 462 | static int dm9161_ack_interrupt (struct uec_mii_info *mii_info) |
7737d5c6 | 463 | { |
dd520bf3 WD |
464 | /* Clear the interrupt by reading the reg */ |
465 | phy_read (mii_info, MII_DM9161_INTR); | |
7737d5c6 | 466 | |
dd520bf3 | 467 | return 0; |
7737d5c6 DL |
468 | } |
469 | ||
dd520bf3 | 470 | static int dm9161_config_intr (struct uec_mii_info *mii_info) |
7737d5c6 | 471 | { |
dd520bf3 WD |
472 | if (mii_info->interrupts == MII_INTERRUPT_ENABLED) |
473 | phy_write (mii_info, MII_DM9161_INTR, MII_DM9161_INTR_INIT); | |
474 | else | |
475 | phy_write (mii_info, MII_DM9161_INTR, MII_DM9161_INTR_STOP); | |
7737d5c6 | 476 | |
dd520bf3 | 477 | return 0; |
7737d5c6 DL |
478 | } |
479 | ||
dd520bf3 | 480 | static void dm9161_close (struct uec_mii_info *mii_info) |
7737d5c6 DL |
481 | { |
482 | } | |
483 | ||
484 | static struct phy_info phy_info_dm9161 = { | |
dd520bf3 WD |
485 | .phy_id = 0x0181b880, |
486 | .phy_id_mask = 0x0ffffff0, | |
487 | .name = "Davicom DM9161E", | |
488 | .init = dm9161_init, | |
489 | .config_aneg = dm9161_config_aneg, | |
490 | .read_status = dm9161_read_status, | |
491 | .close = dm9161_close, | |
7737d5c6 DL |
492 | }; |
493 | ||
494 | static struct phy_info phy_info_dm9161a = { | |
dd520bf3 WD |
495 | .phy_id = 0x0181b8a0, |
496 | .phy_id_mask = 0x0ffffff0, | |
497 | .name = "Davicom DM9161A", | |
498 | .features = MII_BASIC_FEATURES, | |
499 | .init = dm9161_init, | |
500 | .config_aneg = dm9161_config_aneg, | |
501 | .read_status = dm9161_read_status, | |
502 | .ack_interrupt = dm9161_ack_interrupt, | |
503 | .config_intr = dm9161_config_intr, | |
504 | .close = dm9161_close, | |
7737d5c6 DL |
505 | }; |
506 | ||
507 | static struct phy_info phy_info_marvell = { | |
dd520bf3 WD |
508 | .phy_id = 0x01410c00, |
509 | .phy_id_mask = 0xffffff00, | |
510 | .name = "Marvell 88E11x1", | |
511 | .features = MII_GBIT_FEATURES, | |
512 | .config_aneg = &marvell_config_aneg, | |
513 | .read_status = &marvell_read_status, | |
514 | .ack_interrupt = &marvell_ack_interrupt, | |
515 | .config_intr = &marvell_config_intr, | |
7737d5c6 DL |
516 | }; |
517 | ||
dd520bf3 WD |
518 | static struct phy_info phy_info_genmii = { |
519 | .phy_id = 0x00000000, | |
520 | .phy_id_mask = 0x00000000, | |
521 | .name = "Generic MII", | |
522 | .features = MII_BASIC_FEATURES, | |
523 | .config_aneg = genmii_config_aneg, | |
524 | .read_status = genmii_read_status, | |
7737d5c6 DL |
525 | }; |
526 | ||
527 | static struct phy_info *phy_info[] = { | |
dd520bf3 WD |
528 | &phy_info_dm9161, |
529 | &phy_info_dm9161a, | |
530 | &phy_info_marvell, | |
531 | &phy_info_genmii, | |
532 | NULL | |
7737d5c6 DL |
533 | }; |
534 | ||
dd520bf3 | 535 | u16 phy_read (struct uec_mii_info *mii_info, u16 regnum) |
7737d5c6 | 536 | { |
dd520bf3 | 537 | return mii_info->mdio_read (mii_info->dev, mii_info->mii_id, regnum); |
7737d5c6 DL |
538 | } |
539 | ||
dd520bf3 | 540 | void phy_write (struct uec_mii_info *mii_info, u16 regnum, u16 val) |
7737d5c6 | 541 | { |
dd520bf3 | 542 | mii_info->mdio_write (mii_info->dev, mii_info->mii_id, regnum, val); |
7737d5c6 DL |
543 | } |
544 | ||
545 | /* Use the PHY ID registers to determine what type of PHY is attached | |
546 | * to device dev. return a struct phy_info structure describing that PHY | |
547 | */ | |
da9d4610 | 548 | struct phy_info *uec_get_phy_info (struct uec_mii_info *mii_info) |
7737d5c6 | 549 | { |
dd520bf3 WD |
550 | u16 phy_reg; |
551 | u32 phy_ID; | |
552 | int i; | |
553 | struct phy_info *theInfo = NULL; | |
554 | ||
555 | /* Grab the bits from PHYIR1, and put them in the upper half */ | |
556 | phy_reg = phy_read (mii_info, PHY_PHYIDR1); | |
557 | phy_ID = (phy_reg & 0xffff) << 16; | |
558 | ||
559 | /* Grab the bits from PHYIR2, and put them in the lower half */ | |
560 | phy_reg = phy_read (mii_info, PHY_PHYIDR2); | |
561 | phy_ID |= (phy_reg & 0xffff); | |
562 | ||
563 | /* loop through all the known PHY types, and find one that */ | |
564 | /* matches the ID we read from the PHY. */ | |
565 | for (i = 0; phy_info[i]; i++) | |
566 | if (phy_info[i]->phy_id == | |
567 | (phy_ID & phy_info[i]->phy_id_mask)) { | |
568 | theInfo = phy_info[i]; | |
569 | break; | |
570 | } | |
571 | ||
572 | /* This shouldn't happen, as we have generic PHY support */ | |
573 | if (theInfo == NULL) { | |
574 | ugphy_info ("UEC: PHY id %x is not supported!", phy_ID); | |
575 | return NULL; | |
576 | } else { | |
577 | ugphy_info ("UEC: PHY is %s (%x)", theInfo->name, phy_ID); | |
578 | } | |
579 | ||
580 | return theInfo; | |
7737d5c6 DL |
581 | } |
582 | ||
dd520bf3 WD |
583 | void marvell_phy_interface_mode (struct eth_device *dev, |
584 | enet_interface_e mode) | |
7737d5c6 | 585 | { |
dd520bf3 WD |
586 | uec_private_t *uec = (uec_private_t *) dev->priv; |
587 | struct uec_mii_info *mii_info; | |
f655adef | 588 | u16 status; |
7737d5c6 DL |
589 | |
590 | if (!uec->mii_info) { | |
f30b6154 | 591 | printf ("%s: the PHY not initialized\n", __FUNCTION__); |
7737d5c6 DL |
592 | return; |
593 | } | |
594 | mii_info = uec->mii_info; | |
595 | ||
596 | if (mode == ENET_100_RGMII) { | |
dd520bf3 WD |
597 | phy_write (mii_info, 0x00, 0x9140); |
598 | phy_write (mii_info, 0x1d, 0x001f); | |
599 | phy_write (mii_info, 0x1e, 0x200c); | |
600 | phy_write (mii_info, 0x1d, 0x0005); | |
601 | phy_write (mii_info, 0x1e, 0x0000); | |
602 | phy_write (mii_info, 0x1e, 0x0100); | |
603 | phy_write (mii_info, 0x09, 0x0e00); | |
604 | phy_write (mii_info, 0x04, 0x01e1); | |
605 | phy_write (mii_info, 0x00, 0x9140); | |
606 | phy_write (mii_info, 0x00, 0x1000); | |
607 | udelay (100000); | |
608 | phy_write (mii_info, 0x00, 0x2900); | |
609 | phy_write (mii_info, 0x14, 0x0cd2); | |
610 | phy_write (mii_info, 0x00, 0xa100); | |
611 | phy_write (mii_info, 0x09, 0x0000); | |
612 | phy_write (mii_info, 0x1b, 0x800b); | |
613 | phy_write (mii_info, 0x04, 0x05e1); | |
614 | phy_write (mii_info, 0x00, 0xa100); | |
615 | phy_write (mii_info, 0x00, 0x2100); | |
616 | udelay (1000000); | |
7737d5c6 | 617 | } else if (mode == ENET_10_RGMII) { |
dd520bf3 WD |
618 | phy_write (mii_info, 0x14, 0x8e40); |
619 | phy_write (mii_info, 0x1b, 0x800b); | |
620 | phy_write (mii_info, 0x14, 0x0c82); | |
621 | phy_write (mii_info, 0x00, 0x8100); | |
622 | udelay (1000000); | |
7737d5c6 | 623 | } |
f655adef KP |
624 | |
625 | /* handle 88e1111 rev.B2 erratum 5.6 */ | |
626 | if (mii_info->autoneg) { | |
627 | status = phy_read (mii_info, PHY_BMCR); | |
628 | phy_write (mii_info, PHY_BMCR, status | PHY_BMCR_AUTON); | |
629 | } | |
630 | /* now the B2 will correctly report autoneg completion status */ | |
7737d5c6 DL |
631 | } |
632 | ||
dd520bf3 | 633 | void change_phy_interface_mode (struct eth_device *dev, enet_interface_e mode) |
7737d5c6 DL |
634 | { |
635 | #ifdef CONFIG_PHY_MODE_NEED_CHANGE | |
dd520bf3 | 636 | marvell_phy_interface_mode (dev, mode); |
7737d5c6 DL |
637 | #endif |
638 | } | |
639 | #endif /* CONFIG_QE */ |