Skip to content

Commit a2f0751

Browse files
Ryceancurrydavem330
authored andcommitted
net: bcmasp: Add support for WoL magic packet
Add support for Wake-On-Lan magic packet and magic packet with password. Signed-off-by: Justin Chen <justin.chen@broadcom.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 490cb41 commit a2f0751

File tree

4 files changed

+262
-12
lines changed

4 files changed

+262
-12
lines changed

drivers/net/ethernet/broadcom/asp2/bcmasp.c

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,135 @@ void bcmasp_core_clock_set_intf(struct bcmasp_intf *intf, bool en)
436436
spin_unlock_irqrestore(&priv->clk_lock, flags);
437437
}
438438

439+
static irqreturn_t bcmasp_isr_wol(int irq, void *data)
440+
{
441+
struct bcmasp_priv *priv = data;
442+
u32 status;
443+
444+
/* No L3 IRQ, so we good */
445+
if (priv->wol_irq <= 0)
446+
goto irq_handled;
447+
448+
status = wakeup_intr2_core_rl(priv, ASP_WAKEUP_INTR2_STATUS) &
449+
~wakeup_intr2_core_rl(priv, ASP_WAKEUP_INTR2_MASK_STATUS);
450+
wakeup_intr2_core_wl(priv, status, ASP_WAKEUP_INTR2_CLEAR);
451+
452+
irq_handled:
453+
pm_wakeup_event(&priv->pdev->dev, 0);
454+
return IRQ_HANDLED;
455+
}
456+
457+
static int bcmasp_get_and_request_irq(struct bcmasp_priv *priv, int i)
458+
{
459+
struct platform_device *pdev = priv->pdev;
460+
int irq, ret;
461+
462+
irq = platform_get_irq_optional(pdev, i);
463+
if (irq < 0)
464+
return irq;
465+
466+
ret = devm_request_irq(&pdev->dev, irq, bcmasp_isr_wol, 0,
467+
pdev->name, priv);
468+
if (ret)
469+
return ret;
470+
471+
return irq;
472+
}
473+
474+
static void bcmasp_init_wol_shared(struct bcmasp_priv *priv)
475+
{
476+
struct platform_device *pdev = priv->pdev;
477+
struct device *dev = &pdev->dev;
478+
int irq;
479+
480+
irq = bcmasp_get_and_request_irq(priv, 1);
481+
if (irq < 0) {
482+
dev_warn(dev, "Failed to init WoL irq: %d\n", irq);
483+
return;
484+
}
485+
486+
priv->wol_irq = irq;
487+
priv->wol_irq_enabled_mask = 0;
488+
device_set_wakeup_capable(&pdev->dev, 1);
489+
}
490+
491+
static void bcmasp_enable_wol_shared(struct bcmasp_intf *intf, bool en)
492+
{
493+
struct bcmasp_priv *priv = intf->parent;
494+
struct device *dev = &priv->pdev->dev;
495+
496+
if (en) {
497+
if (priv->wol_irq_enabled_mask) {
498+
set_bit(intf->port, &priv->wol_irq_enabled_mask);
499+
return;
500+
}
501+
502+
/* First enable */
503+
set_bit(intf->port, &priv->wol_irq_enabled_mask);
504+
enable_irq_wake(priv->wol_irq);
505+
device_set_wakeup_enable(dev, 1);
506+
} else {
507+
if (!priv->wol_irq_enabled_mask)
508+
return;
509+
510+
clear_bit(intf->port, &priv->wol_irq_enabled_mask);
511+
if (priv->wol_irq_enabled_mask)
512+
return;
513+
514+
/* Last disable */
515+
disable_irq_wake(priv->wol_irq);
516+
device_set_wakeup_enable(dev, 0);
517+
}
518+
}
519+
520+
static void bcmasp_wol_irq_destroy_shared(struct bcmasp_priv *priv)
521+
{
522+
if (priv->wol_irq > 0)
523+
free_irq(priv->wol_irq, priv);
524+
}
525+
526+
static void bcmasp_init_wol_per_intf(struct bcmasp_priv *priv)
527+
{
528+
struct platform_device *pdev = priv->pdev;
529+
struct device *dev = &pdev->dev;
530+
struct bcmasp_intf *intf;
531+
int irq;
532+
533+
list_for_each_entry(intf, &priv->intfs, list) {
534+
irq = bcmasp_get_and_request_irq(priv, intf->port + 1);
535+
if (irq < 0) {
536+
dev_warn(dev, "Failed to init WoL irq(port %d): %d\n",
537+
intf->port, irq);
538+
continue;
539+
}
540+
541+
intf->wol_irq = irq;
542+
intf->wol_irq_enabled = false;
543+
device_set_wakeup_capable(&pdev->dev, 1);
544+
}
545+
}
546+
547+
static void bcmasp_enable_wol_per_intf(struct bcmasp_intf *intf, bool en)
548+
{
549+
struct device *dev = &intf->parent->pdev->dev;
550+
551+
if (en ^ intf->wol_irq_enabled)
552+
irq_set_irq_wake(intf->wol_irq, en);
553+
554+
intf->wol_irq_enabled = en;
555+
device_set_wakeup_enable(dev, en);
556+
}
557+
558+
static void bcmasp_wol_irq_destroy_per_intf(struct bcmasp_priv *priv)
559+
{
560+
struct bcmasp_intf *intf;
561+
562+
list_for_each_entry(intf, &priv->intfs, list) {
563+
if (intf->wol_irq > 0)
564+
free_irq(intf->wol_irq, priv);
565+
}
566+
}
567+
439568
static struct bcmasp_hw_info v20_hw_info = {
440569
.rx_ctrl_flush = ASP_RX_CTRL_FLUSH,
441570
.umac2fb = UMAC2FB_OFFSET,
@@ -445,6 +574,9 @@ static struct bcmasp_hw_info v20_hw_info = {
445574
};
446575

447576
static const struct bcmasp_plat_data v20_plat_data = {
577+
.init_wol = bcmasp_init_wol_per_intf,
578+
.enable_wol = bcmasp_enable_wol_per_intf,
579+
.destroy_wol = bcmasp_wol_irq_destroy_per_intf,
448580
.hw_info = &v20_hw_info,
449581
};
450582

@@ -458,6 +590,9 @@ static struct bcmasp_hw_info v21_hw_info = {
458590
};
459591

460592
static const struct bcmasp_plat_data v21_plat_data = {
593+
.init_wol = bcmasp_init_wol_shared,
594+
.enable_wol = bcmasp_enable_wol_shared,
595+
.destroy_wol = bcmasp_wol_irq_destroy_shared,
461596
.hw_info = &v21_hw_info,
462597
};
463598

@@ -521,12 +656,16 @@ static int bcmasp_probe(struct platform_device *pdev)
521656
priv->pdev = pdev;
522657
spin_lock_init(&priv->mda_lock);
523658
spin_lock_init(&priv->clk_lock);
659+
mutex_init(&priv->wol_lock);
524660
INIT_LIST_HEAD(&priv->intfs);
525661

526662
pdata = device_get_match_data(&pdev->dev);
527663
if (!pdata)
528664
return dev_err_probe(dev, -EINVAL, "unable to find platform data\n");
529665

666+
priv->init_wol = pdata->init_wol;
667+
priv->enable_wol = pdata->enable_wol;
668+
priv->destroy_wol = pdata->destroy_wol;
530669
priv->hw_info = pdata->hw_info;
531670

532671
/* Enable all clocks to ensure successful probing */
@@ -570,6 +709,9 @@ static int bcmasp_probe(struct platform_device *pdev)
570709
i++;
571710
}
572711

