| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Driver for Aquantia PHY |
| * |
| * Author: Shaohui Xie <Shaohui.Xie@freescale.com> |
| * |
| * Copyright 2015 Freescale Semiconductor, Inc. |
| */ |
| |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/delay.h> |
| #include <linux/phy.h> |
| |
| #include "aquantia.h" |
| |
| #define PHY_ID_AQ1202 0x03a1b445 |
| #define PHY_ID_AQ2104 0x03a1b460 |
| #define PHY_ID_AQR105 0x03a1b4a2 |
| #define PHY_ID_AQR106 0x03a1b4d0 |
| #define PHY_ID_AQR107 0x03a1b4e0 |
| #define PHY_ID_AQCS109 0x03a1b5c2 |
| #define PHY_ID_AQR405 0x03a1b4b0 |
| |
| #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_TX_VEND_STATUS1 0xc800 |
| #define MDIO_AN_TX_VEND_STATUS1_10BASET (0x0 << 1) |
| #define MDIO_AN_TX_VEND_STATUS1_100BASETX (0x1 << 1) |
| #define MDIO_AN_TX_VEND_STATUS1_1000BASET (0x2 << 1) |
| #define MDIO_AN_TX_VEND_STATUS1_10GBASET (0x3 << 1) |
| #define MDIO_AN_TX_VEND_STATUS1_2500BASET (0x4 << 1) |
| #define MDIO_AN_TX_VEND_STATUS1_5000BASET (0x5 << 1) |
| #define MDIO_AN_TX_VEND_STATUS1_RATE_MASK (0x7 << 1) |
| #define MDIO_AN_TX_VEND_STATUS1_FULL_DUPLEX BIT(0) |
| |
| #define MDIO_AN_TX_VEND_INT_STATUS2 0xcc01 |
| |
| #define MDIO_AN_TX_VEND_INT_MASK2 0xd401 |
| #define MDIO_AN_TX_VEND_INT_MASK2_LINK BIT(0) |
| |
| #define MDIO_AN_RX_LP_STAT1 0xe820 |
| #define MDIO_AN_RX_LP_STAT1_1000BASET_FULL BIT(15) |
| #define MDIO_AN_RX_LP_STAT1_1000BASET_HALF BIT(14) |
| |
| /* Vendor specific 1, MDIO_MMD_VEND1 */ |
| #define VEND1_GLOBAL_INT_STD_STATUS 0xfc00 |
| #define VEND1_GLOBAL_INT_VEND_STATUS 0xfc01 |
| |
| #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) |
| |
| static int aqr_config_aneg(struct phy_device *phydev) |
| { |
| bool changed = false; |
| u16 reg; |
| int ret; |
| |
| if (phydev->autoneg == AUTONEG_DISABLE) |
| return genphy_c45_pma_setup_forced(phydev); |
| |
| ret = genphy_c45_an_config_aneg(phydev); |
| if (ret < 0) |
| return ret; |
| if (ret > 0) |
| changed = true; |
| |
| /* Clause 45 has no standardized support for 1000BaseT, therefore |
| * use vendor registers for this mode. |
| */ |
| reg = 0; |
| if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, |
| phydev->advertising)) |
| reg |= MDIO_AN_VEND_PROV_1000BASET_FULL; |
| |
| if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, |
| phydev->advertising)) |
| 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, reg); |
| if (ret < 0) |
| return ret; |
| if (ret > 0) |
| changed = true; |
| |
| return genphy_c45_check_and_restart_aneg(phydev, changed); |
| } |
| |
| static int aqr_config_intr(struct phy_device *phydev) |
| { |
| int err; |
| |
| if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { |
| err = phy_write_mmd(phydev, MDIO_MMD_AN, |
| MDIO_AN_TX_VEND_INT_MASK2, |
| MDIO_AN_TX_VEND_INT_MASK2_LINK); |
| if (err < 0) |
| return err; |
| |
| err = phy_write_mmd(phydev, MDIO_MMD_VEND1, |
| VEND1_GLOBAL_INT_STD_MASK, |
| VEND1_GLOBAL_INT_STD_MASK_ALL); |
| if (err < 0) |
| return err; |
| |
| err = phy_write_mmd(phydev, MDIO_MMD_VEND1, |
| VEND1_GLOBAL_INT_VEND_MASK, |
| VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 | |
| VEND1_GLOBAL_INT_VEND_MASK_AN); |
| } else { |
| err = phy_write_mmd(phydev, MDIO_MMD_AN, |
| MDIO_AN_TX_VEND_INT_MASK2, 0); |
| if (err < 0) |
| return err; |
| |
| err = phy_write_mmd(phydev, MDIO_MMD_VEND1, |
| VEND1_GLOBAL_INT_STD_MASK, 0); |
| if (err < 0) |
| return err; |
| |
| err = phy_write_mmd(phydev, MDIO_MMD_VEND1, |
| VEND1_GLOBAL_INT_VEND_MASK, 0); |
| } |
| |
| return err; |
| } |
| |
| static int aqr_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 aqr_read_status(struct phy_device *phydev) |
| { |
| int val; |
| |
| if (phydev->autoneg == AUTONEG_ENABLE) { |
| val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RX_LP_STAT1); |
| if (val < 0) |
| return val; |
| |
| linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, |
| phydev->lp_advertising, |
| val & MDIO_AN_RX_LP_STAT1_1000BASET_FULL); |
| linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, |
| phydev->lp_advertising, |
| val & MDIO_AN_RX_LP_STAT1_1000BASET_HALF); |
| } |
| |
| return genphy_c45_read_status(phydev); |
| } |
| |
| static int aqr107_config_init(struct phy_device *phydev) |
| { |
| /* Check that the PHY interface type is compatible */ |
| if (phydev->interface != PHY_INTERFACE_MODE_SGMII && |
| phydev->interface != PHY_INTERFACE_MODE_2500BASEX && |
| phydev->interface != PHY_INTERFACE_MODE_10GKR) |
| return -ENODEV; |
| |
| return 0; |
| } |
| |
| static int aqcs109_config_init(struct phy_device *phydev) |
| { |
| /* Check that the PHY interface type is compatible */ |
| if (phydev->interface != PHY_INTERFACE_MODE_SGMII && |
| phydev->interface != PHY_INTERFACE_MODE_2500BASEX) |
| return -ENODEV; |
| |
| /* AQCS109 belongs to a chip family partially supporting 10G and 5G. |
| * PMA speed ability bits are the same for all members of the family, |
| * AQCS109 however supports speeds up to 2.5G only. |
| */ |
| return phy_set_max_speed(phydev, SPEED_2500); |
| } |
| |
| static struct phy_driver aqr_driver[] = { |
| { |
| PHY_ID_MATCH_MODEL(PHY_ID_AQ1202), |
| .name = "Aquantia AQ1202", |
| .aneg_done = genphy_c45_aneg_done, |
| .get_features = genphy_c45_pma_read_abilities, |
| .config_aneg = aqr_config_aneg, |
| .config_intr = aqr_config_intr, |
| .ack_interrupt = aqr_ack_interrupt, |
| .read_status = aqr_read_status, |
| }, |
| { |
| PHY_ID_MATCH_MODEL(PHY_ID_AQ2104), |
| .name = "Aquantia AQ2104", |
| .aneg_done = genphy_c45_aneg_done, |
| .get_features = genphy_c45_pma_read_abilities, |
| .config_aneg = aqr_config_aneg, |
| .config_intr = aqr_config_intr, |
| .ack_interrupt = aqr_ack_interrupt, |
| .read_status = aqr_read_status, |
| }, |
| { |
| PHY_ID_MATCH_MODEL(PHY_ID_AQR105), |
| .name = "Aquantia AQR105", |
| .aneg_done = genphy_c45_aneg_done, |
| .get_features = genphy_c45_pma_read_abilities, |
| .config_aneg = aqr_config_aneg, |
| .config_intr = aqr_config_intr, |
| .ack_interrupt = aqr_ack_interrupt, |
| .read_status = aqr_read_status, |
| }, |
| { |
| PHY_ID_MATCH_MODEL(PHY_ID_AQR106), |
| .name = "Aquantia AQR106", |
| .aneg_done = genphy_c45_aneg_done, |
| .get_features = genphy_c45_pma_read_abilities, |
| .config_aneg = aqr_config_aneg, |
| .config_intr = aqr_config_intr, |
| .ack_interrupt = aqr_ack_interrupt, |
| .read_status = aqr_read_status, |
| }, |
| { |
| PHY_ID_MATCH_MODEL(PHY_ID_AQR107), |
| .name = "Aquantia AQR107", |
| .aneg_done = genphy_c45_aneg_done, |
| .get_features = genphy_c45_pma_read_abilities, |
| .probe = aqr_hwmon_probe, |
| .config_init = aqr107_config_init, |
| .config_aneg = aqr_config_aneg, |
| .config_intr = aqr_config_intr, |
| .ack_interrupt = aqr_ack_interrupt, |
| .read_status = aqr_read_status, |
| }, |
| { |
| PHY_ID_MATCH_MODEL(PHY_ID_AQCS109), |
| .name = "Aquantia AQCS109", |
| .aneg_done = genphy_c45_aneg_done, |
| .get_features = genphy_c45_pma_read_abilities, |
| .probe = aqr_hwmon_probe, |
| .config_init = aqcs109_config_init, |
| .config_aneg = aqr_config_aneg, |
| .config_intr = aqr_config_intr, |
| .ack_interrupt = aqr_ack_interrupt, |
| .read_status = aqr_read_status, |
| }, |
| { |
| PHY_ID_MATCH_MODEL(PHY_ID_AQR405), |
| .name = "Aquantia AQR405", |
| .aneg_done = genphy_c45_aneg_done, |
| .get_features = genphy_c45_pma_read_abilities, |
| .config_aneg = aqr_config_aneg, |
| .config_intr = aqr_config_intr, |
| .ack_interrupt = aqr_ack_interrupt, |
| .read_status = aqr_read_status, |
| }, |
| }; |
| |
| module_phy_driver(aqr_driver); |
| |
| static struct mdio_device_id __maybe_unused aqr_tbl[] = { |
| { PHY_ID_MATCH_MODEL(PHY_ID_AQ1202) }, |
| { PHY_ID_MATCH_MODEL(PHY_ID_AQ2104) }, |
| { PHY_ID_MATCH_MODEL(PHY_ID_AQR105) }, |
| { PHY_ID_MATCH_MODEL(PHY_ID_AQR106) }, |
| { PHY_ID_MATCH_MODEL(PHY_ID_AQR107) }, |
| { PHY_ID_MATCH_MODEL(PHY_ID_AQCS109) }, |
| { PHY_ID_MATCH_MODEL(PHY_ID_AQR405) }, |
| { } |
| }; |
| |
| MODULE_DEVICE_TABLE(mdio, aqr_tbl); |
| |
| MODULE_DESCRIPTION("Aquantia PHY driver"); |
| MODULE_AUTHOR("Shaohui Xie <Shaohui.Xie@freescale.com>"); |
| MODULE_LICENSE("GPL v2"); |