* [PATCH net-next 1/7] net: sparx5: call sparx5_start() last in probe()
2026-02-25 9:05 [PATCH net-next 0/7] net: sparx5: clean up probe/remove init and deinit paths Daniel Machon
@ 2026-02-25 9:05 ` Daniel Machon
2026-02-25 9:05 ` [PATCH net-next 2/7] net: sparx5: move netdev and notifier block registration to probe Daniel Machon
` (6 subsequent siblings)
7 siblings, 0 replies; 10+ messages in thread
From: Daniel Machon @ 2026-02-25 9:05 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Steen Hegelund, UNGLinuxDriver, Richard Cochran
Cc: netdev, linux-arm-kernel, linux-kernel
The sparx5_start() function initializes hardware and enables interrupts,
so it should be the last function called in probe(). Over time, new init
functions have been added after it, breaking this assumption.
Move sparx5_start() back to being last. To avoid breaking PTP between
patches in this series, also consolidate all PTP initialization into
sparx5_ptp_init() and add proper teardown via sparx5_ptp_deinit().
Signed-off-by: Daniel Machon <daniel.machon@microchip.com>
---
.../net/ethernet/microchip/sparx5/sparx5_main.c | 35 ++++++++++------------
drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c | 13 ++++++++
2 files changed, 29 insertions(+), 19 deletions(-)
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
index 582145713cfd..d7e823fe4ab9 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
@@ -820,18 +820,6 @@ static int sparx5_start(struct sparx5 *sparx5)
sparx5->xtr_irq = -ENXIO;
}
- if (sparx5->ptp_irq >= 0 &&
- sparx5_has_feature(sparx5, SPX5_FEATURE_PTP)) {
- err = devm_request_threaded_irq(sparx5->dev, sparx5->ptp_irq,
- NULL, ops->ptp_irq_handler,
- IRQF_ONESHOT, "sparx5-ptp",
- sparx5);
- if (err)
- sparx5->ptp_irq = -ENXIO;
-
- sparx5->ptp = 1;
- }
-
return err;
}
@@ -1000,12 +988,6 @@ static int mchp_sparx5_probe(struct platform_device *pdev)
}
}
- err = sparx5_start(sparx5);
- if (err) {
- dev_err(sparx5->dev, "Start failed\n");
- goto cleanup_ports;
- }
-
err = sparx5_qos_init(sparx5);
if (err) {
dev_err(sparx5->dev, "Failed to initialize QoS\n");
@@ -1014,14 +996,25 @@ static int mchp_sparx5_probe(struct platform_device *pdev)
err = sparx5_ptp_init(sparx5);
if (err) {
- dev_err(sparx5->dev, "PTP failed\n");
+ dev_err(sparx5->dev, "Failed to initialize PTP\n");
goto cleanup_ports;
}
INIT_LIST_HEAD(&sparx5->mall_entries);
+ /* Start the rest of the initialization and enable interrupts. Must be
+ * called last, after all subsystems are initialized.
+ */
+ err = sparx5_start(sparx5);
+ if (err) {
+ dev_err(sparx5->dev, "Start failed\n");
+ goto cleanup_ptp;
+ }
+
goto cleanup_config;
+cleanup_ptp:
+ sparx5_ptp_deinit(sparx5);
cleanup_ports:
sparx5_cleanup_ports(sparx5);
if (sparx5->mact_queue)
@@ -1047,6 +1040,10 @@ static void mchp_sparx5_remove(struct platform_device *pdev)
disable_irq(sparx5->fdma_irq);
sparx5->fdma_irq = -ENXIO;
}
+ if (sparx5->ptp_irq) {
+ disable_irq(sparx5->ptp_irq);
+ sparx5->ptp_irq = -ENXIO;
+ }
sparx5_ptp_deinit(sparx5);
ops->fdma_deinit(sparx5);
sparx5_cleanup_ports(sparx5);
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c b/drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c
index 8b2e07821a95..84327ee5b8a2 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c
@@ -606,9 +606,22 @@ static int sparx5_ptp_phc_init(struct sparx5 *sparx5,
int sparx5_ptp_init(struct sparx5 *sparx5)
{
u64 tod_adj = sparx5_ptp_get_nominal_value(sparx5);
+ const struct sparx5_ops *ops = sparx5->data->ops;
struct sparx5_port *port;
int err, i;
+ if (sparx5->ptp_irq >= 0 &&
+ sparx5_has_feature(sparx5, SPX5_FEATURE_PTP)) {
+ err = devm_request_threaded_irq(sparx5->dev, sparx5->ptp_irq,
+ NULL, ops->ptp_irq_handler,
+ IRQF_ONESHOT, "sparx5-ptp",
+ sparx5);
+ if (err)
+ sparx5->ptp_irq = -ENXIO;
+
+ sparx5->ptp = 1;
+ }
+
if (!sparx5->ptp)
return 0;
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH net-next 2/7] net: sparx5: move netdev and notifier block registration to probe
2026-02-25 9:05 [PATCH net-next 0/7] net: sparx5: clean up probe/remove init and deinit paths Daniel Machon
2026-02-25 9:05 ` [PATCH net-next 1/7] net: sparx5: call sparx5_start() last in probe() Daniel Machon
@ 2026-02-25 9:05 ` Daniel Machon
2026-02-25 9:05 ` [PATCH net-next 3/7] net: sparx5: move VCAP initialization " Daniel Machon
` (5 subsequent siblings)
7 siblings, 0 replies; 10+ messages in thread
From: Daniel Machon @ 2026-02-25 9:05 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Steen Hegelund, UNGLinuxDriver, Richard Cochran
Cc: netdev, linux-arm-kernel, linux-kernel
Move netdev registration and notifier block registration from
sparx5_start() to probe(). This allows proper cleanup via goto-based
error labels in probe().
Also, remove the sparx5_cleanup_ports() helper as its functionality is now
split between sparx5_unregister_netdevs() and sparx5_destroy_netdevs()
called at appropriate points.
Signed-off-by: Daniel Machon <daniel.machon@microchip.com>
---
.../net/ethernet/microchip/sparx5/sparx5_main.c | 43 +++++++++++-----------
1 file changed, 22 insertions(+), 21 deletions(-)
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
index d7e823fe4ab9..9887a260bafc 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
@@ -773,20 +773,11 @@ static int sparx5_start(struct sparx5 *sparx5)
mutex_init(&sparx5->mdb_lock);
INIT_LIST_HEAD(&sparx5->mdb_entries);
- err = sparx5_register_netdevs(sparx5);
- if (err)
- return err;
-
sparx5_board_init(sparx5);
- err = sparx5_register_notifier_blocks(sparx5);
- if (err)
- return err;
err = sparx5_vcap_init(sparx5);
- if (err) {
- sparx5_unregister_notifier_blocks(sparx5);
+ if (err)
return err;
- }
/* Start Frame DMA with fallback to register based INJ/XTR */
err = -ENXIO;
@@ -823,12 +814,6 @@ static int sparx5_start(struct sparx5 *sparx5)
return err;
}
-static void sparx5_cleanup_ports(struct sparx5 *sparx5)
-{
- sparx5_unregister_netdevs(sparx5);
- sparx5_destroy_netdevs(sparx5);
-}
-
static int mchp_sparx5_probe(struct platform_device *pdev)
{
struct initial_port_config *configs, *config;
@@ -1002,21 +987,37 @@ static int mchp_sparx5_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&sparx5->mall_entries);
+ err = sparx5_register_netdevs(sparx5);
+ if (err) {
+ dev_err(sparx5->dev, "Failed to register net devices\n");
+ goto cleanup_ptp;
+ }
+
+ err = sparx5_register_notifier_blocks(sparx5);
+ if (err) {
+ dev_err(sparx5->dev, "Failed to register notifier blocks\n");
+ goto cleanup_netdevs;
+ }
+
/* Start the rest of the initialization and enable interrupts. Must be
* called last, after all subsystems are initialized.
*/
err = sparx5_start(sparx5);
if (err) {
dev_err(sparx5->dev, "Start failed\n");
- goto cleanup_ptp;
+ goto cleanup_notifiers;
}
goto cleanup_config;
+cleanup_notifiers:
+ sparx5_unregister_notifier_blocks(sparx5);
+cleanup_netdevs:
+ sparx5_unregister_netdevs(sparx5);
cleanup_ptp:
sparx5_ptp_deinit(sparx5);
cleanup_ports:
- sparx5_cleanup_ports(sparx5);
+ sparx5_destroy_netdevs(sparx5);
if (sparx5->mact_queue)
destroy_workqueue(sparx5->mact_queue);
cleanup_config:
@@ -1044,12 +1045,12 @@ static void mchp_sparx5_remove(struct platform_device *pdev)
disable_irq(sparx5->ptp_irq);
sparx5->ptp_irq = -ENXIO;
}
+ sparx5_unregister_notifier_blocks(sparx5);
+ sparx5_unregister_netdevs(sparx5);
sparx5_ptp_deinit(sparx5);
ops->fdma_deinit(sparx5);
- sparx5_cleanup_ports(sparx5);
sparx5_vcap_destroy(sparx5);
- /* Unregister netdevs */
- sparx5_unregister_notifier_blocks(sparx5);
+ sparx5_destroy_netdevs(sparx5);
destroy_workqueue(sparx5->mact_queue);
}
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH net-next 3/7] net: sparx5: move VCAP initialization to probe
2026-02-25 9:05 [PATCH net-next 0/7] net: sparx5: clean up probe/remove init and deinit paths Daniel Machon
2026-02-25 9:05 ` [PATCH net-next 1/7] net: sparx5: call sparx5_start() last in probe() Daniel Machon
2026-02-25 9:05 ` [PATCH net-next 2/7] net: sparx5: move netdev and notifier block registration to probe Daniel Machon
@ 2026-02-25 9:05 ` Daniel Machon
2026-02-25 9:05 ` [PATCH net-next 4/7] net: sparx5: move MAC table initialization and add deinit function Daniel Machon
` (4 subsequent siblings)
7 siblings, 0 replies; 10+ messages in thread
From: Daniel Machon @ 2026-02-25 9:05 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Steen Hegelund, UNGLinuxDriver, Richard Cochran
Cc: netdev, linux-arm-kernel, linux-kernel
Move the VCAP initialization code from sparx5_start() to probe(). Add
proper error handling with a cleanup_vcap label and sparx5_vcap_deinit()
call.
Also, rename sparx5_vcap_destroy() to sparx5_vcap_deinit() to stay
consistent with the naming.
Signed-off-by: Daniel Machon <daniel.machon@microchip.com>
---
drivers/net/ethernet/microchip/sparx5/sparx5_main.c | 16 ++++++++++------
drivers/net/ethernet/microchip/sparx5/sparx5_main.h | 2 +-
drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c | 2 +-
3 files changed, 12 insertions(+), 8 deletions(-)
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
index 9887a260bafc..28ee45815c7f 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
@@ -775,10 +775,6 @@ static int sparx5_start(struct sparx5 *sparx5)
sparx5_board_init(sparx5);
- err = sparx5_vcap_init(sparx5);
- if (err)
- return err;
-
/* Start Frame DMA with fallback to register based INJ/XTR */
err = -ENXIO;
if (sparx5->fdma_irq >= 0) {
@@ -985,12 +981,18 @@ static int mchp_sparx5_probe(struct platform_device *pdev)
goto cleanup_ports;
}
+ err = sparx5_vcap_init(sparx5);
+ if (err) {
+ dev_err(sparx5->dev, "Failed to initialize VCAP\n");
+ goto cleanup_ptp;
+ }
+
INIT_LIST_HEAD(&sparx5->mall_entries);
err = sparx5_register_netdevs(sparx5);
if (err) {
dev_err(sparx5->dev, "Failed to register net devices\n");
- goto cleanup_ptp;
+ goto cleanup_vcap;
}
err = sparx5_register_notifier_blocks(sparx5);
@@ -1014,6 +1016,8 @@ static int mchp_sparx5_probe(struct platform_device *pdev)
sparx5_unregister_notifier_blocks(sparx5);
cleanup_netdevs:
sparx5_unregister_netdevs(sparx5);
+cleanup_vcap:
+ sparx5_vcap_deinit(sparx5);
cleanup_ptp:
sparx5_ptp_deinit(sparx5);
cleanup_ports:
@@ -1047,9 +1051,9 @@ static void mchp_sparx5_remove(struct platform_device *pdev)
}
sparx5_unregister_notifier_blocks(sparx5);
sparx5_unregister_netdevs(sparx5);
+ sparx5_vcap_deinit(sparx5);
sparx5_ptp_deinit(sparx5);
ops->fdma_deinit(sparx5);
- sparx5_vcap_destroy(sparx5);
sparx5_destroy_netdevs(sparx5);
destroy_workqueue(sparx5->mact_queue);
}
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
index fe7d8bcc0cd9..6a069434fca6 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
@@ -563,7 +563,7 @@ void sparx5_get_hwtimestamp(struct sparx5 *sparx5,
/* sparx5_vcap_impl.c */
int sparx5_vcap_init(struct sparx5 *sparx5);
-void sparx5_vcap_destroy(struct sparx5 *sparx5);
+void sparx5_vcap_deinit(struct sparx5 *sparx5);
/* sparx5_pgid.c */
enum sparx5_pgid_type {
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c
index 25066ddb8d4d..9b4ea3e22ef8 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vcap_impl.c
@@ -2083,7 +2083,7 @@ int sparx5_vcap_init(struct sparx5 *sparx5)
return err;
}
-void sparx5_vcap_destroy(struct sparx5 *sparx5)
+void sparx5_vcap_deinit(struct sparx5 *sparx5)
{
struct vcap_control *ctrl = sparx5->vcap_ctrl;
struct vcap_admin *admin, *admin_next;
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH net-next 4/7] net: sparx5: move MAC table initialization and add deinit function
2026-02-25 9:05 [PATCH net-next 0/7] net: sparx5: clean up probe/remove init and deinit paths Daniel Machon
` (2 preceding siblings ...)
2026-02-25 9:05 ` [PATCH net-next 3/7] net: sparx5: move VCAP initialization " Daniel Machon
@ 2026-02-25 9:05 ` Daniel Machon
2026-02-25 9:05 ` [PATCH net-next 5/7] net: sparx5: move stats " Daniel Machon
` (3 subsequent siblings)
7 siblings, 0 replies; 10+ messages in thread
From: Daniel Machon @ 2026-02-25 9:05 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Steen Hegelund, UNGLinuxDriver, Richard Cochran
Cc: netdev, linux-arm-kernel, linux-kernel
Consolidate all MAC table initialization from sparx5_start() into
sparx5_mact_init(), move it to probe(), and add a deinit function for
proper teardown.
Also, make sparx5_mact_pull_work() static since it is only used within
sparx5_mactable.c.
Signed-off-by: Daniel Machon <daniel.machon@microchip.com>
---
.../ethernet/microchip/sparx5/sparx5_mactable.c | 27 +++++++++++++++++--
.../net/ethernet/microchip/sparx5/sparx5_main.c | 31 +++++++---------------
.../net/ethernet/microchip/sparx5/sparx5_main.h | 4 +--
3 files changed, 37 insertions(+), 25 deletions(-)
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c b/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c
index f5584244612c..626ce6f9046d 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c
@@ -419,7 +419,7 @@ static void sparx5_mact_handle_entry(struct sparx5 *sparx5,
true);
}
-void sparx5_mact_pull_work(struct work_struct *work)
+static void sparx5_mact_pull_work(struct work_struct *work)
{
struct delayed_work *del_work = to_delayed_work(work);
struct sparx5 *sparx5 = container_of(del_work, struct sparx5,
@@ -489,8 +489,10 @@ void sparx5_set_ageing(struct sparx5 *sparx5, int msecs)
LRN_AUTOAGE_CFG(0));
}
-void sparx5_mact_init(struct sparx5 *sparx5)
+int sparx5_mact_init(struct sparx5 *sparx5)
{
+ char queue_name[32];
+
mutex_init(&sparx5->lock);
/* Flush MAC table */
@@ -502,4 +504,25 @@ void sparx5_mact_init(struct sparx5 *sparx5)
dev_warn(sparx5->dev, "MAC flush error\n");
sparx5_set_ageing(sparx5, BR_DEFAULT_AGEING_TIME / HZ * 1000);
+
+ mutex_init(&sparx5->mact_lock);
+ INIT_LIST_HEAD(&sparx5->mact_entries);
+ snprintf(queue_name, sizeof(queue_name), "%s-mact",
+ dev_name(sparx5->dev));
+ sparx5->mact_queue = create_singlethread_workqueue(queue_name);
+ if (!sparx5->mact_queue)
+ return -ENOMEM;
+
+ INIT_DELAYED_WORK(&sparx5->mact_work, sparx5_mact_pull_work);
+ queue_delayed_work(sparx5->mact_queue, &sparx5->mact_work,
+ SPX5_MACT_PULL_DELAY);
+
+ return 0;
+}
+
+void sparx5_mact_deinit(struct sparx5 *sparx5)
+{
+ cancel_delayed_work_sync(&sparx5->mact_work);
+ destroy_workqueue(sparx5->mact_queue);
+ mutex_destroy(&sparx5->mact_lock);
}
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
index 28ee45815c7f..97dd3cea0554 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
@@ -688,7 +688,6 @@ static int sparx5_start(struct sparx5 *sparx5)
u8 broadcast[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
const struct sparx5_consts *consts = sparx5->data->consts;
const struct sparx5_ops *ops = sparx5->data->ops;
- char queue_name[32];
u32 idx;
int err;
@@ -728,9 +727,6 @@ static int sparx5_start(struct sparx5 *sparx5)
ANA_CL_FILTER_CTRL_FORCE_FCS_UPDATE_ENA,
sparx5, ANA_CL_FILTER_CTRL(idx));
- /* Init MAC table, ageing */
- sparx5_mact_init(sparx5);
-
/* Init PGID table arbitrator */
sparx5_pgid_init(sparx5);
@@ -757,19 +753,6 @@ static int sparx5_start(struct sparx5 *sparx5)
if (err)
return err;
- /* Init mact_sw struct */
- mutex_init(&sparx5->mact_lock);
- INIT_LIST_HEAD(&sparx5->mact_entries);
- snprintf(queue_name, sizeof(queue_name), "%s-mact",
- dev_name(sparx5->dev));
- sparx5->mact_queue = create_singlethread_workqueue(queue_name);
- if (!sparx5->mact_queue)
- return -ENOMEM;
-
- INIT_DELAYED_WORK(&sparx5->mact_work, sparx5_mact_pull_work);
- queue_delayed_work(sparx5->mact_queue, &sparx5->mact_work,
- SPX5_MACT_PULL_DELAY);
-
mutex_init(&sparx5->mdb_lock);
INIT_LIST_HEAD(&sparx5->mdb_entries);
@@ -987,12 +970,18 @@ static int mchp_sparx5_probe(struct platform_device *pdev)
goto cleanup_ptp;
}
+ err = sparx5_mact_init(sparx5);
+ if (err) {
+ dev_err(sparx5->dev, "Failed to initialize MAC table\n");
+ goto cleanup_vcap;
+ }
+
INIT_LIST_HEAD(&sparx5->mall_entries);
err = sparx5_register_netdevs(sparx5);
if (err) {
dev_err(sparx5->dev, "Failed to register net devices\n");
- goto cleanup_vcap;
+ goto cleanup_mact;
}
err = sparx5_register_notifier_blocks(sparx5);
@@ -1016,14 +1005,14 @@ static int mchp_sparx5_probe(struct platform_device *pdev)
sparx5_unregister_notifier_blocks(sparx5);
cleanup_netdevs:
sparx5_unregister_netdevs(sparx5);
+cleanup_mact:
+ sparx5_mact_deinit(sparx5);
cleanup_vcap:
sparx5_vcap_deinit(sparx5);
cleanup_ptp:
sparx5_ptp_deinit(sparx5);
cleanup_ports:
sparx5_destroy_netdevs(sparx5);
- if (sparx5->mact_queue)
- destroy_workqueue(sparx5->mact_queue);
cleanup_config:
kfree(configs);
cleanup_pnode:
@@ -1051,11 +1040,11 @@ static void mchp_sparx5_remove(struct platform_device *pdev)
}
sparx5_unregister_notifier_blocks(sparx5);
sparx5_unregister_netdevs(sparx5);
+ sparx5_mact_deinit(sparx5);
sparx5_vcap_deinit(sparx5);
sparx5_ptp_deinit(sparx5);
ops->fdma_deinit(sparx5);
sparx5_destroy_netdevs(sparx5);
- destroy_workqueue(sparx5->mact_queue);
}
static const struct sparx5_regs sparx5_regs = {
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
index 6a069434fca6..e4c39cca7b26 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
@@ -470,7 +470,6 @@ void sparx5_fdma_reload(struct sparx5 *sparx5, struct fdma *fdma);
void sparx5_fdma_injection_mode(struct sparx5 *sparx5);
/* sparx5_mactable.c */
-void sparx5_mact_pull_work(struct work_struct *work);
int sparx5_mact_learn(struct sparx5 *sparx5, int port,
const unsigned char mac[ETH_ALEN], u16 vid);
bool sparx5_mact_getnext(struct sparx5 *sparx5,
@@ -489,7 +488,8 @@ int sparx5_del_mact_entry(struct sparx5 *sparx5,
int sparx5_mc_sync(struct net_device *dev, const unsigned char *addr);
int sparx5_mc_unsync(struct net_device *dev, const unsigned char *addr);
void sparx5_set_ageing(struct sparx5 *sparx5, int msecs);
-void sparx5_mact_init(struct sparx5 *sparx5);
+int sparx5_mact_init(struct sparx5 *sparx5);
+void sparx5_mact_deinit(struct sparx5 *sparx5);
/* sparx5_vlan.c */
void sparx5_pgid_update_mask(struct sparx5_port *port, int pgid, bool enable);
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH net-next 5/7] net: sparx5: move stats initialization and add deinit function
2026-02-25 9:05 [PATCH net-next 0/7] net: sparx5: clean up probe/remove init and deinit paths Daniel Machon
` (3 preceding siblings ...)
2026-02-25 9:05 ` [PATCH net-next 4/7] net: sparx5: move MAC table initialization and add deinit function Daniel Machon
@ 2026-02-25 9:05 ` Daniel Machon
2026-02-25 9:05 ` [PATCH net-next 6/7] net: sparx5: move calendar initialization to probe Daniel Machon
` (2 subsequent siblings)
7 siblings, 0 replies; 10+ messages in thread
From: Daniel Machon @ 2026-02-25 9:05 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Steen Hegelund, UNGLinuxDriver, Richard Cochran
Cc: netdev, linux-arm-kernel, linux-kernel
The sparx5_stats_init() function starts a worker thread which needs to
be cleaned up. Move the initialization code to probe() and add a
deinit() function for proper teardown.
Also, rename sparx_stats_init() to sparx5_stats_init() to match the
driver naming convention.
Signed-off-by: Daniel Machon <daniel.machon@microchip.com>
---
drivers/net/ethernet/microchip/sparx5/sparx5_ethtool.c | 9 ++++++++-
drivers/net/ethernet/microchip/sparx5/sparx5_main.c | 16 ++++++++++------
drivers/net/ethernet/microchip/sparx5/sparx5_main.h | 3 ++-
3 files changed, 20 insertions(+), 8 deletions(-)
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_ethtool.c b/drivers/net/ethernet/microchip/sparx5/sparx5_ethtool.c
index 049541eeaae0..d42c57bead89 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_ethtool.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_ethtool.c
@@ -1244,7 +1244,7 @@ const struct ethtool_ops sparx5_ethtool_ops = {
.set_pauseparam = sparx5_set_pauseparam,
};
-int sparx_stats_init(struct sparx5 *sparx5)
+int sparx5_stats_init(struct sparx5 *sparx5)
{
const struct sparx5_consts *consts = sparx5->data->consts;
char queue_name[32];
@@ -1278,3 +1278,10 @@ int sparx_stats_init(struct sparx5 *sparx5)
return 0;
}
+
+void sparx5_stats_deinit(struct sparx5 *sparx5)
+{
+ cancel_delayed_work_sync(&sparx5->stats_work);
+ destroy_workqueue(sparx5->stats_queue);
+ mutex_destroy(&sparx5->queue_stats_lock);
+}
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
index 97dd3cea0554..17748854ca48 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
@@ -748,11 +748,6 @@ static int sparx5_start(struct sparx5 *sparx5)
if (err)
return err;
- /* Init stats */
- err = sparx_stats_init(sparx5);
- if (err)
- return err;
-
mutex_init(&sparx5->mdb_lock);
INIT_LIST_HEAD(&sparx5->mdb_entries);
@@ -976,12 +971,18 @@ static int mchp_sparx5_probe(struct platform_device *pdev)
goto cleanup_vcap;
}
+ err = sparx5_stats_init(sparx5);
+ if (err) {
+ dev_err(sparx5->dev, "Failed to initialize stats\n");
+ goto cleanup_mact;
+ }
+
INIT_LIST_HEAD(&sparx5->mall_entries);
err = sparx5_register_netdevs(sparx5);
if (err) {
dev_err(sparx5->dev, "Failed to register net devices\n");
- goto cleanup_mact;
+ goto cleanup_stats;
}
err = sparx5_register_notifier_blocks(sparx5);
@@ -1005,6 +1006,8 @@ static int mchp_sparx5_probe(struct platform_device *pdev)
sparx5_unregister_notifier_blocks(sparx5);
cleanup_netdevs:
sparx5_unregister_netdevs(sparx5);
+cleanup_stats:
+ sparx5_stats_deinit(sparx5);
cleanup_mact:
sparx5_mact_deinit(sparx5);
cleanup_vcap:
@@ -1040,6 +1043,7 @@ static void mchp_sparx5_remove(struct platform_device *pdev)
}
sparx5_unregister_notifier_blocks(sparx5);
sparx5_unregister_netdevs(sparx5);
+ sparx5_stats_deinit(sparx5);
sparx5_mact_deinit(sparx5);
sparx5_vcap_deinit(sparx5);
sparx5_ptp_deinit(sparx5);
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
index e4c39cca7b26..97d53e229ad6 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
@@ -514,7 +514,8 @@ enum sparx5_cal_bw sparx5_get_port_cal_speed(struct sparx5 *sparx5, u32 portno);
/* sparx5_ethtool.c */
void sparx5_get_stats64(struct net_device *ndev, struct rtnl_link_stats64 *stats);
-int sparx_stats_init(struct sparx5 *sparx5);
+int sparx5_stats_init(struct sparx5 *sparx5);
+void sparx5_stats_deinit(struct sparx5 *sparx5);
/* sparx5_dcb.c */
#ifdef CONFIG_SPARX5_DCB
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH net-next 6/7] net: sparx5: move calendar initialization to probe
2026-02-25 9:05 [PATCH net-next 0/7] net: sparx5: clean up probe/remove init and deinit paths Daniel Machon
` (4 preceding siblings ...)
2026-02-25 9:05 ` [PATCH net-next 5/7] net: sparx5: move stats " Daniel Machon
@ 2026-02-25 9:05 ` Daniel Machon
2026-02-25 9:05 ` [PATCH net-next 7/7] net: sparx5: move remaining init functions from start() to probe() Daniel Machon
2026-02-25 9:15 ` [PATCH net-next 0/7] net: sparx5: clean up probe/remove init and deinit paths Russell King (Oracle)
7 siblings, 0 replies; 10+ messages in thread
From: Daniel Machon @ 2026-02-25 9:05 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Steen Hegelund, UNGLinuxDriver, Richard Cochran
Cc: netdev, linux-arm-kernel, linux-kernel
Move the calendar initialization from sparx5_start() to probe() by
creating a new sparx5_calendar_init() wrapper function that calls both
sparx5_config_auto_calendar() and sparx5_config_dsm_calendar().
Calendar initialization does not require cleanup.
Also, make the individual calendar config functions static since they
are now only called from within sparx5_calendar.c.
Signed-off-by: Daniel Machon <daniel.machon@microchip.com>
---
drivers/net/ethernet/microchip/sparx5/sparx5_calendar.c | 15 +++++++++++++--
drivers/net/ethernet/microchip/sparx5/sparx5_main.c | 14 ++++++--------
drivers/net/ethernet/microchip/sparx5/sparx5_main.h | 3 +--
3 files changed, 20 insertions(+), 12 deletions(-)
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_calendar.c b/drivers/net/ethernet/microchip/sparx5/sparx5_calendar.c
index 5c46d81de530..4ec95590a3c6 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_calendar.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_calendar.c
@@ -151,7 +151,7 @@ enum sparx5_cal_bw sparx5_get_port_cal_speed(struct sparx5 *sparx5, u32 portno)
}
/* Auto configure the QSYS calendar based on port configuration */
-int sparx5_config_auto_calendar(struct sparx5 *sparx5)
+static int sparx5_config_auto_calendar(struct sparx5 *sparx5)
{
const struct sparx5_consts *consts = sparx5->data->consts;
u32 cal[7], value, idx, portno;
@@ -578,7 +578,7 @@ static int sparx5_dsm_calendar_update(struct sparx5 *sparx5, u32 taxi,
}
/* Configure the DSM calendar based on port configuration */
-int sparx5_config_dsm_calendar(struct sparx5 *sparx5)
+static int sparx5_config_dsm_calendar(struct sparx5 *sparx5)
{
const struct sparx5_ops *ops = sparx5->data->ops;
int taxi;
@@ -610,3 +610,14 @@ int sparx5_config_dsm_calendar(struct sparx5 *sparx5)
kfree(data);
return err;
}
+
+int sparx5_calendar_init(struct sparx5 *sparx5)
+{
+ int err;
+
+ err = sparx5_config_auto_calendar(sparx5);
+ if (err)
+ return err;
+
+ return sparx5_config_dsm_calendar(sparx5);
+}
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
index 17748854ca48..581f87eb9781 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
@@ -740,14 +740,6 @@ static int sparx5_start(struct sparx5 *sparx5)
/* Enable queue limitation watermarks */
sparx5_qlim_set(sparx5);
- err = sparx5_config_auto_calendar(sparx5);
- if (err)
- return err;
-
- err = sparx5_config_dsm_calendar(sparx5);
- if (err)
- return err;
-
mutex_init(&sparx5->mdb_lock);
INIT_LIST_HEAD(&sparx5->mdb_entries);
@@ -947,6 +939,12 @@ static int mchp_sparx5_probe(struct platform_device *pdev)
}
}
+ err = sparx5_calendar_init(sparx5);
+ if (err) {
+ dev_err(sparx5->dev, "Failed to initialize calendar\n");
+ goto cleanup_ports;
+ }
+
err = sparx5_qos_init(sparx5);
if (err) {
dev_err(sparx5->dev, "Failed to initialize QoS\n");
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
index 97d53e229ad6..6a745bb71b5c 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
@@ -504,8 +504,7 @@ int sparx5_vlan_vid_del(struct sparx5_port *port, u16 vid);
void sparx5_vlan_port_apply(struct sparx5 *sparx5, struct sparx5_port *port);
/* sparx5_calendar.c */
-int sparx5_config_auto_calendar(struct sparx5 *sparx5);
-int sparx5_config_dsm_calendar(struct sparx5 *sparx5);
+int sparx5_calendar_init(struct sparx5 *sparx5);
int sparx5_dsm_calendar_calc(struct sparx5 *sparx5, u32 taxi,
struct sparx5_calendar_data *data);
u32 sparx5_cal_speed_to_value(enum sparx5_cal_bw speed);
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH net-next 7/7] net: sparx5: move remaining init functions from start() to probe()
2026-02-25 9:05 [PATCH net-next 0/7] net: sparx5: clean up probe/remove init and deinit paths Daniel Machon
` (5 preceding siblings ...)
2026-02-25 9:05 ` [PATCH net-next 6/7] net: sparx5: move calendar initialization to probe Daniel Machon
@ 2026-02-25 9:05 ` Daniel Machon
2026-02-25 9:15 ` [PATCH net-next 0/7] net: sparx5: clean up probe/remove init and deinit paths Russell King (Oracle)
7 siblings, 0 replies; 10+ messages in thread
From: Daniel Machon @ 2026-02-25 9:05 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Steen Hegelund, UNGLinuxDriver, Richard Cochran
Cc: netdev, linux-arm-kernel, linux-kernel
Move sparx5_pgid_init(), sparx5_vlan_init(), and sparx5_board_init()
from sparx5_start() to probe(). These functions do not require cleanup.
Signed-off-by: Daniel Machon <daniel.machon@microchip.com>
---
drivers/net/ethernet/microchip/sparx5/sparx5_main.c | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
index 581f87eb9781..fb3010446707 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
@@ -727,12 +727,6 @@ static int sparx5_start(struct sparx5 *sparx5)
ANA_CL_FILTER_CTRL_FORCE_FCS_UPDATE_ENA,
sparx5, ANA_CL_FILTER_CTRL(idx));
- /* Init PGID table arbitrator */
- sparx5_pgid_init(sparx5);
-
- /* Setup VLANs */
- sparx5_vlan_init(sparx5);
-
/* Add host mode BC address (points only to CPU) */
sparx5_mact_learn(sparx5, sparx5_get_pgid(sparx5, PGID_CPU), broadcast,
NULL_VID);
@@ -743,8 +737,6 @@ static int sparx5_start(struct sparx5 *sparx5)
mutex_init(&sparx5->mdb_lock);
INIT_LIST_HEAD(&sparx5->mdb_entries);
- sparx5_board_init(sparx5);
-
/* Start Frame DMA with fallback to register based INJ/XTR */
err = -ENXIO;
if (sparx5->fdma_irq >= 0) {
@@ -939,6 +931,10 @@ static int mchp_sparx5_probe(struct platform_device *pdev)
}
}
+ sparx5_pgid_init(sparx5);
+ sparx5_vlan_init(sparx5);
+ sparx5_board_init(sparx5);
+
err = sparx5_calendar_init(sparx5);
if (err) {
dev_err(sparx5->dev, "Failed to initialize calendar\n");
--
2.34.1
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH net-next 0/7] net: sparx5: clean up probe/remove init and deinit paths
2026-02-25 9:05 [PATCH net-next 0/7] net: sparx5: clean up probe/remove init and deinit paths Daniel Machon
` (6 preceding siblings ...)
2026-02-25 9:05 ` [PATCH net-next 7/7] net: sparx5: move remaining init functions from start() to probe() Daniel Machon
@ 2026-02-25 9:15 ` Russell King (Oracle)
2026-02-26 20:12 ` Daniel Machon
7 siblings, 1 reply; 10+ messages in thread
From: Russell King (Oracle) @ 2026-02-25 9:15 UTC (permalink / raw)
To: Daniel Machon
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Steen Hegelund, UNGLinuxDriver, Richard Cochran,
netdev, linux-arm-kernel, linux-kernel
On Wed, Feb 25, 2026 at 10:05:23AM +0100, Daniel Machon wrote:
> This series refactors the sparx5 init and deinit code out of
> sparx5_start() and into probe(), adding proper per-subsystem cleanup
> labels and deinit functions.
>
> Currently, the sparx5 driver initializes most subsystems inside
> sparx5_start(), which is called from probe(). This includes registering
> netdevs, starting worker threads for stats and MAC table polling,
> requesting PTP IRQs, and initializing VCAP. The function has grown to
> handle many unrelated subsystems, and has no granular error handling —
> it either succeeds entirely or returns an error, leaving cleanup to a
> single catch-all label in probe().
>
> The remove() path has a similar problem: teardown is not structured as
> the reverse of initialization, and several subsystems lack proper deinit
> functions. For example, the stats workqueue has no corresponding
> cleanup, and the mact workqueue is destroyed without first cancelling
> its delayed work.
>
> Refactor this by moving each init function out of sparx5_start() and
> into probe(), with a corresponding goto-based cleanup label. Add deinit
> functions for subsystems that allocate resources, to properly cancel
> work and destroy workqueues. Ensure that cleanup order in both error
> paths and remove() follows the reverse of initialization order. What
> remains in sparx5_start() is only hardware register setup and FDMA/XTR
> initialization that does not require cleanup.
>
> Before this series, most init functions live inside sparx5_start() with
> no individual cleanup:
>
> probe():
> sparx5_start(): <- no granular error handling
> sparx5_mact_init()
> sparx_stats_init() <- starts worker, no cleanup
> mact_queue setup <- no cancel on teardown
> sparx5_register_netdevs()
> sparx5_register_notifier_blocks()
> sparx5_vcap_init()
> sparx5_ptp_init()
>
> probe() error path:
> cleanup_ports:
> sparx5_cleanup_ports()
> destroy_workqueue(mact_queue)
>
> After this series, probe() initializes subsystems in order with
> matching cleanup labels, and remove() tears down in reverse:
>
> probe():
> sparx5_ptp_init()
> sparx5_vcap_init()
> sparx5_mact_init()
> sparx5_stats_init()
> sparx5_register_netdevs()
> sparx5_register_notifier_blocks()
> sparx5_start()
>
> remove():
> sparx5_unregister_notifier_blocks()
> sparx5_unregister_netdevs()
> sparx5_stats_deinit()
> sparx5_mact_deinit()
> sparx5_vcap_deinit()
> sparx5_ptp_deinit()
> sparx5_destroy_netdevs()
>
> Signed-off-by: Daniel Machon <daniel.machon@microchip.com>
Note that there is the general principle that drivers should not
"publish" themselves (aka register their netdevs and/or ptp) until
they have initialised enough so the facility that has been published
is functional.
That, in general, means that there should be very little initialisation
after the calls to register the netdevs and ptp.
It's fine if something gets published and then a later publication of
another interface fails, causing the first publication to be withdrawn,
that is pretty much unavoidable, but in such scenarios, one would want
to do as much of the initialisation that may fail before any
publication of any user interfaces.
--
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [PATCH net-next 0/7] net: sparx5: clean up probe/remove init and deinit paths
2026-02-25 9:15 ` [PATCH net-next 0/7] net: sparx5: clean up probe/remove init and deinit paths Russell King (Oracle)
@ 2026-02-26 20:12 ` Daniel Machon
0 siblings, 0 replies; 10+ messages in thread
From: Daniel Machon @ 2026-02-26 20:12 UTC (permalink / raw)
To: Russell King (Oracle)
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Steen Hegelund, UNGLinuxDriver, Richard Cochran,
netdev, linux-arm-kernel, linux-kernel
Hi Russell,
> > This series refactors the sparx5 init and deinit code out of
> > sparx5_start() and into probe(), adding proper per-subsystem cleanup
> > labels and deinit functions.
> >
> > Currently, the sparx5 driver initializes most subsystems inside
> > sparx5_start(), which is called from probe(). This includes registering
> > netdevs, starting worker threads for stats and MAC table polling,
> > requesting PTP IRQs, and initializing VCAP. The function has grown to
> > handle many unrelated subsystems, and has no granular error handling —
> > it either succeeds entirely or returns an error, leaving cleanup to a
> > single catch-all label in probe().
> >
> > The remove() path has a similar problem: teardown is not structured as
> > the reverse of initialization, and several subsystems lack proper deinit
> > functions. For example, the stats workqueue has no corresponding
> > cleanup, and the mact workqueue is destroyed without first cancelling
> > its delayed work.
> >
> > Refactor this by moving each init function out of sparx5_start() and
> > into probe(), with a corresponding goto-based cleanup label. Add deinit
> > functions for subsystems that allocate resources, to properly cancel
> > work and destroy workqueues. Ensure that cleanup order in both error
> > paths and remove() follows the reverse of initialization order. What
> > remains in sparx5_start() is only hardware register setup and FDMA/XTR
> > initialization that does not require cleanup.
> >
> > Before this series, most init functions live inside sparx5_start() with
> > no individual cleanup:
> >
> > probe():
> > sparx5_start(): <- no granular error handling
> > sparx5_mact_init()
> > sparx_stats_init() <- starts worker, no cleanup
> > mact_queue setup <- no cancel on teardown
> > sparx5_register_netdevs()
> > sparx5_register_notifier_blocks()
> > sparx5_vcap_init()
> > sparx5_ptp_init()
> >
> > probe() error path:
> > cleanup_ports:
> > sparx5_cleanup_ports()
> > destroy_workqueue(mact_queue)
> >
> > After this series, probe() initializes subsystems in order with
> > matching cleanup labels, and remove() tears down in reverse:
> >
> > probe():
> > sparx5_ptp_init()
> > sparx5_vcap_init()
> > sparx5_mact_init()
> > sparx5_stats_init()
> > sparx5_register_netdevs()
> > sparx5_register_notifier_blocks()
> > sparx5_start()
> >
> > remove():
> > sparx5_unregister_notifier_blocks()
> > sparx5_unregister_netdevs()
> > sparx5_stats_deinit()
> > sparx5_mact_deinit()
> > sparx5_vcap_deinit()
> > sparx5_ptp_deinit()
> > sparx5_destroy_netdevs()
> >
> > Signed-off-by: Daniel Machon <daniel.machon@microchip.com>
>
> Note that there is the general principle that drivers should not
> "publish" themselves (aka register their netdevs and/or ptp) until
> they have initialised enough so the facility that has been published
> is functional.
>
> That, in general, means that there should be very little initialisation
> after the calls to register the netdevs and ptp.
>
> It's fine if something gets published and then a later publication of
> another interface fails, causing the first publication to be withdrawn,
> that is pretty much unavoidable, but in such scenarios, one would want
> to do as much of the initialisation that may fail before any
> publication of any user interfaces.
>
> --
> RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
> FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
Thanks for pointing this out.
Before this series, quite a bit of initialization was already done after
publication. But as a consequence of moving netdev registration out of
sparx5_start() (patch #2), some initialization that previously happened before
publication now happens after — which is the wrong direction.
I will do some reordering to fix this in v2.
Thanks!
^ permalink raw reply [flat|nested] 10+ messages in thread