aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorUwe Kleine-König <u.kleine-koenig@pengutronix.de>2009-12-15 19:46:09 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2009-12-16 10:20:00 -0500
commit43299f285937c907abcdd987c670c755194943cc (patch)
tree8ee8a8338531b393f3dfd7e1b911fbe0495a9306 /drivers
parent6f38b0436f7f0f0626d1f078edf4c38b0802b8f8 (diff)
rtc: add Freescale MC13783 RTC driver
This driver provides support for the RTC part integrated into the Freescale MC13783 PMIC and bases on patch created earlier by Sascha Hauer. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> Acked-by: Valentin Longchamp <valentin.longchamp@epfl.ch> Cc: Paul Gortmaker <p_gortmaker@yahoo.com> Signed-off-by: Alessandro Zummo <a.zummo@towertech.it> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/rtc/Kconfig6
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/rtc-mc13783.c262
3 files changed, 269 insertions, 0 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 71fbd6e8edf7..564f3d173b19 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -846,4 +846,10 @@ config RTC_DRV_PCAP
846 If you say Y here you will get support for the RTC found on 846 If you say Y here you will get support for the RTC found on
847 the PCAP2 ASIC used on some Motorola phones. 847 the PCAP2 ASIC used on some Motorola phones.
848 848
849config RTC_DRV_MC13783
850 depends on MFD_MC13783
851 tristate "Freescale MC13783 RTC"
852 help
853 This enables support for the Freescale MC13783 PMIC RTC
854
849endif # RTC_CLASS 855endif # RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 7da6efb3e953..6fa20f4ac3f1 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o
52obj-$(CONFIG_RTC_MXC) += rtc-mxc.o 52obj-$(CONFIG_RTC_MXC) += rtc-mxc.o
53obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o 53obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o
54obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o 54obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o
55obj-$(CONFIG_RTC_DRV_MC13783) += rtc-mc13783.o
55obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o 56obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o
56obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o 57obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o
57obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o 58obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o
diff --git a/drivers/rtc/rtc-mc13783.c b/drivers/rtc/rtc-mc13783.c
new file mode 100644
index 000000000000..850f983c039c
--- /dev/null
+++ b/drivers/rtc/rtc-mc13783.c
@@ -0,0 +1,262 @@
1/*
2 * Real Time Clock driver for Freescale MC13783 PMIC
3 *
4 * (C) 2009 Sascha Hauer, Pengutronix
5 * (C) 2009 Uwe Kleine-Koenig, Pengutronix
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#include <linux/mfd/mc13783.h>
13#include <linux/platform_device.h>
14#include <linux/kernel.h>
15#include <linux/module.h>
16#include <linux/rtc.h>
17
18#define DRIVER_NAME "mc13783-rtc"
19
20#define MC13783_RTCTOD 20
21#define MC13783_RTCTODA 21
22#define MC13783_RTCDAY 22
23#define MC13783_RTCDAYA 23
24
25struct mc13783_rtc {
26 struct rtc_device *rtc;
27 struct mc13783 *mc13783;
28 int valid;
29};
30
31static int mc13783_rtc_read_time(struct device *dev, struct rtc_time *tm)
32{
33 struct mc13783_rtc *priv = dev_get_drvdata(dev);
34 unsigned int seconds, days1, days2;
35 unsigned long s1970;
36 int ret;
37
38 mc13783_lock(priv->mc13783);
39
40 if (!priv->valid) {
41 ret = -ENODATA;
42 goto out;
43 }
44
45 ret = mc13783_reg_read(priv->mc13783, MC13783_RTCDAY, &days1);
46 if (unlikely(ret))
47 goto out;
48
49 ret = mc13783_reg_read(priv->mc13783, MC13783_RTCTOD, &seconds);
50 if (unlikely(ret))
51 goto out;
52
53 ret = mc13783_reg_read(priv->mc13783, MC13783_RTCDAY, &days2);
54out:
55 mc13783_unlock(priv->mc13783);
56
57 if (ret)
58 return ret;
59
60 if (days2 == days1 + 1) {
61 if (seconds >= 86400 / 2)
62 days2 = days1;
63 else
64 days1 = days2;
65 }
66
67 if (days1 != days2)
68 return -EIO;
69
70 s1970 = days1 * 86400 + seconds;
71
72 rtc_time_to_tm(s1970, tm);
73
74 return rtc_valid_tm(tm);
75}
76
77static int mc13783_rtc_set_mmss(struct device *dev, unsigned long secs)
78{
79 struct mc13783_rtc *priv = dev_get_drvdata(dev);
80 unsigned int seconds, days;
81 int ret;
82
83 seconds = secs % 86400;
84 days = secs / 86400;
85
86 mc13783_lock(priv->mc13783);
87
88 /*
89 * first write seconds=0 to prevent a day switch between writing days
90 * and seconds below
91 */
92 ret = mc13783_reg_write(priv->mc13783, MC13783_RTCTOD, 0);
93 if (unlikely(ret))
94 goto out;
95
96 ret = mc13783_reg_write(priv->mc13783, MC13783_RTCDAY, days);
97 if (unlikely(ret))
98 goto out;
99
100 ret = mc13783_reg_write(priv->mc13783, MC13783_RTCTOD, seconds);
101 if (unlikely(ret))
102 goto out;
103
104 ret = mc13783_ackirq(priv->mc13783, MC13783_IRQ_RTCRST);
105 if (unlikely(ret))
106 goto out;
107
108 ret = mc13783_unmask(priv->mc13783, MC13783_IRQ_RTCRST);
109out:
110 priv->valid = !ret;
111
112 mc13783_unlock(priv->mc13783);
113
114 return ret;
115}
116
117static irqreturn_t mc13783_rtc_update_handler(int irq, void *dev)
118{
119 struct mc13783_rtc *priv = dev;
120 struct mc13783 *mc13783 = priv->mc13783;
121
122 dev_dbg(&priv->rtc->dev, "1HZ\n");
123
124 rtc_update_irq(priv->rtc, 1, RTC_IRQF | RTC_UF);
125
126 mc13783_ackirq(mc13783, irq);
127
128 return IRQ_HANDLED;
129}
130
131static int mc13783_rtc_update_irq_enable(struct device *dev,
132 unsigned int enabled)
133{
134 struct mc13783_rtc *priv = dev_get_drvdata(dev);
135 int ret = -ENODATA;
136
137 mc13783_lock(priv->mc13783);
138 if (!priv->valid)
139 goto out;
140
141 ret = (enabled ? mc13783_unmask : mc13783_mask)(priv->mc13783,
142 MC13783_IRQ_1HZ);
143out:
144 mc13783_unlock(priv->mc13783);
145
146 return ret;
147}
148
149static const struct rtc_class_ops mc13783_rtc_ops = {
150 .read_time = mc13783_rtc_read_time,
151 .set_mmss = mc13783_rtc_set_mmss,
152 .update_irq_enable = mc13783_rtc_update_irq_enable,
153};
154
155static irqreturn_t mc13783_rtc_reset_handler(int irq, void *dev)
156{
157 struct mc13783_rtc *priv = dev;
158 struct mc13783 *mc13783 = priv->mc13783;
159
160 dev_dbg(&priv->rtc->dev, "RTCRST\n");
161 priv->valid = 0;
162
163 mc13783_mask(mc13783, irq);
164
165 return IRQ_HANDLED;
166}
167
168static int __devinit mc13783_rtc_probe(struct platform_device *pdev)
169{
170 int ret;
171 struct mc13783_rtc *priv;
172
173 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
174 if (!priv)
175 return -ENOMEM;
176
177 priv->mc13783 = dev_get_drvdata(pdev->dev.parent);
178 platform_set_drvdata(pdev, priv);
179
180 priv->valid = 1;
181
182 mc13783_lock(priv->mc13783);
183
184 ret = mc13783_irq_request(priv->mc13783, MC13783_IRQ_RTCRST,
185 mc13783_rtc_reset_handler, DRIVER_NAME, priv);
186 if (ret)
187 goto err_reset_irq_request;
188
189 ret = mc13783_irq_request_nounmask(priv->mc13783, MC13783_IRQ_1HZ,
190 mc13783_rtc_update_handler, DRIVER_NAME, priv);
191 if (ret)
192 goto err_update_irq_request;
193
194 mc13783_unlock(priv->mc13783);
195
196 priv->rtc = rtc_device_register(pdev->name,
197 &pdev->dev, &mc13783_rtc_ops, THIS_MODULE);
198
199 if (IS_ERR(priv->rtc)) {
200 ret = PTR_ERR(priv->rtc);
201
202 mc13783_lock(priv->mc13783);
203
204 mc13783_irq_free(priv->mc13783, MC13783_IRQ_1HZ, priv);
205err_update_irq_request:
206
207 mc13783_irq_free(priv->mc13783, MC13783_IRQ_RTCRST, priv);
208err_reset_irq_request:
209
210 mc13783_unlock(priv->mc13783);
211
212 platform_set_drvdata(pdev, NULL);
213 kfree(priv);
214 }
215
216 return ret;
217}
218
219static int __devexit mc13783_rtc_remove(struct platform_device *pdev)
220{
221 struct mc13783_rtc *priv = platform_get_drvdata(pdev);
222
223 rtc_device_unregister(priv->rtc);
224
225 mc13783_lock(priv->mc13783);
226
227 mc13783_irq_free(priv->mc13783, MC13783_IRQ_1HZ, priv);
228 mc13783_irq_free(priv->mc13783, MC13783_IRQ_RTCRST, priv);
229
230 mc13783_unlock(priv->mc13783);
231
232 platform_set_drvdata(pdev, NULL);
233
234 kfree(priv);
235
236 return 0;
237}
238
239static struct platform_driver mc13783_rtc_driver = {
240 .remove = __devexit_p(mc13783_rtc_remove),
241 .driver = {
242 .name = DRIVER_NAME,
243 .owner = THIS_MODULE,
244 },
245};
246
247static int __init mc13783_rtc_init(void)
248{
249 return platform_driver_probe(&mc13783_rtc_driver, &mc13783_rtc_probe);
250}
251module_init(mc13783_rtc_init);
252
253static void __exit mc13783_rtc_exit(void)
254{
255 platform_driver_unregister(&mc13783_rtc_driver);
256}
257module_exit(mc13783_rtc_exit);
258
259MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
260MODULE_DESCRIPTION("RTC driver for Freescale MC13783 PMIC");
261MODULE_LICENSE("GPL v2");
262MODULE_ALIAS("platform:" DRIVER_NAME);