From 2c931ae9788444a8994928366c18d540c9a760eb Mon Sep 17 00:00:00 2001 From: Peter Geis Date: Thu, 20 May 2021 12:32:30 -0400 Subject: [PATCH 1/4] net: phy: add driver for Motorcomm yt8511 phy Add a driver for the Motorcomm yt8511 phy that will be used in the production Pine64 rk3566-quartz64 development board. It supports gigabit transfer speeds, rgmii, and 125mhz clk output. Signed-off-by: Peter Geis Signed-off-by: David S. Miller --- MAINTAINERS | 6 ++ drivers/net/phy/Kconfig | 6 ++ drivers/net/phy/Makefile | 1 + drivers/net/phy/motorcomm.c | 136 ++++++++++++++++++++++++++++++++++++ 4 files changed, 149 insertions(+) create mode 100644 drivers/net/phy/motorcomm.c diff --git a/MAINTAINERS b/MAINTAINERS index 4fef10dd2975..0d1d0845da3b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11812,6 +11812,12 @@ F: Documentation/userspace-api/media/drivers/meye* F: drivers/media/pci/meye/ F: include/uapi/linux/meye.h +MOTORCOMM PHY DRIVER +M: Peter Geis +L: netdev@vger.kernel.org +S: Maintained +F: drivers/net/phy/motorcomm.c + MOXA SMARTIO/INDUSTIO/INTELLIO SERIAL CARD M: Jiri Slaby S: Maintained diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 698bea312adc..940f2d701f86 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -223,6 +223,12 @@ config MICROSEMI_PHY help Currently supports VSC8514, VSC8530, VSC8531, VSC8540 and VSC8541 PHYs +config MOTORCOMM_PHY + tristate "Motorcomm PHYs" + help + Enables support for Motorcomm network PHYs. + Currently supports the YT8511 gigabit PHY. + config NATIONAL_PHY tristate "National Semiconductor PHYs" help diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index a13e402074cf..5b9b714b8dbd 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -69,6 +69,7 @@ obj-$(CONFIG_MICREL_PHY) += micrel.o obj-$(CONFIG_MICROCHIP_PHY) += microchip.o obj-$(CONFIG_MICROCHIP_T1_PHY) += microchip_t1.o obj-$(CONFIG_MICROSEMI_PHY) += mscc/ +obj-$(CONFIG_MOTORCOMM_PHY) += motorcomm.o obj-$(CONFIG_NATIONAL_PHY) += national.o obj-$(CONFIG_NXP_TJA11XX_PHY) += nxp-tja11xx.o obj-$(CONFIG_QSEMI_PHY) += qsemi.o diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c new file mode 100644 index 000000000000..796b68f4b499 --- /dev/null +++ b/drivers/net/phy/motorcomm.c @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for Motorcomm PHYs + * + * Author: Peter Geis + */ + +#include +#include +#include + +#define PHY_ID_YT8511 0x0000010a + +#define YT8511_PAGE_SELECT 0x1e +#define YT8511_PAGE 0x1f +#define YT8511_EXT_CLK_GATE 0x0c +#define YT8511_EXT_DELAY_DRIVE 0x0d +#define YT8511_EXT_SLEEP_CTRL 0x27 + +/* 2b00 25m from pll + * 2b01 25m from xtl *default* + * 2b10 62.m from pll + * 2b11 125m from pll + */ +#define YT8511_CLK_125M (BIT(2) | BIT(1)) +#define YT8511_PLLON_SLP BIT(14) + +/* RX Delay enabled = 1.8ns 1000T, 8ns 10/100T */ +#define YT8511_DELAY_RX BIT(0) + +/* TX Gig-E Delay is bits 7:4, default 0x5 + * TX Fast-E Delay is bits 15:12, default 0xf + * Delay = 150ps * N - 250ps + * On = 2000ps, off = 50ps + */ +#define YT8511_DELAY_GE_TX_EN (0xf << 4) +#define YT8511_DELAY_GE_TX_DIS (0x2 << 4) +#define YT8511_DELAY_FE_TX_EN (0xf << 12) +#define YT8511_DELAY_FE_TX_DIS (0x2 << 12) + +static int yt8511_read_page(struct phy_device *phydev) +{ + return __phy_read(phydev, YT8511_PAGE_SELECT); +}; + +static int yt8511_write_page(struct phy_device *phydev, int page) +{ + return __phy_write(phydev, YT8511_PAGE_SELECT, page); +}; + +static int yt8511_config_init(struct phy_device *phydev) +{ + unsigned int ge, fe; + int ret, oldpage; + + /* set clock mode to 125mhz */ + oldpage = phy_select_page(phydev, YT8511_EXT_CLK_GATE); + if (oldpage < 0) + goto err_restore_page; + + ret = __phy_modify(phydev, YT8511_PAGE, 0, YT8511_CLK_125M); + if (ret < 0) + goto err_restore_page; + + /* set rgmii delay mode */ + switch (phydev->interface) { + case PHY_INTERFACE_MODE_RGMII: + ge = YT8511_DELAY_GE_TX_DIS; + fe = YT8511_DELAY_FE_TX_DIS; + break; + case PHY_INTERFACE_MODE_RGMII_RXID: + ge = YT8511_DELAY_RX | YT8511_DELAY_GE_TX_DIS; + fe = YT8511_DELAY_FE_TX_DIS; + break; + case PHY_INTERFACE_MODE_RGMII_TXID: + ge = YT8511_DELAY_GE_TX_EN; + fe = YT8511_DELAY_FE_TX_EN; + break; + case PHY_INTERFACE_MODE_RGMII_ID: + ge = YT8511_DELAY_RX | YT8511_DELAY_GE_TX_EN; + fe = YT8511_DELAY_FE_TX_EN; + break; + default: /* leave everything alone in other modes */ + break; + } + + ret = __phy_modify(phydev, YT8511_PAGE, (YT8511_DELAY_RX | YT8511_DELAY_GE_TX_EN), ge); + if (ret < 0) + goto err_restore_page; + + /* fast ethernet delay is in a separate page */ + ret = __phy_write(phydev, YT8511_PAGE_SELECT, YT8511_EXT_DELAY_DRIVE); + if (ret < 0) + goto err_restore_page; + + ret = __phy_modify(phydev, YT8511_PAGE, YT8511_DELAY_FE_TX_EN, fe); + if (ret < 0) + goto err_restore_page; + + /* leave pll enabled in sleep */ + ret = __phy_write(phydev, YT8511_PAGE_SELECT, YT8511_EXT_SLEEP_CTRL); + if (ret < 0) + goto err_restore_page; + + ret = __phy_modify(phydev, YT8511_PAGE, 0, YT8511_PLLON_SLP); + if (ret < 0) + goto err_restore_page; + +err_restore_page: + return phy_restore_page(phydev, oldpage, ret); +} + +static struct phy_driver motorcomm_phy_drvs[] = { + { + PHY_ID_MATCH_EXACT(PHY_ID_YT8511), + .name = "YT8511 Gigabit Ethernet", + .config_init = yt8511_config_init, + .suspend = genphy_suspend, + .resume = genphy_resume, + .read_page = yt8511_read_page, + .write_page = yt8511_write_page, + }, +}; + +module_phy_driver(motorcomm_phy_drvs); + +MODULE_DESCRIPTION("Motorcomm PHY driver"); +MODULE_AUTHOR("Peter Geis"); +MODULE_LICENSE("GPL"); + +static const struct mdio_device_id __maybe_unused motorcomm_tbl[] = { + { PHY_ID_MATCH_EXACT(PHY_ID_YT8511) }, + { /* sentinal */ } +}; + +MODULE_DEVICE_TABLE(mdio, motorcomm_tbl); -- 2.30.2 From 5e464ac4cd49603515a053b7292597bfd0e9672c Mon Sep 17 00:00:00 2001 From: Peter Geis Date: Sat, 29 May 2021 07:05:55 -0400 Subject: [PATCH 2/4] net: phy: fix yt8511 clang uninitialized variable warning clang doesn't preinitialize variables. If phy_select_page failed and returned an error, phy_restore_page would be called with `ret` being uninitialized. Even though phy_restore_page won't use `ret` in this scenario, initialize `ret` to silence the warning. Fixes: 48e8c6f1612b ("net: phy: add driver for Motorcomm yt8511 phy") Reported-by: kernel test robot Reviewed-by: Andrew Lunn Signed-off-by: Peter Geis Signed-off-by: Jakub Kicinski --- drivers/net/phy/motorcomm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c index 796b68f4b499..68cd19540c67 100644 --- a/drivers/net/phy/motorcomm.c +++ b/drivers/net/phy/motorcomm.c @@ -50,8 +50,8 @@ static int yt8511_write_page(struct phy_device *phydev, int page) static int yt8511_config_init(struct phy_device *phydev) { + int oldpage, ret = 0; unsigned int ge, fe; - int ret, oldpage; /* set clock mode to 125mhz */ oldpage = phy_select_page(phydev, YT8511_EXT_CLK_GATE); -- 2.30.2 From a085ebd8f84bb556116ab41961c4fa671dc84fba Mon Sep 17 00:00:00 2001 From: Peter Geis Date: Sat, 29 May 2021 07:05:56 -0400 Subject: [PATCH 3/4] net: phy: abort loading yt8511 driver in unsupported modes While investigating the clang `ge` uninitialized variable report, it was discovered the default switch would have unintended consequences. Due to the switch to __phy_modify, the driver would modify the ID values in the default scenario. Fix this by promoting the interface mode switch and aborting when the mode is not a supported RGMII mode. This prevents the `ge` and `fe` variables from ever being used uninitialized. Fixes: 48e8c6f1612b ("net: phy: add driver for Motorcomm yt8511 phy") Reported-by: kernel test robot Reviewed-by: Andrew Lunn Signed-off-by: Peter Geis Signed-off-by: Jakub Kicinski --- drivers/net/phy/motorcomm.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c index 68cd19540c67..7e6ac2c5e27e 100644 --- a/drivers/net/phy/motorcomm.c +++ b/drivers/net/phy/motorcomm.c @@ -53,15 +53,10 @@ static int yt8511_config_init(struct phy_device *phydev) int oldpage, ret = 0; unsigned int ge, fe; - /* set clock mode to 125mhz */ oldpage = phy_select_page(phydev, YT8511_EXT_CLK_GATE); if (oldpage < 0) goto err_restore_page; - ret = __phy_modify(phydev, YT8511_PAGE, 0, YT8511_CLK_125M); - if (ret < 0) - goto err_restore_page; - /* set rgmii delay mode */ switch (phydev->interface) { case PHY_INTERFACE_MODE_RGMII: @@ -80,14 +75,20 @@ static int yt8511_config_init(struct phy_device *phydev) ge = YT8511_DELAY_RX | YT8511_DELAY_GE_TX_EN; fe = YT8511_DELAY_FE_TX_EN; break; - default: /* leave everything alone in other modes */ - break; + default: /* do not support other modes */ + ret = -EOPNOTSUPP; + goto err_restore_page; } ret = __phy_modify(phydev, YT8511_PAGE, (YT8511_DELAY_RX | YT8511_DELAY_GE_TX_EN), ge); if (ret < 0) goto err_restore_page; + /* set clock mode to 125mhz */ + ret = __phy_modify(phydev, YT8511_PAGE, 0, YT8511_CLK_125M); + if (ret < 0) + goto err_restore_page; + /* fast ethernet delay is in a separate page */ ret = __phy_write(phydev, YT8511_PAGE_SELECT, YT8511_EXT_DELAY_DRIVE); if (ret < 0) -- 2.30.2