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