aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ucc_geth_phy.c
diff options
context:
space:
mode:
authorLi Yang <leoli@freescale.com>2006-08-15 02:00:11 -0400
committerJeff Garzik <jeff@garzik.org>2006-08-19 17:44:29 -0400
commitce973b141dfac4a0f160c7435d65e3ea47753ce8 (patch)
tree2dfda8aa29023fca37e1f5127d2e5e6fae1c6180 /drivers/net/ucc_geth_phy.c
parente4c780b1ffc7d7bc27b7dc57fcf17ebb8d3006bc (diff)
[PATCH] Freescale QE UCC gigabit ethernet driver
QE(QUICC Engine) is a new generation communication coprocessor, which can be found on some of the latest Freescale PowerQUICC CPUs(e.g. MPC8360). The UCC(Unified Communications Controller) module of QE can work as gigabit Ethernet device. This patch provides driver for the device. Signed-off-by: Shlomi Gridish <gridish@freescale.com> Signed-off-by: Li Yang <leoli@freescale.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/net/ucc_geth_phy.c')
-rw-r--r--drivers/net/ucc_geth_phy.c801
1 files changed, 801 insertions, 0 deletions
diff --git a/drivers/net/ucc_geth_phy.c b/drivers/net/ucc_geth_phy.c
new file mode 100644
index 000000000000..f91028c5386d
--- /dev/null
+++ b/drivers/net/ucc_geth_phy.c
@@ -0,0 +1,801 @@
1/*
2 * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights reserved.
3 *
4 * Author: Shlomi Gridish <gridish@freescale.com>
5 *
6 * Description:
7 * UCC GETH Driver -- PHY handling
8 *
9 * Changelog:
10 * Jun 28, 2006 Li Yang <LeoLi@freescale.com>
11 * - Rearrange code and style fixes
12 *
13 * This program is free software; you can redistribute it and/or modify it
14 * under the terms of the GNU General Public License as published by the
15 * Free Software Foundation; either version 2 of the License, or (at your
16 * option) any later version.
17 *
18 */
19
20#include <linux/config.h>
21#include <linux/kernel.h>
22#include <linux/sched.h>
23#include <linux/string.h>
24#include <linux/errno.h>
25#include <linux/slab.h>
26#include <linux/interrupt.h>
27#include <linux/init.h>
28#include <linux/delay.h>
29#include <linux/netdevice.h>
30#include <linux/etherdevice.h>
31#include <linux/skbuff.h>
32#include <linux/spinlock.h>
33#include <linux/mm.h>
34#include <linux/module.h>
35#include <linux/version.h>
36#include <linux/crc32.h>
37#include <linux/mii.h>
38#include <linux/ethtool.h>
39
40#include <asm/io.h>
41#include <asm/irq.h>
42#include <asm/uaccess.h>
43
44#include "ucc_geth.h"
45#include "ucc_geth_phy.h"
46#include <platforms/83xx/mpc8360e_pb.h>
47
48#define ugphy_printk(level, format, arg...) \
49 printk(level format "\n", ## arg)
50
51#define ugphy_dbg(format, arg...) \
52 ugphy_printk(KERN_DEBUG, format , ## arg)
53#define ugphy_err(format, arg...) \
54 ugphy_printk(KERN_ERR, format , ## arg)
55#define ugphy_info(format, arg...) \
56 ugphy_printk(KERN_INFO, format , ## arg)
57#define ugphy_warn(format, arg...) \
58 ugphy_printk(KERN_WARNING, format , ## arg)
59
60#ifdef UGETH_VERBOSE_DEBUG
61#define ugphy_vdbg ugphy_dbg
62#else
63#define ugphy_vdbg(fmt, args...) do { } while (0)
64#endif /* UGETH_VERBOSE_DEBUG */
65
66static void config_genmii_advert(struct ugeth_mii_info *mii_info);
67static void genmii_setup_forced(struct ugeth_mii_info *mii_info);
68static void genmii_restart_aneg(struct ugeth_mii_info *mii_info);
69static int gbit_config_aneg(struct ugeth_mii_info *mii_info);
70static int genmii_config_aneg(struct ugeth_mii_info *mii_info);
71static int genmii_update_link(struct ugeth_mii_info *mii_info);
72static int genmii_read_status(struct ugeth_mii_info *mii_info);
73u16 phy_read(struct ugeth_mii_info *mii_info, u16 regnum);
74void phy_write(struct ugeth_mii_info *mii_info, u16 regnum, u16 val);
75
76static u8 *bcsr_regs = NULL;
77
78/* Write value to the PHY for this device to the register at regnum, */
79/* waiting until the write is done before it returns. All PHY */
80/* configuration has to be done through the TSEC1 MIIM regs */
81void write_phy_reg(struct net_device *dev, int mii_id, int regnum, int value)
82{
83 ucc_geth_private_t *ugeth = netdev_priv(dev);
84 ucc_mii_mng_t *mii_regs;
85 enet_tbi_mii_reg_e mii_reg = (enet_tbi_mii_reg_e) regnum;
86 u32 tmp_reg;
87
88 ugphy_vdbg("%s: IN", __FUNCTION__);
89
90 spin_lock_irq(&ugeth->lock);
91
92 mii_regs = ugeth->mii_info->mii_regs;
93
94 /* Set this UCC to be the master of the MII managment */
95 ucc_set_qe_mux_mii_mng(ugeth->ug_info->uf_info.ucc_num);
96
97 /* Stop the MII management read cycle */
98 out_be32(&mii_regs->miimcom, 0);
99 /* Setting up the MII Mangement Address Register */
100 tmp_reg = ((u32) mii_id << MIIMADD_PHY_ADDRESS_SHIFT) | mii_reg;
101 out_be32(&mii_regs->miimadd, tmp_reg);
102
103 /* Setting up the MII Mangement Control Register with the value */
104 out_be32(&mii_regs->miimcon, (u32) value);
105
106 /* Wait till MII management write is complete */
107 while ((in_be32(&mii_regs->miimind)) & MIIMIND_BUSY)
108 cpu_relax();
109
110 spin_unlock_irq(&ugeth->lock);
111
112 udelay(10000);
113}
114
115/* Reads from register regnum in the PHY for device dev, */
116/* returning the value. Clears miimcom first. All PHY */
117/* configuration has to be done through the TSEC1 MIIM regs */
118int read_phy_reg(struct net_device *dev, int mii_id, int regnum)
119{
120 ucc_geth_private_t *ugeth = netdev_priv(dev);
121 ucc_mii_mng_t *mii_regs;
122 enet_tbi_mii_reg_e mii_reg = (enet_tbi_mii_reg_e) regnum;
123 u32 tmp_reg;
124 u16 value;
125
126 ugphy_vdbg("%s: IN", __FUNCTION__);
127
128 spin_lock_irq(&ugeth->lock);
129
130 mii_regs = ugeth->mii_info->mii_regs;
131
132 /* Setting up the MII Mangement Address Register */
133 tmp_reg = ((u32) mii_id << MIIMADD_PHY_ADDRESS_SHIFT) | mii_reg;
134 out_be32(&mii_regs->miimadd, tmp_reg);
135
136 /* Perform an MII management read cycle */
137 out_be32(&mii_regs->miimcom, MIIMCOM_READ_CYCLE);
138
139 /* Wait till MII management write is complete */
140 while ((in_be32(&mii_regs->miimind)) & MIIMIND_BUSY)
141 cpu_relax();
142
143 udelay(10000);
144
145 /* Read MII management status */
146 value = (u16) in_be32(&mii_regs->miimstat);
147 out_be32(&mii_regs->miimcom, 0);
148 if (value == 0xffff)
149 ugphy_warn("read wrong value : mii_id %d,mii_reg %d, base %08x",
150 mii_id, mii_reg, (u32) & (mii_regs->miimcfg));
151
152 spin_unlock_irq(&ugeth->lock);
153
154 return (value);
155}
156
157void mii_clear_phy_interrupt(struct ugeth_mii_info *mii_info)
158{
159 ugphy_vdbg("%s: IN", __FUNCTION__);
160
161 if (mii_info->phyinfo->ack_interrupt)
162 mii_info->phyinfo->ack_interrupt(mii_info);
163}
164
165void mii_configure_phy_interrupt(struct ugeth_mii_info *mii_info,
166 u32 interrupts)
167{
168 ugphy_vdbg("%s: IN", __FUNCTION__);
169
170 mii_info->interrupts = interrupts;
171 if (mii_info->phyinfo->config_intr)
172 mii_info->phyinfo->config_intr(mii_info);
173}
174
175/* Writes MII_ADVERTISE with the appropriate values, after
176 * sanitizing advertise to make sure only supported features
177 * are advertised
178 */
179static void config_genmii_advert(struct ugeth_mii_info *mii_info)
180{
181 u32 advertise;
182 u16 adv;
183
184 ugphy_vdbg("%s: IN", __FUNCTION__);
185
186 /* Only allow advertising what this PHY supports */
187 mii_info->advertising &= mii_info->phyinfo->features;
188 advertise = mii_info->advertising;
189
190 /* Setup standard advertisement */
191 adv = phy_read(mii_info, MII_ADVERTISE);
192 adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
193 if (advertise & ADVERTISED_10baseT_Half)
194 adv |= ADVERTISE_10HALF;
195 if (advertise & ADVERTISED_10baseT_Full)
196 adv |= ADVERTISE_10FULL;
197 if (advertise & ADVERTISED_100baseT_Half)
198 adv |= ADVERTISE_100HALF;
199 if (advertise & ADVERTISED_100baseT_Full)
200 adv |= ADVERTISE_100FULL;
201 phy_write(mii_info, MII_ADVERTISE, adv);
202}
203
204static void genmii_setup_forced(struct ugeth_mii_info *mii_info)
205{
206 u16 ctrl;
207 u32 features = mii_info->phyinfo->features;
208
209 ugphy_vdbg("%s: IN", __FUNCTION__);
210
211 ctrl = phy_read(mii_info, MII_BMCR);
212
213 ctrl &=
214 ~(BMCR_FULLDPLX | BMCR_SPEED100 | BMCR_SPEED1000 | BMCR_ANENABLE);
215 ctrl |= BMCR_RESET;
216
217 switch (mii_info->speed) {
218 case SPEED_1000:
219 if (features & (SUPPORTED_1000baseT_Half
220 | SUPPORTED_1000baseT_Full)) {
221 ctrl |= BMCR_SPEED1000;
222 break;
223 }
224 mii_info->speed = SPEED_100;
225 case SPEED_100:
226 if (features & (SUPPORTED_100baseT_Half
227 | SUPPORTED_100baseT_Full)) {
228 ctrl |= BMCR_SPEED100;
229 break;
230 }
231 mii_info->speed = SPEED_10;
232 case SPEED_10:
233 if (features & (SUPPORTED_10baseT_Half
234 | SUPPORTED_10baseT_Full))
235 break;
236 default: /* Unsupported speed! */
237 ugphy_err("%s: Bad speed!", mii_info->dev->name);
238 break;
239 }
240
241 phy_write(mii_info, MII_BMCR, ctrl);
242}
243
244/* Enable and Restart Autonegotiation */
245static void genmii_restart_aneg(struct ugeth_mii_info *mii_info)
246{
247 u16 ctl;
248
249 ugphy_vdbg("%s: IN", __FUNCTION__);
250
251 ctl = phy_read(mii_info, MII_BMCR);
252 ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
253 phy_write(mii_info, MII_BMCR, ctl);
254}
255
256static int gbit_config_aneg(struct ugeth_mii_info *mii_info)
257{
258 u16 adv;
259 u32 advertise;
260
261 ugphy_vdbg("%s: IN", __FUNCTION__);
262
263 if (mii_info->autoneg) {
264 /* Configure the ADVERTISE register */
265 config_genmii_advert(mii_info);
266 advertise = mii_info->advertising;
267
268 adv = phy_read(mii_info, MII_1000BASETCONTROL);
269 adv &= ~(MII_1000BASETCONTROL_FULLDUPLEXCAP |
270 MII_1000BASETCONTROL_HALFDUPLEXCAP);
271 if (advertise & SUPPORTED_1000baseT_Half)
272 adv |= MII_1000BASETCONTROL_HALFDUPLEXCAP;
273 if (advertise & SUPPORTED_1000baseT_Full)
274 adv |= MII_1000BASETCONTROL_FULLDUPLEXCAP;
275 phy_write(mii_info, MII_1000BASETCONTROL, adv);
276
277 /* Start/Restart aneg */
278 genmii_restart_aneg(mii_info);
279 } else
280 genmii_setup_forced(mii_info);
281
282 return 0;
283}
284
285static int genmii_config_aneg(struct ugeth_mii_info *mii_info)
286{
287 ugphy_vdbg("%s: IN", __FUNCTION__);
288
289 if (mii_info->autoneg) {
290 config_genmii_advert(mii_info);
291 genmii_restart_aneg(mii_info);
292 } else
293 genmii_setup_forced(mii_info);
294
295 return 0;
296}
297
298static int genmii_update_link(struct ugeth_mii_info *mii_info)
299{
300 u16 status;
301
302 ugphy_vdbg("%s: IN", __FUNCTION__);
303
304 /* Do a fake read */
305 phy_read(mii_info, MII_BMSR);
306
307 /* Read link and autonegotiation status */
308 status = phy_read(mii_info, MII_BMSR);
309 if ((status & BMSR_LSTATUS) == 0)
310 mii_info->link = 0;
311 else
312 mii_info->link = 1;
313
314 /* If we are autonegotiating, and not done,
315 * return an error */
316 if (mii_info->autoneg && !(status & BMSR_ANEGCOMPLETE))
317 return -EAGAIN;
318
319 return 0;
320}
321
322static int genmii_read_status(struct ugeth_mii_info *mii_info)
323{
324 u16 status;
325 int err;
326
327 ugphy_vdbg("%s: IN", __FUNCTION__);
328
329 /* Update the link, but return if there
330 * was an error */
331 err = genmii_update_link(mii_info);
332 if (err)
333 return err;
334
335 if (mii_info->autoneg) {
336 status = phy_read(mii_info, MII_LPA);
337
338 if (status & (LPA_10FULL | LPA_100FULL))
339 mii_info->duplex = DUPLEX_FULL;
340 else
341 mii_info->duplex = DUPLEX_HALF;
342 if (status & (LPA_100FULL | LPA_100HALF))
343 mii_info->speed = SPEED_100;
344 else
345 mii_info->speed = SPEED_10;
346 mii_info->pause = 0;
347 }
348 /* On non-aneg, we assume what we put in BMCR is the speed,
349 * though magic-aneg shouldn't prevent this case from occurring
350 */
351
352 return 0;
353}
354
355static int marvell_init(struct ugeth_mii_info *mii_info)
356{
357 ugphy_vdbg("%s: IN", __FUNCTION__);
358
359 phy_write(mii_info, 0x14, 0x0cd2);
360 phy_write(mii_info, MII_BMCR,
361 phy_read(mii_info, MII_BMCR) | BMCR_RESET);
362 msleep(4000);
363
364 return 0;
365}
366
367static int marvell_config_aneg(struct ugeth_mii_info *mii_info)
368{
369 ugphy_vdbg("%s: IN", __FUNCTION__);
370
371 /* The Marvell PHY has an errata which requires
372 * that certain registers get written in order
373 * to restart autonegotiation */
374 phy_write(mii_info, MII_BMCR, BMCR_RESET);
375
376 phy_write(mii_info, 0x1d, 0x1f);
377 phy_write(mii_info, 0x1e, 0x200c);
378 phy_write(mii_info, 0x1d, 0x5);
379 phy_write(mii_info, 0x1e, 0);
380 phy_write(mii_info, 0x1e, 0x100);
381
382 gbit_config_aneg(mii_info);
383
384 return 0;
385}
386
387static int marvell_read_status(struct ugeth_mii_info *mii_info)
388{
389 u16 status;
390 int err;
391
392 ugphy_vdbg("%s: IN", __FUNCTION__);
393
394 /* Update the link, but return if there
395 * was an error */
396 err = genmii_update_link(mii_info);
397 if (err)
398 return err;
399
400 /* If the link is up, read the speed and duplex */
401 /* If we aren't autonegotiating, assume speeds
402 * are as set */
403 if (mii_info->autoneg && mii_info->link) {
404 int speed;
405 status = phy_read(mii_info, MII_M1011_PHY_SPEC_STATUS);
406
407 /* Get the duplexity */
408 if (status & MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX)
409 mii_info->duplex = DUPLEX_FULL;
410 else
411 mii_info->duplex = DUPLEX_HALF;
412
413 /* Get the speed */
414 speed = status & MII_M1011_PHY_SPEC_STATUS_SPD_MASK;
415 switch (speed) {
416 case MII_M1011_PHY_SPEC_STATUS_1000:
417 mii_info->speed = SPEED_1000;
418 break;
419 case MII_M1011_PHY_SPEC_STATUS_100:
420 mii_info->speed = SPEED_100;
421 break;
422 default:
423 mii_info->speed = SPEED_10;
424 break;
425 }
426 mii_info->pause = 0;
427 }
428
429 return 0;
430}
431
432static int marvell_ack_interrupt(struct ugeth_mii_info *mii_info)
433{
434 ugphy_vdbg("%s: IN", __FUNCTION__);
435
436 /* Clear the interrupts by reading the reg */
437 phy_read(mii_info, MII_M1011_IEVENT);
438
439 return 0;
440}
441
442static int marvell_config_intr(struct ugeth_mii_info *mii_info)
443{
444 ugphy_vdbg("%s: IN", __FUNCTION__);
445
446 if (mii_info->interrupts == MII_INTERRUPT_ENABLED)
447 phy_write(mii_info, MII_M1011_IMASK, MII_M1011_IMASK_INIT);
448 else
449 phy_write(mii_info, MII_M1011_IMASK, MII_M1011_IMASK_CLEAR);
450
451 return 0;
452}
453
454static int cis820x_init(struct ugeth_mii_info *mii_info)
455{
456 ugphy_vdbg("%s: IN", __FUNCTION__);
457
458 phy_write(mii_info, MII_CIS8201_AUX_CONSTAT,
459 MII_CIS8201_AUXCONSTAT_INIT);
460 phy_write(mii_info, MII_CIS8201_EXT_CON1, MII_CIS8201_EXTCON1_INIT);
461
462 return 0;
463}
464
465static int cis820x_read_status(struct ugeth_mii_info *mii_info)
466{
467 u16 status;
468 int err;
469
470 ugphy_vdbg("%s: IN", __FUNCTION__);
471
472 /* Update the link, but return if there
473 * was an error */
474 err = genmii_update_link(mii_info);
475 if (err)
476 return err;
477
478 /* If the link is up, read the speed and duplex */
479 /* If we aren't autonegotiating, assume speeds
480 * are as set */
481 if (mii_info->autoneg && mii_info->link) {
482 int speed;
483
484 status = phy_read(mii_info, MII_CIS8201_AUX_CONSTAT);
485 if (status & MII_CIS8201_AUXCONSTAT_DUPLEX)
486 mii_info->duplex = DUPLEX_FULL;
487 else
488 mii_info->duplex = DUPLEX_HALF;
489
490 speed = status & MII_CIS8201_AUXCONSTAT_SPEED;
491
492 switch (speed) {
493 case MII_CIS8201_AUXCONSTAT_GBIT:
494 mii_info->speed = SPEED_1000;
495 break;
496 case MII_CIS8201_AUXCONSTAT_100:
497 mii_info->speed = SPEED_100;
498 break;
499 default:
500 mii_info->speed = SPEED_10;
501 break;
502 }
503 }
504
505 return 0;
506}
507
508static int cis820x_ack_interrupt(struct ugeth_mii_info *mii_info)
509{
510 ugphy_vdbg("%s: IN", __FUNCTION__);
511
512 phy_read(mii_info, MII_CIS8201_ISTAT);
513
514 return 0;
515}
516
517static int cis820x_config_intr(struct ugeth_mii_info *mii_info)
518{
519 ugphy_vdbg("%s: IN", __FUNCTION__);
520
521 if (mii_info->interrupts == MII_INTERRUPT_ENABLED)
522 phy_write(mii_info, MII_CIS8201_IMASK, MII_CIS8201_IMASK_MASK);
523 else
524 phy_write(mii_info, MII_CIS8201_IMASK, 0);
525
526 return 0;
527}
528
529#define DM9161_DELAY 10
530
531static int dm9161_read_status(struct ugeth_mii_info *mii_info)
532{
533 u16 status;
534 int err;
535
536 ugphy_vdbg("%s: IN", __FUNCTION__);
537
538 /* Update the link, but return if there
539 * was an error */
540 err = genmii_update_link(mii_info);
541 if (err)
542 return err;
543
544 /* If the link is up, read the speed and duplex */
545 /* If we aren't autonegotiating, assume speeds
546 * are as set */
547 if (mii_info->autoneg && mii_info->link) {
548 status = phy_read(mii_info, MII_DM9161_SCSR);
549 if (status & (MII_DM9161_SCSR_100F | MII_DM9161_SCSR_100H))
550 mii_info->speed = SPEED_100;
551 else
552 mii_info->speed = SPEED_10;
553
554 if (status & (MII_DM9161_SCSR_100F | MII_DM9161_SCSR_10F))
555 mii_info->duplex = DUPLEX_FULL;
556 else
557 mii_info->duplex = DUPLEX_HALF;
558 }
559
560 return 0;
561}
562
563static int dm9161_config_aneg(struct ugeth_mii_info *mii_info)
564{
565 struct dm9161_private *priv = mii_info->priv;
566
567 ugphy_vdbg("%s: IN", __FUNCTION__);
568
569 if (0 == priv->resetdone)
570 return -EAGAIN;
571
572 return 0;
573}
574
575static void dm9161_timer(unsigned long data)
576{
577 struct ugeth_mii_info *mii_info = (struct ugeth_mii_info *)data;
578 struct dm9161_private *priv = mii_info->priv;
579 u16 status = phy_read(mii_info, MII_BMSR);
580
581 ugphy_vdbg("%s: IN", __FUNCTION__);
582
583 if (status & BMSR_ANEGCOMPLETE) {
584 priv->resetdone = 1;
585 } else
586 mod_timer(&priv->timer, jiffies + DM9161_DELAY * HZ);
587}
588
589static int dm9161_init(struct ugeth_mii_info *mii_info)
590{
591 struct dm9161_private *priv;
592
593 ugphy_vdbg("%s: IN", __FUNCTION__);
594
595 /* Allocate the private data structure */
596 priv = kmalloc(sizeof(struct dm9161_private), GFP_KERNEL);
597
598 if (NULL == priv)
599 return -ENOMEM;
600
601 mii_info->priv = priv;
602
603 /* Reset is not done yet */
604 priv->resetdone = 0;
605
606 phy_write(mii_info, MII_BMCR,
607 phy_read(mii_info, MII_BMCR) | BMCR_RESET);
608
609 phy_write(mii_info, MII_BMCR,
610 phy_read(mii_info, MII_BMCR) & ~BMCR_ISOLATE);
611
612 config_genmii_advert(mii_info);
613 /* Start/Restart aneg */
614 genmii_config_aneg(mii_info);
615
616 /* Start a timer for DM9161_DELAY seconds to wait
617 * for the PHY to be ready */
618 init_timer(&priv->timer);
619 priv->timer.function = &dm9161_timer;
620 priv->timer.data = (unsigned long)mii_info;
621 mod_timer(&priv->timer, jiffies + DM9161_DELAY * HZ);
622
623 return 0;
624}
625
626static void dm9161_close(struct ugeth_mii_info *mii_info)
627{
628 struct dm9161_private *priv = mii_info->priv;
629
630 ugphy_vdbg("%s: IN", __FUNCTION__);
631
632 del_timer_sync(&priv->timer);
633 kfree(priv);
634}
635
636static int dm9161_ack_interrupt(struct ugeth_mii_info *mii_info)
637{
638/* FIXME: This lines are for BUG fixing in the mpc8325.
639Remove this from here when it's fixed */
640 if (bcsr_regs == NULL)
641 bcsr_regs = (u8 *) ioremap(BCSR_PHYS_ADDR, BCSR_SIZE);
642 bcsr_regs[14] |= 0x40;
643 ugphy_vdbg("%s: IN", __FUNCTION__);
644
645 /* Clear the interrupts by reading the reg */
646 phy_read(mii_info, MII_DM9161_INTR);
647
648
649 return 0;
650}
651
652static int dm9161_config_intr(struct ugeth_mii_info *mii_info)
653{
654/* FIXME: This lines are for BUG fixing in the mpc8325.
655Remove this from here when it's fixed */
656 if (bcsr_regs == NULL) {
657 bcsr_regs = (u8 *) ioremap(BCSR_PHYS_ADDR, BCSR_SIZE);
658 bcsr_regs[14] &= ~0x40;
659 }
660 ugphy_vdbg("%s: IN", __FUNCTION__);
661
662 if (mii_info->interrupts == MII_INTERRUPT_ENABLED)
663 phy_write(mii_info, MII_DM9161_INTR, MII_DM9161_INTR_INIT);
664 else
665 phy_write(mii_info, MII_DM9161_INTR, MII_DM9161_INTR_STOP);
666
667 return 0;
668}
669
670/* Cicada 820x */
671static struct phy_info phy_info_cis820x = {
672 .phy_id = 0x000fc440,
673 .name = "Cicada Cis8204",
674 .phy_id_mask = 0x000fffc0,
675 .features = MII_GBIT_FEATURES,
676 .init = &cis820x_init,
677 .config_aneg = &gbit_config_aneg,
678 .read_status = &cis820x_read_status,
679 .ack_interrupt = &cis820x_ack_interrupt,
680 .config_intr = &cis820x_config_intr,
681};
682
683static struct phy_info phy_info_dm9161 = {
684 .phy_id = 0x0181b880,
685 .phy_id_mask = 0x0ffffff0,
686 .name = "Davicom DM9161E",
687 .init = dm9161_init,
688 .config_aneg = dm9161_config_aneg,
689 .read_status = dm9161_read_status,
690 .close = dm9161_close,
691};
692
693static struct phy_info phy_info_dm9161a = {
694 .phy_id = 0x0181b8a0,
695 .phy_id_mask = 0x0ffffff0,
696 .name = "Davicom DM9161A",
697 .features = MII_BASIC_FEATURES,
698 .init = dm9161_init,
699 .config_aneg = dm9161_config_aneg,
700 .read_status = dm9161_read_status,
701 .ack_interrupt = dm9161_ack_interrupt,
702 .config_intr = dm9161_config_intr,
703 .close = dm9161_close,
704};
705
706static struct phy_info phy_info_marvell = {
707 .phy_id = 0x01410c00,
708 .phy_id_mask = 0xffffff00,
709 .name = "Marvell 88E11x1",
710 .features = MII_GBIT_FEATURES,
711 .init = &marvell_init,
712 .config_aneg = &marvell_config_aneg,
713 .read_status = &marvell_read_status,
714 .ack_interrupt = &marvell_ack_interrupt,
715 .config_intr = &marvell_config_intr,
716};
717
718static struct phy_info phy_info_genmii = {
719 .phy_id = 0x00000000,
720 .phy_id_mask = 0x00000000,
721 .name = "Generic MII",
722 .features = MII_BASIC_FEATURES,
723 .config_aneg = genmii_config_aneg,
724 .read_status = genmii_read_status,
725};
726
727static struct phy_info *phy_info[] = {
728 &phy_info_cis820x,
729 &phy_info_marvell,
730 &phy_info_dm9161,
731 &phy_info_dm9161a,
732 &phy_info_genmii,
733 NULL
734};
735
736u16 phy_read(struct ugeth_mii_info *mii_info, u16 regnum)
737{
738 u16 retval;
739 unsigned long flags;
740
741 ugphy_vdbg("%s: IN", __FUNCTION__);
742
743 spin_lock_irqsave(&mii_info->mdio_lock, flags);
744 retval = mii_info->mdio_read(mii_info->dev, mii_info->mii_id, regnum);
745 spin_unlock_irqrestore(&mii_info->mdio_lock, flags);
746
747 return retval;
748}
749
750void phy_write(struct ugeth_mii_info *mii_info, u16 regnum, u16 val)
751{
752 unsigned long flags;
753
754 ugphy_vdbg("%s: IN", __FUNCTION__);
755
756 spin_lock_irqsave(&mii_info->mdio_lock, flags);
757 mii_info->mdio_write(mii_info->dev, mii_info->mii_id, regnum, val);
758 spin_unlock_irqrestore(&mii_info->mdio_lock, flags);
759}
760
761/* Use the PHY ID registers to determine what type of PHY is attached
762 * to device dev. return a struct phy_info structure describing that PHY
763 */
764struct phy_info *get_phy_info(struct ugeth_mii_info *mii_info)
765{
766 u16 phy_reg;
767 u32 phy_ID;
768 int i;
769 struct phy_info *theInfo = NULL;
770 struct net_device *dev = mii_info->dev;
771
772 ugphy_vdbg("%s: IN", __FUNCTION__);
773
774 /* Grab the bits from PHYIR1, and put them in the upper half */
775 phy_reg = phy_read(mii_info, MII_PHYSID1);
776 phy_ID = (phy_reg & 0xffff) << 16;
777
778 /* Grab the bits from PHYIR2, and put them in the lower half */
779 phy_reg = phy_read(mii_info, MII_PHYSID2);
780 phy_ID |= (phy_reg & 0xffff);
781
782 /* loop through all the known PHY types, and find one that */
783 /* matches the ID we read from the PHY. */
784 for (i = 0; phy_info[i]; i++)
785 if (phy_info[i]->phy_id == (phy_ID & phy_info[i]->phy_id_mask)){
786 theInfo = phy_info[i];
787 break;
788 }
789
790 /* This shouldn't happen, as we have generic PHY support */
791 if (theInfo == NULL) {
792 ugphy_info("%s: PHY id %x is not supported!", dev->name,
793 phy_ID);
794 return NULL;
795 } else {
796 ugphy_info("%s: PHY is %s (%x)", dev->name, theInfo->name,
797 phy_ID);
798 }
799
800 return theInfo;
801}