* [PATCH net-next 01/10] net: dsa: microchip: implement ksz8463_setup()
2026-07-09 6:42 [PATCH net-next 00/10] net: dsa: microchip: add PTP support for KSZ8463 Bastien Curutchet (Schneider Electric)
@ 2026-07-09 6:42 ` Bastien Curutchet (Schneider Electric)
2026-07-10 7:55 ` Bastien Curutchet
2026-07-09 6:42 ` [PATCH net-next 02/10] net: dsa: microchip: split ksz8_config_cpu_port() Bastien Curutchet (Schneider Electric)
` (8 subsequent siblings)
9 siblings, 1 reply; 13+ messages in thread
From: Bastien Curutchet (Schneider Electric) @ 2026-07-09 6:42 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Richard Cochran, Russell King, Simon Horman, Maxime Chevallier
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel, Bastien Curutchet (Schneider Electric)
KSZ8463 uses the ksz8_setup() as setup() callback for its DSA
operations. Its behavior is quite different than other KSZ8 switches,
especially its interrupt scheme.
Remove from the ksz8_setup()/ksz8_reset_switch() everything that is
ksz8463-related.
Create a dedicated ksz8463_setup() and a ksz8463_reset_switch() function.
This new ksz8463_setup() is widely inspired from ksz8_setup, it has
following differences:
- it doesn't configure drive strength (not supported on KSZ8463)
- it uses the ksz8463_reset_switch()
- it doesn't configure IRQs
- it doesn't call ksz8_handle_global_errata() (the handled errata only
affects the KSZ87xx variant)
Remove the teardown implementation from the KSZ8463 operations. Since
PTP and interrupts aren't setup, the common ksz_teardown() wouldn't do
anything anyway.
Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
---
drivers/net/dsa/microchip/ksz8.c | 126 +++++++++++++++++++++++++++++++++++----
1 file changed, 114 insertions(+), 12 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c
index c4c769028a20..5ed63f425013 100644
--- a/drivers/net/dsa/microchip/ksz8.c
+++ b/drivers/net/dsa/microchip/ksz8.c
@@ -181,6 +181,14 @@ static int ksz8_pme_pwrite8(struct ksz_device *dev, int port, int offset, u8 dat
return ksz8_ind_write8(dev, table, (u8)(offset), data);
}
+static int ksz8463_reset_switch(struct ksz_device *dev)
+{
+ ksz_cfg(dev, KSZ8463_REG_SW_RESET, KSZ8463_GLOBAL_SOFTWARE_RESET, true);
+ ksz_cfg(dev, KSZ8463_REG_SW_RESET, KSZ8463_GLOBAL_SOFTWARE_RESET,
+ false);
+ return 0;
+}
+
static int ksz8_reset_switch(struct ksz_device *dev)
{
if (ksz_is_ksz88x3(dev)) {
@@ -189,11 +197,6 @@ static int ksz8_reset_switch(struct ksz_device *dev)
KSZ8863_GLOBAL_SOFTWARE_RESET | KSZ8863_PCS_RESET, true);
ksz_cfg(dev, KSZ8863_REG_SW_RESET,
KSZ8863_GLOBAL_SOFTWARE_RESET | KSZ8863_PCS_RESET, false);
- } else if (ksz_is_ksz8463(dev)) {
- ksz_cfg(dev, KSZ8463_REG_SW_RESET,
- KSZ8463_GLOBAL_SOFTWARE_RESET, true);
- ksz_cfg(dev, KSZ8463_REG_SW_RESET,
- KSZ8463_GLOBAL_SOFTWARE_RESET, false);
} else {
/* reset switch */
ksz_write8(dev, REG_POWER_MANAGEMENT_1,
@@ -2300,6 +2303,110 @@ static void ksz88xx_r_mib_stats64(struct ksz_device *dev, int port)
spin_unlock(&mib->stats64_lock);
}
+static int ksz8463_setup(struct dsa_switch *ds)
+{
+ struct ksz_device *dev = ds->priv;
+ u16 storm_mask, storm_rate;
+ struct ksz_port *p;
+ const u16 *regs;
+ int i, ret;
+
+ regs = dev->info->regs;
+
+ dev->vlan_cache = devm_kcalloc(dev->dev, sizeof(struct vlan_table),
+ dev->info->num_vlans, GFP_KERNEL);
+ if (!dev->vlan_cache)
+ return -ENOMEM;
+
+ ret = ksz8463_reset_switch(dev);
+ if (ret) {
+ dev_err(ds->dev, "failed to reset switch\n");
+ return ret;
+ }
+
+ /* set broadcast storm protection 10% rate */
+ storm_mask = BROADCAST_STORM_RATE;
+ storm_rate = (BROADCAST_STORM_VALUE * BROADCAST_STORM_PROT_RATE) / 100;
+ storm_mask = swab16(storm_mask);
+ storm_rate = swab16(storm_rate);
+ regmap_update_bits(ksz_regmap_16(dev), regs[S_BROADCAST_CTRL],
+ storm_mask, storm_rate);
+
+ ksz8_config_cpu_port(ds);
+
+ ksz8_enable_stp_addr(dev);
+
+ ds->num_tx_queues = dev->info->num_tx_queues;
+
+ regmap_update_bits(ksz_regmap_8(dev), regs[S_MULTICAST_CTRL],
+ MULTICAST_STORM_DISABLE, MULTICAST_STORM_DISABLE);
+
+ ksz_init_mib_timer(dev);
+
+ ds->configure_vlan_while_not_filtering = false;
+ ds->dscp_prio_mapping_is_global = true;
+ ds->mtu_enforcement_ingress = true;
+
+ /* We rely on software untagging on the CPU port, so that we
+ * can support both tagged and untagged VLANs
+ */
+ ds->untag_bridge_pvid = true;
+
+ /* VLAN filtering is partly controlled by the global VLAN
+ * Enable flag
+ */
+ ds->vlan_filtering_is_global = true;
+
+ /* Enable automatic fast aging when link changed detected. */
+ ksz_cfg(dev, S_LINK_AGING_CTRL, SW_LINK_AUTO_AGING, true);
+
+ /* Enable aggressive back off algorithm in half duplex mode. */
+ ret = ksz_rmw8(dev, REG_SW_CTRL_1, SW_AGGR_BACKOFF, SW_AGGR_BACKOFF);
+ if (ret)
+ return ret;
+
+ /*
+ * Make sure unicast VLAN boundary is set as default and
+ * enable no excessive collision drop.
+ */
+ ret = ksz_rmw8(dev, REG_SW_CTRL_2,
+ UNICAST_VLAN_BOUNDARY | NO_EXC_COLLISION_DROP,
+ UNICAST_VLAN_BOUNDARY | NO_EXC_COLLISION_DROP);
+ if (ret)
+ return ret;
+
+ ksz_cfg(dev, S_REPLACE_VID_CTRL, SW_REPLACE_VID, false);
+
+ ksz_cfg(dev, S_MIRROR_CTRL, SW_MIRROR_RX_TX, false);
+
+ for (i = 0; i < (dev->info->num_vlans / 4); i++)
+ ksz8_r_vlan_entries(dev, i);
+
+ /* Start with learning disabled on standalone user ports, and enabled
+ * on the CPU port. In lack of other finer mechanisms, learning on the
+ * CPU port will avoid flooding bridge local addresses on the network
+ * in some cases.
+ */
+ p = &dev->ports[dev->cpu_port];
+ p->learning = true;
+
+ ret = ksz_mdio_register(dev);
+ if (ret < 0) {
+ dev_err(dev->dev, "failed to register the mdio");
+ return ret;
+ }
+
+ ret = ksz_dcb_init(dev);
+ if (ret)
+ return ret;
+
+ /* start switch */
+ regmap_update_bits(ksz_regmap_8(dev), regs[S_START_CTRL],
+ SW_START, SW_START);
+
+ return 0;
+}
+
/**
* ksz88x3_drive_strength_write() - Set the drive strength configuration for
* KSZ8863 compatible chip variants.
@@ -2445,10 +2552,6 @@ static int ksz8_setup(struct dsa_switch *ds)
/* set broadcast storm protection 10% rate */
storm_mask = BROADCAST_STORM_RATE;
storm_rate = (BROADCAST_STORM_VALUE * BROADCAST_STORM_PROT_RATE) / 100;
- if (ksz_is_ksz8463(dev)) {
- storm_mask = swab16(storm_mask);
- storm_rate = swab16(storm_rate);
- }
regmap_update_bits(ksz_regmap_16(dev), regs[S_BROADCAST_CTRL],
storm_mask, storm_rate);
@@ -2499,7 +2602,7 @@ static int ksz8_setup(struct dsa_switch *ds)
ksz_cfg(dev, S_MIRROR_CTRL, SW_MIRROR_RX_TX, false);
- if (!ksz_is_ksz88x3(dev) && !ksz_is_ksz8463(dev))
+ if (!ksz_is_ksz88x3(dev))
ksz_cfg(dev, REG_SW_CTRL_19, SW_INS_TAG_ENABLE, true);
for (i = 0; i < (dev->info->num_vlans / 4); i++)
@@ -2889,8 +2992,7 @@ const struct ksz_dev_ops ksz88xx_dev_ops = {
const struct dsa_switch_ops ksz8463_switch_ops = {
.get_tag_protocol = ksz8463_get_tag_protocol,
.connect_tag_protocol = ksz8463_connect_tag_protocol,
- .setup = ksz8_setup,
- .teardown = ksz_teardown,
+ .setup = ksz8463_setup,
.phy_read = ksz8463_phy_read16,
.phy_write = ksz8463_phy_write16,
.phylink_get_caps = ksz8_phylink_get_caps,
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH net-next 01/10] net: dsa: microchip: implement ksz8463_setup()
2026-07-09 6:42 ` [PATCH net-next 01/10] net: dsa: microchip: implement ksz8463_setup() Bastien Curutchet (Schneider Electric)
@ 2026-07-10 7:55 ` Bastien Curutchet
0 siblings, 0 replies; 13+ messages in thread
From: Bastien Curutchet @ 2026-07-10 7:55 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Richard Cochran, Russell King, Simon Horman, Maxime Chevallier
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel
Hi all,
On 7/9/26 8:42 AM, Bastien Curutchet (Schneider Electric) wrote:
> KSZ8463 uses the ksz8_setup() as setup() callback for its DSA
> operations. Its behavior is quite different than other KSZ8 switches,
> especially its interrupt scheme.
>
> Remove from the ksz8_setup()/ksz8_reset_switch() everything that is
> ksz8463-related.
> Create a dedicated ksz8463_setup() and a ksz8463_reset_switch() function.
> This new ksz8463_setup() is widely inspired from ksz8_setup, it has
> following differences:
> - it doesn't configure drive strength (not supported on KSZ8463)
> - it uses the ksz8463_reset_switch()
> - it doesn't configure IRQs
Sashiko said "Can this cause a probe failure or interrupt corruption
when a hardware interrupt is configured in the device tree?"
It doesn't cause a probe failure on my setup. However, I think it's
right when it says that the IRQ setup could lead to 'erroneously return
an active Linux IRQ belonging to another hardware device'.
I'll change the patch order in next iteration and put this one after the
KSZ8463 interrupt support to avoid this issue.
FYI, Sashiko made two other comments on this patch about bugs that would
be triggered when 'the DSA master netdev is rebound'. I'm not sure to
understand how these bugs can be triggered and the comments start with
'This isn't a bug introduced by this patch' so I don't plan to
investigate it further.
Best regards,
Bastien
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH net-next 02/10] net: dsa: microchip: split ksz8_config_cpu_port()
2026-07-09 6:42 [PATCH net-next 00/10] net: dsa: microchip: add PTP support for KSZ8463 Bastien Curutchet (Schneider Electric)
2026-07-09 6:42 ` [PATCH net-next 01/10] net: dsa: microchip: implement ksz8463_setup() Bastien Curutchet (Schneider Electric)
@ 2026-07-09 6:42 ` Bastien Curutchet (Schneider Electric)
2026-07-09 6:42 ` [PATCH net-next 03/10] net: dsa: microchip: allow the use of other IRQ operations Bastien Curutchet (Schneider Electric)
` (7 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Bastien Curutchet (Schneider Electric) @ 2026-07-09 6:42 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Richard Cochran, Russell King, Simon Horman, Maxime Chevallier
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel, Bastien Curutchet (Schneider Electric)
ksz8_config_cpu_port() is only called twice, once by ksz8_setup() and
once by ksz8463_setup(). It contains a ksz8463 branch that could be
avoided in the ksz8_setup() case and a ksz87xx/ksz88xx branches that
could be avoided in ksz8463_setup() case.
Create ksz8463_config_cpu_port() that only handles the ksz8463 case and
remove the ksz8463 specificities from the common ksz8_config_cpu_port().
Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
---
drivers/net/dsa/microchip/ksz8.c | 73 +++++++++++++++++++++++++---------------
1 file changed, 45 insertions(+), 28 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c
index 5ed63f425013..3bbca6f9cfc5 100644
--- a/drivers/net/dsa/microchip/ksz8.c
+++ b/drivers/net/dsa/microchip/ksz8.c
@@ -2029,6 +2029,50 @@ static void ksz88x3_config_rmii_clk(struct ksz_device *dev)
KSZ88X3_PORT3_RMII_CLK_INTERNAL, rmii_clk_internal);
}
+static void ksz8463_config_cpu_port(struct dsa_switch *ds)
+{
+ struct ksz_device *dev = ds->priv;
+ struct ksz_port *p;
+ u8 fiber_ports = 0;
+ const u32 *masks;
+ const u16 *regs;
+ int i;
+
+ masks = dev->info->masks;
+ regs = dev->info->regs;
+
+ ksz_cfg(dev, regs[S_TAIL_TAG_CTRL], masks[SW_TAIL_TAG_ENABLE], true);
+
+ ksz8_port_setup(dev, dev->cpu_port, true);
+
+ for (i = 0; i < dev->phy_port_cnt; i++)
+ ksz_port_stp_state_set(ds, i, BR_STATE_DISABLED);
+
+ for (i = 0; i < dev->phy_port_cnt; i++) {
+ p = &dev->ports[i];
+ ksz_port_cfg(dev, i, regs[P_STP_CTRL], PORT_FORCE_FLOW_CTRL,
+ p->fiber);
+ if (p->fiber)
+ fiber_ports |= (1 << i);
+ }
+
+ /* Setup fiber ports. */
+ if (fiber_ports) {
+ fiber_ports &= 3;
+ regmap_update_bits(ksz_regmap_16(dev), KSZ8463_REG_CFG_CTRL,
+ fiber_ports << PORT_COPPER_MODE_S,
+ 0);
+ regmap_update_bits(ksz_regmap_16(dev), KSZ8463_REG_DSP_CTRL_6,
+ COPPER_RECEIVE_ADJUSTMENT, 0);
+ }
+
+ /* Turn off PTP function as the switch enables it by default */
+ regmap_update_bits(ksz_regmap_16(dev), KSZ8463_PTP_MSG_CONF1,
+ PTP_ENABLE, 0);
+ regmap_update_bits(ksz_regmap_16(dev), KSZ8463_PTP_CLK_CTRL,
+ PTP_CLK_ENABLE, 0);
+}
+
static void ksz8_config_cpu_port(struct dsa_switch *ds)
{
struct ksz_device *dev = ds->priv;
@@ -2036,7 +2080,6 @@ static void ksz8_config_cpu_port(struct dsa_switch *ds)
const u32 *masks;
const u16 *regs;
u8 remote;
- u8 fiber_ports = 0;
int i;
masks = dev->info->masks;
@@ -2067,32 +2110,6 @@ static void ksz8_config_cpu_port(struct dsa_switch *ds)
else
ksz_port_cfg(dev, i, regs[P_STP_CTRL],
PORT_FORCE_FLOW_CTRL, false);
- if (p->fiber)
- fiber_ports |= (1 << i);
- }
- if (ksz_is_ksz8463(dev)) {
- /* Setup fiber ports. */
- if (fiber_ports) {
- fiber_ports &= 3;
- regmap_update_bits(ksz_regmap_16(dev),
- KSZ8463_REG_CFG_CTRL,
- fiber_ports << PORT_COPPER_MODE_S,
- 0);
- regmap_update_bits(ksz_regmap_16(dev),
- KSZ8463_REG_DSP_CTRL_6,
- COPPER_RECEIVE_ADJUSTMENT, 0);
- }
-
- /* Turn off PTP function as the switch's proprietary way of
- * handling timestamp is not supported in current Linux PTP
- * stack implementation.
- */
- regmap_update_bits(ksz_regmap_16(dev),
- KSZ8463_PTP_MSG_CONF1,
- PTP_ENABLE, 0);
- regmap_update_bits(ksz_regmap_16(dev),
- KSZ8463_PTP_CLK_CTRL,
- PTP_CLK_ENABLE, 0);
}
}
@@ -2332,7 +2349,7 @@ static int ksz8463_setup(struct dsa_switch *ds)
regmap_update_bits(ksz_regmap_16(dev), regs[S_BROADCAST_CTRL],
storm_mask, storm_rate);
- ksz8_config_cpu_port(ds);
+ ksz8463_config_cpu_port(ds);
ksz8_enable_stp_addr(dev);
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH net-next 03/10] net: dsa: microchip: allow the use of other IRQ operations.
2026-07-09 6:42 [PATCH net-next 00/10] net: dsa: microchip: add PTP support for KSZ8463 Bastien Curutchet (Schneider Electric)
2026-07-09 6:42 ` [PATCH net-next 01/10] net: dsa: microchip: implement ksz8463_setup() Bastien Curutchet (Schneider Electric)
2026-07-09 6:42 ` [PATCH net-next 02/10] net: dsa: microchip: split ksz8_config_cpu_port() Bastien Curutchet (Schneider Electric)
@ 2026-07-09 6:42 ` Bastien Curutchet (Schneider Electric)
2026-07-09 6:42 ` [PATCH net-next 04/10] net: dsa: microchip: add PTP interrupt handling for KSZ8463 Bastien Curutchet (Schneider Electric)
` (6 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Bastien Curutchet (Schneider Electric) @ 2026-07-09 6:42 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Richard Cochran, Russell King, Simon Horman, Maxime Chevallier
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel, Bastien Curutchet (Schneider Electric)
The IRQ setup uses an hardcoded set of IRQ operations. These operations
don't fit with the KSZ8463 which has an inverted bit logic (it uses an
'enable irq' register instead of a 'mask irq' one) and 16-bits registers.
Take the IRQ domain operations as input of ksz_irq_common_setup() to
allow KSZ8463 to use the already existing setup with its own set of IRQ
operations.
Expose ksz_irq_common_setup() and ksz_irq_bus_lock/unlock() so they can
be used by ksz8.c.
Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
---
drivers/net/dsa/microchip/ksz_common.c | 15 ++++++++-------
drivers/net/dsa/microchip/ksz_common.h | 5 +++++
2 files changed, 13 insertions(+), 7 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
index 67ab6ddb9e53..ff1be540b546 100644
--- a/drivers/net/dsa/microchip/ksz_common.c
+++ b/drivers/net/dsa/microchip/ksz_common.c
@@ -2419,14 +2419,14 @@ static void ksz_irq_unmask(struct irq_data *d)
kirq->masked &= ~BIT(d->hwirq);
}
-static void ksz_irq_bus_lock(struct irq_data *d)
+void ksz_irq_bus_lock(struct irq_data *d)
{
struct ksz_irq *kirq = irq_data_get_irq_chip_data(d);
mutex_lock(&kirq->dev->lock_irq);
}
-static void ksz_irq_bus_sync_unlock(struct irq_data *d)
+void ksz_irq_bus_sync_unlock(struct irq_data *d)
{
struct ksz_irq *kirq = irq_data_get_irq_chip_data(d);
struct ksz_device *dev = kirq->dev;
@@ -2504,14 +2504,15 @@ static irqreturn_t ksz_irq_thread_fn(int irq, void *dev_id)
return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE);
}
-static int ksz_irq_common_setup(struct ksz_device *dev, struct ksz_irq *kirq)
+int ksz_irq_common_setup(struct ksz_device *dev, struct ksz_irq *kirq,
+ const struct irq_domain_ops *ops)
{
int ret, n;
kirq->dev = dev;
- kirq->domain = irq_domain_create_simple(dev_fwnode(dev->dev), kirq->nirqs, 0,
- &ksz_irq_domain_ops, kirq);
+ kirq->domain = irq_domain_create_simple(dev_fwnode(dev->dev),
+ kirq->nirqs, 0, ops, kirq);
if (!kirq->domain)
return -ENOMEM;
@@ -2543,7 +2544,7 @@ int ksz_girq_setup(struct ksz_device *dev)
girq->irq_num = dev->irq;
- return ksz_irq_common_setup(dev, girq);
+ return ksz_irq_common_setup(dev, girq, &ksz_irq_domain_ops);
}
int ksz_pirq_setup(struct ksz_device *dev, u8 p)
@@ -2560,7 +2561,7 @@ int ksz_pirq_setup(struct ksz_device *dev, u8 p)
if (!pirq->irq_num)
return -EINVAL;
- return ksz_irq_common_setup(dev, pirq);
+ return ksz_irq_common_setup(dev, pirq, &ksz_irq_domain_ops);
}
void ksz_teardown(struct dsa_switch *ds)
diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
index acaf70e6f393..0f2abb22ca91 100644
--- a/drivers/net/dsa/microchip/ksz_common.h
+++ b/drivers/net/dsa/microchip/ksz_common.h
@@ -8,6 +8,7 @@
#define __KSZ_COMMON_H
#include <linux/etherdevice.h>
+#include <linux/irqdomain.h>
#include <linux/kernel.h>
#include <linux/mutex.h>
#include <linux/pcs/pcs-xpcs.h>
@@ -508,6 +509,10 @@ int ksz_sw_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val);
int ksz_parent_mdio_read(struct mii_bus *bus, int addr, int regnum);
int ksz_parent_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val);
int ksz_mdio_register(struct ksz_device *dev);
+void ksz_irq_bus_lock(struct irq_data *d);
+void ksz_irq_bus_sync_unlock(struct irq_data *d);
+int ksz_irq_common_setup(struct ksz_device *dev, struct ksz_irq *kirq,
+ const struct irq_domain_ops *ops);
int ksz_pirq_setup(struct ksz_device *dev, u8 p);
int ksz_girq_setup(struct ksz_device *dev);
void ksz_irq_free(struct ksz_irq *kirq);
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH net-next 04/10] net: dsa: microchip: add PTP interrupt handling for KSZ8463
2026-07-09 6:42 [PATCH net-next 00/10] net: dsa: microchip: add PTP support for KSZ8463 Bastien Curutchet (Schneider Electric)
` (2 preceding siblings ...)
2026-07-09 6:42 ` [PATCH net-next 03/10] net: dsa: microchip: allow the use of other IRQ operations Bastien Curutchet (Schneider Electric)
@ 2026-07-09 6:42 ` Bastien Curutchet (Schneider Electric)
2026-07-10 7:57 ` Bastien Curutchet
2026-07-09 6:42 ` [PATCH net-next 05/10] net: dsa: microchip: adapt port offset for KSZ8463's PTP register Bastien Curutchet (Schneider Electric)
` (5 subsequent siblings)
9 siblings, 1 reply; 13+ messages in thread
From: Bastien Curutchet (Schneider Electric) @ 2026-07-09 6:42 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Richard Cochran, Russell King, Simon Horman, Maxime Chevallier
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel, Bastien Curutchet (Schneider Electric)
KSZ8463 PTP interrupts aren't handled by the driver.
The interrupt layout in KSZ8463 has nothing to do with the other
switches:
- Its global interrupt enable register is 16-bits long and follow an
'enable' logic, instead of a 'mask' one
- all the interrupts of all ports are grouped into one status register
while others have one interrupt register per port
- xdelay_req and pdresp timestamps share one single interrupt bit on the
KSZ8463 while each of them has its own interrupt bit on other switches
Create a KSZ8463-specific set of interrupt domain operations to handle
the global IRQ layer. To limit code duplication, it uses the same
interrupt handler than the other switches. Since other switches have
8-bits registers, only the high-byte of the interrupt status/enable
registers are used. This high-byte is where the PTP interrupts are
located. The low-byte contains the wake-up detection interrupts so if at
some points these interrupts are needed we'll need a bit of rework here.
Create KSZ8463-specific functions to setup the PTP interrupts. The
created IRQ domain is tied to the first port of the KSZ8463. Again,
the same PTP interrupt handler than the others switches is used.
Implement the teardown callback to release the interrupts.
Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
---
drivers/net/dsa/microchip/ksz8.c | 93 ++++++++++++++++++++++-
drivers/net/dsa/microchip/ksz_ptp.c | 129 ++++++++++++++++++++++++++++++++
drivers/net/dsa/microchip/ksz_ptp.h | 9 +++
drivers/net/dsa/microchip/ksz_ptp_reg.h | 6 ++
4 files changed, 235 insertions(+), 2 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c
index 3bbca6f9cfc5..c099a7005808 100644
--- a/drivers/net/dsa/microchip/ksz8.c
+++ b/drivers/net/dsa/microchip/ksz8.c
@@ -36,6 +36,13 @@
#include "ksz8_reg.h"
#include "ksz8.h"
+/*
+ * We use only the high-byte (so odd addresses) of the 16-bits registers to fit
+ * in the common IRQ framework
+ */
+#define KSZ8463_REG_ISR 0x191
+#define KSZ8463_REG_IER 0x193
+
/* ksz88x3_drive_strengths - Drive strength mapping for KSZ8863, KSZ8873, ..
* variants.
* This values are documented in KSZ8873 and KSZ8863 datasheets.
@@ -181,6 +188,58 @@ static int ksz8_pme_pwrite8(struct ksz_device *dev, int port, int offset, u8 dat
return ksz8_ind_write8(dev, table, (u8)(offset), data);
}
+static void ksz8463_irq_mask(struct irq_data *d)
+{
+ struct ksz_irq *kirq = irq_data_get_irq_chip_data(d);
+
+ kirq->masked &= ~BIT(d->hwirq);
+}
+
+static void ksz8463_irq_unmask(struct irq_data *d)
+{
+ struct ksz_irq *kirq = irq_data_get_irq_chip_data(d);
+
+ kirq->masked |= BIT(d->hwirq);
+}
+
+static const struct irq_chip ksz8463_irq_chip = {
+ .name = "ksz8463-irq",
+ .irq_mask = ksz8463_irq_mask,
+ .irq_unmask = ksz8463_irq_unmask,
+ .irq_bus_lock = ksz_irq_bus_lock,
+ .irq_bus_sync_unlock = ksz_irq_bus_sync_unlock,
+};
+
+static int ksz8463_irq_domain_map(struct irq_domain *d,
+ unsigned int irq, irq_hw_number_t hwirq)
+{
+ irq_set_chip_data(irq, d->host_data);
+ irq_set_chip_and_handler(irq, &ksz8463_irq_chip, handle_level_irq);
+ irq_set_noprobe(irq);
+
+ return 0;
+}
+
+static const struct irq_domain_ops ksz8463_irq_domain_ops = {
+ .map = ksz8463_irq_domain_map,
+ .xlate = irq_domain_xlate_twocell,
+};
+
+static int ksz8463_girq_setup(struct ksz_device *dev)
+{
+ struct ksz_irq *girq = &dev->girq;
+
+ girq->nirqs = 8;
+ girq->reg_mask = KSZ8463_REG_IER;
+ girq->reg_status = KSZ8463_REG_ISR;
+ girq->masked = 0;
+ snprintf(girq->name, sizeof(girq->name), "ksz8463-girq");
+
+ girq->irq_num = dev->irq;
+
+ return ksz_irq_common_setup(dev, girq, &ksz8463_irq_domain_ops);
+}
+
static int ksz8463_reset_switch(struct ksz_device *dev)
{
ksz_cfg(dev, KSZ8463_REG_SW_RESET, KSZ8463_GLOBAL_SOFTWARE_RESET, true);
@@ -2407,21 +2466,50 @@ static int ksz8463_setup(struct dsa_switch *ds)
p = &dev->ports[dev->cpu_port];
p->learning = true;
+ if (dev->irq > 0) {
+ ret = ksz8463_girq_setup(dev);
+ if (ret)
+ return ret;
+
+ ret = ksz8463_ptp_irq_setup(ds);
+ if (ret)
+ goto free_girq;
+ }
+
ret = ksz_mdio_register(dev);
if (ret < 0) {
dev_err(dev->dev, "failed to register the mdio");
- return ret;
+ goto free_ptp_irq;
}
ret = ksz_dcb_init(dev);
if (ret)
- return ret;
+ goto free_ptp_irq;
/* start switch */
regmap_update_bits(ksz_regmap_8(dev), regs[S_START_CTRL],
SW_START, SW_START);
return 0;
+
+free_ptp_irq:
+ if (dev->irq > 0)
+ ksz8463_ptp_irq_free(ds);
+free_girq:
+ if (dev->irq > 0)
+ ksz_irq_free(&dev->girq);
+
+ return ret;
+}
+
+static void ksz8463_teardown(struct dsa_switch *ds)
+{
+ struct ksz_device *dev = ds->priv;
+
+ if (dev->irq > 0) {
+ ksz8463_ptp_irq_free(ds);
+ ksz_irq_free(&dev->girq);
+ }
}
/**
@@ -3010,6 +3098,7 @@ const struct dsa_switch_ops ksz8463_switch_ops = {
.get_tag_protocol = ksz8463_get_tag_protocol,
.connect_tag_protocol = ksz8463_connect_tag_protocol,
.setup = ksz8463_setup,
+ .teardown = ksz8463_teardown,
.phy_read = ksz8463_phy_read16,
.phy_write = ksz8463_phy_write16,
.phylink_get_caps = ksz8_phylink_get_caps,
diff --git a/drivers/net/dsa/microchip/ksz_ptp.c b/drivers/net/dsa/microchip/ksz_ptp.c
index 8b98039320ad..7a74befda9ad 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.c
+++ b/drivers/net/dsa/microchip/ksz_ptp.c
@@ -32,6 +32,15 @@
#define KSZ_PTP_INT_START 13
+/*
+ * PTP interrupt bit is the bit 12 of the 16-bits ISR/IER. But ksz_common.c only
+ * accesses the high-byte of these registers so the PTP interrupt bit becomes 4.
+ */
+#define KSZ8463_SRC_PTP_INT 4
+#define KSZ8463_PTP_PORT1_INT_START 12
+#define KSZ8463_PTP_PORT2_INT_START 14
+#define KSZ8463_PTP_INT_START KSZ8463_PTP_PORT1_INT_START
+
static int ksz_ptp_tou_gpio(struct ksz_device *dev)
{
int ret;
@@ -1129,6 +1138,126 @@ static int ksz_ptp_msg_irq_setup(struct ksz_port *port, u8 n)
return ret;
}
+static int ksz8463_ptp_port_irq_setup(struct ksz_irq *ptpirq,
+ struct ksz_port *port, int hw_irq)
+{
+ u16 ts_reg[] = {KSZ8463_REG_PORT_SYNC_TS, KSZ8463_REG_PORT_DREQ_TS};
+ static const char * const name[] = {"sync-msg", "delay-msg"};
+ const struct ksz_dev_ops *ops = port->ksz_dev->dev_ops;
+ struct ksz_ptp_irq *ptpmsg_irq;
+ int ret;
+ int i;
+
+ init_completion(&port->tstamp_msg_comp);
+
+ for (i = 0; i < 2; i++) {
+ ptpmsg_irq = &port->ptpmsg_irq[i];
+ ptpmsg_irq->num = irq_create_mapping(ptpirq->domain,
+ hw_irq + i);
+ if (!ptpmsg_irq->num)
+ goto release_msg_irq;
+
+ ptpmsg_irq->port = port;
+ ptpmsg_irq->ts_reg = ops->get_port_addr(port->num, ts_reg[i]);
+
+ strscpy(ptpmsg_irq->name, name[i]);
+
+ ret = request_threaded_irq(ptpmsg_irq->num, NULL,
+ ksz_ptp_msg_thread_fn, IRQF_ONESHOT,
+ ptpmsg_irq->name, ptpmsg_irq);
+ if (ret) {
+ irq_dispose_mapping(ptpmsg_irq->num);
+ goto release_msg_irq;
+ }
+ }
+
+ return 0;
+
+release_msg_irq:
+ while (i--)
+ ksz_ptp_msg_irq_free(port, i);
+
+ return ret;
+}
+
+static void ksz8463_ptp_port_irq_teardown(struct ksz_port *port)
+{
+ int i;
+
+ for (i = 0; i < 2; i++)
+ ksz_ptp_msg_irq_free(port, i);
+}
+
+int ksz8463_ptp_irq_setup(struct dsa_switch *ds)
+{
+ struct ksz_device *dev = ds->priv;
+ struct ksz_port *port1, *port2;
+ struct ksz_irq *ptpirq;
+ int ret;
+
+ port1 = &dev->ports[0];
+ port2 = &dev->ports[1];
+ ptpirq = &port1->ptpirq;
+
+ ptpirq->irq_num = irq_find_mapping(dev->girq.domain,
+ KSZ8463_SRC_PTP_INT);
+ if (!ptpirq->irq_num)
+ return -EINVAL;
+
+ ptpirq->dev = dev;
+ ptpirq->nirqs = 4;
+ ptpirq->reg_mask = KSZ8463_PTP_TS_IER;
+ ptpirq->reg_status = KSZ8463_PTP_TS_ISR;
+ ptpirq->irq0_offset = KSZ8463_PTP_INT_START;
+ snprintf(ptpirq->name, sizeof(ptpirq->name), "ptp-irq");
+
+ ptpirq->domain = irq_domain_create_linear(dev_fwnode(dev->dev),
+ ptpirq->nirqs,
+ &ksz_ptp_irq_domain_ops,
+ ptpirq);
+ if (!ptpirq->domain)
+ return -ENOMEM;
+
+ ret = request_threaded_irq(ptpirq->irq_num, NULL, ksz_ptp_irq_thread_fn,
+ IRQF_ONESHOT, ptpirq->name, ptpirq);
+ if (ret)
+ goto release_domain;
+
+ ret = ksz8463_ptp_port_irq_setup(ptpirq, port1,
+ KSZ8463_PTP_PORT1_INT_START - KSZ8463_PTP_INT_START);
+ if (ret)
+ goto release_irq;
+
+ ret = ksz8463_ptp_port_irq_setup(ptpirq, port2,
+ KSZ8463_PTP_PORT2_INT_START - KSZ8463_PTP_INT_START);
+ if (ret)
+ goto free_port1;
+
+ return 0;
+
+free_port1:
+ ksz8463_ptp_port_irq_teardown(port1);
+release_irq:
+ free_irq(ptpirq->irq_num, ptpirq);
+release_domain:
+ irq_domain_remove(ptpirq->domain);
+
+ return ret;
+}
+
+void ksz8463_ptp_irq_free(struct dsa_switch *ds)
+{
+ struct ksz_device *dev = ds->priv;
+ struct ksz_port *port1 = &dev->ports[0];
+ struct ksz_port *port2 = &dev->ports[1];
+ struct ksz_irq *ptpirq = &port1->ptpirq;
+
+ ksz8463_ptp_port_irq_teardown(port1);
+ ksz8463_ptp_port_irq_teardown(port2);
+ free_irq(ptpirq->irq_num, ptpirq);
+ irq_domain_remove(ptpirq->domain);
+}
+
int ksz_ptp_irq_setup(struct dsa_switch *ds, u8 p)
{
struct ksz_device *dev = ds->priv;
diff --git a/drivers/net/dsa/microchip/ksz_ptp.h b/drivers/net/dsa/microchip/ksz_ptp.h
index 3086e519b1b6..11408580031d 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.h
+++ b/drivers/net/dsa/microchip/ksz_ptp.h
@@ -50,6 +50,8 @@ bool ksz_port_rxtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb,
unsigned int type);
int ksz_ptp_irq_setup(struct dsa_switch *ds, u8 p);
void ksz_ptp_irq_free(struct dsa_switch *ds, u8 p);
+int ksz8463_ptp_irq_setup(struct dsa_switch *ds);
+void ksz8463_ptp_irq_free(struct dsa_switch *ds);
#else
@@ -72,6 +74,13 @@ static inline int ksz_ptp_irq_setup(struct dsa_switch *ds, u8 p)
static inline void ksz_ptp_irq_free(struct dsa_switch *ds, u8 p) {}
+static inline int ksz8463_ptp_irq_setup(struct dsa_switch *ds)
+{
+ return 0;
+}
+
+static inline void ksz8463_ptp_irq_free(struct dsa_switch *ds) {}
+
#define ksz_get_ts_info NULL
#define ksz_hwtstamp_get NULL
diff --git a/drivers/net/dsa/microchip/ksz_ptp_reg.h b/drivers/net/dsa/microchip/ksz_ptp_reg.h
index eab9aecb7fa8..1a669d6ee889 100644
--- a/drivers/net/dsa/microchip/ksz_ptp_reg.h
+++ b/drivers/net/dsa/microchip/ksz_ptp_reg.h
@@ -121,6 +121,12 @@
#define REG_PTP_PORT_SYNC_TS 0x0C0C
#define REG_PTP_PORT_PDRESP_TS 0x0C10
+#define KSZ8463_REG_PORT_DREQ_TS 0x0648
+#define KSZ8463_REG_PORT_SYNC_TS 0x064C
+#define KSZ8463_REG_PORT_DRESP_TS 0x0650
+#define KSZ8463_PTP_TS_ISR 0x068C
+#define KSZ8463_PTP_TS_IER 0x068E
+
#define REG_PTP_PORT_TX_INT_STATUS__2 0x0C14
#define REG_PTP_PORT_TX_INT_ENABLE__2 0x0C16
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH net-next 04/10] net: dsa: microchip: add PTP interrupt handling for KSZ8463
2026-07-09 6:42 ` [PATCH net-next 04/10] net: dsa: microchip: add PTP interrupt handling for KSZ8463 Bastien Curutchet (Schneider Electric)
@ 2026-07-10 7:57 ` Bastien Curutchet
0 siblings, 0 replies; 13+ messages in thread
From: Bastien Curutchet @ 2026-07-10 7:57 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Richard Cochran, Russell King, Simon Horman, Maxime Chevallier
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel
Hi all,
On 7/9/26 8:42 AM, Bastien Curutchet (Schneider Electric) wrote:
> KSZ8463 PTP interrupts aren't handled by the driver.
> The interrupt layout in KSZ8463 has nothing to do with the other
> switches:
> - Its global interrupt enable register is 16-bits long and follow an
> 'enable' logic, instead of a 'mask' one
> - all the interrupts of all ports are grouped into one status register
> while others have one interrupt register per port
> - xdelay_req and pdresp timestamps share one single interrupt bit on the
> KSZ8463 while each of them has its own interrupt bit on other switches
>
> Create a KSZ8463-specific set of interrupt domain operations to handle
> the global IRQ layer. To limit code duplication, it uses the same
> interrupt handler than the other switches. Since other switches have
> 8-bits registers, only the high-byte of the interrupt status/enable
> registers are used. This high-byte is where the PTP interrupts are
> located. The low-byte contains the wake-up detection interrupts so if at
> some points these interrupts are needed we'll need a bit of rework here.
>
> Create KSZ8463-specific functions to setup the PTP interrupts. The
> created IRQ domain is tied to the first port of the KSZ8463. Again,
> the same PTP interrupt handler than the others switches is used.
>
> Implement the teardown callback to release the interrupts.
>
> Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
> ---
> drivers/net/dsa/microchip/ksz8.c | 93 ++++++++++++++++++++++-
> drivers/net/dsa/microchip/ksz_ptp.c | 129 ++++++++++++++++++++++++++++++++
> drivers/net/dsa/microchip/ksz_ptp.h | 9 +++
> drivers/net/dsa/microchip/ksz_ptp_reg.h | 6 ++
> 4 files changed, 235 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c
> index 3bbca6f9cfc5..c099a7005808 100644
> --- a/drivers/net/dsa/microchip/ksz8.c
> +++ b/drivers/net/dsa/microchip/ksz8.c
> @@ -36,6 +36,13 @@
> #include "ksz8_reg.h"
> #include "ksz8.h"
>
> +/*
> + * We use only the high-byte (so odd addresses) of the 16-bits registers to fit
> + * in the common IRQ framework
> + */
> +#define KSZ8463_REG_ISR 0x191
> +#define KSZ8463_REG_IER 0x193
> +
> /* ksz88x3_drive_strengths - Drive strength mapping for KSZ8863, KSZ8873, ..
> * variants.
> * This values are documented in KSZ8873 and KSZ8863 datasheets.
> @@ -181,6 +188,58 @@ static int ksz8_pme_pwrite8(struct ksz_device *dev, int port, int offset, u8 dat
> return ksz8_ind_write8(dev, table, (u8)(offset), data);
> }
>
> +static void ksz8463_irq_mask(struct irq_data *d)
> +{
> + struct ksz_irq *kirq = irq_data_get_irq_chip_data(d);
> +
> + kirq->masked &= ~BIT(d->hwirq);
> +}
> +
> +static void ksz8463_irq_unmask(struct irq_data *d)
> +{
> + struct ksz_irq *kirq = irq_data_get_irq_chip_data(d);
> +
> + kirq->masked |= BIT(d->hwirq);
> +}
> +
> +static const struct irq_chip ksz8463_irq_chip = {
> + .name = "ksz8463-irq",
> + .irq_mask = ksz8463_irq_mask,
> + .irq_unmask = ksz8463_irq_unmask,
> + .irq_bus_lock = ksz_irq_bus_lock,
> + .irq_bus_sync_unlock = ksz_irq_bus_sync_unlock,
> +};
> +
> +static int ksz8463_irq_domain_map(struct irq_domain *d,
> + unsigned int irq, irq_hw_number_t hwirq)
> +{
> + irq_set_chip_data(irq, d->host_data);
> + irq_set_chip_and_handler(irq, &ksz8463_irq_chip, handle_level_irq);
> + irq_set_noprobe(irq);
> +
> + return 0;
> +}
> +
> +static const struct irq_domain_ops ksz8463_irq_domain_ops = {
> + .map = ksz8463_irq_domain_map,
> + .xlate = irq_domain_xlate_twocell,
> +};
> +
> +static int ksz8463_girq_setup(struct ksz_device *dev)
> +{
> + struct ksz_irq *girq = &dev->girq;
> +
> + girq->nirqs = 8;
> + girq->reg_mask = KSZ8463_REG_IER;
> + girq->reg_status = KSZ8463_REG_ISR;
> + girq->masked = 0;
> + snprintf(girq->name, sizeof(girq->name), "ksz8463-girq");
> +
> + girq->irq_num = dev->irq;
> +
> + return ksz_irq_common_setup(dev, girq, &ksz8463_irq_domain_ops);
> +}
> +
> static int ksz8463_reset_switch(struct ksz_device *dev)
> {
> ksz_cfg(dev, KSZ8463_REG_SW_RESET, KSZ8463_GLOBAL_SOFTWARE_RESET, true);
> @@ -2407,21 +2466,50 @@ static int ksz8463_setup(struct dsa_switch *ds)
> p = &dev->ports[dev->cpu_port];
> p->learning = true;
>
> + if (dev->irq > 0) {
> + ret = ksz8463_girq_setup(dev);
> + if (ret)
> + return ret;
> +
> + ret = ksz8463_ptp_irq_setup(ds);
> + if (ret)
> + goto free_girq;
> + }
> +
> ret = ksz_mdio_register(dev);
> if (ret < 0) {
> dev_err(dev->dev, "failed to register the mdio");
> - return ret;
> + goto free_ptp_irq;
> }
>
> ret = ksz_dcb_init(dev);
> if (ret)
> - return ret;
> + goto free_ptp_irq;
>
> /* start switch */
> regmap_update_bits(ksz_regmap_8(dev), regs[S_START_CTRL],
> SW_START, SW_START);
>
> return 0;
> +
> +free_ptp_irq:
> + if (dev->irq > 0)
> + ksz8463_ptp_irq_free(ds);
> +free_girq:
> + if (dev->irq > 0)
> + ksz_irq_free(&dev->girq);
> +
> + return ret;
> +}
> +
> +static void ksz8463_teardown(struct dsa_switch *ds)
> +{
> + struct ksz_device *dev = ds->priv;
> +
> + if (dev->irq > 0) {
> + ksz8463_ptp_irq_free(ds);
> + ksz_irq_free(&dev->girq);
> + }
> }
>
> /**
> @@ -3010,6 +3098,7 @@ const struct dsa_switch_ops ksz8463_switch_ops = {
> .get_tag_protocol = ksz8463_get_tag_protocol,
> .connect_tag_protocol = ksz8463_connect_tag_protocol,
> .setup = ksz8463_setup,
> + .teardown = ksz8463_teardown,
> .phy_read = ksz8463_phy_read16,
> .phy_write = ksz8463_phy_write16,
> .phylink_get_caps = ksz8_phylink_get_caps,
> diff --git a/drivers/net/dsa/microchip/ksz_ptp.c b/drivers/net/dsa/microchip/ksz_ptp.c
> index 8b98039320ad..7a74befda9ad 100644
> --- a/drivers/net/dsa/microchip/ksz_ptp.c
> +++ b/drivers/net/dsa/microchip/ksz_ptp.c
> @@ -32,6 +32,15 @@
>
> #define KSZ_PTP_INT_START 13
>
> +/*
> + * PTP interrupt bit is the bit 12 of the 16-bits ISR/IER. But ksz_common.c only
> + * accesses the high-byte of these registers so the PTP interrupt bit becomes 4.
> + */
> +#define KSZ8463_SRC_PTP_INT 4
> +#define KSZ8463_PTP_PORT1_INT_START 12
> +#define KSZ8463_PTP_PORT2_INT_START 14
> +#define KSZ8463_PTP_INT_START KSZ8463_PTP_PORT1_INT_START
> +
> static int ksz_ptp_tou_gpio(struct ksz_device *dev)
> {
> int ret;
> @@ -1129,6 +1138,126 @@ static int ksz_ptp_msg_irq_setup(struct ksz_port *port, u8 n)
> return ret;
> }
>
> +static int ksz8463_ptp_port_irq_setup(struct ksz_irq *ptpirq,
> + struct ksz_port *port, int hw_irq)
> +{
> + u16 ts_reg[] = {KSZ8463_REG_PORT_SYNC_TS, KSZ8463_REG_PORT_DREQ_TS};
> + static const char * const name[] = {"sync-msg", "delay-msg"};
> + const struct ksz_dev_ops *ops = port->ksz_dev->dev_ops;
> + struct ksz_ptp_irq *ptpmsg_irq;
> + int ret;
> + int i;
> +
> + init_completion(&port->tstamp_msg_comp);
> +
> + for (i = 0; i < 2; i++) {
> + ptpmsg_irq = &port->ptpmsg_irq[i];
> + ptpmsg_irq->num = irq_create_mapping(ptpirq->domain,
> + hw_irq + i);
> + if (!ptpmsg_irq->num)
> + goto release_msg_irq;
> +
Sashiko says : "Does this code return an uninitialized or incorrect
error code on failure?"
Yes it does, I'll fix it in next iteration
Best regards,
Bastien
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH net-next 05/10] net: dsa: microchip: adapt port offset for KSZ8463's PTP register
2026-07-09 6:42 [PATCH net-next 00/10] net: dsa: microchip: add PTP support for KSZ8463 Bastien Curutchet (Schneider Electric)
` (3 preceding siblings ...)
2026-07-09 6:42 ` [PATCH net-next 04/10] net: dsa: microchip: add PTP interrupt handling for KSZ8463 Bastien Curutchet (Schneider Electric)
@ 2026-07-09 6:42 ` Bastien Curutchet (Schneider Electric)
2026-07-09 6:42 ` [PATCH net-next 06/10] net: dsa: tag_ksz: move the KSZ8795 tag handling below ksz_xmit_timestamp() Bastien Curutchet (Schneider Electric)
` (4 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Bastien Curutchet (Schneider Electric) @ 2026-07-09 6:42 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Richard Cochran, Russell King, Simon Horman, Maxime Chevallier
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel, Bastien Curutchet (Schneider Electric)
In KSZ8463 register's layout, the offset between port 1 and port 2
registers isn't the same in the generic control register area than in
the PTP register area. The get_port_addr() always uses the same offset
so it doesn't work when it's used to access PTP registers.
Adapt the port offset in get_port_addr() when the accessed register is
in the PTP area.
Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
---
drivers/net/dsa/microchip/ksz8.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c
index c099a7005808..5e5bfc5cae2d 100644
--- a/drivers/net/dsa/microchip/ksz8.c
+++ b/drivers/net/dsa/microchip/ksz8.c
@@ -2831,6 +2831,9 @@ static u32 ksz8_get_port_addr(int port, int offset)
static u32 ksz8463_get_port_addr(int port, int offset)
{
+ if (offset >= KSZ8463_PTP_CLK_CTRL)
+ return offset + 0x20 * port;
+
return offset + 0x18 * port;
}
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH net-next 06/10] net: dsa: tag_ksz: move the KSZ8795 tag handling below ksz_xmit_timestamp()
2026-07-09 6:42 [PATCH net-next 00/10] net: dsa: microchip: add PTP support for KSZ8463 Bastien Curutchet (Schneider Electric)
` (4 preceding siblings ...)
2026-07-09 6:42 ` [PATCH net-next 05/10] net: dsa: microchip: adapt port offset for KSZ8463's PTP register Bastien Curutchet (Schneider Electric)
@ 2026-07-09 6:42 ` Bastien Curutchet (Schneider Electric)
2026-07-09 6:42 ` [PATCH net-next 07/10] net: dsa: tag_ksz: share code for KSZ8795 and KSZ9893 xmit operations Bastien Curutchet (Schneider Electric)
` (3 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Bastien Curutchet (Schneider Electric) @ 2026-07-09 6:42 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Richard Cochran, Russell King, Simon Horman, Maxime Chevallier
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel, Bastien Curutchet (Schneider Electric)
Upcoming patch reduces code duplication between KSZ8795 and KSZ9893 by
introducing a common xmit() function. This rework needs the KSZ8795
handlers to be implemented below ksz_defer_xmit().
Do the move now to reduce the noise in next patch.
No functionnal change is intended in this patch.
Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
---
net/dsa/tag_ksz.c | 132 +++++++++++++++++++++++++++---------------------------
1 file changed, 66 insertions(+), 66 deletions(-)
diff --git a/net/dsa/tag_ksz.c b/net/dsa/tag_ksz.c
index 67fa89f102e0..f58ce0f0e9e4 100644
--- a/net/dsa/tag_ksz.c
+++ b/net/dsa/tag_ksz.c
@@ -103,72 +103,6 @@ static struct sk_buff *ksz_common_rcv(struct sk_buff *skb,
return skb;
}
-/*
- * For Ingress (Host -> KSZ8795), 1 byte is added before FCS.
- * ---------------------------------------------------------------------------
- * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag(1byte)|FCS(4bytes)
- * ---------------------------------------------------------------------------
- * tag : each bit represents port (eg, 0x01=port1, 0x02=port2, 0x10=port5)
- *
- * For Egress (KSZ8795 -> Host), 1 byte is added before FCS.
- * ---------------------------------------------------------------------------
- * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag0(1byte)|FCS(4bytes)
- * ---------------------------------------------------------------------------
- * tag0 : zero-based value represents port
- * (eg, 0x0=port1, 0x2=port3, 0x3=port4)
- */
-
-#define KSZ8795_TAIL_TAG_EG_PORT_M GENMASK(1, 0)
-#define KSZ8795_TAIL_TAG_OVERRIDE BIT(6)
-#define KSZ8795_TAIL_TAG_LOOKUP BIT(7)
-
-static struct sk_buff *ksz8795_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- struct ethhdr *hdr;
- u8 *tag;
-
- if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) {
- kfree_skb(skb);
- return NULL;
- }
-
- /* Tag encoding */
- tag = skb_put(skb, KSZ_INGRESS_TAG_LEN);
- hdr = skb_eth_hdr(skb);
-
- *tag = dsa_xmit_port_mask(skb, dev);
- if (is_link_local_ether_addr(hdr->h_dest))
- *tag |= KSZ8795_TAIL_TAG_OVERRIDE;
-
- return skb;
-}
-
-static struct sk_buff *ksz8795_rcv(struct sk_buff *skb, struct net_device *dev)
-{
- u8 *tag;
-
- if (skb_linearize(skb)) {
- kfree_skb(skb);
- return NULL;
- }
-
- tag = skb_tail_pointer(skb) - KSZ_EGRESS_TAG_LEN;
-
- return ksz_common_rcv(skb, dev, tag[0] & KSZ8795_TAIL_TAG_EG_PORT_M,
- KSZ_EGRESS_TAG_LEN);
-}
-
-static const struct dsa_device_ops ksz8795_netdev_ops = {
- .name = KSZ8795_NAME,
- .proto = DSA_TAG_PROTO_KSZ8795,
- .xmit = ksz8795_xmit,
- .rcv = ksz8795_rcv,
- .needed_tailroom = KSZ_INGRESS_TAG_LEN,
-};
-
-DSA_TAG_DRIVER(ksz8795_netdev_ops);
-MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ8795, KSZ8795_NAME);
-
/*
* For Ingress (Host -> KSZ9477), 2/6 bytes are added before FCS.
* ---------------------------------------------------------------------------
@@ -353,6 +287,72 @@ static const struct dsa_device_ops ksz9477_netdev_ops = {
DSA_TAG_DRIVER(ksz9477_netdev_ops);
MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ9477, KSZ9477_NAME);
+/*
+ * For Ingress (Host -> KSZ8795), 1 byte is added before FCS.
+ * ---------------------------------------------------------------------------
+ * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag(1byte)|FCS(4bytes)
+ * ---------------------------------------------------------------------------
+ * tag : each bit represents port (eg, 0x01=port1, 0x02=port2, 0x10=port5)
+ *
+ * For Egress (KSZ8795 -> Host), 1 byte is added before FCS.
+ * ---------------------------------------------------------------------------
+ * DA(6bytes)|SA(6bytes)|....|Data(nbytes)|tag0(1byte)|FCS(4bytes)
+ * ---------------------------------------------------------------------------
+ * tag0 : zero-based value represents port
+ * (eg, 0x0=port1, 0x2=port3, 0x3=port4)
+ */
+
+#define KSZ8795_TAIL_TAG_EG_PORT_M GENMASK(1, 0)
+#define KSZ8795_TAIL_TAG_OVERRIDE BIT(6)
+#define KSZ8795_TAIL_TAG_LOOKUP BIT(7)
+
+static struct sk_buff *ksz8795_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ethhdr *hdr;
+ u8 *tag;
+
+ if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) {
+ kfree_skb(skb);
+ return NULL;
+ }
+
+ /* Tag encoding */
+ tag = skb_put(skb, KSZ_INGRESS_TAG_LEN);
+ hdr = skb_eth_hdr(skb);
+
+ *tag = dsa_xmit_port_mask(skb, dev);
+ if (is_link_local_ether_addr(hdr->h_dest))
+ *tag |= KSZ8795_TAIL_TAG_OVERRIDE;
+
+ return skb;
+}
+
+static struct sk_buff *ksz8795_rcv(struct sk_buff *skb, struct net_device *dev)
+{
+ u8 *tag;
+
+ if (skb_linearize(skb)) {
+ kfree_skb(skb);
+ return NULL;
+ }
+
+ tag = skb_tail_pointer(skb) - KSZ_EGRESS_TAG_LEN;
+
+ return ksz_common_rcv(skb, dev, tag[0] & KSZ8795_TAIL_TAG_EG_PORT_M,
+ KSZ_EGRESS_TAG_LEN);
+}
+
+static const struct dsa_device_ops ksz8795_netdev_ops = {
+ .name = KSZ8795_NAME,
+ .proto = DSA_TAG_PROTO_KSZ8795,
+ .xmit = ksz8795_xmit,
+ .rcv = ksz8795_rcv,
+ .needed_tailroom = KSZ_INGRESS_TAG_LEN,
+};
+
+DSA_TAG_DRIVER(ksz8795_netdev_ops);
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ8795, KSZ8795_NAME);
+
#define KSZ9893_TAIL_TAG_PRIO GENMASK(4, 3)
#define KSZ9893_TAIL_TAG_OVERRIDE BIT(5)
#define KSZ9893_TAIL_TAG_LOOKUP BIT(6)
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH net-next 07/10] net: dsa: tag_ksz: share code for KSZ8795 and KSZ9893 xmit operations
2026-07-09 6:42 [PATCH net-next 00/10] net: dsa: microchip: add PTP support for KSZ8463 Bastien Curutchet (Schneider Electric)
` (5 preceding siblings ...)
2026-07-09 6:42 ` [PATCH net-next 06/10] net: dsa: tag_ksz: move the KSZ8795 tag handling below ksz_xmit_timestamp() Bastien Curutchet (Schneider Electric)
@ 2026-07-09 6:42 ` Bastien Curutchet (Schneider Electric)
2026-07-09 6:42 ` [PATCH net-next 08/10] net: dsa: microchip: add KSZ8463 tail tag handling Bastien Curutchet (Schneider Electric)
` (2 subsequent siblings)
9 siblings, 0 replies; 13+ messages in thread
From: Bastien Curutchet (Schneider Electric) @ 2026-07-09 6:42 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Richard Cochran, Russell King, Simon Horman, Maxime Chevallier
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel, Bastien Curutchet (Schneider Electric)
KSZ8795 and KSZ9893 have very similar tag handling in the xmit path,
leading to code duplication.
There are only two differences between the two ksz*_xmit():
- the KSZ8795 doesn't handle priorities between frames
- ksz8795_xmit() directly returns the SKB instead of calling
ksz_defer_xmit(). Yet, ksz_defer_xmit() also returns directly the SKB
if no clone is present inside the SKB. Clones are only created by the KSZ
driver when the PTP feature is enabled. Since KSZ8795 doesn't support
PTP, returning the SKB directly or ksz_defer_xmit() is the same.
The upcoming support for the KSZ8463 also requires a similar xmit().
Gather the common code from ksz8795_xmit() and ksz9893_xmit() into a new
ksz_common_xmit() function that takes three input arguments:
- do_tstamp to tell whether ksz_xmit_timestamp() should be called
- prio to give the priority tag (if any)
- override_mask to give the location of the override bit (if any)
Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
---
net/dsa/tag_ksz.c | 73 ++++++++++++++++++++++++++-----------------------------
1 file changed, 35 insertions(+), 38 deletions(-)
diff --git a/net/dsa/tag_ksz.c b/net/dsa/tag_ksz.c
index f58ce0f0e9e4..f8b40437c5fa 100644
--- a/net/dsa/tag_ksz.c
+++ b/net/dsa/tag_ksz.c
@@ -218,6 +218,37 @@ static struct sk_buff *ksz_defer_xmit(struct dsa_port *dp, struct sk_buff *skb)
return NULL;
}
+static struct sk_buff *ksz_common_xmit(struct sk_buff *skb,
+ struct net_device *dev,
+ bool do_tstamp,
+ u8 prio,
+ u8 override_mask)
+{
+ struct dsa_port *dp = dsa_user_to_port(dev);
+ struct ethhdr *hdr;
+ u8 *tag;
+
+ if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) {
+ kfree_skb(skb);
+ return NULL;
+ }
+
+ /* Tag encoding */
+ if (do_tstamp)
+ ksz_xmit_timestamp(dp, skb);
+
+ tag = skb_put(skb, KSZ_INGRESS_TAG_LEN);
+ hdr = skb_eth_hdr(skb);
+
+ *tag = dsa_xmit_port_mask(skb, dev);
+ *tag |= prio;
+
+ if (is_link_local_ether_addr(hdr->h_dest))
+ *tag |= override_mask;
+
+ return ksz_defer_xmit(dp, skb);
+}
+
static struct sk_buff *ksz9477_xmit(struct sk_buff *skb,
struct net_device *dev)
{
@@ -308,23 +339,7 @@ MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ9477, KSZ9477_NAME);
static struct sk_buff *ksz8795_xmit(struct sk_buff *skb, struct net_device *dev)
{
- struct ethhdr *hdr;
- u8 *tag;
-
- if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) {
- kfree_skb(skb);
- return NULL;
- }
-
- /* Tag encoding */
- tag = skb_put(skb, KSZ_INGRESS_TAG_LEN);
- hdr = skb_eth_hdr(skb);
-
- *tag = dsa_xmit_port_mask(skb, dev);
- if (is_link_local_ether_addr(hdr->h_dest))
- *tag |= KSZ8795_TAIL_TAG_OVERRIDE;
-
- return skb;
+ return ksz_common_xmit(skb, dev, false, 0, KSZ8795_TAIL_TAG_OVERRIDE);
}
static struct sk_buff *ksz8795_rcv(struct sk_buff *skb, struct net_device *dev)
@@ -362,28 +377,10 @@ static struct sk_buff *ksz9893_xmit(struct sk_buff *skb,
{
u16 queue_mapping = skb_get_queue_mapping(skb);
u8 prio = netdev_txq_to_tc(dev, queue_mapping);
- struct dsa_port *dp = dsa_user_to_port(dev);
- struct ethhdr *hdr;
- u8 *tag;
-
- if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) {
- kfree_skb(skb);
- return NULL;
- }
-
- /* Tag encoding */
- ksz_xmit_timestamp(dp, skb);
-
- tag = skb_put(skb, KSZ_INGRESS_TAG_LEN);
- hdr = skb_eth_hdr(skb);
-
- *tag = dsa_xmit_port_mask(skb, dev);
- *tag |= FIELD_PREP(KSZ9893_TAIL_TAG_PRIO, prio);
- if (is_link_local_ether_addr(hdr->h_dest))
- *tag |= KSZ9893_TAIL_TAG_OVERRIDE;
-
- return ksz_defer_xmit(dp, skb);
+ return ksz_common_xmit(skb, dev, true,
+ FIELD_PREP(KSZ9893_TAIL_TAG_PRIO, prio),
+ KSZ9893_TAIL_TAG_OVERRIDE);
}
static const struct dsa_device_ops ksz9893_netdev_ops = {
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH net-next 08/10] net: dsa: microchip: add KSZ8463 tail tag handling
2026-07-09 6:42 [PATCH net-next 00/10] net: dsa: microchip: add PTP support for KSZ8463 Bastien Curutchet (Schneider Electric)
` (6 preceding siblings ...)
2026-07-09 6:42 ` [PATCH net-next 07/10] net: dsa: tag_ksz: share code for KSZ8795 and KSZ9893 xmit operations Bastien Curutchet (Schneider Electric)
@ 2026-07-09 6:42 ` Bastien Curutchet (Schneider Electric)
2026-07-09 6:42 ` [PATCH net-next 09/10] net: dsa: microchip: explicitly enable detection of L2 PTP frames Bastien Curutchet (Schneider Electric)
2026-07-09 6:42 ` [PATCH net-next 10/10] net: dsa: microchip: add two-steps PTP support for KSZ8463 Bastien Curutchet (Schneider Electric)
9 siblings, 0 replies; 13+ messages in thread
From: Bastien Curutchet (Schneider Electric) @ 2026-07-09 6:42 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Richard Cochran, Russell King, Simon Horman, Maxime Chevallier
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel, Bastien Curutchet (Schneider Electric)
KSZ8463 uses the KSZ9893 DSA TAG driver. However, the KSZ8463 doesn't
use the tail tag to convey timestamps to the host as KSZ9893 does. It
uses the reserved fields in the PTP header instead.
Add a KSZ8463-specific DSA_TAG driver to handle KSZ8463 timestamps.
There is no information in the tail tag to distinguish PTP packets from
others so use the ptp_classify_raw() helper to find the PTP packets and
extract the timestamp from their PTP headers.
Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
---
drivers/net/dsa/microchip/ksz8.c | 4 +--
include/net/dsa.h | 2 ++
net/dsa/tag_ksz.c | 62 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 66 insertions(+), 2 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c
index 5e5bfc5cae2d..ac9e8ef5774a 100644
--- a/drivers/net/dsa/microchip/ksz8.c
+++ b/drivers/net/dsa/microchip/ksz8.c
@@ -2966,7 +2966,7 @@ static enum dsa_tag_protocol ksz8463_get_tag_protocol(struct dsa_switch *ds,
int port,
enum dsa_tag_protocol mp)
{
- return DSA_TAG_PROTO_KSZ9893;
+ return DSA_TAG_PROTO_KSZ8463;
}
static int ksz8463_connect_tag_protocol(struct dsa_switch *ds,
@@ -2974,7 +2974,7 @@ static int ksz8463_connect_tag_protocol(struct dsa_switch *ds,
{
struct ksz_tagger_data *tagger_data;
- if (proto != DSA_TAG_PROTO_KSZ9893)
+ if (proto != DSA_TAG_PROTO_KSZ8463)
return -EPROTONOSUPPORT;
tagger_data = ksz_tagger_data(ds);
diff --git a/include/net/dsa.h b/include/net/dsa.h
index 8c16ef23cc10..6f7f5c17b532 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -59,6 +59,7 @@ struct tc_action;
#define DSA_TAG_PROTO_MXL_GSW1XX_VALUE 31
#define DSA_TAG_PROTO_MXL862_VALUE 32
#define DSA_TAG_PROTO_NETC_VALUE 33
+#define DSA_TAG_PROTO_KSZ8463_VALUE 34
enum dsa_tag_protocol {
DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE,
@@ -95,6 +96,7 @@ enum dsa_tag_protocol {
DSA_TAG_PROTO_MXL_GSW1XX = DSA_TAG_PROTO_MXL_GSW1XX_VALUE,
DSA_TAG_PROTO_MXL862 = DSA_TAG_PROTO_MXL862_VALUE,
DSA_TAG_PROTO_NETC = DSA_TAG_PROTO_NETC_VALUE,
+ DSA_TAG_PROTO_KSZ8463 = DSA_TAG_PROTO_KSZ8463_VALUE,
};
struct dsa_switch;
diff --git a/net/dsa/tag_ksz.c b/net/dsa/tag_ksz.c
index f8b40437c5fa..633511679cad 100644
--- a/net/dsa/tag_ksz.c
+++ b/net/dsa/tag_ksz.c
@@ -12,6 +12,7 @@
#include "tag.h"
+#define KSZ8463_NAME "ksz8463"
#define KSZ8795_NAME "ksz8795"
#define KSZ9477_NAME "ksz9477"
#define KSZ9893_NAME "ksz9893"
@@ -396,6 +397,66 @@ static const struct dsa_device_ops ksz9893_netdev_ops = {
DSA_TAG_DRIVER(ksz9893_netdev_ops);
MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ9893, KSZ9893_NAME);
+#define KSZ8463_TAIL_TAG_PRIO GENMASK(4, 3)
+#define KSZ8463_TAIL_TAG_EG_PORT_M GENMASK(2, 0)
+
+static struct sk_buff *ksz8463_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ u16 queue_mapping = skb_get_queue_mapping(skb);
+ u8 prio = netdev_txq_to_tc(dev, queue_mapping);
+
+ return ksz_common_xmit(skb, dev, false,
+ FIELD_PREP(KSZ8463_TAIL_TAG_PRIO, prio),
+ 0);
+}
+
+static struct sk_buff *ksz8463_rcv(struct sk_buff *skb, struct net_device *dev)
+{
+ unsigned int len = KSZ_EGRESS_TAG_LEN;
+ struct ptp_header *ptp_hdr;
+ unsigned int ptp_class;
+ unsigned int port;
+ ktime_t ts;
+ u8 *tag;
+
+ if (skb_linearize(skb))
+ return NULL;
+
+ /* Tag decoding */
+ tag = skb_tail_pointer(skb) - KSZ_EGRESS_TAG_LEN;
+ port = tag[0] & KSZ8463_TAIL_TAG_EG_PORT_M;
+
+ __skb_push(skb, ETH_HLEN);
+ ptp_class = ptp_classify_raw(skb);
+ __skb_pull(skb, ETH_HLEN);
+ if (ptp_class == PTP_CLASS_NONE)
+ goto common_rcv;
+
+ ptp_hdr = ptp_parse_header(skb, ptp_class);
+ if (ptp_hdr) {
+ ts = ksz_decode_tstamp(get_unaligned_be32(&ptp_hdr->reserved2));
+ KSZ_SKB_CB(skb)->tstamp = ts;
+ ptp_hdr->reserved2 = 0;
+ }
+
+common_rcv:
+ return ksz_common_rcv(skb, dev, port, len);
+}
+
+static const struct dsa_device_ops ksz8463_netdev_ops = {
+ .name = KSZ8463_NAME,
+ .proto = DSA_TAG_PROTO_KSZ8463,
+ .xmit = ksz8463_xmit,
+ .rcv = ksz8463_rcv,
+ .connect = ksz_connect,
+ .disconnect = ksz_disconnect,
+ .needed_tailroom = KSZ_INGRESS_TAG_LEN,
+};
+
+DSA_TAG_DRIVER(ksz8463_netdev_ops);
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_KSZ8463, KSZ8463_NAME);
+
/* For xmit, 2/6 bytes are added before FCS.
* ---------------------------------------------------------------------------
* DA(6bytes)|SA(6bytes)|....|Data(nbytes)|ts(4bytes)|tag0(1byte)|tag1(1byte)|
@@ -468,6 +529,7 @@ DSA_TAG_DRIVER(lan937x_netdev_ops);
MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_LAN937X, LAN937X_NAME);
static struct dsa_tag_driver *dsa_tag_driver_array[] = {
+ &DSA_TAG_DRIVER_NAME(ksz8463_netdev_ops),
&DSA_TAG_DRIVER_NAME(ksz8795_netdev_ops),
&DSA_TAG_DRIVER_NAME(ksz9477_netdev_ops),
&DSA_TAG_DRIVER_NAME(ksz9893_netdev_ops),
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH net-next 09/10] net: dsa: microchip: explicitly enable detection of L2 PTP frames
2026-07-09 6:42 [PATCH net-next 00/10] net: dsa: microchip: add PTP support for KSZ8463 Bastien Curutchet (Schneider Electric)
` (7 preceding siblings ...)
2026-07-09 6:42 ` [PATCH net-next 08/10] net: dsa: microchip: add KSZ8463 tail tag handling Bastien Curutchet (Schneider Electric)
@ 2026-07-09 6:42 ` Bastien Curutchet (Schneider Electric)
2026-07-09 6:42 ` [PATCH net-next 10/10] net: dsa: microchip: add two-steps PTP support for KSZ8463 Bastien Curutchet (Schneider Electric)
9 siblings, 0 replies; 13+ messages in thread
From: Bastien Curutchet (Schneider Electric) @ 2026-07-09 6:42 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Richard Cochran, Russell King, Simon Horman, Maxime Chevallier
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel, Bastien Curutchet (Schneider Electric)
Detection of L2 PTP frames needs to be enabled for PTP to work at the L2
layer. The bit enabling this detection is set by default on the switches
currently supported by the driver, but it is unset by default on the
KSZ8463 for which support will be added in upcoming patches.
Explicitly enable the detection of L2 PTP frames for all switches when
PTP is enabled.
Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
---
drivers/net/dsa/microchip/ksz_ptp.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz_ptp.c b/drivers/net/dsa/microchip/ksz_ptp.c
index 7a74befda9ad..5323fdc8862e 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.c
+++ b/drivers/net/dsa/microchip/ksz_ptp.c
@@ -953,8 +953,9 @@ int ksz_ptp_clock_register(struct dsa_switch *ds)
/* Currently only P2P mode is supported. When 802_1AS bit is set, it
* forwards all PTP packets to host port and none to other ports.
*/
- ret = ksz_rmw16(dev, regs[PTP_MSG_CONF1], PTP_TC_P2P | PTP_802_1AS,
- PTP_TC_P2P | PTP_802_1AS);
+ ret = ksz_rmw16(dev, regs[PTP_MSG_CONF1],
+ PTP_TC_P2P | PTP_802_1AS | PTP_ETH_ENABLE,
+ PTP_TC_P2P | PTP_802_1AS | PTP_ETH_ENABLE);
if (ret)
return ret;
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH net-next 10/10] net: dsa: microchip: add two-steps PTP support for KSZ8463
2026-07-09 6:42 [PATCH net-next 00/10] net: dsa: microchip: add PTP support for KSZ8463 Bastien Curutchet (Schneider Electric)
` (8 preceding siblings ...)
2026-07-09 6:42 ` [PATCH net-next 09/10] net: dsa: microchip: explicitly enable detection of L2 PTP frames Bastien Curutchet (Schneider Electric)
@ 2026-07-09 6:42 ` Bastien Curutchet (Schneider Electric)
9 siblings, 0 replies; 13+ messages in thread
From: Bastien Curutchet (Schneider Electric) @ 2026-07-09 6:42 UTC (permalink / raw)
To: Woojung Huh, UNGLinuxDriver, Andrew Lunn, Vladimir Oltean,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Richard Cochran, Russell King, Simon Horman, Maxime Chevallier
Cc: Pascal Eberhard, Miquèl Raynal, Thomas Petazzoni, netdev,
linux-kernel, Bastien Curutchet (Schneider Electric)
The KSZ8463 switch supports PTP but it's not supported by the driver.
Add L2 two-step PTP support for the KSZ8463. IPv4 and IPv6 layers aren't
supported. Neither is one-step PTP. Use KSZ8463-specific implementations
of the .get_ts_info and .port_hwtstamp_set callbacks.
The pdelay_req and pdelay_resp timestamps share one interrupt bit status
while they're located in two different registers. So introduce
last_tx_is_pdelayresp to keep track of the last sent event type. This
flag is set by the xmit worker right before sending the packet and then
used in the interrupt handler to retrieve the timestamp location.
Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com>
---
drivers/net/dsa/microchip/ksz8.c | 26 +++++--
drivers/net/dsa/microchip/ksz8_reg.h | 1 +
drivers/net/dsa/microchip/ksz_common.h | 1 +
drivers/net/dsa/microchip/ksz_ptp.c | 127 +++++++++++++++++++++++++++++++-
drivers/net/dsa/microchip/ksz_ptp.h | 7 ++
drivers/net/dsa/microchip/ksz_ptp_reg.h | 4 +
6 files changed, 159 insertions(+), 7 deletions(-)
diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c
index ac9e8ef5774a..941ae9f66f70 100644
--- a/drivers/net/dsa/microchip/ksz8.c
+++ b/drivers/net/dsa/microchip/ksz8.c
@@ -242,8 +242,11 @@ static int ksz8463_girq_setup(struct ksz_device *dev)
static int ksz8463_reset_switch(struct ksz_device *dev)
{
- ksz_cfg(dev, KSZ8463_REG_SW_RESET, KSZ8463_GLOBAL_SOFTWARE_RESET, true);
- ksz_cfg(dev, KSZ8463_REG_SW_RESET, KSZ8463_GLOBAL_SOFTWARE_RESET,
+ ksz_cfg(dev, KSZ8463_REG_SW_RESET,
+ KSZ8463_GLOBAL_SOFTWARE_RESET | KSZ8463_PTP_SOFTWARE_RESET,
+ true);
+ ksz_cfg(dev, KSZ8463_REG_SW_RESET,
+ KSZ8463_GLOBAL_SOFTWARE_RESET | KSZ8463_PTP_SOFTWARE_RESET,
false);
return 0;
}
@@ -2474,17 +2477,24 @@ static int ksz8463_setup(struct dsa_switch *ds)
ret = ksz8463_ptp_irq_setup(ds);
if (ret)
goto free_girq;
+
+ ret = ksz_ptp_clock_register(ds);
+ if (ret) {
+ dev_err(dev->dev, "Failed to register PTP clock: %d\n",
+ ret);
+ goto free_ptp_irq;
+ }
}
ret = ksz_mdio_register(dev);
if (ret < 0) {
dev_err(dev->dev, "failed to register the mdio");
- goto free_ptp_irq;
+ goto ptp_clock_unregister;
}
ret = ksz_dcb_init(dev);
if (ret)
- goto free_ptp_irq;
+ goto ptp_clock_unregister;
/* start switch */
regmap_update_bits(ksz_regmap_8(dev), regs[S_START_CTRL],
@@ -2492,6 +2502,9 @@ static int ksz8463_setup(struct dsa_switch *ds)
return 0;
+ptp_clock_unregister:
+ if (dev->irq > 0)
+ ksz_ptp_clock_unregister(ds);
free_ptp_irq:
if (dev->irq > 0)
ksz8463_ptp_irq_free(ds);
@@ -2507,6 +2520,7 @@ static void ksz8463_teardown(struct dsa_switch *ds)
struct ksz_device *dev = ds->priv;
if (dev->irq > 0) {
+ ksz_ptp_clock_unregister(ds);
ksz8463_ptp_irq_free(ds);
ksz_irq_free(&dev->girq);
}
@@ -3129,9 +3143,9 @@ const struct dsa_switch_ops ksz8463_switch_ops = {
.port_max_mtu = ksz88xx_max_mtu,
.suspend = ksz_suspend,
.resume = ksz_resume,
- .get_ts_info = ksz_get_ts_info,
+ .get_ts_info = ksz8463_get_ts_info,
.port_hwtstamp_get = ksz_hwtstamp_get,
- .port_hwtstamp_set = ksz_hwtstamp_set,
+ .port_hwtstamp_set = ksz8463_hwtstamp_set,
.port_txtstamp = ksz_port_txtstamp,
.port_rxtstamp = ksz_port_rxtstamp,
.port_setup_tc = ksz8_setup_tc,
diff --git a/drivers/net/dsa/microchip/ksz8_reg.h b/drivers/net/dsa/microchip/ksz8_reg.h
index 981ab441d9b7..6bc511da1f7d 100644
--- a/drivers/net/dsa/microchip/ksz8_reg.h
+++ b/drivers/net/dsa/microchip/ksz8_reg.h
@@ -786,6 +786,7 @@
#define KSZ8463_REG_SW_RESET 0x126
#define KSZ8463_GLOBAL_SOFTWARE_RESET BIT(0)
+#define KSZ8463_PTP_SOFTWARE_RESET BIT(2)
#define KSZ8463_PTP_CLK_CTRL 0x600
diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
index 0f2abb22ca91..cbe98494578c 100644
--- a/drivers/net/dsa/microchip/ksz_common.h
+++ b/drivers/net/dsa/microchip/ksz_common.h
@@ -194,6 +194,7 @@ struct ksz_port {
struct kernel_hwtstamp_config tstamp_config;
bool hwts_tx_en;
bool hwts_rx_en;
+ bool last_tx_is_pdelayresp;
struct ksz_irq ptpirq;
struct ksz_ptp_irq ptpmsg_irq[3];
ktime_t tstamp_msg;
diff --git a/drivers/net/dsa/microchip/ksz_ptp.c b/drivers/net/dsa/microchip/ksz_ptp.c
index 5323fdc8862e..a3cc7b97caaf 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.c
+++ b/drivers/net/dsa/microchip/ksz_ptp.c
@@ -297,6 +297,31 @@ static int ksz_ptp_enable_mode(struct ksz_device *dev)
tag_en ? PTP_ENABLE : 0);
}
+int ksz8463_get_ts_info(struct dsa_switch *ds, int port,
+ struct kernel_ethtool_ts_info *ts)
+{
+ struct ksz_device *dev = ds->priv;
+ struct ksz_ptp_data *ptp_data;
+
+ ptp_data = &dev->ptp_data;
+
+ if (!ptp_data->clock)
+ return -ENODEV;
+
+ ts->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE |
+ SOF_TIMESTAMPING_RX_HARDWARE |
+ SOF_TIMESTAMPING_RAW_HARDWARE;
+
+ ts->tx_types = BIT(HWTSTAMP_TX_OFF) | BIT(HWTSTAMP_TX_ON);
+
+ ts->rx_filters = BIT(HWTSTAMP_FILTER_NONE) |
+ BIT(HWTSTAMP_FILTER_PTP_V2_L2_EVENT);
+
+ ts->phc_index = ptp_clock_index(ptp_data->clock);
+
+ return 0;
+}
+
/* The function is return back the capability of timestamping feature when
* requested through ethtool -T <interface> utility
*/
@@ -341,6 +366,72 @@ int ksz_hwtstamp_get(struct dsa_switch *ds, int port,
return 0;
}
+static int ksz8463_set_hwtstamp_config(struct ksz_device *dev,
+ struct ksz_port *prt,
+ struct kernel_hwtstamp_config *config)
+{
+ const u16 *regs = dev->info->regs;
+ int ret;
+
+ if (config->flags)
+ return -EINVAL;
+
+ switch (config->tx_type) {
+ case HWTSTAMP_TX_OFF:
+ prt->ptpmsg_irq[KSZ8463_SYNC_MSG].ts_en = false;
+ prt->ptpmsg_irq[KSZ8463_XDREQ_PDRES_MSG].ts_en = false;
+ prt->hwts_tx_en = false;
+ break;
+ case HWTSTAMP_TX_ON:
+ prt->ptpmsg_irq[KSZ8463_SYNC_MSG].ts_en = true;
+ prt->ptpmsg_irq[KSZ8463_XDREQ_PDRES_MSG].ts_en = true;
+ prt->hwts_tx_en = true;
+
+ ret = ksz_rmw16(dev, regs[PTP_MSG_CONF1], PTP_1STEP, 0);
+ if (ret)
+ return ret;
+
+ break;
+ default:
+ return -ERANGE;
+ }
+
+ switch (config->rx_filter) {
+ case HWTSTAMP_FILTER_NONE:
+ prt->hwts_rx_en = false;
+ break;
+ case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
+ config->rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT;
+ prt->hwts_rx_en = true;
+ break;
+ default:
+ config->rx_filter = HWTSTAMP_FILTER_NONE;
+ return -ERANGE;
+ }
+
+ return ksz_ptp_enable_mode(dev);
+}
+
+int ksz8463_hwtstamp_set(struct dsa_switch *ds, int port,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack)
+{
+ struct ksz_device *dev = ds->priv;
+ struct ksz_port *prt;
+ int ret;
+
+ prt = &dev->ports[port];
+
+ ret = ksz8463_set_hwtstamp_config(dev, prt, config);
+ if (ret)
+ return ret;
+
+ prt->tstamp_config = *config;
+
+ return 0;
+}
+
static int ksz_set_hwtstamp_config(struct ksz_device *dev,
struct ksz_port *prt,
struct kernel_hwtstamp_config *config)
@@ -571,6 +662,28 @@ static void ksz_ptp_txtstamp_skb(struct ksz_device *dev,
skb_complete_tx_timestamp(skb, &hwtstamps);
}
+static void ksz8463_set_pdelayresp_flag(struct ksz_port *prt,
+ struct sk_buff *skb)
+{
+ struct ptp_header *hdr;
+ unsigned int type;
+ u8 ptp_msg_type;
+
+ if (!ksz_is_ksz8463(prt->ksz_dev))
+ return;
+
+ type = ptp_classify_raw(skb);
+ if (type == PTP_CLASS_NONE)
+ return;
+
+ hdr = ptp_parse_header(skb, type);
+ if (!hdr)
+ return;
+
+ ptp_msg_type = ptp_get_msgtype(hdr, type);
+ prt->last_tx_is_pdelayresp = (ptp_msg_type == PTP_MSGTYPE_PDELAY_RESP);
+}
+
void ksz_port_deferred_xmit(struct kthread_work *work)
{
struct ksz_deferred_xmit_work *xmit_work = work_to_xmit_work(work);
@@ -587,6 +700,8 @@ void ksz_port_deferred_xmit(struct kthread_work *work)
reinit_completion(&prt->tstamp_msg_comp);
+ ksz8463_set_pdelayresp_flag(prt, skb);
+
dsa_enqueue_skb(skb, skb->dev);
ksz_ptp_txtstamp_skb(dev, prt, clone);
@@ -979,7 +1094,17 @@ void ksz_ptp_clock_unregister(struct dsa_switch *ds)
static int ksz_read_ts(struct ksz_port *port, u16 reg, u32 *ts)
{
- return ksz_read32(port->ksz_dev, reg, ts);
+ u16 ts_reg = reg;
+
+ /**
+ * On KSZ8463 DREQ and DRESP timestamps share one interrupt line
+ * so we have to check the nature of the latest event sent to know
+ * where the timestamp is located
+ */
+ if (ksz_is_ksz8463(port->ksz_dev) && port->last_tx_is_pdelayresp)
+ ts_reg += KSZ8463_DRESP_TS_OFFSET;
+
+ return ksz_read32(port->ksz_dev, ts_reg, ts);
}
static irqreturn_t ksz_ptp_msg_thread_fn(int irq, void *dev_id)
diff --git a/drivers/net/dsa/microchip/ksz_ptp.h b/drivers/net/dsa/microchip/ksz_ptp.h
index 11408580031d..7067ec9bd1e6 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.h
+++ b/drivers/net/dsa/microchip/ksz_ptp.h
@@ -39,11 +39,16 @@ void ksz_ptp_clock_unregister(struct dsa_switch *ds);
int ksz_get_ts_info(struct dsa_switch *ds, int port,
struct kernel_ethtool_ts_info *ts);
+int ksz8463_get_ts_info(struct dsa_switch *ds, int port,
+ struct kernel_ethtool_ts_info *ts);
int ksz_hwtstamp_get(struct dsa_switch *ds, int port,
struct kernel_hwtstamp_config *config);
int ksz_hwtstamp_set(struct dsa_switch *ds, int port,
struct kernel_hwtstamp_config *config,
struct netlink_ext_ack *extack);
+int ksz8463_hwtstamp_set(struct dsa_switch *ds, int port,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack);
void ksz_port_txtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb);
void ksz_port_deferred_xmit(struct kthread_work *work);
bool ksz_port_rxtstamp(struct dsa_switch *ds, int port, struct sk_buff *skb,
@@ -82,10 +87,12 @@ static inline int ksz8463_ptp_irq_setup(struct dsa_switch *ds)
static inline void ksz8463_ptp_irq_free(struct dsa_switch *ds) {}
#define ksz_get_ts_info NULL
+#define ksz8463_get_ts_info NULL
#define ksz_hwtstamp_get NULL
#define ksz_hwtstamp_set NULL
+#define ksz8463_hwtstamp_set NULL
#define ksz_port_rxtstamp NULL
diff --git a/drivers/net/dsa/microchip/ksz_ptp_reg.h b/drivers/net/dsa/microchip/ksz_ptp_reg.h
index 1a669d6ee889..65ea8577af75 100644
--- a/drivers/net/dsa/microchip/ksz_ptp_reg.h
+++ b/drivers/net/dsa/microchip/ksz_ptp_reg.h
@@ -137,4 +137,8 @@
#define KSZ_XDREQ_MSG 1
#define KSZ_PDRES_MSG 0
+#define KSZ8463_DRESP_TS_OFFSET (KSZ8463_REG_PORT_DRESP_TS - KSZ8463_REG_PORT_DREQ_TS)
+#define KSZ8463_SYNC_MSG 0
+#define KSZ8463_XDREQ_PDRES_MSG 1
+
#endif
--
2.54.0
^ permalink raw reply related [flat|nested] 13+ messages in thread