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 = kzalloc(sizeof(struct rtc_at32ap700x), GFP_KERNEL);
 206        if (!rtc) {
 207                dev_dbg(&pdev->dev, "out of memory\n");
 208                return -ENOMEM;
 209        }
 210
 211        regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 212        if (!regs) {
 213                dev_dbg(&pdev->dev, "no mmio resource defined\n");
 214                ret = -ENXIO;
 215                goto out;
 216        }
 217
 218        irq = platform_get_irq(pdev, 0);
 219        if (irq <= 0) {
 220                dev_dbg(&pdev->dev, "could not get irq\n");
 221                ret = -ENXIO;
 222                goto out;
 223        }
 224
 225        rtc->irq = irq;
 226        rtc->regs = ioremap(regs->start, resource_size(regs));
 227        if (!rtc->regs) {
 228                ret = -ENOMEM;
 229                dev_dbg(&pdev->dev, "could not map I/O memory\n");
 230                goto out;
 231        }
 232        spin_lock_init(&rtc->lock);
 233
 234        /*
 235         * Maybe init RTC: count from zero at 1 Hz, disable wrap irq.
 236         *
 237         * Do not reset VAL register, as it can hold an old time
 238         * from last JTAG reset.
 239         */
 240        if (!(rtc_readl(rtc, CTRL) & RTC_BIT(CTRL_EN))) {
 241                rtc_writel(rtc, CTRL, RTC_BIT(CTRL_PCLR));
 242                rtc_writel(rtc, IDR, RTC_BIT(IDR_TOPI));
 243                rtc_writel(rtc, CTRL, RTC_BF(CTRL_PSEL, 0xe)
 244                                | RTC_BIT(CTRL_EN));
 245        }
 246
 247        ret = request_irq(irq, at32_rtc_interrupt, IRQF_SHARED, "rtc", rtc);
 248        if (ret) {
 249                dev_dbg(&pdev->dev, "could not request irq %d\n", irq);
 250                goto out_iounmap;
 251        }
 252
 253        platform_set_drvdata(pdev, rtc);
 254
 255        rtc->rtc = rtc_device_register(pdev->name, &pdev->dev,
 256                                &at32_rtc_ops, THIS_MODULE);
 257        if (IS_ERR(rtc->rtc)) {
 258                dev_dbg(&pdev->dev, "could not register rtc device\n");
 259                ret = PTR_ERR(rtc->rtc);
 260                goto out_free_irq;
 261        }
 262
 263        device_init_wakeup(&pdev->dev, 1);
 264
 265        dev_info(&pdev->dev, "Atmel RTC for AT32AP700x at %08lx irq %ld\n",
 266                        (unsigned long)rtc->regs, rtc->irq);
 267
 268        return 0;
 269
 270out_free_irq:
 271        platform_set_drvdata(pdev, NULL);
 272        free_irq(irq, rtc);
 273out_iounmap:
 274        iounmap(rtc->regs);
 275out:
 276        kfree(rtc);
 277        return ret;
 278}
 279
 280static int __exit at32_rtc_remove(struct platform_device *pdev)
 281{
 282        struct rtc_at32ap700x *rtc = platform_get_drvdata(pdev);
 283
 284        device_init_wakeup(&pdev->dev, 0);
 285
 286        free_irq(rtc->irq, rtc);
 287        iounmap(rtc->regs);
 288        rtc_device_unregister(rtc->rtc);
 289        kfree(rtc);
 290        platform_set_drvdata(pdev, NULL);
 291
 292        return 0;
 293}
 294
 295MODULE_ALIAS("platform:at32ap700x_rtc");
 296
 297static struct platform_driver at32_rtc_driver = {
 298        .remove         = __exit_p(at32_rtc_remove),
 299        .driver         = {
 300                .name   = "at32ap700x_rtc",
 301                .owner  = THIS_MODULE,
 302        },
 303};
 304
 305static int __init at32_rtc_init(void)
 306{
 307        return platform_driver_probe(&at32_rtc_driver, at32_rtc_probe);
 308}
 309module_init(at32_rtc_init);
 310
 311static void __exit at32_rtc_exit(void)
 312{
 313        platform_driver_unregister(&at32_rtc_driver);
 314}
 315module_exit(at32_rtc_exit);
 316
 317MODULE_AUTHOR("Hans-Christian Egtvedt <hcegtvedt@atmel.com>");
 318MODULE_DESCRIPTION("Real time clock for AVR32 AT32AP700x");
 319MODULE_LICENSE("GPL");
 320
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.