aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/rtc
diff options
context:
space:
mode:
authorThierry Reding <thierry.reding@avionic-design.de>2012-12-17 19:02:44 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2012-12-17 20:15:21 -0500
commitf803f0d079ded4272d7a1c9813bfd24c58b8ee5f (patch)
treecb22034028237607224ad5c1360ecfa144076992 /drivers/rtc
parentf288cf416ec22de3c5bc18148b6f7620aa32409b (diff)
rtc: add NXP PCF8523 support
Add an RTC driver for PCF8523 chips by NXP Semiconductors. No support is currently provided for the alarm and interrupt functions. Only the time and date functionality is implemented. Signed-off-by: Thierry Reding <thierry.reding@avionic-design.de> Cc: 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/rtc')
-rw-r--r--drivers/rtc/Kconfig9
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/rtc-pcf8523.c326
3 files changed, 336 insertions, 0 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 608a7b1794e3..5bb031461d75 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -269,6 +269,15 @@ config RTC_DRV_X1205
269 This driver can also be built as a module. If so, the module 269 This driver can also be built as a module. If so, the module
270 will be called rtc-x1205. 270 will be called rtc-x1205.
271 271
272config RTC_DRV_PCF8523
273 tristate "NXP PCF8523"
274 help
275 If you say yes here you get support for the NXP PCF8523 RTC
276 chips.
277
278 This driver can also be built as a module. If so, the module
279 will be called rtc-pcf8523.
280
272config RTC_DRV_PCF8563 281config RTC_DRV_PCF8563
273 tristate "Philips PCF8563/Epson RTC8564" 282 tristate "Philips PCF8563/Epson RTC8564"
274 help 283 help
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 56297f0fd388..59e2132a1c14 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -76,6 +76,7 @@ obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o
76obj-$(CONFIG_RTC_DRV_NUC900) += rtc-nuc900.o 76obj-$(CONFIG_RTC_DRV_NUC900) += rtc-nuc900.o
77obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o 77obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o
78obj-$(CONFIG_RTC_DRV_PCAP) += rtc-pcap.o 78obj-$(CONFIG_RTC_DRV_PCAP) += rtc-pcap.o
79obj-$(CONFIG_RTC_DRV_PCF8523) += rtc-pcf8523.o
79obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o 80obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o
80obj-$(CONFIG_RTC_DRV_PCF8583) += rtc-pcf8583.o 81obj-$(CONFIG_RTC_DRV_PCF8583) += rtc-pcf8583.o
81obj-$(CONFIG_RTC_DRV_PCF2123) += rtc-pcf2123.o 82obj-$(CONFIG_RTC_DRV_PCF2123) += rtc-pcf2123.o
diff --git a/drivers/rtc/rtc-pcf8523.c b/drivers/rtc/rtc-pcf8523.c
new file mode 100644
index 000000000000..be05a645f99e
--- /dev/null
+++ b/drivers/rtc/rtc-pcf8523.c
@@ -0,0 +1,326 @@
1/*
2 * Copyright (C) 2012 Avionic Design GmbH
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8
9#include <linux/bcd.h>
10#include <linux/i2c.h>
11#include <linux/module.h>
12#include <linux/rtc.h>
13#include <linux/of.h>
14
15#define DRIVER_NAME "rtc-pcf8523"
16
17#define REG_CONTROL1 0x00
18#define REG_CONTROL1_CAP_SEL (1 << 7)
19#define REG_CONTROL1_STOP (1 << 5)
20
21#define REG_CONTROL3 0x02
22#define REG_CONTROL3_PM_BLD (1 << 7) /* battery low detection disabled */
23#define REG_CONTROL3_PM_VDD (1 << 6) /* switch-over disabled */
24#define REG_CONTROL3_PM_DSM (1 << 5) /* direct switching mode */
25#define REG_CONTROL3_PM_MASK 0xe0
26
27#define REG_SECONDS 0x03
28#define REG_SECONDS_OS (1 << 7)
29
30#define REG_MINUTES 0x04
31#define REG_HOURS 0x05
32#define REG_DAYS 0x06
33#define REG_WEEKDAYS 0x07
34#define REG_MONTHS 0x08
35#define REG_YEARS 0x09
36
37struct pcf8523 {
38 struct rtc_device *rtc;
39};
40
41static int pcf8523_read(struct i2c_client *client, u8 reg, u8 *valuep)
42{
43 struct i2c_msg msgs[2];
44 u8 value = 0;
45 int err;
46
47 msgs[0].addr = client->addr;
48 msgs[0].flags = 0;
49 msgs[0].len = sizeof(reg);
50 msgs[0].buf = &reg;
51
52 msgs[1].addr = client->addr;
53 msgs[1].flags = I2C_M_RD;
54 msgs[1].len = sizeof(value);
55 msgs[1].buf = &value;
56
57 err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
58 if (err < 0)
59 return err;
60
61 *valuep = value;
62
63 return 0;
64}
65
66static int pcf8523_write(struct i2c_client *client, u8 reg, u8 value)
67{
68 u8 buffer[2] = { reg, value };
69 struct i2c_msg msg;
70 int err;
71
72 msg.addr = client->addr;
73 msg.flags = 0;
74 msg.len = sizeof(buffer);
75 msg.buf = buffer;
76
77 err = i2c_transfer(client->adapter, &msg, 1);
78 if (err < 0)
79 return err;
80
81 return 0;
82}
83
84static int pcf8523_select_capacitance(struct i2c_client *client, bool high)
85{
86 u8 value;
87 int err;
88
89 err = pcf8523_read(client, REG_CONTROL1, &value);
90 if (err < 0)
91 return err;
92
93 if (!high)
94 value &= ~REG_CONTROL1_CAP_SEL;
95 else
96 value |= REG_CONTROL1_CAP_SEL;
97
98 err = pcf8523_write(client, REG_CONTROL1, value);
99 if (err < 0)
100 return err;
101
102 return err;
103}
104
105static int pcf8523_set_pm(struct i2c_client *client, u8 pm)
106{
107 u8 value;
108 int err;
109
110 err = pcf8523_read(client, REG_CONTROL3, &value);
111 if (err < 0)
112 return err;
113
114 value = (value & ~REG_CONTROL3_PM_MASK) | pm;
115
116 err = pcf8523_write(client, REG_CONTROL3, value);
117 if (err < 0)
118 return err;
119
120 return 0;
121}
122
123static int pcf8523_stop_rtc(struct i2c_client *client)
124{
125 u8 value;
126 int err;
127
128 err = pcf8523_read(client, REG_CONTROL1, &value);
129 if (err < 0)
130 return err;
131
132 value |= REG_CONTROL1_STOP;
133
134 err = pcf8523_write(client, REG_CONTROL1, value);
135 if (err < 0)
136 return err;
137
138 return 0;
139}
140
141static int pcf8523_start_rtc(struct i2c_client *client)
142{
143 u8 value;
144 int err;
145
146 err = pcf8523_read(client, REG_CONTROL1, &value);
147 if (err < 0)
148 return err;
149
150 value &= ~REG_CONTROL1_STOP;
151
152 err = pcf8523_write(client, REG_CONTROL1, value);
153 if (err < 0)
154 return err;
155
156 return 0;
157}
158
159static int pcf8523_rtc_read_time(struct device *dev, struct rtc_time *tm)
160{
161 struct i2c_client *client = to_i2c_client(dev);
162 u8 start = REG_SECONDS, regs[7];
163 struct i2c_msg msgs[2];
164 int err;
165
166 msgs[0].addr = client->addr;
167 msgs[0].flags = 0;
168 msgs[0].len = 1;
169 msgs[0].buf = &start;
170
171 msgs[1].addr = client->addr;
172 msgs[1].flags = I2C_M_RD;
173 msgs[1].len = sizeof(regs);
174 msgs[1].buf = regs;
175
176 err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
177 if (err < 0)
178 return err;
179
180 if (regs[0] & REG_SECONDS_OS) {
181 /*
182 * If the oscillator was stopped, try to clear the flag. Upon
183 * power-up the flag is always set, but if we cannot clear it
184 * the oscillator isn't running properly for some reason. The
185 * sensible thing therefore is to return an error, signalling
186 * that the clock cannot be assumed to be correct.
187 */
188
189 regs[0] &= ~REG_SECONDS_OS;
190
191 err = pcf8523_write(client, REG_SECONDS, regs[0]);
192 if (err < 0)
193 return err;
194
195 err = pcf8523_read(client, REG_SECONDS, &regs[0]);
196 if (err < 0)
197 return err;
198
199 if (regs[0] & REG_SECONDS_OS)
200 return -EAGAIN;
201 }
202
203 tm->tm_sec = bcd2bin(regs[0] & 0x7f);
204 tm->tm_min = bcd2bin(regs[1] & 0x7f);
205 tm->tm_hour = bcd2bin(regs[2] & 0x3f);
206 tm->tm_mday = bcd2bin(regs[3] & 0x3f);
207 tm->tm_wday = regs[4] & 0x7;
208 tm->tm_mon = bcd2bin(regs[5] & 0x1f);
209 tm->tm_year = bcd2bin(regs[6]) + 100;
210
211 return rtc_valid_tm(tm);
212}
213
214static int pcf8523_rtc_set_time(struct device *dev, struct rtc_time *tm)
215{
216 struct i2c_client *client = to_i2c_client(dev);
217 struct i2c_msg msg;
218 u8 regs[8];
219 int err;
220
221 err = pcf8523_stop_rtc(client);
222 if (err < 0)
223 return err;
224
225 regs[0] = REG_SECONDS;
226 regs[1] = bin2bcd(tm->tm_sec);
227 regs[2] = bin2bcd(tm->tm_min);
228 regs[3] = bin2bcd(tm->tm_hour);
229 regs[4] = bin2bcd(tm->tm_mday);
230 regs[5] = tm->tm_wday;
231 regs[6] = bin2bcd(tm->tm_mon);
232 regs[7] = bin2bcd(tm->tm_year - 100);
233
234 msg.addr = client->addr;
235 msg.flags = 0;
236 msg.len = sizeof(regs);
237 msg.buf = regs;
238
239 err = i2c_transfer(client->adapter, &msg, 1);
240 if (err < 0) {
241 /*
242 * If the time cannot be set, restart the RTC anyway. Note
243 * that errors are ignored if the RTC cannot be started so
244 * that we have a chance to propagate the original error.
245 */
246 pcf8523_start_rtc(client);
247 return err;
248 }
249
250 return pcf8523_start_rtc(client);
251}
252
253static const struct rtc_class_ops pcf8523_rtc_ops = {
254 .read_time = pcf8523_rtc_read_time,
255 .set_time = pcf8523_rtc_set_time,
256};
257
258static int pcf8523_probe(struct i2c_client *client,
259 const struct i2c_device_id *id)
260{
261 struct pcf8523 *pcf;
262 int err;
263
264 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
265 return -ENODEV;
266
267 pcf = devm_kzalloc(&client->dev, sizeof(*pcf), GFP_KERNEL);
268 if (!pcf)
269 return -ENOMEM;
270
271 err = pcf8523_select_capacitance(client, true);
272 if (err < 0)
273 return err;
274
275 err = pcf8523_set_pm(client, 0);
276 if (err < 0)
277 return err;
278
279 pcf->rtc = rtc_device_register(DRIVER_NAME, &client->dev,
280 &pcf8523_rtc_ops, THIS_MODULE);
281 if (IS_ERR(pcf->rtc))
282 return PTR_ERR(pcf->rtc);
283
284 i2c_set_clientdata(client, pcf);
285
286 return 0;
287}
288
289static int pcf8523_remove(struct i2c_client *client)
290{
291 struct pcf8523 *pcf = i2c_get_clientdata(client);
292
293 rtc_device_unregister(pcf->rtc);
294
295 return 0;
296}
297
298static const struct i2c_device_id pcf8523_id[] = {
299 { "pcf8523", 0 },
300 { }
301};
302MODULE_DEVICE_TABLE(i2c, pcf8523_id);
303
304#ifdef CONFIG_OF
305static const struct of_device_id pcf8523_of_match[] = {
306 { .compatible = "nxp,pcf8523" },
307 { }
308};
309MODULE_DEVICE_TABLE(of, pcf8523_of_match);
310#endif
311
312static struct i2c_driver pcf8523_driver = {
313 .driver = {
314 .name = DRIVER_NAME,
315 .owner = THIS_MODULE,
316 .of_match_table = of_match_ptr(pcf8523_of_match),
317 },
318 .probe = pcf8523_probe,
319 .remove = pcf8523_remove,
320 .id_table = pcf8523_id,
321};
322module_i2c_driver(pcf8523_driver);
323
324MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>");
325MODULE_DESCRIPTION("NXP PCF8523 RTC driver");
326MODULE_LICENSE("GPL v2");