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 NULL;
 103}
 104
 105static void 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
 117static void nuc900_rtc_bin2bcd(struct device *dev, struct rtc_time *settm,
 118                                                struct nuc900_bcd_time *gettm)
 119{
 120        gettm->bcd_mday = bin2bcd(settm->tm_mday) << 0;
 121        gettm->bcd_mon  = bin2bcd(settm->tm_mon) << 8;
 122
 123        if (settm->tm_year < 100) {
 124                dev_warn(dev, "The year will be between 1970-1999, right?\n");
 125                gettm->bcd_year = bin2bcd(settm->tm_year) << 16;
 126        } else {
 127                gettm->bcd_year = bin2bcd(settm->tm_year - 100) << 16;
 128        }
 129
 130        gettm->bcd_sec  = bin2bcd(settm->tm_sec) << 0;
 131        gettm->bcd_min  = bin2bcd(settm->tm_min) << 8;
 132        gettm->bcd_hour = bin2bcd(settm->tm_hour) << 16;
 133}
 134
 135static int nuc900_alarm_irq_enable(struct device *dev, unsigned int enabled)
 136{
 137        struct nuc900_rtc *rtc = dev_get_drvdata(dev);
 138
 139        if (enabled)
 140                __raw_writel(__raw_readl(rtc->rtc_reg + REG_RTC_RIER)|
 141                                (ALARMINTENB), rtc->rtc_reg + REG_RTC_RIER);
 142        else
 143                __raw_writel(__raw_readl(rtc->rtc_reg + REG_RTC_RIER)&
 144                                (~ALARMINTENB), rtc->rtc_reg + REG_RTC_RIER);
 145
 146        return 0;
 147}
 148
 149static int nuc900_rtc_read_time(struct device *dev, struct rtc_time *tm)
 150{
 151        struct nuc900_rtc *rtc = dev_get_drvdata(dev);
 152        unsigned int timeval, clrval;
 153
 154        timeval = __raw_readl(rtc->rtc_reg + REG_RTC_TLR);
 155        clrval  = __raw_readl(rtc->rtc_reg + REG_RTC_CLR);
 156
 157        nuc900_rtc_bcd2bin(timeval, clrval, tm);
 158
 159        return 0;
 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        nuc900_rtc_bcd2bin(timeval, carval, &alrm->time);
 193
 194        return rtc_valid_tm(&alrm->time);
 195}
 196
 197static int nuc900_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 198{
 199        struct nuc900_rtc *rtc = dev_get_drvdata(dev);
 200        struct nuc900_bcd_time tm;
 201        unsigned long val;
 202        int *err;
 203
 204        nuc900_rtc_bin2bcd(dev, &alrm->time, &tm);
 205
 206        err = check_rtc_access_enable(rtc);
 207        if (IS_ERR(err))
 208                return PTR_ERR(err);
 209
 210        val = tm.bcd_mday | tm.bcd_mon | tm.bcd_year;
 211        __raw_writel(val, rtc->rtc_reg + REG_RTC_CAR);
 212
 213        val = tm.bcd_sec | tm.bcd_min | tm.bcd_hour;
 214        __raw_writel(val, rtc->rtc_reg + REG_RTC_TAR);
 215
 216        return 0;
 217}
 218
 219static const struct rtc_class_ops nuc900_rtc_ops = {
 220        .read_time = nuc900_rtc_read_time,
 221        .set_time = nuc900_rtc_set_time,
 222        .read_alarm = nuc900_rtc_read_alarm,
 223        .set_alarm = nuc900_rtc_set_alarm,
 224        .alarm_irq_enable = nuc900_alarm_irq_enable,
 225};
 226
 227static int __init nuc900_rtc_probe(struct platform_device *pdev)
 228{
 229        struct resource *res;
 230        struct nuc900_rtc *nuc900_rtc;
 231
 232        nuc900_rtc = devm_kzalloc(&pdev->dev, sizeof(struct nuc900_rtc),
 233                                GFP_KERNEL);
 234        if (!nuc900_rtc)
 235                return -ENOMEM;
 236
 237        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 238        nuc900_rtc->rtc_reg = devm_ioremap_resource(&pdev->dev, res);
 239        if (IS_ERR(nuc900_rtc->rtc_reg))
 240                return PTR_ERR(nuc900_rtc->rtc_reg);
 241
 242        platform_set_drvdata(pdev, nuc900_rtc);
 243
 244        nuc900_rtc->rtcdev = devm_rtc_device_register(&pdev->dev, pdev->name,
 245                                                &nuc900_rtc_ops, THIS_MODULE);
 246        if (IS_ERR(nuc900_rtc->rtcdev)) {
 247                dev_err(&pdev->dev, "rtc device register failed\n");
 248                return PTR_ERR(nuc900_rtc->rtcdev);
 249        }
 250
 251        __raw_writel(__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_TSSR) | MODE24,
 252                                        nuc900_rtc->rtc_reg + REG_RTC_TSSR);
 253
 254        nuc900_rtc->irq_num = platform_get_irq(pdev, 0);
 255        if (devm_request_irq(&pdev->dev, nuc900_rtc->irq_num,
 256                        nuc900_rtc_interrupt, 0, "nuc900rtc", nuc900_rtc)) {
 257                dev_err(&pdev->dev, "NUC900 RTC request irq failed\n");
 258                return -EBUSY;
 259        }
 260
 261        return 0;
 262}
 263
 264static struct platform_driver nuc900_rtc_driver = {
 265        .driver         = {
 266                .name   = "nuc900-rtc",
 267        },
 268};
 269
 270module_platform_driver_probe(nuc900_rtc_driver, nuc900_rtc_probe);
 271
 272MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
 273MODULE_DESCRIPTION("nuc910/nuc920 RTC driver");
 274MODULE_LICENSE("GPL");
 275MODULE_ALIAS("platform:nuc900-rtc");
 276