On 10/03/2017 08:53 AM, Dan Murphy wrote: > Add support for the TI DP83822 10/100Mbit ethernet phy. > > The DP83822 provides flexibility to connect to a MAC through a > standard MII, RMII or RGMII interface. > > Datasheet: > http://www.ti.com/product/DP83822I/datasheet
This looks pretty good, just a few nits below. [snip] > +static int dp83822_set_wol(struct phy_device *phydev, > + struct ethtool_wolinfo *wol) > +{ > + struct net_device *ndev = phydev->attached_dev; > + u16 value; > + const u8 *mac; > + > + if (wol->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) { > + mac = (const u8 *)ndev->dev_addr; > + > + if (!is_valid_ether_addr(mac)) > + return -EFAULT; -EINVAL maybe? > + > + /* MAC addresses start with byte 5, but stored in mac[0]. > + * 822 PHYs store bytes 4|5, 2|3, 0|1 > + */ > + phy_write_mmd(phydev, DP83822_DEVADDR, > + MII_DP83822_WOL_DA1, (mac[1] << 8) | mac[0]); > + phy_write_mmd(phydev, DP83822_DEVADDR, > + MII_DP83822_WOL_DA2, (mac[3] << 8) | mac[2]); > + phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_DA3, > + (mac[5] << 8) | mac[4]); > + > + value = phy_read_mmd(phydev, DP83822_DEVADDR, > + MII_DP83822_WOL_CFG); > + if (wol->wolopts & WAKE_MAGIC) > + value |= DP83822_WOL_MAGIC_EN; > + else > + value &= ~DP83822_WOL_MAGIC_EN; > + > + if (wol->wolopts & WAKE_MAGICSECURE) { > + value |= DP83822_WOL_SECURE_ON; Just in case any of the writes below fail, you would probably want to set this bit last, thus indicating that the password was successfully set. > + phy_write_mmd(phydev, DP83822_DEVADDR, > + MII_DP83822_RXSOP1, > + (wol->sopass[1] << 8) | wol->sopass[0]); > + phy_write_mmd(phydev, DP83822_DEVADDR, > + MII_DP83822_RXSOP2, > + (wol->sopass[3] << 8) | wol->sopass[2]); > + phy_write_mmd(phydev, DP83822_DEVADDR, > + MII_DP83822_RXSOP3, > + (wol->sopass[5] << 8) | wol->sopass[4]); In the else clause, you don't appear to be clearing the MagicPacket SecureOn password, but your get_wol function does not check for DP83822_WOL_SECURE_ON before returning the secure password, so either one of these two should be fixed. I would go with fixing the get_wol function see below. > + } else { > + value &= ~DP83822_WOL_SECURE_ON; > + } > + > + value |= (DP83822_WOL_EN | DP83822_WOL_CLR_INDICATION | > + DP83822_WOL_CLR_INDICATION); The extra parenthesis should not be required here. > + phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG, > + value); > + } else { > + value = > + phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG); > + value &= (~DP83822_WOL_EN); Same here, parenthesis should not be needed. > + phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG, > + value); > + } > + > + return 0; > +} > + > +static void dp83822_get_wol(struct phy_device *phydev, > + struct ethtool_wolinfo *wol) > +{ > + int value; > + > + wol->supported = (WAKE_MAGIC | WAKE_MAGICSECURE); > + wol->wolopts = 0; > + > + value = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG); > + if (value & DP83822_WOL_MAGIC_EN) > + wol->wolopts |= WAKE_MAGIC; > + > + if (value & DP83822_WOL_SECURE_ON) > + wol->wolopts |= WAKE_MAGICSECURE; > + > + if (~value & DP83822_WOL_CLR_INDICATION) > + wol->wolopts = 0; > + > + wol->sopass[0] = (phy_read_mmd(phydev, > + DP83822_DEVADDR, > + MII_DP83822_RXSOP1) & 0xFF); > + wol->sopass[1] = > + (phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_RXSOP1) >> 8); You can save about twice the amount of reads by using a temporary variable to hold the 16-bit register ;) > + wol->sopass[2] = > + (phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_RXSOP2) & 0xFF); > + wol->sopass[3] = > + (phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_RXSOP2) >> 8); > + wol->sopass[4] = > + (phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_RXSOP3) & 0xFF); > + wol->sopass[5] = > + (phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_RXSOP3) >> 8); Unless DP83822_WOL_SECURE_ON is set, you probably should not try reading the password at all, because there is no guarantee it has been correctly set. > +} > +static int dp83822_phy_reset(struct phy_device *phydev) > +{ > + int err; > + > + err = phy_write(phydev, MII_DP83822_RESET_CTRL, DP83822_HW_RESET); > + if (err < 0) > + return err; > + > + return 0; > +} > + > +static int dp83822_suspend(struct phy_device *phydev) > +{ > + int value; > + > + mutex_lock(&phydev->lock); > + > + value = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG); > + if (~value & DP83822_WOL_EN) { > + value = phy_read(phydev, MII_BMCR); > + phy_write(phydev, MII_BMCR, value | BMCR_PDOWN); > + } Can you use genphy_suspend() here along with careful locking of course? > + > + mutex_unlock(&phydev->lock); > + > + return 0; > +} > + > +static int dp83822_resume(struct phy_device *phydev) > +{ > + int value; > + > + mutex_lock(&phydev->lock); > + > + value = phy_read(phydev, MII_BMCR); > + phy_write(phydev, MII_BMCR, value & ~BMCR_PDOWN); And genphy_resume() here as well? > + > + value = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG); > + > + phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG, value | > + DP83822_WOL_CLR_INDICATION); > + > + mutex_unlock(&phydev->lock); > + > + return 0; > +} > + > +static struct phy_driver dp83822_driver[] = { > + { > + .phy_id = DP83822_PHY_ID, > + .phy_id_mask = 0xfffffff0, > + .name = "TI DP83822", > + .features = PHY_BASIC_FEATURES, > + .flags = PHY_HAS_INTERRUPT, > + > + .config_init = genphy_config_init, > + .soft_reset = dp83822_phy_reset, > + > + .get_wol = dp83822_get_wol, > + .set_wol = dp83822_set_wol, > + > + /* IRQ related */ > + .ack_interrupt = dp83822_ack_interrupt, > + .config_intr = dp83822_config_intr, > + > + .config_aneg = genphy_config_aneg, > + .read_status = genphy_read_status, > + .suspend = dp83822_suspend, > + .resume = dp83822_resume, > + }, I would omit newlines between definitions of callbacks, but this is really a personal preference. Unless you are planning on adding new IDs, you could also avoid using an array of 1 element and just a plain phy_driver structure, but that's not a big deal either. -- Florian