linux/drivers/rtc/rtc-mpc5121.c
<<
>>
Prefs
   1/*
   2 * Real-time clock driver for MPC5121
   3 *
   4 * Copyright 2007, Domen Puncer <domen.puncer@telargo.com>
   5 * Copyright 2008, Freescale Semiconductor, Inc. All rights reserved.
   6 * Copyright 2011, Dmitry Eremin-Solenikov
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License version 2 as
  10 * published by the Free Software Foundation.
  11 */
  12
  13#include <linux/init.h>
  14#include <linux/module.h>
  15#include <linux/rtc.h>
  16#include <linux/of.h>
  17#include <linux/of_device.h>
  18#include <linux/of_platform.h>
  19#include <linux/io.h>
  20#include <linux/slab.h>
  21
  22struct mpc5121_rtc_regs {
  23        u8 set_time;            /* RTC + 0x00 */
  24        u8 hour_set;            /* RTC + 0x01 */
  25        u8 minute_set;          /* RTC + 0x02 */
  26        u8 second_set;          /* RTC + 0x03 */
  27
  28        u8 set_date;            /* RTC + 0x04 */
  29        u8 month_set;           /* RTC + 0x05 */
  30        u8 weekday_set;         /* RTC + 0x06 */
  31        u8 date_set;            /* RTC + 0x07 */
  32
  33        u8 write_sw;            /* RTC + 0x08 */
  34        u8 sw_set;              /* RTC + 0x09 */
  35        u16 year_set;           /* RTC + 0x0a */
  36
  37        u8 alm_enable;          /* RTC + 0x0c */
  38        u8 alm_hour_set;        /* RTC + 0x0d */
  39        u8 alm_min_set;         /* RTC + 0x0e */
  40        u8 int_enable;          /* RTC + 0x0f */
  41
  42        u8 reserved1;
  43        u8 hour;                /* RTC + 0x11 */
  44        u8 minute;              /* RTC + 0x12 */
  45        u8 second;              /* RTC + 0x13 */
  46
  47        u8 month;               /* RTC + 0x14 */
  48        u8 wday_mday;           /* RTC + 0x15 */
  49        u16 year;               /* RTC + 0x16 */
  50
  51        u8 int_alm;             /* RTC + 0x18 */
  52        u8 int_sw;              /* RTC + 0x19 */
  53        u8 alm_status;          /* RTC + 0x1a */
  54        u8 sw_minute;           /* RTC + 0x1b */
  55
  56        u8 bus_error_1;         /* RTC + 0x1c */
  57        u8 int_day;             /* RTC + 0x1d */
  58        u8 int_min;             /* RTC + 0x1e */
  59        u8 int_sec;             /* RTC + 0x1f */
  60
  61        /*
  62         * target_time:
  63         *      intended to be used for hibernation but hibernation
  64         *      does not work on silicon rev 1.5 so use it for non-volatile
  65         *      storage of offset between the actual_time register and linux
  66         *      time
  67         */
  68        u32 target_time;        /* RTC + 0x20 */
  69        /*
  70         * actual_time:
  71         *      readonly time since VBAT_RTC was last connected
  72         */
  73        u32 actual_time;        /* RTC + 0x24 */
  74        u32 keep_alive;         /* RTC + 0x28 */
  75};
  76
  77struct mpc5121_rtc_data {
  78        unsigned irq;
  79        unsigned irq_periodic;
  80        struct mpc5121_rtc_regs __iomem *regs;
  81        struct rtc_device *rtc;
  82        struct rtc_wkalrm wkalarm;
  83};
  84
  85/*
  86 * Update second/minute/hour registers.
  87 *
  88 * This is just so alarm will work.
  89 */
  90static void mpc5121_rtc_update_smh(struct mpc5121_rtc_regs __iomem *regs,
  91                                   struct rtc_time *tm)
  92{
  93        out_8(&regs->second_set, tm->tm_sec);
  94        out_8(&regs->minute_set, tm->tm_min);
  95        out_8(&regs->hour_set, tm->tm_hour);
  96
  97        /* set time sequence */
  98        out_8(&regs->set_time, 0x1);
  99        out_8(&regs->set_time, 0x3);
 100        out_8(&regs->set_time, 0x1);
 101        out_8(&regs->set_time, 0x0);
 102}
 103
 104static int mpc5121_rtc_read_time(struct device *dev, struct rtc_time *tm)
 105{
 106        struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
 107        struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
 108        unsigned long now;
 109
 110        /*
 111         * linux time is actual_time plus the offset saved in target_time
 112         */
 113        now = in_be32(&regs->actual_time) + in_be32(&regs->target_time);
 114
 115        rtc_time_to_tm(now, tm);
 116
 117        /*
 118         * update second minute hour registers
 119         * so alarms will work
 120         */
 121        mpc5121_rtc_update_smh(regs, tm);
 122
 123        return rtc_valid_tm(tm);
 124}
 125
 126static int mpc5121_rtc_set_time(struct device *dev, struct rtc_time *tm)
 127{
 128        struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
 129        struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
 130        int ret;
 131        unsigned long now;
 132
 133        /*
 134         * The actual_time register is read only so we write the offset
 135         * between it and linux time to the target_time register.
 136         */
 137        ret = rtc_tm_to_time(tm, &now);
 138        if (ret == 0)
 139                out_be32(&regs->target_time, now - in_be32(&regs->actual_time));
 140
 141        /*
 142         * update second minute hour registers
 143         * so alarms will work
 144         */
 145        mpc5121_rtc_update_smh(regs, tm);
 146
 147        return 0;
 148}
 149
 150static int mpc5200_rtc_read_time(struct device *dev, struct rtc_time *tm)
 151{
 152        struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
 153        struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
 154        int tmp;
 155
 156        tm->tm_sec = in_8(&regs->second);
 157        tm->tm_min = in_8(&regs->minute);
 158
 159        /* 12 hour format? */
 160        if (in_8(&regs->hour) & 0x20)
 161                tm->tm_hour = (in_8(&regs->hour) >> 1) +
 162                        (in_8(&regs->hour) & 1 ? 12 : 0);
 163        else
 164                tm->tm_hour = in_8(&regs->hour);
 165
 166        tmp = in_8(&regs->wday_mday);
 167        tm->tm_mday = tmp & 0x1f;
 168        tm->tm_mon = in_8(&regs->month) - 1;
 169        tm->tm_year = in_be16(&regs->year) - 1900;
 170        tm->tm_wday = (tmp >> 5) % 7;
 171        tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
 172        tm->tm_isdst = 0;
 173
 174        return 0;
 175}
 176
 177static int mpc5200_rtc_set_time(struct device *dev, struct rtc_time *tm)
 178{
 179        struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
 180        struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
 181
 182        mpc5121_rtc_update_smh(regs, tm);
 183
 184        /* date */
 185        out_8(&regs->month_set, tm->tm_mon + 1);
 186        out_8(&regs->weekday_set, tm->tm_wday ? tm->tm_wday : 7);
 187        out_8(&regs->date_set, tm->tm_mday);
 188        out_be16(&regs->year_set, tm->tm_year + 1900);
 189
 190        /* set date sequence */
 191        out_8(&regs->set_date, 0x1);
 192        out_8(&regs->set_date, 0x3);
 193        out_8(&regs->set_date, 0x1);
 194        out_8(&regs->set_date, 0x0);
 195
 196        return 0;
 197}
 198
 199static int mpc5121_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
 200{
 201        struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
 202        struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
 203
 204        *alarm = rtc->wkalarm;
 205
 206        alarm->pending = in_8(&regs->alm_status);
 207
 208        return 0;
 209}
 210
 211static int mpc5121_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
 212{
 213        struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
 214        struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
 215
 216        /*
 217         * the alarm has no seconds so deal with it
 218         */
 219        if (alarm->time.tm_sec) {
 220                alarm->time.tm_sec = 0;
 221                alarm->time.tm_min++;
 222                if (alarm->time.tm_min >= 60) {
 223                        alarm->time.tm_min = 0;
 224                        alarm->time.tm_hour++;
 225                        if (alarm->time.tm_hour >= 24)
 226                                alarm->time.tm_hour = 0;
 227                }
 228        }
 229
 230        alarm->time.tm_mday = -1;
 231        alarm->time.tm_mon = -1;
 232        alarm->time.tm_year = -1;
 233
 234        out_8(&regs->alm_min_set, alarm->time.tm_min);
 235        out_8(&regs->alm_hour_set, alarm->time.tm_hour);
 236
 237        out_8(&regs->alm_enable, alarm->enabled);
 238
 239        rtc->wkalarm = *alarm;
 240        return 0;
 241}
 242
 243static irqreturn_t mpc5121_rtc_handler(int irq, void *dev)
 244{
 245        struct mpc5121_rtc_data *rtc = dev_get_drvdata((struct device *)dev);
 246        struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
 247
 248        if (in_8(&regs->int_alm)) {
 249                /* acknowledge and clear status */
tmdrivers/rtc/rtc-mpc5121.c#L249" id="L249" class="#L238gf="+code=tm_hour"href="drivers/rtc/rtc-mpca>        int_alm)) {
tmdrivers/rtc/rtc-mpc5121.c#L2240"> 240 class="sref">tm)
 =2time.mpc5121_rtc_regs mlas    deegs" clas2="sref">regs = time__iomem *tmp;
<2 href25e" class="sref">time.i2_8(&__iomem *(&minute);
<2 href25tc/rtc-mpc5121.c#L228" id="L228" class="linmpc5121.c2L159" id="L159" class="l2ne" n26drivers/rtc/rtc-mpc5121.c#L242" id="L242" class="line" name="L242"> 242
 243static irqreturn_t >in_82&rtc_time *(&2mp; 151{
 152        struct mpc5121_rtc_data *rtc = dea>(&<2 href="+code=regs" class2"sref26rivers/rtc/rtc-mpc5121.c#L212" id="L212" class==dev" class="sref">dev);
 246        struct mpc5121_rtc_regs __iomem *regs->in_8(& 247
 67"> 1mpc5121.c#L247" id="L247" class="line" name="L247"> 247
             if (hour);
<2 href26="sref">time.in_8timess="comment">/* acknowledge and clear status */
tmdrivers/rtc/rtc-mpc5121.c#Lf">tm2>alarm->in_2(& 138        if (mpc5121_rtc_regs mlas    de">in_be162/a>(&__iomem * = 1.c#L147" id="L147" class="line" name=2lass="sre2">tm_mon,  172        __iomem *tm_isdst = 0;
<2 href27_tm" class="sref">rtc_valid_tm( 123
rtc-> 172        return 0;
 211static int rtc-> 175}
<2 href27class="sref">time>>>>>>>>>>>>>>>>>>>>>>>>> 130rtc-mpc5121.c#L125"de=regs" class="sref">regs->" class="sref">rtc->tm2dev, struct rtc_time *tm)
 178{
 179        struct mpc5121_rtc_data *dev_get_drvdata(dev);
 180        struct mpc5121_rtc_regs __iomem *regs = __iomem *regs;
<2 href28rivers/rtc/rtc-mpc5121.c#L241" id="L241" clsref">mpc2121_rtc_update_smh(<2 href28drivers/rtc/rtc-mpc5121.c#L247" de=regs" class="sref">regs->" class="sref">rtc->tm);
<2 href28rtc-mpc5121.c#L163" id="L163" class=valref="+code=__ioval claref/a>        hourmo2th_set, time. = *tm_2day ? rtc->, tm-> 236
 237         * 238
regs-&gref="+code=mpc5121valref="+code=__ioval class="sref">__iomem *tm_year + 1900);
<2 href29rivers/rtc/rtc-mpc5121.c#L209" id="L209" clc5121.c#L290" id="L190" class="lin2" nam2="L190"> 190wkalarm = *class="sref">rtc_valid_tm((&2mp;time.rs/rtc/const5121_rtc_set_alarm" class="a hre_opline" name="L171"> 1a hre_opl2"> 242
-&grefme" class="sref">rtc_time *(&2mp;href="driversref="+code=mpc5121198
 17/a>
rtc->set_date, 0x0);
<2 href2"drivers/rtc/r>
 17/a>
rtc->tm_25

 198
rtc->        return 0;
 href="driversref="+code=mpc5121198
 210
rtc-> 197}
<2 href299rivers/rtc/r>
rtc-> = *alarm)
rtc-> 1a hre_opl2"> 242

-&grefme" class="sref">rtc_time *regs = href="driversref="+code=mpc5121198<"L149" class="line" name="L149"> 149
rtc-> 176
rtc->
 198
rtc->wkalarm;
<3 href30drivers/rtc/r> href="driversref="+code=mpc5121198
 210
rtc->
rtc-> = * 197}
<3c/rtc30tc/rtc-mpc5121.c#L228" id="L228" class="li3L208"> 203        return 0;
o" class="line" nop>->" class="sref">rtc-> 209}
<3 href31tc_time" class="sref">rtc_time *, 3truct  151{
 = *alarm)
 = *regs->regs = time. *regs;
