aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/rtc/rtc-m41t93.c
diff options
context:
space:
mode:
authorVoss, Nikolaus <N.Voss@weinmann.de>2011-05-26 19:25:07 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2011-05-26 20:12:33 -0400
commit74d34d4be60928c3504fdad9d67707a6e3244a80 (patch)
tree4019642f3e6fe78cda4b692eab52f8b189b2af96 /drivers/rtc/rtc-m41t93.c
parent52365230eed7e291bbb2a015465cac48bcb3928f (diff)
rtc: add basic support for ST M41T93 SPI RTC
Add basic support for ST m41t93 SPI RTCs. Tested with factory-new and with "run-in" species with and without backup batteries. Signed-off-by: Nikolaus Voss <n.voss@weinmann.de> Cc: Alessandro Zummo <a.zummo@towertech.it> Cc: Grant Likely <grant.likely@secretlab.ca> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/rtc/rtc-m41t93.c')
-rw-r--r--drivers/rtc/rtc-m41t93.c225
1 files changed, 225 insertions, 0 deletions
diff --git a/drivers/rtc/rtc-m41t93.c b/drivers/rtc/rtc-m41t93.c
new file mode 100644
index 000000000000..1a84b3e227d1
--- /dev/null
+++ b/drivers/rtc/rtc-m41t93.c
@@ -0,0 +1,225 @@
1/*
2 *
3 * Driver for ST M41T93 SPI RTC
4 *
5 * (c) 2010 Nikolaus Voss, Weinmann Medical GmbH
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/bcd.h>
13#include <linux/kernel.h>
14#include <linux/module.h>
15#include <linux/platform_device.h>
16#include <linux/rtc.h>
17#include <linux/spi/spi.h>
18
19#define M41T93_REG_SSEC 0
20#define M41T93_REG_ST_SEC 1
21#define M41T93_REG_MIN 2
22#define M41T93_REG_CENT_HOUR 3
23#define M41T93_REG_WDAY 4
24#define M41T93_REG_DAY 5
25#define M41T93_REG_MON 6
26#define M41T93_REG_YEAR 7
27
28
29#define M41T93_REG_ALM_HOUR_HT 0xc
30#define M41T93_REG_FLAGS 0xf
31
32#define M41T93_FLAG_ST (1 << 7)
33#define M41T93_FLAG_OF (1 << 2)
34#define M41T93_FLAG_BL (1 << 4)
35#define M41T93_FLAG_HT (1 << 6)
36
37static inline int m41t93_set_reg(struct spi_device *spi, u8 addr, u8 data)
38{
39 u8 buf[2];
40
41 /* MSB must be '1' to write */
42 buf[0] = addr | 0x80;
43 buf[1] = data;
44
45 return spi_write(spi, buf, sizeof(buf));
46}
47
48static int m41t93_set_time(struct device *dev, struct rtc_time *tm)
49{
50 struct spi_device *spi = to_spi_device(dev);
51 u8 buf[9] = {0x80}; /* write cmd + 8 data bytes */
52 u8 * const data = &buf[1]; /* ptr to first data byte */
53
54 dev_dbg(dev, "%s secs=%d, mins=%d, "
55 "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
56 "write", tm->tm_sec, tm->tm_min,
57 tm->tm_hour, tm->tm_mday,
58 tm->tm_mon, tm->tm_year, tm->tm_wday);
59
60 if (tm->tm_year < 100) {
61 dev_warn(&spi->dev, "unsupported date (before 2000-01-01).\n");
62 return -EINVAL;
63 }
64
65 data[M41T93_REG_SSEC] = 0;
66 data[M41T93_REG_ST_SEC] = bin2bcd(tm->tm_sec);
67 data[M41T93_REG_MIN] = bin2bcd(tm->tm_min);
68 data[M41T93_REG_CENT_HOUR] = bin2bcd(tm->tm_hour) |
69 ((tm->tm_year/100-1) << 6);
70 data[M41T93_REG_DAY] = bin2bcd(tm->tm_mday);
71 data[M41T93_REG_WDAY] = bin2bcd(tm->tm_wday + 1);
72 data[M41T93_REG_MON] = bin2bcd(tm->tm_mon + 1);
73 data[M41T93_REG_YEAR] = bin2bcd(tm->tm_year % 100);
74
75 return spi_write(spi, buf, sizeof(buf));
76}
77
78
79static int m41t93_get_time(struct device *dev, struct rtc_time *tm)
80{
81 struct spi_device *spi = to_spi_device(dev);
82 const u8 start_addr = 0;
83 u8 buf[8];
84 int century_after_1900;
85 int tmp;
86 int ret = 0;
87
88 /* Check status of clock. Two states must be considered:
89 1. halt bit (HT) is set: the clock is running but update of readout
90 registers has been disabled due to power failure. This is normal
91 case after poweron. Time is valid after resetting HT bit.
92 2. oscillator fail bit (OF) is set. Oscillator has be stopped and
93 time is invalid:
94 a) OF can be immeditely reset.
95 b) OF cannot be immediately reset: oscillator has to be restarted.
96 */
97 tmp = spi_w8r8(spi, M41T93_REG_ALM_HOUR_HT);
98 if (tmp < 0)
99 return tmp;
100
101 if (tmp & M41T93_FLAG_HT) {
102 dev_dbg(&spi->dev, "HT bit is set, reenable clock update.\n");
103 m41t93_set_reg(spi, M41T93_REG_ALM_HOUR_HT,
104 tmp & ~M41T93_FLAG_HT);
105 }
106
107 tmp = spi_w8r8(spi, M41T93_REG_FLAGS);
108 if (tmp < 0)
109 return tmp;
110
111 if (tmp & M41T93_FLAG_OF) {
112 ret = -EINVAL;
113 dev_warn(&spi->dev, "OF bit is set, resetting.\n");
114 m41t93_set_reg(spi, M41T93_REG_FLAGS, tmp & ~M41T93_FLAG_OF);
115
116 tmp = spi_w8r8(spi, M41T93_REG_FLAGS);
117 if (tmp < 0)
118 return tmp;
119 else if (tmp & M41T93_FLAG_OF) {
120 u8 reset_osc = buf[M41T93_REG_ST_SEC] | M41T93_FLAG_ST;
121
122 dev_warn(&spi->dev,
123 "OF bit is still set, kickstarting clock.\n");
124 m41t93_set_reg(spi, M41T93_REG_ST_SEC, reset_osc);
125 reset_osc &= ~M41T93_FLAG_ST;
126 m41t93_set_reg(spi, M41T93_REG_ST_SEC, reset_osc);
127 }
128 }
129
130 if (tmp & M41T93_FLAG_BL)
131 dev_warn(&spi->dev, "BL bit is set, replace battery.\n");
132
133 /* read actual time/date */
134 tmp = spi_write_then_read(spi, &start_addr, 1, buf, sizeof(buf));
135 if (tmp < 0)
136 return tmp;
137
138 tm->tm_sec = bcd2bin(buf[M41T93_REG_ST_SEC]);
139 tm->tm_min = bcd2bin(buf[M41T93_REG_MIN]);
140 tm->tm_hour = bcd2bin(buf[M41T93_REG_CENT_HOUR] & 0x3f);
141 tm->tm_mday = bcd2bin(buf[M41T93_REG_DAY]);
142 tm->tm_mon = bcd2bin(buf[M41T93_REG_MON]) - 1;
143 tm->tm_wday = bcd2bin(buf[M41T93_REG_WDAY] & 0x0f) - 1;
144
145 century_after_1900 = (buf[M41T93_REG_CENT_HOUR] >> 6) + 1;
146 tm->tm_year = bcd2bin(buf[M41T93_REG_YEAR]) + century_after_1900 * 100;
147
148 dev_dbg(dev, "%s secs=%d, mins=%d, "
149 "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
150 "read", tm->tm_sec, tm->tm_min,
151 tm->tm_hour, tm->tm_mday,
152 tm->tm_mon, tm->tm_year, tm->tm_wday);
153
154 return ret < 0 ? ret : rtc_valid_tm(tm);
155}
156
157
158static const struct rtc_class_ops m41t93_rtc_ops = {
159 .read_time = m41t93_get_time,
160 .set_time = m41t93_set_time,
161};
162
163static struct spi_driver m41t93_driver;
164
165static int __devinit m41t93_probe(struct spi_device *spi)
166{
167 struct rtc_device *rtc;
168 int res;
169
170 spi->bits_per_word = 8;
171 spi_setup(spi);
172
173 res = spi_w8r8(spi, M41T93_REG_WDAY);
174 if (res < 0 || (res & 0xf8) != 0) {
175 dev_err(&spi->dev, "not found 0x%x.\n", res);
176 return -ENODEV;
177 }
178
179 rtc = rtc_device_register(m41t93_driver.driver.name,
180 &spi->dev, &m41t93_rtc_ops, THIS_MODULE);
181 if (IS_ERR(rtc))
182 return PTR_ERR(rtc);
183
184 dev_set_drvdata(&spi->dev, rtc);
185
186 return 0;
187}
188
189
190static int __devexit m41t93_remove(struct spi_device *spi)
191{
192 struct rtc_device *rtc = platform_get_drvdata(spi);
193
194 if (rtc)
195 rtc_device_unregister(rtc);
196
197 return 0;
198}
199
200static struct spi_driver m41t93_driver = {
201 .driver = {
202 .name = "rtc-m41t93",
203 .bus = &spi_bus_type,
204 .owner = THIS_MODULE,
205 },
206 .probe = m41t93_probe,
207 .remove = __devexit_p(m41t93_remove),
208};
209
210static __init int m41t93_init(void)
211{
212 return spi_register_driver(&m41t93_driver);
213}
214module_init(m41t93_init);
215
216static __exit void m41t93_exit(void)
217{
218 spi_unregister_driver(&m41t93_driver);
219}
220module_exit(m41t93_exit);
221
222MODULE_AUTHOR("Nikolaus Voss <n.voss@weinmann.de>");
223MODULE_DESCRIPTION("Driver for ST M41T93 SPI RTC");
224MODULE_LICENSE("GPL");
225MODULE_ALIAS("spi:rtc-m41t93");