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