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 */
 250                out_8(&regs->int_alm, 1);
 251                out_8(&regs->alm_status, 1);
 252
 253                rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF);
 254                return IRQ_HANDLED;
 255        }
 256
 257        return IRQ_NONE;
 258}
 259
 260static irqreturn_t mpc5121_rtc_handler_upd(int irq, void *dev)
 261{
 262        struct mpc5121_rtc_data *rtc = dev_get_drvdata((struct device *)dev);
 263        struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
 264
 265        if (in_8(&regs->int_sec) && (in_8(&regs->int_enable) & 0x1)) {
 266                /* acknowledge */
 267                out_8(&regs->int_sec, 1);
 268
 269                rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_UF);
 270                return IRQ_HANDLED;
 271        }
 272
 273        return IRQ_NONE;
 274}
 275
 276static int mpc5121_rtc_alarm_irq_enable(struct device *dev,
 277                                        unsigned int enabled)
 278{
 279        struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
 280        struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
 281        int val;
 282
 283        if (enabled)
 284                val = 1;
 285        else
 286                val = 0;
 287
 288        out_8(&regs->alm_enable, val);
 289        rtc->wkalarm.enabled = val;
 290
 291        return 0;
 292}
 293
 294static const struct rtc_class_ops mpc5121_rtc_ops = {
 295        .read_time = mpc5121_rtc_read_time,
 296        .set_time = mpc5121_rtc_set_time,
 297        .read_alarm = mpc5121_rtc_read_alarm,
 298        .set_alarm = mpc5121_rtc_set_alarm,
 299        .alarm_irq_enable = mpc5121_rtc_alarm_irq_enable,
 300};
 301
 302static const struct rtc_class_ops mpc5200_rtc_ops = {
 303        .read_time = mpc5200_rtc_read_time,
 304        .set_time = mpc5200_rtc_set_time,
 305        .read_alarm = mpc5121_rtc_read_alarm,
 306        .set_alarm = mpc5121_rtc_set_alarm,
 307        .alarm_irq_enable = mpc5121_rtc_alarm_irq_enable,
 308};
 309
 310static int mpc5121_rtc_probe(struct platform_device *op)
 311{
 312        struct mpc5121_rtc_data *rtc;
 313        int err = 0;
 314
 315        rtc = devm_kzalloc(&op->dev, sizeof(*rtc), GFP_KERNEL);
 316        if (!rtc)
 317                return -ENOMEM;
 318
 319        rtc->regs = of_iomap(op->dev.of_node, 0);
 320        if (!rtc->regs) {
 321                dev_err(&op->dev, "%s: couldn't map io space\n", __func__);
 322                return -ENOSYS;
 323        }
 324
 325        device_init_wakeup(&op->dev, 1);
 326
 327        platform_set_drvdata(op, rtc);
 328
 329        rtc->irq = irq_of_parse_and_map(op->dev.of_node, 1);
 330        err = request_irq(rtc->irq, mpc5121_rtc_handler, 0,
 331                                                "mpc5121-rtc", &op->dev);
 332        if (err) {
 333                dev_err(&op->dev, "%s: could not request irq: %i\n",
 334                                                        __func__, rtc->irq);
 335                goto out_dispose;
 336        }
 337
 338        rtc->irq_periodic = irq_of_parse_and_map(op->dev.of_node, 0);
 339        err = request_irq(rtc->irq_periodic, mpc5121_rtc_handler_upd,
 340                                0, "mpc5121-rtc_upd", &op->dev);
 341        if (err) {
 342                dev_err(&op->dev, "%s: could not request irq: %i\n",
 343                                                __func__, rtc->irq_periodic);
 344                goto out_dispose2;
 345        }
 346
 347        if (of_device_is_compatible(op->dev.of_node, "fsl,mpc5121-rtc")) {
 348                u32 ka;
 349                ka = in_be32(&rtc->regs->keep_alive);
 350                if (ka & 0x02) {
 351                        dev_warn(&op->dev,
 352                                "mpc5121-rtc: Battery or oscillator failure!\n");
 353                        out_be32(&rtc->regs->keep_alive, ka);
 354                }
 355
 356                rtc->rtc = devm_rtc_device_register(&op->dev, "mpc5121-rtc",
 357                                                &mpc5121_rtc_ops, THIS_MODULE);
 358        } else {
 359                rtc->rtc = devm_rtc_device_register(&op->dev, "mpc5200-rtc",
 360                                                &mpc5200_rtc_ops, THIS_MODULE);
 361        }
 362
 363        if (IS_ERR(rtc->rtc)) {
 364                err = PTR_ERR(rtc->rtc);
 365                goto out_free_irq;
 366        }
 367        rtc->rtc->uie_unsupported = 1;
 368
 369        return 0;
 370
 371out_free_irq:
 372        free_irq(rtc->irq_periodic, &op->dev);
 373out_dispose2:
 374        irq_dispose_mapping(rtc->irq_periodic);
 375        free_irq(rtc->irq, &op->dev);
 376out_dispose:
 377        irq_dispose_mapping(rtc->irq);
 378        iounmap(rtc->regs);
 379
 380        return err;
 381}
 382
 383static int mpc5121_rtc_remove(struct platform_device *op)
 384{
 385        struct mpc5121_rtc_data *rtc = platform_get_drvdata(op);
 386        struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
 387
 388        /* disable interrupt, so there are no nasty surprises */
 389        out_8(&regs->alm_enable, 0);
 390        out_8(&regs->int_enable, in_8(&regs->int_enable) & ~0x1);
 391
 392        iounmap(rtc->regs);
 393        free_irq(rtc->irq, &op->dev);
 394        free_irq(rtc->irq_periodic, &op->dev);
 395        irq_dispose_mapping(rtc->irq);
 396        irq_dispose_mapping(rtc->irq_periodic);
 397
 398        return 0;
 399}
 400
 401#ifdef CONFIG_OF
 402static struct of_device_id mpc5121_rtc_match[] = {
 403        { .compatible = "fsl,mpc5121-rtc", },
 404        { .compatible = "fsl,mpc5200-rtc", },
 405        {},
 406};
 407#endif
 408
 409static struct platform_driver mpc5121_rtc_driver = {
 410        .driver = {
 411                .name = "mpc5121-rtc",
 412                .owner = THIS_MODULE,
 413                .of_match_table = of_match_ptr(mpc5121_rtc_match),
 414        },
 415        .probe = mpc5121_rtc_probe,
 416        .remove = mpc5121_rtc_remove,
 417};
 418
 419module_platform_driver(mpc5121_rtc_driver);
 420
 421MODULE_LICENSE("GPL");
 422MODULE_AUTHOR("John Rigby <jcrigby@gmail.com>");
 423
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.