162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2005-2008 Chelsio, Inc. All rights reserved. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * This software is available to you under a choice of one of two 562306a36Sopenharmony_ci * licenses. You may choose to be licensed under the terms of the GNU 662306a36Sopenharmony_ci * General Public License (GPL) Version 2, available from the file 762306a36Sopenharmony_ci * COPYING in the main directory of this source tree, or the 862306a36Sopenharmony_ci * OpenIB.org BSD license below: 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or 1162306a36Sopenharmony_ci * without modification, are permitted provided that the following 1262306a36Sopenharmony_ci * conditions are met: 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * - Redistributions of source code must retain the above 1562306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 1662306a36Sopenharmony_ci * disclaimer. 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * - Redistributions in binary form must reproduce the above 1962306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 2062306a36Sopenharmony_ci * disclaimer in the documentation and/or other materials 2162306a36Sopenharmony_ci * provided with the distribution. 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 2462306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 2562306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 2662306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 2762306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 2862306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 2962306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 3062306a36Sopenharmony_ci * SOFTWARE. 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ci#include "common.h" 3362306a36Sopenharmony_ci#include "regs.h" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cienum { 3662306a36Sopenharmony_ci AEL100X_TX_CONFIG1 = 0xc002, 3762306a36Sopenharmony_ci AEL1002_PWR_DOWN_HI = 0xc011, 3862306a36Sopenharmony_ci AEL1002_PWR_DOWN_LO = 0xc012, 3962306a36Sopenharmony_ci AEL1002_XFI_EQL = 0xc015, 4062306a36Sopenharmony_ci AEL1002_LB_EN = 0xc017, 4162306a36Sopenharmony_ci AEL_OPT_SETTINGS = 0xc017, 4262306a36Sopenharmony_ci AEL_I2C_CTRL = 0xc30a, 4362306a36Sopenharmony_ci AEL_I2C_DATA = 0xc30b, 4462306a36Sopenharmony_ci AEL_I2C_STAT = 0xc30c, 4562306a36Sopenharmony_ci AEL2005_GPIO_CTRL = 0xc214, 4662306a36Sopenharmony_ci AEL2005_GPIO_STAT = 0xc215, 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci AEL2020_GPIO_INTR = 0xc103, /* Latch High (LH) */ 4962306a36Sopenharmony_ci AEL2020_GPIO_CTRL = 0xc108, /* Store Clear (SC) */ 5062306a36Sopenharmony_ci AEL2020_GPIO_STAT = 0xc10c, /* Read Only (RO) */ 5162306a36Sopenharmony_ci AEL2020_GPIO_CFG = 0xc110, /* Read Write (RW) */ 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci AEL2020_GPIO_SDA = 0, /* IN: i2c serial data */ 5462306a36Sopenharmony_ci AEL2020_GPIO_MODDET = 1, /* IN: Module Detect */ 5562306a36Sopenharmony_ci AEL2020_GPIO_0 = 3, /* IN: unassigned */ 5662306a36Sopenharmony_ci AEL2020_GPIO_1 = 2, /* OUT: unassigned */ 5762306a36Sopenharmony_ci AEL2020_GPIO_LSTAT = AEL2020_GPIO_1, /* wired to link status LED */ 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cienum { edc_none, edc_sr, edc_twinax }; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* PHY module I2C device address */ 6362306a36Sopenharmony_cienum { 6462306a36Sopenharmony_ci MODULE_DEV_ADDR = 0xa0, 6562306a36Sopenharmony_ci SFF_DEV_ADDR = 0xa2, 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/* PHY transceiver type */ 6962306a36Sopenharmony_cienum { 7062306a36Sopenharmony_ci phy_transtype_unknown = 0, 7162306a36Sopenharmony_ci phy_transtype_sfp = 3, 7262306a36Sopenharmony_ci phy_transtype_xfp = 6, 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci#define AEL2005_MODDET_IRQ 4 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistruct reg_val { 7862306a36Sopenharmony_ci unsigned short mmd_addr; 7962306a36Sopenharmony_ci unsigned short reg_addr; 8062306a36Sopenharmony_ci unsigned short clear_bits; 8162306a36Sopenharmony_ci unsigned short set_bits; 8262306a36Sopenharmony_ci}; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic int set_phy_regs(struct cphy *phy, const struct reg_val *rv) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci int err; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci for (err = 0; rv->mmd_addr && !err; rv++) { 8962306a36Sopenharmony_ci if (rv->clear_bits == 0xffff) 9062306a36Sopenharmony_ci err = t3_mdio_write(phy, rv->mmd_addr, rv->reg_addr, 9162306a36Sopenharmony_ci rv->set_bits); 9262306a36Sopenharmony_ci else 9362306a36Sopenharmony_ci err = t3_mdio_change_bits(phy, rv->mmd_addr, 9462306a36Sopenharmony_ci rv->reg_addr, rv->clear_bits, 9562306a36Sopenharmony_ci rv->set_bits); 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci return err; 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic void ael100x_txon(struct cphy *phy) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci int tx_on_gpio = 10362306a36Sopenharmony_ci phy->mdio.prtad == 0 ? F_GPIO7_OUT_VAL : F_GPIO2_OUT_VAL; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci msleep(100); 10662306a36Sopenharmony_ci t3_set_reg_field(phy->adapter, A_T3DBG_GPIO_EN, 0, tx_on_gpio); 10762306a36Sopenharmony_ci msleep(30); 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci/* 11162306a36Sopenharmony_ci * Read an 8-bit word from a device attached to the PHY's i2c bus. 11262306a36Sopenharmony_ci */ 11362306a36Sopenharmony_cistatic int ael_i2c_rd(struct cphy *phy, int dev_addr, int word_addr) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci int i, err; 11662306a36Sopenharmony_ci unsigned int stat, data; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL_I2C_CTRL, 11962306a36Sopenharmony_ci (dev_addr << 8) | (1 << 8) | word_addr); 12062306a36Sopenharmony_ci if (err) 12162306a36Sopenharmony_ci return err; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci for (i = 0; i < 200; i++) { 12462306a36Sopenharmony_ci msleep(1); 12562306a36Sopenharmony_ci err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL_I2C_STAT, &stat); 12662306a36Sopenharmony_ci if (err) 12762306a36Sopenharmony_ci return err; 12862306a36Sopenharmony_ci if ((stat & 3) == 1) { 12962306a36Sopenharmony_ci err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL_I2C_DATA, 13062306a36Sopenharmony_ci &data); 13162306a36Sopenharmony_ci if (err) 13262306a36Sopenharmony_ci return err; 13362306a36Sopenharmony_ci return data >> 8; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci CH_WARN(phy->adapter, "PHY %u i2c read of dev.addr %#x.%#x timed out\n", 13762306a36Sopenharmony_ci phy->mdio.prtad, dev_addr, word_addr); 13862306a36Sopenharmony_ci return -ETIMEDOUT; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic int ael1002_power_down(struct cphy *phy, int enable) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci int err; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, MDIO_PMA_TXDIS, !!enable); 14662306a36Sopenharmony_ci if (!err) 14762306a36Sopenharmony_ci err = mdio_set_flag(&phy->mdio, phy->mdio.prtad, 14862306a36Sopenharmony_ci MDIO_MMD_PMAPMD, MDIO_CTRL1, 14962306a36Sopenharmony_ci MDIO_CTRL1_LPOWER, enable); 15062306a36Sopenharmony_ci return err; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic int ael1002_reset(struct cphy *phy, int wait) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci int err; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if ((err = ael1002_power_down(phy, 0)) || 15862306a36Sopenharmony_ci (err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL100X_TX_CONFIG1, 1)) || 15962306a36Sopenharmony_ci (err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL1002_PWR_DOWN_HI, 0)) || 16062306a36Sopenharmony_ci (err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL1002_PWR_DOWN_LO, 0)) || 16162306a36Sopenharmony_ci (err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL1002_XFI_EQL, 0x18)) || 16262306a36Sopenharmony_ci (err = t3_mdio_change_bits(phy, MDIO_MMD_PMAPMD, AEL1002_LB_EN, 16362306a36Sopenharmony_ci 0, 1 << 5))) 16462306a36Sopenharmony_ci return err; 16562306a36Sopenharmony_ci return 0; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic int ael1002_intr_noop(struct cphy *phy) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci return 0; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci/* 17462306a36Sopenharmony_ci * Get link status for a 10GBASE-R device. 17562306a36Sopenharmony_ci */ 17662306a36Sopenharmony_cistatic int get_link_status_r(struct cphy *phy, int *link_ok, int *speed, 17762306a36Sopenharmony_ci int *duplex, int *fc) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci if (link_ok) { 18062306a36Sopenharmony_ci unsigned int stat0, stat1, stat2; 18162306a36Sopenharmony_ci int err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, 18262306a36Sopenharmony_ci MDIO_PMA_RXDET, &stat0); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (!err) 18562306a36Sopenharmony_ci err = t3_mdio_read(phy, MDIO_MMD_PCS, 18662306a36Sopenharmony_ci MDIO_PCS_10GBRT_STAT1, &stat1); 18762306a36Sopenharmony_ci if (!err) 18862306a36Sopenharmony_ci err = t3_mdio_read(phy, MDIO_MMD_PHYXS, 18962306a36Sopenharmony_ci MDIO_PHYXS_LNSTAT, &stat2); 19062306a36Sopenharmony_ci if (err) 19162306a36Sopenharmony_ci return err; 19262306a36Sopenharmony_ci *link_ok = (stat0 & stat1 & (stat2 >> 12)) & 1; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci if (speed) 19562306a36Sopenharmony_ci *speed = SPEED_10000; 19662306a36Sopenharmony_ci if (duplex) 19762306a36Sopenharmony_ci *duplex = DUPLEX_FULL; 19862306a36Sopenharmony_ci return 0; 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic const struct cphy_ops ael1002_ops = { 20262306a36Sopenharmony_ci .reset = ael1002_reset, 20362306a36Sopenharmony_ci .intr_enable = ael1002_intr_noop, 20462306a36Sopenharmony_ci .intr_disable = ael1002_intr_noop, 20562306a36Sopenharmony_ci .intr_clear = ael1002_intr_noop, 20662306a36Sopenharmony_ci .intr_handler = ael1002_intr_noop, 20762306a36Sopenharmony_ci .get_link_status = get_link_status_r, 20862306a36Sopenharmony_ci .power_down = ael1002_power_down, 20962306a36Sopenharmony_ci .mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS, 21062306a36Sopenharmony_ci}; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ciint t3_ael1002_phy_prep(struct cphy *phy, struct adapter *adapter, 21362306a36Sopenharmony_ci int phy_addr, const struct mdio_ops *mdio_ops) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci cphy_init(phy, adapter, phy_addr, &ael1002_ops, mdio_ops, 21662306a36Sopenharmony_ci SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE, 21762306a36Sopenharmony_ci "10GBASE-R"); 21862306a36Sopenharmony_ci ael100x_txon(phy); 21962306a36Sopenharmony_ci return 0; 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic int ael1006_reset(struct cphy *phy, int wait) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci return t3_phy_reset(phy, MDIO_MMD_PMAPMD, wait); 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic const struct cphy_ops ael1006_ops = { 22862306a36Sopenharmony_ci .reset = ael1006_reset, 22962306a36Sopenharmony_ci .intr_enable = t3_phy_lasi_intr_enable, 23062306a36Sopenharmony_ci .intr_disable = t3_phy_lasi_intr_disable, 23162306a36Sopenharmony_ci .intr_clear = t3_phy_lasi_intr_clear, 23262306a36Sopenharmony_ci .intr_handler = t3_phy_lasi_intr_handler, 23362306a36Sopenharmony_ci .get_link_status = get_link_status_r, 23462306a36Sopenharmony_ci .power_down = ael1002_power_down, 23562306a36Sopenharmony_ci .mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS, 23662306a36Sopenharmony_ci}; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ciint t3_ael1006_phy_prep(struct cphy *phy, struct adapter *adapter, 23962306a36Sopenharmony_ci int phy_addr, const struct mdio_ops *mdio_ops) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci cphy_init(phy, adapter, phy_addr, &ael1006_ops, mdio_ops, 24262306a36Sopenharmony_ci SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE, 24362306a36Sopenharmony_ci "10GBASE-SR"); 24462306a36Sopenharmony_ci ael100x_txon(phy); 24562306a36Sopenharmony_ci return 0; 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci/* 24962306a36Sopenharmony_ci * Decode our module type. 25062306a36Sopenharmony_ci */ 25162306a36Sopenharmony_cistatic int ael2xxx_get_module_type(struct cphy *phy, int delay_ms) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci int v; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (delay_ms) 25662306a36Sopenharmony_ci msleep(delay_ms); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* see SFF-8472 for below */ 25962306a36Sopenharmony_ci v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 3); 26062306a36Sopenharmony_ci if (v < 0) 26162306a36Sopenharmony_ci return v; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (v == 0x10) 26462306a36Sopenharmony_ci return phy_modtype_sr; 26562306a36Sopenharmony_ci if (v == 0x20) 26662306a36Sopenharmony_ci return phy_modtype_lr; 26762306a36Sopenharmony_ci if (v == 0x40) 26862306a36Sopenharmony_ci return phy_modtype_lrm; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 6); 27162306a36Sopenharmony_ci if (v < 0) 27262306a36Sopenharmony_ci return v; 27362306a36Sopenharmony_ci if (v != 4) 27462306a36Sopenharmony_ci goto unknown; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 10); 27762306a36Sopenharmony_ci if (v < 0) 27862306a36Sopenharmony_ci return v; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci if (v & 0x80) { 28162306a36Sopenharmony_ci v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 0x12); 28262306a36Sopenharmony_ci if (v < 0) 28362306a36Sopenharmony_ci return v; 28462306a36Sopenharmony_ci return v > 10 ? phy_modtype_twinax_long : phy_modtype_twinax; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ciunknown: 28762306a36Sopenharmony_ci return phy_modtype_unknown; 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci/* 29162306a36Sopenharmony_ci * Code to support the Aeluros/NetLogic 2005 10Gb PHY. 29262306a36Sopenharmony_ci */ 29362306a36Sopenharmony_cistatic int ael2005_setup_sr_edc(struct cphy *phy) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci static const struct reg_val regs[] = { 29662306a36Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xc003, 0xffff, 0x181 }, 29762306a36Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xc010, 0xffff, 0x448a }, 29862306a36Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xc04a, 0xffff, 0x5200 }, 29962306a36Sopenharmony_ci { 0, 0, 0, 0 } 30062306a36Sopenharmony_ci }; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci int i, err; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci err = set_phy_regs(phy, regs); 30562306a36Sopenharmony_ci if (err) 30662306a36Sopenharmony_ci return err; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci msleep(50); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (phy->priv != edc_sr) 31162306a36Sopenharmony_ci err = t3_get_edc_fw(phy, EDC_OPT_AEL2005, 31262306a36Sopenharmony_ci EDC_OPT_AEL2005_SIZE); 31362306a36Sopenharmony_ci if (err) 31462306a36Sopenharmony_ci return err; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci for (i = 0; i < EDC_OPT_AEL2005_SIZE / sizeof(u16) && !err; i += 2) 31762306a36Sopenharmony_ci err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, 31862306a36Sopenharmony_ci phy->phy_cache[i], 31962306a36Sopenharmony_ci phy->phy_cache[i + 1]); 32062306a36Sopenharmony_ci if (!err) 32162306a36Sopenharmony_ci phy->priv = edc_sr; 32262306a36Sopenharmony_ci return err; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic int ael2005_setup_twinax_edc(struct cphy *phy, int modtype) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci static const struct reg_val regs[] = { 32862306a36Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xc04a, 0xffff, 0x5a00 }, 32962306a36Sopenharmony_ci { 0, 0, 0, 0 } 33062306a36Sopenharmony_ci }; 33162306a36Sopenharmony_ci static const struct reg_val preemphasis[] = { 33262306a36Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xc014, 0xffff, 0xfe16 }, 33362306a36Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xc015, 0xffff, 0xa000 }, 33462306a36Sopenharmony_ci { 0, 0, 0, 0 } 33562306a36Sopenharmony_ci }; 33662306a36Sopenharmony_ci int i, err; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci err = set_phy_regs(phy, regs); 33962306a36Sopenharmony_ci if (!err && modtype == phy_modtype_twinax_long) 34062306a36Sopenharmony_ci err = set_phy_regs(phy, preemphasis); 34162306a36Sopenharmony_ci if (err) 34262306a36Sopenharmony_ci return err; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci msleep(50); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci if (phy->priv != edc_twinax) 34762306a36Sopenharmony_ci err = t3_get_edc_fw(phy, EDC_TWX_AEL2005, 34862306a36Sopenharmony_ci EDC_TWX_AEL2005_SIZE); 34962306a36Sopenharmony_ci if (err) 35062306a36Sopenharmony_ci return err; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci for (i = 0; i < EDC_TWX_AEL2005_SIZE / sizeof(u16) && !err; i += 2) 35362306a36Sopenharmony_ci err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, 35462306a36Sopenharmony_ci phy->phy_cache[i], 35562306a36Sopenharmony_ci phy->phy_cache[i + 1]); 35662306a36Sopenharmony_ci if (!err) 35762306a36Sopenharmony_ci phy->priv = edc_twinax; 35862306a36Sopenharmony_ci return err; 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistatic int ael2005_get_module_type(struct cphy *phy, int delay_ms) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci int v; 36462306a36Sopenharmony_ci unsigned int stat; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci v = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL, &stat); 36762306a36Sopenharmony_ci if (v) 36862306a36Sopenharmony_ci return v; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if (stat & (1 << 8)) /* module absent */ 37162306a36Sopenharmony_ci return phy_modtype_none; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci return ael2xxx_get_module_type(phy, delay_ms); 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_cistatic int ael2005_intr_enable(struct cphy *phy) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci int err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL, 0x200); 37962306a36Sopenharmony_ci return err ? err : t3_phy_lasi_intr_enable(phy); 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic int ael2005_intr_disable(struct cphy *phy) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci int err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL, 0x100); 38562306a36Sopenharmony_ci return err ? err : t3_phy_lasi_intr_disable(phy); 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistatic int ael2005_intr_clear(struct cphy *phy) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci int err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL, 0xd00); 39162306a36Sopenharmony_ci return err ? err : t3_phy_lasi_intr_clear(phy); 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic int ael2005_reset(struct cphy *phy, int wait) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci static const struct reg_val regs0[] = { 39762306a36Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xc001, 0, 1 << 5 }, 39862306a36Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xc017, 0, 1 << 5 }, 39962306a36Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xc013, 0xffff, 0xf341 }, 40062306a36Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xc210, 0xffff, 0x8000 }, 40162306a36Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xc210, 0xffff, 0x8100 }, 40262306a36Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xc210, 0xffff, 0x8000 }, 40362306a36Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xc210, 0xffff, 0 }, 40462306a36Sopenharmony_ci { 0, 0, 0, 0 } 40562306a36Sopenharmony_ci }; 40662306a36Sopenharmony_ci static const struct reg_val regs1[] = { 40762306a36Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xca00, 0xffff, 0x0080 }, 40862306a36Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xca12, 0xffff, 0 }, 40962306a36Sopenharmony_ci { 0, 0, 0, 0 } 41062306a36Sopenharmony_ci }; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci int err; 41362306a36Sopenharmony_ci unsigned int lasi_ctrl; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_CTRL, 41662306a36Sopenharmony_ci &lasi_ctrl); 41762306a36Sopenharmony_ci if (err) 41862306a36Sopenharmony_ci return err; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci err = t3_phy_reset(phy, MDIO_MMD_PMAPMD, 0); 42162306a36Sopenharmony_ci if (err) 42262306a36Sopenharmony_ci return err; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci msleep(125); 42562306a36Sopenharmony_ci phy->priv = edc_none; 42662306a36Sopenharmony_ci err = set_phy_regs(phy, regs0); 42762306a36Sopenharmony_ci if (err) 42862306a36Sopenharmony_ci return err; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci msleep(50); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci err = ael2005_get_module_type(phy, 0); 43362306a36Sopenharmony_ci if (err < 0) 43462306a36Sopenharmony_ci return err; 43562306a36Sopenharmony_ci phy->modtype = err; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci if (err == phy_modtype_twinax || err == phy_modtype_twinax_long) 43862306a36Sopenharmony_ci err = ael2005_setup_twinax_edc(phy, err); 43962306a36Sopenharmony_ci else 44062306a36Sopenharmony_ci err = ael2005_setup_sr_edc(phy); 44162306a36Sopenharmony_ci if (err) 44262306a36Sopenharmony_ci return err; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci err = set_phy_regs(phy, regs1); 44562306a36Sopenharmony_ci if (err) 44662306a36Sopenharmony_ci return err; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci /* reset wipes out interrupts, reenable them if they were on */ 44962306a36Sopenharmony_ci if (lasi_ctrl & 1) 45062306a36Sopenharmony_ci err = ael2005_intr_enable(phy); 45162306a36Sopenharmony_ci return err; 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic int ael2005_intr_handler(struct cphy *phy) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci unsigned int stat; 45762306a36Sopenharmony_ci int ret, edc_needed, cause = 0; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci ret = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_STAT, &stat); 46062306a36Sopenharmony_ci if (ret) 46162306a36Sopenharmony_ci return ret; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (stat & AEL2005_MODDET_IRQ) { 46462306a36Sopenharmony_ci ret = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL, 46562306a36Sopenharmony_ci 0xd00); 46662306a36Sopenharmony_ci if (ret) 46762306a36Sopenharmony_ci return ret; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci /* modules have max 300 ms init time after hot plug */ 47062306a36Sopenharmony_ci ret = ael2005_get_module_type(phy, 300); 47162306a36Sopenharmony_ci if (ret < 0) 47262306a36Sopenharmony_ci return ret; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci phy->modtype = ret; 47562306a36Sopenharmony_ci if (ret == phy_modtype_none) 47662306a36Sopenharmony_ci edc_needed = phy->priv; /* on unplug retain EDC */ 47762306a36Sopenharmony_ci else if (ret == phy_modtype_twinax || 47862306a36Sopenharmony_ci ret == phy_modtype_twinax_long) 47962306a36Sopenharmony_ci edc_needed = edc_twinax; 48062306a36Sopenharmony_ci else 48162306a36Sopenharmony_ci edc_needed = edc_sr; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci if (edc_needed != phy->priv) { 48462306a36Sopenharmony_ci ret = ael2005_reset(phy, 0); 48562306a36Sopenharmony_ci return ret ? ret : cphy_cause_module_change; 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci cause = cphy_cause_module_change; 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci ret = t3_phy_lasi_intr_handler(phy); 49162306a36Sopenharmony_ci if (ret < 0) 49262306a36Sopenharmony_ci return ret; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci ret |= cause; 49562306a36Sopenharmony_ci return ret ? ret : cphy_cause_link_change; 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_cistatic const struct cphy_ops ael2005_ops = { 49962306a36Sopenharmony_ci .reset = ael2005_reset, 50062306a36Sopenharmony_ci .intr_enable = ael2005_intr_enable, 50162306a36Sopenharmony_ci .intr_disable = ael2005_intr_disable, 50262306a36Sopenharmony_ci .intr_clear = ael2005_intr_clear, 50362306a36Sopenharmony_ci .intr_handler = ael2005_intr_handler, 50462306a36Sopenharmony_ci .get_link_status = get_link_status_r, 50562306a36Sopenharmony_ci .power_down = ael1002_power_down, 50662306a36Sopenharmony_ci .mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS, 50762306a36Sopenharmony_ci}; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ciint t3_ael2005_phy_prep(struct cphy *phy, struct adapter *adapter, 51062306a36Sopenharmony_ci int phy_addr, const struct mdio_ops *mdio_ops) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci cphy_init(phy, adapter, phy_addr, &ael2005_ops, mdio_ops, 51362306a36Sopenharmony_ci SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE | 51462306a36Sopenharmony_ci SUPPORTED_IRQ, "10GBASE-R"); 51562306a36Sopenharmony_ci msleep(125); 51662306a36Sopenharmony_ci return t3_mdio_change_bits(phy, MDIO_MMD_PMAPMD, AEL_OPT_SETTINGS, 0, 51762306a36Sopenharmony_ci 1 << 5); 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci/* 52162306a36Sopenharmony_ci * Setup EDC and other parameters for operation with an optical module. 52262306a36Sopenharmony_ci */ 52362306a36Sopenharmony_cistatic int ael2020_setup_sr_edc(struct cphy *phy) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci static const struct reg_val regs[] = { 52662306a36Sopenharmony_ci /* set CDR offset to 10 */ 52762306a36Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xcc01, 0xffff, 0x488a }, 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci /* adjust 10G RX bias current */ 53062306a36Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xcb1b, 0xffff, 0x0200 }, 53162306a36Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xcb1c, 0xffff, 0x00f0 }, 53262306a36Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xcc06, 0xffff, 0x00e0 }, 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci /* end */ 53562306a36Sopenharmony_ci { 0, 0, 0, 0 } 53662306a36Sopenharmony_ci }; 53762306a36Sopenharmony_ci int err; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci err = set_phy_regs(phy, regs); 54062306a36Sopenharmony_ci msleep(50); 54162306a36Sopenharmony_ci if (err) 54262306a36Sopenharmony_ci return err; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci phy->priv = edc_sr; 54562306a36Sopenharmony_ci return 0; 54662306a36Sopenharmony_ci} 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci/* 54962306a36Sopenharmony_ci * Setup EDC and other parameters for operation with an TWINAX module. 55062306a36Sopenharmony_ci */ 55162306a36Sopenharmony_cistatic int ael2020_setup_twinax_edc(struct cphy *phy, int modtype) 55262306a36Sopenharmony_ci{ 55362306a36Sopenharmony_ci /* set uC to 40MHz */ 55462306a36Sopenharmony_ci static const struct reg_val uCclock40MHz[] = { 55562306a36Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xff28, 0xffff, 0x4001 }, 55662306a36Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xff2a, 0xffff, 0x0002 }, 55762306a36Sopenharmony_ci { 0, 0, 0, 0 } 55862306a36Sopenharmony_ci }; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci /* activate uC clock */ 56162306a36Sopenharmony_ci static const struct reg_val uCclockActivate[] = { 56262306a36Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xd000, 0xffff, 0x5200 }, 56362306a36Sopenharmony_ci { 0, 0, 0, 0 } 56462306a36Sopenharmony_ci }; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci /* set PC to start of SRAM and activate uC */ 56762306a36Sopenharmony_ci static const struct reg_val uCactivate[] = { 56862306a36Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xd080, 0xffff, 0x0100 }, 56962306a36Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xd092, 0xffff, 0x0000 }, 57062306a36Sopenharmony_ci { 0, 0, 0, 0 } 57162306a36Sopenharmony_ci }; 57262306a36Sopenharmony_ci int i, err; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci /* set uC clock and activate it */ 57562306a36Sopenharmony_ci err = set_phy_regs(phy, uCclock40MHz); 57662306a36Sopenharmony_ci msleep(500); 57762306a36Sopenharmony_ci if (err) 57862306a36Sopenharmony_ci return err; 57962306a36Sopenharmony_ci err = set_phy_regs(phy, uCclockActivate); 58062306a36Sopenharmony_ci msleep(500); 58162306a36Sopenharmony_ci if (err) 58262306a36Sopenharmony_ci return err; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci if (phy->priv != edc_twinax) 58562306a36Sopenharmony_ci err = t3_get_edc_fw(phy, EDC_TWX_AEL2020, 58662306a36Sopenharmony_ci EDC_TWX_AEL2020_SIZE); 58762306a36Sopenharmony_ci if (err) 58862306a36Sopenharmony_ci return err; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci for (i = 0; i < EDC_TWX_AEL2020_SIZE / sizeof(u16) && !err; i += 2) 59162306a36Sopenharmony_ci err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, 59262306a36Sopenharmony_ci phy->phy_cache[i], 59362306a36Sopenharmony_ci phy->phy_cache[i + 1]); 59462306a36Sopenharmony_ci /* activate uC */ 59562306a36Sopenharmony_ci err = set_phy_regs(phy, uCactivate); 59662306a36Sopenharmony_ci if (!err) 59762306a36Sopenharmony_ci phy->priv = edc_twinax; 59862306a36Sopenharmony_ci return err; 59962306a36Sopenharmony_ci} 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci/* 60262306a36Sopenharmony_ci * Return Module Type. 60362306a36Sopenharmony_ci */ 60462306a36Sopenharmony_cistatic int ael2020_get_module_type(struct cphy *phy, int delay_ms) 60562306a36Sopenharmony_ci{ 60662306a36Sopenharmony_ci int v; 60762306a36Sopenharmony_ci unsigned int stat; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci v = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2020_GPIO_STAT, &stat); 61062306a36Sopenharmony_ci if (v) 61162306a36Sopenharmony_ci return v; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci if (stat & (0x1 << (AEL2020_GPIO_MODDET*4))) { 61462306a36Sopenharmony_ci /* module absent */ 61562306a36Sopenharmony_ci return phy_modtype_none; 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci return ael2xxx_get_module_type(phy, delay_ms); 61962306a36Sopenharmony_ci} 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci/* 62262306a36Sopenharmony_ci * Enable PHY interrupts. We enable "Module Detection" interrupts (on any 62362306a36Sopenharmony_ci * state transition) and then generic Link Alarm Status Interrupt (LASI). 62462306a36Sopenharmony_ci */ 62562306a36Sopenharmony_cistatic int ael2020_intr_enable(struct cphy *phy) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci static const struct reg_val regs[] = { 62862306a36Sopenharmony_ci /* output Module's Loss Of Signal (LOS) to LED */ 62962306a36Sopenharmony_ci { MDIO_MMD_PMAPMD, AEL2020_GPIO_CFG+AEL2020_GPIO_LSTAT, 63062306a36Sopenharmony_ci 0xffff, 0x4 }, 63162306a36Sopenharmony_ci { MDIO_MMD_PMAPMD, AEL2020_GPIO_CTRL, 63262306a36Sopenharmony_ci 0xffff, 0x8 << (AEL2020_GPIO_LSTAT*4) }, 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci /* enable module detect status change interrupts */ 63562306a36Sopenharmony_ci { MDIO_MMD_PMAPMD, AEL2020_GPIO_CTRL, 63662306a36Sopenharmony_ci 0xffff, 0x2 << (AEL2020_GPIO_MODDET*4) }, 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci /* end */ 63962306a36Sopenharmony_ci { 0, 0, 0, 0 } 64062306a36Sopenharmony_ci }; 64162306a36Sopenharmony_ci int err, link_ok = 0; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci /* set up "link status" LED and enable module change interrupts */ 64462306a36Sopenharmony_ci err = set_phy_regs(phy, regs); 64562306a36Sopenharmony_ci if (err) 64662306a36Sopenharmony_ci return err; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci err = get_link_status_r(phy, &link_ok, NULL, NULL, NULL); 64962306a36Sopenharmony_ci if (err) 65062306a36Sopenharmony_ci return err; 65162306a36Sopenharmony_ci if (link_ok) 65262306a36Sopenharmony_ci t3_link_changed(phy->adapter, 65362306a36Sopenharmony_ci phy2portid(phy)); 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci err = t3_phy_lasi_intr_enable(phy); 65662306a36Sopenharmony_ci if (err) 65762306a36Sopenharmony_ci return err; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci return 0; 66062306a36Sopenharmony_ci} 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci/* 66362306a36Sopenharmony_ci * Disable PHY interrupts. The mirror of the above ... 66462306a36Sopenharmony_ci */ 66562306a36Sopenharmony_cistatic int ael2020_intr_disable(struct cphy *phy) 66662306a36Sopenharmony_ci{ 66762306a36Sopenharmony_ci static const struct reg_val regs[] = { 66862306a36Sopenharmony_ci /* reset "link status" LED to "off" */ 66962306a36Sopenharmony_ci { MDIO_MMD_PMAPMD, AEL2020_GPIO_CTRL, 67062306a36Sopenharmony_ci 0xffff, 0xb << (AEL2020_GPIO_LSTAT*4) }, 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci /* disable module detect status change interrupts */ 67362306a36Sopenharmony_ci { MDIO_MMD_PMAPMD, AEL2020_GPIO_CTRL, 67462306a36Sopenharmony_ci 0xffff, 0x1 << (AEL2020_GPIO_MODDET*4) }, 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci /* end */ 67762306a36Sopenharmony_ci { 0, 0, 0, 0 } 67862306a36Sopenharmony_ci }; 67962306a36Sopenharmony_ci int err; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci /* turn off "link status" LED and disable module change interrupts */ 68262306a36Sopenharmony_ci err = set_phy_regs(phy, regs); 68362306a36Sopenharmony_ci if (err) 68462306a36Sopenharmony_ci return err; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci return t3_phy_lasi_intr_disable(phy); 68762306a36Sopenharmony_ci} 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci/* 69062306a36Sopenharmony_ci * Clear PHY interrupt state. 69162306a36Sopenharmony_ci */ 69262306a36Sopenharmony_cistatic int ael2020_intr_clear(struct cphy *phy) 69362306a36Sopenharmony_ci{ 69462306a36Sopenharmony_ci /* 69562306a36Sopenharmony_ci * The GPIO Interrupt register on the AEL2020 is a "Latching High" 69662306a36Sopenharmony_ci * (LH) register which is cleared to the current state when it's read. 69762306a36Sopenharmony_ci * Thus, we simply read the register and discard the result. 69862306a36Sopenharmony_ci */ 69962306a36Sopenharmony_ci unsigned int stat; 70062306a36Sopenharmony_ci int err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2020_GPIO_INTR, &stat); 70162306a36Sopenharmony_ci return err ? err : t3_phy_lasi_intr_clear(phy); 70262306a36Sopenharmony_ci} 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_cistatic const struct reg_val ael2020_reset_regs[] = { 70562306a36Sopenharmony_ci /* Erratum #2: CDRLOL asserted, causing PMA link down status */ 70662306a36Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xc003, 0xffff, 0x3101 }, 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci /* force XAUI to send LF when RX_LOS is asserted */ 70962306a36Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xcd40, 0xffff, 0x0001 }, 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci /* allow writes to transceiver module EEPROM on i2c bus */ 71262306a36Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xff02, 0xffff, 0x0023 }, 71362306a36Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xff03, 0xffff, 0x0000 }, 71462306a36Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xff04, 0xffff, 0x0000 }, 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci /* end */ 71762306a36Sopenharmony_ci { 0, 0, 0, 0 } 71862306a36Sopenharmony_ci}; 71962306a36Sopenharmony_ci/* 72062306a36Sopenharmony_ci * Reset the PHY and put it into a canonical operating state. 72162306a36Sopenharmony_ci */ 72262306a36Sopenharmony_cistatic int ael2020_reset(struct cphy *phy, int wait) 72362306a36Sopenharmony_ci{ 72462306a36Sopenharmony_ci int err; 72562306a36Sopenharmony_ci unsigned int lasi_ctrl; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci /* grab current interrupt state */ 72862306a36Sopenharmony_ci err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_CTRL, 72962306a36Sopenharmony_ci &lasi_ctrl); 73062306a36Sopenharmony_ci if (err) 73162306a36Sopenharmony_ci return err; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci err = t3_phy_reset(phy, MDIO_MMD_PMAPMD, 125); 73462306a36Sopenharmony_ci if (err) 73562306a36Sopenharmony_ci return err; 73662306a36Sopenharmony_ci msleep(100); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci /* basic initialization for all module types */ 73962306a36Sopenharmony_ci phy->priv = edc_none; 74062306a36Sopenharmony_ci err = set_phy_regs(phy, ael2020_reset_regs); 74162306a36Sopenharmony_ci if (err) 74262306a36Sopenharmony_ci return err; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci /* determine module type and perform appropriate initialization */ 74562306a36Sopenharmony_ci err = ael2020_get_module_type(phy, 0); 74662306a36Sopenharmony_ci if (err < 0) 74762306a36Sopenharmony_ci return err; 74862306a36Sopenharmony_ci phy->modtype = (u8)err; 74962306a36Sopenharmony_ci if (err == phy_modtype_twinax || err == phy_modtype_twinax_long) 75062306a36Sopenharmony_ci err = ael2020_setup_twinax_edc(phy, err); 75162306a36Sopenharmony_ci else 75262306a36Sopenharmony_ci err = ael2020_setup_sr_edc(phy); 75362306a36Sopenharmony_ci if (err) 75462306a36Sopenharmony_ci return err; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci /* reset wipes out interrupts, reenable them if they were on */ 75762306a36Sopenharmony_ci if (lasi_ctrl & 1) 75862306a36Sopenharmony_ci err = ael2005_intr_enable(phy); 75962306a36Sopenharmony_ci return err; 76062306a36Sopenharmony_ci} 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci/* 76362306a36Sopenharmony_ci * Handle a PHY interrupt. 76462306a36Sopenharmony_ci */ 76562306a36Sopenharmony_cistatic int ael2020_intr_handler(struct cphy *phy) 76662306a36Sopenharmony_ci{ 76762306a36Sopenharmony_ci unsigned int stat; 76862306a36Sopenharmony_ci int ret, edc_needed, cause = 0; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci ret = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2020_GPIO_INTR, &stat); 77162306a36Sopenharmony_ci if (ret) 77262306a36Sopenharmony_ci return ret; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci if (stat & (0x1 << AEL2020_GPIO_MODDET)) { 77562306a36Sopenharmony_ci /* modules have max 300 ms init time after hot plug */ 77662306a36Sopenharmony_ci ret = ael2020_get_module_type(phy, 300); 77762306a36Sopenharmony_ci if (ret < 0) 77862306a36Sopenharmony_ci return ret; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci phy->modtype = (u8)ret; 78162306a36Sopenharmony_ci if (ret == phy_modtype_none) 78262306a36Sopenharmony_ci edc_needed = phy->priv; /* on unplug retain EDC */ 78362306a36Sopenharmony_ci else if (ret == phy_modtype_twinax || 78462306a36Sopenharmony_ci ret == phy_modtype_twinax_long) 78562306a36Sopenharmony_ci edc_needed = edc_twinax; 78662306a36Sopenharmony_ci else 78762306a36Sopenharmony_ci edc_needed = edc_sr; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci if (edc_needed != phy->priv) { 79062306a36Sopenharmony_ci ret = ael2020_reset(phy, 0); 79162306a36Sopenharmony_ci return ret ? ret : cphy_cause_module_change; 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci cause = cphy_cause_module_change; 79462306a36Sopenharmony_ci } 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci ret = t3_phy_lasi_intr_handler(phy); 79762306a36Sopenharmony_ci if (ret < 0) 79862306a36Sopenharmony_ci return ret; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci ret |= cause; 80162306a36Sopenharmony_ci return ret ? ret : cphy_cause_link_change; 80262306a36Sopenharmony_ci} 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_cistatic const struct cphy_ops ael2020_ops = { 80562306a36Sopenharmony_ci .reset = ael2020_reset, 80662306a36Sopenharmony_ci .intr_enable = ael2020_intr_enable, 80762306a36Sopenharmony_ci .intr_disable = ael2020_intr_disable, 80862306a36Sopenharmony_ci .intr_clear = ael2020_intr_clear, 80962306a36Sopenharmony_ci .intr_handler = ael2020_intr_handler, 81062306a36Sopenharmony_ci .get_link_status = get_link_status_r, 81162306a36Sopenharmony_ci .power_down = ael1002_power_down, 81262306a36Sopenharmony_ci .mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS, 81362306a36Sopenharmony_ci}; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ciint t3_ael2020_phy_prep(struct cphy *phy, struct adapter *adapter, int phy_addr, 81662306a36Sopenharmony_ci const struct mdio_ops *mdio_ops) 81762306a36Sopenharmony_ci{ 81862306a36Sopenharmony_ci cphy_init(phy, adapter, phy_addr, &ael2020_ops, mdio_ops, 81962306a36Sopenharmony_ci SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE | 82062306a36Sopenharmony_ci SUPPORTED_IRQ, "10GBASE-R"); 82162306a36Sopenharmony_ci msleep(125); 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci return set_phy_regs(phy, ael2020_reset_regs); 82462306a36Sopenharmony_ci} 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci/* 82762306a36Sopenharmony_ci * Get link status for a 10GBASE-X device. 82862306a36Sopenharmony_ci */ 82962306a36Sopenharmony_cistatic int get_link_status_x(struct cphy *phy, int *link_ok, int *speed, 83062306a36Sopenharmony_ci int *duplex, int *fc) 83162306a36Sopenharmony_ci{ 83262306a36Sopenharmony_ci if (link_ok) { 83362306a36Sopenharmony_ci unsigned int stat0, stat1, stat2; 83462306a36Sopenharmony_ci int err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, 83562306a36Sopenharmony_ci MDIO_PMA_RXDET, &stat0); 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci if (!err) 83862306a36Sopenharmony_ci err = t3_mdio_read(phy, MDIO_MMD_PCS, 83962306a36Sopenharmony_ci MDIO_PCS_10GBX_STAT1, &stat1); 84062306a36Sopenharmony_ci if (!err) 84162306a36Sopenharmony_ci err = t3_mdio_read(phy, MDIO_MMD_PHYXS, 84262306a36Sopenharmony_ci MDIO_PHYXS_LNSTAT, &stat2); 84362306a36Sopenharmony_ci if (err) 84462306a36Sopenharmony_ci return err; 84562306a36Sopenharmony_ci *link_ok = (stat0 & (stat1 >> 12) & (stat2 >> 12)) & 1; 84662306a36Sopenharmony_ci } 84762306a36Sopenharmony_ci if (speed) 84862306a36Sopenharmony_ci *speed = SPEED_10000; 84962306a36Sopenharmony_ci if (duplex) 85062306a36Sopenharmony_ci *duplex = DUPLEX_FULL; 85162306a36Sopenharmony_ci return 0; 85262306a36Sopenharmony_ci} 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_cistatic const struct cphy_ops qt2045_ops = { 85562306a36Sopenharmony_ci .reset = ael1006_reset, 85662306a36Sopenharmony_ci .intr_enable = t3_phy_lasi_intr_enable, 85762306a36Sopenharmony_ci .intr_disable = t3_phy_lasi_intr_disable, 85862306a36Sopenharmony_ci .intr_clear = t3_phy_lasi_intr_clear, 85962306a36Sopenharmony_ci .intr_handler = t3_phy_lasi_intr_handler, 86062306a36Sopenharmony_ci .get_link_status = get_link_status_x, 86162306a36Sopenharmony_ci .power_down = ael1002_power_down, 86262306a36Sopenharmony_ci .mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS, 86362306a36Sopenharmony_ci}; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ciint t3_qt2045_phy_prep(struct cphy *phy, struct adapter *adapter, 86662306a36Sopenharmony_ci int phy_addr, const struct mdio_ops *mdio_ops) 86762306a36Sopenharmony_ci{ 86862306a36Sopenharmony_ci unsigned int stat; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci cphy_init(phy, adapter, phy_addr, &qt2045_ops, mdio_ops, 87162306a36Sopenharmony_ci SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_TP, 87262306a36Sopenharmony_ci "10GBASE-CX4"); 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci /* 87562306a36Sopenharmony_ci * Some cards where the PHY is supposed to be at address 0 actually 87662306a36Sopenharmony_ci * have it at 1. 87762306a36Sopenharmony_ci */ 87862306a36Sopenharmony_ci if (!phy_addr && 87962306a36Sopenharmony_ci !t3_mdio_read(phy, MDIO_MMD_PMAPMD, MDIO_STAT1, &stat) && 88062306a36Sopenharmony_ci stat == 0xffff) 88162306a36Sopenharmony_ci phy->mdio.prtad = 1; 88262306a36Sopenharmony_ci return 0; 88362306a36Sopenharmony_ci} 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_cistatic int xaui_direct_reset(struct cphy *phy, int wait) 88662306a36Sopenharmony_ci{ 88762306a36Sopenharmony_ci return 0; 88862306a36Sopenharmony_ci} 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_cistatic int xaui_direct_get_link_status(struct cphy *phy, int *link_ok, 89162306a36Sopenharmony_ci int *speed, int *duplex, int *fc) 89262306a36Sopenharmony_ci{ 89362306a36Sopenharmony_ci if (link_ok) { 89462306a36Sopenharmony_ci unsigned int status; 89562306a36Sopenharmony_ci int prtad = phy->mdio.prtad; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci status = t3_read_reg(phy->adapter, 89862306a36Sopenharmony_ci XGM_REG(A_XGM_SERDES_STAT0, prtad)) | 89962306a36Sopenharmony_ci t3_read_reg(phy->adapter, 90062306a36Sopenharmony_ci XGM_REG(A_XGM_SERDES_STAT1, prtad)) | 90162306a36Sopenharmony_ci t3_read_reg(phy->adapter, 90262306a36Sopenharmony_ci XGM_REG(A_XGM_SERDES_STAT2, prtad)) | 90362306a36Sopenharmony_ci t3_read_reg(phy->adapter, 90462306a36Sopenharmony_ci XGM_REG(A_XGM_SERDES_STAT3, prtad)); 90562306a36Sopenharmony_ci *link_ok = !(status & F_LOWSIG0); 90662306a36Sopenharmony_ci } 90762306a36Sopenharmony_ci if (speed) 90862306a36Sopenharmony_ci *speed = SPEED_10000; 90962306a36Sopenharmony_ci if (duplex) 91062306a36Sopenharmony_ci *duplex = DUPLEX_FULL; 91162306a36Sopenharmony_ci return 0; 91262306a36Sopenharmony_ci} 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_cistatic int xaui_direct_power_down(struct cphy *phy, int enable) 91562306a36Sopenharmony_ci{ 91662306a36Sopenharmony_ci return 0; 91762306a36Sopenharmony_ci} 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_cistatic const struct cphy_ops xaui_direct_ops = { 92062306a36Sopenharmony_ci .reset = xaui_direct_reset, 92162306a36Sopenharmony_ci .intr_enable = ael1002_intr_noop, 92262306a36Sopenharmony_ci .intr_disable = ael1002_intr_noop, 92362306a36Sopenharmony_ci .intr_clear = ael1002_intr_noop, 92462306a36Sopenharmony_ci .intr_handler = ael1002_intr_noop, 92562306a36Sopenharmony_ci .get_link_status = xaui_direct_get_link_status, 92662306a36Sopenharmony_ci .power_down = xaui_direct_power_down, 92762306a36Sopenharmony_ci}; 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ciint t3_xaui_direct_phy_prep(struct cphy *phy, struct adapter *adapter, 93062306a36Sopenharmony_ci int phy_addr, const struct mdio_ops *mdio_ops) 93162306a36Sopenharmony_ci{ 93262306a36Sopenharmony_ci cphy_init(phy, adapter, phy_addr, &xaui_direct_ops, mdio_ops, 93362306a36Sopenharmony_ci SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_TP, 93462306a36Sopenharmony_ci "10GBASE-CX4"); 93562306a36Sopenharmony_ci return 0; 93662306a36Sopenharmony_ci} 937