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