Skip to content

Commit dfaed0e

Browse files
committed
Merge branch 'net-dsa-microchip-provide-wake-on-lan-support-part-2'
Oleksij Rempel says: ==================== net: dsa: microchip: provide Wake on LAN support (part 2) This patch series introduces extensive Wake on LAN (WoL) support for the Microchip KSZ9477 family of switches, coupled with some code refactoring and error handling enhancements. The principal aim is to enable and manage Wake on Magic Packet and other PHY event triggers for waking up the system, whilst ensuring that the switch isn't reset during a shutdown if WoL is active. The Wake on LAN functionality is optional and is particularly beneficial if the PME pins are connected to the SoC as a wake source or to a PMIC that can enable or wake the SoC. ==================== Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
2 parents 3a04927 + 8afb91a commit dfaed0e

File tree

6 files changed

+200
-24
lines changed

6 files changed

+200
-24
lines changed

drivers/net/dsa/microchip/ksz9477.c

Lines changed: 99 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@ static int ksz9477_handle_wake_reason(struct ksz_device *dev, int port)
8181
if (!pme_status)
8282
return 0;
8383

84-
dev_dbg(dev->dev, "Wake event on port %d due to:%s%s\n", port,
84+
dev_dbg(dev->dev, "Wake event on port %d due to:%s%s%s\n", port,
85+
pme_status & PME_WOL_MAGICPKT ? " \"Magic Packet\"" : "",
8586
pme_status & PME_WOL_LINKUP ? " \"Link Up\"" : "",
8687
pme_status & PME_WOL_ENERGY ? " \"Enery detect\"" : "");
8788

@@ -109,10 +110,19 @@ void ksz9477_get_wol(struct ksz_device *dev, int port,
109110

110111
wol->supported = WAKE_PHY;
111112

113+
/* Check if the current MAC address on this port can be set
114+
* as global for WAKE_MAGIC support. The result may vary
115+
* dynamically based on other ports configurations.
116+
*/
117+
if (ksz_is_port_mac_global_usable(dev->ds, port))
118+
wol->supported |= WAKE_MAGIC;
119+
112120
ret = ksz_pread8(dev, port, REG_PORT_PME_CTRL, &pme_ctrl);
113121
if (ret)
114122
return;
115123

124+
if (pme_ctrl & PME_WOL_MAGICPKT)
125+
wol->wolopts |= WAKE_MAGIC;
116126
if (pme_ctrl & (PME_WOL_LINKUP | PME_WOL_ENERGY))
117127
wol->wolopts |= WAKE_PHY;
118128
}
@@ -134,10 +144,12 @@ void ksz9477_get_wol(struct ksz_device *dev, int port,
134144
int ksz9477_set_wol(struct ksz_device *dev, int port,
135145
struct ethtool_wolinfo *wol)
136146
{
137-
u8 pme_ctrl = 0;
147+
u8 pme_ctrl = 0, pme_ctrl_old = 0;
148+
bool magic_switched_off;
149+
bool magic_switched_on;
138150
int ret;
139151

140-
if (wol->wolopts & ~WAKE_PHY)
152+
if (wol->wolopts & ~(WAKE_PHY | WAKE_MAGIC))
141153
return -EINVAL;
142154

143155
if (!dev->wakeup_source)
@@ -147,10 +159,82 @@ int ksz9477_set_wol(struct ksz_device *dev, int port,
147159
if (ret)
148160
return ret;
149161

162+
if (wol->wolopts & WAKE_MAGIC)
163+
pme_ctrl |= PME_WOL_MAGICPKT;
150164
if (wol->wolopts & WAKE_PHY)
151165
pme_ctrl |= PME_WOL_LINKUP | PME_WOL_ENERGY;
152166

153-
return ksz_pwrite8(dev, port, REG_PORT_PME_CTRL, pme_ctrl);
167+
ret = ksz_pread8(dev, port, REG_PORT_PME_CTRL, &pme_ctrl_old);
168+
if (ret)
169+
return ret;
170+
171+
if (pme_ctrl_old == pme_ctrl)
172+
return 0;
173+
174+
magic_switched_off = (pme_ctrl_old & PME_WOL_MAGICPKT) &&
175+
!(pme_ctrl & PME_WOL_MAGICPKT);
176+
magic_switched_on = !(pme_ctrl_old & PME_WOL_MAGICPKT) &&
177+
(pme_ctrl & PME_WOL_MAGICPKT);
178+
179+
/* To keep reference count of MAC address, we should do this
180+
* operation only on change of WOL settings.
181+
*/
182+
if (magic_switched_on) {
183+
ret = ksz_switch_macaddr_get(dev->ds, port, NULL);
184+
if (ret)
185+
return ret;
186+
} else if (magic_switched_off) {
187+
ksz_switch_macaddr_put(dev->ds);
188+
}
189+
190+
ret = ksz_pwrite8(dev, port, REG_PORT_PME_CTRL, pme_ctrl);
191+
if (ret) {
192+
if (magic_switched_on)
193+
ksz_switch_macaddr_put(dev->ds);
194+
return ret;
195+
}
196+
197+
return 0;
198+
}
199+
200+
/**
201+
* ksz9477_wol_pre_shutdown - Prepares the switch device for shutdown while
202+
* considering Wake-on-LAN (WoL) settings.
203+
* @dev: The switch device structure.
204+
* @wol_enabled: Pointer to a boolean which will be set to true if WoL is
205+
* enabled on any port.
206+
*
207+
* This function prepares the switch device for a safe shutdown while taking
208+
* into account the Wake-on-LAN (WoL) settings on the user ports. It updates
209+
* the wol_enabled flag accordingly to reflect whether WoL is active on any
210+
* port.
211+
*/
212+
void ksz9477_wol_pre_shutdown(struct ksz_device *dev, bool *wol_enabled)
213+
{
214+
struct dsa_port *dp;
215+
int ret;
216+
217+
*wol_enabled = false;
218+
219+
if (!dev->wakeup_source)
220+
return;
221+
222+
dsa_switch_for_each_user_port(dp, dev->ds) {
223+
u8 pme_ctrl = 0;
224+
225+
ret = ksz_pread8(dev, dp->index, REG_PORT_PME_CTRL, &pme_ctrl);
226+
if (!ret && pme_ctrl)
227+
*wol_enabled = true;
228+
229+
/* make sure there are no pending wake events which would
230+
* prevent the device from going to sleep/shutdown.
231+
*/
232+
ksz9477_handle_wake_reason(dev, dp->index);
233+
}
234+
235+
/* Now we are save to enable PME pin. */
236+
if (*wol_enabled)
237+
ksz_write8(dev, REG_SW_PME_CTRL, PME_ENABLE);
154238
}
155239

156240
static int ksz9477_wait_vlan_ctrl_ready(struct ksz_device *dev)
@@ -1106,6 +1190,11 @@ void ksz9477_port_setup(struct ksz_device *dev, int port, bool cpu_port)
11061190

11071191
/* clear pending wake flags */
11081192
ksz9477_handle_wake_reason(dev, port);
1193+
1194+
/* Disable all WoL options by default. Otherwise
1195+
* ksz_switch_macaddr_get/put logic will not work properly.
1196+
*/
1197+
ksz_pwrite8(dev, port, REG_PORT_PME_CTRL, 0);
11091198
}
11101199

11111200
void ksz9477_config_cpu_port(struct dsa_switch *ds)
@@ -1228,6 +1317,12 @@ int ksz9477_setup(struct dsa_switch *ds)
12281317
/* enable global MIB counter freeze function */
12291318
ksz_cfg(dev, REG_SW_MAC_CTRL_6, SW_MIB_COUNTER_FREEZE, true);
12301319

1320+
/* Make sure PME (WoL) is not enabled. If requested, it will be
1321+
* enabled by ksz9477_wol_pre_shutdown(). Otherwise, some PMICs do not
1322+
* like PME events changes before shutdown.
1323+
*/
1324+
ksz_write8(dev, REG_SW_PME_CTRL, 0);
1325+
12311326
return 0;
12321327
}
12331328

drivers/net/dsa/microchip/ksz9477.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ void ksz9477_get_wol(struct ksz_device *dev, int port,
6262
struct ethtool_wolinfo *wol);
6363
int ksz9477_set_wol(struct ksz_device *dev, int port,
6464
struct ethtool_wolinfo *wol);
65+
void ksz9477_wol_pre_shutdown(struct ksz_device *dev, bool *wol_enabled);
6566

6667
int ksz9477_port_acl_init(struct ksz_device *dev, int port);
6768
void ksz9477_port_acl_free(struct ksz_device *dev, int port);

drivers/net/dsa/microchip/ksz9477_i2c.c

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,7 @@ static void ksz9477_i2c_shutdown(struct i2c_client *i2c)
6666
if (!dev)
6767
return;
6868

69-
if (dev->dev_ops->reset)
70-
dev->dev_ops->reset(dev);
71-
72-
dsa_switch_shutdown(dev->ds);
69+
ksz_switch_shutdown(dev);
7370

7471
i2c_set_clientdata(i2c, NULL);
7572
}

