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