xref: /OK3568_Linux_fs/kernel/drivers/net/ethernet/chelsio/cxgb3/ael1002.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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