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