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-> 378        else
ENODEV;
 379                 290<3a>
imxdi = ioaddr + s="sref">devm_kzalloc(&pdev->dev, res->start,  381        return 0;
rc = res),
 292<3a>}
ioaddr + ss="sref">devm_kzaNUL href="drivers/rNUL i.c#L382" id="L382" class="line" name="L393"> 293<3a>
/* fis="sref">ENOMEM;
 294<3a>static struct  295<3a>        .imxdi->work);
 296<3a>        . 297<3a>        .now = work);
platform_getc/rp;pdev,  298<3a>        .alarm_timea href="+code=work" class="sref">work);
no#L218" id="L218" class="line" name="L399"> 299<3a>        .rc;
work);
 300<4a>};
 301<4a>
di_writinimxdi->work);
);
 302<4a>4* 303<4a>4* dry40ass="sref">u32 imxdi->work);
dryice_work(struct  303<4a>static struct  305<4a>static imxdi->work);
););
 306<4a>{
 307<4a>        struct now = work);
pdev->dev, res-> 307<4a>        .alarm_timeIS_ERdrivers/rtc/rtc-IS_ERdref">imxdi->rtc, 1,  309<4a>        rc;
imxdi->rtc, 1,  310<4a>
imxdi = ,
imxdi->rtc, 1,  301<4a>         312<4a>
rc 313<4a>         303<4a>        if (( 315<4a>                 316<4a>4     41/* don't allow setting alarm in thf="drall
 317<4a>4     41ass="sref">now = 0res->ioaddr + >DIER);
 318<4a>                if ( 319<4a>                      4 retu4n s="sref">devm_kzalloc<="+code=a>(int (s="sref">pdev->dev, res->work);
dryice_work(irq(int  320<4a>
dryice_wohref="+code=a>-&i.c#L273" id="L2730"+code=dryice_rtc_set_alarm" class="sreres-> 321<4a>                ;
 322<4a>                dsr = (&pdev->dev, resllow settingstr hre>href=" 313<4a>                if ((<4 href4"+code=dsr" class="sregotosr =  324<4a>                      4  325<4a>                      4  326<4a>
 327<4a>                      4 rw = imxdi->ioaddr + DSR);
DSR_CAF) {
 318<4a>                      4 ;
devm_kzal_wait(imxdi, DSR_CAF, DSR_WEF))) {
SVf="+code=DSR" class=SVref">DSR);
 329<4a>
dsr &  330<4a>                      4  331<4a>                      4  332<4a>                }
 333<4a>        }
 334<4a>
rtc_updivers/rtc/rtc-imxdi.c#>s="sref">devm_kzal_wait(imxdi, DSR_CAF, <-imx_UNSETrivers/rtc/rtc-i-imx_UNSET="sref">DSR_CAF, <-imxrivers/rtc/rtc-i-imxdi.c#L362" id="L362" class="line" name="L435"> 335<4a>        dsr &  326<4a>        if (( 327<4a>                now = s="sref">devm_kzal_wait(imxdi,  318<4a>                alarm_timerivers/rtc/rtc-imxdi.c#L284" id="L284" class="line" name="L439"> 329<4a>                if ( 340<4a>                      4  341<4a>                      4 

 342<4a>
imxdi->ioaddr + DSR);
DSR_CAF) {
 343<4a>                      4 /* fiw = s="sref">devm_kzal_wait(imxdi, DSR_CAF) {
 344<4a>                      4 alarm_timerivers/rtc/rtc-imxdi.c#L284" id="L284" class="line" name="L445"> 345<4a>                      4 rc =  326<4a>                }
 347<4a>        }
 318<4a>        return  329<4a>}
imxdi->ioaddr + DSR);
ss=0c#L340" id="L340" class="line" name="L450"> 350<4a>
 = s="sref">devm_kzal_wait(imxdi,  351<4a>4*alarm_timerivers/rtc/rtc-imxdi.c#L284" id="L284" class="line" name="L452"> 352<4a>4* pos45=dsr" class="sref">dsref">rc =  353<4a>4* on 45/rtc-imxdi.c#L334" id="L334" class="line" name="L454"> 354<4a>4*/ 355<4a>static void  356<4a>{
imxdi->ioaddr + DSR);
DSR_CAF) {CR_TCErivers/rtc/rtc-i-x_TCEresof"#L340" id="L340" class="line" name="L457"> 347<4a>        struct di_int_divers/rtc/rtc-imxdi.c#>s="sref">devm_kzal_wait(imxdi,  318<4a>                      4 stru4t di_int_i" cla(imxdi->ioaddr + DSR);
DSR_WEF)))CR_TCErivers/rtc/rtc-i-x_TCEresoe340" id="L340" class="line" name="L459"> 329<4a>
di_int_=Cxrivers/rtc/rtc-i-xdi.c#L377" id="L377" class="line" name="L460"> 360<4a>        alarm_timerivers/rtc/rtc-imxdi.c#L284" id="L284" class="line" name="L461"> 361<4a>        rc =  362<4a>
 363<4a>         364<4a>        rtc_up>platform/rtcdrvdata="+code=dev" claplatform/rtcdrvdataref">imxdi->IORESOa href="+code=work" class="sr#L312" id="L312" class="line" name="L465"> 365<4a>}
