Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

rtc: Add Nuvoton NCT6694 RTC support

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>
Link: https://lore.kernel.org/r/20250912091952.1169369-8-a0282524688@gmail.com
Signed-off-by: Lee Jones <lee@kernel.org>

authored by

Ming Yu and committed by
Lee Jones
d463bb14 197e779d

+309
+1
MAINTAINERS
··· 18090 18090 F: drivers/i2c/busses/i2c-nct6694.c 18091 18091 F: drivers/mfd/nct6694.c 18092 18092 F: drivers/net/can/usb/nct6694_canfd.c 18093 + F: drivers/rtc/rtc-nct6694.c 18093 18094 F: drivers/watchdog/nct6694_wdt.c 18094 18095 F: include/linux/mfd/nct6694.h 18095 18096
+10
drivers/rtc/Kconfig
··· 416 416 This driver can also be built as a module, if so, the module will be 417 417 called "rtc-nct3018y". 418 418 419 + config RTC_DRV_NCT6694 420 + tristate "Nuvoton NCT6694 RTC support" 421 + depends on MFD_NCT6694 422 + help 423 + If you say yes to this option, support will be included for Nuvoton 424 + NCT6694, a USB device to RTC. 425 + 426 + This driver can also be built as a module. If so, the module will 427 + be called rtc-nct6694. 428 + 419 429 config RTC_DRV_RK808 420 430 tristate "Rockchip RK805/RK808/RK809/RK817/RK818 RTC" 421 431 depends on MFD_RK8XX
+1
drivers/rtc/Makefile
··· 119 119 obj-$(CONFIG_RTC_DRV_MXC_V2) += rtc-mxc_v2.o 120 120 obj-$(CONFIG_RTC_DRV_GAMECUBE) += rtc-gamecube.o 121 121 obj-$(CONFIG_RTC_DRV_NCT3018Y) += rtc-nct3018y.o 122 + obj-$(CONFIG_RTC_DRV_NCT6694) += rtc-nct6694.o 122 123 obj-$(CONFIG_RTC_DRV_NTXEC) += rtc-ntxec.o 123 124 obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o 124 125 obj-$(CONFIG_RTC_DRV_OPAL) += rtc-opal.o
+297
drivers/rtc/rtc-nct6694.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Nuvoton NCT6694 RTC driver based on USB interface. 4 + * 5 + * Copyright (C) 2025 Nuvoton Technology Corp. 6 + */ 7 + 8 + #include <linux/bcd.h> 9 + #include <linux/irqdomain.h> 10 + #include <linux/kernel.h> 11 + #include <linux/mfd/nct6694.h> 12 + #include <linux/module.h> 13 + #include <linux/platform_device.h> 14 + #include <linux/rtc.h> 15 + #include <linux/slab.h> 16 + 17 + /* 18 + * USB command module type for NCT6694 RTC controller. 19 + * This defines the module type used for communication with the NCT6694 20 + * RTC controller over the USB interface. 21 + */ 22 + #define NCT6694_RTC_MOD 0x08 23 + 24 + /* Command 00h - RTC Time */ 25 + #define NCT6694_RTC_TIME 0x0000 26 + #define NCT6694_RTC_TIME_SEL 0x00 27 + 28 + /* Command 01h - RTC Alarm */ 29 + #define NCT6694_RTC_ALARM 0x01 30 + #define NCT6694_RTC_ALARM_SEL 0x00 31 + 32 + /* Command 02h - RTC Status */ 33 + #define NCT6694_RTC_STATUS 0x02 34 + #define NCT6694_RTC_STATUS_SEL 0x00 35 + 36 + #define NCT6694_RTC_IRQ_INT_EN BIT(0) /* Transmit a USB INT-in when RTC alarm */ 37 + #define NCT6694_RTC_IRQ_GPO_EN BIT(5) /* Trigger a GPO Low Pulse when RTC alarm */ 38 + 39 + #define NCT6694_RTC_IRQ_EN (NCT6694_RTC_IRQ_INT_EN | NCT6694_RTC_IRQ_GPO_EN) 40 + #define NCT6694_RTC_IRQ_STS BIT(0) /* Write 1 clear IRQ status */ 41 + 42 + struct __packed nct6694_rtc_time { 43 + u8 sec; 44 + u8 min; 45 + u8 hour; 46 + u8 week; 47 + u8 day; 48 + u8 month; 49 + u8 year; 50 + }; 51 + 52 + struct __packed nct6694_rtc_alarm { 53 + u8 sec; 54 + u8 min; 55 + u8 hour; 56 + u8 alarm_en; 57 + u8 alarm_pend; 58 + }; 59 + 60 + struct __packed nct6694_rtc_status { 61 + u8 irq_en; 62 + u8 irq_pend; 63 + }; 64 + 65 + union __packed nct6694_rtc_msg { 66 + struct nct6694_rtc_time time; 67 + struct nct6694_rtc_alarm alarm; 68 + struct nct6694_rtc_status sts; 69 + }; 70 + 71 + struct nct6694_rtc_data { 72 + struct nct6694 *nct6694; 73 + struct rtc_device *rtc; 74 + union nct6694_rtc_msg *msg; 75 + int irq; 76 + }; 77 + 78 + static int nct6694_rtc_read_time(struct device *dev, struct rtc_time *tm) 79 + { 80 + struct nct6694_rtc_data *data = dev_get_drvdata(dev); 81 + struct nct6694_rtc_time *time = &data->msg->time; 82 + static const struct nct6694_cmd_header cmd_hd = { 83 + .mod = NCT6694_RTC_MOD, 84 + .cmd = NCT6694_RTC_TIME, 85 + .sel = NCT6694_RTC_TIME_SEL, 86 + .len = cpu_to_le16(sizeof(*time)) 87 + }; 88 + int ret; 89 + 90 + ret = nct6694_read_msg(data->nct6694, &cmd_hd, time); 91 + if (ret) 92 + return ret; 93 + 94 + tm->tm_sec = bcd2bin(time->sec); /* tm_sec expect 0 ~ 59 */ 95 + tm->tm_min = bcd2bin(time->min); /* tm_min expect 0 ~ 59 */ 96 + tm->tm_hour = bcd2bin(time->hour); /* tm_hour expect 0 ~ 23 */ 97 + tm->tm_wday = bcd2bin(time->week) - 1; /* tm_wday expect 0 ~ 6 */ 98 + tm->tm_mday = bcd2bin(time->day); /* tm_mday expect 1 ~ 31 */ 99 + tm->tm_mon = bcd2bin(time->month) - 1; /* tm_month expect 0 ~ 11 */ 100 + tm->tm_year = bcd2bin(time->year) + 100; /* tm_year expect since 1900 */ 101 + 102 + return ret; 103 + } 104 + 105 + static int nct6694_rtc_set_time(struct device *dev, struct rtc_time *tm) 106 + { 107 + struct nct6694_rtc_data *data = dev_get_drvdata(dev); 108 + struct nct6694_rtc_time *time = &data->msg->time; 109 + static const struct nct6694_cmd_header cmd_hd = { 110 + .mod = NCT6694_RTC_MOD, 111 + .cmd = NCT6694_RTC_TIME, 112 + .sel = NCT6694_RTC_TIME_SEL, 113 + .len = cpu_to_le16(sizeof(*time)) 114 + }; 115 + 116 + time->sec = bin2bcd(tm->tm_sec); 117 + time->min = bin2bcd(tm->tm_min); 118 + time->hour = bin2bcd(tm->tm_hour); 119 + time->week = bin2bcd(tm->tm_wday + 1); 120 + time->day = bin2bcd(tm->tm_mday); 121 + time->month = bin2bcd(tm->tm_mon + 1); 122 + time->year = bin2bcd(tm->tm_year - 100); 123 + 124 + return nct6694_write_msg(data->nct6694, &cmd_hd, time); 125 + } 126 + 127 + static int nct6694_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) 128 + { 129 + struct nct6694_rtc_data *data = dev_get_drvdata(dev); 130 + struct nct6694_rtc_alarm *alarm = &data->msg->alarm; 131 + static const struct nct6694_cmd_header cmd_hd = { 132 + .mod = NCT6694_RTC_MOD, 133 + .cmd = NCT6694_RTC_ALARM, 134 + .sel = NCT6694_RTC_ALARM_SEL, 135 + .len = cpu_to_le16(sizeof(*alarm)) 136 + }; 137 + int ret; 138 + 139 + ret = nct6694_read_msg(data->nct6694, &cmd_hd, alarm); 140 + if (ret) 141 + return ret; 142 + 143 + alrm->time.tm_sec = bcd2bin(alarm->sec); 144 + alrm->time.tm_min = bcd2bin(alarm->min); 145 + alrm->time.tm_hour = bcd2bin(alarm->hour); 146 + alrm->enabled = alarm->alarm_en; 147 + alrm->pending = alarm->alarm_pend; 148 + 149 + return ret; 150 + } 151 + 152 + static int nct6694_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) 153 + { 154 + struct nct6694_rtc_data *data = dev_get_drvdata(dev); 155 + struct nct6694_rtc_alarm *alarm = &data->msg->alarm; 156 + static const struct nct6694_cmd_header cmd_hd = { 157 + .mod = NCT6694_RTC_MOD, 158 + .cmd = NCT6694_RTC_ALARM, 159 + .sel = NCT6694_RTC_ALARM_SEL, 160 + .len = cpu_to_le16(sizeof(*alarm)) 161 + }; 162 + 163 + alarm->sec = bin2bcd(alrm->time.tm_sec); 164 + alarm->min = bin2bcd(alrm->time.tm_min); 165 + alarm->hour = bin2bcd(alrm->time.tm_hour); 166 + alarm->alarm_en = alrm->enabled ? NCT6694_RTC_IRQ_EN : 0; 167 + alarm->alarm_pend = 0; 168 + 169 + return nct6694_write_msg(data->nct6694, &cmd_hd, alarm); 170 + } 171 + 172 + static int nct6694_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) 173 + { 174 + struct nct6694_rtc_data *data = dev_get_drvdata(dev); 175 + struct nct6694_rtc_status *sts = &data->msg->sts; 176 + static const struct nct6694_cmd_header cmd_hd = { 177 + .mod = NCT6694_RTC_MOD, 178 + .cmd = NCT6694_RTC_STATUS, 179 + .sel = NCT6694_RTC_STATUS_SEL, 180 + .len = cpu_to_le16(sizeof(*sts)) 181 + }; 182 + 183 + if (enabled) 184 + sts->irq_en |= NCT6694_RTC_IRQ_EN; 185 + else 186 + sts->irq_en &= ~NCT6694_RTC_IRQ_EN; 187 + 188 + sts->irq_pend = 0; 189 + 190 + return nct6694_write_msg(data->nct6694, &cmd_hd, sts); 191 + } 192 + 193 + static const struct rtc_class_ops nct6694_rtc_ops = { 194 + .read_time = nct6694_rtc_read_time, 195 + .set_time = nct6694_rtc_set_time, 196 + .read_alarm = nct6694_rtc_read_alarm, 197 + .set_alarm = nct6694_rtc_set_alarm, 198 + .alarm_irq_enable = nct6694_rtc_alarm_irq_enable, 199 + }; 200 + 201 + static irqreturn_t nct6694_irq(int irq, void *dev_id) 202 + { 203 + struct nct6694_rtc_data *data = dev_id; 204 + struct nct6694_rtc_status *sts = &data->msg->sts; 205 + static const struct nct6694_cmd_header cmd_hd = { 206 + .mod = NCT6694_RTC_MOD, 207 + .cmd = NCT6694_RTC_STATUS, 208 + .sel = NCT6694_RTC_STATUS_SEL, 209 + .len = cpu_to_le16(sizeof(*sts)) 210 + }; 211 + int ret; 212 + 213 + rtc_lock(data->rtc); 214 + 215 + sts->irq_en = NCT6694_RTC_IRQ_EN; 216 + sts->irq_pend = NCT6694_RTC_IRQ_STS; 217 + ret = nct6694_write_msg(data->nct6694, &cmd_hd, sts); 218 + if (ret) { 219 + rtc_unlock(data->rtc); 220 + return IRQ_NONE; 221 + } 222 + 223 + rtc_update_irq(data->rtc, 1, RTC_IRQF | RTC_AF); 224 + 225 + rtc_unlock(data->rtc); 226 + 227 + return IRQ_HANDLED; 228 + } 229 + 230 + static void nct6694_irq_dispose_mapping(void *d) 231 + { 232 + struct nct6694_rtc_data *data = d; 233 + 234 + irq_dispose_mapping(data->irq); 235 + } 236 + 237 + static int nct6694_rtc_probe(struct platform_device *pdev) 238 + { 239 + struct nct6694_rtc_data *data; 240 + struct nct6694 *nct6694 = dev_get_drvdata(pdev->dev.parent); 241 + int ret; 242 + 243 + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 244 + if (!data) 245 + return -ENOMEM; 246 + 247 + data->msg = devm_kzalloc(&pdev->dev, sizeof(union nct6694_rtc_msg), 248 + GFP_KERNEL); 249 + if (!data->msg) 250 + return -ENOMEM; 251 + 252 + data->irq = irq_create_mapping(nct6694->domain, NCT6694_IRQ_RTC); 253 + if (!data->irq) 254 + return -EINVAL; 255 + 256 + ret = devm_add_action_or_reset(&pdev->dev, nct6694_irq_dispose_mapping, 257 + data); 258 + if (ret) 259 + return ret; 260 + 261 + ret = devm_device_init_wakeup(&pdev->dev); 262 + if (ret) 263 + return dev_err_probe(&pdev->dev, ret, "Failed to init wakeup\n"); 264 + 265 + data->rtc = devm_rtc_allocate_device(&pdev->dev); 266 + if (IS_ERR(data->rtc)) 267 + return PTR_ERR(data->rtc); 268 + 269 + data->nct6694 = nct6694; 270 + data->rtc->ops = &nct6694_rtc_ops; 271 + data->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; 272 + data->rtc->range_max = RTC_TIMESTAMP_END_2099; 273 + 274 + platform_set_drvdata(pdev, data); 275 + 276 + ret = devm_request_threaded_irq(&pdev->dev, data->irq, NULL, 277 + nct6694_irq, IRQF_ONESHOT, 278 + "rtc-nct6694", data); 279 + if (ret < 0) 280 + return dev_err_probe(&pdev->dev, ret, "Failed to request irq\n"); 281 + 282 + return devm_rtc_register_device(data->rtc); 283 + } 284 + 285 + static struct platform_driver nct6694_rtc_driver = { 286 + .driver = { 287 + .name = "nct6694-rtc", 288 + }, 289 + .probe = nct6694_rtc_probe, 290 + }; 291 + 292 + module_platform_driver(nct6694_rtc_driver); 293 + 294 + MODULE_DESCRIPTION("USB-RTC driver for NCT6694"); 295 + MODULE_AUTHOR("Ming Yu <tmyu0@nuvoton.com>"); 296 + MODULE_LICENSE("GPL"); 297 + MODULE_ALIAS("platform:nct6694-rtc");