xref: /freebsd/sys/dev/cxgb/common/cxgb_ael1002.c (revision ef72318f)
1 /**************************************************************************
2 
3 Copyright (c) 2007, 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 enum {
40 	AEL100X_TX_DISABLE  = 9,
41 	AEL100X_TX_CONFIG1  = 0xc002,
42 	AEL1002_PWR_DOWN_HI = 0xc011,
43 	AEL1002_PWR_DOWN_LO = 0xc012,
44 	AEL1002_XFI_EQL     = 0xc015,
45 	AEL1002_LB_EN       = 0xc017,
46 
47 	LASI_CTRL   = 0x9002,
48 	LASI_STAT   = 0x9005
49 };
50 
51 static void ael100x_txon(struct cphy *phy)
52 {
53 	int tx_on_gpio = phy->addr == 0 ? F_GPIO7_OUT_VAL : F_GPIO2_OUT_VAL;
54 
55 	t3_os_sleep(100);
56 	t3_set_reg_field(phy->adapter, A_T3DBG_GPIO_EN, 0, tx_on_gpio);
57 	t3_os_sleep(30);
58 }
59 
60 static int ael1002_power_down(struct cphy *phy, int enable)
61 {
62 	int err;
63 
64 	err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL100X_TX_DISABLE, !!enable);
65 	if (!err)
66 		err = t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR,
67 					  BMCR_PDOWN, enable ? BMCR_PDOWN : 0);
68 	return err;
69 }
70 
71 static int ael1002_reset(struct cphy *phy, int wait)
72 {
73 	int err;
74 
75 	if ((err = ael1002_power_down(phy, 0)) ||
76 	    (err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL100X_TX_CONFIG1, 1)) ||
77 	    (err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL1002_PWR_DOWN_HI, 0)) ||
78 	    (err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL1002_PWR_DOWN_LO, 0)) ||
79 	    (err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL1002_XFI_EQL, 0x18)) ||
80 	    (err = t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, AEL1002_LB_EN,
81 				       0, 1 << 5)))
82 		return err;
83 	return 0;
84 }
85 
86 static int ael1002_intr_noop(struct cphy *phy)
87 {
88 	return 0;
89 }
90 
91 static int ael100x_get_link_status(struct cphy *phy, int *link_ok,
92 				   int *speed, int *duplex, int *fc)
93 {
94 	if (link_ok) {
95 		unsigned int status;
96 		int err = mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMSR, &status);
97 
98 		/*
99 		 * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
100 		 * once more to get the current link state.
101 		 */
102 		if (!err && !(status & BMSR_LSTATUS))
103 			err = mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMSR,
104 					&status);
105 		if (err)
106 			return err;
107 		*link_ok = !!(status & BMSR_LSTATUS);
108 	}
109 	if (speed)
110 		*speed = SPEED_10000;
111 	if (duplex)
112 		*duplex = DUPLEX_FULL;
113 	return 0;
114 }
115 
116 #ifdef C99_NOT_SUPPORTED
117 static struct cphy_ops ael1002_ops = {
118 	NULL,
119 	ael1002_reset,
120 	ael1002_intr_noop,
121 	ael1002_intr_noop,
122 	ael1002_intr_noop,
123 	ael1002_intr_noop,
124 	NULL,
125 	NULL,
126 	NULL,
127 	NULL,
128 	NULL,
129 	ael100x_get_link_status,
130 	ael1002_power_down,
131 };
132 #else
133 static struct cphy_ops ael1002_ops = {
134 	.reset           = ael1002_reset,
135 	.intr_enable     = ael1002_intr_noop,
136 	.intr_disable    = ael1002_intr_noop,
137 	.intr_clear      = ael1002_intr_noop,
138 	.intr_handler    = ael1002_intr_noop,
139 	.get_link_status = ael100x_get_link_status,
140 	.power_down      = ael1002_power_down,
141 };
142 #endif
143 
144 void t3_ael1002_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr,
145 			 const struct mdio_ops *mdio_ops)
146 {
147 	cphy_init(phy, adapter, phy_addr, &ael1002_ops, mdio_ops);
148 	ael100x_txon(phy);
149 }
150 
151 static int ael1006_reset(struct cphy *phy, int wait)
152 {
153 	return t3_phy_reset(phy, MDIO_DEV_PMA_PMD, wait);
154 }
155 
156 static int ael1006_intr_enable(struct cphy *phy)
157 {
158 	return mdio_write(phy, MDIO_DEV_PMA_PMD, LASI_CTRL, 1);
159 }
160 
161 static int ael1006_intr_disable(struct cphy *phy)
162 {
163 	return mdio_write(phy, MDIO_DEV_PMA_PMD, LASI_CTRL, 0);
164 }
165 
166 static int ael1006_intr_clear(struct cphy *phy)
167 {
168 	u32 val;
169 
170 	return mdio_read(phy, MDIO_DEV_PMA_PMD, LASI_STAT, &val);
171 }
172 
173 static int ael1006_intr_handler(struct cphy *phy)
174 {
175 	unsigned int status;
176 	int err = mdio_read(phy, MDIO_DEV_PMA_PMD, LASI_STAT, &status);
177 
178 	if (err)
179 		return err;
180 	return (status & 1) ?  cphy_cause_link_change : 0;
181 }
182 
183 static int ael1006_power_down(struct cphy *phy, int enable)
184 {
185 	return t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR,
186 				   BMCR_PDOWN, enable ? BMCR_PDOWN : 0);
187 }
188 
189 #ifdef C99_NOT_SUPPORTED
190 static struct cphy_ops ael1006_ops = {
191 	NULL,
192 	ael1006_reset,
193 	ael1006_intr_enable,
194 	ael1006_intr_disable,
195 	ael1006_intr_clear,
196 	ael1006_intr_handler,
197 	NULL,
198 	NULL,
199 	NULL,
200 	NULL,
201 	NULL,
202 	ael100x_get_link_status,
203 	ael1006_power_down,
204 };
205 #else
206 static struct cphy_ops ael1006_ops = {
207 	.reset           = ael1006_reset,
208 	.intr_enable     = ael1006_intr_enable,
209 	.intr_disable    = ael1006_intr_disable,
210 	.intr_clear      = ael1006_intr_clear,
211 	.intr_handler    = ael1006_intr_handler,
212 	.get_link_status = ael100x_get_link_status,
213 	.power_down      = ael1006_power_down,
214 };
215 #endif
216 
217 void t3_ael1006_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr,
218 			 const struct mdio_ops *mdio_ops)
219 {
220 	cphy_init(phy, adapter, phy_addr, &ael1006_ops, mdio_ops);
221 	ael100x_txon(phy);
222 }
223 
224 #ifdef C99_NOT_SUPPORTED
225 static struct cphy_ops qt2045_ops = {
226 	NULL,
227 	ael1006_reset,
228 	ael1006_intr_enable,
229 	ael1006_intr_disable,
230 	ael1006_intr_clear,
231 	ael1006_intr_handler,
232 	NULL,
233 	NULL,
234 	NULL,
235 	NULL,
236 	NULL,
237 	ael100x_get_link_status,
238 	ael1006_power_down,
239 };
240 #else
241 static struct cphy_ops qt2045_ops = {
242 	.reset           = ael1006_reset,
243 	.intr_enable     = ael1006_intr_enable,
244 	.intr_disable    = ael1006_intr_disable,
245 	.intr_clear      = ael1006_intr_clear,
246 	.intr_handler    = ael1006_intr_handler,
247 	.get_link_status = ael100x_get_link_status,
248 	.power_down      = ael1006_power_down,
249 };
250 #endif
251 
252 void t3_qt2045_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr,
253 			const struct mdio_ops *mdio_ops)
254 {
255 	unsigned int stat;
256 
257 	cphy_init(phy, adapter, phy_addr, &qt2045_ops, mdio_ops);
258 
259 	/*
260 	 * Some cards where the PHY is supposed to be at address 0 actually
261 	 * have it at 1.
262 	 */
263 	if (!phy_addr && !mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMSR, &stat) &&
264 	    stat == 0xffff)
265 		phy->addr = 1;
266 }
267 
268 static int xaui_direct_reset(struct cphy *phy, int wait)
269 {
270 	return 0;
271 }
272 
273 static int xaui_direct_get_link_status(struct cphy *phy, int *link_ok,
274 				       int *speed, int *duplex, int *fc)
275 {
276 	if (link_ok) {
277 		unsigned int status;
278 
279 		status = t3_read_reg(phy->adapter,
280 				     XGM_REG(A_XGM_SERDES_STAT0, phy->addr)) |
281 			 t3_read_reg(phy->adapter,
282 				     XGM_REG(A_XGM_SERDES_STAT1, phy->addr)) |
283 			 t3_read_reg(phy->adapter,
284 				     XGM_REG(A_XGM_SERDES_STAT2, phy->addr)) |
285 			 t3_read_reg(phy->adapter,
286 				     XGM_REG(A_XGM_SERDES_STAT3, phy->addr));
287 		*link_ok = !(status & F_LOWSIG0);
288 	}
289 	if (speed)
290 		*speed = SPEED_10000;
291 	if (duplex)
292 		*duplex = DUPLEX_FULL;
293 	return 0;
294 }
295 
296 static int xaui_direct_power_down(struct cphy *phy, int enable)
297 {
298 	return 0;
299 }
300 
301 #ifdef C99_NOT_SUPPORTED
302 static struct cphy_ops xaui_direct_ops = {
303 	NULL,
304 	xaui_direct_reset,
305 	ael1002_intr_noop,
306 	ael1002_intr_noop,
307 	ael1002_intr_noop,
308 	ael1002_intr_noop,
309 	NULL,
310 	NULL,
311 	NULL,
312 	NULL,
313 	NULL,
314 	xaui_direct_get_link_status,
315 	xaui_direct_power_down,
316 };
317 #else
318 static struct cphy_ops xaui_direct_ops = {
319 	.reset           = xaui_direct_reset,
320 	.intr_enable     = ael1002_intr_noop,
321 	.intr_disable    = ael1002_intr_noop,
322 	.intr_clear      = ael1002_intr_noop,
323 	.intr_handler    = ael1002_intr_noop,
324 	.get_link_status = xaui_direct_get_link_status,
325 	.power_down      = xaui_direct_power_down,
326 };
327 #endif
328 
329 void t3_xaui_direct_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr,
330 			     const struct mdio_ops *mdio_ops)
331 {
332 	cphy_init(phy, adapter, phy_addr, &xaui_direct_ops, mdio_ops);
333 }
334