[AGPGART] uninorth: Add module param 'aperture' for aperture size
[sfrench/cifs-2.6.git] / drivers / net / ucc_geth_phy.c
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
66 static void config_genmii_advert(struct ugeth_mii_info *mii_info);
67 static void genmii_setup_forced(struct ugeth_mii_info *mii_info);
68 static void genmii_restart_aneg(struct ugeth_mii_info *mii_info);
69 static int gbit_config_aneg(struct ugeth_mii_info *mii_info);
70 static int genmii_config_aneg(struct ugeth_mii_info *mii_info);
71 static int genmii_update_link(struct ugeth_mii_info *mii_info);
72 static int genmii_read_status(struct ugeth_mii_info *mii_info);
73 u16 phy_read(struct ugeth_mii_info *mii_info, u16 regnum);
74 void phy_write(struct ugeth_mii_info *mii_info, u16 regnum, u16 val);
75
76 static 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 */
81 void 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 */
118 int 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
157 void 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
165 void 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  */
179 static 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
204 static 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 */
245 static 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
256 static 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
285 static 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
298 static 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
322 static 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
355 static 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
367 static 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
387 static 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
432 static 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
442 static 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
454 static 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
465 static 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
508 static 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
517 static 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
531 static 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
563 static 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
575 static 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
589 static 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
626 static 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
636 static int dm9161_ack_interrupt(struct ugeth_mii_info *mii_info)
637 {
638 /* FIXME: This lines are for BUG fixing in the mpc8325.
639 Remove 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
652 static int dm9161_config_intr(struct ugeth_mii_info *mii_info)
653 {
654 /* FIXME: This lines are for BUG fixing in the mpc8325.
655 Remove 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 */
671 static 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
683 static 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
693 static 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
706 static 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
718 static 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
727 static 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
736 u16 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
750 void 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  */
764 struct 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 }