xref: /freebsd/sys/dev/cxgb/common/cxgb_mv88e1xxx.c (revision 0957b409)
1 /**************************************************************************
2 SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 
4 Copyright (c) 2007, Chelsio Inc.
5 All rights reserved.
6 
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9 
10  1. Redistributions of source code must retain the above copyright notice,
11     this list of conditions and the following disclaimer.
12 
13  2. Neither the name of the Chelsio Corporation nor the names of its
14     contributors may be used to endorse or promote products derived from
15     this software without specific prior written permission.
16 
17 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 POSSIBILITY OF SUCH DAMAGE.
28 
29 ***************************************************************************/
30 
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33 
34 #include <cxgb_include.h>
35 
36 /* Marvell PHY interrupt status bits. */
37 #define MV_INTR_JABBER          0x0001
38 #define MV_INTR_POLARITY_CHNG   0x0002
39 #define MV_INTR_ENG_DETECT_CHNG 0x0010
40 #define MV_INTR_DOWNSHIFT       0x0020
41 #define MV_INTR_MDI_XOVER_CHNG  0x0040
42 #define MV_INTR_FIFO_OVER_UNDER 0x0080
43 #define MV_INTR_FALSE_CARRIER   0x0100
44 #define MV_INTR_SYMBOL_ERROR    0x0200
45 #define MV_INTR_LINK_CHNG       0x0400
46 #define MV_INTR_AUTONEG_DONE    0x0800
47 #define MV_INTR_PAGE_RECV       0x1000
48 #define MV_INTR_DUPLEX_CHNG     0x2000
49 #define MV_INTR_SPEED_CHNG      0x4000
50 #define MV_INTR_AUTONEG_ERR     0x8000
51 
52 /* Marvell PHY specific registers. */
53 #define MV88E1XXX_SPECIFIC_CNTRL          16
54 #define MV88E1XXX_SPECIFIC_STATUS         17
55 #define MV88E1XXX_INTR_ENABLE             18
56 #define MV88E1XXX_INTR_STATUS             19
57 #define MV88E1XXX_EXT_SPECIFIC_CNTRL      20
58 #define MV88E1XXX_RECV_ERR                21
59 #define MV88E1XXX_EXT_ADDR                22
60 #define MV88E1XXX_GLOBAL_STATUS           23
61 #define MV88E1XXX_LED_CNTRL               24
62 #define MV88E1XXX_LED_OVERRIDE            25
63 #define MV88E1XXX_EXT_SPECIFIC_CNTRL2     26
64 #define MV88E1XXX_EXT_SPECIFIC_STATUS     27
65 #define MV88E1XXX_VIRTUAL_CABLE_TESTER    28
66 #define MV88E1XXX_EXTENDED_ADDR           29
67 #define MV88E1XXX_EXTENDED_DATA           30
68 
69 /* PHY specific control register fields */
70 #define S_PSCR_MDI_XOVER_MODE    5
71 #define M_PSCR_MDI_XOVER_MODE    0x3
72 #define V_PSCR_MDI_XOVER_MODE(x) ((x) << S_PSCR_MDI_XOVER_MODE)
73 
74 /* Extended PHY specific control register fields */
75 #define S_DOWNSHIFT_ENABLE 8
76 #define V_DOWNSHIFT_ENABLE (1 << S_DOWNSHIFT_ENABLE)
77 
78 #define S_DOWNSHIFT_CNT    9
79 #define M_DOWNSHIFT_CNT    0x7
80 #define V_DOWNSHIFT_CNT(x) ((x) << S_DOWNSHIFT_CNT)
81 
82 /* PHY specific status register fields */
83 #define S_PSSR_JABBER 0
84 #define V_PSSR_JABBER (1 << S_PSSR_JABBER)
85 
86 #define S_PSSR_POLARITY 1
87 #define V_PSSR_POLARITY (1 << S_PSSR_POLARITY)
88 
89 #define S_PSSR_RX_PAUSE 2
90 #define V_PSSR_RX_PAUSE (1 << S_PSSR_RX_PAUSE)
91 
92 #define S_PSSR_TX_PAUSE 3
93 #define V_PSSR_TX_PAUSE (1 << S_PSSR_TX_PAUSE)
94 
95 #define S_PSSR_ENERGY_DETECT 4
96 #define V_PSSR_ENERGY_DETECT (1 << S_PSSR_ENERGY_DETECT)
97 
98 #define S_PSSR_DOWNSHIFT_STATUS 5
99 #define V_PSSR_DOWNSHIFT_STATUS (1 << S_PSSR_DOWNSHIFT_STATUS)
100 
101 #define S_PSSR_MDI 6
102 #define V_PSSR_MDI (1 << S_PSSR_MDI)
103 
104 #define S_PSSR_CABLE_LEN    7
105 #define M_PSSR_CABLE_LEN    0x7
106 #define V_PSSR_CABLE_LEN(x) ((x) << S_PSSR_CABLE_LEN)
107 #define G_PSSR_CABLE_LEN(x) (((x) >> S_PSSR_CABLE_LEN) & M_PSSR_CABLE_LEN)
108 
109 #define S_PSSR_LINK 10
110 #define V_PSSR_LINK (1 << S_PSSR_LINK)
111 
112 #define S_PSSR_STATUS_RESOLVED 11
113 #define V_PSSR_STATUS_RESOLVED (1 << S_PSSR_STATUS_RESOLVED)
114 
115 #define S_PSSR_PAGE_RECEIVED 12
116 #define V_PSSR_PAGE_RECEIVED (1 << S_PSSR_PAGE_RECEIVED)
117 
118 #define S_PSSR_DUPLEX 13
119 #define V_PSSR_DUPLEX (1 << S_PSSR_DUPLEX)
120 
121 #define S_PSSR_SPEED    14
122 #define M_PSSR_SPEED    0x3
123 #define V_PSSR_SPEED(x) ((x) << S_PSSR_SPEED)
124 #define G_PSSR_SPEED(x) (((x) >> S_PSSR_SPEED) & M_PSSR_SPEED)
125 
126 /* MV88E1XXX MDI crossover register values */
127 #define CROSSOVER_MDI   0
128 #define CROSSOVER_MDIX  1
129 #define CROSSOVER_AUTO  3
130 
131 #define INTR_ENABLE_MASK (MV_INTR_SPEED_CHNG | MV_INTR_DUPLEX_CHNG | \
132 	MV_INTR_AUTONEG_DONE | MV_INTR_LINK_CHNG | MV_INTR_FIFO_OVER_UNDER | \
133 	MV_INTR_ENG_DETECT_CHNG)
134 
135 /*
136  * Reset the PHY.  If 'wait' is set wait until the reset completes.
137  */
138 static int mv88e1xxx_reset(struct cphy *cphy, int wait)
139 {
140 	return t3_phy_reset(cphy, 0, wait);
141 }
142 
143 static int mv88e1xxx_intr_enable(struct cphy *cphy)
144 {
145 	return mdio_write(cphy, 0, MV88E1XXX_INTR_ENABLE, INTR_ENABLE_MASK);
146 }
147 
148 static int mv88e1xxx_intr_disable(struct cphy *cphy)
149 {
150 	return mdio_write(cphy, 0, MV88E1XXX_INTR_ENABLE, 0);
151 }
152 
153 static int mv88e1xxx_intr_clear(struct cphy *cphy)
154 {
155 	u32 val;
156 
157 	/* Clear PHY interrupts by reading the register. */
158 	return mdio_read(cphy, 0, MV88E1XXX_INTR_STATUS, &val);
159 }
160 
161 static int mv88e1xxx_crossover_set(struct cphy *cphy, int crossover)
162 {
163 	return t3_mdio_change_bits(cphy, 0, MV88E1XXX_SPECIFIC_CNTRL,
164 				   V_PSCR_MDI_XOVER_MODE(M_PSCR_MDI_XOVER_MODE),
165 				   V_PSCR_MDI_XOVER_MODE(crossover));
166 }
167 
168 static int mv88e1xxx_autoneg_enable(struct cphy *cphy)
169 {
170 	mv88e1xxx_crossover_set(cphy, CROSSOVER_AUTO);
171 
172 	/* restart autoneg for change to take effect */
173 	return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE,
174 			 	   BMCR_ANENABLE | BMCR_ANRESTART);
175 }
176 
177 static int mv88e1xxx_autoneg_restart(struct cphy *cphy)
178 {
179 	return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE,
180 			 	   BMCR_ANRESTART);
181 }
182 
183 static int mv88e1xxx_set_loopback(struct cphy *cphy, int mmd, int dir, int on)
184 {
185 	return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_LOOPBACK,
186 			 	   on ? BMCR_LOOPBACK : 0);
187 }
188 
189 static int mv88e1xxx_get_link_status(struct cphy *cphy, int *link_state,
190 				     int *speed, int *duplex, int *fc)
191 {
192 	u32 status;
193 	int sp = -1, dplx = -1, pause = 0;
194 
195 	mdio_read(cphy, 0, MV88E1XXX_SPECIFIC_STATUS, &status);
196 	if ((status & V_PSSR_STATUS_RESOLVED) != 0) {
197 		if (status & V_PSSR_RX_PAUSE)
198 			pause |= PAUSE_RX;
199 		if (status & V_PSSR_TX_PAUSE)
200 			pause |= PAUSE_TX;
201 		dplx = (status & V_PSSR_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF;
202 		sp = G_PSSR_SPEED(status);
203 		if (sp == 0)
204 			sp = SPEED_10;
205 		else if (sp == 1)
206 			sp = SPEED_100;
207 		else
208 			sp = SPEED_1000;
209 	}
210 	if (link_state)
211 		*link_state = status & V_PSSR_LINK ? PHY_LINK_UP :
212 		    PHY_LINK_DOWN;
213 	if (speed)
214 		*speed = sp;
215 	if (duplex)
216 		*duplex = dplx;
217 	if (fc)
218 		*fc = pause;
219 	return 0;
220 }
221 
222 static int mv88e1xxx_set_speed_duplex(struct cphy *phy, int speed, int duplex)
223 {
224 	int err = t3_set_phy_speed_duplex(phy, speed, duplex);
225 
226 	/* PHY needs reset for new settings to take effect */
227 	if (!err)
228 		err = mv88e1xxx_reset(phy, 0);
229 	return err;
230 }
231 
232 static int mv88e1xxx_downshift_set(struct cphy *cphy, int downshift_enable)
233 {
234 	/*
235 	 * Set the downshift counter to 2 so we try to establish Gb link
236 	 * twice before downshifting.
237 	 */
238 	return t3_mdio_change_bits(cphy, 0, MV88E1XXX_EXT_SPECIFIC_CNTRL,
239 		V_DOWNSHIFT_ENABLE | V_DOWNSHIFT_CNT(M_DOWNSHIFT_CNT),
240 		downshift_enable ? V_DOWNSHIFT_ENABLE | V_DOWNSHIFT_CNT(2) : 0);
241 }
242 
243 static int mv88e1xxx_power_down(struct cphy *cphy, int enable)
244 {
245 	return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN,
246 				   enable ? BMCR_PDOWN : 0);
247 }
248 
249 static int mv88e1xxx_intr_handler(struct cphy *cphy)
250 {
251 	const u32 link_change_intrs = MV_INTR_LINK_CHNG |
252 		MV_INTR_AUTONEG_DONE | MV_INTR_DUPLEX_CHNG |
253 		MV_INTR_SPEED_CHNG | MV_INTR_DOWNSHIFT;
254 
255 	u32 cause;
256 	int cphy_cause = 0;
257 
258 	mdio_read(cphy, 0, MV88E1XXX_INTR_STATUS, &cause);
259 	cause &= INTR_ENABLE_MASK;
260 	if (cause & link_change_intrs)
261 		cphy_cause |= cphy_cause_link_change;
262 	if (cause & MV_INTR_FIFO_OVER_UNDER)
263 		cphy_cause |= cphy_cause_fifo_error;
264 	return cphy_cause;
265 }
266 
267 #ifdef C99_NOT_SUPPORTED
268 static struct cphy_ops mv88e1xxx_ops = {
269 	mv88e1xxx_reset,
270 	mv88e1xxx_intr_enable,
271 	mv88e1xxx_intr_disable,
272 	mv88e1xxx_intr_clear,
273 	mv88e1xxx_intr_handler,
274 	mv88e1xxx_autoneg_enable,
275 	mv88e1xxx_autoneg_restart,
276 	t3_phy_advertise,
277 	mv88e1xxx_set_loopback,
278 	mv88e1xxx_set_speed_duplex,
279 	mv88e1xxx_get_link_status,
280 	mv88e1xxx_power_down,
281 };
282 #else
283 static struct cphy_ops mv88e1xxx_ops = {
284 	.reset             = mv88e1xxx_reset,
285 	.intr_enable       = mv88e1xxx_intr_enable,
286 	.intr_disable      = mv88e1xxx_intr_disable,
287 	.intr_clear        = mv88e1xxx_intr_clear,
288 	.intr_handler      = mv88e1xxx_intr_handler,
289 	.autoneg_enable    = mv88e1xxx_autoneg_enable,
290 	.autoneg_restart   = mv88e1xxx_autoneg_restart,
291 	.advertise         = t3_phy_advertise,
292 	.set_loopback      = mv88e1xxx_set_loopback,
293 	.set_speed_duplex  = mv88e1xxx_set_speed_duplex,
294 	.get_link_status   = mv88e1xxx_get_link_status,
295 	.power_down        = mv88e1xxx_power_down,
296 };
297 #endif
298 
299 int t3_mv88e1xxx_phy_prep(pinfo_t *pinfo, int phy_addr,
300 			  const struct mdio_ops *mdio_ops)
301 {
302 	struct cphy *phy = &pinfo->phy;
303 	int err;
304 
305 	cphy_init(phy, pinfo->adapter, pinfo, phy_addr, &mv88e1xxx_ops, mdio_ops,
306 		  SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Full |
307 		  SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_MII |
308 		  SUPPORTED_TP | SUPPORTED_IRQ, "10/100/1000BASE-T");
309 
310 	/* Configure copper PHY transmitter as class A to reduce EMI. */
311 	err = mdio_write(phy, 0, MV88E1XXX_EXTENDED_ADDR, 0xb);
312 	if (!err)
313 		err = mdio_write(phy, 0, MV88E1XXX_EXTENDED_DATA, 0x8004);
314 
315 	if (!err)
316 		err = mv88e1xxx_downshift_set(phy, 1);   /* Enable downshift */
317 	return err;
318 }
319