linux/drivers/rtc/rtc-bfin.c
<<
>>
Prefs
   1/*
   2 * Blackfin On-Chip Real Time Clock Driver
   3 *  Supports BF53[123]/BF53[467]/BF54[2489]
   4 *
   5 * Copyright 2004-2007 Analog Devices Inc.
   6 *
   7 * Enter bugs at http://blackfin.uclinux.org/
   8 *
   9 * Licensed under the GPL-2 or later.
  10 */
  11
  12/* The biggest issue we deal with in this driver is that register writes are
  13 * synced to the RTC frequency of 1Hz.  So if you write to a register and
  14 * attempt to write again before the first write has completed, the new write
  15 * is simply discarded.  This can easily be troublesome if userspace disables
  16 * one event (say periodic) and then right after enables an event (say alarm).
  17 * Since all events are maintained in the same interrupt mask register, if
  18 * we wrote to it to disable the first event and then wrote to it again to
  19 * enable the second event, that second event would not be enabled as the
  20 * write would be discarded and things quickly fall apart.
  21 *
  22 * To keep this delay from significantly degrading performance (we, in theory,
  23 * would have to sleep for up to 1 second everytime we wanted to write a
  24 * register), we only check the write pending status before we start to issue
  25 * a new write.  We bank on the idea that it doesnt matter when the sync
  26 * happens so long as we don't attempt another write before it does.  The only
  27 * time userspace would take this penalty is when they try and do multiple
  28 * operations right after another ... but in this case, they need to take the
  29 * sync penalty, so we should be OK.
  30 *
  31 * Also note that the RTC_ISTAT register does not suffer this penalty; its
  32 * writes to clear status registers complete immediately.
  33 */
  34
  35#include <linux/module.h>
  36#include <linux/kernel.h>
  37#include <linux/bcd.h>
  38#include <linux/rtc.h>
  39#include <linux/init.h>
  40#include <linux/platform_device.h>
  41#include <linux/seq_file.h>
  42#include <linux/interrupt.h>
  43#include <linux/spinlock.h>
  44#include <linux/delay.h>
  45
  46#include <asm/blackfin.h>
  47
  48#define stamp(fmt, args...) pr_debug("%s:%i: " fmt "\n", __FUNCTION__, __LINE__, ## args)
  49#define stampit() stamp("here i am")
  50
  51struct bfin_rtc {
  52        struct rtc_device *rtc_dev;
  53        struct rtc_time rtc_alarm;
  54        spinlock_t lock;
  55};
  56
  57/* Bit values for the ISTAT / ICTL registers */
  58#define RTC_ISTAT_WRITE_COMPLETE  0x8000
  59#define RTC_ISTAT_WRITE_PENDING   0x4000
  60#define RTC_ISTAT_ALARM_DAY       0x0040
  61#define RTC_ISTAT_24HR            0x0020
  62#define RTC_ISTAT_HOUR            0x0010
  63#define RTC_ISTAT_MIN             0x0008
  64#define RTC_ISTAT_SEC             0x0004
  65#define RTC_ISTAT_ALARM           0x0002
  66#define RTC_ISTAT_STOPWATCH       0x0001
  67
  68/* Shift values for RTC_STAT register */
  69#define DAY_BITS_OFF    17
  70#define HOUR_BITS_OFF   12
  71#define MIN_BITS_OFF    6
  72#define SEC_BITS_OFF    0
  73
  74/* Some helper functions to convert between the common RTC notion of time
  75 * and the internal Blackfin notion that is stored in 32bits.
  76 */
  77static inline u32 rtc_time_to_bfin(unsigned long now)
  78{
  79        u32 sec  = (now % 60);
  80        u32 min  = (now % (60 * 60)) / 60;
  81        u32 hour = (now % (60 * 60 * 24)) / (60 * 60);
  82        u32 days = (now / (60 * 60 * 24));
  83        return (sec  << SEC_BITS_OFF) +
  84               (min  << MIN_BITS_OFF) +
  85               (hour << HOUR_BITS_OFF) +
  86               (days << DAY_BITS_OFF);
  87}
  88static inline unsigned long rtc_bfin_to_time(u32 rtc_bfin)
  89{
  90        return (((rtc_bfin >> SEC_BITS_OFF)  & 0x003F)) +
  91               (((rtc_bfin >> MIN_BITS_OFF)  & 0x003F) * 60) +
  92               (((rtc_bfin >> HOUR_BITS_OFF) & 0x001F) * 60 * 60) +
  93               (((rtc_bfin >> DAY_BITS_OFF)  & 0x7FFF) * 60 * 60 * 24);
  94}
  95static inline void rtc_bfin_to_tm(u32 rtc_bfin, struct rtc_time *tm)
  96{
  97        rtc_time_to_tm(rtc_bfin_to_time(rtc_bfin), tm);
  98}
  99
 100/* Wait for the previous write to a RTC register to complete.
 101 * Unfortunately, we can't sleep here as that introduces a race condition when
 102 * turning on interrupt events.  Consider this:
 103 *  - process sets alarm
 104 *  - process enables alarm
 105 *  - process sleeps while waiting for rtc write to sync
 106 *  - interrupt fires while process is sleeping
 107 *  - interrupt acks the event by writing to ISTAT
 108 *  - interrupt sets the WRITE PENDING bit
 109 *  - interrupt handler finishes
 110 *  - process wakes up, sees WRITE PENDING bit set, goes to sleep
 111 *  - interrupt fires while process is sleeping
 112 * If anyone can point out the obvious solution here, i'm listening :).  This
 113 * shouldn't be an issue on an SMP or preempt system as this function should
 114 * only be called with the rtc lock held.
 115 */
 116static void rtc_bfin_sync_pending(void)
 117{
 118        stampit();
 119        while (!(bfin_read_RTC_ISTAT() & RTC_ISTAT_WRITE_COMPLETE)) {
 120                if (!(bfin_read_RTC_ISTAT() & RTC_ISTAT_WRITE_PENDING))
 121                        break;
 122        }
 123        bfin_write_RTC_ISTAT(RTC_ISTAT_WRITE_COMPLETE);
 124}
 125
 126static void rtc_bfin_reset(struct bfin_rtc *rtc)
 127{
 128        /* Initialize the RTC. Enable pre-scaler to scale RTC clock
 129         * to 1Hz and clear interrupt/status registers. */
 130        spin_lock_irq(&rtc->lock);
 131        rtc_bfin_sync_pending();
 132        bfin_write_RTC_PREN(0x1);
 133        bfin_write_RTC_ICTL(0);
 134        bfin_write_RTC_SWCNT(0);
 135        bfin_write_RTC_ALARM(0);
 136        bfin_write_RTC_ISTAT(0xFFFF);
 137        spin_unlock_irq(&rtc->lock);
 138}
 139
 140static irqreturn_t bfin_rtc_interrupt(int irq, void *dev_id)
 141{
 142        struct platform_device *pdev = to_platform_device(dev_id);
 143        struct bfin_rtc *rtc = platform_get_drvdata(pdev);
 144        unsigned long events = 0;
 145        u16 rtc_istat;
 146
 147        stampit();
 148
 149        spin_lock_irq(&rtc->lock);
 150
 151        rtc_istat = bfin_read_RTC_ISTAT();
 152
 153        if (rtc_istat & (RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY)) {
 154                bfin_write_RTC_ISTAT(RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY);
 155                events |= RTC_AF | RTC_IRQF;
 156        }
 157
 158        if (rtc_istat & RTC_ISTAT_STOPWATCH) {
 159                bfin_write_RTC_ISTAT(RTC_ISTAT_STOPWATCH);
 160                events |= RTC_PF | RTC_IRQF;
 161                bfin_write_RTC_SWCNT(rtc->rtc_dev->irq_freq);
 162        }
 163
 164        if (rtc_istat & RTC_ISTAT_SEC) {
 165                bfin_write_RTC_ISTAT(RTC_ISTAT_SEC);
 166                events |= RTC_UF | RTC_IRQF;
 167        }
 168
 169        rtc_update_irq(rtc->rtc_dev, 1, events);
 170
 171        spin_unlock_irq(&rtc->lock);
 172
 173        return IRQ_HANDLED;
 174}
 175
 176static int bfin_rtc_open(struct device *dev)
 177{
 178        struct bfin_rtc *rtc = dev_get_drvdata(dev);
 179        int ret;
 180
 181        stampit();
 182
 183        ret = request_irq(IRQ_RTC, bfin_rtc_interrupt, IRQF_DISABLED, "rtc-bfin", dev);
 184        if (unlikely(ret)) {
 185                dev_err(dev, "request RTC IRQ failed with %d\n", ret);
 186                return ret;
 187        }
 188
 189        rtc_bfin_reset(rtc);
 190
 191        return ret;
 192}
 193
 194static void bfin_rtc_release(struct device *dev)
 195{
 196        struct bfin_rtc *rtc = dev_get_drvdata(dev);
 197        stampit();
 198        rtc_bfin_reset(rtc);
 199        free_irq(IRQ_RTC, dev);
 200}
 201
 202static int bfin_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
 203{
 204        struct bfin_rtc *rtc = dev_get_drvdata(dev);
 205
 206        stampit();
 207
 208        switch (cmd) {
 209        case RTC_PIE_ON:
 210                stampit();
 211                spin_lock_irq(&rtc->lock);
 212                rtc_bfin_sync_pending();
 213                bfin_write_RTC_ISTAT(RTC_ISTAT_STOPWATCH);
 214                bfin_write_RTC_SWCNT(rtc->rtc_dev->irq_freq);
 215                bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() | RTC_ISTAT_STOPWATCH);
 216                spin_unlock_irq(&rtc->lock);
 217                return 0;
 218        case RTC_PIE_OFF:
 219                stampit();
 220                spin_lock_irq(&rtc->lock);
 221                rtc_bfin_sync_pending();
 222                bfin_write_RTC_SWCNT(0);
 223                bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() & ~RTC_ISTAT_STOPWATCH);
 224                spin_unlock_irq(&rtc->lock);
 225                return 0;
 226
 227        case RTC_UIE_ON:
 228                stampit();
 229                spin_lock_irq(&rtc->lock);
 230                rtc_bfin_sync_pending();
 231                bfin_write_RTC_ISTAT(RTC_ISTAT_SEC);
 232                bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() | RTC_ISTAT_SEC);
 233                spin_unlock_irq(&rtc->lock);
 234                return 0;
 235        case RTC_UIE_OFF:
 236                stampit();
 237                spin_lock_irq(&rtc->lock);
 238                rtc_bfin_sync_pending();
 239                bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() & ~RTC_ISTAT_SEC);
 240                spin_unlock_irq(&rtc->lock);
 241                return 0;
 242
 243        case RTC_AIE_ON: {
 244                unsigned long rtc_alarm;
 245                u16 which_alarm;
 246                int ret = 0;
 247
 248                stampit();
 249
 250                spin_lock_irq(&rtc->lock);
 251
 252                rtc_bfin_sync_pending();
 253                if (rtc->rtc_alarm.tm_yday == -1) {
 254                        struct rtc_time now;
 255                        rtc_bfin_to_tm(bfin_read_RTC_STAT(), &now);
 256                        now.tm_sec = rtc->rtc_alarm.tm_sec;
 257                        now.tm_min = rtc->rtc_alarm.tm_min;
 258                        now.tm_hour = rtc->rtc_alarm.tm_hour;
 259                        ret = rtc_tm_to_time(&now, &rtc_alarm);
 260                        which_alarm = RTC_ISTAT_ALARM;
 261                } else {
 262                        ret = rtc_tm_to_time(&rtc->rtc_alarm, &rtc_alarm);
 263                        which_alarm = RTC_ISTAT_ALARM_DAY;
 264                }
 265                if (ret == 0) {
 266                        bfin_write_RTC_ISTAT(which_alarm);
 267                        bfin_write_RTC_ALARM(rtc_time_to_bfin(rtc_alarm));
 268                        bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() | which_alarm);
 269                }
 270
 271                spin_unlock_irq(&rtc->lock);
 272
 273                return ret;
 274        }
 275        case RTC_AIE_OFF:
 276                stampit();
 277                spin_lock_irq(&rtc->lock);
 278                rtc_bfin_sync_pending();
 279                bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() & ~(RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY));
 280                spin_unlock_irq(&rtc->lock);
 281                return 0;
 282        }
 283
 284        return -ENOIOCTLCMD;
 285}
 286
 287static int bfin_rtc_read_time(struct device *dev, struct rtc_time *tm)
 288{
 289        struct bfin_rtc *rtc = dev_get_drvdata(dev);
 290
 291        stampit();
 292
 293        spin_lock_irq(&rtc->lock);
 294        rtc_bfin_sync_pending();
 295        rtc_bfin_to_tm(bfin_read_RTC_STAT(), tm);
 296        spin_unlock_irq(&rtc->lock);
 297
 298        return 0;
 299}
 300
 301static int bfin_rtc_set_time(struct device *dev, struct rtc_time *tm)
 302{
 303        struct bfin_rtc *rtc = dev_get_drvdata(dev);
 304        int ret;
 305        unsigned long now;
 306
 307        stampit();
 308
 309        spin_lock_irq(&rtc->lock);
 310
 311        ret = rtc_tm_to_time(tm, &now);
 312        if (ret == 0) {
 313                rtc_bfin_sync_pending();
 314                bfin_write_RTC_STAT(rtc_time_to_bfin(now));
 315        }
 316
 317        spin_unlock_irq(&rtc->lock);
 318
 319        return ret;
 320}
 321
 322static int bfin_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 323{
 324        struct bfin_rtc *rtc = dev_get_drvdata(dev);
 325        stampit();
 326        memcpy(&alrm->time, &rtc->rtc_alarm, sizeof(struct rtc_time));
 327        alrm->pending = !!(bfin_read_RTC_ICTL() & (RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY));
 328        return 0;
 329}
 330
 331static int bfin_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 332{
 333        struct bfin_rtc *rtc = dev_get_drvdata(dev);
 334        stampit();
 335        memcpy(&rtc->rtc_alarm, &alrm->time, sizeof(struct rtc_time));
 336        return 0;
 337}
 338
 339static int bfin_rtc_proc(struct device *dev, struct seq_file *seq)
 340{
 341#define yesno(x) (x ? "yes" : "no")
 342        u16 ictl = bfin_read_RTC_ICTL();
 343        stampit();
 344        seq_printf(seq, "alarm_IRQ\t: %s\n", yesno(ictl & RTC_ISTAT_ALARM));
 345        seq_printf(seq, "wkalarm_IRQ\t: %s\n", yesno(ictl & RTC_ISTAT_ALARM_DAY));
 346        seq_printf(seq, "seconds_IRQ\t: %s\n", yesno(ictl & RTC_ISTAT_SEC));
 347        seq_printf(seq, "periodic_IRQ\t: %s\n", yesno(ictl & RTC_ISTAT_STOPWATCH));
 348#ifdef DEBUG
 349        seq_printf(seq, "RTC_STAT\t: 0x%08X\n", bfin_read_RTC_STAT());
 350        seq_printf(seq, "RTC_ICTL\t: 0x%04X\n", bfin_read_RTC_ICTL());
 351        seq_printf(seq, "RTC_ISTAT\t: 0x%04X\n", bfin_read_RTC_ISTAT());
 352        seq_printf(seq, "RTC_SWCNT\t: 0x%04X\n", bfin_read_RTC_SWCNT());
 353        seq_printf(seq, "RTC_ALARM\t: 0x%08X\n", bfin_read_RTC_ALARM());
 354        seq_printf(seq, "RTC_PREN\t: 0x%04X\n", bfin_read_RTC_PREN());
 355#endif
 356        return 0;
 357}
 358
 359static int bfin_irq_set_freq(struct device *dev, int freq)
 360{
 361        struct bfin_rtc *rtc = dev_get_drvdata(dev);
 362        stampit();
 363        rtc->rtc_dev->irq_freq = freq;
 364        return 0;
 365}
 366
 367static struct rtc_class_ops bfin_rtc_ops = {
 368        .open          = bfin_rtc_open,
 369        .release       = bfin_rtc_release,
 370        .ioctl         = bfin_rtc_ioctl,
 371        .read_time     = bfin_rtc_read_time,
 372        .set_time      = bfin_rtc_set_time,
 373        .read_alarm    = bfin_rtc_read_alarm,
 374        .set_alarm     = bfin_rtc_set_alarm,
 375        .proc          = bfin_rtc_proc,
 376        .irq_set_freq  = bfin_irq_set_freq,
 377};
 378
 379static int __devinit bfin_rtc_probe(struct platform_device *pdev)
 380{
 381        struct bfin_rtc *rtc;
 382        int ret = 0;
 383
 384        stampit();
 385
 386        rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
 387        if (unlikely(!rtc))
 388                return -ENOMEM;
 389
 390        spin_lock_init(&rtc->lock);
 391
 392        rtc->rtc_dev = rtc_device_register(pdev->name, &pdev->dev, &bfin_rtc_ops, THIS_MODULE);
 393        if (unlikely(IS_ERR(rtc))) {
 394                ret = PTR_ERR(rtc->rtc_dev);
 395                goto err;
 396        }
 397        rtc->rtc_dev->irq_freq = 0;
 398        rtc->rtc_dev->max_user_freq = (2 << 16); /* stopwatch is an unsigned 16 bit reg */
 399
 400        platform_set_drvdata(pdev, rtc);
 401
 402        return 0;
 403
 404err:
 405        kfree(rtc);
 406        return ret;
 407}
 408
 409static int __devexit bfin_rtc_remove(struct platform_device *pdev)
 410{
 411        struct bfin_rtc *rtc = platform_get_drvdata(pdev);
 412
 413        rtc_device_unregister(rtc->rtc_dev);
 414        platform_set_drvdata(pdev, NULL);
 415        kfree(rtc);
 416
 417        return 0;
 418}
 419
 420static struct platform_driver bfin_rtc_driver = {
 421        .driver         = {
 422                .name   = "rtc-bfin",
 423                .owner  = THIS_MODULE,
 424        },
 425        .probe          = bfin_rtc_probe,
 426        .remove         = __devexit_p(bfin_rtc_remove),
 427};
 428
 429static int __init bfin_rtc_init(void)
 430{
 431        stampit();
 432        return platform_driver_register(&bfin_rtc_driver);
 433}
 434
 435static void __exit bfin_rtc_exit(void)
 436{
 437        platform_driver_unregister(&bfin_rtc_driver);
 438}
 439
 440module_init(bfin_rtc_init);
 441module_exit(bfin_rtc_exit);
 442
 443MODULE_DESCRIPTION("Blackfin On-Chip Real Time Clock Driver");
 444MODULE_AUTHOR("Mike Frysinger <vapier@gentoo.org>");
 445MODULE_LICENSE("GPL");
 446