]>
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 | ||
7737d5c6 | 29 | #define ugphy_printk(format, arg...) \ |
dd520bf3 | 30 | printf(format "\n", ## arg) |
7737d5c6 | 31 | |
dd520bf3 WD |
32 | #define ugphy_dbg(format, arg...) \ |
33 | ugphy_printk(format , ## arg) | |
34 | #define ugphy_err(format, arg...) \ | |
35 | ugphy_printk(format , ## arg) | |
36 | #define ugphy_info(format, arg...) \ | |
37 | ugphy_printk(format , ## arg) | |
38 | #define ugphy_warn(format, arg...) \ | |
39 | ugphy_printk(format , ## arg) | |
7737d5c6 DL |
40 | |
41 | #ifdef UEC_VERBOSE_DEBUG | |
42 | #define ugphy_vdbg ugphy_dbg | |
43 | #else | |
44 | #define ugphy_vdbg(ugeth, fmt, args...) do { } while (0) | |
45 | #endif /* UEC_VERBOSE_DEBUG */ | |
46 | ||
edf3fe7d RR |
47 | /*--------------------------------------------------------------------+ |
48 | * Fixed PHY (PHY-less) support for Ethernet Ports. | |
49 | * | |
50 | * Copied from cpu/ppc4xx/4xx_enet.c | |
51 | *--------------------------------------------------------------------*/ | |
52 | ||
53 | /* | |
1443cd7e RR |
54 | * Some boards do not have a PHY for each ethernet port. These ports are known |
55 | * as Fixed PHY (or PHY-less) ports. For such ports, set the appropriate | |
56 | * CONFIG_SYS_UECx_PHY_ADDR equal to CONFIG_FIXED_PHY_ADDR (an unused address) | |
57 | * When the drver tries to identify the PHYs, CONFIG_FIXED_PHY will be returned | |
58 | * and the driver will search CONFIG_SYS_FIXED_PHY_PORTS to find what network | |
59 | * speed and duplex should be for the port. | |
edf3fe7d | 60 | * |
1443cd7e | 61 | * Example board header configuration file: |
edf3fe7d | 62 | * #define CONFIG_FIXED_PHY 0xFFFFFFFF |
1443cd7e | 63 | * #define CONFIG_SYS_FIXED_PHY_ADDR 0x1E (pick an unused phy address) |
edf3fe7d | 64 | * |
1443cd7e RR |
65 | * #define CONFIG_SYS_UEC1_PHY_ADDR CONFIG_SYS_FIXED_PHY_ADDR |
66 | * #define CONFIG_SYS_UEC2_PHY_ADDR 0x02 | |
67 | * #define CONFIG_SYS_UEC3_PHY_ADDR CONFIG_SYS_FIXED_PHY_ADDR | |
68 | * #define CONFIG_SYS_UEC4_PHY_ADDR 0x04 | |
edf3fe7d | 69 | * |
1443cd7e RR |
70 | * #define CONFIG_SYS_FIXED_PHY_PORT(name,speed,duplex) \ |
71 | * {name, speed, duplex}, | |
edf3fe7d RR |
72 | * |
73 | * #define CONFIG_SYS_FIXED_PHY_PORTS \ | |
1443cd7e RR |
74 | * CONFIG_SYS_FIXED_PHY_PORT("FSL UEC0",SPEED_100,DUPLEX_FULL) \ |
75 | * CONFIG_SYS_FIXED_PHY_PORT("FSL UEC2",SPEED_100,DUPLEX_HALF) | |
edf3fe7d RR |
76 | */ |
77 | ||
78 | #ifndef CONFIG_FIXED_PHY | |
79 | #define CONFIG_FIXED_PHY 0xFFFFFFFF /* Fixed PHY (PHY-less) */ | |
80 | #endif | |
81 | ||
82 | #ifndef CONFIG_SYS_FIXED_PHY_PORTS | |
83 | #define CONFIG_SYS_FIXED_PHY_PORTS /* default is an empty array */ | |
84 | #endif | |
85 | ||
86 | struct fixed_phy_port { | |
1443cd7e | 87 | char name[NAMESIZE]; /* ethernet port name */ |
edf3fe7d RR |
88 | unsigned int speed; /* specified speed 10,100 or 1000 */ |
89 | unsigned int duplex; /* specified duplex FULL or HALF */ | |
90 | }; | |
91 | ||
92 | static const struct fixed_phy_port fixed_phy_port[] = { | |
93 | CONFIG_SYS_FIXED_PHY_PORTS /* defined in board configuration file */ | |
94 | }; | |
95 | ||
dd520bf3 WD |
96 | static void config_genmii_advert (struct uec_mii_info *mii_info); |
97 | static void genmii_setup_forced (struct uec_mii_info *mii_info); | |
98 | static void genmii_restart_aneg (struct uec_mii_info *mii_info); | |
99 | static int gbit_config_aneg (struct uec_mii_info *mii_info); | |
100 | static int genmii_config_aneg (struct uec_mii_info *mii_info); | |
101 | static int genmii_update_link (struct uec_mii_info *mii_info); | |
102 | static int genmii_read_status (struct uec_mii_info *mii_info); | |
103 | u16 phy_read (struct uec_mii_info *mii_info, u16 regnum); | |
104 | void phy_write (struct uec_mii_info *mii_info, u16 regnum, u16 val); | |
7737d5c6 DL |
105 | |
106 | /* Write value to the PHY for this device to the register at regnum, */ | |
107 | /* waiting until the write is done before it returns. All PHY */ | |
108 | /* configuration has to be done through the TSEC1 MIIM regs */ | |
da9d4610 | 109 | void uec_write_phy_reg (struct eth_device *dev, int mii_id, int regnum, int value) |
7737d5c6 | 110 | { |
dd520bf3 | 111 | uec_private_t *ugeth = (uec_private_t *) dev->priv; |
da9d4610 | 112 | uec_mii_t *ug_regs; |
dd520bf3 WD |
113 | enet_tbi_mii_reg_e mii_reg = (enet_tbi_mii_reg_e) regnum; |
114 | u32 tmp_reg; | |
7737d5c6 | 115 | |
da9d4610 | 116 | ug_regs = ugeth->uec_mii_regs; |
7737d5c6 | 117 | |
dd520bf3 WD |
118 | /* Stop the MII management read cycle */ |
119 | out_be32 (&ug_regs->miimcom, 0); | |
120 | /* Setting up the MII Mangement Address Register */ | |
121 | tmp_reg = ((u32) mii_id << MIIMADD_PHY_ADDRESS_SHIFT) | mii_reg; | |
122 | out_be32 (&ug_regs->miimadd, tmp_reg); | |
7737d5c6 | 123 | |
dd520bf3 WD |
124 | /* Setting up the MII Mangement Control Register with the value */ |
125 | out_be32 (&ug_regs->miimcon, (u32) value); | |
ee62ed32 | 126 | sync(); |
7737d5c6 | 127 | |
dd520bf3 WD |
128 | /* Wait till MII management write is complete */ |
129 | while ((in_be32 (&ug_regs->miimind)) & MIIMIND_BUSY); | |
7737d5c6 DL |
130 | } |
131 | ||
132 | /* Reads from register regnum in the PHY for device dev, */ | |
133 | /* returning the value. Clears miimcom first. All PHY */ | |
134 | /* configuration has to be done through the TSEC1 MIIM regs */ | |
da9d4610 | 135 | int uec_read_phy_reg (struct eth_device *dev, int mii_id, int regnum) |
7737d5c6 | 136 | { |
dd520bf3 | 137 | uec_private_t *ugeth = (uec_private_t *) dev->priv; |
da9d4610 | 138 | uec_mii_t *ug_regs; |
dd520bf3 WD |
139 | enet_tbi_mii_reg_e mii_reg = (enet_tbi_mii_reg_e) regnum; |
140 | u32 tmp_reg; | |
141 | u16 value; | |
7737d5c6 | 142 | |
da9d4610 | 143 | ug_regs = ugeth->uec_mii_regs; |
7737d5c6 | 144 | |
dd520bf3 WD |
145 | /* Setting up the MII Mangement Address Register */ |
146 | tmp_reg = ((u32) mii_id << MIIMADD_PHY_ADDRESS_SHIFT) | mii_reg; | |
147 | out_be32 (&ug_regs->miimadd, tmp_reg); | |
7737d5c6 | 148 | |
ee62ed32 | 149 | /* clear MII management command cycle */ |
dd520bf3 | 150 | out_be32 (&ug_regs->miimcom, 0); |
ee62ed32 KP |
151 | sync(); |
152 | ||
153 | /* Perform an MII management read cycle */ | |
dd520bf3 | 154 | out_be32 (&ug_regs->miimcom, MIIMCOM_READ_CYCLE); |
7737d5c6 | 155 | |
dd520bf3 WD |
156 | /* Wait till MII management write is complete */ |
157 | while ((in_be32 (&ug_regs->miimind)) & | |
158 | (MIIMIND_NOT_VALID | MIIMIND_BUSY)); | |
7737d5c6 | 159 | |
dd520bf3 WD |
160 | /* Read MII management status */ |
161 | value = (u16) in_be32 (&ug_regs->miimstat); | |
162 | if (value == 0xffff) | |
84a3047b | 163 | ugphy_vdbg |
dd520bf3 WD |
164 | ("read wrong value : mii_id %d,mii_reg %d, base %08x", |
165 | mii_id, mii_reg, (u32) & (ug_regs->miimcfg)); | |
7737d5c6 | 166 | |
dd520bf3 | 167 | return (value); |
7737d5c6 DL |
168 | } |
169 | ||
dd520bf3 | 170 | void mii_clear_phy_interrupt (struct uec_mii_info *mii_info) |
7737d5c6 | 171 | { |
dd520bf3 WD |
172 | if (mii_info->phyinfo->ack_interrupt) |
173 | mii_info->phyinfo->ack_interrupt (mii_info); | |
7737d5c6 DL |
174 | } |
175 | ||
dd520bf3 WD |
176 | void mii_configure_phy_interrupt (struct uec_mii_info *mii_info, |
177 | u32 interrupts) | |
7737d5c6 | 178 | { |
dd520bf3 WD |
179 | mii_info->interrupts = interrupts; |
180 | if (mii_info->phyinfo->config_intr) | |
181 | mii_info->phyinfo->config_intr (mii_info); | |
7737d5c6 DL |
182 | } |
183 | ||
184 | /* Writes MII_ADVERTISE with the appropriate values, after | |
185 | * sanitizing advertise to make sure only supported features | |
186 | * are advertised | |
187 | */ | |
dd520bf3 | 188 | static void config_genmii_advert (struct uec_mii_info *mii_info) |
7737d5c6 | 189 | { |
dd520bf3 WD |
190 | u32 advertise; |
191 | u16 adv; | |
192 | ||
193 | /* Only allow advertising what this PHY supports */ | |
194 | mii_info->advertising &= mii_info->phyinfo->features; | |
195 | advertise = mii_info->advertising; | |
196 | ||
197 | /* Setup standard advertisement */ | |
198 | adv = phy_read (mii_info, PHY_ANAR); | |
199 | adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4); | |
200 | if (advertise & ADVERTISED_10baseT_Half) | |
201 | adv |= ADVERTISE_10HALF; | |
202 | if (advertise & ADVERTISED_10baseT_Full) | |
203 | adv |= ADVERTISE_10FULL; | |
204 | if (advertise & ADVERTISED_100baseT_Half) | |
205 | adv |= ADVERTISE_100HALF; | |
206 | if (advertise & ADVERTISED_100baseT_Full) | |
207 | adv |= ADVERTISE_100FULL; | |
208 | phy_write (mii_info, PHY_ANAR, adv); | |
7737d5c6 DL |
209 | } |
210 | ||
dd520bf3 | 211 | static void genmii_setup_forced (struct uec_mii_info *mii_info) |
7737d5c6 | 212 | { |
dd520bf3 WD |
213 | u16 ctrl; |
214 | u32 features = mii_info->phyinfo->features; | |
215 | ||
216 | ctrl = phy_read (mii_info, PHY_BMCR); | |
217 | ||
218 | ctrl &= ~(PHY_BMCR_DPLX | PHY_BMCR_100_MBPS | | |
219 | PHY_BMCR_1000_MBPS | PHY_BMCR_AUTON); | |
220 | ctrl |= PHY_BMCR_RESET; | |
221 | ||
222 | switch (mii_info->speed) { | |
223 | case SPEED_1000: | |
224 | if (features & (SUPPORTED_1000baseT_Half | |
225 | | SUPPORTED_1000baseT_Full)) { | |
226 | ctrl |= PHY_BMCR_1000_MBPS; | |
227 | break; | |
228 | } | |
229 | mii_info->speed = SPEED_100; | |
230 | case SPEED_100: | |
231 | if (features & (SUPPORTED_100baseT_Half | |
232 | | SUPPORTED_100baseT_Full)) { | |
233 | ctrl |= PHY_BMCR_100_MBPS; | |
234 | break; | |
235 | } | |
236 | mii_info->speed = SPEED_10; | |
237 | case SPEED_10: | |
238 | if (features & (SUPPORTED_10baseT_Half | |
239 | | SUPPORTED_10baseT_Full)) | |
240 | break; | |
241 | default: /* Unsupported speed! */ | |
242 | ugphy_err ("%s: Bad speed!", mii_info->dev->name); | |
243 | break; | |
244 | } | |
245 | ||
246 | phy_write (mii_info, PHY_BMCR, ctrl); | |
7737d5c6 DL |
247 | } |
248 | ||
249 | /* Enable and Restart Autonegotiation */ | |
dd520bf3 | 250 | static void genmii_restart_aneg (struct uec_mii_info *mii_info) |
7737d5c6 | 251 | { |
dd520bf3 | 252 | u16 ctl; |
7737d5c6 | 253 | |
dd520bf3 WD |
254 | ctl = phy_read (mii_info, PHY_BMCR); |
255 | ctl |= (PHY_BMCR_AUTON | PHY_BMCR_RST_NEG); | |
256 | phy_write (mii_info, PHY_BMCR, ctl); | |
7737d5c6 DL |
257 | } |
258 | ||
dd520bf3 | 259 | static int gbit_config_aneg (struct uec_mii_info *mii_info) |
7737d5c6 | 260 | { |
dd520bf3 WD |
261 | u16 adv; |
262 | u32 advertise; | |
263 | ||
264 | if (mii_info->autoneg) { | |
265 | /* Configure the ADVERTISE register */ | |
266 | config_genmii_advert (mii_info); | |
267 | advertise = mii_info->advertising; | |
268 | ||
269 | adv = phy_read (mii_info, MII_1000BASETCONTROL); | |
270 | adv &= ~(MII_1000BASETCONTROL_FULLDUPLEXCAP | | |
271 | MII_1000BASETCONTROL_HALFDUPLEXCAP); | |
272 | if (advertise & SUPPORTED_1000baseT_Half) | |
273 | adv |= MII_1000BASETCONTROL_HALFDUPLEXCAP; | |
274 | if (advertise & SUPPORTED_1000baseT_Full) | |
275 | adv |= MII_1000BASETCONTROL_FULLDUPLEXCAP; | |
276 | phy_write (mii_info, MII_1000BASETCONTROL, adv); | |
277 | ||
278 | /* Start/Restart aneg */ | |
279 | genmii_restart_aneg (mii_info); | |
280 | } else | |
281 | genmii_setup_forced (mii_info); | |
282 | ||
283 | return 0; | |
7737d5c6 DL |
284 | } |
285 | ||
dd520bf3 | 286 | static int marvell_config_aneg (struct uec_mii_info *mii_info) |
7737d5c6 | 287 | { |
dd520bf3 WD |
288 | /* The Marvell PHY has an errata which requires |
289 | * that certain registers get written in order | |
290 | * to restart autonegotiation */ | |
291 | phy_write (mii_info, PHY_BMCR, PHY_BMCR_RESET); | |
7737d5c6 | 292 | |
dd520bf3 WD |
293 | phy_write (mii_info, 0x1d, 0x1f); |
294 | phy_write (mii_info, 0x1e, 0x200c); | |
295 | phy_write (mii_info, 0x1d, 0x5); | |
296 | phy_write (mii_info, 0x1e, 0); | |
297 | phy_write (mii_info, 0x1e, 0x100); | |
7737d5c6 | 298 | |
dd520bf3 | 299 | gbit_config_aneg (mii_info); |
7737d5c6 | 300 | |
dd520bf3 | 301 | return 0; |
7737d5c6 DL |
302 | } |
303 | ||
dd520bf3 | 304 | static int genmii_config_aneg (struct uec_mii_info *mii_info) |
7737d5c6 | 305 | { |
dd520bf3 WD |
306 | if (mii_info->autoneg) { |
307 | config_genmii_advert (mii_info); | |
308 | genmii_restart_aneg (mii_info); | |
309 | } else | |
310 | genmii_setup_forced (mii_info); | |
7737d5c6 | 311 | |
dd520bf3 | 312 | return 0; |
7737d5c6 DL |
313 | } |
314 | ||
dd520bf3 | 315 | static int genmii_update_link (struct uec_mii_info *mii_info) |
7737d5c6 | 316 | { |
dd520bf3 | 317 | u16 status; |
7737d5c6 | 318 | |
ee62ed32 | 319 | /* Status is read once to clear old link state */ |
dd520bf3 | 320 | phy_read (mii_info, PHY_BMSR); |
7737d5c6 | 321 | |
ee62ed32 KP |
322 | /* |
323 | * Wait if the link is up, and autonegotiation is in progress | |
324 | * (ie - we're capable and it's not done) | |
325 | */ | |
326 | status = phy_read(mii_info, PHY_BMSR); | |
327 | if ((status & PHY_BMSR_LS) && (status & PHY_BMSR_AUTN_ABLE) | |
328 | && !(status & PHY_BMSR_AUTN_COMP)) { | |
329 | int i = 0; | |
330 | ||
331 | while (!(status & PHY_BMSR_AUTN_COMP)) { | |
332 | /* | |
333 | * Timeout reached ? | |
334 | */ | |
335 | if (i > UGETH_AN_TIMEOUT) { | |
336 | mii_info->link = 0; | |
337 | return 0; | |
338 | } | |
339 | ||
f30b6154 | 340 | i++; |
ee62ed32 KP |
341 | udelay(1000); /* 1 ms */ |
342 | status = phy_read(mii_info, PHY_BMSR); | |
343 | } | |
dd520bf3 | 344 | mii_info->link = 1; |
ee62ed32 KP |
345 | udelay(500000); /* another 500 ms (results in faster booting) */ |
346 | } else { | |
347 | if (status & PHY_BMSR_LS) | |
348 | mii_info->link = 1; | |
349 | else | |
350 | mii_info->link = 0; | |
351 | } | |
7737d5c6 | 352 | |
dd520bf3 | 353 | return 0; |
7737d5c6 DL |
354 | } |
355 | ||
dd520bf3 | 356 | static int genmii_read_status (struct uec_mii_info *mii_info) |
7737d5c6 | 357 | { |
dd520bf3 WD |
358 | u16 status; |
359 | int err; | |
360 | ||
361 | /* Update the link, but return if there | |
362 | * was an error */ | |
363 | err = genmii_update_link (mii_info); | |
364 | if (err) | |
365 | return err; | |
366 | ||
367 | if (mii_info->autoneg) { | |
91cdaa3a AV |
368 | status = phy_read(mii_info, MII_1000BASETSTATUS); |
369 | ||
370 | if (status & (LPA_1000FULL | LPA_1000HALF)) { | |
371 | mii_info->speed = SPEED_1000; | |
372 | if (status & LPA_1000FULL) | |
373 | mii_info->duplex = DUPLEX_FULL; | |
374 | else | |
375 | mii_info->duplex = DUPLEX_HALF; | |
376 | } else { | |
377 | status = phy_read(mii_info, PHY_ANLPAR); | |
378 | ||
379 | if (status & (PHY_ANLPAR_10FD | PHY_ANLPAR_TXFD)) | |
380 | mii_info->duplex = DUPLEX_FULL; | |
381 | else | |
382 | mii_info->duplex = DUPLEX_HALF; | |
383 | if (status & (PHY_ANLPAR_TXFD | PHY_ANLPAR_TX)) | |
384 | mii_info->speed = SPEED_100; | |
385 | else | |
386 | mii_info->speed = SPEED_10; | |
387 | } | |
dd520bf3 WD |
388 | mii_info->pause = 0; |
389 | } | |
390 | /* On non-aneg, we assume what we put in BMCR is the speed, | |
391 | * though magic-aneg shouldn't prevent this case from occurring | |
392 | */ | |
393 | ||
394 | return 0; | |
7737d5c6 DL |
395 | } |
396 | ||
300615dc AV |
397 | static int bcm_init(struct uec_mii_info *mii_info) |
398 | { | |
399 | struct eth_device *edev = mii_info->dev; | |
400 | uec_private_t *uec = edev->priv; | |
401 | ||
402 | gbit_config_aneg(mii_info); | |
403 | ||
582c55a0 HS |
404 | if ((uec->uec_info->enet_interface_type == RGMII_RXID) && |
405 | (uec->uec_info->speed == 1000)) { | |
300615dc AV |
406 | u16 val; |
407 | int cnt = 50; | |
408 | ||
409 | /* Wait for aneg to complete. */ | |
410 | do | |
411 | val = phy_read(mii_info, PHY_BMSR); | |
412 | while (--cnt && !(val & PHY_BMSR_AUTN_COMP)); | |
413 | ||
414 | /* Set RDX clk delay. */ | |
415 | phy_write(mii_info, 0x18, 0x7 | (7 << 12)); | |
416 | ||
417 | val = phy_read(mii_info, 0x18); | |
418 | /* Set RDX-RXC skew. */ | |
419 | val |= (1 << 8); | |
420 | val |= (7 | (7 << 12)); | |
421 | /* Write bits 14:0. */ | |
422 | val |= (1 << 15); | |
423 | phy_write(mii_info, 0x18, val); | |
424 | } | |
425 | ||
426 | return 0; | |
427 | } | |
428 | ||
41410eee HW |
429 | static int marvell_init(struct uec_mii_info *mii_info) |
430 | { | |
431 | struct eth_device *edev = mii_info->dev; | |
432 | uec_private_t *uec = edev->priv; | |
582c55a0 HS |
433 | enum enet_interface_type iface = uec->uec_info->enet_interface_type; |
434 | int speed = uec->uec_info->speed; | |
41410eee | 435 | |
582c55a0 HS |
436 | if ((speed == 1000) && |
437 | (iface == RGMII_ID || | |
438 | iface == RGMII_RXID || | |
439 | iface == RGMII_TXID)) { | |
41410eee HW |
440 | int temp; |
441 | ||
442 | temp = phy_read(mii_info, MII_M1111_PHY_EXT_CR); | |
582c55a0 | 443 | if (iface == RGMII_ID) { |
6185f80c | 444 | temp |= MII_M1111_RX_DELAY | MII_M1111_TX_DELAY; |
582c55a0 | 445 | } else if (iface == RGMII_RXID) { |
6185f80c AV |
446 | temp &= ~MII_M1111_TX_DELAY; |
447 | temp |= MII_M1111_RX_DELAY; | |
582c55a0 | 448 | } else if (iface == RGMII_TXID) { |
6185f80c AV |
449 | temp &= ~MII_M1111_RX_DELAY; |
450 | temp |= MII_M1111_TX_DELAY; | |
451 | } | |
41410eee HW |
452 | phy_write(mii_info, MII_M1111_PHY_EXT_CR, temp); |
453 | ||
454 | temp = phy_read(mii_info, MII_M1111_PHY_EXT_SR); | |
455 | temp &= ~MII_M1111_HWCFG_MODE_MASK; | |
456 | temp |= MII_M1111_HWCFG_MODE_RGMII; | |
457 | phy_write(mii_info, MII_M1111_PHY_EXT_SR, temp); | |
458 | ||
459 | phy_write(mii_info, PHY_BMCR, PHY_BMCR_RESET); | |
460 | } | |
461 | ||
462 | return 0; | |
463 | } | |
464 | ||
dd520bf3 | 465 | static int marvell_read_status (struct uec_mii_info *mii_info) |
7737d5c6 | 466 | { |
dd520bf3 WD |
467 | u16 status; |
468 | int err; | |
469 | ||
470 | /* Update the link, but return if there | |
471 | * was an error */ | |
472 | err = genmii_update_link (mii_info); | |
473 | if (err) | |
474 | return err; | |
475 | ||
476 | /* If the link is up, read the speed and duplex */ | |
477 | /* If we aren't autonegotiating, assume speeds | |
478 | * are as set */ | |
479 | if (mii_info->autoneg && mii_info->link) { | |
480 | int speed; | |
481 | ||
482 | status = phy_read (mii_info, MII_M1011_PHY_SPEC_STATUS); | |
483 | ||
484 | /* Get the duplexity */ | |
485 | if (status & MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX) | |
486 | mii_info->duplex = DUPLEX_FULL; | |
487 | else | |
488 | mii_info->duplex = DUPLEX_HALF; | |
489 | ||
490 | /* Get the speed */ | |
491 | speed = status & MII_M1011_PHY_SPEC_STATUS_SPD_MASK; | |
492 | switch (speed) { | |
493 | case MII_M1011_PHY_SPEC_STATUS_1000: | |
494 | mii_info->speed = SPEED_1000; | |
495 | break; | |
496 | case MII_M1011_PHY_SPEC_STATUS_100: | |
497 | mii_info->speed = SPEED_100; | |
498 | break; | |
499 | default: | |
500 | mii_info->speed = SPEED_10; | |
501 | break; | |
502 | } | |
503 | mii_info->pause = 0; | |
504 | } | |
505 | ||
506 | return 0; | |
7737d5c6 DL |
507 | } |
508 | ||
dd520bf3 | 509 | static int marvell_ack_interrupt (struct uec_mii_info *mii_info) |
7737d5c6 | 510 | { |
dd520bf3 WD |
511 | /* Clear the interrupts by reading the reg */ |
512 | phy_read (mii_info, MII_M1011_IEVENT); | |
7737d5c6 | 513 | |
dd520bf3 | 514 | return 0; |
7737d5c6 DL |
515 | } |
516 | ||
dd520bf3 | 517 | static int marvell_config_intr (struct uec_mii_info *mii_info) |
7737d5c6 | 518 | { |
dd520bf3 WD |
519 | if (mii_info->interrupts == MII_INTERRUPT_ENABLED) |
520 | phy_write (mii_info, MII_M1011_IMASK, MII_M1011_IMASK_INIT); | |
521 | else | |
522 | phy_write (mii_info, MII_M1011_IMASK, MII_M1011_IMASK_CLEAR); | |
7737d5c6 | 523 | |
dd520bf3 | 524 | return 0; |
7737d5c6 DL |
525 | } |
526 | ||
dd520bf3 | 527 | static int dm9161_init (struct uec_mii_info *mii_info) |
7737d5c6 | 528 | { |
dd520bf3 WD |
529 | /* Reset the PHY */ |
530 | phy_write (mii_info, PHY_BMCR, phy_read (mii_info, PHY_BMCR) | | |
531 | PHY_BMCR_RESET); | |
532 | /* PHY and MAC connect */ | |
533 | phy_write (mii_info, PHY_BMCR, phy_read (mii_info, PHY_BMCR) & | |
534 | ~PHY_BMCR_ISO); | |
ee62ed32 | 535 | |
dd520bf3 | 536 | phy_write (mii_info, MII_DM9161_SCR, MII_DM9161_SCR_INIT); |
ee62ed32 | 537 | |
dd520bf3 WD |
538 | config_genmii_advert (mii_info); |
539 | /* Start/restart aneg */ | |
540 | genmii_config_aneg (mii_info); | |
7737d5c6 | 541 | |
dd520bf3 | 542 | return 0; |
7737d5c6 DL |
543 | } |
544 | ||
dd520bf3 | 545 | static int dm9161_config_aneg (struct uec_mii_info *mii_info) |
7737d5c6 | 546 | { |
dd520bf3 | 547 | return 0; |
7737d5c6 DL |
548 | } |
549 | ||
dd520bf3 | 550 | static int dm9161_read_status (struct uec_mii_info *mii_info) |
7737d5c6 | 551 | { |
dd520bf3 WD |
552 | u16 status; |
553 | int err; | |
554 | ||
555 | /* Update the link, but return if there was an error */ | |
556 | err = genmii_update_link (mii_info); | |
557 | if (err) | |
558 | return err; | |
559 | /* If the link is up, read the speed and duplex | |
560 | If we aren't autonegotiating assume speeds are as set */ | |
561 | if (mii_info->autoneg && mii_info->link) { | |
562 | status = phy_read (mii_info, MII_DM9161_SCSR); | |
563 | if (status & (MII_DM9161_SCSR_100F | MII_DM9161_SCSR_100H)) | |
564 | mii_info->speed = SPEED_100; | |
565 | else | |
566 | mii_info->speed = SPEED_10; | |
567 | ||
568 | if (status & (MII_DM9161_SCSR_100F | MII_DM9161_SCSR_10F)) | |
569 | mii_info->duplex = DUPLEX_FULL; | |
570 | else | |
571 | mii_info->duplex = DUPLEX_HALF; | |
572 | } | |
573 | ||
574 | return 0; | |
7737d5c6 DL |
575 | } |
576 | ||
dd520bf3 | 577 | static int dm9161_ack_interrupt (struct uec_mii_info *mii_info) |
7737d5c6 | 578 | { |
dd520bf3 WD |
579 | /* Clear the interrupt by reading the reg */ |
580 | phy_read (mii_info, MII_DM9161_INTR); | |
7737d5c6 | 581 | |
dd520bf3 | 582 | return 0; |
7737d5c6 DL |
583 | } |
584 | ||
dd520bf3 | 585 | static int dm9161_config_intr (struct uec_mii_info *mii_info) |
7737d5c6 | 586 | { |
dd520bf3 WD |
587 | if (mii_info->interrupts == MII_INTERRUPT_ENABLED) |
588 | phy_write (mii_info, MII_DM9161_INTR, MII_DM9161_INTR_INIT); | |
589 | else | |
590 | phy_write (mii_info, MII_DM9161_INTR, MII_DM9161_INTR_STOP); | |
7737d5c6 | 591 | |
dd520bf3 | 592 | return 0; |
7737d5c6 DL |
593 | } |
594 | ||
dd520bf3 | 595 | static void dm9161_close (struct uec_mii_info *mii_info) |
7737d5c6 DL |
596 | { |
597 | } | |
598 | ||
edf3fe7d RR |
599 | static int fixed_phy_aneg (struct uec_mii_info *mii_info) |
600 | { | |
601 | mii_info->autoneg = 0; /* Turn off auto negotiation for fixed phy */ | |
602 | return 0; | |
603 | } | |
604 | ||
605 | static int fixed_phy_read_status (struct uec_mii_info *mii_info) | |
606 | { | |
607 | int i = 0; | |
608 | ||
609 | for (i = 0; i < ARRAY_SIZE(fixed_phy_port); i++) { | |
1443cd7e RR |
610 | if (strncmp(mii_info->dev->name, fixed_phy_port[i].name, |
611 | strlen(mii_info->dev->name)) == 0) { | |
edf3fe7d RR |
612 | mii_info->speed = fixed_phy_port[i].speed; |
613 | mii_info->duplex = fixed_phy_port[i].duplex; | |
614 | mii_info->link = 1; /* Link is always UP */ | |
615 | mii_info->pause = 0; | |
616 | break; | |
617 | } | |
618 | } | |
619 | return 0; | |
620 | } | |
621 | ||
8b69b563 HS |
622 | static int smsc_config_aneg (struct uec_mii_info *mii_info) |
623 | { | |
624 | return 0; | |
625 | } | |
626 | ||
627 | static int smsc_read_status (struct uec_mii_info *mii_info) | |
628 | { | |
629 | u16 status; | |
630 | int err; | |
631 | ||
632 | /* Update the link, but return if there | |
633 | * was an error */ | |
634 | err = genmii_update_link (mii_info); | |
635 | if (err) | |
636 | return err; | |
637 | ||
638 | /* If the link is up, read the speed and duplex */ | |
639 | /* If we aren't autonegotiating, assume speeds | |
640 | * are as set */ | |
641 | if (mii_info->autoneg && mii_info->link) { | |
642 | int val; | |
643 | ||
644 | status = phy_read (mii_info, 0x1f); | |
645 | val = (status & 0x1c) >> 2; | |
646 | ||
647 | switch (val) { | |
648 | case 1: | |
649 | mii_info->duplex = DUPLEX_HALF; | |
650 | mii_info->speed = SPEED_10; | |
651 | break; | |
652 | case 5: | |
653 | mii_info->duplex = DUPLEX_FULL; | |
654 | mii_info->speed = SPEED_10; | |
655 | break; | |
656 | case 2: | |
657 | mii_info->duplex = DUPLEX_HALF; | |
658 | mii_info->speed = SPEED_100; | |
659 | break; | |
660 | case 6: | |
661 | mii_info->duplex = DUPLEX_FULL; | |
662 | mii_info->speed = SPEED_100; | |
663 | break; | |
664 | } | |
665 | mii_info->pause = 0; | |
666 | } | |
667 | ||
668 | return 0; | |
669 | } | |
670 | ||
7737d5c6 | 671 | static struct phy_info phy_info_dm9161 = { |
dd520bf3 WD |
672 | .phy_id = 0x0181b880, |
673 | .phy_id_mask = 0x0ffffff0, | |
674 | .name = "Davicom DM9161E", | |
675 | .init = dm9161_init, | |
676 | .config_aneg = dm9161_config_aneg, | |
677 | .read_status = dm9161_read_status, | |
678 | .close = dm9161_close, | |
7737d5c6 DL |
679 | }; |
680 | ||
681 | static struct phy_info phy_info_dm9161a = { | |
dd520bf3 WD |
682 | .phy_id = 0x0181b8a0, |
683 | .phy_id_mask = 0x0ffffff0, | |
684 | .name = "Davicom DM9161A", | |
685 | .features = MII_BASIC_FEATURES, | |
686 | .init = dm9161_init, | |
687 | .config_aneg = dm9161_config_aneg, | |
688 | .read_status = dm9161_read_status, | |
689 | .ack_interrupt = dm9161_ack_interrupt, | |
690 | .config_intr = dm9161_config_intr, | |
691 | .close = dm9161_close, | |
7737d5c6 DL |
692 | }; |
693 | ||
694 | static struct phy_info phy_info_marvell = { | |
dd520bf3 WD |
695 | .phy_id = 0x01410c00, |
696 | .phy_id_mask = 0xffffff00, | |
697 | .name = "Marvell 88E11x1", | |
698 | .features = MII_GBIT_FEATURES, | |
41410eee | 699 | .init = &marvell_init, |
dd520bf3 WD |
700 | .config_aneg = &marvell_config_aneg, |
701 | .read_status = &marvell_read_status, | |
702 | .ack_interrupt = &marvell_ack_interrupt, | |
703 | .config_intr = &marvell_config_intr, | |
7737d5c6 DL |
704 | }; |
705 | ||
300615dc AV |
706 | static struct phy_info phy_info_bcm5481 = { |
707 | .phy_id = 0x0143bca0, | |
708 | .phy_id_mask = 0xffffff0, | |
709 | .name = "Broadcom 5481", | |
710 | .features = MII_GBIT_FEATURES, | |
711 | .read_status = genmii_read_status, | |
712 | .init = bcm_init, | |
713 | }; | |
714 | ||
edf3fe7d RR |
715 | static struct phy_info phy_info_fixedphy = { |
716 | .phy_id = CONFIG_FIXED_PHY, | |
717 | .phy_id_mask = CONFIG_FIXED_PHY, | |
718 | .name = "Fixed PHY", | |
719 | .config_aneg = fixed_phy_aneg, | |
720 | .read_status = fixed_phy_read_status, | |
721 | }; | |
722 | ||
8b69b563 HS |
723 | static struct phy_info phy_info_smsclan8700 = { |
724 | .phy_id = 0x0007c0c0, | |
725 | .phy_id_mask = 0xfffffff0, | |
726 | .name = "SMSC LAN8700", | |
727 | .features = MII_BASIC_FEATURES, | |
728 | .config_aneg = smsc_config_aneg, | |
729 | .read_status = smsc_read_status, | |
730 | }; | |
731 | ||
dd520bf3 WD |
732 | static struct phy_info phy_info_genmii = { |
733 | .phy_id = 0x00000000, | |
734 | .phy_id_mask = 0x00000000, | |
735 | .name = "Generic MII", | |
736 | .features = MII_BASIC_FEATURES, | |
737 | .config_aneg = genmii_config_aneg, | |
738 | .read_status = genmii_read_status, | |
7737d5c6 DL |
739 | }; |
740 | ||
741 | static struct phy_info *phy_info[] = { | |
dd520bf3 WD |
742 | &phy_info_dm9161, |
743 | &phy_info_dm9161a, | |
744 | &phy_info_marvell, | |
300615dc | 745 | &phy_info_bcm5481, |
8b69b563 | 746 | &phy_info_smsclan8700, |
edf3fe7d | 747 | &phy_info_fixedphy, |
dd520bf3 WD |
748 | &phy_info_genmii, |
749 | NULL | |
7737d5c6 DL |
750 | }; |
751 | ||
dd520bf3 | 752 | u16 phy_read (struct uec_mii_info *mii_info, u16 regnum) |
7737d5c6 | 753 | { |
dd520bf3 | 754 | return mii_info->mdio_read (mii_info->dev, mii_info->mii_id, regnum); |
7737d5c6 DL |
755 | } |
756 | ||
dd520bf3 | 757 | void phy_write (struct uec_mii_info *mii_info, u16 regnum, u16 val) |
7737d5c6 | 758 | { |
dd520bf3 | 759 | mii_info->mdio_write (mii_info->dev, mii_info->mii_id, regnum, val); |
7737d5c6 DL |
760 | } |
761 | ||
762 | /* Use the PHY ID registers to determine what type of PHY is attached | |
763 | * to device dev. return a struct phy_info structure describing that PHY | |
764 | */ | |
da9d4610 | 765 | struct phy_info *uec_get_phy_info (struct uec_mii_info *mii_info) |
7737d5c6 | 766 | { |
dd520bf3 WD |
767 | u16 phy_reg; |
768 | u32 phy_ID; | |
769 | int i; | |
770 | struct phy_info *theInfo = NULL; | |
771 | ||
772 | /* Grab the bits from PHYIR1, and put them in the upper half */ | |
773 | phy_reg = phy_read (mii_info, PHY_PHYIDR1); | |
774 | phy_ID = (phy_reg & 0xffff) << 16; | |
775 | ||
776 | /* Grab the bits from PHYIR2, and put them in the lower half */ | |
777 | phy_reg = phy_read (mii_info, PHY_PHYIDR2); | |
778 | phy_ID |= (phy_reg & 0xffff); | |
779 | ||
780 | /* loop through all the known PHY types, and find one that */ | |
781 | /* matches the ID we read from the PHY. */ | |
782 | for (i = 0; phy_info[i]; i++) | |
783 | if (phy_info[i]->phy_id == | |
784 | (phy_ID & phy_info[i]->phy_id_mask)) { | |
785 | theInfo = phy_info[i]; | |
786 | break; | |
787 | } | |
788 | ||
789 | /* This shouldn't happen, as we have generic PHY support */ | |
790 | if (theInfo == NULL) { | |
791 | ugphy_info ("UEC: PHY id %x is not supported!", phy_ID); | |
792 | return NULL; | |
793 | } else { | |
794 | ugphy_info ("UEC: PHY is %s (%x)", theInfo->name, phy_ID); | |
795 | } | |
796 | ||
797 | return theInfo; | |
7737d5c6 DL |
798 | } |
799 | ||
dd520bf3 | 800 | void marvell_phy_interface_mode (struct eth_device *dev, |
582c55a0 HS |
801 | enet_interface_type_e type, |
802 | int speed | |
803 | ) | |
7737d5c6 | 804 | { |
dd520bf3 WD |
805 | uec_private_t *uec = (uec_private_t *) dev->priv; |
806 | struct uec_mii_info *mii_info; | |
f655adef | 807 | u16 status; |
7737d5c6 DL |
808 | |
809 | if (!uec->mii_info) { | |
f30b6154 | 810 | printf ("%s: the PHY not initialized\n", __FUNCTION__); |
7737d5c6 DL |
811 | return; |
812 | } | |
813 | mii_info = uec->mii_info; | |
814 | ||
582c55a0 HS |
815 | if (type == RGMII) { |
816 | if (speed == 100) { | |
817 | phy_write (mii_info, 0x00, 0x9140); | |
818 | phy_write (mii_info, 0x1d, 0x001f); | |
819 | phy_write (mii_info, 0x1e, 0x200c); | |
820 | phy_write (mii_info, 0x1d, 0x0005); | |
821 | phy_write (mii_info, 0x1e, 0x0000); | |
822 | phy_write (mii_info, 0x1e, 0x0100); | |
823 | phy_write (mii_info, 0x09, 0x0e00); | |
824 | phy_write (mii_info, 0x04, 0x01e1); | |
825 | phy_write (mii_info, 0x00, 0x9140); | |
826 | phy_write (mii_info, 0x00, 0x1000); | |
827 | udelay (100000); | |
828 | phy_write (mii_info, 0x00, 0x2900); | |
829 | phy_write (mii_info, 0x14, 0x0cd2); | |
830 | phy_write (mii_info, 0x00, 0xa100); | |
831 | phy_write (mii_info, 0x09, 0x0000); | |
832 | phy_write (mii_info, 0x1b, 0x800b); | |
833 | phy_write (mii_info, 0x04, 0x05e1); | |
834 | phy_write (mii_info, 0x00, 0xa100); | |
835 | phy_write (mii_info, 0x00, 0x2100); | |
836 | udelay (1000000); | |
837 | } else if (speed == 10) { | |
838 | phy_write (mii_info, 0x14, 0x8e40); | |
839 | phy_write (mii_info, 0x1b, 0x800b); | |
840 | phy_write (mii_info, 0x14, 0x0c82); | |
841 | phy_write (mii_info, 0x00, 0x8100); | |
842 | udelay (1000000); | |
843 | } | |
7737d5c6 | 844 | } |
f655adef KP |
845 | |
846 | /* handle 88e1111 rev.B2 erratum 5.6 */ | |
847 | if (mii_info->autoneg) { | |
848 | status = phy_read (mii_info, PHY_BMCR); | |
849 | phy_write (mii_info, PHY_BMCR, status | PHY_BMCR_AUTON); | |
850 | } | |
851 | /* now the B2 will correctly report autoneg completion status */ | |
7737d5c6 DL |
852 | } |
853 | ||
582c55a0 HS |
854 | void change_phy_interface_mode (struct eth_device *dev, |
855 | enet_interface_type_e type, int speed) | |
7737d5c6 DL |
856 | { |
857 | #ifdef CONFIG_PHY_MODE_NEED_CHANGE | |
582c55a0 | 858 | marvell_phy_interface_mode (dev, type, speed); |
7737d5c6 DL |
859 | #endif |
860 | } |