1167514Skmacy/**************************************************************************
2167514Skmacy
3167514SkmacyCopyright (c) 2007, Chelsio Inc.
4167514SkmacyAll rights reserved.
5167514Skmacy
6167514SkmacyRedistribution and use in source and binary forms, with or without
7167514Skmacymodification, are permitted provided that the following conditions are met:
8167514Skmacy
9167514Skmacy 1. Redistributions of source code must retain the above copyright notice,
10167514Skmacy    this list of conditions and the following disclaimer.
11167514Skmacy
12170076Skmacy 2. Neither the name of the Chelsio Corporation nor the names of its
13167514Skmacy    contributors may be used to endorse or promote products derived from
14167514Skmacy    this software without specific prior written permission.
15167514Skmacy
16167514SkmacyTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17167514SkmacyAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18167514SkmacyIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19167514SkmacyARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20167514SkmacyLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21167514SkmacyCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22167514SkmacySUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23167514SkmacyINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24167514SkmacyCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25167514SkmacyARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26167514SkmacyPOSSIBILITY OF SUCH DAMAGE.
27167514Skmacy
28167514Skmacy***************************************************************************/
29167514Skmacy
30167514Skmacy#include <sys/cdefs.h>
31167514Skmacy__FBSDID("$FreeBSD$");
32167514Skmacy
33170076Skmacy#include <cxgb_include.h>
34167514Skmacy
35167514Skmacy/* Marvell PHY interrupt status bits. */
36167514Skmacy#define MV_INTR_JABBER          0x0001
37167514Skmacy#define MV_INTR_POLARITY_CHNG   0x0002
38167514Skmacy#define MV_INTR_ENG_DETECT_CHNG 0x0010
39167514Skmacy#define MV_INTR_DOWNSHIFT       0x0020
40167514Skmacy#define MV_INTR_MDI_XOVER_CHNG  0x0040
41167514Skmacy#define MV_INTR_FIFO_OVER_UNDER 0x0080
42167514Skmacy#define MV_INTR_FALSE_CARRIER   0x0100
43167514Skmacy#define MV_INTR_SYMBOL_ERROR    0x0200
44167514Skmacy#define MV_INTR_LINK_CHNG       0x0400
45167514Skmacy#define MV_INTR_AUTONEG_DONE    0x0800
46167514Skmacy#define MV_INTR_PAGE_RECV       0x1000
47167514Skmacy#define MV_INTR_DUPLEX_CHNG     0x2000
48167514Skmacy#define MV_INTR_SPEED_CHNG      0x4000
49167514Skmacy#define MV_INTR_AUTONEG_ERR     0x8000
50167514Skmacy
51167514Skmacy/* Marvell PHY specific registers. */
52167514Skmacy#define MV88E1XXX_SPECIFIC_CNTRL          16
53167514Skmacy#define MV88E1XXX_SPECIFIC_STATUS         17
54167514Skmacy#define MV88E1XXX_INTR_ENABLE             18
55167514Skmacy#define MV88E1XXX_INTR_STATUS             19
56167514Skmacy#define MV88E1XXX_EXT_SPECIFIC_CNTRL      20
57167514Skmacy#define MV88E1XXX_RECV_ERR                21
58167514Skmacy#define MV88E1XXX_EXT_ADDR                22
59167514Skmacy#define MV88E1XXX_GLOBAL_STATUS           23
60167514Skmacy#define MV88E1XXX_LED_CNTRL               24
61167514Skmacy#define MV88E1XXX_LED_OVERRIDE            25
62167514Skmacy#define MV88E1XXX_EXT_SPECIFIC_CNTRL2     26
63167514Skmacy#define MV88E1XXX_EXT_SPECIFIC_STATUS     27
64167514Skmacy#define MV88E1XXX_VIRTUAL_CABLE_TESTER    28
65167514Skmacy#define MV88E1XXX_EXTENDED_ADDR           29
66167514Skmacy#define MV88E1XXX_EXTENDED_DATA           30
67167514Skmacy
68167514Skmacy/* PHY specific control register fields */
69167514Skmacy#define S_PSCR_MDI_XOVER_MODE    5
70167514Skmacy#define M_PSCR_MDI_XOVER_MODE    0x3
71167514Skmacy#define V_PSCR_MDI_XOVER_MODE(x) ((x) << S_PSCR_MDI_XOVER_MODE)
72167514Skmacy
73167514Skmacy/* Extended PHY specific control register fields */
74167514Skmacy#define S_DOWNSHIFT_ENABLE 8
75167514Skmacy#define V_DOWNSHIFT_ENABLE (1 << S_DOWNSHIFT_ENABLE)
76167514Skmacy
77167514Skmacy#define S_DOWNSHIFT_CNT    9
78167514Skmacy#define M_DOWNSHIFT_CNT    0x7
79167514Skmacy#define V_DOWNSHIFT_CNT(x) ((x) << S_DOWNSHIFT_CNT)
80167514Skmacy
81167514Skmacy/* PHY specific status register fields */
82167514Skmacy#define S_PSSR_JABBER 0
83167514Skmacy#define V_PSSR_JABBER (1 << S_PSSR_JABBER)
84167514Skmacy
85167514Skmacy#define S_PSSR_POLARITY 1
86167514Skmacy#define V_PSSR_POLARITY (1 << S_PSSR_POLARITY)
87167514Skmacy
88167514Skmacy#define S_PSSR_RX_PAUSE 2
89167514Skmacy#define V_PSSR_RX_PAUSE (1 << S_PSSR_RX_PAUSE)
90167514Skmacy
91167514Skmacy#define S_PSSR_TX_PAUSE 3
92167514Skmacy#define V_PSSR_TX_PAUSE (1 << S_PSSR_TX_PAUSE)
93167514Skmacy
94167514Skmacy#define S_PSSR_ENERGY_DETECT 4
95167514Skmacy#define V_PSSR_ENERGY_DETECT (1 << S_PSSR_ENERGY_DETECT)
96167514Skmacy
97167514Skmacy#define S_PSSR_DOWNSHIFT_STATUS 5
98167514Skmacy#define V_PSSR_DOWNSHIFT_STATUS (1 << S_PSSR_DOWNSHIFT_STATUS)
99167514Skmacy
100167514Skmacy#define S_PSSR_MDI 6
101167514Skmacy#define V_PSSR_MDI (1 << S_PSSR_MDI)
102167514Skmacy
103167514Skmacy#define S_PSSR_CABLE_LEN    7
104167514Skmacy#define M_PSSR_CABLE_LEN    0x7
105167514Skmacy#define V_PSSR_CABLE_LEN(x) ((x) << S_PSSR_CABLE_LEN)
106167514Skmacy#define G_PSSR_CABLE_LEN(x) (((x) >> S_PSSR_CABLE_LEN) & M_PSSR_CABLE_LEN)
107167514Skmacy
108167514Skmacy#define S_PSSR_LINK 10
109167514Skmacy#define V_PSSR_LINK (1 << S_PSSR_LINK)
110167514Skmacy
111167514Skmacy#define S_PSSR_STATUS_RESOLVED 11
112167514Skmacy#define V_PSSR_STATUS_RESOLVED (1 << S_PSSR_STATUS_RESOLVED)
113167514Skmacy
114167514Skmacy#define S_PSSR_PAGE_RECEIVED 12
115167514Skmacy#define V_PSSR_PAGE_RECEIVED (1 << S_PSSR_PAGE_RECEIVED)
116167514Skmacy
117167514Skmacy#define S_PSSR_DUPLEX 13
118167514Skmacy#define V_PSSR_DUPLEX (1 << S_PSSR_DUPLEX)
119167514Skmacy
120167514Skmacy#define S_PSSR_SPEED    14
121167514Skmacy#define M_PSSR_SPEED    0x3
122167514Skmacy#define V_PSSR_SPEED(x) ((x) << S_PSSR_SPEED)
123167514Skmacy#define G_PSSR_SPEED(x) (((x) >> S_PSSR_SPEED) & M_PSSR_SPEED)
124167514Skmacy
125167514Skmacy/* MV88E1XXX MDI crossover register values */
126167514Skmacy#define CROSSOVER_MDI   0
127167514Skmacy#define CROSSOVER_MDIX  1
128167514Skmacy#define CROSSOVER_AUTO  3
129167514Skmacy
130167514Skmacy#define INTR_ENABLE_MASK (MV_INTR_SPEED_CHNG | MV_INTR_DUPLEX_CHNG | \
131167514Skmacy	MV_INTR_AUTONEG_DONE | MV_INTR_LINK_CHNG | MV_INTR_FIFO_OVER_UNDER | \
132167514Skmacy	MV_INTR_ENG_DETECT_CHNG)
133167514Skmacy
134167514Skmacy/*
135167514Skmacy * Reset the PHY.  If 'wait' is set wait until the reset completes.
136167514Skmacy */
137167514Skmacystatic int mv88e1xxx_reset(struct cphy *cphy, int wait)
138167514Skmacy{
139167514Skmacy	return t3_phy_reset(cphy, 0, wait);
140167514Skmacy}
141167514Skmacy
142167514Skmacystatic int mv88e1xxx_intr_enable(struct cphy *cphy)
143167514Skmacy{
144167514Skmacy	return mdio_write(cphy, 0, MV88E1XXX_INTR_ENABLE, INTR_ENABLE_MASK);
145167514Skmacy}
146167514Skmacy
147167514Skmacystatic int mv88e1xxx_intr_disable(struct cphy *cphy)
148167514Skmacy{
149167514Skmacy	return mdio_write(cphy, 0, MV88E1XXX_INTR_ENABLE, 0);
150167514Skmacy}
151167514Skmacy
152167514Skmacystatic int mv88e1xxx_intr_clear(struct cphy *cphy)
153167514Skmacy{
154167514Skmacy	u32 val;
155167514Skmacy
156167514Skmacy	/* Clear PHY interrupts by reading the register. */
157167514Skmacy	return mdio_read(cphy, 0, MV88E1XXX_INTR_STATUS, &val);
158167514Skmacy}
159167514Skmacy
160167514Skmacystatic int mv88e1xxx_crossover_set(struct cphy *cphy, int crossover)
161167514Skmacy{
162167514Skmacy	return t3_mdio_change_bits(cphy, 0, MV88E1XXX_SPECIFIC_CNTRL,
163167514Skmacy				   V_PSCR_MDI_XOVER_MODE(M_PSCR_MDI_XOVER_MODE),
164167514Skmacy				   V_PSCR_MDI_XOVER_MODE(crossover));
165167514Skmacy}
166167514Skmacy
167167514Skmacystatic int mv88e1xxx_autoneg_enable(struct cphy *cphy)
168167514Skmacy{
169167514Skmacy	mv88e1xxx_crossover_set(cphy, CROSSOVER_AUTO);
170167514Skmacy
171167514Skmacy	/* restart autoneg for change to take effect */
172167514Skmacy	return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE,
173167514Skmacy			 	   BMCR_ANENABLE | BMCR_ANRESTART);
174167514Skmacy}
175167514Skmacy
176167514Skmacystatic int mv88e1xxx_autoneg_restart(struct cphy *cphy)
177167514Skmacy{
178167514Skmacy	return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE,
179167514Skmacy			 	   BMCR_ANRESTART);
180167514Skmacy}
181167514Skmacy
182167514Skmacystatic int mv88e1xxx_set_loopback(struct cphy *cphy, int mmd, int dir, int on)
183167514Skmacy{
184167514Skmacy	return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_LOOPBACK,
185167514Skmacy			 	   on ? BMCR_LOOPBACK : 0);
186167514Skmacy}
187167514Skmacy
188276959Snpstatic int mv88e1xxx_get_link_status(struct cphy *cphy, int *link_state,
189167514Skmacy				     int *speed, int *duplex, int *fc)
190167514Skmacy{
191167514Skmacy	u32 status;
192167514Skmacy	int sp = -1, dplx = -1, pause = 0;
193167514Skmacy
194167514Skmacy	mdio_read(cphy, 0, MV88E1XXX_SPECIFIC_STATUS, &status);
195167514Skmacy	if ((status & V_PSSR_STATUS_RESOLVED) != 0) {
196167514Skmacy		if (status & V_PSSR_RX_PAUSE)
197167514Skmacy			pause |= PAUSE_RX;
198167514Skmacy		if (status & V_PSSR_TX_PAUSE)
199167514Skmacy			pause |= PAUSE_TX;
200167514Skmacy		dplx = (status & V_PSSR_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF;
201167514Skmacy		sp = G_PSSR_SPEED(status);
202167514Skmacy		if (sp == 0)
203167514Skmacy			sp = SPEED_10;
204167514Skmacy		else if (sp == 1)
205167514Skmacy			sp = SPEED_100;
206167514Skmacy		else
207167514Skmacy			sp = SPEED_1000;
208167514Skmacy	}
209276959Snp	if (link_state)
210276959Snp		*link_state = status & V_PSSR_LINK ? PHY_LINK_UP :
211276959Snp		    PHY_LINK_DOWN;
212167514Skmacy	if (speed)
213167514Skmacy		*speed = sp;
214167514Skmacy	if (duplex)
215167514Skmacy		*duplex = dplx;
216167514Skmacy	if (fc)
217167514Skmacy		*fc = pause;
218167514Skmacy	return 0;
219167514Skmacy}
220167514Skmacy
221176472Skmacystatic int mv88e1xxx_set_speed_duplex(struct cphy *phy, int speed, int duplex)
222176472Skmacy{
223176472Skmacy	int err = t3_set_phy_speed_duplex(phy, speed, duplex);
224176472Skmacy
225176472Skmacy	/* PHY needs reset for new settings to take effect */
226176472Skmacy	if (!err)
227176472Skmacy		err = mv88e1xxx_reset(phy, 0);
228176472Skmacy	return err;
229176472Skmacy}
230176472Skmacy
231167514Skmacystatic int mv88e1xxx_downshift_set(struct cphy *cphy, int downshift_enable)
232167514Skmacy{
233167514Skmacy	/*
234167514Skmacy	 * Set the downshift counter to 2 so we try to establish Gb link
235167514Skmacy	 * twice before downshifting.
236167514Skmacy	 */
237167514Skmacy	return t3_mdio_change_bits(cphy, 0, MV88E1XXX_EXT_SPECIFIC_CNTRL,
238167514Skmacy		V_DOWNSHIFT_ENABLE | V_DOWNSHIFT_CNT(M_DOWNSHIFT_CNT),
239167514Skmacy		downshift_enable ? V_DOWNSHIFT_ENABLE | V_DOWNSHIFT_CNT(2) : 0);
240167514Skmacy}
241167514Skmacy
242167514Skmacystatic int mv88e1xxx_power_down(struct cphy *cphy, int enable)
243167514Skmacy{
244167514Skmacy	return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN,
245167514Skmacy				   enable ? BMCR_PDOWN : 0);
246167514Skmacy}
247167514Skmacy
248167514Skmacystatic int mv88e1xxx_intr_handler(struct cphy *cphy)
249167514Skmacy{
250167514Skmacy	const u32 link_change_intrs = MV_INTR_LINK_CHNG |
251167514Skmacy		MV_INTR_AUTONEG_DONE | MV_INTR_DUPLEX_CHNG |
252167514Skmacy		MV_INTR_SPEED_CHNG | MV_INTR_DOWNSHIFT;
253167514Skmacy
254167514Skmacy	u32 cause;
255167514Skmacy	int cphy_cause = 0;
256167514Skmacy
257167514Skmacy	mdio_read(cphy, 0, MV88E1XXX_INTR_STATUS, &cause);
258167514Skmacy	cause &= INTR_ENABLE_MASK;
259167514Skmacy	if (cause & link_change_intrs)
260167514Skmacy		cphy_cause |= cphy_cause_link_change;
261167514Skmacy	if (cause & MV_INTR_FIFO_OVER_UNDER)
262167514Skmacy		cphy_cause |= cphy_cause_fifo_error;
263167514Skmacy	return cphy_cause;
264167514Skmacy}
265167514Skmacy
266167514Skmacy#ifdef C99_NOT_SUPPORTED
267167514Skmacystatic struct cphy_ops mv88e1xxx_ops = {
268167514Skmacy	mv88e1xxx_reset,
269167514Skmacy	mv88e1xxx_intr_enable,
270167514Skmacy	mv88e1xxx_intr_disable,
271167514Skmacy	mv88e1xxx_intr_clear,
272167514Skmacy	mv88e1xxx_intr_handler,
273167514Skmacy	mv88e1xxx_autoneg_enable,
274167514Skmacy	mv88e1xxx_autoneg_restart,
275167514Skmacy	t3_phy_advertise,
276167514Skmacy	mv88e1xxx_set_loopback,
277176472Skmacy	mv88e1xxx_set_speed_duplex,
278167514Skmacy	mv88e1xxx_get_link_status,
279167514Skmacy	mv88e1xxx_power_down,
280167514Skmacy};
281167514Skmacy#else
282167514Skmacystatic struct cphy_ops mv88e1xxx_ops = {
283167514Skmacy	.reset             = mv88e1xxx_reset,
284167514Skmacy	.intr_enable       = mv88e1xxx_intr_enable,
285167514Skmacy	.intr_disable      = mv88e1xxx_intr_disable,
286167514Skmacy	.intr_clear        = mv88e1xxx_intr_clear,
287167514Skmacy	.intr_handler      = mv88e1xxx_intr_handler,
288167514Skmacy	.autoneg_enable    = mv88e1xxx_autoneg_enable,
289167514Skmacy	.autoneg_restart   = mv88e1xxx_autoneg_restart,
290167514Skmacy	.advertise         = t3_phy_advertise,
291167514Skmacy	.set_loopback      = mv88e1xxx_set_loopback,
292176472Skmacy	.set_speed_duplex  = mv88e1xxx_set_speed_duplex,
293167514Skmacy	.get_link_status   = mv88e1xxx_get_link_status,
294167514Skmacy	.power_down        = mv88e1xxx_power_down,
295167514Skmacy};
296167514Skmacy#endif
297167514Skmacy
298197791Snpint t3_mv88e1xxx_phy_prep(pinfo_t *pinfo, int phy_addr,
299180583Skmacy			  const struct mdio_ops *mdio_ops)
300167514Skmacy{
301197791Snp	struct cphy *phy = &pinfo->phy;
302176472Skmacy	int err;
303167514Skmacy
304197791Snp	cphy_init(phy, pinfo->adapter, pinfo, phy_addr, &mv88e1xxx_ops, mdio_ops,
305176472Skmacy		  SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Full |
306176472Skmacy		  SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_MII |
307176472Skmacy		  SUPPORTED_TP | SUPPORTED_IRQ, "10/100/1000BASE-T");
308176472Skmacy
309167514Skmacy	/* Configure copper PHY transmitter as class A to reduce EMI. */
310176472Skmacy	err = mdio_write(phy, 0, MV88E1XXX_EXTENDED_ADDR, 0xb);
311176472Skmacy	if (!err)
312176472Skmacy		err = mdio_write(phy, 0, MV88E1XXX_EXTENDED_DATA, 0x8004);
313180583Skmacy
314176472Skmacy	if (!err)
315176472Skmacy		err = mv88e1xxx_downshift_set(phy, 1);   /* Enable downshift */
316176472Skmacy	return err;
317167514Skmacy}
318