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