Commit 7eef636e authored by David S. Miller's avatar David S. Miller

Merge branch 'broadcom-phy-wol'

Florian Fainelli says:

====================
Support for Wake-on-LAN for Broadcom PHYs

This patch series adds support for Wake-on-LAN to the Broadcom PHY
driver. Specifically the BCM54210E/B50212E are capable of supporting
Wake-on-LAN using an external pin typically wired up to a system's GPIO.

These PHY operate a programmable Ethernet MAC destination address
comparator which will fire up an interrupt whenever a match is received.
Because of that, it was necessary to introduce patch #1 which allows the
PHY driver's ->suspend() routine to be called unconditionally. This is
necessary in our case because we need a hook point into the device
suspend/resume flow to enable the wake-up interrupt as late as possible.

Patch #2 adds support for the Broadcom PHY library and driver for
Wake-on-LAN proper with the WAKE_UCAST, WAKE_MCAST, WAKE_BCAST,
WAKE_MAGIC and WAKE_MAGICSECURE. Note that WAKE_FILTER is supportable,
however this will require further discussions and be submitted as a RFC
series later on.

Patch #3 updates the GENET driver to defer to the PHY for Wake-on-LAN if
the PHY supports it, thus allowing the MAC to be powered down to
conserve power.

Changes in v3:

- collected Reviewed-by tags
- explicitly use return 0 in bcm54xx_phy_probe() (Paolo)

Changes in v2:

- introduce PHY_ALWAYS_CALL_SUSPEND and only have the Broadcom PHY
  driver set this flag to minimize changes to the suspend flow to only
  drivers that need it

