diff options
Diffstat (limited to 'drivers/net/phy/dp83867.c')
-rw-r--r-- | drivers/net/phy/dp83867.c | 265 |
1 files changed, 209 insertions, 56 deletions
diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c index 37fceaf9fa10..9cd9dcee4eb2 100644 --- a/drivers/net/phy/dp83867.c +++ b/drivers/net/phy/dp83867.c @@ -12,6 +12,8 @@ #include <linux/of.h> #include <linux/phy.h> #include <linux/delay.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> #include <dt-bindings/net/ti-dp83867.h> @@ -21,8 +23,9 @@ #define MII_DP83867_PHYCTRL 0x10 #define MII_DP83867_MICR 0x12 #define MII_DP83867_ISR 0x13 -#define DP83867_CTRL 0x1f +#define DP83867_CFG2 0x14 #define DP83867_CFG3 0x1e +#define DP83867_CTRL 0x1f /* Extended Registers */ #define DP83867_CFG4 0x0031 @@ -36,6 +39,13 @@ #define DP83867_STRAP_STS1 0x006E #define DP83867_STRAP_STS2 0x006f #define DP83867_RGMIIDCTL 0x0086 +#define DP83867_RXFCFG 0x0134 +#define DP83867_RXFPMD1 0x0136 +#define DP83867_RXFPMD2 0x0137 +#define DP83867_RXFPMD3 0x0138 +#define DP83867_RXFSOP1 0x0139 +#define DP83867_RXFSOP2 0x013A +#define DP83867_RXFSOP3 0x013B #define DP83867_IO_MUX_CFG 0x0170 #define DP83867_SGMIICTL 0x00D3 #define DP83867_10M_SGMII_CFG 0x016F @@ -65,6 +75,13 @@ /* SGMIICTL bits */ #define DP83867_SGMII_TYPE BIT(14) +/* RXFCFG bits*/ +#define DP83867_WOL_MAGIC_EN BIT(0) +#define DP83867_WOL_BCAST_EN BIT(2) +#define DP83867_WOL_UCAST_EN BIT(4) +#define DP83867_WOL_SEC_EN BIT(5) +#define DP83867_WOL_ENH_MAC BIT(7) + /* STRAP_STS1 bits */ #define DP83867_STRAP_STS1_RESERVED BIT(11) @@ -84,8 +101,11 @@ /* RGMIIDCTL bits */ #define DP83867_RGMII_TX_CLK_DELAY_MAX 0xf #define DP83867_RGMII_TX_CLK_DELAY_SHIFT 4 +#define DP83867_RGMII_TX_CLK_DELAY_INV (DP83867_RGMII_TX_CLK_DELAY_MAX + 1) #define DP83867_RGMII_RX_CLK_DELAY_MAX 0xf #define DP83867_RGMII_RX_CLK_DELAY_SHIFT 0 +#define DP83867_RGMII_RX_CLK_DELAY_INV (DP83867_RGMII_RX_CLK_DELAY_MAX + 1) + /* IO_MUX_CFG bits */ #define DP83867_IO_MUX_CFG_IO_IMPEDANCE_MASK 0x1f @@ -95,6 +115,10 @@ #define DP83867_IO_MUX_CFG_CLK_O_SEL_MASK (0x1f << 8) #define DP83867_IO_MUX_CFG_CLK_O_SEL_SHIFT 8 +/* CFG3 bits */ +#define DP83867_CFG3_INT_OE BIT(7) +#define DP83867_CFG3_ROBUST_AUTO_MDIX BIT(9) + /* CFG4 bits */ #define DP83867_CFG4_PORT_MIRROR_EN BIT(0) @@ -126,6 +150,115 @@ static int dp83867_ack_interrupt(struct phy_device *phydev) return 0; } +static int dp83867_set_wol(struct phy_device *phydev, + struct ethtool_wolinfo *wol) +{ + struct net_device *ndev = phydev->attached_dev; + u16 val_rxcfg, val_micr; + u8 *mac; + + val_rxcfg = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_RXFCFG); + val_micr = phy_read(phydev, MII_DP83867_MICR); + + if (wol->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_UCAST | + WAKE_BCAST)) { + val_rxcfg |= DP83867_WOL_ENH_MAC; + val_micr |= MII_DP83867_MICR_WOL_INT_EN; + + if (wol->wolopts & WAKE_MAGIC) { + mac = (u8 *)ndev->dev_addr; + + if (!is_valid_ether_addr(mac)) + return -EINVAL; + + phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RXFPMD1, + (mac[1] << 8 | mac[0])); + phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RXFPMD2, + (mac[3] << 8 | mac[2])); + phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RXFPMD3, + (mac[5] << 8 | mac[4])); + + val_rxcfg |= DP83867_WOL_MAGIC_EN; + } else { + val_rxcfg &= ~DP83867_WOL_MAGIC_EN; + } + + if (wol->wolopts & WAKE_MAGICSECURE) { + phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RXFSOP1, + (wol->sopass[1] << 8) | wol->sopass[0]); + phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RXFSOP1, + (wol->sopass[3] << 8) | wol->sopass[2]); + phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RXFSOP1, + (wol->sopass[5] << 8) | wol->sopass[4]); + + val_rxcfg |= DP83867_WOL_SEC_EN; + } else { + val_rxcfg &= ~DP83867_WOL_SEC_EN; + } + + if (wol->wolopts & WAKE_UCAST) + val_rxcfg |= DP83867_WOL_UCAST_EN; + else + val_rxcfg &= ~DP83867_WOL_UCAST_EN; + + if (wol->wolopts & WAKE_BCAST) + val_rxcfg |= DP83867_WOL_BCAST_EN; + else + val_rxcfg &= ~DP83867_WOL_BCAST_EN; + } else { + val_rxcfg &= ~DP83867_WOL_ENH_MAC; + val_micr &= ~MII_DP83867_MICR_WOL_INT_EN; + } + + phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RXFCFG, val_rxcfg); + phy_write(phydev, MII_DP83867_MICR, val_micr); + + return 0; +} + +static void dp83867_get_wol(struct phy_device *phydev, + struct ethtool_wolinfo *wol) +{ + u16 value, sopass_val; + + wol->supported = (WAKE_UCAST | WAKE_BCAST | WAKE_MAGIC | + WAKE_MAGICSECURE); + wol->wolopts = 0; + + value = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_RXFCFG); + + if (value & DP83867_WOL_UCAST_EN) + wol->wolopts |= WAKE_UCAST; + + if (value & DP83867_WOL_BCAST_EN) + wol->wolopts |= WAKE_BCAST; + + if (value & DP83867_WOL_MAGIC_EN) + wol->wolopts |= WAKE_MAGIC; + + if (value & DP83867_WOL_SEC_EN) { + sopass_val = phy_read_mmd(phydev, DP83867_DEVADDR, + DP83867_RXFSOP1); + wol->sopass[0] = (sopass_val & 0xff); + wol->sopass[1] = (sopass_val >> 8); + + sopass_val = phy_read_mmd(phydev, DP83867_DEVADDR, + DP83867_RXFSOP2); + wol->sopass[2] = (sopass_val & 0xff); + wol->sopass[3] = (sopass_val >> 8); + + sopass_val = phy_read_mmd(phydev, DP83867_DEVADDR, + DP83867_RXFSOP3); + wol->sopass[4] = (sopass_val & 0xff); + wol->sopass[5] = (sopass_val >> 8); + + wol->wolopts |= WAKE_MAGICSECURE; + } + + if (!(value & DP83867_WOL_ENH_MAC)) + wol->wolopts = 0; +} + static int dp83867_config_intr(struct phy_device *phydev) { int micr_status; @@ -164,6 +297,48 @@ static int dp83867_config_port_mirroring(struct phy_device *phydev) return 0; } +static int dp83867_verify_rgmii_cfg(struct phy_device *phydev) +{ + struct dp83867_private *dp83867 = phydev->priv; + + /* Existing behavior was to use default pin strapping delay in rgmii + * mode, but rgmii should have meant no delay. Warn existing users. + */ + if (phydev->interface == PHY_INTERFACE_MODE_RGMII) { + const u16 val = phy_read_mmd(phydev, DP83867_DEVADDR, + DP83867_STRAP_STS2); + const u16 txskew = (val & DP83867_STRAP_STS2_CLK_SKEW_TX_MASK) >> + DP83867_STRAP_STS2_CLK_SKEW_TX_SHIFT; + const u16 rxskew = (val & DP83867_STRAP_STS2_CLK_SKEW_RX_MASK) >> + DP83867_STRAP_STS2_CLK_SKEW_RX_SHIFT; + + if (txskew != DP83867_STRAP_STS2_CLK_SKEW_NONE || + rxskew != DP83867_STRAP_STS2_CLK_SKEW_NONE) + phydev_warn(phydev, + "PHY has delays via pin strapping, but phy-mode = 'rgmii'\n" + "Should be 'rgmii-id' to use internal delays txskew:%x rxskew:%x\n", + txskew, rxskew); + } + + /* RX delay *must* be specified if internal delay of RX is used. */ + if ((phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || + phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) && + dp83867->rx_id_delay == DP83867_RGMII_RX_CLK_DELAY_INV) { + phydev_err(phydev, "ti,rx-internal-delay must be specified\n"); + return -EINVAL; + } + + /* TX delay *must* be specified if internal delay of TX is used. */ + if ((phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || + phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) && + dp83867->tx_id_delay == DP83867_RGMII_TX_CLK_DELAY_INV) { + phydev_err(phydev, "ti,tx-internal-delay must be specified\n"); + return -EINVAL; + } + + return 0; +} + #ifdef CONFIG_OF_MDIO static int dp83867_of_init(struct phy_device *phydev) { @@ -205,55 +380,25 @@ static int dp83867_of_init(struct phy_device *phydev) dp83867->sgmii_ref_clk_en = of_property_read_bool(of_node, "ti,sgmii-ref-clock-output-enable"); - /* Existing behavior was to use default pin strapping delay in rgmii - * mode, but rgmii should have meant no delay. Warn existing users. - */ - if (phydev->interface == PHY_INTERFACE_MODE_RGMII) { - const u16 val = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_STRAP_STS2); - const u16 txskew = (val & DP83867_STRAP_STS2_CLK_SKEW_TX_MASK) >> - DP83867_STRAP_STS2_CLK_SKEW_TX_SHIFT; - const u16 rxskew = (val & DP83867_STRAP_STS2_CLK_SKEW_RX_MASK) >> - DP83867_STRAP_STS2_CLK_SKEW_RX_SHIFT; - - if (txskew != DP83867_STRAP_STS2_CLK_SKEW_NONE || - rxskew != DP83867_STRAP_STS2_CLK_SKEW_NONE) - phydev_warn(phydev, - "PHY has delays via pin strapping, but phy-mode = 'rgmii'\n" - "Should be 'rgmii-id' to use internal delays\n"); - } - /* RX delay *must* be specified if internal delay of RX is used. */ - if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || - phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) { - ret = of_property_read_u32(of_node, "ti,rx-internal-delay", - &dp83867->rx_id_delay); - if (ret) { - phydev_err(phydev, "ti,rx-internal-delay must be specified\n"); - return ret; - } - if (dp83867->rx_id_delay > DP83867_RGMII_RX_CLK_DELAY_MAX) { - phydev_err(phydev, - "ti,rx-internal-delay value of %u out of range\n", - dp83867->rx_id_delay); - return -EINVAL; - } + dp83867->rx_id_delay = DP83867_RGMII_RX_CLK_DELAY_INV; + ret = of_property_read_u32(of_node, "ti,rx-internal-delay", + &dp83867->rx_id_delay); + if (!ret && dp83867->rx_id_delay > DP83867_RGMII_RX_CLK_DELAY_MAX) { + phydev_err(phydev, + "ti,rx-internal-delay value of %u out of range\n", + dp83867->rx_id_delay); + return -EINVAL; } - /* TX delay *must* be specified if internal delay of RX is used. */ - if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || - phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) { - ret = of_property_read_u32(of_node, "ti,tx-internal-delay", - &dp83867->tx_id_delay); - if (ret) { - phydev_err(phydev, "ti,tx-internal-delay must be specified\n"); - return ret; - } - if (dp83867->tx_id_delay > DP83867_RGMII_TX_CLK_DELAY_MAX) { - phydev_err(phydev, - "ti,tx-internal-delay value of %u out of range\n", - dp83867->tx_id_delay); - return -EINVAL; - } + dp83867->tx_id_delay = DP83867_RGMII_TX_CLK_DELAY_INV; + ret = of_property_read_u32(of_node, "ti,tx-internal-delay", + &dp83867->tx_id_delay); + if (!ret && dp83867->tx_id_delay > DP83867_RGMII_TX_CLK_DELAY_MAX) { + phydev_err(phydev, + "ti,tx-internal-delay value of %u out of range\n", + dp83867->tx_id_delay); + return -EINVAL; } if (of_property_read_bool(of_node, "enet-phy-lane-swap")) @@ -295,7 +440,7 @@ static int dp83867_probe(struct phy_device *phydev) phydev->priv = dp83867; - return 0; + return dp83867_of_init(phydev); } static int dp83867_config_init(struct phy_device *phydev) @@ -304,7 +449,7 @@ static int dp83867_config_init(struct phy_device *phydev) int ret, val, bs; u16 delay; - ret = dp83867_of_init(phydev); + ret = dp83867_verify_rgmii_cfg(phydev); if (ret) return ret; @@ -359,8 +504,12 @@ static int dp83867_config_init(struct phy_device *phydev) phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RGMIICTL, val); - delay = (dp83867->rx_id_delay | - (dp83867->tx_id_delay << DP83867_RGMII_TX_CLK_DELAY_SHIFT)); + delay = 0; + if (dp83867->rx_id_delay != DP83867_RGMII_RX_CLK_DELAY_INV) + delay |= dp83867->rx_id_delay; + if (dp83867->tx_id_delay != DP83867_RGMII_TX_CLK_DELAY_INV) + delay |= dp83867->tx_id_delay << + DP83867_RGMII_TX_CLK_DELAY_SHIFT; phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RGMIIDCTL, delay); @@ -410,12 +559,13 @@ static int dp83867_config_init(struct phy_device *phydev) phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_SGMIICTL, val); } + val = phy_read(phydev, DP83867_CFG3); /* Enable Interrupt output INT_OE in CFG3 register */ - if (phy_interrupt_is_valid(phydev)) { - val = phy_read(phydev, DP83867_CFG3); - val |= BIT(7); - phy_write(phydev, DP83867_CFG3, val); - } + if (phy_interrupt_is_valid(phydev)) + val |= DP83867_CFG3_INT_OE; + + val |= DP83867_CFG3_ROBUST_AUTO_MDIX; + phy_write(phydev, DP83867_CFG3, val); if (dp83867->port_mirroring != DP83867_PORT_MIRROING_KEEP) dp83867_config_port_mirroring(phydev); @@ -463,6 +613,9 @@ static struct phy_driver dp83867_driver[] = { .config_init = dp83867_config_init, .soft_reset = dp83867_phy_reset, + .get_wol = dp83867_get_wol, + .set_wol = dp83867_set_wol, + /* IRQ related */ .ack_interrupt = dp83867_ack_interrupt, .config_intr = dp83867_config_intr, |