aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/dsa/microchip/ksz_common.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/dsa/microchip/ksz_common.c')
-rw-r--r--drivers/net/dsa/microchip/ksz_common.c163
1 files changed, 136 insertions, 27 deletions
diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
index b800ace40ce1..9545aed905f5 100644
--- a/drivers/net/dsa/microchip/ksz_common.c
+++ b/drivers/net/dsa/microchip/ksz_common.c
@@ -277,6 +277,7 @@ static const struct ksz_dev_ops ksz8_dev_ops = {
.mirror_add = ksz8_port_mirror_add,
.mirror_del = ksz8_port_mirror_del,
.get_caps = ksz8_get_caps,
+ .phylink_mac_link_up = ksz8_phylink_mac_link_up,
.config_cpu_port = ksz8_config_cpu_port,
.enable_stp_addr = ksz8_enable_stp_addr,
.reset = ksz8_reset_switch,
@@ -319,6 +320,9 @@ static const struct ksz_dev_ops ksz9477_dev_ops = {
.mdb_del = ksz9477_mdb_del,
.change_mtu = ksz9477_change_mtu,
.phylink_mac_link_up = ksz9477_phylink_mac_link_up,
+ .get_wol = ksz9477_get_wol,
+ .set_wol = ksz9477_set_wol,
+ .wol_pre_shutdown = ksz9477_wol_pre_shutdown,
.config_cpu_port = ksz9477_config_cpu_port,
.tc_cbs_set_cinc = ksz9477_tc_cbs_set_cinc,
.enable_stp_addr = ksz9477_enable_stp_addr,
@@ -441,6 +445,7 @@ static const u8 ksz8795_shifts[] = {
};
static const u16 ksz8863_regs[] = {
+ [REG_SW_MAC_ADDR] = 0x70,
[REG_IND_CTRL_0] = 0x79,
[REG_IND_DATA_8] = 0x7B,
[REG_IND_DATA_CHECK] = 0x7B,
@@ -1945,14 +1950,14 @@ static int ksz_irq_phy_setup(struct ksz_device *dev)
ret = irq;
goto out;
}
- ds->slave_mii_bus->irq[phy] = irq;
+ ds->user_mii_bus->irq[phy] = irq;
}
}
return 0;
out:
while (phy--)
if (BIT(phy) & ds->phys_mii_mask)
- irq_dispose_mapping(ds->slave_mii_bus->irq[phy]);
+ irq_dispose_mapping(ds->user_mii_bus->irq[phy]);
return ret;
}
@@ -1964,7 +1969,7 @@ static void ksz_irq_phy_free(struct ksz_device *dev)
for (phy = 0; phy < KSZ_MAX_NUM_PORTS; phy++)
if (BIT(phy) & ds->phys_mii_mask)
- irq_dispose_mapping(ds->slave_mii_bus->irq[phy]);
+ irq_dispose_mapping(ds->user_mii_bus->irq[phy]);
}
static int ksz_mdio_register(struct ksz_device *dev)
@@ -1987,12 +1992,12 @@ static int ksz_mdio_register(struct ksz_device *dev)
bus->priv = dev;
bus->read = ksz_sw_mdio_read;
bus->write = ksz_sw_mdio_write;
- bus->name = "ksz slave smi";
+ bus->name = "ksz user smi";
snprintf(bus->id, MII_BUS_ID_SIZE, "SMI-%d", ds->index);
bus->parent = ds->dev;
bus->phy_mask = ~ds->phys_mii_mask;
- ds->slave_mii_bus = bus;
+ ds->user_mii_bus = bus;
if (dev->irq > 0) {
ret = ksz_irq_phy_setup(dev);
@@ -2344,7 +2349,7 @@ static void ksz_mib_read_work(struct work_struct *work)
if (!p->read) {
const struct dsa_port *dp = dsa_to_port(dev->ds, i);
- if (!netif_carrier_ok(dp->slave))
+ if (!netif_carrier_ok(dp->user))
mib->cnt_ptr = dev->info->reg_mib_cnt;
}
port_r_cnt(dev, i);
@@ -2464,7 +2469,7 @@ static void ksz_get_ethtool_stats(struct dsa_switch *ds, int port,
mutex_lock(&mib->cnt_mutex);
/* Only read dropped counters if no link. */
- if (!netif_carrier_ok(dp->slave))
+ if (!netif_carrier_ok(dp->user))
mib->cnt_ptr = dev->info->reg_mib_cnt;
port_r_cnt(dev, port);
memcpy(buf, mib->counters, dev->info->mib_cnt * sizeof(u64));
@@ -2574,7 +2579,7 @@ static int ksz_port_setup(struct dsa_switch *ds, int port)
if (!dsa_is_user_port(ds, port))
return 0;
- /* setup slave port */
+ /* setup user port */
dev->dev_ops->port_setup(dev, port, false);
/* port_stp_state_set() will be called after to enable the port so
@@ -2962,8 +2967,10 @@ static void ksz_phylink_mac_config(struct dsa_switch *ds, int port,
{
struct ksz_device *dev = ds->priv;
- if (ksz_is_ksz88x3(dev))
+ if (ksz_is_ksz88x3(dev)) {
+ dev->ports[port].manual_flow = !(state->pause & MLO_PAUSE_AN);
return;
+ }
/* Internal PHYs */
if (dev->info->internal_phy[port])
@@ -3106,10 +3113,8 @@ static void ksz_phylink_mac_link_up(struct dsa_switch *ds, int port,
{
struct ksz_device *dev = ds->priv;
- if (dev->dev_ops->phylink_mac_link_up)
- dev->dev_ops->phylink_mac_link_up(dev, port, mode, interface,
- phydev, speed, duplex,
- tx_pause, rx_pause);
+ dev->dev_ops->phylink_mac_link_up(dev, port, mode, interface, phydev,
+ speed, duplex, tx_pause, rx_pause);
}
static int ksz_switch_detect(struct ksz_device *dev)
@@ -3542,10 +3547,31 @@ static int ksz_setup_tc(struct dsa_switch *ds, int port,
}
}
+static void ksz_get_wol(struct dsa_switch *ds, int port,
+ struct ethtool_wolinfo *wol)
+{
+ struct ksz_device *dev = ds->priv;
+
+ if (dev->dev_ops->get_wol)
+ dev->dev_ops->get_wol(dev, port, wol);
+}
+
+static int ksz_set_wol(struct dsa_switch *ds, int port,
+ struct ethtool_wolinfo *wol)
+{
+ struct ksz_device *dev = ds->priv;
+
+ if (dev->dev_ops->set_wol)
+ return dev->dev_ops->set_wol(dev, port, wol);
+
+ return -EOPNOTSUPP;
+}
+
static int ksz_port_set_mac_address(struct dsa_switch *ds, int port,
const unsigned char *addr)
{
struct dsa_port *dp = dsa_to_port(ds, port);
+ struct ethtool_wolinfo wol;
if (dp->hsr_dev) {
dev_err(ds->dev,
@@ -3554,25 +3580,69 @@ static int ksz_port_set_mac_address(struct dsa_switch *ds, int port,
return -EBUSY;
}
+ ksz_get_wol(ds, dp->index, &wol);
+ if (wol.wolopts & WAKE_MAGIC) {
+ dev_err(ds->dev,
+ "Cannot change MAC address on port %d with active Wake on Magic Packet\n",
+ port);
+ return -EBUSY;
+ }
+
return 0;
}
-/* Program the switch's MAC address register with the MAC address of the
- * requesting user port. This single address is used by the switch for multiple
- * features, like HSR self-address filtering and WoL. Other user ports are
- * allowed to share ownership of this address as long as their MAC address is
- * the same. The user ports' MAC addresses must not change while they have
- * ownership of the switch MAC address.
+/**
+ * ksz_is_port_mac_global_usable - Check if the MAC address on a given port
+ * can be used as a global address.
+ * @ds: Pointer to the DSA switch structure.
+ * @port: The port number on which the MAC address is to be checked.
+ *
+ * This function examines the MAC address set on the specified port and
+ * determines if it can be used as a global address for the switch.
+ *
+ * Return: true if the port's MAC address can be used as a global address, false
+ * otherwise.
*/
-static int ksz_switch_macaddr_get(struct dsa_switch *ds, int port,
- struct netlink_ext_ack *extack)
+bool ksz_is_port_mac_global_usable(struct dsa_switch *ds, int port)
{
- struct net_device *slave = dsa_to_port(ds, port)->slave;
- const unsigned char *addr = slave->dev_addr;
+ struct net_device *user = dsa_to_port(ds, port)->user;
+ const unsigned char *addr = user->dev_addr;
+ struct ksz_switch_macaddr *switch_macaddr;
+ struct ksz_device *dev = ds->priv;
+
+ ASSERT_RTNL();
+
+ switch_macaddr = dev->switch_macaddr;
+ if (switch_macaddr && !ether_addr_equal(switch_macaddr->addr, addr))
+ return false;
+
+ return true;
+}
+
+/**
+ * ksz_switch_macaddr_get - Program the switch's MAC address register.
+ * @ds: DSA switch instance.
+ * @port: Port number.
+ * @extack: Netlink extended acknowledgment.
+ *
+ * This function programs the switch's MAC address register with the MAC address
+ * of the requesting user port. This single address is used by the switch for
+ * multiple features like HSR self-address filtering and WoL. Other user ports
+ * can share ownership of this address as long as their MAC address is the same.
+ * The MAC addresses of user ports must not change while they have ownership of
+ * the switch MAC address.
+ *
+ * Return: 0 on success, or other error codes on failure.
+ */
+int ksz_switch_macaddr_get(struct dsa_switch *ds, int port,
+ struct netlink_ext_ack *extack)
+{
+ struct net_device *user = dsa_to_port(ds, port)->user;
+ const unsigned char *addr = user->dev_addr;
struct ksz_switch_macaddr *switch_macaddr;
struct ksz_device *dev = ds->priv;
const u16 *regs = dev->info->regs;
- int i;
+ int i, ret;
/* Make sure concurrent MAC address changes are blocked */
ASSERT_RTNL();
@@ -3599,13 +3669,23 @@ static int ksz_switch_macaddr_get(struct dsa_switch *ds, int port,
dev->switch_macaddr = switch_macaddr;
/* Program the switch MAC address to hardware */
- for (i = 0; i < ETH_ALEN; i++)
- ksz_write8(dev, regs[REG_SW_MAC_ADDR] + i, addr[i]);
+ for (i = 0; i < ETH_ALEN; i++) {
+ ret = ksz_write8(dev, regs[REG_SW_MAC_ADDR] + i, addr[i]);
+ if (ret)
+ goto macaddr_drop;
+ }
return 0;
+
+macaddr_drop:
+ dev->switch_macaddr = NULL;
+ refcount_set(&switch_macaddr->refcount, 0);
+ kfree(switch_macaddr);
+
+ return ret;
}
-static void ksz_switch_macaddr_put(struct dsa_switch *ds)
+void ksz_switch_macaddr_put(struct dsa_switch *ds)
{
struct ksz_switch_macaddr *switch_macaddr;
struct ksz_device *dev = ds->priv;
@@ -3726,6 +3806,8 @@ static const struct dsa_switch_ops ksz_switch_ops = {
.get_pause_stats = ksz_get_pause_stats,
.port_change_mtu = ksz_change_mtu,
.port_max_mtu = ksz_max_mtu,
+ .get_wol = ksz_get_wol,
+ .set_wol = ksz_set_wol,
.get_ts_info = ksz_get_ts_info,
.port_hwtstamp_get = ksz_hwtstamp_get,
.port_hwtstamp_set = ksz_hwtstamp_set,
@@ -3765,6 +3847,30 @@ struct ksz_device *ksz_switch_alloc(struct device *base, void *priv)
}
EXPORT_SYMBOL(ksz_switch_alloc);
+/**
+ * ksz_switch_shutdown - Shutdown routine for the switch device.
+ * @dev: The switch device structure.
+ *
+ * This function is responsible for initiating a shutdown sequence for the
+ * switch device. It invokes the reset operation defined in the device
+ * operations, if available, to reset the switch. Subsequently, it calls the
+ * DSA framework's shutdown function to ensure a proper shutdown of the DSA
+ * switch.
+ */
+void ksz_switch_shutdown(struct ksz_device *dev)
+{
+ bool wol_enabled = false;
+
+ if (dev->dev_ops->wol_pre_shutdown)
+ dev->dev_ops->wol_pre_shutdown(dev, &wol_enabled);
+
+ if (dev->dev_ops->reset && !wol_enabled)
+ dev->dev_ops->reset(dev);
+
+ dsa_switch_shutdown(dev->ds);
+}
+EXPORT_SYMBOL(ksz_switch_shutdown);
+
static void ksz_parse_rgmii_delay(struct ksz_device *dev, int port_num,
struct device_node *port_dn)
{
@@ -4158,6 +4264,9 @@ int ksz_switch_register(struct ksz_device *dev)
dev_err(dev->dev, "inconsistent synclko settings\n");
return -EINVAL;
}
+
+ dev->wakeup_source = of_property_read_bool(dev->dev->of_node,
+ "wakeup-source");
}
ret = dsa_register_switch(dev->ds);