linux/drivers/rtc/rtc-pl031.c
<<
>>
Prefs
   1/*
   2 * drivers/rtc/rtc-pl031.c
   3 *
   4 * Real Time Clock interface for ARM AMBA PrimeCell 031 RTC
   5 *
   6 * Author: Deepak Saxena <dsaxena@plexity.net>
   7 *
   8 * Copyright 2006 (c) MontaVista Software, Inc.
   9 *
  10 * Author: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com>
  11 * Copyright 2010 (c) ST-Ericsson AB
  12 *
  13 * This program is free software; you can redistribute it and/or
  14 * modify it under the terms of the GNU General Public License
  15 * as published by the Free Software Foundation; either version
  16 * 2 of the License, or (at your option) any later version.
  17 */
  18#include <linux/module.h>
  19#include <linux/rtc.h>
  20#include <linux/init.h>
  21#include <linux/interrupt.h>
  22#include <linux/amba/bus.h>
  23#include <linux/io.h>
  24#include <linux/bcd.h>
  25#include <linux/delay.h>
  26#include <linux/slab.h>
  27
  28/*
  29 * Register definitions
  30 */
  31#define RTC_DR          0x00    /* Data read register */
  32#define RTC_MR          0x04    /* Match register */
  33#define RTC_LR          0x08    /* Data load register */
  34#define RTC_CR          0x0c    /* Control register */
  35#define RTC_IMSC        0x10    /* Interrupt mask and set register */
  36#define RTC_RIS         0x14    /* Raw interrupt status register */
  37#define RTC_MIS         0x18    /* Masked interrupt status register */
  38#define RTC_ICR         0x1c    /* Interrupt clear register */
  39/* ST variants have additional timer functionality */
  40#define RTC_TDR         0x20    /* Timer data read register */
  41#define RTC_TLR         0x24    /* Timer data load register */
  42#define RTC_TCR         0x28    /* Timer control register */
  43#define RTC_YDR         0x30    /* Year data read register */
  44#define RTC_YMR         0x34    /* Year match register */
  45#define RTC_YLR         0x38    /* Year data load register */
  46
  47#define RTC_CR_CWEN     (1 << 26)       /* Clockwatch enable bit */
  48
  49#define RTC_TCR_EN      (1 << 1) /* Periodic timer enable bit */
  50
  51/* Common bit definitions for Interrupt status and control registers */
  52#define RTC_BIT_AI      (1 << 0) /* Alarm interrupt bit */
  53#define RTC_BIT_PI      (1 << 1) /* Periodic interrupt bit. ST variants only. */
  54
  55/* Common bit definations for ST v2 for reading/writing time */
  56#define RTC_SEC_SHIFT 0
  57#define RTC_SEC_MASK (0x3F << RTC_SEC_SHIFT) /* Second [0-59] */
  58#define RTC_MIN_SHIFT 6
  59#define RTC_MIN_MASK (0x3F << RTC_MIN_SHIFT) /* Minute [0-59] */
  60#define RTC_HOUR_SHIFT 12
  61#define RTC_HOUR_MASK (0x1F << RTC_HOUR_SHIFT) /* Hour [0-23] */
  62#define RTC_WDAY_SHIFT 17
  63#define RTC_WDAY_MASK (0x7 << RTC_WDAY_SHIFT) /* Day of Week [1-7] 1=Sunday */
  64#define RTC_MDAY_SHIFT 20
  65#define RTC_MDAY_MASK (0x1F << RTC_MDAY_SHIFT) /* Day of Month [1-31] */
  66#define RTC_MON_SHIFT 25
  67#define RTC_MON_MASK (0xF << RTC_MON_SHIFT) /* Month [1-12] 1=January */
  68
  69#define RTC_TIMER_FREQ 32768
  70
  71/**
  72 * struct pl031_vendor_data - per-vendor variations
  73 * @ops: the vendor-specific operations used on this silicon version
  74 * @clockwatch: if this is an ST Microelectronics silicon version with a
  75 *      clockwatch function
  76 * @st_weekday: if this is an ST Microelectronics silicon version that need
  77 *      the weekday fix
  78 * @irqflags: special IRQ flags per variant
  79 */
  80struct pl031_vendor_data {
  81        struct rtc_class_ops ops;
  82        bool clockwatch;
  83        bool st_weekday;
  84        unsigned long irqflags;
  85};
  86
  87struct pl031_local {
  88        struct pl031_vendor_data *vendor;
  89        struct rtc_device *rtc;
  90        void __iomem *base;
  91};
  92
  93static int pl031_alarm_irq_enable(struct device *dev,
  94        unsigned int enabled)
  95{
  96        struct pl031_local *ldata = dev_get_drvdata(dev);
  97        unsigned long imsc;
  98
  99        /* Clear any pending alarm interrupts. */
 100        writel(RTC_BIT_AI, ldata->base + RTC_ICR);
 101
 102        imsc = readl(ldata->base + RTC_IMSC);
 103
 104        if (enabled == 1)
 105                writel(imsc | RTC_BIT_AI, ldata->base + RTC_IMSC);
 106        else
 107                writel(imsc & ~RTC_BIT_AI, ldata->base + RTC_IMSC);
 108
 109        return 0;
 110}
 111
 112/*
 113 * Convert Gregorian date to ST v2 RTC format.
 114 */
 115static int pl031_stv2_tm_to_time(struct device *dev,
 116                                 struct rtc_time *tm, unsigned long *st_time,
 117        unsigned long *bcd_year)
 118{
 119        int year = tm->tm_year + 1900;
 120        int wday = tm->tm_wday;
 121
 122        /* wday masking is not working in hardware so wday must be valid */
 123        if (wday < -1 || wday > 6) {
 124                dev_err(dev, "invalid wday value %d\n", tm->tm_wday);
 125                return -EINVAL;
 126        } else if (wday == -1) {
 127                /* wday is not provided, calculate it here */
 128                unsigned long time;
 129                struct rtc_time calc_tm;
 130
 131                rtc_tm_to_time(tm, &time);
 132                rtc_time_to_tm(time, &calc_tm);
 133                wday = calc_tm.tm_wday;
 134        }
 135
 136        *bcd_year = (bin2bcd(year % 100) | bin2bcd(year / 100) << 8);
 137
 138        *st_time = ((tm->tm_mon + 1) << RTC_MON_SHIFT)
 139                        |       (tm->tm_mday << RTC_MDAY_SHIFT)
 140                        |       ((wday + 1) << RTC_WDAY_SHIFT)
 141                        |       (tm->tm_hour << RTC_HOUR_SHIFT)
 142                        |       (tm->tm_min << RTC_MIN_SHIFT)
 143                        |       (tm->tm_sec << RTC_SEC_SHIFT);
 144
 145        return 0;
 146}
 147
 148/*
 149 * Convert ST v2 RTC format to Gregorian date.
 150 */
 151static int pl031_stv2_time_to_tm(unsigned long st_time, unsigned long bcd_year,
 152        struct rtc_time *tm)
 153{
 154        tm->tm_year = bcd2bin(bcd_year) + (bcd2bin(bcd_year >> 8) * 100);
 155        tm->tm_mon  = ((st_time & RTC_MON_MASK) >> RTC_MON_SHIFT) - 1;
 156        tm->tm_mday = ((st_time & RTC_MDAY_MASK) >> RTC_MDAY_SHIFT);
 157        tm->tm_wday = ((st_time & RTC_WDAY_MASK) >> RTC_WDAY_SHIFT) - 1;
 158        tm->tm_hour = ((st_time & RTC_HOUR_MASK) >> RTC_HOUR_SHIFT);
 159        tm->tm_min  = ((st_time & RTC_MIN_MASK) >> RTC_MIN_SHIFT);
 160        tm->tm_sec  = ((st_time & RTC_SEC_MASK) >> RTC_SEC_SHIFT);
 161
 162        tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
 163        tm->tm_year -= 1900;
 164
 165        return 0;
 166}
 167
 168static int pl031_stv2_read_time(struct device *dev, struct rtc_time *tm)
 169{
 170        struct pl031_local *ldata = dev_get_drvdata(dev);
 171
 172        pl031_stv2_time_to_tm(readl(ldata->base + RTC_DR),
 173                        readl(ldata->base + RTC_YDR), tm);
 174
 175        return 0;
 176}
 177
 178static int pl031_stv2_set_time(struct device *dev, struct rtc_time *tm)
 179{
 180        unsigned long time;
 181        unsigned long bcd_year;
 182        struct pl031_local *ldata = dev_get_drvdata(dev);
 183        int ret;
 184
 185        ret = pl031_stv2_tm_to_time(dev, tm, &time, &bcd_year);
 186        if (ret == 0) {
 187                writel(bcd_year, ldata->base + RTC_YLR);
 188                writel(time, ldata->base + RTC_LR);
 189        }
 190
 191        return ret;
 192}
 193
 194static int pl031_stv2_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
 195{
 196        struct pl031_local *ldata = dev_get_drvdata(dev);
 197        int ret;
 198
 199        ret = pl031_stv2_time_to_tm(readl(ldata->base + RTC_MR),
 200                        readl(ldata->base + RTC_YMR), &alarm->time);
 201
 202        alarm->pending = readl(ldata->base + RTC_RIS) & RTC_BIT_AI;
 203        alarm->enabled = readl(ldata->base + RTC_IMSC) & RTC_BIT_AI;
 204
 205        return ret;
 206}
 207
 208static int pl031_stv2_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
 209{
 210        struct pl031_local *ldata = dev_get_drvdata(dev);
 211        unsigned long time;
 212        unsigned long bcd_year;
 213        int ret;
 214
 215        /* At the moment, we can only deal with non-wildcarded alarm times. */
 216        ret = rtc_valid_tm(&alarm->time);
 217        if (ret == 0) {
 218                ret = pl031_stv2_tm_to_time(dev, &alarm->time,
 219                                            &time, &bcd_year);
 220                if (ret == 0) {
 221                        writel(bcd_year, ldata->base + RTC_YMR);
 222                        writel(time, ldata->base + RTC_MR);
 223
 224                        pl031_alarm_irq_enable(dev, alarm->enabled);
 225                }
 226        }
 227
 228        return ret;
 229}
 230
 231static irqreturn_t pl031_interrupt(int irq, void *dev_id)
 232{
 233        struct pl031_local *ldata = dev_id;
 234        unsigned long rtcmis;
 235        unsigned long events = 0;
 236
 237        rtcmis = readl(ldata->base + RTC_MIS);
 238        if (rtcmis & RTC_BIT_AI) {
 239                writel(RTC_BIT_AI, ldata->base + RTC_ICR);
 240                events |= (RTC_AF | RTC_IRQF);
 241                rtc_update_irq(ldata->rtc, 1, events);
 242
 243                return IRQ_HANDLED;
 244        }
 245
 246        return IRQ_NONE;
 247}
 248
 249static int pl031_read_time(struct device *dev, struct rtc_time *tm)
 250{
 251        struct pl031_local *ldata = dev_get_drvdata(dev);
 252
 253        rtc_time_to_tm(readl(ldata->base + RTC_DR), tm);
 254
 255        return 0;
 256}
 257
 258static int pl031_set_time(struct device *dev, struct rtc_time *tm)
 259{
 260        unsigned long time;
 261        struct pl031_local *ldata = dev_get_drvdata(dev);
 262        int ret;
 263
 264        ret = rtc_tm_to_time(tm, &time);
 265
 266        if (ret == 0)
 267                writel(time, ldata->base + RTC_LR);
 268
 269        return ret;
 270}
 271
 272static int pl031_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
 273{
 274        struct pl031_local *ldata = dev_get_drvdata(dev);
 275
 276        rtc_time_to_tm(readl(ldata->base + RTC_MR), &alarm->time);
 277
 278        alarm->pending = readl(ldata->base + RTC_RIS) & RTC_BIT_AI;
 279        alarm->enabled = readl(ldata->base + RTC_IMSC) & RTC_BIT_AI;
 280
 281        return 0;
 282}
 283
 284static int pl031_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
 285{
 286        struct pl031_local *ldata = dev_get_drvdata(dev);
 287        unsigned long time;
 288        int ret;
 289
 290        /* At the moment, we can only deal with non-wildcarded alarm times. */
 291        ret = rtc_valid_tm(&alarm->time);
 292        if (ret == 0) {
 293                ret = rtc_tm_to_time(&alarm->time, &time);
 294                if (ret == 0) {
 295                        writel(time, ldata->base + RTC_MR);
 296                        pl031_alarm_irq_enable(dev, alarm->enabled);
 297                }
 298        }
 299
 300        return ret;
 301}
 302
 303static int pl031_remove(struct amba_device *adev)
 304{
 305        struct pl031_local *ldata = dev_get_drvdata(&adev->dev);
 306
 307        amba_set_drvdata(adev, NULL);
 308        free_irq(adev->irq[0], ldata->rtc);
 309        rtc_device_unregister(ldata->rtc);
 310        iounmap(ldata->base);
 311        kfree(ldata);
 312        amba_release_regions(adev);
 313
 314        return 0;
 315}
 316
 317static int pl031_probe(struct amba_device *adev, const struct amba_id *id)
 318{
 319        int ret;
 320        struct pl031_local *ldata;
 321        struct pl031_vendor_data *vendor = id->data;
 322        struct rtc_class_ops *ops = &vendor->ops;
 323        unsigned long time;
 324
 325        ret = amba_request_regions(adev, NULL);
 326        if (ret)
 327                goto err_req;
 328
 329        ldata = kzalloc(sizeof(struct pl031_local), GFP_KERNEL);
 330        if (!ldata) {
 331                ret = -ENOMEM;
 332                goto out;
 333        }
 334        ldata->vendor = vendor;
 335
 336        ldata->base = ioremap(adev->res.start, resource_size(&adev->res));
 337
 338        if (!ldata->base) {
 339                ret = -ENOMEM;
 340                goto out_no_remap;
 341        }
 342
 343        amba_set_drvdata(adev, ldata);
 344
 345        dev_dbg(&adev->dev, "designer ID = 0x%02x\n", amba_manf(adev));
 346        dev_dbg(&adev->dev, "revision = 0x%01x\n", amba_rev(adev));
 347
 348        /* Enable the clockwatch on ST Variants */
 349        if (vendor->clockwatch)
 350                writel(readl(ldata->base + RTC_CR) | RTC_CR_CWEN,
 351                       ldata->base + RTC_CR);
 352
 353        /*
 354         * On ST PL031 variants, the RTC reset value does not provide correct
 355         * weekday for 2000-01-01. Correct the erroneous sunday to saturday.
 356         */
 357        if (vendor->st_weekday) {
 358                if (readl(ldata->base + RTC_YDR) == 0x2000) {
 359                        time = readl(ldata->base + RTC_DR);
 360                        if ((time &
 361                             (RTC_MON_MASK | RTC_MDAY_MASK | RTC_WDAY_MASK))
 362                            == 0x02120000) {
 363                                time = time | (0x7 << RTC_WDAY_SHIFT);
 364                                writel(0x2000, ldata->base + RTC_YLR);
 365                                writel(time, ldata->base + RTC_LR);
 366                        }
 367                }
 368        }
 369
 370        ldata->rtc = rtc_device_register("pl031", &adev->dev, ops,
 371                                        THIS_MODULE);
 372        if (IS_ERR(ldata->rtc)) {
 373                ret = PTR_ERR(ldata->rtc);
 374                goto out_no_rtc;
 375        }
 376
 377        if (request_irq(adev->irq[0], pl031_interrupt,
 378                        vendor->irqflags, "rtc-pl031", ldata)) {
 379                ret = -EIO;
 380                goto out_no_irq;
 381        }
 382
 383        return 0;
 384
 385out_no_irq:
 386        rtc_device_unregister(ldata->rtc);
 387out_no_rtc:
 388        iounmap(ldata->base);
 389        amba_set_drvdata(adev, NULL);
 390out_no_remap:
 391        kfree(ldata);
 392out:
 393        amba_release_regions(adev);
 394err_req:
 395
 396        return ret;
 397}
 398
 399/* Operations for the original ARM version */
 400static struct pl031_vendor_data arm_pl031 = {
 401        .ops = {
 402                .read_time = pl031_read_time,
 403                .set_time = pl031_set_time,
 404                .read_alarm = pl031_read_alarm,
 405                .set_alarm = pl031_set_alarm,
 406                .alarm_irq_enable = pl031_alarm_irq_enable,
 407        },
 408        .irqflags = IRQF_NO_SUSPEND,
 409};
 410
 411/* The First ST derivative */
 412static struct pl031_vendor_data stv1_pl031 = {
 413        .ops = {
 414                .read_time = pl031_read_time,
 415                .set_time = pl031_set_time,
 416                .read_alarm = pl031_read_alarm,
 417                .set_alarm = pl031_set_alarm,
 418                .alarm_irq_enable = pl031_alarm_irq_enable,
 419        },
 420        .clockwatch = true,
 421        .st_weekday = true,
 422        .irqflags = IRQF_NO_SUSPEND,
 423};
 424
 425/* And the second ST derivative */
 426static struct pl031_vendor_data stv2_pl031 = {
 427        .ops = {
 428                .read_time = pl031_stv2_read_time,
 429                .set_time = pl031_stv2_set_time,
 430                .read_alarm = pl031_stv2_read_alarm,
 431                .set_alarm = pl031_stv2_set_alarm,
 432                .alarm_irq_enable = pl031_alarm_irq_enable,
 433        },
 434        .clockwatch = true,
 435        .st_weekday = true,
 436        /*
 437         * This variant shares the IRQ with another block and must not
 438         * suspend that IRQ line.
 439         */
 440        .irqflags = IRQF_SHARED | IRQF_NO_SUSPEND,
 441};
 442
 443static struct amba_id pl031_ids[] = {
 444        {
 445                .id = 0x00041031,
 446                .mask = 0x000fffff,
 447                .data = &arm_pl031,
 448        },
 449        /* ST Micro variants */
 450        {
 451                .id = 0x00180031,
 452                .mask = 0x00ffffff,
 453                .data = &stv1_pl031,
 454        },
 455        {
 456                .id = 0x00280031,
 457                .mask = 0x00ffffff,
 458                .data = &stv2_pl031,
 459        },
 460        {0, 0},
 461};
 462
 463MODULE_DEVICE_TABLE(amba, pl031_ids);
 464
 465static struct amba_driver pl031_driver = {
 466        .drv = {
 467                .name = "rtc-pl031",
 468        },
 469        .id_table = pl031_ids,
 470        .probe = pl031_probe,
 471        .remove = pl031_remove,
 472};
 473
 474module_amba_driver(pl031_driver);
 475
 476MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net");
 477MODULE_DESCRIPTION("ARM AMBA PL031 RTC Driver");
 478MODULE_LICENSE("GPL");
 479
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.