<3 href3"drivers/rtc/rtc-m!vers/rtc/rtc-mpc5121.c#L152" id="L152" c)/a> *time">wkala-vers/rtc/rtc-mpENOMEM21.c#L152" id="ENOMEM2" cm = * 217<3a>alarm-> 238        struct -&g121.c#L187" id="L187"s="sref">mpc5121_rtc_dat>o3ass="sref3>alarm-> 159!vers/rtc/rtc-mpc5121.c#L152" id="L152" c" name="L238"> 238        struct 3larm->time. *alarm->timempc5121.c#L125"drpc5121.c#L243" idrp claref-vers/rtc/rtc-mpENOSYS21.c#L152" id="ENOSYS2" cm = *alarm->time.tm-> = * =3larm->rtc_valid_tm(3larm->rtc->alarm->tmdrivers/rtc/rtc-mpc5121.c#3"sref">al3rm->rtc->tm-> 179        struct tmdrivers/rtc/rtc-mpc5121.c#3""drivers3            }
 228        }
<3 href3"drivers/rtc/rtc-mpc5121.c#L2c5121.c#L152" id="L152" c" name="L238"> 238-&g121.c#L187" id="L187"s="sref">mpc5121_rtc_dat>tmdrivers/rtc/rtc-mpc5121.c#3sref">ala3m->time. 238 238 151{
rtc->a3arm->timeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee_rdRTC_AFc_rdRTC_AFc_rquot;;tmdrivers/rtc/rtc-mpc5121.c#3s>alarm->tm_year = -1;
<3 href33rtc-mpc5121.c#L163" id="L163" class=ref=drpc5121.c#L243" iref=drp47is21.c#L196121.c#L152""+code=dev_get_drvdata" class="sref 3f="+codRTC_AFc_rdRTC_AFc_rdRTC_AFc_rdRTC_AFc_rdRTC_AFc_rquot;     s:/could not _1quest de=:      i\nrquot;a href=     if (3">alarm->timeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee_  238tmdrivers/rtc/rtc-mpc5121.c#3s>alarm->time.tm-> = *tm_hour);
<3 href3"drivers/rtc/rclass="sref">rtc_valid_tm(alm_ena3le, alarm->enabled);
<3 href3"drivers/rtc/rtc-mpc5121.c#L238" id="L238" class="line" name="L238"> 238-&g121.c#L187" id="L187"s="sref">mpc5121_rtc_dat>o3="sref">r3c-> 238 238alar3;
timetmdrivers/rtc/rtc-mpc5121.c#3L240"> 243        return 0;
 241}
