Commit | Line | Data |
---|---|---|
4d22de3e | 1 | /* |
a02d44a0 | 2 | * Copyright (c) 2005-2008 Chelsio, Inc. All rights reserved. |
4d22de3e | 3 | * |
1d68e93d DLR |
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: | |
4d22de3e | 9 | * |
1d68e93d DLR |
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. | |
4d22de3e | 31 | */ |
4d22de3e DLR |
32 | #include "common.h" |
33 | #include "regs.h" | |
34 | ||
1e882025 | 35 | enum { |
4d22de3e DLR |
36 | AEL100X_TX_CONFIG1 = 0xc002, |
37 | AEL1002_PWR_DOWN_HI = 0xc011, | |
38 | AEL1002_PWR_DOWN_LO = 0xc012, | |
39 | AEL1002_XFI_EQL = 0xc015, | |
40 | AEL1002_LB_EN = 0xc017, | |
1e882025 DLR |
41 | AEL_OPT_SETTINGS = 0xc017, |
42 | AEL_I2C_CTRL = 0xc30a, | |
43 | AEL_I2C_DATA = 0xc30b, | |
44 | AEL_I2C_STAT = 0xc30c, | |
45 | AEL2005_GPIO_CTRL = 0xc214, | |
46 | AEL2005_GPIO_STAT = 0xc215, | |
74451424 DLR |
47 | |
48 | AEL2020_GPIO_INTR = 0xc103, /* Latch High (LH) */ | |
49 | AEL2020_GPIO_CTRL = 0xc108, /* Store Clear (SC) */ | |
50 | AEL2020_GPIO_STAT = 0xc10c, /* Read Only (RO) */ | |
51 | AEL2020_GPIO_CFG = 0xc110, /* Read Write (RW) */ | |
52 | ||
53 | AEL2020_GPIO_SDA = 0, /* IN: i2c serial data */ | |
54 | AEL2020_GPIO_MODDET = 1, /* IN: Module Detect */ | |
55 | AEL2020_GPIO_0 = 3, /* IN: unassigned */ | |
56 | AEL2020_GPIO_1 = 2, /* OUT: unassigned */ | |
57 | AEL2020_GPIO_LSTAT = AEL2020_GPIO_1, /* wired to link status LED */ | |
1e882025 DLR |
58 | }; |
59 | ||
60 | enum { edc_none, edc_sr, edc_twinax }; | |
61 | ||
62 | /* PHY module I2C device address */ | |
74451424 DLR |
63 | enum { |
64 | MODULE_DEV_ADDR = 0xa0, | |
65 | SFF_DEV_ADDR = 0xa2, | |
66 | }; | |
67 | ||
68 | /* PHY transceiver type */ | |
69 | enum { | |
70 | phy_transtype_unknown = 0, | |
71 | phy_transtype_sfp = 3, | |
72 | phy_transtype_xfp = 6, | |
73 | }; | |
1e882025 DLR |
74 | |
75 | #define AEL2005_MODDET_IRQ 4 | |
76 | ||
77 | struct reg_val { | |
78 | unsigned short mmd_addr; | |
79 | unsigned short reg_addr; | |
80 | unsigned short clear_bits; | |
81 | unsigned short set_bits; | |
4d22de3e DLR |
82 | }; |
83 | ||
1e882025 DLR |
84 | static int set_phy_regs(struct cphy *phy, const struct reg_val *rv) |
85 | { | |
86 | int err; | |
87 | ||
88 | for (err = 0; rv->mmd_addr && !err; rv++) { | |
89 | if (rv->clear_bits == 0xffff) | |
0f07c4ee BH |
90 | err = t3_mdio_write(phy, rv->mmd_addr, rv->reg_addr, |
91 | rv->set_bits); | |
1e882025 DLR |
92 | else |
93 | err = t3_mdio_change_bits(phy, rv->mmd_addr, | |
94 | rv->reg_addr, rv->clear_bits, | |
95 | rv->set_bits); | |
96 | } | |
97 | return err; | |
98 | } | |
99 | ||
4d22de3e DLR |
100 | static void ael100x_txon(struct cphy *phy) |
101 | { | |
0f07c4ee BH |
102 | int tx_on_gpio = |
103 | phy->mdio.prtad == 0 ? F_GPIO7_OUT_VAL : F_GPIO2_OUT_VAL; | |
4d22de3e DLR |
104 | |
105 | msleep(100); | |
106 | t3_set_reg_field(phy->adapter, A_T3DBG_GPIO_EN, 0, tx_on_gpio); | |
107 | msleep(30); | |
108 | } | |
109 | ||
74451424 DLR |
110 | /* |
111 | * Read an 8-bit word from a device attached to the PHY's i2c bus. | |
112 | */ | |
113 | static int ael_i2c_rd(struct cphy *phy, int dev_addr, int word_addr) | |
114 | { | |
115 | int i, err; | |
116 | unsigned int stat, data; | |
117 | ||
118 | err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL_I2C_CTRL, | |
119 | (dev_addr << 8) | (1 << 8) | word_addr); | |
120 | if (err) | |
121 | return err; | |
122 | ||
123 | for (i = 0; i < 200; i++) { | |
124 | msleep(1); | |
125 | err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL_I2C_STAT, &stat); | |
126 | if (err) | |
127 | return err; | |
128 | if ((stat & 3) == 1) { | |
129 | err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL_I2C_DATA, | |
130 | &data); | |
131 | if (err) | |
132 | return err; | |
133 | return data >> 8; | |
134 | } | |
135 | } | |
136 | CH_WARN(phy->adapter, "PHY %u i2c read of dev.addr %#x.%#x timed out\n", | |
137 | phy->mdio.prtad, dev_addr, word_addr); | |
138 | return -ETIMEDOUT; | |
139 | } | |
140 | ||
4d22de3e DLR |
141 | static int ael1002_power_down(struct cphy *phy, int enable) |
142 | { | |
143 | int err; | |
144 | ||
0f07c4ee | 145 | err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, MDIO_PMA_TXDIS, !!enable); |
4d22de3e | 146 | if (!err) |
0f07c4ee BH |
147 | err = mdio_set_flag(&phy->mdio, phy->mdio.prtad, |
148 | MDIO_MMD_PMAPMD, MDIO_CTRL1, | |
149 | MDIO_CTRL1_LPOWER, enable); | |
4d22de3e DLR |
150 | return err; |
151 | } | |
152 | ||
153 | static int ael1002_reset(struct cphy *phy, int wait) | |
154 | { | |
155 | int err; | |
156 | ||
157 | if ((err = ael1002_power_down(phy, 0)) || | |
0f07c4ee BH |
158 | (err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL100X_TX_CONFIG1, 1)) || |
159 | (err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL1002_PWR_DOWN_HI, 0)) || | |
160 | (err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL1002_PWR_DOWN_LO, 0)) || | |
161 | (err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL1002_XFI_EQL, 0x18)) || | |
162 | (err = t3_mdio_change_bits(phy, MDIO_MMD_PMAPMD, AEL1002_LB_EN, | |
4d22de3e DLR |
163 | 0, 1 << 5))) |
164 | return err; | |
165 | return 0; | |
166 | } | |
167 | ||
168 | static int ael1002_intr_noop(struct cphy *phy) | |
169 | { | |
170 | return 0; | |
171 | } | |
172 | ||
1e882025 DLR |
173 | /* |
174 | * Get link status for a 10GBASE-R device. | |
175 | */ | |
176 | static int get_link_status_r(struct cphy *phy, int *link_ok, int *speed, | |
177 | int *duplex, int *fc) | |
4d22de3e DLR |
178 | { |
179 | if (link_ok) { | |
1e882025 | 180 | unsigned int stat0, stat1, stat2; |
0f07c4ee BH |
181 | int err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, |
182 | MDIO_PMA_RXDET, &stat0); | |
1e882025 DLR |
183 | |
184 | if (!err) | |
0f07c4ee BH |
185 | err = t3_mdio_read(phy, MDIO_MMD_PCS, |
186 | MDIO_PCS_10GBRT_STAT1, &stat1); | |
1e882025 | 187 | if (!err) |
0f07c4ee BH |
188 | err = t3_mdio_read(phy, MDIO_MMD_PHYXS, |
189 | MDIO_PHYXS_LNSTAT, &stat2); | |
4d22de3e DLR |
190 | if (err) |
191 | return err; | |
1e882025 | 192 | *link_ok = (stat0 & stat1 & (stat2 >> 12)) & 1; |
4d22de3e DLR |
193 | } |
194 | if (speed) | |
195 | *speed = SPEED_10000; | |
196 | if (duplex) | |
197 | *duplex = DUPLEX_FULL; | |
198 | return 0; | |
199 | } | |
200 | ||
201 | static struct cphy_ops ael1002_ops = { | |
202 | .reset = ael1002_reset, | |
203 | .intr_enable = ael1002_intr_noop, | |
204 | .intr_disable = ael1002_intr_noop, | |
205 | .intr_clear = ael1002_intr_noop, | |
206 | .intr_handler = ael1002_intr_noop, | |
1e882025 | 207 | .get_link_status = get_link_status_r, |
4d22de3e | 208 | .power_down = ael1002_power_down, |
0f07c4ee | 209 | .mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS, |
4d22de3e DLR |
210 | }; |
211 | ||
78e4689e DLR |
212 | int t3_ael1002_phy_prep(struct cphy *phy, struct adapter *adapter, |
213 | int phy_addr, const struct mdio_ops *mdio_ops) | |
4d22de3e | 214 | { |
04497982 DLR |
215 | cphy_init(phy, adapter, phy_addr, &ael1002_ops, mdio_ops, |
216 | SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE, | |
217 | "10GBASE-R"); | |
4d22de3e | 218 | ael100x_txon(phy); |
78e4689e | 219 | return 0; |
4d22de3e DLR |
220 | } |
221 | ||
222 | static int ael1006_reset(struct cphy *phy, int wait) | |
223 | { | |
0f07c4ee | 224 | return t3_phy_reset(phy, MDIO_MMD_PMAPMD, wait); |
4d22de3e DLR |
225 | } |
226 | ||
4d22de3e DLR |
227 | static struct cphy_ops ael1006_ops = { |
228 | .reset = ael1006_reset, | |
9b1e3656 DLR |
229 | .intr_enable = t3_phy_lasi_intr_enable, |
230 | .intr_disable = t3_phy_lasi_intr_disable, | |
231 | .intr_clear = t3_phy_lasi_intr_clear, | |
232 | .intr_handler = t3_phy_lasi_intr_handler, | |
1e882025 | 233 | .get_link_status = get_link_status_r, |
619f05cf | 234 | .power_down = ael1002_power_down, |
0f07c4ee | 235 | .mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS, |
4d22de3e DLR |
236 | }; |
237 | ||
78e4689e DLR |
238 | int t3_ael1006_phy_prep(struct cphy *phy, struct adapter *adapter, |
239 | int phy_addr, const struct mdio_ops *mdio_ops) | |
4d22de3e | 240 | { |
04497982 DLR |
241 | cphy_init(phy, adapter, phy_addr, &ael1006_ops, mdio_ops, |
242 | SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE, | |
243 | "10GBASE-SR"); | |
4d22de3e | 244 | ael100x_txon(phy); |
78e4689e | 245 | return 0; |
4d22de3e DLR |
246 | } |
247 | ||
74451424 DLR |
248 | /* |
249 | * Decode our module type. | |
250 | */ | |
251 | static int ael2xxx_get_module_type(struct cphy *phy, int delay_ms) | |
252 | { | |
253 | int v; | |
254 | ||
255 | if (delay_ms) | |
256 | msleep(delay_ms); | |
257 | ||
258 | /* see SFF-8472 for below */ | |
259 | v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 3); | |
260 | if (v < 0) | |
261 | return v; | |
262 | ||
263 | if (v == 0x10) | |
264 | return phy_modtype_sr; | |
265 | if (v == 0x20) | |
266 | return phy_modtype_lr; | |
267 | if (v == 0x40) | |
268 | return phy_modtype_lrm; | |
269 | ||
270 | v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 6); | |
271 | if (v < 0) | |
272 | return v; | |
273 | if (v != 4) | |
274 | goto unknown; | |
275 | ||
276 | v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 10); | |
277 | if (v < 0) | |
278 | return v; | |
279 | ||
280 | if (v & 0x80) { | |
281 | v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 0x12); | |
282 | if (v < 0) | |
283 | return v; | |
284 | return v > 10 ? phy_modtype_twinax_long : phy_modtype_twinax; | |
285 | } | |
286 | unknown: | |
287 | return phy_modtype_unknown; | |
288 | } | |
289 | ||
290 | /* | |
291 | * Code to support the Aeluros/NetLogic 2005 10Gb PHY. | |
292 | */ | |
1e882025 DLR |
293 | static int ael2005_setup_sr_edc(struct cphy *phy) |
294 | { | |
295 | static struct reg_val regs[] = { | |
0f07c4ee BH |
296 | { MDIO_MMD_PMAPMD, 0xc003, 0xffff, 0x181 }, |
297 | { MDIO_MMD_PMAPMD, 0xc010, 0xffff, 0x448a }, | |
298 | { MDIO_MMD_PMAPMD, 0xc04a, 0xffff, 0x5200 }, | |
1e882025 DLR |
299 | { 0, 0, 0, 0 } |
300 | }; | |
2e8c07c3 | 301 | |
1e882025 DLR |
302 | int i, err; |
303 | ||
304 | err = set_phy_regs(phy, regs); | |
305 | if (err) | |
306 | return err; | |
307 | ||
308 | msleep(50); | |
309 | ||
2e8c07c3 DLR |
310 | if (phy->priv != edc_sr) |
311 | err = t3_get_edc_fw(phy, EDC_OPT_AEL2005, | |
312 | EDC_OPT_AEL2005_SIZE); | |
313 | if (err) | |
314 | return err; | |
315 | ||
316 | for (i = 0; i < EDC_OPT_AEL2005_SIZE / sizeof(u16) && !err; i += 2) | |
317 | err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, | |
318 | phy->phy_cache[i], | |
319 | phy->phy_cache[i + 1]); | |
1e882025 DLR |
320 | if (!err) |
321 | phy->priv = edc_sr; | |
322 | return err; | |
323 | } | |
324 | ||
325 | static int ael2005_setup_twinax_edc(struct cphy *phy, int modtype) | |
326 | { | |
327 | static struct reg_val regs[] = { | |
0f07c4ee | 328 | { MDIO_MMD_PMAPMD, 0xc04a, 0xffff, 0x5a00 }, |
1e882025 DLR |
329 | { 0, 0, 0, 0 } |
330 | }; | |
331 | static struct reg_val preemphasis[] = { | |
0f07c4ee BH |
332 | { MDIO_MMD_PMAPMD, 0xc014, 0xffff, 0xfe16 }, |
333 | { MDIO_MMD_PMAPMD, 0xc015, 0xffff, 0xa000 }, | |
1e882025 DLR |
334 | { 0, 0, 0, 0 } |
335 | }; | |
1e882025 DLR |
336 | int i, err; |
337 | ||
338 | err = set_phy_regs(phy, regs); | |
339 | if (!err && modtype == phy_modtype_twinax_long) | |
340 | err = set_phy_regs(phy, preemphasis); | |
341 | if (err) | |
342 | return err; | |
343 | ||
344 | msleep(50); | |
345 | ||
2e8c07c3 DLR |
346 | if (phy->priv != edc_twinax) |
347 | err = t3_get_edc_fw(phy, EDC_TWX_AEL2005, | |
348 | EDC_TWX_AEL2005_SIZE); | |
349 | if (err) | |
350 | return err; | |
351 | ||
352 | for (i = 0; i < EDC_TWX_AEL2005_SIZE / sizeof(u16) && !err; i += 2) | |
353 | err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, | |
354 | phy->phy_cache[i], | |
355 | phy->phy_cache[i + 1]); | |
1e882025 DLR |
356 | if (!err) |
357 | phy->priv = edc_twinax; | |
358 | return err; | |
359 | } | |
360 | ||
74451424 | 361 | static int ael2005_get_module_type(struct cphy *phy, int delay_ms) |
1e882025 DLR |
362 | { |
363 | int v; | |
364 | unsigned int stat; | |
365 | ||
0f07c4ee | 366 | v = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL, &stat); |
1e882025 DLR |
367 | if (v) |
368 | return v; | |
369 | ||
370 | if (stat & (1 << 8)) /* module absent */ | |
371 | return phy_modtype_none; | |
372 | ||
74451424 | 373 | return ael2xxx_get_module_type(phy, delay_ms); |
1e882025 DLR |
374 | } |
375 | ||
376 | static int ael2005_intr_enable(struct cphy *phy) | |
377 | { | |
0f07c4ee | 378 | int err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL, 0x200); |
1e882025 DLR |
379 | return err ? err : t3_phy_lasi_intr_enable(phy); |
380 | } | |
381 | ||
382 | static int ael2005_intr_disable(struct cphy *phy) | |
383 | { | |
0f07c4ee | 384 | int err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL, 0x100); |
1e882025 DLR |
385 | return err ? err : t3_phy_lasi_intr_disable(phy); |
386 | } | |
387 | ||
388 | static int ael2005_intr_clear(struct cphy *phy) | |
389 | { | |
0f07c4ee | 390 | int err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL, 0xd00); |
1e882025 DLR |
391 | return err ? err : t3_phy_lasi_intr_clear(phy); |
392 | } | |
393 | ||
394 | static int ael2005_reset(struct cphy *phy, int wait) | |
395 | { | |
396 | static struct reg_val regs0[] = { | |
0f07c4ee BH |
397 | { MDIO_MMD_PMAPMD, 0xc001, 0, 1 << 5 }, |
398 | { MDIO_MMD_PMAPMD, 0xc017, 0, 1 << 5 }, | |
399 | { MDIO_MMD_PMAPMD, 0xc013, 0xffff, 0xf341 }, | |
400 | { MDIO_MMD_PMAPMD, 0xc210, 0xffff, 0x8000 }, | |
401 | { MDIO_MMD_PMAPMD, 0xc210, 0xffff, 0x8100 }, | |
402 | { MDIO_MMD_PMAPMD, 0xc210, 0xffff, 0x8000 }, | |
403 | { MDIO_MMD_PMAPMD, 0xc210, 0xffff, 0 }, | |
1e882025 DLR |
404 | { 0, 0, 0, 0 } |
405 | }; | |
406 | static struct reg_val regs1[] = { | |
0f07c4ee BH |
407 | { MDIO_MMD_PMAPMD, 0xca00, 0xffff, 0x0080 }, |
408 | { MDIO_MMD_PMAPMD, 0xca12, 0xffff, 0 }, | |
1e882025 DLR |
409 | { 0, 0, 0, 0 } |
410 | }; | |
411 | ||
a243f848 HE |
412 | int err; |
413 | unsigned int lasi_ctrl; | |
1e882025 | 414 | |
64318334 BH |
415 | err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_CTRL, |
416 | &lasi_ctrl); | |
1e882025 DLR |
417 | if (err) |
418 | return err; | |
419 | ||
0f07c4ee | 420 | err = t3_phy_reset(phy, MDIO_MMD_PMAPMD, 0); |
1e882025 DLR |
421 | if (err) |
422 | return err; | |
423 | ||
424 | msleep(125); | |
425 | phy->priv = edc_none; | |
426 | err = set_phy_regs(phy, regs0); | |
427 | if (err) | |
428 | return err; | |
429 | ||
430 | msleep(50); | |
431 | ||
74451424 | 432 | err = ael2005_get_module_type(phy, 0); |
1e882025 DLR |
433 | if (err < 0) |
434 | return err; | |
435 | phy->modtype = err; | |
436 | ||
437 | if (err == phy_modtype_twinax || err == phy_modtype_twinax_long) | |
438 | err = ael2005_setup_twinax_edc(phy, err); | |
439 | else | |
440 | err = ael2005_setup_sr_edc(phy); | |
441 | if (err) | |
442 | return err; | |
443 | ||
444 | err = set_phy_regs(phy, regs1); | |
445 | if (err) | |
446 | return err; | |
447 | ||
448 | /* reset wipes out interrupts, reenable them if they were on */ | |
449 | if (lasi_ctrl & 1) | |
450 | err = ael2005_intr_enable(phy); | |
451 | return err; | |
452 | } | |
453 | ||
454 | static int ael2005_intr_handler(struct cphy *phy) | |
455 | { | |
456 | unsigned int stat; | |
457 | int ret, edc_needed, cause = 0; | |
458 | ||
0f07c4ee | 459 | ret = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_STAT, &stat); |
1e882025 DLR |
460 | if (ret) |
461 | return ret; | |
462 | ||
463 | if (stat & AEL2005_MODDET_IRQ) { | |
0f07c4ee BH |
464 | ret = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL, |
465 | 0xd00); | |
1e882025 DLR |
466 | if (ret) |
467 | return ret; | |
468 | ||
469 | /* modules have max 300 ms init time after hot plug */ | |
74451424 | 470 | ret = ael2005_get_module_type(phy, 300); |
1e882025 DLR |
471 | if (ret < 0) |
472 | return ret; | |
473 | ||
474 | phy->modtype = ret; | |
475 | if (ret == phy_modtype_none) | |
476 | edc_needed = phy->priv; /* on unplug retain EDC */ | |
477 | else if (ret == phy_modtype_twinax || | |
478 | ret == phy_modtype_twinax_long) | |
479 | edc_needed = edc_twinax; | |
480 | else | |
481 | edc_needed = edc_sr; | |
482 | ||
483 | if (edc_needed != phy->priv) { | |
484 | ret = ael2005_reset(phy, 0); | |
485 | return ret ? ret : cphy_cause_module_change; | |
486 | } | |
487 | cause = cphy_cause_module_change; | |
488 | } | |
489 | ||
490 | ret = t3_phy_lasi_intr_handler(phy); | |
491 | if (ret < 0) | |
492 | return ret; | |
493 | ||
494 | ret |= cause; | |
495 | return ret ? ret : cphy_cause_link_change; | |
496 | } | |
497 | ||
498 | static struct cphy_ops ael2005_ops = { | |
499 | .reset = ael2005_reset, | |
500 | .intr_enable = ael2005_intr_enable, | |
501 | .intr_disable = ael2005_intr_disable, | |
502 | .intr_clear = ael2005_intr_clear, | |
503 | .intr_handler = ael2005_intr_handler, | |
504 | .get_link_status = get_link_status_r, | |
505 | .power_down = ael1002_power_down, | |
0f07c4ee | 506 | .mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS, |
1e882025 DLR |
507 | }; |
508 | ||
509 | int t3_ael2005_phy_prep(struct cphy *phy, struct adapter *adapter, | |
510 | int phy_addr, const struct mdio_ops *mdio_ops) | |
511 | { | |
512 | cphy_init(phy, adapter, phy_addr, &ael2005_ops, mdio_ops, | |
513 | SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE | | |
514 | SUPPORTED_IRQ, "10GBASE-R"); | |
515 | msleep(125); | |
0f07c4ee | 516 | return t3_mdio_change_bits(phy, MDIO_MMD_PMAPMD, AEL_OPT_SETTINGS, 0, |
1e882025 DLR |
517 | 1 << 5); |
518 | } | |
519 | ||
74451424 DLR |
520 | /* |
521 | * Setup EDC and other parameters for operation with an optical module. | |
522 | */ | |
523 | static int ael2020_setup_sr_edc(struct cphy *phy) | |
524 | { | |
525 | static struct reg_val regs[] = { | |
526 | /* set CDR offset to 10 */ | |
527 | { MDIO_MMD_PMAPMD, 0xcc01, 0xffff, 0x488a }, | |
528 | ||
529 | /* adjust 10G RX bias current */ | |
530 | { MDIO_MMD_PMAPMD, 0xcb1b, 0xffff, 0x0200 }, | |
531 | { MDIO_MMD_PMAPMD, 0xcb1c, 0xffff, 0x00f0 }, | |
532 | { MDIO_MMD_PMAPMD, 0xcc06, 0xffff, 0x00e0 }, | |
533 | ||
534 | /* end */ | |
535 | { 0, 0, 0, 0 } | |
536 | }; | |
537 | int err; | |
538 | ||
539 | err = set_phy_regs(phy, regs); | |
540 | msleep(50); | |
541 | if (err) | |
542 | return err; | |
543 | ||
544 | phy->priv = edc_sr; | |
545 | return 0; | |
546 | } | |
547 | ||
548 | /* | |
549 | * Setup EDC and other parameters for operation with an TWINAX module. | |
550 | */ | |
551 | static int ael2020_setup_twinax_edc(struct cphy *phy, int modtype) | |
552 | { | |
553 | /* set uC to 40MHz */ | |
554 | static struct reg_val uCclock40MHz[] = { | |
555 | { MDIO_MMD_PMAPMD, 0xff28, 0xffff, 0x4001 }, | |
556 | { MDIO_MMD_PMAPMD, 0xff2a, 0xffff, 0x0002 }, | |
557 | { 0, 0, 0, 0 } | |
558 | }; | |
559 | ||
560 | /* activate uC clock */ | |
561 | static struct reg_val uCclockActivate[] = { | |
562 | { MDIO_MMD_PMAPMD, 0xd000, 0xffff, 0x5200 }, | |
563 | { 0, 0, 0, 0 } | |
564 | }; | |
565 | ||
566 | /* set PC to start of SRAM and activate uC */ | |
567 | static struct reg_val uCactivate[] = { | |
568 | { MDIO_MMD_PMAPMD, 0xd080, 0xffff, 0x0100 }, | |
569 | { MDIO_MMD_PMAPMD, 0xd092, 0xffff, 0x0000 }, | |
570 | { 0, 0, 0, 0 } | |
571 | }; | |
74451424 DLR |
572 | int i, err; |
573 | ||
574 | /* set uC clock and activate it */ | |
575 | err = set_phy_regs(phy, uCclock40MHz); | |
576 | msleep(500); | |
577 | if (err) | |
578 | return err; | |
579 | err = set_phy_regs(phy, uCclockActivate); | |
580 | msleep(500); | |
581 | if (err) | |
582 | return err; | |
583 | ||
2e8c07c3 DLR |
584 | if (phy->priv != edc_twinax) |
585 | err = t3_get_edc_fw(phy, EDC_TWX_AEL2020, | |
586 | EDC_TWX_AEL2020_SIZE); | |
587 | if (err) | |
588 | return err; | |
589 | ||
590 | for (i = 0; i < EDC_TWX_AEL2020_SIZE / sizeof(u16) && !err; i += 2) | |
591 | err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, | |
592 | phy->phy_cache[i], | |
593 | phy->phy_cache[i + 1]); | |
74451424 DLR |
594 | /* activate uC */ |
595 | err = set_phy_regs(phy, uCactivate); | |
596 | if (!err) | |
597 | phy->priv = edc_twinax; | |
598 | return err; | |
599 | } | |
600 | ||
601 | /* | |
602 | * Return Module Type. | |
603 | */ | |
604 | static int ael2020_get_module_type(struct cphy *phy, int delay_ms) | |
605 | { | |
606 | int v; | |
607 | unsigned int stat; | |
608 | ||
609 | v = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2020_GPIO_STAT, &stat); | |
610 | if (v) | |
611 | return v; | |
612 | ||
613 | if (stat & (0x1 << (AEL2020_GPIO_MODDET*4))) { | |
614 | /* module absent */ | |
615 | return phy_modtype_none; | |
616 | } | |
617 | ||
618 | return ael2xxx_get_module_type(phy, delay_ms); | |
619 | } | |
620 | ||
621 | /* | |
622 | * Enable PHY interrupts. We enable "Module Detection" interrupts (on any | |
623 | * state transition) and then generic Link Alarm Status Interrupt (LASI). | |
624 | */ | |
625 | static int ael2020_intr_enable(struct cphy *phy) | |
626 | { | |
5e659515 DLR |
627 | struct reg_val regs[] = { |
628 | /* output Module's Loss Of Signal (LOS) to LED */ | |
629 | { MDIO_MMD_PMAPMD, AEL2020_GPIO_CFG+AEL2020_GPIO_LSTAT, | |
630 | 0xffff, 0x4 }, | |
631 | { MDIO_MMD_PMAPMD, AEL2020_GPIO_CTRL, | |
632 | 0xffff, 0x8 << (AEL2020_GPIO_LSTAT*4) }, | |
633 | ||
634 | /* enable module detect status change interrupts */ | |
635 | { MDIO_MMD_PMAPMD, AEL2020_GPIO_CTRL, | |
636 | 0xffff, 0x2 << (AEL2020_GPIO_MODDET*4) }, | |
637 | ||
638 | /* end */ | |
639 | { 0, 0, 0, 0 } | |
640 | }; | |
641 | int err, link_ok = 0; | |
642 | ||
643 | /* set up "link status" LED and enable module change interrupts */ | |
644 | err = set_phy_regs(phy, regs); | |
645 | if (err) | |
646 | return err; | |
647 | ||
648 | err = get_link_status_r(phy, &link_ok, NULL, NULL, NULL); | |
649 | if (err) | |
650 | return err; | |
651 | if (link_ok) | |
652 | t3_link_changed(phy->adapter, | |
653 | phy2portid(phy)); | |
654 | ||
655 | err = t3_phy_lasi_intr_enable(phy); | |
656 | if (err) | |
657 | return err; | |
658 | ||
659 | return 0; | |
74451424 DLR |
660 | } |
661 | ||
662 | /* | |
663 | * Disable PHY interrupts. The mirror of the above ... | |
664 | */ | |
665 | static int ael2020_intr_disable(struct cphy *phy) | |
666 | { | |
5e659515 DLR |
667 | struct reg_val regs[] = { |
668 | /* reset "link status" LED to "off" */ | |
669 | { MDIO_MMD_PMAPMD, AEL2020_GPIO_CTRL, | |
670 | 0xffff, 0xb << (AEL2020_GPIO_LSTAT*4) }, | |
671 | ||
672 | /* disable module detect status change interrupts */ | |
673 | { MDIO_MMD_PMAPMD, AEL2020_GPIO_CTRL, | |
674 | 0xffff, 0x1 << (AEL2020_GPIO_MODDET*4) }, | |
675 | ||
676 | /* end */ | |
677 | { 0, 0, 0, 0 } | |
678 | }; | |
679 | int err; | |
680 | ||
681 | /* turn off "link status" LED and disable module change interrupts */ | |
682 | err = set_phy_regs(phy, regs); | |
683 | if (err) | |
684 | return err; | |
685 | ||
686 | return t3_phy_lasi_intr_disable(phy); | |
74451424 DLR |
687 | } |
688 | ||
689 | /* | |
690 | * Clear PHY interrupt state. | |
691 | */ | |
692 | static int ael2020_intr_clear(struct cphy *phy) | |
693 | { | |
694 | /* | |
695 | * The GPIO Interrupt register on the AEL2020 is a "Latching High" | |
696 | * (LH) register which is cleared to the current state when it's read. | |
697 | * Thus, we simply read the register and discard the result. | |
698 | */ | |
699 | unsigned int stat; | |
700 | int err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2020_GPIO_INTR, &stat); | |
701 | return err ? err : t3_phy_lasi_intr_clear(phy); | |
702 | } | |
703 | ||
5e659515 DLR |
704 | static struct reg_val ael2020_reset_regs[] = { |
705 | /* Erratum #2: CDRLOL asserted, causing PMA link down status */ | |
706 | { MDIO_MMD_PMAPMD, 0xc003, 0xffff, 0x3101 }, | |
707 | ||
708 | /* force XAUI to send LF when RX_LOS is asserted */ | |
709 | { MDIO_MMD_PMAPMD, 0xcd40, 0xffff, 0x0001 }, | |
710 | ||
711 | /* allow writes to transceiver module EEPROM on i2c bus */ | |
712 | { MDIO_MMD_PMAPMD, 0xff02, 0xffff, 0x0023 }, | |
713 | { MDIO_MMD_PMAPMD, 0xff03, 0xffff, 0x0000 }, | |
714 | { MDIO_MMD_PMAPMD, 0xff04, 0xffff, 0x0000 }, | |
715 | ||
716 | /* end */ | |
717 | { 0, 0, 0, 0 } | |
718 | }; | |
74451424 DLR |
719 | /* |
720 | * Reset the PHY and put it into a canonical operating state. | |
721 | */ | |
722 | static int ael2020_reset(struct cphy *phy, int wait) | |
723 | { | |
74451424 DLR |
724 | int err; |
725 | unsigned int lasi_ctrl; | |
726 | ||
727 | /* grab current interrupt state */ | |
728 | err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_CTRL, | |
729 | &lasi_ctrl); | |
730 | if (err) | |
731 | return err; | |
732 | ||
733 | err = t3_phy_reset(phy, MDIO_MMD_PMAPMD, 125); | |
734 | if (err) | |
735 | return err; | |
736 | msleep(100); | |
737 | ||
738 | /* basic initialization for all module types */ | |
739 | phy->priv = edc_none; | |
5e659515 | 740 | err = set_phy_regs(phy, ael2020_reset_regs); |
74451424 DLR |
741 | if (err) |
742 | return err; | |
743 | ||
744 | /* determine module type and perform appropriate initialization */ | |
745 | err = ael2020_get_module_type(phy, 0); | |
746 | if (err < 0) | |
747 | return err; | |
748 | phy->modtype = (u8)err; | |
749 | if (err == phy_modtype_twinax || err == phy_modtype_twinax_long) | |
750 | err = ael2020_setup_twinax_edc(phy, err); | |
751 | else | |
752 | err = ael2020_setup_sr_edc(phy); | |
753 | if (err) | |
754 | return err; | |
755 | ||
756 | /* reset wipes out interrupts, reenable them if they were on */ | |
757 | if (lasi_ctrl & 1) | |
758 | err = ael2005_intr_enable(phy); | |
759 | return err; | |
760 | } | |
761 | ||
762 | /* | |
763 | * Handle a PHY interrupt. | |
764 | */ | |
765 | static int ael2020_intr_handler(struct cphy *phy) | |
766 | { | |
767 | unsigned int stat; | |
768 | int ret, edc_needed, cause = 0; | |
769 | ||
770 | ret = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2020_GPIO_INTR, &stat); | |
771 | if (ret) | |
772 | return ret; | |
773 | ||
774 | if (stat & (0x1 << AEL2020_GPIO_MODDET)) { | |
775 | /* modules have max 300 ms init time after hot plug */ | |
776 | ret = ael2020_get_module_type(phy, 300); | |
777 | if (ret < 0) | |
778 | return ret; | |
779 | ||
780 | phy->modtype = (u8)ret; | |
781 | if (ret == phy_modtype_none) | |
782 | edc_needed = phy->priv; /* on unplug retain EDC */ | |
783 | else if (ret == phy_modtype_twinax || | |
784 | ret == phy_modtype_twinax_long) | |
785 | edc_needed = edc_twinax; | |
786 | else | |
787 | edc_needed = edc_sr; | |
788 | ||
789 | if (edc_needed != phy->priv) { | |
790 | ret = ael2020_reset(phy, 0); | |
791 | return ret ? ret : cphy_cause_module_change; | |
792 | } | |
793 | cause = cphy_cause_module_change; | |
794 | } | |
795 | ||
796 | ret = t3_phy_lasi_intr_handler(phy); | |
797 | if (ret < 0) | |
798 | return ret; | |
799 | ||
800 | ret |= cause; | |
801 | return ret ? ret : cphy_cause_link_change; | |
802 | } | |
803 | ||
804 | static struct cphy_ops ael2020_ops = { | |
805 | .reset = ael2020_reset, | |
806 | .intr_enable = ael2020_intr_enable, | |
807 | .intr_disable = ael2020_intr_disable, | |
808 | .intr_clear = ael2020_intr_clear, | |
809 | .intr_handler = ael2020_intr_handler, | |
810 | .get_link_status = get_link_status_r, | |
811 | .power_down = ael1002_power_down, | |
812 | .mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS, | |
813 | }; | |
814 | ||
815 | int t3_ael2020_phy_prep(struct cphy *phy, struct adapter *adapter, int phy_addr, | |
816 | const struct mdio_ops *mdio_ops) | |
817 | { | |
5e659515 DLR |
818 | int err; |
819 | ||
74451424 DLR |
820 | cphy_init(phy, adapter, phy_addr, &ael2020_ops, mdio_ops, |
821 | SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE | | |
822 | SUPPORTED_IRQ, "10GBASE-R"); | |
823 | msleep(125); | |
5e659515 DLR |
824 | |
825 | err = set_phy_regs(phy, ael2020_reset_regs); | |
826 | if (err) | |
827 | return err; | |
74451424 DLR |
828 | return 0; |
829 | } | |
830 | ||
1e882025 DLR |
831 | /* |
832 | * Get link status for a 10GBASE-X device. | |
833 | */ | |
834 | static int get_link_status_x(struct cphy *phy, int *link_ok, int *speed, | |
835 | int *duplex, int *fc) | |
836 | { | |
837 | if (link_ok) { | |
838 | unsigned int stat0, stat1, stat2; | |
0f07c4ee BH |
839 | int err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, |
840 | MDIO_PMA_RXDET, &stat0); | |
1e882025 DLR |
841 | |
842 | if (!err) | |
0f07c4ee BH |
843 | err = t3_mdio_read(phy, MDIO_MMD_PCS, |
844 | MDIO_PCS_10GBX_STAT1, &stat1); | |
1e882025 | 845 | if (!err) |
0f07c4ee BH |
846 | err = t3_mdio_read(phy, MDIO_MMD_PHYXS, |
847 | MDIO_PHYXS_LNSTAT, &stat2); | |
1e882025 DLR |
848 | if (err) |
849 | return err; | |
850 | *link_ok = (stat0 & (stat1 >> 12) & (stat2 >> 12)) & 1; | |
851 | } | |
852 | if (speed) | |
853 | *speed = SPEED_10000; | |
854 | if (duplex) | |
855 | *duplex = DUPLEX_FULL; | |
856 | return 0; | |
857 | } | |
858 | ||
4d22de3e DLR |
859 | static struct cphy_ops qt2045_ops = { |
860 | .reset = ael1006_reset, | |
9b1e3656 DLR |
861 | .intr_enable = t3_phy_lasi_intr_enable, |
862 | .intr_disable = t3_phy_lasi_intr_disable, | |
863 | .intr_clear = t3_phy_lasi_intr_clear, | |
864 | .intr_handler = t3_phy_lasi_intr_handler, | |
1e882025 | 865 | .get_link_status = get_link_status_x, |
619f05cf | 866 | .power_down = ael1002_power_down, |
0f07c4ee | 867 | .mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS, |
4d22de3e DLR |
868 | }; |
869 | ||
78e4689e DLR |
870 | int t3_qt2045_phy_prep(struct cphy *phy, struct adapter *adapter, |
871 | int phy_addr, const struct mdio_ops *mdio_ops) | |
4d22de3e DLR |
872 | { |
873 | unsigned int stat; | |
874 | ||
04497982 DLR |
875 | cphy_init(phy, adapter, phy_addr, &qt2045_ops, mdio_ops, |
876 | SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_TP, | |
877 | "10GBASE-CX4"); | |
4d22de3e DLR |
878 | |
879 | /* | |
880 | * Some cards where the PHY is supposed to be at address 0 actually | |
881 | * have it at 1. | |
882 | */ | |
0f07c4ee BH |
883 | if (!phy_addr && |
884 | !t3_mdio_read(phy, MDIO_MMD_PMAPMD, MDIO_STAT1, &stat) && | |
4d22de3e | 885 | stat == 0xffff) |
0f07c4ee | 886 | phy->mdio.prtad = 1; |
78e4689e | 887 | return 0; |
4d22de3e DLR |
888 | } |
889 | ||
890 | static int xaui_direct_reset(struct cphy *phy, int wait) | |
891 | { | |
892 | return 0; | |
893 | } | |
894 | ||
895 | static int xaui_direct_get_link_status(struct cphy *phy, int *link_ok, | |
896 | int *speed, int *duplex, int *fc) | |
897 | { | |
898 | if (link_ok) { | |
899 | unsigned int status; | |
0f07c4ee | 900 | int prtad = phy->mdio.prtad; |
4d22de3e DLR |
901 | |
902 | status = t3_read_reg(phy->adapter, | |
0f07c4ee | 903 | XGM_REG(A_XGM_SERDES_STAT0, prtad)) | |
c706bfb5 | 904 | t3_read_reg(phy->adapter, |
0f07c4ee | 905 | XGM_REG(A_XGM_SERDES_STAT1, prtad)) | |
c706bfb5 | 906 | t3_read_reg(phy->adapter, |
0f07c4ee | 907 | XGM_REG(A_XGM_SERDES_STAT2, prtad)) | |
c706bfb5 | 908 | t3_read_reg(phy->adapter, |
0f07c4ee | 909 | XGM_REG(A_XGM_SERDES_STAT3, prtad)); |
4d22de3e DLR |
910 | *link_ok = !(status & F_LOWSIG0); |
911 | } | |
912 | if (speed) | |
913 | *speed = SPEED_10000; | |
914 | if (duplex) | |
915 | *duplex = DUPLEX_FULL; | |
916 | return 0; | |
917 | } | |
918 | ||
919 | static int xaui_direct_power_down(struct cphy *phy, int enable) | |
920 | { | |
921 | return 0; | |
922 | } | |
923 | ||
924 | static struct cphy_ops xaui_direct_ops = { | |
925 | .reset = xaui_direct_reset, | |
926 | .intr_enable = ael1002_intr_noop, | |
927 | .intr_disable = ael1002_intr_noop, | |
928 | .intr_clear = ael1002_intr_noop, | |
929 | .intr_handler = ael1002_intr_noop, | |
930 | .get_link_status = xaui_direct_get_link_status, | |
931 | .power_down = xaui_direct_power_down, | |
932 | }; | |
933 | ||
78e4689e DLR |
934 | int t3_xaui_direct_phy_prep(struct cphy *phy, struct adapter *adapter, |
935 | int phy_addr, const struct mdio_ops *mdio_ops) | |
4d22de3e | 936 | { |
0f07c4ee | 937 | cphy_init(phy, adapter, MDIO_PRTAD_NONE, &xaui_direct_ops, mdio_ops, |
04497982 DLR |
938 | SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_TP, |
939 | "10GBASE-CX4"); | |
78e4689e | 940 | return 0; |
4d22de3e | 941 | } |