1 /**************************************************************************
3 Copyright (c) 2007, Chelsio Inc.
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are met:
9 1. Redistributions of source code must retain the above copyright notice,
10 this list of conditions and the following disclaimer.
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.
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.
28 ***************************************************************************/
30 #include <sys/cdefs.h>
32 __KERNEL_RCSID(0, "$NetBSD: cxgb_vsc8211.c,v 1.2 2007/12/11 11:25:50 lukem Exp $");
35 __FBSDID("$FreeBSD: src/sys/dev/cxgb/common/cxgb_vsc8211.c,v 1.3 2007/08/25 21:07:37 kmacy Exp $");
39 #include <cxgb_include.h>
42 #include <dev/cxgb/cxgb_include.h>
45 #include <dev/pci/cxgb_include.h>
49 /* VSC8211 PHY specific registers. */
51 VSC8211_INTR_ENABLE
= 25,
52 VSC8211_INTR_STATUS
= 26,
53 VSC8211_AUX_CTRL_STAT
= 28,
57 VSC_INTR_RX_ERR
= 1 << 0,
58 VSC_INTR_MS_ERR
= 1 << 1, /* master/slave resolution error */
59 VSC_INTR_CABLE
= 1 << 2, /* cable impairment */
60 VSC_INTR_FALSE_CARR
= 1 << 3, /* false carrier */
61 VSC_INTR_MEDIA_CHG
= 1 << 4, /* AMS media change */
62 VSC_INTR_RX_FIFO
= 1 << 5, /* Rx FIFO over/underflow */
63 VSC_INTR_TX_FIFO
= 1 << 6, /* Tx FIFO over/underflow */
64 VSC_INTR_DESCRAMBL
= 1 << 7, /* descrambler lock-lost */
65 VSC_INTR_SYMBOL_ERR
= 1 << 8, /* symbol error */
66 VSC_INTR_NEG_DONE
= 1 << 10, /* autoneg done */
67 VSC_INTR_NEG_ERR
= 1 << 11, /* autoneg error */
68 VSC_INTR_LINK_CHG
= 1 << 13, /* link change */
69 VSC_INTR_ENABLE
= 1 << 15, /* interrupt enable */
72 #define CFG_CHG_INTR_MASK (VSC_INTR_LINK_CHG | VSC_INTR_NEG_ERR | \
74 #define INTR_MASK (CFG_CHG_INTR_MASK | VSC_INTR_TX_FIFO | VSC_INTR_RX_FIFO | \
77 /* PHY specific auxiliary control & status register fields */
78 #define S_ACSR_ACTIPHY_TMR 0
79 #define M_ACSR_ACTIPHY_TMR 0x3
80 #define V_ACSR_ACTIPHY_TMR(x) ((x) << S_ACSR_ACTIPHY_TMR)
82 #define S_ACSR_SPEED 3
83 #define M_ACSR_SPEED 0x3
84 #define G_ACSR_SPEED(x) (((x) >> S_ACSR_SPEED) & M_ACSR_SPEED)
86 #define S_ACSR_DUPLEX 5
87 #define F_ACSR_DUPLEX (1 << S_ACSR_DUPLEX)
89 #define S_ACSR_ACTIPHY 6
90 #define F_ACSR_ACTIPHY (1 << S_ACSR_ACTIPHY)
93 * Reset the PHY. This PHY completes reset immediately so we never wait.
95 static int vsc8211_reset(struct cphy
*cphy
, int wait
)
97 return t3_phy_reset(cphy
, 0, 0);
100 static int vsc8211_intr_enable(struct cphy
*cphy
)
102 return mdio_write(cphy
, 0, VSC8211_INTR_ENABLE
, INTR_MASK
);
105 static int vsc8211_intr_disable(struct cphy
*cphy
)
107 return mdio_write(cphy
, 0, VSC8211_INTR_ENABLE
, 0);
110 static int vsc8211_intr_clear(struct cphy
*cphy
)
114 /* Clear PHY interrupts by reading the register. */
115 return mdio_read(cphy
, 0, VSC8211_INTR_STATUS
, &val
);
118 static int vsc8211_autoneg_enable(struct cphy
*cphy
)
120 return t3_mdio_change_bits(cphy
, 0, MII_BMCR
, BMCR_PDOWN
| BMCR_ISOLATE
,
121 BMCR_ANENABLE
| BMCR_ANRESTART
);
124 static int vsc8211_autoneg_restart(struct cphy
*cphy
)
126 return t3_mdio_change_bits(cphy
, 0, MII_BMCR
, BMCR_PDOWN
| BMCR_ISOLATE
,
130 static int vsc8211_get_link_status(struct cphy
*cphy
, int *link_ok
,
131 int *speed
, int *duplex
, int *fc
)
133 unsigned int bmcr
, status
, lpa
, adv
;
134 int err
, sp
= -1, dplx
= -1, pause
= 0;
136 err
= mdio_read(cphy
, 0, MII_BMCR
, &bmcr
);
138 err
= mdio_read(cphy
, 0, MII_BMSR
, &status
);
144 * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
145 * once more to get the current link state.
147 if (!(status
& BMSR_LSTATUS
))
148 err
= mdio_read(cphy
, 0, MII_BMSR
, &status
);
151 *link_ok
= (status
& BMSR_LSTATUS
) != 0;
153 if (!(bmcr
& BMCR_ANENABLE
)) {
154 dplx
= (bmcr
& BMCR_FULLDPLX
) ? DUPLEX_FULL
: DUPLEX_HALF
;
155 if (bmcr
& BMCR_SPEED1000
)
157 else if (bmcr
& BMCR_SPEED100
)
161 } else if (status
& BMSR_ANEGCOMPLETE
) {
162 err
= mdio_read(cphy
, 0, VSC8211_AUX_CTRL_STAT
, &status
);
166 dplx
= (status
& F_ACSR_DUPLEX
) ? DUPLEX_FULL
: DUPLEX_HALF
;
167 sp
= G_ACSR_SPEED(status
);
175 if (fc
&& dplx
== DUPLEX_FULL
) {
176 err
= mdio_read(cphy
, 0, MII_LPA
, &lpa
);
178 err
= mdio_read(cphy
, 0, MII_ADVERTISE
, &adv
);
182 if (lpa
& adv
& ADVERTISE_PAUSE_CAP
)
183 pause
= PAUSE_RX
| PAUSE_TX
;
184 else if ((lpa
& ADVERTISE_PAUSE_CAP
) &&
185 (lpa
& ADVERTISE_PAUSE_ASYM
) &&
186 (adv
& ADVERTISE_PAUSE_ASYM
))
188 else if ((lpa
& ADVERTISE_PAUSE_ASYM
) &&
189 (adv
& ADVERTISE_PAUSE_CAP
))
202 static int vsc8211_power_down(struct cphy
*cphy
, int enable
)
204 return t3_mdio_change_bits(cphy
, 0, MII_BMCR
, BMCR_PDOWN
,
205 enable
? BMCR_PDOWN
: 0);
208 static int vsc8211_intr_handler(struct cphy
*cphy
)
211 int err
, cphy_cause
= 0;
213 err
= mdio_read(cphy
, 0, VSC8211_INTR_STATUS
, &cause
);
218 if (cause
& CFG_CHG_INTR_MASK
)
219 cphy_cause
|= cphy_cause_link_change
;
220 if (cause
& (VSC_INTR_RX_FIFO
| VSC_INTR_TX_FIFO
))
221 cphy_cause
|= cphy_cause_fifo_error
;
225 #ifdef C99_NOT_SUPPORTED
226 static struct cphy_ops vsc8211_ops
= {
230 vsc8211_intr_disable
,
232 vsc8211_intr_handler
,
233 vsc8211_autoneg_enable
,
234 vsc8211_autoneg_restart
,
237 t3_set_phy_speed_duplex
,
238 vsc8211_get_link_status
,
242 static struct cphy_ops vsc8211_ops
= {
243 .reset
= vsc8211_reset
,
244 .intr_enable
= vsc8211_intr_enable
,
245 .intr_disable
= vsc8211_intr_disable
,
246 .intr_clear
= vsc8211_intr_clear
,
247 .intr_handler
= vsc8211_intr_handler
,
248 .autoneg_enable
= vsc8211_autoneg_enable
,
249 .autoneg_restart
= vsc8211_autoneg_restart
,
250 .advertise
= t3_phy_advertise
,
251 .set_speed_duplex
= t3_set_phy_speed_duplex
,
252 .get_link_status
= vsc8211_get_link_status
,
253 .power_down
= vsc8211_power_down
,
257 void t3_vsc8211_phy_prep(struct cphy
*phy
, adapter_t
*adapter
, int phy_addr
,
258 const struct mdio_ops
*mdio_ops
)
260 cphy_init(phy
, adapter
, phy_addr
, &vsc8211_ops
, mdio_ops
);
261 t3_os_sleep(20); /* PHY needs ~10ms to start responding to MDIO */