18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (c) 2005-2008 Chelsio, Inc. All rights reserved. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This software is available to you under a choice of one of two 58c2ecf20Sopenharmony_ci * licenses. You may choose to be licensed under the terms of the GNU 68c2ecf20Sopenharmony_ci * General Public License (GPL) Version 2, available from the file 78c2ecf20Sopenharmony_ci * COPYING in the main directory of this source tree, or the 88c2ecf20Sopenharmony_ci * OpenIB.org BSD license below: 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or 118c2ecf20Sopenharmony_ci * without modification, are permitted provided that the following 128c2ecf20Sopenharmony_ci * conditions are met: 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * - Redistributions of source code must retain the above 158c2ecf20Sopenharmony_ci * copyright notice, this list of conditions and the following 168c2ecf20Sopenharmony_ci * disclaimer. 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * - Redistributions in binary form must reproduce the above 198c2ecf20Sopenharmony_ci * copyright notice, this list of conditions and the following 208c2ecf20Sopenharmony_ci * disclaimer in the documentation and/or other materials 218c2ecf20Sopenharmony_ci * provided with the distribution. 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 248c2ecf20Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 258c2ecf20Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 268c2ecf20Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 278c2ecf20Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 288c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 298c2ecf20Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 308c2ecf20Sopenharmony_ci * SOFTWARE. 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_ci#include "common.h" 338c2ecf20Sopenharmony_ci#include "regs.h" 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cienum { 368c2ecf20Sopenharmony_ci AEL100X_TX_CONFIG1 = 0xc002, 378c2ecf20Sopenharmony_ci AEL1002_PWR_DOWN_HI = 0xc011, 388c2ecf20Sopenharmony_ci AEL1002_PWR_DOWN_LO = 0xc012, 398c2ecf20Sopenharmony_ci AEL1002_XFI_EQL = 0xc015, 408c2ecf20Sopenharmony_ci AEL1002_LB_EN = 0xc017, 418c2ecf20Sopenharmony_ci AEL_OPT_SETTINGS = 0xc017, 428c2ecf20Sopenharmony_ci AEL_I2C_CTRL = 0xc30a, 438c2ecf20Sopenharmony_ci AEL_I2C_DATA = 0xc30b, 448c2ecf20Sopenharmony_ci AEL_I2C_STAT = 0xc30c, 458c2ecf20Sopenharmony_ci AEL2005_GPIO_CTRL = 0xc214, 468c2ecf20Sopenharmony_ci AEL2005_GPIO_STAT = 0xc215, 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci AEL2020_GPIO_INTR = 0xc103, /* Latch High (LH) */ 498c2ecf20Sopenharmony_ci AEL2020_GPIO_CTRL = 0xc108, /* Store Clear (SC) */ 508c2ecf20Sopenharmony_ci AEL2020_GPIO_STAT = 0xc10c, /* Read Only (RO) */ 518c2ecf20Sopenharmony_ci AEL2020_GPIO_CFG = 0xc110, /* Read Write (RW) */ 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci AEL2020_GPIO_SDA = 0, /* IN: i2c serial data */ 548c2ecf20Sopenharmony_ci AEL2020_GPIO_MODDET = 1, /* IN: Module Detect */ 558c2ecf20Sopenharmony_ci AEL2020_GPIO_0 = 3, /* IN: unassigned */ 568c2ecf20Sopenharmony_ci AEL2020_GPIO_1 = 2, /* OUT: unassigned */ 578c2ecf20Sopenharmony_ci AEL2020_GPIO_LSTAT = AEL2020_GPIO_1, /* wired to link status LED */ 588c2ecf20Sopenharmony_ci}; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cienum { edc_none, edc_sr, edc_twinax }; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/* PHY module I2C device address */ 638c2ecf20Sopenharmony_cienum { 648c2ecf20Sopenharmony_ci MODULE_DEV_ADDR = 0xa0, 658c2ecf20Sopenharmony_ci SFF_DEV_ADDR = 0xa2, 668c2ecf20Sopenharmony_ci}; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* PHY transceiver type */ 698c2ecf20Sopenharmony_cienum { 708c2ecf20Sopenharmony_ci phy_transtype_unknown = 0, 718c2ecf20Sopenharmony_ci phy_transtype_sfp = 3, 728c2ecf20Sopenharmony_ci phy_transtype_xfp = 6, 738c2ecf20Sopenharmony_ci}; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci#define AEL2005_MODDET_IRQ 4 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistruct reg_val { 788c2ecf20Sopenharmony_ci unsigned short mmd_addr; 798c2ecf20Sopenharmony_ci unsigned short reg_addr; 808c2ecf20Sopenharmony_ci unsigned short clear_bits; 818c2ecf20Sopenharmony_ci unsigned short set_bits; 828c2ecf20Sopenharmony_ci}; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic int set_phy_regs(struct cphy *phy, const struct reg_val *rv) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci int err; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci for (err = 0; rv->mmd_addr && !err; rv++) { 898c2ecf20Sopenharmony_ci if (rv->clear_bits == 0xffff) 908c2ecf20Sopenharmony_ci err = t3_mdio_write(phy, rv->mmd_addr, rv->reg_addr, 918c2ecf20Sopenharmony_ci rv->set_bits); 928c2ecf20Sopenharmony_ci else 938c2ecf20Sopenharmony_ci err = t3_mdio_change_bits(phy, rv->mmd_addr, 948c2ecf20Sopenharmony_ci rv->reg_addr, rv->clear_bits, 958c2ecf20Sopenharmony_ci rv->set_bits); 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci return err; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic void ael100x_txon(struct cphy *phy) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci int tx_on_gpio = 1038c2ecf20Sopenharmony_ci phy->mdio.prtad == 0 ? F_GPIO7_OUT_VAL : F_GPIO2_OUT_VAL; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci msleep(100); 1068c2ecf20Sopenharmony_ci t3_set_reg_field(phy->adapter, A_T3DBG_GPIO_EN, 0, tx_on_gpio); 1078c2ecf20Sopenharmony_ci msleep(30); 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci/* 1118c2ecf20Sopenharmony_ci * Read an 8-bit word from a device attached to the PHY's i2c bus. 1128c2ecf20Sopenharmony_ci */ 1138c2ecf20Sopenharmony_cistatic int ael_i2c_rd(struct cphy *phy, int dev_addr, int word_addr) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci int i, err; 1168c2ecf20Sopenharmony_ci unsigned int stat, data; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL_I2C_CTRL, 1198c2ecf20Sopenharmony_ci (dev_addr << 8) | (1 << 8) | word_addr); 1208c2ecf20Sopenharmony_ci if (err) 1218c2ecf20Sopenharmony_ci return err; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci for (i = 0; i < 200; i++) { 1248c2ecf20Sopenharmony_ci msleep(1); 1258c2ecf20Sopenharmony_ci err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL_I2C_STAT, &stat); 1268c2ecf20Sopenharmony_ci if (err) 1278c2ecf20Sopenharmony_ci return err; 1288c2ecf20Sopenharmony_ci if ((stat & 3) == 1) { 1298c2ecf20Sopenharmony_ci err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL_I2C_DATA, 1308c2ecf20Sopenharmony_ci &data); 1318c2ecf20Sopenharmony_ci if (err) 1328c2ecf20Sopenharmony_ci return err; 1338c2ecf20Sopenharmony_ci return data >> 8; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci CH_WARN(phy->adapter, "PHY %u i2c read of dev.addr %#x.%#x timed out\n", 1378c2ecf20Sopenharmony_ci phy->mdio.prtad, dev_addr, word_addr); 1388c2ecf20Sopenharmony_ci return -ETIMEDOUT; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic int ael1002_power_down(struct cphy *phy, int enable) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci int err; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, MDIO_PMA_TXDIS, !!enable); 1468c2ecf20Sopenharmony_ci if (!err) 1478c2ecf20Sopenharmony_ci err = mdio_set_flag(&phy->mdio, phy->mdio.prtad, 1488c2ecf20Sopenharmony_ci MDIO_MMD_PMAPMD, MDIO_CTRL1, 1498c2ecf20Sopenharmony_ci MDIO_CTRL1_LPOWER, enable); 1508c2ecf20Sopenharmony_ci return err; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic int ael1002_reset(struct cphy *phy, int wait) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci int err; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if ((err = ael1002_power_down(phy, 0)) || 1588c2ecf20Sopenharmony_ci (err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL100X_TX_CONFIG1, 1)) || 1598c2ecf20Sopenharmony_ci (err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL1002_PWR_DOWN_HI, 0)) || 1608c2ecf20Sopenharmony_ci (err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL1002_PWR_DOWN_LO, 0)) || 1618c2ecf20Sopenharmony_ci (err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL1002_XFI_EQL, 0x18)) || 1628c2ecf20Sopenharmony_ci (err = t3_mdio_change_bits(phy, MDIO_MMD_PMAPMD, AEL1002_LB_EN, 1638c2ecf20Sopenharmony_ci 0, 1 << 5))) 1648c2ecf20Sopenharmony_ci return err; 1658c2ecf20Sopenharmony_ci return 0; 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic int ael1002_intr_noop(struct cphy *phy) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci return 0; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci/* 1748c2ecf20Sopenharmony_ci * Get link status for a 10GBASE-R device. 1758c2ecf20Sopenharmony_ci */ 1768c2ecf20Sopenharmony_cistatic int get_link_status_r(struct cphy *phy, int *link_ok, int *speed, 1778c2ecf20Sopenharmony_ci int *duplex, int *fc) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci if (link_ok) { 1808c2ecf20Sopenharmony_ci unsigned int stat0, stat1, stat2; 1818c2ecf20Sopenharmony_ci int err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, 1828c2ecf20Sopenharmony_ci MDIO_PMA_RXDET, &stat0); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci if (!err) 1858c2ecf20Sopenharmony_ci err = t3_mdio_read(phy, MDIO_MMD_PCS, 1868c2ecf20Sopenharmony_ci MDIO_PCS_10GBRT_STAT1, &stat1); 1878c2ecf20Sopenharmony_ci if (!err) 1888c2ecf20Sopenharmony_ci err = t3_mdio_read(phy, MDIO_MMD_PHYXS, 1898c2ecf20Sopenharmony_ci MDIO_PHYXS_LNSTAT, &stat2); 1908c2ecf20Sopenharmony_ci if (err) 1918c2ecf20Sopenharmony_ci return err; 1928c2ecf20Sopenharmony_ci *link_ok = (stat0 & stat1 & (stat2 >> 12)) & 1; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci if (speed) 1958c2ecf20Sopenharmony_ci *speed = SPEED_10000; 1968c2ecf20Sopenharmony_ci if (duplex) 1978c2ecf20Sopenharmony_ci *duplex = DUPLEX_FULL; 1988c2ecf20Sopenharmony_ci return 0; 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic const struct cphy_ops ael1002_ops = { 2028c2ecf20Sopenharmony_ci .reset = ael1002_reset, 2038c2ecf20Sopenharmony_ci .intr_enable = ael1002_intr_noop, 2048c2ecf20Sopenharmony_ci .intr_disable = ael1002_intr_noop, 2058c2ecf20Sopenharmony_ci .intr_clear = ael1002_intr_noop, 2068c2ecf20Sopenharmony_ci .intr_handler = ael1002_intr_noop, 2078c2ecf20Sopenharmony_ci .get_link_status = get_link_status_r, 2088c2ecf20Sopenharmony_ci .power_down = ael1002_power_down, 2098c2ecf20Sopenharmony_ci .mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS, 2108c2ecf20Sopenharmony_ci}; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ciint t3_ael1002_phy_prep(struct cphy *phy, struct adapter *adapter, 2138c2ecf20Sopenharmony_ci int phy_addr, const struct mdio_ops *mdio_ops) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci cphy_init(phy, adapter, phy_addr, &ael1002_ops, mdio_ops, 2168c2ecf20Sopenharmony_ci SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE, 2178c2ecf20Sopenharmony_ci "10GBASE-R"); 2188c2ecf20Sopenharmony_ci ael100x_txon(phy); 2198c2ecf20Sopenharmony_ci return 0; 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic int ael1006_reset(struct cphy *phy, int wait) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci return t3_phy_reset(phy, MDIO_MMD_PMAPMD, wait); 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic const struct cphy_ops ael1006_ops = { 2288c2ecf20Sopenharmony_ci .reset = ael1006_reset, 2298c2ecf20Sopenharmony_ci .intr_enable = t3_phy_lasi_intr_enable, 2308c2ecf20Sopenharmony_ci .intr_disable = t3_phy_lasi_intr_disable, 2318c2ecf20Sopenharmony_ci .intr_clear = t3_phy_lasi_intr_clear, 2328c2ecf20Sopenharmony_ci .intr_handler = t3_phy_lasi_intr_handler, 2338c2ecf20Sopenharmony_ci .get_link_status = get_link_status_r, 2348c2ecf20Sopenharmony_ci .power_down = ael1002_power_down, 2358c2ecf20Sopenharmony_ci .mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS, 2368c2ecf20Sopenharmony_ci}; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ciint t3_ael1006_phy_prep(struct cphy *phy, struct adapter *adapter, 2398c2ecf20Sopenharmony_ci int phy_addr, const struct mdio_ops *mdio_ops) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci cphy_init(phy, adapter, phy_addr, &ael1006_ops, mdio_ops, 2428c2ecf20Sopenharmony_ci SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE, 2438c2ecf20Sopenharmony_ci "10GBASE-SR"); 2448c2ecf20Sopenharmony_ci ael100x_txon(phy); 2458c2ecf20Sopenharmony_ci return 0; 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci/* 2498c2ecf20Sopenharmony_ci * Decode our module type. 2508c2ecf20Sopenharmony_ci */ 2518c2ecf20Sopenharmony_cistatic int ael2xxx_get_module_type(struct cphy *phy, int delay_ms) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci int v; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (delay_ms) 2568c2ecf20Sopenharmony_ci msleep(delay_ms); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci /* see SFF-8472 for below */ 2598c2ecf20Sopenharmony_ci v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 3); 2608c2ecf20Sopenharmony_ci if (v < 0) 2618c2ecf20Sopenharmony_ci return v; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci if (v == 0x10) 2648c2ecf20Sopenharmony_ci return phy_modtype_sr; 2658c2ecf20Sopenharmony_ci if (v == 0x20) 2668c2ecf20Sopenharmony_ci return phy_modtype_lr; 2678c2ecf20Sopenharmony_ci if (v == 0x40) 2688c2ecf20Sopenharmony_ci return phy_modtype_lrm; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 6); 2718c2ecf20Sopenharmony_ci if (v < 0) 2728c2ecf20Sopenharmony_ci return v; 2738c2ecf20Sopenharmony_ci if (v != 4) 2748c2ecf20Sopenharmony_ci goto unknown; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 10); 2778c2ecf20Sopenharmony_ci if (v < 0) 2788c2ecf20Sopenharmony_ci return v; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci if (v & 0x80) { 2818c2ecf20Sopenharmony_ci v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 0x12); 2828c2ecf20Sopenharmony_ci if (v < 0) 2838c2ecf20Sopenharmony_ci return v; 2848c2ecf20Sopenharmony_ci return v > 10 ? phy_modtype_twinax_long : phy_modtype_twinax; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ciunknown: 2878c2ecf20Sopenharmony_ci return phy_modtype_unknown; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci/* 2918c2ecf20Sopenharmony_ci * Code to support the Aeluros/NetLogic 2005 10Gb PHY. 2928c2ecf20Sopenharmony_ci */ 2938c2ecf20Sopenharmony_cistatic int ael2005_setup_sr_edc(struct cphy *phy) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci static const struct reg_val regs[] = { 2968c2ecf20Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xc003, 0xffff, 0x181 }, 2978c2ecf20Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xc010, 0xffff, 0x448a }, 2988c2ecf20Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xc04a, 0xffff, 0x5200 }, 2998c2ecf20Sopenharmony_ci { 0, 0, 0, 0 } 3008c2ecf20Sopenharmony_ci }; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci int i, err; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci err = set_phy_regs(phy, regs); 3058c2ecf20Sopenharmony_ci if (err) 3068c2ecf20Sopenharmony_ci return err; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci msleep(50); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (phy->priv != edc_sr) 3118c2ecf20Sopenharmony_ci err = t3_get_edc_fw(phy, EDC_OPT_AEL2005, 3128c2ecf20Sopenharmony_ci EDC_OPT_AEL2005_SIZE); 3138c2ecf20Sopenharmony_ci if (err) 3148c2ecf20Sopenharmony_ci return err; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci for (i = 0; i < EDC_OPT_AEL2005_SIZE / sizeof(u16) && !err; i += 2) 3178c2ecf20Sopenharmony_ci err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, 3188c2ecf20Sopenharmony_ci phy->phy_cache[i], 3198c2ecf20Sopenharmony_ci phy->phy_cache[i + 1]); 3208c2ecf20Sopenharmony_ci if (!err) 3218c2ecf20Sopenharmony_ci phy->priv = edc_sr; 3228c2ecf20Sopenharmony_ci return err; 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic int ael2005_setup_twinax_edc(struct cphy *phy, int modtype) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci static const struct reg_val regs[] = { 3288c2ecf20Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xc04a, 0xffff, 0x5a00 }, 3298c2ecf20Sopenharmony_ci { 0, 0, 0, 0 } 3308c2ecf20Sopenharmony_ci }; 3318c2ecf20Sopenharmony_ci static const struct reg_val preemphasis[] = { 3328c2ecf20Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xc014, 0xffff, 0xfe16 }, 3338c2ecf20Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xc015, 0xffff, 0xa000 }, 3348c2ecf20Sopenharmony_ci { 0, 0, 0, 0 } 3358c2ecf20Sopenharmony_ci }; 3368c2ecf20Sopenharmony_ci int i, err; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci err = set_phy_regs(phy, regs); 3398c2ecf20Sopenharmony_ci if (!err && modtype == phy_modtype_twinax_long) 3408c2ecf20Sopenharmony_ci err = set_phy_regs(phy, preemphasis); 3418c2ecf20Sopenharmony_ci if (err) 3428c2ecf20Sopenharmony_ci return err; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci msleep(50); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci if (phy->priv != edc_twinax) 3478c2ecf20Sopenharmony_ci err = t3_get_edc_fw(phy, EDC_TWX_AEL2005, 3488c2ecf20Sopenharmony_ci EDC_TWX_AEL2005_SIZE); 3498c2ecf20Sopenharmony_ci if (err) 3508c2ecf20Sopenharmony_ci return err; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci for (i = 0; i < EDC_TWX_AEL2005_SIZE / sizeof(u16) && !err; i += 2) 3538c2ecf20Sopenharmony_ci err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, 3548c2ecf20Sopenharmony_ci phy->phy_cache[i], 3558c2ecf20Sopenharmony_ci phy->phy_cache[i + 1]); 3568c2ecf20Sopenharmony_ci if (!err) 3578c2ecf20Sopenharmony_ci phy->priv = edc_twinax; 3588c2ecf20Sopenharmony_ci return err; 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic int ael2005_get_module_type(struct cphy *phy, int delay_ms) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci int v; 3648c2ecf20Sopenharmony_ci unsigned int stat; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci v = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL, &stat); 3678c2ecf20Sopenharmony_ci if (v) 3688c2ecf20Sopenharmony_ci return v; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (stat & (1 << 8)) /* module absent */ 3718c2ecf20Sopenharmony_ci return phy_modtype_none; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci return ael2xxx_get_module_type(phy, delay_ms); 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cistatic int ael2005_intr_enable(struct cphy *phy) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci int err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL, 0x200); 3798c2ecf20Sopenharmony_ci return err ? err : t3_phy_lasi_intr_enable(phy); 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_cistatic int ael2005_intr_disable(struct cphy *phy) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci int err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL, 0x100); 3858c2ecf20Sopenharmony_ci return err ? err : t3_phy_lasi_intr_disable(phy); 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cistatic int ael2005_intr_clear(struct cphy *phy) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci int err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL, 0xd00); 3918c2ecf20Sopenharmony_ci return err ? err : t3_phy_lasi_intr_clear(phy); 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cistatic int ael2005_reset(struct cphy *phy, int wait) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci static const struct reg_val regs0[] = { 3978c2ecf20Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xc001, 0, 1 << 5 }, 3988c2ecf20Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xc017, 0, 1 << 5 }, 3998c2ecf20Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xc013, 0xffff, 0xf341 }, 4008c2ecf20Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xc210, 0xffff, 0x8000 }, 4018c2ecf20Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xc210, 0xffff, 0x8100 }, 4028c2ecf20Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xc210, 0xffff, 0x8000 }, 4038c2ecf20Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xc210, 0xffff, 0 }, 4048c2ecf20Sopenharmony_ci { 0, 0, 0, 0 } 4058c2ecf20Sopenharmony_ci }; 4068c2ecf20Sopenharmony_ci static const struct reg_val regs1[] = { 4078c2ecf20Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xca00, 0xffff, 0x0080 }, 4088c2ecf20Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xca12, 0xffff, 0 }, 4098c2ecf20Sopenharmony_ci { 0, 0, 0, 0 } 4108c2ecf20Sopenharmony_ci }; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci int err; 4138c2ecf20Sopenharmony_ci unsigned int lasi_ctrl; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_CTRL, 4168c2ecf20Sopenharmony_ci &lasi_ctrl); 4178c2ecf20Sopenharmony_ci if (err) 4188c2ecf20Sopenharmony_ci return err; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci err = t3_phy_reset(phy, MDIO_MMD_PMAPMD, 0); 4218c2ecf20Sopenharmony_ci if (err) 4228c2ecf20Sopenharmony_ci return err; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci msleep(125); 4258c2ecf20Sopenharmony_ci phy->priv = edc_none; 4268c2ecf20Sopenharmony_ci err = set_phy_regs(phy, regs0); 4278c2ecf20Sopenharmony_ci if (err) 4288c2ecf20Sopenharmony_ci return err; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci msleep(50); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci err = ael2005_get_module_type(phy, 0); 4338c2ecf20Sopenharmony_ci if (err < 0) 4348c2ecf20Sopenharmony_ci return err; 4358c2ecf20Sopenharmony_ci phy->modtype = err; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci if (err == phy_modtype_twinax || err == phy_modtype_twinax_long) 4388c2ecf20Sopenharmony_ci err = ael2005_setup_twinax_edc(phy, err); 4398c2ecf20Sopenharmony_ci else 4408c2ecf20Sopenharmony_ci err = ael2005_setup_sr_edc(phy); 4418c2ecf20Sopenharmony_ci if (err) 4428c2ecf20Sopenharmony_ci return err; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci err = set_phy_regs(phy, regs1); 4458c2ecf20Sopenharmony_ci if (err) 4468c2ecf20Sopenharmony_ci return err; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci /* reset wipes out interrupts, reenable them if they were on */ 4498c2ecf20Sopenharmony_ci if (lasi_ctrl & 1) 4508c2ecf20Sopenharmony_ci err = ael2005_intr_enable(phy); 4518c2ecf20Sopenharmony_ci return err; 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_cistatic int ael2005_intr_handler(struct cphy *phy) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci unsigned int stat; 4578c2ecf20Sopenharmony_ci int ret, edc_needed, cause = 0; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci ret = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_STAT, &stat); 4608c2ecf20Sopenharmony_ci if (ret) 4618c2ecf20Sopenharmony_ci return ret; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci if (stat & AEL2005_MODDET_IRQ) { 4648c2ecf20Sopenharmony_ci ret = t3_mdio_write(phy, MDIO_MMD_PMAPMD, AEL2005_GPIO_CTRL, 4658c2ecf20Sopenharmony_ci 0xd00); 4668c2ecf20Sopenharmony_ci if (ret) 4678c2ecf20Sopenharmony_ci return ret; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci /* modules have max 300 ms init time after hot plug */ 4708c2ecf20Sopenharmony_ci ret = ael2005_get_module_type(phy, 300); 4718c2ecf20Sopenharmony_ci if (ret < 0) 4728c2ecf20Sopenharmony_ci return ret; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci phy->modtype = ret; 4758c2ecf20Sopenharmony_ci if (ret == phy_modtype_none) 4768c2ecf20Sopenharmony_ci edc_needed = phy->priv; /* on unplug retain EDC */ 4778c2ecf20Sopenharmony_ci else if (ret == phy_modtype_twinax || 4788c2ecf20Sopenharmony_ci ret == phy_modtype_twinax_long) 4798c2ecf20Sopenharmony_ci edc_needed = edc_twinax; 4808c2ecf20Sopenharmony_ci else 4818c2ecf20Sopenharmony_ci edc_needed = edc_sr; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci if (edc_needed != phy->priv) { 4848c2ecf20Sopenharmony_ci ret = ael2005_reset(phy, 0); 4858c2ecf20Sopenharmony_ci return ret ? ret : cphy_cause_module_change; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci cause = cphy_cause_module_change; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci ret = t3_phy_lasi_intr_handler(phy); 4918c2ecf20Sopenharmony_ci if (ret < 0) 4928c2ecf20Sopenharmony_ci return ret; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci ret |= cause; 4958c2ecf20Sopenharmony_ci return ret ? ret : cphy_cause_link_change; 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cistatic const struct cphy_ops ael2005_ops = { 4998c2ecf20Sopenharmony_ci .reset = ael2005_reset, 5008c2ecf20Sopenharmony_ci .intr_enable = ael2005_intr_enable, 5018c2ecf20Sopenharmony_ci .intr_disable = ael2005_intr_disable, 5028c2ecf20Sopenharmony_ci .intr_clear = ael2005_intr_clear, 5038c2ecf20Sopenharmony_ci .intr_handler = ael2005_intr_handler, 5048c2ecf20Sopenharmony_ci .get_link_status = get_link_status_r, 5058c2ecf20Sopenharmony_ci .power_down = ael1002_power_down, 5068c2ecf20Sopenharmony_ci .mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS, 5078c2ecf20Sopenharmony_ci}; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ciint t3_ael2005_phy_prep(struct cphy *phy, struct adapter *adapter, 5108c2ecf20Sopenharmony_ci int phy_addr, const struct mdio_ops *mdio_ops) 5118c2ecf20Sopenharmony_ci{ 5128c2ecf20Sopenharmony_ci cphy_init(phy, adapter, phy_addr, &ael2005_ops, mdio_ops, 5138c2ecf20Sopenharmony_ci SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE | 5148c2ecf20Sopenharmony_ci SUPPORTED_IRQ, "10GBASE-R"); 5158c2ecf20Sopenharmony_ci msleep(125); 5168c2ecf20Sopenharmony_ci return t3_mdio_change_bits(phy, MDIO_MMD_PMAPMD, AEL_OPT_SETTINGS, 0, 5178c2ecf20Sopenharmony_ci 1 << 5); 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci/* 5218c2ecf20Sopenharmony_ci * Setup EDC and other parameters for operation with an optical module. 5228c2ecf20Sopenharmony_ci */ 5238c2ecf20Sopenharmony_cistatic int ael2020_setup_sr_edc(struct cphy *phy) 5248c2ecf20Sopenharmony_ci{ 5258c2ecf20Sopenharmony_ci static const struct reg_val regs[] = { 5268c2ecf20Sopenharmony_ci /* set CDR offset to 10 */ 5278c2ecf20Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xcc01, 0xffff, 0x488a }, 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci /* adjust 10G RX bias current */ 5308c2ecf20Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xcb1b, 0xffff, 0x0200 }, 5318c2ecf20Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xcb1c, 0xffff, 0x00f0 }, 5328c2ecf20Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xcc06, 0xffff, 0x00e0 }, 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci /* end */ 5358c2ecf20Sopenharmony_ci { 0, 0, 0, 0 } 5368c2ecf20Sopenharmony_ci }; 5378c2ecf20Sopenharmony_ci int err; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci err = set_phy_regs(phy, regs); 5408c2ecf20Sopenharmony_ci msleep(50); 5418c2ecf20Sopenharmony_ci if (err) 5428c2ecf20Sopenharmony_ci return err; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci phy->priv = edc_sr; 5458c2ecf20Sopenharmony_ci return 0; 5468c2ecf20Sopenharmony_ci} 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci/* 5498c2ecf20Sopenharmony_ci * Setup EDC and other parameters for operation with an TWINAX module. 5508c2ecf20Sopenharmony_ci */ 5518c2ecf20Sopenharmony_cistatic int ael2020_setup_twinax_edc(struct cphy *phy, int modtype) 5528c2ecf20Sopenharmony_ci{ 5538c2ecf20Sopenharmony_ci /* set uC to 40MHz */ 5548c2ecf20Sopenharmony_ci static const struct reg_val uCclock40MHz[] = { 5558c2ecf20Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xff28, 0xffff, 0x4001 }, 5568c2ecf20Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xff2a, 0xffff, 0x0002 }, 5578c2ecf20Sopenharmony_ci { 0, 0, 0, 0 } 5588c2ecf20Sopenharmony_ci }; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci /* activate uC clock */ 5618c2ecf20Sopenharmony_ci static const struct reg_val uCclockActivate[] = { 5628c2ecf20Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xd000, 0xffff, 0x5200 }, 5638c2ecf20Sopenharmony_ci { 0, 0, 0, 0 } 5648c2ecf20Sopenharmony_ci }; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci /* set PC to start of SRAM and activate uC */ 5678c2ecf20Sopenharmony_ci static const struct reg_val uCactivate[] = { 5688c2ecf20Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xd080, 0xffff, 0x0100 }, 5698c2ecf20Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xd092, 0xffff, 0x0000 }, 5708c2ecf20Sopenharmony_ci { 0, 0, 0, 0 } 5718c2ecf20Sopenharmony_ci }; 5728c2ecf20Sopenharmony_ci int i, err; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci /* set uC clock and activate it */ 5758c2ecf20Sopenharmony_ci err = set_phy_regs(phy, uCclock40MHz); 5768c2ecf20Sopenharmony_ci msleep(500); 5778c2ecf20Sopenharmony_ci if (err) 5788c2ecf20Sopenharmony_ci return err; 5798c2ecf20Sopenharmony_ci err = set_phy_regs(phy, uCclockActivate); 5808c2ecf20Sopenharmony_ci msleep(500); 5818c2ecf20Sopenharmony_ci if (err) 5828c2ecf20Sopenharmony_ci return err; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci if (phy->priv != edc_twinax) 5858c2ecf20Sopenharmony_ci err = t3_get_edc_fw(phy, EDC_TWX_AEL2020, 5868c2ecf20Sopenharmony_ci EDC_TWX_AEL2020_SIZE); 5878c2ecf20Sopenharmony_ci if (err) 5888c2ecf20Sopenharmony_ci return err; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci for (i = 0; i < EDC_TWX_AEL2020_SIZE / sizeof(u16) && !err; i += 2) 5918c2ecf20Sopenharmony_ci err = t3_mdio_write(phy, MDIO_MMD_PMAPMD, 5928c2ecf20Sopenharmony_ci phy->phy_cache[i], 5938c2ecf20Sopenharmony_ci phy->phy_cache[i + 1]); 5948c2ecf20Sopenharmony_ci /* activate uC */ 5958c2ecf20Sopenharmony_ci err = set_phy_regs(phy, uCactivate); 5968c2ecf20Sopenharmony_ci if (!err) 5978c2ecf20Sopenharmony_ci phy->priv = edc_twinax; 5988c2ecf20Sopenharmony_ci return err; 5998c2ecf20Sopenharmony_ci} 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci/* 6028c2ecf20Sopenharmony_ci * Return Module Type. 6038c2ecf20Sopenharmony_ci */ 6048c2ecf20Sopenharmony_cistatic int ael2020_get_module_type(struct cphy *phy, int delay_ms) 6058c2ecf20Sopenharmony_ci{ 6068c2ecf20Sopenharmony_ci int v; 6078c2ecf20Sopenharmony_ci unsigned int stat; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci v = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2020_GPIO_STAT, &stat); 6108c2ecf20Sopenharmony_ci if (v) 6118c2ecf20Sopenharmony_ci return v; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci if (stat & (0x1 << (AEL2020_GPIO_MODDET*4))) { 6148c2ecf20Sopenharmony_ci /* module absent */ 6158c2ecf20Sopenharmony_ci return phy_modtype_none; 6168c2ecf20Sopenharmony_ci } 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci return ael2xxx_get_module_type(phy, delay_ms); 6198c2ecf20Sopenharmony_ci} 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci/* 6228c2ecf20Sopenharmony_ci * Enable PHY interrupts. We enable "Module Detection" interrupts (on any 6238c2ecf20Sopenharmony_ci * state transition) and then generic Link Alarm Status Interrupt (LASI). 6248c2ecf20Sopenharmony_ci */ 6258c2ecf20Sopenharmony_cistatic int ael2020_intr_enable(struct cphy *phy) 6268c2ecf20Sopenharmony_ci{ 6278c2ecf20Sopenharmony_ci static const struct reg_val regs[] = { 6288c2ecf20Sopenharmony_ci /* output Module's Loss Of Signal (LOS) to LED */ 6298c2ecf20Sopenharmony_ci { MDIO_MMD_PMAPMD, AEL2020_GPIO_CFG+AEL2020_GPIO_LSTAT, 6308c2ecf20Sopenharmony_ci 0xffff, 0x4 }, 6318c2ecf20Sopenharmony_ci { MDIO_MMD_PMAPMD, AEL2020_GPIO_CTRL, 6328c2ecf20Sopenharmony_ci 0xffff, 0x8 << (AEL2020_GPIO_LSTAT*4) }, 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci /* enable module detect status change interrupts */ 6358c2ecf20Sopenharmony_ci { MDIO_MMD_PMAPMD, AEL2020_GPIO_CTRL, 6368c2ecf20Sopenharmony_ci 0xffff, 0x2 << (AEL2020_GPIO_MODDET*4) }, 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci /* end */ 6398c2ecf20Sopenharmony_ci { 0, 0, 0, 0 } 6408c2ecf20Sopenharmony_ci }; 6418c2ecf20Sopenharmony_ci int err, link_ok = 0; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci /* set up "link status" LED and enable module change interrupts */ 6448c2ecf20Sopenharmony_ci err = set_phy_regs(phy, regs); 6458c2ecf20Sopenharmony_ci if (err) 6468c2ecf20Sopenharmony_ci return err; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci err = get_link_status_r(phy, &link_ok, NULL, NULL, NULL); 6498c2ecf20Sopenharmony_ci if (err) 6508c2ecf20Sopenharmony_ci return err; 6518c2ecf20Sopenharmony_ci if (link_ok) 6528c2ecf20Sopenharmony_ci t3_link_changed(phy->adapter, 6538c2ecf20Sopenharmony_ci phy2portid(phy)); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci err = t3_phy_lasi_intr_enable(phy); 6568c2ecf20Sopenharmony_ci if (err) 6578c2ecf20Sopenharmony_ci return err; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci return 0; 6608c2ecf20Sopenharmony_ci} 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci/* 6638c2ecf20Sopenharmony_ci * Disable PHY interrupts. The mirror of the above ... 6648c2ecf20Sopenharmony_ci */ 6658c2ecf20Sopenharmony_cistatic int ael2020_intr_disable(struct cphy *phy) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci static const struct reg_val regs[] = { 6688c2ecf20Sopenharmony_ci /* reset "link status" LED to "off" */ 6698c2ecf20Sopenharmony_ci { MDIO_MMD_PMAPMD, AEL2020_GPIO_CTRL, 6708c2ecf20Sopenharmony_ci 0xffff, 0xb << (AEL2020_GPIO_LSTAT*4) }, 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci /* disable module detect status change interrupts */ 6738c2ecf20Sopenharmony_ci { MDIO_MMD_PMAPMD, AEL2020_GPIO_CTRL, 6748c2ecf20Sopenharmony_ci 0xffff, 0x1 << (AEL2020_GPIO_MODDET*4) }, 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci /* end */ 6778c2ecf20Sopenharmony_ci { 0, 0, 0, 0 } 6788c2ecf20Sopenharmony_ci }; 6798c2ecf20Sopenharmony_ci int err; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci /* turn off "link status" LED and disable module change interrupts */ 6828c2ecf20Sopenharmony_ci err = set_phy_regs(phy, regs); 6838c2ecf20Sopenharmony_ci if (err) 6848c2ecf20Sopenharmony_ci return err; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci return t3_phy_lasi_intr_disable(phy); 6878c2ecf20Sopenharmony_ci} 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci/* 6908c2ecf20Sopenharmony_ci * Clear PHY interrupt state. 6918c2ecf20Sopenharmony_ci */ 6928c2ecf20Sopenharmony_cistatic int ael2020_intr_clear(struct cphy *phy) 6938c2ecf20Sopenharmony_ci{ 6948c2ecf20Sopenharmony_ci /* 6958c2ecf20Sopenharmony_ci * The GPIO Interrupt register on the AEL2020 is a "Latching High" 6968c2ecf20Sopenharmony_ci * (LH) register which is cleared to the current state when it's read. 6978c2ecf20Sopenharmony_ci * Thus, we simply read the register and discard the result. 6988c2ecf20Sopenharmony_ci */ 6998c2ecf20Sopenharmony_ci unsigned int stat; 7008c2ecf20Sopenharmony_ci int err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2020_GPIO_INTR, &stat); 7018c2ecf20Sopenharmony_ci return err ? err : t3_phy_lasi_intr_clear(phy); 7028c2ecf20Sopenharmony_ci} 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_cistatic const struct reg_val ael2020_reset_regs[] = { 7058c2ecf20Sopenharmony_ci /* Erratum #2: CDRLOL asserted, causing PMA link down status */ 7068c2ecf20Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xc003, 0xffff, 0x3101 }, 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci /* force XAUI to send LF when RX_LOS is asserted */ 7098c2ecf20Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xcd40, 0xffff, 0x0001 }, 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci /* allow writes to transceiver module EEPROM on i2c bus */ 7128c2ecf20Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xff02, 0xffff, 0x0023 }, 7138c2ecf20Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xff03, 0xffff, 0x0000 }, 7148c2ecf20Sopenharmony_ci { MDIO_MMD_PMAPMD, 0xff04, 0xffff, 0x0000 }, 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci /* end */ 7178c2ecf20Sopenharmony_ci { 0, 0, 0, 0 } 7188c2ecf20Sopenharmony_ci}; 7198c2ecf20Sopenharmony_ci/* 7208c2ecf20Sopenharmony_ci * Reset the PHY and put it into a canonical operating state. 7218c2ecf20Sopenharmony_ci */ 7228c2ecf20Sopenharmony_cistatic int ael2020_reset(struct cphy *phy, int wait) 7238c2ecf20Sopenharmony_ci{ 7248c2ecf20Sopenharmony_ci int err; 7258c2ecf20Sopenharmony_ci unsigned int lasi_ctrl; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci /* grab current interrupt state */ 7288c2ecf20Sopenharmony_ci err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_CTRL, 7298c2ecf20Sopenharmony_ci &lasi_ctrl); 7308c2ecf20Sopenharmony_ci if (err) 7318c2ecf20Sopenharmony_ci return err; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci err = t3_phy_reset(phy, MDIO_MMD_PMAPMD, 125); 7348c2ecf20Sopenharmony_ci if (err) 7358c2ecf20Sopenharmony_ci return err; 7368c2ecf20Sopenharmony_ci msleep(100); 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci /* basic initialization for all module types */ 7398c2ecf20Sopenharmony_ci phy->priv = edc_none; 7408c2ecf20Sopenharmony_ci err = set_phy_regs(phy, ael2020_reset_regs); 7418c2ecf20Sopenharmony_ci if (err) 7428c2ecf20Sopenharmony_ci return err; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci /* determine module type and perform appropriate initialization */ 7458c2ecf20Sopenharmony_ci err = ael2020_get_module_type(phy, 0); 7468c2ecf20Sopenharmony_ci if (err < 0) 7478c2ecf20Sopenharmony_ci return err; 7488c2ecf20Sopenharmony_ci phy->modtype = (u8)err; 7498c2ecf20Sopenharmony_ci if (err == phy_modtype_twinax || err == phy_modtype_twinax_long) 7508c2ecf20Sopenharmony_ci err = ael2020_setup_twinax_edc(phy, err); 7518c2ecf20Sopenharmony_ci else 7528c2ecf20Sopenharmony_ci err = ael2020_setup_sr_edc(phy); 7538c2ecf20Sopenharmony_ci if (err) 7548c2ecf20Sopenharmony_ci return err; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci /* reset wipes out interrupts, reenable them if they were on */ 7578c2ecf20Sopenharmony_ci if (lasi_ctrl & 1) 7588c2ecf20Sopenharmony_ci err = ael2005_intr_enable(phy); 7598c2ecf20Sopenharmony_ci return err; 7608c2ecf20Sopenharmony_ci} 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci/* 7638c2ecf20Sopenharmony_ci * Handle a PHY interrupt. 7648c2ecf20Sopenharmony_ci */ 7658c2ecf20Sopenharmony_cistatic int ael2020_intr_handler(struct cphy *phy) 7668c2ecf20Sopenharmony_ci{ 7678c2ecf20Sopenharmony_ci unsigned int stat; 7688c2ecf20Sopenharmony_ci int ret, edc_needed, cause = 0; 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci ret = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2020_GPIO_INTR, &stat); 7718c2ecf20Sopenharmony_ci if (ret) 7728c2ecf20Sopenharmony_ci return ret; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci if (stat & (0x1 << AEL2020_GPIO_MODDET)) { 7758c2ecf20Sopenharmony_ci /* modules have max 300 ms init time after hot plug */ 7768c2ecf20Sopenharmony_ci ret = ael2020_get_module_type(phy, 300); 7778c2ecf20Sopenharmony_ci if (ret < 0) 7788c2ecf20Sopenharmony_ci return ret; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci phy->modtype = (u8)ret; 7818c2ecf20Sopenharmony_ci if (ret == phy_modtype_none) 7828c2ecf20Sopenharmony_ci edc_needed = phy->priv; /* on unplug retain EDC */ 7838c2ecf20Sopenharmony_ci else if (ret == phy_modtype_twinax || 7848c2ecf20Sopenharmony_ci ret == phy_modtype_twinax_long) 7858c2ecf20Sopenharmony_ci edc_needed = edc_twinax; 7868c2ecf20Sopenharmony_ci else 7878c2ecf20Sopenharmony_ci edc_needed = edc_sr; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci if (edc_needed != phy->priv) { 7908c2ecf20Sopenharmony_ci ret = ael2020_reset(phy, 0); 7918c2ecf20Sopenharmony_ci return ret ? ret : cphy_cause_module_change; 7928c2ecf20Sopenharmony_ci } 7938c2ecf20Sopenharmony_ci cause = cphy_cause_module_change; 7948c2ecf20Sopenharmony_ci } 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci ret = t3_phy_lasi_intr_handler(phy); 7978c2ecf20Sopenharmony_ci if (ret < 0) 7988c2ecf20Sopenharmony_ci return ret; 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci ret |= cause; 8018c2ecf20Sopenharmony_ci return ret ? ret : cphy_cause_link_change; 8028c2ecf20Sopenharmony_ci} 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_cistatic const struct cphy_ops ael2020_ops = { 8058c2ecf20Sopenharmony_ci .reset = ael2020_reset, 8068c2ecf20Sopenharmony_ci .intr_enable = ael2020_intr_enable, 8078c2ecf20Sopenharmony_ci .intr_disable = ael2020_intr_disable, 8088c2ecf20Sopenharmony_ci .intr_clear = ael2020_intr_clear, 8098c2ecf20Sopenharmony_ci .intr_handler = ael2020_intr_handler, 8108c2ecf20Sopenharmony_ci .get_link_status = get_link_status_r, 8118c2ecf20Sopenharmony_ci .power_down = ael1002_power_down, 8128c2ecf20Sopenharmony_ci .mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS, 8138c2ecf20Sopenharmony_ci}; 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ciint t3_ael2020_phy_prep(struct cphy *phy, struct adapter *adapter, int phy_addr, 8168c2ecf20Sopenharmony_ci const struct mdio_ops *mdio_ops) 8178c2ecf20Sopenharmony_ci{ 8188c2ecf20Sopenharmony_ci cphy_init(phy, adapter, phy_addr, &ael2020_ops, mdio_ops, 8198c2ecf20Sopenharmony_ci SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE | 8208c2ecf20Sopenharmony_ci SUPPORTED_IRQ, "10GBASE-R"); 8218c2ecf20Sopenharmony_ci msleep(125); 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci return set_phy_regs(phy, ael2020_reset_regs); 8248c2ecf20Sopenharmony_ci} 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci/* 8278c2ecf20Sopenharmony_ci * Get link status for a 10GBASE-X device. 8288c2ecf20Sopenharmony_ci */ 8298c2ecf20Sopenharmony_cistatic int get_link_status_x(struct cphy *phy, int *link_ok, int *speed, 8308c2ecf20Sopenharmony_ci int *duplex, int *fc) 8318c2ecf20Sopenharmony_ci{ 8328c2ecf20Sopenharmony_ci if (link_ok) { 8338c2ecf20Sopenharmony_ci unsigned int stat0, stat1, stat2; 8348c2ecf20Sopenharmony_ci int err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, 8358c2ecf20Sopenharmony_ci MDIO_PMA_RXDET, &stat0); 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci if (!err) 8388c2ecf20Sopenharmony_ci err = t3_mdio_read(phy, MDIO_MMD_PCS, 8398c2ecf20Sopenharmony_ci MDIO_PCS_10GBX_STAT1, &stat1); 8408c2ecf20Sopenharmony_ci if (!err) 8418c2ecf20Sopenharmony_ci err = t3_mdio_read(phy, MDIO_MMD_PHYXS, 8428c2ecf20Sopenharmony_ci MDIO_PHYXS_LNSTAT, &stat2); 8438c2ecf20Sopenharmony_ci if (err) 8448c2ecf20Sopenharmony_ci return err; 8458c2ecf20Sopenharmony_ci *link_ok = (stat0 & (stat1 >> 12) & (stat2 >> 12)) & 1; 8468c2ecf20Sopenharmony_ci } 8478c2ecf20Sopenharmony_ci if (speed) 8488c2ecf20Sopenharmony_ci *speed = SPEED_10000; 8498c2ecf20Sopenharmony_ci if (duplex) 8508c2ecf20Sopenharmony_ci *duplex = DUPLEX_FULL; 8518c2ecf20Sopenharmony_ci return 0; 8528c2ecf20Sopenharmony_ci} 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_cistatic const struct cphy_ops qt2045_ops = { 8558c2ecf20Sopenharmony_ci .reset = ael1006_reset, 8568c2ecf20Sopenharmony_ci .intr_enable = t3_phy_lasi_intr_enable, 8578c2ecf20Sopenharmony_ci .intr_disable = t3_phy_lasi_intr_disable, 8588c2ecf20Sopenharmony_ci .intr_clear = t3_phy_lasi_intr_clear, 8598c2ecf20Sopenharmony_ci .intr_handler = t3_phy_lasi_intr_handler, 8608c2ecf20Sopenharmony_ci .get_link_status = get_link_status_x, 8618c2ecf20Sopenharmony_ci .power_down = ael1002_power_down, 8628c2ecf20Sopenharmony_ci .mmds = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | MDIO_DEVS_PHYXS, 8638c2ecf20Sopenharmony_ci}; 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ciint t3_qt2045_phy_prep(struct cphy *phy, struct adapter *adapter, 8668c2ecf20Sopenharmony_ci int phy_addr, const struct mdio_ops *mdio_ops) 8678c2ecf20Sopenharmony_ci{ 8688c2ecf20Sopenharmony_ci unsigned int stat; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci cphy_init(phy, adapter, phy_addr, &qt2045_ops, mdio_ops, 8718c2ecf20Sopenharmony_ci SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_TP, 8728c2ecf20Sopenharmony_ci "10GBASE-CX4"); 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci /* 8758c2ecf20Sopenharmony_ci * Some cards where the PHY is supposed to be at address 0 actually 8768c2ecf20Sopenharmony_ci * have it at 1. 8778c2ecf20Sopenharmony_ci */ 8788c2ecf20Sopenharmony_ci if (!phy_addr && 8798c2ecf20Sopenharmony_ci !t3_mdio_read(phy, MDIO_MMD_PMAPMD, MDIO_STAT1, &stat) && 8808c2ecf20Sopenharmony_ci stat == 0xffff) 8818c2ecf20Sopenharmony_ci phy->mdio.prtad = 1; 8828c2ecf20Sopenharmony_ci return 0; 8838c2ecf20Sopenharmony_ci} 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_cistatic int xaui_direct_reset(struct cphy *phy, int wait) 8868c2ecf20Sopenharmony_ci{ 8878c2ecf20Sopenharmony_ci return 0; 8888c2ecf20Sopenharmony_ci} 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_cistatic int xaui_direct_get_link_status(struct cphy *phy, int *link_ok, 8918c2ecf20Sopenharmony_ci int *speed, int *duplex, int *fc) 8928c2ecf20Sopenharmony_ci{ 8938c2ecf20Sopenharmony_ci if (link_ok) { 8948c2ecf20Sopenharmony_ci unsigned int status; 8958c2ecf20Sopenharmony_ci int prtad = phy->mdio.prtad; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci status = t3_read_reg(phy->adapter, 8988c2ecf20Sopenharmony_ci XGM_REG(A_XGM_SERDES_STAT0, prtad)) | 8998c2ecf20Sopenharmony_ci t3_read_reg(phy->adapter, 9008c2ecf20Sopenharmony_ci XGM_REG(A_XGM_SERDES_STAT1, prtad)) | 9018c2ecf20Sopenharmony_ci t3_read_reg(phy->adapter, 9028c2ecf20Sopenharmony_ci XGM_REG(A_XGM_SERDES_STAT2, prtad)) | 9038c2ecf20Sopenharmony_ci t3_read_reg(phy->adapter, 9048c2ecf20Sopenharmony_ci XGM_REG(A_XGM_SERDES_STAT3, prtad)); 9058c2ecf20Sopenharmony_ci *link_ok = !(status & F_LOWSIG0); 9068c2ecf20Sopenharmony_ci } 9078c2ecf20Sopenharmony_ci if (speed) 9088c2ecf20Sopenharmony_ci *speed = SPEED_10000; 9098c2ecf20Sopenharmony_ci if (duplex) 9108c2ecf20Sopenharmony_ci *duplex = DUPLEX_FULL; 9118c2ecf20Sopenharmony_ci return 0; 9128c2ecf20Sopenharmony_ci} 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_cistatic int xaui_direct_power_down(struct cphy *phy, int enable) 9158c2ecf20Sopenharmony_ci{ 9168c2ecf20Sopenharmony_ci return 0; 9178c2ecf20Sopenharmony_ci} 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_cistatic const struct cphy_ops xaui_direct_ops = { 9208c2ecf20Sopenharmony_ci .reset = xaui_direct_reset, 9218c2ecf20Sopenharmony_ci .intr_enable = ael1002_intr_noop, 9228c2ecf20Sopenharmony_ci .intr_disable = ael1002_intr_noop, 9238c2ecf20Sopenharmony_ci .intr_clear = ael1002_intr_noop, 9248c2ecf20Sopenharmony_ci .intr_handler = ael1002_intr_noop, 9258c2ecf20Sopenharmony_ci .get_link_status = xaui_direct_get_link_status, 9268c2ecf20Sopenharmony_ci .power_down = xaui_direct_power_down, 9278c2ecf20Sopenharmony_ci}; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ciint t3_xaui_direct_phy_prep(struct cphy *phy, struct adapter *adapter, 9308c2ecf20Sopenharmony_ci int phy_addr, const struct mdio_ops *mdio_ops) 9318c2ecf20Sopenharmony_ci{ 9328c2ecf20Sopenharmony_ci cphy_init(phy, adapter, phy_addr, &xaui_direct_ops, mdio_ops, 9338c2ecf20Sopenharmony_ci SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_TP, 9348c2ecf20Sopenharmony_ci "10GBASE-CX4"); 9358c2ecf20Sopenharmony_ci return 0; 9368c2ecf20Sopenharmony_ci} 937