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