162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Driver for Vitesse PHYs
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Author: Kriston Carson
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/kernel.h>
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/mii.h>
1162306a36Sopenharmony_ci#include <linux/ethtool.h>
1262306a36Sopenharmony_ci#include <linux/phy.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci/* Vitesse Extended Page Magic Register(s) */
1562306a36Sopenharmony_ci#define MII_VSC82X4_EXT_PAGE_16E	0x10
1662306a36Sopenharmony_ci#define MII_VSC82X4_EXT_PAGE_17E	0x11
1762306a36Sopenharmony_ci#define MII_VSC82X4_EXT_PAGE_18E	0x12
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/* Vitesse Extended Control Register 1 */
2062306a36Sopenharmony_ci#define MII_VSC8244_EXT_CON1           0x17
2162306a36Sopenharmony_ci#define MII_VSC8244_EXTCON1_INIT       0x0000
2262306a36Sopenharmony_ci#define MII_VSC8244_EXTCON1_TX_SKEW_MASK	0x0c00
2362306a36Sopenharmony_ci#define MII_VSC8244_EXTCON1_RX_SKEW_MASK	0x0300
2462306a36Sopenharmony_ci#define MII_VSC8244_EXTCON1_TX_SKEW	0x0800
2562306a36Sopenharmony_ci#define MII_VSC8244_EXTCON1_RX_SKEW	0x0200
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci/* Vitesse Interrupt Mask Register */
2862306a36Sopenharmony_ci#define MII_VSC8244_IMASK		0x19
2962306a36Sopenharmony_ci#define MII_VSC8244_IMASK_IEN		0x8000
3062306a36Sopenharmony_ci#define MII_VSC8244_IMASK_SPEED		0x4000
3162306a36Sopenharmony_ci#define MII_VSC8244_IMASK_LINK		0x2000
3262306a36Sopenharmony_ci#define MII_VSC8244_IMASK_DUPLEX	0x1000
3362306a36Sopenharmony_ci#define MII_VSC8244_IMASK_MASK		0xf000
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define MII_VSC8221_IMASK_MASK		0xa000
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci/* Vitesse Interrupt Status Register */
3862306a36Sopenharmony_ci#define MII_VSC8244_ISTAT		0x1a
3962306a36Sopenharmony_ci#define MII_VSC8244_ISTAT_STATUS	0x8000
4062306a36Sopenharmony_ci#define MII_VSC8244_ISTAT_SPEED		0x4000
4162306a36Sopenharmony_ci#define MII_VSC8244_ISTAT_LINK		0x2000
4262306a36Sopenharmony_ci#define MII_VSC8244_ISTAT_DUPLEX	0x1000
4362306a36Sopenharmony_ci#define MII_VSC8244_ISTAT_MASK		(MII_VSC8244_ISTAT_SPEED | \
4462306a36Sopenharmony_ci					 MII_VSC8244_ISTAT_LINK | \
4562306a36Sopenharmony_ci					 MII_VSC8244_ISTAT_DUPLEX)
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#define MII_VSC8221_ISTAT_MASK		MII_VSC8244_ISTAT_LINK
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/* Vitesse Auxiliary Control/Status Register */
5062306a36Sopenharmony_ci#define MII_VSC8244_AUX_CONSTAT		0x1c
5162306a36Sopenharmony_ci#define MII_VSC8244_AUXCONSTAT_INIT	0x0000
5262306a36Sopenharmony_ci#define MII_VSC8244_AUXCONSTAT_DUPLEX	0x0020
5362306a36Sopenharmony_ci#define MII_VSC8244_AUXCONSTAT_SPEED	0x0018
5462306a36Sopenharmony_ci#define MII_VSC8244_AUXCONSTAT_GBIT	0x0010
5562306a36Sopenharmony_ci#define MII_VSC8244_AUXCONSTAT_100	0x0008
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci#define MII_VSC8221_AUXCONSTAT_INIT	0x0004 /* need to set this bit? */
5862306a36Sopenharmony_ci#define MII_VSC8221_AUXCONSTAT_RESERVED	0x0004
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci/* Vitesse Extended Page Access Register */
6162306a36Sopenharmony_ci#define MII_VSC82X4_EXT_PAGE_ACCESS	0x1f
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci/* Vitesse VSC8601 Extended PHY Control Register 1 */
6462306a36Sopenharmony_ci#define MII_VSC8601_EPHY_CTL		0x17
6562306a36Sopenharmony_ci#define MII_VSC8601_EPHY_CTL_RGMII_SKEW	(1 << 8)
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci#define PHY_ID_VSC8234			0x000fc620
6862306a36Sopenharmony_ci#define PHY_ID_VSC8244			0x000fc6c0
6962306a36Sopenharmony_ci#define PHY_ID_VSC8572			0x000704d0
7062306a36Sopenharmony_ci#define PHY_ID_VSC8601			0x00070420
7162306a36Sopenharmony_ci#define PHY_ID_VSC7385			0x00070450
7262306a36Sopenharmony_ci#define PHY_ID_VSC7388			0x00070480
7362306a36Sopenharmony_ci#define PHY_ID_VSC7395			0x00070550
7462306a36Sopenharmony_ci#define PHY_ID_VSC7398			0x00070580
7562306a36Sopenharmony_ci#define PHY_ID_VSC8662			0x00070660
7662306a36Sopenharmony_ci#define PHY_ID_VSC8221			0x000fc550
7762306a36Sopenharmony_ci#define PHY_ID_VSC8211			0x000fc4b0
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ciMODULE_DESCRIPTION("Vitesse PHY driver");
8062306a36Sopenharmony_ciMODULE_AUTHOR("Kriston Carson");
8162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic int vsc824x_add_skew(struct phy_device *phydev)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	int err;
8662306a36Sopenharmony_ci	int extcon;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	extcon = phy_read(phydev, MII_VSC8244_EXT_CON1);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	if (extcon < 0)
9162306a36Sopenharmony_ci		return extcon;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	extcon &= ~(MII_VSC8244_EXTCON1_TX_SKEW_MASK |
9462306a36Sopenharmony_ci			MII_VSC8244_EXTCON1_RX_SKEW_MASK);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	extcon |= (MII_VSC8244_EXTCON1_TX_SKEW |
9762306a36Sopenharmony_ci			MII_VSC8244_EXTCON1_RX_SKEW);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	err = phy_write(phydev, MII_VSC8244_EXT_CON1, extcon);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	return err;
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic int vsc824x_config_init(struct phy_device *phydev)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	int err;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	err = phy_write(phydev, MII_VSC8244_AUX_CONSTAT,
10962306a36Sopenharmony_ci			MII_VSC8244_AUXCONSTAT_INIT);
11062306a36Sopenharmony_ci	if (err < 0)
11162306a36Sopenharmony_ci		return err;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
11462306a36Sopenharmony_ci		err = vsc824x_add_skew(phydev);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	return err;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci#define VSC73XX_EXT_PAGE_ACCESS 0x1f
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic int vsc73xx_read_page(struct phy_device *phydev)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	return __phy_read(phydev, VSC73XX_EXT_PAGE_ACCESS);
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic int vsc73xx_write_page(struct phy_device *phydev, int page)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	return __phy_write(phydev, VSC73XX_EXT_PAGE_ACCESS, page);
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic void vsc73xx_config_init(struct phy_device *phydev)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	/* Receiver init */
13462306a36Sopenharmony_ci	phy_write(phydev, 0x1f, 0x2a30);
13562306a36Sopenharmony_ci	phy_modify(phydev, 0x0c, 0x0300, 0x0200);
13662306a36Sopenharmony_ci	phy_write(phydev, 0x1f, 0x0000);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	/* Config LEDs 0x61 */
13962306a36Sopenharmony_ci	phy_modify(phydev, MII_TPISTATUS, 0xff00, 0x0061);
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic int vsc738x_config_init(struct phy_device *phydev)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	u16 rev;
14562306a36Sopenharmony_ci	/* This magic sequence appear in the application note
14662306a36Sopenharmony_ci	 * "VSC7385/7388 PHY Configuration".
14762306a36Sopenharmony_ci	 *
14862306a36Sopenharmony_ci	 * Maybe one day we will get to know what it all means.
14962306a36Sopenharmony_ci	 */
15062306a36Sopenharmony_ci	phy_write(phydev, 0x1f, 0x2a30);
15162306a36Sopenharmony_ci	phy_modify(phydev, 0x08, 0x0200, 0x0200);
15262306a36Sopenharmony_ci	phy_write(phydev, 0x1f, 0x52b5);
15362306a36Sopenharmony_ci	phy_write(phydev, 0x10, 0xb68a);
15462306a36Sopenharmony_ci	phy_modify(phydev, 0x12, 0xff07, 0x0003);
15562306a36Sopenharmony_ci	phy_modify(phydev, 0x11, 0x00ff, 0x00a2);
15662306a36Sopenharmony_ci	phy_write(phydev, 0x10, 0x968a);
15762306a36Sopenharmony_ci	phy_write(phydev, 0x1f, 0x2a30);
15862306a36Sopenharmony_ci	phy_modify(phydev, 0x08, 0x0200, 0x0000);
15962306a36Sopenharmony_ci	phy_write(phydev, 0x1f, 0x0000);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	/* Read revision */
16262306a36Sopenharmony_ci	rev = phy_read(phydev, MII_PHYSID2);
16362306a36Sopenharmony_ci	rev &= 0x0f;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	/* Special quirk for revision 0 */
16662306a36Sopenharmony_ci	if (rev == 0) {
16762306a36Sopenharmony_ci		phy_write(phydev, 0x1f, 0x2a30);
16862306a36Sopenharmony_ci		phy_modify(phydev, 0x08, 0x0200, 0x0200);
16962306a36Sopenharmony_ci		phy_write(phydev, 0x1f, 0x52b5);
17062306a36Sopenharmony_ci		phy_write(phydev, 0x12, 0x0000);
17162306a36Sopenharmony_ci		phy_write(phydev, 0x11, 0x0689);
17262306a36Sopenharmony_ci		phy_write(phydev, 0x10, 0x8f92);
17362306a36Sopenharmony_ci		phy_write(phydev, 0x1f, 0x52b5);
17462306a36Sopenharmony_ci		phy_write(phydev, 0x12, 0x0000);
17562306a36Sopenharmony_ci		phy_write(phydev, 0x11, 0x0e35);
17662306a36Sopenharmony_ci		phy_write(phydev, 0x10, 0x9786);
17762306a36Sopenharmony_ci		phy_write(phydev, 0x1f, 0x2a30);
17862306a36Sopenharmony_ci		phy_modify(phydev, 0x08, 0x0200, 0x0000);
17962306a36Sopenharmony_ci		phy_write(phydev, 0x17, 0xff80);
18062306a36Sopenharmony_ci		phy_write(phydev, 0x17, 0x0000);
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	phy_write(phydev, 0x1f, 0x0000);
18462306a36Sopenharmony_ci	phy_write(phydev, 0x12, 0x0048);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	if (rev == 0) {
18762306a36Sopenharmony_ci		phy_write(phydev, 0x1f, 0x2a30);
18862306a36Sopenharmony_ci		phy_write(phydev, 0x14, 0x6600);
18962306a36Sopenharmony_ci		phy_write(phydev, 0x1f, 0x0000);
19062306a36Sopenharmony_ci		phy_write(phydev, 0x18, 0xa24e);
19162306a36Sopenharmony_ci	} else {
19262306a36Sopenharmony_ci		phy_write(phydev, 0x1f, 0x2a30);
19362306a36Sopenharmony_ci		phy_modify(phydev, 0x16, 0x0fc0, 0x0240);
19462306a36Sopenharmony_ci		phy_modify(phydev, 0x14, 0x6000, 0x4000);
19562306a36Sopenharmony_ci		/* bits 14-15 in extended register 0x14 controls DACG amplitude
19662306a36Sopenharmony_ci		 * 6 = -8%, 2 is hardware default
19762306a36Sopenharmony_ci		 */
19862306a36Sopenharmony_ci		phy_write(phydev, 0x1f, 0x0001);
19962306a36Sopenharmony_ci		phy_modify(phydev, 0x14, 0xe000, 0x6000);
20062306a36Sopenharmony_ci		phy_write(phydev, 0x1f, 0x0000);
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	vsc73xx_config_init(phydev);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	return 0;
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic int vsc739x_config_init(struct phy_device *phydev)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	/* This magic sequence appears in the VSC7395 SparX-G5e application
21162306a36Sopenharmony_ci	 * note "VSC7395/VSC7398 PHY Configuration"
21262306a36Sopenharmony_ci	 *
21362306a36Sopenharmony_ci	 * Maybe one day we will get to know what it all means.
21462306a36Sopenharmony_ci	 */
21562306a36Sopenharmony_ci	phy_write(phydev, 0x1f, 0x2a30);
21662306a36Sopenharmony_ci	phy_modify(phydev, 0x08, 0x0200, 0x0200);
21762306a36Sopenharmony_ci	phy_write(phydev, 0x1f, 0x52b5);
21862306a36Sopenharmony_ci	phy_write(phydev, 0x10, 0xb68a);
21962306a36Sopenharmony_ci	phy_modify(phydev, 0x12, 0xff07, 0x0003);
22062306a36Sopenharmony_ci	phy_modify(phydev, 0x11, 0x00ff, 0x00a2);
22162306a36Sopenharmony_ci	phy_write(phydev, 0x10, 0x968a);
22262306a36Sopenharmony_ci	phy_write(phydev, 0x1f, 0x2a30);
22362306a36Sopenharmony_ci	phy_modify(phydev, 0x08, 0x0200, 0x0000);
22462306a36Sopenharmony_ci	phy_write(phydev, 0x1f, 0x0000);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	phy_write(phydev, 0x1f, 0x0000);
22762306a36Sopenharmony_ci	phy_write(phydev, 0x12, 0x0048);
22862306a36Sopenharmony_ci	phy_write(phydev, 0x1f, 0x2a30);
22962306a36Sopenharmony_ci	phy_modify(phydev, 0x16, 0x0fc0, 0x0240);
23062306a36Sopenharmony_ci	phy_modify(phydev, 0x14, 0x6000, 0x4000);
23162306a36Sopenharmony_ci	phy_write(phydev, 0x1f, 0x0001);
23262306a36Sopenharmony_ci	phy_modify(phydev, 0x14, 0xe000, 0x6000);
23362306a36Sopenharmony_ci	phy_write(phydev, 0x1f, 0x0000);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	vsc73xx_config_init(phydev);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	return 0;
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cistatic int vsc73xx_config_aneg(struct phy_device *phydev)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	/* The VSC73xx switches does not like to be instructed to
24362306a36Sopenharmony_ci	 * do autonegotiation in any way, it prefers that you just go
24462306a36Sopenharmony_ci	 * with the power-on/reset defaults. Writing some registers will
24562306a36Sopenharmony_ci	 * just make autonegotiation permanently fail.
24662306a36Sopenharmony_ci	 */
24762306a36Sopenharmony_ci	return 0;
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci/* This adds a skew for both TX and RX clocks, so the skew should only be
25162306a36Sopenharmony_ci * applied to "rgmii-id" interfaces. It may not work as expected
25262306a36Sopenharmony_ci * on "rgmii-txid", "rgmii-rxid" or "rgmii" interfaces.
25362306a36Sopenharmony_ci */
25462306a36Sopenharmony_cistatic int vsc8601_add_skew(struct phy_device *phydev)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	int ret;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	ret = phy_read(phydev, MII_VSC8601_EPHY_CTL);
25962306a36Sopenharmony_ci	if (ret < 0)
26062306a36Sopenharmony_ci		return ret;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	ret |= MII_VSC8601_EPHY_CTL_RGMII_SKEW;
26362306a36Sopenharmony_ci	return phy_write(phydev, MII_VSC8601_EPHY_CTL, ret);
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cistatic int vsc8601_config_init(struct phy_device *phydev)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	int ret = 0;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
27162306a36Sopenharmony_ci		ret = vsc8601_add_skew(phydev);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	if (ret < 0)
27462306a36Sopenharmony_ci		return ret;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	return 0;
27762306a36Sopenharmony_ci}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_cistatic int vsc82xx_config_intr(struct phy_device *phydev)
28062306a36Sopenharmony_ci{
28162306a36Sopenharmony_ci	int err;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
28462306a36Sopenharmony_ci		/* Don't bother to ACK the interrupts since the 824x cannot
28562306a36Sopenharmony_ci		 * clear the interrupts if they are disabled.
28662306a36Sopenharmony_ci		 */
28762306a36Sopenharmony_ci		err = phy_write(phydev, MII_VSC8244_IMASK,
28862306a36Sopenharmony_ci			(phydev->drv->phy_id == PHY_ID_VSC8234 ||
28962306a36Sopenharmony_ci			 phydev->drv->phy_id == PHY_ID_VSC8244 ||
29062306a36Sopenharmony_ci			 phydev->drv->phy_id == PHY_ID_VSC8572 ||
29162306a36Sopenharmony_ci			 phydev->drv->phy_id == PHY_ID_VSC8601) ?
29262306a36Sopenharmony_ci				MII_VSC8244_IMASK_MASK :
29362306a36Sopenharmony_ci				MII_VSC8221_IMASK_MASK);
29462306a36Sopenharmony_ci	else {
29562306a36Sopenharmony_ci		/* The Vitesse PHY cannot clear the interrupt
29662306a36Sopenharmony_ci		 * once it has disabled them, so we clear them first
29762306a36Sopenharmony_ci		 */
29862306a36Sopenharmony_ci		err = phy_read(phydev, MII_VSC8244_ISTAT);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci		if (err < 0)
30162306a36Sopenharmony_ci			return err;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci		err = phy_write(phydev, MII_VSC8244_IMASK, 0);
30462306a36Sopenharmony_ci	}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	return err;
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_cistatic irqreturn_t vsc82xx_handle_interrupt(struct phy_device *phydev)
31062306a36Sopenharmony_ci{
31162306a36Sopenharmony_ci	int irq_status, irq_mask;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	if (phydev->drv->phy_id == PHY_ID_VSC8244 ||
31462306a36Sopenharmony_ci	    phydev->drv->phy_id == PHY_ID_VSC8572 ||
31562306a36Sopenharmony_ci	    phydev->drv->phy_id == PHY_ID_VSC8601)
31662306a36Sopenharmony_ci		irq_mask = MII_VSC8244_ISTAT_MASK;
31762306a36Sopenharmony_ci	else
31862306a36Sopenharmony_ci		irq_mask = MII_VSC8221_ISTAT_MASK;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	irq_status = phy_read(phydev, MII_VSC8244_ISTAT);
32162306a36Sopenharmony_ci	if (irq_status < 0) {
32262306a36Sopenharmony_ci		phy_error(phydev);
32362306a36Sopenharmony_ci		return IRQ_NONE;
32462306a36Sopenharmony_ci	}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	if (!(irq_status & irq_mask))
32762306a36Sopenharmony_ci		return IRQ_NONE;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	phy_trigger_machine(phydev);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	return IRQ_HANDLED;
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistatic int vsc8221_config_init(struct phy_device *phydev)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	int err;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	err = phy_write(phydev, MII_VSC8244_AUX_CONSTAT,
33962306a36Sopenharmony_ci			MII_VSC8221_AUXCONSTAT_INIT);
34062306a36Sopenharmony_ci	return err;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	/* Perhaps we should set EXT_CON1 based on the interface?
34362306a36Sopenharmony_ci	 * Options are 802.3Z SerDes or SGMII
34462306a36Sopenharmony_ci	 */
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci/* vsc82x4_config_autocross_enable - Enable auto MDI/MDI-X for forced links
34862306a36Sopenharmony_ci * @phydev: target phy_device struct
34962306a36Sopenharmony_ci *
35062306a36Sopenharmony_ci * Enable auto MDI/MDI-X when in 10/100 forced link speeds by writing
35162306a36Sopenharmony_ci * special values in the VSC8234/VSC8244 extended reserved registers
35262306a36Sopenharmony_ci */
35362306a36Sopenharmony_cistatic int vsc82x4_config_autocross_enable(struct phy_device *phydev)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	int ret;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	if (phydev->autoneg == AUTONEG_ENABLE || phydev->speed > SPEED_100)
35862306a36Sopenharmony_ci		return 0;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	/* map extended registers set 0x10 - 0x1e */
36162306a36Sopenharmony_ci	ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_ACCESS, 0x52b5);
36262306a36Sopenharmony_ci	if (ret >= 0)
36362306a36Sopenharmony_ci		ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_18E, 0x0012);
36462306a36Sopenharmony_ci	if (ret >= 0)
36562306a36Sopenharmony_ci		ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_17E, 0x2803);
36662306a36Sopenharmony_ci	if (ret >= 0)
36762306a36Sopenharmony_ci		ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_16E, 0x87fa);
36862306a36Sopenharmony_ci	/* map standard registers set 0x10 - 0x1e */
36962306a36Sopenharmony_ci	if (ret >= 0)
37062306a36Sopenharmony_ci		ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_ACCESS, 0x0000);
37162306a36Sopenharmony_ci	else
37262306a36Sopenharmony_ci		phy_write(phydev, MII_VSC82X4_EXT_PAGE_ACCESS, 0x0000);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	return ret;
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci/* vsc82x4_config_aneg - restart auto-negotiation or write BMCR
37862306a36Sopenharmony_ci * @phydev: target phy_device struct
37962306a36Sopenharmony_ci *
38062306a36Sopenharmony_ci * Description: If auto-negotiation is enabled, we configure the
38162306a36Sopenharmony_ci *   advertising, and then restart auto-negotiation.  If it is not
38262306a36Sopenharmony_ci *   enabled, then we write the BMCR and also start the auto
38362306a36Sopenharmony_ci *   MDI/MDI-X feature
38462306a36Sopenharmony_ci */
38562306a36Sopenharmony_cistatic int vsc82x4_config_aneg(struct phy_device *phydev)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	int ret;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	/* Enable auto MDI/MDI-X when in 10/100 forced link speeds by
39062306a36Sopenharmony_ci	 * writing special values in the VSC8234 extended reserved registers
39162306a36Sopenharmony_ci	 */
39262306a36Sopenharmony_ci	if (phydev->autoneg != AUTONEG_ENABLE && phydev->speed <= SPEED_100) {
39362306a36Sopenharmony_ci		ret = genphy_setup_forced(phydev);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci		if (ret < 0) /* error */
39662306a36Sopenharmony_ci			return ret;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci		return vsc82x4_config_autocross_enable(phydev);
39962306a36Sopenharmony_ci	}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	return genphy_config_aneg(phydev);
40262306a36Sopenharmony_ci}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci/* Vitesse 82xx */
40562306a36Sopenharmony_cistatic struct phy_driver vsc82xx_driver[] = {
40662306a36Sopenharmony_ci{
40762306a36Sopenharmony_ci	.phy_id         = PHY_ID_VSC8234,
40862306a36Sopenharmony_ci	.name           = "Vitesse VSC8234",
40962306a36Sopenharmony_ci	.phy_id_mask    = 0x000ffff0,
41062306a36Sopenharmony_ci	/* PHY_GBIT_FEATURES */
41162306a36Sopenharmony_ci	.config_init    = &vsc824x_config_init,
41262306a36Sopenharmony_ci	.config_aneg    = &vsc82x4_config_aneg,
41362306a36Sopenharmony_ci	.config_intr    = &vsc82xx_config_intr,
41462306a36Sopenharmony_ci	.handle_interrupt = &vsc82xx_handle_interrupt,
41562306a36Sopenharmony_ci}, {
41662306a36Sopenharmony_ci	.phy_id		= PHY_ID_VSC8244,
41762306a36Sopenharmony_ci	.name		= "Vitesse VSC8244",
41862306a36Sopenharmony_ci	.phy_id_mask	= 0x000fffc0,
41962306a36Sopenharmony_ci	/* PHY_GBIT_FEATURES */
42062306a36Sopenharmony_ci	.config_init	= &vsc824x_config_init,
42162306a36Sopenharmony_ci	.config_aneg	= &vsc82x4_config_aneg,
42262306a36Sopenharmony_ci	.config_intr	= &vsc82xx_config_intr,
42362306a36Sopenharmony_ci	.handle_interrupt = &vsc82xx_handle_interrupt,
42462306a36Sopenharmony_ci}, {
42562306a36Sopenharmony_ci	.phy_id         = PHY_ID_VSC8572,
42662306a36Sopenharmony_ci	.name           = "Vitesse VSC8572",
42762306a36Sopenharmony_ci	.phy_id_mask    = 0x000ffff0,
42862306a36Sopenharmony_ci	/* PHY_GBIT_FEATURES */
42962306a36Sopenharmony_ci	.config_init    = &vsc824x_config_init,
43062306a36Sopenharmony_ci	.config_aneg    = &vsc82x4_config_aneg,
43162306a36Sopenharmony_ci	.config_intr    = &vsc82xx_config_intr,
43262306a36Sopenharmony_ci	.handle_interrupt = &vsc82xx_handle_interrupt,
43362306a36Sopenharmony_ci}, {
43462306a36Sopenharmony_ci	.phy_id         = PHY_ID_VSC8601,
43562306a36Sopenharmony_ci	.name           = "Vitesse VSC8601",
43662306a36Sopenharmony_ci	.phy_id_mask    = 0x000ffff0,
43762306a36Sopenharmony_ci	/* PHY_GBIT_FEATURES */
43862306a36Sopenharmony_ci	.config_init    = &vsc8601_config_init,
43962306a36Sopenharmony_ci	.config_intr    = &vsc82xx_config_intr,
44062306a36Sopenharmony_ci	.handle_interrupt = &vsc82xx_handle_interrupt,
44162306a36Sopenharmony_ci}, {
44262306a36Sopenharmony_ci	.phy_id         = PHY_ID_VSC7385,
44362306a36Sopenharmony_ci	.name           = "Vitesse VSC7385",
44462306a36Sopenharmony_ci	.phy_id_mask    = 0x000ffff0,
44562306a36Sopenharmony_ci	/* PHY_GBIT_FEATURES */
44662306a36Sopenharmony_ci	.config_init    = vsc738x_config_init,
44762306a36Sopenharmony_ci	.config_aneg    = vsc73xx_config_aneg,
44862306a36Sopenharmony_ci	.read_page      = vsc73xx_read_page,
44962306a36Sopenharmony_ci	.write_page     = vsc73xx_write_page,
45062306a36Sopenharmony_ci}, {
45162306a36Sopenharmony_ci	.phy_id         = PHY_ID_VSC7388,
45262306a36Sopenharmony_ci	.name           = "Vitesse VSC7388",
45362306a36Sopenharmony_ci	.phy_id_mask    = 0x000ffff0,
45462306a36Sopenharmony_ci	/* PHY_GBIT_FEATURES */
45562306a36Sopenharmony_ci	.config_init    = vsc738x_config_init,
45662306a36Sopenharmony_ci	.config_aneg    = vsc73xx_config_aneg,
45762306a36Sopenharmony_ci	.read_page      = vsc73xx_read_page,
45862306a36Sopenharmony_ci	.write_page     = vsc73xx_write_page,
45962306a36Sopenharmony_ci}, {
46062306a36Sopenharmony_ci	.phy_id         = PHY_ID_VSC7395,
46162306a36Sopenharmony_ci	.name           = "Vitesse VSC7395",
46262306a36Sopenharmony_ci	.phy_id_mask    = 0x000ffff0,
46362306a36Sopenharmony_ci	/* PHY_GBIT_FEATURES */
46462306a36Sopenharmony_ci	.config_init    = vsc739x_config_init,
46562306a36Sopenharmony_ci	.config_aneg    = vsc73xx_config_aneg,
46662306a36Sopenharmony_ci	.read_page      = vsc73xx_read_page,
46762306a36Sopenharmony_ci	.write_page     = vsc73xx_write_page,
46862306a36Sopenharmony_ci}, {
46962306a36Sopenharmony_ci	.phy_id         = PHY_ID_VSC7398,
47062306a36Sopenharmony_ci	.name           = "Vitesse VSC7398",
47162306a36Sopenharmony_ci	.phy_id_mask    = 0x000ffff0,
47262306a36Sopenharmony_ci	/* PHY_GBIT_FEATURES */
47362306a36Sopenharmony_ci	.config_init    = vsc739x_config_init,
47462306a36Sopenharmony_ci	.config_aneg    = vsc73xx_config_aneg,
47562306a36Sopenharmony_ci	.read_page      = vsc73xx_read_page,
47662306a36Sopenharmony_ci	.write_page     = vsc73xx_write_page,
47762306a36Sopenharmony_ci}, {
47862306a36Sopenharmony_ci	.phy_id         = PHY_ID_VSC8662,
47962306a36Sopenharmony_ci	.name           = "Vitesse VSC8662",
48062306a36Sopenharmony_ci	.phy_id_mask    = 0x000ffff0,
48162306a36Sopenharmony_ci	/* PHY_GBIT_FEATURES */
48262306a36Sopenharmony_ci	.config_init    = &vsc824x_config_init,
48362306a36Sopenharmony_ci	.config_aneg    = &vsc82x4_config_aneg,
48462306a36Sopenharmony_ci	.config_intr    = &vsc82xx_config_intr,
48562306a36Sopenharmony_ci	.handle_interrupt = &vsc82xx_handle_interrupt,
48662306a36Sopenharmony_ci}, {
48762306a36Sopenharmony_ci	/* Vitesse 8221 */
48862306a36Sopenharmony_ci	.phy_id		= PHY_ID_VSC8221,
48962306a36Sopenharmony_ci	.phy_id_mask	= 0x000ffff0,
49062306a36Sopenharmony_ci	.name		= "Vitesse VSC8221",
49162306a36Sopenharmony_ci	/* PHY_GBIT_FEATURES */
49262306a36Sopenharmony_ci	.config_init	= &vsc8221_config_init,
49362306a36Sopenharmony_ci	.config_intr	= &vsc82xx_config_intr,
49462306a36Sopenharmony_ci	.handle_interrupt = &vsc82xx_handle_interrupt,
49562306a36Sopenharmony_ci}, {
49662306a36Sopenharmony_ci	/* Vitesse 8211 */
49762306a36Sopenharmony_ci	.phy_id		= PHY_ID_VSC8211,
49862306a36Sopenharmony_ci	.phy_id_mask	= 0x000ffff0,
49962306a36Sopenharmony_ci	.name		= "Vitesse VSC8211",
50062306a36Sopenharmony_ci	/* PHY_GBIT_FEATURES */
50162306a36Sopenharmony_ci	.config_init	= &vsc8221_config_init,
50262306a36Sopenharmony_ci	.config_intr	= &vsc82xx_config_intr,
50362306a36Sopenharmony_ci	.handle_interrupt = &vsc82xx_handle_interrupt,
50462306a36Sopenharmony_ci} };
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_cimodule_phy_driver(vsc82xx_driver);
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_cistatic struct mdio_device_id __maybe_unused vitesse_tbl[] = {
50962306a36Sopenharmony_ci	{ PHY_ID_VSC8234, 0x000ffff0 },
51062306a36Sopenharmony_ci	{ PHY_ID_VSC8244, 0x000fffc0 },
51162306a36Sopenharmony_ci	{ PHY_ID_VSC8572, 0x000ffff0 },
51262306a36Sopenharmony_ci	{ PHY_ID_VSC7385, 0x000ffff0 },
51362306a36Sopenharmony_ci	{ PHY_ID_VSC7388, 0x000ffff0 },
51462306a36Sopenharmony_ci	{ PHY_ID_VSC7395, 0x000ffff0 },
51562306a36Sopenharmony_ci	{ PHY_ID_VSC7398, 0x000ffff0 },
51662306a36Sopenharmony_ci	{ PHY_ID_VSC8662, 0x000ffff0 },
51762306a36Sopenharmony_ci	{ PHY_ID_VSC8221, 0x000ffff0 },
51862306a36Sopenharmony_ci	{ PHY_ID_VSC8211, 0x000ffff0 },
51962306a36Sopenharmony_ci	{ }
52062306a36Sopenharmony_ci};
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(mdio, vitesse_tbl);
523