mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-29 04:34:45 -04:00
mac->phy_node is acquired via of_parse_phandle() in spl2sw_probe() and
stored in the mac private data, transferring ownership of the
device_node reference to mac. On driver removal, spl2sw_phy_remove()
disconnects the PHY but never drops that reference, so each
probe-then-remove cycle leaks one of_node refcount per port permanently.
Drop the reference after phy_disconnect(). While at it, remove the
redundant inner "if (ndev)" check; comm->ndev[i] was just verified
non-NULL on the line above.
Compile-tested only; no SP7021 hardware available.
Fixes: fd3040b939 ("net: ethernet: Add driver for Sunplus SP7021")
Signed-off-by: Shitalkumar Gandhi <shitalkumar.gandhi@cambiumnetworks.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Link: https://patch.msgid.link/f3bdd4c91f3e2269b4e256075f9dc70808b1b8e9.1782195965.git.shitalkumar.gandhi@cambiumnetworks.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
93 lines
2.1 KiB
C
93 lines
2.1 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/* Copyright Sunplus Technology Co., Ltd.
|
|
* All rights reserved.
|
|
*/
|
|
|
|
#include <linux/netdevice.h>
|
|
#include <linux/bitfield.h>
|
|
#include <linux/of_mdio.h>
|
|
|
|
#include "spl2sw_register.h"
|
|
#include "spl2sw_define.h"
|
|
#include "spl2sw_phy.h"
|
|
|
|
static void spl2sw_mii_link_change(struct net_device *ndev)
|
|
{
|
|
struct spl2sw_mac *mac = netdev_priv(ndev);
|
|
struct phy_device *phydev = ndev->phydev;
|
|
struct spl2sw_common *comm = mac->comm;
|
|
u32 reg;
|
|
|
|
reg = readl(comm->l2sw_reg_base + L2SW_MAC_FORCE_MODE);
|
|
|
|
if (phydev->link) {
|
|
reg |= FIELD_PREP(MAC_FORCE_RMII_LINK, mac->lan_port);
|
|
|
|
if (phydev->speed == 100) {
|
|
reg |= FIELD_PREP(MAC_FORCE_RMII_SPD, mac->lan_port);
|
|
} else {
|
|
reg &= FIELD_PREP(MAC_FORCE_RMII_SPD, ~mac->lan_port) |
|
|
~MAC_FORCE_RMII_SPD;
|
|
}
|
|
|
|
if (phydev->duplex) {
|
|
reg |= FIELD_PREP(MAC_FORCE_RMII_DPX, mac->lan_port);
|
|
} else {
|
|
reg &= FIELD_PREP(MAC_FORCE_RMII_DPX, ~mac->lan_port) |
|
|
~MAC_FORCE_RMII_DPX;
|
|
}
|
|
|
|
if (phydev->pause) {
|
|
reg |= FIELD_PREP(MAC_FORCE_RMII_FC, mac->lan_port);
|
|
} else {
|
|
reg &= FIELD_PREP(MAC_FORCE_RMII_FC, ~mac->lan_port) |
|
|
~MAC_FORCE_RMII_FC;
|
|
}
|
|
} else {
|
|
reg &= FIELD_PREP(MAC_FORCE_RMII_LINK, ~mac->lan_port) |
|
|
~MAC_FORCE_RMII_LINK;
|
|
}
|
|
|
|
writel(reg, comm->l2sw_reg_base + L2SW_MAC_FORCE_MODE);
|
|
|
|
phy_print_status(phydev);
|
|
}
|
|
|
|
int spl2sw_phy_connect(struct spl2sw_common *comm)
|
|
{
|
|
struct phy_device *phydev;
|
|
struct net_device *ndev;
|
|
struct spl2sw_mac *mac;
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_NETDEV_NUM; i++)
|
|
if (comm->ndev[i]) {
|
|
ndev = comm->ndev[i];
|
|
mac = netdev_priv(ndev);
|
|
phydev = of_phy_connect(ndev, mac->phy_node, spl2sw_mii_link_change,
|
|
0, mac->phy_mode);
|
|
if (!phydev)
|
|
return -ENODEV;
|
|
|
|
phy_support_asym_pause(phydev);
|
|
phy_attached_info(phydev);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void spl2sw_phy_remove(struct spl2sw_common *comm)
|
|
{
|
|
struct net_device *ndev;
|
|
struct spl2sw_mac *mac;
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_NETDEV_NUM; i++)
|
|
if (comm->ndev[i]) {
|
|
ndev = comm->ndev[i];
|
|
mac = netdev_priv(ndev);
|
|
phy_disconnect(ndev->phydev);
|
|
of_node_put(mac->phy_node);
|
|
}
|
|
}
|