Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Oleksij Rempel | 598 | 100.00% | 3 | 100.00% |
Total | 598 | 3 |
// SPDX-License-Identifier: GPL-2.0 /* Driver for the Texas Instruments DP83TG720 PHY * Copyright (c) 2023 Pengutronix, Oleksij Rempel <kernel@pengutronix.de> */ #include <linux/bitfield.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/phy.h> #define DP83TG720S_PHY_ID 0x2000a284 /* MDIO_MMD_VEND2 registers */ #define DP83TG720S_MII_REG_10 0x10 #define DP83TG720S_STS_MII_INT BIT(7) #define DP83TG720S_LINK_STATUS BIT(0) #define DP83TG720S_PHY_RESET 0x1f #define DP83TG720S_HW_RESET BIT(15) #define DP83TG720S_LPS_CFG3 0x18c /* Power modes are documented as bit fields but used as values */ /* Power Mode 0 is Normal mode */ #define DP83TG720S_LPS_CFG3_PWR_MODE_0 BIT(0) #define DP83TG720S_RGMII_DELAY_CTRL 0x602 /* In RGMII mode, Enable or disable the internal delay for RXD */ #define DP83TG720S_RGMII_RX_CLK_SEL BIT(1) /* In RGMII mode, Enable or disable the internal delay for TXD */ #define DP83TG720S_RGMII_TX_CLK_SEL BIT(0) #define DP83TG720S_SQI_REG_1 0x871 #define DP83TG720S_SQI_OUT_WORST GENMASK(7, 5) #define DP83TG720S_SQI_OUT GENMASK(3, 1) #define DP83TG720_SQI_MAX 7 static int dp83tg720_config_aneg(struct phy_device *phydev) { int ret; /* Autoneg is not supported and this PHY supports only one speed. * We need to care only about master/slave configuration if it was * changed by user. */ ret = genphy_c45_pma_baset1_setup_master_slave(phydev); if (ret) return ret; /* Re-read role configuration to make changes visible even if * the link is in administrative down state. */ return genphy_c45_pma_baset1_read_master_slave(phydev); } static int dp83tg720_read_status(struct phy_device *phydev) { u16 phy_sts; int ret; phydev->pause = 0; phydev->asym_pause = 0; /* Most of Clause 45 registers are not present, so we can't use * genphy_c45_read_status() here. */ phy_sts = phy_read(phydev, DP83TG720S_MII_REG_10); phydev->link = !!(phy_sts & DP83TG720S_LINK_STATUS); if (!phydev->link) { /* According to the "DP83TC81x, DP83TG72x Software * Implementation Guide", the PHY needs to be reset after a * link loss or if no link is created after at least 100ms. * * Currently we are polling with the PHY_STATE_TIME (1000ms) * interval, which is still enough for not automotive use cases. */ ret = phy_init_hw(phydev); if (ret) return ret; /* After HW reset we need to restore master/slave configuration. * genphy_c45_pma_baset1_read_master_slave() call will be done * by the dp83tg720_config_aneg() function. */ ret = dp83tg720_config_aneg(phydev); if (ret) return ret; phydev->speed = SPEED_UNKNOWN; phydev->duplex = DUPLEX_UNKNOWN; } else { /* PMA/PMD control 1 register (Register 1.0) is present, but it * doesn't contain the link speed information. * So genphy_c45_read_pma() can't be used here. */ ret = genphy_c45_pma_baset1_read_master_slave(phydev); if (ret) return ret; phydev->duplex = DUPLEX_FULL; phydev->speed = SPEED_1000; } return 0; } static int dp83tg720_get_sqi(struct phy_device *phydev) { int ret; if (!phydev->link) return 0; ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TG720S_SQI_REG_1); if (ret < 0) return ret; return FIELD_GET(DP83TG720S_SQI_OUT, ret); } static int dp83tg720_get_sqi_max(struct phy_device *phydev) { return DP83TG720_SQI_MAX; } static int dp83tg720_config_rgmii_delay(struct phy_device *phydev) { u16 rgmii_delay_mask; u16 rgmii_delay = 0; switch (phydev->interface) { case PHY_INTERFACE_MODE_RGMII: rgmii_delay = 0; break; case PHY_INTERFACE_MODE_RGMII_ID: rgmii_delay = DP83TG720S_RGMII_RX_CLK_SEL | DP83TG720S_RGMII_TX_CLK_SEL; break; case PHY_INTERFACE_MODE_RGMII_RXID: rgmii_delay = DP83TG720S_RGMII_RX_CLK_SEL; break; case PHY_INTERFACE_MODE_RGMII_TXID: rgmii_delay = DP83TG720S_RGMII_TX_CLK_SEL; break; default: return 0; } rgmii_delay_mask = DP83TG720S_RGMII_RX_CLK_SEL | DP83TG720S_RGMII_TX_CLK_SEL; return phy_modify_mmd(phydev, MDIO_MMD_VEND2, DP83TG720S_RGMII_DELAY_CTRL, rgmii_delay_mask, rgmii_delay); } static int dp83tg720_config_init(struct phy_device *phydev) { int ret; /* Software Restart is not enough to recover from a link failure. * Using Hardware Reset instead. */ ret = phy_write(phydev, DP83TG720S_PHY_RESET, DP83TG720S_HW_RESET); if (ret) return ret; /* Wait until MDC can be used again. * The wait value of one 1ms is documented in "DP83TG720S-Q1 1000BASE-T1 * Automotive Ethernet PHY with SGMII and RGMII" datasheet. */ usleep_range(1000, 2000); if (phy_interface_is_rgmii(phydev)) { ret = dp83tg720_config_rgmii_delay(phydev); if (ret) return ret; } /* In case the PHY is bootstrapped in managed mode, we need to * wake it. */ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, DP83TG720S_LPS_CFG3, DP83TG720S_LPS_CFG3_PWR_MODE_0); if (ret) return ret; /* Make role configuration visible for ethtool on init and after * rest. */ return genphy_c45_pma_baset1_read_master_slave(phydev); } static struct phy_driver dp83tg720_driver[] = { { PHY_ID_MATCH_MODEL(DP83TG720S_PHY_ID), .name = "TI DP83TG720S", .config_aneg = dp83tg720_config_aneg, .read_status = dp83tg720_read_status, .get_features = genphy_c45_pma_read_ext_abilities, .config_init = dp83tg720_config_init, .get_sqi = dp83tg720_get_sqi, .get_sqi_max = dp83tg720_get_sqi_max, .suspend = genphy_suspend, .resume = genphy_resume, } }; module_phy_driver(dp83tg720_driver); static struct mdio_device_id __maybe_unused dp83tg720_tbl[] = { { PHY_ID_MATCH_MODEL(DP83TG720S_PHY_ID) }, { } }; MODULE_DEVICE_TABLE(mdio, dp83tg720_tbl); MODULE_DESCRIPTION("Texas Instruments DP83TG720S PHY driver"); MODULE_AUTHOR("Oleksij Rempel <kernel@pengutronix.de>"); MODULE_LICENSE("GPL");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1