File Index
Files and directories
Sign in
FILE

rtc-nct6694.c

File preview with syntax highlighting.

linux-6.19.14/drivers/rtc/rtc-nct6694.c 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
 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");
~/ Read-only file preview.