Loading drivers/net/phy/micrel.c +160 −3 Original line number Diff line number Diff line Loading @@ -28,6 +28,8 @@ #include <linux/micrel_phy.h> #include <linux/of.h> #include <linux/clk.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> /* Operation Mode Strap Override */ #define MII_KSZPHY_OMSO 0x16 Loading Loading @@ -73,6 +75,21 @@ #define PS_TO_REG 200 /*Register 2.10. 15:14 PME Output Select*/ #define MII_KSZPHY_OMSO_PME_N2 BIT(10) /*Register 2.10. BITS 6, 1 and 0 to detect the type of WOL */ #define MII_KSZPHY_WOL_MAGIC_PKT BIT(6) #define MII_KSZPHY_WOL_LINK_DOWN BIT(1) #define MII_KSZPHY_WOL_LINK_UP BIT(0) /* Register 2.10.15:14 PME Output Select */ #define MII_KSZPHY_WOL_CTRL_PME_N2 BIT(15) #define MII_KSZPHY_WOL_CTRL_INT_N BIT(14) /* MMD Address 2h, Register 2h Operation Mode Strap Override*/ #define MII_KSZPHY_OMSO_REG 0x2 /* MMD Address 2h, Register 10h Wake-On-LAN Control */ #define MII_KSZPHY_WOL_CTRL_REG 0x10 struct kszphy_hw_stat { const char *string; u8 reg; Loading Loading @@ -461,6 +478,37 @@ static int ksz9031_extended_read(struct phy_device *phydev, return phy_read(phydev, MII_KSZ9031RN_MMD_REGDATA_REG); } static int ksz9031_ack_interrupt(struct phy_device *phydev) { /* bit[7..0] int status, which is a read and clear register. */ int rc; u32 reg_value; rc = phy_read(phydev, MII_KSZPHY_INTCS); reg_value = ksz9031_extended_read( phydev, OP_DATA, 0x2, MII_KSZPHY_OMSO_REG); if (reg_value & MII_KSZPHY_OMSO_PME_N2) { /* PME output is cleared by disabling the PME trigger src */ reg_value = ksz9031_extended_read( phydev, OP_DATA, 0x2, MII_KSZPHY_WOL_CTRL_REG); reg_value &= ~MII_KSZPHY_WOL_MAGIC_PKT; reg_value &= ~MII_KSZPHY_WOL_LINK_UP; reg_value &= ~MII_KSZPHY_WOL_LINK_DOWN; ksz9031_extended_write( phydev, OP_DATA, 0x2, MII_KSZPHY_WOL_CTRL_REG, reg_value); reg_value = ksz9031_extended_read( phydev, OP_DATA, 0x2, MII_KSZPHY_WOL_CTRL_REG); reg_value |= MII_KSZPHY_WOL_MAGIC_PKT; reg_value |= MII_KSZPHY_WOL_LINK_UP; reg_value |= MII_KSZPHY_WOL_LINK_DOWN; ksz9031_extended_write( phydev, OP_DATA, 0x2, MII_KSZPHY_WOL_CTRL_REG, reg_value); } return (rc < 0) ? rc : 0; } static int ksz9031_of_load_skew_values(struct phy_device *phydev, const struct device_node *of_node, u16 reg, size_t field_sz, Loading Loading @@ -790,6 +838,113 @@ static int kszphy_probe(struct phy_device *phydev) return 0; } static void ksz9031_set_wol_settings(struct phy_device *phydev) { u32 reg_value; /* Enable both PHY and PME_N2 interrupts */ reg_value = ksz9031_extended_read( phydev, OP_DATA, 0x2, MII_KSZPHY_WOL_CTRL_REG); reg_value |= MII_KSZPHY_WOL_CTRL_PME_N2; reg_value &= ~MII_KSZPHY_WOL_CTRL_INT_N; reg_value |= MII_KSZPHY_WOL_MAGIC_PKT; reg_value |= MII_KSZPHY_WOL_LINK_UP; reg_value |= MII_KSZPHY_WOL_LINK_DOWN; ksz9031_extended_write( phydev, OP_DATA, 0x2, MII_KSZPHY_WOL_CTRL_REG, reg_value); } static int ksz9031_set_wol( struct phy_device *phydev, struct ethtool_wolinfo *wol) { struct net_device *ndev = phydev->attached_dev; const u8 *mac; int ret = 0; u32 reg_value; if (!ndev) return -ENODEV; if (wol->wolopts & WAKE_MAGIC) { mac = (const u8 *)ndev->dev_addr; if (!is_valid_ether_addr(mac)) return -EINVAL; ksz9031_extended_write( phydev, OP_DATA, 0x2, 0x11, mac[5] | (mac[4] << 8)); ksz9031_extended_write( phydev, OP_DATA, 0x2, 0x12, mac[3] | (mac[2] << 8)); ksz9031_extended_write( phydev, OP_DATA, 0x2, 0x13, mac[1] | (mac[0] << 8)); /* Enable WOL interrupt for magic pkt, link up and down */ ksz9031_set_wol_settings(phydev); /* Enable PME_N2 output */ reg_value = ksz9031_extended_read( phydev, OP_DATA, 0x2, MII_KSZPHY_OMSO_REG); reg_value |= MII_KSZPHY_OMSO_PME_N2; ksz9031_extended_write( phydev, OP_DATA, 0x2, MII_KSZPHY_OMSO_REG, reg_value); } return ret; } static void ksz9031_get_wol( struct phy_device *phydev, struct ethtool_wolinfo *wol) { u32 reg_value; wol->supported = WAKE_MAGIC; wol->wolopts = 0; reg_value = ksz9031_extended_read( phydev, OP_DATA, 0x2, MII_KSZPHY_OMSO_REG); if (reg_value & MII_KSZPHY_OMSO_PME_N2) wol->wolopts |= WAKE_MAGIC; } static int ksz9031_suspend(struct phy_device *phydev) { int value; int wol_enabled; u32 reg_value; mutex_lock(&phydev->lock); reg_value = ksz9031_extended_read( phydev, OP_DATA, 0x2, MII_KSZPHY_OMSO_REG); wol_enabled = reg_value & MII_KSZPHY_OMSO_PME_N2; value = phy_read(phydev, MII_BMCR); if (wol_enabled) value |= BMCR_ISOLATE; else value |= BMCR_PDOWN; phy_write(phydev, MII_BMCR, value); mutex_unlock(&phydev->lock); return 0; } static int ksz9031_resume(struct phy_device *phydev) { int value; mutex_lock(&phydev->lock); value = phy_read(phydev, MII_BMCR); value &= ~(BMCR_PDOWN | BMCR_ISOLATE); phy_write(phydev, MII_BMCR, value); mutex_unlock(&phydev->lock); return 0; } static struct phy_driver ksphy_driver[] = { { .phy_id = PHY_ID_KS8737, Loading Loading @@ -981,13 +1136,15 @@ static struct phy_driver ksphy_driver[] = { .config_init = ksz9031_config_init, .config_aneg = genphy_config_aneg, .read_status = ksz9031_read_status, .ack_interrupt = kszphy_ack_interrupt, .ack_interrupt = ksz9031_ack_interrupt, .config_intr = kszphy_config_intr, .get_sset_count = kszphy_get_sset_count, .get_strings = kszphy_get_strings, .get_stats = kszphy_get_stats, .suspend = genphy_suspend, .resume = kszphy_resume, .set_wol = ksz9031_set_wol, .get_wol = ksz9031_get_wol, .suspend = ksz9031_suspend, .resume = ksz9031_resume, }, { .phy_id = PHY_ID_KSZ8873MLL, .phy_id_mask = MICREL_PHY_ID_MASK, Loading Loading
drivers/net/phy/micrel.c +160 −3 Original line number Diff line number Diff line Loading @@ -28,6 +28,8 @@ #include <linux/micrel_phy.h> #include <linux/of.h> #include <linux/clk.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> /* Operation Mode Strap Override */ #define MII_KSZPHY_OMSO 0x16 Loading Loading @@ -73,6 +75,21 @@ #define PS_TO_REG 200 /*Register 2.10. 15:14 PME Output Select*/ #define MII_KSZPHY_OMSO_PME_N2 BIT(10) /*Register 2.10. BITS 6, 1 and 0 to detect the type of WOL */ #define MII_KSZPHY_WOL_MAGIC_PKT BIT(6) #define MII_KSZPHY_WOL_LINK_DOWN BIT(1) #define MII_KSZPHY_WOL_LINK_UP BIT(0) /* Register 2.10.15:14 PME Output Select */ #define MII_KSZPHY_WOL_CTRL_PME_N2 BIT(15) #define MII_KSZPHY_WOL_CTRL_INT_N BIT(14) /* MMD Address 2h, Register 2h Operation Mode Strap Override*/ #define MII_KSZPHY_OMSO_REG 0x2 /* MMD Address 2h, Register 10h Wake-On-LAN Control */ #define MII_KSZPHY_WOL_CTRL_REG 0x10 struct kszphy_hw_stat { const char *string; u8 reg; Loading Loading @@ -461,6 +478,37 @@ static int ksz9031_extended_read(struct phy_device *phydev, return phy_read(phydev, MII_KSZ9031RN_MMD_REGDATA_REG); } static int ksz9031_ack_interrupt(struct phy_device *phydev) { /* bit[7..0] int status, which is a read and clear register. */ int rc; u32 reg_value; rc = phy_read(phydev, MII_KSZPHY_INTCS); reg_value = ksz9031_extended_read( phydev, OP_DATA, 0x2, MII_KSZPHY_OMSO_REG); if (reg_value & MII_KSZPHY_OMSO_PME_N2) { /* PME output is cleared by disabling the PME trigger src */ reg_value = ksz9031_extended_read( phydev, OP_DATA, 0x2, MII_KSZPHY_WOL_CTRL_REG); reg_value &= ~MII_KSZPHY_WOL_MAGIC_PKT; reg_value &= ~MII_KSZPHY_WOL_LINK_UP; reg_value &= ~MII_KSZPHY_WOL_LINK_DOWN; ksz9031_extended_write( phydev, OP_DATA, 0x2, MII_KSZPHY_WOL_CTRL_REG, reg_value); reg_value = ksz9031_extended_read( phydev, OP_DATA, 0x2, MII_KSZPHY_WOL_CTRL_REG); reg_value |= MII_KSZPHY_WOL_MAGIC_PKT; reg_value |= MII_KSZPHY_WOL_LINK_UP; reg_value |= MII_KSZPHY_WOL_LINK_DOWN; ksz9031_extended_write( phydev, OP_DATA, 0x2, MII_KSZPHY_WOL_CTRL_REG, reg_value); } return (rc < 0) ? rc : 0; } static int ksz9031_of_load_skew_values(struct phy_device *phydev, const struct device_node *of_node, u16 reg, size_t field_sz, Loading Loading @@ -790,6 +838,113 @@ static int kszphy_probe(struct phy_device *phydev) return 0; } static void ksz9031_set_wol_settings(struct phy_device *phydev) { u32 reg_value; /* Enable both PHY and PME_N2 interrupts */ reg_value = ksz9031_extended_read( phydev, OP_DATA, 0x2, MII_KSZPHY_WOL_CTRL_REG); reg_value |= MII_KSZPHY_WOL_CTRL_PME_N2; reg_value &= ~MII_KSZPHY_WOL_CTRL_INT_N; reg_value |= MII_KSZPHY_WOL_MAGIC_PKT; reg_value |= MII_KSZPHY_WOL_LINK_UP; reg_value |= MII_KSZPHY_WOL_LINK_DOWN; ksz9031_extended_write( phydev, OP_DATA, 0x2, MII_KSZPHY_WOL_CTRL_REG, reg_value); } static int ksz9031_set_wol( struct phy_device *phydev, struct ethtool_wolinfo *wol) { struct net_device *ndev = phydev->attached_dev; const u8 *mac; int ret = 0; u32 reg_value; if (!ndev) return -ENODEV; if (wol->wolopts & WAKE_MAGIC) { mac = (const u8 *)ndev->dev_addr; if (!is_valid_ether_addr(mac)) return -EINVAL; ksz9031_extended_write( phydev, OP_DATA, 0x2, 0x11, mac[5] | (mac[4] << 8)); ksz9031_extended_write( phydev, OP_DATA, 0x2, 0x12, mac[3] | (mac[2] << 8)); ksz9031_extended_write( phydev, OP_DATA, 0x2, 0x13, mac[1] | (mac[0] << 8)); /* Enable WOL interrupt for magic pkt, link up and down */ ksz9031_set_wol_settings(phydev); /* Enable PME_N2 output */ reg_value = ksz9031_extended_read( phydev, OP_DATA, 0x2, MII_KSZPHY_OMSO_REG); reg_value |= MII_KSZPHY_OMSO_PME_N2; ksz9031_extended_write( phydev, OP_DATA, 0x2, MII_KSZPHY_OMSO_REG, reg_value); } return ret; } static void ksz9031_get_wol( struct phy_device *phydev, struct ethtool_wolinfo *wol) { u32 reg_value; wol->supported = WAKE_MAGIC; wol->wolopts = 0; reg_value = ksz9031_extended_read( phydev, OP_DATA, 0x2, MII_KSZPHY_OMSO_REG); if (reg_value & MII_KSZPHY_OMSO_PME_N2) wol->wolopts |= WAKE_MAGIC; } static int ksz9031_suspend(struct phy_device *phydev) { int value; int wol_enabled; u32 reg_value; mutex_lock(&phydev->lock); reg_value = ksz9031_extended_read( phydev, OP_DATA, 0x2, MII_KSZPHY_OMSO_REG); wol_enabled = reg_value & MII_KSZPHY_OMSO_PME_N2; value = phy_read(phydev, MII_BMCR); if (wol_enabled) value |= BMCR_ISOLATE; else value |= BMCR_PDOWN; phy_write(phydev, MII_BMCR, value); mutex_unlock(&phydev->lock); return 0; } static int ksz9031_resume(struct phy_device *phydev) { int value; mutex_lock(&phydev->lock); value = phy_read(phydev, MII_BMCR); value &= ~(BMCR_PDOWN | BMCR_ISOLATE); phy_write(phydev, MII_BMCR, value); mutex_unlock(&phydev->lock); return 0; } static struct phy_driver ksphy_driver[] = { { .phy_id = PHY_ID_KS8737, Loading Loading @@ -981,13 +1136,15 @@ static struct phy_driver ksphy_driver[] = { .config_init = ksz9031_config_init, .config_aneg = genphy_config_aneg, .read_status = ksz9031_read_status, .ack_interrupt = kszphy_ack_interrupt, .ack_interrupt = ksz9031_ack_interrupt, .config_intr = kszphy_config_intr, .get_sset_count = kszphy_get_sset_count, .get_strings = kszphy_get_strings, .get_stats = kszphy_get_stats, .suspend = genphy_suspend, .resume = kszphy_resume, .set_wol = ksz9031_set_wol, .get_wol = ksz9031_get_wol, .suspend = ksz9031_suspend, .resume = ksz9031_resume, }, { .phy_id = PHY_ID_KSZ8873MLL, .phy_id_mask = MICREL_PHY_ID_MASK, Loading