1 | /* |
2 | * Copyright (c) 2005-2008 Chelsio, Inc. All rights reserved. |
3 | * |
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: |
9 | * |
10 | * Redistribution and use in source and binary forms, with or |
11 | * without modification, are permitted provided that the following |
12 | * conditions are met: |
13 | * |
14 | * - Redistributions of source code must retain the above |
15 | * copyright notice, this list of conditions and the following |
16 | * disclaimer. |
17 | * |
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. |
22 | * |
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 |
30 | * SOFTWARE. |
31 | */ |
32 | |
33 | #include "common.h" |
34 | #include "regs.h" |
35 | |
36 | enum { |
37 | /* MDIO_DEV_PMA_PMD registers */ |
38 | AQ_LINK_STAT = 0xe800, |
39 | AQ_IMASK_PMA = 0xf000, |
40 | |
41 | /* MDIO_DEV_XGXS registers */ |
42 | AQ_XAUI_RX_CFG = 0xc400, |
43 | AQ_XAUI_TX_CFG = 0xe400, |
44 | |
45 | /* MDIO_DEV_ANEG registers */ |
46 | AQ_1G_CTRL = 0xc400, |
47 | AQ_ANEG_STAT = 0xc800, |
48 | |
49 | /* MDIO_DEV_VEND1 registers */ |
50 | AQ_FW_VERSION = 0x0020, |
51 | AQ_IFLAG_GLOBAL = 0xfc00, |
52 | AQ_IMASK_GLOBAL = 0xff00, |
53 | }; |
54 | |
55 | enum { |
56 | IMASK_PMA = 1 << 2, |
57 | IMASK_GLOBAL = 1 << 15, |
58 | ADV_1G_FULL = 1 << 15, |
59 | ADV_1G_HALF = 1 << 14, |
60 | ADV_10G_FULL = 1 << 12, |
61 | AQ_RESET = (1 << 14) | (1 << 15), |
62 | AQ_LOWPOWER = 1 << 12, |
63 | }; |
64 | |
65 | static int aq100x_reset(struct cphy *phy, int wait) |
66 | { |
67 | /* |
68 | * Ignore the caller specified wait time; always wait for the reset to |
69 | * complete. Can take up to 3s. |
70 | */ |
71 | int err = t3_phy_reset(phy, MDIO_MMD_VEND1, wait: 3000); |
72 | |
73 | if (err) |
74 | CH_WARN(phy->adapter, "PHY%d: reset failed (0x%x).\n" , |
75 | phy->mdio.prtad, err); |
76 | |
77 | return err; |
78 | } |
79 | |
80 | static int aq100x_intr_enable(struct cphy *phy) |
81 | { |
82 | int err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, reg: AQ_IMASK_PMA, val: IMASK_PMA); |
83 | if (err) |
84 | return err; |
85 | |
86 | err = t3_mdio_write(phy, MDIO_MMD_VEND1, reg: AQ_IMASK_GLOBAL, val: IMASK_GLOBAL); |
87 | return err; |
88 | } |
89 | |
90 | static int aq100x_intr_disable(struct cphy *phy) |
91 | { |
92 | return t3_mdio_write(phy, MDIO_MMD_VEND1, reg: AQ_IMASK_GLOBAL, val: 0); |
93 | } |
94 | |
95 | static int aq100x_intr_clear(struct cphy *phy) |
96 | { |
97 | unsigned int v; |
98 | |
99 | t3_mdio_read(phy, MDIO_MMD_VEND1, reg: AQ_IFLAG_GLOBAL, valp: &v); |
100 | t3_mdio_read(phy, MDIO_MMD_PMAPMD, MDIO_STAT1, valp: &v); |
101 | |
102 | return 0; |
103 | } |
104 | |
105 | static int aq100x_intr_handler(struct cphy *phy) |
106 | { |
107 | int err; |
108 | unsigned int cause, v; |
109 | |
110 | err = t3_mdio_read(phy, MDIO_MMD_VEND1, reg: AQ_IFLAG_GLOBAL, valp: &cause); |
111 | if (err) |
112 | return err; |
113 | |
114 | /* Read (and reset) the latching version of the status */ |
115 | t3_mdio_read(phy, MDIO_MMD_PMAPMD, MDIO_STAT1, valp: &v); |
116 | |
117 | return cphy_cause_link_change; |
118 | } |
119 | |
120 | static int aq100x_power_down(struct cphy *phy, int off) |
121 | { |
122 | return mdio_set_flag(mdio: &phy->mdio, prtad: phy->mdio.prtad, |
123 | MDIO_MMD_PMAPMD, MDIO_CTRL1, |
124 | MDIO_CTRL1_LPOWER, sense: off); |
125 | } |
126 | |
127 | static int aq100x_autoneg_enable(struct cphy *phy) |
128 | { |
129 | int err; |
130 | |
131 | err = aq100x_power_down(phy, off: 0); |
132 | if (!err) |
133 | err = mdio_set_flag(mdio: &phy->mdio, prtad: phy->mdio.prtad, |
134 | MDIO_MMD_AN, MDIO_CTRL1, |
135 | BMCR_ANENABLE | BMCR_ANRESTART, sense: 1); |
136 | |
137 | return err; |
138 | } |
139 | |
140 | static int aq100x_autoneg_restart(struct cphy *phy) |
141 | { |
142 | int err; |
143 | |
144 | err = aq100x_power_down(phy, off: 0); |
145 | if (!err) |
146 | err = mdio_set_flag(mdio: &phy->mdio, prtad: phy->mdio.prtad, |
147 | MDIO_MMD_AN, MDIO_CTRL1, |
148 | BMCR_ANENABLE | BMCR_ANRESTART, sense: 1); |
149 | |
150 | return err; |
151 | } |
152 | |
153 | static int aq100x_advertise(struct cphy *phy, unsigned int advertise_map) |
154 | { |
155 | unsigned int adv; |
156 | int err; |
157 | |
158 | /* 10G advertisement */ |
159 | adv = 0; |
160 | if (advertise_map & ADVERTISED_10000baseT_Full) |
161 | adv |= ADV_10G_FULL; |
162 | err = t3_mdio_change_bits(phy, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL, |
163 | clear: ADV_10G_FULL, set: adv); |
164 | if (err) |
165 | return err; |
166 | |
167 | /* 1G advertisement */ |
168 | adv = 0; |
169 | if (advertise_map & ADVERTISED_1000baseT_Full) |
170 | adv |= ADV_1G_FULL; |
171 | if (advertise_map & ADVERTISED_1000baseT_Half) |
172 | adv |= ADV_1G_HALF; |
173 | err = t3_mdio_change_bits(phy, MDIO_MMD_AN, reg: AQ_1G_CTRL, |
174 | clear: ADV_1G_FULL | ADV_1G_HALF, set: adv); |
175 | if (err) |
176 | return err; |
177 | |
178 | /* 100M, pause advertisement */ |
179 | adv = 0; |
180 | if (advertise_map & ADVERTISED_100baseT_Half) |
181 | adv |= ADVERTISE_100HALF; |
182 | if (advertise_map & ADVERTISED_100baseT_Full) |
183 | adv |= ADVERTISE_100FULL; |
184 | if (advertise_map & ADVERTISED_Pause) |
185 | adv |= ADVERTISE_PAUSE_CAP; |
186 | if (advertise_map & ADVERTISED_Asym_Pause) |
187 | adv |= ADVERTISE_PAUSE_ASYM; |
188 | err = t3_mdio_change_bits(phy, MDIO_MMD_AN, MDIO_AN_ADVERTISE, |
189 | clear: 0xfe0, set: adv); |
190 | |
191 | return err; |
192 | } |
193 | |
194 | static int aq100x_set_loopback(struct cphy *phy, int mmd, int dir, int enable) |
195 | { |
196 | return mdio_set_flag(mdio: &phy->mdio, prtad: phy->mdio.prtad, |
197 | MDIO_MMD_PMAPMD, MDIO_CTRL1, |
198 | BMCR_LOOPBACK, sense: enable); |
199 | } |
200 | |
201 | static int aq100x_set_speed_duplex(struct cphy *phy, int speed, int duplex) |
202 | { |
203 | /* no can do */ |
204 | return -1; |
205 | } |
206 | |
207 | static int aq100x_get_link_status(struct cphy *phy, int *link_ok, |
208 | int *speed, int *duplex, int *fc) |
209 | { |
210 | int err; |
211 | unsigned int v; |
212 | |
213 | if (link_ok) { |
214 | err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, reg: AQ_LINK_STAT, valp: &v); |
215 | if (err) |
216 | return err; |
217 | |
218 | *link_ok = v & 1; |
219 | if (!*link_ok) |
220 | return 0; |
221 | } |
222 | |
223 | err = t3_mdio_read(phy, MDIO_MMD_AN, reg: AQ_ANEG_STAT, valp: &v); |
224 | if (err) |
225 | return err; |
226 | |
227 | if (speed) { |
228 | switch (v & 0x6) { |
229 | case 0x6: |
230 | *speed = SPEED_10000; |
231 | break; |
232 | case 0x4: |
233 | *speed = SPEED_1000; |
234 | break; |
235 | case 0x2: |
236 | *speed = SPEED_100; |
237 | break; |
238 | case 0x0: |
239 | *speed = SPEED_10; |
240 | break; |
241 | } |
242 | } |
243 | |
244 | if (duplex) |
245 | *duplex = v & 1 ? DUPLEX_FULL : DUPLEX_HALF; |
246 | |
247 | return 0; |
248 | } |
249 | |
250 | static const struct cphy_ops aq100x_ops = { |
251 | .reset = aq100x_reset, |
252 | .intr_enable = aq100x_intr_enable, |
253 | .intr_disable = aq100x_intr_disable, |
254 | .intr_clear = aq100x_intr_clear, |
255 | .intr_handler = aq100x_intr_handler, |
256 | .autoneg_enable = aq100x_autoneg_enable, |
257 | .autoneg_restart = aq100x_autoneg_restart, |
258 | .advertise = aq100x_advertise, |
259 | .set_loopback = aq100x_set_loopback, |
260 | .set_speed_duplex = aq100x_set_speed_duplex, |
261 | .get_link_status = aq100x_get_link_status, |
262 | .power_down = aq100x_power_down, |
263 | .mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS, |
264 | }; |
265 | |
266 | int t3_aq100x_phy_prep(struct cphy *phy, struct adapter *adapter, int phy_addr, |
267 | const struct mdio_ops *mdio_ops) |
268 | { |
269 | unsigned int v, v2, gpio, wait; |
270 | int err; |
271 | |
272 | cphy_init(phy, adapter, phy_addr, phy_ops: &aq100x_ops, mdio_ops, |
273 | SUPPORTED_1000baseT_Full | SUPPORTED_10000baseT_Full | |
274 | SUPPORTED_TP | SUPPORTED_Autoneg | SUPPORTED_AUI, |
275 | desc: "1000/10GBASE-T" ); |
276 | |
277 | /* |
278 | * The PHY has been out of reset ever since the system powered up. So |
279 | * we do a hard reset over here. |
280 | */ |
281 | gpio = phy_addr ? F_GPIO10_OUT_VAL : F_GPIO6_OUT_VAL; |
282 | t3_set_reg_field(adap: adapter, A_T3DBG_GPIO_EN, mask: gpio, val: 0); |
283 | msleep(msecs: 1); |
284 | t3_set_reg_field(adap: adapter, A_T3DBG_GPIO_EN, mask: gpio, val: gpio); |
285 | |
286 | /* |
287 | * Give it enough time to load the firmware and get ready for mdio. |
288 | */ |
289 | msleep(msecs: 1000); |
290 | wait = 500; /* in 10ms increments */ |
291 | do { |
292 | err = t3_mdio_read(phy, MDIO_MMD_VEND1, MDIO_CTRL1, valp: &v); |
293 | if (err || v == 0xffff) { |
294 | |
295 | /* Allow prep_adapter to succeed when ffff is read */ |
296 | |
297 | CH_WARN(adapter, "PHY%d: reset failed (0x%x, 0x%x).\n" , |
298 | phy_addr, err, v); |
299 | goto done; |
300 | } |
301 | |
302 | v &= AQ_RESET; |
303 | if (v) |
304 | msleep(msecs: 10); |
305 | } while (v && --wait); |
306 | if (v) { |
307 | CH_WARN(adapter, "PHY%d: reset timed out (0x%x).\n" , |
308 | phy_addr, v); |
309 | |
310 | goto done; /* let prep_adapter succeed */ |
311 | } |
312 | |
313 | /* Datasheet says 3s max but this has been observed */ |
314 | wait = (500 - wait) * 10 + 1000; |
315 | if (wait > 3000) |
316 | CH_WARN(adapter, "PHY%d: reset took %ums\n" , phy_addr, wait); |
317 | |
318 | /* Firmware version check. */ |
319 | t3_mdio_read(phy, MDIO_MMD_VEND1, reg: AQ_FW_VERSION, valp: &v); |
320 | if (v != 101) |
321 | CH_WARN(adapter, "PHY%d: unsupported firmware %d\n" , |
322 | phy_addr, v); |
323 | |
324 | /* |
325 | * The PHY should start in really-low-power mode. Prepare it for normal |
326 | * operations. |
327 | */ |
328 | err = t3_mdio_read(phy, MDIO_MMD_VEND1, MDIO_CTRL1, valp: &v); |
329 | if (err) |
330 | return err; |
331 | if (v & AQ_LOWPOWER) { |
332 | err = t3_mdio_change_bits(phy, MDIO_MMD_VEND1, MDIO_CTRL1, |
333 | clear: AQ_LOWPOWER, set: 0); |
334 | if (err) |
335 | return err; |
336 | msleep(msecs: 10); |
337 | } else |
338 | CH_WARN(adapter, "PHY%d does not start in low power mode.\n" , |
339 | phy_addr); |
340 | |
341 | /* |
342 | * Verify XAUI settings, but let prep succeed no matter what. |
343 | */ |
344 | v = v2 = 0; |
345 | t3_mdio_read(phy, MDIO_MMD_PHYXS, reg: AQ_XAUI_RX_CFG, valp: &v); |
346 | t3_mdio_read(phy, MDIO_MMD_PHYXS, reg: AQ_XAUI_TX_CFG, valp: &v2); |
347 | if (v != 0x1b || v2 != 0x1b) |
348 | CH_WARN(adapter, |
349 | "PHY%d: incorrect XAUI settings (0x%x, 0x%x).\n" , |
350 | phy_addr, v, v2); |
351 | |
352 | done: |
353 | return err; |
354 | } |
355 | |