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