aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/cxgb3/vsc8211.c
diff options
context:
space:
mode:
authorDivy Le Ray <divy@chelsio.com>2007-01-18 22:04:14 -0500
committerJeff Garzik <jeff@garzik.org>2007-02-05 16:58:46 -0500
commit4d22de3e6cc4a09c369b504cd8bcde3385a974cd (patch)
treeaf13a2ee582105d961c79fc4e55fce0b5e043310 /drivers/net/cxgb3/vsc8211.c
parent0bf94faf64afaba6e7b49fd11541b59d2ba06d0e (diff)
Add support for the latest 1G/10G Chelsio adapter, T3.
This driver is required by the Chelsio T3 RDMA driver posted by Steve Wise. Signed-off-by: Divy Le Ray <divy@chelsio.com> Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers/net/cxgb3/vsc8211.c')
-rw-r--r--drivers/net/cxgb3/vsc8211.c208
1 files changed, 208 insertions, 0 deletions
diff --git a/drivers/net/cxgb3/vsc8211.c b/drivers/net/cxgb3/vsc8211.c
new file mode 100644
index 00000000000..6a0a815b89c
--- /dev/null
+++ b/drivers/net/cxgb3/vsc8211.c
@@ -0,0 +1,208 @@
1/*
2 * This file is part of the Chelsio T3 Ethernet driver.
3 *
4 * Copyright (C) 2005-2006 Chelsio Communications. All rights reserved.
5 *
6 * This program is distributed in the hope that it will be useful, but WITHOUT
7 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
8 * FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE file included in this
9 * release for licensing terms and conditions.
10 */
11
12#include "common.h"
13
14/* VSC8211 PHY specific registers. */
15enum {
16 VSC8211_INTR_ENABLE = 25,
17 VSC8211_INTR_STATUS = 26,
18 VSC8211_AUX_CTRL_STAT = 28,
19};
20
21enum {
22 VSC_INTR_RX_ERR = 1 << 0,
23 VSC_INTR_MS_ERR = 1 << 1, /* master/slave resolution error */
24 VSC_INTR_CABLE = 1 << 2, /* cable impairment */
25 VSC_INTR_FALSE_CARR = 1 << 3, /* false carrier */
26 VSC_INTR_MEDIA_CHG = 1 << 4, /* AMS media change */
27 VSC_INTR_RX_FIFO = 1 << 5, /* Rx FIFO over/underflow */
28 VSC_INTR_TX_FIFO = 1 << 6, /* Tx FIFO over/underflow */
29 VSC_INTR_DESCRAMBL = 1 << 7, /* descrambler lock-lost */
30 VSC_INTR_SYMBOL_ERR = 1 << 8, /* symbol error */
31 VSC_INTR_NEG_DONE = 1 << 10, /* autoneg done */
32 VSC_INTR_NEG_ERR = 1 << 11, /* autoneg error */
33 VSC_INTR_LINK_CHG = 1 << 13, /* link change */
34 VSC_INTR_ENABLE = 1 << 15, /* interrupt enable */
35};
36
37#define CFG_CHG_INTR_MASK (VSC_INTR_LINK_CHG | VSC_INTR_NEG_ERR | \
38 VSC_INTR_NEG_DONE)
39#define INTR_MASK (CFG_CHG_INTR_MASK | VSC_INTR_TX_FIFO | VSC_INTR_RX_FIFO | \
40 VSC_INTR_ENABLE)
41
42/* PHY specific auxiliary control & status register fields */
43#define S_ACSR_ACTIPHY_TMR 0
44#define M_ACSR_ACTIPHY_TMR 0x3
45#define V_ACSR_ACTIPHY_TMR(x) ((x) << S_ACSR_ACTIPHY_TMR)
46
47#define S_ACSR_SPEED 3
48#define M_ACSR_SPEED 0x3
49#define G_ACSR_SPEED(x) (((x) >> S_ACSR_SPEED) & M_ACSR_SPEED)
50
51#define S_ACSR_DUPLEX 5
52#define F_ACSR_DUPLEX (1 << S_ACSR_DUPLEX)
53
54#define S_ACSR_ACTIPHY 6
55#define F_ACSR_ACTIPHY (1 << S_ACSR_ACTIPHY)
56
57/*
58 * Reset the PHY. This PHY completes reset immediately so we never wait.
59 */
60static int vsc8211_reset(struct cphy *cphy, int wait)
61{
62 return t3_phy_reset(cphy, 0, 0);
63}
64
65static int vsc8211_intr_enable(struct cphy *cphy)
66{
67 return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, INTR_MASK);
68}
69
70static int vsc8211_intr_disable(struct cphy *cphy)
71{
72 return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, 0);
73}
74
75static int vsc8211_intr_clear(struct cphy *cphy)
76{
77 u32 val;
78
79 /* Clear PHY interrupts by reading the register. */
80 return mdio_read(cphy, 0, VSC8211_INTR_STATUS, &val);
81}
82
83static int vsc8211_autoneg_enable(struct cphy *cphy)
84{
85 return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE,
86 BMCR_ANENABLE | BMCR_ANRESTART);
87}
88
89static int vsc8211_autoneg_restart(struct cphy *cphy)
90{
91 return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE,
92 BMCR_ANRESTART);
93}
94
95static int vsc8211_get_link_status(struct cphy *cphy, int *link_ok,
96 int *speed, int *duplex, int *fc)
97{
98 unsigned int bmcr, status, lpa, adv;
99 int err, sp = -1, dplx = -1, pause = 0;
100
101 err = mdio_read(cphy, 0, MII_BMCR, &bmcr);
102 if (!err)
103 err = mdio_read(cphy, 0, MII_BMSR, &status);
104 if (err)
105 return err;
106
107 if (link_ok) {
108 /*
109 * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
110 * once more to get the current link state.
111 */
112 if (!(status & BMSR_LSTATUS))
113 err = mdio_read(cphy, 0, MII_BMSR, &status);
114 if (err)
115 return err;
116 *link_ok = (status & BMSR_LSTATUS) != 0;
117 }
118 if (!(bmcr & BMCR_ANENABLE)) {
119 dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
120 if (bmcr & BMCR_SPEED1000)
121 sp = SPEED_1000;
122 else if (bmcr & BMCR_SPEED100)
123 sp = SPEED_100;
124 else
125 sp = SPEED_10;
126 } else if (status & BMSR_ANEGCOMPLETE) {
127 err = mdio_read(cphy, 0, VSC8211_AUX_CTRL_STAT, &status);
128 if (err)
129 return err;
130
131 dplx = (status & F_ACSR_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF;
132 sp = G_ACSR_SPEED(status);
133 if (sp == 0)
134 sp = SPEED_10;
135 else if (sp == 1)
136 sp = SPEED_100;
137 else
138 sp = SPEED_1000;
139
140 if (fc && dplx == DUPLEX_FULL) {
141 err = mdio_read(cphy, 0, MII_LPA, &lpa);
142 if (!err)
143 err = mdio_read(cphy, 0, MII_ADVERTISE, &adv);
144 if (err)
145 return err;
146
147 if (lpa & adv & ADVERTISE_PAUSE_CAP)
148 pause = PAUSE_RX | PAUSE_TX;
149 else if ((lpa & ADVERTISE_PAUSE_CAP) &&
150 (lpa & ADVERTISE_PAUSE_ASYM) &&
151 (adv & ADVERTISE_PAUSE_ASYM))
152 pause = PAUSE_TX;
153 else if ((lpa & ADVERTISE_PAUSE_ASYM) &&
154 (adv & ADVERTISE_PAUSE_CAP))
155 pause = PAUSE_RX;
156 }
157 }
158 if (speed)
159 *speed = sp;
160 if (duplex)
161 *duplex = dplx;
162 if (fc)
163 *fc = pause;
164 return 0;
165}
166
167static int vsc8211_power_down(struct cphy *cphy, int enable)
168{
169 return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN,
170 enable ? BMCR_PDOWN : 0);
171}
172
173static int vsc8211_intr_handler(struct cphy *cphy)
174{
175 unsigned int cause;
176 int err, cphy_cause = 0;
177
178 err = mdio_read(cphy, 0, VSC8211_INTR_STATUS, &cause);
179 if (err)
180 return err;
181
182 cause &= INTR_MASK;
183 if (cause & CFG_CHG_INTR_MASK)
184 cphy_cause |= cphy_cause_link_change;
185 if (cause & (VSC_INTR_RX_FIFO | VSC_INTR_TX_FIFO))
186 cphy_cause |= cphy_cause_fifo_error;
187 return cphy_cause;
188}
189
190static struct cphy_ops vsc8211_ops = {
191 .reset = vsc8211_reset,
192 .intr_enable = vsc8211_intr_enable,
193 .intr_disable = vsc8211_intr_disable,
194 .intr_clear = vsc8211_intr_clear,
195 .intr_handler = vsc8211_intr_handler,
196 .autoneg_enable = vsc8211_autoneg_enable,
197 .autoneg_restart = vsc8211_autoneg_restart,
198 .advertise = t3_phy_advertise,
199 .set_speed_duplex = t3_set_phy_speed_duplex,
200 .get_link_status = vsc8211_get_link_status,
201 .power_down = vsc8211_power_down,
202};
203
204void t3_vsc8211_phy_prep(struct cphy *phy, struct adapter *adapter,
205 int phy_addr, const struct mdio_ops *mdio_ops)
206{
207 cphy_init(phy, adapter, phy_addr, &vsc8211_ops, mdio_ops);
208}