cxgb_mv88e1xxx.c revision 180583
1107120Sjulian/**************************************************************************
2107120Sjulian
3107120SjulianCopyright (c) 2007, Chelsio Inc.
4107120SjulianAll rights reserved.
5107120Sjulian
6107120SjulianRedistribution and use in source and binary forms, with or without
7107120Sjulianmodification, are permitted provided that the following conditions are met:
8107120Sjulian
9107120Sjulian 1. Redistributions of source code must retain the above copyright notice,
10107120Sjulian    this list of conditions and the following disclaimer.
11107120Sjulian
12107120Sjulian 2. Neither the name of the Chelsio Corporation nor the names of its
13107120Sjulian    contributors may be used to endorse or promote products derived from
14107120Sjulian    this software without specific prior written permission.
15107120Sjulian
16107120SjulianTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17107120SjulianAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18107120SjulianIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19107120SjulianARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20107120SjulianLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21107120SjulianCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22107120SjulianSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23107120SjulianINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24107120SjulianCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25107120SjulianARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26107120SjulianPOSSIBILITY OF SUCH DAMAGE.
27107120Sjulian
28121054Semax***************************************************************************/
29107120Sjulian
30107120Sjulian#include <sys/cdefs.h>
31107120Sjulian__FBSDID("$FreeBSD: head/sys/dev/cxgb/common/cxgb_mv88e1xxx.c 180583 2008-07-18 06:12:31Z kmacy $");
32107120Sjulian
33121054Semax#ifdef CONFIG_DEFINED
34107120Sjulian#include <cxgb_include.h>
35107120Sjulian#else
36107120Sjulian#include <dev/cxgb/cxgb_include.h>
37107120Sjulian#endif
38107120Sjulian
39107120Sjulian/* Marvell PHY interrupt status bits. */
40107120Sjulian#define MV_INTR_JABBER          0x0001
41107120Sjulian#define MV_INTR_POLARITY_CHNG   0x0002
42107120Sjulian#define MV_INTR_ENG_DETECT_CHNG 0x0010
43107120Sjulian#define MV_INTR_DOWNSHIFT       0x0020
44107120Sjulian#define MV_INTR_MDI_XOVER_CHNG  0x0040
45107120Sjulian#define MV_INTR_FIFO_OVER_UNDER 0x0080
46107120Sjulian#define MV_INTR_FALSE_CARRIER   0x0100
47107120Sjulian#define MV_INTR_SYMBOL_ERROR    0x0200
48107120Sjulian#define MV_INTR_LINK_CHNG       0x0400
49107120Sjulian#define MV_INTR_AUTONEG_DONE    0x0800
50121054Semax#define MV_INTR_PAGE_RECV       0x1000
51121054Semax#define MV_INTR_DUPLEX_CHNG     0x2000
52121054Semax#define MV_INTR_SPEED_CHNG      0x4000
53107120Sjulian#define MV_INTR_AUTONEG_ERR     0x8000
54107120Sjulian
55107120Sjulian/* Marvell PHY specific registers. */
56107120Sjulian#define MV88E1XXX_SPECIFIC_CNTRL          16
57107120Sjulian#define MV88E1XXX_SPECIFIC_STATUS         17
58107120Sjulian#define MV88E1XXX_INTR_ENABLE             18
59107120Sjulian#define MV88E1XXX_INTR_STATUS             19
60107120Sjulian#define MV88E1XXX_EXT_SPECIFIC_CNTRL      20
61107120Sjulian#define MV88E1XXX_RECV_ERR                21
62121054Semax#define MV88E1XXX_EXT_ADDR                22
63107120Sjulian#define MV88E1XXX_GLOBAL_STATUS           23
64121054Semax#define MV88E1XXX_LED_CNTRL               24
65121054Semax#define MV88E1XXX_LED_OVERRIDE            25
66121054Semax#define MV88E1XXX_EXT_SPECIFIC_CNTRL2     26
67107120Sjulian#define MV88E1XXX_EXT_SPECIFIC_STATUS     27
68121054Semax#define MV88E1XXX_VIRTUAL_CABLE_TESTER    28
69121054Semax#define MV88E1XXX_EXTENDED_ADDR           29
70121054Semax#define MV88E1XXX_EXTENDED_DATA           30
71121054Semax
72107120Sjulian/* PHY specific control register fields */
73121054Semax#define S_PSCR_MDI_XOVER_MODE    5
74107120Sjulian#define M_PSCR_MDI_XOVER_MODE    0x3
75121054Semax#define V_PSCR_MDI_XOVER_MODE(x) ((x) << S_PSCR_MDI_XOVER_MODE)
76121054Semax
77121054Semax/* Extended PHY specific control register fields */
78107120Sjulian#define S_DOWNSHIFT_ENABLE 8
79114879Sjulian#define V_DOWNSHIFT_ENABLE (1 << S_DOWNSHIFT_ENABLE)
80107120Sjulian
81107120Sjulian#define S_DOWNSHIFT_CNT    9
82107120Sjulian#define M_DOWNSHIFT_CNT    0x7
83107120Sjulian#define V_DOWNSHIFT_CNT(x) ((x) << S_DOWNSHIFT_CNT)
84107120Sjulian
85107120Sjulian/* PHY specific status register fields */
86107120Sjulian#define S_PSSR_JABBER 0
87107120Sjulian#define V_PSSR_JABBER (1 << S_PSSR_JABBER)
88107120Sjulian
89107120Sjulian#define S_PSSR_POLARITY 1
90107120Sjulian#define V_PSSR_POLARITY (1 << S_PSSR_POLARITY)
91107120Sjulian
92107120Sjulian#define S_PSSR_RX_PAUSE 2
93107120Sjulian#define V_PSSR_RX_PAUSE (1 << S_PSSR_RX_PAUSE)
94107120Sjulian
95107120Sjulian#define S_PSSR_TX_PAUSE 3
96107120Sjulian#define V_PSSR_TX_PAUSE (1 << S_PSSR_TX_PAUSE)
97107120Sjulian
98107120Sjulian#define S_PSSR_ENERGY_DETECT 4
99107120Sjulian#define V_PSSR_ENERGY_DETECT (1 << S_PSSR_ENERGY_DETECT)
100107120Sjulian
101107120Sjulian#define S_PSSR_DOWNSHIFT_STATUS 5
102107120Sjulian#define V_PSSR_DOWNSHIFT_STATUS (1 << S_PSSR_DOWNSHIFT_STATUS)
103107120Sjulian
104107120Sjulian#define S_PSSR_MDI 6
105107120Sjulian#define V_PSSR_MDI (1 << S_PSSR_MDI)
106107120Sjulian
107107120Sjulian#define S_PSSR_CABLE_LEN    7
108107120Sjulian#define M_PSSR_CABLE_LEN    0x7
109107120Sjulian#define V_PSSR_CABLE_LEN(x) ((x) << S_PSSR_CABLE_LEN)
110107120Sjulian#define G_PSSR_CABLE_LEN(x) (((x) >> S_PSSR_CABLE_LEN) & M_PSSR_CABLE_LEN)
111107120Sjulian
112107120Sjulian#define S_PSSR_LINK 10
113107120Sjulian#define V_PSSR_LINK (1 << S_PSSR_LINK)
114107120Sjulian
115107120Sjulian#define S_PSSR_STATUS_RESOLVED 11
116107120Sjulian#define V_PSSR_STATUS_RESOLVED (1 << S_PSSR_STATUS_RESOLVED)
117107120Sjulian
118107120Sjulian#define S_PSSR_PAGE_RECEIVED 12
119107120Sjulian#define V_PSSR_PAGE_RECEIVED (1 << S_PSSR_PAGE_RECEIVED)
120107120Sjulian
121107120Sjulian#define S_PSSR_DUPLEX 13
122107120Sjulian#define V_PSSR_DUPLEX (1 << S_PSSR_DUPLEX)
123107120Sjulian
124107120Sjulian#define S_PSSR_SPEED    14
125107120Sjulian#define M_PSSR_SPEED    0x3
126107120Sjulian#define V_PSSR_SPEED(x) ((x) << S_PSSR_SPEED)
127107120Sjulian#define G_PSSR_SPEED(x) (((x) >> S_PSSR_SPEED) & M_PSSR_SPEED)
128107120Sjulian
129107120Sjulian/* MV88E1XXX MDI crossover register values */
130107120Sjulian#define CROSSOVER_MDI   0
131107120Sjulian#define CROSSOVER_MDIX  1
132107120Sjulian#define CROSSOVER_AUTO  3
133107120Sjulian
134107120Sjulian#define INTR_ENABLE_MASK (MV_INTR_SPEED_CHNG | MV_INTR_DUPLEX_CHNG | \
135107120Sjulian	MV_INTR_AUTONEG_DONE | MV_INTR_LINK_CHNG | MV_INTR_FIFO_OVER_UNDER | \
136107120Sjulian	MV_INTR_ENG_DETECT_CHNG)
137107120Sjulian
138107120Sjulian/*
139107120Sjulian * Reset the PHY.  If 'wait' is set wait until the reset completes.
140107120Sjulian */
141107120Sjulianstatic int mv88e1xxx_reset(struct cphy *cphy, int wait)
142107120Sjulian{
143121054Semax	return t3_phy_reset(cphy, 0, wait);
144107120Sjulian}
145107120Sjulian
146107120Sjulianstatic int mv88e1xxx_intr_enable(struct cphy *cphy)
147107120Sjulian{
148107120Sjulian	return mdio_write(cphy, 0, MV88E1XXX_INTR_ENABLE, INTR_ENABLE_MASK);
149107120Sjulian}
150107120Sjulian
151107120Sjulianstatic int mv88e1xxx_intr_disable(struct cphy *cphy)
152107120Sjulian{
153107120Sjulian	return mdio_write(cphy, 0, MV88E1XXX_INTR_ENABLE, 0);
154107120Sjulian}
155107120Sjulian
156107120Sjulianstatic int mv88e1xxx_intr_clear(struct cphy *cphy)
157107120Sjulian{
158107120Sjulian	u32 val;
159107120Sjulian
160107120Sjulian	/* Clear PHY interrupts by reading the register. */
161107120Sjulian	return mdio_read(cphy, 0, MV88E1XXX_INTR_STATUS, &val);
162107120Sjulian}
163107120Sjulian
164107120Sjulianstatic int mv88e1xxx_crossover_set(struct cphy *cphy, int crossover)
165107120Sjulian{
166107120Sjulian	return t3_mdio_change_bits(cphy, 0, MV88E1XXX_SPECIFIC_CNTRL,
167107120Sjulian				   V_PSCR_MDI_XOVER_MODE(M_PSCR_MDI_XOVER_MODE),
168107120Sjulian				   V_PSCR_MDI_XOVER_MODE(crossover));
169107120Sjulian}
170107120Sjulian
171107120Sjulianstatic int mv88e1xxx_autoneg_enable(struct cphy *cphy)
172107120Sjulian{
173107120Sjulian	mv88e1xxx_crossover_set(cphy, CROSSOVER_AUTO);
174107120Sjulian
175107120Sjulian	/* restart autoneg for change to take effect */
176107120Sjulian	return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE,
177107120Sjulian			 	   BMCR_ANENABLE | BMCR_ANRESTART);
178107120Sjulian}
179107120Sjulian
180107120Sjulianstatic int mv88e1xxx_autoneg_restart(struct cphy *cphy)
181107120Sjulian{
182107120Sjulian	return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE,
183107120Sjulian			 	   BMCR_ANRESTART);
184107120Sjulian}
185107120Sjulian
186107120Sjulianstatic int mv88e1xxx_set_loopback(struct cphy *cphy, int mmd, int dir, int on)
187107120Sjulian{
188107120Sjulian	return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_LOOPBACK,
189107120Sjulian			 	   on ? BMCR_LOOPBACK : 0);
190107120Sjulian}
191107120Sjulian
192107120Sjulianstatic int mv88e1xxx_get_link_status(struct cphy *cphy, int *link_ok,
193107120Sjulian				     int *speed, int *duplex, int *fc)
194107120Sjulian{
195107120Sjulian	u32 status;
196121054Semax	int sp = -1, dplx = -1, pause = 0;
197107120Sjulian
198107120Sjulian	mdio_read(cphy, 0, MV88E1XXX_SPECIFIC_STATUS, &status);
199107120Sjulian	if ((status & V_PSSR_STATUS_RESOLVED) != 0) {
200107120Sjulian		if (status & V_PSSR_RX_PAUSE)
201107120Sjulian			pause |= PAUSE_RX;
202107120Sjulian		if (status & V_PSSR_TX_PAUSE)
203107120Sjulian			pause |= PAUSE_TX;
204107120Sjulian		dplx = (status & V_PSSR_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF;
205107120Sjulian		sp = G_PSSR_SPEED(status);
206107120Sjulian		if (sp == 0)
207107120Sjulian			sp = SPEED_10;
208107120Sjulian		else if (sp == 1)
209107120Sjulian			sp = SPEED_100;
210220840Semax		else
211220840Semax			sp = SPEED_1000;
212220840Semax	}
213220840Semax	if (link_ok)
214220840Semax		*link_ok = (status & V_PSSR_LINK) != 0;
215220840Semax	if (speed)
216220840Semax		*speed = sp;
217220840Semax	if (duplex)
218107120Sjulian		*duplex = dplx;
219107120Sjulian	if (fc)
220107120Sjulian		*fc = pause;
221	return 0;
222}
223
224static int mv88e1xxx_set_speed_duplex(struct cphy *phy, int speed, int duplex)
225{
226	int err = t3_set_phy_speed_duplex(phy, speed, duplex);
227
228	/* PHY needs reset for new settings to take effect */
229	if (!err)
230		err = mv88e1xxx_reset(phy, 0);
231	return err;
232}
233
234static int mv88e1xxx_downshift_set(struct cphy *cphy, int downshift_enable)
235{
236	/*
237	 * Set the downshift counter to 2 so we try to establish Gb link
238	 * twice before downshifting.
239	 */
240	return t3_mdio_change_bits(cphy, 0, MV88E1XXX_EXT_SPECIFIC_CNTRL,
241		V_DOWNSHIFT_ENABLE | V_DOWNSHIFT_CNT(M_DOWNSHIFT_CNT),
242		downshift_enable ? V_DOWNSHIFT_ENABLE | V_DOWNSHIFT_CNT(2) : 0);
243}
244
245static int mv88e1xxx_power_down(struct cphy *cphy, int enable)
246{
247	return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN,
248				   enable ? BMCR_PDOWN : 0);
249}
250
251static int mv88e1xxx_intr_handler(struct cphy *cphy)
252{
253	const u32 link_change_intrs = MV_INTR_LINK_CHNG |
254		MV_INTR_AUTONEG_DONE | MV_INTR_DUPLEX_CHNG |
255		MV_INTR_SPEED_CHNG | MV_INTR_DOWNSHIFT;
256
257	u32 cause;
258	int cphy_cause = 0;
259
260	mdio_read(cphy, 0, MV88E1XXX_INTR_STATUS, &cause);
261	cause &= INTR_ENABLE_MASK;
262	if (cause & link_change_intrs)
263		cphy_cause |= cphy_cause_link_change;
264	if (cause & MV_INTR_FIFO_OVER_UNDER)
265		cphy_cause |= cphy_cause_fifo_error;
266	return cphy_cause;
267}
268
269#ifdef C99_NOT_SUPPORTED
270static struct cphy_ops mv88e1xxx_ops = {
271	mv88e1xxx_reset,
272	mv88e1xxx_intr_enable,
273	mv88e1xxx_intr_disable,
274	mv88e1xxx_intr_clear,
275	mv88e1xxx_intr_handler,
276	mv88e1xxx_autoneg_enable,
277	mv88e1xxx_autoneg_restart,
278	t3_phy_advertise,
279	mv88e1xxx_set_loopback,
280	mv88e1xxx_set_speed_duplex,
281	mv88e1xxx_get_link_status,
282	mv88e1xxx_power_down,
283};
284#else
285static struct cphy_ops mv88e1xxx_ops = {
286	.reset             = mv88e1xxx_reset,
287	.intr_enable       = mv88e1xxx_intr_enable,
288	.intr_disable      = mv88e1xxx_intr_disable,
289	.intr_clear        = mv88e1xxx_intr_clear,
290	.intr_handler      = mv88e1xxx_intr_handler,
291	.autoneg_enable    = mv88e1xxx_autoneg_enable,
292	.autoneg_restart   = mv88e1xxx_autoneg_restart,
293	.advertise         = t3_phy_advertise,
294	.set_loopback      = mv88e1xxx_set_loopback,
295	.set_speed_duplex  = mv88e1xxx_set_speed_duplex,
296	.get_link_status   = mv88e1xxx_get_link_status,
297	.power_down        = mv88e1xxx_power_down,
298};
299#endif
300
301int t3_mv88e1xxx_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr,
302			  const struct mdio_ops *mdio_ops)
303{
304	int err;
305
306	cphy_init(phy, adapter, phy_addr, &mv88e1xxx_ops, mdio_ops,
307		  SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Full |
308		  SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_MII |
309		  SUPPORTED_TP | SUPPORTED_IRQ, "10/100/1000BASE-T");
310
311	/* Configure copper PHY transmitter as class A to reduce EMI. */
312	err = mdio_write(phy, 0, MV88E1XXX_EXTENDED_ADDR, 0xb);
313	if (!err)
314		err = mdio_write(phy, 0, MV88E1XXX_EXTENDED_DATA, 0x8004);
315
316	if (!err)
317		err = mv88e1xxx_downshift_set(phy, 1);   /* Enable downshift */
318	return err;
319}
320