<3 href34="sref">time.time. 238tmdrivers/rtc/rtc-mpc5121.c#3L="sref">3class="sref">dev)
tm-> = *rtc_valid_tm(regs = rtc->regs;
<3 href3"drivers/rtc/rtc-mpc5121.c#L247" of"" id="_is_compati="line" name="L237of"" id="_is_compati="lpc5121_rtc_data" clao" class="line" nop>-&g121.c#L187" id="L187"s="sref">mpc5121_rtc_dat>in_8<3a>(&regs->t; = * 2493/a>  3             /*k        struct  238        struct  238tmdrivers/rtc/rtc-mpc5121.c#3249" id="3249" class="#L238gf="+co3e=tm_3our"href="drivers/rtc/tc-mpc5121.c#L247" k        struct     02       if ( 243 class="sref">tm)
timeeeeeeeee="+code=mpc5121ref=waralm_min_set" claref=wara47is21.c#L196121.c#L152""+code=dev_get_drvdata" class="sref 3f="+codRTC_AFc_rdRTC_AFc_rdRTC_AF    if (time.timeeeeeeeee=rdRTC_AFc_rdRTC_AFc_rquot;;tmdrivers/rtc/rtc-mpc5121.c#32pc5121_r3="sref">regs = time 238        struct  238tmdrivers/rtc/rtc-mpc5121.c#32="sref">321.c#L154" id="L154" cla3s="li35ime" class="slass="srclass="sref">rtc_valid_tm(tmp;
<3 href35e" class="sref">time.i3_8(&timess="comment">/*c5121.c#L152" id="L152" c" name="L238"> 238-&g<" id="_uctistinpc5121rdRTC_AFc_rdRTC_AFc_rquot;;(&time.timeeeeeeeeeeeeeeeeeeeeeeeee1.c#L196121.c#L152"""drivers/rtcopline" name="L171"drivers/rtcopl>-&gFc_ers/rtc/rtc-mpTHIS_MODULEine" name="L171THIS_MODULE2" cs="line"2">tmdrivers/rtc/rtc-mpc5121.c#32f">in_8<3ss="sref">minute);
<3 href35ss="sref">reg}=/*c5121.c#L152" id="L152" c" name="L238"> 238-&g<" id="_uctistinpc5121rdRTC_AFc_rdRTC_AFc_rquot;;time

-&gFc_ers/rtc/rtc-mpTHIS_MODULEine" name="L171THIS_MODULE2" cs="line"2">tmdrivers/rtc/rtc-mpc5121.c#3n_8(&3mp;rtc_valid_tm((&<3 href="+code=regs" class3"sref36lass="sref">time. 238in_8(& 238 238tmdrivers/rtc/rtc-mpc5121.c#3ncode=tmp3lass="sref">hour);
<3 href36="sref">time.tm-> = *in_8rtc_valid_tm(tm3>tm-> 238 238uie_unsupportgs claref/a>        in_3(&in_be163/a>(&wkalarm = *rtc->tm_mon, rtc->tm-> 238tmdrivers/rtc/rtc-mpc5121.c#3" class="3ref">tm_isdst = 0;
<3 href37_tm" >tm->rtc->
time. 238tmdrivers/rtc/rtc-mpc5121.c#3"code=tmp3        return 0;
 238tmdrivers/rtc/rtc-mpc5121.c#3">in_8 175}
<3 href37class>tm->rtc->tm3dev, struct tm-> 238tmdrivers/rtc/rtc-mpc5121.c#3"ref">in_3 class="sref">tm)
 238        struct tmdrivers/rtc/rtc-mpc5121.c#3rtc =3rtc->regs = time.tmdrivers/rtc/rtc-mpc5121.c#3rass="sre3class="sref">regs;
<3 href38rivers/rtc/rtc-mpc5121.c#L241" id="L241" c3sref">mpc3121_rtc_update_smh(<3 href38drivers/rtc/rtc-mpc5121.c#L122" id=drpc5121.c#L243" idrp cla="line"2">tmdrivers/rtc/rtc-mpc5121.c#3r class="3 class="sref">tm);
<3 href38_tm" class="sref">rtc_valid_tm(rtc->mo3th_set, o" class="line" nop>->" class="sref">rtc->tm_3day ? ,  178{
 179        struct tmdrivers/rtc/rtc-mpc5121.c#3r+code=tm3t, dev);
 180        struct mpc5121_rtc_regs __iomem *tm_year + 1900);
<3 href39rivers/rtc/rtc-mpc5121.c#L209" id="L209" c3c5121.c#L390" id="L190" class="lin3" nam3="L190"> 190time. 236
 237          o3t_8(&3mp;tm->
 247
  ~      o3t class="3mp;regs->time.<-&g<" id="_unuctistine" name="L151">-&g<" id="_unuctistinpc5121_rtc_data" clac5121.c#L152" id="L152" c" name="L238"> 238tmdrivers/rtc/rtc-mpc5121.c#3class="sr3f">set_date, 0x0);
<3 href39drivers/rtc/rtc-mpc5121.c#L2iounla" class="line" nlounla"pc5121_rtc_data" clac5121.c#L152" id="L152" c" name="L238"> 238        struct tmdrivers/rtc/rtc-mpc5121.c#3cref">tm_35
 238tmdrivers/rtc/rtc-mpc5121.c#3cs="sref"3        return 0;
tm-> 238tmdrivers/rtc/rtc-mpc5121.c#3c+code=tm3 name="L197"> 197}
<3 href39drivers/rtc/rtc-mpc5121.c#L2iatsdisposc_la"p_AFcine" name="L22catsdisposc_la"p_AF2" c21_rtc_data" clac5121.c#L152" id="L152" c" name="L238"> 238tmdrivers/rtc/rtc-mpc5121.c#4dev, 4truct  238tmdrivers/rtc/rtc-mpc5121.c#4=alarm" c4ass="sref">alarm)
time. 179        struct tmdrivers/rtc/rtc-mpc5121.c#4=2larm" c4ap;time.tmdrivers/rtc/rtc-mpc5121.c#4egs" clas4="sref">regs = time.wkalarm = *rtc_valid_tm(wkalarm;
<4 href40e" class="sref">time.time.time.of"" id="_isf="drivers/rtc/rtc-mdev);
dev);
rtc_time *(4aname="L197"> 197}
<4c/rtc40drivers/rtc/r{=> 204        return 0;
 209}
<4 href41lass="sref">t{}F    if (alarm)
 = *regs->regs = rtc_time *regs;
<4 href41drivers/rtc/r>rtc_time *(4/rtc/rtc-mpc5121.c#L216"4id="L41class="sref">time>rtc_time *(4"line" name="L217"> 217<4a>time.rtc_time *(4rtc/rtc-mpc5121.c#L218" 4d="L241ss="sref">regs-&g>);
dev);
rtc_time * 159}F    if (4larm->
alarm->-> =4larm->regs->4larm->);
tmdrivers/rtc/rtc-mpc5121.c#4">alarm->time.al4rm->tmdrivers/rtc/rtc-mpc5121.c#4m_hourtmdrivers/rtc/rtc-mpc5121.c#4m9_8(4            }
thttp://sources="ge.net/projects/lxne>LXR ode=unityTC_AFcthis experi=intal iverion by line"2">tmailto:lxn@ }
ux.no">lxn@ }
ux.noTC_A.
lxn. } ux.no kindly hosted by line"2">thttp://www.redpill- } pro.no">Redpill L} pro ASTC_AFcprovider of L} ux/consult_AF and oper/rtons serid="s since 1995.