xref: /netbsd-src/sys/dev/pci/cxgb/cxgb_ael1002.c (revision 86bb752ce493a5c81fcaec5025f4fd6bec24ddfb)
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_ael1002.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 enum {
40     AEL100X_TX_DISABLE  = 9,
41     AEL100X_TX_CONFIG1  = 0xc002,
42     AEL1002_PWR_DOWN_HI = 0xc011,
43     AEL1002_PWR_DOWN_LO = 0xc012,
44     AEL1002_XFI_EQL     = 0xc015,
45     AEL1002_LB_EN       = 0xc017,
46 
47     LASI_CTRL   = 0x9002,
48     LASI_STAT   = 0x9005
49 };
50 
ael100x_txon(struct cphy * phy)51 static void ael100x_txon(struct cphy *phy)
52 {
53     int tx_on_gpio = phy->addr == 0 ? F_GPIO7_OUT_VAL : F_GPIO2_OUT_VAL;
54 
55     t3_os_sleep(100);
56     t3_set_reg_field(phy->adapter, A_T3DBG_GPIO_EN, 0, tx_on_gpio);
57     t3_os_sleep(30);
58 }
59 
ael1002_power_down(struct cphy * phy,int enable)60 static int ael1002_power_down(struct cphy *phy, int enable)
61 {
62     int err;
63 
64     err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL100X_TX_DISABLE, !!enable);
65     if (!err)
66         err = t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR,
67                       BMCR_PDOWN, enable ? BMCR_PDOWN : 0);
68     return err;
69 }
70 
ael1002_reset(struct cphy * phy,int wait)71 static int ael1002_reset(struct cphy *phy, int wait)
72 {
73     int err;
74 
75     if ((err = ael1002_power_down(phy, 0)) ||
76         (err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL100X_TX_CONFIG1, 1)) ||
77         (err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL1002_PWR_DOWN_HI, 0)) ||
78         (err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL1002_PWR_DOWN_LO, 0)) ||
79         (err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL1002_XFI_EQL, 0x18)) ||
80         (err = t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, AEL1002_LB_EN,
81                        0, 1 << 5)))
82         return err;
83     return 0;
84 }
85 
ael1002_intr_noop(struct cphy * phy)86 static int ael1002_intr_noop(struct cphy *phy)
87 {
88     return 0;
89 }
90 
ael100x_get_link_status(struct cphy * phy,int * link_ok,int * speed,int * duplex,int * fc)91 static int ael100x_get_link_status(struct cphy *phy, int *link_ok,
92                    int *speed, int *duplex, int *fc)
93 {
94     if (link_ok) {
95         unsigned int status;
96         int err = mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMSR, &status);
97 
98         /*
99          * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
100          * once more to get the current link state.
101          */
102         if (!err && !(status & BMSR_LSTATUS))
103             err = mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMSR,
104                     &status);
105         if (err)
106             return err;
107         *link_ok = !!(status & BMSR_LSTATUS);
108     }
109     if (speed)
110         *speed = SPEED_10000;
111     if (duplex)
112         *duplex = DUPLEX_FULL;
113     return 0;
114 }
115 
116 #ifdef C99_NOT_SUPPORTED
117 static struct cphy_ops ael1002_ops = {
118     NULL,
119     ael1002_reset,
120     ael1002_intr_noop,
121     ael1002_intr_noop,
122     ael1002_intr_noop,
123     ael1002_intr_noop,
124     NULL,
125     NULL,
126     NULL,
127     NULL,
128     NULL,
129     ael100x_get_link_status,
130     ael1002_power_down,
131 };
132 #else
133 static struct cphy_ops ael1002_ops = {
134     .reset           = ael1002_reset,
135     .intr_enable     = ael1002_intr_noop,
136     .intr_disable    = ael1002_intr_noop,
137     .intr_clear      = ael1002_intr_noop,
138     .intr_handler    = ael1002_intr_noop,
139     .get_link_status = ael100x_get_link_status,
140     .power_down      = ael1002_power_down,
141 };
142 #endif
143 
t3_ael1002_phy_prep(struct cphy * phy,adapter_t * adapter,int phy_addr,const struct mdio_ops * mdio_ops)144 void t3_ael1002_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr,
145              const struct mdio_ops *mdio_ops)
146 {
147     cphy_init(phy, adapter, phy_addr, &ael1002_ops, mdio_ops);
148     ael100x_txon(phy);
149 }
150 
ael1006_reset(struct cphy * phy,int wait)151 static int ael1006_reset(struct cphy *phy, int wait)
152 {
153     return t3_phy_reset(phy, MDIO_DEV_PMA_PMD, wait);
154 }
155 
ael1006_intr_enable(struct cphy * phy)156 static int ael1006_intr_enable(struct cphy *phy)
157 {
158     return mdio_write(phy, MDIO_DEV_PMA_PMD, LASI_CTRL, 1);
159 }
160 
ael1006_intr_disable(struct cphy * phy)161 static int ael1006_intr_disable(struct cphy *phy)
162 {
163     return mdio_write(phy, MDIO_DEV_PMA_PMD, LASI_CTRL, 0);
164 }
165 
ael1006_intr_clear(struct cphy * phy)166 static int ael1006_intr_clear(struct cphy *phy)
167 {
168     u32 val;
169 
170     return mdio_read(phy, MDIO_DEV_PMA_PMD, LASI_STAT, &val);
171 }
172 
ael1006_intr_handler(struct cphy * phy)173 static int ael1006_intr_handler(struct cphy *phy)
174 {
175     unsigned int status;
176     int err = mdio_read(phy, MDIO_DEV_PMA_PMD, LASI_STAT, &status);
177 
178     if (err)
179         return err;
180     return (status & 1) ?  cphy_cause_link_change : 0;
181 }
182 
ael1006_power_down(struct cphy * phy,int enable)183 static int ael1006_power_down(struct cphy *phy, int enable)
184 {
185     return t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR,
186                    BMCR_PDOWN, enable ? BMCR_PDOWN : 0);
187 }
188 
189 #ifdef C99_NOT_SUPPORTED
190 static struct cphy_ops ael1006_ops = {
191     NULL,
192     ael1006_reset,
193     ael1006_intr_enable,
194     ael1006_intr_disable,
195     ael1006_intr_clear,
196     ael1006_intr_handler,
197     NULL,
198     NULL,
199     NULL,
200     NULL,
201     NULL,
202     ael100x_get_link_status,
203     ael1006_power_down,
204 };
205 #else
206 static struct cphy_ops ael1006_ops = {
207     .reset           = ael1006_reset,
208     .intr_enable     = ael1006_intr_enable,
209     .intr_disable    = ael1006_intr_disable,
210     .intr_clear      = ael1006_intr_clear,
211     .intr_handler    = ael1006_intr_handler,
212     .get_link_status = ael100x_get_link_status,
213     .power_down      = ael1006_power_down,
214 };
215 #endif
216 
t3_ael1006_phy_prep(struct cphy * phy,adapter_t * adapter,int phy_addr,const struct mdio_ops * mdio_ops)217 void t3_ael1006_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr,
218              const struct mdio_ops *mdio_ops)
219 {
220     cphy_init(phy, adapter, phy_addr, &ael1006_ops, mdio_ops);
221     ael100x_txon(phy);
222 }
223 
224 #ifdef C99_NOT_SUPPORTED
225 static struct cphy_ops qt2045_ops = {
226     NULL,
227     ael1006_reset,
228     ael1006_intr_enable,
229     ael1006_intr_disable,
230     ael1006_intr_clear,
231     ael1006_intr_handler,
232     NULL,
233     NULL,
234     NULL,
235     NULL,
236     NULL,
237     ael100x_get_link_status,
238     ael1006_power_down,
239 };
240 #else
241 static struct cphy_ops qt2045_ops = {
242     .reset           = ael1006_reset,
243     .intr_enable     = ael1006_intr_enable,
244     .intr_disable    = ael1006_intr_disable,
245     .intr_clear      = ael1006_intr_clear,
246     .intr_handler    = ael1006_intr_handler,
247     .get_link_status = ael100x_get_link_status,
248     .power_down      = ael1006_power_down,
249 };
250 #endif
251 
t3_qt2045_phy_prep(struct cphy * phy,adapter_t * adapter,int phy_addr,const struct mdio_ops * mdio_ops)252 void t3_qt2045_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr,
253             const struct mdio_ops *mdio_ops)
254 {
255     unsigned int stat;
256 
257     cphy_init(phy, adapter, phy_addr, &qt2045_ops, mdio_ops);
258 
259     /*
260      * Some cards where the PHY is supposed to be at address 0 actually
261      * have it at 1.
262      */
263     if (!phy_addr && !mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMSR, &stat) &&
264         stat == 0xffff)
265         phy->addr = 1;
266 }
267 
xaui_direct_reset(struct cphy * phy,int wait)268 static int xaui_direct_reset(struct cphy *phy, int wait)
269 {
270     return 0;
271 }
272 
xaui_direct_get_link_status(struct cphy * phy,int * link_ok,int * speed,int * duplex,int * fc)273 static int xaui_direct_get_link_status(struct cphy *phy, int *link_ok,
274                        int *speed, int *duplex, int *fc)
275 {
276     if (link_ok) {
277         unsigned int status;
278 
279         status = t3_read_reg(phy->adapter,
280                      XGM_REG(A_XGM_SERDES_STAT0, phy->addr)) |
281              t3_read_reg(phy->adapter,
282                      XGM_REG(A_XGM_SERDES_STAT1, phy->addr)) |
283              t3_read_reg(phy->adapter,
284                      XGM_REG(A_XGM_SERDES_STAT2, phy->addr)) |
285              t3_read_reg(phy->adapter,
286                      XGM_REG(A_XGM_SERDES_STAT3, phy->addr));
287         *link_ok = !(status & F_LOWSIG0);
288     }
289     if (speed)
290         *speed = SPEED_10000;
291     if (duplex)
292         *duplex = DUPLEX_FULL;
293     return 0;
294 }
295 
xaui_direct_power_down(struct cphy * phy,int enable)296 static int xaui_direct_power_down(struct cphy *phy, int enable)
297 {
298     return 0;
299 }
300 
301 #ifdef C99_NOT_SUPPORTED
302 static struct cphy_ops xaui_direct_ops = {
303     NULL,
304     xaui_direct_reset,
305     ael1002_intr_noop,
306     ael1002_intr_noop,
307     ael1002_intr_noop,
308     ael1002_intr_noop,
309     NULL,
310     NULL,
311     NULL,
312     NULL,
313     NULL,
314     xaui_direct_get_link_status,
315     xaui_direct_power_down,
316 };
317 #else
318 static struct cphy_ops xaui_direct_ops = {
319     .reset           = xaui_direct_reset,
320     .intr_enable     = ael1002_intr_noop,
321     .intr_disable    = ael1002_intr_noop,
322     .intr_clear      = ael1002_intr_noop,
323     .intr_handler    = ael1002_intr_noop,
324     .get_link_status = xaui_direct_get_link_status,
325     .power_down      = xaui_direct_power_down,
326 };
327 #endif
328 
t3_xaui_direct_phy_prep(struct cphy * phy,adapter_t * adapter,int phy_addr,const struct mdio_ops * mdio_ops)329 void t3_xaui_direct_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr,
330                  const struct mdio_ops *mdio_ops)
331 {
332     cphy_init(phy, adapter, phy_addr, &xaui_direct_ops, mdio_ops);
333 }
334