/* * Aquantia PHY drivers * * SPDX-License-Identifier: GPL-2.0+ * * Copyright 2014 Freescale Semiconductor, Inc. */ #include #include #include #ifndef CONFIG_PHYLIB_10G #error The Aquantia PHY needs 10G support #endif #define AQUNTIA_10G_CTL 0x20 #define AQUNTIA_VENDOR_P1 0xc400 #define AQUNTIA_SPEED_LSB_MASK 0x2000 #define AQUNTIA_SPEED_MSB_MASK 0x40 int aquantia_config(struct phy_device *phydev) { u32 val = phy_read(phydev, MDIO_MMD_PMAPMD, MII_BMCR); if (phydev->interface == PHY_INTERFACE_MODE_SGMII) { /* 1000BASE-T mode */ phydev->advertising = SUPPORTED_1000baseT_Full; phydev->supported = phydev->advertising; val = (val & ~AQUNTIA_SPEED_LSB_MASK) | AQUNTIA_SPEED_MSB_MASK; phy_write(phydev, MDIO_MMD_PMAPMD, MII_BMCR, val); } else if (phydev->interface == PHY_INTERFACE_MODE_XGMII) { /* 10GBASE-T mode */ phydev->advertising = SUPPORTED_10000baseT_Full; phydev->supported = phydev->advertising; if (!(val & AQUNTIA_SPEED_LSB_MASK) || !(val & AQUNTIA_SPEED_MSB_MASK)) phy_write(phydev, MDIO_MMD_PMAPMD, MII_BMCR, AQUNTIA_SPEED_LSB_MASK | AQUNTIA_SPEED_MSB_MASK); } else if (phydev->interface == PHY_INTERFACE_MODE_SGMII_2500) { /* 2.5GBASE-T mode */ phydev->advertising = SUPPORTED_1000baseT_Full; phydev->supported = phydev->advertising; phy_write(phydev, MDIO_MMD_AN, AQUNTIA_10G_CTL, 1); phy_write(phydev, MDIO_MMD_AN, AQUNTIA_VENDOR_P1, 0x9440); } else if (phydev->interface == PHY_INTERFACE_MODE_MII) { /* 100BASE-TX mode */ phydev->advertising = SUPPORTED_100baseT_Full; phydev->supported = phydev->advertising; val = (val & ~AQUNTIA_SPEED_MSB_MASK) | AQUNTIA_SPEED_LSB_MASK; phy_write(phydev, MDIO_MMD_PMAPMD, MII_BMCR, val); } return 0; } int aquantia_startup(struct phy_device *phydev) { u32 reg, speed; int i = 0; phydev->duplex = DUPLEX_FULL; /* if the AN is still in progress, wait till timeout. */ phy_read(phydev, MDIO_MMD_AN, MDIO_STAT1); reg = phy_read(phydev, MDIO_MMD_AN, MDIO_STAT1); if (!(reg & MDIO_AN_STAT1_COMPLETE)) { printf("%s Waiting for PHY auto negotiation to complete", phydev->dev->name); do { udelay(1000); reg = phy_read(phydev, MDIO_MMD_AN, MDIO_STAT1); if ((i++ % 500) == 0) printf("."); } while (!(reg & MDIO_AN_STAT1_COMPLETE) && i < (4 * PHY_ANEG_TIMEOUT)); if (i > PHY_ANEG_TIMEOUT) printf(" TIMEOUT !\n"); } /* Read twice because link state is latched and a * read moves the current state into the register */ phy_read(phydev, MDIO_MMD_AN, MDIO_STAT1); reg = phy_read(phydev, MDIO_MMD_AN, MDIO_STAT1); if (reg < 0 || !(reg & MDIO_STAT1_LSTATUS)) phydev->link = 0; else phydev->link = 1; speed = phy_read(phydev, MDIO_MMD_PMAPMD, MII_BMCR); if (speed & AQUNTIA_SPEED_MSB_MASK) { if (speed & AQUNTIA_SPEED_LSB_MASK) phydev->speed = SPEED_10000; else phydev->speed = SPEED_1000; } else { if (speed & AQUNTIA_SPEED_LSB_MASK) phydev->speed = SPEED_100; else phydev->speed = SPEED_10; } return 0; } struct phy_driver aq1202_driver = { .name = "Aquantia AQ1202", .uid = 0x3a1b445, .mask = 0xfffffff0, .features = PHY_10G_FEATURES, .mmds = (MDIO_MMD_PMAPMD | MDIO_MMD_PCS| MDIO_MMD_PHYXS | MDIO_MMD_AN | MDIO_MMD_VEND1), .config = &aquantia_config, .startup = &aquantia_startup, .shutdown = &gen10g_shutdown, }; struct phy_driver aq2104_driver = { .name = "Aquantia AQ2104", .uid = 0x3a1b460, .mask = 0xfffffff0, .features = PHY_10G_FEATURES, .mmds = (MDIO_MMD_PMAPMD | MDIO_MMD_PCS| MDIO_MMD_PHYXS | MDIO_MMD_AN | MDIO_MMD_VEND1), .config = &aquantia_config, .startup = &aquantia_startup, .shutdown = &gen10g_shutdown, }; struct phy_driver aqr105_driver = { .name = "Aquantia AQR105", .uid = 0x3a1b4a2, .mask = 0xfffffff0, .features = PHY_10G_FEATURES, .mmds = (MDIO_MMD_PMAPMD | MDIO_MMD_PCS| MDIO_MMD_PHYXS | MDIO_MMD_AN | MDIO_MMD_VEND1), .config = &aquantia_config, .startup = &aquantia_startup, .shutdown = &gen10g_shutdown, }; int phy_aquantia_init(void) { phy_register(&aq1202_driver); phy_register(&aq2104_driver); phy_register(&aqr105_driver); return 0; }