Merge master.kernel.org:/pub/scm/linux/kernel/git/wim/linux-2.6-watchdog
[linux-2.6] / drivers / net / cxgb3 / vsc8211.c
1 /*
2  * Copyright (c) 2005-2007 Chelsio, Inc. All rights reserved.
3  *
4  * This software is available to you under a choice of one of two
5  * licenses.  You may choose to be licensed under the terms of the GNU
6  * General Public License (GPL) Version 2, available from the file
7  * COPYING in the main directory of this source tree, or the
8  * OpenIB.org BSD license below:
9  *
10  *     Redistribution and use in source and binary forms, with or
11  *     without modification, are permitted provided that the following
12  *     conditions are met:
13  *
14  *      - Redistributions of source code must retain the above
15  *        copyright notice, this list of conditions and the following
16  *        disclaimer.
17  *
18  *      - Redistributions in binary form must reproduce the above
19  *        copyright notice, this list of conditions and the following
20  *        disclaimer in the documentation and/or other materials
21  *        provided with the distribution.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30  * SOFTWARE.
31  */
32 #include "common.h"
33
34 /* VSC8211 PHY specific registers. */
35 enum {
36         VSC8211_INTR_ENABLE = 25,
37         VSC8211_INTR_STATUS = 26,
38         VSC8211_AUX_CTRL_STAT = 28,
39 };
40
41 enum {
42         VSC_INTR_RX_ERR = 1 << 0,
43         VSC_INTR_MS_ERR = 1 << 1,       /* master/slave resolution error */
44         VSC_INTR_CABLE = 1 << 2,        /* cable impairment */
45         VSC_INTR_FALSE_CARR = 1 << 3,   /* false carrier */
46         VSC_INTR_MEDIA_CHG = 1 << 4,    /* AMS media change */
47         VSC_INTR_RX_FIFO = 1 << 5,      /* Rx FIFO over/underflow */
48         VSC_INTR_TX_FIFO = 1 << 6,      /* Tx FIFO over/underflow */
49         VSC_INTR_DESCRAMBL = 1 << 7,    /* descrambler lock-lost */
50         VSC_INTR_SYMBOL_ERR = 1 << 8,   /* symbol error */
51         VSC_INTR_NEG_DONE = 1 << 10,    /* autoneg done */
52         VSC_INTR_NEG_ERR = 1 << 11,     /* autoneg error */
53         VSC_INTR_LINK_CHG = 1 << 13,    /* link change */
54         VSC_INTR_ENABLE = 1 << 15,      /* interrupt enable */
55 };
56
57 #define CFG_CHG_INTR_MASK (VSC_INTR_LINK_CHG | VSC_INTR_NEG_ERR | \
58                            VSC_INTR_NEG_DONE)
59 #define INTR_MASK (CFG_CHG_INTR_MASK | VSC_INTR_TX_FIFO | VSC_INTR_RX_FIFO | \
60                    VSC_INTR_ENABLE)
61
62 /* PHY specific auxiliary control & status register fields */
63 #define S_ACSR_ACTIPHY_TMR    0
64 #define M_ACSR_ACTIPHY_TMR    0x3
65 #define V_ACSR_ACTIPHY_TMR(x) ((x) << S_ACSR_ACTIPHY_TMR)
66
67 #define S_ACSR_SPEED    3
68 #define M_ACSR_SPEED    0x3
69 #define G_ACSR_SPEED(x) (((x) >> S_ACSR_SPEED) & M_ACSR_SPEED)
70
71 #define S_ACSR_DUPLEX 5
72 #define F_ACSR_DUPLEX (1 << S_ACSR_DUPLEX)
73
74 #define S_ACSR_ACTIPHY 6
75 #define F_ACSR_ACTIPHY (1 << S_ACSR_ACTIPHY)
76
77 /*
78  * Reset the PHY.  This PHY completes reset immediately so we never wait.
79  */
80 static int vsc8211_reset(struct cphy *cphy, int wait)
81 {
82         return t3_phy_reset(cphy, 0, 0);
83 }
84
85 static int vsc8211_intr_enable(struct cphy *cphy)
86 {
87         return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, INTR_MASK);
88 }
89
90 static int vsc8211_intr_disable(struct cphy *cphy)
91 {
92         return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, 0);
93 }
94
95 static int vsc8211_intr_clear(struct cphy *cphy)
96 {
97         u32 val;
98
99         /* Clear PHY interrupts by reading the register. */
100         return mdio_read(cphy, 0, VSC8211_INTR_STATUS, &val);
101 }
102
103 static int vsc8211_autoneg_enable(struct cphy *cphy)
104 {
105         return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE,
106                                    BMCR_ANENABLE | BMCR_ANRESTART);
107 }
108
109 static int vsc8211_autoneg_restart(struct cphy *cphy)
110 {
111         return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE,
112                                    BMCR_ANRESTART);
113 }
114
115 static int vsc8211_get_link_status(struct cphy *cphy, int *link_ok,
116                                    int *speed, int *duplex, int *fc)
117 {
118         unsigned int bmcr, status, lpa, adv;
119         int err, sp = -1, dplx = -1, pause = 0;
120
121         err = mdio_read(cphy, 0, MII_BMCR, &bmcr);
122         if (!err)
123                 err = mdio_read(cphy, 0, MII_BMSR, &status);
124         if (err)
125                 return err;
126
127         if (link_ok) {
128                 /*
129                  * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
130                  * once more to get the current link state.
131                  */
132                 if (!(status & BMSR_LSTATUS))
133                         err = mdio_read(cphy, 0, MII_BMSR, &status);
134                 if (err)
135                         return err;
136                 *link_ok = (status & BMSR_LSTATUS) != 0;
137         }
138         if (!(bmcr & BMCR_ANENABLE)) {
139                 dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
140                 if (bmcr & BMCR_SPEED1000)
141                         sp = SPEED_1000;
142                 else if (bmcr & BMCR_SPEED100)
143                         sp = SPEED_100;
144                 else
145                         sp = SPEED_10;
146         } else if (status & BMSR_ANEGCOMPLETE) {
147                 err = mdio_read(cphy, 0, VSC8211_AUX_CTRL_STAT, &status);
148                 if (err)
149                         return err;
150
151                 dplx = (status & F_ACSR_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF;
152                 sp = G_ACSR_SPEED(status);
153                 if (sp == 0)
154                         sp = SPEED_10;
155                 else if (sp == 1)
156                         sp = SPEED_100;
157                 else
158                         sp = SPEED_1000;
159
160                 if (fc && dplx == DUPLEX_FULL) {
161                         err = mdio_read(cphy, 0, MII_LPA, &lpa);
162                         if (!err)
163                                 err = mdio_read(cphy, 0, MII_ADVERTISE, &adv);
164                         if (err)
165                                 return err;
166
167                         if (lpa & adv & ADVERTISE_PAUSE_CAP)
168                                 pause = PAUSE_RX | PAUSE_TX;
169                         else if ((lpa & ADVERTISE_PAUSE_CAP) &&
170                                  (lpa & ADVERTISE_PAUSE_ASYM) &&
171                                  (adv & ADVERTISE_PAUSE_ASYM))
172                                 pause = PAUSE_TX;
173                         else if ((lpa & ADVERTISE_PAUSE_ASYM) &&
174                                  (adv & ADVERTISE_PAUSE_CAP))
175                                 pause = PAUSE_RX;
176                 }
177         }
178         if (speed)
179                 *speed = sp;
180         if (duplex)
181                 *duplex = dplx;
182         if (fc)
183                 *fc = pause;
184         return 0;
185 }
186
187 static int vsc8211_power_down(struct cphy *cphy, int enable)
188 {
189         return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN,
190                                    enable ? BMCR_PDOWN : 0);
191 }
192
193 static int vsc8211_intr_handler(struct cphy *cphy)
194 {
195         unsigned int cause;
196         int err, cphy_cause = 0;
197
198         err = mdio_read(cphy, 0, VSC8211_INTR_STATUS, &cause);
199         if (err)
200                 return err;
201
202         cause &= INTR_MASK;
203         if (cause & CFG_CHG_INTR_MASK)
204                 cphy_cause |= cphy_cause_link_change;
205         if (cause & (VSC_INTR_RX_FIFO | VSC_INTR_TX_FIFO))
206                 cphy_cause |= cphy_cause_fifo_error;
207         return cphy_cause;
208 }
209
210 static struct cphy_ops vsc8211_ops = {
211         .reset = vsc8211_reset,
212         .intr_enable = vsc8211_intr_enable,
213         .intr_disable = vsc8211_intr_disable,
214         .intr_clear = vsc8211_intr_clear,
215         .intr_handler = vsc8211_intr_handler,
216         .autoneg_enable = vsc8211_autoneg_enable,
217         .autoneg_restart = vsc8211_autoneg_restart,
218         .advertise = t3_phy_advertise,
219         .set_speed_duplex = t3_set_phy_speed_duplex,
220         .get_link_status = vsc8211_get_link_status,
221         .power_down = vsc8211_power_down,
222 };
223
224 void t3_vsc8211_phy_prep(struct cphy *phy, struct adapter *adapter,
225                          int phy_addr, const struct mdio_ops *mdio_ops)
226 {
227         cphy_init(phy, adapter, phy_addr, &vsc8211_ops, mdio_ops);
228 }