[PATCH v5 6/7] mfd: nct6694: Extract core device management into a separate module
From: a0282524688
Date: Mon May 25 2026 - 04:19:56 EST
From: Ming Yu <a0282524688@xxxxxxxxx>
Extract the transport-agnostic core logic, including IRQ domain setup,
IDA initialization, and MFD sub-device registration, from the USB driver
into a new nct6694-core.c module.
The core routines are exported as nct6694_core_probe() and
nct6694_core_remove() to be consumed by the transport drivers. The USB
driver is updated to pass its specific MFD cells to the core probe
routine.
This completes the transport abstraction, ensuring that the shared
device management logic is cleanly separated from the underlying I/O
implementation, and is fully ready for new transport backends
(e.g., HIF).
Signed-off-by: Ming Yu <a0282524688@xxxxxxxxx>
---
Changes in v5:
- Split from the monolithic v4 patch to follow the single logical change principle.
MAINTAINERS | 2 +-
drivers/mfd/Makefile | 1 +
drivers/mfd/nct6694-core.c | 136 ++++++++++++++++++++++++++++++++++++
drivers/mfd/nct6694-usb.c | 95 +++----------------------
include/linux/mfd/nct6694.h | 9 ++-
5 files changed, 155 insertions(+), 88 deletions(-)
create mode 100644 drivers/mfd/nct6694-core.c
diff --git a/MAINTAINERS b/MAINTAINERS
index d52a3028e7c0..53a81f8afc37 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -19098,7 +19098,7 @@ S: Supported
F: drivers/gpio/gpio-nct6694.c
F: drivers/hwmon/nct6694-hwmon.c
F: drivers/i2c/busses/i2c-nct6694.c
-F: drivers/mfd/nct6694-usb.c
+F: drivers/mfd/nct6694-*.c
F: drivers/net/can/usb/nct6694_canfd.c
F: drivers/rtc/rtc-nct6694.c
F: drivers/watchdog/nct6694_wdt.c
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 361c5360e656..668a69dc66a7 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -124,6 +124,7 @@ obj-$(CONFIG_MFD_MC13XXX_I2C) += mc13xxx-i2c.o
obj-$(CONFIG_MFD_PF1550) += pf1550.o
+obj-$(CONFIG_MFD_NCT6694) += nct6694-core.o
obj-$(CONFIG_MFD_NCT6694_USB) += nct6694-usb.o
obj-$(CONFIG_MFD_CORE) += mfd-core.o
diff --git a/drivers/mfd/nct6694-core.c b/drivers/mfd/nct6694-core.c
new file mode 100644
index 000000000000..a2afd5f33ea5
--- /dev/null
+++ b/drivers/mfd/nct6694-core.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Nuvoton Technology Corp.
+ *
+ * Nuvoton NCT6694 MFD core driver.
+ *
+ * This provides common registration for IRQ domain, IDA pools,
+ * and MFD sub-devices shared by all transport drivers (USB, HIF).
+ */
+
+#include <linux/idr.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/nct6694.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+
+static void nct6694_irq_enable(struct irq_data *data)
+{
+ struct nct6694 *nct6694 = irq_data_get_irq_chip_data(data);
+ irq_hw_number_t hwirq = irqd_to_hwirq(data);
+
+ guard(spinlock_irqsave)(&nct6694->irq_lock);
+
+ nct6694->irq_enable |= BIT(hwirq);
+}
+
+static void nct6694_irq_disable(struct irq_data *data)
+{
+ struct nct6694 *nct6694 = irq_data_get_irq_chip_data(data);
+ irq_hw_number_t hwirq = irqd_to_hwirq(data);
+
+ guard(spinlock_irqsave)(&nct6694->irq_lock);
+
+ nct6694->irq_enable &= ~BIT(hwirq);
+}
+
+static const struct irq_chip nct6694_irq_chip = {
+ .name = "nct6694-irq",
+ .flags = IRQCHIP_SKIP_SET_WAKE,
+ .irq_enable = nct6694_irq_enable,
+ .irq_disable = nct6694_irq_disable,
+};
+
+static int nct6694_irq_domain_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hw)
+{
+ struct nct6694 *nct6694 = d->host_data;
+
+ irq_set_chip_data(irq, nct6694);
+ irq_set_chip_and_handler(irq, &nct6694_irq_chip, handle_simple_irq);
+
+ return 0;
+}
+
+static void nct6694_irq_domain_unmap(struct irq_domain *d, unsigned int irq)
+{
+ irq_set_chip_and_handler(irq, NULL, NULL);
+ irq_set_chip_data(irq, NULL);
+}
+
+static const struct irq_domain_ops nct6694_irq_domain_ops = {
+ .map = nct6694_irq_domain_map,
+ .unmap = nct6694_irq_domain_unmap,
+};
+
+/**
+ * nct6694_core_probe() - Register IRQ domain, IDAs, and MFD sub-devices
+ * @dev: parent device (USB interface or platform device)
+ * @nct6694: initialized nct6694 structure with transport callbacks set
+ *
+ * This function completes the common probe steps shared by all transport
+ * drivers: IRQ domain creation, IDA initialization, and MFD cell registration.
+ *
+ * The caller must have already set nct6694->dev, nct6694->priv,
+ * nct6694->read_msg, and nct6694->write_msg before calling this.
+ *
+ * Return: 0 on success or negative errno on failure.
+ */
+int nct6694_core_probe(struct device *dev, struct nct6694 *nct6694,
+ const struct mfd_cell *cells, int n_cells)
+{
+ int ret;
+
+ spin_lock_init(&nct6694->irq_lock);
+
+ ida_init(&nct6694->gpio_ida);
+ ida_init(&nct6694->i2c_ida);
+ ida_init(&nct6694->canfd_ida);
+ ida_init(&nct6694->wdt_ida);
+
+ nct6694->domain = irq_domain_create_simple(NULL, NCT6694_NR_IRQS, 0,
+ &nct6694_irq_domain_ops,
+ nct6694);
+ if (!nct6694->domain) {
+ ret = -ENODEV;
+ goto err_ida;
+ }
+
+ ret = mfd_add_hotplug_devices(dev, cells, n_cells);
+ if (ret)
+ goto err_domain;
+
+ return 0;
+
+err_domain:
+ irq_domain_remove(nct6694->domain);
+err_ida:
+ ida_destroy(&nct6694->wdt_ida);
+ ida_destroy(&nct6694->canfd_ida);
+ ida_destroy(&nct6694->i2c_ida);
+ ida_destroy(&nct6694->gpio_ida);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(nct6694_core_probe);
+
+/**
+ * nct6694_core_remove() - Unregister MFD sub-devices and free core resources
+ * @nct6694: nct6694 structure previously passed to nct6694_core_probe()
+ */
+void nct6694_core_remove(struct nct6694 *nct6694)
+{
+ mfd_remove_devices(nct6694->dev);
+ irq_domain_remove(nct6694->domain);
+ ida_destroy(&nct6694->wdt_ida);
+ ida_destroy(&nct6694->canfd_ida);
+ ida_destroy(&nct6694->i2c_ida);
+ ida_destroy(&nct6694->gpio_ida);
+}
+EXPORT_SYMBOL_GPL(nct6694_core_remove);
+
+MODULE_DESCRIPTION("Nuvoton NCT6694 MFD core driver");
+MODULE_AUTHOR("Ming Yu <tmyu0@xxxxxxxxxxx>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/nct6694-usb.c b/drivers/mfd/nct6694-usb.c
index 289580bbc472..666ad1c442b8 100644
--- a/drivers/mfd/nct6694-usb.c
+++ b/drivers/mfd/nct6694-usb.c
@@ -10,7 +10,6 @@
*/
#include <linux/bits.h>
-#include <linux/idr.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
@@ -19,7 +18,6 @@
#include <linux/mfd/nct6694.h>
#include <linux/module.h>
#include <linux/slab.h>
-#include <linux/spinlock.h>
#include <linux/usb.h>
#define NCT6694_VENDOR_ID 0x0416
@@ -43,7 +41,7 @@ struct nct6694_usb_data {
__le32 *int_buffer;
};
-static const struct mfd_cell nct6694_devs[] = {
+static const struct mfd_cell nct6694_usb_devs[] = {
MFD_CELL_NAME("nct6694-gpio"),
MFD_CELL_NAME("nct6694-gpio"),
MFD_CELL_NAME("nct6694-gpio"),
@@ -244,57 +242,9 @@ static void usb_int_callback(struct urb *urb)
resubmit:
ret = usb_submit_urb(urb, GFP_ATOMIC);
if (ret)
- dev_warn(nct6694->dev, "Failed to resubmit urb, status %pe", ERR_PTR(ret));
+ dev_warn(nct6694->dev, "Failed to resubmit urb, status %pe", ERR_PTR(ret));
}
-static void nct6694_irq_enable(struct irq_data *data)
-{
- struct nct6694 *nct6694 = irq_data_get_irq_chip_data(data);
- irq_hw_number_t hwirq = irqd_to_hwirq(data);
-
- guard(spinlock_irqsave)(&nct6694->irq_lock);
-
- nct6694->irq_enable |= BIT(hwirq);
-}
-
-static void nct6694_irq_disable(struct irq_data *data)
-{
- struct nct6694 *nct6694 = irq_data_get_irq_chip_data(data);
- irq_hw_number_t hwirq = irqd_to_hwirq(data);
-
- guard(spinlock_irqsave)(&nct6694->irq_lock);
-
- nct6694->irq_enable &= ~BIT(hwirq);
-}
-
-static const struct irq_chip nct6694_irq_chip = {
- .name = "nct6694-irq",
- .flags = IRQCHIP_SKIP_SET_WAKE,
- .irq_enable = nct6694_irq_enable,
- .irq_disable = nct6694_irq_disable,
-};
-
-static int nct6694_irq_domain_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
-{
- struct nct6694 *nct6694 = d->host_data;
-
- irq_set_chip_data(irq, nct6694);
- irq_set_chip_and_handler(irq, &nct6694_irq_chip, handle_simple_irq);
-
- return 0;
-}
-
-static void nct6694_irq_domain_unmap(struct irq_domain *d, unsigned int irq)
-{
- irq_set_chip_and_handler(irq, NULL, NULL);
- irq_set_chip_data(irq, NULL);
-}
-
-static const struct irq_domain_ops nct6694_irq_domain_ops = {
- .map = nct6694_irq_domain_map,
- .unmap = nct6694_irq_domain_unmap,
-};
-
static int nct6694_usb_probe(struct usb_interface *iface,
const struct usb_device_id *id)
{
@@ -328,37 +278,21 @@ static int nct6694_usb_probe(struct usb_interface *iface,
udata->udev = udev;
+ nct6694->dev = dev;
nct6694->priv = udata;
nct6694->read_msg = nct6694_usb_read_msg;
nct6694->write_msg = nct6694_usb_write_msg;
- nct6694->domain = irq_domain_create_simple(NULL, NCT6694_NR_IRQS, 0,
- &nct6694_irq_domain_ops,
- nct6694);
- if (!nct6694->domain) {
- ret = -ENODEV;
- goto err_urb;
- }
-
- nct6694->dev = dev;
-
- 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, &udata->access_lock);
if (ret)
- goto err_ida;
+ goto err_urb;
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_urb;
}
usb_fill_int_urb(udata->int_in_urb, udev, usb_rcvintpipe(udev, NCT6694_INT_IN_EP),
@@ -367,11 +301,11 @@ static int nct6694_usb_probe(struct usb_interface *iface,
ret = usb_submit_urb(udata->int_in_urb, GFP_KERNEL);
if (ret)
- goto err_ida;
+ goto err_urb;
usb_set_intfdata(iface, nct6694);
- ret = mfd_add_hotplug_devices(dev, nct6694_devs, ARRAY_SIZE(nct6694_devs));
+ ret = nct6694_core_probe(dev, nct6694, nct6694_usb_devs, ARRAY_SIZE(nct6694_usb_devs));
if (ret)
goto err_mfd;
@@ -379,12 +313,6 @@ static int nct6694_usb_probe(struct usb_interface *iface,
err_mfd:
usb_kill_urb(udata->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);
- irq_domain_remove(nct6694->domain);
err_urb:
usb_free_urb(udata->int_in_urb);
return ret;
@@ -395,13 +323,8 @@ static void nct6694_usb_disconnect(struct usb_interface *iface)
struct nct6694 *nct6694 = usb_get_intfdata(iface);
struct nct6694_usb_data *udata = nct6694->priv;
- mfd_remove_devices(nct6694->dev);
+ nct6694_core_remove(nct6694);
usb_kill_urb(udata->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(udata->int_in_urb);
}
@@ -419,6 +342,6 @@ static struct usb_driver nct6694_usb_driver = {
};
module_usb_driver(nct6694_usb_driver);
-MODULE_DESCRIPTION("Nuvoton NCT6694 core driver");
+MODULE_DESCRIPTION("Nuvoton NCT6694 USB transport driver");
MODULE_AUTHOR("Ming Yu <tmyu0@xxxxxxxxxxx>");
MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/nct6694.h b/include/linux/mfd/nct6694.h
index 3079c74110aa..a150c7d6ceba 100644
--- a/include/linux/mfd/nct6694.h
+++ b/include/linux/mfd/nct6694.h
@@ -10,10 +10,13 @@
#define __MFD_NCT6694_H
#include <linux/idr.h>
-#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/types.h>
+struct device;
+struct irq_domain;
+struct mfd_cell;
+
#define NCT6694_HWMON_MOD 0x00
#define NCT6694_PWM_MOD 0x01
#define NCT6694_I2C_MOD 0x03
@@ -115,4 +118,8 @@ static inline int nct6694_write_msg(struct nct6694 *nct6694,
return nct6694->write_msg(nct6694, cmd_hd, buf);
}
+int nct6694_core_probe(struct device *dev, struct nct6694 *nct6694,
+ const struct mfd_cell *cells, int n_cells);
+void nct6694_core_remove(struct nct6694 *nct6694);
+
#endif
--
2.34.1