162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2015-2017 Broadcom
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include "bcm-phy-lib.h"
762306a36Sopenharmony_ci#include <linux/bitfield.h>
862306a36Sopenharmony_ci#include <linux/brcmphy.h>
962306a36Sopenharmony_ci#include <linux/etherdevice.h>
1062306a36Sopenharmony_ci#include <linux/export.h>
1162306a36Sopenharmony_ci#include <linux/mdio.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/phy.h>
1462306a36Sopenharmony_ci#include <linux/ethtool.h>
1562306a36Sopenharmony_ci#include <linux/ethtool_netlink.h>
1662306a36Sopenharmony_ci#include <linux/netdevice.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#define MII_BCM_CHANNEL_WIDTH     0x2000
1962306a36Sopenharmony_ci#define BCM_CL45VEN_EEE_ADV       0x3c
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ciint __bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	int rc;
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	rc = __phy_write(phydev, MII_BCM54XX_EXP_SEL, reg);
2662306a36Sopenharmony_ci	if (rc < 0)
2762306a36Sopenharmony_ci		return rc;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	return __phy_write(phydev, MII_BCM54XX_EXP_DATA, val);
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__bcm_phy_write_exp);
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ciint bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	int rc;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	phy_lock_mdio_bus(phydev);
3862306a36Sopenharmony_ci	rc = __bcm_phy_write_exp(phydev, reg, val);
3962306a36Sopenharmony_ci	phy_unlock_mdio_bus(phydev);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	return rc;
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_write_exp);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ciint __bcm_phy_read_exp(struct phy_device *phydev, u16 reg)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	int val;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	val = __phy_write(phydev, MII_BCM54XX_EXP_SEL, reg);
5062306a36Sopenharmony_ci	if (val < 0)
5162306a36Sopenharmony_ci		return val;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	val = __phy_read(phydev, MII_BCM54XX_EXP_DATA);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	/* Restore default value.  It's O.K. if this write fails. */
5662306a36Sopenharmony_ci	__phy_write(phydev, MII_BCM54XX_EXP_SEL, 0);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	return val;
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__bcm_phy_read_exp);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ciint bcm_phy_read_exp(struct phy_device *phydev, u16 reg)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	int rc;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	phy_lock_mdio_bus(phydev);
6762306a36Sopenharmony_ci	rc = __bcm_phy_read_exp(phydev, reg);
6862306a36Sopenharmony_ci	phy_unlock_mdio_bus(phydev);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	return rc;
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_read_exp);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ciint __bcm_phy_modify_exp(struct phy_device *phydev, u16 reg, u16 mask, u16 set)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	int new, ret;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	ret = __phy_write(phydev, MII_BCM54XX_EXP_SEL, reg);
7962306a36Sopenharmony_ci	if (ret < 0)
8062306a36Sopenharmony_ci		return ret;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	ret = __phy_read(phydev, MII_BCM54XX_EXP_DATA);
8362306a36Sopenharmony_ci	if (ret < 0)
8462306a36Sopenharmony_ci		return ret;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	new = (ret & ~mask) | set;
8762306a36Sopenharmony_ci	if (new == ret)
8862306a36Sopenharmony_ci		return 0;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	return __phy_write(phydev, MII_BCM54XX_EXP_DATA, new);
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__bcm_phy_modify_exp);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ciint bcm_phy_modify_exp(struct phy_device *phydev, u16 reg, u16 mask, u16 set)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	int ret;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	phy_lock_mdio_bus(phydev);
9962306a36Sopenharmony_ci	ret = __bcm_phy_modify_exp(phydev, reg, mask, set);
10062306a36Sopenharmony_ci	phy_unlock_mdio_bus(phydev);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	return ret;
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_modify_exp);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ciint bcm54xx_auxctl_read(struct phy_device *phydev, u16 regnum)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	/* The register must be written to both the Shadow Register Select and
10962306a36Sopenharmony_ci	 * the Shadow Read Register Selector
11062306a36Sopenharmony_ci	 */
11162306a36Sopenharmony_ci	phy_write(phydev, MII_BCM54XX_AUX_CTL, MII_BCM54XX_AUXCTL_SHDWSEL_MASK |
11262306a36Sopenharmony_ci		  regnum << MII_BCM54XX_AUXCTL_SHDWSEL_READ_SHIFT);
11362306a36Sopenharmony_ci	return phy_read(phydev, MII_BCM54XX_AUX_CTL);
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm54xx_auxctl_read);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ciint bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	return phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum | val);
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ciEXPORT_SYMBOL(bcm54xx_auxctl_write);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ciint bcm_phy_write_misc(struct phy_device *phydev,
12462306a36Sopenharmony_ci		       u16 reg, u16 chl, u16 val)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	int rc;
12762306a36Sopenharmony_ci	int tmp;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	rc = phy_write(phydev, MII_BCM54XX_AUX_CTL,
13062306a36Sopenharmony_ci		       MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
13162306a36Sopenharmony_ci	if (rc < 0)
13262306a36Sopenharmony_ci		return rc;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL);
13562306a36Sopenharmony_ci	tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA;
13662306a36Sopenharmony_ci	rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp);
13762306a36Sopenharmony_ci	if (rc < 0)
13862306a36Sopenharmony_ci		return rc;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	tmp = (chl * MII_BCM_CHANNEL_WIDTH) | reg;
14162306a36Sopenharmony_ci	rc = bcm_phy_write_exp(phydev, tmp, val);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	return rc;
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_write_misc);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ciint bcm_phy_read_misc(struct phy_device *phydev,
14862306a36Sopenharmony_ci		      u16 reg, u16 chl)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	int rc;
15162306a36Sopenharmony_ci	int tmp;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	rc = phy_write(phydev, MII_BCM54XX_AUX_CTL,
15462306a36Sopenharmony_ci		       MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
15562306a36Sopenharmony_ci	if (rc < 0)
15662306a36Sopenharmony_ci		return rc;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	tmp = phy_read(phydev, MII_BCM54XX_AUX_CTL);
15962306a36Sopenharmony_ci	tmp |= MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA;
16062306a36Sopenharmony_ci	rc = phy_write(phydev, MII_BCM54XX_AUX_CTL, tmp);
16162306a36Sopenharmony_ci	if (rc < 0)
16262306a36Sopenharmony_ci		return rc;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	tmp = (chl * MII_BCM_CHANNEL_WIDTH) | reg;
16562306a36Sopenharmony_ci	rc = bcm_phy_read_exp(phydev, tmp);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	return rc;
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_read_misc);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ciint bcm_phy_ack_intr(struct phy_device *phydev)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	int reg;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	/* Clear pending interrupts.  */
17662306a36Sopenharmony_ci	reg = phy_read(phydev, MII_BCM54XX_ISR);
17762306a36Sopenharmony_ci	if (reg < 0)
17862306a36Sopenharmony_ci		return reg;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	return 0;
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_ack_intr);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ciint bcm_phy_config_intr(struct phy_device *phydev)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	int reg, err;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	reg = phy_read(phydev, MII_BCM54XX_ECR);
18962306a36Sopenharmony_ci	if (reg < 0)
19062306a36Sopenharmony_ci		return reg;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
19362306a36Sopenharmony_ci		err = bcm_phy_ack_intr(phydev);
19462306a36Sopenharmony_ci		if (err)
19562306a36Sopenharmony_ci			return err;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci		reg &= ~MII_BCM54XX_ECR_IM;
19862306a36Sopenharmony_ci		err = phy_write(phydev, MII_BCM54XX_ECR, reg);
19962306a36Sopenharmony_ci	} else {
20062306a36Sopenharmony_ci		reg |= MII_BCM54XX_ECR_IM;
20162306a36Sopenharmony_ci		err = phy_write(phydev, MII_BCM54XX_ECR, reg);
20262306a36Sopenharmony_ci		if (err)
20362306a36Sopenharmony_ci			return err;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci		err = bcm_phy_ack_intr(phydev);
20662306a36Sopenharmony_ci	}
20762306a36Sopenharmony_ci	return err;
20862306a36Sopenharmony_ci}
20962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_config_intr);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ciirqreturn_t bcm_phy_handle_interrupt(struct phy_device *phydev)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	int irq_status, irq_mask;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	irq_status = phy_read(phydev, MII_BCM54XX_ISR);
21662306a36Sopenharmony_ci	if (irq_status < 0) {
21762306a36Sopenharmony_ci		phy_error(phydev);
21862306a36Sopenharmony_ci		return IRQ_NONE;
21962306a36Sopenharmony_ci	}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	/* If a bit from the Interrupt Mask register is set, the corresponding
22262306a36Sopenharmony_ci	 * bit from the Interrupt Status register is masked. So read the IMR
22362306a36Sopenharmony_ci	 * and then flip the bits to get the list of possible interrupt
22462306a36Sopenharmony_ci	 * sources.
22562306a36Sopenharmony_ci	 */
22662306a36Sopenharmony_ci	irq_mask = phy_read(phydev, MII_BCM54XX_IMR);
22762306a36Sopenharmony_ci	if (irq_mask < 0) {
22862306a36Sopenharmony_ci		phy_error(phydev);
22962306a36Sopenharmony_ci		return IRQ_NONE;
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci	irq_mask = ~irq_mask;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	if (!(irq_status & irq_mask))
23462306a36Sopenharmony_ci		return IRQ_NONE;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	phy_trigger_machine(phydev);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	return IRQ_HANDLED;
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_handle_interrupt);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ciint bcm_phy_read_shadow(struct phy_device *phydev, u16 shadow)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	phy_write(phydev, MII_BCM54XX_SHD, MII_BCM54XX_SHD_VAL(shadow));
24562306a36Sopenharmony_ci	return MII_BCM54XX_SHD_DATA(phy_read(phydev, MII_BCM54XX_SHD));
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_read_shadow);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ciint bcm_phy_write_shadow(struct phy_device *phydev, u16 shadow,
25062306a36Sopenharmony_ci			 u16 val)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	return phy_write(phydev, MII_BCM54XX_SHD,
25362306a36Sopenharmony_ci			 MII_BCM54XX_SHD_WRITE |
25462306a36Sopenharmony_ci			 MII_BCM54XX_SHD_VAL(shadow) |
25562306a36Sopenharmony_ci			 MII_BCM54XX_SHD_DATA(val));
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_write_shadow);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ciint __bcm_phy_read_rdb(struct phy_device *phydev, u16 rdb)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	int val;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	val = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb);
26462306a36Sopenharmony_ci	if (val < 0)
26562306a36Sopenharmony_ci		return val;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	return __phy_read(phydev, MII_BCM54XX_RDB_DATA);
26862306a36Sopenharmony_ci}
26962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__bcm_phy_read_rdb);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ciint bcm_phy_read_rdb(struct phy_device *phydev, u16 rdb)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	int ret;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	phy_lock_mdio_bus(phydev);
27662306a36Sopenharmony_ci	ret = __bcm_phy_read_rdb(phydev, rdb);
27762306a36Sopenharmony_ci	phy_unlock_mdio_bus(phydev);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	return ret;
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_read_rdb);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ciint __bcm_phy_write_rdb(struct phy_device *phydev, u16 rdb, u16 val)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci	int ret;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	ret = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb);
28862306a36Sopenharmony_ci	if (ret < 0)
28962306a36Sopenharmony_ci		return ret;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	return __phy_write(phydev, MII_BCM54XX_RDB_DATA, val);
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__bcm_phy_write_rdb);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ciint bcm_phy_write_rdb(struct phy_device *phydev, u16 rdb, u16 val)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	int ret;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	phy_lock_mdio_bus(phydev);
30062306a36Sopenharmony_ci	ret = __bcm_phy_write_rdb(phydev, rdb, val);
30162306a36Sopenharmony_ci	phy_unlock_mdio_bus(phydev);
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	return ret;
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_write_rdb);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ciint __bcm_phy_modify_rdb(struct phy_device *phydev, u16 rdb, u16 mask, u16 set)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	int new, ret;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	ret = __phy_write(phydev, MII_BCM54XX_RDB_ADDR, rdb);
31262306a36Sopenharmony_ci	if (ret < 0)
31362306a36Sopenharmony_ci		return ret;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	ret = __phy_read(phydev, MII_BCM54XX_RDB_DATA);
31662306a36Sopenharmony_ci	if (ret < 0)
31762306a36Sopenharmony_ci		return ret;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	new = (ret & ~mask) | set;
32062306a36Sopenharmony_ci	if (new == ret)
32162306a36Sopenharmony_ci		return 0;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	return __phy_write(phydev, MII_BCM54XX_RDB_DATA, new);
32462306a36Sopenharmony_ci}
32562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__bcm_phy_modify_rdb);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ciint bcm_phy_modify_rdb(struct phy_device *phydev, u16 rdb, u16 mask, u16 set)
32862306a36Sopenharmony_ci{
32962306a36Sopenharmony_ci	int ret;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	phy_lock_mdio_bus(phydev);
33262306a36Sopenharmony_ci	ret = __bcm_phy_modify_rdb(phydev, rdb, mask, set);
33362306a36Sopenharmony_ci	phy_unlock_mdio_bus(phydev);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	return ret;
33662306a36Sopenharmony_ci}
33762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_modify_rdb);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ciint bcm_phy_enable_apd(struct phy_device *phydev, bool dll_pwr_down)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	int val;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	if (dll_pwr_down) {
34462306a36Sopenharmony_ci		val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3);
34562306a36Sopenharmony_ci		if (val < 0)
34662306a36Sopenharmony_ci			return val;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci		val |= BCM54XX_SHD_SCR3_DLLAPD_DIS;
34962306a36Sopenharmony_ci		bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, val);
35062306a36Sopenharmony_ci	}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_APD);
35362306a36Sopenharmony_ci	if (val < 0)
35462306a36Sopenharmony_ci		return val;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	/* Clear APD bits */
35762306a36Sopenharmony_ci	val &= BCM_APD_CLR_MASK;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	if (phydev->autoneg == AUTONEG_ENABLE)
36062306a36Sopenharmony_ci		val |= BCM54XX_SHD_APD_EN;
36162306a36Sopenharmony_ci	else
36262306a36Sopenharmony_ci		val |= BCM_NO_ANEG_APD_EN;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	/* Enable energy detect single link pulse for easy wakeup */
36562306a36Sopenharmony_ci	val |= BCM_APD_SINGLELP_EN;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	/* Enable Auto Power-Down (APD) for the PHY */
36862306a36Sopenharmony_ci	return bcm_phy_write_shadow(phydev, BCM54XX_SHD_APD, val);
36962306a36Sopenharmony_ci}
37062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_enable_apd);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ciint bcm_phy_set_eee(struct phy_device *phydev, bool enable)
37362306a36Sopenharmony_ci{
37462306a36Sopenharmony_ci	int val, mask = 0;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	/* Enable EEE at PHY level */
37762306a36Sopenharmony_ci	val = phy_read_mmd(phydev, MDIO_MMD_AN, BRCM_CL45VEN_EEE_CONTROL);
37862306a36Sopenharmony_ci	if (val < 0)
37962306a36Sopenharmony_ci		return val;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	if (enable)
38262306a36Sopenharmony_ci		val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X;
38362306a36Sopenharmony_ci	else
38462306a36Sopenharmony_ci		val &= ~(LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	phy_write_mmd(phydev, MDIO_MMD_AN, BRCM_CL45VEN_EEE_CONTROL, (u32)val);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	/* Advertise EEE */
38962306a36Sopenharmony_ci	val = phy_read_mmd(phydev, MDIO_MMD_AN, BCM_CL45VEN_EEE_ADV);
39062306a36Sopenharmony_ci	if (val < 0)
39162306a36Sopenharmony_ci		return val;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
39462306a36Sopenharmony_ci			      phydev->supported))
39562306a36Sopenharmony_ci		mask |= MDIO_EEE_1000T;
39662306a36Sopenharmony_ci	if (linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
39762306a36Sopenharmony_ci			      phydev->supported))
39862306a36Sopenharmony_ci		mask |= MDIO_EEE_100TX;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	if (enable)
40162306a36Sopenharmony_ci		val |= mask;
40262306a36Sopenharmony_ci	else
40362306a36Sopenharmony_ci		val &= ~mask;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	phy_write_mmd(phydev, MDIO_MMD_AN, BCM_CL45VEN_EEE_ADV, (u32)val);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	return 0;
40862306a36Sopenharmony_ci}
40962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_set_eee);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ciint bcm_phy_downshift_get(struct phy_device *phydev, u8 *count)
41262306a36Sopenharmony_ci{
41362306a36Sopenharmony_ci	int val;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
41662306a36Sopenharmony_ci	if (val < 0)
41762306a36Sopenharmony_ci		return val;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	/* Check if wirespeed is enabled or not */
42062306a36Sopenharmony_ci	if (!(val & MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN)) {
42162306a36Sopenharmony_ci		*count = DOWNSHIFT_DEV_DISABLE;
42262306a36Sopenharmony_ci		return 0;
42362306a36Sopenharmony_ci	}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR2);
42662306a36Sopenharmony_ci	if (val < 0)
42762306a36Sopenharmony_ci		return val;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	/* Downgrade after one link attempt */
43062306a36Sopenharmony_ci	if (val & BCM54XX_SHD_SCR2_WSPD_RTRY_DIS) {
43162306a36Sopenharmony_ci		*count = 1;
43262306a36Sopenharmony_ci	} else {
43362306a36Sopenharmony_ci		/* Downgrade after configured retry count */
43462306a36Sopenharmony_ci		val >>= BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT;
43562306a36Sopenharmony_ci		val &= BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK;
43662306a36Sopenharmony_ci		*count = val + BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET;
43762306a36Sopenharmony_ci	}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	return 0;
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_downshift_get);
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ciint bcm_phy_downshift_set(struct phy_device *phydev, u8 count)
44462306a36Sopenharmony_ci{
44562306a36Sopenharmony_ci	int val = 0, ret = 0;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	/* Range check the number given */
44862306a36Sopenharmony_ci	if (count - BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET >
44962306a36Sopenharmony_ci	    BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK &&
45062306a36Sopenharmony_ci	    count != DOWNSHIFT_DEV_DEFAULT_COUNT) {
45162306a36Sopenharmony_ci		return -ERANGE;
45262306a36Sopenharmony_ci	}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
45562306a36Sopenharmony_ci	if (val < 0)
45662306a36Sopenharmony_ci		return val;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	/* Se the write enable bit */
45962306a36Sopenharmony_ci	val |= MII_BCM54XX_AUXCTL_MISC_WREN;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	if (count == DOWNSHIFT_DEV_DISABLE) {
46262306a36Sopenharmony_ci		val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN;
46362306a36Sopenharmony_ci		return bcm54xx_auxctl_write(phydev,
46462306a36Sopenharmony_ci					    MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
46562306a36Sopenharmony_ci					    val);
46662306a36Sopenharmony_ci	} else {
46762306a36Sopenharmony_ci		val |= MII_BCM54XX_AUXCTL_SHDWSEL_MISC_WIRESPEED_EN;
46862306a36Sopenharmony_ci		ret = bcm54xx_auxctl_write(phydev,
46962306a36Sopenharmony_ci					   MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
47062306a36Sopenharmony_ci					   val);
47162306a36Sopenharmony_ci		if (ret < 0)
47262306a36Sopenharmony_ci			return ret;
47362306a36Sopenharmony_ci	}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR2);
47662306a36Sopenharmony_ci	val &= ~(BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_MASK <<
47762306a36Sopenharmony_ci		 BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT |
47862306a36Sopenharmony_ci		 BCM54XX_SHD_SCR2_WSPD_RTRY_DIS);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	switch (count) {
48162306a36Sopenharmony_ci	case 1:
48262306a36Sopenharmony_ci		val |= BCM54XX_SHD_SCR2_WSPD_RTRY_DIS;
48362306a36Sopenharmony_ci		break;
48462306a36Sopenharmony_ci	case DOWNSHIFT_DEV_DEFAULT_COUNT:
48562306a36Sopenharmony_ci		val |= 1 << BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT;
48662306a36Sopenharmony_ci		break;
48762306a36Sopenharmony_ci	default:
48862306a36Sopenharmony_ci		val |= (count - BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_OFFSET) <<
48962306a36Sopenharmony_ci			BCM54XX_SHD_SCR2_WSPD_RTRY_LMT_SHIFT;
49062306a36Sopenharmony_ci		break;
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	return bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR2, val);
49462306a36Sopenharmony_ci}
49562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_downshift_set);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_cistruct bcm_phy_hw_stat {
49862306a36Sopenharmony_ci	const char *string;
49962306a36Sopenharmony_ci	int devad;
50062306a36Sopenharmony_ci	u16 reg;
50162306a36Sopenharmony_ci	u8 shift;
50262306a36Sopenharmony_ci	u8 bits;
50362306a36Sopenharmony_ci};
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci/* Counters freeze at either 0xffff or 0xff, better than nothing */
50662306a36Sopenharmony_cistatic const struct bcm_phy_hw_stat bcm_phy_hw_stats[] = {
50762306a36Sopenharmony_ci	{ "phy_receive_errors", -1, MII_BRCM_CORE_BASE12, 0, 16 },
50862306a36Sopenharmony_ci	{ "phy_serdes_ber_errors", -1, MII_BRCM_CORE_BASE13, 8, 8 },
50962306a36Sopenharmony_ci	{ "phy_false_carrier_sense_errors", -1, MII_BRCM_CORE_BASE13, 0, 8 },
51062306a36Sopenharmony_ci	{ "phy_local_rcvr_nok", -1, MII_BRCM_CORE_BASE14, 8, 8 },
51162306a36Sopenharmony_ci	{ "phy_remote_rcv_nok", -1, MII_BRCM_CORE_BASE14, 0, 8 },
51262306a36Sopenharmony_ci	{ "phy_lpi_count", MDIO_MMD_AN, BRCM_CL45VEN_EEE_LPI_CNT, 0, 16 },
51362306a36Sopenharmony_ci};
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ciint bcm_phy_get_sset_count(struct phy_device *phydev)
51662306a36Sopenharmony_ci{
51762306a36Sopenharmony_ci	return ARRAY_SIZE(bcm_phy_hw_stats);
51862306a36Sopenharmony_ci}
51962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_get_sset_count);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_civoid bcm_phy_get_strings(struct phy_device *phydev, u8 *data)
52262306a36Sopenharmony_ci{
52362306a36Sopenharmony_ci	unsigned int i;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++)
52662306a36Sopenharmony_ci		strscpy(data + i * ETH_GSTRING_LEN,
52762306a36Sopenharmony_ci			bcm_phy_hw_stats[i].string, ETH_GSTRING_LEN);
52862306a36Sopenharmony_ci}
52962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_get_strings);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci/* Caller is supposed to provide appropriate storage for the library code to
53262306a36Sopenharmony_ci * access the shadow copy
53362306a36Sopenharmony_ci */
53462306a36Sopenharmony_cistatic u64 bcm_phy_get_stat(struct phy_device *phydev, u64 *shadow,
53562306a36Sopenharmony_ci			    unsigned int i)
53662306a36Sopenharmony_ci{
53762306a36Sopenharmony_ci	struct bcm_phy_hw_stat stat = bcm_phy_hw_stats[i];
53862306a36Sopenharmony_ci	int val;
53962306a36Sopenharmony_ci	u64 ret;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	if (stat.devad < 0)
54262306a36Sopenharmony_ci		val = phy_read(phydev, stat.reg);
54362306a36Sopenharmony_ci	else
54462306a36Sopenharmony_ci		val = phy_read_mmd(phydev, stat.devad, stat.reg);
54562306a36Sopenharmony_ci	if (val < 0) {
54662306a36Sopenharmony_ci		ret = U64_MAX;
54762306a36Sopenharmony_ci	} else {
54862306a36Sopenharmony_ci		val >>= stat.shift;
54962306a36Sopenharmony_ci		val = val & ((1 << stat.bits) - 1);
55062306a36Sopenharmony_ci		shadow[i] += val;
55162306a36Sopenharmony_ci		ret = shadow[i];
55262306a36Sopenharmony_ci	}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	return ret;
55562306a36Sopenharmony_ci}
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_civoid bcm_phy_get_stats(struct phy_device *phydev, u64 *shadow,
55862306a36Sopenharmony_ci		       struct ethtool_stats *stats, u64 *data)
55962306a36Sopenharmony_ci{
56062306a36Sopenharmony_ci	unsigned int i;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++)
56362306a36Sopenharmony_ci		data[i] = bcm_phy_get_stat(phydev, shadow, i);
56462306a36Sopenharmony_ci}
56562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_get_stats);
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_civoid bcm_phy_r_rc_cal_reset(struct phy_device *phydev)
56862306a36Sopenharmony_ci{
56962306a36Sopenharmony_ci	/* Reset R_CAL/RC_CAL Engine */
57062306a36Sopenharmony_ci	bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0010);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	/* Disable Reset R_AL/RC_CAL Engine */
57362306a36Sopenharmony_ci	bcm_phy_write_exp_sel(phydev, 0x00b0, 0x0000);
57462306a36Sopenharmony_ci}
57562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_r_rc_cal_reset);
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ciint bcm_phy_28nm_a0b0_afe_config_init(struct phy_device *phydev)
57862306a36Sopenharmony_ci{
57962306a36Sopenharmony_ci	/* Increase VCO range to prevent unlocking problem of PLL at low
58062306a36Sopenharmony_ci	 * temp
58162306a36Sopenharmony_ci	 */
58262306a36Sopenharmony_ci	bcm_phy_write_misc(phydev, PLL_PLLCTRL_1, 0x0048);
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	/* Change Ki to 011 */
58562306a36Sopenharmony_ci	bcm_phy_write_misc(phydev, PLL_PLLCTRL_2, 0x021b);
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	/* Disable loading of TVCO buffer to bandgap, set bandgap trim
58862306a36Sopenharmony_ci	 * to 111
58962306a36Sopenharmony_ci	 */
59062306a36Sopenharmony_ci	bcm_phy_write_misc(phydev, PLL_PLLCTRL_4, 0x0e20);
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	/* Adjust bias current trim by -3 */
59362306a36Sopenharmony_ci	bcm_phy_write_misc(phydev, DSP_TAP10, 0x690b);
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	/* Switch to CORE_BASE1E */
59662306a36Sopenharmony_ci	phy_write(phydev, MII_BRCM_CORE_BASE1E, 0xd);
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	bcm_phy_r_rc_cal_reset(phydev);
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	/* write AFE_RXCONFIG_0 */
60162306a36Sopenharmony_ci	bcm_phy_write_misc(phydev, AFE_RXCONFIG_0, 0xeb19);
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	/* write AFE_RXCONFIG_1 */
60462306a36Sopenharmony_ci	bcm_phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9a3f);
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	/* write AFE_RX_LP_COUNTER */
60762306a36Sopenharmony_ci	bcm_phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0);
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	/* write AFE_HPF_TRIM_OTHERS */
61062306a36Sopenharmony_ci	bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x000b);
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	/* write AFTE_TX_CONFIG */
61362306a36Sopenharmony_ci	bcm_phy_write_misc(phydev, AFE_TX_CONFIG, 0x0800);
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	return 0;
61662306a36Sopenharmony_ci}
61762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_28nm_a0b0_afe_config_init);
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ciint bcm_phy_enable_jumbo(struct phy_device *phydev)
62062306a36Sopenharmony_ci{
62162306a36Sopenharmony_ci	int ret;
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	ret = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL);
62462306a36Sopenharmony_ci	if (ret < 0)
62562306a36Sopenharmony_ci		return ret;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	/* Enable extended length packet reception */
62862306a36Sopenharmony_ci	ret = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL,
62962306a36Sopenharmony_ci				   ret | MII_BCM54XX_AUXCTL_ACTL_EXT_PKT_LEN);
63062306a36Sopenharmony_ci	if (ret < 0)
63162306a36Sopenharmony_ci		return ret;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	/* Enable the elastic FIFO for raising the transmission limit from
63462306a36Sopenharmony_ci	 * 4.5KB to 10KB, at the expense of an additional 16 ns in propagation
63562306a36Sopenharmony_ci	 * latency.
63662306a36Sopenharmony_ci	 */
63762306a36Sopenharmony_ci	return phy_set_bits(phydev, MII_BCM54XX_ECR, MII_BCM54XX_ECR_FIFOE);
63862306a36Sopenharmony_ci}
63962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_enable_jumbo);
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_cistatic int __bcm_phy_enable_rdb_access(struct phy_device *phydev)
64262306a36Sopenharmony_ci{
64362306a36Sopenharmony_ci	return __bcm_phy_write_exp(phydev, BCM54XX_EXP_REG7E, 0);
64462306a36Sopenharmony_ci}
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_cistatic int __bcm_phy_enable_legacy_access(struct phy_device *phydev)
64762306a36Sopenharmony_ci{
64862306a36Sopenharmony_ci	return __bcm_phy_write_rdb(phydev, BCM54XX_RDB_REG0087,
64962306a36Sopenharmony_ci				   BCM54XX_ACCESS_MODE_LEGACY_EN);
65062306a36Sopenharmony_ci}
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_cistatic int _bcm_phy_cable_test_start(struct phy_device *phydev, bool is_rdb)
65362306a36Sopenharmony_ci{
65462306a36Sopenharmony_ci	u16 mask, set;
65562306a36Sopenharmony_ci	int ret;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	/* Auto-negotiation must be enabled for cable diagnostics to work, but
65862306a36Sopenharmony_ci	 * don't advertise any capabilities.
65962306a36Sopenharmony_ci	 */
66062306a36Sopenharmony_ci	phy_write(phydev, MII_BMCR, BMCR_ANENABLE);
66162306a36Sopenharmony_ci	phy_write(phydev, MII_ADVERTISE, ADVERTISE_CSMA);
66262306a36Sopenharmony_ci	phy_write(phydev, MII_CTRL1000, 0);
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	phy_lock_mdio_bus(phydev);
66562306a36Sopenharmony_ci	if (is_rdb) {
66662306a36Sopenharmony_ci		ret = __bcm_phy_enable_legacy_access(phydev);
66762306a36Sopenharmony_ci		if (ret)
66862306a36Sopenharmony_ci			goto out;
66962306a36Sopenharmony_ci	}
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	mask = BCM54XX_ECD_CTRL_CROSS_SHORT_DIS | BCM54XX_ECD_CTRL_UNIT_MASK;
67262306a36Sopenharmony_ci	set = BCM54XX_ECD_CTRL_RUN | BCM54XX_ECD_CTRL_BREAK_LINK |
67362306a36Sopenharmony_ci	      FIELD_PREP(BCM54XX_ECD_CTRL_UNIT_MASK,
67462306a36Sopenharmony_ci			 BCM54XX_ECD_CTRL_UNIT_CM);
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	ret = __bcm_phy_modify_exp(phydev, BCM54XX_EXP_ECD_CTRL, mask, set);
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ciout:
67962306a36Sopenharmony_ci	/* re-enable the RDB access even if there was an error */
68062306a36Sopenharmony_ci	if (is_rdb)
68162306a36Sopenharmony_ci		ret = __bcm_phy_enable_rdb_access(phydev) ? : ret;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	phy_unlock_mdio_bus(phydev);
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	return ret;
68662306a36Sopenharmony_ci}
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_cistatic int bcm_phy_cable_test_report_trans(int result)
68962306a36Sopenharmony_ci{
69062306a36Sopenharmony_ci	switch (result) {
69162306a36Sopenharmony_ci	case BCM54XX_ECD_FAULT_TYPE_OK:
69262306a36Sopenharmony_ci		return ETHTOOL_A_CABLE_RESULT_CODE_OK;
69362306a36Sopenharmony_ci	case BCM54XX_ECD_FAULT_TYPE_OPEN:
69462306a36Sopenharmony_ci		return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
69562306a36Sopenharmony_ci	case BCM54XX_ECD_FAULT_TYPE_SAME_SHORT:
69662306a36Sopenharmony_ci		return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
69762306a36Sopenharmony_ci	case BCM54XX_ECD_FAULT_TYPE_CROSS_SHORT:
69862306a36Sopenharmony_ci		return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
69962306a36Sopenharmony_ci	case BCM54XX_ECD_FAULT_TYPE_INVALID:
70062306a36Sopenharmony_ci	case BCM54XX_ECD_FAULT_TYPE_BUSY:
70162306a36Sopenharmony_ci	default:
70262306a36Sopenharmony_ci		return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
70362306a36Sopenharmony_ci	}
70462306a36Sopenharmony_ci}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_cistatic bool bcm_phy_distance_valid(int result)
70762306a36Sopenharmony_ci{
70862306a36Sopenharmony_ci	switch (result) {
70962306a36Sopenharmony_ci	case BCM54XX_ECD_FAULT_TYPE_OPEN:
71062306a36Sopenharmony_ci	case BCM54XX_ECD_FAULT_TYPE_SAME_SHORT:
71162306a36Sopenharmony_ci	case BCM54XX_ECD_FAULT_TYPE_CROSS_SHORT:
71262306a36Sopenharmony_ci		return true;
71362306a36Sopenharmony_ci	}
71462306a36Sopenharmony_ci	return false;
71562306a36Sopenharmony_ci}
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_cistatic int bcm_phy_report_length(struct phy_device *phydev, int pair)
71862306a36Sopenharmony_ci{
71962306a36Sopenharmony_ci	int val;
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	val = __bcm_phy_read_exp(phydev,
72262306a36Sopenharmony_ci				 BCM54XX_EXP_ECD_PAIR_A_LENGTH_RESULTS + pair);
72362306a36Sopenharmony_ci	if (val < 0)
72462306a36Sopenharmony_ci		return val;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	if (val == BCM54XX_ECD_LENGTH_RESULTS_INVALID)
72762306a36Sopenharmony_ci		return 0;
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	ethnl_cable_test_fault_length(phydev, pair, val);
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	return 0;
73262306a36Sopenharmony_ci}
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_cistatic int _bcm_phy_cable_test_get_status(struct phy_device *phydev,
73562306a36Sopenharmony_ci					  bool *finished, bool is_rdb)
73662306a36Sopenharmony_ci{
73762306a36Sopenharmony_ci	int pair_a, pair_b, pair_c, pair_d, ret;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	*finished = false;
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	phy_lock_mdio_bus(phydev);
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	if (is_rdb) {
74462306a36Sopenharmony_ci		ret = __bcm_phy_enable_legacy_access(phydev);
74562306a36Sopenharmony_ci		if (ret)
74662306a36Sopenharmony_ci			goto out;
74762306a36Sopenharmony_ci	}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	ret = __bcm_phy_read_exp(phydev, BCM54XX_EXP_ECD_CTRL);
75062306a36Sopenharmony_ci	if (ret < 0)
75162306a36Sopenharmony_ci		goto out;
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	if (ret & BCM54XX_ECD_CTRL_IN_PROGRESS) {
75462306a36Sopenharmony_ci		ret = 0;
75562306a36Sopenharmony_ci		goto out;
75662306a36Sopenharmony_ci	}
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	ret = __bcm_phy_read_exp(phydev, BCM54XX_EXP_ECD_FAULT_TYPE);
75962306a36Sopenharmony_ci	if (ret < 0)
76062306a36Sopenharmony_ci		goto out;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	pair_a = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_A_MASK, ret);
76362306a36Sopenharmony_ci	pair_b = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_B_MASK, ret);
76462306a36Sopenharmony_ci	pair_c = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_C_MASK, ret);
76562306a36Sopenharmony_ci	pair_d = FIELD_GET(BCM54XX_ECD_FAULT_TYPE_PAIR_D_MASK, ret);
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A,
76862306a36Sopenharmony_ci				bcm_phy_cable_test_report_trans(pair_a));
76962306a36Sopenharmony_ci	ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_B,
77062306a36Sopenharmony_ci				bcm_phy_cable_test_report_trans(pair_b));
77162306a36Sopenharmony_ci	ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_C,
77262306a36Sopenharmony_ci				bcm_phy_cable_test_report_trans(pair_c));
77362306a36Sopenharmony_ci	ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_D,
77462306a36Sopenharmony_ci				bcm_phy_cable_test_report_trans(pair_d));
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	if (bcm_phy_distance_valid(pair_a))
77762306a36Sopenharmony_ci		bcm_phy_report_length(phydev, 0);
77862306a36Sopenharmony_ci	if (bcm_phy_distance_valid(pair_b))
77962306a36Sopenharmony_ci		bcm_phy_report_length(phydev, 1);
78062306a36Sopenharmony_ci	if (bcm_phy_distance_valid(pair_c))
78162306a36Sopenharmony_ci		bcm_phy_report_length(phydev, 2);
78262306a36Sopenharmony_ci	if (bcm_phy_distance_valid(pair_d))
78362306a36Sopenharmony_ci		bcm_phy_report_length(phydev, 3);
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	ret = 0;
78662306a36Sopenharmony_ci	*finished = true;
78762306a36Sopenharmony_ciout:
78862306a36Sopenharmony_ci	/* re-enable the RDB access even if there was an error */
78962306a36Sopenharmony_ci	if (is_rdb)
79062306a36Sopenharmony_ci		ret = __bcm_phy_enable_rdb_access(phydev) ? : ret;
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	phy_unlock_mdio_bus(phydev);
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	return ret;
79562306a36Sopenharmony_ci}
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ciint bcm_phy_cable_test_start(struct phy_device *phydev)
79862306a36Sopenharmony_ci{
79962306a36Sopenharmony_ci	return _bcm_phy_cable_test_start(phydev, false);
80062306a36Sopenharmony_ci}
80162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_cable_test_start);
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ciint bcm_phy_cable_test_get_status(struct phy_device *phydev, bool *finished)
80462306a36Sopenharmony_ci{
80562306a36Sopenharmony_ci	return _bcm_phy_cable_test_get_status(phydev, finished, false);
80662306a36Sopenharmony_ci}
80762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_cable_test_get_status);
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci/* We assume that all PHYs which support RDB access can be switched to legacy
81062306a36Sopenharmony_ci * mode. If, in the future, this is not true anymore, we have to re-implement
81162306a36Sopenharmony_ci * this with RDB access.
81262306a36Sopenharmony_ci */
81362306a36Sopenharmony_ciint bcm_phy_cable_test_start_rdb(struct phy_device *phydev)
81462306a36Sopenharmony_ci{
81562306a36Sopenharmony_ci	return _bcm_phy_cable_test_start(phydev, true);
81662306a36Sopenharmony_ci}
81762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_cable_test_start_rdb);
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ciint bcm_phy_cable_test_get_status_rdb(struct phy_device *phydev,
82062306a36Sopenharmony_ci				      bool *finished)
82162306a36Sopenharmony_ci{
82262306a36Sopenharmony_ci	return _bcm_phy_cable_test_get_status(phydev, finished, true);
82362306a36Sopenharmony_ci}
82462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_cable_test_get_status_rdb);
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci#define BCM54XX_WOL_SUPPORTED_MASK	(WAKE_UCAST | \
82762306a36Sopenharmony_ci					 WAKE_MCAST | \
82862306a36Sopenharmony_ci					 WAKE_BCAST | \
82962306a36Sopenharmony_ci					 WAKE_MAGIC | \
83062306a36Sopenharmony_ci					 WAKE_MAGICSECURE)
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ciint bcm_phy_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
83362306a36Sopenharmony_ci{
83462306a36Sopenharmony_ci	struct net_device *ndev = phydev->attached_dev;
83562306a36Sopenharmony_ci	u8 da[ETH_ALEN], mask[ETH_ALEN];
83662306a36Sopenharmony_ci	unsigned int i;
83762306a36Sopenharmony_ci	u16 ctl;
83862306a36Sopenharmony_ci	int ret;
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	/* Allow a MAC driver to play through its own Wake-on-LAN
84162306a36Sopenharmony_ci	 * implementation
84262306a36Sopenharmony_ci	 */
84362306a36Sopenharmony_ci	if (wol->wolopts & ~BCM54XX_WOL_SUPPORTED_MASK)
84462306a36Sopenharmony_ci		return -EOPNOTSUPP;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	/* The PHY supports passwords of 4, 6 and 8 bytes in size, but Linux's
84762306a36Sopenharmony_ci	 * ethtool only supports 6, for now.
84862306a36Sopenharmony_ci	 */
84962306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(wol->sopass) != ETH_ALEN);
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	/* Clear previous interrupts */
85262306a36Sopenharmony_ci	ret = bcm_phy_read_exp(phydev, BCM54XX_WOL_INT_STATUS);
85362306a36Sopenharmony_ci	if (ret < 0)
85462306a36Sopenharmony_ci		return ret;
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	ret = bcm_phy_read_exp(phydev, BCM54XX_WOL_MAIN_CTL);
85762306a36Sopenharmony_ci	if (ret < 0)
85862306a36Sopenharmony_ci		return ret;
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	ctl = ret;
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	if (!wol->wolopts) {
86362306a36Sopenharmony_ci		if (phy_interrupt_is_valid(phydev))
86462306a36Sopenharmony_ci			disable_irq_wake(phydev->irq);
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci		/* Leave all interrupts disabled */
86762306a36Sopenharmony_ci		ret = bcm_phy_write_exp(phydev, BCM54XX_WOL_INT_MASK,
86862306a36Sopenharmony_ci					BCM54XX_WOL_ALL_INTRS);
86962306a36Sopenharmony_ci		if (ret < 0)
87062306a36Sopenharmony_ci			return ret;
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci		/* Disable the global Wake-on-LAN enable bit */
87362306a36Sopenharmony_ci		ctl &= ~BCM54XX_WOL_EN;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci		return bcm_phy_write_exp(phydev, BCM54XX_WOL_MAIN_CTL, ctl);
87662306a36Sopenharmony_ci	}
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	/* Clear the previously configured mode and mask mode for Wake-on-LAN */
87962306a36Sopenharmony_ci	ctl &= ~(BCM54XX_WOL_MODE_MASK << BCM54XX_WOL_MODE_SHIFT);
88062306a36Sopenharmony_ci	ctl &= ~(BCM54XX_WOL_MASK_MODE_MASK << BCM54XX_WOL_MASK_MODE_SHIFT);
88162306a36Sopenharmony_ci	ctl &= ~BCM54XX_WOL_DIR_PKT_EN;
88262306a36Sopenharmony_ci	ctl &= ~(BCM54XX_WOL_SECKEY_OPT_MASK << BCM54XX_WOL_SECKEY_OPT_SHIFT);
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	/* When using WAKE_MAGIC, we program the magic pattern filter to match
88562306a36Sopenharmony_ci	 * the device's MAC address and we accept any MAC DA in the Ethernet
88662306a36Sopenharmony_ci	 * frame.
88762306a36Sopenharmony_ci	 *
88862306a36Sopenharmony_ci	 * When using WAKE_UCAST, WAKE_BCAST or WAKE_MCAST, we program the
88962306a36Sopenharmony_ci	 * following:
89062306a36Sopenharmony_ci	 * - WAKE_UCAST -> MAC DA is the device's MAC with a perfect match
89162306a36Sopenharmony_ci	 * - WAKE_MCAST -> MAC DA is X1:XX:XX:XX:XX:XX where XX is don't care
89262306a36Sopenharmony_ci	 * - WAKE_BCAST -> MAC DA is FF:FF:FF:FF:FF:FF with a perfect match
89362306a36Sopenharmony_ci	 *
89462306a36Sopenharmony_ci	 * Note that the Broadcast MAC DA is inherently going to match the
89562306a36Sopenharmony_ci	 * multicast pattern being matched.
89662306a36Sopenharmony_ci	 */
89762306a36Sopenharmony_ci	memset(mask, 0, sizeof(mask));
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	if (wol->wolopts & WAKE_MCAST) {
90062306a36Sopenharmony_ci		memset(da, 0, sizeof(da));
90162306a36Sopenharmony_ci		memset(mask, 0xff, sizeof(mask));
90262306a36Sopenharmony_ci		da[0] = 0x01;
90362306a36Sopenharmony_ci		mask[0] = ~da[0];
90462306a36Sopenharmony_ci	} else {
90562306a36Sopenharmony_ci		if (wol->wolopts & WAKE_UCAST) {
90662306a36Sopenharmony_ci			ether_addr_copy(da, ndev->dev_addr);
90762306a36Sopenharmony_ci		} else if (wol->wolopts & WAKE_BCAST) {
90862306a36Sopenharmony_ci			eth_broadcast_addr(da);
90962306a36Sopenharmony_ci		} else if (wol->wolopts & WAKE_MAGICSECURE) {
91062306a36Sopenharmony_ci			ether_addr_copy(da, wol->sopass);
91162306a36Sopenharmony_ci		} else if (wol->wolopts & WAKE_MAGIC) {
91262306a36Sopenharmony_ci			memset(da, 0, sizeof(da));
91362306a36Sopenharmony_ci			memset(mask, 0xff, sizeof(mask));
91462306a36Sopenharmony_ci		}
91562306a36Sopenharmony_ci	}
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	for (i = 0; i < ETH_ALEN / 2; i++) {
91862306a36Sopenharmony_ci		if (wol->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) {
91962306a36Sopenharmony_ci			ret = bcm_phy_write_exp(phydev,
92062306a36Sopenharmony_ci						BCM54XX_WOL_MPD_DATA1(2 - i),
92162306a36Sopenharmony_ci						ndev->dev_addr[i * 2] << 8 |
92262306a36Sopenharmony_ci						ndev->dev_addr[i * 2 + 1]);
92362306a36Sopenharmony_ci			if (ret < 0)
92462306a36Sopenharmony_ci				return ret;
92562306a36Sopenharmony_ci		}
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci		ret = bcm_phy_write_exp(phydev, BCM54XX_WOL_MPD_DATA2(2 - i),
92862306a36Sopenharmony_ci					da[i * 2] << 8 | da[i * 2 + 1]);
92962306a36Sopenharmony_ci		if (ret < 0)
93062306a36Sopenharmony_ci			return ret;
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci		ret = bcm_phy_write_exp(phydev, BCM54XX_WOL_MASK(2 - i),
93362306a36Sopenharmony_ci					mask[i * 2] << 8 | mask[i * 2 + 1]);
93462306a36Sopenharmony_ci		if (ret)
93562306a36Sopenharmony_ci			return ret;
93662306a36Sopenharmony_ci	}
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	if (wol->wolopts & WAKE_MAGICSECURE) {
93962306a36Sopenharmony_ci		ctl |= BCM54XX_WOL_SECKEY_OPT_6B <<
94062306a36Sopenharmony_ci		       BCM54XX_WOL_SECKEY_OPT_SHIFT;
94162306a36Sopenharmony_ci		ctl |= BCM54XX_WOL_MODE_SINGLE_MPDSEC << BCM54XX_WOL_MODE_SHIFT;
94262306a36Sopenharmony_ci		ctl |= BCM54XX_WOL_MASK_MODE_DA_FF <<
94362306a36Sopenharmony_ci		       BCM54XX_WOL_MASK_MODE_SHIFT;
94462306a36Sopenharmony_ci	} else {
94562306a36Sopenharmony_ci		if (wol->wolopts & WAKE_MAGIC)
94662306a36Sopenharmony_ci			ctl |= BCM54XX_WOL_MODE_SINGLE_MPD;
94762306a36Sopenharmony_ci		else
94862306a36Sopenharmony_ci			ctl |= BCM54XX_WOL_DIR_PKT_EN;
94962306a36Sopenharmony_ci		ctl |= BCM54XX_WOL_MASK_MODE_DA_ONLY <<
95062306a36Sopenharmony_ci		       BCM54XX_WOL_MASK_MODE_SHIFT;
95162306a36Sopenharmony_ci	}
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	/* Globally enable Wake-on-LAN */
95462306a36Sopenharmony_ci	ctl |= BCM54XX_WOL_EN | BCM54XX_WOL_CRC_CHK;
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	ret = bcm_phy_write_exp(phydev, BCM54XX_WOL_MAIN_CTL, ctl);
95762306a36Sopenharmony_ci	if (ret < 0)
95862306a36Sopenharmony_ci		return ret;
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	/* Enable WOL interrupt on LED4 */
96162306a36Sopenharmony_ci	ret = bcm_phy_read_exp(phydev, BCM54XX_TOP_MISC_LED_CTL);
96262306a36Sopenharmony_ci	if (ret < 0)
96362306a36Sopenharmony_ci		return ret;
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	ret |= BCM54XX_LED4_SEL_INTR;
96662306a36Sopenharmony_ci	ret = bcm_phy_write_exp(phydev, BCM54XX_TOP_MISC_LED_CTL, ret);
96762306a36Sopenharmony_ci	if (ret < 0)
96862306a36Sopenharmony_ci		return ret;
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	/* Enable all Wake-on-LAN interrupt sources */
97162306a36Sopenharmony_ci	ret = bcm_phy_write_exp(phydev, BCM54XX_WOL_INT_MASK, 0);
97262306a36Sopenharmony_ci	if (ret < 0)
97362306a36Sopenharmony_ci		return ret;
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	if (phy_interrupt_is_valid(phydev))
97662306a36Sopenharmony_ci		enable_irq_wake(phydev->irq);
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	return 0;
97962306a36Sopenharmony_ci}
98062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_set_wol);
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_civoid bcm_phy_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
98362306a36Sopenharmony_ci{
98462306a36Sopenharmony_ci	struct net_device *ndev = phydev->attached_dev;
98562306a36Sopenharmony_ci	u8 da[ETH_ALEN];
98662306a36Sopenharmony_ci	unsigned int i;
98762306a36Sopenharmony_ci	int ret;
98862306a36Sopenharmony_ci	u16 ctl;
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	wol->supported = BCM54XX_WOL_SUPPORTED_MASK;
99162306a36Sopenharmony_ci	wol->wolopts = 0;
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	ret = bcm_phy_read_exp(phydev, BCM54XX_WOL_MAIN_CTL);
99462306a36Sopenharmony_ci	if (ret < 0)
99562306a36Sopenharmony_ci		return;
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	ctl = ret;
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	if (!(ctl & BCM54XX_WOL_EN))
100062306a36Sopenharmony_ci		return;
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	for (i = 0; i < sizeof(da) / 2; i++) {
100362306a36Sopenharmony_ci		ret = bcm_phy_read_exp(phydev,
100462306a36Sopenharmony_ci				       BCM54XX_WOL_MPD_DATA2(2 - i));
100562306a36Sopenharmony_ci		if (ret < 0)
100662306a36Sopenharmony_ci			return;
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci		da[i * 2] = ret >> 8;
100962306a36Sopenharmony_ci		da[i * 2 + 1] = ret & 0xff;
101062306a36Sopenharmony_ci	}
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	if (ctl & BCM54XX_WOL_DIR_PKT_EN) {
101362306a36Sopenharmony_ci		if (is_broadcast_ether_addr(da))
101462306a36Sopenharmony_ci			wol->wolopts |= WAKE_BCAST;
101562306a36Sopenharmony_ci		else if (is_multicast_ether_addr(da))
101662306a36Sopenharmony_ci			wol->wolopts |= WAKE_MCAST;
101762306a36Sopenharmony_ci		else if (ether_addr_equal(da, ndev->dev_addr))
101862306a36Sopenharmony_ci			wol->wolopts |= WAKE_UCAST;
101962306a36Sopenharmony_ci	} else {
102062306a36Sopenharmony_ci		ctl = (ctl >> BCM54XX_WOL_MODE_SHIFT) & BCM54XX_WOL_MODE_MASK;
102162306a36Sopenharmony_ci		switch (ctl) {
102262306a36Sopenharmony_ci		case BCM54XX_WOL_MODE_SINGLE_MPD:
102362306a36Sopenharmony_ci			wol->wolopts |= WAKE_MAGIC;
102462306a36Sopenharmony_ci			break;
102562306a36Sopenharmony_ci		case BCM54XX_WOL_MODE_SINGLE_MPDSEC:
102662306a36Sopenharmony_ci			wol->wolopts |= WAKE_MAGICSECURE;
102762306a36Sopenharmony_ci			memcpy(wol->sopass, da, sizeof(da));
102862306a36Sopenharmony_ci			break;
102962306a36Sopenharmony_ci		default:
103062306a36Sopenharmony_ci			break;
103162306a36Sopenharmony_ci		}
103262306a36Sopenharmony_ci	}
103362306a36Sopenharmony_ci}
103462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_get_wol);
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ciirqreturn_t bcm_phy_wol_isr(int irq, void *dev_id)
103762306a36Sopenharmony_ci{
103862306a36Sopenharmony_ci	return IRQ_HANDLED;
103962306a36Sopenharmony_ci}
104062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_wol_isr);
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ciint bcm_phy_led_brightness_set(struct phy_device *phydev,
104362306a36Sopenharmony_ci			       u8 index, enum led_brightness value)
104462306a36Sopenharmony_ci{
104562306a36Sopenharmony_ci	u8 led_num;
104662306a36Sopenharmony_ci	int ret;
104762306a36Sopenharmony_ci	u16 reg;
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	if (index >= 4)
105062306a36Sopenharmony_ci		return -EINVAL;
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	/* Two LEDS per register */
105362306a36Sopenharmony_ci	led_num = index % 2;
105462306a36Sopenharmony_ci	reg = index >= 2 ? BCM54XX_SHD_LEDS2 : BCM54XX_SHD_LEDS1;
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	ret = bcm_phy_read_shadow(phydev, reg);
105762306a36Sopenharmony_ci	if (ret < 0)
105862306a36Sopenharmony_ci		return ret;
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	ret &= ~(BCM_LED_SRC_MASK << BCM54XX_SHD_LEDS_SHIFT(led_num));
106162306a36Sopenharmony_ci	if (value == LED_OFF)
106262306a36Sopenharmony_ci		ret |= BCM_LED_SRC_OFF << BCM54XX_SHD_LEDS_SHIFT(led_num);
106362306a36Sopenharmony_ci	else
106462306a36Sopenharmony_ci		ret |= BCM_LED_SRC_ON << BCM54XX_SHD_LEDS_SHIFT(led_num);
106562306a36Sopenharmony_ci	return bcm_phy_write_shadow(phydev, reg, ret);
106662306a36Sopenharmony_ci}
106762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(bcm_phy_led_brightness_set);
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ciMODULE_DESCRIPTION("Broadcom PHY Library");
107062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
107162306a36Sopenharmony_ciMODULE_AUTHOR("Broadcom Corporation");
1072