Root/drivers/rtc/rtc-mpc5121.c

1/*
2 * Real-time clock driver for MPC5121
3 *
4 * Copyright 2007, Domen Puncer <domen.puncer@telargo.com>
5 * Copyright 2008, Freescale Semiconductor, Inc. All rights reserved.
6 * Copyright 2011, Dmitry Eremin-Solenikov
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#include <linux/init.h>
14#include <linux/module.h>
15#include <linux/rtc.h>
16#include <linux/of_device.h>
17#include <linux/of_platform.h>
18#include <linux/io.h>
19#include <linux/slab.h>
20
21struct mpc5121_rtc_regs {
22    u8 set_time; /* RTC + 0x00 */
23    u8 hour_set; /* RTC + 0x01 */
24    u8 minute_set; /* RTC + 0x02 */
25    u8 second_set; /* RTC + 0x03 */
26
27    u8 set_date; /* RTC + 0x04 */
28    u8 month_set; /* RTC + 0x05 */
29    u8 weekday_set; /* RTC + 0x06 */
30    u8 date_set; /* RTC + 0x07 */
31
32    u8 write_sw; /* RTC + 0x08 */
33    u8 sw_set; /* RTC + 0x09 */
34    u16 year_set; /* RTC + 0x0a */
35
36    u8 alm_enable; /* RTC + 0x0c */
37    u8 alm_hour_set; /* RTC + 0x0d */
38    u8 alm_min_set; /* RTC + 0x0e */
39    u8 int_enable; /* RTC + 0x0f */
40
41    u8 reserved1;
42    u8 hour; /* RTC + 0x11 */
43    u8 minute; /* RTC + 0x12 */
44    u8 second; /* RTC + 0x13 */
45
46    u8 month; /* RTC + 0x14 */
47    u8 wday_mday; /* RTC + 0x15 */
48    u16 year; /* RTC + 0x16 */
49
50    u8 int_alm; /* RTC + 0x18 */
51    u8 int_sw; /* RTC + 0x19 */
52    u8 alm_status; /* RTC + 0x1a */
53    u8 sw_minute; /* RTC + 0x1b */
54
55    u8 bus_error_1; /* RTC + 0x1c */
56    u8 int_day; /* RTC + 0x1d */
57    u8 int_min; /* RTC + 0x1e */
58    u8 int_sec; /* RTC + 0x1f */
59
60    /*
61     * target_time:
62     * intended to be used for hibernation but hibernation
63     * does not work on silicon rev 1.5 so use it for non-volatile
64     * storage of offset between the actual_time register and linux
65     * time
66     */
67    u32 target_time; /* RTC + 0x20 */
68    /*
69     * actual_time:
70     * readonly time since VBAT_RTC was last connected
71     */
72    u32 actual_time; /* RTC + 0x24 */
73    u32 keep_alive; /* RTC + 0x28 */
74};
75
76struct mpc5121_rtc_data {
77    unsigned irq;
78    unsigned irq_periodic;
79    struct mpc5121_rtc_regs __iomem *regs;
80    struct rtc_device *rtc;
81    struct rtc_wkalrm wkalarm;
82};
83
84/*
85 * Update second/minute/hour registers.
86 *
87 * This is just so alarm will work.
88 */
89static void mpc5121_rtc_update_smh(struct mpc5121_rtc_regs __iomem *regs,
90                   struct rtc_time *tm)
91{
92    out_8(&regs->second_set, tm->tm_sec);
93    out_8(&regs->minute_set, tm->tm_min);
94    out_8(&regs->hour_set, tm->tm_hour);
95
96    /* set time sequence */
97    out_8(&regs->set_time, 0x1);
98    out_8(&regs->set_time, 0x3);
99    out_8(&regs->set_time, 0x1);
100    out_8(&regs->set_time, 0x0);
101}
102
103static int mpc5121_rtc_read_time(struct device *dev, struct rtc_time *tm)
104{
105    struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
106    struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
107    unsigned long now;
108
109    /*
110     * linux time is actual_time plus the offset saved in target_time
111     */
112    now = in_be32(&regs->actual_time) + in_be32(&regs->target_time);
113
114    rtc_time_to_tm(now, tm);
115
116    /*
117     * update second minute hour registers
118     * so alarms will work
119     */
120    mpc5121_rtc_update_smh(regs, tm);
121
122    return rtc_valid_tm(tm);
123}
124
125static int mpc5121_rtc_set_time(struct device *dev, struct rtc_time *tm)
126{
127    struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
128    struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
129    int ret;
130    unsigned long now;
131
132    /*
133     * The actual_time register is read only so we write the offset
134     * between it and linux time to the target_time register.
135     */
136    ret = rtc_tm_to_time(tm, &now);
137    if (ret == 0)
138        out_be32(&regs->target_time, now - in_be32(&regs->actual_time));
139
140    /*
141     * update second minute hour registers
142     * so alarms will work
143     */
144    mpc5121_rtc_update_smh(regs, tm);
145
146    return 0;
147}
148
149static int mpc5200_rtc_read_time(struct device *dev, struct rtc_time *tm)
150{
151    struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
152    struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
153    int tmp;
154
155    tm->tm_sec = in_8(&regs->second);
156    tm->tm_min = in_8(&regs->minute);
157
158    /* 12 hour format? */
159    if (in_8(&regs->hour) & 0x20)
160        tm->tm_hour = (in_8(&regs->hour) >> 1) +
161            (in_8(&regs->hour) & 1 ? 12 : 0);
162    else
163        tm->tm_hour = in_8(&regs->hour);
164
165    tmp = in_8(&regs->wday_mday);
166    tm->tm_mday = tmp & 0x1f;
167    tm->tm_mon = in_8(&regs->month) - 1;
168    tm->tm_year = in_be16(&regs->year) - 1900;
169    tm->tm_wday = (tmp >> 5) % 7;
170    tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
171    tm->tm_isdst = 0;
172
173    return 0;
174}
175
176static int mpc5200_rtc_set_time(struct device *dev, struct rtc_time *tm)
177{
178    struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
179    struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
180
181    mpc5121_rtc_update_smh(regs, tm);
182
183    /* date */
184    out_8(&regs->month_set, tm->tm_mon + 1);
185    out_8(&regs->weekday_set, tm->tm_wday ? tm->tm_wday : 7);
186    out_8(&regs->date_set, tm->tm_mday);
187    out_be16(&regs->year_set, tm->tm_year + 1900);
188
189    /* set date sequence */
190    out_8(&regs->set_date, 0x1);
191    out_8(&regs->set_date, 0x3);
192    out_8(&regs->set_date, 0x1);
193    out_8(&regs->set_date, 0x0);
194
195    return 0;
196}
197
198static int mpc5121_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
199{
200    struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
201    struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
202
203    *alarm = rtc->wkalarm;
204
205    alarm->pending = in_8(&regs->alm_status);
206
207    return 0;
208}
209
210static int mpc5121_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
211{
212    struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
213    struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
214
215    /*
216     * the alarm has no seconds so deal with it
217     */
218    if (alarm->time.tm_sec) {
219        alarm->time.tm_sec = 0;
220        alarm->time.tm_min++;
221        if (alarm->time.tm_min >= 60) {
222            alarm->time.tm_min = 0;
223            alarm->time.tm_hour++;
224            if (alarm->time.tm_hour >= 24)
225                alarm->time.tm_hour = 0;
226        }
227    }
228
229    alarm->time.tm_mday = -1;
230    alarm->time.tm_mon = -1;
231    alarm->time.tm_year = -1;
232
233    out_8(&regs->alm_min_set, alarm->time.tm_min);
234    out_8(&regs->alm_hour_set, alarm->time.tm_hour);
235
236    out_8(&regs->alm_enable, alarm->enabled);
237
238    rtc->wkalarm = *alarm;
239    return 0;
240}
241
242static irqreturn_t mpc5121_rtc_handler(int irq, void *dev)
243{
244    struct mpc5121_rtc_data *rtc = dev_get_drvdata((struct device *)dev);
245    struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
246
247    if (in_8(&regs->int_alm)) {
248        /* acknowledge and clear status */
249        out_8(&regs->int_alm, 1);
250        out_8(&regs->alm_status, 1);
251
252        rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF);
253        return IRQ_HANDLED;
254    }
255
256    return IRQ_NONE;
257}
258
259static irqreturn_t mpc5121_rtc_handler_upd(int irq, void *dev)
260{
261    struct mpc5121_rtc_data *rtc = dev_get_drvdata((struct device *)dev);
262    struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
263
264    if (in_8(&regs->int_sec) && (in_8(&regs->int_enable) & 0x1)) {
265        /* acknowledge */
266        out_8(&regs->int_sec, 1);
267
268        rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_UF);
269        return IRQ_HANDLED;
270    }
271
272    return IRQ_NONE;
273}
274
275static int mpc5121_rtc_alarm_irq_enable(struct device *dev,
276                    unsigned int enabled)
277{
278    struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
279    struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
280    int val;
281
282    if (enabled)
283        val = 1;
284    else
285        val = 0;
286
287    out_8(&regs->alm_enable, val);
288    rtc->wkalarm.enabled = val;
289
290    return 0;
291}
292
293static const struct rtc_class_ops mpc5121_rtc_ops = {
294    .read_time = mpc5121_rtc_read_time,
295    .set_time = mpc5121_rtc_set_time,
296    .read_alarm = mpc5121_rtc_read_alarm,
297    .set_alarm = mpc5121_rtc_set_alarm,
298    .alarm_irq_enable = mpc5121_rtc_alarm_irq_enable,
299};
300
301static const struct rtc_class_ops mpc5200_rtc_ops = {
302    .read_time = mpc5200_rtc_read_time,
303    .set_time = mpc5200_rtc_set_time,
304    .read_alarm = mpc5121_rtc_read_alarm,
305    .set_alarm = mpc5121_rtc_set_alarm,
306    .alarm_irq_enable = mpc5121_rtc_alarm_irq_enable,
307};
308
309static int __devinit mpc5121_rtc_probe(struct platform_device *op)
310{
311    struct mpc5121_rtc_data *rtc;
312    int err = 0;
313
314    rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
315    if (!rtc)
316        return -ENOMEM;
317
318    rtc->regs = of_iomap(op->dev.of_node, 0);
319    if (!rtc->regs) {
320        dev_err(&op->dev, "%s: couldn't map io space\n", __func__);
321        err = -ENOSYS;
322        goto out_free;
323    }
324
325    device_init_wakeup(&op->dev, 1);
326
327    dev_set_drvdata(&op->dev, rtc);
328
329    rtc->irq = irq_of_parse_and_map(op->dev.of_node, 1);
330    err = request_irq(rtc->irq, mpc5121_rtc_handler, 0,
331                        "mpc5121-rtc", &op->dev);
332    if (err) {
333        dev_err(&op->dev, "%s: could not request irq: %i\n",
334                            __func__, rtc->irq);
335        goto out_dispose;
336    }
337
338    rtc->irq_periodic = irq_of_parse_and_map(op->dev.of_node, 0);
339    err = request_irq(rtc->irq_periodic, mpc5121_rtc_handler_upd,
340                0, "mpc5121-rtc_upd", &op->dev);
341    if (err) {
342        dev_err(&op->dev, "%s: could not request irq: %i\n",
343                        __func__, rtc->irq_periodic);
344        goto out_dispose2;
345    }
346
347    if (of_device_is_compatible(op->dev.of_node, "fsl,mpc5121-rtc")) {
348        u32 ka;
349        ka = in_be32(&rtc->regs->keep_alive);
350        if (ka & 0x02) {
351            dev_warn(&op->dev,
352                "mpc5121-rtc: Battery or oscillator failure!\n");
353            out_be32(&rtc->regs->keep_alive, ka);
354        }
355
356        rtc->rtc = rtc_device_register("mpc5121-rtc", &op->dev,
357                        &mpc5121_rtc_ops, THIS_MODULE);
358    } else {
359        rtc->rtc = rtc_device_register("mpc5200-rtc", &op->dev,
360                        &mpc5200_rtc_ops, THIS_MODULE);
361    }
362
363    if (IS_ERR(rtc->rtc)) {
364        err = PTR_ERR(rtc->rtc);
365        goto out_free_irq;
366    }
367    rtc->rtc->uie_unsupported = 1;
368
369    return 0;
370
371out_free_irq:
372    free_irq(rtc->irq_periodic, &op->dev);
373out_dispose2:
374    irq_dispose_mapping(rtc->irq_periodic);
375    free_irq(rtc->irq, &op->dev);
376out_dispose:
377    irq_dispose_mapping(rtc->irq);
378    iounmap(rtc->regs);
379out_free:
380    kfree(rtc);
381
382    return err;
383}
384
385static int __devexit mpc5121_rtc_remove(struct platform_device *op)
386{
387    struct mpc5121_rtc_data *rtc = dev_get_drvdata(&op->dev);
388    struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
389
390    /* disable interrupt, so there are no nasty surprises */
391    out_8(&regs->alm_enable, 0);
392    out_8(&regs->int_enable, in_8(&regs->int_enable) & ~0x1);
393
394    rtc_device_unregister(rtc->rtc);
395    iounmap(rtc->regs);
396    free_irq(rtc->irq, &op->dev);
397    free_irq(rtc->irq_periodic, &op->dev);
398    irq_dispose_mapping(rtc->irq);
399    irq_dispose_mapping(rtc->irq_periodic);
400    dev_set_drvdata(&op->dev, NULL);
401    kfree(rtc);
402
403    return 0;
404}
405
406static struct of_device_id mpc5121_rtc_match[] __devinitdata = {
407    { .compatible = "fsl,mpc5121-rtc", },
408    { .compatible = "fsl,mpc5200-rtc", },
409    {},
410};
411
412static struct platform_driver mpc5121_rtc_driver = {
413    .driver = {
414        .name = "mpc5121-rtc",
415        .owner = THIS_MODULE,
416        .of_match_table = mpc5121_rtc_match,
417    },
418    .probe = mpc5121_rtc_probe,
419    .remove = __devexit_p(mpc5121_rtc_remove),
420};
421
422module_platform_driver(mpc5121_rtc_driver);
423
424MODULE_LICENSE("GPL");
425MODULE_AUTHOR("John Rigby <jcrigby@gmail.com>");
426

Archive Download this file



interactive