712+
/* Check and enable WoL */
713+
priv->init_wol(priv);
714+
573715
/* Drop the clock reference count now and let ndo_open()/ndo_close()
574716
* manage it for us from now on.
575717
*/
@@ -585,6 +727,7 @@ static int bcmasp_probe(struct platform_device *pdev)
585727
if (ret) {
586728
netdev_err(intf->ndev,
587729
"failed to register net_device: %d\n", ret);
730+
priv->destroy_wol(priv);
588731
bcmasp_remove_intfs(priv);
589732
goto of_put_exit;
590733
}
@@ -605,6 +748,7 @@ static int bcmasp_remove(struct platform_device *pdev)
605748
if (!priv)
606749
return 0;
607750

751+
priv->destroy_wol(priv);
608752
bcmasp_remove_intfs(priv);
609753

610754
return 0;

drivers/net/ethernet/broadcom/asp2/bcmasp.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,12 @@ struct bcmasp_intf {
301301

302302
/* Statistics */
303303
struct bcmasp_intf_stats64 stats64;
304+
305+
u32 wolopts;
306+
u8 sopass[SOPASS_MAX];
307+
/* Used if per intf wol irq */
308+
int wol_irq;
309+
unsigned int wol_irq_enabled:1;
304310
};
305311

306312
#define NUM_MDA_FILTERS 32
@@ -321,6 +327,9 @@ struct bcmasp_hw_info {
321327
};
322328

