cxgb_ael1002.c revision 177340
1/**************************************************************************
2
3Copyright (c) 2007-2008, Chelsio Inc.
4All rights reserved.
5
6Redistribution and use in source and binary forms, with or without
7modification, 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
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26POSSIBILITY OF SUCH DAMAGE.
27
28***************************************************************************/
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: head/sys/dev/cxgb/common/cxgb_ael1002.c 177340 2008-03-18 03:55:12Z kmacy $");
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
42enum {
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
54static 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
63static 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
74static 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
89static int ael1002_intr_noop(struct cphy *phy)
90{
91	return 0;
92}
93
94static 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
120static 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
135static 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
146int 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
156static int ael1006_reset(struct cphy *phy, int wait)
157{
158	return t3_phy_reset(phy, MDIO_DEV_PMA_PMD, wait);
159}
160
161static int ael1006_intr_enable(struct cphy *phy)
162{
163	return mdio_write(phy, MDIO_DEV_PMA_PMD, LASI_CTRL, 1);
164}
165
166static int ael1006_intr_disable(struct cphy *phy)
167{
168	return mdio_write(phy, MDIO_DEV_PMA_PMD, LASI_CTRL, 0);
169}
170
171static 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
178static 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
188static 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
195static 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
210static 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
221int 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
232static 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
247static 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
258int 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
277static int xaui_direct_reset(struct cphy *phy, int wait)
278{
279	return 0;
280}
281
282static 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
305static int xaui_direct_power_down(struct cphy *phy, int enable)
306{
307	return 0;
308}
309
310#ifdef C99_NOT_SUPPORTED
311static 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
326static 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
337int 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