Skip to content

Commit d463bb1

Browse files
m1ng1109lag-linaro
authored andcommitted
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>
1 parent 197e779 commit d463bb1

4 files changed

Lines changed: 309 additions & 0 deletions

File tree

MAINTAINERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18090,6 +18090,7 @@ F: drivers/hwmon/nct6694-hwmon.c
1809018090
F: drivers/i2c/busses/i2c-nct6694.c
1809118091
F: drivers/mfd/nct6694.c
1809218092
F: drivers/net/can/usb/nct6694_canfd.c
18093+
F: drivers/rtc/rtc-nct6694.c
1809318094
F: drivers/watchdog/nct6694_wdt.c
1809418095
F: include/linux/mfd/nct6694.h
1809518096

drivers/rtc/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,16 @@ config RTC_DRV_NCT3018Y
416416
This driver can also be built as a module, if so, the module will be
417417
called "rtc-nct3018y".
418418

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+
419429
config RTC_DRV_RK808
420430
tristate "Rockchip RK805/RK808/RK809/RK817/RK818 RTC"
421431
depends on MFD_RK8XX

drivers/rtc/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ obj-$(CONFIG_RTC_DRV_MXC) += rtc-mxc.o
119119
obj-$(CONFIG_RTC_DRV_MXC_V2) += rtc-mxc_v2.o
120120
obj-$(CONFIG_RTC_DRV_GAMECUBE) += rtc-gamecube.o
121121
obj-$(CONFIG_RTC_DRV_NCT3018Y) += rtc-nct3018y.o
122+
obj-$(CONFIG_RTC_DRV_NCT6694) += rtc-nct6694.o
122123
obj-$(CONFIG_RTC_DRV_NTXEC) += rtc-ntxec.o
123124
obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o
124125
obj-$(CONFIG_RTC_DRV_OPAL) += rtc-opal.o

drivers/rtc/rtc-nct6694.c

Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
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");

0 commit comments

Comments
 (0)