Commit | Line | Data |
---|---|---|
c6d8f400 SL |
1 | /* |
2 | * rtc-fm3130.c - RTC driver for Ramtron FM3130 I2C chip. | |
3 | * | |
4 | * Copyright (C) 2008 Sergey Lapin | |
5 | * Based on ds1307 driver by James Chapman and David Brownell | |
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/module.h> | |
13 | #include <linux/i2c.h> | |
14 | #include <linux/rtc.h> | |
15 | #include <linux/bcd.h> | |
5a0e3ad6 | 16 | #include <linux/slab.h> |
c6d8f400 SL |
17 | |
18 | #define FM3130_RTC_CONTROL (0x0) | |
19 | #define FM3130_CAL_CONTROL (0x1) | |
20 | #define FM3130_RTC_SECONDS (0x2) | |
21 | #define FM3130_RTC_MINUTES (0x3) | |
22 | #define FM3130_RTC_HOURS (0x4) | |
23 | #define FM3130_RTC_DAY (0x5) | |
24 | #define FM3130_RTC_DATE (0x6) | |
25 | #define FM3130_RTC_MONTHS (0x7) | |
26 | #define FM3130_RTC_YEARS (0x8) | |
27 | ||
28 | #define FM3130_ALARM_SECONDS (0x9) | |
29 | #define FM3130_ALARM_MINUTES (0xa) | |
30 | #define FM3130_ALARM_HOURS (0xb) | |
31 | #define FM3130_ALARM_DATE (0xc) | |
32 | #define FM3130_ALARM_MONTHS (0xd) | |
33 | #define FM3130_ALARM_WP_CONTROL (0xe) | |
34 | ||
35 | #define FM3130_CAL_CONTROL_BIT_nOSCEN (1 << 7) /* Osciallator enabled */ | |
36 | #define FM3130_RTC_CONTROL_BIT_LB (1 << 7) /* Low battery */ | |
37 | #define FM3130_RTC_CONTROL_BIT_AF (1 << 6) /* Alarm flag */ | |
38 | #define FM3130_RTC_CONTROL_BIT_CF (1 << 5) /* Century overflow */ | |
39 | #define FM3130_RTC_CONTROL_BIT_POR (1 << 4) /* Power on reset */ | |
40 | #define FM3130_RTC_CONTROL_BIT_AEN (1 << 3) /* Alarm enable */ | |
41 | #define FM3130_RTC_CONTROL_BIT_CAL (1 << 2) /* Calibration mode */ | |
42 | #define FM3130_RTC_CONTROL_BIT_WRITE (1 << 1) /* W=1 -> write mode W=0 normal */ | |
43 | #define FM3130_RTC_CONTROL_BIT_READ (1 << 0) /* R=1 -> read mode R=0 normal */ | |
44 | ||
45 | #define FM3130_CLOCK_REGS 7 | |
46 | #define FM3130_ALARM_REGS 5 | |
47 | ||
48 | struct fm3130 { | |
49 | u8 reg_addr_time; | |
50 | u8 reg_addr_alarm; | |
51 | u8 regs[15]; | |
52 | struct i2c_msg msg[4]; | |
53 | struct i2c_client *client; | |
54 | struct rtc_device *rtc; | |
f3f99cf3 | 55 | int alarm_valid; |
c6d8f400 | 56 | int data_valid; |
c6d8f400 SL |
57 | }; |
58 | static const struct i2c_device_id fm3130_id[] = { | |
876550aa | 59 | { "fm3130", 0 }, |
c6d8f400 SL |
60 | { } |
61 | }; | |
62 | MODULE_DEVICE_TABLE(i2c, fm3130_id); | |
63 | ||
64 | #define FM3130_MODE_NORMAL 0 | |
65 | #define FM3130_MODE_WRITE 1 | |
66 | #define FM3130_MODE_READ 2 | |
67 | ||
68 | static void fm3130_rtc_mode(struct device *dev, int mode) | |
69 | { | |
70 | struct fm3130 *fm3130 = dev_get_drvdata(dev); | |
71 | ||
72 | fm3130->regs[FM3130_RTC_CONTROL] = | |
73 | i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL); | |
74 | switch (mode) { | |
75 | case FM3130_MODE_NORMAL: | |
76 | fm3130->regs[FM3130_RTC_CONTROL] &= | |
77 | ~(FM3130_RTC_CONTROL_BIT_WRITE | | |
78 | FM3130_RTC_CONTROL_BIT_READ); | |
79 | break; | |
80 | case FM3130_MODE_WRITE: | |
81 | fm3130->regs[FM3130_RTC_CONTROL] |= FM3130_RTC_CONTROL_BIT_WRITE; | |
82 | break; | |
83 | case FM3130_MODE_READ: | |
84 | fm3130->regs[FM3130_RTC_CONTROL] |= FM3130_RTC_CONTROL_BIT_READ; | |
85 | break; | |
86 | default: | |
87 | dev_dbg(dev, "invalid mode %d\n", mode); | |
88 | break; | |
89 | } | |
f3f99cf3 | 90 | |
c6d8f400 SL |
91 | i2c_smbus_write_byte_data(fm3130->client, |
92 | FM3130_RTC_CONTROL, fm3130->regs[FM3130_RTC_CONTROL]); | |
93 | } | |
94 | ||
95 | static int fm3130_get_time(struct device *dev, struct rtc_time *t) | |
96 | { | |
97 | struct fm3130 *fm3130 = dev_get_drvdata(dev); | |
98 | int tmp; | |
99 | ||
100 | if (!fm3130->data_valid) { | |
101 | /* We have invalid data in RTC, probably due | |
102 | to battery faults or other problems. Return EIO | |
fd0961ff | 103 | for now, it will allow us to set data later instead |
c6d8f400 SL |
104 | of error during probing which disables device */ |
105 | return -EIO; | |
106 | } | |
107 | fm3130_rtc_mode(dev, FM3130_MODE_READ); | |
108 | ||
109 | /* read the RTC date and time registers all at once */ | |
110 | tmp = i2c_transfer(to_i2c_adapter(fm3130->client->dev.parent), | |
111 | fm3130->msg, 2); | |
112 | if (tmp != 2) { | |
113 | dev_err(dev, "%s error %d\n", "read", tmp); | |
114 | return -EIO; | |
115 | } | |
116 | ||
117 | fm3130_rtc_mode(dev, FM3130_MODE_NORMAL); | |
118 | ||
119 | dev_dbg(dev, "%s: %02x %02x %02x %02x %02x %02x %02x %02x" | |
120 | "%02x %02x %02x %02x %02x %02x %02x\n", | |
121 | "read", | |
122 | fm3130->regs[0], fm3130->regs[1], | |
123 | fm3130->regs[2], fm3130->regs[3], | |
124 | fm3130->regs[4], fm3130->regs[5], | |
125 | fm3130->regs[6], fm3130->regs[7], | |
126 | fm3130->regs[8], fm3130->regs[9], | |
127 | fm3130->regs[0xa], fm3130->regs[0xb], | |
128 | fm3130->regs[0xc], fm3130->regs[0xd], | |
129 | fm3130->regs[0xe]); | |
130 | ||
fe20ba70 AB |
131 | t->tm_sec = bcd2bin(fm3130->regs[FM3130_RTC_SECONDS] & 0x7f); |
132 | t->tm_min = bcd2bin(fm3130->regs[FM3130_RTC_MINUTES] & 0x7f); | |
c6d8f400 | 133 | tmp = fm3130->regs[FM3130_RTC_HOURS] & 0x3f; |
fe20ba70 AB |
134 | t->tm_hour = bcd2bin(tmp); |
135 | t->tm_wday = bcd2bin(fm3130->regs[FM3130_RTC_DAY] & 0x07) - 1; | |
136 | t->tm_mday = bcd2bin(fm3130->regs[FM3130_RTC_DATE] & 0x3f); | |
c6d8f400 | 137 | tmp = fm3130->regs[FM3130_RTC_MONTHS] & 0x1f; |
fe20ba70 | 138 | t->tm_mon = bcd2bin(tmp) - 1; |
c6d8f400 SL |
139 | |
140 | /* assume 20YY not 19YY, and ignore CF bit */ | |
fe20ba70 | 141 | t->tm_year = bcd2bin(fm3130->regs[FM3130_RTC_YEARS]) + 100; |
c6d8f400 SL |
142 | |
143 | dev_dbg(dev, "%s secs=%d, mins=%d, " | |
144 | "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", | |
145 | "read", t->tm_sec, t->tm_min, | |
146 | t->tm_hour, t->tm_mday, | |
147 | t->tm_mon, t->tm_year, t->tm_wday); | |
148 | ||
149 | /* initial clock setting can be undefined */ | |
150 | return rtc_valid_tm(t); | |
151 | } | |
152 | ||
153 | ||
154 | static int fm3130_set_time(struct device *dev, struct rtc_time *t) | |
155 | { | |
156 | struct fm3130 *fm3130 = dev_get_drvdata(dev); | |
157 | int tmp, i; | |
158 | u8 *buf = fm3130->regs; | |
159 | ||
160 | dev_dbg(dev, "%s secs=%d, mins=%d, " | |
161 | "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", | |
162 | "write", t->tm_sec, t->tm_min, | |
163 | t->tm_hour, t->tm_mday, | |
164 | t->tm_mon, t->tm_year, t->tm_wday); | |
165 | ||
166 | /* first register addr */ | |
fe20ba70 AB |
167 | buf[FM3130_RTC_SECONDS] = bin2bcd(t->tm_sec); |
168 | buf[FM3130_RTC_MINUTES] = bin2bcd(t->tm_min); | |
169 | buf[FM3130_RTC_HOURS] = bin2bcd(t->tm_hour); | |
170 | buf[FM3130_RTC_DAY] = bin2bcd(t->tm_wday + 1); | |
171 | buf[FM3130_RTC_DATE] = bin2bcd(t->tm_mday); | |
172 | buf[FM3130_RTC_MONTHS] = bin2bcd(t->tm_mon + 1); | |
c6d8f400 SL |
173 | |
174 | /* assume 20YY not 19YY */ | |
175 | tmp = t->tm_year - 100; | |
fe20ba70 | 176 | buf[FM3130_RTC_YEARS] = bin2bcd(tmp); |
c6d8f400 SL |
177 | |
178 | dev_dbg(dev, "%s: %02x %02x %02x %02x %02x %02x %02x" | |
179 | "%02x %02x %02x %02x %02x %02x %02x %02x\n", | |
180 | "write", buf[0], buf[1], buf[2], buf[3], | |
181 | buf[4], buf[5], buf[6], buf[7], | |
182 | buf[8], buf[9], buf[0xa], buf[0xb], | |
183 | buf[0xc], buf[0xd], buf[0xe]); | |
184 | ||
185 | fm3130_rtc_mode(dev, FM3130_MODE_WRITE); | |
186 | ||
187 | /* Writing time registers, we don't support multibyte transfers */ | |
188 | for (i = 0; i < FM3130_CLOCK_REGS; i++) { | |
189 | i2c_smbus_write_byte_data(fm3130->client, | |
190 | FM3130_RTC_SECONDS + i, | |
191 | fm3130->regs[FM3130_RTC_SECONDS + i]); | |
192 | } | |
193 | ||
194 | fm3130_rtc_mode(dev, FM3130_MODE_NORMAL); | |
195 | ||
196 | /* We assume here that data are valid once written */ | |
197 | if (!fm3130->data_valid) | |
198 | fm3130->data_valid = 1; | |
199 | return 0; | |
200 | } | |
201 | ||
202 | static int fm3130_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) | |
203 | { | |
204 | struct fm3130 *fm3130 = dev_get_drvdata(dev); | |
205 | int tmp; | |
206 | struct rtc_time *tm = &alrm->time; | |
f3f99cf3 SM |
207 | |
208 | if (!fm3130->alarm_valid) { | |
209 | /* | |
210 | * We have invalid alarm in RTC, probably due to battery faults | |
211 | * or other problems. Return EIO for now, it will allow us to | |
212 | * set alarm value later instead of error during probing which | |
213 | * disables device | |
214 | */ | |
215 | return -EIO; | |
216 | } | |
217 | ||
c6d8f400 SL |
218 | /* read the RTC alarm registers all at once */ |
219 | tmp = i2c_transfer(to_i2c_adapter(fm3130->client->dev.parent), | |
220 | &fm3130->msg[2], 2); | |
221 | if (tmp != 2) { | |
222 | dev_err(dev, "%s error %d\n", "read", tmp); | |
223 | return -EIO; | |
224 | } | |
225 | dev_dbg(dev, "alarm read %02x %02x %02x %02x %02x\n", | |
226 | fm3130->regs[FM3130_ALARM_SECONDS], | |
227 | fm3130->regs[FM3130_ALARM_MINUTES], | |
228 | fm3130->regs[FM3130_ALARM_HOURS], | |
229 | fm3130->regs[FM3130_ALARM_DATE], | |
230 | fm3130->regs[FM3130_ALARM_MONTHS]); | |
231 | ||
fe20ba70 AB |
232 | tm->tm_sec = bcd2bin(fm3130->regs[FM3130_ALARM_SECONDS] & 0x7F); |
233 | tm->tm_min = bcd2bin(fm3130->regs[FM3130_ALARM_MINUTES] & 0x7F); | |
234 | tm->tm_hour = bcd2bin(fm3130->regs[FM3130_ALARM_HOURS] & 0x3F); | |
235 | tm->tm_mday = bcd2bin(fm3130->regs[FM3130_ALARM_DATE] & 0x3F); | |
236 | tm->tm_mon = bcd2bin(fm3130->regs[FM3130_ALARM_MONTHS] & 0x1F); | |
f3f99cf3 | 237 | |
c6d8f400 SL |
238 | if (tm->tm_mon > 0) |
239 | tm->tm_mon -= 1; /* RTC is 1-12, tm_mon is 0-11 */ | |
f3f99cf3 | 240 | |
c6d8f400 SL |
241 | dev_dbg(dev, "%s secs=%d, mins=%d, " |
242 | "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", | |
243 | "read alarm", tm->tm_sec, tm->tm_min, | |
244 | tm->tm_hour, tm->tm_mday, | |
245 | tm->tm_mon, tm->tm_year, tm->tm_wday); | |
246 | ||
f3f99cf3 SM |
247 | /* check if alarm enabled */ |
248 | fm3130->regs[FM3130_RTC_CONTROL] = | |
249 | i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL); | |
250 | ||
251 | if ((fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AEN) && | |
252 | (~fm3130->regs[FM3130_RTC_CONTROL] & | |
253 | FM3130_RTC_CONTROL_BIT_CAL)) { | |
254 | alrm->enabled = 1; | |
255 | } | |
256 | ||
c6d8f400 SL |
257 | return 0; |
258 | } | |
259 | ||
260 | static int fm3130_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) | |
261 | { | |
262 | struct fm3130 *fm3130 = dev_get_drvdata(dev); | |
263 | struct rtc_time *tm = &alrm->time; | |
264 | int i; | |
265 | ||
266 | dev_dbg(dev, "%s secs=%d, mins=%d, " | |
267 | "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", | |
268 | "write alarm", tm->tm_sec, tm->tm_min, | |
269 | tm->tm_hour, tm->tm_mday, | |
270 | tm->tm_mon, tm->tm_year, tm->tm_wday); | |
271 | ||
f3f99cf3 SM |
272 | fm3130->regs[FM3130_ALARM_SECONDS] = |
273 | (tm->tm_sec != -1) ? bin2bcd(tm->tm_sec) : 0x80; | |
c6d8f400 | 274 | |
f3f99cf3 SM |
275 | fm3130->regs[FM3130_ALARM_MINUTES] = |
276 | (tm->tm_min != -1) ? bin2bcd(tm->tm_min) : 0x80; | |
c6d8f400 | 277 | |
f3f99cf3 SM |
278 | fm3130->regs[FM3130_ALARM_HOURS] = |
279 | (tm->tm_hour != -1) ? bin2bcd(tm->tm_hour) : 0x80; | |
c6d8f400 | 280 | |
f3f99cf3 SM |
281 | fm3130->regs[FM3130_ALARM_DATE] = |
282 | (tm->tm_mday != -1) ? bin2bcd(tm->tm_mday) : 0x80; | |
c6d8f400 | 283 | |
f3f99cf3 SM |
284 | fm3130->regs[FM3130_ALARM_MONTHS] = |
285 | (tm->tm_mon != -1) ? bin2bcd(tm->tm_mon + 1) : 0x80; | |
c6d8f400 SL |
286 | |
287 | dev_dbg(dev, "alarm write %02x %02x %02x %02x %02x\n", | |
288 | fm3130->regs[FM3130_ALARM_SECONDS], | |
289 | fm3130->regs[FM3130_ALARM_MINUTES], | |
290 | fm3130->regs[FM3130_ALARM_HOURS], | |
291 | fm3130->regs[FM3130_ALARM_DATE], | |
292 | fm3130->regs[FM3130_ALARM_MONTHS]); | |
293 | /* Writing time registers, we don't support multibyte transfers */ | |
294 | for (i = 0; i < FM3130_ALARM_REGS; i++) { | |
295 | i2c_smbus_write_byte_data(fm3130->client, | |
296 | FM3130_ALARM_SECONDS + i, | |
297 | fm3130->regs[FM3130_ALARM_SECONDS + i]); | |
298 | } | |
299 | fm3130->regs[FM3130_RTC_CONTROL] = | |
300 | i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL); | |
f3f99cf3 SM |
301 | |
302 | /* enable or disable alarm */ | |
c6d8f400 SL |
303 | if (alrm->enabled) { |
304 | i2c_smbus_write_byte_data(fm3130->client, FM3130_RTC_CONTROL, | |
305 | (fm3130->regs[FM3130_RTC_CONTROL] & | |
306 | ~(FM3130_RTC_CONTROL_BIT_CAL)) | | |
307 | FM3130_RTC_CONTROL_BIT_AEN); | |
308 | } else { | |
309 | i2c_smbus_write_byte_data(fm3130->client, FM3130_RTC_CONTROL, | |
310 | fm3130->regs[FM3130_RTC_CONTROL] & | |
f3f99cf3 SM |
311 | ~(FM3130_RTC_CONTROL_BIT_CAL) & |
312 | ~(FM3130_RTC_CONTROL_BIT_AEN)); | |
c6d8f400 | 313 | } |
f3f99cf3 SM |
314 | |
315 | /* We assume here that data is valid once written */ | |
316 | if (!fm3130->alarm_valid) | |
317 | fm3130->alarm_valid = 1; | |
318 | ||
c6d8f400 SL |
319 | return 0; |
320 | } | |
321 | ||
f3f99cf3 SM |
322 | static int fm3130_alarm_irq_enable(struct device *dev, unsigned int enabled) |
323 | { | |
324 | struct fm3130 *fm3130 = dev_get_drvdata(dev); | |
325 | int ret = 0; | |
326 | ||
327 | fm3130->regs[FM3130_RTC_CONTROL] = | |
328 | i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL); | |
329 | ||
330 | dev_dbg(dev, "alarm_irq_enable: enable=%d, FM3130_RTC_CONTROL=%02x\n", | |
331 | enabled, fm3130->regs[FM3130_RTC_CONTROL]); | |
332 | ||
333 | switch (enabled) { | |
334 | case 0: /* alarm off */ | |
335 | ret = i2c_smbus_write_byte_data(fm3130->client, | |
336 | FM3130_RTC_CONTROL, fm3130->regs[FM3130_RTC_CONTROL] & | |
337 | ~(FM3130_RTC_CONTROL_BIT_CAL) & | |
338 | ~(FM3130_RTC_CONTROL_BIT_AEN)); | |
339 | break; | |
340 | case 1: /* alarm on */ | |
341 | ret = i2c_smbus_write_byte_data(fm3130->client, | |
342 | FM3130_RTC_CONTROL, (fm3130->regs[FM3130_RTC_CONTROL] & | |
343 | ~(FM3130_RTC_CONTROL_BIT_CAL)) | | |
344 | FM3130_RTC_CONTROL_BIT_AEN); | |
345 | break; | |
346 | default: | |
347 | ret = -EINVAL; | |
348 | break; | |
349 | } | |
350 | ||
351 | return ret; | |
352 | } | |
353 | ||
c6d8f400 SL |
354 | static const struct rtc_class_ops fm3130_rtc_ops = { |
355 | .read_time = fm3130_get_time, | |
356 | .set_time = fm3130_set_time, | |
357 | .read_alarm = fm3130_read_alarm, | |
358 | .set_alarm = fm3130_set_alarm, | |
f3f99cf3 | 359 | .alarm_irq_enable = fm3130_alarm_irq_enable, |
c6d8f400 SL |
360 | }; |
361 | ||
362 | static struct i2c_driver fm3130_driver; | |
363 | ||
364 | static int __devinit fm3130_probe(struct i2c_client *client, | |
365 | const struct i2c_device_id *id) | |
366 | { | |
367 | struct fm3130 *fm3130; | |
368 | int err = -ENODEV; | |
369 | int tmp; | |
370 | struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); | |
371 | ||
372 | if (!i2c_check_functionality(adapter, | |
373 | I2C_FUNC_I2C | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) | |
374 | return -EIO; | |
375 | ||
376 | fm3130 = kzalloc(sizeof(struct fm3130), GFP_KERNEL); | |
377 | ||
378 | if (!fm3130) | |
379 | return -ENOMEM; | |
380 | ||
381 | fm3130->client = client; | |
382 | i2c_set_clientdata(client, fm3130); | |
383 | fm3130->reg_addr_time = FM3130_RTC_SECONDS; | |
384 | fm3130->reg_addr_alarm = FM3130_ALARM_SECONDS; | |
385 | ||
386 | /* Messages to read time */ | |
387 | fm3130->msg[0].addr = client->addr; | |
388 | fm3130->msg[0].flags = 0; | |
389 | fm3130->msg[0].len = 1; | |
390 | fm3130->msg[0].buf = &fm3130->reg_addr_time; | |
391 | ||
392 | fm3130->msg[1].addr = client->addr; | |
393 | fm3130->msg[1].flags = I2C_M_RD; | |
394 | fm3130->msg[1].len = FM3130_CLOCK_REGS; | |
395 | fm3130->msg[1].buf = &fm3130->regs[FM3130_RTC_SECONDS]; | |
396 | ||
397 | /* Messages to read alarm */ | |
398 | fm3130->msg[2].addr = client->addr; | |
399 | fm3130->msg[2].flags = 0; | |
400 | fm3130->msg[2].len = 1; | |
401 | fm3130->msg[2].buf = &fm3130->reg_addr_alarm; | |
402 | ||
403 | fm3130->msg[3].addr = client->addr; | |
404 | fm3130->msg[3].flags = I2C_M_RD; | |
405 | fm3130->msg[3].len = FM3130_ALARM_REGS; | |
406 | fm3130->msg[3].buf = &fm3130->regs[FM3130_ALARM_SECONDS]; | |
407 | ||
f3f99cf3 | 408 | fm3130->alarm_valid = 0; |
c6d8f400 SL |
409 | fm3130->data_valid = 0; |
410 | ||
411 | tmp = i2c_transfer(adapter, fm3130->msg, 4); | |
412 | if (tmp != 4) { | |
413 | pr_debug("read error %d\n", tmp); | |
414 | err = -EIO; | |
415 | goto exit_free; | |
416 | } | |
417 | ||
418 | fm3130->regs[FM3130_RTC_CONTROL] = | |
419 | i2c_smbus_read_byte_data(client, FM3130_RTC_CONTROL); | |
420 | fm3130->regs[FM3130_CAL_CONTROL] = | |
421 | i2c_smbus_read_byte_data(client, FM3130_CAL_CONTROL); | |
422 | ||
c6d8f400 | 423 | /* Disabling calibration mode */ |
f4b51628 | 424 | if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_CAL) { |
c6d8f400 SL |
425 | i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL, |
426 | fm3130->regs[FM3130_RTC_CONTROL] & | |
427 | ~(FM3130_RTC_CONTROL_BIT_CAL)); | |
428 | dev_warn(&client->dev, "Disabling calibration mode!\n"); | |
f4b51628 | 429 | } |
c6d8f400 SL |
430 | |
431 | /* Disabling read and write modes */ | |
432 | if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_WRITE || | |
f4b51628 | 433 | fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_READ) { |
c6d8f400 SL |
434 | i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL, |
435 | fm3130->regs[FM3130_RTC_CONTROL] & | |
436 | ~(FM3130_RTC_CONTROL_BIT_READ | | |
437 | FM3130_RTC_CONTROL_BIT_WRITE)); | |
438 | dev_warn(&client->dev, "Disabling READ or WRITE mode!\n"); | |
f4b51628 | 439 | } |
c6d8f400 SL |
440 | |
441 | /* oscillator off? turn it on, so clock can tick. */ | |
442 | if (fm3130->regs[FM3130_CAL_CONTROL] & FM3130_CAL_CONTROL_BIT_nOSCEN) | |
443 | i2c_smbus_write_byte_data(client, FM3130_CAL_CONTROL, | |
444 | fm3130->regs[FM3130_CAL_CONTROL] & | |
445 | ~(FM3130_CAL_CONTROL_BIT_nOSCEN)); | |
446 | ||
f3f99cf3 SM |
447 | /* low battery? clear flag, and warn */ |
448 | if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_LB) { | |
449 | i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL, | |
450 | fm3130->regs[FM3130_RTC_CONTROL] & | |
451 | ~(FM3130_RTC_CONTROL_BIT_LB)); | |
c6d8f400 | 452 | dev_warn(&client->dev, "Low battery!\n"); |
f3f99cf3 | 453 | } |
c6d8f400 | 454 | |
f3f99cf3 | 455 | /* check if Power On Reset bit is set */ |
c6d8f400 SL |
456 | if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_POR) { |
457 | i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL, | |
458 | fm3130->regs[FM3130_RTC_CONTROL] & | |
459 | ~FM3130_RTC_CONTROL_BIT_POR); | |
f3f99cf3 | 460 | dev_dbg(&client->dev, "POR bit is set\n"); |
c6d8f400 SL |
461 | } |
462 | /* ACS is controlled by alarm */ | |
463 | i2c_smbus_write_byte_data(client, FM3130_ALARM_WP_CONTROL, 0x80); | |
464 | ||
f3f99cf3 SM |
465 | /* alarm registers sanity check */ |
466 | tmp = bcd2bin(fm3130->regs[FM3130_RTC_SECONDS] & 0x7f); | |
467 | if (tmp > 59) | |
468 | goto bad_alarm; | |
469 | ||
fe20ba70 | 470 | tmp = bcd2bin(fm3130->regs[FM3130_RTC_MINUTES] & 0x7f); |
f3f99cf3 SM |
471 | if (tmp > 59) |
472 | goto bad_alarm; | |
473 | ||
474 | tmp = bcd2bin(fm3130->regs[FM3130_RTC_HOURS] & 0x3f); | |
475 | if (tmp > 23) | |
476 | goto bad_alarm; | |
c6d8f400 | 477 | |
fe20ba70 | 478 | tmp = bcd2bin(fm3130->regs[FM3130_RTC_DATE] & 0x3f); |
c6d8f400 | 479 | if (tmp == 0 || tmp > 31) |
f3f99cf3 | 480 | goto bad_alarm; |
c6d8f400 | 481 | |
fe20ba70 | 482 | tmp = bcd2bin(fm3130->regs[FM3130_RTC_MONTHS] & 0x1f); |
c6d8f400 | 483 | if (tmp == 0 || tmp > 12) |
f3f99cf3 | 484 | goto bad_alarm; |
c6d8f400 | 485 | |
f3f99cf3 SM |
486 | fm3130->alarm_valid = 1; |
487 | ||
488 | bad_alarm: | |
489 | ||
490 | /* clock registers sanity chek */ | |
491 | tmp = bcd2bin(fm3130->regs[FM3130_RTC_SECONDS] & 0x7f); | |
492 | if (tmp > 59) | |
493 | goto bad_clock; | |
494 | ||
495 | tmp = bcd2bin(fm3130->regs[FM3130_RTC_MINUTES] & 0x7f); | |
496 | if (tmp > 59) | |
497 | goto bad_clock; | |
498 | ||
499 | tmp = bcd2bin(fm3130->regs[FM3130_RTC_HOURS] & 0x3f); | |
500 | if (tmp > 23) | |
501 | goto bad_clock; | |
502 | ||
503 | tmp = bcd2bin(fm3130->regs[FM3130_RTC_DAY] & 0x7); | |
504 | if (tmp == 0 || tmp > 7) | |
505 | goto bad_clock; | |
506 | ||
507 | tmp = bcd2bin(fm3130->regs[FM3130_RTC_DATE] & 0x3f); | |
508 | if (tmp == 0 || tmp > 31) | |
509 | goto bad_clock; | |
510 | ||
511 | tmp = bcd2bin(fm3130->regs[FM3130_RTC_MONTHS] & 0x1f); | |
512 | if (tmp == 0 || tmp > 12) | |
513 | goto bad_clock; | |
c6d8f400 SL |
514 | |
515 | fm3130->data_valid = 1; | |
516 | ||
f3f99cf3 SM |
517 | bad_clock: |
518 | ||
519 | if (!fm3130->data_valid || !fm3130->alarm_valid) | |
c6d8f400 SL |
520 | dev_dbg(&client->dev, |
521 | "%s: %02x %02x %02x %02x %02x %02x %02x %02x" | |
522 | "%02x %02x %02x %02x %02x %02x %02x\n", | |
523 | "bogus registers", | |
524 | fm3130->regs[0], fm3130->regs[1], | |
525 | fm3130->regs[2], fm3130->regs[3], | |
526 | fm3130->regs[4], fm3130->regs[5], | |
527 | fm3130->regs[6], fm3130->regs[7], | |
528 | fm3130->regs[8], fm3130->regs[9], | |
529 | fm3130->regs[0xa], fm3130->regs[0xb], | |
530 | fm3130->regs[0xc], fm3130->regs[0xd], | |
531 | fm3130->regs[0xe]); | |
532 | ||
533 | /* We won't bail out here because we just got invalid data. | |
534 | Time setting from u-boot doesn't work anyway */ | |
535 | fm3130->rtc = rtc_device_register(client->name, &client->dev, | |
536 | &fm3130_rtc_ops, THIS_MODULE); | |
537 | if (IS_ERR(fm3130->rtc)) { | |
538 | err = PTR_ERR(fm3130->rtc); | |
539 | dev_err(&client->dev, | |
540 | "unable to register the class device\n"); | |
541 | goto exit_free; | |
542 | } | |
543 | return 0; | |
544 | exit_free: | |
545 | kfree(fm3130); | |
546 | return err; | |
547 | } | |
548 | ||
549 | static int __devexit fm3130_remove(struct i2c_client *client) | |
550 | { | |
551 | struct fm3130 *fm3130 = i2c_get_clientdata(client); | |
552 | ||
553 | rtc_device_unregister(fm3130->rtc); | |
554 | kfree(fm3130); | |
555 | return 0; | |
556 | } | |
557 | ||
558 | static struct i2c_driver fm3130_driver = { | |
559 | .driver = { | |
560 | .name = "rtc-fm3130", | |
561 | .owner = THIS_MODULE, | |
562 | }, | |
563 | .probe = fm3130_probe, | |
564 | .remove = __devexit_p(fm3130_remove), | |
565 | .id_table = fm3130_id, | |
566 | }; | |
567 | ||
0abc9201 | 568 | module_i2c_driver(fm3130_driver); |
c6d8f400 SL |
569 | |
570 | MODULE_DESCRIPTION("RTC driver for FM3130"); | |
571 | MODULE_AUTHOR("Sergey Lapin <slapin@ossfans.org>"); | |
572 | MODULE_LICENSE("GPL"); | |
573 |