linux/drivers/rtc/rtc-imxdi.c
<<
>>
Prefs
   1/*
   2 * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
   3 * Copyright 2010 Orex Computed Radiography
   4 */
   5
   6/*
   7 * The code contained herein is licensed under the GNU General Public
   8 * License. You may obtain a copy of the GNU General Public License
   9 * Version 2 or later at the following locations:
  10 *
  11 * http://www.opensource.org/licenses/gpl-license.html
  12 * http://www.gnu.org/copyleft/gpl.html
  13 */
  14
  15/* based on rtc-mc13892.c */
  16
  17/*
  18 * This driver uses the 47-bit 32 kHz counter in the Freescale DryIce block
  19 * to implement a Linux RTC. Times and alarms are truncated to seconds.
  20 * Since the RTC framework performs API locking via rtc->ops_lock the
  21 * only simultaneous accesses we need to deal with is updating DryIce
  22 * registers while servicing an alarm.
  23 *
  24 * Note that reading the DSR (DryIce Status Register) automatically clears
  25 * the WCF (Write Complete Flag). All DryIce writes are synchronized to the
  26 * LP (Low Power) domain and set the WCF upon completion. Writes to the
  27 * DIER (DryIce Interrupt Enable Register) are the only exception. These
  28 * occur at normal bus speeds and do not set WCF.  Periodic interrupts are
  29 * not supported by the hardware.
  30 */
  31
  32#include <linux/io.h>
  33#include <linux/clk.h>
  34#include <linux/delay.h>
  35#include <linux/module.h>
  36#include <linux/platform_device.h>
  37#include <linux/rtc.h>
  38#include <linux/sched.h>
  39#include <linux/workqueue.h>
  40
  41/* DryIce Register Definitions */
  42
  43#define DTCMR     0x00           /* Time Counter MSB Reg */
  44#define DTCLR     0x04           /* Time Counter LSB Reg */
  45
  46#define DCAMR     0x08           /* Clock Alarm MSB Reg */
  47#define DCALR     0x0c           /* Clock Alarm LSB Reg */
  48#define DCAMR_UNSET  0xFFFFFFFF  /* doomsday - 1 sec */
  49
  50#define DCR       0x10           /* Control Reg */
  51#define DCR_TCE   (1 << 3)       /* Time Counter Enable */
  52
  53#define DSR       0x14           /* Status Reg */
  54#define DSR_WBF   (1 << 10)      /* Write Busy Flag */
  55#define DSR_WNF   (1 << 9)       /* Write Next Flag */
  56#define DSR_WCF   (1 << 8)       /* Write Complete Flag */
  57#define DSR_WEF   (1 << 7)       /* Write Error Flag */
  58#define DSR_CAF   (1 << 4)       /* Clock Alarm Flag */
  59#define DSR_NVF   (1 << 1)       /* Non-Valid Flag */
  60#define DSR_SVF   (1 << 0)       /* Security Violation Flag */
  61
  62#define DIER      0x18           /* Interrupt Enable Reg */
  63#define DIER_WNIE (1 << 9)       /* Write Next Interrupt Enable */
  64#define DIER_WCIE (1 << 8)       /* Write Complete Interrupt Enable */
  65#define DIER_WEIE (1 << 7)       /* Write Error Interrupt Enable */
  66#define DIER_CAIE (1 << 4)       /* Clock Alarm Interrupt Enable */
  67
  68/**
  69 * struct imxdi_dev - private imxdi rtc data
  70 * @pdev: pionter to platform dev
  71 * @rtc: pointer to rtc struct
  72 * @ioaddr: IO registers pointer
  73 * @irq: dryice normal interrupt
  74 * @clk: input reference clock
  75 * @dsr: copy of the DSR register
  76 * @irq_lock: interrupt enable register (DIER) lock
  77 * @write_wait: registers write complete queue
  78 * @write_mutex: serialize registers write
  79 * @work: schedule alarm work
  80 */
  81struct imxdi_dev {
  82        struct platform_device *pdev;
  83        struct rtc_device *rtc;
  84        void __iomem *ioaddr;
  85        int irq;
  86        struct clk *clk;
  87        u32 dsr;
  88        spinlock_t irq_lock;
  89        wait_queue_head_t write_wait;
  90        struct mutex write_mutex;
  91        struct work_struct work;
  92};
  93
  94/*
  95 * enable a dryice interrupt
  96 */
  97static void di_int_enable(struct imxdi_dev *imxdi, u32 intr)
  98{
  99        unsigned long flags;
 100
 101        spin_lock_irqsave(&imxdi->irq_lock, flags);
 102        __raw_writel(__raw_readl(imxdi->ioaddr + DIER) | intr,
 103                        imxdi->ioaddr + DIER);
 104        spin_unlock_irqrestore(&imxdi->irq_lock, flags);
 105}
 106
 107/*
 108 * disable a dryice interrupt
 109 */
 110static void di_int_disable(struct imxdi_dev *imxdi, u32 intr)
 111{
 112        unsigned long flags;
 113
 114        spin_lock_irqsave(&imxdi->irq_lock, flags);
 115        __raw_writel(__raw_readl(imxdi->ioaddr + DIER) & ~intr,
 116                        imxdi->ioaddr + DIER);
 117        spin_unlock_irqrestore(&imxdi->irq_lock, flags);
 118}
 119
 120/*
 121 * This function attempts to clear the dryice write-error flag.
 122 *
 123 * A dryice write error is similar to a bus fault and should not occur in
 124 * normal operation.  Clearing the flag requires another write, so the root
 125 * cause of the problem may need to be fixed before the flag can be cleared.
 126 */
 127static void clear_write_error(struct imxdi_dev *imxdi)
 128{
 129        int cnt;
 130
 131        dev_warn(&imxdi->pdev->dev, "WARNING: Register write error!\n");
 132
 133        /* clear the write error flag */
 134        __raw_writel(DSR_WEF, imxdi->ioaddr + DSR);
 135
 136        /* wait for it to take effect */
 137        for (cnt = 0; cnt < 1000; cnt++) {
 138                if ((__raw_readl(imxdi->ioaddr + DSR) & DSR_WEF) == 0)
 139                        return;
 140                udelay(10);
 141        }
 142        dev_err(&imxdi->pdev->dev,
 143                        "ERROR: Cannot clear write-error flag!\n");
 144}
 145
 146/*
 147 * Write a dryice register and wait until it completes.
 148 *
 149 * This function uses interrupts to determine when the
 150 * write has completed.
 151 */
 152static int di_write_wait(struct imxdi_dev *imxdi, u32 val, int reg)
 153{
 154        int ret;
 155        int rc = 0;
 156
 157        /* serialize register writes */
 158        mutex_lock(&imxdi->write_mutex);
 159
 160        /* enable the write-complete interrupt */
 161        di_int_enable(imxdi, DIER_WCIE);
 162
 163        imxdi->dsr = 0;
 164
 165        /* do the register write */
 166        __raw_writel(val, imxdi->ioaddr + reg);
 167
 168        /* wait for the write to finish */
 169        ret = wait_event_interruptible_timeout(imxdi->write_wait,
 170                        imxdi->dsr & (DSR_WCF | DSR_WEF), msecs_to_jiffies(1));
 171        if (ret < 0) {
 172                rc = ret;
 173                goto out;
 174        } else if (ret == 0) {
 175                dev_warn(&imxdi->pdev->dev,
 176                                "Write-wait timeout "
 177                                "val = 0x%08x reg = 0x%08x\n", val, reg);
 178        }
 179
 180        /* check for write error */
 181        if (imxdi->dsr & DSR_WEF) {
 182                clear_write_error(imxdi);
 183                rc = -EIO;
 184        }
 185
 186out:
 187        mutex_unlock(&imxdi->write_mutex);
 188
 189        return rc;
 190}
 191
 192/*
 193 * read the seconds portion of the current time from the dryice time counter
 194 */
 195static int dryice_rtc_read_time(struct device *dev, struct rtc_time *tm)
 196{
 197        struct imxdi_dev *imxdi = dev_get_drvdata(dev);
 198        unsigned long now;
 199
 200        now = __raw_readl(imxdi->ioaddr + DTCMR);
 201        rtc_time_to_tm(now, tm);
 202
 203        return 0;
 204}
 205
 206/*
 207 * set the seconds portion of dryice time counter and clear the
 208 * fractional part.
 209 */
 210static int dryice_rtc_set_mmss(struct device *dev, unsigned long secs)
 211{
 212        struct imxdi_dev *imxdi = dev_get_drvdata(dev);
 213        int rc;
 214
 215        /* zero the fractional part first */
 216        rc = di_write_wait(imxdi, 0, DTCLR);
 217        if (rc == 0)
 218                rc = di_write_wait(imxdi, secs, DTCMR);
 219
 220        return rc;
 221}
 222
 223static int dryice_rtc_alarm_irq_enable(struct device *dev,
 224                unsigned int enabled)
 225{
 226        struct imxdi_dev *imxdi = dev_get_drvdata(dev);
 227
 228        if (enabled)
 229                di_int_enable(imxdi, DIER_CAIE);
 230        else
 231                di_int_disable(imxdi, DIER_CAIE);
 232
 233        return 0;
 234}
 235
 236/*
 237 * read the seconds portion of the alarm register.
 238 * the fractional part of the alarm register is always zero.
 239 */
 240static int dryice_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
 241{
 242        struct imxdi_dev *imxdi = dev_get_drvdata(dev);
 243        u32 dcamr;
 244
 245        dcamr = __raw_readl(imxdi->ioaddr + DCAMR);
 246        rtc_time_to_tm(dcamr, &alarm->time);
 247
 248        /* alarm is enabled if the interrupt is enabled */
 249        alarm->enabled = (__raw_readl(imxdi->ioaddr + DIER) & DIER_CAIE) != 0;
 250
 251        /* don't allow the DSR read to mess up DSR_WCF */
 252        mutex_lock(&imxdi->write_mutex);
 253
 254        /* alarm is pending if the alarm flag is set */
 255        alarm->pending = (__raw_readl(imxdi->ioaddr + DSR) & DSR_CAF) != 0;
 256
 257        mutex_unlock(&imxdi->write_mutex);
 258
 259        return 0;
 260}
 261
 262/*
 263 * set the seconds portion of dryice alarm register
 264 */
 265static int dryice_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
 266{
 267        struct imxdi_dev *imxdi = dev_get_drvdata(dev);
 268        unsigned long now;
 269        unsigned long alarm_time;
 270        int rc;
 271
 272        rc = rtc_tm_to_time(&alarm->time, &alarm_time);
 273        if (rc)
 274                return rc;
 275
 276        /* don't allow setting alarm in the past */
 277        now = __raw_readl(imxdi->ioaddr + DTCMR);
 278        if (alarm_time < now)
 279                return -EINVAL;
 280
 281        /* write the new alarm time */
 282        rc = di_write_wait(imxdi, (u32)alarm_time, DCAMR);
 283        if (rc)
 284                return rc;
 285
 286        if (alarm->enabled)
 287                di_int_enable(imxdi, DIER_CAIE);  /* enable alarm intr */
 288        else
 289                di_int_disable(imxdi, DIER_CAIE); /* disable alarm intr */
 290
 291        return 0;
 292}
 293
 294static struct rtc_class_ops dryice_rtc_ops = {
 295        .read_time              = dryice_rtc_read_time,
 296        .set_mmss               = dryice_rtc_set_mmss,
 297        .alarm_irq_enable       = dryice_rtc_alarm_irq_enable,
 298        .read_alarm             = dryice_rtc_read_alarm,
 299        .set_alarm              = dryice_rtc_set_alarm,
 300};
 301
 302/*
 303 * dryice "normal" interrupt handler
 304 */
 305static irqreturn_t dryice_norm_irq(int irq, void *dev_id)
 306{
 307        struct imxdi_dev *imxdi = dev_id;
 308        u32 dsr, dier;
 309        irqreturn_t rc = IRQ_NONE;
 310
 311        dier = __raw_readl(imxdi->ioaddr + DIER);
 312
 313        /* handle write complete and write error cases */
 314        if ((dier & DIER_WCIE)) {
 315                /*If the write wait queue is empty then there is no pending
 316                  operations. It means the interrupt is for DryIce -Security.
 317                  IRQ must be returned as none.*/
 318                if (list_empty_careful(&imxdi->write_wait.task_list))
 319                        return rc;
 320
 321                /* DSR_WCF clears itself on DSR read */
 322                dsr = __raw_readl(imxdi->ioaddr + DSR);
 323                if ((dsr & (DSR_WCF | DSR_WEF))) {
 324                        /* mask the interrupt */
 325                        di_int_disable(imxdi, DIER_WCIE);
 326
 327                        /* save the dsr value for the wait queue */
 328                        imxdi->dsr |= dsr;
 329
 330                        wake_up_interruptible(&imxdi->write_wait);
 331                        rc = IRQ_HANDLED;
 332                }
 333        }
 334
 335        /* handle the alarm case */
 336        if ((dier & DIER_CAIE)) {
 337                /* DSR_WCF clears itself on DSR read */
 338                dsr = __raw_readl(imxdi->ioaddr + DSR);
 339                if (dsr & DSR_CAF) {
 340                        /* mask the interrupt */
 341                        di_int_disable(imxdi, DIER_CAIE);
 342
 343                        /* finish alarm in user context */
 344                        schedule_work(&imxdi->work);
 345                        rc = IRQ_HANDLED;
 346                }
 347        }
 348        return rc;
 349}
 350
 351/*
 352 * post the alarm event from user context so it can sleep
 353 * on the write completion.
 354 */
 355static void dryice_work(struct work_struct *work)
 356{
 357        struct imxdi_dev *imxdi = container_of(work,
 358                        struct imxdi_dev, work);
 359
 360        /* dismiss the interrupt (ignore error) */
 361        di_write_wait(imxdi, DSR_CAF, DSR);
 362
 363        /* pass the alarm event to the rtc framework. */
 364        rtc_update_irq(imxdi->rtc, 1, RTC_AF | RTC_IRQF);
 365}
 366
 367/*
 368 * probe for dryice rtc device
 369 */
 370static int dryice_rtc_probe(struct platform_device *pdev)
 371{
 372        struct resource *res;
 373        struct imxdi_dev *imxdi;
 374        int rc;
 375
 376        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 377        if (!res)
 378                return -ENODEV;
 379
 380        imxdi = devm_kzalloc(&pdev->dev, sizeof(*imxdi), GFP_KERNEL);
 381        if (!imxdi)
 382                return -ENOMEM;
 383
 384        imxdi->pdev = pdev;
 385
 386        if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res),
 387                                pdev->name))
 388                return -EBUSY;
 389
 390        imxdi->ioaddr = devm_ioremap(&pdev->dev, res->start,
 391                        resource_size(res));
 392        if (imxdi->ioaddr == NULL)
 393                return -ENOMEM;
 394
 395        spin_lock_init(&imxdi->irq_lock);
 396
 397        imxdi->irq = platform_get_irq(pdev, 0);
 398        if (imxdi->irq < 0)
 399                return imxdi->irq;
 400
 401        init_waitqueue_head(&imxdi->write_wait);
 402
 403        INIT_WORK(&imxdi->work, dryice_work);
 404
 405        mutex_init(&imxdi->write_mutex);
 406
 407        imxdi->clk = clk_get(&pdev->dev, NULL);
 408        if (IS_ERR(imxdi->clk))
 409                return PTR_ERR(imxdi->clk);
 410        clk_prepare_enable(imxdi->clk);
 411
 412        /*
 413         * Initialize dryice hardware
 414         */
 415
 416        /* mask all interrupts */
 417        __raw_writel(0, imxdi->ioaddr + DIER);
 418
 419        rc = devm_request_irq(&pdev->dev, imxdi->irq, dryice_norm_irq,
 420                        IRQF_SHARED, pdev->name, imxdi);
 421        if (rc) {
 422                dev_warn(&pdev->dev, "interrupt not available.\n");
 423                goto err;
 424        }
 425
 426        /* put dryice into valid state */
 427        if (__raw_readl(imxdi->ioaddr + DSR) & DSR_NVF) {
 428                rc = di_write_wait(imxdi, DSR_NVF | DSR_SVF, DSR);
 429                if (rc)
 430                        goto err;
 431        }
 432
 433        /* initialize alarm */
 434        rc = di_write_wait(imxdi, DCAMR_UNSET, DCAMR);
 435        if (rc)
 436                goto err;
 437        rc = di_write_wait(imxdi, 0, DCALR);
 438        if (rc)
 439                goto err;
 440
 441        /* clear alarm flag */
 442        if (__raw_readl(imxdi->ioaddr + DSR) & DSR_CAF) {
 443                rc = di_write_wait(imxdi, DSR_CAF, DSR);
 444                if (rc)
 445                        goto err;
 446        }
 447
 448        /* the timer won't count if it has never been written to */
 449        if (__raw_readl(imxdi->ioaddr + DTCMR) == 0) {
 450                rc = di_write_wait(imxdi, 0, DTCMR);
 451                if (rc)
 452                        goto err;
 453        }
 454
 455        /* start keeping time */
 456        if (!(__raw_readl(imxdi->ioaddr + DCR) & DCR_TCE)) {
 457                rc = di_write_wait(imxdi,
 458                                __raw_readl(imxdi->ioaddr + DCR) | DCR_TCE,
 459                                DCR);
 460                if (rc)
 461                        goto err;
 462        }
 463
 464        platform_set_drvdata(pdev, imxdi);
 465        imxdi->rtc = rtc_device_register(pdev->name, &pdev->dev,
 466                                  &dryice_rtc_ops, THIS_MODULE);
 467        if (IS_ERR(imxdi->rtc)) {
 468                rc = PTR_ERR(imxdi->rtc);
 469                goto err;
 470        }
 471
 472        return 0;
 473
 474err:
 475        clk_disable_unprepare(imxdi->clk);
 476        clk_put(imxdi->clk);
 477
 478        return rc;
 479}
 480
 481static int __devexit dryice_rtc_remove(struct platform_device *pdev)
 482{
 483        struct imxdi_dev *imxdi = platform_get_drvdata(pdev);
 484
 485        flush_work(&imxdi->work);
 486
 487        /* mask all interrupts */
 488        __raw_writel(0, imxdi->ioaddr + DIER);
 489
 490        rtc_device_unregister(imxdi->rtc);
 491
 492        clk_disable_unprepare(imxdi->clk);
 493        clk_put(imxdi->clk);
 494
 495        return 0;
 496}
 497
 498static struct platform_driver dryice_rtc_driver = {
 499        .driver = {
 500                   .name = "imxdi_rtc",
 501                   .owner = THIS_MODULE,
 502                   },
 503        .remove = __devexit_p(dryice_rtc_remove),
 504};
 505
 506static int __init dryice_rtc_init(void)
 507{
 508        return platform_driver_probe(&dryice_rtc_driver, dryice_rtc_probe);
 509}
 510
 511static void __exit dryice_rtc_exit(void)
 512{
 513        platform_driver_unregister(&dryice_rtc_driver);
 514}
 515
 516module_init(dryice_rtc_init);
 517module_exit(dryice_rtc_exit);
 518
 519MODULE_AUTHOR("Freescale Semiconductor, Inc.");
 520MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
 521MODULE_DESCRIPTION("IMX DryIce Realtime Clock Driver (RTC)");
 522MODULE_LICENSE("GPL");
 523
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.