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_device.h>
  17#include <linux/of_platform.h>
  18#include <linux/io.h>
  19#include <linux/slab.h>
  20
  21struct mpc5121_rtc_regs {
  22        u8 set_time;            /* RTC + 0x00 */
  23        u8 hour_set;            /* RTC + 0x01 */
  24        u8 minute_set;          /* RTC + 0x02 */
  25        u8 second_set;          /* RTC + 0x03 */
  26
  27        u8 set_date;            /* RTC + 0x04 */
  28        u8 month_set;           /* RTC + 0x05 */
  29        u8 weekday_set;         /* RTC + 0x06 */
  30        u8 date_set;            /* RTC + 0x07 */
  31
  32        u8 write_sw;            /* RTC + 0x08 */
  33        u8 sw_set;              /* RTC + 0x09 */
  34        u16 year_set;           /* RTC + 0x0a */
  35
  36        u8 alm_enable;          /* RTC + 0x0c */
  37        u8 alm_hour_set;        /* RTC + 0x0d */
  38        u8 alm_min_set;         /* RTC + 0x0e */
  39        u8 int_enable;          /* RTC + 0x0f */
  40
  41        u8 reserved1;
  42        u8 hour;                /* RTC + 0x11 */
  43        u8 minute;              /* RTC + 0x12 */
  44        u8 second;              /* RTC + 0x13 */
  45
  46        u8 month;               /* RTC + 0x14 */
  47        u8 wday_mday;           /* RTC + 0x15 */
  48        u16 year;               /* RTC + 0x16 */
  49
  50        u8 int_alm;             /* RTC + 0x18 */
  51        u8 int_sw;              /* RTC + 0x19 */
  52        u8 alm_status;          /* RTC + 0x1a */
  53        u8 sw_minute;           /* RTC + 0x1b */
  54
  55        u8 bus_error_1;         /* RTC + 0x1c */
  56        u8 int_day;             /* RTC + 0x1d */
  57        u8 int_min;             /* RTC + 0x1e */
  58        u8 int_sec;             /* RTC + 0x1f */
  59
  60        /*
  61         * target_time:
  62         *      intended to be used for hibernation but hibernation
  63         *      does not work on silicon rev 1.5 so use it for non-volatile
  64         *      storage of offset between the actual_time register and linux
  65         *      time
  66         */
  67        u32 target_time;        /* RTC + 0x20 */
  68        /*
  69         * actual_time:
  70         *      readonly time since VBAT_RTC was last connected
  71         */
  72        u32 actual_time;        /* RTC + 0x24 */
  73        u32 keep_alive;         /* RTC + 0x28 */
  74};
  75
  76struct mpc5121_rtc_data {
  77        unsigned irq;
  78        unsigned irq_periodic;
  79        struct mpc5121_rtc_regs __iomem *regs;
  80        struct rtc_device *rtc;
  81        struct rtc_wkalrm wkalarm;
  82};
  83
  84/*
  85 * Update second/minute/hour registers.
  86 *
  87 * This is just so alarm will work.
  88 */
  89static void mpc5121_rtc_update_smh(struct mpc5121_rtc_regs __iomem *regs,
  90                                   struct rtc_time *tm)
  91{
  92        out_8(&regs->second_set, tm->tm_sec);
  93        out_8(&regs->minute_set, tm->tm_min);
  94        out_8(&regs->hour_set, tm->tm_hour);
  95
  96        /* set time sequence */
  97        out_8(&regs->set_time, 0x1);
  98        out_8(&regs->set_time, 0x3);
  99        out_8(&regs->set_time, 0x1);
 100        out_8(&regs->set_time, 0x0);
 101}
 102
 103static int mpc5121_rtc_read_time(struct device *dev, struct rtc_time *tm)
 104{
 105        struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
 106        struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
 107        unsigned long now;
 108
 109        /*
 110         * linux time is actual_time plus the offset saved in target_time
 111         */
 112        now = in_be32(&regs->actual_time) + in_be32(&regs->target_time);
 113
 114        rtc_time_to_tm(now, tm);
 115
 116        /*
 117         * update second minute hour registers
 118         * so alarms will work
 119         */
 120        mpc5121_rtc_update_smh(regs, tm);
 121
 122        return rtc_valid_tm(tm);
 123}
 124
 125static int mpc5121_rtc_set_time(struct device *dev, struct rtc_time *tm)
 126{
 127        struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
 128        struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
 129        int ret;
 130        unsigned long now;
 131
 132        /*
 133         * The actual_time register is read only so we write the offset
 134         * between it and linux time to the target_time register.
 135         */
 136        ret = rtc_tm_to_time(tm, &now);
 137        if (ret == 0)
 138                out_be32(&regs->target_time, now - in_be32(&regs->actual_time));
 139
 140        /*
 141         * update second minute hour registers
 142         * so alarms will work
 143         */
 144        mpc5121_rtc_update_smh(regs, tm);
 145
 146        return 0;
 147}
 148
 149static int mpc5200_rtc_read_time(struct device *dev, struct rtc_time *tm)
 150{
 151        struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
 152        struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
 153        int tmp;
 154
 155        tm->tm_sec = in_8(&regs->second);
 156        tm->tm_min = in_8(&regs->minute);
 157
 158        /* 12 hour format? */
 159        if (in_8(&regs->hour) & 0x20)
 160                tm->tm_hour = (in_8(&regs->hour) >> 1) +
 161                        (in_8(&regs->hour) & 1 ? 12 : 0);
 162        else
 163                tm->tm_hour = in_8(&regs->hour);
 164
 165        tmp = in_8(&regs->wday_mday);
 166        tm->tm_mday = tmp & 0x1f;
 167        tm->tm_mon = in_8(&regs->month) - 1;
 168        tm->tm_year = in_be16(&regs->year) - 1900;
 169        tm->tm_wday = (tmp >> 5) % 7;
 170        tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
 171        tm->tm_isdst = 0;
 172
 173        return 0;
 174}
 175
 176static int mpc5200_rtc_set_time(struct device *dev, struct rtc_time *tm)
 177{
 178        struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
 179        struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
 180
 181        mpc5121_rtc_update_smh(regs, tm);
 182
 183        /* date */
 184        out_8(&regs->month_set, tm->tm_mon + 1);
 185        out_8(&regs->weekday_set, tm->tm_wday ? tm->tm_wday : 7);
 186        out_8(&regs->date_set, tm->tm_mday);
 187        out_be16(&regs->year_set, tm->tm_year + 1900);
 188
 189        /* set date sequence */
 190        out_8(&regs->set_date, 0x1);
 191        out_8(&regs->set_date, 0x3);
 192        out_8(&regs->set_date, 0x1);
 193        out_8(&regs->set_date, 0x0);
 194
 195        return 0;
 196}
 197
 198static int mpc5121_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
 199{
 200        struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
 201        struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
 202
 203        *alarm = rtc->wkalarm;
 204
 205        alarm->pending = in_8(&regs->alm_status);
 206
 207        return 0;
 208}
 209
 210static int mpc5121_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
 211{
 212        struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
 213        struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
 214
 215        /*
 216         * the alarm has no seconds so deal with it
 217         */
 218        if (alarm->time.tm_sec) {
 219                alarm->time.tm_sec = 0;
 220                alarm->time.tm_min++;
 221                if (alarm->time.tm_min >= 60) {
 222                        alarm->time.tm_min = 0;
 223                        alarm->time.tm_hour++;
 224                        if (alarm->time.tm_hour >= 24)
 225                                alarm->time.tm_hour = 0;
 226                }
 227        }
 228
 229        alarm->time.tm_mday = -1;
 230        alarm->time.tm_mon = -1;
 231        alarm->time.tm_year = -1;
 232
 233        out_8(&regs->alm_min_set, alarm->time.tm_min);
 234        out_8(&regs->alm_hour_set, alarm->time.tm_hour);
 235
 236        out_8(&regs->alm_enable, alarm->enabled);
 237
 238        rtc->wkalarm = *alarm;
 239        return 0;
 240}
 241
 242static irqreturn_t mpc5121_rtc_handler(int irq, void *dev)
 243{
 244        struct mpc5121_rtc_data *rtc = dev_get_drvdata((struct device *)dev);
 245        struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
 246
 247        if (in_8(&regs->int_alm)) {
 248                /* acknowledge and clear status */
 249                out_8(&regs->int_alm, 1);
 250                out_8(&regs->alm_status, 1);
 251
 252                rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF);
 253                return IRQ_HANDLED;
 254        }
 255
 256        return IRQ_NONE;
 257}
 258
 259static irqreturn_t mpc5121_rtc_handler_upd(int irq, void *dev)
 260{
 261        struct mpc5121_rtc_data *rtc = dev_get_drvdata((struct device *)dev);
 262        struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
 263
 264        if (in_8(&regs->int_sec) && (in_8(&regs->int_enable) & 0x1)) {
 265                /* acknowledge */
 266                out_8(&regs->int_sec, 1);
 267
 268                rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_UF);
 269                return IRQ_HANDLED;
 270        }
 271
 272        return IRQ_NONE;
 273}
 274
 275static int mpc5121_rtc_alarm_irq_enable(struct device *dev,
 276                                        unsigned int enabled)
 277{
 278        struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
 279        struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
 280        int val;
 281
 282        if (enabled)
 283                val = 1;
 284        else
 285                val = 0;
 286
 287        out_8(&regs->alm_enable, val);
 288        rtc->wkalarm.enabled = val;
 289
 290        return 0;
 291}
 292
 293static const struct rtc_class_ops mpc5121_rtc_ops = {
 294        .read_time = mpc5121_rtc_read_time,
 295        .set_time = mpc5121_rtc_set_time,
 296        .read_alarm = mpc5121_rtc_read_alarm,
 297        .set_alarm = mpc5121_rtc_set_alarm,
 298        .alarm_irq_enable = mpc5121_rtc_alarm_irq_enable,
 299};
 300
 301static const struct rtc_class_ops mpc5200_rtc_ops = {
 302        .read_time = mpc5200_rtc_read_time,
 303        .set_time = mpc5200_rtc_set_time,
 304        .read_alarm = mpc5121_rtc_read_alarm,
 305        .set_alarm = mpc5121_rtc_set_alarm,
 306        .alarm_irq_enable = mpc5121_rtc_alarm_irq_enable,
 307};
 308
 309static int __devinit mpc5121_rtc_probe(struct platform_device *op)
 310{
 311        struct mpc5121_rtc_data *rtc;
 312        int err = 0;
 313
 314        rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
 315        if (!rtc)
 316                return -ENOMEM;
 317
 318        rtc->regs = of_iomap(op->dev.of_node, 0);
 319        if (!rtc->regs) {
 320                dev_err(&op->dev, "%s: couldn't map io space\n", __func__);
 321                err = -ENOSYS;
 322                goto out_free;
 323        }
 324
 325        device_init_wakeup(&op->dev, 1);
 326
 327        dev_set_drvdata(&op->dev, 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 = rtc_device_register("mpc5121-rtc", &op->dev,
 357                                                &mpc5121_rtc_ops, THIS_MODULE);
 358        } else {
 359                rtc->rtc = rtc_device_register("mpc5200-rtc", &op->dev,
 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);
 379out_free:
 380        kfree(rtc);
 381
 382        return err;
 383}
 384
 385static int __devexit mpc5121_rtc_remove(struct platform_device *op)
 386{
 387        struct mpc5121_rtc_data *rtc = dev_get_drvdata(&op->dev);
 388        struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
 389
 390        /* disable interrupt, so there are no nasty surprises */
 391        out_8(&regs->alm_enable, 0);
 392        out_8(&regs->int_enable, in_8(&regs->int_enable) & ~0x1);
 393
 394        rtc_device_unregister(rtc->rtc);
 395        iounmap(rtc->regs);
 396        free_irq(rtc->irq, &op->dev);
 397        free_irq(rtc->irq_periodic, &op->dev);
 398        irq_dispose_mapping(rtc->irq);
 399        irq_dispose_mapping(rtc->irq_periodic);
 400        dev_set_drvdata(&op->dev, NULL);
 401        kfree(rtc);
 402
 403        return 0;
 404}
 405
 406static struct of_device_id mpc5121_rtc_match[] __devinitdata = {
 407        { .compatible = "fsl,mpc5121-rtc", },
 408        { .compatible = "fsl,mpc5200-rtc", },
 409        {},
 410};
 411
 412static struct platform_driver mpc5121_rtc_driver = {
 413        .driver = {
 414                .name = "mpc5121-rtc",
 415                .owner = THIS_MODULE,
 416                .of_match_table = mpc5121_rtc_match,
 417        },
 418        .probe = mpc5121_rtc_probe,
 419        .remove = __devexit_p(mpc5121_rtc_remove),
 420};
 421
 422module_platform_driver(mpc5121_rtc_driver);
 423
 424MODULE_LICENSE("GPL");
 425MODULE_AUTHOR("John Rigby <jcrigby@gmail.com>");
 426
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.