drivers/net/dsa/microchip/ksz_common.c

Lines changed: 92 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,7 @@ static const struct ksz_dev_ops ksz9477_dev_ops = {
321321
.phylink_mac_link_up = ksz9477_phylink_mac_link_up,
322322
.get_wol = ksz9477_get_wol,
323323
.set_wol = ksz9477_set_wol,
324+
.wol_pre_shutdown = ksz9477_wol_pre_shutdown,
324325
.config_cpu_port = ksz9477_config_cpu_port,
325326
.tc_cbs_set_cinc = ksz9477_tc_cbs_set_cinc,
326327
.enable_stp_addr = ksz9477_enable_stp_addr,
@@ -3569,6 +3570,7 @@ static int ksz_port_set_mac_address(struct dsa_switch *ds, int port,
35693570
const unsigned char *addr)
35703571
{
35713572
struct dsa_port *dp = dsa_to_port(ds, port);
3573+
struct ethtool_wolinfo wol;
35723574

35733575
if (dp->hsr_dev) {
35743576
dev_err(ds->dev,
@@ -3577,25 +3579,69 @@ static int ksz_port_set_mac_address(struct dsa_switch *ds, int port,
35773579
return -EBUSY;
35783580
}
35793581

3582+
ksz_get_wol(ds, dp->index, &wol);
3583+
if (wol.wolopts & WAKE_MAGIC) {
3584+
dev_err(ds->dev,
3585+
"Cannot change MAC address on port %d with active Wake on Magic Packet\n",
3586+
port);
3587+
return -EBUSY;
3588+
}
3589+
35803590
return 0;
35813591
}
35823592

3583-
/* Program the switch's MAC address register with the MAC address of the
3584-
* requesting user port. This single address is used by the switch for multiple
3585-
* features, like HSR self-address filtering and WoL. Other user ports are
3586-
* allowed to share ownership of this address as long as their MAC address is
3587-
* the same. The user ports' MAC addresses must not change while they have
3588-
* ownership of the switch MAC address.
3593+
/**
3594+
* ksz_is_port_mac_global_usable - Check if the MAC address on a given port
3595+
* can be used as a global address.
3596+
* @ds: Pointer to the DSA switch structure.
3597+
* @port: The port number on which the MAC address is to be checked.
3598+
*
3599+
* This function examines the MAC address set on the specified port and
3600+
* determines if it can be used as a global address for the switch.
3601+
*
3602+
* Return: true if the port's MAC address can be used as a global address, false
3603+
* otherwise.
35893604
*/
3590-
static int ksz_switch_macaddr_get(struct dsa_switch *ds, int port,
3591-
struct netlink_ext_ack *extack)
3605+
bool ksz_is_port_mac_global_usable(struct dsa_switch *ds, int port)
3606+
{
3607+
struct net_device *user = dsa_to_port(ds, port)->user;
3608+
const unsigned char *addr = user->dev_addr;
3609+
struct ksz_switch_macaddr *switch_macaddr;
3610+
struct ksz_device *dev = ds->priv;
3611+
3612+
ASSERT_RTNL();
3613+
3614+
switch_macaddr = dev->switch_macaddr;
3615+
if (switch_macaddr && !ether_addr_equal(switch_macaddr->addr, addr))
3616+
return false;
3617+
3618+
return true;
3619+
}
3620+
3621+
/**
3622+
* ksz_switch_macaddr_get - Program the switch's MAC address register.
3623+
* @ds: DSA switch instance.
3624+
* @port: Port number.
3625+
* @extack: Netlink extended acknowledgment.
3626+
*
3627+
* This function programs the switch's MAC address register with the MAC address
3628+
* of the requesting user port. This single address is used by the switch for
3629+
* multiple features like HSR self-address filtering and WoL. Other user ports
3630+
* can share ownership of this address as long as their MAC address is the same.
3631+
* The MAC addresses of user ports must not change while they have ownership of
3632+
* the switch MAC address.
3633+
*
3634+
* Return: 0 on success, or other error codes on failure.
3635+
*/
3636+
int ksz_switch_macaddr_get(struct dsa_switch *ds, int port,
3637+
struct netlink_ext_ack *extack)
35923638
{
35933639
struct net_device *user = dsa_to_port(ds, port)->user;
35943640
const unsigned char *addr = user->dev_addr;
35953641
struct ksz_switch_macaddr *switch_macaddr;
35963642
struct ksz_device *dev = ds->priv;
35973643
const u16 *regs = dev->info->regs;
3598-
int i;
3644+
int i, ret;
35993645

36003646
/* Make sure concurrent MAC address changes are blocked */
36013647
ASSERT_RTNL();
@@ -3622,13 +3668,23 @@ static int ksz_switch_macaddr_get(struct dsa_switch *ds, int port,
36223668
dev->switch_macaddr = switch_macaddr;
36233669

36243670
/* Program the switch MAC address to hardware */
3625-
for (i = 0; i < ETH_ALEN; i++)
3626-
ksz_write8(dev, regs[REG_SW_MAC_ADDR] + i, addr[i]);
3671+
for (i = 0; i < ETH_ALEN; i++) {
3672+
ret = ksz_write8(dev, regs[REG_SW_MAC_ADDR] + i, addr[i]);
3673+
if (ret)
3674+
goto macaddr_drop;
3675+
}
36273676

36283677
return 0;
3678+
3679+
macaddr_drop:
3680+
dev->switch_macaddr = NULL;
3681+
refcount_set(&switch_macaddr->refcount, 0);
3682+
kfree(switch_macaddr);
3683+
3684+
return ret;
36293685
}
36303686

3631-
static void ksz_switch_macaddr_put(struct dsa_switch *ds)
3687+
void ksz_switch_macaddr_put(struct dsa_switch *ds)
36323688
{
36333689
struct ksz_switch_macaddr *switch_macaddr;
36343690
struct ksz_device *dev = ds->priv;
@@ -3790,6 +3846,30 @@ struct ksz_device *ksz_switch_alloc(struct device *base, void *priv)
37903846
}
37913847
EXPORT_SYMBOL(ksz_switch_alloc);
37923848

3849+
/**
3850+
* ksz_switch_shutdown - Shutdown routine for the switch device.
3851+
* @dev: The switch device structure.
3852+
*
3853+
* This function is responsible for initiating a shutdown sequence for the
3854+
* switch device. It invokes the reset operation defined in the device
3855+
* operations, if available, to reset the switch. Subsequently, it calls the
3856+
* DSA framework's shutdown function to ensure a proper shutdown of the DSA
3857+
* switch.
3858+
*/
3859+
void ksz_switch_shutdown(struct ksz_device *dev)
3860+
{
3861+
bool wol_enabled = false;
3862+
3863+
if (dev->dev_ops->wol_pre_shutdown)
3864+
dev->dev_ops->wol_pre_shutdown(dev, &wol_enabled);
3865+
3866+
if (dev->dev_ops->reset && !wol_enabled)
3867+
dev->dev_ops->reset(dev);
3868+
3869+
dsa_switch_shutdown(dev->ds);
3870+
}
3871+
EXPORT_SYMBOL(ksz_switch_shutdown);
3872+
37933873
static void ksz_parse_rgmii_delay(struct ksz_device *dev, int port_num,
37943874
struct device_node *port_dn)
37953875
{

drivers/net/dsa/microchip/ksz_common.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,7 @@ struct ksz_dev_ops {
378378
struct ethtool_wolinfo *wol);
379379
int (*set_wol)(struct ksz_device *dev, int port,
380380
struct ethtool_wolinfo *wol);
381+
void (*wol_pre_shutdown)(struct ksz_device *dev, bool *wol_enabled);
381382
void (*config_cpu_port)(struct dsa_switch *ds);
382383
int (*enable_stp_addr)(struct ksz_device *dev);
383384
int (*reset)(struct ksz_device *dev);
@@ -390,12 +391,17 @@ int ksz_switch_register(struct ksz_device *dev);
390391
void ksz_switch_remove(struct ksz_device *dev);
391392

392393
void ksz_init_mib_timer(struct ksz_device *dev);
394+
bool ksz_is_port_mac_global_usable(struct dsa_switch *ds, int port);
393395
void ksz_r_mib_stats64(struct ksz_device *dev, int port);
394396
void ksz88xx_r_mib_stats64(struct ksz_device *dev, int port);
395397
void ksz_port_stp_state_set(struct dsa_switch *ds, int port, u8 state);
396398
bool ksz_get_gbit(struct ksz_device *dev, int port);
397399
phy_interface_t ksz_get_xmii(struct ksz_device *dev, int port, bool gbit);
398400
extern const struct ksz_chip_data ksz_switch_chips[];
401+
int ksz_switch_macaddr_get(struct dsa_switch *ds, int port,
402+
struct netlink_ext_ack *extack);
403+
void ksz_switch_macaddr_put(struct dsa_switch *ds);
404+
void ksz_switch_shutdown(struct ksz_device *dev);
399405

400406
/* Common register access functions */
401407
static inline struct regmap *ksz_regmap_8(struct ksz_device *dev)

drivers/net/dsa/microchip/ksz_spi.c

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,7 @@ static void ksz_spi_shutdown(struct spi_device *spi)
114114
if (!dev)
115115
return;
116116

117-
if (dev->dev_ops->reset)
118-
dev->dev_ops->reset(dev);
119-
120-
dsa_switch_shutdown(dev->ds);
117+
ksz_switch_shutdown(dev);
121118

122119
spi_set_drvdata(spi, NULL);
123120
}

0 commit comments

Comments
 (0)