blob: 5248f9e0b2f4c4a934a78cf8209381da57077978 [file] [log] [blame]
Divy Le Ray4d22de32007-01-18 22:04:14 -05001/*
Divy Le Raya02d44a2008-10-13 18:47:30 -07002 * Copyright (c) 2005-2008 Chelsio, Inc. All rights reserved.
Divy Le Ray4d22de32007-01-18 22:04:14 -05003 *
Divy Le Ray1d68e932007-01-30 19:44:35 -08004 * 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:
Divy Le Ray4d22de32007-01-18 22:04:14 -05009 *
Divy Le Ray1d68e932007-01-30 19:44:35 -080010 * 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.
Divy Le Ray4d22de32007-01-18 22:04:14 -050031 */
Divy Le Ray4d22de32007-01-18 22:04:14 -050032#include "common.h"
33#include "regs.h"
34
35enum {
Divy Le Ray4d22de32007-01-18 22:04:14 -050036 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,
Divy Le Ray1e882022008-10-08 17:40:07 -070041 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,
Divy Le Ray74451422009-05-29 12:52:44 +000047
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 */
Divy Le Ray4d22de32007-01-18 22:04:14 -050058};
59
Divy Le Ray1e882022008-10-08 17:40:07 -070060enum { edc_none, edc_sr, edc_twinax };
61
62/* PHY module I2C device address */
Divy Le Ray74451422009-05-29 12:52:44 +000063enum {
64 MODULE_DEV_ADDR = 0xa0,
65 SFF_DEV_ADDR = 0xa2,
66};
67
68/* PHY transceiver type */
69enum {
70 phy_transtype_unknown = 0,
71 phy_transtype_sfp = 3,
72 phy_transtype_xfp = 6,
73};
Divy Le Ray1e882022008-10-08 17:40:07 -070074
75#define AEL2005_MODDET_IRQ 4
76
77struct reg_val {
78 unsigned short mmd_addr;
79 unsigned short reg_addr;
80 unsigned short clear_bits;
81 unsigned short set_bits;
82};
83
84static 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)
Ben Hutchings0f07c4e2009-04-29 08:07:20 +000090 err = t3_mdio_write(phy, rv->mmd_addr, rv->reg_addr,
91 rv->set_bits);
Divy Le Ray1e882022008-10-08 17:40:07 -070092 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
Divy Le Ray4d22de32007-01-18 22:04:14 -0500100static void ael100x_txon(struct cphy *phy)
101{
Ben Hutchings0f07c4e2009-04-29 08:07:20 +0000102 int tx_on_gpio =
103 phy->mdio.prtad == 0 ? F_GPIO7_OUT_VAL : F_GPIO2_OUT_VAL;
Divy Le Ray4d22de32007-01-18 22:04:14 -0500104
105 msleep(100);
106 t3_set_reg_field(phy->adapter, A_T3DBG_GPIO_EN, 0, tx_on_gpio);
107 msleep(30);
108}
109
Divy Le Ray74451422009-05-29 12:52:44 +0000110/*
111 * Read an 8-bit word from a device attached to the PHY's i2c bus.
112 */
113static 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
Divy Le Ray4d22de32007-01-18 22:04:14 -0500141static int ael1002_power_down(struct cphy *phy, int enable)
142{
143 int err;
144
Ben Hutchings0f07c4e2009-04-29 08:07:20 +0000145 err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, MDIO_PMA_TXDIS, !!enable);
Divy Le Ray4d22de32007-01-18 22:04:14 -0500146 if (!err)
Ben Hutchings0f07c4e2009-04-29 08:07:20 +0000147 err = mdio_set_flag(&phy->mdio, phy->mdio.prtad,
148 MDIO_MMD_PMAPMD, MDIO_CTRL1,
149 MDIO_CTRL1_LPOWER, enable);
Divy Le Ray4d22de32007-01-18 22:04:14 -0500150 return err;
151}
152
153static int ael1002_reset(struct cphy *phy, int wait)
154{
155 int err;
156
157 if ((err = ael1002_power_down(phy, 0)) ||
Ben Hutchings0f07c4e2009-04-29 08:07:20 +0000158 (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,
Divy Le Ray4d22de32007-01-18 22:04:14 -0500163 0, 1 << 5)))
164 return err;
165 return 0;
166}
167
168static int ael1002_intr_noop(struct cphy *phy)
169{
170 return 0;
171}
172
Divy Le Ray1e882022008-10-08 17:40:07 -0700173/*
174 * Get link status for a 10GBASE-R device.
175 */
176static int get_link_status_r(struct cphy *phy, int *link_ok, int *speed,
177 int *duplex, int *fc)
Divy Le Ray4d22de32007-01-18 22:04:14 -0500178{
179 if (link_ok) {
Divy Le Ray1e882022008-10-08 17:40:07 -0700180 unsigned int stat0, stat1, stat2;
Ben Hutchings0f07c4e2009-04-29 08:07:20 +0000181 int err = t3_mdio_read(phy, MDIO_MMD_PMAPMD,
182 MDIO_PMA_RXDET, &stat0);
Divy Le Ray4d22de32007-01-18 22:04:14 -0500183
Divy Le Ray1e882022008-10-08 17:40:07 -0700184 if (!err)
Ben Hutchings0f07c4e2009-04-29 08:07:20 +0000185 err = t3_mdio_read(phy, MDIO_MMD_PCS,
186 MDIO_PCS_10GBRT_STAT1, &stat1);
Divy Le Ray1e882022008-10-08 17:40:07 -0700187 if (!err)
Ben Hutchings0f07c4e2009-04-29 08:07:20 +0000188 err = t3_mdio_read(phy, MDIO_MMD_PHYXS,
189 MDIO_PHYXS_LNSTAT, &stat2);
Divy Le Ray4d22de32007-01-18 22:04:14 -0500190 if (err)
191 return err;
Divy Le Ray1e882022008-10-08 17:40:07 -0700192 *link_ok = (stat0 & stat1 & (stat2 >> 12)) & 1;
Divy Le Ray4d22de32007-01-18 22:04:14 -0500193 }
194 if (speed)
195 *speed = SPEED_10000;
196 if (duplex)
197 *duplex = DUPLEX_FULL;
198 return 0;
199}
200
201static 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,
Divy Le Ray1e882022008-10-08 17:40:07 -0700207 .get_link_status = get_link_status_r,
Divy Le Ray4d22de32007-01-18 22:04:14 -0500208 .power_down = ael1002_power_down,
Ben Hutchings0f07c4e2009-04-29 08:07:20 +0000209 .mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS,
Divy Le Ray4d22de32007-01-18 22:04:14 -0500210};
211
Divy Le Ray78e46892008-10-08 17:38:01 -0700212int t3_ael1002_phy_prep(struct cphy *phy, struct adapter *adapter,
213 int phy_addr, const struct mdio_ops *mdio_ops)
Divy Le Ray4d22de32007-01-18 22:04:14 -0500214{
Divy Le Ray04497982008-10-08 17:38:29 -0700215 cphy_init(phy, adapter, phy_addr, &ael1002_ops, mdio_ops,
216 SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE,
217 "10GBASE-R");
Divy Le Ray4d22de32007-01-18 22:04:14 -0500218 ael100x_txon(phy);
Divy Le Ray78e46892008-10-08 17:38:01 -0700219 return 0;
Divy Le Ray4d22de32007-01-18 22:04:14 -0500220}
221
222static int ael1006_reset(struct cphy *phy, int wait)
223{
Ben Hutchings0f07c4e2009-04-29 08:07:20 +0000224 return t3_phy_reset(phy, MDIO_MMD_PMAPMD, wait);
Divy Le Ray4d22de32007-01-18 22:04:14 -0500225}
226
Divy Le Ray4d22de32007-01-18 22:04:14 -0500227static struct cphy_ops ael1006_ops = {
228 .reset = ael1006_reset,
Divy Le Ray9b1e3652008-10-08 17:39:31 -0700229 .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,
Divy Le Ray1e882022008-10-08 17:40:07 -0700233 .get_link_status = get_link_status_r,
Divy Le Ray619f05c2009-07-07 19:48:53 +0000234 .power_down = ael1002_power_down,
Ben Hutchings0f07c4e2009-04-29 08:07:20 +0000235 .mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS,
Divy Le Ray4d22de32007-01-18 22:04:14 -0500236};
237
Divy Le Ray78e46892008-10-08 17:38:01 -0700238int t3_ael1006_phy_prep(struct cphy *phy, struct adapter *adapter,
239 int phy_addr, const struct mdio_ops *mdio_ops)
Divy Le Ray4d22de32007-01-18 22:04:14 -0500240{
Divy Le Ray04497982008-10-08 17:38:29 -0700241 cphy_init(phy, adapter, phy_addr, &ael1006_ops, mdio_ops,
242 SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE,
243 "10GBASE-SR");
Divy Le Ray4d22de32007-01-18 22:04:14 -0500244 ael100x_txon(phy);
Divy Le Ray78e46892008-10-08 17:38:01 -0700245 return 0;
Divy Le Ray4d22de32007-01-18 22:04:14 -0500246}
247
Divy Le Ray74451422009-05-29 12:52:44 +0000248/*
249 * Decode our module type.
250 */
251static 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 }
286unknown:
287 return phy_modtype_unknown;
288}
289
290/*
291 * Code to support the Aeluros/NetLogic 2005 10Gb PHY.
292 */
Divy Le Ray1e882022008-10-08 17:40:07 -0700293static int ael2005_setup_sr_edc(struct cphy *phy)
294{
295 static struct reg_val regs[] = {
Ben Hutchings0f07c4e2009-04-29 08:07:20 +0000296 { MDIO_MMD_PMAPMD, 0xc003, 0xffff, 0x181 },
297 { MDIO_MMD_PMAPMD, 0xc010, 0xffff, 0x448a },
298 { MDIO_MMD_PMAPMD, 0xc04a, 0xffff, 0x5200 },
Divy Le Ray1e882022008-10-08 17:40:07 -0700299 { 0, 0, 0, 0 }
300 };
Divy Le Ray2e8c07c2009-07-07 19:49:09 +0000301
Divy Le Ray1e882022008-10-08 17:40:07 -0700302 int i, err;
303
304 err = set_phy_regs(phy, regs);
305 if (err)
306 return err;
307
308 msleep(50);
309
Divy Le Ray2e8c07c2009-07-07 19:49:09 +0000310 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]);
Divy Le Ray1e882022008-10-08 17:40:07 -0700320 if (!err)
321 phy->priv = edc_sr;
322 return err;
323}
324
325static int ael2005_setup_twinax_edc(struct cphy *phy, int modtype)
326{
327 static struct reg_val regs[] = {
Ben Hutchings0f07c4e2009-04-29 08:07:20 +0000328 { MDIO_MMD_PMAPMD, 0xc04a, 0xffff, 0x5a00 },
Divy Le Ray1e882022008-10-08 17:40:07 -0700329 { 0, 0, 0, 0 }
330 };
331 static struct reg_val preemphasis[] = {
Ben Hutchings0f07c4e2009-04-29 08:07:20 +0000332 { MDIO_MMD_PMAPMD, 0xc014, 0xffff, 0xfe16 },
333 { MDIO_MMD_PMAPMD, 0xc015, 0xffff, 0xa000 },
Divy Le Ray1e882022008-10-08 17:40:07 -0700334 { 0, 0, 0, 0 }
335 };
Divy Le Ray1e882022008-10-08 17:40:07 -0700336 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
Divy Le Ray2e8c07c2009-07-07 19:49:09 +0000346 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]);
Divy Le Ray1e882022008-10-08 17:40:07 -0700356 if (!err)
357 phy->priv = edc_twinax;
358 return err;
359}
360
Divy Le Ray74451422009-05-29 12:52:44 +0000361static int ael2005_get_module_type(struct cphy *phy, int delay_ms)
Divy Le Ray1e882022008-10-08 17:40:07 -0700362{
363 int v;
364 unsigned int stat;
365
Ben Hutchings0f07c4e2009-04-29 08:07:20 +0000366 v = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL, &stat);
Divy Le Ray1e882022008-10-08 17:40:07 -0700367 if (v)
368 return v;
369
370 if (stat & (1 << 8)) /* module absent */
371 return phy_modtype_none;
372
Divy Le Ray74451422009-05-29 12:52:44 +0000373 return ael2xxx_get_module_type(phy, delay_ms);
Divy Le Ray1e882022008-10-08 17:40:07 -0700374}
375
376static int ael2005_intr_enable(struct cphy *phy)
377{
Ben Hutchings0f07c4e2009-04-29 08:07:20 +0000378 int err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL, 0x200);
Divy Le Ray1e882022008-10-08 17:40:07 -0700379 return err ? err : t3_phy_lasi_intr_enable(phy);
380}
381
382static int ael2005_intr_disable(struct cphy *phy)
383{
Ben Hutchings0f07c4e2009-04-29 08:07:20 +0000384 int err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL, 0x100);
Divy Le Ray1e882022008-10-08 17:40:07 -0700385 return err ? err : t3_phy_lasi_intr_disable(phy);
386}
387
388static int ael2005_intr_clear(struct cphy *phy)
389{
Ben Hutchings0f07c4e2009-04-29 08:07:20 +0000390 int err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL, 0xd00);
Divy Le Ray1e882022008-10-08 17:40:07 -0700391 return err ? err : t3_phy_lasi_intr_clear(phy);
392}
393
394static int ael2005_reset(struct cphy *phy, int wait)
395{
396 static struct reg_val regs0[] = {
Ben Hutchings0f07c4e2009-04-29 08:07:20 +0000397 { 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 },
Divy Le Ray1e882022008-10-08 17:40:07 -0700404 { 0, 0, 0, 0 }
405 };
406 static struct reg_val regs1[] = {
Ben Hutchings0f07c4e2009-04-29 08:07:20 +0000407 { MDIO_MMD_PMAPMD, 0xca00, 0xffff, 0x0080 },
408 { MDIO_MMD_PMAPMD, 0xca12, 0xffff, 0 },
Divy Le Ray1e882022008-10-08 17:40:07 -0700409 { 0, 0, 0, 0 }
410 };
411
Hannes Edera243f842009-02-14 11:16:19 +0000412 int err;
413 unsigned int lasi_ctrl;
Divy Le Ray1e882022008-10-08 17:40:07 -0700414
Ben Hutchings64318332009-05-19 13:22:30 +0000415 err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_CTRL,
416 &lasi_ctrl);
Divy Le Ray1e882022008-10-08 17:40:07 -0700417 if (err)
418 return err;
419
Ben Hutchings0f07c4e2009-04-29 08:07:20 +0000420 err = t3_phy_reset(phy, MDIO_MMD_PMAPMD, 0);
Divy Le Ray1e882022008-10-08 17:40:07 -0700421 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
Divy Le Ray74451422009-05-29 12:52:44 +0000432 err = ael2005_get_module_type(phy, 0);
Divy Le Ray1e882022008-10-08 17:40:07 -0700433 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
454static int ael2005_intr_handler(struct cphy *phy)
455{
456 unsigned int stat;
457 int ret, edc_needed, cause = 0;
458
Ben Hutchings0f07c4e2009-04-29 08:07:20 +0000459 ret = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_STAT, &stat);
Divy Le Ray1e882022008-10-08 17:40:07 -0700460 if (ret)
461 return ret;
462
463 if (stat & AEL2005_MODDET_IRQ) {
Ben Hutchings0f07c4e2009-04-29 08:07:20 +0000464 ret = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL,
465 0xd00);
Divy Le Ray1e882022008-10-08 17:40:07 -0700466 if (ret)
467 return ret;
468
469 /* modules have max 300 ms init time after hot plug */
Divy Le Ray74451422009-05-29 12:52:44 +0000470 ret = ael2005_get_module_type(phy, 300);
Divy Le Ray1e882022008-10-08 17:40:07 -0700471 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
498static 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,
Ben Hutchings0f07c4e2009-04-29 08:07:20 +0000506 .mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS,
Divy Le Ray1e882022008-10-08 17:40:07 -0700507};
508
509int 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);
Ben Hutchings0f07c4e2009-04-29 08:07:20 +0000516 return t3_mdio_change_bits(phy, MDIO_MMD_PMAPMD, AEL_OPT_SETTINGS, 0,
Divy Le Ray1e882022008-10-08 17:40:07 -0700517 1 << 5);
518}
519
520/*
Divy Le Ray74451422009-05-29 12:52:44 +0000521 * Setup EDC and other parameters for operation with an optical module.
522 */
523static 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 */
551static 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 };
Divy Le Ray74451422009-05-29 12:52:44 +0000572 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
Divy Le Ray2e8c07c2009-07-07 19:49:09 +0000584 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]);
Divy Le Ray74451422009-05-29 12:52:44 +0000594 /* 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 */
604static 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 */
625static int ael2020_intr_enable(struct cphy *phy)
626{
Divy Le Ray5e659512009-07-07 19:48:43 +0000627 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;
Divy Le Ray74451422009-05-29 12:52:44 +0000660}
661
662/*
663 * Disable PHY interrupts. The mirror of the above ...
664 */
665static int ael2020_intr_disable(struct cphy *phy)
666{
Divy Le Ray5e659512009-07-07 19:48:43 +0000667 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);
Divy Le Ray74451422009-05-29 12:52:44 +0000687}
688
689/*
690 * Clear PHY interrupt state.
691 */
692static 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
Divy Le Ray5e659512009-07-07 19:48:43 +0000704static 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};
Divy Le Ray74451422009-05-29 12:52:44 +0000719/*
720 * Reset the PHY and put it into a canonical operating state.
721 */
722static int ael2020_reset(struct cphy *phy, int wait)
723{
Divy Le Ray74451422009-05-29 12:52:44 +0000724 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;
Divy Le Ray5e659512009-07-07 19:48:43 +0000740 err = set_phy_regs(phy, ael2020_reset_regs);
Divy Le Ray74451422009-05-29 12:52:44 +0000741 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 */
765static 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
804static 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
815int t3_ael2020_phy_prep(struct cphy *phy, struct adapter *adapter, int phy_addr,
816 const struct mdio_ops *mdio_ops)
817{
Divy Le Ray5e659512009-07-07 19:48:43 +0000818 int err;
819
Divy Le Ray74451422009-05-29 12:52:44 +0000820 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);
Divy Le Ray5e659512009-07-07 19:48:43 +0000824
825 err = set_phy_regs(phy, ael2020_reset_regs);
826 if (err)
827 return err;
Divy Le Ray74451422009-05-29 12:52:44 +0000828 return 0;
829}
830
831/*
Divy Le Ray1e882022008-10-08 17:40:07 -0700832 * Get link status for a 10GBASE-X device.
833 */
834static 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;
Ben Hutchings0f07c4e2009-04-29 08:07:20 +0000839 int err = t3_mdio_read(phy, MDIO_MMD_PMAPMD,
840 MDIO_PMA_RXDET, &stat0);
Divy Le Ray1e882022008-10-08 17:40:07 -0700841
842 if (!err)
Ben Hutchings0f07c4e2009-04-29 08:07:20 +0000843 err = t3_mdio_read(phy, MDIO_MMD_PCS,
844 MDIO_PCS_10GBX_STAT1, &stat1);
Divy Le Ray1e882022008-10-08 17:40:07 -0700845 if (!err)
Ben Hutchings0f07c4e2009-04-29 08:07:20 +0000846 err = t3_mdio_read(phy, MDIO_MMD_PHYXS,
847 MDIO_PHYXS_LNSTAT, &stat2);
Divy Le Ray1e882022008-10-08 17:40:07 -0700848 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
Divy Le Ray4d22de32007-01-18 22:04:14 -0500859static struct cphy_ops qt2045_ops = {
860 .reset = ael1006_reset,
Divy Le Ray9b1e3652008-10-08 17:39:31 -0700861 .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,
Divy Le Ray1e882022008-10-08 17:40:07 -0700865 .get_link_status = get_link_status_x,
Divy Le Ray619f05c2009-07-07 19:48:53 +0000866 .power_down = ael1002_power_down,
Ben Hutchings0f07c4e2009-04-29 08:07:20 +0000867 .mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS,
Divy Le Ray4d22de32007-01-18 22:04:14 -0500868};
869
Divy Le Ray78e46892008-10-08 17:38:01 -0700870int t3_qt2045_phy_prep(struct cphy *phy, struct adapter *adapter,
871 int phy_addr, const struct mdio_ops *mdio_ops)
Divy Le Ray4d22de32007-01-18 22:04:14 -0500872{
873 unsigned int stat;
874
Divy Le Ray04497982008-10-08 17:38:29 -0700875 cphy_init(phy, adapter, phy_addr, &qt2045_ops, mdio_ops,
876 SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_TP,
877 "10GBASE-CX4");
Divy Le Ray4d22de32007-01-18 22:04:14 -0500878
879 /*
880 * Some cards where the PHY is supposed to be at address 0 actually
881 * have it at 1.
882 */
Ben Hutchings0f07c4e2009-04-29 08:07:20 +0000883 if (!phy_addr &&
884 !t3_mdio_read(phy, MDIO_MMD_PMAPMD, MDIO_STAT1, &stat) &&
Divy Le Ray4d22de32007-01-18 22:04:14 -0500885 stat == 0xffff)
Ben Hutchings0f07c4e2009-04-29 08:07:20 +0000886 phy->mdio.prtad = 1;
Divy Le Ray78e46892008-10-08 17:38:01 -0700887 return 0;
Divy Le Ray4d22de32007-01-18 22:04:14 -0500888}
889
890static int xaui_direct_reset(struct cphy *phy, int wait)
891{
892 return 0;
893}
894
895static 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;
Ben Hutchings0f07c4e2009-04-29 08:07:20 +0000900 int prtad = phy->mdio.prtad;
Divy Le Ray4d22de32007-01-18 22:04:14 -0500901
902 status = t3_read_reg(phy->adapter,
Ben Hutchings0f07c4e2009-04-29 08:07:20 +0000903 XGM_REG(A_XGM_SERDES_STAT0, prtad)) |
Divy Le Rayc706bfb2007-05-30 10:01:39 -0700904 t3_read_reg(phy->adapter,
Ben Hutchings0f07c4e2009-04-29 08:07:20 +0000905 XGM_REG(A_XGM_SERDES_STAT1, prtad)) |
Divy Le Rayc706bfb2007-05-30 10:01:39 -0700906 t3_read_reg(phy->adapter,
Ben Hutchings0f07c4e2009-04-29 08:07:20 +0000907 XGM_REG(A_XGM_SERDES_STAT2, prtad)) |
Divy Le Rayc706bfb2007-05-30 10:01:39 -0700908 t3_read_reg(phy->adapter,
Ben Hutchings0f07c4e2009-04-29 08:07:20 +0000909 XGM_REG(A_XGM_SERDES_STAT3, prtad));
Divy Le Ray4d22de32007-01-18 22:04:14 -0500910 *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
919static int xaui_direct_power_down(struct cphy *phy, int enable)
920{
921 return 0;
922}
923
924static 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
Divy Le Ray78e46892008-10-08 17:38:01 -0700934int t3_xaui_direct_phy_prep(struct cphy *phy, struct adapter *adapter,
935 int phy_addr, const struct mdio_ops *mdio_ops)
Divy Le Ray4d22de32007-01-18 22:04:14 -0500936{
Ben Hutchings0f07c4e2009-04-29 08:07:20 +0000937 cphy_init(phy, adapter, MDIO_PRTAD_NONE, &xaui_direct_ops, mdio_ops,
Divy Le Ray04497982008-10-08 17:38:29 -0700938 SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_TP,
939 "10GBASE-CX4");
Divy Le Ray78e46892008-10-08 17:38:01 -0700940 return 0;
Divy Le Ray4d22de32007-01-18 22:04:14 -0500941}