xref: /OK3568_Linux_fs/kernel/drivers/net/ethernet/chelsio/cxgb/mv88x201x.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*****************************************************************************
2*4882a593Smuzhiyun  *                                                                           *
3*4882a593Smuzhiyun  * File: mv88x201x.c                                                         *
4*4882a593Smuzhiyun  * $Revision: 1.12 $                                                         *
5*4882a593Smuzhiyun  * $Date: 2005/04/15 19:27:14 $                                              *
6*4882a593Smuzhiyun  * Description:                                                              *
7*4882a593Smuzhiyun  *  Marvell PHY (mv88x201x) functionality.                                   *
8*4882a593Smuzhiyun  *  part of the Chelsio 10Gb Ethernet Driver.                                *
9*4882a593Smuzhiyun  *                                                                           *
10*4882a593Smuzhiyun  * This program is free software; you can redistribute it and/or modify      *
11*4882a593Smuzhiyun  * it under the terms of the GNU General Public License, version 2, as       *
12*4882a593Smuzhiyun  * published by the Free Software Foundation.                                *
13*4882a593Smuzhiyun  *                                                                           *
14*4882a593Smuzhiyun  * You should have received a copy of the GNU General Public License along   *
15*4882a593Smuzhiyun  * with this program; if not, see <http://www.gnu.org/licenses/>.            *
16*4882a593Smuzhiyun  *                                                                           *
17*4882a593Smuzhiyun  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED    *
18*4882a593Smuzhiyun  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF      *
19*4882a593Smuzhiyun  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.                     *
20*4882a593Smuzhiyun  *                                                                           *
21*4882a593Smuzhiyun  * http://www.chelsio.com                                                    *
22*4882a593Smuzhiyun  *                                                                           *
23*4882a593Smuzhiyun  * Copyright (c) 2003 - 2005 Chelsio Communications, Inc.                    *
24*4882a593Smuzhiyun  * All rights reserved.                                                      *
25*4882a593Smuzhiyun  *                                                                           *
26*4882a593Smuzhiyun  * Maintainers: maintainers@chelsio.com                                      *
27*4882a593Smuzhiyun  *                                                                           *
28*4882a593Smuzhiyun  * Authors: Dimitrios Michailidis   <dm@chelsio.com>                         *
29*4882a593Smuzhiyun  *          Tina Yang               <tainay@chelsio.com>                     *
30*4882a593Smuzhiyun  *          Felix Marti             <felix@chelsio.com>                      *
31*4882a593Smuzhiyun  *          Scott Bardone           <sbardone@chelsio.com>                   *
32*4882a593Smuzhiyun  *          Kurt Ottaway            <kottaway@chelsio.com>                   *
33*4882a593Smuzhiyun  *          Frank DiMambro          <frank@chelsio.com>                      *
34*4882a593Smuzhiyun  *                                                                           *
35*4882a593Smuzhiyun  * History:                                                                  *
36*4882a593Smuzhiyun  *                                                                           *
37*4882a593Smuzhiyun  ****************************************************************************/
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun #include "cphy.h"
40*4882a593Smuzhiyun #include "elmer0.h"
41*4882a593Smuzhiyun 
42*4882a593Smuzhiyun /*
43*4882a593Smuzhiyun  * The 88x2010 Rev C. requires some link status registers * to be read
44*4882a593Smuzhiyun  * twice in order to get the right values. Future * revisions will fix
45*4882a593Smuzhiyun  * this problem and then this macro * can disappear.
46*4882a593Smuzhiyun  */
47*4882a593Smuzhiyun #define MV88x2010_LINK_STATUS_BUGS    1
48*4882a593Smuzhiyun 
led_init(struct cphy * cphy)49*4882a593Smuzhiyun static int led_init(struct cphy *cphy)
50*4882a593Smuzhiyun {
51*4882a593Smuzhiyun 	/* Setup the LED registers so we can turn on/off.
52*4882a593Smuzhiyun 	 * Writing these bits maps control to another
53*4882a593Smuzhiyun 	 * register. mmd(0x1) addr(0x7)
54*4882a593Smuzhiyun 	 */
55*4882a593Smuzhiyun 	cphy_mdio_write(cphy, MDIO_MMD_PCS, 0x8304, 0xdddd);
56*4882a593Smuzhiyun 	return 0;
57*4882a593Smuzhiyun }
58*4882a593Smuzhiyun 
led_link(struct cphy * cphy,u32 do_enable)59*4882a593Smuzhiyun static int led_link(struct cphy *cphy, u32 do_enable)
60*4882a593Smuzhiyun {
61*4882a593Smuzhiyun 	u32 led = 0;
62*4882a593Smuzhiyun #define LINK_ENABLE_BIT 0x1
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun 	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_CTRL2, &led);
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun 	if (do_enable & LINK_ENABLE_BIT) {
67*4882a593Smuzhiyun 		led |= LINK_ENABLE_BIT;
68*4882a593Smuzhiyun 		cphy_mdio_write(cphy, MDIO_MMD_PMAPMD, MDIO_CTRL2, led);
69*4882a593Smuzhiyun 	} else {
70*4882a593Smuzhiyun 		led &= ~LINK_ENABLE_BIT;
71*4882a593Smuzhiyun 		cphy_mdio_write(cphy, MDIO_MMD_PMAPMD, MDIO_CTRL2, led);
72*4882a593Smuzhiyun 	}
73*4882a593Smuzhiyun 	return 0;
74*4882a593Smuzhiyun }
75*4882a593Smuzhiyun 
76*4882a593Smuzhiyun /* Port Reset */
mv88x201x_reset(struct cphy * cphy,int wait)77*4882a593Smuzhiyun static int mv88x201x_reset(struct cphy *cphy, int wait)
78*4882a593Smuzhiyun {
79*4882a593Smuzhiyun 	/* This can be done through registers.  It is not required since
80*4882a593Smuzhiyun 	 * a full chip reset is used.
81*4882a593Smuzhiyun 	 */
82*4882a593Smuzhiyun 	return 0;
83*4882a593Smuzhiyun }
84*4882a593Smuzhiyun 
mv88x201x_interrupt_enable(struct cphy * cphy)85*4882a593Smuzhiyun static int mv88x201x_interrupt_enable(struct cphy *cphy)
86*4882a593Smuzhiyun {
87*4882a593Smuzhiyun 	/* Enable PHY LASI interrupts. */
88*4882a593Smuzhiyun 	cphy_mdio_write(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_CTRL,
89*4882a593Smuzhiyun 			MDIO_PMA_LASI_LSALARM);
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun 	/* Enable Marvell interrupts through Elmer0. */
92*4882a593Smuzhiyun 	if (t1_is_asic(cphy->adapter)) {
93*4882a593Smuzhiyun 		u32 elmer;
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun 		t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer);
96*4882a593Smuzhiyun 		elmer |= ELMER0_GP_BIT6;
97*4882a593Smuzhiyun 		t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer);
98*4882a593Smuzhiyun 	}
99*4882a593Smuzhiyun 	return 0;
100*4882a593Smuzhiyun }
101*4882a593Smuzhiyun 
mv88x201x_interrupt_disable(struct cphy * cphy)102*4882a593Smuzhiyun static int mv88x201x_interrupt_disable(struct cphy *cphy)
103*4882a593Smuzhiyun {
104*4882a593Smuzhiyun 	/* Disable PHY LASI interrupts. */
105*4882a593Smuzhiyun 	cphy_mdio_write(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_CTRL, 0x0);
106*4882a593Smuzhiyun 
107*4882a593Smuzhiyun 	/* Disable Marvell interrupts through Elmer0. */
108*4882a593Smuzhiyun 	if (t1_is_asic(cphy->adapter)) {
109*4882a593Smuzhiyun 		u32 elmer;
110*4882a593Smuzhiyun 
111*4882a593Smuzhiyun 		t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer);
112*4882a593Smuzhiyun 		elmer &= ~ELMER0_GP_BIT6;
113*4882a593Smuzhiyun 		t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer);
114*4882a593Smuzhiyun 	}
115*4882a593Smuzhiyun 	return 0;
116*4882a593Smuzhiyun }
117*4882a593Smuzhiyun 
mv88x201x_interrupt_clear(struct cphy * cphy)118*4882a593Smuzhiyun static int mv88x201x_interrupt_clear(struct cphy *cphy)
119*4882a593Smuzhiyun {
120*4882a593Smuzhiyun 	u32 elmer;
121*4882a593Smuzhiyun 	u32 val;
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun #ifdef MV88x2010_LINK_STATUS_BUGS
124*4882a593Smuzhiyun 	/* Required to read twice before clear takes affect. */
125*4882a593Smuzhiyun 	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_RXSTAT, &val);
126*4882a593Smuzhiyun 	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_TXSTAT, &val);
127*4882a593Smuzhiyun 	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_STAT, &val);
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun 	/* Read this register after the others above it else
130*4882a593Smuzhiyun 	 * the register doesn't clear correctly.
131*4882a593Smuzhiyun 	 */
132*4882a593Smuzhiyun 	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_STAT1, &val);
133*4882a593Smuzhiyun #endif
134*4882a593Smuzhiyun 
135*4882a593Smuzhiyun 	/* Clear link status. */
136*4882a593Smuzhiyun 	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_STAT1, &val);
137*4882a593Smuzhiyun 	/* Clear PHY LASI interrupts. */
138*4882a593Smuzhiyun 	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_STAT, &val);
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun #ifdef MV88x2010_LINK_STATUS_BUGS
141*4882a593Smuzhiyun 	/* Do it again. */
142*4882a593Smuzhiyun 	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_RXSTAT, &val);
143*4882a593Smuzhiyun 	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_TXSTAT, &val);
144*4882a593Smuzhiyun #endif
145*4882a593Smuzhiyun 
146*4882a593Smuzhiyun 	/* Clear Marvell interrupts through Elmer0. */
147*4882a593Smuzhiyun 	if (t1_is_asic(cphy->adapter)) {
148*4882a593Smuzhiyun 		t1_tpi_read(cphy->adapter, A_ELMER0_INT_CAUSE, &elmer);
149*4882a593Smuzhiyun 		elmer |= ELMER0_GP_BIT6;
150*4882a593Smuzhiyun 		t1_tpi_write(cphy->adapter, A_ELMER0_INT_CAUSE, elmer);
151*4882a593Smuzhiyun 	}
152*4882a593Smuzhiyun 	return 0;
153*4882a593Smuzhiyun }
154*4882a593Smuzhiyun 
mv88x201x_interrupt_handler(struct cphy * cphy)155*4882a593Smuzhiyun static int mv88x201x_interrupt_handler(struct cphy *cphy)
156*4882a593Smuzhiyun {
157*4882a593Smuzhiyun 	/* Clear interrupts */
158*4882a593Smuzhiyun 	mv88x201x_interrupt_clear(cphy);
159*4882a593Smuzhiyun 
160*4882a593Smuzhiyun 	/* We have only enabled link change interrupts and so
161*4882a593Smuzhiyun 	 * cphy_cause must be a link change interrupt.
162*4882a593Smuzhiyun 	 */
163*4882a593Smuzhiyun 	return cphy_cause_link_change;
164*4882a593Smuzhiyun }
165*4882a593Smuzhiyun 
mv88x201x_set_loopback(struct cphy * cphy,int on)166*4882a593Smuzhiyun static int mv88x201x_set_loopback(struct cphy *cphy, int on)
167*4882a593Smuzhiyun {
168*4882a593Smuzhiyun 	return 0;
169*4882a593Smuzhiyun }
170*4882a593Smuzhiyun 
mv88x201x_get_link_status(struct cphy * cphy,int * link_ok,int * speed,int * duplex,int * fc)171*4882a593Smuzhiyun static int mv88x201x_get_link_status(struct cphy *cphy, int *link_ok,
172*4882a593Smuzhiyun 				     int *speed, int *duplex, int *fc)
173*4882a593Smuzhiyun {
174*4882a593Smuzhiyun 	u32 val = 0;
175*4882a593Smuzhiyun 
176*4882a593Smuzhiyun 	if (link_ok) {
177*4882a593Smuzhiyun 		/* Read link status. */
178*4882a593Smuzhiyun 		cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_STAT1, &val);
179*4882a593Smuzhiyun 		val &= MDIO_STAT1_LSTATUS;
180*4882a593Smuzhiyun 		*link_ok = (val == MDIO_STAT1_LSTATUS);
181*4882a593Smuzhiyun 		/* Turn on/off Link LED */
182*4882a593Smuzhiyun 		led_link(cphy, *link_ok);
183*4882a593Smuzhiyun 	}
184*4882a593Smuzhiyun 	if (speed)
185*4882a593Smuzhiyun 		*speed = SPEED_10000;
186*4882a593Smuzhiyun 	if (duplex)
187*4882a593Smuzhiyun 		*duplex = DUPLEX_FULL;
188*4882a593Smuzhiyun 	if (fc)
189*4882a593Smuzhiyun 		*fc = PAUSE_RX | PAUSE_TX;
190*4882a593Smuzhiyun 	return 0;
191*4882a593Smuzhiyun }
192*4882a593Smuzhiyun 
mv88x201x_destroy(struct cphy * cphy)193*4882a593Smuzhiyun static void mv88x201x_destroy(struct cphy *cphy)
194*4882a593Smuzhiyun {
195*4882a593Smuzhiyun 	kfree(cphy);
196*4882a593Smuzhiyun }
197*4882a593Smuzhiyun 
198*4882a593Smuzhiyun static const struct cphy_ops mv88x201x_ops = {
199*4882a593Smuzhiyun 	.destroy           = mv88x201x_destroy,
200*4882a593Smuzhiyun 	.reset             = mv88x201x_reset,
201*4882a593Smuzhiyun 	.interrupt_enable  = mv88x201x_interrupt_enable,
202*4882a593Smuzhiyun 	.interrupt_disable = mv88x201x_interrupt_disable,
203*4882a593Smuzhiyun 	.interrupt_clear   = mv88x201x_interrupt_clear,
204*4882a593Smuzhiyun 	.interrupt_handler = mv88x201x_interrupt_handler,
205*4882a593Smuzhiyun 	.get_link_status   = mv88x201x_get_link_status,
206*4882a593Smuzhiyun 	.set_loopback      = mv88x201x_set_loopback,
207*4882a593Smuzhiyun 	.mmds              = (MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS |
208*4882a593Smuzhiyun 			      MDIO_DEVS_PHYXS | MDIO_DEVS_WIS),
209*4882a593Smuzhiyun };
210*4882a593Smuzhiyun 
mv88x201x_phy_create(struct net_device * dev,int phy_addr,const struct mdio_ops * mdio_ops)211*4882a593Smuzhiyun static struct cphy *mv88x201x_phy_create(struct net_device *dev, int phy_addr,
212*4882a593Smuzhiyun 					 const struct mdio_ops *mdio_ops)
213*4882a593Smuzhiyun {
214*4882a593Smuzhiyun 	u32 val;
215*4882a593Smuzhiyun 	struct cphy *cphy = kzalloc(sizeof(*cphy), GFP_KERNEL);
216*4882a593Smuzhiyun 
217*4882a593Smuzhiyun 	if (!cphy)
218*4882a593Smuzhiyun 		return NULL;
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 	cphy_init(cphy, dev, phy_addr, &mv88x201x_ops, mdio_ops);
221*4882a593Smuzhiyun 
222*4882a593Smuzhiyun 	/* Commands the PHY to enable XFP's clock. */
223*4882a593Smuzhiyun 	cphy_mdio_read(cphy, MDIO_MMD_PCS, 0x8300, &val);
224*4882a593Smuzhiyun 	cphy_mdio_write(cphy, MDIO_MMD_PCS, 0x8300, val | 1);
225*4882a593Smuzhiyun 
226*4882a593Smuzhiyun 	/* Clear link status. Required because of a bug in the PHY.  */
227*4882a593Smuzhiyun 	cphy_mdio_read(cphy, MDIO_MMD_PMAPMD, MDIO_STAT2, &val);
228*4882a593Smuzhiyun 	cphy_mdio_read(cphy, MDIO_MMD_PCS, MDIO_STAT2, &val);
229*4882a593Smuzhiyun 
230*4882a593Smuzhiyun 	/* Allows for Link,Ack LED turn on/off */
231*4882a593Smuzhiyun 	led_init(cphy);
232*4882a593Smuzhiyun 	return cphy;
233*4882a593Smuzhiyun }
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun /* Chip Reset */
mv88x201x_phy_reset(adapter_t * adapter)236*4882a593Smuzhiyun static int mv88x201x_phy_reset(adapter_t *adapter)
237*4882a593Smuzhiyun {
238*4882a593Smuzhiyun 	u32 val;
239*4882a593Smuzhiyun 
240*4882a593Smuzhiyun 	t1_tpi_read(adapter, A_ELMER0_GPO, &val);
241*4882a593Smuzhiyun 	val &= ~4;
242*4882a593Smuzhiyun 	t1_tpi_write(adapter, A_ELMER0_GPO, val);
243*4882a593Smuzhiyun 	msleep(100);
244*4882a593Smuzhiyun 
245*4882a593Smuzhiyun 	t1_tpi_write(adapter, A_ELMER0_GPO, val | 4);
246*4882a593Smuzhiyun 	msleep(1000);
247*4882a593Smuzhiyun 
248*4882a593Smuzhiyun 	/* Now lets enable the Laser. Delay 100us */
249*4882a593Smuzhiyun 	t1_tpi_read(adapter, A_ELMER0_GPO, &val);
250*4882a593Smuzhiyun 	val |= 0x8000;
251*4882a593Smuzhiyun 	t1_tpi_write(adapter, A_ELMER0_GPO, val);
252*4882a593Smuzhiyun 	udelay(100);
253*4882a593Smuzhiyun 	return 0;
254*4882a593Smuzhiyun }
255*4882a593Smuzhiyun 
256*4882a593Smuzhiyun const struct gphy t1_mv88x201x_ops = {
257*4882a593Smuzhiyun 	.create = mv88x201x_phy_create,
258*4882a593Smuzhiyun 	.reset = mv88x201x_phy_reset
259*4882a593Smuzhiyun };
260