ioaddr + devm_kzacla_ass="sv" clsthref="+code=DIER_cla_ass="sv" clsthrref">imxdi->pdev->dev, r312" id="L312" class="line" name="L466"> 356<4a>
pdev-> = {
IORESOTHIS_MODULErivers/rtc/rtc-THIS_MODULE="sr#L312" id="L312" class="line" name="L467"> 347<4a>4*rw = imxdi->rtc, 1,  368<4a>4* pro46f="+code=imxdi" class/a>;
devm_kzaPTR_ERdrivers/rtc/rtc-PTR_ERdref">imxdi->rtc, 1,  329<4a>4*/ 370<4a>static int  371<4a>{
 372<4a>        struct rc<0L377" id="L377" class="line" name="L473"> 363<4a>        struct  374<4a>        int  =  365<4a>
imxdi->rtc, 1,  376<4a>        res = imxdi->rtc, 1,  347<4a>        if (! 378<4a>                return4-rc;
 379<4a>
 380<4a>         381<4a>        if (!dryi__assex->ce" class="sref">platform_device *pdev)
 382<4a>                return4- 383<4a>
imxdi_dev *imxdi;
platform_getdrvdata="+code=dev" claplatformgrtcdrvdataref">imxdi-> 384<4a>         385<4a>
ref">imxdi->work);
 386<4a>        if (! 387<4a>                      4     4    378<4a>        else
di_int_i" clartc/rhref="+code=imxdi" clartc/rhref">0res->ioaddr + >DIER);
 379<4a>                 290<4a>
imxdi = imxdi->rtc, 1,  381<4a>        return 0;
 292<4a>}
imxdi->rtc, 1,  293<4a>
u32 imxdi->rtc, 1,  294<4a>static struct  295<4a>        .rc<0L377" id="L377" class="line" name="L496"> 296<4a>        . 297<4a>        . 298<4a>        .platform_device66" c*-> 299<4a>        .task_list 300<5a>};
task_list 301<5a>
task_list 301<5a>}
dsref}r312" id="L312" class="line" name="L503"> 301<5a>
u3ref">task_list__raw_readlassex(&imxdi-><_rtc_ops 303<5a>static struct  305<5a>static  306<5a>{
dryi__in->ass=L371" id="L371" class="line" name="L507"> 307<5a>        struct  307<5a>        .rc;
(struct ref">ref">imxdi-><_rtc_opsdryice_work(rrobe(struct  309<5a>         310<5a>
 301<5a>        dryice_wo__ex->ass=L371" id="L371" class="line" name="L512"> 312<5a>
 313<5a>        u32 ref">imxdi-><_rtc_ops 303<5a>        if (( 305<5a>                 316<5a>5     51/* doimxdi->imxdi-><_rtc_ops 307<5a>5     51ass="imxdi->imxdi-><_rtc_ops 307<5a>                if ( 319<5a>                      5 retu5n ->illow settingstr hre>href="Freescale Semicond clor, Inc.href="xdi.c#L#L312" id="L312" class="line" name="L520"> 320<5a>
->illow settingstr hre>href="Ba" ch Siach f">nba" ch@tkos.co.il">rthref="xdi.c#L#L312" id="L312" class="line" name="L521"> 301<5a>                ->illow settingstr hre>href="IMX DryIce Real/rtc-C(&am Dr2" c (RTC)href="xdi.c#L#L312" id="L312" class="line" name="L522"> 312<5a>                ->illow settingstr hre>href="GPLhref="xdi.c#L#L312" id="L312" class="line" name="L523"> 313<5a>                if ((<5 href5"+code


The original LXR software by f="d12" id="Lhttp:// classv" ge.net/projects/lxre>LXR alaunlxr@ ux.noi.c#.
lxr. ux.no kindly hosthd by 12" id="Lhttp://www.redpill- pro.no">Redpill L pro ASi.c#L"provid c of L uxhrefsul/rng and operatc-is sers="ss since 1995.