Merge branches 'fixes', 'cleanups' and 'boards'
[linux-2.6] / drivers / rtc / rtc-pl030.c
1 /*
2  *  linux/drivers/rtc/rtc-pl030.c
3  *
4  *  Copyright (C) 2000-2001 Deep Blue Solutions Ltd.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  */
10 #include <linux/module.h>
11 #include <linux/rtc.h>
12 #include <linux/init.h>
13 #include <linux/interrupt.h>
14 #include <linux/amba/bus.h>
15 #include <linux/io.h>
16
17 #define RTC_DR          (0)
18 #define RTC_MR          (4)
19 #define RTC_STAT        (8)
20 #define RTC_EOI         (8)
21 #define RTC_LR          (12)
22 #define RTC_CR          (16)
23 #define RTC_CR_MIE      (1 << 0)
24
25 struct pl030_rtc {
26         struct rtc_device       *rtc;
27         void __iomem            *base;
28 };
29
30 static irqreturn_t pl030_interrupt(int irq, void *dev_id)
31 {
32         struct pl030_rtc *rtc = dev_id;
33         writel(0, rtc->base + RTC_EOI);
34         return IRQ_HANDLED;
35 }
36
37 static int pl030_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
38 {
39         return -ENOIOCTLCMD;
40 }
41
42 static int pl030_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
43 {
44         struct pl030_rtc *rtc = dev_get_drvdata(dev);
45
46         rtc_time_to_tm(readl(rtc->base + RTC_MR), &alrm->time);
47         return 0;
48 }
49
50 static int pl030_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
51 {
52         struct pl030_rtc *rtc = dev_get_drvdata(dev);
53         unsigned long time;
54         int ret;
55
56         /*
57          * At the moment, we can only deal with non-wildcarded alarm times.
58          */
59         ret = rtc_valid_tm(&alrm->time);
60         if (ret == 0)
61                 ret = rtc_tm_to_time(&alrm->time, &time);
62         if (ret == 0)
63                 writel(time, rtc->base + RTC_MR);
64         return ret;
65 }
66
67 static int pl030_read_time(struct device *dev, struct rtc_time *tm)
68 {
69         struct pl030_rtc *rtc = dev_get_drvdata(dev);
70
71         rtc_time_to_tm(readl(rtc->base + RTC_DR), tm);
72
73         return 0;
74 }
75
76 /*
77  * Set the RTC time.  Unfortunately, we can't accurately set
78  * the point at which the counter updates.
79  *
80  * Also, since RTC_LR is transferred to RTC_CR on next rising
81  * edge of the 1Hz clock, we must write the time one second
82  * in advance.
83  */
84 static int pl030_set_time(struct device *dev, struct rtc_time *tm)
85 {
86         struct pl030_rtc *rtc = dev_get_drvdata(dev);
87         unsigned long time;
88         int ret;
89
90         ret = rtc_tm_to_time(tm, &time);
91         if (ret == 0)
92                 writel(time + 1, rtc->base + RTC_LR);
93
94         return ret;
95 }
96
97 static const struct rtc_class_ops pl030_ops = {
98         .ioctl          = pl030_ioctl,
99         .read_time      = pl030_read_time,
100         .set_time       = pl030_set_time,
101         .read_alarm     = pl030_read_alarm,
102         .set_alarm      = pl030_set_alarm,
103 };
104
105 static int pl030_probe(struct amba_device *dev, void *id)
106 {
107         struct pl030_rtc *rtc;
108         int ret;
109
110         ret = amba_request_regions(dev, NULL);
111         if (ret)
112                 goto err_req;
113
114         rtc = kmalloc(sizeof(*rtc), GFP_KERNEL);
115         if (!rtc) {
116                 ret = -ENOMEM;
117                 goto err_rtc;
118         }
119
120         rtc->base = ioremap(dev->res.start, SZ_4K);
121         if (!rtc->base) {
122                 ret = -ENOMEM;
123                 goto err_map;
124         }
125
126         __raw_writel(0, rtc->base + RTC_CR);
127         __raw_writel(0, rtc->base + RTC_EOI);
128
129         amba_set_drvdata(dev, rtc);
130
131         ret = request_irq(dev->irq[0], pl030_interrupt, IRQF_DISABLED,
132                           "rtc-pl030", rtc);
133         if (ret)
134                 goto err_irq;
135
136         rtc->rtc = rtc_device_register("pl030", &dev->dev, &pl030_ops,
137                                        THIS_MODULE);
138         if (IS_ERR(rtc->rtc)) {
139                 ret = PTR_ERR(rtc->rtc);
140                 goto err_reg;
141         }
142
143         return 0;
144
145  err_reg:
146         free_irq(dev->irq[0], rtc);
147  err_irq:
148         iounmap(rtc->base);
149  err_map:
150         kfree(rtc);
151  err_rtc:
152         amba_release_regions(dev);
153  err_req:
154         return ret;
155 }
156
157 static int pl030_remove(struct amba_device *dev)
158 {
159         struct pl030_rtc *rtc = amba_get_drvdata(dev);
160
161         amba_set_drvdata(dev, NULL);
162
163         writel(0, rtc->base + RTC_CR);
164
165         free_irq(dev->irq[0], rtc);
166         rtc_device_unregister(rtc->rtc);
167         iounmap(rtc->base);
168         kfree(rtc);
169         amba_release_regions(dev);
170
171         return 0;
172 }
173
174 static struct amba_id pl030_ids[] = {
175         {
176                 .id     = 0x00041030,
177                 .mask   = 0x000fffff,
178         },
179         { 0, 0 },
180 };
181
182 static struct amba_driver pl030_driver = {
183         .drv            = {
184                 .name   = "rtc-pl030",
185         },
186         .probe          = pl030_probe,
187         .remove         = pl030_remove,
188         .id_table       = pl030_ids,
189 };
190
191 static int __init pl030_init(void)
192 {
193         return amba_driver_register(&pl030_driver);
194 }
195
196 static void __exit pl030_exit(void)
197 {
198         amba_driver_unregister(&pl030_driver);
199 }
200
201 module_init(pl030_init);
202 module_exit(pl030_exit);
203
204 MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
205 MODULE_DESCRIPTION("ARM AMBA PL030 RTC Driver");
206 MODULE_LICENSE("GPL");