1 /**************************************************************************
2 SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 Copyright (c) 2009 Chelsio Inc.
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
10 1. Redistributions of source code must retain the above copyright notice,
11 this list of conditions and the following disclaimer.
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.
17 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 POSSIBILITY OF SUCH DAMAGE.
29 ***************************************************************************/
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
34 #include <cxgb_include.h>
37 #define msleep t3_os_sleep
40 /* MDIO_DEV_PMA_PMD registers */
41 AQ_LINK_STAT = 0xe800,
43 /* MDIO_DEV_XGXS registers */
44 AQ_XAUI_RX_CFG = 0xc400,
45 AQ_XAUI_KX_CFG = 0xc440,
46 AQ_XAUI_TX_CFG = 0xe400,
48 /* MDIO_DEV_ANEG registers */
49 AQ_100M_CTRL = 0x0010,
52 AQ_ANEG_STAT = 0xc800,
54 /* MDIO_DEV_VEND1 registers */
55 AQ_FW_VERSION = 0x0020,
56 AQ_THERMAL_THR = 0xc421,
59 AQ_IFLAG_GLOBAL = 0xfc00,
60 AQ_IMASK_GLOBAL = 0xff00,
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)
68 #define AQ_WRITE_REGS(phy, regs) do { \
70 for (i = 0; i < ARRAY_SIZE(regs); i++) { \
71 (void) mdio_write(phy, regs[i].mmd, regs[i].reg, regs[i].val); \
74 #define AQ_READ_REGS(phy, regs) do { \
76 for (i = 0; i < ARRAY_SIZE(regs); i++) { \
77 (void) mdio_read(phy, regs[i].mmd, regs[i].reg, &v); \
82 * Return value is temperature in celcius, 0xffff for error or don't know.
85 aq100x_temperature(struct cphy *phy)
89 if (mdio_read(phy, MDIO_DEV_VEND1, AQ_THERMAL2, &v) ||
90 v == 0xffff || (v & 1) != 1)
93 if (mdio_read(phy, MDIO_DEV_VEND1, AQ_THERMAL1, &v))
96 return ((int)((signed char)(v >> 8)));
100 aq100x_set_defaults(struct cphy *phy)
102 return mdio_write(phy, MDIO_DEV_VEND1, AQ_THERMAL_THR, 0x6c00);
106 aq100x_reset(struct cphy *phy, int wait)
109 err = t3_phy_reset(phy, MDIO_DEV_PMA_PMD, wait);
111 err = aq100x_set_defaults(phy);
116 aq100x_intr_enable(struct cphy *phy)
123 {MDIO_DEV_VEND1, 0xd400, AQBIT(e)},
124 {MDIO_DEV_VEND1, 0xff01, AQBIT(2)},
125 {MDIO_DEV_VEND1, AQ_IMASK_GLOBAL, AQBIT(0)}
128 AQ_WRITE_REGS(phy, imasks);
134 aq100x_intr_disable(struct cphy *phy)
141 {MDIO_DEV_VEND1, 0xd400, 0},
142 {MDIO_DEV_VEND1, 0xff01, 0},
143 {MDIO_DEV_VEND1, AQ_IMASK_GLOBAL, 0}
146 AQ_WRITE_REGS(phy, imasks);
152 aq100x_intr_clear(struct cphy *phy)
158 {MDIO_DEV_VEND1, 0xcc00},
159 {MDIO_DEV_VEND1, AQ_IMASK_GLOBAL} /* needed? */
162 AQ_READ_REGS(phy, iclr);
168 aq100x_vendor_intr(struct cphy *phy, int *rc)
171 unsigned int cause, v;
173 err = mdio_read(phy, MDIO_DEV_VEND1, 0xfc01, &cause);
177 if (cause & AQBIT(2)) {
178 err = mdio_read(phy, MDIO_DEV_VEND1, 0xcc00, &v);
183 CH_WARN(phy->adapter, "PHY%d: temperature is now %dC\n",
184 phy->addr, aq100x_temperature(phy));
186 t3_set_reg_field(phy->adapter, A_T3DBG_GPIO_EN,
187 phy->addr ? F_GPIO10_OUT_VAL : F_GPIO6_OUT_VAL, 0);
189 *rc |= cphy_cause_alarm;
196 CH_WARN(phy->adapter, "PHY%d: unhandled vendor interrupt"
197 " (0x%x)\n", phy->addr, cause);
204 aq100x_intr_handler(struct cphy *phy)
209 err = mdio_read(phy, MDIO_DEV_VEND1, AQ_IFLAG_GLOBAL, &cause);
213 if (cause & AQBIT(0)) {
214 err = aq100x_vendor_intr(phy, &rc);
221 CH_WARN(phy->adapter, "PHY%d: unhandled interrupt (0x%x)\n",
228 aq100x_power_down(struct cphy *phy, int off)
233 err = t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR, BMCR_PDOWN,
234 off ? BMCR_PDOWN : 0);
240 err = mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMCR, &v);
246 } while (v && --wait);
248 CH_WARN(phy->adapter, "PHY%d: power-up timed out (0x%x).\n",
257 aq100x_autoneg_enable(struct cphy *phy)
261 err = aq100x_power_down(phy, 0);
263 err = t3_mdio_change_bits(phy, MDIO_DEV_ANEG, MII_BMCR,
264 BMCR_RESET, BMCR_ANENABLE | BMCR_ANRESTART);
270 aq100x_autoneg_restart(struct cphy *phy)
272 return aq100x_autoneg_enable(phy);
276 aq100x_advertise(struct cphy *phy, unsigned int advertise_map)
281 /* 10G advertisement */
283 if (advertise_map & ADVERTISED_10000baseT_Full)
285 err = t3_mdio_change_bits(phy, MDIO_DEV_ANEG, AQ_10G_CTRL,
290 /* 1G advertisement */
292 if (advertise_map & ADVERTISED_1000baseT_Full)
294 if (advertise_map & ADVERTISED_1000baseT_Half)
296 err = t3_mdio_change_bits(phy, MDIO_DEV_ANEG, AQ_1G_CTRL,
297 ADV_1G_FULL | ADV_1G_HALF, adv);
301 /* 100M, pause advertisement */
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)
310 adv |= ADVERTISE_PAUSE_ASYM;
311 err = t3_mdio_change_bits(phy, MDIO_DEV_ANEG, AQ_100M_CTRL, 0xfe0, adv);
317 aq100x_set_loopback(struct cphy *phy, int mmd, int dir, int enable)
319 return t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR,
320 BMCR_LOOPBACK, enable ? BMCR_LOOPBACK : 0);
324 aq100x_set_speed_duplex(struct cphy *phy, int speed, int duplex)
328 if (speed == SPEED_100)
330 else if (speed == SPEED_1000)
331 set = BMCR_SPEED1000;
332 else if (speed == SPEED_10000)
333 set = BMCR_SPEED1000 | BMCR_SPEED100;
337 if (duplex != DUPLEX_FULL)
340 err = t3_mdio_change_bits(phy, MDIO_DEV_ANEG, MII_BMCR,
341 BMCR_RESET | BMCR_ANENABLE | BMCR_ANRESTART, 0);
345 err = t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR,
346 BMCR_SPEED1000 | BMCR_SPEED100, set);
354 aq100x_get_link_status(struct cphy *phy, int *link_state, int *speed, int *duplex,
358 unsigned int v, link = 0;
360 err = mdio_read(phy, MDIO_DEV_PMA_PMD, AQ_LINK_STAT, &v);
363 if (v == 0xffff || !(v & 1))
366 err = mdio_read(phy, MDIO_DEV_ANEG, MII_BMCR, &v);
371 if (v & BMCR_ANENABLE) {
373 err = mdio_read(phy, MDIO_DEV_ANEG, 1, &v);
379 err = mdio_read(phy, MDIO_DEV_ANEG, AQ_ANEG_STAT, &v);
385 case 0x6: *speed = SPEED_10000;
387 case 0x4: *speed = SPEED_1000;
389 case 0x2: *speed = SPEED_100;
391 case 0x0: *speed = SPEED_10;
397 *duplex = v & 1 ? DUPLEX_FULL : DUPLEX_HALF;
400 unsigned int lpa, adv;
401 err = mdio_read(phy, MDIO_DEV_ANEG, 0x13, &lpa);
403 err = mdio_read(phy, MDIO_DEV_ANEG,
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 &&
412 adv & ADVERTISE_PAUSE_ASYM)
414 else if (lpa & ADVERTISE_PAUSE_ASYM &&
415 adv & ADVERTISE_PAUSE_CAP)
422 err = mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMCR, &v);
426 v &= BMCR_SPEED1000 | BMCR_SPEED100;
428 if (v == (BMCR_SPEED1000 | BMCR_SPEED100))
429 *speed = SPEED_10000;
430 else if (v == BMCR_SPEED1000)
432 else if (v == BMCR_SPEED100)
439 *duplex = DUPLEX_FULL;
445 *link_state = link ? PHY_LINK_UP : PHY_LINK_DOWN;
449 static struct cphy_ops aq100x_ops = {
450 .reset = aq100x_reset,
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,
465 t3_aq100x_phy_prep(pinfo_t *pinfo, int phy_addr,
466 const struct mdio_ops *mdio_ops)
468 struct cphy *phy = &pinfo->phy;
469 unsigned int v, v2, gpio, wait;
471 adapter_t *adapter = pinfo->adapter;
473 cphy_init(&pinfo->phy, adapter, pinfo, phy_addr, &aq100x_ops, mdio_ops,
474 SUPPORTED_1000baseT_Full | SUPPORTED_10000baseT_Full |
475 SUPPORTED_TP | SUPPORTED_Autoneg | SUPPORTED_AUI |
476 SUPPORTED_MISC_IRQ, "1000/10GBASE-T");
479 * Hard reset the PHY.
481 gpio = phy_addr ? F_GPIO10_OUT_VAL : F_GPIO6_OUT_VAL;
482 t3_set_reg_field(adapter, A_T3DBG_GPIO_EN, gpio, 0);
484 t3_set_reg_field(adapter, A_T3DBG_GPIO_EN, gpio, gpio);
487 * Give it enough time to load the firmware and get ready for mdio.
490 wait = 500; /* in 10ms increments */
492 err = mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMCR, &v);
493 if (err || v == 0xffff) {
495 /* Allow prep_adapter to succeed when ffff is read */
497 CH_WARN(adapter, "PHY%d: reset failed (0x%x, 0x%x).\n",
505 } while (v && --wait);
507 CH_WARN(adapter, "PHY%d: reset timed out (0x%x).\n",
510 goto done; /* let prep_adapter succeed */
513 /* Firmware version check. */
514 (void) mdio_read(phy, MDIO_DEV_VEND1, AQ_FW_VERSION, &v);
516 CH_WARN(adapter, "PHY%d: unknown firmware %d.%d\n", phy_addr,
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",
526 * Verify XAUI and 1000-X settings, but let prep succeed no matter what.
529 (void) mdio_read(phy, MDIO_DEV_XGXS, AQ_XAUI_RX_CFG, &v);
530 (void) mdio_read(phy, MDIO_DEV_XGXS, AQ_XAUI_TX_CFG, &v2);
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);
535 (void) mdio_read(phy, MDIO_DEV_XGXS, AQ_XAUI_KX_CFG, &v);
536 if ((v & 0xf) != 0xf)
537 CH_WARN(adapter, "PHY%d: incorrect 1000-X settings "
538 "(0x%x).\n", phy_addr, v);
540 (void) aq100x_set_defaults(phy);