xref: /titanic_44/usr/src/uts/common/io/chxge/com/mv88x201x.c (revision d39a76e7b087a3d0927cbe6898dc0a6770fa6c68)
1*d39a76e7Sxw161283 /*
2*d39a76e7Sxw161283  * CDDL HEADER START
3*d39a76e7Sxw161283  *
4*d39a76e7Sxw161283  * The contents of this file are subject to the terms of the
5*d39a76e7Sxw161283  * Common Development and Distribution License (the "License").
6*d39a76e7Sxw161283  * You may not use this file except in compliance with the License.
7*d39a76e7Sxw161283  *
8*d39a76e7Sxw161283  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*d39a76e7Sxw161283  * or http://www.opensolaris.org/os/licensing.
10*d39a76e7Sxw161283  * See the License for the specific language governing permissions
11*d39a76e7Sxw161283  * and limitations under the License.
12*d39a76e7Sxw161283  *
13*d39a76e7Sxw161283  * When distributing Covered Code, include this CDDL HEADER in each
14*d39a76e7Sxw161283  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*d39a76e7Sxw161283  * If applicable, add the following below this CDDL HEADER, with the
16*d39a76e7Sxw161283  * fields enclosed by brackets "[]" replaced with your own identifying
17*d39a76e7Sxw161283  * information: Portions Copyright [yyyy] [name of copyright owner]
18*d39a76e7Sxw161283  *
19*d39a76e7Sxw161283  * CDDL HEADER END
20*d39a76e7Sxw161283  */
21*d39a76e7Sxw161283 
22*d39a76e7Sxw161283 /*
23*d39a76e7Sxw161283  * Copyright (C) 2003-2005 Chelsio Communications.  All rights reserved.
24*d39a76e7Sxw161283  */
25*d39a76e7Sxw161283 
26*d39a76e7Sxw161283 #pragma ident	"%Z%%M%	%I%	%E% SMI"	/* mv88x201x.c */
27*d39a76e7Sxw161283 
28*d39a76e7Sxw161283 #include "cphy.h"
29*d39a76e7Sxw161283 #include "elmer0.h"
30*d39a76e7Sxw161283 
31*d39a76e7Sxw161283 /*
32*d39a76e7Sxw161283  * The 88x2010 Rev C. requires some link status registers * to be read
33*d39a76e7Sxw161283  * twice in order to get the right values. Future * revisions will fix
34*d39a76e7Sxw161283  * this problem and then this macro * can disappear.
35*d39a76e7Sxw161283  */
36*d39a76e7Sxw161283 #define MV88x2010_LINK_STATUS_BUGS    1
37*d39a76e7Sxw161283 
led_init(struct cphy * cphy)38*d39a76e7Sxw161283 static int led_init(struct cphy *cphy)
39*d39a76e7Sxw161283 {
40*d39a76e7Sxw161283 	/* Setup the LED registers so we can turn on/off.
41*d39a76e7Sxw161283 	 * Writing these bits maps control to another
42*d39a76e7Sxw161283 	 * register. mmd(0x1) addr(0x7)
43*d39a76e7Sxw161283 	 */
44*d39a76e7Sxw161283 	(void) mdio_write(cphy, 0x3, 0x8304, 0xdddd);
45*d39a76e7Sxw161283 	return 0;
46*d39a76e7Sxw161283 }
47*d39a76e7Sxw161283 
led_link(struct cphy * cphy,u32 do_enable)48*d39a76e7Sxw161283 static int led_link(struct cphy *cphy, u32 do_enable)
49*d39a76e7Sxw161283 {
50*d39a76e7Sxw161283 	u32 led = 0;
51*d39a76e7Sxw161283 #define LINK_ENABLE_BIT 0x1
52*d39a76e7Sxw161283 
53*d39a76e7Sxw161283 	(void) mdio_read(cphy, 0x1, 0x7, &led);
54*d39a76e7Sxw161283 
55*d39a76e7Sxw161283 	if (do_enable & LINK_ENABLE_BIT) {
56*d39a76e7Sxw161283 		led |= LINK_ENABLE_BIT;
57*d39a76e7Sxw161283 		(void) mdio_write(cphy, 0x1, 0x7, led);
58*d39a76e7Sxw161283 	} else {
59*d39a76e7Sxw161283 		led &= ~LINK_ENABLE_BIT;
60*d39a76e7Sxw161283 		(void) mdio_write(cphy, 0x1, 0x7, led);
61*d39a76e7Sxw161283 	}
62*d39a76e7Sxw161283 	return 0;
63*d39a76e7Sxw161283 }
64*d39a76e7Sxw161283 
65*d39a76e7Sxw161283 /* Port Reset */
66*d39a76e7Sxw161283 /* ARGSUSED */
mv88x201x_reset(struct cphy * cphy,int wait)67*d39a76e7Sxw161283 static int mv88x201x_reset(struct cphy *cphy, int wait)
68*d39a76e7Sxw161283 {
69*d39a76e7Sxw161283 	/* This can be done through registers.  It is not required since
70*d39a76e7Sxw161283 	 * a full chip reset is used.
71*d39a76e7Sxw161283 	 */
72*d39a76e7Sxw161283 	return 0;
73*d39a76e7Sxw161283 }
74*d39a76e7Sxw161283 
mv88x201x_interrupt_enable(struct cphy * cphy)75*d39a76e7Sxw161283 static int mv88x201x_interrupt_enable(struct cphy *cphy)
76*d39a76e7Sxw161283 {
77*d39a76e7Sxw161283 	/* Enable PHY LASI interrupts. */
78*d39a76e7Sxw161283 	(void) mdio_write(cphy, 0x1, 0x9002, 0x1);
79*d39a76e7Sxw161283 
80*d39a76e7Sxw161283 	/* Enable Marvell interrupts through Elmer0. */
81*d39a76e7Sxw161283 	if (t1_is_asic(cphy->adapter)) {
82*d39a76e7Sxw161283 		u32 elmer;
83*d39a76e7Sxw161283 
84*d39a76e7Sxw161283 		(void) t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer);
85*d39a76e7Sxw161283 		elmer |= ELMER0_GP_BIT6;
86*d39a76e7Sxw161283 		(void) t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer);
87*d39a76e7Sxw161283 	}
88*d39a76e7Sxw161283 	return 0;
89*d39a76e7Sxw161283 }
90*d39a76e7Sxw161283 
mv88x201x_interrupt_disable(struct cphy * cphy)91*d39a76e7Sxw161283 static int mv88x201x_interrupt_disable(struct cphy *cphy)
92*d39a76e7Sxw161283 {
93*d39a76e7Sxw161283 	/* Disable PHY LASI interrupts. */
94*d39a76e7Sxw161283 	(void) mdio_write(cphy, 0x1, 0x9002, 0x0);
95*d39a76e7Sxw161283 
96*d39a76e7Sxw161283 	/* Disable Marvell interrupts through Elmer0. */
97*d39a76e7Sxw161283 	if (t1_is_asic(cphy->adapter)) {
98*d39a76e7Sxw161283 		u32 elmer;
99*d39a76e7Sxw161283 
100*d39a76e7Sxw161283 		(void) t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer);
101*d39a76e7Sxw161283 		elmer &= ~ELMER0_GP_BIT6;
102*d39a76e7Sxw161283 		(void) t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer);
103*d39a76e7Sxw161283 	}
104*d39a76e7Sxw161283 	return 0;
105*d39a76e7Sxw161283 }
106*d39a76e7Sxw161283 
mv88x201x_interrupt_clear(struct cphy * cphy)107*d39a76e7Sxw161283 static int mv88x201x_interrupt_clear(struct cphy *cphy)
108*d39a76e7Sxw161283 {
109*d39a76e7Sxw161283 	u32 elmer;
110*d39a76e7Sxw161283 	u32 val;
111*d39a76e7Sxw161283 
112*d39a76e7Sxw161283 #ifdef MV88x2010_LINK_STATUS_BUGS
113*d39a76e7Sxw161283 	/* Required to read twice before clear takes affect. */
114*d39a76e7Sxw161283 	(void) mdio_read(cphy, 0x1, 0x9003, &val);
115*d39a76e7Sxw161283 	(void) mdio_read(cphy, 0x1, 0x9004, &val);
116*d39a76e7Sxw161283 	(void) mdio_read(cphy, 0x1, 0x9005, &val);
117*d39a76e7Sxw161283 
118*d39a76e7Sxw161283 	/* Read this register after the others above it else
119*d39a76e7Sxw161283 	 * the register doesn't clear correctly.
120*d39a76e7Sxw161283 	 */
121*d39a76e7Sxw161283 	(void) mdio_read(cphy, 0x1, 0x1, &val);
122*d39a76e7Sxw161283 #endif
123*d39a76e7Sxw161283 
124*d39a76e7Sxw161283 	/* Clear link status. */
125*d39a76e7Sxw161283 	(void) mdio_read(cphy, 0x1, 0x1, &val);
126*d39a76e7Sxw161283 	/* Clear PHY LASI interrupts. */
127*d39a76e7Sxw161283 	(void) mdio_read(cphy, 0x1, 0x9005, &val);
128*d39a76e7Sxw161283 
129*d39a76e7Sxw161283 #ifdef MV88x2010_LINK_STATUS_BUGS
130*d39a76e7Sxw161283 	/* Do it again. */
131*d39a76e7Sxw161283 	(void) mdio_read(cphy, 0x1, 0x9003, &val);
132*d39a76e7Sxw161283 	(void) mdio_read(cphy, 0x1, 0x9004, &val);
133*d39a76e7Sxw161283 #endif
134*d39a76e7Sxw161283 
135*d39a76e7Sxw161283 	/* Clear Marvell interrupts through Elmer0. */
136*d39a76e7Sxw161283 	if (t1_is_asic(cphy->adapter)) {
137*d39a76e7Sxw161283 		(void) t1_tpi_read(cphy->adapter, A_ELMER0_INT_CAUSE, &elmer);
138*d39a76e7Sxw161283 		elmer |= ELMER0_GP_BIT6;
139*d39a76e7Sxw161283 		(void) t1_tpi_write(cphy->adapter, A_ELMER0_INT_CAUSE, elmer);
140*d39a76e7Sxw161283 	}
141*d39a76e7Sxw161283 	return 0;
142*d39a76e7Sxw161283 }
143*d39a76e7Sxw161283 
mv88x201x_interrupt_handler(struct cphy * cphy)144*d39a76e7Sxw161283 static int mv88x201x_interrupt_handler(struct cphy *cphy)
145*d39a76e7Sxw161283 {
146*d39a76e7Sxw161283 	/* Clear interrupts */
147*d39a76e7Sxw161283 	(void) mv88x201x_interrupt_clear(cphy);
148*d39a76e7Sxw161283 
149*d39a76e7Sxw161283 	/* We have only enabled link change interrupts and so
150*d39a76e7Sxw161283 	 * cphy_cause must be a link change interrupt.
151*d39a76e7Sxw161283 	 */
152*d39a76e7Sxw161283 	return cphy_cause_link_change;
153*d39a76e7Sxw161283 }
154*d39a76e7Sxw161283 
155*d39a76e7Sxw161283 /* ARGSUSED */
mv88x201x_set_loopback(struct cphy * cphy,int on)156*d39a76e7Sxw161283 static int mv88x201x_set_loopback(struct cphy *cphy, int on)
157*d39a76e7Sxw161283 {
158*d39a76e7Sxw161283 	return 0;
159*d39a76e7Sxw161283 }
160*d39a76e7Sxw161283 
mv88x201x_get_link_status(struct cphy * cphy,int * link_ok,int * speed,int * duplex,int * fc)161*d39a76e7Sxw161283 static int mv88x201x_get_link_status(struct cphy *cphy, int *link_ok,
162*d39a76e7Sxw161283 				     int *speed, int *duplex, int *fc)
163*d39a76e7Sxw161283 {
164*d39a76e7Sxw161283 	u32 val = 0;
165*d39a76e7Sxw161283 #define LINK_STATUS_BIT 0x4
166*d39a76e7Sxw161283 
167*d39a76e7Sxw161283 	if (link_ok) {
168*d39a76e7Sxw161283 		/* Read link status. */
169*d39a76e7Sxw161283 		(void) mdio_read(cphy, 0x1, 0x1, &val);
170*d39a76e7Sxw161283 		val &= LINK_STATUS_BIT;
171*d39a76e7Sxw161283 		*link_ok = (val == LINK_STATUS_BIT);
172*d39a76e7Sxw161283 		/* Turn on/off Link LED */
173*d39a76e7Sxw161283 		(void) led_link(cphy, *link_ok);
174*d39a76e7Sxw161283 	}
175*d39a76e7Sxw161283 	if (speed)
176*d39a76e7Sxw161283 		*speed = SPEED_10000;
177*d39a76e7Sxw161283 	if (duplex)
178*d39a76e7Sxw161283 		*duplex = DUPLEX_FULL;
179*d39a76e7Sxw161283 	if (fc)
180*d39a76e7Sxw161283 		*fc = PAUSE_RX | PAUSE_TX;
181*d39a76e7Sxw161283 	return 0;
182*d39a76e7Sxw161283 }
183*d39a76e7Sxw161283 
mv88x201x_destroy(struct cphy * cphy)184*d39a76e7Sxw161283 static void mv88x201x_destroy(struct cphy *cphy)
185*d39a76e7Sxw161283 {
186*d39a76e7Sxw161283 	t1_os_free((void *) cphy, sizeof(*cphy));
187*d39a76e7Sxw161283 }
188*d39a76e7Sxw161283 
189*d39a76e7Sxw161283 #ifdef C99_NOT_SUPPORTED
190*d39a76e7Sxw161283 static struct cphy_ops mv88x201x_ops = {
191*d39a76e7Sxw161283 	mv88x201x_destroy,
192*d39a76e7Sxw161283 	mv88x201x_reset,
193*d39a76e7Sxw161283 	mv88x201x_interrupt_enable,
194*d39a76e7Sxw161283 	mv88x201x_interrupt_disable,
195*d39a76e7Sxw161283 	mv88x201x_interrupt_clear,
196*d39a76e7Sxw161283 	mv88x201x_interrupt_handler,
197*d39a76e7Sxw161283 	NULL,
198*d39a76e7Sxw161283 	NULL,
199*d39a76e7Sxw161283 	NULL,
200*d39a76e7Sxw161283 	NULL,
201*d39a76e7Sxw161283 	mv88x201x_set_loopback,
202*d39a76e7Sxw161283 	NULL,
203*d39a76e7Sxw161283 	mv88x201x_get_link_status,
204*d39a76e7Sxw161283 };
205*d39a76e7Sxw161283 #else
206*d39a76e7Sxw161283 static struct cphy_ops mv88x201x_ops = {
207*d39a76e7Sxw161283 	.destroy           = mv88x201x_destroy,
208*d39a76e7Sxw161283 	.reset             = mv88x201x_reset,
209*d39a76e7Sxw161283 	.interrupt_enable  = mv88x201x_interrupt_enable,
210*d39a76e7Sxw161283 	.interrupt_disable = mv88x201x_interrupt_disable,
211*d39a76e7Sxw161283 	.interrupt_clear   = mv88x201x_interrupt_clear,
212*d39a76e7Sxw161283 	.interrupt_handler = mv88x201x_interrupt_handler,
213*d39a76e7Sxw161283 	.get_link_status   = mv88x201x_get_link_status,
214*d39a76e7Sxw161283 	.set_loopback      = mv88x201x_set_loopback,
215*d39a76e7Sxw161283 };
216*d39a76e7Sxw161283 #endif
217*d39a76e7Sxw161283 
mv88x201x_phy_create(adapter_t * adapter,int phy_addr,struct mdio_ops * mdio_ops)218*d39a76e7Sxw161283 static struct cphy *mv88x201x_phy_create(adapter_t *adapter, int phy_addr,
219*d39a76e7Sxw161283 					 struct mdio_ops *mdio_ops)
220*d39a76e7Sxw161283 {
221*d39a76e7Sxw161283 	u32 val;
222*d39a76e7Sxw161283 	struct cphy *cphy = t1_os_malloc_wait_zero(sizeof(*cphy));
223*d39a76e7Sxw161283 
224*d39a76e7Sxw161283 	if (!cphy)
225*d39a76e7Sxw161283 		return NULL;
226*d39a76e7Sxw161283 
227*d39a76e7Sxw161283 	cphy_init(cphy, adapter, phy_addr, &mv88x201x_ops, mdio_ops);
228*d39a76e7Sxw161283 
229*d39a76e7Sxw161283 	/* Commands the PHY to enable XFP's clock. */
230*d39a76e7Sxw161283 	(void) mdio_read(cphy, 0x3, 0x8300, &val);
231*d39a76e7Sxw161283 	(void) mdio_write(cphy, 0x3, 0x8300, val | 1);
232*d39a76e7Sxw161283 
233*d39a76e7Sxw161283 	/* Clear link status. Required because of a bug in the PHY.  */
234*d39a76e7Sxw161283 	(void) mdio_read(cphy, 0x1, 0x8, &val);
235*d39a76e7Sxw161283 	(void) mdio_read(cphy, 0x3, 0x8, &val);
236*d39a76e7Sxw161283 
237*d39a76e7Sxw161283 	/* Allows for Link,Ack LED turn on/off */
238*d39a76e7Sxw161283 	(void) led_init(cphy);
239*d39a76e7Sxw161283 	return cphy;
240*d39a76e7Sxw161283 }
241*d39a76e7Sxw161283 
242*d39a76e7Sxw161283 /* Chip Reset */
mv88x201x_phy_reset(adapter_t * adapter)243*d39a76e7Sxw161283 static int mv88x201x_phy_reset(adapter_t *adapter)
244*d39a76e7Sxw161283 {
245*d39a76e7Sxw161283 	u32 val;
246*d39a76e7Sxw161283 
247*d39a76e7Sxw161283 	(void) t1_tpi_read(adapter, A_ELMER0_GPO, &val);
248*d39a76e7Sxw161283 	val &= ~4;
249*d39a76e7Sxw161283 	(void) t1_tpi_write(adapter, A_ELMER0_GPO, val);
250*d39a76e7Sxw161283 	DELAY_MS(100);
251*d39a76e7Sxw161283 
252*d39a76e7Sxw161283 	(void) t1_tpi_write(adapter, A_ELMER0_GPO, val | 4);
253*d39a76e7Sxw161283 	DELAY_MS(1000);
254*d39a76e7Sxw161283 
255*d39a76e7Sxw161283 	/* Now lets enable the Laser. Delay 100us */
256*d39a76e7Sxw161283 	(void) t1_tpi_read(adapter, A_ELMER0_GPO, &val);
257*d39a76e7Sxw161283 	val |= 0x8000;
258*d39a76e7Sxw161283 	(void) t1_tpi_write(adapter, A_ELMER0_GPO, val);
259*d39a76e7Sxw161283 	DELAY_US(100);
260*d39a76e7Sxw161283 	return 0;
261*d39a76e7Sxw161283 }
262*d39a76e7Sxw161283 
263*d39a76e7Sxw161283 struct gphy t1_mv88x201x_ops = {
264*d39a76e7Sxw161283 	mv88x201x_phy_create,
265*d39a76e7Sxw161283 	mv88x201x_phy_reset
266*d39a76e7Sxw161283 };
267