Deleted Added
full compact
cxgb_mv88e1xxx.c (167514) cxgb_mv88e1xxx.c (170076)
1/**************************************************************************
2
3Copyright (c) 2007, Chelsio Inc.
4All rights reserved.
5
6Redistribution and use in source and binary forms, with or without
7modification, 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
1/**************************************************************************
2
3Copyright (c) 2007, Chelsio Inc.
4All rights reserved.
5
6Redistribution and use in source and binary forms, with or without
7modification, 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. Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
15
16 3. Neither the name of the Chelsio Corporation nor the names of its
12 2. Neither the name of the Chelsio Corporation nor the names of its
17 contributors may be used to endorse or promote products derived from
18 this software without specific prior written permission.
19
20THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30POSSIBILITY OF SUCH DAMAGE.
31
32***************************************************************************/
33
34#include <sys/cdefs.h>
13 contributors may be used to endorse or promote products derived from
14 this software without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26POSSIBILITY OF SUCH DAMAGE.
27
28***************************************************************************/
29
30#include <sys/cdefs.h>
35__FBSDID("$FreeBSD: head/sys/dev/cxgb/common/cxgb_mv88e1xxx.c 167514 2007-03-14 02:37:44Z kmacy $");
31__FBSDID("$FreeBSD: head/sys/dev/cxgb/common/cxgb_mv88e1xxx.c 170076 2007-05-28 22:57:27Z kmacy $");
36
32
37#include <dev/cxgb/common/cxgb_common.h>
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 */
141static int mv88e1xxx_reset(struct cphy *cphy, int wait)
142{
143 return t3_phy_reset(cphy, 0, wait);
144}
145
146static int mv88e1xxx_intr_enable(struct cphy *cphy)
147{
148 return mdio_write(cphy, 0, MV88E1XXX_INTR_ENABLE, INTR_ENABLE_MASK);
149}
150
151static int mv88e1xxx_intr_disable(struct cphy *cphy)
152{
153 return mdio_write(cphy, 0, MV88E1XXX_INTR_ENABLE, 0);
154}
155
156static 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
164static 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
171static 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
180static 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
186static 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
192static 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
224static 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
235static 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
241static 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
260static 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
276static 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
292void 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}
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 */
141static int mv88e1xxx_reset(struct cphy *cphy, int wait)
142{
143 return t3_phy_reset(cphy, 0, wait);
144}
145
146static int mv88e1xxx_intr_enable(struct cphy *cphy)
147{
148 return mdio_write(cphy, 0, MV88E1XXX_INTR_ENABLE, INTR_ENABLE_MASK);
149}
150
151static int mv88e1xxx_intr_disable(struct cphy *cphy)
152{
153 return mdio_write(cphy, 0, MV88E1XXX_INTR_ENABLE, 0);
154}
155
156static 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
164static 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
171static 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
180static 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
186static 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
192static 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
224static 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
235static 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
241static 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
260static 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
276static 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
292void 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}