Messages in this thread | | | From | Peter Geis <> | Date | Mon, 20 Jun 2022 07:14:01 -0400 | Subject | Re: [PATCH v1] net: phy: Add driver for Motorcomm yt8521 gigabit ethernet phy |
| |
On Sun, Jun 19, 2022 at 10:37 PM Frank <Frank.Sae@motor-comm.com> wrote: > > Add a driver for the motorcomm yt8521 gigabit ethernet phy. We have verified > the driver on StarFive VisionFive development board, which is developed by > Shanghai StarFive Technology Co., Ltd.. On the board, yt8521 gigabit ethernet > phy works in utp mode, RGMII interface, supports 1000M/100M/10M speeds, and > wol(magic package). > > Signed-off-by: Frank <Frank.Sae@motor-comm.com>
Good Morning Frank,
Please run this patch through checkpatch.pl --strict. I will try to avoid repeating what other reviewers have stated already.
> --- > MAINTAINERS | 1 + > drivers/net/phy/Kconfig | 2 +- > drivers/net/phy/motorcomm.c | 819 +++++++++++++++++++++++++++++++++++- > 3 files changed, 817 insertions(+), 5 deletions(-) > > diff --git a/MAINTAINERS b/MAINTAINERS > index a6d3bd9..59813c5 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -13437,6 +13437,7 @@ F: include/uapi/linux/meye.h > > MOTORCOMM PHY DRIVER > M: Peter Geis <pgwipeout@gmail.com> > +M: Frank <Frank.Sae@motor-comm.com> > L: netdev@vger.kernel.org > S: Maintained > F: drivers/net/phy/motorcomm.c > diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig > index 9fee639..9061c76 100644 > --- a/drivers/net/phy/Kconfig > +++ b/drivers/net/phy/Kconfig > @@ -253,7 +253,7 @@ config MOTORCOMM_PHY > tristate "Motorcomm PHYs" > help > Enables support for Motorcomm network PHYs. > - Currently supports the YT8511 gigabit PHY. > + Currently supports the YT8511, YT8521 Gigabit Ethernet PHYs. > > config NATIONAL_PHY > tristate "National Semiconductor PHYs" > diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c > index 7e6ac2c..2db2ef3 100644 > --- a/drivers/net/phy/motorcomm.c > +++ b/drivers/net/phy/motorcomm.c > @@ -1,15 +1,112 @@ > // SPDX-License-Identifier: GPL-2.0+ > /* > - * Driver for Motorcomm PHYs > + * motorcomm.c: Motorcomm 8511/8521 PHY driver. > * > - * Author: Peter Geis <pgwipeout@gmail.com> > + * Author: Frank <Frank.Sae@motor-comm.com> > */
Hello there.
> > #include <linux/kernel.h> > #include <linux/module.h> > #include <linux/phy.h> > +#include <linux/netdevice.h> > +#include <linux/etherdevice.h> > + > +// clang-format off
Is there a particular reason you are including clang-format off? This strikes me as something that should stay in your IDE.
> +#define YTPHY_KERNEL_DRIVER_VERSION "1.0.47" > +#define YT8521_DRIVER_ENAME "YT8521 Gigabit Ethernet" > + > > #define PHY_ID_YT8511 0x0000010a > +#define PHY_ID_YT8521 0x0000011A > +#define YT_PHY_ID_MASK 0x00000FFF > + > +/* Specific Function Control Register */ > +#define YTPHY_SPECIFIC_FUNCTION_CONTROL_REG 0x10 > + > +/* 2b00 Manual MDI configuration > + * 2b01 Manual MDIX configuration > + * 2b10 Reserved > + * 2b11 Enable automatic crossover for all modes *default* > + */ > +#define YTPHY_SFCR_MDI_CROSSOVER_MODE_MASK (BIT(6) | BIT(5)) > +#define YTPHY_SFCR_CROSSOVER_EN BIT(3) > +#define YTPHY_SFCR_SQE_TEST_EN BIT(2) > +#define YTPHY_SFCR_POLARITY_REVERSAL_EN BIT(1) > +#define YTPHY_SFCR_JABBER_DIS BIT(0) > + > +/* Specific Status Register */ > +#define YTPHY_SPECIFIC_STATUS_REG 0x11 > +#define YTPHY_SSR_SPEED_MODE_BIT 14 > + > +/* 2b00 10 Mbps > + * 2b01 100 Mbps > + * 2b10 1000 Mbps > + * 2b11 Reserved > + */ > +#define YTPHY_SSR_SPEED_MODE_MASK (BIT(15) | BIT(14)) > +#define YTPHY_SSR_SPEED_10M 0x0 > +#define YTPHY_SSR_SPEED_100M 0x1 > +#define YTPHY_SSR_SPEED_1000M 0x2 > +#define YTPHY_SSR_DUPLEX_BIT 13 > +#define YTPHY_SSR_DUPLEX BIT(13) > +#define YTPHY_SSR_PAGE_RECEIVED BIT(12) > +#define YTPHY_SSR_SPEED_DUPLEX_RESOLVED BIT(11) > +#define YTPHY_SSR_LINK BIT(10) > +#define YTPHY_SSR_MDIX_CROSSOVER BIT(6) > +#define YTPHY_SSR_DOWNGRADE BIT(5) > +#define YTPHY_SSR_TRANSMIT_PAUSE BIT(3) > +#define YTPHY_SSR_RECEIVE_PAUSE BIT(2) > +#define YTPHY_SSR_POLARITY BIT(1) > +#define YTPHY_SSR_JABBER BIT(0) > + > +/* Interrupt enable Register */ > +#define YTPHY_INTERRUPT_ENABLE_REG 0x12 > +#define YTPHY_IER_AUTONEG_ERR BIT(15) > +#define YTPHY_IER_SPEED_CHANGED BIT(14) > +#define YTPHY_IER_DUPLEX_CHANGED BIT(13) > +#define YTPHY_IER_PAGE_RECEIVED BIT(12) > +#define YTPHY_IER_LINK_FAILED BIT(11) > +#define YTPHY_IER_LINK_SUCCESSED BIT(10) > +#define YTPHY_IER_WOL BIT(6) > +#define YTPHY_IER_WIRESPEED_DOWNGRADE BIT(5) > +#define YTPHY_IER_SERDES_LINK_FAILED BIT(3) > +#define YTPHY_IER_SERDES_LINK_SUCCESSED BIT(2) > +#define YTPHY_IER_POLARITY_CHANGED BIT(1) > +#define YTPHY_IER_JABBER_HAPPENED BIT(0) > + > +/* Interrupt Status Register */ > +#define YTPHY_INTERRUPT_STATUS_REG 0x13 > +#define YTPHY_ISR_AUTONEG_ERR BIT(15) > +#define YTPHY_ISR_SPEED_CHANGED BIT(14) > +#define YTPHY_ISR_DUPLEX_CHANGED BIT(13) > +#define YTPHY_ISR_PAGE_RECEIVED BIT(12) > +#define YTPHY_ISR_LINK_FAILED BIT(11) > +#define YTPHY_ISR_LINK_SUCCESSED BIT(10) > +#define YTPHY_ISR_WOL BIT(6) > +#define YTPHY_ISR_WIRESPEED_DOWNGRADE BIT(5) > +#define YTPHY_ISR_SERDES_LINK_FAILED BIT(3) > +#define YTPHY_ISR_SERDES_LINK_SUCCESSED BIT(2) > +#define YTPHY_ISR_POLARITY_CHANGED BIT(1) > +#define YTPHY_ISR_JABBER_HAPPENED BIT(0) > + > +/* Speed Auto Downgrade Control Register */ > +#define YTPHY_SPEED_AUTO_DOWNGRADE_CONTROL_REG 0x14 > +#define YTPHY_SADCR_SPEED_DOWNGRADE_EN BIT(5) > + > +/* If these bits are set to 3, the PHY attempts five times ( 3(set value) + > + * additional 2) before downgrading, default 0x3 > + */ > +#define YTPHY_SADCR_SPEED_RETRY_LIMIT (0x3 << 2) > + > +/* Rx Error Counter Register */ > +#define YTPHY_RX_ERROR_COUNTER_REG 0x15
Do the above YTPHY_ apply universally to all YT designated PHYs? If not, please define these to the model that introduced them, such as YT8521_, or if they apply also to the YT8511 continue with common convention and name them YT8511_.
> + > +/* Extended Register's Address Offset Register */ > +#define YTPHY_PAGE_SELECT 0x1E > + > +/* Extended Register's Data Register */ > +#define YTPHY_PAGE_DATA 0x1F
These are defined exactly the same way as below. Please reuse code where possible.
> + > > #define YT8511_PAGE_SELECT 0x1e > #define YT8511_PAGE 0x1f > @@ -38,6 +135,301 @@ > #define YT8511_DELAY_FE_TX_EN (0xf << 12) > #define YT8511_DELAY_FE_TX_DIS (0x2 << 12) > > + > +/* Extended register is different from MMD Register and MII Register. > + * We can use ytphy_read_ext/ytphy_write_ext/ytphy_modify_ext function to > + * operate extended register. > + * Extended Register start > + */ > + > +#define YT8521_EXTREG_SLEEP_CONTROL1_REG 0x27 > +#define YT8521_ESC1R_SLEEP_SW BIT(15) > +#define YT8521_ESC1R_PLLON_SLP BIT(14) > + > +#define YT8521_REG_SPACE_SELECT_REG 0xA000 > +#define YT8521_RSSR_FIBER_SPACE BIT(1) > +#define YT8521_RSSR_UTP_SPACE 0x0 > + > +#define YT8521_CHIP_CONFIG_REG 0xA001 > +#define YT8521_CCR_SW_RST BIT(15) > + > +/* 8 working modes: > + * 3b000 UTP_TO_RGMII *default* > + * 3b001 FIBER_TO_RGMII > + * 3b010 UTP_FIBER_TO_RGMII > + * 3b011 UTP_TO_SGMII > + * 3b100 SGPHY_TO_RGMAC > + * 3b101 SGMAC_TO_RGPHY > + * 3b110 UTP_TO_FIBER_AUTO > + * 3b111 UTP_TO_FIBER_FORCE > + */ > +#define YT8521_CCR_MODE_SEL_MASK (BIT(2) | BIT(1) | BIT(0)) > + > +/* 3 Phy register space pooling modes */ > +#define YT8521_MODE_FIBER 0x1 > +#define YT8521_MODE_UTP 0x2 > +#define YT8521_MODE_POLL 0x3 > + > +#define YT8521_RGMII_CONFIG1_REG 0xA003 > + > +/* 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 YTPHY_WOL_MACADDR2_REG 0xA007 > +#define YTPHY_WOL_MACADDR1_REG 0xA008 > +#define YTPHY_WOL_MACADDR0_REG 0xA009 > + > +#define YTPHY_WOL_CONFIG_REG 0xA00A > +#define YTPHY_WCR_INTR_SEL BIT(6) > +#define YTPHY_WCR_ENABLE BIT(3) > + > +/* 2b00 84ms > + * 2b01 168ms *default* > + * 2b10 336ms > + * 2b11 672ms > + */ > +#define YTPHY_WCR_PUSEL_WIDTH_MASK (BIT(2) | BIT(1)) > +#define YTPHY_WCR_PUSEL_WIDTH_672MS (BIT(2) | BIT(1)) > + > +/* 1b0 Interrupt and WOL events is level triggerd and active LOW *default* > + * 1b1 Interrupt and WOL events is pulse triggered and active LOW > + */ > +#define YTPHY_WCR_TYPE_PULSE BIT(0) > + > +#define YTPHY_SYNC_CLK_CONFIG_REG 0xA012 > + > +/* 1b1 output 125m clock *default* > + * 1b0 output 25m clock > + */ > +#define YTPHY_SCCR_Clk_125M BIT(3) > +/* Extended Register end */ > + > +// clang-format on > + > +/** > + * ytphy_read_ext() - read a PHY's extended register > + * @phydev: a pointer to a &struct phy_device > + * @regnum: register number to read > + * > + * returns the value of regnum reg or negative error code > + */ > +static int ytphy_read_ext(struct phy_device *phydev, u16 regnum) > +{ > + int ret; > + > + ret = phy_write(phydev, YTPHY_PAGE_SELECT, regnum); > + if (ret < 0) > + return ret; > + > + return phy_read(phydev, YTPHY_PAGE_DATA); > +} > + > +/** > + * ytphy_write_ext() - write a PHY's extended register > + * @phydev: a pointer to a &struct phy_device > + * @regnum: register number to write > + * @val: value to write to @regnum > + * > + * returns 0 or negative error code > + */ > +static int ytphy_write_ext(struct phy_device *phydev, u16 regnum, u16 val) > +{ > + int ret; > + > + ret = phy_write(phydev, YTPHY_PAGE_SELECT, regnum); > + if (ret < 0) > + return ret; > + > + return phy_write(phydev, YTPHY_PAGE_DATA, val); > +} > + > +/** > + * ytphy_modify_ext() - bits modify a PHY's extended register > + * @phydev: a pointer to a &struct phy_device > + * @regnum: register number to write > + * @mask: bit mask of bits to clear > + * @set: bit mask of bits to set > + * > + * NOTE: Convenience function which allows a PHY‘s extended register to be > + * modified as new register value = (old register value & ~mask) | set > + * > + * returns 0 or negative error code > + */ > +static int ytphy_modify_ext(struct phy_device *phydev, u16 regnum, u16 mask, > + u16 set) > +{ > + u16 val; > + int ret; > + > + ret = ytphy_read_ext(phydev, regnum); > + if (ret < 0) > + return ret; > + > + val = ret & 0xffff; > + val &= ~mask; > + val |= set; > + > + return ytphy_write_ext(phydev, regnum, val);
This should all be done within a single lock context.
> +} > + > +/** > + * ytphy_read_page() - read reg page > + * @phydev: a pointer to a &struct phy_device > + * > + * returns current reg space of yt8521 (YT8521_RSSR_FIBER_SPACE / > + * YT8521_RSSR_UTP_SPACE) or negative errno code > + */ > +static int ytphy_read_page(struct phy_device *phydev) > +{ > + int ret; > + > + ret = ytphy_read_ext(phydev, YT8521_REG_SPACE_SELECT_REG); > + if (ret < 0) > + return ret; > + > + if (ret & YT8521_RSSR_FIBER_SPACE) > + return YT8521_RSSR_FIBER_SPACE; > + > + return YT8521_RSSR_UTP_SPACE;
So there's only two possible pages that can be used in the extended register space?
> +}; > + > +/** > + * ytphy_write_page() - write reg page > + * @phydev: a pointer to a &struct phy_device > + * @page: The reg page(YT8521_RSSR_FIBER_SPACE/YT8521_RSSR_UTP_SPACE) to write. > + * > + * returns 0 or negative errno code > + */ > +static int ytphy_write_page(struct phy_device *phydev, int page) > +{ > + int mask = YT8521_RSSR_FIBER_SPACE; > + int set = YT8521_RSSR_UTP_SPACE; > + > + if (page == YT8521_RSSR_FIBER_SPACE) > + set = YT8521_RSSR_FIBER_SPACE; > + > + return ytphy_modify_ext(phydev, YT8521_REG_SPACE_SELECT_REG, mask, set); > +}; > + > +/** > + * ytphy_get_wol() - report whether wake-on-lan is enabled > + * @phydev: a pointer to a &struct phy_device > + * @wol: a pointer to a &struct ethtool_wolinfo > + * > + */ > +static void ytphy_get_wol(struct phy_device *phydev, > + struct ethtool_wolinfo *wol) > +{ > + int val = 0; > + > + wol->supported = WAKE_MAGIC; > + wol->wolopts = 0; > + > + val = ytphy_read_ext(phydev, YTPHY_WOL_CONFIG_REG); > + if (val < 0) > + return; > + > + if (val & YTPHY_WCR_ENABLE) > + wol->wolopts |= WAKE_MAGIC; > +} > + > +/** > + * ytphy_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 ytphy_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) > +{ > + int ret; > + u16 mask; > + u16 val; > + u8 i; > + int curr_reg_space; > + const u8 *mac_addr; > + struct net_device *p_attached_dev; > + static const u16 mac_addr_reg[] = { > + YTPHY_WOL_MACADDR2_REG, > + YTPHY_WOL_MACADDR1_REG, > + YTPHY_WOL_MACADDR0_REG, > + }; > + > + curr_reg_space = ytphy_read_page(phydev); > + if (curr_reg_space < 0) > + return curr_reg_space; > + > + 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; > + > + /* switch to UTP reg space */ > + ret = ytphy_write_page(phydev, YT8521_RSSR_UTP_SPACE); > + if (ret < 0) > + goto err_restore_page; > + > + /* Store the device address for the magic packet */ > + for (i = 0; i < 3; i++) { > + ret = ytphy_write_ext(phydev, mac_addr_reg[i], > + ((mac_addr[i * 2] << 8)) | > + (mac_addr[i * 2 + 1])); > + if (ret < 0) > + goto err_restore_page; > + } > + > + /* Enable the WOL feature */ > + val = 0; > + mask = YTPHY_WCR_PUSEL_WIDTH_MASK | YTPHY_WCR_INTR_SEL; > + val |= YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL; > + val |= YTPHY_WCR_TYPE_PULSE | YTPHY_WCR_PUSEL_WIDTH_672MS; > + ret = ytphy_modify_ext(phydev, YTPHY_WOL_CONFIG_REG, mask, val); > + if (ret < 0) > + goto err_restore_page; > + > + /* Enable the WOL feature interrupt */ > + ret = phy_set_bits(phydev, YTPHY_INTERRUPT_ENABLE_REG, > + YTPHY_IER_WOL); > + if (ret < 0) > + goto err_restore_page; > + > + } else { > + /* switch to UTP reg space */ > + ret = ytphy_write_page(phydev, YT8521_RSSR_UTP_SPACE); > + if (ret < 0) > + goto err_restore_page; > + > + /* Disable the WOL feature */ > + val = 0; > + mask = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL; > + ret = ytphy_modify_ext(phydev, YTPHY_WOL_CONFIG_REG, mask, val); > + if (ret < 0) > + goto err_restore_page; > + } > + > +err_restore_page: > + /* Recover to previous register space page */ > + return ytphy_write_page(phydev, curr_reg_space); > +} > + > +// clang-format off > static int yt8511_read_page(struct phy_device *phydev) > { > return __phy_read(phydev, YT8511_PAGE_SELECT); > @@ -111,6 +503,406 @@ err_restore_page: > return phy_restore_page(phydev, oldpage, ret); > } > > +// clang-format on > + > +/** > + * yt8521_get_mode() - get the mode of yt8521(pooling mode) > + * @phydev: a pointer to a &struct phy_device > + * > + * returns current mode of yt8521 (YT8521_MODE_FIBER / YT8521_MODE_UTP / > + * YT8521_MODE_POLL) or negative errno code > + */ > +static int yt8521_get_mode(struct phy_device *phydev) > +{ > + int val = 0; > + > + val = ytphy_read_ext(phydev, YT8521_CHIP_CONFIG_REG); > + if (val < 0) > + return val; > + > + switch (val & YT8521_CCR_MODE_SEL_MASK) { > + case 1: > + case 4: > + case 5: > + return YT8521_MODE_FIBER; > + case 2: > + case 6: > + case 7: > + return YT8521_MODE_POLL; > + case 3: > + case 0: > + return YT8521_MODE_UTP; > + default: /* do not support other modes */ > + return -EOPNOTSUPP; > + } > +} > + > +/** > + * yt8521_adjust_status() - adjust speed and duplex according to is_utp, then > + * update speed and duplex to phydev > + * > + * @phydev: a pointer to a &struct phy_device > + * @status: yt8521 status read from YTPHY_SPECIFIC_STATUS_REG > + * @is_utp: false(yt8521 work in fiber mode) or true(yt8521 work in utp mode) > + * > + * NOTE: there is no 10M speed mode in fiber mode, so need adjust. > + * > + * returns 0 > + */ > +static int yt8521_adjust_status(struct phy_device *phydev, int status, > + bool is_utp) > +{ > + int speed_mode, duplex; > + int speed = SPEED_UNKNOWN; > + > + if (is_utp) > + duplex = (status & YTPHY_SSR_DUPLEX) >> YTPHY_SSR_DUPLEX_BIT; > + else > + duplex = 1; > + > + speed_mode = (status & YTPHY_SSR_SPEED_MODE_MASK) >> > + YTPHY_SSR_SPEED_MODE_BIT; > + switch (speed_mode) { > + case YTPHY_SSR_SPEED_10M: > + if (is_utp) > + speed = SPEED_10; > + break; > + case YTPHY_SSR_SPEED_100M: > + speed = SPEED_100; > + break; > + case YTPHY_SSR_SPEED_1000M: > + speed = SPEED_1000; > + break; > + default: > + speed = SPEED_UNKNOWN; > + break; > + } > + > + phydev->speed = speed; > + phydev->duplex = duplex; > + phydev->port = is_utp ? PORT_TP : PORT_FIBRE; > + > + return 0; > +} > +/** > + * yt8521_read_status() - determines the negotiated speed and duplex > + * @phydev: a pointer to a &struct phy_device > + * > + * returns 0 or negative errno code > + */ > +static int yt8521_read_status(struct phy_device *phydev) > +{ > + int ret; > + int val; > + int yt8521_fiber_latch_val; > + int yt8521_fiber_curr_val; > + int link; > + int link_utp = 0, link_fiber = 0; > + int yt8521_mode; > + > + yt8521_mode = yt8521_get_mode(phydev); > + if (yt8521_mode < 0) > + return yt8521_mode; > + > + /* YT8521 has two reg space (utp/fiber) and three work mode (utp/fiber/poll), > + * each reg space has YTPHY_SPECIFIC_STATUS_REG. The mode must be set > + * before we can read the state (YTPHY_SPECIFIC_STATUS_REG). Pool mode > + * combines utp and faber mode ,so we need do both. > + */ > + > + if ((yt8521_mode == YT8521_MODE_UTP) || > + (yt8521_mode == YT8521_MODE_POLL)) { > + ret = ytphy_write_page(phydev, YT8521_RSSR_UTP_SPACE); > + if (ret < 0) > + return ret; > + > + ret = genphy_read_status(phydev); > + if (ret) > + return ret; > + > + val = phy_read(phydev, YTPHY_SPECIFIC_STATUS_REG); > + if (val < 0) > + return val; > + > + link = val & YTPHY_SSR_LINK; > + if (link) { > + link_utp = 1; > + ret = yt8521_adjust_status(phydev, val, true); > + if (ret < 0) > + return ret; > + } else { > + link_utp = 0; > + } > + } > + > + if ((yt8521_mode == YT8521_MODE_FIBER) || > + (yt8521_mode == YT8521_MODE_POLL)) { > + ret = ytphy_write_page(phydev, YT8521_RSSR_FIBER_SPACE); > + if (ret < 0) > + return ret; > + > + ret = genphy_read_status(phydev); > + if (ret) > + return ret; > + > + val = phy_read(phydev, YTPHY_SPECIFIC_STATUS_REG); > + if (val < 0) > + return val; > + > + /* When PHY is in fiber mode, speed transferred > + * from 1000Mbps to 100Mbps, there is not link down > + * from 0x11, so we need check reg 1 to identify such case. > + */ > + yt8521_fiber_latch_val = phy_read(phydev, MII_BMSR); > + if (yt8521_fiber_latch_val < 0) > + return yt8521_fiber_latch_val; > + > + yt8521_fiber_curr_val = phy_read(phydev, MII_BMSR); > + if (yt8521_fiber_curr_val < 0) > + return yt8521_fiber_curr_val; > + > + link = val & YTPHY_SSR_LINK; > + if (link && yt8521_fiber_latch_val != yt8521_fiber_curr_val) { > + link = 0; > + > + netdev_info( > + phydev->attached_dev, > + "%s, phy addr: %d, fiber link down detect, latch = %04x, curr = %04x\n", > + __func__, phydev->mdio.addr, > + yt8521_fiber_latch_val, yt8521_fiber_curr_val); > + } > + if (link) { > + link_fiber = 1; > + ret = yt8521_adjust_status(phydev, val, false); > + if (ret < 0) > + return ret; > + } else { > + link_fiber = 0; > + } > + } > + > + if (link_utp || link_fiber) { > + if (phydev->link == 0) > + phydev->link = 1; > + } else { > + phydev->link = 0; > + } > + > + /* set utp reg space as default reg space */ > + return ytphy_write_page(phydev, YT8521_RSSR_UTP_SPACE); > +} > + > +/** > + * yt8521_modify_BMCR - bits modify a PHY's BMCR register according to the > + * current mode > + * @phydev: the phy_device struct > + * @mask: bit mask of bits to clear > + * @set: bit mask of bits to set > + * > + * NOTE: Convenience function which allows a PHY‘s BMCR register to be > + * modified as new register value = (old register value & ~mask) | set. > + * YT8521 has two space (utp/fiber) and three mode (utp/fiber/poll), each space > + * has MII_BMCR. pool mode combines utp and faber,so need do both. > + * > + * returns 0 or negative errno code > + */ > +static int yt8521_modify_BMCR(struct phy_device *phydev, u16 mask, u16 set) > +{ > + int ret; > + int yt8521_mode; > + > + yt8521_mode = yt8521_get_mode(phydev); > + if (yt8521_mode < 0) > + return yt8521_mode; > + > + if ((yt8521_mode == YT8521_MODE_UTP) || > + (yt8521_mode == YT8521_MODE_POLL)) { > + ret = ytphy_write_page(phydev, YT8521_RSSR_UTP_SPACE); > + if (ret < 0) > + return ret; > + > + ret = phy_modify(phydev, MII_BMCR, mask, set); > + if (ret < 0) > + return ret; > + } > + > + if ((yt8521_mode == YT8521_MODE_FIBER) || > + (yt8521_mode == YT8521_MODE_POLL)) { > + ret = ytphy_write_page(phydev, YT8521_RSSR_FIBER_SPACE); > + if (ret < 0) > + return ret; > + > + ret = phy_modify(phydev, MII_BMCR, mask, set); > + if (ret < 0) > + return ret; > + > + /* set utp reg apace as default reg apace*/ > + ret = ytphy_write_page(phydev, YT8521_RSSR_UTP_SPACE); > + if (ret < 0) > + return ret; > + } > + return 0; > +} > + > +/** > + * yt8521_soft_reset() - called to issue a PHY software reset > + * @phydev: a pointer to a &struct phy_device > + * > + * returns 0 or negative errno code > + */ > +int yt8521_soft_reset(struct phy_device *phydev) > +{ > + return yt8521_modify_BMCR(phydev, 0, BMCR_RESET); > +} > + > +/** > + * yt8521_suspend() - suspend the hardware > + * @phydev: a pointer to a &struct phy_device > + * > + * returns 0 or negative errno code > + */ > +int yt8521_suspend(struct phy_device *phydev) > +{ > + int val; > + int wol_enabled; > + > + val = ytphy_read_ext(phydev, YTPHY_WOL_CONFIG_REG); > + if (val < 0) > + return val; > + > + wol_enabled = (val & YTPHY_WCR_ENABLE); > + if (wol_enabled) > + val = BMCR_ISOLATE; > + else > + val = BMCR_PDOWN; > + > + return yt8521_modify_BMCR(phydev, 0, val); > +} > + > +/** > + * yt8521_resume() - resume the hardware > + * @phydev: a pointer to a &struct phy_device > + * > + * returns 0 or negative errno code > + */ > +int yt8521_resume(struct phy_device *phydev) > +{ > + int ret; > + /* disable auto sleep */ > + ret = ytphy_modify_ext(phydev, YT8521_EXTREG_SLEEP_CONTROL1_REG, > + YT8521_ESC1R_SLEEP_SW, 0); > + if (ret < 0) > + return ret; > + > + return yt8521_modify_BMCR(phydev, BMCR_ISOLATE | BMCR_PDOWN, 0); > +} > + > +/** > + * yt8521_config_init() - called to initialize the PHY > + * @phydev: a pointer to a &struct phy_device > + * > + * returns 0 or negative errno code > + */ > +static int yt8521_config_init(struct phy_device *phydev) > +{ > + int ret; > + u16 val = 0; > + int hw_strap_mode; > + > + /* Explicitly switch to UTP reg space, just to be sure */ > + ret = ytphy_write_page(phydev, YT8521_RSSR_UTP_SPACE); > + if (ret < 0) > + return ret; > + > + ret = phy_read(phydev, MII_PHYSID2); > + if (ret < 0) > + return ret; > + > + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || > + phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) > + val |= (YT8521_RC1R_RX_DELAY_EN); > + > + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || > + phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) > + val |= (YT8521_RC1R_GE_TX_DELAY_EN | > + YT8521_RC1R_FE_TX_DELAY_EN); > + > + 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) > + return ret; > + > + /* disable auto sleep */ > + 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 */ > + ret = ytphy_modify_ext(phydev, 0xc, BIT(12), 0); > + if (ret < 0) > + return ret; > + > + hw_strap_mode = ytphy_read_ext(phydev, YT8521_CHIP_CONFIG_REG); > + if (hw_strap_mode < 0) > + return hw_strap_mode; > + > + netdev_info( > + phydev->attached_dev, > + "%s done, phy addr: %d, strap mode = %ld, polling mode = %d\n", > + __func__, phydev->mdio.addr, > + hw_strap_mode & YT8521_CCR_MODE_SEL_MASK, > + yt8521_get_mode(phydev)); > + > + return ret; > +} > + > +/** > + * yt8521_aneg_done() - determines the auto negotiation result > + * @phydev: a pointer to a &struct phy_device > + * > + * NOTE: for fiber mode, when speed is 100M, there is no definition for > + * autonegotiation, and this function handles this case and return > + * 1 per linux kernel's polling. > + * > + * returns 0(no link)or 1( fiber or utp link) or negative errno code > + */ > +int yt8521_aneg_done(struct phy_device *phydev) > +{ > + int link_fiber = 0, link_utp = 0; > + int ret; > + > + /* Explicitly switch to FIBER reg space*/ > + ret = ytphy_write_page(phydev, YT8521_RSSR_FIBER_SPACE); > + if (ret < 0) > + return ret; > + > + ret = ytphy_write_page(phydev, YTPHY_SPECIFIC_STATUS_REG); > + if (ret < 0) > + return ret; > + > + link_fiber = !!(ret & YTPHY_SSR_LINK); > + > + /* Explicitly switch to UTP reg space */ > + ret = ytphy_write_page(phydev, YT8521_RSSR_UTP_SPACE); > + if (ret < 0) > + return ret; > + > + if (!link_fiber) { > + ret = ytphy_write_page(phydev, YTPHY_SPECIFIC_STATUS_REG); > + if (ret < 0) > + return ret; > + > + link_fiber = !!(ret & YTPHY_SSR_LINK); > + } > + > + return !!(link_fiber | link_utp); > +} > + > +// clang-format off > static struct phy_driver motorcomm_phy_drvs[] = { > { > PHY_ID_MATCH_EXACT(PHY_ID_YT8511), > @@ -121,16 +913,35 @@ static struct phy_driver motorcomm_phy_drvs[] = { > .read_page = yt8511_read_page, > .write_page = yt8511_write_page, > }, > + { > + .phy_id = PHY_ID_YT8521, > + .name = YT8521_DRIVER_ENAME, > + .phy_id_mask = YT_PHY_ID_MASK, > + .read_page = ytphy_read_page, > + .write_page = ytphy_write_page, > + .get_wol = ytphy_get_wol, > + .set_wol = ytphy_set_wol, > + .config_aneg = genphy_config_aneg, > + .soft_reset = yt8521_soft_reset, > + .aneg_done = yt8521_aneg_done, > + .config_init = yt8521_config_init, > + .read_status = yt8521_read_status, > + .suspend = yt8521_suspend, > + .resume = yt8521_resume, > + }, > }; > +// clang-format on > > module_phy_driver(motorcomm_phy_drvs); > > -MODULE_DESCRIPTION("Motorcomm PHY driver"); > -MODULE_AUTHOR("Peter Geis"); > +MODULE_DESCRIPTION("Motorcomm 8511/8521 PHY driver"); > +MODULE_AUTHOR("Frank"); > MODULE_LICENSE("GPL"); > +MODULE_VERSION(YTPHY_KERNEL_DRIVER_VERSION); > > static const struct mdio_device_id __maybe_unused motorcomm_tbl[] = { > { PHY_ID_MATCH_EXACT(PHY_ID_YT8511) }, > + { PHY_ID_MATCH_EXACT(PHY_ID_YT8521) }, > { /* sentinal */ } > }; > > -- > 2.31.0.windows.1 >
I've been working on extending the YT8511 support, as well as adding support for the YT8531. I look forward to your V2.
Very Respectfully, Peter Geis
| |