[SCSI] Merge up to linux-2.6 head
[linux-2.6] / drivers / net / phy / broadcom.c
1 /*
2  *      drivers/net/phy/broadcom.c
3  *
4  *      Broadcom BCM5411, BCM5421 and BCM5461 Gigabit Ethernet
5  *      transceivers.
6  *
7  *      Copyright (c) 2006  Maciej W. Rozycki
8  *
9  *      Inspired by code written by Amy Fong.
10  *
11  *      This program is free software; you can redistribute it and/or
12  *      modify it under the terms of the GNU General Public License
13  *      as published by the Free Software Foundation; either version
14  *      2 of the License, or (at your option) any later version.
15  */
16
17 #include <linux/module.h>
18 #include <linux/phy.h>
19
20 #define MII_BCM54XX_ECR         0x10    /* BCM54xx extended control register */
21 #define MII_BCM54XX_ECR_IM      0x1000  /* Interrupt mask */
22 #define MII_BCM54XX_ECR_IF      0x0800  /* Interrupt force */
23
24 #define MII_BCM54XX_ESR         0x11    /* BCM54xx extended status register */
25 #define MII_BCM54XX_ESR_IS      0x1000  /* Interrupt status */
26
27 #define MII_BCM54XX_ISR         0x1a    /* BCM54xx interrupt status register */
28 #define MII_BCM54XX_IMR         0x1b    /* BCM54xx interrupt mask register */
29 #define MII_BCM54XX_INT_CRCERR  0x0001  /* CRC error */
30 #define MII_BCM54XX_INT_LINK    0x0002  /* Link status changed */
31 #define MII_BCM54XX_INT_SPEED   0x0004  /* Link speed change */
32 #define MII_BCM54XX_INT_DUPLEX  0x0008  /* Duplex mode changed */
33 #define MII_BCM54XX_INT_LRS     0x0010  /* Local receiver status changed */
34 #define MII_BCM54XX_INT_RRS     0x0020  /* Remote receiver status changed */
35 #define MII_BCM54XX_INT_SSERR   0x0040  /* Scrambler synchronization error */
36 #define MII_BCM54XX_INT_UHCD    0x0080  /* Unsupported HCD negotiated */
37 #define MII_BCM54XX_INT_NHCD    0x0100  /* No HCD */
38 #define MII_BCM54XX_INT_NHCDL   0x0200  /* No HCD link */
39 #define MII_BCM54XX_INT_ANPR    0x0400  /* Auto-negotiation page received */
40 #define MII_BCM54XX_INT_LC      0x0800  /* All counters below 128 */
41 #define MII_BCM54XX_INT_HC      0x1000  /* Counter above 32768 */
42 #define MII_BCM54XX_INT_MDIX    0x2000  /* MDIX status change */
43 #define MII_BCM54XX_INT_PSERR   0x4000  /* Pair swap error */
44
45 MODULE_DESCRIPTION("Broadcom PHY driver");
46 MODULE_AUTHOR("Maciej W. Rozycki");
47 MODULE_LICENSE("GPL");
48
49 static int bcm54xx_config_init(struct phy_device *phydev)
50 {
51         int reg, err;
52
53         reg = phy_read(phydev, MII_BCM54XX_ECR);
54         if (reg < 0)
55                 return reg;
56
57         /* Mask interrupts globally.  */
58         reg |= MII_BCM54XX_ECR_IM;
59         err = phy_write(phydev, MII_BCM54XX_ECR, reg);
60         if (err < 0)
61                 return err;
62
63         /* Unmask events we are interested in.  */
64         reg = ~(MII_BCM54XX_INT_DUPLEX |
65                 MII_BCM54XX_INT_SPEED |
66                 MII_BCM54XX_INT_LINK);
67         err = phy_write(phydev, MII_BCM54XX_IMR, reg);
68         if (err < 0)
69                 return err;
70         return 0;
71 }
72
73 static int bcm54xx_ack_interrupt(struct phy_device *phydev)
74 {
75         int reg;
76
77         /* Clear pending interrupts.  */
78         reg = phy_read(phydev, MII_BCM54XX_ISR);
79         if (reg < 0)
80                 return reg;
81
82         return 0;
83 }
84
85 static int bcm54xx_config_intr(struct phy_device *phydev)
86 {
87         int reg, err;
88
89         reg = phy_read(phydev, MII_BCM54XX_ECR);
90         if (reg < 0)
91                 return reg;
92
93         if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
94                 reg &= ~MII_BCM54XX_ECR_IM;
95         else
96                 reg |= MII_BCM54XX_ECR_IM;
97
98         err = phy_write(phydev, MII_BCM54XX_ECR, reg);
99         return err;
100 }
101
102 static struct phy_driver bcm5411_driver = {
103         .phy_id         = 0x00206070,
104         .phy_id_mask    = 0xfffffff0,
105         .name           = "Broadcom BCM5411",
106         .features       = PHY_GBIT_FEATURES,
107         .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
108         .config_init    = bcm54xx_config_init,
109         .config_aneg    = genphy_config_aneg,
110         .read_status    = genphy_read_status,
111         .ack_interrupt  = bcm54xx_ack_interrupt,
112         .config_intr    = bcm54xx_config_intr,
113         .driver         = { .owner = THIS_MODULE },
114 };
115
116 static struct phy_driver bcm5421_driver = {
117         .phy_id         = 0x002060e0,
118         .phy_id_mask    = 0xfffffff0,
119         .name           = "Broadcom BCM5421",
120         .features       = PHY_GBIT_FEATURES,
121         .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
122         .config_init    = bcm54xx_config_init,
123         .config_aneg    = genphy_config_aneg,
124         .read_status    = genphy_read_status,
125         .ack_interrupt  = bcm54xx_ack_interrupt,
126         .config_intr    = bcm54xx_config_intr,
127         .driver         = { .owner = THIS_MODULE },
128 };
129
130 static struct phy_driver bcm5461_driver = {
131         .phy_id         = 0x002060c0,
132         .phy_id_mask    = 0xfffffff0,
133         .name           = "Broadcom BCM5461",
134         .features       = PHY_GBIT_FEATURES,
135         .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
136         .config_init    = bcm54xx_config_init,
137         .config_aneg    = genphy_config_aneg,
138         .read_status    = genphy_read_status,
139         .ack_interrupt  = bcm54xx_ack_interrupt,
140         .config_intr    = bcm54xx_config_intr,
141         .driver         = { .owner = THIS_MODULE },
142 };
143
144 static int __init broadcom_init(void)
145 {
146         int ret;
147
148         ret = phy_driver_register(&bcm5411_driver);
149         if (ret)
150                 goto out_5411;
151         ret = phy_driver_register(&bcm5421_driver);
152         if (ret)
153                 goto out_5421;
154         ret = phy_driver_register(&bcm5461_driver);
155         if (ret)
156                 goto out_5461;
157         return ret;
158
159 out_5461:
160         phy_driver_unregister(&bcm5421_driver);
161 out_5421:
162         phy_driver_unregister(&bcm5411_driver);
163 out_5411:
164         return ret;
165 }
166
167 static void __exit broadcom_exit(void)
168 {
169         phy_driver_unregister(&bcm5461_driver);
170         phy_driver_unregister(&bcm5421_driver);
171         phy_driver_unregister(&bcm5411_driver);
172 }
173
174 module_init(broadcom_init);
175 module_exit(broadcom_exit);