Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * linux/arch/arm/mach-integrator/time.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/kernel.h> | |
12 | #include <linux/time.h> | |
13 | #include <linux/mc146818rtc.h> | |
14 | #include <linux/interrupt.h> | |
15 | #include <linux/init.h> | |
16 | #include <linux/device.h> | |
a62c80e5 | 17 | #include <linux/amba/bus.h> |
1da177e4 | 18 | |
1da177e4 LT |
19 | #include <asm/hardware.h> |
20 | #include <asm/io.h> | |
21 | #include <asm/uaccess.h> | |
22 | #include <asm/rtc.h> | |
23 | ||
24 | #include <asm/mach/time.h> | |
25 | ||
26 | #define RTC_DR (0) | |
27 | #define RTC_MR (4) | |
28 | #define RTC_STAT (8) | |
29 | #define RTC_EOI (8) | |
30 | #define RTC_LR (12) | |
31 | #define RTC_CR (16) | |
32 | #define RTC_CR_MIE (1 << 0) | |
33 | ||
34 | extern int (*set_rtc)(void); | |
35 | static void __iomem *rtc_base; | |
36 | ||
37 | static int integrator_set_rtc(void) | |
38 | { | |
39 | __raw_writel(xtime.tv_sec, rtc_base + RTC_LR); | |
40 | return 1; | |
41 | } | |
42 | ||
4079c39a | 43 | static int integrator_rtc_read_alarm(struct rtc_wkalrm *alrm) |
1da177e4 LT |
44 | { |
45 | rtc_time_to_tm(readl(rtc_base + RTC_MR), &alrm->time); | |
d5aa207e | 46 | return 0; |
1da177e4 LT |
47 | } |
48 | ||
4079c39a | 49 | static inline int integrator_rtc_set_alarm(struct rtc_wkalrm *alrm) |
1da177e4 LT |
50 | { |
51 | unsigned long time; | |
52 | int ret; | |
53 | ||
d5aa207e RK |
54 | /* |
55 | * At the moment, we can only deal with non-wildcarded alarm times. | |
56 | */ | |
57 | ret = rtc_valid_tm(&alrm->time); | |
58 | if (ret == 0) | |
59 | ret = rtc_tm_to_time(&alrm->time, &time); | |
1da177e4 LT |
60 | if (ret == 0) |
61 | writel(time, rtc_base + RTC_MR); | |
62 | return ret; | |
63 | } | |
64 | ||
4079c39a | 65 | static int integrator_rtc_read_time(struct rtc_time *tm) |
1da177e4 LT |
66 | { |
67 | rtc_time_to_tm(readl(rtc_base + RTC_DR), tm); | |
d5aa207e | 68 | return 0; |
1da177e4 LT |
69 | } |
70 | ||
71 | /* | |
72 | * Set the RTC time. Unfortunately, we can't accurately set | |
73 | * the point at which the counter updates. | |
74 | * | |
75 | * Also, since RTC_LR is transferred to RTC_CR on next rising | |
76 | * edge of the 1Hz clock, we must write the time one second | |
77 | * in advance. | |
78 | */ | |
4079c39a | 79 | static inline int integrator_rtc_set_time(struct rtc_time *tm) |
1da177e4 LT |
80 | { |
81 | unsigned long time; | |
82 | int ret; | |
83 | ||
84 | ret = rtc_tm_to_time(tm, &time); | |
85 | if (ret == 0) | |
86 | writel(time + 1, rtc_base + RTC_LR); | |
87 | ||
88 | return ret; | |
89 | } | |
90 | ||
91 | static struct rtc_ops rtc_ops = { | |
92 | .owner = THIS_MODULE, | |
4079c39a RP |
93 | .read_time = integrator_rtc_read_time, |
94 | .set_time = integrator_rtc_set_time, | |
95 | .read_alarm = integrator_rtc_read_alarm, | |
96 | .set_alarm = integrator_rtc_set_alarm, | |
1da177e4 LT |
97 | }; |
98 | ||
2a10e0b2 AB |
99 | static irqreturn_t arm_rtc_interrupt(int irq, void *dev_id, |
100 | struct pt_regs *regs) | |
1da177e4 LT |
101 | { |
102 | writel(0, rtc_base + RTC_EOI); | |
103 | return IRQ_HANDLED; | |
104 | } | |
105 | ||
106 | static int rtc_probe(struct amba_device *dev, void *id) | |
107 | { | |
108 | int ret; | |
109 | ||
110 | if (rtc_base) | |
111 | return -EBUSY; | |
112 | ||
113 | ret = amba_request_regions(dev, NULL); | |
114 | if (ret) | |
115 | goto out; | |
116 | ||
117 | rtc_base = ioremap(dev->res.start, SZ_4K); | |
118 | if (!rtc_base) { | |
119 | ret = -ENOMEM; | |
120 | goto res_out; | |
121 | } | |
122 | ||
123 | __raw_writel(0, rtc_base + RTC_CR); | |
124 | __raw_writel(0, rtc_base + RTC_EOI); | |
125 | ||
126 | xtime.tv_sec = __raw_readl(rtc_base + RTC_DR); | |
127 | ||
52e405ea | 128 | ret = request_irq(dev->irq[0], arm_rtc_interrupt, IRQF_DISABLED, |
1da177e4 LT |
129 | "rtc-pl030", dev); |
130 | if (ret) | |
131 | goto map_out; | |
132 | ||
133 | ret = register_rtc(&rtc_ops); | |
134 | if (ret) | |
135 | goto irq_out; | |
136 | ||
137 | set_rtc = integrator_set_rtc; | |
138 | return 0; | |
139 | ||
140 | irq_out: | |
141 | free_irq(dev->irq[0], dev); | |
142 | map_out: | |
143 | iounmap(rtc_base); | |
144 | rtc_base = NULL; | |
145 | res_out: | |
146 | amba_release_regions(dev); | |
147 | out: | |
148 | return ret; | |
149 | } | |
150 | ||
151 | static int rtc_remove(struct amba_device *dev) | |
152 | { | |
153 | set_rtc = NULL; | |
154 | ||
155 | writel(0, rtc_base + RTC_CR); | |
156 | ||
157 | free_irq(dev->irq[0], dev); | |
158 | unregister_rtc(&rtc_ops); | |
159 | ||
160 | iounmap(rtc_base); | |
161 | rtc_base = NULL; | |
162 | amba_release_regions(dev); | |
163 | ||
164 | return 0; | |
165 | } | |
166 | ||
167 | static struct timespec rtc_delta; | |
168 | ||
169 | static int rtc_suspend(struct amba_device *dev, pm_message_t state) | |
170 | { | |
171 | struct timespec rtc; | |
172 | ||
173 | rtc.tv_sec = readl(rtc_base + RTC_DR); | |
174 | rtc.tv_nsec = 0; | |
175 | save_time_delta(&rtc_delta, &rtc); | |
176 | ||
177 | return 0; | |
178 | } | |
179 | ||
180 | static int rtc_resume(struct amba_device *dev) | |
181 | { | |
182 | struct timespec rtc; | |
183 | ||
184 | rtc.tv_sec = readl(rtc_base + RTC_DR); | |
185 | rtc.tv_nsec = 0; | |
186 | restore_time_delta(&rtc_delta, &rtc); | |
187 | ||
188 | return 0; | |
189 | } | |
190 | ||
191 | static struct amba_id rtc_ids[] = { | |
192 | { | |
193 | .id = 0x00041030, | |
194 | .mask = 0x000fffff, | |
195 | }, | |
196 | { 0, 0 }, | |
197 | }; | |
198 | ||
199 | static struct amba_driver rtc_driver = { | |
200 | .drv = { | |
201 | .name = "rtc-pl030", | |
202 | }, | |
203 | .probe = rtc_probe, | |
204 | .remove = rtc_remove, | |
205 | .suspend = rtc_suspend, | |
206 | .resume = rtc_resume, | |
207 | .id_table = rtc_ids, | |
208 | }; | |
209 | ||
210 | static int __init integrator_rtc_init(void) | |
211 | { | |
212 | return amba_driver_register(&rtc_driver); | |
213 | } | |
214 | ||
215 | static void __exit integrator_rtc_exit(void) | |
216 | { | |
217 | amba_driver_unregister(&rtc_driver); | |
218 | } | |
219 | ||
220 | module_init(integrator_rtc_init); | |
221 | module_exit(integrator_rtc_exit); |