cxgb_ael1002.c revision 167514
1/**************************************************************************
2
3Copyright (c) 2007, 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. 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
20THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30POSSIBILITY OF SUCH DAMAGE.
31
32***************************************************************************/
33
34#include <sys/cdefs.h>
35__FBSDID("$FreeBSD: head/sys/dev/cxgb/common/cxgb_ael1002.c 167514 2007-03-14 02:37:44Z kmacy $");
36
37#include <dev/cxgb/common/cxgb_common.h>
38#include <dev/cxgb/common/cxgb_regs.h>
39
40enum {
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
52static 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
61static 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
72static 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
87static int ael1002_intr_noop(struct cphy *phy)
88{
89	return 0;
90}
91
92static 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
118static 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
134static 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
145void 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
152static int ael1006_reset(struct cphy *phy, int wait)
153{
154	return t3_phy_reset(phy, MDIO_DEV_PMA_PMD, wait);
155}
156
157static int ael1006_intr_enable(struct cphy *phy)
158{
159	return mdio_write(phy, MDIO_DEV_PMA_PMD, LASI_CTRL, 1);
160}
161
162static int ael1006_intr_disable(struct cphy *phy)
163{
164	return mdio_write(phy, MDIO_DEV_PMA_PMD, LASI_CTRL, 0);
165}
166
167static 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
174static 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
184static 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
191static 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
207static 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
218void 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
226static 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
242static 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
253void 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
269static int xaui_direct_reset(struct cphy *phy, int wait)
270{
271	return 0;
272}
273
274static 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
291static int xaui_direct_power_down(struct cphy *phy, int enable)
292{
293	return 0;
294}
295
296#ifdef C99_NOT_SUPPORTED
297static 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
313static 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
324void 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