| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Broadcom GENET (Gigabit Ethernet) Wake-on-LAN support |
|---|
| 3 | 4 | * |
|---|
| 4 | | - * Copyright (c) 2014-2017 Broadcom |
|---|
| 5 | | - * |
|---|
| 6 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 7 | | - * it under the terms of the GNU General Public License version 2 as |
|---|
| 8 | | - * published by the Free Software Foundation. |
|---|
| 5 | + * Copyright (c) 2014-2020 Broadcom |
|---|
| 9 | 6 | */ |
|---|
| 10 | 7 | |
|---|
| 11 | 8 | #define pr_fmt(fmt) "bcmgenet_wol: " fmt |
|---|
| .. | .. |
|---|
| 44 | 41 | void bcmgenet_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) |
|---|
| 45 | 42 | { |
|---|
| 46 | 43 | struct bcmgenet_priv *priv = netdev_priv(dev); |
|---|
| 47 | | - u32 reg; |
|---|
| 44 | + struct device *kdev = &priv->pdev->dev; |
|---|
| 48 | 45 | |
|---|
| 49 | | - wol->supported = WAKE_MAGIC | WAKE_MAGICSECURE; |
|---|
| 46 | + if (!device_can_wakeup(kdev)) { |
|---|
| 47 | + wol->supported = 0; |
|---|
| 48 | + wol->wolopts = 0; |
|---|
| 49 | + return; |
|---|
| 50 | + } |
|---|
| 51 | + |
|---|
| 52 | + wol->supported = WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_FILTER; |
|---|
| 50 | 53 | wol->wolopts = priv->wolopts; |
|---|
| 51 | 54 | memset(wol->sopass, 0, sizeof(wol->sopass)); |
|---|
| 52 | 55 | |
|---|
| 53 | | - if (wol->wolopts & WAKE_MAGICSECURE) { |
|---|
| 54 | | - reg = bcmgenet_umac_readl(priv, UMAC_MPD_PW_MS); |
|---|
| 55 | | - put_unaligned_be16(reg, &wol->sopass[0]); |
|---|
| 56 | | - reg = bcmgenet_umac_readl(priv, UMAC_MPD_PW_LS); |
|---|
| 57 | | - put_unaligned_be32(reg, &wol->sopass[2]); |
|---|
| 58 | | - } |
|---|
| 56 | + if (wol->wolopts & WAKE_MAGICSECURE) |
|---|
| 57 | + memcpy(wol->sopass, priv->sopass, sizeof(priv->sopass)); |
|---|
| 59 | 58 | } |
|---|
| 60 | 59 | |
|---|
| 61 | 60 | /* ethtool function - set WOL (Wake on LAN) settings. |
|---|
| .. | .. |
|---|
| 65 | 64 | { |
|---|
| 66 | 65 | struct bcmgenet_priv *priv = netdev_priv(dev); |
|---|
| 67 | 66 | struct device *kdev = &priv->pdev->dev; |
|---|
| 68 | | - u32 reg; |
|---|
| 69 | 67 | |
|---|
| 70 | 68 | if (!device_can_wakeup(kdev)) |
|---|
| 71 | 69 | return -ENOTSUPP; |
|---|
| 72 | 70 | |
|---|
| 73 | | - if (wol->wolopts & ~(WAKE_MAGIC | WAKE_MAGICSECURE)) |
|---|
| 71 | + if (wol->wolopts & ~(WAKE_MAGIC | WAKE_MAGICSECURE | WAKE_FILTER)) |
|---|
| 74 | 72 | return -EINVAL; |
|---|
| 75 | 73 | |
|---|
| 76 | | - reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL); |
|---|
| 77 | | - if (wol->wolopts & WAKE_MAGICSECURE) { |
|---|
| 78 | | - bcmgenet_umac_writel(priv, get_unaligned_be16(&wol->sopass[0]), |
|---|
| 79 | | - UMAC_MPD_PW_MS); |
|---|
| 80 | | - bcmgenet_umac_writel(priv, get_unaligned_be32(&wol->sopass[2]), |
|---|
| 81 | | - UMAC_MPD_PW_LS); |
|---|
| 82 | | - reg |= MPD_PW_EN; |
|---|
| 83 | | - } else { |
|---|
| 84 | | - reg &= ~MPD_PW_EN; |
|---|
| 85 | | - } |
|---|
| 86 | | - bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL); |
|---|
| 74 | + if (wol->wolopts & WAKE_MAGICSECURE) |
|---|
| 75 | + memcpy(priv->sopass, wol->sopass, sizeof(priv->sopass)); |
|---|
| 87 | 76 | |
|---|
| 88 | 77 | /* Flag the device and relevant IRQ as wakeup capable */ |
|---|
| 89 | 78 | if (wol->wolopts) { |
|---|
| .. | .. |
|---|
| 123 | 112 | return retries; |
|---|
| 124 | 113 | } |
|---|
| 125 | 114 | |
|---|
| 115 | +static void bcmgenet_set_mpd_password(struct bcmgenet_priv *priv) |
|---|
| 116 | +{ |
|---|
| 117 | + bcmgenet_umac_writel(priv, get_unaligned_be16(&priv->sopass[0]), |
|---|
| 118 | + UMAC_MPD_PW_MS); |
|---|
| 119 | + bcmgenet_umac_writel(priv, get_unaligned_be32(&priv->sopass[2]), |
|---|
| 120 | + UMAC_MPD_PW_LS); |
|---|
| 121 | +} |
|---|
| 122 | + |
|---|
| 126 | 123 | int bcmgenet_wol_power_down_cfg(struct bcmgenet_priv *priv, |
|---|
| 127 | 124 | enum bcmgenet_power_mode mode) |
|---|
| 128 | 125 | { |
|---|
| 129 | 126 | struct net_device *dev = priv->dev; |
|---|
| 127 | + struct bcmgenet_rxnfc_rule *rule; |
|---|
| 128 | + u32 reg, hfb_ctrl_reg, hfb_enable = 0; |
|---|
| 130 | 129 | int retries = 0; |
|---|
| 131 | | - u32 reg; |
|---|
| 132 | 130 | |
|---|
| 133 | 131 | if (mode != GENET_POWER_WOL_MAGIC) { |
|---|
| 134 | 132 | netif_err(priv, wol, dev, "unsupported mode: %d\n", mode); |
|---|
| 135 | 133 | return -EINVAL; |
|---|
| 136 | 134 | } |
|---|
| 137 | 135 | |
|---|
| 138 | | - /* disable RX */ |
|---|
| 136 | + /* Can't suspend with WoL if MAC is still in reset */ |
|---|
| 139 | 137 | reg = bcmgenet_umac_readl(priv, UMAC_CMD); |
|---|
| 138 | + if (reg & CMD_SW_RESET) |
|---|
| 139 | + reg &= ~CMD_SW_RESET; |
|---|
| 140 | + |
|---|
| 141 | + /* disable RX */ |
|---|
| 140 | 142 | reg &= ~CMD_RX_EN; |
|---|
| 141 | 143 | bcmgenet_umac_writel(priv, reg, UMAC_CMD); |
|---|
| 142 | 144 | mdelay(10); |
|---|
| 143 | 145 | |
|---|
| 144 | | - reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL); |
|---|
| 145 | | - reg |= MPD_EN; |
|---|
| 146 | | - bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL); |
|---|
| 146 | + if (priv->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) { |
|---|
| 147 | + reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL); |
|---|
| 148 | + reg |= MPD_EN; |
|---|
| 149 | + if (priv->wolopts & WAKE_MAGICSECURE) { |
|---|
| 150 | + bcmgenet_set_mpd_password(priv); |
|---|
| 151 | + reg |= MPD_PW_EN; |
|---|
| 152 | + } |
|---|
| 153 | + bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL); |
|---|
| 154 | + } |
|---|
| 155 | + |
|---|
| 156 | + hfb_ctrl_reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL); |
|---|
| 157 | + if (priv->wolopts & WAKE_FILTER) { |
|---|
| 158 | + list_for_each_entry(rule, &priv->rxnfc_list, list) |
|---|
| 159 | + if (rule->fs.ring_cookie == RX_CLS_FLOW_WAKE) |
|---|
| 160 | + hfb_enable |= (1 << rule->fs.location); |
|---|
| 161 | + reg = (hfb_ctrl_reg & ~RBUF_HFB_EN) | RBUF_ACPI_EN; |
|---|
| 162 | + bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL); |
|---|
| 163 | + } |
|---|
| 147 | 164 | |
|---|
| 148 | 165 | /* Do not leave UniMAC in MPD mode only */ |
|---|
| 149 | 166 | retries = bcmgenet_poll_wol_status(priv); |
|---|
| 150 | 167 | if (retries < 0) { |
|---|
| 151 | 168 | reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL); |
|---|
| 152 | | - reg &= ~MPD_EN; |
|---|
| 169 | + reg &= ~(MPD_EN | MPD_PW_EN); |
|---|
| 153 | 170 | bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL); |
|---|
| 171 | + bcmgenet_hfb_reg_writel(priv, hfb_ctrl_reg, HFB_CTRL); |
|---|
| 154 | 172 | return retries; |
|---|
| 155 | 173 | } |
|---|
| 156 | 174 | |
|---|
| 157 | 175 | netif_dbg(priv, wol, dev, "MPD WOL-ready status set after %d msec\n", |
|---|
| 158 | 176 | retries); |
|---|
| 177 | + |
|---|
| 178 | + clk_prepare_enable(priv->clk_wol); |
|---|
| 179 | + priv->wol_active = 1; |
|---|
| 180 | + |
|---|
| 181 | + if (hfb_enable) { |
|---|
| 182 | + bcmgenet_hfb_reg_writel(priv, hfb_enable, |
|---|
| 183 | + HFB_FLT_ENABLE_V3PLUS + 4); |
|---|
| 184 | + hfb_ctrl_reg = RBUF_HFB_EN | RBUF_ACPI_EN; |
|---|
| 185 | + bcmgenet_hfb_reg_writel(priv, hfb_ctrl_reg, HFB_CTRL); |
|---|
| 186 | + } |
|---|
| 159 | 187 | |
|---|
| 160 | 188 | /* Enable CRC forward */ |
|---|
| 161 | 189 | reg = bcmgenet_umac_readl(priv, UMAC_CMD); |
|---|
| .. | .. |
|---|
| 165 | 193 | /* Receiver must be enabled for WOL MP detection */ |
|---|
| 166 | 194 | reg |= CMD_RX_EN; |
|---|
| 167 | 195 | bcmgenet_umac_writel(priv, reg, UMAC_CMD); |
|---|
| 196 | + |
|---|
| 197 | + reg = UMAC_IRQ_MPD_R; |
|---|
| 198 | + if (hfb_enable) |
|---|
| 199 | + reg |= UMAC_IRQ_HFB_SM | UMAC_IRQ_HFB_MM; |
|---|
| 200 | + |
|---|
| 201 | + bcmgenet_intrl2_0_writel(priv, reg, INTRL2_CPU_MASK_CLEAR); |
|---|
| 168 | 202 | |
|---|
| 169 | 203 | return 0; |
|---|
| 170 | 204 | } |
|---|
| .. | .. |
|---|
| 179 | 213 | return; |
|---|
| 180 | 214 | } |
|---|
| 181 | 215 | |
|---|
| 182 | | - reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL); |
|---|
| 183 | | - if (!(reg & MPD_EN)) |
|---|
| 184 | | - return; /* already powered up so skip the rest */ |
|---|
| 185 | | - reg &= ~MPD_EN; |
|---|
| 186 | | - bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL); |
|---|
| 216 | + if (!priv->wol_active) |
|---|
| 217 | + return; /* failed to suspend so skip the rest */ |
|---|
| 218 | + |
|---|
| 219 | + priv->wol_active = 0; |
|---|
| 220 | + clk_disable_unprepare(priv->clk_wol); |
|---|
| 221 | + priv->crc_fwd_en = 0; |
|---|
| 222 | + |
|---|
| 223 | + /* Disable Magic Packet Detection */ |
|---|
| 224 | + if (priv->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) { |
|---|
| 225 | + reg = bcmgenet_umac_readl(priv, UMAC_MPD_CTRL); |
|---|
| 226 | + if (!(reg & MPD_EN)) |
|---|
| 227 | + return; /* already reset so skip the rest */ |
|---|
| 228 | + reg &= ~(MPD_EN | MPD_PW_EN); |
|---|
| 229 | + bcmgenet_umac_writel(priv, reg, UMAC_MPD_CTRL); |
|---|
| 230 | + } |
|---|
| 231 | + |
|---|
| 232 | + /* Disable WAKE_FILTER Detection */ |
|---|
| 233 | + if (priv->wolopts & WAKE_FILTER) { |
|---|
| 234 | + reg = bcmgenet_hfb_reg_readl(priv, HFB_CTRL); |
|---|
| 235 | + if (!(reg & RBUF_ACPI_EN)) |
|---|
| 236 | + return; /* already reset so skip the rest */ |
|---|
| 237 | + reg &= ~(RBUF_HFB_EN | RBUF_ACPI_EN); |
|---|
| 238 | + bcmgenet_hfb_reg_writel(priv, reg, HFB_CTRL); |
|---|
| 239 | + } |
|---|
| 187 | 240 | |
|---|
| 188 | 241 | /* Disable CRC Forward */ |
|---|
| 189 | 242 | reg = bcmgenet_umac_readl(priv, UMAC_CMD); |
|---|
| 190 | 243 | reg &= ~CMD_CRC_FWD; |
|---|
| 191 | 244 | bcmgenet_umac_writel(priv, reg, UMAC_CMD); |
|---|
| 192 | | - priv->crc_fwd_en = 0; |
|---|
| 193 | 245 | } |
|---|