323329
struct bcmasp_plat_data {
330+
void (*init_wol)(struct bcmasp_priv *priv);
331+
void (*enable_wol)(struct bcmasp_intf *intf, bool en);
332+
void (*destroy_wol)(struct bcmasp_priv *priv);
324333
struct bcmasp_hw_info *hw_info;
325334
};
326335

@@ -331,6 +340,15 @@ struct bcmasp_priv {
331340
int irq;
332341
u32 irq_mask;
333342

343+
/* Used if shared wol irq */
344+
struct mutex wol_lock;
345+
int wol_irq;
346+
unsigned long wol_irq_enabled_mask;
347+
348+
void (*init_wol)(struct bcmasp_priv *priv);
349+
void (*enable_wol)(struct bcmasp_intf *intf, bool en);
350+
void (*destroy_wol)(struct bcmasp_priv *priv);
351+
334352
void __iomem *base;
335353
struct bcmasp_hw_info *hw_info;
336354

drivers/net/ethernet/broadcom/asp2/bcmasp_ethtool.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,47 @@ static void bcmasp_set_msglevel(struct net_device *dev, u32 level)
3030
intf->msg_enable = level;
3131
}
3232

33+
#define BCMASP_SUPPORTED_WAKE (WAKE_MAGIC | WAKE_MAGICSECURE)
34+
static void bcmasp_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
35+
{
36+
struct bcmasp_intf *intf = netdev_priv(dev);
37+
38+
wol->supported = BCMASP_SUPPORTED_WAKE;
39+
wol->wolopts = intf->wolopts;
40+
memset(wol->sopass, 0, sizeof(wol->sopass));
41+
42+
if (wol->wolopts & WAKE_MAGICSECURE)
43+
memcpy(wol->sopass, intf->sopass, sizeof(intf->sopass));
44+
}
45+
46+
static int bcmasp_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
47+
{
48+
struct bcmasp_intf *intf = netdev_priv(dev);
49+
struct bcmasp_priv *priv = intf->parent;
50+
struct device *kdev = &priv->pdev->dev;
51+
52+
if (!device_can_wakeup(kdev))
53+
return -EOPNOTSUPP;
54+
55+
/* Interface Specific */
56+
intf->wolopts = wol->wolopts;
57+
if (intf->wolopts & WAKE_MAGICSECURE)
58+
memcpy(intf->sopass, wol->sopass, sizeof(wol->sopass));
59+
60+
mutex_lock(&priv->wol_lock);
61+
priv->enable_wol(intf, !!intf->wolopts);
62+
mutex_unlock(&priv->wol_lock);
63+
64+
return 0;
65+
}
66+
3367
const struct ethtool_ops bcmasp_ethtool_ops = {
3468
.get_drvinfo = bcmasp_get_drvinfo,
3569
.get_link = ethtool_op_get_link,
3670
.get_link_ksettings = phy_ethtool_get_link_ksettings,
3771
.set_link_ksettings = phy_ethtool_set_link_ksettings,
3872
.get_msglevel = bcmasp_get_msglevel,
3973
.set_msglevel = bcmasp_set_msglevel,
74+
.get_wol = bcmasp_get_wol,
75+
.set_wol = bcmasp_set_wol,
4076
};

0 commit comments

Comments
 (0)