Hi Sascha, On Tue, Jun 6, 2023 at 10:17 AM <yegorslists@xxxxxxxxxxxxxx> wrote: > > From: Yegor Yefremov <yegorslists@xxxxxxxxxxxxxx> > > The driver corresponds to the kernel 6.1.27. > > Signed-off-by: Yegor Yefremov <yegorslists@xxxxxxxxxxxxxx> > --- > Changes: > v1 -> v2: add the related kernel version > > drivers/net/phy/Kconfig | 5 ++ > drivers/net/phy/Makefile | 1 + > drivers/net/phy/motorcomm.c | 128 ++++++++++++++++++++++++++++++++++++ > 3 files changed, 134 insertions(+) > create mode 100644 drivers/net/phy/motorcomm.c > > diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig > index cd20e1de27..e95e2a3228 100644 > --- a/drivers/net/phy/Kconfig > +++ b/drivers/net/phy/Kconfig > @@ -50,6 +50,11 @@ config MICREL_PHY > help > Supports the KSZ9021, VSC8201, KS8001 PHYs. > > +config MOTORCOMM_PHY > + bool "Driver for Motorcomm PHYs" > + help > + Currently supports the YT8511 PHY. > + > config NATIONAL_PHY > bool "Driver for National Semiconductor PHYs" > help > diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile > index 83f46f11d3..26e4ad884d 100644 > --- a/drivers/net/phy/Makefile > +++ b/drivers/net/phy/Makefile > @@ -6,6 +6,7 @@ obj-$(CONFIG_DAVICOM_PHY) += davicom.o > obj-$(CONFIG_LXT_PHY) += lxt.o > obj-$(CONFIG_MARVELL_PHY) += marvell.o > obj-$(CONFIG_MICREL_PHY) += micrel.o > +obj-$(CONFIG_MOTORCOMM_PHY) += motorcomm.o > obj-$(CONFIG_NATIONAL_PHY) += national.o > obj-$(CONFIG_REALTEK_PHY) += realtek.o > obj-$(CONFIG_SMSC_PHY) += smsc.o > diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c > new file mode 100644 > index 0000000000..4bcd84342c > --- /dev/null > +++ b/drivers/net/phy/motorcomm.c > @@ -0,0 +1,128 @@ > +// SPDX-License-Identifier: GPL-2.0-or-later > +/* > + * drivers/net/phy/motorcomm.c > + * > + * Driver for Motorcomm PHYs > + * > + * Author: Peter Geis <pgwipeout@xxxxxxxxx> > + */ > + > +#include <common.h> > +#include <init.h> > +#include <linux/phy.h> > +#include <linux/mdio.h> > + > +#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) > +{ > + int oldpage, ret = 0; > + unsigned int ge, fe; > + > + oldpage = phy_select_page(phydev, YT8511_EXT_CLK_GATE); > + if (oldpage < 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: /* 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) > + 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); As for this approach, it is also used by some other drivers in the Linux kernel: drivers/net/phy/realtek.c drivers/net/phy/icplus.c Regards, Yegor > +} > + > +static struct phy_driver motorcomm_phy_drvs[] = { > + { > + .phy_id = PHY_ID_YT8511, > + .drv.name = "YT8511 Gigabit Ethernet", > + .config_init = yt8511_config_init, > + .features = PHY_GBIT_FEATURES, > + .read_page = yt8511_read_page, > + .write_page = yt8511_write_page, > + }, > +}; > + > +device_phy_drivers(motorcomm_phy_drvs); > -- > 2.34.1 >