]>
Commit | Line | Data |
---|---|---|
2cb7cef9 BS |
1 | Commit-Id: 0ce2f03bade2046d6eb6184d52d065688382d7bd |
2 | From: Divy Le Ray <divy@chelsio.com> | |
3 | Date: Wed, 8 Oct 2008 17:40:28 -0700 | |
4 | Acked-by: Karsten Keil <kkeil@novell.com> | |
5 | Subject: [PATCH] cxgb3: Add 1G fiber support | |
6 | Reference: bnc#446739 | |
7 | ||
8 | ||
9 | Add support for 1G optical Vitesse PHY. | |
10 | ||
11 | Signed-off-by: Divy Le Ray <divy@chelsio.com> | |
12 | Signed-off-by: David S. Miller <davem@davemloft.net> | |
13 | ||
14 | Index: linux-2.6.27/drivers/net/cxgb3/common.h | |
15 | =================================================================== | |
16 | --- linux-2.6.27.orig/drivers/net/cxgb3/common.h | |
17 | +++ linux-2.6.27/drivers/net/cxgb3/common.h | |
18 | @@ -691,6 +691,7 @@ int t3_mdio_change_bits(struct cphy *phy | |
19 | unsigned int set); | |
20 | int t3_phy_reset(struct cphy *phy, int mmd, int wait); | |
21 | int t3_phy_advertise(struct cphy *phy, unsigned int advert); | |
22 | +int t3_phy_advertise_fiber(struct cphy *phy, unsigned int advert); | |
23 | int t3_set_phy_speed_duplex(struct cphy *phy, int speed, int duplex); | |
24 | int t3_phy_lasi_intr_enable(struct cphy *phy); | |
25 | int t3_phy_lasi_intr_disable(struct cphy *phy); | |
26 | Index: linux-2.6.27/drivers/net/cxgb3/t3_hw.c | |
27 | =================================================================== | |
28 | --- linux-2.6.27.orig/drivers/net/cxgb3/t3_hw.c | |
29 | +++ linux-2.6.27/drivers/net/cxgb3/t3_hw.c | |
30 | @@ -408,6 +408,29 @@ int t3_phy_advertise(struct cphy *phy, u | |
31 | } | |
32 | ||
33 | /** | |
34 | + * t3_phy_advertise_fiber - set fiber PHY advertisement register | |
35 | + * @phy: the PHY to operate on | |
36 | + * @advert: bitmap of capabilities the PHY should advertise | |
37 | + * | |
38 | + * Sets a fiber PHY's advertisement register to advertise the | |
39 | + * requested capabilities. | |
40 | + */ | |
41 | +int t3_phy_advertise_fiber(struct cphy *phy, unsigned int advert) | |
42 | +{ | |
43 | + unsigned int val = 0; | |
44 | + | |
45 | + if (advert & ADVERTISED_1000baseT_Half) | |
46 | + val |= ADVERTISE_1000XHALF; | |
47 | + if (advert & ADVERTISED_1000baseT_Full) | |
48 | + val |= ADVERTISE_1000XFULL; | |
49 | + if (advert & ADVERTISED_Pause) | |
50 | + val |= ADVERTISE_1000XPAUSE; | |
51 | + if (advert & ADVERTISED_Asym_Pause) | |
52 | + val |= ADVERTISE_1000XPSE_ASYM; | |
53 | + return mdio_write(phy, 0, MII_ADVERTISE, val); | |
54 | +} | |
55 | + | |
56 | +/** | |
57 | * t3_set_phy_speed_duplex - force PHY speed and duplex | |
58 | * @phy: the PHY to operate on | |
59 | * @speed: requested PHY speed | |
60 | Index: linux-2.6.27/drivers/net/cxgb3/vsc8211.c | |
61 | =================================================================== | |
62 | --- linux-2.6.27.orig/drivers/net/cxgb3/vsc8211.c | |
63 | +++ linux-2.6.27/drivers/net/cxgb3/vsc8211.c | |
64 | @@ -33,28 +33,40 @@ | |
65 | ||
66 | /* VSC8211 PHY specific registers. */ | |
67 | enum { | |
68 | + VSC8211_SIGDET_CTRL = 19, | |
69 | + VSC8211_EXT_CTRL = 23, | |
70 | VSC8211_INTR_ENABLE = 25, | |
71 | VSC8211_INTR_STATUS = 26, | |
72 | + VSC8211_LED_CTRL = 27, | |
73 | VSC8211_AUX_CTRL_STAT = 28, | |
74 | + VSC8211_EXT_PAGE_AXS = 31, | |
75 | }; | |
76 | ||
77 | enum { | |
78 | VSC_INTR_RX_ERR = 1 << 0, | |
79 | - VSC_INTR_MS_ERR = 1 << 1, /* master/slave resolution error */ | |
80 | - VSC_INTR_CABLE = 1 << 2, /* cable impairment */ | |
81 | - VSC_INTR_FALSE_CARR = 1 << 3, /* false carrier */ | |
82 | - VSC_INTR_MEDIA_CHG = 1 << 4, /* AMS media change */ | |
83 | - VSC_INTR_RX_FIFO = 1 << 5, /* Rx FIFO over/underflow */ | |
84 | - VSC_INTR_TX_FIFO = 1 << 6, /* Tx FIFO over/underflow */ | |
85 | - VSC_INTR_DESCRAMBL = 1 << 7, /* descrambler lock-lost */ | |
86 | - VSC_INTR_SYMBOL_ERR = 1 << 8, /* symbol error */ | |
87 | - VSC_INTR_NEG_DONE = 1 << 10, /* autoneg done */ | |
88 | - VSC_INTR_NEG_ERR = 1 << 11, /* autoneg error */ | |
89 | - VSC_INTR_LINK_CHG = 1 << 13, /* link change */ | |
90 | - VSC_INTR_ENABLE = 1 << 15, /* interrupt enable */ | |
91 | + VSC_INTR_MS_ERR = 1 << 1, /* master/slave resolution error */ | |
92 | + VSC_INTR_CABLE = 1 << 2, /* cable impairment */ | |
93 | + VSC_INTR_FALSE_CARR = 1 << 3, /* false carrier */ | |
94 | + VSC_INTR_MEDIA_CHG = 1 << 4, /* AMS media change */ | |
95 | + VSC_INTR_RX_FIFO = 1 << 5, /* Rx FIFO over/underflow */ | |
96 | + VSC_INTR_TX_FIFO = 1 << 6, /* Tx FIFO over/underflow */ | |
97 | + VSC_INTR_DESCRAMBL = 1 << 7, /* descrambler lock-lost */ | |
98 | + VSC_INTR_SYMBOL_ERR = 1 << 8, /* symbol error */ | |
99 | + VSC_INTR_NEG_DONE = 1 << 10, /* autoneg done */ | |
100 | + VSC_INTR_NEG_ERR = 1 << 11, /* autoneg error */ | |
101 | + VSC_INTR_DPLX_CHG = 1 << 12, /* duplex change */ | |
102 | + VSC_INTR_LINK_CHG = 1 << 13, /* link change */ | |
103 | + VSC_INTR_SPD_CHG = 1 << 14, /* speed change */ | |
104 | + VSC_INTR_ENABLE = 1 << 15, /* interrupt enable */ | |
105 | +}; | |
106 | + | |
107 | +enum { | |
108 | + VSC_CTRL_CLAUSE37_VIEW = 1 << 4, /* Switch to Clause 37 view */ | |
109 | + VSC_CTRL_MEDIA_MODE_HI = 0xf000 /* High part of media mode select */ | |
110 | }; | |
111 | ||
112 | #define CFG_CHG_INTR_MASK (VSC_INTR_LINK_CHG | VSC_INTR_NEG_ERR | \ | |
113 | + VSC_INTR_DPLX_CHG | VSC_INTR_SPD_CHG | \ | |
114 | VSC_INTR_NEG_DONE) | |
115 | #define INTR_MASK (CFG_CHG_INTR_MASK | VSC_INTR_TX_FIFO | VSC_INTR_RX_FIFO | \ | |
116 | VSC_INTR_ENABLE) | |
117 | @@ -184,6 +196,112 @@ static int vsc8211_get_link_status(struc | |
118 | return 0; | |
119 | } | |
120 | ||
121 | +static int vsc8211_get_link_status_fiber(struct cphy *cphy, int *link_ok, | |
122 | + int *speed, int *duplex, int *fc) | |
123 | +{ | |
124 | + unsigned int bmcr, status, lpa, adv; | |
125 | + int err, sp = -1, dplx = -1, pause = 0; | |
126 | + | |
127 | + err = mdio_read(cphy, 0, MII_BMCR, &bmcr); | |
128 | + if (!err) | |
129 | + err = mdio_read(cphy, 0, MII_BMSR, &status); | |
130 | + if (err) | |
131 | + return err; | |
132 | + | |
133 | + if (link_ok) { | |
134 | + /* | |
135 | + * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it | |
136 | + * once more to get the current link state. | |
137 | + */ | |
138 | + if (!(status & BMSR_LSTATUS)) | |
139 | + err = mdio_read(cphy, 0, MII_BMSR, &status); | |
140 | + if (err) | |
141 | + return err; | |
142 | + *link_ok = (status & BMSR_LSTATUS) != 0; | |
143 | + } | |
144 | + if (!(bmcr & BMCR_ANENABLE)) { | |
145 | + dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF; | |
146 | + if (bmcr & BMCR_SPEED1000) | |
147 | + sp = SPEED_1000; | |
148 | + else if (bmcr & BMCR_SPEED100) | |
149 | + sp = SPEED_100; | |
150 | + else | |
151 | + sp = SPEED_10; | |
152 | + } else if (status & BMSR_ANEGCOMPLETE) { | |
153 | + err = mdio_read(cphy, 0, MII_LPA, &lpa); | |
154 | + if (!err) | |
155 | + err = mdio_read(cphy, 0, MII_ADVERTISE, &adv); | |
156 | + if (err) | |
157 | + return err; | |
158 | + | |
159 | + if (adv & lpa & ADVERTISE_1000XFULL) { | |
160 | + dplx = DUPLEX_FULL; | |
161 | + sp = SPEED_1000; | |
162 | + } else if (adv & lpa & ADVERTISE_1000XHALF) { | |
163 | + dplx = DUPLEX_HALF; | |
164 | + sp = SPEED_1000; | |
165 | + } | |
166 | + | |
167 | + if (fc && dplx == DUPLEX_FULL) { | |
168 | + if (lpa & adv & ADVERTISE_1000XPAUSE) | |
169 | + pause = PAUSE_RX | PAUSE_TX; | |
170 | + else if ((lpa & ADVERTISE_1000XPAUSE) && | |
171 | + (adv & lpa & ADVERTISE_1000XPSE_ASYM)) | |
172 | + pause = PAUSE_TX; | |
173 | + else if ((lpa & ADVERTISE_1000XPSE_ASYM) && | |
174 | + (adv & ADVERTISE_1000XPAUSE)) | |
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 | +/* | |
188 | + * Enable/disable auto MDI/MDI-X in forced link speed mode. | |
189 | + */ | |
190 | +static int vsc8211_set_automdi(struct cphy *phy, int enable) | |
191 | +{ | |
192 | + int err; | |
193 | + | |
194 | + err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0x52b5); | |
195 | + if (err) | |
196 | + return err; | |
197 | + | |
198 | + err = mdio_write(phy, 0, 18, 0x12); | |
199 | + if (err) | |
200 | + return err; | |
201 | + | |
202 | + err = mdio_write(phy, 0, 17, enable ? 0x2803 : 0x3003); | |
203 | + if (err) | |
204 | + return err; | |
205 | + | |
206 | + err = mdio_write(phy, 0, 16, 0x87fa); | |
207 | + if (err) | |
208 | + return err; | |
209 | + | |
210 | + err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0); | |
211 | + if (err) | |
212 | + return err; | |
213 | + | |
214 | + return 0; | |
215 | +} | |
216 | + | |
217 | +int vsc8211_set_speed_duplex(struct cphy *phy, int speed, int duplex) | |
218 | +{ | |
219 | + int err; | |
220 | + | |
221 | + err = t3_set_phy_speed_duplex(phy, speed, duplex); | |
222 | + if (!err) | |
223 | + err = vsc8211_set_automdi(phy, 1); | |
224 | + return err; | |
225 | +} | |
226 | + | |
227 | static int vsc8211_power_down(struct cphy *cphy, int enable) | |
228 | { | |
229 | return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN, | |
230 | @@ -221,12 +339,66 @@ static struct cphy_ops vsc8211_ops = { | |
231 | .power_down = vsc8211_power_down, | |
232 | }; | |
233 | ||
234 | +static struct cphy_ops vsc8211_fiber_ops = { | |
235 | + .reset = vsc8211_reset, | |
236 | + .intr_enable = vsc8211_intr_enable, | |
237 | + .intr_disable = vsc8211_intr_disable, | |
238 | + .intr_clear = vsc8211_intr_clear, | |
239 | + .intr_handler = vsc8211_intr_handler, | |
240 | + .autoneg_enable = vsc8211_autoneg_enable, | |
241 | + .autoneg_restart = vsc8211_autoneg_restart, | |
242 | + .advertise = t3_phy_advertise_fiber, | |
243 | + .set_speed_duplex = t3_set_phy_speed_duplex, | |
244 | + .get_link_status = vsc8211_get_link_status_fiber, | |
245 | + .power_down = vsc8211_power_down, | |
246 | +}; | |
247 | + | |
248 | int t3_vsc8211_phy_prep(struct cphy *phy, struct adapter *adapter, | |
249 | int phy_addr, const struct mdio_ops *mdio_ops) | |
250 | { | |
251 | + int err; | |
252 | + unsigned int val; | |
253 | + | |
254 | cphy_init(phy, adapter, phy_addr, &vsc8211_ops, mdio_ops, | |
255 | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Full | | |
256 | SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_MII | | |
257 | SUPPORTED_TP | SUPPORTED_IRQ, "10/100/1000BASE-T"); | |
258 | + msleep(20); /* PHY needs ~10ms to start responding to MDIO */ | |
259 | + | |
260 | + err = mdio_read(phy, 0, VSC8211_EXT_CTRL, &val); | |
261 | + if (err) | |
262 | + return err; | |
263 | + if (val & VSC_CTRL_MEDIA_MODE_HI) { | |
264 | + /* copper interface, just need to configure the LEDs */ | |
265 | + return mdio_write(phy, 0, VSC8211_LED_CTRL, 0x100); | |
266 | + } | |
267 | + | |
268 | + phy->caps = SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | | |
269 | + SUPPORTED_MII | SUPPORTED_FIBRE | SUPPORTED_IRQ; | |
270 | + phy->desc = "1000BASE-X"; | |
271 | + phy->ops = &vsc8211_fiber_ops; | |
272 | + | |
273 | + err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 1); | |
274 | + if (err) | |
275 | + return err; | |
276 | + | |
277 | + err = mdio_write(phy, 0, VSC8211_SIGDET_CTRL, 1); | |
278 | + if (err) | |
279 | + return err; | |
280 | + | |
281 | + err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0); | |
282 | + if (err) | |
283 | + return err; | |
284 | + | |
285 | + err = mdio_write(phy, 0, VSC8211_EXT_CTRL, | |
286 | + val | VSC_CTRL_CLAUSE37_VIEW); | |
287 | + if (err) | |
288 | + return err; | |
289 | + | |
290 | + err = vsc8211_reset(phy, 0); | |
291 | + if (err) | |
292 | + return err; | |
293 | + | |
294 | + udelay(5); /* delay after reset before next SMI */ | |
295 | return 0; | |
296 | } |