|
10 | 10 | #include <linux/bitops.h>
|
11 | 11 | #include <linux/of.h>
|
12 | 12 | #include <linux/phy.h>
|
| 13 | +#include <linux/netdevice.h> |
13 | 14 | #include <linux/module.h>
|
14 | 15 | #include <linux/delay.h>
|
15 | 16 | #include <linux/clk.h>
|
|
38 | 39 |
|
39 | 40 | #define RTL8211F_INSR 0x1d
|
40 | 41 |
|
| 42 | +/* RTL8211F WOL interrupt configuration */ |
| 43 | +#define RTL8211F_INTBCR_PAGE 0xd40 |
| 44 | +#define RTL8211F_INTBCR 0x16 |
| 45 | +#define RTL8211F_INTBCR_INTB_PMEB BIT(5) |
| 46 | + |
| 47 | +/* RTL8211F WOL settings */ |
| 48 | +#define RTL8211F_WOL_SETTINGS_PAGE 0xd8a |
| 49 | +#define RTL8211F_WOL_SETTINGS_EVENTS 16 |
| 50 | +#define RTL8211F_WOL_EVENT_MAGIC BIT(12) |
| 51 | +#define RTL8211F_WOL_SETTINGS_STATUS 17 |
| 52 | +#define RTL8211F_WOL_STATUS_RESET (BIT(15) | 0x1fff) |
| 53 | + |
| 54 | +/* RTL8211F Unique phyiscal and multicast address (WOL) */ |
| 55 | +#define RTL8211F_PHYSICAL_ADDR_PAGE 0xd8c |
| 56 | +#define RTL8211F_PHYSICAL_ADDR_WORD0 16 |
| 57 | +#define RTL8211F_PHYSICAL_ADDR_WORD1 17 |
| 58 | +#define RTL8211F_PHYSICAL_ADDR_WORD2 18 |
| 59 | + |
41 | 60 | #define RTL8211F_LEDCR 0x10
|
42 | 61 | #define RTL8211F_LEDCR_MODE BIT(15)
|
43 | 62 | #define RTL8211F_LEDCR_ACT_TXRX BIT(4)
|
@@ -123,6 +142,7 @@ struct rtl821x_priv {
|
123 | 142 | u16 phycr2;
|
124 | 143 | bool has_phycr2;
|
125 | 144 | struct clk *clk;
|
| 145 | + u32 saved_wolopts; |
126 | 146 | };
|
127 | 147 |
|
128 | 148 | static int rtl821x_read_page(struct phy_device *phydev)
|
@@ -354,6 +374,53 @@ static irqreturn_t rtl8211f_handle_interrupt(struct phy_device *phydev)
|
354 | 374 | return IRQ_HANDLED;
|
355 | 375 | }
|
356 | 376 |
|
| 377 | +static void rtl8211f_get_wol(struct phy_device *dev, struct ethtool_wolinfo *wol) |
| 378 | +{ |
| 379 | + wol->supported = WAKE_MAGIC; |
| 380 | + if (phy_read_paged(dev, RTL8211F_WOL_SETTINGS_PAGE, RTL8211F_WOL_SETTINGS_EVENTS) |
| 381 | + & RTL8211F_WOL_EVENT_MAGIC) |
| 382 | + wol->wolopts = WAKE_MAGIC; |
| 383 | +} |
| 384 | + |
| 385 | +static int rtl8211f_set_wol(struct phy_device *dev, struct ethtool_wolinfo *wol) |
| 386 | +{ |
| 387 | + const u8 *mac_addr = dev->attached_dev->dev_addr; |
| 388 | + int oldpage; |
| 389 | + |
| 390 | + oldpage = phy_save_page(dev); |
| 391 | + if (oldpage < 0) |
| 392 | + goto err; |
| 393 | + |
| 394 | + if (wol->wolopts & WAKE_MAGIC) { |
| 395 | + /* Store the device address for the magic packet */ |
| 396 | + rtl821x_write_page(dev, RTL8211F_PHYSICAL_ADDR_PAGE); |
| 397 | + __phy_write(dev, RTL8211F_PHYSICAL_ADDR_WORD0, mac_addr[1] << 8 | (mac_addr[0])); |
| 398 | + __phy_write(dev, RTL8211F_PHYSICAL_ADDR_WORD1, mac_addr[3] << 8 | (mac_addr[2])); |
| 399 | + __phy_write(dev, RTL8211F_PHYSICAL_ADDR_WORD2, mac_addr[5] << 8 | (mac_addr[4])); |
| 400 | + |
| 401 | + /* Enable magic packet matching and reset WOL status */ |
| 402 | + rtl821x_write_page(dev, RTL8211F_WOL_SETTINGS_PAGE); |
| 403 | + __phy_write(dev, RTL8211F_WOL_SETTINGS_EVENTS, RTL8211F_WOL_EVENT_MAGIC); |
| 404 | + __phy_write(dev, RTL8211F_WOL_SETTINGS_STATUS, RTL8211F_WOL_STATUS_RESET); |
| 405 | + |
| 406 | + /* Enable the WOL interrupt */ |
| 407 | + rtl821x_write_page(dev, RTL8211F_INTBCR_PAGE); |
| 408 | + __phy_set_bits(dev, RTL8211F_INTBCR, RTL8211F_INTBCR_INTB_PMEB); |
| 409 | + } else { |
| 410 | + /* Disable the WOL interrupt */ |
| 411 | + rtl821x_write_page(dev, RTL8211F_INTBCR_PAGE); |
| 412 | + __phy_clear_bits(dev, RTL8211F_INTBCR, RTL8211F_INTBCR_INTB_PMEB); |
| 413 | + |
| 414 | + /* Disable magic packet matching and reset WOL status */ |
| 415 | + rtl821x_write_page(dev, RTL8211F_WOL_SETTINGS_PAGE); |
| 416 | + __phy_write(dev, RTL8211F_WOL_SETTINGS_EVENTS, 0); |
| 417 | + __phy_write(dev, RTL8211F_WOL_SETTINGS_STATUS, RTL8211F_WOL_STATUS_RESET); |
| 418 | + } |
| 419 | + |
| 420 | +err: |
| 421 | + return phy_restore_page(dev, oldpage, 0); |
| 422 | +} |
| 423 | + |
357 | 424 | static int rtl8211_config_aneg(struct phy_device *phydev)
|
358 | 425 | {
|
359 | 426 | int ret;
|
@@ -1400,6 +1467,8 @@ static struct phy_driver realtek_drvs[] = {
|
1400 | 1467 | .read_status = rtlgen_read_status,
|
1401 | 1468 | .config_intr = &rtl8211f_config_intr,
|
1402 | 1469 | .handle_interrupt = rtl8211f_handle_interrupt,
|
| 1470 | + .set_wol = rtl8211f_set_wol, |
| 1471 | + .get_wol = rtl8211f_get_wol, |
1403 | 1472 | .suspend = rtl821x_suspend,
|
1404 | 1473 | .resume = rtl821x_resume,
|
1405 | 1474 | .read_page = rtl821x_read_page,
|
|
0 commit comments