From: a0282524688@gmail.com
To: tmyu0@nuvoton.com, lee@kernel.org, linus.walleij@linaro.org,
brgl@bgdev.pl, andi.shyti@kernel.org, mkl@pengutronix.de,
mailhol.vincent@wanadoo.fr, andrew+netdev@lunn.ch,
davem@davemloft.net, edumazet@google.com, kuba@kernel.org,
pabeni@redhat.com, wim@linux-watchdog.org, linux@roeck-us.net,
jdelvare@suse.com, alexandre.belloni@bootlin.com
Cc: linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org,
linux-i2c@vger.kernel.org, linux-can@vger.kernel.org,
netdev@vger.kernel.org, linux-watchdog@vger.kernel.org,
linux-hwmon@vger.kernel.org, linux-rtc@vger.kernel.org,
linux-usb@vger.kernel.org, Ming Yu <a0282524688@gmail.com>
Subject: [PATCH v13 7/7] rtc: Add Nuvoton NCT6694 RTC support
Date: Fri, 27 Jun 2025 18:27:30 +0800 [thread overview]
Message-ID: <20250627102730.71222-8-a0282524688@gmail.com> (raw)
In-Reply-To: <20250627102730.71222-1-a0282524688@gmail.com>
From: Ming Yu <a0282524688@gmail.com>
This driver supports RTC functionality for NCT6694 MFD device
based on USB interface.
Acked-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Signed-off-by: Ming Yu <a0282524688@gmail.com>
---
Changes since version 12:
- Use same email address in the signature
Changes since version 11:
Changes since version 10:
Changes since version 9:
- Add devm_add_action_or_reset() to dispose irq mapping
Changes since version 8:
- Modify the signed-off-by with my work address
- Add irq_dispose_mapping() in the error handling path and in the remove
function
Changes since version 7:
Changes since version 6:
Changes since version 5:
- Modify the module name and the driver name consistently
Changes since version 4:
- Modify arguments in read/write function to a pointer to cmd_header
- Modify all callers that call the read/write function
Changes since version 3:
- Modify array buffer to structure
- Fix defines and comments
- Drop private mutex and use rtc core lock
- Modify device_set_wakeup_capable() to device_init_wakeup()
Changes since version 2:
- Add MODULE_ALIAS()
Changes since version 1:
- Add each driver's command structure
- Fix platform driver registration
- Drop unnecessary logs
- Fix overwrite error return values
- Modify to use dev_err_probe API
MAINTAINERS | 1 +
drivers/rtc/Kconfig | 10 ++
drivers/rtc/Makefile | 1 +
drivers/rtc/rtc-nct6694.c | 297 ++++++++++++++++++++++++++++++++++++++
4 files changed, 309 insertions(+)
create mode 100644 drivers/rtc/rtc-nct6694.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 66da10824aa9..b12c9a690559 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -17797,6 +17797,7 @@ F: drivers/hwmon/nct6694-hwmon.c
F: drivers/i2c/busses/i2c-nct6694.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
F: include/linux/mfd/nct6694.h
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 9aec922613ce..7fd2439436c1 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -416,6 +416,16 @@ config RTC_DRV_NCT3018Y
This driver can also be built as a module, if so, the module will be
called "rtc-nct3018y".
+config RTC_DRV_NCT6694
+ tristate "Nuvoton NCT6694 RTC support"
+ depends on MFD_NCT6694
+ help
+ If you say yes to this option, support will be included for Nuvoton
+ NCT6694, a USB device to RTC.
+
+ This driver can also be built as a module. If so, the module will
+ be called rtc-nct6694.
+
config RTC_DRV_RK808
tristate "Rockchip RK805/RK808/RK809/RK817/RK818 RTC"
depends on MFD_RK8XX
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 4619aa2ac469..f564f9a9fa5e 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -119,6 +119,7 @@ obj-$(CONFIG_RTC_DRV_MXC) += rtc-mxc.o
obj-$(CONFIG_RTC_DRV_MXC_V2) += rtc-mxc_v2.o
obj-$(CONFIG_RTC_DRV_GAMECUBE) += rtc-gamecube.o
obj-$(CONFIG_RTC_DRV_NCT3018Y) += rtc-nct3018y.o
+obj-$(CONFIG_RTC_DRV_NCT6694) += rtc-nct6694.o
obj-$(CONFIG_RTC_DRV_NTXEC) += rtc-ntxec.o
obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o
obj-$(CONFIG_RTC_DRV_OPAL) += rtc-opal.o
diff --git a/drivers/rtc/rtc-nct6694.c b/drivers/rtc/rtc-nct6694.c
new file mode 100644
index 000000000000..35401a0d9cf5
--- /dev/null
+++ b/drivers/rtc/rtc-nct6694.c
@@ -0,0 +1,297 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Nuvoton NCT6694 RTC driver based on USB interface.
+ *
+ * Copyright (C) 2025 Nuvoton Technology Corp.
+ */
+
+#include <linux/bcd.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/mfd/nct6694.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+
+/*
+ * USB command module type for NCT6694 RTC controller.
+ * This defines the module type used for communication with the NCT6694
+ * RTC controller over the USB interface.
+ */
+#define NCT6694_RTC_MOD 0x08
+
+/* Command 00h - RTC Time */
+#define NCT6694_RTC_TIME 0x0000
+#define NCT6694_RTC_TIME_SEL 0x00
+
+/* Command 01h - RTC Alarm */
+#define NCT6694_RTC_ALARM 0x01
+#define NCT6694_RTC_ALARM_SEL 0x00
+
+/* Command 02h - RTC Status */
+#define NCT6694_RTC_STATUS 0x02
+#define NCT6694_RTC_STATUS_SEL 0x00
+
+#define NCT6694_RTC_IRQ_INT_EN BIT(0) /* Transmit a USB INT-in when RTC alarm */
+#define NCT6694_RTC_IRQ_GPO_EN BIT(5) /* Trigger a GPO Low Pulse when RTC alarm */
+
+#define NCT6694_RTC_IRQ_EN (NCT6694_RTC_IRQ_INT_EN | NCT6694_RTC_IRQ_GPO_EN)
+#define NCT6694_RTC_IRQ_STS BIT(0) /* Write 1 clear IRQ status */
+
+struct __packed nct6694_rtc_time {
+ u8 sec;
+ u8 min;
+ u8 hour;
+ u8 week;
+ u8 day;
+ u8 month;
+ u8 year;
+};
+
+struct __packed nct6694_rtc_alarm {
+ u8 sec;
+ u8 min;
+ u8 hour;
+ u8 alarm_en;
+ u8 alarm_pend;
+};
+
+struct __packed nct6694_rtc_status {
+ u8 irq_en;
+ u8 irq_pend;
+};
+
+union __packed nct6694_rtc_msg {
+ struct nct6694_rtc_time time;
+ struct nct6694_rtc_alarm alarm;
+ struct nct6694_rtc_status sts;
+};
+
+struct nct6694_rtc_data {
+ struct nct6694 *nct6694;
+ struct rtc_device *rtc;
+ union nct6694_rtc_msg *msg;
+ int irq;
+};
+
+static int nct6694_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct nct6694_rtc_data *data = dev_get_drvdata(dev);
+ struct nct6694_rtc_time *time = &data->msg->time;
+ static const struct nct6694_cmd_header cmd_hd = {
+ .mod = NCT6694_RTC_MOD,
+ .cmd = NCT6694_RTC_TIME,
+ .sel = NCT6694_RTC_TIME_SEL,
+ .len = cpu_to_le16(sizeof(*time))
+ };
+ int ret;
+
+ ret = nct6694_read_msg(data->nct6694, &cmd_hd, time);
+ if (ret)
+ return ret;
+
+ tm->tm_sec = bcd2bin(time->sec); /* tm_sec expect 0 ~ 59 */
+ tm->tm_min = bcd2bin(time->min); /* tm_min expect 0 ~ 59 */
+ tm->tm_hour = bcd2bin(time->hour); /* tm_hour expect 0 ~ 23 */
+ tm->tm_wday = bcd2bin(time->week) - 1; /* tm_wday expect 0 ~ 6 */
+ tm->tm_mday = bcd2bin(time->day); /* tm_mday expect 1 ~ 31 */
+ tm->tm_mon = bcd2bin(time->month) - 1; /* tm_month expect 0 ~ 11 */
+ tm->tm_year = bcd2bin(time->year) + 100; /* tm_year expect since 1900 */
+
+ return ret;
+}
+
+static int nct6694_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct nct6694_rtc_data *data = dev_get_drvdata(dev);
+ struct nct6694_rtc_time *time = &data->msg->time;
+ static const struct nct6694_cmd_header cmd_hd = {
+ .mod = NCT6694_RTC_MOD,
+ .cmd = NCT6694_RTC_TIME,
+ .sel = NCT6694_RTC_TIME_SEL,
+ .len = cpu_to_le16(sizeof(*time))
+ };
+
+ time->sec = bin2bcd(tm->tm_sec);
+ time->min = bin2bcd(tm->tm_min);
+ time->hour = bin2bcd(tm->tm_hour);
+ time->week = bin2bcd(tm->tm_wday + 1);
+ time->day = bin2bcd(tm->tm_mday);
+ time->month = bin2bcd(tm->tm_mon + 1);
+ time->year = bin2bcd(tm->tm_year - 100);
+
+ return nct6694_write_msg(data->nct6694, &cmd_hd, time);
+}
+
+static int nct6694_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct nct6694_rtc_data *data = dev_get_drvdata(dev);
+ struct nct6694_rtc_alarm *alarm = &data->msg->alarm;
+ static const struct nct6694_cmd_header cmd_hd = {
+ .mod = NCT6694_RTC_MOD,
+ .cmd = NCT6694_RTC_ALARM,
+ .sel = NCT6694_RTC_ALARM_SEL,
+ .len = cpu_to_le16(sizeof(*alarm))
+ };
+ int ret;
+
+ ret = nct6694_read_msg(data->nct6694, &cmd_hd, alarm);
+ if (ret)
+ return ret;
+
+ alrm->time.tm_sec = bcd2bin(alarm->sec);
+ alrm->time.tm_min = bcd2bin(alarm->min);
+ alrm->time.tm_hour = bcd2bin(alarm->hour);
+ alrm->enabled = alarm->alarm_en;
+ alrm->pending = alarm->alarm_pend;
+
+ return ret;
+}
+
+static int nct6694_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct nct6694_rtc_data *data = dev_get_drvdata(dev);
+ struct nct6694_rtc_alarm *alarm = &data->msg->alarm;
+ static const struct nct6694_cmd_header cmd_hd = {
+ .mod = NCT6694_RTC_MOD,
+ .cmd = NCT6694_RTC_ALARM,
+ .sel = NCT6694_RTC_ALARM_SEL,
+ .len = cpu_to_le16(sizeof(*alarm))
+ };
+
+ alarm->sec = bin2bcd(alrm->time.tm_sec);
+ alarm->min = bin2bcd(alrm->time.tm_min);
+ alarm->hour = bin2bcd(alrm->time.tm_hour);
+ alarm->alarm_en = alrm->enabled ? NCT6694_RTC_IRQ_EN : 0;
+ alarm->alarm_pend = 0;
+
+ return nct6694_write_msg(data->nct6694, &cmd_hd, alarm);
+}
+
+static int nct6694_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct nct6694_rtc_data *data = dev_get_drvdata(dev);
+ struct nct6694_rtc_status *sts = &data->msg->sts;
+ static const struct nct6694_cmd_header cmd_hd = {
+ .mod = NCT6694_RTC_MOD,
+ .cmd = NCT6694_RTC_STATUS,
+ .sel = NCT6694_RTC_STATUS_SEL,
+ .len = cpu_to_le16(sizeof(*sts))
+ };
+
+ if (enabled)
+ sts->irq_en |= NCT6694_RTC_IRQ_EN;
+ else
+ sts->irq_en &= ~NCT6694_RTC_IRQ_EN;
+
+ sts->irq_pend = 0;
+
+ return nct6694_write_msg(data->nct6694, &cmd_hd, sts);
+}
+
+static const struct rtc_class_ops nct6694_rtc_ops = {
+ .read_time = nct6694_rtc_read_time,
+ .set_time = nct6694_rtc_set_time,
+ .read_alarm = nct6694_rtc_read_alarm,
+ .set_alarm = nct6694_rtc_set_alarm,
+ .alarm_irq_enable = nct6694_rtc_alarm_irq_enable,
+};
+
+static irqreturn_t nct6694_irq(int irq, void *dev_id)
+{
+ struct nct6694_rtc_data *data = dev_id;
+ struct nct6694_rtc_status *sts = &data->msg->sts;
+ static const struct nct6694_cmd_header cmd_hd = {
+ .mod = NCT6694_RTC_MOD,
+ .cmd = NCT6694_RTC_STATUS,
+ .sel = NCT6694_RTC_STATUS_SEL,
+ .len = cpu_to_le16(sizeof(*sts))
+ };
+ int ret;
+
+ rtc_lock(data->rtc);
+
+ sts->irq_en = NCT6694_RTC_IRQ_EN;
+ sts->irq_pend = NCT6694_RTC_IRQ_STS;
+ ret = nct6694_write_msg(data->nct6694, &cmd_hd, sts);
+ if (ret) {
+ rtc_unlock(data->rtc);
+ return IRQ_NONE;
+ }
+
+ rtc_update_irq(data->rtc, 1, RTC_IRQF | RTC_AF);
+
+ rtc_unlock(data->rtc);
+
+ return IRQ_HANDLED;
+}
+
+static void nct6694_irq_dispose_mapping(void *d)
+{
+ struct nct6694_rtc_data *data = d;
+
+ irq_dispose_mapping(data->irq);
+}
+
+static int nct6694_rtc_probe(struct platform_device *pdev)
+{
+ struct nct6694_rtc_data *data;
+ struct nct6694 *nct6694 = dev_get_drvdata(pdev->dev.parent);
+ int ret;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->msg = devm_kzalloc(&pdev->dev, sizeof(union nct6694_rtc_msg),
+ GFP_KERNEL);
+ if (!data->msg)
+ return -ENOMEM;
+
+ data->irq = irq_create_mapping(nct6694->domain, NCT6694_IRQ_RTC);
+ if (!data->irq)
+ return -EINVAL;
+
+ ret = devm_add_action_or_reset(&pdev->dev, nct6694_irq_dispose_mapping,
+ data);
+ if (ret)
+ return ret;
+
+ ret = devm_device_init_wakeup(&pdev->dev);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Failed to init wakeup\n");
+
+ data->rtc = devm_rtc_allocate_device(&pdev->dev);
+ if (IS_ERR(data->rtc))
+ return PTR_ERR(data->rtc);
+
+ data->nct6694 = nct6694;
+ data->rtc->ops = &nct6694_rtc_ops;
+ data->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
+ data->rtc->range_max = RTC_TIMESTAMP_END_2099;
+
+ platform_set_drvdata(pdev, data);
+
+ ret = devm_request_threaded_irq(&pdev->dev, data->irq, NULL,
+ nct6694_irq, IRQF_ONESHOT,
+ "rtc-nct6694", data);
+ if (ret < 0)
+ return dev_err_probe(&pdev->dev, ret, "Failed to request irq\n");
+
+ return devm_rtc_register_device(data->rtc);
+}
+
+static struct platform_driver nct6694_rtc_driver = {
+ .driver = {
+ .name = "nct6694-rtc",
+ },
+ .probe = nct6694_rtc_probe,
+};
+
+module_platform_driver(nct6694_rtc_driver);
+
+MODULE_DESCRIPTION("USB-RTC driver for NCT6694");
+MODULE_AUTHOR("Ming Yu <tmyu0@nuvoton.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:nct6694-rtc");
--
2.34.1
prev parent reply other threads:[~2025-06-27 10:28 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-06-27 10:27 [PATCH v13 0/7] Add Nuvoton NCT6694 MFD drivers a0282524688
2025-06-27 10:27 ` [PATCH v13 1/7] mfd: Add core driver for Nuvoton NCT6694 a0282524688
2025-06-28 12:30 ` Markus Elfring
2025-07-02 16:15 ` Lee Jones
2025-07-03 2:39 ` Ming Yu
2025-07-03 2:44 ` Vincent Mailhol
2025-07-07 10:07 ` Marc Kleine-Budde
2025-06-27 10:27 ` [PATCH v13 2/7] gpio: Add Nuvoton NCT6694 GPIO support a0282524688
2025-06-27 10:27 ` [PATCH v13 3/7] i2c: Add Nuvoton NCT6694 I2C support a0282524688
2025-07-14 7:37 ` Wolfram Sang
2025-06-27 10:27 ` [PATCH v13 4/7] can: Add Nuvoton NCT6694 CANFD support a0282524688
2025-06-27 10:27 ` [PATCH v13 5/7] watchdog: Add Nuvoton NCT6694 WDT support a0282524688
2025-07-02 22:16 ` Guenter Roeck
2025-06-27 10:27 ` [PATCH v13 6/7] hwmon: Add Nuvoton NCT6694 HWMON support a0282524688
2025-06-27 10:27 ` a0282524688 [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250627102730.71222-8-a0282524688@gmail.com \
--to=a0282524688@gmail.com \
--cc=alexandre.belloni@bootlin.com \
--cc=andi.shyti@kernel.org \
--cc=andrew+netdev@lunn.ch \
--cc=brgl@bgdev.pl \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=jdelvare@suse.com \
--cc=kuba@kernel.org \
--cc=lee@kernel.org \
--cc=linus.walleij@linaro.org \
--cc=linux-can@vger.kernel.org \
--cc=linux-gpio@vger.kernel.org \
--cc=linux-hwmon@vger.kernel.org \
--cc=linux-i2c@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-rtc@vger.kernel.org \
--cc=linux-usb@vger.kernel.org \
--cc=linux-watchdog@vger.kernel.org \
--cc=linux@roeck-us.net \
--cc=mailhol.vincent@wanadoo.fr \
--cc=mkl@pengutronix.de \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=tmyu0@nuvoton.com \
--cc=wim@linux-watchdog.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.