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_update_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                                (TICKINTENB), rtc->rtc_reg + REG_RTC_RIER);
 144        else
 145                __raw_writel(__raw_readl(rtc->rtc_reg + REG_RTC_RIER)&
 146                                (~TICKINTENB), rtc->rtc_reg + REG_RTC_RIER);
 147
 148        return 0;
 149}
 150
 151static int nuc900_alarm_irq_enable(struct device *dev, unsigned int enabled)
 152{
 153        struct nuc900_rtc *rtc = dev_get_drvdata(dev);
 154
 155        if (enabled)
 156                __raw_writel(__raw_readl(rtc->rtc_reg + REG_RTC_RIER)|
 157                                (ALARMINTENB), rtc->rtc_reg + REG_RTC_RIER);
 158        else
 159                __raw_writel(__raw_readl(rtc->rtc_reg + REG_RTC_RIER)&
 160                                (~ALARMINTENB), rtc->rtc_reg + REG_RTC_RIER);
 161
 162        return 0;
 163}
 164
 165static int nuc900_rtc_read_time(struct device *dev, struct rtc_time *tm)
 166{
 167        struct nuc900_rtc *rtc = dev_get_drvdata(dev);
 168        unsigned int timeval, clrval;
 169
 170        timeval = __raw_readl(rtc->rtc_reg + REG_RTC_TLR);
 171        clrval  = __raw_readl(rtc->rtc_reg + REG_RTC_CLR);
 172
 173        return nuc900_rtc_bcd2bin(timeval, clrval, tm);
 174}
 175
 176static int nuc900_rtc_set_time(struct device *dev, struct rtc_time *tm)
 177{
 178        struct nuc900_rtc *rtc = dev_get_drvdata(dev);
 179        struct nuc900_bcd_time gettm;
 180        unsigned long val;
 181        int *err;
 182
 183        nuc900_rtc_bin2bcd(dev, tm, &gettm);
 184
 185        err = check_rtc_access_enable(rtc);
 186        if (IS_ERR(err))
 187                return PTR_ERR(err);
 188
 189        val = gettm.bcd_mday | gettm.bcd_mon | gettm.bcd_year;
 190        __raw_writel(val, rtc->rtc_reg + REG_RTC_CLR);
 191
 192        val = gettm.bcd_sec | gettm.bcd_min | gettm.bcd_hour;
 193        __raw_writel(val, rtc->rtc_reg + REG_RTC_TLR);
 194
 195        return 0;
 196}
 197
 198static int nuc900_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 199{
 200        struct nuc900_rtc *rtc = dev_get_drvdata(dev);
 201        unsigned int timeval, carval;
 202
 203        timeval = __raw_readl(rtc->rtc_reg + REG_RTC_TAR);
 204        carval  = __raw_readl(rtc->rtc_reg + REG_RTC_CAR);
 205
 206        return nuc900_rtc_bcd2bin(timeval, carval, &alrm->time);
 207}
 208
 209static int nuc900_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 210{
 211        struct nuc900_rtc *rtc = dev_get_drvdata(dev);
 212        struct nuc900_bcd_time tm;
 213        unsigned long val;
 214        int *err;
 215
 216        nuc900_rtc_bin2bcd(dev, &alrm->time, &tm);
 217
 218        err = check_rtc_access_enable(rtc);
 219        if (IS_ERR(err))
 220                return PTR_ERR(err);
 221
 222        val = tm.bcd_mday | tm.bcd_mon | tm.bcd_year;
 223        __raw_writel(val, rtc->rtc_reg + REG_RTC_CAR);
 224
 225        val = tm.bcd_sec | tm.bcd_min | tm.bcd_hour;
 226        __raw_writel(val, rtc->rtc_reg + REG_RTC_TAR);
 227
 228        return 0;
 229}
 230
 231static struct rtc_class_ops nuc900_rtc_ops = {
 232        .read_time = nuc900_rtc_read_time,
 233        .set_time = nuc900_rtc_set_time,
 234        .read_alarm = nuc900_rtc_read_alarm,
 235        .set_alarm = nuc900_rtc_set_alarm,
 236        .alarm_irq_enable = nuc900_alarm_irq_enable,
 237        .update_irq_enable = nuc900_update_irq_enable,
 238};
 239
 240static int __devinit nuc900_rtc_probe(struct platform_device *pdev)
 241{
 242        struct resource *res;
 243        struct nuc900_rtc *nuc900_rtc;
 244        int err = 0;
 245
 246        nuc900_rtc = kzalloc(sizeof(struct nuc900_rtc), GFP_KERNEL);
 247        if (!nuc900_rtc) {
 248                dev_err(&pdev->dev, "kzalloc nuc900_rtc failed\n");
 249                return -ENOMEM;
 250        }
 251        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 252        if (!res) {
 253                dev_err(&pdev->dev, "platform_get_resource failed\n");
 254                err = -ENXIO;
 255                goto fail1;
 256        }
 257
 258        if (!request_mem_region(res->start, resource_size(res),
 259                                pdev->name)) {
 260                dev_err(&pdev->dev, "request_mem_region failed\n");
 261                err = -EBUSY;
 262                goto fail1;
 263        }
 264
 265        nuc900_rtc->rtc_reg = ioremap(res->start, resource_size(res));
 266        if (!nuc900_rtc->rtc_reg) {
 267                dev_err(&pdev->dev, "ioremap rtc_reg failed\n");
 268                err = -ENOMEM;
 269                goto fail2;
 270        }
 271
 272        platform_set_drvdata(pdev, nuc900_rtc);
 273
 274        nuc900_rtc->rtcdev = rtc_device_register(pdev->name, &pdev->dev,
 275                                                &nuc900_rtc_ops, THIS_MODULE);
 276        if (IS_ERR(nuc900_rtc->rtcdev)) {
 277                dev_err(&pdev->dev, "rtc device register failed\n");
 278                err = PTR_ERR(nuc900_rtc->rtcdev);
 279                goto fail3;
 280        }
 281
 282        __raw_writel(__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_TSSR) | MODE24,
 283                                        nuc900_rtc->rtc_reg + REG_RTC_TSSR);
 284
 285        nuc900_rtc->irq_num = platform_get_irq(pdev, 0);
 286        if (request_irq(nuc900_rtc->irq_num, nuc900_rtc_interrupt,
 287                                IRQF_DISABLED, "nuc900rtc", nuc900_rtc)) {
 288                dev_err(&pdev->dev, "NUC900 RTC request irq failed\n");
 289                err = -EBUSY;
 290                goto fail4;
 291        }
 292
 293        return 0;
 294
 295fail4:  rtc_device_unregister(nuc900_rtc->rtcdev);
 296fail3:  iounmap(nuc900_rtc->rtc_reg);
 297fail2:  release_mem_region(res->start, resource_size(res));
 298fail1:  kfree(nuc900_rtc);
 299        return err;
 300}
 301
 302static int __devexit nuc900_rtc_remove(struct platform_device *pdev)
 303{
 304        struct nuc900_rtc *nuc900_rtc = platform_get_drvdata(pdev);
 305        struct resource *res;
 306
 307        free_irq(nuc900_rtc->irq_num, nuc900_rtc);
 308        rtc_device_unregister(nuc900_rtc->rtcdev);
 309        iounmap(nuc900_rtc->rtc_reg);
 310
 311        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 312        release_mem_region(res->start, resource_size(res));
 313
 314        kfree(nuc900_rtc);
 315
 316        platform_set_drvdata(pdev, NULL);
 317
 318        return 0;
 319}
 320
 321static struct platform_driver nuc900_rtc_driver = {
 322        .remove         = __devexit_p(nuc900_rtc_remove),
 323        .driver         = {
 324                .name   = "nuc900-rtc",
 325                .owner  = THIS_MODULE,
 326        },
 327};
 328
 329static int __init nuc900_rtc_init(void)
 330{
 331        return platform_driver_probe(&nuc900_rtc_driver, nuc900_rtc_probe);
 332}
 333
 334static void __exit nuc900_rtc_exit(void)
 335{
 336        platform_driver_unregister(&nuc900_rtc_driver);
 337}
 338
 339module_init(nuc900_rtc_init);
 340module_exit(nuc900_rtc_exit);
 341
 342MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
 343MODULE_DESCRIPTION("nuc910/nuc920 RTC driver");
 344MODULE_LICENSE("GPL");
 345MODULE_ALIAS("platform:nuc900-rtc");
 346