aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorMike Frysinger <michael.frysinger@analog.com>2008-02-06 04:38:51 -0500
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2008-02-06 13:41:14 -0500
commit095b9d546f8fdac335989bd3d60405ff3af40ee9 (patch)
treeefe341cd8269af5ed7bfc7e8ec1a1c89a398e941 /drivers
parent48c1a56b4bf936590ddbee93bf7accf703ec0411 (diff)
Blackfin RTC driver: convert sync wait to use the irq write complete notice
- thus clearing out the need for spin locks - add a small optimization for reading of the rtc field Signed-off-by: Mike Frysinger <michael.frysinger@analog.com> Signed-off-by: Bryan Wu <bryan.wu@analog.com> Acked-by: Alessandro Zummo <alessandro.zummo@towertech.it> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/rtc/rtc-bfin.c249
1 files changed, 136 insertions, 113 deletions
diff --git a/drivers/rtc/rtc-bfin.c b/drivers/rtc/rtc-bfin.c
index 69810a2fc43b..d90ba860d216 100644
--- a/drivers/rtc/rtc-bfin.c
+++ b/drivers/rtc/rtc-bfin.c
@@ -1,6 +1,6 @@
1/* 1/*
2 * Blackfin On-Chip Real Time Clock Driver 2 * Blackfin On-Chip Real Time Clock Driver
3 * Supports BF53[123]/BF53[467]/BF54[2489] 3 * Supports BF52[257]/BF53[123]/BF53[467]/BF54[24789]
4 * 4 *
5 * Copyright 2004-2007 Analog Devices Inc. 5 * Copyright 2004-2007 Analog Devices Inc.
6 * 6 *
@@ -32,16 +32,16 @@
32 * writes to clear status registers complete immediately. 32 * writes to clear status registers complete immediately.
33 */ 33 */
34 34
35#include <linux/module.h>
36#include <linux/kernel.h>
37#include <linux/bcd.h> 35#include <linux/bcd.h>
38#include <linux/rtc.h> 36#include <linux/completion.h>
37#include <linux/delay.h>
39#include <linux/init.h> 38#include <linux/init.h>
39#include <linux/interrupt.h>
40#include <linux/kernel.h>
41#include <linux/module.h>
40#include <linux/platform_device.h> 42#include <linux/platform_device.h>
43#include <linux/rtc.h>
41#include <linux/seq_file.h> 44#include <linux/seq_file.h>
42#include <linux/interrupt.h>
43#include <linux/spinlock.h>
44#include <linux/delay.h>
45 45
46#include <asm/blackfin.h> 46#include <asm/blackfin.h>
47 47
@@ -50,7 +50,7 @@
50struct bfin_rtc { 50struct bfin_rtc {
51 struct rtc_device *rtc_dev; 51 struct rtc_device *rtc_dev;
52 struct rtc_time rtc_alarm; 52 struct rtc_time rtc_alarm;
53 spinlock_t lock; 53 u16 rtc_wrote_regs;
54}; 54};
55 55
56/* Bit values for the ISTAT / ICTL registers */ 56/* Bit values for the ISTAT / ICTL registers */
@@ -96,7 +96,10 @@ static inline void rtc_bfin_to_tm(u32 rtc_bfin, struct rtc_time *tm)
96 rtc_time_to_tm(rtc_bfin_to_time(rtc_bfin), tm); 96 rtc_time_to_tm(rtc_bfin_to_time(rtc_bfin), tm);
97} 97}
98 98
99/* Wait for the previous write to a RTC register to complete. 99/**
100 * bfin_rtc_sync_pending - make sure pending writes have complete
101 *
102 * Wait for the previous write to a RTC register to complete.
100 * Unfortunately, we can't sleep here as that introduces a race condition when 103 * Unfortunately, we can't sleep here as that introduces a race condition when
101 * turning on interrupt events. Consider this: 104 * turning on interrupt events. Consider this:
102 * - process sets alarm 105 * - process sets alarm
@@ -117,64 +120,102 @@ static inline void rtc_bfin_to_tm(u32 rtc_bfin, struct rtc_time *tm)
117 * inc rate for all RTC registers from 1HZ to 32.768kHZ ... 120 * inc rate for all RTC registers from 1HZ to 32.768kHZ ...
118 * - use the write complete IRQ 121 * - use the write complete IRQ
119 */ 122 */
120static void rtc_bfin_sync_pending(void) 123/*
124static void bfin_rtc_sync_pending_polled(void)
121{ 125{
122 while (!(bfin_read_RTC_ISTAT() & RTC_ISTAT_WRITE_COMPLETE)) { 126 while (!(bfin_read_RTC_ISTAT() & RTC_ISTAT_WRITE_COMPLETE))
123 if (!(bfin_read_RTC_ISTAT() & RTC_ISTAT_WRITE_PENDING)) 127 if (!(bfin_read_RTC_ISTAT() & RTC_ISTAT_WRITE_PENDING))
124 break; 128 break;
125 }
126 bfin_write_RTC_ISTAT(RTC_ISTAT_WRITE_COMPLETE); 129 bfin_write_RTC_ISTAT(RTC_ISTAT_WRITE_COMPLETE);
127} 130}
131*/
132static DECLARE_COMPLETION(bfin_write_complete);
133static void bfin_rtc_sync_pending(struct device *dev)
134{
135 dev_dbg_stamp(dev);
136 while (bfin_read_RTC_ISTAT() & RTC_ISTAT_WRITE_PENDING)
137 wait_for_completion_timeout(&bfin_write_complete, HZ * 5);
138 dev_dbg_stamp(dev);
139}
128 140
129static void rtc_bfin_reset(struct device *dev) 141/**
142 * bfin_rtc_reset - set RTC to sane/known state
143 *
144 * Initialize the RTC. Enable pre-scaler to scale RTC clock
145 * to 1Hz and clear interrupt/status registers.
146 */
147static void bfin_rtc_reset(struct device *dev)
130{ 148{
131 struct bfin_rtc *rtc = dev_get_drvdata(dev); 149 struct bfin_rtc *rtc = dev_get_drvdata(dev);
132 /* Initialize the RTC. Enable pre-scaler to scale RTC clock 150 dev_dbg_stamp(dev);
133 * to 1Hz and clear interrupt/status registers. */ 151 bfin_rtc_sync_pending(dev);
134 spin_lock_irq(&rtc->lock);
135 rtc_bfin_sync_pending();
136 bfin_write_RTC_PREN(0x1); 152 bfin_write_RTC_PREN(0x1);
137 bfin_write_RTC_ICTL(0); 153 bfin_write_RTC_ICTL(RTC_ISTAT_WRITE_COMPLETE);
138 bfin_write_RTC_SWCNT(0); 154 bfin_write_RTC_SWCNT(0);
139 bfin_write_RTC_ALARM(0); 155 bfin_write_RTC_ALARM(0);
140 bfin_write_RTC_ISTAT(0xFFFF); 156 bfin_write_RTC_ISTAT(0xFFFF);
141 spin_unlock_irq(&rtc->lock); 157 rtc->rtc_wrote_regs = 0;
142} 158}
143 159
160/**
161 * bfin_rtc_interrupt - handle interrupt from RTC
162 *
163 * Since we handle all RTC events here, we have to make sure the requested
164 * interrupt is enabled (in RTC_ICTL) as the event status register (RTC_ISTAT)
165 * always gets updated regardless of the interrupt being enabled. So when one
166 * even we care about (e.g. stopwatch) goes off, we don't want to turn around
167 * and say that other events have happened as well (e.g. second). We do not
168 * have to worry about pending writes to the RTC_ICTL register as interrupts
169 * only fire if they are enabled in the RTC_ICTL register.
170 */
144static irqreturn_t bfin_rtc_interrupt(int irq, void *dev_id) 171static irqreturn_t bfin_rtc_interrupt(int irq, void *dev_id)
145{ 172{
146 struct device *dev = dev_id; 173 struct device *dev = dev_id;
147 struct bfin_rtc *rtc = dev_get_drvdata(dev); 174 struct bfin_rtc *rtc = dev_get_drvdata(dev);
148 unsigned long events = 0; 175 unsigned long events = 0;
149 u16 rtc_istat; 176 bool write_complete = false;
177 u16 rtc_istat, rtc_ictl;
150 178
151 dev_dbg_stamp(dev); 179 dev_dbg_stamp(dev);
152 180
153 spin_lock_irq(&rtc->lock);
154
155 rtc_istat = bfin_read_RTC_ISTAT(); 181 rtc_istat = bfin_read_RTC_ISTAT();
182 rtc_ictl = bfin_read_RTC_ICTL();
156 183
157 if (rtc_istat & (RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY)) { 184 if (rtc_istat & RTC_ISTAT_WRITE_COMPLETE) {
158 bfin_write_RTC_ISTAT(RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY); 185 bfin_write_RTC_ISTAT(RTC_ISTAT_WRITE_COMPLETE);
159 events |= RTC_AF | RTC_IRQF; 186 write_complete = true;
187 complete(&bfin_write_complete);
160 } 188 }
161 189
162 if (rtc_istat & RTC_ISTAT_STOPWATCH) { 190 if (rtc_ictl & (RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY)) {
163 bfin_write_RTC_ISTAT(RTC_ISTAT_STOPWATCH); 191 if (rtc_istat & (RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY)) {
164 events |= RTC_PF | RTC_IRQF; 192 bfin_write_RTC_ISTAT(RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY);
165 bfin_write_RTC_SWCNT(rtc->rtc_dev->irq_freq); 193 events |= RTC_AF | RTC_IRQF;
194 }
166 } 195 }
167 196
168 if (rtc_istat & RTC_ISTAT_SEC) { 197 if (rtc_ictl & RTC_ISTAT_STOPWATCH) {
169 bfin_write_RTC_ISTAT(RTC_ISTAT_SEC); 198 if (rtc_istat & RTC_ISTAT_STOPWATCH) {
170 events |= RTC_UF | RTC_IRQF; 199 bfin_write_RTC_ISTAT(RTC_ISTAT_STOPWATCH);
200 events |= RTC_PF | RTC_IRQF;
201 bfin_write_RTC_SWCNT(rtc->rtc_dev->irq_freq);
202 }
171 } 203 }
172 204
173 rtc_update_irq(rtc->rtc_dev, 1, events); 205 if (rtc_ictl & RTC_ISTAT_SEC) {
206 if (rtc_istat & RTC_ISTAT_SEC) {
207 bfin_write_RTC_ISTAT(RTC_ISTAT_SEC);
208 events |= RTC_UF | RTC_IRQF;
209 }
210 }
174 211
175 spin_unlock_irq(&rtc->lock); 212 if (events)
213 rtc_update_irq(rtc->rtc_dev, 1, events);
176 214
177 return IRQ_HANDLED; 215 if (write_complete || events)
216 return IRQ_HANDLED;
217 else
218 return IRQ_NONE;
178} 219}
179 220
180static int bfin_rtc_open(struct device *dev) 221static int bfin_rtc_open(struct device *dev)
@@ -183,13 +224,9 @@ static int bfin_rtc_open(struct device *dev)
183 224
184 dev_dbg_stamp(dev); 225 dev_dbg_stamp(dev);
185 226
186 ret = request_irq(IRQ_RTC, bfin_rtc_interrupt, IRQF_DISABLED, "rtc-bfin", dev); 227 ret = request_irq(IRQ_RTC, bfin_rtc_interrupt, IRQF_SHARED, to_platform_device(dev)->name, dev);
187 if (unlikely(ret)) { 228 if (!ret)
188 dev_err(dev, "request RTC IRQ failed with %d\n", ret); 229 bfin_rtc_reset(dev);
189 return ret;
190 }
191
192 rtc_bfin_reset(dev);
193 230
194 return ret; 231 return ret;
195} 232}
@@ -197,93 +234,70 @@ static int bfin_rtc_open(struct device *dev)
197static void bfin_rtc_release(struct device *dev) 234static void bfin_rtc_release(struct device *dev)
198{ 235{
199 dev_dbg_stamp(dev); 236 dev_dbg_stamp(dev);
200 rtc_bfin_reset(dev); 237 bfin_rtc_reset(dev);
201 free_irq(IRQ_RTC, dev); 238 free_irq(IRQ_RTC, dev);
202} 239}
203 240
241static void bfin_rtc_int_set(struct bfin_rtc *rtc, u16 rtc_int)
242{
243 bfin_write_RTC_ISTAT(rtc_int);
244 bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() | rtc_int);
245}
246static void bfin_rtc_int_clear(struct bfin_rtc *rtc, u16 rtc_int)
247{
248 bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() & rtc_int);
249}
250static void bfin_rtc_int_set_alarm(struct bfin_rtc *rtc)
251{
252 /* Blackfin has different bits for whether the alarm is
253 * more than 24 hours away.
254 */
255 bfin_rtc_int_set(rtc, (rtc->rtc_alarm.tm_yday == -1 ? RTC_ISTAT_ALARM : RTC_ISTAT_ALARM_DAY));
256}
204static int bfin_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) 257static int bfin_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
205{ 258{
206 struct bfin_rtc *rtc = dev_get_drvdata(dev); 259 struct bfin_rtc *rtc = dev_get_drvdata(dev);
260 int ret = 0;
207 261
208 dev_dbg_stamp(dev); 262 dev_dbg_stamp(dev);
209 263
264 bfin_rtc_sync_pending(dev);
265
210 switch (cmd) { 266 switch (cmd) {
211 case RTC_PIE_ON: 267 case RTC_PIE_ON:
212 dev_dbg_stamp(dev); 268 dev_dbg_stamp(dev);
213 spin_lock_irq(&rtc->lock); 269 bfin_rtc_int_set(rtc, RTC_ISTAT_STOPWATCH);
214 rtc_bfin_sync_pending();
215 bfin_write_RTC_ISTAT(RTC_ISTAT_STOPWATCH);
216 bfin_write_RTC_SWCNT(rtc->rtc_dev->irq_freq); 270 bfin_write_RTC_SWCNT(rtc->rtc_dev->irq_freq);
217 bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() | RTC_ISTAT_STOPWATCH); 271 break;
218 spin_unlock_irq(&rtc->lock);
219 return 0;
220 case RTC_PIE_OFF: 272 case RTC_PIE_OFF:
221 dev_dbg_stamp(dev); 273 dev_dbg_stamp(dev);
222 spin_lock_irq(&rtc->lock); 274 bfin_rtc_int_clear(rtc, ~RTC_ISTAT_STOPWATCH);
223 rtc_bfin_sync_pending(); 275 break;
224 bfin_write_RTC_SWCNT(0);
225 bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() & ~RTC_ISTAT_STOPWATCH);
226 spin_unlock_irq(&rtc->lock);
227 return 0;
228 276
229 case RTC_UIE_ON: 277 case RTC_UIE_ON:
230 dev_dbg_stamp(dev); 278 dev_dbg_stamp(dev);
231 spin_lock_irq(&rtc->lock); 279 bfin_rtc_int_set(rtc, RTC_ISTAT_SEC);
232 rtc_bfin_sync_pending(); 280 break;
233 bfin_write_RTC_ISTAT(RTC_ISTAT_SEC);
234 bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() | RTC_ISTAT_SEC);
235 spin_unlock_irq(&rtc->lock);
236 return 0;
237 case RTC_UIE_OFF: 281 case RTC_UIE_OFF:
238 dev_dbg_stamp(dev); 282 dev_dbg_stamp(dev);
239 spin_lock_irq(&rtc->lock); 283 bfin_rtc_int_clear(rtc, ~RTC_ISTAT_SEC);
240 rtc_bfin_sync_pending(); 284 break;
241 bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() & ~RTC_ISTAT_SEC);
242 spin_unlock_irq(&rtc->lock);
243 return 0;
244
245 case RTC_AIE_ON: {
246 unsigned long rtc_alarm;
247 u16 which_alarm;
248 int ret = 0;
249 285
286 case RTC_AIE_ON:
250 dev_dbg_stamp(dev); 287 dev_dbg_stamp(dev);
251 288 bfin_rtc_int_set_alarm(rtc);
252 spin_lock_irq(&rtc->lock); 289 break;
253
254 rtc_bfin_sync_pending();
255 if (rtc->rtc_alarm.tm_yday == -1) {
256 struct rtc_time now;
257 rtc_bfin_to_tm(bfin_read_RTC_STAT(), &now);
258 now.tm_sec = rtc->rtc_alarm.tm_sec;
259 now.tm_min = rtc->rtc_alarm.tm_min;
260 now.tm_hour = rtc->rtc_alarm.tm_hour;
261 ret = rtc_tm_to_time(&now, &rtc_alarm);
262 which_alarm = RTC_ISTAT_ALARM;
263 } else {
264 ret = rtc_tm_to_time(&rtc->rtc_alarm, &rtc_alarm);
265 which_alarm = RTC_ISTAT_ALARM_DAY;
266 }
267 if (ret == 0) {
268 bfin_write_RTC_ISTAT(which_alarm);
269 bfin_write_RTC_ALARM(rtc_time_to_bfin(rtc_alarm));
270 bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() | which_alarm);
271 }
272
273 spin_unlock_irq(&rtc->lock);
274
275 return ret;
276 }
277 case RTC_AIE_OFF: 290 case RTC_AIE_OFF:
278 dev_dbg_stamp(dev); 291 dev_dbg_stamp(dev);
279 spin_lock_irq(&rtc->lock); 292 bfin_rtc_int_clear(rtc, ~(RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY));
280 rtc_bfin_sync_pending(); 293 break;
281 bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() & ~(RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY)); 294
282 spin_unlock_irq(&rtc->lock); 295 default:
283 return 0; 296 dev_dbg_stamp(dev);
297 ret = -ENOIOCTLCMD;
284 } 298 }
285 299
286 return -ENOIOCTLCMD; 300 return ret;
287} 301}
288 302
289static int bfin_rtc_read_time(struct device *dev, struct rtc_time *tm) 303static int bfin_rtc_read_time(struct device *dev, struct rtc_time *tm)
@@ -292,10 +306,10 @@ static int bfin_rtc_read_time(struct device *dev, struct rtc_time *tm)
292 306
293 dev_dbg_stamp(dev); 307 dev_dbg_stamp(dev);
294 308
295 spin_lock_irq(&rtc->lock); 309 if (rtc->rtc_wrote_regs & 0x1)
296 rtc_bfin_sync_pending(); 310 bfin_rtc_sync_pending(dev);
311
297 rtc_bfin_to_tm(bfin_read_RTC_STAT(), tm); 312 rtc_bfin_to_tm(bfin_read_RTC_STAT(), tm);
298 spin_unlock_irq(&rtc->lock);
299 313
300 return 0; 314 return 0;
301} 315}
@@ -308,16 +322,14 @@ static int bfin_rtc_set_time(struct device *dev, struct rtc_time *tm)
308 322
309 dev_dbg_stamp(dev); 323 dev_dbg_stamp(dev);
310 324
311 spin_lock_irq(&rtc->lock);
312
313 ret = rtc_tm_to_time(tm, &now); 325 ret = rtc_tm_to_time(tm, &now);
314 if (ret == 0) { 326 if (ret == 0) {
315 rtc_bfin_sync_pending(); 327 if (rtc->rtc_wrote_regs & 0x1)
328 bfin_rtc_sync_pending(dev);
316 bfin_write_RTC_STAT(rtc_time_to_bfin(now)); 329 bfin_write_RTC_STAT(rtc_time_to_bfin(now));
330 rtc->rtc_wrote_regs = 0x1;
317 } 331 }
318 332
319 spin_unlock_irq(&rtc->lock);
320
321 return ret; 333 return ret;
322} 334}
323 335
@@ -326,6 +338,7 @@ static int bfin_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
326 struct bfin_rtc *rtc = dev_get_drvdata(dev); 338 struct bfin_rtc *rtc = dev_get_drvdata(dev);
327 dev_dbg_stamp(dev); 339 dev_dbg_stamp(dev);
328 alrm->time = rtc->rtc_alarm; 340 alrm->time = rtc->rtc_alarm;
341 bfin_rtc_sync_pending(dev);
329 alrm->enabled = !!(bfin_read_RTC_ICTL() & (RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY)); 342 alrm->enabled = !!(bfin_read_RTC_ICTL() & (RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY));
330 return 0; 343 return 0;
331} 344}
@@ -333,8 +346,20 @@ static int bfin_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
333static int bfin_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) 346static int bfin_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
334{ 347{
335 struct bfin_rtc *rtc = dev_get_drvdata(dev); 348 struct bfin_rtc *rtc = dev_get_drvdata(dev);
349 unsigned long rtc_alarm;
350
336 dev_dbg_stamp(dev); 351 dev_dbg_stamp(dev);
352
353 if (rtc_tm_to_time(&alrm->time, &rtc_alarm))
354 return -EINVAL;
355
337 rtc->rtc_alarm = alrm->time; 356 rtc->rtc_alarm = alrm->time;
357
358 bfin_rtc_sync_pending(dev);
359 bfin_write_RTC_ALARM(rtc_time_to_bfin(rtc_alarm));
360 if (alrm->enabled)
361 bfin_rtc_int_set_alarm(rtc);
362
338 return 0; 363 return 0;
339} 364}
340 365
@@ -393,8 +418,6 @@ static int __devinit bfin_rtc_probe(struct platform_device *pdev)
393 if (unlikely(!rtc)) 418 if (unlikely(!rtc))
394 return -ENOMEM; 419 return -ENOMEM;
395 420
396 spin_lock_init(&rtc->lock);
397
398 rtc->rtc_dev = rtc_device_register(pdev->name, &pdev->dev, &bfin_rtc_ops, THIS_MODULE); 421 rtc->rtc_dev = rtc_device_register(pdev->name, &pdev->dev, &bfin_rtc_ops, THIS_MODULE);
399 if (unlikely(IS_ERR(rtc))) { 422 if (unlikely(IS_ERR(rtc))) {
400 ret = PTR_ERR(rtc->rtc_dev); 423 ret = PTR_ERR(rtc->rtc_dev);