linux/drivers/rtc/rtc-nuc900.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2008-2009 Nuvoton technology corporation.
   3 *
   4 * Wan ZongShun <mcuos.com@gmail.com>
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation;version 2 of the License.
   9 *
  10 */
  11
  12#include <linux/module.h>
  13#include <linux/init.h>
  14#include <linux/platform_device.h>
  15#include <linux/slab.h>
  16#include <linux/rtc.h>
  17#include <linux/delay.h>
  18#include <linux/io.h>
  19#include <linux/bcd.h>
  20
  21/* RTC Control Registers */
  22#define REG_RTC_INIR            0x00
  23#define REG_RTC_AER             0x04
  24#define REG_RTC_FCR             0x08
  25#define REG_RTC_TLR             0x0C
  26#define REG_RTC_CLR             0x10
  27#define REG_RTC_TSSR            0x14
  28#define REG_RTC_DWR             0x18
  29#define REG_RTC_TAR             0x1C
  30#define REG_RTC_CAR             0x20
  31#define REG_RTC_LIR             0x24
  32#define REG_RTC_RIER            0x28
  33#define REG_RTC_RIIR            0x2C
  34#define REG_RTC_TTR             0x30
  35
  36#define RTCSET                  0x01
  37#define AERRWENB                0x10000
  38#define INIRRESET               0xa5eb1357
  39#define AERPOWERON              0xA965
  40#define AERPOWEROFF             0x0000
  41#define LEAPYEAR                0x0001
  42#define TICKENB                 0x80
  43#define TICKINTENB              0x0002
  44#define ALARMINTENB             0x0001
  45#define MODE24                  0x0001
  46
  47struct nuc900_rtc {
  48        int                     irq_num;
  49        void __iomem            *rtc_reg;
  50        struct rtc_device       *rtcdev;
  51};
  52
  53struct nuc900_bcd_time {
  54        int bcd_sec;
  55        int bcd_min;
  56        int bcd_hour;
  57        int bcd_mday;
  58        int bcd_mon;
  59        int bcd_year;
  60};
  61
  62static irqreturn_t nuc900_rtc_interrupt(int irq, void *_rtc)
  63{
  64        struct nuc900_rtc *rtc = _rtc;
  65        unsigned long events = 0, rtc_irq;
  66
  67        rtc_irq = __raw_readl(rtc->rtc_reg + REG_RTC_RIIR);
  68
  69        if (rtc_irq & ALARMINTENB) {
  70                rtc_irq &= ~ALARMINTENB;
  71                __raw_writel(rtc_irq, rtc->rtc_reg + REG_RTC_RIIR);
  72                events |= RTC_AF | RTC_IRQF;
  73        }
  74
  75        if (rtc_irq & TICKINTENB) {
  76                rtc_irq &= ~TICKINTENB;
  77                __raw_writel(rtc_irq, rtc->rtc_reg + REG_RTC_RIIR);
  78                events |= RTC_UF | RTC_IRQF;
  79        }
  80
  81        rtc_update_irq(rtc->rtcdev, 1, events);
  82
  83        return IRQ_HANDLED;
  84}
  85
  86static int *check_rtc_access_enable(struct nuc900_rtc *nuc900_rtc)
  87{
  88        unsigned int timeout = 0x1000;
  89        __raw_writel(INIRRESET, nuc900_rtc->rtc_reg + REG_RTC_INIR);
  90
  91        mdelay(10);
  92
  93        __raw_writel(AERPOWERON, nuc900_rtc->rtc_reg + REG_RTC_AER);
  94
  95        while (!(__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_AER) & AERRWENB)
  96                                                                && timeout--)
  97                mdelay(1);
  98
  99        if (!timeout)
 100                return ERR_PTR(-EPERM);
 101
 102        return 0;
 103}
 104
 105static int nuc900_rtc_bcd2bin(unsigned int timereg,
 106                                unsigned int calreg, struct rtc_time *tm)
 107{
 108        tm->tm_mday     = bcd2bin(calreg >> 0);
 109        tm->tm_mon      = bcd2bin(calreg >> 8);
 110        tm->tm_year     = bcd2bin(calreg >> 16) + 100;
 111
 112        tm->tm_sec      = bcd2bin(timereg >> 0);
 113        tm->tm_min      = bcd2bin(timereg >> 8);
 114        tm->tm_hour     = bcd2bin(timereg >> 16);
 115
 116        return rtc_valid_tm(tm);
 117}
 118
 119static void nuc900_rtc_bin2bcd(struct device *dev, struct rtc_time *settm,
 120                                                struct nuc900_bcd_time *gettm)
 121{
 122        gettm->bcd_mday = bin2bcd(settm->tm_mday) << 0;
 123        gettm->bcd_mon  = bin2bcd(settm->tm_mon) << 8;
 124
 125        if (settm->tm_year < 100) {
 126                dev_warn(dev, "The year will be between 1970-1999, right?\n");
 127                gettm->bcd_year = bin2bcd(settm->tm_year) << 16;
 128        } else {
 129                gettm->bcd_year = bin2bcd(settm->tm_year - 100) << 16;
 130        }
 131
 132        gettm->bcd_sec  = bin2bcd(settm->tm_sec) << 0;
 133        gettm->bcd_min  = bin2bcd(settm->tm_min) << 8;
 134        gettm->bcd_hour = bin2bcd(settm->tm_hour) << 16;
 135}
 136
 137static int nuc900_alarm_irq_enable(struct device *dev, unsigned int enabled)
 138{
 139        struct nuc900_rtc *rtc = dev_get_drvdata(dev);
 140
 141        if (enabled)
 142                __raw_writel(__raw_readl(rtc->rtc_reg + REG_RTC_RIER)|
 143                                (ALARMINTENB), rtc->rtc_reg + REG_RTC_RIER);
 144        else
 145                __raw_writel(__raw_readl(rtc->rtc_reg + REG_RTC_RIER)&
 146                                (~ALARMINTENB), rtc->rtc_reg + REG_RTC_RIER);
 147
 148        return 0;
 149}
 150
 151static int nuc900_rtc_read_time(struct device *dev, struct rtc_time *tm)
 152{
 153        struct nuc900_rtc *rtc = dev_get_drvdata(dev);
 154        unsigned int timeval, clrval;
 155
 156        timeval = __raw_readl(rtc->rtc_reg + REG_RTC_TLR);
 157        clrval  = __raw_readl(rtc->rtc_reg + REG_RTC_CLR);
 158
 159        return nuc900_rtc_bcd2bin(timeval, clrval, tm);
 160}
 161
 162static int nuc900_rtc_set_time(struct device *dev, struct rtc_time *tm)
 163{
 164        struct nuc900_rtc *rtc = dev_get_drvdata(dev);
 165        struct nuc900_bcd_time gettm;
 166        unsigned long val;
 167        int *err;
 168
 169        nuc900_rtc_bin2bcd(dev, tm, &gettm);
 170
 171        err = check_rtc_access_enable(rtc);
 172        if (IS_ERR(err))
 173                return PTR_ERR(err);
 174
 175        val = gettm.bcd_mday | gettm.bcd_mon | gettm.bcd_year;
 176        __raw_writel(val, rtc->rtc_reg + REG_RTC_CLR);
 177
 178        val = gettm.bcd_sec | gettm.bcd_min | gettm.bcd_hour;
 179        __raw_writel(val, rtc->rtc_reg + REG_RTC_TLR);
 180
 181        return 0;
 182}
 183
 184static int nuc900_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 185{
 186        struct nuc900_rtc *rtc = dev_get_drvdata(dev);
 187        unsigned int timeval, carval;
 188
 189        timeval = __raw_readl(rtc->rtc_reg + REG_RTC_TAR);
 190        carval  = __raw_readl(rtc->rtc_reg + REG_RTC_CAR);
 191
 192        return nuc900_rtc_bcd2bin(timeval, carval, &alrm->time);
 193}
 194
 195static int nuc900_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 196{
 197        struct nuc900_rtc *rtc = dev_get_drvdata(dev);
 198        struct nuc900_bcd_time tm;
 199        unsigned long val;
 200        int *err;
 201
 202        nuc900_rtc_bin2bcd(dev, &alrm->time, &tm);
 203
 204        err = check_rtc_access_enable(rtc);
 205        if (IS_ERR(err))
 206                return PTR_ERR(err);
 207
 208        val = tm.bcd_mday | tm.bcd_mon | tm.bcd_year;
 209        __raw_writel(val, rtc->rtc_reg + REG_RTC_CAR);
 210
 211        val = tm.bcd_sec | tm.bcd_min | tm.bcd_hour;
 212        __raw_writel(val, rtc->rtc_reg + REG_RTC_TAR);
 213
 214        return 0;
 215}
 216
 217static struct rtc_class_ops nuc900_rtc_ops = {
 218        .read_time = nuc900_rtc_read_time,
 219        .set_time = nuc900_rtc_set_time,
 220        .read_alarm = nuc900_rtc_read_alarm,
 221        .set_alarm = nuc900_rtc_set_alarm,
 222        .alarm_irq_enable = nuc900_alarm_irq_enable,
 223};
 224
 225static int __devinit nuc900_rtc_probe(struct platform_device *pdev)
 226{
 227        struct resource *res;
 228        struct nuc900_rtc *nuc900_rtc;
 229        int err = 0;
 230
 231        nuc900_rtc = kzalloc(sizeof(struct nuc900_rtc), GFP_KERNEL);
 232        if (!nuc900_rtc) {
 233                dev_err(&pdev->dev, "kzalloc nuc900_rtc failed\n");
 234                return -ENOMEM;
 235        }
 236        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 237        if (!res) {
 238                dev_err(&pdev->dev, "platform_get_resource failed\n");
 239                err = -ENXIO;
 240                goto fail1;
 241        }
 242
 243        if (!request_mem_region(res->start, resource_size(res),
 244                                pdev->name)) {
 245                dev_err(&pdev->dev, "request_mem_region failed\n");
 246                err = -EBUSY;
 247                goto fail1;
 248        }
 249
 250        nuc900_rtc->rtc_reg = ioremap(res->start, resource_size(res));
 251        if (!nuc900_rtc->rtc_reg) {
 252                dev_err(&pdev->dev, "ioremap rtc_reg failed\n");
 253                err = -ENOMEM;
 254                goto fail2;
 255        }
 256
 257        platform_set_drvdata(pdev, nuc900_rtc);
 258
 259        nuc900_rtc->rtcdev = rtc_device_register(pdev->name, &pdev->dev,
 260                                                &nuc900_rtc_ops, THIS_MODULE);
 261        if (IS_ERR(nuc900_rtc->rtcdev)) {
 262                dev_err(&pdev->dev, "rtc device register failed\n");
 263                err = PTR_ERR(nuc900_rtc->rtcdev);
 264                goto fail3;
 265        }
 266
 267        __raw_writel(__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_TSSR) | MODE24,
 268                                        nuc900_rtc->rtc_reg + REG_RTC_TSSR);
 269
 270        nuc900_rtc->irq_num = platform_get_irq(pdev, 0);
 271        if (request_irq(nuc900_rtc->irq_num, nuc900_rtc_interrupt,
 272                                0, "nuc900rtc", nuc900_rtc)) {
 273                dev_err(&pdev->dev, "NUC900 RTC request irq failed\n");
 274                err = -EBUSY;
 275                goto fail4;
 276        }
 277
 278        return 0;
 279
 280fail4:  rtc_device_unregister(nuc900_rtc->rtcdev);
 281fail3:  iounmap(nuc900_rtc->rtc_reg);
 282fail2:  release_mem_region(res->start, resource_size(res));
 283fail1:  kfree(nuc900_rtc);
 284        return err;
 285}
 286
 287static int __devexit nuc900_rtc_remove(struct platform_device *pdev)
 288{
 289        struct nuc900_rtc *nuc900_rtc = platform_get_drvdata(pdev);
 290        struct resource *res;
 291
 292        free_irq(nuc900_rtc->irq_num, nuc900_rtc);
 293        rtc_device_unregister(nuc900_rtc->rtcdev);
 294        iounmap(nuc900_rtc->rtc_reg);
 295
 296        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 297        release_mem_region(res->start, resource_size(res));
 298
 299        kfree(nuc900_rtc);
 300
 301        platform_set_drvdata(pdev, NULL);
 302
 303        return 0;
 304}
 305
 306static struct platform_driver nuc900_rtc_driver = {
 307        .remove         = __devexit_p(nuc900_rtc_remove),
 308        .driver         = {
 309                .name   = "nuc900-rtc",
 310                .owner  = THIS_MODULE,
 311        },
 312};
 313
 314static int __init nuc900_rtc_init(void)
 315{
 316        return platform_driver_probe(&nuc900_rtc_driver, nuc900_rtc_probe);
 317}
 318
 319static void __exit nuc900_rtc_exit(void)
 320{
 321        platform_driver_unregister(&nuc900_rtc_driver);
 322}
 323
 324module_init(nuc900_rtc_init);
 325module_exit(nuc900_rtc_exit);
 326
 327MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
 328MODULE_DESCRIPTION("nuc910/nuc920 RTC driver");
 329MODULE_LICENSE("GPL");
 330MODULE_ALIAS("platform:nuc900-rtc");
 331
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.