/* * Driver for Aquantia PHY * * Author: Shaohui Xie * * Copyright 2015 Freescale Semiconductor, Inc. * * This file is licensed under the terms of the GNU General Public License * version 2. This program is licensed "as is" without any warranty of any * kind, whether express or implied. */ #include #include #include #include #include #include #include #include #define PHY_ID_AQ1202 0x03a1b445 #define PHY_ID_AQ2104 0x03a1b460 #define PHY_ID_AQR105 0x03a1b4a2 #define PHY_ID_AQR107 0x03a1b4e0 #define PHY_ID_AQR405 0x03a1b4b0 #define PHY_ID_AQR113 0x31c31c40 /* vendor registers definitions */ #define MDIO_AN_VEND_PROV 0xc400 #define MDIO_AN_VEND_PROV_1000BASET_FULL BIT(15) #define MDIO_AN_VEND_PROV_1000BASET_HALF BIT(14) #define MDIO_AN_VEND_PROV_5000_SUPPORT BIT(11) #define MDIO_AN_VEND_PROV_2500_SUPPORT BIT(10) #define MDIO_AN_TX_VEND_STATUS1 0xc800 #define MDIO_AN_TX_VEND_STATUS1_RATE_MASK GENMASK(3, 1) #define MDIO_AN_TX_VEND_STATUS1_10BASET 0 #define MDIO_AN_TX_VEND_STATUS1_100BASETX 1 #define MDIO_AN_TX_VEND_STATUS1_1000BASET 2 #define MDIO_AN_TX_VEND_STATUS1_10GBASET 3 #define MDIO_AN_TX_VEND_STATUS1_2500BASET 4 #define MDIO_AN_TX_VEND_STATUS1_5000BASET 5 #define MDIO_AN_TX_VEND_STATUS1_FULL_DUPLEX BIT(0) #define MDIO_AN_TX_VEND_INT_STATUS2 0xcc01 #define MDIO_AN_TX_VEND_INT_STATUS2_MASK BIT(0) #define MDIO_AN_TX_VEND_INT_MASK2 0xd401 #define MDIO_AN_TX_VEND_INT_MASK2_LINK BIT(0) #define VEND1_GLOBAL_INT_STD_MASK 0xff00 #define VEND1_GLOBAL_INT_STD_MASK_PMA1 BIT(15) #define VEND1_GLOBAL_INT_STD_MASK_PMA2 BIT(14) #define VEND1_GLOBAL_INT_STD_MASK_PCS1 BIT(13) #define VEND1_GLOBAL_INT_STD_MASK_PCS2 BIT(12) #define VEND1_GLOBAL_INT_STD_MASK_PCS3 BIT(11) #define VEND1_GLOBAL_INT_STD_MASK_PHY_XS1 BIT(10) #define VEND1_GLOBAL_INT_STD_MASK_PHY_XS2 BIT(9) #define VEND1_GLOBAL_INT_STD_MASK_AN1 BIT(8) #define VEND1_GLOBAL_INT_STD_MASK_AN2 BIT(7) #define VEND1_GLOBAL_INT_STD_MASK_GBE BIT(6) #define VEND1_GLOBAL_INT_STD_MASK_ALL BIT(0) #define VEND1_GLOBAL_INT_VEND_MASK 0xff01 #define VEND1_GLOBAL_INT_VEND_MASK_PMA BIT(15) #define VEND1_GLOBAL_INT_VEND_MASK_PCS BIT(14) #define VEND1_GLOBAL_INT_VEND_MASK_PHY_XS BIT(13) #define VEND1_GLOBAL_INT_VEND_MASK_AN BIT(12) #define VEND1_GLOBAL_INT_VEND_MASK_GBE BIT(11) #define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL1 BIT(2) #define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2 BIT(1) #define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 BIT(0) /* phy support functions backported from upstram kernel (5.15.rc1) * to simplify porting other driver changes */ /* AN 10GBASE-T control register defines not in 4.9.x */ #define MDIO_AN_10GBT_CTRL_ADV2_5G 0x0080 /* Advertise 2.5GBASE-T */ #define MDIO_AN_10GBT_CTRL_ADV5G 0x0100 /* Advertise 5GBASE-T */ static inline int linkmode_test_bit(int nr, const volatile unsigned long *addr) { return test_bit(nr, addr); } /** * linkmode_adv_to_mii_10gbt_adv_t * @advertising: the linkmode advertisement settings * * A small helper function that translates linkmode advertisement * settings to phy autonegotiation advertisements for the C45 * 10GBASE-T AN CONTROL (7.32) register. */ static inline u32 linkmode_adv_to_mii_10gbt_adv_t(unsigned long *advertising) { u32 result = 0; /* disable 2.5G and 5G support, as they are not supported in 4.9 kernel. */ #if 0 if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, advertising)) result |= MDIO_AN_10GBT_CTRL_ADV2_5G; if (linkmode_test_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, advertising)) result |= MDIO_AN_10GBT_CTRL_ADV5G; #endif if (linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, advertising)) result |= MDIO_AN_10GBT_CTRL_ADV10G; return result; } /** * phy_modify_mmd_changed - Function for modifying a register on MMD * @phydev: the phy_device struct * @devad: the MMD containing register to modify * @regnum: register number to modify * @mask: bit mask of bits to clear * @set: new value of bits set in mask to write to @regnum * * Unlocked helper function which allows a MMD register to be modified as * new register value = (old register value & ~mask) | set * * Returns negative errno, 0 if there was no change, and 1 in case of change */ static int phy_modify_mmd_changed(struct phy_device *phydev, int devad, u32 regnum, u16 mask, u16 set) { int new, ret; ret = phy_read_mmd(phydev, devad, regnum); if (ret < 0) return ret; new = (ret & ~mask) | set; if (new == ret) return 0; ret = phy_write_mmd(phydev, devad, regnum, new); return ret < 0 ? ret : 1; } /** * phy_modify_mmd - Convenience function for modifying a register on MMD * @phydev: the phy_device struct * @devad: the MMD containing register to modify * @regnum: register number to modify * @mask: bit mask of bits to clear * @set: new value of bits set in mask to write to @regnum * * NOTE: MUST NOT be called from interrupt context, * because the bus read/write functions may wait for an interrupt * to conclude the operation. */ static int phy_modify_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 mask, u16 set) { int ret; ret = phy_modify_mmd_changed(phydev, devad, regnum, mask, set); return ret < 0 ? ret : 0; } /* end of backport */ #define PHY_AQUANTIA_FEATURES (SUPPORTED_10000baseT_Full | \ SUPPORTED_1000baseT_Full | \ SUPPORTED_100baseT_Full | \ PHY_DEFAULT_FEATURES) static int aquantia_c45_restart_aneg(struct phy_device *phydev) { return phy_modify_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1, 0, MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART); } static int aquantia_config_advert(struct phy_device *phydev) { int ret, changed = 0; u32 adv; u16 reg; phydev->advertising &= phydev->supported; adv = ethtool_adv_to_mii_adv_t(phydev->advertising); ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE, ADVERTISE_ALL | ADVERTISE_100BASE4 | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM, adv); if (ret < 0) return ret; if (ret > 0) changed = 1; /* Clause 45 has no standardized support for 1000BaseT, therefore * use vendor registers for this mode. */ reg = 0; if (phydev->advertising & SUPPORTED_1000baseT_Full) reg |= MDIO_AN_VEND_PROV_1000BASET_FULL; if (phydev->advertising & SUPPORTED_1000baseT_Half) reg |= MDIO_AN_VEND_PROV_1000BASET_HALF; ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_VEND_PROV, MDIO_AN_VEND_PROV_1000BASET_HALF | MDIO_AN_VEND_PROV_1000BASET_FULL | MDIO_AN_VEND_PROV_5000_SUPPORT | MDIO_AN_VEND_PROV_2500_SUPPORT, reg); if (ret < 0) return ret; adv = linkmode_adv_to_mii_10gbt_adv_t(phydev->extended_advertising); /* workaround, as we don't set all modes in extended_advertising */ if (phydev->advertising & SUPPORTED_10000baseT_Full) adv |= MDIO_AN_10GBT_CTRL_ADV10G; ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL, MDIO_AN_10GBT_CTRL_ADV10G | MDIO_AN_10GBT_CTRL_ADV5G | MDIO_AN_10GBT_CTRL_ADV2_5G, adv); if (ret < 0) return ret; if (ret > 0) changed = 1; return changed; } static int aquantia_config_aneg(struct phy_device *phydev) { int err; phydev->supported = PHY_AQUANTIA_FEATURES; if (phydev->autoneg != AUTONEG_ENABLE) { phydev_err(phydev, "Autoneg-off is not supported\n"); /* revert advertising mode to default */ phydev->advertising = phydev->supported; return -EINVAL; } err = aquantia_config_advert(phydev); if (err > 0) err = aquantia_c45_restart_aneg(phydev); return err; } static int aquantia_aneg_done(struct phy_device *phydev) { int reg; reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); return (reg < 0) ? reg : (reg & BMSR_ANEGCOMPLETE); } static int aquantia_config_intr(struct phy_device *phydev) { bool en = phydev->interrupts == PHY_INTERRUPT_ENABLED; int err; if (en) { /* Clear any pending interrupts before enabling them */ err = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_INT_STATUS2); if (err < 0) return err; } err = phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_INT_MASK2, en ? MDIO_AN_TX_VEND_INT_MASK2_LINK : 0); if (err < 0) return err; err = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_INT_STD_MASK, en ? VEND1_GLOBAL_INT_STD_MASK_ALL : 0); if (err < 0) return err; err = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_INT_VEND_MASK, en ? VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 | VEND1_GLOBAL_INT_VEND_MASK_AN : 0); if (err < 0) return err; if (!en) { /* Clear any pending interrupts after we have disabled them */ err = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_INT_STATUS2); if (err < 0) return err; } return 0; } static int aquantia_ack_interrupt(struct phy_device *phydev) { int reg; reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_INT_STATUS2); return (reg < 0) ? reg : 0; } static int aquantia_read_status(struct phy_device *phydev) { int reg; reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); if (reg & MDIO_STAT1_LSTATUS) phydev->link = 1; else phydev->link = 0; reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_STATUS1); mdelay(10); reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_TX_VEND_STATUS1); if (reg < 0) return reg; switch (FIELD_GET(MDIO_AN_TX_VEND_STATUS1_RATE_MASK, reg)) { case MDIO_AN_TX_VEND_STATUS1_10BASET: phydev->speed = SPEED_10; break; case MDIO_AN_TX_VEND_STATUS1_100BASETX: phydev->speed = SPEED_100; break; case MDIO_AN_TX_VEND_STATUS1_1000BASET: phydev->speed = SPEED_1000; break; case MDIO_AN_TX_VEND_STATUS1_2500BASET: phydev->speed = SPEED_2500; break; case MDIO_AN_TX_VEND_STATUS1_5000BASET: phydev->speed = SPEED_5000; break; case MDIO_AN_TX_VEND_STATUS1_10GBASET: phydev->speed = SPEED_10000; break; default: phydev->speed = SPEED_UNKNOWN; break; } if (reg & MDIO_AN_TX_VEND_STATUS1_FULL_DUPLEX) phydev->duplex = DUPLEX_FULL; else phydev->duplex = DUPLEX_HALF; return 0; } static struct phy_driver aquantia_driver[] = { { .phy_id = PHY_ID_AQ1202, .phy_id_mask = 0xfffffff0, .name = "Aquantia AQ1202", .features = PHY_AQUANTIA_FEATURES, .flags = PHY_HAS_INTERRUPT, .aneg_done = aquantia_aneg_done, .config_aneg = aquantia_config_aneg, .config_intr = aquantia_config_intr, .ack_interrupt = aquantia_ack_interrupt, .read_status = aquantia_read_status, }, { .phy_id = PHY_ID_AQ2104, .phy_id_mask = 0xfffffff0, .name = "Aquantia AQ2104", .features = PHY_AQUANTIA_FEATURES, .flags = PHY_HAS_INTERRUPT, .aneg_done = aquantia_aneg_done, .config_aneg = aquantia_config_aneg, .config_intr = aquantia_config_intr, .ack_interrupt = aquantia_ack_interrupt, .read_status = aquantia_read_status, }, { .phy_id = PHY_ID_AQR105, .phy_id_mask = 0xfffffff0, .name = "Aquantia AQR105", .features = PHY_AQUANTIA_FEATURES, .flags = PHY_HAS_INTERRUPT, .aneg_done = aquantia_aneg_done, .config_aneg = aquantia_config_aneg, .config_intr = aquantia_config_intr, .ack_interrupt = aquantia_ack_interrupt, .read_status = aquantia_read_status, }, { .phy_id = PHY_ID_AQR107, .phy_id_mask = 0xfffffff0, .name = "Aquantia AQR107", .features = PHY_AQUANTIA_FEATURES, .flags = PHY_HAS_INTERRUPT, .aneg_done = aquantia_aneg_done, .config_aneg = aquantia_config_aneg, .config_intr = aquantia_config_intr, .ack_interrupt = aquantia_ack_interrupt, .read_status = aquantia_read_status, }, { .phy_id = PHY_ID_AQR405, .phy_id_mask = 0xfffffff0, .name = "Aquantia AQR405", .features = PHY_AQUANTIA_FEATURES, .flags = PHY_HAS_INTERRUPT, .aneg_done = aquantia_aneg_done, .config_aneg = aquantia_config_aneg, .config_intr = aquantia_config_intr, .ack_interrupt = aquantia_ack_interrupt, .read_status = aquantia_read_status, }, { .phy_id = PHY_ID_AQR113, .phy_id_mask = 0xfffffff0, .name = "Aquantia AQR113", .features = PHY_AQUANTIA_FEATURES, .flags = PHY_HAS_INTERRUPT, .aneg_done = aquantia_aneg_done, .config_aneg = aquantia_config_aneg, .config_intr = aquantia_config_intr, .ack_interrupt = aquantia_ack_interrupt, .read_status = aquantia_read_status, }, }; module_phy_driver(aquantia_driver); static struct mdio_device_id __maybe_unused aquantia_tbl[] = { { PHY_ID_AQ1202, 0xfffffff0 }, { PHY_ID_AQ2104, 0xfffffff0 }, { PHY_ID_AQR105, 0xfffffff0 }, { PHY_ID_AQR107, 0xfffffff0 }, { PHY_ID_AQR405, 0xfffffff0 }, { PHY_ID_AQR113, 0xfffffff0 }, {} }; MODULE_DEVICE_TABLE(mdio, aquantia_tbl); MODULE_DESCRIPTION("Aquantia PHY driver"); MODULE_AUTHOR("Shaohui Xie "); MODULE_LICENSE("GPL v2");