Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: fec: add Wake-on-LAN support

Support for Wake-on-LAN using Magic Packet. ENET IP supports sleep mode
in low power status, when system enter suspend status, Magic packet can
wake up system even if all SOC clocks are gate. The patch doing below things:
- flagging the device as a wakeup source for the system, as well as
its Wake-on-LAN interrupt
- prepare the hardware for entering WoL mode
- add standard ethtool WOL interface
- enable the ENET interrupt to wake us

Tested on i.MX6q/dl sabresd, sabreauto boards, i.MX6SX arm2 boards.

Signed-off-by: Fugang Duan <B38611@freescale.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Nimrod Andy and committed by
David S. Miller
de40ed31 03366a33

+99 -10
+2
Documentation/devicetree/bindings/net/fsl-fec.txt
··· 22 22 - fsl,num-rx-queues : The property is valid for enet-avb IP, which supports 23 23 hw multi queues. Should specify the rx queue number, otherwise set rx queue 24 24 number to 1. 25 + - fsl,magic-packet : If present, indicates that the hardware supports waking 26 + up via magic packet. 25 27 26 28 Optional subnodes: 27 29 - mdio : specifies the mdio bus in the FEC, used as a container for phy nodes
+2
drivers/net/ethernet/freescale/fec.h
··· 357 357 #define FEC_ENET_RXB ((uint)0x01000000) /* A buffer was received */ 358 358 #define FEC_ENET_MII ((uint)0x00800000) /* MII interrupt */ 359 359 #define FEC_ENET_EBERR ((uint)0x00400000) /* SDMA bus error */ 360 + #define FEC_ENET_WAKEUP ((uint)0x00020000) /* Wakeup request */ 360 361 #define FEC_ENET_TXF (FEC_ENET_TXF_0 | FEC_ENET_TXF_1 | FEC_ENET_TXF_2) 361 362 #define FEC_ENET_RXF (FEC_ENET_RXF_0 | FEC_ENET_RXF_1 | FEC_ENET_RXF_2) 362 363 #define FEC_ENET_TS_AVAIL ((uint)0x00010000) ··· 513 512 int irq[FEC_IRQ_NUM]; 514 513 bool bufdesc_ex; 515 514 int pause_flag; 515 + int wol_flag; 516 516 u32 quirks; 517 517 518 518 struct napi_struct napi;
+94 -10
drivers/net/ethernet/freescale/fec_main.c
··· 187 187 #define FEC_MMFR_RA(v) ((v & 0x1f) << 18) 188 188 #define FEC_MMFR_TA (2 << 16) 189 189 #define FEC_MMFR_DATA(v) (v & 0xffff) 190 + /* FEC ECR bits definition */ 191 + #define FEC_ECR_MAGICEN (1 << 2) 192 + #define FEC_ECR_SLEEP (1 << 3) 190 193 191 194 #define FEC_MII_TIMEOUT 30000 /* us */ 192 195 ··· 198 195 199 196 #define FEC_PAUSE_FLAG_AUTONEG 0x1 200 197 #define FEC_PAUSE_FLAG_ENABLE 0x2 198 + #define FEC_WOL_HAS_MAGIC_PACKET (0x1 << 0) 199 + #define FEC_WOL_FLAG_ENABLE (0x1 << 1) 200 + #define FEC_WOL_FLAG_SLEEP_ON (0x1 << 2) 201 201 202 202 #define COPYBREAK_DEFAULT 256 203 203 ··· 1095 1089 fec_stop(struct net_device *ndev) 1096 1090 { 1097 1091 struct fec_enet_private *fep = netdev_priv(ndev); 1092 + struct fec_platform_data *pdata = fep->pdev->dev.platform_data; 1098 1093 u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & (1 << 8); 1094 + u32 val; 1099 1095 1100 1096 /* We cannot expect a graceful transmit stop without link !!! */ 1101 1097 if (fep->link) { ··· 1111 1103 * For i.MX6SX SOC, enet use AXI bus, we use disable MAC 1112 1104 * instead of reset MAC itself. 1113 1105 */ 1114 - if (fep->quirks & FEC_QUIRK_HAS_AVB) { 1115 - writel(0, fep->hwp + FEC_ECNTRL); 1106 + if (!(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) { 1107 + if (fep->quirks & FEC_QUIRK_HAS_AVB) { 1108 + writel(0, fep->hwp + FEC_ECNTRL); 1109 + } else { 1110 + writel(1, fep->hwp + FEC_ECNTRL); 1111 + udelay(10); 1112 + } 1113 + writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 1116 1114 } else { 1117 - writel(1, fep->hwp + FEC_ECNTRL); 1118 - udelay(10); 1115 + writel(FEC_DEFAULT_IMASK | FEC_ENET_WAKEUP, fep->hwp + FEC_IMASK); 1116 + val = readl(fep->hwp + FEC_ECNTRL); 1117 + val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP); 1118 + writel(val, fep->hwp + FEC_ECNTRL); 1119 + 1120 + if (pdata && pdata->sleep_mode_enable) 1121 + pdata->sleep_mode_enable(true); 1119 1122 } 1120 1123 writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); 1121 - writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); 1122 1124 1123 1125 /* We have to keep ENET enabled to have MII interrupt stay working */ 1124 - if (fep->quirks & FEC_QUIRK_ENET_MAC) { 1126 + if (fep->quirks & FEC_QUIRK_ENET_MAC && 1127 + !(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) { 1125 1128 writel(2, fep->hwp + FEC_ECNTRL); 1126 1129 writel(rmii_mode, fep->hwp + FEC_R_CNTRL); 1127 1130 } ··· 2446 2427 return ret; 2447 2428 } 2448 2429 2430 + static void 2431 + fec_enet_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) 2432 + { 2433 + struct fec_enet_private *fep = netdev_priv(ndev); 2434 + 2435 + if (fep->wol_flag & FEC_WOL_HAS_MAGIC_PACKET) { 2436 + wol->supported = WAKE_MAGIC; 2437 + wol->wolopts = fep->wol_flag & FEC_WOL_FLAG_ENABLE ? WAKE_MAGIC : 0; 2438 + } else { 2439 + wol->supported = wol->wolopts = 0; 2440 + } 2441 + } 2442 + 2443 + static int 2444 + fec_enet_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) 2445 + { 2446 + struct fec_enet_private *fep = netdev_priv(ndev); 2447 + 2448 + if (!(fep->wol_flag & FEC_WOL_HAS_MAGIC_PACKET)) 2449 + return -EINVAL; 2450 + 2451 + if (wol->wolopts & ~WAKE_MAGIC) 2452 + return -EINVAL; 2453 + 2454 + device_set_wakeup_enable(&ndev->dev, wol->wolopts & WAKE_MAGIC); 2455 + if (device_may_wakeup(&ndev->dev)) { 2456 + fep->wol_flag |= FEC_WOL_FLAG_ENABLE; 2457 + if (fep->irq[0] > 0) 2458 + enable_irq_wake(fep->irq[0]); 2459 + } else { 2460 + fep->wol_flag &= (~FEC_WOL_FLAG_ENABLE); 2461 + if (fep->irq[0] > 0) 2462 + disable_irq_wake(fep->irq[0]); 2463 + } 2464 + 2465 + return 0; 2466 + } 2467 + 2449 2468 static const struct ethtool_ops fec_enet_ethtool_ops = { 2450 2469 .get_settings = fec_enet_get_settings, 2451 2470 .set_settings = fec_enet_set_settings, ··· 2502 2445 .get_ts_info = fec_enet_get_ts_info, 2503 2446 .get_tunable = fec_enet_get_tunable, 2504 2447 .set_tunable = fec_enet_set_tunable, 2448 + .get_wol = fec_enet_get_wol, 2449 + .set_wol = fec_enet_set_wol, 2505 2450 }; 2506 2451 2507 2452 static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) ··· 2763 2704 napi_enable(&fep->napi); 2764 2705 phy_start(fep->phy_dev); 2765 2706 netif_tx_start_all_queues(ndev); 2707 + 2708 + device_set_wakeup_enable(&ndev->dev, fep->wol_flag & 2709 + FEC_WOL_FLAG_ENABLE); 2766 2710 2767 2711 return 0; 2768 2712 ··· 3215 3153 3216 3154 platform_set_drvdata(pdev, ndev); 3217 3155 3156 + if (of_get_property(np, "fsl,magic-packet", NULL)) 3157 + fep->wol_flag |= FEC_WOL_HAS_MAGIC_PACKET; 3158 + 3218 3159 phy_node = of_parse_phandle(np, "phy-handle", 0); 3219 3160 if (!phy_node && of_phy_is_fixed_link(np)) { 3220 3161 ret = of_phy_register_fixed_link(np); ··· 3312 3247 0, pdev->name, ndev); 3313 3248 if (ret) 3314 3249 goto failed_irq; 3250 + 3251 + fep->irq[i] = irq; 3315 3252 } 3316 3253 3317 3254 init_completion(&fep->mdio_done); ··· 3329 3262 ret = register_netdev(ndev); 3330 3263 if (ret) 3331 3264 goto failed_register; 3265 + 3266 + device_init_wakeup(&ndev->dev, fep->wol_flag & 3267 + FEC_WOL_HAS_MAGIC_PACKET); 3332 3268 3333 3269 if (fep->bufdesc_ex && fep->ptp_clock) 3334 3270 netdev_info(ndev, "registered PHC device %d\n", fep->dev_id); ··· 3386 3316 3387 3317 rtnl_lock(); 3388 3318 if (netif_running(ndev)) { 3319 + if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) 3320 + fep->wol_flag |= FEC_WOL_FLAG_SLEEP_ON; 3389 3321 phy_stop(fep->phy_dev); 3390 3322 napi_disable(&fep->napi); 3391 3323 netif_tx_lock_bh(ndev); ··· 3395 3323 netif_tx_unlock_bh(ndev); 3396 3324 fec_stop(ndev); 3397 3325 fec_enet_clk_enable(ndev, false); 3398 - pinctrl_pm_select_sleep_state(&fep->pdev->dev); 3326 + if (!(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) 3327 + pinctrl_pm_select_sleep_state(&fep->pdev->dev); 3399 3328 } 3400 3329 rtnl_unlock(); 3401 3330 3402 - if (fep->reg_phy) 3331 + if (fep->reg_phy && !(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) 3403 3332 regulator_disable(fep->reg_phy); 3404 3333 3405 3334 /* SOC supply clock to phy, when clock is disabled, phy link down ··· 3416 3343 { 3417 3344 struct net_device *ndev = dev_get_drvdata(dev); 3418 3345 struct fec_enet_private *fep = netdev_priv(ndev); 3346 + struct fec_platform_data *pdata = fep->pdev->dev.platform_data; 3419 3347 int ret; 3348 + int val; 3420 3349 3421 - if (fep->reg_phy) { 3350 + if (fep->reg_phy && !(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) { 3422 3351 ret = regulator_enable(fep->reg_phy); 3423 3352 if (ret) 3424 3353 return ret; ··· 3428 3353 3429 3354 rtnl_lock(); 3430 3355 if (netif_running(ndev)) { 3431 - pinctrl_pm_select_default_state(&fep->pdev->dev); 3432 3356 ret = fec_enet_clk_enable(ndev, true); 3433 3357 if (ret) { 3434 3358 rtnl_unlock(); 3435 3359 goto failed_clk; 3360 + } 3361 + if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) { 3362 + if (pdata && pdata->sleep_mode_enable) 3363 + pdata->sleep_mode_enable(false); 3364 + val = readl(fep->hwp + FEC_ECNTRL); 3365 + val &= ~(FEC_ECR_MAGICEN | FEC_ECR_SLEEP); 3366 + writel(val, fep->hwp + FEC_ECNTRL); 3367 + fep->wol_flag &= ~FEC_WOL_FLAG_SLEEP_ON; 3368 + } else { 3369 + pinctrl_pm_select_default_state(&fep->pdev->dev); 3436 3370 } 3437 3371 fec_restart(ndev); 3438 3372 netif_tx_lock_bh(ndev);
+1
include/linux/fec.h
··· 19 19 struct fec_platform_data { 20 20 phy_interface_t phy; 21 21 unsigned char mac[ETH_ALEN]; 22 + void (*sleep_mode_enable)(int enabled); 22 23 }; 23 24 24 25 #endif