linux/drivers/rtc/rtc-at32ap700x.c
<<
>>
Prefs
   1/*
   2 * An RTC driver for the AVR32 AT32AP700x processor series.
   3 *
   4 * Copyright (C) 2007 Atmel Corporation
   5 *
   6 * This program is free software; you can redistribute it and/or modify it
   7 * under the terms of the GNU General Public License version 2 as published
   8 * by the Free Software Foundation.
   9 */
  10
  11#include <linux/module.h>
  12#include <linux/kernel.h>
  13#include <linux/platform_device.h>
  14#include <linux/rtc.h>
  15#include <linux/io.h>
  16
  17/*
  18 * This is a bare-bones RTC. It runs during most system sleep states, but has
  19 * no battery backup and gets reset during system restart.  It must be
  20 * initialized from an external clock (network, I2C, etc) before it can be of
  21 * much use.
  22 *
  23 * The alarm functionality is limited by the hardware, not supporting
  24 * periodic interrupts.
  25 */
  26
  27#define RTC_CTRL                0x00
  28#define RTC_CTRL_EN                0
  29#define RTC_CTRL_PCLR              1
  30#define RTC_CTRL_TOPEN             2
  31#define RTC_CTRL_PSEL              8
  32
  33#define RTC_VAL                 0x04
  34
  35#define RTC_TOP                 0x08
  36
  37#define RTC_IER                 0x10
  38#define RTC_IER_TOPI               0
  39
  40#define RTC_IDR                 0x14
  41#define RTC_IDR_TOPI               0
  42
  43#define RTC_IMR                 0x18
  44#define RTC_IMR_TOPI               0
  45
  46#define RTC_ISR                 0x1c
  47#define RTC_ISR_TOPI               0
  48
  49#define RTC_ICR                 0x20
  50#define RTC_ICR_TOPI               0
  51
  52#define RTC_BIT(name)           (1 << RTC_##name)
  53#define RTC_BF(name, value)     ((value) << RTC_##name)
  54
  55#define rtc_readl(dev, reg)                             \
  56        __raw_readl((dev)->regs + RTC_##reg)
  57#define rtc_writel(dev, reg, value)                     \
  58        __raw_writel((value), (dev)->regs + RTC_##reg)
  59
  60struct rtc_at32ap700x {
  61        struct rtc_device       *rtc;
  62        void __iomem            *regs;
  63        unsigned long           alarm_time;
  64        unsigned long           irq;
  65        /* Protect against concurrent register access. */
  66        spinlock_t              lock;
  67};
  68
  69static int at32_rtc_readtime(struct device *dev, struct rtc_time *tm)
  70{
  71        struct rtc_at32ap700x *rtc = dev_get_drvdata(dev);
  72        unsigned long now;
  73
  74        now = rtc_readl(rtc, VAL);
  75        rtc_time_to_tm(now, tm);
  76
  77        return 0;
  78}
  79
  80static int at32_rtc_settime(struct device *dev, struct rtc_time *tm)
  81{
  82        struct rtc_at32ap700x *rtc = dev_get_drvdata(dev);
  83        unsigned long now;
  84        int ret;
  85
  86        ret = rtc_tm_to_time(tm, &now);
  87        if (ret == 0)
  88                rtc_writel(rtc, VAL, now);
  89
  90        return ret;
  91}
  92
  93static int at32_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
  94{
  95        struct rtc_at32ap700x *rtc = dev_get_drvdata(dev);
  96
  97        spin_lock_irq(&rtc->lock);
  98        rtc_time_to_tm(rtc->alarm_time, &alrm->time);
  99        alrm->enabled = rtc_readl(rtc, IMR) & RTC_BIT(IMR_TOPI) ? 1 : 0;
 100        alrm->pending = rtc_readl(rtc, ISR) & RTC_BIT(ISR_TOPI) ? 1 : 0;
 101        spin_unlock_irq(&rtc->lock);
 102
 103        return 0;
 104}
 105
 106static int at32_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
 107{
 108        struct rtc_at32ap700x *rtc = dev_get_drvdata(dev);
 109        unsigned long rtc_unix_time;
 110        unsigned long alarm_unix_time;
 111        int ret;
 112
 113        rtc_unix_time = rtc_readl(rtc, VAL);
 114
 115        ret = rtc_tm_to_time(&alrm->time, &alarm_unix_time);
 116        if (ret)
 117                return ret;
 118
 119        if (alarm_unix_time < rtc_unix_time)
 120                return -EINVAL;
 121
 122        spin_lock_irq(&rtc->lock);
 123        rtc->alarm_time = alarm_unix_time;
 124        rtc_writel(rtc, TOP, rtc->alarm_time);
 125        if (alrm->enabled)
 126                rtc_writel(rtc, CTRL, rtc_readl(rtc, CTRL)
 127                                | RTC_BIT(CTRL_TOPEN));
 128        else
 129                rtc_writel(rtc, CTRL, rtc_readl(rtc, CTRL)
 130                                & ~RTC_BIT(CTRL_TOPEN));
 131        spin_unlock_irq(&rtc->lock);
 132
 133        return ret;
 134}
 135
 136static int at32_rtc_ioctl(struct device *dev, unsigned int cmd,
 137                        unsigned long arg)
 138{
 139        struct rtc_at32ap700x *rtc = dev_get_drvdata(dev);
 140        int ret = 0;
 141
 142        spin_lock_irq(&rtc->lock);
 143
 144        switch (cmd) {
 145        case RTC_AIE_ON:
 146                if (rtc_readl(rtc, VAL) > rtc->alarm_time) {
 147                        ret = -EINVAL;
 148                        break;
 149                }
 150                rtc_writel(rtc, CTRL, rtc_readl(rtc, CTRL)
 151                                | RTC_BIT(CTRL_TOPEN));
 152                rtc_writel(rtc, ICR, RTC_BIT(ICR_TOPI));
 153                rtc_writel(rtc, IER, RTC_BIT(IER_TOPI));
 154                break;
 155        case RTC_AIE_OFF:
 156                rtc_writel(rtc, CTRL, rtc_readl(rtc, CTRL)
 157                                & ~RTC_BIT(CTRL_TOPEN));
 158                rtc_writel(rtc, IDR, RTC_BIT(IDR_TOPI));
 159                rtc_writel(rtc, ICR, RTC_BIT(ICR_TOPI));
 160                break;
 161        default:
 162                ret = -ENOIOCTLCMD;
 163                break;
 164        }
 165
 166        spin_unlock_irq(&rtc->lock);
 167
 168        return ret;
 169}
 170
 171static irqreturn_t at32_rtc_interrupt(int irq, void *dev_id)
 172{
 173        struct rtc_at32ap700x *rtc = (struct rtc_at32ap700x *)dev_id;
 174        unsigned long isr = rtc_readl(rtc, ISR);
 175        unsigned long events = 0;
 176        int ret = IRQ_NONE;
 177
 178        spin_lock(&rtc->lock);
 179
 180        if (isr & RTC_BIT(ISR_TOPI)) {
 181                rtc_writel(rtc, ICR, RTC_BIT(ICR_TOPI));
 182                rtc_writel(rtc, IDR, RTC_BIT(IDR_TOPI));
 183                rtc_writel(rtc, CTRL, rtc_readl(rtc, CTRL)
 184                                & ~RTC_BIT(CTRL_TOPEN));
 185                rtc_writel(rtc, VAL, rtc->alarm_time);
 186                events = RTC_AF | RTC_IRQF;
 187                rtc_update_irq(rtc->rtc, 1, events);
 188                ret = IRQ_HANDLED;
 189        }
 190
 191        spin_unlock(&rtc->lock);
 192
 193        return ret;
 194}
 195
 196static struct rtc_class_ops at32_rtc_ops = {
 197        .ioctl          = at32_rtc_ioctl,
 198        .read_time      = at32_rtc_readtime,
 199        .set_time       = at32_rtc_settime,
 200        .read_alarm     = at32_rtc_readalarm,
 201        .set_alarm      = at32_rtc_setalarm,
 202};
 203
 204static int __init at32_rtc_probe(struct platform_device *pdev)
 205{
 206        struct resource *regs;
 207        struct rtc_at32ap700x *rtc;
 208        int irq;
 209        int ret;
 210
 211        rtc = kzalloc(sizeof(struct rtc_at32ap700x), GFP_KERNEL);
 212        if (!rtc) {
 213                dev_dbg(&pdev->dev, "out of memory\n");
 214                return -ENOMEM;
 215        }
 216
 217        regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 218        if (!regs) {
 219                dev_dbg(&pdev->dev, "no mmio resource defined\n");
 220                ret = -ENXIO;
 221                goto out;
 222        }
 223
 224        irq = platform_get_irq(pdev, 0);
 225        if (irq <= 0) {
 226                dev_dbg(&pdev->dev, "could not get irq\n");
 227                ret = -ENXIO;
 228                goto out;
 229        }
 230
 231        rtc->irq = irq;
 232        rtc->regs = ioremap(regs->start, regs->end - regs->start + 1);
 233        if (!rtc->regs) {
 234                ret = -ENOMEM;
 235                dev_dbg(&pdev->dev, "could not map I/O memory\n");
 236                goto out;
 237        }
 238        spin_lock_init(&rtc->lock);
 239
 240        /*
 241         * Maybe init RTC: count from zero at 1 Hz, disable wrap irq.
 242         *
 243         * Do not reset VAL register, as it can hold an old time
 244         * from last JTAG reset.
 245         */
 246        if (!(rtc_readl(rtc, CTRL) & RTC_BIT(CTRL_EN))) {
 247                rtc_writel(rtc, CTRL, RTC_BIT(CTRL_PCLR));
 248                rtc_writel(rtc, IDR, RTC_BIT(IDR_TOPI));
 249                rtc_writel(rtc, CTRL, RTC_BF(CTRL_PSEL, 0xe)
 250                                | RTC_BIT(CTRL_EN));
 251        }
 252
 253        ret = request_irq(irq, at32_rtc_interrupt, IRQF_SHARED, "rtc", rtc);
 254        if (ret) {
 255                dev_dbg(&pdev->dev, "could not request irq %d\n", irq);
 256                goto out_iounmap;
 257        }
 258
 259        rtc->rtc = rtc_device_register(pdev->name, &pdev->dev,
 260                                &at32_rtc_ops, THIS_MODULE);
 261        if (IS_ERR(rtc->rtc)) {
 262                dev_dbg(&pdev->dev, "could not register rtc device\n");
 263                ret = PTR_ERR(rtc->rtc);
 264                goto out_free_irq;
 265        }
 266
 267        platform_set_drvdata(pdev, rtc);
 268        device_init_wakeup(&pdev->dev, 1);
 269
 270        dev_info(&pdev->dev, "Atmel RTC for AT32AP700x at %08lx irq %ld\n",
 271                        (unsigned long)rtc->regs, rtc->irq);
 272
 273        return 0;
 274
 275out_free_irq:
 276        free_irq(irq, rtc);
 277out_iounmap:
 278        iounmap(rtc->regs);
 279out:
 280        kfree(rtc);
 281        return ret;
 282}
 283
 284static int __exit at32_rtc_remove(struct platform_device *pdev)
 285{
 286        struct rtc_at32ap700x *rtc = platform_get_drvdata(pdev);
 287
 288        device_init_wakeup(&pdev->dev, 0);
 289
 290        free_irq(rtc->irq, rtc);
 291        iounmap(rtc->regs);
 292        rtc_device_unregister(rtc->rtc);
 293        kfree(rtc);
 294        platform_set_drvdata(pdev, NULL);
 295
 296        return 0;
 297}
 298
 299MODULE_ALIAS("platform:at32ap700x_rtc");
 300
 301static struct platform_driver at32_rtc_driver = {
 302        .remove         = __exit_p(at32_rtc_remove),
 303        .driver         = {
 304                .name   = "at32ap700x_rtc",
 305                .owner  = THIS_MODULE,
 306        },
 307};
 308
 309static int __init at32_rtc_init(void)
 310{
 311        return platform_driver_probe(&at32_rtc_driver, at32_rtc_probe);
 312}
 313module_init(at32_rtc_init);
 314
 315static void __exit at32_rtc_exit(void)
 316{
 317        platform_driver_unregister(&at32_rtc_driver);
 318}
 319module_exit(at32_rtc_exit);
 320
 321MODULE_AUTHOR("Hans-Christian Egtvedt <hcegtvedt@atmel.com>");
 322MODULE_DESCRIPTION("Real time clock for AVR32 AT32AP700x");
 323MODULE_LICENSE("GPL");
 324