2 * Copyright (c) 2005-2007 Chelsio, Inc. All rights reserved.
4 * This software is available to you under a choice of one of two
5 * licenses. You may choose to be licensed under the terms of the GNU
6 * General Public License (GPL) Version 2, available from the file
7 * COPYING in the main directory of this source tree, or the
8 * OpenIB.org BSD license below:
10 * Redistribution and use in source and binary forms, with or
11 * without modification, are permitted provided that the following
14 * - Redistributions of source code must retain the above
15 * copyright notice, this list of conditions and the following
18 * - Redistributions in binary form must reproduce the above
19 * copyright notice, this list of conditions and the following
20 * disclaimer in the documentation and/or other materials
21 * provided with the distribution.
23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
34 /* VSC8211 PHY specific registers. */
36 VSC8211_INTR_ENABLE
= 25,
37 VSC8211_INTR_STATUS
= 26,
38 VSC8211_AUX_CTRL_STAT
= 28,
42 VSC_INTR_RX_ERR
= 1 << 0,
43 VSC_INTR_MS_ERR
= 1 << 1, /* master/slave resolution error */
44 VSC_INTR_CABLE
= 1 << 2, /* cable impairment */
45 VSC_INTR_FALSE_CARR
= 1 << 3, /* false carrier */
46 VSC_INTR_MEDIA_CHG
= 1 << 4, /* AMS media change */
47 VSC_INTR_RX_FIFO
= 1 << 5, /* Rx FIFO over/underflow */
48 VSC_INTR_TX_FIFO
= 1 << 6, /* Tx FIFO over/underflow */
49 VSC_INTR_DESCRAMBL
= 1 << 7, /* descrambler lock-lost */
50 VSC_INTR_SYMBOL_ERR
= 1 << 8, /* symbol error */
51 VSC_INTR_NEG_DONE
= 1 << 10, /* autoneg done */
52 VSC_INTR_NEG_ERR
= 1 << 11, /* autoneg error */
53 VSC_INTR_LINK_CHG
= 1 << 13, /* link change */
54 VSC_INTR_ENABLE
= 1 << 15, /* interrupt enable */
57 #define CFG_CHG_INTR_MASK (VSC_INTR_LINK_CHG | VSC_INTR_NEG_ERR | \
59 #define INTR_MASK (CFG_CHG_INTR_MASK | VSC_INTR_TX_FIFO | VSC_INTR_RX_FIFO | \
62 /* PHY specific auxiliary control & status register fields */
63 #define S_ACSR_ACTIPHY_TMR 0
64 #define M_ACSR_ACTIPHY_TMR 0x3
65 #define V_ACSR_ACTIPHY_TMR(x) ((x) << S_ACSR_ACTIPHY_TMR)
67 #define S_ACSR_SPEED 3
68 #define M_ACSR_SPEED 0x3
69 #define G_ACSR_SPEED(x) (((x) >> S_ACSR_SPEED) & M_ACSR_SPEED)
71 #define S_ACSR_DUPLEX 5
72 #define F_ACSR_DUPLEX (1 << S_ACSR_DUPLEX)
74 #define S_ACSR_ACTIPHY 6
75 #define F_ACSR_ACTIPHY (1 << S_ACSR_ACTIPHY)
78 * Reset the PHY. This PHY completes reset immediately so we never wait.
80 static int vsc8211_reset(struct cphy
*cphy
, int wait
)
82 return t3_phy_reset(cphy
, 0, 0);
85 static int vsc8211_intr_enable(struct cphy
*cphy
)
87 return mdio_write(cphy
, 0, VSC8211_INTR_ENABLE
, INTR_MASK
);
90 static int vsc8211_intr_disable(struct cphy
*cphy
)
92 return mdio_write(cphy
, 0, VSC8211_INTR_ENABLE
, 0);
95 static int vsc8211_intr_clear(struct cphy
*cphy
)
99 /* Clear PHY interrupts by reading the register. */
100 return mdio_read(cphy
, 0, VSC8211_INTR_STATUS
, &val
);
103 static int vsc8211_autoneg_enable(struct cphy
*cphy
)
105 return t3_mdio_change_bits(cphy
, 0, MII_BMCR
, BMCR_PDOWN
| BMCR_ISOLATE
,
106 BMCR_ANENABLE
| BMCR_ANRESTART
);
109 static int vsc8211_autoneg_restart(struct cphy
*cphy
)
111 return t3_mdio_change_bits(cphy
, 0, MII_BMCR
, BMCR_PDOWN
| BMCR_ISOLATE
,
115 static int vsc8211_get_link_status(struct cphy
*cphy
, int *link_ok
,
116 int *speed
, int *duplex
, int *fc
)
118 unsigned int bmcr
, status
, lpa
, adv
;
119 int err
, sp
= -1, dplx
= -1, pause
= 0;
121 err
= mdio_read(cphy
, 0, MII_BMCR
, &bmcr
);
123 err
= mdio_read(cphy
, 0, MII_BMSR
, &status
);
129 * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
130 * once more to get the current link state.
132 if (!(status
& BMSR_LSTATUS
))
133 err
= mdio_read(cphy
, 0, MII_BMSR
, &status
);
136 *link_ok
= (status
& BMSR_LSTATUS
) != 0;
138 if (!(bmcr
& BMCR_ANENABLE
)) {
139 dplx
= (bmcr
& BMCR_FULLDPLX
) ? DUPLEX_FULL
: DUPLEX_HALF
;
140 if (bmcr
& BMCR_SPEED1000
)
142 else if (bmcr
& BMCR_SPEED100
)
146 } else if (status
& BMSR_ANEGCOMPLETE
) {
147 err
= mdio_read(cphy
, 0, VSC8211_AUX_CTRL_STAT
, &status
);
151 dplx
= (status
& F_ACSR_DUPLEX
) ? DUPLEX_FULL
: DUPLEX_HALF
;
152 sp
= G_ACSR_SPEED(status
);
160 if (fc
&& dplx
== DUPLEX_FULL
) {
161 err
= mdio_read(cphy
, 0, MII_LPA
, &lpa
);
163 err
= mdio_read(cphy
, 0, MII_ADVERTISE
, &adv
);
167 if (lpa
& adv
& ADVERTISE_PAUSE_CAP
)
168 pause
= PAUSE_RX
| PAUSE_TX
;
169 else if ((lpa
& ADVERTISE_PAUSE_CAP
) &&
170 (lpa
& ADVERTISE_PAUSE_ASYM
) &&
171 (adv
& ADVERTISE_PAUSE_ASYM
))
173 else if ((lpa
& ADVERTISE_PAUSE_ASYM
) &&
174 (adv
& ADVERTISE_PAUSE_CAP
))
187 static int vsc8211_power_down(struct cphy
*cphy
, int enable
)
189 return t3_mdio_change_bits(cphy
, 0, MII_BMCR
, BMCR_PDOWN
,
190 enable
? BMCR_PDOWN
: 0);
193 static int vsc8211_intr_handler(struct cphy
*cphy
)
196 int err
, cphy_cause
= 0;
198 err
= mdio_read(cphy
, 0, VSC8211_INTR_STATUS
, &cause
);
203 if (cause
& CFG_CHG_INTR_MASK
)
204 cphy_cause
|= cphy_cause_link_change
;
205 if (cause
& (VSC_INTR_RX_FIFO
| VSC_INTR_TX_FIFO
))
206 cphy_cause
|= cphy_cause_fifo_error
;
210 static struct cphy_ops vsc8211_ops
= {
211 .reset
= vsc8211_reset
,
212 .intr_enable
= vsc8211_intr_enable
,
213 .intr_disable
= vsc8211_intr_disable
,
214 .intr_clear
= vsc8211_intr_clear
,
215 .intr_handler
= vsc8211_intr_handler
,
216 .autoneg_enable
= vsc8211_autoneg_enable
,
217 .autoneg_restart
= vsc8211_autoneg_restart
,
218 .advertise
= t3_phy_advertise
,
219 .set_speed_duplex
= t3_set_phy_speed_duplex
,
220 .get_link_status
= vsc8211_get_link_status
,
221 .power_down
= vsc8211_power_down
,
224 void t3_vsc8211_phy_prep(struct cphy
*phy
, struct adapter
*adapter
,
225 int phy_addr
, const struct mdio_ops
*mdio_ops
)
227 cphy_init(phy
, adapter
, phy_addr
, &vsc8211_ops
, mdio_ops
);