FreeBSD kernel CXGB device code
cxgb_aq100x.c
Go to the documentation of this file.
1/**************************************************************************
2SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3
4Copyright (c) 2009 Chelsio Inc.
5All rights reserved.
6
7Redistribution and use in source and binary forms, with or without
8modification, are permitted provided that the following conditions are met:
9
10 1. Redistributions of source code must retain the above copyright notice,
11 this list of conditions and the following disclaimer.
12
13 2. Neither the name of the Chelsio Corporation nor the names of its
14 contributors may be used to endorse or promote products derived from
15 this software without specific prior written permission.
16
17THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27POSSIBILITY OF SUCH DAMAGE.
28
29***************************************************************************/
30
31#include <sys/cdefs.h>
32__FBSDID("$FreeBSD$");
33
34#include <cxgb_include.h>
35
36#undef msleep
37#define msleep t3_os_sleep
38
39enum {
40 /* MDIO_DEV_PMA_PMD registers */
41 AQ_LINK_STAT = 0xe800,
42
43 /* MDIO_DEV_XGXS registers */
47
48 /* MDIO_DEV_ANEG registers */
49 AQ_100M_CTRL = 0x0010,
50 AQ_10G_CTRL = 0x0020,
51 AQ_1G_CTRL = 0xc400,
52 AQ_ANEG_STAT = 0xc800,
53
54 /* MDIO_DEV_VEND1 registers */
55 AQ_FW_VERSION = 0x0020,
57 AQ_THERMAL1 = 0xc820,
58 AQ_THERMAL2 = 0xc821,
61};
62
63#define AQBIT(x) (1 << (0x##x))
64#define ADV_1G_FULL AQBIT(f)
65#define ADV_1G_HALF AQBIT(e)
66#define ADV_10G_FULL AQBIT(c)
67
68#define AQ_WRITE_REGS(phy, regs) do { \
69 int i; \
70 for (i = 0; i < ARRAY_SIZE(regs); i++) { \
71 (void) mdio_write(phy, regs[i].mmd, regs[i].reg, regs[i].val); \
72 } \
73} while (0)
74#define AQ_READ_REGS(phy, regs) do { \
75 unsigned i, v; \
76 for (i = 0; i < ARRAY_SIZE(regs); i++) { \
77 (void) mdio_read(phy, regs[i].mmd, regs[i].reg, &v); \
78 } \
79} while (0)
80
81/*
82 * Return value is temperature in celsius, 0xffff for error or don't know.
83 */
84static int
86{
87 unsigned int v;
88
90 v == 0xffff || (v & 1) != 1)
91 return (0xffff);
92
94 return (0xffff);
95
96 return ((int)((signed char)(v >> 8)));
97}
98
99static int
101{
103}
104
105static int
106aq100x_reset(struct cphy *phy, int wait)
107{
108 int err;
109 err = t3_phy_reset(phy, MDIO_DEV_PMA_PMD, wait);
110 if (!err)
112 return (err);
113}
114
115static int
117{
118 struct {
119 int mmd;
120 int reg;
121 int val;
122 } imasks[] = {
123 {MDIO_DEV_VEND1, 0xd400, AQBIT(e)},
124 {MDIO_DEV_VEND1, 0xff01, AQBIT(2)},
126 };
127
128 AQ_WRITE_REGS(phy, imasks);
129
130 return (0);
131}
132
133static int
135{
136 struct {
137 int mmd;
138 int reg;
139 int val;
140 } imasks[] = {
141 {MDIO_DEV_VEND1, 0xd400, 0},
142 {MDIO_DEV_VEND1, 0xff01, 0},
144 };
145
146 AQ_WRITE_REGS(phy, imasks);
147
148 return (0);
149}
150
151static int
153{
154 struct {
155 int mmd;
156 int reg;
157 } iclr[] = {
158 {MDIO_DEV_VEND1, 0xcc00},
159 {MDIO_DEV_VEND1, AQ_IMASK_GLOBAL} /* needed? */
160 };
161
162 AQ_READ_REGS(phy, iclr);
163
164 return (0);
165}
166
167static int
168aq100x_vendor_intr(struct cphy *phy, int *rc)
169{
170 int err;
171 unsigned int cause, v;
172
173 err = mdio_read(phy, MDIO_DEV_VEND1, 0xfc01, &cause);
174 if (err)
175 return (err);
176
177 if (cause & AQBIT(2)) {
178 err = mdio_read(phy, MDIO_DEV_VEND1, 0xcc00, &v);
179 if (err)
180 return (err);
181
182 if (v & AQBIT(e)) {
183 CH_WARN(phy->adapter, "PHY%d: temperature is now %dC\n",
185
188
189 *rc |= cphy_cause_alarm;
190 }
191
192 cause &= ~4;
193 }
194
195 if (cause)
196 CH_WARN(phy->adapter, "PHY%d: unhandled vendor interrupt"
197 " (0x%x)\n", phy->addr, cause);
198
199 return (0);
200
201}
202
203static int
205{
206 int err, rc = 0;
207 unsigned int cause;
208
210 if (err)
211 return (err);
212
213 if (cause & AQBIT(0)) {
214 err = aq100x_vendor_intr(phy, &rc);
215 if (err)
216 return (err);
217 cause &= ~AQBIT(0);
218 }
219
220 if (cause)
221 CH_WARN(phy->adapter, "PHY%d: unhandled interrupt (0x%x)\n",
222 phy->addr, cause);
223
224 return (rc);
225}
226
227static int
228aq100x_power_down(struct cphy *phy, int off)
229{
230 int err, wait = 500;
231 unsigned int v;
232
233 err = t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR, BMCR_PDOWN,
234 off ? BMCR_PDOWN : 0);
235 if (err || off)
236 return (err);
237
238 msleep(300);
239 do {
240 err = mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMCR, &v);
241 if (err)
242 return (err);
243 v &= BMCR_RESET;
244 if (v)
245 msleep(10);
246 } while (v && --wait);
247 if (v) {
248 CH_WARN(phy->adapter, "PHY%d: power-up timed out (0x%x).\n",
249 phy->addr, v);
250 return (ETIMEDOUT);
251 }
252
253 return (0);
254}
255
256static int
258{
259 int err;
260
261 err = aq100x_power_down(phy, 0);
262 if (!err)
263 err = t3_mdio_change_bits(phy, MDIO_DEV_ANEG, MII_BMCR,
264 BMCR_RESET, BMCR_ANENABLE | BMCR_ANRESTART);
265
266 return (err);
267}
268
269static int
271{
273}
274
275static int
276aq100x_advertise(struct cphy *phy, unsigned int advertise_map)
277{
278 unsigned int adv;
279 int err;
280
281 /* 10G advertisement */
282 adv = 0;
283 if (advertise_map & ADVERTISED_10000baseT_Full)
284 adv |= ADV_10G_FULL;
286 ADV_10G_FULL, adv);
287 if (err)
288 return (err);
289
290 /* 1G advertisement */
291 adv = 0;
292 if (advertise_map & ADVERTISED_1000baseT_Full)
293 adv |= ADV_1G_FULL;
294 if (advertise_map & ADVERTISED_1000baseT_Half)
295 adv |= ADV_1G_HALF;
297 ADV_1G_FULL | ADV_1G_HALF, adv);
298 if (err)
299 return (err);
300
301 /* 100M, pause advertisement */
302 adv = 0;
303 if (advertise_map & ADVERTISED_100baseT_Half)
304 adv |= ADVERTISE_100HALF;
305 if (advertise_map & ADVERTISED_100baseT_Full)
306 adv |= ADVERTISE_100FULL;
307 if (advertise_map & ADVERTISED_Pause)
308 adv |= ADVERTISE_PAUSE_CAP;
309 if (advertise_map & ADVERTISED_Asym_Pause)
312
313 return (err);
314}
315
316static int
317aq100x_set_loopback(struct cphy *phy, int mmd, int dir, int enable)
318{
319 return t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR,
320 BMCR_LOOPBACK, enable ? BMCR_LOOPBACK : 0);
321}
322
323static int
324aq100x_set_speed_duplex(struct cphy *phy, int speed, int duplex)
325{
326 int err, set;
327
328 if (speed == SPEED_100)
329 set = BMCR_SPEED100;
330 else if (speed == SPEED_1000)
331 set = BMCR_SPEED1000;
332 else if (speed == SPEED_10000)
334 else
335 return (EINVAL);
336
337 if (duplex != DUPLEX_FULL)
338 return (EINVAL);
339
340 err = t3_mdio_change_bits(phy, MDIO_DEV_ANEG, MII_BMCR,
341 BMCR_RESET | BMCR_ANENABLE | BMCR_ANRESTART, 0);
342 if (err)
343 return (err);
344
347 if (err)
348 return (err);
349
350 return (0);
351}
352
353static int
354aq100x_get_link_status(struct cphy *phy, int *link_state, int *speed, int *duplex,
355 int *fc)
356{
357 int err;
358 unsigned int v, link = 0;
359
361 if (err)
362 return (err);
363 if (v == 0xffff || !(v & 1))
364 goto done;
365
366 err = mdio_read(phy, MDIO_DEV_ANEG, MII_BMCR, &v);
367 if (err)
368 return (err);
369 if (v & 0x8000)
370 goto done;
371 if (v & BMCR_ANENABLE) {
372
373 err = mdio_read(phy, MDIO_DEV_ANEG, 1, &v);
374 if (err)
375 return (err);
376 if ((v & 0x20) == 0)
377 goto done;
378
380 if (err)
381 return (err);
382
383 if (speed) {
384 switch (v & 0x6) {
385 case 0x6: *speed = SPEED_10000;
386 break;
387 case 0x4: *speed = SPEED_1000;
388 break;
389 case 0x2: *speed = SPEED_100;
390 break;
391 case 0x0: *speed = SPEED_10;
392 break;
393 }
394 }
395
396 if (duplex)
397 *duplex = v & 1 ? DUPLEX_FULL : DUPLEX_HALF;
398
399 if (fc) {
400 unsigned int lpa, adv;
401 err = mdio_read(phy, MDIO_DEV_ANEG, 0x13, &lpa);
402 if (!err)
404 AQ_100M_CTRL, &adv);
405 if (err)
406 return err;
407
408 if (lpa & adv & ADVERTISE_PAUSE_CAP)
409 *fc = PAUSE_RX | PAUSE_TX;
410 else if (lpa & ADVERTISE_PAUSE_CAP &&
411 lpa & ADVERTISE_PAUSE_ASYM &&
413 *fc = PAUSE_TX;
414 else if (lpa & ADVERTISE_PAUSE_ASYM &&
416 *fc = PAUSE_RX;
417 else
418 *fc = 0;
419 }
420
421 } else {
422 err = mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMCR, &v);
423 if (err)
424 return (err);
425
427 if (speed) {
428 if (v == (BMCR_SPEED1000 | BMCR_SPEED100))
429 *speed = SPEED_10000;
430 else if (v == BMCR_SPEED1000)
431 *speed = SPEED_1000;
432 else if (v == BMCR_SPEED100)
433 *speed = SPEED_100;
434 else
435 *speed = SPEED_10;
436 }
437
438 if (duplex)
439 *duplex = DUPLEX_FULL;
440 }
441
442 link = 1;
443done:
444 if (link_state)
445 *link_state = link ? PHY_LINK_UP : PHY_LINK_DOWN;
446 return (0);
447}
448
449static struct cphy_ops aq100x_ops = {
451 .intr_enable = aq100x_intr_enable,
452 .intr_disable = aq100x_intr_disable,
453 .intr_clear = aq100x_intr_clear,
454 .intr_handler = aq100x_intr_handler,
455 .autoneg_enable = aq100x_autoneg_enable,
456 .autoneg_restart = aq100x_autoneg_restart,
457 .advertise = aq100x_advertise,
458 .set_loopback = aq100x_set_loopback,
459 .set_speed_duplex = aq100x_set_speed_duplex,
460 .get_link_status = aq100x_get_link_status,
461 .power_down = aq100x_power_down,
462};
463
464int
465t3_aq100x_phy_prep(pinfo_t *pinfo, int phy_addr,
466 const struct mdio_ops *mdio_ops)
467{
468 struct cphy *phy = &pinfo->phy;
469 unsigned int v, v2, gpio, wait;
470 int err;
472
476 SUPPORTED_MISC_IRQ, "1000/10GBASE-T");
477
478 /*
479 * Hard reset the PHY.
480 */
481 gpio = phy_addr ? F_GPIO10_OUT_VAL : F_GPIO6_OUT_VAL;
483 msleep(1);
485
486 /*
487 * Give it enough time to load the firmware and get ready for mdio.
488 */
489 msleep(1000);
490 wait = 500; /* in 10ms increments */
491 do {
492 err = mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMCR, &v);
493 if (err || v == 0xffff) {
494
495 /* Allow prep_adapter to succeed when ffff is read */
496
497 CH_WARN(adapter, "PHY%d: reset failed (0x%x, 0x%x).\n",
498 phy_addr, err, v);
499 goto done;
500 }
501
502 v &= BMCR_RESET;
503 if (v)
504 msleep(10);
505 } while (v && --wait);
506 if (v) {
507 CH_WARN(adapter, "PHY%d: reset timed out (0x%x).\n",
508 phy_addr, v);
509
510 goto done; /* let prep_adapter succeed */
511 }
512
513 /* Firmware version check. */
515 if (v < 0x115)
516 CH_WARN(adapter, "PHY%d: unknown firmware %d.%d\n", phy_addr,
517 v >> 8, v & 0xff);
518
519 /* The PHY should start in really-low-power mode. */
520 (void) mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMCR, &v);
521 if ((v & BMCR_PDOWN) == 0)
522 CH_WARN(adapter, "PHY%d does not start in low power mode.\n",
523 phy_addr);
524
525 /*
526 * Verify XAUI and 1000-X settings, but let prep succeed no matter what.
527 */
528 v = v2 = 0;
531 if (v != 0x1b || v2 != 0x1b)
532 CH_WARN(adapter, "PHY%d: incorrect XAUI settings "
533 "(0x%x, 0x%x).\n", phy_addr, v, v2);
534 v = 0;
536 if ((v & 0xf) != 0xf)
537 CH_WARN(adapter, "PHY%d: incorrect 1000-X settings "
538 "(0x%x).\n", phy_addr, v);
539
540 (void) aq100x_set_defaults(phy);
541done:
542 return (err);
543}
struct cphy phy
Definition: cxgb_adapter.h:5
static int aq100x_autoneg_enable(struct cphy *phy)
Definition: cxgb_aq100x.c:257
#define ADV_1G_HALF
Definition: cxgb_aq100x.c:65
static struct cphy_ops aq100x_ops
Definition: cxgb_aq100x.c:449
int t3_aq100x_phy_prep(pinfo_t *pinfo, int phy_addr, const struct mdio_ops *mdio_ops)
Definition: cxgb_aq100x.c:465
#define AQ_WRITE_REGS(phy, regs)
Definition: cxgb_aq100x.c:68
@ AQ_1G_CTRL
Definition: cxgb_aq100x.c:51
@ AQ_100M_CTRL
Definition: cxgb_aq100x.c:49
@ AQ_FW_VERSION
Definition: cxgb_aq100x.c:55
@ AQ_XAUI_TX_CFG
Definition: cxgb_aq100x.c:46
@ AQ_10G_CTRL
Definition: cxgb_aq100x.c:50
@ AQ_LINK_STAT
Definition: cxgb_aq100x.c:41
@ AQ_XAUI_KX_CFG
Definition: cxgb_aq100x.c:45
@ AQ_IFLAG_GLOBAL
Definition: cxgb_aq100x.c:59
@ AQ_THERMAL_THR
Definition: cxgb_aq100x.c:56
@ AQ_THERMAL1
Definition: cxgb_aq100x.c:57
@ AQ_IMASK_GLOBAL
Definition: cxgb_aq100x.c:60
@ AQ_ANEG_STAT
Definition: cxgb_aq100x.c:52
@ AQ_THERMAL2
Definition: cxgb_aq100x.c:58
@ AQ_XAUI_RX_CFG
Definition: cxgb_aq100x.c:44
static int aq100x_intr_clear(struct cphy *phy)
Definition: cxgb_aq100x.c:152
static int aq100x_vendor_intr(struct cphy *phy, int *rc)
Definition: cxgb_aq100x.c:168
static int aq100x_power_down(struct cphy *phy, int off)
Definition: cxgb_aq100x.c:228
#define ADV_10G_FULL
Definition: cxgb_aq100x.c:66
__FBSDID("$FreeBSD$")
#define AQ_READ_REGS(phy, regs)
Definition: cxgb_aq100x.c:74
static int aq100x_temperature(struct cphy *phy)
Definition: cxgb_aq100x.c:85
#define msleep
Definition: cxgb_aq100x.c:37
#define ADV_1G_FULL
Definition: cxgb_aq100x.c:64
static int aq100x_set_loopback(struct cphy *phy, int mmd, int dir, int enable)
Definition: cxgb_aq100x.c:317
static int aq100x_autoneg_restart(struct cphy *phy)
Definition: cxgb_aq100x.c:270
static int aq100x_set_speed_duplex(struct cphy *phy, int speed, int duplex)
Definition: cxgb_aq100x.c:324
static int aq100x_intr_disable(struct cphy *phy)
Definition: cxgb_aq100x.c:134
static int aq100x_intr_enable(struct cphy *phy)
Definition: cxgb_aq100x.c:116
static int aq100x_get_link_status(struct cphy *phy, int *link_state, int *speed, int *duplex, int *fc)
Definition: cxgb_aq100x.c:354
static int aq100x_intr_handler(struct cphy *phy)
Definition: cxgb_aq100x.c:204
static int aq100x_advertise(struct cphy *phy, unsigned int advertise_map)
Definition: cxgb_aq100x.c:276
#define AQBIT(x)
Definition: cxgb_aq100x.c:63
static int aq100x_set_defaults(struct cphy *phy)
Definition: cxgb_aq100x.c:100
static int aq100x_reset(struct cphy *phy, int wait)
Definition: cxgb_aq100x.c:106
static int mdio_read(struct cphy *phy, int mmd, int reg, unsigned int *valp)
Definition: cxgb_common.h:592
@ PAUSE_TX
Definition: cxgb_common.h:55
@ PAUSE_RX
Definition: cxgb_common.h:54
void t3_set_reg_field(adapter_t *adap, unsigned int addr, u32 mask, u32 val)
Definition: cxgb_t3_hw.c:103
int t3_mdio_change_bits(struct cphy *phy, int mmd, int reg, unsigned int clear, unsigned int set)
Definition: cxgb_t3_hw.c:372
int t3_phy_reset(struct cphy *phy, int mmd, int wait)
Definition: cxgb_t3_hw.c:396
@ PHY_LINK_DOWN
Definition: cxgb_common.h:548
@ PHY_LINK_UP
Definition: cxgb_common.h:549
@ cphy_cause_alarm
Definition: cxgb_common.h:533
@ MDIO_DEV_XGXS
Definition: cxgb_common.h:506
@ MDIO_DEV_VEND1
Definition: cxgb_common.h:508
@ MDIO_DEV_ANEG
Definition: cxgb_common.h:507
@ MDIO_DEV_PMA_PMD
Definition: cxgb_common.h:503
static int mdio_write(struct cphy *phy, int mmd, int reg, unsigned int val)
Definition: cxgb_common.h:598
@ SUPPORTED_MISC_IRQ
Definition: cxgb_common.h:62
static void cphy_init(struct cphy *phy, adapter_t *adapter, pinfo_t *pinfo, int phy_addr, struct cphy_ops *phy_ops, const struct mdio_ops *mdio_ops, unsigned int caps, const char *desc)
Definition: cxgb_common.h:605
#define ADVERTISED_1000baseT_Full
Definition: cxgb_osdep.h:246
#define ADVERTISED_Pause
Definition: cxgb_osdep.h:254
#define ADVERTISED_10000baseT_Full
Definition: cxgb_osdep.h:253
#define BMCR_SPEED100
Definition: cxgb_osdep.h:139
#define ADVERTISE_PAUSE_ASYM
Definition: cxgb_osdep.h:150
#define SPEED_100
Definition: cxgb_osdep.h:264
#define SUPPORTED_AUI
Definition: cxgb_osdep.h:232
#define SPEED_1000
Definition: cxgb_osdep.h:265
#define SUPPORTED_TP
Definition: cxgb_osdep.h:231
#define ADVERTISE_100FULL
Definition: cxgb_osdep.h:156
#define SUPPORTED_10000baseT_Full
Definition: cxgb_osdep.h:236
#define DUPLEX_FULL
Definition: cxgb_osdep.h:268
#define SUPPORTED_Autoneg
Definition: cxgb_osdep.h:230
#define ADVERTISED_100baseT_Half
Definition: cxgb_osdep.h:243
#define ADVERTISED_100baseT_Full
Definition: cxgb_osdep.h:244
#define SPEED_10000
Definition: cxgb_osdep.h:266
#define BMCR_SPEED1000
Definition: cxgb_osdep.h:138
#define SUPPORTED_1000baseT_Full
Definition: cxgb_osdep.h:229
#define DUPLEX_HALF
Definition: cxgb_osdep.h:267
#define BMCR_ANRESTART
Definition: cxgb_osdep.h:140
#define SPEED_10
Definition: cxgb_osdep.h:263
#define BMCR_ANENABLE
Definition: cxgb_osdep.h:137
#define ADVERTISED_1000baseT_Half
Definition: cxgb_osdep.h:245
#define ADVERTISE_100HALF
Definition: cxgb_osdep.h:157
#define BMCR_LOOPBACK
Definition: cxgb_osdep.h:135
#define CH_WARN(adap, fmt,...)
Definition: cxgb_osdep.h:124
#define ADVERTISED_Asym_Pause
Definition: cxgb_osdep.h:255
#define ADVERTISE_PAUSE_CAP
Definition: cxgb_osdep.h:149
#define F_GPIO6_OUT_VAL
Definition: cxgb_regs.h:2180
#define A_T3DBG_GPIO_EN
Definition: cxgb_regs.h:2108
#define F_GPIO10_OUT_VAL
Definition: cxgb_regs.h:2164
int(* reset)(struct cphy *phy, int wait)
Definition: cxgb_common.h:555
u8 addr
Definition: cxgb_common.h:575
pinfo_t * pinfo
Definition: cxgb_common.h:581
adapter_t * adapter
Definition: cxgb_common.h:580
struct cphy phy
Definition: cxgb_adapter.h:97
struct adapter * adapter
Definition: cxgb_adapter.h:92