[PATCH v1 1/2] mfd: nct6694: Switch to devm_mfd_add_devices() and drop IDA

From: a0282524688

Date: Thu Apr 02 2026 - 01:18:23 EST


From: Ming Yu <a0282524688@xxxxxxxxx>

Currently, the nct6694 core driver uses mfd_add_hotplug_devices()
and an IDA to manage subdevice IDs.

Switch the core implementation to use the managed
devm_mfd_add_devices() API, which simplifies the error handling and
device lifecycle management. Concurrently, drop the custom IDA
implementation and transition to using pdev->id.

Signed-off-by: Ming Yu <a0282524688@xxxxxxxxx>
---
drivers/gpio/gpio-nct6694.c | 19 +------
drivers/i2c/busses/i2c-nct6694.c | 19 +------
drivers/mfd/nct6694.c | 83 ++++++++++++-----------------
drivers/net/can/usb/nct6694_canfd.c | 12 +----
drivers/watchdog/nct6694_wdt.c | 20 +------
include/linux/mfd/nct6694.h | 8 +--
6 files changed, 43 insertions(+), 118 deletions(-)

diff --git a/drivers/gpio/gpio-nct6694.c b/drivers/gpio/gpio-nct6694.c
index a8607f0d9915..3703a61209e6 100644
--- a/drivers/gpio/gpio-nct6694.c
+++ b/drivers/gpio/gpio-nct6694.c
@@ -7,7 +7,6 @@

#include <linux/bits.h>
#include <linux/gpio/driver.h>
-#include <linux/idr.h>
#include <linux/interrupt.h>
#include <linux/mfd/nct6694.h>
#include <linux/module.h>
@@ -381,14 +380,6 @@ static void nct6694_irq_dispose_mapping(void *d)
irq_dispose_mapping(data->irq);
}

-static void nct6694_gpio_ida_free(void *d)
-{
- struct nct6694_gpio_data *data = d;
- struct nct6694 *nct6694 = data->nct6694;
-
- ida_free(&nct6694->gpio_ida, data->group);
-}
-
static int nct6694_gpio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -403,15 +394,7 @@ static int nct6694_gpio_probe(struct platform_device *pdev)
return -ENOMEM;

data->nct6694 = nct6694;
-
- ret = ida_alloc(&nct6694->gpio_ida, GFP_KERNEL);
- if (ret < 0)
- return ret;
- data->group = ret;
-
- ret = devm_add_action_or_reset(dev, nct6694_gpio_ida_free, data);
- if (ret)
- return ret;
+ data->group = pdev->id;

names = devm_kcalloc(dev, NCT6694_NR_GPIO, sizeof(char *),
GFP_KERNEL);
diff --git a/drivers/i2c/busses/i2c-nct6694.c b/drivers/i2c/busses/i2c-nct6694.c
index 1413ab6f9462..7d8ad997f6d2 100644
--- a/drivers/i2c/busses/i2c-nct6694.c
+++ b/drivers/i2c/busses/i2c-nct6694.c
@@ -6,7 +6,6 @@
*/

#include <linux/i2c.h>
-#include <linux/idr.h>
#include <linux/kernel.h>
#include <linux/mfd/nct6694.h>
#include <linux/module.h>
@@ -134,14 +133,6 @@ static int nct6694_i2c_set_baudrate(struct nct6694_i2c_data *data)
return 0;
}

-static void nct6694_i2c_ida_free(void *d)
-{
- struct nct6694_i2c_data *data = d;
- struct nct6694 *nct6694 = data->nct6694;
-
- ida_free(&nct6694->i2c_ida, data->port);
-}
-
static int nct6694_i2c_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -155,15 +146,7 @@ static int nct6694_i2c_probe(struct platform_device *pdev)

data->dev = dev;
data->nct6694 = nct6694;
-
- ret = ida_alloc(&nct6694->i2c_ida, GFP_KERNEL);
- if (ret < 0)
- return ret;
- data->port = ret;
-
- ret = devm_add_action_or_reset(dev, nct6694_i2c_ida_free, data);
- if (ret)
- return ret;
+ data->port = pdev->id;

