xref: /freebsd/sys/dev/cxgb/common/cxgb_ael1002.c (revision db612abe8df3355d1eb23bb3b50fdd97bc21e979)
1 /**************************************************************************
2 
3 Copyright (c) 2007-2008, Chelsio Inc.
4 All rights reserved.
5 
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are met:
8 
9  1. Redistributions of source code must retain the above copyright notice,
10     this list of conditions and the following disclaimer.
11 
12  2. Neither the name of the Chelsio Corporation nor the names of its
13     contributors may be used to endorse or promote products derived from
14     this software without specific prior written permission.
15 
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 POSSIBILITY OF SUCH DAMAGE.
27 
28 ***************************************************************************/
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 #ifdef CONFIG_DEFINED
34 #include <cxgb_include.h>
35 #else
36 #include <dev/cxgb/cxgb_include.h>
37 #endif
38 
39 #undef msleep
40 #define msleep t3_os_sleep
41 
42 enum {
43 	AEL100X_TX_DISABLE  = 9,
44 	AEL100X_TX_CONFIG1  = 0xc002,
45 	AEL1002_PWR_DOWN_HI = 0xc011,
46 	AEL1002_PWR_DOWN_LO = 0xc012,
47 	AEL1002_XFI_EQL     = 0xc015,
48 	AEL1002_LB_EN       = 0xc017,
49 
50 	LASI_CTRL   = 0x9002,
51 	LASI_STAT   = 0x9005
52 };
53 
54 static void ael100x_txon(struct cphy *phy)
55 {
56 	int tx_on_gpio = phy->addr == 0 ? F_GPIO7_OUT_VAL : F_GPIO2_OUT_VAL;
57 
58 	msleep(100);
59 	t3_set_reg_field(phy->adapter, A_T3DBG_GPIO_EN, 0, tx_on_gpio);
60 	msleep(30);
61 }
62 
63 static int ael1002_power_down(struct cphy *phy, int enable)
64 {
65 	int err;
66 
67 	err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL100X_TX_DISABLE, !!enable);
68 	if (!err)
69 		err = t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR,
70 					  BMCR_PDOWN, enable ? BMCR_PDOWN : 0);
71 	return err;
72 }
73 
74 static int ael1002_reset(struct cphy *phy, int wait)
75 {
76 	int err;
77 
78 	if ((err = ael1002_power_down(phy, 0)) ||
79 	    (err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL100X_TX_CONFIG1, 1)) ||
80 	    (err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL1002_PWR_DOWN_HI, 0)) ||
81 	    (err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL1002_PWR_DOWN_LO, 0)) ||
82 	    (err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL1002_XFI_EQL, 0x18)) ||
83 	    (err = t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, AEL1002_LB_EN,
84 				       0, 1 << 5)))
85 		return err;
86 	return 0;
87 }
88 
89 static int ael1002_intr_noop(struct cphy *phy)
90 {
91 	return 0;
92 }
93 
94 static int ael100x_get_link_status(struct cphy *phy, int *link_ok,
95 				   int *speed, int *duplex, int *fc)
96 {
97 	if (link_ok) {
98 		unsigned int status;
99 		int err = mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMSR, &status);
100 
101 		/*
102 		 * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
103 		 * once more to get the current link state.
104 		 */
105 		if (!err && !(status & BMSR_LSTATUS))
106 			err = mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMSR,
107 					&status);
108 		if (err)
109 			return err;
110 		*link_ok = !!(status & BMSR_LSTATUS);
111 	}
112 	if (speed)
113 		*speed = SPEED_10000;
114 	if (duplex)
115 		*duplex = DUPLEX_FULL;
116 	return 0;
117 }
118 
119 #ifdef C99_NOT_SUPPORTED
120 static struct cphy_ops ael1002_ops = {
121 	ael1002_reset,
122 	ael1002_intr_noop,
123 	ael1002_intr_noop,
124 	ael1002_intr_noop,
125 	ael1002_intr_noop,
126 	NULL,
127 	NULL,
128 	NULL,
129 	NULL,
130 	NULL,
131 	ael100x_get_link_status,
132 	ael1002_power_down,
133 };
134 #else
135 static struct cphy_ops ael1002_ops = {
136 	.reset           = ael1002_reset,
137 	.intr_enable     = ael1002_intr_noop,
138 	.intr_disable    = ael1002_intr_noop,
139 	.intr_clear      = ael1002_intr_noop,
140 	.intr_handler    = ael1002_intr_noop,
141 	.get_link_status = ael100x_get_link_status,
142 	.power_down      = ael1002_power_down,
143 };
144 #endif
145 
146 int t3_ael1002_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr,
147 			const struct mdio_ops *mdio_ops)
148 {
149 	cphy_init(phy, adapter, phy_addr, &ael1002_ops, mdio_ops,
150 		  SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE,
151 		  "10GBASE-R");
152 	ael100x_txon(phy);
153 	return 0;
154 }
155 
156 static int ael1006_reset(struct cphy *phy, int wait)
157 {
158 	return t3_phy_reset(phy, MDIO_DEV_PMA_PMD, wait);
159 }
160 
161 static int ael1006_intr_enable(struct cphy *phy)
162 {
163 	return mdio_write(phy, MDIO_DEV_PMA_PMD, LASI_CTRL, 1);
164 }
165 
166 static int ael1006_intr_disable(struct cphy *phy)
167 {
168 	return mdio_write(phy, MDIO_DEV_PMA_PMD, LASI_CTRL, 0);
169 }
170 
171 static int ael1006_intr_clear(struct cphy *phy)
172 {
173 	u32 val;
174 
175 	return mdio_read(phy, MDIO_DEV_PMA_PMD, LASI_STAT, &val);
176 }
177 
178 static int ael1006_intr_handler(struct cphy *phy)
179 {
180 	unsigned int status;
181 	int err = mdio_read(phy, MDIO_DEV_PMA_PMD, LASI_STAT, &status);
182 
183 	if (err)
184 		return err;
185 	return (status & 1) ?  cphy_cause_link_change : 0;
186 }
187 
188 static int ael1006_power_down(struct cphy *phy, int enable)
189 {
190 	return t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR,
191 				   BMCR_PDOWN, enable ? BMCR_PDOWN : 0);
192 }
193 
194 #ifdef C99_NOT_SUPPORTED
195 static struct cphy_ops ael1006_ops = {
196 	ael1006_reset,
197 	ael1006_intr_enable,
198 	ael1006_intr_disable,
199 	ael1006_intr_clear,
200 	ael1006_intr_handler,
201 	NULL,
202 	NULL,
203 	NULL,
204 	NULL,
205 	NULL,
206 	ael100x_get_link_status,
207 	ael1006_power_down,
208 };
209 #else
210 static struct cphy_ops ael1006_ops = {
211 	.reset           = ael1006_reset,
212 	.intr_enable     = ael1006_intr_enable,
213 	.intr_disable    = ael1006_intr_disable,
214 	.intr_clear      = ael1006_intr_clear,
215 	.intr_handler    = ael1006_intr_handler,
216 	.get_link_status = ael100x_get_link_status,
217 	.power_down      = ael1006_power_down,
218 };
219 #endif
220 
221 int t3_ael1006_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr,
222 			const struct mdio_ops *mdio_ops)
223 {
224 	cphy_init(phy, adapter, phy_addr, &ael1006_ops, mdio_ops,
225 		  SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE,
226 		  "10GBASE-SR");
227 	ael100x_txon(phy);
228 	return 0;
229 }
230 
231 #ifdef C99_NOT_SUPPORTED
232 static struct cphy_ops qt2045_ops = {
233 	ael1006_reset,
234 	ael1006_intr_enable,
235 	ael1006_intr_disable,
236 	ael1006_intr_clear,
237 	ael1006_intr_handler,
238 	NULL,
239 	NULL,
240 	NULL,
241 	NULL,
242 	NULL,
243 	ael100x_get_link_status,
244 	ael1006_power_down,
245 };
246 #else
247 static struct cphy_ops qt2045_ops = {
248 	.reset           = ael1006_reset,
249 	.intr_enable     = ael1006_intr_enable,
250 	.intr_disable    = ael1006_intr_disable,
251 	.intr_clear      = ael1006_intr_clear,
252 	.intr_handler    = ael1006_intr_handler,
253 	.get_link_status = ael100x_get_link_status,
254 	.power_down      = ael1006_power_down,
255 };
256 #endif
257 
258 int t3_qt2045_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr,
259 		       const struct mdio_ops *mdio_ops)
260 {
261 	unsigned int stat;
262 
263 	cphy_init(phy, adapter, phy_addr, &qt2045_ops, mdio_ops,
264 		  SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_TP,
265 		  "10GBASE-CX4");
266 
267 	/*
268 	 * Some cards where the PHY is supposed to be at address 0 actually
269 	 * have it at 1.
270 	 */
271 	if (!phy_addr && !mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMSR, &stat) &&
272 	    stat == 0xffff)
273 		phy->addr = 1;
274 	return 0;
275 }
276 
277 static int xaui_direct_reset(struct cphy *phy, int wait)
278 {
279 	return 0;
280 }
281 
282 static int xaui_direct_get_link_status(struct cphy *phy, int *link_ok,
283 				       int *speed, int *duplex, int *fc)
284 {
285 	if (link_ok) {
286 		unsigned int status;
287 
288 		status = t3_read_reg(phy->adapter,
289 				     XGM_REG(A_XGM_SERDES_STAT0, phy->addr)) |
290 			 t3_read_reg(phy->adapter,
291 				     XGM_REG(A_XGM_SERDES_STAT1, phy->addr)) |
292 			 t3_read_reg(phy->adapter,
293 				     XGM_REG(A_XGM_SERDES_STAT2, phy->addr)) |
294 			 t3_read_reg(phy->adapter,
295 				     XGM_REG(A_XGM_SERDES_STAT3, phy->addr));
296 		*link_ok = !(status & F_LOWSIG0);
297 	}
298 	if (speed)
299 		*speed = SPEED_10000;
300 	if (duplex)
301 		*duplex = DUPLEX_FULL;
302 	return 0;
303 }
304 
305 static int xaui_direct_power_down(struct cphy *phy, int enable)
306 {
307 	return 0;
308 }
309 
310 #ifdef C99_NOT_SUPPORTED
311 static struct cphy_ops xaui_direct_ops = {
312 	xaui_direct_reset,
313 	ael1002_intr_noop,
314 	ael1002_intr_noop,
315 	ael1002_intr_noop,
316 	ael1002_intr_noop,
317 	NULL,
318 	NULL,
319 	NULL,
320 	NULL,
321 	NULL,
322 	xaui_direct_get_link_status,
323 	xaui_direct_power_down,
324 };
325 #else
326 static struct cphy_ops xaui_direct_ops = {
327 	.reset           = xaui_direct_reset,
328 	.intr_enable     = ael1002_intr_noop,
329 	.intr_disable    = ael1002_intr_noop,
330 	.intr_clear      = ael1002_intr_noop,
331 	.intr_handler    = ael1002_intr_noop,
332 	.get_link_status = xaui_direct_get_link_status,
333 	.power_down      = xaui_direct_power_down,
334 };
335 #endif
336 
337 int t3_xaui_direct_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr,
338 			    const struct mdio_ops *mdio_ops)
339 {
340 	cphy_init(phy, adapter, phy_addr, &xaui_direct_ops, mdio_ops,
341 		  SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_TP,
342 		  "10GBASE-CX4");
343 	return 0;
344 }
345