cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

mxl-gpy.c (19869B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/* Copyright (C) 2021 Maxlinear Corporation
      3 * Copyright (C) 2020 Intel Corporation
      4 *
      5 * Drivers for Maxlinear Ethernet GPY
      6 *
      7 */
      8
      9#include <linux/module.h>
     10#include <linux/bitfield.h>
     11#include <linux/phy.h>
     12#include <linux/netdevice.h>
     13
     14/* PHY ID */
     15#define PHY_ID_GPYx15B_MASK	0xFFFFFFFC
     16#define PHY_ID_GPY21xB_MASK	0xFFFFFFF9
     17#define PHY_ID_GPY2xx		0x67C9DC00
     18#define PHY_ID_GPY115B		0x67C9DF00
     19#define PHY_ID_GPY115C		0x67C9DF10
     20#define PHY_ID_GPY211B		0x67C9DE08
     21#define PHY_ID_GPY211C		0x67C9DE10
     22#define PHY_ID_GPY212B		0x67C9DE09
     23#define PHY_ID_GPY212C		0x67C9DE20
     24#define PHY_ID_GPY215B		0x67C9DF04
     25#define PHY_ID_GPY215C		0x67C9DF20
     26#define PHY_ID_GPY241B		0x67C9DE40
     27#define PHY_ID_GPY241BM		0x67C9DE80
     28#define PHY_ID_GPY245B		0x67C9DEC0
     29
     30#define PHY_MIISTAT		0x18	/* MII state */
     31#define PHY_IMASK		0x19	/* interrupt mask */
     32#define PHY_ISTAT		0x1A	/* interrupt status */
     33#define PHY_FWV			0x1E	/* firmware version */
     34
     35#define PHY_MIISTAT_SPD_MASK	GENMASK(2, 0)
     36#define PHY_MIISTAT_DPX		BIT(3)
     37#define PHY_MIISTAT_LS		BIT(10)
     38
     39#define PHY_MIISTAT_SPD_10	0
     40#define PHY_MIISTAT_SPD_100	1
     41#define PHY_MIISTAT_SPD_1000	2
     42#define PHY_MIISTAT_SPD_2500	4
     43
     44#define PHY_IMASK_WOL		BIT(15)	/* Wake-on-LAN */
     45#define PHY_IMASK_ANC		BIT(10)	/* Auto-Neg complete */
     46#define PHY_IMASK_ADSC		BIT(5)	/* Link auto-downspeed detect */
     47#define PHY_IMASK_DXMC		BIT(2)	/* Duplex mode change */
     48#define PHY_IMASK_LSPC		BIT(1)	/* Link speed change */
     49#define PHY_IMASK_LSTC		BIT(0)	/* Link state change */
     50#define PHY_IMASK_MASK		(PHY_IMASK_LSTC | \
     51				 PHY_IMASK_LSPC | \
     52				 PHY_IMASK_DXMC | \
     53				 PHY_IMASK_ADSC | \
     54				 PHY_IMASK_ANC)
     55
     56#define PHY_FWV_REL_MASK	BIT(15)
     57#define PHY_FWV_TYPE_MASK	GENMASK(11, 8)
     58#define PHY_FWV_MINOR_MASK	GENMASK(7, 0)
     59
     60/* SGMII */
     61#define VSPEC1_SGMII_CTRL	0x08
     62#define VSPEC1_SGMII_CTRL_ANEN	BIT(12)		/* Aneg enable */
     63#define VSPEC1_SGMII_CTRL_ANRS	BIT(9)		/* Restart Aneg */
     64#define VSPEC1_SGMII_ANEN_ANRS	(VSPEC1_SGMII_CTRL_ANEN | \
     65				 VSPEC1_SGMII_CTRL_ANRS)
     66
     67/* WoL */
     68#define VPSPEC2_WOL_CTL		0x0E06
     69#define VPSPEC2_WOL_AD01	0x0E08
     70#define VPSPEC2_WOL_AD23	0x0E09
     71#define VPSPEC2_WOL_AD45	0x0E0A
     72#define WOL_EN			BIT(0)
     73
     74static const struct {
     75	int type;
     76	int minor;
     77} ver_need_sgmii_reaneg[] = {
     78	{7, 0x6D},
     79	{8, 0x6D},
     80	{9, 0x73},
     81};
     82
     83static int gpy_config_init(struct phy_device *phydev)
     84{
     85	int ret;
     86
     87	/* Mask all interrupts */
     88	ret = phy_write(phydev, PHY_IMASK, 0);
     89	if (ret)
     90		return ret;
     91
     92	/* Clear all pending interrupts */
     93	ret = phy_read(phydev, PHY_ISTAT);
     94	return ret < 0 ? ret : 0;
     95}
     96
     97static int gpy_probe(struct phy_device *phydev)
     98{
     99	int ret;
    100
    101	if (!phydev->is_c45) {
    102		ret = phy_get_c45_ids(phydev);
    103		if (ret < 0)
    104			return ret;
    105	}
    106
    107	/* Show GPY PHY FW version in dmesg */
    108	ret = phy_read(phydev, PHY_FWV);
    109	if (ret < 0)
    110		return ret;
    111
    112	phydev_info(phydev, "Firmware Version: 0x%04X (%s)\n", ret,
    113		    (ret & PHY_FWV_REL_MASK) ? "release" : "test");
    114
    115	return 0;
    116}
    117
    118static bool gpy_sgmii_need_reaneg(struct phy_device *phydev)
    119{
    120	int fw_ver, fw_type, fw_minor;
    121	size_t i;
    122
    123	fw_ver = phy_read(phydev, PHY_FWV);
    124	if (fw_ver < 0)
    125		return true;
    126
    127	fw_type = FIELD_GET(PHY_FWV_TYPE_MASK, fw_ver);
    128	fw_minor = FIELD_GET(PHY_FWV_MINOR_MASK, fw_ver);
    129
    130	for (i = 0; i < ARRAY_SIZE(ver_need_sgmii_reaneg); i++) {
    131		if (fw_type != ver_need_sgmii_reaneg[i].type)
    132			continue;
    133		if (fw_minor < ver_need_sgmii_reaneg[i].minor)
    134			return true;
    135		break;
    136	}
    137
    138	return false;
    139}
    140
    141static bool gpy_2500basex_chk(struct phy_device *phydev)
    142{
    143	int ret;
    144
    145	ret = phy_read(phydev, PHY_MIISTAT);
    146	if (ret < 0) {
    147		phydev_err(phydev, "Error: MDIO register access failed: %d\n",
    148			   ret);
    149		return false;
    150	}
    151
    152	if (!(ret & PHY_MIISTAT_LS) ||
    153	    FIELD_GET(PHY_MIISTAT_SPD_MASK, ret) != PHY_MIISTAT_SPD_2500)
    154		return false;
    155
    156	phydev->speed = SPEED_2500;
    157	phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
    158	phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
    159		       VSPEC1_SGMII_CTRL_ANEN, 0);
    160	return true;
    161}
    162
    163static bool gpy_sgmii_aneg_en(struct phy_device *phydev)
    164{
    165	int ret;
    166
    167	ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL);
    168	if (ret < 0) {
    169		phydev_err(phydev, "Error: MMD register access failed: %d\n",
    170			   ret);
    171		return true;
    172	}
    173
    174	return (ret & VSPEC1_SGMII_CTRL_ANEN) ? true : false;
    175}
    176
    177static int gpy_config_aneg(struct phy_device *phydev)
    178{
    179	bool changed = false;
    180	u32 adv;
    181	int ret;
    182
    183	if (phydev->autoneg == AUTONEG_DISABLE) {
    184		/* Configure half duplex with genphy_setup_forced,
    185		 * because genphy_c45_pma_setup_forced does not support.
    186		 */
    187		return phydev->duplex != DUPLEX_FULL
    188			? genphy_setup_forced(phydev)
    189			: genphy_c45_pma_setup_forced(phydev);
    190	}
    191
    192	ret = genphy_c45_an_config_aneg(phydev);
    193	if (ret < 0)
    194		return ret;
    195	if (ret > 0)
    196		changed = true;
    197
    198	adv = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising);
    199	ret = phy_modify_changed(phydev, MII_CTRL1000,
    200				 ADVERTISE_1000FULL | ADVERTISE_1000HALF,
    201				 adv);
    202	if (ret < 0)
    203		return ret;
    204	if (ret > 0)
    205		changed = true;
    206
    207	ret = genphy_c45_check_and_restart_aneg(phydev, changed);
    208	if (ret < 0)
    209		return ret;
    210
    211	if (phydev->interface == PHY_INTERFACE_MODE_USXGMII ||
    212	    phydev->interface == PHY_INTERFACE_MODE_INTERNAL)
    213		return 0;
    214
    215	/* No need to trigger re-ANEG if link speed is 2.5G or SGMII ANEG is
    216	 * disabled.
    217	 */
    218	if (!gpy_sgmii_need_reaneg(phydev) || gpy_2500basex_chk(phydev) ||
    219	    !gpy_sgmii_aneg_en(phydev))
    220		return 0;
    221
    222	/* There is a design constraint in GPY2xx device where SGMII AN is
    223	 * only triggered when there is change of speed. If, PHY link
    224	 * partner`s speed is still same even after PHY TPI is down and up
    225	 * again, SGMII AN is not triggered and hence no new in-band message
    226	 * from GPY to MAC side SGMII.
    227	 * This could cause an issue during power up, when PHY is up prior to
    228	 * MAC. At this condition, once MAC side SGMII is up, MAC side SGMII
    229	 * wouldn`t receive new in-band message from GPY with correct link
    230	 * status, speed and duplex info.
    231	 *
    232	 * 1) If PHY is already up and TPI link status is still down (such as
    233	 *    hard reboot), TPI link status is polled for 4 seconds before
    234	 *    retriggerring SGMII AN.
    235	 * 2) If PHY is already up and TPI link status is also up (such as soft
    236	 *    reboot), polling of TPI link status is not needed and SGMII AN is
    237	 *    immediately retriggered.
    238	 * 3) Other conditions such as PHY is down, speed change etc, skip
    239	 *    retriggering SGMII AN. Note: in case of speed change, GPY FW will
    240	 *    initiate SGMII AN.
    241	 */
    242
    243	if (phydev->state != PHY_UP)
    244		return 0;
    245
    246	ret = phy_read_poll_timeout(phydev, MII_BMSR, ret, ret & BMSR_LSTATUS,
    247				    20000, 4000000, false);
    248	if (ret == -ETIMEDOUT)
    249		return 0;
    250	else if (ret < 0)
    251		return ret;
    252
    253	/* Trigger SGMII AN. */
    254	return phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
    255			      VSPEC1_SGMII_CTRL_ANRS, VSPEC1_SGMII_CTRL_ANRS);
    256}
    257
    258static void gpy_update_interface(struct phy_device *phydev)
    259{
    260	int ret;
    261
    262	/* Interface mode is fixed for USXGMII and integrated PHY */
    263	if (phydev->interface == PHY_INTERFACE_MODE_USXGMII ||
    264	    phydev->interface == PHY_INTERFACE_MODE_INTERNAL)
    265		return;
    266
    267	/* Automatically switch SERDES interface between SGMII and 2500-BaseX
    268	 * according to speed. Disable ANEG in 2500-BaseX mode.
    269	 */
    270	switch (phydev->speed) {
    271	case SPEED_2500:
    272		phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
    273		ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
    274				     VSPEC1_SGMII_CTRL_ANEN, 0);
    275		if (ret < 0)
    276			phydev_err(phydev,
    277				   "Error: Disable of SGMII ANEG failed: %d\n",
    278				   ret);
    279		break;
    280	case SPEED_1000:
    281	case SPEED_100:
    282	case SPEED_10:
    283		phydev->interface = PHY_INTERFACE_MODE_SGMII;
    284		if (gpy_sgmii_aneg_en(phydev))
    285			break;
    286		/* Enable and restart SGMII ANEG for 10/100/1000Mbps link speed
    287		 * if ANEG is disabled (in 2500-BaseX mode).
    288		 */
    289		ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL,
    290				     VSPEC1_SGMII_ANEN_ANRS,
    291				     VSPEC1_SGMII_ANEN_ANRS);
    292		if (ret < 0)
    293			phydev_err(phydev,
    294				   "Error: Enable of SGMII ANEG failed: %d\n",
    295				   ret);
    296		break;
    297	}
    298}
    299
    300static int gpy_read_status(struct phy_device *phydev)
    301{
    302	int ret;
    303
    304	ret = genphy_update_link(phydev);
    305	if (ret)
    306		return ret;
    307
    308	phydev->speed = SPEED_UNKNOWN;
    309	phydev->duplex = DUPLEX_UNKNOWN;
    310	phydev->pause = 0;
    311	phydev->asym_pause = 0;
    312
    313	if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) {
    314		ret = genphy_c45_read_lpa(phydev);
    315		if (ret < 0)
    316			return ret;
    317
    318		/* Read the link partner's 1G advertisement */
    319		ret = phy_read(phydev, MII_STAT1000);
    320		if (ret < 0)
    321			return ret;
    322		mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, ret);
    323	} else if (phydev->autoneg == AUTONEG_DISABLE) {
    324		linkmode_zero(phydev->lp_advertising);
    325	}
    326
    327	ret = phy_read(phydev, PHY_MIISTAT);
    328	if (ret < 0)
    329		return ret;
    330
    331	phydev->link = (ret & PHY_MIISTAT_LS) ? 1 : 0;
    332	phydev->duplex = (ret & PHY_MIISTAT_DPX) ? DUPLEX_FULL : DUPLEX_HALF;
    333	switch (FIELD_GET(PHY_MIISTAT_SPD_MASK, ret)) {
    334	case PHY_MIISTAT_SPD_10:
    335		phydev->speed = SPEED_10;
    336		break;
    337	case PHY_MIISTAT_SPD_100:
    338		phydev->speed = SPEED_100;
    339		break;
    340	case PHY_MIISTAT_SPD_1000:
    341		phydev->speed = SPEED_1000;
    342		break;
    343	case PHY_MIISTAT_SPD_2500:
    344		phydev->speed = SPEED_2500;
    345		break;
    346	}
    347
    348	if (phydev->link)
    349		gpy_update_interface(phydev);
    350
    351	return 0;
    352}
    353
    354static int gpy_config_intr(struct phy_device *phydev)
    355{
    356	u16 mask = 0;
    357
    358	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
    359		mask = PHY_IMASK_MASK;
    360
    361	return phy_write(phydev, PHY_IMASK, mask);
    362}
    363
    364static irqreturn_t gpy_handle_interrupt(struct phy_device *phydev)
    365{
    366	int reg;
    367
    368	reg = phy_read(phydev, PHY_ISTAT);
    369	if (reg < 0) {
    370		phy_error(phydev);
    371		return IRQ_NONE;
    372	}
    373
    374	if (!(reg & PHY_IMASK_MASK))
    375		return IRQ_NONE;
    376
    377	phy_trigger_machine(phydev);
    378
    379	return IRQ_HANDLED;
    380}
    381
    382static int gpy_set_wol(struct phy_device *phydev,
    383		       struct ethtool_wolinfo *wol)
    384{
    385	struct net_device *attach_dev = phydev->attached_dev;
    386	int ret;
    387
    388	if (wol->wolopts & WAKE_MAGIC) {
    389		/* MAC address - Byte0:Byte1:Byte2:Byte3:Byte4:Byte5
    390		 * VPSPEC2_WOL_AD45 = Byte0:Byte1
    391		 * VPSPEC2_WOL_AD23 = Byte2:Byte3
    392		 * VPSPEC2_WOL_AD01 = Byte4:Byte5
    393		 */
    394		ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
    395				       VPSPEC2_WOL_AD45,
    396				       ((attach_dev->dev_addr[0] << 8) |
    397				       attach_dev->dev_addr[1]));
    398		if (ret < 0)
    399			return ret;
    400
    401		ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
    402				       VPSPEC2_WOL_AD23,
    403				       ((attach_dev->dev_addr[2] << 8) |
    404				       attach_dev->dev_addr[3]));
    405		if (ret < 0)
    406			return ret;
    407
    408		ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
    409				       VPSPEC2_WOL_AD01,
    410				       ((attach_dev->dev_addr[4] << 8) |
    411				       attach_dev->dev_addr[5]));
    412		if (ret < 0)
    413			return ret;
    414
    415		/* Enable the WOL interrupt */
    416		ret = phy_write(phydev, PHY_IMASK, PHY_IMASK_WOL);
    417		if (ret < 0)
    418			return ret;
    419
    420		/* Enable magic packet matching */
    421		ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
    422				       VPSPEC2_WOL_CTL,
    423				       WOL_EN);
    424		if (ret < 0)
    425			return ret;
    426
    427		/* Clear the interrupt status register.
    428		 * Only WoL is enabled so clear all.
    429		 */
    430		ret = phy_read(phydev, PHY_ISTAT);
    431		if (ret < 0)
    432			return ret;
    433	} else {
    434		/* Disable magic packet matching */
    435		ret = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2,
    436					 VPSPEC2_WOL_CTL,
    437					 WOL_EN);
    438		if (ret < 0)
    439			return ret;
    440	}
    441
    442	if (wol->wolopts & WAKE_PHY) {
    443		/* Enable the link state change interrupt */
    444		ret = phy_set_bits(phydev, PHY_IMASK, PHY_IMASK_LSTC);
    445		if (ret < 0)
    446			return ret;
    447
    448		/* Clear the interrupt status register */
    449		ret = phy_read(phydev, PHY_ISTAT);
    450		if (ret < 0)
    451			return ret;
    452
    453		if (ret & (PHY_IMASK_MASK & ~PHY_IMASK_LSTC))
    454			phy_trigger_machine(phydev);
    455
    456		return 0;
    457	}
    458
    459	/* Disable the link state change interrupt */
    460	return phy_clear_bits(phydev, PHY_IMASK, PHY_IMASK_LSTC);
    461}
    462
    463static void gpy_get_wol(struct phy_device *phydev,
    464			struct ethtool_wolinfo *wol)
    465{
    466	int ret;
    467
    468	wol->supported = WAKE_MAGIC | WAKE_PHY;
    469	wol->wolopts = 0;
    470
    471	ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, VPSPEC2_WOL_CTL);
    472	if (ret & WOL_EN)
    473		wol->wolopts |= WAKE_MAGIC;
    474
    475	ret = phy_read(phydev, PHY_IMASK);
    476	if (ret & PHY_IMASK_LSTC)
    477		wol->wolopts |= WAKE_PHY;
    478}
    479
    480static int gpy_loopback(struct phy_device *phydev, bool enable)
    481{
    482	int ret;
    483
    484	ret = phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK,
    485			 enable ? BMCR_LOOPBACK : 0);
    486	if (!ret) {
    487		/* It takes some time for PHY device to switch
    488		 * into/out-of loopback mode.
    489		 */
    490		msleep(100);
    491	}
    492
    493	return ret;
    494}
    495
    496static int gpy115_loopback(struct phy_device *phydev, bool enable)
    497{
    498	int ret;
    499	int fw_minor;
    500
    501	if (enable)
    502		return gpy_loopback(phydev, enable);
    503
    504	ret = phy_read(phydev, PHY_FWV);
    505	if (ret < 0)
    506		return ret;
    507
    508	fw_minor = FIELD_GET(PHY_FWV_MINOR_MASK, ret);
    509	if (fw_minor > 0x0076)
    510		return gpy_loopback(phydev, 0);
    511
    512	return genphy_soft_reset(phydev);
    513}
    514
    515static struct phy_driver gpy_drivers[] = {
    516	{
    517		PHY_ID_MATCH_MODEL(PHY_ID_GPY2xx),
    518		.name		= "Maxlinear Ethernet GPY2xx",
    519		.get_features	= genphy_c45_pma_read_abilities,
    520		.config_init	= gpy_config_init,
    521		.probe		= gpy_probe,
    522		.suspend	= genphy_suspend,
    523		.resume		= genphy_resume,
    524		.config_aneg	= gpy_config_aneg,
    525		.aneg_done	= genphy_c45_aneg_done,
    526		.read_status	= gpy_read_status,
    527		.config_intr	= gpy_config_intr,
    528		.handle_interrupt = gpy_handle_interrupt,
    529		.set_wol	= gpy_set_wol,
    530		.get_wol	= gpy_get_wol,
    531		.set_loopback	= gpy_loopback,
    532	},
    533	{
    534		.phy_id		= PHY_ID_GPY115B,
    535		.phy_id_mask	= PHY_ID_GPYx15B_MASK,
    536		.name		= "Maxlinear Ethernet GPY115B",
    537		.get_features	= genphy_c45_pma_read_abilities,
    538		.config_init	= gpy_config_init,
    539		.probe		= gpy_probe,
    540		.suspend	= genphy_suspend,
    541		.resume		= genphy_resume,
    542		.config_aneg	= gpy_config_aneg,
    543		.aneg_done	= genphy_c45_aneg_done,
    544		.read_status	= gpy_read_status,
    545		.config_intr	= gpy_config_intr,
    546		.handle_interrupt = gpy_handle_interrupt,
    547		.set_wol	= gpy_set_wol,
    548		.get_wol	= gpy_get_wol,
    549		.set_loopback	= gpy115_loopback,
    550	},
    551	{
    552		PHY_ID_MATCH_MODEL(PHY_ID_GPY115C),
    553		.name		= "Maxlinear Ethernet GPY115C",
    554		.get_features	= genphy_c45_pma_read_abilities,
    555		.config_init	= gpy_config_init,
    556		.probe		= gpy_probe,
    557		.suspend	= genphy_suspend,
    558		.resume		= genphy_resume,
    559		.config_aneg	= gpy_config_aneg,
    560		.aneg_done	= genphy_c45_aneg_done,
    561		.read_status	= gpy_read_status,
    562		.config_intr	= gpy_config_intr,
    563		.handle_interrupt = gpy_handle_interrupt,
    564		.set_wol	= gpy_set_wol,
    565		.get_wol	= gpy_get_wol,
    566		.set_loopback	= gpy115_loopback,
    567	},
    568	{
    569		.phy_id		= PHY_ID_GPY211B,
    570		.phy_id_mask	= PHY_ID_GPY21xB_MASK,
    571		.name		= "Maxlinear Ethernet GPY211B",
    572		.get_features	= genphy_c45_pma_read_abilities,
    573		.config_init	= gpy_config_init,
    574		.probe		= gpy_probe,
    575		.suspend	= genphy_suspend,
    576		.resume		= genphy_resume,
    577		.config_aneg	= gpy_config_aneg,
    578		.aneg_done	= genphy_c45_aneg_done,
    579		.read_status	= gpy_read_status,
    580		.config_intr	= gpy_config_intr,
    581		.handle_interrupt = gpy_handle_interrupt,
    582		.set_wol	= gpy_set_wol,
    583		.get_wol	= gpy_get_wol,
    584		.set_loopback	= gpy_loopback,
    585	},
    586	{
    587		PHY_ID_MATCH_MODEL(PHY_ID_GPY211C),
    588		.name		= "Maxlinear Ethernet GPY211C",
    589		.get_features	= genphy_c45_pma_read_abilities,
    590		.config_init	= gpy_config_init,
    591		.probe		= gpy_probe,
    592		.suspend	= genphy_suspend,
    593		.resume		= genphy_resume,
    594		.config_aneg	= gpy_config_aneg,
    595		.aneg_done	= genphy_c45_aneg_done,
    596		.read_status	= gpy_read_status,
    597		.config_intr	= gpy_config_intr,
    598		.handle_interrupt = gpy_handle_interrupt,
    599		.set_wol	= gpy_set_wol,
    600		.get_wol	= gpy_get_wol,
    601		.set_loopback	= gpy_loopback,
    602	},
    603	{
    604		.phy_id		= PHY_ID_GPY212B,
    605		.phy_id_mask	= PHY_ID_GPY21xB_MASK,
    606		.name		= "Maxlinear Ethernet GPY212B",
    607		.get_features	= genphy_c45_pma_read_abilities,
    608		.config_init	= gpy_config_init,
    609		.probe		= gpy_probe,
    610		.suspend	= genphy_suspend,
    611		.resume		= genphy_resume,
    612		.config_aneg	= gpy_config_aneg,
    613		.aneg_done	= genphy_c45_aneg_done,
    614		.read_status	= gpy_read_status,
    615		.config_intr	= gpy_config_intr,
    616		.handle_interrupt = gpy_handle_interrupt,
    617		.set_wol	= gpy_set_wol,
    618		.get_wol	= gpy_get_wol,
    619		.set_loopback	= gpy_loopback,
    620	},
    621	{
    622		PHY_ID_MATCH_MODEL(PHY_ID_GPY212C),
    623		.name		= "Maxlinear Ethernet GPY212C",
    624		.get_features	= genphy_c45_pma_read_abilities,
    625		.config_init	= gpy_config_init,
    626		.probe		= gpy_probe,
    627		.suspend	= genphy_suspend,
    628		.resume		= genphy_resume,
    629		.config_aneg	= gpy_config_aneg,
    630		.aneg_done	= genphy_c45_aneg_done,
    631		.read_status	= gpy_read_status,
    632		.config_intr	= gpy_config_intr,
    633		.handle_interrupt = gpy_handle_interrupt,
    634		.set_wol	= gpy_set_wol,
    635		.get_wol	= gpy_get_wol,
    636		.set_loopback	= gpy_loopback,
    637	},
    638	{
    639		.phy_id		= PHY_ID_GPY215B,
    640		.phy_id_mask	= PHY_ID_GPYx15B_MASK,
    641		.name		= "Maxlinear Ethernet GPY215B",
    642		.get_features	= genphy_c45_pma_read_abilities,
    643		.config_init	= gpy_config_init,
    644		.probe		= gpy_probe,
    645		.suspend	= genphy_suspend,
    646		.resume		= genphy_resume,
    647		.config_aneg	= gpy_config_aneg,
    648		.aneg_done	= genphy_c45_aneg_done,
    649		.read_status	= gpy_read_status,
    650		.config_intr	= gpy_config_intr,
    651		.handle_interrupt = gpy_handle_interrupt,
    652		.set_wol	= gpy_set_wol,
    653		.get_wol	= gpy_get_wol,
    654		.set_loopback	= gpy_loopback,
    655	},
    656	{
    657		PHY_ID_MATCH_MODEL(PHY_ID_GPY215C),
    658		.name		= "Maxlinear Ethernet GPY215C",
    659		.get_features	= genphy_c45_pma_read_abilities,
    660		.config_init	= gpy_config_init,
    661		.probe		= gpy_probe,
    662		.suspend	= genphy_suspend,
    663		.resume		= genphy_resume,
    664		.config_aneg	= gpy_config_aneg,
    665		.aneg_done	= genphy_c45_aneg_done,
    666		.read_status	= gpy_read_status,
    667		.config_intr	= gpy_config_intr,
    668		.handle_interrupt = gpy_handle_interrupt,
    669		.set_wol	= gpy_set_wol,
    670		.get_wol	= gpy_get_wol,
    671		.set_loopback	= gpy_loopback,
    672	},
    673	{
    674		PHY_ID_MATCH_MODEL(PHY_ID_GPY241B),
    675		.name		= "Maxlinear Ethernet GPY241B",
    676		.get_features	= genphy_c45_pma_read_abilities,
    677		.config_init	= gpy_config_init,
    678		.probe		= gpy_probe,
    679		.suspend	= genphy_suspend,
    680		.resume		= genphy_resume,
    681		.config_aneg	= gpy_config_aneg,
    682		.aneg_done	= genphy_c45_aneg_done,
    683		.read_status	= gpy_read_status,
    684		.config_intr	= gpy_config_intr,
    685		.handle_interrupt = gpy_handle_interrupt,
    686		.set_wol	= gpy_set_wol,
    687		.get_wol	= gpy_get_wol,
    688		.set_loopback	= gpy_loopback,
    689	},
    690	{
    691		PHY_ID_MATCH_MODEL(PHY_ID_GPY241BM),
    692		.name		= "Maxlinear Ethernet GPY241BM",
    693		.get_features	= genphy_c45_pma_read_abilities,
    694		.config_init	= gpy_config_init,
    695		.probe		= gpy_probe,
    696		.suspend	= genphy_suspend,
    697		.resume		= genphy_resume,
    698		.config_aneg	= gpy_config_aneg,
    699		.aneg_done	= genphy_c45_aneg_done,
    700		.read_status	= gpy_read_status,
    701		.config_intr	= gpy_config_intr,
    702		.handle_interrupt = gpy_handle_interrupt,
    703		.set_wol	= gpy_set_wol,
    704		.get_wol	= gpy_get_wol,
    705		.set_loopback	= gpy_loopback,
    706	},
    707	{
    708		PHY_ID_MATCH_MODEL(PHY_ID_GPY245B),
    709		.name		= "Maxlinear Ethernet GPY245B",
    710		.get_features	= genphy_c45_pma_read_abilities,
    711		.config_init	= gpy_config_init,
    712		.probe		= gpy_probe,
    713		.suspend	= genphy_suspend,
    714		.resume		= genphy_resume,
    715		.config_aneg	= gpy_config_aneg,
    716		.aneg_done	= genphy_c45_aneg_done,
    717		.read_status	= gpy_read_status,
    718		.config_intr	= gpy_config_intr,
    719		.handle_interrupt = gpy_handle_interrupt,
    720		.set_wol	= gpy_set_wol,
    721		.get_wol	= gpy_get_wol,
    722		.set_loopback	= gpy_loopback,
    723	},
    724};
    725module_phy_driver(gpy_drivers);
    726
    727static struct mdio_device_id __maybe_unused gpy_tbl[] = {
    728	{PHY_ID_MATCH_MODEL(PHY_ID_GPY2xx)},
    729	{PHY_ID_GPY115B, PHY_ID_GPYx15B_MASK},
    730	{PHY_ID_MATCH_MODEL(PHY_ID_GPY115C)},
    731	{PHY_ID_GPY211B, PHY_ID_GPY21xB_MASK},
    732	{PHY_ID_MATCH_MODEL(PHY_ID_GPY211C)},
    733	{PHY_ID_GPY212B, PHY_ID_GPY21xB_MASK},
    734	{PHY_ID_MATCH_MODEL(PHY_ID_GPY212C)},
    735	{PHY_ID_GPY215B, PHY_ID_GPYx15B_MASK},
    736	{PHY_ID_MATCH_MODEL(PHY_ID_GPY215C)},
    737	{PHY_ID_MATCH_MODEL(PHY_ID_GPY241B)},
    738	{PHY_ID_MATCH_MODEL(PHY_ID_GPY241BM)},
    739	{PHY_ID_MATCH_MODEL(PHY_ID_GPY245B)},
    740	{ }
    741};
    742MODULE_DEVICE_TABLE(mdio, gpy_tbl);
    743
    744MODULE_DESCRIPTION("Maxlinear Ethernet GPY Driver");
    745MODULE_AUTHOR("Xu Liang");
    746MODULE_LICENSE("GPL");