- corrected possibly uninitialized variable in bcm54xx_set_wakeup_irq
  (Simon)
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents ba79e9a7 7e400ff3
...@@ -42,6 +42,12 @@ void bcmgenet_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) ...@@ -42,6 +42,12 @@ void bcmgenet_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
struct bcmgenet_priv *priv = netdev_priv(dev); struct bcmgenet_priv *priv = netdev_priv(dev);
struct device *kdev = &priv->pdev->dev; struct device *kdev = &priv->pdev->dev;
if (dev->phydev) {
phy_ethtool_get_wol(dev->phydev, wol);
if (wol->supported)
return;
}
if (!device_can_wakeup(kdev)) { if (!device_can_wakeup(kdev)) {
wol->supported = 0; wol->supported = 0;
wol->wolopts = 0; wol->wolopts = 0;
...@@ -63,6 +69,14 @@ int bcmgenet_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) ...@@ -63,6 +69,14 @@ int bcmgenet_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{ {
struct bcmgenet_priv *priv = netdev_priv(dev); struct bcmgenet_priv *priv = netdev_priv(dev);
struct device *kdev = &priv->pdev->dev; struct device *kdev = &priv->pdev->dev;
int ret;
/* Try Wake-on-LAN from the PHY first */
if (dev->phydev) {
ret = phy_ethtool_set_wol(dev->phydev, wol);
if (ret != -EOPNOTSUPP)
return ret;
}
if (!device_can_wakeup(kdev)) if (!device_can_wakeup(kdev))
return -ENOTSUPP; return -ENOTSUPP;
......
...@@ -6,12 +6,14 @@ ...@@ -6,12 +6,14 @@
#include "bcm-phy-lib.h" #include "bcm-phy-lib.h"
#include <linux/bitfield.h> #include <linux/bitfield.h>
#include <linux/brcmphy.h> #include <linux/brcmphy.h>
#include <linux/etherdevice.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/mdio.h> #include <linux/mdio.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/phy.h> #include <linux/phy.h>
#include <linux/ethtool.h> #include <linux/ethtool.h>
#include <linux/ethtool_netlink.h> #include <linux/ethtool_netlink.h>
#include <linux/netdevice.h>
#define MII_BCM_CHANNEL_WIDTH 0x2000 #define MII_BCM_CHANNEL_WIDTH 0x2000
#define BCM_CL45VEN_EEE_ADV 0x3c #define BCM_CL45VEN_EEE_ADV 0x3c
...@@ -816,6 +818,216 @@ int bcm_phy_cable_test_get_status_rdb(struct phy_device *phydev, ...@@ -816,6 +818,216 @@ int bcm_phy_cable_test_get_status_rdb(struct phy_device *phydev,
} }
EXPORT_SYMBOL_GPL(bcm_phy_cable_test_get_status_rdb); EXPORT_SYMBOL_GPL(bcm_phy_cable_test_get_status_rdb);
#define BCM54XX_WOL_SUPPORTED_MASK (WAKE_UCAST | \
WAKE_MCAST | \
WAKE_BCAST | \
WAKE_MAGIC | \
WAKE_MAGICSECURE)
int bcm_phy_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
{
struct net_device *ndev = phydev->attached_dev;
u8 da[ETH_ALEN], mask[ETH_ALEN];
unsigned int i;
u16 ctl;
int ret;
/* Allow a MAC driver to play through its own Wake-on-LAN
* implementation
*/
if (wol->wolopts & ~BCM54XX_WOL_SUPPORTED_MASK)
return -EOPNOTSUPP;
/* The PHY supports passwords of 4, 6 and 8 bytes in size, but Linux's
* ethtool only supports 6, for now.
*/
BUILD_BUG_ON(sizeof(wol->sopass) != ETH_ALEN);
/* Clear previous interrupts */
ret = bcm_phy_read_exp(phydev, BCM54XX_WOL_INT_STATUS);
if (ret < 0)
return ret;
ret = bcm_phy_read_exp(phydev, BCM54XX_WOL_MAIN_CTL);
if (ret < 0)
return ret;
ctl = ret;
if (!wol->wolopts) {
if (phy_interrupt_is_valid(phydev))
disable_irq_wake(phydev->irq);
/* Leave all interrupts disabled */
ret = bcm_phy_write_exp(phydev, BCM54XX_WOL_INT_MASK,
BCM54XX_WOL_ALL_INTRS);
if (ret < 0)
return ret;
/* Disable the global Wake-on-LAN enable bit */
ctl &= ~BCM54XX_WOL_EN;
return bcm_phy_write_exp(phydev, BCM54XX_WOL_MAIN_CTL, ctl);
}
/* Clear the previously configured mode and mask mode for Wake-on-LAN */
ctl &= ~(BCM54XX_WOL_MODE_MASK << BCM54XX_WOL_MODE_SHIFT);
ctl &= ~(BCM54XX_WOL_MASK_MODE_MASK << BCM54XX_WOL_MASK_MODE_SHIFT);
ctl &= ~BCM54XX_WOL_DIR_PKT_EN;
ctl &= ~(BCM54XX_WOL_SECKEY_OPT_MASK << BCM54XX_WOL_SECKEY_OPT_SHIFT);
/* When using WAKE_MAGIC, we program the magic pattern filter to match
* the device's MAC address and we accept any MAC DA in the Ethernet
* frame.
*
* When using WAKE_UCAST, WAKE_BCAST or WAKE_MCAST, we program the
* following:
* - WAKE_UCAST -> MAC DA is the device's MAC with a perfect match
* - WAKE_MCAST -> MAC DA is X1:XX:XX:XX:XX:XX where XX is don't care
* - WAKE_BCAST -> MAC DA is FF:FF:FF:FF:FF:FF with a perfect match
*
* Note that the Broadcast MAC DA is inherently going to match the
* multicast pattern being matched.
*/
memset(mask, 0, sizeof(mask));
if (wol->wolopts & WAKE_MCAST) {
memset(da, 0, sizeof(da));
memset(mask, 0xff, sizeof(mask));
da[0] = 0x01;
mask[0] = ~da[0];
} else {
if (wol->wolopts & WAKE_UCAST) {
ether_addr_copy(da, ndev->dev_addr);
} else if (wol->wolopts & WAKE_BCAST) {
eth_broadcast_addr(da);
} else if (wol->wolopts & WAKE_MAGICSECURE) {
ether_addr_copy(da, wol->sopass);
} else if (wol->wolopts & WAKE_MAGIC) {
memset(da, 0, sizeof(da));
memset(mask, 0xff, sizeof(mask));
}
}
for (i = 0; i < ETH_ALEN / 2; i++) {
if (wol->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) {
ret = bcm_phy_write_exp(phydev,
BCM54XX_WOL_MPD_DATA1(2 - i),
ndev->dev_addr[i * 2] << 8 |
ndev->dev_addr[i * 2 + 1]);
if (ret < 0)
return ret;
}
ret = bcm_phy_write_exp(phydev, BCM54XX_WOL_MPD_DATA2(2 - i),
da[i * 2] << 8 | da[i * 2 + 1]);
if (ret < 0)
return ret;
ret = bcm_phy_write_exp(phydev, BCM54XX_WOL_MASK(2 - i),
mask[i * 2] << 8 | mask[i * 2 + 1]);
if (ret)
return ret;
}
if (wol->wolopts & WAKE_MAGICSECURE) {
ctl |= BCM54XX_WOL_SECKEY_OPT_6B <<
BCM54XX_WOL_SECKEY_OPT_SHIFT;
ctl |= BCM54XX_WOL_MODE_SINGLE_MPDSEC << BCM54XX_WOL_MODE_SHIFT;
ctl |= BCM54XX_WOL_MASK_MODE_DA_FF <<
BCM54XX_WOL_MASK_MODE_SHIFT;
} else {
if (wol->wolopts & WAKE_MAGIC)
ctl |= BCM54XX_WOL_MODE_SINGLE_MPD;
else
ctl |= BCM54XX_WOL_DIR_PKT_EN;
ctl |= BCM54XX_WOL_MASK_MODE_DA_ONLY <<
BCM54XX_WOL_MASK_MODE_SHIFT;
}
/* Globally enable Wake-on-LAN */
ctl |= BCM54XX_WOL_EN | BCM54XX_WOL_CRC_CHK;
ret = bcm_phy_write_exp(phydev, BCM54XX_WOL_MAIN_CTL, ctl);
if (ret < 0)
return ret;
/* Enable WOL interrupt on LED4 */
ret = bcm_phy_read_exp(phydev, BCM54XX_TOP_MISC_LED_CTL);
if (ret < 0)
return ret;
ret |= BCM54XX_LED4_SEL_INTR;
ret = bcm_phy_write_exp(phydev, BCM54XX_TOP_MISC_LED_CTL, ret);
if (ret < 0)
return ret;
/* Enable all Wake-on-LAN interrupt sources */
ret = bcm_phy_write_exp(phydev, BCM54XX_WOL_INT_MASK, 0);
if (ret < 0)
return ret;
if (phy_interrupt_is_valid(phydev))
enable_irq_wake(phydev->irq);
return 0;
}
EXPORT_SYMBOL_GPL(bcm_phy_set_wol);
void bcm_phy_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
{
struct net_device *ndev = phydev->attached_dev;
u8 da[ETH_ALEN];
unsigned int i;
int ret;
u16 ctl;
wol->supported = BCM54XX_WOL_SUPPORTED_MASK;
wol->wolopts = 0;
ret = bcm_phy_read_exp(phydev, BCM54XX_WOL_MAIN_CTL);
if (ret < 0)
return;
ctl = ret;
if (!(ctl & BCM54XX_WOL_EN))
return;
for (i = 0; i < sizeof(da) / 2; i++) {
ret = bcm_phy_read_exp(phydev,
BCM54XX_WOL_MPD_DATA2(2 - i));
if (ret < 0)
return;
da[i * 2] = ret >> 8;
da[i * 2 + 1] = ret & 0xff;
}
if (ctl & BCM54XX_WOL_DIR_PKT_EN) {
if (is_broadcast_ether_addr(da))
wol->wolopts |= WAKE_BCAST;
else if (is_multicast_ether_addr(da))
wol->wolopts |= WAKE_MCAST;
else if (ether_addr_equal(da, ndev->dev_addr))
wol->wolopts |= WAKE_UCAST;
} else {
ctl = (ctl >> BCM54XX_WOL_MODE_SHIFT) & BCM54XX_WOL_MODE_MASK;
switch (ctl) {
case BCM54XX_WOL_MODE_SINGLE_MPD:
wol->wolopts |= WAKE_MAGIC;
break;
case BCM54XX_WOL_MODE_SINGLE_MPDSEC:
wol->wolopts |= WAKE_MAGICSECURE;
memcpy(wol->sopass, da, sizeof(da));
break;
default:
break;
}
}
}
EXPORT_SYMBOL_GPL(bcm_phy_get_wol);
MODULE_DESCRIPTION("Broadcom PHY Library"); MODULE_DESCRIPTION("Broadcom PHY Library");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Broadcom Corporation"); MODULE_AUTHOR("Broadcom Corporation");
...@@ -9,6 +9,8 @@ ...@@ -9,6 +9,8 @@
#include <linux/brcmphy.h> #include <linux/brcmphy.h>
#include <linux/phy.h> #include <linux/phy.h>
struct ethtool_wolinfo;
/* 28nm only register definitions */ /* 28nm only register definitions */
#define MISC_ADDR(base, channel) base, channel #define MISC_ADDR(base, channel) base, channel
...@@ -111,4 +113,7 @@ static inline void bcm_ptp_stop(struct bcm_ptp_private *priv) ...@@ -111,4 +113,7 @@ static inline void bcm_ptp_stop(struct bcm_ptp_private *priv)
} }
#endif #endif
int bcm_phy_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol);
void bcm_phy_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol);
#endif /* _LINUX_BCM_PHY_LIB_H */ #endif /* _LINUX_BCM_PHY_LIB_H */
...@@ -14,8 +14,12 @@ ...@@ -14,8 +14,12 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/phy.h> #include <linux/phy.h>
#include <linux/pm_wakeup.h>
#include <linux/brcmphy.h> #include <linux/brcmphy.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/gpio/consumer.h>
#define BRCM_PHY_MODEL(phydev) \ #define BRCM_PHY_MODEL(phydev) \
((phydev)->drv->phy_id & (phydev)->drv->phy_id_mask) ((phydev)->drv->phy_id & (phydev)->drv->phy_id_mask)
...@@ -30,8 +34,17 @@ MODULE_LICENSE("GPL"); ...@@ -30,8 +34,17 @@ MODULE_LICENSE("GPL");
struct bcm54xx_phy_priv { struct bcm54xx_phy_priv {
u64 *stats; u64 *stats;
struct bcm_ptp_private *ptp; struct bcm_ptp_private *ptp;
int wake_irq;
bool wake_irq_enabled;
}; };
static bool bcm54xx_phy_can_wakeup(struct phy_device *phydev)
{
struct bcm54xx_phy_priv *priv = phydev->priv;
return phy_interrupt_is_valid(phydev) || priv->wake_irq >= 0;
}
static int bcm54xx_config_clock_delay(struct phy_device *phydev) static int bcm54xx_config_clock_delay(struct phy_device *phydev)
{ {
int rc, val; int rc, val;
...@@ -413,6 +426,16 @@ static int bcm54xx_config_init(struct phy_device *phydev) ...@@ -413,6 +426,16 @@ static int bcm54xx_config_init(struct phy_device *phydev)
bcm54xx_ptp_config_init(phydev); bcm54xx_ptp_config_init(phydev);
/* Acknowledge any left over interrupt and charge the device for
* wake-up.
*/
err = bcm_phy_read_exp(phydev, BCM54XX_WOL_INT_STATUS);
if (err < 0)
return err;
if (err)
pm_wakeup_event(&phydev->mdio.dev, 0);
return 0; return 0;
} }
...@@ -437,12 +460,39 @@ static int bcm54xx_iddq_set(struct phy_device *phydev, bool enable) ...@@ -437,12 +460,39 @@ static int bcm54xx_iddq_set(struct phy_device *phydev, bool enable)
return ret; return ret;
} }
static int bcm54xx_set_wakeup_irq(struct phy_device *phydev, bool state)
{
struct bcm54xx_phy_priv *priv = phydev->priv;
int ret = 0;
if (!bcm54xx_phy_can_wakeup(phydev))
return ret;
if (priv->wake_irq_enabled != state) {
if (state)
ret = enable_irq_wake(priv->wake_irq);
else
ret = disable_irq_wake(priv->wake_irq);
priv->wake_irq_enabled = state;
}
return ret;
}
static int bcm54xx_suspend(struct phy_device *phydev) static int bcm54xx_suspend(struct phy_device *phydev)
{ {
int ret; int ret = 0;
bcm54xx_ptp_stop(phydev); bcm54xx_ptp_stop(phydev);
/* Acknowledge any Wake-on-LAN interrupt prior to suspend */
ret = bcm_phy_read_exp(phydev, BCM54XX_WOL_INT_STATUS);
if (ret < 0)
return ret;
if (phydev->wol_enabled)
return bcm54xx_set_wakeup_irq(phydev, true);
/* We cannot use a read/modify/write here otherwise the PHY gets into /* We cannot use a read/modify/write here otherwise the PHY gets into
* a bad state where its LEDs keep flashing, thus defeating the purpose * a bad state where its LEDs keep flashing, thus defeating the purpose
* of low power mode. * of low power mode.
...@@ -456,7 +506,13 @@ static int bcm54xx_suspend(struct phy_device *phydev) ...@@ -456,7 +506,13 @@ static int bcm54xx_suspend(struct phy_device *phydev)
static int bcm54xx_resume(struct phy_device *phydev) static int bcm54xx_resume(struct phy_device *phydev)
{ {
int ret; int ret = 0;
if (phydev->wol_enabled) {
ret = bcm54xx_set_wakeup_irq(phydev, false);
if (ret)
return ret;
}
ret = bcm54xx_iddq_set(phydev, false); ret = bcm54xx_iddq_set(phydev, false);
if (ret < 0) if (ret < 0)
...@@ -801,14 +857,54 @@ static int brcm_fet_suspend(struct phy_device *phydev) ...@@ -801,14 +857,54 @@ static int brcm_fet_suspend(struct phy_device *phydev)
return err; return err;
} }
static void bcm54xx_phy_get_wol(struct phy_device *phydev,
struct ethtool_wolinfo *wol)
{
/* We cannot wake-up if we do not have a dedicated PHY interrupt line
* or an out of band GPIO descriptor for wake-up. Zeroing
* wol->supported allows the caller (MAC driver) to play through and
* offer its own Wake-on-LAN scheme if available.
*/
if (!bcm54xx_phy_can_wakeup(phydev)) {
wol->supported = 0;
return;
}
bcm_phy_get_wol(phydev, wol);
}
static int bcm54xx_phy_set_wol(struct phy_device *phydev,
struct ethtool_wolinfo *wol)
{
int ret;
/* We cannot wake-up if we do not have a dedicated PHY interrupt line
* or an out of band GPIO descriptor for wake-up. Returning -EOPNOTSUPP
* allows the caller (MAC driver) to play through and offer its own
* Wake-on-LAN scheme if available.
*/
if (!bcm54xx_phy_can_wakeup(phydev))
return -EOPNOTSUPP;
ret = bcm_phy_set_wol(phydev, wol);
if (ret < 0)
return ret;
return 0;
}
static int bcm54xx_phy_probe(struct phy_device *phydev) static int bcm54xx_phy_probe(struct phy_device *phydev)
{ {
struct bcm54xx_phy_priv *priv; struct bcm54xx_phy_priv *priv;
struct gpio_desc *wakeup_gpio;
int ret = 0;
priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL); priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
if (!priv) if (!priv)
return -ENOMEM; return -ENOMEM;
priv->wake_irq = -ENXIO;
phydev->priv = priv; phydev->priv = priv;
priv->stats = devm_kcalloc(&phydev->mdio.dev, priv->stats = devm_kcalloc(&phydev->mdio.dev,
...@@ -821,7 +917,28 @@ static int bcm54xx_phy_probe(struct phy_device *phydev) ...@@ -821,7 +917,28 @@ static int bcm54xx_phy_probe(struct phy_device *phydev)
if (IS_ERR(priv->ptp)) if (IS_ERR(priv->ptp))
return PTR_ERR(priv->ptp); return PTR_ERR(priv->ptp);
return 0; /* We cannot utilize the _optional variant here since we want to know
* whether the GPIO descriptor exists or not to advertise Wake-on-LAN
* support or not.
*/
wakeup_gpio = devm_gpiod_get(&phydev->mdio.dev, "wakeup", GPIOD_IN);
if (PTR_ERR(wakeup_gpio) == -EPROBE_DEFER)
return PTR_ERR(wakeup_gpio);
if (!IS_ERR(wakeup_gpio)) {
priv->wake_irq = gpiod_to_irq(wakeup_gpio);
ret = irq_set_irq_type(priv->wake_irq, IRQ_TYPE_LEVEL_LOW);
if (ret)
return ret;
}
/* If we do not have a main interrupt or a side-band wake-up interrupt,
* then the device cannot be marked as wake-up capable.
*/
if (!bcm54xx_phy_can_wakeup(phydev))
return 0;
return device_init_wakeup(&phydev->mdio.dev, true);
} }
static void bcm54xx_get_stats(struct phy_device *phydev, static void bcm54xx_get_stats(struct phy_device *phydev,
...@@ -894,6 +1011,7 @@ static struct phy_driver broadcom_drivers[] = { ...@@ -894,6 +1011,7 @@ static struct phy_driver broadcom_drivers[] = {
.phy_id_mask = 0xfffffff0, .phy_id_mask = 0xfffffff0,
.name = "Broadcom BCM54210E", .name = "Broadcom BCM54210E",
/* PHY_GBIT_FEATURES */ /* PHY_GBIT_FEATURES */
.flags = PHY_ALWAYS_CALL_SUSPEND,
.get_sset_count = bcm_phy_get_sset_count, .get_sset_count = bcm_phy_get_sset_count,
.get_strings = bcm_phy_get_strings, .get_strings = bcm_phy_get_strings,
.get_stats = bcm54xx_get_stats, .get_stats = bcm54xx_get_stats,
...@@ -904,6 +1022,8 @@ static struct phy_driver broadcom_drivers[] = { ...@@ -904,6 +1022,8 @@ static struct phy_driver broadcom_drivers[] = {
.link_change_notify = bcm54xx_link_change_notify, .link_change_notify = bcm54xx_link_change_notify,
.suspend = bcm54xx_suspend, .suspend = bcm54xx_suspend,
.resume = bcm54xx_resume, .resume = bcm54xx_resume,
.get_wol = bcm54xx_phy_get_wol,
.set_wol = bcm54xx_phy_set_wol,
}, { }, {
.phy_id = PHY_ID_BCM5461, .phy_id = PHY_ID_BCM5461,
.phy_id_mask = 0xfffffff0, .phy_id_mask = 0xfffffff0,
......
...@@ -1860,9 +1860,10 @@ int phy_suspend(struct phy_device *phydev) ...@@ -1860,9 +1860,10 @@ int phy_suspend(struct phy_device *phydev)
if (phydev->suspended) if (phydev->suspended)
return 0; return 0;
/* If the device has WOL enabled, we cannot suspend the PHY */
phy_ethtool_get_wol(phydev, &wol); phy_ethtool_get_wol(phydev, &wol);
if (wol.wolopts || (netdev && netdev->wol_enabled)) phydev->wol_enabled = wol.wolopts || (netdev && netdev->wol_enabled);
/* If the device has WOL enabled, we cannot suspend the PHY */
if (phydev->wol_enabled && !(phydrv->flags & PHY_ALWAYS_CALL_SUSPEND))
return -EBUSY; return -EBUSY;
if (!phydrv || !phydrv->suspend) if (!phydrv || !phydrv->suspend)
......
...@@ -89,6 +89,7 @@ ...@@ -89,6 +89,7 @@
#define MII_BCM54XX_EXP_SEL 0x17 /* Expansion register select */ #define MII_BCM54XX_EXP_SEL 0x17 /* Expansion register select */
#define MII_BCM54XX_EXP_SEL_TOP 0x0d00 /* TOP_MISC expansion register select */ #define MII_BCM54XX_EXP_SEL_TOP 0x0d00 /* TOP_MISC expansion register select */
#define MII_BCM54XX_EXP_SEL_SSD 0x0e00 /* Secondary SerDes select */ #define MII_BCM54XX_EXP_SEL_SSD 0x0e00 /* Secondary SerDes select */
#define MII_BCM54XX_EXP_SEL_WOL 0x0e00 /* Wake-on-LAN expansion select register */
#define MII_BCM54XX_EXP_SEL_ER 0x0f00 /* Expansion register select */ #define MII_BCM54XX_EXP_SEL_ER 0x0f00 /* Expansion register select */
#define MII_BCM54XX_EXP_SEL_ETC 0x0d00 /* Expansion register spare + 2k mem */ #define MII_BCM54XX_EXP_SEL_ETC 0x0d00 /* Expansion register spare + 2k mem */
...@@ -253,6 +254,9 @@ ...@@ -253,6 +254,9 @@
#define BCM54XX_TOP_MISC_IDDQ_SD (1 << 2) #define BCM54XX_TOP_MISC_IDDQ_SD (1 << 2)
#define BCM54XX_TOP_MISC_IDDQ_SR (1 << 3) #define BCM54XX_TOP_MISC_IDDQ_SR (1 << 3)
#define BCM54XX_TOP_MISC_LED_CTL (MII_BCM54XX_EXP_SEL_TOP + 0x0C)
#define BCM54XX_LED4_SEL_INTR BIT(1)
/* /*
* BCM5482: Secondary SerDes registers * BCM5482: Secondary SerDes registers
*/ */
...@@ -272,6 +276,57 @@ ...@@ -272,6 +276,57 @@
#define BCM54612E_EXP_SPARE0 (MII_BCM54XX_EXP_SEL_ETC + 0x34) #define BCM54612E_EXP_SPARE0 (MII_BCM54XX_EXP_SEL_ETC + 0x34)
#define BCM54612E_LED4_CLK125OUT_EN (1 << 1) #define BCM54612E_LED4_CLK125OUT_EN (1 << 1)
/* Wake-on-LAN registers */
#define BCM54XX_WOL_MAIN_CTL (MII_BCM54XX_EXP_SEL_WOL + 0x80)
#define BCM54XX_WOL_EN BIT(0)
#define BCM54XX_WOL_MODE_SINGLE_MPD 0
#define BCM54XX_WOL_MODE_SINGLE_MPDSEC 1
#define BCM54XX_WOL_MODE_DUAL 2
#define BCM54XX_WOL_MODE_SHIFT 1
#define BCM54XX_WOL_MODE_MASK 0x3
#define BCM54XX_WOL_MP_MSB_FF_EN BIT(3)
#define BCM54XX_WOL_SECKEY_OPT_4B 0
#define BCM54XX_WOL_SECKEY_OPT_6B 1
#define BCM54XX_WOL_SECKEY_OPT_8B 2
#define BCM54XX_WOL_SECKEY_OPT_SHIFT 4
#define BCM54XX_WOL_SECKEY_OPT_MASK 0x3
#define BCM54XX_WOL_L2_TYPE_CHK BIT(6)
#define BCM54XX_WOL_L4IPV4UDP_CHK BIT(7)
#define BCM54XX_WOL_L4IPV6UDP_CHK BIT(8)
#define BCM54XX_WOL_UDPPORT_CHK BIT(9)
#define BCM54XX_WOL_CRC_CHK BIT(10)
#define BCM54XX_WOL_SECKEY_MODE BIT(11)
#define BCM54XX_WOL_RST BIT(12)
#define BCM54XX_WOL_DIR_PKT_EN BIT(13)
#define BCM54XX_WOL_MASK_MODE_DA_FF 0
#define BCM54XX_WOL_MASK_MODE_DA_MPD 1
#define BCM54XX_WOL_MASK_MODE_DA_ONLY 2
#define BCM54XX_WOL_MASK_MODE_MPD 3
#define BCM54XX_WOL_MASK_MODE_SHIFT 14
#define BCM54XX_WOL_MASK_MODE_MASK 0x3
#define BCM54XX_WOL_INNER_PROTO (MII_BCM54XX_EXP_SEL_WOL + 0x81)
#define BCM54XX_WOL_OUTER_PROTO (MII_BCM54XX_EXP_SEL_WOL + 0x82)
#define BCM54XX_WOL_OUTER_PROTO2 (MII_BCM54XX_EXP_SEL_WOL + 0x83)
#define BCM54XX_WOL_MPD_DATA1(x) (MII_BCM54XX_EXP_SEL_WOL + 0x84 + (x))
#define BCM54XX_WOL_MPD_DATA2(x) (MII_BCM54XX_EXP_SEL_WOL + 0x87 + (x))
#define BCM54XX_WOL_SEC_KEY_8B (MII_BCM54XX_EXP_SEL_WOL + 0x8A)
#define BCM54XX_WOL_MASK(x) (MII_BCM54XX_EXP_SEL_WOL + 0x8B + (x))
#define BCM54XX_SEC_KEY_STORE(x) (MII_BCM54XX_EXP_SEL_WOL + 0x8E)
#define BCM54XX_WOL_SHARED_CNT (MII_BCM54XX_EXP_SEL_WOL + 0x92)
#define BCM54XX_WOL_INT_MASK (MII_BCM54XX_EXP_SEL_WOL + 0x93)
#define BCM54XX_WOL_PKT1 BIT(0)
#define BCM54XX_WOL_PKT2 BIT(1)
#define BCM54XX_WOL_DIR BIT(2)
#define BCM54XX_WOL_ALL_INTRS (BCM54XX_WOL_PKT1 | \
BCM54XX_WOL_PKT2 | \
BCM54XX_WOL_DIR)
#define BCM54XX_WOL_INT_STATUS (MII_BCM54XX_EXP_SEL_WOL + 0x94)
/*****************************************************************************/ /*****************************************************************************/
/* Fast Ethernet Transceiver definitions. */ /* Fast Ethernet Transceiver definitions. */
/*****************************************************************************/ /*****************************************************************************/
......
...@@ -86,6 +86,7 @@ extern const int phy_10gbit_features_array[1]; ...@@ -86,6 +86,7 @@ extern const int phy_10gbit_features_array[1];
#define PHY_IS_INTERNAL 0x00000001 #define PHY_IS_INTERNAL 0x00000001
#define PHY_RST_AFTER_CLK_EN 0x00000002 #define PHY_RST_AFTER_CLK_EN 0x00000002
#define PHY_POLL_CABLE_TEST 0x00000004 #define PHY_POLL_CABLE_TEST 0x00000004
#define PHY_ALWAYS_CALL_SUSPEND 0x00000008
#define MDIO_DEVICE_IS_PHY 0x80000000 #define MDIO_DEVICE_IS_PHY 0x80000000
/** /**
...@@ -548,6 +549,8 @@ struct macsec_ops; ...@@ -548,6 +549,8 @@ struct macsec_ops;
* @downshifted_rate: Set true if link speed has been downshifted. * @downshifted_rate: Set true if link speed has been downshifted.
* @is_on_sfp_module: Set true if PHY is located on an SFP module. * @is_on_sfp_module: Set true if PHY is located on an SFP module.
* @mac_managed_pm: Set true if MAC driver takes of suspending/resuming PHY * @mac_managed_pm: Set true if MAC driver takes of suspending/resuming PHY
* @wol_enabled: Set to true if the PHY or the attached MAC have Wake-on-LAN
* enabled.
* @state: State of the PHY for management purposes * @state: State of the PHY for management purposes
* @dev_flags: Device-specific flags used by the PHY driver. * @dev_flags: Device-specific flags used by the PHY driver.
* *
...@@ -644,6 +647,7 @@ struct phy_device { ...@@ -644,6 +647,7 @@ struct phy_device {
unsigned downshifted_rate:1; unsigned downshifted_rate:1;
unsigned is_on_sfp_module:1; unsigned is_on_sfp_module:1;
unsigned mac_managed_pm:1; unsigned mac_managed_pm:1;
unsigned wol_enabled:1;
unsigned autoneg:1; unsigned autoneg:1;
/* The most recently read link state */ /* The most recently read link state */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment