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 __KERNEL_RCSID(0, "$NetBSD: cxgb_vsc8211.c,v 1.1 2010/03/21 21:11:13 jklos Exp $");
32 
33 #ifdef CONFIG_DEFINED
34 #include <cxgb_include.h>
35 #else
36 #include <dev/pci/cxgb/cxgb_include.h>
37 #endif
38 
39 /* VSC8211 PHY specific registers. */
40 enum {
41     VSC8211_INTR_ENABLE   = 25,
42     VSC8211_INTR_STATUS   = 26,
43     VSC8211_AUX_CTRL_STAT = 28,
44 };
45 
46 enum {
47     VSC_INTR_RX_ERR     = 1 << 0,
48     VSC_INTR_MS_ERR     = 1 << 1,  /* master/slave resolution error */
49     VSC_INTR_CABLE      = 1 << 2,  /* cable impairment */
50     VSC_INTR_FALSE_CARR = 1 << 3,  /* false carrier */
51     VSC_INTR_MEDIA_CHG  = 1 << 4,  /* AMS media change */
52     VSC_INTR_RX_FIFO    = 1 << 5,  /* Rx FIFO over/underflow */
53     VSC_INTR_TX_FIFO    = 1 << 6,  /* Tx FIFO over/underflow */
54     VSC_INTR_DESCRAMBL  = 1 << 7,  /* descrambler lock-lost */
55     VSC_INTR_SYMBOL_ERR = 1 << 8,  /* symbol error */
56     VSC_INTR_NEG_DONE   = 1 << 10, /* autoneg done */
57     VSC_INTR_NEG_ERR    = 1 << 11, /* autoneg error */
58     VSC_INTR_LINK_CHG   = 1 << 13, /* link change */
59     VSC_INTR_ENABLE     = 1 << 15, /* interrupt enable */
60 };
61 
62 #define CFG_CHG_INTR_MASK (VSC_INTR_LINK_CHG | VSC_INTR_NEG_ERR | \
63                VSC_INTR_NEG_DONE)
64 #define INTR_MASK (CFG_CHG_INTR_MASK | VSC_INTR_TX_FIFO | VSC_INTR_RX_FIFO | \
65            VSC_INTR_ENABLE)
66 
67 /* PHY specific auxiliary control & status register fields */
68 #define S_ACSR_ACTIPHY_TMR    0
69 #define M_ACSR_ACTIPHY_TMR    0x3
70 #define V_ACSR_ACTIPHY_TMR(x) ((x) << S_ACSR_ACTIPHY_TMR)
71 
72 #define S_ACSR_SPEED    3
73 #define M_ACSR_SPEED    0x3
74 #define G_ACSR_SPEED(x) (((x) >> S_ACSR_SPEED) & M_ACSR_SPEED)
75 
76 #define S_ACSR_DUPLEX 5
77 #define F_ACSR_DUPLEX (1 << S_ACSR_DUPLEX)
78 
79 #define S_ACSR_ACTIPHY 6
80 #define F_ACSR_ACTIPHY (1 << S_ACSR_ACTIPHY)
81 
82 /*
83  * Reset the PHY.  This PHY completes reset immediately so we never wait.
84  */
vsc8211_reset(struct cphy * cphy,int wait)85 static int vsc8211_reset(struct cphy *cphy, int wait)
86 {
87     return t3_phy_reset(cphy, 0, 0);
88 }
89 
vsc8211_intr_enable(struct cphy * cphy)90 static int vsc8211_intr_enable(struct cphy *cphy)
91 {
92     return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, INTR_MASK);
93 }
94 
vsc8211_intr_disable(struct cphy * cphy)95 static int vsc8211_intr_disable(struct cphy *cphy)
96 {
97     return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, 0);
98 }
99 
vsc8211_intr_clear(struct cphy * cphy)100 static int vsc8211_intr_clear(struct cphy *cphy)
101 {
102     u32 val;
103 
104     /* Clear PHY interrupts by reading the register. */
105     return mdio_read(cphy, 0, VSC8211_INTR_STATUS, &val);
106 }
107 
vsc8211_autoneg_enable(struct cphy * cphy)108 static int vsc8211_autoneg_enable(struct cphy *cphy)
109 {
110     return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE,
111                    BMCR_ANENABLE | BMCR_ANRESTART);
112 }
113 
vsc8211_autoneg_restart(struct cphy * cphy)114 static int vsc8211_autoneg_restart(struct cphy *cphy)
115 {
116     return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE,
117                    BMCR_ANRESTART);
118 }
119 
vsc8211_get_link_status(struct cphy * cphy,int * link_ok,int * speed,int * duplex,int * fc)120 static int vsc8211_get_link_status(struct cphy *cphy, int *link_ok,
121                      int *speed, int *duplex, int *fc)
122 {
123     unsigned int bmcr, status, lpa, adv;
124     int err, sp = -1, dplx = -1, pause = 0;
125 
126     err = mdio_read(cphy, 0, MII_BMCR, &bmcr);
127     if (!err)
128         err = mdio_read(cphy, 0, MII_BMSR, &status);
129     if (err)
130         return err;
131 
132     if (link_ok) {
133         /*
134          * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
135          * once more to get the current link state.
136          */
137         if (!(status & BMSR_LSTATUS))
138             err = mdio_read(cphy, 0, MII_BMSR, &status);
139         if (err)
140             return err;
141         *link_ok = (status & BMSR_LSTATUS) != 0;
142     }
143     if (!(bmcr & BMCR_ANENABLE)) {
144         dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
145         if (bmcr & BMCR_SPEED1000)
146             sp = SPEED_1000;
147         else if (bmcr & BMCR_SPEED100)
148             sp = SPEED_100;
149         else
150             sp = SPEED_10;
151     } else if (status & BMSR_ANEGCOMPLETE) {
152         err = mdio_read(cphy, 0, VSC8211_AUX_CTRL_STAT, &status);
153         if (err)
154             return err;
155 
156         dplx = (status & F_ACSR_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF;
157         sp = G_ACSR_SPEED(status);
158         if (sp == 0)
159             sp = SPEED_10;
160         else if (sp == 1)
161             sp = SPEED_100;
162         else
163             sp = SPEED_1000;
164 
165         if (fc && dplx == DUPLEX_FULL) {
166             err = mdio_read(cphy, 0, MII_LPA, &lpa);
167             if (!err)
168                 err = mdio_read(cphy, 0, MII_ADVERTISE, &adv);
169             if (err)
170                 return err;
171 
172             if (lpa & adv & ADVERTISE_PAUSE_CAP)
173                 pause = PAUSE_RX | PAUSE_TX;
174             else if ((lpa & ADVERTISE_PAUSE_CAP) &&
175                  (lpa & ADVERTISE_PAUSE_ASYM) &&
176                  (adv & ADVERTISE_PAUSE_ASYM))
177                 pause = PAUSE_TX;
178             else if ((lpa & ADVERTISE_PAUSE_ASYM) &&
179                  (adv & ADVERTISE_PAUSE_CAP))
180                 pause = PAUSE_RX;
181         }
182     }
183     if (speed)
184         *speed = sp;
185     if (duplex)
186         *duplex = dplx;
187     if (fc)
188         *fc = pause;
189     return 0;
190 }
191 
vsc8211_power_down(struct cphy * cphy,int enable)192 static int vsc8211_power_down(struct cphy *cphy, int enable)
193 {
194     return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN,
195                    enable ? BMCR_PDOWN : 0);
196 }
197 
vsc8211_intr_handler(struct cphy * cphy)198 static int vsc8211_intr_handler(struct cphy *cphy)
199 {
200     unsigned int cause;
201     int err, cphy_cause = 0;
202 
203     err = mdio_read(cphy, 0, VSC8211_INTR_STATUS, &cause);
204     if (err)
205         return err;
206 
207     cause &= INTR_MASK;
208     if (cause & CFG_CHG_INTR_MASK)
209         cphy_cause |= cphy_cause_link_change;
210     if (cause & (VSC_INTR_RX_FIFO | VSC_INTR_TX_FIFO))
211         cphy_cause |= cphy_cause_fifo_error;
212     return cphy_cause;
213 }
214 
215 #ifdef C99_NOT_SUPPORTED
216 static struct cphy_ops vsc8211_ops = {
217     NULL,
218     vsc8211_reset,
219     vsc8211_intr_enable,
220     vsc8211_intr_disable,
221     vsc8211_intr_clear,
222     vsc8211_intr_handler,
223     vsc8211_autoneg_enable,
224     vsc8211_autoneg_restart,
225     t3_phy_advertise,
226     NULL,
227     t3_set_phy_speed_duplex,
228     vsc8211_get_link_status,
229     vsc8211_power_down,
230 };
231 #else
232 static struct cphy_ops vsc8211_ops = {
233     .reset             = vsc8211_reset,
234     .intr_enable       = vsc8211_intr_enable,
235     .intr_disable      = vsc8211_intr_disable,
236     .intr_clear        = vsc8211_intr_clear,
237     .intr_handler      = vsc8211_intr_handler,
238     .autoneg_enable    = vsc8211_autoneg_enable,
239     .autoneg_restart   = vsc8211_autoneg_restart,
240     .advertise         = t3_phy_advertise,
241     .set_speed_duplex  = t3_set_phy_speed_duplex,
242     .get_link_status   = vsc8211_get_link_status,
243     .power_down        = vsc8211_power_down,
244 };
245 #endif
246 
t3_vsc8211_phy_prep(struct cphy * phy,adapter_t * adapter,int phy_addr,const struct mdio_ops * mdio_ops)247 void t3_vsc8211_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr,
248              const struct mdio_ops *mdio_ops)
249 {
250     cphy_init(phy, adapter, phy_addr, &vsc8211_ops, mdio_ops);
251     t3_os_sleep(20);       /* PHY needs ~10ms to start responding to MDIO */
252 }
253