From b2f44badc26e0b80634aaf91ca1a62460bb17253 Mon Sep 17 00:00:00 2001 From: Tianling Shen Date: Sun, 15 Jan 2023 17:11:02 +0800 Subject: [PATCH] rockchip: fix init motorcomm ethernet Signed-off-by: Tianling Shen --- .../src/arch/arm/dts/rk3328-nanopi-r2c.dts | 11 + .../arm/dts/rk3328-orangepi-r1-plus-lts.dts | 7 + .../boot/dts/rockchip/rk3328-nanopi-r2c.dts | 13 + .../rockchip/rk3328-orangepi-r1-plus-lts.dts | 7 + ...or-Motorcomm-yt8521-gigabit-ethernet.patch | 2 +- ...or-Motorcomm-yt8531-gigabit-ethernet.patch | 397 ----------- ...rt-for-Motorcomm-yt8521-yt8531s-giga.patch | 664 ++++++++++++++++++ ...-phy-Add-driver-for-Motorcomm-yt8531.patch | 223 ++++++ 8 files changed, 926 insertions(+), 398 deletions(-) delete mode 100644 target/linux/rockchip/patches-5.15/109-net-phy-Add-driver-for-Motorcomm-yt8531-gigabit-ethernet.patch create mode 100644 target/linux/rockchip/patches-5.15/109-net-phy-Add-dts-support-for-Motorcomm-yt8521-yt8531s-giga.patch create mode 100644 target/linux/rockchip/patches-5.15/110-net-phy-Add-driver-for-Motorcomm-yt8531.patch diff --git a/package/boot/uboot-rockchip/src/arch/arm/dts/rk3328-nanopi-r2c.dts b/package/boot/uboot-rockchip/src/arch/arm/dts/rk3328-nanopi-r2c.dts index adf91a0306..d1633e5949 100644 --- a/package/boot/uboot-rockchip/src/arch/arm/dts/rk3328-nanopi-r2c.dts +++ b/package/boot/uboot-rockchip/src/arch/arm/dts/rk3328-nanopi-r2c.dts @@ -13,10 +13,16 @@ / { model = "FriendlyElec NanoPi R2C"; compatible = "friendlyarm,nanopi-r2c", "rockchip,rk3328"; + + aliases { + ethernet1 = &yt8521s; + }; }; &gmac2io { phy-handle = <&yt8521s>; + tx_delay = <0x22>; + rx_delay = <0x12>; mdio { /delete-node/ ethernet-phy@1; @@ -25,6 +31,11 @@ compatible = "ethernet-phy-id0000.011a", "ethernet-phy-ieee802.3-c22"; reg = <3>; + + motorcomm,clk-out-frequency = <125000000>; + motorcomm,keep-pll-enabled; + motorcomm,auto-sleep-disabled; + pinctrl-0 = <ð_phy_reset_pin>; pinctrl-names = "default"; reset-assert-us = <10000>; diff --git a/package/boot/uboot-rockchip/src/arch/arm/dts/rk3328-orangepi-r1-plus-lts.dts b/package/boot/uboot-rockchip/src/arch/arm/dts/rk3328-orangepi-r1-plus-lts.dts index c718a5a481..1585bf2def 100644 --- a/package/boot/uboot-rockchip/src/arch/arm/dts/rk3328-orangepi-r1-plus-lts.dts +++ b/package/boot/uboot-rockchip/src/arch/arm/dts/rk3328-orangepi-r1-plus-lts.dts @@ -6,6 +6,8 @@ * Copyright (c) 2021 Tianling Shen */ +/dts-v1/; + #include "rk3328-orangepi-r1-plus.dts" / { @@ -25,6 +27,11 @@ compatible = "ethernet-phy-id4f51.e91b", "ethernet-phy-ieee802.3-c22"; reg = <0>; + + motorcomm,clk-out-frequency = <125000000>; + motorcomm,keep-pll-enabled; + motorcomm,auto-sleep-disabled; + pinctrl-0 = <ð_phy_reset_pin>; pinctrl-names = "default"; reset-assert-us = <15000>; diff --git a/target/linux/rockchip/files/arch/arm64/boot/dts/rockchip/rk3328-nanopi-r2c.dts b/target/linux/rockchip/files/arch/arm64/boot/dts/rockchip/rk3328-nanopi-r2c.dts index eac6c0391f..a1d26eb104 100644 --- a/target/linux/rockchip/files/arch/arm64/boot/dts/rockchip/rk3328-nanopi-r2c.dts +++ b/target/linux/rockchip/files/arch/arm64/boot/dts/rockchip/rk3328-nanopi-r2c.dts @@ -6,15 +6,23 @@ * Copyright (c) 2021 Tianling Shen */ +/dts-v1/; + #include "rk3328-nanopi-r2s.dts" / { model = "FriendlyElec NanoPi R2C"; compatible = "friendlyarm,nanopi-r2c", "rockchip,rk3328"; + + aliases { + ethernet1 = &yt8521s; + }; }; &gmac2io { phy-handle = <&yt8521s>; + tx_delay = <0x22>; + rx_delay = <0x12>; mdio { /delete-node/ ethernet-phy@1; @@ -23,6 +31,11 @@ compatible = "ethernet-phy-id0000.011a", "ethernet-phy-ieee802.3-c22"; reg = <3>; + + motorcomm,clk-out-frequency = <125000000>; + motorcomm,keep-pll-enabled; + motorcomm,auto-sleep-disabled; + pinctrl-0 = <ð_phy_reset_pin>; pinctrl-names = "default"; reset-assert-us = <10000>; diff --git a/target/linux/rockchip/files/arch/arm64/boot/dts/rockchip/rk3328-orangepi-r1-plus-lts.dts b/target/linux/rockchip/files/arch/arm64/boot/dts/rockchip/rk3328-orangepi-r1-plus-lts.dts index 1ad0991b61..8b52d829f9 100644 --- a/target/linux/rockchip/files/arch/arm64/boot/dts/rockchip/rk3328-orangepi-r1-plus-lts.dts +++ b/target/linux/rockchip/files/arch/arm64/boot/dts/rockchip/rk3328-orangepi-r1-plus-lts.dts @@ -6,6 +6,8 @@ * Copyright (c) 2021 Tianling Shen */ +/dts-v1/; + #include "rk3328-orangepi-r1-plus.dts" / { @@ -40,6 +42,11 @@ compatible = "ethernet-phy-id4f51.e91b", "ethernet-phy-ieee802.3-c22"; reg = <0>; + + motorcomm,clk-out-frequency = <125000000>; + motorcomm,keep-pll-enabled; + motorcomm,auto-sleep-disabled; + pinctrl-0 = <ð_phy_reset_pin>; pinctrl-names = "default"; reset-assert-us = <15000>; diff --git a/target/linux/rockchip/patches-5.15/011-02-v6.2-net-phy-Add-driver-for-Motorcomm-yt8521-gigabit-ethernet.patch b/target/linux/rockchip/patches-5.15/011-02-v6.2-net-phy-Add-driver-for-Motorcomm-yt8521-gigabit-ethernet.patch index e24c31b444..a9fe17376f 100644 --- a/target/linux/rockchip/patches-5.15/011-02-v6.2-net-phy-Add-driver-for-Motorcomm-yt8521-gigabit-ethernet.patch +++ b/target/linux/rockchip/patches-5.15/011-02-v6.2-net-phy-Add-driver-for-Motorcomm-yt8521-gigabit-ethernet.patch @@ -21,7 +21,7 @@ Signed-off-by: David S. Miller --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -12689,6 +12689,7 @@ F: include/uapi/linux/meye.h +@@ -12697,6 +12697,7 @@ F: include/uapi/linux/meye.h MOTORCOMM PHY DRIVER M: Peter Geis diff --git a/target/linux/rockchip/patches-5.15/109-net-phy-Add-driver-for-Motorcomm-yt8531-gigabit-ethernet.patch b/target/linux/rockchip/patches-5.15/109-net-phy-Add-driver-for-Motorcomm-yt8531-gigabit-ethernet.patch deleted file mode 100644 index 3c897fd2b2..0000000000 --- a/target/linux/rockchip/patches-5.15/109-net-phy-Add-driver-for-Motorcomm-yt8531-gigabit-ethernet.patch +++ /dev/null @@ -1,397 +0,0 @@ -From: Frank -To: Peter Geis , Andrew Lunn , - Heiner Kallweit , - Russell King , - "David S . Miller" , - Eric Dumazet , - Jakub Kicinski , Paolo Abeni -Cc: yinghong.zhang@motor-comm.com, fei.zhang@motor-comm.com, - hua.sun@motor-comm.com, netdev@vger.kernel.org, - linux-kernel@vger.kernel.org, Frank -Subject: [PATCH net-next v2] net: phy: Add driver for Motorcomm yt8531 gigabit ethernet phy -Date: Fri, 2 Dec 2022 15:36:48 +0800 [thread overview] -Message-ID: <20221202073648.3182-1-Frank.Sae@motor-comm.com> (raw) - -Add a driver for the motorcomm yt8531 gigabit ethernet phy. We have verified -the patch on AM335x platform which has one YT8531 interface -card and passed all test cases. -The tested cases indluding: YT8531 UTP function with support of 10M/100M/1000M -and wol(based on magic packet). - -Signed-off-by: Frank ---- -Hi Russell, Andrew - Thanks and based on your comments we added lock accordingly in the v2 patch. - -v2: -- Fixed yt8531_set_wol's lock issue. -- Added ASSERT_MDIO(phydev) to check mdio lock. - -Thanks and BR, -Frank - - drivers/net/phy/Kconfig | 2 +- - drivers/net/phy/motorcomm.c | 194 +++++++++++++++++++++++++++++++++++- - 2 files changed, 190 insertions(+), 6 deletions(-) - ---- a/drivers/net/phy/Kconfig -+++ b/drivers/net/phy/Kconfig -@@ -319,7 +319,7 @@ config MOTORCOMM_PHY - tristate "Motorcomm PHYs" - help - Enables support for Motorcomm network PHYs. -- Currently supports the YT8511, YT8521, YT8531S Gigabit Ethernet PHYs. -+ Currently supports the YT8511, YT8521, YT8531, YT8531S Gigabit Ethernet PHYs. - - config NATIONAL_PHY - tristate "National Semiconductor PHYs" ---- a/drivers/net/phy/motorcomm.c -+++ b/drivers/net/phy/motorcomm.c -@@ -1,6 +1,6 @@ - // SPDX-License-Identifier: GPL-2.0+ - /* -- * Motorcomm 8511/8521/8531S PHY driver. -+ * Motorcomm 8511/8521/8531/8531S PHY driver. - * - * Author: Peter Geis - * Author: Frank -@@ -12,8 +12,9 @@ - #include - - #define PHY_ID_YT8511 0x0000010a --#define PHY_ID_YT8521 0x0000011A --#define PHY_ID_YT8531S 0x4F51E91A -+#define PHY_ID_YT8521 0x0000011a -+#define PHY_ID_YT8531 0x4f51e91b -+#define PHY_ID_YT8531S 0x4f51e91a - - /* YT8521/YT8531S Register Overview - * UTP Register space | FIBER Register space -@@ -225,6 +226,9 @@ - #define YT8531S_SYNCE_CFG_REG 0xA012 - #define YT8531S_SCR_SYNCE_ENABLE BIT(6) - -+#define YT8531_SYNCE_CFG_REG 0xA012 -+#define YT8531_SCR_SYNCE_ENABLE BIT(6) -+ - /* Extended Register end */ - - struct yt8521_priv { -@@ -245,6 +249,15 @@ struct yt8521_priv { - u8 reg_page; - }; - -+static bool mdio_is_locked(struct phy_device *phydev) -+{ -+ return mutex_is_locked(&phydev->mdio.bus->mdio_lock); -+} -+ -+#define ASSERT_MDIO(phydev) \ -+ WARN_ONCE(!mdio_is_locked(phydev), \ -+ "MDIO: assertion failed at %s (%d)\n", __FILE__, __LINE__) -+ - /** - * ytphy_read_ext() - read a PHY's extended register - * @phydev: a pointer to a &struct phy_device -@@ -258,6 +271,8 @@ static int ytphy_read_ext(struct phy_dev - { - int ret; - -+ ASSERT_MDIO(phydev); -+ - ret = __phy_write(phydev, YTPHY_PAGE_SELECT, regnum); - if (ret < 0) - return ret; -@@ -297,6 +312,8 @@ static int ytphy_write_ext(struct phy_de - { - int ret; - -+ ASSERT_MDIO(phydev); -+ - ret = __phy_write(phydev, YTPHY_PAGE_SELECT, regnum); - if (ret < 0) - return ret; -@@ -342,6 +359,8 @@ static int ytphy_modify_ext(struct phy_d - { - int ret; - -+ ASSERT_MDIO(phydev); -+ - ret = __phy_write(phydev, YTPHY_PAGE_SELECT, regnum); - if (ret < 0) - return ret; -@@ -479,6 +498,76 @@ err_restore_page: - return phy_restore_page(phydev, old_page, ret); - } - -+/** -+ * yt8531_set_wol() - turn wake-on-lan on or off -+ * @phydev: a pointer to a &struct phy_device -+ * @wol: a pointer to a &struct ethtool_wolinfo -+ * -+ * returns 0 or negative errno code -+ */ -+static int yt8531_set_wol(struct phy_device *phydev, -+ struct ethtool_wolinfo *wol) -+{ -+ struct net_device *p_attached_dev; -+ const u16 mac_addr_reg[] = { -+ YTPHY_WOL_MACADDR2_REG, -+ YTPHY_WOL_MACADDR1_REG, -+ YTPHY_WOL_MACADDR0_REG, -+ }; -+ const u8 *mac_addr; -+ u16 mask; -+ u16 val; -+ int ret; -+ u8 i; -+ -+ if (wol->wolopts & WAKE_MAGIC) { -+ p_attached_dev = phydev->attached_dev; -+ if (!p_attached_dev) -+ return -ENODEV; -+ -+ mac_addr = (const u8 *)p_attached_dev->dev_addr; -+ if (!is_valid_ether_addr(mac_addr)) -+ return -EINVAL; -+ -+ /* Store the device address for the magic packet */ -+ for (i = 0; i < 3; i++) { -+ ret = ytphy_write_ext_with_lock(phydev, mac_addr_reg[i], -+ ((mac_addr[i * 2] << 8)) | -+ (mac_addr[i * 2 + 1])); -+ if (ret < 0) -+ return ret; -+ } -+ -+ /* Enable WOL feature */ -+ mask = YTPHY_WCR_PULSE_WIDTH_MASK | YTPHY_WCR_INTR_SEL; -+ val = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL; -+ val |= YTPHY_WCR_TYPE_PULSE | YTPHY_WCR_PULSE_WIDTH_672MS; -+ ret = ytphy_modify_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG, -+ mask, val); -+ if (ret < 0) -+ return ret; -+ -+ /* Enable WOL interrupt */ -+ ret = phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG, 0, -+ YTPHY_IER_WOL); -+ if (ret < 0) -+ return ret; -+ } else { -+ /* Disable WOL feature */ -+ mask = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL; -+ ret = ytphy_modify_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG, -+ mask, 0); -+ -+ /* Disable WOL interrupt */ -+ ret = phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG, -+ YTPHY_IER_WOL, 0); -+ if (ret < 0) -+ return ret; -+ } -+ -+ return 0; -+} -+ - static int yt8511_read_page(struct phy_device *phydev) - { - return __phy_read(phydev, YT8511_PAGE_SELECT); -@@ -652,6 +741,19 @@ static int yt8521_probe(struct phy_devic - } - - /** -+ * yt8531_probe() - Now only disable SyncE clock output -+ * @phydev: a pointer to a &struct phy_device -+ * -+ * returns 0 or negative errno code -+ */ -+static int yt8531_probe(struct phy_device *phydev) -+{ -+ /* Disable SyncE clock output by default */ -+ return ytphy_modify_ext_with_lock(phydev, YT8531_SYNCE_CFG_REG, -+ YT8531_SCR_SYNCE_ENABLE, 0); -+} -+ -+/** - * yt8531s_probe() - read chip config then set suitable polling_mode - * @phydev: a pointer to a &struct phy_device - * -@@ -683,6 +785,8 @@ static int ytphy_utp_read_lpa(struct phy - { - int lpa, lpagb; - -+ ASSERT_MDIO(phydev); -+ - if (phydev->autoneg == AUTONEG_ENABLE) { - if (!phydev->autoneg_complete) { - mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, -@@ -744,6 +848,8 @@ static int yt8521_adjust_status(struct p - int err; - int lpa; - -+ ASSERT_MDIO(phydev); -+ - if (is_utp) - duplex = (status & YTPHY_SSR_DUPLEX) >> YTPHY_SSR_DUPLEX_OFFSET; - else -@@ -1193,6 +1299,59 @@ err_restore_page: - } - - /** -+ * yt8531_config_init() - called to initialize the PHY -+ * @phydev: a pointer to a &struct phy_device -+ * -+ * returns 0 or negative errno code -+ */ -+static int yt8531_config_init(struct phy_device *phydev) -+{ -+ int ret; -+ u16 val; -+ -+ switch (phydev->interface) { -+ case PHY_INTERFACE_MODE_RGMII: -+ val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_FE_TX_DELAY_DIS; -+ val |= YT8521_RC1R_RX_DELAY_DIS; -+ break; -+ case PHY_INTERFACE_MODE_RGMII_RXID: -+ val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_FE_TX_DELAY_DIS; -+ val |= YT8521_RC1R_RX_DELAY_EN; -+ break; -+ case PHY_INTERFACE_MODE_RGMII_TXID: -+ val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_FE_TX_DELAY_EN; -+ val |= YT8521_RC1R_RX_DELAY_DIS; -+ break; -+ case PHY_INTERFACE_MODE_RGMII_ID: -+ val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_FE_TX_DELAY_EN; -+ val |= YT8521_RC1R_RX_DELAY_EN; -+ break; -+ default: /* do not support other modes */ -+ return -EOPNOTSUPP; -+ } -+ -+ /* set rgmii delay mode */ -+ ret = ytphy_modify_ext_with_lock(phydev, YT8521_RGMII_CONFIG1_REG, -+ (YT8521_RC1R_RX_DELAY_MASK | -+ YT8521_RC1R_FE_TX_DELAY_MASK | -+ YT8521_RC1R_GE_TX_DELAY_MASK), -+ val); -+ if (ret < 0) -+ return ret; -+ -+ /* disable auto sleep */ -+ ret = ytphy_modify_ext_with_lock(phydev, -+ YT8521_EXTREG_SLEEP_CONTROL1_REG, -+ YT8521_ESC1R_SLEEP_SW, 0); -+ if (ret < 0) -+ return ret; -+ -+ /* enable RXC clock when no wire plug */ -+ return ytphy_modify_ext_with_lock(phydev, YT8521_CLOCK_GATING_REG, -+ YT8521_CGR_RX_CLK_EN, 0); -+} -+ -+/** - * yt8521_prepare_fiber_features() - A small helper function that setup - * fiber's features. - * @phydev: a pointer to a &struct phy_device -@@ -1220,6 +1379,8 @@ static int yt8521_fiber_setup_forced(str - u16 val; - int ret; - -+ ASSERT_MDIO(phydev); -+ - if (phydev->speed == SPEED_1000) - val = YTPHY_MCR_FIBER_1000BX; - else if (phydev->speed == SPEED_100) -@@ -1259,6 +1420,8 @@ static int ytphy_check_and_restart_aneg( - { - int ret; - -+ ASSERT_MDIO(phydev); -+ - if (!restart) { - /* Advertisement hasn't changed, but maybe aneg was never on to - * begin with? Or maybe phy was isolated? -@@ -1295,6 +1458,8 @@ static int yt8521_fiber_config_aneg(stru - int bmcr; - u16 adv; - -+ ASSERT_MDIO(phydev); -+ - if (phydev->autoneg != AUTONEG_ENABLE) - return yt8521_fiber_setup_forced(phydev); - -@@ -1352,6 +1517,8 @@ static int ytphy_setup_master_slave(stru - { - u16 ctl = 0; - -+ ASSERT_MDIO(phydev); -+ - if (!phydev->is_gigabit_capable) - return 0; - -@@ -1397,6 +1564,8 @@ static int ytphy_utp_config_advert(struc - int err, bmsr, changed = 0; - u32 adv; - -+ ASSERT_MDIO(phydev); -+ - /* Only allow advertising what this PHY supports */ - linkmode_and(phydev->advertising, phydev->advertising, - phydev->supported); -@@ -1454,6 +1623,8 @@ static int ytphy_utp_config_aneg(struct - int err; - u16 ctl; - -+ ASSERT_MDIO(phydev); -+ - err = ytphy_setup_master_slave(phydev); - if (err < 0) - return err; -@@ -1655,6 +1826,8 @@ static int ytphy_utp_read_abilities(stru - { - int val; - -+ ASSERT_MDIO(phydev); -+ - linkmode_set_bit_array(phy_basic_ports_array, - ARRAY_SIZE(phy_basic_ports_array), - phydev->supported); -@@ -1775,6 +1948,16 @@ static struct phy_driver motorcomm_phy_d - .resume = yt8521_resume, - }, - { -+ PHY_ID_MATCH_EXACT(PHY_ID_YT8531), -+ .name = "YT8531 Gigabit Ethernet", -+ .probe = yt8531_probe, -+ .config_init = yt8531_config_init, -+ .suspend = genphy_suspend, -+ .resume = genphy_resume, -+ .get_wol = ytphy_get_wol, -+ .set_wol = yt8531_set_wol, -+ }, -+ { - PHY_ID_MATCH_EXACT(PHY_ID_YT8531S), - .name = "YT8531S Gigabit Ethernet", - .get_features = yt8521_get_features, -@@ -1795,7 +1978,7 @@ static struct phy_driver motorcomm_phy_d - - module_phy_driver(motorcomm_phy_drvs); - --MODULE_DESCRIPTION("Motorcomm 8511/8521/8531S PHY driver"); -+MODULE_DESCRIPTION("Motorcomm 8511/8521/8531/8531S PHY driver"); - MODULE_AUTHOR("Peter Geis"); - MODULE_AUTHOR("Frank"); - MODULE_LICENSE("GPL"); -@@ -1803,8 +1986,9 @@ MODULE_LICENSE("GPL"); - static const struct mdio_device_id __maybe_unused motorcomm_tbl[] = { - { PHY_ID_MATCH_EXACT(PHY_ID_YT8511) }, - { PHY_ID_MATCH_EXACT(PHY_ID_YT8521) }, -+ { PHY_ID_MATCH_EXACT(PHY_ID_YT8531) }, - { PHY_ID_MATCH_EXACT(PHY_ID_YT8531S) }, -- { /* sentinal */ } -+ { /* sentinel */ } - }; - - MODULE_DEVICE_TABLE(mdio, motorcomm_tbl); diff --git a/target/linux/rockchip/patches-5.15/109-net-phy-Add-dts-support-for-Motorcomm-yt8521-yt8531s-giga.patch b/target/linux/rockchip/patches-5.15/109-net-phy-Add-dts-support-for-Motorcomm-yt8521-yt8531s-giga.patch new file mode 100644 index 0000000000..b76dec51d3 --- /dev/null +++ b/target/linux/rockchip/patches-5.15/109-net-phy-Add-dts-support-for-Motorcomm-yt8521-yt8531s-giga.patch @@ -0,0 +1,664 @@ +From: Frank +To: Peter Geis , Andrew Lunn , + Heiner Kallweit , + Russell King , + "David S . Miller" , + Eric Dumazet , + Jakub Kicinski , Paolo Abeni , + Rob Herring , + Krzysztof Kozlowski +Cc: xiaogang.fan@motor-comm.com, fei.zhang@motor-comm.com, + hua.sun@motor-comm.com, netdev@vger.kernel.org, + linux-kernel@vger.kernel.org, Frank , + devicetree@vger.kernel.org +Subject: [PATCH net-next v1 2/3] net: phy: Add dts support for Motorcomm yt8521/yt8531s gigabit ethernet phy +Date: Thu, 5 Jan 2023 15:30:23 +0800 [thread overview] +Message-ID: <20230105073024.8390-3-Frank.Sae@motor-comm.com> (raw) +In-Reply-To: <20230105073024.8390-1-Frank.Sae@motor-comm.com> + +Add dts support for yt8521 and yt8531s. This patch has +been tested on AM335x platform which has one YT8531S interface +card and passed all test cases. + +Signed-off-by: Frank +--- + drivers/net/phy/motorcomm.c | 517 ++++++++++++++++++++++++++++++------ + 1 file changed, 434 insertions(+), 83 deletions(-) + +--- a/drivers/net/phy/motorcomm.c ++++ b/drivers/net/phy/motorcomm.c +@@ -10,10 +10,11 @@ + #include + #include + #include ++#include + + #define PHY_ID_YT8511 0x0000010a +-#define PHY_ID_YT8521 0x0000011A +-#define PHY_ID_YT8531S 0x4F51E91A ++#define PHY_ID_YT8521 0x0000011a ++#define PHY_ID_YT8531S 0x4f51e91a + + /* YT8521/YT8531S Register Overview + * UTP Register space | FIBER Register space +@@ -144,6 +145,16 @@ + #define YT8521_ESC1R_SLEEP_SW BIT(15) + #define YT8521_ESC1R_PLLON_SLP BIT(14) + ++/* Phy Serdes analog cfg2 Register */ ++#define YTPHY_SERDES_ANALOG_CFG2_REG 0xA1 ++#define YTPHY_SAC2R_TX_AMPLITUDE_MASK ((0x7 << 13) | (0x7 << 1)) ++#define YT8521_SAC2R_TX_AMPLITUDE_LOW ((0x7 << 13) | (0x0 << 1)) ++#define YT8521_SAC2R_TX_AMPLITUDE_MIDDLE ((0x5 << 13) | (0x5 << 1)) ++#define YT8521_SAC2R_TX_AMPLITUDE_HIGH ((0x3 << 13) | (0x6 << 1)) ++#define YT8531S_SAC2R_TX_AMPLITUDE_LOW ((0x0 << 13) | (0x0 << 1)) ++#define YT8531S_SAC2R_TX_AMPLITUDE_MIDDLE ((0x0 << 13) | (0x1 << 1)) ++#define YT8531S_SAC2R_TX_AMPLITUDE_HIGH ((0x0 << 13) | (0x2 << 1)) ++ + /* Phy fiber Link timer cfg2 Register */ + #define YT8521_LINK_TIMER_CFG2_REG 0xA5 + #define YT8521_LTCR_EN_AUTOSEN BIT(15) +@@ -161,6 +172,7 @@ + + #define YT8521_CHIP_CONFIG_REG 0xA001 + #define YT8521_CCR_SW_RST BIT(15) ++#define YT8521_CCR_RXC_DLY_EN BIT(8) + + #define YT8521_CCR_MODE_SEL_MASK (BIT(2) | BIT(1) | BIT(0)) + #define YT8521_CCR_MODE_UTP_TO_RGMII 0 +@@ -178,22 +190,27 @@ + #define YT8521_MODE_POLL 0x3 + + #define YT8521_RGMII_CONFIG1_REG 0xA003 +- ++#define YT8521_RC1R_TX_CLK_SEL_MASK BIT(14) ++#define YT8521_RC1R_TX_CLK_SEL_ORIGINAL (0x0 << 14) ++#define YT8521_RC1R_TX_CLK_SEL_INVERTED (0x1 << 14) + /* TX Gig-E Delay is bits 3:0, default 0x1 + * TX Fast-E Delay is bits 7:4, default 0xf + * RX Delay is bits 13:10, default 0x0 + * Delay = 150ps * N + * On = 2250ps, off = 0ps + */ +-#define YT8521_RC1R_RX_DELAY_MASK (0xF << 10) +-#define YT8521_RC1R_RX_DELAY_EN (0xF << 10) +-#define YT8521_RC1R_RX_DELAY_DIS (0x0 << 10) +-#define YT8521_RC1R_FE_TX_DELAY_MASK (0xF << 4) +-#define YT8521_RC1R_FE_TX_DELAY_EN (0xF << 4) +-#define YT8521_RC1R_FE_TX_DELAY_DIS (0x0 << 4) +-#define YT8521_RC1R_GE_TX_DELAY_MASK (0xF << 0) +-#define YT8521_RC1R_GE_TX_DELAY_EN (0xF << 0) +-#define YT8521_RC1R_GE_TX_DELAY_DIS (0x0 << 0) ++#define YT8521_RC1R_GE_TX_DELAY_BIT (0) ++#define YT8521_RC1R_FE_TX_DELAY_BIT (4) ++#define YT8521_RC1R_RX_DELAY_BIT (10) ++#define YT8521_RC1R_RX_DELAY_MASK (0xF << YT8521_RC1R_RX_DELAY_BIT) ++#define YT8521_RC1R_RX_DELAY_EN (0xF << YT8521_RC1R_RX_DELAY_BIT) ++#define YT8521_RC1R_RX_DELAY_DIS (0x0 << YT8521_RC1R_RX_DELAY_BIT) ++#define YT8521_RC1R_FE_TX_DELAY_MASK (0xF << YT8521_RC1R_FE_TX_DELAY_BIT) ++#define YT8521_RC1R_FE_TX_DELAY_EN (0xF << YT8521_RC1R_FE_TX_DELAY_BIT) ++#define YT8521_RC1R_FE_TX_DELAY_DIS (0x0 << YT8521_RC1R_FE_TX_DELAY_BIT) ++#define YT8521_RC1R_GE_TX_DELAY_MASK (0xF << YT8521_RC1R_GE_TX_DELAY_BIT) ++#define YT8521_RC1R_GE_TX_DELAY_EN (0xF << YT8521_RC1R_GE_TX_DELAY_BIT) ++#define YT8521_RC1R_GE_TX_DELAY_DIS (0x0 << YT8521_RC1R_GE_TX_DELAY_BIT) + + #define YTPHY_MISC_CONFIG_REG 0xA006 + #define YTPHY_MCR_FIBER_SPEED_MASK BIT(0) +@@ -222,11 +239,33 @@ + */ + #define YTPHY_WCR_TYPE_PULSE BIT(0) + +-#define YT8531S_SYNCE_CFG_REG 0xA012 +-#define YT8531S_SCR_SYNCE_ENABLE BIT(6) ++#define YTPHY_SYNCE_CFG_REG 0xA012 ++#define YT8521_SCR_CLK_SRC_MASK (BIT(2) | BIT(1)) ++#define YT8521_SCR_CLK_SRC_PLL_125M (0x0 << 1) ++#define YT8521_SCR_CLK_SRC_REF_25M (0x3 << 1) ++#define YT8521_SCR_SYNCE_ENABLE BIT(5) ++#define YT8521_SCR_CLK_FRE_SEL_MASK BIT(3) ++#define YT8521_SCR_CLK_FRE_SEL_125M (0x1 << 3) ++#define YT8521_SCR_CLK_FRE_SEL_25M (0x0 << 3) ++#define YT8531_SCR_CLK_SRC_MASK (BIT(3) | BIT(2) | BIT(1)) ++#define YT8531_SCR_CLK_SRC_PLL_125M (0x0 << 1) ++#define YT8531_SCR_CLK_SRC_REF_25M (0x4 << 1) ++#define YT8531_SCR_SYNCE_ENABLE BIT(6) ++#define YT8531_SCR_CLK_FRE_SEL_MASK BIT(4) ++#define YT8531_SCR_CLK_FRE_SEL_125M (0x1 << 4) ++#define YT8531_SCR_CLK_FRE_SEL_25M (0x0 << 4) + + /* Extended Register end */ + ++#define YTPHY_DTS_MAX_TX_AMPLITUDE 0x2 ++#define YTPHY_DTS_MAX_DELAY_VAL 2250 ++#define YTPHY_DTS_STEP_DELAY_VAL 150 ++#define YTPHY_DTS_INVAL_VAL 0xFF ++ ++#define YTPHY_DTS_OUTPUT_CLK_DIS 0 ++#define YTPHY_DTS_OUTPUT_CLK_25M 25000000 ++#define YTPHY_DTS_OUTPUT_CLK_125M 125000000 ++ + struct yt8521_priv { + /* combo_advertising is used for case of YT8521 in combo mode, + * this means that yt8521 may work in utp or fiber mode which depends +@@ -243,6 +282,30 @@ struct yt8521_priv { + * YT8521_RSSR_TO_BE_ARBITRATED + */ + u8 reg_page; ++ ++ /* The following parameters are from dts */ ++ /* rx delay = rx_delay_basic + rx_delay_additional ++ * basic delay is ~2ns, 0 = off, 1 = on ++ * rx_delay_additional,delay time = 150ps * val ++ */ ++ u8 rx_delay_basic; ++ u8 rx_delay_additional; ++ ++ /* tx_delay_ge is tx_delay for 1000Mbps ++ * tx_delay_fe is tx_delay for 100Mbps or 10Mbps ++ * delay time = 150ps * val ++ */ ++ u8 tx_delay_ge; ++ u8 tx_delay_fe; ++ u8 sds_tx_amplitude; ++ bool keep_pll_enabled; ++ bool auto_sleep_disabled; ++ bool clock_ouput; /* output clock ctl: 0=off, 1=on */ ++ bool clock_freq_125M; /* output clock freq selcect: 0=25M, 1=125M */ ++ bool tx_clk_adj_enabled;/* tx clk adj ctl: 0=off, 1=on */ ++ bool tx_clk_10_inverted; ++ bool tx_clk_100_inverted; ++ bool tx_clk_1000_inverted; + }; + + /** +@@ -593,6 +656,325 @@ static int yt8521_write_page(struct phy_ + return ytphy_modify_ext(phydev, YT8521_REG_SPACE_SELECT_REG, mask, set); + }; + ++static int ytphy_parse_dt(struct phy_device *phydev) ++{ ++ struct device_node *node = phydev->mdio.dev.of_node; ++ struct yt8521_priv *priv = phydev->priv; ++ u32 freq, val; ++ int ret; ++ ++ priv->rx_delay_additional = YTPHY_DTS_INVAL_VAL; ++ priv->sds_tx_amplitude = YTPHY_DTS_INVAL_VAL; ++ priv->rx_delay_basic = YTPHY_DTS_INVAL_VAL; ++ priv->tx_delay_ge = YTPHY_DTS_INVAL_VAL; ++ priv->tx_delay_fe = YTPHY_DTS_INVAL_VAL; ++ ++ if (!IS_ENABLED(CONFIG_OF_MDIO)) { ++ priv->auto_sleep_disabled = true; ++ priv->keep_pll_enabled = true; ++ return 0; ++ } ++ ++ ret = of_property_read_u32(node, "motorcomm,clk-out-frequency", &freq); ++ if (ret < 0) ++ freq = YTPHY_DTS_OUTPUT_CLK_DIS;/* default value as dts*/ ++ ++ switch (freq) { ++ case YTPHY_DTS_OUTPUT_CLK_DIS: ++ priv->clock_ouput = false; ++ break; ++ case YTPHY_DTS_OUTPUT_CLK_25M: ++ priv->clock_freq_125M = false; ++ priv->clock_ouput = true; ++ break; ++ case YTPHY_DTS_OUTPUT_CLK_125M: ++ priv->clock_freq_125M = true; ++ priv->clock_ouput = true; ++ break; ++ default: ++ phydev_err(phydev, "invalid motorcomm,clk-out-frequency\n"); ++ return -EINVAL; ++ } ++ ++ if (!of_property_read_u32(node, "motorcomm,rx-delay-basic", &val)) { ++ if (val > 1) { ++ phydev_err(phydev, ++ "invalid motorcomm,rx-delay-basic\n"); ++ return -EINVAL; ++ } ++ priv->rx_delay_basic = val; ++ } ++ ++ if (!of_property_read_u32(node, "motorcomm,rx-delay-additional-ps", &val)) { ++ if (val > YTPHY_DTS_MAX_DELAY_VAL) { ++ phydev_err(phydev, "invalid motorcomm,rx-delay-additional-ps\n"); ++ return -EINVAL; ++ } ++ if (val) ++ val /= YTPHY_DTS_STEP_DELAY_VAL; ++ priv->rx_delay_additional = val; ++ } ++ ++ if (!of_property_read_u32(node, "motorcomm,tx-delay-fe-ps", &val)) { ++ if (val > YTPHY_DTS_MAX_DELAY_VAL) { ++ phydev_err(phydev, ++ "invalid motorcomm,tx-delay-fe-ps\n"); ++ return -EINVAL; ++ } ++ if (val) ++ val /= YTPHY_DTS_STEP_DELAY_VAL; ++ priv->tx_delay_fe = val; ++ } ++ ++ if (!of_property_read_u32(node, "motorcomm,tx-delay-ge-ps", &val)) { ++ if (val > YTPHY_DTS_MAX_DELAY_VAL) { ++ phydev_err(phydev, ++ "invalid motorcomm,tx-delay-ge-ps\n"); ++ return -EINVAL; ++ } ++ if (val) ++ val /= YTPHY_DTS_STEP_DELAY_VAL; ++ priv->tx_delay_ge = val; ++ } ++ ++ if (of_property_read_bool(node, "motorcomm,keep-pll-enabled")) ++ priv->keep_pll_enabled = true; ++ ++ if (of_property_read_bool(node, "motorcomm,auto-sleep-disabled")) ++ priv->auto_sleep_disabled = true; ++ ++ if (of_property_read_bool(node, "motorcomm,tx-clk-adj-enabled")) ++ priv->tx_clk_adj_enabled = true; ++ if (priv->tx_clk_adj_enabled) { ++ if (of_property_read_bool(node, "motorcomm,tx-clk-10-inverted")) ++ priv->tx_clk_10_inverted = true; ++ if (of_property_read_bool(node, "motorcomm,tx-clk-100-inverted")) ++ priv->tx_clk_100_inverted = true; ++ if (of_property_read_bool(node, "motorcomm,tx-clk-1000-inverted")) ++ priv->tx_clk_1000_inverted = true; ++ } ++ ++ if (!of_property_read_u32(node, "motorcomm,sds-tx-amplitude", &val)) { ++ if (val > YTPHY_DTS_MAX_TX_AMPLITUDE) { ++ phydev_err(phydev, ++ "invalid motorcomm,sds-tx-amplitude\n"); ++ return -EINVAL; ++ } ++ priv->sds_tx_amplitude = val; ++ } ++ ++ return 0; ++} ++ ++static int ytphy_clk_out_config(struct phy_device *phydev) ++{ ++ struct yt8521_priv *priv = phydev->priv; ++ u16 set = 0; ++ u16 mask; ++ ++ switch (phydev->drv->phy_id) { ++ case PHY_ID_YT8511: ++ /* YT8511 will be supported later */ ++ return -EOPNOTSUPP; ++ case PHY_ID_YT8521: ++ mask = YT8521_SCR_SYNCE_ENABLE; ++ if (priv->clock_ouput) { ++ mask |= YT8521_SCR_CLK_SRC_MASK; ++ mask |= YT8521_SCR_CLK_FRE_SEL_MASK; ++ set |= YT8521_SCR_SYNCE_ENABLE; ++ if (priv->clock_freq_125M) { ++ set |= YT8521_SCR_CLK_FRE_SEL_125M; ++ set |= YT8521_SCR_CLK_SRC_PLL_125M; ++ } else { ++ set |= YT8521_SCR_CLK_FRE_SEL_25M; ++ set |= YT8521_SCR_CLK_SRC_REF_25M; ++ } ++ } ++ break; ++ case PHY_ID_YT8531: ++ case PHY_ID_YT8531S: ++ mask = YT8531_SCR_SYNCE_ENABLE; ++ if (priv->clock_ouput) { ++ mask |= YT8531_SCR_CLK_SRC_MASK; ++ mask |= YT8531_SCR_CLK_FRE_SEL_MASK; ++ set |= YT8531_SCR_SYNCE_ENABLE; ++ if (priv->clock_freq_125M) { ++ set |= YT8531_SCR_CLK_FRE_SEL_125M; ++ set |= YT8531_SCR_CLK_SRC_PLL_125M; ++ } else { ++ set |= YT8531_SCR_CLK_FRE_SEL_25M; ++ set |= YT8531_SCR_CLK_SRC_REF_25M; ++ } ++ } ++ break; ++ default: ++ phydev_err(phydev, "invalid phy id\n"); ++ return -EINVAL; ++ } ++ ++ return ytphy_modify_ext(phydev, YTPHY_SYNCE_CFG_REG, mask, set); ++} ++ ++static int ytphy_serdes_tx_amplitude_config(struct phy_device *phydev) ++{ ++ u16 yt8531s_tx_amplitude[] = { YT8531S_SAC2R_TX_AMPLITUDE_LOW, ++ YT8531S_SAC2R_TX_AMPLITUDE_MIDDLE, ++ YT8531S_SAC2R_TX_AMPLITUDE_HIGH }; ++ u16 yt8521_tx_amplitude[] = { YT8521_SAC2R_TX_AMPLITUDE_LOW, ++ YT8521_SAC2R_TX_AMPLITUDE_MIDDLE, ++ YT8521_SAC2R_TX_AMPLITUDE_HIGH }; ++ struct yt8521_priv *priv = phydev->priv; ++ u16 tx_amplitude; ++ ++ if (priv->sds_tx_amplitude == YTPHY_DTS_INVAL_VAL) ++ /* don't config Serdes tx amplitude.*/ ++ return 0; ++ ++ switch (phydev->drv->phy_id) { ++ case PHY_ID_YT8511: ++ case PHY_ID_YT8531: ++ /* YT8511 and YT8531 not support this function.*/ ++ return -EOPNOTSUPP; ++ case PHY_ID_YT8521: ++ tx_amplitude = yt8521_tx_amplitude[priv->sds_tx_amplitude]; ++ break; ++ case PHY_ID_YT8531S: ++ tx_amplitude = yt8531s_tx_amplitude[priv->sds_tx_amplitude]; ++ break; ++ default: ++ phydev_err(phydev, "invalid phy id\n"); ++ return -EINVAL; ++ } ++ ++ return ytphy_modify_ext(phydev, YTPHY_SERDES_ANALOG_CFG2_REG, ++ YTPHY_SAC2R_TX_AMPLITUDE_MASK, tx_amplitude); ++} ++ ++static int ytphy_rgmii_clk_delay_config(struct phy_device *phydev) ++{ ++ struct yt8521_priv *priv = phydev->priv; ++ u16 mask = 0; ++ u16 val = 0; ++ int ret; ++ ++ /* rx delay basic controlled by dts.*/ ++ if (priv->rx_delay_basic != YTPHY_DTS_INVAL_VAL) { ++ if (priv->rx_delay_basic) ++ val = YT8521_CCR_RXC_DLY_EN; ++ ret = ytphy_modify_ext(phydev, YT8521_CHIP_CONFIG_REG, ++ YT8521_CCR_RXC_DLY_EN, val); ++ if (ret < 0) ++ return ret; ++ } ++ ++ val = 0; ++ /* If rx_delay_additional and tx_delay_* are all not be seted in dts, ++ * then used the fixed *_DELAY_DIS or *_DELAY_EN. Otherwise, use the ++ * value set by rx_delay_additional, tx_delay_ge and tx_delay_fe. ++ */ ++ if ((priv->rx_delay_additional & priv->tx_delay_ge & priv->tx_delay_fe) ++ == YTPHY_DTS_INVAL_VAL) { ++ switch (phydev->interface) { ++ case PHY_INTERFACE_MODE_RGMII: ++ val |= YT8521_RC1R_GE_TX_DELAY_DIS; ++ val |= YT8521_RC1R_FE_TX_DELAY_DIS; ++ val |= YT8521_RC1R_RX_DELAY_DIS; ++ break; ++ case PHY_INTERFACE_MODE_RGMII_RXID: ++ val |= YT8521_RC1R_GE_TX_DELAY_DIS; ++ val |= YT8521_RC1R_FE_TX_DELAY_DIS; ++ val |= YT8521_RC1R_RX_DELAY_EN; ++ break; ++ case PHY_INTERFACE_MODE_RGMII_TXID: ++ val |= YT8521_RC1R_GE_TX_DELAY_EN; ++ val |= YT8521_RC1R_FE_TX_DELAY_EN; ++ val |= YT8521_RC1R_RX_DELAY_DIS; ++ break; ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ val |= YT8521_RC1R_GE_TX_DELAY_EN; ++ val |= YT8521_RC1R_FE_TX_DELAY_EN; ++ val |= YT8521_RC1R_RX_DELAY_EN; ++ break; ++ default: /* do not support other modes */ ++ return -EOPNOTSUPP; ++ } ++ mask = YT8521_RC1R_RX_DELAY_MASK | YT8521_RC1R_FE_TX_DELAY_MASK ++ | YT8521_RC1R_GE_TX_DELAY_MASK; ++ } else { ++ switch (phydev->interface) { ++ case PHY_INTERFACE_MODE_RGMII: ++ case PHY_INTERFACE_MODE_RGMII_RXID: ++ case PHY_INTERFACE_MODE_RGMII_TXID: ++ case PHY_INTERFACE_MODE_RGMII_ID: ++ if (priv->rx_delay_additional != YTPHY_DTS_INVAL_VAL) { ++ mask |= YT8521_RC1R_RX_DELAY_MASK; ++ val |= (priv->rx_delay_additional) << YT8521_RC1R_RX_DELAY_BIT; ++ } ++ if (priv->tx_delay_fe != YTPHY_DTS_INVAL_VAL) { ++ mask |= YT8521_RC1R_FE_TX_DELAY_MASK; ++ val |= (priv->tx_delay_fe) << YT8521_RC1R_FE_TX_DELAY_BIT; ++ } ++ if (priv->tx_delay_ge != YTPHY_DTS_INVAL_VAL) { ++ mask |= YT8521_RC1R_GE_TX_DELAY_MASK; ++ val |= (priv->tx_delay_ge) << YT8521_RC1R_GE_TX_DELAY_BIT; ++ } ++ break; ++ default: /* do not support other modes */ ++ return -EOPNOTSUPP; ++ } ++ } ++ ++ return ytphy_modify_ext(phydev, YT8521_RGMII_CONFIG1_REG, mask, val); ++} ++ ++static int ytphy_clk_delay_config(struct phy_device *phydev) ++{ ++ switch (phydev->drv->phy_id) { ++ case PHY_ID_YT8511: ++ /* YT8511 will be supported later */ ++ return -EOPNOTSUPP; ++ case PHY_ID_YT8521: ++ case PHY_ID_YT8531S: ++ /* YT8521 and YT8531S support SGMII mode, but don't need ++ * delay. ++ */ ++ if (phydev->interface == PHY_INTERFACE_MODE_SGMII) ++ return 0; ++ ++ return ytphy_rgmii_clk_delay_config(phydev); ++ case PHY_ID_YT8531: ++ /* YT8531 don't support SGMII mode. */ ++ return ytphy_rgmii_clk_delay_config(phydev); ++ default: ++ phydev_err(phydev, "invalid phy id\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int ytphy_probe_helper(struct phy_device *phydev) ++{ ++ struct device *dev = &phydev->mdio.dev; ++ struct yt8521_priv *priv; ++ int ret; ++ ++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ phydev->priv = priv; ++ ++ ret = ytphy_parse_dt(phydev); ++ if (ret < 0) ++ return ret; ++ ++ phy_lock_mdio_bus(phydev); ++ ret = ytphy_clk_out_config(phydev); ++ phy_unlock_mdio_bus(phydev); ++ return ret; ++} ++ + /** + * yt8521_probe() - read chip config then set suitable polling_mode + * @phydev: a pointer to a &struct phy_device +@@ -601,16 +983,15 @@ static int yt8521_write_page(struct phy_ + */ + static int yt8521_probe(struct phy_device *phydev) + { +- struct device *dev = &phydev->mdio.dev; + struct yt8521_priv *priv; + int chip_config; + int ret; + +- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); +- if (!priv) +- return -ENOMEM; ++ ret = ytphy_probe_helper(phydev); ++ if (ret < 0) ++ return ret; + +- phydev->priv = priv; ++ priv = phydev->priv; + + chip_config = ytphy_read_ext_with_lock(phydev, YT8521_CHIP_CONFIG_REG); + if (chip_config < 0) +@@ -652,26 +1033,6 @@ static int yt8521_probe(struct phy_devic + } + + /** +- * yt8531s_probe() - read chip config then set suitable polling_mode +- * @phydev: a pointer to a &struct phy_device +- * +- * returns 0 or negative errno code +- */ +-static int yt8531s_probe(struct phy_device *phydev) +-{ +- int ret; +- +- /* Disable SyncE clock output by default */ +- ret = ytphy_modify_ext_with_lock(phydev, YT8531S_SYNCE_CFG_REG, +- YT8531S_SCR_SYNCE_ENABLE, 0); +- if (ret < 0) +- return ret; +- +- /* same as yt8521_probe */ +- return yt8521_probe(phydev); +-} +- +-/** + * ytphy_utp_read_lpa() - read LPA then setup lp_advertising for utp + * @phydev: a pointer to a &struct phy_device + * +@@ -1125,6 +1486,34 @@ static int yt8521_resume(struct phy_devi + return yt8521_modify_utp_fiber_bmcr(phydev, BMCR_PDOWN, 0); + } + ++static int ytphy_config_init_helper(struct phy_device *phydev) ++{ ++ struct yt8521_priv *priv = phydev->priv; ++ int ret; ++ ++ ret = ytphy_clk_delay_config(phydev); ++ if (ret < 0) ++ return ret; ++ ++ /* disable auto sleep */ ++ if (priv->auto_sleep_disabled) { ++ ret = ytphy_modify_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1_REG, ++ YT8521_ESC1R_SLEEP_SW, 0); ++ if (ret < 0) ++ return ret; ++ } ++ ++ /* enable RXC clock when no wire plug */ ++ if (priv->keep_pll_enabled) { ++ ret = ytphy_modify_ext(phydev, YT8521_CLOCK_GATING_REG, ++ YT8521_CGR_RX_CLK_EN, 0); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ + /** + * yt8521_config_init() - called to initialize the PHY + * @phydev: a pointer to a &struct phy_device +@@ -1135,59 +1524,21 @@ static int yt8521_config_init(struct phy + { + int old_page; + int ret = 0; +- u16 val; + + old_page = phy_select_page(phydev, YT8521_RSSR_UTP_SPACE); + if (old_page < 0) + goto err_restore_page; + +- switch (phydev->interface) { +- case PHY_INTERFACE_MODE_RGMII: +- val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_FE_TX_DELAY_DIS; +- val |= YT8521_RC1R_RX_DELAY_DIS; +- break; +- case PHY_INTERFACE_MODE_RGMII_RXID: +- val = YT8521_RC1R_GE_TX_DELAY_DIS | YT8521_RC1R_FE_TX_DELAY_DIS; +- val |= YT8521_RC1R_RX_DELAY_EN; +- break; +- case PHY_INTERFACE_MODE_RGMII_TXID: +- val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_FE_TX_DELAY_EN; +- val |= YT8521_RC1R_RX_DELAY_DIS; +- break; +- case PHY_INTERFACE_MODE_RGMII_ID: +- val = YT8521_RC1R_GE_TX_DELAY_EN | YT8521_RC1R_FE_TX_DELAY_EN; +- val |= YT8521_RC1R_RX_DELAY_EN; +- break; +- case PHY_INTERFACE_MODE_SGMII: +- break; +- default: /* do not support other modes */ +- ret = -EOPNOTSUPP; +- goto err_restore_page; +- } +- +- /* set rgmii delay mode */ +- if (phydev->interface != PHY_INTERFACE_MODE_SGMII) { +- ret = ytphy_modify_ext(phydev, YT8521_RGMII_CONFIG1_REG, +- (YT8521_RC1R_RX_DELAY_MASK | +- YT8521_RC1R_FE_TX_DELAY_MASK | +- YT8521_RC1R_GE_TX_DELAY_MASK), +- val); +- if (ret < 0) +- goto err_restore_page; +- } +- +- /* disable auto sleep */ +- ret = ytphy_modify_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1_REG, +- YT8521_ESC1R_SLEEP_SW, 0); ++ ret = ytphy_config_init_helper(phydev); + if (ret < 0) + goto err_restore_page; + +- /* enable RXC clock when no wire plug */ +- ret = ytphy_modify_ext(phydev, YT8521_CLOCK_GATING_REG, +- YT8521_CGR_RX_CLK_EN, 0); ++ ret = yt8521_write_page(phydev, YT8521_RSSR_FIBER_SPACE); + if (ret < 0) + goto err_restore_page; + ++ ret = ytphy_serdes_tx_amplitude_config(phydev); ++ + err_restore_page: + return phy_restore_page(phydev, old_page, ret); + } +@@ -1778,7 +2129,7 @@ static struct phy_driver motorcomm_phy_d + PHY_ID_MATCH_EXACT(PHY_ID_YT8531S), + .name = "YT8531S Gigabit Ethernet", + .get_features = yt8521_get_features, +- .probe = yt8531s_probe, ++ .probe = yt8521_probe, + .read_page = yt8521_read_page, + .write_page = yt8521_write_page, + .get_wol = ytphy_get_wol, +@@ -1804,7 +2155,7 @@ static const struct mdio_device_id __may + { PHY_ID_MATCH_EXACT(PHY_ID_YT8511) }, + { PHY_ID_MATCH_EXACT(PHY_ID_YT8521) }, + { PHY_ID_MATCH_EXACT(PHY_ID_YT8531S) }, +- { /* sentinal */ } ++ { /* sentinel */ } + }; + + MODULE_DEVICE_TABLE(mdio, motorcomm_tbl); diff --git a/target/linux/rockchip/patches-5.15/110-net-phy-Add-driver-for-Motorcomm-yt8531.patch b/target/linux/rockchip/patches-5.15/110-net-phy-Add-driver-for-Motorcomm-yt8531.patch new file mode 100644 index 0000000000..78fe0e3dd3 --- /dev/null +++ b/target/linux/rockchip/patches-5.15/110-net-phy-Add-driver-for-Motorcomm-yt8531.patch @@ -0,0 +1,223 @@ +From: Frank +To: Peter Geis , Andrew Lunn , + Heiner Kallweit , + Russell King , + "David S . Miller" , + Eric Dumazet , + Jakub Kicinski , Paolo Abeni , + Rob Herring , + Krzysztof Kozlowski +Cc: xiaogang.fan@motor-comm.com, fei.zhang@motor-comm.com, + hua.sun@motor-comm.com, netdev@vger.kernel.org, + linux-kernel@vger.kernel.org, Frank , + devicetree@vger.kernel.org +Subject: [PATCH net-next v1 3/3] net: phy: Add driver for Motorcomm yt8531 gigabit ethernet phy +Date: Thu, 5 Jan 2023 15:30:24 +0800 [thread overview] +Message-ID: <20230105073024.8390-4-Frank.Sae@motor-comm.com> (raw) +In-Reply-To: <20230105073024.8390-1-Frank.Sae@motor-comm.com> + +Add driver for Motorcomm yt8531 gigabit ethernet phy. This patch has +been tested on AM335x platform which has one YT8531 interface +card and passed all test cases. + +Signed-off-by: Frank +--- + drivers/net/phy/Kconfig | 2 +- + drivers/net/phy/motorcomm.c | 127 +++++++++++++++++++++++++++++++++++- + 2 files changed, 126 insertions(+), 3 deletions(-) + +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -319,7 +319,7 @@ config MOTORCOMM_PHY + tristate "Motorcomm PHYs" + help + Enables support for Motorcomm network PHYs. +- Currently supports the YT8511, YT8521, YT8531S Gigabit Ethernet PHYs. ++ Currently supports the YT8511, YT8521, YT8531, YT8531S Gigabit Ethernet PHYs. + + config NATIONAL_PHY + tristate "National Semiconductor PHYs" +--- a/drivers/net/phy/motorcomm.c ++++ b/drivers/net/phy/motorcomm.c +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0+ + /* +- * Motorcomm 8511/8521/8531S PHY driver. ++ * Motorcomm 8511/8521/8531/8531S PHY driver. + * + * Author: Peter Geis + * Author: Frank +@@ -14,6 +14,7 @@ + + #define PHY_ID_YT8511 0x0000010a + #define PHY_ID_YT8521 0x0000011a ++#define PHY_ID_YT8531 0x4f51e91b + #define PHY_ID_YT8531S 0x4f51e91a + + /* YT8521/YT8531S Register Overview +@@ -542,6 +543,69 @@ err_restore_page: + return phy_restore_page(phydev, old_page, ret); + } + ++static int yt8531_set_wol(struct phy_device *phydev, ++ struct ethtool_wolinfo *wol) ++{ ++ struct net_device *p_attached_dev; ++ const u16 mac_addr_reg[] = { ++ YTPHY_WOL_MACADDR2_REG, ++ YTPHY_WOL_MACADDR1_REG, ++ YTPHY_WOL_MACADDR0_REG, ++ }; ++ const u8 *mac_addr; ++ u16 mask; ++ u16 val; ++ int ret; ++ u8 i; ++ ++ if (wol->wolopts & WAKE_MAGIC) { ++ p_attached_dev = phydev->attached_dev; ++ if (!p_attached_dev) ++ return -ENODEV; ++ ++ mac_addr = (const u8 *)p_attached_dev->dev_addr; ++ if (!is_valid_ether_addr(mac_addr)) ++ return -EINVAL; ++ ++ /* Store the device address for the magic packet */ ++ for (i = 0; i < 3; i++) { ++ ret = ytphy_write_ext_with_lock(phydev, mac_addr_reg[i], ++ ((mac_addr[i * 2] << 8)) | ++ (mac_addr[i * 2 + 1])); ++ if (ret < 0) ++ return ret; ++ } ++ ++ /* Enable WOL feature */ ++ mask = YTPHY_WCR_PULSE_WIDTH_MASK | YTPHY_WCR_INTR_SEL; ++ val = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL; ++ val |= YTPHY_WCR_TYPE_PULSE | YTPHY_WCR_PULSE_WIDTH_672MS; ++ ret = ytphy_modify_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG, ++ mask, val); ++ if (ret < 0) ++ return ret; ++ ++ /* Enable WOL interrupt */ ++ ret = phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG, 0, ++ YTPHY_IER_WOL); ++ if (ret < 0) ++ return ret; ++ } else { ++ /* Disable WOL feature */ ++ mask = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL; ++ ret = ytphy_modify_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG, ++ mask, 0); ++ ++ /* Disable WOL interrupt */ ++ ret = phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG, ++ YTPHY_IER_WOL, 0); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ + static int yt8511_read_page(struct phy_device *phydev) + { + return __phy_read(phydev, YT8511_PAGE_SELECT); +@@ -1032,6 +1096,11 @@ static int yt8521_probe(struct phy_devic + return 0; + } + ++static int yt8531_probe(struct phy_device *phydev) ++{ ++ return ytphy_probe_helper(phydev); ++} ++ + /** + * ytphy_utp_read_lpa() - read LPA then setup lp_advertising for utp + * @phydev: a pointer to a &struct phy_device +@@ -1543,6 +1612,48 @@ err_restore_page: + return phy_restore_page(phydev, old_page, ret); + } + ++static int yt8531_config_init(struct phy_device *phydev) ++{ ++ int ret; ++ ++ phy_lock_mdio_bus(phydev); ++ ret = ytphy_config_init_helper(phydev); ++ phy_unlock_mdio_bus(phydev); ++ ++ return ret; ++} ++ ++static void yt8531_link_change_notify(struct phy_device *phydev) ++{ ++ struct yt8521_priv *priv = phydev->priv; ++ u16 val = 0; ++ ++ if (!(priv->tx_clk_adj_enabled)) ++ return; ++ ++ if (phydev->speed < 0) ++ return; ++ ++ switch (phydev->speed) { ++ case SPEED_1000: ++ if (priv->tx_clk_1000_inverted) ++ val = YT8521_RC1R_TX_CLK_SEL_INVERTED; ++ break; ++ case SPEED_100: ++ if (priv->tx_clk_100_inverted) ++ val = YT8521_RC1R_TX_CLK_SEL_INVERTED; ++ break; ++ case SPEED_10: ++ if (priv->tx_clk_10_inverted) ++ val = YT8521_RC1R_TX_CLK_SEL_INVERTED; ++ break; ++ default: ++ return; ++ } ++ ytphy_modify_ext_with_lock(phydev, YT8521_RGMII_CONFIG1_REG, ++ YT8521_RC1R_TX_CLK_SEL_MASK, val); ++} ++ + /** + * yt8521_prepare_fiber_features() - A small helper function that setup + * fiber's features. +@@ -2126,6 +2237,17 @@ static struct phy_driver motorcomm_phy_d + .resume = yt8521_resume, + }, + { ++ PHY_ID_MATCH_EXACT(PHY_ID_YT8531), ++ .name = "YT8531 Gigabit Ethernet", ++ .probe = yt8531_probe, ++ .config_init = yt8531_config_init, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .get_wol = ytphy_get_wol, ++ .set_wol = yt8531_set_wol, ++ .link_change_notify = yt8531_link_change_notify, ++ }, ++ { + PHY_ID_MATCH_EXACT(PHY_ID_YT8531S), + .name = "YT8531S Gigabit Ethernet", + .get_features = yt8521_get_features, +@@ -2146,7 +2268,7 @@ static struct phy_driver motorcomm_phy_d + + module_phy_driver(motorcomm_phy_drvs); + +-MODULE_DESCRIPTION("Motorcomm 8511/8521/8531S PHY driver"); ++MODULE_DESCRIPTION("Motorcomm 8511/8521/8531/8531S PHY driver"); + MODULE_AUTHOR("Peter Geis"); + MODULE_AUTHOR("Frank"); + MODULE_LICENSE("GPL"); +@@ -2154,6 +2276,7 @@ MODULE_LICENSE("GPL"); + static const struct mdio_device_id __maybe_unused motorcomm_tbl[] = { + { PHY_ID_MATCH_EXACT(PHY_ID_YT8511) }, + { PHY_ID_MATCH_EXACT(PHY_ID_YT8521) }, ++ { PHY_ID_MATCH_EXACT(PHY_ID_YT8531) }, + { PHY_ID_MATCH_EXACT(PHY_ID_YT8531S) }, + { /* sentinel */ } + };