void bcmgenet_wol_power_up_cfg(struct bcmgenet_priv *priv, enum bcmgenet_power_mode mode) { u32 cpu_mask_set; u32 reg; if (mode != GENET_POWER_WOL_MAGIC) { netif_err(priv, wol, priv->dev, "invalid mode: %d\n", mode); return; } reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL); reg &= ~MPD_EN; bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL); /* Disable CRC Forward */ reg = bcmgenet_umac_readl(priv, UMAC_CMD); reg &= ~CMD_CRC_FWD; bcmgenet_umac_writel(priv, reg, UMAC_CMD); priv->crc_fwd_en = 0; /* Stop monitoring magic packet IRQ */ cpu_mask_set = UMAC_IRQ_MPD_R; /* Stop monitoring magic packet IRQ */ bcmgenet_intrl2_0_writel(priv, cpu_mask_set, INTRL2_CPU_MASK_SET); }
int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv, enum bcmgenet_power_mode mode) { struct net_device *dev = priv->dev; u32 cpu_mask_clear; int retries = 0; u32 reg; if (mode != GENET_POWER_WOL_MAGIC) { netif_err(priv, wol, dev, "unsupported mode: %d\n", mode); return -EINVAL; } /* disable RX */ reg = bcmgenet_umac_readl(priv, UMAC_CMD); reg &= ~CMD_RX_EN; bcmgenet_umac_writel(priv, reg, UMAC_CMD); mdelay(10); reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL); reg |= MPD_EN; bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL); /* Do not leave UniMAC in MPD mode only */ retries = bcmgenet_poll_wol_status(priv); if (retries < 0) { reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL); reg &= ~MPD_EN; bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL); return retries; } netif_dbg(priv, wol, dev, "MPD WOL-ready status set after %d msec\n", retries); /* Enable CRC forward */ reg = bcmgenet_umac_readl(priv, UMAC_CMD); priv->crc_fwd_en = 1; reg |= CMD_CRC_FWD; /* Receiver must be enabled for WOL MP detection */ reg |= CMD_RX_EN; bcmgenet_umac_writel(priv, reg, UMAC_CMD); if (priv->hw_params->flags & GENET_HAS_EXT) { reg = bcmgenet_ext_readl(priv, EXT_EXT_PWR_MGMT); reg &= ~EXT_ENERGY_DET_MASK; bcmgenet_ext_writel(priv, reg, EXT_EXT_PWR_MGMT); } /* Enable the MPD interrupt */ cpu_mask_clear = UMAC_IRQ_MPD_R; bcmgenet_intrl2_0_writel(priv, cpu_mask_clear, INTRL2_CPU_MASK_CLEAR); return 0; }
/* ethtool function - set WOL (Wake on LAN) settings. * Only for magic packet detection mode. */ int bcmgenet_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { struct bcmgenet_priv *priv = netdev_priv(dev); struct device *kdev = &priv->pdev->dev; u32 reg; if (!device_can_wakeup(kdev)) return -ENOTSUPP; if (wol->wolopts & ~(WAKE_MAGIC | WAKE_MAGICSECURE)) return -EINVAL; reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL); if (wol->wolopts & WAKE_MAGICSECURE) { bcmgenet_umac_writel(priv, get_unaligned_be16(&wol->sopass[0]), UMAC_MPD_PW_MS); bcmgenet_umac_writel(priv, get_unaligned_be32(&wol->sopass[2]), UMAC_MPD_PW_LS); reg |= MPD_PW_EN; } else { reg &= ~MPD_PW_EN; } bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL); /* Flag the device and relevant IRQ as wakeup capable */ if (wol->wolopts) { device_set_wakeup_enable(kdev, 1); /* Avoid unbalanced enable_irq_wake calls */ if (priv->wol_irq_disabled) enable_irq_wake(priv->wol_irq); priv->wol_irq_disabled = false; } else { device_set_wakeup_enable(kdev, 0); /* Avoid unbalanced disable_irq_wake calls */ if (!priv->wol_irq_disabled) disable_irq_wake(priv->wol_irq); priv->wol_irq_disabled = true; } priv->wolopts = wol->wolopts; return 0; }
/* write a value to the MII */ static int bcmgenet_mii_write(struct mii_bus *bus, int phy_id, int location, u16 val) { struct net_device *dev = bus->priv; struct bcmgenet_priv *priv = netdev_priv(dev); u32 reg; bcmgenet_umac_writel(priv, (MDIO_WR | (phy_id << MDIO_PMD_SHIFT) | (location << MDIO_REG_SHIFT) | (0xffff & val)), UMAC_MDIO_CMD); reg = bcmgenet_umac_readl(priv, UMAC_MDIO_CMD); reg |= MDIO_START_BUSY; bcmgenet_umac_writel(priv, reg, UMAC_MDIO_CMD); wait_event_timeout(priv->wq, !(bcmgenet_umac_readl(priv, UMAC_MDIO_CMD) & MDIO_START_BUSY), HZ / 100); return 0; }
/* read a value from the MII */ static int bcmgenet_mii_read(struct mii_bus *bus, int phy_id, int location) { int ret; struct net_device *dev = bus->priv; struct bcmgenet_priv *priv = netdev_priv(dev); u32 reg; bcmgenet_umac_writel(priv, (MDIO_RD | (phy_id << MDIO_PMD_SHIFT) | (location << MDIO_REG_SHIFT)), UMAC_MDIO_CMD); /* Start MDIO transaction*/ reg = bcmgenet_umac_readl(priv, UMAC_MDIO_CMD); reg |= MDIO_START_BUSY; bcmgenet_umac_writel(priv, reg, UMAC_MDIO_CMD); wait_event_timeout(priv->wq, !(bcmgenet_umac_readl(priv, UMAC_MDIO_CMD) & MDIO_START_BUSY), HZ / 100); ret = bcmgenet_umac_readl(priv, UMAC_MDIO_CMD); if (ret & MDIO_READ_FAIL) return -EIO; return ret & 0xffff; }
/* setup netdev link state when PHY link status change and * update UMAC and RGMII block when link up */ void bcmgenet_mii_setup(struct net_device *dev) { struct bcmgenet_priv *priv = netdev_priv(dev); struct phy_device *phydev = priv->phydev; u32 reg, cmd_bits = 0; bool status_changed = false; if (priv->old_link != phydev->link) { status_changed = true; priv->old_link = phydev->link; } if (phydev->link) { /* check speed/duplex/pause changes */ if (priv->old_speed != phydev->speed) { status_changed = true; priv->old_speed = phydev->speed; } if (priv->old_duplex != phydev->duplex) { status_changed = true; priv->old_duplex = phydev->duplex; } if (priv->old_pause != phydev->pause) { status_changed = true; priv->old_pause = phydev->pause; } /* done if nothing has changed */ if (!status_changed) return; /* speed */ if (phydev->speed == SPEED_1000) cmd_bits = UMAC_SPEED_1000; else if (phydev->speed == SPEED_100) cmd_bits = UMAC_SPEED_100; else cmd_bits = UMAC_SPEED_10; cmd_bits <<= CMD_SPEED_SHIFT; /* duplex */ if (phydev->duplex != DUPLEX_FULL) cmd_bits |= CMD_HD_EN; /* pause capability */ if (!phydev->pause) cmd_bits |= CMD_RX_PAUSE_IGNORE | CMD_TX_PAUSE_IGNORE; /* * Program UMAC and RGMII block based on established * link speed, duplex, and pause. The speed set in * umac->cmd tell RGMII block which clock to use for * transmit -- 25MHz(100Mbps) or 125MHz(1Gbps). * Receive clock is provided by the PHY. */ reg = bcmgenet_ext_readl(priv, EXT_RGMII_OOB_CTRL); reg &= ~OOB_DISABLE; reg |= RGMII_LINK; bcmgenet_ext_writel(priv, reg, EXT_RGMII_OOB_CTRL); reg = bcmgenet_umac_readl(priv, UMAC_CMD); reg &= ~((CMD_SPEED_MASK << CMD_SPEED_SHIFT) | CMD_HD_EN | CMD_RX_PAUSE_IGNORE | CMD_TX_PAUSE_IGNORE); reg |= cmd_bits; bcmgenet_umac_writel(priv, reg, UMAC_CMD); } else { /* done if nothing has changed */ if (!status_changed) return; /* needed for MoCA fixed PHY to reflect correct link status */ netif_carrier_off(dev); } phy_print_status(phydev); }
/* setup netdev link state when PHY link status change and * update UMAC and RGMII block when link up */ static void bcmgenet_mii_setup(struct net_device *dev) { struct bcmgenet_priv *priv = netdev_priv(dev); struct phy_device *phydev = priv->phydev; u32 reg, cmd_bits = 0; unsigned int status_changed = 0; if (priv->old_link != phydev->link) { status_changed = 1; priv->old_link = phydev->link; } if (phydev->link) { /* program UMAC and RGMII block based on established link * speed, pause, and duplex. * the speed set in umac->cmd tell RGMII block which clock * 25MHz(100Mbps)/125MHz(1Gbps) to use for transmit. * receive clock is provided by PHY. */ reg = bcmgenet_ext_readl(priv, EXT_RGMII_OOB_CTRL); reg &= ~OOB_DISABLE; reg |= RGMII_LINK; bcmgenet_ext_writel(priv, reg, EXT_RGMII_OOB_CTRL); /* speed */ if (phydev->speed == SPEED_1000) cmd_bits = UMAC_SPEED_1000; else if (phydev->speed == SPEED_100) cmd_bits = UMAC_SPEED_100; else cmd_bits = UMAC_SPEED_10; cmd_bits <<= CMD_SPEED_SHIFT; if (priv->old_duplex != phydev->duplex) { status_changed = 1; priv->old_duplex = phydev->duplex; } /* duplex */ if (phydev->duplex != DUPLEX_FULL) cmd_bits |= CMD_HD_EN; if (priv->old_pause != phydev->pause) { status_changed = 1; priv->old_pause = phydev->pause; } /* pause capability */ if (!phydev->pause) cmd_bits |= CMD_RX_PAUSE_IGNORE | CMD_TX_PAUSE_IGNORE; } if (status_changed) { reg = bcmgenet_umac_readl(priv, UMAC_CMD); reg &= ~((CMD_SPEED_MASK << CMD_SPEED_SHIFT) | CMD_HD_EN | CMD_RX_PAUSE_IGNORE | CMD_TX_PAUSE_IGNORE); reg |= cmd_bits; bcmgenet_umac_writel(priv, reg, UMAC_CMD); phy_print_status(phydev); } }