aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastian Reichel <sre@kernel.org>2017-03-01 19:27:09 -0500
committerAlexandre Belloni <alexandre.belloni@free-electrons.com>2017-03-08 19:32:35 -0500
commitdd3bf50b35e3e111d6325207177b12af88aec824 (patch)
treed79c91ac695a01741c7e1dc8e0753f06ec7074a9
parent0522de00929e9e9ee51235fc40035179e4d45381 (diff)
rtc: cpcap: new rtc driver
This driver supports the Motorola CPCAP PMIC found on some of Motorola's mobile phones, such as the Droid 4. Tested-by: Tony Lindgren <tony@atomide.com> Signed-off-by: Sebastian Reichel <sre@kernel.org> Acked-by: Rob Herring <robh@kernel.org> Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
-rw-r--r--Documentation/devicetree/bindings/rtc/cpcap-rtc.txt18
-rw-r--r--drivers/rtc/Kconfig7
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/rtc-cpcap.c332
4 files changed, 358 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/rtc/cpcap-rtc.txt b/Documentation/devicetree/bindings/rtc/cpcap-rtc.txt
new file mode 100644
index 000000000000..45750ff3112d
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/cpcap-rtc.txt
@@ -0,0 +1,18 @@
1Motorola CPCAP PMIC RTC
2-----------------------
3
4This module is part of the CPCAP. For more details about the whole
5chip see Documentation/devicetree/bindings/mfd/motorola-cpcap.txt.
6
7Requires node properties:
8- compatible: should contain "motorola,cpcap-rtc"
9- interrupts: An interrupt specifier for alarm and 1 Hz irq
10
11Example:
12
13&cpcap {
14 cpcap_rtc: rtc {
15 compatible = "motorola,cpcap-rtc";
16 interrupts = <39 IRQ_TYPE_NONE>, <26 IRQ_TYPE_NONE>;
17 };
18};
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index ee1b0e9dde79..050bec749fae 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1731,6 +1731,13 @@ config RTC_DRV_STM32
1731 This driver can also be built as a module, if so, the module 1731 This driver can also be built as a module, if so, the module
1732 will be called "rtc-stm32". 1732 will be called "rtc-stm32".
1733 1733
1734config RTC_DRV_CPCAP
1735 depends on MFD_CPCAP
1736 tristate "Motorola CPCAP RTC"
1737 help
1738 Say y here for CPCAP rtc found on some Motorola phones
1739 and tablets such as Droid 4.
1740
1734comment "HID Sensor RTC drivers" 1741comment "HID Sensor RTC drivers"
1735 1742
1736config RTC_DRV_HID_SENSOR_TIME 1743config RTC_DRV_HID_SENSOR_TIME
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index f07297b1460a..13857d2fce09 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_RTC_DRV_BQ32K) += rtc-bq32k.o
40obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o 40obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o
41obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o 41obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o
42obj-$(CONFIG_RTC_DRV_COH901331) += rtc-coh901331.o 42obj-$(CONFIG_RTC_DRV_COH901331) += rtc-coh901331.o
43obj-$(CONFIG_RTC_DRV_CPCAP) += rtc-cpcap.o
43obj-$(CONFIG_RTC_DRV_DA9052) += rtc-da9052.o 44obj-$(CONFIG_RTC_DRV_DA9052) += rtc-da9052.o
44obj-$(CONFIG_RTC_DRV_DA9055) += rtc-da9055.o 45obj-$(CONFIG_RTC_DRV_DA9055) += rtc-da9055.o
45obj-$(CONFIG_RTC_DRV_DA9063) += rtc-da9063.o 46obj-$(CONFIG_RTC_DRV_DA9063) += rtc-da9063.o
diff --git a/drivers/rtc/rtc-cpcap.c b/drivers/rtc/rtc-cpcap.c
new file mode 100644
index 000000000000..7c6a3c3167bd
--- /dev/null
+++ b/drivers/rtc/rtc-cpcap.c
@@ -0,0 +1,332 @@
1/*
2 * Motorola CPCAP PMIC RTC driver
3 *
4 * Based on cpcap-regulator.c from Motorola Linux kernel tree
5 * Copyright (C) 2009 Motorola, Inc.
6 *
7 * Rewritten for mainline kernel
8 * - use DT
9 * - use regmap
10 * - use standard interrupt framework
11 * - use managed device resources
12 * - remove custom "secure clock daemon" helpers
13 *
14 * Copyright (C) 2017 Sebastian Reichel <sre@kernel.org>
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License version 2 as
18 * published by the Free Software Foundation.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 */
25#include <linux/kernel.h>
26#include <linux/module.h>
27#include <linux/init.h>
28#include <linux/device.h>
29#include <linux/platform_device.h>
30#include <linux/rtc.h>
31#include <linux/err.h>
32#include <linux/regmap.h>
33#include <linux/mfd/motorola-cpcap.h>
34#include <linux/slab.h>
35#include <linux/sched.h>
36
37#define SECS_PER_DAY 86400
38#define DAY_MASK 0x7FFF
39#define TOD1_MASK 0x00FF
40#define TOD2_MASK 0x01FF
41
42struct cpcap_time {
43 int day;
44 int tod1;
45 int tod2;
46};
47
48struct cpcap_rtc {
49 struct regmap *regmap;
50 struct rtc_device *rtc_dev;
51 u16 vendor;
52 int alarm_irq;
53 bool alarm_enabled;
54 int update_irq;
55 bool update_enabled;
56};
57
58static void cpcap2rtc_time(struct rtc_time *rtc, struct cpcap_time *cpcap)
59{
60 unsigned long int tod;
61 unsigned long int time;
62
63 tod = (cpcap->tod1 & TOD1_MASK) | ((cpcap->tod2 & TOD2_MASK) << 8);
64 time = tod + ((cpcap->day & DAY_MASK) * SECS_PER_DAY);
65
66 rtc_time_to_tm(time, rtc);
67}
68
69static void rtc2cpcap_time(struct cpcap_time *cpcap, struct rtc_time *rtc)
70{
71 unsigned long time;
72
73 rtc_tm_to_time(rtc, &time);
74
75 cpcap->day = time / SECS_PER_DAY;
76 time %= SECS_PER_DAY;
77 cpcap->tod2 = (time >> 8) & TOD2_MASK;
78 cpcap->tod1 = time & TOD1_MASK;
79}
80
81static int cpcap_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
82{
83 struct cpcap_rtc *rtc = dev_get_drvdata(dev);
84
85 if (rtc->alarm_enabled == enabled)
86 return 0;
87
88 if (enabled)
89 enable_irq(rtc->alarm_irq);
90 else
91 disable_irq(rtc->alarm_irq);
92
93 rtc->alarm_enabled = !!enabled;
94
95 return 0;
96}
97
98static int cpcap_rtc_read_time(struct device *dev, struct rtc_time *tm)
99{
100 struct cpcap_rtc *rtc;
101 struct cpcap_time cpcap_tm;
102 int temp_tod2;
103 int ret;
104
105 rtc = dev_get_drvdata(dev);
106
107 ret = regmap_read(rtc->regmap, CPCAP_REG_TOD2, &temp_tod2);
108 ret |= regmap_read(rtc->regmap, CPCAP_REG_DAY, &cpcap_tm.day);
109 ret |= regmap_read(rtc->regmap, CPCAP_REG_TOD1, &cpcap_tm.tod1);
110 ret |= regmap_read(rtc->regmap, CPCAP_REG_TOD2, &cpcap_tm.tod2);
111
112 if (temp_tod2 > cpcap_tm.tod2)
113 ret |= regmap_read(rtc->regmap, CPCAP_REG_DAY, &cpcap_tm.day);
114
115 if (ret) {
116 dev_err(dev, "Failed to read time\n");
117 return -EIO;
118 }
119
120 cpcap2rtc_time(tm, &cpcap_tm);
121
122 return rtc_valid_tm(tm);
123}
124
125static int cpcap_rtc_set_time(struct device *dev, struct rtc_time *tm)
126{
127 struct cpcap_rtc *rtc;
128 struct cpcap_time cpcap_tm;
129 int ret = 0;
130
131 rtc = dev_get_drvdata(dev);
132
133 rtc2cpcap_time(&cpcap_tm, tm);
134
135 if (rtc->alarm_enabled)
136 disable_irq(rtc->alarm_irq);
137 if (rtc->update_enabled)
138 disable_irq(rtc->update_irq);
139
140 if (rtc->vendor == CPCAP_VENDOR_ST) {
141 /* The TOD1 and TOD2 registers MUST be written in this order
142 * for the change to properly set.
143 */
144 ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD1,
145 TOD1_MASK, cpcap_tm.tod1);
146 ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD2,
147 TOD2_MASK, cpcap_tm.tod2);
148 ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_DAY,
149 DAY_MASK, cpcap_tm.day);
150 } else {
151 /* Clearing the upper lower 8 bits of the TOD guarantees that
152 * the upper half of TOD (TOD2) will not increment for 0xFF RTC
153 * ticks (255 seconds). During this time we can safely write
154 * to DAY, TOD2, then TOD1 (in that order) and expect RTC to be
155 * synchronized to the exact time requested upon the final write
156 * to TOD1.
157 */
158 ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD1,
159 TOD1_MASK, 0);
160 ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_DAY,
161 DAY_MASK, cpcap_tm.day);
162 ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD2,
163 TOD2_MASK, cpcap_tm.tod2);
164 ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD1,
165 TOD1_MASK, cpcap_tm.tod1);
166 }
167
168 if (rtc->update_enabled)
169 enable_irq(rtc->update_irq);
170 if (rtc->alarm_enabled)
171 enable_irq(rtc->alarm_irq);
172
173 return ret;
174}
175
176static int cpcap_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
177{
178 struct cpcap_rtc *rtc;
179 struct cpcap_time cpcap_tm;
180 int ret;
181
182 rtc = dev_get_drvdata(dev);
183
184 alrm->enabled = rtc->alarm_enabled;
185
186 ret = regmap_read(rtc->regmap, CPCAP_REG_DAYA, &cpcap_tm.day);
187 ret |= regmap_read(rtc->regmap, CPCAP_REG_TODA2, &cpcap_tm.tod2);
188 ret |= regmap_read(rtc->regmap, CPCAP_REG_TODA1, &cpcap_tm.tod1);
189
190 if (ret) {
191 dev_err(dev, "Failed to read time\n");
192 return -EIO;
193 }
194
195 cpcap2rtc_time(&alrm->time, &cpcap_tm);
196 return rtc_valid_tm(&alrm->time);
197}
198
199static int cpcap_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
200{
201 struct cpcap_rtc *rtc;
202 struct cpcap_time cpcap_tm;
203 int ret;
204
205 rtc = dev_get_drvdata(dev);
206
207 rtc2cpcap_time(&cpcap_tm, &alrm->time);
208
209 if (rtc->alarm_enabled)
210 disable_irq(rtc->alarm_irq);
211
212 ret = regmap_update_bits(rtc->regmap, CPCAP_REG_DAYA, DAY_MASK,
213 cpcap_tm.day);
214 ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TODA2, TOD2_MASK,
215 cpcap_tm.tod2);
216 ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TODA1, TOD1_MASK,
217 cpcap_tm.tod1);
218
219 if (!ret) {
220 enable_irq(rtc->alarm_irq);
221 rtc->alarm_enabled = true;
222 }
223
224 return ret;
225}
226
227static const struct rtc_class_ops cpcap_rtc_ops = {
228 .read_time = cpcap_rtc_read_time,
229 .set_time = cpcap_rtc_set_time,
230 .read_alarm = cpcap_rtc_read_alarm,
231 .set_alarm = cpcap_rtc_set_alarm,
232 .alarm_irq_enable = cpcap_rtc_alarm_irq_enable,
233};
234
235static irqreturn_t cpcap_rtc_alarm_irq(int irq, void *data)
236{
237 struct cpcap_rtc *rtc = data;
238
239 rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF);
240 return IRQ_HANDLED;
241}
242
243static irqreturn_t cpcap_rtc_update_irq(int irq, void *data)
244{
245 struct cpcap_rtc *rtc = data;
246
247 rtc_update_irq(rtc->rtc_dev, 1, RTC_UF | RTC_IRQF);
248 return IRQ_HANDLED;
249}
250
251static int cpcap_rtc_probe(struct platform_device *pdev)
252{
253 struct device *dev = &pdev->dev;
254 struct cpcap_rtc *rtc;
255 int err;
256
257 rtc = devm_kzalloc(dev, sizeof(*rtc), GFP_KERNEL);
258 if (!rtc)
259 return -ENOMEM;
260
261 rtc->regmap = dev_get_regmap(dev->parent, NULL);
262 if (!rtc->regmap)
263 return -ENODEV;
264
265 platform_set_drvdata(pdev, rtc);
266 rtc->rtc_dev = devm_rtc_device_register(dev, "cpcap_rtc",
267 &cpcap_rtc_ops, THIS_MODULE);
268
269 if (IS_ERR(rtc->rtc_dev)) {
270 kfree(rtc);
271 return PTR_ERR(rtc->rtc_dev);
272 }
273
274 err = cpcap_get_vendor(dev, rtc->regmap, &rtc->vendor);
275 if (err)
276 return err;
277
278 rtc->alarm_irq = platform_get_irq(pdev, 0);
279 err = devm_request_threaded_irq(dev, rtc->alarm_irq, NULL,
280 cpcap_rtc_alarm_irq, IRQ_NONE,
281 "rtc_alarm", rtc);
282 if (err) {
283 dev_err(dev, "Could not request alarm irq: %d\n", err);
284 return err;
285 }
286 disable_irq(rtc->alarm_irq);
287
288 /* Stock Android uses the 1 Hz interrupt for "secure clock daemon",
289 * which is not supported by the mainline kernel. The mainline kernel
290 * does not use the irq at the moment, but we explicitly request and
291 * disable it, so that its masked and does not wake up the processor
292 * every second.
293 */
294 rtc->update_irq = platform_get_irq(pdev, 1);
295 err = devm_request_threaded_irq(dev, rtc->update_irq, NULL,
296 cpcap_rtc_update_irq, IRQ_NONE,
297 "rtc_1hz", rtc);
298 if (err) {
299 dev_err(dev, "Could not request update irq: %d\n", err);
300 return err;
301 }
302 disable_irq(rtc->update_irq);
303
304 err = device_init_wakeup(dev, 1);
305 if (err) {
306 dev_err(dev, "wakeup initialization failed (%d)\n", err);
307 /* ignore error and continue without wakeup support */
308 }
309
310 return 0;
311}
312
313static const struct of_device_id cpcap_rtc_of_match[] = {
314 { .compatible = "motorola,cpcap-rtc", },
315 {},
316};
317MODULE_DEVICE_TABLE(of, cpcap_rtc_of_match);
318
319static struct platform_driver cpcap_rtc_driver = {
320 .probe = cpcap_rtc_probe,
321 .driver = {
322 .name = "cpcap-rtc",
323 .of_match_table = cpcap_rtc_of_match,
324 },
325};
326
327module_platform_driver(cpcap_rtc_driver);
328
329MODULE_ALIAS("platform:cpcap-rtc");
330MODULE_DESCRIPTION("CPCAP RTC driver");
331MODULE_AUTHOR("Sebastian Reichel <sre@kernel.org>");
332MODULE_LICENSE("GPL");