ret = nct6694_i2c_set_baudrate(data);
if (ret)
diff --git a/drivers/mfd/nct6694.c b/drivers/mfd/nct6694.c
index 308b2fda3055..8ce2c4985aab 100644
--- a/drivers/mfd/nct6694.c
+++ b/drivers/mfd/nct6694.c
@@ -11,7 +11,6 @@

#include <linux/bits.h>
#include <linux/interrupt.h>
-#include <linux/idr.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/kernel.h>
@@ -23,35 +22,35 @@
#include <linux/usb.h>

static const struct mfd_cell nct6694_devs[] = {
- MFD_CELL_NAME("nct6694-gpio"),
- MFD_CELL_NAME("nct6694-gpio"),
- MFD_CELL_NAME("nct6694-gpio"),
- MFD_CELL_NAME("nct6694-gpio"),
- MFD_CELL_NAME("nct6694-gpio"),
- MFD_CELL_NAME("nct6694-gpio"),
- MFD_CELL_NAME("nct6694-gpio"),
- MFD_CELL_NAME("nct6694-gpio"),
- MFD_CELL_NAME("nct6694-gpio"),
- MFD_CELL_NAME("nct6694-gpio"),
- MFD_CELL_NAME("nct6694-gpio"),
- MFD_CELL_NAME("nct6694-gpio"),
- MFD_CELL_NAME("nct6694-gpio"),
- MFD_CELL_NAME("nct6694-gpio"),
- MFD_CELL_NAME("nct6694-gpio"),
- MFD_CELL_NAME("nct6694-gpio"),
-
- MFD_CELL_NAME("nct6694-i2c"),
- MFD_CELL_NAME("nct6694-i2c"),
- MFD_CELL_NAME("nct6694-i2c"),
- MFD_CELL_NAME("nct6694-i2c"),
- MFD_CELL_NAME("nct6694-i2c"),
- MFD_CELL_NAME("nct6694-i2c"),
-
- MFD_CELL_NAME("nct6694-canfd"),
- MFD_CELL_NAME("nct6694-canfd"),
-
- MFD_CELL_NAME("nct6694-wdt"),
- MFD_CELL_NAME("nct6694-wdt"),
+ MFD_CELL_BASIC("nct6694-gpio", NULL, NULL, 0, 0),
+ MFD_CELL_BASIC("nct6694-gpio", NULL, NULL, 0, 1),
+ MFD_CELL_BASIC("nct6694-gpio", NULL, NULL, 0, 2),
+ MFD_CELL_BASIC("nct6694-gpio", NULL, NULL, 0, 3),
+ MFD_CELL_BASIC("nct6694-gpio", NULL, NULL, 0, 4),
+ MFD_CELL_BASIC("nct6694-gpio", NULL, NULL, 0, 5),
+ MFD_CELL_BASIC("nct6694-gpio", NULL, NULL, 0, 6),
+ MFD_CELL_BASIC("nct6694-gpio", NULL, NULL, 0, 7),
+ MFD_CELL_BASIC("nct6694-gpio", NULL, NULL, 0, 8),
+ MFD_CELL_BASIC("nct6694-gpio", NULL, NULL, 0, 9),
+ MFD_CELL_BASIC("nct6694-gpio", NULL, NULL, 0, 10),
+ MFD_CELL_BASIC("nct6694-gpio", NULL, NULL, 0, 11),
+ MFD_CELL_BASIC("nct6694-gpio", NULL, NULL, 0, 12),
+ MFD_CELL_BASIC("nct6694-gpio", NULL, NULL, 0, 13),
+ MFD_CELL_BASIC("nct6694-gpio", NULL, NULL, 0, 14),
+ MFD_CELL_BASIC("nct6694-gpio", NULL, NULL, 0, 15),
+
+ MFD_CELL_BASIC("nct6694-i2c", NULL, NULL, 0, 0),
+ MFD_CELL_BASIC("nct6694-i2c", NULL, NULL, 0, 1),
+ MFD_CELL_BASIC("nct6694-i2c", NULL, NULL, 0, 2),
+ MFD_CELL_BASIC("nct6694-i2c", NULL, NULL, 0, 3),
+ MFD_CELL_BASIC("nct6694-i2c", NULL, NULL, 0, 4),
+ MFD_CELL_BASIC("nct6694-i2c", NULL, NULL, 0, 5),
+
+ MFD_CELL_BASIC("nct6694-canfd", NULL, NULL, 0, 0),
+ MFD_CELL_BASIC("nct6694-canfd", NULL, NULL, 0, 1),
+
+ MFD_CELL_BASIC("nct6694-wdt", NULL, NULL, 0, 0),
+ MFD_CELL_BASIC("nct6694-wdt", NULL, NULL, 0, 1),

MFD_CELL_NAME("nct6694-hwmon"),

@@ -307,23 +306,18 @@ static int nct6694_usb_probe(struct usb_interface *iface,
nct6694->dev = dev;
nct6694->udev = udev;

- ida_init(&nct6694->gpio_ida);
- ida_init(&nct6694->i2c_ida);
- ida_init(&nct6694->canfd_ida);
- ida_init(&nct6694->wdt_ida);
-
spin_lock_init(&nct6694->irq_lock);

ret = devm_mutex_init(dev, &nct6694->access_lock);
if (ret)
- goto err_ida;
+ goto err_irq_domain;

interface = iface->cur_altsetting;

int_endpoint = &interface->endpoint[0].desc;
if (!usb_endpoint_is_int_in(int_endpoint)) {
ret = -ENODEV;
- goto err_ida;
+ goto err_irq_domain;
}

usb_fill_int_urb(nct6694->int_in_urb, udev, usb_rcvintpipe(udev, NCT6694_INT_IN_EP),
@@ -332,11 +326,11 @@ static int nct6694_usb_probe(struct usb_interface *iface,

ret = usb_submit_urb(nct6694->int_in_urb, GFP_KERNEL);
if (ret)
- goto err_ida;
+ goto err_irq_domain;

usb_set_intfdata(iface, nct6694);

- ret = mfd_add_hotplug_devices(dev, nct6694_devs, ARRAY_SIZE(nct6694_devs));
+ ret = devm_mfd_add_devices(dev, 0, nct6694_devs, ARRAY_SIZE(nct6694_devs), NULL, 0, NULL);
if (ret)
goto err_mfd;

@@ -344,11 +338,7 @@ static int nct6694_usb_probe(struct usb_interface *iface,

err_mfd:
usb_kill_urb(nct6694->int_in_urb);
-err_ida:
- ida_destroy(&nct6694->wdt_ida);
- ida_destroy(&nct6694->canfd_ida);
- ida_destroy(&nct6694->i2c_ida);
- ida_destroy(&nct6694->gpio_ida);
+err_irq_domain:
irq_domain_remove(nct6694->domain);
err_urb:
usb_free_urb(nct6694->int_in_urb);
@@ -359,12 +349,7 @@ static void nct6694_usb_disconnect(struct usb_interface *iface)
{
struct nct6694 *nct6694 = usb_get_intfdata(iface);

- mfd_remove_devices(nct6694->dev);
usb_kill_urb(nct6694->int_in_urb);
- ida_destroy(&nct6694->wdt_ida);
- ida_destroy(&nct6694->canfd_ida);
- ida_destroy(&nct6694->i2c_ida);
- ida_destroy(&nct6694->gpio_ida);
irq_domain_remove(nct6694->domain);
usb_free_urb(nct6694->int_in_urb);
}
diff --git a/drivers/net/can/usb/nct6694_canfd.c b/drivers/net/can/usb/nct6694_canfd.c
index e5f7f8849a73..29282c56430f 100644
--- a/drivers/net/can/usb/nct6694_canfd.c
+++ b/drivers/net/can/usb/nct6694_canfd.c
@@ -8,7 +8,6 @@
#include <linux/can/dev.h>
#include <linux/can/rx-offload.h>
#include <linux/ethtool.h>
-#include <linux/idr.h>
#include <linux/irqdomain.h>
#include <linux/kernel.h>
#include <linux/mfd/nct6694.h>
@@ -725,15 +724,13 @@ static int nct6694_canfd_probe(struct platform_device *pdev)
struct net_device *ndev;
int port, irq, ret, can_clk;

- port = ida_alloc(&nct6694->canfd_ida, GFP_KERNEL);
- if (port < 0)
- return port;
+ port = pdev->id;

irq = irq_create_mapping(nct6694->domain,
NCT6694_IRQ_CAN0 + port);
if (!irq) {
ret = -EINVAL;
- goto free_ida;
+ return ret;
}

ndev = alloc_candev(sizeof(struct nct6694_canfd_priv), 1);
@@ -796,24 +793,19 @@ static int nct6694_canfd_probe(struct platform_device *pdev)
free_candev(ndev);
dispose_irq:
irq_dispose_mapping(irq);
-free_ida:
- ida_free(&nct6694->canfd_ida, port);
return ret;
}

static void nct6694_canfd_remove(struct platform_device *pdev)
{
struct nct6694_canfd_priv *priv = platform_get_drvdata(pdev);
- struct nct6694 *nct6694 = priv->nct6694;
struct net_device *ndev = priv->ndev;
- int port = ndev->dev_port;
int irq = ndev->irq;

unregister_candev(ndev);
can_rx_offload_del(&priv->offload);
free_candev(ndev);
irq_dispose_mapping(irq);
- ida_free(&nct6694->canfd_ida, port);
}

static struct platform_driver nct6694_canfd_driver = {
diff --git a/drivers/watchdog/nct6694_wdt.c b/drivers/watchdog/nct6694_wdt.c
index bc3689bd4b6b..2b4b804a1739 100644
--- a/drivers/watchdog/nct6694_wdt.c
+++ b/drivers/watchdog/nct6694_wdt.c
@@ -5,7 +5,6 @@
* Copyright (C) 2025 Nuvoton Technology Corp.
*/

-#include <linux/idr.h>
#include <linux/kernel.h>
#include <linux/mfd/nct6694.h>
#include <linux/module.h>
@@ -233,21 +232,12 @@ static const struct watchdog_ops nct6694_wdt_ops = {
.ping = nct6694_wdt_ping,
};

-static void nct6694_wdt_ida_free(void *d)
-{
- struct nct6694_wdt_data *data = d;
- struct nct6694 *nct6694 = data->nct6694;
-
- ida_free(&nct6694->wdt_ida, data->wdev_idx);
-}
-
static int nct6694_wdt_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct nct6694 *nct6694 = dev_get_drvdata(dev->parent);
struct nct6694_wdt_data *data;
struct watchdog_device *wdev;
- int ret;

data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
@@ -260,15 +250,7 @@ static int nct6694_wdt_probe(struct platform_device *pdev)

data->dev = dev;
data->nct6694 = nct6694;
-
- ret = ida_alloc(&nct6694->wdt_ida, GFP_KERNEL);
- if (ret < 0)
- return ret;
- data->wdev_idx = ret;
-
- ret = devm_add_action_or_reset(dev, nct6694_wdt_ida_free, data);
- if (ret)
- return ret;
+ data->wdev_idx = pdev->id;

wdev = &data->wdev;
wdev->info = &nct6694_wdt_info;
diff --git a/include/linux/mfd/nct6694.h b/include/linux/mfd/nct6694.h
index 6eb9be2cd4a0..496da72949d9 100644
--- a/include/linux/mfd/nct6694.h
+++ b/include/linux/mfd/nct6694.h
@@ -8,6 +8,10 @@
#ifndef __MFD_NCT6694_H
#define __MFD_NCT6694_H

+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
#define NCT6694_VENDOR_ID 0x0416
#define NCT6694_PRODUCT_ID 0x200B
#define NCT6694_INT_IN_EP 0x81
@@ -82,10 +86,6 @@ union __packed nct6694_usb_msg {

struct nct6694 {
struct device *dev;
- struct ida gpio_ida;
- struct ida i2c_ida;
- struct ida canfd_ida;
- struct ida wdt_ida;
struct irq_domain *domain;
struct mutex access_lock;
spinlock_t irq_lock;
--
2.34.1