xref: /freebsd/sys/dev/cxgb/common/cxgb_aq100x.c (revision 685dc743)
1c01f2b83SNavdeep Parhar /**************************************************************************
24d846d26SWarner Losh SPDX-License-Identifier: BSD-2-Clause
3c01f2b83SNavdeep Parhar 
4c01f2b83SNavdeep Parhar Copyright (c) 2009 Chelsio Inc.
5c01f2b83SNavdeep Parhar All rights reserved.
6c01f2b83SNavdeep Parhar 
7c01f2b83SNavdeep Parhar Redistribution and use in source and binary forms, with or without
8c01f2b83SNavdeep Parhar modification, are permitted provided that the following conditions are met:
9c01f2b83SNavdeep Parhar 
10c01f2b83SNavdeep Parhar  1. Redistributions of source code must retain the above copyright notice,
11c01f2b83SNavdeep Parhar     this list of conditions and the following disclaimer.
12c01f2b83SNavdeep Parhar 
13c01f2b83SNavdeep Parhar  2. Neither the name of the Chelsio Corporation nor the names of its
14c01f2b83SNavdeep Parhar     contributors may be used to endorse or promote products derived from
15c01f2b83SNavdeep Parhar     this software without specific prior written permission.
16c01f2b83SNavdeep Parhar 
17c01f2b83SNavdeep Parhar THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18c01f2b83SNavdeep Parhar AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19c01f2b83SNavdeep Parhar IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20c01f2b83SNavdeep Parhar ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21c01f2b83SNavdeep Parhar LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22c01f2b83SNavdeep Parhar CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23c01f2b83SNavdeep Parhar SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24c01f2b83SNavdeep Parhar INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25c01f2b83SNavdeep Parhar CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26c01f2b83SNavdeep Parhar ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27c01f2b83SNavdeep Parhar POSSIBILITY OF SUCH DAMAGE.
28c01f2b83SNavdeep Parhar 
29c01f2b83SNavdeep Parhar ***************************************************************************/
30c01f2b83SNavdeep Parhar 
31c01f2b83SNavdeep Parhar #include <sys/cdefs.h>
32c01f2b83SNavdeep Parhar #include <cxgb_include.h>
33c01f2b83SNavdeep Parhar 
34c01f2b83SNavdeep Parhar #undef msleep
35c01f2b83SNavdeep Parhar #define msleep t3_os_sleep
36c01f2b83SNavdeep Parhar 
37c01f2b83SNavdeep Parhar enum {
38c01f2b83SNavdeep Parhar 	/* MDIO_DEV_PMA_PMD registers */
39c01f2b83SNavdeep Parhar 	AQ_LINK_STAT	= 0xe800,
40c01f2b83SNavdeep Parhar 
41c01f2b83SNavdeep Parhar 	/* MDIO_DEV_XGXS registers */
42c01f2b83SNavdeep Parhar 	AQ_XAUI_RX_CFG	= 0xc400,
43c01f2b83SNavdeep Parhar 	AQ_XAUI_KX_CFG	= 0xc440,
44c01f2b83SNavdeep Parhar 	AQ_XAUI_TX_CFG	= 0xe400,
45c01f2b83SNavdeep Parhar 
46c01f2b83SNavdeep Parhar 	/* MDIO_DEV_ANEG registers */
47c01f2b83SNavdeep Parhar 	AQ_100M_CTRL	= 0x0010,
48c01f2b83SNavdeep Parhar 	AQ_10G_CTRL	= 0x0020,
49c01f2b83SNavdeep Parhar 	AQ_1G_CTRL	= 0xc400,
50c01f2b83SNavdeep Parhar 	AQ_ANEG_STAT	= 0xc800,
51c01f2b83SNavdeep Parhar 
52c01f2b83SNavdeep Parhar 	/* MDIO_DEV_VEND1 registers */
53c01f2b83SNavdeep Parhar 	AQ_FW_VERSION	= 0x0020,
54c01f2b83SNavdeep Parhar 	AQ_THERMAL_THR	= 0xc421,
55c01f2b83SNavdeep Parhar 	AQ_THERMAL1	= 0xc820,
56c01f2b83SNavdeep Parhar 	AQ_THERMAL2	= 0xc821,
57c01f2b83SNavdeep Parhar 	AQ_IFLAG_GLOBAL	= 0xfc00,
58c01f2b83SNavdeep Parhar 	AQ_IMASK_GLOBAL	= 0xff00,
59c01f2b83SNavdeep Parhar };
60c01f2b83SNavdeep Parhar 
61c01f2b83SNavdeep Parhar #define AQBIT(x)	(1 << (0x##x))
62c01f2b83SNavdeep Parhar #define ADV_1G_FULL	AQBIT(f)
63c01f2b83SNavdeep Parhar #define ADV_1G_HALF	AQBIT(e)
64c01f2b83SNavdeep Parhar #define ADV_10G_FULL	AQBIT(c)
65c01f2b83SNavdeep Parhar 
66c01f2b83SNavdeep Parhar #define AQ_WRITE_REGS(phy, regs) do { \
67c01f2b83SNavdeep Parhar 	int i; \
68c01f2b83SNavdeep Parhar 	for (i = 0; i < ARRAY_SIZE(regs); i++) { \
69c01f2b83SNavdeep Parhar 		(void) mdio_write(phy, regs[i].mmd, regs[i].reg, regs[i].val); \
70c01f2b83SNavdeep Parhar 	} \
71c01f2b83SNavdeep Parhar } while (0)
72c01f2b83SNavdeep Parhar #define AQ_READ_REGS(phy, regs) do { \
73c01f2b83SNavdeep Parhar 	unsigned i, v; \
74c01f2b83SNavdeep Parhar 	for (i = 0; i < ARRAY_SIZE(regs); i++) { \
75c01f2b83SNavdeep Parhar 		(void) mdio_read(phy, regs[i].mmd, regs[i].reg, &v); \
76c01f2b83SNavdeep Parhar 	} \
77c01f2b83SNavdeep Parhar } while (0)
78c01f2b83SNavdeep Parhar 
79c01f2b83SNavdeep Parhar /*
809097ac9aSElyes HAOUAS  * Return value is temperature in celsius, 0xffff for error or don't know.
81c01f2b83SNavdeep Parhar  */
82c01f2b83SNavdeep Parhar static int
aq100x_temperature(struct cphy * phy)83c01f2b83SNavdeep Parhar aq100x_temperature(struct cphy *phy)
84c01f2b83SNavdeep Parhar {
85c01f2b83SNavdeep Parhar 	unsigned int v;
86c01f2b83SNavdeep Parhar 
87c01f2b83SNavdeep Parhar 	if (mdio_read(phy, MDIO_DEV_VEND1, AQ_THERMAL2, &v) ||
88c01f2b83SNavdeep Parhar 	    v == 0xffff || (v & 1) != 1)
89c01f2b83SNavdeep Parhar 		return (0xffff);
90c01f2b83SNavdeep Parhar 
91c01f2b83SNavdeep Parhar 	if (mdio_read(phy, MDIO_DEV_VEND1, AQ_THERMAL1, &v))
92c01f2b83SNavdeep Parhar 		return (0xffff);
93c01f2b83SNavdeep Parhar 
94c01f2b83SNavdeep Parhar 	return ((int)((signed char)(v >> 8)));
95c01f2b83SNavdeep Parhar }
96c01f2b83SNavdeep Parhar 
97c01f2b83SNavdeep Parhar static int
aq100x_set_defaults(struct cphy * phy)98c01f2b83SNavdeep Parhar aq100x_set_defaults(struct cphy *phy)
99c01f2b83SNavdeep Parhar {
100c01f2b83SNavdeep Parhar 	return mdio_write(phy, MDIO_DEV_VEND1, AQ_THERMAL_THR, 0x6c00);
101c01f2b83SNavdeep Parhar }
102c01f2b83SNavdeep Parhar 
103c01f2b83SNavdeep Parhar static int
aq100x_reset(struct cphy * phy,int wait)104c01f2b83SNavdeep Parhar aq100x_reset(struct cphy *phy, int wait)
105c01f2b83SNavdeep Parhar {
106c01f2b83SNavdeep Parhar 	int err;
107c01f2b83SNavdeep Parhar 	err = t3_phy_reset(phy, MDIO_DEV_PMA_PMD, wait);
108c01f2b83SNavdeep Parhar 	if (!err)
109c01f2b83SNavdeep Parhar 		err = aq100x_set_defaults(phy);
110c01f2b83SNavdeep Parhar 	return (err);
111c01f2b83SNavdeep Parhar }
112c01f2b83SNavdeep Parhar 
113c01f2b83SNavdeep Parhar static int
aq100x_intr_enable(struct cphy * phy)114c01f2b83SNavdeep Parhar aq100x_intr_enable(struct cphy *phy)
115c01f2b83SNavdeep Parhar {
116c01f2b83SNavdeep Parhar 	struct {
117c01f2b83SNavdeep Parhar 		int mmd;
118c01f2b83SNavdeep Parhar 		int reg;
119c01f2b83SNavdeep Parhar 		int val;
120c01f2b83SNavdeep Parhar 	} imasks[] = {
121c01f2b83SNavdeep Parhar 		{MDIO_DEV_VEND1, 0xd400, AQBIT(e)},
122c01f2b83SNavdeep Parhar 		{MDIO_DEV_VEND1, 0xff01, AQBIT(2)},
123c01f2b83SNavdeep Parhar 		{MDIO_DEV_VEND1, AQ_IMASK_GLOBAL, AQBIT(0)}
124c01f2b83SNavdeep Parhar 	};
125c01f2b83SNavdeep Parhar 
126c01f2b83SNavdeep Parhar 	AQ_WRITE_REGS(phy, imasks);
127c01f2b83SNavdeep Parhar 
128c01f2b83SNavdeep Parhar 	return (0);
129c01f2b83SNavdeep Parhar }
130c01f2b83SNavdeep Parhar 
131c01f2b83SNavdeep Parhar static int
aq100x_intr_disable(struct cphy * phy)132c01f2b83SNavdeep Parhar aq100x_intr_disable(struct cphy *phy)
133c01f2b83SNavdeep Parhar {
134c01f2b83SNavdeep Parhar 	struct {
135c01f2b83SNavdeep Parhar 		int mmd;
136c01f2b83SNavdeep Parhar 		int reg;
137c01f2b83SNavdeep Parhar 		int val;
138c01f2b83SNavdeep Parhar 	} imasks[] = {
139c01f2b83SNavdeep Parhar 		{MDIO_DEV_VEND1, 0xd400, 0},
140c01f2b83SNavdeep Parhar 		{MDIO_DEV_VEND1, 0xff01, 0},
141c01f2b83SNavdeep Parhar 		{MDIO_DEV_VEND1, AQ_IMASK_GLOBAL, 0}
142c01f2b83SNavdeep Parhar 	};
143c01f2b83SNavdeep Parhar 
144c01f2b83SNavdeep Parhar 	AQ_WRITE_REGS(phy, imasks);
145c01f2b83SNavdeep Parhar 
146c01f2b83SNavdeep Parhar 	return (0);
147c01f2b83SNavdeep Parhar }
148c01f2b83SNavdeep Parhar 
149c01f2b83SNavdeep Parhar static int
aq100x_intr_clear(struct cphy * phy)150c01f2b83SNavdeep Parhar aq100x_intr_clear(struct cphy *phy)
151c01f2b83SNavdeep Parhar {
152c01f2b83SNavdeep Parhar 	struct {
153c01f2b83SNavdeep Parhar 		int mmd;
154c01f2b83SNavdeep Parhar 		int reg;
155c01f2b83SNavdeep Parhar 	} iclr[] = {
156c01f2b83SNavdeep Parhar 		{MDIO_DEV_VEND1, 0xcc00},
157c01f2b83SNavdeep Parhar 		{MDIO_DEV_VEND1, AQ_IMASK_GLOBAL} /* needed? */
158c01f2b83SNavdeep Parhar 	};
159c01f2b83SNavdeep Parhar 
160c01f2b83SNavdeep Parhar 	AQ_READ_REGS(phy, iclr);
161c01f2b83SNavdeep Parhar 
162c01f2b83SNavdeep Parhar 	return (0);
163c01f2b83SNavdeep Parhar }
164c01f2b83SNavdeep Parhar 
165c01f2b83SNavdeep Parhar static int
aq100x_vendor_intr(struct cphy * phy,int * rc)166c01f2b83SNavdeep Parhar aq100x_vendor_intr(struct cphy *phy, int *rc)
167c01f2b83SNavdeep Parhar {
168c01f2b83SNavdeep Parhar 	int err;
169c01f2b83SNavdeep Parhar 	unsigned int cause, v;
170c01f2b83SNavdeep Parhar 
171c01f2b83SNavdeep Parhar 	err = mdio_read(phy, MDIO_DEV_VEND1, 0xfc01, &cause);
172c01f2b83SNavdeep Parhar 	if (err)
173c01f2b83SNavdeep Parhar 		return (err);
174c01f2b83SNavdeep Parhar 
175c01f2b83SNavdeep Parhar 	if (cause & AQBIT(2)) {
176c01f2b83SNavdeep Parhar 		err = mdio_read(phy, MDIO_DEV_VEND1, 0xcc00, &v);
177c01f2b83SNavdeep Parhar 		if (err)
178c01f2b83SNavdeep Parhar 			return (err);
179c01f2b83SNavdeep Parhar 
180c01f2b83SNavdeep Parhar 		if (v & AQBIT(e)) {
181c01f2b83SNavdeep Parhar 			CH_WARN(phy->adapter, "PHY%d: temperature is now %dC\n",
182c01f2b83SNavdeep Parhar 			    phy->addr, aq100x_temperature(phy));
183c01f2b83SNavdeep Parhar 
184c01f2b83SNavdeep Parhar 			t3_set_reg_field(phy->adapter, A_T3DBG_GPIO_EN,
185c01f2b83SNavdeep Parhar 			    phy->addr ? F_GPIO10_OUT_VAL : F_GPIO6_OUT_VAL, 0);
186c01f2b83SNavdeep Parhar 
187c01f2b83SNavdeep Parhar 			*rc |= cphy_cause_alarm;
188c01f2b83SNavdeep Parhar 		}
189c01f2b83SNavdeep Parhar 
190c01f2b83SNavdeep Parhar 		cause &= ~4;
191c01f2b83SNavdeep Parhar 	}
192c01f2b83SNavdeep Parhar 
193c01f2b83SNavdeep Parhar 	if (cause)
194c01f2b83SNavdeep Parhar 		CH_WARN(phy->adapter, "PHY%d: unhandled vendor interrupt"
195c01f2b83SNavdeep Parhar 		    " (0x%x)\n", phy->addr, cause);
196c01f2b83SNavdeep Parhar 
197c01f2b83SNavdeep Parhar 	return (0);
198c01f2b83SNavdeep Parhar 
199c01f2b83SNavdeep Parhar }
200c01f2b83SNavdeep Parhar 
201c01f2b83SNavdeep Parhar static int
aq100x_intr_handler(struct cphy * phy)202c01f2b83SNavdeep Parhar aq100x_intr_handler(struct cphy *phy)
203c01f2b83SNavdeep Parhar {
204c01f2b83SNavdeep Parhar 	int err, rc = 0;
205c01f2b83SNavdeep Parhar 	unsigned int cause;
206c01f2b83SNavdeep Parhar 
207c01f2b83SNavdeep Parhar 	err = mdio_read(phy, MDIO_DEV_VEND1, AQ_IFLAG_GLOBAL, &cause);
208c01f2b83SNavdeep Parhar 	if (err)
209c01f2b83SNavdeep Parhar 		return (err);
210c01f2b83SNavdeep Parhar 
211c01f2b83SNavdeep Parhar 	if (cause & AQBIT(0)) {
212c01f2b83SNavdeep Parhar 		err = aq100x_vendor_intr(phy, &rc);
213c01f2b83SNavdeep Parhar 		if (err)
214c01f2b83SNavdeep Parhar 			return (err);
215c01f2b83SNavdeep Parhar 		cause &= ~AQBIT(0);
216c01f2b83SNavdeep Parhar 	}
217c01f2b83SNavdeep Parhar 
218c01f2b83SNavdeep Parhar 	if (cause)
219c01f2b83SNavdeep Parhar 		CH_WARN(phy->adapter, "PHY%d: unhandled interrupt (0x%x)\n",
220c01f2b83SNavdeep Parhar 		    phy->addr, cause);
221c01f2b83SNavdeep Parhar 
222c01f2b83SNavdeep Parhar 	return (rc);
223c01f2b83SNavdeep Parhar }
224c01f2b83SNavdeep Parhar 
225c01f2b83SNavdeep Parhar static int
aq100x_power_down(struct cphy * phy,int off)226c01f2b83SNavdeep Parhar aq100x_power_down(struct cphy *phy, int off)
227c01f2b83SNavdeep Parhar {
228c01f2b83SNavdeep Parhar 	int err, wait = 500;
229c01f2b83SNavdeep Parhar 	unsigned int v;
230c01f2b83SNavdeep Parhar 
231c01f2b83SNavdeep Parhar 	err = t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR, BMCR_PDOWN,
232c01f2b83SNavdeep Parhar 	    off ? BMCR_PDOWN : 0);
233c01f2b83SNavdeep Parhar 	if (err || off)
234e050eaa3SNavdeep Parhar 		return (err);
235c01f2b83SNavdeep Parhar 
236c01f2b83SNavdeep Parhar 	msleep(300);
237c01f2b83SNavdeep Parhar 	do {
238c01f2b83SNavdeep Parhar 		err = mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMCR, &v);
239c01f2b83SNavdeep Parhar 		if (err)
240c01f2b83SNavdeep Parhar 			return (err);
241c01f2b83SNavdeep Parhar 		v &= BMCR_RESET;
242c01f2b83SNavdeep Parhar 		if (v)
243c01f2b83SNavdeep Parhar 			msleep(10);
244c01f2b83SNavdeep Parhar 	} while (v && --wait);
245c01f2b83SNavdeep Parhar 	if (v) {
246c01f2b83SNavdeep Parhar 		CH_WARN(phy->adapter, "PHY%d: power-up timed out (0x%x).\n",
247c01f2b83SNavdeep Parhar 		    phy->addr, v);
248c01f2b83SNavdeep Parhar 		return (ETIMEDOUT);
249c01f2b83SNavdeep Parhar 	}
250c01f2b83SNavdeep Parhar 
251c01f2b83SNavdeep Parhar 	return (0);
252c01f2b83SNavdeep Parhar }
253c01f2b83SNavdeep Parhar 
254c01f2b83SNavdeep Parhar static int
aq100x_autoneg_enable(struct cphy * phy)255c01f2b83SNavdeep Parhar aq100x_autoneg_enable(struct cphy *phy)
256c01f2b83SNavdeep Parhar {
257c01f2b83SNavdeep Parhar 	int err;
258c01f2b83SNavdeep Parhar 
259c01f2b83SNavdeep Parhar 	err = aq100x_power_down(phy, 0);
260c01f2b83SNavdeep Parhar 	if (!err)
261c01f2b83SNavdeep Parhar 		err = t3_mdio_change_bits(phy, MDIO_DEV_ANEG, MII_BMCR,
262c01f2b83SNavdeep Parhar 		    BMCR_RESET, BMCR_ANENABLE | BMCR_ANRESTART);
263c01f2b83SNavdeep Parhar 
264c01f2b83SNavdeep Parhar 	return (err);
265c01f2b83SNavdeep Parhar }
266c01f2b83SNavdeep Parhar 
267c01f2b83SNavdeep Parhar static int
aq100x_autoneg_restart(struct cphy * phy)268c01f2b83SNavdeep Parhar aq100x_autoneg_restart(struct cphy *phy)
269c01f2b83SNavdeep Parhar {
270c01f2b83SNavdeep Parhar 	return aq100x_autoneg_enable(phy);
271c01f2b83SNavdeep Parhar }
272c01f2b83SNavdeep Parhar 
273c01f2b83SNavdeep Parhar static int
aq100x_advertise(struct cphy * phy,unsigned int advertise_map)274c01f2b83SNavdeep Parhar aq100x_advertise(struct cphy *phy, unsigned int advertise_map)
275c01f2b83SNavdeep Parhar {
276c01f2b83SNavdeep Parhar 	unsigned int adv;
277c01f2b83SNavdeep Parhar 	int err;
278c01f2b83SNavdeep Parhar 
279c01f2b83SNavdeep Parhar 	/* 10G advertisement */
280c01f2b83SNavdeep Parhar 	adv = 0;
281c01f2b83SNavdeep Parhar 	if (advertise_map & ADVERTISED_10000baseT_Full)
282c01f2b83SNavdeep Parhar 		adv |= ADV_10G_FULL;
283c01f2b83SNavdeep Parhar 	err = t3_mdio_change_bits(phy, MDIO_DEV_ANEG, AQ_10G_CTRL,
284c01f2b83SNavdeep Parhar 				  ADV_10G_FULL, adv);
285c01f2b83SNavdeep Parhar 	if (err)
286c01f2b83SNavdeep Parhar 		return (err);
287c01f2b83SNavdeep Parhar 
288c01f2b83SNavdeep Parhar 	/* 1G advertisement */
289c01f2b83SNavdeep Parhar 	adv = 0;
290c01f2b83SNavdeep Parhar 	if (advertise_map & ADVERTISED_1000baseT_Full)
291c01f2b83SNavdeep Parhar 		adv |= ADV_1G_FULL;
292c01f2b83SNavdeep Parhar 	if (advertise_map & ADVERTISED_1000baseT_Half)
293c01f2b83SNavdeep Parhar 		adv |= ADV_1G_HALF;
294c01f2b83SNavdeep Parhar 	err = t3_mdio_change_bits(phy, MDIO_DEV_ANEG, AQ_1G_CTRL,
295c01f2b83SNavdeep Parhar 				  ADV_1G_FULL | ADV_1G_HALF, adv);
296c01f2b83SNavdeep Parhar 	if (err)
297c01f2b83SNavdeep Parhar 		return (err);
298c01f2b83SNavdeep Parhar 
299c01f2b83SNavdeep Parhar 	/* 100M, pause advertisement */
300c01f2b83SNavdeep Parhar 	adv = 0;
301c01f2b83SNavdeep Parhar 	if (advertise_map & ADVERTISED_100baseT_Half)
302c01f2b83SNavdeep Parhar 		adv |= ADVERTISE_100HALF;
303c01f2b83SNavdeep Parhar 	if (advertise_map & ADVERTISED_100baseT_Full)
304c01f2b83SNavdeep Parhar 		adv |= ADVERTISE_100FULL;
305c01f2b83SNavdeep Parhar 	if (advertise_map & ADVERTISED_Pause)
306c01f2b83SNavdeep Parhar 		adv |= ADVERTISE_PAUSE_CAP;
307c01f2b83SNavdeep Parhar 	if (advertise_map & ADVERTISED_Asym_Pause)
308c01f2b83SNavdeep Parhar 		adv |= ADVERTISE_PAUSE_ASYM;
309c01f2b83SNavdeep Parhar 	err = t3_mdio_change_bits(phy, MDIO_DEV_ANEG, AQ_100M_CTRL, 0xfe0, adv);
310c01f2b83SNavdeep Parhar 
311c01f2b83SNavdeep Parhar 	return (err);
312c01f2b83SNavdeep Parhar }
313c01f2b83SNavdeep Parhar 
314c01f2b83SNavdeep Parhar static int
aq100x_set_loopback(struct cphy * phy,int mmd,int dir,int enable)315c01f2b83SNavdeep Parhar aq100x_set_loopback(struct cphy *phy, int mmd, int dir, int enable)
316c01f2b83SNavdeep Parhar {
317c01f2b83SNavdeep Parhar 	return t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR,
318c01f2b83SNavdeep Parhar 				   BMCR_LOOPBACK, enable ? BMCR_LOOPBACK : 0);
319c01f2b83SNavdeep Parhar }
320c01f2b83SNavdeep Parhar 
321c01f2b83SNavdeep Parhar static int
aq100x_set_speed_duplex(struct cphy * phy,int speed,int duplex)322c01f2b83SNavdeep Parhar aq100x_set_speed_duplex(struct cphy *phy, int speed, int duplex)
323c01f2b83SNavdeep Parhar {
324c01f2b83SNavdeep Parhar 	int err, set;
325c01f2b83SNavdeep Parhar 
326c01f2b83SNavdeep Parhar 	if (speed == SPEED_100)
327c01f2b83SNavdeep Parhar 		set = BMCR_SPEED100;
328c01f2b83SNavdeep Parhar 	else if (speed == SPEED_1000)
329c01f2b83SNavdeep Parhar 		set = BMCR_SPEED1000;
330c01f2b83SNavdeep Parhar 	else if (speed == SPEED_10000)
331c01f2b83SNavdeep Parhar 		set = BMCR_SPEED1000 | BMCR_SPEED100;
332c01f2b83SNavdeep Parhar 	else
333c01f2b83SNavdeep Parhar 		return (EINVAL);
334c01f2b83SNavdeep Parhar 
335c01f2b83SNavdeep Parhar 	if (duplex != DUPLEX_FULL)
336c01f2b83SNavdeep Parhar 		return (EINVAL);
337c01f2b83SNavdeep Parhar 
338c01f2b83SNavdeep Parhar 	err = t3_mdio_change_bits(phy, MDIO_DEV_ANEG, MII_BMCR,
339c01f2b83SNavdeep Parhar 	    BMCR_RESET | BMCR_ANENABLE | BMCR_ANRESTART, 0);
340c01f2b83SNavdeep Parhar 	if (err)
341c01f2b83SNavdeep Parhar 		return (err);
342c01f2b83SNavdeep Parhar 
343c01f2b83SNavdeep Parhar 	err = t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR,
344c01f2b83SNavdeep Parhar 	    BMCR_SPEED1000 | BMCR_SPEED100, set);
345c01f2b83SNavdeep Parhar 	if (err)
346c01f2b83SNavdeep Parhar 		return (err);
347c01f2b83SNavdeep Parhar 
348c01f2b83SNavdeep Parhar 	return (0);
349c01f2b83SNavdeep Parhar }
350c01f2b83SNavdeep Parhar 
351c01f2b83SNavdeep Parhar static int
aq100x_get_link_status(struct cphy * phy,int * link_state,int * speed,int * duplex,int * fc)352a5eb009bSNavdeep Parhar aq100x_get_link_status(struct cphy *phy, int *link_state, int *speed, int *duplex,
353c01f2b83SNavdeep Parhar 		       int *fc)
354c01f2b83SNavdeep Parhar {
355c01f2b83SNavdeep Parhar 	int err;
356c01f2b83SNavdeep Parhar 	unsigned int v, link = 0;
357c01f2b83SNavdeep Parhar 
358c01f2b83SNavdeep Parhar 	err = mdio_read(phy, MDIO_DEV_PMA_PMD, AQ_LINK_STAT, &v);
359c01f2b83SNavdeep Parhar 	if (err)
360c01f2b83SNavdeep Parhar 		return (err);
361c01f2b83SNavdeep Parhar 	if (v == 0xffff || !(v & 1))
362c01f2b83SNavdeep Parhar 		goto done;
363c01f2b83SNavdeep Parhar 
364c01f2b83SNavdeep Parhar 	err = mdio_read(phy, MDIO_DEV_ANEG, MII_BMCR, &v);
365c01f2b83SNavdeep Parhar 	if (err)
366c01f2b83SNavdeep Parhar 		return (err);
367c01f2b83SNavdeep Parhar 	if (v & 0x8000)
368c01f2b83SNavdeep Parhar 		goto done;
369c01f2b83SNavdeep Parhar 	if (v & BMCR_ANENABLE) {
370c01f2b83SNavdeep Parhar 
371c01f2b83SNavdeep Parhar 		err = mdio_read(phy, MDIO_DEV_ANEG, 1, &v);
372c01f2b83SNavdeep Parhar 		if (err)
373c01f2b83SNavdeep Parhar 			return (err);
374c01f2b83SNavdeep Parhar 		if ((v & 0x20) == 0)
375c01f2b83SNavdeep Parhar 			goto done;
376c01f2b83SNavdeep Parhar 
377c01f2b83SNavdeep Parhar 		err = mdio_read(phy, MDIO_DEV_ANEG, AQ_ANEG_STAT, &v);
378c01f2b83SNavdeep Parhar 		if (err)
379c01f2b83SNavdeep Parhar 			return (err);
380c01f2b83SNavdeep Parhar 
381c01f2b83SNavdeep Parhar 		if (speed) {
382c01f2b83SNavdeep Parhar 			switch (v & 0x6) {
383c01f2b83SNavdeep Parhar 			case 0x6: *speed = SPEED_10000;
384c01f2b83SNavdeep Parhar 				break;
385c01f2b83SNavdeep Parhar 			case 0x4: *speed = SPEED_1000;
386c01f2b83SNavdeep Parhar 				break;
387c01f2b83SNavdeep Parhar 			case 0x2: *speed = SPEED_100;
388c01f2b83SNavdeep Parhar 				break;
389c01f2b83SNavdeep Parhar 			case 0x0: *speed = SPEED_10;
390c01f2b83SNavdeep Parhar 				break;
391c01f2b83SNavdeep Parhar 			}
392c01f2b83SNavdeep Parhar 		}
393c01f2b83SNavdeep Parhar 
394c01f2b83SNavdeep Parhar 		if (duplex)
395c01f2b83SNavdeep Parhar 			*duplex = v & 1 ? DUPLEX_FULL : DUPLEX_HALF;
396c01f2b83SNavdeep Parhar 
397c01f2b83SNavdeep Parhar 		if (fc) {
398c01f2b83SNavdeep Parhar 			unsigned int lpa, adv;
399c01f2b83SNavdeep Parhar 			err = mdio_read(phy, MDIO_DEV_ANEG, 0x13, &lpa);
400c01f2b83SNavdeep Parhar 			if (!err)
401c01f2b83SNavdeep Parhar 				err = mdio_read(phy, MDIO_DEV_ANEG,
402c01f2b83SNavdeep Parhar 				    AQ_100M_CTRL, &adv);
403c01f2b83SNavdeep Parhar 			if (err)
404c01f2b83SNavdeep Parhar 				return err;
405c01f2b83SNavdeep Parhar 
406c01f2b83SNavdeep Parhar 			if (lpa & adv & ADVERTISE_PAUSE_CAP)
407c01f2b83SNavdeep Parhar 				*fc = PAUSE_RX | PAUSE_TX;
408c01f2b83SNavdeep Parhar 			else if (lpa & ADVERTISE_PAUSE_CAP &&
409c01f2b83SNavdeep Parhar 			    lpa & ADVERTISE_PAUSE_ASYM &&
410c01f2b83SNavdeep Parhar 			    adv & ADVERTISE_PAUSE_ASYM)
411c01f2b83SNavdeep Parhar 				*fc = PAUSE_TX;
412c01f2b83SNavdeep Parhar 			else if (lpa & ADVERTISE_PAUSE_ASYM &&
413c01f2b83SNavdeep Parhar 			    adv & ADVERTISE_PAUSE_CAP)
414c01f2b83SNavdeep Parhar 				*fc = PAUSE_RX;
415c01f2b83SNavdeep Parhar 			else
416c01f2b83SNavdeep Parhar 				*fc = 0;
417c01f2b83SNavdeep Parhar 		}
418c01f2b83SNavdeep Parhar 
419c01f2b83SNavdeep Parhar 	} else {
420c01f2b83SNavdeep Parhar 		err = mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMCR, &v);
421c01f2b83SNavdeep Parhar 		if (err)
422c01f2b83SNavdeep Parhar 			return (err);
423c01f2b83SNavdeep Parhar 
424c01f2b83SNavdeep Parhar 		v &= BMCR_SPEED1000 | BMCR_SPEED100;
425c01f2b83SNavdeep Parhar 		if (speed) {
426c01f2b83SNavdeep Parhar 			if (v == (BMCR_SPEED1000 | BMCR_SPEED100))
427c01f2b83SNavdeep Parhar 				*speed = SPEED_10000;
428c01f2b83SNavdeep Parhar 			else if (v == BMCR_SPEED1000)
429c01f2b83SNavdeep Parhar 				*speed = SPEED_1000;
430c01f2b83SNavdeep Parhar 			else if (v == BMCR_SPEED100)
431c01f2b83SNavdeep Parhar 				*speed = SPEED_100;
432c01f2b83SNavdeep Parhar 			else
433c01f2b83SNavdeep Parhar 				*speed = SPEED_10;
434c01f2b83SNavdeep Parhar 		}
435c01f2b83SNavdeep Parhar 
436c01f2b83SNavdeep Parhar 		if (duplex)
437c01f2b83SNavdeep Parhar 			*duplex = DUPLEX_FULL;
438c01f2b83SNavdeep Parhar 	}
439c01f2b83SNavdeep Parhar 
440c01f2b83SNavdeep Parhar 	link = 1;
441c01f2b83SNavdeep Parhar done:
442a5eb009bSNavdeep Parhar 	if (link_state)
443a5eb009bSNavdeep Parhar 		*link_state = link ? PHY_LINK_UP : PHY_LINK_DOWN;
444c01f2b83SNavdeep Parhar 	return (0);
445c01f2b83SNavdeep Parhar }
446c01f2b83SNavdeep Parhar 
447c01f2b83SNavdeep Parhar static struct cphy_ops aq100x_ops = {
448c01f2b83SNavdeep Parhar 	.reset             = aq100x_reset,
449c01f2b83SNavdeep Parhar 	.intr_enable       = aq100x_intr_enable,
450c01f2b83SNavdeep Parhar 	.intr_disable      = aq100x_intr_disable,
451c01f2b83SNavdeep Parhar 	.intr_clear        = aq100x_intr_clear,
452c01f2b83SNavdeep Parhar 	.intr_handler      = aq100x_intr_handler,
453c01f2b83SNavdeep Parhar 	.autoneg_enable    = aq100x_autoneg_enable,
454c01f2b83SNavdeep Parhar 	.autoneg_restart   = aq100x_autoneg_restart,
455c01f2b83SNavdeep Parhar 	.advertise         = aq100x_advertise,
456c01f2b83SNavdeep Parhar 	.set_loopback      = aq100x_set_loopback,
457c01f2b83SNavdeep Parhar 	.set_speed_duplex  = aq100x_set_speed_duplex,
458c01f2b83SNavdeep Parhar 	.get_link_status   = aq100x_get_link_status,
459c01f2b83SNavdeep Parhar 	.power_down        = aq100x_power_down,
460c01f2b83SNavdeep Parhar };
461c01f2b83SNavdeep Parhar 
462c01f2b83SNavdeep Parhar int
t3_aq100x_phy_prep(pinfo_t * pinfo,int phy_addr,const struct mdio_ops * mdio_ops)463c01f2b83SNavdeep Parhar t3_aq100x_phy_prep(pinfo_t *pinfo, int phy_addr,
464c01f2b83SNavdeep Parhar 		       const struct mdio_ops *mdio_ops)
465c01f2b83SNavdeep Parhar {
466c01f2b83SNavdeep Parhar 	struct cphy *phy = &pinfo->phy;
467c01f2b83SNavdeep Parhar 	unsigned int v, v2, gpio, wait;
468c01f2b83SNavdeep Parhar 	int err;
469c01f2b83SNavdeep Parhar 	adapter_t *adapter = pinfo->adapter;
470c01f2b83SNavdeep Parhar 
471c01f2b83SNavdeep Parhar 	cphy_init(&pinfo->phy, adapter, pinfo, phy_addr, &aq100x_ops, mdio_ops,
472c01f2b83SNavdeep Parhar 		  SUPPORTED_1000baseT_Full | SUPPORTED_10000baseT_Full |
473c01f2b83SNavdeep Parhar 		  SUPPORTED_TP | SUPPORTED_Autoneg | SUPPORTED_AUI |
474c01f2b83SNavdeep Parhar 		  SUPPORTED_MISC_IRQ, "1000/10GBASE-T");
475c01f2b83SNavdeep Parhar 
476c01f2b83SNavdeep Parhar 	/*
477c01f2b83SNavdeep Parhar 	 * Hard reset the PHY.
478c01f2b83SNavdeep Parhar 	 */
479c01f2b83SNavdeep Parhar 	gpio = phy_addr ? F_GPIO10_OUT_VAL : F_GPIO6_OUT_VAL;
480c01f2b83SNavdeep Parhar 	t3_set_reg_field(adapter, A_T3DBG_GPIO_EN, gpio, 0);
481c01f2b83SNavdeep Parhar 	msleep(1);
482c01f2b83SNavdeep Parhar 	t3_set_reg_field(adapter, A_T3DBG_GPIO_EN, gpio, gpio);
483c01f2b83SNavdeep Parhar 
484c01f2b83SNavdeep Parhar 	/*
485c01f2b83SNavdeep Parhar 	 * Give it enough time to load the firmware and get ready for mdio.
486c01f2b83SNavdeep Parhar 	 */
487c01f2b83SNavdeep Parhar 	msleep(1000);
488c01f2b83SNavdeep Parhar 	wait = 500; /* in 10ms increments */
489c01f2b83SNavdeep Parhar 	do {
490c01f2b83SNavdeep Parhar 		err = mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMCR, &v);
491c01f2b83SNavdeep Parhar 		if (err || v == 0xffff) {
492c01f2b83SNavdeep Parhar 
493c01f2b83SNavdeep Parhar 			/* Allow prep_adapter to succeed when ffff is read */
494c01f2b83SNavdeep Parhar 
495c01f2b83SNavdeep Parhar 			CH_WARN(adapter, "PHY%d: reset failed (0x%x, 0x%x).\n",
496c01f2b83SNavdeep Parhar 				phy_addr, err, v);
497c01f2b83SNavdeep Parhar 			goto done;
498c01f2b83SNavdeep Parhar 		}
499c01f2b83SNavdeep Parhar 
500c01f2b83SNavdeep Parhar 		v &= BMCR_RESET;
501c01f2b83SNavdeep Parhar 		if (v)
502c01f2b83SNavdeep Parhar 			msleep(10);
503c01f2b83SNavdeep Parhar 	} while (v && --wait);
504c01f2b83SNavdeep Parhar 	if (v) {
505c01f2b83SNavdeep Parhar 		CH_WARN(adapter, "PHY%d: reset timed out (0x%x).\n",
506c01f2b83SNavdeep Parhar 			phy_addr, v);
507c01f2b83SNavdeep Parhar 
508c01f2b83SNavdeep Parhar 		goto done; /* let prep_adapter succeed */
509c01f2b83SNavdeep Parhar 	}
510c01f2b83SNavdeep Parhar 
511c01f2b83SNavdeep Parhar 	/* Firmware version check. */
512c01f2b83SNavdeep Parhar 	(void) mdio_read(phy, MDIO_DEV_VEND1, AQ_FW_VERSION, &v);
513c01f2b83SNavdeep Parhar 	if (v < 0x115)
514c01f2b83SNavdeep Parhar 		CH_WARN(adapter, "PHY%d: unknown firmware %d.%d\n", phy_addr,
515c01f2b83SNavdeep Parhar 		    v >> 8, v & 0xff);
516c01f2b83SNavdeep Parhar 
517c01f2b83SNavdeep Parhar 	/* The PHY should start in really-low-power mode. */
518c01f2b83SNavdeep Parhar 	(void) mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMCR, &v);
519c01f2b83SNavdeep Parhar 	if ((v & BMCR_PDOWN) == 0)
520c01f2b83SNavdeep Parhar 		CH_WARN(adapter, "PHY%d does not start in low power mode.\n",
521c01f2b83SNavdeep Parhar 			phy_addr);
522c01f2b83SNavdeep Parhar 
523c01f2b83SNavdeep Parhar 	/*
524c01f2b83SNavdeep Parhar 	 * Verify XAUI and 1000-X settings, but let prep succeed no matter what.
525c01f2b83SNavdeep Parhar 	 */
526c01f2b83SNavdeep Parhar 	v = v2 = 0;
527c01f2b83SNavdeep Parhar 	(void) mdio_read(phy, MDIO_DEV_XGXS, AQ_XAUI_RX_CFG, &v);
528c01f2b83SNavdeep Parhar 	(void) mdio_read(phy, MDIO_DEV_XGXS, AQ_XAUI_TX_CFG, &v2);
529c01f2b83SNavdeep Parhar 	if (v != 0x1b || v2 != 0x1b)
530c01f2b83SNavdeep Parhar 		CH_WARN(adapter, "PHY%d: incorrect XAUI settings "
531c01f2b83SNavdeep Parhar 		    "(0x%x, 0x%x).\n", phy_addr, v, v2);
532c01f2b83SNavdeep Parhar 	v = 0;
533c01f2b83SNavdeep Parhar 	(void) mdio_read(phy, MDIO_DEV_XGXS, AQ_XAUI_KX_CFG, &v);
534c01f2b83SNavdeep Parhar 	if ((v & 0xf) != 0xf)
535c01f2b83SNavdeep Parhar 		CH_WARN(adapter, "PHY%d: incorrect 1000-X settings "
536c01f2b83SNavdeep Parhar 		    "(0x%x).\n", phy_addr, v);
537c01f2b83SNavdeep Parhar 
538c01f2b83SNavdeep Parhar 	(void) aq100x_set_defaults(phy);
539c01f2b83SNavdeep Parhar done:
540c01f2b83SNavdeep Parhar 	return (err);
541c01f2b83SNavdeep Parhar }
542