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