937 lines · c
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 two5 * licenses. You may choose to be licensed under the terms of the GNU6 * General Public License (GPL) Version 2, available from the file7 * COPYING in the main directory of this source tree, or the8 * OpenIB.org BSD license below:9 *10 * Redistribution and use in source and binary forms, with or11 * without modification, are permitted provided that the following12 * conditions are met:13 *14 * - Redistributions of source code must retain the above15 * copyright notice, this list of conditions and the following16 * disclaimer.17 *18 * - Redistributions in binary form must reproduce the above19 * copyright notice, this list of conditions and the following20 * disclaimer in the documentation and/or other materials21 * 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 OF25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE30 * SOFTWARE.31 */32#include "common.h"33#include "regs.h"34 35enum {36 AEL100X_TX_CONFIG1 = 0xc002,37 AEL1002_PWR_DOWN_HI = 0xc011,38 AEL1002_PWR_DOWN_LO = 0xc012,39 AEL1002_XFI_EQL = 0xc015,40 AEL1002_LB_EN = 0xc017,41 AEL_OPT_SETTINGS = 0xc017,42 AEL_I2C_CTRL = 0xc30a,43 AEL_I2C_DATA = 0xc30b,44 AEL_I2C_STAT = 0xc30c,45 AEL2005_GPIO_CTRL = 0xc214,46 AEL2005_GPIO_STAT = 0xc215,47 48 AEL2020_GPIO_INTR = 0xc103, /* Latch High (LH) */49 AEL2020_GPIO_CTRL = 0xc108, /* Store Clear (SC) */50 AEL2020_GPIO_STAT = 0xc10c, /* Read Only (RO) */51 AEL2020_GPIO_CFG = 0xc110, /* Read Write (RW) */52 53 AEL2020_GPIO_SDA = 0, /* IN: i2c serial data */54 AEL2020_GPIO_MODDET = 1, /* IN: Module Detect */55 AEL2020_GPIO_0 = 3, /* IN: unassigned */56 AEL2020_GPIO_1 = 2, /* OUT: unassigned */57 AEL2020_GPIO_LSTAT = AEL2020_GPIO_1, /* wired to link status LED */58};59 60enum { edc_none, edc_sr, edc_twinax };61 62/* PHY module I2C device address */63enum {64 MODULE_DEV_ADDR = 0xa0,65 SFF_DEV_ADDR = 0xa2,66};67 68/* PHY transceiver type */69enum {70 phy_transtype_unknown = 0,71 phy_transtype_sfp = 3,72 phy_transtype_xfp = 6,73};74 75#define AEL2005_MODDET_IRQ 476 77struct reg_val {78 unsigned short mmd_addr;79 unsigned short reg_addr;80 unsigned short clear_bits;81 unsigned short set_bits;82};83 84static int set_phy_regs(struct cphy *phy, const struct reg_val *rv)85{86 int err;87 88 for (err = 0; rv->mmd_addr && !err; rv++) {89 if (rv->clear_bits == 0xffff)90 err = t3_mdio_write(phy, rv->mmd_addr, rv->reg_addr,91 rv->set_bits);92 else93 err = t3_mdio_change_bits(phy, rv->mmd_addr,94 rv->reg_addr, rv->clear_bits,95 rv->set_bits);96 }97 return err;98}99 100static void ael100x_txon(struct cphy *phy)101{102 int tx_on_gpio =103 phy->mdio.prtad == 0 ? F_GPIO7_OUT_VAL : F_GPIO2_OUT_VAL;104 105 msleep(100);106 t3_set_reg_field(phy->adapter, A_T3DBG_GPIO_EN, 0, tx_on_gpio);107 msleep(30);108}109 110/*111 * Read an 8-bit word from a device attached to the PHY's i2c bus.112 */113static int ael_i2c_rd(struct cphy *phy, int dev_addr, int word_addr)114{115 int i, err;116 unsigned int stat, data;117 118 err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL_I2C_CTRL,119 (dev_addr << 8) | (1 << 8) | word_addr);120 if (err)121 return err;122 123 for (i = 0; i < 200; i++) {124 msleep(1);125 err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL_I2C_STAT, &stat);126 if (err)127 return err;128 if ((stat & 3) == 1) {129 err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL_I2C_DATA,130 &data);131 if (err)132 return err;133 return data >> 8;134 }135 }136 CH_WARN(phy->adapter, "PHY %u i2c read of dev.addr %#x.%#x timed out\n",137 phy->mdio.prtad, dev_addr, word_addr);138 return -ETIMEDOUT;139}140 141static int ael1002_power_down(struct cphy *phy, int enable)142{143 int err;144 145 err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, MDIO_PMA_TXDIS, !!enable);146 if (!err)147 err = mdio_set_flag(&phy->mdio, phy->mdio.prtad,148 MDIO_MMD_PMAPMD, MDIO_CTRL1,149 MDIO_CTRL1_LPOWER, enable);150 return err;151}152 153static int ael1002_reset(struct cphy *phy, int wait)154{155 int err;156 157 if ((err = ael1002_power_down(phy, 0)) ||158 (err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL100X_TX_CONFIG1, 1)) ||159 (err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL1002_PWR_DOWN_HI, 0)) ||160 (err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL1002_PWR_DOWN_LO, 0)) ||161 (err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL1002_XFI_EQL, 0x18)) ||162 (err = t3_mdio_change_bits(phy, MDIO_MMD_PMAPMD, AEL1002_LB_EN,163 0, 1 << 5)))164 return err;165 return 0;166}167 168static int ael1002_intr_noop(struct cphy *phy)169{170 return 0;171}172 173/*174 * Get link status for a 10GBASE-R device.175 */176static int get_link_status_r(struct cphy *phy, int *link_ok, int *speed,177 int *duplex, int *fc)178{179 if (link_ok) {180 unsigned int stat0, stat1, stat2;181 int err = t3_mdio_read(phy, MDIO_MMD_PMAPMD,182 MDIO_PMA_RXDET, &stat0);183 184 if (!err)185 err = t3_mdio_read(phy, MDIO_MMD_PCS,186 MDIO_PCS_10GBRT_STAT1, &stat1);187 if (!err)188 err = t3_mdio_read(phy, MDIO_MMD_PHYXS,189 MDIO_PHYXS_LNSTAT, &stat2);190 if (err)191 return err;192 *link_ok = (stat0 & stat1 & (stat2 >> 12)) & 1;193 }194 if (speed)195 *speed = SPEED_10000;196 if (duplex)197 *duplex = DUPLEX_FULL;198 return 0;199}200 201static const struct cphy_ops ael1002_ops = {202 .reset = ael1002_reset,203 .intr_enable = ael1002_intr_noop,204 .intr_disable = ael1002_intr_noop,205 .intr_clear = ael1002_intr_noop,206 .intr_handler = ael1002_intr_noop,207 .get_link_status = get_link_status_r,208 .power_down = ael1002_power_down,209 .mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS,210};211 212int t3_ael1002_phy_prep(struct cphy *phy, struct adapter *adapter,213 int phy_addr, const struct mdio_ops *mdio_ops)214{215 cphy_init(phy, adapter, phy_addr, &ael1002_ops, mdio_ops,216 SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE,217 "10GBASE-R");218 ael100x_txon(phy);219 return 0;220}221 222static int ael1006_reset(struct cphy *phy, int wait)223{224 return t3_phy_reset(phy, MDIO_MMD_PMAPMD, wait);225}226 227static const struct cphy_ops ael1006_ops = {228 .reset = ael1006_reset,229 .intr_enable = t3_phy_lasi_intr_enable,230 .intr_disable = t3_phy_lasi_intr_disable,231 .intr_clear = t3_phy_lasi_intr_clear,232 .intr_handler = t3_phy_lasi_intr_handler,233 .get_link_status = get_link_status_r,234 .power_down = ael1002_power_down,235 .mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS,236};237 238int t3_ael1006_phy_prep(struct cphy *phy, struct adapter *adapter,239 int phy_addr, const struct mdio_ops *mdio_ops)240{241 cphy_init(phy, adapter, phy_addr, &ael1006_ops, mdio_ops,242 SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE,243 "10GBASE-SR");244 ael100x_txon(phy);245 return 0;246}247 248/*249 * Decode our module type.250 */251static int ael2xxx_get_module_type(struct cphy *phy, int delay_ms)252{253 int v;254 255 if (delay_ms)256 msleep(delay_ms);257 258 /* see SFF-8472 for below */259 v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 3);260 if (v < 0)261 return v;262 263 if (v == 0x10)264 return phy_modtype_sr;265 if (v == 0x20)266 return phy_modtype_lr;267 if (v == 0x40)268 return phy_modtype_lrm;269 270 v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 6);271 if (v < 0)272 return v;273 if (v != 4)274 goto unknown;275 276 v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 10);277 if (v < 0)278 return v;279 280 if (v & 0x80) {281 v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 0x12);282 if (v < 0)283 return v;284 return v > 10 ? phy_modtype_twinax_long : phy_modtype_twinax;285 }286unknown:287 return phy_modtype_unknown;288}289 290/*291 * Code to support the Aeluros/NetLogic 2005 10Gb PHY.292 */293static int ael2005_setup_sr_edc(struct cphy *phy)294{295 static const struct reg_val regs[] = {296 { MDIO_MMD_PMAPMD, 0xc003, 0xffff, 0x181 },297 { MDIO_MMD_PMAPMD, 0xc010, 0xffff, 0x448a },298 { MDIO_MMD_PMAPMD, 0xc04a, 0xffff, 0x5200 },299 { 0, 0, 0, 0 }300 };301 302 int i, err;303 304 err = set_phy_regs(phy, regs);305 if (err)306 return err;307 308 msleep(50);309 310 if (phy->priv != edc_sr)311 err = t3_get_edc_fw(phy, EDC_OPT_AEL2005,312 EDC_OPT_AEL2005_SIZE);313 if (err)314 return err;315 316 for (i = 0; i < EDC_OPT_AEL2005_SIZE / sizeof(u16) && !err; i += 2)317 err = t3_mdio_write(phy, MDIO_MMD_PMAPMD,318 phy->phy_cache[i],319 phy->phy_cache[i + 1]);320 if (!err)321 phy->priv = edc_sr;322 return err;323}324 325static int ael2005_setup_twinax_edc(struct cphy *phy, int modtype)326{327 static const struct reg_val regs[] = {328 { MDIO_MMD_PMAPMD, 0xc04a, 0xffff, 0x5a00 },329 { 0, 0, 0, 0 }330 };331 static const struct reg_val preemphasis[] = {332 { MDIO_MMD_PMAPMD, 0xc014, 0xffff, 0xfe16 },333 { MDIO_MMD_PMAPMD, 0xc015, 0xffff, 0xa000 },334 { 0, 0, 0, 0 }335 };336 int i, err;337 338 err = set_phy_regs(phy, regs);339 if (!err && modtype == phy_modtype_twinax_long)340 err = set_phy_regs(phy, preemphasis);341 if (err)342 return err;343 344 msleep(50);345 346 if (phy->priv != edc_twinax)347 err = t3_get_edc_fw(phy, EDC_TWX_AEL2005,348 EDC_TWX_AEL2005_SIZE);349 if (err)350 return err;351 352 for (i = 0; i < EDC_TWX_AEL2005_SIZE / sizeof(u16) && !err; i += 2)353 err = t3_mdio_write(phy, MDIO_MMD_PMAPMD,354 phy->phy_cache[i],355 phy->phy_cache[i + 1]);356 if (!err)357 phy->priv = edc_twinax;358 return err;359}360 361static int ael2005_get_module_type(struct cphy *phy, int delay_ms)362{363 int v;364 unsigned int stat;365 366 v = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL, &stat);367 if (v)368 return v;369 370 if (stat & (1 << 8)) /* module absent */371 return phy_modtype_none;372 373 return ael2xxx_get_module_type(phy, delay_ms);374}375 376static int ael2005_intr_enable(struct cphy *phy)377{378 int err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL, 0x200);379 return err ? err : t3_phy_lasi_intr_enable(phy);380}381 382static int ael2005_intr_disable(struct cphy *phy)383{384 int err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL, 0x100);385 return err ? err : t3_phy_lasi_intr_disable(phy);386}387 388static int ael2005_intr_clear(struct cphy *phy)389{390 int err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL, 0xd00);391 return err ? err : t3_phy_lasi_intr_clear(phy);392}393 394static int ael2005_reset(struct cphy *phy, int wait)395{396 static const struct reg_val regs0[] = {397 { MDIO_MMD_PMAPMD, 0xc001, 0, 1 << 5 },398 { MDIO_MMD_PMAPMD, 0xc017, 0, 1 << 5 },399 { MDIO_MMD_PMAPMD, 0xc013, 0xffff, 0xf341 },400 { MDIO_MMD_PMAPMD, 0xc210, 0xffff, 0x8000 },401 { MDIO_MMD_PMAPMD, 0xc210, 0xffff, 0x8100 },402 { MDIO_MMD_PMAPMD, 0xc210, 0xffff, 0x8000 },403 { MDIO_MMD_PMAPMD, 0xc210, 0xffff, 0 },404 { 0, 0, 0, 0 }405 };406 static const struct reg_val regs1[] = {407 { MDIO_MMD_PMAPMD, 0xca00, 0xffff, 0x0080 },408 { MDIO_MMD_PMAPMD, 0xca12, 0xffff, 0 },409 { 0, 0, 0, 0 }410 };411 412 int err;413 unsigned int lasi_ctrl;414 415 err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_CTRL,416 &lasi_ctrl);417 if (err)418 return err;419 420 err = t3_phy_reset(phy, MDIO_MMD_PMAPMD, 0);421 if (err)422 return err;423 424 msleep(125);425 phy->priv = edc_none;426 err = set_phy_regs(phy, regs0);427 if (err)428 return err;429 430 msleep(50);431 432 err = ael2005_get_module_type(phy, 0);433 if (err < 0)434 return err;435 phy->modtype = err;436 437 if (err == phy_modtype_twinax || err == phy_modtype_twinax_long)438 err = ael2005_setup_twinax_edc(phy, err);439 else440 err = ael2005_setup_sr_edc(phy);441 if (err)442 return err;443 444 err = set_phy_regs(phy, regs1);445 if (err)446 return err;447 448 /* reset wipes out interrupts, reenable them if they were on */449 if (lasi_ctrl & 1)450 err = ael2005_intr_enable(phy);451 return err;452}453 454static int ael2005_intr_handler(struct cphy *phy)455{456 unsigned int stat;457 int ret, edc_needed, cause = 0;458 459 ret = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_STAT, &stat);460 if (ret)461 return ret;462 463 if (stat & AEL2005_MODDET_IRQ) {464 ret = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL,465 0xd00);466 if (ret)467 return ret;468 469 /* modules have max 300 ms init time after hot plug */470 ret = ael2005_get_module_type(phy, 300);471 if (ret < 0)472 return ret;473 474 phy->modtype = ret;475 if (ret == phy_modtype_none)476 edc_needed = phy->priv; /* on unplug retain EDC */477 else if (ret == phy_modtype_twinax ||478 ret == phy_modtype_twinax_long)479 edc_needed = edc_twinax;480 else481 edc_needed = edc_sr;482 483 if (edc_needed != phy->priv) {484 ret = ael2005_reset(phy, 0);485 return ret ? ret : cphy_cause_module_change;486 }487 cause = cphy_cause_module_change;488 }489 490 ret = t3_phy_lasi_intr_handler(phy);491 if (ret < 0)492 return ret;493 494 ret |= cause;495 return ret ? ret : cphy_cause_link_change;496}497 498static const struct cphy_ops ael2005_ops = {499 .reset = ael2005_reset,500 .intr_enable = ael2005_intr_enable,501 .intr_disable = ael2005_intr_disable,502 .intr_clear = ael2005_intr_clear,503 .intr_handler = ael2005_intr_handler,504 .get_link_status = get_link_status_r,505 .power_down = ael1002_power_down,506 .mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS,507};508 509int t3_ael2005_phy_prep(struct cphy *phy, struct adapter *adapter,510 int phy_addr, const struct mdio_ops *mdio_ops)511{512 cphy_init(phy, adapter, phy_addr, &ael2005_ops, mdio_ops,513 SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE |514 SUPPORTED_IRQ, "10GBASE-R");515 msleep(125);516 return t3_mdio_change_bits(phy, MDIO_MMD_PMAPMD, AEL_OPT_SETTINGS, 0,517 1 << 5);518}519 520/*521 * Setup EDC and other parameters for operation with an optical module.522 */523static int ael2020_setup_sr_edc(struct cphy *phy)524{525 static const struct reg_val regs[] = {526 /* set CDR offset to 10 */527 { MDIO_MMD_PMAPMD, 0xcc01, 0xffff, 0x488a },528 529 /* adjust 10G RX bias current */530 { MDIO_MMD_PMAPMD, 0xcb1b, 0xffff, 0x0200 },531 { MDIO_MMD_PMAPMD, 0xcb1c, 0xffff, 0x00f0 },532 { MDIO_MMD_PMAPMD, 0xcc06, 0xffff, 0x00e0 },533 534 /* end */535 { 0, 0, 0, 0 }536 };537 int err;538 539 err = set_phy_regs(phy, regs);540 msleep(50);541 if (err)542 return err;543 544 phy->priv = edc_sr;545 return 0;546}547 548/*549 * Setup EDC and other parameters for operation with an TWINAX module.550 */551static int ael2020_setup_twinax_edc(struct cphy *phy, int modtype)552{553 /* set uC to 40MHz */554 static const struct reg_val uCclock40MHz[] = {555 { MDIO_MMD_PMAPMD, 0xff28, 0xffff, 0x4001 },556 { MDIO_MMD_PMAPMD, 0xff2a, 0xffff, 0x0002 },557 { 0, 0, 0, 0 }558 };559 560 /* activate uC clock */561 static const struct reg_val uCclockActivate[] = {562 { MDIO_MMD_PMAPMD, 0xd000, 0xffff, 0x5200 },563 { 0, 0, 0, 0 }564 };565 566 /* set PC to start of SRAM and activate uC */567 static const struct reg_val uCactivate[] = {568 { MDIO_MMD_PMAPMD, 0xd080, 0xffff, 0x0100 },569 { MDIO_MMD_PMAPMD, 0xd092, 0xffff, 0x0000 },570 { 0, 0, 0, 0 }571 };572 int i, err;573 574 /* set uC clock and activate it */575 err = set_phy_regs(phy, uCclock40MHz);576 msleep(500);577 if (err)578 return err;579 err = set_phy_regs(phy, uCclockActivate);580 msleep(500);581 if (err)582 return err;583 584 if (phy->priv != edc_twinax)585 err = t3_get_edc_fw(phy, EDC_TWX_AEL2020,586 EDC_TWX_AEL2020_SIZE);587 if (err)588 return err;589 590 for (i = 0; i < EDC_TWX_AEL2020_SIZE / sizeof(u16) && !err; i += 2)591 err = t3_mdio_write(phy, MDIO_MMD_PMAPMD,592 phy->phy_cache[i],593 phy->phy_cache[i + 1]);594 /* activate uC */595 err = set_phy_regs(phy, uCactivate);596 if (!err)597 phy->priv = edc_twinax;598 return err;599}600 601/*602 * Return Module Type.603 */604static int ael2020_get_module_type(struct cphy *phy, int delay_ms)605{606 int v;607 unsigned int stat;608 609 v = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2020_GPIO_STAT, &stat);610 if (v)611 return v;612 613 if (stat & (0x1 << (AEL2020_GPIO_MODDET*4))) {614 /* module absent */615 return phy_modtype_none;616 }617 618 return ael2xxx_get_module_type(phy, delay_ms);619}620 621/*622 * Enable PHY interrupts. We enable "Module Detection" interrupts (on any623 * state transition) and then generic Link Alarm Status Interrupt (LASI).624 */625static int ael2020_intr_enable(struct cphy *phy)626{627 static const struct reg_val regs[] = {628 /* output Module's Loss Of Signal (LOS) to LED */629 { MDIO_MMD_PMAPMD, AEL2020_GPIO_CFG+AEL2020_GPIO_LSTAT,630 0xffff, 0x4 },631 { MDIO_MMD_PMAPMD, AEL2020_GPIO_CTRL,632 0xffff, 0x8 << (AEL2020_GPIO_LSTAT*4) },633 634 /* enable module detect status change interrupts */635 { MDIO_MMD_PMAPMD, AEL2020_GPIO_CTRL,636 0xffff, 0x2 << (AEL2020_GPIO_MODDET*4) },637 638 /* end */639 { 0, 0, 0, 0 }640 };641 int err, link_ok = 0;642 643 /* set up "link status" LED and enable module change interrupts */644 err = set_phy_regs(phy, regs);645 if (err)646 return err;647 648 err = get_link_status_r(phy, &link_ok, NULL, NULL, NULL);649 if (err)650 return err;651 if (link_ok)652 t3_link_changed(phy->adapter,653 phy2portid(phy));654 655 err = t3_phy_lasi_intr_enable(phy);656 if (err)657 return err;658 659 return 0;660}661 662/*663 * Disable PHY interrupts. The mirror of the above ...664 */665static int ael2020_intr_disable(struct cphy *phy)666{667 static const struct reg_val regs[] = {668 /* reset "link status" LED to "off" */669 { MDIO_MMD_PMAPMD, AEL2020_GPIO_CTRL,670 0xffff, 0xb << (AEL2020_GPIO_LSTAT*4) },671 672 /* disable module detect status change interrupts */673 { MDIO_MMD_PMAPMD, AEL2020_GPIO_CTRL,674 0xffff, 0x1 << (AEL2020_GPIO_MODDET*4) },675 676 /* end */677 { 0, 0, 0, 0 }678 };679 int err;680 681 /* turn off "link status" LED and disable module change interrupts */682 err = set_phy_regs(phy, regs);683 if (err)684 return err;685 686 return t3_phy_lasi_intr_disable(phy);687}688 689/*690 * Clear PHY interrupt state.691 */692static int ael2020_intr_clear(struct cphy *phy)693{694 /*695 * The GPIO Interrupt register on the AEL2020 is a "Latching High"696 * (LH) register which is cleared to the current state when it's read.697 * Thus, we simply read the register and discard the result.698 */699 unsigned int stat;700 int err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2020_GPIO_INTR, &stat);701 return err ? err : t3_phy_lasi_intr_clear(phy);702}703 704static const struct reg_val ael2020_reset_regs[] = {705 /* Erratum #2: CDRLOL asserted, causing PMA link down status */706 { MDIO_MMD_PMAPMD, 0xc003, 0xffff, 0x3101 },707 708 /* force XAUI to send LF when RX_LOS is asserted */709 { MDIO_MMD_PMAPMD, 0xcd40, 0xffff, 0x0001 },710 711 /* allow writes to transceiver module EEPROM on i2c bus */712 { MDIO_MMD_PMAPMD, 0xff02, 0xffff, 0x0023 },713 { MDIO_MMD_PMAPMD, 0xff03, 0xffff, 0x0000 },714 { MDIO_MMD_PMAPMD, 0xff04, 0xffff, 0x0000 },715 716 /* end */717 { 0, 0, 0, 0 }718};719/*720 * Reset the PHY and put it into a canonical operating state.721 */722static int ael2020_reset(struct cphy *phy, int wait)723{724 int err;725 unsigned int lasi_ctrl;726 727 /* grab current interrupt state */728 err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_CTRL,729 &lasi_ctrl);730 if (err)731 return err;732 733 err = t3_phy_reset(phy, MDIO_MMD_PMAPMD, 125);734 if (err)735 return err;736 msleep(100);737 738 /* basic initialization for all module types */739 phy->priv = edc_none;740 err = set_phy_regs(phy, ael2020_reset_regs);741 if (err)742 return err;743 744 /* determine module type and perform appropriate initialization */745 err = ael2020_get_module_type(phy, 0);746 if (err < 0)747 return err;748 phy->modtype = (u8)err;749 if (err == phy_modtype_twinax || err == phy_modtype_twinax_long)750 err = ael2020_setup_twinax_edc(phy, err);751 else752 err = ael2020_setup_sr_edc(phy);753 if (err)754 return err;755 756 /* reset wipes out interrupts, reenable them if they were on */757 if (lasi_ctrl & 1)758 err = ael2005_intr_enable(phy);759 return err;760}761 762/*763 * Handle a PHY interrupt.764 */765static int ael2020_intr_handler(struct cphy *phy)766{767 unsigned int stat;768 int ret, edc_needed, cause = 0;769 770 ret = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2020_GPIO_INTR, &stat);771 if (ret)772 return ret;773 774 if (stat & (0x1 << AEL2020_GPIO_MODDET)) {775 /* modules have max 300 ms init time after hot plug */776 ret = ael2020_get_module_type(phy, 300);777 if (ret < 0)778 return ret;779 780 phy->modtype = (u8)ret;781 if (ret == phy_modtype_none)782 edc_needed = phy->priv; /* on unplug retain EDC */783 else if (ret == phy_modtype_twinax ||784 ret == phy_modtype_twinax_long)785 edc_needed = edc_twinax;786 else787 edc_needed = edc_sr;788 789 if (edc_needed != phy->priv) {790 ret = ael2020_reset(phy, 0);791 return ret ? ret : cphy_cause_module_change;792 }793 cause = cphy_cause_module_change;794 }795 796 ret = t3_phy_lasi_intr_handler(phy);797 if (ret < 0)798 return ret;799 800 ret |= cause;801 return ret ? ret : cphy_cause_link_change;802}803 804static const struct cphy_ops ael2020_ops = {805 .reset = ael2020_reset,806 .intr_enable = ael2020_intr_enable,807 .intr_disable = ael2020_intr_disable,808 .intr_clear = ael2020_intr_clear,809 .intr_handler = ael2020_intr_handler,810 .get_link_status = get_link_status_r,811 .power_down = ael1002_power_down,812 .mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS,813};814 815int t3_ael2020_phy_prep(struct cphy *phy, struct adapter *adapter, int phy_addr,816 const struct mdio_ops *mdio_ops)817{818 cphy_init(phy, adapter, phy_addr, &ael2020_ops, mdio_ops,819 SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE |820 SUPPORTED_IRQ, "10GBASE-R");821 msleep(125);822 823 return set_phy_regs(phy, ael2020_reset_regs);824}825 826/*827 * Get link status for a 10GBASE-X device.828 */829static int get_link_status_x(struct cphy *phy, int *link_ok, int *speed,830 int *duplex, int *fc)831{832 if (link_ok) {833 unsigned int stat0, stat1, stat2;834 int err = t3_mdio_read(phy, MDIO_MMD_PMAPMD,835 MDIO_PMA_RXDET, &stat0);836 837 if (!err)838 err = t3_mdio_read(phy, MDIO_MMD_PCS,839 MDIO_PCS_10GBX_STAT1, &stat1);840 if (!err)841 err = t3_mdio_read(phy, MDIO_MMD_PHYXS,842 MDIO_PHYXS_LNSTAT, &stat2);843 if (err)844 return err;845 *link_ok = (stat0 & (stat1 >> 12) & (stat2 >> 12)) & 1;846 }847 if (speed)848 *speed = SPEED_10000;849 if (duplex)850 *duplex = DUPLEX_FULL;851 return 0;852}853 854static const struct cphy_ops qt2045_ops = {855 .reset = ael1006_reset,856 .intr_enable = t3_phy_lasi_intr_enable,857 .intr_disable = t3_phy_lasi_intr_disable,858 .intr_clear = t3_phy_lasi_intr_clear,859 .intr_handler = t3_phy_lasi_intr_handler,860 .get_link_status = get_link_status_x,861 .power_down = ael1002_power_down,862 .mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS,863};864 865int t3_qt2045_phy_prep(struct cphy *phy, struct adapter *adapter,866 int phy_addr, const struct mdio_ops *mdio_ops)867{868 unsigned int stat;869 870 cphy_init(phy, adapter, phy_addr, &qt2045_ops, mdio_ops,871 SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_TP,872 "10GBASE-CX4");873 874 /*875 * Some cards where the PHY is supposed to be at address 0 actually876 * have it at 1.877 */878 if (!phy_addr &&879 !t3_mdio_read(phy, MDIO_MMD_PMAPMD, MDIO_STAT1, &stat) &&880 stat == 0xffff)881 phy->mdio.prtad = 1;882 return 0;883}884 885static int xaui_direct_reset(struct cphy *phy, int wait)886{887 return 0;888}889 890static int xaui_direct_get_link_status(struct cphy *phy, int *link_ok,891 int *speed, int *duplex, int *fc)892{893 if (link_ok) {894 unsigned int status;895 int prtad = phy->mdio.prtad;896 897 status = t3_read_reg(phy->adapter,898 XGM_REG(A_XGM_SERDES_STAT0, prtad)) |899 t3_read_reg(phy->adapter,900 XGM_REG(A_XGM_SERDES_STAT1, prtad)) |901 t3_read_reg(phy->adapter,902 XGM_REG(A_XGM_SERDES_STAT2, prtad)) |903 t3_read_reg(phy->adapter,904 XGM_REG(A_XGM_SERDES_STAT3, prtad));905 *link_ok = !(status & F_LOWSIG0);906 }907 if (speed)908 *speed = SPEED_10000;909 if (duplex)910 *duplex = DUPLEX_FULL;911 return 0;912}913 914static int xaui_direct_power_down(struct cphy *phy, int enable)915{916 return 0;917}918 919static const struct cphy_ops xaui_direct_ops = {920 .reset = xaui_direct_reset,921 .intr_enable = ael1002_intr_noop,922 .intr_disable = ael1002_intr_noop,923 .intr_clear = ael1002_intr_noop,924 .intr_handler = ael1002_intr_noop,925 .get_link_status = xaui_direct_get_link_status,926 .power_down = xaui_direct_power_down,927};928 929int t3_xaui_direct_phy_prep(struct cphy *phy, struct adapter *adapter,930 int phy_addr, const struct mdio_ops *mdio_ops)931{932 cphy_init(phy, adapter, phy_addr, &xaui_direct_ops, mdio_ops,933 SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_TP,934 "10GBASE-CX4");935 return 0;936}937