aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Verges <chrisv@cyberswitching.com>2009-09-22 19:46:20 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2009-09-23 10:39:44 -0400
commit7f3923a184bb8e7ede5e5f58f1114bf7b8c611ea (patch)
tree6c11806b0f93fd2a20758c2b40883d0067d81e59
parent568d0697f42771425ae9f1e9a3db769fef7e10b6 (diff)
rtc: Philips PCF2123 RTC SPI driver
Add support for the Philips/NXP PCF2123 RTC. Signed-off: Chris Verges <chrisv@cyberswitching.com> Tested-by: Chris Verges <chrisv@cyberswitching.com> Signed-off: Christian Pellegrin <chripell@fsfe.org> Tested-by: Christian Pellegrin <chripell@fsfe.org> Cc: Alessandro Zummo <a.zummo@towertech.it> Cc: David Brownell <david-b@pacbell.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--drivers/rtc/Kconfig9
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/rtc-pcf2123.c344
3 files changed, 354 insertions, 0 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 73771b09fbd3..2ccd351c52ed 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -378,6 +378,15 @@ config RTC_DRV_DS3234
378 This driver can also be built as a module. If so, the module 378 This driver can also be built as a module. If so, the module
379 will be called rtc-ds3234. 379 will be called rtc-ds3234.
380 380
381config RTC_DRV_PCF2123
382 tristate "NXP PCF2123"
383 help
384 If you say yes here you get support for the NXP PCF2123
385 RTC chip.
386
387 This driver can also be built as a module. If so, the module
388 will be called rtc-pcf2123.
389
381endif # SPI_MASTER 390endif # SPI_MASTER
382 391
383comment "Platform RTC drivers" 392comment "Platform RTC drivers"
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 5e152ffe5058..0e9414995cae 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o
55obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o 55obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o
56obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o 56obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o
57obj-$(CONFIG_RTC_DRV_PCF8583) += rtc-pcf8583.o 57obj-$(CONFIG_RTC_DRV_PCF8583) += rtc-pcf8583.o
58obj-$(CONFIG_RTC_DRV_PCF2123) += rtc-pcf2123.o
58obj-$(CONFIG_RTC_DRV_PL030) += rtc-pl030.o 59obj-$(CONFIG_RTC_DRV_PL030) += rtc-pl030.o
59obj-$(CONFIG_RTC_DRV_PL031) += rtc-pl031.o 60obj-$(CONFIG_RTC_DRV_PL031) += rtc-pl031.o
60obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o 61obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o
diff --git a/drivers/rtc/rtc-pcf2123.c b/drivers/rtc/rtc-pcf2123.c
new file mode 100644
index 000000000000..d07b199a0c94
--- /dev/null
+++ b/drivers/rtc/rtc-pcf2123.c
@@ -0,0 +1,344 @@
1/*
2 * An SPI driver for the Philips PCF2123 RTC
3 * Copyright 2009 Cyber Switching, Inc.
4 *
5 * Author: Chris Verges <chrisv@cyberswitching.com>
6 * Maintainers: http://www.cyberswitching.com
7 *
8 * based on the RS5C348 driver in this same directory.
9 *
10 * Thanks to Christian Pellegrin <chripell@fsfe.org> for
11 * the sysfs contributions to this driver.
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License version 2 as
15 * published by the Free Software Foundation.
16 *
17 * Please note that the CS is active high, so platform data
18 * should look something like:
19 *
20 * static struct spi_board_info ek_spi_devices[] = {
21 * ...
22 * {
23 * .modalias = "rtc-pcf2123",
24 * .chip_select = 1,
25 * .controller_data = (void *)AT91_PIN_PA10,
26 * .max_speed_hz = 1000 * 1000,
27 * .mode = SPI_CS_HIGH,
28 * .bus_num = 0,
29 * },
30 * ...
31 *};
32 *
33 */
34
35#include <linux/bcd.h>
36#include <linux/delay.h>
37#include <linux/device.h>
38#include <linux/errno.h>
39#include <linux/init.h>
40#include <linux/kernel.h>
41#include <linux/string.h>
42#include <linux/rtc.h>
43#include <linux/spi/spi.h>
44
45#define DRV_VERSION "0.5"
46
47#define PCF2123_REG_CTRL1 (0x00) /* Control Register 1 */
48#define PCF2123_REG_CTRL2 (0x01) /* Control Register 2 */
49#define PCF2123_REG_SC (0x02) /* datetime */
50#define PCF2123_REG_MN (0x03)
51#define PCF2123_REG_HR (0x04)
52#define PCF2123_REG_DM (0x05)
53#define PCF2123_REG_DW (0x06)
54#define PCF2123_REG_MO (0x07)
55#define PCF2123_REG_YR (0x08)
56
57#define PCF2123_SUBADDR (1 << 4)
58#define PCF2123_WRITE ((0 << 7) | PCF2123_SUBADDR)
59#define PCF2123_READ ((1 << 7) | PCF2123_SUBADDR)
60
61static struct spi_driver pcf2123_driver;
62
63struct pcf2123_sysfs_reg {
64 struct device_attribute attr; /* must be first */
65 char name[2];
66};
67
68struct pcf2123_plat_data {
69 struct rtc_device *rtc;
70 struct pcf2123_sysfs_reg regs[16];
71};
72
73/*
74 * Causes a 30 nanosecond delay to ensure that the PCF2123 chip select
75 * is released properly after an SPI write. This function should be
76 * called after EVERY read/write call over SPI.
77 */
78static inline void pcf2123_delay_trec(void)
79{
80 ndelay(30);
81}
82
83static ssize_t pcf2123_show(struct device *dev, struct device_attribute *attr,
84 char *buffer)
85{
86 struct spi_device *spi = to_spi_device(dev);
87 struct pcf2123_sysfs_reg *r = (struct pcf2123_sysfs_reg *) attr;
88 u8 txbuf[1], rxbuf[1];
89 int ret;
90
91 txbuf[0] = PCF2123_READ | simple_strtoul(r->name, NULL, 16);
92 ret = spi_write_then_read(spi, txbuf, 1, rxbuf, 1);
93 if (ret < 0)
94 return sprintf(buffer, "error: %d", ret);
95 pcf2123_delay_trec();
96 return sprintf(buffer, "0x%x", rxbuf[0]);
97}
98
99static ssize_t pcf2123_store(struct device *dev, struct device_attribute *attr,
100 const char *buffer, size_t count) {
101 struct spi_device *spi = to_spi_device(dev);
102 struct pcf2123_sysfs_reg *r = (struct pcf2123_sysfs_reg *) attr;
103 u8 txbuf[2];
104 int ret;
105
106 txbuf[0] = PCF2123_WRITE | simple_strtoul(r->name, NULL, 16);
107 txbuf[1] = simple_strtoul(buffer, NULL, 0);
108 ret = spi_write(spi, txbuf, sizeof(txbuf));
109 if (ret < 0)
110 return -EIO;
111 pcf2123_delay_trec();
112 return count;
113}
114
115static int pcf2123_rtc_read_time(struct device *dev, struct rtc_time *tm)
116{
117 struct spi_device *spi = to_spi_device(dev);
118 u8 txbuf[1], rxbuf[7];
119 int ret;
120
121 txbuf[0] = PCF2123_READ | PCF2123_REG_SC;
122 ret = spi_write_then_read(spi, txbuf, sizeof(txbuf),
123 rxbuf, sizeof(rxbuf));
124 if (ret < 0)
125 return ret;
126 pcf2123_delay_trec();
127
128 tm->tm_sec = bcd2bin(rxbuf[0] & 0x7F);
129 tm->tm_min = bcd2bin(rxbuf[1] & 0x7F);
130 tm->tm_hour = bcd2bin(rxbuf[2] & 0x3F); /* rtc hr 0-23 */
131 tm->tm_mday = bcd2bin(rxbuf[3] & 0x3F);
132 tm->tm_wday = rxbuf[4] & 0x07;
133 tm->tm_mon = bcd2bin(rxbuf[5] & 0x1F) - 1; /* rtc mn 1-12 */
134 tm->tm_year = bcd2bin(rxbuf[6]);
135 if (tm->tm_year < 70)
136 tm->tm_year += 100; /* assume we are in 1970...2069 */
137
138 dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
139 "mday=%d, mon=%d, year=%d, wday=%d\n",
140 __func__,
141 tm->tm_sec, tm->tm_min, tm->tm_hour,
142 tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
143
144 /* the clock can give out invalid datetime, but we cannot return
145 * -EINVAL otherwise hwclock will refuse to set the time on bootup.
146 */
147 if (rtc_valid_tm(tm) < 0)
148 dev_err(dev, "retrieved date/time is not valid.\n");
149
150 return 0;
151}
152
153static int pcf2123_rtc_set_time(struct device *dev, struct rtc_time *tm)
154{
155 struct spi_device *spi = to_spi_device(dev);
156 u8 txbuf[8];
157 int ret;
158
159 dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
160 "mday=%d, mon=%d, year=%d, wday=%d\n",
161 __func__,
162 tm->tm_sec, tm->tm_min, tm->tm_hour,
163 tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
164
165 /* Stop the counter first */
166 txbuf[0] = PCF2123_WRITE | PCF2123_REG_CTRL1;
167 txbuf[1] = 0x20;
168 ret = spi_write(spi, txbuf, 2);
169 if (ret < 0)
170 return ret;
171 pcf2123_delay_trec();
172
173 /* Set the new time */
174 txbuf[0] = PCF2123_WRITE | PCF2123_REG_SC;
175 txbuf[1] = bin2bcd(tm->tm_sec & 0x7F);
176 txbuf[2] = bin2bcd(tm->tm_min & 0x7F);
177 txbuf[3] = bin2bcd(tm->tm_hour & 0x3F);
178 txbuf[4] = bin2bcd(tm->tm_mday & 0x3F);
179 txbuf[5] = tm->tm_wday & 0x07;
180 txbuf[6] = bin2bcd((tm->tm_mon + 1) & 0x1F); /* rtc mn 1-12 */
181 txbuf[7] = bin2bcd(tm->tm_year < 100 ? tm->tm_year : tm->tm_year - 100);
182
183 ret = spi_write(spi, txbuf, sizeof(txbuf));
184 if (ret < 0)
185 return ret;
186 pcf2123_delay_trec();
187
188 /* Start the counter */
189 txbuf[0] = PCF2123_WRITE | PCF2123_REG_CTRL1;
190 txbuf[1] = 0x00;
191 ret = spi_write(spi, txbuf, 2);
192 if (ret < 0)
193 return ret;
194 pcf2123_delay_trec();
195
196 return 0;
197}
198
199static const struct rtc_class_ops pcf2123_rtc_ops = {
200 .read_time = pcf2123_rtc_read_time,
201 .set_time = pcf2123_rtc_set_time,
202};
203
204static int __devinit pcf2123_probe(struct spi_device *spi)
205{
206 struct rtc_device *rtc;
207 struct pcf2123_plat_data *pdata;
208 u8 txbuf[2], rxbuf[2];
209 int ret, i;
210
211 pdata = kzalloc(sizeof(struct pcf2123_plat_data), GFP_KERNEL);
212 if (!pdata)
213 return -ENOMEM;
214 spi->dev.platform_data = pdata;
215
216 /* Send a software reset command */
217 txbuf[0] = PCF2123_WRITE | PCF2123_REG_CTRL1;
218 txbuf[1] = 0x58;
219 dev_dbg(&spi->dev, "resetting RTC (0x%02X 0x%02X)\n",
220 txbuf[0], txbuf[1]);
221 ret = spi_write(spi, txbuf, 2 * sizeof(u8));
222 if (ret < 0)
223 return ret;
224 pcf2123_delay_trec();
225
226 /* Stop the counter */
227 txbuf[0] = PCF2123_WRITE | PCF2123_REG_CTRL1;
228 txbuf[1] = 0x20;
229 dev_dbg(&spi->dev, "stopping RTC (0x%02X 0x%02X)\n",
230 txbuf[0], txbuf[1]);
231 ret = spi_write(spi, txbuf, 2 * sizeof(u8));
232 if (ret < 0)
233 return ret;
234 pcf2123_delay_trec();
235
236 /* See if the counter was actually stopped */
237 txbuf[0] = PCF2123_READ | PCF2123_REG_CTRL1;
238 dev_dbg(&spi->dev, "checking for presence of RTC (0x%02X)\n",
239 txbuf[0]);
240 ret = spi_write_then_read(spi, txbuf, 1 * sizeof(u8),
241 rxbuf, 2 * sizeof(u8));
242 dev_dbg(&spi->dev, "received data from RTC (0x%02X 0x%02X)\n",
243 rxbuf[0], rxbuf[1]);
244 if (ret < 0)
245 goto kfree_exit;
246 pcf2123_delay_trec();
247
248 if (!(rxbuf[0] & 0x20)) {
249 dev_err(&spi->dev, "chip not found\n");
250 goto kfree_exit;
251 }
252
253 dev_info(&spi->dev, "chip found, driver version " DRV_VERSION "\n");
254 dev_info(&spi->dev, "spiclk %u KHz.\n",
255 (spi->max_speed_hz + 500) / 1000);
256
257 /* Start the counter */
258 txbuf[0] = PCF2123_WRITE | PCF2123_REG_CTRL1;
259 txbuf[1] = 0x00;
260 ret = spi_write(spi, txbuf, sizeof(txbuf));
261 if (ret < 0)
262 goto kfree_exit;
263 pcf2123_delay_trec();
264
265 /* Finalize the initialization */
266 rtc = rtc_device_register(pcf2123_driver.driver.name, &spi->dev,
267 &pcf2123_rtc_ops, THIS_MODULE);
268
269 if (IS_ERR(rtc)) {
270 dev_err(&spi->dev, "failed to register.\n");
271 ret = PTR_ERR(rtc);
272 goto kfree_exit;
273 }
274
275 pdata->rtc = rtc;
276
277 for (i = 0; i < 16; i++) {
278 sprintf(pdata->regs[i].name, "%1x", i);
279 pdata->regs[i].attr.attr.mode = S_IRUGO | S_IWUSR;
280 pdata->regs[i].attr.attr.name = pdata->regs[i].name;
281 pdata->regs[i].attr.show = pcf2123_show;
282 pdata->regs[i].attr.store = pcf2123_store;
283 ret = device_create_file(&spi->dev, &pdata->regs[i].attr);
284 if (ret) {
285 dev_err(&spi->dev, "Unable to create sysfs %s\n",
286 pdata->regs[i].name);
287 pdata->regs[i].name[0] = '\0';
288 }
289 }
290
291 return 0;
292kfree_exit:
293 kfree(pdata);
294 spi->dev.platform_data = NULL;
295 return ret;
296}
297
298static int pcf2123_remove(struct spi_device *spi)
299{
300 struct pcf2123_plat_data *pdata = spi->dev.platform_data;
301 int i;
302
303 if (pdata) {
304 struct rtc_device *rtc = pdata->rtc;
305
306 if (rtc)
307 rtc_device_unregister(rtc);
308 for (i = 0; i < 16; i++)
309 if (pdata->regs[i].name[0])
310 device_remove_file(&spi->dev,
311 &pdata->regs[i].attr);
312 kfree(pdata);
313 }
314
315 return 0;
316}
317
318static struct spi_driver pcf2123_driver = {
319 .driver = {
320 .name = "rtc-pcf2123",
321 .bus = &spi_bus_type,
322 .owner = THIS_MODULE,
323 },
324 .probe = pcf2123_probe,
325 .remove = __devexit_p(pcf2123_remove),
326};
327
328static int __init pcf2123_init(void)
329{
330 return spi_register_driver(&pcf2123_driver);
331}
332
333static void __exit pcf2123_exit(void)
334{
335 spi_unregister_driver(&pcf2123_driver);
336}
337
338MODULE_AUTHOR("Chris Verges <chrisv@cyberswitching.com>");
339MODULE_DESCRIPTION("NXP PCF2123 RTC driver");
340MODULE_LICENSE("GPL");
341MODULE_VERSION(DRV_VERSION);
342
343module_init(pcf2123_init);
344module_exit(pcf2123_exit);