162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2015 Microchip Technology
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include <linux/kernel.h>
662306a36Sopenharmony_ci#include <linux/module.h>
762306a36Sopenharmony_ci#include <linux/mii.h>
862306a36Sopenharmony_ci#include <linux/ethtool.h>
962306a36Sopenharmony_ci#include <linux/phy.h>
1062306a36Sopenharmony_ci#include <linux/microchipphy.h>
1162306a36Sopenharmony_ci#include <linux/delay.h>
1262306a36Sopenharmony_ci#include <linux/of.h>
1362306a36Sopenharmony_ci#include <dt-bindings/net/microchip-lan78xx.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define DRIVER_AUTHOR	"WOOJUNG HUH <woojung.huh@microchip.com>"
1662306a36Sopenharmony_ci#define DRIVER_DESC	"Microchip LAN88XX PHY driver"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistruct lan88xx_priv {
1962306a36Sopenharmony_ci	int	chip_id;
2062306a36Sopenharmony_ci	int	chip_rev;
2162306a36Sopenharmony_ci	__u32	wolopts;
2262306a36Sopenharmony_ci};
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic int lan88xx_read_page(struct phy_device *phydev)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	return __phy_read(phydev, LAN88XX_EXT_PAGE_ACCESS);
2762306a36Sopenharmony_ci}
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic int lan88xx_write_page(struct phy_device *phydev, int page)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	return __phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS, page);
3262306a36Sopenharmony_ci}
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic int lan88xx_phy_config_intr(struct phy_device *phydev)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	int rc;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
3962306a36Sopenharmony_ci		/* unmask all source and clear them before enable */
4062306a36Sopenharmony_ci		rc = phy_write(phydev, LAN88XX_INT_MASK, 0x7FFF);
4162306a36Sopenharmony_ci		rc = phy_read(phydev, LAN88XX_INT_STS);
4262306a36Sopenharmony_ci		rc = phy_write(phydev, LAN88XX_INT_MASK,
4362306a36Sopenharmony_ci			       LAN88XX_INT_MASK_MDINTPIN_EN_ |
4462306a36Sopenharmony_ci			       LAN88XX_INT_MASK_LINK_CHANGE_);
4562306a36Sopenharmony_ci	} else {
4662306a36Sopenharmony_ci		rc = phy_write(phydev, LAN88XX_INT_MASK, 0);
4762306a36Sopenharmony_ci		if (rc)
4862306a36Sopenharmony_ci			return rc;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci		/* Ack interrupts after they have been disabled */
5162306a36Sopenharmony_ci		rc = phy_read(phydev, LAN88XX_INT_STS);
5262306a36Sopenharmony_ci	}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	return rc < 0 ? rc : 0;
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic irqreturn_t lan88xx_handle_interrupt(struct phy_device *phydev)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	int irq_status;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	irq_status = phy_read(phydev, LAN88XX_INT_STS);
6262306a36Sopenharmony_ci	if (irq_status < 0) {
6362306a36Sopenharmony_ci		phy_error(phydev);
6462306a36Sopenharmony_ci		return IRQ_NONE;
6562306a36Sopenharmony_ci	}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	if (!(irq_status & LAN88XX_INT_STS_LINK_CHANGE_))
6862306a36Sopenharmony_ci		return IRQ_NONE;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	phy_trigger_machine(phydev);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	return IRQ_HANDLED;
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic int lan88xx_suspend(struct phy_device *phydev)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	struct lan88xx_priv *priv = phydev->priv;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	/* do not power down PHY when WOL is enabled */
8062306a36Sopenharmony_ci	if (!priv->wolopts)
8162306a36Sopenharmony_ci		genphy_suspend(phydev);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	return 0;
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic int lan88xx_TR_reg_set(struct phy_device *phydev, u16 regaddr,
8762306a36Sopenharmony_ci			      u32 data)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	int val, save_page, ret = 0;
9062306a36Sopenharmony_ci	u16 buf;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	/* Save current page */
9362306a36Sopenharmony_ci	save_page = phy_save_page(phydev);
9462306a36Sopenharmony_ci	if (save_page < 0) {
9562306a36Sopenharmony_ci		phydev_warn(phydev, "Failed to get current page\n");
9662306a36Sopenharmony_ci		goto err;
9762306a36Sopenharmony_ci	}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	/* Switch to TR page */
10062306a36Sopenharmony_ci	lan88xx_write_page(phydev, LAN88XX_EXT_PAGE_ACCESS_TR);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	ret = __phy_write(phydev, LAN88XX_EXT_PAGE_TR_LOW_DATA,
10362306a36Sopenharmony_ci			  (data & 0xFFFF));
10462306a36Sopenharmony_ci	if (ret < 0) {
10562306a36Sopenharmony_ci		phydev_warn(phydev, "Failed to write TR low data\n");
10662306a36Sopenharmony_ci		goto err;
10762306a36Sopenharmony_ci	}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	ret = __phy_write(phydev, LAN88XX_EXT_PAGE_TR_HIGH_DATA,
11062306a36Sopenharmony_ci			  (data & 0x00FF0000) >> 16);
11162306a36Sopenharmony_ci	if (ret < 0) {
11262306a36Sopenharmony_ci		phydev_warn(phydev, "Failed to write TR high data\n");
11362306a36Sopenharmony_ci		goto err;
11462306a36Sopenharmony_ci	}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	/* Config control bits [15:13] of register */
11762306a36Sopenharmony_ci	buf = (regaddr & ~(0x3 << 13));/* Clr [14:13] to write data in reg */
11862306a36Sopenharmony_ci	buf |= 0x8000; /* Set [15] to Packet transmit */
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	ret = __phy_write(phydev, LAN88XX_EXT_PAGE_TR_CR, buf);
12162306a36Sopenharmony_ci	if (ret < 0) {
12262306a36Sopenharmony_ci		phydev_warn(phydev, "Failed to write data in reg\n");
12362306a36Sopenharmony_ci		goto err;
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	usleep_range(1000, 2000);/* Wait for Data to be written */
12762306a36Sopenharmony_ci	val = __phy_read(phydev, LAN88XX_EXT_PAGE_TR_CR);
12862306a36Sopenharmony_ci	if (!(val & 0x8000))
12962306a36Sopenharmony_ci		phydev_warn(phydev, "TR Register[0x%X] configuration failed\n",
13062306a36Sopenharmony_ci			    regaddr);
13162306a36Sopenharmony_cierr:
13262306a36Sopenharmony_ci	return phy_restore_page(phydev, save_page, ret);
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic void lan88xx_config_TR_regs(struct phy_device *phydev)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	int err;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	/* Get access to Channel 0x1, Node 0xF , Register 0x01.
14062306a36Sopenharmony_ci	 * Write 24-bit value 0x12B00A to register. Setting MrvlTrFix1000Kf,
14162306a36Sopenharmony_ci	 * MrvlTrFix1000Kp, MasterEnableTR bits.
14262306a36Sopenharmony_ci	 */
14362306a36Sopenharmony_ci	err = lan88xx_TR_reg_set(phydev, 0x0F82, 0x12B00A);
14462306a36Sopenharmony_ci	if (err < 0)
14562306a36Sopenharmony_ci		phydev_warn(phydev, "Failed to Set Register[0x0F82]\n");
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	/* Get access to Channel b'10, Node b'1101, Register 0x06.
14862306a36Sopenharmony_ci	 * Write 24-bit value 0xD2C46F to register. Setting SSTrKf1000Slv,
14962306a36Sopenharmony_ci	 * SSTrKp1000Mas bits.
15062306a36Sopenharmony_ci	 */
15162306a36Sopenharmony_ci	err = lan88xx_TR_reg_set(phydev, 0x168C, 0xD2C46F);
15262306a36Sopenharmony_ci	if (err < 0)
15362306a36Sopenharmony_ci		phydev_warn(phydev, "Failed to Set Register[0x168C]\n");
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	/* Get access to Channel b'10, Node b'1111, Register 0x11.
15662306a36Sopenharmony_ci	 * Write 24-bit value 0x620 to register. Setting rem_upd_done_thresh
15762306a36Sopenharmony_ci	 * bits
15862306a36Sopenharmony_ci	 */
15962306a36Sopenharmony_ci	err = lan88xx_TR_reg_set(phydev, 0x17A2, 0x620);
16062306a36Sopenharmony_ci	if (err < 0)
16162306a36Sopenharmony_ci		phydev_warn(phydev, "Failed to Set Register[0x17A2]\n");
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	/* Get access to Channel b'10, Node b'1101, Register 0x10.
16462306a36Sopenharmony_ci	 * Write 24-bit value 0xEEFFDD to register. Setting
16562306a36Sopenharmony_ci	 * eee_TrKp1Long_1000, eee_TrKp2Long_1000, eee_TrKp3Long_1000,
16662306a36Sopenharmony_ci	 * eee_TrKp1Short_1000,eee_TrKp2Short_1000, eee_TrKp3Short_1000 bits.
16762306a36Sopenharmony_ci	 */
16862306a36Sopenharmony_ci	err = lan88xx_TR_reg_set(phydev, 0x16A0, 0xEEFFDD);
16962306a36Sopenharmony_ci	if (err < 0)
17062306a36Sopenharmony_ci		phydev_warn(phydev, "Failed to Set Register[0x16A0]\n");
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	/* Get access to Channel b'10, Node b'1101, Register 0x13.
17362306a36Sopenharmony_ci	 * Write 24-bit value 0x071448 to register. Setting
17462306a36Sopenharmony_ci	 * slv_lpi_tr_tmr_val1, slv_lpi_tr_tmr_val2 bits.
17562306a36Sopenharmony_ci	 */
17662306a36Sopenharmony_ci	err = lan88xx_TR_reg_set(phydev, 0x16A6, 0x071448);
17762306a36Sopenharmony_ci	if (err < 0)
17862306a36Sopenharmony_ci		phydev_warn(phydev, "Failed to Set Register[0x16A6]\n");
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	/* Get access to Channel b'10, Node b'1101, Register 0x12.
18162306a36Sopenharmony_ci	 * Write 24-bit value 0x13132F to register. Setting
18262306a36Sopenharmony_ci	 * slv_sigdet_timer_val1, slv_sigdet_timer_val2 bits.
18362306a36Sopenharmony_ci	 */
18462306a36Sopenharmony_ci	err = lan88xx_TR_reg_set(phydev, 0x16A4, 0x13132F);
18562306a36Sopenharmony_ci	if (err < 0)
18662306a36Sopenharmony_ci		phydev_warn(phydev, "Failed to Set Register[0x16A4]\n");
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	/* Get access to Channel b'10, Node b'1101, Register 0x14.
18962306a36Sopenharmony_ci	 * Write 24-bit value 0x0 to register. Setting eee_3level_delay,
19062306a36Sopenharmony_ci	 * eee_TrKf_freeze_delay bits.
19162306a36Sopenharmony_ci	 */
19262306a36Sopenharmony_ci	err = lan88xx_TR_reg_set(phydev, 0x16A8, 0x0);
19362306a36Sopenharmony_ci	if (err < 0)
19462306a36Sopenharmony_ci		phydev_warn(phydev, "Failed to Set Register[0x16A8]\n");
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	/* Get access to Channel b'01, Node b'1111, Register 0x34.
19762306a36Sopenharmony_ci	 * Write 24-bit value 0x91B06C to register. Setting
19862306a36Sopenharmony_ci	 * FastMseSearchThreshLong1000, FastMseSearchThreshShort1000,
19962306a36Sopenharmony_ci	 * FastMseSearchUpdGain1000 bits.
20062306a36Sopenharmony_ci	 */
20162306a36Sopenharmony_ci	err = lan88xx_TR_reg_set(phydev, 0x0FE8, 0x91B06C);
20262306a36Sopenharmony_ci	if (err < 0)
20362306a36Sopenharmony_ci		phydev_warn(phydev, "Failed to Set Register[0x0FE8]\n");
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	/* Get access to Channel b'01, Node b'1111, Register 0x3E.
20662306a36Sopenharmony_ci	 * Write 24-bit value 0xC0A028 to register. Setting
20762306a36Sopenharmony_ci	 * FastMseKp2ThreshLong1000, FastMseKp2ThreshShort1000,
20862306a36Sopenharmony_ci	 * FastMseKp2UpdGain1000, FastMseKp2ExitEn1000 bits.
20962306a36Sopenharmony_ci	 */
21062306a36Sopenharmony_ci	err = lan88xx_TR_reg_set(phydev, 0x0FFC, 0xC0A028);
21162306a36Sopenharmony_ci	if (err < 0)
21262306a36Sopenharmony_ci		phydev_warn(phydev, "Failed to Set Register[0x0FFC]\n");
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	/* Get access to Channel b'01, Node b'1111, Register 0x35.
21562306a36Sopenharmony_ci	 * Write 24-bit value 0x041600 to register. Setting
21662306a36Sopenharmony_ci	 * FastMseSearchPhShNum1000, FastMseSearchClksPerPh1000,
21762306a36Sopenharmony_ci	 * FastMsePhChangeDelay1000 bits.
21862306a36Sopenharmony_ci	 */
21962306a36Sopenharmony_ci	err = lan88xx_TR_reg_set(phydev, 0x0FEA, 0x041600);
22062306a36Sopenharmony_ci	if (err < 0)
22162306a36Sopenharmony_ci		phydev_warn(phydev, "Failed to Set Register[0x0FEA]\n");
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	/* Get access to Channel b'10, Node b'1101, Register 0x03.
22462306a36Sopenharmony_ci	 * Write 24-bit value 0x000004 to register. Setting TrFreeze bits.
22562306a36Sopenharmony_ci	 */
22662306a36Sopenharmony_ci	err = lan88xx_TR_reg_set(phydev, 0x1686, 0x000004);
22762306a36Sopenharmony_ci	if (err < 0)
22862306a36Sopenharmony_ci		phydev_warn(phydev, "Failed to Set Register[0x1686]\n");
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic int lan88xx_probe(struct phy_device *phydev)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	struct device *dev = &phydev->mdio.dev;
23462306a36Sopenharmony_ci	struct lan88xx_priv *priv;
23562306a36Sopenharmony_ci	u32 led_modes[4];
23662306a36Sopenharmony_ci	int len;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
23962306a36Sopenharmony_ci	if (!priv)
24062306a36Sopenharmony_ci		return -ENOMEM;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	priv->wolopts = 0;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	len = of_property_read_variable_u32_array(dev->of_node,
24562306a36Sopenharmony_ci						  "microchip,led-modes",
24662306a36Sopenharmony_ci						  led_modes,
24762306a36Sopenharmony_ci						  0,
24862306a36Sopenharmony_ci						  ARRAY_SIZE(led_modes));
24962306a36Sopenharmony_ci	if (len >= 0) {
25062306a36Sopenharmony_ci		u32 reg = 0;
25162306a36Sopenharmony_ci		int i;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci		for (i = 0; i < len; i++) {
25462306a36Sopenharmony_ci			if (led_modes[i] > 15)
25562306a36Sopenharmony_ci				return -EINVAL;
25662306a36Sopenharmony_ci			reg |= led_modes[i] << (i * 4);
25762306a36Sopenharmony_ci		}
25862306a36Sopenharmony_ci		for (; i < ARRAY_SIZE(led_modes); i++)
25962306a36Sopenharmony_ci			reg |= LAN78XX_FORCE_LED_OFF << (i * 4);
26062306a36Sopenharmony_ci		(void)phy_write(phydev, LAN78XX_PHY_LED_MODE_SELECT, reg);
26162306a36Sopenharmony_ci	} else if (len == -EOVERFLOW) {
26262306a36Sopenharmony_ci		return -EINVAL;
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	/* these values can be used to identify internal PHY */
26662306a36Sopenharmony_ci	priv->chip_id = phy_read_mmd(phydev, 3, LAN88XX_MMD3_CHIP_ID);
26762306a36Sopenharmony_ci	priv->chip_rev = phy_read_mmd(phydev, 3, LAN88XX_MMD3_CHIP_REV);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	phydev->priv = priv;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	return 0;
27262306a36Sopenharmony_ci}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_cistatic void lan88xx_remove(struct phy_device *phydev)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	struct device *dev = &phydev->mdio.dev;
27762306a36Sopenharmony_ci	struct lan88xx_priv *priv = phydev->priv;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	if (priv)
28062306a36Sopenharmony_ci		devm_kfree(dev, priv);
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cistatic int lan88xx_set_wol(struct phy_device *phydev,
28462306a36Sopenharmony_ci			   struct ethtool_wolinfo *wol)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	struct lan88xx_priv *priv = phydev->priv;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	priv->wolopts = wol->wolopts;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	return 0;
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_cistatic void lan88xx_set_mdix(struct phy_device *phydev)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	int buf;
29662306a36Sopenharmony_ci	int val;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	switch (phydev->mdix_ctrl) {
29962306a36Sopenharmony_ci	case ETH_TP_MDI:
30062306a36Sopenharmony_ci		val = LAN88XX_EXT_MODE_CTRL_MDI_;
30162306a36Sopenharmony_ci		break;
30262306a36Sopenharmony_ci	case ETH_TP_MDI_X:
30362306a36Sopenharmony_ci		val = LAN88XX_EXT_MODE_CTRL_MDI_X_;
30462306a36Sopenharmony_ci		break;
30562306a36Sopenharmony_ci	case ETH_TP_MDI_AUTO:
30662306a36Sopenharmony_ci		val = LAN88XX_EXT_MODE_CTRL_AUTO_MDIX_;
30762306a36Sopenharmony_ci		break;
30862306a36Sopenharmony_ci	default:
30962306a36Sopenharmony_ci		return;
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS, LAN88XX_EXT_PAGE_SPACE_1);
31362306a36Sopenharmony_ci	buf = phy_read(phydev, LAN88XX_EXT_MODE_CTRL);
31462306a36Sopenharmony_ci	buf &= ~LAN88XX_EXT_MODE_CTRL_MDIX_MASK_;
31562306a36Sopenharmony_ci	buf |= val;
31662306a36Sopenharmony_ci	phy_write(phydev, LAN88XX_EXT_MODE_CTRL, buf);
31762306a36Sopenharmony_ci	phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS, LAN88XX_EXT_PAGE_SPACE_0);
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistatic int lan88xx_config_init(struct phy_device *phydev)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	int val;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	/*Zerodetect delay enable */
32562306a36Sopenharmony_ci	val = phy_read_mmd(phydev, MDIO_MMD_PCS,
32662306a36Sopenharmony_ci			   PHY_ARDENNES_MMD_DEV_3_PHY_CFG);
32762306a36Sopenharmony_ci	val |= PHY_ARDENNES_MMD_DEV_3_PHY_CFG_ZD_DLY_EN_;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	phy_write_mmd(phydev, MDIO_MMD_PCS, PHY_ARDENNES_MMD_DEV_3_PHY_CFG,
33062306a36Sopenharmony_ci		      val);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	/* Config DSP registers */
33362306a36Sopenharmony_ci	lan88xx_config_TR_regs(phydev);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	return 0;
33662306a36Sopenharmony_ci}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_cistatic int lan88xx_config_aneg(struct phy_device *phydev)
33962306a36Sopenharmony_ci{
34062306a36Sopenharmony_ci	lan88xx_set_mdix(phydev);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	return genphy_config_aneg(phydev);
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_cistatic void lan88xx_link_change_notify(struct phy_device *phydev)
34662306a36Sopenharmony_ci{
34762306a36Sopenharmony_ci	int temp;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	/* At forced 100 F/H mode, chip may fail to set mode correctly
35062306a36Sopenharmony_ci	 * when cable is switched between long(~50+m) and short one.
35162306a36Sopenharmony_ci	 * As workaround, set to 10 before setting to 100
35262306a36Sopenharmony_ci	 * at forced 100 F/H mode.
35362306a36Sopenharmony_ci	 */
35462306a36Sopenharmony_ci	if (!phydev->autoneg && phydev->speed == 100) {
35562306a36Sopenharmony_ci		/* disable phy interrupt */
35662306a36Sopenharmony_ci		temp = phy_read(phydev, LAN88XX_INT_MASK);
35762306a36Sopenharmony_ci		temp &= ~LAN88XX_INT_MASK_MDINTPIN_EN_;
35862306a36Sopenharmony_ci		phy_write(phydev, LAN88XX_INT_MASK, temp);
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci		temp = phy_read(phydev, MII_BMCR);
36162306a36Sopenharmony_ci		temp &= ~(BMCR_SPEED100 | BMCR_SPEED1000);
36262306a36Sopenharmony_ci		phy_write(phydev, MII_BMCR, temp); /* set to 10 first */
36362306a36Sopenharmony_ci		temp |= BMCR_SPEED100;
36462306a36Sopenharmony_ci		phy_write(phydev, MII_BMCR, temp); /* set to 100 later */
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci		/* clear pending interrupt generated while workaround */
36762306a36Sopenharmony_ci		temp = phy_read(phydev, LAN88XX_INT_STS);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci		/* enable phy interrupt back */
37062306a36Sopenharmony_ci		temp = phy_read(phydev, LAN88XX_INT_MASK);
37162306a36Sopenharmony_ci		temp |= LAN88XX_INT_MASK_MDINTPIN_EN_;
37262306a36Sopenharmony_ci		phy_write(phydev, LAN88XX_INT_MASK, temp);
37362306a36Sopenharmony_ci	}
37462306a36Sopenharmony_ci}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_cistatic struct phy_driver microchip_phy_driver[] = {
37762306a36Sopenharmony_ci{
37862306a36Sopenharmony_ci	.phy_id		= 0x0007c132,
37962306a36Sopenharmony_ci	/* This mask (0xfffffff2) is to differentiate from
38062306a36Sopenharmony_ci	 * LAN8742 (phy_id 0x0007c130 and 0x0007c131)
38162306a36Sopenharmony_ci	 * and allows future phy_id revisions.
38262306a36Sopenharmony_ci	 */
38362306a36Sopenharmony_ci	.phy_id_mask	= 0xfffffff2,
38462306a36Sopenharmony_ci	.name		= "Microchip LAN88xx",
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	/* PHY_GBIT_FEATURES */
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	.probe		= lan88xx_probe,
38962306a36Sopenharmony_ci	.remove		= lan88xx_remove,
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	.config_init	= lan88xx_config_init,
39262306a36Sopenharmony_ci	.config_aneg	= lan88xx_config_aneg,
39362306a36Sopenharmony_ci	.link_change_notify = lan88xx_link_change_notify,
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	.config_intr	= lan88xx_phy_config_intr,
39662306a36Sopenharmony_ci	.handle_interrupt = lan88xx_handle_interrupt,
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	.suspend	= lan88xx_suspend,
39962306a36Sopenharmony_ci	.resume		= genphy_resume,
40062306a36Sopenharmony_ci	.set_wol	= lan88xx_set_wol,
40162306a36Sopenharmony_ci	.read_page	= lan88xx_read_page,
40262306a36Sopenharmony_ci	.write_page	= lan88xx_write_page,
40362306a36Sopenharmony_ci} };
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_cimodule_phy_driver(microchip_phy_driver);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_cistatic struct mdio_device_id __maybe_unused microchip_tbl[] = {
40862306a36Sopenharmony_ci	{ 0x0007c132, 0xfffffff2 },
40962306a36Sopenharmony_ci	{ }
41062306a36Sopenharmony_ci};
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(mdio, microchip_tbl);
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR);
41562306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
41662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
417