aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/rtc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/rtc')
-rw-r--r--drivers/rtc/Kconfig10
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/rtc-palmas.c339
3 files changed, 350 insertions, 0 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index e6ab071fb6fd..79fbe3832dfc 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -305,6 +305,16 @@ config RTC_DRV_X1205
305 This driver can also be built as a module. If so, the module 305 This driver can also be built as a module. If so, the module
306 will be called rtc-x1205. 306 will be called rtc-x1205.
307 307
308config RTC_DRV_PALMAS
309 tristate "TI Palmas RTC driver"
310 depends on MFD_PALMAS
311 help
312 If you say yes here you get support for the RTC of TI PALMA series PMIC
313 chips.
314
315 This driver can also be built as a module. If so, the module
316 will be called rtc-palma.
317
308config RTC_DRV_PCF8523 318config RTC_DRV_PCF8523
309 tristate "NXP PCF8523" 319 tristate "NXP PCF8523"
310 help 320 help
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index e8f2e2fee06f..c33f86f1a69b 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -81,6 +81,7 @@ obj-$(CONFIG_RTC_DRV_MPC5121) += rtc-mpc5121.o
81obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o 81obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o
82obj-$(CONFIG_RTC_DRV_NUC900) += rtc-nuc900.o 82obj-$(CONFIG_RTC_DRV_NUC900) += rtc-nuc900.o
83obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o 83obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o
84obj-$(CONFIG_RTC_DRV_PALMAS) += rtc-palmas.o
84obj-$(CONFIG_RTC_DRV_PCAP) += rtc-pcap.o 85obj-$(CONFIG_RTC_DRV_PCAP) += rtc-pcap.o
85obj-$(CONFIG_RTC_DRV_PCF8523) += rtc-pcf8523.o 86obj-$(CONFIG_RTC_DRV_PCF8523) += rtc-pcf8523.o
86obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o 87obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o
diff --git a/drivers/rtc/rtc-palmas.c b/drivers/rtc/rtc-palmas.c
new file mode 100644
index 000000000000..59c42986254e
--- /dev/null
+++ b/drivers/rtc/rtc-palmas.c
@@ -0,0 +1,339 @@
1/*
2 * rtc-palmas.c -- Palmas Real Time Clock driver.
3
4 * RTC driver for TI Palma series devices like TPS65913,
5 * TPS65914 power management IC.
6 *
7 * Copyright (c) 2012, NVIDIA Corporation.
8 *
9 * Author: Laxman Dewangan <ldewangan@nvidia.com>
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License as
13 * published by the Free Software Foundation version 2.
14 *
15 * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
16 * whether express or implied; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
23 * 02111-1307, USA
24 */
25
26#include <linux/bcd.h>
27#include <linux/errno.h>
28#include <linux/init.h>
29#include <linux/interrupt.h>
30#include <linux/kernel.h>
31#include <linux/mfd/palmas.h>
32#include <linux/module.h>
33#include <linux/rtc.h>
34#include <linux/types.h>
35#include <linux/platform_device.h>
36#include <linux/pm.h>
37
38struct palmas_rtc {
39 struct rtc_device *rtc;
40 struct device *dev;
41 unsigned int irq;
42};
43
44/* Total number of RTC registers needed to set time*/
45#define PALMAS_NUM_TIME_REGS (PALMAS_YEARS_REG - PALMAS_SECONDS_REG + 1)
46
47static int palmas_rtc_read_time(struct device *dev, struct rtc_time *tm)
48{
49 unsigned char rtc_data[PALMAS_NUM_TIME_REGS];
50 struct palmas *palmas = dev_get_drvdata(dev->parent);
51 int ret;
52
53 /* Copy RTC counting registers to static registers or latches */
54 ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG,
55 PALMAS_RTC_CTRL_REG_GET_TIME, PALMAS_RTC_CTRL_REG_GET_TIME);
56 if (ret < 0) {
57 dev_err(dev, "RTC CTRL reg update failed, err: %d\n", ret);
58 return ret;
59 }
60
61 ret = palmas_bulk_read(palmas, PALMAS_RTC_BASE, PALMAS_SECONDS_REG,
62 rtc_data, PALMAS_NUM_TIME_REGS);
63 if (ret < 0) {
64 dev_err(dev, "RTC_SECONDS reg read failed, err = %d\n", ret);
65 return ret;
66 }
67
68 tm->tm_sec = bcd2bin(rtc_data[0]);
69 tm->tm_min = bcd2bin(rtc_data[1]);
70 tm->tm_hour = bcd2bin(rtc_data[2]);
71 tm->tm_mday = bcd2bin(rtc_data[3]);
72 tm->tm_mon = bcd2bin(rtc_data[4]) - 1;
73 tm->tm_year = bcd2bin(rtc_data[5]) + 100;
74
75 return ret;
76}
77
78static int palmas_rtc_set_time(struct device *dev, struct rtc_time *tm)
79{
80 unsigned char rtc_data[PALMAS_NUM_TIME_REGS];
81 struct palmas *palmas = dev_get_drvdata(dev->parent);
82 int ret;
83
84 rtc_data[0] = bin2bcd(tm->tm_sec);
85 rtc_data[1] = bin2bcd(tm->tm_min);
86 rtc_data[2] = bin2bcd(tm->tm_hour);
87 rtc_data[3] = bin2bcd(tm->tm_mday);
88 rtc_data[4] = bin2bcd(tm->tm_mon + 1);
89 rtc_data[5] = bin2bcd(tm->tm_year - 100);
90
91 /* Stop RTC while updating the RTC time registers */
92 ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG,
93 PALMAS_RTC_CTRL_REG_STOP_RTC, 0);
94 if (ret < 0) {
95 dev_err(dev, "RTC stop failed, err = %d\n", ret);
96 return ret;
97 }
98
99 ret = palmas_bulk_write(palmas, PALMAS_RTC_BASE, PALMAS_SECONDS_REG,
100 rtc_data, PALMAS_NUM_TIME_REGS);
101 if (ret < 0) {
102 dev_err(dev, "RTC_SECONDS reg write failed, err = %d\n", ret);
103 return ret;
104 }
105
106 /* Start back RTC */
107 ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG,
108 PALMAS_RTC_CTRL_REG_STOP_RTC, PALMAS_RTC_CTRL_REG_STOP_RTC);
109 if (ret < 0)
110 dev_err(dev, "RTC start failed, err = %d\n", ret);
111 return ret;
112}
113
114static int palmas_rtc_alarm_irq_enable(struct device *dev, unsigned enabled)
115{
116 struct palmas *palmas = dev_get_drvdata(dev->parent);
117 u8 val;
118
119 val = enabled ? PALMAS_RTC_INTERRUPTS_REG_IT_ALARM : 0;
120 return palmas_write(palmas, PALMAS_RTC_BASE,
121 PALMAS_RTC_INTERRUPTS_REG, val);
122}
123
124static int palmas_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
125{
126 unsigned char alarm_data[PALMAS_NUM_TIME_REGS];
127 u32 int_val;
128 struct palmas *palmas = dev_get_drvdata(dev->parent);
129 int ret;
130
131 ret = palmas_bulk_read(palmas, PALMAS_RTC_BASE,
132 PALMAS_ALARM_SECONDS_REG,
133 alarm_data, PALMAS_NUM_TIME_REGS);
134 if (ret < 0) {
135 dev_err(dev, "RTC_ALARM_SECONDS read failed, err = %d\n", ret);
136 return ret;
137 }
138
139 alm->time.tm_sec = bcd2bin(alarm_data[0]);
140 alm->time.tm_min = bcd2bin(alarm_data[1]);
141 alm->time.tm_hour = bcd2bin(alarm_data[2]);
142 alm->time.tm_mday = bcd2bin(alarm_data[3]);
143 alm->time.tm_mon = bcd2bin(alarm_data[4]) - 1;
144 alm->time.tm_year = bcd2bin(alarm_data[5]) + 100;
145
146 ret = palmas_read(palmas, PALMAS_RTC_BASE, PALMAS_RTC_INTERRUPTS_REG,
147 &int_val);
148 if (ret < 0) {
149 dev_err(dev, "RTC_INTERRUPTS reg read failed, err = %d\n", ret);
150 return ret;
151 }
152
153 if (int_val & PALMAS_RTC_INTERRUPTS_REG_IT_ALARM)
154 alm->enabled = 1;
155 return ret;
156}
157
158static int palmas_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
159{
160 unsigned char alarm_data[PALMAS_NUM_TIME_REGS];
161 struct palmas *palmas = dev_get_drvdata(dev->parent);
162 int ret;
163
164 ret = palmas_rtc_alarm_irq_enable(dev, 0);
165 if (ret < 0) {
166 dev_err(dev, "Disable RTC alarm failed\n");
167 return ret;
168 }
169
170 alarm_data[0] = bin2bcd(alm->time.tm_sec);
171 alarm_data[1] = bin2bcd(alm->time.tm_min);
172 alarm_data[2] = bin2bcd(alm->time.tm_hour);
173 alarm_data[3] = bin2bcd(alm->time.tm_mday);
174 alarm_data[4] = bin2bcd(alm->time.tm_mon + 1);
175 alarm_data[5] = bin2bcd(alm->time.tm_year - 100);
176
177 ret = palmas_bulk_write(palmas, PALMAS_RTC_BASE,
178 PALMAS_ALARM_SECONDS_REG, alarm_data, PALMAS_NUM_TIME_REGS);
179 if (ret < 0) {
180 dev_err(dev, "ALARM_SECONDS_REG write failed, err = %d\n", ret);
181 return ret;
182 }
183
184 if (alm->enabled)
185 ret = palmas_rtc_alarm_irq_enable(dev, 1);
186 return ret;
187}
188
189static int palmas_clear_interrupts(struct device *dev)
190{
191 struct palmas *palmas = dev_get_drvdata(dev->parent);
192 unsigned int rtc_reg;
193 int ret;
194
195 ret = palmas_read(palmas, PALMAS_RTC_BASE, PALMAS_RTC_STATUS_REG,
196 &rtc_reg);
197 if (ret < 0) {
198 dev_err(dev, "RTC_STATUS read failed, err = %d\n", ret);
199 return ret;
200 }
201
202 ret = palmas_write(palmas, PALMAS_RTC_BASE, PALMAS_RTC_STATUS_REG,
203 rtc_reg);
204 if (ret < 0) {
205 dev_err(dev, "RTC_STATUS write failed, err = %d\n", ret);
206 return ret;
207 }
208 return 0;
209}
210
211static irqreturn_t palmas_rtc_interrupt(int irq, void *context)
212{
213 struct palmas_rtc *palmas_rtc = context;
214 struct device *dev = palmas_rtc->dev;
215 int ret;
216
217 ret = palmas_clear_interrupts(dev);
218 if (ret < 0) {
219 dev_err(dev, "RTC interrupt clear failed, err = %d\n", ret);
220 return IRQ_NONE;
221 }
222
223 rtc_update_irq(palmas_rtc->rtc, 1, RTC_IRQF | RTC_AF);
224 return IRQ_HANDLED;
225}
226
227static struct rtc_class_ops palmas_rtc_ops = {
228 .read_time = palmas_rtc_read_time,
229 .set_time = palmas_rtc_set_time,
230 .read_alarm = palmas_rtc_read_alarm,
231 .set_alarm = palmas_rtc_set_alarm,
232 .alarm_irq_enable = palmas_rtc_alarm_irq_enable,
233};
234
235static int palmas_rtc_probe(struct platform_device *pdev)
236{
237 struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
238 struct palmas_rtc *palmas_rtc = NULL;
239 int ret;
240
241 palmas_rtc = devm_kzalloc(&pdev->dev, sizeof(struct palmas_rtc),
242 GFP_KERNEL);
243 if (!palmas_rtc)
244 return -ENOMEM;
245
246 /* Clear pending interrupts */
247 ret = palmas_clear_interrupts(&pdev->dev);
248 if (ret < 0) {
249 dev_err(&pdev->dev, "clear RTC int failed, err = %d\n", ret);
250 return ret;
251 }
252
253 palmas_rtc->dev = &pdev->dev;
254 platform_set_drvdata(pdev, palmas_rtc);
255
256 /* Start RTC */
257 ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG,
258 PALMAS_RTC_CTRL_REG_STOP_RTC,
259 PALMAS_RTC_CTRL_REG_STOP_RTC);
260 if (ret < 0) {
261 dev_err(&pdev->dev, "RTC_CTRL write failed, err = %d\n", ret);
262 return ret;
263 }
264
265 palmas_rtc->irq = platform_get_irq(pdev, 0);
266
267 palmas_rtc->rtc = rtc_device_register(pdev->name, &pdev->dev,
268 &palmas_rtc_ops, THIS_MODULE);
269 if (IS_ERR(palmas_rtc->rtc)) {
270 ret = PTR_ERR(palmas_rtc->rtc);
271 dev_err(&pdev->dev, "RTC register failed, err = %d\n", ret);
272 return ret;
273 }
274
275 ret = request_threaded_irq(palmas_rtc->irq, NULL,
276 palmas_rtc_interrupt,
277 IRQF_TRIGGER_LOW | IRQF_ONESHOT |
278 IRQF_EARLY_RESUME,
279 dev_name(&pdev->dev), palmas_rtc);
280 if (ret < 0) {
281 dev_err(&pdev->dev, "IRQ request failed, err = %d\n", ret);
282 rtc_device_unregister(palmas_rtc->rtc);
283 return ret;
284 }
285
286 device_set_wakeup_capable(&pdev->dev, 1);
287 return 0;
288}
289
290static int palmas_rtc_remove(struct platform_device *pdev)
291{
292 struct palmas_rtc *palmas_rtc = platform_get_drvdata(pdev);
293
294 palmas_rtc_alarm_irq_enable(&pdev->dev, 0);
295 free_irq(palmas_rtc->irq, palmas_rtc);
296 rtc_device_unregister(palmas_rtc->rtc);
297 return 0;
298}
299
300#ifdef CONFIG_PM_SLEEP
301static int palmas_rtc_suspend(struct device *dev)
302{
303 struct palmas_rtc *palmas_rtc = dev_get_drvdata(dev);
304
305 if (device_may_wakeup(dev))
306 enable_irq_wake(palmas_rtc->irq);
307 return 0;
308}
309
310static int palmas_rtc_resume(struct device *dev)
311{
312 struct palmas_rtc *palmas_rtc = dev_get_drvdata(dev);
313
314 if (device_may_wakeup(dev))
315 disable_irq_wake(palmas_rtc->irq);
316 return 0;
317}
318#endif
319
320static const struct dev_pm_ops palmas_rtc_pm_ops = {
321 SET_SYSTEM_SLEEP_PM_OPS(palmas_rtc_suspend, palmas_rtc_resume)
322};
323
324static struct platform_driver palmas_rtc_driver = {
325 .probe = palmas_rtc_probe,
326 .remove = palmas_rtc_remove,
327 .driver = {
328 .owner = THIS_MODULE,
329 .name = "palmas-rtc",
330 .pm = &palmas_rtc_pm_ops,
331 },
332};
333
334module_platform_driver(palmas_rtc_driver);
335
336MODULE_ALIAS("platform:palmas_rtc");
337MODULE_DESCRIPTION("TI PALMAS series RTC driver");
338MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
339MODULE_LICENSE("GPL v2");