1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /* NXP PCF50633 RTC Driver
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * (C) 2006-2008 by Openmoko, Inc.
5*4882a593Smuzhiyun * Author: Balaji Rao <balajirrao@openmoko.org>
6*4882a593Smuzhiyun * All rights reserved.
7*4882a593Smuzhiyun *
8*4882a593Smuzhiyun * Broken down from monstrous PCF50633 driver mainly by
9*4882a593Smuzhiyun * Harald Welte, Andy Green and Werner Almesberger
10*4882a593Smuzhiyun */
11*4882a593Smuzhiyun
12*4882a593Smuzhiyun #include <linux/kernel.h>
13*4882a593Smuzhiyun #include <linux/module.h>
14*4882a593Smuzhiyun #include <linux/init.h>
15*4882a593Smuzhiyun #include <linux/device.h>
16*4882a593Smuzhiyun #include <linux/slab.h>
17*4882a593Smuzhiyun #include <linux/platform_device.h>
18*4882a593Smuzhiyun #include <linux/rtc.h>
19*4882a593Smuzhiyun #include <linux/bcd.h>
20*4882a593Smuzhiyun #include <linux/err.h>
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun #include <linux/mfd/pcf50633/core.h>
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun #define PCF50633_REG_RTCSC 0x59 /* Second */
25*4882a593Smuzhiyun #define PCF50633_REG_RTCMN 0x5a /* Minute */
26*4882a593Smuzhiyun #define PCF50633_REG_RTCHR 0x5b /* Hour */
27*4882a593Smuzhiyun #define PCF50633_REG_RTCWD 0x5c /* Weekday */
28*4882a593Smuzhiyun #define PCF50633_REG_RTCDT 0x5d /* Day */
29*4882a593Smuzhiyun #define PCF50633_REG_RTCMT 0x5e /* Month */
30*4882a593Smuzhiyun #define PCF50633_REG_RTCYR 0x5f /* Year */
31*4882a593Smuzhiyun #define PCF50633_REG_RTCSCA 0x60 /* Alarm Second */
32*4882a593Smuzhiyun #define PCF50633_REG_RTCMNA 0x61 /* Alarm Minute */
33*4882a593Smuzhiyun #define PCF50633_REG_RTCHRA 0x62 /* Alarm Hour */
34*4882a593Smuzhiyun #define PCF50633_REG_RTCWDA 0x63 /* Alarm Weekday */
35*4882a593Smuzhiyun #define PCF50633_REG_RTCDTA 0x64 /* Alarm Day */
36*4882a593Smuzhiyun #define PCF50633_REG_RTCMTA 0x65 /* Alarm Month */
37*4882a593Smuzhiyun #define PCF50633_REG_RTCYRA 0x66 /* Alarm Year */
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun enum pcf50633_time_indexes {
40*4882a593Smuzhiyun PCF50633_TI_SEC,
41*4882a593Smuzhiyun PCF50633_TI_MIN,
42*4882a593Smuzhiyun PCF50633_TI_HOUR,
43*4882a593Smuzhiyun PCF50633_TI_WKDAY,
44*4882a593Smuzhiyun PCF50633_TI_DAY,
45*4882a593Smuzhiyun PCF50633_TI_MONTH,
46*4882a593Smuzhiyun PCF50633_TI_YEAR,
47*4882a593Smuzhiyun PCF50633_TI_EXTENT /* always last */
48*4882a593Smuzhiyun };
49*4882a593Smuzhiyun
50*4882a593Smuzhiyun struct pcf50633_time {
51*4882a593Smuzhiyun u_int8_t time[PCF50633_TI_EXTENT];
52*4882a593Smuzhiyun };
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun struct pcf50633_rtc {
55*4882a593Smuzhiyun int alarm_enabled;
56*4882a593Smuzhiyun int alarm_pending;
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun struct pcf50633 *pcf;
59*4882a593Smuzhiyun struct rtc_device *rtc_dev;
60*4882a593Smuzhiyun };
61*4882a593Smuzhiyun
pcf2rtc_time(struct rtc_time * rtc,struct pcf50633_time * pcf)62*4882a593Smuzhiyun static void pcf2rtc_time(struct rtc_time *rtc, struct pcf50633_time *pcf)
63*4882a593Smuzhiyun {
64*4882a593Smuzhiyun rtc->tm_sec = bcd2bin(pcf->time[PCF50633_TI_SEC]);
65*4882a593Smuzhiyun rtc->tm_min = bcd2bin(pcf->time[PCF50633_TI_MIN]);
66*4882a593Smuzhiyun rtc->tm_hour = bcd2bin(pcf->time[PCF50633_TI_HOUR]);
67*4882a593Smuzhiyun rtc->tm_wday = bcd2bin(pcf->time[PCF50633_TI_WKDAY]);
68*4882a593Smuzhiyun rtc->tm_mday = bcd2bin(pcf->time[PCF50633_TI_DAY]);
69*4882a593Smuzhiyun rtc->tm_mon = bcd2bin(pcf->time[PCF50633_TI_MONTH]) - 1;
70*4882a593Smuzhiyun rtc->tm_year = bcd2bin(pcf->time[PCF50633_TI_YEAR]) + 100;
71*4882a593Smuzhiyun }
72*4882a593Smuzhiyun
rtc2pcf_time(struct pcf50633_time * pcf,struct rtc_time * rtc)73*4882a593Smuzhiyun static void rtc2pcf_time(struct pcf50633_time *pcf, struct rtc_time *rtc)
74*4882a593Smuzhiyun {
75*4882a593Smuzhiyun pcf->time[PCF50633_TI_SEC] = bin2bcd(rtc->tm_sec);
76*4882a593Smuzhiyun pcf->time[PCF50633_TI_MIN] = bin2bcd(rtc->tm_min);
77*4882a593Smuzhiyun pcf->time[PCF50633_TI_HOUR] = bin2bcd(rtc->tm_hour);
78*4882a593Smuzhiyun pcf->time[PCF50633_TI_WKDAY] = bin2bcd(rtc->tm_wday);
79*4882a593Smuzhiyun pcf->time[PCF50633_TI_DAY] = bin2bcd(rtc->tm_mday);
80*4882a593Smuzhiyun pcf->time[PCF50633_TI_MONTH] = bin2bcd(rtc->tm_mon + 1);
81*4882a593Smuzhiyun pcf->time[PCF50633_TI_YEAR] = bin2bcd(rtc->tm_year % 100);
82*4882a593Smuzhiyun }
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun static int
pcf50633_rtc_alarm_irq_enable(struct device * dev,unsigned int enabled)85*4882a593Smuzhiyun pcf50633_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
86*4882a593Smuzhiyun {
87*4882a593Smuzhiyun struct pcf50633_rtc *rtc = dev_get_drvdata(dev);
88*4882a593Smuzhiyun int err;
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun if (enabled)
91*4882a593Smuzhiyun err = pcf50633_irq_unmask(rtc->pcf, PCF50633_IRQ_ALARM);
92*4882a593Smuzhiyun else
93*4882a593Smuzhiyun err = pcf50633_irq_mask(rtc->pcf, PCF50633_IRQ_ALARM);
94*4882a593Smuzhiyun
95*4882a593Smuzhiyun if (err < 0)
96*4882a593Smuzhiyun return err;
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun rtc->alarm_enabled = enabled;
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun return 0;
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun
pcf50633_rtc_read_time(struct device * dev,struct rtc_time * tm)103*4882a593Smuzhiyun static int pcf50633_rtc_read_time(struct device *dev, struct rtc_time *tm)
104*4882a593Smuzhiyun {
105*4882a593Smuzhiyun struct pcf50633_rtc *rtc;
106*4882a593Smuzhiyun struct pcf50633_time pcf_tm;
107*4882a593Smuzhiyun int ret;
108*4882a593Smuzhiyun
109*4882a593Smuzhiyun rtc = dev_get_drvdata(dev);
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun ret = pcf50633_read_block(rtc->pcf, PCF50633_REG_RTCSC,
112*4882a593Smuzhiyun PCF50633_TI_EXTENT,
113*4882a593Smuzhiyun &pcf_tm.time[0]);
114*4882a593Smuzhiyun if (ret != PCF50633_TI_EXTENT) {
115*4882a593Smuzhiyun dev_err(dev, "Failed to read time\n");
116*4882a593Smuzhiyun return -EIO;
117*4882a593Smuzhiyun }
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun dev_dbg(dev, "PCF_TIME: %02x.%02x.%02x %02x:%02x:%02x\n",
120*4882a593Smuzhiyun pcf_tm.time[PCF50633_TI_DAY],
121*4882a593Smuzhiyun pcf_tm.time[PCF50633_TI_MONTH],
122*4882a593Smuzhiyun pcf_tm.time[PCF50633_TI_YEAR],
123*4882a593Smuzhiyun pcf_tm.time[PCF50633_TI_HOUR],
124*4882a593Smuzhiyun pcf_tm.time[PCF50633_TI_MIN],
125*4882a593Smuzhiyun pcf_tm.time[PCF50633_TI_SEC]);
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun pcf2rtc_time(tm, &pcf_tm);
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun dev_dbg(dev, "RTC_TIME: %ptRr\n", tm);
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun return 0;
132*4882a593Smuzhiyun }
133*4882a593Smuzhiyun
pcf50633_rtc_set_time(struct device * dev,struct rtc_time * tm)134*4882a593Smuzhiyun static int pcf50633_rtc_set_time(struct device *dev, struct rtc_time *tm)
135*4882a593Smuzhiyun {
136*4882a593Smuzhiyun struct pcf50633_rtc *rtc;
137*4882a593Smuzhiyun struct pcf50633_time pcf_tm;
138*4882a593Smuzhiyun int alarm_masked, ret = 0;
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun rtc = dev_get_drvdata(dev);
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun dev_dbg(dev, "RTC_TIME: %ptRr\n", tm);
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun rtc2pcf_time(&pcf_tm, tm);
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun dev_dbg(dev, "PCF_TIME: %02x.%02x.%02x %02x:%02x:%02x\n",
147*4882a593Smuzhiyun pcf_tm.time[PCF50633_TI_DAY],
148*4882a593Smuzhiyun pcf_tm.time[PCF50633_TI_MONTH],
149*4882a593Smuzhiyun pcf_tm.time[PCF50633_TI_YEAR],
150*4882a593Smuzhiyun pcf_tm.time[PCF50633_TI_HOUR],
151*4882a593Smuzhiyun pcf_tm.time[PCF50633_TI_MIN],
152*4882a593Smuzhiyun pcf_tm.time[PCF50633_TI_SEC]);
153*4882a593Smuzhiyun
154*4882a593Smuzhiyun
155*4882a593Smuzhiyun alarm_masked = pcf50633_irq_mask_get(rtc->pcf, PCF50633_IRQ_ALARM);
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun if (!alarm_masked)
158*4882a593Smuzhiyun pcf50633_irq_mask(rtc->pcf, PCF50633_IRQ_ALARM);
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun /* Returns 0 on success */
161*4882a593Smuzhiyun ret = pcf50633_write_block(rtc->pcf, PCF50633_REG_RTCSC,
162*4882a593Smuzhiyun PCF50633_TI_EXTENT,
163*4882a593Smuzhiyun &pcf_tm.time[0]);
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun if (!alarm_masked)
166*4882a593Smuzhiyun pcf50633_irq_unmask(rtc->pcf, PCF50633_IRQ_ALARM);
167*4882a593Smuzhiyun
168*4882a593Smuzhiyun return ret;
169*4882a593Smuzhiyun }
170*4882a593Smuzhiyun
pcf50633_rtc_read_alarm(struct device * dev,struct rtc_wkalrm * alrm)171*4882a593Smuzhiyun static int pcf50633_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
172*4882a593Smuzhiyun {
173*4882a593Smuzhiyun struct pcf50633_rtc *rtc;
174*4882a593Smuzhiyun struct pcf50633_time pcf_tm;
175*4882a593Smuzhiyun int ret = 0;
176*4882a593Smuzhiyun
177*4882a593Smuzhiyun rtc = dev_get_drvdata(dev);
178*4882a593Smuzhiyun
179*4882a593Smuzhiyun alrm->enabled = rtc->alarm_enabled;
180*4882a593Smuzhiyun alrm->pending = rtc->alarm_pending;
181*4882a593Smuzhiyun
182*4882a593Smuzhiyun ret = pcf50633_read_block(rtc->pcf, PCF50633_REG_RTCSCA,
183*4882a593Smuzhiyun PCF50633_TI_EXTENT, &pcf_tm.time[0]);
184*4882a593Smuzhiyun if (ret != PCF50633_TI_EXTENT) {
185*4882a593Smuzhiyun dev_err(dev, "Failed to read time\n");
186*4882a593Smuzhiyun return -EIO;
187*4882a593Smuzhiyun }
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun pcf2rtc_time(&alrm->time, &pcf_tm);
190*4882a593Smuzhiyun
191*4882a593Smuzhiyun return rtc_valid_tm(&alrm->time);
192*4882a593Smuzhiyun }
193*4882a593Smuzhiyun
pcf50633_rtc_set_alarm(struct device * dev,struct rtc_wkalrm * alrm)194*4882a593Smuzhiyun static int pcf50633_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
195*4882a593Smuzhiyun {
196*4882a593Smuzhiyun struct pcf50633_rtc *rtc;
197*4882a593Smuzhiyun struct pcf50633_time pcf_tm;
198*4882a593Smuzhiyun int alarm_masked, ret = 0;
199*4882a593Smuzhiyun
200*4882a593Smuzhiyun rtc = dev_get_drvdata(dev);
201*4882a593Smuzhiyun
202*4882a593Smuzhiyun rtc2pcf_time(&pcf_tm, &alrm->time);
203*4882a593Smuzhiyun
204*4882a593Smuzhiyun /* do like mktime does and ignore tm_wday */
205*4882a593Smuzhiyun pcf_tm.time[PCF50633_TI_WKDAY] = 7;
206*4882a593Smuzhiyun
207*4882a593Smuzhiyun alarm_masked = pcf50633_irq_mask_get(rtc->pcf, PCF50633_IRQ_ALARM);
208*4882a593Smuzhiyun
209*4882a593Smuzhiyun /* disable alarm interrupt */
210*4882a593Smuzhiyun if (!alarm_masked)
211*4882a593Smuzhiyun pcf50633_irq_mask(rtc->pcf, PCF50633_IRQ_ALARM);
212*4882a593Smuzhiyun
213*4882a593Smuzhiyun /* Returns 0 on success */
214*4882a593Smuzhiyun ret = pcf50633_write_block(rtc->pcf, PCF50633_REG_RTCSCA,
215*4882a593Smuzhiyun PCF50633_TI_EXTENT, &pcf_tm.time[0]);
216*4882a593Smuzhiyun if (!alrm->enabled)
217*4882a593Smuzhiyun rtc->alarm_pending = 0;
218*4882a593Smuzhiyun
219*4882a593Smuzhiyun if (!alarm_masked || alrm->enabled)
220*4882a593Smuzhiyun pcf50633_irq_unmask(rtc->pcf, PCF50633_IRQ_ALARM);
221*4882a593Smuzhiyun rtc->alarm_enabled = alrm->enabled;
222*4882a593Smuzhiyun
223*4882a593Smuzhiyun return ret;
224*4882a593Smuzhiyun }
225*4882a593Smuzhiyun
226*4882a593Smuzhiyun static const struct rtc_class_ops pcf50633_rtc_ops = {
227*4882a593Smuzhiyun .read_time = pcf50633_rtc_read_time,
228*4882a593Smuzhiyun .set_time = pcf50633_rtc_set_time,
229*4882a593Smuzhiyun .read_alarm = pcf50633_rtc_read_alarm,
230*4882a593Smuzhiyun .set_alarm = pcf50633_rtc_set_alarm,
231*4882a593Smuzhiyun .alarm_irq_enable = pcf50633_rtc_alarm_irq_enable,
232*4882a593Smuzhiyun };
233*4882a593Smuzhiyun
pcf50633_rtc_irq(int irq,void * data)234*4882a593Smuzhiyun static void pcf50633_rtc_irq(int irq, void *data)
235*4882a593Smuzhiyun {
236*4882a593Smuzhiyun struct pcf50633_rtc *rtc = data;
237*4882a593Smuzhiyun
238*4882a593Smuzhiyun rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF);
239*4882a593Smuzhiyun rtc->alarm_pending = 1;
240*4882a593Smuzhiyun }
241*4882a593Smuzhiyun
pcf50633_rtc_probe(struct platform_device * pdev)242*4882a593Smuzhiyun static int pcf50633_rtc_probe(struct platform_device *pdev)
243*4882a593Smuzhiyun {
244*4882a593Smuzhiyun struct pcf50633_rtc *rtc;
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
247*4882a593Smuzhiyun if (!rtc)
248*4882a593Smuzhiyun return -ENOMEM;
249*4882a593Smuzhiyun
250*4882a593Smuzhiyun rtc->pcf = dev_to_pcf50633(pdev->dev.parent);
251*4882a593Smuzhiyun platform_set_drvdata(pdev, rtc);
252*4882a593Smuzhiyun rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, "pcf50633-rtc",
253*4882a593Smuzhiyun &pcf50633_rtc_ops, THIS_MODULE);
254*4882a593Smuzhiyun
255*4882a593Smuzhiyun if (IS_ERR(rtc->rtc_dev))
256*4882a593Smuzhiyun return PTR_ERR(rtc->rtc_dev);
257*4882a593Smuzhiyun
258*4882a593Smuzhiyun pcf50633_register_irq(rtc->pcf, PCF50633_IRQ_ALARM,
259*4882a593Smuzhiyun pcf50633_rtc_irq, rtc);
260*4882a593Smuzhiyun return 0;
261*4882a593Smuzhiyun }
262*4882a593Smuzhiyun
pcf50633_rtc_remove(struct platform_device * pdev)263*4882a593Smuzhiyun static int pcf50633_rtc_remove(struct platform_device *pdev)
264*4882a593Smuzhiyun {
265*4882a593Smuzhiyun struct pcf50633_rtc *rtc;
266*4882a593Smuzhiyun
267*4882a593Smuzhiyun rtc = platform_get_drvdata(pdev);
268*4882a593Smuzhiyun pcf50633_free_irq(rtc->pcf, PCF50633_IRQ_ALARM);
269*4882a593Smuzhiyun
270*4882a593Smuzhiyun return 0;
271*4882a593Smuzhiyun }
272*4882a593Smuzhiyun
273*4882a593Smuzhiyun static struct platform_driver pcf50633_rtc_driver = {
274*4882a593Smuzhiyun .driver = {
275*4882a593Smuzhiyun .name = "pcf50633-rtc",
276*4882a593Smuzhiyun },
277*4882a593Smuzhiyun .probe = pcf50633_rtc_probe,
278*4882a593Smuzhiyun .remove = pcf50633_rtc_remove,
279*4882a593Smuzhiyun };
280*4882a593Smuzhiyun
281*4882a593Smuzhiyun module_platform_driver(pcf50633_rtc_driver);
282*4882a593Smuzhiyun
283*4882a593Smuzhiyun MODULE_DESCRIPTION("PCF50633 RTC driver");
284*4882a593Smuzhiyun MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
285*4882a593Smuzhiyun MODULE_LICENSE("GPL");
286*4882a593Smuzhiyun
287