1 |
/** |
2 |
* drivers/cbus/retu-rtc.c |
3 |
* |
4 |
* Support for Retu RTC |
5 |
* |
6 |
* Copyright (C) 2004, 2005 Nokia Corporation |
7 |
* |
8 |
* Written by Paul Mundt <> and |
9 |
* Igor Stoppa <> |
10 |
* |
11 |
* The Retu RTC is essentially a partial read-only RTC that gives us Retu's |
12 |
* idea of what time actually is. It's left as a userspace excercise to map |
13 |
* this back to time in the real world and ensure that calibration settings |
14 |
* are sane to compensate for any horrible drift (on account of not being able |
15 |
* to set the clock to anything). |
16 |
* |
17 |
* Days are semi-writeable. Namely, Retu will only track 255 days for us |
18 |
* consecutively, after which the counter is explicitly stuck at 255 until |
19 |
* someone comes along and clears it with a write. In the event that no one |
20 |
* comes along and clears it, we no longer have any idea what day it is. |
21 |
* |
22 |
* This file is subject to the terms and conditions of the GNU General |
23 |
* Public License. See the file "COPYING" in the main directory of this |
24 |
* archive for more details. |
25 |
* |
26 |
* This program is distributed in the hope that it will be useful, |
27 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
28 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
29 |
* GNU General Public License for more details. |
30 |
* |
31 |
* You should have received a copy of the GNU General Public License |
32 |
* along with this program; if not, write to the Free Software |
33 |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
34 |
*/ |
35 |
|
36 |
#include |
37 |
#include |
38 |
#include |
39 |
#include |
40 |
#include |
41 |
#include |
42 |
#include |
43 |
#include |
44 |
|
45 |
#include "cbus.h" |
46 |
#include "retu.h" |
47 |
|
48 |
static struct mutex retu_rtc_mutex;
|
49 |
static u16 retu_rtc_alarm_expired;
|
50 |
static u16 retu_rtc_reset_occurred;
|
51 |
|
52 |
static DECLARE_COMPLETION(retu_rtc_exited);
|
53 |
static DECLARE_COMPLETION(retu_rtc_sync);
|
54 |
|
55 |
static void retu_rtc_barrier(void);
|
56 |
|
57 |
static void retu_rtc_device_release(struct device *dev)
|
58 |
{ |
59 |
complete(&retu_rtc_exited);
|
60 |
} |
61 |
|
62 |
static ssize_t retu_rtc_time_show(struct device *dev, struct device_attribute *attr,
|
63 |
char *buf)
|
64 |
{ |
65 |
u16 dsr, hmr, dsr2;
|
66 |
|
67 |
mutex_lock(&retu_rtc_mutex);
|
68 |
|
69 |
do {
|
70 |
u16 dummy;
|
71 |
|
72 |
/*
|
73 |
* Not being in_interrupt() for a retu rtc IRQ, we need to |
74 |
* read twice for consistency.. |
75 |
*/ |
76 |
dummy = retu_read_reg(RETU_REG_RTCDSR);
|
77 |
dsr = retu_read_reg(RETU_REG_RTCDSR);
|
78 |
|
79 |
dummy = retu_read_reg(RETU_REG_RTCHMR);
|
80 |
hmr = retu_read_reg(RETU_REG_RTCHMR);
|
81 |
|
82 |
dummy = retu_read_reg(RETU_REG_RTCDSR);
|
83 |
dsr2 = retu_read_reg(RETU_REG_RTCDSR);
|
84 |
} while ((dsr != dsr2));
|
85 |
|
86 |
mutex_unlock(&retu_rtc_mutex);
|
87 |
|
88 |
/*
|
89 |
* Format a 32-bit date-string for userspace |
90 |
* |
91 |
* days | hours | minutes | seconds |
92 |
* |
93 |
* 8 bits for each. |
94 |
* |
95 |
* This mostly sucks because days and seconds are tracked in RTCDSR |
96 |
* while hours and minutes are tracked in RTCHMR. And yes, there |
97 |
* really are no words that can describe an 8 bit day register (or |
98 |
* rather, none that will be reprinted here). |
99 |
*/ |
100 |
return sprintf(buf, "0x%08x\n", (((dsr >> 8) & 0xff) << 24) |
|
101 |
(((hmr >> 8) & 0x1f) << 16) |
|
102 |
((hmr & 0x3f) << 8) | (dsr & 0x3f));
|
103 |
} |
104 |
|
105 |
static ssize_t retu_rtc_time_store(struct device *dev, struct device_attribute *attr,
|
106 |
const char *buf, size_t count)
|
107 |
{ |
108 |
mutex_lock(&retu_rtc_mutex);
|
109 |
/*
|
110 |
* Writing anything to the day counter forces it to 0 |
111 |
* The seconds counter would be cleared by resetting the minutes counter, |
112 |
* however this won't happen, since we are using the hh:mm counters as |
113 |
* a set of free running counters and the day counter as a multiple |
114 |
* overflow holder. |
115 |
*/ |
116 |
|
117 |
/* Reset day counter, but keep Temperature Shutdown state */
|
118 |
retu_write_reg(RETU_REG_RTCDSR,
|
119 |
retu_read_reg(RETU_REG_RTCDSR) & (1 << 6));
|
120 |
|
121 |
mutex_unlock(&retu_rtc_mutex);
|
122 |
|
123 |
return count;
|
124 |
} |
125 |
|
126 |
static DEVICE_ATTR(time, S_IRUGO | S_IWUSR, retu_rtc_time_show,
|
127 |
retu_rtc_time_store);
|
128 |
|
129 |
|
130 |
static ssize_t retu_rtc_reset_show(struct device *dev, struct device_attribute *attr, char *buf)
|
131 |
{ |
132 |
/*
|
133 |
* Returns the status of the rtc |
134 |
* |
135 |
* 0: no reset has occurred or the status has been cleared |
136 |
* 1: a reset has occurred |
137 |
* |
138 |
* RTC needs to be reset only when both main battery |
139 |
* _AND_ backup battery are discharged |
140 |
*/ |
141 |
return sprintf(buf, "%u\n", retu_rtc_reset_occurred);
|
142 |
} |
143 |
|
144 |
static void retu_rtc_do_reset(void)
|
145 |
{ |
146 |
u16 ccr1;
|
147 |
|
148 |
ccr1 = retu_read_reg(RETU_REG_CC1);
|
149 |
/* RTC in reset */
|
150 |
retu_write_reg(RETU_REG_CC1, ccr1 | 0x0001);
|
151 |
/* RTC in normal operating mode */
|
152 |
retu_write_reg(RETU_REG_CC1, ccr1 & ~0x0001);
|
153 |
|
154 |
retu_rtc_barrier();
|
155 |
/* Disable alarm and RTC WD */
|
156 |
retu_write_reg(RETU_REG_RTCHMAR, 0x7f3f);
|
157 |
/* Set Calibration register to default value */
|
158 |
retu_write_reg(RETU_REG_RTCCALR, 0x00c0);
|
159 |
|
160 |
retu_rtc_alarm_expired = 0;
|
161 |
retu_rtc_reset_occurred = 1;
|
162 |
} |
163 |
|
164 |
static ssize_t retu_rtc_reset_store(struct device *dev, struct device_attribute *attr,
|
165 |
const char *buf, size_t count)
|
166 |
{ |
167 |
unsigned choice;
|
168 |
|
169 |
if(sscanf(buf, "%u", &choice) != 1)
|
170 |
return count;
|
171 |
mutex_lock(&retu_rtc_mutex);
|
172 |
if (choice == 0)
|
173 |
retu_rtc_reset_occurred = 0;
|
174 |
else if (choice == 1)
|
175 |
retu_rtc_do_reset();
|
176 |
mutex_unlock(&retu_rtc_mutex);
|
177 |
return count;
|
178 |
} |
179 |
|
180 |
static DEVICE_ATTR(reset, S_IRUGO | S_IWUSR, retu_rtc_reset_show,
|
181 |
retu_rtc_reset_store);
|
182 |
|
183 |
static ssize_t retu_rtc_alarm_show(struct device *dev, struct device_attribute *attr,
|
184 |
char *buf)
|
185 |
{ |
186 |
u16 chmar;
|
187 |
ssize_t retval;
|
188 |
|
189 |
mutex_lock(&retu_rtc_mutex);
|
190 |
/*
|
191 |
* Format a 16-bit date-string for userspace |
192 |
* |
193 |
* hours | minutes |
194 |
* 8 bits for each. |
195 |
*/ |
196 |
chmar = retu_read_reg(RETU_REG_RTCHMAR);
|
197 |
/* No shifting needed, only masking unrelated bits */
|
198 |
retval = sprintf(buf, "0x%04x\n", chmar & 0x1f3f);
|
199 |
mutex_unlock(&retu_rtc_mutex);
|
200 |
|
201 |
return retval;
|
202 |
} |
203 |
|
204 |
static ssize_t retu_rtc_alarm_store(struct device *dev, struct device_attribute *attr,
|
205 |
const char *buf, size_t count)
|
206 |
{ |
207 |
u16 chmar;
|
208 |
unsigned alrm;
|
209 |
unsigned hours;
|
210 |
unsigned minutes;
|
211 |
|
212 |
mutex_lock(&retu_rtc_mutex);
|
213 |
|
214 |
if(sscanf(buf, "%x", &alrm) != 1)
|
215 |
return count;
|
216 |
hours = (alrm >> 8) & 0x001f;
|
217 |
minutes = (alrm >> 0) & 0x003f;
|
218 |
if ((hours < 24 && minutes < 60) || (hours == 24 && minutes == 60)) {
|
219 |
/*
|
220 |
* OK, the time format for the alarm is valid (including the |
221 |
* disabling values) |
222 |
*/ |
223 |
/* Keeps the RTC watchdog status */
|
224 |
chmar = retu_read_reg(RETU_REG_RTCHMAR) & 0x6000;
|
225 |
chmar |= alrm & 0x1f3f; /* Stores the requested alarm */
|
226 |
retu_rtc_barrier();
|
227 |
retu_write_reg(RETU_REG_RTCHMAR, chmar);
|
228 |
/* If the alarm is being disabled */
|
229 |
if (hours == 24 && minutes == 60) {
|
230 |
/* disable the interrupt */
|
231 |
retu_disable_irq(RETU_INT_RTCA);
|
232 |
retu_rtc_alarm_expired = 0;
|
233 |
} else
|
234 |
/* enable the interrupt */
|
235 |
retu_enable_irq(RETU_INT_RTCA);
|
236 |
}
|
237 |
mutex_unlock(&retu_rtc_mutex);
|
238 |
|
239 |
return count;
|
240 |
} |
241 |
|
242 |
static DEVICE_ATTR(alarm, S_IRUGO | S_IWUSR, retu_rtc_alarm_show,
|
243 |
retu_rtc_alarm_store);
|
244 |
|
245 |
static ssize_t retu_rtc_alarm_expired_show(struct device *dev, struct device_attribute *attr,
|
246 |
char *buf)
|
247 |
{ |
248 |
ssize_t retval;
|
249 |
|
250 |
retval = sprintf(buf, "%u\n", retu_rtc_alarm_expired);
|
251 |
|
252 |
return retval;
|
253 |
} |
254 |
|
255 |
static ssize_t retu_rtc_alarm_expired_store(struct device *dev, struct device_attribute *attr,
|
256 |
const char *buf, size_t count)
|
257 |
{ |
258 |
retu_rtc_alarm_expired = 0;
|
259 |
|
260 |
return count;
|
261 |
} |
262 |
|
263 |
static DEVICE_ATTR(alarm_expired, S_IRUGO | S_IWUSR, retu_rtc_alarm_expired_show,
|
264 |
retu_rtc_alarm_expired_store);
|
265 |
|
266 |
|
267 |
static ssize_t retu_rtc_cal_show(struct device *dev, struct device_attribute *attr,
|
268 |
char *buf)
|
269 |
{ |
270 |
u16 rtccalr1;
|
271 |
|
272 |
mutex_lock(&retu_rtc_mutex);
|
273 |
rtccalr1 = retu_read_reg(RETU_REG_RTCCALR);
|
274 |
mutex_unlock(&retu_rtc_mutex);
|
275 |
|
276 |
/*
|
277 |
* Shows the status of the Calibration Register. |
278 |
* |
279 |
* Default, after power loss: 0x0000 |
280 |
* Default, for R&D: 0x00C0 |
281 |
* Default, for factory: 0x00?? |
282 |
* |
283 |
*/ |
284 |
return sprintf(buf, "0x%04x\n", rtccalr1 & 0x00ff);
|
285 |
} |
286 |
|
287 |
static ssize_t retu_rtc_cal_store(struct device *dev, struct device_attribute *attr,
|
288 |
const char *buf, size_t count)
|
289 |
{ |
290 |
unsigned calibration_value;
|
291 |
|
292 |
if (sscanf(buf, "%x", &calibration_value) != 1)
|
293 |
return count;
|
294 |
|
295 |
mutex_lock(&retu_rtc_mutex);
|
296 |
retu_rtc_barrier();
|
297 |
retu_write_reg(RETU_REG_RTCCALR, calibration_value & 0x00ff);
|
298 |
mutex_unlock(&retu_rtc_mutex);
|
299 |
|
300 |
return count;
|
301 |
} |
302 |
|
303 |
static DEVICE_ATTR(cal, S_IRUGO | S_IWUSR, retu_rtc_cal_show,
|
304 |
retu_rtc_cal_store);
|
305 |
|
306 |
static struct device_driver retu_rtc_driver;
|
307 |
|
308 |
static void retu_rtca_disable(void)
|
309 |
{ |
310 |
retu_disable_irq(RETU_INT_RTCA);
|
311 |
retu_rtc_alarm_expired = 1;
|
312 |
retu_rtc_barrier();
|
313 |
retu_write_reg(RETU_REG_RTCHMAR, (24 << 8) | 60);
|
314 |
} |
315 |
|
316 |
static void retu_rtca_expired(struct work_struct *unused)
|
317 |
{ |
318 |
retu_rtca_disable();
|
319 |
sysfs_notify(&retu_rtc_driver.kobj, NULL, "alarm_expired");
|
320 |
} |
321 |
|
322 |
DECLARE_WORK(retu_rtca_work, retu_rtca_expired);
|
323 |
|
324 |
/* |
325 |
* RTCHMR RTCHMAR RTCCAL must be accessed within 0.9 s since the seconds |
326 |
* interrupt has been signaled in the IDR register |
327 |
*/ |
328 |
static void retu_rtcs_interrupt(unsigned long unused)
|
329 |
{ |
330 |
retu_ack_irq(RETU_INT_RTCS);
|
331 |
complete(&retu_rtc_sync);
|
332 |
} |
333 |
|
334 |
static void retu_rtca_interrupt(unsigned long unused)
|
335 |
{ |
336 |
retu_ack_irq(RETU_INT_RTCA);
|
337 |
schedule_work(&retu_rtca_work);
|
338 |
} |
339 |
|
340 |
static int retu_rtc_init_irq(void)
|
341 |
{ |
342 |
int ret;
|
343 |
|
344 |
ret = retu_request_irq(RETU_INT_RTCS, retu_rtcs_interrupt, 0, "RTCS");
|
345 |
if (ret != 0)
|
346 |
return ret;
|
347 |
/*
|
348 |
* We will take care of enabling and disabling the interrupt |
349 |
* elsewhere, so leave it off by default.. |
350 |
*/ |
351 |
retu_disable_irq(RETU_INT_RTCS);
|
352 |
|
353 |
ret = retu_request_irq(RETU_INT_RTCA, retu_rtca_interrupt, 0, "RTCA");
|
354 |
if (ret != 0) {
|
355 |
retu_free_irq(RETU_INT_RTCS);
|
356 |
return ret;
|
357 |
}
|
358 |
retu_disable_irq(RETU_INT_RTCA);
|
359 |
|
360 |
return 0;
|
361 |
} |
362 |
|
363 |
|
364 |
static int __devinit retu_rtc_probe(struct device *dev)
|
365 |
{ |
366 |
int r;
|
367 |
|
368 |
retu_rtc_alarm_expired = retu_read_reg(RETU_REG_IDR) &
|
369 |
(0x1 << RETU_INT_RTCA);
|
370 |
|
371 |
if ((r = retu_rtc_init_irq()) != 0)
|
372 |
return r;
|
373 |
|
374 |
mutex_init(&retu_rtc_mutex);
|
375 |
|
376 |
/* If the calibration register is zero, we've probably lost
|
377 |
* power */ |
378 |
if (retu_read_reg(RETU_REG_RTCCALR) & 0x00ff)
|
379 |
retu_rtc_reset_occurred = 0;
|
380 |
else
|
381 |
retu_rtc_do_reset();
|
382 |
|
383 |
if ((r = device_create_file(dev, &dev_attr_time)) != 0)
|
384 |
return r;
|
385 |
else if ((r = device_create_file(dev, &dev_attr_reset)) != 0)
|
386 |
goto err_unregister_time;
|
387 |
else if ((r = device_create_file(dev, &dev_attr_alarm)) != 0)
|
388 |
goto err_unregister_reset;
|
389 |
else if ((r = device_create_file(dev, &dev_attr_alarm_expired)) != 0)
|
390 |
goto err_unregister_alarm;
|
391 |
else if ((r = device_create_file(dev, &dev_attr_cal)) != 0)
|
392 |
goto err_unregister_alarm_expired;
|
393 |
else
|
394 |
return r;
|
395 |
|
396 |
err_unregister_alarm_expired: |
397 |
device_remove_file(dev, &dev_attr_alarm_expired);
|
398 |
err_unregister_alarm: |
399 |
device_remove_file(dev, &dev_attr_alarm);
|
400 |
err_unregister_reset: |
401 |
device_remove_file(dev, &dev_attr_reset);
|
402 |
err_unregister_time: |
403 |
device_remove_file(dev, &dev_attr_time);
|
404 |
return r;
|
405 |
} |
406 |
|
407 |
static int __devexit retu_rtc_remove(struct device *dev)
|
408 |
{ |
409 |
retu_disable_irq(RETU_INT_RTCS);
|
410 |
retu_free_irq(RETU_INT_RTCS);
|
411 |
retu_free_irq(RETU_INT_RTCA);
|
412 |
device_remove_file(dev, &dev_attr_cal);
|
413 |
device_remove_file(dev, &dev_attr_alarm_expired);
|
414 |
device_remove_file(dev, &dev_attr_alarm);
|
415 |
device_remove_file(dev, &dev_attr_reset);
|
416 |
device_remove_file(dev, &dev_attr_time);
|
417 |
return 0;
|
418 |
} |
419 |
|
420 |
static struct device_driver retu_rtc_driver = {
|
421 |
.name = "retu-rtc",
|
422 |
.bus = &platform_bus_type,
|
423 |
.probe = retu_rtc_probe,
|
424 |
.remove = __devexit_p(retu_rtc_remove),
|
425 |
}; |
426 |
|
427 |
static struct platform_device retu_rtc_device = {
|
428 |
.name = "retu-rtc",
|
429 |
.id = -1,
|
430 |
.dev = {
|
431 |
.release = retu_rtc_device_release,
|
432 |
},
|
433 |
}; |
434 |
|
435 |
/* This function provides syncronization with the RTCS interrupt handler */ |
436 |
static void retu_rtc_barrier(void)
|
437 |
{ |
438 |
init_completion(&retu_rtc_sync);
|
439 |
retu_ack_irq(RETU_INT_RTCS);
|
440 |
retu_enable_irq(RETU_INT_RTCS);
|
441 |
wait_for_completion(&retu_rtc_sync);
|
442 |
retu_disable_irq(RETU_INT_RTCS);
|
443 |
} |
444 |
|
445 |
static int __init retu_rtc_init(void)
|
446 |
{ |
447 |
int ret;
|
448 |
|
449 |
init_completion(&retu_rtc_exited);
|
450 |
|
451 |
if ((ret = driver_register(&retu_rtc_driver)) != 0)
|
452 |
return ret;
|
453 |
|
454 |
if ((ret = platform_device_register(&retu_rtc_device)) != 0)
|
455 |
goto err_unregister_driver;
|
456 |
|
457 |
return 0;
|
458 |
|
459 |
err_unregister_driver: |
460 |
driver_unregister(&retu_rtc_driver);
|
461 |
return ret;
|
462 |
} |
463 |
|
464 |
static void __exit retu_rtc_exit(void)
|
465 |
{ |
466 |
platform_device_unregister(&retu_rtc_device);
|
467 |
driver_unregister(&retu_rtc_driver);
|
468 |
|
469 |
wait_for_completion(&retu_rtc_exited);
|
470 |
} |
471 |
|
472 |
module_init(retu_rtc_init);
|
473 |
module_exit(retu_rtc_exit);
|
474 |
|
475 |
MODULE_DESCRIPTION("Retu RTC");
|
476 |
MODULE_LICENSE("GPL");
|
477 |
MODULE_AUTHOR("Paul Mundt and Igor Stoppa");
|