[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <56251b2c-5a81-e739-fa78-5ddce46f9371@maxlinear.com>
Date: Tue, 1 Jun 2021 14:46:19 +0000
From: Liang Xu <lxu@...linear.com>
To: Wong Vee Khee <vee.khee.wong@...ux.intel.com>
CC: "andrew@...n.ch" <andrew@...n.ch>,
"hkallweit1@...il.com" <hkallweit1@...il.com>,
"netdev@...r.kernel.org" <netdev@...r.kernel.org>,
"davem@...emloft.net" <davem@...emloft.net>,
"kuba@...nel.org" <kuba@...nel.org>,
"linux@...linux.org.uk" <linux@...linux.org.uk>,
Hauke Mehrtens <hmehrtens@...linear.com>,
Thomas Mohren <tmohren@...linear.com>
Subject: Re: [PATCH] phy: maxlinear: add Maxlinear GPY115/21x/24x driver
On 1/6/2021 7:41 pm, Wong Vee Khee wrote:
> This email was sent from outside of MaxLinear.
>
>
> On Tue, Jun 01, 2021 at 03:44:27PM +0800, Xu Liang wrote:
>> ---
>> drivers/net/phy/Kconfig | 5 +
>> drivers/net/phy/Makefile | 1 +
>> drivers/net/phy/mxl-gpy.c | 537 ++++++++++++++++++++++++++++++++++++++
>> 3 files changed, 543 insertions(+)
>> create mode 100644 drivers/net/phy/mxl-gpy.c
>>
>> diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
>> index 288bf405ebdb..7f1a0d62d83a 100644
>> --- a/drivers/net/phy/Kconfig
>> +++ b/drivers/net/phy/Kconfig
>> @@ -186,6 +186,11 @@ config INTEL_XWAY_PHY
>> PEF 7061, PEF 7071 and PEF 7072 or integrated into the Intel
>> SoCs xRX200, xRX300, xRX330, xRX350 and xRX550.
>>
>> +config MXL_GPHY
>> + tristate "Maxlinear 2.5G PHYs"
>> + help
>> + Support for the Maxlinear 2.5G PHYs.
>> +
> I think its better to explicitly spell out the supported models.
> i.e. GPY115/21x/24x PHYs.
I will fix in v2 patch.
>> config LSI_ET1011C_PHY
>> tristate "LSI ET1011C PHY"
>> help
>> diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
>> index bcda7ed2455d..28aa2a198d00 100644
>> --- a/drivers/net/phy/Makefile
>> +++ b/drivers/net/phy/Makefile
>> @@ -59,6 +59,7 @@ obj-$(CONFIG_DP83TC811_PHY) += dp83tc811.o
>> obj-$(CONFIG_FIXED_PHY) += fixed_phy.o
>> obj-$(CONFIG_ICPLUS_PHY) += icplus.o
>> obj-$(CONFIG_INTEL_XWAY_PHY) += intel-xway.o
>> +obj-$(CONFIG_MXL_GPHY) += mxl-gpy.o
>> obj-$(CONFIG_LSI_ET1011C_PHY) += et1011c.o
>> obj-$(CONFIG_LXT_PHY) += lxt.o
>> obj-$(CONFIG_MARVELL_10G_PHY) += marvell10g.o
>> diff --git a/drivers/net/phy/mxl-gpy.c b/drivers/net/phy/mxl-gpy.c
>> new file mode 100644
>> index 000000000000..757e65e48567
>> --- /dev/null
>> +++ b/drivers/net/phy/mxl-gpy.c
>> @@ -0,0 +1,537 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/* Copyright (C) 2021 Maxlinear Corporation
>> + * Copyright (C) 2020 Intel Corporation
>> + *
>> + * Maxlinear Ethernet GPY
> Drivers for Maxlinear Ethernet GPY
I will fix in v2 patch.
>> + *
>> + */
>> +
>> +#include <linux/version.h>
>> +#include <linux/module.h>
>> +#include <linux/bitfield.h>
>> +#include <linux/phy.h>
>> +#include <linux/netdevice.h>
>> +
>> +/* PHY ID */
>> +#define PHY_ID_GPY 0x67C9DC00
>> +#define PHY_ID_MASK GENMASK(31, 10)
>> +
>> +#define PHY_MIISTAT 0x18 /* MII state */
>> +#define PHY_IMASK 0x19 /* interrupt mask */
>> +#define PHY_ISTAT 0x1A /* interrupt status */
>> +#define PHY_FWV 0x1E /* firmware version */
>> +
>> +#define PHY_MIISTAT_SPD_MASK GENMASK(2, 0)
>> +#define PHY_MIISTAT_DPX BIT(3)
>> +#define PHY_MIISTAT_LS BIT(10)
>> +
>> +#define PHY_MIISTAT_SPD_10 0
>> +#define PHY_MIISTAT_SPD_100 1
>> +#define PHY_MIISTAT_SPD_1000 2
>> +#define PHY_MIISTAT_SPD_2500 4
>> +
>> +#define PHY_IMASK_WOL BIT(15) /* Wake-on-LAN */
>> +#define PHY_IMASK_ANC BIT(10) /* Auto-Neg complete */
>> +#define PHY_IMASK_ADSC BIT(5) /* Link auto-downspeed detect */
>> +#define PHY_IMASK_DXMC BIT(2) /* Duplex mode change */
>> +#define PHY_IMASK_LSPC BIT(1) /* Link speed change */
>> +#define PHY_IMASK_LSTC BIT(0) /* Link state change */
>> +#define PHY_IMASK_MASK (PHY_IMASK_LSTC | \
>> + PHY_IMASK_LSPC | \
>> + PHY_IMASK_DXMC | \
>> + PHY_IMASK_ADSC | \
>> + PHY_IMASK_ANC)
>> +
>> +#define PHY_FWV_TYPE_MASK GENMASK(11, 8)
>> +#define PHY_FWV_MINOR_MASK GENMASK(7, 0)
>> +
>> +/* ANEG dev */
>> +#define ANEG_MGBT_AN_CTRL 0x20
>> +#define ANEG_MGBT_AN_STAT 0x21
>> +#define CTRL_AB_2G5BT_BIT BIT(7)
>> +#define CTRL_AB_FR_2G5BT BIT(5)
>> +#define STAT_AB_2G5BT_BIT BIT(5)
>> +#define STAT_AB_FR_2G5BT BIT(3)
>> +
>> +/* SGMII */
>> +#define VSPEC1_SGMII_CTRL 0x08
>> +#define VSPEC1_SGMII_CTRL_ANEN BIT(12) /* Aneg enable */
>> +#define VSPEC1_SGMII_CTRL_ANRS BIT(9) /* Restart Aneg */
>> +#define VSPEC1_SGMII_ANEN_ANRS (VSPEC1_SGMII_CTRL_ANEN | \
>> + VSPEC1_SGMII_CTRL_ANRS)
>> +
>> +/* WoL */
>> +#define VPSPEC2_WOL_CTL 0x0E06
>> +#define VPSPEC2_WOL_AD01 0x0E08
>> +#define VPSPEC2_WOL_AD23 0x0E09
>> +#define VPSPEC2_WOL_AD45 0x0E0A
>> +#define WOL_EN BIT(0)
>> +
>> +static int gpy_read_abilities(struct phy_device *phydev)
>> +{
>> + int ret;
>> +
>> + ret = genphy_read_abilities(phydev);
>> + if (ret < 0)
>> + return ret;
>> +
>> + /* Detect 2.5G/5G support. */
>> + ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_STAT2);
>> + if (ret < 0)
>> + return ret;
>> + if (!(ret & MDIO_PMA_STAT2_EXTABLE))
>> + return 0;
>> +
>> + ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_EXTABLE);
>> + if (ret < 0)
>> + return ret;
>> + if (!(ret & MDIO_PMA_EXTABLE_NBT))
>> + return 0;
>> +
>> + ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_NG_EXTABLE);
>> + if (ret < 0)
>> + return ret;
>> +
>> + linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
>> + phydev->supported,
>> + ret & MDIO_PMA_NG_EXTABLE_2_5GBT);
>> +
>> + linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
>> + phydev->supported,
>> + ret & MDIO_PMA_NG_EXTABLE_5GBT);
>> +
>> + return 0;
>> +}
>> +
>> +static int gpy_config_init(struct phy_device *phydev)
>> +{
>> + int ret, fw_ver;
>> +
>> + /* Show GPY PHY FW version in dmesg */
>> + fw_ver = phy_read(phydev, PHY_FWV);
>> + if (fw_ver < 0)
>> + return fw_ver;
>> +
>> + phydev_info(phydev, "Firmware Version: 0x%04X (%s)\n", fw_ver,
>> + (fw_ver & BIT(15)) ? "release" : "test");
>> +
>> + /* Mask all interrupts */
>> + ret = phy_write(phydev, PHY_IMASK, 0);
>> + if (ret)
>> + return ret;
>> +
>> + /* Clear all pending interrupts */
>> + return phy_read(phydev, PHY_ISTAT);
>> +}
>> +
>> +static bool gpy_sgmii_need_reaneg(struct phy_device *phydev)
>> +{
>> + struct {
>> + int type;
>> + int minor;
>> + } table[] = {
>> + {7, 0x6D},
>> + {8, 0x6D},
>> + {9, 0x73},
>> + };
>> +
>> + int fw_ver, fw_type, fw_minor;
>> + size_t i;
>> +
>> + fw_ver = phy_read(phydev, PHY_FWV);
>> + if (fw_ver < 0)
>> + return true;
>> +
>> + fw_type = FIELD_GET(PHY_FWV_TYPE_MASK, fw_ver);
>> + fw_minor = FIELD_GET(PHY_FWV_MINOR_MASK, fw_ver);
>> +
>> + for (i = 0; i < ARRAY_SIZE(table); i++) {
>> + if (fw_type != table[i].type)
>> + continue;
>> + if (fw_minor < table[i].minor)
>> + return true;
>> + break;
>> + }
>> +
>> + return false;
>> +}
>> +
>> +static bool gpy_sgmii_2p5g_chk(struct phy_device *phydev)
>> +{
>> + int ret;
>> +
>> + ret = phy_read(phydev, PHY_MIISTAT);
>> + if (ret < 0) {
>> + phydev_err(phydev, "Error: MDIO register access failed: %d\n",
>> + ret);
>> + return false;
>> + }
>> +
>> + if (!(ret & PHY_MIISTAT_LS)
>> + || FIELD_GET(PHY_MIISTAT_SPD_MASK, ret) != PHY_MIISTAT_SPD_2500)
>> + return false;
>> +
>> + phydev->speed = SPEED_2500;
>> + phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
>> + phy_modify_mmd_changed(phydev, MDIO_MMD_VEND1,
>> + VSPEC1_SGMII_CTRL,
>> + VSPEC1_SGMII_CTRL_ANEN, 0);
>> + return true;
>> +}
>> +
>> +static bool gpy_sgmii_aneg_en(struct phy_device *phydev)
>> +{
>> + int ret;
>> +
>> + ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL);
>> + if (ret < 0) {
>> + phydev_err(phydev, "Error: MMD register access failed: %d\n",
>> + ret);
>> + return true;
> Don't we need some error handling here, instead of return true?
By right, MDIO access failure should not happen.
Even it happens, it does not break function here.
With return value "true", gpy_config_aneg will continue on next step.
>> + }
>> +
>> + return (ret & VSPEC1_SGMII_CTRL_ANEN) ? true : false;
>> +}
>> +
>> +static int gpy_config_aneg(struct phy_device *phydev)
>> +{
>> + bool changed = false;
>> + u32 adv;
>> + int ret;
>> +
>> + if (phydev->autoneg == AUTONEG_DISABLE) {
>> + return phydev->duplex != DUPLEX_FULL
>> + ? genphy_setup_forced(phydev)
>> + : genphy_c45_pma_setup_forced(phydev);
>> + }
>> +
>> + ret = genphy_c45_an_config_aneg(phydev);
>> + if (ret < 0)
>> + return ret;
>> + if (ret)
>> + changed = true;
>> +
>> + adv = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising);
>> + ret = phy_modify_changed(phydev, MII_CTRL1000,
>> + ADVERTISE_1000FULL | ADVERTISE_1000HALF,
>> + adv);
>> + if (ret < 0)
>> + return ret;
>> + if (ret > 0)
>> + changed = true;
>> +
>> + ret = genphy_c45_check_and_restart_aneg(phydev, changed);
>> + if (ret < 0)
>> + return ret;
>> +
>> + if (phydev->interface == PHY_INTERFACE_MODE_USXGMII
>> + || phydev->interface == PHY_INTERFACE_MODE_INTERNAL)
>> + return 0;
>> +
>> + /* No need to trigger re-ANEG if SGMII link speed is 2.5G
>> + * or SGMII ANEG is disabled.
>> + */
>> + if (!gpy_sgmii_need_reaneg(phydev) || gpy_sgmii_2p5g_chk(phydev)
>> + || !gpy_sgmii_aneg_en(phydev))
>> + return 0;
>> +
>> + /* There is a design constraint in GPY2xx device where SGMII AN is
>> + * only triggered when there is change of speed. If, PHY link
>> + * partner`s speed is still same even after PHY TPI is down and up
>> + * again, SGMII AN is not triggered and hence no new in-band message
>> + * from GPY to MAC side SGMII.
>> + * This could cause an issue during power up, when PHY is up prior to
>> + * MAC. At this condition, once MAC side SGMII is up, MAC side SGMII
>> + * wouldn`t receive new in-band message from GPY with correct link
>> + * status, speed and duplex info.
>> + *
>> + * 1) If PHY is already up and TPI link status is still down (such as
>> + * hard reboot), TPI link status is polled for 4 seconds before
>> + * retriggerring SGMII AN.
>> + * 2) If PHY is already up and TPI link status is also up (such as soft
>> + * reboot), polling of TPI link status is not needed and SGMII AN is
>> + * immediately retriggered.
>> + * 3) Other conditions such as PHY is down, speed change etc, skip
>> + * retriggering SGMII AN. Note: in case of speed change, GPY FW will
>> + * initiate SGMII AN.
>> + */
>> +
>> + if (phydev->state != PHY_UP)
>> + return 0;
>> +
>> + ret = phy_read_poll_timeout(phydev, MII_BMSR, ret, ret & BMSR_LSTATUS,
>> + 20000, 4000000, false);
>> + if (ret == -ETIMEDOUT)
>> + return 0;
>> + else if (ret < 0)
>> + return ret;
>> +
>> + /* Trigger SGMII AN. */
>> + return phy_modify_mmd_changed(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
>> + VSPEC1_SGMII_CTRL_ANRS,
>> + VSPEC1_SGMII_CTRL_ANRS);
>> +}
>> +
>> +static void gpy_update_interface(struct phy_device *phydev)
>> +{
>> + int ret;
>> +
>> + /* Interface mode is fixed for USXGMII and integrated PHY */
>> + if (phydev->interface == PHY_INTERFACE_MODE_USXGMII
>> + || phydev->interface == PHY_INTERFACE_MODE_INTERNAL)
>> + return;
>> +
>> + /* Automatically switch SERDES interface between SGMII and 2500-BaseX
>> + * according to speed. Disable ANEG in 2500-BaseX mode.
>> + */
>> + switch (phydev->speed) {
>> + case SPEED_2500:
>> + phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
>> + ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND1,
>> + VSPEC1_SGMII_CTRL,
>> + VSPEC1_SGMII_CTRL_ANEN, 0);
>> + if (ret < 0)
>> + phydev_err(phydev,
>> + "Error: Disable of SGMII ANEG failed: %d\n",
>> + ret);
>> + break;
>> + case SPEED_1000:
>> + case SPEED_100:
>> + case SPEED_10:
>> + phydev->interface = PHY_INTERFACE_MODE_SGMII;
>> + if (gpy_sgmii_aneg_en(phydev))
>> + break;
>> + /* Enable and restart SGMII ANEG for 10/100/1000Mbps link speed
>> + * if ANEG is disabled (in 2500-BaseX mode).
>> + */
>> + ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND1,
>> + VSPEC1_SGMII_CTRL,
>> + VSPEC1_SGMII_ANEN_ANRS,
>> + VSPEC1_SGMII_ANEN_ANRS);
>> + if (ret < 0)
>> + phydev_err(phydev,
>> + "Error: Enable of SGMII ANEG failed: %d\n",
>> + ret);
>> + break;
>> + }
>> +}
>> +
>> +static int gpy_read_status(struct phy_device *phydev)
>> +{
>> + int ret;
>> +
>> + ret = genphy_update_link(phydev);
>> + if (ret)
>> + return ret;
>> +
>> + phydev->speed = SPEED_UNKNOWN;
>> + phydev->duplex = DUPLEX_UNKNOWN;
>> + phydev->pause = 0;
>> + phydev->asym_pause = 0;
>> +
>> + if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) {
>> + ret = genphy_c45_read_lpa(phydev);
>> + if (ret < 0)
>> + return ret;
>> +
>> + /* Read the link partner's 1G advertisement */
>> + ret = phy_read(phydev, MII_STAT1000);
>> + if (ret < 0)
>> + return ret;
>> + mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, ret);
>> + } else if (phydev->autoneg == AUTONEG_DISABLE) {
>> + linkmode_zero(phydev->lp_advertising);
>> + }
>> +
>> + ret = phy_read(phydev, PHY_MIISTAT);
>> + if (ret < 0)
>> + return ret;
>> +
>> + phydev->link = (ret & PHY_MIISTAT_LS) ? 1 : 0;
>> + phydev->duplex = (ret & PHY_MIISTAT_DPX) ? DUPLEX_FULL : DUPLEX_HALF;
>> + switch (FIELD_GET(PHY_MIISTAT_SPD_MASK, ret)) {
>> + case PHY_MIISTAT_SPD_10:
>> + phydev->speed = SPEED_10;
>> + break;
>> + case PHY_MIISTAT_SPD_100:
>> + phydev->speed = SPEED_100;
>> + break;
>> + case PHY_MIISTAT_SPD_1000:
>> + phydev->speed = SPEED_1000;
>> + break;
>> + case PHY_MIISTAT_SPD_2500:
>> + phydev->speed = SPEED_2500;
>> + break;
>> + }
>> +
>> + if (phydev->link)
>> + gpy_update_interface(phydev);
>> +
>> + return 0;
>> +}
>> +
>> +static int gpy_config_intr(struct phy_device *phydev)
>> +{
>> + u16 mask = 0;
>> +
>> + if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
>> + mask = PHY_IMASK_MASK;
>> +
>> + return phy_write(phydev, PHY_IMASK, mask);
>> +}
>> +
>> +static irqreturn_t gpy_handle_interrupt(struct phy_device *phydev)
>> +{
>> + int reg;
>> +
>> + reg = phy_read(phydev, PHY_ISTAT);
>> + if (reg < 0) {
>> + phy_error(phydev);
>> + return IRQ_NONE;
>> + }
>> +
>> + if (!(reg & PHY_IMASK_MASK))
>> + return IRQ_NONE;
>> +
>> + phy_trigger_machine(phydev);
>> +
>> + return IRQ_HANDLED;
>> +}
>> +
>> +static int gpy_set_wol(struct phy_device *phydev,
>> + struct ethtool_wolinfo *wol)
>> +{
>> + struct net_device *attach_dev = phydev->attached_dev;
>> + int ret;
>> +
>> + if (wol->wolopts & WAKE_MAGIC) {
>> + /* MAC address - Byte0:Byte1:Byte2:Byte3:Byte4:Byte5
>> + * VPSPEC2_WOL_AD45 = Byte0:Byte1
>> + * VPSPEC2_WOL_AD23 = Byte2:Byte3
>> + * VPSPEC2_WOL_AD01 = Byte4:Byte5
>> + */
>> + ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
>> + VPSPEC2_WOL_AD45,
>> + ((attach_dev->dev_addr[0] << 8) |
>> + attach_dev->dev_addr[1]));
>> + if (ret < 0)
>> + return ret;
>> +
>> + ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
>> + VPSPEC2_WOL_AD23,
>> + ((attach_dev->dev_addr[2] << 8) |
>> + attach_dev->dev_addr[3]));
>> + if (ret < 0)
>> + return ret;
>> +
>> + ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
>> + VPSPEC2_WOL_AD01,
>> + ((attach_dev->dev_addr[4] << 8) |
>> + attach_dev->dev_addr[5]));
>> + if (ret < 0)
>> + return ret;
>> +
>> + /* Enable the WOL interrupt */
>> + ret = phy_write(phydev, PHY_IMASK, PHY_IMASK_WOL);
>> + if (ret < 0)
>> + return ret;
>> +
>> + /* Enable magic packet matching */
>> + ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
>> + VPSPEC2_WOL_CTL,
>> + WOL_EN);
>> + if (ret < 0)
>> + return ret;
>> +
>> + /* Clear the interrupt status register */
>> + ret = phy_read(phydev, PHY_ISTAT);
>> + if (ret < 0)
>> + return ret;
>> +
>> + } else {
>> + /* Disable magic packet matching */
>> + ret = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2,
>> + VPSPEC2_WOL_CTL,
>> + WOL_EN);
>> + if (ret < 0)
>> + return ret;
>> + }
>> +
>> + if (wol->wolopts & WAKE_PHY) {
>> + /* Enable the link state change interrupt */
>> + ret = phy_set_bits(phydev, PHY_IMASK, PHY_IMASK_LSTC);
>> + if (ret < 0)
>> + return ret;
>> +
>> + /* Clear the interrupt status register */
>> + ret = phy_read(phydev, PHY_ISTAT);
>> + } else {
>> + /* Disable the link state change interrupt */
>> + ret = phy_clear_bits(phydev, PHY_IMASK, PHY_IMASK_LSTC);
>> + }
>> +
>> + return ret;
>> +}
>> +
>> +static void gpy_get_wol(struct phy_device *phydev,
>> + struct ethtool_wolinfo *wol)
>> +{
>> + int ret;
>> +
>> + wol->supported = WAKE_MAGIC | WAKE_PHY;
>> + wol->wolopts = 0;
>> +
>> + ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, VPSPEC2_WOL_CTL);
>> + if (ret & WOL_EN)
>> + wol->wolopts |= WAKE_MAGIC;
>> +
>> + ret = phy_read(phydev, PHY_IMASK);
>> + if (ret & PHY_IMASK_LSTC)
>> + wol->wolopts |= WAKE_PHY;
>> +}
>> +
>> +static int gpy_loopback(struct phy_device *phydev, bool enable)
>> +{
>> + int ret;
>> +
>> + ret = genphy_loopback(phydev, enable);
> genphy_c45_loopback()
The verification is based on genphy_loopback.
I think this should be OK as CL22 is used in the actual system.
>
>> + if (!ret) {
>> + /* It takes some time for PHY deviceto switch
> device to
Will fix in v2 patch.
>> + * into/out-of loopback mode.
>> + */
>> + usleep_range(100, 200);
>> + }
>> +
>> + return ret;
>> +}
>> +
>> +static struct phy_driver gpy_drivers[] = {
>> + {
>> + .phy_id = PHY_ID_GPY,
>> + .phy_id_mask = PHY_ID_MASK,
>> + .name = "Maxlinear Ethernet GPY",
>> + .get_features = gpy_read_abilities,
>> + .config_init = gpy_config_init,
>> + .suspend = genphy_suspend,
>> + .resume = genphy_resume,
> There is the new genphy_c45_pma_{suspend|resume} that you can use here.
I think this should be OK as CL22 is used in the actual system.
>> + .config_aneg = gpy_config_aneg,
>> + .aneg_done = genphy_c45_aneg_done,
>> + .read_status = gpy_read_status,
>> + .config_intr = gpy_config_intr,
>> + .handle_interrupt = gpy_handle_interrupt,
>> + .set_wol = gpy_set_wol,
>> + .get_wol = gpy_get_wol,
>> + .set_loopback = gpy_loopback,
>> + },
>> +};
>> +module_phy_driver(gpy_drivers);
>> +
>> +static struct mdio_device_id __maybe_unused gpy_tbl[] = {
>> + {PHY_ID_GPY, PHY_ID_MASK},
>> + { }
>> +};
>> +MODULE_DEVICE_TABLE(mdio, gpy_tbl);
>> +
>> +MODULE_DESCRIPTION("Maxlinear Ethernet GPY Driver");
>> +MODULE_AUTHOR("Maxlinear Corporation");
>> +MODULE_LICENSE("GPL");
Powered by blists - more mailing lists