1 | /* |
2 | * Copyright (c) 2005-2008 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 | #include "regs.h" |
34 | |
35 | enum { |
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, |
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, |
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 */ |
58 | }; |
59 | |
60 | enum { edc_none, edc_sr, edc_twinax }; |
61 | |
62 | /* PHY module I2C device address */ |
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 | }; |
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; |
82 | }; |
83 | |
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) |
90 | err = t3_mdio_write(phy, mmd: rv->mmd_addr, reg: rv->reg_addr, |
91 | val: rv->set_bits); |
92 | else |
93 | err = t3_mdio_change_bits(phy, mmd: rv->mmd_addr, |
94 | reg: rv->reg_addr, clear: rv->clear_bits, |
95 | set: rv->set_bits); |
96 | } |
97 | return err; |
98 | } |
99 | |
100 | static void ael100x_txon(struct cphy *phy) |
101 | { |
102 | int tx_on_gpio = |
103 | phy->mdio.prtad == 0 ? F_GPIO7_OUT_VAL : F_GPIO2_OUT_VAL; |
104 | |
105 | msleep(msecs: 100); |
106 | t3_set_reg_field(adap: phy->adapter, A_T3DBG_GPIO_EN, mask: 0, val: tx_on_gpio); |
107 | msleep(msecs: 30); |
108 | } |
109 | |
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, reg: AEL_I2C_CTRL, |
119 | val: (dev_addr << 8) | (1 << 8) | word_addr); |
120 | if (err) |
121 | return err; |
122 | |
123 | for (i = 0; i < 200; i++) { |
124 | msleep(msecs: 1); |
125 | err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, reg: AEL_I2C_STAT, valp: &stat); |
126 | if (err) |
127 | return err; |
128 | if ((stat & 3) == 1) { |
129 | err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, reg: AEL_I2C_DATA, |
130 | valp: &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 | |
141 | static int ael1002_power_down(struct cphy *phy, int enable) |
142 | { |
143 | int err; |
144 | |
145 | err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, MDIO_PMA_TXDIS, val: !!enable); |
146 | if (!err) |
147 | err = mdio_set_flag(mdio: &phy->mdio, prtad: phy->mdio.prtad, |
148 | MDIO_MMD_PMAPMD, MDIO_CTRL1, |
149 | MDIO_CTRL1_LPOWER, sense: enable); |
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, enable: 0)) || |
158 | (err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, reg: AEL100X_TX_CONFIG1, val: 1)) || |
159 | (err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, reg: AEL1002_PWR_DOWN_HI, val: 0)) || |
160 | (err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, reg: AEL1002_PWR_DOWN_LO, val: 0)) || |
161 | (err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, reg: AEL1002_XFI_EQL, val: 0x18)) || |
162 | (err = t3_mdio_change_bits(phy, MDIO_MMD_PMAPMD, reg: AEL1002_LB_EN, |
163 | clear: 0, set: 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 | |
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) |
178 | { |
179 | if (link_ok) { |
180 | unsigned int stat0, stat1, stat2; |
181 | int err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, |
182 | MDIO_PMA_RXDET, valp: &stat0); |
183 | |
184 | if (!err) |
185 | err = t3_mdio_read(phy, MDIO_MMD_PCS, |
186 | MDIO_PCS_10GBRT_STAT1, valp: &stat1); |
187 | if (!err) |
188 | err = t3_mdio_read(phy, MDIO_MMD_PHYXS, |
189 | MDIO_PHYXS_LNSTAT, valp: &stat2); |
190 | if (err) |
191 | return err; |
192 | *link_ok = (stat0 & stat1 & (stat2 >> 12)) & 1; |
193 | } |
194 | if (speed) |
195 | *speed = SPEED_10000; |
196 | if (duplex) |
197 | *duplex = DUPLEX_FULL; |
198 | return 0; |
199 | } |
200 | |
201 | static const 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, |
207 | .get_link_status = get_link_status_r, |
208 | .power_down = ael1002_power_down, |
209 | .mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS, |
210 | }; |
211 | |
212 | int t3_ael1002_phy_prep(struct cphy *phy, struct adapter *adapter, |
213 | int phy_addr, const struct mdio_ops *mdio_ops) |
214 | { |
215 | cphy_init(phy, adapter, phy_addr, phy_ops: &ael1002_ops, mdio_ops, |
216 | SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE, |
217 | desc: "10GBASE-R" ); |
218 | ael100x_txon(phy); |
219 | return 0; |
220 | } |
221 | |
222 | static int ael1006_reset(struct cphy *phy, int wait) |
223 | { |
224 | return t3_phy_reset(phy, MDIO_MMD_PMAPMD, wait); |
225 | } |
226 | |
227 | static const struct cphy_ops ael1006_ops = { |
228 | .reset = ael1006_reset, |
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, |
233 | .get_link_status = get_link_status_r, |
234 | .power_down = ael1002_power_down, |
235 | .mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS, |
236 | }; |
237 | |
238 | int t3_ael1006_phy_prep(struct cphy *phy, struct adapter *adapter, |
239 | int phy_addr, const struct mdio_ops *mdio_ops) |
240 | { |
241 | cphy_init(phy, adapter, phy_addr, phy_ops: &ael1006_ops, mdio_ops, |
242 | SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE, |
243 | desc: "10GBASE-SR" ); |
244 | ael100x_txon(phy); |
245 | return 0; |
246 | } |
247 | |
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(msecs: delay_ms); |
257 | |
258 | /* see SFF-8472 for below */ |
259 | v = ael_i2c_rd(phy, dev_addr: MODULE_DEV_ADDR, word_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, dev_addr: MODULE_DEV_ADDR, word_addr: 6); |
271 | if (v < 0) |
272 | return v; |
273 | if (v != 4) |
274 | goto unknown; |
275 | |
276 | v = ael_i2c_rd(phy, dev_addr: MODULE_DEV_ADDR, word_addr: 10); |
277 | if (v < 0) |
278 | return v; |
279 | |
280 | if (v & 0x80) { |
281 | v = ael_i2c_rd(phy, dev_addr: MODULE_DEV_ADDR, word_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 | */ |
293 | static int ael2005_setup_sr_edc(struct cphy *phy) |
294 | { |
295 | static const struct reg_val regs[] = { |
296 | { MDIO_MMD_PMAPMD, 0xc003, 0xffff, 0x181 }, |
297 | { MDIO_MMD_PMAPMD, 0xc010, 0xffff, 0x448a }, |
298 | { MDIO_MMD_PMAPMD, 0xc04a, 0xffff, 0x5200 }, |
299 | { 0, 0, 0, 0 } |
300 | }; |
301 | |
302 | int i, err; |
303 | |
304 | err = set_phy_regs(phy, rv: regs); |
305 | if (err) |
306 | return err; |
307 | |
308 | msleep(msecs: 50); |
309 | |
310 | if (phy->priv != edc_sr) |
311 | err = t3_get_edc_fw(phy, edc_idx: EDC_OPT_AEL2005, |
312 | size: 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 | reg: phy->phy_cache[i], |
319 | val: phy->phy_cache[i + 1]); |
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 const struct reg_val regs[] = { |
328 | { MDIO_MMD_PMAPMD, 0xc04a, 0xffff, 0x5a00 }, |
329 | { 0, 0, 0, 0 } |
330 | }; |
331 | static const struct reg_val preemphasis[] = { |
332 | { MDIO_MMD_PMAPMD, 0xc014, 0xffff, 0xfe16 }, |
333 | { MDIO_MMD_PMAPMD, 0xc015, 0xffff, 0xa000 }, |
334 | { 0, 0, 0, 0 } |
335 | }; |
336 | int i, err; |
337 | |
338 | err = set_phy_regs(phy, rv: regs); |
339 | if (!err && modtype == phy_modtype_twinax_long) |
340 | err = set_phy_regs(phy, rv: preemphasis); |
341 | if (err) |
342 | return err; |
343 | |
344 | msleep(msecs: 50); |
345 | |
346 | if (phy->priv != edc_twinax) |
347 | err = t3_get_edc_fw(phy, edc_idx: EDC_TWX_AEL2005, |
348 | size: 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 | reg: phy->phy_cache[i], |
355 | val: phy->phy_cache[i + 1]); |
356 | if (!err) |
357 | phy->priv = edc_twinax; |
358 | return err; |
359 | } |
360 | |
361 | static int ael2005_get_module_type(struct cphy *phy, int delay_ms) |
362 | { |
363 | int v; |
364 | unsigned int stat; |
365 | |
366 | v = t3_mdio_read(phy, MDIO_MMD_PMAPMD, reg: AEL2005_GPIO_CTRL, valp: &stat); |
367 | if (v) |
368 | return v; |
369 | |
370 | if (stat & (1 << 8)) /* module absent */ |
371 | return phy_modtype_none; |
372 | |
373 | return ael2xxx_get_module_type(phy, delay_ms); |
374 | } |
375 | |
376 | static int ael2005_intr_enable(struct cphy *phy) |
377 | { |
378 | int err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, reg: AEL2005_GPIO_CTRL, val: 0x200); |
379 | return err ? err : t3_phy_lasi_intr_enable(phy); |
380 | } |
381 | |
382 | static int ael2005_intr_disable(struct cphy *phy) |
383 | { |
384 | int err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, reg: AEL2005_GPIO_CTRL, val: 0x100); |
385 | return err ? err : t3_phy_lasi_intr_disable(phy); |
386 | } |
387 | |
388 | static int ael2005_intr_clear(struct cphy *phy) |
389 | { |
390 | int err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, reg: AEL2005_GPIO_CTRL, val: 0xd00); |
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 const struct reg_val regs0[] = { |
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 }, |
404 | { 0, 0, 0, 0 } |
405 | }; |
406 | static const struct reg_val regs1[] = { |
407 | { MDIO_MMD_PMAPMD, 0xca00, 0xffff, 0x0080 }, |
408 | { MDIO_MMD_PMAPMD, 0xca12, 0xffff, 0 }, |
409 | { 0, 0, 0, 0 } |
410 | }; |
411 | |
412 | int err; |
413 | unsigned int lasi_ctrl; |
414 | |
415 | err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_CTRL, |
416 | valp: &lasi_ctrl); |
417 | if (err) |
418 | return err; |
419 | |
420 | err = t3_phy_reset(phy, MDIO_MMD_PMAPMD, wait: 0); |
421 | if (err) |
422 | return err; |
423 | |
424 | msleep(msecs: 125); |
425 | phy->priv = edc_none; |
426 | err = set_phy_regs(phy, rv: regs0); |
427 | if (err) |
428 | return err; |
429 | |
430 | msleep(msecs: 50); |
431 | |
432 | err = ael2005_get_module_type(phy, delay_ms: 0); |
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, modtype: err); |
439 | else |
440 | err = ael2005_setup_sr_edc(phy); |
441 | if (err) |
442 | return err; |
443 | |
444 | err = set_phy_regs(phy, rv: 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 | |
459 | ret = t3_mdio_read(phy, MDIO_MMD_PMAPMD, reg: AEL2005_GPIO_STAT, valp: &stat); |
460 | if (ret) |
461 | return ret; |
462 | |
463 | if (stat & AEL2005_MODDET_IRQ) { |
464 | ret = t3_mdio_write(phy, MDIO_MMD_PMAPMD, reg: AEL2005_GPIO_CTRL, |
465 | val: 0xd00); |
466 | if (ret) |
467 | return ret; |
468 | |
469 | /* modules have max 300 ms init time after hot plug */ |
470 | ret = ael2005_get_module_type(phy, delay_ms: 300); |
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, wait: 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 const 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, |
506 | .mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS, |
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, phy_ops: &ael2005_ops, mdio_ops, |
513 | SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE | |
514 | SUPPORTED_IRQ, desc: "10GBASE-R" ); |
515 | msleep(msecs: 125); |
516 | return t3_mdio_change_bits(phy, MDIO_MMD_PMAPMD, reg: AEL_OPT_SETTINGS, clear: 0, |
517 | set: 1 << 5); |
518 | } |
519 | |
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 const 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, rv: regs); |
540 | msleep(msecs: 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 const 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 const 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 const struct reg_val uCactivate[] = { |
568 | { MDIO_MMD_PMAPMD, 0xd080, 0xffff, 0x0100 }, |
569 | { MDIO_MMD_PMAPMD, 0xd092, 0xffff, 0x0000 }, |
570 | { 0, 0, 0, 0 } |
571 | }; |
572 | int i, err; |
573 | |
574 | /* set uC clock and activate it */ |
575 | err = set_phy_regs(phy, rv: uCclock40MHz); |
576 | msleep(msecs: 500); |
577 | if (err) |
578 | return err; |
579 | err = set_phy_regs(phy, rv: uCclockActivate); |
580 | msleep(msecs: 500); |
581 | if (err) |
582 | return err; |
583 | |
584 | if (phy->priv != edc_twinax) |
585 | err = t3_get_edc_fw(phy, edc_idx: EDC_TWX_AEL2020, |
586 | size: 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 | reg: phy->phy_cache[i], |
593 | val: phy->phy_cache[i + 1]); |
594 | /* activate uC */ |
595 | err = set_phy_regs(phy, rv: 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, reg: AEL2020_GPIO_STAT, valp: &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 | { |
627 | static const 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, rv: regs); |
645 | if (err) |
646 | return err; |
647 | |
648 | err = get_link_status_r(phy, link_ok: &link_ok, NULL, NULL, NULL); |
649 | if (err) |
650 | return err; |
651 | if (link_ok) |
652 | t3_link_changed(adapter: phy->adapter, |
653 | port_id: phy2portid(phy)); |
654 | |
655 | err = t3_phy_lasi_intr_enable(phy); |
656 | if (err) |
657 | return err; |
658 | |
659 | return 0; |
660 | } |
661 | |
662 | /* |
663 | * Disable PHY interrupts. The mirror of the above ... |
664 | */ |
665 | static int ael2020_intr_disable(struct cphy *phy) |
666 | { |
667 | static const 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, rv: regs); |
683 | if (err) |
684 | return err; |
685 | |
686 | return t3_phy_lasi_intr_disable(phy); |
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, reg: AEL2020_GPIO_INTR, valp: &stat); |
701 | return err ? err : t3_phy_lasi_intr_clear(phy); |
702 | } |
703 | |
704 | static const 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 | }; |
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 | { |
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 | valp: &lasi_ctrl); |
730 | if (err) |
731 | return err; |
732 | |
733 | err = t3_phy_reset(phy, MDIO_MMD_PMAPMD, wait: 125); |
734 | if (err) |
735 | return err; |
736 | msleep(msecs: 100); |
737 | |
738 | /* basic initialization for all module types */ |
739 | phy->priv = edc_none; |
740 | err = set_phy_regs(phy, rv: ael2020_reset_regs); |
741 | if (err) |
742 | return err; |
743 | |
744 | /* determine module type and perform appropriate initialization */ |
745 | err = ael2020_get_module_type(phy, delay_ms: 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, modtype: 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, reg: AEL2020_GPIO_INTR, valp: &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, delay_ms: 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, wait: 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 const 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 | { |
818 | cphy_init(phy, adapter, phy_addr, phy_ops: &ael2020_ops, mdio_ops, |
819 | SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE | |
820 | SUPPORTED_IRQ, desc: "10GBASE-R" ); |
821 | msleep(msecs: 125); |
822 | |
823 | return set_phy_regs(phy, rv: ael2020_reset_regs); |
824 | } |
825 | |
826 | /* |
827 | * Get link status for a 10GBASE-X device. |
828 | */ |
829 | static int get_link_status_x(struct cphy *phy, int *link_ok, int *speed, |
830 | int *duplex, int *fc) |
831 | { |
832 | if (link_ok) { |
833 | unsigned int stat0, stat1, stat2; |
834 | int err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, |
835 | MDIO_PMA_RXDET, valp: &stat0); |
836 | |
837 | if (!err) |
838 | err = t3_mdio_read(phy, MDIO_MMD_PCS, |
839 | MDIO_PCS_10GBX_STAT1, valp: &stat1); |
840 | if (!err) |
841 | err = t3_mdio_read(phy, MDIO_MMD_PHYXS, |
842 | MDIO_PHYXS_LNSTAT, valp: &stat2); |
843 | if (err) |
844 | return err; |
845 | *link_ok = (stat0 & (stat1 >> 12) & (stat2 >> 12)) & 1; |
846 | } |
847 | if (speed) |
848 | *speed = SPEED_10000; |
849 | if (duplex) |
850 | *duplex = DUPLEX_FULL; |
851 | return 0; |
852 | } |
853 | |
854 | static const struct cphy_ops qt2045_ops = { |
855 | .reset = ael1006_reset, |
856 | .intr_enable = t3_phy_lasi_intr_enable, |
857 | .intr_disable = t3_phy_lasi_intr_disable, |
858 | .intr_clear = t3_phy_lasi_intr_clear, |
859 | .intr_handler = t3_phy_lasi_intr_handler, |
860 | .get_link_status = get_link_status_x, |
861 | .power_down = ael1002_power_down, |
862 | .mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS, |
863 | }; |
864 | |
865 | int t3_qt2045_phy_prep(struct cphy *phy, struct adapter *adapter, |
866 | int phy_addr, const struct mdio_ops *mdio_ops) |
867 | { |
868 | unsigned int stat; |
869 | |
870 | cphy_init(phy, adapter, phy_addr, phy_ops: &qt2045_ops, mdio_ops, |
871 | SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_TP, |
872 | desc: "10GBASE-CX4" ); |
873 | |
874 | /* |
875 | * Some cards where the PHY is supposed to be at address 0 actually |
876 | * have it at 1. |
877 | */ |
878 | if (!phy_addr && |
879 | !t3_mdio_read(phy, MDIO_MMD_PMAPMD, MDIO_STAT1, valp: &stat) && |
880 | stat == 0xffff) |
881 | phy->mdio.prtad = 1; |
882 | return 0; |
883 | } |
884 | |
885 | static int xaui_direct_reset(struct cphy *phy, int wait) |
886 | { |
887 | return 0; |
888 | } |
889 | |
890 | static int xaui_direct_get_link_status(struct cphy *phy, int *link_ok, |
891 | int *speed, int *duplex, int *fc) |
892 | { |
893 | if (link_ok) { |
894 | unsigned int status; |
895 | int prtad = phy->mdio.prtad; |
896 | |
897 | status = t3_read_reg(adapter: phy->adapter, |
898 | XGM_REG(A_XGM_SERDES_STAT0, prtad)) | |
899 | t3_read_reg(adapter: phy->adapter, |
900 | XGM_REG(A_XGM_SERDES_STAT1, prtad)) | |
901 | t3_read_reg(adapter: phy->adapter, |
902 | XGM_REG(A_XGM_SERDES_STAT2, prtad)) | |
903 | t3_read_reg(adapter: phy->adapter, |
904 | XGM_REG(A_XGM_SERDES_STAT3, prtad)); |
905 | *link_ok = !(status & F_LOWSIG0); |
906 | } |
907 | if (speed) |
908 | *speed = SPEED_10000; |
909 | if (duplex) |
910 | *duplex = DUPLEX_FULL; |
911 | return 0; |
912 | } |
913 | |
914 | static int xaui_direct_power_down(struct cphy *phy, int enable) |
915 | { |
916 | return 0; |
917 | } |
918 | |
919 | static const struct cphy_ops xaui_direct_ops = { |
920 | .reset = xaui_direct_reset, |
921 | .intr_enable = ael1002_intr_noop, |
922 | .intr_disable = ael1002_intr_noop, |
923 | .intr_clear = ael1002_intr_noop, |
924 | .intr_handler = ael1002_intr_noop, |
925 | .get_link_status = xaui_direct_get_link_status, |
926 | .power_down = xaui_direct_power_down, |
927 | }; |
928 | |
929 | int t3_xaui_direct_phy_prep(struct cphy *phy, struct adapter *adapter, |
930 | int phy_addr, const struct mdio_ops *mdio_ops) |
931 | { |
932 | cphy_init(phy, adapter, phy_addr, phy_ops: &xaui_direct_ops, mdio_ops, |
933 | SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_TP, |
934 | desc: "10GBASE-CX4" ); |
935 | return 0; |
936 | } |
937 | |