Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
42struct __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
52struct __packed nct6694_rtc_alarm {
53 u8 sec;
54 u8 min;
55 u8 hour;
56 u8 alarm_en;
57 u8 alarm_pend;
58};
59
60struct __packed nct6694_rtc_status {
61 u8 irq_en;
62 u8 irq_pend;
63};
64
65union __packed nct6694_rtc_msg {
66 struct nct6694_rtc_time time;
67 struct nct6694_rtc_alarm alarm;
68 struct nct6694_rtc_status sts;
69};
70
71struct nct6694_rtc_data {
72 struct nct6694 *nct6694;
73 struct rtc_device *rtc;
74 union nct6694_rtc_msg *msg;
75 int irq;
76};
77
78static 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
105static 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
127static 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
152static 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
172static 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
193static 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
201static 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
230static void nct6694_irq_dispose_mapping(void *d)
231{
232 struct nct6694_rtc_data *data = d;
233
234 irq_dispose_mapping(data->irq);
235}
236
237static 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
285static struct platform_driver nct6694_rtc_driver = {
286 .driver = {
287 .name = "nct6694-rtc",
288 },
289 .probe = nct6694_rtc_probe,
290};
291
292module_platform_driver(nct6694_rtc_driver);
293
294MODULE_DESCRIPTION("USB-RTC driver for NCT6694");
295MODULE_AUTHOR("Ming Yu <tmyu0@nuvoton.com>");
296MODULE_LICENSE("GPL");
297MODULE_ALIAS("platform:nct6694-rtc");